From 4d948f884f6446034d5267a24db46067db55506b Mon Sep 17 00:00:00 2001 From: Causeless Date: Mon, 14 Aug 2023 22:26:36 +0100 Subject: [PATCH 01/24] First implementation of saving/loading menu --- Menus/MainMenuGUI.h | 2 + Menus/PauseMenuGUI.cpp | 30 ++++---- Menus/PauseMenuGUI.h | 6 +- Menus/SaveLoadMenuGUI.cpp | 142 ++++++++++++++++++++++++++++++++++++++ Menus/SaveLoadMenuGUI.h | 101 +++++++++++++++++++++++++++ Menus/meson.build | 1 + RTEA.vcxproj | 2 + RTEA.vcxproj.filters | 2 + 8 files changed, 267 insertions(+), 19 deletions(-) create mode 100644 Menus/SaveLoadMenuGUI.cpp create mode 100644 Menus/SaveLoadMenuGUI.h diff --git a/Menus/MainMenuGUI.h b/Menus/MainMenuGUI.h index edad10a7ea..5fff148f03 100644 --- a/Menus/MainMenuGUI.h +++ b/Menus/MainMenuGUI.h @@ -2,6 +2,8 @@ #define _RTEMAINMENUGUI_ #include "Controller.h" + +#include "SaveLoadMenuGUI.h" #include "SettingsGUI.h" #include "ModManagerGUI.h" diff --git a/Menus/PauseMenuGUI.cpp b/Menus/PauseMenuGUI.cpp index bc16deb284..ba863ecc50 100644 --- a/Menus/PauseMenuGUI.cpp +++ b/Menus/PauseMenuGUI.cpp @@ -7,6 +7,7 @@ #include "UInputMan.h" #include "SettingsMan.h" +#include "SaveLoadMenuGUI.h" #include "SettingsGUI.h" #include "ModManagerGUI.h" @@ -30,6 +31,7 @@ namespace RTE { m_UpdateResult = PauseMenuUpdateResult::NoEvent; m_ResumeButtonBlinkTimer.Reset(); + m_SaveLoadMenu = nullptr; m_SettingsMenu = nullptr; m_ModManagerMenu = nullptr; @@ -60,8 +62,7 @@ namespace RTE { m_PauseMenuBox->CenterInParent(true, true); m_PauseMenuButtons[PauseMenuButton::BackToMainButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMain")); - m_PauseMenuButtons[PauseMenuButton::SaveGameButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonSaveGame")); - m_PauseMenuButtons[PauseMenuButton::LoadLastSaveButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonLoadLastSave")); + m_PauseMenuButtons[PauseMenuButton::SaveOrLoadGameButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonSaveOrLoadGame")); m_PauseMenuButtons[PauseMenuButton::SettingsButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonSettings")); m_PauseMenuButtons[PauseMenuButton::ModManagerButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonModManager")); m_PauseMenuButtons[PauseMenuButton::ResumeButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonResume")); @@ -84,6 +85,7 @@ namespace RTE { const BITMAP *backbuffer = g_FrameMan.GetBackBuffer32(); m_BackdropBitmap = create_bitmap_ex(FrameMan::c_BPP, backbuffer->w, backbuffer->h); + m_SaveLoadMenu = std::make_unique(guiScreen, guiInput, true); m_SettingsMenu = std::make_unique(guiScreen, guiInput, true); m_ModManagerMenu = std::make_unique(guiScreen, guiInput, true); } @@ -119,7 +121,7 @@ namespace RTE { if (m_SavingButtonsDisabled != disableSaving) { int yOffset = 0; - for (size_t pauseMenuButton = PauseMenuButton::SaveGameButton; pauseMenuButton < PauseMenuButton::SettingsButton; ++pauseMenuButton) { + for (size_t pauseMenuButton = PauseMenuButton::SaveOrLoadGameButton; pauseMenuButton < PauseMenuButton::SettingsButton; ++pauseMenuButton) { m_PauseMenuButtons[pauseMenuButton]->SetEnabled(!disableSaving); m_PauseMenuButtons[pauseMenuButton]->SetVisible(!disableSaving); yOffset += m_PauseMenuButtons[pauseMenuButton]->GetHeight(); @@ -183,6 +185,9 @@ namespace RTE { backToMainScreen = HandleInputEvents(); BlinkResumeButton(); break; + case PauseMenuScreen::SaveOrLoadGameScreen: + backToMainScreen = m_SaveLoadMenu->HandleInputEvents(); + break; case PauseMenuScreen::SettingsScreen: backToMainScreen = m_SettingsMenu->HandleInputEvents(); m_ActiveDialogBox = m_SettingsMenu->GetActiveDialogBox(); @@ -236,20 +241,8 @@ namespace RTE { if (guiEvent.GetType() == GUIEvent::Command) { if (guiEvent.GetControl() == m_PauseMenuButtons[PauseMenuButton::ResumeButton]) { return true; - } else if (guiEvent.GetControl() == m_PauseMenuButtons[PauseMenuButton::SaveGameButton]) { - if (g_ActivityMan.GetActivityAllowsSaving() && g_ActivityMan.SaveCurrentGame("QuickSave")) { - g_GUISound.ConfirmSound()->Play(); - return true; - } else { - g_GUISound.UserErrorSound()->Play(); - } - } else if (guiEvent.GetControl() == m_PauseMenuButtons[PauseMenuButton::LoadLastSaveButton]) { - if (g_ActivityMan.LoadAndLaunchGame("QuickSave")) { - g_GUISound.ConfirmSound()->Play(); - return true; - } else { - g_GUISound.UserErrorSound()->Play(); - } + } else if (guiEvent.GetControl() == m_PauseMenuButtons[PauseMenuButton::SaveOrLoadGameButton]) { + SetActiveMenuScreen(PauseMenuScreen::SaveOrLoadGameScreen); } else if (guiEvent.GetControl() == m_PauseMenuButtons[PauseMenuButton::SettingsButton]) { SetActiveMenuScreen(PauseMenuScreen::SettingsScreen); } else if (guiEvent.GetControl() == m_PauseMenuButtons[PauseMenuButton::ModManagerButton]) { @@ -304,6 +297,9 @@ namespace RTE { blit(m_BackdropBitmap, g_FrameMan.GetBackBuffer32(), 0, 0, 0, 0, m_BackdropBitmap->w, m_BackdropBitmap->h); switch (m_ActiveMenuScreen) { + case PauseMenuScreen::SaveOrLoadGameScreen: + m_SaveLoadMenu->Draw(); + break; case PauseMenuScreen::SettingsScreen: m_SettingsMenu->Draw(); break; diff --git a/Menus/PauseMenuGUI.h b/Menus/PauseMenuGUI.h index d9c9eb72ba..c4ae1d413f 100644 --- a/Menus/PauseMenuGUI.h +++ b/Menus/PauseMenuGUI.h @@ -14,6 +14,7 @@ namespace RTE { class GUIButton; class SettingsGUI; class ModManagerGUI; + class SaveLoadMenuGUI; /// /// Handling for the pause menu screen composition and interaction. @@ -85,6 +86,7 @@ namespace RTE { /// enum PauseMenuScreen { MainScreen, + SaveOrLoadGameScreen, SettingsScreen, ModManagerScreen, ScreenCount @@ -95,8 +97,7 @@ namespace RTE { /// enum PauseMenuButton { BackToMainButton, - SaveGameButton, - LoadLastSaveButton, + SaveOrLoadGameButton, SettingsButton, ModManagerButton, ResumeButton, @@ -118,6 +119,7 @@ namespace RTE { Timer m_ResumeButtonBlinkTimer; //!< Activity resume button blink timer. + std::unique_ptr m_SaveLoadMenu; //!< The settings menu screen. std::unique_ptr m_SettingsMenu; //!< The settings menu screen. std::unique_ptr m_ModManagerMenu; //!< The mod manager menu screen. diff --git a/Menus/SaveLoadMenuGUI.cpp b/Menus/SaveLoadMenuGUI.cpp new file mode 100644 index 0000000000..04b40f4a6f --- /dev/null +++ b/Menus/SaveLoadMenuGUI.cpp @@ -0,0 +1,142 @@ +#include "SaveLoadMenuGUI.h" + +#include "ActivityMan.h" +#include "PresetMan.h" +#include "WindowMan.h" + +#include "GUI.h" +#include "AllegroScreen.h" +#include "GUIInputWrapper.h" +#include "GUICollectionBox.h" +#include "GUILabel.h" +#include "GUIButton.h" +#include "GUIListBox.h" +#include "GUITextBox.h" + +namespace RTE { + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + SaveLoadMenuGUI::SaveLoadMenuGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool createForPauseMenu) { + m_GUIControlManager = std::make_unique(); + RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); + m_GUIControlManager->Load("Base.rte/GUIs/SaveLoadMenuGUI.ini"); + + int rootBoxMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); + + GUICollectionBox *rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); + rootBox->Resize(rootBoxMaxWidth, g_WindowMan.GetResY()); + + GUICollectionBox *saveGameMenuBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSaveGameMenu")); + saveGameMenuBox->CenterInParent(true, true); + saveGameMenuBox->SetPositionAbs(saveGameMenuBox->GetXPos(), (rootBox->GetHeight() < 540) ? saveGameMenuBox->GetYPos() - 15 : 140); + + m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMainMenu")); + + if (createForPauseMenu) { + m_BackToMainButton->SetSize(120, 20); + m_BackToMainButton->SetText("Back to Pause Menu"); + } + m_BackToMainButton->SetPositionAbs((rootBox->GetWidth() - m_BackToMainButton->GetWidth()) / 2, saveGameMenuBox->GetYPos() + saveGameMenuBox->GetHeight() + 10); + + m_SaveGamesListBox = dynamic_cast(m_GUIControlManager->GetControl("ListBoxSaveGames")); + m_SaveGamesListBox->SetMouseScrolling(true); + m_SaveGamesListBox->SetScrollBarThickness(15); + m_SaveGamesListBox->SetScrollBarPadding(2); + + m_SaveGameName = dynamic_cast(m_GUIControlManager->GetControl("SaveGameName")); + m_LoadButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonLoad")); + m_CreateButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCreate")); + m_DescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelDescription")); + + m_SaveGamesFetched = false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SaveLoadMenuGUI::PopulateSaveGamesList() { + m_SaveGames.clear(); + m_SaveGamesListBox->ClearList(); + + std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/"; + for (const auto &entry : std::filesystem::directory_iterator(saveFilePath)) { + if (entry.path().extension() == ".ini" && entry.path().filename() != "Index.ini") { + SaveRecord record; + record.SaveName = entry.path().stem().string(); + record.SaveDate = entry.last_write_time(); + m_SaveGames.push_back(record); + } + } + + std::sort(m_SaveGames.begin(), m_SaveGames.end()); + + for (int i = 0; i < m_SaveGames.size(); i++) { + m_SaveGamesListBox->AddItem(m_SaveGames.at(i).SaveName, std::string(), nullptr, nullptr, i); + } + + m_SaveGamesListBox->ScrollToTop(); + m_SaveGamesFetched = true; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SaveLoadMenuGUI::LoadSave() { + bool success = g_ActivityMan.LoadAndLaunchGame(m_SaveGameName->GetText()); + if (success) { + g_GUISound.ConfirmSound()->Play(); + } else { + g_GUISound.UserErrorSound()->Play(); + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SaveLoadMenuGUI::CreateSave() { + bool success = g_ActivityMan.SaveCurrentGame(m_SaveGameName->GetText()); + if (success) { + g_GUISound.ConfirmSound()->Play(); + } else { + g_GUISound.UserErrorSound()->Play(); + } + PopulateSaveGamesList(); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SaveLoadMenuGUI::HandleInputEvents() { + if (!ListsFetched()) { + PopulateSaveGamesList(); + } + m_GUIControlManager->Update(); + + GUIEvent guiEvent; + while (m_GUIControlManager->GetEvent(&guiEvent)) { + if (guiEvent.GetType() == GUIEvent::Command) { + if (guiEvent.GetControl() == m_BackToMainButton) { + return true; + } else if (guiEvent.GetControl() == m_LoadButton) { + LoadSave(); + } else if (guiEvent.GetControl() == m_CreateButton) { + CreateSave(); + } + } else if (guiEvent.GetType() == GUIEvent::Notification) { + if (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl())) { + g_GUISound.SelectionChangeSound()->Play(); + } + + if (guiEvent.GetControl() == m_SaveGamesListBox && (guiEvent.GetMsg() == GUIListBox::Select && m_SaveGamesListBox->GetSelectedIndex() > -1)) { + const SaveRecord &record = m_SaveGames.at(m_SaveGamesListBox->GetSelected()->m_ExtraIndex); + //m_DescriptionLabel->SetText(record.GetDisplayString()); + m_SaveGameName->SetText(record.SaveName); + } + } + } + return false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SaveLoadMenuGUI::Draw() const { + m_GUIControlManager->Draw(); + } +} \ No newline at end of file diff --git a/Menus/SaveLoadMenuGUI.h b/Menus/SaveLoadMenuGUI.h new file mode 100644 index 0000000000..808bc732ae --- /dev/null +++ b/Menus/SaveLoadMenuGUI.h @@ -0,0 +1,101 @@ +#ifndef _RTESAVELOADMENUGUI_ +#define _RTESAVELOADMENUGUI_ + +#include + +namespace RTE { + + class AllegroScreen; + class GUIInputWrapper; + class GUIControlManager; + class GUILabel; + class GUIButton; + class GUIListBox; + class GUITextBox; + + /// + /// Integrated savegame user interface composition and handling. + /// + class SaveLoadMenuGUI { + + public: + +#pragma region Creation + /// + /// Constructor method used to instantiate a SaveLoadMenuGUI object in system memory and make it ready for use. + /// + /// Pointer to a GUIScreen interface that will be used by this SaveLoadMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// Pointer to a GUIInput interface that will be used by this SaveLoadMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// Whether this SettingsGUI is part of SaveLoadMenuGUI and should have a slightly different layout. + SaveLoadMenuGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool createForPauseMenu = false); +#pragma endregion + +#pragma region Concrete Methods + /// + /// Handles the player interaction with the SaveLoadMenuGUI GUI elements. + /// + /// Whether the player requested to return to the main menu. + bool HandleInputEvents(); + + /// + /// Draws the SaveLoadMenuGUI to the screen. + /// + void Draw() const; +#pragma endregion + + private: + /// + /// Struct containing information about a valid Savegame. + /// + struct SaveRecord { + std::string SaveName; //!< Savegame name. + std::filesystem::file_time_type SaveDate; //!< Savegame last modified date. + + bool operator<(const SaveRecord &rhs) const { return SaveDate > rhs.SaveDate; } + }; + + std::unique_ptr m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of the SaveLoadMenuGUI. + + std::vector m_SaveGames; //!< Contains all SaveGames. + + bool m_SaveGamesFetched; //!< Whether the savegames list has been fetched. + + /// + /// GUI elements that compose the Mod Manager menu screen. + /// + GUIButton *m_BackToMainButton; + GUITextBox *m_SaveGameName; + GUIButton *m_LoadButton; + GUIButton *m_CreateButton; + GUIListBox *m_SaveGamesListBox; + GUILabel *m_DescriptionLabel; + +#pragma region Mod and Script Handling + /// + /// Gets whether both lists were fetched, even if nothing valid was added to them. + /// + /// Whether both lists were fetched, even if nothing valid was added to them. + bool ListsFetched() const { return m_SaveGamesFetched; } + + /// + /// Fills the SaveGames list with all valid savegames, then fills the SaveGamesListBox using it. + /// + void PopulateSaveGamesList(); + + /// + /// Loads the currently selected savefile. + /// + void LoadSave(); + + /// + /// Creates a new savefile (or overwrites the existing one) with the name from the textbox. + /// + void CreateSave(); +#pragma endregion + + // Disallow the use of some implicit methods. + SaveLoadMenuGUI(const SaveLoadMenuGUI &reference) = delete; + SaveLoadMenuGUI & operator=(const SaveLoadMenuGUI &rhs) = delete; + }; +} +#endif \ No newline at end of file diff --git a/Menus/meson.build b/Menus/meson.build index 5aaea69299..920d4d422f 100644 --- a/Menus/meson.build +++ b/Menus/meson.build @@ -11,6 +11,7 @@ sources += files( 'ModManagerGUI.cpp', 'ObjectPickerGUI.cpp', 'PauseMenuGUI.cpp', +'SaveLoadMenuGUI.cpp', 'ScenarioActivityConfigGUI.cpp', 'ScenarioGUI.cpp', 'SceneEditorGUI.cpp', diff --git a/RTEA.vcxproj b/RTEA.vcxproj index 708b7cb5c3..1d2fdf49c4 100644 --- a/RTEA.vcxproj +++ b/RTEA.vcxproj @@ -608,6 +608,7 @@ + @@ -844,6 +845,7 @@ + diff --git a/RTEA.vcxproj.filters b/RTEA.vcxproj.filters index 5cedabc110..2e96b38433 100644 --- a/RTEA.vcxproj.filters +++ b/RTEA.vcxproj.filters @@ -591,6 +591,7 @@ System + @@ -1140,6 +1141,7 @@ System + From ece8b62eb28d42eec354c9b43e875d14ff882917 Mon Sep 17 00:00:00 2001 From: Causeless Date: Fri, 18 Aug 2023 00:11:01 +0100 Subject: [PATCH 02/24] Added activity/scene/date info --- Menus/SaveLoadMenuGUI.cpp | 62 +++++++++++++++++++++++++++++++++++---- Menus/SaveLoadMenuGUI.h | 13 ++++++-- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/Menus/SaveLoadMenuGUI.cpp b/Menus/SaveLoadMenuGUI.cpp index 04b40f4a6f..20cf810204 100644 --- a/Menus/SaveLoadMenuGUI.cpp +++ b/Menus/SaveLoadMenuGUI.cpp @@ -6,6 +6,7 @@ #include "GUI.h" #include "AllegroScreen.h" +#include "GAScripted.h" #include "GUIInputWrapper.h" #include "GUICollectionBox.h" #include "GUILabel.h" @@ -40,6 +41,7 @@ namespace RTE { m_BackToMainButton->SetPositionAbs((rootBox->GetWidth() - m_BackToMainButton->GetWidth()) / 2, saveGameMenuBox->GetYPos() + saveGameMenuBox->GetHeight() + 10); m_SaveGamesListBox = dynamic_cast(m_GUIControlManager->GetControl("ListBoxSaveGames")); + m_SaveGamesListBox->SetFont(m_GUIControlManager->GetSkin()->GetFont("FontConsoleMonospace.png")); m_SaveGamesListBox->SetMouseScrolling(true); m_SaveGamesListBox->SetScrollBarThickness(15); m_SaveGamesListBox->SetScrollBarPadding(2); @@ -56,26 +58,76 @@ namespace RTE { void SaveLoadMenuGUI::PopulateSaveGamesList() { m_SaveGames.clear(); - m_SaveGamesListBox->ClearList(); std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/"; for (const auto &entry : std::filesystem::directory_iterator(saveFilePath)) { if (entry.path().extension() == ".ini" && entry.path().filename() != "Index.ini") { SaveRecord record; - record.SaveName = entry.path().stem().string(); + record.SavePath = entry.path(); record.SaveDate = entry.last_write_time(); m_SaveGames.push_back(record); } } + std::for_each(std::execution::par_unseq, + m_SaveGames.begin(), m_SaveGames.end(), + [](SaveRecord &record) { + Reader reader(record.SavePath.string(), true, nullptr, true); + + bool readActivity = false; + bool readSceneName = false; + + GAScripted activity; + + std::string originalScenePresetName; + while (reader.NextProperty()) { + std::string propName = reader.ReadPropName(); + if (propName == "Activity") { + reader >> activity; + readActivity = true; + } else if (propName == "OriginalScenePresetName") { + reader >> originalScenePresetName; + readSceneName = true; + } + + if (readActivity && readSceneName) { + break; + } + } + + record.Activity = activity.GetPresetName(); + record.Scene = originalScenePresetName; + }); + + m_SaveGamesFetched = true; + UpdateSaveGamesGUIList(); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SaveLoadMenuGUI::UpdateSaveGamesGUIList() + { std::sort(m_SaveGames.begin(), m_SaveGames.end()); + m_SaveGamesListBox->ClearList(); for (int i = 0; i < m_SaveGames.size(); i++) { - m_SaveGamesListBox->AddItem(m_SaveGames.at(i).SaveName, std::string(), nullptr, nullptr, i); + const SaveRecord &save = m_SaveGames[i]; + + std::stringstream saveNameText; + saveNameText << std::left << std::setfill(' ') << std::setw(32) << save.SavePath.stem().string(); + + // This is so much more fucking difficult than it has any right to be + const auto saveFsTime = std::chrono::clock_cast(save.SaveDate); + const auto saveTime = std::chrono::system_clock::to_time_t(saveFsTime); + const auto saveTimeLocal = std::localtime(&saveTime); + + std::stringstream saveDateTimeText; + saveDateTimeText << std::put_time(saveTimeLocal, "%Y-%m-%d %X"); + + m_SaveGamesListBox->AddItem(" " + saveNameText.str() + "" + save.Scene + " - " + save.Activity + "", saveDateTimeText.str() + " ", nullptr, nullptr, i); } m_SaveGamesListBox->ScrollToTop(); - m_SaveGamesFetched = true; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -127,7 +179,7 @@ namespace RTE { if (guiEvent.GetControl() == m_SaveGamesListBox && (guiEvent.GetMsg() == GUIListBox::Select && m_SaveGamesListBox->GetSelectedIndex() > -1)) { const SaveRecord &record = m_SaveGames.at(m_SaveGamesListBox->GetSelected()->m_ExtraIndex); //m_DescriptionLabel->SetText(record.GetDisplayString()); - m_SaveGameName->SetText(record.SaveName); + m_SaveGameName->SetText(record.SavePath.stem().string()); } } } diff --git a/Menus/SaveLoadMenuGUI.h b/Menus/SaveLoadMenuGUI.h index 808bc732ae..d50eaaa331 100644 --- a/Menus/SaveLoadMenuGUI.h +++ b/Menus/SaveLoadMenuGUI.h @@ -48,8 +48,10 @@ namespace RTE { /// Struct containing information about a valid Savegame. /// struct SaveRecord { - std::string SaveName; //!< Savegame name. - std::filesystem::file_time_type SaveDate; //!< Savegame last modified date. + std::filesystem::path SavePath; //!< Savegame filepath. + std::filesystem::file_time_type SaveDate; //!< Last modified date. + std::string Activity; //!< The activity name. + std::string Scene; //!< The scene name. bool operator<(const SaveRecord &rhs) const { return SaveDate > rhs.SaveDate; } }; @@ -78,10 +80,15 @@ namespace RTE { bool ListsFetched() const { return m_SaveGamesFetched; } /// - /// Fills the SaveGames list with all valid savegames, then fills the SaveGamesListBox using it. + /// Fills the SaveGames list with all valid savegames. /// void PopulateSaveGamesList(); + /// + /// Updates the SaveGamesListBox GUI. + /// + void UpdateSaveGamesGUIList(); + /// /// Loads the currently selected savefile. /// From 8dcf097026f73e44ba0ce317225dc90c62bbdd63 Mon Sep 17 00:00:00 2001 From: Causeless Date: Fri, 18 Aug 2023 16:55:50 +0100 Subject: [PATCH 03/24] Always show save/load - we should disable the "Create Save" button in unsavable scenes (or show an error dialog?) --- Menus/PauseMenuGUI.cpp | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/Menus/PauseMenuGUI.cpp b/Menus/PauseMenuGUI.cpp index ba863ecc50..4ce4201661 100644 --- a/Menus/PauseMenuGUI.cpp +++ b/Menus/PauseMenuGUI.cpp @@ -110,30 +110,12 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PauseMenuGUI::EnableOrDisablePauseMenuFeatures() { - bool disableSaving = true; bool disableModManager = true; if (const Activity *activity = g_ActivityMan.GetActivity(); activity) { - disableSaving = !activity->ActivityCanBeSaved(); disableModManager = activity->GetClassName() != "GAScripted"; } - if (m_SavingButtonsDisabled != disableSaving) { - int yOffset = 0; - - for (size_t pauseMenuButton = PauseMenuButton::SaveOrLoadGameButton; pauseMenuButton < PauseMenuButton::SettingsButton; ++pauseMenuButton) { - m_PauseMenuButtons[pauseMenuButton]->SetEnabled(!disableSaving); - m_PauseMenuButtons[pauseMenuButton]->SetVisible(!disableSaving); - yOffset += m_PauseMenuButtons[pauseMenuButton]->GetHeight(); - } - - for (size_t pauseMenuButton = PauseMenuButton::SettingsButton; pauseMenuButton < PauseMenuButton::ButtonCount; ++pauseMenuButton) { - m_PauseMenuButtons[pauseMenuButton]->MoveRelative(0, yOffset * (disableSaving ? -1 : 1)); - } - m_PauseMenuBox->MoveRelative(0, yOffset / 2 * (disableSaving ? 1 : -1)); - } - m_SavingButtonsDisabled = disableSaving; - if (m_ModManagerButtonDisabled != disableModManager) { GUIButton *modManagerButton = m_PauseMenuButtons[PauseMenuButton::ModManagerButton]; @@ -143,9 +125,10 @@ namespace RTE { int yOffset = m_PauseMenuButtons[PauseMenuButton::ModManagerButton]->GetHeight(); m_PauseMenuButtons[PauseMenuButton::ResumeButton]->MoveRelative(0, yOffset * (disableModManager ? -1 : 1)); - m_PauseMenuBox->MoveRelative(0, yOffset / 2 * (disableSaving ? 1 : -1)); + m_PauseMenuBox->MoveRelative(0, yOffset / 2 * -1); + + m_ModManagerButtonDisabled = disableModManager; } - m_ModManagerButtonDisabled = disableModManager; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// From 65fef08fe3313a561a917fc76deb227f4d7e1a9f Mon Sep 17 00:00:00 2001 From: Causeless Date: Fri, 18 Aug 2023 16:56:01 +0100 Subject: [PATCH 04/24] Added order-by to save list --- Menus/SaveLoadMenuGUI.cpp | 20 +++++++++++++++++++- Menus/SaveLoadMenuGUI.h | 6 +++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Menus/SaveLoadMenuGUI.cpp b/Menus/SaveLoadMenuGUI.cpp index 20cf810204..321c6d6314 100644 --- a/Menus/SaveLoadMenuGUI.cpp +++ b/Menus/SaveLoadMenuGUI.cpp @@ -13,6 +13,7 @@ #include "GUIButton.h" #include "GUIListBox.h" #include "GUITextBox.h" +#include "GUIComboBox.h" namespace RTE { @@ -32,6 +33,12 @@ namespace RTE { saveGameMenuBox->CenterInParent(true, true); saveGameMenuBox->SetPositionAbs(saveGameMenuBox->GetXPos(), (rootBox->GetHeight() < 540) ? saveGameMenuBox->GetYPos() - 15 : 140); + m_OrderByComboBox = dynamic_cast(m_GUIControlManager->GetControl("ComboOrderBy")); + m_OrderByComboBox->AddItem("Name"); + m_OrderByComboBox->AddItem("Date"); + m_OrderByComboBox->AddItem("Activity"); + m_OrderByComboBox->SetSelectedIndex(1); //order by Date by default + m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMainMenu")); if (createForPauseMenu) { @@ -107,7 +114,14 @@ namespace RTE { void SaveLoadMenuGUI::UpdateSaveGamesGUIList() { - std::sort(m_SaveGames.begin(), m_SaveGames.end()); + const std::string& currentOrder = m_OrderByComboBox->GetSelectedItem()->m_Name; + if (currentOrder == "Name") { + std::stable_sort(m_SaveGames.begin(), m_SaveGames.end(), [](const SaveRecord& lhs, const SaveRecord& rhs) { return lhs.SavePath.stem().string() < rhs.SavePath.stem().string(); }); + } else if (currentOrder == "Date") { + std::stable_sort(m_SaveGames.begin(), m_SaveGames.end(), [](const SaveRecord& lhs, const SaveRecord& rhs) { return lhs.SaveDate > rhs.SaveDate; }); + } else if (currentOrder == "Activity") { + std::stable_sort(m_SaveGames.begin(), m_SaveGames.end(), [](const SaveRecord& lhs, const SaveRecord& rhs) { return lhs.Activity < rhs.Activity; }); + } m_SaveGamesListBox->ClearList(); for (int i = 0; i < m_SaveGames.size(); i++) { @@ -181,6 +195,10 @@ namespace RTE { //m_DescriptionLabel->SetText(record.GetDisplayString()); m_SaveGameName->SetText(record.SavePath.stem().string()); } + + if (guiEvent.GetControl() == m_OrderByComboBox && guiEvent.GetMsg() == GUIComboBox::Closed) { + UpdateSaveGamesGUIList(); + } } } return false; diff --git a/Menus/SaveLoadMenuGUI.h b/Menus/SaveLoadMenuGUI.h index d50eaaa331..fda8cee644 100644 --- a/Menus/SaveLoadMenuGUI.h +++ b/Menus/SaveLoadMenuGUI.h @@ -12,6 +12,7 @@ namespace RTE { class GUIButton; class GUIListBox; class GUITextBox; + class GUIComboBox; /// /// Integrated savegame user interface composition and handling. @@ -52,8 +53,6 @@ namespace RTE { std::filesystem::file_time_type SaveDate; //!< Last modified date. std::string Activity; //!< The activity name. std::string Scene; //!< The scene name. - - bool operator<(const SaveRecord &rhs) const { return SaveDate > rhs.SaveDate; } }; std::unique_ptr m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of the SaveLoadMenuGUI. @@ -70,7 +69,8 @@ namespace RTE { GUIButton *m_LoadButton; GUIButton *m_CreateButton; GUIListBox *m_SaveGamesListBox; - GUILabel *m_DescriptionLabel; + GUILabel* m_DescriptionLabel; + GUIComboBox *m_OrderByComboBox; #pragma region Mod and Script Handling /// From dd02bc41cacfd6e252198bb25add5a8b7991ecbd Mon Sep 17 00:00:00 2001 From: Causeless Date: Fri, 18 Aug 2023 19:46:34 +0100 Subject: [PATCH 05/24] Lots of fixes and improvements. Added save menu to main menu. Lots of QoL fixes. --- Menus/MainMenuGUI.cpp | 15 +++++++- Menus/MainMenuGUI.h | 7 +++- Menus/PauseMenuGUI.cpp | 8 +++- Menus/PauseMenuGUI.h | 5 +++ Menus/SaveLoadMenuGUI.cpp | 78 ++++++++++++++++++++++++++++++++++++--- Menus/SaveLoadMenuGUI.h | 25 ++++++++++--- 6 files changed, 122 insertions(+), 16 deletions(-) diff --git a/Menus/MainMenuGUI.cpp b/Menus/MainMenuGUI.cpp index 6ff5b6432c..7e22c8ec0f 100644 --- a/Menus/MainMenuGUI.cpp +++ b/Menus/MainMenuGUI.cpp @@ -38,6 +38,7 @@ namespace RTE { m_ResumeButtonBlinkTimer.Reset(); m_CreditsScrollTimer.Reset(); + m_SaveLoadMenu = nullptr; m_SettingsMenu = nullptr; m_ModManagerMenu = nullptr; @@ -81,6 +82,7 @@ namespace RTE { CreateCreditsScreen(); CreateQuitScreen(); + m_SaveLoadMenu = std::make_unique(guiScreen, guiInput); m_SettingsMenu = std::make_unique(guiScreen, guiInput); m_ModManagerMenu = std::make_unique(guiScreen, guiInput); @@ -97,6 +99,7 @@ namespace RTE { m_MainMenuButtons[MenuButton::MetaGameButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToMetaGame")); m_MainMenuButtons[MenuButton::ScenarioButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToSkirmish")); m_MainMenuButtons[MenuButton::MultiplayerButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToMultiplayer")); + m_MainMenuButtons[MenuButton::SaveOrLoadGameButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonSaveOrLoadGame")); m_MainMenuButtons[MenuButton::SettingsButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToOptions")); m_MainMenuButtons[MenuButton::ModManagerButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToModManager")); m_MainMenuButtons[MenuButton::EditorsButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToEditor")); @@ -104,7 +107,7 @@ namespace RTE { m_MainMenuButtons[MenuButton::QuitButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonQuit")); m_MainMenuButtons[MenuButton::ResumeButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonResume")); - for (int mainScreenButton = 0; mainScreenButton < 9; ++mainScreenButton) { + for (int mainScreenButton = MenuButton::MetaGameButton; mainScreenButton <= MenuButton::ResumeButton; ++mainScreenButton) { m_MainMenuButtons.at(mainScreenButton)->CenterInParent(true, false); std::string buttonText = m_MainMenuButtons.at(mainScreenButton)->GetText(); std::transform(buttonText.begin(), buttonText.end(), buttonText.begin(), ::toupper); @@ -336,6 +339,9 @@ namespace RTE { if (m_MenuScreenChange) { ShowMetaGameNoticeScreen(); } backToMainMenu = HandleInputEvents(); break; + case MenuScreen::SaveOrLoadGameScreen: + backToMainMenu = m_SaveLoadMenu->HandleInputEvents(); + break; case MenuScreen::SettingsScreen: backToMainMenu = m_SettingsMenu->HandleInputEvents(); m_ActiveDialogBox = m_SettingsMenu->GetActiveDialogBox(); @@ -440,6 +446,8 @@ namespace RTE { m_UpdateResult = MainMenuUpdateResult::ActivityStarted; g_GUISound.BackButtonPressSound()->Play(); g_ActivityMan.SetStartMultiplayerActivity(); + } else if (guiEventControl == m_MainMenuButtons[MenuButton::SaveOrLoadGameButton]) { + SetActiveMenuScreen(MenuScreen::SaveOrLoadGameScreen); } else if (guiEventControl == m_MainMenuButtons[MenuButton::SettingsButton]) { SetActiveMenuScreen(MenuScreen::SettingsScreen); } else if (guiEventControl == m_MainMenuButtons[MenuButton::EditorsButton]) { @@ -508,7 +516,7 @@ namespace RTE { void MainMenuGUI::UpdateMainScreenHoveredButton(const GUIButton *hoveredButton) { int hoveredButtonIndex = -1; if (hoveredButton) { - hoveredButtonIndex = std::distance(m_MainMenuButtons.begin(), std::find(m_MainMenuButtons.begin(), m_MainMenuButtons.begin() + 8, hoveredButton)); + hoveredButtonIndex = std::distance(m_MainMenuButtons.begin(), std::find(m_MainMenuButtons.begin(), m_MainMenuButtons.end(), hoveredButton)); if (hoveredButton != m_MainScreenHoveredButton) { m_MainMenuButtons.at(hoveredButtonIndex)->SetText(m_MainScreenButtonHoveredText.at(hoveredButtonIndex)); } m_MainScreenHoveredButton = m_MainMenuButtons.at(hoveredButtonIndex); } @@ -529,6 +537,9 @@ namespace RTE { return; } switch (m_ActiveMenuScreen) { + case MenuScreen::SaveOrLoadGameScreen: + m_SaveLoadMenu->Draw(); + break; case MenuScreen::SettingsScreen: m_SettingsMenu->Draw(); break; diff --git a/Menus/MainMenuGUI.h b/Menus/MainMenuGUI.h index 5fff148f03..18e0363219 100644 --- a/Menus/MainMenuGUI.h +++ b/Menus/MainMenuGUI.h @@ -75,6 +75,7 @@ namespace RTE { enum MenuScreen { MainScreen, MetaGameNoticeScreen, + SaveOrLoadGameScreen, SettingsScreen, ModManagerScreen, EditorScreen, @@ -90,6 +91,7 @@ namespace RTE { MetaGameButton, ScenarioButton, MultiplayerButton, + SaveOrLoadGameButton, SettingsButton, ModManagerButton, EditorsButton, @@ -124,13 +126,14 @@ namespace RTE { Timer m_ResumeButtonBlinkTimer; //!< Activity resume button blink timer. Timer m_CreditsScrollTimer; //!< Credits scrolling timer. + std::unique_ptr m_SaveLoadMenu; //!< The save/load menu screen. std::unique_ptr m_SettingsMenu; //!< The settings menu screen. std::unique_ptr m_ModManagerMenu; //!< The mod manager menu screen. // TODO: Rework this hacky garbage implementation when setting button font at runtime without loading a different skin is fixed. Would eliminate the need for a second GUIControlManager as well. // Right now the way this works is the font graphic has different character visuals for uppercase and lowercase and the visual change happens by applying the appropriate case string when hovering/unhovering. - std::array m_MainScreenButtonHoveredText; //!< Array containing uppercase strings of the main screen buttons text that are used to display the larger font when a button is hovered over. - std::array m_MainScreenButtonUnhoveredText; //!< Array containing lowercase strings of the main menu screen buttons text that are used to display the smaller font when a button is not hovered over. + std::array m_MainScreenButtonHoveredText; //!< Array containing uppercase strings of the main screen buttons text that are used to display the larger font when a button is hovered over. + std::array m_MainScreenButtonUnhoveredText; //!< Array containing lowercase strings of the main menu screen buttons text that are used to display the smaller font when a button is not hovered over. GUIButton *m_MainScreenHoveredButton; //!< The currently hovered main screen button. int m_MainScreenPrevHoveredButtonIndex; //!< The index of the previously hovered main screen button in the main menu button array. diff --git a/Menus/PauseMenuGUI.cpp b/Menus/PauseMenuGUI.cpp index 4ce4201661..7b0df36c57 100644 --- a/Menus/PauseMenuGUI.cpp +++ b/Menus/PauseMenuGUI.cpp @@ -152,6 +152,12 @@ namespace RTE { draw_trans_sprite(m_BackdropBitmap, g_FrameMan.GetOverlayBitmap32(), 0, 0); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void PauseMenuGUI::ClearBackdrop() { + clear_bitmap(m_BackdropBitmap); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// PauseMenuGUI::PauseMenuUpdateResult PauseMenuGUI::Update() { @@ -169,7 +175,7 @@ namespace RTE { BlinkResumeButton(); break; case PauseMenuScreen::SaveOrLoadGameScreen: - backToMainScreen = m_SaveLoadMenu->HandleInputEvents(); + backToMainScreen = m_SaveLoadMenu->HandleInputEvents(this); break; case PauseMenuScreen::SettingsScreen: backToMainScreen = m_SettingsMenu->HandleInputEvents(); diff --git a/Menus/PauseMenuGUI.h b/Menus/PauseMenuGUI.h index c4ae1d413f..1b41338bb1 100644 --- a/Menus/PauseMenuGUI.h +++ b/Menus/PauseMenuGUI.h @@ -67,6 +67,11 @@ namespace RTE { /// void StoreFrameForUseAsBackdrop(); + /// + /// Clears the current backdrop back to black. + /// + void ClearBackdrop(); + /// /// Updates the PauseMenuGUI state. /// diff --git a/Menus/SaveLoadMenuGUI.cpp b/Menus/SaveLoadMenuGUI.cpp index 321c6d6314..7d06b06d39 100644 --- a/Menus/SaveLoadMenuGUI.cpp +++ b/Menus/SaveLoadMenuGUI.cpp @@ -4,6 +4,10 @@ #include "PresetMan.h" #include "WindowMan.h" +#include "PauseMenuGUI.h" +#include "SettingsGUI.h" +#include "ModManagerGUI.h" + #include "GUI.h" #include "AllegroScreen.h" #include "GAScripted.h" @@ -56,7 +60,8 @@ namespace RTE { m_SaveGameName = dynamic_cast(m_GUIControlManager->GetControl("SaveGameName")); m_LoadButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonLoad")); m_CreateButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCreate")); - m_DescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelDescription")); + m_DeleteButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonDelete")); + m_ActivityCannotBeSavedLabel = dynamic_cast(m_GUIControlManager->GetControl("ActivityCannotBeSavedWarning")); m_SaveGamesFetched = false; } @@ -65,6 +70,9 @@ namespace RTE { void SaveLoadMenuGUI::PopulateSaveGamesList() { m_SaveGames.clear(); + m_SaveGameName->SetText(""); + + m_GUIControlManager->GetManager()->SetFocus(nullptr); std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/"; for (const auto &entry : std::filesystem::directory_iterator(saveFilePath)) { @@ -146,13 +154,16 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::LoadSave() { + bool SaveLoadMenuGUI::LoadSave() { bool success = g_ActivityMan.LoadAndLaunchGame(m_SaveGameName->GetText()); + if (success) { g_GUISound.ConfirmSound()->Play(); } else { g_GUISound.UserErrorSound()->Play(); } + + return success; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -164,15 +175,60 @@ namespace RTE { } else { g_GUISound.UserErrorSound()->Play(); } + PopulateSaveGamesList(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SaveLoadMenuGUI::HandleInputEvents() { + void SaveLoadMenuGUI::DeleteSave() { + std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + m_SaveGameName->GetText(); + + // TODO - it'd be nice to have this all zipped up into one file... + std::vector filePaths; + filePaths.emplace_back(saveFilePath + ".ini"); + filePaths.emplace_back(saveFilePath + " BG.png"); + filePaths.emplace_back(saveFilePath + " FG.png"); + filePaths.emplace_back(saveFilePath + " Mat.png"); + filePaths.emplace_back(saveFilePath + " UST1.png"); + filePaths.emplace_back(saveFilePath + " UST2.png"); + filePaths.emplace_back(saveFilePath + " UST3.png"); + filePaths.emplace_back(saveFilePath + " UST4.png"); + + std::for_each(std::execution::par_unseq, + filePaths.begin(), filePaths.end(), + [](const std::filesystem::path &path) { + std::filesystem::remove(path); + }); + + g_GUISound.ConfirmSound()->Play(); + + PopulateSaveGamesList(); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SaveLoadMenuGUI::UpdateButtonEnabledStates() { + bool allowSave = g_ActivityMan.GetActivityAllowsSaving() && m_SaveGameName->GetText() != ""; + bool allowLoad = m_SaveGamesListBox->GetSelectedIndex() > -1 && m_SaveGameName->GetText() != "" && m_SaveGameName->GetText() == m_SaveGames[m_SaveGamesListBox->GetSelected()->m_ExtraIndex].SavePath.stem().string(); + bool allowDelete = allowLoad; + + m_CreateButton->SetVisible(allowSave); + m_CreateButton->SetEnabled(allowSave); + m_LoadButton->SetEnabled(allowLoad); + m_DeleteButton->SetEnabled(allowDelete); + + m_ActivityCannotBeSavedLabel->SetVisible(g_ActivityMan.GetActivity() && !g_ActivityMan.GetActivityAllowsSaving()); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SaveLoadMenuGUI::HandleInputEvents(PauseMenuGUI *pauseMenu) { if (!ListsFetched()) { PopulateSaveGamesList(); + UpdateButtonEnabledStates(); } + m_GUIControlManager->Update(); GUIEvent guiEvent; @@ -181,9 +237,17 @@ namespace RTE { if (guiEvent.GetControl() == m_BackToMainButton) { return true; } else if (guiEvent.GetControl() == m_LoadButton) { - LoadSave(); + bool gameLoaded = LoadSave(); + if (gameLoaded) { + if (pauseMenu) { + pauseMenu->ClearBackdrop(); + } + return true; + } } else if (guiEvent.GetControl() == m_CreateButton) { CreateSave(); + } else if (guiEvent.GetControl() == m_DeleteButton) { + DeleteSave(); } } else if (guiEvent.GetType() == GUIEvent::Notification) { if (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl())) { @@ -191,8 +255,7 @@ namespace RTE { } if (guiEvent.GetControl() == m_SaveGamesListBox && (guiEvent.GetMsg() == GUIListBox::Select && m_SaveGamesListBox->GetSelectedIndex() > -1)) { - const SaveRecord &record = m_SaveGames.at(m_SaveGamesListBox->GetSelected()->m_ExtraIndex); - //m_DescriptionLabel->SetText(record.GetDisplayString()); + const SaveRecord &record = m_SaveGames[m_SaveGamesListBox->GetSelected()->m_ExtraIndex]; m_SaveGameName->SetText(record.SavePath.stem().string()); } @@ -201,6 +264,9 @@ namespace RTE { } } } + + UpdateButtonEnabledStates(); + return false; } diff --git a/Menus/SaveLoadMenuGUI.h b/Menus/SaveLoadMenuGUI.h index fda8cee644..92b2eff0b4 100644 --- a/Menus/SaveLoadMenuGUI.h +++ b/Menus/SaveLoadMenuGUI.h @@ -5,6 +5,8 @@ namespace RTE { + class PauseMenuGUI; + class AllegroScreen; class GUIInputWrapper; class GUIControlManager; @@ -35,8 +37,9 @@ namespace RTE { /// /// Handles the player interaction with the SaveLoadMenuGUI GUI elements. /// + /// Pointer to the pause menu, if we're being called from the pause menu. Ownership is NOT transferred! /// Whether the player requested to return to the main menu. - bool HandleInputEvents(); + bool HandleInputEvents(PauseMenuGUI *pauseMenu = nullptr); /// /// Draws the SaveLoadMenuGUI to the screen. @@ -68,15 +71,16 @@ namespace RTE { GUITextBox *m_SaveGameName; GUIButton *m_LoadButton; GUIButton *m_CreateButton; + GUIButton *m_DeleteButton; GUIListBox *m_SaveGamesListBox; - GUILabel* m_DescriptionLabel; + GUILabel *m_ActivityCannotBeSavedLabel; GUIComboBox *m_OrderByComboBox; -#pragma region Mod and Script Handling +#pragma region Savegame Handling /// /// Gets whether both lists were fetched, even if nothing valid was added to them. /// - /// Whether both lists were fetched, even if nothing valid was added to them. + /// Whether save games were fetched, even if nothing valid was added to them. bool ListsFetched() const { return m_SaveGamesFetched; } /// @@ -92,14 +96,25 @@ namespace RTE { /// /// Loads the currently selected savefile. /// - void LoadSave(); + /// Whether a same was succesfully loaded. + bool LoadSave(); /// /// Creates a new savefile (or overwrites the existing one) with the name from the textbox. /// void CreateSave(); + + /// + /// Deletes the savefile with the name from the textbox. + /// + void DeleteSave(); #pragma endregion + /// + /// Updates buttons and sets whether or not they should be enabled. + /// + void UpdateButtonEnabledStates(); + // Disallow the use of some implicit methods. SaveLoadMenuGUI(const SaveLoadMenuGUI &reference) = delete; SaveLoadMenuGUI & operator=(const SaveLoadMenuGUI &rhs) = delete; From 79e0a79d9d9ab5bfd7a0c66335fd12b5021f688d Mon Sep 17 00:00:00 2001 From: Causeless Date: Sat, 19 Aug 2023 15:46:13 +0100 Subject: [PATCH 06/24] Cleanup and consistency with listbox selection. Show "Overwrite" when overwriting vs creating a save. --- Menus/SaveLoadMenuGUI.cpp | 33 ++++++++++++++++++++++++++------- Menus/SaveLoadMenuGUI.h | 1 + 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Menus/SaveLoadMenuGUI.cpp b/Menus/SaveLoadMenuGUI.cpp index 7d06b06d39..081af05014 100644 --- a/Menus/SaveLoadMenuGUI.cpp +++ b/Menus/SaveLoadMenuGUI.cpp @@ -60,6 +60,7 @@ namespace RTE { m_SaveGameName = dynamic_cast(m_GUIControlManager->GetControl("SaveGameName")); m_LoadButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonLoad")); m_CreateButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCreate")); + m_OverwriteButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonOverwrite")); m_DeleteButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonDelete")); m_ActivityCannotBeSavedLabel = dynamic_cast(m_GUIControlManager->GetControl("ActivityCannotBeSavedWarning")); @@ -210,13 +211,31 @@ namespace RTE { void SaveLoadMenuGUI::UpdateButtonEnabledStates() { bool allowSave = g_ActivityMan.GetActivityAllowsSaving() && m_SaveGameName->GetText() != ""; - bool allowLoad = m_SaveGamesListBox->GetSelectedIndex() > -1 && m_SaveGameName->GetText() != "" && m_SaveGameName->GetText() == m_SaveGames[m_SaveGamesListBox->GetSelected()->m_ExtraIndex].SavePath.stem().string(); - bool allowDelete = allowLoad; - m_CreateButton->SetVisible(allowSave); - m_CreateButton->SetEnabled(allowSave); - m_LoadButton->SetEnabled(allowLoad); - m_DeleteButton->SetEnabled(allowDelete); + int existingSaveItemIndex = -1; + for (int i = 0; i < m_SaveGamesListBox->GetItemList()->size(); ++i) { + SaveRecord& save = m_SaveGames[m_SaveGamesListBox->GetItem(i)->m_ExtraIndex]; + if (save.SavePath.stem().string() == m_SaveGameName->GetText()) { + existingSaveItemIndex = i; + break; + } + } + + // Select the item in the list - selecting -1 unselects all + m_SaveGamesListBox->SetSelectedIndex(existingSaveItemIndex); + + bool saveExists = existingSaveItemIndex != -1; + + bool allowCreate = allowSave && !saveExists; + m_CreateButton->SetVisible(allowCreate); + m_CreateButton->SetEnabled(allowCreate); + + bool allowOverwrite = allowSave && saveExists; + m_OverwriteButton->SetVisible(allowOverwrite); + m_OverwriteButton->SetEnabled(allowOverwrite); + + m_LoadButton->SetEnabled(saveExists); + m_DeleteButton->SetEnabled(saveExists); m_ActivityCannotBeSavedLabel->SetVisible(g_ActivityMan.GetActivity() && !g_ActivityMan.GetActivityAllowsSaving()); } @@ -244,7 +263,7 @@ namespace RTE { } return true; } - } else if (guiEvent.GetControl() == m_CreateButton) { + } else if (guiEvent.GetControl() == m_CreateButton || guiEvent.GetControl() == m_OverwriteButton) { CreateSave(); } else if (guiEvent.GetControl() == m_DeleteButton) { DeleteSave(); diff --git a/Menus/SaveLoadMenuGUI.h b/Menus/SaveLoadMenuGUI.h index 92b2eff0b4..ff252e0319 100644 --- a/Menus/SaveLoadMenuGUI.h +++ b/Menus/SaveLoadMenuGUI.h @@ -71,6 +71,7 @@ namespace RTE { GUITextBox *m_SaveGameName; GUIButton *m_LoadButton; GUIButton *m_CreateButton; + GUIButton *m_OverwriteButton; GUIButton *m_DeleteButton; GUIListBox *m_SaveGamesListBox; GUILabel *m_ActivityCannotBeSavedLabel; From 1f10ab71bd0bdc0fff088ead70c48c880d55a13e Mon Sep 17 00:00:00 2001 From: Causeless Date: Sat, 19 Aug 2023 16:28:58 +0100 Subject: [PATCH 07/24] Added confirmation dialog for overwrite/delete --- Menus/SaveLoadMenuGUI.cpp | 56 ++++++++++++++++++++++++++++++++++----- Menus/SaveLoadMenuGUI.h | 20 ++++++++++++++ 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/Menus/SaveLoadMenuGUI.cpp b/Menus/SaveLoadMenuGUI.cpp index 081af05014..e39d3d8e14 100644 --- a/Menus/SaveLoadMenuGUI.cpp +++ b/Menus/SaveLoadMenuGUI.cpp @@ -33,9 +33,9 @@ namespace RTE { GUICollectionBox *rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); rootBox->Resize(rootBoxMaxWidth, g_WindowMan.GetResY()); - GUICollectionBox *saveGameMenuBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSaveGameMenu")); - saveGameMenuBox->CenterInParent(true, true); - saveGameMenuBox->SetPositionAbs(saveGameMenuBox->GetXPos(), (rootBox->GetHeight() < 540) ? saveGameMenuBox->GetYPos() - 15 : 140); + m_SaveGameMenuBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSaveGameMenu")); + m_SaveGameMenuBox->CenterInParent(true, true); + m_SaveGameMenuBox->SetPositionAbs(m_SaveGameMenuBox->GetXPos(), (rootBox->GetHeight() < 540) ? m_SaveGameMenuBox->GetYPos() - 15 : 140); m_OrderByComboBox = dynamic_cast(m_GUIControlManager->GetControl("ComboOrderBy")); m_OrderByComboBox->AddItem("Name"); @@ -49,7 +49,7 @@ namespace RTE { m_BackToMainButton->SetSize(120, 20); m_BackToMainButton->SetText("Back to Pause Menu"); } - m_BackToMainButton->SetPositionAbs((rootBox->GetWidth() - m_BackToMainButton->GetWidth()) / 2, saveGameMenuBox->GetYPos() + saveGameMenuBox->GetHeight() + 10); + m_BackToMainButton->SetPositionAbs((rootBox->GetWidth() - m_BackToMainButton->GetWidth()) / 2, m_SaveGameMenuBox->GetYPos() + m_SaveGameMenuBox->GetHeight() + 10); m_SaveGamesListBox = dynamic_cast(m_GUIControlManager->GetControl("ListBoxSaveGames")); m_SaveGamesListBox->SetFont(m_GUIControlManager->GetSkin()->GetFont("FontConsoleMonospace.png")); @@ -64,6 +64,15 @@ namespace RTE { m_DeleteButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonDelete")); m_ActivityCannotBeSavedLabel = dynamic_cast(m_GUIControlManager->GetControl("ActivityCannotBeSavedWarning")); + m_ConfirmationBox = dynamic_cast(m_GUIControlManager->GetControl("ConfirmDialog")); + m_ConfirmationBox->CenterInParent(true, true); + + m_ConfirmationLabel = dynamic_cast(m_GUIControlManager->GetControl("ConfirmLabel")); + m_ConfirmationButton = dynamic_cast(m_GUIControlManager->GetControl("ConfirmButton")); + m_CancelButton = dynamic_cast(m_GUIControlManager->GetControl("CancelButton")); + + SwitchToConfirmDialogMode(ConfirmDialogMode::None); + m_SaveGamesFetched = false; } @@ -240,6 +249,27 @@ namespace RTE { m_ActivityCannotBeSavedLabel->SetVisible(g_ActivityMan.GetActivity() && !g_ActivityMan.GetActivityAllowsSaving()); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SaveLoadMenuGUI::SwitchToConfirmDialogMode(ConfirmDialogMode mode) + { + m_ConfirmDialogMode = mode; + + bool dialogOpen = m_ConfirmDialogMode != ConfirmDialogMode::None; + m_SaveGameMenuBox->SetEnabled(!dialogOpen); + m_ConfirmationBox->SetEnabled(dialogOpen); + m_ConfirmationBox->SetVisible(dialogOpen); + + switch (m_ConfirmDialogMode) { + case ConfirmDialogMode::ConfirmOverwrite: + m_ConfirmationLabel->SetText("Are you sure you want to overwrite this savegame?"); + break; + case ConfirmDialogMode::ConfirmDelete: + m_ConfirmationLabel->SetText("Are you sure you want to delete this savegame?"); + break; + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SaveLoadMenuGUI::HandleInputEvents(PauseMenuGUI *pauseMenu) { @@ -263,10 +293,24 @@ namespace RTE { } return true; } - } else if (guiEvent.GetControl() == m_CreateButton || guiEvent.GetControl() == m_OverwriteButton) { + } else if (guiEvent.GetControl() == m_CreateButton) { CreateSave(); + } else if (guiEvent.GetControl() == m_OverwriteButton) { + SwitchToConfirmDialogMode(ConfirmDialogMode::ConfirmOverwrite); } else if (guiEvent.GetControl() == m_DeleteButton) { - DeleteSave(); + SwitchToConfirmDialogMode(ConfirmDialogMode::ConfirmDelete); + } else if (guiEvent.GetControl() == m_ConfirmationButton) { + switch (m_ConfirmDialogMode) { + case ConfirmDialogMode::ConfirmOverwrite: + CreateSave(); + break; + case ConfirmDialogMode::ConfirmDelete: + DeleteSave(); + break; + } + SwitchToConfirmDialogMode(ConfirmDialogMode::None); + } else if (guiEvent.GetControl() == m_CancelButton) { + SwitchToConfirmDialogMode(ConfirmDialogMode::None); } } else if (guiEvent.GetType() == GUIEvent::Notification) { if (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl())) { diff --git a/Menus/SaveLoadMenuGUI.h b/Menus/SaveLoadMenuGUI.h index ff252e0319..bb567e808d 100644 --- a/Menus/SaveLoadMenuGUI.h +++ b/Menus/SaveLoadMenuGUI.h @@ -15,6 +15,7 @@ namespace RTE { class GUIListBox; class GUITextBox; class GUIComboBox; + class GUICollectionBox; /// /// Integrated savegame user interface composition and handling. @@ -48,6 +49,12 @@ namespace RTE { #pragma endregion private: + enum class ConfirmDialogMode { + None, + ConfirmOverwrite, + ConfirmDelete + }; + /// /// Struct containing information about a valid Savegame. /// @@ -67,6 +74,7 @@ namespace RTE { /// /// GUI elements that compose the Mod Manager menu screen. /// + GUICollectionBox *m_SaveGameMenuBox; GUIButton *m_BackToMainButton; GUITextBox *m_SaveGameName; GUIButton *m_LoadButton; @@ -77,6 +85,13 @@ namespace RTE { GUILabel *m_ActivityCannotBeSavedLabel; GUIComboBox *m_OrderByComboBox; + // The confirmation box and its controls + ConfirmDialogMode m_ConfirmDialogMode; + GUICollectionBox *m_ConfirmationBox; + GUILabel *m_ConfirmationLabel; + GUIButton* m_ConfirmationButton; + GUIButton *m_CancelButton; + #pragma region Savegame Handling /// /// Gets whether both lists were fetched, even if nothing valid was added to them. @@ -116,6 +131,11 @@ namespace RTE { /// void UpdateButtonEnabledStates(); + /// + /// Shows confirmation box for overwrite or delete. + /// + void SwitchToConfirmDialogMode(ConfirmDialogMode mode); + // Disallow the use of some implicit methods. SaveLoadMenuGUI(const SaveLoadMenuGUI &reference) = delete; SaveLoadMenuGUI & operator=(const SaveLoadMenuGUI &rhs) = delete; From bc69c680e3315837155a4da9927b30fdd8e0fd62 Mon Sep 17 00:00:00 2001 From: Causeless Date: Sat, 19 Aug 2023 17:07:21 +0100 Subject: [PATCH 08/24] Fixed spacing --- Menus/SaveLoadMenuGUI.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Menus/SaveLoadMenuGUI.cpp b/Menus/SaveLoadMenuGUI.cpp index e39d3d8e14..b6454d76bb 100644 --- a/Menus/SaveLoadMenuGUI.cpp +++ b/Menus/SaveLoadMenuGUI.cpp @@ -251,8 +251,7 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::SwitchToConfirmDialogMode(ConfirmDialogMode mode) - { + void SaveLoadMenuGUI::SwitchToConfirmDialogMode(ConfirmDialogMode mode) { m_ConfirmDialogMode = mode; bool dialogOpen = m_ConfirmDialogMode != ConfirmDialogMode::None; From 2f29d79daa67fa9b882556fe2759bebcef1e5ce8 Mon Sep 17 00:00:00 2001 From: Causeless Date: Sun, 20 Aug 2023 13:30:24 +0100 Subject: [PATCH 09/24] Hopefully fix build --- Menus/SaveLoadMenuGUI.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Menus/SaveLoadMenuGUI.cpp b/Menus/SaveLoadMenuGUI.cpp index b6454d76bb..f18d5244b6 100644 --- a/Menus/SaveLoadMenuGUI.cpp +++ b/Menus/SaveLoadMenuGUI.cpp @@ -149,8 +149,18 @@ namespace RTE { saveNameText << std::left << std::setfill(' ') << std::setw(32) << save.SavePath.stem().string(); // This is so much more fucking difficult than it has any right to be +#if __cpp_lib_chrono >= 201907L const auto saveFsTime = std::chrono::clock_cast(save.SaveDate); const auto saveTime = std::chrono::system_clock::to_time_t(saveFsTime); +#else + // TODO - kill this monstrosity when we move to GCC13 + auto saveFsTime = std::chrono::system_clock::time_point(save.SaveDate.time_since_epoch()); + #ifdef _WIN32 + // Windows epoch time are the number of seconds since... 1601-01-01 00:00:00. Seriously. + saveFsTime -= std::chrono::seconds(11644473600LL); + #endif + const auto saveTime = std::chrono::system_clock::to_time_t(saveFsTime); +#endif const auto saveTimeLocal = std::localtime(&saveTime); std::stringstream saveDateTimeText; From 7656ea9d2cabb3e7b329dc88b3d5214d08dcbfa1 Mon Sep 17 00:00:00 2001 From: Causeless Date: Sun, 20 Aug 2023 14:20:40 +0100 Subject: [PATCH 10/24] Added read/write with arbitrary streams --- System/Reader.cpp | 34 ++++++++++++++++++++++++++++++---- System/Reader.h | 29 ++++++++++++++++++++++++----- System/Writer.cpp | 26 ++++++++++++++++++++++++-- System/Writer.h | 21 +++++++++++++++++---- 4 files changed, 95 insertions(+), 15 deletions(-) diff --git a/System/Reader.cpp b/System/Reader.cpp index 302f497c87..7160a93386 100644 --- a/System/Reader.cpp +++ b/System/Reader.cpp @@ -26,6 +26,21 @@ namespace RTE { m_NonModulePath = false; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Reader::Reader(const std::string &fileName, bool overwrites, const ProgressCallback &progressCallback, bool failOK, bool nonModulePath) { + Clear(); + m_NonModulePath = nonModulePath; + Create(fileName, overwrites, progressCallback, failOK); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Reader::Reader(std::unique_ptr &&stream, bool overwrites, const ProgressCallback &progressCallback, bool failOK) { + Clear(); + Create(std::move(stream), overwrites, progressCallback, failOK); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Reader::Create(const std::string &fileName, bool overwrites, const ProgressCallback &progressCallback, bool failOK) { @@ -47,16 +62,27 @@ namespace RTE { m_DataModuleID = g_PresetMan.GetModuleID(m_DataModuleName); } + return Create(std::make_unique(m_FilePath), overwrites, progressCallback, failOK); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Reader::Create(std::unique_ptr &&stream, bool overwrites, const ProgressCallback &progressCallback, bool failOK) { m_CanFail = failOK; - m_Stream = std::make_unique(m_FilePath); - if (!m_CanFail) { RTEAssert(System::PathExistsCaseSensitive(m_FilePath) && m_Stream->good(), "Failed to open data file \"" + m_FilePath + "\"!"); } + m_Stream = std::move(stream); + + if (!m_CanFail) { + RTEAssert(System::PathExistsCaseSensitive(m_FilePath) && m_Stream->good(), "Failed to open data file \"" + m_FilePath + "\"!"); + } m_OverwriteExisting = overwrites; // Report that we're starting a new file m_ReportProgress = progressCallback; - if (m_ReportProgress && m_Stream->good()) { m_ReportProgress("\t" + m_FileName + " on line " + std::to_string(m_CurrentLine), true); } + if (m_ReportProgress && m_Stream->good()) { + m_ReportProgress("\t" + m_FileName + " on line " + std::to_string(m_CurrentLine), true); + } return m_Stream->good() ? 0 : -1; } @@ -311,7 +337,7 @@ namespace RTE { if (m_Stream->fail() || !System::PathExistsCaseSensitive(includeFilePath)) { // Backpedal and set up to read the next property in the old stream - m_Stream.reset(m_StreamStack.top().Stream); // Destructs the current m_Stream and takes back ownership and management of the raw StreamInfo std::ifstream pointer. + m_Stream.reset(m_StreamStack.top().Stream); // Destructs the current m_Stream and takes back ownership and management of the raw StreamInfo std::istream pointer. m_FilePath = m_StreamStack.top().FilePath; m_CurrentLine = m_StreamStack.top().CurrentLine; m_PreviousIndent = m_StreamStack.top().PreviousIndent; diff --git a/System/Reader.h b/System/Reader.h index 7fe6f7052e..8848d3f39a 100644 --- a/System/Reader.h +++ b/System/Reader.h @@ -26,7 +26,16 @@ namespace RTE { /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. /// Whether this Reader is reading from path that is not a DataModule and should just read it as provided. - Reader(const std::string &fileName, bool overwrites = false, const ProgressCallback &progressCallback = nullptr, bool failOK = false, bool nonModulePath = false) { Clear(); m_NonModulePath = nonModulePath; Create(fileName, overwrites, progressCallback, failOK); } + Reader(const std::string &fileName, bool overwrites = false, const ProgressCallback &progressCallback = nullptr, bool failOK = false, bool nonModulePath = false); + + /// + /// Constructor method used to instantiate a Reader object in system memory and make it ready for reading from the passed in file path. + /// + /// Stream to read from. + /// Whether object definitions read here overwrite existing ones with the same names. + /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. + /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. + Reader(std::unique_ptr &&stream, bool overwrites = false, const ProgressCallback &progressCallback = nullptr, bool failOK = false); /// /// Makes the Reader object ready for use. @@ -37,6 +46,16 @@ namespace RTE { /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const std::string &fileName, bool overwrites = false, const ProgressCallback &progressCallback = nullptr, bool failOK = false); + + /// + /// Makes the Reader object ready for use. + /// + /// Stream to read from. + /// Whether object definitions read here overwrite existing ones with the same names. + /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. + /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(std::unique_ptr &&stream, bool overwrites = false, const ProgressCallback &progressCallback = nullptr, bool failOK = false); #pragma endregion #pragma region Getters and Setters @@ -152,7 +171,7 @@ namespace RTE { /// Shows whether this is still OK to read from. If file isn't present, etc, this will return false. /// /// Whether this Reader's stream is OK or not. - bool ReaderOK() const { return m_Stream.get() && !m_Stream->fail() && m_Stream->is_open(); } + bool ReaderOK() const { return m_Stream.get() && m_Stream->good(); } /// /// Makes an error message box pop up for the user that tells them something went wrong with the reading, and where. @@ -192,16 +211,16 @@ namespace RTE { /// /// Constructor method used to instantiate a StreamInfo object in system memory. /// - StreamInfo(std::ifstream *stream, const std::string &filePath, int currentLine, int prevIndent) : Stream(stream), FilePath(filePath), CurrentLine(currentLine), PreviousIndent(prevIndent) {} + StreamInfo(std::istream *stream, const std::string &filePath, int currentLine, int prevIndent) : Stream(stream), FilePath(filePath), CurrentLine(currentLine), PreviousIndent(prevIndent) {} // NOTE: These members are owned by the reader that owns this struct, so are not deleted when this is destroyed. - std::ifstream *Stream; //!< Currently used stream, is not on the StreamStack until a new stream is opened. + std::istream *Stream; //!< Currently used stream, is not on the StreamStack until a new stream is opened. std::string FilePath; //!< Currently used stream's filepath. int CurrentLine; //!< The line number the stream is on. int PreviousIndent; //!< Count of tabs encountered on the last line DiscardEmptySpace() discarded. }; - std::unique_ptr m_Stream; //!< Currently used stream, is not on the StreamStack until a new stream is opened. + std::unique_ptr m_Stream; //!< Currently used stream, is not on the StreamStack until a new stream is opened. std::stack m_StreamStack; //!< Stack of open streams in this Reader, each one representing a file opened to read from within another. bool m_EndOfStreams; //!< All streams have been depleted. diff --git a/System/Writer.cpp b/System/Writer.cpp index d3db4470ac..9813ab21fa 100644 --- a/System/Writer.cpp +++ b/System/Writer.cpp @@ -13,6 +13,20 @@ namespace RTE { m_IndentCount = 0; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Writer::Writer(const std::string &fileName, bool append, bool createDir) { + Clear(); + Create(fileName, append, createDir); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Writer::Writer(std::unique_ptr &&stream) { + Clear(); + Create(std::move(stream)); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Writer::Create(const std::string &fileName, bool append, bool createDir) { @@ -23,13 +37,21 @@ namespace RTE { m_FileName = m_FilePath.substr(slashPos + 1); m_FolderPath = m_FilePath.substr(0, slashPos + 1); - if (createDir && !std::filesystem::exists(System::GetWorkingDirectory() + m_FolderPath)) { System::MakeDirectory(System::GetWorkingDirectory() + m_FolderPath); } + if (createDir && !std::filesystem::exists(System::GetWorkingDirectory() + m_FolderPath)) { + System::MakeDirectory(System::GetWorkingDirectory() + m_FolderPath); + } + + return Create(std::make_unique(fileName, append ? (std::ios::out | std::ios::app | std::ios::ate) : (std::ios::out | std::ios::trunc))); + } - m_Stream = std::make_unique(fileName, append ? (std::ios::out | std::ios::app | std::ios::ate) : (std::ios::out | std::ios::trunc)); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int Writer::Create(std::unique_ptr &&stream) { + m_Stream = std::move(stream); if (!m_Stream->good()) { return -1; } + return 0; } diff --git a/System/Writer.h b/System/Writer.h index 4241f97a9b..939cfba631 100644 --- a/System/Writer.h +++ b/System/Writer.h @@ -22,7 +22,13 @@ namespace RTE { /// Path to the file to open for writing. If the directory doesn't exist the stream will fail to open. /// Whether to append to the file if it exists, or to overwrite it. /// Whether to create the directory path to the file name before attempting to open the stream, in case it doesn't exist. - Writer(const std::string &fileName, bool append = false, bool createDir = false) { Clear(); Create(fileName, append, createDir); } + Writer(const std::string &fileName, bool append = false, bool createDir = false); + + /// + /// Constructor method used to instantiate a Writer object in system memory and make it ready for writing to the passed in file path. + /// + /// Stream to write to. + Writer(std::unique_ptr &&stream); /// /// Makes the Writer object ready for use. @@ -32,6 +38,13 @@ namespace RTE { /// Whether to create the directory path to the file name before attempting to open the stream, in case it doesn't exist. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const std::string &fileName, bool append = false, bool createDir = false); + + /// + /// Makes the Writer object ready for use. + /// + /// Stream to write to. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(std::unique_ptr &&stream); #pragma endregion #pragma region Getters @@ -111,12 +124,12 @@ namespace RTE { /// Shows whether the writer is ready to start accepting data streamed to it. /// /// Whether the writer is ready to start accepting data streamed to it or not. - bool WriterOK() const { return m_Stream.get() && !m_Stream->fail() && m_Stream->is_open(); } + bool WriterOK() const { return m_Stream.get() && m_Stream->good(); } /// /// Flushes and closes the output stream of this Writer. This happens automatically at destruction but needs to be called manually if a written file must be read from in the same scope. /// - void EndWrite() const { m_Stream->flush(); m_Stream->close(); } + void EndWrite() { m_Stream->flush(); m_Stream.reset(); } #pragma endregion #pragma region Operator Overloads @@ -144,7 +157,7 @@ namespace RTE { protected: - std::unique_ptr m_Stream; //!< Stream used for writing to files. + std::unique_ptr m_Stream; //!< Stream used for writing. std::string m_FilePath; //!< Currently used stream's filepath. std::string m_FolderPath; //!< Only the path to the folder that we are writing a file in, excluding the filename. std::string m_FileName; //!< Only the name of the currently read file, excluding the path. From 44a0d78a58d16373008434dc423b02abacded412 Mon Sep 17 00:00:00 2001 From: Causeless Date: Sun, 20 Aug 2023 15:30:52 +0100 Subject: [PATCH 11/24] Prototyping for zip saves... --- Entities/Scene.cpp | 23 +++++++++++++ Entities/Scene.h | 7 ++++ Entities/SceneLayer.cpp | 12 +++++++ Entities/SceneLayer.h | 6 ++++ Managers/ActivityMan.cpp | 33 ++++++++++++++++--- System/Writer.h | 5 +++ .../addons/loadpng/loadpng.h | 3 ++ .../addons/loadpng/savepng.c | 14 ++++++++ 8 files changed, 99 insertions(+), 4 deletions(-) diff --git a/Entities/Scene.cpp b/Entities/Scene.cpp index 4d041f391d..769d471bda 100644 --- a/Entities/Scene.cpp +++ b/Entities/Scene.cpp @@ -1066,6 +1066,29 @@ int Scene::SaveData(std::string pathBase, bool doAsyncSaves) return 0; } +std::vector Scene::GetCopiedSceneLayerBitmaps() { + std::vector layerInfos; + + /* + // Save Terrain's data + m_pTerrain + + // Don't bother saving background layers to disk, as they are never altered + + // Save unseen layers' data + char str[64]; + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) + { + if (m_apUnseenLayer[team]) + { + m_apUnseenLayer[team] + } + } + */ + + return layerInfos; +} + ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: SavePreview diff --git a/Entities/Scene.h b/Entities/Scene.h index 552e38c496..a88158a615 100644 --- a/Entities/Scene.h +++ b/Entities/Scene.h @@ -32,6 +32,11 @@ class BunkerAssembly; class SceneObject; class Deployment; +struct SceneLayerInfo { + std::string name; + std::unique_ptr bitmap; +}; + ////////////////////////////////////////////////////////////////////////////////////////// // Class: Scene @@ -397,6 +402,8 @@ EntityAllocation(Scene) int SaveData(std::string pathBase, bool doAsyncSaves = true); + std::vector Scene::GetCopiedSceneLayerBitmaps() + ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: SavePreview diff --git a/Entities/SceneLayer.cpp b/Entities/SceneLayer.cpp index d4e9255d7a..6011e2ff74 100644 --- a/Entities/SceneLayer.cpp +++ b/Entities/SceneLayer.cpp @@ -235,6 +235,18 @@ namespace RTE { return 0; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + template + std::unique_ptr SceneLayerImpl::CopyBitmap() { + BITMAP* outputBitmap = create_bitmap_ex(bitmap_color_depth(m_MainBitmap), m_MainBitmap->w, m_MainBitmap->h); + if (m_MainBitmap) { + outputBitmap = create_bitmap_ex(bitmap_color_depth(m_MainBitmap), m_MainBitmap->w, m_MainBitmap->h); + blit(m_MainBitmap, outputBitmap, 0, 0, 0, 0, m_MainBitmap->w, m_MainBitmap->h); + } + return std::unique_ptr(outputBitmap); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template diff --git a/Entities/SceneLayer.h b/Entities/SceneLayer.h index 6592498d19..667b33743e 100644 --- a/Entities/SceneLayer.h +++ b/Entities/SceneLayer.h @@ -112,6 +112,12 @@ namespace RTE { /// /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int ClearData(); + + /// + /// Copies the bitmap. + /// + /// The copied bitmap. + std::unique_ptr CopyBitmap(); #pragma endregion #pragma region Getters and Setters diff --git a/Managers/ActivityMan.cpp b/Managers/ActivityMan.cpp index 3c75ffbeec..c08e65c4a7 100644 --- a/Managers/ActivityMan.cpp +++ b/Managers/ActivityMan.cpp @@ -25,6 +25,9 @@ #include "MultiplayerServerLobby.h" #include "MultiplayerGame.h" +#include "zip.h" +#include "unzip.h" + namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -105,8 +108,17 @@ namespace RTE { modifiableScene->GetTerrain()->SetPresetName(fileName); modifiableScene->GetTerrain()->MigrateToModule(g_PresetMan.GetModuleID(c_UserScriptedSavesModuleName)); + // Create zip sav file + zipFile zippedSaveFile = zipOpen((g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + fileName + ".ccsave").c_str(), APPEND_STATUS_CREATE); + if (!zippedSaveFile) { + g_ConsoleMan.PrintString("ERROR: Couldn't create zip save file!"); + return false; + } + + std::unique_ptr iniStream = std::make_unique(); + // Block the main thread for a bit to let the Writer access the relevant data. - std::unique_ptr writer(std::make_unique(g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + fileName + ".ini")); + std::unique_ptr writer(std::make_unique(std::move(iniStream))); writer->NewPropertyWithValue("Activity", activity); // Pull all stuff from MovableMan into the Scene for saving, so existing Actors/ADoors are saved, without transferring ownership, so the game can continue. @@ -123,9 +135,22 @@ namespace RTE { writer->NewPropertyWithValue("PlaceUnitsIfSceneIsRestarted", g_SceneMan.GetPlaceUnitsOnLoad()); writer->NewPropertyWithValue("Scene", modifiableScene.get()); - auto saveWriterData = [this](std::unique_ptr writerToSave) { - // Explicitly flush to disk. This'll happen anyways at the end of this scope, but otherwise this lambda looks rather empty :) - writerToSave->EndWrite(); + auto saveWriterData = [&](std::unique_ptr writerToSave) { + std::stringstream *stream = static_cast(writerToSave->GetStream()); + stream->flush(); + + // Ugly copies, but eh. todo - use a string stream that just gives us a raw buffer to grab at + std::string streamAsString = stream->str(); + + zip_fileinfo zfi = { 0 }; + + const int defaultCompression = 6; + zipOpenNewFileInZip(zippedSaveFile, (fileName + ".ini").c_str(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, defaultCompression); + zipWriteInFileInZip(zippedSaveFile, streamAsString.data(), streamAsString.size()); + zipCloseFileInZip(zippedSaveFile); + + zipClose(zippedSaveFile, fileName.c_str()); + DecrementSavingThreadCount(); }; diff --git a/System/Writer.h b/System/Writer.h index 939cfba631..07a6c4996e 100644 --- a/System/Writer.h +++ b/System/Writer.h @@ -126,6 +126,11 @@ namespace RTE { /// Whether the writer is ready to start accepting data streamed to it or not. bool WriterOK() const { return m_Stream.get() && m_Stream->good(); } + /// + /// Returns the underlying stream. + /// + std::ostream * GetStream() { return m_Stream.get(); } + /// /// Flushes and closes the output stream of this Writer. This happens automatically at destruction but needs to be called manually if a written file must be read from in the same scope. /// diff --git a/external/sources/allegro 4.4.3.1-custom/addons/loadpng/loadpng.h b/external/sources/allegro 4.4.3.1-custom/addons/loadpng/loadpng.h index 35111d6e04..4aedb0cbb9 100644 --- a/external/sources/allegro 4.4.3.1-custom/addons/loadpng/loadpng.h +++ b/external/sources/allegro 4.4.3.1-custom/addons/loadpng/loadpng.h @@ -66,6 +66,9 @@ APNG_FUNC(BITMAP *, load_memory_png, (AL_CONST void *buffer, int buffer_size, RG /* Save a bitmap to disk in PNG format. */ APNG_FUNC(int, save_png, (AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal)); +/* Save a bitmap to a PACKFILE in PNG format. */ +APNG_FUNC(int, save_png_pf, (PACKFILE *pack, BITMAP *bmp, AL_CONST RGB *pal)); + /* Adds `PNG' to Allegro's internal file type table. * You can then just use load_bitmap and save_bitmap as usual. */ diff --git a/external/sources/allegro 4.4.3.1-custom/addons/loadpng/savepng.c b/external/sources/allegro 4.4.3.1-custom/addons/loadpng/savepng.c index 527494474b..874e4eec34 100644 --- a/external/sources/allegro 4.4.3.1-custom/addons/loadpng/savepng.c +++ b/external/sources/allegro 4.4.3.1-custom/addons/loadpng/savepng.c @@ -313,3 +313,17 @@ int save_png(AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal) return result; } + +int save_png_pf(PACKFILE *pack, BITMAP *bmp, AL_CONST RGB *pal) +{ + int result; + + ASSERT(pack); + ASSERT(bmp); + + acquire_bitmap(bmp); + result = really_save_png(pack, bmp, pal); + release_bitmap(bmp); + + return result; +} From d621aa615d2d848ff8e23b855e0e64f33dfc1b3e Mon Sep 17 00:00:00 2001 From: Causeless Date: Mon, 13 Nov 2023 13:49:01 +0000 Subject: [PATCH 12/24] Fix compile error --- Entities/Scene.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/Scene.h b/Entities/Scene.h index 49021194f9..87c9aab2c3 100644 --- a/Entities/Scene.h +++ b/Entities/Scene.h @@ -408,7 +408,7 @@ EntityAllocation(Scene) int SaveData(std::string pathBase, bool doAsyncSaves = true); - std::vector Scene::GetCopiedSceneLayerBitmaps() + std::vector GetCopiedSceneLayerBitmaps(); ////////////////////////////////////////////////////////////////////////////////////////// From b22c5ab241f040a16ef502a8b5f2bca125368060 Mon Sep 17 00:00:00 2001 From: HeliumAnt Date: Sat, 20 Jan 2024 20:23:39 +0100 Subject: [PATCH 13/24] format --- Source/Activities/ActorEditor.cpp | 513 +- Source/Activities/ActorEditor.h | 458 +- Source/Activities/AreaEditor.cpp | 1199 +- Source/Activities/AreaEditor.h | 533 +- Source/Activities/AssemblyEditor.cpp | 1240 +- Source/Activities/AssemblyEditor.h | 550 +- Source/Activities/BaseEditor.cpp | 688 +- Source/Activities/BaseEditor.h | 409 +- Source/Activities/EditorActivity.cpp | 1082 +- Source/Activities/EditorActivity.h | 662 +- Source/Activities/GAScripted.cpp | 852 +- Source/Activities/GAScripted.h | 585 +- Source/Activities/GATutorial.cpp | 1891 ++- Source/Activities/GATutorial.h | 613 +- Source/Activities/GameActivity.cpp | 5196 ++++--- Source/Activities/GameActivity.h | 2167 ++- Source/Activities/GibEditor.cpp | 1547 +-- Source/Activities/GibEditor.h | 573 +- Source/Activities/MultiplayerGame.cpp | 152 +- Source/Activities/MultiplayerGame.h | 72 +- Source/Activities/MultiplayerServerLobby.cpp | 746 +- Source/Activities/MultiplayerServerLobby.h | 114 +- Source/Activities/SceneEditor.cpp | 1419 +- Source/Activities/SceneEditor.h | 543 +- Source/Entities/ACDropShip.cpp | 1071 +- Source/Entities/ACDropShip.h | 660 +- Source/Entities/ACRocket.cpp | 1036 +- Source/Entities/ACRocket.h | 607 +- Source/Entities/ACrab.cpp | 3309 ++--- Source/Entities/ACrab.h | 1211 +- Source/Entities/ACraft.cpp | 1832 ++- Source/Entities/ACraft.h | 1282 +- Source/Entities/ADSensor.cpp | 30 +- Source/Entities/ADSensor.h | 15 +- Source/Entities/ADoor.cpp | 243 +- Source/Entities/ADoor.h | 46 +- Source/Entities/AEJetpack.cpp | 137 +- Source/Entities/AEJetpack.h | 415 +- Source/Entities/AEmitter.cpp | 1124 +- Source/Entities/AEmitter.h | 1449 +- Source/Entities/AHuman.cpp | 5433 ++++---- Source/Entities/AHuman.h | 2138 ++- Source/Entities/Activity.cpp | 219 +- Source/Entities/Activity.h | 113 +- Source/Entities/Actor.cpp | 3226 +++-- Source/Entities/Actor.h | 3198 +++-- Source/Entities/Arm.cpp | 115 +- Source/Entities/Arm.h | 80 +- Source/Entities/AtomGroup.cpp | 440 +- Source/Entities/AtomGroup.h | 93 +- Source/Entities/Attachable.cpp | 260 +- Source/Entities/Attachable.h | 91 +- Source/Entities/BunkerAssembly.cpp | 852 +- Source/Entities/BunkerAssembly.h | 487 +- Source/Entities/BunkerAssemblyScheme.cpp | 708 +- Source/Entities/BunkerAssemblyScheme.h | 599 +- Source/Entities/Deployment.cpp | 1298 +- Source/Entities/Deployment.h | 735 +- Source/Entities/Emission.cpp | 300 +- Source/Entities/Emission.h | 616 +- Source/Entities/Gib.cpp | 20 +- Source/Entities/Gib.h | 19 +- Source/Entities/GlobalScript.cpp | 54 +- Source/Entities/GlobalScript.h | 24 +- Source/Entities/HDFirearm.cpp | 2144 +-- Source/Entities/HDFirearm.h | 1996 ++- Source/Entities/HeldDevice.cpp | 999 +- Source/Entities/HeldDevice.h | 1358 +- Source/Entities/Icon.cpp | 30 +- Source/Entities/Icon.h | 35 +- Source/Entities/Leg.cpp | 72 +- Source/Entities/Leg.h | 39 +- Source/Entities/LimbPath.cpp | 1200 +- Source/Entities/LimbPath.h | 1387 +- Source/Entities/Loadout.cpp | 549 +- Source/Entities/Loadout.h | 391 +- Source/Entities/MOPixel.cpp | 90 +- Source/Entities/MOPixel.h | 43 +- Source/Entities/MOSParticle.cpp | 78 +- Source/Entities/MOSParticle.h | 36 +- Source/Entities/MOSRotating.cpp | 3713 ++--- Source/Entities/MOSRotating.h | 1879 ++- Source/Entities/MOSprite.cpp | 1075 +- Source/Entities/MOSprite.h | 1181 +- Source/Entities/Magazine.cpp | 550 +- Source/Entities/Magazine.h | 588 +- Source/Entities/Material.cpp | 18 +- Source/Entities/Material.h | 35 +- Source/Entities/MetaPlayer.cpp | 27 +- Source/Entities/MetaPlayer.h | 40 +- Source/Entities/MetaSave.cpp | 20 +- Source/Entities/MetaSave.h | 20 +- Source/Entities/MovableObject.cpp | 2201 ++- Source/Entities/MovableObject.h | 4227 +++--- Source/Entities/PEmitter.cpp | 165 +- Source/Entities/PEmitter.h | 920 +- Source/Entities/PieMenu.cpp | 480 +- Source/Entities/PieMenu.h | 183 +- Source/Entities/PieSlice.cpp | 88 +- Source/Entities/PieSlice.h | 58 +- Source/Entities/Round.cpp | 47 +- Source/Entities/Round.h | 35 +- Source/Entities/SLBackground.cpp | 59 +- Source/Entities/SLBackground.h | 30 +- Source/Entities/SLTerrain.cpp | 284 +- Source/Entities/SLTerrain.h | 49 +- Source/Entities/Scene.cpp | 5284 ++++--- Source/Entities/Scene.h | 2756 ++-- Source/Entities/SceneLayer.cpp | 166 +- Source/Entities/SceneLayer.h | 67 +- Source/Entities/SceneObject.cpp | 730 +- Source/Entities/SceneObject.h | 1120 +- Source/Entities/SoundContainer.cpp | 77 +- Source/Entities/SoundContainer.h | 130 +- Source/Entities/SoundSet.cpp | 117 +- Source/Entities/SoundSet.h | 45 +- Source/Entities/TDExplosive.cpp | 48 +- Source/Entities/TDExplosive.h | 25 +- Source/Entities/TerrainDebris.cpp | 79 +- Source/Entities/TerrainDebris.h | 25 +- Source/Entities/TerrainFrosting.cpp | 39 +- Source/Entities/TerrainFrosting.h | 22 +- Source/Entities/TerrainObject.cpp | 150 +- Source/Entities/TerrainObject.h | 48 +- Source/Entities/ThrownDevice.cpp | 36 +- Source/Entities/ThrownDevice.h | 32 +- Source/Entities/Turret.cpp | 105 +- Source/Entities/Turret.h | 34 +- Source/GUI/GUI.h | 14 +- Source/GUI/GUIBanner.cpp | 926 +- Source/GUI/GUIBanner.h | 600 +- Source/GUI/GUIButton.cpp | 94 +- Source/GUI/GUIButton.h | 555 +- Source/GUI/GUICheckbox.cpp | 28 +- Source/GUI/GUICheckbox.h | 416 +- Source/GUI/GUICollectionBox.cpp | 59 +- Source/GUI/GUICollectionBox.h | 461 +- Source/GUI/GUIComboBox.cpp | 85 +- Source/GUI/GUIComboBox.h | 907 +- Source/GUI/GUIControl.cpp | 106 +- Source/GUI/GUIControl.h | 510 +- Source/GUI/GUIControlFactory.cpp | 2 +- Source/GUI/GUIControlFactory.h | 28 +- Source/GUI/GUIControlManager.cpp | 105 +- Source/GUI/GUIControlManager.h | 550 +- Source/GUI/GUIEvent.cpp | 4 +- Source/GUI/GUIEvent.h | 147 +- Source/GUI/GUIFont.cpp | 60 +- Source/GUI/GUIFont.h | 363 +- Source/GUI/GUIInput.cpp | 56 +- Source/GUI/GUIInput.h | 400 +- Source/GUI/GUIInterface.h | 36 +- Source/GUI/GUILabel.cpp | 83 +- Source/GUI/GUILabel.h | 540 +- Source/GUI/GUIListBox.cpp | 25 +- Source/GUI/GUIListBox.h | 240 +- Source/GUI/GUIListPanel.cpp | 214 +- Source/GUI/GUIListPanel.h | 1160 +- Source/GUI/GUIManager.cpp | 114 +- Source/GUI/GUIManager.h | 345 +- Source/GUI/GUIPanel.cpp | 122 +- Source/GUI/GUIPanel.h | 1194 +- Source/GUI/GUIProgressBar.cpp | 41 +- Source/GUI/GUIProgressBar.h | 416 +- Source/GUI/GUIProperties.cpp | 86 +- Source/GUI/GUIProperties.h | 389 +- Source/GUI/GUIPropertyPage.cpp | 79 +- Source/GUI/GUIPropertyPage.h | 433 +- Source/GUI/GUIRadioButton.cpp | 47 +- Source/GUI/GUIRadioButton.h | 388 +- Source/GUI/GUIReader.cpp | 143 +- Source/GUI/GUIReader.h | 45 +- Source/GUI/GUIScrollPanel.cpp | 78 +- Source/GUI/GUIScrollPanel.h | 646 +- Source/GUI/GUIScrollbar.cpp | 25 +- Source/GUI/GUIScrollbar.h | 305 +- Source/GUI/GUISkin.cpp | 87 +- Source/GUI/GUISkin.h | 324 +- Source/GUI/GUISlider.cpp | 50 +- Source/GUI/GUISlider.h | 617 +- Source/GUI/GUISound.cpp | 6 +- Source/GUI/GUISound.h | 59 +- Source/GUI/GUITab.cpp | 41 +- Source/GUI/GUITab.h | 390 +- Source/GUI/GUITextBox.cpp | 63 +- Source/GUI/GUITextBox.h | 263 +- Source/GUI/GUITextPanel.cpp | 78 +- Source/GUI/GUITextPanel.h | 581 +- Source/GUI/GUIUtil.cpp | 16 +- Source/GUI/GUIUtil.h | 9 +- Source/GUI/GUIWriter.cpp | 109 +- Source/GUI/GUIWriter.h | 47 +- Source/GUI/Wrappers/AllegroBitmap.cpp | 70 +- Source/GUI/Wrappers/AllegroBitmap.h | 34 +- Source/GUI/Wrappers/AllegroScreen.cpp | 32 +- Source/GUI/Wrappers/AllegroScreen.h | 18 +- Source/GUI/Wrappers/GUIInputWrapper.cpp | 45 +- Source/GUI/Wrappers/GUIInputWrapper.h | 8 +- Source/Lua/LuaAdapterDefinitions.h | 180 +- Source/Lua/LuaAdapters.cpp | 357 +- Source/Lua/LuaBindingRegisterDefinitions.h | 346 +- Source/Lua/LuaBindingsActivities.cpp | 326 +- Source/Lua/LuaBindingsEntities.cpp | 2382 ++-- Source/Lua/LuaBindingsGUI.cpp | 162 +- Source/Lua/LuaBindingsInput.cpp | 1154 +- Source/Lua/LuaBindingsManagers.cpp | 722 +- Source/Lua/LuaBindingsMisc.cpp | 60 +- Source/Lua/LuaBindingsPrimitives.cpp | 66 +- Source/Lua/LuaBindingsSystem.cpp | 416 +- Source/Lua/LuabindDefinitions.h | 12 +- Source/Lua/LuabindObjectWrapper.cpp | 117 +- Source/Lua/LuabindObjectWrapper.h | 29 +- Source/Main.cpp | 54 +- Source/Managers/AchievementMan.cpp | 150 +- Source/Managers/AchievementMan.h | 14 +- Source/Managers/ActivityMan.cpp | 105 +- Source/Managers/ActivityMan.h | 40 +- Source/Managers/AudioMan.cpp | 417 +- Source/Managers/AudioMan.h | 150 +- Source/Managers/CameraMan.cpp | 64 +- Source/Managers/CameraMan.h | 24 +- Source/Managers/ConsoleMan.cpp | 177 +- Source/Managers/ConsoleMan.h | 40 +- Source/Managers/FrameMan.cpp | 221 +- Source/Managers/FrameMan.h | 97 +- Source/Managers/LuaMan.cpp | 736 +- Source/Managers/LuaMan.h | 97 +- Source/Managers/MenuMan.cpp | 46 +- Source/Managers/MenuMan.h | 8 +- Source/Managers/MetaMan.cpp | 2507 ++-- Source/Managers/MetaMan.h | 1173 +- Source/Managers/MovableMan.cpp | 3955 +++--- Source/Managers/MovableMan.h | 2038 ++- Source/Managers/NetworkClient.cpp | 301 +- Source/Managers/NetworkClient.h | 52 +- Source/Managers/NetworkServer.cpp | 500 +- Source/Managers/NetworkServer.h | 64 +- Source/Managers/PerformanceMan.cpp | 63 +- Source/Managers/PerformanceMan.h | 29 +- Source/Managers/PostProcessMan.cpp | 118 +- Source/Managers/PostProcessMan.h | 65 +- Source/Managers/PresetMan.cpp | 1875 ++- Source/Managers/PresetMan.h | 1122 +- Source/Managers/PrimitiveMan.cpp | 156 +- Source/Managers/PrimitiveMan.h | 104 +- Source/Managers/SceneMan.cpp | 5234 ++++--- Source/Managers/SceneMan.h | 2872 ++-- Source/Managers/SettingsMan.cpp | 61 +- Source/Managers/SettingsMan.h | 33 +- Source/Managers/ThreadMan.cpp | 62 +- Source/Managers/ThreadMan.h | 225 +- Source/Managers/TimerMan.cpp | 24 +- Source/Managers/TimerMan.h | 30 +- Source/Managers/UInputMan.cpp | 235 +- Source/Managers/UInputMan.h | 58 +- Source/Managers/WindowMan.cpp | 92 +- Source/Managers/WindowMan.h | 35 +- Source/Menus/AreaEditorGUI.cpp | 1012 +- Source/Menus/AreaEditorGUI.h | 563 +- Source/Menus/AreaPickerGUI.cpp | 829 +- Source/Menus/AreaPickerGUI.h | 550 +- Source/Menus/AssemblyEditorGUI.cpp | 1385 +- Source/Menus/AssemblyEditorGUI.h | 809 +- Source/Menus/BuyMenuGUI.cpp | 3160 ++--- Source/Menus/BuyMenuGUI.h | 1756 ++- Source/Menus/GibEditorGUI.cpp | 1347 +- Source/Menus/GibEditorGUI.h | 729 +- Source/Menus/InventoryMenuGUI.cpp | 447 +- Source/Menus/InventoryMenuGUI.h | 129 +- Source/Menus/LoadingScreen.cpp | 44 +- Source/Menus/LoadingScreen.h | 18 +- Source/Menus/MainMenuGUI.cpp | 181 +- Source/Menus/MainMenuGUI.h | 41 +- Source/Menus/MetagameGUI.cpp | 11509 ++++++++-------- Source/Menus/MetagameGUI.h | 2444 ++-- Source/Menus/ModManagerGUI.cpp | 72 +- Source/Menus/ModManagerGUI.h | 26 +- Source/Menus/MultiplayerGameGUI.cpp | 26 +- Source/Menus/MultiplayerGameGUI.h | 44 +- Source/Menus/ObjectPickerGUI.cpp | 226 +- Source/Menus/ObjectPickerGUI.h | 74 +- Source/Menus/PauseMenuGUI.cpp | 54 +- Source/Menus/PauseMenuGUI.h | 29 +- Source/Menus/SaveLoadMenuGUI.cpp | 161 +- Source/Menus/SaveLoadMenuGUI.h | 37 +- Source/Menus/ScenarioActivityConfigGUI.cpp | 157 +- Source/Menus/ScenarioActivityConfigGUI.h | 68 +- Source/Menus/ScenarioGUI.cpp | 188 +- Source/Menus/ScenarioGUI.h | 67 +- Source/Menus/SceneEditorGUI.cpp | 2414 ++-- Source/Menus/SceneEditorGUI.h | 753 +- Source/Menus/SettingsAudioGUI.cpp | 47 +- Source/Menus/SettingsAudioGUI.h | 34 +- Source/Menus/SettingsGUI.cpp | 52 +- Source/Menus/SettingsGUI.h | 18 +- Source/Menus/SettingsGameplayGUI.cpp | 65 +- Source/Menus/SettingsGameplayGUI.h | 44 +- Source/Menus/SettingsInputGUI.cpp | 63 +- Source/Menus/SettingsInputGUI.h | 38 +- Source/Menus/SettingsInputMappingGUI.cpp | 67 +- Source/Menus/SettingsInputMappingGUI.h | 40 +- .../Menus/SettingsInputMappingWizardGUI.cpp | 131 +- Source/Menus/SettingsInputMappingWizardGUI.h | 80 +- Source/Menus/SettingsMiscGUI.cpp | 37 +- Source/Menus/SettingsMiscGUI.h | 36 +- Source/Menus/SettingsVideoGUI.cpp | 88 +- Source/Menus/SettingsVideoGUI.h | 67 +- Source/Menus/TitleScreen.cpp | 150 +- Source/Menus/TitleScreen.h | 47 +- Source/System/AllegroTools.cpp | 4 +- Source/System/AllegroTools.h | 24 +- Source/System/Atom.cpp | 243 +- Source/System/Atom.h | 89 +- Source/System/Box.cpp | 63 +- Source/System/Box.h | 49 +- Source/System/Color.cpp | 20 +- Source/System/Color.h | 42 +- Source/System/Constants.h | 185 +- Source/System/ContentFile.cpp | 123 +- Source/System/ContentFile.h | 69 +- Source/System/Controller.cpp | 106 +- Source/System/Controller.h | 78 +- Source/System/DataModule.cpp | 159 +- Source/System/DataModule.h | 79 +- Source/System/Entity.cpp | 145 +- Source/System/Entity.h | 172 +- Source/System/GLCheck.cpp | 2 +- Source/System/GLCheck.h | 2 +- Source/System/GameVersion.h | 4 +- Source/System/Gamepad.h | 9 +- Source/System/GenericSavedData.cpp | 64 +- Source/System/GenericSavedData.h | 32 +- Source/System/GraphicalPrimitive.cpp | 128 +- Source/System/GraphicalPrimitive.h | 132 +- Source/System/InputMapping.cpp | 32 +- Source/System/InputMapping.h | 15 +- Source/System/InputScheme.cpp | 47 +- Source/System/InputScheme.h | 9 +- Source/System/Matrix.cpp | 69 +- Source/System/Matrix.h | 98 +- Source/System/NetworkMessages.h | 3 +- Source/System/PathFinder.cpp | 232 +- Source/System/PathFinder.h | 81 +- Source/System/PieQuadrant.cpp | 78 +- Source/System/PieQuadrant.h | 20 +- Source/System/RTEError.cpp | 182 +- Source/System/RTEError.h | 26 +- Source/System/RTEStackTrace.cpp | 8 +- Source/System/RTEStackTrace.h | 14 +- Source/System/RTETools.cpp | 57 +- Source/System/RTETools.h | 403 +- Source/System/Reader.cpp | 108 +- Source/System/Reader.h | 105 +- Source/System/Serializable.cpp | 34 +- Source/System/Serializable.h | 123 +- Source/System/Shader.cpp | 76 +- Source/System/Shader.h | 43 +- Source/System/Singleton.h | 15 +- Source/System/SpatialPartitionGrid.cpp | 64 +- Source/System/SpatialPartitionGrid.h | 23 +- Source/System/StandardIncludes.h | 4 +- Source/System/System.cpp | 99 +- Source/System/System.h | 36 +- Source/System/Timer.cpp | 12 +- Source/System/Timer.h | 33 +- Source/System/Vector.cpp | 14 +- Source/System/Vector.h | 184 +- Source/System/Writer.cpp | 32 +- Source/System/Writer.h | 132 +- 369 files changed, 90265 insertions(+), 94134 deletions(-) diff --git a/Source/Activities/ActorEditor.cpp b/Source/Activities/ActorEditor.cpp index a6baf32f7d..5d8cbe9f92 100644 --- a/Source/Activities/ActorEditor.cpp +++ b/Source/Activities/ActorEditor.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,11 +17,11 @@ #include "FrameMan.h" #include "CameraMan.h" #include "UInputMan.h" -//#include "AHuman.h" -//#include "MOPixel.h" +// #include "AHuman.h" +// #include "MOPixel.h" #include "SLTerrain.h" #include "Controller.h" -//#include "AtomGroup.h" +// #include "AtomGroup.h" #include "Actor.h" #include "AHuman.h" #include "ACRocket.h" @@ -34,335 +33,297 @@ namespace RTE { -ConcreteClassInfo(ActorEditor, EditorActivity, 0); + ConcreteClassInfo(ActorEditor, EditorActivity, 0); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ActorEditor, effectively + // resetting the members of this abstraction level only. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ActorEditor, effectively -// resetting the members of this abstraction level only. + void ActorEditor::Clear() { + m_pEditedActor = 0; + m_pPicker = 0; + } -void ActorEditor::Clear() -{ - m_pEditedActor = 0; - m_pPicker = 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ActorEditor object ready for use. + int ActorEditor::Create() { + if (EditorActivity::Create() < 0) + return -1; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ActorEditor object ready for use. + m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", "Actor Editor Pie Menu")->Clone())); -int ActorEditor::Create() -{ - if (EditorActivity::Create() < 0) - return -1; + return 0; + } - m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", "Actor Editor Pie Menu")->Clone())); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ActorEditor to be identical to another, by deep copy. - return 0; -} + int ActorEditor::Create(const ActorEditor& reference) { + if (EditorActivity::Create(reference) < 0) + return -1; + if (m_Description.empty()) + m_Description = "Load and edit Actors."; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a ActorEditor to be identical to another, by deep copy. + return 0; + } -int ActorEditor::Create(const ActorEditor &reference) -{ - if (EditorActivity::Create(reference) < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int ActorEditor::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); + /* + MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); + MatchProperty("Difficulty", { reader >> m_Difficulty; }); + MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); + */ + EndPropertyList; + } - if (m_Description.empty()) - m_Description = "Load and edit Actors."; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this ActorEditor with a Writer for + // later recreation with Create(Reader &reader); - return 0; -} + int ActorEditor::Save(Writer& writer) const { + EditorActivity::Save(writer); + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the ActorEditor object. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int ActorEditor::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); -/* - MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); - MatchProperty("Difficulty", { reader >> m_Difficulty; }); - MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); -*/ - EndPropertyList; -} + void ActorEditor::Destroy(bool notInherited) { + delete m_pEditedActor; + delete m_pPicker; + if (!notInherited) + EditorActivity::Destroy(); + Clear(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this ActorEditor with a Writer for -// later recreation with Create(Reader &reader); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. -int ActorEditor::Save(Writer &writer) const { - EditorActivity::Save(writer); - return 0; -} + int ActorEditor::Start() { + int error = EditorActivity::Start(); + ////////////////////////////////////////////// + // Allocate and (re)create the picker GUI -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the ActorEditor object. + if (m_pPicker) + m_pPicker->Reset(); + else + m_pPicker = new ObjectPickerGUI; + m_pPicker->Create(&(m_PlayerController[0]), -1, "Actor"); -void ActorEditor::Destroy(bool notInherited) -{ - delete m_pEditedActor; - delete m_pPicker; + m_PieMenu->SetMenuController(&m_PlayerController[0]); - if (!notInherited) - EditorActivity::Destroy(); - Clear(); -} + m_EditorMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + return error; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + + void ActorEditor::SetPaused(bool pause) { + // Override the pause + m_Paused = false; + } -int ActorEditor::Start() -{ - int error = EditorActivity::Start(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. - ////////////////////////////////////////////// - // Allocate and (re)create the picker GUI + void ActorEditor::End() { + EditorActivity::End(); - if (m_pPicker) - m_pPicker->Reset(); - else - m_pPicker = new ObjectPickerGUI; - m_pPicker->Create(&(m_PlayerController[0]), -1, "Actor"); + m_ActivityState = ActivityState::Over; + } - m_PieMenu->SetMenuController(&m_PlayerController[0]); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActorEditor. Supposed to be done every frame + // before drawing. + + void ActorEditor::Update() { + // And object hasn't been loaded yet, so get the loading picker going + if (!m_pEditedActor) { + m_NeedSave = false; + m_HasEverBeenSaved = false; + // Show the picker dialog to select an object to load + m_pPicker->SetEnabled(true); + // Scroll to center of scene + g_CameraMan.SetScrollTarget(g_SceneMan.GetSceneDim() * 0.5); + m_EditorMode = m_PreviousMode = EditorActivity::LOADDIALOG; + m_ModeChange = true; + } - m_EditorMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; + // Mode switching etc - don't need for this yet + // EditorActivity::Update(); + // Update the player controller since we're not calling parent update + m_PlayerController[0].Update(); - return error; -} + if (!g_SceneMan.GetScene()) + return; + ///////////////////////////////////////////////////// + // Update the edited Actor -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. + if (m_pEditedActor) { + m_pEditedActor->SetPos(g_SceneMan.GetSceneDim() * 0.5); + m_pEditedActor->FullUpdate(); + g_CameraMan.SetScrollTarget(m_pEditedActor->GetPos()); + } -void ActorEditor::SetPaused(bool pause) -{ - // Override the pause - m_Paused = false; -} + ///////////////////////////////////////////////////// + // Picker logic + // Enable or disable the picker + m_pPicker->SetEnabled(m_EditorMode == EditorActivity::LOADDIALOG); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. + // Update the picker GUI + m_pPicker->Update(); -void ActorEditor::End() -{ - EditorActivity::End(); + // Set the screen occlusion situation + if (!m_pPicker->IsVisible()) + g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(Players::PlayerOne)); + // Picking something to load into the editor + if (m_EditorMode == EditorActivity::LOADDIALOG) { + g_FrameMan.ClearScreenText(); + g_FrameMan.SetScreenText("Select an Actor to LOAD into the editor ->", 0, 333); + // Picked something! + if (m_pPicker->ObjectPicked() && !m_pPicker->IsEnabled()) { + // Load the picked Actor into the editor + LoadActor(m_pPicker->ObjectPicked()); + } + } else { + g_FrameMan.ClearScreenText(); + g_FrameMan.SetScreenText("Control the actor to see how he moves. Reload data with the pie menu.", 0, 0, 5000); + } - m_ActivityState = ActivityState::Over; -} + ////////////////////////////////////////////// + // Pie menu logic + if (m_pEditedActor) { + PieMenu* editedActorPieMenu = m_pEditedActor->GetPieMenu(); + editedActorPieMenu->SetEnabled(m_PlayerController[0].IsState(ControlState::PIE_MENU_ACTIVE) && m_EditorMode != EditorActivity::LOADDIALOG); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActorEditor. Supposed to be done every frame -// before drawing. - -void ActorEditor::Update() -{ - // And object hasn't been loaded yet, so get the loading picker going - if (!m_pEditedActor) - { - m_NeedSave = false; - m_HasEverBeenSaved = false; - // Show the picker dialog to select an object to load - m_pPicker->SetEnabled(true); - // Scroll to center of scene - g_CameraMan.SetScrollTarget(g_SceneMan.GetSceneDim() * 0.5); - m_EditorMode = m_PreviousMode = EditorActivity::LOADDIALOG; - m_ModeChange = true; - } - - // Mode switching etc - don't need for this yet -// EditorActivity::Update(); - // Update the player controller since we're not calling parent update - m_PlayerController[0].Update(); - - if (!g_SceneMan.GetScene()) - return; - - ///////////////////////////////////////////////////// - // Update the edited Actor - - if (m_pEditedActor) - { - m_pEditedActor->SetPos(g_SceneMan.GetSceneDim() * 0.5); - m_pEditedActor->FullUpdate(); - g_CameraMan.SetScrollTarget(m_pEditedActor->GetPos()); - } - - ///////////////////////////////////////////////////// - // Picker logic - - // Enable or disable the picker - m_pPicker->SetEnabled(m_EditorMode == EditorActivity::LOADDIALOG); - - // Update the picker GUI - m_pPicker->Update(); - - // Set the screen occlusion situation - if (!m_pPicker->IsVisible()) - g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(Players::PlayerOne)); - - // Picking something to load into the editor - if (m_EditorMode == EditorActivity::LOADDIALOG) - { - g_FrameMan.ClearScreenText(); - g_FrameMan.SetScreenText("Select an Actor to LOAD into the editor ->", 0, 333); - - // Picked something! - if (m_pPicker->ObjectPicked() && !m_pPicker->IsEnabled()) - { - // Load the picked Actor into the editor - LoadActor(m_pPicker->ObjectPicked()); - } - } - else - { - g_FrameMan.ClearScreenText(); - g_FrameMan.SetScreenText("Control the actor to see how he moves. Reload data with the pie menu.", 0, 0, 5000); - } - - ////////////////////////////////////////////// - // Pie menu logic - - if (m_pEditedActor) { - PieMenu *editedActorPieMenu = m_pEditedActor->GetPieMenu(); - editedActorPieMenu->SetEnabled(m_PlayerController[0].IsState(ControlState::PIE_MENU_ACTIVE) && m_EditorMode != EditorActivity::LOADDIALOG); - - if (editedActorPieMenu->GetPieCommand() == PieSlice::SliceType::EditorLoad) { - ReloadActorData(); - } else if (editedActorPieMenu->GetPieCommand() == PieSlice::SliceType::EditorPick) { - m_EditorMode = EditorActivity::LOADDIALOG; - m_ModeChange = true; + if (editedActorPieMenu->GetPieCommand() == PieSlice::SliceType::EditorLoad) { + ReloadActorData(); + } else if (editedActorPieMenu->GetPieCommand() == PieSlice::SliceType::EditorPick) { + m_EditorMode = EditorActivity::LOADDIALOG; + m_ModeChange = true; + } } } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. + void ActorEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + // Draw the edited actor and pie menu + if (m_pEditedActor) { + m_pEditedActor->Draw(pTargetBitmap, targetPos, g_DrawColor); + m_pEditedActor->GetPieMenu()->Draw(pTargetBitmap, targetPos); + } -void ActorEditor::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) -{ - // Draw the edited actor and pie menu - if (m_pEditedActor) - { - m_pEditedActor->Draw(pTargetBitmap, targetPos, g_DrawColor); - m_pEditedActor->GetPieMenu()->Draw(pTargetBitmap, targetPos); - } + // Clear out annoying blooms + // TODO: Figure out if this needs a button or piemenu toggle for some edge case + // g_PostProcessMan.ClearScenePostEffects(); - // Clear out annoying blooms - // TODO: Figure out if this needs a button or piemenu toggle for some edge case - //g_PostProcessMan.ClearScenePostEffects(); + m_pPicker->Draw(pTargetBitmap); - m_pPicker->Draw(pTargetBitmap); + EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); + } - EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActorEditor's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + void ActorEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + EditorActivity::Draw(pTargetBitmap, targetPos); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActorEditor's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reloads the Actor itself and sets up the pie menu to match its setup. -void ActorEditor::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) -{ - EditorActivity::Draw(pTargetBitmap, targetPos); -} + bool ActorEditor::LoadActor(const Entity* pActorToLoad) { + if (!pActorToLoad) + return false; + // Replace the old one + if (MovableObject* asMo = dynamic_cast(m_pEditedActor)) { + asMo->DestroyScriptState(); + } + delete m_pEditedActor; + // Make a copy of the picked object reference + m_pEditedActor = dynamic_cast(pActorToLoad->Clone()); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reloads the Actor itself and sets up the pie menu to match its setup. - -bool ActorEditor::LoadActor(const Entity *pActorToLoad) -{ - if (!pActorToLoad) - return false; - - // Replace the old one - if (MovableObject* asMo = dynamic_cast(m_pEditedActor)) { - asMo->DestroyScriptState(); - } - delete m_pEditedActor; - // Make a copy of the picked object reference - m_pEditedActor = dynamic_cast(pActorToLoad->Clone()); - - if (m_pEditedActor) - { - // Set up the editor for the new actor - m_pEditedActor->SetControllerMode(Controller::CIM_PLAYER, Players::PlayerOne); - - for (const PieSlice *pieSlice : m_PieMenu->GetPieSlices()) { - m_pEditedActor->GetPieMenu()->AddPieSlice(dynamic_cast(pieSlice->Clone()), this); + if (m_pEditedActor) { + // Set up the editor for the new actor + m_pEditedActor->SetControllerMode(Controller::CIM_PLAYER, Players::PlayerOne); + + for (const PieSlice* pieSlice: m_PieMenu->GetPieSlices()) { + m_pEditedActor->GetPieMenu()->AddPieSlice(dynamic_cast(pieSlice->Clone()), this); + } + + m_EditorMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } else { + g_FrameMan.ClearScreenText(); + g_FrameMan.SetScreenText("There's something wrong with that picked Actor!?", 0, 333, 2000); + return false; } - m_EditorMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - else - { - g_FrameMan.ClearScreenText(); - g_FrameMan.SetScreenText("There's something wrong with that picked Actor!?", 0, 333, 2000); - return false; - } - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool ActorEditor::ReloadActorData() { - if (m_pEditedActor) { - std::string presetName = m_pEditedActor->GetPresetName(); - std::string className = m_pEditedActor->GetClassName(); - std::string moduleName = g_PresetMan.GetDataModuleName(m_pEditedActor->GetModuleID()); - g_PresetMan.ReloadEntityPreset(presetName, className, moduleName, false); - LoadActor(g_PresetMan.GetEntityPreset(className, presetName, moduleName)); - return m_pEditedActor != nullptr; + return true; } - return false; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool ActorEditor::ReloadActorData() { + if (m_pEditedActor) { + std::string presetName = m_pEditedActor->GetPresetName(); + std::string className = m_pEditedActor->GetClassName(); + std::string moduleName = g_PresetMan.GetDataModuleName(m_pEditedActor->GetModuleID()); + g_PresetMan.ReloadEntityPreset(presetName, className, moduleName, false); + LoadActor(g_PresetMan.GetEntityPreset(className, presetName, moduleName)); + return m_pEditedActor != nullptr; + } + return false; + } } // namespace RTE \ No newline at end of file diff --git a/Source/Activities/ActorEditor.h b/Source/Activities/ActorEditor.h index 715799bf79..54ef3e366a 100644 --- a/Source/Activities/ActorEditor.h +++ b/Source/Activities/ActorEditor.h @@ -10,253 +10,229 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" -namespace RTE -{ - -class Actor; -class ObjectPickerGUI; -class PieMenu; -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUITab; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUILabel; -class GUIComboBox; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: ActorEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activity for testing and quickly iterating on Actor data definitions. -// Parent(s): EditorActivity. -// Class history: 10/08/2007 ActorEditor Created. - -class ActorEditor : public EditorActivity { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(ActorEditor); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: ActorEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a ActorEditor object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - ActorEditor() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~ActorEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a ActorEditor object before deletion -// from system memory. -// Arguments: None. - - ~ActorEditor() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ActorEditor object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a ActorEditor to be identical to another, by deep copy. -// Arguments: A reference to the ActorEditor to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const ActorEditor &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire ActorEditor, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); EditorActivity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the ActorEditor object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reloads the Actor itself and sets up the pie menu to match its setup. -// Arguments: An Entity Preset of the Actor to load into the editor. OWNERSHIP IS NOT TRANSFERRED! -// Return value: Whether the Actor was loaded successfully from the PresetMan. - - bool LoadActor(const Entity *pActorToLoad); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReloadActorData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reloads the ini with the currently edited Actor's definitions. -// Arguments: None. -// Return value: Whether the data file was successfully read. - - bool ReloadActorData(); - - - // Member variables - static Entity::ClassInfo m_sClass; - - // The loaded MOSR of which we are editing. Owned by this. - Actor *m_pEditedActor; - // The picker for selecting which object to load - ObjectPickerGUI *m_pPicker; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class Actor; + class ObjectPickerGUI; + class PieMenu; + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUITab; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUILabel; + class GUIComboBox; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: ActorEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activity for testing and quickly iterating on Actor data definitions. + // Parent(s): EditorActivity. + // Class history: 10/08/2007 ActorEditor Created. + + class ActorEditor : public EditorActivity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(ActorEditor); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: ActorEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a ActorEditor object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + ActorEditor() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~ActorEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a ActorEditor object before deletion + // from system memory. + // Arguments: None. + + ~ActorEditor() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ActorEditor object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ActorEditor to be identical to another, by deep copy. + // Arguments: A reference to the ActorEditor to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const ActorEditor& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire ActorEditor, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + EditorActivity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the ActorEditor object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reloads the Actor itself and sets up the pie menu to match its setup. + // Arguments: An Entity Preset of the Actor to load into the editor. OWNERSHIP IS NOT TRANSFERRED! + // Return value: Whether the Actor was loaded successfully from the PresetMan. + + bool LoadActor(const Entity* pActorToLoad); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReloadActorData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reloads the ini with the currently edited Actor's definitions. + // Arguments: None. + // Return value: Whether the data file was successfully read. + + bool ReloadActorData(); + + // Member variables + static Entity::ClassInfo m_sClass; + + // The loaded MOSR of which we are editing. Owned by this. + Actor* m_pEditedActor; + // The picker for selecting which object to load + ObjectPickerGUI* m_pPicker; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Activities/AreaEditor.cpp b/Source/Activities/AreaEditor.cpp index 1023426224..05e736af0a 100644 --- a/Source/Activities/AreaEditor.cpp +++ b/Source/Activities/AreaEditor.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -41,717 +40,633 @@ namespace RTE { -ConcreteClassInfo(AreaEditor, EditorActivity, 0); + ConcreteClassInfo(AreaEditor, EditorActivity, 0); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this AreaEditor, effectively + // resetting the members of this abstraction level only. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AreaEditor, effectively -// resetting the members of this abstraction level only. + void AreaEditor::Clear() { + m_pEditorGUI = 0; + m_pNewAreaName = 0; + } -void AreaEditor::Clear() -{ - m_pEditorGUI = 0; - m_pNewAreaName = 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the AreaEditor object ready for use. + int AreaEditor::Create() { + if (EditorActivity::Create() < 0) + return -1; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AreaEditor object ready for use. + return 0; + } -int AreaEditor::Create() -{ - if (EditorActivity::Create() < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a AreaEditor to be identical to another, by deep copy. + int AreaEditor::Create(const AreaEditor& reference) { + if (EditorActivity::Create(reference) < 0) + return -1; - return 0; -} + if (m_Description.empty()) + m_Description = "Define and edit Areas on this Scene."; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a AreaEditor to be identical to another, by deep copy. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int AreaEditor::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); + /* + MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); + MatchProperty("Difficulty", { reader >> m_Difficulty; }); + MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); + */ + EndPropertyList; + } -int AreaEditor::Create(const AreaEditor &reference) -{ - if (EditorActivity::Create(reference) < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this AreaEditor with a Writer for + // later recreation with Create(Reader &reader); - if (m_Description.empty()) - m_Description = "Define and edit Areas on this Scene."; + int AreaEditor::Save(Writer& writer) const { + EditorActivity::Save(writer); + return 0; + } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the AreaEditor object. + void AreaEditor::Destroy(bool notInherited) { + delete m_pEditorGUI; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int AreaEditor::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); -/* - MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); - MatchProperty("Difficulty", { reader >> m_Difficulty; }); - MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); -*/ - EndPropertyList; -} + if (!notInherited) + EditorActivity::Destroy(); + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this AreaEditor with a Writer for -// later recreation with Create(Reader &reader); + int AreaEditor::Start() { + int error = EditorActivity::Start(); -int AreaEditor::Save(Writer &writer) const { - EditorActivity::Save(writer); - return 0; -} + ////////////////////////////////////////////// + // Allocate and (re)create the Editor GUI + if (m_pEditorGUI) + m_pEditorGUI->Destroy(); + else + m_pEditorGUI = new AreaEditorGUI; + m_pEditorGUI->Create(&(m_PlayerController[0]), true); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AreaEditor object. + ////////////////////////////////////////////////////////////// + // Hooking up directly to the controls defined in the GUI ini -void AreaEditor::Destroy(bool notInherited) -{ - delete m_pEditorGUI; + m_pGUIController->Load("Base.rte/GUIs/AreaEditorGUI.ini"); - if (!notInherited) - EditorActivity::Destroy(); - Clear(); -} + // Resize the invisible root container so it matches the screen rez + GUICollectionBox* pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); + if (pRootBox) + pRootBox->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of + if (!m_pNewDialogBox) { + m_pNewDialogBox = dynamic_cast(m_pGUIController->GetControl("NewDialogBox")); + // m_pNewDialogBox->SetDrawType(GUICollectionBox::Color); + m_pNewDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pNewDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pNewDialogBox->GetHeight() / 2)); + m_pNewDialogBox->SetVisible(false); + } + m_pNewAreaName = dynamic_cast(m_pGUIController->GetControl("NewAreaNameTB")); + m_pNewButton = dynamic_cast(m_pGUIController->GetControl("NewAreaButton")); + m_pNewCancel = dynamic_cast(m_pGUIController->GetControl("NewCancelButton")); + + // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of + if (!m_pLoadDialogBox) { + m_pLoadDialogBox = dynamic_cast(m_pGUIController->GetControl("LoadDialogBox")); + // m_pLoadDialogBox->SetDrawType(GUICollectionBox::Color); + m_pLoadDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pLoadDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pLoadDialogBox->GetHeight() / 2)); + m_pLoadDialogBox->SetVisible(false); + } + m_pLoadNameCombo = dynamic_cast(m_pGUIController->GetControl("LoadSceneCB")); + m_pLoadNameCombo->SetDropHeight(std::min(m_pLoadNameCombo->GetDropHeight(), g_WindowMan.GetResY() / 2)); + m_pLoadDialogBox->SetSize(m_pLoadDialogBox->GetWidth(), m_pLoadDialogBox->GetHeight() + m_pLoadNameCombo->GetDropHeight()); // Make sure the dropdown can fit, no matter how tall it is. + m_pLoadButton = dynamic_cast(m_pGUIController->GetControl("LoadSceneButton")); + m_pLoadCancel = dynamic_cast(m_pGUIController->GetControl("LoadCancelButton")); + + if (!m_pSaveDialogBox) { + m_pSaveDialogBox = dynamic_cast(m_pGUIController->GetControl("SaveDialogBox")); + + // Set the background image of the parent collection box + // ContentFile backgroundFile("Base.rte/GUIs/BuyMenuBackground.png"); + // m_pSaveDialogBox->SetDrawImage(new AllegroBitmap(backgroundFile.GetAsBitmap())); + // m_pSaveDialogBox->SetDrawBackground(true); + // m_pSaveDialogBox->SetDrawType(GUICollectionBox::Image); + // m_pSaveDialogBox->SetDrawType(GUICollectionBox::Color); + m_pSaveDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pSaveDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pSaveDialogBox->GetHeight() / 2)); + m_pSaveDialogBox->SetVisible(false); + } + m_pSaveNameBox = dynamic_cast(m_pGUIController->GetControl("SaveSceneNameTB")); + m_pSaveModuleLabel = dynamic_cast(m_pGUIController->GetControl("SaveModuleLabel")); + m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveSceneButton")); + m_pSaveCancel = dynamic_cast(m_pGUIController->GetControl("SaveCancelButton")); + + if (!m_pChangesDialogBox) { + m_pChangesDialogBox = dynamic_cast(m_pGUIController->GetControl("ChangesDialogBox")); + m_pChangesDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pChangesDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pChangesDialogBox->GetHeight() / 2)); + m_pChangesDialogBox->SetVisible(false); + } + m_pChangesNameLabel = dynamic_cast(m_pGUIController->GetControl("ChangesNameLabel")); + m_pChangesYesButton = dynamic_cast(m_pGUIController->GetControl("ChangesYesButton")); + m_pChangesNoButton = dynamic_cast(m_pGUIController->GetControl("ChangesNoButton")); + + if (!m_pOverwriteDialogBox) { + m_pOverwriteDialogBox = dynamic_cast(m_pGUIController->GetControl("OverwriteDialogBox")); + m_pOverwriteDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pOverwriteDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pOverwriteDialogBox->GetHeight() / 2)); + m_pOverwriteDialogBox->SetVisible(false); + } + m_pOverwriteNameLabel = dynamic_cast(m_pGUIController->GetControl("OverwriteNameLabel")); + m_pOverwriteYesButton = dynamic_cast(m_pGUIController->GetControl("OverwriteYesButton")); + m_pOverwriteNoButton = dynamic_cast(m_pGUIController->GetControl("OverwriteNoButton")); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. - -int AreaEditor::Start() -{ - int error = EditorActivity::Start(); - - ////////////////////////////////////////////// - // Allocate and (re)create the Editor GUI - - if (m_pEditorGUI) - m_pEditorGUI->Destroy(); - else - m_pEditorGUI = new AreaEditorGUI; - m_pEditorGUI->Create(&(m_PlayerController[0]), true); - - ////////////////////////////////////////////////////////////// - // Hooking up directly to the controls defined in the GUI ini - - m_pGUIController->Load("Base.rte/GUIs/AreaEditorGUI.ini"); - - // Resize the invisible root container so it matches the screen rez - GUICollectionBox *pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); - if (pRootBox) - pRootBox->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); - - // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of - if (!m_pNewDialogBox) - { - m_pNewDialogBox = dynamic_cast(m_pGUIController->GetControl("NewDialogBox")); -// m_pNewDialogBox->SetDrawType(GUICollectionBox::Color); - m_pNewDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pNewDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pNewDialogBox->GetHeight() / 2)); - m_pNewDialogBox->SetVisible(false); - } - m_pNewAreaName = dynamic_cast(m_pGUIController->GetControl("NewAreaNameTB")); - m_pNewButton = dynamic_cast(m_pGUIController->GetControl("NewAreaButton")); - m_pNewCancel = dynamic_cast(m_pGUIController->GetControl("NewCancelButton")); - - // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of - if (!m_pLoadDialogBox) - { - m_pLoadDialogBox = dynamic_cast(m_pGUIController->GetControl("LoadDialogBox")); -// m_pLoadDialogBox->SetDrawType(GUICollectionBox::Color); - m_pLoadDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pLoadDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pLoadDialogBox->GetHeight() / 2)); - m_pLoadDialogBox->SetVisible(false); - } - m_pLoadNameCombo = dynamic_cast(m_pGUIController->GetControl("LoadSceneCB")); - m_pLoadNameCombo->SetDropHeight(std::min(m_pLoadNameCombo->GetDropHeight(), g_WindowMan.GetResY() / 2)); - m_pLoadDialogBox->SetSize(m_pLoadDialogBox->GetWidth(), m_pLoadDialogBox->GetHeight() + m_pLoadNameCombo->GetDropHeight()); // Make sure the dropdown can fit, no matter how tall it is. - m_pLoadButton = dynamic_cast(m_pGUIController->GetControl("LoadSceneButton")); - m_pLoadCancel = dynamic_cast(m_pGUIController->GetControl("LoadCancelButton")); - - if (!m_pSaveDialogBox) - { - m_pSaveDialogBox = dynamic_cast(m_pGUIController->GetControl("SaveDialogBox")); - - // Set the background image of the parent collection box -// ContentFile backgroundFile("Base.rte/GUIs/BuyMenuBackground.png"); -// m_pSaveDialogBox->SetDrawImage(new AllegroBitmap(backgroundFile.GetAsBitmap())); -// m_pSaveDialogBox->SetDrawBackground(true); -// m_pSaveDialogBox->SetDrawType(GUICollectionBox::Image); -// m_pSaveDialogBox->SetDrawType(GUICollectionBox::Color); - m_pSaveDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pSaveDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pSaveDialogBox->GetHeight() / 2)); - m_pSaveDialogBox->SetVisible(false); - } - m_pSaveNameBox = dynamic_cast(m_pGUIController->GetControl("SaveSceneNameTB")); - m_pSaveModuleLabel = dynamic_cast(m_pGUIController->GetControl("SaveModuleLabel")); - m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveSceneButton")); - m_pSaveCancel = dynamic_cast(m_pGUIController->GetControl("SaveCancelButton")); - - if (!m_pChangesDialogBox) - { - m_pChangesDialogBox = dynamic_cast(m_pGUIController->GetControl("ChangesDialogBox")); - m_pChangesDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pChangesDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pChangesDialogBox->GetHeight() / 2)); - m_pChangesDialogBox->SetVisible(false); - } - m_pChangesNameLabel = dynamic_cast(m_pGUIController->GetControl("ChangesNameLabel")); - m_pChangesYesButton = dynamic_cast(m_pGUIController->GetControl("ChangesYesButton")); - m_pChangesNoButton = dynamic_cast(m_pGUIController->GetControl("ChangesNoButton")); - - if (!m_pOverwriteDialogBox) - { - m_pOverwriteDialogBox = dynamic_cast(m_pGUIController->GetControl("OverwriteDialogBox")); - m_pOverwriteDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pOverwriteDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pOverwriteDialogBox->GetHeight() / 2)); - m_pOverwriteDialogBox->SetVisible(false); - } - m_pOverwriteNameLabel = dynamic_cast(m_pGUIController->GetControl("OverwriteNameLabel")); - m_pOverwriteYesButton = dynamic_cast(m_pGUIController->GetControl("OverwriteYesButton")); - m_pOverwriteNoButton = dynamic_cast(m_pGUIController->GetControl("OverwriteNoButton")); - - return error; -} + return error; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. + void AreaEditor::SetPaused(bool pause) { + // Override the pause + m_Paused = false; + } -void AreaEditor::SetPaused(bool pause) -{ - // Override the pause - m_Paused = false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + void AreaEditor::End() { + EditorActivity::End(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. + m_ActivityState = ActivityState::Over; + } -void AreaEditor::End() -{ - EditorActivity::End(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this AreaEditor. Supposed to be done every frame + // before drawing. + void AreaEditor::Update() { + EditorActivity::Update(); + if (!g_SceneMan.GetScene()) + return; - m_ActivityState = ActivityState::Over; -} + Scene* pCurrentScene = g_SceneMan.GetScene(); + // Update the loaded objects of the loaded scene so they look right + pCurrentScene->UpdatePlacedObjects(Scene::PLACEONLOAD); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this AreaEditor. Supposed to be done every frame -// before drawing. - -void AreaEditor::Update() -{ - EditorActivity::Update(); - - if (!g_SceneMan.GetScene()) - return; - - Scene *pCurrentScene = g_SceneMan.GetScene(); - - // Update the loaded objects of the loaded scene so they look right - pCurrentScene->UpdatePlacedObjects(Scene::PLACEONLOAD); - - // If the scene has no Area:s yet, force the user to make a new one - if (pCurrentScene->m_AreaList.empty()) - m_EditorMode = EditorActivity::NEWDIALOG; - - // All dialog boxes are gone and we're editing the scene's Area:s - if (m_EditorMode == EditorActivity::EDITINGOBJECT) - { - if (m_ModeChange) - { - // Start in the add/move mode - m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::PREADDMOVEBOX); - // Hide the cursor for this layer of interface - m_pGUIController->EnableMouse(false); - m_ModeChange = false; - } - g_UInputMan.DisableKeys(false); - } - // We are doing something int he dialog boxes, so don't do anything in the editor interface or show any text messages - else - { - m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); - g_FrameMan.ClearScreenText(); - } - - - ///////////////////////////////////////////////////// - // Update the editor interface - - m_pEditorGUI->Update(); - - // Any edits made, dirtying the scene? - m_NeedSave = m_pEditorGUI->EditMade() || m_NeedSave; - - // Get any mode change commands that the user gave the Editor GUI - if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorNew && m_EditorMode != NEWDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::NEWDIALOG; - m_ModeChange = true; - // This is ahack so we don't get a 'save changes dialog' when we jsut want to create a new area. - // Will turn on dirtyness immediately as New button is pressed below - m_NeedSave = false; - } - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorLoad && m_EditorMode != LOADDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::LOADDIALOG; - m_ModeChange = true; - } - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorSave && m_EditorMode != SAVEDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::SAVEDIALOG; - m_ModeChange = true; - } - // Test the scene by starting a Skirmish Defense with it, after saving - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorDone || m_EditorMode == TESTINGOBJECT) - { - m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); - - if (m_NeedSave) - { - m_PreviousMode = EditorActivity::TESTINGOBJECT; - m_EditorMode = EditorActivity::CHANGESDIALOG; - m_ModeChange = true; -/* - if (m_HasEverBeenSaved) - SaveScene(pCurrentScene->GetPresetName()); - else - { - m_PreviousMode = TESTINGOBJECT; - m_EditorMode = SAVEDIALOG; - m_ModeChange = true; - } -*/ - } - else - { - g_SceneMan.SetSceneToLoad(pCurrentScene->GetPresetName()); - - const Activity *pActivityPreset = dynamic_cast(g_PresetMan.GetEntityPreset("GAScripted", "Skirmish Defense")); - Activity * pActivity = dynamic_cast(pActivityPreset->Clone()); - GameActivity *pTestGame = dynamic_cast(pActivity); - RTEAssert(pTestGame, "Couldn't find the \"Skirmish Defense\" GAScripted Activity! Has it been defined?"); - pTestGame->SetTeamOfPlayer(0, 0); - pTestGame->SetCPUTeam(1); - pTestGame->SetStartingGold(10000); - pTestGame->SetFogOfWarEnabled(false); - pTestGame->SetDifficulty(DifficultySetting::MediumDifficulty); - g_ActivityMan.SetStartActivity(pTestGame); - g_ActivityMan.SetRestartActivity(); - } - } - - //////////////////////////////////////////////////////// - // Handle events for mouse input on the controls - - GUIEvent anEvent; - while(m_pGUIController->GetEvent(&anEvent)) - { - // If we're not supposed to have mouse control, then ignore these messages -// Uh this is not right, editor always has mouse control so far -// if (!m_PlayerController[0].IsMouseControlled()) -// break; - - if (anEvent.GetType() == GUIEvent::Command) - { - ////////////////////////////////////////////////////////// - // NEW button pressed; create a Area in the current scene - - if (anEvent.GetControl() == m_pNewButton) - { - // Make sure we're not trying to create a noname area - if (!m_pNewAreaName->GetText().empty()) - { - // Check if name is already taken, and if so, select the taken one instead of creating a new - if (Scene::Area *pArea = pCurrentScene->GetArea(m_pNewAreaName->GetText())) - { - m_pEditorGUI->SetCurrentArea(pArea); - m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::PREADDMOVEBOX); - } - else - { - // Make and name new Area - Scene::Area newArea(m_pNewAreaName->GetText()); - pCurrentScene->m_AreaList.push_back(newArea); - // Set the new area as the active one in the GUI, note we're getting the correct one from the scene, it's a copy of the one passed in - m_pEditorGUI->SetCurrentArea(pCurrentScene->GetArea(newArea.GetName())); - // Update teh picker list of the GUI so we can mousewheel between all the Areas, incl the new one - m_pEditorGUI->UpdatePickerList(newArea.GetName()); - } - - // Change mode to start editing the new/newly selected Area - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - m_NeedSave = true; - } - else - { -// TODO: Play some error sound? - } - } - - ////////////////////////////////////////////////////////// - // LOAD button pressed; load the selected Scene - - if (anEvent.GetControl() == m_pLoadButton) - { - GUIListPanel::Item *pItem = m_pLoadNameCombo->GetItem(m_pLoadNameCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - // Attempt to load the scene, without applying its placed objects - g_SceneMan.SetSceneToLoad(pItem->m_Name, false); - g_SceneMan.LoadScene(); - // Get the Module ID that the scene exists in, so we can limit the picker to only show objects from that DataModule space - m_ModuleSpaceID = g_SceneMan.GetScene()->GetModuleID(); - if (pCurrentScene) - { - m_pEditorGUI->Destroy(); - m_pEditorGUI->Create(&(m_PlayerController[0]), true); -// TODO: Should read in all the already placed objects in the loaded scene and have them appear int he editor instead - } - } - m_NeedSave = false; - m_HasEverBeenSaved = true; - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - - ////////////////////////////////////////////////////////// - // SAVE button pressed; save the selected Scene - - if (anEvent.GetControl() == m_pSaveButton) - { - if (!m_pSaveNameBox->GetText().empty()) - { - // Save the scene to the name specified in the text box - if (SaveScene(m_pSaveNameBox->GetText())) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the scene - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - // Should really leave dialog box open? - else - { - ; - } - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes YES pressed - - if (anEvent.GetControl() == m_pChangesYesButton) - { - if (m_HasEverBeenSaved) - { - if (SaveScene(pCurrentScene->GetPresetName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the scene - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - } - // Open the save scene dialog to ask user where to save it then - else - { - m_PreviousMode = m_PreviousMode; - m_EditorMode = EditorActivity::SAVEDIALOG; - m_ModeChange = true; - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes NO pressed - - if (anEvent.GetControl() == m_pChangesNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - m_NeedSave = false; - } - - /////////////////////////////////////////////////////////////// - // Overwrite Scene YES pressed - - if (anEvent.GetControl() == m_pOverwriteYesButton) - { - // Force overwrite - if (SaveScene(pCurrentScene->GetPresetName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after overwrite dialog is done, may have been on the way to test the scene - m_EditorMode = m_PreviousMode != EditorActivity::SAVEDIALOG ? m_PreviousMode : EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } -// TODO: Show overwrite error? - } - - /////////////////////////////////////////////////////////////// - // Overwrite Scene NO pressed - - if (anEvent.GetControl() == m_pOverwriteNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - - /////////////////////////////////////////////////////////////// - // CANCEL button pressed; exit any active dialog box - - if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) - { - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - } - - // Notifications - else if (anEvent.GetType() == GUIEvent::Notification) - { -/* - /////////////////////////////////////// - // Clicks on the New Scene Module combo - - if (anEvent.GetControl() == m_pNewModuleCombo) - { - // Closed it, IE selected somehting - if(anEvent.GetMsg() == GUIComboBox::Closed) - UpdateNewDialog(); - } -*/ - } - } -} + // If the scene has no Area:s yet, force the user to make a new one + if (pCurrentScene->m_AreaList.empty()) + m_EditorMode = EditorActivity::NEWDIALOG; + // All dialog boxes are gone and we're editing the scene's Area:s + if (m_EditorMode == EditorActivity::EDITINGOBJECT) { + if (m_ModeChange) { + // Start in the add/move mode + m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::PREADDMOVEBOX); + // Hide the cursor for this layer of interface + m_pGUIController->EnableMouse(false); + m_ModeChange = false; + } + g_UInputMan.DisableKeys(false); + } + // We are doing something int he dialog boxes, so don't do anything in the editor interface or show any text messages + else { + m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); + g_FrameMan.ClearScreenText(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. + ///////////////////////////////////////////////////// + // Update the editor interface + + m_pEditorGUI->Update(); + + // Any edits made, dirtying the scene? + m_NeedSave = m_pEditorGUI->EditMade() || m_NeedSave; + + // Get any mode change commands that the user gave the Editor GUI + if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorNew && m_EditorMode != NEWDIALOG) { + m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::NEWDIALOG; + m_ModeChange = true; + // This is ahack so we don't get a 'save changes dialog' when we jsut want to create a new area. + // Will turn on dirtyness immediately as New button is pressed below + m_NeedSave = false; + } else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorLoad && m_EditorMode != LOADDIALOG) { + m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::LOADDIALOG; + m_ModeChange = true; + } else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorSave && m_EditorMode != SAVEDIALOG) { + m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::SAVEDIALOG; + m_ModeChange = true; + } + // Test the scene by starting a Skirmish Defense with it, after saving + else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorDone || m_EditorMode == TESTINGOBJECT) { + m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::INACTIVE); + + if (m_NeedSave) { + m_PreviousMode = EditorActivity::TESTINGOBJECT; + m_EditorMode = EditorActivity::CHANGESDIALOG; + m_ModeChange = true; + /* + if (m_HasEverBeenSaved) + SaveScene(pCurrentScene->GetPresetName()); + else + { + m_PreviousMode = TESTINGOBJECT; + m_EditorMode = SAVEDIALOG; + m_ModeChange = true; + } + */ + } else { + g_SceneMan.SetSceneToLoad(pCurrentScene->GetPresetName()); + + const Activity* pActivityPreset = dynamic_cast(g_PresetMan.GetEntityPreset("GAScripted", "Skirmish Defense")); + Activity* pActivity = dynamic_cast(pActivityPreset->Clone()); + GameActivity* pTestGame = dynamic_cast(pActivity); + RTEAssert(pTestGame, "Couldn't find the \"Skirmish Defense\" GAScripted Activity! Has it been defined?"); + pTestGame->SetTeamOfPlayer(0, 0); + pTestGame->SetCPUTeam(1); + pTestGame->SetStartingGold(10000); + pTestGame->SetFogOfWarEnabled(false); + pTestGame->SetDifficulty(DifficultySetting::MediumDifficulty); + g_ActivityMan.SetStartActivity(pTestGame); + g_ActivityMan.SetRestartActivity(); + } + } -void AreaEditor::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) -{ - m_pEditorGUI->Draw(pTargetBitmap, targetPos); + //////////////////////////////////////////////////////// + // Handle events for mouse input on the controls + + GUIEvent anEvent; + while (m_pGUIController->GetEvent(&anEvent)) { + // If we're not supposed to have mouse control, then ignore these messages + // Uh this is not right, editor always has mouse control so far + // if (!m_PlayerController[0].IsMouseControlled()) + // break; + + if (anEvent.GetType() == GUIEvent::Command) { + ////////////////////////////////////////////////////////// + // NEW button pressed; create a Area in the current scene + + if (anEvent.GetControl() == m_pNewButton) { + // Make sure we're not trying to create a noname area + if (!m_pNewAreaName->GetText().empty()) { + // Check if name is already taken, and if so, select the taken one instead of creating a new + if (Scene::Area* pArea = pCurrentScene->GetArea(m_pNewAreaName->GetText())) { + m_pEditorGUI->SetCurrentArea(pArea); + m_pEditorGUI->SetEditorGUIMode(AreaEditorGUI::PREADDMOVEBOX); + } else { + // Make and name new Area + Scene::Area newArea(m_pNewAreaName->GetText()); + pCurrentScene->m_AreaList.push_back(newArea); + // Set the new area as the active one in the GUI, note we're getting the correct one from the scene, it's a copy of the one passed in + m_pEditorGUI->SetCurrentArea(pCurrentScene->GetArea(newArea.GetName())); + // Update teh picker list of the GUI so we can mousewheel between all the Areas, incl the new one + m_pEditorGUI->UpdatePickerList(newArea.GetName()); + } - EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); -} + // Change mode to start editing the new/newly selected Area + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + m_NeedSave = true; + } else { + // TODO: Play some error sound? + } + } + ////////////////////////////////////////////////////////// + // LOAD button pressed; load the selected Scene + + if (anEvent.GetControl() == m_pLoadButton) { + GUIListPanel::Item* pItem = m_pLoadNameCombo->GetItem(m_pLoadNameCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) { + // Attempt to load the scene, without applying its placed objects + g_SceneMan.SetSceneToLoad(pItem->m_Name, false); + g_SceneMan.LoadScene(); + // Get the Module ID that the scene exists in, so we can limit the picker to only show objects from that DataModule space + m_ModuleSpaceID = g_SceneMan.GetScene()->GetModuleID(); + if (pCurrentScene) { + m_pEditorGUI->Destroy(); + m_pEditorGUI->Create(&(m_PlayerController[0]), true); + // TODO: Should read in all the already placed objects in the loaded scene and have them appear int he editor instead + } + } + m_NeedSave = false; + m_HasEverBeenSaved = true; + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this AreaEditor's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. + ////////////////////////////////////////////////////////// + // SAVE button pressed; save the selected Scene + + if (anEvent.GetControl() == m_pSaveButton) { + if (!m_pSaveNameBox->GetText().empty()) { + // Save the scene to the name specified in the text box + if (SaveScene(m_pSaveNameBox->GetText())) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the scene + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + // Should really leave dialog box open? + else { + ; + } + } + } -void AreaEditor::Draw(BITMAP* pTargetBitmap, const Vector &targetPos) -{ - EditorActivity::Draw(pTargetBitmap, targetPos); -} + /////////////////////////////////////////////////////////////// + // Save Changes YES pressed + + if (anEvent.GetControl() == m_pChangesYesButton) { + if (m_HasEverBeenSaved) { + if (SaveScene(pCurrentScene->GetPresetName(), true)) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the scene + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + } + // Open the save scene dialog to ask user where to save it then + else { + m_PreviousMode = m_PreviousMode; + m_EditorMode = EditorActivity::SAVEDIALOG; + m_ModeChange = true; + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////// + // Save Changes NO pressed -bool AreaEditor::SaveScene(const std::string &saveAsName, bool forceOverwrite) { - Scene *editedScene = g_SceneMan.GetScene(); - editedScene->SetPresetName(saveAsName); + if (anEvent.GetControl() == m_pChangesNoButton) { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + m_NeedSave = false; + } - std::string dataModuleName = g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName(); - bool savingToUserScenesModule = (dataModuleName == c_UserScenesModuleName); + /////////////////////////////////////////////////////////////// + // Overwrite Scene YES pressed + + if (anEvent.GetControl() == m_pOverwriteYesButton) { + // Force overwrite + if (SaveScene(pCurrentScene->GetPresetName(), true)) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after overwrite dialog is done, may have been on the way to test the scene + m_EditorMode = m_PreviousMode != EditorActivity::SAVEDIALOG ? m_PreviousMode : EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + // TODO: Show overwrite error? + } - std::string dataModuleFullPath = g_PresetMan.GetFullModulePath(dataModuleName); - std::string sceneSavePath; - std::string previewSavePath; + /////////////////////////////////////////////////////////////// + // Overwrite Scene NO pressed - if (savingToUserScenesModule) { - sceneSavePath = dataModuleFullPath + "/" + saveAsName + ".ini"; - previewSavePath = dataModuleFullPath + "/" + saveAsName + ".preview.png"; - } else { - sceneSavePath = dataModuleFullPath + "/Scenes/" + saveAsName + ".ini"; - previewSavePath = dataModuleFullPath + "/Scenes/" + saveAsName + ".preview.png"; - } + if (anEvent.GetControl() == m_pOverwriteNoButton) { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } - if (g_PresetMan.AddEntityPreset(editedScene, m_ModuleSpaceID, forceOverwrite, sceneSavePath)) { - if (Writer sceneWriter(sceneSavePath, false); !sceneWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + sceneSavePath + "\n\nTHE EDITED SCENE PRESET WAS NOT SAVED!!!"); - } else { - // TODO: Check if the ini file already exists, and then ask if overwrite. - sceneWriter.NewPropertyWithValue("AddScene", editedScene); - sceneWriter.EndWrite(); - - editedScene->SavePreview(previewSavePath); - m_HasEverBeenSaved = true; - - if (!savingToUserScenesModule) { - // First find/create a Scenes.ini file to include the new .ini into. - std::string scenesFilePath(dataModuleFullPath + "/Scenes.ini"); - bool scenesFileExists = System::PathExistsCaseSensitive(scenesFilePath); - - if (Writer scenesFileWriter(scenesFilePath, true); !scenesFileWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + scenesFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); - } else { - scenesFileWriter.NewPropertyWithValue("IncludeFile", sceneSavePath); - scenesFileWriter.EndWrite(); - - // Append to the end of the modules' Index.ini to include the newly created Scenes.ini next startup. - // If it's somehow already included without actually existing, it doesn't matter, the definitions will just bounce the second time. - if (!scenesFileExists) { - std::string indexFilePath = dataModuleFullPath + "/Index.ini"; - - if (Writer indexWriter(indexFilePath, true); !indexWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + indexFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); - } else { - // Add extra tab since the DataModule has everything indented. - indexWriter.NewProperty("\tIncludeFile"); - indexWriter << scenesFilePath; - indexWriter.EndWrite(); - } - } + /////////////////////////////////////////////////////////////// + // CANCEL button pressed; exit any active dialog box + + if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) { + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; } } - return true; + + // Notifications + else if (anEvent.GetType() == GUIEvent::Notification) { + /* + /////////////////////////////////////// + // Clicks on the New Scene Module combo + + if (anEvent.GetControl() == m_pNewModuleCombo) + { + // Closed it, IE selected somehting + if(anEvent.GetMsg() == GUIComboBox::Closed) + UpdateNewDialog(); + } + */ + } } - } else { - // Got to ask if we can overwrite the existing preset. - m_PreviousMode = EditorMode::SAVEDIALOG; - m_EditorMode = EditorMode::OVERWRITEDIALOG; - m_ModeChange = true; } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateNewDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New dialog box, populates its lists etc. + void AreaEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + m_pEditorGUI->Draw(pTargetBitmap, targetPos); -void AreaEditor::UpdateNewDialog() -{ - // Reset the new Area name text field - if (m_pNewAreaName->GetText().empty()) - m_pNewAreaName->SetText("Test Area 1"); -} + EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this AreaEditor's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateLoadDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Load dialog box, populates its lists etc. + void AreaEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + EditorActivity::Draw(pTargetBitmap, targetPos); + } -void AreaEditor::UpdateLoadDialog() -{ - // Clear out the control - m_pLoadNameCombo->ClearList(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Get the list of all read in scenes - std::list sceneList; - g_PresetMan.GetAllOfType(sceneList, "Scene"); + bool AreaEditor::SaveScene(const std::string& saveAsName, bool forceOverwrite) { + Scene* editedScene = g_SceneMan.GetScene(); + editedScene->SetPresetName(saveAsName); - // Go through the list and add their names to the combo box - for (std::list::iterator itr = sceneList.begin(); itr != sceneList.end(); ++itr) - { - Scene * pScene = dynamic_cast(*itr); - if (pScene) - // Don't add the special "Editor Scene" or metascenes, users shouldn't be messing with them - if (pScene->GetPresetName() != "Editor Scene" && !pScene->IsMetagameInternal() && !pScene->IsSavedGameInternal() && (pScene->GetMetasceneParent() == "" || g_SettingsMan.ShowMetascenes())) - m_pLoadNameCombo->AddItem(pScene->GetPresetName()); - } + std::string dataModuleName = g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName(); + bool savingToUserScenesModule = (dataModuleName == c_UserScenesModuleName); - // Select the first one - m_pLoadNameCombo->SetSelectedIndex(0); -} + std::string dataModuleFullPath = g_PresetMan.GetFullModulePath(dataModuleName); + std::string sceneSavePath; + std::string previewSavePath; + if (savingToUserScenesModule) { + sceneSavePath = dataModuleFullPath + "/" + saveAsName + ".ini"; + previewSavePath = dataModuleFullPath + "/" + saveAsName + ".preview.png"; + } else { + sceneSavePath = dataModuleFullPath + "/Scenes/" + saveAsName + ".ini"; + previewSavePath = dataModuleFullPath + "/Scenes/" + saveAsName + ".preview.png"; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateSaveDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save dialog box, populates its lists etc. + if (g_PresetMan.AddEntityPreset(editedScene, m_ModuleSpaceID, forceOverwrite, sceneSavePath)) { + if (Writer sceneWriter(sceneSavePath, false); !sceneWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + sceneSavePath + "\n\nTHE EDITED SCENE PRESET WAS NOT SAVED!!!"); + } else { + // TODO: Check if the ini file already exists, and then ask if overwrite. + sceneWriter.NewPropertyWithValue("AddScene", editedScene); + sceneWriter.EndWrite(); + + editedScene->SavePreview(previewSavePath); + m_HasEverBeenSaved = true; + + if (!savingToUserScenesModule) { + // First find/create a Scenes.ini file to include the new .ini into. + std::string scenesFilePath(dataModuleFullPath + "/Scenes.ini"); + bool scenesFileExists = System::PathExistsCaseSensitive(scenesFilePath); + + if (Writer scenesFileWriter(scenesFilePath, true); !scenesFileWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + scenesFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); + } else { + scenesFileWriter.NewPropertyWithValue("IncludeFile", sceneSavePath); + scenesFileWriter.EndWrite(); + + // Append to the end of the modules' Index.ini to include the newly created Scenes.ini next startup. + // If it's somehow already included without actually existing, it doesn't matter, the definitions will just bounce the second time. + if (!scenesFileExists) { + std::string indexFilePath = dataModuleFullPath + "/Index.ini"; + + if (Writer indexWriter(indexFilePath, true); !indexWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + indexFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); + } else { + // Add extra tab since the DataModule has everything indented. + indexWriter.NewProperty("\tIncludeFile"); + indexWriter << scenesFilePath; + indexWriter.EndWrite(); + } + } + } + } + return true; + } + } else { + // Got to ask if we can overwrite the existing preset. + m_PreviousMode = EditorMode::SAVEDIALOG; + m_EditorMode = EditorMode::OVERWRITEDIALOG; + m_ModeChange = true; + } + return false; + } -void AreaEditor::UpdateSaveDialog() -{ - m_pSaveNameBox->SetText((g_SceneMan.GetScene()->GetPresetName() == "None" || !m_HasEverBeenSaved) ? "New Scene" : g_SceneMan.GetScene()->GetPresetName()); - if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) - m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/"); - else - m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes"); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateNewDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New dialog box, populates its lists etc. + void AreaEditor::UpdateNewDialog() { + // Reset the new Area name text field + if (m_pNewAreaName->GetText().empty()) + m_pNewAreaName->SetText("Test Area 1"); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChangesDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save Changes dialog box, populates its lists etc. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateLoadDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Load dialog box, populates its lists etc. + + void AreaEditor::UpdateLoadDialog() { + // Clear out the control + m_pLoadNameCombo->ClearList(); + + // Get the list of all read in scenes + std::list sceneList; + g_PresetMan.GetAllOfType(sceneList, "Scene"); + + // Go through the list and add their names to the combo box + for (std::list::iterator itr = sceneList.begin(); itr != sceneList.end(); ++itr) { + Scene* pScene = dynamic_cast(*itr); + if (pScene) + // Don't add the special "Editor Scene" or metascenes, users shouldn't be messing with them + if (pScene->GetPresetName() != "Editor Scene" && !pScene->IsMetagameInternal() && !pScene->IsSavedGameInternal() && (pScene->GetMetasceneParent() == "" || g_SettingsMan.ShowMetascenes())) + m_pLoadNameCombo->AddItem(pScene->GetPresetName()); + } -void AreaEditor::UpdateChangesDialog() -{ - if (m_HasEverBeenSaved) - { - dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); + // Select the first one + m_pLoadNameCombo->SetSelectedIndex(0); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateSaveDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save dialog box, populates its lists etc. + + void AreaEditor::UpdateSaveDialog() { + m_pSaveNameBox->SetText((g_SceneMan.GetScene()->GetPresetName() == "None" || !m_HasEverBeenSaved) ? "New Scene" : g_SceneMan.GetScene()->GetPresetName()); if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) - m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); + m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/"); else - m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); - } - else - { - dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Save your new Scene first?"); - m_pChangesNameLabel->SetText(""); - } -} + m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes"); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateChangesDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save Changes dialog box, populates its lists etc. + + void AreaEditor::UpdateChangesDialog() { + if (m_HasEverBeenSaved) { + dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); + if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) + m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); + else + m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); + } else { + dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Save your new Scene first?"); + m_pChangesNameLabel->SetText(""); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateOverwriteDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Overwrite dialog box, populates its lists etc. - -void AreaEditor::UpdateOverwriteDialog() -{ - if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) - m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); - else - m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateOverwriteDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Overwrite dialog box, populates its lists etc. + + void AreaEditor::UpdateOverwriteDialog() { + if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) + m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); + else + m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); + } } // namespace RTE \ No newline at end of file diff --git a/Source/Activities/AreaEditor.h b/Source/Activities/AreaEditor.h index 900af5070a..79e82788a4 100644 --- a/Source/Activities/AreaEditor.h +++ b/Source/Activities/AreaEditor.h @@ -10,292 +10,265 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" -namespace RTE -{ - -class AreaEditorGUI; -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUITab; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUILabel; -class GUIComboBox; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: AreaEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activity for editing Scene:s' Area:s. -// Parent(s): EditorActivity. -// Class history: 7/21/2008 AreaEditor created, based off SceneEditor - -class AreaEditor : public EditorActivity { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(AreaEditor); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: AreaEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a AreaEditor object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - AreaEditor() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~AreaEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a AreaEditor object before deletion -// from system memory. -// Arguments: None. - - ~AreaEditor() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AreaEditor object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a AreaEditor to be identical to another, by deep copy. -// Arguments: A reference to the AreaEditor to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const AreaEditor &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire AreaEditor, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); EditorActivity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AreaEditor object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - /// - /// Saves the current Scene to an appropriate ini file, and asks user if they want to overwrite first if scene of this name exists. - /// - /// The name of the new Scene to be saved. - /// Whether to force any existing Scene of that name to be overwritten if it already exists. - /// Whether actually managed to save. Will return false both if a scene of this name already exists, or if other error. - bool SaveScene(const std::string &saveAsName, bool forceOverwrite = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateNewDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateNewDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateLoadDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Load dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateLoadDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateSaveDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateSaveDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateChangesDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save Changes dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateChangesDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateOverwriteDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Overwrite dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateOverwriteDialog() override; - - - // Member variables - static Entity::ClassInfo m_sClass; - - // The editor GUI - AreaEditorGUI *m_pEditorGUI; - - // The textbox for entering new Area names - GUITextBox *m_pNewAreaName; -// // Number which -// int m_NewAreaNumber; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class AreaEditorGUI; + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUITab; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUILabel; + class GUIComboBox; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: AreaEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activity for editing Scene:s' Area:s. + // Parent(s): EditorActivity. + // Class history: 7/21/2008 AreaEditor created, based off SceneEditor + + class AreaEditor : public EditorActivity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(AreaEditor); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: AreaEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a AreaEditor object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + AreaEditor() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~AreaEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a AreaEditor object before deletion + // from system memory. + // Arguments: None. + + ~AreaEditor() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the AreaEditor object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a AreaEditor to be identical to another, by deep copy. + // Arguments: A reference to the AreaEditor to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const AreaEditor& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire AreaEditor, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + EditorActivity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the AreaEditor object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Saves the current Scene to an appropriate ini file, and asks user if they want to overwrite first if scene of this name exists. + /// + /// The name of the new Scene to be saved. + /// Whether to force any existing Scene of that name to be overwritten if it already exists. + /// Whether actually managed to save. Will return false both if a scene of this name already exists, or if other error. + bool SaveScene(const std::string& saveAsName, bool forceOverwrite = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateNewDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateNewDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateLoadDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Load dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateLoadDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateSaveDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateSaveDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateChangesDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save Changes dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateChangesDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateOverwriteDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Overwrite dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateOverwriteDialog() override; + + // Member variables + static Entity::ClassInfo m_sClass; + + // The editor GUI + AreaEditorGUI* m_pEditorGUI; + + // The textbox for entering new Area names + GUITextBox* m_pNewAreaName; + // // Number which + // int m_NewAreaNumber; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Activities/AssemblyEditor.cpp b/Source/Activities/AssemblyEditor.cpp index abf3a9dad0..36c92622a7 100644 --- a/Source/Activities/AssemblyEditor.cpp +++ b/Source/Activities/AssemblyEditor.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -41,775 +40,680 @@ namespace RTE { -ConcreteClassInfo(AssemblyEditor, EditorActivity, 0); + ConcreteClassInfo(AssemblyEditor, EditorActivity, 0); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this AssemblyEditor, effectively + // resetting the members of this abstraction level only. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AssemblyEditor, effectively -// resetting the members of this abstraction level only. + void AssemblyEditor::Clear() { + m_pEditorGUI = 0; + m_pModuleCombo = 0; + } -void AssemblyEditor::Clear() -{ - m_pEditorGUI = 0; - m_pModuleCombo = 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the AssemblyEditor object ready for use. + int AssemblyEditor::Create() { + if (EditorActivity::Create() < 0) + return -1; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AssemblyEditor object ready for use. + return 0; + } -int AssemblyEditor::Create() -{ - if (EditorActivity::Create() < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a AssemblyEditor to be identical to another, by deep copy. + int AssemblyEditor::Create(const AssemblyEditor& reference) { + if (EditorActivity::Create(reference) < 0) + return -1; - return 0; -} + if (m_Description.empty()) + m_Description = "Edit this Scene, including placement of all terrain objects and movable objects, AI blueprints, etc."; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a AssemblyEditor to be identical to another, by deep copy. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int AssemblyEditor::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); + /* + MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); + MatchProperty("Difficulty", { reader >> m_Difficulty; }); + MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); + */ + EndPropertyList; + } -int AssemblyEditor::Create(const AssemblyEditor &reference) -{ - if (EditorActivity::Create(reference) < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this AssemblyEditor with a Writer for + // later recreation with Create(Reader &reader); - if (m_Description.empty()) - m_Description = "Edit this Scene, including placement of all terrain objects and movable objects, AI blueprints, etc."; + int AssemblyEditor::Save(Writer& writer) const { + EditorActivity::Save(writer); + return 0; + } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the AssemblyEditor object. + void AssemblyEditor::Destroy(bool notInherited) { + delete m_pEditorGUI; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int AssemblyEditor::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); -/* - MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); - MatchProperty("Difficulty", { reader >> m_Difficulty; }); - MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); -*/ - EndPropertyList; -} + if (!notInherited) + EditorActivity::Destroy(); + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this AssemblyEditor with a Writer for -// later recreation with Create(Reader &reader); + int AssemblyEditor::Start() { + int error = EditorActivity::Start(); -int AssemblyEditor::Save(Writer &writer) const { - EditorActivity::Save(writer); - return 0; -} + ////////////////////////////////////////////// + // Allocate and (re)create the Editor GUI + if (m_pEditorGUI) + m_pEditorGUI->Destroy(); + else + m_pEditorGUI = new AssemblyEditorGUI; + m_pEditorGUI->Create(&(m_PlayerController[0]), AssemblyEditorGUI::ONLOADEDIT); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AssemblyEditor object. + ////////////////////////////////////////////////////////////// + // Hooking up directly to the controls defined in the GUI ini -void AssemblyEditor::Destroy(bool notInherited) -{ - delete m_pEditorGUI; + m_pGUIController->Load("Base.rte/GUIs/AssemblyEditorGUI.ini"); - if (!notInherited) - EditorActivity::Destroy(); - Clear(); -} + // Resize the invisible root container so it matches the screen rez + GUICollectionBox* pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); + if (pRootBox) + pRootBox->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of + if (!m_pLoadDialogBox) { + m_pLoadDialogBox = dynamic_cast(m_pGUIController->GetControl("LoadDialogBox")); + // m_pLoadDialogBox->SetDrawType(GUICollectionBox::Color); + m_pLoadDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pLoadDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pLoadDialogBox->GetHeight() / 2)); + m_pLoadDialogBox->SetVisible(false); + } + m_pLoadNameCombo = dynamic_cast(m_pGUIController->GetControl("LoadSceneCB")); + m_pLoadNameCombo->SetDropHeight(std::min(m_pLoadNameCombo->GetDropHeight(), g_WindowMan.GetResY() / 2)); + m_pModuleCombo = dynamic_cast(m_pGUIController->GetControl("ModuleCB")); + m_pModuleCombo->SetDropHeight(std::min(m_pModuleCombo->GetDropHeight(), g_WindowMan.GetResY() / 2)); + m_pLoadDialogBox->SetSize(m_pLoadDialogBox->GetWidth(), m_pLoadDialogBox->GetHeight() + (std::max(m_pLoadNameCombo->GetDropHeight(), m_pModuleCombo->GetDropHeight()))); // Make sure the dropdowns can fit, no matter how tall they are. + m_pLoadButton = dynamic_cast(m_pGUIController->GetControl("LoadSceneButton")); + m_pLoadCancel = dynamic_cast(m_pGUIController->GetControl("LoadCancelButton")); + + if (!m_pSaveDialogBox) { + m_pSaveDialogBox = dynamic_cast(m_pGUIController->GetControl("SaveDialogBox")); + + // Set the background image of the parent collection box + m_pSaveDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pSaveDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pSaveDialogBox->GetHeight() / 2)); + m_pSaveDialogBox->SetVisible(false); + } + m_pSaveNameBox = dynamic_cast(m_pGUIController->GetControl("SaveSceneNameTB")); + m_pSaveModuleLabel = dynamic_cast(m_pGUIController->GetControl("SaveModuleLabel")); + m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveSceneButton")); + m_pSaveCancel = dynamic_cast(m_pGUIController->GetControl("SaveCancelButton")); + + if (!m_pChangesDialogBox) { + m_pChangesDialogBox = dynamic_cast(m_pGUIController->GetControl("ChangesDialogBox")); + m_pChangesDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pChangesDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pChangesDialogBox->GetHeight() / 2)); + m_pChangesDialogBox->SetVisible(false); + } + m_pChangesNameLabel = dynamic_cast(m_pGUIController->GetControl("ChangesNameLabel")); + m_pChangesYesButton = dynamic_cast(m_pGUIController->GetControl("ChangesYesButton")); + m_pChangesNoButton = dynamic_cast(m_pGUIController->GetControl("ChangesNoButton")); + + if (!m_pOverwriteDialogBox) { + m_pOverwriteDialogBox = dynamic_cast(m_pGUIController->GetControl("OverwriteDialogBox")); + m_pOverwriteDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pOverwriteDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pOverwriteDialogBox->GetHeight() / 2)); + m_pOverwriteDialogBox->SetVisible(false); + } + m_pOverwriteNameLabel = dynamic_cast(m_pGUIController->GetControl("OverwriteNameLabel")); + m_pOverwriteYesButton = dynamic_cast(m_pGUIController->GetControl("OverwriteYesButton")); + m_pOverwriteNoButton = dynamic_cast(m_pGUIController->GetControl("OverwriteNoButton")); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. - -int AssemblyEditor::Start() -{ - int error = EditorActivity::Start(); - - ////////////////////////////////////////////// - // Allocate and (re)create the Editor GUI - - if (m_pEditorGUI) - m_pEditorGUI->Destroy(); - else - m_pEditorGUI = new AssemblyEditorGUI; - m_pEditorGUI->Create(&(m_PlayerController[0]), AssemblyEditorGUI::ONLOADEDIT); - - ////////////////////////////////////////////////////////////// - // Hooking up directly to the controls defined in the GUI ini - - m_pGUIController->Load("Base.rte/GUIs/AssemblyEditorGUI.ini"); - - // Resize the invisible root container so it matches the screen rez - GUICollectionBox *pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); - if (pRootBox) - pRootBox->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); - - // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of - if (!m_pLoadDialogBox) - { - m_pLoadDialogBox = dynamic_cast(m_pGUIController->GetControl("LoadDialogBox")); -// m_pLoadDialogBox->SetDrawType(GUICollectionBox::Color); - m_pLoadDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pLoadDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pLoadDialogBox->GetHeight() / 2)); - m_pLoadDialogBox->SetVisible(false); - } - m_pLoadNameCombo = dynamic_cast(m_pGUIController->GetControl("LoadSceneCB")); - m_pLoadNameCombo->SetDropHeight(std::min(m_pLoadNameCombo->GetDropHeight(), g_WindowMan.GetResY() / 2)); - m_pModuleCombo = dynamic_cast(m_pGUIController->GetControl("ModuleCB")); - m_pModuleCombo->SetDropHeight(std::min(m_pModuleCombo->GetDropHeight(), g_WindowMan.GetResY() / 2)); - m_pLoadDialogBox->SetSize(m_pLoadDialogBox->GetWidth(), m_pLoadDialogBox->GetHeight() + (std::max(m_pLoadNameCombo->GetDropHeight(), m_pModuleCombo->GetDropHeight()))); // Make sure the dropdowns can fit, no matter how tall they are. - m_pLoadButton = dynamic_cast(m_pGUIController->GetControl("LoadSceneButton")); - m_pLoadCancel = dynamic_cast(m_pGUIController->GetControl("LoadCancelButton")); - - if (!m_pSaveDialogBox) - { - m_pSaveDialogBox = dynamic_cast(m_pGUIController->GetControl("SaveDialogBox")); - - // Set the background image of the parent collection box - m_pSaveDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pSaveDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pSaveDialogBox->GetHeight() / 2)); - m_pSaveDialogBox->SetVisible(false); - } - m_pSaveNameBox = dynamic_cast(m_pGUIController->GetControl("SaveSceneNameTB")); - m_pSaveModuleLabel = dynamic_cast(m_pGUIController->GetControl("SaveModuleLabel")); - m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveSceneButton")); - m_pSaveCancel = dynamic_cast(m_pGUIController->GetControl("SaveCancelButton")); - - if (!m_pChangesDialogBox) - { - m_pChangesDialogBox = dynamic_cast(m_pGUIController->GetControl("ChangesDialogBox")); - m_pChangesDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pChangesDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pChangesDialogBox->GetHeight() / 2)); - m_pChangesDialogBox->SetVisible(false); - } - m_pChangesNameLabel = dynamic_cast(m_pGUIController->GetControl("ChangesNameLabel")); - m_pChangesYesButton = dynamic_cast(m_pGUIController->GetControl("ChangesYesButton")); - m_pChangesNoButton = dynamic_cast(m_pGUIController->GetControl("ChangesNoButton")); - - if (!m_pOverwriteDialogBox) - { - m_pOverwriteDialogBox = dynamic_cast(m_pGUIController->GetControl("OverwriteDialogBox")); - m_pOverwriteDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pOverwriteDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pOverwriteDialogBox->GetHeight() / 2)); - m_pOverwriteDialogBox->SetVisible(false); - } - m_pOverwriteNameLabel = dynamic_cast(m_pGUIController->GetControl("OverwriteNameLabel")); - m_pOverwriteYesButton = dynamic_cast(m_pGUIController->GetControl("OverwriteYesButton")); - m_pOverwriteNoButton = dynamic_cast(m_pGUIController->GetControl("OverwriteNoButton")); - - return error; -} + return error; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. + void AssemblyEditor::SetPaused(bool pause) { + // Override the pause + m_Paused = false; + } -void AssemblyEditor::SetPaused(bool pause) -{ - // Override the pause - m_Paused = false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + void AssemblyEditor::End() { + EditorActivity::End(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. + m_ActivityState = ActivityState::Over; + } -void AssemblyEditor::End() -{ - EditorActivity::End(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this AssemblyEditor. Supposed to be done every frame + // before drawing. + + void AssemblyEditor::Update() { + EditorActivity::Update(); + + if (!g_SceneMan.GetScene()) + return; + + // Update the loaded objects of the loaded scene so they look right + g_SceneMan.GetScene()->UpdatePlacedObjects(Scene::PLACEONLOAD); + + // All dialog boxes are gone and we're editing the scene + if (m_EditorMode == EditorActivity::EDITINGOBJECT) { + if (m_ModeChange) { + // Open the picker depending on whetehr there's somehting in the cursor hand or not + m_pEditorGUI->SetEditorGUIMode(m_pEditorGUI->GetCurrentObject() ? AssemblyEditorGUI::ADDINGOBJECT : AssemblyEditorGUI::PICKINGOBJECT); + // Hide the cursor for this layer of interface + m_pGUIController->EnableMouse(false); + m_ModeChange = false; + } + g_UInputMan.DisableKeys(false); + } + // We are doing something int he dialog boxes, so don't do anything in the editor interface + else + m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); + + ///////////////////////////////////////////////////// + // Update the editor interface + + m_pEditorGUI->Update(); + + // Any edits made, dirtying the scene? + m_NeedSave = m_pEditorGUI->EditMade() || m_NeedSave; + + // Get any mode change commands that the user gave the Editor GUI + if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorNew && m_EditorMode != NEWDIALOG) { + m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::NEWDIALOG; + m_ModeChange = true; + } else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorLoad && m_EditorMode != LOADDIALOG) { + m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::LOADDIALOG; + m_ModeChange = true; + } else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorSave && m_EditorMode != SAVEDIALOG) { + m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::SAVEDIALOG; + m_ModeChange = true; + } + //////////////////////////////////////////////////////// + // Handle events for mouse input on the controls + GUIEvent anEvent; + while (m_pGUIController->GetEvent(&anEvent)) { + // If we're not supposed to have mouse control, then ignore these messages + // Uh this is not right, editor always has mouse control so far + // if (!m_PlayerController[0].IsMouseControlled()) + // break; - m_ActivityState = ActivityState::Over; -} + if (anEvent.GetType() == GUIEvent::Command) { + ////////////////////////////////////////////////////////// + // LOAD TO NEW button pressed; go from the load to the new dialog + if (anEvent.GetControl() == m_pLoadToNewButton) { + m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::NEWDIALOG; + m_ModeChange = true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this AssemblyEditor. Supposed to be done every frame -// before drawing. - -void AssemblyEditor::Update() -{ - EditorActivity::Update(); - - if (!g_SceneMan.GetScene()) - return; - - // Update the loaded objects of the loaded scene so they look right - g_SceneMan.GetScene()->UpdatePlacedObjects(Scene::PLACEONLOAD); - - // All dialog boxes are gone and we're editing the scene - if (m_EditorMode == EditorActivity::EDITINGOBJECT) - { - if (m_ModeChange) - { - // Open the picker depending on whetehr there's somehting in the cursor hand or not - m_pEditorGUI->SetEditorGUIMode(m_pEditorGUI->GetCurrentObject() ? AssemblyEditorGUI::ADDINGOBJECT : AssemblyEditorGUI::PICKINGOBJECT); - // Hide the cursor for this layer of interface - m_pGUIController->EnableMouse(false); - m_ModeChange = false; - } - g_UInputMan.DisableKeys(false); - } - // We are doing something int he dialog boxes, so don't do anything in the editor interface - else - m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); - - - ///////////////////////////////////////////////////// - // Update the editor interface - - m_pEditorGUI->Update(); - - // Any edits made, dirtying the scene? - m_NeedSave = m_pEditorGUI->EditMade() || m_NeedSave; - - // Get any mode change commands that the user gave the Editor GUI - if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorNew && m_EditorMode != NEWDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::NEWDIALOG; - m_ModeChange = true; - } - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorLoad && m_EditorMode != LOADDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::LOADDIALOG; - m_ModeChange = true; - } - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorSave && m_EditorMode != SAVEDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::SAVEDIALOG; - m_ModeChange = true; - } - - //////////////////////////////////////////////////////// - // Handle events for mouse input on the controls - - GUIEvent anEvent; - while(m_pGUIController->GetEvent(&anEvent)) - { - // If we're not supposed to have mouse control, then ignore these messages -// Uh this is not right, editor always has mouse control so far -// if (!m_PlayerController[0].IsMouseControlled()) -// break; - - if (anEvent.GetType() == GUIEvent::Command) - { - ////////////////////////////////////////////////////////// - // LOAD TO NEW button pressed; go from the load to the new dialog - - if (anEvent.GetControl() == m_pLoadToNewButton) - { - m_pEditorGUI->SetEditorGUIMode(AssemblyEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::NEWDIALOG; - m_ModeChange = true; - } - - ////////////////////////////////////////////////////////// - // LOAD button pressed; load the selected Scene - - if (anEvent.GetControl() == m_pLoadButton) - { - GUIListPanel::Item *pItem = m_pLoadNameCombo->GetItem(m_pLoadNameCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - // Attempt to load the scene, without applying its placed objects - g_SceneMan.SetSceneToLoad(pItem->m_Name, false); - g_SceneMan.LoadScene(); - // Get the Module ID that the scene exists in, so we can limit the picker to only show objects from that DataModule space - if (g_SceneMan.GetScene()) - { - //m_ModuleSpaceID = g_SceneMan.GetScene()->GetModuleID(); - m_ModuleSpaceID = g_PresetMan.GetModuleID(m_pModuleCombo->GetSelectedItem()->m_Name); - RTEAssert(m_ModuleSpaceID >= 0, "Loaded Scene's DataModule ID is negative? Should always be a specific one.."); - m_pEditorGUI->Destroy(); - if (m_ModuleSpaceID == g_PresetMan.GetModuleID(c_UserScenesModuleName)) - m_pEditorGUI->Create(&(m_PlayerController[0]), AssemblyEditorGUI::ONLOADEDIT, -1); - else - m_pEditorGUI->Create(&(m_PlayerController[0]), AssemblyEditorGUI::ONLOADEDIT, m_ModuleSpaceID); -// TODO: Should read in all the already placed objects in the loaded scene and have them appear int he editor instead - } - } - m_NeedSave = false; - m_HasEverBeenSaved = true; - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - - ////////////////////////////////////////////////////////// - // SAVE button pressed; save the selected Scene - if (anEvent.GetControl() == m_pSaveButton) - { - if (!m_pSaveNameBox->GetText().empty()) - { - // Save the scene to the name specified in the text box - if (SaveAssembly(m_pSaveNameBox->GetText())) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the scene - m_EditorMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - // Should really leave dialog box open? - else - { - ; - } - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes YES pressed - - if (anEvent.GetControl() == m_pChangesYesButton) - { - if (m_HasEverBeenSaved) - { - if (SaveAssembly(m_pEditorGUI->GetCurrentAssemblyName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the scene - m_EditorMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - } - // Open the save scene dialog to ask user where to save it then - else - { - m_PreviousMode = m_PreviousMode; - m_EditorMode = EditorActivity::SAVEDIALOG; - m_ModeChange = true; - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes NO pressed - - if (anEvent.GetControl() == m_pChangesNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - m_NeedSave = false; - } - - /////////////////////////////////////////////////////////////// - // Overwrite Scene YES pressed - - if (anEvent.GetControl() == m_pOverwriteYesButton) - { - // Force overwrite - if (SaveAssembly(m_pEditorGUI->GetCurrentAssemblyName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after overwrite dialog is done, may have been on the way to test the scene - m_EditorMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - } - - /////////////////////////////////////////////////////////////// - // Overwrite Scene NO pressed - - if (anEvent.GetControl() == m_pOverwriteNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - - /////////////////////////////////////////////////////////////// - // CANCEL button pressed; exit any active dialog box - - if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) - { - // Don't allow canceling out of diags if we're still in the special "Editor Scene", don't allow users to edit it! - // Just exit the whole editor into the main menu - if (g_SceneMan.GetScene()->GetPresetName() == "Editor Scene") - { - g_ActivityMan.PauseActivity(); - } - // Just do normal cancel of the dialog and go back to editing - else - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - - m_ModeChange = true; - } - } - - // Notifications - else if (anEvent.GetType() == GUIEvent::Notification) - { - /////////////////////////////////////// - // Clicks on the New Scene Module combo - - if (anEvent.GetControl() == m_pNewModuleCombo) - { - // Closed it, IE selected somehting - if(anEvent.GetMsg() == GUIComboBox::Closed) - UpdateNewDialog(); - } - } - } -} + ////////////////////////////////////////////////////////// + // LOAD button pressed; load the selected Scene + + if (anEvent.GetControl() == m_pLoadButton) { + GUIListPanel::Item* pItem = m_pLoadNameCombo->GetItem(m_pLoadNameCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) { + // Attempt to load the scene, without applying its placed objects + g_SceneMan.SetSceneToLoad(pItem->m_Name, false); + g_SceneMan.LoadScene(); + // Get the Module ID that the scene exists in, so we can limit the picker to only show objects from that DataModule space + if (g_SceneMan.GetScene()) { + // m_ModuleSpaceID = g_SceneMan.GetScene()->GetModuleID(); + m_ModuleSpaceID = g_PresetMan.GetModuleID(m_pModuleCombo->GetSelectedItem()->m_Name); + RTEAssert(m_ModuleSpaceID >= 0, "Loaded Scene's DataModule ID is negative? Should always be a specific one.."); + m_pEditorGUI->Destroy(); + if (m_ModuleSpaceID == g_PresetMan.GetModuleID(c_UserScenesModuleName)) + m_pEditorGUI->Create(&(m_PlayerController[0]), AssemblyEditorGUI::ONLOADEDIT, -1); + else + m_pEditorGUI->Create(&(m_PlayerController[0]), AssemblyEditorGUI::ONLOADEDIT, m_ModuleSpaceID); + // TODO: Should read in all the already placed objects in the loaded scene and have them appear int he editor instead + } + } + m_NeedSave = false; + m_HasEverBeenSaved = true; + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + ////////////////////////////////////////////////////////// + // SAVE button pressed; save the selected Scene + if (anEvent.GetControl() == m_pSaveButton) { + if (!m_pSaveNameBox->GetText().empty()) { + // Save the scene to the name specified in the text box + if (SaveAssembly(m_pSaveNameBox->GetText())) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the scene + m_EditorMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + // Should really leave dialog box open? + else { + ; + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. + /////////////////////////////////////////////////////////////// + // Save Changes YES pressed + + if (anEvent.GetControl() == m_pChangesYesButton) { + if (m_HasEverBeenSaved) { + if (SaveAssembly(m_pEditorGUI->GetCurrentAssemblyName(), true)) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the scene + m_EditorMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + } + // Open the save scene dialog to ask user where to save it then + else { + m_PreviousMode = m_PreviousMode; + m_EditorMode = EditorActivity::SAVEDIALOG; + m_ModeChange = true; + } + } -void AssemblyEditor::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) -{ - m_pEditorGUI->Draw(pTargetBitmap, targetPos); + /////////////////////////////////////////////////////////////// + // Save Changes NO pressed - EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); -} + if (anEvent.GetControl() == m_pChangesNoButton) { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + m_NeedSave = false; + } + /////////////////////////////////////////////////////////////// + // Overwrite Scene YES pressed + + if (anEvent.GetControl() == m_pOverwriteYesButton) { + // Force overwrite + if (SaveAssembly(m_pEditorGUI->GetCurrentAssemblyName(), true)) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after overwrite dialog is done, may have been on the way to test the scene + m_EditorMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this AssemblyEditor's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. + /////////////////////////////////////////////////////////////// + // Overwrite Scene NO pressed -void AssemblyEditor::Draw(BITMAP* pTargetBitmap, const Vector &targetPos) -{ - EditorActivity::Draw(pTargetBitmap, targetPos); -} + if (anEvent.GetControl() == m_pOverwriteNoButton) { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + /////////////////////////////////////////////////////////////// + // CANCEL button pressed; exit any active dialog box + if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) { + // Don't allow canceling out of diags if we're still in the special "Editor Scene", don't allow users to edit it! + // Just exit the whole editor into the main menu + if (g_SceneMan.GetScene()->GetPresetName() == "Editor Scene") { + g_ActivityMan.PauseActivity(); + } + // Just do normal cancel of the dialog and go back to editing + else + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildAssembly -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and builds assembly which fits currently selected scheme and returns -// it's pointer. Owhership IS transfered. - -BunkerAssembly * AssemblyEditor::BuildAssembly(std::string saveAsName) -{ - // Create new bunker assembly to save - BunkerAssembly *pBA = new BunkerAssembly(); - pBA->Create(m_pEditorGUI->GetCurrentAssemblyScheme()); - - // Retreive some properties if we're overwriting existing bunker assembly - const BunkerAssembly * pExistingBA = dynamic_cast(g_PresetMan.GetEntityPreset("BunkerAssembly", saveAsName, m_ModuleSpaceID)); - if (pExistingBA) - { - pBA->SetSymmetricAssemblyName(pExistingBA->GetSymmetricAssemblyName()); - } + m_ModeChange = true; + } + } - pBA->SetPresetName(saveAsName); - m_pEditorGUI->SetCurrentAssemblyName(saveAsName); + // Notifications + else if (anEvent.GetType() == GUIEvent::Notification) { + /////////////////////////////////////// + // Clicks on the New Scene Module combo - const std::list *pSceneObjectList = 0; - pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); - for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr) - { - //Check if object fits the assembly box - bool skip = true; + if (anEvent.GetControl() == m_pNewModuleCombo) { + // Closed it, IE selected somehting + if (anEvent.GetMsg() == GUIComboBox::Closed) + UpdateNewDialog(); + } + } + } + } - Vector pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset(); - Vector finalPos = pos; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - if ((pos.m_X >= 0) && (pos.m_X < pBA->GetBitmapWidth()) && - (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) - skip = false; + void AssemblyEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + m_pEditorGUI->Draw(pTargetBitmap, targetPos); - // Try to move scene object across seams and see if it fits into assembly box - if (g_SceneMan.GetScene()->WrapsX()) - { - pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset() + Vector(g_SceneMan.GetScene()->GetWidth(), 0); + EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); + } - if ((pos.m_X >= 0) && (pos.m_X < pBA->GetBitmapWidth()) && - (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) - { - skip = false; - finalPos = pos; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this AssemblyEditor's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. - pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset() - Vector(g_SceneMan.GetScene()->GetWidth(), 0); + void AssemblyEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + EditorActivity::Draw(pTargetBitmap, targetPos); + } - if ((pos.m_X >= 0) && (pos.m_X < pBA->GetBitmapWidth()) && - (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) - { - skip = false; - finalPos = pos; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildAssembly + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and builds assembly which fits currently selected scheme and returns + // it's pointer. Owhership IS transfered. + + BunkerAssembly* AssemblyEditor::BuildAssembly(std::string saveAsName) { + // Create new bunker assembly to save + BunkerAssembly* pBA = new BunkerAssembly(); + pBA->Create(m_pEditorGUI->GetCurrentAssemblyScheme()); + + // Retreive some properties if we're overwriting existing bunker assembly + const BunkerAssembly* pExistingBA = dynamic_cast(g_PresetMan.GetEntityPreset("BunkerAssembly", saveAsName, m_ModuleSpaceID)); + if (pExistingBA) { + pBA->SetSymmetricAssemblyName(pExistingBA->GetSymmetricAssemblyName()); } - if (g_SceneMan.GetScene()->WrapsY()) - { - pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset() + Vector(0, g_SceneMan.GetScene()->GetHeight()); + pBA->SetPresetName(saveAsName); + m_pEditorGUI->SetCurrentAssemblyName(saveAsName); - if ((pos.m_X >= 0) && (pos.m_X < pBA->GetBitmapWidth()) && - (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) - { - skip = false; - finalPos = pos; - } + const std::list* pSceneObjectList = 0; + pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); + for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr) { + // Check if object fits the assembly box + bool skip = true; - pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset() - Vector(0, g_SceneMan.GetScene()->GetHeight()); + Vector pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset(); + Vector finalPos = pos; if ((pos.m_X >= 0) && (pos.m_X < pBA->GetBitmapWidth()) && - (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) - { + (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) skip = false; - finalPos = pos; - } - } - if (!skip) - { - SceneObject *pNewSO = dynamic_cast((*itr)->Clone()); + // Try to move scene object across seams and see if it fits into assembly box + if (g_SceneMan.GetScene()->WrapsX()) { + pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset() + Vector(g_SceneMan.GetScene()->GetWidth(), 0); - //Set position relative to this Bunker Assembly - //pNewSO->SetPos(pNewSO->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset()); - pNewSO->SetPos(finalPos); - pBA->AddPlacedObject(pNewSO); - } - } + if ((pos.m_X >= 0) && (pos.m_X < pBA->GetBitmapWidth()) && + (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) { + skip = false; + finalPos = pos; + } + + pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset() - Vector(g_SceneMan.GetScene()->GetWidth(), 0); + + if ((pos.m_X >= 0) && (pos.m_X < pBA->GetBitmapWidth()) && + (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) { + skip = false; + finalPos = pos; + } + } - return pBA; -} + if (g_SceneMan.GetScene()->WrapsY()) { + pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset() + Vector(0, g_SceneMan.GetScene()->GetHeight()); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if ((pos.m_X >= 0) && (pos.m_X < pBA->GetBitmapWidth()) && + (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) { + skip = false; + finalPos = pos; + } -bool AssemblyEditor::SaveAssembly(const std::string &saveAsName, bool forceOverwrite) { - std::unique_ptr editedAssembly(BuildAssembly(saveAsName)); + pos = (*itr)->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset() - Vector(0, g_SceneMan.GetScene()->GetHeight()); - std::string dataModuleName = g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName(); - bool savingToUserScenesModule = (dataModuleName == c_UserScenesModuleName); + if ((pos.m_X >= 0) && (pos.m_X < pBA->GetBitmapWidth()) && + (pos.m_Y >= 0) && (pos.m_Y < pBA->GetBitmapHeight())) { + skip = false; + finalPos = pos; + } + } - std::string dataModuleFullPath = g_PresetMan.GetFullModulePath(dataModuleName); - std::string assemblySavePath; + if (!skip) { + SceneObject* pNewSO = dynamic_cast((*itr)->Clone()); - if (savingToUserScenesModule) { - assemblySavePath = dataModuleFullPath + "/" + saveAsName + ".ini"; - } else { - if (dataModuleName == "Base.rte") { - assemblySavePath = dataModuleFullPath + "/Scenes/Objects/Bunkers/BunkerAssemblies/" + saveAsName + ".ini"; - } else { - System::MakeDirectory((dataModuleFullPath + "/BunkerAssemblies").c_str()); - assemblySavePath = dataModuleFullPath + "/BunkerAssemblies/" + saveAsName + ".ini"; + // Set position relative to this Bunker Assembly + // pNewSO->SetPos(pNewSO->GetPos() - pBA->GetPos() - pBA->GetBitmapOffset()); + pNewSO->SetPos(finalPos); + pBA->AddPlacedObject(pNewSO); + } } + + return pBA; } - if (g_PresetMan.AddEntityPreset(editedAssembly.get(), m_ModuleSpaceID, forceOverwrite, assemblySavePath)) { - if (Writer assemblyWriter(assemblySavePath, false); !assemblyWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + assemblySavePath + "\n\nTHE EDITED BUNKER ASSEMBLY PRESET WAS NOT SAVED!!!"); - } else { - // TODO: Check if the ini file already exists, and then ask if overwrite. - assemblyWriter.NewPropertyWithValue("AddBunkerAssembly", editedAssembly.get()); - assemblyWriter.EndWrite(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - m_HasEverBeenSaved = true; + bool AssemblyEditor::SaveAssembly(const std::string& saveAsName, bool forceOverwrite) { + std::unique_ptr editedAssembly(BuildAssembly(saveAsName)); - if (!savingToUserScenesModule) { - // First find/create a BunkerAssemblies.ini file to include the new .ini into. - std::string assembliesFilePath = dataModuleFullPath + ((dataModuleName == "Base.rte") ? "/Scenes/Objects/Bunkers/BunkerAssemblies/BunkerAssemblies.ini" : "/BunkerAssemblies/BunkerAssemblies.ini"); - bool assembliesFileExists = System::PathExistsCaseSensitive(assembliesFilePath); + std::string dataModuleName = g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName(); + bool savingToUserScenesModule = (dataModuleName == c_UserScenesModuleName); - if (Writer assembliesFileWriter(assembliesFilePath, true); !assembliesFileWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + assembliesFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); - } else { - assembliesFileWriter.NewPropertyWithValue("IncludeFile", assemblySavePath); - assembliesFileWriter.EndWrite(); - - // Append to the end of the modules' Index.ini to include the newly created Scenes.ini next startup. - // If it's somehow already included without actually existing, it doesn't matter, the definitions will just bounce the second time. - if (!assembliesFileExists) { - std::string indexFilePath = dataModuleFullPath + "/Index.ini"; - - if (Writer indexWriter(indexFilePath, true); !indexWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + indexFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); - } else { - // Add extra tab since the DataModule has everything indented. - indexWriter.NewProperty("\tIncludeFile"); - indexWriter << assembliesFilePath; - indexWriter.EndWrite(); + std::string dataModuleFullPath = g_PresetMan.GetFullModulePath(dataModuleName); + std::string assemblySavePath; + + if (savingToUserScenesModule) { + assemblySavePath = dataModuleFullPath + "/" + saveAsName + ".ini"; + } else { + if (dataModuleName == "Base.rte") { + assemblySavePath = dataModuleFullPath + "/Scenes/Objects/Bunkers/BunkerAssemblies/" + saveAsName + ".ini"; + } else { + System::MakeDirectory((dataModuleFullPath + "/BunkerAssemblies").c_str()); + assemblySavePath = dataModuleFullPath + "/BunkerAssemblies/" + saveAsName + ".ini"; + } + } + + if (g_PresetMan.AddEntityPreset(editedAssembly.get(), m_ModuleSpaceID, forceOverwrite, assemblySavePath)) { + if (Writer assemblyWriter(assemblySavePath, false); !assemblyWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + assemblySavePath + "\n\nTHE EDITED BUNKER ASSEMBLY PRESET WAS NOT SAVED!!!"); + } else { + // TODO: Check if the ini file already exists, and then ask if overwrite. + assemblyWriter.NewPropertyWithValue("AddBunkerAssembly", editedAssembly.get()); + assemblyWriter.EndWrite(); + + m_HasEverBeenSaved = true; + + if (!savingToUserScenesModule) { + // First find/create a BunkerAssemblies.ini file to include the new .ini into. + std::string assembliesFilePath = dataModuleFullPath + ((dataModuleName == "Base.rte") ? "/Scenes/Objects/Bunkers/BunkerAssemblies/BunkerAssemblies.ini" : "/BunkerAssemblies/BunkerAssemblies.ini"); + bool assembliesFileExists = System::PathExistsCaseSensitive(assembliesFilePath); + + if (Writer assembliesFileWriter(assembliesFilePath, true); !assembliesFileWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + assembliesFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); + } else { + assembliesFileWriter.NewPropertyWithValue("IncludeFile", assemblySavePath); + assembliesFileWriter.EndWrite(); + + // Append to the end of the modules' Index.ini to include the newly created Scenes.ini next startup. + // If it's somehow already included without actually existing, it doesn't matter, the definitions will just bounce the second time. + if (!assembliesFileExists) { + std::string indexFilePath = dataModuleFullPath + "/Index.ini"; + + if (Writer indexWriter(indexFilePath, true); !indexWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + indexFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); + } else { + // Add extra tab since the DataModule has everything indented. + indexWriter.NewProperty("\tIncludeFile"); + indexWriter << assembliesFilePath; + indexWriter.EndWrite(); + } } } } + return true; } - return true; + } else { + // Got to ask if we can overwrite the existing preset. + m_PreviousMode = EditorMode::SAVEDIALOG; + m_EditorMode = EditorMode::OVERWRITEDIALOG; + m_ModeChange = true; } - } else { - // Got to ask if we can overwrite the existing preset. - m_PreviousMode = EditorMode::SAVEDIALOG; - m_EditorMode = EditorMode::OVERWRITEDIALOG; - m_ModeChange = true; + return false; } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateNewDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateNewDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. + void AssemblyEditor::UpdateNewDialog() { + } -void AssemblyEditor::UpdateNewDialog() -{ + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateLoadDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Load dialog box, populates its lists etc. -} + void AssemblyEditor::UpdateLoadDialog() { + int scenesIndex = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateLoadDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Load dialog box, populates its lists etc. - -void AssemblyEditor::UpdateLoadDialog() -{ - int scenesIndex = 0; - - if (m_pModuleCombo->GetCount() <= 0) - { - for (int module = 0; module < g_PresetMan.GetTotalModuleCount(); ++module) - { - // Cut-off vanilla modules except Base.rte - bool isValid = false; - - // If metascenes are visible then allow to save assemblies to Base.rte - if (g_SettingsMan.ShowMetascenes()) - { - if ((module == 0 || module > 8) && g_PresetMan.GetDataModule(module)->GetFileName() != c_UserConquestSavesModuleName - && g_PresetMan.GetDataModule(module)->GetFileName() != "Missions.rte") - isValid = true; - } else { - if (module > 8 && g_PresetMan.GetDataModule(module)->GetFileName() != c_UserConquestSavesModuleName - && g_PresetMan.GetDataModule(module)->GetFileName() != "Missions.rte" && g_PresetMan.GetDataModule(module)->GetFileName() != c_UserScriptedSavesModuleName) - isValid = true; - } + if (m_pModuleCombo->GetCount() <= 0) { + for (int module = 0; module < g_PresetMan.GetTotalModuleCount(); ++module) { + // Cut-off vanilla modules except Base.rte + bool isValid = false; - // If Saving to Base.rte is enabled, every module is valid for saving - if (g_SettingsMan.AllowSavingToBase()) - isValid = true; - - if (isValid) - { - m_pModuleCombo->AddItem(g_PresetMan.GetDataModule(module)->GetFileName()); + // If metascenes are visible then allow to save assemblies to Base.rte + if (g_SettingsMan.ShowMetascenes()) { + if ((module == 0 || module > 8) && g_PresetMan.GetDataModule(module)->GetFileName() != c_UserConquestSavesModuleName && g_PresetMan.GetDataModule(module)->GetFileName() != "Missions.rte") + isValid = true; + } else { + if (module > 8 && g_PresetMan.GetDataModule(module)->GetFileName() != c_UserConquestSavesModuleName && g_PresetMan.GetDataModule(module)->GetFileName() != "Missions.rte" && g_PresetMan.GetDataModule(module)->GetFileName() != c_UserScriptedSavesModuleName) + isValid = true; + } + // If Saving to Base.rte is enabled, every module is valid for saving if (g_SettingsMan.AllowSavingToBase()) - { - // If editors are in dev-mode then select Base.rte as default module to save stuff - if (g_PresetMan.GetDataModule(module)->GetFileName() == "Base.rte") - scenesIndex = m_pModuleCombo->GetCount() - 1; - } - else - { - if (g_PresetMan.GetDataModule(module)->GetFileName() == c_UserScenesModuleName) - scenesIndex = m_pModuleCombo->GetCount() - 1; - } - } - } + isValid = true; - m_pModuleCombo->SetDropHeight(std::min({ m_pModuleCombo->GetListPanel()->GetStackHeight() + 4, m_pModuleCombo->GetDropHeight(), g_WindowMan.GetResY() / 2 })); - // Select the user scenes module - m_pModuleCombo->SetSelectedIndex(scenesIndex); - } + if (isValid) { + m_pModuleCombo->AddItem(g_PresetMan.GetDataModule(module)->GetFileName()); - // Clear out the control - m_pLoadNameCombo->ClearList(); + if (g_SettingsMan.AllowSavingToBase()) { + // If editors are in dev-mode then select Base.rte as default module to save stuff + if (g_PresetMan.GetDataModule(module)->GetFileName() == "Base.rte") + scenesIndex = m_pModuleCombo->GetCount() - 1; + } else { + if (g_PresetMan.GetDataModule(module)->GetFileName() == c_UserScenesModuleName) + scenesIndex = m_pModuleCombo->GetCount() - 1; + } + } + } - // Get the list of all read in scenes - std::list sceneList; - g_PresetMan.GetAllOfType(sceneList, "Scene"); + m_pModuleCombo->SetDropHeight(std::min({m_pModuleCombo->GetListPanel()->GetStackHeight() + 4, m_pModuleCombo->GetDropHeight(), g_WindowMan.GetResY() / 2})); + // Select the user scenes module + m_pModuleCombo->SetSelectedIndex(scenesIndex); + } - // Go through the list and add their names to the combo box - for (std::list::iterator itr = sceneList.begin(); itr != sceneList.end(); ++itr) - { - Scene * pScene = dynamic_cast(*itr); - if (pScene) - // Don't add the special "Editor Scene" or metascenes, users shouldn't be messing with them - if (pScene->GetPresetName() != "Editor Scene" && !pScene->IsMetagameInternal() && !pScene->IsSavedGameInternal() && (pScene->GetMetasceneParent() == "" || g_SettingsMan.ShowMetascenes())) - m_pLoadNameCombo->AddItem(pScene->GetPresetName()); - } + // Clear out the control + m_pLoadNameCombo->ClearList(); - // Select the first one - m_pLoadNameCombo->SetSelectedIndex(0); -} + // Get the list of all read in scenes + std::list sceneList; + g_PresetMan.GetAllOfType(sceneList, "Scene"); + // Go through the list and add their names to the combo box + for (std::list::iterator itr = sceneList.begin(); itr != sceneList.end(); ++itr) { + Scene* pScene = dynamic_cast(*itr); + if (pScene) + // Don't add the special "Editor Scene" or metascenes, users shouldn't be messing with them + if (pScene->GetPresetName() != "Editor Scene" && !pScene->IsMetagameInternal() && !pScene->IsSavedGameInternal() && (pScene->GetMetasceneParent() == "" || g_SettingsMan.ShowMetascenes())) + m_pLoadNameCombo->AddItem(pScene->GetPresetName()); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateSaveDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save dialog box, populates its lists etc. + // Select the first one + m_pLoadNameCombo->SetSelectedIndex(0); + } -void AssemblyEditor::UpdateSaveDialog() -{ - std::string defaultName = ""; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateSaveDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save dialog box, populates its lists etc. - BunkerAssemblyScheme *pScheme = m_pEditorGUI->GetCurrentAssemblyScheme(); - if (pScheme) - defaultName = pScheme->GetPresetName() + " - "; + void AssemblyEditor::UpdateSaveDialog() { + std::string defaultName = ""; - m_pSaveNameBox->SetText(m_pEditorGUI->GetCurrentAssemblyName() == "" ? defaultName : m_pEditorGUI->GetCurrentAssemblyName()); - m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName()); -} + BunkerAssemblyScheme* pScheme = m_pEditorGUI->GetCurrentAssemblyScheme(); + if (pScheme) + defaultName = pScheme->GetPresetName() + " - "; + m_pSaveNameBox->SetText(m_pEditorGUI->GetCurrentAssemblyName() == "" ? defaultName : m_pEditorGUI->GetCurrentAssemblyName()); + m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName()); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChangesDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save Changes dialog box, populates its lists etc. - -void AssemblyEditor::UpdateChangesDialog() -{ - if (m_HasEverBeenSaved) - { - dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); - m_pChangesNameLabel->SetText(m_pEditorGUI->GetCurrentAssemblyName()); - } - else - { - dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Save your new Assembly first?"); - m_pChangesNameLabel->SetText(m_pEditorGUI->GetCurrentAssemblyName()); - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateChangesDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save Changes dialog box, populates its lists etc. + void AssemblyEditor::UpdateChangesDialog() { + if (m_HasEverBeenSaved) { + dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); + m_pChangesNameLabel->SetText(m_pEditorGUI->GetCurrentAssemblyName()); + } else { + dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Save your new Assembly first?"); + m_pChangesNameLabel->SetText(m_pEditorGUI->GetCurrentAssemblyName()); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateOverwriteDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Overwrite dialog box, populates its lists etc. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateOverwriteDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Overwrite dialog box, populates its lists etc. -void AssemblyEditor::UpdateOverwriteDialog() -{ - m_pOverwriteNameLabel->SetText(m_pEditorGUI->GetCurrentAssemblyName()); -} + void AssemblyEditor::UpdateOverwriteDialog() { + m_pOverwriteNameLabel->SetText(m_pEditorGUI->GetCurrentAssemblyName()); + } } // namespace RTE \ No newline at end of file diff --git a/Source/Activities/AssemblyEditor.h b/Source/Activities/AssemblyEditor.h index de2d6bb7c9..5630e9c5b0 100644 --- a/Source/Activities/AssemblyEditor.h +++ b/Source/Activities/AssemblyEditor.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,294 +17,267 @@ #include "EditorActivity.h" #include "BunkerAssembly.h" -namespace RTE -{ - -class AssemblyEditorGUI; -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUITab; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUILabel; -class GUIComboBox; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: AssemblyEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activity for editing scenes. -// Parent(s): EditorActivity. -// Class history: 8/30/2007 AssemblyEditor created, inheriting directly from Activity. -// 9/17/2007 Spliced out and made to derive from EditorActivty - -class AssemblyEditor : public EditorActivity { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(AssemblyEditor); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: AssemblyEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a AssemblyEditor object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - AssemblyEditor() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~AssemblyEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a AssemblyEditor object before deletion -// from system memory. -// Arguments: None. - - ~AssemblyEditor() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AssemblyEditor object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a AssemblyEditor to be identical to another, by deep copy. -// Arguments: A reference to the AssemblyEditor to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const AssemblyEditor &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire AssemblyEditor, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); EditorActivity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AssemblyEditor object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildAssembly -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and builds assembly which fits currently selected scheme and returns -// it's pointer. Owhership IS transfered. -// Arguments: New assembly name. -// Return value: Built BunkerAssembly - - BunkerAssembly *BuildAssembly(std::string saveAsName); - - - /// - /// Saves the current BunkerAssembly to an appropriate ini file, and asks user if they want to overwrite first if a BunkerAssembly of this name exists. - /// - /// The name of the new BunkerAssembly to be saved. - /// Whether to force any existing BunkerAssembly of that name to be overwritten if it already exists. - /// Whether actually managed to save. Will return false both if a BunkerAssembly of this name already exists, or if other error. - bool SaveAssembly(const std::string &saveAsName, bool forceOverwrite = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateNewDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateNewDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateLoadDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Load dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateLoadDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateSaveDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateSaveDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateChangesDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save Changes dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateChangesDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateOverwriteDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Overwrite dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateOverwriteDialog() override; - - - // Member variables - static Entity::ClassInfo m_sClass; - - // The editor GUI - AssemblyEditorGUI *m_pEditorGUI; - - GUIComboBox *m_pModuleCombo; - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class AssemblyEditorGUI; + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUITab; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUILabel; + class GUIComboBox; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: AssemblyEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activity for editing scenes. + // Parent(s): EditorActivity. + // Class history: 8/30/2007 AssemblyEditor created, inheriting directly from Activity. + // 9/17/2007 Spliced out and made to derive from EditorActivty + + class AssemblyEditor : public EditorActivity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(AssemblyEditor); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: AssemblyEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a AssemblyEditor object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + AssemblyEditor() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~AssemblyEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a AssemblyEditor object before deletion + // from system memory. + // Arguments: None. + + ~AssemblyEditor() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the AssemblyEditor object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a AssemblyEditor to be identical to another, by deep copy. + // Arguments: A reference to the AssemblyEditor to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const AssemblyEditor& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire AssemblyEditor, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + EditorActivity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the AssemblyEditor object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildAssembly + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and builds assembly which fits currently selected scheme and returns + // it's pointer. Owhership IS transfered. + // Arguments: New assembly name. + // Return value: Built BunkerAssembly + + BunkerAssembly* BuildAssembly(std::string saveAsName); + + /// + /// Saves the current BunkerAssembly to an appropriate ini file, and asks user if they want to overwrite first if a BunkerAssembly of this name exists. + /// + /// The name of the new BunkerAssembly to be saved. + /// Whether to force any existing BunkerAssembly of that name to be overwritten if it already exists. + /// Whether actually managed to save. Will return false both if a BunkerAssembly of this name already exists, or if other error. + bool SaveAssembly(const std::string& saveAsName, bool forceOverwrite = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateNewDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateNewDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateLoadDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Load dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateLoadDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateSaveDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateSaveDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateChangesDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save Changes dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateChangesDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateOverwriteDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Overwrite dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateOverwriteDialog() override; + + // Member variables + static Entity::ClassInfo m_sClass; + + // The editor GUI + AssemblyEditorGUI* m_pEditorGUI; + + GUIComboBox* m_pModuleCombo; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Activities/BaseEditor.cpp b/Source/Activities/BaseEditor.cpp index 1fb53e4010..c4dd83c902 100644 --- a/Source/Activities/BaseEditor.cpp +++ b/Source/Activities/BaseEditor.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,11 +19,11 @@ #include "UInputMan.h" #include "SceneMan.h" #include "MetaMan.h" -//#include "AHuman.h" -//#include "MOPixel.h" +// #include "AHuman.h" +// #include "MOPixel.h" #include "SLTerrain.h" #include "Controller.h" -//#include "AtomGroup.h" +// #include "AtomGroup.h" #include "Actor.h" #include "AHuman.h" #include "ACRocket.h" @@ -36,361 +35,330 @@ namespace RTE { -ConcreteClassInfo(BaseEditor, Activity, 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this BaseEditor, effectively -// resetting the members of this abstraction level only. - -void BaseEditor::Clear() -{ - m_pEditorGUI = 0; - m_NeedSave = false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BaseEditor object ready for use. - -int BaseEditor::Create() -{ - if (Activity::Create() < 0) - return -1; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a BaseEditor to be identical to another, by deep copy. - -int BaseEditor::Create(const BaseEditor &reference) -{ - if (Activity::Create(reference) < 0) - return -1; - - if (m_Description.empty()) - m_Description = "Build or Edit a base on this Scene."; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int BaseEditor::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Activity::ReadProperty(propName, reader)); -/* - MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); - MatchProperty("Difficulty", { reader >> m_Difficulty; }); - MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); -*/ - EndPropertyList; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this BaseEditor with a Writer for -// later recreation with Create(Reader &reader); - -int BaseEditor::Save(Writer &writer) const { - Activity::Save(writer); - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the BaseEditor object. - -void BaseEditor::Destroy(bool notInherited) -{ - delete m_pEditorGUI; - - if (!notInherited) - Activity::Destroy(); - Clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. - -int BaseEditor::Start() -{ - // Set the split screen config before the Scene (and it SceneLayers, specifially) are loaded - g_FrameMan.ResetSplitScreens(false, false); -// Too diff -// int error = Activity::Start(); - int error = 0; - - m_ActivityState = ActivityState::Running; - m_Paused = false; - - // Reset the mousemoving so that it won't trap the mouse if the window isn't in focus (common after loading) - g_UInputMan.DisableMouseMoving(true); - g_UInputMan.DisableMouseMoving(false); - - // Enable keys again - g_UInputMan.DisableKeys(false); - - // Load the scene now - error = g_SceneMan.LoadScene(); - if (error < 0) - return error; - - // Open all doors so we can pathfind past them for brain placement - g_MovableMan.OpenAllDoors(true, Teams::NoTeam); - - /////////////////////////////////////// - // Set up player - ONLY ONE ever in a base building activity - - // Figure which player is editing this base.. the first active one we find - int editingPlayer = Players::PlayerOne; - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - if (m_IsActive[player]) - editingPlayer = player; -// TODO: support multiple coop players editing the same base?? - A: NO, silly - -// for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) -// { -// if (!m_IsActive[player]) -// continue; - m_ViewState[editingPlayer] = ViewState::Normal; - g_FrameMan.ClearScreenText(ScreenOfPlayer(editingPlayer)); - // Set the team associations with the first screen so that the correct unseen are shows up - g_CameraMan.SetScreenTeam(m_Team[editingPlayer], ScreenOfPlayer(editingPlayer)); - g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(editingPlayer)); - - m_PlayerController[editingPlayer].Reset(); - m_PlayerController[editingPlayer].Create(Controller::CIM_PLAYER, editingPlayer); - m_PlayerController[editingPlayer].SetTeam(m_Team[editingPlayer]); - - m_MessageTimer[editingPlayer].Reset(); -// } - - // Kill off any actors not of this player's team.. they're not supposed to be here - g_MovableMan.KillAllEnemyActors(GetTeamOfPlayer(editingPlayer)); - - ////////////////////////////////////////////// - // Allocate and (re)create the Editor GUI - - if (m_pEditorGUI) - m_pEditorGUI->Destroy(); - else - m_pEditorGUI = new SceneEditorGUI; - m_pEditorGUI->Create(&(m_PlayerController[editingPlayer]), SceneEditorGUI::BLUEPRINTEDIT); - - // See if we are playing a metagame and which metaplayer this would be editing in this activity - if (g_MetaMan.GameInProgress()) - { - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(editingPlayer); - if (pMetaPlayer) - { - // Set the appropriate modifiers to the prices etc of this editor - m_pEditorGUI->SetNativeTechModule(pMetaPlayer->GetNativeTechModule()); - m_pEditorGUI->SetForeignCostMultiplier(pMetaPlayer->GetForeignCostMultiplier()); - } - } - - // Set the view to scroll to the brain of the editing player, if there is any - if (g_SceneMan.GetScene()->GetResidentBrain(editingPlayer)) - m_pEditorGUI->SetCursorPos(g_SceneMan.GetScene()->GetResidentBrain(editingPlayer)->GetPos()); - - // Test if the resident brain is still in a valid spot, after potentially building it into a tomb since last round - m_pEditorGUI->TestBrainResidence(); - - //////////////////////////////// - // Set up teams - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - m_FundsChanged[team] = false; - m_TeamDeaths[team] = 0; - } - - // Move any brains resident in the Scene to the MovableMan -// Nope - these are manipulated by the SceneEditorGUI directly where they are in the resident lists -// g_SceneMan.GetScene()->PlaceResidentBrains(*this); - - // The get a list of all the placed objects in the Scene and set them to not kick around - const std::list *pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::BLUEPRINT); - for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr) - { - Actor *pActor = dynamic_cast(*itr); - if (pActor) - { - pActor->SetStatus(Actor::INACTIVE); - pActor->GetController()->SetDisabled(true); - } - } - - // Update all blueprints so they look right after load - g_SceneMan.GetScene()->UpdatePlacedObjects(Scene::BLUEPRINT); - - return error; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. - -void BaseEditor::SetPaused(bool pause) -{ - // Override the pause - m_Paused = false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. - -void BaseEditor::End() -{ - Activity::End(); - - m_ActivityState = ActivityState::Over; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this BaseEditor. Supposed to be done every frame -// before drawing. - -void BaseEditor::Update() -{ - Activity::Update(); - - if (!g_SceneMan.GetScene()) - return; - - // Update the loaded objects of the loaded scene so they look right - g_SceneMan.GetScene()->UpdatePlacedObjects(Scene::BLUEPRINT); - - ///////////////////////////////////////////////////// - // Update the editor interface - - m_pEditorGUI->Update(); - - // Any edits made, dirtying the scene? - m_NeedSave = m_pEditorGUI->EditMade() || m_NeedSave; - - // Get any mode change commands that the user gave the Editor GUI - // Done with editing for now; save and return to campaign screen - if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorDone) - { - m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); - - if (m_NeedSave) - SaveScene(g_SceneMan.GetScene()->GetPresetName()); - - // Quit to metagame view - g_ActivityMan.PauseActivity(); - } -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. - -void BaseEditor::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) -{ - m_pEditorGUI->Draw(pTargetBitmap, targetPos); - - Activity::DrawGUI(pTargetBitmap, targetPos, which); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this BaseEditor's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. - -void BaseEditor::Draw(BITMAP* pTargetBitmap, const Vector &targetPos) -{ - Activity::Draw(pTargetBitmap, targetPos); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the current scene to an appropriate ini file, and asks user if -// they want to overwrite first if scene of this name exists. - -bool BaseEditor::SaveScene(std::string saveAsName, bool forceOverwrite) -{ -/* - // Set the name of the current scene in effect - g_SceneMan.GetScene()->SetPresetName(saveAsName); - // Try to save to the data module - string sceneFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + saveAsName + ".ini"); - if (g_PresetMan.AddEntityPreset(g_SceneMan.GetScene(), m_ModuleSpaceID, forceOverwrite, sceneFilePath)) - { - // Does ini already exist? If yes, then no need to add it to a scenes.ini etc - bool sceneFileExisted = System::PathExistsCaseSensitive(sceneFilePath.c_str()); - // Create the writer - Writer sceneWriter(sceneFilePath.c_str(), false); - sceneWriter.NewProperty("AddScene"); -// TODO: Check if the ini file already exists, and then ask if overwrite - // Write the scene out to the new ini - sceneWriter << g_SceneMan.GetScene(); - - if (!sceneFileExisted) - { - // First find/create a .rte/Scenes.ini file to include the new .ini into - string scenesFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes.ini"); - bool scenesFileExisted = System::PathExistsCaseSensitive(scenesFilePath.c_str()); - Writer scenesWriter(scenesFilePath.c_str(), true); - scenesWriter.NewProperty("\nIncludeFile"); - scenesWriter << sceneFilePath; - - // Also add a line to the end of the modules' Index.ini to include the newly created Scenes.ini next startup - // If it's already included, it doens't matter, the definitions will just bounce the second time - if (!scenesFileExisted) - { - string indexFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Index.ini"); - Writer indexWriter(indexFilePath.c_str(), true); - // Add extra tab since the DataModule has everything indented - indexWriter.NewProperty("\tIncludeFile"); - indexWriter << scenesFilePath; - } - } - return m_HasEverBeenSaved = true; - } -*/ - return false; -} + ConcreteClassInfo(BaseEditor, Activity, 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this BaseEditor, effectively + // resetting the members of this abstraction level only. + + void BaseEditor::Clear() { + m_pEditorGUI = 0; + m_NeedSave = false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the BaseEditor object ready for use. + + int BaseEditor::Create() { + if (Activity::Create() < 0) + return -1; + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a BaseEditor to be identical to another, by deep copy. + + int BaseEditor::Create(const BaseEditor& reference) { + if (Activity::Create(reference) < 0) + return -1; + + if (m_Description.empty()) + m_Description = "Build or Edit a base on this Scene."; + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int BaseEditor::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Activity::ReadProperty(propName, reader)); + /* + MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); + MatchProperty("Difficulty", { reader >> m_Difficulty; }); + MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); + */ + EndPropertyList; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this BaseEditor with a Writer for + // later recreation with Create(Reader &reader); + + int BaseEditor::Save(Writer& writer) const { + Activity::Save(writer); + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the BaseEditor object. + + void BaseEditor::Destroy(bool notInherited) { + delete m_pEditorGUI; + + if (!notInherited) + Activity::Destroy(); + Clear(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. + + int BaseEditor::Start() { + // Set the split screen config before the Scene (and it SceneLayers, specifially) are loaded + g_FrameMan.ResetSplitScreens(false, false); + // Too diff + // int error = Activity::Start(); + int error = 0; + + m_ActivityState = ActivityState::Running; + m_Paused = false; + + // Reset the mousemoving so that it won't trap the mouse if the window isn't in focus (common after loading) + g_UInputMan.DisableMouseMoving(true); + g_UInputMan.DisableMouseMoving(false); + + // Enable keys again + g_UInputMan.DisableKeys(false); + + // Load the scene now + error = g_SceneMan.LoadScene(); + if (error < 0) + return error; + + // Open all doors so we can pathfind past them for brain placement + g_MovableMan.OpenAllDoors(true, Teams::NoTeam); + + /////////////////////////////////////// + // Set up player - ONLY ONE ever in a base building activity + + // Figure which player is editing this base.. the first active one we find + int editingPlayer = Players::PlayerOne; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) + if (m_IsActive[player]) + editingPlayer = player; + // TODO: support multiple coop players editing the same base?? - A: NO, silly + + // for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) + // { + // if (!m_IsActive[player]) + // continue; + m_ViewState[editingPlayer] = ViewState::Normal; + g_FrameMan.ClearScreenText(ScreenOfPlayer(editingPlayer)); + // Set the team associations with the first screen so that the correct unseen are shows up + g_CameraMan.SetScreenTeam(m_Team[editingPlayer], ScreenOfPlayer(editingPlayer)); + g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(editingPlayer)); + + m_PlayerController[editingPlayer].Reset(); + m_PlayerController[editingPlayer].Create(Controller::CIM_PLAYER, editingPlayer); + m_PlayerController[editingPlayer].SetTeam(m_Team[editingPlayer]); + + m_MessageTimer[editingPlayer].Reset(); + // } + + // Kill off any actors not of this player's team.. they're not supposed to be here + g_MovableMan.KillAllEnemyActors(GetTeamOfPlayer(editingPlayer)); + + ////////////////////////////////////////////// + // Allocate and (re)create the Editor GUI + + if (m_pEditorGUI) + m_pEditorGUI->Destroy(); + else + m_pEditorGUI = new SceneEditorGUI; + m_pEditorGUI->Create(&(m_PlayerController[editingPlayer]), SceneEditorGUI::BLUEPRINTEDIT); + + // See if we are playing a metagame and which metaplayer this would be editing in this activity + if (g_MetaMan.GameInProgress()) { + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(editingPlayer); + if (pMetaPlayer) { + // Set the appropriate modifiers to the prices etc of this editor + m_pEditorGUI->SetNativeTechModule(pMetaPlayer->GetNativeTechModule()); + m_pEditorGUI->SetForeignCostMultiplier(pMetaPlayer->GetForeignCostMultiplier()); + } + } + + // Set the view to scroll to the brain of the editing player, if there is any + if (g_SceneMan.GetScene()->GetResidentBrain(editingPlayer)) + m_pEditorGUI->SetCursorPos(g_SceneMan.GetScene()->GetResidentBrain(editingPlayer)->GetPos()); + + // Test if the resident brain is still in a valid spot, after potentially building it into a tomb since last round + m_pEditorGUI->TestBrainResidence(); + + //////////////////////////////// + // Set up teams + + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; + m_FundsChanged[team] = false; + m_TeamDeaths[team] = 0; + } + + // Move any brains resident in the Scene to the MovableMan + // Nope - these are manipulated by the SceneEditorGUI directly where they are in the resident lists + // g_SceneMan.GetScene()->PlaceResidentBrains(*this); + + // The get a list of all the placed objects in the Scene and set them to not kick around + const std::list* pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::BLUEPRINT); + for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr) { + Actor* pActor = dynamic_cast(*itr); + if (pActor) { + pActor->SetStatus(Actor::INACTIVE); + pActor->GetController()->SetDisabled(true); + } + } + + // Update all blueprints so they look right after load + g_SceneMan.GetScene()->UpdatePlacedObjects(Scene::BLUEPRINT); + + return error; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + + void BaseEditor::SetPaused(bool pause) { + // Override the pause + m_Paused = false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + + void BaseEditor::End() { + Activity::End(); + + m_ActivityState = ActivityState::Over; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this BaseEditor. Supposed to be done every frame + // before drawing. + + void BaseEditor::Update() { + Activity::Update(); + + if (!g_SceneMan.GetScene()) + return; + + // Update the loaded objects of the loaded scene so they look right + g_SceneMan.GetScene()->UpdatePlacedObjects(Scene::BLUEPRINT); + + ///////////////////////////////////////////////////// + // Update the editor interface + + m_pEditorGUI->Update(); + + // Any edits made, dirtying the scene? + m_NeedSave = m_pEditorGUI->EditMade() || m_NeedSave; + + // Get any mode change commands that the user gave the Editor GUI + // Done with editing for now; save and return to campaign screen + if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorDone) { + m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); + + if (m_NeedSave) + SaveScene(g_SceneMan.GetScene()->GetPresetName()); + + // Quit to metagame view + g_ActivityMan.PauseActivity(); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + + void BaseEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + m_pEditorGUI->Draw(pTargetBitmap, targetPos); + + Activity::DrawGUI(pTargetBitmap, targetPos, which); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this BaseEditor's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + + void BaseEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + Activity::Draw(pTargetBitmap, targetPos); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SaveScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the current scene to an appropriate ini file, and asks user if + // they want to overwrite first if scene of this name exists. + + bool BaseEditor::SaveScene(std::string saveAsName, bool forceOverwrite) { + /* + // Set the name of the current scene in effect + g_SceneMan.GetScene()->SetPresetName(saveAsName); + // Try to save to the data module + string sceneFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + saveAsName + ".ini"); + if (g_PresetMan.AddEntityPreset(g_SceneMan.GetScene(), m_ModuleSpaceID, forceOverwrite, sceneFilePath)) + { + // Does ini already exist? If yes, then no need to add it to a scenes.ini etc + bool sceneFileExisted = System::PathExistsCaseSensitive(sceneFilePath.c_str()); + // Create the writer + Writer sceneWriter(sceneFilePath.c_str(), false); + sceneWriter.NewProperty("AddScene"); + // TODO: Check if the ini file already exists, and then ask if overwrite + // Write the scene out to the new ini + sceneWriter << g_SceneMan.GetScene(); + + if (!sceneFileExisted) + { + // First find/create a .rte/Scenes.ini file to include the new .ini into + string scenesFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes.ini"); + bool scenesFileExisted = System::PathExistsCaseSensitive(scenesFilePath.c_str()); + Writer scenesWriter(scenesFilePath.c_str(), true); + scenesWriter.NewProperty("\nIncludeFile"); + scenesWriter << sceneFilePath; + + // Also add a line to the end of the modules' Index.ini to include the newly created Scenes.ini next startup + // If it's already included, it doens't matter, the definitions will just bounce the second time + if (!scenesFileExisted) + { + string indexFilePath(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Index.ini"); + Writer indexWriter(indexFilePath.c_str(), true); + // Add extra tab since the DataModule has everything indented + indexWriter.NewProperty("\tIncludeFile"); + indexWriter << scenesFilePath; + } + } + return m_HasEverBeenSaved = true; + } + */ + return false; + } } // namespace RTE \ No newline at end of file diff --git a/Source/Activities/BaseEditor.h b/Source/Activities/BaseEditor.h index 7417cc7d79..d481788caa 100644 --- a/Source/Activities/BaseEditor.h +++ b/Source/Activities/BaseEditor.h @@ -10,227 +10,206 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" -namespace RTE -{ - -class SceneEditorGUI; -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUITab; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUILabel; -class GUIComboBox; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: BaseEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activity for editing designs only for the bases of each scene in a -// Campaign metagame. Doesn't need all the document-model dlg boxes. -// Parent(s): Activity. -// Class history: 04/30/2010 Created BaseEditor for the campaign base blueprint editing. - -class BaseEditor : public Activity { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(BaseEditor); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: BaseEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a BaseEditor object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - BaseEditor() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~BaseEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a BaseEditor object before deletion -// from system memory. -// Arguments: None. - - ~BaseEditor() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BaseEditor object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a BaseEditor to be identical to another, by deep copy. -// Arguments: A reference to the BaseEditor to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const BaseEditor &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire BaseEditor, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Activity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the BaseEditor object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the current scene to an appropriate ini file, and asks user if -// they want to overwrite first if scene of this name exists. -// Arguments: The name of the new scene to be saved. -// Whetehr to force any existing Scene of that name to be overwritten if -// it already exists. -// Return value: Whether actually managed to save. Will return false both if a scene -// of this name already exists, or if other error. - - bool SaveScene(std::string saveAsName, bool forceOverwrite = false); - - - // Member variables - static Entity::ClassInfo m_sClass; - - // The editor GUI - SceneEditorGUI *m_pEditorGUI; - - // Dirty Scene? - bool m_NeedSave; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class SceneEditorGUI; + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUITab; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUILabel; + class GUIComboBox; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: BaseEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activity for editing designs only for the bases of each scene in a + // Campaign metagame. Doesn't need all the document-model dlg boxes. + // Parent(s): Activity. + // Class history: 04/30/2010 Created BaseEditor for the campaign base blueprint editing. + + class BaseEditor : public Activity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(BaseEditor); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: BaseEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a BaseEditor object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + BaseEditor() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~BaseEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a BaseEditor object before deletion + // from system memory. + // Arguments: None. + + ~BaseEditor() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the BaseEditor object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a BaseEditor to be identical to another, by deep copy. + // Arguments: A reference to the BaseEditor to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const BaseEditor& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire BaseEditor, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Activity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the BaseEditor object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SaveScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the current scene to an appropriate ini file, and asks user if + // they want to overwrite first if scene of this name exists. + // Arguments: The name of the new scene to be saved. + // Whetehr to force any existing Scene of that name to be overwritten if + // it already exists. + // Return value: Whether actually managed to save. Will return false both if a scene + // of this name already exists, or if other error. + + bool SaveScene(std::string saveAsName, bool forceOverwrite = false); + + // Member variables + static Entity::ClassInfo m_sClass; + + // The editor GUI + SceneEditorGUI* m_pEditorGUI; + + // Dirty Scene? + bool m_NeedSave; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Activities/EditorActivity.cpp b/Source/Activities/EditorActivity.cpp index 4a152e6b4a..cc78f6c319 100644 --- a/Source/Activities/EditorActivity.cpp +++ b/Source/Activities/EditorActivity.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -33,597 +32,550 @@ #include "GUIControlManager.h" #include "GUICollectionBox.h" - namespace RTE { -AbstractClassInfo(EditorActivity, Activity); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this EditorActivity, effectively -// resetting the members of this abstraction level only. - -void EditorActivity::Clear() -{ - // Most editors are single player affairs - m_MaxPlayerSupport = 1; - m_MinTeamsRequired = 1; - m_EditorMode = NEWDIALOG; - m_PreviousMode = EDITINGOBJECT; - m_ModeChange = false; - m_ModuleSpaceID = 0; - m_NeedSave = false; - m_HasEverBeenSaved = false; - m_PieMenu = nullptr; - m_pGUIScreen = 0; - m_pGUIInput = 0; - m_pGUIController = 0; - m_pNewDialogBox = 0; - m_pNewModuleCombo = 0; - m_pNewButton = 0; - m_pNewCancel = 0; - m_pLoadDialogBox = 0; - m_pLoadNameCombo = 0; - m_pLoadToNewButton = 0; - m_pLoadButton = 0; - m_pLoadCancel = 0; - m_pSaveDialogBox = 0; - m_pSaveNameBox = 0; - m_pSaveModuleLabel = 0; - m_pSaveButton = 0; - m_pSaveCancel = 0; - m_pChangesDialogBox = 0; - m_pChangesNameLabel = 0; - m_pChangesYesButton = 0; - m_pChangesNoButton = 0; - m_pOverwriteDialogBox = 0; - m_pOverwriteNameLabel = 0; - m_pOverwriteYesButton = 0; - m_pOverwriteNoButton = 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the EditorActivity object ready for use. - -int EditorActivity::Create() -{ - if (Activity::Create() < 0) - return -1; - - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a EditorActivity to be identical to another, by deep copy. - -int EditorActivity::Create(const EditorActivity &reference) -{ - if (Activity::Create(reference) < 0) - return -1; - -// m_Description = "No description defined for this Activity!"; - - m_EditorMode = reference.m_EditorMode; - m_ModuleSpaceID = reference.m_ModuleSpaceID; - m_NeedSave = reference.m_NeedSave; - if (reference.m_PieMenu) { m_PieMenu = std::unique_ptr(dynamic_cast(reference.m_PieMenu->Clone())); } - - return 0; -} - + AbstractClassInfo(EditorActivity, Activity); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this EditorActivity, effectively + // resetting the members of this abstraction level only. + + void EditorActivity::Clear() { + // Most editors are single player affairs + m_MaxPlayerSupport = 1; + m_MinTeamsRequired = 1; + m_EditorMode = NEWDIALOG; + m_PreviousMode = EDITINGOBJECT; + m_ModeChange = false; + m_ModuleSpaceID = 0; + m_NeedSave = false; + m_HasEverBeenSaved = false; + m_PieMenu = nullptr; + m_pGUIScreen = 0; + m_pGUIInput = 0; + m_pGUIController = 0; + m_pNewDialogBox = 0; + m_pNewModuleCombo = 0; + m_pNewButton = 0; + m_pNewCancel = 0; + m_pLoadDialogBox = 0; + m_pLoadNameCombo = 0; + m_pLoadToNewButton = 0; + m_pLoadButton = 0; + m_pLoadCancel = 0; + m_pSaveDialogBox = 0; + m_pSaveNameBox = 0; + m_pSaveModuleLabel = 0; + m_pSaveButton = 0; + m_pSaveCancel = 0; + m_pChangesDialogBox = 0; + m_pChangesNameLabel = 0; + m_pChangesYesButton = 0; + m_pChangesNoButton = 0; + m_pOverwriteDialogBox = 0; + m_pOverwriteNameLabel = 0; + m_pOverwriteYesButton = 0; + m_pOverwriteNoButton = 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int EditorActivity::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(Activity::ReadProperty(propName, reader)); -/* - MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); - MatchProperty("Difficulty", { reader >> m_Difficulty; }); - MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); -*/ - EndPropertyList; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the EditorActivity object ready for use. + int EditorActivity::Create() { + if (Activity::Create() < 0) + return -1; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this EditorActivity with a Writer for -// later recreation with Create(Reader &reader); - -int EditorActivity::Save(Writer &writer) const { - Activity::Save(writer); - return 0; -} + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a EditorActivity to be identical to another, by deep copy. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the EditorActivity object. + int EditorActivity::Create(const EditorActivity& reference) { + if (Activity::Create(reference) < 0) + return -1; -void EditorActivity::Destroy(bool notInherited) -{ - delete m_pGUIController; - delete m_pGUIInput; - delete m_pGUIScreen; + // m_Description = "No description defined for this Activity!"; - if (!notInherited) - Activity::Destroy(); - Clear(); -} + m_EditorMode = reference.m_EditorMode; + m_ModuleSpaceID = reference.m_ModuleSpaceID; + m_NeedSave = reference.m_NeedSave; + if (reference.m_PieMenu) { + m_PieMenu = std::unique_ptr(dynamic_cast(reference.m_PieMenu->Clone())); + } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. - -int EditorActivity::Start() -{ - int error = 0; -// Don't, too different -// Activity::Start(); - - // Load the scene now - error = g_SceneMan.LoadScene(); - if (error < 0) - return error; - - // Clear the post effects - g_PostProcessMan.ClearScenePostEffects(); - - // Clear the screen messages - g_FrameMan.ClearScreenText(); - - // Reset the mousemoving so that it won't trap the mouse if the window isn't in focus (common after loading) - g_UInputMan.DisableMouseMoving(true); - g_UInputMan.DisableMouseMoving(false); - - m_ActivityState = ActivityState::Editing; - m_Paused = true; -// g_TimerMan.PauseSim(true); - m_ModeChange = true; - - // Play editing music - g_AudioMan.ClearMusicQueue(); - g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/ccambient4.ogg"); - - // Force the split screen config to just be one big screen for editing - g_FrameMan.ResetSplitScreens(false, false); - - /////////////////////////// - // GUI manager setup - - if (!m_pGUIScreen) - m_pGUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer8()); - if (!m_pGUIInput) - m_pGUIInput = new GUIInputWrapper(-1, true); - if (!m_pGUIController) - m_pGUIController = new GUIControlManager(); - if (!m_pGUIController->Create(m_pGUIScreen, m_pGUIInput, "Base.rte/GUIs/Skins", "DefaultSkin.ini")) { - RTEAbort("Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/DefaultSkin.ini"); + return 0; } - return error; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int EditorActivity::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(Activity::ReadProperty(propName, reader)); + /* + MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); + MatchProperty("Difficulty", { reader >> m_Difficulty; }); + MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); + */ + EndPropertyList; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this EditorActivity with a Writer for + // later recreation with Create(Reader &reader); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. + int EditorActivity::Save(Writer& writer) const { + Activity::Save(writer); + return 0; + } -void EditorActivity::SetPaused(bool pause) -{ - // Override the pause - m_Paused = false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the EditorActivity object. + void EditorActivity::Destroy(bool notInherited) { + delete m_pGUIController; + delete m_pGUIInput; + delete m_pGUIScreen; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. + if (!notInherited) + Activity::Destroy(); + Clear(); + } -void EditorActivity::End() -{ - Activity::End(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. + + int EditorActivity::Start() { + int error = 0; + // Don't, too different + // Activity::Start(); + + // Load the scene now + error = g_SceneMan.LoadScene(); + if (error < 0) + return error; + + // Clear the post effects + g_PostProcessMan.ClearScenePostEffects(); + + // Clear the screen messages + g_FrameMan.ClearScreenText(); + + // Reset the mousemoving so that it won't trap the mouse if the window isn't in focus (common after loading) + g_UInputMan.DisableMouseMoving(true); + g_UInputMan.DisableMouseMoving(false); + + m_ActivityState = ActivityState::Editing; + m_Paused = true; + // g_TimerMan.PauseSim(true); + m_ModeChange = true; + + // Play editing music + g_AudioMan.ClearMusicQueue(); + g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/ccambient4.ogg"); + + // Force the split screen config to just be one big screen for editing + g_FrameMan.ResetSplitScreens(false, false); + + /////////////////////////// + // GUI manager setup + + if (!m_pGUIScreen) + m_pGUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer8()); + if (!m_pGUIInput) + m_pGUIInput = new GUIInputWrapper(-1, true); + if (!m_pGUIController) + m_pGUIController = new GUIControlManager(); + if (!m_pGUIController->Create(m_pGUIScreen, m_pGUIInput, "Base.rte/GUIs/Skins", "DefaultSkin.ini")) { + RTEAbort("Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/DefaultSkin.ini"); + } + + return error; + } - + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. - m_ActivityState = ActivityState::Over; -} + void EditorActivity::SetPaused(bool pause) { + // Override the pause + m_Paused = false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this EditorActivity. Supposed to be done every frame -// before drawing. - -void EditorActivity::Update() -{ - // Always show teh messages of the editor - m_MessageTimer[0].Reset(); - - Activity::Update(); - - if (!g_SceneMan.GetScene()) - return; - - // Hide all dialog boxes if mode changes, one will reappear below - if (m_ModeChange) - { - if (m_pNewDialogBox) - m_pNewDialogBox->SetVisible(false); - m_pLoadDialogBox->SetVisible(false); - m_pSaveDialogBox->SetVisible(false); - m_pChangesDialogBox->SetVisible(false); - m_pOverwriteDialogBox->SetVisible(false); - } - - if (m_EditorMode == NEWDIALOG) - { - if (m_ModeChange) - { - if (m_NeedSave) - { - m_PreviousMode = NEWDIALOG; - m_EditorMode = CHANGESDIALOG; - m_ModeChange = true; - } - else - { - UpdateNewDialog(); - // Set the mouse cursor free - m_pGUIController->EnableMouse(true); - g_UInputMan.TrapMousePos(false, 0); - // Show/hide relevant GUI elements - m_pNewDialogBox->SetVisible(true); - m_ModeChange = false; - } - } - } - else if (m_EditorMode == LOADDIALOG) - { - if (m_ModeChange) - { - if (m_NeedSave) - { - m_PreviousMode = LOADDIALOG; - m_EditorMode = CHANGESDIALOG; - m_ModeChange = true; - } - else - { - UpdateLoadDialog(); - // Set the mouse cursor free - m_pGUIController->EnableMouse(true); - g_UInputMan.TrapMousePos(false, 0); - // Show/hide relevant GUI elements - m_pLoadDialogBox->SetVisible(true); - m_ModeChange = false; - } - } - } - else if (m_EditorMode == SAVEDIALOG) - { - if (m_ModeChange) - { - UpdateSaveDialog(); - // Temporarily disable the UInput from reading regular keypresses so we can type - g_UInputMan.DisableKeys(true); - // Set the mouse cursor free - m_pGUIController->EnableMouse(true); - g_UInputMan.TrapMousePos(false, 0); - // Show/hide relevant GUI elements - m_pSaveDialogBox->SetVisible(true); - m_ModeChange = false; - } - } - else if (m_EditorMode == CHANGESDIALOG) - { - if (m_ModeChange) - { - UpdateChangesDialog(); - // Set the mouse cursor free - m_pGUIController->EnableMouse(true); - g_UInputMan.TrapMousePos(false, 0); - // Show/hide relevant GUI elements - m_pChangesDialogBox->SetVisible(true); - m_ModeChange = false; - } - } - else if (m_EditorMode == OVERWRITEDIALOG) - { - if (m_ModeChange) - { - UpdateOverwriteDialog(); - // Set the mouse cursor free - m_pGUIController->EnableMouse(true); - g_UInputMan.TrapMousePos(false, 0); - // Show/hide relevant GUI elements - m_pOverwriteDialogBox->SetVisible(true); - m_ModeChange = false; - } - } -/* Let this be handled by derived class - // Quit all dialog boxes - else - { - if (m_ModeChange) - { - // Hide the cursor for this layer of interface - m_pGUIController->EnableMouse(false); - m_ModeChange = false; - } - g_UInputMan.DisableKeys(false); - } -*/ - ////////////////////////////////////////// - // Update the ControlManager - - m_pGUIController->Update(); - -/* Do this in derived classes - //////////////////////////////////////////////////////// - // Handle events for mouse input on the controls - - GUIEvent anEvent; - while(m_pGUIController->GetEvent(&anEvent)) - { - // If we're not supposed to have mouse control, then ignore these messages -// Uh this is not right, editor always has mouse control so far -// if (!m_PlayerController[0].IsMouseControlled()) -// break; - - if (anEvent.GetType() == GUIEvent::Command) - { - ////////////////////////////////////////////////////////// - // NEW button pressed; create a new scene - - if (anEvent.GetControl() == m_pNewButton) - { - // Get the selected Module - GUIListPanel::Item *pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - m_ModuleSpaceID = g_PresetMan.GetModuleID(pItem->m_Name); - - // Allocate Scene - Scene *pNewScene = new Scene(); - // Get the selected Terrain and create the Scene using it - pItem = m_pNewTerrainCombo->GetItem(m_pNewTerrainCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SLTerrain *pNewTerrain = dynamic_cast(g_PresetMan.GetEntityPreset("SLTerrain", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewTerrain, "No SLTerrain of that name defined!"); - pNewScene->Create(pNewTerrain); - } - - // Add specified scene layers - pItem = m_pNewBG1Combo->GetItem(m_pNewBG1Combo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SceneLayer of the name set as BG1 is defined!"); - pNewScene->GetBackLayers().push_back(pNewLayer); - } - pItem = m_pNewBG2Combo->GetItem(m_pNewBG2Combo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SceneLayer of the name set as BG2 is defined!"); - pNewScene->GetBackLayers().push_back(pNewLayer); - } - pItem = m_pNewBG3Combo->GetItem(m_pNewBG3Combo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SceneLayer of the name set as BG3 is defined!"); - pNewScene->GetBackLayers().push_back(pNewLayer); - } - - // Actually load the scene's data and set it up as the current scene - g_SceneMan.LoadScene(pNewScene); - - // Reset the rest of the editor GUI - m_pEditorGUI->Destroy(); - m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, m_ModuleSpaceID); - } - - m_NeedSave = false; - m_HasEverBeenSaved = false; - m_EditorMode = m_PreviousMode = EDITINGOBJECT; - m_ModeChange = true; - } - - ////////////////////////////////////////////////////////// - // LOAD button pressed; load the selected Scene - - if (anEvent.GetControl() == m_pLoadButton) - { - GUIListPanel::Item *pItem = m_pLoadNameCombo->GetItem(m_pLoadNameCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - // Attempt to load the scene, without applying its placed objects - g_SceneMan.LoadScene(pItem->m_Name, false); - // Get the Module ID that the scene exists in, so we can limit the picker to only show objects from that DataModule space - if (g_SceneMan.GetScene()) - { - m_ModuleSpaceID = g_SceneMan.GetScene()->GetModuleID(); - RTEAssert(m_ModuleSpaceID >= 0, "Loaded Scene's DataModule ID is negative? Should always be a specific one.."); - m_pEditorGUI->Destroy(); - m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, m_ModuleSpaceID); -// TODO: Should read in all the already placed objects in the loaded scene and have them appear int he editor instead - } - } - m_NeedSave = false; - m_HasEverBeenSaved = true; - m_EditorMode = m_PreviousMode = EDITINGOBJECT; - m_ModeChange = true; - } - - ////////////////////////////////////////////////////////// - // SAVE button pressed; save the selected Scene - - if (anEvent.GetControl() == m_pSaveButton) - { - if (!m_pSaveNameBox->GetText().empty()) - { - // Save the scene to the name specified in the text box - if (SaveScene(m_pSaveNameBox->GetText())) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the scene - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - // Should really leave dialog box open? - else - { - ; - } - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes YES pressed - - if (anEvent.GetControl() == m_pChangesYesButton) - { - if (m_HasEverBeenSaved) - { - if (SaveScene(g_SceneMan.GetScene()->GetPresetName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the scene - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - } - // Open the save scene dialog to ask user where to save it then - else - { - m_PreviousMode = m_PreviousMode; - m_EditorMode = SAVEDIALOG; - m_ModeChange = true; - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes NO pressed - - if (anEvent.GetControl() == m_pChangesNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - m_NeedSave = false; - } - - /////////////////////////////////////////////////////////////// - // Overwrite Scene YES pressed - - if (anEvent.GetControl() == m_pOverwriteYesButton) - { - // Force overwrite - if (SaveScene(g_SceneMan.GetScene()->GetPresetName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after overwrite dialog is done, may have been on the way to test the scene - m_EditorMode = m_PreviousMode != SAVEDIALOG ? m_PreviousMode : EDITINGOBJECT; - m_ModeChange = true; - } -// TODO: Show overwrite error? - } - - /////////////////////////////////////////////////////////////// - // Overwrite Scene NO pressed - - if (anEvent.GetControl() == m_pOverwriteNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - - /////////////////////////////////////////////////////////////// - // CANCEL button pressed; exit any active dialog box - - if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) - { - m_EditorMode = m_PreviousMode = EDITINGOBJECT; - m_ModeChange = true; - } - } - - // Notifications - else if (anEvent.GetType() == GUIEvent::Notification) - { - /////////////////////////////////////// - // Clicks on the New Scene Module combo - - if (anEvent.GetControl() == m_pNewModuleCombo) - { - // Closed it, IE selected somehting - if(anEvent.GetMsg() == GUIComboBox::Closed) - UpdateNewDialog(); - } - - } - } -*/ -} + void EditorActivity::End() { + Activity::End(); + m_ActivityState = ActivityState::Over; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this EditorActivity. Supposed to be done every frame + // before drawing. + + void EditorActivity::Update() { + // Always show teh messages of the editor + m_MessageTimer[0].Reset(); + + Activity::Update(); + + if (!g_SceneMan.GetScene()) + return; + + // Hide all dialog boxes if mode changes, one will reappear below + if (m_ModeChange) { + if (m_pNewDialogBox) + m_pNewDialogBox->SetVisible(false); + m_pLoadDialogBox->SetVisible(false); + m_pSaveDialogBox->SetVisible(false); + m_pChangesDialogBox->SetVisible(false); + m_pOverwriteDialogBox->SetVisible(false); + } + + if (m_EditorMode == NEWDIALOG) { + if (m_ModeChange) { + if (m_NeedSave) { + m_PreviousMode = NEWDIALOG; + m_EditorMode = CHANGESDIALOG; + m_ModeChange = true; + } else { + UpdateNewDialog(); + // Set the mouse cursor free + m_pGUIController->EnableMouse(true); + g_UInputMan.TrapMousePos(false, 0); + // Show/hide relevant GUI elements + m_pNewDialogBox->SetVisible(true); + m_ModeChange = false; + } + } + } else if (m_EditorMode == LOADDIALOG) { + if (m_ModeChange) { + if (m_NeedSave) { + m_PreviousMode = LOADDIALOG; + m_EditorMode = CHANGESDIALOG; + m_ModeChange = true; + } else { + UpdateLoadDialog(); + // Set the mouse cursor free + m_pGUIController->EnableMouse(true); + g_UInputMan.TrapMousePos(false, 0); + // Show/hide relevant GUI elements + m_pLoadDialogBox->SetVisible(true); + m_ModeChange = false; + } + } + } else if (m_EditorMode == SAVEDIALOG) { + if (m_ModeChange) { + UpdateSaveDialog(); + // Temporarily disable the UInput from reading regular keypresses so we can type + g_UInputMan.DisableKeys(true); + // Set the mouse cursor free + m_pGUIController->EnableMouse(true); + g_UInputMan.TrapMousePos(false, 0); + // Show/hide relevant GUI elements + m_pSaveDialogBox->SetVisible(true); + m_ModeChange = false; + } + } else if (m_EditorMode == CHANGESDIALOG) { + if (m_ModeChange) { + UpdateChangesDialog(); + // Set the mouse cursor free + m_pGUIController->EnableMouse(true); + g_UInputMan.TrapMousePos(false, 0); + // Show/hide relevant GUI elements + m_pChangesDialogBox->SetVisible(true); + m_ModeChange = false; + } + } else if (m_EditorMode == OVERWRITEDIALOG) { + if (m_ModeChange) { + UpdateOverwriteDialog(); + // Set the mouse cursor free + m_pGUIController->EnableMouse(true); + g_UInputMan.TrapMousePos(false, 0); + // Show/hide relevant GUI elements + m_pOverwriteDialogBox->SetVisible(true); + m_ModeChange = false; + } + } + /* Let this be handled by derived class + // Quit all dialog boxes + else + { + if (m_ModeChange) + { + // Hide the cursor for this layer of interface + m_pGUIController->EnableMouse(false); + m_ModeChange = false; + } + g_UInputMan.DisableKeys(false); + } + */ + ////////////////////////////////////////// + // Update the ControlManager + + m_pGUIController->Update(); + + /* Do this in derived classes + //////////////////////////////////////////////////////// + // Handle events for mouse input on the controls + + GUIEvent anEvent; + while(m_pGUIController->GetEvent(&anEvent)) + { + // If we're not supposed to have mouse control, then ignore these messages + // Uh this is not right, editor always has mouse control so far + // if (!m_PlayerController[0].IsMouseControlled()) + // break; + + if (anEvent.GetType() == GUIEvent::Command) + { + ////////////////////////////////////////////////////////// + // NEW button pressed; create a new scene + + if (anEvent.GetControl() == m_pNewButton) + { + // Get the selected Module + GUIListPanel::Item *pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + m_ModuleSpaceID = g_PresetMan.GetModuleID(pItem->m_Name); + + // Allocate Scene + Scene *pNewScene = new Scene(); + // Get the selected Terrain and create the Scene using it + pItem = m_pNewTerrainCombo->GetItem(m_pNewTerrainCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + SLTerrain *pNewTerrain = dynamic_cast(g_PresetMan.GetEntityPreset("SLTerrain", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewTerrain, "No SLTerrain of that name defined!"); + pNewScene->Create(pNewTerrain); + } + + // Add specified scene layers + pItem = m_pNewBG1Combo->GetItem(m_pNewBG1Combo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SceneLayer of the name set as BG1 is defined!"); + pNewScene->GetBackLayers().push_back(pNewLayer); + } + pItem = m_pNewBG2Combo->GetItem(m_pNewBG2Combo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SceneLayer of the name set as BG2 is defined!"); + pNewScene->GetBackLayers().push_back(pNewLayer); + } + pItem = m_pNewBG3Combo->GetItem(m_pNewBG3Combo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SceneLayer of the name set as BG3 is defined!"); + pNewScene->GetBackLayers().push_back(pNewLayer); + } + + // Actually load the scene's data and set it up as the current scene + g_SceneMan.LoadScene(pNewScene); + + // Reset the rest of the editor GUI + m_pEditorGUI->Destroy(); + m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, m_ModuleSpaceID); + } + + m_NeedSave = false; + m_HasEverBeenSaved = false; + m_EditorMode = m_PreviousMode = EDITINGOBJECT; + m_ModeChange = true; + } + + ////////////////////////////////////////////////////////// + // LOAD button pressed; load the selected Scene + + if (anEvent.GetControl() == m_pLoadButton) + { + GUIListPanel::Item *pItem = m_pLoadNameCombo->GetItem(m_pLoadNameCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + // Attempt to load the scene, without applying its placed objects + g_SceneMan.LoadScene(pItem->m_Name, false); + // Get the Module ID that the scene exists in, so we can limit the picker to only show objects from that DataModule space + if (g_SceneMan.GetScene()) + { + m_ModuleSpaceID = g_SceneMan.GetScene()->GetModuleID(); + RTEAssert(m_ModuleSpaceID >= 0, "Loaded Scene's DataModule ID is negative? Should always be a specific one.."); + m_pEditorGUI->Destroy(); + m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, m_ModuleSpaceID); + // TODO: Should read in all the already placed objects in the loaded scene and have them appear int he editor instead + } + } + m_NeedSave = false; + m_HasEverBeenSaved = true; + m_EditorMode = m_PreviousMode = EDITINGOBJECT; + m_ModeChange = true; + } + + ////////////////////////////////////////////////////////// + // SAVE button pressed; save the selected Scene + + if (anEvent.GetControl() == m_pSaveButton) + { + if (!m_pSaveNameBox->GetText().empty()) + { + // Save the scene to the name specified in the text box + if (SaveScene(m_pSaveNameBox->GetText())) + { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the scene + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + // Should really leave dialog box open? + else + { + ; + } + } + } + + /////////////////////////////////////////////////////////////// + // Save Changes YES pressed + + if (anEvent.GetControl() == m_pChangesYesButton) + { + if (m_HasEverBeenSaved) + { + if (SaveScene(g_SceneMan.GetScene()->GetPresetName(), true)) + { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the scene + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + } + // Open the save scene dialog to ask user where to save it then + else + { + m_PreviousMode = m_PreviousMode; + m_EditorMode = SAVEDIALOG; + m_ModeChange = true; + } + } + + /////////////////////////////////////////////////////////////// + // Save Changes NO pressed + + if (anEvent.GetControl() == m_pChangesNoButton) + { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + m_NeedSave = false; + } + + /////////////////////////////////////////////////////////////// + // Overwrite Scene YES pressed + + if (anEvent.GetControl() == m_pOverwriteYesButton) + { + // Force overwrite + if (SaveScene(g_SceneMan.GetScene()->GetPresetName(), true)) + { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after overwrite dialog is done, may have been on the way to test the scene + m_EditorMode = m_PreviousMode != SAVEDIALOG ? m_PreviousMode : EDITINGOBJECT; + m_ModeChange = true; + } + // TODO: Show overwrite error? + } + + /////////////////////////////////////////////////////////////// + // Overwrite Scene NO pressed + + if (anEvent.GetControl() == m_pOverwriteNoButton) + { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + + /////////////////////////////////////////////////////////////// + // CANCEL button pressed; exit any active dialog box + + if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) + { + m_EditorMode = m_PreviousMode = EDITINGOBJECT; + m_ModeChange = true; + } + } + + // Notifications + else if (anEvent.GetType() == GUIEvent::Notification) + { + /////////////////////////////////////// + // Clicks on the New Scene Module combo + + if (anEvent.GetControl() == m_pNewModuleCombo) + { + // Closed it, IE selected somehting + if(anEvent.GetMsg() == GUIComboBox::Closed) + UpdateNewDialog(); + } + + } + } + */ + } -void EditorActivity::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) -{ - AllegroScreen drawScreen(pTargetBitmap); - m_pGUIController->Draw(&drawScreen); - if (m_EditorMode != EDITINGOBJECT) - m_pGUIController->DrawMouse(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + void EditorActivity::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + AllegroScreen drawScreen(pTargetBitmap); + m_pGUIController->Draw(&drawScreen); + if (m_EditorMode != EDITINGOBJECT) + m_pGUIController->DrawMouse(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this EditorActivity's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this EditorActivity's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. -void EditorActivity::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) -{ - -} + void EditorActivity::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + } } // namespace RTE \ No newline at end of file diff --git a/Source/Activities/EditorActivity.h b/Source/Activities/EditorActivity.h index a98edf963c..72781783c3 100644 --- a/Source/Activities/EditorActivity.h +++ b/Source/Activities/EditorActivity.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,350 +19,323 @@ #include "PostProcessMan.h" #include "PieMenu.h" -namespace RTE -{ - -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUITab; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUILabel; -class GUIComboBox; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: EditorActivity -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activity for editing things; owns and manages all the dialog boxes -// etc for docuemnt model, ie new, load, save, etc. -// Parent(s): Activity. -// Class history: 9/17/2007 EditorActivity created. - -class EditorActivity : public Activity { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - SerializableOverrideMethods; - ClassInfoGetters; - - // Different modes of this editor - enum EditorMode - { - NEWDIALOG = 0, - LOADDIALOG, - SAVEDIALOG, - CHANGESDIALOG, - OVERWRITEDIALOG, - EDITINGOBJECT, - TESTINGOBJECT, - EDITORMODECOUNT - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: EditorActivity -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a EditorActivity object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - EditorActivity() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~EditorActivity -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a EditorActivity object before deletion -// from system memory. -// Arguments: None. - - ~EditorActivity() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the EditorActivity object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a EditorActivity to be identical to another, by deep copy. -// Arguments: A reference to the EditorActivity to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const EditorActivity &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire EditorActivity, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Activity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the EditorActivity object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorMode(EditorMode newMode) { m_EditorMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorMode GetEditorMode() const { return m_EditorMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateNewDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - virtual void UpdateNewDialog() {} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateLoadDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Load dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - virtual void UpdateLoadDialog() {} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateSaveDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - virtual void UpdateSaveDialog() {} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChangesDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save Changes dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - virtual void UpdateChangesDialog() {} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateOverwriteDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Overwrite dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - virtual void UpdateOverwriteDialog() {} - - - // Member variables - static Entity::ClassInfo m_sClass; - // The current mode of the whole editor. See EditorGUIMode enum. - EditorMode m_EditorMode; - // Indicates the previous mode before a confirm dialog box was shown - EditorMode m_PreviousMode; - // Whether the mode has been changed - bool m_ModeChange; - // The DataModule space that we are editing within; can only place objects defined in the official modules plus this one - int m_ModuleSpaceID; - // Whether the edited object has been changed since last save - bool m_NeedSave; - // Whether this thing has ever been saved (or loaded). Will be true of new until it is saved - bool m_HasEverBeenSaved; - - std::unique_ptr m_PieMenu; //!< The pie menu this EditorActivity should use, if any. - - // GUI Screen for use by the GUI dialog boxes. Owned - GUIScreen *m_pGUIScreen; - // Input controller for he dialog box gui. Owned - GUIInput *m_pGUIInput; - // The control manager which holds all the gui elements for the dialog boxes. Owned - GUIControlManager *m_pGUIController; - - // New Dialog box - GUICollectionBox *m_pNewDialogBox; - // The combobox which lists all the DataModules that the new Entity can belong to - GUIComboBox *m_pNewModuleCombo; - // The button for asking for new Entity - GUIButton *m_pNewButton; - // The button for canceling new Entity dialog - GUIButton *m_pNewCancel; - - // Load Dialog box - GUICollectionBox *m_pLoadDialogBox; - // The combobox which lists all the Entities that can be loaded - GUIComboBox *m_pLoadNameCombo; - // The button for going to new dialog instead - GUIButton *m_pLoadToNewButton; - // The button for confirming Entity load - GUIButton *m_pLoadButton; - // The button for canceling load dialog - GUIButton *m_pLoadCancel; - - // Save Dialog box - GUICollectionBox *m_pSaveDialogBox; - // Textbox for entering the name of the thing to save. - GUITextBox *m_pSaveNameBox; - // The label which shows which DataModule this Scene is set to be saved to - GUILabel *m_pSaveModuleLabel; - // The button for confirming save - GUIButton *m_pSaveButton; - // The button for canceling save dialog - GUIButton *m_pSaveCancel; - - // Changes Dialog box - GUICollectionBox *m_pChangesDialogBox; - // The label for showing where it'll be saved - GUILabel *m_pChangesNameLabel; - // The button for confirming save changes - GUIButton *m_pChangesYesButton; - // The button for No resposnes to save changes - GUIButton *m_pChangesNoButton; - - // Overwrite Dialog box - GUICollectionBox *m_pOverwriteDialogBox; - // The label for showing what is about to be overwritten - GUILabel *m_pOverwriteNameLabel; - // The button for confirming overwrite existing - GUIButton *m_pOverwriteYesButton; - // The button for No resposnes to overwrite - GUIButton *m_pOverwriteNoButton; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUITab; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUILabel; + class GUIComboBox; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: EditorActivity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activity for editing things; owns and manages all the dialog boxes + // etc for docuemnt model, ie new, load, save, etc. + // Parent(s): Activity. + // Class history: 9/17/2007 EditorActivity created. + + class EditorActivity : public Activity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + SerializableOverrideMethods; + ClassInfoGetters; + + // Different modes of this editor + enum EditorMode { + NEWDIALOG = 0, + LOADDIALOG, + SAVEDIALOG, + CHANGESDIALOG, + OVERWRITEDIALOG, + EDITINGOBJECT, + TESTINGOBJECT, + EDITORMODECOUNT + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: EditorActivity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a EditorActivity object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + EditorActivity() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~EditorActivity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a EditorActivity object before deletion + // from system memory. + // Arguments: None. + + ~EditorActivity() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the EditorActivity object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a EditorActivity to be identical to another, by deep copy. + // Arguments: A reference to the EditorActivity to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const EditorActivity& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire EditorActivity, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Activity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the EditorActivity object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorMode(EditorMode newMode) { m_EditorMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorMode GetEditorMode() const { return m_EditorMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateNewDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + virtual void UpdateNewDialog() {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateLoadDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Load dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + virtual void UpdateLoadDialog() {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateSaveDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + virtual void UpdateSaveDialog() {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateChangesDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save Changes dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + virtual void UpdateChangesDialog() {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateOverwriteDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Overwrite dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + virtual void UpdateOverwriteDialog() {} + + // Member variables + static Entity::ClassInfo m_sClass; + // The current mode of the whole editor. See EditorGUIMode enum. + EditorMode m_EditorMode; + // Indicates the previous mode before a confirm dialog box was shown + EditorMode m_PreviousMode; + // Whether the mode has been changed + bool m_ModeChange; + // The DataModule space that we are editing within; can only place objects defined in the official modules plus this one + int m_ModuleSpaceID; + // Whether the edited object has been changed since last save + bool m_NeedSave; + // Whether this thing has ever been saved (or loaded). Will be true of new until it is saved + bool m_HasEverBeenSaved; + + std::unique_ptr m_PieMenu; //!< The pie menu this EditorActivity should use, if any. + + // GUI Screen for use by the GUI dialog boxes. Owned + GUIScreen* m_pGUIScreen; + // Input controller for he dialog box gui. Owned + GUIInput* m_pGUIInput; + // The control manager which holds all the gui elements for the dialog boxes. Owned + GUIControlManager* m_pGUIController; + + // New Dialog box + GUICollectionBox* m_pNewDialogBox; + // The combobox which lists all the DataModules that the new Entity can belong to + GUIComboBox* m_pNewModuleCombo; + // The button for asking for new Entity + GUIButton* m_pNewButton; + // The button for canceling new Entity dialog + GUIButton* m_pNewCancel; + + // Load Dialog box + GUICollectionBox* m_pLoadDialogBox; + // The combobox which lists all the Entities that can be loaded + GUIComboBox* m_pLoadNameCombo; + // The button for going to new dialog instead + GUIButton* m_pLoadToNewButton; + // The button for confirming Entity load + GUIButton* m_pLoadButton; + // The button for canceling load dialog + GUIButton* m_pLoadCancel; + + // Save Dialog box + GUICollectionBox* m_pSaveDialogBox; + // Textbox for entering the name of the thing to save. + GUITextBox* m_pSaveNameBox; + // The label which shows which DataModule this Scene is set to be saved to + GUILabel* m_pSaveModuleLabel; + // The button for confirming save + GUIButton* m_pSaveButton; + // The button for canceling save dialog + GUIButton* m_pSaveCancel; + + // Changes Dialog box + GUICollectionBox* m_pChangesDialogBox; + // The label for showing where it'll be saved + GUILabel* m_pChangesNameLabel; + // The button for confirming save changes + GUIButton* m_pChangesYesButton; + // The button for No resposnes to save changes + GUIButton* m_pChangesNoButton; + + // Overwrite Dialog box + GUICollectionBox* m_pOverwriteDialogBox; + // The label for showing what is about to be overwritten + GUILabel* m_pOverwriteNameLabel; + // The button for confirming overwrite existing + GUIButton* m_pOverwriteYesButton; + // The button for No resposnes to overwrite + GUIButton* m_pOverwriteNoButton; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Activities/GAScripted.cpp b/Source/Activities/GAScripted.cpp index aab6f8d335..ab1a7e1f99 100644 --- a/Source/Activities/GAScripted.cpp +++ b/Source/Activities/GAScripted.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -39,546 +38,531 @@ namespace RTE { -ConcreteClassInfo(GAScripted, GameActivity, 0); + ConcreteClassInfo(GAScripted, GameActivity, 0); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this GAScripted, effectively + // resetting the members of this abstraction level only. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this GAScripted, effectively -// resetting the members of this abstraction level only. + void GAScripted::Clear() { + m_ScriptPath.clear(); + m_LuaClassName.clear(); + m_RequiredAreas.clear(); + m_PieSlicesToAdd.clear(); + } -void GAScripted::Clear() { - m_ScriptPath.clear(); - m_LuaClassName.clear(); - m_RequiredAreas.clear(); - m_PieSlicesToAdd.clear(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GAScripted object ready for use. + int GAScripted::Create() { + if (GameActivity::Create() < 0) { + return -1; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GAScripted object ready for use. + if (m_Description.empty()) { + m_Description = "A scripted activity without a description yet. Tell the creator to write one."; + } -int GAScripted::Create() { - if (GameActivity::Create() < 0) { - return -1; - } + // Gotto have a script file path and lua class names defined to Create + if (m_ScriptPath.empty() || m_LuaClassName.empty()) { + return -1; + } - if (m_Description.empty()) { - m_Description = "A scripted activity without a description yet. Tell the creator to write one."; - } + // Scan the script file for any mentions/uses of Areas. + CollectRequiredAreas(); - // Gotto have a script file path and lua class names defined to Create - if (m_ScriptPath.empty() || m_LuaClassName.empty()) { - return -1; - } + // If the GAScripted has a OnSave() function, we assume it can be saved by default + ReloadScripts(); + m_AllowsUserSaving = HasSaveFunction(); - // Scan the script file for any mentions/uses of Areas. - CollectRequiredAreas(); + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a GAScripted to be identical to another, by deep copy. - // If the GAScripted has a OnSave() function, we assume it can be saved by default - ReloadScripts(); - m_AllowsUserSaving = HasSaveFunction(); + int GAScripted::Create(const GAScripted& reference) { + if (GameActivity::Create(reference) < 0) { + return -1; + } - return 0; -} + m_ScriptPath = reference.m_ScriptPath; + m_LuaClassName = reference.m_LuaClassName; + for (const std::string& referenceRequiredArea: reference.m_RequiredAreas) { + m_RequiredAreas.emplace(referenceRequiredArea); + } + for (const std::unique_ptr& referencePieSliceToAdd: reference.m_PieSlicesToAdd) { + m_PieSlicesToAdd.emplace_back(std::unique_ptr(dynamic_cast(referencePieSliceToAdd->Clone()))); + } + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a GAScripted to be identical to another, by deep copy. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int GAScripted::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return GameActivity::ReadProperty(propName, reader)); + + MatchProperty("ScriptPath", { + m_ScriptPath = g_PresetMan.GetFullModulePath(reader.ReadPropValue()); + }); + MatchProperty("LuaClassName", { + reader >> m_LuaClassName; + }); + MatchProperty("AddPieSlice", { + m_PieSlicesToAdd.emplace_back(std::unique_ptr(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)))); + }); + + EndPropertyList; + } -int GAScripted::Create(const GAScripted &reference) { - if (GameActivity::Create(reference) < 0) { - return -1; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this GAScripted with a Writer for + // later recreation with Create(Reader &reader); - m_ScriptPath = reference.m_ScriptPath; - m_LuaClassName = reference.m_LuaClassName; - for (const std::string &referenceRequiredArea : reference.m_RequiredAreas) { - m_RequiredAreas.emplace(referenceRequiredArea); - } - for (const std::unique_ptr &referencePieSliceToAdd : reference.m_PieSlicesToAdd) { - m_PieSlicesToAdd.emplace_back(std::unique_ptr(dynamic_cast(referencePieSliceToAdd->Clone()))); - } + int GAScripted::Save(Writer& writer) const { + // Hmm. We should probably be calling this prior to the writer Save, instead of const-casting. + const_cast(this)->RunLuaFunction("OnSave"); - return 0; -} + GameActivity::Save(writer); + writer.NewPropertyWithValue("ScriptPath", m_ScriptPath); + writer.NewPropertyWithValue("LuaClassName", m_LuaClassName); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int GAScripted::ReadProperty(const std::string_view &propName, Reader &reader) { - StartPropertyList(return GameActivity::ReadProperty(propName, reader)); - - MatchProperty("ScriptPath", { - m_ScriptPath = g_PresetMan.GetFullModulePath(reader.ReadPropValue()); - }); - MatchProperty("LuaClassName", { - reader >> m_LuaClassName; - }); - MatchProperty("AddPieSlice", { - m_PieSlicesToAdd.emplace_back(std::unique_ptr(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)))); - }); - - EndPropertyList; -} + for (const std::unique_ptr& pieSliceToAdd: m_PieSlicesToAdd) { + writer.NewPropertyWithValue("AddPieSlice", pieSliceToAdd.get()); + } + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this GAScripted with a Writer for -// later recreation with Create(Reader &reader); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the GAScripted object. -int GAScripted::Save(Writer &writer) const { - // Hmm. We should probably be calling this prior to the writer Save, instead of const-casting. - const_cast(this)->RunLuaFunction("OnSave"); + void GAScripted::Destroy(bool notInherited) { + // Delete global scripts + for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { + delete (*sItr); + } - GameActivity::Save(writer); + m_GlobalScriptsList.clear(); - writer.NewPropertyWithValue("ScriptPath", m_ScriptPath); - writer.NewPropertyWithValue("LuaClassName", m_LuaClassName); + if (!notInherited) { + GameActivity::Destroy(); + } - for (const std::unique_ptr &pieSliceToAdd : m_PieSlicesToAdd) { - writer.NewPropertyWithValue("AddPieSlice", pieSliceToAdd.get()); + Clear(); } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReloadScripts + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reloads the preset scripts of this object, from the same script file + // path as was originally defined. This will also update the original + // preset in the PresetMan with the updated scripts so future objects + // spawned will use the new scripts. + + int GAScripted::ReloadScripts() { + if (m_ScriptPath.empty()) { + return 0; + } + int error = 0; + CollectRequiredAreas(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GAScripted object. + // If it hasn't been yet, run the file that specifies the Lua functions for this' operating logic (including the scene test function) + if (!g_LuaMan.GetMasterScriptState().GlobalIsDefined(m_LuaClassName)) { + // Temporarily store this Activity so the Lua state can access it + g_LuaMan.GetMasterScriptState().SetTempEntity(this); -void GAScripted::Destroy(bool notInherited) { - // Delete global scripts - for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { - delete (*sItr); - } + // Define the var that will hold the script file definitions + if ((error = g_LuaMan.GetMasterScriptState().RunScriptString(m_LuaClassName + " = ToGameActivity(LuaMan.TempEntity);")) < 0) { + return error; + } + } - m_GlobalScriptsList.clear(); + if ((error = g_LuaMan.GetMasterScriptState().RunScriptFile(m_ScriptPath, true, false)) < 0) { + return error; + } - if (!notInherited) { - GameActivity::Destroy(); - } + RefreshActivityFunctions(); - Clear(); -} + return 0; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReloadScripts -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reloads the preset scripts of this object, from the same script file -// path as was originally defined. This will also update the original -// preset in the PresetMan with the updated scripts so future objects -// spawned will use the new scripts. + void GAScripted::RefreshActivityFunctions() { + m_ScriptFunctions.clear(); + if (m_ScriptPath.empty()) { + return; + } -int GAScripted::ReloadScripts() { - if (m_ScriptPath.empty()) { - return 0; - } + // We use m_LuaClassName here, because we ran the script file in the global state instead of in an environment + std::unordered_map scriptFileFunctions; + g_LuaMan.GetMasterScriptState().RetrieveFunctions(m_LuaClassName, GetSupportedScriptFunctionNames(), scriptFileFunctions); - int error = 0; - CollectRequiredAreas(); + for (const auto& [functionName, functionObject]: scriptFileFunctions) { + m_ScriptFunctions[functionName] = std::unique_ptr(functionObject); + } + } - // If it hasn't been yet, run the file that specifies the Lua functions for this' operating logic (including the scene test function) - if (!g_LuaMan.GetMasterScriptState().GlobalIsDefined(m_LuaClassName)) { - // Temporarily store this Activity so the Lua state can access it - g_LuaMan.GetMasterScriptState().SetTempEntity(this); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Define the var that will hold the script file definitions - if ((error = g_LuaMan.GetMasterScriptState().RunScriptString(m_LuaClassName + " = ToGameActivity(LuaMan.TempEntity);")) < 0) { - return error; - } - } + bool GAScripted::HasSaveFunction() const { + return m_ScriptFunctions.find("OnSave") != m_ScriptFunctions.end(); + } - if ((error = g_LuaMan.GetMasterScriptState().RunScriptFile(m_ScriptPath, true, false)) < 0) { - return error; - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - RefreshActivityFunctions(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SceneIsCompatible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells if a particular Scene supports this specific Activity on it. + // Usually that means certain Area:s need to be defined in the Scene. - return 0; -} + bool GAScripted::SceneIsCompatible(Scene* pScene, int teams) { + if (!GameActivity::SceneIsCompatible(pScene, teams)) { + return false; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Check if all Areas required by this are defined in the Scene + for (std::set::iterator itr = m_RequiredAreas.begin(); itr != m_RequiredAreas.end(); ++itr) { + // If Area is missing, this Scene is not up to par + if (!pScene->HasArea(*itr)) { + return false; + } + } -void GAScripted::RefreshActivityFunctions() { - m_ScriptFunctions.clear(); - if (m_ScriptPath.empty()) { - return; - } + // Temporarily store the scene so the Lua state can access it and check for the necessary Areas etc. + g_LuaMan.GetMasterScriptState().SetTempEntity(pScene); + // Cast the test scene it to a Scene object in Lua + if (g_LuaMan.GetMasterScriptState().RunScriptString("TestScene = ToScene(LuaMan.TempEntity);") < 0) { + return false; + } - // We use m_LuaClassName here, because we ran the script file in the global state instead of in an environment - std::unordered_map scriptFileFunctions; - g_LuaMan.GetMasterScriptState().RetrieveFunctions(m_LuaClassName, GetSupportedScriptFunctionNames(), scriptFileFunctions); + // If it hasn't been yet, run the file that specifies the Lua functions for this' operating logic (including the scene test function) + if (!g_LuaMan.GetMasterScriptState().GlobalIsDefined(m_LuaClassName)) { + // Temporarily store this Activity so the Lua state can access it + g_LuaMan.GetMasterScriptState().SetTempEntity(this); + // Define the var that will hold the script file definitions.. + // it's OK if the script fails, then the scene is still deemed compatible + if (g_LuaMan.GetMasterScriptState().RunScriptString(m_LuaClassName + " = ToGameActivity(LuaMan.TempEntity);") < 0) { + return true; + } + // Load and run the file, defining all the scripted functions of this Activity + if (g_LuaMan.GetMasterScriptState().RunScriptFile(m_ScriptPath) < 0) { + return true; + } + } - for (const auto& [functionName, functionObject] : scriptFileFunctions) { - m_ScriptFunctions[functionName] = std::unique_ptr(functionObject); - } -} + // Call the defined function, but only after first checking if it exists + g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".SceneTest then " + m_LuaClassName + ":SceneTest(); end"); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // If the test left the Scene pointer still set, it means it passed the test + return g_LuaMan.GetMasterScriptState().GlobalIsDefined("TestScene"); + } -bool GAScripted::HasSaveFunction() const { - return m_ScriptFunctions.find("OnSave") != m_ScriptFunctions.end(); -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void GAScripted::HandleCraftEnteringOrbit(ACraft* orbitedCraft) { + GameActivity::HandleCraftEnteringOrbit(orbitedCraft); + if (orbitedCraft && g_MovableMan.IsActor(orbitedCraft)) { + g_LuaMan.GetMasterScriptState().RunScriptFunctionString(m_LuaClassName + ".CraftEnteredOrbit", m_LuaClassName, {m_LuaClassName, m_LuaClassName + ".CraftEnteredOrbit"}, {orbitedCraft}); + for (GlobalScript* globalScript: m_GlobalScriptsList) { + globalScript->HandleCraftEnteringOrbit(orbitedCraft); + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SceneIsCompatible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells if a particular Scene supports this specific Activity on it. -// Usually that means certain Area:s need to be defined in the Scene. - -bool GAScripted::SceneIsCompatible(Scene *pScene, int teams) { - if (!GameActivity::SceneIsCompatible(pScene, teams)) { - return false; - } - - // Check if all Areas required by this are defined in the Scene - for (std::set::iterator itr = m_RequiredAreas.begin(); itr != m_RequiredAreas.end(); ++itr) { - // If Area is missing, this Scene is not up to par - if (!pScene->HasArea(*itr)) { - return false; - } - } - - // Temporarily store the scene so the Lua state can access it and check for the necessary Areas etc. - g_LuaMan.GetMasterScriptState().SetTempEntity(pScene); - // Cast the test scene it to a Scene object in Lua - if (g_LuaMan.GetMasterScriptState().RunScriptString("TestScene = ToScene(LuaMan.TempEntity);") < 0) { - return false; - } - - // If it hasn't been yet, run the file that specifies the Lua functions for this' operating logic (including the scene test function) - if (!g_LuaMan.GetMasterScriptState().GlobalIsDefined(m_LuaClassName)) { - // Temporarily store this Activity so the Lua state can access it - g_LuaMan.GetMasterScriptState().SetTempEntity(this); - // Define the var that will hold the script file definitions.. - // it's OK if the script fails, then the scene is still deemed compatible - if (g_LuaMan.GetMasterScriptState().RunScriptString(m_LuaClassName + " = ToGameActivity(LuaMan.TempEntity);") < 0) { - return true; - } - // Load and run the file, defining all the scripted functions of this Activity - if (g_LuaMan.GetMasterScriptState().RunScriptFile(m_ScriptPath) < 0) { - return true; - } - } - - // Call the defined function, but only after first checking if it exists - g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".SceneTest then " + m_LuaClassName + ":SceneTest(); end"); - - // If the test left the Scene pointer still set, it means it passed the test - return g_LuaMan.GetMasterScriptState().GlobalIsDefined("TestScene"); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void GAScripted::HandleCraftEnteringOrbit(ACraft *orbitedCraft) { - GameActivity::HandleCraftEnteringOrbit(orbitedCraft); - - if (orbitedCraft && g_MovableMan.IsActor(orbitedCraft)) { - g_LuaMan.GetMasterScriptState().RunScriptFunctionString(m_LuaClassName + ".CraftEnteredOrbit", m_LuaClassName, {m_LuaClassName, m_LuaClassName + ".CraftEnteredOrbit"}, {orbitedCraft}); - for (GlobalScript *globalScript : m_GlobalScriptsList) { - globalScript->HandleCraftEnteringOrbit(orbitedCraft); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. -int GAScripted::Start() { - ActivityState initialActivityState = m_ActivityState; + int GAScripted::Start() { + ActivityState initialActivityState = m_ActivityState; - int error = GameActivity::Start(); - if (error < 0) { - return error; - } + int error = GameActivity::Start(); + if (error < 0) { + return error; + } - // Temporarily store this Activity so the Lua state can access it - g_LuaMan.GetMasterScriptState().SetTempEntity(this); + // Temporarily store this Activity so the Lua state can access it + g_LuaMan.GetMasterScriptState().SetTempEntity(this); - // Define the var that will hold the script file definitions - if ((error = g_LuaMan.GetMasterScriptState().RunScriptString(m_LuaClassName + " = ToGameActivity(LuaMan.TempEntity);")) < 0) { - return error; - } + // Define the var that will hold the script file definitions + if ((error = g_LuaMan.GetMasterScriptState().RunScriptString(m_LuaClassName + " = ToGameActivity(LuaMan.TempEntity);")) < 0) { + return error; + } - // Run the file that specifies the Lua functions for this' operating logic - if ((error = ReloadScripts()) < 0) { - return error; - } + // Run the file that specifies the Lua functions for this' operating logic + if ((error = ReloadScripts()) < 0) { + return error; + } - // Call the create function - if ((error = RunLuaFunction("StartActivity", {}, { initialActivityState == ActivityState::NotStarted ? "true" : "false" }, {})) < 0) { - return error; - } + // Call the create function + if ((error = RunLuaFunction("StartActivity", {}, {initialActivityState == ActivityState::NotStarted ? "true" : "false"}, {})) < 0) { + return error; + } - // Clear active global scripts - for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { - delete (*sItr); - } + // Clear active global scripts + for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { + delete (*sItr); + } - m_GlobalScriptsList.clear(); + m_GlobalScriptsList.clear(); - // Get all global scripts and add to execution list - std::list globalScripts; - g_PresetMan.GetAllOfType(globalScripts, "GlobalScript"); + // Get all global scripts and add to execution list + std::list globalScripts; + g_PresetMan.GetAllOfType(globalScripts, "GlobalScript"); - for (std::list::iterator sItr = globalScripts.begin(); sItr != globalScripts.end(); ++sItr) { - m_GlobalScriptsList.push_back(dynamic_cast((*sItr)->Clone())); - } + for (std::list::iterator sItr = globalScripts.begin(); sItr != globalScripts.end(); ++sItr) { + m_GlobalScriptsList.push_back(dynamic_cast((*sItr)->Clone())); + } - // Start all global scripts - for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { - (*sItr)->Start(); + // Start all global scripts + for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { + (*sItr)->Start(); + } + + return error; } - return error; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + void GAScripted::SetPaused(bool pause) { + GameActivity::SetPaused(pause); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. + RunLuaFunction("PauseActivity", {}, {pause ? "true" : "false"}, {}); -void GAScripted::SetPaused(bool pause) { - GameActivity::SetPaused(pause); + // Pause all global scripts + for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { + (*sItr)->Pause(pause); + } + } - RunLuaFunction("PauseActivity", {}, { pause ? "true" : "false" }, {}); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. - // Pause all global scripts - for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { - (*sItr)->Pause(pause); - } -} + void GAScripted::End() { + GameActivity::End(); + RunLuaFunction("EndActivity"); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. + // End all global scripts + for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { + (*sItr)->End(); + } -void GAScripted::End() { - GameActivity::End(); + // Delete all global scripts, in case destructor is not called when activity restarts + for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { + delete (*sItr); + } - RunLuaFunction("EndActivity"); + m_GlobalScriptsList.clear(); + } - // End all global scripts - for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { - (*sItr)->End(); - } + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateEditing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: This is a special update step for when any player is still editing the + // scene. - // Delete all global scripts, in case destructor is not called when activity restarts - for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { - delete (*sItr); - } + void GAScripted::UpdateEditing() { + GameActivity::UpdateEditing(); + } + */ - m_GlobalScriptsList.clear(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this GAScripted. Supposed to be done every frame + // before drawing. -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateEditing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: This is a special update step for when any player is still editing the -// scene. + void GAScripted::Update() { + GameActivity::Update(); -void GAScripted::UpdateEditing() { - GameActivity::UpdateEditing(); -} -*/ + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) { + continue; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this GAScripted. Supposed to be done every frame -// before drawing. + // The current player's team + int team = m_Team[player]; + if (team == Teams::NoTeam) { + continue; + } + } + + // If the game didn't end, keep updating activity + if (m_ActivityState != ActivityState::Over) { + AddPieSlicesToActiveActorPieMenus(); -void GAScripted::Update() { - GameActivity::Update(); + // Need to call this continually unfortunately, as something might change due to dofile() + RefreshActivityFunctions(); - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - if (!(m_IsActive[player] && m_IsHuman[player])) { - continue; - } + RunLuaFunction("UpdateActivity"); - // The current player's team - int team = m_Team[player]; - if (team == Teams::NoTeam) { - continue; - } - } + UpdateGlobalScripts(false); + } + } - // If the game didn't end, keep updating activity - if (m_ActivityState != ActivityState::Over) { - AddPieSlicesToActiveActorPieMenus(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateGlobalScripts + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates globals scripts loaded with this activity. - // Need to call this continually unfortunately, as something might change due to dofile() - RefreshActivityFunctions(); + void GAScripted::UpdateGlobalScripts(bool lateUpdate) { + ZoneScoped; - RunLuaFunction("UpdateActivity"); + // Update all global scripts + for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { + if ((*sItr)->ShouldLateUpdate() == lateUpdate) { + (*sItr)->Update(); + } + } + } - UpdateGlobalScripts(false); - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + void GAScripted::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + GameActivity::DrawGUI(pTargetBitmap, targetPos, which); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateGlobalScripts -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates globals scripts loaded with this activity. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this GAScripted's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. -void GAScripted::UpdateGlobalScripts(bool lateUpdate) { - ZoneScoped; + void GAScripted::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + GameActivity::Draw(pTargetBitmap, targetPos); + } - // Update all global scripts - for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { - if ((*sItr)->ShouldLateUpdate() == lateUpdate) { - (*sItr)->Update(); - } - } -} + int GAScripted::RunLuaFunction(const std::string& functionName, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { + // Call the defined function, but only after first checking if it exists + auto funcItr = m_ScriptFunctions.find(functionName); + if (funcItr == m_ScriptFunctions.end()) { + return 0; + } + int error = g_LuaMan.GetMasterScriptState().RunScriptFunctionObject(funcItr->second.get(), "_G", m_LuaClassName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. + return error; + } -void GAScripted::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) { - GameActivity::DrawGUI(pTargetBitmap, targetPos, which); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollectRequiredAreas + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through the script file and checks for any mentions and uses of + // Area:s that are required for this Activity to run in a Scene. + + void GAScripted::CollectRequiredAreas() { + // Open the script file so we can check it out + std::ifstream scriptFile = std::ifstream(g_PresetMan.GetFullModulePath(m_ScriptPath.c_str())); + if (!scriptFile.good()) { + return; + } + // Harvest the required Area:s from the file + m_RequiredAreas.clear(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this GAScripted's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. + bool blockCommented = false; -void GAScripted::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) { - GameActivity::Draw(pTargetBitmap, targetPos); -} + while (!scriptFile.eof()) { + // Go through the script file, line by line + char rawLine[512]; + scriptFile.getline(rawLine, 512); + std::string line = rawLine; + std::string::size_type pos = 0; + std::string::size_type endPos = 0; + std::string::size_type commentPos = std::string::npos; -int GAScripted::RunLuaFunction(const std::string& functionName, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { - // Call the defined function, but only after first checking if it exists - auto funcItr = m_ScriptFunctions.find(functionName); - if (funcItr == m_ScriptFunctions.end()) { - return 0; - } + // Check for block comments + if (!blockCommented && (commentPos = line.find("--[[", 0)) != std::string::npos) { + blockCommented = true; + } - int error = g_LuaMan.GetMasterScriptState().RunScriptFunctionObject(funcItr->second.get(), "_G", m_LuaClassName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + // Find the end of the block comment + if (blockCommented) { + if ((commentPos = line.find("]]", commentPos == std::string::npos ? 0 : commentPos)) != std::string::npos) { + blockCommented = false; + pos = commentPos; + } + } - return error; -} + // Process the line as usual + if (!blockCommented) { + // See if this line is commented out anywhere + commentPos = line.find("--", 0); + do { + // Find the beginning of a mentioned Area name + pos = line.find(":GetArea(\"", pos); + if (pos != std::string::npos && pos < commentPos) { + // Move position forward to the actual Area name + pos += 10; + // Find the end of the Area name + endPos = line.find_first_of('"', pos); + // Copy it out and put into the list + if (endPos != std::string::npos) { + m_RequiredAreas.insert(line.substr(pos, endPos - pos)); + } + } + } while (pos != std::string::npos && pos < commentPos); + } + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollectRequiredAreas -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through the script file and checks for any mentions and uses of -// Area:s that are required for this Activity to run in a Scene. - -void GAScripted::CollectRequiredAreas() { - // Open the script file so we can check it out - std::ifstream scriptFile = std::ifstream(g_PresetMan.GetFullModulePath(m_ScriptPath.c_str())); - if (!scriptFile.good()) { - return; - } - - // Harvest the required Area:s from the file - m_RequiredAreas.clear(); - - bool blockCommented = false; - - while (!scriptFile.eof()) { - // Go through the script file, line by line - char rawLine[512]; - scriptFile.getline(rawLine, 512); - std::string line = rawLine; - std::string::size_type pos = 0; - std::string::size_type endPos = 0; - std::string::size_type commentPos = std::string::npos; - - // Check for block comments - if (!blockCommented && (commentPos = line.find("--[[", 0)) != std::string::npos) { - blockCommented = true; - } - - // Find the end of the block comment - if (blockCommented) { - if ((commentPos = line.find("]]", commentPos == std::string::npos ? 0 : commentPos)) != std::string::npos) { - blockCommented = false; - pos = commentPos; - } - } - - // Process the line as usual - if (!blockCommented) { - // See if this line is commented out anywhere - commentPos = line.find("--", 0); - do { - // Find the beginning of a mentioned Area name - pos = line.find(":GetArea(\"", pos); - if (pos != std::string::npos && pos < commentPos) { - // Move position forward to the actual Area name - pos += 10; - // Find the end of the Area name - endPos = line.find_first_of('"', pos); - // Copy it out and put into the list - if (endPos != std::string::npos) { - m_RequiredAreas.insert(line.substr(pos, endPos - pos)); - } - } - } while(pos != std::string::npos && pos < commentPos); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void GAScripted::AddPieSlicesToActiveActorPieMenus() { - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - if (m_IsActive[player] && m_IsHuman[player] && m_ControlledActor[player] && m_ViewState[player] != ViewState::DeathWatch && m_ViewState[player] != ViewState::ActorSelect && m_ViewState[player] != ViewState::AIGoToPoint && m_ViewState[player] != ViewState::UnitSelectCircle) { - PieMenu *controlledActorPieMenu = m_ControlledActor[player]->GetPieMenu(); - if (controlledActorPieMenu && m_ControlledActor[player]->GetController()->IsState(PIE_MENU_ACTIVE) && controlledActorPieMenu->IsEnabling()) { - for (const std::unique_ptr &pieSlice : m_PieSlicesToAdd) { - controlledActorPieMenu->AddPieSliceIfPresetNameIsUnique(pieSlice.get(), this, true); - } - for (const GlobalScript *globalScript : m_GlobalScriptsList) { - for (const std::unique_ptr &pieSlice : globalScript->GetPieSlicesToAdd()) { - controlledActorPieMenu->AddPieSliceIfPresetNameIsUnique(pieSlice.get(), globalScript, true); + void GAScripted::AddPieSlicesToActiveActorPieMenus() { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (m_IsActive[player] && m_IsHuman[player] && m_ControlledActor[player] && m_ViewState[player] != ViewState::DeathWatch && m_ViewState[player] != ViewState::ActorSelect && m_ViewState[player] != ViewState::AIGoToPoint && m_ViewState[player] != ViewState::UnitSelectCircle) { + PieMenu* controlledActorPieMenu = m_ControlledActor[player]->GetPieMenu(); + if (controlledActorPieMenu && m_ControlledActor[player]->GetController()->IsState(PIE_MENU_ACTIVE) && controlledActorPieMenu->IsEnabling()) { + for (const std::unique_ptr& pieSlice: m_PieSlicesToAdd) { + controlledActorPieMenu->AddPieSliceIfPresetNameIsUnique(pieSlice.get(), this, true); + } + for (const GlobalScript* globalScript: m_GlobalScriptsList) { + for (const std::unique_ptr& pieSlice: globalScript->GetPieSlicesToAdd()) { + controlledActorPieMenu->AddPieSliceIfPresetNameIsUnique(pieSlice.get(), globalScript, true); + } } } } } } -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace RTE \ No newline at end of file diff --git a/Source/Activities/GAScripted.h b/Source/Activities/GAScripted.h index e756588ecd..5304e70897 100644 --- a/Source/Activities/GAScripted.h +++ b/Source/Activities/GAScripted.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,308 +19,288 @@ #include "LuabindObjectWrapper.h" -namespace RTE -{ - -class ACraft; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: GAScripted -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Scripted activity -// Parent(s): GameActivity. -// Class history: 07/03/2008 GAScripted created. - -class GAScripted : public GameActivity { - - friend class LuaMan; - friend class ActivityMan; - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - -ScriptFunctionNames("StartActivity", "UpdateActivity", "PauseActivity", "EndActivity", "OnSave", "CraftEnteredOrbit", "OnMessage", "OnGlobalMessage"); - -// Concrete allocation and cloning definitions -EntityAllocation(GAScripted); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GAScripted -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GAScripted object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - GAScripted() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~GAScripted -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a GAScripted object before deletion -// from system memory. -// Arguments: None. - - ~GAScripted() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GAScripted object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GAScripted object ready for use. -// Arguments: The filepath to the script that defines this' Lua-defined derivation -// of this class. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(std::string scriptPath, std::string scriptClassName) { m_ScriptPath = scriptPath; m_LuaClassName = scriptClassName; return Create(); }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a GAScripted to be identical to another, by deep copy. -// Arguments: A reference to the GAScripted to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const GAScripted &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire GAScripted, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Activity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GAScripted object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReloadScripts -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reloads the preset scripts of this object, from the same script file -// path as was originally defined. This will also update the original -// preset in the PresetMan with the updated scripts so future objects -// spawned will use the new scripts. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int ReloadScripts() override; - - /// - /// Refreshes our activity functions to find any changes from script. - /// - void RefreshActivityFunctions(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLuaClassName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the class name of the Lua-derived class defined in this' script. -// Arguments: None. -// Return value: A string with the friendly-formatted Lua type name of this object. - - const std::string & GetLuaClassName() const { return m_LuaClassName; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SceneIsCompatible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells if a particular Scene supports this specific Activity on it. -// Usually that means certain Area:s need to be defined in the Scene. -// Arguments: The Scene to check if it supports this Activiy. Ownership IS NOT TRANSFERRED! -// How many teams we're checking for. Some scenes may support and activity -// but only for a limited number of teams. If -1, not applicable. -// Return value: Whether the Scene has the right stuff. - - bool SceneIsCompatible(Scene *pScene, int teams = -1) override; - - - /// - /// Handles when an ACraft has left the game scene and entered orbit, though does not delete it. Ownership is NOT transferred, as the ACraft's inventory is just 'unloaded'. - /// - /// The ACraft instance that entered orbit. Ownership is NOT transferred! - void HandleCraftEnteringOrbit(ACraft *orbitedCraft) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateGlobalScripts -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates globals scripts loaded with this activity. -// Arguments: Whether it's an early update, during Activity update, or late update, after MovableMan -// Return value: None. - - void UpdateGlobalScripts(bool lateUpdate); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector& targetPos = Vector()) override; - - int RunLuaFunction(const std::string& functionName, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CollectRequiredAreas -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through the script file and checks for any mentions and uses of -// Area:s that are required for this Activity to run in a Scene. -// Arguments: None. -// Return value: None. - - void CollectRequiredAreas(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: InitAIs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does nothing - we do this in script! Just overrides the base behaviour. -// Arguments: None. -// Return value: None. - - void InitAIs() override {}; - - - // Member variables - static Entity::ClassInfo m_sClass; - - // The path to the lua script file that defines this' behaviors with overrides of its virtual functions - std::string m_ScriptPath; - // The name of the class (table) defining the logic of this in Lua, as specified in the script file - std::string m_LuaClassName; - // The list of Area:s required in a Scene to play this Activity on it - std::set m_RequiredAreas; - std::vector> m_PieSlicesToAdd; //!< A vector of PieSlices that should be added to any PieMenus opened while this GAScripted is running. - // The list of global scripts allowed to run during this activity - std::vector m_GlobalScriptsList; - - std::unordered_map> m_ScriptFunctions; //!< A map of LuabindObjectWrappers that hold Lua functions. Used to maintain script execution order and avoid extraneous Lua calls. - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - /// - /// Returns whether this GAScripted has an OnSave function, to act as a default for whether saving is allowed or not. - /// - /// Whether this GAScripted has an OnSave function - bool HasSaveFunction() const; - - /// - /// Adds this GAScripted's PieSlices, and any active GlobalScripts' PieSlices, to any active PieMenus. - /// - void AddPieSlicesToActiveActorPieMenus(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class ACraft; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: GAScripted + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Scripted activity + // Parent(s): GameActivity. + // Class history: 07/03/2008 GAScripted created. + + class GAScripted : public GameActivity { + + friend class LuaMan; + friend class ActivityMan; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + ScriptFunctionNames("StartActivity", "UpdateActivity", "PauseActivity", "EndActivity", "OnSave", "CraftEnteredOrbit", "OnMessage", "OnGlobalMessage"); + + // Concrete allocation and cloning definitions + EntityAllocation(GAScripted); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GAScripted + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GAScripted object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + GAScripted() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~GAScripted + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a GAScripted object before deletion + // from system memory. + // Arguments: None. + + ~GAScripted() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GAScripted object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GAScripted object ready for use. + // Arguments: The filepath to the script that defines this' Lua-defined derivation + // of this class. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(std::string scriptPath, std::string scriptClassName) { + m_ScriptPath = scriptPath; + m_LuaClassName = scriptClassName; + return Create(); + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a GAScripted to be identical to another, by deep copy. + // Arguments: A reference to the GAScripted to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const GAScripted& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire GAScripted, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Activity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the GAScripted object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReloadScripts + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reloads the preset scripts of this object, from the same script file + // path as was originally defined. This will also update the original + // preset in the PresetMan with the updated scripts so future objects + // spawned will use the new scripts. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int ReloadScripts() override; + + /// + /// Refreshes our activity functions to find any changes from script. + /// + void RefreshActivityFunctions(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLuaClassName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the class name of the Lua-derived class defined in this' script. + // Arguments: None. + // Return value: A string with the friendly-formatted Lua type name of this object. + + const std::string& GetLuaClassName() const { return m_LuaClassName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SceneIsCompatible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells if a particular Scene supports this specific Activity on it. + // Usually that means certain Area:s need to be defined in the Scene. + // Arguments: The Scene to check if it supports this Activiy. Ownership IS NOT TRANSFERRED! + // How many teams we're checking for. Some scenes may support and activity + // but only for a limited number of teams. If -1, not applicable. + // Return value: Whether the Scene has the right stuff. + + bool SceneIsCompatible(Scene* pScene, int teams = -1) override; + + /// + /// Handles when an ACraft has left the game scene and entered orbit, though does not delete it. Ownership is NOT transferred, as the ACraft's inventory is just 'unloaded'. + /// + /// The ACraft instance that entered orbit. Ownership is NOT transferred! + void HandleCraftEnteringOrbit(ACraft* orbitedCraft) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateGlobalScripts + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates globals scripts loaded with this activity. + // Arguments: Whether it's an early update, during Activity update, or late update, after MovableMan + // Return value: None. + + void UpdateGlobalScripts(bool lateUpdate); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + int RunLuaFunction(const std::string& functionName, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CollectRequiredAreas + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through the script file and checks for any mentions and uses of + // Area:s that are required for this Activity to run in a Scene. + // Arguments: None. + // Return value: None. + + void CollectRequiredAreas(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: InitAIs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does nothing - we do this in script! Just overrides the base behaviour. + // Arguments: None. + // Return value: None. + + void InitAIs() override{}; + + // Member variables + static Entity::ClassInfo m_sClass; + + // The path to the lua script file that defines this' behaviors with overrides of its virtual functions + std::string m_ScriptPath; + // The name of the class (table) defining the logic of this in Lua, as specified in the script file + std::string m_LuaClassName; + // The list of Area:s required in a Scene to play this Activity on it + std::set m_RequiredAreas; + std::vector> m_PieSlicesToAdd; //!< A vector of PieSlices that should be added to any PieMenus opened while this GAScripted is running. + // The list of global scripts allowed to run during this activity + std::vector m_GlobalScriptsList; + + std::unordered_map> m_ScriptFunctions; //!< A map of LuabindObjectWrappers that hold Lua functions. Used to maintain script execution order and avoid extraneous Lua calls. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + /// + /// Returns whether this GAScripted has an OnSave function, to act as a default for whether saving is allowed or not. + /// + /// Whether this GAScripted has an OnSave function + bool HasSaveFunction() const; + + /// + /// Adds this GAScripted's PieSlices, and any active GlobalScripts' PieSlices, to any active PieMenus. + /// + void AddPieSlicesToActiveActorPieMenus(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Activities/GATutorial.cpp b/Source/Activities/GATutorial.cpp index b287ce70b2..cdc004f1a8 100644 --- a/Source/Activities/GATutorial.cpp +++ b/Source/Activities/GATutorial.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -37,1038 +36,954 @@ namespace RTE { -ConcreteClassInfo(GATutorial, GameActivity, 0); + ConcreteClassInfo(GATutorial, GameActivity, 0); + GATutorial::TutStep::TutStep(std::string text, int stepDuration, std::string screensPath, int frameCount, int frameDuration) { + m_Text = text; + m_Duration = stepDuration; + m_FrameDuration = frameDuration; -GATutorial::TutStep::TutStep(std::string text, int stepDuration, std::string screensPath, int frameCount, int frameDuration) -{ - m_Text = text; - m_Duration = stepDuration; - m_FrameDuration = frameDuration; + if (!screensPath.empty()) { + ContentFile(screensPath.c_str()).GetAsAnimation(m_pScreens, frameCount); + } + } - if (!screensPath.empty()) - { - ContentFile(screensPath.c_str()).GetAsAnimation(m_pScreens, frameCount); - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this GATutorial, effectively + // resetting the members of this abstraction level only. + + void GATutorial::Clear() { + m_TutorialPlayer = Players::PlayerOne; + + for (int area = 0; area < AREACOUNT; ++area) { + m_TriggerBoxes[area].Reset(); + m_ScreenPositions[area].Reset(); + m_ScreenStates[area] = SCREENOFF; + m_TextOffsets[area].Reset(); + m_TutAreaSteps[area].clear(); + } + for (int room = 0; room < ROOMCOUNT; ++room) { + m_RoomSignPositions[room].Reset(); + m_aapRoomSigns[room][UNLIT] = 0; + m_aapRoomSigns[room][LIT] = 0; + } + m_AreaTimer.Reset(); + m_StepTimer.Reset(); + m_CurrentArea = BRAINCHAMBER; + m_PrevArea = BRAINCHAMBER; + m_ScreenChange = false; + m_CurrentStep = 0; + m_CurrentFrame = 0; + m_CurrentRoom = ROOM0; + + for (int stage = 0; stage < FIGHTSTAGECOUNT; ++stage) + m_FightTriggers[stage].Reset(); + + m_EnemyCount = 0; + m_CurrentFightStage = NOFIGHT; + m_pCPUBrain = 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GATutorial object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this GATutorial, effectively -// resetting the members of this abstraction level only. - -void GATutorial::Clear() -{ - m_TutorialPlayer = Players::PlayerOne; - - for (int area = 0; area < AREACOUNT; ++area) - { - m_TriggerBoxes[area].Reset(); - m_ScreenPositions[area].Reset(); - m_ScreenStates[area] = SCREENOFF; - m_TextOffsets[area].Reset(); - m_TutAreaSteps[area].clear(); - } - for (int room = 0; room < ROOMCOUNT; ++room) - { - m_RoomSignPositions[room].Reset(); - m_aapRoomSigns[room][UNLIT] = 0; - m_aapRoomSigns[room][LIT] = 0; - } - m_AreaTimer.Reset(); - m_StepTimer.Reset(); - m_CurrentArea = BRAINCHAMBER; - m_PrevArea = BRAINCHAMBER; - m_ScreenChange = false; - m_CurrentStep = 0; - m_CurrentFrame = 0; - m_CurrentRoom = ROOM0; - - for (int stage = 0; stage < FIGHTSTAGECOUNT; ++stage) - m_FightTriggers[stage].Reset(); - - m_EnemyCount = 0; - m_CurrentFightStage = NOFIGHT; - m_pCPUBrain = 0; -} + int GATutorial::Create() { + if (GameActivity::Create() < 0) + return -1; + m_Description = "A tutorial for learning how to play Cortex Command. A good place to start!"; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GATutorial object ready for use. + return 0; + } -int GATutorial::Create() -{ - if (GameActivity::Create() < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a GATutorial to be identical to another, by deep copy. + + int GATutorial::Create(const GATutorial& reference) { + if (GameActivity::Create(reference) < 0) + return -1; + + for (int area = 0; area < AREACOUNT; ++area) { + m_TriggerBoxes[area] = reference.m_TriggerBoxes[area]; + m_ScreenPositions[area] = reference.m_ScreenPositions[area]; + m_ScreenStates[area] = reference.m_ScreenStates[area]; + m_TextOffsets[area] = reference.m_TextOffsets[area]; + m_TutAreaSteps[area] = reference.m_TutAreaSteps[area]; + } + for (int room = 0; room < ROOMCOUNT; ++room) { + m_RoomSignPositions[room] = reference.m_RoomSignPositions[room]; + } + m_AreaTimer.Reset(); + m_StepTimer.Reset(); + m_CurrentArea = reference.m_CurrentArea; + m_PrevArea = reference.m_PrevArea; + m_CurrentStep = reference.m_CurrentStep; + m_CurrentFrame = reference.m_CurrentFrame; + m_CurrentRoom = reference.m_CurrentRoom; + + for (int stage = 0; stage < FIGHTSTAGECOUNT; ++stage) + m_FightTriggers[stage] = reference.m_FightTriggers[stage]; + + m_EnemyCount = reference.m_EnemyCount; + m_CurrentFightStage = reference.m_CurrentFightStage; + // DOn't, owned and need to make deep copy in that case + // m_pCPUBrain; + + return 0; + } - m_Description = "A tutorial for learning how to play Cortex Command. A good place to start!"; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int GATutorial::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return GameActivity::ReadProperty(propName, reader)); + /* + MatchProperty("SpawnIntervalEasiest", { reader >> m_SpawnIntervalEasiest; }); + MatchProperty("SpawnIntervalHardest", { reader >> m_SpawnIntervalHardest; }); + MatchProperty("AddAttackerSpawn", + Actor *pNewSpawn = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); + if (pNewSpawn) { m_AttackerSpawns.push_back(pNewSpawn); } ); + */ + EndPropertyList; + } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this GATutorial with a Writer for + // later recreation with Create(Reader &reader); + int GATutorial::Save(Writer& writer) const { + GameActivity::Save(writer); + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a GATutorial to be identical to another, by deep copy. - -int GATutorial::Create(const GATutorial &reference) -{ - if (GameActivity::Create(reference) < 0) - return -1; - - for (int area = 0; area < AREACOUNT; ++area) - { - m_TriggerBoxes[area] = reference.m_TriggerBoxes[area]; - m_ScreenPositions[area] = reference.m_ScreenPositions[area]; - m_ScreenStates[area] = reference.m_ScreenStates[area]; - m_TextOffsets[area] = reference.m_TextOffsets[area]; - m_TutAreaSteps[area] = reference.m_TutAreaSteps[area]; - } - for (int room = 0; room < ROOMCOUNT; ++room) - { - m_RoomSignPositions[room] = reference.m_RoomSignPositions[room]; - } - m_AreaTimer.Reset(); - m_StepTimer.Reset(); - m_CurrentArea = reference.m_CurrentArea; - m_PrevArea = reference.m_PrevArea; - m_CurrentStep = reference.m_CurrentStep; - m_CurrentFrame = reference.m_CurrentFrame; - m_CurrentRoom = reference.m_CurrentRoom; - - for (int stage = 0; stage < FIGHTSTAGECOUNT; ++stage) - m_FightTriggers[stage] = reference.m_FightTriggers[stage]; - - m_EnemyCount = reference.m_EnemyCount; - m_CurrentFightStage = reference.m_CurrentFightStage; -// DOn't, owned and need to make deep copy in that case -// m_pCPUBrain; - - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the GATutorial object. + void GATutorial::Destroy(bool notInherited) { + if (!notInherited) + GameActivity::Destroy(); + Clear(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int GATutorial::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return GameActivity::ReadProperty(propName, reader)); -/* - MatchProperty("SpawnIntervalEasiest", { reader >> m_SpawnIntervalEasiest; }); - MatchProperty("SpawnIntervalHardest", { reader >> m_SpawnIntervalHardest; }); - MatchProperty("AddAttackerSpawn", - Actor *pNewSpawn = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); - if (pNewSpawn) { m_AttackerSpawns.push_back(pNewSpawn); } ); -*/ - EndPropertyList; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SceneIsCompatible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells if a particular Scene supports this specific Activity on it. + // Usually that means certain Area:s need to be defined in the Scene. + bool GATutorial::SceneIsCompatible(Scene* pScene, int teams) { + if (!GameActivity::SceneIsCompatible(pScene, teams)) + return false; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this GATutorial with a Writer for -// later recreation with Create(Reader &reader); + // Quick and dirty hardcoded hack.. only this scene is compatible anyway + if (pScene->GetPresetName() == "Tutorial Bunker") + return true; -int GATutorial::Save(Writer &writer) const { - GameActivity::Save(writer); - return 0; -} + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. + + int GATutorial::Start() { + int error = GameActivity::Start(); + + //////////////////////////////// + // Set up teams + + // Team 2 is always CPU + SetCPUTeam(Teams::TeamTwo); + + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; + + if (team == Teams::TeamOne) { + // See if there are specified landing zone areas defined in the scene + char str[64]; + std::snprintf(str, sizeof(str), "LZ Team %d", team + 1); + Scene::Area* pArea = g_SceneMan.GetScene()->GetArea(str); + // pArea = pArea ? pArea : g_SceneMan.GetScene()->GetArea("Landing Zone"); + // If area is defined, save a copy so we can lock the LZ selection to within its boxes + if (pArea && !pArea->HasNoArea()) + SetLZArea(team, *pArea); + } + + // If this is a CPU controlled team, then try to find a brain for them, or place one + if (team == m_CPUTeam && !m_pCPUBrain) { + // TODO: Make special CPU brain actor which will only appear in CPU brain fights, and have to be placed in the scenes + m_pCPUBrain = g_MovableMan.GetUnassignedBrain(team); + m_pCPUBrain = m_pCPUBrain ? m_pCPUBrain : g_MovableMan.GetFirstBrainActor(team); // Try getting our assigned brain + if (!m_pCPUBrain) { + // Couldn't find an available brain in the scene, so make one and place it + m_pCPUBrain = dynamic_cast(g_PresetMan.GetEntityPreset("Actor", "Brain Case")->Clone()); + if (m_pCPUBrain) { + Vector brainPos; + // Place opposite of the other team's brain + Actor* pOtherBrain = g_MovableMan.GetFirstOtherBrainActor(team); + if (pOtherBrain) { + brainPos = pOtherBrain->GetPos(); + brainPos.m_X += g_SceneMan.GetSceneWidth() / 2; + brainPos.m_Y = g_SceneMan.GetSceneHeight() - 200; + g_SceneMan.WrapPosition(brainPos); + } + // No brain found on other team... then just place somewhere in the ground, spaced out + else { + if (team == Teams::TeamOne) + brainPos.SetXY((float)g_SceneMan.GetSceneWidth() * 0.25, (float)g_SceneMan.GetSceneHeight() * 0.75); + if (team == Teams::TeamTwo) + brainPos.SetXY((float)g_SceneMan.GetSceneWidth() * 0.75, (float)g_SceneMan.GetSceneHeight() * 0.75); + } + m_pCPUBrain->SetPos(brainPos); + m_pCPUBrain->SetTeam(team); + // Transfer ownership here + g_MovableMan.AddActor(m_pCPUBrain); + pOtherBrain = 0; + } + } + m_EnemyCount = g_MovableMan.GetTeamRoster(m_CPUTeam)->size(); + } + // Give the player some scratch + else + m_TeamFunds[team] = this->GetStartingGold(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GATutorial object. + /////////////////////////////////////// + // Set up players + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + // Expand all modules to show the goods + m_pBuyGUI[player]->SetModuleExpanded(0); + + // No need for the LZ to follow the brain, the scene-set one is enough + SetBrainLZWidth(player, 0); + + // If we can't find an unassigned brain in the scene to give each player, then force to go into editing mode to place one + if (!m_Brain[player]) { + g_ConsoleMan.PrintString("ERROR: Can't find brain for tutorial game mode!"); + } + // Set the found brain to be the selected actor at start + else { + m_TutorialPlayer = player; + } + /* + if (m_ActivityState == ActivityState::Editing) + g_FrameMan.SetScreenText((player % 2 == 0) ? "Place your brain vault and build your bunker around it..." : "...then select \"DONE\" from the pie menu!", ScreenOfPlayer(player), 0); + else if (m_ActivityState == ActivityState::Running) + g_FrameMan.SetScreenText((player % 2 == 0) ? "Mine Gold and buy more firepower with the funds..." : "...then smash the competing brain to claim victory!", ScreenOfPlayer(player), 0); + */ + } -void GATutorial::Destroy(bool notInherited) -{ - if (!notInherited) - GameActivity::Destroy(); - Clear(); -} + ///////////////////////////////////////////// + // SET UP TUTORIAL + + // COMMON SCREENS + std::vector apScreens = ContentFile("Missions.rte/Objects/Tutorial/ScreenStatic.png").GetAsAnimation(3); + m_apCommonScreens[SCREENOFF] = apScreens[0]; + m_apCommonScreens[STATICLITTLE] = apScreens[1]; + m_apCommonScreens[STATICLARGE] = apScreens[2]; + + // ROOM SIGNS + ContentFile signFile; + for (int room = 0; room < ROOMCOUNT; ++room) { + if (room == ROOM0) + signFile.SetDataPath("Missions.rte/Objects/Tutorial/TutEntryA.png"); + else if (room == ROOM1) + signFile.SetDataPath("Missions.rte/Objects/Tutorial/TutEntryB.png"); + else if (room == ROOM2) + signFile.SetDataPath("Missions.rte/Objects/Tutorial/TutEntryC.png"); + else if (room == ROOM3) + signFile.SetDataPath("Missions.rte/Objects/Tutorial/TutEntryD.png"); + std::vector apSigns = signFile.GetAsAnimation(2); + m_aapRoomSigns[room][UNLIT] = apSigns[0]; + m_aapRoomSigns[room][LIT] = apSigns[1]; + } + m_RoomSignPositions[ROOM0].SetXY(744, 695); + m_RoomSignPositions[ROOM1].SetXY(888, 695); + m_RoomSignPositions[ROOM2].SetXY(888, 599); + m_RoomSignPositions[ROOM3].SetXY(888, 431); + m_CurrentRoom = ROOM3; + + ////////////////////////////////// + // Setup all the Tutorial Areas and their text instructions etc + + SetupAreas(); + + // Disable all enemy AIs so they dont attack prematurely + DisableAIs(true, Teams::TeamTwo); + + ////////////////////////////////// + // FIGHT TRIGGERS + + m_FightTriggers[DEFENDING].Create(Vector(1330, 0), 770, 400); + + // m_FightTriggerEast.Create(Vector(526, 0), 52, 840); + // m_FightTriggerWest.Create(Vector(1336, 0), 52, 840); + /* + // Start special tutorial playlist + g_AudioMan.ClearMusicQueue(); + g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/ccambient4.ogg", 0); + g_AudioMan.QueueSilence(30); + g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/cc2g.ogg"); + g_AudioMan.QueueSilence(30); + g_AudioMan.QueueMusicStream("Base.rte/Music/Watts/Last Man.ogg"); + g_AudioMan.QueueSilence(30); + g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/cc2g.ogg"); + */ + return error; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SceneIsCompatible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells if a particular Scene supports this specific Activity on it. -// Usually that means certain Area:s need to be defined in the Scene. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. -bool GATutorial::SceneIsCompatible(Scene *pScene, int teams) -{ - if (!GameActivity::SceneIsCompatible(pScene, teams)) - return false; + void GATutorial::SetPaused(bool pause) { + GameActivity::SetPaused(pause); - // Quick and dirty hardcoded hack.. only this scene is compatible anyway - if (pScene->GetPresetName() == "Tutorial Bunker") - return true; + // Re-setup the ares with any updated control mappings that the player might have made in teh menu + if (!pause) + SetupAreas(); + } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + + void GATutorial::End() { + GameActivity::End(); + + bool playerWon = false; + // Show appropriate end game messages + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + if (m_Team[player] == m_WinnerTeam) { + playerWon = true; + // Set the winner's observation view to his controlled actors instead of his brain + if (m_ControlledActor[player] && g_MovableMan.IsActor(m_ControlledActor[player])) + m_ObservationTarget[player] = m_ControlledActor[player]->GetPos(); + } + } + // Temp fix so music doesn't start playing if ending the Activity when changing resolution through the in-game settings. + if (!m_Paused) { + // Play the appropriate tune on player win/lose + if (playerWon) { + g_AudioMan.ClearMusicQueue(); + // Loop it twice, nice tune! + g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/uwinfinal.ogg", 2); + g_AudioMan.QueueSilence(10); + g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg"); + } else { + g_AudioMan.ClearMusicQueue(); + g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/udiedfinal.ogg", 0); + g_AudioMan.QueueSilence(10); + g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg"); + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. - -int GATutorial::Start() -{ - int error = GameActivity::Start(); - - //////////////////////////////// - // Set up teams - - // Team 2 is always CPU - SetCPUTeam(Teams::TeamTwo); - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - - if (team == Teams::TeamOne) - { - // See if there are specified landing zone areas defined in the scene - char str[64]; - std::snprintf(str, sizeof(str), "LZ Team %d", team + 1); - Scene::Area *pArea = g_SceneMan.GetScene()->GetArea(str); - // pArea = pArea ? pArea : g_SceneMan.GetScene()->GetArea("Landing Zone"); - // If area is defined, save a copy so we can lock the LZ selection to within its boxes - if (pArea && !pArea->HasNoArea()) - SetLZArea(team, *pArea); - } - - // If this is a CPU controlled team, then try to find a brain for them, or place one - if (team == m_CPUTeam && !m_pCPUBrain) - { -// TODO: Make special CPU brain actor which will only appear in CPU brain fights, and have to be placed in the scenes - m_pCPUBrain = g_MovableMan.GetUnassignedBrain(team); - m_pCPUBrain = m_pCPUBrain ? m_pCPUBrain : g_MovableMan.GetFirstBrainActor(team); // Try getting our assigned brain - if (!m_pCPUBrain) - { - // Couldn't find an available brain in the scene, so make one and place it - m_pCPUBrain = dynamic_cast(g_PresetMan.GetEntityPreset("Actor", "Brain Case")->Clone()); - if (m_pCPUBrain) - { - Vector brainPos; - // Place opposite of the other team's brain - Actor *pOtherBrain = g_MovableMan.GetFirstOtherBrainActor(team); - if (pOtherBrain) - { - brainPos = pOtherBrain->GetPos(); - brainPos.m_X += g_SceneMan.GetSceneWidth() / 2; - brainPos.m_Y = g_SceneMan.GetSceneHeight() - 200; - g_SceneMan.WrapPosition(brainPos); - } - // No brain found on other team... then just place somewhere in the ground, spaced out - else - { - if (team == Teams::TeamOne) - brainPos.SetXY((float)g_SceneMan.GetSceneWidth() * 0.25, (float)g_SceneMan.GetSceneHeight() * 0.75); - if (team == Teams::TeamTwo) - brainPos.SetXY((float)g_SceneMan.GetSceneWidth() * 0.75, (float)g_SceneMan.GetSceneHeight() * 0.75); - } - m_pCPUBrain->SetPos(brainPos); - m_pCPUBrain->SetTeam(team); - // Transfer ownership here - g_MovableMan.AddActor(m_pCPUBrain); - pOtherBrain = 0; - } - } - m_EnemyCount = g_MovableMan.GetTeamRoster(m_CPUTeam)->size(); - } - // Give the player some scratch - else - m_TeamFunds[team] = this->GetStartingGold(); - } - - /////////////////////////////////////// - // Set up players - - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - // Expand all modules to show the goods - m_pBuyGUI[player]->SetModuleExpanded(0); - - // No need for the LZ to follow the brain, the scene-set one is enough - SetBrainLZWidth(player, 0); - - // If we can't find an unassigned brain in the scene to give each player, then force to go into editing mode to place one - if (!m_Brain[player]) - { - g_ConsoleMan.PrintString("ERROR: Can't find brain for tutorial game mode!"); - } - // Set the found brain to be the selected actor at start - else - { - m_TutorialPlayer = player; - } -/* - if (m_ActivityState == ActivityState::Editing) - g_FrameMan.SetScreenText((player % 2 == 0) ? "Place your brain vault and build your bunker around it..." : "...then select \"DONE\" from the pie menu!", ScreenOfPlayer(player), 0); - else if (m_ActivityState == ActivityState::Running) - g_FrameMan.SetScreenText((player % 2 == 0) ? "Mine Gold and buy more firepower with the funds..." : "...then smash the competing brain to claim victory!", ScreenOfPlayer(player), 0); -*/ - } - - - ///////////////////////////////////////////// - // SET UP TUTORIAL - - // COMMON SCREENS - std::vector apScreens = ContentFile("Missions.rte/Objects/Tutorial/ScreenStatic.png").GetAsAnimation(3); - m_apCommonScreens[SCREENOFF] = apScreens[0]; - m_apCommonScreens[STATICLITTLE] = apScreens[1]; - m_apCommonScreens[STATICLARGE] = apScreens[2]; - - // ROOM SIGNS - ContentFile signFile; - for (int room = 0; room < ROOMCOUNT; ++room) - { - if (room == ROOM0) - signFile.SetDataPath("Missions.rte/Objects/Tutorial/TutEntryA.png"); - else if (room == ROOM1) - signFile.SetDataPath("Missions.rte/Objects/Tutorial/TutEntryB.png"); - else if (room == ROOM2) - signFile.SetDataPath("Missions.rte/Objects/Tutorial/TutEntryC.png"); - else if (room == ROOM3) - signFile.SetDataPath("Missions.rte/Objects/Tutorial/TutEntryD.png"); - std::vector apSigns = signFile.GetAsAnimation(2); - m_aapRoomSigns[room][UNLIT] = apSigns[0]; - m_aapRoomSigns[room][LIT] = apSigns[1]; - } - - m_RoomSignPositions[ROOM0].SetXY(744, 695); - m_RoomSignPositions[ROOM1].SetXY(888, 695); - m_RoomSignPositions[ROOM2].SetXY(888, 599); - m_RoomSignPositions[ROOM3].SetXY(888, 431); - m_CurrentRoom = ROOM3; - - ////////////////////////////////// - // Setup all the Tutorial Areas and their text instructions etc - - SetupAreas(); - - // Disable all enemy AIs so they dont attack prematurely - DisableAIs(true, Teams::TeamTwo); - - ////////////////////////////////// - // FIGHT TRIGGERS - - m_FightTriggers[DEFENDING].Create(Vector(1330, 0), 770, 400); - -// m_FightTriggerEast.Create(Vector(526, 0), 52, 840); -// m_FightTriggerWest.Create(Vector(1336, 0), 52, 840); -/* - // Start special tutorial playlist - g_AudioMan.ClearMusicQueue(); - g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/ccambient4.ogg", 0); - g_AudioMan.QueueSilence(30); - g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/cc2g.ogg"); - g_AudioMan.QueueSilence(30); - g_AudioMan.QueueMusicStream("Base.rte/Music/Watts/Last Man.ogg"); - g_AudioMan.QueueSilence(30); - g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/cc2g.ogg"); -*/ - return error; -} + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateEditing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: This is a special update step for when any player is still editing the + // scene. + void GATutorial::UpdateEditing() + { + GameActivity::UpdateEditing(); + } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this GATutorial. Supposed to be done every frame + // before drawing. + + void GATutorial::Update() { + // Avoid game logic when we're editing + if (m_ActivityState == ActivityState::Editing) { + UpdateEditing(); + return; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. + GameActivity::Update(); + + // Clear all objective markers, they get re-added each frame + ClearObjectivePoints(); + + /////////////////////////////////////////// + // Iterate through all human players + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + // The current player's team + int team = m_Team[player]; + if (team == Teams::NoTeam) + continue; + + // Make sure the game is not already ending + if (m_ActivityState != ActivityState::Over) { + // Check if any player's brain is dead + if (!g_MovableMan.IsActor(m_Brain[player])) { + m_Brain[player] = 0; + g_FrameMan.SetScreenText("Your brain has been destroyed!", ScreenOfPlayer(player), 333); + + // Now see if all brains are dead of this player's team, and if so, end the game + if (!g_MovableMan.GetFirstBrainActor(team)) { + m_WinnerTeam = OtherTeam(team); + End(); + } + + m_MessageTimer[player].Reset(); + } + // Mark the player brain to be protected when the fight happens + else if (m_CurrentFightStage >= DEFENDING) { + // Update the observation target to the brain, so that if/when it dies, the view flies to it in observation mode + // SetObservationTarget(m_Brain[player]->GetPos(), player); + // Mark the player's brain to be protected by his team + AddObjectivePoint("Protect!", m_Brain[player]->GetPos() + Vector(0, 10), team, GameActivity::ARROWUP); + // Mark the CPU brain for desctruction too + if (g_MovableMan.IsActor(m_pCPUBrain)) + AddObjectivePoint("Destroy!", m_pCPUBrain->GetPos() + Vector(0, 12), team, GameActivity::ARROWUP); + } + } + // Game over, show the appropriate messages until a certain time + else if (!m_GameOverTimer.IsPastSimMS(m_GameOverPeriod)) { + // TODO: make more appropriate messages here for run out of funds endings + if (m_Team[player] == m_WinnerTeam) + g_FrameMan.SetScreenText("You destroyed the dummy CPU!\nPress [SPACE] or [START] to continue", ScreenOfPlayer(player)); + else + g_FrameMan.SetScreenText("Your brain has been destroyed!", ScreenOfPlayer(player)); + } + } -void GATutorial::SetPaused(bool pause) -{ - GameActivity::SetPaused(pause); + /////////////////////////////////////////// + // Iterate through all teams + + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; + ///////////////////////////////////////// + // Attacking CPU team logic + /* + if (team == m_CPUTeam) + { + // Spawn the CPU team's attacking forces + if (m_SpawnTimer.IsPastSimMS(m_SpawnInterval) && m_ActivityState != ActivityState::Over) + { + if (!m_AttackerSpawns.empty()) + { + int whichSpawn = std::floor(m_AttackerSpawns.size() * RandomNum()); + Actor *pSpawn = dynamic_cast(m_AttackerSpawns[whichSpawn]->Clone()); + if (pSpawn) + { + Vector landingZone; + Actor *pEnemyBrain = g_MovableMan.GetFirstOtherBrainActor(team); + + // Don't land right on top of player team's base + if (pEnemyBrain) + { + // Get player team's base pos + landingZone = pEnemyBrain->GetPos(); + // Get the opposite side + landingZone.m_X += g_SceneMan.GetSceneWidth() / 2; + // Now give the zone width + landingZone.m_X += (g_SceneMan.GetSceneWidth() / 2) * 0.75 * NormalRand(); + // Wrap + g_SceneMan.WrapPosition(landingZone); + } + else + { + landingZone.m_X = g_SceneMan.GetSceneWidth() * RandomNum(); + } + Vector dropStart(landingZone.m_X, -50); + pSpawn->SetPos(dropStart); + pSpawn->SetTeam(team); + pSpawn->SetControllerMode(Controller::CIM_AI); + // Let the spawn into the world, passing ownership + g_MovableMan.AddActor(pSpawn); + pSpawn = 0; + } + } + m_SpawnTimer.Reset(); + } + } + */ + /////////////////////////////////////////// + // Check for victory conditions + + // Make sure the game is not already ending + if (m_ActivityState != ActivityState::Over && team != m_CPUTeam) { + // TODO: Gotto have budget restrictions in this activity! + /* + // Check for bankruptcy + // TODO Don't hardcode the rocket cost! + if (m_TeamFunds[team] < 0)//&& Only brain is left of actors) + { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) + { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + if (m_Team[player] == team) + g_FrameMan.SetScreenText("Your team can't afford any more transportation!", ScreenOfPlayer(player)); + else + { + g_FrameMan.SetScreenText("Your competition is bankrupt!", ScreenOfPlayer(player)); + m_WinnerTeam = m_Team[player]; + } + m_MessageTimer[player].Reset(); + } + End(); + } + */ + } + } - // Re-setup the ares with any updated control mappings that the player might have made in teh menu - if (!pause) - SetupAreas(); -} + /////////////////////////////////////////// + // TUTORIAL LOGIC + + // Detect the player going into new areas + if (m_ControlledActor[m_TutorialPlayer]) { + for (int area = 0; area < AREACOUNT; ++area) { + // Switch if within the trigger box of a new area + if (area != m_CurrentArea && m_TriggerBoxes[area].IsWithinBox(m_ControlledActor[m_TutorialPlayer]->GetPos())) { + // Change to the new area + m_PrevArea = m_CurrentArea; + m_CurrentArea = (TutorialArea)area; + m_CurrentStep = 0; + m_ScreenChange = true; + m_AreaTimer.Reset(); + m_StepTimer.Reset(); + } + } + /* + // Fight stage triggers + for (int stage = 0; stage < FIGHTSTAGECOUNT; ++stage) + { + + // Switch if within the trigger box of a new area + if (area != m_CurrentArea && m_TriggerBoxes[area].IsWithinBox(m_ControlledActor[m_TutorialPlayer]->GetPos())) + { + + if (m_FightTriggers[stage].Reset(); + } + */ + } + // Cycle through the steps of the current area + if (m_StepTimer.IsPastRealMS(m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Duration)) { + // Go to next step, looping around to the first if necessary + if (++m_CurrentStep == m_TutAreaSteps[m_CurrentArea].size()) + m_CurrentStep = 0; + m_ScreenChange = true; + // Start timing the new step + m_StepTimer.Reset(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. - -void GATutorial::End() -{ - GameActivity::End(); - - bool playerWon = false; - // Show appropriate end game messages - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - if (m_Team[player] == m_WinnerTeam) - { - playerWon = true; - // Set the winner's observation view to his controlled actors instead of his brain - if (m_ControlledActor[player] && g_MovableMan.IsActor(m_ControlledActor[player])) - m_ObservationTarget[player] = m_ControlledActor[player]->GetPos(); - } - } - - // Temp fix so music doesn't start playing if ending the Activity when changing resolution through the in-game settings. - if (!m_Paused) { - // Play the appropriate tune on player win/lose - if (playerWon) { - g_AudioMan.ClearMusicQueue(); - // Loop it twice, nice tune! - g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/uwinfinal.ogg", 2); - g_AudioMan.QueueSilence(10); - g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg"); - } else { - g_AudioMan.ClearMusicQueue(); - g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/udiedfinal.ogg", 0); - g_AudioMan.QueueSilence(10); - g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg"); + // Only mess with animation if there are more than one frame for this step and the frameduration is set to something + int frameCount = m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_pScreens.size(); + if (frameCount > 0 && m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_FrameDuration > 0) { + int newFrame = ((long)m_StepTimer.GetElapsedRealTimeMS() / m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_FrameDuration) % frameCount; + // Clamp the frame + newFrame = newFrame < 0 ? 0 : (newFrame >= frameCount ? frameCount - 1 : newFrame); + if (newFrame != m_CurrentFrame) { + m_CurrentFrame = newFrame; + m_ScreenChange = true; + } } - } -} -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateEditing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: This is a special update step for when any player is still editing the -// scene. + /* Draw this manually over the current screen in DrawGUI + // Take over control of screen messages + m_MessageTimer[m_TutorialPlayer].Reset(); + // Display the text of the current step + // g_FrameMan.ClearScreenText(); + g_FrameMan.SetScreenText(m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Text, 0, 500, -1, true);//, m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Duration); + */ + // Draw the correct current screens + BITMAP* pScreen = 0; + + // Turn ON the screen of the CURRENT area, animating the static-y frames and drawing them to the scene + // Then show the current step image + if (m_ScreenStates[m_CurrentArea] != SHOWINGSTEP || m_ScreenChange) { + // Figure out if to draw static or the step screen + m_ScreenStates[m_CurrentArea] = m_AreaTimer.IsPastRealMS(200) ? SHOWINGSTEP : (m_AreaTimer.IsPastRealMS(100) ? STATICLARGE : STATICLITTLE); + + pScreen = 0; + // Showing step image or a static screen? + if (m_ScreenStates[m_CurrentArea] == SHOWINGSTEP) { + if (m_CurrentFrame < frameCount) + pScreen = m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_pScreens[m_CurrentFrame]; + } else + pScreen = m_apCommonScreens[(int)(m_ScreenStates[m_CurrentArea])]; + + // Draw to the scene bg layer + if (pScreen) + blit(pScreen, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_ScreenPositions[m_CurrentArea].GetFloorIntX(), m_ScreenPositions[m_CurrentArea].GetFloorIntY(), pScreen->w, pScreen->h); + + m_ScreenChange = false; + } -void GATutorial::UpdateEditing() -{ - GameActivity::UpdateEditing(); -} -*/ + // Turn OFF the screen of all the other areas, animating the static-y frames and drawing them to the scene + for (int area = 0; area < AREACOUNT; ++area) { + pScreen = 0; + // Turn off all other areas + if (area != m_CurrentArea && m_ScreenStates[area] != SCREENOFF) { + m_ScreenStates[area] = m_AreaTimer.IsPastRealMS(200) ? SCREENOFF : (m_AreaTimer.IsPastRealMS(100) ? STATICLITTLE : STATICLARGE); + pScreen = m_apCommonScreens[(int)(m_ScreenStates[area])]; + if (pScreen) + blit(pScreen, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_ScreenPositions[area].GetFloorIntX(), m_ScreenPositions[area].GetFloorIntY(), pScreen->w, pScreen->h); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this GATutorial. Supposed to be done every frame -// before drawing. - -void GATutorial::Update() -{ - // Avoid game logic when we're editing - if (m_ActivityState == ActivityState::Editing) - { - UpdateEditing(); - return; - } - - GameActivity::Update(); - - // Clear all objective markers, they get re-added each frame - ClearObjectivePoints(); - - /////////////////////////////////////////// - // Iterate through all human players - - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - // The current player's team - int team = m_Team[player]; - if (team == Teams::NoTeam) - continue; - - // Make sure the game is not already ending - if (m_ActivityState != ActivityState::Over) - { - // Check if any player's brain is dead - if (!g_MovableMan.IsActor(m_Brain[player])) - { - m_Brain[player] = 0; - g_FrameMan.SetScreenText("Your brain has been destroyed!", ScreenOfPlayer(player), 333); - - // Now see if all brains are dead of this player's team, and if so, end the game - if (!g_MovableMan.GetFirstBrainActor(team)) - { - m_WinnerTeam = OtherTeam(team); - End(); - } - - m_MessageTimer[player].Reset(); - } - // Mark the player brain to be protected when the fight happens - else if (m_CurrentFightStage >= DEFENDING) - { - // Update the observation target to the brain, so that if/when it dies, the view flies to it in observation mode -// SetObservationTarget(m_Brain[player]->GetPos(), player); - // Mark the player's brain to be protected by his team - AddObjectivePoint("Protect!", m_Brain[player]->GetPos() + Vector(0, 10), team, GameActivity::ARROWUP); - // Mark the CPU brain for desctruction too - if (g_MovableMan.IsActor(m_pCPUBrain)) - AddObjectivePoint("Destroy!", m_pCPUBrain->GetPos() + Vector(0, 12), team, GameActivity::ARROWUP); - } - } - // Game over, show the appropriate messages until a certain time - else if (!m_GameOverTimer.IsPastSimMS(m_GameOverPeriod)) - { -// TODO: make more appropriate messages here for run out of funds endings - if (m_Team[player] == m_WinnerTeam) - g_FrameMan.SetScreenText("You destroyed the dummy CPU!\nPress [SPACE] or [START] to continue", ScreenOfPlayer(player)); - else - g_FrameMan.SetScreenText("Your brain has been destroyed!", ScreenOfPlayer(player)); - } - } - - /////////////////////////////////////////// - // Iterate through all teams - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - ///////////////////////////////////////// - // Attacking CPU team logic -/* - if (team == m_CPUTeam) - { - // Spawn the CPU team's attacking forces - if (m_SpawnTimer.IsPastSimMS(m_SpawnInterval) && m_ActivityState != ActivityState::Over) - { - if (!m_AttackerSpawns.empty()) - { - int whichSpawn = std::floor(m_AttackerSpawns.size() * RandomNum()); - Actor *pSpawn = dynamic_cast(m_AttackerSpawns[whichSpawn]->Clone()); - if (pSpawn) - { - Vector landingZone; - Actor *pEnemyBrain = g_MovableMan.GetFirstOtherBrainActor(team); - - // Don't land right on top of player team's base - if (pEnemyBrain) - { - // Get player team's base pos - landingZone = pEnemyBrain->GetPos(); - // Get the opposite side - landingZone.m_X += g_SceneMan.GetSceneWidth() / 2; - // Now give the zone width - landingZone.m_X += (g_SceneMan.GetSceneWidth() / 2) * 0.75 * NormalRand(); - // Wrap - g_SceneMan.WrapPosition(landingZone); - } - else - { - landingZone.m_X = g_SceneMan.GetSceneWidth() * RandomNum(); - } - Vector dropStart(landingZone.m_X, -50); - pSpawn->SetPos(dropStart); - pSpawn->SetTeam(team); - pSpawn->SetControllerMode(Controller::CIM_AI); - // Let the spawn into the world, passing ownership - g_MovableMan.AddActor(pSpawn); - pSpawn = 0; - } - } - m_SpawnTimer.Reset(); - } - } -*/ - /////////////////////////////////////////// - // Check for victory conditions - - // Make sure the game is not already ending - if (m_ActivityState != ActivityState::Over && team != m_CPUTeam) - { -// TODO: Gotto have budget restrictions in this activity! -/* - // Check for bankruptcy -// TODO Don't hardcode the rocket cost! - if (m_TeamFunds[team] < 0)//&& Only brain is left of actors) - { - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - if (m_Team[player] == team) - g_FrameMan.SetScreenText("Your team can't afford any more transportation!", ScreenOfPlayer(player)); - else - { - g_FrameMan.SetScreenText("Your competition is bankrupt!", ScreenOfPlayer(player)); - m_WinnerTeam = m_Team[player]; - } - m_MessageTimer[player].Reset(); - } - End(); - } -*/ - } - } - - /////////////////////////////////////////// - // TUTORIAL LOGIC - - // Detect the player going into new areas - if (m_ControlledActor[m_TutorialPlayer]) - { - for (int area = 0; area < AREACOUNT; ++area) - { - // Switch if within the trigger box of a new area - if (area != m_CurrentArea && m_TriggerBoxes[area].IsWithinBox(m_ControlledActor[m_TutorialPlayer]->GetPos())) - { - // Change to the new area - m_PrevArea = m_CurrentArea; - m_CurrentArea = (TutorialArea)area; - m_CurrentStep = 0; - m_ScreenChange = true; - m_AreaTimer.Reset(); - m_StepTimer.Reset(); - } - } -/* - // Fight stage triggers - for (int stage = 0; stage < FIGHTSTAGECOUNT; ++stage) - { - - // Switch if within the trigger box of a new area - if (area != m_CurrentArea && m_TriggerBoxes[area].IsWithinBox(m_ControlledActor[m_TutorialPlayer]->GetPos())) - { - - if (m_FightTriggers[stage].Reset(); - } -*/ - } - - // Cycle through the steps of the current area - if (m_StepTimer.IsPastRealMS(m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Duration)) - { - // Go to next step, looping around to the first if necessary - if (++m_CurrentStep == m_TutAreaSteps[m_CurrentArea].size()) - m_CurrentStep = 0; - m_ScreenChange = true; - // Start timing the new step - m_StepTimer.Reset(); - } - - // Only mess with animation if there are more than one frame for this step and the frameduration is set to something - int frameCount = m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_pScreens.size(); - if (frameCount > 0 && m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_FrameDuration > 0) - { - int newFrame = ((long)m_StepTimer.GetElapsedRealTimeMS() / m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_FrameDuration) % frameCount; - // Clamp the frame - newFrame = newFrame < 0 ? 0 : (newFrame >= frameCount ? frameCount - 1 : newFrame); - if (newFrame != m_CurrentFrame) - { - m_CurrentFrame = newFrame; - m_ScreenChange = true; - } - } - -/* Draw this manually over the current screen in DrawGUI - // Take over control of screen messages - m_MessageTimer[m_TutorialPlayer].Reset(); - // Display the text of the current step -// g_FrameMan.ClearScreenText(); - g_FrameMan.SetScreenText(m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Text, 0, 500, -1, true);//, m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Duration); -*/ - // Draw the correct current screens - BITMAP *pScreen = 0; - - // Turn ON the screen of the CURRENT area, animating the static-y frames and drawing them to the scene - // Then show the current step image - if (m_ScreenStates[m_CurrentArea] != SHOWINGSTEP || m_ScreenChange) - { - // Figure out if to draw static or the step screen - m_ScreenStates[m_CurrentArea] = m_AreaTimer.IsPastRealMS(200) ? SHOWINGSTEP : (m_AreaTimer.IsPastRealMS(100) ? STATICLARGE : STATICLITTLE); - - pScreen = 0; - // Showing step image or a static screen? - if (m_ScreenStates[m_CurrentArea] == SHOWINGSTEP) - { - if (m_CurrentFrame < frameCount) - pScreen = m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_pScreens[m_CurrentFrame]; - } - else - pScreen = m_apCommonScreens[(int)(m_ScreenStates[m_CurrentArea])]; - - // Draw to the scene bg layer - if (pScreen) - blit(pScreen, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_ScreenPositions[m_CurrentArea].GetFloorIntX(), m_ScreenPositions[m_CurrentArea].GetFloorIntY(), pScreen->w, pScreen->h); - - m_ScreenChange = false; - } - - // Turn OFF the screen of all the other areas, animating the static-y frames and drawing them to the scene - for (int area = 0; area < AREACOUNT; ++area) - { - pScreen = 0; - // Turn off all other areas - if (area != m_CurrentArea && m_ScreenStates[area] != SCREENOFF) - { - m_ScreenStates[area] = m_AreaTimer.IsPastRealMS(200) ? SCREENOFF : (m_AreaTimer.IsPastRealMS(100) ? STATICLITTLE : STATICLARGE); - pScreen = m_apCommonScreens[(int)(m_ScreenStates[area])]; - if (pScreen) - blit(pScreen, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_ScreenPositions[area].GetFloorIntX(), m_ScreenPositions[area].GetFloorIntY(), pScreen->w, pScreen->h); - } - } - - //////////////////////// - // ROOM SIGNS - - // Translate current area to a room - TutorialRoom prevRoom = m_CurrentRoom; - if (m_CurrentArea == BRAINCHAMBER) - m_CurrentRoom = ROOM0; - else if (m_CurrentArea == BODYSTORAGE) - m_CurrentRoom = ROOM1; - else if (m_CurrentArea == OBSTACLECOURSE) - m_CurrentRoom = ROOM2; - else if (m_CurrentArea == FIRINGRANGE) - m_CurrentRoom = ROOM3; - - // Draw the correct currently lit or blinking signs - BITMAP *pSign = 0; - if (prevRoom != m_CurrentRoom) - { - pSign = m_aapRoomSigns[ROOM0][m_CurrentRoom >= ROOM0 ? LIT : UNLIT]; - blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[ROOM0].GetFloorIntX(), m_RoomSignPositions[ROOM0].GetFloorIntY(), pSign->w, pSign->h); - pSign = m_aapRoomSigns[ROOM1][m_CurrentRoom >= ROOM1 ? LIT : UNLIT]; - blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[ROOM1].GetFloorIntX(), m_RoomSignPositions[ROOM1].GetFloorIntY(), pSign->w, pSign->h); - pSign = m_aapRoomSigns[ROOM2][m_CurrentRoom >= ROOM2 ? LIT : UNLIT]; - blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[ROOM2].GetFloorIntX(), m_RoomSignPositions[ROOM2].GetFloorIntY(), pSign->w, pSign->h); - pSign = m_aapRoomSigns[ROOM3][m_CurrentRoom >= ROOM3 ? LIT : UNLIT]; - blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[ROOM3].GetFloorIntX(), m_RoomSignPositions[ROOM3].GetFloorIntY(), pSign->w, pSign->h); - } - // Blink the next room's sign - if (m_CurrentRoom < ROOM3) - { - pSign = m_aapRoomSigns[m_CurrentRoom + 1][m_AreaTimer.AlternateReal(200) ? LIT : UNLIT]; - blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[m_CurrentRoom + 1].GetFloorIntX(), m_RoomSignPositions[m_CurrentRoom + 1].GetFloorIntY(), pSign->w, pSign->h); - } - - //////////////////////// - // FIGHT LOGIC - - // Triggered defending stage - if (m_CurrentFightStage == NOFIGHT && ((m_ControlledActor[m_TutorialPlayer] && m_FightTriggers[DEFENDING].IsWithinBox(m_ControlledActor[m_TutorialPlayer]->GetPos())) || g_MovableMan.GetTeamRoster(m_CPUTeam)->size() < m_EnemyCount)) { - // Take over control of screen messages - m_MessageTimer[m_TutorialPlayer].Reset(); - // Display the text of the current step - g_FrameMan.ClearScreenText(ScreenOfPlayer(m_TutorialPlayer)); - g_FrameMan.SetScreenText("DEFEND YOUR BRAIN AGAINST THE INCOMING FORCES!", ScreenOfPlayer(m_TutorialPlayer), 500, 8000, true); - // This will make all the enemy team AI's go into brain hunt mode - GameActivity::InitAIs(); - DisableAIs(false, Teams::TeamTwo); - - // Advance the stage - m_CurrentFightStage = DEFENDING; - } - - /////////////////////////////////////////// - // Check for victory conditions - - // Check if the CPU brain is dead, if we're playing against the CPU - if (!g_MovableMan.IsActor(m_pCPUBrain) && m_ActivityState != ActivityState::Over) - { - m_pCPUBrain = 0; - // Proclaim player winner and end - m_WinnerTeam = Teams::TeamOne; - // Finito! - End(); - } - - // After a while of game over and we won, exit to the campaign menu automatically - if (m_ActivityState == ActivityState::Over && m_WinnerTeam == Teams::TeamOne) - { - if (m_GameOverTimer.IsPastSimMS(m_GameOverPeriod)) - { - g_FrameMan.ClearScreenText(ScreenOfPlayer(m_TutorialPlayer)); - g_FrameMan.SetScreenText("Press [SPACE] or [START] to continue!", ScreenOfPlayer(m_TutorialPlayer), 750); - } - - if (m_GameOverTimer.IsPastSimMS(54000) || g_UInputMan.AnyStartPress()) - { - g_ActivityMan.EndActivity(); - g_ActivityMan.SetInActivity(false); - } - } -} + //////////////////////// + // ROOM SIGNS + + // Translate current area to a room + TutorialRoom prevRoom = m_CurrentRoom; + if (m_CurrentArea == BRAINCHAMBER) + m_CurrentRoom = ROOM0; + else if (m_CurrentArea == BODYSTORAGE) + m_CurrentRoom = ROOM1; + else if (m_CurrentArea == OBSTACLECOURSE) + m_CurrentRoom = ROOM2; + else if (m_CurrentArea == FIRINGRANGE) + m_CurrentRoom = ROOM3; + + // Draw the correct currently lit or blinking signs + BITMAP* pSign = 0; + if (prevRoom != m_CurrentRoom) { + pSign = m_aapRoomSigns[ROOM0][m_CurrentRoom >= ROOM0 ? LIT : UNLIT]; + blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[ROOM0].GetFloorIntX(), m_RoomSignPositions[ROOM0].GetFloorIntY(), pSign->w, pSign->h); + pSign = m_aapRoomSigns[ROOM1][m_CurrentRoom >= ROOM1 ? LIT : UNLIT]; + blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[ROOM1].GetFloorIntX(), m_RoomSignPositions[ROOM1].GetFloorIntY(), pSign->w, pSign->h); + pSign = m_aapRoomSigns[ROOM2][m_CurrentRoom >= ROOM2 ? LIT : UNLIT]; + blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[ROOM2].GetFloorIntX(), m_RoomSignPositions[ROOM2].GetFloorIntY(), pSign->w, pSign->h); + pSign = m_aapRoomSigns[ROOM3][m_CurrentRoom >= ROOM3 ? LIT : UNLIT]; + blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[ROOM3].GetFloorIntX(), m_RoomSignPositions[ROOM3].GetFloorIntY(), pSign->w, pSign->h); + } + // Blink the next room's sign + if (m_CurrentRoom < ROOM3) { + pSign = m_aapRoomSigns[m_CurrentRoom + 1][m_AreaTimer.AlternateReal(200) ? LIT : UNLIT]; + blit(pSign, g_SceneMan.GetTerrain()->GetBGColorBitmap(), 0, 0, m_RoomSignPositions[m_CurrentRoom + 1].GetFloorIntX(), m_RoomSignPositions[m_CurrentRoom + 1].GetFloorIntY(), pSign->w, pSign->h); + } + //////////////////////// + // FIGHT LOGIC + + // Triggered defending stage + if (m_CurrentFightStage == NOFIGHT && ((m_ControlledActor[m_TutorialPlayer] && m_FightTriggers[DEFENDING].IsWithinBox(m_ControlledActor[m_TutorialPlayer]->GetPos())) || g_MovableMan.GetTeamRoster(m_CPUTeam)->size() < m_EnemyCount)) { + // Take over control of screen messages + m_MessageTimer[m_TutorialPlayer].Reset(); + // Display the text of the current step + g_FrameMan.ClearScreenText(ScreenOfPlayer(m_TutorialPlayer)); + g_FrameMan.SetScreenText("DEFEND YOUR BRAIN AGAINST THE INCOMING FORCES!", ScreenOfPlayer(m_TutorialPlayer), 500, 8000, true); + // This will make all the enemy team AI's go into brain hunt mode + GameActivity::InitAIs(); + DisableAIs(false, Teams::TeamTwo); + + // Advance the stage + m_CurrentFightStage = DEFENDING; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. - -void GATutorial::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) -{ - GameActivity::DrawGUI(pTargetBitmap, targetPos, which); - - if (IsRunning())// && (m_AreaTimer.AlternateReal(500) || m_AreaTimer.AlternateReal(250) || m_AreaTimer.AlternateReal(125))) - { - AllegroBitmap pBitmapInt(pTargetBitmap); - Vector screenTextPos = m_ScreenPositions[m_CurrentArea] + m_TextOffsets[m_CurrentArea] - targetPos; - // How long the revealing of the text period will be, clamped, and plus three for the last three dots added later - float revealPeriod = (m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Text.size() + 3) * 30; - if (revealPeriod > m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Duration * 0.85) - revealPeriod = m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Duration * 0.85; - // The normalized reveal control - float revealed = m_StepTimer.GetElapsedRealTimeMS() / revealPeriod; - if (revealed > 1.0) - revealed = 1.0; - std::string revealText = m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Text.substr(0, (m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Text.size() + 3) * revealed); - // Dot blinking logic - if (revealed == 1.0) - { - int timePhase = (int)m_AreaTimer.GetElapsedRealTimeMS() % 1200; - revealText = revealText + (timePhase > 900 ? "..." : (timePhase > 600 ? ".. " : (timePhase > 300 ? ". " : " "))); - } - g_FrameMan.GetSmallFont()->DrawAligned(&pBitmapInt, screenTextPos.m_X, screenTextPos.m_Y, revealText.c_str(), GUIFont::Centre); - } -} + /////////////////////////////////////////// + // Check for victory conditions + + // Check if the CPU brain is dead, if we're playing against the CPU + if (!g_MovableMan.IsActor(m_pCPUBrain) && m_ActivityState != ActivityState::Over) { + m_pCPUBrain = 0; + // Proclaim player winner and end + m_WinnerTeam = Teams::TeamOne; + // Finito! + End(); + } + // After a while of game over and we won, exit to the campaign menu automatically + if (m_ActivityState == ActivityState::Over && m_WinnerTeam == Teams::TeamOne) { + if (m_GameOverTimer.IsPastSimMS(m_GameOverPeriod)) { + g_FrameMan.ClearScreenText(ScreenOfPlayer(m_TutorialPlayer)); + g_FrameMan.SetScreenText("Press [SPACE] or [START] to continue!", ScreenOfPlayer(m_TutorialPlayer), 750); + } + + if (m_GameOverTimer.IsPastSimMS(54000) || g_UInputMan.AnyStartPress()) { + g_ActivityMan.EndActivity(); + g_ActivityMan.SetInActivity(false); + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this GATutorial's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + + void GATutorial::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + GameActivity::DrawGUI(pTargetBitmap, targetPos, which); + + if (IsRunning()) // && (m_AreaTimer.AlternateReal(500) || m_AreaTimer.AlternateReal(250) || m_AreaTimer.AlternateReal(125))) + { + AllegroBitmap pBitmapInt(pTargetBitmap); + Vector screenTextPos = m_ScreenPositions[m_CurrentArea] + m_TextOffsets[m_CurrentArea] - targetPos; + // How long the revealing of the text period will be, clamped, and plus three for the last three dots added later + float revealPeriod = (m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Text.size() + 3) * 30; + if (revealPeriod > m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Duration * 0.85) + revealPeriod = m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Duration * 0.85; + // The normalized reveal control + float revealed = m_StepTimer.GetElapsedRealTimeMS() / revealPeriod; + if (revealed > 1.0) + revealed = 1.0; + std::string revealText = m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Text.substr(0, (m_TutAreaSteps[m_CurrentArea][m_CurrentStep].m_Text.size() + 3) * revealed); + // Dot blinking logic + if (revealed == 1.0) { + int timePhase = (int)m_AreaTimer.GetElapsedRealTimeMS() % 1200; + revealText = revealText + (timePhase > 900 ? "..." : (timePhase > 600 ? ".. " : (timePhase > 300 ? ". " : " "))); + } + g_FrameMan.GetSmallFont()->DrawAligned(&pBitmapInt, screenTextPos.m_X, screenTextPos.m_Y, revealText.c_str(), GUIFont::Centre); + } + } -void GATutorial::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) -{ - GameActivity::Draw(pTargetBitmap, targetPos); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this GATutorial's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + void GATutorial::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + GameActivity::Draw(pTargetBitmap, targetPos); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: InitAIs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through all Actor:s currently in the MovableMan and gives each -// one not controlled by a Controller a CAI and appropriate AIMode setting -// based on team and CPU team. - -void GATutorial::InitAIs() -{ - Actor *pActor = 0; - Actor *pFirstActor = 0; - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - // Get the first one - pFirstActor = pActor = g_MovableMan.GetNextTeamActor(team); - - do - { - // Set up AI controller if currently not player controlled - if (pActor && !pActor->GetController()->IsPlayerControlled()) - { - pActor->SetControllerMode(Controller::CIM_AI); - - // If human, set appropriate AI mode - if (dynamic_cast(pActor) || dynamic_cast(pActor)) - { - // Sentry default - if (team == m_CPUTeam) - pActor->SetAIMode(AHuman::AIMODE_SENTRY); - else if (team >= 0) - pActor->SetAIMode(AHuman::AIMODE_SENTRY); - // Let the non team actors be (the wildlife) - else - ; - } - } - - // Next! - pActor = g_MovableMan.GetNextTeamActor(team, pActor); - } - while (pActor && pActor != pFirstActor); - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: InitAIs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through all Actor:s currently in the MovableMan and gives each + // one not controlled by a Controller a CAI and appropriate AIMode setting + // based on team and CPU team. + + void GATutorial::InitAIs() { + Actor* pActor = 0; + Actor* pFirstActor = 0; + + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; + // Get the first one + pFirstActor = pActor = g_MovableMan.GetNextTeamActor(team); + + do { + // Set up AI controller if currently not player controlled + if (pActor && !pActor->GetController()->IsPlayerControlled()) { + pActor->SetControllerMode(Controller::CIM_AI); + + // If human, set appropriate AI mode + if (dynamic_cast(pActor) || dynamic_cast(pActor)) { + // Sentry default + if (team == m_CPUTeam) + pActor->SetAIMode(AHuman::AIMODE_SENTRY); + else if (team >= 0) + pActor->SetAIMode(AHuman::AIMODE_SENTRY); + // Let the non team actors be (the wildlife) + else + ; + } + } + + // Next! + pActor = g_MovableMan.GetNextTeamActor(team, pActor); + } while (pActor && pActor != pFirstActor); + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetupAreas + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets up or resets the Tutorial Areas to show the current control + // mappings etc. + + void GATutorial::SetupAreas() { + int device = g_UInputMan.GetControlScheme(m_TutorialPlayer)->GetDevice(); + int preset = g_UInputMan.GetControlScheme(m_TutorialPlayer)->GetPreset(); + + // Adjust for special commands when using the keyboard-only setup + std::string JumpName = MAPNAME(INPUT_L_UP); + std::string CrouchName = MAPNAME(INPUT_L_DOWN); + if (device == DEVICE_KEYB_ONLY) { + JumpName = MAPNAME(INPUT_JUMP); + CrouchName = MAPNAME(INPUT_CROUCH); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetupAreas -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets up or resets the Tutorial Areas to show the current control -// mappings etc. - -void GATutorial::SetupAreas() -{ - int device = g_UInputMan.GetControlScheme(m_TutorialPlayer)->GetDevice(); - int preset = g_UInputMan.GetControlScheme(m_TutorialPlayer)->GetPreset(); - - // Adjust for special commands when using the keyboard-only setup - std::string JumpName = MAPNAME(INPUT_L_UP); - std::string CrouchName = MAPNAME(INPUT_L_DOWN); - if (device == DEVICE_KEYB_ONLY) - { - JumpName = MAPNAME(INPUT_JUMP); - CrouchName = MAPNAME(INPUT_CROUCH); - } - - // If no preset, adjust the pie menu and fire names when using the defaults on a gamepad.. otherwise it'll show up as an unhelpful "Joystick" - std::string PieName = MAPNAME(INPUT_PIEMENU_ANALOG); - if (PieName == "") - { - PieName = MAPNAME(INPUT_PIEMENU_DIGITAL); - } - - std::string FireName = MAPNAME(INPUT_FIRE); - if (device >= DEVICE_GAMEPAD_1 && preset == InputScheme::InputPreset::NoPreset) - { - PieName = "Pie Menu Trigger"; - FireName = "Fire Trigger"; - } - - // BRAINCHAMBER - // Set up the trigger area - m_TriggerBoxes[BRAINCHAMBER].SetCorner(Vector(631, 608)); - m_TriggerBoxes[BRAINCHAMBER].SetWidth(137); - m_TriggerBoxes[BRAINCHAMBER].SetHeight(155); - // Screen position - m_ScreenPositions[BRAINCHAMBER].SetXY(673, 688); - // Text offset from screen position - m_TextOffsets[BRAINCHAMBER].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); - // Set up the steps - m_TutAreaSteps[BRAINCHAMBER].clear(); - m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("Welcome to Cortex Command", 4000, "Missions.rte/Objects/Tutorial/CCLogo.png", 2)); - m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("Above is your disembodied brain", 4000, "Missions.rte/Objects/Tutorial/ArrowUp.png", 2)); - m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("With it, you can remotely control other bodies", 4000, "Missions.rte/Objects/Tutorial/BodyZap.png", 2)); - m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("Switch to one now by using [" + MAPNAME(INPUT_PREV) + "] or [" + MAPNAME(INPUT_NEXT) + "]", 4000, "Missions.rte/Objects/Tutorial/BodyZap.png", 2)); - m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("If you haven't set up your controls yet, hit [Esc] and do so", 8000, "Missions.rte/Objects/Tutorial/Joystick.png", 2)); - - // BODYSTORAGE - // Set up the trigger area - m_TriggerBoxes[BODYSTORAGE].SetCorner(Vector(911, 662)); - m_TriggerBoxes[BODYSTORAGE].SetWidth(397); - m_TriggerBoxes[BODYSTORAGE].SetHeight(98); - // Screen position - m_ScreenPositions[BODYSTORAGE].SetXY(961, 688); - // Text offset from screen position - m_TextOffsets[BODYSTORAGE].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); - // Set up the steps - m_TutAreaSteps[BODYSTORAGE].clear(); - m_TutAreaSteps[BODYSTORAGE].push_back(TutStep("Here are some dummy bodies for practice", 4000, "Missions.rte/Objects/Tutorial/BodyHop.png", 2)); - m_TutAreaSteps[BODYSTORAGE].push_back(TutStep("Quickly switch control between them left and right with [" + MAPNAME(INPUT_PREV) + "] and [" + MAPNAME(INPUT_NEXT) + "]", 6000, "Missions.rte/Objects/Tutorial/BodyZap.png", 2)); - m_TutAreaSteps[BODYSTORAGE].push_back(TutStep("Or hold down either [" + MAPNAME(INPUT_PREV) + "] or [" + MAPNAME(INPUT_NEXT) + "] to get a selection cursor", 6000, "Missions.rte/Objects/Tutorial/BodyZap.png", 2)); - m_TutAreaSteps[BODYSTORAGE].push_back(TutStep("Switch to the leftmost body and walk it out of the room with [" + MAPNAME(INPUT_L_LEFT) + "]", 8000, "Missions.rte/Objects/Tutorial/ArrowLeft.png", 2)); - - // SHAFT - // Set up the trigger area - m_TriggerBoxes[SHAFT].SetCorner(Vector(772, 385)); - m_TriggerBoxes[SHAFT].SetWidth(135); - m_TriggerBoxes[SHAFT].SetHeight(380); - // Screen position - m_ScreenPositions[SHAFT].SetXY(817, 688); - // Text offset from screen position - m_TextOffsets[SHAFT].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); - // Set up the steps - m_TutAreaSteps[SHAFT].clear(); - m_TutAreaSteps[SHAFT].push_back(TutStep("Use [" + JumpName + "] to activate jetpack", 4000, "Missions.rte/Objects/Tutorial/ArrowUp.png", 2)); - m_TutAreaSteps[SHAFT].push_back(TutStep("Fire jetpack in bursts for better control", 4000, "Missions.rte/Objects/Tutorial/BodyJetpack.png", 2)); - m_TutAreaSteps[SHAFT].push_back(TutStep("Adjust the jet direction by aiming or looking", 4000, "Missions.rte/Objects/Tutorial/BodyJetpack.png", 2)); - m_TutAreaSteps[SHAFT].push_back(TutStep("Jump height is affected by the body's total weight", 4000, "Missions.rte/Objects/Tutorial/BodyJetpack.png", 2)); - m_TutAreaSteps[SHAFT].push_back(TutStep("So the more you carry, the less you can jump", 8000, "Missions.rte/Objects/Tutorial/BodyJetpack.png", 2)); - - // OBSTACLECOURSE - // Set up the trigger area - m_TriggerBoxes[OBSTACLECOURSE].SetCorner(Vector(915, 492)); - m_TriggerBoxes[OBSTACLECOURSE].SetWidth(395); - m_TriggerBoxes[OBSTACLECOURSE].SetHeight(167); - // Screen position - m_ScreenPositions[OBSTACLECOURSE].SetXY(961, 592); - // Text offset from screen position - m_TextOffsets[OBSTACLECOURSE].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); - // Set up the steps - m_TutAreaSteps[OBSTACLECOURSE].clear(); - m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("Obstacle course", 2000, "Missions.rte/Objects/Tutorial/BodyHop.png", 2)); - m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("Climb obstacles by holding [" + MAPNAME(INPUT_L_RIGHT) + "]", 4000, "Missions.rte/Objects/Tutorial/ArrowRight.png", 2)); - m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("Even climb ladders by simply moving toward them", 8000, "Missions.rte/Objects/Tutorial/BodyClimb.png", 2)); - m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("You can also crouch and crawl with [" + CrouchName + "]", 6000, "Missions.rte/Objects/Tutorial/BodyCrawl.png", 2)); - m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("In tight spaces, you may need to angle your head down to get through", 8000, "Missions.rte/Objects/Tutorial/BodyCrawl.png", 2)); - - // FIRINGRANGE - // Set up the trigger area - m_TriggerBoxes[FIRINGRANGE].SetCorner(Vector(913, 394)); - m_TriggerBoxes[FIRINGRANGE].SetWidth(389); - m_TriggerBoxes[FIRINGRANGE].SetHeight(97); - // Screen position - m_ScreenPositions[FIRINGRANGE].SetXY(961, 424); - // Text offset from screen position - m_TextOffsets[FIRINGRANGE].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); - // Set up the steps - m_TutAreaSteps[FIRINGRANGE].clear(); - m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Firing range", 2000, "Missions.rte/Objects/Tutorial/FireTarget.png", 1)); - m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Pick up the weapon by first standing over it", 4000, "Missions.rte/Objects/Tutorial/ArrowRight.png", 2)); - m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("And then hold down [" + PieName + "]", 4000, "Missions.rte/Objects/Tutorial/MenuPickUp.png", 1, 500)); - m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Point up to 'Pick Up'", 4000, "Missions.rte/Objects/Tutorial/MenuPickUp.png", 2, 500)); - m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Release [" + PieName + "] to complete the command", 4000, "Missions.rte/Objects/Tutorial/MenuPickUp.png", 1)); - m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("If you aim continuously toward your target", 4000, "Missions.rte/Objects/Tutorial/BodyAim.png", 3)); - m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("It improves your accuracy and view distance", 4000, "Missions.rte/Objects/Tutorial/BodyAim.png", 3)); - m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Use [" + FireName + "] to Fire!", 4000, "Missions.rte/Objects/Tutorial/BodyFire.png", 2)); - m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Reload manually with [" + PieName + "] + up", 8000, "Missions.rte/Objects/Tutorial/BodyFire.png", 2)); - - // ROOFTOP - // Set up the trigger area - m_TriggerBoxes[ROOFTOP].SetCorner(Vector(732, 176)); - m_TriggerBoxes[ROOFTOP].SetWidth(356); - m_TriggerBoxes[ROOFTOP].SetHeight(210); - // Screen position - m_ScreenPositions[ROOFTOP].SetXY(961, 316); - // Text offset from screen position - m_TextOffsets[ROOFTOP].SetXY(m_apCommonScreens[0]->w / 2, -16); - // Set up the steps - m_TutAreaSteps[ROOFTOP].clear(); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Pick up the digging tool", 4000, "Missions.rte/Objects/Tutorial/MenuPickUp.png", 2, 500)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Use it on the dirt here", 8000, "Missions.rte/Objects/Tutorial/DigPile.png", 2, 750)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("If you dig up gold, it is added to your team's funds", 4000, "Missions.rte/Objects/Tutorial/Funds.png", 2, 250)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Funds can be spent in the Buy Menu", 4000, "Missions.rte/Objects/Tutorial/Funds.png", 1, 333)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Which is opened through the Command Menu", 4000, "Missions.rte/Objects/Tutorial/MenuBuyMenu.png", 1, 500)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Hold [" + PieName + "] and point up and left to 'Buy Menu'", 6000, "Missions.rte/Objects/Tutorial/MenuBuyMenu.png", 2, 500)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("The Buy Menu works like a shopping cart", 6000, "Missions.rte/Objects/Tutorial/BuyMenuCargo.png", 1, 500)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Add to the Cargo list the items you want delivered", 6000, "Missions.rte/Objects/Tutorial/BuyMenuCargo.png", 2, 500)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Then use the BUY button, or click outside the menu", 4000, "Missions.rte/Objects/Tutorial/BuyMenuBuy.png", 2, 500)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Finally select a flat area where you want the goods delivered", 8000, "Missions.rte/Objects/Tutorial/BuyMenuBuy.png", 1, 500)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Next, you can go explore to the west to try flying and climbing in the wild", 6000, "Missions.rte/Objects/Tutorial/ArrowLeft.png", 2)); - m_TutAreaSteps[ROOFTOP].push_back(TutStep("Or, go to the east to learn about squads!", 8000, "Missions.rte/Objects/Tutorial/ArrowRight.png", 2)); - - // ROOFEAST - // Set up the trigger area - m_TriggerBoxes[ROOFEAST].SetCorner(Vector(1100, 176)); - m_TriggerBoxes[ROOFEAST].SetWidth(200); - m_TriggerBoxes[ROOFEAST].SetHeight(210); - // Screen position - m_ScreenPositions[ROOFEAST].SetXY(1201, 316); - // Text offset from screen position - m_TextOffsets[ROOFEAST].SetXY(m_apCommonScreens[0]->w / 2, -16); - // Set up the steps - m_TutAreaSteps[ROOFEAST].clear(); - m_TutAreaSteps[ROOFEAST].push_back(TutStep("Hold [" + PieName + "] and point down and right to 'Form Squad'", 4000, "Missions.rte/Objects/Tutorial/MenuTeam.png", 2, 500)); - m_TutAreaSteps[ROOFEAST].push_back(TutStep("Adjust selection circle to select nearby bodies", 4000, "Missions.rte/Objects/Tutorial/TeamSelect.png", 4, 500)); - m_TutAreaSteps[ROOFEAST].push_back(TutStep("All selected units will follow you, and engage on their own", 4000, "Missions.rte/Objects/Tutorial/TeamFollow.png", 2, 500)); - m_TutAreaSteps[ROOFEAST].push_back(TutStep("Units with similar weapons will fire in unison with the leader", 4000, "Missions.rte/Objects/Tutorial/TeamFollow.png", 2, 500)); - m_TutAreaSteps[ROOFEAST].push_back(TutStep("Hold [" + PieName + "] and point down and right again to disband squad", 4000, "Missions.rte/Objects/Tutorial/MenuTeam.png", 2, 500)); - m_TutAreaSteps[ROOFEAST].push_back(TutStep("Next, you can head east for a TRIAL BATTLE!", 8000, "Missions.rte/Objects/Tutorial/ArrowRight.png", 2)); - - m_AreaTimer.Reset(); - m_StepTimer.Reset(); -} + // If no preset, adjust the pie menu and fire names when using the defaults on a gamepad.. otherwise it'll show up as an unhelpful "Joystick" + std::string PieName = MAPNAME(INPUT_PIEMENU_ANALOG); + if (PieName == "") { + PieName = MAPNAME(INPUT_PIEMENU_DIGITAL); + } + + std::string FireName = MAPNAME(INPUT_FIRE); + if (device >= DEVICE_GAMEPAD_1 && preset == InputScheme::InputPreset::NoPreset) { + PieName = "Pie Menu Trigger"; + FireName = "Fire Trigger"; + } + + // BRAINCHAMBER + // Set up the trigger area + m_TriggerBoxes[BRAINCHAMBER].SetCorner(Vector(631, 608)); + m_TriggerBoxes[BRAINCHAMBER].SetWidth(137); + m_TriggerBoxes[BRAINCHAMBER].SetHeight(155); + // Screen position + m_ScreenPositions[BRAINCHAMBER].SetXY(673, 688); + // Text offset from screen position + m_TextOffsets[BRAINCHAMBER].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); + // Set up the steps + m_TutAreaSteps[BRAINCHAMBER].clear(); + m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("Welcome to Cortex Command", 4000, "Missions.rte/Objects/Tutorial/CCLogo.png", 2)); + m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("Above is your disembodied brain", 4000, "Missions.rte/Objects/Tutorial/ArrowUp.png", 2)); + m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("With it, you can remotely control other bodies", 4000, "Missions.rte/Objects/Tutorial/BodyZap.png", 2)); + m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("Switch to one now by using [" + MAPNAME(INPUT_PREV) + "] or [" + MAPNAME(INPUT_NEXT) + "]", 4000, "Missions.rte/Objects/Tutorial/BodyZap.png", 2)); + m_TutAreaSteps[BRAINCHAMBER].push_back(TutStep("If you haven't set up your controls yet, hit [Esc] and do so", 8000, "Missions.rte/Objects/Tutorial/Joystick.png", 2)); + + // BODYSTORAGE + // Set up the trigger area + m_TriggerBoxes[BODYSTORAGE].SetCorner(Vector(911, 662)); + m_TriggerBoxes[BODYSTORAGE].SetWidth(397); + m_TriggerBoxes[BODYSTORAGE].SetHeight(98); + // Screen position + m_ScreenPositions[BODYSTORAGE].SetXY(961, 688); + // Text offset from screen position + m_TextOffsets[BODYSTORAGE].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); + // Set up the steps + m_TutAreaSteps[BODYSTORAGE].clear(); + m_TutAreaSteps[BODYSTORAGE].push_back(TutStep("Here are some dummy bodies for practice", 4000, "Missions.rte/Objects/Tutorial/BodyHop.png", 2)); + m_TutAreaSteps[BODYSTORAGE].push_back(TutStep("Quickly switch control between them left and right with [" + MAPNAME(INPUT_PREV) + "] and [" + MAPNAME(INPUT_NEXT) + "]", 6000, "Missions.rte/Objects/Tutorial/BodyZap.png", 2)); + m_TutAreaSteps[BODYSTORAGE].push_back(TutStep("Or hold down either [" + MAPNAME(INPUT_PREV) + "] or [" + MAPNAME(INPUT_NEXT) + "] to get a selection cursor", 6000, "Missions.rte/Objects/Tutorial/BodyZap.png", 2)); + m_TutAreaSteps[BODYSTORAGE].push_back(TutStep("Switch to the leftmost body and walk it out of the room with [" + MAPNAME(INPUT_L_LEFT) + "]", 8000, "Missions.rte/Objects/Tutorial/ArrowLeft.png", 2)); + + // SHAFT + // Set up the trigger area + m_TriggerBoxes[SHAFT].SetCorner(Vector(772, 385)); + m_TriggerBoxes[SHAFT].SetWidth(135); + m_TriggerBoxes[SHAFT].SetHeight(380); + // Screen position + m_ScreenPositions[SHAFT].SetXY(817, 688); + // Text offset from screen position + m_TextOffsets[SHAFT].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); + // Set up the steps + m_TutAreaSteps[SHAFT].clear(); + m_TutAreaSteps[SHAFT].push_back(TutStep("Use [" + JumpName + "] to activate jetpack", 4000, "Missions.rte/Objects/Tutorial/ArrowUp.png", 2)); + m_TutAreaSteps[SHAFT].push_back(TutStep("Fire jetpack in bursts for better control", 4000, "Missions.rte/Objects/Tutorial/BodyJetpack.png", 2)); + m_TutAreaSteps[SHAFT].push_back(TutStep("Adjust the jet direction by aiming or looking", 4000, "Missions.rte/Objects/Tutorial/BodyJetpack.png", 2)); + m_TutAreaSteps[SHAFT].push_back(TutStep("Jump height is affected by the body's total weight", 4000, "Missions.rte/Objects/Tutorial/BodyJetpack.png", 2)); + m_TutAreaSteps[SHAFT].push_back(TutStep("So the more you carry, the less you can jump", 8000, "Missions.rte/Objects/Tutorial/BodyJetpack.png", 2)); + + // OBSTACLECOURSE + // Set up the trigger area + m_TriggerBoxes[OBSTACLECOURSE].SetCorner(Vector(915, 492)); + m_TriggerBoxes[OBSTACLECOURSE].SetWidth(395); + m_TriggerBoxes[OBSTACLECOURSE].SetHeight(167); + // Screen position + m_ScreenPositions[OBSTACLECOURSE].SetXY(961, 592); + // Text offset from screen position + m_TextOffsets[OBSTACLECOURSE].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); + // Set up the steps + m_TutAreaSteps[OBSTACLECOURSE].clear(); + m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("Obstacle course", 2000, "Missions.rte/Objects/Tutorial/BodyHop.png", 2)); + m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("Climb obstacles by holding [" + MAPNAME(INPUT_L_RIGHT) + "]", 4000, "Missions.rte/Objects/Tutorial/ArrowRight.png", 2)); + m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("Even climb ladders by simply moving toward them", 8000, "Missions.rte/Objects/Tutorial/BodyClimb.png", 2)); + m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("You can also crouch and crawl with [" + CrouchName + "]", 6000, "Missions.rte/Objects/Tutorial/BodyCrawl.png", 2)); + m_TutAreaSteps[OBSTACLECOURSE].push_back(TutStep("In tight spaces, you may need to angle your head down to get through", 8000, "Missions.rte/Objects/Tutorial/BodyCrawl.png", 2)); + + // FIRINGRANGE + // Set up the trigger area + m_TriggerBoxes[FIRINGRANGE].SetCorner(Vector(913, 394)); + m_TriggerBoxes[FIRINGRANGE].SetWidth(389); + m_TriggerBoxes[FIRINGRANGE].SetHeight(97); + // Screen position + m_ScreenPositions[FIRINGRANGE].SetXY(961, 424); + // Text offset from screen position + m_TextOffsets[FIRINGRANGE].SetXY(m_apCommonScreens[0]->w / 2, m_apCommonScreens[0]->h + 4); + // Set up the steps + m_TutAreaSteps[FIRINGRANGE].clear(); + m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Firing range", 2000, "Missions.rte/Objects/Tutorial/FireTarget.png", 1)); + m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Pick up the weapon by first standing over it", 4000, "Missions.rte/Objects/Tutorial/ArrowRight.png", 2)); + m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("And then hold down [" + PieName + "]", 4000, "Missions.rte/Objects/Tutorial/MenuPickUp.png", 1, 500)); + m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Point up to 'Pick Up'", 4000, "Missions.rte/Objects/Tutorial/MenuPickUp.png", 2, 500)); + m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Release [" + PieName + "] to complete the command", 4000, "Missions.rte/Objects/Tutorial/MenuPickUp.png", 1)); + m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("If you aim continuously toward your target", 4000, "Missions.rte/Objects/Tutorial/BodyAim.png", 3)); + m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("It improves your accuracy and view distance", 4000, "Missions.rte/Objects/Tutorial/BodyAim.png", 3)); + m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Use [" + FireName + "] to Fire!", 4000, "Missions.rte/Objects/Tutorial/BodyFire.png", 2)); + m_TutAreaSteps[FIRINGRANGE].push_back(TutStep("Reload manually with [" + PieName + "] + up", 8000, "Missions.rte/Objects/Tutorial/BodyFire.png", 2)); + + // ROOFTOP + // Set up the trigger area + m_TriggerBoxes[ROOFTOP].SetCorner(Vector(732, 176)); + m_TriggerBoxes[ROOFTOP].SetWidth(356); + m_TriggerBoxes[ROOFTOP].SetHeight(210); + // Screen position + m_ScreenPositions[ROOFTOP].SetXY(961, 316); + // Text offset from screen position + m_TextOffsets[ROOFTOP].SetXY(m_apCommonScreens[0]->w / 2, -16); + // Set up the steps + m_TutAreaSteps[ROOFTOP].clear(); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Pick up the digging tool", 4000, "Missions.rte/Objects/Tutorial/MenuPickUp.png", 2, 500)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Use it on the dirt here", 8000, "Missions.rte/Objects/Tutorial/DigPile.png", 2, 750)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("If you dig up gold, it is added to your team's funds", 4000, "Missions.rte/Objects/Tutorial/Funds.png", 2, 250)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Funds can be spent in the Buy Menu", 4000, "Missions.rte/Objects/Tutorial/Funds.png", 1, 333)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Which is opened through the Command Menu", 4000, "Missions.rte/Objects/Tutorial/MenuBuyMenu.png", 1, 500)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Hold [" + PieName + "] and point up and left to 'Buy Menu'", 6000, "Missions.rte/Objects/Tutorial/MenuBuyMenu.png", 2, 500)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("The Buy Menu works like a shopping cart", 6000, "Missions.rte/Objects/Tutorial/BuyMenuCargo.png", 1, 500)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Add to the Cargo list the items you want delivered", 6000, "Missions.rte/Objects/Tutorial/BuyMenuCargo.png", 2, 500)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Then use the BUY button, or click outside the menu", 4000, "Missions.rte/Objects/Tutorial/BuyMenuBuy.png", 2, 500)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Finally select a flat area where you want the goods delivered", 8000, "Missions.rte/Objects/Tutorial/BuyMenuBuy.png", 1, 500)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Next, you can go explore to the west to try flying and climbing in the wild", 6000, "Missions.rte/Objects/Tutorial/ArrowLeft.png", 2)); + m_TutAreaSteps[ROOFTOP].push_back(TutStep("Or, go to the east to learn about squads!", 8000, "Missions.rte/Objects/Tutorial/ArrowRight.png", 2)); + + // ROOFEAST + // Set up the trigger area + m_TriggerBoxes[ROOFEAST].SetCorner(Vector(1100, 176)); + m_TriggerBoxes[ROOFEAST].SetWidth(200); + m_TriggerBoxes[ROOFEAST].SetHeight(210); + // Screen position + m_ScreenPositions[ROOFEAST].SetXY(1201, 316); + // Text offset from screen position + m_TextOffsets[ROOFEAST].SetXY(m_apCommonScreens[0]->w / 2, -16); + // Set up the steps + m_TutAreaSteps[ROOFEAST].clear(); + m_TutAreaSteps[ROOFEAST].push_back(TutStep("Hold [" + PieName + "] and point down and right to 'Form Squad'", 4000, "Missions.rte/Objects/Tutorial/MenuTeam.png", 2, 500)); + m_TutAreaSteps[ROOFEAST].push_back(TutStep("Adjust selection circle to select nearby bodies", 4000, "Missions.rte/Objects/Tutorial/TeamSelect.png", 4, 500)); + m_TutAreaSteps[ROOFEAST].push_back(TutStep("All selected units will follow you, and engage on their own", 4000, "Missions.rte/Objects/Tutorial/TeamFollow.png", 2, 500)); + m_TutAreaSteps[ROOFEAST].push_back(TutStep("Units with similar weapons will fire in unison with the leader", 4000, "Missions.rte/Objects/Tutorial/TeamFollow.png", 2, 500)); + m_TutAreaSteps[ROOFEAST].push_back(TutStep("Hold [" + PieName + "] and point down and right again to disband squad", 4000, "Missions.rte/Objects/Tutorial/MenuTeam.png", 2, 500)); + m_TutAreaSteps[ROOFEAST].push_back(TutStep("Next, you can head east for a TRIAL BATTLE!", 8000, "Missions.rte/Objects/Tutorial/ArrowRight.png", 2)); + + m_AreaTimer.Reset(); + m_StepTimer.Reset(); + } } // namespace RTE diff --git a/Source/Activities/GATutorial.h b/Source/Activities/GATutorial.h index a24197943e..7c62db00ed 100644 --- a/Source/Activities/GATutorial.h +++ b/Source/Activities/GATutorial.h @@ -10,333 +10,304 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "GameActivity.h" #include "Box.h" -namespace RTE -{ - -class Actor; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: GATutorial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tutorial mission with lots of special triggering logic. -// Parent(s): GameActivity. -// Class history: 10/13/2007 GATutorial created. - -class GATutorial : public GameActivity { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(GATutorial); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GATutorial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GATutorial object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - GATutorial() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~GATutorial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a GATutorial object before deletion -// from system memory. -// Arguments: None. - - ~GATutorial() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GATutorial object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a GATutorial to be identical to another, by deep copy. -// Arguments: A reference to the GATutorial to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const GATutorial &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire GATutorial, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Activity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GATutorial object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SceneIsCompatible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells if a particular Scene supports this specific Activity on it. -// Usually that means certain Area:s need to be defined in the Scene. -// Arguments: The Scene to check if it supports this Activiy. Ownership IS NOT TRANSFERRED! -// How many teams we're checking for. Some scenes may support and activity -// but only for a limited number of teams. If -1, not applicable. -// Return value: Whether the Scene has the right stuff. - - bool SceneIsCompatible(Scene *pScene, int teams = -1) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: InitAIs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through all Actor:s currently in the MovableMan and sets each -// one not controlled by a player to be AI controlled and AIMode setting -// based on team and CPU team. -// Arguments: None. -// Return value: None. - - void InitAIs() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetupAreas -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets up or resets the Tutorial Areas to show the current control -// mappings etc. -// Arguments: None. -// Return value: None. - - void SetupAreas(); - - - enum TutorialArea - { - BRAINCHAMBER = 0, - BODYSTORAGE, - SHAFT, - OBSTACLECOURSE, - FIRINGRANGE, - ROOFTOP, - ROOFEAST, - AREACOUNT - }; - - enum TutorialRoom - { - ROOM0 = 0, - ROOM1, - ROOM2, - ROOM3, - ROOMCOUNT - }; - - enum LitState - { - UNLIT = 0, - LIT, - LITSTATECOUNT - }; - - enum ScreenState - { - SCREENOFF = 0, - STATICLITTLE, - STATICLARGE, - SHOWINGSTEP, - SREENSTATECOUNT - }; - - enum FightStage - { - NOFIGHT = 0, - DEFENDING, - ATTACK, - FIGHTSTAGECOUNT - }; - - struct TutStep - { - // Text of this step - std::string m_Text; - // Duration of the whole step - int m_Duration; - // BITMAPs not owned here - std::vector m_pScreens; - // The duration of one frame - int m_FrameDuration; - - TutStep(std::string text, int stepDuration, std::string screensPath = "", int frameCount = 1, int frameDuration = 250); - }; - - // Member variables - static Entity::ClassInfo m_sClass; - - // The player which is actually playing the tut - int m_TutorialPlayer; - // The areas that trigger specific sets of steps to be shown - Box m_TriggerBoxes[AREACOUNT]; - // Positions of the screens for each area - Vector m_ScreenPositions[AREACOUNT]; - // The current state of the all the different areas' screens - ScreenState m_ScreenStates[AREACOUNT]; - // Offsets of the center of the text line from the screen position - Vector m_TextOffsets[AREACOUNT]; - // Screen bitmaps common to all areas.. off, static etc - BITMAP *m_apCommonScreens[STATICLARGE + 1]; - // The steps themselves; cycles through for each area - std::vector m_TutAreaSteps[AREACOUNT]; - // Positions of the numbered room signs - Vector m_RoomSignPositions[ROOMCOUNT]; - // Room sign bitmaps, unlit and lit - BITMAP *m_aapRoomSigns[ROOMCOUNT][LITSTATECOUNT]; - // The timer which keeps track of how long each area has been showing - Timer m_AreaTimer; - // The timer which keeps track of how long each step should be shown - Timer m_StepTimer; - // Which are the player-controlled actor is within - TutorialArea m_CurrentArea; - // Which are the player-controlled actor was in last - TutorialArea m_PrevArea; - // If teh screen has just changed and needs to be redrawn - bool m_ScreenChange; - // Which tutorial step of the current area currently being played back - int m_CurrentStep; - // Which frame of the current step's animation are we on? - int m_CurrentFrame; - // Current room - TutorialRoom m_CurrentRoom; - // Trigger box for the subsequent fight - Box m_FightTriggers[FIGHTSTAGECOUNT]; - int m_EnemyCount; //!< The amount of enemy actors at the start of the activity. - // The current fight stage - FightStage m_CurrentFightStage; - // The CPU opponent brain; not owned! - Actor *m_pCPUBrain; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class Actor; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: GATutorial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tutorial mission with lots of special triggering logic. + // Parent(s): GameActivity. + // Class history: 10/13/2007 GATutorial created. + + class GATutorial : public GameActivity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(GATutorial); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GATutorial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GATutorial object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + GATutorial() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~GATutorial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a GATutorial object before deletion + // from system memory. + // Arguments: None. + + ~GATutorial() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GATutorial object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a GATutorial to be identical to another, by deep copy. + // Arguments: A reference to the GATutorial to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const GATutorial& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire GATutorial, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Activity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the GATutorial object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SceneIsCompatible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells if a particular Scene supports this specific Activity on it. + // Usually that means certain Area:s need to be defined in the Scene. + // Arguments: The Scene to check if it supports this Activiy. Ownership IS NOT TRANSFERRED! + // How many teams we're checking for. Some scenes may support and activity + // but only for a limited number of teams. If -1, not applicable. + // Return value: Whether the Scene has the right stuff. + + bool SceneIsCompatible(Scene* pScene, int teams = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: InitAIs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through all Actor:s currently in the MovableMan and sets each + // one not controlled by a player to be AI controlled and AIMode setting + // based on team and CPU team. + // Arguments: None. + // Return value: None. + + void InitAIs() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetupAreas + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets up or resets the Tutorial Areas to show the current control + // mappings etc. + // Arguments: None. + // Return value: None. + + void SetupAreas(); + + enum TutorialArea { + BRAINCHAMBER = 0, + BODYSTORAGE, + SHAFT, + OBSTACLECOURSE, + FIRINGRANGE, + ROOFTOP, + ROOFEAST, + AREACOUNT + }; + + enum TutorialRoom { + ROOM0 = 0, + ROOM1, + ROOM2, + ROOM3, + ROOMCOUNT + }; + + enum LitState { + UNLIT = 0, + LIT, + LITSTATECOUNT + }; + + enum ScreenState { + SCREENOFF = 0, + STATICLITTLE, + STATICLARGE, + SHOWINGSTEP, + SREENSTATECOUNT + }; + + enum FightStage { + NOFIGHT = 0, + DEFENDING, + ATTACK, + FIGHTSTAGECOUNT + }; + + struct TutStep { + // Text of this step + std::string m_Text; + // Duration of the whole step + int m_Duration; + // BITMAPs not owned here + std::vector m_pScreens; + // The duration of one frame + int m_FrameDuration; + + TutStep(std::string text, int stepDuration, std::string screensPath = "", int frameCount = 1, int frameDuration = 250); + }; + + // Member variables + static Entity::ClassInfo m_sClass; + + // The player which is actually playing the tut + int m_TutorialPlayer; + // The areas that trigger specific sets of steps to be shown + Box m_TriggerBoxes[AREACOUNT]; + // Positions of the screens for each area + Vector m_ScreenPositions[AREACOUNT]; + // The current state of the all the different areas' screens + ScreenState m_ScreenStates[AREACOUNT]; + // Offsets of the center of the text line from the screen position + Vector m_TextOffsets[AREACOUNT]; + // Screen bitmaps common to all areas.. off, static etc + BITMAP* m_apCommonScreens[STATICLARGE + 1]; + // The steps themselves; cycles through for each area + std::vector m_TutAreaSteps[AREACOUNT]; + // Positions of the numbered room signs + Vector m_RoomSignPositions[ROOMCOUNT]; + // Room sign bitmaps, unlit and lit + BITMAP* m_aapRoomSigns[ROOMCOUNT][LITSTATECOUNT]; + // The timer which keeps track of how long each area has been showing + Timer m_AreaTimer; + // The timer which keeps track of how long each step should be shown + Timer m_StepTimer; + // Which are the player-controlled actor is within + TutorialArea m_CurrentArea; + // Which are the player-controlled actor was in last + TutorialArea m_PrevArea; + // If teh screen has just changed and needs to be redrawn + bool m_ScreenChange; + // Which tutorial step of the current area currently being played back + int m_CurrentStep; + // Which frame of the current step's animation are we on? + int m_CurrentFrame; + // Current room + TutorialRoom m_CurrentRoom; + // Trigger box for the subsequent fight + Box m_FightTriggers[FIGHTSTAGECOUNT]; + int m_EnemyCount; //!< The amount of enemy actors at the start of the activity. + // The current fight stage + FightStage m_CurrentFightStage; + // The CPU opponent brain; not owned! + Actor* m_pCPUBrain; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Activities/GameActivity.cpp b/Source/Activities/GameActivity.cpp index 78402d7560..8b60ff6dee 100644 --- a/Source/Activities/GameActivity.cpp +++ b/Source/Activities/GameActivity.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -48,296 +47,274 @@ namespace RTE { -AbstractClassInfo(GameActivity, Activity); - + AbstractClassInfo(GameActivity, Activity); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this GameActivity, effectively + // resetting the members of this abstraction level only. + + void GameActivity::Clear() { + m_CPUTeam = -1; + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + m_ObservationTarget[player].Reset(); + m_DeathViewTarget[player].Reset(); + m_ActorSelectTimer[player].Reset(); + m_ActorCursor[player].Reset(); + m_pLastMarkedActor[player] = 0; + m_LandingZone[player].Reset(); + m_AIReturnCraft[player] = true; + m_StrategicModePieMenu.at(player) = nullptr; + m_LZCursorWidth[player] = 0; + m_InventoryMenuGUI[player] = nullptr; + m_pBuyGUI[player] = 0; + m_pEditorGUI[player] = 0; + m_LuaLockActor[player] = false; + m_LuaLockActorMode[player] = Controller::InputMode::CIM_AI; + m_pBannerRed[player] = 0; + m_pBannerYellow[player] = 0; + m_BannerRepeats[player] = 0; + m_ReadyToStart[player] = false; + m_PurchaseOverride[player].clear(); + m_BrainLZWidth[player] = BRAINLZWIDTHDEFAULT; + m_TeamTech[player] = ""; + m_NetworkPlayerNames[player] = ""; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this GameActivity, effectively -// resetting the members of this abstraction level only. - -void GameActivity::Clear() -{ - m_CPUTeam = -1; - - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - m_ObservationTarget[player].Reset(); - m_DeathViewTarget[player].Reset(); - m_ActorSelectTimer[player].Reset(); - m_ActorCursor[player].Reset(); - m_pLastMarkedActor[player] = 0; - m_LandingZone[player].Reset(); - m_AIReturnCraft[player] = true; - m_StrategicModePieMenu.at(player) = nullptr; - m_LZCursorWidth[player] = 0; - m_InventoryMenuGUI[player] = nullptr; - m_pBuyGUI[player] = 0; - m_pEditorGUI[player] = 0; - m_LuaLockActor[player] = false; - m_LuaLockActorMode[player] = Controller::InputMode::CIM_AI; - m_pBannerRed[player] = 0; - m_pBannerYellow[player] = 0; - m_BannerRepeats[player] = 0; - m_ReadyToStart[player] = false; - m_PurchaseOverride[player].clear(); - m_BrainLZWidth[player] = BRAINLZWIDTHDEFAULT; - m_TeamTech[player] = ""; - m_NetworkPlayerNames[player] = ""; - } - - m_StartingGold = 0; - m_FogOfWarEnabled = false; - m_RequireClearPathToOrbit = false; - - m_DefaultFogOfWar = -1; - m_DefaultRequireClearPathToOrbit = -1; - m_DefaultDeployUnits = 1; - m_DefaultGoldCakeDifficulty = -1; - m_DefaultGoldEasyDifficulty = -1; - m_DefaultGoldMediumDifficulty = -1; - m_DefaultGoldHardDifficulty = -1; - m_DefaultGoldNutsDifficulty = -1; - m_DefaultGoldMaxDifficulty = -1; - m_FogOfWarSwitchEnabled = true; - m_DeployUnitsSwitchEnabled = false; - m_GoldSwitchEnabled = true; - m_RequireClearPathToOrbitSwitchEnabled = true; - m_BuyMenuEnabled = true; - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - m_Deliveries[team].clear(); - m_LandingZoneArea[team].Reset(); - m_aLZCursor[team].clear(); - m_aObjCursor[team].clear(); - } - - m_Objectives.clear(); - - m_DeliveryDelay = 4500; - m_CursorTimer.Reset(); - m_GameTimer.Reset(); - m_GameOverTimer.Reset(); - m_GameOverPeriod = 5000; - m_WinnerTeam = -1; -} + m_StartingGold = 0; + m_FogOfWarEnabled = false; + m_RequireClearPathToOrbit = false; + + m_DefaultFogOfWar = -1; + m_DefaultRequireClearPathToOrbit = -1; + m_DefaultDeployUnits = 1; + m_DefaultGoldCakeDifficulty = -1; + m_DefaultGoldEasyDifficulty = -1; + m_DefaultGoldMediumDifficulty = -1; + m_DefaultGoldHardDifficulty = -1; + m_DefaultGoldNutsDifficulty = -1; + m_DefaultGoldMaxDifficulty = -1; + m_FogOfWarSwitchEnabled = true; + m_DeployUnitsSwitchEnabled = false; + m_GoldSwitchEnabled = true; + m_RequireClearPathToOrbitSwitchEnabled = true; + m_BuyMenuEnabled = true; + + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + m_Deliveries[team].clear(); + m_LandingZoneArea[team].Reset(); + m_aLZCursor[team].clear(); + m_aObjCursor[team].clear(); + } + m_Objectives.clear(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GameActivity object ready for use. + m_DeliveryDelay = 4500; + m_CursorTimer.Reset(); + m_GameTimer.Reset(); + m_GameOverTimer.Reset(); + m_GameOverPeriod = 5000; + m_WinnerTeam = -1; + } -int GameActivity::Create() -{ - if (Activity::Create() < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GameActivity object ready for use. -// m_Description = "Define and edit Areas on this Scene."; + int GameActivity::Create() { + if (Activity::Create() < 0) + return -1; - // Load banners - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - m_pBannerRed[player] = new GUIBanner(); - m_pBannerYellow[player] = new GUIBanner(); - } + // m_Description = "Define and edit Areas on this Scene."; - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - m_TeamIsCPU[team] = false; - } + // Load banners + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + m_pBannerRed[player] = new GUIBanner(); + m_pBannerYellow[player] = new GUIBanner(); + } - return 0; -} + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + m_TeamIsCPU[team] = false; + } + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a GameActivity to be identical to another, by deep copy. - -int GameActivity::Create(const GameActivity &reference) -{ - if (Activity::Create(reference) < 0) - return -1; - - m_CPUTeam = reference.m_CPUTeam; - - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - m_ObservationTarget[player] = reference.m_ObservationTarget[player]; - m_DeathViewTarget[player] = reference.m_DeathViewTarget[player]; - m_ActorCursor[player] = reference.m_ActorCursor[player]; - m_pLastMarkedActor[player] = reference.m_pLastMarkedActor[player]; - m_LandingZone[player] = reference.m_LandingZone[player]; - m_AIReturnCraft[player] = reference.m_AIReturnCraft[player]; - m_InventoryMenuGUI[player] = new InventoryMenuGUI; - m_pBuyGUI[player] = new BuyMenuGUI; - m_pEditorGUI[player] = new SceneEditorGUI; - m_LuaLockActor[player] = reference.m_LuaLockActor[player]; - m_LuaLockActorMode[player] = reference.m_LuaLockActorMode[player]; - m_pBannerRed[player] = new GUIBanner(); - m_pBannerYellow[player] = new GUIBanner(); - m_ReadyToStart[player] = reference.m_ReadyToStart[player]; - m_BrainLZWidth[player] = reference.m_BrainLZWidth[player]; - - m_NetworkPlayerNames[player] = reference.m_NetworkPlayerNames[player]; - } - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - m_LandingZoneArea[team] = reference.m_LandingZoneArea[team]; - m_TeamTech[team] = reference.m_TeamTech[team]; - m_TeamIsCPU[team] = reference.m_TeamIsCPU[team]; - } - - m_StartingGold = reference.m_StartingGold; - m_FogOfWarEnabled = reference.m_FogOfWarEnabled; - m_RequireClearPathToOrbit = reference.m_RequireClearPathToOrbit; - - m_DefaultFogOfWar = reference.m_DefaultFogOfWar; - m_DefaultRequireClearPathToOrbit = reference.m_DefaultRequireClearPathToOrbit; - m_DefaultDeployUnits = reference.m_DefaultDeployUnits; - m_DefaultGoldCakeDifficulty = reference.m_DefaultGoldCakeDifficulty; - m_DefaultGoldEasyDifficulty = reference.m_DefaultGoldEasyDifficulty; - m_DefaultGoldMediumDifficulty = reference.m_DefaultGoldMediumDifficulty; - m_DefaultGoldHardDifficulty = reference.m_DefaultGoldHardDifficulty; - m_DefaultGoldNutsDifficulty = reference.m_DefaultGoldNutsDifficulty; - m_DefaultGoldMaxDifficulty = reference.m_DefaultGoldMaxDifficulty; - m_FogOfWarSwitchEnabled = reference.m_FogOfWarSwitchEnabled; - m_DeployUnitsSwitchEnabled = reference.m_DeployUnitsSwitchEnabled; - m_GoldSwitchEnabled = reference.m_GoldSwitchEnabled; - m_RequireClearPathToOrbitSwitchEnabled = reference.m_RequireClearPathToOrbitSwitchEnabled; - m_BuyMenuEnabled = reference.m_BuyMenuEnabled; - - m_DeliveryDelay = reference.m_DeliveryDelay; -// m_CursorTimer = reference.m_CursorTimer; -// m_GameTimer = reference.m_GameTimer; -// m_GameOverTimer = reference.m_GameOverTimer; - m_GameOverPeriod = reference.m_GameOverPeriod; - m_WinnerTeam = reference.m_WinnerTeam; - - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a GameActivity to be identical to another, by deep copy. + + int GameActivity::Create(const GameActivity& reference) { + if (Activity::Create(reference) < 0) + return -1; + + m_CPUTeam = reference.m_CPUTeam; + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + m_ObservationTarget[player] = reference.m_ObservationTarget[player]; + m_DeathViewTarget[player] = reference.m_DeathViewTarget[player]; + m_ActorCursor[player] = reference.m_ActorCursor[player]; + m_pLastMarkedActor[player] = reference.m_pLastMarkedActor[player]; + m_LandingZone[player] = reference.m_LandingZone[player]; + m_AIReturnCraft[player] = reference.m_AIReturnCraft[player]; + m_InventoryMenuGUI[player] = new InventoryMenuGUI; + m_pBuyGUI[player] = new BuyMenuGUI; + m_pEditorGUI[player] = new SceneEditorGUI; + m_LuaLockActor[player] = reference.m_LuaLockActor[player]; + m_LuaLockActorMode[player] = reference.m_LuaLockActorMode[player]; + m_pBannerRed[player] = new GUIBanner(); + m_pBannerYellow[player] = new GUIBanner(); + m_ReadyToStart[player] = reference.m_ReadyToStart[player]; + m_BrainLZWidth[player] = reference.m_BrainLZWidth[player]; + + m_NetworkPlayerNames[player] = reference.m_NetworkPlayerNames[player]; + } + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + m_LandingZoneArea[team] = reference.m_LandingZoneArea[team]; + m_TeamTech[team] = reference.m_TeamTech[team]; + m_TeamIsCPU[team] = reference.m_TeamIsCPU[team]; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int GameActivity::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Activity::ReadProperty(propName, reader)); - - MatchProperty("CPUTeam", - reader >> m_CPUTeam; - SetCPUTeam(m_CPUTeam); ); - MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); - MatchProperty("DefaultFogOfWar", { reader >> m_DefaultFogOfWar; }); - MatchProperty("DefaultRequireClearPathToOrbit", { reader >> m_DefaultRequireClearPathToOrbit; }); - MatchProperty("DefaultDeployUnits", { reader >> m_DefaultDeployUnits; }); - MatchProperty("DefaultGoldCakeDifficulty", { reader >> m_DefaultGoldCakeDifficulty; }); - MatchProperty("DefaultGoldEasyDifficulty", { reader >> m_DefaultGoldEasyDifficulty; }); - MatchProperty("DefaultGoldMediumDifficulty", { reader >> m_DefaultGoldMediumDifficulty; }); - MatchProperty("DefaultGoldHardDifficulty", { reader >> m_DefaultGoldHardDifficulty; }); - MatchProperty("DefaultGoldNutsDifficulty", { reader >> m_DefaultGoldNutsDifficulty; }); - MatchProperty("DefaultGoldMaxDifficulty", { reader >> m_DefaultGoldMaxDifficulty; }); - MatchProperty("FogOfWarSwitchEnabled", { reader >> m_FogOfWarSwitchEnabled; }); - MatchProperty("DeployUnitsSwitchEnabled", { reader >> m_DeployUnitsSwitchEnabled; }); - MatchProperty("GoldSwitchEnabled", { reader >> m_GoldSwitchEnabled; }); - MatchProperty("RequireClearPathToOrbitSwitchEnabled", { reader >> m_RequireClearPathToOrbitSwitchEnabled; }); - MatchProperty("BuyMenuEnabled", { reader >> m_BuyMenuEnabled; }); - MatchForwards("Team1Tech") - MatchForwards("Team2Tech") - MatchForwards("Team3Tech") - MatchProperty("Team4Tech", - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) { - if (propName == "Team" + std::to_string(team + 1) + "Tech") { - std::string techName; - reader >> techName; - SetTeamTech(team, techName); - } - } ); - MatchProperty("SpecialBehaviour_StartingGold", { reader >> m_StartingGold; }); - MatchProperty("SpecialBehaviour_FogOfWarEnabled", { reader >> m_FogOfWarEnabled; }); + m_StartingGold = reference.m_StartingGold; + m_FogOfWarEnabled = reference.m_FogOfWarEnabled; + m_RequireClearPathToOrbit = reference.m_RequireClearPathToOrbit; + + m_DefaultFogOfWar = reference.m_DefaultFogOfWar; + m_DefaultRequireClearPathToOrbit = reference.m_DefaultRequireClearPathToOrbit; + m_DefaultDeployUnits = reference.m_DefaultDeployUnits; + m_DefaultGoldCakeDifficulty = reference.m_DefaultGoldCakeDifficulty; + m_DefaultGoldEasyDifficulty = reference.m_DefaultGoldEasyDifficulty; + m_DefaultGoldMediumDifficulty = reference.m_DefaultGoldMediumDifficulty; + m_DefaultGoldHardDifficulty = reference.m_DefaultGoldHardDifficulty; + m_DefaultGoldNutsDifficulty = reference.m_DefaultGoldNutsDifficulty; + m_DefaultGoldMaxDifficulty = reference.m_DefaultGoldMaxDifficulty; + m_FogOfWarSwitchEnabled = reference.m_FogOfWarSwitchEnabled; + m_DeployUnitsSwitchEnabled = reference.m_DeployUnitsSwitchEnabled; + m_GoldSwitchEnabled = reference.m_GoldSwitchEnabled; + m_RequireClearPathToOrbitSwitchEnabled = reference.m_RequireClearPathToOrbitSwitchEnabled; + m_BuyMenuEnabled = reference.m_BuyMenuEnabled; + + m_DeliveryDelay = reference.m_DeliveryDelay; + // m_CursorTimer = reference.m_CursorTimer; + // m_GameTimer = reference.m_GameTimer; + // m_GameOverTimer = reference.m_GameOverTimer; + m_GameOverPeriod = reference.m_GameOverPeriod; + m_WinnerTeam = reference.m_WinnerTeam; + + return 0; + } - EndPropertyList; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int GameActivity::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Activity::ReadProperty(propName, reader)); + + MatchProperty("CPUTeam", + reader >> m_CPUTeam; + SetCPUTeam(m_CPUTeam);); + MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); + MatchProperty("DefaultFogOfWar", { reader >> m_DefaultFogOfWar; }); + MatchProperty("DefaultRequireClearPathToOrbit", { reader >> m_DefaultRequireClearPathToOrbit; }); + MatchProperty("DefaultDeployUnits", { reader >> m_DefaultDeployUnits; }); + MatchProperty("DefaultGoldCakeDifficulty", { reader >> m_DefaultGoldCakeDifficulty; }); + MatchProperty("DefaultGoldEasyDifficulty", { reader >> m_DefaultGoldEasyDifficulty; }); + MatchProperty("DefaultGoldMediumDifficulty", { reader >> m_DefaultGoldMediumDifficulty; }); + MatchProperty("DefaultGoldHardDifficulty", { reader >> m_DefaultGoldHardDifficulty; }); + MatchProperty("DefaultGoldNutsDifficulty", { reader >> m_DefaultGoldNutsDifficulty; }); + MatchProperty("DefaultGoldMaxDifficulty", { reader >> m_DefaultGoldMaxDifficulty; }); + MatchProperty("FogOfWarSwitchEnabled", { reader >> m_FogOfWarSwitchEnabled; }); + MatchProperty("DeployUnitsSwitchEnabled", { reader >> m_DeployUnitsSwitchEnabled; }); + MatchProperty("GoldSwitchEnabled", { reader >> m_GoldSwitchEnabled; }); + MatchProperty("RequireClearPathToOrbitSwitchEnabled", { reader >> m_RequireClearPathToOrbitSwitchEnabled; }); + MatchProperty("BuyMenuEnabled", { reader >> m_BuyMenuEnabled; }); + MatchForwards("Team1Tech") + MatchForwards("Team2Tech") + MatchForwards("Team3Tech") + MatchProperty( + "Team4Tech", + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) { + if (propName == "Team" + std::to_string(team + 1) + "Tech") { + std::string techName; + reader >> techName; + SetTeamTech(team, techName); + } + }); + MatchProperty("SpecialBehaviour_StartingGold", { reader >> m_StartingGold; }); + MatchProperty("SpecialBehaviour_FogOfWarEnabled", { reader >> m_FogOfWarEnabled; }); + + EndPropertyList; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this GameActivity with a Writer for + // later recreation with Create(Reader &reader); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this GameActivity with a Writer for -// later recreation with Create(Reader &reader); + int GameActivity::Save(Writer& writer) const { + Activity::Save(writer); -int GameActivity::Save(Writer &writer) const { - Activity::Save(writer); + writer.NewPropertyWithValue("CPUTeam", m_CPUTeam); + writer.NewPropertyWithValue("DeliveryDelay", m_DeliveryDelay); + writer.NewPropertyWithValue("BuyMenuEnabled", m_BuyMenuEnabled); - writer.NewPropertyWithValue("CPUTeam", m_CPUTeam); - writer.NewPropertyWithValue("DeliveryDelay", m_DeliveryDelay); - writer.NewPropertyWithValue("BuyMenuEnabled", m_BuyMenuEnabled); + // Note - these special behaviour properties are for saving and loading. Normally these fields are set by the Activity config GUI. + writer.NewPropertyWithValue("SpecialBehaviour_StartingGold", m_StartingGold); + writer.NewPropertyWithValue("SpecialBehaviour_FogOfWarEnabled", m_FogOfWarEnabled); - // Note - these special behaviour properties are for saving and loading. Normally these fields are set by the Activity config GUI. - writer.NewPropertyWithValue("SpecialBehaviour_StartingGold", m_StartingGold); - writer.NewPropertyWithValue("SpecialBehaviour_FogOfWarEnabled", m_FogOfWarEnabled); + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) { + writer.NewPropertyWithValue("Team" + std::to_string(team + 1) + "Tech", GetTeamTech(team)); + } - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) { - writer.NewPropertyWithValue("Team" + std::to_string(team + 1) + "Tech", GetTeamTech(team)); + return 0; } - return 0; -} - + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the GameActivity object. + + void GameActivity::Destroy(bool notInherited) { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + delete m_InventoryMenuGUI[player]; + delete m_pBuyGUI[player]; + delete m_pEditorGUI[player]; + delete m_pBannerRed[player]; + delete m_pBannerYellow[player]; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GameActivity object. - -void GameActivity::Destroy(bool notInherited) -{ - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - delete m_InventoryMenuGUI[player]; - delete m_pBuyGUI[player]; - delete m_pEditorGUI[player]; - delete m_pBannerRed[player]; - delete m_pBannerYellow[player]; - } - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) { - delete itr->pCraft; - } - m_Deliveries[team].clear(); - } - - if (!notInherited) - Activity::Destroy(); - Clear(); -} + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) { + delete itr->pCraft; + } + m_Deliveries[team].clear(); + } + if (!notInherited) + Activity::Destroy(); + Clear(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetTeamTech -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets tech module name for specified team. Module must set must be loaded. - void GameActivity::SetTeamTech(int team, std::string tech) - { - if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) - { + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetTeamTech + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets tech module name for specified team. Module must set must be loaded. + void GameActivity::SetTeamTech(int team, std::string tech) { + if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { if (tech == "-All-" || tech == "-Random-") m_TeamTech[team] = tech; - else - { + else { int id = g_PresetMan.GetModuleID(tech); if (id != -1) m_TeamTech[team] = tech; @@ -347,2626 +324,2409 @@ void GameActivity::Destroy(bool notInherited) } } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCrabToHumanSpawnRatio -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns CrabToHumanSpawnRatio for specified module - float GameActivity::GetCrabToHumanSpawnRatio(int moduleid) - { - if (moduleid > -1) - { - const DataModule * pDataModule = g_PresetMan.GetDataModule(moduleid); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCrabToHumanSpawnRatio + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns CrabToHumanSpawnRatio for specified module + float GameActivity::GetCrabToHumanSpawnRatio(int moduleid) { + if (moduleid > -1) { + const DataModule* pDataModule = g_PresetMan.GetDataModule(moduleid); if (pDataModule) return pDataModule->GetCrabToHumanSpawnRatio(); } return 0.25; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCPUTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current CPU-assisted team, if any (NoTeam) - LEGACY function + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCPUTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current CPU-assisted team, if any (NoTeam) - LEGACY function -void GameActivity::SetCPUTeam(int team) -{ - if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { - // Set the legacy var - m_CPUTeam = team; + void GameActivity::SetCPUTeam(int team) { + if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { + // Set the legacy var + m_CPUTeam = team; - m_TeamActive[team] = true; - m_TeamIsCPU[team] = true; - } + m_TeamActive[team] = true; + m_TeamIsCPU[team] = true; + } -/* whaaaa? - // Also set the newer human indicator flags - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - m_IsHuman[m_Team[player]] = m_IsActive[player] && m_Team[player] != team; -*/ -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool GameActivity::IsBuyGUIVisible(int which) const { - if (which == -1) { - for (short player = Players::PlayerOne; player < this->GetPlayerCount(); player++) { - if (this->GetBuyGUI(player)->IsVisible()) { - return true; - } - } - return false; - } - return this->GetBuyGUI(which)->IsVisible(); - -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool GameActivity::LockControlledActor(Players player, bool lock, Controller::InputMode lockToMode) { - if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { - bool prevLock = m_LuaLockActor[player]; - m_LuaLockActor[player] = lock; - m_LuaLockActorMode[player] = lockToMode; - return true; + /* whaaaa? + // Also set the newer human indicator flags + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) + m_IsHuman[m_Team[player]] = m_IsActive[player] && m_Team[player] != team; + */ } - return false; -} -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SwitchToActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the this to focus player control to a specific Actor for a -// specific team. OWNERSHIP IS NOT TRANSFERRED! + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool GameActivity::IsBuyGUIVisible(int which) const { + if (which == -1) { + for (short player = Players::PlayerOne; player < this->GetPlayerCount(); player++) { + if (this->GetBuyGUI(player)->IsVisible()) { + return true; + } + } + return false; + } + return this->GetBuyGUI(which)->IsVisible(); + } -bool GameActivity::SwitchToActor(Actor *pActor, int player, int team) -{ - // Computer players don't focus on any Actor - if (!m_IsHuman[player]) - return false; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (pActor && pActor->GetPieMenu()) { - pActor->GetPieMenu()->DoDisableAnimation(); + bool GameActivity::LockControlledActor(Players player, bool lock, Controller::InputMode lockToMode) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { + bool prevLock = m_LuaLockActor[player]; + m_LuaLockActor[player] = lock; + m_LuaLockActorMode[player] = lockToMode; + return true; + } + return false; } - m_InventoryMenuGUI[player]->SetEnabled(false); - // Disable the AI command mode since it's connected to the current actor - if (m_ViewState[player] == ViewState::AISentryPoint || m_ViewState[player] == ViewState::AIPatrolPoints || m_ViewState[player] == ViewState::AIGoldDigPoint || m_ViewState[player] == ViewState::AIGoToPoint || m_ViewState[player] == ViewState::UnitSelectCircle) - m_ViewState[player] = ViewState::Normal; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SwitchToActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the this to focus player control to a specific Actor for a + // specific team. OWNERSHIP IS NOT TRANSFERRED! - return Activity::SwitchToActor(pActor, player, team); -} + bool GameActivity::SwitchToActor(Actor* pActor, int player, int team) { + // Computer players don't focus on any Actor + if (!m_IsHuman[player]) + return false; + if (pActor && pActor->GetPieMenu()) { + pActor->GetPieMenu()->DoDisableAnimation(); + } + m_InventoryMenuGUI[player]->SetEnabled(false); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SwitchToNextActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the this to focus player control to the next Actor of a -// specific team, other than the current one focused on. + // Disable the AI command mode since it's connected to the current actor + if (m_ViewState[player] == ViewState::AISentryPoint || m_ViewState[player] == ViewState::AIPatrolPoints || m_ViewState[player] == ViewState::AIGoldDigPoint || m_ViewState[player] == ViewState::AIGoToPoint || m_ViewState[player] == ViewState::UnitSelectCircle) + m_ViewState[player] = ViewState::Normal; + + return Activity::SwitchToActor(pActor, player, team); + } -void GameActivity::SwitchToNextActor(int player, int team, Actor *pSkip) -{ - m_InventoryMenuGUI[player]->SetEnabled(false); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SwitchToNextActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the this to focus player control to the next Actor of a + // specific team, other than the current one focused on. - // Disable the AI command mode since it's connected to the current actor - if (m_ViewState[player] == ViewState::AISentryPoint || m_ViewState[player] == ViewState::AIPatrolPoints || m_ViewState[player] == ViewState::AIGoldDigPoint || m_ViewState[player] == ViewState::AIGoToPoint || m_ViewState[player] == ViewState::UnitSelectCircle) - m_ViewState[player] = ViewState::Normal; + void GameActivity::SwitchToNextActor(int player, int team, Actor* pSkip) { + m_InventoryMenuGUI[player]->SetEnabled(false); - Activity::SwitchToNextActor(player, team, pSkip); + // Disable the AI command mode since it's connected to the current actor + if (m_ViewState[player] == ViewState::AISentryPoint || m_ViewState[player] == ViewState::AIPatrolPoints || m_ViewState[player] == ViewState::AIGoldDigPoint || m_ViewState[player] == ViewState::AIGoToPoint || m_ViewState[player] == ViewState::UnitSelectCircle) + m_ViewState[player] = ViewState::Normal; - if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { - m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); - } -} + Activity::SwitchToNextActor(player, team, pSkip); + if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { + m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SwitchToPrevActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces this to focus player control to the previous Actor of a -// specific team, other than the current one focused on. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SwitchToPrevActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces this to focus player control to the previous Actor of a + // specific team, other than the current one focused on. -void GameActivity::SwitchToPrevActor(int player, int team, Actor *pSkip) -{ - m_InventoryMenuGUI[player]->SetEnabled(false); + void GameActivity::SwitchToPrevActor(int player, int team, Actor* pSkip) { + m_InventoryMenuGUI[player]->SetEnabled(false); - // Disable the AI command mode since it's connected to the current actor - if (m_ViewState[player] == ViewState::AISentryPoint || m_ViewState[player] == ViewState::AIPatrolPoints || m_ViewState[player] == ViewState::AIGoldDigPoint || m_ViewState[player] == ViewState::AIGoToPoint || m_ViewState[player] == ViewState::UnitSelectCircle) - m_ViewState[player] = ViewState::Normal; + // Disable the AI command mode since it's connected to the current actor + if (m_ViewState[player] == ViewState::AISentryPoint || m_ViewState[player] == ViewState::AIPatrolPoints || m_ViewState[player] == ViewState::AIGoldDigPoint || m_ViewState[player] == ViewState::AIGoToPoint || m_ViewState[player] == ViewState::UnitSelectCircle) + m_ViewState[player] = ViewState::Normal; - Activity::SwitchToPrevActor(player, team, pSkip); + Activity::SwitchToPrevActor(player, team, pSkip); - if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { - m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); + if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { + m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); + } } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddObjectivePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Created an objective point for one of the teams to show until cleared. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddObjectivePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Created an objective point for one of the teams to show until cleared. + void GameActivity::AddObjectivePoint(std::string description, Vector objPos, int whichTeam, ObjectiveArrowDir arrowDir) { + m_Objectives.push_back(ObjectivePoint(description, objPos, whichTeam, arrowDir)); + } -void GameActivity::AddObjectivePoint(std::string description, Vector objPos, int whichTeam, ObjectiveArrowDir arrowDir) -{ - m_Objectives.push_back(ObjectivePoint(description, objPos, whichTeam, arrowDir)); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: YSortObjectivePoints + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sorts all objective points according to their positions on the Y axis. + void GameActivity::YSortObjectivePoints() { + m_Objectives.sort(ObjPointYPosComparison()); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: YSortObjectivePoints -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sorts all objective points according to their positions on the Y axis. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddOverridePurchase + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds somehting to the purchase list that will override what is set + // in the buy GUI next time CreateDelivery is called. + + int GameActivity::AddOverridePurchase(const SceneObject* pPurchase, int player) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { + // Add to purchase list if valid item + if (pPurchase) { + // Get the preset of this instance passed in, so we make sure we are only storing non-owned instances + const SceneObject* pPreset = dynamic_cast(g_PresetMan.GetEntityPreset(pPurchase->GetClassName(), pPurchase->GetPresetName(), pPurchase->GetModuleID())); + if (pPreset) + m_PurchaseOverride[player].push_back(pPreset); + } -void GameActivity::YSortObjectivePoints() -{ - m_Objectives.sort(ObjPointYPosComparison()); -} + // Take metaplayer tech modifiers into account + int nativeModule = 0; + float foreignCostMult = 1.0; + float nativeCostMult = 1.0; + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + if (g_MetaMan.GameInProgress() && pMetaPlayer) { + nativeModule = pMetaPlayer->GetNativeTechModule(); + foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); + nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); + } + // Calculate the total list cost for this player + int totalListCost = 0; + for (std::list::iterator itr = m_PurchaseOverride[player].begin(); itr != m_PurchaseOverride[player].end(); ++itr) + totalListCost += (*itr)->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddOverridePurchase -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds somehting to the purchase list that will override what is set -// in the buy GUI next time CreateDelivery is called. - -int GameActivity::AddOverridePurchase(const SceneObject *pPurchase, int player) -{ - if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) - { - // Add to purchase list if valid item - if (pPurchase) - { - // Get the preset of this instance passed in, so we make sure we are only storing non-owned instances - const SceneObject *pPreset = dynamic_cast(g_PresetMan.GetEntityPreset(pPurchase->GetClassName(), pPurchase->GetPresetName(), pPurchase->GetModuleID())); - if (pPreset) - m_PurchaseOverride[player].push_back(pPreset); + return totalListCost; } - // Take metaplayer tech modifiers into account - int nativeModule = 0; - float foreignCostMult = 1.0; - float nativeCostMult = 1.0; - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - if (g_MetaMan.GameInProgress() && pMetaPlayer) - { - nativeModule = pMetaPlayer->GetNativeTechModule(); - foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); - nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); - } + return 0; + } - // Calculate the total list cost for this player - int totalListCost = 0; - for (std::list::iterator itr = m_PurchaseOverride[player].begin(); itr != m_PurchaseOverride[player].end(); ++itr) - totalListCost += (*itr)->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOverridePurchaseList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: First clears and then adds all the stuff in a Loadout to the override + // purchase list. - return totalListCost; - } + int GameActivity::SetOverridePurchaseList(const Loadout* pLoadout, int player) { + // First clear out the list + ClearOverridePurchase(player); + + int finalListCost = 0; - return 0; -} + // Sanity check + if (!pLoadout) { + g_ConsoleMan.PrintString("ERROR: Tried to set an override purchase list based on a nonexistent Loadout!"); + return 0; + } + const ACraft* pCraftPreset = pLoadout->GetDeliveryCraft(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOverridePurchaseList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: First clears and then adds all the stuff in a Loadout to the override -// purchase list. - -int GameActivity::SetOverridePurchaseList(const Loadout *pLoadout, int player) -{ - // First clear out the list - ClearOverridePurchase(player); - - int finalListCost = 0; - - // Sanity check - if (!pLoadout) - { - g_ConsoleMan.PrintString("ERROR: Tried to set an override purchase list based on a nonexistent Loadout!"); - return 0; - } - - const ACraft *pCraftPreset = pLoadout->GetDeliveryCraft(); - - // Check if we even have a craft and substitute a default if we don't - if (!pCraftPreset) - { -// Too verbose for a recoverable error -// g_ConsoleMan.PrintString("ERROR: Tried to set an override purchase list with no delivery craft defined. Using a default instead."); - const Loadout *pDefault = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Default", -1)); - if (pDefault) - pCraftPreset = pDefault->GetDeliveryCraft(); - - // If still no go, then fuck it - if (!pCraftPreset) - { - g_ConsoleMan.PrintString("ERROR: Couldn't even find a \"Default\" Loadout in Base.rte! Aborting."); - return 0; - } - } - - // Add the delivery Craft - finalListCost = AddOverridePurchase(pCraftPreset, player); - - // Add the rest of the cargo list - std::list *pCargoList = const_cast(pLoadout)->GetCargoList(); - for (std::list::iterator itr = pCargoList->begin(); itr != pCargoList->end(); ++itr) - finalListCost = AddOverridePurchase(*itr, player); - - return finalListCost; -} + // Check if we even have a craft and substitute a default if we don't + if (!pCraftPreset) { + // Too verbose for a recoverable error + // g_ConsoleMan.PrintString("ERROR: Tried to set an override purchase list with no delivery craft defined. Using a default instead."); + const Loadout* pDefault = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Default", -1)); + if (pDefault) + pCraftPreset = pDefault->GetDeliveryCraft(); + + // If still no go, then fuck it + if (!pCraftPreset) { + g_ConsoleMan.PrintString("ERROR: Couldn't even find a \"Default\" Loadout in Base.rte! Aborting."); + return 0; + } + } + // Add the delivery Craft + finalListCost = AddOverridePurchase(pCraftPreset, player); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOverridePurchaseList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: First clears and then adds all the stuff in a Loadout to the override -// purchase list. + // Add the rest of the cargo list + std::list* pCargoList = const_cast(pLoadout)->GetCargoList(); + for (std::list::iterator itr = pCargoList->begin(); itr != pCargoList->end(); ++itr) + finalListCost = AddOverridePurchase(*itr, player); -int GameActivity::SetOverridePurchaseList(std::string loadoutName, int player) -{ - // Find out the native module of this player - int nativeModule = 0; - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - if (g_MetaMan.GameInProgress() && pMetaPlayer) - nativeModule = pMetaPlayer->GetNativeTechModule(); + return finalListCost; + } - // Find the Loadout that this Deployment is referring to - const Loadout *pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", loadoutName, nativeModule)); - if (pLoadout) - return SetOverridePurchaseList(pLoadout, player); - else - g_ConsoleMan.PrintString("ERROR: Tried to set an override purchase list based on a nonexistent Loadout!"); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOverridePurchaseList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: First clears and then adds all the stuff in a Loadout to the override + // purchase list. - return 0; -} + int GameActivity::SetOverridePurchaseList(std::string loadoutName, int player) { + // Find out the native module of this player + int nativeModule = 0; + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + if (g_MetaMan.GameInProgress() && pMetaPlayer) + nativeModule = pMetaPlayer->GetNativeTechModule(); + // Find the Loadout that this Deployment is referring to + const Loadout* pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", loadoutName, nativeModule)); + if (pLoadout) + return SetOverridePurchaseList(pLoadout, player); + else + g_ConsoleMan.PrintString("ERROR: Tried to set an override purchase list based on a nonexistent Loadout!"); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateDelivery -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes the current order out of a player's buy GUI, creates a Delivery -// based off it, and stuffs it into that player's delivery queue. - -bool GameActivity::CreateDelivery(int player, int mode, Vector &waypoint, Actor * pTargetMO) -{ - int team = m_Team[player]; - if (team == Teams::NoTeam) - return false; - - // Prepare the Craft, stuff everything into it and add it to the queue - // Retrieve the ordered craft and its inventory - std::list purchaseList; - - ACraft *pDeliveryCraft = 0; - // If we have a list to purchase that overrides the buy GUI, then use it and clear it - if (!m_PurchaseOverride[player].empty()) - { - const ACraft *pCraftPreset = 0; - for (std::list::iterator itr = m_PurchaseOverride[player].begin(); itr != m_PurchaseOverride[player].end(); ++itr) - { - // Find the first craft to use as the delivery craft - pCraftPreset = dynamic_cast(*itr); - if (!pDeliveryCraft && pCraftPreset) - pDeliveryCraft = dynamic_cast((*itr)->Clone()); - else - purchaseList.push_back(*itr); - } - } - // Otherwise, use what's set in the buy GUI as usual - else - { - m_pBuyGUI[player]->GetOrderList(purchaseList); - pDeliveryCraft = dynamic_cast(m_pBuyGUI[player]->GetDeliveryCraftPreset()->Clone()); - // If we don't have any loadout presets in the menu, save the current config for later - if (m_pBuyGUI[player]->GetLoadoutPresets().empty()) - m_pBuyGUI[player]->SaveCurrentLoadout(); - } - - if (pDeliveryCraft && (!m_HadBrain[player] || m_Brain[player])) - { - // Take metaplayer tech modifiers into account when calculating costs of this delivery - int nativeModule = 0; - float foreignCostMult = 1.0; - float nativeCostMult = 1.0; - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - if (g_MetaMan.GameInProgress() && pMetaPlayer) - { - nativeModule = pMetaPlayer->GetNativeTechModule(); - foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); - nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); - } - // Start with counting the craft - float totalCost = 0; - - if (m_pBuyGUI[player]->GetOnlyShowOwnedItems()) - { - if (!m_pBuyGUI[player]->CommitPurchase(pDeliveryCraft->GetModuleAndPresetName())) - { - if (m_pBuyGUI[player]->IsAlwaysAllowedItem(pDeliveryCraft->GetModuleAndPresetName())) - totalCost = pDeliveryCraft->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateDelivery + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes the current order out of a player's buy GUI, creates a Delivery + // based off it, and stuffs it into that player's delivery queue. + + bool GameActivity::CreateDelivery(int player, int mode, Vector& waypoint, Actor* pTargetMO) { + int team = m_Team[player]; + if (team == Teams::NoTeam) + return false; + + // Prepare the Craft, stuff everything into it and add it to the queue + // Retrieve the ordered craft and its inventory + std::list purchaseList; + + ACraft* pDeliveryCraft = 0; + // If we have a list to purchase that overrides the buy GUI, then use it and clear it + if (!m_PurchaseOverride[player].empty()) { + const ACraft* pCraftPreset = 0; + for (std::list::iterator itr = m_PurchaseOverride[player].begin(); itr != m_PurchaseOverride[player].end(); ++itr) { + // Find the first craft to use as the delivery craft + pCraftPreset = dynamic_cast(*itr); + if (!pDeliveryCraft && pCraftPreset) + pDeliveryCraft = dynamic_cast((*itr)->Clone()); else - return false; + purchaseList.push_back(*itr); } } - else - { - if (!m_pBuyGUI[player]->CommitPurchase(pDeliveryCraft->GetModuleAndPresetName())) - totalCost = pDeliveryCraft->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); + // Otherwise, use what's set in the buy GUI as usual + else { + m_pBuyGUI[player]->GetOrderList(purchaseList); + pDeliveryCraft = dynamic_cast(m_pBuyGUI[player]->GetDeliveryCraftPreset()->Clone()); + // If we don't have any loadout presets in the menu, save the current config for later + if (m_pBuyGUI[player]->GetLoadoutPresets().empty()) + m_pBuyGUI[player]->SaveCurrentLoadout(); } - // Go through the list of things ordered, and give any actors all the items that is present after them, - // until the next actor. Also, the first actor gets all stuff in the list above him. - MovableObject *pInventoryObject = 0; - Actor *pPassenger = 0; - Actor *pLastPassenger = 0; - std::list cargoItems; - - for (std::list::iterator itr = purchaseList.begin(); itr != purchaseList.end(); ++itr) - { - bool purchaseItem = true; - - // Add to the total cost tally - if (m_pBuyGUI[player]->GetOnlyShowOwnedItems()) - { - if (!m_pBuyGUI[player]->CommitPurchase((*itr)->GetModuleAndPresetName())) - { - if (m_pBuyGUI[player]->IsAlwaysAllowedItem((*itr)->GetModuleAndPresetName())) - totalCost += (*itr)->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); + if (pDeliveryCraft && (!m_HadBrain[player] || m_Brain[player])) { + // Take metaplayer tech modifiers into account when calculating costs of this delivery + int nativeModule = 0; + float foreignCostMult = 1.0; + float nativeCostMult = 1.0; + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + if (g_MetaMan.GameInProgress() && pMetaPlayer) { + nativeModule = pMetaPlayer->GetNativeTechModule(); + foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); + nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); + } + // Start with counting the craft + float totalCost = 0; + + if (m_pBuyGUI[player]->GetOnlyShowOwnedItems()) { + if (!m_pBuyGUI[player]->CommitPurchase(pDeliveryCraft->GetModuleAndPresetName())) { + if (m_pBuyGUI[player]->IsAlwaysAllowedItem(pDeliveryCraft->GetModuleAndPresetName())) + totalCost = pDeliveryCraft->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); else - purchaseItem = false; + return false; } + } else { + if (!m_pBuyGUI[player]->CommitPurchase(pDeliveryCraft->GetModuleAndPresetName())) + totalCost = pDeliveryCraft->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); } - else - { - if (!m_pBuyGUI[player]->CommitPurchase((*itr)->GetModuleAndPresetName())) - totalCost += (*itr)->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); - } - if (purchaseItem) - { - // Make copy of the preset instance in the list - pInventoryObject = dynamic_cast((*itr)->Clone()); - // See if it's actually a passenger, as opposed to a regular item - pPassenger = dynamic_cast(pInventoryObject); - // If it's an actor, then set its team and add it to the Craft's inventory! - if (pPassenger) - { - if (dynamic_cast(pPassenger)) { - // If this is the first passenger, then give him all the shit found in the list before him - if (!pLastPassenger) { - for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) - pPassenger->AddInventoryItem(*iItr); + // Go through the list of things ordered, and give any actors all the items that is present after them, + // until the next actor. Also, the first actor gets all stuff in the list above him. + MovableObject* pInventoryObject = 0; + Actor* pPassenger = 0; + Actor* pLastPassenger = 0; + std::list cargoItems; + + for (std::list::iterator itr = purchaseList.begin(); itr != purchaseList.end(); ++itr) { + bool purchaseItem = true; + + // Add to the total cost tally + if (m_pBuyGUI[player]->GetOnlyShowOwnedItems()) { + if (!m_pBuyGUI[player]->CommitPurchase((*itr)->GetModuleAndPresetName())) { + if (m_pBuyGUI[player]->IsAlwaysAllowedItem((*itr)->GetModuleAndPresetName())) + totalCost += (*itr)->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); + else + purchaseItem = false; + } + } else { + if (!m_pBuyGUI[player]->CommitPurchase((*itr)->GetModuleAndPresetName())) + totalCost += (*itr)->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); + } + + if (purchaseItem) { + // Make copy of the preset instance in the list + pInventoryObject = dynamic_cast((*itr)->Clone()); + // See if it's actually a passenger, as opposed to a regular item + pPassenger = dynamic_cast(pInventoryObject); + // If it's an actor, then set its team and add it to the Craft's inventory! + if (pPassenger) { + if (dynamic_cast(pPassenger)) { + // If this is the first passenger, then give him all the shit found in the list before him + if (!pLastPassenger) { + for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) + pPassenger->AddInventoryItem(*iItr); + } + // This isn't the first passenger, so give the previous guy all the stuff that was found since processing him + else { + for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) + pLastPassenger->AddInventoryItem(*iItr); + } + + // Now set the current passenger as the 'last passenger' so he'll eventually get everything found after him. + pLastPassenger = pPassenger; + } else if (pLastPassenger) { + for (MovableObject* cargoItem: cargoItems) { + pLastPassenger->AddInventoryItem(cargoItem); + } + pLastPassenger = nullptr; } - // This isn't the first passenger, so give the previous guy all the stuff that was found since processing him - else { - for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) - pLastPassenger->AddInventoryItem(*iItr); + // Clear out the temporary cargo list since we've assign all the stuff in it to a passenger + cargoItems.clear(); + // Set the team etc for the current passenger and stuff him into the craft + pPassenger->SetTeam(team); + pPassenger->SetControllerMode(Controller::CIM_AI); + pPassenger->SetAIMode((Actor::AIMode)mode); + + if (pTargetMO != NULL) { + Actor* pTarget = dynamic_cast(pTargetMO); + if (pTarget) + pPassenger->AddAIMOWaypoint(pTarget); + } else if (waypoint.m_X > 0 && waypoint.m_Y > 0) { + pPassenger->AddAISceneWaypoint(waypoint); } - // Now set the current passenger as the 'last passenger' so he'll eventually get everything found after him. - pLastPassenger = pPassenger; - } else if (pLastPassenger) { - for (MovableObject *cargoItem : cargoItems) { - pLastPassenger->AddInventoryItem(cargoItem); - } - pLastPassenger = nullptr; + pDeliveryCraft->AddInventoryItem(pPassenger); } - // Clear out the temporary cargo list since we've assign all the stuff in it to a passenger - cargoItems.clear(); - // Set the team etc for the current passenger and stuff him into the craft - pPassenger->SetTeam(team); - pPassenger->SetControllerMode(Controller::CIM_AI); - pPassenger->SetAIMode((Actor::AIMode)mode); - - if (pTargetMO != NULL) - { - Actor * pTarget = dynamic_cast(pTargetMO); - if (pTarget) - pPassenger->AddAIMOWaypoint(pTarget); - } - else if (waypoint.m_X > 0 && waypoint.m_Y > 0) - { - pPassenger->AddAISceneWaypoint(waypoint); - } - - pDeliveryCraft->AddInventoryItem(pPassenger); + // If not, then add it to the temp list of items which will be added to the last passenger's inventory + else + cargoItems.push_back(pInventoryObject); } - // If not, then add it to the temp list of items which will be added to the last passenger's inventory - else - cargoItems.push_back(pInventoryObject); } - } - - pPassenger = 0; - - // If there was a last passenger and things after him, stuff all the items into his inventory - if (pLastPassenger) - { - for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) - pLastPassenger->AddInventoryItem(*iItr); - } - // Otherwise, stuff it all stuff directly into the craft instead - else - { - for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) - pDeliveryCraft->AddInventoryItem(*iItr); - } - - float spawnY = 0.0f; - if (g_SceneMan.GetTerrain() && g_SceneMan.GetTerrain()->GetOrbitDirection() == Directions::Down) { - spawnY = g_SceneMan.GetSceneHeight(); - } - - // Delivery craft appear straight over the selected LZ - pDeliveryCraft->SetPos(Vector(m_LandingZone[player].m_X, spawnY)); -// pDeliveryCraft->SetPos(Vector(m_LandingZone[player].m_X, 300)); - - pDeliveryCraft->SetTeam(team); -// TODO: The after-delivery AI mode needs to be set depending on what the user has set in teh LZ selection mode - pDeliveryCraft->SetControllerMode(Controller::CIM_AI); - pDeliveryCraft->SetAIMode(m_AIReturnCraft[player] ? Actor::AIMODE_DELIVER : Actor::AIMODE_STAY); - - // Prepare the Delivery struct and stuff the ready to go craft in there - Delivery newDelivery; - // Pass ownership of the craft to the new Delivery struct - newDelivery.pCraft = pDeliveryCraft; - newDelivery.orderedByPlayer = player; - newDelivery.landingZone = m_LandingZone[player]; - newDelivery.multiOrderYOffset = 0; - newDelivery.delay = m_DeliveryDelay * pDeliveryCraft->GetDeliveryDelayMultiplier(); - newDelivery.timer.Reset(); - - // Add the new Delivery to the queue - m_Deliveries[team].push_back(newDelivery); - - pDeliveryCraft = 0; - pLastPassenger = 0; - - // Deduct cost from team's funds - m_TeamFunds[team] -= totalCost; - - // Go 'ding!', but only if player is human, or it may be confusing - if (PlayerHuman(player)) - g_GUISound.ConfirmSound()->Play(player); - - // Clear out the override purchase list, whether anything was in there or not, it should not override twice. - m_PurchaseOverride[player].clear(); - - return true; - } - - return false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetupPlayers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Precalculates the player-to-screen index map, counts the number of -// active players, teams etc. - -void GameActivity::SetupPlayers() -{ - Activity::SetupPlayers(); - - // Add the locked cpu team that can't have any players - if (m_CPUTeam != Teams::NoTeam && !m_TeamActive[m_CPUTeam]) { - m_TeamCount++; - // Also activate the CPU team - m_TeamActive[m_CPUTeam] = true; - } - - // Don't clear a CPU team's active status though - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { - if (m_TeamIsCPU[team] && !m_TeamActive[team]) { - m_TeamCount++; - m_TeamActive[team] = true; - } - } -} + pPassenger = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. - -int GameActivity::Start() -{ - // Set the split screen config before the Scene (and it SceneLayers, specifially) are loaded - int humanCount = GetHumanCount(); - // Depending on the resolution aspect ratio, split first horizontally (if wide screen) - if (((float)g_WindowMan.GetResX() / (float)g_WindowMan.GetResY()) >= 1.6) - g_FrameMan.ResetSplitScreens(humanCount > 1, humanCount > 2); - // or vertically (if 4:3-ish) - else - g_FrameMan.ResetSplitScreens(humanCount > 2, humanCount > 1); - - int error = Activity::Start(); - if (error < 0) - return error; - - m_WinnerTeam = Teams::NoTeam; - - //////////////////////////////// - // Set up teams - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - - m_Deliveries[team].clear(); - - // Clear delivery queues - for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) { - delete itr->pCraft; - } - - m_Deliveries[team].clear(); -/* This is taken care of by the individual Activity logic - // See if there are specified landing zone areas defined in the scene - char str[64]; - std::snprintf(str, sizeof(str), "LZ Team %d", team + 1); - Scene::Area *pArea = g_SceneMan.GetScene()->GetArea(str); - pArea = pArea ? pArea : g_SceneMan.GetScene()->GetArea("Landing Zone"); - // If area is defined, save a copy so we can lock the LZ selection to within its boxes - if (pArea && !pArea->HasNoArea()) - m_LandingZoneArea[team] = *pArea; -*/ - } - - /////////////////////////////////////// - // Set up human players - - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - // Set the team associations with each screen displayed - g_CameraMan.SetScreenTeam(m_Team[player], ScreenOfPlayer(player)); - // And occlusion - g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(player)); - - // Allocate and (re)create the Inventory Menu GUIs - if (m_InventoryMenuGUI[player]) { - m_InventoryMenuGUI[player]->Destroy(); - } else { - m_InventoryMenuGUI[player] = new InventoryMenuGUI; - } - m_InventoryMenuGUI[player]->Create(&m_PlayerController[player]); - - // Allocate and (re)create the Editor GUIs - if (m_pEditorGUI[player]) - m_pEditorGUI[player]->Destroy(); - else - m_pEditorGUI[player] = new SceneEditorGUI; - m_pEditorGUI[player]->Create(&m_PlayerController[player]); - m_ReadyToStart[player] = false; - - // Allocate and (re)create the Buy GUIs - if (m_pBuyGUI[player]) - m_pBuyGUI[player]->Destroy(); - else - m_pBuyGUI[player] = new BuyMenuGUI; - m_pBuyGUI[player]->Create(&m_PlayerController[player]); - - // Load correct loadouts into buy menu if we're starting a non meta-game activity - if (m_pBuyGUI[player]->GetMetaPlayer() == Players::NoPlayer) { - int techModuleID = g_PresetMan.GetModuleID(GetTeamTech(GetTeamOfPlayer(player))); - - m_pBuyGUI[player]->SetNativeTechModule(techModuleID); - m_pBuyGUI[player]->SetForeignCostMultiplier(1.0); - m_pBuyGUI[player]->LoadAllLoadoutsFromFile(); - - // Change Editor GUI native tech module so it could load and show correct deployment prices - m_pEditorGUI[player]->SetNativeTechModule(techModuleID); - } + // If there was a last passenger and things after him, stuff all the items into his inventory + if (pLastPassenger) { + for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) + pLastPassenger->AddInventoryItem(*iItr); + } + // Otherwise, stuff it all stuff directly into the craft instead + else { + for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) + pDeliveryCraft->AddInventoryItem(*iItr); + } - //////////////////////////////////// - // GUI split screen setup - // If there are split screens, set up the GUIs to draw and their mouses to point correctly - if (g_FrameMan.IsInMultiplayerMode()) - { - m_pEditorGUI[player]->SetPosOnScreen(0, 0); - m_pBuyGUI[player]->SetPosOnScreen(0, 0); - } - else - { - if (g_FrameMan.GetScreenCount() > 1) - { - // Screen 1 Always upper left corner - if (ScreenOfPlayer(player) == 0) - { - m_pEditorGUI[player]->SetPosOnScreen(0, 0); - m_pBuyGUI[player]->SetPosOnScreen(0, 0); - } - else if (ScreenOfPlayer(player) == 1) - { - // If both splits, or just Vsplit, then in upper right quadrant - if ((g_FrameMan.GetVSplit() && !g_FrameMan.GetHSplit()) || (g_FrameMan.GetVSplit() && g_FrameMan.GetVSplit())) - { - m_pEditorGUI[player]->SetPosOnScreen(g_WindowMan.GetResX() / 2, 0); - m_pBuyGUI[player]->SetPosOnScreen(g_WindowMan.GetResX() / 2, 0); - } - // If only hsplit, then lower left quadrant - else - { - m_pEditorGUI[player]->SetPosOnScreen(0, g_WindowMan.GetResY() / 2); - m_pBuyGUI[player]->SetPosOnScreen(0, g_WindowMan.GetResY() / 2); - } - } - // Screen 3 is lower left quadrant - else if (ScreenOfPlayer(player) == 2) - { - m_pEditorGUI[player]->SetPosOnScreen(0, g_WindowMan.GetResY() / 2); - m_pBuyGUI[player]->SetPosOnScreen(0, g_WindowMan.GetResY() / 2); - } - // Screen 4 is lower right quadrant - else if (ScreenOfPlayer(player) == 3) - { - m_pEditorGUI[player]->SetPosOnScreen(g_WindowMan.GetResX() / 2, g_WindowMan.GetResY() / 2); - m_pBuyGUI[player]->SetPosOnScreen(g_WindowMan.GetResX() / 2, g_WindowMan.GetResY() / 2); - } + float spawnY = 0.0f; + if (g_SceneMan.GetTerrain() && g_SceneMan.GetTerrain()->GetOrbitDirection() == Directions::Down) { + spawnY = g_SceneMan.GetSceneHeight(); } - } - // Allocate and (re)create the banners - if (m_pBannerRed[player]) - m_pBannerRed[player]->Destroy(); - else - m_pBannerRed[player] = new GUIBanner; - m_pBannerRed[player]->Create("Base.rte/GUIs/Fonts/BannerFontRedReg.png", "Base.rte/GUIs/Fonts/BannerFontRedBlur.png", 8); - - // Allocate and (re)create the banners - if (m_pBannerYellow[player]) - m_pBannerYellow[player]->Destroy(); - else - m_pBannerYellow[player] = new GUIBanner; - m_pBannerYellow[player]->Create("Base.rte/GUIs/Fonts/BannerFontYellowReg.png", "Base.rte/GUIs/Fonts/BannerFontYellowBlur.png", 8); - - // Resetting the banner repeat counter - m_BannerRepeats[player] = 0; - - // Draw GO! game start notification, if it's a new game - if (m_ActivityState == ActivityState::NotStarted) { - m_pBannerYellow[player]->ShowText("GO!", GUIBanner::FLYBYLEFTWARD, 1000, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 500); - g_FrameMan.SetScreenText((player % 2 == 0) ? "Mine Gold and buy more firepower with the funds..." : "...then smash the competing brain to claim victory!", ScreenOfPlayer(player), 0); - } - - m_ActorCursor[player].Reset(); - m_LandingZone[player].Reset(); - - // Set the initial landing zones to be above the respective brains, but not for the observer player in a three player game - if (m_Brain[player] && !(m_PlayerCount == 3 && ScreenOfPlayer(player) == 3)) - { - // Also set the brain to be the selected actor at start - SwitchToActor(m_Brain[player], player, m_Team[player]); - m_ActorCursor[player] = m_Brain[player]->GetPos(); - m_LandingZone[player].m_X = m_Brain[player]->GetPos().m_X; - // Set the observation target to the brain, so that if/when it dies, the view flies to it in observation mode - m_ObservationTarget[player] = m_Brain[player]->GetPos(); - } - } - - // Set up the AI controllers for everyone - InitAIs(); - - // Start the game timer - m_GameTimer.Reset(); - - if (m_aLZCursor[0].empty()) - { - ContentFile cursorFile("Base.rte/GUIs/Indicators/LZArrowRedL.png"); - cursorFile.GetAsAnimation(m_aLZCursor[0], LZCURSORFRAMECOUNT); - cursorFile.SetDataPath("Base.rte/GUIs/Indicators/LZArrowGreenL.png"); - cursorFile.GetAsAnimation(m_aLZCursor[1], LZCURSORFRAMECOUNT); - cursorFile.SetDataPath("Base.rte/GUIs/Indicators/LZArrowBlueL.png"); - cursorFile.GetAsAnimation(m_aLZCursor[2], LZCURSORFRAMECOUNT); - cursorFile.SetDataPath("Base.rte/GUIs/Indicators/LZArrowYellowL.png"); - cursorFile.GetAsAnimation(m_aLZCursor[3], LZCURSORFRAMECOUNT); - } - - if (m_aObjCursor[0].empty()) - { - ContentFile cursorFile("Base.rte/GUIs/Indicators/ObjArrowRed.png"); - cursorFile.GetAsAnimation(m_aObjCursor[0], OBJARROWFRAMECOUNT); - cursorFile.SetDataPath("Base.rte/GUIs/Indicators/ObjArrowGreen.png"); - cursorFile.GetAsAnimation(m_aObjCursor[1], OBJARROWFRAMECOUNT); - cursorFile.SetDataPath("Base.rte/GUIs/Indicators/ObjArrowBlue.png"); - cursorFile.GetAsAnimation(m_aObjCursor[2], OBJARROWFRAMECOUNT); - cursorFile.SetDataPath("Base.rte/GUIs/Indicators/ObjArrowYellow.png"); - cursorFile.GetAsAnimation(m_aObjCursor[3], OBJARROWFRAMECOUNT); - } - - // Start the in-game music - g_AudioMan.ClearMusicQueue(); - g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/cc2g.ogg", 0); - g_AudioMan.QueueSilence(30); - g_AudioMan.QueueMusicStream("Base.rte/Music/Watts/Last Man.ogg"); - g_AudioMan.QueueSilence(30); - g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/cc2g.ogg"); - - return error; -} + // Delivery craft appear straight over the selected LZ + pDeliveryCraft->SetPos(Vector(m_LandingZone[player].m_X, spawnY)); + // pDeliveryCraft->SetPos(Vector(m_LandingZone[player].m_X, 300)); + pDeliveryCraft->SetTeam(team); + // TODO: The after-delivery AI mode needs to be set depending on what the user has set in teh LZ selection mode + pDeliveryCraft->SetControllerMode(Controller::CIM_AI); + pDeliveryCraft->SetAIMode(m_AIReturnCraft[player] ? Actor::AIMODE_DELIVER : Actor::AIMODE_STAY); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. + // Prepare the Delivery struct and stuff the ready to go craft in there + Delivery newDelivery; + // Pass ownership of the craft to the new Delivery struct + newDelivery.pCraft = pDeliveryCraft; + newDelivery.orderedByPlayer = player; + newDelivery.landingZone = m_LandingZone[player]; + newDelivery.multiOrderYOffset = 0; + newDelivery.delay = m_DeliveryDelay * pDeliveryCraft->GetDeliveryDelayMultiplier(); + newDelivery.timer.Reset(); -void GameActivity::SetPaused(bool pause) -{ - Activity::SetPaused(pause); -} + // Add the new Delivery to the queue + m_Deliveries[team].push_back(newDelivery); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. - -void GameActivity::End() -{ - Activity::End(); - - bool playerWon = false; - - // Disable control of actors.. will be handed over to the observation targets instead - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(player)); - - if (m_Team[player] == m_WinnerTeam) - { - playerWon = true; - // Set the winner's observation view to his controlled actors instead of his brain - if (m_ControlledActor[player] && g_MovableMan.IsActor(m_ControlledActor[player])) - m_ObservationTarget[player] = m_ControlledActor[player]->GetPos(); - } - } - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) { - if (MovableObject* asMo = dynamic_cast(itr->pCraft)) { - asMo->DestroyScriptState(); - } - delete itr->pCraft; - } - m_Deliveries[team].clear(); - } - - -/* Now controlled by the scripted activities - // Play the approriate tune on player win/lose - if (playerWon) - { -// Didn't work well, has gap between intro and loop tracks -// g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/uwinintro.ogg", 0); -// g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/uwinloop.ogg"); - g_AudioMan.ClearMusicQueue(); - // Loop it twice, nice tune! - g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/uwinfinal.ogg", 2); - g_AudioMan.QueueSilence(10); - g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg"); - } - else - { - g_AudioMan.ClearMusicQueue(); - g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/udiedfinal.ogg", 0); - g_AudioMan.QueueSilence(10); - g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg"); - } -*/ - - m_ActivityState = ActivityState::Over; - m_GameOverTimer.Reset(); -} + pDeliveryCraft = 0; + pLastPassenger = 0; + // Deduct cost from team's funds + m_TeamFunds[team] -= totalCost; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateEditing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: This is a special update step for when any player is still editing the -// scene. - -void GameActivity::UpdateEditing() -{ - // Editing the scene, just update the editor guis and see if players are ready to start or not - if (m_ActivityState != ActivityState::Editing) - return; - - /////////////////////////////////////////// - // Iterate through all human players - - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - m_pEditorGUI[player]->Update(); - - // Set the team associations with each screen displayed - g_CameraMan.SetScreenTeam(m_Team[player], ScreenOfPlayer(player)); - - // Check if the player says he's done editing, and if so, make sure he really is good to go - if (m_pEditorGUI[player]->GetEditorGUIMode() == SceneEditorGUI::DONEEDITING) - { - // See if a brain has been placed yet by this player - IN A VALID LOCATION - if (!m_pEditorGUI[player]->TestBrainResidence()) - { - // Hm not ready yet without resident brain in the right spot, so let user know - m_ReadyToStart[player] = false; - const Entity *pBrain = g_PresetMan.GetEntityPreset("Actor", "Brain Case"); - if (pBrain) - m_pEditorGUI[player]->SetCurrentObject(dynamic_cast(pBrain->Clone())); - m_pEditorGUI[player]->SetEditorGUIMode(SceneEditorGUI::INSTALLINGBRAIN); - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - g_FrameMan.SetScreenText("PLACE YOUR BRAIN IN A VALID SPOT FIRST!", ScreenOfPlayer(player), 250, 3500); - m_MessageTimer[player].Reset(); - } - // Ready to start - else - { - m_ReadyToStart[player] = true; - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - g_FrameMan.SetScreenText("READY to start - wait for others to finish...", ScreenOfPlayer(player), 333); - m_pEditorGUI[player]->SetEditorGUIMode(SceneEditorGUI::ADDINGOBJECT); - } - } - - // Keep showing ready message - if (m_ReadyToStart[player]) - g_FrameMan.SetScreenText("READY to start - wait for others to finish...", ScreenOfPlayer(player), 333); - } - - // Have all players flagged themselves as ready to start the game? - bool allReady = true; - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - if (!m_ReadyToStart[player]) - allReady = false; - } - - // YES, we are allegedly all ready to stop editing and start the game! - if (allReady) - { - // Make sure any players haven't moved or entombed their brains in the period after flagging themselves "done" - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - // See if a brain has been placed yet by this player - IN A VALID LOCATION - if (!m_pEditorGUI[player]->TestBrainResidence()) - { - // Hm not ready yet without resident brain in the right spot, so let user know - m_ReadyToStart[player] = false; - allReady = false; - const Entity *pBrain = g_PresetMan.GetEntityPreset("Actor", "Brain Case"); - if (pBrain) - m_pEditorGUI[player]->SetCurrentObject(dynamic_cast(pBrain->Clone())); - m_pEditorGUI[player]->SetEditorGUIMode(SceneEditorGUI::INSTALLINGBRAIN); - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - g_FrameMan.SetScreenText("PLACE YOUR BRAIN IN A VALID SPOT FIRST!", ScreenOfPlayer(player), 333, 3500); - m_MessageTimer[player].Reset(); - } - } - - // Still good to go?? - if (allReady) - { - // All resident brains are still in valid spots - place them into the simulation - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - // Place this player's resident brain into the simulation and set it as the player's assigned brain - g_SceneMan.GetScene()->PlaceResidentBrain(player, *this); - - // Still no brain of this player? Last ditch effort to find one and assign it to this player - if (!m_Brain[player]) - m_Brain[player] = g_MovableMan.GetUnassignedBrain(m_Team[player]); - // Um, something went wrong.. we're not done placing brains after all?? - if (!m_Brain[player]) - { - allReady = false; - // Get the brains back into residency so the players who are OK are still so - g_SceneMan.GetScene()->RetrieveResidentBrains(*this); - break; - } - - // Set the brain to be the selected actor at start - SwitchToActor(m_Brain[player], player, m_Team[player]); - m_ActorCursor[player] = m_Brain[player]->GetPos(); - m_LandingZone[player].m_X = m_Brain[player]->GetPos().m_X; - // Set the observation target to the brain, so that if/when it dies, the view flies to it in observation mode - m_ObservationTarget[player] = m_Brain[player]->GetPos(); - // CLear the messages before starting the game - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - // Reset the screen occlusion if any players are still in menus - g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(player)); - } - } - - // Still good to go?? then GO - if (allReady) - { - // START the game! - m_ActivityState = ActivityState::Running; - // Re-enable the AI's if we are done editing - DisableAIs(false); - InitAIs(); - // Reset the mouse value and pathfinding so it'll know about the newly placed stuff - g_UInputMan.SetMouseValueMagnitude(0); - g_SceneMan.GetScene()->ResetPathFinding(); - // Start the in-game track - g_AudioMan.ClearMusicQueue(); - g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/cc2g.ogg", 0); - g_AudioMan.QueueSilence(30); - g_AudioMan.QueueMusicStream("Base.rte/Music/Watts/Last Man.ogg"); - g_AudioMan.QueueSilence(30); - g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/cc2g.ogg"); - } - } -} + // Go 'ding!', but only if player is human, or it may be confusing + if (PlayerHuman(player)) + g_GUISound.ConfirmSound()->Play(player); + // Clear out the override purchase list, whether anything was in there or not, it should not override twice. + m_PurchaseOverride[player].clear(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this GameActivity. Supposed to be done every frame -// before drawing. - -void GameActivity::Update() -{ - Activity::Update(); - - // Avoid game logic when we're editing - if (m_ActivityState == ActivityState::Editing) - { - UpdateEditing(); - return; - } - - /////////////////////////////////////////// - // Iterate through all human players - - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - // The current player's team - int team = m_Team[player]; - if (team == Teams::NoTeam) - continue; - - // Temporary hack to avoid teh buy menu buy button to be pressed immediately after selecting an LZ for a previous order - bool skipBuyUpdate = false; - - // Set the team associations with each screen displayed - g_CameraMan.SetScreenTeam(team, ScreenOfPlayer(player)); - - ////////////////////////////////////////////////////// - // Assure that Controlled Actor is a safe pointer - - // Only allow this if player's brain is intact - if (m_Brain[player] && !m_Brain[player]->IsDead() ) - { - // Note that we have now had a brain - m_HadBrain[player] = true; - - // Tracking normally - if (m_ViewState[player] == ViewState::Normal) - { - // Get a next actor if there isn't one - if (!m_ControlledActor[player]) - SwitchToNextActor(player, team); - - // Continually set the observation target to the brain during play, so that if/when it dies, the view flies to it in observation mode - if (m_ActivityState != ActivityState::Over && m_ViewState[player] != ViewState::Observe) - m_ObservationTarget[player] = m_Brain[player]->GetPos(); - - // Save the location of the currently controlled actor so we can know where to watch if he died on us - if (g_MovableMan.IsActor(m_ControlledActor[player])) - { - m_DeathViewTarget[player] = m_ControlledActor[player]->GetPos(); - m_DeathTimer[player].Reset(); - } - // Add delay after death before switching so the death comedy can be witnessed - // Died, so enter death watch mode - else - { - LoseControlOfActor(player); - } - } - // Ok, done watching death comedy, now automatically switch - else if (m_ViewState[player] == ViewState::DeathWatch && m_DeathTimer[player].IsPastSimMS(1500)) - { - // Get a next actor if there isn't one - if (!m_ControlledActor[player]) - SwitchToNextActor(player, team); - - // If currently focused actor died, get next one - if (!g_MovableMan.IsActor(m_ControlledActor[player])) - SwitchToNextActor(player, team); - - if (m_ViewState[player] != ViewState::ActorSelect) - m_ViewState[player] = ViewState::Normal; - } - // Any other viewing mode and the actor died... go to deathwatch - else if (m_ControlledActor[player] && !g_MovableMan.IsActor(m_ControlledActor[player])) - { - LoseControlOfActor(player); - } - } - // Player brain is now gone! Remove any control he may have had - else if (m_HadBrain[player]) - { - if (m_ControlledActor[player] && g_MovableMan.IsActor(m_ControlledActor[player])) - m_ControlledActor[player]->SetControllerMode(Controller::CIM_AI); - m_ControlledActor[player] = 0; - m_ViewState[player] = ViewState::Observe; - } - // Never had a brain, and no actor is selected, so just select the first one we do have - else if (m_ViewState[player] != ViewState::Observe && !g_MovableMan.IsActor(m_ControlledActor[player])) - { - // Only try to switch if there's somehting to switch to - if (!g_MovableMan.GetTeamRoster(team)->empty()) - SwitchToNextActor(player, team); - else - m_ControlledActor[player] = 0; - } - - // Player-commanded actor switching - if (m_ViewState[player] != ViewState::Observe) { - // Switch to brain actor directly if the player wants to - if (m_PlayerController[player].IsState(ACTOR_BRAIN) && m_ViewState[player] != ViewState::ActorSelect) { - SwitchToActor(m_Brain[player], player, team); - m_ViewState[player] = ViewState::Normal; - } else if (m_PlayerController[player].IsState(ACTOR_NEXT) && m_ViewState[player] != ViewState::ActorSelect && !m_pBuyGUI[player]->IsVisible() && !m_LuaLockActor[player]) { - // Switch to next actor if the player wants to. Don't do it while the buy menu is open - if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { - m_ControlledActor[player]->GetPieMenu()->SetEnabled(false); - } - - SwitchToNextActor(player, team); - m_ViewState[player] = ViewState::Normal; - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - } - // Switch to prev actor if the player wants to. Don't do it while the buy menu is open - else if (m_PlayerController[player].IsState(ACTOR_PREV) && m_ViewState[player] != ViewState::ActorSelect && !m_pBuyGUI[player]->IsVisible()) { - if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { - m_ControlledActor[player]->GetPieMenu()->SetEnabled(false); - } - - SwitchToPrevActor(player, team); - m_ViewState[player] = ViewState::Normal; - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - } else if (m_ViewState[player] != ViewState::ActorSelect && !m_pBuyGUI[player]->IsVisible() && !m_LuaLockActor[player] && (m_PlayerController[player].IsState(ACTOR_NEXT_PREP) || m_PlayerController[player].IsState(ACTOR_PREV_PREP))) { - // Go into manual actor select mode if either actor switch buttons are held for a duration - if (m_ActorSelectTimer[player].IsPastRealMS(250)) { - // Set cursor to start at the head of controlled actor - if (m_ControlledActor[player]) { - // Give switched from actor an AI controller - m_ControlledActor[player]->SetControllerMode(Controller::CIM_AI); - m_ControlledActor[player]->GetController()->SetDisabled(false); - m_ActorCursor[player] = m_ControlledActor[player]->GetCPUPos(); - m_CursorTimer.Reset(); - } - - m_ViewState[player] = ViewState::ActorSelect; - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - } - } else { - m_ActorSelectTimer[player].Reset(); - } - } - - //////////////////////////////////// - // Update sceneman scroll targets - - if (m_ViewState[player] == ViewState::Observe) - { - // If we're observing game over state, freeze the view for a bit so the player's input doesn't ruin the focus - if (!(m_ActivityState == ActivityState::Over && !m_GameOverTimer.IsPastRealMS(1000))) - { - // Get cursor input - m_PlayerController[player].RelativeCursorMovement(m_ObservationTarget[player], 1.2f); - } - // Set the view to the observation position - g_SceneMan.ForceBounds(m_ObservationTarget[player]); - g_CameraMan.SetScrollTarget(m_ObservationTarget[player], 0.1, ScreenOfPlayer(player)); - } - - /////////////////////////////////////////////////// - // Manually selecting a new actor to switch to - - else if (m_ViewState[player] == ViewState::ActorSelect) - { - // Continuously display message - g_FrameMan.SetScreenText("Select a body to switch control to...", ScreenOfPlayer(player)); - // Get cursor input - m_PlayerController[player].RelativeCursorMovement(m_ActorCursor[player]); - - // Find the actor closest to the cursor, if any within the radius - Vector markedDistance; - Actor *pMarkedActor = g_MovableMan.GetClosestTeamActor(team, player, m_ActorCursor[player], g_SceneMan.GetSceneWidth(), markedDistance, true); -// Actor *pMarkedActor = g_MovableMan.GetClosestTeamActor(team, player, m_ActorCursor[player], g_FrameMan.GetPlayerScreenWidth() / 4); - - // Player canceled selection of actor - if (m_PlayerController[player].IsState(PRESS_SECONDARY)) { - // Reset the mouse so the actor doesn't change aim because mouse has been moved - if (m_PlayerController[player].IsMouseControlled()) { g_UInputMan.SetMouseValueMagnitude(0); } - - m_ViewState[player] = ViewState::Normal; - g_GUISound.UserErrorSound()->Play(player); - if (m_ControlledActor[player]) { - if (m_ControlledActor[player]->GetPieMenu()) { - m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); - } - m_ControlledActor[player]->SetControllerMode(Controller::CIM_PLAYER, player); - } - if (pMarkedActor && pMarkedActor->GetPieMenu()) { - pMarkedActor->GetPieMenu()->DoDisableAnimation(); - } - } - // Player is done selecting new actor; switch to it if we have anything marked - else if (m_PlayerController[player].IsState(ACTOR_NEXT) || m_PlayerController[player].IsState(ACTOR_PREV) || m_PlayerController[player].IsState(PRESS_FACEBUTTON) || m_PlayerController[player].IsState(PRESS_PRIMARY)) { - // Reset the mouse so the actor doesn't change aim because mouse has been moved - if (m_PlayerController[player].IsMouseControlled()) { g_UInputMan.SetMouseValueMagnitude(0); } + return true; + } - if (pMarkedActor) { - SwitchToActor(pMarkedActor, player, team); - } else { - g_GUISound.UserErrorSound()->Play(player); - } + return false; + } - m_ViewState[player] = ViewState::Normal; - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - // Flash the same actor, jsut to show the control went back to him - if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { - m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); - } - } - else if (pMarkedActor && pMarkedActor->GetPieMenu()) { - int quarterFrameBuffer = g_FrameMan.GetPlayerFrameBufferWidth(player) / 4; - if (markedDistance.MagnitudeIsGreaterThan(static_cast(quarterFrameBuffer))) { - pMarkedActor->GetPieMenu()->Wobble(); - } else { - pMarkedActor->GetPieMenu()->FreezeAtRadius(30); - } - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetupPlayers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Precalculates the player-to-screen index map, counts the number of + // active players, teams etc. - // Set the view to the cursor pos - g_SceneMan.ForceBounds(m_ActorCursor[player]); - g_CameraMan.SetScrollTarget(m_ActorCursor[player], 0.1, ScreenOfPlayer(player)); + void GameActivity::SetupPlayers() { + Activity::SetupPlayers(); - if (m_pLastMarkedActor[player]) { - if (!g_MovableMan.ValidMO(m_pLastMarkedActor[player])) { - m_pLastMarkedActor[player] = nullptr; - } else if (m_pLastMarkedActor[player] != pMarkedActor && m_pLastMarkedActor[player]->GetPieMenu()) { - m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); - } - } - if (pMarkedActor) { m_pLastMarkedActor[player] = pMarkedActor; } - } - - /////////////////////////////////////////////////// - // Selecting points on the scene for the AI to go to - - else if (m_ViewState[player] == ViewState::AIGoToPoint) - { - // Continuously display message - g_FrameMan.SetScreenText("Set waypoints for the AI to go to...", ScreenOfPlayer(player)); - // Get cursor input - m_PlayerController[player].RelativeCursorMovement(m_ActorCursor[player]); - - // If we are pointing to an actor to follow, then snap cursor to that actor's position - Actor *pTargetActor = 0; - Vector distance; - if (pTargetActor = g_MovableMan.GetClosestActor(m_ActorCursor[player], 40, distance, m_ControlledActor[player]); pTargetActor && pTargetActor->GetPieMenu()) { - if (m_pLastMarkedActor[player] && m_pLastMarkedActor[player]->GetPieMenu()) { - m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); - } - pTargetActor->GetPieMenu()->FreezeAtRadius(15); - m_pLastMarkedActor[player] = pTargetActor; - } else if (m_pLastMarkedActor[player] && m_pLastMarkedActor[player]->GetPieMenu()) { - m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); - } + // Add the locked cpu team that can't have any players + if (m_CPUTeam != Teams::NoTeam && !m_TeamActive[m_CPUTeam]) { + m_TeamCount++; + // Also activate the CPU team + m_TeamActive[m_CPUTeam] = true; + } - // Set the view to the cursor pos - g_SceneMan.ForceBounds(m_ActorCursor[player]); - g_CameraMan.SetScrollTarget(m_ActorCursor[player], 0.1, ScreenOfPlayer(player)); - - // Draw the actor's waypoints - m_ControlledActor[player]->DrawWaypoints(true); - - // Disable the actor's controller - m_ControlledActor[player]->GetController()->SetDisabled(true); - - // Player is done setting waypoints - if (m_PlayerController[player].IsState(PRESS_SECONDARY) || m_PlayerController[player].IsState(ACTOR_NEXT_PREP) || m_PlayerController[player].IsState(ACTOR_PREV_PREP)) { - // Stop drawing the waypoints -// m_ControlledActor[player]->DrawWaypoints(false); - // Update the player's move path now to the first waypoint set - m_ControlledActor[player]->UpdateMovePath(); - // Give player control back to actor - m_ControlledActor[player]->GetController()->SetDisabled(false); - // Switch back to normal view - m_ViewState[player] = ViewState::Normal; - // Stop displaying the message - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - if (m_pLastMarkedActor[player] && m_pLastMarkedActor[player]->GetPieMenu()) { - m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); - } - } - // Player set a new waypoint - else if (m_ControlledActor[player] && m_PlayerController[player].IsState(PRESS_FACEBUTTON) || m_PlayerController[player].IsState(PRESS_PRIMARY)) - { -// TODO: Sound? - // If we are pointing to an actor to follow, tehn give that kind of waypoint command - if (pTargetActor) - m_ControlledActor[player]->AddAIMOWaypoint(pTargetActor); - // Just pointing into somewhere in the scene, so give that command - else - m_ControlledActor[player]->AddAISceneWaypoint(m_ActorCursor[player]); - // Update the player's move path now to the first waypoint set - m_ControlledActor[player]->UpdateMovePath(); - if (m_pLastMarkedActor[player] && m_pLastMarkedActor[player]->GetPieMenu()) { - m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); - } - } - } - else if (m_ViewState[player] == ViewState::UnitSelectCircle) - { - // Continuously display message - g_FrameMan.SetScreenText("Select units to group...", ScreenOfPlayer(player)); - - m_PlayerController[player].RelativeCursorMovement(m_ActorCursor[player]); - - Vector relativeToActor = m_ActorCursor[player] - m_ControlledActor[player]->GetPos(); - - float sceneWidth = static_cast(g_SceneMan.GetSceneWidth()); - float seamMinimum = 350.0F; - - //Check if we crossed the seam - if (g_SceneMan.GetScene()->WrapsX()) { - float halfSceneWidth = sceneWidth * 0.5F; - if (relativeToActor.MagnitudeIsGreaterThan(std::max(halfSceneWidth, seamMinimum))) { - if (m_ActorCursor->m_X < halfSceneWidth) { - relativeToActor = m_ActorCursor[player] + Vector(sceneWidth, 0) - m_ControlledActor[player]->GetPos(); - } else { - relativeToActor = m_ActorCursor[player] - Vector(sceneWidth, 0) - m_ControlledActor[player]->GetPos(); - } - } + // Don't clear a CPU team's active status though + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (m_TeamIsCPU[team] && !m_TeamActive[team]) { + m_TeamCount++; + m_TeamActive[team] = true; } + } + } - // Limit selection range - relativeToActor = relativeToActor.CapMagnitude(seamMinimum); - m_ActorCursor[player] = m_ControlledActor[player]->GetPos() + relativeToActor; - - bool wrapped; - - // Set the view to the cursor pos - wrapped = g_SceneMan.ForceBounds(m_ActorCursor[player]); - //g_CameraMan.SetScrollTarget(m_ActorCursor[player], 0.1, wrapped, ScreenOfPlayer(player)); - - // Set the view to the actor pos - Vector scrollPos = Vector(m_ControlledActor[player]->GetPos()); - g_SceneMan.ForceBounds(scrollPos); - g_CameraMan.SetScrollTarget(scrollPos, 0.1, ScreenOfPlayer(player)); - - // Disable the actor's controller - m_ControlledActor[player]->GetController()->SetDisabled(true); - - // Player is done setting waypoints - if (m_PlayerController[player].IsState(PRESS_SECONDARY) || m_PlayerController[player].IsState(ACTOR_NEXT_PREP) || m_PlayerController[player].IsState(ACTOR_PREV_PREP)) { - // Give player control back to actor - m_ControlledActor[player]->GetController()->SetDisabled(false); - // Switch back to normal view - m_ViewState[player] = ViewState::Normal; - // Stop displaying the message - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - } - // Player set a new waypoint - else if (m_ControlledActor[player] && m_PlayerController[player].IsState(PRESS_FACEBUTTON) || m_PlayerController[player].IsState(PRESS_PRIMARY)) - { - // m_ControlledActor[player]->AddAISceneWaypoint(m_ActorCursor[player]); - // Give player control back to actor - m_ControlledActor[player]->GetController()->SetDisabled(false); - // Switch back to normal view - m_ViewState[player] = ViewState::Normal; - // Stop displaying the message - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - - //Switch commander to sentry mode - m_ControlledActor[player]->SetAIMode(Actor::AIMODE_SENTRY); - - // Detect nearby actors and attach them to commander - float sqrRadius = g_SceneMan.ShortestDistance(m_ActorCursor[player],m_ControlledActor[player]->GetPos(), true).GetSqrMagnitude(); - - Actor *pActor = 0; - Actor *pFirstActor = 0; - - // Get the first one - pFirstActor = pActor = g_MovableMan.GetNextTeamActor(m_ControlledActor[player]->GetTeam()); - - do - { - // Set up commander if actor is not player controlled and not brain - if (pActor && !pActor->GetController()->IsPlayerControlled() && !pActor->IsInGroup("Brains")) - { - // If human, set appropriate AI mode - if (dynamic_cast(pActor) || dynamic_cast(pActor)) - if (g_SceneMan.ShortestDistance(m_ControlledActor[player]->GetPos(), pActor->GetPos(),true).GetSqrMagnitude() < sqrRadius) - { - pActor->FlashWhite(); - pActor->ClearAIWaypoints(); - pActor->SetAIMode(Actor::AIMODE_SQUAD); - pActor->AddAIMOWaypoint(m_ControlledActor[player]); - pActor->UpdateMovePath(); // Make sure pActor has m_ControlledActor registered as an AIMOWaypoint - } - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. + + int GameActivity::Start() { + // Set the split screen config before the Scene (and it SceneLayers, specifially) are loaded + int humanCount = GetHumanCount(); + // Depending on the resolution aspect ratio, split first horizontally (if wide screen) + if (((float)g_WindowMan.GetResX() / (float)g_WindowMan.GetResY()) >= 1.6) + g_FrameMan.ResetSplitScreens(humanCount > 1, humanCount > 2); + // or vertically (if 4:3-ish) + else + g_FrameMan.ResetSplitScreens(humanCount > 2, humanCount > 1); - // Next! - pActor = g_MovableMan.GetNextTeamActor(team, pActor); - } - while (pActor && pActor != pFirstActor); - } - } - /////////////////////////////////////////////////// - // Selecting LZ, a place for the craft to land - - else if (m_ViewState[player] == ViewState::LandingZoneSelect) - { - g_FrameMan.SetScreenText("Choose your landing zone... Hold UP or DOWN to place multiple orders", ScreenOfPlayer(player)); - - // Save the x pos so we can see which direction the user is moving it - float prevLZX = m_LandingZone[player].m_X; - - // See if there's analog input - if (m_PlayerController[player].GetAnalogMove().m_X > 0.1) - m_LandingZone[player].m_X += m_PlayerController[player].GetAnalogMove().m_X * 8; - // Try the mouse - else if (!m_PlayerController[player].GetMouseMovement().IsZero()) - m_LandingZone[player].m_X += m_PlayerController[player].GetMouseMovement().m_X; - // Digital movement - else - { - if (m_PlayerController[player].IsState(MOVE_RIGHT)) - m_LandingZone[player].m_X += 8; - else if (m_PlayerController[player].IsState(MOVE_LEFT)) - m_LandingZone[player].m_X -= 8; - } - - // Limit the LZ selection to the special LZ Area:s both specified in the derived Activity and moving with the brain - if (!m_LandingZoneArea[m_Team[player]].HasNoArea()) - { - // Add up the static LZ loaded from the Scene to the one(s) around the player's team's brains - Scene::Area totalLZ(m_LandingZoneArea[m_Team[player]]); -/* This whole concept kinda sucks - defensive AA robots are more fun way to go to prevent bumrushing the brain with craft - for (int p = Players::PlayerOne; p < Players::MaxPlayerCount; ++p) - { - if (!(m_IsActive[p] && m_IsHuman[p] && m_BrainLZWidth[p] > 0)) - continue; - // Same team as this player and has a brain - if (m_Brain[p] && m_Team[p] == m_Team[player]) - { - Box brainBox(Vector(0, 0), m_BrainLZWidth[p], 100); - // Center the brain LZ box aroud the player's brain - brainBox.SetCenter(m_Brain[p]->GetPos()); - // Add it to the total LZ - totalLZ.AddBox(brainBox); - } - } -*/ - // Move the actual LZ cursor to within the valid LZ Area. We pass in 0 for direction so it doesn't try to wrap around on wrapping maps. - totalLZ.MovePointInsideX(m_LandingZone[player].m_X, 0); - } - - // Interface for the craft AI post-delivery mode - if (m_PlayerController[player].IsState(PRESS_DOWN)) { - if (m_AIReturnCraft[player]) { g_GUISound.SelectionChangeSound()->Play(player); } - - m_AIReturnCraft[player] = false; - } else if (m_PlayerController[player].IsState(PRESS_UP)) { - if (!m_AIReturnCraft[player]) { g_GUISound.SelectionChangeSound()->Play(player); } - - m_AIReturnCraft[player] = true; - } + int error = Activity::Start(); + if (error < 0) + return error; - // Player canceled the order while selecting LZ - can't be done in pregame - if (m_PlayerController[player].IsState(PRESS_SECONDARY) && m_ActivityState != ActivityState::PreGame) - { - // Switch back to normal view - m_ViewState[player] = ViewState::Normal; - // Play err sound to indicate cancellation - g_FrameMan.SetScreenText("Order canceled!", ScreenOfPlayer(player), 333); - m_MessageTimer[player].Reset(); - g_GUISound.UserErrorSound()->Play(player); - // Flash the same actor, jsut to show the control went back to him - if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { - m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); - } - } else if (m_PlayerController[player].IsState(PRESS_FACEBUTTON) || m_PlayerController[player].IsState(PRESS_PRIMARY)) { - m_LandingZone[player].m_Y = 0; - float lzOffsetY = 0; - // Holding up or down will allow the player to make multiple orders without exiting the delivery phase. TODO: this should probably have a cooldown? - if (!m_PlayerController[player].IsState(MOVE_UP) && !m_PlayerController[player].IsState(MOVE_DOWN)) { - m_LandingZone[player].m_Y = g_SceneMan.FindAltitude(m_LandingZone[player], g_SceneMan.GetSceneHeight(), 10, true); - if (!g_MovableMan.GetNextTeamActor(team)) { - m_ObservationTarget[player] = m_LandingZone[player]; - m_ViewState[player] = ViewState::Observe; - } else { - m_ViewState[player] = ViewState::Normal; - } - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { - m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); - } + m_WinnerTeam = Teams::NoTeam; - CreateDelivery(player); - } else { - // Place the new marker above the cursor so that they don't intersect with each other. - lzOffsetY += m_AIReturnCraft[player] ? -32.0F : 32.0F; - if (g_SceneMan.GetTerrain()->GetOrbitDirection() == Directions::Down) { - lzOffsetY *= -1.0f; - } + //////////////////////////////// + // Set up teams - m_LandingZone[player].m_Y = g_SceneMan.FindAltitude(m_LandingZone[player], g_SceneMan.GetSceneHeight(), 10, true) + lzOffsetY; + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; - if (m_pBuyGUI[player]->GetTotalOrderCost() > GetTeamFunds(team)) { - g_GUISound.UserErrorSound()->Play(player); - m_FundsChanged[team] = true; - if (!g_MovableMan.GetNextTeamActor(team)) { - m_ObservationTarget[player] = m_LandingZone[player]; - m_ViewState[player] = ViewState::Observe; - } else { - m_ViewState[player] = ViewState::Normal; - } - } else { - CreateDelivery(player); - m_Deliveries[team].rbegin()->multiOrderYOffset = lzOffsetY; - } - } - // Revert the Y offset so that the cursor doesn't flinch. - m_LandingZone[player].m_Y -= lzOffsetY; + m_Deliveries[team].clear(); + + // Clear delivery queues + for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) { + delete itr->pCraft; } - g_SceneMan.ForceBounds(m_LandingZone[player]); - - // Interpolate the LZ altitude to the height of the highest terrain point at the player-chosen X - float prevHeight = m_LandingZone[player].m_Y; - - float viewOffset = g_FrameMan.GetPlayerScreenHeight() / 4; - m_LandingZone[player].m_Y = 0.0f; - if (g_SceneMan.GetTerrain() && g_SceneMan.GetTerrain()->GetOrbitDirection() == Directions::Down) { - m_LandingZone[player].m_Y = g_SceneMan.GetSceneHeight(); - viewOffset *= -1; - } - - m_LandingZone[player].m_Y = prevHeight + ((g_SceneMan.FindAltitude(m_LandingZone[player], g_SceneMan.GetSceneHeight(), 10, true) - prevHeight) * 0.2); - - // Set the view to a little above the LZ position - Vector viewTarget(m_LandingZone[player].m_X, m_LandingZone[player].m_Y - viewOffset); - g_CameraMan.SetScrollTarget(viewTarget, 0.1, ScreenOfPlayer(player)); - } - - //////////////////////////// - // Deathwatching - - else if (m_ViewState[player] == ViewState::DeathWatch) - { - // Continuously deathwatch message - g_FrameMan.SetScreenText("Lost control of remote body!", ScreenOfPlayer(player)); - // Don't move anything, just stay put watching the death funnies - g_CameraMan.SetScrollTarget(m_DeathViewTarget[player], 0.1, ScreenOfPlayer(player)); - } - - //////////////////////////////////////////////////// - // Normal scrolling to view the currently controlled Actor - // But only if we're not editing something, because editor will scroll the screen himself - // and double scrolling will cause CC gitch when we'll cross the seam - else if (m_ControlledActor[player] && m_ActivityState != ActivityState::Editing && m_ActivityState != ActivityState::PreGame) - { - g_CameraMan.SetScrollTarget(m_ControlledActor[player]->GetViewPoint(), 0.1, ScreenOfPlayer(player)); - } - - if (m_ControlledActor[player] && m_ViewState[player] != ViewState::DeathWatch && m_ViewState[player] != ViewState::ActorSelect && m_ViewState[player] != ViewState::AIGoToPoint && m_ViewState[player] != ViewState::UnitSelectCircle) { - PieMenu *controlledActorPieMenu = m_ControlledActor[player]->GetPieMenu(); - if (controlledActorPieMenu && m_ControlledActor[player]->GetController()->IsState(PIE_MENU_ACTIVE)) { - if (!m_BuyMenuEnabled && controlledActorPieMenu->IsEnabling()) { - controlledActorPieMenu->RemovePieSlicesByType(PieSlice::SliceType::BuyMenu); - } + m_Deliveries[team].clear(); + /* This is taken care of by the individual Activity logic + // See if there are specified landing zone areas defined in the scene + char str[64]; + std::snprintf(str, sizeof(str), "LZ Team %d", team + 1); + Scene::Area *pArea = g_SceneMan.GetScene()->GetArea(str); + pArea = pArea ? pArea : g_SceneMan.GetScene()->GetArea("Landing Zone"); + // If area is defined, save a copy so we can lock the LZ selection to within its boxes + if (pArea && !pArea->HasNoArea()) + m_LandingZoneArea[team] = *pArea; + */ + } - if (controlledActorPieMenu->IsEnabled() && controlledActorPieMenu->HasSubPieMenuOpen() && m_InventoryMenuGUI[player]->GetMenuMode() == InventoryMenuGUI::MenuMode::Carousel) { - m_InventoryMenuGUI[player]->SetEnabled(false); - } else if (m_InventoryMenuGUI[player]->GetMenuMode() == InventoryMenuGUI::MenuMode::Carousel || !m_InventoryMenuGUI[player]->IsVisible()) { - m_InventoryMenuGUI[player]->SetMenuMode(InventoryMenuGUI::MenuMode::Carousel); - m_InventoryMenuGUI[player]->EnableIfNotEmpty(); - } - } else if (!m_PlayerController[player].IsState(PIE_MENU_ACTIVE) && m_InventoryMenuGUI[player]->GetMenuMode() == InventoryMenuGUI::MenuMode::Carousel) { - m_InventoryMenuGUI[player]->SetEnabled(false); + /////////////////////////////////////// + // Set up human players + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + // Set the team associations with each screen displayed + g_CameraMan.SetScreenTeam(m_Team[player], ScreenOfPlayer(player)); + // And occlusion + g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(player)); + + // Allocate and (re)create the Inventory Menu GUIs + if (m_InventoryMenuGUI[player]) { + m_InventoryMenuGUI[player]->Destroy(); + } else { + m_InventoryMenuGUI[player] = new InventoryMenuGUI; } + m_InventoryMenuGUI[player]->Create(&m_PlayerController[player]); - if (PieSlice::SliceType command = controlledActorPieMenu->GetPieCommand(); command != PieSlice::SliceType::NoType) { - // AI mode commands that need extra points set in special view modes here - //TODO I don't think these viewstates are actually used?! - if (command == PieSlice::SliceType::Sentry) { - m_ViewState[player] = ViewState::AISentryPoint; - } else if (command == PieSlice::SliceType::Patrol) { - m_ViewState[player] = ViewState::AIPatrolPoints; - } else if (command == PieSlice::SliceType::GoldDig) { - m_ViewState[player] = ViewState::AIGoldDigPoint; - } else if (command == PieSlice::SliceType::GoTo) { - m_ViewState[player] = ViewState::AIGoToPoint; - m_ControlledActor[player]->ClearAIWaypoints(); - m_ActorCursor[player] = m_ControlledActor[player]->GetPos(); - m_ControlledActor[player]->GetController()->SetDisabled(true); - } else if (command == PieSlice::SliceType::FormSquad) { - //Find out if we have any connected units, and disconnect them - bool isCommander = false; - - Actor *pActor = 0; - Actor *pFirstActor = 0; + // Allocate and (re)create the Editor GUIs + if (m_pEditorGUI[player]) + m_pEditorGUI[player]->Destroy(); + else + m_pEditorGUI[player] = new SceneEditorGUI; + m_pEditorGUI[player]->Create(&m_PlayerController[player]); + m_ReadyToStart[player] = false; - pFirstActor = pActor = g_MovableMan.GetNextTeamActor(m_ControlledActor[player]->GetTeam()); + // Allocate and (re)create the Buy GUIs + if (m_pBuyGUI[player]) + m_pBuyGUI[player]->Destroy(); + else + m_pBuyGUI[player] = new BuyMenuGUI; + m_pBuyGUI[player]->Create(&m_PlayerController[player]); - // Reset commander if we have any subordinates - do { - if (pActor) { - // Set appropriate AI mode - if (dynamic_cast(pActor) || dynamic_cast(pActor)) - if (pActor->GetAIMOWaypointID() == m_ControlledActor[player]->GetID()) { - pActor->FlashWhite(); - pActor->ClearAIWaypoints(); - pActor->SetAIMode((Actor::AIMode)m_ControlledActor[player]->GetAIMode()); // Inherit the leader's AI mode - isCommander = true; - } - } - pActor = g_MovableMan.GetNextTeamActor(team, pActor); - } while (pActor && pActor != pFirstActor); + // Load correct loadouts into buy menu if we're starting a non meta-game activity + if (m_pBuyGUI[player]->GetMetaPlayer() == Players::NoPlayer) { + int techModuleID = g_PresetMan.GetModuleID(GetTeamTech(GetTeamOfPlayer(player))); - //Now turn on selection UI, if we didn't disconnect anyone - if (!isCommander) { - m_ViewState[player] = ViewState::UnitSelectCircle; - // Set cursor to the actor - m_ActorCursor[player] = m_ControlledActor[player]->GetPos() + Vector(50, -50); - // Disable Actor's controller while we set the waypoints - m_ControlledActor[player]->GetController()->SetDisabled(true); - if (controlledActorPieMenu) { - controlledActorPieMenu->SetEnabled(false); + m_pBuyGUI[player]->SetNativeTechModule(techModuleID); + m_pBuyGUI[player]->SetForeignCostMultiplier(1.0); + m_pBuyGUI[player]->LoadAllLoadoutsFromFile(); + + // Change Editor GUI native tech module so it could load and show correct deployment prices + m_pEditorGUI[player]->SetNativeTechModule(techModuleID); + } + + //////////////////////////////////// + // GUI split screen setup + // If there are split screens, set up the GUIs to draw and their mouses to point correctly + if (g_FrameMan.IsInMultiplayerMode()) { + m_pEditorGUI[player]->SetPosOnScreen(0, 0); + m_pBuyGUI[player]->SetPosOnScreen(0, 0); + } else { + if (g_FrameMan.GetScreenCount() > 1) { + // Screen 1 Always upper left corner + if (ScreenOfPlayer(player) == 0) { + m_pEditorGUI[player]->SetPosOnScreen(0, 0); + m_pBuyGUI[player]->SetPosOnScreen(0, 0); + } else if (ScreenOfPlayer(player) == 1) { + // If both splits, or just Vsplit, then in upper right quadrant + if ((g_FrameMan.GetVSplit() && !g_FrameMan.GetHSplit()) || (g_FrameMan.GetVSplit() && g_FrameMan.GetVSplit())) { + m_pEditorGUI[player]->SetPosOnScreen(g_WindowMan.GetResX() / 2, 0); + m_pBuyGUI[player]->SetPosOnScreen(g_WindowMan.GetResX() / 2, 0); + } + // If only hsplit, then lower left quadrant + else { + m_pEditorGUI[player]->SetPosOnScreen(0, g_WindowMan.GetResY() / 2); + m_pBuyGUI[player]->SetPosOnScreen(0, g_WindowMan.GetResY() / 2); } } - } else if (command == PieSlice::SliceType::BuyMenu) { - m_pBuyGUI[player]->SetEnabled(true); - skipBuyUpdate = true; - } else if (command == PieSlice::SliceType::FullInventory) { - controlledActorPieMenu->SetEnabled(false); - m_InventoryMenuGUI[player]->SetEnabled(false); - m_InventoryMenuGUI[player]->SetMenuMode(InventoryMenuGUI::MenuMode::Full); - m_InventoryMenuGUI[player]->SetEnabled(true); + // Screen 3 is lower left quadrant + else if (ScreenOfPlayer(player) == 2) { + m_pEditorGUI[player]->SetPosOnScreen(0, g_WindowMan.GetResY() / 2); + m_pBuyGUI[player]->SetPosOnScreen(0, g_WindowMan.GetResY() / 2); + } + // Screen 4 is lower right quadrant + else if (ScreenOfPlayer(player) == 3) { + m_pEditorGUI[player]->SetPosOnScreen(g_WindowMan.GetResX() / 2, g_WindowMan.GetResY() / 2); + m_pBuyGUI[player]->SetPosOnScreen(g_WindowMan.GetResX() / 2, g_WindowMan.GetResY() / 2); + } } - m_ControlledActor[player]->HandlePieCommand(command); } - m_InventoryMenuGUI[player]->SetInventoryActor(m_ControlledActor[player]); - m_InventoryMenuGUI[player]->Update(); - } + // Allocate and (re)create the banners + if (m_pBannerRed[player]) + m_pBannerRed[player]->Destroy(); + else + m_pBannerRed[player] = new GUIBanner; + m_pBannerRed[player]->Create("Base.rte/GUIs/Fonts/BannerFontRedReg.png", "Base.rte/GUIs/Fonts/BannerFontRedBlur.png", 8); - /////////////////////////////////////// - // Update Buy Menu GUIs - - // Enable or disable the Buy Menus if the brain is selected, Skip if an LZ selection button press was just performed - if (!skipBuyUpdate) - { -// m_pBuyGUI[player]->SetEnabled(m_ControlledActor[player] == m_Brain[player] && m_ViewState[player] != ViewState::LandingZoneSelect && m_ActivityState != ActivityState::Over); - m_pBuyGUI[player]->Update(); - } - - // Trap the mouse if we're in gameplay and not in menus - g_UInputMan.TrapMousePos(!m_pBuyGUI[player]->IsEnabled() && !m_InventoryMenuGUI[player]->IsEnabledAndNotCarousel() && !m_LuaLockActor[player], player); - - // Start LZ picking mode if a purchase was made - if (m_pBuyGUI[player]->PurchaseMade()) - { - m_LZCursorWidth[player] = std::min(m_pBuyGUI[player]->GetDeliveryWidth(), g_FrameMan.GetPlayerScreenWidth() - 24); - m_pBuyGUI[player]->SetEnabled(false); -// SwitchToPrevActor(player, team, m_Brain[player]); - // Start selecting the landing zone - m_ViewState[player] = ViewState::LandingZoneSelect; - - // Set this to zero so the cursor interpolates down from the sky - float landingSpot = 0.0f; - if (g_SceneMan.GetTerrain() && g_SceneMan.GetTerrain()->GetOrbitDirection() == Directions::Down) { - landingSpot = g_SceneMan.GetSceneHeight(); - } - - m_LandingZone[player].m_Y = landingSpot; - } - - // After a while of game over, change messages to the final one for everyone - if (m_ActivityState == ActivityState::Over && m_GameOverTimer.IsPastRealMS(m_GameOverPeriod)) - { - g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); - //g_FrameMan.SetScreenText("Press [Esc] to leave the battlefield", ScreenOfPlayer(player), 750); - if (g_FrameMan.IsInMultiplayerMode()) - g_FrameMan.SetScreenText("All players must press and hold [BACKSPACE] to continue!", ScreenOfPlayer(player), 750); + // Allocate and (re)create the banners + if (m_pBannerYellow[player]) + m_pBannerYellow[player]->Destroy(); else - g_FrameMan.SetScreenText("Press [SPACE] or [START] to continue!", ScreenOfPlayer(player), 750); - - // Actually end on space - if (m_GameOverTimer.IsPastSimMS(55000) || g_UInputMan.AnyStartPress()) - { - g_ActivityMan.EndActivity(); - g_ActivityMan.SetInActivity(false); - } - } - - /////////////////////////////////// - // Enable/disable controlled actors' AI as appropriate when in menus - - if (m_ControlledActor[player] && m_ControlledActor[player]->GetController()->GetPlayerRaw() == player) - { - // Don't disable when pie menu is active; it is done inside the Controller Update - if (m_pBuyGUI[player]->IsVisible() || m_ViewState[player] == ViewState::ActorSelect || m_ViewState[player] == ViewState::LandingZoneSelect || m_ViewState[player] == ViewState::Observe) { - m_ControlledActor[player]->GetController()->SetInputMode(Controller::CIM_AI); - } else if (m_InventoryMenuGUI[player]->IsEnabledAndNotCarousel()) { - m_ControlledActor[player]->GetController()->SetInputMode(Controller::CIM_DISABLED); - } else if (m_LuaLockActor[player]) { - m_ControlledActor[player]->GetController()->SetInputMode(m_LuaLockActorMode[player]); - } else { - m_ControlledActor[player]->GetController()->SetInputMode(Controller::CIM_PLAYER); - } - } - - /////////////////////////////////////// - // Configure banners to show when important things happen, like the game over or death of brain - - if (IsOver()) - { - // Override previous messages - if (m_pBannerRed[player]->IsVisible() && m_pBannerRed[player]->GetBannerText() != "FAIL") - m_pBannerRed[player]->HideText(2500, 0); - if (m_pBannerYellow[player]->IsVisible() && m_pBannerYellow[player]->GetBannerText() != "WIN") - m_pBannerYellow[player]->HideText(2500, 0); - - // Player on a winning team - if (GetWinnerTeam() == m_Team[player] && !m_pBannerYellow[player]->IsVisible()) - m_pBannerYellow[player]->ShowText("WIN", GUIBanner::FLYBYRIGHTWARD, 1000, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 400); - - // Loser player - if (GetWinnerTeam() != m_Team[player] && !m_pBannerRed[player]->IsVisible()) - m_pBannerRed[player]->ShowText("FAIL", GUIBanner::FLYBYLEFTWARD, 1000, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 400); - } - // If a player had a brain that is now dead, but his team is not yet done, show the dead banner on his screen - else if (m_ActivityState != ActivityState::Editing && m_ActivityState != ActivityState::Starting && m_HadBrain[player] && !m_Brain[player] && !m_pBannerRed[player]->IsVisible()) - { - // If repeated too many times, just let the banner stop at showing and not cycle - if (m_BannerRepeats[player]++ < 6) - m_pBannerRed[player]->ShowText("DEAD", GUIBanner::FLYBYLEFTWARD, 1000, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 400); - else - m_pBannerRed[player]->ShowText("DEAD", GUIBanner::FLYBYLEFTWARD, -1, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 400); - } - - /////////////////////////////////////// - // Update message banners - - m_pBannerRed[player]->Update(); - m_pBannerYellow[player]->Update(); - } - - /////////////////////////////////////////// - // Iterate through all teams - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - - // Pause deliveries if game hasn't started yet - if (m_ActivityState == ActivityState::PreGame) - { - for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) - (*itr).timer.Reset(); - } - - //////////////////////////////// - // Delivery status update - - if (!m_Deliveries[team].empty()) - { - int player = m_Deliveries[team].front().orderedByPlayer; - if (m_MessageTimer[player].IsPastSimMS(1000)) - { - char message[512]; - std::snprintf(message, sizeof(message), "Next delivery in %i secs", ((int)m_Deliveries[team].front().delay - (int)m_Deliveries[team].front().timer.GetElapsedSimTimeMS()) / 1000); - g_FrameMan.SetScreenText(message, ScreenOfPlayer(player)); - m_MessageTimer[player].Reset(); - } - } - - // Delivery has arrived! Unpack and put into the world - if (!m_Deliveries[team].empty() && m_Deliveries[team].front().timer.IsPastSimMS(m_Deliveries[team].front().delay)) - { - // This is transferring ownership of the craft instance from the Delivery struct - ACraft *pDeliveryCraft = m_Deliveries[team].front().pCraft; - int player = m_Deliveries[team].front().orderedByPlayer; - if (pDeliveryCraft) - { - g_FrameMan.SetScreenText("Your order has arrived!", ScreenOfPlayer(player), 333); - m_MessageTimer[player].Reset(); - - pDeliveryCraft->ResetAllTimers(); - pDeliveryCraft->Update(); - - // Add the delivery craft to the world, TRANSFERRING OWNERSHIP - g_MovableMan.AddActor(pDeliveryCraft); -/* - // If the player who ordered this seems stuck int he manu waiting for the delivery, give him direct control - if (m_ControlledActor[player] == m_Brain[player] && m_ViewState[player] != ViewState::LandingZoneSelect) - { - SwitchToActor(pDeliveryCraft, player, team); - } -*/ - } - m_Deliveries[team].pop_front(); - } - } - - /////////////////////////////////////////// - // Special observer mode for the FOURTH screen in a THREE player game -/* Problematic with new player scheme.. what if the fourth player is active?? - if (m_PlayerCount == 3) - { - // Update the controller of the observation view - m_PlayerController[Players::PlayerFour].Update(); - - // Observer user control override - if (m_PlayerController[Players::PlayerFour].RelativeCursorMovement(m_ObservationTarget[Players::PlayerFour], 1.2)) - m_DeathTimer[Players::PlayerFour].Reset(); - - // If no user input in a few seconds, start scrolling along the terrain - if (m_DeathTimer[Players::PlayerFour].IsPastSimMS(5000)) - { - // Make it scroll along - m_ObservationTarget[Players::PlayerFour].m_X += 0.5; - - // Make view follow the terrain - float prevHeight = m_ObservationTarget[Players::PlayerFour].m_Y; - m_ObservationTarget[Players::PlayerFour].m_Y = 0; - m_ObservationTarget[Players::PlayerFour].m_Y = prevHeight + ((g_SceneMan.FindAltitude(m_ObservationTarget[Players::PlayerFour], g_SceneMan.GetSceneHeight(), 20, true) - prevHeight) * 0.02); - } - - // Set the view to the observation position - g_CameraMan.SetScrollTarget(m_ObservationTarget[Players::PlayerFour], 0.1, g_SceneMan.ForceBounds(m_ObservationTarget[Players::PlayerFour]), ScreenOfPlayer(Players::PlayerFour)); - } -*/ -} + m_pBannerYellow[player] = new GUIBanner; + m_pBannerYellow[player]->Create("Base.rte/GUIs/Fonts/BannerFontYellowReg.png", "Base.rte/GUIs/Fonts/BannerFontYellowBlur.png", 8); + // Resetting the banner repeat counter + m_BannerRepeats[player] = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. - -void GameActivity::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) -{ - if (which < 0 || which >= c_MaxScreenCount) - return; - - char str[512]; - int yTextPos = 0; - int team = Teams::NoTeam; - int cursor = 0; - int PoS = PlayerOfScreen(which); - if (PoS < Players::PlayerOne || PoS >= Players::MaxPlayerCount) - return; - Box screenBox(targetPos, pTargetBitmap->w, pTargetBitmap->h); - GUIFont *pLargeFont = g_FrameMan.GetLargeFont(); - GUIFont *pSmallFont = g_FrameMan.GetSmallFont(); - AllegroBitmap pBitmapInt(pTargetBitmap); - int frame = ((int)m_CursorTimer.GetElapsedSimTimeMS() % 1000) / 250; - Vector landZone; - - // Iterate through all players, drawing each currently used LZ cursor. - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - if (m_ViewState[player] == ViewState::LandingZoneSelect) - { - int halfWidth = std::max(m_LZCursorWidth[player]/2, 36); - team = m_Team[player]; - if (team == Teams::NoTeam) - continue; - cursor = team; - landZone = m_LandingZone[player] - targetPos; - // Cursor - draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X - halfWidth, landZone.m_Y - 48); - draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); - // Text - pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 42, m_AIReturnCraft[player] ? "Deliver here" : "Travel here", GUIFont::Centre); - pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 36, "and then", GUIFont::Centre); - pLargeFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 30, m_AIReturnCraft[player] ? "RETURN" : "STAY", GUIFont::Centre); - // Draw wrap around the world if necessary, and only if this is being drawn directly to a scenewide target bitmap - if (targetPos.IsZero() && (landZone.m_X < halfWidth || landZone.m_X > g_SceneMan.GetSceneWidth() - halfWidth)) - { - // Wrap shit around and draw dupe on the other side - int wrappedX = landZone.m_X + (landZone.m_X < halfWidth ? g_SceneMan.GetSceneWidth() : -g_SceneMan.GetSceneWidth()); - // Cursor - draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX - halfWidth, landZone.m_Y - 48); - draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); - // Text - pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 42, m_AIReturnCraft[player] ? "Deliver here" : "Travel here", GUIFont::Centre); - pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 36, "and then", GUIFont::Centre); - pLargeFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 30, m_AIReturnCraft[player] ? "RETURN" : "STAY", GUIFont::Centre); - } - } - } - - // Iterate through all teams, drawing all pending delivery cursors - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - char str[64]; - cursor = team; - for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) - { - int halfWidth = 24; - landZone = itr->landingZone - targetPos; - bool anyPlayerOnTeamIsInLandingZoneSelectViewState = false; - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - if (GetTeamOfPlayer(player) == team && m_ViewState[player] == ViewState::LandingZoneSelect) { - anyPlayerOnTeamIsInLandingZoneSelectViewState = true; - break; - } + // Draw GO! game start notification, if it's a new game + if (m_ActivityState == ActivityState::NotStarted) { + m_pBannerYellow[player]->ShowText("GO!", GUIBanner::FLYBYLEFTWARD, 1000, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 500); + g_FrameMan.SetScreenText((player % 2 == 0) ? "Mine Gold and buy more firepower with the funds..." : "...then smash the competing brain to claim victory!", ScreenOfPlayer(player), 0); } - if (!anyPlayerOnTeamIsInLandingZoneSelectViewState) { landZone.m_Y -= itr->multiOrderYOffset; } - // Cursor - draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X - halfWidth, landZone.m_Y - 48); - draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); - // Text - pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 38, "ETA:", GUIFont::Centre); - if (m_ActivityState == ActivityState::PreGame) - std::snprintf(str, sizeof(str), "???s"); - else - std::snprintf(str, sizeof(str), "%is", ((int)itr->delay - (int)itr->timer.GetElapsedSimTimeMS()) / 1000); - pLargeFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 32, str, GUIFont::Centre); - // Draw wrap around the world if necessary, and only if this is being drawn directly to a scenewide target bitmap - if (targetPos.IsZero() && (landZone.m_X < halfWidth || landZone.m_X > g_SceneMan.GetSceneWidth() - halfWidth)) - { - // Wrap shit around and draw dupe on the other side - int wrappedX = landZone.m_X + (landZone.m_X < halfWidth ? g_SceneMan.GetSceneWidth() : -g_SceneMan.GetSceneWidth()); - // Cursor - draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX - halfWidth, landZone.m_Y - 48); - draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); - // Text - pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 38, "ETA:", GUIFont::Centre); - pLargeFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 32, str, GUIFont::Centre); - } - } - } - - // The team of the screen - team = m_Team[PoS]; - if (team == Teams::NoTeam) - return; - - // None of the following player-specific GUI elements apply if this isn't a played human actor - if (!(m_IsActive[PoS] && m_IsHuman[PoS])) - return; - - // Get all possible wrapped boxes of the screen - std::list wrappedBoxes; - g_SceneMan.WrapBox(screenBox, wrappedBoxes); - Vector wrappingOffset, objScenePos, onScreenEdgePos; - float distance, shortestDist; - float sceneWidth = static_cast(g_SceneMan.GetSceneWidth()); - float halfScreenWidth = pTargetBitmap->w / 2; - float halfScreenHeight = pTargetBitmap->h / 2; - // THis is the max distance that is possible between a point inside the scene, but outside the screen box, and the screen box's outer edge closest to the point (taking wrapping into account) - float maxOffScreenSceneWidth = g_SceneMan.SceneWrapsX() ? ((sceneWidth / 2) - halfScreenWidth) : (sceneWidth - pTargetBitmap->w); - // These handle arranging the left and right stacks of arrows, so they don't pile up on top of each other - cursor = team; - float leftStackY = halfScreenHeight - m_aObjCursor[cursor][frame]->h * 2; - float rightStackY = leftStackY; - - // Draw the objective points this player should care about - for (std::list::iterator itr = m_Objectives.begin(); itr != m_Objectives.end(); ++itr) - { - // Only draw objectives of the same team as the current player - if (itr->m_Team == team) - { - // Iterate through the wrapped screen boxes - will only be one if there's no wrapping - // Try to the find one that contains the objective point - bool withinAny = false; - std::list::iterator nearestBoxItr = wrappedBoxes.begin(); - shortestDist = 1000000.0; - for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - // See if we found the point to be within the screen or not - if (wItr->IsWithinBox((*itr).m_ScenePos)) - { - nearestBoxItr = wItr; - withinAny = true; - break; - } - // Well, which wrapped screen box is closest to the point? - distance = g_SceneMan.ShortestDistance(wItr->GetCenter(), (*itr).m_ScenePos).GetLargest(); - if (distance < shortestDist) - { - shortestDist = distance; - nearestBoxItr = wItr; - } - } - - // Get the difference that the wrapped screen has from the actual one - wrappingOffset = screenBox.GetCorner() - nearestBoxItr->GetCorner(); - // Apply that offet to the objective point's position - objScenePos = itr->m_ScenePos + wrappingOffset; - - // Objective is within the screen, so draw the set arrow over it - if (withinAny) - itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], objScenePos - targetPos, itr->m_ArrowDir); - // Outside the screen, so draw it at the edge of it - else - { - // Figure out which point is closest to the box, taking scene wrapping into account - objScenePos = nearestBoxItr->GetCenter() + g_SceneMan.ShortestDistance(nearestBoxItr->GetCenter(), objScenePos); - // Shortest distance from the edge of the screen box, not the center. - shortestDist -= halfScreenWidth; - - // Make the arrow point toward the edge of the screen - if (objScenePos.m_X >= nearestBoxItr->GetCorner().m_X + nearestBoxItr->GetWidth()) - { - // Make the edge position approach the center of the vertical edge of the screen the farther away the objective position is from the screen - onScreenEdgePos = nearestBoxItr->GetWithinBox(objScenePos) - targetPos; - // Double the EaseIn to make it even more exponential, want the arrow to stay close to the edge for a long time - onScreenEdgePos.m_Y = rightStackY + EaseIn(0, onScreenEdgePos.m_Y - rightStackY, EaseIn(0, 1.0, 1.0 - (shortestDist / maxOffScreenSceneWidth))); - itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], onScreenEdgePos, ARROWRIGHT); - // Stack cursor moves down an arrowheight - rightStackY += m_aObjCursor[cursor][frame]->h; - } - else if (objScenePos.m_X < nearestBoxItr->GetCorner().m_X) - { - // Make the edge position approach the center of the vertical edge of the screen the farther away the objective position is from the screen - onScreenEdgePos = nearestBoxItr->GetWithinBox(objScenePos) - targetPos; - // Double the EaseIn to make it even more exponential, want the arrow to stay close to the edge for a long time - onScreenEdgePos.m_Y = leftStackY + EaseIn(0, onScreenEdgePos.m_Y - leftStackY, EaseIn(0, 1.0, 1.0 - (shortestDist / maxOffScreenSceneWidth))); - itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], onScreenEdgePos, ARROWLEFT); - // Stack cursor moves down an arrowheight - leftStackY += m_aObjCursor[cursor][frame]->h; - } - else if (objScenePos.m_Y < nearestBoxItr->GetCorner().m_Y) - itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], nearestBoxItr->GetWithinBox(objScenePos) - targetPos, ARROWUP); - else - itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], nearestBoxItr->GetWithinBox(objScenePos) - targetPos, ARROWDOWN); - } - } - } - - // Team Icon up in the top left corner - const Icon *pIcon = GetTeamIcon(m_Team[PoS]); - if (pIcon) - draw_sprite(pTargetBitmap, pIcon->GetBitmaps8()[0], MAX(2, g_CameraMan.GetScreenOcclusion(which).m_X + 2), 2); - // Gold - std::snprintf(str, sizeof(str), "%c Funds: %.10g oz", TeamFundsChanged(which) ? -57 : -58, std::floor(GetTeamFunds(m_Team[PoS]))); - g_FrameMan.GetLargeFont()->DrawAligned(&pBitmapInt, MAX(16, g_CameraMan.GetScreenOcclusion(which).m_X + 16), yTextPos, str, GUIFont::Left); -/* Not applicable anymore to the 4-team games - // Body losses - std::snprintf(str, sizeof(str), "%c Losses: %c%i %c%i", -39, -62, GetTeamDeathCount(Teams::TeamOne), -59, GetTeamDeathCount(Teams::TeamTwo)); - g_FrameMan.GetLargeFont()->DrawAligned(&pBitmapInt, MIN(pTargetBitmap->w - 4, pTargetBitmap->w - 4 + g_CameraMan.GetScreenOcclusion(which).m_X), yTextPos, str, GUIFont::Right); -*/ - // Show the player's controller scheme icon in the upper right corner of his screen, but only for a minute - if (m_GameTimer.GetElapsedRealTimeS() < 30) - { -// TODO: Only blink if there hasn't been any input on a controller since start of game?? - // Blink them at first, but only if there's more than one human player - if (m_GameTimer.GetElapsedRealTimeS() > 4 || m_GameTimer.AlternateReal(150) || GetHumanCount() < 2) - { - pIcon = g_UInputMan.GetSchemeIcon(PoS); - if (pIcon) - { - draw_sprite(pTargetBitmap, pIcon->GetBitmaps8()[0], MIN(pTargetBitmap->w - pIcon->GetBitmaps8()[0]->w - 2, pTargetBitmap->w - pIcon->GetBitmaps8()[0]->w - 2 + g_CameraMan.GetScreenOcclusion(which).m_X), yTextPos); -// TODO: make a black Activity intro screen, saying "Player X, press any key/button to show that you are ready!, and display their controller icon, then fade into the scene" -// stretch_sprite(pTargetBitmap, pIcon->GetBitmaps8()[0], 10, 10, pIcon->GetBitmaps8()[0]->w * 4, pIcon->GetBitmaps8()[0]->h * 4); - } - } - } - - if (m_ActivityState == ActivityState::Running) { - if (m_InventoryMenuGUI[PoS] && m_InventoryMenuGUI[PoS]->IsVisible()) { m_InventoryMenuGUI[PoS]->Draw(pTargetBitmap, targetPos); } - if (m_pBuyGUI[PoS] && m_pBuyGUI[PoS]->IsVisible()) { m_pBuyGUI[PoS]->Draw(pTargetBitmap); } - } - - // Draw actor picking crosshairs if applicable - if (m_ViewState[PoS] == ViewState::ActorSelect && m_IsActive[PoS] && m_IsHuman[PoS]) - { - Vector center = m_ActorCursor[PoS] - targetPos; - circle(pTargetBitmap, center.m_X, center.m_Y, m_CursorTimer.AlternateReal(150) ? 6 : 8, g_YellowGlowColor); - // Add pixel glow area around it, in scene coordinates - g_PostProcessMan.RegisterGlowArea(m_ActorCursor[PoS], 10); -/* Crosshairs - putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); -*/ - } - // AI point commands cursor - else if (m_ViewState[PoS] == ViewState::AIGoToPoint) - { - Vector center = m_ActorCursor[PoS] - targetPos; - circle(pTargetBitmap, center.m_X, center.m_Y, m_CursorTimer.AlternateReal(150) ? 6 : 8, g_YellowGlowColor); - circlefill(pTargetBitmap, center.m_X, center.m_Y, 2, g_YellowGlowColor); -// putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); - // Add pixel glow area around it, in scene coordinates - g_PostProcessMan.RegisterGlowArea(m_ActorCursor[PoS], 10); - - // Draw a line from the last set waypoint to the cursor - if (m_ControlledActor[PoS] && g_MovableMan.IsActor(m_ControlledActor[PoS])) - g_FrameMan.DrawLine(pTargetBitmap, m_ControlledActor[PoS]->GetLastAIWaypoint() - targetPos, m_ActorCursor[PoS] - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, 0, true); - } - // Group selection circle - else if (m_ViewState[PoS] == ViewState::UnitSelectCircle) - { - if (m_ControlledActor[PoS] && g_MovableMan.IsActor(m_ControlledActor[PoS])) - { - Vector cursorDrawPos = m_ActorCursor[PoS] - targetPos; - Vector actorPos = m_ControlledActor[PoS]->GetPos(); - Vector drawPos = actorPos - targetPos; - - //Fix cursor coordinates - if (!targetPos.IsZero()) - { - // Spans vertical scene seam - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) - { - if ((targetPos.m_X < 0) && (m_ActorCursor[PoS].m_X > (sceneWidth - pTargetBitmap->w))) - cursorDrawPos.m_X -= sceneWidth; - else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_ActorCursor[PoS].m_X < pTargetBitmap->w)) - cursorDrawPos.m_X += sceneWidth; - } - // Spans horizontal scene seam - int sceneHeight = g_SceneMan.GetSceneHeight(); - if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) - { - if ((targetPos.m_Y < 0) && (m_ActorCursor[PoS].m_Y > (sceneHeight - pTargetBitmap->h))) - cursorDrawPos.m_Y -= sceneHeight; - else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_ActorCursor[PoS].m_Y < pTargetBitmap->h)) - cursorDrawPos.m_Y += sceneHeight; - } + + m_ActorCursor[player].Reset(); + m_LandingZone[player].Reset(); + + // Set the initial landing zones to be above the respective brains, but not for the observer player in a three player game + if (m_Brain[player] && !(m_PlayerCount == 3 && ScreenOfPlayer(player) == 3)) { + // Also set the brain to be the selected actor at start + SwitchToActor(m_Brain[player], player, m_Team[player]); + m_ActorCursor[player] = m_Brain[player]->GetPos(); + m_LandingZone[player].m_X = m_Brain[player]->GetPos().m_X; + // Set the observation target to the brain, so that if/when it dies, the view flies to it in observation mode + m_ObservationTarget[player] = m_Brain[player]->GetPos(); } + } + // Set up the AI controllers for everyone + InitAIs(); + + // Start the game timer + m_GameTimer.Reset(); + + if (m_aLZCursor[0].empty()) { + ContentFile cursorFile("Base.rte/GUIs/Indicators/LZArrowRedL.png"); + cursorFile.GetAsAnimation(m_aLZCursor[0], LZCURSORFRAMECOUNT); + cursorFile.SetDataPath("Base.rte/GUIs/Indicators/LZArrowGreenL.png"); + cursorFile.GetAsAnimation(m_aLZCursor[1], LZCURSORFRAMECOUNT); + cursorFile.SetDataPath("Base.rte/GUIs/Indicators/LZArrowBlueL.png"); + cursorFile.GetAsAnimation(m_aLZCursor[2], LZCURSORFRAMECOUNT); + cursorFile.SetDataPath("Base.rte/GUIs/Indicators/LZArrowYellowL.png"); + cursorFile.GetAsAnimation(m_aLZCursor[3], LZCURSORFRAMECOUNT); + } - // Fix circle center coordinates - if (!targetPos.IsZero()) - { - // Spans vertical scene seam - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) - { - if ((targetPos.m_X < 0) && (actorPos.m_X > (sceneWidth - pTargetBitmap->w))) - drawPos.m_X -= sceneWidth; - else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (actorPos.m_X < pTargetBitmap->w)) - drawPos.m_X += sceneWidth; - } - // Spans horizontal scene seam - int sceneHeight = g_SceneMan.GetSceneHeight(); - if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) - { - if ((targetPos.m_Y < 0) && (actorPos.m_Y > (sceneHeight - pTargetBitmap->h))) - drawPos.m_Y -= sceneHeight; - else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (actorPos.m_Y < pTargetBitmap->h)) - drawPos.m_Y += sceneHeight; - } - } + if (m_aObjCursor[0].empty()) { + ContentFile cursorFile("Base.rte/GUIs/Indicators/ObjArrowRed.png"); + cursorFile.GetAsAnimation(m_aObjCursor[0], OBJARROWFRAMECOUNT); + cursorFile.SetDataPath("Base.rte/GUIs/Indicators/ObjArrowGreen.png"); + cursorFile.GetAsAnimation(m_aObjCursor[1], OBJARROWFRAMECOUNT); + cursorFile.SetDataPath("Base.rte/GUIs/Indicators/ObjArrowBlue.png"); + cursorFile.GetAsAnimation(m_aObjCursor[2], OBJARROWFRAMECOUNT); + cursorFile.SetDataPath("Base.rte/GUIs/Indicators/ObjArrowYellow.png"); + cursorFile.GetAsAnimation(m_aObjCursor[3], OBJARROWFRAMECOUNT); + } - float radius = g_SceneMan.ShortestDistance(m_ActorCursor[PoS], m_ControlledActor[PoS]->GetPos(), true).GetMagnitude(); + // Start the in-game music + g_AudioMan.ClearMusicQueue(); + g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/cc2g.ogg", 0); + g_AudioMan.QueueSilence(30); + g_AudioMan.QueueMusicStream("Base.rte/Music/Watts/Last Man.ogg"); + g_AudioMan.QueueSilence(30); + g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/cc2g.ogg"); - circle(pTargetBitmap, cursorDrawPos.m_X, cursorDrawPos.m_Y, m_CursorTimer.AlternateReal(150) ? 6 : 8, g_YellowGlowColor); - circlefill(pTargetBitmap, cursorDrawPos.m_X, cursorDrawPos.m_Y, 2, g_YellowGlowColor); + return error; + } - Vector unwrappedPos; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. - //Check if we crossed the seam - if (g_SceneMan.GetScene()->WrapsX()) - { - //Calculate unwrapped cursor position, or it won't glow - unwrappedPos = m_ActorCursor[PoS] - m_ControlledActor[PoS]->GetPos(); - float halfSceneWidth = sceneWidth * 0.5F; - float seamMinimum = 350.0F; + void GameActivity::SetPaused(bool pause) { + Activity::SetPaused(pause); + } - if (unwrappedPos.MagnitudeIsGreaterThan(std::max(halfSceneWidth, seamMinimum))) - { - if (m_ActorCursor->m_X < halfSceneWidth) - unwrappedPos = m_ActorCursor[PoS] + Vector(sceneWidth , 0); - else - unwrappedPos = m_ActorCursor[PoS] - Vector(sceneWidth , 0); - g_PostProcessMan.RegisterGlowArea(unwrappedPos, 10); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + + void GameActivity::End() { + Activity::End(); + + bool playerWon = false; + + // Disable control of actors.. will be handed over to the observation targets instead + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(player)); + + if (m_Team[player] == m_WinnerTeam) { + playerWon = true; + // Set the winner's observation view to his controlled actors instead of his brain + if (m_ControlledActor[player] && g_MovableMan.IsActor(m_ControlledActor[player])) + m_ObservationTarget[player] = m_ControlledActor[player]->GetPos(); } - else - unwrappedPos = m_ActorCursor[PoS]; + } - g_PostProcessMan.RegisterGlowArea(m_ActorCursor[PoS], 10); + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; + for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) { + if (MovableObject* asMo = dynamic_cast(itr->pCraft)) { + asMo->DestroyScriptState(); + } + delete itr->pCraft; + } + m_Deliveries[team].clear(); + } - //Glowing dotted circle version - int dots = 2 * c_PI * radius / 25;//5 + (int)(radius / 10); - float radsperdot = 2 * 3.14159265359 / dots; + /* Now controlled by the scripted activities + // Play the approriate tune on player win/lose + if (playerWon) + { + // Didn't work well, has gap between intro and loop tracks + // g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/uwinintro.ogg", 0); + // g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/uwinloop.ogg"); + g_AudioMan.ClearMusicQueue(); + // Loop it twice, nice tune! + g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/uwinfinal.ogg", 2); + g_AudioMan.QueueSilence(10); + g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg"); + } + else + { + g_AudioMan.ClearMusicQueue(); + g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/udiedfinal.ogg", 0); + g_AudioMan.QueueSilence(10); + g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg"); + } + */ + + m_ActivityState = ActivityState::Over; + m_GameOverTimer.Reset(); + } - for (int i = 0; i < dots; i++) - { - Vector dotPos = Vector(actorPos.m_X + sin(i * radsperdot) * radius, actorPos.m_Y + cos(i * radsperdot) * radius); - Vector dotDrawPos = dotPos - targetPos; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateEditing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: This is a special update step for when any player is still editing the + // scene. + + void GameActivity::UpdateEditing() { + // Editing the scene, just update the editor guis and see if players are ready to start or not + if (m_ActivityState != ActivityState::Editing) + return; + + /////////////////////////////////////////// + // Iterate through all human players + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + m_pEditorGUI[player]->Update(); + + // Set the team associations with each screen displayed + g_CameraMan.SetScreenTeam(m_Team[player], ScreenOfPlayer(player)); + + // Check if the player says he's done editing, and if so, make sure he really is good to go + if (m_pEditorGUI[player]->GetEditorGUIMode() == SceneEditorGUI::DONEEDITING) { + // See if a brain has been placed yet by this player - IN A VALID LOCATION + if (!m_pEditorGUI[player]->TestBrainResidence()) { + // Hm not ready yet without resident brain in the right spot, so let user know + m_ReadyToStart[player] = false; + const Entity* pBrain = g_PresetMan.GetEntityPreset("Actor", "Brain Case"); + if (pBrain) + m_pEditorGUI[player]->SetCurrentObject(dynamic_cast(pBrain->Clone())); + m_pEditorGUI[player]->SetEditorGUIMode(SceneEditorGUI::INSTALLINGBRAIN); + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + g_FrameMan.SetScreenText("PLACE YOUR BRAIN IN A VALID SPOT FIRST!", ScreenOfPlayer(player), 250, 3500); + m_MessageTimer[player].Reset(); + } + // Ready to start + else { + m_ReadyToStart[player] = true; + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + g_FrameMan.SetScreenText("READY to start - wait for others to finish...", ScreenOfPlayer(player), 333); + m_pEditorGUI[player]->SetEditorGUIMode(SceneEditorGUI::ADDINGOBJECT); + } + } - if (!targetPos.IsZero()) - { - // Spans vertical scene seam - if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) - { - if ((targetPos.m_X < 0) && (dotPos.m_X > (sceneWidth - pTargetBitmap->w))) - { - dotDrawPos.m_X -= sceneWidth; + // Keep showing ready message + if (m_ReadyToStart[player]) + g_FrameMan.SetScreenText("READY to start - wait for others to finish...", ScreenOfPlayer(player), 333); + } + + // Have all players flagged themselves as ready to start the game? + bool allReady = true; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + if (!m_ReadyToStart[player]) + allReady = false; + } + + // YES, we are allegedly all ready to stop editing and start the game! + if (allReady) { + // Make sure any players haven't moved or entombed their brains in the period after flagging themselves "done" + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + // See if a brain has been placed yet by this player - IN A VALID LOCATION + if (!m_pEditorGUI[player]->TestBrainResidence()) { + // Hm not ready yet without resident brain in the right spot, so let user know + m_ReadyToStart[player] = false; + allReady = false; + const Entity* pBrain = g_PresetMan.GetEntityPreset("Actor", "Brain Case"); + if (pBrain) + m_pEditorGUI[player]->SetCurrentObject(dynamic_cast(pBrain->Clone())); + m_pEditorGUI[player]->SetEditorGUIMode(SceneEditorGUI::INSTALLINGBRAIN); + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + g_FrameMan.SetScreenText("PLACE YOUR BRAIN IN A VALID SPOT FIRST!", ScreenOfPlayer(player), 333, 3500); + m_MessageTimer[player].Reset(); + } + } + + // Still good to go?? + if (allReady) { + // All resident brains are still in valid spots - place them into the simulation + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + // Place this player's resident brain into the simulation and set it as the player's assigned brain + g_SceneMan.GetScene()->PlaceResidentBrain(player, *this); + + // Still no brain of this player? Last ditch effort to find one and assign it to this player + if (!m_Brain[player]) + m_Brain[player] = g_MovableMan.GetUnassignedBrain(m_Team[player]); + // Um, something went wrong.. we're not done placing brains after all?? + if (!m_Brain[player]) { + allReady = false; + // Get the brains back into residency so the players who are OK are still so + g_SceneMan.GetScene()->RetrieveResidentBrains(*this); + break; + } + + // Set the brain to be the selected actor at start + SwitchToActor(m_Brain[player], player, m_Team[player]); + m_ActorCursor[player] = m_Brain[player]->GetPos(); + m_LandingZone[player].m_X = m_Brain[player]->GetPos().m_X; + // Set the observation target to the brain, so that if/when it dies, the view flies to it in observation mode + m_ObservationTarget[player] = m_Brain[player]->GetPos(); + // CLear the messages before starting the game + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + // Reset the screen occlusion if any players are still in menus + g_CameraMan.SetScreenOcclusion(Vector(), ScreenOfPlayer(player)); + } + } + + // Still good to go?? then GO + if (allReady) { + // START the game! + m_ActivityState = ActivityState::Running; + // Re-enable the AI's if we are done editing + DisableAIs(false); + InitAIs(); + // Reset the mouse value and pathfinding so it'll know about the newly placed stuff + g_UInputMan.SetMouseValueMagnitude(0); + g_SceneMan.GetScene()->ResetPathFinding(); + // Start the in-game track + g_AudioMan.ClearMusicQueue(); + g_AudioMan.PlayMusic("Base.rte/Music/dBSoundworks/cc2g.ogg", 0); + g_AudioMan.QueueSilence(30); + g_AudioMan.QueueMusicStream("Base.rte/Music/Watts/Last Man.ogg"); + g_AudioMan.QueueSilence(30); + g_AudioMan.QueueMusicStream("Base.rte/Music/dBSoundworks/cc2g.ogg"); + } + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this GameActivity. Supposed to be done every frame + // before drawing. + + void GameActivity::Update() { + Activity::Update(); + + // Avoid game logic when we're editing + if (m_ActivityState == ActivityState::Editing) { + UpdateEditing(); + return; + } + + /////////////////////////////////////////// + // Iterate through all human players + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + // The current player's team + int team = m_Team[player]; + if (team == Teams::NoTeam) + continue; + + // Temporary hack to avoid teh buy menu buy button to be pressed immediately after selecting an LZ for a previous order + bool skipBuyUpdate = false; + + // Set the team associations with each screen displayed + g_CameraMan.SetScreenTeam(team, ScreenOfPlayer(player)); + + ////////////////////////////////////////////////////// + // Assure that Controlled Actor is a safe pointer + + // Only allow this if player's brain is intact + if (m_Brain[player] && !m_Brain[player]->IsDead()) { + // Note that we have now had a brain + m_HadBrain[player] = true; + + // Tracking normally + if (m_ViewState[player] == ViewState::Normal) { + // Get a next actor if there isn't one + if (!m_ControlledActor[player]) + SwitchToNextActor(player, team); + + // Continually set the observation target to the brain during play, so that if/when it dies, the view flies to it in observation mode + if (m_ActivityState != ActivityState::Over && m_ViewState[player] != ViewState::Observe) + m_ObservationTarget[player] = m_Brain[player]->GetPos(); + + // Save the location of the currently controlled actor so we can know where to watch if he died on us + if (g_MovableMan.IsActor(m_ControlledActor[player])) { + m_DeathViewTarget[player] = m_ControlledActor[player]->GetPos(); + m_DeathTimer[player].Reset(); + } + // Add delay after death before switching so the death comedy can be witnessed + // Died, so enter death watch mode + else { + LoseControlOfActor(player); + } + } + // Ok, done watching death comedy, now automatically switch + else if (m_ViewState[player] == ViewState::DeathWatch && m_DeathTimer[player].IsPastSimMS(1500)) { + // Get a next actor if there isn't one + if (!m_ControlledActor[player]) + SwitchToNextActor(player, team); + + // If currently focused actor died, get next one + if (!g_MovableMan.IsActor(m_ControlledActor[player])) + SwitchToNextActor(player, team); + + if (m_ViewState[player] != ViewState::ActorSelect) + m_ViewState[player] = ViewState::Normal; + } + // Any other viewing mode and the actor died... go to deathwatch + else if (m_ControlledActor[player] && !g_MovableMan.IsActor(m_ControlledActor[player])) { + LoseControlOfActor(player); + } + } + // Player brain is now gone! Remove any control he may have had + else if (m_HadBrain[player]) { + if (m_ControlledActor[player] && g_MovableMan.IsActor(m_ControlledActor[player])) + m_ControlledActor[player]->SetControllerMode(Controller::CIM_AI); + m_ControlledActor[player] = 0; + m_ViewState[player] = ViewState::Observe; + } + // Never had a brain, and no actor is selected, so just select the first one we do have + else if (m_ViewState[player] != ViewState::Observe && !g_MovableMan.IsActor(m_ControlledActor[player])) { + // Only try to switch if there's somehting to switch to + if (!g_MovableMan.GetTeamRoster(team)->empty()) + SwitchToNextActor(player, team); + else + m_ControlledActor[player] = 0; + } + + // Player-commanded actor switching + if (m_ViewState[player] != ViewState::Observe) { + // Switch to brain actor directly if the player wants to + if (m_PlayerController[player].IsState(ACTOR_BRAIN) && m_ViewState[player] != ViewState::ActorSelect) { + SwitchToActor(m_Brain[player], player, team); + m_ViewState[player] = ViewState::Normal; + } else if (m_PlayerController[player].IsState(ACTOR_NEXT) && m_ViewState[player] != ViewState::ActorSelect && !m_pBuyGUI[player]->IsVisible() && !m_LuaLockActor[player]) { + // Switch to next actor if the player wants to. Don't do it while the buy menu is open + if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { + m_ControlledActor[player]->GetPieMenu()->SetEnabled(false); + } + + SwitchToNextActor(player, team); + m_ViewState[player] = ViewState::Normal; + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + } + // Switch to prev actor if the player wants to. Don't do it while the buy menu is open + else if (m_PlayerController[player].IsState(ACTOR_PREV) && m_ViewState[player] != ViewState::ActorSelect && !m_pBuyGUI[player]->IsVisible()) { + if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { + m_ControlledActor[player]->GetPieMenu()->SetEnabled(false); + } + + SwitchToPrevActor(player, team); + m_ViewState[player] = ViewState::Normal; + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + } else if (m_ViewState[player] != ViewState::ActorSelect && !m_pBuyGUI[player]->IsVisible() && !m_LuaLockActor[player] && (m_PlayerController[player].IsState(ACTOR_NEXT_PREP) || m_PlayerController[player].IsState(ACTOR_PREV_PREP))) { + // Go into manual actor select mode if either actor switch buttons are held for a duration + if (m_ActorSelectTimer[player].IsPastRealMS(250)) { + // Set cursor to start at the head of controlled actor + if (m_ControlledActor[player]) { + // Give switched from actor an AI controller + m_ControlledActor[player]->SetControllerMode(Controller::CIM_AI); + m_ControlledActor[player]->GetController()->SetDisabled(false); + m_ActorCursor[player] = m_ControlledActor[player]->GetCPUPos(); + m_CursorTimer.Reset(); } - else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (actorPos.m_X < pTargetBitmap->w)) - { - dotDrawPos.m_X += sceneWidth; + + m_ViewState[player] = ViewState::ActorSelect; + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + } + } else { + m_ActorSelectTimer[player].Reset(); + } + } + + //////////////////////////////////// + // Update sceneman scroll targets + + if (m_ViewState[player] == ViewState::Observe) { + // If we're observing game over state, freeze the view for a bit so the player's input doesn't ruin the focus + if (!(m_ActivityState == ActivityState::Over && !m_GameOverTimer.IsPastRealMS(1000))) { + // Get cursor input + m_PlayerController[player].RelativeCursorMovement(m_ObservationTarget[player], 1.2f); + } + // Set the view to the observation position + g_SceneMan.ForceBounds(m_ObservationTarget[player]); + g_CameraMan.SetScrollTarget(m_ObservationTarget[player], 0.1, ScreenOfPlayer(player)); + } + + /////////////////////////////////////////////////// + // Manually selecting a new actor to switch to + + else if (m_ViewState[player] == ViewState::ActorSelect) { + // Continuously display message + g_FrameMan.SetScreenText("Select a body to switch control to...", ScreenOfPlayer(player)); + // Get cursor input + m_PlayerController[player].RelativeCursorMovement(m_ActorCursor[player]); + + // Find the actor closest to the cursor, if any within the radius + Vector markedDistance; + Actor* pMarkedActor = g_MovableMan.GetClosestTeamActor(team, player, m_ActorCursor[player], g_SceneMan.GetSceneWidth(), markedDistance, true); + // Actor *pMarkedActor = g_MovableMan.GetClosestTeamActor(team, player, m_ActorCursor[player], g_FrameMan.GetPlayerScreenWidth() / 4); + + // Player canceled selection of actor + if (m_PlayerController[player].IsState(PRESS_SECONDARY)) { + // Reset the mouse so the actor doesn't change aim because mouse has been moved + if (m_PlayerController[player].IsMouseControlled()) { + g_UInputMan.SetMouseValueMagnitude(0); + } + + m_ViewState[player] = ViewState::Normal; + g_GUISound.UserErrorSound()->Play(player); + if (m_ControlledActor[player]) { + if (m_ControlledActor[player]->GetPieMenu()) { + m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); } + m_ControlledActor[player]->SetControllerMode(Controller::CIM_PLAYER, player); } - // Spans horizontal scene seam - int sceneHeight = g_SceneMan.GetSceneHeight(); - if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) - { - if ((targetPos.m_Y < 0) && (dotPos.m_Y > (sceneHeight - pTargetBitmap->h))) - { - dotDrawPos.m_Y -= sceneHeight; + if (pMarkedActor && pMarkedActor->GetPieMenu()) { + pMarkedActor->GetPieMenu()->DoDisableAnimation(); + } + } + // Player is done selecting new actor; switch to it if we have anything marked + else if (m_PlayerController[player].IsState(ACTOR_NEXT) || m_PlayerController[player].IsState(ACTOR_PREV) || m_PlayerController[player].IsState(PRESS_FACEBUTTON) || m_PlayerController[player].IsState(PRESS_PRIMARY)) { + // Reset the mouse so the actor doesn't change aim because mouse has been moved + if (m_PlayerController[player].IsMouseControlled()) { + g_UInputMan.SetMouseValueMagnitude(0); + } + + if (pMarkedActor) { + SwitchToActor(pMarkedActor, player, team); + } else { + g_GUISound.UserErrorSound()->Play(player); + } + + m_ViewState[player] = ViewState::Normal; + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + // Flash the same actor, jsut to show the control went back to him + if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { + m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); + } + } else if (pMarkedActor && pMarkedActor->GetPieMenu()) { + int quarterFrameBuffer = g_FrameMan.GetPlayerFrameBufferWidth(player) / 4; + if (markedDistance.MagnitudeIsGreaterThan(static_cast(quarterFrameBuffer))) { + pMarkedActor->GetPieMenu()->Wobble(); + } else { + pMarkedActor->GetPieMenu()->FreezeAtRadius(30); + } + } + + // Set the view to the cursor pos + g_SceneMan.ForceBounds(m_ActorCursor[player]); + g_CameraMan.SetScrollTarget(m_ActorCursor[player], 0.1, ScreenOfPlayer(player)); + + if (m_pLastMarkedActor[player]) { + if (!g_MovableMan.ValidMO(m_pLastMarkedActor[player])) { + m_pLastMarkedActor[player] = nullptr; + } else if (m_pLastMarkedActor[player] != pMarkedActor && m_pLastMarkedActor[player]->GetPieMenu()) { + m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); + } + } + if (pMarkedActor) { + m_pLastMarkedActor[player] = pMarkedActor; + } + } + + /////////////////////////////////////////////////// + // Selecting points on the scene for the AI to go to + + else if (m_ViewState[player] == ViewState::AIGoToPoint) { + // Continuously display message + g_FrameMan.SetScreenText("Set waypoints for the AI to go to...", ScreenOfPlayer(player)); + // Get cursor input + m_PlayerController[player].RelativeCursorMovement(m_ActorCursor[player]); + + // If we are pointing to an actor to follow, then snap cursor to that actor's position + Actor* pTargetActor = 0; + Vector distance; + if (pTargetActor = g_MovableMan.GetClosestActor(m_ActorCursor[player], 40, distance, m_ControlledActor[player]); pTargetActor && pTargetActor->GetPieMenu()) { + if (m_pLastMarkedActor[player] && m_pLastMarkedActor[player]->GetPieMenu()) { + m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); + } + pTargetActor->GetPieMenu()->FreezeAtRadius(15); + m_pLastMarkedActor[player] = pTargetActor; + } else if (m_pLastMarkedActor[player] && m_pLastMarkedActor[player]->GetPieMenu()) { + m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); + } + + // Set the view to the cursor pos + g_SceneMan.ForceBounds(m_ActorCursor[player]); + g_CameraMan.SetScrollTarget(m_ActorCursor[player], 0.1, ScreenOfPlayer(player)); + + // Draw the actor's waypoints + m_ControlledActor[player]->DrawWaypoints(true); + + // Disable the actor's controller + m_ControlledActor[player]->GetController()->SetDisabled(true); + + // Player is done setting waypoints + if (m_PlayerController[player].IsState(PRESS_SECONDARY) || m_PlayerController[player].IsState(ACTOR_NEXT_PREP) || m_PlayerController[player].IsState(ACTOR_PREV_PREP)) { + // Stop drawing the waypoints + // m_ControlledActor[player]->DrawWaypoints(false); + // Update the player's move path now to the first waypoint set + m_ControlledActor[player]->UpdateMovePath(); + // Give player control back to actor + m_ControlledActor[player]->GetController()->SetDisabled(false); + // Switch back to normal view + m_ViewState[player] = ViewState::Normal; + // Stop displaying the message + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + if (m_pLastMarkedActor[player] && m_pLastMarkedActor[player]->GetPieMenu()) { + m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); + } + } + // Player set a new waypoint + else if (m_ControlledActor[player] && m_PlayerController[player].IsState(PRESS_FACEBUTTON) || m_PlayerController[player].IsState(PRESS_PRIMARY)) { + // TODO: Sound? + // If we are pointing to an actor to follow, tehn give that kind of waypoint command + if (pTargetActor) + m_ControlledActor[player]->AddAIMOWaypoint(pTargetActor); + // Just pointing into somewhere in the scene, so give that command + else + m_ControlledActor[player]->AddAISceneWaypoint(m_ActorCursor[player]); + // Update the player's move path now to the first waypoint set + m_ControlledActor[player]->UpdateMovePath(); + if (m_pLastMarkedActor[player] && m_pLastMarkedActor[player]->GetPieMenu()) { + m_pLastMarkedActor[player]->GetPieMenu()->SetAnimationModeToNormal(); + } + } + } else if (m_ViewState[player] == ViewState::UnitSelectCircle) { + // Continuously display message + g_FrameMan.SetScreenText("Select units to group...", ScreenOfPlayer(player)); + + m_PlayerController[player].RelativeCursorMovement(m_ActorCursor[player]); + + Vector relativeToActor = m_ActorCursor[player] - m_ControlledActor[player]->GetPos(); + + float sceneWidth = static_cast(g_SceneMan.GetSceneWidth()); + float seamMinimum = 350.0F; + + // Check if we crossed the seam + if (g_SceneMan.GetScene()->WrapsX()) { + float halfSceneWidth = sceneWidth * 0.5F; + if (relativeToActor.MagnitudeIsGreaterThan(std::max(halfSceneWidth, seamMinimum))) { + if (m_ActorCursor->m_X < halfSceneWidth) { + relativeToActor = m_ActorCursor[player] + Vector(sceneWidth, 0) - m_ControlledActor[player]->GetPos(); + } else { + relativeToActor = m_ActorCursor[player] - Vector(sceneWidth, 0) - m_ControlledActor[player]->GetPos(); } - else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (actorPos.m_Y < pTargetBitmap->h)) - { - dotDrawPos.m_Y += sceneHeight; + } + } + + // Limit selection range + relativeToActor = relativeToActor.CapMagnitude(seamMinimum); + m_ActorCursor[player] = m_ControlledActor[player]->GetPos() + relativeToActor; + + bool wrapped; + + // Set the view to the cursor pos + wrapped = g_SceneMan.ForceBounds(m_ActorCursor[player]); + // g_CameraMan.SetScrollTarget(m_ActorCursor[player], 0.1, wrapped, ScreenOfPlayer(player)); + + // Set the view to the actor pos + Vector scrollPos = Vector(m_ControlledActor[player]->GetPos()); + g_SceneMan.ForceBounds(scrollPos); + g_CameraMan.SetScrollTarget(scrollPos, 0.1, ScreenOfPlayer(player)); + + // Disable the actor's controller + m_ControlledActor[player]->GetController()->SetDisabled(true); + + // Player is done setting waypoints + if (m_PlayerController[player].IsState(PRESS_SECONDARY) || m_PlayerController[player].IsState(ACTOR_NEXT_PREP) || m_PlayerController[player].IsState(ACTOR_PREV_PREP)) { + // Give player control back to actor + m_ControlledActor[player]->GetController()->SetDisabled(false); + // Switch back to normal view + m_ViewState[player] = ViewState::Normal; + // Stop displaying the message + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + } + // Player set a new waypoint + else if (m_ControlledActor[player] && m_PlayerController[player].IsState(PRESS_FACEBUTTON) || m_PlayerController[player].IsState(PRESS_PRIMARY)) { + // m_ControlledActor[player]->AddAISceneWaypoint(m_ActorCursor[player]); + // Give player control back to actor + m_ControlledActor[player]->GetController()->SetDisabled(false); + // Switch back to normal view + m_ViewState[player] = ViewState::Normal; + // Stop displaying the message + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + + // Switch commander to sentry mode + m_ControlledActor[player]->SetAIMode(Actor::AIMODE_SENTRY); + + // Detect nearby actors and attach them to commander + float sqrRadius = g_SceneMan.ShortestDistance(m_ActorCursor[player], m_ControlledActor[player]->GetPos(), true).GetSqrMagnitude(); + + Actor* pActor = 0; + Actor* pFirstActor = 0; + + // Get the first one + pFirstActor = pActor = g_MovableMan.GetNextTeamActor(m_ControlledActor[player]->GetTeam()); + + do { + // Set up commander if actor is not player controlled and not brain + if (pActor && !pActor->GetController()->IsPlayerControlled() && !pActor->IsInGroup("Brains")) { + // If human, set appropriate AI mode + if (dynamic_cast(pActor) || dynamic_cast(pActor)) + if (g_SceneMan.ShortestDistance(m_ControlledActor[player]->GetPos(), pActor->GetPos(), true).GetSqrMagnitude() < sqrRadius) { + pActor->FlashWhite(); + pActor->ClearAIWaypoints(); + pActor->SetAIMode(Actor::AIMODE_SQUAD); + pActor->AddAIMOWaypoint(m_ControlledActor[player]); + pActor->UpdateMovePath(); // Make sure pActor has m_ControlledActor registered as an AIMOWaypoint + } + } + + // Next! + pActor = g_MovableMan.GetNextTeamActor(team, pActor); + } while (pActor && pActor != pFirstActor); + } + } + /////////////////////////////////////////////////// + // Selecting LZ, a place for the craft to land + + else if (m_ViewState[player] == ViewState::LandingZoneSelect) { + g_FrameMan.SetScreenText("Choose your landing zone... Hold UP or DOWN to place multiple orders", ScreenOfPlayer(player)); + + // Save the x pos so we can see which direction the user is moving it + float prevLZX = m_LandingZone[player].m_X; + + // See if there's analog input + if (m_PlayerController[player].GetAnalogMove().m_X > 0.1) + m_LandingZone[player].m_X += m_PlayerController[player].GetAnalogMove().m_X * 8; + // Try the mouse + else if (!m_PlayerController[player].GetMouseMovement().IsZero()) + m_LandingZone[player].m_X += m_PlayerController[player].GetMouseMovement().m_X; + // Digital movement + else { + if (m_PlayerController[player].IsState(MOVE_RIGHT)) + m_LandingZone[player].m_X += 8; + else if (m_PlayerController[player].IsState(MOVE_LEFT)) + m_LandingZone[player].m_X -= 8; + } + + // Limit the LZ selection to the special LZ Area:s both specified in the derived Activity and moving with the brain + if (!m_LandingZoneArea[m_Team[player]].HasNoArea()) { + // Add up the static LZ loaded from the Scene to the one(s) around the player's team's brains + Scene::Area totalLZ(m_LandingZoneArea[m_Team[player]]); + /* This whole concept kinda sucks - defensive AA robots are more fun way to go to prevent bumrushing the brain with craft + for (int p = Players::PlayerOne; p < Players::MaxPlayerCount; ++p) + { + if (!(m_IsActive[p] && m_IsHuman[p] && m_BrainLZWidth[p] > 0)) + continue; + // Same team as this player and has a brain + if (m_Brain[p] && m_Team[p] == m_Team[player]) + { + Box brainBox(Vector(0, 0), m_BrainLZWidth[p], 100); + // Center the brain LZ box aroud the player's brain + brainBox.SetCenter(m_Brain[p]->GetPos()); + // Add it to the total LZ + totalLZ.AddBox(brainBox); + } + } + */ + // Move the actual LZ cursor to within the valid LZ Area. We pass in 0 for direction so it doesn't try to wrap around on wrapping maps. + totalLZ.MovePointInsideX(m_LandingZone[player].m_X, 0); + } + + // Interface for the craft AI post-delivery mode + if (m_PlayerController[player].IsState(PRESS_DOWN)) { + if (m_AIReturnCraft[player]) { + g_GUISound.SelectionChangeSound()->Play(player); + } + + m_AIReturnCraft[player] = false; + } else if (m_PlayerController[player].IsState(PRESS_UP)) { + if (!m_AIReturnCraft[player]) { + g_GUISound.SelectionChangeSound()->Play(player); + } + + m_AIReturnCraft[player] = true; + } + + // Player canceled the order while selecting LZ - can't be done in pregame + if (m_PlayerController[player].IsState(PRESS_SECONDARY) && m_ActivityState != ActivityState::PreGame) { + // Switch back to normal view + m_ViewState[player] = ViewState::Normal; + // Play err sound to indicate cancellation + g_FrameMan.SetScreenText("Order canceled!", ScreenOfPlayer(player), 333); + m_MessageTimer[player].Reset(); + g_GUISound.UserErrorSound()->Play(player); + // Flash the same actor, jsut to show the control went back to him + if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { + m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); + } + } else if (m_PlayerController[player].IsState(PRESS_FACEBUTTON) || m_PlayerController[player].IsState(PRESS_PRIMARY)) { + m_LandingZone[player].m_Y = 0; + float lzOffsetY = 0; + // Holding up or down will allow the player to make multiple orders without exiting the delivery phase. TODO: this should probably have a cooldown? + if (!m_PlayerController[player].IsState(MOVE_UP) && !m_PlayerController[player].IsState(MOVE_DOWN)) { + m_LandingZone[player].m_Y = g_SceneMan.FindAltitude(m_LandingZone[player], g_SceneMan.GetSceneHeight(), 10, true); + if (!g_MovableMan.GetNextTeamActor(team)) { + m_ObservationTarget[player] = m_LandingZone[player]; + m_ViewState[player] = ViewState::Observe; + } else { + m_ViewState[player] = ViewState::Normal; + } + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + if (m_ControlledActor[player] && m_ControlledActor[player]->GetPieMenu()) { + m_ControlledActor[player]->GetPieMenu()->DoDisableAnimation(); + } + + CreateDelivery(player); + } else { + // Place the new marker above the cursor so that they don't intersect with each other. + lzOffsetY += m_AIReturnCraft[player] ? -32.0F : 32.0F; + if (g_SceneMan.GetTerrain()->GetOrbitDirection() == Directions::Down) { + lzOffsetY *= -1.0f; + } + + m_LandingZone[player].m_Y = g_SceneMan.FindAltitude(m_LandingZone[player], g_SceneMan.GetSceneHeight(), 10, true) + lzOffsetY; + + if (m_pBuyGUI[player]->GetTotalOrderCost() > GetTeamFunds(team)) { + g_GUISound.UserErrorSound()->Play(player); + m_FundsChanged[team] = true; + if (!g_MovableMan.GetNextTeamActor(team)) { + m_ObservationTarget[player] = m_LandingZone[player]; + m_ViewState[player] = ViewState::Observe; + } else { + m_ViewState[player] = ViewState::Normal; + } + } else { + CreateDelivery(player); + m_Deliveries[team].rbegin()->multiOrderYOffset = lzOffsetY; + } + } + // Revert the Y offset so that the cursor doesn't flinch. + m_LandingZone[player].m_Y -= lzOffsetY; + } + + g_SceneMan.ForceBounds(m_LandingZone[player]); + + // Interpolate the LZ altitude to the height of the highest terrain point at the player-chosen X + float prevHeight = m_LandingZone[player].m_Y; + + float viewOffset = g_FrameMan.GetPlayerScreenHeight() / 4; + m_LandingZone[player].m_Y = 0.0f; + if (g_SceneMan.GetTerrain() && g_SceneMan.GetTerrain()->GetOrbitDirection() == Directions::Down) { + m_LandingZone[player].m_Y = g_SceneMan.GetSceneHeight(); + viewOffset *= -1; + } + + m_LandingZone[player].m_Y = prevHeight + ((g_SceneMan.FindAltitude(m_LandingZone[player], g_SceneMan.GetSceneHeight(), 10, true) - prevHeight) * 0.2); + + // Set the view to a little above the LZ position + Vector viewTarget(m_LandingZone[player].m_X, m_LandingZone[player].m_Y - viewOffset); + g_CameraMan.SetScrollTarget(viewTarget, 0.1, ScreenOfPlayer(player)); + } + + //////////////////////////// + // Deathwatching + + else if (m_ViewState[player] == ViewState::DeathWatch) { + // Continuously deathwatch message + g_FrameMan.SetScreenText("Lost control of remote body!", ScreenOfPlayer(player)); + // Don't move anything, just stay put watching the death funnies + g_CameraMan.SetScrollTarget(m_DeathViewTarget[player], 0.1, ScreenOfPlayer(player)); + } + + //////////////////////////////////////////////////// + // Normal scrolling to view the currently controlled Actor + // But only if we're not editing something, because editor will scroll the screen himself + // and double scrolling will cause CC gitch when we'll cross the seam + else if (m_ControlledActor[player] && m_ActivityState != ActivityState::Editing && m_ActivityState != ActivityState::PreGame) { + g_CameraMan.SetScrollTarget(m_ControlledActor[player]->GetViewPoint(), 0.1, ScreenOfPlayer(player)); + } + + if (m_ControlledActor[player] && m_ViewState[player] != ViewState::DeathWatch && m_ViewState[player] != ViewState::ActorSelect && m_ViewState[player] != ViewState::AIGoToPoint && m_ViewState[player] != ViewState::UnitSelectCircle) { + PieMenu* controlledActorPieMenu = m_ControlledActor[player]->GetPieMenu(); + if (controlledActorPieMenu && m_ControlledActor[player]->GetController()->IsState(PIE_MENU_ACTIVE)) { + if (!m_BuyMenuEnabled && controlledActorPieMenu->IsEnabling()) { + controlledActorPieMenu->RemovePieSlicesByType(PieSlice::SliceType::BuyMenu); + } + + if (controlledActorPieMenu->IsEnabled() && controlledActorPieMenu->HasSubPieMenuOpen() && m_InventoryMenuGUI[player]->GetMenuMode() == InventoryMenuGUI::MenuMode::Carousel) { + m_InventoryMenuGUI[player]->SetEnabled(false); + } else if (m_InventoryMenuGUI[player]->GetMenuMode() == InventoryMenuGUI::MenuMode::Carousel || !m_InventoryMenuGUI[player]->IsVisible()) { + m_InventoryMenuGUI[player]->SetMenuMode(InventoryMenuGUI::MenuMode::Carousel); + m_InventoryMenuGUI[player]->EnableIfNotEmpty(); + } + } else if (!m_PlayerController[player].IsState(PIE_MENU_ACTIVE) && m_InventoryMenuGUI[player]->GetMenuMode() == InventoryMenuGUI::MenuMode::Carousel) { + m_InventoryMenuGUI[player]->SetEnabled(false); + } + + if (PieSlice::SliceType command = controlledActorPieMenu->GetPieCommand(); command != PieSlice::SliceType::NoType) { + // AI mode commands that need extra points set in special view modes here + // TODO I don't think these viewstates are actually used?! + if (command == PieSlice::SliceType::Sentry) { + m_ViewState[player] = ViewState::AISentryPoint; + } else if (command == PieSlice::SliceType::Patrol) { + m_ViewState[player] = ViewState::AIPatrolPoints; + } else if (command == PieSlice::SliceType::GoldDig) { + m_ViewState[player] = ViewState::AIGoldDigPoint; + } else if (command == PieSlice::SliceType::GoTo) { + m_ViewState[player] = ViewState::AIGoToPoint; + m_ControlledActor[player]->ClearAIWaypoints(); + m_ActorCursor[player] = m_ControlledActor[player]->GetPos(); + m_ControlledActor[player]->GetController()->SetDisabled(true); + } else if (command == PieSlice::SliceType::FormSquad) { + // Find out if we have any connected units, and disconnect them + bool isCommander = false; + + Actor* pActor = 0; + Actor* pFirstActor = 0; + + pFirstActor = pActor = g_MovableMan.GetNextTeamActor(m_ControlledActor[player]->GetTeam()); + + // Reset commander if we have any subordinates + do { + if (pActor) { + // Set appropriate AI mode + if (dynamic_cast(pActor) || dynamic_cast(pActor)) + if (pActor->GetAIMOWaypointID() == m_ControlledActor[player]->GetID()) { + pActor->FlashWhite(); + pActor->ClearAIWaypoints(); + pActor->SetAIMode((Actor::AIMode)m_ControlledActor[player]->GetAIMode()); // Inherit the leader's AI mode + isCommander = true; + } + } + pActor = g_MovableMan.GetNextTeamActor(team, pActor); + } while (pActor && pActor != pFirstActor); + + // Now turn on selection UI, if we didn't disconnect anyone + if (!isCommander) { + m_ViewState[player] = ViewState::UnitSelectCircle; + // Set cursor to the actor + m_ActorCursor[player] = m_ControlledActor[player]->GetPos() + Vector(50, -50); + // Disable Actor's controller while we set the waypoints + m_ControlledActor[player]->GetController()->SetDisabled(true); + if (controlledActorPieMenu) { + controlledActorPieMenu->SetEnabled(false); + } } + } else if (command == PieSlice::SliceType::BuyMenu) { + m_pBuyGUI[player]->SetEnabled(true); + skipBuyUpdate = true; + } else if (command == PieSlice::SliceType::FullInventory) { + controlledActorPieMenu->SetEnabled(false); + m_InventoryMenuGUI[player]->SetEnabled(false); + m_InventoryMenuGUI[player]->SetMenuMode(InventoryMenuGUI::MenuMode::Full); + m_InventoryMenuGUI[player]->SetEnabled(true); } + m_ControlledActor[player]->HandlePieCommand(command); + } - circlefill(pTargetBitmap, dotDrawPos.m_X, dotDrawPos.m_Y, 1, g_YellowGlowColor); - g_PostProcessMan.RegisterGlowArea(dotPos, 3); + m_InventoryMenuGUI[player]->SetInventoryActor(m_ControlledActor[player]); + m_InventoryMenuGUI[player]->Update(); + } + + /////////////////////////////////////// + // Update Buy Menu GUIs + + // Enable or disable the Buy Menus if the brain is selected, Skip if an LZ selection button press was just performed + if (!skipBuyUpdate) { + // m_pBuyGUI[player]->SetEnabled(m_ControlledActor[player] == m_Brain[player] && m_ViewState[player] != ViewState::LandingZoneSelect && m_ActivityState != ActivityState::Over); + m_pBuyGUI[player]->Update(); + } + + // Trap the mouse if we're in gameplay and not in menus + g_UInputMan.TrapMousePos(!m_pBuyGUI[player]->IsEnabled() && !m_InventoryMenuGUI[player]->IsEnabledAndNotCarousel() && !m_LuaLockActor[player], player); + + // Start LZ picking mode if a purchase was made + if (m_pBuyGUI[player]->PurchaseMade()) { + m_LZCursorWidth[player] = std::min(m_pBuyGUI[player]->GetDeliveryWidth(), g_FrameMan.GetPlayerScreenWidth() - 24); + m_pBuyGUI[player]->SetEnabled(false); + // SwitchToPrevActor(player, team, m_Brain[player]); + // Start selecting the landing zone + m_ViewState[player] = ViewState::LandingZoneSelect; + + // Set this to zero so the cursor interpolates down from the sky + float landingSpot = 0.0f; + if (g_SceneMan.GetTerrain() && g_SceneMan.GetTerrain()->GetOrbitDirection() == Directions::Down) { + landingSpot = g_SceneMan.GetSceneHeight(); } + + m_LandingZone[player].m_Y = landingSpot; } + + // After a while of game over, change messages to the final one for everyone + if (m_ActivityState == ActivityState::Over && m_GameOverTimer.IsPastRealMS(m_GameOverPeriod)) { + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + // g_FrameMan.SetScreenText("Press [Esc] to leave the battlefield", ScreenOfPlayer(player), 750); + if (g_FrameMan.IsInMultiplayerMode()) + g_FrameMan.SetScreenText("All players must press and hold [BACKSPACE] to continue!", ScreenOfPlayer(player), 750); + else + g_FrameMan.SetScreenText("Press [SPACE] or [START] to continue!", ScreenOfPlayer(player), 750); + + // Actually end on space + if (m_GameOverTimer.IsPastSimMS(55000) || g_UInputMan.AnyStartPress()) { + g_ActivityMan.EndActivity(); + g_ActivityMan.SetInActivity(false); + } + } + + /////////////////////////////////// + // Enable/disable controlled actors' AI as appropriate when in menus + + if (m_ControlledActor[player] && m_ControlledActor[player]->GetController()->GetPlayerRaw() == player) { + // Don't disable when pie menu is active; it is done inside the Controller Update + if (m_pBuyGUI[player]->IsVisible() || m_ViewState[player] == ViewState::ActorSelect || m_ViewState[player] == ViewState::LandingZoneSelect || m_ViewState[player] == ViewState::Observe) { + m_ControlledActor[player]->GetController()->SetInputMode(Controller::CIM_AI); + } else if (m_InventoryMenuGUI[player]->IsEnabledAndNotCarousel()) { + m_ControlledActor[player]->GetController()->SetInputMode(Controller::CIM_DISABLED); + } else if (m_LuaLockActor[player]) { + m_ControlledActor[player]->GetController()->SetInputMode(m_LuaLockActorMode[player]); + } else { + m_ControlledActor[player]->GetController()->SetInputMode(Controller::CIM_PLAYER); + } + } + + /////////////////////////////////////// + // Configure banners to show when important things happen, like the game over or death of brain + + if (IsOver()) { + // Override previous messages + if (m_pBannerRed[player]->IsVisible() && m_pBannerRed[player]->GetBannerText() != "FAIL") + m_pBannerRed[player]->HideText(2500, 0); + if (m_pBannerYellow[player]->IsVisible() && m_pBannerYellow[player]->GetBannerText() != "WIN") + m_pBannerYellow[player]->HideText(2500, 0); + + // Player on a winning team + if (GetWinnerTeam() == m_Team[player] && !m_pBannerYellow[player]->IsVisible()) + m_pBannerYellow[player]->ShowText("WIN", GUIBanner::FLYBYRIGHTWARD, 1000, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 400); + + // Loser player + if (GetWinnerTeam() != m_Team[player] && !m_pBannerRed[player]->IsVisible()) + m_pBannerRed[player]->ShowText("FAIL", GUIBanner::FLYBYLEFTWARD, 1000, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 400); + } + // If a player had a brain that is now dead, but his team is not yet done, show the dead banner on his screen + else if (m_ActivityState != ActivityState::Editing && m_ActivityState != ActivityState::Starting && m_HadBrain[player] && !m_Brain[player] && !m_pBannerRed[player]->IsVisible()) { + // If repeated too many times, just let the banner stop at showing and not cycle + if (m_BannerRepeats[player]++ < 6) + m_pBannerRed[player]->ShowText("DEAD", GUIBanner::FLYBYLEFTWARD, 1000, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 400); + else + m_pBannerRed[player]->ShowText("DEAD", GUIBanner::FLYBYLEFTWARD, -1, Vector(g_FrameMan.GetPlayerFrameBufferWidth(player), g_FrameMan.GetPlayerFrameBufferHeight(player)), 0.5, 1500, 400); + } + + /////////////////////////////////////// + // Update message banners + + m_pBannerRed[player]->Update(); + m_pBannerYellow[player]->Update(); } - else - { - // Cancel squad selection + /////////////////////////////////////////// + // Iterate through all teams + + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; + + // Pause deliveries if game hasn't started yet + if (m_ActivityState == ActivityState::PreGame) { + for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) + (*itr).timer.Reset(); + } + + //////////////////////////////// + // Delivery status update + + if (!m_Deliveries[team].empty()) { + int player = m_Deliveries[team].front().orderedByPlayer; + if (m_MessageTimer[player].IsPastSimMS(1000)) { + char message[512]; + std::snprintf(message, sizeof(message), "Next delivery in %i secs", ((int)m_Deliveries[team].front().delay - (int)m_Deliveries[team].front().timer.GetElapsedSimTimeMS()) / 1000); + g_FrameMan.SetScreenText(message, ScreenOfPlayer(player)); + m_MessageTimer[player].Reset(); + } + } + + // Delivery has arrived! Unpack and put into the world + if (!m_Deliveries[team].empty() && m_Deliveries[team].front().timer.IsPastSimMS(m_Deliveries[team].front().delay)) { + // This is transferring ownership of the craft instance from the Delivery struct + ACraft* pDeliveryCraft = m_Deliveries[team].front().pCraft; + int player = m_Deliveries[team].front().orderedByPlayer; + if (pDeliveryCraft) { + g_FrameMan.SetScreenText("Your order has arrived!", ScreenOfPlayer(player), 333); + m_MessageTimer[player].Reset(); + + pDeliveryCraft->ResetAllTimers(); + pDeliveryCraft->Update(); + + // Add the delivery craft to the world, TRANSFERRING OWNERSHIP + g_MovableMan.AddActor(pDeliveryCraft); + /* + // If the player who ordered this seems stuck int he manu waiting for the delivery, give him direct control + if (m_ControlledActor[player] == m_Brain[player] && m_ViewState[player] != ViewState::LandingZoneSelect) + { + SwitchToActor(pDeliveryCraft, player, team); + } + */ + } + m_Deliveries[team].pop_front(); + } } + + /////////////////////////////////////////// + // Special observer mode for the FOURTH screen in a THREE player game + /* Problematic with new player scheme.. what if the fourth player is active?? + if (m_PlayerCount == 3) + { + // Update the controller of the observation view + m_PlayerController[Players::PlayerFour].Update(); + + // Observer user control override + if (m_PlayerController[Players::PlayerFour].RelativeCursorMovement(m_ObservationTarget[Players::PlayerFour], 1.2)) + m_DeathTimer[Players::PlayerFour].Reset(); + + // If no user input in a few seconds, start scrolling along the terrain + if (m_DeathTimer[Players::PlayerFour].IsPastSimMS(5000)) + { + // Make it scroll along + m_ObservationTarget[Players::PlayerFour].m_X += 0.5; + + // Make view follow the terrain + float prevHeight = m_ObservationTarget[Players::PlayerFour].m_Y; + m_ObservationTarget[Players::PlayerFour].m_Y = 0; + m_ObservationTarget[Players::PlayerFour].m_Y = prevHeight + ((g_SceneMan.FindAltitude(m_ObservationTarget[Players::PlayerFour], g_SceneMan.GetSceneHeight(), 20, true) - prevHeight) * 0.02); + } + + // Set the view to the observation position + g_CameraMan.SetScrollTarget(m_ObservationTarget[Players::PlayerFour], 0.1, g_SceneMan.ForceBounds(m_ObservationTarget[Players::PlayerFour]), ScreenOfPlayer(Players::PlayerFour)); + } + */ } - if ((m_ActivityState == ActivityState::Editing || m_ActivityState == ActivityState::PreGame) && m_pEditorGUI[PoS]) - m_pEditorGUI[PoS]->Draw(pTargetBitmap, targetPos); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + + void GameActivity::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + if (which < 0 || which >= c_MaxScreenCount) + return; + + char str[512]; + int yTextPos = 0; + int team = Teams::NoTeam; + int cursor = 0; + int PoS = PlayerOfScreen(which); + if (PoS < Players::PlayerOne || PoS >= Players::MaxPlayerCount) + return; + Box screenBox(targetPos, pTargetBitmap->w, pTargetBitmap->h); + GUIFont* pLargeFont = g_FrameMan.GetLargeFont(); + GUIFont* pSmallFont = g_FrameMan.GetSmallFont(); + AllegroBitmap pBitmapInt(pTargetBitmap); + int frame = ((int)m_CursorTimer.GetElapsedSimTimeMS() % 1000) / 250; + Vector landZone; + + // Iterate through all players, drawing each currently used LZ cursor. + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + if (m_ViewState[player] == ViewState::LandingZoneSelect) { + int halfWidth = std::max(m_LZCursorWidth[player] / 2, 36); + team = m_Team[player]; + if (team == Teams::NoTeam) + continue; + cursor = team; + landZone = m_LandingZone[player] - targetPos; + // Cursor + draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X - halfWidth, landZone.m_Y - 48); + draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); + // Text + pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 42, m_AIReturnCraft[player] ? "Deliver here" : "Travel here", GUIFont::Centre); + pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 36, "and then", GUIFont::Centre); + pLargeFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 30, m_AIReturnCraft[player] ? "RETURN" : "STAY", GUIFont::Centre); + // Draw wrap around the world if necessary, and only if this is being drawn directly to a scenewide target bitmap + if (targetPos.IsZero() && (landZone.m_X < halfWidth || landZone.m_X > g_SceneMan.GetSceneWidth() - halfWidth)) { + // Wrap shit around and draw dupe on the other side + int wrappedX = landZone.m_X + (landZone.m_X < halfWidth ? g_SceneMan.GetSceneWidth() : -g_SceneMan.GetSceneWidth()); + // Cursor + draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX - halfWidth, landZone.m_Y - 48); + draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); + // Text + pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 42, m_AIReturnCraft[player] ? "Deliver here" : "Travel here", GUIFont::Centre); + pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 36, "and then", GUIFont::Centre); + pLargeFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 30, m_AIReturnCraft[player] ? "RETURN" : "STAY", GUIFont::Centre); + } + } + } - // Draw Banners - m_pBannerRed[PoS]->Draw(pTargetBitmap); - m_pBannerYellow[PoS]->Draw(pTargetBitmap); -} + // Iterate through all teams, drawing all pending delivery cursors + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; + char str[64]; + cursor = team; + for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) { + int halfWidth = 24; + landZone = itr->landingZone - targetPos; + bool anyPlayerOnTeamIsInLandingZoneSelectViewState = false; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (GetTeamOfPlayer(player) == team && m_ViewState[player] == ViewState::LandingZoneSelect) { + anyPlayerOnTeamIsInLandingZoneSelectViewState = true; + break; + } + } + if (!anyPlayerOnTeamIsInLandingZoneSelectViewState) { + landZone.m_Y -= itr->multiOrderYOffset; + } + // Cursor + draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X - halfWidth, landZone.m_Y - 48); + draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); + // Text + pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 38, "ETA:", GUIFont::Centre); + if (m_ActivityState == ActivityState::PreGame) + std::snprintf(str, sizeof(str), "???s"); + else + std::snprintf(str, sizeof(str), "%is", ((int)itr->delay - (int)itr->timer.GetElapsedSimTimeMS()) / 1000); + pLargeFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 32, str, GUIFont::Centre); + // Draw wrap around the world if necessary, and only if this is being drawn directly to a scenewide target bitmap + if (targetPos.IsZero() && (landZone.m_X < halfWidth || landZone.m_X > g_SceneMan.GetSceneWidth() - halfWidth)) { + // Wrap shit around and draw dupe on the other side + int wrappedX = landZone.m_X + (landZone.m_X < halfWidth ? g_SceneMan.GetSceneWidth() : -g_SceneMan.GetSceneWidth()); + // Cursor + draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX - halfWidth, landZone.m_Y - 48); + draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); + // Text + pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 38, "ETA:", GUIFont::Centre); + pLargeFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 32, str, GUIFont::Centre); + } + } + } + // The team of the screen + team = m_Team[PoS]; + if (team == Teams::NoTeam) + return; + + // None of the following player-specific GUI elements apply if this isn't a played human actor + if (!(m_IsActive[PoS] && m_IsHuman[PoS])) + return; + + // Get all possible wrapped boxes of the screen + std::list wrappedBoxes; + g_SceneMan.WrapBox(screenBox, wrappedBoxes); + Vector wrappingOffset, objScenePos, onScreenEdgePos; + float distance, shortestDist; + float sceneWidth = static_cast(g_SceneMan.GetSceneWidth()); + float halfScreenWidth = pTargetBitmap->w / 2; + float halfScreenHeight = pTargetBitmap->h / 2; + // THis is the max distance that is possible between a point inside the scene, but outside the screen box, and the screen box's outer edge closest to the point (taking wrapping into account) + float maxOffScreenSceneWidth = g_SceneMan.SceneWrapsX() ? ((sceneWidth / 2) - halfScreenWidth) : (sceneWidth - pTargetBitmap->w); + // These handle arranging the left and right stacks of arrows, so they don't pile up on top of each other + cursor = team; + float leftStackY = halfScreenHeight - m_aObjCursor[cursor][frame]->h * 2; + float rightStackY = leftStackY; + + // Draw the objective points this player should care about + for (std::list::iterator itr = m_Objectives.begin(); itr != m_Objectives.end(); ++itr) { + // Only draw objectives of the same team as the current player + if (itr->m_Team == team) { + // Iterate through the wrapped screen boxes - will only be one if there's no wrapping + // Try to the find one that contains the objective point + bool withinAny = false; + std::list::iterator nearestBoxItr = wrappedBoxes.begin(); + shortestDist = 1000000.0; + for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + // See if we found the point to be within the screen or not + if (wItr->IsWithinBox((*itr).m_ScenePos)) { + nearestBoxItr = wItr; + withinAny = true; + break; + } + // Well, which wrapped screen box is closest to the point? + distance = g_SceneMan.ShortestDistance(wItr->GetCenter(), (*itr).m_ScenePos).GetLargest(); + if (distance < shortestDist) { + shortestDist = distance; + nearestBoxItr = wItr; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this GameActivity's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. - -void GameActivity::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) -{ - GUIFont *pLargeFont = g_FrameMan.GetLargeFont(); - GUIFont *pSmallFont = g_FrameMan.GetSmallFont(); - AllegroBitmap pBitmapInt(pTargetBitmap); - int frame = ((int)m_CursorTimer.GetElapsedSimTimeMS() % 1000) / 250; - int cursor = 0; - Vector landZone; - - // Iterate through all players, drawing each currently used LZ cursor. - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (!(m_IsActive[player] && m_IsHuman[player])) - continue; - - if (m_ViewState[player] == ViewState::LandingZoneSelect) - { - int halfWidth = std::max(m_LZCursorWidth[player] / 2, 36); - int team = m_Team[player]; - if (team == Teams::NoTeam) - continue; - landZone = m_LandingZone[player] - targetPos; - // Cursor - draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X - halfWidth, landZone.m_Y - 48); - draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); - // Text - pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 42, m_AIReturnCraft[player] ? "Deliver here" : "Travel here", GUIFont::Centre); - pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 36, "and then", GUIFont::Centre); - pLargeFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 30, m_AIReturnCraft[player] ? "RETURN" : "STAY", GUIFont::Centre); - // Draw wrap around the world if necessary, and only if this is being drawn directly to a scenewide target bitmap - if (targetPos.IsZero() && (landZone.m_X < halfWidth || landZone.m_X > g_SceneMan.GetSceneWidth() - halfWidth)) - { - // Wrap shit around and draw dupe on the other side - int wrappedX = landZone.m_X + (landZone.m_X < halfWidth ? g_SceneMan.GetSceneWidth() : -g_SceneMan.GetSceneWidth()); - // Cursor - draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX - halfWidth, landZone.m_Y - 48); - draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); - // Text - pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 42, m_AIReturnCraft[player] ? "Deliver here" : "Travel here", GUIFont::Centre); - pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 36, "and then", GUIFont::Centre); - pLargeFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 30, m_AIReturnCraft[player] ? "RETURN" : "STAY", GUIFont::Centre); - } - } - } - - // Iterate through all teams, drawing all pending delivery cursors - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - char str[64]; - for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) - { - int halfWidth = 24; - landZone = itr->landingZone - targetPos; - // Cursor - draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X - halfWidth, landZone.m_Y - 48); - draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); - // Text - pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 38, "ETA:", GUIFont::Centre); - if (m_ActivityState == ActivityState::PreGame) - std::snprintf(str, sizeof(str), "???s"); - else - std::snprintf(str, sizeof(str), "%is", ((int)itr->delay - (int)itr->timer.GetElapsedSimTimeMS()) / 1000); - pLargeFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 32, str, GUIFont::Centre); - // Draw wrap around the world if necessary, and only if this is being drawn directly to a scenewide target bitmap - if (targetPos.IsZero() && (landZone.m_X < halfWidth || landZone.m_X > g_SceneMan.GetSceneWidth() - halfWidth)) - { - // Wrap shit around and draw dupe on the other side - int wrappedX = landZone.m_X + (landZone.m_X < halfWidth ? g_SceneMan.GetSceneWidth() : -g_SceneMan.GetSceneWidth()); - // Cursor - draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX - halfWidth, landZone.m_Y - 48); - draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); - // Text - pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 38, "ETA:", GUIFont::Centre); - pLargeFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 32, str, GUIFont::Centre); - } - } - } -} + // Get the difference that the wrapped screen has from the actual one + wrappingOffset = screenBox.GetCorner() - nearestBoxItr->GetCorner(); + // Apply that offet to the objective point's position + objScenePos = itr->m_ScenePos + wrappingOffset; + + // Objective is within the screen, so draw the set arrow over it + if (withinAny) + itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], objScenePos - targetPos, itr->m_ArrowDir); + // Outside the screen, so draw it at the edge of it + else { + // Figure out which point is closest to the box, taking scene wrapping into account + objScenePos = nearestBoxItr->GetCenter() + g_SceneMan.ShortestDistance(nearestBoxItr->GetCenter(), objScenePos); + // Shortest distance from the edge of the screen box, not the center. + shortestDist -= halfScreenWidth; + + // Make the arrow point toward the edge of the screen + if (objScenePos.m_X >= nearestBoxItr->GetCorner().m_X + nearestBoxItr->GetWidth()) { + // Make the edge position approach the center of the vertical edge of the screen the farther away the objective position is from the screen + onScreenEdgePos = nearestBoxItr->GetWithinBox(objScenePos) - targetPos; + // Double the EaseIn to make it even more exponential, want the arrow to stay close to the edge for a long time + onScreenEdgePos.m_Y = rightStackY + EaseIn(0, onScreenEdgePos.m_Y - rightStackY, EaseIn(0, 1.0, 1.0 - (shortestDist / maxOffScreenSceneWidth))); + itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], onScreenEdgePos, ARROWRIGHT); + // Stack cursor moves down an arrowheight + rightStackY += m_aObjCursor[cursor][frame]->h; + } else if (objScenePos.m_X < nearestBoxItr->GetCorner().m_X) { + // Make the edge position approach the center of the vertical edge of the screen the farther away the objective position is from the screen + onScreenEdgePos = nearestBoxItr->GetWithinBox(objScenePos) - targetPos; + // Double the EaseIn to make it even more exponential, want the arrow to stay close to the edge for a long time + onScreenEdgePos.m_Y = leftStackY + EaseIn(0, onScreenEdgePos.m_Y - leftStackY, EaseIn(0, 1.0, 1.0 - (shortestDist / maxOffScreenSceneWidth))); + itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], onScreenEdgePos, ARROWLEFT); + // Stack cursor moves down an arrowheight + leftStackY += m_aObjCursor[cursor][frame]->h; + } else if (objScenePos.m_Y < nearestBoxItr->GetCorner().m_Y) + itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], nearestBoxItr->GetWithinBox(objScenePos) - targetPos, ARROWUP); + else + itr->Draw(pTargetBitmap, m_aObjCursor[cursor][frame], nearestBoxItr->GetWithinBox(objScenePos) - targetPos, ARROWDOWN); + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActiveCPUTeamCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns active CPU team count. -// Arguments: None. -// Return value: Returns active CPU team count. + // Team Icon up in the top left corner + const Icon* pIcon = GetTeamIcon(m_Team[PoS]); + if (pIcon) + draw_sprite(pTargetBitmap, pIcon->GetBitmaps8()[0], MAX(2, g_CameraMan.GetScreenOcclusion(which).m_X + 2), 2); + // Gold + std::snprintf(str, sizeof(str), "%c Funds: %.10g oz", TeamFundsChanged(which) ? -57 : -58, std::floor(GetTeamFunds(m_Team[PoS]))); + g_FrameMan.GetLargeFont()->DrawAligned(&pBitmapInt, MAX(16, g_CameraMan.GetScreenOcclusion(which).m_X + 16), yTextPos, str, GUIFont::Left); + /* Not applicable anymore to the 4-team games + // Body losses + std::snprintf(str, sizeof(str), "%c Losses: %c%i %c%i", -39, -62, GetTeamDeathCount(Teams::TeamOne), -59, GetTeamDeathCount(Teams::TeamTwo)); + g_FrameMan.GetLargeFont()->DrawAligned(&pBitmapInt, MIN(pTargetBitmap->w - 4, pTargetBitmap->w - 4 + g_CameraMan.GetScreenOcclusion(which).m_X), yTextPos, str, GUIFont::Right); + */ + // Show the player's controller scheme icon in the upper right corner of his screen, but only for a minute + if (m_GameTimer.GetElapsedRealTimeS() < 30) { + // TODO: Only blink if there hasn't been any input on a controller since start of game?? + // Blink them at first, but only if there's more than one human player + if (m_GameTimer.GetElapsedRealTimeS() > 4 || m_GameTimer.AlternateReal(150) || GetHumanCount() < 2) { + pIcon = g_UInputMan.GetSchemeIcon(PoS); + if (pIcon) { + draw_sprite(pTargetBitmap, pIcon->GetBitmaps8()[0], MIN(pTargetBitmap->w - pIcon->GetBitmaps8()[0]->w - 2, pTargetBitmap->w - pIcon->GetBitmaps8()[0]->w - 2 + g_CameraMan.GetScreenOcclusion(which).m_X), yTextPos); + // TODO: make a black Activity intro screen, saying "Player X, press any key/button to show that you are ready!, and display their controller icon, then fade into the scene" + // stretch_sprite(pTargetBitmap, pIcon->GetBitmaps8()[0], 10, 10, pIcon->GetBitmaps8()[0]->w * 4, pIcon->GetBitmaps8()[0]->h * 4); + } + } + } -int GameActivity::GetActiveCPUTeamCount() const -{ - int count = 0; + if (m_ActivityState == ActivityState::Running) { + if (m_InventoryMenuGUI[PoS] && m_InventoryMenuGUI[PoS]->IsVisible()) { + m_InventoryMenuGUI[PoS]->Draw(pTargetBitmap, targetPos); + } + if (m_pBuyGUI[PoS] && m_pBuyGUI[PoS]->IsVisible()) { + m_pBuyGUI[PoS]->Draw(pTargetBitmap); + } + } - for (int team = Teams::TeamOne; team < Activity::MaxTeamCount; team++) - if (TeamActive(team) && TeamIsCPU(team)) - count++; + // Draw actor picking crosshairs if applicable + if (m_ViewState[PoS] == ViewState::ActorSelect && m_IsActive[PoS] && m_IsHuman[PoS]) { + Vector center = m_ActorCursor[PoS] - targetPos; + circle(pTargetBitmap, center.m_X, center.m_Y, m_CursorTimer.AlternateReal(150) ? 6 : 8, g_YellowGlowColor); + // Add pixel glow area around it, in scene coordinates + g_PostProcessMan.RegisterGlowArea(m_ActorCursor[PoS], 10); + /* Crosshairs + putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); + */ + } + // AI point commands cursor + else if (m_ViewState[PoS] == ViewState::AIGoToPoint) { + Vector center = m_ActorCursor[PoS] - targetPos; + circle(pTargetBitmap, center.m_X, center.m_Y, m_CursorTimer.AlternateReal(150) ? 6 : 8, g_YellowGlowColor); + circlefill(pTargetBitmap, center.m_X, center.m_Y, 2, g_YellowGlowColor); + // putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); + // Add pixel glow area around it, in scene coordinates + g_PostProcessMan.RegisterGlowArea(m_ActorCursor[PoS], 10); - return count; -} + // Draw a line from the last set waypoint to the cursor + if (m_ControlledActor[PoS] && g_MovableMan.IsActor(m_ControlledActor[PoS])) + g_FrameMan.DrawLine(pTargetBitmap, m_ControlledActor[PoS]->GetLastAIWaypoint() - targetPos, m_ActorCursor[PoS] - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, 0, true); + } + // Group selection circle + else if (m_ViewState[PoS] == ViewState::UnitSelectCircle) { + if (m_ControlledActor[PoS] && g_MovableMan.IsActor(m_ControlledActor[PoS])) { + Vector cursorDrawPos = m_ActorCursor[PoS] - targetPos; + Vector actorPos = m_ControlledActor[PoS]->GetPos(); + Vector drawPos = actorPos - targetPos; + + // Fix cursor coordinates + if (!targetPos.IsZero()) { + // Spans vertical scene seam + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) { + if ((targetPos.m_X < 0) && (m_ActorCursor[PoS].m_X > (sceneWidth - pTargetBitmap->w))) + cursorDrawPos.m_X -= sceneWidth; + else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_ActorCursor[PoS].m_X < pTargetBitmap->w)) + cursorDrawPos.m_X += sceneWidth; + } + // Spans horizontal scene seam + int sceneHeight = g_SceneMan.GetSceneHeight(); + if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) { + if ((targetPos.m_Y < 0) && (m_ActorCursor[PoS].m_Y > (sceneHeight - pTargetBitmap->h))) + cursorDrawPos.m_Y -= sceneHeight; + else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_ActorCursor[PoS].m_Y < pTargetBitmap->h)) + cursorDrawPos.m_Y += sceneHeight; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActiveHumanTeamCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns active human team count. -// Arguments: None. -// Return value: Returns active human team count. + // Fix circle center coordinates + if (!targetPos.IsZero()) { + // Spans vertical scene seam + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) { + if ((targetPos.m_X < 0) && (actorPos.m_X > (sceneWidth - pTargetBitmap->w))) + drawPos.m_X -= sceneWidth; + else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (actorPos.m_X < pTargetBitmap->w)) + drawPos.m_X += sceneWidth; + } + // Spans horizontal scene seam + int sceneHeight = g_SceneMan.GetSceneHeight(); + if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) { + if ((targetPos.m_Y < 0) && (actorPos.m_Y > (sceneHeight - pTargetBitmap->h))) + drawPos.m_Y -= sceneHeight; + else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (actorPos.m_Y < pTargetBitmap->h)) + drawPos.m_Y += sceneHeight; + } + } -int GameActivity::GetActiveHumanTeamCount() const -{ - int count = 0; + float radius = g_SceneMan.ShortestDistance(m_ActorCursor[PoS], m_ControlledActor[PoS]->GetPos(), true).GetMagnitude(); - for (int team = Teams::TeamOne; team < Activity::MaxTeamCount; team++) - if (TeamActive(team) && !TeamIsCPU(team)) - count++; + circle(pTargetBitmap, cursorDrawPos.m_X, cursorDrawPos.m_Y, m_CursorTimer.AlternateReal(150) ? 6 : 8, g_YellowGlowColor); + circlefill(pTargetBitmap, cursorDrawPos.m_X, cursorDrawPos.m_Y, 2, g_YellowGlowColor); - return count; -} + Vector unwrappedPos; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OtherTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next other team number from the one passed in, if any. If there -// are more than two teams in this game, then the next one in the series -// will be returned here. + // Check if we crossed the seam + if (g_SceneMan.GetScene()->WrapsX()) { + // Calculate unwrapped cursor position, or it won't glow + unwrappedPos = m_ActorCursor[PoS] - m_ControlledActor[PoS]->GetPos(); + float halfSceneWidth = sceneWidth * 0.5F; + float seamMinimum = 350.0F; -int GameActivity::OtherTeam(int team) -{ - // Only one team in this game, so can't return another one - if (m_TeamCount == 1) - return Teams::NoTeam; + if (unwrappedPos.MagnitudeIsGreaterThan(std::max(halfSceneWidth, seamMinimum))) { + if (m_ActorCursor->m_X < halfSceneWidth) + unwrappedPos = m_ActorCursor[PoS] + Vector(sceneWidth, 0); + else + unwrappedPos = m_ActorCursor[PoS] - Vector(sceneWidth, 0); + g_PostProcessMan.RegisterGlowArea(unwrappedPos, 10); + } + } else + unwrappedPos = m_ActorCursor[PoS]; + + g_PostProcessMan.RegisterGlowArea(m_ActorCursor[PoS], 10); + + // Glowing dotted circle version + int dots = 2 * c_PI * radius / 25; // 5 + (int)(radius / 10); + float radsperdot = 2 * 3.14159265359 / dots; + + for (int i = 0; i < dots; i++) { + Vector dotPos = Vector(actorPos.m_X + sin(i * radsperdot) * radius, actorPos.m_Y + cos(i * radsperdot) * radius); + Vector dotDrawPos = dotPos - targetPos; + + if (!targetPos.IsZero()) { + // Spans vertical scene seam + if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) { + if ((targetPos.m_X < 0) && (dotPos.m_X > (sceneWidth - pTargetBitmap->w))) { + dotDrawPos.m_X -= sceneWidth; + } else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (actorPos.m_X < pTargetBitmap->w)) { + dotDrawPos.m_X += sceneWidth; + } + } + // Spans horizontal scene seam + int sceneHeight = g_SceneMan.GetSceneHeight(); + if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) { + if ((targetPos.m_Y < 0) && (dotPos.m_Y > (sceneHeight - pTargetBitmap->h))) { + dotDrawPos.m_Y -= sceneHeight; + } else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (actorPos.m_Y < pTargetBitmap->h)) { + dotDrawPos.m_Y += sceneHeight; + } + } - // Find another team that is active - bool loopedOnce = false; - for (int t = team + 1; ; t++) - { - // Loop - if (t >= MaxTeamCount) - t = Teams::TeamOne; + circlefill(pTargetBitmap, dotDrawPos.m_X, dotDrawPos.m_Y, 1, g_YellowGlowColor); + g_PostProcessMan.RegisterGlowArea(dotPos, 3); + } + } + } else { + // Cancel squad selection + } + } - if (t == team) - break; + if ((m_ActivityState == ActivityState::Editing || m_ActivityState == ActivityState::PreGame) && m_pEditorGUI[PoS]) + m_pEditorGUI[PoS]->Draw(pTargetBitmap, targetPos); - if (m_TeamActive[t]) - return t; - } + // Draw Banners + m_pBannerRed[PoS]->Draw(pTargetBitmap); + m_pBannerYellow[PoS]->Draw(pTargetBitmap); + } - return Teams::NoTeam; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this GameActivity's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + + void GameActivity::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + GUIFont* pLargeFont = g_FrameMan.GetLargeFont(); + GUIFont* pSmallFont = g_FrameMan.GetSmallFont(); + AllegroBitmap pBitmapInt(pTargetBitmap); + int frame = ((int)m_CursorTimer.GetElapsedSimTimeMS() % 1000) / 250; + int cursor = 0; + Vector landZone; + + // Iterate through all players, drawing each currently used LZ cursor. + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (!(m_IsActive[player] && m_IsHuman[player])) + continue; + + if (m_ViewState[player] == ViewState::LandingZoneSelect) { + int halfWidth = std::max(m_LZCursorWidth[player] / 2, 36); + int team = m_Team[player]; + if (team == Teams::NoTeam) + continue; + landZone = m_LandingZone[player] - targetPos; + // Cursor + draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X - halfWidth, landZone.m_Y - 48); + draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); + // Text + pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 42, m_AIReturnCraft[player] ? "Deliver here" : "Travel here", GUIFont::Centre); + pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 36, "and then", GUIFont::Centre); + pLargeFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 30, m_AIReturnCraft[player] ? "RETURN" : "STAY", GUIFont::Centre); + // Draw wrap around the world if necessary, and only if this is being drawn directly to a scenewide target bitmap + if (targetPos.IsZero() && (landZone.m_X < halfWidth || landZone.m_X > g_SceneMan.GetSceneWidth() - halfWidth)) { + // Wrap shit around and draw dupe on the other side + int wrappedX = landZone.m_X + (landZone.m_X < halfWidth ? g_SceneMan.GetSceneWidth() : -g_SceneMan.GetSceneWidth()); + // Cursor + draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX - halfWidth, landZone.m_Y - 48); + draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); + // Text + pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 42, m_AIReturnCraft[player] ? "Deliver here" : "Travel here", GUIFont::Centre); + pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 36, "and then", GUIFont::Centre); + pLargeFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 30, m_AIReturnCraft[player] ? "RETURN" : "STAY", GUIFont::Centre); + } + } + } + // Iterate through all teams, drawing all pending delivery cursors + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; + char str[64]; + for (std::deque::iterator itr = m_Deliveries[team].begin(); itr != m_Deliveries[team].end(); ++itr) { + int halfWidth = 24; + landZone = itr->landingZone - targetPos; + // Cursor + draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X - halfWidth, landZone.m_Y - 48); + draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], landZone.m_X + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); + // Text + pSmallFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 38, "ETA:", GUIFont::Centre); + if (m_ActivityState == ActivityState::PreGame) + std::snprintf(str, sizeof(str), "???s"); + else + std::snprintf(str, sizeof(str), "%is", ((int)itr->delay - (int)itr->timer.GetElapsedSimTimeMS()) / 1000); + pLargeFont->DrawAligned(&pBitmapInt, landZone.m_X, landZone.m_Y - 32, str, GUIFont::Centre); + // Draw wrap around the world if necessary, and only if this is being drawn directly to a scenewide target bitmap + if (targetPos.IsZero() && (landZone.m_X < halfWidth || landZone.m_X > g_SceneMan.GetSceneWidth() - halfWidth)) { + // Wrap shit around and draw dupe on the other side + int wrappedX = landZone.m_X + (landZone.m_X < halfWidth ? g_SceneMan.GetSceneWidth() : -g_SceneMan.GetSceneWidth()); + // Cursor + draw_sprite(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX - halfWidth, landZone.m_Y - 48); + draw_sprite_h_flip(pTargetBitmap, m_aLZCursor[cursor][frame], wrappedX + halfWidth - m_aLZCursor[cursor][frame]->w, landZone.m_Y - 48); + // Text + pSmallFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 38, "ETA:", GUIFont::Centre); + pLargeFont->DrawAligned(&pBitmapInt, wrappedX, landZone.m_Y - 32, str, GUIFont::Centre); + } + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OneOrNoneTeamsLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether there is one and only one team left this game with -// a brain in its ranks. - -bool GameActivity::OneOrNoneTeamsLeft() -{ - // See if only one team remains with any brains - int brainTeamCount = 0; - int brainTeam = Teams::NoTeam; - for (int t = Teams::TeamOne; t < Teams::MaxTeamCount; ++t) - { - if (!m_TeamActive[t]) - continue; - if (g_MovableMan.GetFirstBrainActor(t)) - { - brainTeamCount++; - brainTeam = t; - } - } - - // If less than two teams left with any brains, they get indicated - // Also, if NO teams with brain are left, that is indicated with NoTeam - if (brainTeamCount <= 1) - return true; - - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActiveCPUTeamCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns active CPU team count. + // Arguments: None. + // Return value: Returns active CPU team count. + int GameActivity::GetActiveCPUTeamCount() const { + int count = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WhichTeamLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates which single team is left, if any. -// Arguments: None. - -int GameActivity::WhichTeamLeft() -{ - int whichTeam = Teams::NoTeam; - - // See if only one team remains with any brains - int brainTeamCount = 0; - int brainTeam = Teams::NoTeam; - for (int t = Teams::TeamOne; t < Teams::MaxTeamCount; ++t) - { - if (!m_TeamActive[t]) - continue; - if (g_MovableMan.GetFirstBrainActor(t)) - { - brainTeamCount++; - brainTeam = t; - } - } - - // If exactly one team with brains, return that - if (brainTeamCount == 1) - return brainTeam; - - return Teams::NoTeam; -} + for (int team = Teams::TeamOne; team < Activity::MaxTeamCount; team++) + if (TeamActive(team) && TeamIsCPU(team)) + count++; + return count; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: NoTeamLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether there are NO teams left with any brains at all! + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActiveHumanTeamCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns active human team count. + // Arguments: None. + // Return value: Returns active human team count. -bool GameActivity::NoTeamLeft() -{ - for (int t = Teams::TeamOne; t < Teams::MaxTeamCount; ++t) - { - if (!m_TeamActive[t]) - continue; - if (g_MovableMan.GetFirstBrainActor(t)) - return false; - } - return true; -} + int GameActivity::GetActiveHumanTeamCount() const { + int count = 0; + for (int team = Teams::TeamOne; team < Activity::MaxTeamCount; team++) + if (TeamActive(team) && !TeamIsCPU(team)) + count++; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: InitAIs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through all Actor:s currently in the MovableMan and gives each -// one not controlled by a Controller a CAI and appropriate AIMode setting -// based on team and CPU team. - -void GameActivity::InitAIs() -{ - Actor *pActor = 0; - Actor *pFirstActor = 0; - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team]) - continue; - // Get the first one - pFirstActor = pActor = g_MovableMan.GetNextTeamActor(team); - - do - { - // Set up AI controller if currently not player controlled - if (pActor && !pActor->GetController()->IsPlayerControlled()) - { - pActor->SetControllerMode(Controller::CIM_AI); - - // If human, set appropriate AI mode -// TODO: IMPROVE - if (dynamic_cast(pActor) || dynamic_cast(pActor)) - { - // Hunt if of CPU team, sentry default if of player team - if (team == m_CPUTeam) - pActor->SetAIMode(AHuman::AIMODE_BRAINHUNT); - else - pActor->SetAIMode(AHuman::AIMODE_SENTRY); - } - } - - // Next! - pActor = g_MovableMan.GetNextTeamActor(team, pActor); - } - while (pActor && pActor != pFirstActor); - } -} + return count; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OtherTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the next other team number from the one passed in, if any. If there + // are more than two teams in this game, then the next one in the series + // will be returned here. + + int GameActivity::OtherTeam(int team) { + // Only one team in this game, so can't return another one + if (m_TeamCount == 1) + return Teams::NoTeam; + + // Find another team that is active + bool loopedOnce = false; + for (int t = team + 1;; t++) { + // Loop + if (t >= MaxTeamCount) + t = Teams::TeamOne; + + if (t == team) + break; + + if (m_TeamActive[t]) + return t; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DisableAIs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through all Actor:s currently in the MovableMan and gives each -// one not controlled by a Controller a CAI and appropriate AIMode setting -// based on team and CPU team. - -void GameActivity::DisableAIs(bool disable, int whichTeam) -{ - Actor *pActor = 0; - Actor *pFirstActor = 0; - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { - if (!m_TeamActive[team] || (whichTeam != Teams::NoTeam && team != whichTeam)) - continue; - // Get the first one - pFirstActor = pActor = g_MovableMan.GetNextTeamActor(team); - - do - { - // Disable the AI controllers - if (pActor && pActor->GetController()->GetInputMode() == Controller::CIM_AI) - pActor->GetController()->SetDisabled(disable); - - // Next! - pActor = g_MovableMan.GetNextTeamActor(team, pActor); - } - while (pActor && pActor != pFirstActor); - } -} + return Teams::NoTeam; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OneOrNoneTeamsLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether there is one and only one team left this game with + // a brain in its ranks. + + bool GameActivity::OneOrNoneTeamsLeft() { + // See if only one team remains with any brains + int brainTeamCount = 0; + int brainTeam = Teams::NoTeam; + for (int t = Teams::TeamOne; t < Teams::MaxTeamCount; ++t) { + if (!m_TeamActive[t]) + continue; + if (g_MovableMan.GetFirstBrainActor(t)) { + brainTeamCount++; + brainTeam = t; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Simply draws this' arrow relative to a point on a bitmap. - -void GameActivity::ObjectivePoint::Draw(BITMAP *pTargetBitmap, BITMAP *pArrowBitmap, const Vector &arrowPoint, ObjectiveArrowDir arrowDir) -{ - if (!pTargetBitmap || !pArrowBitmap) - return; - - AllegroBitmap allegroBitmap(pTargetBitmap); - int x = arrowPoint.GetFloorIntX(); - int y = arrowPoint.GetFloorIntY(); - int halfWidth = pArrowBitmap->w / 2; - int halfHeight = pArrowBitmap->h / 2; - int textSpace = 4; - - // Constrain the point within a gutter of the whole screen so the arrow is never right up agains teh edge of the screen. -/* - Box constrainBox(halfWidth, pArrowBitmap->h, pTargetBitmap->w - halfWidth, pTargetBitmap->h - pArrowBitmap->h); - x = constrainBox.GetWithinBoxX(x); - y = constrainBox.GetWithinBoxY(y); -*/ - if (x < halfWidth || x > pTargetBitmap->w - halfWidth) - { - if (x < halfWidth) - x = halfWidth; - if (x > pTargetBitmap->w - halfWidth) - x = pTargetBitmap->w - halfWidth - 1.0; - - if (y > pTargetBitmap->h - pArrowBitmap->h) - y = pTargetBitmap->h - pArrowBitmap->h; - else if (y < pArrowBitmap->h) - y = pArrowBitmap->h; - } - else if (y < halfHeight || y > pTargetBitmap->h - halfHeight) - { - if (y < halfHeight) - y = halfHeight; - if (y > pTargetBitmap->h - halfHeight) - y = pTargetBitmap->h - halfHeight - 1.0; - - if (x > pTargetBitmap->w - pArrowBitmap->w) - x = pTargetBitmap->w - pArrowBitmap->w; - else if (x < pArrowBitmap->w) - x = pArrowBitmap->w; - } - - // Draw the arrow and text descritpion of the Object so the point of the arrow ends up on the arrowPoint - if (arrowDir == ARROWDOWN) - { - masked_blit(pArrowBitmap, pTargetBitmap, 0, 0, x - halfWidth, y - pArrowBitmap->h, pArrowBitmap->w, pArrowBitmap->h); - g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, x, y - pArrowBitmap->h - textSpace, m_Description, GUIFont::Centre, GUIFont::Bottom); - } - else if (arrowDir == ARROWLEFT) - { - rotate_sprite(pTargetBitmap, pArrowBitmap, x, y - halfHeight, itofix(64)); - g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, x + pArrowBitmap->w + textSpace, y, m_Description, GUIFont::Left, GUIFont::Middle); - } - else if (arrowDir == ARROWRIGHT) - { - rotate_sprite(pTargetBitmap, pArrowBitmap, x - pArrowBitmap->w, y - halfHeight, itofix(-64)); - g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, x - pArrowBitmap->w - textSpace, y, m_Description, GUIFont::Right, GUIFont::Middle); - } - else if (arrowDir == ARROWUP) - { - rotate_sprite(pTargetBitmap, pArrowBitmap, x - halfWidth, y, itofix(-128)); - g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, x, y + pArrowBitmap->h + textSpace, m_Description, GUIFont::Centre, GUIFont::Top); - } -} - -std::string & GameActivity::GetNetworkPlayerName(int player) -{ - if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) - return m_NetworkPlayerNames[player]; - else - return m_NetworkPlayerNames[0]; -} - -void GameActivity::SetNetworkPlayerName(int player, std::string name) -{ - if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) - m_NetworkPlayerNames[player] = name; -} + // If less than two teams left with any brains, they get indicated + // Also, if NO teams with brain are left, that is indicated with NoTeam + if (brainTeamCount <= 1) + return true; + + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WhichTeamLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates which single team is left, if any. + // Arguments: None. + + int GameActivity::WhichTeamLeft() { + int whichTeam = Teams::NoTeam; + + // See if only one team remains with any brains + int brainTeamCount = 0; + int brainTeam = Teams::NoTeam; + for (int t = Teams::TeamOne; t < Teams::MaxTeamCount; ++t) { + if (!m_TeamActive[t]) + continue; + if (g_MovableMan.GetFirstBrainActor(t)) { + brainTeamCount++; + brainTeam = t; + } + } + + // If exactly one team with brains, return that + if (brainTeamCount == 1) + return brainTeam; + + return Teams::NoTeam; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: NoTeamLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether there are NO teams left with any brains at all! + + bool GameActivity::NoTeamLeft() { + for (int t = Teams::TeamOne; t < Teams::MaxTeamCount; ++t) { + if (!m_TeamActive[t]) + continue; + if (g_MovableMan.GetFirstBrainActor(t)) + return false; + } + return true; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: InitAIs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through all Actor:s currently in the MovableMan and gives each + // one not controlled by a Controller a CAI and appropriate AIMode setting + // based on team and CPU team. + + void GameActivity::InitAIs() { + Actor* pActor = 0; + Actor* pFirstActor = 0; + + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team]) + continue; + // Get the first one + pFirstActor = pActor = g_MovableMan.GetNextTeamActor(team); + + do { + // Set up AI controller if currently not player controlled + if (pActor && !pActor->GetController()->IsPlayerControlled()) { + pActor->SetControllerMode(Controller::CIM_AI); + + // If human, set appropriate AI mode + // TODO: IMPROVE + if (dynamic_cast(pActor) || dynamic_cast(pActor)) { + // Hunt if of CPU team, sentry default if of player team + if (team == m_CPUTeam) + pActor->SetAIMode(AHuman::AIMODE_BRAINHUNT); + else + pActor->SetAIMode(AHuman::AIMODE_SENTRY); + } + } + + // Next! + pActor = g_MovableMan.GetNextTeamActor(team, pActor); + } while (pActor && pActor != pFirstActor); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DisableAIs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through all Actor:s currently in the MovableMan and gives each + // one not controlled by a Controller a CAI and appropriate AIMode setting + // based on team and CPU team. + + void GameActivity::DisableAIs(bool disable, int whichTeam) { + Actor* pActor = 0; + Actor* pFirstActor = 0; + + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { + if (!m_TeamActive[team] || (whichTeam != Teams::NoTeam && team != whichTeam)) + continue; + // Get the first one + pFirstActor = pActor = g_MovableMan.GetNextTeamActor(team); + + do { + // Disable the AI controllers + if (pActor && pActor->GetController()->GetInputMode() == Controller::CIM_AI) + pActor->GetController()->SetDisabled(disable); + + // Next! + pActor = g_MovableMan.GetNextTeamActor(team, pActor); + } while (pActor && pActor != pFirstActor); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Simply draws this' arrow relative to a point on a bitmap. + + void GameActivity::ObjectivePoint::Draw(BITMAP* pTargetBitmap, BITMAP* pArrowBitmap, const Vector& arrowPoint, ObjectiveArrowDir arrowDir) { + if (!pTargetBitmap || !pArrowBitmap) + return; + + AllegroBitmap allegroBitmap(pTargetBitmap); + int x = arrowPoint.GetFloorIntX(); + int y = arrowPoint.GetFloorIntY(); + int halfWidth = pArrowBitmap->w / 2; + int halfHeight = pArrowBitmap->h / 2; + int textSpace = 4; + + // Constrain the point within a gutter of the whole screen so the arrow is never right up agains teh edge of the screen. + /* + Box constrainBox(halfWidth, pArrowBitmap->h, pTargetBitmap->w - halfWidth, pTargetBitmap->h - pArrowBitmap->h); + x = constrainBox.GetWithinBoxX(x); + y = constrainBox.GetWithinBoxY(y); + */ + if (x < halfWidth || x > pTargetBitmap->w - halfWidth) { + if (x < halfWidth) + x = halfWidth; + if (x > pTargetBitmap->w - halfWidth) + x = pTargetBitmap->w - halfWidth - 1.0; + + if (y > pTargetBitmap->h - pArrowBitmap->h) + y = pTargetBitmap->h - pArrowBitmap->h; + else if (y < pArrowBitmap->h) + y = pArrowBitmap->h; + } else if (y < halfHeight || y > pTargetBitmap->h - halfHeight) { + if (y < halfHeight) + y = halfHeight; + if (y > pTargetBitmap->h - halfHeight) + y = pTargetBitmap->h - halfHeight - 1.0; + + if (x > pTargetBitmap->w - pArrowBitmap->w) + x = pTargetBitmap->w - pArrowBitmap->w; + else if (x < pArrowBitmap->w) + x = pArrowBitmap->w; + } + + // Draw the arrow and text descritpion of the Object so the point of the arrow ends up on the arrowPoint + if (arrowDir == ARROWDOWN) { + masked_blit(pArrowBitmap, pTargetBitmap, 0, 0, x - halfWidth, y - pArrowBitmap->h, pArrowBitmap->w, pArrowBitmap->h); + g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, x, y - pArrowBitmap->h - textSpace, m_Description, GUIFont::Centre, GUIFont::Bottom); + } else if (arrowDir == ARROWLEFT) { + rotate_sprite(pTargetBitmap, pArrowBitmap, x, y - halfHeight, itofix(64)); + g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, x + pArrowBitmap->w + textSpace, y, m_Description, GUIFont::Left, GUIFont::Middle); + } else if (arrowDir == ARROWRIGHT) { + rotate_sprite(pTargetBitmap, pArrowBitmap, x - pArrowBitmap->w, y - halfHeight, itofix(-64)); + g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, x - pArrowBitmap->w - textSpace, y, m_Description, GUIFont::Right, GUIFont::Middle); + } else if (arrowDir == ARROWUP) { + rotate_sprite(pTargetBitmap, pArrowBitmap, x - halfWidth, y, itofix(-128)); + g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, x, y + pArrowBitmap->h + textSpace, m_Description, GUIFont::Centre, GUIFont::Top); + } + } + + std::string& GameActivity::GetNetworkPlayerName(int player) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) + return m_NetworkPlayerNames[player]; + else + return m_NetworkPlayerNames[0]; + } + + void GameActivity::SetNetworkPlayerName(int player, std::string name) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) + m_NetworkPlayerNames[player] = name; + } } // namespace RTE diff --git a/Source/Activities/GameActivity.h b/Source/Activities/GameActivity.h index a60b9595cf..0165b71764 100644 --- a/Source/Activities/GameActivity.h +++ b/Source/Activities/GameActivity.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,1120 +19,1066 @@ #include "Scene.h" #include "Actor.h" -namespace RTE -{ +namespace RTE { #define OBJARROWFRAMECOUNT 4 #define LZCURSORFRAMECOUNT 4 -class Actor; -class ACraft; -class PieMenu; -class InventoryMenuGUI; -class BuyMenuGUI; -class SceneEditorGUI; -class GUIBanner; -class Loadout; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: GameActivity -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Base class for all GameActivity:s, including game modes and editors. -// Parent(s): Activity. -// Class history: 8/7/2007 GameActivity created. - -class GameActivity : public Activity { - - friend struct ActivityLuaBindings; - - // Keeps track of everything about a delivery in transit after purchase has been made with the menu - struct Delivery - { - // OWNED by this until the delivery is made! - ACraft *pCraft; - // Which player ordered this delivery - int orderedByPlayer; - // Where to land - Vector landingZone; - // How much this delivery was offset upwards for multi-ordering, stored to help with delivery icons. If 0, this was presumably not a multi-order. - float multiOrderYOffset; - // How long left until entry, in ms - long delay; - // Times how long we've been in transit - Timer timer; - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - SerializableOverrideMethods; - ClassInfoGetters; - - enum ObjectiveArrowDir - { - ARROWDOWN = 0, - ARROWLEFT, - ARROWRIGHT, - ARROWUP - }; - - enum BannerColor - { - RED = 0, - YELLOW - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GameActivity -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GameActivity object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - GameActivity() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~GameActivity -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a GameActivity object before deletion -// from system memory. -// Arguments: None. - - ~GameActivity() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GameActivity object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a GameActivity to be identical to another, by deep copy. -// Arguments: A reference to the GameActivity to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const GameActivity &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire GameActivity, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Activity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GameActivity object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCPUTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current CPU-assisted team, if any (NoTeam) - LEGACY function -// Arguments: None. -// Return value: The current setting. NoTeam is no team is assisted. - - int GetCPUTeam() const { return m_CPUTeam; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCPUTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current CPU-assisted team, if any (NoTeam) - LEGACY function -// Arguments: The new setting. NoTeam is no team is assisted. -// Return value: None. - - void SetCPUTeam(int team = Activity::NoTeam); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetObservationTarget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the observation sceneman scroll targets, for when the game is -// over or a player is in observation mode -// Arguments: The new absolute position to observe. -// Which player to set it for. -// Return value: None. - - void SetObservationTarget(const Vector &newTarget, int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_ObservationTarget[player] = newTarget; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDeathViewTarget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the player death sceneman scroll targets, for when a player- -// controlled actor dies and the view should go to his last position -// Arguments: The new absolute position to set as death view. -// Which player to set it for. -// Return value: None. - - void SetDeathViewTarget(const Vector &newTarget, int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_DeathViewTarget[player] = newTarget; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLandingZone -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the he last selected landing zone. -// Arguments: The new absolute position to set as the last selected landing zone. -// Which player to set it for. -// Return value: None. - - void SetLandingZone(const Vector &newZone, int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_LandingZone[player] = newZone; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLandingZone -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the he last selected landing zone. -// Arguments: Which player to get it for. -// Return value: The new absolute position to set as the last selected landing zone. - - Vector GetLandingZone(int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) return m_LandingZone[player]; else return Vector(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetActorSelectCursor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the actor selection cursor position. -// Arguments: The new absolute position to put the cursor at. -// Which player to set it for. -// Return value: None. - - void SetActorSelectCursor(const Vector &newPos, int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_ActorCursor[player] = newPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBuyGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the an in-game GUI Object for a specific player. -// Arguments: Which player to get the GUI for. -// Return value: A pointer to a BuyMenuGUI. Ownership is NOT transferred! - - BuyMenuGUI * GetBuyGUI(unsigned int which = 0) const { return m_pBuyGUI[which]; } - - /// - /// Checks if the in-game GUI Object is visible for a specific player. - /// - /// Which player to check the GUI for. -1 will check all players. - /// Whether or not the BuyMenuGUI is visible for input player(s). - bool IsBuyGUIVisible(int which = 0) const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the an in-game editor GUI Object for a specific player. -// Arguments: Which player to get the GUI for. -// Return value: A pointer to a SceneEditorGUI. Ownership is NOT transferred! - - SceneEditorGUI * GetEditorGUI(unsigned int which = 0) const { return m_pEditorGUI[which]; } - - /// - /// Locks a player controlled actor to a specific controller mode. - /// Locking the actor will disable player input, including switching actors. - /// - /// Which player to lock the actor for. - /// Whether to lock or unlock the actor. (Default: true) - /// Which controller mode to lock the actor to. (Default: `CIM_AI`) - /// Whether the (un)lock was performed. - bool LockControlledActor(Players player, bool lock = true, Controller::InputMode lockToMode = Controller::InputMode::CIM_AI); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SwitchToActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the this to focus player control to a specific Actor for a -// specific team. OWNERSHIP IS NOT TRANSFERRED! -// Arguments: Which Actor to switch focus to. The team of this Actor will be set -// once it is passed in. Ownership IS NOT TRANSFERRED! The Actor should -// be added to MovableMan already. -// Return value: Whether the focus switch was successful or not. - - bool SwitchToActor(Actor *pActor, int player = 0, int team = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SwitchToNextActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the this to focus player control to the next Actor of a -// specific team, other than the current one focused on. -// Arguments: Which team to switch to next actor on. -// An actor pointer to skip in the sequence. -// Return value: None. - - void SwitchToNextActor(int player, int team, Actor *pSkip = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SwitchToPrevActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces this to focus player control to the previous Actor of a -// specific team, other than the current one focused on. -// Arguments: Which team to switch to next actor on. -// An actor pointer to skip in the sequence. -// Return value: None. - - void SwitchToPrevActor(int player, int team, Actor *pSkip = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetWinnerTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team is the winner, when the game is over. -// Arguments: The team number of the winning team. 0 is team #1. Negative number -// means the game isn't over yet. -// Return value: None. - - void SetWinnerTeam(int winnerTeam) { m_WinnerTeam = winnerTeam; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetWinnerTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates which team is the winner, when the game is over. -// Arguments: None. -// Return value: The team number of the winning team. 0 is team #1. Negative number -// means the game isn't over yet. - - int GetWinnerTeam() const { return m_WinnerTeam; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBanner -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets access to the huge banner of any player that can display -// messages which can not be missed or ignored. -// Arguments: Which color banner to get - see the GameActivity::BannerColor enum. -// Which player's banner to get. -// Return value: A pointer to the GUIBanner object that we can - - GUIBanner * GetBanner(int whichColor = YELLOW, int player = Players::PlayerOne) { return whichColor == YELLOW ? m_pBannerYellow[player] : m_pBannerRed[player]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLZArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the Area within which a team can land things. -// Arguments: The number of the team we're setting for. -// The Area we're setting to limit their landings within. -// Return value: None. - - void SetLZArea(int team, const Scene::Area &newArea) { m_LandingZoneArea[team].Reset(); m_LandingZoneArea[team].Create(newArea); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLZArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the Area within which a team can land things. OWNERSHIP IS NOT TRANSFERRED! -// Arguments: The number of the team we're setting for. -// Return value: The Area we're using to limit their landings within. OWNERSHIP IS NOT TRANSFERRED! - - const Scene::Area & GetLZArea(int team) const { return m_LandingZoneArea[team]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBrainLZWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the width of the landing zone box that follows around a player's -// brain. -// Arguments: The number of the in-game player we're setting for. -// The width of the box, in pixels. 0 means disabled. -// Return value: None. - - void SetBrainLZWidth(int player, int width) { m_BrainLZWidth[player] = width; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBrainLZWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the width of the landing zone box that follows around a player's -// brain. -// Arguments: The number of the player we're getting for. -// Return value: The width in pixels of the landing zone. - - int GetBrainLZWidth(int player) const { return m_BrainLZWidth[player]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddObjectivePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Created an objective point for one of the teams to show until cleared. -// Arguments: The team number of the team to give objective. 0 is team #1. -// The very short description of what the objective is (three short words max) -// The absolute scene coordiante position of the objective. -// The desired direction of the arrow when the point is on screen. -// Return value: None. - - void AddObjectivePoint(std::string description, Vector objPos, int whichTeam = Teams::TeamOne, ObjectiveArrowDir arrowDir = ARROWDOWN); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: YSortObjectivePoints -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sorts all objective points according to their positions on the Y axis. -// Arguments: None. -// Return value: None. - - void YSortObjectivePoints(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearObjectivePoints -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all objective points previously added, for both teams. -// Arguments: None. -// Return value: None. - - void ClearObjectivePoints() { m_Objectives.clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddOverridePurchase -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds somehting to the purchase list that will override what is set -// in the buy guy next time CreateDelivery is called. -// Arguments: The SceneObject preset to add to the override purchase list. OWNERSHIP IS NOT TRANSFERRED! -// Which player's list to add an override purchase item to. -// Return value: The new total value of what's in the override purchase list. - - int AddOverridePurchase(const SceneObject *pPurchase, int player); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOverridePurchaseList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: First clears and then adds all the stuff in a Loadout to the override -// purchase list. -// Arguments: The Loadout preset to set the override purchase list to reflect. OWNERSHIP IS NOT TRANSFERRED! -// The player we're talking about. -// Return value: The new total value of what's in the override purchase list. - - int SetOverridePurchaseList(const Loadout *pLoadout, int player); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOverridePurchaseList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: First clears and then adds all the stuff in a Loadout to the override -// purchase list. -// Arguments: The name of the Loadout preset to set the override purchase list to -// represent. -// Return value: The new total value of what's in the override purchase list. - - int SetOverridePurchaseList(std::string loadoutName, int player); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearOverridePurchase -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all items from a specific player's override purchase list. -// Arguments: Which player's override purchase list to clear. -// Return value: None. - - void ClearOverridePurchase(int player) { m_PurchaseOverride[player].clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateDelivery -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes the current order out of a player's buy GUI, creates a Delivery -// based off it, and stuffs it into that player's delivery queue. -// Arguments: Which player to create the delivery for. Cargo AI mode and waypoint. -// Return value: Success or not. - - bool CreateDelivery(int player, int mode, Vector &waypoint) { return CreateDelivery(player, mode, waypoint, NULL); }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateDelivery -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes the current order out of a player's buy GUI, creates a Delivery -// based off it, and stuffs it into that player's delivery queue. -// Arguments: Which player to create the delivery for. Cargo AI mode and TargetMO. -// Return value: Success or not. - - bool CreateDelivery(int player, int mode, Actor *pTargetMO) { Vector point( -1, -1 ); return CreateDelivery(player, mode, point, pTargetMO); }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateDelivery -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes the current order out of a player's buy GUI, creates a Delivery -// based off it, and stuffs it into that player's delivery queue. -// Arguments: Which player to create the delivery for and Cargo AI mode. -// Return value: Success or not. - - bool CreateDelivery(int player, int mode) { Vector point( -1, -1 ); return CreateDelivery(player, mode, point, NULL); }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateDelivery -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes the current order out of a player's buy GUI, creates a Delivery -// based off it, and stuffs it into that player's delivery queue. -// Arguments: Which player to create the delivery for. -// Return value: Success or not. - - bool CreateDelivery(int player) { Vector point( -1, -1 ); return CreateDelivery(player, Actor::AIMODE_SENTRY, point, NULL); }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDeliveryCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows how many deliveries this team has pending. -// Arguments: Which team to check the delivery count for. -// Return value: The number of deliveries this team has coming. - - int GetDeliveryCount(int team) { return m_Deliveries[team].size(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetupPlayers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Precalculates the player-to-screen index map, counts the number of -// active players etc. -// Arguments: None. -// Return value: None. - - void SetupPlayers() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateEditing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: This is a special update step for when any player is still editing the -// scene. -// Arguments: None. -// Return value: None. - - void UpdateEditing(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTeamTech -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the name of the tech module selected for this team during scenario setup -// Arguments: Team to return tech module for -// Return value: Tech module name, for example Dummy.rte, or empty string if there is no team - std::string GetTeamTech(int team) const { return (team >= Teams::TeamOne && team < Teams::MaxTeamCount) ? m_TeamTech[team] : ""; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetTeamTech -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets tech module name for specified team. Module must set must be loaded. -// Arguments: Team to set module, module name, for example Dummy.rte -// Return value: None - void SetTeamTech(int team, std::string tech); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TeamIsCPU -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether a specific team is assigned a CPU player in the current game. -// Arguments: Which team index to check. -// Return value: Whether the team is assigned a CPU player in the current activity. - bool TeamIsCPU(int team) const { return (team >= Teams::TeamOne && team < Teams::MaxTeamCount) ? m_TeamIsCPU[team] : false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActiveCPUTeamCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns active CPU team count. -// Arguments: None. -// Return value: Returns active CPU team count. - int GetActiveCPUTeamCount() const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActiveHumanTeamCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns active human team count. -// Arguments: None. -// Return value: Returns active human team count. - int GetActiveHumanTeamCount() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetStartingGold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes how much starting gold was selected in scenario setup dialog. 20000 - infinite amount. -// Arguments: Starting gold amount -// Return value: None. - void SetStartingGold(int amount) { m_StartingGold = amount; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetStartingGold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns how much starting gold was selected in scenario setup dialog. 20000 - infinite amount. -// Arguments: None. -// Return value: How much starting gold must be given to human players. - int GetStartingGold() { return m_StartingGold; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFogOfWarEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes whether fog of war must be enabled for this activity or not. -// Never hides or reveals anything, just changes internal flag. -// Arguments: New fog of war state. true = enabled. -// Return value: None. - void SetFogOfWarEnabled(bool enable) { m_FogOfWarEnabled = enable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFogOfWareEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether fog of war must be enabled for this activity or not. -// Call it to determine whether you should call MakeAllUnseen or not at the start of activity. -// Arguments: None. -// Return value: Whether Fog of war flag was checked during scenario setup dialog. - bool GetFogOfWarEnabled() { return m_FogOfWarEnabled; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRequireClearPathToOrbit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether player activity requires a cleat path to orbit to place brain -// -// Arguments: None. -// Return value: Whether we need a clear path to orbit to place brains. - bool GetRequireClearPathToOrbit() const { return m_RequireClearPathToOrbit; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetRequireClearPathToOrbit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether player activity requires a cleat path to orbit to place brain -// -// Arguments: Whether we need a clear path to orbit to place brains. -// Return value: None. - void SetRequireClearPathToOrbit(bool newvalue) { m_RequireClearPathToOrbit = newvalue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - int GetDefaultFogOfWar() const { return m_DefaultFogOfWar; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - int GetDefaultRequireClearPathToOrbit() const { return m_DefaultRequireClearPathToOrbit; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - int GetDefaultDeployUnits() const { return m_DefaultDeployUnits; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - int GetDefaultGoldCakeDifficulty() const { return m_DefaultGoldCakeDifficulty; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - int GetDefaultGoldEasyDifficulty() const { return m_DefaultGoldEasyDifficulty; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - int GetDefaultGoldMediumDifficulty() const { return m_DefaultGoldMediumDifficulty; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - int GetDefaultGoldHardDifficulty() const { return m_DefaultGoldHardDifficulty; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - int GetDefaultGoldNutsDifficulty() const { return m_DefaultGoldNutsDifficulty; } - - - /// - /// Gets the default gold for max difficulty. - /// - /// The default gold for max difficulty. - int GetDefaultGoldMaxDifficulty() const { return m_DefaultGoldMaxDifficulty; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - bool GetFogOfWarSwitchEnabled() const { return m_FogOfWarSwitchEnabled; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - bool GetDeployUnitsSwitchEnabled() const { return m_DeployUnitsSwitchEnabled; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - - bool GetGoldSwitchEnabled() const { return m_GoldSwitchEnabled; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Arguments: None. -// Return value: - - bool GetRequireClearPathToOrbitSwitchEnabled() const { return m_RequireClearPathToOrbitSwitchEnabled; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCrabToHumanSpawnRatio -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns CrabToHumanSpawnRatio for specified module -// Arguments: None. -// Return value: Crab-To-Human spawn ratio value set for specified module, 0.25 is default. - - float GetCrabToHumanSpawnRatio(int moduleid); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDeliveryDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns current delivery delay -// Arguments: None. -// Return value: Returns current delivery delay - - long GetDeliveryDelay() const { return m_DeliveryDelay; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDeliveryDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets delivery delay -// Arguments: New delivery delay value in ms -// Return value: None - - void SetDeliveryDelay(long newDeliveryDelay) { m_DeliveryDelay = newDeliveryDelay > 1 ? newDeliveryDelay : 1; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBuyMenuEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether buy menu is enabled in this activity. -// Arguments: True if buy menu enabled false otherwise -// Return value: None. - - bool GetBuyMenuEnabled() const { return m_BuyMenuEnabled; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBuyMenuEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether buy menu is enabled in this activity -// Arguments: True to enable buy menu, false otherwise -// Return value: None. - - void SetBuyMenuEnabled(bool newValue) { m_BuyMenuEnabled = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNetworkPlayerName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns network player name -// Arguments: Player -// Return value: Network player name - - std::string & GetNetworkPlayerName(int player); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetNetworkPlayerName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets network player name -// Arguments: Player number, player name -// Return value: None - - void SetNetworkPlayerName(int player, std::string name); - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateDelivery - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes the current order out of a player's buy GUI, creates a Delivery - // based off it, and stuffs it into that player's delivery queue. - // Arguments: Which player to create the delivery for. Cargo AI mode waypoint or TargetMO. - // Return value: Success or not. - - bool CreateDelivery(int player, int mode, Vector &waypoint, Actor *pTargetMO); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Struct: ObjectivePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A struct to keep all data about a mission objective. - // Parent(s): None. - // Class history: 11/17/2008 ObjectivePoint - - struct ObjectivePoint - { - ObjectivePoint() { m_Description.clear(); m_ScenePos.Reset(); m_Team = Teams::NoTeam; m_ArrowDir = ARROWDOWN; } - ObjectivePoint(const std::string &desc, const Vector &pos, int team = -1, ObjectiveArrowDir arrowDir = ARROWDOWN) { m_Description = desc; m_ScenePos = pos; m_Team = (Teams)team; m_ArrowDir = arrowDir; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Simply draws this' arrow relative to a point on a bitmap. - // Arguments: A pointer to the BITMAP to draw on. - // The arrow bitmap to draw, assuming it points downward. - // The absolute position on the bitmap to draw the point of the arrow at. - // Which orientation to draw the arrow in, relative to the point. - // Return value: None. - - void Draw(BITMAP *pTargetBitmap, BITMAP *pArrowBitmap, const Vector &arrowPoint, ObjectiveArrowDir arrowDir = ARROWDOWN); - - - // The description of this objective point - std::string m_Description; - // Absolute position in the scene where this is pointed - Vector m_ScenePos; - // The team this objective is relevant to - Teams m_Team; - // The positioning of the arrow that points at this objective - ObjectiveArrowDir m_ArrowDir; - }; - - - // Comparison functor for sorting objective points by their y pos using STL's sort - struct ObjPointYPosComparison { - bool operator()(ObjectivePoint &rhs, ObjectivePoint &lhs) { return rhs.m_ScenePos.m_Y < lhs.m_ScenePos.m_Y; } - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OtherTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next other team number from the one passed in, if any. If there -// are more than two teams in this game, then the next one in the series -// will be returned here. -// Arguments: The team not to get. -// Return value: The other team's number. - - int OtherTeam(int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OneOrNoneTeamsLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether there is less than two teams left in this game with -// a brain in its ranks. -// Arguments: None. -// Return value: Whether less than two teams have brains in them left. - - bool OneOrNoneTeamsLeft(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WhichTeamLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates which single team is left, if any. -// Arguments: None. -// Return value: Which team stands alone with any brains in its ranks, if any. NoTeam -// is returned if there's either more than one team, OR there are no -// teams at all left with brains in em. - - int WhichTeamLeft(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: NoTeamLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether there are NO teams left with any brains at all! -// Arguments: None. -// Return value: Whether any team has a brain in it at all. - - bool NoTeamLeft(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: InitAIs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through all Actor:s currently in the MovableMan and sets each -// one not controlled by a player to be AI controlled and AIMode setting -// based on team and CPU team. -// Arguments: None. -// Return value: None. - - virtual void InitAIs(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DisableAIs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through all Actor:s currently in the MovableMan and disables or -// enables each one with a Controller set to AI input. -// Arguments: Whether to disable or enable them; -// Which team to do this to. If all, then pass Teams::NoTeam -// Return value: None. - - void DisableAIs(bool disable = true, int whichTeam = Teams::NoTeam); - - - - - // Member variables - static Entity::ClassInfo m_sClass; - - // Which team is CPU-managed, if any (-1) - LEGACY, now controlled by Activity::m_IsHuman - int m_CPUTeam; - // Team is active or not this game - bool m_TeamIsCPU[Teams::MaxTeamCount]; - - // The observation sceneman scroll targets, for when the game is over or a player is in observation mode - Vector m_ObservationTarget[Players::MaxPlayerCount]; - // The player death sceneman scroll targets, for when a player-controlled actor dies and the view should go to his last position - Vector m_DeathViewTarget[Players::MaxPlayerCount]; - // Times the delay between regular actor swtich, and going into manual siwtch mode - Timer m_ActorSelectTimer[Players::MaxPlayerCount]; - // The cursor for selecting new Actors - Vector m_ActorCursor[Players::MaxPlayerCount]; - // Highlighted actor while cursor switching; will be switched to if switch button is released now - Actor *m_pLastMarkedActor[Players::MaxPlayerCount]; - // The last selected landing zone - Vector m_LandingZone[Players::MaxPlayerCount]; - // Whether the last craft was set to return or not after delivering - bool m_AIReturnCraft[Players::MaxPlayerCount]; - std::array, Players::MaxPlayerCount> m_StrategicModePieMenu; //!< The strategic mode PieMenus for each Player. - // The inventory menu gui for each player - InventoryMenuGUI *m_InventoryMenuGUI[Players::MaxPlayerCount]; - // The in-game buy GUIs for each player - BuyMenuGUI *m_pBuyGUI[Players::MaxPlayerCount]; - // The in-game scene editor GUI for each player - SceneEditorGUI *m_pEditorGUI[Players::MaxPlayerCount]; - bool m_LuaLockActor[Players::MaxPlayerCount]; //!< Whether or not to lock input for each player while lua has control. - Controller::InputMode m_LuaLockActorMode[Players::MaxPlayerCount]; //!< The input mode to lock to while lua has control. - // The in-game important message banners for each player - GUIBanner *m_pBannerRed[Players::MaxPlayerCount]; - GUIBanner *m_pBannerYellow[Players::MaxPlayerCount]; - // How many times a banner has been repeated.. so we dont' annoy by repeating forever - int m_BannerRepeats[Players::MaxPlayerCount]; - // Whether each player has marked himself as ready to start. Can still edit while this is set, but when all are set, the game starts - bool m_ReadyToStart[Players::MaxPlayerCount]; - // An override purchase list that can be set by a script and will be used instead of what's in the buy menu. Object held in here are NOT OWNED - // Once a delivery is made with anything in here, this list is automatically cleared out, and the next delivery will be what's set in the buy menu. - std::list m_PurchaseOverride[Players::MaxPlayerCount]; - - // The delivery queue which contains all the info about all the made orders currently in transit to delivery - std::deque m_Deliveries[Teams::MaxTeamCount]; - // The box within where landing zones can be put - Scene::Area m_LandingZoneArea[Teams::MaxTeamCount]; - // How wide around the brain the automatic LZ is following - int m_BrainLZWidth[Players::MaxPlayerCount]; - // The objective points for each team - std::list m_Objectives; - - // Tech of player - std::string m_TeamTech[Teams::MaxTeamCount]; - - // Initial gold amount selected by player in scenario setup dialog - int m_StartingGold; - // Whether fog of war was enabled or not in scenario setup dialog - bool m_FogOfWarEnabled; - // Whether we need a clear path to orbit to place brain - bool m_RequireClearPathToOrbit; - - // Default fog of war switch state for this activity, default -1 (unspecified) - int m_DefaultFogOfWar; - // Default clear path to orbit switch value, default -1 (unspecified) - int m_DefaultRequireClearPathToOrbit; - // Default deploy units swutch value, default -1 (unspecified) - int m_DefaultDeployUnits; - // Default gold amount for different difficulties, defalt -1 (unspecified) - int m_DefaultGoldCakeDifficulty; - int m_DefaultGoldEasyDifficulty; - int m_DefaultGoldMediumDifficulty; - int m_DefaultGoldHardDifficulty; - int m_DefaultGoldNutsDifficulty; - int m_DefaultGoldMaxDifficulty; - // Whether those switches are enabled or disabled in scenario setup dialog, true by default - bool m_FogOfWarSwitchEnabled; - bool m_DeployUnitsSwitchEnabled; - bool m_GoldSwitchEnabled; - bool m_RequireClearPathToOrbitSwitchEnabled; - bool m_BuyMenuEnabled; - - // The cursor animations for the LZ indicators - std::vector m_aLZCursor[4]; - std::array m_LZCursorWidth; //!< The width of each players' LZ cursor. - // The cursor animations for the objective indications - std::vector m_aObjCursor[4]; - - // Time it takes for a delivery to be made, in ms - long m_DeliveryDelay; - // Cursor animation timer - Timer m_CursorTimer; - // Total gameplay timer, not including editing phases - Timer m_GameTimer; - // Game end timer - Timer m_GameOverTimer; - // Time between game over and reset - long m_GameOverPeriod; - // The winning team number, when the game is over - int m_WinnerTeam; - - std::string m_NetworkPlayerNames[Players::MaxPlayerCount]; - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; + class Actor; + class ACraft; + class PieMenu; + class InventoryMenuGUI; + class BuyMenuGUI; + class SceneEditorGUI; + class GUIBanner; + class Loadout; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: GameActivity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Base class for all GameActivity:s, including game modes and editors. + // Parent(s): Activity. + // Class history: 8/7/2007 GameActivity created. + + class GameActivity : public Activity { + + friend struct ActivityLuaBindings; + + // Keeps track of everything about a delivery in transit after purchase has been made with the menu + struct Delivery { + // OWNED by this until the delivery is made! + ACraft* pCraft; + // Which player ordered this delivery + int orderedByPlayer; + // Where to land + Vector landingZone; + // How much this delivery was offset upwards for multi-ordering, stored to help with delivery icons. If 0, this was presumably not a multi-order. + float multiOrderYOffset; + // How long left until entry, in ms + long delay; + // Times how long we've been in transit + Timer timer; + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + SerializableOverrideMethods; + ClassInfoGetters; + + enum ObjectiveArrowDir { + ARROWDOWN = 0, + ARROWLEFT, + ARROWRIGHT, + ARROWUP + }; + + enum BannerColor { + RED = 0, + YELLOW + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GameActivity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GameActivity object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + GameActivity() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~GameActivity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a GameActivity object before deletion + // from system memory. + // Arguments: None. + + ~GameActivity() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GameActivity object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a GameActivity to be identical to another, by deep copy. + // Arguments: A reference to the GameActivity to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const GameActivity& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire GameActivity, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Activity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the GameActivity object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCPUTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current CPU-assisted team, if any (NoTeam) - LEGACY function + // Arguments: None. + // Return value: The current setting. NoTeam is no team is assisted. + + int GetCPUTeam() const { return m_CPUTeam; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCPUTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current CPU-assisted team, if any (NoTeam) - LEGACY function + // Arguments: The new setting. NoTeam is no team is assisted. + // Return value: None. + + void SetCPUTeam(int team = Activity::NoTeam); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetObservationTarget + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the observation sceneman scroll targets, for when the game is + // over or a player is in observation mode + // Arguments: The new absolute position to observe. + // Which player to set it for. + // Return value: None. + + void SetObservationTarget(const Vector& newTarget, int player = 0) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) + m_ObservationTarget[player] = newTarget; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDeathViewTarget + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the player death sceneman scroll targets, for when a player- + // controlled actor dies and the view should go to his last position + // Arguments: The new absolute position to set as death view. + // Which player to set it for. + // Return value: None. + + void SetDeathViewTarget(const Vector& newTarget, int player = 0) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) + m_DeathViewTarget[player] = newTarget; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLandingZone + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the he last selected landing zone. + // Arguments: The new absolute position to set as the last selected landing zone. + // Which player to set it for. + // Return value: None. + + void SetLandingZone(const Vector& newZone, int player = 0) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) + m_LandingZone[player] = newZone; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLandingZone + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the he last selected landing zone. + // Arguments: Which player to get it for. + // Return value: The new absolute position to set as the last selected landing zone. + + Vector GetLandingZone(int player = 0) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) + return m_LandingZone[player]; + else + return Vector(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetActorSelectCursor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the actor selection cursor position. + // Arguments: The new absolute position to put the cursor at. + // Which player to set it for. + // Return value: None. + + void SetActorSelectCursor(const Vector& newPos, int player = 0) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) + m_ActorCursor[player] = newPos; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBuyGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the an in-game GUI Object for a specific player. + // Arguments: Which player to get the GUI for. + // Return value: A pointer to a BuyMenuGUI. Ownership is NOT transferred! + + BuyMenuGUI* GetBuyGUI(unsigned int which = 0) const { return m_pBuyGUI[which]; } + + /// + /// Checks if the in-game GUI Object is visible for a specific player. + /// + /// Which player to check the GUI for. -1 will check all players. + /// Whether or not the BuyMenuGUI is visible for input player(s). + bool IsBuyGUIVisible(int which = 0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the an in-game editor GUI Object for a specific player. + // Arguments: Which player to get the GUI for. + // Return value: A pointer to a SceneEditorGUI. Ownership is NOT transferred! + + SceneEditorGUI* GetEditorGUI(unsigned int which = 0) const { return m_pEditorGUI[which]; } + + /// + /// Locks a player controlled actor to a specific controller mode. + /// Locking the actor will disable player input, including switching actors. + /// + /// Which player to lock the actor for. + /// Whether to lock or unlock the actor. (Default: true) + /// Which controller mode to lock the actor to. (Default: `CIM_AI`) + /// Whether the (un)lock was performed. + bool LockControlledActor(Players player, bool lock = true, Controller::InputMode lockToMode = Controller::InputMode::CIM_AI); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SwitchToActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the this to focus player control to a specific Actor for a + // specific team. OWNERSHIP IS NOT TRANSFERRED! + // Arguments: Which Actor to switch focus to. The team of this Actor will be set + // once it is passed in. Ownership IS NOT TRANSFERRED! The Actor should + // be added to MovableMan already. + // Return value: Whether the focus switch was successful or not. + + bool SwitchToActor(Actor* pActor, int player = 0, int team = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SwitchToNextActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the this to focus player control to the next Actor of a + // specific team, other than the current one focused on. + // Arguments: Which team to switch to next actor on. + // An actor pointer to skip in the sequence. + // Return value: None. + + void SwitchToNextActor(int player, int team, Actor* pSkip = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SwitchToPrevActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces this to focus player control to the previous Actor of a + // specific team, other than the current one focused on. + // Arguments: Which team to switch to next actor on. + // An actor pointer to skip in the sequence. + // Return value: None. + + void SwitchToPrevActor(int player, int team, Actor* pSkip = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetWinnerTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team is the winner, when the game is over. + // Arguments: The team number of the winning team. 0 is team #1. Negative number + // means the game isn't over yet. + // Return value: None. + + void SetWinnerTeam(int winnerTeam) { m_WinnerTeam = winnerTeam; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetWinnerTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates which team is the winner, when the game is over. + // Arguments: None. + // Return value: The team number of the winning team. 0 is team #1. Negative number + // means the game isn't over yet. + + int GetWinnerTeam() const { return m_WinnerTeam; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBanner + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets access to the huge banner of any player that can display + // messages which can not be missed or ignored. + // Arguments: Which color banner to get - see the GameActivity::BannerColor enum. + // Which player's banner to get. + // Return value: A pointer to the GUIBanner object that we can + + GUIBanner* GetBanner(int whichColor = YELLOW, int player = Players::PlayerOne) { return whichColor == YELLOW ? m_pBannerYellow[player] : m_pBannerRed[player]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLZArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the Area within which a team can land things. + // Arguments: The number of the team we're setting for. + // The Area we're setting to limit their landings within. + // Return value: None. + + void SetLZArea(int team, const Scene::Area& newArea) { + m_LandingZoneArea[team].Reset(); + m_LandingZoneArea[team].Create(newArea); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLZArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the Area within which a team can land things. OWNERSHIP IS NOT TRANSFERRED! + // Arguments: The number of the team we're setting for. + // Return value: The Area we're using to limit their landings within. OWNERSHIP IS NOT TRANSFERRED! + + const Scene::Area& GetLZArea(int team) const { return m_LandingZoneArea[team]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBrainLZWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the width of the landing zone box that follows around a player's + // brain. + // Arguments: The number of the in-game player we're setting for. + // The width of the box, in pixels. 0 means disabled. + // Return value: None. + + void SetBrainLZWidth(int player, int width) { m_BrainLZWidth[player] = width; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBrainLZWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the width of the landing zone box that follows around a player's + // brain. + // Arguments: The number of the player we're getting for. + // Return value: The width in pixels of the landing zone. + + int GetBrainLZWidth(int player) const { return m_BrainLZWidth[player]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddObjectivePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Created an objective point for one of the teams to show until cleared. + // Arguments: The team number of the team to give objective. 0 is team #1. + // The very short description of what the objective is (three short words max) + // The absolute scene coordiante position of the objective. + // The desired direction of the arrow when the point is on screen. + // Return value: None. + + void AddObjectivePoint(std::string description, Vector objPos, int whichTeam = Teams::TeamOne, ObjectiveArrowDir arrowDir = ARROWDOWN); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: YSortObjectivePoints + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sorts all objective points according to their positions on the Y axis. + // Arguments: None. + // Return value: None. + + void YSortObjectivePoints(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearObjectivePoints + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all objective points previously added, for both teams. + // Arguments: None. + // Return value: None. + + void ClearObjectivePoints() { m_Objectives.clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddOverridePurchase + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds somehting to the purchase list that will override what is set + // in the buy guy next time CreateDelivery is called. + // Arguments: The SceneObject preset to add to the override purchase list. OWNERSHIP IS NOT TRANSFERRED! + // Which player's list to add an override purchase item to. + // Return value: The new total value of what's in the override purchase list. + + int AddOverridePurchase(const SceneObject* pPurchase, int player); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOverridePurchaseList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: First clears and then adds all the stuff in a Loadout to the override + // purchase list. + // Arguments: The Loadout preset to set the override purchase list to reflect. OWNERSHIP IS NOT TRANSFERRED! + // The player we're talking about. + // Return value: The new total value of what's in the override purchase list. + + int SetOverridePurchaseList(const Loadout* pLoadout, int player); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOverridePurchaseList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: First clears and then adds all the stuff in a Loadout to the override + // purchase list. + // Arguments: The name of the Loadout preset to set the override purchase list to + // represent. + // Return value: The new total value of what's in the override purchase list. + + int SetOverridePurchaseList(std::string loadoutName, int player); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearOverridePurchase + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all items from a specific player's override purchase list. + // Arguments: Which player's override purchase list to clear. + // Return value: None. + + void ClearOverridePurchase(int player) { m_PurchaseOverride[player].clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateDelivery + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes the current order out of a player's buy GUI, creates a Delivery + // based off it, and stuffs it into that player's delivery queue. + // Arguments: Which player to create the delivery for. Cargo AI mode and waypoint. + // Return value: Success or not. + + bool CreateDelivery(int player, int mode, Vector& waypoint) { return CreateDelivery(player, mode, waypoint, NULL); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateDelivery + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes the current order out of a player's buy GUI, creates a Delivery + // based off it, and stuffs it into that player's delivery queue. + // Arguments: Which player to create the delivery for. Cargo AI mode and TargetMO. + // Return value: Success or not. + + bool CreateDelivery(int player, int mode, Actor* pTargetMO) { + Vector point(-1, -1); + return CreateDelivery(player, mode, point, pTargetMO); + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateDelivery + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes the current order out of a player's buy GUI, creates a Delivery + // based off it, and stuffs it into that player's delivery queue. + // Arguments: Which player to create the delivery for and Cargo AI mode. + // Return value: Success or not. + + bool CreateDelivery(int player, int mode) { + Vector point(-1, -1); + return CreateDelivery(player, mode, point, NULL); + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateDelivery + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes the current order out of a player's buy GUI, creates a Delivery + // based off it, and stuffs it into that player's delivery queue. + // Arguments: Which player to create the delivery for. + // Return value: Success or not. + + bool CreateDelivery(int player) { + Vector point(-1, -1); + return CreateDelivery(player, Actor::AIMODE_SENTRY, point, NULL); + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDeliveryCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows how many deliveries this team has pending. + // Arguments: Which team to check the delivery count for. + // Return value: The number of deliveries this team has coming. + + int GetDeliveryCount(int team) { return m_Deliveries[team].size(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetupPlayers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Precalculates the player-to-screen index map, counts the number of + // active players etc. + // Arguments: None. + // Return value: None. + + void SetupPlayers() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateEditing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: This is a special update step for when any player is still editing the + // scene. + // Arguments: None. + // Return value: None. + + void UpdateEditing(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTeamTech + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the name of the tech module selected for this team during scenario setup + // Arguments: Team to return tech module for + // Return value: Tech module name, for example Dummy.rte, or empty string if there is no team + std::string GetTeamTech(int team) const { return (team >= Teams::TeamOne && team < Teams::MaxTeamCount) ? m_TeamTech[team] : ""; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetTeamTech + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets tech module name for specified team. Module must set must be loaded. + // Arguments: Team to set module, module name, for example Dummy.rte + // Return value: None + void SetTeamTech(int team, std::string tech); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TeamIsCPU + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether a specific team is assigned a CPU player in the current game. + // Arguments: Which team index to check. + // Return value: Whether the team is assigned a CPU player in the current activity. + bool TeamIsCPU(int team) const { return (team >= Teams::TeamOne && team < Teams::MaxTeamCount) ? m_TeamIsCPU[team] : false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActiveCPUTeamCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns active CPU team count. + // Arguments: None. + // Return value: Returns active CPU team count. + int GetActiveCPUTeamCount() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActiveHumanTeamCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns active human team count. + // Arguments: None. + // Return value: Returns active human team count. + int GetActiveHumanTeamCount() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetStartingGold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes how much starting gold was selected in scenario setup dialog. 20000 - infinite amount. + // Arguments: Starting gold amount + // Return value: None. + void SetStartingGold(int amount) { m_StartingGold = amount; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetStartingGold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns how much starting gold was selected in scenario setup dialog. 20000 - infinite amount. + // Arguments: None. + // Return value: How much starting gold must be given to human players. + int GetStartingGold() { return m_StartingGold; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetFogOfWarEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes whether fog of war must be enabled for this activity or not. + // Never hides or reveals anything, just changes internal flag. + // Arguments: New fog of war state. true = enabled. + // Return value: None. + void SetFogOfWarEnabled(bool enable) { m_FogOfWarEnabled = enable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFogOfWareEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether fog of war must be enabled for this activity or not. + // Call it to determine whether you should call MakeAllUnseen or not at the start of activity. + // Arguments: None. + // Return value: Whether Fog of war flag was checked during scenario setup dialog. + bool GetFogOfWarEnabled() { return m_FogOfWarEnabled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRequireClearPathToOrbit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether player activity requires a cleat path to orbit to place brain + // + // Arguments: None. + // Return value: Whether we need a clear path to orbit to place brains. + bool GetRequireClearPathToOrbit() const { return m_RequireClearPathToOrbit; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetRequireClearPathToOrbit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether player activity requires a cleat path to orbit to place brain + // + // Arguments: Whether we need a clear path to orbit to place brains. + // Return value: None. + void SetRequireClearPathToOrbit(bool newvalue) { m_RequireClearPathToOrbit = newvalue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + int GetDefaultFogOfWar() const { return m_DefaultFogOfWar; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + int GetDefaultRequireClearPathToOrbit() const { return m_DefaultRequireClearPathToOrbit; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + int GetDefaultDeployUnits() const { return m_DefaultDeployUnits; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + int GetDefaultGoldCakeDifficulty() const { return m_DefaultGoldCakeDifficulty; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + int GetDefaultGoldEasyDifficulty() const { return m_DefaultGoldEasyDifficulty; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + int GetDefaultGoldMediumDifficulty() const { return m_DefaultGoldMediumDifficulty; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + int GetDefaultGoldHardDifficulty() const { return m_DefaultGoldHardDifficulty; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + int GetDefaultGoldNutsDifficulty() const { return m_DefaultGoldNutsDifficulty; } + + /// + /// Gets the default gold for max difficulty. + /// + /// The default gold for max difficulty. + int GetDefaultGoldMaxDifficulty() const { return m_DefaultGoldMaxDifficulty; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + bool GetFogOfWarSwitchEnabled() const { return m_FogOfWarSwitchEnabled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + bool GetDeployUnitsSwitchEnabled() const { return m_DeployUnitsSwitchEnabled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + + bool GetGoldSwitchEnabled() const { return m_GoldSwitchEnabled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // Arguments: None. + // Return value: + + bool GetRequireClearPathToOrbitSwitchEnabled() const { return m_RequireClearPathToOrbitSwitchEnabled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCrabToHumanSpawnRatio + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns CrabToHumanSpawnRatio for specified module + // Arguments: None. + // Return value: Crab-To-Human spawn ratio value set for specified module, 0.25 is default. + + float GetCrabToHumanSpawnRatio(int moduleid); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDeliveryDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns current delivery delay + // Arguments: None. + // Return value: Returns current delivery delay + + long GetDeliveryDelay() const { return m_DeliveryDelay; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDeliveryDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets delivery delay + // Arguments: New delivery delay value in ms + // Return value: None + + void SetDeliveryDelay(long newDeliveryDelay) { m_DeliveryDelay = newDeliveryDelay > 1 ? newDeliveryDelay : 1; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBuyMenuEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether buy menu is enabled in this activity. + // Arguments: True if buy menu enabled false otherwise + // Return value: None. + + bool GetBuyMenuEnabled() const { return m_BuyMenuEnabled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBuyMenuEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether buy menu is enabled in this activity + // Arguments: True to enable buy menu, false otherwise + // Return value: None. + + void SetBuyMenuEnabled(bool newValue) { m_BuyMenuEnabled = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNetworkPlayerName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns network player name + // Arguments: Player + // Return value: Network player name + + std::string& GetNetworkPlayerName(int player); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetNetworkPlayerName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets network player name + // Arguments: Player number, player name + // Return value: None + + void SetNetworkPlayerName(int player, std::string name); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateDelivery + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes the current order out of a player's buy GUI, creates a Delivery + // based off it, and stuffs it into that player's delivery queue. + // Arguments: Which player to create the delivery for. Cargo AI mode waypoint or TargetMO. + // Return value: Success or not. + + bool CreateDelivery(int player, int mode, Vector& waypoint, Actor* pTargetMO); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Struct: ObjectivePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A struct to keep all data about a mission objective. + // Parent(s): None. + // Class history: 11/17/2008 ObjectivePoint + + struct ObjectivePoint { + ObjectivePoint() { + m_Description.clear(); + m_ScenePos.Reset(); + m_Team = Teams::NoTeam; + m_ArrowDir = ARROWDOWN; + } + ObjectivePoint(const std::string& desc, const Vector& pos, int team = -1, ObjectiveArrowDir arrowDir = ARROWDOWN) { + m_Description = desc; + m_ScenePos = pos; + m_Team = (Teams)team; + m_ArrowDir = arrowDir; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Simply draws this' arrow relative to a point on a bitmap. + // Arguments: A pointer to the BITMAP to draw on. + // The arrow bitmap to draw, assuming it points downward. + // The absolute position on the bitmap to draw the point of the arrow at. + // Which orientation to draw the arrow in, relative to the point. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, BITMAP* pArrowBitmap, const Vector& arrowPoint, ObjectiveArrowDir arrowDir = ARROWDOWN); + + // The description of this objective point + std::string m_Description; + // Absolute position in the scene where this is pointed + Vector m_ScenePos; + // The team this objective is relevant to + Teams m_Team; + // The positioning of the arrow that points at this objective + ObjectiveArrowDir m_ArrowDir; + }; + + // Comparison functor for sorting objective points by their y pos using STL's sort + struct ObjPointYPosComparison { + bool operator()(ObjectivePoint& rhs, ObjectivePoint& lhs) { return rhs.m_ScenePos.m_Y < lhs.m_ScenePos.m_Y; } + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OtherTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the next other team number from the one passed in, if any. If there + // are more than two teams in this game, then the next one in the series + // will be returned here. + // Arguments: The team not to get. + // Return value: The other team's number. + + int OtherTeam(int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OneOrNoneTeamsLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether there is less than two teams left in this game with + // a brain in its ranks. + // Arguments: None. + // Return value: Whether less than two teams have brains in them left. + + bool OneOrNoneTeamsLeft(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WhichTeamLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates which single team is left, if any. + // Arguments: None. + // Return value: Which team stands alone with any brains in its ranks, if any. NoTeam + // is returned if there's either more than one team, OR there are no + // teams at all left with brains in em. + + int WhichTeamLeft(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: NoTeamLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether there are NO teams left with any brains at all! + // Arguments: None. + // Return value: Whether any team has a brain in it at all. + + bool NoTeamLeft(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: InitAIs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through all Actor:s currently in the MovableMan and sets each + // one not controlled by a player to be AI controlled and AIMode setting + // based on team and CPU team. + // Arguments: None. + // Return value: None. + + virtual void InitAIs(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DisableAIs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through all Actor:s currently in the MovableMan and disables or + // enables each one with a Controller set to AI input. + // Arguments: Whether to disable or enable them; + // Which team to do this to. If all, then pass Teams::NoTeam + // Return value: None. + + void DisableAIs(bool disable = true, int whichTeam = Teams::NoTeam); + + // Member variables + static Entity::ClassInfo m_sClass; + + // Which team is CPU-managed, if any (-1) - LEGACY, now controlled by Activity::m_IsHuman + int m_CPUTeam; + // Team is active or not this game + bool m_TeamIsCPU[Teams::MaxTeamCount]; + + // The observation sceneman scroll targets, for when the game is over or a player is in observation mode + Vector m_ObservationTarget[Players::MaxPlayerCount]; + // The player death sceneman scroll targets, for when a player-controlled actor dies and the view should go to his last position + Vector m_DeathViewTarget[Players::MaxPlayerCount]; + // Times the delay between regular actor swtich, and going into manual siwtch mode + Timer m_ActorSelectTimer[Players::MaxPlayerCount]; + // The cursor for selecting new Actors + Vector m_ActorCursor[Players::MaxPlayerCount]; + // Highlighted actor while cursor switching; will be switched to if switch button is released now + Actor* m_pLastMarkedActor[Players::MaxPlayerCount]; + // The last selected landing zone + Vector m_LandingZone[Players::MaxPlayerCount]; + // Whether the last craft was set to return or not after delivering + bool m_AIReturnCraft[Players::MaxPlayerCount]; + std::array, Players::MaxPlayerCount> m_StrategicModePieMenu; //!< The strategic mode PieMenus for each Player. + // The inventory menu gui for each player + InventoryMenuGUI* m_InventoryMenuGUI[Players::MaxPlayerCount]; + // The in-game buy GUIs for each player + BuyMenuGUI* m_pBuyGUI[Players::MaxPlayerCount]; + // The in-game scene editor GUI for each player + SceneEditorGUI* m_pEditorGUI[Players::MaxPlayerCount]; + bool m_LuaLockActor[Players::MaxPlayerCount]; //!< Whether or not to lock input for each player while lua has control. + Controller::InputMode m_LuaLockActorMode[Players::MaxPlayerCount]; //!< The input mode to lock to while lua has control. + // The in-game important message banners for each player + GUIBanner* m_pBannerRed[Players::MaxPlayerCount]; + GUIBanner* m_pBannerYellow[Players::MaxPlayerCount]; + // How many times a banner has been repeated.. so we dont' annoy by repeating forever + int m_BannerRepeats[Players::MaxPlayerCount]; + // Whether each player has marked himself as ready to start. Can still edit while this is set, but when all are set, the game starts + bool m_ReadyToStart[Players::MaxPlayerCount]; + // An override purchase list that can be set by a script and will be used instead of what's in the buy menu. Object held in here are NOT OWNED + // Once a delivery is made with anything in here, this list is automatically cleared out, and the next delivery will be what's set in the buy menu. + std::list m_PurchaseOverride[Players::MaxPlayerCount]; + + // The delivery queue which contains all the info about all the made orders currently in transit to delivery + std::deque m_Deliveries[Teams::MaxTeamCount]; + // The box within where landing zones can be put + Scene::Area m_LandingZoneArea[Teams::MaxTeamCount]; + // How wide around the brain the automatic LZ is following + int m_BrainLZWidth[Players::MaxPlayerCount]; + // The objective points for each team + std::list m_Objectives; + + // Tech of player + std::string m_TeamTech[Teams::MaxTeamCount]; + + // Initial gold amount selected by player in scenario setup dialog + int m_StartingGold; + // Whether fog of war was enabled or not in scenario setup dialog + bool m_FogOfWarEnabled; + // Whether we need a clear path to orbit to place brain + bool m_RequireClearPathToOrbit; + + // Default fog of war switch state for this activity, default -1 (unspecified) + int m_DefaultFogOfWar; + // Default clear path to orbit switch value, default -1 (unspecified) + int m_DefaultRequireClearPathToOrbit; + // Default deploy units swutch value, default -1 (unspecified) + int m_DefaultDeployUnits; + // Default gold amount for different difficulties, defalt -1 (unspecified) + int m_DefaultGoldCakeDifficulty; + int m_DefaultGoldEasyDifficulty; + int m_DefaultGoldMediumDifficulty; + int m_DefaultGoldHardDifficulty; + int m_DefaultGoldNutsDifficulty; + int m_DefaultGoldMaxDifficulty; + // Whether those switches are enabled or disabled in scenario setup dialog, true by default + bool m_FogOfWarSwitchEnabled; + bool m_DeployUnitsSwitchEnabled; + bool m_GoldSwitchEnabled; + bool m_RequireClearPathToOrbitSwitchEnabled; + bool m_BuyMenuEnabled; + + // The cursor animations for the LZ indicators + std::vector m_aLZCursor[4]; + std::array m_LZCursorWidth; //!< The width of each players' LZ cursor. + // The cursor animations for the objective indications + std::vector m_aObjCursor[4]; + + // Time it takes for a delivery to be made, in ms + long m_DeliveryDelay; + // Cursor animation timer + Timer m_CursorTimer; + // Total gameplay timer, not including editing phases + Timer m_GameTimer; + // Game end timer + Timer m_GameOverTimer; + // Time between game over and reset + long m_GameOverPeriod; + // The winning team number, when the game is over + int m_WinnerTeam; + + std::string m_NetworkPlayerNames[Players::MaxPlayerCount]; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Activities/GibEditor.cpp b/Source/Activities/GibEditor.cpp index fbfa030a02..f4c8cd9469 100644 --- a/Source/Activities/GibEditor.cpp +++ b/Source/Activities/GibEditor.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,11 +17,11 @@ #include "MovableMan.h" #include "FrameMan.h" #include "UInputMan.h" -//#include "AHuman.h" -//#include "MOPixel.h" +// #include "AHuman.h" +// #include "MOPixel.h" #include "SLTerrain.h" #include "Controller.h" -//#include "AtomGroup.h" +// #include "AtomGroup.h" #include "Actor.h" #include "AHuman.h" #include "ACRocket.h" @@ -42,894 +41,806 @@ namespace RTE { -ConcreteClassInfo(GibEditor, EditorActivity, 0); + ConcreteClassInfo(GibEditor, EditorActivity, 0); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this GibEditor, effectively + // resetting the members of this abstraction level only. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this GibEditor, effectively -// resetting the members of this abstraction level only. + void GibEditor::Clear() { + m_pEditedObject = 0; + m_pTestingObject = 0; + m_TestCounter = 0; + m_pObjectToLoad = 0; + m_pEditorGUI = 0; + } -void GibEditor::Clear() -{ - m_pEditedObject = 0; - m_pTestingObject = 0; - m_TestCounter = 0; - m_pObjectToLoad = 0; - m_pEditorGUI = 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GibEditor object ready for use. + int GibEditor::Create() { + if (EditorActivity::Create() < 0) + return -1; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GibEditor object ready for use. + return 0; + } -int GibEditor::Create() -{ - if (EditorActivity::Create() < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a GibEditor to be identical to another, by deep copy. + int GibEditor::Create(const GibEditor& reference) { + if (EditorActivity::Create(reference) < 0) + return -1; - return 0; -} + if (m_Description.empty()) + m_Description = "Edit how Movable Objects break apart into smaller pieces."; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a GibEditor to be identical to another, by deep copy. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int GibEditor::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); + /* + MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); + MatchProperty("Difficulty", { reader >> m_Difficulty; }); + MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); + */ + EndPropertyList; + } -int GibEditor::Create(const GibEditor &reference) -{ - if (EditorActivity::Create(reference) < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this GibEditor with a Writer for + // later recreation with Create(Reader &reader); - if (m_Description.empty()) - m_Description = "Edit how Movable Objects break apart into smaller pieces."; + int GibEditor::Save(Writer& writer) const { + EditorActivity::Save(writer); + return 0; + } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the GibEditor object. + void GibEditor::Destroy(bool notInherited) { + delete m_pEditedObject; + delete m_pTestingObject; + delete m_pEditorGUI; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int GibEditor::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); -/* - MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); - MatchProperty("Difficulty", { reader >> m_Difficulty; }); - MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); -*/ - EndPropertyList; -} + if (!notInherited) + EditorActivity::Destroy(); + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this GibEditor with a Writer for -// later recreation with Create(Reader &reader); + int GibEditor::Start() { + int error = EditorActivity::Start(); -int GibEditor::Save(Writer &writer) const { - EditorActivity::Save(writer); - return 0; -} + ////////////////////////////////////////////// + // Allocate and (re)create the Editor GUI + if (m_pEditorGUI) + m_pEditorGUI->Destroy(); + else + m_pEditorGUI = new GibEditorGUI; + m_pEditorGUI->Create(&(m_PlayerController[0])); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GibEditor object. + ////////////////////////////////////////////////////////////// + // Hooking up directly to the controls defined in the GUI ini -void GibEditor::Destroy(bool notInherited) -{ - delete m_pEditedObject; - delete m_pTestingObject; - delete m_pEditorGUI; + m_pGUIController->Load("Base.rte/GUIs/GibEditorGUI.ini"); - if (!notInherited) - EditorActivity::Destroy(); - Clear(); -} + // Resize the invisible root container so it matches the screen rez + GUICollectionBox* pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); + if (pRootBox) + pRootBox->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of + if (!m_pNewDialogBox) { + m_pNewDialogBox = dynamic_cast(m_pGUIController->GetControl("NewDialogBox")); + // m_pNewDialogBox->SetDrawType(GUICollectionBox::Color); + m_pNewDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pNewDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pNewDialogBox->GetHeight() / 2)); + m_pNewDialogBox->SetVisible(false); + } + // m_pNewModuleCombo = dynamic_cast(m_pGUIController->GetControl("NewModuleCB")); + m_pNewButton = dynamic_cast(m_pGUIController->GetControl("NewSceneButton")); + m_pNewCancel = dynamic_cast(m_pGUIController->GetControl("NewCancelButton")); + + // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of + if (!m_pLoadDialogBox) { + m_pLoadDialogBox = dynamic_cast(m_pGUIController->GetControl("LoadDialogBox")); + // m_pLoadDialogBox->SetDrawType(GUICollectionBox::Color); + m_pLoadDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pLoadDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pLoadDialogBox->GetHeight() / 2)); + m_pLoadDialogBox->SetVisible(false); + } + m_pLoadButton = dynamic_cast(m_pGUIController->GetControl("LoadButton")); + m_pLoadCancel = dynamic_cast(m_pGUIController->GetControl("LoadCancelButton")); + + if (!m_pSaveDialogBox) { + m_pSaveDialogBox = dynamic_cast(m_pGUIController->GetControl("SaveDialogBox")); + + // Set the background image of the parent collection box + // ContentFile backgroundFile("Base.rte/GUIs/BuyMenuBackground.png"); + // m_pSaveDialogBox->SetDrawImage(new AllegroBitmap(backgroundFile.GetAsBitmap())); + // m_pSaveDialogBox->SetDrawBackground(true); + // m_pSaveDialogBox->SetDrawType(GUICollectionBox::Image); + // m_pSaveDialogBox->SetDrawType(GUICollectionBox::Color); + m_pSaveDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pSaveDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pSaveDialogBox->GetHeight() / 2)); + m_pSaveDialogBox->SetVisible(false); + } + m_pSaveNameBox = dynamic_cast(m_pGUIController->GetControl("SaveNameTB")); + m_pSaveModuleLabel = dynamic_cast(m_pGUIController->GetControl("SaveModuleLabel")); + m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveButton")); + m_pSaveCancel = dynamic_cast(m_pGUIController->GetControl("SaveCancelButton")); + + if (!m_pChangesDialogBox) { + m_pChangesDialogBox = dynamic_cast(m_pGUIController->GetControl("ChangesDialogBox")); + m_pChangesDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pChangesDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pChangesDialogBox->GetHeight() / 2)); + m_pChangesDialogBox->SetVisible(false); + } + m_pChangesNameLabel = dynamic_cast(m_pGUIController->GetControl("ChangesNameLabel")); + m_pChangesYesButton = dynamic_cast(m_pGUIController->GetControl("ChangesYesButton")); + m_pChangesNoButton = dynamic_cast(m_pGUIController->GetControl("ChangesNoButton")); + + if (!m_pOverwriteDialogBox) { + m_pOverwriteDialogBox = dynamic_cast(m_pGUIController->GetControl("OverwriteDialogBox")); + m_pOverwriteDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pOverwriteDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pOverwriteDialogBox->GetHeight() / 2)); + m_pOverwriteDialogBox->SetVisible(false); + } + m_pOverwriteNameLabel = dynamic_cast(m_pGUIController->GetControl("OverwriteNameLabel")); + m_pOverwriteYesButton = dynamic_cast(m_pGUIController->GetControl("OverwriteYesButton")); + m_pOverwriteNoButton = dynamic_cast(m_pGUIController->GetControl("OverwriteNoButton")); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. - -int GibEditor::Start() -{ - int error = EditorActivity::Start(); - - ////////////////////////////////////////////// - // Allocate and (re)create the Editor GUI - - if (m_pEditorGUI) - m_pEditorGUI->Destroy(); - else - m_pEditorGUI = new GibEditorGUI; - m_pEditorGUI->Create(&(m_PlayerController[0])); - - ////////////////////////////////////////////////////////////// - // Hooking up directly to the controls defined in the GUI ini - - m_pGUIController->Load("Base.rte/GUIs/GibEditorGUI.ini"); - - // Resize the invisible root container so it matches the screen rez - GUICollectionBox *pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); - if (pRootBox) - pRootBox->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); - - // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of - if (!m_pNewDialogBox) - { - m_pNewDialogBox = dynamic_cast(m_pGUIController->GetControl("NewDialogBox")); -// m_pNewDialogBox->SetDrawType(GUICollectionBox::Color); - m_pNewDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pNewDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pNewDialogBox->GetHeight() / 2)); - m_pNewDialogBox->SetVisible(false); - } -// m_pNewModuleCombo = dynamic_cast(m_pGUIController->GetControl("NewModuleCB")); - m_pNewButton = dynamic_cast(m_pGUIController->GetControl("NewSceneButton")); - m_pNewCancel = dynamic_cast(m_pGUIController->GetControl("NewCancelButton")); - - // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of - if (!m_pLoadDialogBox) - { - m_pLoadDialogBox = dynamic_cast(m_pGUIController->GetControl("LoadDialogBox")); -// m_pLoadDialogBox->SetDrawType(GUICollectionBox::Color); - m_pLoadDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pLoadDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pLoadDialogBox->GetHeight() / 2)); - m_pLoadDialogBox->SetVisible(false); - } - m_pLoadButton = dynamic_cast(m_pGUIController->GetControl("LoadButton")); - m_pLoadCancel = dynamic_cast(m_pGUIController->GetControl("LoadCancelButton")); - - if (!m_pSaveDialogBox) - { - m_pSaveDialogBox = dynamic_cast(m_pGUIController->GetControl("SaveDialogBox")); - - // Set the background image of the parent collection box -// ContentFile backgroundFile("Base.rte/GUIs/BuyMenuBackground.png"); -// m_pSaveDialogBox->SetDrawImage(new AllegroBitmap(backgroundFile.GetAsBitmap())); -// m_pSaveDialogBox->SetDrawBackground(true); -// m_pSaveDialogBox->SetDrawType(GUICollectionBox::Image); -// m_pSaveDialogBox->SetDrawType(GUICollectionBox::Color); - m_pSaveDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pSaveDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pSaveDialogBox->GetHeight() / 2)); - m_pSaveDialogBox->SetVisible(false); - } - m_pSaveNameBox = dynamic_cast(m_pGUIController->GetControl("SaveNameTB")); - m_pSaveModuleLabel = dynamic_cast(m_pGUIController->GetControl("SaveModuleLabel")); - m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveButton")); - m_pSaveCancel = dynamic_cast(m_pGUIController->GetControl("SaveCancelButton")); - - if (!m_pChangesDialogBox) - { - m_pChangesDialogBox = dynamic_cast(m_pGUIController->GetControl("ChangesDialogBox")); - m_pChangesDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pChangesDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pChangesDialogBox->GetHeight() / 2)); - m_pChangesDialogBox->SetVisible(false); - } - m_pChangesNameLabel = dynamic_cast(m_pGUIController->GetControl("ChangesNameLabel")); - m_pChangesYesButton = dynamic_cast(m_pGUIController->GetControl("ChangesYesButton")); - m_pChangesNoButton = dynamic_cast(m_pGUIController->GetControl("ChangesNoButton")); - - if (!m_pOverwriteDialogBox) - { - m_pOverwriteDialogBox = dynamic_cast(m_pGUIController->GetControl("OverwriteDialogBox")); - m_pOverwriteDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pOverwriteDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pOverwriteDialogBox->GetHeight() / 2)); - m_pOverwriteDialogBox->SetVisible(false); - } - m_pOverwriteNameLabel = dynamic_cast(m_pGUIController->GetControl("OverwriteNameLabel")); - m_pOverwriteYesButton = dynamic_cast(m_pGUIController->GetControl("OverwriteYesButton")); - m_pOverwriteNoButton = dynamic_cast(m_pGUIController->GetControl("OverwriteNoButton")); - - m_EditorMode = EditorActivity::EDITINGOBJECT; - // Show the picker dialog to select an object to load - m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::PICKOBJECTTOLOAD); - m_ModeChange = true; - - return error; -} + m_EditorMode = EditorActivity::EDITINGOBJECT; + // Show the picker dialog to select an object to load + m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::PICKOBJECTTOLOAD); + m_ModeChange = true; + return error; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. -void GibEditor::SetPaused(bool pause) -{ - // Override the pause - m_Paused = false; -} + void GibEditor::SetPaused(bool pause) { + // Override the pause + m_Paused = false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. + void GibEditor::End() { + EditorActivity::End(); -void GibEditor::End() -{ - EditorActivity::End(); + m_ActivityState = ActivityState::Over; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this GibEditor. Supposed to be done every frame + // before drawing. + + void GibEditor::Update() { + // And object hasn't been loaded yet, so get the loading picker going + if (!m_pEditedObject && !m_pObjectToLoad) { + m_NeedSave = false; + m_HasEverBeenSaved = false; + m_PreviousMode = EditorActivity::LOADDIALOG; + m_EditorMode = EditorActivity::EDITINGOBJECT; + // Show the picker dialog to select an object to load + m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::PICKOBJECTTOLOAD); + m_ModeChange = true; + } + // Mode switching etc + EditorActivity::Update(); - m_ActivityState = ActivityState::Over; -} + if (!g_SceneMan.GetScene()) + return; + ////////////////////////////////////// + // Special testing mode -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this GibEditor. Supposed to be done every frame -// before drawing. - -void GibEditor::Update() -{ - // And object hasn't been loaded yet, so get the loading picker going - if (!m_pEditedObject && !m_pObjectToLoad) - { - m_NeedSave = false; - m_HasEverBeenSaved = false; - m_PreviousMode = EditorActivity::LOADDIALOG; - m_EditorMode = EditorActivity::EDITINGOBJECT; - // Show the picker dialog to select an object to load - m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::PICKOBJECTTOLOAD); - m_ModeChange = true; - } - - // Mode switching etc - EditorActivity::Update(); - - if (!g_SceneMan.GetScene()) - return; - - ////////////////////////////////////// - // Special testing mode - - if (m_EditorMode == EditorActivity::TESTINGOBJECT) - { - if (m_ModeChange) - { - m_pGUIController->EnableMouse(false); - m_ModeChange = false; - } - - // We haven't detonated yet - if (m_pTestingObject) - { - g_FrameMan.SetScreenText("Click to test gib the object!", 0, 333); - - // Detonate on command! - if (m_PlayerController[0].IsState(PRESS_PRIMARY) || m_PlayerController[0].IsState(PRESS_SECONDARY) || m_PlayerController[0].IsState(PRESS_FACEBUTTON)) - { - // This adds all the gibs to the movableman - m_pTestingObject->GibThis(); - // Now safe to get rid of the test subject - m_pTestingObject->DestroyScriptState(); - delete m_pTestingObject; - m_pTestingObject = nullptr; - } - } - // Test has blown up, now waiting for user to finish watching the pieces fly - else - { - g_FrameMan.SetScreenText("Click again to go back to editing..."); - - if (m_PlayerController[0].IsState(PRESS_PRIMARY) || m_PlayerController[0].IsState(PRESS_SECONDARY) || m_PlayerController[0].IsState(PRESS_FACEBUTTON)) - { - // Clear out the terrain after a few tests - if (m_TestCounter >= 3) - { - ClearTestArea(); - m_TestCounter = 0; - } - // Clear all crap still flying around - g_MovableMan.PurgeAllMOs(); - g_MovableMan.Update(); - // Go back to editing the edited object - m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - // Pause the sim again - m_Paused = true; - } - } - } - // All dialog boxes are gone and we're editing the object - else if (m_pEditedObject && m_EditorMode == EditorActivity::EDITINGOBJECT) - { - if (m_ModeChange) - { - // Open the picker depending on whetehr there's somehting in the cursor hand or not - m_pEditorGUI->SetEditorGUIMode(m_pEditorGUI->GetCurrentGib() ? GibEditorGUI::ADDINGGIB : GibEditorGUI::PICKINGGIB); - // Hide the cursor for this layer of interface - m_pGUIController->EnableMouse(false); - m_ModeChange = false; - } - g_UInputMan.DisableKeys(false); - } - // We are doing something in the dialog boxes, so don't do anything in the editor interface - else if (m_pEditedObject) - m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); - - ///////////////////////////////////////////////////// - // Update the editor interface - - m_pEditorGUI->Update(); - - // Any edits made, dirtying the object? - m_NeedSave = m_NeedSave || m_pEditorGUI->EditMade(); - - // Get any mode change commands that the user gave the Editor GUI - if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorNew && m_EditorMode != NEWDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::NEWDIALOG; - m_ModeChange = true; - } - // Loading is done differently, only when user has already picked an object ot load from the picker should the dialog box appear - else if (m_pEditorGUI->GetObjectToLoad() && m_EditorMode != LOADDIALOG) - { - m_pObjectToLoad = m_pEditorGUI->GetObjectToLoad(); - m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::LOADDIALOG; - m_ModeChange = true; - } - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorSave && m_EditorMode != SAVEDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::SAVEDIALOG; - m_ModeChange = true; - } - // Test the object by allowing the player to gib temporary test copy instances of the edited object - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorDone) - { - // Make the copy of the current edited object - m_pTestingObject->DestroyScriptState(); - delete m_pTestingObject; - m_pTestingObject = dynamic_cast(m_pEditedObject->Clone()); - - // Put the proxy gibs into the test object - StuffEditedGibs(m_pTestingObject); - // Increment the number of tests, after certain amount the terrain is cleared of debris - m_TestCounter++; - - m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); - m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_EditorMode = EditorActivity::TESTINGOBJECT; - m_ModeChange = true; - // Start running the sim - m_Paused = false; - } - - //////////////////////////////////////////////////////// - // Handle events for mouse input on the controls - - GUIEvent anEvent; - while(m_pGUIController->GetEvent(&anEvent)) - { - // If we're not supposed to have mouse control, then ignore these messages -// Uh this is not right, editor always has mouse control so far -// if (!m_PlayerController[0].IsMouseControlled()) -// break; - - if (anEvent.GetType() == GUIEvent::Command) - { -/* - ////////////////////////////////////////////////////////// - // NEW button pressed; create a new object - - if (anEvent.GetControl() == m_pNewButton) - { - // Get the selected Module - GUIListPanel::Item *pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - m_ModuleSpaceID = g_PresetMan.GetModuleID(pItem->m_Name); - - // Allocate Scene - Scene *pNewScene = new Scene(); - // Get the selected Terrain and create the Scene using it - pItem = m_pNewTerrainCombo->GetItem(m_pNewTerrainCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SLTerrain *pNewTerrain = dynamic_cast(g_PresetMan.GetEntityPreset("SLTerrain", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewTerrain, "No SLTerrain of that name defined!"); - pNewScene->Create(pNewTerrain); - } - - // Add specified object layers - pItem = m_pNewBG1Combo->GetItem(m_pNewBG1Combo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SceneLayer of the name set as BG1 is defined!"); - pNewScene->GetBackLayers().push_back(pNewLayer); - } - pItem = m_pNewBG2Combo->GetItem(m_pNewBG2Combo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SceneLayer of the name set as BG2 is defined!"); - pNewScene->GetBackLayers().push_back(pNewLayer); - } - pItem = m_pNewBG3Combo->GetItem(m_pNewBG3Combo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SceneLayer of the name set as BG3 is defined!"); - pNewScene->GetBackLayers().push_back(pNewLayer); - } - - // Actually load the object's data and set it up as the current object - g_SceneMan.LoadScene(pNewScene); - - // Reset the rest of the editor GUI - m_pEditorGUI->Destroy(); - m_pEditorGUI->Create(&(m_PlayerController[0]), m_ModuleSpaceID); - } - - m_NeedSave = false; - m_HasEverBeenSaved = false; - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } -*/ - ////////////////////////////////////////////////////////// - // LOAD button pressed; load the selected Object - - if (anEvent.GetControl() == m_pLoadButton && m_pObjectToLoad) - { - m_pEditedObject = dynamic_cast(m_pObjectToLoad->Clone()); - if (m_pEditedObject) - { - // Get the module space ID - m_ModuleSpaceID = m_pEditedObject->GetModuleID(); - RTEAssert(m_ModuleSpaceID >= 0, "Loaded Object's DataModule ID is negative? Should always be a specific one.."); - - // Restart the editor GUI - m_pEditorGUI->Destroy(); - m_pEditorGUI->Create(&(m_PlayerController[0]), m_ModuleSpaceID); - - // Set the position of the loaded edited object to the middle of the scene - m_pEditedObject->SetPos(Vector(g_SceneMan.GetSceneWidth() / 2, g_SceneMan.GetSceneHeight() / 2)); - m_pEditedObject->Update(); - - // Make proxy copies of the loaded objects' gib reference instances and place them in the list to be edited - std::list *pLoadedGibList = m_pEditedObject->GetGibList(); - std::list *pEditedGibList = m_pEditorGUI->GetPlacedGibs(); - MovableObject *pGibCopy = 0; - - for (auto gItr = pLoadedGibList->begin(); gItr != pLoadedGibList->end(); ++gItr) - { - pGibCopy = dynamic_cast((*gItr).GetParticlePreset()->Clone()); - if (pGibCopy) - { - pGibCopy->SetPos(m_pEditedObject->GetPos() + (*gItr).GetOffset()); - pEditedGibList->push_back(pGibCopy); - } - pGibCopy = 0; - } - - // Clear out the testing area - ClearTestArea(); - m_TestCounter = 0; - - m_pObjectToLoad = 0; - } - m_NeedSave = false; - m_HasEverBeenSaved = true; - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - - ////////////////////////////////////////////////////////// - // SAVE button pressed; save the selected Object - - if (anEvent.GetControl() == m_pSaveButton) - { - if (!m_pSaveNameBox->GetText().empty()) - { - // Save the object to the name specified in the text box - if (SaveObject(m_pSaveNameBox->GetText())) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the object - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - // Should really leave dialog box open? error handling, bitte - else - { - ; - } - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes YES pressed - - if (anEvent.GetControl() == m_pChangesYesButton && m_pEditedObject) - { - if (m_HasEverBeenSaved) - { - if (SaveObject(m_pEditedObject->GetPresetName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the object - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - } - // Open the save object dialog to ask user where to save it then - else - { - m_PreviousMode = m_PreviousMode; - m_EditorMode = EditorActivity::SAVEDIALOG; - m_ModeChange = true; - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes NO pressed - - if (anEvent.GetControl() == m_pChangesNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - m_NeedSave = false; - } - - /////////////////////////////////////////////////////////////// - // Overwrite Object YES pressed - - if (anEvent.GetControl() == m_pOverwriteYesButton && m_pEditedObject) - { - // Force overwrite - if (SaveObject(m_pEditedObject->GetPresetName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after overwrite dialog is done, may have been on the way to test the object - m_EditorMode = m_PreviousMode != EditorActivity::SAVEDIALOG ? m_PreviousMode : EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } -// TODO: Show overwrite error? - } - - /////////////////////////////////////////////////////////////// - // Overwrite Object NO pressed - - if (anEvent.GetControl() == m_pOverwriteNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - - /////////////////////////////////////////////////////////////// - // CANCEL button pressed; exit any active dialog box - - // If load is cancel when just opening the scene - if (anEvent.GetControl() == m_pLoadCancel && !m_pEditedObject) - { - m_PreviousMode = EditorActivity::LOADDIALOG; - m_EditorMode = EditorActivity::EDITINGOBJECT; - // Show the picker dialog to select an object to load - m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::PICKOBJECTTOLOAD); - m_ModeChange = true; - } - if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) - { - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - } - - // Notifications - else if (anEvent.GetType() == GUIEvent::Notification) - { - /////////////////////////////////////// - // Clicks on the New Object Module combo - - if (anEvent.GetControl() == m_pNewModuleCombo) - { - // Closed it, IE selected somehting - if(anEvent.GetMsg() == GUIComboBox::Closed) - UpdateNewDialog(); - } - } - } -} + if (m_EditorMode == EditorActivity::TESTINGOBJECT) { + if (m_ModeChange) { + m_pGUIController->EnableMouse(false); + m_ModeChange = false; + } + // We haven't detonated yet + if (m_pTestingObject) { + g_FrameMan.SetScreenText("Click to test gib the object!", 0, 333); + + // Detonate on command! + if (m_PlayerController[0].IsState(PRESS_PRIMARY) || m_PlayerController[0].IsState(PRESS_SECONDARY) || m_PlayerController[0].IsState(PRESS_FACEBUTTON)) { + // This adds all the gibs to the movableman + m_pTestingObject->GibThis(); + // Now safe to get rid of the test subject + m_pTestingObject->DestroyScriptState(); + delete m_pTestingObject; + m_pTestingObject = nullptr; + } + } + // Test has blown up, now waiting for user to finish watching the pieces fly + else { + g_FrameMan.SetScreenText("Click again to go back to editing..."); + + if (m_PlayerController[0].IsState(PRESS_PRIMARY) || m_PlayerController[0].IsState(PRESS_SECONDARY) || m_PlayerController[0].IsState(PRESS_FACEBUTTON)) { + // Clear out the terrain after a few tests + if (m_TestCounter >= 3) { + ClearTestArea(); + m_TestCounter = 0; + } + // Clear all crap still flying around + g_MovableMan.PurgeAllMOs(); + g_MovableMan.Update(); + // Go back to editing the edited object + m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + // Pause the sim again + m_Paused = true; + } + } + } + // All dialog boxes are gone and we're editing the object + else if (m_pEditedObject && m_EditorMode == EditorActivity::EDITINGOBJECT) { + if (m_ModeChange) { + // Open the picker depending on whetehr there's somehting in the cursor hand or not + m_pEditorGUI->SetEditorGUIMode(m_pEditorGUI->GetCurrentGib() ? GibEditorGUI::ADDINGGIB : GibEditorGUI::PICKINGGIB); + // Hide the cursor for this layer of interface + m_pGUIController->EnableMouse(false); + m_ModeChange = false; + } + g_UInputMan.DisableKeys(false); + } + // We are doing something in the dialog boxes, so don't do anything in the editor interface + else if (m_pEditedObject) + m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. - -void GibEditor::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) -{ - // Testing mode - if (m_EditorMode == EditorActivity::TESTINGOBJECT) - { - if (m_pTestingObject) - m_pTestingObject->Draw(pTargetBitmap, targetPos, g_DrawColor, true); - } - // Regular drawing mode - else - { - // Draw ghost outline of edited object to place gibs upon - if (m_pEditedObject) - { - g_FrameMan.SetTransTableFromPreset(TransparencyPreset::MoreTrans); - // Draw only the MOSRotating since that's all we are adding gibs for; any attachables have to be edited separately - m_pEditedObject->MOSRotating::Draw(pTargetBitmap, targetPos, g_DrawTrans, true); - } - - m_pEditorGUI->Draw(pTargetBitmap, targetPos); - EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); - } -} + ///////////////////////////////////////////////////// + // Update the editor interface + m_pEditorGUI->Update(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this GibEditor's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. + // Any edits made, dirtying the object? + m_NeedSave = m_NeedSave || m_pEditorGUI->EditMade(); -void GibEditor::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) -{ - EditorActivity::Draw(pTargetBitmap, targetPos); -} + // Get any mode change commands that the user gave the Editor GUI + if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorNew && m_EditorMode != NEWDIALOG) { + m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::NEWDIALOG; + m_ModeChange = true; + } + // Loading is done differently, only when user has already picked an object ot load from the picker should the dialog box appear + else if (m_pEditorGUI->GetObjectToLoad() && m_EditorMode != LOADDIALOG) { + m_pObjectToLoad = m_pEditorGUI->GetObjectToLoad(); + m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::LOADDIALOG; + m_ModeChange = true; + } else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorSave && m_EditorMode != SAVEDIALOG) { + m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::SAVEDIALOG; + m_ModeChange = true; + } + // Test the object by allowing the player to gib temporary test copy instances of the edited object + else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorDone) { + // Make the copy of the current edited object + m_pTestingObject->DestroyScriptState(); + delete m_pTestingObject; + m_pTestingObject = dynamic_cast(m_pEditedObject->Clone()); + + // Put the proxy gibs into the test object + StuffEditedGibs(m_pTestingObject); + // Increment the number of tests, after certain amount the terrain is cleared of debris + m_TestCounter++; + + m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::INACTIVE); + m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_EditorMode = EditorActivity::TESTINGOBJECT; + m_ModeChange = true; + // Start running the sim + m_Paused = false; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////// + // Handle events for mouse input on the controls + + GUIEvent anEvent; + while (m_pGUIController->GetEvent(&anEvent)) { + // If we're not supposed to have mouse control, then ignore these messages + // Uh this is not right, editor always has mouse control so far + // if (!m_PlayerController[0].IsMouseControlled()) + // break; + + if (anEvent.GetType() == GUIEvent::Command) { + /* + ////////////////////////////////////////////////////////// + // NEW button pressed; create a new object + + if (anEvent.GetControl() == m_pNewButton) + { + // Get the selected Module + GUIListPanel::Item *pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + m_ModuleSpaceID = g_PresetMan.GetModuleID(pItem->m_Name); + + // Allocate Scene + Scene *pNewScene = new Scene(); + // Get the selected Terrain and create the Scene using it + pItem = m_pNewTerrainCombo->GetItem(m_pNewTerrainCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + SLTerrain *pNewTerrain = dynamic_cast(g_PresetMan.GetEntityPreset("SLTerrain", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewTerrain, "No SLTerrain of that name defined!"); + pNewScene->Create(pNewTerrain); + } + + // Add specified object layers + pItem = m_pNewBG1Combo->GetItem(m_pNewBG1Combo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SceneLayer of the name set as BG1 is defined!"); + pNewScene->GetBackLayers().push_back(pNewLayer); + } + pItem = m_pNewBG2Combo->GetItem(m_pNewBG2Combo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SceneLayer of the name set as BG2 is defined!"); + pNewScene->GetBackLayers().push_back(pNewLayer); + } + pItem = m_pNewBG3Combo->GetItem(m_pNewBG3Combo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + { + SceneLayer *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SceneLayer", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SceneLayer of the name set as BG3 is defined!"); + pNewScene->GetBackLayers().push_back(pNewLayer); + } + + // Actually load the object's data and set it up as the current object + g_SceneMan.LoadScene(pNewScene); + + // Reset the rest of the editor GUI + m_pEditorGUI->Destroy(); + m_pEditorGUI->Create(&(m_PlayerController[0]), m_ModuleSpaceID); + } + + m_NeedSave = false; + m_HasEverBeenSaved = false; + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + */ + ////////////////////////////////////////////////////////// + // LOAD button pressed; load the selected Object + + if (anEvent.GetControl() == m_pLoadButton && m_pObjectToLoad) { + m_pEditedObject = dynamic_cast(m_pObjectToLoad->Clone()); + if (m_pEditedObject) { + // Get the module space ID + m_ModuleSpaceID = m_pEditedObject->GetModuleID(); + RTEAssert(m_ModuleSpaceID >= 0, "Loaded Object's DataModule ID is negative? Should always be a specific one.."); + + // Restart the editor GUI + m_pEditorGUI->Destroy(); + m_pEditorGUI->Create(&(m_PlayerController[0]), m_ModuleSpaceID); + + // Set the position of the loaded edited object to the middle of the scene + m_pEditedObject->SetPos(Vector(g_SceneMan.GetSceneWidth() / 2, g_SceneMan.GetSceneHeight() / 2)); + m_pEditedObject->Update(); + + // Make proxy copies of the loaded objects' gib reference instances and place them in the list to be edited + std::list* pLoadedGibList = m_pEditedObject->GetGibList(); + std::list* pEditedGibList = m_pEditorGUI->GetPlacedGibs(); + MovableObject* pGibCopy = 0; + + for (auto gItr = pLoadedGibList->begin(); gItr != pLoadedGibList->end(); ++gItr) { + pGibCopy = dynamic_cast((*gItr).GetParticlePreset()->Clone()); + if (pGibCopy) { + pGibCopy->SetPos(m_pEditedObject->GetPos() + (*gItr).GetOffset()); + pEditedGibList->push_back(pGibCopy); + } + pGibCopy = 0; + } + + // Clear out the testing area + ClearTestArea(); + m_TestCounter = 0; + + m_pObjectToLoad = 0; + } + m_NeedSave = false; + m_HasEverBeenSaved = true; + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } -bool GibEditor::SaveObject(const std::string &saveAsName, bool forceOverwrite) { - if (!m_pEditedObject) { - return false; - } - m_pEditedObject->SetPresetName(saveAsName); + ////////////////////////////////////////////////////////// + // SAVE button pressed; save the selected Object + + if (anEvent.GetControl() == m_pSaveButton) { + if (!m_pSaveNameBox->GetText().empty()) { + // Save the object to the name specified in the text box + if (SaveObject(m_pSaveNameBox->GetText())) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the object + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + // Should really leave dialog box open? error handling, bitte + else { + ; + } + } + } - // Replace the gibs of the object with the proxies that have been edited in the GUI. - StuffEditedGibs(m_pEditedObject); + /////////////////////////////////////////////////////////////// + // Save Changes YES pressed + + if (anEvent.GetControl() == m_pChangesYesButton && m_pEditedObject) { + if (m_HasEverBeenSaved) { + if (SaveObject(m_pEditedObject->GetPresetName(), true)) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the object + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + } + // Open the save object dialog to ask user where to save it then + else { + m_PreviousMode = m_PreviousMode; + m_EditorMode = EditorActivity::SAVEDIALOG; + m_ModeChange = true; + } + } - std::string dataModuleName = g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName(); - std::string dataModuleFullPath = g_PresetMan.GetFullModulePath(dataModuleName); - std::string newDataDir = dataModuleFullPath + "/NewData"; - std::string objectSavePath = newDataDir + "/" + saveAsName + ".ini"; + /////////////////////////////////////////////////////////////// + // Save Changes NO pressed - if (!System::PathExistsCaseSensitive(newDataDir) && !System::MakeDirectory(newDataDir)) { - RTEError::ShowMessageBox("Failed to create NewData directory in:\n\n" + dataModuleFullPath + "\n\nTHE EDITED OBJECT PRESET WAS NOT SAVED!!!"); - return false; - } + if (anEvent.GetControl() == m_pChangesNoButton) { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + m_NeedSave = false; + } - // Force overwrite the stored preset so we aren't prompted to every time for every preset. This doesn't screw with the original ini because there's no system for that in place - // and doesn't actually get loaded on the next game start either, so any changes will be discarded at the end of the current runtime. - if (g_PresetMan.AddEntityPreset(m_pEditedObject, m_ModuleSpaceID, true, objectSavePath)) { - // Show the overwrite dialog only when actually overwriting a saved ini in NewData or forcing an overwrite. - if (!System::PathExistsCaseSensitive(objectSavePath) || forceOverwrite) { - if (Writer objectWriter(objectSavePath, false); !objectWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + objectSavePath + "\n\nTHE EDITED OBJECT PRESET WAS NOT SAVED!!!"); - } else { - std::string addObjectType; - switch (m_pEditedObject->GetMOType()) { - case MovableObject::MOType::TypeActor: - addObjectType = "AddActor"; - break; - case MovableObject::MOType::TypeHeldDevice: - case MovableObject::MOType::TypeThrownDevice: - addObjectType = "AddDevice"; - break; - default: - addObjectType = "AddEffect"; + /////////////////////////////////////////////////////////////// + // Overwrite Object YES pressed + + if (anEvent.GetControl() == m_pOverwriteYesButton && m_pEditedObject) { + // Force overwrite + if (SaveObject(m_pEditedObject->GetPresetName(), true)) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after overwrite dialog is done, may have been on the way to test the object + m_EditorMode = m_PreviousMode != EditorActivity::SAVEDIALOG ? m_PreviousMode : EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + // TODO: Show overwrite error? } - objectWriter.NewProperty(addObjectType); - m_pEditedObject->Entity::Save(objectWriter); - for (const Gib &gib : *m_pEditedObject->GetGibList()) { - objectWriter.NewPropertyWithValue("AddGib", gib); + + /////////////////////////////////////////////////////////////// + // Overwrite Object NO pressed + + if (anEvent.GetControl() == m_pOverwriteNoButton) { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; } - objectWriter.ObjectEnd(); - objectWriter.EndWrite(); - m_HasEverBeenSaved = true; + /////////////////////////////////////////////////////////////// + // CANCEL button pressed; exit any active dialog box - // TODO: Maybe make system for saving into/over the existing definition read originally from the ini's, wherever it was. + // If load is cancel when just opening the scene + if (anEvent.GetControl() == m_pLoadCancel && !m_pEditedObject) { + m_PreviousMode = EditorActivity::LOADDIALOG; + m_EditorMode = EditorActivity::EDITINGOBJECT; + // Show the picker dialog to select an object to load + m_pEditorGUI->SetEditorGUIMode(GibEditorGUI::PICKOBJECTTOLOAD); + m_ModeChange = true; + } + if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) { + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + } - return true; + // Notifications + else if (anEvent.GetType() == GUIEvent::Notification) { + /////////////////////////////////////// + // Clicks on the New Object Module combo + + if (anEvent.GetControl() == m_pNewModuleCombo) { + // Closed it, IE selected somehting + if (anEvent.GetMsg() == GUIComboBox::Closed) + UpdateNewDialog(); + } } - } else { - // Got to ask if we can overwrite the existing object. - m_PreviousMode = EditorMode::SAVEDIALOG; - m_EditorMode = EditorMode::OVERWRITEDIALOG; - m_ModeChange = true; } } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StuffEditedGibs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Replaces all the gibs owned by the passed in MOSR with the ones being -// edited that represent the new gibbing setup. - -void GibEditor::StuffEditedGibs(MOSRotating *pEditedObject) -{ - if (!pEditedObject) - return; - - // Replace the gibs of the object with the proxies that have been edited in the gui - std::list *pObjectGibList = pEditedObject->GetGibList(); - pObjectGibList->clear(); - - // Take each proxy object and stuff it into a Gib instance which then gets stuffed into the object to be saved - std::list *pProxyGibList = m_pEditorGUI->GetPlacedGibs(); - for (std::list::iterator gItr = pProxyGibList->begin(); gItr != pProxyGibList->end(); ++gItr) - { - Gib newGib; - // Only set the refernce instance directly from the isntanceman. OWNERSHIP IS NOT TRANSFERRED! - newGib.m_GibParticle = dynamic_cast(g_PresetMan.GetEntityPreset((*gItr)->GetClassName(), (*gItr)->GetPresetName(), m_ModuleSpaceID)); - if (newGib.m_GibParticle) - { - newGib.m_Count = 1; - newGib.m_Offset = (*gItr)->GetPos() - pEditedObject->GetPos(); -// TODO: do proper velocity calculations here! -// ... actually leave these as 0 and let them be calculated in GibThis -// newGib.m_MinVelocity = (100.0f + 50.0f * NormalRand()) / (*gItr)->GetMass(); -// newGib.m_MaxVelocity = newGib.m_MinVelocity + ((100.0f * RandomNum()) / (*gItr)->GetMass()); - pObjectGibList->push_back(newGib); - } - } -} + void GibEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + // Testing mode + if (m_EditorMode == EditorActivity::TESTINGOBJECT) { + if (m_pTestingObject) + m_pTestingObject->Draw(pTargetBitmap, targetPos, g_DrawColor, true); + } + // Regular drawing mode + else { + // Draw ghost outline of edited object to place gibs upon + if (m_pEditedObject) { + g_FrameMan.SetTransTableFromPreset(TransparencyPreset::MoreTrans); + // Draw only the MOSRotating since that's all we are adding gibs for; any attachables have to be edited separately + m_pEditedObject->MOSRotating::Draw(pTargetBitmap, targetPos, g_DrawTrans, true); + } + m_pEditorGUI->Draw(pTargetBitmap, targetPos); + EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateNewDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New dialog box, populates its lists etc. - -void GibEditor::UpdateNewDialog() -{ -/* - // Only refill modules if empty - if (m_pNewModuleCombo->GetCount() <= 0) - { - for (int module = 0; module < g_PresetMan.GetTotalModuleCount(); ++module) - m_pNewModuleCombo->AddItem(g_PresetMan.GetDataModule(module)->GetFileName()); - - // Select the first one - m_pNewModuleCombo->SetSelectedIndex(0); - } - - // Get the ID of the module currently selected so we can limit the following boxes to only show stuff in that module - int selectedModuleID = -1; - GUIListPanel::Item *pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - selectedModuleID = g_PresetMan.GetModuleID(pItem->m_Name); - - // Refill Terrains - m_pNewTerrainCombo->ClearList(); - // Get the list of all read in terrains - list terrainList; - g_PresetMan.GetAllOfTypeInModuleSpace(terrainList, "SLTerrain", selectedModuleID); - // Go through the list and add their names to the combo box - for (list::iterator itr = terrainList.begin(); itr != terrainList.end(); ++itr) - m_pNewTerrainCombo->AddItem((*itr)->GetPresetName()); - // Select the first one - m_pNewTerrainCombo->SetSelectedIndex(0); - - // Refill backdrops - m_pNewBG1Combo->SetText(""); - m_pNewBG2Combo->SetText(""); - m_pNewBG3Combo->SetText(""); - m_pNewBG1Combo->ClearList(); - m_pNewBG2Combo->ClearList(); - m_pNewBG3Combo->ClearList(); - // Get the list of all read in terrains - list bgList; - g_PresetMan.GetAllOfTypeInModuleSpace(bgList, "SceneLayer", selectedModuleID); - // Go through the list and add their names to the combo box - for (list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) - { - m_pNewBG1Combo->AddItem((*itr)->GetPresetName()); - m_pNewBG2Combo->AddItem((*itr)->GetPresetName()); - m_pNewBG3Combo->AddItem((*itr)->GetPresetName()); - } - // Select the first one - m_pNewBG1Combo->SetSelectedIndex(0); - m_pNewBG2Combo->SetSelectedIndex(0); - m_pNewBG3Combo->SetSelectedIndex(0); -*/ -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this GibEditor's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + void GibEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + EditorActivity::Draw(pTargetBitmap, targetPos); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateLoadDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Load dialog box, populates its lists etc. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GibEditor::UpdateLoadDialog() -{ - if (m_pObjectToLoad) - dynamic_cast(m_pGUIController->GetControl("LoadNameLabel"))->SetText("Load object named " + m_pObjectToLoad->GetPresetName() + "?"); -} + bool GibEditor::SaveObject(const std::string& saveAsName, bool forceOverwrite) { + if (!m_pEditedObject) { + return false; + } + m_pEditedObject->SetPresetName(saveAsName); + // Replace the gibs of the object with the proxies that have been edited in the GUI. + StuffEditedGibs(m_pEditedObject); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateSaveDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save dialog box, populates its lists etc. + std::string dataModuleName = g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName(); + std::string dataModuleFullPath = g_PresetMan.GetFullModulePath(dataModuleName); + std::string newDataDir = dataModuleFullPath + "/NewData"; + std::string objectSavePath = newDataDir + "/" + saveAsName + ".ini"; -void GibEditor::UpdateSaveDialog() -{ - if (!m_pEditedObject) - return; + if (!System::PathExistsCaseSensitive(newDataDir) && !System::MakeDirectory(newDataDir)) { + RTEError::ShowMessageBox("Failed to create NewData directory in:\n\n" + dataModuleFullPath + "\n\nTHE EDITED OBJECT PRESET WAS NOT SAVED!!!"); + return false; + } - m_pSaveNameBox->SetText((m_pEditedObject->GetPresetName() == "None" || !m_HasEverBeenSaved) ? "New Object" : m_pEditedObject->GetPresetName()); -// TODO: Really? - m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/NewData/"); -} + // Force overwrite the stored preset so we aren't prompted to every time for every preset. This doesn't screw with the original ini because there's no system for that in place + // and doesn't actually get loaded on the next game start either, so any changes will be discarded at the end of the current runtime. + if (g_PresetMan.AddEntityPreset(m_pEditedObject, m_ModuleSpaceID, true, objectSavePath)) { + // Show the overwrite dialog only when actually overwriting a saved ini in NewData or forcing an overwrite. + if (!System::PathExistsCaseSensitive(objectSavePath) || forceOverwrite) { + if (Writer objectWriter(objectSavePath, false); !objectWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + objectSavePath + "\n\nTHE EDITED OBJECT PRESET WAS NOT SAVED!!!"); + } else { + std::string addObjectType; + switch (m_pEditedObject->GetMOType()) { + case MovableObject::MOType::TypeActor: + addObjectType = "AddActor"; + break; + case MovableObject::MOType::TypeHeldDevice: + case MovableObject::MOType::TypeThrownDevice: + addObjectType = "AddDevice"; + break; + default: + addObjectType = "AddEffect"; + } + objectWriter.NewProperty(addObjectType); + m_pEditedObject->Entity::Save(objectWriter); + for (const Gib& gib: *m_pEditedObject->GetGibList()) { + objectWriter.NewPropertyWithValue("AddGib", gib); + } + objectWriter.ObjectEnd(); + objectWriter.EndWrite(); + + m_HasEverBeenSaved = true; + + // TODO: Maybe make system for saving into/over the existing definition read originally from the ini's, wherever it was. + + return true; + } + } else { + // Got to ask if we can overwrite the existing object. + m_PreviousMode = EditorMode::SAVEDIALOG; + m_EditorMode = EditorMode::OVERWRITEDIALOG; + m_ModeChange = true; + } + } + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StuffEditedGibs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Replaces all the gibs owned by the passed in MOSR with the ones being + // edited that represent the new gibbing setup. + + void GibEditor::StuffEditedGibs(MOSRotating* pEditedObject) { + if (!pEditedObject) + return; + + // Replace the gibs of the object with the proxies that have been edited in the gui + std::list* pObjectGibList = pEditedObject->GetGibList(); + pObjectGibList->clear(); + + // Take each proxy object and stuff it into a Gib instance which then gets stuffed into the object to be saved + std::list* pProxyGibList = m_pEditorGUI->GetPlacedGibs(); + for (std::list::iterator gItr = pProxyGibList->begin(); gItr != pProxyGibList->end(); ++gItr) { + Gib newGib; + // Only set the refernce instance directly from the isntanceman. OWNERSHIP IS NOT TRANSFERRED! + newGib.m_GibParticle = dynamic_cast(g_PresetMan.GetEntityPreset((*gItr)->GetClassName(), (*gItr)->GetPresetName(), m_ModuleSpaceID)); + if (newGib.m_GibParticle) { + newGib.m_Count = 1; + newGib.m_Offset = (*gItr)->GetPos() - pEditedObject->GetPos(); + // TODO: do proper velocity calculations here! + // ... actually leave these as 0 and let them be calculated in GibThis + // newGib.m_MinVelocity = (100.0f + 50.0f * NormalRand()) / (*gItr)->GetMass(); + // newGib.m_MaxVelocity = newGib.m_MinVelocity + ((100.0f * RandomNum()) / (*gItr)->GetMass()); + pObjectGibList->push_back(newGib); + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChangesDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save Changes dialog box, populates its lists etc. - -void GibEditor::UpdateChangesDialog() -{ - if (!m_pEditedObject) - return; - - if (m_HasEverBeenSaved) - { - dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); - m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/NewData/" + m_pEditedObject->GetPresetName() + ".ini"); - } - else - { - dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Save your new Object first?"); - m_pChangesNameLabel->SetText(""); - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateNewDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New dialog box, populates its lists etc. + + void GibEditor::UpdateNewDialog() { + /* + // Only refill modules if empty + if (m_pNewModuleCombo->GetCount() <= 0) + { + for (int module = 0; module < g_PresetMan.GetTotalModuleCount(); ++module) + m_pNewModuleCombo->AddItem(g_PresetMan.GetDataModule(module)->GetFileName()); + + // Select the first one + m_pNewModuleCombo->SetSelectedIndex(0); + } + + // Get the ID of the module currently selected so we can limit the following boxes to only show stuff in that module + int selectedModuleID = -1; + GUIListPanel::Item *pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + selectedModuleID = g_PresetMan.GetModuleID(pItem->m_Name); + + // Refill Terrains + m_pNewTerrainCombo->ClearList(); + // Get the list of all read in terrains + list terrainList; + g_PresetMan.GetAllOfTypeInModuleSpace(terrainList, "SLTerrain", selectedModuleID); + // Go through the list and add their names to the combo box + for (list::iterator itr = terrainList.begin(); itr != terrainList.end(); ++itr) + m_pNewTerrainCombo->AddItem((*itr)->GetPresetName()); + // Select the first one + m_pNewTerrainCombo->SetSelectedIndex(0); + + // Refill backdrops + m_pNewBG1Combo->SetText(""); + m_pNewBG2Combo->SetText(""); + m_pNewBG3Combo->SetText(""); + m_pNewBG1Combo->ClearList(); + m_pNewBG2Combo->ClearList(); + m_pNewBG3Combo->ClearList(); + // Get the list of all read in terrains + list bgList; + g_PresetMan.GetAllOfTypeInModuleSpace(bgList, "SceneLayer", selectedModuleID); + // Go through the list and add their names to the combo box + for (list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) + { + m_pNewBG1Combo->AddItem((*itr)->GetPresetName()); + m_pNewBG2Combo->AddItem((*itr)->GetPresetName()); + m_pNewBG3Combo->AddItem((*itr)->GetPresetName()); + } + // Select the first one + m_pNewBG1Combo->SetSelectedIndex(0); + m_pNewBG2Combo->SetSelectedIndex(0); + m_pNewBG3Combo->SetSelectedIndex(0); + */ + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateLoadDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Load dialog box, populates its lists etc. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateOverwriteDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Overwrite dialog box, populates its lists etc. + void GibEditor::UpdateLoadDialog() { + if (m_pObjectToLoad) + dynamic_cast(m_pGUIController->GetControl("LoadNameLabel"))->SetText("Load object named " + m_pObjectToLoad->GetPresetName() + "?"); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateSaveDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save dialog box, populates its lists etc. + + void GibEditor::UpdateSaveDialog() { + if (!m_pEditedObject) + return; + + m_pSaveNameBox->SetText((m_pEditedObject->GetPresetName() == "None" || !m_HasEverBeenSaved) ? "New Object" : m_pEditedObject->GetPresetName()); + // TODO: Really? + m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/NewData/"); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateChangesDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save Changes dialog box, populates its lists etc. -void GibEditor::UpdateOverwriteDialog() -{ - if (m_pEditedObject) - m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/NewData/" + m_pEditedObject->GetPresetName() + ".ini"); -} + void GibEditor::UpdateChangesDialog() { + if (!m_pEditedObject) + return; + + if (m_HasEverBeenSaved) { + dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); + m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/NewData/" + m_pEditedObject->GetPresetName() + ".ini"); + } else { + dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Save your new Object first?"); + m_pChangesNameLabel->SetText(""); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateOverwriteDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Overwrite dialog box, populates its lists etc. + + void GibEditor::UpdateOverwriteDialog() { + if (m_pEditedObject) + m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/NewData/" + m_pEditedObject->GetPresetName() + ".ini"); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GibEditor::ClearTestArea() const { clear_bitmap(g_SceneMan.GetTerrain()->GetFGColorBitmap()); clear_bitmap(g_SceneMan.GetTerrain()->GetBGColorBitmap()); clear_bitmap(g_SceneMan.GetTerrain()->GetMaterialBitmap()); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Activities/GibEditor.h b/Source/Activities/GibEditor.h index 088c56a433..a559548273 100644 --- a/Source/Activities/GibEditor.h +++ b/Source/Activities/GibEditor.h @@ -10,313 +10,284 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" -namespace RTE -{ - -class MOSRotating; -class GibEditorGUI; -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUITab; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUILabel; -class GUIComboBox; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: GibEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activity for editing gib placement within MOSRotating:s. -// Parent(s): EditorActivity. -// Class history: 9/17/2007 GibEditor Created. - -class GibEditor : public EditorActivity { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(GibEditor); -SerializableOverrideMethods; -ClassInfoGetters; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GibEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GibEditor object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - GibEditor() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~GibEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a GibEditor object before deletion -// from system memory. -// Arguments: None. - - ~GibEditor() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GibEditor object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a GibEditor to be identical to another, by deep copy. -// Arguments: A reference to the GibEditor to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const GibEditor &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire GibEditor, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); EditorActivity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GibEditor object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - /// - /// Saves the current object to an appropriate ini file, and asks user if they want to overwrite first if object of this name exists. - /// - /// The name of the new object to be saved. - /// Whether to force any existing Object of that name to be overwritten if it already exists. - /// Whether actually managed to save. Will return false both if an object of this name already exists (and not overwriting), or if other error. - bool SaveObject(const std::string &saveAsName, bool forceOverwrite = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StuffEditedGibs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Replaces all the gibs owned by the passed in MOSR with the ones being -// edited that represent the new gibbing setup. -// Arguments: The MOSRotating to replace the gibs of. Ownership is NOT trnasferred! -// Return value: None. - - void StuffEditedGibs(MOSRotating *pEditedObject); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateNewDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateNewDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateLoadDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Load dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateLoadDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateSaveDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateSaveDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateChangesDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save Changes dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateChangesDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateOverwriteDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Overwrite dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateOverwriteDialog() override; - - - // Member variables - static Entity::ClassInfo m_sClass; - - // The loaded MOSR of which we are editing the gibs. Owned by this. - MOSRotating *m_pEditedObject; - // The temporary copy of the edited object which will be gibbed in the test mode. Owned by this. - MOSRotating *m_pTestingObject; - // How many times tests ahve been run; then the terrain is cleared - int m_TestCounter; - // The MOSR which the user has picked to load. Not owned - const MOSRotating *m_pObjectToLoad; - // The editor GUI - GibEditorGUI *m_pEditorGUI; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - /// - /// Clears all the layers of the testing area terrain so nothing that somehow settled lingers between edited object changes and testing phases. - /// - void ClearTestArea() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class MOSRotating; + class GibEditorGUI; + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUITab; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUILabel; + class GUIComboBox; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: GibEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activity for editing gib placement within MOSRotating:s. + // Parent(s): EditorActivity. + // Class history: 9/17/2007 GibEditor Created. + + class GibEditor : public EditorActivity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(GibEditor); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GibEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GibEditor object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + GibEditor() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~GibEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a GibEditor object before deletion + // from system memory. + // Arguments: None. + + ~GibEditor() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GibEditor object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a GibEditor to be identical to another, by deep copy. + // Arguments: A reference to the GibEditor to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const GibEditor& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire GibEditor, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + EditorActivity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the GibEditor object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Saves the current object to an appropriate ini file, and asks user if they want to overwrite first if object of this name exists. + /// + /// The name of the new object to be saved. + /// Whether to force any existing Object of that name to be overwritten if it already exists. + /// Whether actually managed to save. Will return false both if an object of this name already exists (and not overwriting), or if other error. + bool SaveObject(const std::string& saveAsName, bool forceOverwrite = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StuffEditedGibs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Replaces all the gibs owned by the passed in MOSR with the ones being + // edited that represent the new gibbing setup. + // Arguments: The MOSRotating to replace the gibs of. Ownership is NOT trnasferred! + // Return value: None. + + void StuffEditedGibs(MOSRotating* pEditedObject); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateNewDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateNewDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateLoadDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Load dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateLoadDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateSaveDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateSaveDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateChangesDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save Changes dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateChangesDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateOverwriteDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Overwrite dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateOverwriteDialog() override; + + // Member variables + static Entity::ClassInfo m_sClass; + + // The loaded MOSR of which we are editing the gibs. Owned by this. + MOSRotating* m_pEditedObject; + // The temporary copy of the edited object which will be gibbed in the test mode. Owned by this. + MOSRotating* m_pTestingObject; + // How many times tests ahve been run; then the terrain is cleared + int m_TestCounter; + // The MOSR which the user has picked to load. Not owned + const MOSRotating* m_pObjectToLoad; + // The editor GUI + GibEditorGUI* m_pEditorGUI; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + /// + /// Clears all the layers of the testing area terrain so nothing that somehow settled lingers between edited object changes and testing phases. + /// + void ClearTestArea() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Activities/MultiplayerGame.cpp b/Source/Activities/MultiplayerGame.cpp index 4f96d99780..ad766d335e 100644 --- a/Source/Activities/MultiplayerGame.cpp +++ b/Source/Activities/MultiplayerGame.cpp @@ -5,7 +5,6 @@ // Project: Retro Terrain Engine // Author(s): - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -51,8 +50,7 @@ namespace RTE { // Description: Clears all the member variables of this MultiplayerGame, effectively // resetting the members of this abstraction level only. - void MultiplayerGame::Clear() - { + void MultiplayerGame::Clear() { m_pGUIController = 0; m_pGUIInput = 0; m_pGUIScreen = 0; @@ -81,21 +79,18 @@ namespace RTE { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the MultiplayerGame object ready for use. - int MultiplayerGame::Create() - { + int MultiplayerGame::Create() { if (Activity::Create() < 0) return -1; return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a MultiplayerGame to be identical to another, by deep copy. - int MultiplayerGame::Create(const MultiplayerGame &reference) - { + int MultiplayerGame::Create(const MultiplayerGame& reference) { if (Activity::Create(reference) < 0) return -1; @@ -105,7 +100,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: ReadProperty ////////////////////////////////////////////////////////////////////////////////////////// @@ -114,32 +108,27 @@ namespace RTE { // is called. If the property isn't recognized by any of the base classes, // false is returned, and the reader's position is untouched. - int MultiplayerGame::ReadProperty(const std::string_view &propName, Reader &reader) - { + int MultiplayerGame::ReadProperty(const std::string_view& propName, Reader& reader) { return Activity::ReadProperty(propName, reader); } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Save ////////////////////////////////////////////////////////////////////////////////////////// // Description: Saves the complete state of this MultiplayerGame with a Writer for // later recreation with Create(Reader &reader); - int MultiplayerGame::Save(Writer &writer) const - { + int MultiplayerGame::Save(Writer& writer) const { Activity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the MultiplayerGame object. - void MultiplayerGame::Destroy(bool notInherited) - { + void MultiplayerGame::Destroy(bool notInherited) { g_FrameMan.SetDrawNetworkBackBuffer(false); g_NetworkClient.Disconnect(); @@ -158,8 +147,7 @@ namespace RTE { // Description: Officially starts this. Creates all the data etc necessary to start // the activity. - int MultiplayerGame::Start() - { + int MultiplayerGame::Start() { int error = Activity::Start(); g_AudioMan.ClearMusicQueue(); @@ -179,22 +167,21 @@ namespace RTE { m_pGUIController->EnableMouse(true); // Resize the invisible root container so it matches the screen rez - GUICollectionBox *pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); + GUICollectionBox* pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); if (pRootBox) pRootBox->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); - m_BackToMainButton = dynamic_cast(m_pGUIController->GetControl("ButtonBackToMain")); + m_BackToMainButton = dynamic_cast(m_pGUIController->GetControl("ButtonBackToMain")); - GUICollectionBox *pDialogBox = dynamic_cast(m_pGUIController->GetControl("ConnectDialogBox")); - if (pDialogBox) - { + GUICollectionBox* pDialogBox = dynamic_cast(m_pGUIController->GetControl("ConnectDialogBox")); + if (pDialogBox) { pDialogBox->SetPositionAbs(g_WindowMan.GetResX() / 2 - pDialogBox->GetWidth() / 2, g_WindowMan.GetResY() / 2 - pDialogBox->GetHeight() / 2); m_BackToMainButton->SetPositionAbs((g_WindowMan.GetResX() - m_BackToMainButton->GetWidth()) / 2, pDialogBox->GetYPos() + pDialogBox->GetHeight() + 10); } - m_pServerNameTextBox = dynamic_cast(m_pGUIController->GetControl("ServerNameTB")); - m_pPlayerNameTextBox = dynamic_cast(m_pGUIController->GetControl("PlayerNameTB")); - m_pConnectButton = dynamic_cast(m_pGUIController->GetControl("ConnectButton")); + m_pServerNameTextBox = dynamic_cast(m_pGUIController->GetControl("ServerNameTB")); + m_pPlayerNameTextBox = dynamic_cast(m_pGUIController->GetControl("PlayerNameTB")); + m_pConnectButton = dynamic_cast(m_pGUIController->GetControl("ConnectButton")); /* m_pNATServiceServerNameTextBox = dynamic_cast(m_pGUIController->GetControl("NATServiceNameTB")); @@ -208,8 +195,8 @@ namespace RTE { m_pConnectNATButton->SetVisible(false); */ - //NOTE Instruction labels aren't dynamic so they don't really need to be gotten. Status label should be empty unless there's a status to report. - m_pStatusLabel = dynamic_cast(m_pGUIController->GetControl("StatusLabel")); + // NOTE Instruction labels aren't dynamic so they don't really need to be gotten. Status label should be empty unless there's a status to report. + m_pStatusLabel = dynamic_cast(m_pGUIController->GetControl("StatusLabel")); m_pStatusLabel->SetText(""); m_pServerNameTextBox->SetText(g_SettingsMan.GetNetworkServerAddress()); @@ -229,30 +216,23 @@ namespace RTE { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Pauses and unpauses the game. - void MultiplayerGame::SetPaused(bool pause) - { + void MultiplayerGame::SetPaused(bool pause) { // Override the pause - //m_Paused = false; + // m_Paused = false; Activity::SetPaused(pause); - if (pause) - { - if (g_AudioMan.IsMusicPlaying()) - { + if (pause) { + if (g_AudioMan.IsMusicPlaying()) { m_LastMusic = g_AudioMan.GetMusicPath(); m_LastMusicPos = g_AudioMan.GetMusicPosition(); } - } - else - { - if (m_LastMusic != "") - { + } else { + if (m_LastMusic != "") { g_AudioMan.PlayMusic(m_LastMusic.c_str()); g_AudioMan.SetMusicPosition(m_LastMusicPos); } - if (m_Mode == GAMEPLAY) - { + if (m_Mode == GAMEPLAY) { g_UInputMan.TrapMousePos(true, 0); } } @@ -263,8 +243,7 @@ namespace RTE { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Forces the current game's end. - void MultiplayerGame::End() - { + void MultiplayerGame::End() { Activity::End(); m_ActivityState = ActivityState::Over; @@ -277,14 +256,12 @@ namespace RTE { // Description: Updates the state of this MultiplayerGame. Supposed to be done every frame // before drawing. - void MultiplayerGame::Update() - { + void MultiplayerGame::Update() { Activity::Update(); ///////////////////////////////////////////////////// // Update the editor interface - if (m_Mode == SETUP) - { + if (m_Mode == SETUP) { m_pGUIController->Update(); //////////////////////////////////////////////////////// @@ -293,34 +270,28 @@ namespace RTE { bool toExit = false; GUIEvent anEvent; - while (m_pGUIController->GetEvent(&anEvent)) - { - if (anEvent.GetType() == GUIEvent::Command) - { + while (m_pGUIController->GetEvent(&anEvent)) { + if (anEvent.GetType() == GUIEvent::Command) { if (anEvent.GetControl() == m_BackToMainButton) { g_ActivityMan.PauseActivity(); return; } - if (anEvent.GetControl() == m_pConnectButton) - { + if (anEvent.GetControl() == m_pConnectButton) { std::string serverName; int port; std::string::size_type portPos = std::string::npos; portPos = m_pServerNameTextBox->GetText().find(":"); - if (portPos != std::string::npos) - { + if (portPos != std::string::npos) { serverName = m_pServerNameTextBox->GetText().substr(0, portPos); std::string portStr = m_pServerNameTextBox->GetText().substr(portPos + 1, m_pServerNameTextBox->GetText().length() - 2); port = atoi(portStr.c_str()); if (port == 0) port = 8000; - } - else - { + } else { serverName = m_pServerNameTextBox->GetText(); port = 8000; } @@ -332,14 +303,12 @@ namespace RTE { g_NetworkClient.Connect(serverName, port, playerName); bool saveSettings = false; - if (g_SettingsMan.GetPlayerNetworkName() != m_pPlayerNameTextBox->GetText()) - { + if (g_SettingsMan.GetPlayerNetworkName() != m_pPlayerNameTextBox->GetText()) { g_SettingsMan.SetPlayerNetworkName(m_pPlayerNameTextBox->GetText()); saveSettings = true; } - if (g_SettingsMan.GetNetworkServerAddress() != m_pServerNameTextBox->GetText()) - { + if (g_SettingsMan.GetNetworkServerAddress() != m_pServerNameTextBox->GetText()) { g_SettingsMan.SetNetworkServerAddress(m_pServerNameTextBox->GetText()); saveSettings = true; } @@ -353,26 +322,21 @@ namespace RTE { g_GUISound.ButtonPressSound()->Play(); } - - if (anEvent.GetControl() == m_pConnectNATButton) - { + if (anEvent.GetControl() == m_pConnectNATButton) { std::string serverName; int port; std::string::size_type portPos = std::string::npos; portPos = m_pNATServiceServerNameTextBox->GetText().find(":"); - if (portPos != std::string::npos) - { + if (portPos != std::string::npos) { serverName = m_pNATServiceServerNameTextBox->GetText().substr(0, portPos); std::string portStr = m_pNATServiceServerNameTextBox->GetText().substr(portPos + 1, m_pNATServiceServerNameTextBox->GetText().length() - 2); port = atoi(portStr.c_str()); if (port == 0) port = 61111; - } - else - { + } else { serverName = m_pNATServiceServerNameTextBox->GetText(); port = 61111; } @@ -384,26 +348,22 @@ namespace RTE { g_NetworkClient.PerformNATPunchThrough(serverName, port, playerName, m_pNATServerNameTextBox->GetText(), m_pNATServerPasswordTextBox->GetText()); bool saveSettings = false; - if (g_SettingsMan.GetPlayerNetworkName() != m_pPlayerNameTextBox->GetText()) - { + if (g_SettingsMan.GetPlayerNetworkName() != m_pPlayerNameTextBox->GetText()) { g_SettingsMan.SetPlayerNetworkName(m_pPlayerNameTextBox->GetText()); saveSettings = true; } - if (g_SettingsMan.GetNATServiceAddress() != m_pNATServiceServerNameTextBox->GetText()) - { + if (g_SettingsMan.GetNATServiceAddress() != m_pNATServiceServerNameTextBox->GetText()) { g_SettingsMan.SetNATServiceAddress(m_pNATServiceServerNameTextBox->GetText()); saveSettings = true; } - if (g_SettingsMan.GetNATServerName() != m_pNATServerNameTextBox->GetText()) - { + if (g_SettingsMan.GetNATServerName() != m_pNATServerNameTextBox->GetText()) { g_SettingsMan.SetNATServerName(m_pNATServerNameTextBox->GetText()); saveSettings = true; } - if (g_SettingsMan.GetNATServerPassword() != m_pNATServerPasswordTextBox->GetText()) - { + if (g_SettingsMan.GetNATServerPassword() != m_pNATServerPasswordTextBox->GetText()) { g_SettingsMan.SetNATServerPassword(m_pNATServerPasswordTextBox->GetText()); saveSettings = true; } @@ -418,19 +378,16 @@ namespace RTE { } } // Notifications - else if (anEvent.GetType() == GUIEvent::Notification) - { + else if (anEvent.GetType() == GUIEvent::Notification) { } } } - if (m_Mode == CONNECTION) - { + if (m_Mode == CONNECTION) { if (g_NetworkClient.IsConnectedAndRegistered()) m_Mode = GAMEPLAY; - if (m_ConnectionWaitTimer.IsPastRealMS(8000)) - { + if (m_ConnectionWaitTimer.IsPastRealMS(8000)) { g_NetworkClient.Disconnect(); m_Mode = SETUP; m_pStatusLabel->SetText("Connection failed. Check console for error messages."); @@ -438,16 +395,14 @@ namespace RTE { } } - if (m_Mode == GAMEPLAY) - { + if (m_Mode == GAMEPLAY) { g_UInputMan.TrapMousePos(true, 0); g_FrameMan.SetDrawNetworkBackBuffer(true); m_pGUIController->EnableMouse(false); - if (!g_NetworkClient.IsConnectedAndRegistered()) - { - //g_ActivityMan.EndActivity(); - //g_ActivityMan.SetRestartActivity(); + if (!g_NetworkClient.IsConnectedAndRegistered()) { + // g_ActivityMan.EndActivity(); + // g_ActivityMan.SetRestartActivity(); m_Mode = SETUP; m_pGUIController->EnableMouse(true); g_UInputMan.TrapMousePos(false, 0); @@ -456,9 +411,9 @@ namespace RTE { } /*if (g_UInputMan.ElementHeld(0, UInputMan::INPUT_FIRE)) - g_FrameMan.SetScreenText("FIRE", 0, 0, -1, false); + g_FrameMan.SetScreenText("FIRE", 0, 0, -1, false); else - g_FrameMan.SetScreenText("-", 0, 0, -1, false);*/ + g_FrameMan.SetScreenText("-", 0, 0, -1, false);*/ g_NetworkClient.Update(); } @@ -468,10 +423,8 @@ namespace RTE { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void MultiplayerGame::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) - { - if (m_pGUIController) - { + void MultiplayerGame::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + if (m_pGUIController) { AllegroScreen drawScreen(pTargetBitmap); m_pGUIController->Draw(&drawScreen); if (m_Mode == SETUP) @@ -485,8 +438,7 @@ namespace RTE { // Description: Draws this MultiplayerGame's current graphical representation to a // BITMAP of choice. This includes all game-related graphics. - void MultiplayerGame::Draw(BITMAP* pTargetBitmap, const Vector &targetPos) - { + void MultiplayerGame::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { Activity::Draw(pTargetBitmap, targetPos); } diff --git a/Source/Activities/MultiplayerGame.h b/Source/Activities/MultiplayerGame.h index 0fc755755c..66efb10480 100644 --- a/Source/Activities/MultiplayerGame.h +++ b/Source/Activities/MultiplayerGame.h @@ -4,10 +4,9 @@ ////////////////////////////////////////////////////////////////////////////////////////// // File: MultiplayerGame.h ////////////////////////////////////////////////////////////////////////////////////////// -// Description: +// Description: // Project: Retro Terrain Engine -// Author(s): - +// Author(s): ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -15,8 +14,7 @@ #include "RTETools.h" #include "ActivityMan.h" -namespace RTE -{ +namespace RTE { class MultiplayerGameGUI; class GUIScreen; @@ -30,7 +28,6 @@ namespace RTE class GUILabel; class GUIComboBox; - ////////////////////////////////////////////////////////////////////////////////////////// // Class: MultiplayerGame ////////////////////////////////////////////////////////////////////////////////////////// @@ -45,21 +42,17 @@ namespace RTE // Public member variable, method and friend function declarations public: - // Concrete allocation and cloning definitions EntityAllocation(MultiplayerGame); SerializableOverrideMethods; ClassInfoGetters; - - enum MultiplayerGameMode - { + enum MultiplayerGameMode { SETUP, CONNECTION, GAMEPLAY }; - ////////////////////////////////////////////////////////////////////////////////////////// // Constructor: MultiplayerGame ////////////////////////////////////////////////////////////////////////////////////////// @@ -69,7 +62,6 @@ namespace RTE MultiplayerGame() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Destructor: ~MultiplayerGame ////////////////////////////////////////////////////////////////////////////////////////// @@ -79,7 +71,6 @@ namespace RTE ~MultiplayerGame() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Create ////////////////////////////////////////////////////////////////////////////////////////// @@ -90,7 +81,6 @@ namespace RTE int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Create ////////////////////////////////////////////////////////////////////////////////////////// @@ -99,8 +89,7 @@ namespace RTE // Return value: An error return value signaling sucess or any particular failure. // Anything below 0 is an error signal. - int Create(const MultiplayerGame &reference); - + int Create(const MultiplayerGame& reference); ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Reset @@ -110,8 +99,10 @@ namespace RTE // Arguments: None. // Return value: None. - void Reset() override { Clear(); Activity::Reset(); } - + void Reset() override { + Clear(); + Activity::Reset(); + } ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Destroy @@ -123,7 +114,6 @@ namespace RTE void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Start ////////////////////////////////////////////////////////////////////////////////////////// @@ -134,7 +124,6 @@ namespace RTE int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Pause ////////////////////////////////////////////////////////////////////////////////////////// @@ -144,7 +133,6 @@ namespace RTE void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: End ////////////////////////////////////////////////////////////////////////////////////////// @@ -154,7 +142,6 @@ namespace RTE void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Update ////////////////////////////////////////////////////////////////////////////////////////// @@ -165,7 +152,6 @@ namespace RTE void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: DrawGUI ////////////////////////////////////////////////////////////////////////////////////////// @@ -175,8 +161,7 @@ namespace RTE // Which screen's GUI to draw onto the bitmap. // Return value: None. - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Draw @@ -187,8 +172,7 @@ namespace RTE // The absolute position of the target bitmap's upper left corner in the scene. // Return value: None. - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; ////////////////////////////////////////////////////////////////////////////////////////// // Protected member variable and method declarations @@ -198,37 +182,33 @@ namespace RTE static Entity::ClassInfo m_sClass; // The editor GUI - //MultiplayerGameGUI *m_pEditorGUI; - - GUIButton *m_BackToMainButton; //!< Button to return to main menu. + // MultiplayerGameGUI *m_pEditorGUI; - // - GUITextBox *m_pServerNameTextBox; + GUIButton* m_BackToMainButton; //!< Button to return to main menu. - GUITextBox *m_pPlayerNameTextBox; + // + GUITextBox* m_pServerNameTextBox; - GUIButton *m_pConnectButton; + GUITextBox* m_pPlayerNameTextBox; + GUIButton* m_pConnectButton; + GUITextBox* m_pNATServiceServerNameTextBox; - GUITextBox *m_pNATServiceServerNameTextBox; + GUITextBox* m_pNATServerNameTextBox; - GUITextBox *m_pNATServerNameTextBox; + GUITextBox* m_pNATServerPasswordTextBox; - GUITextBox *m_pNATServerPasswordTextBox; + GUIButton* m_pConnectNATButton; - GUIButton *m_pConnectNATButton; - - - - GUILabel *m_pStatusLabel; + GUILabel* m_pStatusLabel; // GUI Screen for use by the GUI dialog boxes. Owned - GUIScreen *m_pGUIScreen; + GUIScreen* m_pGUIScreen; // Input controller for he dialog box gui. Owned - GUIInput *m_pGUIInput; + GUIInput* m_pGUIInput; // The control manager which holds all the gui elements for the dialog boxes. Owned - GUIControlManager *m_pGUIController; + GUIControlManager* m_pGUIController; // Current state of the activity MultiplayerGameMode m_Mode; @@ -241,12 +221,10 @@ namespace RTE // Position of music being played, used to recover playback state after pause double m_LastMusicPos; - ////////////////////////////////////////////////////////////////////////////////////////// // Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Source/Activities/MultiplayerServerLobby.cpp b/Source/Activities/MultiplayerServerLobby.cpp index 43ad9b4b36..4188217955 100644 --- a/Source/Activities/MultiplayerServerLobby.cpp +++ b/Source/Activities/MultiplayerServerLobby.cpp @@ -1,10 +1,9 @@ ////////////////////////////////////////////////////////////////////////////////////////// // File: MultiplayerServerLobby.cpp ////////////////////////////////////////////////////////////////////////////////////////// -// Description: +// Description: // Project: Retro Terrain Engine -// Author(s): - +// Author(s): ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -51,9 +50,8 @@ namespace RTE { // Description: Clears all the member variables of this MultiplayerServerLobby, effectively // resetting the members of this abstraction level only. - void MultiplayerServerLobby::Clear() - { - //m_pEditorGUI = 0; + void MultiplayerServerLobby::Clear() { + // m_pEditorGUI = 0; m_pGUIController = 0; m_pGUIInput = 0; m_pGUIScreen = 0; @@ -67,23 +65,19 @@ namespace RTE { m_pDifficultyLabel = 0; m_pDifficultySlider = 0; - for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) - { - for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) - { + for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) { + for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) { // m_aaControls = team == TEAM_DISABLED; m_aapPlayerBoxes[player][team] = 0; } } - for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) - { + for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) { m_apTeamBoxes[team] = 0; m_apTeamNameLabels[team] = 0; } - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { m_apTeamTechSelect[team] = 0; m_apTeamAISkillSlider[team] = 0; m_apTeamAISkillLabel[team] = 0; @@ -109,28 +103,24 @@ namespace RTE { m_pUIDrawBitmap = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the MultiplayerServerLobby object ready for use. - int MultiplayerServerLobby::Create() - { + int MultiplayerServerLobby::Create() { if (Activity::Create() < 0) return -1; return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a MultiplayerServerLobby to be identical to another, by deep copy. - int MultiplayerServerLobby::Create(const MultiplayerServerLobby &reference) - { + int MultiplayerServerLobby::Create(const MultiplayerServerLobby& reference) { if (Activity::Create(reference) < 0) return -1; @@ -140,7 +130,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: ReadProperty ////////////////////////////////////////////////////////////////////////////////////////// @@ -149,32 +138,27 @@ namespace RTE { // is called. If the property isn't recognized by any of the base classes, // false is returned, and the reader's position is untouched. - int MultiplayerServerLobby::ReadProperty(const std::string_view &propName, Reader &reader) - { + int MultiplayerServerLobby::ReadProperty(const std::string_view& propName, Reader& reader) { return Activity::ReadProperty(propName, reader); } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Save ////////////////////////////////////////////////////////////////////////////////////////// // Description: Saves the complete state of this MultiplayerServerLobby with a Writer for // later recreation with Create(Reader &reader); - int MultiplayerServerLobby::Save(Writer &writer) const - { + int MultiplayerServerLobby::Save(Writer& writer) const { Activity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the MultiplayerServerLobby object. - void MultiplayerServerLobby::Destroy(bool notInherited) - { + void MultiplayerServerLobby::Destroy(bool notInherited) { delete m_pGUIController; delete m_pGUIInput; delete m_pGUIScreen; @@ -197,8 +181,7 @@ namespace RTE { // Description: Officially starts this. Creates all the data etc necessary to start // the activity. - int MultiplayerServerLobby::Start() - { + int MultiplayerServerLobby::Start() { int error = GameActivity::Start(); g_AudioMan.ClearMusicQueue(); @@ -210,9 +193,9 @@ namespace RTE { // Allocate and (re)create the Editor GUI /*if (m_pEditorGUI) - m_pEditorGUI->Destroy(); + m_pEditorGUI->Destroy(); else - m_pEditorGUI = new MultiplayerServerLobbyGUI; + m_pEditorGUI = new MultiplayerServerLobbyGUI; m_pEditorGUI->Create(&(m_PlayerController[0]));*/ ////////////////////////////////////////////////////////////// @@ -231,56 +214,51 @@ namespace RTE { m_pGUIController->EnableMouse(true); // Resize the invisible root container so it matches the screen rez - m_pRootBox = dynamic_cast(m_pGUIController->GetControl("root")); - m_pPlayerSetupBox = dynamic_cast(m_pGUIController->GetControl("PlayerSetupBox")); - + m_pRootBox = dynamic_cast(m_pGUIController->GetControl("root")); + m_pPlayerSetupBox = dynamic_cast(m_pGUIController->GetControl("PlayerSetupBox")); // Activity Selection Box - m_pActivitySelect = dynamic_cast(m_pGUIController->GetControl("ActivitySelectCombo")); - m_pSceneSelect = dynamic_cast(m_pGUIController->GetControl("SceneSelectCombo")); - m_pDifficultyLabel = dynamic_cast(m_pGUIController->GetControl("DifficultyLabel")); - m_pDifficultySlider = dynamic_cast(m_pGUIController->GetControl("DifficultySlider")); - //m_pActivitySelect->SetDropHeight(64); - // m_pActivitySelect->GetListPanel()->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); - //m_pActivityLabel->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); + m_pActivitySelect = dynamic_cast(m_pGUIController->GetControl("ActivitySelectCombo")); + m_pSceneSelect = dynamic_cast(m_pGUIController->GetControl("SceneSelectCombo")); + m_pDifficultyLabel = dynamic_cast(m_pGUIController->GetControl("DifficultyLabel")); + m_pDifficultySlider = dynamic_cast(m_pGUIController->GetControl("DifficultySlider")); + // m_pActivitySelect->SetDropHeight(64); + // m_pActivitySelect->GetListPanel()->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); + // m_pActivityLabel->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); // Player team assignment box char str[128]; - for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) - { - for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) - { + for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) { + for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) { // +1 because the controls are indexed starting at 1, not 0 std::snprintf(str, sizeof(str), "P%dT%dBox", player + 1, team + 1); - m_aapPlayerBoxes[player][team] = dynamic_cast(m_pGUIController->GetControl(str)); + m_aapPlayerBoxes[player][team] = dynamic_cast(m_pGUIController->GetControl(str)); } } - m_apTeamBoxes[TEAM_DISABLED] = dynamic_cast(m_pGUIController->GetControl("TDIcon")); - m_apTeamBoxes[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1Icon")); - m_apTeamBoxes[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2Icon")); - m_apTeamBoxes[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3Icon")); - m_apTeamBoxes[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4Icon")); - m_apTeamNameLabels[TEAM_DISABLED] = dynamic_cast(m_pGUIController->GetControl("TDLabel")); - m_apTeamNameLabels[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1Label")); - m_apTeamNameLabels[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2Label")); - m_apTeamNameLabels[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3Label")); - m_apTeamNameLabels[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4Label")); - m_apTeamTechSelect[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1TechCombo")); - m_apTeamTechSelect[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2TechCombo")); - m_apTeamTechSelect[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3TechCombo")); - m_apTeamTechSelect[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4TechCombo")); - m_apTeamAISkillSlider[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1AISkillSlider")); - m_apTeamAISkillSlider[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2AISkillSlider")); - m_apTeamAISkillSlider[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3AISkillSlider")); - m_apTeamAISkillSlider[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4AISkillSlider")); - m_apTeamAISkillLabel[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1AISkillLabel")); - m_apTeamAISkillLabel[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2AISkillLabel")); - m_apTeamAISkillLabel[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3AISkillLabel")); - m_apTeamAISkillLabel[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4AISkillLabel")); - - - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) - { + m_apTeamBoxes[TEAM_DISABLED] = dynamic_cast(m_pGUIController->GetControl("TDIcon")); + m_apTeamBoxes[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1Icon")); + m_apTeamBoxes[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2Icon")); + m_apTeamBoxes[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3Icon")); + m_apTeamBoxes[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4Icon")); + m_apTeamNameLabels[TEAM_DISABLED] = dynamic_cast(m_pGUIController->GetControl("TDLabel")); + m_apTeamNameLabels[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1Label")); + m_apTeamNameLabels[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2Label")); + m_apTeamNameLabels[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3Label")); + m_apTeamNameLabels[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4Label")); + m_apTeamTechSelect[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1TechCombo")); + m_apTeamTechSelect[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2TechCombo")); + m_apTeamTechSelect[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3TechCombo")); + m_apTeamTechSelect[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4TechCombo")); + m_apTeamAISkillSlider[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1AISkillSlider")); + m_apTeamAISkillSlider[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2AISkillSlider")); + m_apTeamAISkillSlider[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3AISkillSlider")); + m_apTeamAISkillSlider[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4AISkillSlider")); + m_apTeamAISkillLabel[Teams::TeamOne] = dynamic_cast(m_pGUIController->GetControl("T1AISkillLabel")); + m_apTeamAISkillLabel[Teams::TeamTwo] = dynamic_cast(m_pGUIController->GetControl("T2AISkillLabel")); + m_apTeamAISkillLabel[Teams::TeamThree] = dynamic_cast(m_pGUIController->GetControl("T3AISkillLabel")); + m_apTeamAISkillLabel[Teams::TeamFour] = dynamic_cast(m_pGUIController->GetControl("T4AISkillLabel")); + + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) { // Hide tech combobxes by default m_apTeamTechSelect[team]->SetEnabled(false); m_apTeamTechSelect[team]->SetVisible(false); @@ -297,12 +275,12 @@ namespace RTE { m_apTeamAISkillLabel[team]->SetVisible(false); m_apTeamAISkillLabel[team]->SetText(Activity::GetAISkillString(m_apTeamAISkillSlider[team]->GetValue())); } - m_pStartErrorLabel = dynamic_cast(m_pGUIController->GetControl("StartErrorLabel")); - m_pCPULockLabel = dynamic_cast(m_pGUIController->GetControl("CPULockLabel")); + m_pStartErrorLabel = dynamic_cast(m_pGUIController->GetControl("StartErrorLabel")); + m_pCPULockLabel = dynamic_cast(m_pGUIController->GetControl("CPULockLabel")); // Populate the tech comboboxes with the available tech modules for (int moduleID = 0; moduleID < g_PresetMan.GetTotalModuleCount(); ++moduleID) { - if (const DataModule *dataModule = g_PresetMan.GetDataModule(moduleID)) { + if (const DataModule* dataModule = g_PresetMan.GetDataModule(moduleID)) { if (dataModule->IsFaction()) { for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { m_apTeamTechSelect[team]->GetListPanel()->AddItem(dataModule->GetFriendlyName(), "", nullptr, nullptr, moduleID); @@ -312,35 +290,34 @@ namespace RTE { } } - m_pGoldLabel = dynamic_cast(m_pGUIController->GetControl("GoldLabel")); - m_pGoldSlider = dynamic_cast(m_pGUIController->GetControl("GoldSlider")); - m_pFogOfWarCheckbox = dynamic_cast(m_pGUIController->GetControl("FogOfWarCheckbox")); - m_pRequireClearPathToOrbitCheckbox = dynamic_cast(m_pGUIController->GetControl("RequireClearPathToOrbitCheckbox")); - m_pDeployUnitsCheckbox = dynamic_cast(m_pGUIController->GetControl("DeployUnitsCheckbox")); + m_pGoldLabel = dynamic_cast(m_pGUIController->GetControl("GoldLabel")); + m_pGoldSlider = dynamic_cast(m_pGUIController->GetControl("GoldSlider")); + m_pFogOfWarCheckbox = dynamic_cast(m_pGUIController->GetControl("FogOfWarCheckbox")); + m_pRequireClearPathToOrbitCheckbox = dynamic_cast(m_pGUIController->GetControl("RequireClearPathToOrbitCheckbox")); + m_pDeployUnitsCheckbox = dynamic_cast(m_pGUIController->GetControl("DeployUnitsCheckbox")); - m_pStartScenarioButton = dynamic_cast(m_pGUIController->GetControl("StartButton")); + m_pStartScenarioButton = dynamic_cast(m_pGUIController->GetControl("StartButton")); // TODO: Use old dimensions because don't feel like redesigning this whole GUI. Deal with this eventually. m_pScenePreviewBitmap = create_bitmap_ex(8, 140, 55); - //m_pScenePreviewBitmap = create_bitmap_ex(8, c_ScenePreviewWidth, c_ScenePreviewHeight); + // m_pScenePreviewBitmap = create_bitmap_ex(8, c_ScenePreviewWidth, c_ScenePreviewHeight); ContentFile defaultPreview("Base.rte/GUIs/DefaultPreview000.png"); m_pDefaultPreviewBitmap = defaultPreview.GetAsBitmap(COLORCONV_NONE, false); clear_to_color(m_pScenePreviewBitmap, g_MaskColor); - m_apPlayerIcons[0] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad 1")); - m_apPlayerIcons[1] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad 2")); - m_apPlayerIcons[2] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad 3")); - m_apPlayerIcons[3] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad 4")); + m_apPlayerIcons[0] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad 1")); + m_apPlayerIcons[1] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad 2")); + m_apPlayerIcons[2] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad 3")); + m_apPlayerIcons[3] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad 4")); - m_apPlayerNameLabel[0] = dynamic_cast(m_pGUIController->GetControl("Player1NameLabel")); - m_apPlayerNameLabel[1] = dynamic_cast(m_pGUIController->GetControl("Player2NameLabel")); - m_apPlayerNameLabel[2] = dynamic_cast(m_pGUIController->GetControl("Player3NameLabel")); - m_apPlayerNameLabel[3] = dynamic_cast(m_pGUIController->GetControl("Player4NameLabel")); + m_apPlayerNameLabel[0] = dynamic_cast(m_pGUIController->GetControl("Player1NameLabel")); + m_apPlayerNameLabel[1] = dynamic_cast(m_pGUIController->GetControl("Player2NameLabel")); + m_apPlayerNameLabel[2] = dynamic_cast(m_pGUIController->GetControl("Player3NameLabel")); + m_apPlayerNameLabel[3] = dynamic_cast(m_pGUIController->GetControl("Player4NameLabel")); - if (!m_pCursor) - { + if (!m_pCursor) { ContentFile cursorFile("Base.rte/GUIs/Skins/Cursor.png"); m_pCursor = cursorFile.GetAsBitmap(); } @@ -352,62 +329,53 @@ namespace RTE { UpdateActivityBox(); // This allow to reduce looby bandwidth by 50% while still somewhat descent looking - //g_NetworkServer.SetInterlacingMode(true); + // g_NetworkServer.SetInterlacingMode(true); return error; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateActivityBox ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the contents of the Activity selection box. - void MultiplayerServerLobby::UpdateActivityBox() - { + void MultiplayerServerLobby::UpdateActivityBox() { // Get the currently selected Activity - const Activity *pSelected = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; + const Activity* pSelected = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; - if (pSelected) - { + if (pSelected) { m_pSceneSelect->ClearList(); // Pull out the list of Scenes that are compatible with this Activity - std::map >::iterator asItr; - if (m_Activities.end() != (asItr = m_Activities.find(const_cast(pSelected)))) - { + std::map>::iterator asItr; + if (m_Activities.end() != (asItr = m_Activities.find(const_cast(pSelected)))) { m_pScenes = &((*asItr).second); // Fill scenes combo with compatible scenes - for (std::list::iterator pItr = m_pScenes->begin(); pItr != m_pScenes->end(); ++pItr) - { - Scene *pScene = (*pItr); + for (std::list::iterator pItr = m_pScenes->begin(); pItr != m_pScenes->end(); ++pItr) { + Scene* pScene = (*pItr); m_pSceneSelect->AddItem(pScene->GetPresetName(), "", 0, pScene); } if (m_pSceneSelect->GetCount() > 0) m_pSceneSelect->SetSelectedIndex(0); - m_pSelectedScene = m_pSceneSelect->GetSelectedItem() ? dynamic_cast(m_pSceneSelect->GetSelectedItem()->m_pEntity) : 0; - } - else - { + m_pSelectedScene = m_pSceneSelect->GetSelectedItem() ? dynamic_cast(m_pSceneSelect->GetSelectedItem()->m_pEntity) : 0; + } else { m_pSelectedScene = 0; m_pScenes = 0; } // Resize the box to fit the desc - //m_pActivityBox->Resize(m_pActivityBox->GetWidth(), newHeight + 110); - //UpdateScenesBox(); + // m_pActivityBox->Resize(m_pActivityBox->GetWidth(), newHeight + 110); + // UpdateScenesBox(); - const GameActivity * pSelectedGA = dynamic_cast(pSelected); - if (pSelectedGA) - { + const GameActivity* pSelectedGA = dynamic_cast(pSelected); + if (pSelectedGA) { UpdateGoldSlider(pSelectedGA); UpdateDifficultySlider(); - //Set default fog of war flag and enable or disable it if necessary - if (pSelectedGA->GetDefaultFogOfWar() > -1) - { + // Set default fog of war flag and enable or disable it if necessary + if (pSelectedGA->GetDefaultFogOfWar() > -1) { if (pSelectedGA->GetDefaultFogOfWar() == 0) m_pFogOfWarCheckbox->SetCheck(0); else @@ -415,9 +383,8 @@ namespace RTE { } m_pFogOfWarCheckbox->SetEnabled(pSelectedGA->GetFogOfWarSwitchEnabled()); - //Set default clear path to orbit flag and enable or disable it if necessary - if (pSelectedGA->GetDefaultRequireClearPathToOrbit() > -1) - { + // Set default clear path to orbit flag and enable or disable it if necessary + if (pSelectedGA->GetDefaultRequireClearPathToOrbit() > -1) { if (pSelectedGA->GetDefaultRequireClearPathToOrbit() == 0) m_pRequireClearPathToOrbitCheckbox->SetCheck(0); else @@ -425,9 +392,8 @@ namespace RTE { } m_pRequireClearPathToOrbitCheckbox->SetEnabled(pSelectedGA->GetRequireClearPathToOrbitSwitchEnabled()); - //Set default deploy units flag and enable or disable it if necessary - if (pSelectedGA->GetDefaultDeployUnits() > -1) - { + // Set default deploy units flag and enable or disable it if necessary + if (pSelectedGA->GetDefaultDeployUnits() > -1) { if (pSelectedGA->GetDefaultDeployUnits() == 0) m_pDeployUnitsCheckbox->SetCheck(0); else @@ -439,47 +405,34 @@ namespace RTE { } } - void MultiplayerServerLobby::UpdateGoldSlider(const GameActivity * pSelectedGA) - { + void MultiplayerServerLobby::UpdateGoldSlider(const GameActivity* pSelectedGA) { if (!pSelectedGA) return; // Set gold slider value if activity sepcifies default gold amounts for difficulties - if (m_pDifficultySlider->GetValue() < DifficultySetting::CakeDifficulty) - { + if (m_pDifficultySlider->GetValue() < DifficultySetting::CakeDifficulty) { if (pSelectedGA->GetDefaultGoldCakeDifficulty() > -1) m_pGoldSlider->SetValue(pSelectedGA->GetDefaultGoldCakeDifficulty()); - } - else if (m_pDifficultySlider->GetValue() < DifficultySetting::EasyDifficulty) - { + } else if (m_pDifficultySlider->GetValue() < DifficultySetting::EasyDifficulty) { if (pSelectedGA->GetDefaultGoldEasyDifficulty() > -1) m_pGoldSlider->SetValue(pSelectedGA->GetDefaultGoldEasyDifficulty()); - } - else if (m_pDifficultySlider->GetValue() < DifficultySetting::MediumDifficulty) - { + } else if (m_pDifficultySlider->GetValue() < DifficultySetting::MediumDifficulty) { if (pSelectedGA->GetDefaultGoldMediumDifficulty() > -1) m_pGoldSlider->SetValue(pSelectedGA->GetDefaultGoldMediumDifficulty()); - } - else if (m_pDifficultySlider->GetValue() < DifficultySetting::HardDifficulty) - { + } else if (m_pDifficultySlider->GetValue() < DifficultySetting::HardDifficulty) { if (pSelectedGA->GetDefaultGoldHardDifficulty() > -1) m_pGoldSlider->SetValue(pSelectedGA->GetDefaultGoldHardDifficulty()); - } - else if (m_pDifficultySlider->GetValue() < DifficultySetting::NutsDifficulty) - { + } else if (m_pDifficultySlider->GetValue() < DifficultySetting::NutsDifficulty) { if (pSelectedGA->GetDefaultGoldNutsDifficulty() > -1) m_pGoldSlider->SetValue(pSelectedGA->GetDefaultGoldNutsDifficulty()); - } - else - { + } else { if (pSelectedGA->GetDefaultGoldNutsDifficulty() > -1) m_pGoldSlider->SetValue(pSelectedGA->GetDefaultGoldNutsDifficulty()); } m_pGoldSlider->SetEnabled(pSelectedGA->GetGoldSwitchEnabled()); } - void MultiplayerServerLobby::UpdateDifficultySlider() - { + void MultiplayerServerLobby::UpdateDifficultySlider() { // Set the description if (m_pDifficultySlider->GetValue() < DifficultySetting::CakeDifficulty) m_pDifficultyLabel->SetText("Difficulty: Cake"); @@ -495,43 +448,36 @@ namespace RTE { m_pDifficultyLabel->SetText("Difficulty: Nuts!"); } - - void MultiplayerServerLobby::UpdateSkillSlider() - { - + void MultiplayerServerLobby::UpdateSkillSlider() { } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePlayersBox ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the contents of the player config box. - void MultiplayerServerLobby::UpdatePlayersBox(bool newActivity) - { + void MultiplayerServerLobby::UpdatePlayersBox(bool newActivity) { // Get the currently selected Activity - const Activity *pActivity = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; - const Icon *pIcon = 0; + const Activity* pActivity = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; + const Icon* pIcon = 0; bool teamHasPlayers = false; bool teamHasHumans = false; int teamsWithPlayers = 0; int teamsWithHumans = 0; - if (pActivity && m_pSelectedScene) - { + if (pActivity && m_pSelectedScene) { // Get mouse position and figure out if any cell is being hovered over int mouseX, mouseY; m_pGUIInput->GetMousePosition(&mouseX, &mouseY); Vector mousePos(mouseX, mouseY); bool menuButtonHeld = g_UInputMan.MenuButtonHeld(UInputMan::MENU_EITHER); bool menuButtonReleased = g_UInputMan.MenuButtonReleased(UInputMan::MENU_EITHER); - GUICollectionBox *pHoveredCell = dynamic_cast(m_pGUIController->GetControlUnderPoint(mouseX, mouseY, m_pPlayerSetupBox, 1)); + GUICollectionBox* pHoveredCell = dynamic_cast(m_pGUIController->GetControlUnderPoint(mouseX, mouseY, m_pPlayerSetupBox, 1)); // Is this a game activity? - const GameActivity *pGameActivity = dynamic_cast(pActivity); + const GameActivity* pGameActivity = dynamic_cast(pActivity); - if (newActivity && pGameActivity) - { + if (newActivity && pGameActivity) { // The pre-set team that should absolutely be CPU played m_LockedCPUTeam = pGameActivity->GetCPUTeam(); // Align the locked CPU team text label with the appropriate row @@ -540,39 +486,30 @@ namespace RTE { } // Set up the matrix of player control boxes - for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) - { - for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) - { - if (newActivity) - { + for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) { + for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) { + if (newActivity) { // Everyone starts on the Disabled row, except perhaps the CPU which may be on its locked team - if (team == TEAM_DISABLED) - { + if (team == TEAM_DISABLED) { m_aapPlayerBoxes[player][team]->SetDrawType(GUICollectionBox::Image); - pIcon = player == PLAYER_CPU ? dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")) : m_apPlayerIcons[player];// g_UInputMan.GetSchemeIcon(player); + pIcon = player == PLAYER_CPU ? dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")) : m_apPlayerIcons[player]; // g_UInputMan.GetSchemeIcon(player); if (pIcon) m_aapPlayerBoxes[player][team]->SetDrawImage(new AllegroBitmap(pIcon->GetBitmaps8()[0])); } // De-highlight all other cells initially - else - { + else { m_aapPlayerBoxes[player][team]->SetDrawType(GUICollectionBox::Color); m_aapPlayerBoxes[player][team]->SetDrawColor(c_PlayerSlotColorDefault); } // The CPU gets placed on its locked team - if (m_LockedCPUTeam != Teams::NoTeam && player == PLAYER_CPU) - { - if (team == m_LockedCPUTeam) - { + if (m_LockedCPUTeam != Teams::NoTeam && player == PLAYER_CPU) { + if (team == m_LockedCPUTeam) { m_aapPlayerBoxes[player][team]->SetDrawType(GUICollectionBox::Image); - pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); + pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); if (pIcon) m_aapPlayerBoxes[player][team]->SetDrawImage(new AllegroBitmap(pIcon->GetBitmaps8()[0])); - } - else - { + } else { m_aapPlayerBoxes[player][team]->SetDrawType(GUICollectionBox::Color); m_aapPlayerBoxes[player][team]->SetDrawColor(c_PlayerSlotColorDefault); } @@ -581,45 +518,37 @@ namespace RTE { // Make the hovered cell light up and able to be selected if (m_aapPlayerBoxes[player][team] == pHoveredCell - // if an active team row, but including the 'not playing' row - && (pActivity->TeamActive(team) || team == TEAM_DISABLED) - // That isn't on a team row locked to the CPU - && m_LockedCPUTeam != team - // And not the CPU player if he is locked to a CPU team - && (m_LockedCPUTeam == Teams::NoTeam || player != PLAYER_CPU)) - // And a cell not already selected - //&& m_aapPlayerBoxes[player][team]->GetDrawType() != GUICollectionBox::Image) - // And players aren't maxed out for this Activity, or we are removing a player from team assignment - // && (player == PLAYER_CPU || team == TEAM_DISABLED || (pGameActivity && PlayerCount() < pGameActivity->GetMaxPlayerSupport()))) + // if an active team row, but including the 'not playing' row + && (pActivity->TeamActive(team) || team == TEAM_DISABLED) + // That isn't on a team row locked to the CPU + && m_LockedCPUTeam != team + // And not the CPU player if he is locked to a CPU team + && (m_LockedCPUTeam == Teams::NoTeam || player != PLAYER_CPU)) + // And a cell not already selected + //&& m_aapPlayerBoxes[player][team]->GetDrawType() != GUICollectionBox::Image) + // And players aren't maxed out for this Activity, or we are removing a player from team assignment + // && (player == PLAYER_CPU || team == TEAM_DISABLED || (pGameActivity && PlayerCount() < pGameActivity->GetMaxPlayerSupport()))) { // Is this being pushed and selected? - if (menuButtonReleased) - { + if (menuButtonReleased) { // Need to clear all other rows of this column - // TODO: -- unless the CPU column? - for (int t2 = Teams::TeamOne; t2 < TEAMROWCOUNT; ++t2) - { + // TODO: -- unless the CPU column? + for (int t2 = Teams::TeamOne; t2 < TEAMROWCOUNT; ++t2) { // This clicked cell should get the icon of this column - if (t2 == team) - { - if (player != PLAYER_CPU) - { + if (t2 == team) { + if (player != PLAYER_CPU) { m_aapPlayerBoxes[player][t2]->SetDrawType(GUICollectionBox::Image); - pIcon = player == PLAYER_CPU ? dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")) : m_apPlayerIcons[player]; //g_UInputMan.GetSchemeIcon(player); + pIcon = player == PLAYER_CPU ? dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")) : m_apPlayerIcons[player]; // g_UInputMan.GetSchemeIcon(player); if (pIcon) m_aapPlayerBoxes[player][t2]->SetDrawImage(new AllegroBitmap(pIcon->GetBitmaps8()[0])); - } - else { - //Select or unselect CPU cells - if (m_aapPlayerBoxes[player][t2]->GetDrawType() == GUICollectionBox::Image) - { + } else { + // Select or unselect CPU cells + if (m_aapPlayerBoxes[player][t2]->GetDrawType() == GUICollectionBox::Image) { m_aapPlayerBoxes[player][t2]->SetDrawType(GUICollectionBox::Color); m_aapPlayerBoxes[player][t2]->SetDrawColor(c_PlayerSlotColorDefault); - } - else { - pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); - if (pIcon) - { + } else { + pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); + if (pIcon) { m_aapPlayerBoxes[player][t2]->SetDrawType(GUICollectionBox::Image); m_aapPlayerBoxes[player][t2]->SetDrawImage(new AllegroBitmap(pIcon->GetBitmaps8()[0])); } @@ -627,23 +556,18 @@ namespace RTE { } } // Now unselected columns - else - { - if (player != PLAYER_CPU) - { + else { + if (player != PLAYER_CPU) { m_aapPlayerBoxes[player][t2]->SetDrawType(GUICollectionBox::Color); m_aapPlayerBoxes[player][t2]->SetDrawColor(c_PlayerSlotColorDefault); } } } // If CPU changed to an actual team assignment, clear all human players off his new team - if (player == PLAYER_CPU && team != TEAM_DISABLED) - { - for (int p2 = Players::PlayerOne; p2 < Players::MaxPlayerCount; ++p2) - { + if (player == PLAYER_CPU && team != TEAM_DISABLED) { + for (int p2 = Players::PlayerOne; p2 < Players::MaxPlayerCount; ++p2) { // Deselect the player's team assignment if he's on the same team as the CPU - if (m_aapPlayerBoxes[p2][team]->GetDrawType() == GUICollectionBox::Image) - { + if (m_aapPlayerBoxes[p2][team]->GetDrawType() == GUICollectionBox::Image) { m_aapPlayerBoxes[p2][team]->SetDrawType(GUICollectionBox::Color); m_aapPlayerBoxes[p2][team]->SetDrawColor(c_PlayerSlotColorDefault); // Move him to disabled @@ -655,62 +579,52 @@ namespace RTE { } } // If Player clicked CPU disabled button, clear CPU row - if (player == PLAYER_CPU && team == TEAM_DISABLED) - { - for (int t2 = Teams::TeamOne; t2 <= Teams::TeamFour; ++t2) - { - if (m_aapPlayerBoxes[PLAYER_CPU][t2]->GetDrawType() == GUICollectionBox::Image) - { + if (player == PLAYER_CPU && team == TEAM_DISABLED) { + for (int t2 = Teams::TeamOne; t2 <= Teams::TeamFour; ++t2) { + if (m_aapPlayerBoxes[PLAYER_CPU][t2]->GetDrawType() == GUICollectionBox::Image) { m_aapPlayerBoxes[PLAYER_CPU][t2]->SetDrawType(GUICollectionBox::Color); m_aapPlayerBoxes[PLAYER_CPU][t2]->SetDrawColor(c_PlayerSlotColorDefault); } } } // If a human player changed to a CPU team, remove the CPU guy - else if (player != PLAYER_CPU && team != TEAM_DISABLED) - { + else if (player != PLAYER_CPU && team != TEAM_DISABLED) { // Deselect the CPU's team assignment if he's on the same team as the newly assigned human player - if (m_aapPlayerBoxes[PLAYER_CPU][team]->GetDrawType() == GUICollectionBox::Image) - { + if (m_aapPlayerBoxes[PLAYER_CPU][team]->GetDrawType() == GUICollectionBox::Image) { m_aapPlayerBoxes[PLAYER_CPU][team]->SetDrawType(GUICollectionBox::Color); m_aapPlayerBoxes[PLAYER_CPU][team]->SetDrawColor(c_PlayerSlotColorDefault); // Move him to disabled - //m_aapPlayerBoxes[PLAYER_CPU][TEAM_DISABLED]->SetDrawType(GUICollectionBox::Image); - //pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); - //if (pIcon) + // m_aapPlayerBoxes[PLAYER_CPU][TEAM_DISABLED]->SetDrawType(GUICollectionBox::Image); + // pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); + // if (pIcon) // m_aapPlayerBoxes[PLAYER_CPU][TEAM_DISABLED]->SetDrawImage(new AllegroBitmap(pIcon->GetBitmaps8()[0])); } } - //g_GUISound.FocusChangeSound()->Play(); + // g_GUISound.FocusChangeSound()->Play(); - //Check if we need to clear or set CPU disabled team icon + // Check if we need to clear or set CPU disabled team icon bool noCPUs = true; - for (int t2 = Teams::TeamOne; t2 <= Teams::TeamFour; ++t2) - { + for (int t2 = Teams::TeamOne; t2 <= Teams::TeamFour; ++t2) { if (m_aapPlayerBoxes[PLAYER_CPU][t2]->GetDrawType() == GUICollectionBox::Image) noCPUs = false; } - //Select or unselect CPU disabled icon - if (noCPUs) - { - pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); - if (pIcon) - { + // Select or unselect CPU disabled icon + if (noCPUs) { + pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); + if (pIcon) { m_aapPlayerBoxes[PLAYER_CPU][TEAM_DISABLED]->SetDrawType(GUICollectionBox::Image); m_aapPlayerBoxes[PLAYER_CPU][TEAM_DISABLED]->SetDrawImage(new AllegroBitmap(pIcon->GetBitmaps8()[0])); } - } - else { + } else { m_aapPlayerBoxes[PLAYER_CPU][TEAM_DISABLED]->SetDrawType(GUICollectionBox::Color); m_aapPlayerBoxes[PLAYER_CPU][TEAM_DISABLED]->SetDrawColor(c_PlayerSlotColorDefault); } } // Just highlight the cell - else if (m_aapPlayerBoxes[player][team]->GetDrawColor() != c_PlayerSlotColorHovered) - { + else if (m_aapPlayerBoxes[player][team]->GetDrawColor() != c_PlayerSlotColorHovered) { m_aapPlayerBoxes[player][team]->SetDrawColor(c_PlayerSlotColorHovered); - //g_GUISound.SelectionChangeSound()->Play(); + // g_GUISound.SelectionChangeSound()->Play(); } } // Un-highlight all other cells @@ -720,45 +634,40 @@ namespace RTE { } // Team info columns - for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) - { + for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) { // Update the team names and such - if (newActivity) - { + if (newActivity) { m_apTeamBoxes[team]->SetDrawType(GUICollectionBox::Image); /* pointless; the CPU player icon suffices, and doesn't block the real team banner - // CPU Team - if (pGameActivity && pGameActivity->GetCPUTeam() == team) - { - pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "CPU Team")); - m_apTeamNameLabels[team]->SetText(pActivity->GetTeamName(team) + ":"); - } - // The not playing row - else */if (team == TEAM_DISABLED) - { - pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Disabled Team")); - m_apTeamNameLabels[team]->SetText("Not Playing:"); - } + // CPU Team + if (pGameActivity && pGameActivity->GetCPUTeam() == team) + { + pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "CPU Team")); + m_apTeamNameLabels[team]->SetText(pActivity->GetTeamName(team) + ":"); + } + // The not playing row + else */ + if (team == TEAM_DISABLED) { + pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Disabled Team")); + m_apTeamNameLabels[team]->SetText("Not Playing:"); + } // Active player team - else if (pActivity->TeamActive(team)) - { - // Set the team flag icons on the floating player bars - pIcon = pActivity->GetTeamIcon(team); - // Revert to default if needed - if (!pIcon) - { - char str[128]; - std::snprintf(str, sizeof(str), "Team %d Default", team + 1); - pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", str)); - } - m_apTeamNameLabels[team]->SetText(pActivity->GetTeamName(team) + ":"); - } + else if (pActivity->TeamActive(team)) { + // Set the team flag icons on the floating player bars + pIcon = pActivity->GetTeamIcon(team); + // Revert to default if needed + if (!pIcon) { + char str[128]; + std::snprintf(str, sizeof(str), "Team %d Default", team + 1); + pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", str)); + } + m_apTeamNameLabels[team]->SetText(pActivity->GetTeamName(team) + ":"); + } // Disabled/unplayable teams - else - { - pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Locked Team")); - m_apTeamNameLabels[team]->SetText("Unavailable"); - } + else { + pIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Locked Team")); + m_apTeamNameLabels[team]->SetText("Unavailable"); + } // Finally set whatever Icon we came up with if (pIcon) @@ -766,14 +675,11 @@ namespace RTE { } // Check if the team has any players assigned at all - if (pActivity->TeamActive(team)) - { + if (pActivity->TeamActive(team)) { teamHasPlayers = false; - for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) - { + for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) { // CPU is sometimes disabled, but still counts as a team - if (team != TEAM_DISABLED && m_aapPlayerBoxes[player][team]->GetDrawType() == GUICollectionBox::Image) - { + if (team != TEAM_DISABLED && m_aapPlayerBoxes[player][team]->GetDrawType() == GUICollectionBox::Image) { teamHasPlayers = true; if (player != PLAYER_CPU) teamHasHumans = true; @@ -790,11 +696,8 @@ namespace RTE { m_apTeamAISkillSlider[team]->SetEnabled(true); m_apTeamAISkillSlider[team]->SetVisible(true); m_apTeamAISkillLabel[team]->SetVisible(true); - } - else - { - if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) - { + } else { + if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { m_apTeamTechSelect[team]->SetEnabled(false); m_apTeamTechSelect[team]->SetVisible(false); @@ -806,8 +709,7 @@ namespace RTE { } // If we are over capacity with players, disable the start button and show why - if (pActivity->GetMaxPlayerSupport() < PlayerCount()) - { + if (pActivity->GetMaxPlayerSupport() < PlayerCount()) { m_pStartScenarioButton->SetVisible(false); m_pStartErrorLabel->SetVisible(true); char str[256]; @@ -815,8 +717,7 @@ namespace RTE { m_pStartErrorLabel->SetText(str); } // If we are under the required number of teams with players assigned, disable the start button and show why - else if (pActivity->GetMinTeamsRequired() > teamsWithPlayers) - { + else if (pActivity->GetMinTeamsRequired() > teamsWithPlayers) { m_pStartScenarioButton->SetVisible(false); m_pStartErrorLabel->SetVisible(true); char str[256]; @@ -824,22 +725,20 @@ namespace RTE { m_pStartErrorLabel->SetText(str); } // Assign at least one human player - else if (teamsWithHumans == 0) - { + else if (teamsWithHumans == 0) { m_pStartScenarioButton->SetVisible(false); m_pStartErrorLabel->SetVisible(true); m_pStartErrorLabel->SetText("Assign human players\nto at least one team!"); } // Everything checks out; let the player start if they want to - else - { + else { m_pStartScenarioButton->SetVisible(true); m_pStartErrorLabel->SetVisible(false); } // How much starting gold does the slider yield char str[256]; - //int startGold = (float)m_pGoldSlider->GetMinimum() + ((m_pGoldSlider->GetMaximum() - m_pGoldSlider->GetMinimum()) * (float)m_pGoldSlider->GetValue() / 100.0); + // int startGold = (float)m_pGoldSlider->GetMinimum() + ((m_pGoldSlider->GetMaximum() - m_pGoldSlider->GetMinimum()) * (float)m_pGoldSlider->GetValue() / 100.0); int startGold = m_pGoldSlider->GetValue(); startGold = startGold - startGold % 500; if (m_pGoldSlider->GetValue() == m_pGoldSlider->GetMaximum()) @@ -848,17 +747,14 @@ namespace RTE { std::snprintf(str, sizeof(str), "Starting Gold: %c %d oz", -58, startGold); m_pGoldLabel->SetText(str); - // Set skill labels - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) - { + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) { m_apTeamAISkillLabel[team]->SetText(Activity::GetAISkillString(m_apTeamAISkillSlider[team]->GetValue())); } } // Reset all buttons and positions of things if a new activity has been selected - if (newActivity) - { + if (newActivity) { m_pStartScenarioButton->SetVisible(false); m_pStartErrorLabel->SetVisible(true); m_pCPULockLabel->SetVisible(m_LockedCPUTeam != Teams::NoTeam); @@ -870,14 +766,11 @@ namespace RTE { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Counts how many players are currently assigned to play this Activity. - int MultiplayerServerLobby::PlayerCount() - { + int MultiplayerServerLobby::PlayerCount() { int count = 0; // Go through all the on-team non-CPU cells and see how many players are already assigned. - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { if (m_aapPlayerBoxes[player][team]->GetDrawType() == GUICollectionBox::Image) ++count; } @@ -885,7 +778,6 @@ namespace RTE { return count; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: StartGame ////////////////////////////////////////////////////////////////////////////////////////// @@ -893,21 +785,19 @@ namespace RTE { // Arguments: None. // Return value: None. - bool MultiplayerServerLobby::StartGame() - { + bool MultiplayerServerLobby::StartGame() { // Get the currently selected Activity - const Activity *pActivityPreset = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; + const Activity* pActivityPreset = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; if (!pActivityPreset || !m_pSelectedScene) return false; // Create the actual instance of hte Activity we want to start - Activity *pActivity = dynamic_cast(pActivityPreset->Clone()); - GameActivity *pGameActivity = dynamic_cast(pActivity); + Activity* pActivity = dynamic_cast(pActivityPreset->Clone()); + GameActivity* pGameActivity = dynamic_cast(pActivity); // Set up the basic settings - if (pGameActivity) - { + if (pGameActivity) { pGameActivity->SetDifficulty(m_pDifficultySlider->GetValue()); pGameActivity->SetFogOfWarEnabled(m_pFogOfWarCheckbox->GetCheck()); @@ -919,8 +809,7 @@ namespace RTE { // If gold slider is at it max value then the amount is 'infinite' and we must set some rediculously high value if (m_pGoldSlider->GetValue() == m_pGoldSlider->GetMaximum()) pGameActivity->SetStartingGold(1000000); - else - { + else { int startGold = m_pGoldSlider->GetValue(); startGold = startGold - startGold % 500; pGameActivity->SetStartingGold(startGold); @@ -931,32 +820,26 @@ namespace RTE { // Set up the player and team assignments pActivity->ClearPlayers(false); - for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) - { - for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) - { - if (team != TEAM_DISABLED && m_aapPlayerBoxes[player][team]->GetDrawType() == GUICollectionBox::Image) - { + for (int player = Players::PlayerOne; player < PLAYERCOLUMNCOUNT; ++player) { + for (int team = Teams::TeamOne; team < TEAMROWCOUNT; ++team) { + if (team != TEAM_DISABLED && m_aapPlayerBoxes[player][team]->GetDrawType() == GUICollectionBox::Image) { // Add the human players, not including CPU players if (player != PLAYER_CPU) pActivity->AddPlayer(player, true, team, 0); // CPU team, so mark it as such.. there are no actual CPU players - else if (pGameActivity) - { + else if (pGameActivity) { pGameActivity->SetCPUTeam(team); } } } } - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { // Set up techs - GUIListPanel::Item *pTechItem = m_apTeamTechSelect[team]->GetSelectedItem(); - if (pTechItem) - { + GUIListPanel::Item* pTechItem = m_apTeamTechSelect[team]->GetSelectedItem(); + if (pTechItem) { // If the "random" selection, choose one from the list of loaded techs - if (m_apTeamTechSelect[team]->GetSelectedIndex() == 1)//pTechItem->m_ExtraIndex < 0) + if (m_apTeamTechSelect[team]->GetSelectedIndex() == 1) // pTechItem->m_ExtraIndex < 0) { int selection = RandomNum(1, m_apTeamTechSelect[team]->GetListPanel()->GetItemList()->size() - 1); m_apTeamTechSelect[team]->SetSelectedIndex(selection); @@ -981,7 +864,7 @@ namespace RTE { pGameActivity->SetTeamAISkill(team, AISkillSetting::DefaultSkill); } - //Force close all previously opened files + // Force close all previously opened files g_LuaMan.FileCloseAll(); // Put the new and newly set up Activity as the one to start @@ -998,30 +881,27 @@ namespace RTE { g_MetaMan.EndGame(); // Signal the start of this Activity we just set up - return /*m_ActivityRestarted = */true; + return /*m_ActivityRestarted = */ true; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetAllScenesAndActivities ////////////////////////////////////////////////////////////////////////////////////////// // Description: Gathers all the available Scene:s and Activity presets there are. - void MultiplayerServerLobby::GetAllScenesAndActivities() - { + void MultiplayerServerLobby::GetAllScenesAndActivities() { // Redo the list of Activities m_Activities.clear(); // Get the list of all read in Scene presets - std::list presetList; + std::list presetList; g_PresetMan.GetAllOfType(presetList, "Scene"); - std::list filteredScenes; - Scene *pScene = 0; + std::list filteredScenes; + Scene* pScene = 0; // Go through the list and cast all the pointers to scenes so we have a handy list - for (std::list::iterator pItr = presetList.begin(); pItr != presetList.end(); ++pItr) - { - pScene = dynamic_cast(*pItr); + for (std::list::iterator pItr = presetList.begin(); pItr != presetList.end(); ++pItr) { + pScene = dynamic_cast(*pItr); // Only add non-editor and non-special scenes, or ones that don't have locations defined, or have Test in their names, or are metascenes if (pScene && !pScene->GetLocation().IsZero() && !pScene->IsMetagameInternal() && !pScene->IsSavedGameInternal() && (pScene->GetMetasceneParent() == "" || g_SettingsMan.ShowMetascenes())) filteredScenes.push_back(pScene); @@ -1030,7 +910,7 @@ namespace RTE { // Get the list of all read-in Activity presets presetList.clear(); g_PresetMan.GetAllOfType(presetList, "Activity"); - Activity *pActivity = 0; + Activity* pActivity = 0; int selectedActivityIndex = m_pActivitySelect->GetSelectedIndex(); @@ -1040,18 +920,15 @@ namespace RTE { m_pActivitySelect->ClearList(); int index = 0; int skirmishIndex = -1; - for (std::list::iterator pItr = presetList.begin(); pItr != presetList.end(); ++pItr) - { + for (std::list::iterator pItr = presetList.begin(); pItr != presetList.end(); ++pItr) { bool isMetaActivity = false; - pActivity = dynamic_cast(*pItr); + pActivity = dynamic_cast(*pItr); // Only add non-editor and non-special activities - if (pActivity/* && pActivity->GetClassName() != "GATutorial" */&& pActivity->GetClassName().find("Editor") == std::string::npos) - { + if (pActivity /* && pActivity->GetClassName() != "GATutorial" */ && pActivity->GetClassName().find("Editor") == std::string::npos) { // Prepare a new entry in the list of Activity:ies that we have - std::pair > newPair(pActivity, std::list()); - for (std::list::iterator sItr = filteredScenes.begin(); sItr != filteredScenes.end(); ++sItr) - { + std::pair> newPair(pActivity, std::list()); + for (std::list::iterator sItr = filteredScenes.begin(); sItr != filteredScenes.end(); ++sItr) { // Check if the Scene has the required Area:s and such needed for this Activity if (pActivity->SceneIsCompatible(*sItr)) newPair.second.push_back(*sItr); @@ -1076,22 +953,20 @@ namespace RTE { // Select the Tutorial Activity and Scene by default to start if (skirmishIndex >= 0) m_pActivitySelect->SetSelectedIndex(skirmishIndex); - else + else m_pActivitySelect->SetSelectedIndex(0); UpdateActivityBox(); - //UpdateScenesBox(); + // UpdateScenesBox(); m_pSelectedScene = m_pScenes ? m_pScenes->front() : 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Pause ////////////////////////////////////////////////////////////////////////////////////////// // Description: Pauses and unpauses the game. - void MultiplayerServerLobby::SetPaused(bool pause) - { + void MultiplayerServerLobby::SetPaused(bool pause) { // Override the pause m_Paused = false; } @@ -1101,8 +976,7 @@ namespace RTE { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Forces the current game's end. - void MultiplayerServerLobby::End() - { + void MultiplayerServerLobby::End() { Activity::End(); m_ActivityState = ActivityState::Over; @@ -1115,19 +989,14 @@ namespace RTE { // Description: Updates the state of this MultiplayerServerLobby. Supposed to be done every frame // before drawing. - void MultiplayerServerLobby::Update() - { + void MultiplayerServerLobby::Update() { Activity::Update(); - for (int i = 0; i < c_MaxClients; i++) - { - if (g_NetworkServer.IsPlayerConnected(i)) - { + for (int i = 0; i < c_MaxClients; i++) { + if (g_NetworkServer.IsPlayerConnected(i)) { if (m_apPlayerNameLabel[i]->GetText() != g_NetworkServer.GetPlayerName(i) && g_NetworkServer.GetPlayerName(i) != "") m_apPlayerNameLabel[i]->SetText(g_NetworkServer.GetPlayerName(i)); - } - else - { + } else { if (m_apPlayerNameLabel[i]->GetText() != "- NO PLAYER -") m_apPlayerNameLabel[i]->SetText("- NO PLAYER -"); } @@ -1162,8 +1031,7 @@ namespace RTE { // Arguments: None. // Return value: None. - void MultiplayerServerLobby::UpdateInput() - { + void MultiplayerServerLobby::UpdateInput() { // Move the dialog to the center of the player 0 screen so it could operate in absoute mouse coordintaes of the player 0 screen // During draw we move the dialog to 0,0 before drawing to draw it properly into the intermediate buffer co we can draw it centered on other player's screens. BITMAP* drawBitmap = m_pUIDrawBitmap; @@ -1175,7 +1043,6 @@ namespace RTE { // Move to the center of the player 0 screen m_pRootBox->SetPositionAbs(offsetX, offsetY); - m_pGUIController->Update(); // Move to next state as current have been processed in GUIController:Update @@ -1194,51 +1061,42 @@ namespace RTE { // Handle events GUIEvent anEvent; - while (m_pGUIController->GetEvent(&anEvent)) - { + while (m_pGUIController->GetEvent(&anEvent)) { // Commands - if (anEvent.GetType() == GUIEvent::Command) - { + if (anEvent.GetType() == GUIEvent::Command) { // Start game button pressed - if (anEvent.GetControl() == m_pStartScenarioButton) - { + if (anEvent.GetControl() == m_pStartScenarioButton) { // Try to start the game - if (StartGame()) - { + if (StartGame()) { // Hide all previously shown screens, show - //HideAllScreens(); + // HideAllScreens(); // m_MenuScreen = SCENESELECT; // m_ScreenChange = true; - //g_GUISound.ButtonPressSound()->Play(); - } - else + // g_GUISound.ButtonPressSound()->Play(); + } else g_GUISound.UserErrorSound()->Play(); } } // Notifications - else if (anEvent.GetType() == GUIEvent::Notification) - { + else if (anEvent.GetType() == GUIEvent::Notification) { // Difficulty slider changed - if (anEvent.GetControl() == m_pDifficultySlider) - { + if (anEvent.GetControl() == m_pDifficultySlider) { // Update the difficulty label etc - //UpdateActivityBox(); + // UpdateActivityBox(); UpdateDifficultySlider(); - const Activity *pSelected = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; - const GameActivity * pSelectedGA = dynamic_cast(pSelected); + const Activity* pSelected = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; + const GameActivity* pSelectedGA = dynamic_cast(pSelected); if (pSelectedGA) UpdateGoldSlider(pSelectedGA); } // Activity selection box affected - if (anEvent.GetControl() == m_pActivitySelect) - { + if (anEvent.GetControl() == m_pActivitySelect) { // The activity selection changed - if (anEvent.GetMsg() == GUIComboBox::Closed) - { + if (anEvent.GetMsg() == GUIComboBox::Closed) { // Update the difficulty label etc UpdateActivityBox(); @@ -1250,19 +1108,17 @@ namespace RTE { m_pSelectedScene = 0; // Update the scene info box - //UpdateScenesBox(); - //g_GUISound.ItemChangeSound()->Play(); + // UpdateScenesBox(); + // g_GUISound.ItemChangeSound()->Play(); } } // Scene selection affected - if (anEvent.GetControl() == m_pSceneSelect) - { + if (anEvent.GetControl() == m_pSceneSelect) { // Scene selection changed - if (anEvent.GetMsg() == GUIComboBox::Closed) - { + if (anEvent.GetMsg() == GUIComboBox::Closed) { if (m_pSceneSelect->GetSelectedItem()) - m_pSelectedScene = m_pSceneSelect->GetSelectedItem() ? dynamic_cast(m_pSceneSelect->GetSelectedItem()->m_pEntity) : 0; + m_pSelectedScene = m_pSceneSelect->GetSelectedItem() ? dynamic_cast(m_pSceneSelect->GetSelectedItem()->m_pEntity) : 0; } } } @@ -1271,19 +1127,17 @@ namespace RTE { } } - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: DrawGUI ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void MultiplayerServerLobby::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) - { - if (!m_pGUIController) return; + void MultiplayerServerLobby::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + if (!m_pGUIController) + return; - BITMAP * drawBitmap = m_pUIDrawBitmap; - BITMAP * finalDestBitmap = 0; + BITMAP* drawBitmap = m_pUIDrawBitmap; + BITMAP* finalDestBitmap = 0; clear_to_color(drawBitmap, g_MaskColor); @@ -1295,8 +1149,7 @@ namespace RTE { m_pRootBox->SetPositionAbs(0, 0); // We need to manually draw UI's to intermediate buffer first, then to the player's backbuffer to make it centered on each player's screen. - for (int i = 0; i < 4; i++) - { + for (int i = 0; i < 4; i++) { if (i < c_MaxClients) finalDestBitmap = g_FrameMan.GetNetworkBackBufferIntermediateGUI8Current(i); else @@ -1304,70 +1157,61 @@ namespace RTE { AllegroScreen drawScreen(drawBitmap); m_pGUIController->Draw(&drawScreen); - //m_pGUIController->DrawMouse(); + // m_pGUIController->DrawMouse(); - //Draw player icons + // Draw player icons for (int j = 0; j < c_MaxClients; j++) draw_sprite(drawBitmap, m_apPlayerIcons[j]->GetBitmaps8()[0], m_apPlayerNameLabel[j]->GetXPos() - 32, m_apPlayerNameLabel[j]->GetYPos() - 5); // Draw scene preview after GUI - if (m_pSelectedScene) - { - BITMAP * preview = m_pSelectedScene->GetPreviewBitmap(); - if (preview) - { + if (m_pSelectedScene) { + BITMAP* preview = m_pSelectedScene->GetPreviewBitmap(); + if (preview) { int xOffset = 0; int yOffset = 0; // TODO: Scale down the previews to old dimensions so they fit. Deal with this when redesigning GUI one day. - stretch_blit(preview, m_pScenePreviewBitmap, xOffset, yOffset, c_ScenePreviewWidth, c_ScenePreviewHeight, 0, 0, 140, 55 ); - //blit(preview, m_pScenePreviewBitmap, xOffset, yOffset, 0, 0, m_pScenePreviewBitmap->w, m_pScenePreviewBitmap->h); - } - else - { + stretch_blit(preview, m_pScenePreviewBitmap, xOffset, yOffset, c_ScenePreviewWidth, c_ScenePreviewHeight, 0, 0, 140, 55); + // blit(preview, m_pScenePreviewBitmap, xOffset, yOffset, 0, 0, m_pScenePreviewBitmap->w, m_pScenePreviewBitmap->h); + } else { int xOffset = 0; int yOffset = 0; // TODO: Scale down the previews to old dimensions so they fit. Deal with this when redesigning GUI one day. stretch_blit(m_pDefaultPreviewBitmap, m_pScenePreviewBitmap, xOffset, yOffset, c_ScenePreviewWidth, c_ScenePreviewHeight, 0, 0, 140, 55); - //blit(m_pDefaultPreviewBitmap, m_pScenePreviewBitmap, xOffset, yOffset, 0, 0, m_pScenePreviewBitmap->w, m_pScenePreviewBitmap->h); + // blit(m_pDefaultPreviewBitmap, m_pScenePreviewBitmap, xOffset, yOffset, 0, 0, m_pScenePreviewBitmap->w, m_pScenePreviewBitmap->h); } - draw_sprite(drawBitmap, m_pScenePreviewBitmap, 419, 57); + draw_sprite(drawBitmap, m_pScenePreviewBitmap, 419, 57); } // Draw the Player-Team matrix lines and disabled overlay effects - const Activity *pActivity = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; + const Activity* pActivity = m_pActivitySelect->GetSelectedItem() ? dynamic_cast(m_pActivitySelect->GetSelectedItem()->m_pEntity) : 0; int lineY = 80; - for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) - { + for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { // Disabled shaded boxes - if (pActivity && (!pActivity->TeamActive(team) || m_LockedCPUTeam == team)) - { + if (pActivity && (!pActivity->TeamActive(team) || m_LockedCPUTeam == team)) { // TODO: understand why the blending isnt working as desired - // Transparency effect on the overlay boxes - //drawing_mode(DRAW_MODE_TRANS, 0, 0, 0); - // Screen blend the dots and lines, with some flicekring in its intensity - //int blendAmount = 230; - //set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + // Transparency effect on the overlay boxes + // drawing_mode(DRAW_MODE_TRANS, 0, 0, 0); + // Screen blend the dots and lines, with some flicekring in its intensity + // int blendAmount = 230; + // set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); rectfill(drawBitmap, m_pPlayerSetupBox->GetXPos() + 110, m_pPlayerSetupBox->GetYPos() + lineY + 56, m_pPlayerSetupBox->GetXPos() + m_pPlayerSetupBox->GetWidth() - 12, m_pPlayerSetupBox->GetYPos() + lineY + 25 + 56, c_PlayerSlotColorDisabled); } // Back to solid drawing - //drawing_mode(DRAW_MODE_SOLID, 0, 0, 0); + // drawing_mode(DRAW_MODE_SOLID, 0, 0, 0); // Cell border separator lines line(drawBitmap, m_pPlayerSetupBox->GetXPos() + 110, m_pPlayerSetupBox->GetYPos() + lineY + 56, m_pPlayerSetupBox->GetXPos() + m_pPlayerSetupBox->GetWidth() - 12, m_pPlayerSetupBox->GetYPos() + lineY + 56, c_PlayerSlotColorHovered); lineY += 25; } // Manually draw UI elements on top of colored rectangle - for (int team = Teams::MaxTeamCount - 1; team >= Teams::TeamOne; team--) - { - if (m_apTeamTechSelect[team]->GetVisible()) - { + for (int team = Teams::MaxTeamCount - 1; team >= Teams::TeamOne; team--) { + if (m_apTeamTechSelect[team]->GetVisible()) { m_apTeamTechSelect[team]->Draw(&drawScreen); if (m_apTeamTechSelect[team]->IsDropped()) m_apTeamTechSelect[team]->GetListPanel()->Draw(&drawScreen); } - if (m_apTeamAISkillSlider[team]->GetVisible()) - { + if (m_apTeamAISkillSlider[team]->GetVisible()) { m_apTeamAISkillSlider[team]->Draw(&drawScreen); m_apTeamAISkillLabel[team]->Draw(&drawScreen); } @@ -1381,16 +1225,15 @@ namespace RTE { masked_blit(drawBitmap, finalDestBitmap, 0, 0, offsetX, offsetY, drawBitmap->w, drawBitmap->h); - if (i == 0) - { + if (i == 0) { // Don't apply offsets for player 0 as he always sees mouse in abs coords draw_sprite(finalDestBitmap, m_pCursor, mouseX, mouseY); draw_sprite(finalDestBitmap, m_apPlayerIcons[0]->GetBitmaps8()[0], mouseX + 7, mouseY + 7); } /*else { - draw_sprite(finalDestBitmap, m_pCursor, mouseX - offsetX, mouseY - offsetY); - draw_sprite(finalDestBitmap, m_apPlayerIcons[0]->GetBitmaps8()[0], mouseX + 7 + (baseOffsetX - offsetX), mouseY + 7 + (baseOffsetY - offsetY)); + draw_sprite(finalDestBitmap, m_pCursor, mouseX - offsetX, mouseY - offsetY); + draw_sprite(finalDestBitmap, m_apPlayerIcons[0]->GetBitmaps8()[0], mouseX + 7 + (baseOffsetX - offsetX), mouseY + 7 + (baseOffsetY - offsetY)); }*/ } } @@ -1401,8 +1244,7 @@ namespace RTE { // Description: Draws this MultiplayerServerLobby's current graphical representation to a // BITMAP of choice. This includes all game-related graphics. - void MultiplayerServerLobby::Draw(BITMAP* pTargetBitmap, const Vector &targetPos) - { + void MultiplayerServerLobby::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { Activity::Draw(pTargetBitmap, targetPos); } diff --git a/Source/Activities/MultiplayerServerLobby.h b/Source/Activities/MultiplayerServerLobby.h index 9575db046d..03fa83e5ea 100644 --- a/Source/Activities/MultiplayerServerLobby.h +++ b/Source/Activities/MultiplayerServerLobby.h @@ -4,10 +4,9 @@ ////////////////////////////////////////////////////////////////////////////////////////// // File: MultiplayerServerLobby.h ////////////////////////////////////////////////////////////////////////////////////////// -// Description: +// Description: // Project: Retro Terrain Engine -// Author(s): - +// Author(s): ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -16,8 +15,7 @@ #include "ActivityMan.h" #include "GameActivity.h" -namespace RTE -{ +namespace RTE { class GUIScreen; class GUIInput; class GUIControlManager; @@ -34,7 +32,6 @@ namespace RTE class Scene; class Activity; - ////////////////////////////////////////////////////////////////////////////////////////// // Class: MultiplayerServerLobby ////////////////////////////////////////////////////////////////////////////////////////// @@ -49,7 +46,6 @@ namespace RTE // Public member variable, method and friend function declarations public: - // Concrete allocation and cloning definitions EntityAllocation(MultiplayerServerLobby); SerializableOverrideMethods; @@ -64,7 +60,6 @@ namespace RTE MultiplayerServerLobby() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Destructor: ~MultiplayerServerLobby ////////////////////////////////////////////////////////////////////////////////////////// @@ -74,7 +69,6 @@ namespace RTE ~MultiplayerServerLobby() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Create ////////////////////////////////////////////////////////////////////////////////////////// @@ -85,7 +79,6 @@ namespace RTE int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Create ////////////////////////////////////////////////////////////////////////////////////////// @@ -94,8 +87,7 @@ namespace RTE // Return value: An error return value signaling sucess or any particular failure. // Anything below 0 is an error signal. - int Create(const MultiplayerServerLobby &reference); - + int Create(const MultiplayerServerLobby& reference); ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Reset @@ -105,8 +97,10 @@ namespace RTE // Arguments: None. // Return value: None. - void Reset() override { Clear(); Activity::Reset(); } - + void Reset() override { + Clear(); + Activity::Reset(); + } ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Destroy @@ -118,7 +112,6 @@ namespace RTE void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Start ////////////////////////////////////////////////////////////////////////////////////////// @@ -129,7 +122,6 @@ namespace RTE int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Pause ////////////////////////////////////////////////////////////////////////////////////////// @@ -139,7 +131,6 @@ namespace RTE void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: End ////////////////////////////////////////////////////////////////////////////////////////// @@ -149,7 +140,6 @@ namespace RTE void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Update ////////////////////////////////////////////////////////////////////////////////////////// @@ -160,7 +150,6 @@ namespace RTE void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: DrawGUI ////////////////////////////////////////////////////////////////////////////////////////// @@ -170,8 +159,7 @@ namespace RTE // Which screen's GUI to draw onto the bitmap. // Return value: None. - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Draw @@ -182,17 +170,17 @@ namespace RTE // The absolute position of the target bitmap's upper left corner in the scene. // Return value: None. - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; void UpdateInput(); void UpdateActivityBox(); - //void UpdateScenesBox(); + // void UpdateScenesBox(); void UpdatePlayersBox(bool newActivity); - void UpdateGoldSlider(const GameActivity * pSelectedGA); + void UpdateGoldSlider(const GameActivity* pSelectedGA); void UpdateDifficultySlider(); @@ -204,95 +192,89 @@ namespace RTE int PlayerCount(); - ////////////////////////////////////////////////////////////////////////////////////////// // Protected member variable and method declarations protected: - // These add on the player and team max counts - enum PlayerColumns - { + enum PlayerColumns { PLAYER_CPU = Players::MaxPlayerCount, PLAYERCOLUMNCOUNT }; - enum TeamRows - { + enum TeamRows { TEAM_DISABLED = Teams::MaxTeamCount, TEAMROWCOUNT }; - // Member variables static Entity::ClassInfo m_sClass; // The editor GUI - //MultiplayerServerLobbyGUI *m_pEditorGUI; + // MultiplayerServerLobbyGUI *m_pEditorGUI; // GUI Screen for use by the GUI dialog boxes. Owned - GUIScreen *m_pGUIScreen; + GUIScreen* m_pGUIScreen; // Input controller for he dialog box gui. Owned - GUIInput *m_pGUIInput; + GUIInput* m_pGUIInput; // The control manager which holds all the gui elements for the dialog boxes. Owned - GUIControlManager *m_pGUIController; + GUIControlManager* m_pGUIController; - GUICollectionBox *m_pRootBox; - GUICollectionBox *m_pPlayerSetupBox; + GUICollectionBox* m_pRootBox; + GUICollectionBox* m_pPlayerSetupBox; // Activity selection screen controls - GUIComboBox *m_pActivitySelect; - GUIComboBox *m_pSceneSelect; - GUILabel *m_pDifficultyLabel; - GUISlider *m_pDifficultySlider; - - GUICollectionBox *m_aapPlayerBoxes[PLAYERCOLUMNCOUNT][TEAMROWCOUNT]; - GUICollectionBox *m_apTeamBoxes[TEAMROWCOUNT]; - GUILabel *m_apTeamNameLabels[TEAMROWCOUNT]; - GUILabel *m_pStartErrorLabel; - GUILabel *m_pCPULockLabel; + GUIComboBox* m_pActivitySelect; + GUIComboBox* m_pSceneSelect; + GUILabel* m_pDifficultyLabel; + GUISlider* m_pDifficultySlider; + + GUICollectionBox* m_aapPlayerBoxes[PLAYERCOLUMNCOUNT][TEAMROWCOUNT]; + GUICollectionBox* m_apTeamBoxes[TEAMROWCOUNT]; + GUILabel* m_apTeamNameLabels[TEAMROWCOUNT]; + GUILabel* m_pStartErrorLabel; + GUILabel* m_pCPULockLabel; // Which team the CPU is locked to, if any int m_LockedCPUTeam; - //Tech selection combos - GUIComboBox *m_apTeamTechSelect[Teams::MaxTeamCount]; + // Tech selection combos + GUIComboBox* m_apTeamTechSelect[Teams::MaxTeamCount]; // AI skill selection - GUISlider *m_apTeamAISkillSlider[Teams::MaxTeamCount]; - GUILabel *m_apTeamAISkillLabel[Teams::MaxTeamCount]; + GUISlider* m_apTeamAISkillSlider[Teams::MaxTeamCount]; + GUILabel* m_apTeamAISkillLabel[Teams::MaxTeamCount]; - GUILabel *m_pGoldLabel; - GUISlider *m_pGoldSlider; - GUICheckbox *m_pFogOfWarCheckbox; - GUICheckbox *m_pRequireClearPathToOrbitCheckbox; - GUICheckbox *m_pDeployUnitsCheckbox; + GUILabel* m_pGoldLabel; + GUISlider* m_pGoldSlider; + GUICheckbox* m_pFogOfWarCheckbox; + GUICheckbox* m_pRequireClearPathToOrbitCheckbox; + GUICheckbox* m_pDeployUnitsCheckbox; - const Icon *m_apPlayerIcons[c_MaxClients]; + const Icon* m_apPlayerIcons[c_MaxClients]; - GUILabel * m_apPlayerNameLabel[c_MaxClients]; + GUILabel* m_apPlayerNameLabel[c_MaxClients]; BITMAP* m_pUIDrawBitmap; - BITMAP * m_pCursor; + BITMAP* m_pCursor; - BITMAP *m_pScenePreviewBitmap; - BITMAP *m_pDefaultPreviewBitmap; + BITMAP* m_pScenePreviewBitmap; + BITMAP* m_pDefaultPreviewBitmap; - GUIButton *m_pStartScenarioButton; + GUIButton* m_pStartScenarioButton; // The scene preset currently selected, NOT OWNED - const Scene *m_pSelectedScene; + const Scene* m_pSelectedScene; // The current set of Scenes being displayed - not owned, nor are the scenes - std::list *m_pScenes; + std::list* m_pScenes; // The map of Activity:ies, and the Scene:s compatible with each, neither of which are owned here - std::map > m_Activities; + std::map> m_Activities; ////////////////////////////////////////////////////////////////////////////////////////// // Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Source/Activities/SceneEditor.cpp b/Source/Activities/SceneEditor.cpp index 1bf0155729..aa26146992 100644 --- a/Source/Activities/SceneEditor.cpp +++ b/Source/Activities/SceneEditor.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -42,838 +41,750 @@ namespace RTE { -ConcreteClassInfo(SceneEditor, EditorActivity, 0); + ConcreteClassInfo(SceneEditor, EditorActivity, 0); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this SceneEditor, effectively + // resetting the members of this abstraction level only. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SceneEditor, effectively -// resetting the members of this abstraction level only. + void SceneEditor::Clear() { + m_pEditorGUI = 0; + m_pNewTerrainCombo = 0; + m_pNewBG1Combo = 0; + m_pNewBG2Combo = 0; + m_pNewBG3Combo = 0; + } -void SceneEditor::Clear() -{ - m_pEditorGUI = 0; - m_pNewTerrainCombo = 0; - m_pNewBG1Combo = 0; - m_pNewBG2Combo = 0; - m_pNewBG3Combo = 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the SceneEditor object ready for use. + int SceneEditor::Create() { + if (EditorActivity::Create() < 0) + return -1; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneEditor object ready for use. + return 0; + } -int SceneEditor::Create() -{ - if (EditorActivity::Create() < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a SceneEditor to be identical to another, by deep copy. + int SceneEditor::Create(const SceneEditor& reference) { + if (EditorActivity::Create(reference) < 0) + return -1; - return 0; -} + if (m_Description.empty()) + m_Description = "Edit this Scene, including placement of all terrain objects and movable objects, AI blueprints, etc."; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a SceneEditor to be identical to another, by deep copy. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int SceneEditor::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); + /* + MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); + MatchProperty("Difficulty", { reader >> m_Difficulty; }); + MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); + */ + EndPropertyList; + } -int SceneEditor::Create(const SceneEditor &reference) -{ - if (EditorActivity::Create(reference) < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this SceneEditor with a Writer for + // later recreation with Create(Reader &reader); - if (m_Description.empty()) - m_Description = "Edit this Scene, including placement of all terrain objects and movable objects, AI blueprints, etc."; + int SceneEditor::Save(Writer& writer) const { + EditorActivity::Save(writer); + return 0; + } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneEditor object. + void SceneEditor::Destroy(bool notInherited) { + delete m_pEditorGUI; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int SceneEditor::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); -/* - MatchProperty("CPUTeam", { reader >> m_CPUTeam; }); - MatchProperty("Difficulty", { reader >> m_Difficulty; }); - MatchProperty("DeliveryDelay", { reader >> m_DeliveryDelay; }); -*/ - EndPropertyList; -} + if (!notInherited) + EditorActivity::Destroy(); + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts this. Creates all the data etc necessary to start + // the activity. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this SceneEditor with a Writer for -// later recreation with Create(Reader &reader); + int SceneEditor::Start() { + int error = EditorActivity::Start(); -int SceneEditor::Save(Writer &writer) const { - EditorActivity::Save(writer); - return 0; -} + ////////////////////////////////////////////// + // Allocate and (re)create the Editor GUI + if (m_pEditorGUI) + m_pEditorGUI->Destroy(); + else + m_pEditorGUI = new SceneEditorGUI; + m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneEditor object. + ////////////////////////////////////////////////////////////// + // Hooking up directly to the controls defined in the GUI ini -void SceneEditor::Destroy(bool notInherited) -{ - delete m_pEditorGUI; + m_pGUIController->Load("Base.rte/GUIs/SceneEditorGUI.ini"); - if (!notInherited) - EditorActivity::Destroy(); - Clear(); -} + // Resize the invisible root container so it matches the screen rez + GUICollectionBox* pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); + if (pRootBox) + pRootBox->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of + if (!m_pNewDialogBox) { + m_pNewDialogBox = dynamic_cast(m_pGUIController->GetControl("NewDialogBox")); + // m_pNewDialogBox->SetDrawType(GUICollectionBox::Color); + m_pNewDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pNewDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pNewDialogBox->GetHeight() / 2)); + m_pNewDialogBox->SetVisible(false); + } + m_pNewModuleCombo = dynamic_cast(m_pGUIController->GetControl("NewModuleCB")); + if (g_SettingsMan.AllowSavingToBase()) + m_pNewModuleCombo->SetEnabled(true); + else + m_pNewModuleCombo->SetEnabled(false); + m_pNewTerrainCombo = dynamic_cast(m_pGUIController->GetControl("NewTerrainCB")); + m_pNewBG1Combo = dynamic_cast(m_pGUIController->GetControl("NewBG1CB")); + m_pNewBG2Combo = dynamic_cast(m_pGUIController->GetControl("NewBG2CB")); + m_pNewBG3Combo = dynamic_cast(m_pGUIController->GetControl("NewBG3CB")); + m_pNewButton = dynamic_cast(m_pGUIController->GetControl("NewSceneButton")); + m_pNewCancel = dynamic_cast(m_pGUIController->GetControl("NewCancelButton")); + + // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of + if (!m_pLoadDialogBox) { + m_pLoadDialogBox = dynamic_cast(m_pGUIController->GetControl("LoadDialogBox")); + // m_pLoadDialogBox->SetDrawType(GUICollectionBox::Color); + m_pLoadDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pLoadDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pLoadDialogBox->GetHeight() / 2)); + m_pLoadDialogBox->SetVisible(false); + } + m_pLoadNameCombo = dynamic_cast(m_pGUIController->GetControl("LoadSceneCB")); + m_pLoadNameCombo->SetDropHeight(std::min(m_pLoadNameCombo->GetDropHeight(), g_WindowMan.GetResY() / 2)); + m_pLoadDialogBox->SetSize(m_pLoadDialogBox->GetWidth(), m_pLoadDialogBox->GetHeight() + m_pLoadNameCombo->GetDropHeight()); // Make sure the dropdown can fit, no matter how tall it is. + m_pLoadToNewButton = dynamic_cast(m_pGUIController->GetControl("LoadToNewButton")); + m_pLoadButton = dynamic_cast(m_pGUIController->GetControl("LoadSceneButton")); + m_pLoadCancel = dynamic_cast(m_pGUIController->GetControl("LoadCancelButton")); + + if (!m_pSaveDialogBox) { + m_pSaveDialogBox = dynamic_cast(m_pGUIController->GetControl("SaveDialogBox")); + + // Set the background image of the parent collection box + // ContentFile backgroundFile("Base.rte/GUIs/BuyMenuBackground.png"); + // m_pSaveDialogBox->SetDrawImage(new AllegroBitmap(backgroundFile.GetAsBitmap())); + // m_pSaveDialogBox->SetDrawBackground(true); + // m_pSaveDialogBox->SetDrawType(GUICollectionBox::Image); + // m_pSaveDialogBox->SetDrawType(GUICollectionBox::Color); + m_pSaveDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pSaveDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pSaveDialogBox->GetHeight() / 2)); + m_pSaveDialogBox->SetVisible(false); + } + m_pSaveNameBox = dynamic_cast(m_pGUIController->GetControl("SaveSceneNameTB")); + m_pSaveModuleLabel = dynamic_cast(m_pGUIController->GetControl("SaveModuleLabel")); + m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveSceneButton")); + m_pSaveCancel = dynamic_cast(m_pGUIController->GetControl("SaveCancelButton")); + + if (!m_pChangesDialogBox) { + m_pChangesDialogBox = dynamic_cast(m_pGUIController->GetControl("ChangesDialogBox")); + m_pChangesDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pChangesDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pChangesDialogBox->GetHeight() / 2)); + m_pChangesDialogBox->SetVisible(false); + } + m_pChangesNameLabel = dynamic_cast(m_pGUIController->GetControl("ChangesNameLabel")); + m_pChangesYesButton = dynamic_cast(m_pGUIController->GetControl("ChangesYesButton")); + m_pChangesNoButton = dynamic_cast(m_pGUIController->GetControl("ChangesNoButton")); + + if (!m_pOverwriteDialogBox) { + m_pOverwriteDialogBox = dynamic_cast(m_pGUIController->GetControl("OverwriteDialogBox")); + m_pOverwriteDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pOverwriteDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pOverwriteDialogBox->GetHeight() / 2)); + m_pOverwriteDialogBox->SetVisible(false); + } + m_pOverwriteNameLabel = dynamic_cast(m_pGUIController->GetControl("OverwriteNameLabel")); + m_pOverwriteYesButton = dynamic_cast(m_pGUIController->GetControl("OverwriteYesButton")); + m_pOverwriteNoButton = dynamic_cast(m_pGUIController->GetControl("OverwriteNoButton")); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts this. Creates all the data etc necessary to start -// the activity. - -int SceneEditor::Start() -{ - int error = EditorActivity::Start(); - - ////////////////////////////////////////////// - // Allocate and (re)create the Editor GUI - - if (m_pEditorGUI) - m_pEditorGUI->Destroy(); - else - m_pEditorGUI = new SceneEditorGUI; - m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT); - - ////////////////////////////////////////////////////////////// - // Hooking up directly to the controls defined in the GUI ini - - m_pGUIController->Load("Base.rte/GUIs/SceneEditorGUI.ini"); - - // Resize the invisible root container so it matches the screen rez - GUICollectionBox *pRootBox = dynamic_cast(m_pGUIController->GetControl("base")); - if (pRootBox) - pRootBox->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); - - // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of - if (!m_pNewDialogBox) - { - m_pNewDialogBox = dynamic_cast(m_pGUIController->GetControl("NewDialogBox")); -// m_pNewDialogBox->SetDrawType(GUICollectionBox::Color); - m_pNewDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pNewDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pNewDialogBox->GetHeight() / 2)); - m_pNewDialogBox->SetVisible(false); - } - m_pNewModuleCombo = dynamic_cast(m_pGUIController->GetControl("NewModuleCB")); - if (g_SettingsMan.AllowSavingToBase()) - m_pNewModuleCombo->SetEnabled(true); - else - m_pNewModuleCombo->SetEnabled(false); - m_pNewTerrainCombo = dynamic_cast(m_pGUIController->GetControl("NewTerrainCB")); - m_pNewBG1Combo = dynamic_cast(m_pGUIController->GetControl("NewBG1CB")); - m_pNewBG2Combo = dynamic_cast(m_pGUIController->GetControl("NewBG2CB")); - m_pNewBG3Combo = dynamic_cast(m_pGUIController->GetControl("NewBG3CB")); - m_pNewButton = dynamic_cast(m_pGUIController->GetControl("NewSceneButton")); - m_pNewCancel = dynamic_cast(m_pGUIController->GetControl("NewCancelButton")); - - // Make sure we have convenient points to the containing GUI dialog boxes that we will manipulate the positions of - if (!m_pLoadDialogBox) - { - m_pLoadDialogBox = dynamic_cast(m_pGUIController->GetControl("LoadDialogBox")); -// m_pLoadDialogBox->SetDrawType(GUICollectionBox::Color); - m_pLoadDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pLoadDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pLoadDialogBox->GetHeight() / 2)); - m_pLoadDialogBox->SetVisible(false); - } - m_pLoadNameCombo = dynamic_cast(m_pGUIController->GetControl("LoadSceneCB")); - m_pLoadNameCombo->SetDropHeight(std::min(m_pLoadNameCombo->GetDropHeight(), g_WindowMan.GetResY() / 2)); - m_pLoadDialogBox->SetSize(m_pLoadDialogBox->GetWidth(), m_pLoadDialogBox->GetHeight() + m_pLoadNameCombo->GetDropHeight()); // Make sure the dropdown can fit, no matter how tall it is. - m_pLoadToNewButton = dynamic_cast(m_pGUIController->GetControl("LoadToNewButton")); - m_pLoadButton = dynamic_cast(m_pGUIController->GetControl("LoadSceneButton")); - m_pLoadCancel = dynamic_cast(m_pGUIController->GetControl("LoadCancelButton")); - - if (!m_pSaveDialogBox) - { - m_pSaveDialogBox = dynamic_cast(m_pGUIController->GetControl("SaveDialogBox")); - - // Set the background image of the parent collection box -// ContentFile backgroundFile("Base.rte/GUIs/BuyMenuBackground.png"); -// m_pSaveDialogBox->SetDrawImage(new AllegroBitmap(backgroundFile.GetAsBitmap())); -// m_pSaveDialogBox->SetDrawBackground(true); -// m_pSaveDialogBox->SetDrawType(GUICollectionBox::Image); -// m_pSaveDialogBox->SetDrawType(GUICollectionBox::Color); - m_pSaveDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pSaveDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pSaveDialogBox->GetHeight() / 2)); - m_pSaveDialogBox->SetVisible(false); - } - m_pSaveNameBox = dynamic_cast(m_pGUIController->GetControl("SaveSceneNameTB")); - m_pSaveModuleLabel = dynamic_cast(m_pGUIController->GetControl("SaveModuleLabel")); - m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveSceneButton")); - m_pSaveCancel = dynamic_cast(m_pGUIController->GetControl("SaveCancelButton")); - - if (!m_pChangesDialogBox) - { - m_pChangesDialogBox = dynamic_cast(m_pGUIController->GetControl("ChangesDialogBox")); - m_pChangesDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pChangesDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pChangesDialogBox->GetHeight() / 2)); - m_pChangesDialogBox->SetVisible(false); - } - m_pChangesNameLabel = dynamic_cast(m_pGUIController->GetControl("ChangesNameLabel")); - m_pChangesYesButton = dynamic_cast(m_pGUIController->GetControl("ChangesYesButton")); - m_pChangesNoButton = dynamic_cast(m_pGUIController->GetControl("ChangesNoButton")); - - if (!m_pOverwriteDialogBox) - { - m_pOverwriteDialogBox = dynamic_cast(m_pGUIController->GetControl("OverwriteDialogBox")); - m_pOverwriteDialogBox->SetPositionAbs((g_FrameMan.GetPlayerScreenWidth() / 2) - (m_pOverwriteDialogBox->GetWidth() / 2), (g_FrameMan.GetPlayerScreenHeight() / 2) - (m_pOverwriteDialogBox->GetHeight() / 2)); - m_pOverwriteDialogBox->SetVisible(false); - } - m_pOverwriteNameLabel = dynamic_cast(m_pGUIController->GetControl("OverwriteNameLabel")); - m_pOverwriteYesButton = dynamic_cast(m_pGUIController->GetControl("OverwriteYesButton")); - m_pOverwriteNoButton = dynamic_cast(m_pGUIController->GetControl("OverwriteNoButton")); - - return error; -} + return error; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. + void SceneEditor::SetPaused(bool pause) { + // Override the pause + m_Paused = false; + } -void SceneEditor::SetPaused(bool pause) -{ - // Override the pause - m_Paused = false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + void SceneEditor::End() { + EditorActivity::End(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. + m_ActivityState = ActivityState::Over; + } -void SceneEditor::End() -{ - EditorActivity::End(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this SceneEditor. Supposed to be done every frame + // before drawing. + + void SceneEditor::Update() { + EditorActivity::Update(); + + if (!g_SceneMan.GetScene()) + return; + + // Update the loaded objects of the loaded scene so they look right + g_SceneMan.GetScene()->UpdatePlacedObjects(Scene::PLACEONLOAD); + + // All dialog boxes are gone and we're editing the scene + if (m_EditorMode == EditorActivity::EDITINGOBJECT) { + if (m_ModeChange) { + // Open the picker depending on whetehr there's somehting in the cursor hand or not + m_pEditorGUI->SetEditorGUIMode(m_pEditorGUI->GetCurrentObject() ? SceneEditorGUI::ADDINGOBJECT : SceneEditorGUI::PICKINGOBJECT); + // Hide the cursor for this layer of interface + m_pGUIController->EnableMouse(false); + m_ModeChange = false; + } + g_UInputMan.DisableKeys(false); + } + // We are doing something int he dialog boxes, so don't do anything in the editor interface + else + m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); + + ///////////////////////////////////////////////////// + // Update the editor interface + + m_pEditorGUI->Update(); + + // Any edits made, dirtying the scene? + m_NeedSave = m_pEditorGUI->EditMade() || m_NeedSave; + + // Get any mode change commands that the user gave the Editor GUI + if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorNew && m_EditorMode != NEWDIALOG) { + m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::NEWDIALOG; + m_ModeChange = true; + } else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorLoad && m_EditorMode != LOADDIALOG) { + m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::LOADDIALOG; + m_ModeChange = true; + } else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorSave && m_EditorMode != SAVEDIALOG) { + m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::SAVEDIALOG; + m_ModeChange = true; + } + // Test the scene by starting a Skirmish Defense with it, after saving + else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorDone || m_EditorMode == TESTINGOBJECT) { + m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); + + if (m_NeedSave) { + m_PreviousMode = EditorActivity::TESTINGOBJECT; + m_EditorMode = EditorActivity::CHANGESDIALOG; + m_ModeChange = true; + /* + if (m_HasEverBeenSaved) + SaveScene(g_SceneMan.GetScene()->GetPresetName()); + else + { + m_PreviousMode = TESTINGOBJECT; + m_EditorMode = SAVEDIALOG; + m_ModeChange = true; + } + */ + } else { + g_SceneMan.SetSceneToLoad(g_SceneMan.GetScene()->GetPresetName(), Scene::PLACEONLOAD); + + const Activity* pActivityPreset = dynamic_cast(g_PresetMan.GetEntityPreset("GAScripted", "Skirmish Defense")); + Activity* pActivity = dynamic_cast(pActivityPreset->Clone()); + GameActivity* pTestGame = dynamic_cast(pActivity); + RTEAssert(pTestGame, "Couldn't find the \"Skirmish Defense\" GAScripted Activity! Has it been defined?"); + pTestGame->SetTeamOfPlayer(0, 0); + pTestGame->SetCPUTeam(1); + pTestGame->SetStartingGold(10000); + pTestGame->SetFogOfWarEnabled(false); + pTestGame->SetDifficulty(DifficultySetting::MediumDifficulty); + g_ActivityMan.SetStartActivity(pTestGame); + g_ActivityMan.SetRestartActivity(); + } + } + //////////////////////////////////////////////////////// + // Handle events for mouse input on the controls + + GUIEvent anEvent; + while (m_pGUIController->GetEvent(&anEvent)) { + // If we're not supposed to have mouse control, then ignore these messages + // Uh this is not right, editor always has mouse control so far + // if (!m_PlayerController[0].IsMouseControlled()) + // break; + + if (anEvent.GetType() == GUIEvent::Command) { + ////////////////////////////////////////////////////////// + // NEW button pressed; create a new scene + + if (anEvent.GetControl() == m_pNewButton) { + // Get the selected Module + GUIListPanel::Item* pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) { + m_ModuleSpaceID = g_PresetMan.GetModuleID(pItem->m_Name); + + // Allocate Scene + Scene* pNewScene = new Scene(); + // Get the selected Terrain and create the Scene using it + pItem = m_pNewTerrainCombo->GetItem(m_pNewTerrainCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) { + SLTerrain* pNewTerrain = dynamic_cast(g_PresetMan.GetEntityPreset("SLTerrain", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewTerrain, "No SLTerrain of that name defined!"); + pNewScene->Create(pNewTerrain); + } + // Add specified scene layers + pItem = m_pNewBG1Combo->GetItem(m_pNewBG1Combo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) { + SLBackground* pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SLBackground", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SLBackground of the name set as BG1 is defined!"); + pNewScene->GetBackLayers().push_back(pNewLayer); + } + pItem = m_pNewBG2Combo->GetItem(m_pNewBG2Combo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) { + SLBackground* pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SLBackground", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SLBackground of the name set as BG2 is defined!"); + pNewScene->GetBackLayers().push_back(pNewLayer); + } + pItem = m_pNewBG3Combo->GetItem(m_pNewBG3Combo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) { + SLBackground* pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SLBackground", pItem->m_Name, m_ModuleSpaceID)->Clone()); + RTEAssert(pNewLayer, "No SLBackground of the name set as BG3 is defined!"); + pNewScene->GetBackLayers().push_back(pNewLayer); + } - m_ActivityState = ActivityState::Over; -} + // Make random planet coord's for this scene + float angle = RandomNum(0.0F, c_TwoPI); + Vector pos = Vector((int)(150 * cos(angle)), (int)(150 * sin(angle))); + pNewScene->SetLocation(pos); + // Actually load the scene's data and set it up as the current scene + g_SceneMan.LoadScene(pNewScene, Scene::PLACEONLOAD); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this SceneEditor. Supposed to be done every frame -// before drawing. - -void SceneEditor::Update() -{ - EditorActivity::Update(); - - if (!g_SceneMan.GetScene()) - return; - - // Update the loaded objects of the loaded scene so they look right - g_SceneMan.GetScene()->UpdatePlacedObjects(Scene::PLACEONLOAD); - - // All dialog boxes are gone and we're editing the scene - if (m_EditorMode == EditorActivity::EDITINGOBJECT) - { - if (m_ModeChange) - { - // Open the picker depending on whetehr there's somehting in the cursor hand or not - m_pEditorGUI->SetEditorGUIMode(m_pEditorGUI->GetCurrentObject() ? SceneEditorGUI::ADDINGOBJECT : SceneEditorGUI::PICKINGOBJECT); - // Hide the cursor for this layer of interface - m_pGUIController->EnableMouse(false); - m_ModeChange = false; - } - g_UInputMan.DisableKeys(false); - } - // We are doing something int he dialog boxes, so don't do anything in the editor interface - else - m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); - - - ///////////////////////////////////////////////////// - // Update the editor interface - - m_pEditorGUI->Update(); - - // Any edits made, dirtying the scene? - m_NeedSave = m_pEditorGUI->EditMade() || m_NeedSave; - - // Get any mode change commands that the user gave the Editor GUI - if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorNew && m_EditorMode != NEWDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::NEWDIALOG; - m_ModeChange = true; - } - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorLoad && m_EditorMode != LOADDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::LOADDIALOG; - m_ModeChange = true; - } - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorSave && m_EditorMode != SAVEDIALOG) - { - m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::SAVEDIALOG; - m_ModeChange = true; - } - // Test the scene by starting a Skirmish Defense with it, after saving - else if (m_pEditorGUI->GetActivatedPieSlice() == PieSlice::SliceType::EditorDone || m_EditorMode == TESTINGOBJECT) - { - m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); - - if (m_NeedSave) - { - m_PreviousMode = EditorActivity::TESTINGOBJECT; - m_EditorMode = EditorActivity::CHANGESDIALOG; - m_ModeChange = true; -/* - if (m_HasEverBeenSaved) - SaveScene(g_SceneMan.GetScene()->GetPresetName()); - else - { - m_PreviousMode = TESTINGOBJECT; - m_EditorMode = SAVEDIALOG; - m_ModeChange = true; - } -*/ - } - else - { - g_SceneMan.SetSceneToLoad(g_SceneMan.GetScene()->GetPresetName(), Scene::PLACEONLOAD); - - const Activity *pActivityPreset = dynamic_cast(g_PresetMan.GetEntityPreset("GAScripted", "Skirmish Defense")); - Activity * pActivity = dynamic_cast(pActivityPreset->Clone()); - GameActivity *pTestGame = dynamic_cast(pActivity); - RTEAssert(pTestGame, "Couldn't find the \"Skirmish Defense\" GAScripted Activity! Has it been defined?"); - pTestGame->SetTeamOfPlayer(0, 0); - pTestGame->SetCPUTeam(1); - pTestGame->SetStartingGold(10000); - pTestGame->SetFogOfWarEnabled(false); - pTestGame->SetDifficulty(DifficultySetting::MediumDifficulty); - g_ActivityMan.SetStartActivity(pTestGame); - g_ActivityMan.SetRestartActivity(); - } - } - - //////////////////////////////////////////////////////// - // Handle events for mouse input on the controls - - GUIEvent anEvent; - while(m_pGUIController->GetEvent(&anEvent)) - { - // If we're not supposed to have mouse control, then ignore these messages -// Uh this is not right, editor always has mouse control so far -// if (!m_PlayerController[0].IsMouseControlled()) -// break; - - if (anEvent.GetType() == GUIEvent::Command) - { - ////////////////////////////////////////////////////////// - // NEW button pressed; create a new scene - - if (anEvent.GetControl() == m_pNewButton) - { - // Get the selected Module - GUIListPanel::Item *pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - m_ModuleSpaceID = g_PresetMan.GetModuleID(pItem->m_Name); - - // Allocate Scene - Scene *pNewScene = new Scene(); - // Get the selected Terrain and create the Scene using it - pItem = m_pNewTerrainCombo->GetItem(m_pNewTerrainCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SLTerrain *pNewTerrain = dynamic_cast(g_PresetMan.GetEntityPreset("SLTerrain", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewTerrain, "No SLTerrain of that name defined!"); - pNewScene->Create(pNewTerrain); - } - - // Add specified scene layers - pItem = m_pNewBG1Combo->GetItem(m_pNewBG1Combo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SLBackground *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SLBackground", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SLBackground of the name set as BG1 is defined!"); - pNewScene->GetBackLayers().push_back(pNewLayer); - } - pItem = m_pNewBG2Combo->GetItem(m_pNewBG2Combo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SLBackground *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SLBackground", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SLBackground of the name set as BG2 is defined!"); - pNewScene->GetBackLayers().push_back(pNewLayer); - } - pItem = m_pNewBG3Combo->GetItem(m_pNewBG3Combo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - SLBackground *pNewLayer = dynamic_cast(g_PresetMan.GetEntityPreset("SLBackground", pItem->m_Name, m_ModuleSpaceID)->Clone()); - RTEAssert(pNewLayer, "No SLBackground of the name set as BG3 is defined!"); - pNewScene->GetBackLayers().push_back(pNewLayer); - } - - // Make random planet coord's for this scene - float angle = RandomNum(0.0F, c_TwoPI); - Vector pos = Vector((int)(150 * cos(angle)), (int)(150 * sin(angle))); - pNewScene->SetLocation(pos); - - // Actually load the scene's data and set it up as the current scene - g_SceneMan.LoadScene(pNewScene, Scene::PLACEONLOAD); - - // Reset the rest of the editor GUI - m_pEditorGUI->Destroy(); - if (m_ModuleSpaceID == g_PresetMan.GetModuleID(c_UserScenesModuleName)) - m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, -1); - else - m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, m_ModuleSpaceID); - } - - m_NeedSave = false; - m_HasEverBeenSaved = false; - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - - ////////////////////////////////////////////////////////// - // LOAD TO NEW button pressed; go from the load to the new dialog - - if (anEvent.GetControl() == m_pLoadToNewButton) - { - m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); - m_EditorMode = EditorActivity::NEWDIALOG; - m_ModeChange = true; - } - - ////////////////////////////////////////////////////////// - // LOAD button pressed; load the selected Scene - - if (anEvent.GetControl() == m_pLoadButton) - { - GUIListPanel::Item *pItem = m_pLoadNameCombo->GetItem(m_pLoadNameCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - { - // Attempt to load the scene, without applying its placed objects - g_SceneMan.SetSceneToLoad(pItem->m_Name, false); - g_SceneMan.LoadScene(); - // Get the Module ID that the scene exists in, so we can limit the picker to only show objects from that DataModule space - if (g_SceneMan.GetScene()) - { - m_ModuleSpaceID = g_SceneMan.GetScene()->GetModuleID(); - RTEAssert(m_ModuleSpaceID >= 0, "Loaded Scene's DataModule ID is negative? Should always be a specific one.."); - m_pEditorGUI->Destroy(); + // Reset the rest of the editor GUI + m_pEditorGUI->Destroy(); if (m_ModuleSpaceID == g_PresetMan.GetModuleID(c_UserScenesModuleName)) m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, -1); else m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, m_ModuleSpaceID); -// TODO: Should read in all the already placed objects in the loaded scene and have them appear int he editor instead - } - } - m_NeedSave = false; - m_HasEverBeenSaved = true; - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } - - ////////////////////////////////////////////////////////// - // SAVE button pressed; save the selected Scene - - if (anEvent.GetControl() == m_pSaveButton) - { - if (!m_pSaveNameBox->GetText().empty()) - { - // Save the scene to the name specified in the text box - if (SaveScene(m_pSaveNameBox->GetText())) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the scene - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - // Should really leave dialog box open? - else - { - ; - } - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes YES pressed - - if (anEvent.GetControl() == m_pChangesYesButton) - { - if (m_HasEverBeenSaved) - { - if (SaveScene(g_SceneMan.GetScene()->GetPresetName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after save dialog is done, may have been on the way to test the scene - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - } - // Open the save scene dialog to ask user where to save it then - else - { - m_PreviousMode = m_PreviousMode; - m_EditorMode = EditorActivity::SAVEDIALOG; - m_ModeChange = true; - } - } - - /////////////////////////////////////////////////////////////// - // Save Changes NO pressed - - if (anEvent.GetControl() == m_pChangesNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - m_NeedSave = false; - } - - /////////////////////////////////////////////////////////////// - // Overwrite Scene YES pressed - - if (anEvent.GetControl() == m_pOverwriteYesButton) - { - // Force overwrite - if (SaveScene(g_SceneMan.GetScene()->GetPresetName(), true)) - { - // Close the dialog box on success - m_NeedSave = false; - m_HasEverBeenSaved = true; - // Go back to previous mode after overwrite dialog is done, may have been on the way to test the scene - m_EditorMode = m_PreviousMode != EditorActivity::SAVEDIALOG ? m_PreviousMode : EditorActivity::EDITINGOBJECT; - m_ModeChange = true; - } -// TODO: Show overwrite error? - } - - /////////////////////////////////////////////////////////////// - // Overwrite Scene NO pressed - - if (anEvent.GetControl() == m_pOverwriteNoButton) - { - // Just go back to previous mode - m_EditorMode = m_PreviousMode; - m_ModeChange = true; - } - - /////////////////////////////////////////////////////////////// - // CANCEL button pressed; exit any active dialog box - - if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) - { - // Don't allow canceling out of diags if we're still in the special "Editor Scene", don't allow users to edit it! - // Just exit the whole editor into the main menu - if (g_SceneMan.GetScene()->GetPresetName() == "Editor Scene") - { - g_ActivityMan.PauseActivity(); - } - // Just do normal cancel of the dialog and go back to editing - else - m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; - - m_ModeChange = true; - } - } - - // Notifications - else if (anEvent.GetType() == GUIEvent::Notification) - { - /////////////////////////////////////// - // Clicks on the New Scene Module combo - - if (anEvent.GetControl() == m_pNewModuleCombo) - { - // Closed it, IE selected somehting - if(anEvent.GetMsg() == GUIComboBox::Closed) - UpdateNewDialog(); - } - } - } -} + } + m_NeedSave = false; + m_HasEverBeenSaved = false; + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. + ////////////////////////////////////////////////////////// + // LOAD TO NEW button pressed; go from the load to the new dialog -void SceneEditor::DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos, int which) -{ - m_pEditorGUI->Draw(pTargetBitmap, targetPos); + if (anEvent.GetControl() == m_pLoadToNewButton) { + m_pEditorGUI->SetEditorGUIMode(SceneEditorGUI::INACTIVE); + m_EditorMode = EditorActivity::NEWDIALOG; + m_ModeChange = true; + } - EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); -} + ////////////////////////////////////////////////////////// + // LOAD button pressed; load the selected Scene + + if (anEvent.GetControl() == m_pLoadButton) { + GUIListPanel::Item* pItem = m_pLoadNameCombo->GetItem(m_pLoadNameCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) { + // Attempt to load the scene, without applying its placed objects + g_SceneMan.SetSceneToLoad(pItem->m_Name, false); + g_SceneMan.LoadScene(); + // Get the Module ID that the scene exists in, so we can limit the picker to only show objects from that DataModule space + if (g_SceneMan.GetScene()) { + m_ModuleSpaceID = g_SceneMan.GetScene()->GetModuleID(); + RTEAssert(m_ModuleSpaceID >= 0, "Loaded Scene's DataModule ID is negative? Should always be a specific one.."); + m_pEditorGUI->Destroy(); + if (m_ModuleSpaceID == g_PresetMan.GetModuleID(c_UserScenesModuleName)) + m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, -1); + else + m_pEditorGUI->Create(&(m_PlayerController[0]), SceneEditorGUI::ONLOADEDIT, m_ModuleSpaceID); + // TODO: Should read in all the already placed objects in the loaded scene and have them appear int he editor instead + } + } + m_NeedSave = false; + m_HasEverBeenSaved = true; + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + ////////////////////////////////////////////////////////// + // SAVE button pressed; save the selected Scene + + if (anEvent.GetControl() == m_pSaveButton) { + if (!m_pSaveNameBox->GetText().empty()) { + // Save the scene to the name specified in the text box + if (SaveScene(m_pSaveNameBox->GetText())) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the scene + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + // Should really leave dialog box open? + else { + ; + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SceneEditor's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. + /////////////////////////////////////////////////////////////// + // Save Changes YES pressed + + if (anEvent.GetControl() == m_pChangesYesButton) { + if (m_HasEverBeenSaved) { + if (SaveScene(g_SceneMan.GetScene()->GetPresetName(), true)) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after save dialog is done, may have been on the way to test the scene + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } + } + // Open the save scene dialog to ask user where to save it then + else { + m_PreviousMode = m_PreviousMode; + m_EditorMode = EditorActivity::SAVEDIALOG; + m_ModeChange = true; + } + } -void SceneEditor::Draw(BITMAP* pTargetBitmap, const Vector &targetPos) -{ - EditorActivity::Draw(pTargetBitmap, targetPos); -} + /////////////////////////////////////////////////////////////// + // Save Changes NO pressed -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (anEvent.GetControl() == m_pChangesNoButton) { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + m_NeedSave = false; + } -bool SceneEditor::SaveScene(const std::string &saveAsName, bool forceOverwrite) { - Scene *editedScene = g_SceneMan.GetScene(); - editedScene->SetPresetName(saveAsName); + /////////////////////////////////////////////////////////////// + // Overwrite Scene YES pressed + + if (anEvent.GetControl() == m_pOverwriteYesButton) { + // Force overwrite + if (SaveScene(g_SceneMan.GetScene()->GetPresetName(), true)) { + // Close the dialog box on success + m_NeedSave = false; + m_HasEverBeenSaved = true; + // Go back to previous mode after overwrite dialog is done, may have been on the way to test the scene + m_EditorMode = m_PreviousMode != EditorActivity::SAVEDIALOG ? m_PreviousMode : EditorActivity::EDITINGOBJECT; + m_ModeChange = true; + } + // TODO: Show overwrite error? + } - std::string dataModuleName = g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName(); - bool savingToUserScenesModule = (dataModuleName == c_UserScenesModuleName); + /////////////////////////////////////////////////////////////// + // Overwrite Scene NO pressed - std::string dataModuleFullPath = g_PresetMan.GetFullModulePath(dataModuleName); - std::string sceneSavePath; - std::string previewSavePath; + if (anEvent.GetControl() == m_pOverwriteNoButton) { + // Just go back to previous mode + m_EditorMode = m_PreviousMode; + m_ModeChange = true; + } - if (savingToUserScenesModule) { - sceneSavePath = dataModuleFullPath + "/" + saveAsName + ".ini"; - previewSavePath = dataModuleFullPath + "/" + saveAsName + ".preview.png"; - } else { - sceneSavePath = dataModuleFullPath + "/Scenes/" + saveAsName + ".ini"; - previewSavePath = dataModuleFullPath + "/Scenes/" + saveAsName + ".preview.png"; - } + /////////////////////////////////////////////////////////////// + // CANCEL button pressed; exit any active dialog box - if (g_PresetMan.AddEntityPreset(editedScene, m_ModuleSpaceID, forceOverwrite, sceneSavePath)) { - if (Writer sceneWriter(sceneSavePath, false); !sceneWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + sceneSavePath + "\n\nTHE EDITED SCENE PRESET WAS NOT SAVED!!!"); - } else { - // TODO: Check if the ini file already exists, and then ask if overwrite. - sceneWriter.NewPropertyWithValue("AddScene", editedScene); - sceneWriter.EndWrite(); - - editedScene->SavePreview(previewSavePath); - m_HasEverBeenSaved = true; - - if (!savingToUserScenesModule) { - // First find/create a Scenes.ini file to include the new .ini into. - std::string scenesFilePath(dataModuleFullPath + "/Scenes.ini"); - bool scenesFileExists = System::PathExistsCaseSensitive(scenesFilePath); - - if (Writer scenesFileWriter(scenesFilePath, true); !scenesFileWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + scenesFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); - } else { - scenesFileWriter.NewPropertyWithValue("IncludeFile", sceneSavePath); - scenesFileWriter.EndWrite(); - - // Append to the end of the modules' Index.ini to include the newly created Scenes.ini next startup. - // If it's somehow already included without actually existing, it doesn't matter, the definitions will just bounce the second time. - if (!scenesFileExists) { - std::string indexFilePath = dataModuleFullPath + "/Index.ini"; - - if (Writer indexWriter(indexFilePath, true); !indexWriter.WriterOK()) { - RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + indexFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); - } else { - // Add extra tab since the DataModule has everything indented. - indexWriter.NewProperty("\tIncludeFile"); - indexWriter << scenesFilePath; - indexWriter.EndWrite(); - } + if (anEvent.GetControl() == m_pNewCancel || anEvent.GetControl() == m_pLoadCancel || anEvent.GetControl() == m_pSaveCancel) { + // Don't allow canceling out of diags if we're still in the special "Editor Scene", don't allow users to edit it! + // Just exit the whole editor into the main menu + if (g_SceneMan.GetScene()->GetPresetName() == "Editor Scene") { + g_ActivityMan.PauseActivity(); } + // Just do normal cancel of the dialog and go back to editing + else + m_EditorMode = m_PreviousMode = EditorActivity::EDITINGOBJECT; + + m_ModeChange = true; + } + } + + // Notifications + else if (anEvent.GetType() == GUIEvent::Notification) { + /////////////////////////////////////// + // Clicks on the New Scene Module combo + + if (anEvent.GetControl() == m_pNewModuleCombo) { + // Closed it, IE selected somehting + if (anEvent.GetMsg() == GUIComboBox::Closed) + UpdateNewDialog(); } } - return true; } - } else { - // Got to ask if we can overwrite the existing preset. - m_PreviousMode = EditorMode::SAVEDIALOG; - m_EditorMode = EditorMode::OVERWRITEDIALOG; - m_ModeChange = true; } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateNewDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New dialog box, populates its lists etc. + void SceneEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { + m_pEditorGUI->Draw(pTargetBitmap, targetPos); -void SceneEditor::UpdateNewDialog() -{ - int scenesIndex = 0; + EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); + } - // Only refill modules if empty - if (m_pNewModuleCombo->GetCount() <= 0) - { - for (int module = 0; module < g_PresetMan.GetTotalModuleCount(); ++module) - { - m_pNewModuleCombo->AddItem(g_PresetMan.GetDataModule(module)->GetFileName()); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this SceneEditor's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. - if (g_PresetMan.GetDataModule(module)->GetFileName() == c_UserScenesModuleName) - scenesIndex = m_pNewModuleCombo->GetCount() - 1; - } - - // Select the user scenes module - m_pNewModuleCombo->SetSelectedIndex(scenesIndex); - } - - // Get the ID of the module currently selected so we can limit the following boxes to only show stuff in that module - int selectedModuleID = -1; - GUIListPanel::Item *pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); - if (pItem && !pItem->m_Name.empty()) - selectedModuleID = g_PresetMan.GetModuleID(pItem->m_Name); - - // Refill Terrains - m_pNewTerrainCombo->ClearList(); - // Get the list of all read in terrains - std::list terrainList; - g_PresetMan.GetAllOfTypeInModuleSpace(terrainList, "SLTerrain", selectedModuleID); - // Go through the list and add their names to the combo box - for (std::list::iterator itr = terrainList.begin(); itr != terrainList.end(); ++itr) - { - if ((*itr)->GetPresetName() != "Editor Terrain" && - (*itr)->GetPresetName() != "Physics Test Terrain") - m_pNewTerrainCombo->AddItem((*itr)->GetPresetName()); + void SceneEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + EditorActivity::Draw(pTargetBitmap, targetPos); } - // Select the first one - m_pNewTerrainCombo->SetSelectedIndex(0); - - // Refill backdrops - m_pNewBG1Combo->SetText(""); - m_pNewBG2Combo->SetText(""); - m_pNewBG3Combo->SetText(""); - m_pNewBG1Combo->ClearList(); - m_pNewBG2Combo->ClearList(); - m_pNewBG3Combo->ClearList(); - - // Get the list of all read in NEAR background layers - std::list bgList; - g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Near Backdrops", "SLBackground", selectedModuleID); - // Go through the list and add their names to the combo box - for (std::list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) - m_pNewBG1Combo->AddItem((*itr)->GetPresetName()); - - // Get the list of all read in MID background layers - bgList.clear(); - g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Mid Backdrops", "SLBackground", selectedModuleID); - // Go through the list and add their names to the combo box - for (std::list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) - m_pNewBG2Combo->AddItem((*itr)->GetPresetName()); - - // Get the list of all read in FAR background layers - bgList.clear(); - g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Far Backdrops", "SLBackground", selectedModuleID); - // Go through the list and add their names to the combo box - for (std::list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) - m_pNewBG3Combo->AddItem((*itr)->GetPresetName()); - - // Select the first one for each - m_pNewBG1Combo->SetSelectedIndex(0); - m_pNewBG2Combo->SetSelectedIndex(0); - m_pNewBG3Combo->SetSelectedIndex(0); -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateLoadDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Load dialog box, populates its lists etc. + bool SceneEditor::SaveScene(const std::string& saveAsName, bool forceOverwrite) { + Scene* editedScene = g_SceneMan.GetScene(); + editedScene->SetPresetName(saveAsName); -void SceneEditor::UpdateLoadDialog() -{ - // Clear out the control - m_pLoadNameCombo->ClearList(); + std::string dataModuleName = g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName(); + bool savingToUserScenesModule = (dataModuleName == c_UserScenesModuleName); - // Get the list of all read in scenes - std::list sceneList; - g_PresetMan.GetAllOfType(sceneList, "Scene"); + std::string dataModuleFullPath = g_PresetMan.GetFullModulePath(dataModuleName); + std::string sceneSavePath; + std::string previewSavePath; - // Go through the list and add their names to the combo box - for (std::list::iterator itr = sceneList.begin(); itr != sceneList.end(); ++itr) - { - Scene * pScene = dynamic_cast(*itr); - if (pScene) - // Don't add the special "Editor Scene" or metascenes, users shouldn't be messing with them - if (pScene->GetPresetName() != "Editor Scene" && !pScene->IsMetagameInternal() && !pScene->IsSavedGameInternal() && (pScene->GetMetasceneParent() == "" || g_SettingsMan.ShowMetascenes())) - m_pLoadNameCombo->AddItem(pScene->GetPresetName()); - } + if (savingToUserScenesModule) { + sceneSavePath = dataModuleFullPath + "/" + saveAsName + ".ini"; + previewSavePath = dataModuleFullPath + "/" + saveAsName + ".preview.png"; + } else { + sceneSavePath = dataModuleFullPath + "/Scenes/" + saveAsName + ".ini"; + previewSavePath = dataModuleFullPath + "/Scenes/" + saveAsName + ".preview.png"; + } - // Select the first one - m_pLoadNameCombo->SetSelectedIndex(0); -} + if (g_PresetMan.AddEntityPreset(editedScene, m_ModuleSpaceID, forceOverwrite, sceneSavePath)) { + if (Writer sceneWriter(sceneSavePath, false); !sceneWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + sceneSavePath + "\n\nTHE EDITED SCENE PRESET WAS NOT SAVED!!!"); + } else { + // TODO: Check if the ini file already exists, and then ask if overwrite. + sceneWriter.NewPropertyWithValue("AddScene", editedScene); + sceneWriter.EndWrite(); + + editedScene->SavePreview(previewSavePath); + m_HasEverBeenSaved = true; + + if (!savingToUserScenesModule) { + // First find/create a Scenes.ini file to include the new .ini into. + std::string scenesFilePath(dataModuleFullPath + "/Scenes.ini"); + bool scenesFileExists = System::PathExistsCaseSensitive(scenesFilePath); + + if (Writer scenesFileWriter(scenesFilePath, true); !scenesFileWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + scenesFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); + } else { + scenesFileWriter.NewPropertyWithValue("IncludeFile", sceneSavePath); + scenesFileWriter.EndWrite(); + + // Append to the end of the modules' Index.ini to include the newly created Scenes.ini next startup. + // If it's somehow already included without actually existing, it doesn't matter, the definitions will just bounce the second time. + if (!scenesFileExists) { + std::string indexFilePath = dataModuleFullPath + "/Index.ini"; + + if (Writer indexWriter(indexFilePath, true); !indexWriter.WriterOK()) { + RTEError::ShowMessageBox("Failed to create Writer to path:\n\n" + indexFilePath + "\n\nThe edited Scene preset was saved but will not be loaded on next game start!\nPlease include the Scene preset manually!"); + } else { + // Add extra tab since the DataModule has everything indented. + indexWriter.NewProperty("\tIncludeFile"); + indexWriter << scenesFilePath; + indexWriter.EndWrite(); + } + } + } + } + return true; + } + } else { + // Got to ask if we can overwrite the existing preset. + m_PreviousMode = EditorMode::SAVEDIALOG; + m_EditorMode = EditorMode::OVERWRITEDIALOG; + m_ModeChange = true; + } + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateNewDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New dialog box, populates its lists etc. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateSaveDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save dialog box, populates its lists etc. + void SceneEditor::UpdateNewDialog() { + int scenesIndex = 0; -void SceneEditor::UpdateSaveDialog() -{ - m_pSaveNameBox->SetText((g_SceneMan.GetScene()->GetPresetName() == "None" || !m_HasEverBeenSaved) ? "New Scene" : g_SceneMan.GetScene()->GetPresetName()); + // Only refill modules if empty + if (m_pNewModuleCombo->GetCount() <= 0) { + for (int module = 0; module < g_PresetMan.GetTotalModuleCount(); ++module) { + m_pNewModuleCombo->AddItem(g_PresetMan.GetDataModule(module)->GetFileName()); - if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) - m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/"); - else - m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes"); -} + if (g_PresetMan.GetDataModule(module)->GetFileName() == c_UserScenesModuleName) + scenesIndex = m_pNewModuleCombo->GetCount() - 1; + } + // Select the user scenes module + m_pNewModuleCombo->SetSelectedIndex(scenesIndex); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChangesDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save Changes dialog box, populates its lists etc. + // Get the ID of the module currently selected so we can limit the following boxes to only show stuff in that module + int selectedModuleID = -1; + GUIListPanel::Item* pItem = m_pNewModuleCombo->GetItem(m_pNewModuleCombo->GetSelectedIndex()); + if (pItem && !pItem->m_Name.empty()) + selectedModuleID = g_PresetMan.GetModuleID(pItem->m_Name); + + // Refill Terrains + m_pNewTerrainCombo->ClearList(); + // Get the list of all read in terrains + std::list terrainList; + g_PresetMan.GetAllOfTypeInModuleSpace(terrainList, "SLTerrain", selectedModuleID); + // Go through the list and add their names to the combo box + for (std::list::iterator itr = terrainList.begin(); itr != terrainList.end(); ++itr) { + if ((*itr)->GetPresetName() != "Editor Terrain" && + (*itr)->GetPresetName() != "Physics Test Terrain") + m_pNewTerrainCombo->AddItem((*itr)->GetPresetName()); + } + // Select the first one + m_pNewTerrainCombo->SetSelectedIndex(0); + + // Refill backdrops + m_pNewBG1Combo->SetText(""); + m_pNewBG2Combo->SetText(""); + m_pNewBG3Combo->SetText(""); + m_pNewBG1Combo->ClearList(); + m_pNewBG2Combo->ClearList(); + m_pNewBG3Combo->ClearList(); + + // Get the list of all read in NEAR background layers + std::list bgList; + g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Near Backdrops", "SLBackground", selectedModuleID); + // Go through the list and add their names to the combo box + for (std::list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) + m_pNewBG1Combo->AddItem((*itr)->GetPresetName()); + + // Get the list of all read in MID background layers + bgList.clear(); + g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Mid Backdrops", "SLBackground", selectedModuleID); + // Go through the list and add their names to the combo box + for (std::list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) + m_pNewBG2Combo->AddItem((*itr)->GetPresetName()); + + // Get the list of all read in FAR background layers + bgList.clear(); + g_PresetMan.GetAllOfGroupInModuleSpace(bgList, "Far Backdrops", "SLBackground", selectedModuleID); + // Go through the list and add their names to the combo box + for (std::list::iterator itr = bgList.begin(); itr != bgList.end(); ++itr) + m_pNewBG3Combo->AddItem((*itr)->GetPresetName()); + + // Select the first one for each + m_pNewBG1Combo->SetSelectedIndex(0); + m_pNewBG2Combo->SetSelectedIndex(0); + m_pNewBG3Combo->SetSelectedIndex(0); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateLoadDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Load dialog box, populates its lists etc. + + void SceneEditor::UpdateLoadDialog() { + // Clear out the control + m_pLoadNameCombo->ClearList(); + + // Get the list of all read in scenes + std::list sceneList; + g_PresetMan.GetAllOfType(sceneList, "Scene"); + + // Go through the list and add their names to the combo box + for (std::list::iterator itr = sceneList.begin(); itr != sceneList.end(); ++itr) { + Scene* pScene = dynamic_cast(*itr); + if (pScene) + // Don't add the special "Editor Scene" or metascenes, users shouldn't be messing with them + if (pScene->GetPresetName() != "Editor Scene" && !pScene->IsMetagameInternal() && !pScene->IsSavedGameInternal() && (pScene->GetMetasceneParent() == "" || g_SettingsMan.ShowMetascenes())) + m_pLoadNameCombo->AddItem(pScene->GetPresetName()); + } + + // Select the first one + m_pLoadNameCombo->SetSelectedIndex(0); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateSaveDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save dialog box, populates its lists etc. + + void SceneEditor::UpdateSaveDialog() { + m_pSaveNameBox->SetText((g_SceneMan.GetScene()->GetPresetName() == "None" || !m_HasEverBeenSaved) ? "New Scene" : g_SceneMan.GetScene()->GetPresetName()); -void SceneEditor::UpdateChangesDialog() -{ - if (m_HasEverBeenSaved) - { - dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) - m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); + m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/"); else - m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); - } - else - { - dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Save your new Scene first?"); - m_pChangesNameLabel->SetText(""); - } -} + m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes"); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateChangesDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save Changes dialog box, populates its lists etc. + + void SceneEditor::UpdateChangesDialog() { + if (m_HasEverBeenSaved) { + dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); + if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) + m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); + else + m_pChangesNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); + } else { + dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Save your new Scene first?"); + m_pChangesNameLabel->SetText(""); + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateOverwriteDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Overwrite dialog box, populates its lists etc. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateOverwriteDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Overwrite dialog box, populates its lists etc. - -void SceneEditor::UpdateOverwriteDialog() -{ - if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) - m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); - else - m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); -} + void SceneEditor::UpdateOverwriteDialog() { + if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) + m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); + else + m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); + } } // namespace RTE \ No newline at end of file diff --git a/Source/Activities/SceneEditor.h b/Source/Activities/SceneEditor.h index 4930f0fa29..b9e1cb84d5 100644 --- a/Source/Activities/SceneEditor.h +++ b/Source/Activities/SceneEditor.h @@ -10,297 +10,270 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" -namespace RTE -{ - -class SceneEditorGUI; -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUITab; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUILabel; -class GUIComboBox; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: SceneEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activity for editing scenes. -// Parent(s): EditorActivity. -// Class history: 8/30/2007 SceneEditor created, inheriting directly from Activity. -// 9/17/2007 Spliced out and made to derive from EditorActivty - -class SceneEditor : public EditorActivity { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(SceneEditor); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: SceneEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a SceneEditor object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - SceneEditor() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~SceneEditor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a SceneEditor object before deletion -// from system memory. -// Arguments: None. - - ~SceneEditor() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneEditor object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a SceneEditor to be identical to another, by deep copy. -// Arguments: A reference to the SceneEditor to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const SceneEditor &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire SceneEditor, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); EditorActivity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneEditor object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Start -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Officially starts the game accroding to parameters previously set. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Start() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Pause -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pauses and unpauses the game. -// Arguments: Whether to pause the game or not. -// Return value: None. - - void SetPaused(bool pause = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: End -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces the current game's end. -// Arguments: None. -// Return value: None. - - void End() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this ActivityMan. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the currently active GUI of a screen to a BITMAP of choice. -// Arguments: A pointer to a screen-sized BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which screen's GUI to draw onto the bitmap. -// Return value: None. - - void DrawGUI(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ActivityMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. -// Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - /// - /// Saves the current Scene to an appropriate ini file, and asks user if they want to overwrite first if scene of this name exists. - /// - /// The name of the new Scene to be saved. - /// Whether to force any existing Scene of that name to be overwritten if it already exists. - /// Whether actually managed to save. Will return false both if a scene of this name already exists, or if other error. - bool SaveScene(const std::string &saveAsName, bool forceOverwrite = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateNewDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateNewDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateLoadDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Load dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateLoadDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateSaveDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateSaveDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateChangesDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Save Changes dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateChangesDialog() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: UpdateOverwriteDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Overwrite dialog box, populates its lists etc. -// Arguments: None. -// Return value: None. - - void UpdateOverwriteDialog() override; - - - // Member variables - static Entity::ClassInfo m_sClass; - - // The editor GUI - SceneEditorGUI *m_pEditorGUI; - - // The combobox which lists all the Terrain:s that can be loaded for a new scene - GUIComboBox *m_pNewTerrainCombo; - // The combobox which lists all the background SceneLayer:s that can be loaded for a new scene, near - GUIComboBox *m_pNewBG1Combo; - // The combobox which lists all the background SceneLayer:s that can be loaded for a new scene, mid - GUIComboBox *m_pNewBG2Combo; - // The combobox which lists all the background SceneLayer:s that can be loaded for a new scene, far/sky - GUIComboBox *m_pNewBG3Combo; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Activity, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class SceneEditorGUI; + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUITab; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUILabel; + class GUIComboBox; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: SceneEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activity for editing scenes. + // Parent(s): EditorActivity. + // Class history: 8/30/2007 SceneEditor created, inheriting directly from Activity. + // 9/17/2007 Spliced out and made to derive from EditorActivty + + class SceneEditor : public EditorActivity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(SceneEditor); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: SceneEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a SceneEditor object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + SceneEditor() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~SceneEditor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a SceneEditor object before deletion + // from system memory. + // Arguments: None. + + ~SceneEditor() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the SceneEditor object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a SceneEditor to be identical to another, by deep copy. + // Arguments: A reference to the SceneEditor to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const SceneEditor& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire SceneEditor, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + EditorActivity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneEditor object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Start + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Officially starts the game accroding to parameters previously set. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Start() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Pause + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pauses and unpauses the game. + // Arguments: Whether to pause the game or not. + // Return value: None. + + void SetPaused(bool pause = true) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: End + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces the current game's end. + // Arguments: None. + // Return value: None. + + void End() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this ActivityMan. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the currently active GUI of a screen to a BITMAP of choice. + // Arguments: A pointer to a screen-sized BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which screen's GUI to draw onto the bitmap. + // Return value: None. + + void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ActivityMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Saves the current Scene to an appropriate ini file, and asks user if they want to overwrite first if scene of this name exists. + /// + /// The name of the new Scene to be saved. + /// Whether to force any existing Scene of that name to be overwritten if it already exists. + /// Whether actually managed to save. Will return false both if a scene of this name already exists, or if other error. + bool SaveScene(const std::string& saveAsName, bool forceOverwrite = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateNewDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateNewDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateLoadDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Load dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateLoadDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateSaveDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateSaveDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateChangesDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Save Changes dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateChangesDialog() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: UpdateOverwriteDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Overwrite dialog box, populates its lists etc. + // Arguments: None. + // Return value: None. + + void UpdateOverwriteDialog() override; + + // Member variables + static Entity::ClassInfo m_sClass; + + // The editor GUI + SceneEditorGUI* m_pEditorGUI; + + // The combobox which lists all the Terrain:s that can be loaded for a new scene + GUIComboBox* m_pNewTerrainCombo; + // The combobox which lists all the background SceneLayer:s that can be loaded for a new scene, near + GUIComboBox* m_pNewBG1Combo; + // The combobox which lists all the background SceneLayer:s that can be loaded for a new scene, mid + GUIComboBox* m_pNewBG2Combo; + // The combobox which lists all the background SceneLayer:s that can be loaded for a new scene, far/sky + GUIComboBox* m_pNewBG3Combo; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Activity, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Entities/ACDropShip.cpp b/Source/Entities/ACDropShip.cpp index b3db4b68e5..f4460bf278 100644 --- a/Source/Entities/ACDropShip.cpp +++ b/Source/Entities/ACDropShip.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -22,600 +21,618 @@ namespace RTE { -ConcreteClassInfo(ACDropShip, ACraft, 10); + ConcreteClassInfo(ACDropShip, ACraft, 10); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ACDropShip, effectively + // resetting the members of this abstraction level only. + + void ACDropShip::Clear() { + m_pBodyAG = 0; + m_pRThruster = 0; + m_pLThruster = 0; + m_pURThruster = 0; + m_pULThruster = 0; + m_pRHatch = 0; + m_pLHatch = 0; + m_HatchSwingRange.SetDegAngle(90); + m_HatchOpeness = 0; + m_LateralControl = 0; + m_LateralControlSpeed = 6.0f; + m_AutoStabilize = 1; + m_MaxEngineAngle = 20.0f; + m_HoverHeightModifier = 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ACDropShip object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ACDropShip, effectively -// resetting the members of this abstraction level only. - -void ACDropShip::Clear() -{ - m_pBodyAG = 0; - m_pRThruster = 0; - m_pLThruster = 0; - m_pURThruster = 0; - m_pULThruster = 0; - m_pRHatch = 0; - m_pLHatch = 0; - m_HatchSwingRange.SetDegAngle(90); - m_HatchOpeness = 0; - m_LateralControl = 0; - m_LateralControlSpeed = 6.0f; - m_AutoStabilize = 1; - m_MaxEngineAngle = 20.0f; - m_HoverHeightModifier = 0; -} + int ACDropShip::Create() { + if (ACraft::Create() < 0) + return -1; + // Save the AtomGroup read in by MOSRotating, as we are going to make it + // into a composite group, and want to have the base body stored for reference. + m_pBodyAG = dynamic_cast(m_pAtomGroup->Clone()); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ACDropShip object ready for use. + return 0; + } -int ACDropShip::Create() -{ - if (ACraft::Create() < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ACDropShip to be identical to another, by deep copy. - // Save the AtomGroup read in by MOSRotating, as we are going to make it - // into a composite group, and want to have the base body stored for reference. - m_pBodyAG = dynamic_cast(m_pAtomGroup->Clone()); + int ACDropShip::Create(const ACDropShip& reference) { + if (reference.m_pRThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRThruster->GetUniqueID()); + } + if (reference.m_pLThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLThruster->GetUniqueID()); + } + if (reference.m_pURThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pURThruster->GetUniqueID()); + } + if (reference.m_pULThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pULThruster->GetUniqueID()); + } + if (reference.m_pRHatch) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRHatch->GetUniqueID()); + } + if (reference.m_pLHatch) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLHatch->GetUniqueID()); + } - return 0; -} + ACraft::Create(reference); + if (reference.m_pRThruster) { + SetRightThruster(dynamic_cast(reference.m_pRThruster->Clone())); + } + if (reference.m_pLThruster) { + SetLeftThruster(dynamic_cast(reference.m_pLThruster->Clone())); + } + if (reference.m_pURThruster) { + SetURightThruster(dynamic_cast(reference.m_pURThruster->Clone())); + } + if (reference.m_pULThruster) { + SetULeftThruster(dynamic_cast(reference.m_pULThruster->Clone())); + } + if (reference.m_pRHatch) { + SetRightHatch(dynamic_cast(reference.m_pRHatch->Clone())); + } + if (reference.m_pLHatch) { + SetLeftHatch(dynamic_cast(reference.m_pLHatch->Clone())); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a ACDropShip to be identical to another, by deep copy. + m_pBodyAG = dynamic_cast(reference.m_pBodyAG->Clone()); + m_pBodyAG->SetOwner(this); + m_HatchSwingRange = reference.m_HatchSwingRange; + m_HatchOpeness = reference.m_HatchOpeness; -int ACDropShip::Create(const ACDropShip &reference) { - if (reference.m_pRThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRThruster->GetUniqueID()); } - if (reference.m_pLThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLThruster->GetUniqueID()); } - if (reference.m_pURThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pURThruster->GetUniqueID()); } - if (reference.m_pULThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pULThruster->GetUniqueID()); } - if (reference.m_pRHatch) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRHatch->GetUniqueID()); } - if (reference.m_pLHatch) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLHatch->GetUniqueID()); } + m_LateralControl = reference.m_LateralControl; + m_LateralControlSpeed = reference.m_LateralControlSpeed; + m_AutoStabilize = reference.m_AutoStabilize; - ACraft::Create(reference); + m_MaxEngineAngle = reference.m_MaxEngineAngle; - if (reference.m_pRThruster) { SetRightThruster(dynamic_cast(reference.m_pRThruster->Clone())); } - if (reference.m_pLThruster) { SetLeftThruster(dynamic_cast(reference.m_pLThruster->Clone())); } - if (reference.m_pURThruster) { SetURightThruster(dynamic_cast(reference.m_pURThruster->Clone())); } - if (reference.m_pULThruster) { SetULeftThruster(dynamic_cast(reference.m_pULThruster->Clone())); } - if (reference.m_pRHatch) { SetRightHatch(dynamic_cast(reference.m_pRHatch->Clone())); } - if (reference.m_pLHatch) { SetLeftHatch(dynamic_cast(reference.m_pLHatch->Clone())); } + m_HoverHeightModifier = reference.m_HoverHeightModifier; - m_pBodyAG = dynamic_cast(reference.m_pBodyAG->Clone()); - m_pBodyAG->SetOwner(this); - m_HatchSwingRange = reference.m_HatchSwingRange; - m_HatchOpeness = reference.m_HatchOpeness; + return 0; + } - m_LateralControl = reference.m_LateralControl; - m_LateralControlSpeed = reference.m_LateralControlSpeed; - m_AutoStabilize = reference.m_AutoStabilize; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int ACDropShip::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return ACraft::ReadProperty(propName, reader)); + + MatchForwards("RThruster") MatchForwards("RightThruster") MatchProperty("RightEngine", { SetRightThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("LThruster") MatchForwards("LeftThruster") MatchProperty("LeftEngine", { SetLeftThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("URThruster") MatchProperty("UpRightThruster", { SetURightThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("ULThruster") MatchProperty("UpLeftThruster", { SetULeftThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("RHatchDoor") MatchProperty("RightHatchDoor", { SetRightHatch(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("LHatchDoor") MatchProperty("LeftHatchDoor", { SetLeftHatch(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("HatchDoorSwingRange", { reader >> m_HatchSwingRange; }); + MatchProperty("AutoStabilize", { reader >> m_AutoStabilize; }); + MatchProperty("MaxEngineAngle", { reader >> m_MaxEngineAngle; }); + MatchProperty("LateralControlSpeed", { reader >> m_LateralControlSpeed; }); + MatchProperty("HoverHeightModifier", { reader >> m_HoverHeightModifier; }); + + EndPropertyList; + } - m_MaxEngineAngle = reference.m_MaxEngineAngle; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this ACDropShip with a Writer for + // later recreation with Create(Reader &reader); + + int ACDropShip::Save(Writer& writer) const { + ACraft::Save(writer); + + writer.NewProperty("RThruster"); + writer << m_pRThruster; + writer.NewProperty("LThruster"); + writer << m_pLThruster; + writer.NewProperty("URThruster"); + writer << m_pURThruster; + writer.NewProperty("ULThruster"); + writer << m_pULThruster; + writer.NewProperty("RHatchDoor"); + writer << m_pRHatch; + writer.NewProperty("LHatchDoor"); + writer << m_pLHatch; + writer.NewProperty("HatchDoorSwingRange"); + writer << m_HatchSwingRange; + writer.NewProperty("AutoStabilize"); + writer << m_AutoStabilize; + writer.NewProperty("MaxEngineAngle"); + writer << m_MaxEngineAngle; + writer.NewProperty("LateralControlSpeed"); + writer << m_LateralControlSpeed; + writer.NewPropertyWithValue("HoverHeightModifier", m_HoverHeightModifier); + + return 0; + } - m_HoverHeightModifier = reference.m_HoverHeightModifier; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the ACDropShip object. - return 0; -} + void ACDropShip::Destroy(bool notInherited) { + delete m_pBodyAG; + if (!notInherited) + ACraft::Destroy(); + Clear(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int ACDropShip::ReadProperty(const std::string_view &propName, Reader &reader) { - StartPropertyList(return ACraft::ReadProperty(propName, reader)); - - MatchForwards("RThruster") MatchForwards("RightThruster") MatchProperty("RightEngine", { SetRightThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("LThruster") MatchForwards("LeftThruster") MatchProperty("LeftEngine", { SetLeftThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("URThruster") MatchProperty("UpRightThruster", { SetURightThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("ULThruster") MatchProperty("UpLeftThruster", { SetULeftThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("RHatchDoor") MatchProperty("RightHatchDoor", { SetRightHatch(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("LHatchDoor") MatchProperty("LeftHatchDoor", { SetLeftHatch(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("HatchDoorSwingRange", { reader >> m_HatchSwingRange; }); - MatchProperty("AutoStabilize", { reader >> m_AutoStabilize; }); - MatchProperty("MaxEngineAngle", { reader >> m_MaxEngineAngle; }); - MatchProperty("LateralControlSpeed", { reader >> m_LateralControlSpeed; }); - MatchProperty("HoverHeightModifier", { reader >> m_HoverHeightModifier; }); - - EndPropertyList; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAltitude + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the altitide of this' pos (or appropriate low point) over the + // terrain, in pixels. + float ACDropShip::GetAltitude(int max, int accuracy) { + // Check altitude both thrusters, and report the one closest to the ground. + Vector rPos, lPos; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this ACDropShip with a Writer for -// later recreation with Create(Reader &reader); - -int ACDropShip::Save(Writer &writer) const -{ - ACraft::Save(writer); - - writer.NewProperty("RThruster"); - writer << m_pRThruster; - writer.NewProperty("LThruster"); - writer << m_pLThruster; - writer.NewProperty("URThruster"); - writer << m_pURThruster; - writer.NewProperty("ULThruster"); - writer << m_pULThruster; - writer.NewProperty("RHatchDoor"); - writer << m_pRHatch; - writer.NewProperty("LHatchDoor"); - writer << m_pLHatch; - writer.NewProperty("HatchDoorSwingRange"); - writer << m_HatchSwingRange; - writer.NewProperty("AutoStabilize"); - writer << m_AutoStabilize; - writer.NewProperty("MaxEngineAngle"); - writer << m_MaxEngineAngle; - writer.NewProperty("LateralControlSpeed"); - writer << m_LateralControlSpeed; - writer.NewPropertyWithValue("HoverHeightModifier", m_HoverHeightModifier); - - return 0; -} + if (m_pRThruster && m_pRThruster->IsAttached()) + rPos = m_Pos + RotateOffset(m_pRThruster->GetParentOffset()); // + Vector(m_pRThruster->GetRadius(), 0)); + else + rPos = m_Pos; + if (m_pLThruster && m_pLThruster->IsAttached()) + lPos = m_Pos + RotateOffset(m_pLThruster->GetParentOffset()); // + Vector(-m_pLThruster->GetRadius(), 0)); + else + lPos = m_Pos; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the ACDropShip object. + // Wrap the engine positions + g_SceneMan.WrapPosition(lPos); + g_SceneMan.WrapPosition(rPos); -void ACDropShip::Destroy(bool notInherited) -{ - delete m_pBodyAG; + // Check center too + float cAlt = g_SceneMan.FindAltitude(m_Pos, max, accuracy, true); + float rAlt = g_SceneMan.FindAltitude(rPos, max, accuracy, true); + float lAlt = g_SceneMan.FindAltitude(lPos, max, accuracy, true); - if (!notInherited) - ACraft::Destroy(); - Clear(); -} + // Return the lowest of the three + return MIN(cAlt, MIN(rAlt, lAlt)); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DetectObstacle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks for obstacles in the travel direction. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAltitude -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the altitide of this' pos (or appropriate low point) over the -// terrain, in pixels. + MOID ACDropShip::DetectObstacle(float distance) { + // Check altitude both thrusters, and report the one closest to the ground. + Vector rPos, lPos; -float ACDropShip::GetAltitude(int max, int accuracy) -{ - // Check altitude both thrusters, and report the one closest to the ground. - Vector rPos, lPos; + if (m_pRThruster && m_pRThruster->IsAttached()) + rPos = m_Pos + RotateOffset(m_pRThruster->GetParentOffset() + Vector(m_pRThruster->GetRadius(), 0)); + else + rPos = m_Pos; - if (m_pRThruster && m_pRThruster->IsAttached()) - rPos = m_Pos + RotateOffset(m_pRThruster->GetParentOffset());// + Vector(m_pRThruster->GetRadius(), 0)); - else - rPos = m_Pos; + if (m_pLThruster && m_pLThruster->IsAttached()) + lPos = m_Pos + RotateOffset(m_pLThruster->GetParentOffset() + Vector(-m_pLThruster->GetRadius(), 0)); + else + lPos = m_Pos; + + // Wrap the engine positions + g_SceneMan.WrapPosition(lPos); + g_SceneMan.WrapPosition(rPos); + + // Make the ray to check along point in an appropriate direction + Vector checkRay; + if (m_AltitudeMoveState == DESCEND) + checkRay.m_Y = distance; + else if (m_AltitudeMoveState == ASCEND) + checkRay.m_Y = -distance; + // Just rotate it to align iwth the velocity + else { + checkRay.m_X = distance; + checkRay.AbsRotateTo(m_Vel); + } - if (m_pLThruster && m_pLThruster->IsAttached()) - lPos = m_Pos + RotateOffset(m_pLThruster->GetParentOffset());// + Vector(-m_pLThruster->GetRadius(), 0)); - else - lPos = m_Pos; + MOID detected = g_NoMOID; - // Wrap the engine positions - g_SceneMan.WrapPosition(lPos); - g_SceneMan.WrapPosition(rPos); + // Check center too? + if ((detected = g_SceneMan.CastMORay(m_Pos, checkRay, m_RootMOID, Activity::NoTeam, 0, true, 30)) != g_NoMOID) + return detected; + if ((detected = g_SceneMan.CastMORay(rPos, checkRay, m_RootMOID, Activity::NoTeam, 0, true, 30)) != g_NoMOID) + return detected; + if ((detected = g_SceneMan.CastMORay(lPos, checkRay, m_RootMOID, Activity::NoTeam, 0, true, 30)) != g_NoMOID) + return detected; - // Check center too - float cAlt = g_SceneMan.FindAltitude(m_Pos, max, accuracy, true); - float rAlt = g_SceneMan.FindAltitude(rPos, max, accuracy, true); - float lAlt = g_SceneMan.FindAltitude(lPos, max, accuracy, true); + return false; + } - // Return the lowest of the three - return MIN(cAlt, MIN(rAlt, lAlt)); -} + ////////////////////////////////////////////////////////////////////////////////////////// + void ACDropShip::PreControllerUpdate() { + ZoneScoped; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DetectObstacle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks for obstacles in the travel direction. - -MOID ACDropShip::DetectObstacle(float distance) -{ - // Check altitude both thrusters, and report the one closest to the ground. - Vector rPos, lPos; - - if (m_pRThruster && m_pRThruster->IsAttached()) - rPos = m_Pos + RotateOffset(m_pRThruster->GetParentOffset() + Vector(m_pRThruster->GetRadius(), 0)); - else - rPos = m_Pos; - - if (m_pLThruster && m_pLThruster->IsAttached()) - lPos = m_Pos + RotateOffset(m_pLThruster->GetParentOffset() + Vector(-m_pLThruster->GetRadius(), 0)); - else - lPos = m_Pos; - - // Wrap the engine positions - g_SceneMan.WrapPosition(lPos); - g_SceneMan.WrapPosition(rPos); - - // Make the ray to check along point in an appropriate direction - Vector checkRay; - if (m_AltitudeMoveState == DESCEND) - checkRay.m_Y = distance; - else if (m_AltitudeMoveState == ASCEND) - checkRay.m_Y = -distance; - // Just rotate it to align iwth the velocity - else - { - checkRay.m_X = distance; - checkRay.AbsRotateTo(m_Vel); - } - - MOID detected = g_NoMOID; - - // Check center too? - if ((detected = g_SceneMan.CastMORay(m_Pos, checkRay, m_RootMOID, Activity::NoTeam, 0, true, 30)) != g_NoMOID) - return detected; - if ((detected = g_SceneMan.CastMORay(rPos, checkRay, m_RootMOID, Activity::NoTeam, 0, true, 30)) != g_NoMOID) - return detected; - if ((detected = g_SceneMan.CastMORay(lPos, checkRay, m_RootMOID, Activity::NoTeam, 0, true, 30)) != g_NoMOID) - return detected; - - return false; -} + ACraft::PreControllerUpdate(); -////////////////////////////////////////////////////////////////////////////////////////// + // TODO: Improve and make optional thrusters more robust! + if (m_Status != DEAD && m_Status != DYING) { + float targetYVel = 0.0F; + float throttleRange = 7.5f; -void ACDropShip::PreControllerUpdate() -{ - ZoneScoped; + if (m_Controller.IsState(PRESS_UP)) { + // This is to make sure se get loose from being sideways stuck + m_ForceDeepCheck = true; + } + // TODO: make framerate independent! + // Altitude control, check analog first + if (fabs(m_Controller.GetAnalogMove().m_Y) > 0.1) { + targetYVel = -m_Controller.GetAnalogMove().m_Y * throttleRange; + } + // Fall back to digital altitude control + else if (m_Controller.IsState(MOVE_UP) || m_Controller.IsState(AIM_UP)) + targetYVel = throttleRange; + else if (m_Controller.IsState(MOVE_DOWN) || m_Controller.IsState(AIM_DOWN)) + targetYVel = -throttleRange; - ACraft::PreControllerUpdate(); + ////////////////////////////////////////////////////// + // Main thruster throttling to stay hovering - // TODO: Improve and make optional thrusters more robust! - if (m_Status != DEAD && m_Status != DYING) - { - float targetYVel = 0.0F; - float throttleRange = 7.5f; + // ugly hacks. the entire trimming to hover system is shit and should be replaced - if (m_Controller.IsState(PRESS_UP)) - { - // This is to make sure se get loose from being sideways stuck - m_ForceDeepCheck = true; - } - // TODO: make framerate independent! - // Altitude control, check analog first - if (fabs(m_Controller.GetAnalogMove().m_Y) > 0.1) - { - targetYVel = -m_Controller.GetAnalogMove().m_Y * throttleRange; - } - // Fall back to digital altitude control - else if (m_Controller.IsState(MOVE_UP) || m_Controller.IsState(AIM_UP)) - targetYVel = throttleRange; - else if (m_Controller.IsState(MOVE_DOWN) || m_Controller.IsState(AIM_DOWN)) - targetYVel = -throttleRange; + // This is to trim the hover so it's perfectly still altitude-wise + float trimming = -2.6f; - ////////////////////////////////////////////////////// - // Main thruster throttling to stay hovering + float throttle = (targetYVel + m_Vel.m_Y + trimming) / throttleRange; - // ugly hacks. the entire trimming to hover system is shit and should be replaced + // Adjust trim based on weight. Dropships hover nicely at zero weight, but tend to drop when they have a large inventory + float massAdjustment = GetMass() / GetBaseMass(); - // This is to trim the hover so it's perfectly still altitude-wise - float trimming = -2.6f; + // Right main thruster + if (m_pRThruster && m_pRThruster->IsAttached()) { + float baseThrottleForThruster = m_pRThruster->GetThrottleForThrottleFactor(1.0f); + float rightThrottle = m_pRThruster->GetScaledThrottle(throttle + baseThrottleForThruster, massAdjustment); + + // Throttle override control for correcting heavy tilt, only applies if both engines are present + if (m_pLThruster && m_pLThruster->IsAttached()) { + if (m_Rotation.GetRadAngle() > c_SixteenthPI) { + rightThrottle = -0.8f; + } else if (m_Rotation.GetRadAngle() < -c_SixteenthPI) { + rightThrottle = 0.8f; + } + } + + if (rightThrottle > m_pRThruster->GetThrottle()) { + rightThrottle = rightThrottle * 0.3f + m_pRThruster->GetThrottle() * 0.7f; // Increase throttle slowly + } + + m_pRThruster->EnableEmission(m_Status == STABLE); + m_pRThruster->SetThrottle(rightThrottle); + m_pRThruster->SetFlashScale((m_pRThruster->GetThrottle() + 1.5f) / 2.0f); + // Engines are noisy! Make AI aware of them + m_pRThruster->AlarmOnEmit(m_Team); + } + // Left main thruster + if (m_pLThruster && m_pLThruster->IsAttached()) { + float baseThrottleForThruster = m_pLThruster->GetThrottleForThrottleFactor(1.0f); + float leftThrottle = m_pLThruster->GetScaledThrottle(throttle + baseThrottleForThruster, massAdjustment); + + // Throttle override control for correcting heavy tilt, only applies if both engines are present + if (m_pRThruster && m_pRThruster->IsAttached()) { + if (m_Rotation.GetRadAngle() > c_SixteenthPI) { + leftThrottle = 0.8f; + } else if (m_Rotation.GetRadAngle() < -c_SixteenthPI) { + leftThrottle = -0.8f; + } + } + + if (leftThrottle > m_pLThruster->GetThrottle()) { + leftThrottle = leftThrottle * 0.3f + m_pLThruster->GetThrottle() * 0.7f; // Increase throttle slowly + } + + m_pLThruster->EnableEmission(m_Status == STABLE); + m_pLThruster->SetThrottle(leftThrottle); + m_pLThruster->SetFlashScale((m_pLThruster->GetThrottle() + 1.5f) / 2.0F); + // Engines are noisy! Make AI aware of them + m_pLThruster->AlarmOnEmit(m_Team); + } - float throttle = (targetYVel + m_Vel.m_Y + trimming) / throttleRange; + /////////////////////////////////////////////// + // Lateral control - // Adjust trim based on weight. Dropships hover nicely at zero weight, but tend to drop when they have a large inventory - float massAdjustment = GetMass() / GetBaseMass(); + // Check analog first + if (fabs(m_Controller.GetAnalogMove().m_X) > 0.1) { + if (m_LateralControl < -m_Controller.GetAnalogMove().m_X) + m_LateralControl += m_LateralControlSpeed * g_TimerMan.GetDeltaTimeSecs(); // 0.1 per update at 60fps + else if (m_LateralControl > -m_Controller.GetAnalogMove().m_X) + m_LateralControl -= m_LateralControlSpeed * g_TimerMan.GetDeltaTimeSecs(); + } + // Fall back to digital lateral control + else { + if (m_Controller.IsState(MOVE_RIGHT)) + m_LateralControl -= m_LateralControlSpeed * g_TimerMan.GetDeltaTimeSecs(); + else if (m_Controller.IsState(MOVE_LEFT)) + m_LateralControl += m_LateralControlSpeed * g_TimerMan.GetDeltaTimeSecs(); + else if (m_LateralControl != 0.0) + m_LateralControl *= 54.0f * g_TimerMan.GetDeltaTimeSecs(); // 90% per update at 60fps + } - // Right main thruster - if (m_pRThruster && m_pRThruster->IsAttached()) - { - float baseThrottleForThruster = m_pRThruster->GetThrottleForThrottleFactor(1.0f); - float rightThrottle = m_pRThruster->GetScaledThrottle(throttle + baseThrottleForThruster, massAdjustment); + // Clamp the lateral control + if (m_LateralControl > 1.0) + m_LateralControl = 1.0; + else if (m_LateralControl < -1.0) + m_LateralControl = -1.0; + + if (m_Controller.IsState(PRESS_FACEBUTTON)) { + if (m_HatchState == CLOSED) + DropAllInventory(); + else if (m_HatchState == OPEN) + CloseHatch(); + } + } + // No Controller present, or dead + else { + if (m_pRThruster && m_pRThruster->IsAttached()) + m_pRThruster->EnableEmission(false); + if (m_pLThruster && m_pLThruster->IsAttached()) + m_pLThruster->EnableEmission(false); + /* + if (m_pURThruster && m_pURThruster->IsAttached()) + m_pURThruster->EnableEmission(false); + if (m_pULThruster && m_pULThruster->IsAttached()) + m_pULThruster->EnableEmission(false); + */ + } - // Throttle override control for correcting heavy tilt, only applies if both engines are present - if (m_pLThruster && m_pLThruster->IsAttached()) { - if (m_Rotation.GetRadAngle() > c_SixteenthPI) { - rightThrottle = -0.8f; - } else if (m_Rotation.GetRadAngle() < -c_SixteenthPI) { - rightThrottle = 0.8f; - } + //////////////////////////////////////// + // Hatch Operation + + if (m_HatchState == OPENING) { + if (m_HatchDelay > 0 && !m_HatchTimer.IsPastSimMS(m_HatchDelay)) + m_HatchOpeness = (float)m_HatchTimer.GetElapsedSimTimeMS() / (float)m_HatchDelay; + else { + m_HatchOpeness = 1.0; + m_HatchState = OPEN; + DropAllInventory(); + } + } else if (m_HatchState == CLOSING) { + if (m_HatchDelay > 0 && !m_HatchTimer.IsPastSimMS(m_HatchDelay)) + m_HatchOpeness = 1.0 - ((float)m_HatchTimer.GetElapsedSimTimeMS() / (float)m_HatchDelay); + else { + m_HatchOpeness = 0; + m_HatchState = CLOSED; } + } - if (rightThrottle > m_pRThruster->GetThrottle()) { - rightThrottle = rightThrottle * 0.3f + m_pRThruster->GetThrottle() * 0.7f; // Increase throttle slowly - } + ///////////////////////////////// + // Manage Attachable:s + Matrix engineRot = 0; + if (m_Rotation.GetDegAngle() > m_MaxEngineAngle) { + engineRot.SetDegAngle(m_Rotation.GetDegAngle() - m_MaxEngineAngle); + } else if (m_Rotation.GetDegAngle() < -m_MaxEngineAngle) { + engineRot.SetDegAngle(m_Rotation.GetDegAngle() + m_MaxEngineAngle); + } else { + // Lateral control application + engineRot.SetDegAngle(m_MaxEngineAngle * m_LateralControl); + } - m_pRThruster->EnableEmission(m_Status == STABLE); - m_pRThruster->SetThrottle(rightThrottle); - m_pRThruster->SetFlashScale((m_pRThruster->GetThrottle() + 1.5f) / 2.0f); - // Engines are noisy! Make AI aware of them - m_pRThruster->AlarmOnEmit(m_Team); + if (m_pRThruster && m_pRThruster->IsAttached()) { + m_pRThruster->SetRotAngle(engineRot.GetRadAngle()); + m_pRThruster->SetAngularVel(0.0F); } - // Left main thruster - if (m_pLThruster && m_pLThruster->IsAttached()) - { - float baseThrottleForThruster = m_pLThruster->GetThrottleForThrottleFactor(1.0f); - float leftThrottle = m_pLThruster->GetScaledThrottle(throttle + baseThrottleForThruster, massAdjustment); - // Throttle override control for correcting heavy tilt, only applies if both engines are present - if (m_pRThruster && m_pRThruster->IsAttached()) { - if (m_Rotation.GetRadAngle() > c_SixteenthPI) { - leftThrottle = 0.8f; - } else if (m_Rotation.GetRadAngle() < -c_SixteenthPI) { - leftThrottle = -0.8f; - } + if (m_pLThruster && m_pLThruster->IsAttached()) { + m_pLThruster->SetRotAngle(engineRot.GetRadAngle()); + m_pLThruster->SetAngularVel(0.0F); + } + + // Auto balancing with the up thrusters + if (m_pURThruster && m_pURThruster->IsAttached() && m_pULThruster && m_pULThruster->IsAttached()) { + if (m_AutoStabilize) { + // Use a PD-controller for balance + float change = 0.9F * m_AngularVel + 0.8F * m_Rotation.GetRadAngle(); + if (change > 0.2F) { + if (!m_pURThruster->IsEmitting()) { + m_pURThruster->TriggerBurst(); + } + m_pURThruster->EnableEmission(true); + } else { + m_pURThruster->EnableEmission(false); + } + + if (change < -0.2F) { + if (!m_pULThruster->IsEmitting()) { + m_pULThruster->TriggerBurst(); + } + m_pULThruster->EnableEmission(true); + } else { + m_pULThruster->EnableEmission(false); + } } + } - if (leftThrottle > m_pLThruster->GetThrottle()) { - leftThrottle = leftThrottle * 0.3f + m_pLThruster->GetThrottle() * 0.7f; // Increase throttle slowly - } + // Hatch door pieces + if (m_pRHatch && m_pRHatch->IsAttached()) { + m_pRHatch->SetRotAngle(m_Rotation.GetRadAngle() + m_HatchSwingRange.GetRadAngle() * m_HatchOpeness); + } - m_pLThruster->EnableEmission(m_Status == STABLE); - m_pLThruster->SetThrottle(leftThrottle); - m_pLThruster->SetFlashScale((m_pLThruster->GetThrottle() + 1.5f) / 2.0F); - // Engines are noisy! Make AI aware of them - m_pLThruster->AlarmOnEmit(m_Team); + if (m_pLHatch && m_pLHatch->IsAttached()) { + m_pLHatch->SetRotAngle(m_Rotation.GetRadAngle() - m_HatchSwingRange.GetRadAngle() * m_HatchOpeness); } + } - /////////////////////////////////////////////// - // Lateral control + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Check analog first - if (fabs(m_Controller.GetAnalogMove().m_X) > 0.1) - { - if (m_LateralControl < -m_Controller.GetAnalogMove().m_X) - m_LateralControl += m_LateralControlSpeed * g_TimerMan.GetDeltaTimeSecs(); // 0.1 per update at 60fps - else if (m_LateralControl > -m_Controller.GetAnalogMove().m_X) - m_LateralControl -= m_LateralControlSpeed * g_TimerMan.GetDeltaTimeSecs(); + void ACDropShip::SetRightThruster(AEmitter* newThruster) { + if (m_pRThruster && m_pRThruster->IsAttached()) { + RemoveAndDeleteAttachable(m_pRThruster); } - // Fall back to digital lateral control - else - { - if (m_Controller.IsState(MOVE_RIGHT)) - m_LateralControl -= m_LateralControlSpeed * g_TimerMan.GetDeltaTimeSecs(); - else if (m_Controller.IsState(MOVE_LEFT)) - m_LateralControl += m_LateralControlSpeed * g_TimerMan.GetDeltaTimeSecs(); - else if (m_LateralControl != 0.0) - m_LateralControl *= 54.0f * g_TimerMan.GetDeltaTimeSecs(); // 90% per update at 60fps - } - - // Clamp the lateral control - if (m_LateralControl > 1.0) - m_LateralControl = 1.0; - else if (m_LateralControl < -1.0) - m_LateralControl = -1.0; - - if (m_Controller.IsState(PRESS_FACEBUTTON)) - { - if (m_HatchState == CLOSED) - DropAllInventory(); - else if (m_HatchState == OPEN) - CloseHatch(); + if (newThruster == nullptr) { + m_pRThruster = nullptr; + } else { + m_pRThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEmitter* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightThruster"); + dynamic_cast(parent)->SetRightThruster(castedAttachable); + }}); + + if (m_pRThruster->HasNoSetDamageMultiplier()) { + m_pRThruster->SetDamageMultiplier(1.0F); + } + m_pRThruster->SetInheritsRotAngle(false); } } - // No Controller present, or dead - else - { - if (m_pRThruster && m_pRThruster->IsAttached()) - m_pRThruster->EnableEmission(false); - if (m_pLThruster && m_pLThruster->IsAttached()) - m_pLThruster->EnableEmission(false); - /* - if (m_pURThruster && m_pURThruster->IsAttached()) - m_pURThruster->EnableEmission(false); - if (m_pULThruster && m_pULThruster->IsAttached()) - m_pULThruster->EnableEmission(false); - */ + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACDropShip::SetLeftThruster(AEmitter* newThruster) { + if (m_pLThruster && m_pLThruster->IsAttached()) { + RemoveAndDeleteAttachable(m_pLThruster); + } + if (newThruster == nullptr) { + m_pLThruster = nullptr; + } else { + m_pLThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEmitter* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftThruster"); + dynamic_cast(parent)->SetLeftThruster(castedAttachable); + }}); + + if (m_pLThruster->HasNoSetDamageMultiplier()) { + m_pLThruster->SetDamageMultiplier(1.0F); + } + m_pLThruster->SetInheritsRotAngle(false); + } } - //////////////////////////////////////// - // Hatch Operation + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (m_HatchState == OPENING) - { - if (m_HatchDelay > 0 && !m_HatchTimer.IsPastSimMS(m_HatchDelay)) - m_HatchOpeness = (float)m_HatchTimer.GetElapsedSimTimeMS() / (float)m_HatchDelay; - else - { - m_HatchOpeness = 1.0; - m_HatchState = OPEN; - DropAllInventory(); + void ACDropShip::SetURightThruster(AEmitter* newThruster) { + if (m_pURThruster && m_pURThruster->IsAttached()) { + RemoveAndDeleteAttachable(m_pURThruster); + } + if (newThruster == nullptr) { + m_pURThruster = nullptr; + } else { + m_pURThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEmitter* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetURightThruster"); + dynamic_cast(parent)->SetURightThruster(castedAttachable); + }}); + + if (m_pURThruster->HasNoSetDamageMultiplier()) { + m_pURThruster->SetDamageMultiplier(1.0F); + } } } - else if (m_HatchState == CLOSING) - { - if (m_HatchDelay > 0 && !m_HatchTimer.IsPastSimMS(m_HatchDelay)) - m_HatchOpeness = 1.0 - ((float)m_HatchTimer.GetElapsedSimTimeMS() / (float)m_HatchDelay); - else - { - m_HatchOpeness = 0; - m_HatchState = CLOSED; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACDropShip::SetULeftThruster(AEmitter* newThruster) { + if (m_pULThruster && m_pULThruster->IsAttached()) { + RemoveAndDeleteAttachable(m_pULThruster); + } + if (newThruster == nullptr) { + m_pULThruster = nullptr; + } else { + m_pULThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEmitter* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetULeftThruster"); + dynamic_cast(parent)->SetULeftThruster(castedAttachable); + }}); + + if (m_pULThruster->HasNoSetDamageMultiplier()) { + m_pULThruster->SetDamageMultiplier(1.0F); + } } } - ///////////////////////////////// - // Manage Attachable:s - Matrix engineRot = 0; - if (m_Rotation.GetDegAngle() > m_MaxEngineAngle) { - engineRot.SetDegAngle(m_Rotation.GetDegAngle() - m_MaxEngineAngle); - } else if (m_Rotation.GetDegAngle() < -m_MaxEngineAngle) { - engineRot.SetDegAngle(m_Rotation.GetDegAngle() + m_MaxEngineAngle); - } else { - // Lateral control application - engineRot.SetDegAngle(m_MaxEngineAngle * m_LateralControl); - } - - if (m_pRThruster && m_pRThruster->IsAttached()) { - m_pRThruster->SetRotAngle(engineRot.GetRadAngle()); - m_pRThruster->SetAngularVel(0.0F); - } - - if (m_pLThruster && m_pLThruster->IsAttached()) { - m_pLThruster->SetRotAngle(engineRot.GetRadAngle()); - m_pLThruster->SetAngularVel(0.0F); - } - - // Auto balancing with the up thrusters - if (m_pURThruster && m_pURThruster->IsAttached() && m_pULThruster && m_pULThruster->IsAttached()) { - if (m_AutoStabilize) { - // Use a PD-controller for balance - float change = 0.9F * m_AngularVel + 0.8F * m_Rotation.GetRadAngle(); - if (change > 0.2F) { - if (!m_pURThruster->IsEmitting()) { - m_pURThruster->TriggerBurst(); - } - m_pURThruster->EnableEmission(true); - } else { - m_pURThruster->EnableEmission(false); - } - - if (change < -0.2F) { - if (!m_pULThruster->IsEmitting()) { - m_pULThruster->TriggerBurst(); - } - m_pULThruster->EnableEmission(true); - } else { - m_pULThruster->EnableEmission(false); - } - } - } - - // Hatch door pieces - if (m_pRHatch && m_pRHatch->IsAttached()) { - m_pRHatch->SetRotAngle(m_Rotation.GetRadAngle() + m_HatchSwingRange.GetRadAngle() * m_HatchOpeness); - } - - if (m_pLHatch && m_pLHatch->IsAttached()) { - m_pLHatch->SetRotAngle(m_Rotation.GetRadAngle() - m_HatchSwingRange.GetRadAngle() * m_HatchOpeness); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACDropShip::SetRightThruster(AEmitter *newThruster) { - if (m_pRThruster && m_pRThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pRThruster); } - if (newThruster == nullptr) { - m_pRThruster = nullptr; - } else { - m_pRThruster = newThruster; - AddAttachable(newThruster); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEmitter *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightThruster"); - dynamic_cast(parent)->SetRightThruster(castedAttachable); - }}); - - if (m_pRThruster->HasNoSetDamageMultiplier()) { m_pRThruster->SetDamageMultiplier(1.0F); } - m_pRThruster->SetInheritsRotAngle(false); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACDropShip::SetLeftThruster(AEmitter *newThruster) { - if (m_pLThruster && m_pLThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pLThruster); } - if (newThruster == nullptr) { - m_pLThruster = nullptr; - } else { - m_pLThruster = newThruster; - AddAttachable(newThruster); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEmitter *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftThruster"); - dynamic_cast(parent)->SetLeftThruster(castedAttachable); - }}); - - if (m_pLThruster->HasNoSetDamageMultiplier()) { m_pLThruster->SetDamageMultiplier(1.0F); } - m_pLThruster->SetInheritsRotAngle(false); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACDropShip::SetURightThruster(AEmitter *newThruster) { - if (m_pURThruster && m_pURThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pURThruster); } - if (newThruster == nullptr) { - m_pURThruster = nullptr; - } else { - m_pURThruster = newThruster; - AddAttachable(newThruster); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEmitter *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetURightThruster"); - dynamic_cast(parent)->SetURightThruster(castedAttachable); - }}); - - if (m_pURThruster->HasNoSetDamageMultiplier()) { m_pURThruster->SetDamageMultiplier(1.0F); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACDropShip::SetULeftThruster(AEmitter *newThruster) { - if (m_pULThruster && m_pULThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pULThruster); } - if (newThruster == nullptr) { - m_pULThruster = nullptr; - } else { - m_pULThruster = newThruster; - AddAttachable(newThruster); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEmitter *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetULeftThruster"); - dynamic_cast(parent)->SetULeftThruster(castedAttachable); - }}); - - if (m_pULThruster->HasNoSetDamageMultiplier()) { m_pULThruster->SetDamageMultiplier(1.0F); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACDropShip::SetRightHatch(Attachable *newHatch) { - if (m_pRHatch && m_pRHatch->IsAttached()) { RemoveAndDeleteAttachable(m_pRHatch); } - if (newHatch == nullptr) { - m_pRHatch = nullptr; - } else { - m_pRHatch = newHatch; - AddAttachable(newHatch); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newHatch->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - dynamic_cast(parent)->SetRightHatch(attachable); - }}); - - if (m_pRHatch->HasNoSetDamageMultiplier()) { m_pRHatch->SetDamageMultiplier(1.0F); } - m_pRHatch->SetInheritsRotAngle(false); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACDropShip::SetLeftHatch(Attachable *newHatch) { - if (m_pLHatch && m_pLHatch->IsAttached()) { RemoveAndDeleteAttachable(m_pLHatch); } - if (newHatch == nullptr) { - m_pLHatch = nullptr; - } else { - m_pLHatch = newHatch; - AddAttachable(newHatch); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newHatch->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - dynamic_cast(parent)->SetLeftHatch(attachable); - }}); - - if (m_pLHatch->HasNoSetDamageMultiplier()) { m_pLHatch->SetDamageMultiplier(1.0F); } - m_pLHatch->SetInheritsRotAngle(false); - } -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACDropShip::SetRightHatch(Attachable* newHatch) { + if (m_pRHatch && m_pRHatch->IsAttached()) { + RemoveAndDeleteAttachable(m_pRHatch); + } + if (newHatch == nullptr) { + m_pRHatch = nullptr; + } else { + m_pRHatch = newHatch; + AddAttachable(newHatch); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newHatch->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + dynamic_cast(parent)->SetRightHatch(attachable); + }}); + + if (m_pRHatch->HasNoSetDamageMultiplier()) { + m_pRHatch->SetDamageMultiplier(1.0F); + } + m_pRHatch->SetInheritsRotAngle(false); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACDropShip::SetLeftHatch(Attachable* newHatch) { + if (m_pLHatch && m_pLHatch->IsAttached()) { + RemoveAndDeleteAttachable(m_pLHatch); + } + if (newHatch == nullptr) { + m_pLHatch = nullptr; + } else { + m_pLHatch = newHatch; + AddAttachable(newHatch); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newHatch->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + dynamic_cast(parent)->SetLeftHatch(attachable); + }}); + + if (m_pLHatch->HasNoSetDamageMultiplier()) { + m_pLHatch->SetDamageMultiplier(1.0F); + } + m_pLHatch->SetInheritsRotAngle(false); + } + } } // namespace RTE diff --git a/Source/Entities/ACDropShip.h b/Source/Entities/ACDropShip.h index aeb171fbad..5ee6747b27 100644 --- a/Source/Entities/ACDropShip.h +++ b/Source/Entities/ACDropShip.h @@ -10,353 +10,329 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "ACraft.h" -namespace RTE -{ - -class Attachable; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: ACDropShip -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A hovering craft, with two engines on each attached on each end which -// tilt independently of the body to achieve steering. -// Parent(s): ACraft. -// Class history: 12/13/2006 ACDropShip created. - -class ACDropShip : public ACraft { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(ACDropShip); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: ACDropShip -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a ACDropShip object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - ACDropShip() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~ACDropShip -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a ACDropShip object before deletion -// from system memory. -// Arguments: None. - - ~ACDropShip() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ACDropShip object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a ACDropShip to be identical to another, by deep copy. -// Arguments: A reference to the ACDropShip to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const ACDropShip &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire ACDropShip, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); ACraft::Reset(); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAltitude -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the altitide of this' pos (or appropriate low point) over the -// terrain, in pixels. -// Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. -// The accuracy within which measurement is acceptable. Higher number -// here means less calculation. -// Return value: The rough altitude over the terrain, in pixels. - - float GetAltitude(int max = 0, int accuracy = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DetectObstacle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks for obstacles in the travel direction. -// Arguments: How far ahead of travel direction to check for obstacles. -// Return value: Which MOID was detected as obstacle. g_NoMOID means nothing was detected. - - MOID DetectObstacle(float distance); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AutoStabilizing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether this has the means and will try to right itself, or if -// that's up to the Controller to do. -// Arguments: None. -// Return value: Wheter this will try to auto stabilize. - - bool AutoStabilizing() override { return true; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreControllerUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void PreControllerUpdate() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMaxPassengers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The recomended, not absolute, maximum number of actors that fit in the -// invetory. Used by the activity AI. -// Arguments: None. -// Return value: An integer with the recomended number of actors that fit in the craft. -// Default is four. - - int GetMaxPassengers() const override { return m_MaxPassengers > -1 ? m_MaxPassengers : 4; } - - - /// - /// Gets the right side thruster of this ACDropship. - /// - /// A pointer to the right side thruster of this ACDropship. Ownership is NOT transferred. - AEmitter * GetRightThruster() const { return m_pRThruster; } - - /// - /// Sets the right side thruster for this ACDropship. - /// - /// The new thruster to use. - void SetRightThruster(AEmitter *newThruster); - - /// - /// Gets the left side thruster of this ACDropship. - /// - /// A pointer to the left side thruster of this ACDropship. Ownership is NOT transferred. - AEmitter * GetLeftThruster() const { return m_pLThruster; } - - /// - /// Sets the left side thruster for this ACDropship. - /// - /// The new thruster to use. - void SetLeftThruster(AEmitter *newThruster); - - /// - /// Gets the right side secondary thruster of this ACDropship. - /// - /// A pointer to the right side secondary thruster of this ACDropship. Ownership is NOT transferred. - AEmitter * GetURightThruster() const { return m_pURThruster; } - - /// - /// Sets the right side secondary thruster for this ACDropship. - /// - /// The new thruster to use. - void SetURightThruster(AEmitter *newThruster); - - /// - /// Gets the left side secondary thruster of this ACDropship. - /// - /// A pointer to the left side secondary thruster of this ACDropship. Ownership is NOT transferred. - AEmitter * GetULeftThruster() const { return m_pULThruster; } - - /// - /// Sets the left side secondary thruster for this ACDropship. - /// - /// The new thruster to use. - void SetULeftThruster(AEmitter *newThruster); - - /// - /// Gets the left side hatch of this ACDropship. - /// - /// A pointer to the left side hatch of this ACDropship. Ownership is NOT transferred. - Attachable * GetLeftHatch() const { return m_pLHatch; } - - /// - /// Sets the left side hatch for this ACDropship. - /// - /// The new hatch to use. - void SetLeftHatch(Attachable *newHatch); - - /// - /// Gets the right side hatch of this ACDropship. - /// - /// A pointer to the right side hatch of this ACDropship. Ownership is NOT transferred. - Attachable * GetRightHatch() const { return m_pRHatch; } - - /// - /// Sets the right side hatch for this ACDropship. - /// - /// The new hatch to use. - void SetRightHatch(Attachable *newHatch); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMaxEngineAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get max engine rotation angle in degrees. -// Arguments: None. -// Return value: Max engine angle in degrees. - - float GetMaxEngineAngle() const { return m_MaxEngineAngle; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetMaxEngineAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets max engine rotation angle in degrees. -// Arguments: Max engine angle in degrees. -// Return value: None. - - void SetMaxEngineAngle(float newAngle) { m_MaxEngineAngle = newAngle; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLateralControlSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the abstract rate of LateralControl change. Default is 6 -// Arguments: None. -// Return value: Current lateral control speed value. - - float GetLateralControlSpeed() const { return m_LateralControlSpeed; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetLateralControlSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the abstract rate of LateralControl change. Default is 6 -// Arguments: New lateral control speed value. -// Return value: None. - - void SetLateralControlSpeed(float newSpeed) { m_LateralControl = newSpeed; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLateralControl -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets lateral control value -1.0 to 1.0 control of sideways movement. 0 means try to stand still in X. -// Arguments: None. -// Return value: Current lateral control value. - - float GetLateralControl() const { return m_LateralControl; } - - /// - /// Gets the modifier for height at which this ACDropship should hover above terrain. - /// - /// The modifier for height at which this ACDropship should hover above terrain. - float GetHoverHeightModifier() const { return m_HoverHeightModifier; } - - /// - /// Sets the modifier for height at which this ACDropship should hover above terrain. - /// - /// The new modifier for height at which this ACDropship should hover above terrain. - void SetHoverHeightModifier(float newHoverHeightModifier) { m_HoverHeightModifier = newHoverHeightModifier; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - - // Member variables - static Entity::ClassInfo m_sClass; - // Body AtomGroups. - AtomGroup *m_pBodyAG; - // Thruster emitters. - //TODO when this class is cleaned up, these and their getters and setters should probably be renamed (I'd argue the lua bindings should be broken to match but that's debatable). L and R should be Left and Right and they should probably be Primary and Secondary. - AEmitter *m_pRThruster; - AEmitter *m_pLThruster; - AEmitter *m_pURThruster; - AEmitter *m_pULThruster; - - // Hatch doors - Attachable *m_pRHatch; - Attachable *m_pLHatch; - // How much the hatch doors rotate to open - Matrix m_HatchSwingRange; - // From 0 to 1.0, the state of hatch door openness - float m_HatchOpeness; - - // -1.0 to 1.0 control of sideways movement. 0 means try to stand still in X. - float m_LateralControl; - // Abstract speed at which Lateralcontroll is changed - float m_LateralControlSpeed; - - // Automatically stabilize the craft with the upper thrusters? Defaults to yes. - int m_AutoStabilize; - - // Maximum engine rotation in degrees - float m_MaxEngineAngle; - - float m_HoverHeightModifier; //!< The modifier for the height at which this ACDropShip should hover above terrain when releasing its cargo. Used in cpp and Lua AI. - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ACDropShip, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - ACDropShip(const ACDropShip &reference) = delete; - ACDropShip & operator=(const ACDropShip &rhs) = delete; - -}; +namespace RTE { + + class Attachable; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: ACDropShip + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A hovering craft, with two engines on each attached on each end which + // tilt independently of the body to achieve steering. + // Parent(s): ACraft. + // Class history: 12/13/2006 ACDropShip created. + + class ACDropShip : public ACraft { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(ACDropShip); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: ACDropShip + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a ACDropShip object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + ACDropShip() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~ACDropShip + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a ACDropShip object before deletion + // from system memory. + // Arguments: None. + + ~ACDropShip() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ACDropShip object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ACDropShip to be identical to another, by deep copy. + // Arguments: A reference to the ACDropShip to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const ACDropShip& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire ACDropShip, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + ACraft::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAltitude + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the altitide of this' pos (or appropriate low point) over the + // terrain, in pixels. + // Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. + // The accuracy within which measurement is acceptable. Higher number + // here means less calculation. + // Return value: The rough altitude over the terrain, in pixels. + + float GetAltitude(int max = 0, int accuracy = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DetectObstacle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks for obstacles in the travel direction. + // Arguments: How far ahead of travel direction to check for obstacles. + // Return value: Which MOID was detected as obstacle. g_NoMOID means nothing was detected. + + MOID DetectObstacle(float distance); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AutoStabilizing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether this has the means and will try to right itself, or if + // that's up to the Controller to do. + // Arguments: None. + // Return value: Wheter this will try to auto stabilize. + + bool AutoStabilizing() override { return true; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreControllerUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void PreControllerUpdate() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMaxPassengers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The recomended, not absolute, maximum number of actors that fit in the + // invetory. Used by the activity AI. + // Arguments: None. + // Return value: An integer with the recomended number of actors that fit in the craft. + // Default is four. + + int GetMaxPassengers() const override { return m_MaxPassengers > -1 ? m_MaxPassengers : 4; } + + /// + /// Gets the right side thruster of this ACDropship. + /// + /// A pointer to the right side thruster of this ACDropship. Ownership is NOT transferred. + AEmitter* GetRightThruster() const { return m_pRThruster; } + + /// + /// Sets the right side thruster for this ACDropship. + /// + /// The new thruster to use. + void SetRightThruster(AEmitter* newThruster); + + /// + /// Gets the left side thruster of this ACDropship. + /// + /// A pointer to the left side thruster of this ACDropship. Ownership is NOT transferred. + AEmitter* GetLeftThruster() const { return m_pLThruster; } + + /// + /// Sets the left side thruster for this ACDropship. + /// + /// The new thruster to use. + void SetLeftThruster(AEmitter* newThruster); + + /// + /// Gets the right side secondary thruster of this ACDropship. + /// + /// A pointer to the right side secondary thruster of this ACDropship. Ownership is NOT transferred. + AEmitter* GetURightThruster() const { return m_pURThruster; } + + /// + /// Sets the right side secondary thruster for this ACDropship. + /// + /// The new thruster to use. + void SetURightThruster(AEmitter* newThruster); + + /// + /// Gets the left side secondary thruster of this ACDropship. + /// + /// A pointer to the left side secondary thruster of this ACDropship. Ownership is NOT transferred. + AEmitter* GetULeftThruster() const { return m_pULThruster; } + + /// + /// Sets the left side secondary thruster for this ACDropship. + /// + /// The new thruster to use. + void SetULeftThruster(AEmitter* newThruster); + + /// + /// Gets the left side hatch of this ACDropship. + /// + /// A pointer to the left side hatch of this ACDropship. Ownership is NOT transferred. + Attachable* GetLeftHatch() const { return m_pLHatch; } + + /// + /// Sets the left side hatch for this ACDropship. + /// + /// The new hatch to use. + void SetLeftHatch(Attachable* newHatch); + + /// + /// Gets the right side hatch of this ACDropship. + /// + /// A pointer to the right side hatch of this ACDropship. Ownership is NOT transferred. + Attachable* GetRightHatch() const { return m_pRHatch; } + + /// + /// Sets the right side hatch for this ACDropship. + /// + /// The new hatch to use. + void SetRightHatch(Attachable* newHatch); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMaxEngineAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get max engine rotation angle in degrees. + // Arguments: None. + // Return value: Max engine angle in degrees. + + float GetMaxEngineAngle() const { return m_MaxEngineAngle; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetMaxEngineAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets max engine rotation angle in degrees. + // Arguments: Max engine angle in degrees. + // Return value: None. + + void SetMaxEngineAngle(float newAngle) { m_MaxEngineAngle = newAngle; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLateralControlSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the abstract rate of LateralControl change. Default is 6 + // Arguments: None. + // Return value: Current lateral control speed value. + + float GetLateralControlSpeed() const { return m_LateralControlSpeed; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetLateralControlSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the abstract rate of LateralControl change. Default is 6 + // Arguments: New lateral control speed value. + // Return value: None. + + void SetLateralControlSpeed(float newSpeed) { m_LateralControl = newSpeed; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLateralControl + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets lateral control value -1.0 to 1.0 control of sideways movement. 0 means try to stand still in X. + // Arguments: None. + // Return value: Current lateral control value. + + float GetLateralControl() const { return m_LateralControl; } + + /// + /// Gets the modifier for height at which this ACDropship should hover above terrain. + /// + /// The modifier for height at which this ACDropship should hover above terrain. + float GetHoverHeightModifier() const { return m_HoverHeightModifier; } + + /// + /// Sets the modifier for height at which this ACDropship should hover above terrain. + /// + /// The new modifier for height at which this ACDropship should hover above terrain. + void SetHoverHeightModifier(float newHoverHeightModifier) { m_HoverHeightModifier = newHoverHeightModifier; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + // Body AtomGroups. + AtomGroup* m_pBodyAG; + // Thruster emitters. + // TODO when this class is cleaned up, these and their getters and setters should probably be renamed (I'd argue the lua bindings should be broken to match but that's debatable). L and R should be Left and Right and they should probably be Primary and Secondary. + AEmitter* m_pRThruster; + AEmitter* m_pLThruster; + AEmitter* m_pURThruster; + AEmitter* m_pULThruster; + + // Hatch doors + Attachable* m_pRHatch; + Attachable* m_pLHatch; + // How much the hatch doors rotate to open + Matrix m_HatchSwingRange; + // From 0 to 1.0, the state of hatch door openness + float m_HatchOpeness; + + // -1.0 to 1.0 control of sideways movement. 0 means try to stand still in X. + float m_LateralControl; + // Abstract speed at which Lateralcontroll is changed + float m_LateralControlSpeed; + + // Automatically stabilize the craft with the upper thrusters? Defaults to yes. + int m_AutoStabilize; + + // Maximum engine rotation in degrees + float m_MaxEngineAngle; + + float m_HoverHeightModifier; //!< The modifier for the height at which this ACDropShip should hover above terrain when releasing its cargo. Used in cpp and Lua AI. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ACDropShip, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + ACDropShip(const ACDropShip& reference) = delete; + ACDropShip& operator=(const ACDropShip& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/ACRocket.cpp b/Source/Entities/ACRocket.cpp index a1eb56a492..7219d80b4c 100644 --- a/Source/Entities/ACRocket.cpp +++ b/Source/Entities/ACRocket.cpp @@ -7,11 +7,9 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files - #include "ACRocket.h" #include "AtomGroup.h" #include "Attachable.h" @@ -27,546 +25,616 @@ namespace RTE { -ConcreteClassInfo(ACRocket, ACraft, 10); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ACRocket, effectively -// resetting the members of this abstraction level only. - -void ACRocket::Clear() -{ -// m_pCapsule = 0; - m_pRLeg = 0; - m_pLLeg = 0; - m_pBodyAG = 0; - m_pRFootGroup = 0; - m_pLFootGroup = 0; - m_pMThruster = 0; - m_pRThruster = 0; - m_pLThruster = 0; - m_pURThruster = 0; - m_pULThruster = 0; - m_GearState = RAISED; - for (int i = 0; i < GearStateCount; ++i) { - m_Paths[RIGHT][i].Reset(); - m_Paths[LEFT][i].Reset(); - m_Paths[RIGHT][i].Terminate(); - m_Paths[LEFT][i].Terminate(); - } - m_MaxGimbalAngle = 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ACRocket object ready for use. - -int ACRocket::Create() -{ - // Read all the properties - if (ACraft::Create() < 0) - return -1; - - // Save the AtomGroup read in by MOSRotating, as we are going to make it - // into a composite group, and want to have the base body stored for reference. - m_pBodyAG = dynamic_cast(m_pAtomGroup->Clone()); + ConcreteClassInfo(ACRocket, ACraft, 10); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ACRocket, effectively + // resetting the members of this abstraction level only. + + void ACRocket::Clear() { + // m_pCapsule = 0; + m_pRLeg = 0; + m_pLLeg = 0; + m_pBodyAG = 0; + m_pRFootGroup = 0; + m_pLFootGroup = 0; + m_pMThruster = 0; + m_pRThruster = 0; + m_pLThruster = 0; + m_pURThruster = 0; + m_pULThruster = 0; + m_GearState = RAISED; + for (int i = 0; i < GearStateCount; ++i) { + m_Paths[RIGHT][i].Reset(); + m_Paths[LEFT][i].Reset(); + m_Paths[RIGHT][i].Terminate(); + m_Paths[LEFT][i].Terminate(); + } + m_MaxGimbalAngle = 0; + } - // Mirror the limb paths - for (int i = 0; i < GearStateCount; ++i) - { - m_Paths[LEFT][i].Create(m_Paths[RIGHT][i]); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ACRocket object ready for use. - // Override the body animation mode - m_SpriteAnimMode = PINGPONGOPENCLOSE; + int ACRocket::Create() { + // Read all the properties + if (ACraft::Create() < 0) + return -1; - return 0; -} + // Save the AtomGroup read in by MOSRotating, as we are going to make it + // into a composite group, and want to have the base body stored for reference. + m_pBodyAG = dynamic_cast(m_pAtomGroup->Clone()); + // Mirror the limb paths + for (int i = 0; i < GearStateCount; ++i) { + m_Paths[LEFT][i].Create(m_Paths[RIGHT][i]); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a ACRocket to be identical to another, by deep copy. - -int ACRocket::Create(const ACRocket &reference) { - if (reference.m_pRLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRLeg->GetUniqueID()); } - if (reference.m_pLLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLLeg->GetUniqueID()); } - if (reference.m_pMThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pMThruster->GetUniqueID()); } - if (reference.m_pRThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRThruster->GetUniqueID()); } - if (reference.m_pLThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLThruster->GetUniqueID()); } - if (reference.m_pURThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pURThruster->GetUniqueID()); } - if (reference.m_pULThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pULThruster->GetUniqueID()); } - - ACraft::Create(reference); - - if (reference.m_pRLeg) { SetRightLeg(dynamic_cast(reference.m_pRLeg->Clone())); } - if (reference.m_pLLeg) { SetLeftLeg(dynamic_cast(reference.m_pLLeg->Clone())); } - if (reference.m_pMThruster) { SetMainThruster(dynamic_cast(reference.m_pMThruster->Clone())); } - if (reference.m_pRThruster) { SetRightThruster(dynamic_cast(reference.m_pRThruster->Clone())); } - if (reference.m_pLThruster) { SetLeftThruster(dynamic_cast(reference.m_pLThruster->Clone())); } - if (reference.m_pURThruster) { SetURightThruster(dynamic_cast(reference.m_pURThruster->Clone())); } - if (reference.m_pULThruster) { SetULeftThruster(dynamic_cast(reference.m_pULThruster->Clone())); } - - m_pBodyAG = dynamic_cast(reference.m_pBodyAG->Clone()); - m_pBodyAG->SetOwner(this); - - if (reference.m_pLFootGroup) { - m_pLFootGroup = dynamic_cast(reference.m_pLFootGroup->Clone()); - } else if (m_pLLeg) { - m_pLFootGroup = m_pLLeg->GetFootGroupFromFootAtomGroup(); - } - if (m_pLFootGroup) { - m_pLFootGroup->SetOwner(this); - } + // Override the body animation mode + m_SpriteAnimMode = PINGPONGOPENCLOSE; - if (reference.m_pRFootGroup) { - m_pRFootGroup = dynamic_cast(reference.m_pRFootGroup->Clone()); - } else if (m_pRLeg) { - m_pRFootGroup = m_pRLeg->GetFootGroupFromFootAtomGroup(); - } - if (m_pRFootGroup) { - m_pRFootGroup->SetOwner(this); + return 0; } - m_GearState = reference.m_GearState; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ACRocket to be identical to another, by deep copy. - for (int i = 0; i < GearStateCount; ++i) { - m_Paths[RIGHT][i].Create(reference.m_Paths[RIGHT][i]); - m_Paths[LEFT][i].Create(reference.m_Paths[LEFT][i]); - } + int ACRocket::Create(const ACRocket& reference) { + if (reference.m_pRLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRLeg->GetUniqueID()); + } + if (reference.m_pLLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLLeg->GetUniqueID()); + } + if (reference.m_pMThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pMThruster->GetUniqueID()); + } + if (reference.m_pRThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRThruster->GetUniqueID()); + } + if (reference.m_pLThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLThruster->GetUniqueID()); + } + if (reference.m_pURThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pURThruster->GetUniqueID()); + } + if (reference.m_pULThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pULThruster->GetUniqueID()); + } - m_MaxGimbalAngle = reference.m_MaxGimbalAngle; + ACraft::Create(reference); - return 0; -} + if (reference.m_pRLeg) { + SetRightLeg(dynamic_cast(reference.m_pRLeg->Clone())); + } + if (reference.m_pLLeg) { + SetLeftLeg(dynamic_cast(reference.m_pLLeg->Clone())); + } + if (reference.m_pMThruster) { + SetMainThruster(dynamic_cast(reference.m_pMThruster->Clone())); + } + if (reference.m_pRThruster) { + SetRightThruster(dynamic_cast(reference.m_pRThruster->Clone())); + } + if (reference.m_pLThruster) { + SetLeftThruster(dynamic_cast(reference.m_pLThruster->Clone())); + } + if (reference.m_pURThruster) { + SetURightThruster(dynamic_cast(reference.m_pURThruster->Clone())); + } + if (reference.m_pULThruster) { + SetULeftThruster(dynamic_cast(reference.m_pULThruster->Clone())); + } + m_pBodyAG = dynamic_cast(reference.m_pBodyAG->Clone()); + m_pBodyAG->SetOwner(this); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int ACRocket::ReadProperty(const std::string_view &propName, Reader &reader) { - StartPropertyList(return ACraft::ReadProperty(propName, reader)); - - MatchForwards("RLeg") MatchProperty("RightLeg", { SetRightLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("LLeg") MatchProperty("LeftLeg", { SetLeftLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("RFootGroup") MatchProperty("RightFootGroup", { - delete m_pRFootGroup; - m_pRFootGroup = new AtomGroup(); - reader >> m_pRFootGroup; - m_pRFootGroup->SetOwner(this); - }); - MatchForwards("LFootGroup") MatchProperty("LeftFootGroup", { - delete m_pLFootGroup; - m_pLFootGroup = new AtomGroup(); - reader >> m_pLFootGroup; - m_pLFootGroup->SetOwner(this); - }); - MatchForwards("MThruster") MatchProperty("MainThruster", { SetMainThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("RThruster") MatchProperty("RightThruster", { SetRightThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("LThruster") MatchProperty("LeftThruster", { SetLeftThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("URThruster") MatchProperty("UpRightThruster", { SetURightThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("ULThruster") MatchProperty("UpLeftThruster", { SetULeftThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("RaisedGearLimbPath", { reader >> m_Paths[RIGHT][RAISED]; }); - MatchProperty("LoweredGearLimbPath", { reader >> m_Paths[RIGHT][LOWERED]; }); - MatchProperty("LoweringGearLimbPath", { reader >> m_Paths[RIGHT][LOWERING]; }); - MatchProperty("RaisingGearLimbPath", { reader >> m_Paths[RIGHT][RAISING]; }); - MatchProperty("MaxGimbalAngle", { - reader >> m_MaxGimbalAngle; - m_MaxGimbalAngle *= (c_PI / 180.0F); - }); - - EndPropertyList; -} + if (reference.m_pLFootGroup) { + m_pLFootGroup = dynamic_cast(reference.m_pLFootGroup->Clone()); + } else if (m_pLLeg) { + m_pLFootGroup = m_pLLeg->GetFootGroupFromFootAtomGroup(); + } + if (m_pLFootGroup) { + m_pLFootGroup->SetOwner(this); + } + if (reference.m_pRFootGroup) { + m_pRFootGroup = dynamic_cast(reference.m_pRFootGroup->Clone()); + } else if (m_pRLeg) { + m_pRFootGroup = m_pRLeg->GetFootGroupFromFootAtomGroup(); + } + if (m_pRFootGroup) { + m_pRFootGroup->SetOwner(this); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this ACRocket with a Writer for -// later recreation with Create(Reader &reader); - -int ACRocket::Save(Writer &writer) const -{ - ACraft::Save(writer); - - writer.NewProperty("RLeg"); - writer << m_pRLeg; - writer.NewProperty("LLeg"); - writer << m_pLLeg; - writer.NewProperty("RFootGroup"); - writer << m_pRFootGroup; - writer.NewProperty("LFootGroup"); - writer << m_pLFootGroup; - writer.NewProperty("MThruster"); - writer << m_pMThruster; - writer.NewProperty("RThruster"); - writer << m_pRThruster; - writer.NewProperty("LThruster"); - writer << m_pLThruster; - writer.NewProperty("URThruster"); - writer << m_pURThruster; - writer.NewProperty("ULThruster"); - writer << m_pULThruster; - writer.NewProperty("RaisedGearLimbPath"); - writer << m_Paths[RIGHT][RAISED]; - writer.NewProperty("LoweredGearLimbPath"); - writer << m_Paths[RIGHT][LOWERED]; - writer.NewProperty("LoweringGearLimbPath"); - writer << m_Paths[RIGHT][LOWERING]; - writer.NewProperty("RaisingGearLimbPath"); - writer << m_Paths[RIGHT][RAISING]; - writer.NewProperty("MaxGimbalAngle"); - writer << m_MaxGimbalAngle / (c_PI / 180.0F); - - return 0; -} + m_GearState = reference.m_GearState; + for (int i = 0; i < GearStateCount; ++i) { + m_Paths[RIGHT][i].Create(reference.m_Paths[RIGHT][i]); + m_Paths[LEFT][i].Create(reference.m_Paths[LEFT][i]); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the ACRocket object. + m_MaxGimbalAngle = reference.m_MaxGimbalAngle; -void ACRocket::Destroy(bool notInherited) -{ - delete m_pBodyAG; - delete m_pRFootGroup; - delete m_pLFootGroup; + return 0; + } -// for (deque::iterator itr = m_WalkPaths.begin(); -// itr != m_WalkPaths.end(); ++itr) -// delete *itr; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int ACRocket::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return ACraft::ReadProperty(propName, reader)); + + MatchForwards("RLeg") MatchProperty("RightLeg", { SetRightLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("LLeg") MatchProperty("LeftLeg", { SetLeftLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("RFootGroup") MatchProperty("RightFootGroup", { + delete m_pRFootGroup; + m_pRFootGroup = new AtomGroup(); + reader >> m_pRFootGroup; + m_pRFootGroup->SetOwner(this); + }); + MatchForwards("LFootGroup") MatchProperty("LeftFootGroup", { + delete m_pLFootGroup; + m_pLFootGroup = new AtomGroup(); + reader >> m_pLFootGroup; + m_pLFootGroup->SetOwner(this); + }); + MatchForwards("MThruster") MatchProperty("MainThruster", { SetMainThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("RThruster") MatchProperty("RightThruster", { SetRightThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("LThruster") MatchProperty("LeftThruster", { SetLeftThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("URThruster") MatchProperty("UpRightThruster", { SetURightThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("ULThruster") MatchProperty("UpLeftThruster", { SetULeftThruster(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("RaisedGearLimbPath", { reader >> m_Paths[RIGHT][RAISED]; }); + MatchProperty("LoweredGearLimbPath", { reader >> m_Paths[RIGHT][LOWERED]; }); + MatchProperty("LoweringGearLimbPath", { reader >> m_Paths[RIGHT][LOWERING]; }); + MatchProperty("RaisingGearLimbPath", { reader >> m_Paths[RIGHT][RAISING]; }); + MatchProperty("MaxGimbalAngle", { + reader >> m_MaxGimbalAngle; + m_MaxGimbalAngle *= (c_PI / 180.0F); + }); + + EndPropertyList; + } - if (!notInherited) - ACraft::Destroy(); - Clear(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this ACRocket with a Writer for + // later recreation with Create(Reader &reader); + + int ACRocket::Save(Writer& writer) const { + ACraft::Save(writer); + + writer.NewProperty("RLeg"); + writer << m_pRLeg; + writer.NewProperty("LLeg"); + writer << m_pLLeg; + writer.NewProperty("RFootGroup"); + writer << m_pRFootGroup; + writer.NewProperty("LFootGroup"); + writer << m_pLFootGroup; + writer.NewProperty("MThruster"); + writer << m_pMThruster; + writer.NewProperty("RThruster"); + writer << m_pRThruster; + writer.NewProperty("LThruster"); + writer << m_pLThruster; + writer.NewProperty("URThruster"); + writer << m_pURThruster; + writer.NewProperty("ULThruster"); + writer << m_pULThruster; + writer.NewProperty("RaisedGearLimbPath"); + writer << m_Paths[RIGHT][RAISED]; + writer.NewProperty("LoweredGearLimbPath"); + writer << m_Paths[RIGHT][LOWERED]; + writer.NewProperty("LoweringGearLimbPath"); + writer << m_Paths[RIGHT][LOWERING]; + writer.NewProperty("RaisingGearLimbPath"); + writer << m_Paths[RIGHT][RAISING]; + writer.NewProperty("MaxGimbalAngle"); + writer << m_MaxGimbalAngle / (c_PI / 180.0F); + + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the ACRocket object. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAltitude -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the altitide of this' pos (or appropriate low point) over the -// terrain, in pixels. + void ACRocket::Destroy(bool notInherited) { + delete m_pBodyAG; + delete m_pRFootGroup; + delete m_pLFootGroup; -float ACRocket::GetAltitude(int max, int accuracy) -{ - // Use the main thruster's position as the position ot measure from - Vector pos; - if (m_pMThruster && m_pMThruster->IsAttached()) - pos = m_Pos + RotateOffset(m_pMThruster->GetParentOffset()); - else - pos = m_Pos; + // for (deque::iterator itr = m_WalkPaths.begin(); + // itr != m_WalkPaths.end(); ++itr) + // delete *itr; - return g_SceneMan.FindAltitude(pos, max, accuracy, true); -} + if (!notInherited) + ACraft::Destroy(); + Clear(); + } -////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAltitude + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the altitide of this' pos (or appropriate low point) over the + // terrain, in pixels. + + float ACRocket::GetAltitude(int max, int accuracy) { + // Use the main thruster's position as the position ot measure from + Vector pos; + if (m_pMThruster && m_pMThruster->IsAttached()) + pos = m_Pos + RotateOffset(m_pMThruster->GetParentOffset()); + else + pos = m_Pos; + + return g_SceneMan.FindAltitude(pos, max, accuracy, true); + } -void ACRocket::PreControllerUpdate() -{ - ACraft::PreControllerUpdate(); + ////////////////////////////////////////////////////////////////////////////////////////// - float deltaTime = g_TimerMan.GetDeltaTimeSecs(); + void ACRocket::PreControllerUpdate() { + ACraft::PreControllerUpdate(); - // Look/aim update, make the scanner point aftward if the rocket is falling - m_AimAngle = m_Vel.m_Y < 0 ? c_HalfPI : -c_HalfPI; + float deltaTime = g_TimerMan.GetDeltaTimeSecs(); - ///////////////////////////////// - // Controller update and handling + // Look/aim update, make the scanner point aftward if the rocket is falling + m_AimAngle = m_Vel.m_Y < 0 ? c_HalfPI : -c_HalfPI; - if ((m_Status == STABLE || m_Status == UNSTABLE) && !m_Controller.IsDisabled()) { - if (m_pMThruster) { - if (m_MaxGimbalAngle != 0) { m_pMThruster->SetInheritedRotAngleOffset(std::sin(m_Rotation.GetRadAngle()) * m_MaxGimbalAngle - c_HalfPI); } + ///////////////////////////////// + // Controller update and handling - if (m_Controller.IsState(MOVE_UP) || m_Controller.IsState(AIM_UP)) { - if (!m_pMThruster->IsEmitting()) { - m_pMThruster->TriggerBurst(); - m_ForceDeepCheck = true; + if ((m_Status == STABLE || m_Status == UNSTABLE) && !m_Controller.IsDisabled()) { + if (m_pMThruster) { + if (m_MaxGimbalAngle != 0) { + m_pMThruster->SetInheritedRotAngleOffset(std::sin(m_Rotation.GetRadAngle()) * m_MaxGimbalAngle - c_HalfPI); } - m_pMThruster->EnableEmission(true); - m_pMThruster->AlarmOnEmit(m_Team); - if (m_HatchState == OPEN) { + if (m_Controller.IsState(MOVE_UP) || m_Controller.IsState(AIM_UP)) { + if (!m_pMThruster->IsEmitting()) { + m_pMThruster->TriggerBurst(); + m_ForceDeepCheck = true; + } + m_pMThruster->EnableEmission(true); + m_pMThruster->AlarmOnEmit(m_Team); + + if (m_HatchState == OPEN) { + CloseHatch(); + m_HatchTimer.Reset(); + } + } else { + m_pMThruster->EnableEmission(false); + } + } + if (m_pULThruster || m_pURThruster) { + if (m_Controller.IsState(MOVE_DOWN) || m_Controller.IsState(AIM_DOWN)) { + if (m_pURThruster) { + if (!m_pURThruster->IsEmitting()) { + m_pURThruster->TriggerBurst(); + } + m_pURThruster->EnableEmission(true); + } + if (m_pULThruster) { + if (!m_pULThruster->IsEmitting()) { + m_pULThruster->TriggerBurst(); + } + m_pULThruster->EnableEmission(true); + } + } else { + if (m_pURThruster) { + m_pURThruster->EnableEmission(false); + } + if (m_pULThruster) { + m_pULThruster->EnableEmission(false); + } + } + } + if (m_pLThruster) { + if (m_Controller.IsState(MOVE_RIGHT)) { + if (!m_pLThruster->IsEmitting()) { + m_pLThruster->TriggerBurst(); + } + m_pLThruster->EnableEmission(true); + } else { + m_pLThruster->EnableEmission(false); + } + } + if (m_pRThruster) { + if (m_Controller.IsState(MOVE_LEFT)) { + if (!m_pRThruster->IsEmitting()) { + m_pRThruster->TriggerBurst(); + } + m_pRThruster->EnableEmission(true); + } else { + m_pRThruster->EnableEmission(false); + } + } + if (m_Controller.IsState(PRESS_FACEBUTTON)) { + if (m_HatchState == CLOSED) { + DropAllInventory(); + } else if (m_HatchState == OPEN) { CloseHatch(); - m_HatchTimer.Reset(); } - } else { + } + } else { + if (m_pMThruster) { m_pMThruster->EnableEmission(false); } - } - if (m_pULThruster || m_pURThruster) { - if (m_Controller.IsState(MOVE_DOWN) || m_Controller.IsState(AIM_DOWN)) { - if (m_pURThruster) { - if (!m_pURThruster->IsEmitting()) { m_pURThruster->TriggerBurst(); } - m_pURThruster->EnableEmission(true); + if (m_pRThruster) { + m_pRThruster->EnableEmission(false); + } + if (m_pLThruster) { + m_pLThruster->EnableEmission(false); + } + if (m_pURThruster) { + m_pURThruster->EnableEmission(false); + } + if (m_pULThruster) { + m_pULThruster->EnableEmission(false); + } + } + + /////////////////////////////////////////////////// + // Travel the landing gear AtomGroup:s + + if (m_pMThruster) { + m_GearState = m_pMThruster->IsEmitting() ? LandingGearState::RAISED : LandingGearState::LOWERED; + + m_Paths[RIGHT][m_GearState].SetHFlip(m_HFlipped); + m_Paths[LEFT][m_GearState].SetHFlip(!m_HFlipped); + + if (!m_LimbPushForcesAndCollisionsDisabled) { + if (m_pRLeg) { + m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pRLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHT][m_GearState], deltaTime, nullptr, true); } - if (m_pULThruster) { - if (!m_pULThruster->IsEmitting()) { m_pULThruster->TriggerBurst(); } - m_pULThruster->EnableEmission(true); + if (m_pLLeg) { + m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pLLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFT][m_GearState], deltaTime, nullptr, true); } } else { - if (m_pURThruster) { m_pURThruster->EnableEmission(false); } - if (m_pULThruster) { m_pULThruster->EnableEmission(false); } + if (m_pRLeg) { + m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pRLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHT][m_GearState], deltaTime, nullptr, true); + } + if (m_pLLeg) { + m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pLLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFT][m_GearState], deltaTime, nullptr, true); + } } } - if (m_pLThruster) { - if (m_Controller.IsState(MOVE_RIGHT)) { - if (!m_pLThruster->IsEmitting()) { m_pLThruster->TriggerBurst(); } - m_pLThruster->EnableEmission(true); - } else { - m_pLThruster->EnableEmission(false); + + ///////////////////////////////// + // Manage Attachable:s + if (m_pRLeg && m_pRLeg->IsAttached()) { + m_pRLeg->SetTargetPosition(m_pRFootGroup->GetLimbPos(m_HFlipped)); + } + + if (m_pLLeg && m_pLLeg->IsAttached()) { + m_pLLeg->SetTargetPosition(m_pLFootGroup->GetLimbPos(!m_HFlipped)); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void ACRocket::Update() { + ACraft::Update(); + + //////////////////////////////////////// + // Hatch Operation + + unsigned int lastFrame = m_FrameCount - 1; + + if (m_HatchState == OPENING) { + if (m_HatchTimer.GetElapsedSimTimeMS() <= m_HatchDelay && m_HatchDelay) + m_Frame = static_cast(static_cast(lastFrame) * (m_HatchTimer.GetElapsedSimTimeMS() / static_cast(m_HatchDelay))); + else { + m_Frame = lastFrame; + m_HatchState = OPEN; + DropAllInventory(); + } + } else if (m_HatchState == CLOSING) { + if (m_HatchTimer.GetElapsedSimTimeMS() <= m_HatchDelay && m_HatchDelay) + m_Frame = lastFrame - static_cast(static_cast(lastFrame) * (m_HatchTimer.GetElapsedSimTimeMS() / static_cast(m_HatchDelay))); + else { + m_Frame = 0; + m_HatchState = CLOSED; } } - if (m_pRThruster) { - if (m_Controller.IsState(MOVE_LEFT)) { - if (!m_pRThruster->IsEmitting()) { m_pRThruster->TriggerBurst(); } - m_pRThruster->EnableEmission(true); - } else { - m_pRThruster->EnableEmission(false); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACRocket::SetRightLeg(Leg* newLeg) { + if (m_pRLeg && m_pRLeg->IsAttached()) { + RemoveAndDeleteAttachable(m_pRLeg); + } + if (newLeg == nullptr) { + m_pRLeg = nullptr; + } else { + m_pRLeg = newLeg; + AddAttachable(newLeg); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Leg* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightLeg"); + dynamic_cast(parent)->SetRightLeg(castedAttachable); + }}); + + if (m_pRLeg->HasNoSetDamageMultiplier()) { + m_pRLeg->SetDamageMultiplier(1.0F); } } - if (m_Controller.IsState(PRESS_FACEBUTTON)) { - if (m_HatchState == CLOSED) { - DropAllInventory(); - } else if (m_HatchState == OPEN) { - CloseHatch(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACRocket::SetLeftLeg(Leg* newLeg) { + if (m_pLLeg && m_pLLeg->IsAttached()) { + RemoveAndDeleteAttachable(m_pLLeg); + } + if (newLeg == nullptr) { + m_pLLeg = nullptr; + } else { + m_pLLeg = newLeg; + AddAttachable(newLeg); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Leg* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftLeg"); + dynamic_cast(parent)->SetLeftLeg(castedAttachable); + }}); + + if (m_pLLeg->HasNoSetDamageMultiplier()) { + m_pLLeg->SetDamageMultiplier(1.0F); } + m_pLLeg->SetInheritsHFlipped(-1); } - } else { - if (m_pMThruster) { m_pMThruster->EnableEmission(false); } - if (m_pRThruster) { m_pRThruster->EnableEmission(false); } - if (m_pLThruster) { m_pLThruster->EnableEmission(false); } - if (m_pURThruster) { m_pURThruster->EnableEmission(false); } - if (m_pULThruster) { m_pULThruster->EnableEmission(false); } - } + } - /////////////////////////////////////////////////// - // Travel the landing gear AtomGroup:s + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (m_pMThruster) { - m_GearState = m_pMThruster->IsEmitting() ? LandingGearState::RAISED : LandingGearState::LOWERED; + void ACRocket::SetMainThruster(AEmitter* newThruster) { + if (m_pMThruster && m_pMThruster->IsAttached()) { + RemoveAndDeleteAttachable(m_pMThruster); + } + if (newThruster == nullptr) { + m_pMThruster = nullptr; + } else { + m_pMThruster = newThruster; + AddAttachable(newThruster); - m_Paths[RIGHT][m_GearState].SetHFlip(m_HFlipped); - m_Paths[LEFT][m_GearState].SetHFlip(!m_HFlipped); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEmitter* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetMainThruster"); + dynamic_cast(parent)->SetMainThruster(castedAttachable); + }}); - if (!m_LimbPushForcesAndCollisionsDisabled) { - if (m_pRLeg) { m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pRLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHT][m_GearState], deltaTime, nullptr, true); } - if (m_pLLeg) { m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pLLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFT][m_GearState], deltaTime, nullptr, true); } + if (m_pMThruster->HasNoSetDamageMultiplier()) { + m_pMThruster->SetDamageMultiplier(1.0F); + } + m_pMThruster->SetInheritedRotAngleOffset(-c_HalfPI); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACRocket::SetRightThruster(AEmitter* newThruster) { + if (m_pRThruster && m_pRThruster->IsAttached()) { + RemoveAndDeleteAttachable(m_pRThruster); + } + if (newThruster == nullptr) { + m_pRThruster = nullptr; } else { - if (m_pRLeg) { m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pRLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHT][m_GearState], deltaTime, nullptr, true); } - if (m_pLLeg) { m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pLLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFT][m_GearState], deltaTime, nullptr, true); } + m_pRThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEmitter* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightThruster"); + dynamic_cast(parent)->SetRightThruster(castedAttachable); + }}); + + if (m_pRThruster->HasNoSetDamageMultiplier()) { + m_pRThruster->SetDamageMultiplier(1.0F); + } + m_pRThruster->SetInheritedRotAngleOffset(c_EighthPI); } } - ///////////////////////////////// - // Manage Attachable:s - if (m_pRLeg && m_pRLeg->IsAttached()) { - m_pRLeg->SetTargetPosition(m_pRFootGroup->GetLimbPos(m_HFlipped)); - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (m_pLLeg && m_pLLeg->IsAttached()) { - m_pLLeg->SetTargetPosition(m_pLFootGroup->GetLimbPos(!m_HFlipped)); - } -} + void ACRocket::SetLeftThruster(AEmitter* newThruster) { + if (m_pLThruster && m_pLThruster->IsAttached()) { + RemoveAndDeleteAttachable(m_pLThruster); + } + if (newThruster == nullptr) { + m_pLThruster = nullptr; + } else { + m_pLThruster = newThruster; + AddAttachable(newThruster); -////////////////////////////////////////////////////////////////////////////////////////// + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEmitter* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftThruster"); + dynamic_cast(parent)->SetLeftThruster(castedAttachable); + }}); + + if (m_pLThruster->HasNoSetDamageMultiplier()) { + m_pLThruster->SetDamageMultiplier(1.0F); + } + m_pLThruster->SetInheritedRotAngleOffset(c_PI - c_EighthPI); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACRocket::SetURightThruster(AEmitter* newThruster) { + if (m_pURThruster && m_pURThruster->IsAttached()) { + RemoveAndDeleteAttachable(m_pURThruster); + } + if (newThruster == nullptr) { + m_pURThruster = nullptr; + } else { + m_pURThruster = newThruster; + AddAttachable(newThruster); -void ACRocket::Update() -{ - ACraft::Update(); - - //////////////////////////////////////// - // Hatch Operation - - unsigned int lastFrame = m_FrameCount - 1; - - if (m_HatchState == OPENING) { - if (m_HatchTimer.GetElapsedSimTimeMS() <= m_HatchDelay && m_HatchDelay) - m_Frame = static_cast(static_cast(lastFrame) * (m_HatchTimer.GetElapsedSimTimeMS() / static_cast(m_HatchDelay))); - else - { - m_Frame = lastFrame; - m_HatchState = OPEN; - DropAllInventory(); - } - } - else if (m_HatchState == CLOSING) { - if (m_HatchTimer.GetElapsedSimTimeMS() <= m_HatchDelay && m_HatchDelay) - m_Frame = lastFrame - static_cast(static_cast(lastFrame) * (m_HatchTimer.GetElapsedSimTimeMS() / static_cast(m_HatchDelay))); - else - { - m_Frame = 0; - m_HatchState = CLOSED; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACRocket::SetRightLeg(Leg *newLeg) { - if (m_pRLeg && m_pRLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pRLeg); } - if (newLeg == nullptr) { - m_pRLeg = nullptr; - } else { - m_pRLeg = newLeg; - AddAttachable(newLeg); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Leg *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightLeg"); - dynamic_cast(parent)->SetRightLeg(castedAttachable); - }}); - - if (m_pRLeg->HasNoSetDamageMultiplier()) { m_pRLeg->SetDamageMultiplier(1.0F); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACRocket::SetLeftLeg(Leg *newLeg) { - if (m_pLLeg && m_pLLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pLLeg); } - if (newLeg == nullptr) { - m_pLLeg = nullptr; - } else { - m_pLLeg = newLeg; - AddAttachable(newLeg); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Leg *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftLeg"); - dynamic_cast(parent)->SetLeftLeg(castedAttachable); - }}); - - if (m_pLLeg->HasNoSetDamageMultiplier()) { m_pLLeg->SetDamageMultiplier(1.0F); } - m_pLLeg->SetInheritsHFlipped(-1); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACRocket::SetMainThruster(AEmitter *newThruster) { - if (m_pMThruster && m_pMThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pMThruster); } - if (newThruster == nullptr) { - m_pMThruster = nullptr; - } else { - m_pMThruster = newThruster; - AddAttachable(newThruster); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEmitter *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetMainThruster"); - dynamic_cast(parent)->SetMainThruster(castedAttachable); - }}); - - if (m_pMThruster->HasNoSetDamageMultiplier()) { m_pMThruster->SetDamageMultiplier(1.0F); } - m_pMThruster->SetInheritedRotAngleOffset(-c_HalfPI); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACRocket::SetRightThruster(AEmitter *newThruster) { - if (m_pRThruster && m_pRThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pRThruster); } - if (newThruster == nullptr) { - m_pRThruster = nullptr; - } else { - m_pRThruster = newThruster; - AddAttachable(newThruster); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEmitter *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightThruster"); - dynamic_cast(parent)->SetRightThruster(castedAttachable); - }}); - - if (m_pRThruster->HasNoSetDamageMultiplier()) { m_pRThruster->SetDamageMultiplier(1.0F); } - m_pRThruster->SetInheritedRotAngleOffset(c_EighthPI); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACRocket::SetLeftThruster(AEmitter *newThruster) { - if (m_pLThruster && m_pLThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pLThruster); } - if (newThruster == nullptr) { - m_pLThruster = nullptr; - } else { - m_pLThruster = newThruster; - AddAttachable(newThruster); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEmitter *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftThruster"); - dynamic_cast(parent)->SetLeftThruster(castedAttachable); - }}); - - if (m_pLThruster->HasNoSetDamageMultiplier()) { m_pLThruster->SetDamageMultiplier(1.0F); } - m_pLThruster->SetInheritedRotAngleOffset(c_PI - c_EighthPI); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACRocket::SetURightThruster(AEmitter *newThruster) { - if (m_pURThruster && m_pURThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pURThruster); } - if (newThruster == nullptr) { - m_pURThruster = nullptr; - } else { - m_pURThruster = newThruster; - AddAttachable(newThruster); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEmitter *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetURightThruster"); - dynamic_cast(parent)->SetURightThruster(castedAttachable); - }}); - - if (m_pURThruster->HasNoSetDamageMultiplier()) { m_pURThruster->SetDamageMultiplier(1.0F); } - m_pURThruster->SetInheritedRotAngleOffset(c_HalfPI - c_EighthPI); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACRocket::SetULeftThruster(AEmitter *newThruster) { - if (m_pULThruster && m_pULThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pULThruster); } - if (newThruster == nullptr) { - m_pULThruster = nullptr; - } else { - m_pULThruster = newThruster; - AddAttachable(newThruster); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEmitter *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetULeftThruster"); - dynamic_cast(parent)->SetULeftThruster(castedAttachable); - }}); - - if (m_pULThruster->HasNoSetDamageMultiplier()) { m_pULThruster->SetDamageMultiplier(1.0F); } - m_pULThruster->SetInheritedRotAngleOffset(c_HalfPI + c_EighthPI); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACRocket::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { - ACraft::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawHandAndFootGroupVisualizations()) { - m_pRFootGroup->Draw(pTargetBitmap, targetPos, true, 13); - m_pLFootGroup->Draw(pTargetBitmap, targetPos, true, 13); - } -} + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEmitter* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetURightThruster"); + dynamic_cast(parent)->SetURightThruster(castedAttachable); + }}); + + if (m_pURThruster->HasNoSetDamageMultiplier()) { + m_pURThruster->SetDamageMultiplier(1.0F); + } + m_pURThruster->SetInheritedRotAngleOffset(c_HalfPI - c_EighthPI); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACRocket::SetULeftThruster(AEmitter* newThruster) { + if (m_pULThruster && m_pULThruster->IsAttached()) { + RemoveAndDeleteAttachable(m_pULThruster); + } + if (newThruster == nullptr) { + m_pULThruster = nullptr; + } else { + m_pULThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEmitter* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetULeftThruster"); + dynamic_cast(parent)->SetULeftThruster(castedAttachable); + }}); + + if (m_pULThruster->HasNoSetDamageMultiplier()) { + m_pULThruster->SetDamageMultiplier(1.0F); + } + m_pULThruster->SetInheritedRotAngleOffset(c_HalfPI + c_EighthPI); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACRocket::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { + ACraft::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + + if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawHandAndFootGroupVisualizations()) { + m_pRFootGroup->Draw(pTargetBitmap, targetPos, true, 13); + m_pLFootGroup->Draw(pTargetBitmap, targetPos, true, 13); + } + } } // namespace RTE diff --git a/Source/Entities/ACRocket.h b/Source/Entities/ACRocket.h index 191fb8d791..3c05a5399b 100644 --- a/Source/Entities/ACRocket.h +++ b/Source/Entities/ACRocket.h @@ -10,325 +10,304 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "ACraft.h" -namespace RTE -{ - -class Attachable; -class Arm; -class Leg; -//class LimbPath; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: ACRocket -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A rocket craft, with main engine and rotation of the whole body as a -// means of steering. -// Parent(s): ACraft. -// Class history: 09/02/2004 ARocket created. -// 12/13/2006 ARocket changed names to ACRocket, parent changed to ACraft - -class ACRocket : public ACraft { - friend struct EntityLuaBindings; - -enum LandingGearState -{ - RAISED = 0, - LOWERED, - LOWERING, - RAISING, - GearStateCount -}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(ACRocket); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: ACRocket -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a ACRocket object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - ACRocket() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~ACRocket -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a ACRocket object before deletion -// from system memory. -// Arguments: None. - - ~ACRocket() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ACRocket object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a ACRocket to be identical to another, by deep copy. -// Arguments: A reference to the ACRocket to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const ACRocket &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire ACRocket, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); ACraft::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAltitude -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the altitide of this' pos (or appropriate low point) over the -// terrain, in pixels. -// Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. -// The accuracy within which measurement is acceptable. Higher number -// here means less calculation. -// Return value: The rough altitude over the terrain, in pixels. - - float GetAltitude(int max = 0, int accuracy = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreControllerUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void PreControllerUpdate() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: Nosssssssne. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ACRocket's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMaxPassengers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The recomended, not absolute, maximum number of actors that fit in the -// invetory. Used by the activity AI. -// Arguments: None. -// Return value: An integer with the recomended number of actors that fit in the craft. -// Default is two. - - int GetMaxPassengers() const override { return m_MaxPassengers > -1 ? m_MaxPassengers : 2; } - - /// - /// Gets the right leg of this ACRocket. - /// - /// A pointer to the right Leg of this ACRocket. Ownership is NOT transferred. - Leg * GetRightLeg() const { return m_pRLeg; } - - /// - /// Sets the right Leg for this ACRocket. - /// - /// The new Leg to use. - void SetRightLeg(Leg *newLeg); - - /// - /// Gets the left Leg of this ACRocket. - /// - /// A pointer to the left Leg of this ACRocket. Ownership is NOT transferred. - Leg * GetLeftLeg() const { return m_pLLeg; } - - /// - /// Sets the left Leg for this ACRocket. - /// - /// The new Leg to use. - void SetLeftLeg(Leg *newLeg); - - /// - /// Gets the main thruster of this ACRocket. - /// - /// A pointer to the main thruster of this ACRocket. Ownership is NOT transferred. - AEmitter * GetMainThruster() const { return m_pMThruster; } - - /// - /// Sets the main thruster for this ACRocket. - /// - /// The new thruster to use. - void SetMainThruster(AEmitter *newThruster); - - /// - /// Gets the right side thruster of this ACRocket. - /// - /// A pointer to the right side thruster of this ACRocket. Ownership is NOT transferred. - AEmitter * GetRightThruster() const { return m_pRThruster; } - - /// - /// Sets the right side thruster for this ACRocket. - /// - /// The new thruster to use. - void SetRightThruster(AEmitter *newThruster); - - /// - /// Gets the left side thruster of this ACRocket. - /// - /// A pointer to the left side thruster of this ACRocket. Ownership is NOT transferred. - AEmitter * GetLeftThruster() const { return m_pLThruster; } - - /// - /// Sets the left side thruster for this ACRocket. - /// - /// The new thruster to use. - void SetLeftThruster(AEmitter *newThruster); - - /// - /// Gets the right side secondary thruster of this ACRocket. - /// - /// A pointer to the right side secondary thruster of this ACRocket. Ownership is NOT transferred. - AEmitter * GetURightThruster() const { return m_pURThruster; } - - /// - /// Sets the right side secondary thruster for this ACRocket. - /// - /// The new thruster to use. - void SetURightThruster(AEmitter *newThruster); - - /// - /// Gets the left side secondary thruster of this ACRocket. - /// - /// A pointer to the left side secondary thruster of this ACRocket. Ownership is NOT transferred. - AEmitter * GetULeftThruster() const { return m_pULThruster; } - - /// - /// Sets the left side secondary thruster for this ACRocket. - /// - /// The new thruster to use. - void SetULeftThruster(AEmitter *newThruster); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGearState -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the the landing gear state -// Arguments: None. -// Return value: Current landing gear state. - - unsigned int GetGearState() const { return m_GearState; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - - // Member variables - static Entity::ClassInfo m_sClass; - // Attached nosecone/capsule. -// Attachable *m_pCapsule; - // Right landing gear. - Leg *m_pRLeg; - // Left landing gear. - Leg *m_pLLeg; - // Body AtomGroups. - AtomGroup *m_pBodyAG; - // Limb AtomGroups. - AtomGroup *m_pRFootGroup; - AtomGroup *m_pLFootGroup; - // Thruster emitters. - AEmitter *m_pMThruster; - AEmitter *m_pRThruster; - AEmitter *m_pLThruster; - AEmitter *m_pURThruster; - AEmitter *m_pULThruster; - // Current landing gear action state. - unsigned int m_GearState; - // Limb paths for different movement states. - // [0] is for the right limbs, and [1] is for left. - LimbPath m_Paths[2][GearStateCount]; - float m_MaxGimbalAngle; //!< How much the main engine is able to tilt in order to stabilize the rocket. - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ACRocket, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - // Disallow the use of some implicit methods. - ACRocket(const ACRocket &reference) = delete; - ACRocket & operator=(const ACRocket &rhs) = delete; - -}; +namespace RTE { + + class Attachable; + class Arm; + class Leg; + // class LimbPath; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: ACRocket + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A rocket craft, with main engine and rotation of the whole body as a + // means of steering. + // Parent(s): ACraft. + // Class history: 09/02/2004 ARocket created. + // 12/13/2006 ARocket changed names to ACRocket, parent changed to ACraft + + class ACRocket : public ACraft { + friend struct EntityLuaBindings; + + enum LandingGearState { + RAISED = 0, + LOWERED, + LOWERING, + RAISING, + GearStateCount + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(ACRocket); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: ACRocket + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a ACRocket object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + ACRocket() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~ACRocket + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a ACRocket object before deletion + // from system memory. + // Arguments: None. + + ~ACRocket() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ACRocket object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ACRocket to be identical to another, by deep copy. + // Arguments: A reference to the ACRocket to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const ACRocket& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire ACRocket, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + ACraft::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAltitude + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the altitide of this' pos (or appropriate low point) over the + // terrain, in pixels. + // Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. + // The accuracy within which measurement is acceptable. Higher number + // here means less calculation. + // Return value: The rough altitude over the terrain, in pixels. + + float GetAltitude(int max = 0, int accuracy = 0) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreControllerUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void PreControllerUpdate() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: Nosssssssne. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ACRocket's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMaxPassengers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The recomended, not absolute, maximum number of actors that fit in the + // invetory. Used by the activity AI. + // Arguments: None. + // Return value: An integer with the recomended number of actors that fit in the craft. + // Default is two. + + int GetMaxPassengers() const override { return m_MaxPassengers > -1 ? m_MaxPassengers : 2; } + + /// + /// Gets the right leg of this ACRocket. + /// + /// A pointer to the right Leg of this ACRocket. Ownership is NOT transferred. + Leg* GetRightLeg() const { return m_pRLeg; } + + /// + /// Sets the right Leg for this ACRocket. + /// + /// The new Leg to use. + void SetRightLeg(Leg* newLeg); + + /// + /// Gets the left Leg of this ACRocket. + /// + /// A pointer to the left Leg of this ACRocket. Ownership is NOT transferred. + Leg* GetLeftLeg() const { return m_pLLeg; } + + /// + /// Sets the left Leg for this ACRocket. + /// + /// The new Leg to use. + void SetLeftLeg(Leg* newLeg); + + /// + /// Gets the main thruster of this ACRocket. + /// + /// A pointer to the main thruster of this ACRocket. Ownership is NOT transferred. + AEmitter* GetMainThruster() const { return m_pMThruster; } + + /// + /// Sets the main thruster for this ACRocket. + /// + /// The new thruster to use. + void SetMainThruster(AEmitter* newThruster); + + /// + /// Gets the right side thruster of this ACRocket. + /// + /// A pointer to the right side thruster of this ACRocket. Ownership is NOT transferred. + AEmitter* GetRightThruster() const { return m_pRThruster; } + + /// + /// Sets the right side thruster for this ACRocket. + /// + /// The new thruster to use. + void SetRightThruster(AEmitter* newThruster); + + /// + /// Gets the left side thruster of this ACRocket. + /// + /// A pointer to the left side thruster of this ACRocket. Ownership is NOT transferred. + AEmitter* GetLeftThruster() const { return m_pLThruster; } + + /// + /// Sets the left side thruster for this ACRocket. + /// + /// The new thruster to use. + void SetLeftThruster(AEmitter* newThruster); + + /// + /// Gets the right side secondary thruster of this ACRocket. + /// + /// A pointer to the right side secondary thruster of this ACRocket. Ownership is NOT transferred. + AEmitter* GetURightThruster() const { return m_pURThruster; } + + /// + /// Sets the right side secondary thruster for this ACRocket. + /// + /// The new thruster to use. + void SetURightThruster(AEmitter* newThruster); + + /// + /// Gets the left side secondary thruster of this ACRocket. + /// + /// A pointer to the left side secondary thruster of this ACRocket. Ownership is NOT transferred. + AEmitter* GetULeftThruster() const { return m_pULThruster; } + + /// + /// Sets the left side secondary thruster for this ACRocket. + /// + /// The new thruster to use. + void SetULeftThruster(AEmitter* newThruster); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGearState + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the the landing gear state + // Arguments: None. + // Return value: Current landing gear state. + + unsigned int GetGearState() const { return m_GearState; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + // Attached nosecone/capsule. + // Attachable *m_pCapsule; + // Right landing gear. + Leg* m_pRLeg; + // Left landing gear. + Leg* m_pLLeg; + // Body AtomGroups. + AtomGroup* m_pBodyAG; + // Limb AtomGroups. + AtomGroup* m_pRFootGroup; + AtomGroup* m_pLFootGroup; + // Thruster emitters. + AEmitter* m_pMThruster; + AEmitter* m_pRThruster; + AEmitter* m_pLThruster; + AEmitter* m_pURThruster; + AEmitter* m_pULThruster; + // Current landing gear action state. + unsigned int m_GearState; + // Limb paths for different movement states. + // [0] is for the right limbs, and [1] is for left. + LimbPath m_Paths[2][GearStateCount]; + float m_MaxGimbalAngle; //!< How much the main engine is able to tilt in order to stabilize the rocket. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ACRocket, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + ACRocket(const ACRocket& reference) = delete; + ACRocket& operator=(const ACRocket& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/ACrab.cpp b/Source/Entities/ACrab.cpp index 7a563ec958..3b742084b8 100644 --- a/Source/Entities/ACrab.cpp +++ b/Source/Entities/ACrab.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -35,1039 +34,1104 @@ namespace RTE { -ConcreteClassInfo(ACrab, Actor, 20); + ConcreteClassInfo(ACrab, Actor, 20); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ACrab, effectively + // resetting the members of this abstraction level only. + + void ACrab::Clear() { + m_pTurret = 0; + m_pLFGLeg = 0; + m_pLBGLeg = 0; + m_pRFGLeg = 0; + m_pRBGLeg = 0; + m_pLFGFootGroup = 0; + m_BackupLFGFootGroup = nullptr; + m_pLBGFootGroup = 0; + m_BackupLBGFootGroup = nullptr; + m_pRFGFootGroup = 0; + m_BackupRFGFootGroup = nullptr; + m_pRBGFootGroup = 0; + m_BackupRBGFootGroup = nullptr; + m_StrideSound = nullptr; + m_pJetpack = nullptr; + m_MoveState = STAND; + m_StrideFrame = false; + for (int side = 0; side < SIDECOUNT; ++side) { + for (int layer = 0; layer < LAYERCOUNT; ++layer) { + for (int state = 0; state < MOVEMENTSTATECOUNT; ++state) { + m_Paths[side][layer][state].Reset(); + m_Paths[side][layer][state].Terminate(); + } + } + m_StrideStart[side] = false; + // m_StrideTimer[side].Reset(); + } + m_Aiming = false; + + m_DeviceState = SCANNING; + m_SweepState = NOSWEEP; + m_DigState = NOTDIGGING; + m_JumpState = NOTJUMPING; + m_JumpTarget.Reset(); + m_JumpingRight = true; + m_DigTunnelEndPos.Reset(); + m_SweepCenterAimAngle = 0; + m_SweepRange = c_EighthPI; + m_DigTarget.Reset(); + m_FireTimer.Reset(); + m_SweepTimer.Reset(); + m_PatrolTimer.Reset(); + m_JumpTimer.Reset(); + m_AimRangeUpperLimit = -1; + m_AimRangeLowerLimit = -1; + m_LockMouseAimInput = false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ACrab object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ACrab, effectively -// resetting the members of this abstraction level only. - -void ACrab::Clear() -{ - m_pTurret = 0; - m_pLFGLeg = 0; - m_pLBGLeg = 0; - m_pRFGLeg = 0; - m_pRBGLeg = 0; - m_pLFGFootGroup = 0; - m_BackupLFGFootGroup = nullptr; - m_pLBGFootGroup = 0; - m_BackupLBGFootGroup = nullptr; - m_pRFGFootGroup = 0; - m_BackupRFGFootGroup = nullptr; - m_pRBGFootGroup = 0; - m_BackupRBGFootGroup = nullptr; - m_StrideSound = nullptr; - m_pJetpack = nullptr; - m_MoveState = STAND; - m_StrideFrame = false; - for (int side = 0; side < SIDECOUNT; ++side) - { - for (int layer = 0; layer < LAYERCOUNT; ++layer) - { - for (int state = 0; state < MOVEMENTSTATECOUNT; ++state) - { - m_Paths[side][layer][state].Reset(); - m_Paths[side][layer][state].Terminate(); - } - } - m_StrideStart[side] = false; -// m_StrideTimer[side].Reset(); - } - m_Aiming = false; - - m_DeviceState = SCANNING; - m_SweepState = NOSWEEP; - m_DigState = NOTDIGGING; - m_JumpState = NOTJUMPING; - m_JumpTarget.Reset(); - m_JumpingRight = true; - m_DigTunnelEndPos.Reset(); - m_SweepCenterAimAngle = 0; - m_SweepRange = c_EighthPI; - m_DigTarget.Reset(); - m_FireTimer.Reset(); - m_SweepTimer.Reset(); - m_PatrolTimer.Reset(); - m_JumpTimer.Reset(); - m_AimRangeUpperLimit = -1; - m_AimRangeLowerLimit = -1; - m_LockMouseAimInput = false; -} + int ACrab::Create() { + // Read all the properties + if (Actor::Create() < 0) { + return -1; + } + if (m_AIMode == Actor::AIMODE_NONE) { + m_AIMode = Actor::AIMODE_BRAINHUNT; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ACrab object ready for use. + // Create the background paths copied from the foreground ones which were already read in + for (int side = 0; side < SIDECOUNT; ++side) { + for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { + m_Paths[side][BGROUND][i].Destroy(); + m_Paths[side][BGROUND][i].Create(m_Paths[side][FGROUND][i]); + } + } + + // All ACrabs by default avoid hitting each other ont he same team + m_IgnoresTeamHits = true; + + // Check whether UpperLimit and LowerLimit are defined, if not, copy general AimRange value to preserve compatibility + if (m_AimRangeUpperLimit == -1 || m_AimRangeLowerLimit == -1) { + m_AimRangeUpperLimit = m_AimRange; + m_AimRangeLowerLimit = m_AimRange; + } -int ACrab::Create() -{ - // Read all the properties - if (Actor::Create() < 0) { - return -1; + return 0; } - if (m_AIMode == Actor::AIMODE_NONE) { - m_AIMode = Actor::AIMODE_BRAINHUNT; + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ACrab object ready for use. + + int ACrab::Create(BITMAP *pSprite, + Controller *pController, + const float mass, + const Vector &position, + const Vector &velocity, + AtomGroup *hitBody, + const unsigned long lifetime, + Status status, + const int health) + { + + + return Actor::Create(pSprite, + pController, + mass, + position, + velocity, + hitBody, + lifetime, + status, + health); } + */ - // Create the background paths copied from the foreground ones which were already read in - for (int side = 0; side < SIDECOUNT; ++side) - { - for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) - { - m_Paths[side][BGROUND][i].Destroy(); - m_Paths[side][BGROUND][i].Create(m_Paths[side][FGROUND][i]); - } - } - - // All ACrabs by default avoid hitting each other ont he same team - m_IgnoresTeamHits = true; - - // Check whether UpperLimit and LowerLimit are defined, if not, copy general AimRange value to preserve compatibility - if (m_AimRangeUpperLimit == -1 || m_AimRangeLowerLimit == -1) - { - m_AimRangeUpperLimit = m_AimRange; - m_AimRangeLowerLimit = m_AimRange; - } - - return 0; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ACrab object ready for use. - -int ACrab::Create(BITMAP *pSprite, - Controller *pController, - const float mass, - const Vector &position, - const Vector &velocity, - AtomGroup *hitBody, - const unsigned long lifetime, - Status status, - const int health) -{ - - - return Actor::Create(pSprite, - pController, - mass, - position, - velocity, - hitBody, - lifetime, - status, - health); -} -*/ + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ACrab to be identical to another, by deep copy. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a ACrab to be identical to another, by deep copy. - -int ACrab::Create(const ACrab &reference) { - if (reference.m_pLBGLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLBGLeg->GetUniqueID()); } - if (reference.m_pRBGLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRBGLeg->GetUniqueID()); } - if (reference.m_pJetpack) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pJetpack->GetUniqueID()); } - if (reference.m_pTurret) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pTurret->GetUniqueID()); } - if (reference.m_pLFGLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLFGLeg->GetUniqueID()); } - if (reference.m_pRFGLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRFGLeg->GetUniqueID()); } - - Actor::Create(reference); - - //Note - hardcoded attachable copying is organized based on desired draw order here. - if (reference.m_pLBGLeg) { SetLeftBGLeg(dynamic_cast(reference.m_pLBGLeg->Clone())); } - if (reference.m_pRBGLeg) { SetRightBGLeg(dynamic_cast(reference.m_pRBGLeg->Clone())); } - if (reference.m_pJetpack) { SetJetpack(dynamic_cast(reference.m_pJetpack->Clone())); } - if (reference.m_pTurret) { SetTurret(dynamic_cast(reference.m_pTurret->Clone())); } - if (reference.m_pLFGLeg) { SetLeftFGLeg(dynamic_cast(reference.m_pLFGLeg->Clone())); } - if (reference.m_pRFGLeg) { SetRightFGLeg(dynamic_cast(reference.m_pRFGLeg->Clone())); } - - AtomGroup *atomGroupToUseAsFootGroupLFG = reference.m_pLFGFootGroup ? dynamic_cast(reference.m_pLFGFootGroup->Clone()) : m_pLFGLeg->GetFootGroupFromFootAtomGroup(); - RTEAssert(atomGroupToUseAsFootGroupLFG, "Failed to fallback to using LFGFoot AtomGroup as LFGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a LFGFootGroup or LFGLeg Foot attachable!"); - - AtomGroup *atomGroupToUseAsFootGroupLBG = reference.m_pLBGFootGroup ? dynamic_cast(reference.m_pLBGFootGroup->Clone()) : m_pLBGLeg->GetFootGroupFromFootAtomGroup(); - RTEAssert(atomGroupToUseAsFootGroupLBG, "Failed to fallback to using LBGFoot AtomGroup as LBGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a LBGFootGroup or LBGLeg Foot attachable!"); - - AtomGroup *atomGroupToUseAsFootGroupRFG = reference.m_pRFGFootGroup ? dynamic_cast(reference.m_pRFGFootGroup->Clone()) : m_pRFGLeg->GetFootGroupFromFootAtomGroup(); - RTEAssert(atomGroupToUseAsFootGroupRFG, "Failed to fallback to using RFGFoot AtomGroup as RFGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a RFGFootGroup or RFGLeg Foot attachable!"); - - AtomGroup *atomGroupToUseAsFootGroupRBG = reference.m_pRBGFootGroup ? dynamic_cast(reference.m_pRBGFootGroup->Clone()) : m_pRBGLeg->GetFootGroupFromFootAtomGroup(); - RTEAssert(atomGroupToUseAsFootGroupRBG, "Failed to fallback to using RBGFoot AtomGroup as RBGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a RBGFootGroup or RBGLeg Foot attachable!"); - - m_pLFGFootGroup = atomGroupToUseAsFootGroupLFG; - m_pLFGFootGroup->SetOwner(this); - m_BackupLFGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupLFG->Clone()); - m_BackupLFGFootGroup->RemoveAllAtoms(); - m_BackupLFGFootGroup->SetOwner(this); - m_BackupLFGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupLFG->GetLimbPos()); - m_pLBGFootGroup = atomGroupToUseAsFootGroupLBG; - m_pLBGFootGroup->SetOwner(this); - m_BackupLBGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupLBG->Clone()); - m_BackupLBGFootGroup->RemoveAllAtoms(); - m_BackupLBGFootGroup->SetOwner(this); - m_BackupLBGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupLFG->GetLimbPos()); - m_pRFGFootGroup = atomGroupToUseAsFootGroupRFG; - m_pRFGFootGroup->SetOwner(this); - m_BackupRFGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupRFG->Clone()); - m_BackupRFGFootGroup->RemoveAllAtoms(); - m_BackupRFGFootGroup->SetOwner(this); - m_BackupRFGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupLFG->GetLimbPos()); - m_pRBGFootGroup = atomGroupToUseAsFootGroupRBG; - m_pRBGFootGroup->SetOwner(this); - m_BackupRBGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupRBG->Clone()); - m_BackupRBGFootGroup->RemoveAllAtoms(); - m_BackupRBGFootGroup->SetOwner(this); - m_BackupRBGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupLFG->GetLimbPos()); - - if (reference.m_StrideSound) { m_StrideSound = dynamic_cast(reference.m_StrideSound->Clone()); } - - m_MoveState = reference.m_MoveState; - - for (int side = 0; side < SIDECOUNT; ++side) { - for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { - m_Paths[side][FGROUND][i].Create(reference.m_Paths[side][FGROUND][i]); - m_Paths[side][BGROUND][i].Create(reference.m_Paths[side][BGROUND][i]); - } - } - - m_DeviceState = reference.m_DeviceState; - m_SweepState = reference.m_SweepState; - m_DigState = reference.m_DigState; - m_JumpState = reference.m_JumpState; - m_JumpTarget = reference.m_JumpTarget; - m_JumpingRight = reference.m_JumpingRight; - m_DigTunnelEndPos = reference.m_DigTunnelEndPos; - m_SweepCenterAimAngle = reference.m_SweepCenterAimAngle; - m_SweepRange = reference.m_SweepRange; - m_AimRangeUpperLimit = reference.m_AimRangeUpperLimit; - m_AimRangeLowerLimit = reference.m_AimRangeLowerLimit; - m_LockMouseAimInput = reference.m_LockMouseAimInput; - - return 0; -} + int ACrab::Create(const ACrab& reference) { + if (reference.m_pLBGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLBGLeg->GetUniqueID()); + } + if (reference.m_pRBGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRBGLeg->GetUniqueID()); + } + if (reference.m_pJetpack) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pJetpack->GetUniqueID()); + } + if (reference.m_pTurret) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pTurret->GetUniqueID()); + } + if (reference.m_pLFGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLFGLeg->GetUniqueID()); + } + if (reference.m_pRFGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRFGLeg->GetUniqueID()); + } + Actor::Create(reference); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int ACrab::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Actor::ReadProperty(propName, reader)); - - MatchProperty("Turret", { SetTurret(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("Jetpack", { SetJetpack(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("LFGLeg") MatchProperty("LeftFGLeg", { SetLeftFGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("LBGLeg") MatchProperty("LeftBGLeg", { SetLeftBGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("RFGLeg") MatchProperty("RightFGLeg", { SetRightFGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("RBGLeg") MatchProperty("RightBGLeg", { SetRightBGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchForwards("LFootGroup") MatchProperty("LeftFootGroup", { - delete m_pLFGFootGroup; - delete m_pLBGFootGroup; - delete m_BackupLFGFootGroup; - delete m_BackupLBGFootGroup; - m_pLFGFootGroup = new AtomGroup(); - m_pLBGFootGroup = new AtomGroup(); - reader >> m_pLFGFootGroup; - m_pLBGFootGroup->Create(*m_pLFGFootGroup); - m_pLFGFootGroup->SetOwner(this); - m_pLBGFootGroup->SetOwner(this); - m_BackupLFGFootGroup = new AtomGroup(*m_pLFGFootGroup); - m_BackupLFGFootGroup->RemoveAllAtoms(); - m_BackupLBGFootGroup = new AtomGroup(*m_BackupLFGFootGroup); - }); - MatchForwards("RFootGroup") MatchProperty("RightFootGroup", { - delete m_pRFGFootGroup; - delete m_pRBGFootGroup; - delete m_BackupRFGFootGroup; - delete m_BackupRBGFootGroup; - m_pRFGFootGroup = new AtomGroup(); - m_pRBGFootGroup = new AtomGroup(); - reader >> m_pRFGFootGroup; - m_pRBGFootGroup->Create(*m_pRFGFootGroup); - m_pRFGFootGroup->SetOwner(this); - m_pRBGFootGroup->SetOwner(this); - m_BackupRFGFootGroup = new AtomGroup(*m_pRFGFootGroup); - m_BackupRFGFootGroup->RemoveAllAtoms(); - m_BackupRBGFootGroup = new AtomGroup(*m_BackupRFGFootGroup); - }); - MatchForwards("LFGFootGroup") MatchProperty("LeftFGFootGroup", { - delete m_pLFGFootGroup; - delete m_BackupLFGFootGroup; - m_pLFGFootGroup = new AtomGroup(); - reader >> m_pLFGFootGroup; - m_pLFGFootGroup->SetOwner(this); - m_BackupLFGFootGroup = new AtomGroup(*m_pLFGFootGroup); - m_BackupLFGFootGroup->RemoveAllAtoms(); - }); - MatchForwards("LBGFootGroup") MatchProperty("LeftBGFootGroup", { - delete m_pLBGFootGroup; - delete m_BackupLBGFootGroup; - m_pLBGFootGroup = new AtomGroup(); - reader >> m_pLBGFootGroup; - m_pLBGFootGroup->SetOwner(this); - m_BackupLBGFootGroup = new AtomGroup(*m_pLBGFootGroup); - m_BackupLBGFootGroup->RemoveAllAtoms(); - }); - MatchForwards("RFGFootGroup") MatchProperty("RightFGFootGroup", { - delete m_pRFGFootGroup; - delete m_BackupRFGFootGroup; - m_pRFGFootGroup = new AtomGroup(); - reader >> m_pRFGFootGroup; - m_pRFGFootGroup->SetOwner(this); - m_BackupRFGFootGroup = new AtomGroup(*m_pRFGFootGroup); - m_BackupRFGFootGroup->RemoveAllAtoms(); - }); - MatchForwards("RBGFootGroup") MatchProperty("RightBGFootGroup", { - delete m_pRBGFootGroup; - delete m_BackupRBGFootGroup; - m_pRBGFootGroup = new AtomGroup(); - reader >> m_pRBGFootGroup; - m_pRBGFootGroup->SetOwner(this); - m_BackupRBGFootGroup = new AtomGroup(*m_pRBGFootGroup); - m_BackupRBGFootGroup->RemoveAllAtoms(); - }); - MatchProperty("StrideSound", { - m_StrideSound = new SoundContainer; - reader >> m_StrideSound; - }); - MatchForwards("LStandLimbPath") MatchProperty("LeftStandLimbPath", { reader >> m_Paths[LEFTSIDE][FGROUND][STAND]; }); - MatchForwards("LWalkLimbPath") MatchProperty("LeftWalkLimbPath", { reader >> m_Paths[LEFTSIDE][FGROUND][WALK]; }); - MatchForwards("LDislodgeLimbPath") MatchProperty("LeftDislodgeLimbPath", { reader >> m_Paths[LEFTSIDE][FGROUND][DISLODGE]; }); - MatchForwards("RStandLimbPath") MatchProperty("RightStandLimbPath", { reader >> m_Paths[RIGHTSIDE][FGROUND][STAND]; }); - MatchForwards("RWalkLimbPath") MatchProperty("RightWalkLimbPath", { reader >> m_Paths[RIGHTSIDE][FGROUND][WALK]; }); - MatchForwards("RDislodgeLimbPath") MatchProperty("RightDislodgeLimbPath", { reader >> m_Paths[RIGHTSIDE][FGROUND][DISLODGE]; }); - MatchProperty("AimRangeUpperLimit", { reader >> m_AimRangeUpperLimit; }); - MatchProperty("AimRangeLowerLimit", { reader >> m_AimRangeLowerLimit; }); - MatchProperty("LockMouseAimInput", { reader >> m_LockMouseAimInput; }); - - EndPropertyList; -} + // Note - hardcoded attachable copying is organized based on desired draw order here. + if (reference.m_pLBGLeg) { + SetLeftBGLeg(dynamic_cast(reference.m_pLBGLeg->Clone())); + } + if (reference.m_pRBGLeg) { + SetRightBGLeg(dynamic_cast(reference.m_pRBGLeg->Clone())); + } + if (reference.m_pJetpack) { + SetJetpack(dynamic_cast(reference.m_pJetpack->Clone())); + } + if (reference.m_pTurret) { + SetTurret(dynamic_cast(reference.m_pTurret->Clone())); + } + if (reference.m_pLFGLeg) { + SetLeftFGLeg(dynamic_cast(reference.m_pLFGLeg->Clone())); + } + if (reference.m_pRFGLeg) { + SetRightFGLeg(dynamic_cast(reference.m_pRFGLeg->Clone())); + } + AtomGroup* atomGroupToUseAsFootGroupLFG = reference.m_pLFGFootGroup ? dynamic_cast(reference.m_pLFGFootGroup->Clone()) : m_pLFGLeg->GetFootGroupFromFootAtomGroup(); + RTEAssert(atomGroupToUseAsFootGroupLFG, "Failed to fallback to using LFGFoot AtomGroup as LFGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a LFGFootGroup or LFGLeg Foot attachable!"); + + AtomGroup* atomGroupToUseAsFootGroupLBG = reference.m_pLBGFootGroup ? dynamic_cast(reference.m_pLBGFootGroup->Clone()) : m_pLBGLeg->GetFootGroupFromFootAtomGroup(); + RTEAssert(atomGroupToUseAsFootGroupLBG, "Failed to fallback to using LBGFoot AtomGroup as LBGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a LBGFootGroup or LBGLeg Foot attachable!"); + + AtomGroup* atomGroupToUseAsFootGroupRFG = reference.m_pRFGFootGroup ? dynamic_cast(reference.m_pRFGFootGroup->Clone()) : m_pRFGLeg->GetFootGroupFromFootAtomGroup(); + RTEAssert(atomGroupToUseAsFootGroupRFG, "Failed to fallback to using RFGFoot AtomGroup as RFGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a RFGFootGroup or RFGLeg Foot attachable!"); + + AtomGroup* atomGroupToUseAsFootGroupRBG = reference.m_pRBGFootGroup ? dynamic_cast(reference.m_pRBGFootGroup->Clone()) : m_pRBGLeg->GetFootGroupFromFootAtomGroup(); + RTEAssert(atomGroupToUseAsFootGroupRBG, "Failed to fallback to using RBGFoot AtomGroup as RBGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a RBGFootGroup or RBGLeg Foot attachable!"); + + m_pLFGFootGroup = atomGroupToUseAsFootGroupLFG; + m_pLFGFootGroup->SetOwner(this); + m_BackupLFGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupLFG->Clone()); + m_BackupLFGFootGroup->RemoveAllAtoms(); + m_BackupLFGFootGroup->SetOwner(this); + m_BackupLFGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupLFG->GetLimbPos()); + m_pLBGFootGroup = atomGroupToUseAsFootGroupLBG; + m_pLBGFootGroup->SetOwner(this); + m_BackupLBGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupLBG->Clone()); + m_BackupLBGFootGroup->RemoveAllAtoms(); + m_BackupLBGFootGroup->SetOwner(this); + m_BackupLBGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupLFG->GetLimbPos()); + m_pRFGFootGroup = atomGroupToUseAsFootGroupRFG; + m_pRFGFootGroup->SetOwner(this); + m_BackupRFGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupRFG->Clone()); + m_BackupRFGFootGroup->RemoveAllAtoms(); + m_BackupRFGFootGroup->SetOwner(this); + m_BackupRFGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupLFG->GetLimbPos()); + m_pRBGFootGroup = atomGroupToUseAsFootGroupRBG; + m_pRBGFootGroup->SetOwner(this); + m_BackupRBGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupRBG->Clone()); + m_BackupRBGFootGroup->RemoveAllAtoms(); + m_BackupRBGFootGroup->SetOwner(this); + m_BackupRBGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupLFG->GetLimbPos()); + + if (reference.m_StrideSound) { + m_StrideSound = dynamic_cast(reference.m_StrideSound->Clone()); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this ACrab with a Writer for -// later recreation with Create(Reader &reader); - -int ACrab::Save(Writer &writer) const -{ - Actor::Save(writer); - - writer.NewProperty("Turret"); - writer << m_pTurret; - writer.NewProperty("Jetpack"); - writer << m_pJetpack; - writer.NewProperty("LFGLeg"); - writer << m_pLFGLeg; - writer.NewProperty("LBGLeg"); - writer << m_pLBGLeg; - writer.NewProperty("RFGLeg"); - writer << m_pRFGLeg; - writer.NewProperty("RBGLeg"); - writer << m_pRBGLeg; - writer.NewProperty("LFGFootGroup"); - writer << m_pLFGFootGroup; - writer.NewProperty("LBGFootGroup"); - writer << m_pLBGFootGroup; - writer.NewProperty("RFGFootGroup"); - writer << m_pRFGFootGroup; - writer.NewProperty("RBGFootGroup"); - writer << m_pRBGFootGroup; - writer.NewProperty("StrideSound"); - writer << m_StrideSound; - - writer.NewProperty("LStandLimbPath"); - writer << m_Paths[LEFTSIDE][FGROUND][STAND]; - writer.NewProperty("LWalkLimbPath"); - writer << m_Paths[LEFTSIDE][FGROUND][WALK]; - writer.NewProperty("LDislodgeLimbPath"); - writer << m_Paths[LEFTSIDE][FGROUND][DISLODGE]; - writer.NewProperty("RStandLimbPath"); - writer << m_Paths[RIGHTSIDE][FGROUND][STAND]; - writer.NewProperty("RWalkLimbPath"); - writer << m_Paths[RIGHTSIDE][FGROUND][WALK]; - writer.NewProperty("RDislodgeLimbPath"); - writer << m_Paths[RIGHTSIDE][FGROUND][DISLODGE]; - - writer.NewProperty("AimRangeUpperLimit"); - writer << m_AimRangeUpperLimit; - writer.NewProperty("AimRangeLowerLimit"); - writer << m_AimRangeLowerLimit; - writer.NewProperty("LockMouseAimInput"); - writer << m_LockMouseAimInput; - - return 0; -} + m_MoveState = reference.m_MoveState; + for (int side = 0; side < SIDECOUNT; ++side) { + for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { + m_Paths[side][FGROUND][i].Create(reference.m_Paths[side][FGROUND][i]); + m_Paths[side][BGROUND][i].Create(reference.m_Paths[side][BGROUND][i]); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the ACrab object. - -void ACrab::Destroy(bool notInherited) -{ - delete m_pLFGFootGroup; - delete m_pLBGFootGroup; - delete m_pRFGFootGroup; - delete m_pRBGFootGroup; - - delete m_StrideSound; -// for (deque::iterator itr = m_WalkPaths.begin(); -// itr != m_WalkPaths.end(); ++itr) -// delete *itr; - - if (!notInherited) - Actor::Destroy(); - Clear(); -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of this Actor and all its carried -// gold and inventory. + m_DeviceState = reference.m_DeviceState; + m_SweepState = reference.m_SweepState; + m_DigState = reference.m_DigState; + m_JumpState = reference.m_JumpState; + m_JumpTarget = reference.m_JumpTarget; + m_JumpingRight = reference.m_JumpingRight; + m_DigTunnelEndPos = reference.m_DigTunnelEndPos; + m_SweepCenterAimAngle = reference.m_SweepCenterAimAngle; + m_SweepRange = reference.m_SweepRange; + m_AimRangeUpperLimit = reference.m_AimRangeUpperLimit; + m_AimRangeLowerLimit = reference.m_AimRangeLowerLimit; + m_LockMouseAimInput = reference.m_LockMouseAimInput; + + return 0; + } -float ACrab::GetTotalValue(int nativeModule, float foreignMult) const -{ - float totalValue = Actor::GetTotalValue(nativeModule, foreignMult); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int ACrab::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Actor::ReadProperty(propName, reader)); + + MatchProperty("Turret", { SetTurret(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("Jetpack", { SetJetpack(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("LFGLeg") MatchProperty("LeftFGLeg", { SetLeftFGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("LBGLeg") MatchProperty("LeftBGLeg", { SetLeftBGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("RFGLeg") MatchProperty("RightFGLeg", { SetRightFGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("RBGLeg") MatchProperty("RightBGLeg", { SetRightBGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchForwards("LFootGroup") MatchProperty("LeftFootGroup", { + delete m_pLFGFootGroup; + delete m_pLBGFootGroup; + delete m_BackupLFGFootGroup; + delete m_BackupLBGFootGroup; + m_pLFGFootGroup = new AtomGroup(); + m_pLBGFootGroup = new AtomGroup(); + reader >> m_pLFGFootGroup; + m_pLBGFootGroup->Create(*m_pLFGFootGroup); + m_pLFGFootGroup->SetOwner(this); + m_pLBGFootGroup->SetOwner(this); + m_BackupLFGFootGroup = new AtomGroup(*m_pLFGFootGroup); + m_BackupLFGFootGroup->RemoveAllAtoms(); + m_BackupLBGFootGroup = new AtomGroup(*m_BackupLFGFootGroup); + }); + MatchForwards("RFootGroup") MatchProperty("RightFootGroup", { + delete m_pRFGFootGroup; + delete m_pRBGFootGroup; + delete m_BackupRFGFootGroup; + delete m_BackupRBGFootGroup; + m_pRFGFootGroup = new AtomGroup(); + m_pRBGFootGroup = new AtomGroup(); + reader >> m_pRFGFootGroup; + m_pRBGFootGroup->Create(*m_pRFGFootGroup); + m_pRFGFootGroup->SetOwner(this); + m_pRBGFootGroup->SetOwner(this); + m_BackupRFGFootGroup = new AtomGroup(*m_pRFGFootGroup); + m_BackupRFGFootGroup->RemoveAllAtoms(); + m_BackupRBGFootGroup = new AtomGroup(*m_BackupRFGFootGroup); + }); + MatchForwards("LFGFootGroup") MatchProperty("LeftFGFootGroup", { + delete m_pLFGFootGroup; + delete m_BackupLFGFootGroup; + m_pLFGFootGroup = new AtomGroup(); + reader >> m_pLFGFootGroup; + m_pLFGFootGroup->SetOwner(this); + m_BackupLFGFootGroup = new AtomGroup(*m_pLFGFootGroup); + m_BackupLFGFootGroup->RemoveAllAtoms(); + }); + MatchForwards("LBGFootGroup") MatchProperty("LeftBGFootGroup", { + delete m_pLBGFootGroup; + delete m_BackupLBGFootGroup; + m_pLBGFootGroup = new AtomGroup(); + reader >> m_pLBGFootGroup; + m_pLBGFootGroup->SetOwner(this); + m_BackupLBGFootGroup = new AtomGroup(*m_pLBGFootGroup); + m_BackupLBGFootGroup->RemoveAllAtoms(); + }); + MatchForwards("RFGFootGroup") MatchProperty("RightFGFootGroup", { + delete m_pRFGFootGroup; + delete m_BackupRFGFootGroup; + m_pRFGFootGroup = new AtomGroup(); + reader >> m_pRFGFootGroup; + m_pRFGFootGroup->SetOwner(this); + m_BackupRFGFootGroup = new AtomGroup(*m_pRFGFootGroup); + m_BackupRFGFootGroup->RemoveAllAtoms(); + }); + MatchForwards("RBGFootGroup") MatchProperty("RightBGFootGroup", { + delete m_pRBGFootGroup; + delete m_BackupRBGFootGroup; + m_pRBGFootGroup = new AtomGroup(); + reader >> m_pRBGFootGroup; + m_pRBGFootGroup->SetOwner(this); + m_BackupRBGFootGroup = new AtomGroup(*m_pRBGFootGroup); + m_BackupRBGFootGroup->RemoveAllAtoms(); + }); + MatchProperty("StrideSound", { + m_StrideSound = new SoundContainer; + reader >> m_StrideSound; + }); + MatchForwards("LStandLimbPath") MatchProperty("LeftStandLimbPath", { reader >> m_Paths[LEFTSIDE][FGROUND][STAND]; }); + MatchForwards("LWalkLimbPath") MatchProperty("LeftWalkLimbPath", { reader >> m_Paths[LEFTSIDE][FGROUND][WALK]; }); + MatchForwards("LDislodgeLimbPath") MatchProperty("LeftDislodgeLimbPath", { reader >> m_Paths[LEFTSIDE][FGROUND][DISLODGE]; }); + MatchForwards("RStandLimbPath") MatchProperty("RightStandLimbPath", { reader >> m_Paths[RIGHTSIDE][FGROUND][STAND]; }); + MatchForwards("RWalkLimbPath") MatchProperty("RightWalkLimbPath", { reader >> m_Paths[RIGHTSIDE][FGROUND][WALK]; }); + MatchForwards("RDislodgeLimbPath") MatchProperty("RightDislodgeLimbPath", { reader >> m_Paths[RIGHTSIDE][FGROUND][DISLODGE]; }); + MatchProperty("AimRangeUpperLimit", { reader >> m_AimRangeUpperLimit; }); + MatchProperty("AimRangeLowerLimit", { reader >> m_AimRangeLowerLimit; }); + MatchProperty("LockMouseAimInput", { reader >> m_LockMouseAimInput; }); + + EndPropertyList; + } - return totalValue; -} -*/ -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetCPUPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of this' brain, or equivalent. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this ACrab with a Writer for + // later recreation with Create(Reader &reader); + + int ACrab::Save(Writer& writer) const { + Actor::Save(writer); + + writer.NewProperty("Turret"); + writer << m_pTurret; + writer.NewProperty("Jetpack"); + writer << m_pJetpack; + writer.NewProperty("LFGLeg"); + writer << m_pLFGLeg; + writer.NewProperty("LBGLeg"); + writer << m_pLBGLeg; + writer.NewProperty("RFGLeg"); + writer << m_pRFGLeg; + writer.NewProperty("RBGLeg"); + writer << m_pRBGLeg; + writer.NewProperty("LFGFootGroup"); + writer << m_pLFGFootGroup; + writer.NewProperty("LBGFootGroup"); + writer << m_pLBGFootGroup; + writer.NewProperty("RFGFootGroup"); + writer << m_pRFGFootGroup; + writer.NewProperty("RBGFootGroup"); + writer << m_pRBGFootGroup; + writer.NewProperty("StrideSound"); + writer << m_StrideSound; + + writer.NewProperty("LStandLimbPath"); + writer << m_Paths[LEFTSIDE][FGROUND][STAND]; + writer.NewProperty("LWalkLimbPath"); + writer << m_Paths[LEFTSIDE][FGROUND][WALK]; + writer.NewProperty("LDislodgeLimbPath"); + writer << m_Paths[LEFTSIDE][FGROUND][DISLODGE]; + writer.NewProperty("RStandLimbPath"); + writer << m_Paths[RIGHTSIDE][FGROUND][STAND]; + writer.NewProperty("RWalkLimbPath"); + writer << m_Paths[RIGHTSIDE][FGROUND][WALK]; + writer.NewProperty("RDislodgeLimbPath"); + writer << m_Paths[RIGHTSIDE][FGROUND][DISLODGE]; + + writer.NewProperty("AimRangeUpperLimit"); + writer << m_AimRangeUpperLimit; + writer.NewProperty("AimRangeLowerLimit"); + writer << m_AimRangeLowerLimit; + writer.NewProperty("LockMouseAimInput"); + writer << m_LockMouseAimInput; + + return 0; + } -Vector ACrab::GetCPUPos() const -{ - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->GetMountedMO()) - return m_pTurret->GetMountedMO()->GetPos(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the ACrab object. + + void ACrab::Destroy(bool notInherited) { + delete m_pLFGFootGroup; + delete m_pLBGFootGroup; + delete m_pRFGFootGroup; + delete m_pRBGFootGroup; + + delete m_StrideSound; + // for (deque::iterator itr = m_WalkPaths.begin(); + // itr != m_WalkPaths.end(); ++itr) + // delete *itr; + + if (!notInherited) + Actor::Destroy(); + Clear(); + } - return m_Pos; -} -*/ + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of this Actor and all its carried + // gold and inventory. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetEyePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of this' eye, or equivalent, where look -// vector starts from. - -Vector ACrab::GetEyePos() const -{ - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) - return m_pTurret->GetFirstMountedDevice()->GetPos(); - - return m_Pos; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACrab::SetTurret(Turret *newTurret) { - if (m_pTurret && m_pTurret->IsAttached()) { RemoveAndDeleteAttachable(m_pTurret); } - if (newTurret == nullptr) { - m_pTurret = nullptr; - } else { - m_pTurret = newTurret; - AddAttachable(newTurret); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newTurret->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Turret *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetTurret"); - dynamic_cast(parent)->SetTurret(castedAttachable); - }}); - - if (m_pTurret->HasNoSetDamageMultiplier()) { m_pTurret->SetDamageMultiplier(5.0F); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACrab::SetJetpack(AEJetpack *newJetpack) { - if (m_pJetpack && m_pJetpack->IsAttached()) { RemoveAndDeleteAttachable(m_pJetpack); } - if (newJetpack == nullptr) { - m_pJetpack = nullptr; - } else { - m_pJetpack = newJetpack; - AddAttachable(newJetpack); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newJetpack->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEJetpack *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetJetpack"); - dynamic_cast(parent)->SetJetpack(castedAttachable); - }}); - - if (m_pJetpack->HasNoSetDamageMultiplier()) { m_pJetpack->SetDamageMultiplier(0.0F); } - m_pJetpack->SetApplyTransferredForcesAtOffset(false); - m_pJetpack->SetDeleteWhenRemovedFromParent(true); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACrab::SetLeftFGLeg(Leg *newLeg) { - if (m_pLFGLeg && m_pLFGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pLFGLeg); } - if (newLeg == nullptr) { - m_pLFGLeg = nullptr; - } else { - m_pLFGLeg = newLeg; - AddAttachable(newLeg); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Leg *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftFGLeg"); - dynamic_cast(parent)->SetLeftFGLeg(castedAttachable); - }}); - - if (m_pLFGLeg->HasNoSetDamageMultiplier()) { m_pLFGLeg->SetDamageMultiplier(1.0F); } - m_pLFGLeg->SetInheritsHFlipped(-1); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACrab::SetLeftBGLeg(Leg *newLeg) { - if (m_pLBGLeg && m_pLBGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pLBGLeg); } - if (newLeg == nullptr) { - m_pLBGLeg = nullptr; - } else { - m_pLBGLeg = newLeg; - AddAttachable(newLeg); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Leg *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftBGLeg"); - dynamic_cast(parent)->SetLeftBGLeg(castedAttachable); - }}); - - if (m_pLBGLeg->HasNoSetDamageMultiplier()) { m_pLBGLeg->SetDamageMultiplier(1.0F); } - m_pLBGLeg->SetInheritsHFlipped(-1); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACrab::SetRightFGLeg(Leg *newLeg) { - if (m_pRFGLeg && m_pRFGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pRFGLeg); } - if (newLeg == nullptr) { - m_pRFGLeg = nullptr; - } else { - m_pRFGLeg = newLeg; - AddAttachable(newLeg); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Leg *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightFGLeg"); - dynamic_cast(parent)->SetRightFGLeg(castedAttachable); - }}); - - if (m_pRFGLeg->HasNoSetDamageMultiplier()) { m_pRFGLeg->SetDamageMultiplier(1.0F); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACrab::SetRightBGLeg(Leg *newLeg) { - if (m_pRBGLeg && m_pRBGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pRBGLeg); } - if (newLeg == nullptr) { - m_pRBGLeg = nullptr; - } else { - m_pRBGLeg = newLeg; - AddAttachable(newLeg); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Leg *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightBGLeg"); - dynamic_cast(parent)->SetRightBGLeg(castedAttachable); - }}); - - if (m_pRBGLeg->HasNoSetDamageMultiplier()) { m_pRBGLeg->SetDamageMultiplier(1.0F); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -BITMAP * ACrab::GetGraphicalIcon() const { - return m_GraphicalIcon ? m_GraphicalIcon : (m_pTurret ? m_pTurret->GetSpriteFrame(0) : GetSpriteFrame(0)); -} + float ACrab::GetTotalValue(int nativeModule, float foreignMult) const + { + float totalValue = Actor::GetTotalValue(nativeModule, foreignMult); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. - -bool ACrab::CollideAtPoint(HitData &hd) -{ - return Actor::CollideAtPoint(hd); - -/* - hd.ResImpulse[HITOR].Reset(); - hd.ResImpulse[HITEE].Reset(); - hd.HitRadius[HITEE] = (hd.HitPoint - m_Pos) * c_MPP; - hd.mass[HITEE] = m_Mass; - hd.MomInertia[HITEE] = m_pAtomGroup->GetMomentOfInertia(); - hd.HitVel[HITEE] = m_Vel + hd.HitRadius[HITEE].GetPerpendicular() * m_AngularVel; - hd.VelDiff = hd.HitVel[HITOR] - hd.HitVel[HITEE]; - Vector hitAcc = -hd.VelDiff * (1 + hd.Body[HITOR]->GetMaterial().restitution * GetMaterial().restitution); - - float hittorLever = hd.HitRadius[HITOR].GetPerpendicular().Dot(hd.BitmapNormal); - float hitteeLever = hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.BitmapNormal); - hittorLever *= hittorLever; - hitteeLever *= hitteeLever; - float impulse = hitAcc.Dot(hd.BitmapNormal) / (((1 / hd.mass[HITOR]) + (1 / hd.mass[HITEE])) + - (hittorLever / hd.MomInertia[HITOR]) + (hitteeLever / hd.MomInertia[HITEE])); - - hd.ResImpulse[HITOR] = hd.BitmapNormal * impulse * hd.ImpulseFactor[HITOR]; - hd.ResImpulse[HITEE] = hd.BitmapNormal * -impulse * hd.ImpulseFactor[HITEE]; - - //////////////////////////////////////////////////////////////////////////////// - // If a particle, which does not penetrate, but bounces, do any additional - // effects of that bounce. - if (!ParticlePenetration()) -// TODO: Add blunt trauma effects here!") - ; - } - - m_Vel += hd.ResImpulse[HITEE] / hd.mass[HITEE]; - m_AngularVel += hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.ResImpulse[HITEE]) / - hd.MomInertia[HITEE]; -*/ -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnBounce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits and then -// bounces off of something. This is called by the owned Atom/AtomGroup -// of this MovableObject during travel. + return totalValue; + } + */ + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetCPUPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of this' brain, or equivalent. + + Vector ACrab::GetCPUPos() const + { + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->GetMountedMO()) + return m_pTurret->GetMountedMO()->GetPos(); + + return m_Pos; + } + */ -bool ACrab::OnBounce(const Vector &pos) -{ - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetEyePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of this' eye, or equivalent, where look + // vector starts from. + Vector ACrab::GetEyePos() const { + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) + return m_pTurret->GetFirstMountedDevice()->GetPos(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnSink -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits and then -// sink into something. This is called by the owned Atom/AtomGroup -// of this MovableObject during travel. - -bool ACrab::OnSink(const Vector &pos) -{ - return false; -} -*/ - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool ACrab::HandlePieCommand(PieSlice::SliceType pieSliceIndex) { - if (pieSliceIndex != PieSlice::SliceType::NoType) { - if (pieSliceIndex == PieSlice::SliceType::Reload) { - m_Controller.SetState(WEAPON_RELOAD); - } else if (pieSliceIndex == PieSlice::SliceType::Sentry) { - m_AIMode = AIMODE_SENTRY; - } else if (pieSliceIndex == PieSlice::SliceType::Patrol) { - m_AIMode = AIMODE_PATROL; - } else if (pieSliceIndex == PieSlice::SliceType::BrainHunt) { - m_AIMode = AIMODE_BRAINHUNT; - ClearAIWaypoints(); - } else if (pieSliceIndex == PieSlice::SliceType::GoTo) { - m_AIMode = AIMODE_GOTO; - ClearAIWaypoints(); - m_UpdateMovePath = true; - } else { - return Actor::HandlePieCommand(pieSliceIndex); - } - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + return m_Pos; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: GetEquippedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whatever is equipped in the turret, if anything. OWNERSHIP IS NOT TRANSFERRED! + void ACrab::SetTurret(Turret* newTurret) { + if (m_pTurret && m_pTurret->IsAttached()) { + RemoveAndDeleteAttachable(m_pTurret); + } + if (newTurret == nullptr) { + m_pTurret = nullptr; + } else { + m_pTurret = newTurret; + AddAttachable(newTurret); -MovableObject * ACrab::GetEquippedItem() const -{ - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) - { - return m_pTurret->GetFirstMountedDevice(); - } + m_HardcodedAttachableUniqueIDsAndSetters.insert({newTurret->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Turret* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetTurret"); + dynamic_cast(parent)->SetTurret(castedAttachable); + }}); - return 0; -} + if (m_pTurret->HasNoSetDamageMultiplier()) { + m_pTurret->SetDamageMultiplier(5.0F); + } + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmIsReady -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held device's current mag is empty on -// ammo or not. + void ACrab::SetJetpack(AEJetpack* newJetpack) { + if (m_pJetpack && m_pJetpack->IsAttached()) { + RemoveAndDeleteAttachable(m_pJetpack); + } + if (newJetpack == nullptr) { + m_pJetpack = nullptr; + } else { + m_pJetpack = newJetpack; + AddAttachable(newJetpack); -bool ACrab::FirearmIsReady() const { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - for (const HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { - if (const HDFirearm *mountedFirearm = dynamic_cast(mountedDevice); mountedFirearm && mountedFirearm->GetRoundInMagCount() != 0) { - return true; - } - } - } + m_HardcodedAttachableUniqueIDsAndSetters.insert({newJetpack->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEJetpack* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetJetpack"); + dynamic_cast(parent)->SetJetpack(castedAttachable); + }}); - return false; -} + if (m_pJetpack->HasNoSetDamageMultiplier()) { + m_pJetpack->SetDamageMultiplier(0.0F); + } + m_pJetpack->SetApplyTransferredForcesAtOffset(false); + m_pJetpack->SetDeleteWhenRemovedFromParent(true); + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmIsEmpty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is out of ammo. + void ACrab::SetLeftFGLeg(Leg* newLeg) { + if (m_pLFGLeg && m_pLFGLeg->IsAttached()) { + RemoveAndDeleteAttachable(m_pLFGLeg); + } + if (newLeg == nullptr) { + m_pLFGLeg = nullptr; + } else { + m_pLFGLeg = newLeg; + AddAttachable(newLeg); -bool ACrab::FirearmIsEmpty() const { - return !FirearmIsReady() && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice(); -} + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Leg* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftFGLeg"); + dynamic_cast(parent)->SetLeftFGLeg(castedAttachable); + }}); + if (m_pLFGLeg->HasNoSetDamageMultiplier()) { + m_pLFGLeg->SetDamageMultiplier(1.0F); + } + m_pLFGLeg->SetInheritsHFlipped(-1); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmNeedsReload -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is almost out of ammo. - -bool ACrab::FirearmsAreFull() const { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - for (const HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { - if (const HDFirearm *mountedFirearm = dynamic_cast(mountedDevice); mountedFirearm && !mountedFirearm->IsFull()) { - return false; - } - } - } - return true; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// + void ACrab::SetLeftBGLeg(Leg* newLeg) { + if (m_pLBGLeg && m_pLBGLeg->IsAttached()) { + RemoveAndDeleteAttachable(m_pLBGLeg); + } + if (newLeg == nullptr) { + m_pLBGLeg = nullptr; + } else { + m_pLBGLeg = newLeg; + AddAttachable(newLeg); -bool ACrab::FirearmNeedsReload() const { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - for (const HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { - if (const HDFirearm *mountedFirearm = dynamic_cast(mountedDevice); mountedFirearm && mountedFirearm->NeedsReloading()) { - return true; - } - } - } + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Leg* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftBGLeg"); + dynamic_cast(parent)->SetLeftBGLeg(castedAttachable); + }}); - return false; -} + if (m_pLBGLeg->HasNoSetDamageMultiplier()) { + m_pLBGLeg->SetDamageMultiplier(1.0F); + } + m_pLBGLeg->SetInheritsHFlipped(-1); + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmIsSemiAuto -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is semi or full auto. + void ACrab::SetRightFGLeg(Leg* newLeg) { + if (m_pRFGLeg && m_pRFGLeg->IsAttached()) { + RemoveAndDeleteAttachable(m_pRFGLeg); + } + if (newLeg == nullptr) { + m_pRFGLeg = nullptr; + } else { + m_pRFGLeg = newLeg; + AddAttachable(newLeg); -bool ACrab::FirearmIsSemiAuto() const -{ - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) - { - HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetFirstMountedDevice()); - return pWeapon && !pWeapon->IsFullAuto(); - } - return false; -} + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Leg* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightFGLeg"); + dynamic_cast(parent)->SetRightFGLeg(castedAttachable); + }}); + if (m_pRFGLeg->HasNoSetDamageMultiplier()) { + m_pRFGLeg->SetDamageMultiplier(1.0F); + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: ReloadFirearms -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reloads the currently held firearms, if any. -// Arguments: None. -// Return value: None. - -void ACrab::ReloadFirearms() { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - for (HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { - if (HDFirearm *mountedFirearm = dynamic_cast(mountedDevice)) { - mountedFirearm->Reload(); - } - } - } -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmActivationDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the currently held device's delay between pulling the trigger -// and activating. + void ACrab::SetRightBGLeg(Leg* newLeg) { + if (m_pRBGLeg && m_pRBGLeg->IsAttached()) { + RemoveAndDeleteAttachable(m_pRBGLeg); + } + if (newLeg == nullptr) { + m_pRBGLeg = nullptr; + } else { + m_pRBGLeg = newLeg; + AddAttachable(newLeg); -int ACrab::FirearmActivationDelay() const -{ - // Check if the currently held device is already the desired type - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) - { - HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetFirstMountedDevice()); - if (pWeapon) - return pWeapon->GetActivationDelay(); - } + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Leg* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightBGLeg"); + dynamic_cast(parent)->SetRightBGLeg(castedAttachable); + }}); - return 0; -} + if (m_pRBGLeg->HasNoSetDamageMultiplier()) { + m_pRBGLeg->SetDamageMultiplier(1.0F); + } + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsWithinRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a point on the scene is within range of the currently -// used device and aiming status, if applicable. + BITMAP* ACrab::GetGraphicalIcon() const { + return m_GraphicalIcon ? m_GraphicalIcon : (m_pTurret ? m_pTurret->GetSpriteFrame(0) : GetSpriteFrame(0)); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. + + bool ACrab::CollideAtPoint(HitData& hd) { + return Actor::CollideAtPoint(hd); + + /* + hd.ResImpulse[HITOR].Reset(); + hd.ResImpulse[HITEE].Reset(); + hd.HitRadius[HITEE] = (hd.HitPoint - m_Pos) * c_MPP; + hd.mass[HITEE] = m_Mass; + hd.MomInertia[HITEE] = m_pAtomGroup->GetMomentOfInertia(); + hd.HitVel[HITEE] = m_Vel + hd.HitRadius[HITEE].GetPerpendicular() * m_AngularVel; + hd.VelDiff = hd.HitVel[HITOR] - hd.HitVel[HITEE]; + Vector hitAcc = -hd.VelDiff * (1 + hd.Body[HITOR]->GetMaterial().restitution * GetMaterial().restitution); + + float hittorLever = hd.HitRadius[HITOR].GetPerpendicular().Dot(hd.BitmapNormal); + float hitteeLever = hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.BitmapNormal); + hittorLever *= hittorLever; + hitteeLever *= hitteeLever; + float impulse = hitAcc.Dot(hd.BitmapNormal) / (((1 / hd.mass[HITOR]) + (1 / hd.mass[HITEE])) + + (hittorLever / hd.MomInertia[HITOR]) + (hitteeLever / hd.MomInertia[HITEE])); + + hd.ResImpulse[HITOR] = hd.BitmapNormal * impulse * hd.ImpulseFactor[HITOR]; + hd.ResImpulse[HITEE] = hd.BitmapNormal * -impulse * hd.ImpulseFactor[HITEE]; + + //////////////////////////////////////////////////////////////////////////////// + // If a particle, which does not penetrate, but bounces, do any additional + // effects of that bounce. + if (!ParticlePenetration()) + // TODO: Add blunt trauma effects here!") + ; + } + + m_Vel += hd.ResImpulse[HITEE] / hd.mass[HITEE]; + m_AngularVel += hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.ResImpulse[HITEE]) / + hd.MomInertia[HITEE]; + */ + } -bool ACrab::IsWithinRange(Vector &point) const -{ - if (m_SharpAimMaxedOut) - return true; + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnBounce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits and then + // bounces off of something. This is called by the owned Atom/AtomGroup + // of this MovableObject during travel. + + bool ACrab::OnBounce(const Vector &pos) + { + return false; + } - Vector diff = g_SceneMan.ShortestDistance(m_Pos, point, false); - float sqrDistance = diff.GetSqrMagnitude(); - // Really close! - if (sqrDistance <= (m_CharHeight * m_CharHeight)) { - return true; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnSink + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits and then + // sink into something. This is called by the owned Atom/AtomGroup + // of this MovableObject during travel. + + bool ACrab::OnSink(const Vector &pos) + { + return false; + } + */ + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool ACrab::HandlePieCommand(PieSlice::SliceType pieSliceIndex) { + if (pieSliceIndex != PieSlice::SliceType::NoType) { + if (pieSliceIndex == PieSlice::SliceType::Reload) { + m_Controller.SetState(WEAPON_RELOAD); + } else if (pieSliceIndex == PieSlice::SliceType::Sentry) { + m_AIMode = AIMODE_SENTRY; + } else if (pieSliceIndex == PieSlice::SliceType::Patrol) { + m_AIMode = AIMODE_PATROL; + } else if (pieSliceIndex == PieSlice::SliceType::BrainHunt) { + m_AIMode = AIMODE_BRAINHUNT; + ClearAIWaypoints(); + } else if (pieSliceIndex == PieSlice::SliceType::GoTo) { + m_AIMode = AIMODE_GOTO; + ClearAIWaypoints(); + m_UpdateMovePath = true; + } else { + return Actor::HandlePieCommand(pieSliceIndex); + } + } + return false; } - // Start with the default aim distance - float range = m_AimDistance; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Add the sharp range of the equipped weapon - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) - { - range += m_pTurret->GetFirstMountedDevice()->GetSharpLength() * m_SharpAimProgress; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: GetEquippedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whatever is equipped in the turret, if anything. OWNERSHIP IS NOT TRANSFERRED! - return sqrDistance <= (range * range); -} + MovableObject* ACrab::GetEquippedItem() const { + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + return m_pTurret->GetFirstMountedDevice(); + } + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Look -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an unseen-revealing ray in the direction of where this is facing. -// Arguments: The degree angle to deviate from the current view point in the ray -// casting. A random ray will be chosen out of this +-range. - -bool ACrab::Look(float FOVSpread, float range) -{ - if (!g_SceneMan.AnythingUnseen(m_Team)) - return false; - - // Set the length of the look vector - float aimDistance = m_AimDistance + range; - Vector aimPos = GetCPUPos(); - - // If aiming down the barrel, look through that - if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) - { - aimPos = m_pTurret->GetFirstMountedDevice()->GetPos(); - aimDistance += m_pTurret->GetFirstMountedDevice()->GetSharpLength(); - } - // If just looking, use the sensors on the turret instead - else if (m_pTurret && m_pTurret->IsAttached()) - { - aimPos = GetEyePos(); - } - - // Create the vector to trace along - Vector lookVector(aimDistance, 0); - // Set the rotation to the actual aiming angle - Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); - aimMatrix.SetXFlipped(m_HFlipped); - lookVector *= aimMatrix; - // Add the spread - lookVector.DegRotate(FOVSpread * RandomNormalNum()); - - // TODO: generate an alarm event if we spot an enemy actor? - - Vector ignored; - // Cast the seeing ray, adjusting the skip to match the resolution of the unseen map - return g_SceneMan.CastSeeRay(m_Team, aimPos, lookVector, ignored, 25, (int)g_SceneMan.GetUnseenResolution(m_Team).GetSmallest() / 2); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmIsReady + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held device's current mag is empty on + // ammo or not. + + bool ACrab::FirearmIsReady() const { + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + for (const HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { + if (const HDFirearm* mountedFirearm = dynamic_cast(mountedDevice); mountedFirearm && mountedFirearm->GetRoundInMagCount() != 0) { + return true; + } + } + } + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LookForMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an MO detecting ray in the direction of where the head is looking -// at the time. Factors including head rotation, sharp aim mode, and -// other variables determine how this ray is cast. - -MovableObject * ACrab::LookForMOs(float FOVSpread, unsigned char ignoreMaterial, bool ignoreAllTerrain) -{ - MovableObject *pSeenMO = 0; - Vector aimPos = m_Pos; - float aimDistance = m_AimDistance + g_FrameMan.GetPlayerScreenWidth() * 0.51; // Set the length of the look vector - - // If aiming down the barrel, look through that - if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) - { - aimPos = m_pTurret->GetFirstMountedDevice()->GetPos(); - aimDistance += m_pTurret->GetFirstMountedDevice()->GetSharpLength(); - } - // If just looking, use the sensors on the turret instead - else if (m_pTurret && m_pTurret->IsAttached()) - aimPos = GetEyePos(); - // If no turret... - else - aimPos = GetCPUPos(); - - // Create the vector to trace along - Vector lookVector(aimDistance, 0); - // Set the rotation to the actual aiming angle - Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); - aimMatrix.SetXFlipped(m_HFlipped); - lookVector *= aimMatrix; - // Add the spread - lookVector.DegRotate(FOVSpread * RandomNormalNum()); - - MOID seenMOID = g_SceneMan.CastMORay(aimPos, lookVector, m_MOID, IgnoresWhichTeam(), ignoreMaterial, ignoreAllTerrain, 5); - pSeenMO = g_MovableMan.GetMOFromID(seenMOID); - if (pSeenMO) - return pSeenMO->GetRootParent(); - - return pSeenMO; -} - -void ACrab::OnNewMovePath() -{ - Actor::OnNewMovePath(); - - // Process the new path we now have, if any - if (!m_MovePath.empty()) - { - // Smash all airborne waypoints down to just above the ground, except for when it makes the path intersect terrain or it is the final destination - std::list::iterator finalItr = m_MovePath.end(); - finalItr--; - Vector smashedPoint; - Vector previousPoint = *(m_MovePath.begin()); - std::list::iterator nextItr = m_MovePath.begin(); - for (std::list::iterator lItr = m_MovePath.begin(); lItr != finalItr; ++lItr) - { - nextItr++; - smashedPoint = g_SceneMan.MovePointToGround((*lItr), m_CharHeight*0.2, 7); - - // Only smash if the new location doesn't cause the path to intersect hard terrain ahead or behind of it - // Try three times to halve the height to see if that won't intersect - for (int i = 0; i < 3; i++) - { - Vector notUsed; - if (!g_SceneMan.CastStrengthRay(previousPoint, smashedPoint - previousPoint, 5, notUsed, 3, g_MaterialDoor) && - nextItr != m_MovePath.end() && !g_SceneMan.CastStrengthRay(smashedPoint, (*nextItr) - smashedPoint, 5, notUsed, 3, g_MaterialDoor)) - { - (*lItr) = smashedPoint; - break; - } - else - smashedPoint.m_Y -= ((smashedPoint.m_Y - (*lItr).m_Y) / 2); - } - - previousPoint = (*lItr); - } - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmIsEmpty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is out of ammo. -////////////////////////////////////////////////////////////////////////////////////////// + bool ACrab::FirearmIsEmpty() const { + return !FirearmIsReady() && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice(); + } -void ACrab::PreControllerUpdate() -{ - ZoneScoped; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmNeedsReload + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is almost out of ammo. - Actor::PreControllerUpdate(); + bool ACrab::FirearmsAreFull() const { + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + for (const HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { + if (const HDFirearm* mountedFirearm = dynamic_cast(mountedDevice); mountedFirearm && !mountedFirearm->IsFull()) { + return false; + } + } + } + return true; + } - float deltaTime = g_TimerMan.GetDeltaTimeSecs(); - float mass = GetMass(); + ////////////////////////////////////////////////////////////////////////////////////////// - Vector analogAim = m_Controller.GetAnalogAim(); - const float analogAimDeadzone = 0.1F; + bool ACrab::FirearmNeedsReload() const { + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + for (const HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { + if (const HDFirearm* mountedFirearm = dynamic_cast(mountedDevice); mountedFirearm && mountedFirearm->NeedsReloading()) { + return true; + } + } + } - // Set Default direction of all the paths! - for (int side = 0; side < SIDECOUNT; ++side) - { - for (int layer = 0; layer < LAYERCOUNT; ++layer) - { - m_Paths[side][layer][WALK].SetHFlip(m_HFlipped); - m_Paths[side][layer][STAND].SetHFlip(m_HFlipped); - } - } + return false; + } - if (m_pJetpack && m_pJetpack->IsAttached()) { - m_pJetpack->UpdateBurstState(*this); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmIsSemiAuto + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is semi or full auto. + + bool ACrab::FirearmIsSemiAuto() const { + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + HDFirearm* pWeapon = dynamic_cast(m_pTurret->GetFirstMountedDevice()); + return pWeapon && !pWeapon->IsFullAuto(); + } + return false; } - //////////////////////////////////// - // Movement direction - const float movementThreshold = 1.0F; - bool isStill = (m_Vel + m_PrevVel).MagnitudeIsLessThan(movementThreshold); - - // If the pie menu is on, try to preserve whatever move state we had before it going into effect. - // This is only done for digital input, where the user needs to use the keyboard to choose pie slices. - // For analog input, this doesn't matter - the mouse or aiming analog stick controls the pie menu. - bool keepOldState = m_Controller.IsKeyboardOnlyControlled() && m_Controller.IsState(PIE_MENU_ACTIVE); - - if (!keepOldState) { - if (m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP && m_Status != INACTIVE) { - if (m_MoveState != JUMP) - { - // Restart the stride if we're just starting to walk or crawl - if (m_MoveState != WALK) - { - m_StrideStart[LEFTSIDE] = true; - m_StrideStart[RIGHTSIDE] = true; - MoveOutOfTerrain(g_MaterialGrass); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: ReloadFirearms + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reloads the currently held firearms, if any. + // Arguments: None. + // Return value: None. + + void ACrab::ReloadFirearms() { + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + for (HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { + if (HDFirearm* mountedFirearm = dynamic_cast(mountedDevice)) { + mountedFirearm->Reload(); } + } + } + } - m_MoveState = WALK; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmActivationDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the currently held device's delay between pulling the trigger + // and activating. + + int ACrab::FirearmActivationDelay() const { + // Check if the currently held device is already the desired type + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + HDFirearm* pWeapon = dynamic_cast(m_pTurret->GetFirstMountedDevice()); + if (pWeapon) + return pWeapon->GetActivationDelay(); + } - for (int side = 0; side < SIDECOUNT; ++side) - { - m_Paths[side][FGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); - m_Paths[side][BGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsWithinRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a point on the scene is within range of the currently + // used device and aiming status, if applicable. + + bool ACrab::IsWithinRange(Vector& point) const { + if (m_SharpAimMaxedOut) + return true; + + Vector diff = g_SceneMan.ShortestDistance(m_Pos, point, false); + float sqrDistance = diff.GetSqrMagnitude(); + + // Really close! + if (sqrDistance <= (m_CharHeight * m_CharHeight)) { + return true; + } + + // Start with the default aim distance + float range = m_AimDistance; + + // Add the sharp range of the equipped weapon + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + range += m_pTurret->GetFirstMountedDevice()->GetSharpLength() * m_SharpAimProgress; + } + + return sqrDistance <= (range * range); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Look + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an unseen-revealing ray in the direction of where this is facing. + // Arguments: The degree angle to deviate from the current view point in the ray + // casting. A random ray will be chosen out of this +-range. + + bool ACrab::Look(float FOVSpread, float range) { + if (!g_SceneMan.AnythingUnseen(m_Team)) + return false; + + // Set the length of the look vector + float aimDistance = m_AimDistance + range; + Vector aimPos = GetCPUPos(); + + // If aiming down the barrel, look through that + if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + aimPos = m_pTurret->GetFirstMountedDevice()->GetPos(); + aimDistance += m_pTurret->GetFirstMountedDevice()->GetSharpLength(); + } + // If just looking, use the sensors on the turret instead + else if (m_pTurret && m_pTurret->IsAttached()) { + aimPos = GetEyePos(); + } + + // Create the vector to trace along + Vector lookVector(aimDistance, 0); + // Set the rotation to the actual aiming angle + Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); + aimMatrix.SetXFlipped(m_HFlipped); + lookVector *= aimMatrix; + // Add the spread + lookVector.DegRotate(FOVSpread * RandomNormalNum()); + + // TODO: generate an alarm event if we spot an enemy actor? + + Vector ignored; + // Cast the seeing ray, adjusting the skip to match the resolution of the unseen map + return g_SceneMan.CastSeeRay(m_Team, aimPos, lookVector, ignored, 25, (int)g_SceneMan.GetUnseenResolution(m_Team).GetSmallest() / 2); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: LookForMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an MO detecting ray in the direction of where the head is looking + // at the time. Factors including head rotation, sharp aim mode, and + // other variables determine how this ray is cast. + + MovableObject* ACrab::LookForMOs(float FOVSpread, unsigned char ignoreMaterial, bool ignoreAllTerrain) { + MovableObject* pSeenMO = 0; + Vector aimPos = m_Pos; + float aimDistance = m_AimDistance + g_FrameMan.GetPlayerScreenWidth() * 0.51; // Set the length of the look vector + + // If aiming down the barrel, look through that + if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + aimPos = m_pTurret->GetFirstMountedDevice()->GetPos(); + aimDistance += m_pTurret->GetFirstMountedDevice()->GetSharpLength(); + } + // If just looking, use the sensors on the turret instead + else if (m_pTurret && m_pTurret->IsAttached()) + aimPos = GetEyePos(); + // If no turret... + else + aimPos = GetCPUPos(); + + // Create the vector to trace along + Vector lookVector(aimDistance, 0); + // Set the rotation to the actual aiming angle + Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); + aimMatrix.SetXFlipped(m_HFlipped); + lookVector *= aimMatrix; + // Add the spread + lookVector.DegRotate(FOVSpread * RandomNormalNum()); + + MOID seenMOID = g_SceneMan.CastMORay(aimPos, lookVector, m_MOID, IgnoresWhichTeam(), ignoreMaterial, ignoreAllTerrain, 5); + pSeenMO = g_MovableMan.GetMOFromID(seenMOID); + if (pSeenMO) + return pSeenMO->GetRootParent(); + + return pSeenMO; + } + + void ACrab::OnNewMovePath() { + Actor::OnNewMovePath(); + + // Process the new path we now have, if any + if (!m_MovePath.empty()) { + // Smash all airborne waypoints down to just above the ground, except for when it makes the path intersect terrain or it is the final destination + std::list::iterator finalItr = m_MovePath.end(); + finalItr--; + Vector smashedPoint; + Vector previousPoint = *(m_MovePath.begin()); + std::list::iterator nextItr = m_MovePath.begin(); + for (std::list::iterator lItr = m_MovePath.begin(); lItr != finalItr; ++lItr) { + nextItr++; + smashedPoint = g_SceneMan.MovePointToGround((*lItr), m_CharHeight * 0.2, 7); + + // Only smash if the new location doesn't cause the path to intersect hard terrain ahead or behind of it + // Try three times to halve the height to see if that won't intersect + for (int i = 0; i < 3; i++) { + Vector notUsed; + if (!g_SceneMan.CastStrengthRay(previousPoint, smashedPoint - previousPoint, 5, notUsed, 3, g_MaterialDoor) && + nextItr != m_MovePath.end() && !g_SceneMan.CastStrengthRay(smashedPoint, (*nextItr) - smashedPoint, 5, notUsed, 3, g_MaterialDoor)) { + (*lItr) = smashedPoint; + break; + } else + smashedPoint.m_Y -= ((smashedPoint.m_Y - (*lItr).m_Y) / 2); } + + previousPoint = (*lItr); } + } + } - // Walk backwards if the aiming is already focused in the opposite direction of travel. - if (std::abs(analogAim.m_X) > 0 || m_Controller.IsState(AIM_SHARP)) { - for (int side = 0; side < SIDECOUNT; ++side) { - m_Paths[side][FGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); - m_Paths[side][BGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); + ////////////////////////////////////////////////////////////////////////////////////////// + + void ACrab::PreControllerUpdate() { + ZoneScoped; + + Actor::PreControllerUpdate(); + + float deltaTime = g_TimerMan.GetDeltaTimeSecs(); + float mass = GetMass(); + + Vector analogAim = m_Controller.GetAnalogAim(); + const float analogAimDeadzone = 0.1F; + + // Set Default direction of all the paths! + for (int side = 0; side < SIDECOUNT; ++side) { + for (int layer = 0; layer < LAYERCOUNT; ++layer) { + m_Paths[side][layer][WALK].SetHFlip(m_HFlipped); + m_Paths[side][layer][STAND].SetHFlip(m_HFlipped); + } + } + + if (m_pJetpack && m_pJetpack->IsAttached()) { + m_pJetpack->UpdateBurstState(*this); + } + + //////////////////////////////////// + // Movement direction + const float movementThreshold = 1.0F; + bool isStill = (m_Vel + m_PrevVel).MagnitudeIsLessThan(movementThreshold); + + // If the pie menu is on, try to preserve whatever move state we had before it going into effect. + // This is only done for digital input, where the user needs to use the keyboard to choose pie slices. + // For analog input, this doesn't matter - the mouse or aiming analog stick controls the pie menu. + bool keepOldState = m_Controller.IsKeyboardOnlyControlled() && m_Controller.IsState(PIE_MENU_ACTIVE); + + if (!keepOldState) { + if (m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP && m_Status != INACTIVE) { + if (m_MoveState != JUMP) { + // Restart the stride if we're just starting to walk or crawl + if (m_MoveState != WALK) { + m_StrideStart[LEFTSIDE] = true; + m_StrideStart[RIGHTSIDE] = true; + MoveOutOfTerrain(g_MaterialGrass); + } + + m_MoveState = WALK; + + for (int side = 0; side < SIDECOUNT; ++side) { + m_Paths[side][FGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); + m_Paths[side][BGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); + } + } + + // Walk backwards if the aiming is already focused in the opposite direction of travel. + if (std::abs(analogAim.m_X) > 0 || m_Controller.IsState(AIM_SHARP)) { + for (int side = 0; side < SIDECOUNT; ++side) { + m_Paths[side][FGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); + m_Paths[side][BGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); + } + } else if ((m_Controller.IsState(MOVE_RIGHT) && m_HFlipped) || (m_Controller.IsState(MOVE_LEFT) && !m_HFlipped)) { + m_HFlipped = !m_HFlipped; + m_CheckTerrIntersection = true; + MoveOutOfTerrain(g_MaterialGrass); + for (int side = 0; side < SIDECOUNT; ++side) { + for (int layer = 0; layer < LAYERCOUNT; ++layer) { + m_Paths[side][layer][m_MoveState].SetHFlip(m_HFlipped); + m_Paths[side][layer][WALK].Terminate(); + m_Paths[side][layer][STAND].Terminate(); + } + m_StrideStart[side] = true; + } } - } else if ((m_Controller.IsState(MOVE_RIGHT) && m_HFlipped) || (m_Controller.IsState(MOVE_LEFT) && !m_HFlipped)) { + } else { + m_MoveState = STAND; + } + } + + //////////////////////////////////// + // Reload held MO, if applicable + + if (m_Controller.IsState(WEAPON_RELOAD) && !FirearmsAreFull() && m_Status != INACTIVE) { + ReloadFirearms(); + + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); + } + + // Interrupt sharp aiming + m_SharpAimTimer.Reset(); + m_SharpAimProgress = 0; + } + + //////////////////////////////////// + // Aiming + + // Get rotation angle of crab + float rotAngle = GetRotAngle(); + + // Adjust AimRange limits to crab rotation + float adjustedAimRangeUpperLimit = (m_HFlipped) ? m_AimRangeUpperLimit - rotAngle : m_AimRangeUpperLimit + rotAngle; + float adjustedAimRangeLowerLimit = (m_HFlipped) ? -m_AimRangeLowerLimit - rotAngle : -m_AimRangeLowerLimit + rotAngle; + + if (m_Controller.IsState(AIM_UP) && m_Status != INACTIVE) { + // Set the timer to a base number so we don't get a sluggish feeling at start. + if (m_AimState != AIMUP) { + m_AimTmr.SetElapsedSimTimeMS(m_AimState == AIMSTILL ? 150 : 300); + } + m_AimState = AIMUP; + m_AimAngle += m_Controller.IsState(AIM_SHARP) ? std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00005F, 0.05F) : std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00015F, 0.15F) * m_Controller.GetDigitalAimSpeed(); + + } else if (m_Controller.IsState(AIM_DOWN) && m_Status != INACTIVE) { + // Set the timer to a base number so we don't get a sluggish feeling at start. + if (m_AimState != AIMDOWN) { + m_AimTmr.SetElapsedSimTimeMS(m_AimState == AIMSTILL ? 150 : 300); + } + m_AimState = AIMDOWN; + m_AimAngle -= m_Controller.IsState(AIM_SHARP) ? std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00005F, 0.05F) : std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00015F, 0.15F) * m_Controller.GetDigitalAimSpeed(); + + } else if (analogAim.MagnitudeIsGreaterThan(analogAimDeadzone) && m_Status != INACTIVE) { + // Hack to avoid the GetAbsRadAngle to mangle an aim angle straight down + if (analogAim.m_X == 0) { + analogAim.m_X += 0.01F * GetFlipFactor(); + } + m_AimAngle = analogAim.GetAbsRadAngle(); + + // Check for flip change + if ((analogAim.m_X > 0 && m_HFlipped) || (analogAim.m_X < 0 && !m_HFlipped)) { m_HFlipped = !m_HFlipped; + // Instead of simply carving out a silhouette of the now flipped actor, isntead disable any atoms which are embedded int eh terrain until they emerge again + // m_ForceDeepCheck = true; m_CheckTerrIntersection = true; MoveOutOfTerrain(g_MaterialGrass); - for (int side = 0; side < SIDECOUNT; ++side) - { - for (int layer = 0; layer < LAYERCOUNT; ++layer) - { + for (int side = 0; side < SIDECOUNT; ++side) { + for (int layer = 0; layer < LAYERCOUNT; ++layer) { m_Paths[side][layer][m_MoveState].SetHFlip(m_HFlipped); m_Paths[side][layer][WALK].Terminate(); m_Paths[side][layer][STAND].Terminate(); @@ -1075,760 +1139,717 @@ void ACrab::PreControllerUpdate() m_StrideStart[side] = true; } } - } else { - m_MoveState = STAND; - } - } + // Correct angle based on flip + m_AimAngle = FacingAngle(m_AimAngle); + + // Clamp the analog aim too, so it doesn't feel "sticky" at the edges of the aim limit + if (m_Controller.IsPlayerControlled() && m_LockMouseAimInput) { + float mouseAngle = g_UInputMan.AnalogAimValues(m_Controller.GetPlayer()).GetAbsRadAngle(); + Clamp(mouseAngle, FacingAngle(adjustedAimRangeUpperLimit), FacingAngle(adjustedAimRangeLowerLimit)); + g_UInputMan.SetMouseValueAngle(mouseAngle, m_Controller.GetPlayer()); + } + } else + m_AimState = AIMSTILL; + + // Clamp aim angle so it's within adjusted limit ranges, for all control types + Clamp(m_AimAngle, adjustedAimRangeUpperLimit, adjustedAimRangeLowerLimit); - //////////////////////////////////// - // Reload held MO, if applicable - - if (m_Controller.IsState(WEAPON_RELOAD) && !FirearmsAreFull() && m_Status != INACTIVE) { - ReloadFirearms(); - - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - - // Interrupt sharp aiming - m_SharpAimTimer.Reset(); - m_SharpAimProgress = 0; - } - - //////////////////////////////////// - // Aiming - - // Get rotation angle of crab - float rotAngle = GetRotAngle(); - - // Adjust AimRange limits to crab rotation - float adjustedAimRangeUpperLimit = (m_HFlipped) ? m_AimRangeUpperLimit - rotAngle : m_AimRangeUpperLimit + rotAngle; - float adjustedAimRangeLowerLimit = (m_HFlipped) ? -m_AimRangeLowerLimit - rotAngle : -m_AimRangeLowerLimit + rotAngle; - - if (m_Controller.IsState(AIM_UP) && m_Status != INACTIVE) { - // Set the timer to a base number so we don't get a sluggish feeling at start. - if (m_AimState != AIMUP) { m_AimTmr.SetElapsedSimTimeMS(m_AimState == AIMSTILL ? 150 : 300); } - m_AimState = AIMUP; - m_AimAngle += m_Controller.IsState(AIM_SHARP) ? std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00005F, 0.05F) : std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00015F, 0.15F) * m_Controller.GetDigitalAimSpeed(); - - } else if (m_Controller.IsState(AIM_DOWN) && m_Status != INACTIVE) { - // Set the timer to a base number so we don't get a sluggish feeling at start. - if (m_AimState != AIMDOWN) { m_AimTmr.SetElapsedSimTimeMS(m_AimState == AIMSTILL ? 150 : 300); } - m_AimState = AIMDOWN; - m_AimAngle -= m_Controller.IsState(AIM_SHARP) ? std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00005F, 0.05F) : std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00015F, 0.15F) * m_Controller.GetDigitalAimSpeed(); - - } else if (analogAim.MagnitudeIsGreaterThan(analogAimDeadzone) && m_Status != INACTIVE) { - // Hack to avoid the GetAbsRadAngle to mangle an aim angle straight down - if (analogAim.m_X == 0) { analogAim.m_X += 0.01F * GetFlipFactor(); } - m_AimAngle = analogAim.GetAbsRadAngle(); - - // Check for flip change - if ((analogAim.m_X > 0 && m_HFlipped) || (analogAim.m_X < 0 && !m_HFlipped)) { - m_HFlipped = !m_HFlipped; - // Instead of simply carving out a silhouette of the now flipped actor, isntead disable any atoms which are embedded int eh terrain until they emerge again - //m_ForceDeepCheck = true; - m_CheckTerrIntersection = true; - MoveOutOfTerrain(g_MaterialGrass); - for (int side = 0; side < SIDECOUNT; ++side) - { - for (int layer = 0; layer < LAYERCOUNT; ++layer) - { - m_Paths[side][layer][m_MoveState].SetHFlip(m_HFlipped); - m_Paths[side][layer][WALK].Terminate(); - m_Paths[side][layer][STAND].Terminate(); - } - m_StrideStart[side] = true; - } - } - // Correct angle based on flip - m_AimAngle = FacingAngle(m_AimAngle); - - // Clamp the analog aim too, so it doesn't feel "sticky" at the edges of the aim limit - if (m_Controller.IsPlayerControlled() && m_LockMouseAimInput) { - float mouseAngle = g_UInputMan.AnalogAimValues(m_Controller.GetPlayer()).GetAbsRadAngle(); - Clamp(mouseAngle, FacingAngle(adjustedAimRangeUpperLimit), FacingAngle(adjustedAimRangeLowerLimit)); - g_UInputMan.SetMouseValueAngle(mouseAngle, m_Controller.GetPlayer()); - } - } - else - m_AimState = AIMSTILL; - - // Clamp aim angle so it's within adjusted limit ranges, for all control types - Clamp(m_AimAngle, adjustedAimRangeUpperLimit, adjustedAimRangeLowerLimit); - - ////////////////////////////// - // Sharp aim calculation - - if (m_Controller.IsState(AIM_SHARP) && m_Status == STABLE && m_Vel.MagnitudeIsLessThan(5.0F)) { - float aimMag = analogAim.GetMagnitude(); - - // If aim sharp is being done digitally, then translate to full magnitude. - if (aimMag < 0.1F) { aimMag = 1.0F; } - if (m_MoveState == WALK) { aimMag *= 0.3F; } - - if (m_SharpAimTimer.IsPastSimMS(m_SharpAimDelay)) { - // Only go slower outward. - if (m_SharpAimProgress < aimMag) { - m_SharpAimProgress += (aimMag - m_SharpAimProgress) * 0.035F; + ////////////////////////////// + // Sharp aim calculation + + if (m_Controller.IsState(AIM_SHARP) && m_Status == STABLE && m_Vel.MagnitudeIsLessThan(5.0F)) { + float aimMag = analogAim.GetMagnitude(); + + // If aim sharp is being done digitally, then translate to full magnitude. + if (aimMag < 0.1F) { + aimMag = 1.0F; + } + if (m_MoveState == WALK) { + aimMag *= 0.3F; + } + + if (m_SharpAimTimer.IsPastSimMS(m_SharpAimDelay)) { + // Only go slower outward. + if (m_SharpAimProgress < aimMag) { + m_SharpAimProgress += (aimMag - m_SharpAimProgress) * 0.035F; + } else { + m_SharpAimProgress = aimMag; + } } else { - m_SharpAimProgress = aimMag; + m_SharpAimProgress *= 0.95F; } } else { - m_SharpAimProgress *= 0.95F; + m_SharpAimProgress = std::max(m_SharpAimProgress * 0.95F - 0.1F, 0.0F); } - } else { - m_SharpAimProgress = std::max(m_SharpAimProgress * 0.95F - 0.1F, 0.0F); - } - //////////////////////////////////// - // Fire/Activate held devices - - if (m_pTurret && m_pTurret->IsAttached() && m_Status != INACTIVE) { - for (HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { - mountedDevice->SetSharpAim(m_SharpAimProgress); - if (m_Controller.IsState(WEAPON_FIRE)) { - mountedDevice->Activate(); - if (mountedDevice->IsEmpty()) { mountedDevice->Reload(); } - } else { - mountedDevice->Deactivate(); - } - } - } - - // Controller disabled - if (m_Controller.IsDisabled()) - { - m_MoveState = STAND; - if (m_pJetpack && m_pJetpack->IsAttached()) - m_pJetpack->EnableEmission(false); - } - -// m_aSprite->SetAngle((m_AimAngle / 180) * 3.141592654); -// m_aSprite->SetScale(2.0); - - - /////////////////////////////////////////////////// - // Travel the limb AtomGroup:s - - m_StrideFrame = false; - - if (m_Status == STABLE && !m_LimbPushForcesAndCollisionsDisabled) - { - // This exists to support disabling foot collisions if the limbpath has that flag set. - if ((m_pLFGFootGroup->GetAtomCount() == 0 && m_BackupLFGFootGroup->GetAtomCount() > 0) != m_Paths[LEFTSIDE][FGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { - m_BackupLFGFootGroup->SetLimbPos(m_pLFGFootGroup->GetLimbPos()); - std::swap(m_pLFGFootGroup, m_BackupLFGFootGroup); - } - if ((m_pLBGFootGroup->GetAtomCount() == 0 && m_BackupLBGFootGroup->GetAtomCount() > 0) != m_Paths[LEFTSIDE][BGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { - m_BackupLBGFootGroup->SetLimbPos(m_pLBGFootGroup->GetLimbPos()); - std::swap(m_pLBGFootGroup, m_BackupLBGFootGroup); - } - if ((m_pRFGFootGroup->GetAtomCount() == 0 && m_BackupRFGFootGroup->GetAtomCount() > 0) != m_Paths[RIGHTSIDE][FGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { - m_BackupRFGFootGroup->SetLimbPos(m_pRFGFootGroup->GetLimbPos()); - std::swap(m_pRFGFootGroup, m_BackupRFGFootGroup); - } - if ((m_pRBGFootGroup->GetAtomCount() == 0 && m_BackupRBGFootGroup->GetAtomCount() > 0) != m_Paths[RIGHTSIDE][BGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { - m_BackupRBGFootGroup->SetLimbPos(m_pRBGFootGroup->GetLimbPos()); - std::swap(m_pRBGFootGroup, m_BackupRBGFootGroup); - } - - // WALKING - if (m_MoveState == WALK) - { - for (int side = 0; side < SIDECOUNT; ++side) - for (int layer = 0; layer < LAYERCOUNT; ++layer) - m_Paths[side][layer][STAND].Terminate(); - - float LFGLegProg = m_Paths[LEFTSIDE][FGROUND][WALK].GetRegularProgress(); - float LBGLegProg = m_Paths[LEFTSIDE][BGROUND][WALK].GetRegularProgress(); - float RFGLegProg = m_Paths[RIGHTSIDE][FGROUND][WALK].GetRegularProgress(); - float RBGLegProg = m_Paths[RIGHTSIDE][BGROUND][WALK].GetRegularProgress(); - - bool restarted = false; - Matrix walkAngle(rotAngle * 0.5F); - - // Make sure we are starting a stride if we're basically stopped. - if (isStill) { m_StrideStart[LEFTSIDE] = true; } - - ////////////////// - // LEFT LEGS - - if (m_pLFGLeg && (!m_pLBGLeg || (!(m_Paths[LEFTSIDE][FGROUND][WALK].PathEnded() && LBGLegProg < 0.5F) || m_StrideStart[LEFTSIDE]))) { - m_StrideTimer[LEFTSIDE].Reset(); - m_pLFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pLFGLeg->GetParentOffset()), m_Vel, walkAngle, m_Paths[LEFTSIDE][FGROUND][WALK], deltaTime, &restarted); - } + //////////////////////////////////// + // Fire/Activate held devices - if (m_pLBGLeg) { - if (!m_pLFGLeg || !(m_Paths[LEFTSIDE][BGROUND][WALK].PathEnded() && LFGLegProg < 0.5F)) { - m_StrideStart[LEFTSIDE] = false; - m_StrideTimer[LEFTSIDE].Reset(); - m_pLBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pLBGLeg->GetParentOffset()), m_Vel, walkAngle, m_Paths[LEFTSIDE][BGROUND][WALK], deltaTime); + if (m_pTurret && m_pTurret->IsAttached() && m_Status != INACTIVE) { + for (HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { + mountedDevice->SetSharpAim(m_SharpAimProgress); + if (m_Controller.IsState(WEAPON_FIRE)) { + mountedDevice->Activate(); + if (mountedDevice->IsEmpty()) { + mountedDevice->Reload(); + } } else { - m_pLBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLBGLeg->GetParentOffset()), m_pLBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pLBGLeg->GetMass(), deltaTime); + mountedDevice->Deactivate(); } } + } + + // Controller disabled + if (m_Controller.IsDisabled()) { + m_MoveState = STAND; + if (m_pJetpack && m_pJetpack->IsAttached()) + m_pJetpack->EnableEmission(false); + } - // Reset the left-side walking stride if it's taking longer than it should. - if (m_StrideTimer[LEFTSIDE].IsPastSimMS(static_cast(m_Paths[LEFTSIDE][FGROUND][WALK].GetTotalPathTime() * 1.1F))) { m_StrideStart[LEFTSIDE] = true; } + // m_aSprite->SetAngle((m_AimAngle / 180) * 3.141592654); + // m_aSprite->SetScale(2.0); - /////////////////// - // RIGHT LEGS + /////////////////////////////////////////////////// + // Travel the limb AtomGroup:s - if (m_pRFGLeg) { - if (!m_pRBGLeg || !(m_Paths[RIGHTSIDE][FGROUND][WALK].PathEnded() && RBGLegProg < 0.5F)) { - m_StrideStart[RIGHTSIDE] = false; + m_StrideFrame = false; + + if (m_Status == STABLE && !m_LimbPushForcesAndCollisionsDisabled) { + // This exists to support disabling foot collisions if the limbpath has that flag set. + if ((m_pLFGFootGroup->GetAtomCount() == 0 && m_BackupLFGFootGroup->GetAtomCount() > 0) != m_Paths[LEFTSIDE][FGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupLFGFootGroup->SetLimbPos(m_pLFGFootGroup->GetLimbPos()); + std::swap(m_pLFGFootGroup, m_BackupLFGFootGroup); + } + if ((m_pLBGFootGroup->GetAtomCount() == 0 && m_BackupLBGFootGroup->GetAtomCount() > 0) != m_Paths[LEFTSIDE][BGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupLBGFootGroup->SetLimbPos(m_pLBGFootGroup->GetLimbPos()); + std::swap(m_pLBGFootGroup, m_BackupLBGFootGroup); + } + if ((m_pRFGFootGroup->GetAtomCount() == 0 && m_BackupRFGFootGroup->GetAtomCount() > 0) != m_Paths[RIGHTSIDE][FGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupRFGFootGroup->SetLimbPos(m_pRFGFootGroup->GetLimbPos()); + std::swap(m_pRFGFootGroup, m_BackupRFGFootGroup); + } + if ((m_pRBGFootGroup->GetAtomCount() == 0 && m_BackupRBGFootGroup->GetAtomCount() > 0) != m_Paths[RIGHTSIDE][BGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupRBGFootGroup->SetLimbPos(m_pRBGFootGroup->GetLimbPos()); + std::swap(m_pRBGFootGroup, m_BackupRBGFootGroup); + } + + // WALKING + if (m_MoveState == WALK) { + for (int side = 0; side < SIDECOUNT; ++side) + for (int layer = 0; layer < LAYERCOUNT; ++layer) + m_Paths[side][layer][STAND].Terminate(); + + float LFGLegProg = m_Paths[LEFTSIDE][FGROUND][WALK].GetRegularProgress(); + float LBGLegProg = m_Paths[LEFTSIDE][BGROUND][WALK].GetRegularProgress(); + float RFGLegProg = m_Paths[RIGHTSIDE][FGROUND][WALK].GetRegularProgress(); + float RBGLegProg = m_Paths[RIGHTSIDE][BGROUND][WALK].GetRegularProgress(); + + bool restarted = false; + Matrix walkAngle(rotAngle * 0.5F); + + // Make sure we are starting a stride if we're basically stopped. + if (isStill) { + m_StrideStart[LEFTSIDE] = true; + } + + ////////////////// + // LEFT LEGS + + if (m_pLFGLeg && (!m_pLBGLeg || (!(m_Paths[LEFTSIDE][FGROUND][WALK].PathEnded() && LBGLegProg < 0.5F) || m_StrideStart[LEFTSIDE]))) { + m_StrideTimer[LEFTSIDE].Reset(); + m_pLFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pLFGLeg->GetParentOffset()), m_Vel, walkAngle, m_Paths[LEFTSIDE][FGROUND][WALK], deltaTime, &restarted); + } + + if (m_pLBGLeg) { + if (!m_pLFGLeg || !(m_Paths[LEFTSIDE][BGROUND][WALK].PathEnded() && LFGLegProg < 0.5F)) { + m_StrideStart[LEFTSIDE] = false; + m_StrideTimer[LEFTSIDE].Reset(); + m_pLBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pLBGLeg->GetParentOffset()), m_Vel, walkAngle, m_Paths[LEFTSIDE][BGROUND][WALK], deltaTime); + } else { + m_pLBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLBGLeg->GetParentOffset()), m_pLBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pLBGLeg->GetMass(), deltaTime); + } + } + + // Reset the left-side walking stride if it's taking longer than it should. + if (m_StrideTimer[LEFTSIDE].IsPastSimMS(static_cast(m_Paths[LEFTSIDE][FGROUND][WALK].GetTotalPathTime() * 1.1F))) { + m_StrideStart[LEFTSIDE] = true; + } + + /////////////////// + // RIGHT LEGS + + if (m_pRFGLeg) { + if (!m_pRBGLeg || !(m_Paths[RIGHTSIDE][FGROUND][WALK].PathEnded() && RBGLegProg < 0.5F)) { + m_StrideStart[RIGHTSIDE] = false; + m_StrideTimer[RIGHTSIDE].Reset(); + m_pRFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pRFGLeg->GetParentOffset()), m_Vel, walkAngle, m_Paths[RIGHTSIDE][FGROUND][WALK], deltaTime, &restarted); + } else { + m_pRFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRFGLeg->GetParentOffset()), m_pRFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pRFGLeg->GetMass(), deltaTime); + } + } + + if (m_pRBGLeg && (!m_pRFGLeg || (!(m_Paths[RIGHTSIDE][BGROUND][WALK].PathEnded() && RFGLegProg < 0.5F) || m_StrideStart[RIGHTSIDE]))) { m_StrideTimer[RIGHTSIDE].Reset(); - m_pRFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pRFGLeg->GetParentOffset()), m_Vel, walkAngle, m_Paths[RIGHTSIDE][FGROUND][WALK], deltaTime, &restarted); - } else { - m_pRFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRFGLeg->GetParentOffset()), m_pRFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pRFGLeg->GetMass(), deltaTime); + m_pRBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pRBGLeg->GetParentOffset()), m_Vel, walkAngle, m_Paths[RIGHTSIDE][BGROUND][WALK], deltaTime); } - } - if (m_pRBGLeg && (!m_pRFGLeg || (!(m_Paths[RIGHTSIDE][BGROUND][WALK].PathEnded() && RFGLegProg < 0.5F) || m_StrideStart[RIGHTSIDE]))) { - m_StrideTimer[RIGHTSIDE].Reset(); - m_pRBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pRBGLeg->GetParentOffset()), m_Vel, walkAngle, m_Paths[RIGHTSIDE][BGROUND][WALK], deltaTime); - } + // Reset the right-side walking stride if it's taking longer than it should. + if (m_StrideTimer[RIGHTSIDE].IsPastSimMS(static_cast(m_Paths[RIGHTSIDE][FGROUND][WALK].GetTotalPathTime() * 1.1F))) { + m_StrideStart[RIGHTSIDE] = true; + } - // Reset the right-side walking stride if it's taking longer than it should. - if (m_StrideTimer[RIGHTSIDE].IsPastSimMS(static_cast(m_Paths[RIGHTSIDE][FGROUND][WALK].GetTotalPathTime() * 1.1F))) { m_StrideStart[RIGHTSIDE] = true; } - - if (m_StrideSound) { - m_StrideSound->SetPosition(m_Pos); - if (m_StrideSound->GetLoopSetting() < 0) { - if (!m_StrideSound->IsBeingPlayed()) { m_StrideSound->Play(); } - } else if (restarted) { - m_StrideSound->Play(); + if (m_StrideSound) { + m_StrideSound->SetPosition(m_Pos); + if (m_StrideSound->GetLoopSetting() < 0) { + if (!m_StrideSound->IsBeingPlayed()) { + m_StrideSound->Play(); + } + } else if (restarted) { + m_StrideSound->Play(); + } } - } - if (restarted) { - m_StrideFrame = true; - RunScriptedFunctionInAppropriateScripts("OnStride"); - } - } else if (m_pLFGLeg || m_pLBGLeg || m_pRFGLeg || m_pRBGLeg) { - if (m_MoveState == JUMP) { - // TODO: Utilize jump paths in an intuitive way? - if (m_pLFGLeg) { m_pLFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLFGLeg->GetParentOffset()), m_pLFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pLFGLeg->GetMass(), deltaTime); } - if (m_pLBGLeg) { m_pLBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLBGLeg->GetParentOffset()), m_pLBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pLBGLeg->GetMass(), deltaTime); } - if (m_pRFGLeg) { m_pRFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRFGLeg->GetParentOffset()), m_pRFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pRFGLeg->GetMass(), deltaTime); } - if (m_pRBGLeg) { m_pRBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRBGLeg->GetParentOffset()), m_pRBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pRBGLeg->GetMass(), deltaTime); } - - if (m_pJetpack == nullptr || m_pJetpack->IsOutOfFuel()) { - m_MoveState = STAND; - m_Paths[LEFTSIDE][FGROUND][JUMP].Terminate(); - m_Paths[LEFTSIDE][BGROUND][JUMP].Terminate(); - m_Paths[LEFTSIDE][FGROUND][STAND].Terminate(); - m_Paths[LEFTSIDE][BGROUND][STAND].Terminate(); - m_Paths[LEFTSIDE][FGROUND][WALK].Terminate(); - m_Paths[LEFTSIDE][BGROUND][WALK].Terminate(); - m_Paths[RIGHTSIDE][FGROUND][JUMP].Terminate(); - m_Paths[RIGHTSIDE][BGROUND][JUMP].Terminate(); - m_Paths[RIGHTSIDE][FGROUND][STAND].Terminate(); - m_Paths[RIGHTSIDE][BGROUND][STAND].Terminate(); - m_Paths[RIGHTSIDE][FGROUND][WALK].Terminate(); - m_Paths[RIGHTSIDE][BGROUND][WALK].Terminate(); + if (restarted) { + m_StrideFrame = true; + RunScriptedFunctionInAppropriateScripts("OnStride"); } - } else { - for (int side = 0; side < SIDECOUNT; ++side) { - for (int layer = 0; layer < LAYERCOUNT; ++layer) { - m_Paths[side][layer][WALK].Terminate(); + } else if (m_pLFGLeg || m_pLBGLeg || m_pRFGLeg || m_pRBGLeg) { + if (m_MoveState == JUMP) { + // TODO: Utilize jump paths in an intuitive way? + if (m_pLFGLeg) { + m_pLFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLFGLeg->GetParentOffset()), m_pLFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pLFGLeg->GetMass(), deltaTime); + } + if (m_pLBGLeg) { + m_pLBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLBGLeg->GetParentOffset()), m_pLBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pLBGLeg->GetMass(), deltaTime); + } + if (m_pRFGLeg) { + m_pRFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRFGLeg->GetParentOffset()), m_pRFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pRFGLeg->GetMass(), deltaTime); + } + if (m_pRBGLeg) { + m_pRBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRBGLeg->GetParentOffset()), m_pRBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pRBGLeg->GetMass(), deltaTime); + } + + if (m_pJetpack == nullptr || m_pJetpack->IsOutOfFuel()) { + m_MoveState = STAND; + m_Paths[LEFTSIDE][FGROUND][JUMP].Terminate(); + m_Paths[LEFTSIDE][BGROUND][JUMP].Terminate(); + m_Paths[LEFTSIDE][FGROUND][STAND].Terminate(); + m_Paths[LEFTSIDE][BGROUND][STAND].Terminate(); + m_Paths[LEFTSIDE][FGROUND][WALK].Terminate(); + m_Paths[LEFTSIDE][BGROUND][WALK].Terminate(); + m_Paths[RIGHTSIDE][FGROUND][JUMP].Terminate(); + m_Paths[RIGHTSIDE][BGROUND][JUMP].Terminate(); + m_Paths[RIGHTSIDE][FGROUND][STAND].Terminate(); + m_Paths[RIGHTSIDE][BGROUND][STAND].Terminate(); + m_Paths[RIGHTSIDE][FGROUND][WALK].Terminate(); + m_Paths[RIGHTSIDE][BGROUND][WALK].Terminate(); + } + } else { + for (int side = 0; side < SIDECOUNT; ++side) { + for (int layer = 0; layer < LAYERCOUNT; ++layer) { + m_Paths[side][layer][WALK].Terminate(); + } + } + if (m_pLFGLeg) { + m_pLFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pLFGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFTSIDE][FGROUND][STAND], deltaTime, nullptr, !m_pRFGLeg); + } + + if (m_pLBGLeg) { + m_pLBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pLBGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFTSIDE][BGROUND][STAND], deltaTime); + } + + if (m_pRFGLeg) { + m_pRFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pRFGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHTSIDE][FGROUND][STAND], deltaTime, nullptr, !m_pLFGLeg); + } + + if (m_pRBGLeg) { + m_pRBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pRBGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHTSIDE][BGROUND][STAND], deltaTime); } } - if (m_pLFGLeg) { m_pLFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pLFGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFTSIDE][FGROUND][STAND], deltaTime, nullptr, !m_pRFGLeg); } + } + } else { + // Not stable/standing, so make sure the end of limbs are moving around limply in a ragdoll fashion. + // TODO: Make the limb atom groups fly around and react to terrain, without getting stuck etc. + if (m_pLFGLeg) { + m_pLFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLFGLeg->GetParentOffset()), m_pLFGLeg->GetMaxLength(), m_PrevVel * m_pLFGLeg->GetJointStiffness(), m_AngularVel, m_pLFGLeg->GetMass(), deltaTime); + } - if (m_pLBGLeg) { m_pLBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pLBGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFTSIDE][BGROUND][STAND], deltaTime); } + if (m_pLBGLeg) { + m_pLBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLBGLeg->GetParentOffset()), m_pLBGLeg->GetMaxLength(), m_PrevVel * m_pLBGLeg->GetJointStiffness(), m_AngularVel, m_pLBGLeg->GetMass(), deltaTime); + } - if (m_pRFGLeg) { m_pRFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pRFGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHTSIDE][FGROUND][STAND], deltaTime, nullptr, !m_pLFGLeg); } + if (m_pRFGLeg) { + m_pRFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRFGLeg->GetParentOffset()), m_pRFGLeg->GetMaxLength(), m_PrevVel * m_pRFGLeg->GetJointStiffness(), m_AngularVel, m_pRFGLeg->GetMass(), deltaTime); + } - if (m_pRBGLeg) { m_pRBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pRBGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHTSIDE][BGROUND][STAND], deltaTime); } + if (m_pRBGLeg) { + m_pRBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRBGLeg->GetParentOffset()), m_pRBGLeg->GetMaxLength(), m_PrevVel * m_pRBGLeg->GetJointStiffness(), m_AngularVel, m_pRBGLeg->GetMass(), deltaTime); } } - } else { - // Not stable/standing, so make sure the end of limbs are moving around limply in a ragdoll fashion. - // TODO: Make the limb atom groups fly around and react to terrain, without getting stuck etc. - if (m_pLFGLeg) { m_pLFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLFGLeg->GetParentOffset()), m_pLFGLeg->GetMaxLength(), m_PrevVel * m_pLFGLeg->GetJointStiffness(), m_AngularVel, m_pLFGLeg->GetMass(), deltaTime); } + if (m_MoveState != WALK && m_StrideSound && m_StrideSound->GetLoopSetting() < 0) { + m_StrideSound->Stop(); + } - if (m_pLBGLeg) { m_pLBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pLBGLeg->GetParentOffset()), m_pLBGLeg->GetMaxLength(), m_PrevVel * m_pLBGLeg->GetJointStiffness(), m_AngularVel, m_pLBGLeg->GetMass(), deltaTime); } + ///////////////////////////////// + // Manage Attachable:s + if (m_pTurret && m_pTurret->IsAttached()) { + m_pTurret->SetMountedDeviceRotationOffset((m_AimAngle * GetFlipFactor()) - m_Rotation.GetRadAngle()); + } - if (m_pRFGLeg) { m_pRFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRFGLeg->GetParentOffset()), m_pRFGLeg->GetMaxLength(), m_PrevVel * m_pRFGLeg->GetJointStiffness(), m_AngularVel, m_pRFGLeg->GetMass(), deltaTime); } + if (m_pLFGLeg && m_pLFGLeg->IsAttached()) { + m_pLFGLeg->EnableIdle(m_Status != UNSTABLE); + m_pLFGLeg->SetTargetPosition(m_pLFGFootGroup->GetLimbPos(m_HFlipped)); + } - if (m_pRBGLeg) { m_pRBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pRBGLeg->GetParentOffset()), m_pRBGLeg->GetMaxLength(), m_PrevVel * m_pRBGLeg->GetJointStiffness(), m_AngularVel, m_pRBGLeg->GetMass(), deltaTime); } + if (m_pLBGLeg && m_pLBGLeg->IsAttached()) { + m_pLBGLeg->EnableIdle(m_Status != UNSTABLE); + m_pLBGLeg->SetTargetPosition(m_pLBGFootGroup->GetLimbPos(m_HFlipped)); + } + + if (m_pRFGLeg && m_pRFGLeg->IsAttached()) { + m_pRFGLeg->EnableIdle(m_Status != UNSTABLE); + m_pRFGLeg->SetTargetPosition(m_pRFGFootGroup->GetLimbPos(m_HFlipped)); + } + + if (m_pRBGLeg && m_pRBGLeg->IsAttached()) { + m_pRBGLeg->EnableIdle(m_Status != UNSTABLE); + m_pRBGLeg->SetTargetPosition(m_pRBGFootGroup->GetLimbPos(m_HFlipped)); + } } - if (m_MoveState != WALK && m_StrideSound && m_StrideSound->GetLoopSetting() < 0) { - m_StrideSound->Stop(); - } - - ///////////////////////////////// - // Manage Attachable:s - if (m_pTurret && m_pTurret->IsAttached()) { - m_pTurret->SetMountedDeviceRotationOffset((m_AimAngle * GetFlipFactor()) - m_Rotation.GetRadAngle()); - } - - if (m_pLFGLeg && m_pLFGLeg->IsAttached()) { - m_pLFGLeg->EnableIdle(m_Status != UNSTABLE); - m_pLFGLeg->SetTargetPosition(m_pLFGFootGroup->GetLimbPos(m_HFlipped)); - } - - if (m_pLBGLeg && m_pLBGLeg->IsAttached()) { - m_pLBGLeg->EnableIdle(m_Status != UNSTABLE); - m_pLBGLeg->SetTargetPosition(m_pLBGFootGroup->GetLimbPos(m_HFlipped)); - } - - if (m_pRFGLeg && m_pRFGLeg->IsAttached()) { - m_pRFGLeg->EnableIdle(m_Status != UNSTABLE); - m_pRFGLeg->SetTargetPosition(m_pRFGFootGroup->GetLimbPos(m_HFlipped)); - } - - if (m_pRBGLeg && m_pRBGLeg->IsAttached()) { - m_pRBGLeg->EnableIdle(m_Status != UNSTABLE); - m_pRBGLeg->SetTargetPosition(m_pRBGFootGroup->GetLimbPos(m_HFlipped)); - } -} -////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// -void ACrab::Update() -{ - ZoneScoped; - - Actor::Update(); - - //////////////////////////////////// - // Update viewpoint - - // Set viewpoint based on how we are aiming etc. - Vector aimSight(m_AimDistance, 0); - Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); - aimMatrix.SetXFlipped(m_HFlipped); - // Reset this each frame - m_SharpAimMaxedOut = false; - - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) - { - float maxLength = m_pTurret->GetFirstMountedDevice()->GetSharpLength(); - - // Use a non-terrain check ray to cap the magnitude, so we can't see into objects etc - if (m_SharpAimProgress > 0) - { - Vector notUsed; - Vector sharpAimVector(maxLength, 0); - sharpAimVector *= aimMatrix; - - // See how far along the sharp aim vector there is opaque air -// float result = g_SceneMan.CastNotMaterialRay(m_pLFGLeg->GetFirstMountedDevice()->GetMuzzlePos(), sharpAimVector, g_MaterialAir, 5); - float result = g_SceneMan.CastObstacleRay(m_pTurret->GetFirstMountedDevice()->GetMuzzlePos(), sharpAimVector, notUsed, notUsed, GetRootID(), IgnoresWhichTeam(), g_MaterialAir, 5); - // If we didn't find anything but air before the sharpdistance, then don't alter the sharp distance - if (result >= 0 && result < (maxLength * m_SharpAimProgress)) - { - m_SharpAimProgress = result / maxLength; - m_SharpAimMaxedOut = true; - } - } - // Indicate maxed outedness if we really are, too - if (m_SharpAimProgress > 0.9) - m_SharpAimMaxedOut = true; - -// sharpDistance *= m_Controller.GetAnalogAim().GetMagnitude(); - aimSight.m_X += maxLength * m_SharpAimProgress; - } - - // Rotate the aiming spot vector and add it to the view point - aimSight *= aimMatrix; - m_ViewPoint = m_Pos.GetFloored() + aimSight; - - // Add velocity also so the viewpoint moves ahead at high speeds - if (m_Vel.MagnitudeIsGreaterThan(10.0F)) - m_ViewPoint += m_Vel * std::sqrt(m_Vel.GetMagnitude() * 0.1F); - -/* Done by pie menu now, see HandlePieCommand() - //////////////////////////////////////// - // AI mode setting - - if (m_Controller.IsState(AI_MODE_SET)) - { - if (m_Controller.IsState(PRESS_RIGHT)) - { - m_AIMode = AIMODE_BRAINHUNT; - m_UpdateMovePath = true; - } - else if (m_Controller.IsState(PRESS_LEFT)) - { - m_AIMode = AIMODE_PATROL; - } - else if (m_Controller.IsState(PRESS_UP)) - { - m_AIMode = AIMODE_SENTRY; - } - else if (m_Controller.IsState(PRESS_DOWN)) - { - m_AIMode = AIMODE_GOLDDIG; - } - - m_DeviceState = SCANNING; - } -*/ - - //////////////////////////////////////// - // Balance stuff - - // Get the rotation in radians. - float rot = m_Rotation.GetRadAngle(); -// rot = fabs(rot) < c_QuarterPI ? rot : (rot > 0 ? c_QuarterPI : -c_QuarterPI); - // Eliminate full rotations - while (fabs(rot) > c_TwoPI) { - rot -= rot > 0 ? c_TwoPI : -c_TwoPI; - } - // Eliminate rotations over half a turn - if (fabs(rot) > c_PI) - { - rot = (rot > 0 ? -c_PI : c_PI) + (rot - (rot > 0 ? c_PI : -c_PI)); - // If we're upside down, we're unstable damnit - if (m_Status == STABLE) { m_Status = UNSTABLE; } - m_StableRecoverTimer.Reset(); - } - - // Rotational balancing spring calc - if (m_Status == STABLE) - { - // Upright body posture - m_AngularVel = m_AngularVel * 0.9F - (rot * 0.3F); - } - // While dying, pull body quickly toward down toward horizontal - else if (m_Status == DYING) - { - float rotTarget = rot > 0 ? c_HalfPI : -c_HalfPI; -// float rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; - float rotDiff = rotTarget - rot; - if (!m_DeathTmr.IsPastSimMS(125) && fabs(rotDiff) > 0.1 && fabs(rotDiff) < c_PI) - { - m_AngularVel += rotDiff * 0.5;//fabs(rotDiff); -// m_Vel.m_X += (m_HFlipped ? -fabs(rotDiff) : fabs(rotDiff)) * 0.35; - m_Vel.m_X += (rotTarget > 0 ? -fabs(rotDiff) : fabs(rotDiff)) * 0.35; - } - else - m_Status = DEAD; - -// else if (fabs(m_AngularVel) > 0.1) -// m_AngularVel *= 0.5; - } - m_Rotation.SetRadAngle(rot); - - /////////////////////////////////////////////////// - // Death detection and handling - - // Losing all limbs should kill... eventually - if (!m_pLFGLeg && !m_pLBGLeg && !m_pRFGLeg && !m_pRBGLeg && m_Status != DYING && m_Status != DEAD) - m_Health -= 0.1; - - ///////////////////////////////////////// - // Misc. - -// m_DeepCheck = true/*m_Status == DEAD*/; -} - - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ACrab::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { - Actor::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawHandAndFootGroupVisualizations()) { - m_pLFGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); - m_pLBGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); - m_pRFGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); - m_pRBGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); - } - - if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawLimbPathVisualizations()) { - m_Paths[LEFTSIDE][BGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); - m_Paths[LEFTSIDE][FGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); - m_Paths[RIGHTSIDE][BGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); - m_Paths[RIGHTSIDE][FGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); - } -} + void ACrab::Update() { + ZoneScoped; + Actor::Update(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Actor's current graphical HUD overlay representation to a -// BITMAP of choice. + //////////////////////////////////// + // Update viewpoint -void ACrab::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScreen, bool playerControlled) { - m_HUDStack = -m_CharHeight / 2; + // Set viewpoint based on how we are aiming etc. + Vector aimSight(m_AimDistance, 0); + Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); + aimMatrix.SetXFlipped(m_HFlipped); + // Reset this each frame + m_SharpAimMaxedOut = false; - // Only do HUD if on a team - if (m_Team < 0) - return; + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + float maxLength = m_pTurret->GetFirstMountedDevice()->GetSharpLength(); - // Only draw if the team viewing this is on the same team OR has seen the space where this is located. - int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); - if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam && (!g_SettingsMan.ShowEnemyHUD() || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam))) { - return; - } + // Use a non-terrain check ray to cap the magnitude, so we can't see into objects etc + if (m_SharpAimProgress > 0) { + Vector notUsed; + Vector sharpAimVector(maxLength, 0); + sharpAimVector *= aimMatrix; + + // See how far along the sharp aim vector there is opaque air + // float result = g_SceneMan.CastNotMaterialRay(m_pLFGLeg->GetFirstMountedDevice()->GetMuzzlePos(), sharpAimVector, g_MaterialAir, 5); + float result = g_SceneMan.CastObstacleRay(m_pTurret->GetFirstMountedDevice()->GetMuzzlePos(), sharpAimVector, notUsed, notUsed, GetRootID(), IgnoresWhichTeam(), g_MaterialAir, 5); + // If we didn't find anything but air before the sharpdistance, then don't alter the sharp distance + if (result >= 0 && result < (maxLength * m_SharpAimProgress)) { + m_SharpAimProgress = result / maxLength; + m_SharpAimMaxedOut = true; + } + } + // Indicate maxed outedness if we really are, too + if (m_SharpAimProgress > 0.9) + m_SharpAimMaxedOut = true; - Actor::DrawHUD(pTargetBitmap, targetPos, whichScreen); + // sharpDistance *= m_Controller.GetAnalogAim().GetMagnitude(); + aimSight.m_X += maxLength * m_SharpAimProgress; + } - if (!m_HUDVisible) { - return; + // Rotate the aiming spot vector and add it to the view point + aimSight *= aimMatrix; + m_ViewPoint = m_Pos.GetFloored() + aimSight; + + // Add velocity also so the viewpoint moves ahead at high speeds + if (m_Vel.MagnitudeIsGreaterThan(10.0F)) + m_ViewPoint += m_Vel * std::sqrt(m_Vel.GetMagnitude() * 0.1F); + + /* Done by pie menu now, see HandlePieCommand() + //////////////////////////////////////// + // AI mode setting + + if (m_Controller.IsState(AI_MODE_SET)) + { + if (m_Controller.IsState(PRESS_RIGHT)) + { + m_AIMode = AIMODE_BRAINHUNT; + m_UpdateMovePath = true; + } + else if (m_Controller.IsState(PRESS_LEFT)) + { + m_AIMode = AIMODE_PATROL; + } + else if (m_Controller.IsState(PRESS_UP)) + { + m_AIMode = AIMODE_SENTRY; + } + else if (m_Controller.IsState(PRESS_DOWN)) + { + m_AIMode = AIMODE_GOLDDIG; + } + + m_DeviceState = SCANNING; + } + */ + + //////////////////////////////////////// + // Balance stuff + + // Get the rotation in radians. + float rot = m_Rotation.GetRadAngle(); + // rot = fabs(rot) < c_QuarterPI ? rot : (rot > 0 ? c_QuarterPI : -c_QuarterPI); + // Eliminate full rotations + while (fabs(rot) > c_TwoPI) { + rot -= rot > 0 ? c_TwoPI : -c_TwoPI; + } + // Eliminate rotations over half a turn + if (fabs(rot) > c_PI) { + rot = (rot > 0 ? -c_PI : c_PI) + (rot - (rot > 0 ? c_PI : -c_PI)); + // If we're upside down, we're unstable damnit + if (m_Status == STABLE) { + m_Status = UNSTABLE; + } + m_StableRecoverTimer.Reset(); + } + + // Rotational balancing spring calc + if (m_Status == STABLE) { + // Upright body posture + m_AngularVel = m_AngularVel * 0.9F - (rot * 0.3F); + } + // While dying, pull body quickly toward down toward horizontal + else if (m_Status == DYING) { + float rotTarget = rot > 0 ? c_HalfPI : -c_HalfPI; + // float rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; + float rotDiff = rotTarget - rot; + if (!m_DeathTmr.IsPastSimMS(125) && fabs(rotDiff) > 0.1 && fabs(rotDiff) < c_PI) { + m_AngularVel += rotDiff * 0.5; // fabs(rotDiff); + // m_Vel.m_X += (m_HFlipped ? -fabs(rotDiff) : fabs(rotDiff)) * 0.35; + m_Vel.m_X += (rotTarget > 0 ? -fabs(rotDiff) : fabs(rotDiff)) * 0.35; + } else + m_Status = DEAD; + + // else if (fabs(m_AngularVel) > 0.1) + // m_AngularVel *= 0.5; + } + m_Rotation.SetRadAngle(rot); + + /////////////////////////////////////////////////// + // Death detection and handling + + // Losing all limbs should kill... eventually + if (!m_pLFGLeg && !m_pLBGLeg && !m_pRFGLeg && !m_pRBGLeg && m_Status != DYING && m_Status != DEAD) + m_Health -= 0.1; + + ///////////////////////////////////////// + // Misc. + + // m_DeepCheck = true/*m_Status == DEAD*/; } -/* -// TODO: REMOVE< THIS IS TEMP - // Draw the AI paths - list::iterator last = m_MovePath.begin(); - Vector waypoint, lastPoint, lineVec; - for (list::iterator lItr = m_MovePath.begin(); lItr != m_MovePath.end(); ++lItr) - { - lastPoint = (*last) - targetPos; - waypoint = lastPoint + g_SceneMan.ShortestDistance(lastPoint, (*lItr) - targetPos); - line(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, waypoint.m_X, waypoint.m_Y, g_RedColor); - last = lItr; - } - waypoint = m_MoveTarget - targetPos; - circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 3, g_RedColor); - lastPoint = m_PrevPathTarget - targetPos; - circlefill(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, 2, g_YellowGlowColor); - lastPoint = m_DigTunnelEndPos - targetPos; - circlefill(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, 2, g_YellowGlowColor); - // Raidus -// waypoint = m_Pos - targetPos; -// circle(pTargetBitmap, waypoint.m_X, waypoint.m_Y, m_MoveProximityLimit, g_RedColor); -// TODO: REMOVE THIS IS TEMP -*/ - - // Player AI drawing - - if ((m_Controller.IsState(AIM_SHARP) || (m_Controller.IsPlayerControlled() && !m_Controller.IsState(PIE_MENU_ACTIVE))) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - m_pTurret->GetFirstMountedDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsState(AIM_SHARP) && m_Controller.IsPlayerControlled()); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACrab::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { + Actor::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + + if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawHandAndFootGroupVisualizations()) { + m_pLFGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); + m_pLBGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); + m_pRFGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); + m_pRBGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); + } + + if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawLimbPathVisualizations()) { + m_Paths[LEFTSIDE][BGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); + m_Paths[LEFTSIDE][FGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); + m_Paths[RIGHTSIDE][BGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); + m_Paths[RIGHTSIDE][FGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); + } } - ////////////////////////////////////// - // Draw stat info HUD - char str[64]; - - GUIFont *pSymbolFont = g_FrameMan.GetLargeFont(); - GUIFont *pSmallFont = g_FrameMan.GetSmallFont(); - - // Only show extra HUD if this guy is controlled by the same player that this screen belongs to - if (m_Controller.IsPlayerControlled() && g_ActivityMan.GetActivity()->ScreenOfPlayer(m_Controller.GetPlayer()) == whichScreen && pSmallFont && pSymbolFont) - { - AllegroBitmap allegroBitmap(pTargetBitmap); - - Vector drawPos = m_Pos - targetPos; - - // Adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam - if (!targetPos.IsZero()) - { - // Spans vertical scene seam - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) - { - if ((targetPos.m_X < 0) && (m_Pos.m_X > (sceneWidth - pTargetBitmap->w))) - drawPos.m_X -= sceneWidth; - else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_Pos.m_X < pTargetBitmap->w)) - drawPos.m_X += sceneWidth; - } - // Spans horizontal scene seam - int sceneHeight = g_SceneMan.GetSceneHeight(); - if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) - { - if ((targetPos.m_Y < 0) && (m_Pos.m_Y > (sceneHeight - pTargetBitmap->h))) - drawPos.m_Y -= sceneHeight; - else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_Pos.m_Y < pTargetBitmap->h)) - drawPos.m_Y += sceneHeight; - } - } - - // Held-related GUI stuff - if (m_pTurret) { - std::string textString; - for (const HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { - if (const HDFirearm *mountedFirearm = dynamic_cast(mountedDevice)) { - if (!textString.empty()) { textString += " | "; } - int totalTextWidth = pSmallFont->CalculateWidth(textString); - if (mountedFirearm->IsReloading()) { - textString += "Reloading"; - rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1 + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, drawPos.GetFloorIntX() + 29 + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 14, 245); - rectfill(pTargetBitmap, drawPos.GetFloorIntX() + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 12, drawPos.GetFloorIntX() + static_cast(28.0F * mountedFirearm->GetReloadProgress() + 0.5F) + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, 77); - } else { - textString += mountedFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(mountedFirearm->GetRoundInMagCount()); - } - } - } - if (!textString.empty()) { - str[0] = -56; str[1] = 0; - pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 10, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Left); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 0, drawPos.GetFloorIntY() + m_HUDStack + 3, textString, GUIFont::Left); - m_HUDStack -= 9; - } - } else { - std::snprintf(str, sizeof(str), "NO TURRET!"); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X + 2, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Centre); - m_HUDStack += -9; - } - - if (m_pJetpack && m_Status != INACTIVE && !m_Controller.IsState(PIE_MENU_ACTIVE) && (m_Controller.IsState(BODY_JUMP) || !m_pJetpack->IsFullyFueled())) { - if (m_pJetpack->GetJetTimeLeft() < 100.0F) { - str[0] = m_IconBlinkTimer.AlternateSim(100) ? -26 : -25; - } else if (m_pJetpack->IsEmitting()) { - float acceleration = m_pJetpack->EstimateImpulse(false) / std::max(GetMass(), 0.1F); - if (acceleration > 0.41F) { - str[0] = acceleration > 0.47F ? -31 : -30; - } else { - str[0] = acceleration > 0.35F ? -29 : -28; - if (m_IconBlinkTimer.AlternateSim(200)) { str[0] = -27; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Actor's current graphical HUD overlay representation to a + // BITMAP of choice. + + void ACrab::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { + m_HUDStack = -m_CharHeight / 2; + + // Only do HUD if on a team + if (m_Team < 0) + return; + + // Only draw if the team viewing this is on the same team OR has seen the space where this is located. + int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); + if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam && (!g_SettingsMan.ShowEnemyHUD() || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam))) { + return; + } + + Actor::DrawHUD(pTargetBitmap, targetPos, whichScreen); + + if (!m_HUDVisible) { + return; + } + /* + // TODO: REMOVE< THIS IS TEMP + // Draw the AI paths + list::iterator last = m_MovePath.begin(); + Vector waypoint, lastPoint, lineVec; + for (list::iterator lItr = m_MovePath.begin(); lItr != m_MovePath.end(); ++lItr) + { + lastPoint = (*last) - targetPos; + waypoint = lastPoint + g_SceneMan.ShortestDistance(lastPoint, (*lItr) - targetPos); + line(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, waypoint.m_X, waypoint.m_Y, g_RedColor); + last = lItr; + } + waypoint = m_MoveTarget - targetPos; + circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 3, g_RedColor); + lastPoint = m_PrevPathTarget - targetPos; + circlefill(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, 2, g_YellowGlowColor); + lastPoint = m_DigTunnelEndPos - targetPos; + circlefill(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, 2, g_YellowGlowColor); + // Raidus + // waypoint = m_Pos - targetPos; + // circle(pTargetBitmap, waypoint.m_X, waypoint.m_Y, m_MoveProximityLimit, g_RedColor); + // TODO: REMOVE THIS IS TEMP + */ + + // Player AI drawing + + if ((m_Controller.IsState(AIM_SHARP) || (m_Controller.IsPlayerControlled() && !m_Controller.IsState(PIE_MENU_ACTIVE))) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + m_pTurret->GetFirstMountedDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsState(AIM_SHARP) && m_Controller.IsPlayerControlled()); + } + ////////////////////////////////////// + // Draw stat info HUD + char str[64]; + + GUIFont* pSymbolFont = g_FrameMan.GetLargeFont(); + GUIFont* pSmallFont = g_FrameMan.GetSmallFont(); + + // Only show extra HUD if this guy is controlled by the same player that this screen belongs to + if (m_Controller.IsPlayerControlled() && g_ActivityMan.GetActivity()->ScreenOfPlayer(m_Controller.GetPlayer()) == whichScreen && pSmallFont && pSymbolFont) { + AllegroBitmap allegroBitmap(pTargetBitmap); + + Vector drawPos = m_Pos - targetPos; + + // Adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam + if (!targetPos.IsZero()) { + // Spans vertical scene seam + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) { + if ((targetPos.m_X < 0) && (m_Pos.m_X > (sceneWidth - pTargetBitmap->w))) + drawPos.m_X -= sceneWidth; + else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_Pos.m_X < pTargetBitmap->w)) + drawPos.m_X += sceneWidth; + } + // Spans horizontal scene seam + int sceneHeight = g_SceneMan.GetSceneHeight(); + if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) { + if ((targetPos.m_Y < 0) && (m_Pos.m_Y > (sceneHeight - pTargetBitmap->h))) + drawPos.m_Y -= sceneHeight; + else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_Pos.m_Y < pTargetBitmap->h)) + drawPos.m_Y += sceneHeight; + } + } + + // Held-related GUI stuff + if (m_pTurret) { + std::string textString; + for (const HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { + if (const HDFirearm* mountedFirearm = dynamic_cast(mountedDevice)) { + if (!textString.empty()) { + textString += " | "; + } + int totalTextWidth = pSmallFont->CalculateWidth(textString); + if (mountedFirearm->IsReloading()) { + textString += "Reloading"; + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1 + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, drawPos.GetFloorIntX() + 29 + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 14, 245); + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 12, drawPos.GetFloorIntX() + static_cast(28.0F * mountedFirearm->GetReloadProgress() + 0.5F) + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, 77); + } else { + textString += mountedFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(mountedFirearm->GetRoundInMagCount()); + } + } + } + if (!textString.empty()) { + str[0] = -56; + str[1] = 0; + pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 10, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Left); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 0, drawPos.GetFloorIntY() + m_HUDStack + 3, textString, GUIFont::Left); + m_HUDStack -= 9; } } else { - str[0] = -27; + std::snprintf(str, sizeof(str), "NO TURRET!"); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X + 2, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Centre); + m_HUDStack += -9; } - str[1] = 0; - pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 7, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Centre); - - rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 7, drawPos.GetFloorIntX() + 15, drawPos.GetFloorIntY() + m_HUDStack + 8, 245); - if (m_pJetpack->GetJetTimeTotal() > 0.0F) { - float jetTimeRatio = m_pJetpack->GetJetTimeRatio(); - int gaugeColor; - if (jetTimeRatio > 0.75F) { - gaugeColor = 149; - } else if (jetTimeRatio > 0.5F) { - gaugeColor = 133; - } else if (jetTimeRatio > 0.375F) { - gaugeColor = 77; - } else if (jetTimeRatio > 0.25F) { - gaugeColor = 48; + + if (m_pJetpack && m_Status != INACTIVE && !m_Controller.IsState(PIE_MENU_ACTIVE) && (m_Controller.IsState(BODY_JUMP) || !m_pJetpack->IsFullyFueled())) { + if (m_pJetpack->GetJetTimeLeft() < 100.0F) { + str[0] = m_IconBlinkTimer.AlternateSim(100) ? -26 : -25; + } else if (m_pJetpack->IsEmitting()) { + float acceleration = m_pJetpack->EstimateImpulse(false) / std::max(GetMass(), 0.1F); + if (acceleration > 0.41F) { + str[0] = acceleration > 0.47F ? -31 : -30; + } else { + str[0] = acceleration > 0.35F ? -29 : -28; + if (m_IconBlinkTimer.AlternateSim(200)) { + str[0] = -27; + } + } } else { - gaugeColor = 13; + str[0] = -27; + } + str[1] = 0; + pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 7, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Centre); + + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 7, drawPos.GetFloorIntX() + 15, drawPos.GetFloorIntY() + m_HUDStack + 8, 245); + if (m_pJetpack->GetJetTimeTotal() > 0.0F) { + float jetTimeRatio = m_pJetpack->GetJetTimeRatio(); + int gaugeColor; + if (jetTimeRatio > 0.75F) { + gaugeColor = 149; + } else if (jetTimeRatio > 0.5F) { + gaugeColor = 133; + } else if (jetTimeRatio > 0.375F) { + gaugeColor = 77; + } else if (jetTimeRatio > 0.25F) { + gaugeColor = 48; + } else { + gaugeColor = 13; + } + rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 6, drawPos.GetFloorIntX() + static_cast(15.0F * jetTimeRatio), drawPos.GetFloorIntY() + m_HUDStack + 7, gaugeColor); } - rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 6, drawPos.GetFloorIntX() + static_cast(15.0F * jetTimeRatio), drawPos.GetFloorIntY() + m_HUDStack + 7, gaugeColor); + m_HUDStack -= 9; } - m_HUDStack -= 9; - } - - // Print aim angle and rot angle stoff - /*{ - std::snprintf(str, sizeof(str), "Aim %.2f Rot %.2f Lim %.2f", m_AimAngle, GetRotAngle(), m_AimRange + GetRotAngle()); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X - 0, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Centre); - - m_HUDStack += -10; - }*/ - -/* - // AI Mode select GUI HUD - if (m_Controller.IsState(AI_MODE_SET)) - { - int iconOff = m_apAIIcons[0]->w + 2; - int iconColor = m_Team == Activity::TeamOne ? AIICON_RED : AIICON_GREEN; - Vector iconPos = GetCPUPos() - targetPos; - - if (m_AIMode == AIMODE_SENTRY) - { - std::snprintf(str, sizeof(str), "%s", "Sentry"); - pSmallFont->DrawAligned(&allegroBitmap, iconPos.m_X, iconPos.m_Y - 18, str, GUIFont::Centre); - } - else if (m_AIMode == AIMODE_PATROL) - { - std::snprintf(str, sizeof(str), "%s", "Patrol"); - pSmallFont->DrawAligned(&allegroBitmap, iconPos.m_X - 9, iconPos.m_Y - 5, str, GUIFont::Right); - } - else if (m_AIMode == AIMODE_BRAINHUNT) - { - std::snprintf(str, sizeof(str), "%s", "Brainhunt"); - pSmallFont->DrawAligned(&allegroBitmap, iconPos.m_X + 9, iconPos.m_Y - 5, str, GUIFont::Left); - } - else if (m_AIMode == AIMODE_GOLDDIG) - { - std::snprintf(str, sizeof(str), "%s", "Gold Dig"); - pSmallFont->DrawAligned(&allegroBitmap, iconPos.m_X, iconPos.m_Y + 8, str, GUIFont::Centre); - } - - // Draw the mode alternatives if they are not the current one - if (m_AIMode != AIMODE_SENTRY) - { - draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_SENTRY], iconPos.m_X - 6, iconPos.m_Y - 6 - iconOff); - } - if (m_AIMode != AIMODE_PATROL) - { - draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_PATROL], iconPos.m_X - 6 - iconOff, iconPos.m_Y - 6); - } - if (m_AIMode != AIMODE_BRAINHUNT) - { - draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_BRAINHUNT], iconPos.m_X - 6 + iconOff, iconPos.m_Y - 6); - } - if (m_AIMode != AIMODE_GOLDDIG) - { - draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_GOLDDIG], iconPos.m_X - 6, iconPos.m_Y - 6 + iconOff); - } - } -*/ - } -} -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLimbPathSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get walking limb path speed for the specified preset. + // Print aim angle and rot angle stoff + /*{ + std::snprintf(str, sizeof(str), "Aim %.2f Rot %.2f Lim %.2f", m_AimAngle, GetRotAngle(), m_AimRange + GetRotAngle()); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X - 0, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Centre); + + m_HUDStack += -10; + }*/ + + /* + // AI Mode select GUI HUD + if (m_Controller.IsState(AI_MODE_SET)) + { + int iconOff = m_apAIIcons[0]->w + 2; + int iconColor = m_Team == Activity::TeamOne ? AIICON_RED : AIICON_GREEN; + Vector iconPos = GetCPUPos() - targetPos; + + if (m_AIMode == AIMODE_SENTRY) + { + std::snprintf(str, sizeof(str), "%s", "Sentry"); + pSmallFont->DrawAligned(&allegroBitmap, iconPos.m_X, iconPos.m_Y - 18, str, GUIFont::Centre); + } + else if (m_AIMode == AIMODE_PATROL) + { + std::snprintf(str, sizeof(str), "%s", "Patrol"); + pSmallFont->DrawAligned(&allegroBitmap, iconPos.m_X - 9, iconPos.m_Y - 5, str, GUIFont::Right); + } + else if (m_AIMode == AIMODE_BRAINHUNT) + { + std::snprintf(str, sizeof(str), "%s", "Brainhunt"); + pSmallFont->DrawAligned(&allegroBitmap, iconPos.m_X + 9, iconPos.m_Y - 5, str, GUIFont::Left); + } + else if (m_AIMode == AIMODE_GOLDDIG) + { + std::snprintf(str, sizeof(str), "%s", "Gold Dig"); + pSmallFont->DrawAligned(&allegroBitmap, iconPos.m_X, iconPos.m_Y + 8, str, GUIFont::Centre); + } + + // Draw the mode alternatives if they are not the current one + if (m_AIMode != AIMODE_SENTRY) + { + draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_SENTRY], iconPos.m_X - 6, iconPos.m_Y - 6 - iconOff); + } + if (m_AIMode != AIMODE_PATROL) + { + draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_PATROL], iconPos.m_X - 6 - iconOff, iconPos.m_Y - 6); + } + if (m_AIMode != AIMODE_BRAINHUNT) + { + draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_BRAINHUNT], iconPos.m_X - 6 + iconOff, iconPos.m_Y - 6); + } + if (m_AIMode != AIMODE_GOLDDIG) + { + draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_GOLDDIG], iconPos.m_X - 6, iconPos.m_Y - 6 + iconOff); + } + } + */ + } + } -float ACrab::GetLimbPathSpeed(int speedPreset) const -{ - return m_Paths[LEFTSIDE][FGROUND][WALK].GetSpeed(speedPreset); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLimbPathSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get walking limb path speed for the specified preset. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetLimbPathSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Set walking limb path speed for the specified preset. + float ACrab::GetLimbPathSpeed(int speedPreset) const { + return m_Paths[LEFTSIDE][FGROUND][WALK].GetSpeed(speedPreset); + } -void ACrab::SetLimbPathSpeed(int speedPreset, float speed) -{ - m_Paths[LEFTSIDE][FGROUND][WALK].OverrideSpeed(speedPreset, speed); - m_Paths[RIGHTSIDE][FGROUND][WALK].OverrideSpeed(speedPreset, speed); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetLimbPathSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Set walking limb path speed for the specified preset. - m_Paths[LEFTSIDE][BGROUND][WALK].OverrideSpeed(speedPreset, speed); - m_Paths[RIGHTSIDE][BGROUND][WALK].OverrideSpeed(speedPreset, speed); -} + void ACrab::SetLimbPathSpeed(int speedPreset, float speed) { + m_Paths[LEFTSIDE][FGROUND][WALK].OverrideSpeed(speedPreset, speed); + m_Paths[RIGHTSIDE][FGROUND][WALK].OverrideSpeed(speedPreset, speed); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLimbPathPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the force that a limb traveling walking LimbPath can push against -// stuff in the scene with. + m_Paths[LEFTSIDE][BGROUND][WALK].OverrideSpeed(speedPreset, speed); + m_Paths[RIGHTSIDE][BGROUND][WALK].OverrideSpeed(speedPreset, speed); + } -float ACrab::GetLimbPathPushForce() const -{ - return m_Paths[LEFTSIDE][FGROUND][WALK].GetDefaultPushForce(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLimbPathPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the force that a limb traveling walking LimbPath can push against + // stuff in the scene with. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetLimbPathPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the default force that a limb traveling walking LimbPath can push against -// stuff in the scene with. + float ACrab::GetLimbPathPushForce() const { + return m_Paths[LEFTSIDE][FGROUND][WALK].GetDefaultPushForce(); + } -void ACrab::SetLimbPathPushForce(float force) -{ - m_Paths[LEFTSIDE][FGROUND][WALK].OverridePushForce(force); - m_Paths[RIGHTSIDE][FGROUND][WALK].OverridePushForce(force); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetLimbPathPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the default force that a limb traveling walking LimbPath can push against + // stuff in the scene with. - m_Paths[LEFTSIDE][BGROUND][WALK].OverridePushForce(force); - m_Paths[RIGHTSIDE][BGROUND][WALK].OverridePushForce(force); -} + void ACrab::SetLimbPathPushForce(float force) { + m_Paths[LEFTSIDE][FGROUND][WALK].OverridePushForce(force); + m_Paths[RIGHTSIDE][FGROUND][WALK].OverridePushForce(force); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + m_Paths[LEFTSIDE][BGROUND][WALK].OverridePushForce(force); + m_Paths[RIGHTSIDE][BGROUND][WALK].OverridePushForce(force); + } -int ACrab::WhilePieMenuOpenListener(const PieMenu *pieMenu) { - int result = Actor::WhilePieMenuOpenListener(pieMenu); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - for (PieSlice *pieSlice : GetPieMenu()->GetPieSlices()) { - if (pieSlice->GetType() == PieSlice::SliceType::Reload) { - if (m_pTurret && m_pTurret->HasMountedDevice()) { - pieSlice->SetDescription("Reload"); - pieSlice->SetEnabled(!FirearmsAreFull()); - } else { - pieSlice->SetDescription(m_pTurret ? "No Weapons" : "No Turret"); - pieSlice->SetEnabled(false); + int ACrab::WhilePieMenuOpenListener(const PieMenu* pieMenu) { + int result = Actor::WhilePieMenuOpenListener(pieMenu); + + for (PieSlice* pieSlice: GetPieMenu()->GetPieSlices()) { + if (pieSlice->GetType() == PieSlice::SliceType::Reload) { + if (m_pTurret && m_pTurret->HasMountedDevice()) { + pieSlice->SetDescription("Reload"); + pieSlice->SetEnabled(!FirearmsAreFull()); + } else { + pieSlice->SetDescription(m_pTurret ? "No Weapons" : "No Turret"); + pieSlice->SetEnabled(false); + } + break; } - break; } + return result; } - return result; -} - } // namespace RTE diff --git a/Source/Entities/ACrab.h b/Source/Entities/ACrab.h index c915c0a729..8258f884c8 100644 --- a/Source/Entities/ACrab.h +++ b/Source/Entities/ACrab.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,639 +19,605 @@ struct BITMAP; -namespace RTE -{ - -class Turret; -class AEJetpack; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: ACrab -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A crab-like actor with four legs. -// Parent(s): Actor. -// Class history: 10/24/2007 ACrab created. - -class ACrab : public Actor { - friend struct EntityLuaBindings; - - enum Side { - LEFTSIDE = 0, - RIGHTSIDE, - SIDECOUNT - }; - - enum Layer { - FGROUND = 0, - BGROUND, - LAYERCOUNT - }; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions - EntityAllocation(ACrab); - AddScriptFunctionNames(Actor, "OnStride"); - SerializableOverrideMethods; - ClassInfoGetters; - DefaultPieMenuNameGetter(HasObjectInGroup("Turrets") ? "Default Turret Pie Menu" : "Default Crab Pie Menu"); - - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: ACrab - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a ACrab object in system - // memory. Create() should be called before using the object. - // Arguments: None. - - ACrab() { Clear(); } +namespace RTE { + class Turret; + class AEJetpack; ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~ACrab + // Class: ACrab ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a ACrab object before deletion - // from system memory. - // Arguments: None. - - ~ACrab() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ACrab object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ACrab to be identical to another, by deep copy. - // Arguments: A reference to the ACrab to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - - int Create(const ACrab &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire ACrab, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Actor::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetEyePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of this' eye, or equivalent, where look -// vector starts from. -// Arguments: None. -// Return value: A Vector with the absolute position of this' eye or view point. - - Vector GetEyePos() const override; - - - /// - /// Gets the Turret of this ACrab. - /// - /// A pointer to Turret of this ACrab. Ownership is NOT transferred! - Turret * GetTurret() const { return m_pTurret; } - - /// - /// Sets the Turret for this ACrab. Ownership IS transferred! - /// - /// The new Turret to use. - void SetTurret(Turret *newTurret); - - /// - /// Gets the jetpack of this ACrab. - /// - /// A pointer to the jetpack of this ACrab. Ownership is NOT transferred! - AEJetpack * GetJetpack() const { return m_pJetpack; } - - /// - /// Sets the jetpack for this ACrab. Ownership IS Transferred! - /// - /// The new jetpack to use. - void SetJetpack(AEJetpack *newJetpack); - - /// - /// Gets the left foreground Leg of this ACrab. - /// - /// A pointer to the left foreground Leg of this ACrab. Ownership is NOT transferred! - Leg * GetLeftFGLeg() const { return m_pLFGLeg; } - - /// - /// Sets the left foreground Leg for this ACrab. Ownership IS transferred! - /// - /// The new Leg to use. - void SetLeftFGLeg(Leg *newLeg); - - /// - /// Gets the left background Leg of this ACrab. - /// - /// A pointer to the left background Leg of this ACrab. Ownership is NOT transferred! - Leg * GetLeftBGLeg() const { return m_pLBGLeg; } - - /// - /// Sets the left background Leg for this ACrab. Ownership IS transferred! - /// - /// The new Leg to use. - void SetLeftBGLeg(Leg *newLeg); - - /// - /// Gets the right foreground Leg of this ACrab. - /// - /// A pointer to the right foreground Leg of this ACrab. Ownership is NOT transferred! - Leg * GetRightFGLeg() const { return m_pRFGLeg; } - - /// - /// Sets the right foreground Leg for this ACrab. Ownership IS transferred! - /// - /// The new Leg to use. - void SetRightFGLeg(Leg *newLeg); - - /// - /// Gets the right BG Leg of this ACrab. - /// - /// A pointer to the right background Leg of this ACrab. Ownership is NOT transferred! - Leg * GetRightBGLeg() const { return m_pRBGLeg; } - - /// - /// Sets the right background Leg for this ACrab. Ownership IS transferred! - /// - /// The new Leg to use. - void SetRightBGLeg(Leg *newLeg); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. -// Arguments: Reference to the HitData struct which describes the collision. This -// will be modified to represent the results of the collision. -// Return value: Whether the collision has been deemed valid. If false, then disregard -// any impulses in the Hitdata. - - bool CollideAtPoint(HitData &hitData) override; - - /// - /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. - /// - /// The SliceType of the PieSlice being handled. - /// Whether or not the activated PieSlice SliceType was able to be handled. - bool HandlePieCommand(PieSlice::SliceType pieSliceType) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEquippedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whatever is equipped in the turret, if anything. OWNERSHIP IS NOT TRANSFERRED! -// Arguments: None. -// Return value: The currently equipped item, if any. - - MovableObject * GetEquippedItem() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FirearmIsReady -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is ready for use, and has -// ammo etc. -// Arguments: None. -// Return value: Whether a currently HDFirearm (if any) is ready for use. - - bool FirearmIsReady() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FirearmIsEmpty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is out of ammo. -// Arguments: None. -// Return value: Whether a currently HDFirearm (if any) is out of ammo. - - bool FirearmIsEmpty() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FirearmNeedsReload -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is almost out of ammo. -// Arguments: None. -// Return value: Whether a currently HDFirearm (if any) has less than half of ammo left. - - bool FirearmNeedsReload() const; - - /// - /// Gets whether or not all of this ACrab's Turret's HDFirearms are full. - /// - /// Whether or not all of this ACrab's Turret's HDFirearms are full. Will return true if there is no Turret or no HDFirearms. - bool FirearmsAreFull() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FirearmIsSemiAuto -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is semi or full auto. -// Arguments: None. -// Return value: Whether a currently HDFirearm (if any) is a semi auto device. - - bool FirearmIsSemiAuto() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FirearmActivationDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the currently held device's delay between pulling the trigger -// and activating. -// Arguments: None. -// Return value: Delay in ms or zero if not a HDFirearm. - -int FirearmActivationDelay() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReloadFirearms -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reloads the currently held firearms, if any. -// Arguments: None. -// Return value: None. - - void ReloadFirearms(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsWithinRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a point on the scene is within close range of the currently -// used device and aiming status, if applicable. -// Arguments: A Vector witht he aboslute coordinates of a point to check. -// Return value: Whether the point is within close range of this. - - bool IsWithinRange(Vector &point) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Look -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an unseen-revealing ray in the direction of where this is facing. -// Arguments: The degree angle to deviate from the current view point in the ray -// casting. A random ray will be chosen out of this +-range. -// The range, in pixels, beyond the actors sharp aim that the ray will have. -// Return value: Whether any unseen pixels were revealed by this look. - - bool Look(float FOVSpread, float range) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LookForMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an MO detecting ray in the direction of where the head is looking -// at the time. Factors including head rotation, sharp aim mode, and -// other variables determine how this ray is cast. -// Arguments: The degree angle to deviate from the current view point in the ray -// casting. A random ray will be chosen out of this +-range. -// A specific material ID to ignore (see through) -// Whether to ignore all terrain or not (true means 'x-ray vision'). -// Return value: A pointer to the MO seen while looking. - - MovableObject * LookForMOs(float FOVSpread = 45, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false); - - - /// - /// Gets the GUI representation of this ACrab, only defaulting to its Turret or body if no GraphicalIcon has been defined. - /// - /// The graphical representation of this ACrab as a BITMAP. - BITMAP * GetGraphicalIcon() const override; - - - /// - /// Gets whether this ACrab has just taken a stride this frame. - /// - /// Whether this ACrab has taken a stride this frame or not. - bool StrideFrame() const { return m_StrideFrame; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreControllerUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void PreControllerUpdate() override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ACrab's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Actor's current graphical HUD overlay representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// Which player's screen this is being drawn to. May affect what HUD elements -// get drawn etc. -// Return value: None. - - void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - - - /// - /// Gets the LimbPath corresponding to the passed in Side, Layer and MovementState values. - /// - /// Whether to get the left or right side. - /// Whether to get foreground or background LimbPath. - /// Which movement state to get the LimbPath for. - /// The LimbPath corresponding to the passed in Layer and MovementState values. - LimbPath *GetLimbPath(Side side, Layer layer, MovementState movementState) { return &m_Paths[side][layer][movementState]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLimbPathSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get walking limb path speed for the specified preset. -// Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST -// Return value: Limb path speed for the specified preset in m/s. - - float GetLimbPathSpeed(int speedPreset) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLimbPathSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Set walking limb path speed for the specified preset. -// Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST. New speed value in m/s. -// Return value: None. - - void SetLimbPathSpeed(int speedPreset, float speed); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLimbPathPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the default force that a limb traveling walking LimbPath can push against -// stuff in the scene with. -// Arguments: None. -// Return value: The default set force maximum, in kg * m/s^2. - - float GetLimbPathPushForce() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLimbPathPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the default force that a limb traveling walking LimbPath can push against -// stuff in the scene with. -// Arguments: The default set force maximum, in kg * m/s^2. -// Return value: None - - void SetLimbPathPushForce(float force); - - - /// - /// Gets this ACrab's stride sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ACrab's stride sound. - SoundContainer * GetStrideSound() const { return m_StrideSound; } - - /// - /// Sets this ACrab's stride sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ACrab's stride sound. - void SetStrideSound(SoundContainer *newSound) { m_StrideSound = newSound; } - - /// - /// Gets the upper limit of this ACrab's aim range. - /// - /// The upper limit of this ACrab's aim range. - float GetAimRangeUpperLimit() const { return m_AimRangeUpperLimit; } - - /// - /// Sets the upper limit of this ACrab's aim range. - /// - /// The new upper limit of this ACrab's aim range. - void SetAimRangeUpperLimit(float aimRangeUpperLimit) { m_AimRangeUpperLimit = aimRangeUpperLimit; } - - /// - /// Gets the lower limit of this ACrab's aim range. - /// - /// The lower limit of this ACrab's aim range. - float GetAimRangeLowerLimit() const { return m_AimRangeLowerLimit; } - - /// - /// Sets the lower limit of this ACrab's aim range. - /// - /// The new lower limit of this ACrab's aim range. - void SetAimRangeLowerLimit(float aimRangeLowerLimit) { m_AimRangeLowerLimit = aimRangeLowerLimit; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - /// - /// Function that is called when we get a new movepath. - /// This processes and cleans up the movepath. - /// - void OnNewMovePath() override; - - // Member variables - static Entity::ClassInfo m_sClass; - - // Turret which can be mounted with a weapon - Turret *m_pTurret; - //TODO when this class is cleaned up these legs and footgroups should probably be renamed. L and R should be expanded to Left and Right. I think FG and BG can stay as is cause they're everywhere. - // Left Foreground leg. - Leg *m_pLFGLeg; - // Left Background leg. - Leg *m_pLBGLeg; - // Right Foreground leg. - Leg *m_pRFGLeg; - // Right Background leg. - Leg *m_pRBGLeg; - // Limb AtomGroups. - AtomGroup *m_pLFGFootGroup; - AtomGroup *m_BackupLFGFootGroup; - AtomGroup *m_pLBGFootGroup; - AtomGroup *m_BackupLBGFootGroup; - AtomGroup *m_pRFGFootGroup; - AtomGroup *m_BackupRFGFootGroup; - AtomGroup *m_pRBGFootGroup; - AtomGroup *m_BackupRBGFootGroup; - // The sound of the actor taking a step (think robot servo) - SoundContainer *m_StrideSound; - // Jetpack booster. - AEJetpack *m_pJetpack; - // Blink timer - Timer m_IconBlinkTimer; - // Whether a stride was taken this frame or not. - bool m_StrideFrame = false; - // Limb paths for different movement states. - // First which side, then which background/foreground, then the movement state - LimbPath m_Paths[SIDECOUNT][LAYERCOUNT][MOVEMENTSTATECOUNT]; - // Whether was aiming during the last frame too. - bool m_Aiming; - // Controls the start of leg synch. - bool m_StrideStart[SIDECOUNT]; - // Times the strides to make sure they get restarted if they end up too long - Timer m_StrideTimer[SIDECOUNT]; - // The maximum angle MountedMO can be aimed up, positive values only, in radians - float m_AimRangeUpperLimit; - // The maximum angle MountedMO can be aimed down, positive values only, in radians - float m_AimRangeLowerLimit; - - //////////////// - // AI States - - enum DeviceHandlingState { - STILL = 0, - POINTING, - SCANNING, - AIMING, - FIRING, - THROWING, - DIGGING - }; - - enum SweepState { - NOSWEEP = 0, - SWEEPINGUP, - SWEEPUPPAUSE, - SWEEPINGDOWN, - SWEEPDOWNPAUSE - }; - - enum DigState { - NOTDIGGING = 0, - PREDIG, - STARTDIG, - TUNNELING, - FINISHINGDIG, - PAUSEDIGGER - }; - - enum JumpState { - NOTJUMPING = 0, - FORWARDJUMP, - PREUPJUMP, - UPJUMP, - APEXJUMP, - LANDJUMP - }; - - // What the AI is doing with its held devices - DeviceHandlingState m_DeviceState; - // What we are doing with a device sweeping - SweepState m_SweepState; - // The current digging state - DigState m_DigState; - // The current jumping state - JumpState m_JumpState; - // Jumping target, overshoot this and the jump is completed - Vector m_JumpTarget; - // Jumping left or right - bool m_JumpingRight; - // The position of the end of the current tunnel being dug. When it is reached, digging can stop. - Vector m_DigTunnelEndPos; - // The center angle (in rads) for the sweeping motion done duing scannign and digging - float m_SweepCenterAimAngle; - // The range to each direction of the center that the sweeping motion will be done in - float m_SweepRange; - // The absolute coordinates of the last detected gold deposits - Vector m_DigTarget; - // Timer for how long to be shooting at a seen enemy target - Timer m_FireTimer; - // Timer for how long to be shooting at a seen enemy target - Timer m_SweepTimer; - // Timer for how long to be patrolling in a direction - Timer m_PatrolTimer; - // Timer for how long to be firing the jetpack in a direction - Timer m_JumpTimer; - // Whether mouse input should be locked to the aim range, or whether we can "wander" out. For static emplacements like turrets, this is good, but for moving things it's a bit sticky. - bool m_LockMouseAimInput; + // Description: A crab-like actor with four legs. + // Parent(s): Actor. + // Class history: 10/24/2007 ACrab created. + + class ACrab : public Actor { + friend struct EntityLuaBindings; + + enum Side { + LEFTSIDE = 0, + RIGHTSIDE, + SIDECOUNT + }; + + enum Layer { + FGROUND = 0, + BGROUND, + LAYERCOUNT + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(ACrab); + AddScriptFunctionNames(Actor, "OnStride"); + SerializableOverrideMethods; + ClassInfoGetters; + DefaultPieMenuNameGetter(HasObjectInGroup("Turrets") ? "Default Turret Pie Menu" : "Default Crab Pie Menu"); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: ACrab + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a ACrab object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + ACrab() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~ACrab + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a ACrab object before deletion + // from system memory. + // Arguments: None. + + ~ACrab() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ACrab object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ACrab to be identical to another, by deep copy. + // Arguments: A reference to the ACrab to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const ACrab& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire ACrab, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Actor::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetEyePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of this' eye, or equivalent, where look + // vector starts from. + // Arguments: None. + // Return value: A Vector with the absolute position of this' eye or view point. + + Vector GetEyePos() const override; + + /// + /// Gets the Turret of this ACrab. + /// + /// A pointer to Turret of this ACrab. Ownership is NOT transferred! + Turret* GetTurret() const { return m_pTurret; } + + /// + /// Sets the Turret for this ACrab. Ownership IS transferred! + /// + /// The new Turret to use. + void SetTurret(Turret* newTurret); + + /// + /// Gets the jetpack of this ACrab. + /// + /// A pointer to the jetpack of this ACrab. Ownership is NOT transferred! + AEJetpack* GetJetpack() const { return m_pJetpack; } + + /// + /// Sets the jetpack for this ACrab. Ownership IS Transferred! + /// + /// The new jetpack to use. + void SetJetpack(AEJetpack* newJetpack); + + /// + /// Gets the left foreground Leg of this ACrab. + /// + /// A pointer to the left foreground Leg of this ACrab. Ownership is NOT transferred! + Leg* GetLeftFGLeg() const { return m_pLFGLeg; } + + /// + /// Sets the left foreground Leg for this ACrab. Ownership IS transferred! + /// + /// The new Leg to use. + void SetLeftFGLeg(Leg* newLeg); + + /// + /// Gets the left background Leg of this ACrab. + /// + /// A pointer to the left background Leg of this ACrab. Ownership is NOT transferred! + Leg* GetLeftBGLeg() const { return m_pLBGLeg; } + + /// + /// Sets the left background Leg for this ACrab. Ownership IS transferred! + /// + /// The new Leg to use. + void SetLeftBGLeg(Leg* newLeg); + + /// + /// Gets the right foreground Leg of this ACrab. + /// + /// A pointer to the right foreground Leg of this ACrab. Ownership is NOT transferred! + Leg* GetRightFGLeg() const { return m_pRFGLeg; } + + /// + /// Sets the right foreground Leg for this ACrab. Ownership IS transferred! + /// + /// The new Leg to use. + void SetRightFGLeg(Leg* newLeg); + + /// + /// Gets the right BG Leg of this ACrab. + /// + /// A pointer to the right background Leg of this ACrab. Ownership is NOT transferred! + Leg* GetRightBGLeg() const { return m_pRBGLeg; } + + /// + /// Sets the right background Leg for this ACrab. Ownership IS transferred! + /// + /// The new Leg to use. + void SetRightBGLeg(Leg* newLeg); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. + // Arguments: Reference to the HitData struct which describes the collision. This + // will be modified to represent the results of the collision. + // Return value: Whether the collision has been deemed valid. If false, then disregard + // any impulses in the Hitdata. + + bool CollideAtPoint(HitData& hitData) override; + + /// + /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. + /// + /// The SliceType of the PieSlice being handled. + /// Whether or not the activated PieSlice SliceType was able to be handled. + bool HandlePieCommand(PieSlice::SliceType pieSliceType) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEquippedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whatever is equipped in the turret, if anything. OWNERSHIP IS NOT TRANSFERRED! + // Arguments: None. + // Return value: The currently equipped item, if any. + + MovableObject* GetEquippedItem() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FirearmIsReady + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is ready for use, and has + // ammo etc. + // Arguments: None. + // Return value: Whether a currently HDFirearm (if any) is ready for use. + + bool FirearmIsReady() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FirearmIsEmpty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is out of ammo. + // Arguments: None. + // Return value: Whether a currently HDFirearm (if any) is out of ammo. + + bool FirearmIsEmpty() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FirearmNeedsReload + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is almost out of ammo. + // Arguments: None. + // Return value: Whether a currently HDFirearm (if any) has less than half of ammo left. + + bool FirearmNeedsReload() const; + + /// + /// Gets whether or not all of this ACrab's Turret's HDFirearms are full. + /// + /// Whether or not all of this ACrab's Turret's HDFirearms are full. Will return true if there is no Turret or no HDFirearms. + bool FirearmsAreFull() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FirearmIsSemiAuto + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is semi or full auto. + // Arguments: None. + // Return value: Whether a currently HDFirearm (if any) is a semi auto device. + + bool FirearmIsSemiAuto() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FirearmActivationDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the currently held device's delay between pulling the trigger + // and activating. + // Arguments: None. + // Return value: Delay in ms or zero if not a HDFirearm. + + int FirearmActivationDelay() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReloadFirearms + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reloads the currently held firearms, if any. + // Arguments: None. + // Return value: None. + + void ReloadFirearms(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsWithinRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a point on the scene is within close range of the currently + // used device and aiming status, if applicable. + // Arguments: A Vector witht he aboslute coordinates of a point to check. + // Return value: Whether the point is within close range of this. + + bool IsWithinRange(Vector& point) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Look + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an unseen-revealing ray in the direction of where this is facing. + // Arguments: The degree angle to deviate from the current view point in the ray + // casting. A random ray will be chosen out of this +-range. + // The range, in pixels, beyond the actors sharp aim that the ray will have. + // Return value: Whether any unseen pixels were revealed by this look. + + bool Look(float FOVSpread, float range) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LookForMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an MO detecting ray in the direction of where the head is looking + // at the time. Factors including head rotation, sharp aim mode, and + // other variables determine how this ray is cast. + // Arguments: The degree angle to deviate from the current view point in the ray + // casting. A random ray will be chosen out of this +-range. + // A specific material ID to ignore (see through) + // Whether to ignore all terrain or not (true means 'x-ray vision'). + // Return value: A pointer to the MO seen while looking. + + MovableObject* LookForMOs(float FOVSpread = 45, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false); + + /// + /// Gets the GUI representation of this ACrab, only defaulting to its Turret or body if no GraphicalIcon has been defined. + /// + /// The graphical representation of this ACrab as a BITMAP. + BITMAP* GetGraphicalIcon() const override; + + /// + /// Gets whether this ACrab has just taken a stride this frame. + /// + /// Whether this ACrab has taken a stride this frame or not. + bool StrideFrame() const { return m_StrideFrame; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreControllerUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void PreControllerUpdate() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this ACrab's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Actor's current graphical HUD overlay representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // Which player's screen this is being drawn to. May affect what HUD elements + // get drawn etc. + // Return value: None. + + void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; + + /// + /// Gets the LimbPath corresponding to the passed in Side, Layer and MovementState values. + /// + /// Whether to get the left or right side. + /// Whether to get foreground or background LimbPath. + /// Which movement state to get the LimbPath for. + /// The LimbPath corresponding to the passed in Layer and MovementState values. + LimbPath* GetLimbPath(Side side, Layer layer, MovementState movementState) { return &m_Paths[side][layer][movementState]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLimbPathSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get walking limb path speed for the specified preset. + // Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST + // Return value: Limb path speed for the specified preset in m/s. + + float GetLimbPathSpeed(int speedPreset) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLimbPathSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Set walking limb path speed for the specified preset. + // Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST. New speed value in m/s. + // Return value: None. + + void SetLimbPathSpeed(int speedPreset, float speed); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLimbPathPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the default force that a limb traveling walking LimbPath can push against + // stuff in the scene with. + // Arguments: None. + // Return value: The default set force maximum, in kg * m/s^2. + + float GetLimbPathPushForce() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLimbPathPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the default force that a limb traveling walking LimbPath can push against + // stuff in the scene with. + // Arguments: The default set force maximum, in kg * m/s^2. + // Return value: None + + void SetLimbPathPushForce(float force); + + /// + /// Gets this ACrab's stride sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this ACrab's stride sound. + SoundContainer* GetStrideSound() const { return m_StrideSound; } + + /// + /// Sets this ACrab's stride sound. Ownership IS transferred! + /// + /// The new SoundContainer for this ACrab's stride sound. + void SetStrideSound(SoundContainer* newSound) { m_StrideSound = newSound; } + + /// + /// Gets the upper limit of this ACrab's aim range. + /// + /// The upper limit of this ACrab's aim range. + float GetAimRangeUpperLimit() const { return m_AimRangeUpperLimit; } + + /// + /// Sets the upper limit of this ACrab's aim range. + /// + /// The new upper limit of this ACrab's aim range. + void SetAimRangeUpperLimit(float aimRangeUpperLimit) { m_AimRangeUpperLimit = aimRangeUpperLimit; } + + /// + /// Gets the lower limit of this ACrab's aim range. + /// + /// The lower limit of this ACrab's aim range. + float GetAimRangeLowerLimit() const { return m_AimRangeLowerLimit; } + + /// + /// Sets the lower limit of this ACrab's aim range. + /// + /// The new lower limit of this ACrab's aim range. + void SetAimRangeLowerLimit(float aimRangeLowerLimit) { m_AimRangeLowerLimit = aimRangeLowerLimit; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Function that is called when we get a new movepath. + /// This processes and cleans up the movepath. + /// + void OnNewMovePath() override; + + // Member variables + static Entity::ClassInfo m_sClass; + + // Turret which can be mounted with a weapon + Turret* m_pTurret; + // TODO when this class is cleaned up these legs and footgroups should probably be renamed. L and R should be expanded to Left and Right. I think FG and BG can stay as is cause they're everywhere. + // Left Foreground leg. + Leg* m_pLFGLeg; + // Left Background leg. + Leg* m_pLBGLeg; + // Right Foreground leg. + Leg* m_pRFGLeg; + // Right Background leg. + Leg* m_pRBGLeg; + // Limb AtomGroups. + AtomGroup* m_pLFGFootGroup; + AtomGroup* m_BackupLFGFootGroup; + AtomGroup* m_pLBGFootGroup; + AtomGroup* m_BackupLBGFootGroup; + AtomGroup* m_pRFGFootGroup; + AtomGroup* m_BackupRFGFootGroup; + AtomGroup* m_pRBGFootGroup; + AtomGroup* m_BackupRBGFootGroup; + // The sound of the actor taking a step (think robot servo) + SoundContainer* m_StrideSound; + // Jetpack booster. + AEJetpack* m_pJetpack; + // Blink timer + Timer m_IconBlinkTimer; + // Whether a stride was taken this frame or not. + bool m_StrideFrame = false; + // Limb paths for different movement states. + // First which side, then which background/foreground, then the movement state + LimbPath m_Paths[SIDECOUNT][LAYERCOUNT][MOVEMENTSTATECOUNT]; + // Whether was aiming during the last frame too. + bool m_Aiming; + // Controls the start of leg synch. + bool m_StrideStart[SIDECOUNT]; + // Times the strides to make sure they get restarted if they end up too long + Timer m_StrideTimer[SIDECOUNT]; + // The maximum angle MountedMO can be aimed up, positive values only, in radians + float m_AimRangeUpperLimit; + // The maximum angle MountedMO can be aimed down, positive values only, in radians + float m_AimRangeLowerLimit; + + //////////////// + // AI States + + enum DeviceHandlingState { + STILL = 0, + POINTING, + SCANNING, + AIMING, + FIRING, + THROWING, + DIGGING + }; + + enum SweepState { + NOSWEEP = 0, + SWEEPINGUP, + SWEEPUPPAUSE, + SWEEPINGDOWN, + SWEEPDOWNPAUSE + }; + + enum DigState { + NOTDIGGING = 0, + PREDIG, + STARTDIG, + TUNNELING, + FINISHINGDIG, + PAUSEDIGGER + }; + + enum JumpState { + NOTJUMPING = 0, + FORWARDJUMP, + PREUPJUMP, + UPJUMP, + APEXJUMP, + LANDJUMP + }; + + // What the AI is doing with its held devices + DeviceHandlingState m_DeviceState; + // What we are doing with a device sweeping + SweepState m_SweepState; + // The current digging state + DigState m_DigState; + // The current jumping state + JumpState m_JumpState; + // Jumping target, overshoot this and the jump is completed + Vector m_JumpTarget; + // Jumping left or right + bool m_JumpingRight; + // The position of the end of the current tunnel being dug. When it is reached, digging can stop. + Vector m_DigTunnelEndPos; + // The center angle (in rads) for the sweeping motion done duing scannign and digging + float m_SweepCenterAimAngle; + // The range to each direction of the center that the sweeping motion will be done in + float m_SweepRange; + // The absolute coordinates of the last detected gold deposits + Vector m_DigTarget; + // Timer for how long to be shooting at a seen enemy target + Timer m_FireTimer; + // Timer for how long to be shooting at a seen enemy target + Timer m_SweepTimer; + // Timer for how long to be patrolling in a direction + Timer m_PatrolTimer; + // Timer for how long to be firing the jetpack in a direction + Timer m_JumpTimer; + // Whether mouse input should be locked to the aim range, or whether we can "wander" out. For static emplacements like turrets, this is good, but for moving things it's a bit sticky. + bool m_LockMouseAimInput; #pragma region Event Handling - /// - /// Event listener to be run while this ACrab's PieMenu is opened. - /// - /// The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int WhilePieMenuOpenListener(const PieMenu *pieMenu) override; + /// + /// Event listener to be run while this ACrab's PieMenu is opened. + /// + /// The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int WhilePieMenuOpenListener(const PieMenu* pieMenu) override; #pragma endregion + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ACrab, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ACrab, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. - void Clear(); + void Clear(); - // Disallow the use of some implicit methods. - ACrab(const ACrab &reference) = delete; - ACrab & operator=(const ACrab &rhs) = delete; - -}; + // Disallow the use of some implicit methods. + ACrab(const ACrab& reference) = delete; + ACrab& operator=(const ACrab& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/ACraft.cpp b/Source/Entities/ACraft.cpp index 5518f59833..092b7b0c32 100644 --- a/Source/Entities/ACraft.cpp +++ b/Source/Entities/ACraft.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -30,1062 +29,993 @@ namespace RTE { -AbstractClassInfo(ACraft, Actor); -const std::string ACraft::Exit::c_ClassName = "Exit"; + AbstractClassInfo(ACraft, Actor); + const std::string ACraft::Exit::c_ClassName = "Exit"; -bool ACraft::s_CrabBombInEffect = false; + bool ACraft::s_CrabBombInEffect = false; #define EXITLINESPACING 7 #define EXITSUCKDELAYMS 1500 + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Exit, effectively + // resetting the members of this abstraction level only. + + void ACraft::Exit::Clear() { + m_Offset.Reset(); + m_Velocity.Reset(); + m_VelSpread = 0.2f; + m_Radius = 10; + m_Range = 35; + m_Clear = true; + m_pIncomingMO = 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Exit, effectively -// resetting the members of this abstraction level only. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Exit to be identical to another, by deep copy. -void ACraft::Exit::Clear() -{ - m_Offset.Reset(); - m_Velocity.Reset(); - m_VelSpread = 0.2f; - m_Radius = 10; - m_Range = 35; - m_Clear = true; - m_pIncomingMO = 0; -} + int ACraft::Exit::Create(const Exit& reference) { + m_Offset = reference.m_Offset; + m_Velocity = reference.m_Velocity; + m_VelSpread = reference.m_VelSpread; + m_Radius = reference.m_Radius; + m_Range = reference.m_Range; + m_Clear = reference.m_Clear; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Exit to be identical to another, by deep copy. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Exit object ready for use. -int ACraft::Exit::Create(const Exit &reference) -{ - m_Offset = reference.m_Offset; - m_Velocity = reference.m_Velocity; - m_VelSpread = reference.m_VelSpread; - m_Radius = reference.m_Radius; - m_Range = reference.m_Range; - m_Clear = reference.m_Clear; + int ACraft::Exit::Create() { + if (Serializable::Create() < 0) + return -1; - return 0; -} + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Exit object ready for use. + int ACraft::Exit::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Serializable::ReadProperty(propName, reader)); -int ACraft::Exit::Create() -{ - if (Serializable::Create() < 0) - return -1; + MatchProperty("Offset", { reader >> m_Offset; }); + MatchProperty("Velocity", { reader >> m_Velocity; }); + MatchProperty("VelocitySpread", { reader >> m_VelSpread; }); + MatchProperty("Radius", { reader >> m_Radius; }); + MatchProperty("Range", { reader >> m_Range; }); - return 0; -} + EndPropertyList; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this Exit with a Writer for + // later recreation with Create(Reader &reader); + + int ACraft::Exit::Save(Writer& writer) const { + Serializable::Save(writer); + + writer.NewProperty("Offset"); + writer << m_Offset; + writer.NewProperty("Velocity"); + writer << m_Velocity; + writer.NewProperty("VelocitySpread"); + writer << m_VelSpread; + writer.NewProperty("Radius"); + writer << m_Radius; + writer.NewProperty("Range"); + writer << m_Range; + + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CheckIfClear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates whether this exit is currently clear enough of terrain to + // safely put things through without them ending up in the terrain. + + bool ACraft::Exit::CheckIfClear(const Vector& pos, Matrix& rot, float size) { + Vector notUsed; + Vector ray = m_Velocity; + ray.SetMagnitude(size); + return m_Clear = !g_SceneMan.CastNotMaterialRay(pos + (m_Offset * rot), ray * rot, g_MaterialAir, notUsed); + } -int ACraft::Exit::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Serializable::ReadProperty(propName, reader)); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SuckInMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Uses cast MO rays to see if anyhting is able to be drawn into this + // exit. If so, it will alter the positiona nd velocity of the objet so + // it flies into the exit until it is sufficiently inside and then it'll + // return the MO here, OWNERHIP NOT TRANSFERRED! It is still in MovableMan! + + MOSRotating* ACraft::Exit::SuckInMOs(ACraft* pExitOwner) { + if (!pExitOwner || !m_Clear) + return 0; + + Vector exitPos = pExitOwner->GetPos() + pExitOwner->RotateOffset(m_Offset); + Vector exitRadius; + Vector exitCorner; + Vector rayVec = pExitOwner->RotateOffset(m_Velocity); + + // If we're sucking on an MO already + if (m_pIncomingMO) { + const float suckageRange = m_Range * 1.5F; + + // Check that it's still active and valid (not destroyed) + if (!(g_MovableMan.IsDevice(m_pIncomingMO) || g_MovableMan.IsActor(m_pIncomingMO))) { + m_pIncomingMO = 0; + } + // See if it's now out of range of suckage + else if ((exitPos - m_pIncomingMO->GetPos()).MagnitudeIsGreaterThan(suckageRange)) { + m_pIncomingMO = 0; + } + // See if the object has been sucked in far enough to be considered picked up by the exit + else if ((m_pIncomingMO->GetPos() - exitPos).Dot(rayVec) < 0) { + // Yes, the object has been sucked in beyond the offset position of the exit itself, so we're done picking it up + // Zero out the incoming member pointer to show that we're not sucking on anyhting anymore + MOSRotating* pReturnMO = m_pIncomingMO; + m_pIncomingMO = 0; + return pReturnMO; + } + // Ok it's not quite there yet, so keep sucking it in + else { + // Figure the distance left for the object to go to reach the exit + Vector toGo = exitPos - m_pIncomingMO->GetPos(); + // If the object is still a bit away from the exit goal, override velocity of the object to head straight into the exit + const float threshold = 1.0F; + if (toGo.MagnitudeIsGreaterThan(threshold)) { + m_pIncomingMO->SetVel(toGo.SetMagnitude(m_Velocity.GetMagnitude())); + } - MatchProperty("Offset", { reader >> m_Offset; }); - MatchProperty("Velocity", { reader >> m_Velocity; }); - MatchProperty("VelocitySpread", { reader >> m_VelSpread; }); - MatchProperty("Radius", { reader >> m_Radius; }); - MatchProperty("Range", { reader >> m_Range; }); + // Turn off collisions between the object and the craft sucking it in + m_pIncomingMO->SetWhichMOToNotHit(pExitOwner, 3); + pExitOwner->SetWhichMOToNotHit(m_pIncomingMO, 3); + } + } - EndPropertyList; -} + // Not sucking in anything, so see if there's anyhting to start doing it to + if (!m_pIncomingMO) { + exitRadius = pExitOwner->RotateOffset(m_Velocity.GetPerpendicular().SetMagnitude(m_Radius)); + exitCorner = exitPos + exitRadius; + rayVec = pExitOwner->RotateOffset(m_Velocity); + rayVec.SetMagnitude(m_Range); + + MOID itemMOID = g_SceneMan.CastMORay(exitCorner, rayVec, pExitOwner->GetRootID(), Activity::NoTeam, g_MaterialGrass, true, 4); + // Try the other side if we didn't find anything + if (itemMOID == g_NoMOID) { + exitCorner -= exitRadius * 2; + itemMOID = g_SceneMan.CastMORay(exitCorner, rayVec, pExitOwner->GetRootID(), Activity::NoTeam, g_MaterialGrass, true, 4); + } + // Try center beam if we STILL didn't find anything + if (itemMOID == g_NoMOID) { + exitCorner += exitRadius; + itemMOID = g_SceneMan.CastMORay(exitCorner, rayVec, pExitOwner->GetRootID(), Activity::NoTeam, g_MaterialGrass, true, 4); + } + // See if we caught anything + MovableObject* pItem = g_MovableMan.GetMOFromID(itemMOID); + if (pItem) { + // We did! Now start sucking it in (next frame) + m_pIncomingMO = dynamic_cast(pItem->GetRootParent()); + // Don't suck in other ACraft! + if (dynamic_cast(m_pIncomingMO)) + m_pIncomingMO = 0; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Exit with a Writer for -// later recreation with Create(Reader &reader); + // Nothing was sucked in far enough to be returned as done + return 0; + } -int ACraft::Exit::Save(Writer &writer) const -{ - Serializable::Save(writer); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ACraft, effectively + // resetting the members of this abstraction level only. + + void ACraft::Clear() { + m_AIMode = AIMODE_DELIVER; + + m_MoveState = 0; + m_HatchState = CLOSED; + m_HatchTimer.Reset(); + m_HatchDelay = 0; + m_HatchOpenSound = nullptr; + m_HatchCloseSound = nullptr; + m_CollectedInventory.clear(); + m_Exits.clear(); + m_CurrentExit = m_Exits.begin(); + m_ExitInterval = 1000; + m_ExitTimer.Reset(); + m_ExitLinePhase = 0; + m_HasDelivered = false; + m_LandingCraft = true; + m_FlippedTimer.Reset(); + m_CrashTimer.Reset(); + m_CrashSound = nullptr; - writer.NewProperty("Offset"); - writer << m_Offset; - writer.NewProperty("Velocity"); - writer << m_Velocity; - writer.NewProperty("VelocitySpread"); - writer << m_VelSpread; - writer.NewProperty("Radius"); - writer << m_Radius; - writer.NewProperty("Range"); - writer << m_Range; + m_DeliveryState = FALL; + m_AltitudeMoveState = HOVER; + m_AltitudeControl = 0; + m_MaxPassengers = -1; - return 0; -} + m_DeliveryDelayMultiplier = 1.0; + m_ScuttleIfFlippedTime = 4000; + m_ScuttleOnDeath = true; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ACraft object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CheckIfClear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates whether this exit is currently clear enough of terrain to -// safely put things through without them ending up in the terrain. + int ACraft::Create() { + // Read all the properties + if (Actor::Create() < 0) + return -1; -bool ACraft::Exit::CheckIfClear(const Vector &pos, Matrix &rot, float size) -{ - Vector notUsed; - Vector ray = m_Velocity; - ray.SetMagnitude(size); - return m_Clear = !g_SceneMan.CastNotMaterialRay(pos + (m_Offset * rot), ray * rot, g_MaterialAir, notUsed); -} + if (m_AIMode == Actor::AIMODE_NONE) { + m_AIMode = Actor::AIMODE_DELIVER; + } + m_CurrentExit = m_Exits.begin(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SuckInMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Uses cast MO rays to see if anyhting is able to be drawn into this -// exit. If so, it will alter the positiona nd velocity of the objet so -// it flies into the exit until it is sufficiently inside and then it'll -// return the MO here, OWNERHIP NOT TRANSFERRED! It is still in MovableMan! - -MOSRotating * ACraft::Exit::SuckInMOs(ACraft *pExitOwner) -{ - if (!pExitOwner || !m_Clear) - return 0; - - Vector exitPos = pExitOwner->GetPos() + pExitOwner->RotateOffset(m_Offset); - Vector exitRadius; - Vector exitCorner; - Vector rayVec = pExitOwner->RotateOffset(m_Velocity); - - // If we're sucking on an MO already - if (m_pIncomingMO) - { - const float suckageRange = m_Range * 1.5F; - - // Check that it's still active and valid (not destroyed) - if (!(g_MovableMan.IsDevice(m_pIncomingMO) || g_MovableMan.IsActor(m_pIncomingMO))) - { - m_pIncomingMO = 0; - } - // See if it's now out of range of suckage - else if ((exitPos - m_pIncomingMO->GetPos()).MagnitudeIsGreaterThan(suckageRange)) - { - m_pIncomingMO = 0; - } - // See if the object has been sucked in far enough to be considered picked up by the exit - else if ((m_pIncomingMO->GetPos() - exitPos).Dot(rayVec) < 0) - { - // Yes, the object has been sucked in beyond the offset position of the exit itself, so we're done picking it up - // Zero out the incoming member pointer to show that we're not sucking on anyhting anymore - MOSRotating *pReturnMO = m_pIncomingMO; - m_pIncomingMO = 0; - return pReturnMO; - } - // Ok it's not quite there yet, so keep sucking it in - else - { - // Figure the distance left for the object to go to reach the exit - Vector toGo = exitPos - m_pIncomingMO->GetPos(); - // If the object is still a bit away from the exit goal, override velocity of the object to head straight into the exit - const float threshold = 1.0F; - if (toGo.MagnitudeIsGreaterThan(threshold)) - { - m_pIncomingMO->SetVel(toGo.SetMagnitude(m_Velocity.GetMagnitude())); - } - - // Turn off collisions between the object and the craft sucking it in - m_pIncomingMO->SetWhichMOToNotHit(pExitOwner, 3); - pExitOwner->SetWhichMOToNotHit(m_pIncomingMO, 3); - } - } - - // Not sucking in anything, so see if there's anyhting to start doing it to - if (!m_pIncomingMO) - { - exitRadius = pExitOwner->RotateOffset(m_Velocity.GetPerpendicular().SetMagnitude(m_Radius)); - exitCorner = exitPos + exitRadius; - rayVec = pExitOwner->RotateOffset(m_Velocity); - rayVec.SetMagnitude(m_Range); - - MOID itemMOID = g_SceneMan.CastMORay(exitCorner, rayVec, pExitOwner->GetRootID(), Activity::NoTeam, g_MaterialGrass, true, 4); - // Try the other side if we didn't find anything - if (itemMOID == g_NoMOID) - { - exitCorner -= exitRadius * 2; - itemMOID = g_SceneMan.CastMORay(exitCorner, rayVec, pExitOwner->GetRootID(), Activity::NoTeam, g_MaterialGrass, true, 4); - } - // Try center beam if we STILL didn't find anything - if (itemMOID == g_NoMOID) - { - exitCorner += exitRadius; - itemMOID = g_SceneMan.CastMORay(exitCorner, rayVec, pExitOwner->GetRootID(), Activity::NoTeam, g_MaterialGrass, true, 4); - } - - // See if we caught anything - MovableObject *pItem = g_MovableMan.GetMOFromID(itemMOID); - if (pItem) - { - // We did! Now start sucking it in (next frame) - m_pIncomingMO = dynamic_cast(pItem->GetRootParent()); - // Don't suck in other ACraft! - if (dynamic_cast(m_pIncomingMO)) - m_pIncomingMO = 0; - } - } - - // Nothing was sucked in far enough to be returned as done - return 0; -} + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ACraft to be identical to another, by deep copy. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ACraft, effectively -// resetting the members of this abstraction level only. - -void ACraft::Clear() -{ - m_AIMode = AIMODE_DELIVER; - - m_MoveState = 0; - m_HatchState = CLOSED; - m_HatchTimer.Reset(); - m_HatchDelay = 0; - m_HatchOpenSound = nullptr; - m_HatchCloseSound = nullptr; - m_CollectedInventory.clear(); - m_Exits.clear(); - m_CurrentExit = m_Exits.begin(); - m_ExitInterval = 1000; - m_ExitTimer.Reset(); - m_ExitLinePhase = 0; - m_HasDelivered = false; - m_LandingCraft = true; - m_FlippedTimer.Reset(); - m_CrashTimer.Reset(); - m_CrashSound = nullptr; - - m_DeliveryState = FALL; - m_AltitudeMoveState = HOVER; - m_AltitudeControl = 0; - m_MaxPassengers = -1; - - m_DeliveryDelayMultiplier = 1.0; - m_ScuttleIfFlippedTime = 4000; - m_ScuttleOnDeath = true; -} + int ACraft::Create(const ACraft& reference) { + Actor::Create(reference); + m_MoveState = reference.m_MoveState; + m_HatchState = reference.m_HatchState; + m_HatchDelay = reference.m_HatchDelay; + if (reference.m_HatchOpenSound) { + m_HatchOpenSound = dynamic_cast(reference.m_HatchOpenSound->Clone()); + } + if (reference.m_HatchCloseSound) { + m_HatchCloseSound = dynamic_cast(reference.m_HatchCloseSound->Clone()); + } else if (reference.m_HatchOpenSound) { + m_HatchCloseSound = dynamic_cast(reference.m_HatchOpenSound->Clone()); + } + for (std::deque::const_iterator niItr = reference.m_CollectedInventory.begin(); niItr != reference.m_CollectedInventory.end(); ++niItr) + m_CollectedInventory.push_back(dynamic_cast((*niItr)->Clone())); + for (std::list::const_iterator eItr = reference.m_Exits.begin(); eItr != reference.m_Exits.end(); ++eItr) + m_Exits.push_back(*eItr); + m_CurrentExit = m_Exits.begin(); + m_ExitInterval = reference.m_ExitInterval; + m_HasDelivered = reference.m_HasDelivered; + m_LandingCraft = reference.m_LandingCraft; + if (reference.m_CrashSound) { + m_CrashSound = dynamic_cast(reference.m_CrashSound->Clone()); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ACraft object ready for use. + m_DeliveryState = reference.m_DeliveryState; + m_AltitudeMoveState = reference.m_AltitudeMoveState; + m_AltitudeControl = reference.m_AltitudeControl; + m_MaxPassengers = reference.m_MaxPassengers; -int ACraft::Create() -{ - // Read all the properties - if (Actor::Create() < 0) - return -1; + m_DeliveryDelayMultiplier = reference.m_DeliveryDelayMultiplier; + m_ScuttleIfFlippedTime = reference.m_ScuttleIfFlippedTime; + m_ScuttleOnDeath = reference.m_ScuttleOnDeath; - if (m_AIMode == Actor::AIMODE_NONE) { - m_AIMode = Actor::AIMODE_DELIVER; + return 0; } - m_CurrentExit = m_Exits.begin(); - - return 0; -} - + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int ACraft::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Actor::ReadProperty(propName, reader)); + + MatchProperty("HatchDelay", { reader >> m_HatchDelay; }); + MatchProperty("HatchOpenSound", { + m_HatchOpenSound = new SoundContainer; + reader >> m_HatchOpenSound; + }); + MatchProperty("HatchCloseSound", { + m_HatchCloseSound = new SoundContainer; + reader >> m_HatchCloseSound; + }); + MatchProperty("CrashSound", { + m_CrashSound = new SoundContainer; + reader >> m_CrashSound; + }); + MatchProperty("AddExit", + { + Exit exit; + reader >> exit; + m_Exits.push_back(exit); + }); + MatchProperty("DeliveryDelayMultiplier", { reader >> m_DeliveryDelayMultiplier; }); + MatchProperty("ExitInterval", { reader >> m_ExitInterval; }); + MatchProperty("CanLand", { reader >> m_LandingCraft; }); + MatchProperty("MaxPassengers", { reader >> m_MaxPassengers; }); + MatchProperty("ScuttleIfFlippedTime", { reader >> m_ScuttleIfFlippedTime; }); + MatchProperty("ScuttleOnDeath", { reader >> m_ScuttleOnDeath; }); + + EndPropertyList; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a ACraft to be identical to another, by deep copy. - -int ACraft::Create(const ACraft &reference) -{ - Actor::Create(reference); - - m_MoveState = reference.m_MoveState; - m_HatchState = reference.m_HatchState; - m_HatchDelay = reference.m_HatchDelay; - if (reference.m_HatchOpenSound) { m_HatchOpenSound = dynamic_cast(reference.m_HatchOpenSound->Clone()); } - if (reference.m_HatchCloseSound) { - m_HatchCloseSound = dynamic_cast(reference.m_HatchCloseSound->Clone()); - } else if (reference.m_HatchOpenSound) { - m_HatchCloseSound = dynamic_cast(reference.m_HatchOpenSound->Clone()); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this ACraft with a Writer for + // later recreation with Create(Reader &reader); + + int ACraft::Save(Writer& writer) const { + Actor::Save(writer); + + writer.NewProperty("HatchDelay"); + writer << m_HatchDelay; + writer.NewProperty("HatchOpenSound"); + writer << m_HatchOpenSound; + writer.NewProperty("HatchCloseSound"); + writer << m_HatchCloseSound; + for (std::list::const_iterator itr = m_Exits.begin(); itr != m_Exits.end(); ++itr) { + writer.NewProperty("AddExit"); + writer << (*itr); + } + writer.NewProperty("DeliveryDelayMultiplier"); + writer << m_DeliveryDelayMultiplier; + writer.NewProperty("ExitInterval"); + writer << m_ExitInterval; + writer.NewProperty("CanLand"); + writer << m_LandingCraft; + + writer.NewProperty("CrashSound"); + writer << m_CrashSound; + + writer.NewProperty("MaxPassengers"); + writer << m_MaxPassengers; + writer.NewProperty("ScuttleIfFlippedTime"); + writer << m_ScuttleIfFlippedTime; + writer.NewProperty("ScuttleOnDeath"); + writer << m_ScuttleOnDeath; + + return 0; } - for (std::deque::const_iterator niItr = reference.m_CollectedInventory.begin(); niItr != reference.m_CollectedInventory.end(); ++niItr) - m_CollectedInventory.push_back(dynamic_cast((*niItr)->Clone())); - for (std::list::const_iterator eItr = reference.m_Exits.begin(); eItr != reference.m_Exits.end(); ++eItr) - m_Exits.push_back(*eItr); - m_CurrentExit = m_Exits.begin(); - m_ExitInterval = reference.m_ExitInterval; - m_HasDelivered = reference.m_HasDelivered; - m_LandingCraft = reference.m_LandingCraft; - if (reference.m_CrashSound) { m_CrashSound = dynamic_cast(reference.m_CrashSound->Clone()); } - m_DeliveryState = reference.m_DeliveryState; - m_AltitudeMoveState = reference.m_AltitudeMoveState; - m_AltitudeControl = reference.m_AltitudeControl; - m_MaxPassengers = reference.m_MaxPassengers; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the ACraft object. - m_DeliveryDelayMultiplier = reference.m_DeliveryDelayMultiplier; - m_ScuttleIfFlippedTime = reference.m_ScuttleIfFlippedTime; - m_ScuttleOnDeath = reference.m_ScuttleOnDeath; + void ACraft::Destroy(bool notInherited) { + delete m_HatchOpenSound; + delete m_HatchCloseSound; + delete m_CrashSound; - return 0; -} + if (!notInherited) + Actor::Destroy(); + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of this Actor and all its carried + // gold and inventory. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int ACraft::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Actor::ReadProperty(propName, reader)); - - MatchProperty("HatchDelay", { reader >> m_HatchDelay; }); - MatchProperty("HatchOpenSound", { - m_HatchOpenSound = new SoundContainer; - reader >> m_HatchOpenSound; - }); - MatchProperty("HatchCloseSound", { - m_HatchCloseSound = new SoundContainer; - reader >> m_HatchCloseSound; - }); - MatchProperty("CrashSound", { - m_CrashSound = new SoundContainer; - reader >> m_CrashSound; - }); - MatchProperty("AddExit", - { - Exit exit; - reader >> exit; - m_Exits.push_back(exit); - }); - MatchProperty("DeliveryDelayMultiplier", { reader >> m_DeliveryDelayMultiplier; }); - MatchProperty("ExitInterval", { reader >> m_ExitInterval; }); - MatchProperty("CanLand", { reader >> m_LandingCraft; }); - MatchProperty("MaxPassengers", { reader >> m_MaxPassengers; }); - MatchProperty("ScuttleIfFlippedTime", { reader >> m_ScuttleIfFlippedTime; }); - MatchProperty("ScuttleOnDeath", { reader >> m_ScuttleOnDeath; }); - - EndPropertyList; -} + float ACraft::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const { + float totalValue = Actor::GetTotalValue(nativeModule, foreignMult, nativeMult); + MOSprite* pItem = 0; + for (std::deque::const_iterator itr = m_CollectedInventory.begin(); itr != m_CollectedInventory.end(); ++itr) { + pItem = dynamic_cast(*itr); + if (pItem) + totalValue += pItem->GetTotalValue(nativeModule, foreignMult, nativeMult); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this ACraft with a Writer for -// later recreation with Create(Reader &reader); - -int ACraft::Save(Writer &writer) const -{ - Actor::Save(writer); - - writer.NewProperty("HatchDelay"); - writer << m_HatchDelay; - writer.NewProperty("HatchOpenSound"); - writer << m_HatchOpenSound; - writer.NewProperty("HatchCloseSound"); - writer << m_HatchCloseSound; - for (std::list::const_iterator itr = m_Exits.begin(); itr != m_Exits.end(); ++itr) - { - writer.NewProperty("AddExit"); - writer << (*itr); - } - writer.NewProperty("DeliveryDelayMultiplier"); - writer << m_DeliveryDelayMultiplier; - writer.NewProperty("ExitInterval"); - writer << m_ExitInterval; - writer.NewProperty("CanLand"); - writer << m_LandingCraft; - - writer.NewProperty("CrashSound"); - writer << m_CrashSound; - - writer.NewProperty("MaxPassengers"); - writer << m_MaxPassengers; - writer.NewProperty("ScuttleIfFlippedTime"); - writer << m_ScuttleIfFlippedTime; - writer.NewProperty("ScuttleOnDeath"); - writer << m_ScuttleOnDeath; - - return 0; -} + return totalValue; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this carries a specifically named object in its inventory. + // Also looks through the inventories of potential passengers, as applicable. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the ACraft object. + bool ACraft::HasObject(std::string objectName) const { + if (Actor::HasObject(objectName)) + return true; -void ACraft::Destroy(bool notInherited) -{ - delete m_HatchOpenSound; - delete m_HatchCloseSound; - delete m_CrashSound; + for (std::deque::const_iterator itr = m_CollectedInventory.begin(); itr != m_CollectedInventory.end(); ++itr) { + if ((*itr) && (*itr)->HasObject(objectName)) + return true; + } - if (!notInherited) - Actor::Destroy(); - Clear(); -} + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObjectInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically grouped object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of this Actor and all its carried -// gold and inventory. + bool ACraft::HasObjectInGroup(std::string groupName) const { + if (Actor::HasObjectInGroup(groupName)) + return true; -float ACraft::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const -{ - float totalValue = Actor::GetTotalValue(nativeModule, foreignMult, nativeMult); + for (std::deque::const_iterator itr = m_CollectedInventory.begin(); itr != m_CollectedInventory.end(); ++itr) { + if ((*itr) && (*itr)->HasObjectInGroup(groupName)) + return true; + } - MOSprite *pItem = 0; - for (std::deque::const_iterator itr = m_CollectedInventory.begin(); itr != m_CollectedInventory.end(); ++itr) - { - pItem = dynamic_cast(*itr); - if (pItem) - totalValue += pItem->GetTotalValue(nativeModule, foreignMult, nativeMult); - } + return false; + } - return totalValue; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this belongs to, and all its inventory too. + void ACraft::SetTeam(int team) { + Actor::SetTeam(team); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this carries a specifically named object in its inventory. -// Also looks through the inventories of potential passengers, as applicable. + // Also set all actors in the new inventory + Actor* pActor = 0; + for (std::deque::iterator itr = m_CollectedInventory.begin(); itr != m_CollectedInventory.end(); ++itr) { + pActor = dynamic_cast(*itr); + if (pActor) + pActor->SetTeam(team); + } + } -bool ACraft::HasObject(std::string objectName) const -{ - if (Actor::HasObject(objectName)) - return true; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool ACraft::HandlePieCommand(PieSlice::SliceType pieSliceIndex) { + if (pieSliceIndex != PieSlice::SliceType::NoType) { + if (pieSliceIndex == PieSlice::SliceType::Deliver) { + m_AIMode = AIMODE_DELIVER; + m_DeliveryState = FALL; + m_HasDelivered = false; + } else if (pieSliceIndex == PieSlice::SliceType::Return) { + m_AIMode = AIMODE_RETURN; + m_DeliveryState = LAUNCH; + } else if (pieSliceIndex == PieSlice::SliceType::Stay) { + m_AIMode = AIMODE_STAY; + m_DeliveryState = FALL; + } else if (pieSliceIndex == PieSlice::SliceType::Sentry) { + m_AIMode = AIMODE_SENTRY; + m_DeliveryState = FALL; + } else if (pieSliceIndex == PieSlice::SliceType::Return) { + m_AIMode = AIMODE_RETURN; + m_DeliveryState = LAUNCH; + } else if (pieSliceIndex == PieSlice::SliceType::GoTo) { + m_AIMode = AIMODE_GOTO; + m_DeliveryState = FALL; + ClearAIWaypoints(); + m_UpdateMovePath = true; + } else if (pieSliceIndex == PieSlice::SliceType::Scuttle) { + m_AIMode = AIMODE_SCUTTLE; + } else { + return Actor::HandlePieCommand(pieSliceIndex); + } + } + return false; + } - for (std::deque::const_iterator itr = m_CollectedInventory.begin(); itr != m_CollectedInventory.end(); ++itr) - { - if ((*itr) && (*itr)->HasObject(objectName)) - return true; - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OpenHatch + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Opens the hatch doors, if they're closed. + // Arguments: None. + // Return value: None. + void ACraft::OpenHatch() { + if (m_HatchState == CLOSED || m_HatchState == CLOSING) { + m_HatchState = OPENING; + m_HatchTimer.Reset(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObjectInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically grouped object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. + // PSCHHT + if (m_HatchOpenSound) { + m_HatchOpenSound->Play(m_Pos); + } + } + } -bool ACraft::HasObjectInGroup(std::string groupName) const -{ - if (Actor::HasObjectInGroup(groupName)) - return true; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CloseHatch + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Closes the hatch doors, if they're open. + // Arguments: None. + // Return value: None. - for (std::deque::const_iterator itr = m_CollectedInventory.begin(); itr != m_CollectedInventory.end(); ++itr) - { - if ((*itr) && (*itr)->HasObjectInGroup(groupName)) - return true; - } + void ACraft::CloseHatch() { + if (m_HatchState == OPEN || m_HatchState == OPENING) { + m_HatchState = CLOSING; + m_HatchTimer.Reset(); - return false; -} + // When closing, move all newly added inventory to the regular inventory list so it'll be ejected next time doors open + for (std::deque::const_iterator niItr = m_CollectedInventory.begin(); niItr != m_CollectedInventory.end(); ++niItr) { + AddToInventoryBack(*niItr); + } + // Clear the new inventory hold, it's all been moved to the regular inventory + m_CollectedInventory.clear(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this belongs to, and all its inventory too. - -void ACraft::SetTeam(int team) -{ - Actor::SetTeam(team); - - // Also set all actors in the new inventory - Actor *pActor = 0; - for (std::deque::iterator itr = m_CollectedInventory.begin(); itr != m_CollectedInventory.end(); ++itr) - { - pActor = dynamic_cast(*itr); - if (pActor) - pActor->SetTeam(team); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool ACraft::HandlePieCommand(PieSlice::SliceType pieSliceIndex) { - if (pieSliceIndex != PieSlice::SliceType::NoType) { - if (pieSliceIndex == PieSlice::SliceType::Deliver) { - m_AIMode = AIMODE_DELIVER; - m_DeliveryState = FALL; - m_HasDelivered = false; - } else if (pieSliceIndex == PieSlice::SliceType::Return) { - m_AIMode = AIMODE_RETURN; - m_DeliveryState = LAUNCH; - } else if (pieSliceIndex == PieSlice::SliceType::Stay) { - m_AIMode = AIMODE_STAY; - m_DeliveryState = FALL; - } else if (pieSliceIndex == PieSlice::SliceType::Sentry) { - m_AIMode = AIMODE_SENTRY; - m_DeliveryState = FALL; - } else if (pieSliceIndex == PieSlice::SliceType::Return) { - m_AIMode = AIMODE_RETURN; - m_DeliveryState = LAUNCH; - } else if (pieSliceIndex == PieSlice::SliceType::GoTo) { - m_AIMode = AIMODE_GOTO; - m_DeliveryState = FALL; - ClearAIWaypoints(); - m_UpdateMovePath = true; - } else if (pieSliceIndex == PieSlice::SliceType::Scuttle) { - m_AIMode = AIMODE_SCUTTLE; - } else { - return Actor::HandlePieCommand(pieSliceIndex); - } - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // PSCHHT + if (m_HatchCloseSound) { + m_HatchCloseSound->Play(m_Pos); + } + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: AddInventoryItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an inventory item to this Actor. + // Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! + // Return value: None.. + + void ACraft::AddInventoryItem(MovableObject* pItemToAdd) { + if (pItemToAdd) { + // If the hatch is open, then only add the new item to the intermediate new inventory list + // so that it doesn't get chucked out right away again + if (m_HatchState == OPEN || m_HatchState == OPENING) { + m_CollectedInventory.push_back(pItemToAdd); + } else { + // If doors are already closed, it's safe to put the item directly the regular inventory + AddToInventoryBack(pItemToAdd); + } + if (Actor* itemAsActor = dynamic_cast(pItemToAdd); itemAsActor && itemAsActor->GetGoldCarried() > 0) { + m_GoldCarried += itemAsActor->GetGoldCarried(); + itemAsActor->SetGoldCarried(0); + m_GoldPicked = true; + if (g_ActivityMan.GetActivity()->IsHumanTeam(m_Team)) { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { + if (g_ActivityMan.GetActivity()->GetTeamOfPlayer(player) == m_Team) { + g_GUISound.FundsChangedSound()->Play(player); + } + } + } + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OpenHatch -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Opens the hatch doors, if they're closed. -// Arguments: None. -// Return value: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DropAllInventory + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Opens the hatches and makes everything in the Rocket fly out, including + // the passenger Actors, one after another. It may not happen + // instantaneously, so check for ejection being complete with + // IsInventoryEmpty(). + + void ACraft::DropAllInventory() { + if (m_HatchState == OPEN && !m_Exits.empty()) { + // Cancel if we're not due to release + if (!m_ExitTimer.IsPastSimMS(m_ExitInterval)) + return; + + bool exitExists = false; + std::list::iterator exit = m_Exits.begin(); + // Check which exits are clear of the terrain, if any + for (; exit != m_Exits.end(); ++exit) { + if (exit->CheckIfClear(m_Pos, m_Rotation, 18)) + exitExists = true; + } -void ACraft::OpenHatch() -{ - if (m_HatchState == CLOSED || m_HatchState == CLOSING) - { - m_HatchState = OPENING; - m_HatchTimer.Reset(); + // Cancel if no exits are clear + if (!exitExists) { + // TODO: give some kind of audio feedback to user + return; + } - // PSCHHT - if (m_HatchOpenSound) { m_HatchOpenSound->Play(m_Pos); } - } -} + // Eject inventory and passengers, through alternating clear exits + Vector exitVel, exitVelNorm, gravityNorm; + float antiGravBoost = 0; + Actor* pPassenger = 0; + bool droppedSomething = false; + for (std::deque::iterator exitee = m_Inventory.begin(); exitee != m_Inventory.end(); ++exitee) { + // Select next clear exit + do { + if (++m_CurrentExit == m_Exits.end()) + m_CurrentExit = m_Exits.begin(); + } while (!m_CurrentExit->IsClear()); + + //(*exitee)->SetPos(m_Pos + m_CurrentExit->GetOffset() * m_Rotation); + (*exitee)->SetPos(m_Pos + RotateOffset(m_CurrentExit->GetOffset())); + // Reset all the timers of the object being shot out so it doesn't emit a bunch of particles that have been backed up while dormant in inventory + (*exitee)->ResetAllTimers(); + // exitVel = m_CurrentExit->GetVelocity() * m_Rotation; + exitVel = RotateOffset(m_CurrentExit->GetVelocity()); + + // Boost against gravity + // Normalize the gravity and exit velocity vectors + exitVelNorm = exitVel; + exitVelNorm.SetMagnitude(1.0); + gravityNorm = g_SceneMan.GetGlobalAcc(); + gravityNorm.SetMagnitude(1.0); + // Make the gravity boost be proportional to counteract gravity + antiGravBoost = 1.0 + 1.0 * exitVelNorm.Dot(-gravityNorm); + if (antiGravBoost < 1.0) + antiGravBoost = 1.0; + + // Detect whether we're dealing with a passenger and add it as Actor instead + pPassenger = dynamic_cast(*exitee); + if (pPassenger && m_ExitTimer.IsPastSimMS(m_ExitInterval)) { + pPassenger->SetVel(m_Vel + exitVel * antiGravBoost); + // pPassenger->SetRotAngle(m_Rotation + exitVel.GetAbsRadAngle() (ejectDir > 0 ? -c_HalfPI : c_HalfPI)); + // pPassenger->SetHFlipped(ejectDir <= 0); + // Avoid having immediate collisions with this + pPassenger->SetWhichMOToNotHit(this, 0.5f); + // Avoid this immediate collisions with it + SetWhichMOToNotHit(pPassenger, 0.5f); + // Add to scene + g_MovableMan.AddActor(pPassenger); + + // If this craft is being directly controlled by a player, and has landed, switch control to the first guy out + if (pPassenger->GetTeam() == m_Team && m_Controller.IsPlayerControlled() && g_ActivityMan.GetActivity()->GetControlledActor(m_Controller.GetPlayer()) == this && m_LandingCraft) { + g_ActivityMan.GetActivity()->SwitchToActor(pPassenger, m_Controller.GetPlayer(), m_Team); + // To avoid jump in the view, Update the passenger so its viewpoint is next to it and not at 0,0 + pPassenger->Update(); + } + + // Remove from inventory + m_Inventory.erase(exitee); + // Reset timer interval and quit until next one is due + m_ExitTimer.Reset(); + break; + } else { + (*exitee)->SetVel(m_Vel + exitVel * antiGravBoost); + (*exitee)->SetAngularVel(5.0F * RandomNormalNum()); + // Avoid it having immediate collisions with this + (*exitee)->SetWhichMOToNotHit(this, 0.5f); + // Avoid this immediate collisions with it + SetWhichMOToNotHit(*exitee, 0.5f); + // Add to scene + g_MovableMan.AddMO(*exitee); + // Remove passenger from inventory + m_Inventory.erase(exitee); + // Reset timer interval and quit until next one is due + m_ExitTimer.Reset(); + break; + } + droppedSomething = true; + } + if (m_Inventory.empty()) { + m_HasDelivered = true; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CloseHatch -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Closes the hatch doors, if they're open. -// Arguments: None. -// Return value: None. - -void ACraft::CloseHatch() -{ - if (m_HatchState == OPEN || m_HatchState == OPENING) - { - m_HatchState = CLOSING; - m_HatchTimer.Reset(); - - // When closing, move all newly added inventory to the regular inventory list so it'll be ejected next time doors open - for (std::deque::const_iterator niItr = m_CollectedInventory.begin(); niItr != m_CollectedInventory.end(); ++niItr) { - AddToInventoryBack(*niItr); - } - - // Clear the new inventory hold, it's all been moved to the regular inventory - m_CollectedInventory.clear(); - - // PSCHHT - if (m_HatchCloseSound) { m_HatchCloseSound->Play(m_Pos); } + // Kill craft if it is lying down. + if (std::fabs(m_Rotation.GetRadAngle()) > c_HalfPI && m_Status != DYING) { + m_Status = DYING; + m_DeathTmr.Reset(); + } + // Reset exit timer so we can time until when we should start sucking things in again + // Don't reset it if inventory is empty but we didn't drop anyhting, it means that doors just opened and nothing was inside, so immediately start sucking + if (droppedSomething) + m_ExitTimer.Reset(); + } + } else { + if (m_HatchState != OPENING) { + if (m_HatchOpenSound) { + m_HatchOpenSound->Play(m_Pos); + } + g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, 0.4)); + m_HatchTimer.Reset(); + } + m_HatchState = OPENING; + } } -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: AddInventoryItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an inventory item to this Actor. -// Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! -// Return value: None.. - -void ACraft::AddInventoryItem(MovableObject *pItemToAdd) -{ - if (pItemToAdd) - { - // If the hatch is open, then only add the new item to the intermediate new inventory list - // so that it doesn't get chucked out right away again - if (m_HatchState == OPEN || m_HatchState == OPENING) { - m_CollectedInventory.push_back(pItemToAdd); - } else { - // If doors are already closed, it's safe to put the item directly the regular inventory - AddToInventoryBack(pItemToAdd); + float ACraft::GetCollectedInventoryMass() const { + float inventoryMass = 0.0F; + for (const MovableObject* inventoryItem: m_CollectedInventory) { + inventoryMass += inventoryItem->GetMass(); } - if (Actor *itemAsActor = dynamic_cast(pItemToAdd); itemAsActor && itemAsActor->GetGoldCarried() > 0) { - m_GoldCarried += itemAsActor->GetGoldCarried(); - itemAsActor->SetGoldCarried(0); - m_GoldPicked = true; - if (g_ActivityMan.GetActivity()->IsHumanTeam(m_Team)) { - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { - if (g_ActivityMan.GetActivity()->GetTeamOfPlayer(player) == m_Team) { g_GUISound.FundsChangedSound()->Play(player); } + return inventoryMass; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void ACraft::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { + if (g_SettingsMan.CrabBombsEnabled() && !s_CrabBombInEffect) { + s_CrabBombInEffect = true; + int crabCount = 0; + for (const MovableObject* inventoryEntry: m_Inventory) { + if (inventoryEntry->GetPresetName() == "Crab") { + crabCount++; } } + if (crabCount >= g_SettingsMan.GetCrabBombThreshold()) { + for (int moid = 1; moid < g_MovableMan.GetMOIDCount() - 1; moid++) { + Actor* actor = dynamic_cast(g_MovableMan.GetMOFromID(moid)); + if (actor && actor != this && actor->GetClassName() != "ADoor" && !actor->IsInGroup("Brains")) { + actor->GibThis(); + } + } + } + s_CrabBombInEffect = false; } - } -} + Actor::GibThis(impactImpulse, movableObjectToIgnore); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DropAllInventory -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Opens the hatches and makes everything in the Rocket fly out, including -// the passenger Actors, one after another. It may not happen -// instantaneously, so check for ejection being complete with -// IsInventoryEmpty(). - -void ACraft::DropAllInventory() -{ - if (m_HatchState == OPEN && !m_Exits.empty()) - { - // Cancel if we're not due to release - if (!m_ExitTimer.IsPastSimMS(m_ExitInterval)) - return; - - bool exitExists = false; - std::list::iterator exit = m_Exits.begin(); - // Check which exits are clear of the terrain, if any - for (; exit != m_Exits.end(); ++exit) - { - if (exit->CheckIfClear(m_Pos, m_Rotation, 18)) - exitExists = true; - } - - // Cancel if no exits are clear - if (!exitExists) - { -// TODO: give some kind of audio feedback to user - return; - } - - // Eject inventory and passengers, through alternating clear exits - Vector exitVel, exitVelNorm, gravityNorm; - float antiGravBoost = 0; - Actor *pPassenger = 0; - bool droppedSomething = false; - for (std::deque::iterator exitee = m_Inventory.begin(); exitee != m_Inventory.end(); ++exitee) - { - // Select next clear exit - do - { - if (++m_CurrentExit == m_Exits.end()) - m_CurrentExit = m_Exits.begin(); - } - while (!m_CurrentExit->IsClear()); - - //(*exitee)->SetPos(m_Pos + m_CurrentExit->GetOffset() * m_Rotation); - (*exitee)->SetPos(m_Pos + RotateOffset(m_CurrentExit->GetOffset())); - // Reset all the timers of the object being shot out so it doesn't emit a bunch of particles that have been backed up while dormant in inventory - (*exitee)->ResetAllTimers(); - //exitVel = m_CurrentExit->GetVelocity() * m_Rotation; - exitVel = RotateOffset(m_CurrentExit->GetVelocity()); - - // Boost against gravity - // Normalize the gravity and exit velocity vectors - exitVelNorm = exitVel; - exitVelNorm.SetMagnitude(1.0); - gravityNorm = g_SceneMan.GetGlobalAcc(); - gravityNorm.SetMagnitude(1.0); - // Make the gravity boost be proportional to counteract gravity - antiGravBoost = 1.0 + 1.0 * exitVelNorm.Dot(-gravityNorm); - if (antiGravBoost < 1.0) - antiGravBoost = 1.0; - - // Detect whether we're dealing with a passenger and add it as Actor instead - pPassenger = dynamic_cast(*exitee); - if (pPassenger && m_ExitTimer.IsPastSimMS(m_ExitInterval)) - { - pPassenger->SetVel(m_Vel + exitVel * antiGravBoost); -// pPassenger->SetRotAngle(m_Rotation + exitVel.GetAbsRadAngle() (ejectDir > 0 ? -c_HalfPI : c_HalfPI)); -// pPassenger->SetHFlipped(ejectDir <= 0); - // Avoid having immediate collisions with this - pPassenger->SetWhichMOToNotHit(this, 0.5f); - // Avoid this immediate collisions with it - SetWhichMOToNotHit(pPassenger, 0.5f); - // Add to scene - g_MovableMan.AddActor(pPassenger); - - // If this craft is being directly controlled by a player, and has landed, switch control to the first guy out - if (pPassenger->GetTeam() == m_Team && m_Controller.IsPlayerControlled() && g_ActivityMan.GetActivity()->GetControlledActor(m_Controller.GetPlayer()) == this && m_LandingCraft) - { - g_ActivityMan.GetActivity()->SwitchToActor(pPassenger, m_Controller.GetPlayer(), m_Team); - // To avoid jump in the view, Update the passenger so its viewpoint is next to it and not at 0,0 - pPassenger->Update(); - } - - // Remove from inventory - m_Inventory.erase(exitee); - // Reset timer interval and quit until next one is due - m_ExitTimer.Reset(); - break; - } - else - { - (*exitee)->SetVel(m_Vel + exitVel * antiGravBoost); - (*exitee)->SetAngularVel(5.0F * RandomNormalNum()); - // Avoid it having immediate collisions with this - (*exitee)->SetWhichMOToNotHit(this, 0.5f); - // Avoid this immediate collisions with it - SetWhichMOToNotHit(*exitee, 0.5f); - // Add to scene - g_MovableMan.AddMO(*exitee); - // Remove passenger from inventory - m_Inventory.erase(exitee); - // Reset timer interval and quit until next one is due - m_ExitTimer.Reset(); - break; - } - droppedSomething = true; - } - - if (m_Inventory.empty()) - { - m_HasDelivered = true; - - // Kill craft if it is lying down. - if (std::fabs(m_Rotation.GetRadAngle()) > c_HalfPI && m_Status != DYING) { - m_Status = DYING; - m_DeathTmr.Reset(); - } - // Reset exit timer so we can time until when we should start sucking things in again - // Don't reset it if inventory is empty but we didn't drop anyhting, it means that doors just opened and nothing was inside, so immediately start sucking - if (droppedSomething) - m_ExitTimer.Reset(); - } - } - else - { - if (m_HatchState != OPENING) - { - if (m_HatchOpenSound) { m_HatchOpenSound->Play(m_Pos); } - g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, 0.4)); - m_HatchTimer.Reset(); - } - m_HatchState = OPENING; - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float ACraft::GetCollectedInventoryMass() const { - float inventoryMass = 0.0F; - for (const MovableObject *inventoryItem : m_CollectedInventory) { - inventoryMass += inventoryItem->GetMass(); + void ACraft::ResetAllTimers() { + MOSRotating::ResetAllTimers(); + + m_FlippedTimer.Reset(); } - return inventoryMass; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this ACraft. Supposed to be done every frame. + + void ACraft::Update() { + Actor::Update(); + + //////////////////////////////////// + // Update viewpoint -void ACraft::GibThis(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { - if (g_SettingsMan.CrabBombsEnabled() && !s_CrabBombInEffect) { - s_CrabBombInEffect = true; - int crabCount = 0; - for (const MovableObject *inventoryEntry : m_Inventory) { - if (inventoryEntry->GetPresetName() == "Crab") { crabCount++; } + // Set viewpoint based on how we are aiming etc. + m_ViewPoint = m_Pos.GetFloored(); + // Add velocity also so the viewpoint moves ahead at high speeds + if (m_Vel.MagnitudeIsGreaterThan(10.0F)) { + m_ViewPoint += m_Vel * std::sqrt(m_Vel.GetMagnitude() * 0.1F); } - if (crabCount >= g_SettingsMan.GetCrabBombThreshold()) { - for (int moid = 1; moid < g_MovableMan.GetMOIDCount() - 1; moid++) { - Actor *actor = dynamic_cast(g_MovableMan.GetMOFromID(moid)); - if (actor && actor != this && actor->GetClassName() != "ADoor" && !actor->IsInGroup("Brains")) { actor->GibThis(); } + + /////////////////////////////////////////////////// + // Crash detection and handling + const float crashSpeedThreshold = 1.0F; + if (m_DeepHardness > 5 && m_Vel.MagnitudeIsGreaterThan(crashSpeedThreshold)) { + m_Health -= m_DeepHardness * 0.03; + // TODO: HELLA GHETTO, REWORK + if (m_CrashTimer.GetElapsedSimTimeMS() > 500) { + if (m_CrashSound) { + m_CrashSound->Play(m_Pos); + } + m_CrashTimer.Reset(); } } - s_CrabBombInEffect = false; - } - Actor::GibThis(impactImpulse, movableObjectToIgnore); -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////// + // Doors open logic + + if (m_HatchState == OPEN) { + // Keep ejecting things if hatch is open and inventory isn't empty + if (!IsInventoryEmpty()) + DropAllInventory(); + // If doors are open and all inventory is ejected (and they have had a chance to fall away), then actively look for things to suck in through the exits. + else if (m_Status == STABLE && m_ExitTimer.IsPastSimMS(EXITSUCKDELAYMS)) { + // See if any of the exits have sucked in an MO + for (std::list::iterator exit = m_Exits.begin(); exit != m_Exits.end(); ++exit) { + // If exit sucked in an MO, add it to invetory + MOSRotating* pNewObject = exit->SuckInMOs(this); + if (pNewObject && !pNewObject->IsSetToDelete()) { + // Did we catch an Actor? If so, we need to do special controller switching in case it's player controlled atm + Actor* pCaughtActor = 0; + if (pCaughtActor = dynamic_cast(pNewObject)) { + // Switch control to this craft if the Actor we just caught is on our team and currently player controlled + // Set AI controller of the Actor going into the ship + if (pCaughtActor->GetTeam() == m_Team && g_ActivityMan.GetActivity() && pCaughtActor->GetController()->IsPlayerControlled()) + g_ActivityMan.GetActivity()->SwitchToActor(this, pCaughtActor->GetController()->GetPlayer(), pCaughtActor->GetTeam()); + // Add (copy) of caught Actor to this' inventory + AddInventoryItem(dynamic_cast(pCaughtActor->Clone())); + // Delete the original from scene - this is safer than 'removing' or handing over ownership halfway through MovableMan's update + pCaughtActor->SetToDelete(); + // Negate the team 'loss' that will be reported when the deletion is done + g_ActivityMan.GetActivity()->ReportDeath(pCaughtActor->GetTeam(), -1); + } + // Any other MO has been caught, jsut add it to inventory + // TODO: do we need special case for Attachables?? (so we can detach them first? - probably no because SuckInMOs is unlikely to return an attahced thing) + else { + // Add (copy) of caught Actor to this' inventory + AddInventoryItem(dynamic_cast(pNewObject->Clone())); + // Delete the original from scene - this is safer than 'removing' or handing over ownership halfway through MovableMan's update + pNewObject->SetToDelete(); + } + pNewObject = 0; + } + } + } + } -void ACraft::ResetAllTimers() { - MOSRotating::ResetAllTimers(); + ///////////////////////////////////////// + // Check for having gone into orbit - m_FlippedTimer.Reset(); -} + if (m_Pos.m_Y < -m_CharHeight || m_Pos.m_Y > g_SceneMan.GetSceneHeight() + m_CharHeight) { + g_ActivityMan.GetActivity()->HandleCraftEnteringOrbit(this); + // Play fading away thruster sound + // if (m_pMThruster && m_pMThruster->IsEmitting()) + // m_pMThruster->(pTargetBitmap, targetPos, mode, onlyPhysical); + m_ToDelete = true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this ACraft. Supposed to be done every frame. - -void ACraft::Update() -{ - Actor::Update(); - - //////////////////////////////////// - // Update viewpoint - - // Set viewpoint based on how we are aiming etc. - m_ViewPoint = m_Pos.GetFloored(); - // Add velocity also so the viewpoint moves ahead at high speeds - if (m_Vel.MagnitudeIsGreaterThan(10.0F)) { m_ViewPoint += m_Vel * std::sqrt(m_Vel.GetMagnitude() * 0.1F); } - - /////////////////////////////////////////////////// - // Crash detection and handling - const float crashSpeedThreshold = 1.0F; - if (m_DeepHardness > 5 && m_Vel.MagnitudeIsGreaterThan(crashSpeedThreshold)) - { - m_Health -= m_DeepHardness * 0.03; -// TODO: HELLA GHETTO, REWORK - if (m_CrashTimer.GetElapsedSimTimeMS() > 500) - { - if (m_CrashSound) { m_CrashSound->Play(m_Pos); } - m_CrashTimer.Reset(); - } - } - - /////////////////////////////////////////////////// - // Doors open logic - - if (m_HatchState == OPEN) - { - // Keep ejecting things if hatch is open and inventory isn't empty - if (!IsInventoryEmpty()) - DropAllInventory(); - // If doors are open and all inventory is ejected (and they have had a chance to fall away), then actively look for things to suck in through the exits. - else if (m_Status == STABLE && m_ExitTimer.IsPastSimMS(EXITSUCKDELAYMS)) - { - // See if any of the exits have sucked in an MO - for (std::list::iterator exit = m_Exits.begin(); exit != m_Exits.end(); ++exit) - { - // If exit sucked in an MO, add it to invetory - MOSRotating *pNewObject = exit->SuckInMOs(this); - if (pNewObject && !pNewObject->IsSetToDelete()) - { - // Did we catch an Actor? If so, we need to do special controller switching in case it's player controlled atm - Actor *pCaughtActor = 0; - if (pCaughtActor = dynamic_cast(pNewObject)) - { - // Switch control to this craft if the Actor we just caught is on our team and currently player controlled - // Set AI controller of the Actor going into the ship - if (pCaughtActor->GetTeam() == m_Team && g_ActivityMan.GetActivity() && pCaughtActor->GetController()->IsPlayerControlled()) - g_ActivityMan.GetActivity()->SwitchToActor(this, pCaughtActor->GetController()->GetPlayer(), pCaughtActor->GetTeam()); - // Add (copy) of caught Actor to this' inventory - AddInventoryItem(dynamic_cast(pCaughtActor->Clone())); - // Delete the original from scene - this is safer than 'removing' or handing over ownership halfway through MovableMan's update - pCaughtActor->SetToDelete(); - // Negate the team 'loss' that will be reported when the deletion is done - g_ActivityMan.GetActivity()->ReportDeath(pCaughtActor->GetTeam(), -1); - } - // Any other MO has been caught, jsut add it to inventory -// TODO: do we need special case for Attachables?? (so we can detach them first? - probably no because SuckInMOs is unlikely to return an attahced thing) - else - { - // Add (copy) of caught Actor to this' inventory - AddInventoryItem(dynamic_cast(pNewObject->Clone())); - // Delete the original from scene - this is safer than 'removing' or handing over ownership halfway through MovableMan's update - pNewObject->SetToDelete(); - } - pNewObject = 0; - } - } - } - } - - ///////////////////////////////////////// - // Check for having gone into orbit - - if (m_Pos.m_Y < -m_CharHeight || m_Pos.m_Y > g_SceneMan.GetSceneHeight() + m_CharHeight) - { - g_ActivityMan.GetActivity()->HandleCraftEnteringOrbit(this); - // Play fading away thruster sound -// if (m_pMThruster && m_pMThruster->IsEmitting()) -// m_pMThruster->(pTargetBitmap, targetPos, mode, onlyPhysical); - m_ToDelete = true; - } - - if (g_ActivityMan.GetActivity()->GetCraftOrbitAtTheEdge()) - { - if (g_SceneMan.GetScene() && !g_SceneMan.GetScene()->WrapsX()) - { - if (m_Pos.m_X < -GetSpriteWidth() || m_Pos.m_X > g_SceneMan.GetSceneWidth() + GetSpriteWidth()) - { - g_ActivityMan.GetActivity()->HandleCraftEnteringOrbit(this); - m_ToDelete = true; + if (g_ActivityMan.GetActivity()->GetCraftOrbitAtTheEdge()) { + if (g_SceneMan.GetScene() && !g_SceneMan.GetScene()->WrapsX()) { + if (m_Pos.m_X < -GetSpriteWidth() || m_Pos.m_X > g_SceneMan.GetSceneWidth() + GetSpriteWidth()) { + g_ActivityMan.GetActivity()->HandleCraftEnteringOrbit(this); + m_ToDelete = true; + } } } - } - if (m_Status == DEAD) { - if (m_ScuttleOnDeath || m_AIMode == AIMODE_SCUTTLE) { GibThis(); } - } else if (m_Status == DYING) { - if ((m_ScuttleOnDeath || m_AIMode == AIMODE_SCUTTLE) && m_DeathTmr.IsPastSimMS(500) && m_DeathTmr.AlternateSim(100)) { FlashWhite(10); } - } else if (m_Health <= 0 || m_AIMode == AIMODE_SCUTTLE || (m_ScuttleIfFlippedTime >= 0 && m_FlippedTimer.IsPastSimMS(m_ScuttleIfFlippedTime))) { - m_Status = DYING; - m_DeathTmr.Reset(); - DropAllInventory(); - } + if (m_Status == DEAD) { + if (m_ScuttleOnDeath || m_AIMode == AIMODE_SCUTTLE) { + GibThis(); + } + } else if (m_Status == DYING) { + if ((m_ScuttleOnDeath || m_AIMode == AIMODE_SCUTTLE) && m_DeathTmr.IsPastSimMS(500) && m_DeathTmr.AlternateSim(100)) { + FlashWhite(10); + } + } else if (m_Health <= 0 || m_AIMode == AIMODE_SCUTTLE || (m_ScuttleIfFlippedTime >= 0 && m_FlippedTimer.IsPastSimMS(m_ScuttleIfFlippedTime))) { + m_Status = DYING; + m_DeathTmr.Reset(); + DropAllInventory(); + } - // Get the rotation in radians. - float rot = m_Rotation.GetRadAngle(); + // Get the rotation in radians. + float rot = m_Rotation.GetRadAngle(); - // Eliminate rotations over half a turn. - if (std::abs(rot) > c_PI) { - rot += (rot > 0) ? -c_TwoPI : c_TwoPI; - m_Rotation.SetRadAngle(rot); - } - if (rot < c_HalfPI && rot > -c_HalfPI) { - m_FlippedTimer.Reset(); + // Eliminate rotations over half a turn. + if (std::abs(rot) > c_PI) { + rot += (rot > 0) ? -c_TwoPI : c_TwoPI; + m_Rotation.SetRadAngle(rot); + } + if (rot < c_HalfPI && rot > -c_HalfPI) { + m_FlippedTimer.Reset(); + } + + ///////////////////////////////////////// + // Misc. + + m_DeepCheck = true /*m_Status == DEAD*/; } - ///////////////////////////////////////// - // Misc. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Actor's current graphical HUD overlay representation to a + // BITMAP of choice. - m_DeepCheck = true/*m_Status == DEAD*/; -} + void ACraft::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { + m_HUDStack = -m_CharHeight / 2; + // Only do HUD if on a team + if (m_Team < 0) + return; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Actor's current graphical HUD overlay representation to a -// BITMAP of choice. - -void ACraft::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScreen, bool playerControlled) { - m_HUDStack = -m_CharHeight / 2; - - // Only do HUD if on a team - if (m_Team < 0) - return; - - // Only draw if the team viewing this is on the same team OR has seen the space where this is located. - int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); - if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam && (!g_SettingsMan.ShowEnemyHUD() || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam))) { - return; - } + // Only draw if the team viewing this is on the same team OR has seen the space where this is located. + int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); + if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam && (!g_SettingsMan.ShowEnemyHUD() || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam))) { + return; + } - Actor::DrawHUD(pTargetBitmap, targetPos, whichScreen); + Actor::DrawHUD(pTargetBitmap, targetPos, whichScreen); - if (!m_HUDVisible) { - return; - } + if (!m_HUDVisible) { + return; + } - GUIFont *pSymbolFont = g_FrameMan.GetLargeFont(); - GUIFont *pSmallFont = g_FrameMan.GetSmallFont(); - - // Draw hud guides for the Exits, depending on whether the doors are open - if (m_HatchState == OPEN)// || m_HatchState == OPENING) - { - // Doors open and inventory not empty yet, so show arrows pointing out of the exits since things are still coming out - if (!IsInventoryEmpty()) - { - // -------- - // | \ \ + GUIFont* pSymbolFont = g_FrameMan.GetLargeFont(); + GUIFont* pSmallFont = g_FrameMan.GetSmallFont(); + + // Draw hud guides for the Exits, depending on whether the doors are open + if (m_HatchState == OPEN) // || m_HatchState == OPENING) + { + // Doors open and inventory not empty yet, so show arrows pointing out of the exits since things are still coming out + if (!IsInventoryEmpty()) { + // -------- + // | \ \ // -+- | | - // | / / - // -------- - // Make the dotted lines crawl out of the exit, indicating that things are still coming out - if (--m_ExitLinePhase < 0) - m_ExitLinePhase = EXITLINESPACING - 1; - } - // Inventory empty and doors open, so show arrows pointing into the exits IF the delay to allow for things to eject away all the way has passed - else if (m_ExitTimer.IsPastSimMS(EXITSUCKDELAYMS)) - { - // Make the dotted lines crawl back into the exit, inviting people to jump in - if (++m_ExitLinePhase >= EXITLINESPACING) - m_ExitLinePhase = 0; - } - - Vector exitRadius; - Vector exitCorner; - Vector arrowVec; - // Draw the actual dotted lines - for (std::list::iterator exit = m_Exits.begin(); exit != m_Exits.end(); ++exit) - { - if (exit->CheckIfClear(m_Pos, m_Rotation, 18)) - { - exitRadius = RotateOffset(exit->GetVelocity().GetPerpendicular().SetMagnitude(exit->GetRadius())); - exitCorner = m_Pos - targetPos + RotateOffset(exit->GetOffset()) + exitRadius; - arrowVec = RotateOffset(exit->GetVelocity().SetMagnitude(exit->GetRange())); - g_FrameMan.DrawLine(pTargetBitmap, exitCorner, exitCorner + arrowVec, 120, 120, EXITLINESPACING, m_ExitLinePhase); - exitCorner -= exitRadius * 2; - g_FrameMan.DrawLine(pTargetBitmap, exitCorner, exitCorner + arrowVec, 120, 120, EXITLINESPACING, m_ExitLinePhase); - } - } - } - - // Only show extra HUD if this guy is controlled by a player - if (m_Controller.IsPlayerControlled() && pSmallFont && pSymbolFont) - { - AllegroBitmap pBitmapInt(pTargetBitmap); -/* - // AI Mode select GUI HUD - if (m_Controller && m_Controller.IsState(PIE_MENU_ACTIVE)) - { - char str[64]; - int iconOff = m_apAIIcons[0]->w + 2; - int iconColor = m_Team == Activity::TeamOne ? AIICON_RED : AIICON_GREEN; - Vector iconPos = GetCPUPos() - targetPos; - - if (m_AIMode == AIMODE_RETURN) - { - std::snprintf(str, sizeof(str), "%s", "Return"); - pSmallFont->DrawAligned(&pBitmapInt, iconPos.m_X, iconPos.m_Y - 18, str, GUIFont::Centre); - } - else if (m_AIMode == AIMODE_DELIVER) - { - std::snprintf(str, sizeof(str), "%s", "Deliver"); - pSmallFont->DrawAligned(&pBitmapInt, iconPos.m_X - 9, iconPos.m_Y - 5, str, GUIFont::Right); - } - else if (m_AIMode == AIMODE_SCUTTLE) - { - std::snprintf(str, sizeof(str), "%s", "Scuttle"); - pSmallFont->DrawAligned(&pBitmapInt, iconPos.m_X + 9, iconPos.m_Y - 5, str, GUIFont::Left); - } - else if (m_AIMode == AIMODE_STAY) - { - std::snprintf(str, sizeof(str), "%s", "Stay"); - pSmallFont->DrawAligned(&pBitmapInt, iconPos.m_X, iconPos.m_Y + 8, str, GUIFont::Centre); - } - - // Draw the mode alternatives if they are not the current one - if (m_AIMode != AIMODE_RETURN) - { - draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_RETURN], iconPos.m_X - 6, iconPos.m_Y - 6 - iconOff); - } - if (m_AIMode != AIMODE_DELIVER) - { - draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_DELIVER], iconPos.m_X - 6 - iconOff, iconPos.m_Y - 6); - } - if (m_AIMode != AIMODE_SCUTTLE) - { - draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_SCUTTLE], iconPos.m_X - 6 + iconOff, iconPos.m_Y - 6); - } - if (m_AIMode != AIMODE_STAY) - { - draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_STAY], iconPos.m_X - 6, iconPos.m_Y - 6 + iconOff); - } - } -*/ - } -} + // | / / + // -------- + // Make the dotted lines crawl out of the exit, indicating that things are still coming out + if (--m_ExitLinePhase < 0) + m_ExitLinePhase = EXITLINESPACING - 1; + } + // Inventory empty and doors open, so show arrows pointing into the exits IF the delay to allow for things to eject away all the way has passed + else if (m_ExitTimer.IsPastSimMS(EXITSUCKDELAYMS)) { + // Make the dotted lines crawl back into the exit, inviting people to jump in + if (++m_ExitLinePhase >= EXITLINESPACING) + m_ExitLinePhase = 0; + } + + Vector exitRadius; + Vector exitCorner; + Vector arrowVec; + // Draw the actual dotted lines + for (std::list::iterator exit = m_Exits.begin(); exit != m_Exits.end(); ++exit) { + if (exit->CheckIfClear(m_Pos, m_Rotation, 18)) { + exitRadius = RotateOffset(exit->GetVelocity().GetPerpendicular().SetMagnitude(exit->GetRadius())); + exitCorner = m_Pos - targetPos + RotateOffset(exit->GetOffset()) + exitRadius; + arrowVec = RotateOffset(exit->GetVelocity().SetMagnitude(exit->GetRange())); + g_FrameMan.DrawLine(pTargetBitmap, exitCorner, exitCorner + arrowVec, 120, 120, EXITLINESPACING, m_ExitLinePhase); + exitCorner -= exitRadius * 2; + g_FrameMan.DrawLine(pTargetBitmap, exitCorner, exitCorner + arrowVec, 120, 120, EXITLINESPACING, m_ExitLinePhase); + } + } + } + + // Only show extra HUD if this guy is controlled by a player + if (m_Controller.IsPlayerControlled() && pSmallFont && pSymbolFont) { + AllegroBitmap pBitmapInt(pTargetBitmap); + /* + // AI Mode select GUI HUD + if (m_Controller && m_Controller.IsState(PIE_MENU_ACTIVE)) + { + char str[64]; + int iconOff = m_apAIIcons[0]->w + 2; + int iconColor = m_Team == Activity::TeamOne ? AIICON_RED : AIICON_GREEN; + Vector iconPos = GetCPUPos() - targetPos; + + if (m_AIMode == AIMODE_RETURN) + { + std::snprintf(str, sizeof(str), "%s", "Return"); + pSmallFont->DrawAligned(&pBitmapInt, iconPos.m_X, iconPos.m_Y - 18, str, GUIFont::Centre); + } + else if (m_AIMode == AIMODE_DELIVER) + { + std::snprintf(str, sizeof(str), "%s", "Deliver"); + pSmallFont->DrawAligned(&pBitmapInt, iconPos.m_X - 9, iconPos.m_Y - 5, str, GUIFont::Right); + } + else if (m_AIMode == AIMODE_SCUTTLE) + { + std::snprintf(str, sizeof(str), "%s", "Scuttle"); + pSmallFont->DrawAligned(&pBitmapInt, iconPos.m_X + 9, iconPos.m_Y - 5, str, GUIFont::Left); + } + else if (m_AIMode == AIMODE_STAY) + { + std::snprintf(str, sizeof(str), "%s", "Stay"); + pSmallFont->DrawAligned(&pBitmapInt, iconPos.m_X, iconPos.m_Y + 8, str, GUIFont::Centre); + } + + // Draw the mode alternatives if they are not the current one + if (m_AIMode != AIMODE_RETURN) + { + draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_RETURN], iconPos.m_X - 6, iconPos.m_Y - 6 - iconOff); + } + if (m_AIMode != AIMODE_DELIVER) + { + draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_DELIVER], iconPos.m_X - 6 - iconOff, iconPos.m_Y - 6); + } + if (m_AIMode != AIMODE_SCUTTLE) + { + draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_SCUTTLE], iconPos.m_X - 6 + iconOff, iconPos.m_Y - 6); + } + if (m_AIMode != AIMODE_STAY) + { + draw_sprite(pTargetBitmap, m_apAIIcons[AIMODE_STAY], iconPos.m_X - 6, iconPos.m_Y - 6 + iconOff); + } + } + */ + } + } } // namespace RTE \ No newline at end of file diff --git a/Source/Entities/ACraft.h b/Source/Entities/ACraft.h index f381da8003..28398613b7 100644 --- a/Source/Entities/ACraft.h +++ b/Source/Entities/ACraft.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -19,682 +18,627 @@ struct BITMAP; -namespace RTE -{ - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: ACraft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A flying Actor which carries other things and can drop them. -// Parent(s): Actor. -// Class history: 12/13/2006 ACraft created. - -class ACraft : public Actor { - friend struct EntityLuaBindings; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - SerializableOverrideMethods; - ClassInfoGetters; - DefaultPieMenuNameGetter("Default Craft Pie Menu"); - +namespace RTE { -enum HatchState -{ - CLOSED = 0, - OPENING, - OPEN, - CLOSING, - HatchStateCount -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: ACraft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A flying Actor which carries other things and can drop them. + // Parent(s): Actor. + // Class history: 12/13/2006 ACraft created. -enum Side -{ - RIGHT = 0, - LEFT -}; + class ACraft : public Actor { + friend struct EntityLuaBindings; - ////////////////////////////////////////////////////////////////////////////////////////// - // Nested class: Exit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Something to bundle the properties of ACraft exits together. - // Parent(s): Serializable. - // Class history: 12/19/2006 Exit created. + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations - class Exit: - public Serializable - { - - friend class ACraft; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - - public: - - SerializableClassNameGetter; + public: SerializableOverrideMethods; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Exit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Exit object in system - // memory. Create() should be called before using the object. - // Arguments: None. - - Exit() { Clear(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Exit object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. + ClassInfoGetters; + DefaultPieMenuNameGetter("Default Craft Pie Menu"); + + enum HatchState { + CLOSED = 0, + OPENING, + OPEN, + CLOSING, + HatchStateCount + }; + + enum Side { + RIGHT = 0, + LEFT + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Nested class: Exit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Something to bundle the properties of ACraft exits together. + // Parent(s): Serializable. + // Class history: 12/19/2006 Exit created. + + class Exit : + public Serializable { + + friend class ACraft; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + SerializableClassNameGetter; + SerializableOverrideMethods; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: Exit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a Exit object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + Exit() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Exit object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Exit to be identical to another, by deep copy. + // Arguments: A reference to the Exit to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const Exit& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire Serializable, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the position offset of this exit from the position of its ACraft. + // Arguments: None. + // Return value: The coordinates relative to the m_Pos of this' ACraft. + + Vector GetOffset() const { return m_Offset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetVelocity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the velocity of anything that exits through this. + // Arguments: None. + // Return value: The velocity vector for anything exiting through this. + + Vector GetVelocity() const { return m_Velocity * (1.0F + m_VelSpread * RandomNormalNum()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRadius + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the width from the center tanget created by the velocity vector + // out from the offet point. This times two gives the total width of the + // opening. + // Arguments: None. + // Return value: Half the total width of the opening. + + float GetRadius() const { return m_Radius; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the distance this exit can suck in objects from. + // Arguments: None. + // Return value: The sucking range of this. + + float GetRange() const { return m_Range; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CheckIfClear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates whether this exit is currently clear enough of terrain to + // safely put things through without them ending up in the terrain. + // Arguments: The position of the parent ACraft of this. + // The rotation of the parent ACraft of this. + // How large (radius) the item is that is supposed to fit. + // Return value: If this has been determined clear to put anything through. + + bool CheckIfClear(const Vector& pos, Matrix& rot, float size = 20); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsClear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells if this is clear of the terrain to put things through. Faster than + // CheckIfClear(). + // Arguments: None. + // Return value: If this has been determined clear to put anything through. + + bool IsClear() const { return m_Clear; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SuckInMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Uses cast MO rays to see if anyhting is able to be drawn into this + // exit. If so, it will alter the positiona nd velocity of the objet so + // it flies into the exit until it is sufficiently inside and then it'll + // return the MO here, OWNERHIP NOT TRANSFERRED! It is still in MovableMan! + // Arguments: A pointer to the ACraft owner of this Exit. OWNERSHIP IS NOT TRANSFERRED! + // Return value: If an MO has been fully drawn into the exit, it will be returned here, + // OWNERSHIP NOT TRANSFERRED! + + MOSRotating* SuckInMOs(ACraft* pExitOwner); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // The offset of this exit relative the position of its ACraft + Vector m_Offset; + // The exiting velocity of anyhting exiting through this + Vector m_Velocity; + // The spread in velocity, ratio + float m_VelSpread; + // The width from the center tanget created by the velocity vector out from the offet point. This times two gives the total width of the opening. + float m_Radius; + // How far away the exit cna suck objects in from + float m_Range; + // Temporary var to check if this is clear of terrain for putting things through + bool m_Clear; + // Movable Object that is being drawn into this exit + MOSRotating* m_pIncomingMO; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Exit, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; + + // Concrete allocation and cloning definitions + // EntityAllocation(ACraft) + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: ACraft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a ACraft object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + ACraft() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~ACraft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a ACraft object before deletion + // from system memory. + // Arguments: None. + + ~ACraft() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ACraft object ready for use. + // Arguments: A Reader that the ACraft will create itself with. + // Whether there is a class name in the stream to check against to make + // sure the correct type is being read from the stream. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. int Create() override; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Exit to be identical to another, by deep copy. - // Arguments: A reference to the Exit to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - - int Create(const Exit &reference); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Serializable, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - - void Reset() override { Clear(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the position offset of this exit from the position of its ACraft. - // Arguments: None. - // Return value: The coordinates relative to the m_Pos of this' ACraft. - - Vector GetOffset() const { return m_Offset; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the velocity of anything that exits through this. - // Arguments: None. - // Return value: The velocity vector for anything exiting through this. - - Vector GetVelocity() const { return m_Velocity * (1.0F + m_VelSpread * RandomNormalNum()); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRadius - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the width from the center tanget created by the velocity vector - // out from the offet point. This times two gives the total width of the - // opening. - // Arguments: None. - // Return value: Half the total width of the opening. - - float GetRadius() const { return m_Radius; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the distance this exit can suck in objects from. - // Arguments: None. - // Return value: The sucking range of this. - - float GetRange() const { return m_Range; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CheckIfClear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates whether this exit is currently clear enough of terrain to - // safely put things through without them ending up in the terrain. - // Arguments: The position of the parent ACraft of this. - // The rotation of the parent ACraft of this. - // How large (radius) the item is that is supposed to fit. - // Return value: If this has been determined clear to put anything through. - - bool CheckIfClear(const Vector &pos, Matrix &rot, float size = 20); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsClear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells if this is clear of the terrain to put things through. Faster than - // CheckIfClear(). - // Arguments: None. - // Return value: If this has been determined clear to put anything through. - - bool IsClear() const { return m_Clear; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SuckInMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Uses cast MO rays to see if anyhting is able to be drawn into this - // exit. If so, it will alter the positiona nd velocity of the objet so - // it flies into the exit until it is sufficiently inside and then it'll - // return the MO here, OWNERHIP NOT TRANSFERRED! It is still in MovableMan! - // Arguments: A pointer to the ACraft owner of this Exit. OWNERSHIP IS NOT TRANSFERRED! - // Return value: If an MO has been fully drawn into the exit, it will be returned here, - // OWNERSHIP NOT TRANSFERRED! - - MOSRotating * SuckInMOs(ACraft *pExitOwner); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - - protected: - - // The offset of this exit relative the position of its ACraft - Vector m_Offset; - // The exiting velocity of anyhting exiting through this - Vector m_Velocity; - // The spread in velocity, ratio - float m_VelSpread; - // The width from the center tanget created by the velocity vector out from the offet point. This times two gives the total width of the opening. - float m_Radius; - // How far away the exit cna suck objects in from - float m_Range; - // Temporary var to check if this is clear of terrain for putting things through - bool m_Clear; - // Movable Object that is being drawn into this exit - MOSRotating *m_pIncomingMO; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - - private: - - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Exit, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - - void Clear(); - - }; - - -// Concrete allocation and cloning definitions -//EntityAllocation(ACraft) - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: ACraft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a ACraft object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - ACraft() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~ACraft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a ACraft object before deletion -// from system memory. -// Arguments: None. - - ~ACraft() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ACraft object ready for use. -// Arguments: A Reader that the ACraft will create itself with. -// Whether there is a class name in the stream to check against to make -// sure the correct type is being read from the stream. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a ACraft to be identical to another, by deep copy. -// Arguments: A reference to the ACraft to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const ACraft &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire ACraft, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Actor::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of this Actor and all its carried -// gold and inventory. -// Arguments: If this is supposed to be adjusted for a specific Tech's subjective -// value, then pass in the native DataModule ID of that tech. 0 means -// no Tech is specified and the base value is returned. -// How much to multiply the value if this happens to be a foreign Tech. -// Return value: The current value of this Actor and all his carried assets. - - float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically named object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. -// Arguments: The Preset name of the object to look for. -// Return value: Whetehr the object was found carried by this. - - bool HasObject(std::string objectName) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObjectInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically grouped object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. -// Arguments: The name of the group to look for. -// Return value: Whetehr the object in the group was found carried by this. - - bool HasObjectInGroup(std::string groupName) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetHatchState -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current state of the hatch. -// Arguments: None. -// Return value: An int encoding the hatch state. See the HatchState enum. - - unsigned int GetHatchState() const { return m_HatchState; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this belongs to, and all its inventory too. -// Arguments: The assigned team number. -// Return value: None. - - void SetTeam(int team) override; - - /// - /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. - /// - /// The SliceType of the PieSlice being handled. - /// Whether or not the activated PieSlice SliceType was able to be handled. - bool HandlePieCommand(PieSlice::SliceType pieSliceType) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AutoStabilizing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether this has the means and will try to right itself, or if -// that's up to the Controller to do. -// Arguments: None. -// Return value: Wheter this will try to auto stabilize. - - virtual bool AutoStabilizing() { return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OpenHatch -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Opens the hatch doors, if they're closed or closing. -// Arguments: None. -// Return value: None. - - void OpenHatch(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CloseHatch -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Closes the hatch doors, if they're open or opening. -// Arguments: None. -// Return value: None. - - void CloseHatch(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: AddInventoryItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an inventory item to this Actor. -// Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! -// Return value: None.. - - void AddInventoryItem(MovableObject *pItemToAdd) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DropAllInventory -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Opens the hatches and makes everything in the Rocket fly out, including -// the passenger Actors, one after another. It may not happen -// instantaneously, so check for ejection being complete with -// IsInventoryEmpty(). -// Arguments: None. -// Return value: None. - - void DropAllInventory() override; - - /// - /// Gets the mass of this ACraft's inventory of newly collected items. - /// - /// The mass of this ACraft's newly collected inventory. - float GetCollectedInventoryMass() const; - - /// - /// Gets the mass of this ACraft, including the mass of its Attachables, wounds and inventory. - /// - /// The mass of this ACraft, its inventory and all its Attachables and wounds in Kilograms (kg). - float GetMass() const override { return Actor::GetMass() + GetCollectedInventoryMass(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasDelivered -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicated whether this has landed and delivered yet on its current run. -// Arguments: None. -// Return value: Whether this has delivered yet. - - bool HasDelivered() { return m_HasDelivered; } - - /// - /// Resets all the timers related to this, including the scuttle timer. - /// - void ResetAllTimers() override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Actor's current graphical HUD overlay representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// Which player's screen this is being drawn to. May affect what HUD elements -// get drawn etc. -// Return value: None. - - void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMaxPassengers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The recomended, not absolute, maximum number of actors that fit in the -// invetory. Used by the activity AI. -// Arguments: None. -// Return value: An integer with the recomended number of actors that fit in the craft. -// Default is -1 (unknown). - - virtual int GetMaxPassengers() const { return m_MaxPassengers; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetMaxPassengers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the recomended, not absolute, maximum number of actors that fit in the -// invetory. Used by the activity AI. -// Arguments: An integer with the recomended number of actors that fit in the craft. -// Default is -1 (unknown). -// Return value: None. - - virtual void SetMaxPassengers(int max) { m_MaxPassengers = max; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDeliveryDelayMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns delivery delay multiplier. -// Arguments: None. -// Return value: Delivery delay multiplier. - - float GetDeliveryDelayMultiplier() const { return m_DeliveryDelayMultiplier; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDeliveryDelayMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets delivery delay multiplier. -// Arguments: Delivery delay multiplier. -// Return value: None. - - void SetDeliveryDelayMultiplier(float newValue) { m_DeliveryDelayMultiplier = newValue; } - - - /// - /// Gets whether this ACraft will scuttle automatically on death. - /// - /// Whether this ACraft will scuttle automatically on death. - bool GetScuttleOnDeath() const { return m_ScuttleOnDeath; } - - /// - /// Sets whether this ACraft will scuttle automatically on death. - /// - /// Whether this ACraft will scuttle automatically on death. - void SetScuttleOnDeath(bool scuttleOnDeath) { m_ScuttleOnDeath = scuttleOnDeath; } - - /// - /// Gets the hatch opening/closing delay of this ACraft. - /// - /// The hatch delay of this ACraft. - int GetHatchDelay() const { return m_HatchDelay; } - - /// - /// Sets the hatch opening/closing delay of this ACraft. - /// - /// The new hatch delay of this ACraft. - void SetHatchDelay(int newDelay) { m_HatchDelay = newDelay; } - - /// - /// Destroys this ACraft and creates its specified Gibs in its place with appropriate velocities. Any Attachables are removed and also given appropriate velocities. - /// - /// The impulse (kg * m/s) of the impact causing the gibbing to happen. - /// A pointer to an MO which the Gibs and Attachables should not be colliding with. - void GibThis(const Vector &impactImpulse = Vector(), MovableObject *movableObjectToIgnore = nullptr) override; - - /// - /// Gets this ACraft's hatch opening sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ACraft's hatch opening sound. - SoundContainer * GetHatchOpenSound() const { return m_HatchOpenSound; } - - /// - /// Sets this ACraft's hatch opening sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ACraft's hatch opening sound. - void SetHatchOpenSound(SoundContainer *newSound) { m_HatchOpenSound = newSound; } - - /// - /// Gets this ACraft's hatch closing sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ACraft's hatch closing sound. - SoundContainer * GetHatchCloseSound() const { return m_HatchCloseSound; } - - /// - /// Sets this ACraft's hatch closing sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ACraft's hatch closing sound. - void SetHatchCloseSound(SoundContainer *newSound) { m_HatchCloseSound = newSound; } - - /// - /// Gets this ACraft's crash sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ACraft's crash sound. - SoundContainer * GetCrashSound() const { return m_CrashSound; } - - /// - /// Sets this ACraft's crash sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ACraft's crash sound. - void SetCrashSound(SoundContainer *newSound) { m_CrashSound = newSound; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - // Current movement state. - unsigned int m_MoveState; - // Current hatch action state. - unsigned int m_HatchState; - // Timer for opening and closing hatches - Timer m_HatchTimer; - // The time it takes to open or close the hatch, in ms. - int m_HatchDelay; - // Sound for opening the hatch - SoundContainer *m_HatchOpenSound; - // Sound for closing the hatch - SoundContainer *m_HatchCloseSound; - std::deque m_CollectedInventory; //!< A separate inventory to temporarily store newly collected items, so that they don't get immediately ejected from the main inventory while the hatch is still open. - // All the possible exits for when ejecting stuff out of this. - std::list m_Exits; - // Last used exit so we can alternate/cycle - std::list::iterator m_CurrentExit; - // The delay between each exiting passenger Actor - long m_ExitInterval; - // Times the exit interval - Timer m_ExitTimer; - // The phase of the exit lines animation - int m_ExitLinePhase; - // Whether this has landed and delivered yet on its current run - bool m_HasDelivered; - // Whether this is capable of landing on the ground at all - bool m_LandingCraft; - // Timer for checking if craft is hopelessly flipped and should die - Timer m_FlippedTimer; - // Timer to measure how long ago a crash sound was played - Timer m_CrashTimer; - // Crash sound - SoundContainer *m_CrashSound; - // The maximum number of actors that fit in the inventory - int m_MaxPassengers; - int m_ScuttleIfFlippedTime; //!< The time after which the craft will scuttle automatically, if tipped over. - bool m_ScuttleOnDeath; //!< Whether the craft will self-destruct at zero health. - - static bool s_CrabBombInEffect; //!< Flag to determine if a craft is triggering the Crab Bomb effect. - - //////// - // AI states - - enum CraftDeliverySequence - { - FALL = 0, - LAND, - STANDBY, - UNLOAD, - LAUNCH, - UNSTICK - }; - - enum AltitudeMoveState - { - HOVER = 0, - DESCEND, - ASCEND - }; - - // What the rocket/ship is currently doing in an AI landing sequence - int m_DeliveryState; - // Whether the AI is trying to go higher, lower, or stand still in altitude - int m_AltitudeMoveState; - // Controls the altitude gain/loss the AI is trying to achieve. Normalized -1.0 (max rise) to 1.0 (max drop). 0 is approximate hover. - float m_AltitudeControl; - // Mutliplier to apply to default delivery time - float m_DeliveryDelayMultiplier; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ACraft, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - ACraft(const ACraft &reference) = delete; - ACraft & operator=(const ACraft &rhs) = delete; - -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a ACraft to be identical to another, by deep copy. + // Arguments: A reference to the ACraft to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const ACraft& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire ACraft, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Actor::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of this Actor and all its carried + // gold and inventory. + // Arguments: If this is supposed to be adjusted for a specific Tech's subjective + // value, then pass in the native DataModule ID of that tech. 0 means + // no Tech is specified and the base value is returned. + // How much to multiply the value if this happens to be a foreign Tech. + // Return value: The current value of this Actor and all his carried assets. + + float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically named object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. + // Arguments: The Preset name of the object to look for. + // Return value: Whetehr the object was found carried by this. + + bool HasObject(std::string objectName) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObjectInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically grouped object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. + // Arguments: The name of the group to look for. + // Return value: Whetehr the object in the group was found carried by this. + + bool HasObjectInGroup(std::string groupName) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetHatchState + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current state of the hatch. + // Arguments: None. + // Return value: An int encoding the hatch state. See the HatchState enum. + + unsigned int GetHatchState() const { return m_HatchState; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this belongs to, and all its inventory too. + // Arguments: The assigned team number. + // Return value: None. + + void SetTeam(int team) override; + + /// + /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. + /// + /// The SliceType of the PieSlice being handled. + /// Whether or not the activated PieSlice SliceType was able to be handled. + bool HandlePieCommand(PieSlice::SliceType pieSliceType) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AutoStabilizing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether this has the means and will try to right itself, or if + // that's up to the Controller to do. + // Arguments: None. + // Return value: Wheter this will try to auto stabilize. + + virtual bool AutoStabilizing() { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OpenHatch + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Opens the hatch doors, if they're closed or closing. + // Arguments: None. + // Return value: None. + + void OpenHatch(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CloseHatch + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Closes the hatch doors, if they're open or opening. + // Arguments: None. + // Return value: None. + + void CloseHatch(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: AddInventoryItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an inventory item to this Actor. + // Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! + // Return value: None.. + + void AddInventoryItem(MovableObject* pItemToAdd) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DropAllInventory + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Opens the hatches and makes everything in the Rocket fly out, including + // the passenger Actors, one after another. It may not happen + // instantaneously, so check for ejection being complete with + // IsInventoryEmpty(). + // Arguments: None. + // Return value: None. + + void DropAllInventory() override; + + /// + /// Gets the mass of this ACraft's inventory of newly collected items. + /// + /// The mass of this ACraft's newly collected inventory. + float GetCollectedInventoryMass() const; + + /// + /// Gets the mass of this ACraft, including the mass of its Attachables, wounds and inventory. + /// + /// The mass of this ACraft, its inventory and all its Attachables and wounds in Kilograms (kg). + float GetMass() const override { return Actor::GetMass() + GetCollectedInventoryMass(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasDelivered + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicated whether this has landed and delivered yet on its current run. + // Arguments: None. + // Return value: Whether this has delivered yet. + + bool HasDelivered() { return m_HasDelivered; } + + /// + /// Resets all the timers related to this, including the scuttle timer. + /// + void ResetAllTimers() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Actor's current graphical HUD overlay representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // Which player's screen this is being drawn to. May affect what HUD elements + // get drawn etc. + // Return value: None. + + void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMaxPassengers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The recomended, not absolute, maximum number of actors that fit in the + // invetory. Used by the activity AI. + // Arguments: None. + // Return value: An integer with the recomended number of actors that fit in the craft. + // Default is -1 (unknown). + + virtual int GetMaxPassengers() const { return m_MaxPassengers; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetMaxPassengers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the recomended, not absolute, maximum number of actors that fit in the + // invetory. Used by the activity AI. + // Arguments: An integer with the recomended number of actors that fit in the craft. + // Default is -1 (unknown). + // Return value: None. + + virtual void SetMaxPassengers(int max) { m_MaxPassengers = max; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDeliveryDelayMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns delivery delay multiplier. + // Arguments: None. + // Return value: Delivery delay multiplier. + + float GetDeliveryDelayMultiplier() const { return m_DeliveryDelayMultiplier; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDeliveryDelayMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets delivery delay multiplier. + // Arguments: Delivery delay multiplier. + // Return value: None. + + void SetDeliveryDelayMultiplier(float newValue) { m_DeliveryDelayMultiplier = newValue; } + + /// + /// Gets whether this ACraft will scuttle automatically on death. + /// + /// Whether this ACraft will scuttle automatically on death. + bool GetScuttleOnDeath() const { return m_ScuttleOnDeath; } + + /// + /// Sets whether this ACraft will scuttle automatically on death. + /// + /// Whether this ACraft will scuttle automatically on death. + void SetScuttleOnDeath(bool scuttleOnDeath) { m_ScuttleOnDeath = scuttleOnDeath; } + + /// + /// Gets the hatch opening/closing delay of this ACraft. + /// + /// The hatch delay of this ACraft. + int GetHatchDelay() const { return m_HatchDelay; } + + /// + /// Sets the hatch opening/closing delay of this ACraft. + /// + /// The new hatch delay of this ACraft. + void SetHatchDelay(int newDelay) { m_HatchDelay = newDelay; } + + /// + /// Destroys this ACraft and creates its specified Gibs in its place with appropriate velocities. Any Attachables are removed and also given appropriate velocities. + /// + /// The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr) override; + + /// + /// Gets this ACraft's hatch opening sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this ACraft's hatch opening sound. + SoundContainer* GetHatchOpenSound() const { return m_HatchOpenSound; } + + /// + /// Sets this ACraft's hatch opening sound. Ownership IS transferred! + /// + /// The new SoundContainer for this ACraft's hatch opening sound. + void SetHatchOpenSound(SoundContainer* newSound) { m_HatchOpenSound = newSound; } + + /// + /// Gets this ACraft's hatch closing sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this ACraft's hatch closing sound. + SoundContainer* GetHatchCloseSound() const { return m_HatchCloseSound; } + + /// + /// Sets this ACraft's hatch closing sound. Ownership IS transferred! + /// + /// The new SoundContainer for this ACraft's hatch closing sound. + void SetHatchCloseSound(SoundContainer* newSound) { m_HatchCloseSound = newSound; } + + /// + /// Gets this ACraft's crash sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this ACraft's crash sound. + SoundContainer* GetCrashSound() const { return m_CrashSound; } + + /// + /// Sets this ACraft's crash sound. Ownership IS transferred! + /// + /// The new SoundContainer for this ACraft's crash sound. + void SetCrashSound(SoundContainer* newSound) { m_CrashSound = newSound; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + // Current movement state. + unsigned int m_MoveState; + // Current hatch action state. + unsigned int m_HatchState; + // Timer for opening and closing hatches + Timer m_HatchTimer; + // The time it takes to open or close the hatch, in ms. + int m_HatchDelay; + // Sound for opening the hatch + SoundContainer* m_HatchOpenSound; + // Sound for closing the hatch + SoundContainer* m_HatchCloseSound; + std::deque m_CollectedInventory; //!< A separate inventory to temporarily store newly collected items, so that they don't get immediately ejected from the main inventory while the hatch is still open. + // All the possible exits for when ejecting stuff out of this. + std::list m_Exits; + // Last used exit so we can alternate/cycle + std::list::iterator m_CurrentExit; + // The delay between each exiting passenger Actor + long m_ExitInterval; + // Times the exit interval + Timer m_ExitTimer; + // The phase of the exit lines animation + int m_ExitLinePhase; + // Whether this has landed and delivered yet on its current run + bool m_HasDelivered; + // Whether this is capable of landing on the ground at all + bool m_LandingCraft; + // Timer for checking if craft is hopelessly flipped and should die + Timer m_FlippedTimer; + // Timer to measure how long ago a crash sound was played + Timer m_CrashTimer; + // Crash sound + SoundContainer* m_CrashSound; + // The maximum number of actors that fit in the inventory + int m_MaxPassengers; + int m_ScuttleIfFlippedTime; //!< The time after which the craft will scuttle automatically, if tipped over. + bool m_ScuttleOnDeath; //!< Whether the craft will self-destruct at zero health. + + static bool s_CrabBombInEffect; //!< Flag to determine if a craft is triggering the Crab Bomb effect. + + //////// + // AI states + + enum CraftDeliverySequence { + FALL = 0, + LAND, + STANDBY, + UNLOAD, + LAUNCH, + UNSTICK + }; + + enum AltitudeMoveState { + HOVER = 0, + DESCEND, + ASCEND + }; + + // What the rocket/ship is currently doing in an AI landing sequence + int m_DeliveryState; + // Whether the AI is trying to go higher, lower, or stand still in altitude + int m_AltitudeMoveState; + // Controls the altitude gain/loss the AI is trying to achieve. Normalized -1.0 (max rise) to 1.0 (max drop). 0 is approximate hover. + float m_AltitudeControl; + // Mutliplier to apply to default delivery time + float m_DeliveryDelayMultiplier; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ACraft, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + ACraft(const ACraft& reference) = delete; + ACraft& operator=(const ACraft& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/ADSensor.cpp b/Source/Entities/ADSensor.cpp index 1e24885877..d11b802f75 100644 --- a/Source/Entities/ADSensor.cpp +++ b/Source/Entities/ADSensor.cpp @@ -5,7 +5,7 @@ namespace RTE { const std::string ADSensor::c_ClassName = "Sensor"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADSensor::Clear() { m_StartOffset.Reset(); @@ -13,9 +13,9 @@ namespace RTE { m_Skip = 3; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADSensor::Create(const ADSensor &reference) { + int ADSensor::Create(const ADSensor& reference) { m_StartOffset = reference.m_StartOffset; m_SensorRay = reference.m_SensorRay; m_Skip = reference.m_Skip; @@ -23,11 +23,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADSensor::ReadProperty(const std::string_view &propName, Reader &reader) { + int ADSensor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("StartOffset", { reader >> m_StartOffset; }); MatchProperty("SensorRay", { reader >> m_SensorRay; }); MatchProperty("SkipPixels", { reader >> m_Skip; }); @@ -35,9 +35,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADSensor::Save(Writer &writer) const { + int ADSensor::Save(Writer& writer) const { Serializable::Save(writer); writer.NewProperty("StartOffset"); @@ -50,22 +50,24 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Actor * ADSensor::SenseActor(const Vector &doorPos, const Matrix &doorRot, bool doorHFlipped, MOID ignoreMOID) { - Actor *sensedActor = 0; + Actor* ADSensor::SenseActor(const Vector& doorPos, const Matrix& doorRot, bool doorHFlipped, MOID ignoreMOID) { + Actor* sensedActor = 0; MOID foundMOID = g_SceneMan.CastMORay(doorPos + m_StartOffset.GetXFlipped(doorHFlipped) * doorRot, m_SensorRay.GetXFlipped(doorHFlipped) * doorRot, ignoreMOID, Activity::NoTeam, 0, true, m_Skip); if (foundMOID) { - sensedActor = dynamic_cast(g_MovableMan.GetMOFromID(g_MovableMan.GetRootMOID(foundMOID))); + sensedActor = dynamic_cast(g_MovableMan.GetMOFromID(g_MovableMan.GetRootMOID(foundMOID))); // Reverse the ray direction if the sensed actor was not valid, to see if we hit anything else relevant. if (!sensedActor || !sensedActor->IsControllable()) { foundMOID = g_SceneMan.CastMORay(doorPos + (m_StartOffset.GetXFlipped(doorHFlipped) + m_SensorRay.GetXFlipped(doorHFlipped)) * doorRot, (-m_SensorRay.GetXFlipped(doorHFlipped)) * doorRot, ignoreMOID, Activity::NoTeam, 0, true, m_Skip); - if (foundMOID) { sensedActor = dynamic_cast(g_MovableMan.GetMOFromID(g_MovableMan.GetRootMOID(foundMOID))); } + if (foundMOID) { + sensedActor = dynamic_cast(g_MovableMan.GetMOFromID(g_MovableMan.GetRootMOID(foundMOID))); + } } } return sensedActor; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/ADSensor.h b/Source/Entities/ADSensor.h index c6ce4a6902..a92c1207c6 100644 --- a/Source/Entities/ADSensor.h +++ b/Source/Entities/ADSensor.h @@ -14,7 +14,6 @@ namespace RTE { class ADSensor : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -35,7 +34,7 @@ namespace RTE { /// /// A reference to the ADSensor to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const ADSensor &reference); + int Create(const ADSensor& reference); #pragma endregion #pragma region Destruction @@ -62,7 +61,7 @@ namespace RTE { /// Sets the starting position offset of this ADSensor from the owning ADoor position. /// /// The new starting coordinates relative to the m_Pos of this' ADoor. - void SetStartOffset(const Vector &startOffsetValue) { m_StartOffset = startOffsetValue; } + void SetStartOffset(const Vector& startOffsetValue) { m_StartOffset = startOffsetValue; } /// /// Gets the sensor ray vector out from the start offset's position. @@ -74,7 +73,7 @@ namespace RTE { /// Sets the sensor ray vector out from the start offset's position. /// /// The new sensor ray vector. - void SetSensorRay(const Vector &sensorRayValue) { m_SensorRay = sensorRayValue; } + void SetSensorRay(const Vector& sensorRayValue) { m_SensorRay = sensorRayValue; } #pragma endregion #pragma region Concrete Methods @@ -86,18 +85,16 @@ namespace RTE { /// Flipping of this ADSensor's ADoor. /// Which MOID to ignore, if any. /// The root Actor of the first MOID hit by the sensor ray. 0 if none. - Actor * SenseActor(const Vector &doorPos, const Matrix &doorRot, bool doorHFlipped = false, MOID ignoreMOID = g_NoMOID); + Actor* SenseActor(const Vector& doorPos, const Matrix& doorRot, bool doorHFlipped = false, MOID ignoreMOID = g_NoMOID); #pragma endregion protected: - Vector m_StartOffset; //!< The offset of the sensor ray start relative to the position of its ADoor. Vector m_SensorRay; //!< The ray out from the offset. - + short m_Skip; //!< How many pixels to skip between sensing pixels. private: - static const std::string c_ClassName; //!< A string with the friendly formatted type name of this object. /// @@ -105,5 +102,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/ADoor.cpp b/Source/Entities/ADoor.cpp index b2907ae848..57830895bc 100644 --- a/Source/Entities/ADoor.cpp +++ b/Source/Entities/ADoor.cpp @@ -12,7 +12,7 @@ namespace RTE { ConcreteClassInfo(ADoor, Actor, 20); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::Clear() { m_InitialSpriteAnimDuration = 0; @@ -53,18 +53,22 @@ namespace RTE { m_CanBeSquished = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADoor::Create(const ADoor &reference) { - if (reference.m_Door) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_Door->GetUniqueID()); } + int ADoor::Create(const ADoor& reference) { + if (reference.m_Door) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_Door->GetUniqueID()); + } Actor::Create(reference); - if (reference.m_Door) { SetDoor(dynamic_cast(reference.m_Door->Clone())); } + if (reference.m_Door) { + SetDoor(dynamic_cast(reference.m_Door->Clone())); + } m_InitialSpriteAnimDuration = reference.m_SpriteAnimDuration; - for (const ADSensor &sensor : reference.m_Sensors) { + for (const ADSensor& sensor: reference.m_Sensors) { m_Sensors.push_back(sensor); } m_SensorInterval = reference.m_SensorInterval; @@ -81,20 +85,28 @@ namespace RTE { m_DrawMaterialLayerWhenClosed = reference.m_DrawMaterialLayerWhenClosed; m_DoorMaterialID = reference.m_DoorMaterialID; - if (reference.m_DoorMoveStartSound) { m_DoorMoveStartSound.reset(dynamic_cast(reference.m_DoorMoveStartSound->Clone())); } - if (reference.m_DoorMoveSound) { m_DoorMoveSound.reset(dynamic_cast(reference.m_DoorMoveSound->Clone())); } - if (reference.m_DoorDirectionChangeSound) { m_DoorDirectionChangeSound.reset(dynamic_cast(reference.m_DoorDirectionChangeSound->Clone())); } - if (reference.m_DoorMoveEndSound) { m_DoorMoveEndSound.reset(dynamic_cast(reference.m_DoorMoveEndSound->Clone())); } + if (reference.m_DoorMoveStartSound) { + m_DoorMoveStartSound.reset(dynamic_cast(reference.m_DoorMoveStartSound->Clone())); + } + if (reference.m_DoorMoveSound) { + m_DoorMoveSound.reset(dynamic_cast(reference.m_DoorMoveSound->Clone())); + } + if (reference.m_DoorDirectionChangeSound) { + m_DoorDirectionChangeSound.reset(dynamic_cast(reference.m_DoorDirectionChangeSound->Clone())); + } + if (reference.m_DoorMoveEndSound) { + m_DoorMoveEndSound.reset(dynamic_cast(reference.m_DoorMoveEndSound->Clone())); + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADoor::ReadProperty(const std::string_view &propName, Reader &reader) { + int ADoor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Actor::ReadProperty(propName, reader)); - - MatchProperty("Door", { SetDoor(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + + MatchProperty("Door", { SetDoor(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); MatchProperty("OpenOffset", { reader >> m_OpenOffset; }); MatchProperty("ClosedOffset", { reader >> m_ClosedOffset; }); MatchProperty("OpenClosedOffset", { @@ -105,13 +117,17 @@ namespace RTE { Matrix rotation; reader >> rotation; m_OpenAngle = rotation.GetRadAngle(); - if (m_OpenAngle < 0) { reader.ReportError("Door OpenAngle cannot be less than 0."); } + if (m_OpenAngle < 0) { + reader.ReportError("Door OpenAngle cannot be less than 0."); + } }); MatchProperty("ClosedAngle", { Matrix rotation; reader >> rotation; m_ClosedAngle = rotation.GetRadAngle(); - if (m_ClosedAngle < 0) { reader.ReportError("Door ClosedAngle cannot be less than 0."); } + if (m_ClosedAngle < 0) { + reader.ReportError("Door ClosedAngle cannot be less than 0."); + } }); MatchProperty("OpenClosedAngle", { Matrix rotation; @@ -130,18 +146,17 @@ namespace RTE { }); MatchProperty("DrawMaterialLayerWhenOpen", { reader >> m_DrawMaterialLayerWhenOpen; }); MatchProperty("DrawMaterialLayerWhenClosed", { reader >> m_DrawMaterialLayerWhenClosed; }); - MatchProperty("DoorMoveStartSound", { m_DoorMoveStartSound.reset(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("DoorMoveSound", { m_DoorMoveSound.reset(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("DoorDirectionChangeSound", { m_DoorDirectionChangeSound.reset(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("DoorMoveEndSound", { m_DoorMoveEndSound.reset(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - - + MatchProperty("DoorMoveStartSound", { m_DoorMoveStartSound.reset(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("DoorMoveSound", { m_DoorMoveSound.reset(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("DoorDirectionChangeSound", { m_DoorDirectionChangeSound.reset(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("DoorMoveEndSound", { m_DoorMoveEndSound.reset(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADoor::Save(Writer &writer) const { + int ADoor::Save(Writer& writer) const { Actor::Save(writer); writer.NewProperty("Door"); @@ -162,7 +177,7 @@ namespace RTE { writer << m_ResetToDefaultStateDelay; writer.NewProperty("SensorInterval"); writer << m_SensorInterval; - for (const ADSensor &sensor : m_Sensors) { + for (const ADSensor& sensor: m_Sensors) { writer.NewProperty("AddSensor"); writer << sensor; } @@ -182,16 +197,26 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::Destroy(bool notInherited) { - if (m_DoorMoveStartSound) { m_DoorMoveStartSound->Stop(); } - if (m_DoorMoveSound) { m_DoorMoveSound->Stop(); } - if (m_DoorDirectionChangeSound) { m_DoorDirectionChangeSound->Stop(); } - if (m_DoorMoveEndSound) { m_DoorMoveEndSound->Stop(); } - if (!notInherited) { Actor::Destroy(); } + if (m_DoorMoveStartSound) { + m_DoorMoveStartSound->Stop(); + } + if (m_DoorMoveSound) { + m_DoorMoveSound->Stop(); + } + if (m_DoorDirectionChangeSound) { + m_DoorDirectionChangeSound->Stop(); + } + if (m_DoorMoveEndSound) { + m_DoorMoveEndSound->Stop(); + } + if (!notInherited) { + Actor::Destroy(); + } - for (ADSensor &sensor : m_Sensors) { + for (ADSensor& sensor: m_Sensors) { sensor.Destroy(); } if (m_DoorMaterialDrawn) { @@ -200,40 +225,40 @@ namespace RTE { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::SetDoor(Attachable *newDoor) { + void ADoor::SetDoor(Attachable* newDoor) { if (m_DoorMaterialDrawn) { RTEAssert(m_Door, "Door material drawn without an m_Door! This should've been cleared when the door was!"); - EraseDoorMaterial(); + EraseDoorMaterial(); } - if (m_Door && m_Door->IsAttached()) { - RemoveAndDeleteAttachable(m_Door); + if (m_Door && m_Door->IsAttached()) { + RemoveAndDeleteAttachable(m_Door); } m_Door = newDoor; if (m_Door) { AddAttachable(m_Door); - m_HardcodedAttachableUniqueIDsAndSetters.insert({m_Door->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - dynamic_cast(parent)->SetDoor(attachable); - }}); + m_HardcodedAttachableUniqueIDsAndSetters.insert({m_Door->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + dynamic_cast(parent)->SetDoor(attachable); + }}); m_Door->SetInheritsRotAngle(false); m_DoorMaterialID = m_Door->GetMaterial()->GetIndex(); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::DrawDoorMaterial(bool disallowErasingMaterialBeforeDrawing, bool updateMaterialArea) { if (!m_Door || m_DoorMaterialTempErased || !g_SceneMan.GetTerrain() || !g_SceneMan.GetTerrain()->GetMaterialBitmap()) { return; } - if (!disallowErasingMaterialBeforeDrawing && m_DoorMaterialDrawn) { - EraseDoorMaterial(updateMaterialArea); + if (!disallowErasingMaterialBeforeDrawing && m_DoorMaterialDrawn) { + EraseDoorMaterial(updateMaterialArea); } m_Door->Draw(g_SceneMan.GetTerrain()->GetMaterialBitmap(), Vector(), g_DrawDoor, true); @@ -245,7 +270,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ADoor::EraseDoorMaterial(bool updateMaterialArea) { if (!g_SceneMan.GetTerrain() || !g_SceneMan.GetTerrain()->GetMaterialBitmap()) { @@ -261,8 +286,8 @@ namespace RTE { if (g_SceneMan.GetTerrMatter(fillX, fillY) != g_MaterialAir) { floodfill(g_SceneMan.GetTerrain()->GetMaterialBitmap(), fillX, fillY, g_MaterialAir); - if (m_Door && updateMaterialArea) { - g_SceneMan.GetTerrain()->AddUpdatedMaterialArea(m_Door->GetBoundingBox()); + if (m_Door && updateMaterialArea) { + g_SceneMan.GetTerrain()->AddUpdatedMaterialArea(m_Door->GetBoundingBox()); } return true; @@ -270,7 +295,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::TempEraseOrRedrawDoorMaterial(bool erase) { if (!g_SceneMan.GetTerrain() || !g_SceneMan.GetTerrain()->GetMaterialBitmap()) { @@ -289,9 +314,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::GibThis(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { + void ADoor::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { if (m_Door && m_Door->IsAttached()) { EraseDoorMaterial(); m_Door->DeepCheck(true); @@ -300,7 +325,7 @@ namespace RTE { Actor::GibThis(impactImpulse, movableObjectToIgnore); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::CorrectAttachableAndWoundPositionsAndRotations() const { if (m_Door) { @@ -310,7 +335,7 @@ namespace RTE { MOSRotating::CorrectAttachableAndWoundPositionsAndRotations(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::OpenDoor() { if (m_DoorState == STOPPED) { @@ -325,7 +350,7 @@ namespace RTE { m_ResetToDefaultStateTimer.Reset(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::CloseDoor() { if (m_DoorState == STOPPED) { @@ -340,59 +365,79 @@ namespace RTE { m_ResetToDefaultStateTimer.Reset(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::StopDoor() { if (m_DoorState == OPENING || m_DoorState == CLOSING) { - if (m_DrawMaterialLayerWhenOpen || m_DrawMaterialLayerWhenClosed) { DrawDoorMaterial(true); } + if (m_DrawMaterialLayerWhenOpen || m_DrawMaterialLayerWhenClosed) { + DrawDoorMaterial(true); + } m_DoorMoveStopTime = m_DoorMoveTime - m_DoorMoveTimer.GetElapsedSimTimeMS(); m_DoorStateOnStop = m_DoorState; - if (m_DoorMoveSound) { m_DoorMoveSound->Stop(); } - if (m_DoorMoveEndSound) { m_DoorMoveEndSound->Play(m_Pos); } + if (m_DoorMoveSound) { + m_DoorMoveSound->Stop(); + } + if (m_DoorMoveEndSound) { + m_DoorMoveEndSound->Play(m_Pos); + } m_DoorState = STOPPED; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::SharedDoorControls() { if (m_DoorState == OPEN || m_DoorState == CLOSED) { - if (m_DoorMoveStartSound) { m_DoorMoveStartSound->Play(m_Pos); } + if (m_DoorMoveStartSound) { + m_DoorMoveStartSound->Play(m_Pos); + } m_DoorMoveTimer.Reset(); - if (m_DoorMaterialDrawn) { EraseDoorMaterial(); } - if (m_Door) { m_Door->DeepCheck(true); } + if (m_DoorMaterialDrawn) { + EraseDoorMaterial(); + } + if (m_Door) { + m_Door->DeepCheck(true); + } } else if (m_DoorState == OPENING || m_DoorState == CLOSING) { - if (m_DoorMoveSound) { m_DoorMoveSound->Stop(); } + if (m_DoorMoveSound) { + m_DoorMoveSound->Stop(); + } if (!m_ResumeAfterStop) { - if (m_DoorDirectionChangeSound) { m_DoorDirectionChangeSound->Play(m_Pos); } + if (m_DoorDirectionChangeSound) { + m_DoorDirectionChangeSound->Play(m_Pos); + } m_DoorMoveTimer.SetElapsedSimTimeMS(m_DoorMoveTime - m_DoorMoveTimer.GetElapsedSimTimeMS()); } else { - if (m_DoorMoveStartSound) { m_DoorMoveStartSound->Play(m_Pos); } + if (m_DoorMoveStartSound) { + m_DoorMoveStartSound->Play(m_Pos); + } m_DoorMoveTimer.SetElapsedSimTimeMS(m_ChangedDirectionAfterStop ? m_DoorMoveTime - m_DoorMoveStopTime : m_DoorMoveStopTime); m_ChangedDirectionAfterStop = false; m_ResumeAfterStop = false; } } else if (m_DoorState == STOPPED) { - if (m_DoorMaterialDrawn) { EraseDoorMaterial(); } + if (m_DoorMaterialDrawn) { + EraseDoorMaterial(); + } m_ResumeAfterStop = true; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::Update() { ZoneScoped; if (m_Door) { - if (m_DoorState != STOPPED && m_Status != Actor::Status::INACTIVE && m_SensorTimer.IsPastSimMS(m_SensorInterval)) { - UpdateSensors(); + if (m_DoorState != STOPPED && m_Status != Actor::Status::INACTIVE && m_SensorTimer.IsPastSimMS(m_SensorInterval)) { + UpdateSensors(); } UpdateDoorAttachableActions(); bool shouldDrawDoorMaterial = ((m_DrawMaterialLayerWhenOpen && m_DoorState == OPEN) || - (m_DrawMaterialLayerWhenClosed && m_DoorState == CLOSED) || - ((m_DrawMaterialLayerWhenOpen || m_DrawMaterialLayerWhenClosed) && m_DoorState == STOPPED)) && - m_DoorMaterialRedrawTimer.IsPastSimTimeLimit(); + (m_DrawMaterialLayerWhenClosed && m_DoorState == CLOSED) || + ((m_DrawMaterialLayerWhenOpen || m_DrawMaterialLayerWhenClosed) && m_DoorState == STOPPED)) && + m_DoorMaterialRedrawTimer.IsPastSimTimeLimit(); if (shouldDrawDoorMaterial) { DrawDoorMaterial(true); m_DoorMaterialRedrawTimer.Reset(); @@ -402,8 +447,8 @@ namespace RTE { Actor::Update(); // Start the spinning out of control animation for the motor, start it slow - if (!m_Door) { - m_SpriteAnimDuration *= 4; + if (!m_Door) { + m_SpriteAnimDuration *= 4; } if (m_SpriteAnimMode == LOOPWHENOPENCLOSE && m_FrameCount > 1 && (m_DoorState == OPENING || m_DoorState == CLOSING) && m_SpriteAnimTimer.IsPastSimMS(m_SpriteAnimDuration)) { @@ -417,25 +462,27 @@ namespace RTE { m_SpriteAnimDuration = static_cast(LERP(0, m_MaxHealth, 10.0F, static_cast(m_InitialSpriteAnimDuration), m_Health)); if (m_DoorMoveSound) { - if (!m_DoorMoveSound->IsBeingPlayed()) { m_DoorMoveSound->Play(m_Pos); } + if (!m_DoorMoveSound->IsBeingPlayed()) { + m_DoorMoveSound->Play(m_Pos); + } m_DoorMoveSound->SetPitch(LERP(10.0F, static_cast(m_InitialSpriteAnimDuration), 2.0F, 1.0F, static_cast(m_SpriteAnimDuration))); } m_Health -= 0.4F; } - if (m_Status == DEAD) { - GibThis(); + if (m_Status == DEAD) { + GibThis(); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::UpdateSensors() { - const Actor *foundActor = nullptr; + void ADoor::UpdateSensors() { + const Actor* foundActor = nullptr; bool anySensorInput = false; - for (ADSensor &sensor : m_Sensors) { + for (ADSensor& sensor: m_Sensors) { foundActor = sensor.SenseActor(m_Pos, m_Rotation, m_HFlipped, m_MOID); if (foundActor && foundActor->IsControllable()) { anySensorInput = true; @@ -443,11 +490,11 @@ namespace RTE { if (m_Team == Activity::NoTeam) { OpenDoor(); break; - // If a sensor has found an enemy Actor, close the door and stop looking, so we don't accidentally open it for a friendly Actor. + // If a sensor has found an enemy Actor, close the door and stop looking, so we don't accidentally open it for a friendly Actor. } else if (foundActor->GetTeam() != m_Team) { CloseDoor(); break; - } else if (foundActor->GetTeam() == m_Team) { + } else if (foundActor->GetTeam() == m_Team) { OpenDoor(); } } @@ -462,7 +509,7 @@ namespace RTE { m_SensorTimer.Reset(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::UpdateDoorAttachableActions() { Vector startOffset; @@ -486,7 +533,9 @@ namespace RTE { m_Door->SetParentOffset(endOffset); m_Door->SetRotAngle(m_Rotation.GetRadAngle() + (endAngle * GetFlipFactor())); } else if (m_DoorState == OPENING || m_DoorState == CLOSING) { - if (m_DoorMoveSound && !m_DoorMoveSound->IsBeingPlayed()) { m_DoorMoveSound->Play(m_Pos); } + if (m_DoorMoveSound && !m_DoorMoveSound->IsBeingPlayed()) { + m_DoorMoveSound->Play(m_Pos); + } if (m_DoorMoveTimer.IsPastSimMS(m_DoorMoveTime)) { m_ResetToDefaultStateTimer.Reset(); @@ -494,23 +543,27 @@ namespace RTE { m_Door->SetParentOffset(endOffset); m_Door->SetRotAngle(m_Rotation.GetRadAngle() + (endAngle * GetFlipFactor())); - if (m_DoorMoveSound) { m_DoorMoveSound->Stop(); } - if (m_DoorMoveEndSound) { m_DoorMoveEndSound->Play(m_Pos); } + if (m_DoorMoveSound) { + m_DoorMoveSound->Stop(); + } + if (m_DoorMoveEndSound) { + m_DoorMoveEndSound->Play(m_Pos); + } if (m_DoorState == OPENING) { - if (m_DrawMaterialLayerWhenOpen) { - DrawDoorMaterial(); + if (m_DrawMaterialLayerWhenOpen) { + DrawDoorMaterial(); } m_DoorState = OPEN; } else if (m_DoorState == CLOSING) { - if (m_DrawMaterialLayerWhenClosed) { - DrawDoorMaterial(); + if (m_DrawMaterialLayerWhenClosed) { + DrawDoorMaterial(); } m_DoorState = CLOSED; } } else { Vector updatedOffset(LERP(0, m_DoorMoveTime, startOffset.m_X, endOffset.m_X, m_DoorMoveTimer.GetElapsedSimTimeMS()), LERP(0, m_DoorMoveTime, startOffset.m_Y, endOffset.m_Y, m_DoorMoveTimer.GetElapsedSimTimeMS())); - + // TODO: Make this work across rotation 0. Probably the best solution would be to setup an angle LERP that properly handles the 2PI border and +- angles. // TODO_MULTITHREAD: multithread branch has lerped rotation, so once that's done! float updatedAngle = LERP(0, m_DoorMoveTime, startAngle, endAngle, m_DoorMoveTimer.GetElapsedSimTimeMS()); @@ -519,16 +572,16 @@ namespace RTE { m_Door->SetRotAngle(m_Rotation.GetRadAngle() + (updatedAngle * GetFlipFactor())); // Clear away any terrain debris when the door is moving but only after a short delay so it doesn't take a chunk out of the ground. - if (m_DoorMoveTimer.IsPastSimMS(50)) { - m_Door->DeepCheck(true); + if (m_DoorMoveTimer.IsPastSimMS(50)) { + m_Door->DeepCheck(true); } } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::DrawHUD(BITMAP *targetBitmap, const Vector &targetPos, int whichScreen, bool playerControlled) { + void ADoor::DrawHUD(BITMAP* targetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { m_HUDStack = -static_cast(m_CharHeight) / 2; if (!m_HUDVisible) { @@ -540,4 +593,4 @@ namespace RTE { return; } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/ADoor.h b/Source/Entities/ADoor.h index d7db3766aa..fe1c4f21b3 100644 --- a/Source/Entities/ADoor.h +++ b/Source/Entities/ADoor.h @@ -14,7 +14,6 @@ namespace RTE { class ADoor : public Actor { public: - EntityAllocation(ADoor); SerializableOverrideMethods; ClassInfoGetters; @@ -45,7 +44,7 @@ namespace RTE { /// /// A reference to the ADoor to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const ADoor &reference); + int Create(const ADoor& reference); #pragma endregion #pragma region Destruction @@ -63,7 +62,10 @@ namespace RTE { /// /// Resets the entire ADoor, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Actor::Reset(); } + void Reset() override { + Clear(); + Actor::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -71,13 +73,13 @@ namespace RTE { /// Gets the moving door Attachable of this ADoor /// /// A pointer to the door Attachable of this. Ownership is NOT transferred! - Attachable * GetDoor() const { return m_Door; } + Attachable* GetDoor() const { return m_Door; } /// /// Sets the moving door Attachable for this ADoor. /// /// The new moving door attachable to use. - void SetDoor(Attachable *newDoor); + void SetDoor(Attachable* newDoor); /// /// Gets the current state of the door. @@ -107,49 +109,49 @@ namespace RTE { /// Gets this ADoor's door move start sound. Ownership is NOT transferred! /// /// The SoundContainer for this ADoor's door move start sound. - SoundContainer * GetDoorMoveStartSound() const { return m_DoorMoveStartSound.get(); } + SoundContainer* GetDoorMoveStartSound() const { return m_DoorMoveStartSound.get(); } /// /// Sets this ADoor's door move start sound. Ownership IS transferred! /// /// The new SoundContainer for this ADoor's door move start sound. - void SetDoorMoveStartSound(SoundContainer *newSound) { m_DoorMoveStartSound.reset(newSound); } + void SetDoorMoveStartSound(SoundContainer* newSound) { m_DoorMoveStartSound.reset(newSound); } /// /// Gets this ADoor's door move sound. Ownership is NOT transferred! /// /// The SoundContainer for this ADoor's door move sound. - SoundContainer * GetDoorMoveSound() const { return m_DoorMoveSound.get(); } + SoundContainer* GetDoorMoveSound() const { return m_DoorMoveSound.get(); } /// /// Sets this ADoor's door move sound. Ownership IS transferred! /// /// The new SoundContainer for this ADoor's door move sound. - void SetDoorMoveSound(SoundContainer *newSound) { m_DoorMoveSound.reset(newSound); } + void SetDoorMoveSound(SoundContainer* newSound) { m_DoorMoveSound.reset(newSound); } /// /// Gets this ADoor's door direction change sound. Ownership is NOT transferred! /// /// The SoundContainer for this ADoor's door direction change sound. - SoundContainer * GetDoorDirectionChangeSound() const { return m_DoorDirectionChangeSound.get(); } + SoundContainer* GetDoorDirectionChangeSound() const { return m_DoorDirectionChangeSound.get(); } /// /// Sets this ADoor's door direction change sound. Ownership IS transferred! /// /// The new SoundContainer for this ADoor's door direction change sound. - void SetDoorDirectionChangeSound(SoundContainer *newSound) { m_DoorDirectionChangeSound.reset(newSound); } + void SetDoorDirectionChangeSound(SoundContainer* newSound) { m_DoorDirectionChangeSound.reset(newSound); } /// /// Gets this ADoor's door move end sound. Ownership is NOT transferred! /// /// The SoundContainer for this ADoor's door move end sound. - SoundContainer * GetDoorMoveEndSound() const { return m_DoorMoveEndSound.get(); } + SoundContainer* GetDoorMoveEndSound() const { return m_DoorMoveEndSound.get(); } /// /// Sets this ADoor's door move end sound. Ownership IS transferred! /// /// The new SoundContainer for this ADoor's door move end sound. - void SetDoorMoveEndSound(SoundContainer *newSound) { m_DoorMoveEndSound.reset(newSound); } + void SetDoorMoveEndSound(SoundContainer* newSound) { m_DoorMoveEndSound.reset(newSound); } #pragma endregion #pragma region Concrete Methods @@ -187,7 +189,7 @@ namespace RTE { /// /// The impulse (kg * m/s) of the impact causing the gibbing to happen. /// A pointer to an MO which the Gibs and Attachables should not be colliding with. - void GibThis(const Vector &impactImpulse = Vector(), MovableObject *movableObjectToIgnore = nullptr) override; + void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr) override; /// /// Ensures all attachables and wounds are positioned and rotated correctly. Must be run when this ADoor is added to MovableMan to avoid issues with Attachables spawning in at (0, 0). @@ -206,11 +208,10 @@ namespace RTE { /// The absolute position of the target bitmap's upper left corner in the Scene. /// Which player's screen this is being drawn to. May affect what HUD elements get drawn etc. /// Whether or not this MovableObject is currently player controlled (not applicable for ADoor) - void DrawHUD(BITMAP *targetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; + void DrawHUD(BITMAP* targetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. int m_InitialSpriteAnimDuration; //!< This stores the original SpriteAnimDuration value so we can drive the death spin-up animation using LERP. For internal use only. @@ -219,7 +220,7 @@ namespace RTE { Timer m_SensorTimer; //!< Times the exit interval. long m_SensorInterval; //!< The delay between each sensing pass in ms. - Attachable *m_Door; //!< Actual door module that moves. Owned by this. + Attachable* m_Door; //!< Actual door module that moves. Owned by this. DoorState m_DoorState; //!< Current door action state. DoorState m_DoorStateOnStop; //!< The state this door was in when it was stopped. For internal use only. @@ -238,7 +239,7 @@ namespace RTE { bool m_ResumeAfterStop; //!< Whether the door is starting movement after being forced stopped. For internal use only. bool m_ChangedDirectionAfterStop; //!< Whether the door changed directions while moving between states. For internal use only. double m_DoorMoveStopTime; //!< The elapsed time of m_DoorMoveTimer when the door was forced stopped. For internal use only. - + Timer m_ResetToDefaultStateTimer; //!< Timer for the resetting to the default state. int m_ResetToDefaultStateDelay; //!< How long the door stays in the non-default state before returning to the default state. @@ -253,11 +254,10 @@ namespace RTE { std::unique_ptr m_DoorMoveStartSound; //!< Sound played when the door starts moving from fully open/closed position towards the opposite end. std::unique_ptr m_DoorMoveSound; //!< Sound played while the door is moving between open/closed position. - std::unique_ptr m_DoorDirectionChangeSound; //!< Sound played when the door is interrupted while moving and changes directions. + std::unique_ptr m_DoorDirectionChangeSound; //!< Sound played when the door is interrupted while moving and changes directions. std::unique_ptr m_DoorMoveEndSound; //!< Sound played when the door stops moving and is at fully open/closed position. private: - #pragma region Update Breakdown /// /// Iterates through the sensor list looking for actors and acts accordingly. Resets to the default state if none are found and past the delay timer. This is called from Update(). @@ -296,8 +296,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - ADoor(const ADoor &reference) = delete; - ADoor & operator=(const ADoor &rhs) = delete; + ADoor(const ADoor& reference) = delete; + ADoor& operator=(const ADoor& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/AEJetpack.cpp b/Source/Entities/AEJetpack.cpp index 6a67efca10..a64890c8f5 100644 --- a/Source/Entities/AEJetpack.cpp +++ b/Source/Entities/AEJetpack.cpp @@ -5,58 +5,58 @@ namespace RTE { - ConcreteClassInfo(AEJetpack, AEmitter, 20); + ConcreteClassInfo(AEJetpack, AEmitter, 20); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AEJetpack::Clear() { m_JetpackType = JetpackType::Standard; m_JetTimeTotal = 0.0F; - m_JetTimeLeft = 0.0F; + m_JetTimeLeft = 0.0F; m_JetThrustBonusMultiplier = 1.0F; - m_JetReplenishRate = 1.0F; + m_JetReplenishRate = 1.0F; m_MinimumFuelRatio = 0.0F; - m_JetAngleRange = 0.25F; + m_JetAngleRange = 0.25F; m_CanAdjustAngleWhileFiring = true; m_AdjustsThrottleForWeight = true; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AEJetpack::Create() { - if (Attachable::Create() < 0) { - return -1; - } + int AEJetpack::Create() { + if (Attachable::Create() < 0) { + return -1; + } - // Initalize the jump time left - m_JetTimeLeft = m_JetTimeTotal; + // Initalize the jump time left + m_JetTimeLeft = m_JetTimeTotal; EnableEmission(false); return 0; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AEJetpack::Create(const AEJetpack &reference) { - AEmitter::Create(reference); + int AEJetpack::Create(const AEJetpack& reference) { + AEmitter::Create(reference); m_JetpackType = reference.m_JetpackType; - m_JetTimeTotal = reference.m_JetTimeTotal; - m_JetTimeLeft = reference.m_JetTimeLeft; - m_JetReplenishRate = reference.m_JetReplenishRate; + m_JetTimeTotal = reference.m_JetTimeTotal; + m_JetTimeLeft = reference.m_JetTimeLeft; + m_JetReplenishRate = reference.m_JetReplenishRate; m_MinimumFuelRatio = reference.m_MinimumFuelRatio; - m_JetAngleRange = reference.m_JetAngleRange; + m_JetAngleRange = reference.m_JetAngleRange; m_CanAdjustAngleWhileFiring = reference.m_CanAdjustAngleWhileFiring; m_AdjustsThrottleForWeight = reference.m_AdjustsThrottleForWeight; return 0; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int AEJetpack::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return AEmitter::ReadProperty(propName, reader)); - + MatchProperty("JetpackType", { std::string jetpackType; reader >> jetpackType; @@ -70,14 +70,14 @@ namespace RTE { } }); MatchForwards("JumpTime") MatchProperty("JetTime", { - reader >> m_JetTimeTotal; - m_JetTimeTotal *= 1000.0f; // Convert to ms - }); + reader >> m_JetTimeTotal; + m_JetTimeTotal *= 1000.0f; // Convert to ms + }); MatchForwards("JumpReplenishRate") MatchProperty("JetReplenishRate", { reader >> m_JetReplenishRate; }); MatchProperty("MinimumFuelRatio", { reader >> m_MinimumFuelRatio; m_MinimumFuelRatio = std::clamp(m_MinimumFuelRatio, 0.0F, 1.0F); - }); + }); MatchForwards("JumpAngleRange") MatchProperty("JetAngleRange", { reader >> m_JetAngleRange; }); MatchProperty("CanAdjustAngleWhileFiring", { reader >> m_CanAdjustAngleWhileFiring; }); MatchProperty("AdjustsThrottleForWeight", { reader >> m_AdjustsThrottleForWeight; }); @@ -85,20 +85,20 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int AEJetpack::Save(Writer& writer) const { AEmitter::Save(writer); writer.NewProperty("JetpackType"); switch (m_JetpackType) { - default: - case JetpackType::Standard: - writer << "Standard"; - break; - case JetpackType::JumpPack: - writer << "JumpPack"; - break; + default: + case JetpackType::Standard: + writer << "Standard"; + break; + case JetpackType::JumpPack: + writer << "JumpPack"; + break; } writer.NewPropertyWithValue("JumpTime", m_JetTimeTotal / 1000.0f); // Convert to seconds @@ -111,10 +111,10 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AEJetpack::UpdateBurstState(Actor &parentActor) { - const Controller &controller = *parentActor.GetController(); + void AEJetpack::UpdateBurstState(Actor& parentActor) { + const Controller& controller = *parentActor.GetController(); if (parentActor.GetStatus() == Actor::INACTIVE) { Recharge(parentActor); @@ -140,27 +140,26 @@ namespace RTE { // So enforce a minimum time we must be able to thrust for (in ms) const float minimumTimeToBeginThrusting = 250.0f * fuelUseMultiplier; if (m_JetTimeLeft > minimumTimeToBeginThrusting || wasEmittingLastFrame || IsFullyFueled()) { - switch (m_JetpackType) - { - case JetpackType::Standard: - if (controller.IsState(BODY_JUMPSTART) && !IsOutOfFuel()) { - Burst(parentActor, fuelUseMultiplier); - } else if (controller.IsState(BODY_JUMP) && !IsOutOfFuel() && (GetJetTimeRatio() >= m_MinimumFuelRatio || wasEmittingLastFrame)) { - Thrust(parentActor, fuelUseMultiplier); - } else { - Recharge(parentActor); - } - break; - - case JetpackType::JumpPack: - if (wasEmittingLastFrame && !IsOutOfFuel()) { - Thrust(parentActor, fuelUseMultiplier); - } else if (controller.IsState(BODY_JUMPSTART) && GetJetTimeRatio() >= m_MinimumFuelRatio) { - Burst(parentActor, fuelUseMultiplier); - } else { - Recharge(parentActor); - } - break; + switch (m_JetpackType) { + case JetpackType::Standard: + if (controller.IsState(BODY_JUMPSTART) && !IsOutOfFuel()) { + Burst(parentActor, fuelUseMultiplier); + } else if (controller.IsState(BODY_JUMP) && !IsOutOfFuel() && (GetJetTimeRatio() >= m_MinimumFuelRatio || wasEmittingLastFrame)) { + Thrust(parentActor, fuelUseMultiplier); + } else { + Recharge(parentActor); + } + break; + + case JetpackType::JumpPack: + if (wasEmittingLastFrame && !IsOutOfFuel()) { + Thrust(parentActor, fuelUseMultiplier); + } else if (controller.IsState(BODY_JUMPSTART) && GetJetTimeRatio() >= m_MinimumFuelRatio) { + Burst(parentActor, fuelUseMultiplier); + } else { + Recharge(parentActor); + } + break; } } else { Recharge(parentActor); @@ -173,7 +172,7 @@ namespace RTE { if (canAdjustAngle) { // Direct the jetpack nozzle according to either analog stick input or aim angle. float maxAngle = c_HalfPI * m_JetAngleRange; - const float analogDeadzone = 0.1F; + const float analogDeadzone = 0.1F; if (controller.GetAnalogMove().MagnitudeIsGreaterThan(analogDeadzone)) { float jetAngle = std::clamp(controller.GetAnalogMove().GetAbsRadAngle() - c_HalfPI, -maxAngle, maxAngle); SetEmitAngle(parentActor.FacingAngle(jetAngle - c_HalfPI)); @@ -181,20 +180,20 @@ namespace RTE { // Thrust in the opposite direction when strafing. float flip = ((parentActor.IsHFlipped() && controller.IsState(MOVE_RIGHT)) || (!parentActor.IsHFlipped() && controller.IsState(MOVE_LEFT))) ? -1.0F : 1.0F; // Halve the jet angle when looking downwards so the actor isn't forced to go sideways - // TODO: don't hardcode this ratio? - float aimAngle = parentActor.GetAimAngle(false); + // TODO: don't hardcode this ratio? + float aimAngle = parentActor.GetAimAngle(false); float jetAngle = (aimAngle > 0 ? aimAngle * m_JetAngleRange : -aimAngle * m_JetAngleRange * 0.5F) - maxAngle; // FacingAngle isn't needed because it's already been applied to AimAngle since last update. SetEmitAngle(jetAngle * flip - c_HalfPI); } } - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AEJetpack::Burst(Actor& parentActor, float fuelUseMultiplier) { parentActor.SetMovementState(Actor::JUMP); - + // TODO - find a better solution! This stops the actor getting stuck, but it's awful... parentActor.ForceDeepCheck(); @@ -207,7 +206,7 @@ namespace RTE { m_JetTimeLeft -= fuelUsage; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AEJetpack::Thrust(Actor& parentActor, float fuelUseMultiplier) { parentActor.SetMovementState(Actor::JUMP); @@ -219,7 +218,7 @@ namespace RTE { m_JetTimeLeft -= fuelUsage; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AEJetpack::Recharge(Actor& parentActor) { EnableEmission(false); @@ -229,4 +228,4 @@ namespace RTE { m_JetTimeLeft += g_TimerMan.GetDeltaTimeMS() * m_JetReplenishRate; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/AEJetpack.h b/Source/Entities/AEJetpack.h index 0db1c07915..722704fa7e 100644 --- a/Source/Entities/AEJetpack.h +++ b/Source/Entities/AEJetpack.h @@ -3,221 +3,222 @@ #include "AEmitter.h" -namespace RTE -{ - - /// - /// A jetpack MO, which can be used to generate thrust - /// - class AEJetpack : public AEmitter { - friend struct EntityLuaBindings; - - public: - - // Concrete allocation and cloning definitions - EntityAllocation(AEJetpack); - SerializableOverrideMethods; - ClassInfoGetters; - - enum JetpackType { - Standard, // Can be intermittently tapped to produce small amounts of thrust - JumpPack // Spends all of it's fuel until empty, and cannot fire again until recharged - }; - - #pragma region Creation - /// - /// Constructor method used to instantiate a AEJetpack object in system memory. Create() should be called before using the object. - /// - AEJetpack() { Clear(); } - - /// - /// Makes the AEJetpack object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create() override; - - /// - /// Creates a AEJetpack to be identical to another, by deep copy. - /// - /// A reference to the AEJetpack to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const AEJetpack &reference); - #pragma endregion - - #pragma region Destruction - /// - /// Destructor method used to clean up a AEJetpack object before deletion from system memory. - /// - ~AEJetpack() override { Destroy(true); } - - /// - /// Resets the entire AEJetpack, including its inherited members, to their default settings or values. - /// - void Reset() override { Clear(); AEmitter::Reset(); } - #pragma endregion +namespace RTE { + + /// + /// A jetpack MO, which can be used to generate thrust + /// + class AEJetpack : public AEmitter { + friend struct EntityLuaBindings; + + public: + // Concrete allocation and cloning definitions + EntityAllocation(AEJetpack); + SerializableOverrideMethods; + ClassInfoGetters; + + enum JetpackType { + Standard, // Can be intermittently tapped to produce small amounts of thrust + JumpPack // Spends all of it's fuel until empty, and cannot fire again until recharged + }; + +#pragma region Creation + /// + /// Constructor method used to instantiate a AEJetpack object in system memory. Create() should be called before using the object. + /// + AEJetpack() { Clear(); } + + /// + /// Makes the AEJetpack object ready for use. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create() override; + + /// + /// Creates a AEJetpack to be identical to another, by deep copy. + /// + /// A reference to the AEJetpack to deep copy. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(const AEJetpack& reference); +#pragma endregion + +#pragma region Destruction + /// + /// Destructor method used to clean up a AEJetpack object before deletion from system memory. + /// + ~AEJetpack() override { Destroy(true); } + + /// + /// Resets the entire AEJetpack, including its inherited members, to their default settings or values. + /// + void Reset() override { + Clear(); + AEmitter::Reset(); + } +#pragma endregion /// /// Updates this AEJetpack from our parent actor. /// - void UpdateBurstState(Actor &parentActor); - - /// - /// Returns whether or not this jetpack is fully fueled. - /// - /// Whether or not this jetpack is fully fueled. - bool IsFullyFueled() const { return m_JetTimeLeft >= m_JetTimeTotal - (m_JetTimeTotal * std::numeric_limits::epsilon()); } - - /// - /// Returns whether or not this jetpack is out of fuel. - /// - /// Whether or not this jetpack is out of fuel. - bool IsOutOfFuel() const { return m_JetTimeLeft <= std::numeric_limits::epsilon(); } - - /// - /// Gets the amount of time this jetpack can fire when filled, in ms. - /// - /// The amount of time this jetpack can fire when it's at max. - float GetJetTimeTotal() const { return m_JetTimeTotal; } - - /// - /// Sets the amount of time this' jetpack can fire when filled, in ms. - /// - /// The amount of time this jetpack can fire when it's at max. - void SetJetTimeTotal(float newValue) { m_JetTimeTotal = newValue; } - - /// - /// Gets the amount of time this jetpack can still fire until out, in ms. - /// - /// The amount of time this jetpack can still fire before running out. - float GetJetTimeLeft() const { return m_JetTimeLeft; } - - /// - /// Sets the amount of time this' jetpack can still fire until out, in ms. - /// - /// The amount of time this' jetpack can still fire before running out. - void SetJetTimeLeft(float newValue) { m_JetTimeLeft = newValue < m_JetTimeTotal ? newValue : m_JetTimeTotal; } - - /// - /// Gets the ratio of jetpack time that is left. - /// - /// The ratio of jetpack time that is left. - float GetJetTimeRatio() { return m_JetTimeLeft / m_JetTimeTotal; } - - /// - /// Gets the rate at which this AHuman's jetpack is replenished during downtime. - /// - /// The rate at which the jetpack is replenished. - float GetJetReplenishRate() const { return m_JetReplenishRate; } - - /// - /// Sets the rate at which this AHuman's jetpack is replenished during downtime. - /// - /// The rate at which the jetpack is replenished. - void SetJetReplenishRate(float newValue) { m_JetReplenishRate = newValue; } - - /// - /// Gets the rate at which this AHuman's jetpack is replenished during downtime. - /// - /// The rate at which the jetpack is replenished. - float GetMinimumFuelRatio() const { return m_MinimumFuelRatio; } - - /// - /// Sets the rate at which this AHuman's jetpack is replenished during downtime. - /// - /// The rate at which the jetpack is replenished. - void SetMinimumFuelRatio(float newValue) { m_MinimumFuelRatio = newValue; } - - /// - /// Gets the scalar ratio at which this jetpack's thrust angle follows the aim angle of the user. - /// - /// The ratio at which this jetpack follows the aim angle of the user. - float GetJetAngleRange() const { return m_JetAngleRange; } - - /// - /// Sets the scalar ratio at which this jetpack's thrust angle follows the aim angle of the user. - /// - /// The ratio at which this jetpack follows the aim angle of the user. - void SetJetAngleRange(float newValue) { m_JetAngleRange = newValue; } - - /// - /// Gets the type of this jetpack. - /// - /// The type of this jetpack. - JetpackType GetJetpackType() const { return m_JetpackType; } - - /// - /// Sets the type of this jetpack. - /// - /// The new type of this jetpack. - void SetJetpackType(JetpackType newType) { m_JetpackType = newType; } - - /// - /// Returns whether the angle of this jetpack can adjust while firing, or if it can only be aimed while off. - /// - /// Whether the angle of this jetpack can adjust while firing. - bool GetCanAdjustAngleWhileFiring() const { return m_CanAdjustAngleWhileFiring; } - - /// - /// Sets whether the angle of this can adjust while firing, or if it can only be aimed while off. - /// - /// The new value for whether the angle of this jetpack can adjust while firing. - void SetCanAdjustAngleWhileFiring(bool newValue) { m_CanAdjustAngleWhileFiring = newValue; } - - /// - /// Returns whether this jetpack adjusts it's throttle to balance for extra weight. - /// - /// Whether this jetpack adjusts it's throttle to balance for extra weight. - bool GetAdjustsThrottleForWeight() const { return m_AdjustsThrottleForWeight; } - - /// - /// Sets whether this jetpack adjusts it's throttle to balance for extra weight. - /// - /// The new value for whether this jetpack adjusts it's throttle to balance for extra weight. - void SetAdjustsThrottleForWeight(bool newValue) { m_AdjustsThrottleForWeight = newValue; } - - protected: - static Entity::ClassInfo m_sClass; - - JetpackType m_JetpackType; //!< The type of jetpack - float m_JetTimeTotal; //!< The max total time, in ms, that the jetpack can be used without pause - float m_JetTimeLeft; //!< How much time left the jetpack can go, in ms - float m_JetThrustBonusMultiplier; //!< A multiplier bonus to our produced thrust, which doesn't cost extra fuel. Used for AI buffs. - float m_JetReplenishRate; //!< A multiplier affecting how fast the jetpack fuel will replenish when not in use. 1 means that jet time replenishes at 2x speed in relation to depletion. - float m_MinimumFuelRatio; // Minimum ratio of current fuel to max fuel to be able to initiate the jetpack. - float m_JetAngleRange; //!< Ratio at which the jetpack angle follows aim angle - bool m_CanAdjustAngleWhileFiring; //!< Whether or not the angle of the thrust can change while firing, or if it can only be adjusted while the jetpack is off - bool m_AdjustsThrottleForWeight; //!< Whether or not the jetpack throttle auto-adjusts for weight, at the cost of fuel usage. - - private: - /// - /// The logic to run when bursting. - /// - /// The parent actor using this jetpack. - /// The multiplier to fuel usage rate. - void Burst(Actor& parentActor, float fuelUseMultiplier); - - /// - /// The logic to run when thrusting. - /// - /// The parent actor using this jetpack. - /// The multiplier to fuel usage rate. - void Thrust(Actor& parentActor, float fuelUseMultiplier); - - /// - /// The logic to run when recharging. - /// - /// The parent actor using this jetpack. - void Recharge(Actor& parentActor); - - /// + void UpdateBurstState(Actor& parentActor); + + /// + /// Returns whether or not this jetpack is fully fueled. + /// + /// Whether or not this jetpack is fully fueled. + bool IsFullyFueled() const { return m_JetTimeLeft >= m_JetTimeTotal - (m_JetTimeTotal * std::numeric_limits::epsilon()); } + + /// + /// Returns whether or not this jetpack is out of fuel. + /// + /// Whether or not this jetpack is out of fuel. + bool IsOutOfFuel() const { return m_JetTimeLeft <= std::numeric_limits::epsilon(); } + + /// + /// Gets the amount of time this jetpack can fire when filled, in ms. + /// + /// The amount of time this jetpack can fire when it's at max. + float GetJetTimeTotal() const { return m_JetTimeTotal; } + + /// + /// Sets the amount of time this' jetpack can fire when filled, in ms. + /// + /// The amount of time this jetpack can fire when it's at max. + void SetJetTimeTotal(float newValue) { m_JetTimeTotal = newValue; } + + /// + /// Gets the amount of time this jetpack can still fire until out, in ms. + /// + /// The amount of time this jetpack can still fire before running out. + float GetJetTimeLeft() const { return m_JetTimeLeft; } + + /// + /// Sets the amount of time this' jetpack can still fire until out, in ms. + /// + /// The amount of time this' jetpack can still fire before running out. + void SetJetTimeLeft(float newValue) { m_JetTimeLeft = newValue < m_JetTimeTotal ? newValue : m_JetTimeTotal; } + + /// + /// Gets the ratio of jetpack time that is left. + /// + /// The ratio of jetpack time that is left. + float GetJetTimeRatio() { return m_JetTimeLeft / m_JetTimeTotal; } + + /// + /// Gets the rate at which this AHuman's jetpack is replenished during downtime. + /// + /// The rate at which the jetpack is replenished. + float GetJetReplenishRate() const { return m_JetReplenishRate; } + + /// + /// Sets the rate at which this AHuman's jetpack is replenished during downtime. + /// + /// The rate at which the jetpack is replenished. + void SetJetReplenishRate(float newValue) { m_JetReplenishRate = newValue; } + + /// + /// Gets the rate at which this AHuman's jetpack is replenished during downtime. + /// + /// The rate at which the jetpack is replenished. + float GetMinimumFuelRatio() const { return m_MinimumFuelRatio; } + + /// + /// Sets the rate at which this AHuman's jetpack is replenished during downtime. + /// + /// The rate at which the jetpack is replenished. + void SetMinimumFuelRatio(float newValue) { m_MinimumFuelRatio = newValue; } + + /// + /// Gets the scalar ratio at which this jetpack's thrust angle follows the aim angle of the user. + /// + /// The ratio at which this jetpack follows the aim angle of the user. + float GetJetAngleRange() const { return m_JetAngleRange; } + + /// + /// Sets the scalar ratio at which this jetpack's thrust angle follows the aim angle of the user. + /// + /// The ratio at which this jetpack follows the aim angle of the user. + void SetJetAngleRange(float newValue) { m_JetAngleRange = newValue; } + + /// + /// Gets the type of this jetpack. + /// + /// The type of this jetpack. + JetpackType GetJetpackType() const { return m_JetpackType; } + + /// + /// Sets the type of this jetpack. + /// + /// The new type of this jetpack. + void SetJetpackType(JetpackType newType) { m_JetpackType = newType; } + + /// + /// Returns whether the angle of this jetpack can adjust while firing, or if it can only be aimed while off. + /// + /// Whether the angle of this jetpack can adjust while firing. + bool GetCanAdjustAngleWhileFiring() const { return m_CanAdjustAngleWhileFiring; } + + /// + /// Sets whether the angle of this can adjust while firing, or if it can only be aimed while off. + /// + /// The new value for whether the angle of this jetpack can adjust while firing. + void SetCanAdjustAngleWhileFiring(bool newValue) { m_CanAdjustAngleWhileFiring = newValue; } + + /// + /// Returns whether this jetpack adjusts it's throttle to balance for extra weight. + /// + /// Whether this jetpack adjusts it's throttle to balance for extra weight. + bool GetAdjustsThrottleForWeight() const { return m_AdjustsThrottleForWeight; } + + /// + /// Sets whether this jetpack adjusts it's throttle to balance for extra weight. + /// + /// The new value for whether this jetpack adjusts it's throttle to balance for extra weight. + void SetAdjustsThrottleForWeight(bool newValue) { m_AdjustsThrottleForWeight = newValue; } + + protected: + static Entity::ClassInfo m_sClass; + + JetpackType m_JetpackType; //!< The type of jetpack + float m_JetTimeTotal; //!< The max total time, in ms, that the jetpack can be used without pause + float m_JetTimeLeft; //!< How much time left the jetpack can go, in ms + float m_JetThrustBonusMultiplier; //!< A multiplier bonus to our produced thrust, which doesn't cost extra fuel. Used for AI buffs. + float m_JetReplenishRate; //!< A multiplier affecting how fast the jetpack fuel will replenish when not in use. 1 means that jet time replenishes at 2x speed in relation to depletion. + float m_MinimumFuelRatio; // Minimum ratio of current fuel to max fuel to be able to initiate the jetpack. + float m_JetAngleRange; //!< Ratio at which the jetpack angle follows aim angle + bool m_CanAdjustAngleWhileFiring; //!< Whether or not the angle of the thrust can change while firing, or if it can only be adjusted while the jetpack is off + bool m_AdjustsThrottleForWeight; //!< Whether or not the jetpack throttle auto-adjusts for weight, at the cost of fuel usage. + + private: + /// + /// The logic to run when bursting. + /// + /// The parent actor using this jetpack. + /// The multiplier to fuel usage rate. + void Burst(Actor& parentActor, float fuelUseMultiplier); + + /// + /// The logic to run when thrusting. + /// + /// The parent actor using this jetpack. + /// The multiplier to fuel usage rate. + void Thrust(Actor& parentActor, float fuelUseMultiplier); + + /// + /// The logic to run when recharging. + /// + /// The parent actor using this jetpack. + void Recharge(Actor& parentActor); + + /// /// Clears all the member variables of this AEJetpack, effectively resetting the members of this abstraction level only. /// void Clear(); // Disallow the use of some implicit methods. - AEJetpack(const AEJetpack &reference) = delete; - AEJetpack & operator=(const AEJetpack &rhs) = delete; - }; -} + AEJetpack(const AEJetpack& reference) = delete; + AEJetpack& operator=(const AEJetpack& rhs) = delete; + }; +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/AEmitter.cpp b/Source/Entities/AEmitter.cpp index 4e93697d59..d4c018c221 100644 --- a/Source/Entities/AEmitter.cpp +++ b/Source/Entities/AEmitter.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,612 +17,621 @@ namespace RTE { -ConcreteClassInfo(AEmitter, Attachable, 100); + ConcreteClassInfo(AEmitter, Attachable, 100); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this AEmitter, effectively + // resetting the members of this abstraction level only. + + void AEmitter::Clear() { + m_EmissionList.clear(); + m_EmissionSound = nullptr; + m_BurstSound = nullptr; + m_EndSound = nullptr; + m_EmitEnabled = false; + m_WasEmitting = false; + m_EmitCount = 0; + m_EmitCountLimit = 0; + m_NegativeThrottleMultiplier = 1.0F; + m_PositiveThrottleMultiplier = 1.0F; + m_Throttle = 0; + m_EmissionsIgnoreThis = false; + m_BurstScale = 1.0F; + m_BurstDamage = 0; + m_EmitterDamageMultiplier = 1.0F; + m_BurstTriggered = false; + m_BurstSpacing = 0; + // Set this to really long so an initial burst will be possible + m_BurstTimer.SetElapsedSimTimeS(50000); + m_BurstTimer.SetElapsedRealTimeS(50000); + m_EmitAngle.Reset(); + m_EmissionOffset.Reset(); + m_EmitDamage = 0; + m_LastEmitTmr.Reset(); + m_pFlash = 0; + m_FlashScale = 1.0F; + m_AvgBurstImpulse = -1.0F; + m_AvgImpulse = -1.0F; + m_FlashOnlyOnBurst = true; + m_SustainBurstSound = false; + m_BurstSoundFollowsEmitter = true; + m_LoudnessOnEmit = 1.0F; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AEmitter, effectively -// resetting the members of this abstraction level only. - -void AEmitter::Clear() -{ - m_EmissionList.clear(); - m_EmissionSound = nullptr; - m_BurstSound = nullptr; - m_EndSound = nullptr; - m_EmitEnabled = false; - m_WasEmitting = false; - m_EmitCount = 0; - m_EmitCountLimit = 0; - m_NegativeThrottleMultiplier = 1.0F; - m_PositiveThrottleMultiplier = 1.0F; - m_Throttle = 0; - m_EmissionsIgnoreThis = false; - m_BurstScale = 1.0F; - m_BurstDamage = 0; - m_EmitterDamageMultiplier = 1.0F; - m_BurstTriggered = false; - m_BurstSpacing = 0; - // Set this to really long so an initial burst will be possible - m_BurstTimer.SetElapsedSimTimeS(50000); - m_BurstTimer.SetElapsedRealTimeS(50000); - m_EmitAngle.Reset(); - m_EmissionOffset.Reset(); - m_EmitDamage = 0; - m_LastEmitTmr.Reset(); - m_pFlash = 0; - m_FlashScale = 1.0F; - m_AvgBurstImpulse = -1.0F; - m_AvgImpulse = -1.0F; - m_FlashOnlyOnBurst = true; - m_SustainBurstSound = false; - m_BurstSoundFollowsEmitter = true; - m_LoudnessOnEmit = 1.0F; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a AEmitter to be identical to another, by deep copy. + int AEmitter::Create(const AEmitter& reference) { + if (reference.m_pFlash) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFlash->GetUniqueID()); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a AEmitter to be identical to another, by deep copy. + Attachable::Create(reference); -int AEmitter::Create(const AEmitter &reference) { - if (reference.m_pFlash) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFlash->GetUniqueID()); } + if (reference.m_pFlash) { + SetFlash(dynamic_cast(reference.m_pFlash->Clone())); + } - Attachable::Create(reference); + for (auto itr = reference.m_EmissionList.begin(); itr != reference.m_EmissionList.end(); ++itr) { + m_EmissionList.push_back(*itr); + } + if (reference.m_EmissionSound) { + m_EmissionSound = dynamic_cast(reference.m_EmissionSound->Clone()); + } + if (reference.m_BurstSound) { + m_BurstSound = dynamic_cast(reference.m_BurstSound->Clone()); + } + if (reference.m_EndSound) { + m_EndSound = dynamic_cast(reference.m_EndSound->Clone()); + } + m_EmitEnabled = reference.m_EmitEnabled; + m_EmitCount = reference.m_EmitCount; + m_EmitCountLimit = reference.m_EmitCountLimit; + m_NegativeThrottleMultiplier = reference.m_NegativeThrottleMultiplier; + m_PositiveThrottleMultiplier = reference.m_PositiveThrottleMultiplier; + m_Throttle = reference.m_Throttle; + m_EmissionsIgnoreThis = reference.m_EmissionsIgnoreThis; + m_BurstScale = reference.m_BurstScale; + m_BurstDamage = reference.m_BurstDamage; + m_EmitterDamageMultiplier = reference.m_EmitterDamageMultiplier; + m_BurstSpacing = reference.m_BurstSpacing; + m_BurstTriggered = reference.m_BurstTriggered; + m_EmitAngle = reference.m_EmitAngle; + m_EmissionOffset = reference.m_EmissionOffset; + m_EmitDamage = reference.m_EmitDamage; + m_FlashScale = reference.m_FlashScale; + m_FlashOnlyOnBurst = reference.m_FlashOnlyOnBurst; + m_SustainBurstSound = reference.m_SustainBurstSound; + m_BurstSoundFollowsEmitter = reference.m_BurstSoundFollowsEmitter; + m_LoudnessOnEmit = reference.m_LoudnessOnEmit; + + return 0; + } - if (reference.m_pFlash) { SetFlash(dynamic_cast(reference.m_pFlash->Clone())); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int AEmitter::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Attachable::ReadProperty(propName, reader)); + + MatchProperty("AddEmission", { + Emission emission; + reader >> emission; + m_EmissionList.push_back(emission); + }); + MatchProperty("EmissionSound", { + m_EmissionSound = new SoundContainer; + reader >> m_EmissionSound; + }); + MatchProperty("BurstSound", { + m_BurstSound = new SoundContainer; + reader >> m_BurstSound; + }); + MatchProperty("EndSound", { + m_EndSound = new SoundContainer; + reader >> m_EndSound; + }); + MatchProperty("EmissionEnabled", { reader >> m_EmitEnabled; }); + MatchProperty("EmissionCount", { reader >> m_EmitCount; }); + MatchProperty("EmissionCountLimit", { reader >> m_EmitCountLimit; }); + MatchProperty("ParticlesPerMinute", { + float ppm; + reader >> ppm; + // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility. + for (Emission& emission: m_EmissionList) { + emission.m_PPM = ppm / static_cast(m_EmissionList.size()); + } + }); + MatchProperty("NegativeThrottleMultiplier", { reader >> m_NegativeThrottleMultiplier; }); + MatchProperty("PositiveThrottleMultiplier", { reader >> m_PositiveThrottleMultiplier; }); + MatchProperty("Throttle", { reader >> m_Throttle; }); + MatchProperty("EmissionsIgnoreThis", { reader >> m_EmissionsIgnoreThis; }); + MatchProperty("BurstSize", { + int burstSize; + reader >> burstSize; + // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility. + for (Emission& emission: m_EmissionList) { + emission.m_BurstSize = std::ceil(static_cast(burstSize) / static_cast(m_EmissionList.size())); + } + }); + MatchProperty("BurstScale", { reader >> m_BurstScale; }); + MatchProperty("BurstDamage", { reader >> m_BurstDamage; }); + MatchProperty("EmitterDamageMultiplier", { reader >> m_EmitterDamageMultiplier; }); + MatchProperty("BurstSpacing", { reader >> m_BurstSpacing; }); + MatchProperty("BurstTriggered", { reader >> m_BurstTriggered; }); + MatchProperty("EmissionAngle", { reader >> m_EmitAngle; }); + MatchProperty("EmissionOffset", { reader >> m_EmissionOffset; }); + MatchProperty("EmissionDamage", { reader >> m_EmitDamage; }); + MatchProperty("Flash", { SetFlash(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("FlashScale", { reader >> m_FlashScale; }); + MatchProperty("FlashOnlyOnBurst", { reader >> m_FlashOnlyOnBurst; }); + MatchProperty("SustainBurstSound", { reader >> m_SustainBurstSound; }); + MatchProperty("BurstSoundFollowsEmitter", { reader >> m_BurstSoundFollowsEmitter; }); + MatchProperty("LoudnessOnEmit", { reader >> m_LoudnessOnEmit; }); + + EndPropertyList; + } - for (auto itr = reference.m_EmissionList.begin(); itr != reference.m_EmissionList.end(); ++itr) { - m_EmissionList.push_back(*itr); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this AEmitter with a Writer for + // later recreation with Create(Reader &reader); + + int AEmitter::Save(Writer& writer) const { + Attachable::Save(writer); + + for (auto itr = m_EmissionList.begin(); itr != m_EmissionList.end(); ++itr) { + writer.NewProperty("AddEmission"); + writer << *itr; + } + writer.NewProperty("EmissionSound"); + writer << m_EmissionSound; + writer.NewProperty("BurstSound"); + writer << m_BurstSound; + writer.NewProperty("EndSound"); + writer << m_EndSound; + writer.NewProperty("EmissionEnabled"); + writer << m_EmitEnabled; + writer.NewProperty("EmissionCount"); + writer << m_EmitCount; + writer.NewProperty("EmissionCountLimit"); + writer << m_EmitCountLimit; + writer.NewProperty("EmissionsIgnoreThis"); + writer << m_EmissionsIgnoreThis; + writer.NewProperty("NegativeThrottleMultiplier"); + writer << m_NegativeThrottleMultiplier; + writer.NewProperty("PositiveThrottleMultiplier"); + writer << m_PositiveThrottleMultiplier; + writer.NewProperty("Throttle"); + writer << m_Throttle; + writer.NewProperty("BurstScale"); + writer << m_BurstScale; + writer.NewProperty("BurstDamage"); + writer << m_BurstDamage; + writer.NewProperty("EmitterDamageMultiplier"); + writer << m_EmitterDamageMultiplier; + writer.NewProperty("BurstSpacing"); + writer << m_BurstSpacing; + writer.NewProperty("BurstTriggered"); + writer << m_BurstTriggered; + writer.NewProperty("EmissionAngle"); + writer << m_EmitAngle; + writer.NewProperty("EmissionOffset"); + writer << m_EmissionOffset; + writer.NewProperty("EmissionDamage"); + writer << m_EmitDamage; + writer.NewProperty("Flash"); + writer << m_pFlash; + writer.NewProperty("FlashScale"); + writer << m_FlashScale; + writer.NewProperty("FlashOnlyOnBurst"); + writer << m_FlashOnlyOnBurst; + writer.NewProperty("SustainBurstSound"); + writer << m_SustainBurstSound; + writer.NewProperty("BurstSoundFollowsEmitter"); + writer << m_BurstSoundFollowsEmitter; + writer.NewProperty("LoudnessOnEmit"); + writer << m_LoudnessOnEmit; + + return 0; } - if (reference.m_EmissionSound) { m_EmissionSound = dynamic_cast(reference.m_EmissionSound->Clone()); } - if (reference.m_BurstSound) { m_BurstSound = dynamic_cast(reference.m_BurstSound->Clone()); } - if (reference.m_EndSound) { m_EndSound = dynamic_cast(reference.m_EndSound->Clone()); } - m_EmitEnabled = reference.m_EmitEnabled; - m_EmitCount = reference.m_EmitCount; - m_EmitCountLimit = reference.m_EmitCountLimit; - m_NegativeThrottleMultiplier = reference.m_NegativeThrottleMultiplier; - m_PositiveThrottleMultiplier = reference.m_PositiveThrottleMultiplier; - m_Throttle = reference.m_Throttle; - m_EmissionsIgnoreThis = reference.m_EmissionsIgnoreThis; - m_BurstScale = reference.m_BurstScale; - m_BurstDamage = reference.m_BurstDamage; - m_EmitterDamageMultiplier = reference.m_EmitterDamageMultiplier; - m_BurstSpacing = reference.m_BurstSpacing; - m_BurstTriggered = reference.m_BurstTriggered; - m_EmitAngle = reference.m_EmitAngle; - m_EmissionOffset = reference.m_EmissionOffset; - m_EmitDamage = reference.m_EmitDamage; - m_FlashScale = reference.m_FlashScale; - m_FlashOnlyOnBurst = reference.m_FlashOnlyOnBurst; - m_SustainBurstSound = reference.m_SustainBurstSound; - m_BurstSoundFollowsEmitter = reference.m_BurstSoundFollowsEmitter; - m_LoudnessOnEmit = reference.m_LoudnessOnEmit; - - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the AEmitter object. + + void AEmitter::Destroy(bool notInherited) { + // Stop playback of sounds gracefully + if (m_EmissionSound) { + if (m_EndSound) { + m_EmissionSound->IsBeingPlayed() ? m_EndSound->Play(m_Pos) : m_EndSound->Stop(); + } + m_EmissionSound->Stop(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int AEmitter::ReadProperty(const std::string_view &propName, Reader &reader) { - StartPropertyList(return Attachable::ReadProperty(propName, reader)); - - MatchProperty("AddEmission", { - Emission emission; - reader >> emission; - m_EmissionList.push_back(emission); - }); - MatchProperty("EmissionSound", { - m_EmissionSound = new SoundContainer; - reader >> m_EmissionSound; - }); - MatchProperty("BurstSound", { - m_BurstSound = new SoundContainer; - reader >> m_BurstSound; - }); - MatchProperty("EndSound", { - m_EndSound = new SoundContainer; - reader >> m_EndSound; - }); - MatchProperty("EmissionEnabled", { reader >> m_EmitEnabled; }); - MatchProperty("EmissionCount", { reader >> m_EmitCount; }); - MatchProperty("EmissionCountLimit", { reader >> m_EmitCountLimit; }); - MatchProperty("ParticlesPerMinute", { - float ppm; - reader >> ppm; - // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility. - for (Emission &emission : m_EmissionList) { emission.m_PPM = ppm / static_cast(m_EmissionList.size()); } - }); - MatchProperty("NegativeThrottleMultiplier", { reader >> m_NegativeThrottleMultiplier; }); - MatchProperty("PositiveThrottleMultiplier", { reader >> m_PositiveThrottleMultiplier; }); - MatchProperty("Throttle", { reader >> m_Throttle; }); - MatchProperty("EmissionsIgnoreThis", { reader >> m_EmissionsIgnoreThis; }); - MatchProperty("BurstSize", { - int burstSize; - reader >> burstSize; - // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility. - for (Emission &emission : m_EmissionList) { emission.m_BurstSize = std::ceil(static_cast(burstSize) / static_cast(m_EmissionList.size())); } - }); - MatchProperty("BurstScale", { reader >> m_BurstScale; }); - MatchProperty("BurstDamage", { reader >> m_BurstDamage; }); - MatchProperty("EmitterDamageMultiplier", { reader >> m_EmitterDamageMultiplier; }); - MatchProperty("BurstSpacing", { reader >> m_BurstSpacing; }); - MatchProperty("BurstTriggered", { reader >> m_BurstTriggered; }); - MatchProperty("EmissionAngle", { reader >> m_EmitAngle; }); - MatchProperty("EmissionOffset", { reader >> m_EmissionOffset; }); - MatchProperty("EmissionDamage", { reader >> m_EmitDamage; }); - MatchProperty("Flash", { SetFlash(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("FlashScale", { reader >> m_FlashScale; }); - MatchProperty("FlashOnlyOnBurst", { reader >> m_FlashOnlyOnBurst; }); - MatchProperty("SustainBurstSound", { reader >> m_SustainBurstSound; }); - MatchProperty("BurstSoundFollowsEmitter", { reader >> m_BurstSoundFollowsEmitter; }); - MatchProperty("LoudnessOnEmit", { reader >> m_LoudnessOnEmit; }); - - EndPropertyList; -} + delete m_EmissionSound; + delete m_BurstSound; + delete m_EndSound; + // m_BurstSound.Stop(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this AEmitter with a Writer for -// later recreation with Create(Reader &reader); - -int AEmitter::Save(Writer &writer) const -{ - Attachable::Save(writer); - - for (auto itr = m_EmissionList.begin(); itr != m_EmissionList.end(); ++itr) - { - writer.NewProperty("AddEmission"); - writer << *itr; - } - writer.NewProperty("EmissionSound"); - writer << m_EmissionSound; - writer.NewProperty("BurstSound"); - writer << m_BurstSound; - writer.NewProperty("EndSound"); - writer << m_EndSound; - writer.NewProperty("EmissionEnabled"); - writer << m_EmitEnabled; - writer.NewProperty("EmissionCount"); - writer << m_EmitCount; - writer.NewProperty("EmissionCountLimit"); - writer << m_EmitCountLimit; - writer.NewProperty("EmissionsIgnoreThis"); - writer << m_EmissionsIgnoreThis; - writer.NewProperty("NegativeThrottleMultiplier"); - writer << m_NegativeThrottleMultiplier; - writer.NewProperty("PositiveThrottleMultiplier"); - writer << m_PositiveThrottleMultiplier; - writer.NewProperty("Throttle"); - writer << m_Throttle; - writer.NewProperty("BurstScale"); - writer << m_BurstScale; - writer.NewProperty("BurstDamage"); - writer << m_BurstDamage; - writer.NewProperty("EmitterDamageMultiplier"); - writer << m_EmitterDamageMultiplier; - writer.NewProperty("BurstSpacing"); - writer << m_BurstSpacing; - writer.NewProperty("BurstTriggered"); - writer << m_BurstTriggered; - writer.NewProperty("EmissionAngle"); - writer << m_EmitAngle; - writer.NewProperty("EmissionOffset"); - writer << m_EmissionOffset; - writer.NewProperty("EmissionDamage"); - writer << m_EmitDamage; - writer.NewProperty("Flash"); - writer << m_pFlash; - writer.NewProperty("FlashScale"); - writer << m_FlashScale; - writer.NewProperty("FlashOnlyOnBurst"); - writer << m_FlashOnlyOnBurst; - writer.NewProperty("SustainBurstSound"); - writer << m_SustainBurstSound; - writer.NewProperty("BurstSoundFollowsEmitter"); - writer << m_BurstSoundFollowsEmitter; - writer.NewProperty("LoudnessOnEmit"); - writer << m_LoudnessOnEmit; - - return 0; -} + if (!notInherited) + Attachable::Destroy(); + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ResetEmissionTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reset the timers of all emissions so they will start/stop at the + // correct relative offsets from now. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AEmitter object. - -void AEmitter::Destroy(bool notInherited) -{ - // Stop playback of sounds gracefully - if (m_EmissionSound) { - if (m_EndSound) { m_EmissionSound->IsBeingPlayed() ? m_EndSound->Play(m_Pos) : m_EndSound->Stop(); } - m_EmissionSound->Stop(); + void AEmitter::ResetEmissionTimers() { + m_LastEmitTmr.Reset(); + for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) + (*eItr).ResetEmissionTimers(); } - delete m_EmissionSound; - delete m_BurstSound; - delete m_EndSound; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnableEmission + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this AEmitter to start emitting at the set rate, or to stop. + + void AEmitter::EnableEmission(bool enable) { + if (!m_EmitEnabled && enable) { + m_LastEmitTmr.Reset(); + // Reset counter + m_EmitCount = 0; + // Reset animation + m_Frame = 0; + } + m_EmitEnabled = enable; + } -// m_BurstSound.Stop(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EstimateImpulse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the forces this emitter applies on any parent. + + float AEmitter::EstimateImpulse(bool burst) { + // Calculate the impulse generated by the emissions, once and store the result + if ((!burst && m_AvgImpulse < 0) || (burst && m_AvgBurstImpulse < 0)) { + float impulse = 0; + float velMin, velMax, velRange, spread; + + // Go through all emissions and emit them according to their respective rates + for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) { + // Only check emissions that push the emitter + if ((*eItr).PushesEmitter()) { + double emissions = (*eItr).GetRate() * g_TimerMan.GetDeltaTimeSecs() / 60.0f; + if (burst) { + emissions *= (*eItr).GetBurstSize(); + } - if (!notInherited) - Attachable::Destroy(); - Clear(); -} + velMin = std::min((*eItr).GetMinVelocity(), (*eItr).GetMaxVelocity()); + velMax = std::max((*eItr).GetMinVelocity(), (*eItr).GetMaxVelocity()); + velRange = (velMax - velMin) * 0.5; + spread = std::max(static_cast(c_PI) - (*eItr).GetSpread(), .0f) / c_PI; // A large spread will cause the forces to cancel eachother out -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResetEmissionTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reset the timers of all emissions so they will start/stop at the -// correct relative offsets from now. + // Add to accumulative recoil impulse generated, F = m * a. + impulse += (velMin + velRange) * spread * (*eItr).m_pEmission->GetMass() * emissions; + } + } -void AEmitter::ResetEmissionTimers() -{ - m_LastEmitTmr.Reset(); - for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) - (*eItr).ResetEmissionTimers(); -} + if (burst) + m_AvgBurstImpulse = impulse; + else + m_AvgImpulse = impulse; + } + // Scale the emission rate up or down according to the appropriate throttle multiplier. + float throttleFactor = GetThrottleFactor(); + // Apply the throttle factor to the emission rate per update + if (burst) { + return m_AvgBurstImpulse * throttleFactor; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnableEmission -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this AEmitter to start emitting at the set rate, or to stop. - -void AEmitter::EnableEmission(bool enable) -{ - if (!m_EmitEnabled && enable) - { - m_LastEmitTmr.Reset(); - // Reset counter - m_EmitCount = 0; - // Reset animation - m_Frame = 0; - } - m_EmitEnabled = enable; -} + return m_AvgImpulse * throttleFactor; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EstimateImpulse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the forces this emitter applies on any parent. - -float AEmitter::EstimateImpulse(bool burst) -{ - // Calculate the impulse generated by the emissions, once and store the result - if ((!burst && m_AvgImpulse < 0) || (burst && m_AvgBurstImpulse < 0)) - { - float impulse = 0; - float velMin, velMax, velRange, spread; - - // Go through all emissions and emit them according to their respective rates - for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) - { - // Only check emissions that push the emitter - if ((*eItr).PushesEmitter()) - { - double emissions = (*eItr).GetRate() * g_TimerMan.GetDeltaTimeSecs() / 60.0f; - if (burst) { - emissions *= (*eItr).GetBurstSize(); - } - - velMin = std::min((*eItr).GetMinVelocity(), (*eItr).GetMaxVelocity()); - velMax = std::max((*eItr).GetMinVelocity(), (*eItr).GetMaxVelocity()); - velRange = (velMax - velMin) * 0.5; - spread = std::max(static_cast(c_PI) - (*eItr).GetSpread(), .0f) / c_PI; // A large spread will cause the forces to cancel eachother out - - // Add to accumulative recoil impulse generated, F = m * a. - impulse += (velMin + velRange) * spread * (*eItr).m_pEmission->GetMass() * emissions; - } - } - - if (burst) - m_AvgBurstImpulse = impulse; - else - m_AvgImpulse = impulse; - - } - - // Scale the emission rate up or down according to the appropriate throttle multiplier. - float throttleFactor = GetThrottleFactor(); - // Apply the throttle factor to the emission rate per update - if (burst) { return m_AvgBurstImpulse * throttleFactor; } - - return m_AvgImpulse * throttleFactor; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float AEmitter::GetTotalParticlesPerMinute() const { - float totalPPM = 0; - for (const Emission &emission : m_EmissionList) { - totalPPM += emission.m_PPM; + float AEmitter::GetTotalParticlesPerMinute() const { + float totalPPM = 0; + for (const Emission& emission: m_EmissionList) { + totalPPM += emission.m_PPM; + } + return totalPPM; } - return totalPPM; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int AEmitter::GetTotalBurstSize() const { - int totalBurstSize = 0; - for (const Emission &emission : m_EmissionList) { - totalBurstSize += emission.m_BurstSize; + int AEmitter::GetTotalBurstSize() const { + int totalBurstSize = 0; + for (const Emission& emission: m_EmissionList) { + totalBurstSize += emission.m_BurstSize; + } + return totalBurstSize; } - return totalBurstSize; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -float AEmitter::GetScaledThrottle(float throttle, float multiplier) const { - float throttleFactor = LERP(-1.0f, 1.0f, m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, throttle); - return LERP(m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, -1.0f, 1.0f, throttleFactor * multiplier); -} + float AEmitter::GetScaledThrottle(float throttle, float multiplier) const { + float throttleFactor = LERP(-1.0f, 1.0f, m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, throttle); + return LERP(m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, -1.0f, 1.0f, throttleFactor * multiplier); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void AEmitter::SetFlash(Attachable *newFlash) { - if (m_pFlash && m_pFlash->IsAttached()) { RemoveAndDeleteAttachable(m_pFlash); } - if (newFlash == nullptr) { - m_pFlash = nullptr; - } else { - // Note - this is done here because setting mass on attached Attachables causes values to be updated on the parent (and its parent, and so on), which isn't ideal. Better to do it before the new flash is attached, so there are fewer calculations. - newFlash->SetMass(0.0F); + void AEmitter::SetFlash(Attachable* newFlash) { + if (m_pFlash && m_pFlash->IsAttached()) { + RemoveAndDeleteAttachable(m_pFlash); + } + if (newFlash == nullptr) { + m_pFlash = nullptr; + } else { + // Note - this is done here because setting mass on attached Attachables causes values to be updated on the parent (and its parent, and so on), which isn't ideal. Better to do it before the new flash is attached, so there are fewer calculations. + newFlash->SetMass(0.0F); + + m_pFlash = newFlash; + AddAttachable(newFlash); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newFlash->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + dynamic_cast(parent)->SetFlash(attachable); + }}); + + m_pFlash->SetDrawnNormallyByParent(false); + m_pFlash->SetInheritsRotAngle(false); + m_pFlash->SetDeleteWhenRemovedFromParent(true); + m_pFlash->SetCollidesWithTerrainWhileAttached(false); + } + } - m_pFlash = newFlash; - AddAttachable(newFlash); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - m_HardcodedAttachableUniqueIDsAndSetters.insert({newFlash->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - dynamic_cast(parent)->SetFlash(attachable); - }}); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this AEmitter. Supposed to be done every frame. - m_pFlash->SetDrawnNormallyByParent(false); - m_pFlash->SetInheritsRotAngle(false); - m_pFlash->SetDeleteWhenRemovedFromParent(true); - m_pFlash->SetCollidesWithTerrainWhileAttached(false); - } -} + void AEmitter::Update() { + Attachable::PreUpdate(); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (m_FrameCount > 1) { + if (m_EmitEnabled && m_SpriteAnimMode == NOANIM) { + m_SpriteAnimMode = ALWAYSLOOP; + } else if (!m_EmitEnabled) { + m_SpriteAnimMode = NOANIM; + m_Frame = 0; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this AEmitter. Supposed to be done every frame. - -void AEmitter::Update() -{ - Attachable::PreUpdate(); - - if (m_FrameCount > 1) { - if (m_EmitEnabled && m_SpriteAnimMode == NOANIM) { - m_SpriteAnimMode = ALWAYSLOOP; - } else if (!m_EmitEnabled) { - m_SpriteAnimMode = NOANIM; - m_Frame = 0; - } - } - - // Update and show flash if there is one - if (m_pFlash && (!m_FlashOnlyOnBurst || m_BurstTriggered)) { - m_pFlash->SetParentOffset(m_EmissionOffset); - m_pFlash->SetRotAngle(m_Rotation.GetRadAngle() + (m_EmitAngle.GetRadAngle() * GetFlipFactor())); - m_pFlash->SetScale(m_FlashScale); - m_pFlash->SetNextFrame(); - } - - Attachable::Update(); - - if (m_BurstSoundFollowsEmitter && m_BurstSound) { - m_BurstSound->SetPosition(m_Pos); - } - - if (m_EmitEnabled) - { - if (!m_WasEmitting) - { - // Start playing the sound - if (m_EmissionSound) { m_EmissionSound->Play(m_Pos); } - - // Reset the timers of all emissions so they will start/stop at the correct relative offsets from now - for (Emission &emission : m_EmissionList) - emission.ResetEmissionTimers(); - } - // Update the distance attenuation - else - if (m_EmissionSound) { m_EmissionSound->SetPosition(m_Pos); } - - // Get the parent root of this AEmitter -// TODO: Potentially get this once outside instead, like in attach/detach") - MovableObject *pRootParent = GetRootParent(); + // Update and show flash if there is one + if (m_pFlash && (!m_FlashOnlyOnBurst || m_BurstTriggered)) { + m_pFlash->SetParentOffset(m_EmissionOffset); + m_pFlash->SetRotAngle(m_Rotation.GetRadAngle() + (m_EmitAngle.GetRadAngle() * GetFlipFactor())); + m_pFlash->SetScale(m_FlashScale); + m_pFlash->SetNextFrame(); + } - float throttleFactor = GetThrottleFactor(); - m_FlashScale = throttleFactor; - // Check burst triggering against whether the spacing is fulfilled - if (m_BurstTriggered && CanTriggerBurst()) { - // Play burst sound - if (m_BurstSound) { m_BurstSound->Play(m_Pos); } - // Start timing until next burst - m_BurstTimer.Reset(); - } - // Not enough spacing, cancel the triggering if there was any - else - m_BurstTriggered = false; - - int emissionCountTotal = 0; - float velMin, velRange, spread; - double currentPPM, SPE; - MovableObject *pParticle = 0; - Vector parentVel, emitVel, pushImpulses; - // Go through all emissions and emit them according to their respective rates - for (Emission &emission : m_EmissionList) - { - // Make sure the emissions only happen between the start time and end time - if (emission.IsEmissionTime()) - { - // Apply the throttle factor to the emission rate - currentPPM = emission.GetRate() * throttleFactor; - int emissionCount = 0; - - // Only do all this if the PPM is actually above zero - if (currentPPM > 0) - { - // Calculate secs per emission - SPE = 60.0 / currentPPM; - - // Add the last elapsed time to the accumulator - emission.m_Accumulator += m_LastEmitTmr.GetElapsedSimTimeS(); - - // Now figure how many full emissions can fit in the current accumulator - emissionCount = std::floor(emission.m_Accumulator / SPE); - // Deduct the about to be emitted emissions from the accumulator - emission.m_Accumulator -= emissionCount * SPE; - - RTEAssert(emission.m_Accumulator >= 0, "Emission accumulator negative!"); - } - else { - emission.m_Accumulator = 0; + Attachable::Update(); + + if (m_BurstSoundFollowsEmitter && m_BurstSound) { + m_BurstSound->SetPosition(m_Pos); + } + + if (m_EmitEnabled) { + if (!m_WasEmitting) { + // Start playing the sound + if (m_EmissionSound) { + m_EmissionSound->Play(m_Pos); } - float scale = 1.0F; - // Add extra emissions if bursting. - if (m_BurstTriggered) { - emissionCount += emission.GetBurstSize() * std::floor(throttleFactor); - scale = m_BurstScale; + + // Reset the timers of all emissions so they will start/stop at the correct relative offsets from now + for (Emission& emission: m_EmissionList) + emission.ResetEmissionTimers(); + } + // Update the distance attenuation + else if (m_EmissionSound) { + m_EmissionSound->SetPosition(m_Pos); + } + + // Get the parent root of this AEmitter + // TODO: Potentially get this once outside instead, like in attach/detach") + MovableObject* pRootParent = GetRootParent(); + + float throttleFactor = GetThrottleFactor(); + m_FlashScale = throttleFactor; + // Check burst triggering against whether the spacing is fulfilled + if (m_BurstTriggered && CanTriggerBurst()) { + // Play burst sound + if (m_BurstSound) { + m_BurstSound->Play(m_Pos); } - emissionCountTotal += emissionCount; - pParticle = 0; - emitVel.Reset(); - parentVel = pRootParent->GetVel() * emission.InheritsVelocity(); - - for (int i = 0; i < emissionCount; ++i) - { - velMin = emission.GetMinVelocity() * scale; - velRange = emission.GetMaxVelocity() - emission.GetMinVelocity() * scale; - spread = emission.GetSpread() * scale; - // Make a copy after the reference particle - pParticle = dynamic_cast(emission.GetEmissionParticlePreset()->Clone()); - // Set up its position and velocity according to the parameters of this. - // Emission point offset not set - - if (emission.GetOffset().IsZero()) { - if (m_EmissionOffset.IsZero()) { - pParticle->SetPos(m_Pos); - } else { - pParticle->SetPos(m_Pos + RotateOffset(m_EmissionOffset)); - } + // Start timing until next burst + m_BurstTimer.Reset(); + } + // Not enough spacing, cancel the triggering if there was any + else + m_BurstTriggered = false; + + int emissionCountTotal = 0; + float velMin, velRange, spread; + double currentPPM, SPE; + MovableObject* pParticle = 0; + Vector parentVel, emitVel, pushImpulses; + // Go through all emissions and emit them according to their respective rates + for (Emission& emission: m_EmissionList) { + // Make sure the emissions only happen between the start time and end time + if (emission.IsEmissionTime()) { + // Apply the throttle factor to the emission rate + currentPPM = emission.GetRate() * throttleFactor; + int emissionCount = 0; + + // Only do all this if the PPM is actually above zero + if (currentPPM > 0) { + // Calculate secs per emission + SPE = 60.0 / currentPPM; + + // Add the last elapsed time to the accumulator + emission.m_Accumulator += m_LastEmitTmr.GetElapsedSimTimeS(); + + // Now figure how many full emissions can fit in the current accumulator + emissionCount = std::floor(emission.m_Accumulator / SPE); + // Deduct the about to be emitted emissions from the accumulator + emission.m_Accumulator -= emissionCount * SPE; + + RTEAssert(emission.m_Accumulator >= 0, "Emission accumulator negative!"); } else { - pParticle->SetPos(m_Pos + RotateOffset(emission.GetOffset())); + emission.m_Accumulator = 0; } - // TODO: Optimize making the random angles!") - emitVel.SetXY(velMin + RandomNum(0.0F, velRange), 0.0F); - emitVel.RadRotate(m_EmitAngle.GetRadAngle() + spread * RandomNormalNum()); - emitVel = RotateOffset(emitVel); - pParticle->SetVel(parentVel + emitVel); - pParticle->SetRotAngle(emitVel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); - pParticle->SetHFlipped(m_HFlipped); - - //Scale the particle's lifetime based on life variation and throttle, as long as it's not 0 - if (pParticle->GetLifetime() != 0) { - pParticle->SetLifetime(std::max(static_cast(static_cast(pParticle->GetLifetime()) * (1.0F + (emission.GetLifeVariation() * RandomNormalNum()))), 1)); - pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * throttleFactor), 1)); + float scale = 1.0F; + // Add extra emissions if bursting. + if (m_BurstTriggered) { + emissionCount += emission.GetBurstSize() * std::floor(throttleFactor); + scale = m_BurstScale; } - pParticle->SetTeam(m_Team); - pParticle->SetIgnoresTeamHits(true); - - // Add to accumulative recoil impulse generated, F = m * a - // If enabled, that is - if (emission.PushesEmitter() && (GetParent() || GetMass() > 0)) { pushImpulses -= emitVel * pParticle->GetMass(); } - - // Set the emitted particle to not hit this emitter's parent, if applicable - if (m_EmissionsIgnoreThis) - pParticle->SetWhichMOToNotHit(pRootParent); - - // Let particle loose into the world! - g_MovableMan.AddMO(pParticle); - pParticle = 0; - } - } - } - m_LastEmitTmr.Reset(); + emissionCountTotal += emissionCount; + pParticle = 0; + emitVel.Reset(); + parentVel = pRootParent->GetVel() * emission.InheritsVelocity(); + + for (int i = 0; i < emissionCount; ++i) { + velMin = emission.GetMinVelocity() * scale; + velRange = emission.GetMaxVelocity() - emission.GetMinVelocity() * scale; + spread = emission.GetSpread() * scale; + // Make a copy after the reference particle + pParticle = dynamic_cast(emission.GetEmissionParticlePreset()->Clone()); + // Set up its position and velocity according to the parameters of this. + // Emission point offset not set + + if (emission.GetOffset().IsZero()) { + if (m_EmissionOffset.IsZero()) { + pParticle->SetPos(m_Pos); + } else { + pParticle->SetPos(m_Pos + RotateOffset(m_EmissionOffset)); + } + } else { + pParticle->SetPos(m_Pos + RotateOffset(emission.GetOffset())); + } + // TODO: Optimize making the random angles!") + emitVel.SetXY(velMin + RandomNum(0.0F, velRange), 0.0F); + emitVel.RadRotate(m_EmitAngle.GetRadAngle() + spread * RandomNormalNum()); + emitVel = RotateOffset(emitVel); + pParticle->SetVel(parentVel + emitVel); + pParticle->SetRotAngle(emitVel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); + pParticle->SetHFlipped(m_HFlipped); + + // Scale the particle's lifetime based on life variation and throttle, as long as it's not 0 + if (pParticle->GetLifetime() != 0) { + pParticle->SetLifetime(std::max(static_cast(static_cast(pParticle->GetLifetime()) * (1.0F + (emission.GetLifeVariation() * RandomNormalNum()))), 1)); + pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * throttleFactor), 1)); + } + pParticle->SetTeam(m_Team); + pParticle->SetIgnoresTeamHits(true); - // Apply recoil/push effects. Joint stiffness will take effect when these are transferred to the parent. - if (!pushImpulses.IsZero()) { AddImpulseForce(pushImpulses); } + // Add to accumulative recoil impulse generated, F = m * a + // If enabled, that is + if (emission.PushesEmitter() && (GetParent() || GetMass() > 0)) { + pushImpulses -= emitVel * pParticle->GetMass(); + } + + // Set the emitted particle to not hit this emitter's parent, if applicable + if (m_EmissionsIgnoreThis) + pParticle->SetWhichMOToNotHit(pRootParent); - // Count the the damage caused by the emissions, and only if we're not bursting - if (!m_BurstTriggered) { - m_DamageCount += static_cast(emissionCountTotal) * m_EmitDamage * m_EmitterDamageMultiplier; - } else { // Count the the damage caused by the burst - m_DamageCount += m_BurstDamage * m_EmitterDamageMultiplier; + // Let particle loose into the world! + g_MovableMan.AddMO(pParticle); + pParticle = 0; + } + } + } + m_LastEmitTmr.Reset(); + + // Apply recoil/push effects. Joint stiffness will take effect when these are transferred to the parent. + if (!pushImpulses.IsZero()) { + AddImpulseForce(pushImpulses); + } + + // Count the the damage caused by the emissions, and only if we're not bursting + if (!m_BurstTriggered) { + m_DamageCount += static_cast(emissionCountTotal) * m_EmitDamage * m_EmitterDamageMultiplier; + } else { // Count the the damage caused by the burst + m_DamageCount += m_BurstDamage * m_EmitterDamageMultiplier; + } + + // Count the total emissions since enabling, and stop emitting if beyond limit (and limit is also enabled) + m_EmitCount += emissionCountTotal; + if (m_EmitCountLimit > 0 && m_EmitCount > m_EmitCountLimit) { + EnableEmission(false); + } + + if (m_BurstTriggered) { + m_BurstTriggered = false; + } + + m_WasEmitting = true; + } + // Do stuff to stop emission + else { + if (m_WasEmitting) { + if (m_EmissionSound) { + m_EmissionSound->Stop(); + } + if (m_BurstSound && !m_SustainBurstSound) { + m_BurstSound->Stop(); + } + if (m_EndSound) { + m_EndSound->Play(m_Pos); + } + m_WasEmitting = false; + } } - // Count the total emissions since enabling, and stop emitting if beyond limit (and limit is also enabled) - m_EmitCount += emissionCountTotal; - if (m_EmitCountLimit > 0 && m_EmitCount > m_EmitCountLimit) { EnableEmission(false); } - - if (m_BurstTriggered) { m_BurstTriggered = false; } - - m_WasEmitting = true; - } - // Do stuff to stop emission - else - { - if (m_WasEmitting) - { - if (m_EmissionSound) { m_EmissionSound->Stop(); } - if (m_BurstSound && !m_SustainBurstSound) { m_BurstSound->Stop(); } - if (m_EndSound) { m_EndSound->Play(m_Pos); } - m_WasEmitting = false; + // Set the screen flash effect to draw at the final post processing stage + if (m_EmitEnabled && (!m_FlashOnlyOnBurst || m_BurstTriggered) && m_pFlash && m_pFlash->GetScreenEffect()) { + // Fudge the glow pos forward a bit so it aligns nicely with the flash + Vector emitPos(m_pFlash->GetScreenEffect()->w * 0.3F * m_FlashScale, 0); + emitPos.RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); + emitPos = m_Pos + RotateOffset(m_EmissionOffset) + emitPos; + if (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(emitPos)) { + g_PostProcessMan.RegisterPostEffect(emitPos, m_pFlash->GetScreenEffect(), m_pFlash->GetScreenEffectHash(), RandomNum(m_pFlash->GetEffectStopStrength(), m_pFlash->GetEffectStartStrength()) * std::clamp(m_FlashScale, 0.0F, 1.0F), m_pFlash->GetEffectRotAngle()); + } } } - // Set the screen flash effect to draw at the final post processing stage - if (m_EmitEnabled && (!m_FlashOnlyOnBurst || m_BurstTriggered) && m_pFlash && m_pFlash->GetScreenEffect()) { - // Fudge the glow pos forward a bit so it aligns nicely with the flash - Vector emitPos(m_pFlash->GetScreenEffect()->w * 0.3F * m_FlashScale, 0); - emitPos.RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); - emitPos = m_Pos + RotateOffset(m_EmissionOffset) + emitPos; - if (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(emitPos)) { - g_PostProcessMan.RegisterPostEffect(emitPos, m_pFlash->GetScreenEffect(), m_pFlash->GetScreenEffectHash(), RandomNum(m_pFlash->GetEffectStopStrength(), m_pFlash->GetEffectStartStrength()) * std::clamp(m_FlashScale, 0.0F, 1.0F), m_pFlash->GetEffectRotAngle()); - } - } -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this AEmitter's current graphical representation to a -// BITMAP of choice. - -void AEmitter::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - // Draw flash if there is one - if (m_pFlash && !m_pFlash->IsDrawnAfterParent() && - !onlyPhysical && mode == g_DrawColor && m_EmitEnabled && (!m_FlashOnlyOnBurst || m_BurstTriggered)) - m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - // Update and Draw flash if there is one - if (m_pFlash && m_pFlash->IsDrawnAfterParent() && - !onlyPhysical && mode == g_DrawColor && m_EmitEnabled && (!m_FlashOnlyOnBurst || m_BurstTriggered)) - m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this AEmitter's current graphical representation to a + // BITMAP of choice. + + void AEmitter::Draw(BITMAP* pTargetBitmap, + const Vector& targetPos, + DrawMode mode, + bool onlyPhysical) const { + // Draw flash if there is one + if (m_pFlash && !m_pFlash->IsDrawnAfterParent() && + !onlyPhysical && mode == g_DrawColor && m_EmitEnabled && (!m_FlashOnlyOnBurst || m_BurstTriggered)) + m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + + Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + + // Update and Draw flash if there is one + if (m_pFlash && m_pFlash->IsDrawnAfterParent() && + !onlyPhysical && mode == g_DrawColor && m_EmitEnabled && (!m_FlashOnlyOnBurst || m_BurstTriggered)) + m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } } // namespace RTE \ No newline at end of file diff --git a/Source/Entities/AEmitter.h b/Source/Entities/AEmitter.h index 94fb8b6fd4..2b37b7921d 100644 --- a/Source/Entities/AEmitter.h +++ b/Source/Entities/AEmitter.h @@ -10,752 +10,721 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "Attachable.h" #include "Emission.h" -namespace RTE -{ - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: AEmitter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: An attachable MO that creates and emits particle MO's. -// Parent(s): Attachable. -// Class history: 02/29/2004 AEmitter created. - -class AEmitter : public Attachable { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - friend struct EntityLuaBindings; - -// Concrete allocation and cloning definitions -EntityAllocation(AEmitter); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: AEmitter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a AEmitter object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - AEmitter() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~AEmitter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a AEmitter object before deletion -// from system memory. -// Arguments: None. - - ~AEmitter() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a AEmitter to be identical to another, by deep copy. -// Arguments: A reference to the AEmitter to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const AEmitter &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire AEmitter, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); MOSRotating::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsEmitting -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this AEmitter is currently enabled and emitting. -// Arguments: None. -// Return value: Whether it's emitting or not. - - bool IsEmitting() const { return m_EmitEnabled; } - - /// - /// Returns whether this emitter was emitting last frame. - /// - /// Whether this emitter was emitting last frame. - bool WasEmitting() const { return m_WasEmitting; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResetEmissionTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reset the timers of all emissions so they will start/stop at the -// correct relative offsets from now. -// Arguments: None. -// Return value: None. - - void ResetEmissionTimers(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnableEmission -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this AEmitter to start emitting at the set rate, or to stop. -// Arguments: Whether to enable or disable emission. -// Return value: None. - - void EnableEmission(bool enable = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EstimateImpulse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the forces this emitter applies on any parent. -// Arguments: Whether to calculate a burst update or not. -// Return value: The approximate impulse generated by the emitter. - - float EstimateImpulse(bool burst = false); - - /// - /// Gets the rate at which all of the Emissions of this AEmitter, combined, emit their particles. - /// - /// The combined particles per minute of all Emissions in this AEmitter. - float GetTotalParticlesPerMinute() const; - - /// - /// Gets the number of particles that will be emitted by all the Emissions of this AEmitter combined, in one shot when a burst is triggered. - /// - /// The combined burst size of all Emissions in this AEmitter. - int GetTotalBurstSize() const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBurstScale -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the scale factor that will be applied to the regular spread and -// emission velocity to get the burst particle parameters. -// Arguments: None. -// Return value: The scale factor. - - float GetBurstScale() const { return m_BurstScale; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEmitAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the angle of direction that the emitted particles will be shot at. -// Arguments: None. -// Return value: A float with the angle in radians. - - float GetEmitAngle() const { return m_EmitAngle.GetRadAngle(); } - - const Matrix & GetEmitAngleMatrix() const { return m_EmitAngle; } - - /// - /// Gets the offset of the emission point from this' sprite center, which gets rotated with this. - /// - /// The emission offset. - Vector GetEmitOffset() const { return m_EmissionOffset; } - - /// - /// Sets the offset of the emission point from this' sprite center, which gets rotated with this. - /// - /// The new emission offset. - void SetEmitOffset(const Vector &newOffset) { m_EmissionOffset = newOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEmitVector -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A vector in the direction, including the rotation of the emitter, that -// the emitted particles will be shot at. -// Arguments: None. -// Return value: A unit vector. - - Vector GetEmitVector() const { return Vector(1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRecoilVector -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A vector in the opposite direction, including the rotation of the -// emitter, that the emitted particles will be shot at. -// Arguments: None. -// Return value: A unit vector. - - Vector GetRecoilVector() const { return Vector(-1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBurstSpacing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the BurstSpacing for this emitter. -// Arguments: None. -// Return value: The BurstSpacing in ms. - - float GetBurstSpacing() const { return m_BurstSpacing; } - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEmitSpread -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the angle spread of velocity of the emitted MO's to each side of -// the angle of emission of this AEmitter. -// Arguments: None. -// Return value: A float with the spread in r's. PI/2 would mean that MO's fly out to -// one side only, with the m_EmitAngle defining the middle of that half -// circle. - - float GetEmitSpread() const { return m_Spread; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEmitVelMin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the min end of the range the velocity of a particle being emitted -// by this AEmitter can have. -// Arguments: None. -// Return value: A float with the min vel possible for an emitted particle. - - float GetEmitVelMin() const { return m_MinVelocity; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEmitVelMax -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the max end of the range the velocity of a particle being emitted -// by this AEmitter can have. -// Arguments: None. -// Return value: A float with the max vel possible for an emitted particle. - - float GetEmitVelMax() const { return m_MaxVelocity; } -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetThrottle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the normalized throttle scalar which controls how to affect the -// emission rate as per the emisison rate range. Depricated for Lua, use -// the Throttle property instead. -// Arguments: None. -// Return value: A float with the normalized throttle scalar. 1.0 means max throttle, -// 0 means normal, -1.0 means least emission rate. - - float GetThrottle() const { return m_Throttle; } - - /// - /// Gets the adjusted throttle multiplier that is factored into the emission rate of this AEmitter. - /// - /// The throttle strength as a multiplier. - float GetThrottleFactor() const { return LERP(-1.0f, 1.0f, m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, m_Throttle); } - - /// - /// Gets the throttle value that will achieve a given throttle factor that is factored into the emission rate of this AEmitter. - /// - /// The throttle value that will achieve the given throttle factor. - float GetThrottleForThrottleFactor(float throttleFactor) const { return LERP(m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, -1.0f, 1.0f, throttleFactor); } - - /// - /// Returns a scaled throttle value that represents a linear increase of force. - /// Because of (bad) reasons, throttle is in the range -1.0F to 1.0F, where -1.0F is "minimum force" and 1.0F is "maximum force". - /// 0.0F is "whoever the fuck knows?" force. As such, multiplying throttle by 2 does not mean twice the force emitted, instead it means "whoever the fuck knows?" additional force emitted. - /// All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. - /// ...this helper function lets us apply a scale to throttle and get a sensible result. - /// - /// The throttle value to be considered. - /// The multiplier to scale by, in terms of absolute force emitted. - /// Adjusted throttle value scaled by the multiplier value. - float GetScaledThrottle(float throttle, float multiplier) const; - - /// - /// Gets the negative throttle multiplier of this AEmitter. - /// - /// The negative throttle multiplier of this AEmitter. - float GetNegativeThrottleMultiplier() const { return m_NegativeThrottleMultiplier; } - - /// - /// Gets the positive throttle multiplier of this AEmitter. - /// - /// The positive throttle multiplier of this AEmitter. - float GetPositiveThrottleMultiplier() const { return m_PositiveThrottleMultiplier; } - - /// - /// Sets the negative throttle multiplier of this AEmitter. - /// - /// The new throttle multiplier of this AEmitter. - void SetNegativeThrottleMultiplier(float newValue) { m_NegativeThrottleMultiplier = newValue; } - - /// - /// Sets the positive throttle multiplier of this AEmitter. - /// - /// The new throttle multiplier of this AEmitter. - void SetPositiveThrottleMultiplier(float newValue) { m_PositiveThrottleMultiplier = newValue; } - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEmitRate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the rate at which this AEmitter emits its particles. -// Arguments: A float with the rate in #/min. -// Return value: None. - - void SetEmitRate(const float rate) { m_PPM = rate; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBurstCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the number of particles that will be emitted in one shot upon -// a triggered burst of this AEmitter. -// Arguments: The number of emitted particles a burst should have. 0 means burst -// are disabled. -// Return value: None. - - void SetBurstCount(const int count) { m_BurstSize = count; } -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBurstScale -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the scale factor that will be applied to the regular spread and -// emission velocity to get the burst particle parameters. -// Arguments: The scale factor. -// Return value: None. - - void SetBurstScale(const float scale) { m_BurstScale = scale; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBurstSpacing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the BurstSpacing for this emitter. -// Arguments: The BurstSpacing in ms. -// Return value: None. - - void SetBurstSpacing(const float spacing) { m_BurstSpacing = spacing; } - - - /// - /// Gets the flash of this AEmitter. - /// - /// A pointer to the AEmitter's flash. Ownership is NOT transferred! - Attachable * GetFlash() const { return m_pFlash; } - - /// - /// Sets the flash for this AEmitter. Ownership IS transferred! - /// - /// The new flash to use. - void SetFlash(Attachable *newFlash); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFlashScale -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the display scale factor of the flash effect. This is purely -// visual. -// Arguments: None. -// Return value: The scale factor of the flash draw. - - float GetFlashScale() const { return m_FlashScale; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFlashScale -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the display scale factor of the flash effect. This is purely -// visual. -// Arguments: The scale factor of the flash draw. -// Return value: None. - - void SetFlashScale(float flashScale = 1.0f) { m_FlashScale = flashScale; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEmitAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the angle of direction that the emitted particles will be shot at. -// Arguments: A float with the angle in radians. -// Return value: None. - - void SetEmitAngle(const float angle) { m_EmitAngle.SetRadAngle(angle); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetThrottle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the normalized throttle scalar which controls how to affect the -// emission rate as per the emisison rate range. -// Arguments: A float with the normalized throttle scalar. 1.0 means max throttle, -// 0 means normal, -1.0 means least emission rate. -// Return value: None. - - void SetThrottle(float throttle) { m_Throttle = throttle > 1.0f ? 1.0f : (throttle < -1.0f ? -1.0f : throttle); } - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEmitSpread -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the angle spread of velocity of the emitted MO's to each side of -// angle of emission of this AEmitter. -// Arguments: A float with the spread in r's. PI/2 would mean that MO's fly out to -// one side only, with the m_EmitAngle defining the middle of that half -// circle. -// Return value: None. - - void SetEmitSpread(const float spread) { m_Spread = spread; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEmitVelMin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the min end of the range the velocity of a particle being emitted -// by this AEmitter can have. -// Arguments: A float with the min vel possible for an emitted particle. -// Return value: None. - - void SetEmitVelMin(const float minVel) { m_MinVelocity = minVel; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEmitVelMax -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the max end of the range the velocity of a particle being emitted -// by this AEmitter can have. -// Arguments: A float with the max vel possible for an emitted particle. -// Return value: None. - - void SetEmitVelMax(const float maxVel) { m_MaxVelocity = maxVel; } -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TriggerBurst -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Triggers a one-shot burst of emissions in the number that has -// previously been set. The burst will happen during the next Update of -// this AEmitter. -// Arguments: None. -// Return value: None. - - void TriggerBurst() { m_BurstTriggered = true; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CanTriggerBurst -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if it is possible to trigger a one-shot burst of emissions during -// the next Update of this AEmitter. -// Arguments: None. -// Return value: If it is possible to trigger a burst. - - bool CanTriggerBurst() { return m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsSetToBurst -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this AEmitter is set to burst next update or not. -// Arguments: None. -// Return value: Whether a burst is gonna happen or not.. - - bool IsSetToBurst() const { return m_BurstTriggered; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AlarmOnEmit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Registers a new AlarmEvent if this emitter has a loudness above zero. -// Arguments: Team that will ignore this AlarmEvent. -// Return value: None. - - void AlarmOnEmit(int Team) const { if (m_LoudnessOnEmit > 0) g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, Team, m_LoudnessOnEmit)); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetAllTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resest all the timers used by this. Can be emitters, etc. This is to -// prevent backed up emissions to come out all at once while this has been -// held dormant in an inventory. -// Arguments: None. -// Return value: None. - - void ResetAllTimers() override { Attachable::ResetAllTimers(); m_BurstTimer.Reset(); m_LastEmitTmr.Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - void PostUpdate() override { Attachable::PostUpdate(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBurstDamage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns burst damage of this emitter. -// Arguments: None. -// Return value: Burst damage of emitter. - - float GetBurstDamage() const { return m_BurstDamage * m_EmitterDamageMultiplier; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBurstDamage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets burst damage of this emitter. -// Arguments: Burst damage of emitter. -// Return value: None. - - void SetBurstDamage(float newValue) { m_BurstDamage = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEmitDamage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns emit damage of this emitter. -// Arguments: None. -// Return value: Emit damage of emitter. - - float GetEmitDamage() const { return m_EmitDamage * m_EmitterDamageMultiplier; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEmitDamage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets emit damage of this emitter. -// Arguments: Emit damage of emitter. -// Return value: None. - - void SetEmitDamage(float newValue) { m_EmitDamage = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEmitterDamageMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns damage multiplier of this emitter. -// Arguments: None. -// Return value: Damage multiplier of emitter. - - float GetEmitterDamageMultiplier() const { return m_EmitterDamageMultiplier; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEmitterDamageMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets damage multiplier of this emitter. -// Arguments: New damage multiplier of emitter -// Return value: None. - - void SetEmitterDamageMultiplier(float newValue) { m_EmitterDamageMultiplier = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this AEmitter's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDamaging -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this emitter deals damage. -// Arguments: None. -// Return value: Returns true if this emitter deals damage. - - bool IsDamaging() { return (m_EmitDamage > 0 || m_BurstDamage > 0) && m_EmitterDamageMultiplier > 0; } - - /// - /// Gets the number of emissions emitted since emission was last enabled. - /// - /// The number of emissions emitted since emission was last enabled. - long GetEmitCount() const { return m_EmitCount; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEmitCountLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of emissions left before emitter is disabled. -// Arguments: None. -// Return value: Returns the number of emissions left before emitter is disabled. - - long GetEmitCountLimit() const { return m_EmitCountLimit; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEmitCountLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the number of emissions left before emitter is disabled. -// Arguments: New number of emissions left -// Return value: None. - - void SetEmitCountLimit(long newValue) { m_EmitCountLimit = newValue; } - - /// - /// Gets this AEmitter's emission sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this AEmitter's emission sound. - SoundContainer * GetEmissionSound() const { return m_EmissionSound; } - - /// - /// Sets this AEmitter's emission sound. Ownership IS transferred! - /// - /// The new SoundContainer for this AEmitter's emission sound. - void SetEmissionSound(SoundContainer *newSound) { m_EmissionSound = newSound; } - - /// - /// Gets this AEmitter's burst sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this AEmitter's burst sound. - SoundContainer * GetBurstSound() const { return m_BurstSound; } - - /// - /// Sets this AEmitter's burst sound. Ownership IS transferred! - /// - /// The new SoundContainer for this AEmitter's burst sound. - void SetBurstSound(SoundContainer *newSound) { m_BurstSound = newSound; } - - /// - /// Gets this AEmitter's end sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this AEmitter's end sound. - SoundContainer * GetEndSound() const { return m_EndSound; } - - /// - /// Sets this AEmitter's end sound. Ownership IS transferred! - /// - /// The new SoundContainer for this AEmitter's end sound. - void SetEndSound(SoundContainer *newSound) { m_EndSound = newSound; } - - /// - /// Returns whether this emitter just started emitting this frame. - /// - /// Whether this emitter just started emitting this frame. - bool JustStartedEmitting() const { return !m_WasEmitting && m_EmitEnabled; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - - // The list of MO instances that get emitted - std::vector m_EmissionList; - // Sounds - SoundContainer *m_EmissionSound; - SoundContainer *m_BurstSound; - SoundContainer *m_EndSound; - // Whether emitting is currently enabled or not. - bool m_EmitEnabled; - // Whether or not the it was emitting last frame or not. - bool m_WasEmitting; - // The number of emissions emitted since emission was last enabled - long m_EmitCount; - // The max number of emissions to emit per emit being enabled - long m_EmitCountLimit; - float m_NegativeThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is negative. Relative to the absolute throttle value. - float m_PositiveThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is positive. Relative to the absolute throttle value. - float m_Throttle; //!< The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. - // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. - bool m_EmissionsIgnoreThis; - // The scale factor that will be applied to the regular spread and emission - // velocity to get the the burst particle parameters. - float m_BurstScale; - // Damage dealt to the attached-to parent upon bursting. - float m_BurstDamage; - // Damage multiplier derived from penetrating particle. Affects both burst and emit damage values. - float m_EmitterDamageMultiplier; - // Indicates that a burst is set to happen during the next Update. - bool m_BurstTriggered; - // The shortest possible time between bursts, in ms - float m_BurstSpacing; - // Measures the shortest possible time between bursts - Timer m_BurstTimer; - // The angle of the direction the emitted particles will head in. - // The m_Roataion of this AEmitter will be added to this angle. - Matrix m_EmitAngle; - // Offset of the emission point from this' sprite center, which gets rotated with this - Vector m_EmissionOffset; - // The amount of damage points that this emitter collects when emitting one non-burst particle. - float m_EmitDamage; - // Timer for timing how long ago the last particle was emitted. 0 means no limit. - Timer m_LastEmitTmr; - // Emission flash Attachable - Attachable *m_pFlash; - // Flash display scale - float m_FlashScale; - // How large impulse this emitter generates when bursting - float m_AvgBurstImpulse; - // How large impulse this emitter generates when firing - float m_AvgImpulse; - // How far this is audiable (in screens) when emitting as a jetpack or craft engine - float m_LoudnessOnEmit; - // Whether to only display flash on bursts, and not on any emission frame. - bool m_FlashOnlyOnBurst; - // Whether the burst sound should always play until completion, or whether it stops when this emitter stops emitting - bool m_SustainBurstSound; - // Whether the burst sound follows the emitter - bool m_BurstSoundFollowsEmitter; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AEmitter, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - AEmitter(const AEmitter &reference) = delete; - AEmitter & operator=(const AEmitter &rhs) = delete; - -}; +namespace RTE { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: AEmitter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: An attachable MO that creates and emits particle MO's. + // Parent(s): Attachable. + // Class history: 02/29/2004 AEmitter created. + + class AEmitter : public Attachable { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + friend struct EntityLuaBindings; + + // Concrete allocation and cloning definitions + EntityAllocation(AEmitter); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: AEmitter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a AEmitter object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + AEmitter() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~AEmitter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a AEmitter object before deletion + // from system memory. + // Arguments: None. + + ~AEmitter() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a AEmitter to be identical to another, by deep copy. + // Arguments: A reference to the AEmitter to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const AEmitter& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire AEmitter, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + MOSRotating::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsEmitting + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this AEmitter is currently enabled and emitting. + // Arguments: None. + // Return value: Whether it's emitting or not. + + bool IsEmitting() const { return m_EmitEnabled; } + + /// + /// Returns whether this emitter was emitting last frame. + /// + /// Whether this emitter was emitting last frame. + bool WasEmitting() const { return m_WasEmitting; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ResetEmissionTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reset the timers of all emissions so they will start/stop at the + // correct relative offsets from now. + // Arguments: None. + // Return value: None. + + void ResetEmissionTimers(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnableEmission + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this AEmitter to start emitting at the set rate, or to stop. + // Arguments: Whether to enable or disable emission. + // Return value: None. + + void EnableEmission(bool enable = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EstimateImpulse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the forces this emitter applies on any parent. + // Arguments: Whether to calculate a burst update or not. + // Return value: The approximate impulse generated by the emitter. + + float EstimateImpulse(bool burst = false); + + /// + /// Gets the rate at which all of the Emissions of this AEmitter, combined, emit their particles. + /// + /// The combined particles per minute of all Emissions in this AEmitter. + float GetTotalParticlesPerMinute() const; + + /// + /// Gets the number of particles that will be emitted by all the Emissions of this AEmitter combined, in one shot when a burst is triggered. + /// + /// The combined burst size of all Emissions in this AEmitter. + int GetTotalBurstSize() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBurstScale + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the scale factor that will be applied to the regular spread and + // emission velocity to get the burst particle parameters. + // Arguments: None. + // Return value: The scale factor. + + float GetBurstScale() const { return m_BurstScale; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the angle of direction that the emitted particles will be shot at. + // Arguments: None. + // Return value: A float with the angle in radians. + + float GetEmitAngle() const { return m_EmitAngle.GetRadAngle(); } + + const Matrix& GetEmitAngleMatrix() const { return m_EmitAngle; } + + /// + /// Gets the offset of the emission point from this' sprite center, which gets rotated with this. + /// + /// The emission offset. + Vector GetEmitOffset() const { return m_EmissionOffset; } + + /// + /// Sets the offset of the emission point from this' sprite center, which gets rotated with this. + /// + /// The new emission offset. + void SetEmitOffset(const Vector& newOffset) { m_EmissionOffset = newOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitVector + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A vector in the direction, including the rotation of the emitter, that + // the emitted particles will be shot at. + // Arguments: None. + // Return value: A unit vector. + + Vector GetEmitVector() const { return Vector(1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRecoilVector + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A vector in the opposite direction, including the rotation of the + // emitter, that the emitted particles will be shot at. + // Arguments: None. + // Return value: A unit vector. + + Vector GetRecoilVector() const { return Vector(-1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBurstSpacing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the BurstSpacing for this emitter. + // Arguments: None. + // Return value: The BurstSpacing in ms. + + float GetBurstSpacing() const { return m_BurstSpacing; } + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitSpread + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the angle spread of velocity of the emitted MO's to each side of + // the angle of emission of this AEmitter. + // Arguments: None. + // Return value: A float with the spread in r's. PI/2 would mean that MO's fly out to + // one side only, with the m_EmitAngle defining the middle of that half + // circle. + + float GetEmitSpread() const { return m_Spread; } + + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitVelMin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the min end of the range the velocity of a particle being emitted + // by this AEmitter can have. + // Arguments: None. + // Return value: A float with the min vel possible for an emitted particle. + + float GetEmitVelMin() const { return m_MinVelocity; } + + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitVelMax + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the max end of the range the velocity of a particle being emitted + // by this AEmitter can have. + // Arguments: None. + // Return value: A float with the max vel possible for an emitted particle. + + float GetEmitVelMax() const { return m_MaxVelocity; } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetThrottle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the normalized throttle scalar which controls how to affect the + // emission rate as per the emisison rate range. Depricated for Lua, use + // the Throttle property instead. + // Arguments: None. + // Return value: A float with the normalized throttle scalar. 1.0 means max throttle, + // 0 means normal, -1.0 means least emission rate. + + float GetThrottle() const { return m_Throttle; } + + /// + /// Gets the adjusted throttle multiplier that is factored into the emission rate of this AEmitter. + /// + /// The throttle strength as a multiplier. + float GetThrottleFactor() const { return LERP(-1.0f, 1.0f, m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, m_Throttle); } + + /// + /// Gets the throttle value that will achieve a given throttle factor that is factored into the emission rate of this AEmitter. + /// + /// The throttle value that will achieve the given throttle factor. + float GetThrottleForThrottleFactor(float throttleFactor) const { return LERP(m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, -1.0f, 1.0f, throttleFactor); } + + /// + /// Returns a scaled throttle value that represents a linear increase of force. + /// Because of (bad) reasons, throttle is in the range -1.0F to 1.0F, where -1.0F is "minimum force" and 1.0F is "maximum force". + /// 0.0F is "whoever the fuck knows?" force. As such, multiplying throttle by 2 does not mean twice the force emitted, instead it means "whoever the fuck knows?" additional force emitted. + /// All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. + /// ...this helper function lets us apply a scale to throttle and get a sensible result. + /// + /// The throttle value to be considered. + /// The multiplier to scale by, in terms of absolute force emitted. + /// Adjusted throttle value scaled by the multiplier value. + float GetScaledThrottle(float throttle, float multiplier) const; + + /// + /// Gets the negative throttle multiplier of this AEmitter. + /// + /// The negative throttle multiplier of this AEmitter. + float GetNegativeThrottleMultiplier() const { return m_NegativeThrottleMultiplier; } + + /// + /// Gets the positive throttle multiplier of this AEmitter. + /// + /// The positive throttle multiplier of this AEmitter. + float GetPositiveThrottleMultiplier() const { return m_PositiveThrottleMultiplier; } + + /// + /// Sets the negative throttle multiplier of this AEmitter. + /// + /// The new throttle multiplier of this AEmitter. + void SetNegativeThrottleMultiplier(float newValue) { m_NegativeThrottleMultiplier = newValue; } + + /// + /// Sets the positive throttle multiplier of this AEmitter. + /// + /// The new throttle multiplier of this AEmitter. + void SetPositiveThrottleMultiplier(float newValue) { m_PositiveThrottleMultiplier = newValue; } + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitRate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the rate at which this AEmitter emits its particles. + // Arguments: A float with the rate in #/min. + // Return value: None. + + void SetEmitRate(const float rate) { m_PPM = rate; } + + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBurstCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the number of particles that will be emitted in one shot upon + // a triggered burst of this AEmitter. + // Arguments: The number of emitted particles a burst should have. 0 means burst + // are disabled. + // Return value: None. + + void SetBurstCount(const int count) { m_BurstSize = count; } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBurstScale + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the scale factor that will be applied to the regular spread and + // emission velocity to get the burst particle parameters. + // Arguments: The scale factor. + // Return value: None. + + void SetBurstScale(const float scale) { m_BurstScale = scale; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBurstSpacing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the BurstSpacing for this emitter. + // Arguments: The BurstSpacing in ms. + // Return value: None. + + void SetBurstSpacing(const float spacing) { m_BurstSpacing = spacing; } + + /// + /// Gets the flash of this AEmitter. + /// + /// A pointer to the AEmitter's flash. Ownership is NOT transferred! + Attachable* GetFlash() const { return m_pFlash; } + + /// + /// Sets the flash for this AEmitter. Ownership IS transferred! + /// + /// The new flash to use. + void SetFlash(Attachable* newFlash); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFlashScale + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the display scale factor of the flash effect. This is purely + // visual. + // Arguments: None. + // Return value: The scale factor of the flash draw. + + float GetFlashScale() const { return m_FlashScale; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetFlashScale + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the display scale factor of the flash effect. This is purely + // visual. + // Arguments: The scale factor of the flash draw. + // Return value: None. + + void SetFlashScale(float flashScale = 1.0f) { m_FlashScale = flashScale; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the angle of direction that the emitted particles will be shot at. + // Arguments: A float with the angle in radians. + // Return value: None. + + void SetEmitAngle(const float angle) { m_EmitAngle.SetRadAngle(angle); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetThrottle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the normalized throttle scalar which controls how to affect the + // emission rate as per the emisison rate range. + // Arguments: A float with the normalized throttle scalar. 1.0 means max throttle, + // 0 means normal, -1.0 means least emission rate. + // Return value: None. + + void SetThrottle(float throttle) { m_Throttle = throttle > 1.0f ? 1.0f : (throttle < -1.0f ? -1.0f : throttle); } + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitSpread + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the angle spread of velocity of the emitted MO's to each side of + // angle of emission of this AEmitter. + // Arguments: A float with the spread in r's. PI/2 would mean that MO's fly out to + // one side only, with the m_EmitAngle defining the middle of that half + // circle. + // Return value: None. + + void SetEmitSpread(const float spread) { m_Spread = spread; } + + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitVelMin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the min end of the range the velocity of a particle being emitted + // by this AEmitter can have. + // Arguments: A float with the min vel possible for an emitted particle. + // Return value: None. + + void SetEmitVelMin(const float minVel) { m_MinVelocity = minVel; } + + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitVelMax + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the max end of the range the velocity of a particle being emitted + // by this AEmitter can have. + // Arguments: A float with the max vel possible for an emitted particle. + // Return value: None. + + void SetEmitVelMax(const float maxVel) { m_MaxVelocity = maxVel; } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TriggerBurst + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Triggers a one-shot burst of emissions in the number that has + // previously been set. The burst will happen during the next Update of + // this AEmitter. + // Arguments: None. + // Return value: None. + + void TriggerBurst() { m_BurstTriggered = true; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CanTriggerBurst + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if it is possible to trigger a one-shot burst of emissions during + // the next Update of this AEmitter. + // Arguments: None. + // Return value: If it is possible to trigger a burst. + + bool CanTriggerBurst() { return m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsSetToBurst + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this AEmitter is set to burst next update or not. + // Arguments: None. + // Return value: Whether a burst is gonna happen or not.. + + bool IsSetToBurst() const { return m_BurstTriggered; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AlarmOnEmit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Registers a new AlarmEvent if this emitter has a loudness above zero. + // Arguments: Team that will ignore this AlarmEvent. + // Return value: None. + + void AlarmOnEmit(int Team) const { + if (m_LoudnessOnEmit > 0) + g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, Team, m_LoudnessOnEmit)); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ResetAllTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resest all the timers used by this. Can be emitters, etc. This is to + // prevent backed up emissions to come out all at once while this has been + // held dormant in an inventory. + // Arguments: None. + // Return value: None. + + void ResetAllTimers() override { + Attachable::ResetAllTimers(); + m_BurstTimer.Reset(); + m_LastEmitTmr.Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + void PostUpdate() override { Attachable::PostUpdate(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBurstDamage + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns burst damage of this emitter. + // Arguments: None. + // Return value: Burst damage of emitter. + + float GetBurstDamage() const { return m_BurstDamage * m_EmitterDamageMultiplier; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBurstDamage + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets burst damage of this emitter. + // Arguments: Burst damage of emitter. + // Return value: None. + + void SetBurstDamage(float newValue) { m_BurstDamage = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitDamage + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns emit damage of this emitter. + // Arguments: None. + // Return value: Emit damage of emitter. + + float GetEmitDamage() const { return m_EmitDamage * m_EmitterDamageMultiplier; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitDamage + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets emit damage of this emitter. + // Arguments: Emit damage of emitter. + // Return value: None. + + void SetEmitDamage(float newValue) { m_EmitDamage = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitterDamageMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns damage multiplier of this emitter. + // Arguments: None. + // Return value: Damage multiplier of emitter. + + float GetEmitterDamageMultiplier() const { return m_EmitterDamageMultiplier; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitterDamageMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets damage multiplier of this emitter. + // Arguments: New damage multiplier of emitter + // Return value: None. + + void SetEmitterDamageMultiplier(float newValue) { m_EmitterDamageMultiplier = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this AEmitter's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsDamaging + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this emitter deals damage. + // Arguments: None. + // Return value: Returns true if this emitter deals damage. + + bool IsDamaging() { return (m_EmitDamage > 0 || m_BurstDamage > 0) && m_EmitterDamageMultiplier > 0; } + + /// + /// Gets the number of emissions emitted since emission was last enabled. + /// + /// The number of emissions emitted since emission was last enabled. + long GetEmitCount() const { return m_EmitCount; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitCountLimit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of emissions left before emitter is disabled. + // Arguments: None. + // Return value: Returns the number of emissions left before emitter is disabled. + + long GetEmitCountLimit() const { return m_EmitCountLimit; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitCountLimit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the number of emissions left before emitter is disabled. + // Arguments: New number of emissions left + // Return value: None. + + void SetEmitCountLimit(long newValue) { m_EmitCountLimit = newValue; } + + /// + /// Gets this AEmitter's emission sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this AEmitter's emission sound. + SoundContainer* GetEmissionSound() const { return m_EmissionSound; } + + /// + /// Sets this AEmitter's emission sound. Ownership IS transferred! + /// + /// The new SoundContainer for this AEmitter's emission sound. + void SetEmissionSound(SoundContainer* newSound) { m_EmissionSound = newSound; } + + /// + /// Gets this AEmitter's burst sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this AEmitter's burst sound. + SoundContainer* GetBurstSound() const { return m_BurstSound; } + + /// + /// Sets this AEmitter's burst sound. Ownership IS transferred! + /// + /// The new SoundContainer for this AEmitter's burst sound. + void SetBurstSound(SoundContainer* newSound) { m_BurstSound = newSound; } + + /// + /// Gets this AEmitter's end sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this AEmitter's end sound. + SoundContainer* GetEndSound() const { return m_EndSound; } + + /// + /// Sets this AEmitter's end sound. Ownership IS transferred! + /// + /// The new SoundContainer for this AEmitter's end sound. + void SetEndSound(SoundContainer* newSound) { m_EndSound = newSound; } + + /// + /// Returns whether this emitter just started emitting this frame. + /// + /// Whether this emitter just started emitting this frame. + bool JustStartedEmitting() const { return !m_WasEmitting && m_EmitEnabled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + + // The list of MO instances that get emitted + std::vector m_EmissionList; + // Sounds + SoundContainer* m_EmissionSound; + SoundContainer* m_BurstSound; + SoundContainer* m_EndSound; + // Whether emitting is currently enabled or not. + bool m_EmitEnabled; + // Whether or not the it was emitting last frame or not. + bool m_WasEmitting; + // The number of emissions emitted since emission was last enabled + long m_EmitCount; + // The max number of emissions to emit per emit being enabled + long m_EmitCountLimit; + float m_NegativeThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is negative. Relative to the absolute throttle value. + float m_PositiveThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is positive. Relative to the absolute throttle value. + float m_Throttle; //!< The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. + // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. + bool m_EmissionsIgnoreThis; + // The scale factor that will be applied to the regular spread and emission + // velocity to get the the burst particle parameters. + float m_BurstScale; + // Damage dealt to the attached-to parent upon bursting. + float m_BurstDamage; + // Damage multiplier derived from penetrating particle. Affects both burst and emit damage values. + float m_EmitterDamageMultiplier; + // Indicates that a burst is set to happen during the next Update. + bool m_BurstTriggered; + // The shortest possible time between bursts, in ms + float m_BurstSpacing; + // Measures the shortest possible time between bursts + Timer m_BurstTimer; + // The angle of the direction the emitted particles will head in. + // The m_Roataion of this AEmitter will be added to this angle. + Matrix m_EmitAngle; + // Offset of the emission point from this' sprite center, which gets rotated with this + Vector m_EmissionOffset; + // The amount of damage points that this emitter collects when emitting one non-burst particle. + float m_EmitDamage; + // Timer for timing how long ago the last particle was emitted. 0 means no limit. + Timer m_LastEmitTmr; + // Emission flash Attachable + Attachable* m_pFlash; + // Flash display scale + float m_FlashScale; + // How large impulse this emitter generates when bursting + float m_AvgBurstImpulse; + // How large impulse this emitter generates when firing + float m_AvgImpulse; + // How far this is audiable (in screens) when emitting as a jetpack or craft engine + float m_LoudnessOnEmit; + // Whether to only display flash on bursts, and not on any emission frame. + bool m_FlashOnlyOnBurst; + // Whether the burst sound should always play until completion, or whether it stops when this emitter stops emitting + bool m_SustainBurstSound; + // Whether the burst sound follows the emitter + bool m_BurstSoundFollowsEmitter; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this AEmitter, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + AEmitter(const AEmitter& reference) = delete; + AEmitter& operator=(const AEmitter& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/AHuman.cpp b/Source/Entities/AHuman.cpp index 8dcb6b7d72..0eb2c67598 100644 --- a/Source/Entities/AHuman.cpp +++ b/Source/Entities/AHuman.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -36,2008 +35,2098 @@ namespace RTE { -ConcreteClassInfo(AHuman, Actor, 20); + ConcreteClassInfo(AHuman, Actor, 20); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this AHuman, effectively + // resetting the members of this abstraction level only. + + void AHuman::Clear() { + m_pHead = 0; + m_LookToAimRatio = 0.7F; + m_pJetpack = nullptr; + m_pFGArm = 0; + m_pBGArm = 0; + m_pFGLeg = 0; + m_pBGLeg = 0; + m_pFGHandGroup = 0; + m_pBGHandGroup = 0; + m_pFGFootGroup = 0; + m_BackupFGFootGroup = nullptr; + m_pBGFootGroup = 0; + m_BackupBGFootGroup = nullptr; + m_StrideSound = nullptr; + m_ArmsState = WEAPON_READY; + m_MoveState = STAND; + m_ProneState = NOTPRONE; + m_ProneTimer.Reset(); + m_MaxWalkPathCrouchShift = 6.0F; + m_MaxCrouchRotation = c_QuarterPI * 1.25F; + m_CrouchAmountOverride = -1.0F; + for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { + m_Paths[FGROUND][i].Reset(); + m_Paths[BGROUND][i].Reset(); + m_Paths[FGROUND][i].Terminate(); + m_Paths[BGROUND][i].Terminate(); + m_RotAngleTargets[i] = 0.0F; + } + m_Aiming = false; + m_ArmClimbing[FGROUND] = false; + m_ArmClimbing[BGROUND] = false; + m_StrideFrame = false; + m_StrideStart = false; + m_CanActivateBGItem = false; + m_TriggerPulled = false; + m_WaitingToReloadOffhand = false; + m_ThrowTmr.Reset(); + m_ThrowPrepTime = 1000; + m_SharpAimRevertTimer.Reset(); + m_FGArmFlailScalar = 0.0F; + m_BGArmFlailScalar = 0.7F; + m_EquipHUDTimer.Reset(); + m_WalkAngle.fill(Matrix()); + m_WalkPathOffset.Reset(); + m_ArmSwingRate = 1.0F; + m_DeviceArmSwayRate = 0.5F; + + m_DeviceState = SCANNING; + m_SweepState = NOSWEEP; + m_DigState = NOTDIGGING; + m_JumpState = NOTJUMPING; + m_JumpTarget.Reset(); + m_JumpingRight = true; + m_Crawling = false; + m_DigTunnelEndPos.Reset(); + m_SweepCenterAimAngle = 0; + m_SweepRange = c_EighthPI; + m_DigTarget.Reset(); + m_FireTimer.Reset(); + m_SweepTimer.Reset(); + m_PatrolTimer.Reset(); + m_JumpTimer.Reset(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the AHuman object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AHuman, effectively -// resetting the members of this abstraction level only. - -void AHuman::Clear() -{ - m_pHead = 0; - m_LookToAimRatio = 0.7F; - m_pJetpack = nullptr; - m_pFGArm = 0; - m_pBGArm = 0; - m_pFGLeg = 0; - m_pBGLeg = 0; - m_pFGHandGroup = 0; - m_pBGHandGroup = 0; - m_pFGFootGroup = 0; - m_BackupFGFootGroup = nullptr; - m_pBGFootGroup = 0; - m_BackupBGFootGroup = nullptr; - m_StrideSound = nullptr; - m_ArmsState = WEAPON_READY; - m_MoveState = STAND; - m_ProneState = NOTPRONE; - m_ProneTimer.Reset(); - m_MaxWalkPathCrouchShift = 6.0F; - m_MaxCrouchRotation = c_QuarterPI * 1.25F; - m_CrouchAmountOverride = -1.0F; - for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { - m_Paths[FGROUND][i].Reset(); - m_Paths[BGROUND][i].Reset(); - m_Paths[FGROUND][i].Terminate(); - m_Paths[BGROUND][i].Terminate(); - m_RotAngleTargets[i] = 0.0F; - } - m_Aiming = false; - m_ArmClimbing[FGROUND] = false; - m_ArmClimbing[BGROUND] = false; - m_StrideFrame = false; - m_StrideStart = false; - m_CanActivateBGItem = false; - m_TriggerPulled = false; - m_WaitingToReloadOffhand = false; - m_ThrowTmr.Reset(); - m_ThrowPrepTime = 1000; - m_SharpAimRevertTimer.Reset(); - m_FGArmFlailScalar = 0.0F; - m_BGArmFlailScalar = 0.7F; - m_EquipHUDTimer.Reset(); - m_WalkAngle.fill(Matrix()); - m_WalkPathOffset.Reset(); - m_ArmSwingRate = 1.0F; - m_DeviceArmSwayRate = 0.5F; - - m_DeviceState = SCANNING; - m_SweepState = NOSWEEP; - m_DigState = NOTDIGGING; - m_JumpState = NOTJUMPING; - m_JumpTarget.Reset(); - m_JumpingRight = true; - m_Crawling = false; - m_DigTunnelEndPos.Reset(); - m_SweepCenterAimAngle = 0; - m_SweepRange = c_EighthPI; - m_DigTarget.Reset(); - m_FireTimer.Reset(); - m_SweepTimer.Reset(); - m_PatrolTimer.Reset(); - m_JumpTimer.Reset(); -} + int AHuman::Create() { + if (Actor::Create() < 0) { + return -1; + } + if (m_AIMode == Actor::AIMODE_NONE) { + m_AIMode = Actor::AIMODE_BRAINHUNT; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AHuman object ready for use. + // Cheat to make sure the FG Arm is always at the end of the Attachables list so it draws last. + if (m_pFGArm) { + m_Attachables.erase(std::find(m_Attachables.begin(), m_Attachables.end(), m_pFGArm)); + m_Attachables.push_back(m_pFGArm); + } -int AHuman::Create() -{ - if (Actor::Create() < 0) { - return -1; - } + // Make the limb paths for the background limbs + for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { + // If BG path is not initalized, then copy the FG one to it + if (!m_Paths[BGROUND][i].IsInitialized()) { + m_Paths[BGROUND][i].Destroy(); + m_Paths[BGROUND][i].Create(m_Paths[FGROUND][i]); + } + } - if (m_AIMode == Actor::AIMODE_NONE) { - m_AIMode = Actor::AIMODE_BRAINHUNT; - } + // If empty-handed, equip first thing in inventory + if (m_pFGArm && m_pFGArm->IsAttached() && !m_pFGArm->GetHeldDevice()) { + m_pFGArm->SetHeldDevice(dynamic_cast(SwapNextInventory(nullptr, true))); + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + } - // Cheat to make sure the FG Arm is always at the end of the Attachables list so it draws last. - if (m_pFGArm) { - m_Attachables.erase(std::find(m_Attachables.begin(), m_Attachables.end(), m_pFGArm)); - m_Attachables.push_back(m_pFGArm); - } + // All AHumans by default avoid hitting each other ont he same team + m_IgnoresTeamHits = true; - // Make the limb paths for the background limbs - for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) - { - // If BG path is not initalized, then copy the FG one to it - if (!m_Paths[BGROUND][i].IsInitialized()) - { - m_Paths[BGROUND][i].Destroy(); - m_Paths[BGROUND][i].Create(m_Paths[FGROUND][i]); - } - } + return 0; + } - // If empty-handed, equip first thing in inventory - if (m_pFGArm && m_pFGArm->IsAttached() && !m_pFGArm->GetHeldDevice()) { - m_pFGArm->SetHeldDevice(dynamic_cast(SwapNextInventory(nullptr, true))); - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a AHuman to be identical to another, by deep copy. - // All AHumans by default avoid hitting each other ont he same team - m_IgnoresTeamHits = true; + int AHuman::Create(const AHuman& reference) { + if (reference.m_pBGArm) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pBGArm->GetUniqueID()); + } + if (reference.m_pBGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pBGLeg->GetUniqueID()); + } + if (reference.m_pJetpack) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pJetpack->GetUniqueID()); + } + if (reference.m_pHead) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pHead->GetUniqueID()); + } + if (reference.m_pFGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFGLeg->GetUniqueID()); + } + if (reference.m_pFGArm) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFGArm->GetUniqueID()); + } - return 0; -} + Actor::Create(reference); + // Note - hardcoded attachable copying is organized based on desired draw order here. + if (reference.m_pBGArm) { + SetBGArm(dynamic_cast(reference.m_pBGArm->Clone())); + } + if (reference.m_pBGLeg) { + SetBGLeg(dynamic_cast(reference.m_pBGLeg->Clone())); + } + if (reference.m_pJetpack) { + SetJetpack(dynamic_cast(reference.m_pJetpack->Clone())); + } + if (reference.m_pHead) { + SetHead(dynamic_cast(reference.m_pHead->Clone())); + } + if (reference.m_pFGLeg) { + SetFGLeg(dynamic_cast(reference.m_pFGLeg->Clone())); + } + if (reference.m_pFGArm) { + SetFGArm(dynamic_cast(reference.m_pFGArm->Clone())); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a AHuman to be identical to another, by deep copy. - -int AHuman::Create(const AHuman &reference) { - if (reference.m_pBGArm) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pBGArm->GetUniqueID()); } - if (reference.m_pBGLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pBGLeg->GetUniqueID()); } - if (reference.m_pJetpack) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pJetpack->GetUniqueID()); } - if (reference.m_pHead) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pHead->GetUniqueID()); } - if (reference.m_pFGLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFGLeg->GetUniqueID()); } - if (reference.m_pFGArm) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFGArm->GetUniqueID()); } - - Actor::Create(reference); - - //Note - hardcoded attachable copying is organized based on desired draw order here. - if (reference.m_pBGArm) { SetBGArm(dynamic_cast(reference.m_pBGArm->Clone())); } - if (reference.m_pBGLeg) { SetBGLeg(dynamic_cast(reference.m_pBGLeg->Clone())); } - if (reference.m_pJetpack) { SetJetpack(dynamic_cast(reference.m_pJetpack->Clone())); } - if (reference.m_pHead) { SetHead(dynamic_cast(reference.m_pHead->Clone())); } - if (reference.m_pFGLeg) { SetFGLeg(dynamic_cast(reference.m_pFGLeg->Clone())); } - if (reference.m_pFGArm) { SetFGArm(dynamic_cast(reference.m_pFGArm->Clone())); } - - m_LookToAimRatio = reference.m_LookToAimRatio; - - m_ThrowPrepTime = reference.m_ThrowPrepTime; - m_WaitingToReloadOffhand = reference.m_WaitingToReloadOffhand; - m_FGArmFlailScalar = reference.m_FGArmFlailScalar; - m_BGArmFlailScalar = reference.m_BGArmFlailScalar; - m_ArmSwingRate = reference.m_ArmSwingRate; - m_DeviceArmSwayRate = reference.m_DeviceArmSwayRate; - - m_pFGHandGroup = dynamic_cast(reference.m_pFGHandGroup->Clone()); - m_pFGHandGroup->SetOwner(this); - m_pBGHandGroup = dynamic_cast(reference.m_pBGHandGroup->Clone()); - m_pBGHandGroup->SetOwner(this); - - AtomGroup *atomGroupToUseAsFootGroupFG = reference.m_pFGFootGroup ? dynamic_cast(reference.m_pFGFootGroup->Clone()) : m_pFGLeg->GetFootGroupFromFootAtomGroup(); - RTEAssert(atomGroupToUseAsFootGroupFG, "Failed to fallback to using FGFoot AtomGroup as FGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a FGFootGroup or FGLeg Foot attachable!"); - - AtomGroup *atomGroupToUseAsFootGroupBG = reference.m_pBGFootGroup ? dynamic_cast(reference.m_pBGFootGroup->Clone()) : m_pBGLeg->GetFootGroupFromFootAtomGroup();; - RTEAssert(atomGroupToUseAsFootGroupBG, "Failed to fallback to using BGFoot AtomGroup as BGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a BGFootGroup or BGLeg Foot attachable!"); - - m_pFGFootGroup = atomGroupToUseAsFootGroupFG; - m_pFGFootGroup->SetOwner(this); - m_BackupFGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupFG->Clone()); - m_BackupFGFootGroup->RemoveAllAtoms(); - m_BackupFGFootGroup->SetOwner(this); - m_BackupFGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupFG->GetLimbPos()); - m_pBGFootGroup = atomGroupToUseAsFootGroupBG; - m_pBGFootGroup->SetOwner(this); - m_BackupBGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupBG->Clone()); - m_BackupBGFootGroup->RemoveAllAtoms(); - m_BackupBGFootGroup->SetOwner(this); - m_BackupBGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupBG->GetLimbPos()); - - m_MaxWalkPathCrouchShift = reference.m_MaxWalkPathCrouchShift; - m_MaxCrouchRotation = reference.m_MaxCrouchRotation; - - if (reference.m_StrideSound) { m_StrideSound = dynamic_cast(reference.m_StrideSound->Clone()); } - - m_ArmsState = reference.m_ArmsState; - m_MoveState = reference.m_MoveState; - m_ProneState = reference.m_ProneState; - - for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { - m_Paths[FGROUND][i].Create(reference.m_Paths[FGROUND][i]); - m_Paths[BGROUND][i].Create(reference.m_Paths[BGROUND][i]); - m_RotAngleTargets[i] = reference.m_RotAngleTargets[i]; - } - - m_DeviceState = reference.m_DeviceState; - m_SweepState = reference.m_SweepState; - m_DigState = reference.m_DigState; - m_JumpState = reference.m_JumpState; - m_JumpTarget = reference.m_JumpTarget; - m_JumpingRight = reference.m_JumpingRight; - m_Crawling = reference.m_Crawling; - m_DigTunnelEndPos = reference.m_DigTunnelEndPos; - m_SweepCenterAimAngle = reference.m_SweepCenterAimAngle; - m_SweepRange = reference.m_SweepRange; - - return 0; -} + m_LookToAimRatio = reference.m_LookToAimRatio; + + m_ThrowPrepTime = reference.m_ThrowPrepTime; + m_WaitingToReloadOffhand = reference.m_WaitingToReloadOffhand; + m_FGArmFlailScalar = reference.m_FGArmFlailScalar; + m_BGArmFlailScalar = reference.m_BGArmFlailScalar; + m_ArmSwingRate = reference.m_ArmSwingRate; + m_DeviceArmSwayRate = reference.m_DeviceArmSwayRate; + + m_pFGHandGroup = dynamic_cast(reference.m_pFGHandGroup->Clone()); + m_pFGHandGroup->SetOwner(this); + m_pBGHandGroup = dynamic_cast(reference.m_pBGHandGroup->Clone()); + m_pBGHandGroup->SetOwner(this); + + AtomGroup* atomGroupToUseAsFootGroupFG = reference.m_pFGFootGroup ? dynamic_cast(reference.m_pFGFootGroup->Clone()) : m_pFGLeg->GetFootGroupFromFootAtomGroup(); + RTEAssert(atomGroupToUseAsFootGroupFG, "Failed to fallback to using FGFoot AtomGroup as FGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a FGFootGroup or FGLeg Foot attachable!"); + + AtomGroup* atomGroupToUseAsFootGroupBG = reference.m_pBGFootGroup ? dynamic_cast(reference.m_pBGFootGroup->Clone()) : m_pBGLeg->GetFootGroupFromFootAtomGroup(); + ; + RTEAssert(atomGroupToUseAsFootGroupBG, "Failed to fallback to using BGFoot AtomGroup as BGFootGroup in preset " + this->GetModuleAndPresetName() + "!\nPlease define a BGFootGroup or BGLeg Foot attachable!"); + + m_pFGFootGroup = atomGroupToUseAsFootGroupFG; + m_pFGFootGroup->SetOwner(this); + m_BackupFGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupFG->Clone()); + m_BackupFGFootGroup->RemoveAllAtoms(); + m_BackupFGFootGroup->SetOwner(this); + m_BackupFGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupFG->GetLimbPos()); + m_pBGFootGroup = atomGroupToUseAsFootGroupBG; + m_pBGFootGroup->SetOwner(this); + m_BackupBGFootGroup = dynamic_cast(atomGroupToUseAsFootGroupBG->Clone()); + m_BackupBGFootGroup->RemoveAllAtoms(); + m_BackupBGFootGroup->SetOwner(this); + m_BackupBGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupBG->GetLimbPos()); + + m_MaxWalkPathCrouchShift = reference.m_MaxWalkPathCrouchShift; + m_MaxCrouchRotation = reference.m_MaxCrouchRotation; + + if (reference.m_StrideSound) { + m_StrideSound = dynamic_cast(reference.m_StrideSound->Clone()); + } + m_ArmsState = reference.m_ArmsState; + m_MoveState = reference.m_MoveState; + m_ProneState = reference.m_ProneState; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int AHuman::ReadProperty(const std::string_view &propName, Reader &reader) { - StartPropertyList(return Actor::ReadProperty(propName, reader)); - - MatchProperty("ThrowPrepTime", { reader >> m_ThrowPrepTime; }); - MatchProperty("Head", { SetHead(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("LookToAimRatio", { reader >> m_LookToAimRatio; }); - MatchProperty("Jetpack", { SetJetpack(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("FGArmFlailScalar", { reader >> m_FGArmFlailScalar; }); - MatchProperty("BGArmFlailScalar", { reader >> m_BGArmFlailScalar; }); - MatchProperty("ArmSwingRate", { reader >> m_ArmSwingRate; }); - MatchProperty("DeviceArmSwayRate", { reader >> m_DeviceArmSwayRate; }); - MatchProperty("FGArm", { SetFGArm(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("BGArm", { SetBGArm(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("FGLeg", { SetFGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("BGLeg", { SetBGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("HandGroup", { - delete m_pFGHandGroup; - delete m_pBGHandGroup; - m_pFGHandGroup = new AtomGroup(); - m_pBGHandGroup = new AtomGroup(); - reader >> m_pFGHandGroup; - m_pBGHandGroup->Create(*m_pFGHandGroup); - m_pFGHandGroup->SetOwner(this); - m_pBGHandGroup->SetOwner(this); - }); - MatchProperty("FGFootGroup", { - delete m_pFGFootGroup; - m_pFGFootGroup = new AtomGroup(); - reader >> m_pFGFootGroup; - m_pFGFootGroup->SetOwner(this); - m_BackupFGFootGroup = new AtomGroup(*m_pFGFootGroup); - m_BackupFGFootGroup->RemoveAllAtoms(); - }); - MatchProperty("BGFootGroup", { - delete m_pBGFootGroup; - m_pBGFootGroup = new AtomGroup(); - reader >> m_pBGFootGroup; - m_pBGFootGroup->SetOwner(this); - m_BackupBGFootGroup = new AtomGroup(*m_pBGFootGroup); - m_BackupBGFootGroup->RemoveAllAtoms(); - }); - MatchProperty("MaxWalkPathCrouchShift", { reader >> m_MaxWalkPathCrouchShift; }); - MatchProperty("MaxCrouchRotation", { reader >> m_MaxCrouchRotation; }); - MatchProperty("StrideSound", { - m_StrideSound = new SoundContainer; - reader >> m_StrideSound; - }); - MatchProperty("StandLimbPath", { reader >> m_Paths[FGROUND][STAND]; }); - MatchProperty("StandLimbPathBG", { reader >> m_Paths[BGROUND][STAND]; }); - MatchProperty("WalkLimbPath", { reader >> m_Paths[FGROUND][WALK]; }); - MatchProperty("CrouchLimbPath", { reader >> m_Paths[FGROUND][CROUCH]; }); - MatchProperty("CrouchLimbPathBG", { reader >> m_Paths[BGROUND][CROUCH]; }); - MatchProperty("CrawlLimbPath", { reader >> m_Paths[FGROUND][CRAWL]; }); - MatchProperty("ArmCrawlLimbPath", { reader >> m_Paths[FGROUND][ARMCRAWL]; }); - MatchProperty("ClimbLimbPath", { reader >> m_Paths[FGROUND][CLIMB]; }); - MatchProperty("JumpLimbPath", { reader >> m_Paths[FGROUND][JUMP]; }); - MatchProperty("DislodgeLimbPath", { reader >> m_Paths[FGROUND][DISLODGE]; }); - MatchProperty("StandRotAngleTarget", { reader >> m_RotAngleTargets[STAND]; }); - MatchProperty("WalkRotAngleTarget", { reader >> m_RotAngleTargets[WALK]; }); - MatchProperty("CrouchRotAngleTarget", { reader >> m_RotAngleTargets[CROUCH]; }); - MatchProperty("JumpRotAngleTarget", { reader >> m_RotAngleTargets[JUMP]; }); - - - EndPropertyList; -} + for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { + m_Paths[FGROUND][i].Create(reference.m_Paths[FGROUND][i]); + m_Paths[BGROUND][i].Create(reference.m_Paths[BGROUND][i]); + m_RotAngleTargets[i] = reference.m_RotAngleTargets[i]; + } + m_DeviceState = reference.m_DeviceState; + m_SweepState = reference.m_SweepState; + m_DigState = reference.m_DigState; + m_JumpState = reference.m_JumpState; + m_JumpTarget = reference.m_JumpTarget; + m_JumpingRight = reference.m_JumpingRight; + m_Crawling = reference.m_Crawling; + m_DigTunnelEndPos = reference.m_DigTunnelEndPos; + m_SweepCenterAimAngle = reference.m_SweepCenterAimAngle; + m_SweepRange = reference.m_SweepRange; + + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this AHuman with a Writer for -// later recreation with Create(Reader &reader); - -int AHuman::Save(Writer &writer) const -{ - Actor::Save(writer); - - writer.NewProperty("ThrowPrepTime"); - writer << m_ThrowPrepTime; - writer.NewProperty("Head"); - writer << m_pHead; - writer.NewProperty("LookToAimRatio"); - writer << m_LookToAimRatio; - writer.NewProperty("Jetpack"); - writer << m_pJetpack; - writer.NewProperty("FGArmFlailScalar"); - writer << m_FGArmFlailScalar; - writer.NewProperty("BGArmFlailScalar"); - writer << m_BGArmFlailScalar; - writer.NewProperty("ArmSwingRate"); - writer << m_ArmSwingRate; - writer.NewPropertyWithValue("DeviceArmSwayRate", m_DeviceArmSwayRate); - writer.NewProperty("FGArm"); - writer << m_pFGArm; - writer.NewProperty("BGArm"); - writer << m_pBGArm; - writer.NewProperty("FGLeg"); - writer << m_pFGLeg; - writer.NewProperty("BGLeg"); - writer << m_pBGLeg; - writer.NewProperty("HandGroup"); - writer << m_pFGHandGroup; - writer.NewProperty("FGFootGroup"); - writer << m_pFGFootGroup; - writer.NewProperty("BGFootGroup"); - writer << m_pBGFootGroup; - writer.NewProperty("MaxWalkPathCrouchShift"); - writer << m_MaxWalkPathCrouchShift; - writer.NewProperty("MaxCrouchRotation"); - writer << m_MaxCrouchRotation; - writer.NewProperty("StrideSound"); - writer << m_StrideSound; - - writer.NewProperty("StandLimbPath"); - writer << m_Paths[FGROUND][STAND]; - writer.NewProperty("StandLimbPathBG"); - writer << m_Paths[BGROUND][STAND]; - writer.NewProperty("WalkLimbPath"); - writer << m_Paths[FGROUND][WALK]; - writer.NewProperty("CrouchLimbPath"); - writer << m_Paths[FGROUND][CROUCH]; - writer.NewProperty("CrawlLimbPath"); - writer << m_Paths[FGROUND][CRAWL]; - writer.NewProperty("ArmCrawlLimbPath"); - writer << m_Paths[FGROUND][ARMCRAWL]; - writer.NewProperty("ClimbLimbPath"); - writer << m_Paths[FGROUND][CLIMB]; - writer.NewProperty("JumpLimbPath"); - writer << m_Paths[FGROUND][JUMP]; - writer.NewProperty("DislodgeLimbPath"); - writer << m_Paths[FGROUND][DISLODGE]; - - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int AHuman::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Actor::ReadProperty(propName, reader)); + + MatchProperty("ThrowPrepTime", { reader >> m_ThrowPrepTime; }); + MatchProperty("Head", { SetHead(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("LookToAimRatio", { reader >> m_LookToAimRatio; }); + MatchProperty("Jetpack", { SetJetpack(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("FGArmFlailScalar", { reader >> m_FGArmFlailScalar; }); + MatchProperty("BGArmFlailScalar", { reader >> m_BGArmFlailScalar; }); + MatchProperty("ArmSwingRate", { reader >> m_ArmSwingRate; }); + MatchProperty("DeviceArmSwayRate", { reader >> m_DeviceArmSwayRate; }); + MatchProperty("FGArm", { SetFGArm(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("BGArm", { SetBGArm(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("FGLeg", { SetFGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("BGLeg", { SetBGLeg(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("HandGroup", { + delete m_pFGHandGroup; + delete m_pBGHandGroup; + m_pFGHandGroup = new AtomGroup(); + m_pBGHandGroup = new AtomGroup(); + reader >> m_pFGHandGroup; + m_pBGHandGroup->Create(*m_pFGHandGroup); + m_pFGHandGroup->SetOwner(this); + m_pBGHandGroup->SetOwner(this); + }); + MatchProperty("FGFootGroup", { + delete m_pFGFootGroup; + m_pFGFootGroup = new AtomGroup(); + reader >> m_pFGFootGroup; + m_pFGFootGroup->SetOwner(this); + m_BackupFGFootGroup = new AtomGroup(*m_pFGFootGroup); + m_BackupFGFootGroup->RemoveAllAtoms(); + }); + MatchProperty("BGFootGroup", { + delete m_pBGFootGroup; + m_pBGFootGroup = new AtomGroup(); + reader >> m_pBGFootGroup; + m_pBGFootGroup->SetOwner(this); + m_BackupBGFootGroup = new AtomGroup(*m_pBGFootGroup); + m_BackupBGFootGroup->RemoveAllAtoms(); + }); + MatchProperty("MaxWalkPathCrouchShift", { reader >> m_MaxWalkPathCrouchShift; }); + MatchProperty("MaxCrouchRotation", { reader >> m_MaxCrouchRotation; }); + MatchProperty("StrideSound", { + m_StrideSound = new SoundContainer; + reader >> m_StrideSound; + }); + MatchProperty("StandLimbPath", { reader >> m_Paths[FGROUND][STAND]; }); + MatchProperty("StandLimbPathBG", { reader >> m_Paths[BGROUND][STAND]; }); + MatchProperty("WalkLimbPath", { reader >> m_Paths[FGROUND][WALK]; }); + MatchProperty("CrouchLimbPath", { reader >> m_Paths[FGROUND][CROUCH]; }); + MatchProperty("CrouchLimbPathBG", { reader >> m_Paths[BGROUND][CROUCH]; }); + MatchProperty("CrawlLimbPath", { reader >> m_Paths[FGROUND][CRAWL]; }); + MatchProperty("ArmCrawlLimbPath", { reader >> m_Paths[FGROUND][ARMCRAWL]; }); + MatchProperty("ClimbLimbPath", { reader >> m_Paths[FGROUND][CLIMB]; }); + MatchProperty("JumpLimbPath", { reader >> m_Paths[FGROUND][JUMP]; }); + MatchProperty("DislodgeLimbPath", { reader >> m_Paths[FGROUND][DISLODGE]; }); + MatchProperty("StandRotAngleTarget", { reader >> m_RotAngleTargets[STAND]; }); + MatchProperty("WalkRotAngleTarget", { reader >> m_RotAngleTargets[WALK]; }); + MatchProperty("CrouchRotAngleTarget", { reader >> m_RotAngleTargets[CROUCH]; }); + MatchProperty("JumpRotAngleTarget", { reader >> m_RotAngleTargets[JUMP]; }); + + EndPropertyList; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this AHuman with a Writer for + // later recreation with Create(Reader &reader); + + int AHuman::Save(Writer& writer) const { + Actor::Save(writer); + + writer.NewProperty("ThrowPrepTime"); + writer << m_ThrowPrepTime; + writer.NewProperty("Head"); + writer << m_pHead; + writer.NewProperty("LookToAimRatio"); + writer << m_LookToAimRatio; + writer.NewProperty("Jetpack"); + writer << m_pJetpack; + writer.NewProperty("FGArmFlailScalar"); + writer << m_FGArmFlailScalar; + writer.NewProperty("BGArmFlailScalar"); + writer << m_BGArmFlailScalar; + writer.NewProperty("ArmSwingRate"); + writer << m_ArmSwingRate; + writer.NewPropertyWithValue("DeviceArmSwayRate", m_DeviceArmSwayRate); + writer.NewProperty("FGArm"); + writer << m_pFGArm; + writer.NewProperty("BGArm"); + writer << m_pBGArm; + writer.NewProperty("FGLeg"); + writer << m_pFGLeg; + writer.NewProperty("BGLeg"); + writer << m_pBGLeg; + writer.NewProperty("HandGroup"); + writer << m_pFGHandGroup; + writer.NewProperty("FGFootGroup"); + writer << m_pFGFootGroup; + writer.NewProperty("BGFootGroup"); + writer << m_pBGFootGroup; + writer.NewProperty("MaxWalkPathCrouchShift"); + writer << m_MaxWalkPathCrouchShift; + writer.NewProperty("MaxCrouchRotation"); + writer << m_MaxCrouchRotation; + writer.NewProperty("StrideSound"); + writer << m_StrideSound; + + writer.NewProperty("StandLimbPath"); + writer << m_Paths[FGROUND][STAND]; + writer.NewProperty("StandLimbPathBG"); + writer << m_Paths[BGROUND][STAND]; + writer.NewProperty("WalkLimbPath"); + writer << m_Paths[FGROUND][WALK]; + writer.NewProperty("CrouchLimbPath"); + writer << m_Paths[FGROUND][CROUCH]; + writer.NewProperty("CrawlLimbPath"); + writer << m_Paths[FGROUND][CRAWL]; + writer.NewProperty("ArmCrawlLimbPath"); + writer << m_Paths[FGROUND][ARMCRAWL]; + writer.NewProperty("ClimbLimbPath"); + writer << m_Paths[FGROUND][CLIMB]; + writer.NewProperty("JumpLimbPath"); + writer << m_Paths[FGROUND][JUMP]; + writer.NewProperty("DislodgeLimbPath"); + writer << m_Paths[FGROUND][DISLODGE]; + + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AHuman object. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the AHuman object. -void AHuman::Destroy(bool notInherited) { - delete m_pFGHandGroup; - delete m_pBGHandGroup; - delete m_pFGFootGroup; - delete m_pBGFootGroup; + void AHuman::Destroy(bool notInherited) { + delete m_pFGHandGroup; + delete m_pBGHandGroup; + delete m_pFGFootGroup; + delete m_pBGFootGroup; - delete m_StrideSound; + delete m_StrideSound; - if (!notInherited) { Actor::Destroy(); } - Clear(); -} + if (!notInherited) { + Actor::Destroy(); + } + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of this Actor and all its carried + // gold and inventory. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of this Actor and all its carried -// gold and inventory. + float AHuman::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const { + float totalValue = Actor::GetTotalValue(nativeModule, foreignMult, nativeMult); -float AHuman::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const -{ - float totalValue = Actor::GetTotalValue(nativeModule, foreignMult, nativeMult); + // If holding something, then add its value, too + if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) + totalValue += m_pFGArm->GetHeldDevice()->GetTotalValue(nativeModule, foreignMult, nativeMult); - // If holding something, then add its value, too - if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) - totalValue += m_pFGArm->GetHeldDevice()->GetTotalValue(nativeModule, foreignMult, nativeMult); + return totalValue; + } - return totalValue; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this carries a specifically named object in its inventory. + // Also looks through the inventories of potential passengers, as applicable. + bool AHuman::HasObject(std::string objectName) const { + bool found = Actor::HasObject(objectName); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this carries a specifically named object in its inventory. -// Also looks through the inventories of potential passengers, as applicable. + // If holding something, then check that too + if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) + found = found || m_pFGArm->GetHeldDevice()->HasObject(objectName); + if (m_pBGArm && m_pBGArm->IsAttached() && m_pBGArm->GetHeldDevice()) + found = found || m_pBGArm->GetHeldDevice()->HasObject(objectName); -bool AHuman::HasObject(std::string objectName) const -{ - bool found = Actor::HasObject(objectName); + return found; + } - // If holding something, then check that too - if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) - found = found || m_pFGArm->GetHeldDevice()->HasObject(objectName); - if (m_pBGArm && m_pBGArm->IsAttached() && m_pBGArm->GetHeldDevice()) - found = found || m_pBGArm->GetHeldDevice()->HasObject(objectName); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObjectInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically grouped object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. - return found; -} + bool AHuman::HasObjectInGroup(std::string groupName) const { + bool found = Actor::HasObjectInGroup(groupName); + // If holding something, then check that too + if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) + found = found || m_pFGArm->GetHeldDevice()->HasObjectInGroup(groupName); + if (m_pBGArm && m_pBGArm->IsAttached() && m_pBGArm->GetHeldDevice()) + found = found || m_pBGArm->GetHeldDevice()->HasObjectInGroup(groupName); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObjectInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically grouped object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. + return found; + } -bool AHuman::HasObjectInGroup(std::string groupName) const -{ - bool found = Actor::HasObjectInGroup(groupName); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetCPUPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of this' brain, or equivalent. - // If holding something, then check that too - if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) - found = found || m_pFGArm->GetHeldDevice()->HasObjectInGroup(groupName); - if (m_pBGArm && m_pBGArm->IsAttached() && m_pBGArm->GetHeldDevice()) - found = found || m_pBGArm->GetHeldDevice()->HasObjectInGroup(groupName); + Vector AHuman::GetCPUPos() const { + if (m_pHead && m_pHead->IsAttached()) + return m_Pos + ((m_pHead->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation) * 1.5); - return found; -} + return m_Pos; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetEyePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of this' eye, or equivalent, where look + // vector starts from. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetCPUPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of this' brain, or equivalent. + Vector AHuman::GetEyePos() const { + if (m_pHead && m_pHead->IsAttached()) { + return m_Pos + m_pHead->GetParentOffset() * 1.2F; + } -Vector AHuman::GetCPUPos() const -{ - if (m_pHead && m_pHead->IsAttached()) - return m_Pos + ((m_pHead->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation) * 1.5); + return m_Pos; + } - return m_Pos; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetEyePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of this' eye, or equivalent, where look -// vector starts from. + void AHuman::SetHead(Attachable* newHead) { + if (m_pHead && m_pHead->IsAttached()) { + RemoveAndDeleteAttachable(m_pHead); + } + if (newHead == nullptr) { + m_pHead = nullptr; + } else { + m_pHead = newHead; + AddAttachable(newHead); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newHead->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + dynamic_cast(parent)->SetHead(attachable); + }}); -Vector AHuman::GetEyePos() const -{ - if (m_pHead && m_pHead->IsAttached()) { - return m_Pos + m_pHead->GetParentOffset() * 1.2F; + if (m_pHead->HasNoSetDamageMultiplier()) { + m_pHead->SetDamageMultiplier(4.0F); + } + if (m_pHead->IsDrawnAfterParent()) { + m_pHead->SetDrawnNormallyByParent(false); + } + m_pHead->SetInheritsRotAngle(false); + } } - return m_Pos; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void AHuman::SetHead(Attachable *newHead) { - if (m_pHead && m_pHead->IsAttached()) { RemoveAndDeleteAttachable(m_pHead); } - if (newHead == nullptr) { - m_pHead = nullptr; - } else { - m_pHead = newHead; - AddAttachable(newHead); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newHead->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - dynamic_cast(parent)->SetHead(attachable); - }}); - - if (m_pHead->HasNoSetDamageMultiplier()) { m_pHead->SetDamageMultiplier(4.0F); } - if (m_pHead->IsDrawnAfterParent()) { m_pHead->SetDrawnNormallyByParent(false); } - m_pHead->SetInheritsRotAngle(false); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void AHuman::SetJetpack(AEJetpack *newJetpack) { - if (m_pJetpack && m_pJetpack->IsAttached()) { RemoveAndDeleteAttachable(m_pJetpack); } - if (newJetpack == nullptr) { - m_pJetpack = nullptr; - } else { - m_pJetpack = newJetpack; - AddAttachable(newJetpack); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newJetpack->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - AEJetpack *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetJetpack"); - dynamic_cast(parent)->SetJetpack(castedAttachable); - }}); - - if (m_pJetpack->HasNoSetDamageMultiplier()) { m_pJetpack->SetDamageMultiplier(0.0F); } - m_pJetpack->SetApplyTransferredForcesAtOffset(false); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void AHuman::SetFGArm(Arm *newArm) { - if (m_pFGArm && m_pFGArm->IsAttached()) { RemoveAndDeleteAttachable(m_pFGArm); } - if (newArm == nullptr) { - m_pFGArm = nullptr; - } else { - m_pFGArm = newArm; - AddAttachable(newArm); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newArm->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Arm *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetFGArm"); - dynamic_cast(parent)->SetFGArm(castedAttachable); - }}); - - if (m_pFGArm->HasNoSetDamageMultiplier()) { m_pFGArm->SetDamageMultiplier(1.0F); } - m_pFGArm->SetDrawnAfterParent(true); - m_pFGArm->SetDrawnNormallyByParent(false); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void AHuman::SetBGArm(Arm *newArm) { - if (m_pBGArm && m_pBGArm->IsAttached()) { RemoveAndDeleteAttachable(m_pBGArm); } - if (newArm == nullptr) { - m_pBGArm = nullptr; - } else { - m_pBGArm = newArm; - AddAttachable(newArm); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newArm->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Arm *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetBGArm"); - dynamic_cast(parent)->SetBGArm(castedAttachable); - }}); - - if (m_pBGArm->HasNoSetDamageMultiplier()) { m_pBGArm->SetDamageMultiplier(1.0F); } - m_pBGArm->SetDrawnAfterParent(false); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void AHuman::SetFGLeg(Leg *newLeg) { - if (m_pFGLeg && m_pFGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pFGLeg); } - if (newLeg == nullptr) { - m_pFGLeg = nullptr; - } else { - m_pFGLeg = newLeg; - AddAttachable(newLeg); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Leg *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetFGLeg"); - dynamic_cast(parent)->SetFGLeg(castedAttachable); - }}); - - if (m_pFGLeg->HasNoSetDamageMultiplier()) { m_pFGLeg->SetDamageMultiplier(1.0F); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void AHuman::SetBGLeg(Leg *newLeg) { - if (m_pBGLeg && m_pBGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pBGLeg); } - if (newLeg == nullptr) { - m_pBGLeg = nullptr; - } else { - m_pBGLeg = newLeg; - AddAttachable(newLeg); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Leg *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetBGLeg"); - dynamic_cast(parent)->SetBGLeg(castedAttachable); - }}); - - if (m_pBGLeg->HasNoSetDamageMultiplier()) { m_pBGLeg->SetDamageMultiplier(1.0F); } - m_pBGLeg->SetDrawnAfterParent(false); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -BITMAP * AHuman::GetGraphicalIcon() const { - return m_GraphicalIcon ? m_GraphicalIcon : (m_pHead ? m_pHead->GetSpriteFrame(0) : GetSpriteFrame(0)); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void AHuman::SetJetpack(AEJetpack* newJetpack) { + if (m_pJetpack && m_pJetpack->IsAttached()) { + RemoveAndDeleteAttachable(m_pJetpack); + } + if (newJetpack == nullptr) { + m_pJetpack = nullptr; + } else { + m_pJetpack = newJetpack; + AddAttachable(newJetpack); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. - -bool AHuman::CollideAtPoint(HitData &hd) -{ - return Actor::CollideAtPoint(hd); - -/* - hd.ResImpulse[HITOR].Reset(); - hd.ResImpulse[HITEE].Reset(); - hd.HitRadius[HITEE] = (hd.HitPoint - m_Pos) * c_MPP; - hd.mass[HITEE] = m_Mass; - hd.MomInertia[HITEE] = m_pAtomGroup->GetMomentOfInertia(); - hd.HitVel[HITEE] = m_Vel + hd.HitRadius[HITEE].GetPerpendicular() * m_AngularVel; - hd.VelDiff = hd.HitVel[HITOR] - hd.HitVel[HITEE]; - Vector hitAcc = -hd.VelDiff * (1 + hd.Body[HITOR]->GetMaterial().restitution * GetMaterial().restitution); - - float hittorLever = hd.HitRadius[HITOR].GetPerpendicular().Dot(hd.BitmapNormal); - float hitteeLever = hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.BitmapNormal); - hittorLever *= hittorLever; - hitteeLever *= hitteeLever; - float impulse = hitAcc.Dot(hd.BitmapNormal) / (((1 / hd.mass[HITOR]) + (1 / hd.mass[HITEE])) + - (hittorLever / hd.MomInertia[HITOR]) + (hitteeLever / hd.MomInertia[HITEE])); - - hd.ResImpulse[HITOR] = hd.BitmapNormal * impulse * hd.ImpulseFactor[HITOR]; - hd.ResImpulse[HITEE] = hd.BitmapNormal * -impulse * hd.ImpulseFactor[HITEE]; - - //////////////////////////////////////////////////////////////////////////////// - // If a particle, which does not penetrate, but bounces, do any additional - // effects of that bounce. - if (!ParticlePenetration()) -// TODO: Add blunt trauma effects here!") - ; - } - - m_Vel += hd.ResImpulse[HITEE] / hd.mass[HITEE]; - m_AngularVel += hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.ResImpulse[HITEE]) / - hd.MomInertia[HITEE]; -*/ -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnBounce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits and then -// bounces off of something. This is called by the owned Atom/AtomGroup -// of this MovableObject during travel. + m_HardcodedAttachableUniqueIDsAndSetters.insert({newJetpack->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + AEJetpack* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetJetpack"); + dynamic_cast(parent)->SetJetpack(castedAttachable); + }}); -bool AHuman::OnBounce(const Vector &pos) -{ - return false; -} + if (m_pJetpack->HasNoSetDamageMultiplier()) { + m_pJetpack->SetDamageMultiplier(0.0F); + } + m_pJetpack->SetApplyTransferredForcesAtOffset(false); + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnSink -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits and then -// sink into something. This is called by the owned Atom/AtomGroup -// of this MovableObject during travel. - -bool AHuman::OnSink(const Vector &pos) -{ - return false; -} -*/ - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool AHuman::HandlePieCommand(PieSlice::SliceType pieSliceIndex) { - if (pieSliceIndex != PieSlice::SliceType::NoType) { - if (pieSliceIndex == PieSlice::SliceType::Pickup) { - m_Controller.SetState(WEAPON_PICKUP); - } else if (pieSliceIndex == PieSlice::SliceType::Drop) { - m_Controller.SetState(WEAPON_DROP); - } else if (pieSliceIndex == PieSlice::SliceType::Reload) { - m_Controller.SetState(WEAPON_RELOAD); - } else if (pieSliceIndex == PieSlice::SliceType::NextItem) { - m_Controller.SetState(WEAPON_CHANGE_NEXT, true); - } else if (pieSliceIndex == PieSlice::SliceType::PreviousItem) { - m_Controller.SetState(WEAPON_CHANGE_PREV, true); - } else if (pieSliceIndex == PieSlice::SliceType::Sentry) { - m_AIMode = AIMODE_SENTRY; - } else if (pieSliceIndex == PieSlice::SliceType::Patrol) { - m_AIMode = AIMODE_PATROL; - } else if (pieSliceIndex == PieSlice::SliceType::BrainHunt) { - m_AIMode = AIMODE_BRAINHUNT; - ClearAIWaypoints(); - } else if (pieSliceIndex == PieSlice::SliceType::GoTo) { - m_AIMode = AIMODE_GOTO; - ClearAIWaypoints(); - m_UpdateMovePath = true; - } else if (pieSliceIndex == PieSlice::SliceType::GoldDig) { - m_AIMode = AIMODE_GOLDDIG; - } else { - return Actor::HandlePieCommand(pieSliceIndex); - } - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void AHuman::SetFGArm(Arm* newArm) { + if (m_pFGArm && m_pFGArm->IsAttached()) { + RemoveAndDeleteAttachable(m_pFGArm); + } + if (newArm == nullptr) { + m_pFGArm = nullptr; + } else { + m_pFGArm = newArm; + AddAttachable(newArm); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newArm->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Arm* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetFGArm"); + dynamic_cast(parent)->SetFGArm(castedAttachable); + }}); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: AddInventoryItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an inventory item to this AHuman. This also puts that item -// directly in the hands of this if they are empty. - -void AHuman::AddInventoryItem(MovableObject *pItemToAdd) { - // If we have nothing in inventory, and nothing in our hands, just grab this first thing added to us. - if (HeldDevice *itemToAddAsHeldDevice = dynamic_cast(pItemToAdd); itemToAddAsHeldDevice && m_Inventory.empty() && m_pFGArm && m_pFGArm->IsAttached() && !m_pFGArm->GetHeldDevice()) { - m_pFGArm->SetHeldDevice(itemToAddAsHeldDevice); - m_pFGArm->SetHandPos(m_HolsterOffset.GetXFlipped(m_HFlipped)); - } else { - Actor::AddInventoryItem(pItemToAdd); + if (m_pFGArm->HasNoSetDamageMultiplier()) { + m_pFGArm->SetDamageMultiplier(1.0F); + } + m_pFGArm->SetDrawnAfterParent(true); + m_pFGArm->SetDrawnNormallyByParent(false); + } } - EquipShieldInBGArm(); -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void AHuman::SetBGArm(Arm* newArm) { + if (m_pBGArm && m_pBGArm->IsAttached()) { + RemoveAndDeleteAttachable(m_pBGArm); + } + if (newArm == nullptr) { + m_pBGArm = nullptr; + } else { + m_pBGArm = newArm; + AddAttachable(newArm); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + m_HardcodedAttachableUniqueIDsAndSetters.insert({newArm->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Arm* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetBGArm"); + dynamic_cast(parent)->SetBGArm(castedAttachable); + }}); -MovableObject * AHuman::SwapNextInventory(MovableObject *inventoryItemToSwapIn, bool muteSound) { - MovableObject *swappedInventoryItem = Actor::SwapNextInventory(inventoryItemToSwapIn, muteSound); - while (!dynamic_cast(swappedInventoryItem) && !m_Inventory.empty()) { - g_MovableMan.AddMO(swappedInventoryItem); - swappedInventoryItem = Actor::SwapNextInventory(nullptr, muteSound); + if (m_pBGArm->HasNoSetDamageMultiplier()) { + m_pBGArm->SetDamageMultiplier(1.0F); + } + m_pBGArm->SetDrawnAfterParent(false); + } } - return swappedInventoryItem; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void AHuman::SetFGLeg(Leg* newLeg) { + if (m_pFGLeg && m_pFGLeg->IsAttached()) { + RemoveAndDeleteAttachable(m_pFGLeg); + } + if (newLeg == nullptr) { + m_pFGLeg = nullptr; + } else { + m_pFGLeg = newLeg; + AddAttachable(newLeg); -MovableObject * AHuman::SwapPrevInventory(MovableObject *inventoryItemToSwapIn) { - MovableObject *swappedInventoryItem = Actor::SwapPrevInventory(inventoryItemToSwapIn); - while (!dynamic_cast(swappedInventoryItem) && !m_Inventory.empty()) { - g_MovableMan.AddMO(swappedInventoryItem); - swappedInventoryItem = Actor::SwapNextInventory(nullptr); - } + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Leg* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetFGLeg"); + dynamic_cast(parent)->SetFGLeg(castedAttachable); + }}); - return swappedInventoryItem; -} + if (m_pFGLeg->HasNoSetDamageMultiplier()) { + m_pFGLeg->SetDamageMultiplier(1.0F); + } + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void AHuman::SetBGLeg(Leg* newLeg) { + if (m_pBGLeg && m_pBGLeg->IsAttached()) { + RemoveAndDeleteAttachable(m_pBGLeg); + } + if (newLeg == nullptr) { + m_pBGLeg = nullptr; + } else { + m_pBGLeg = newLeg; + AddAttachable(newLeg); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipFirearm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first found firearm -// in the inventory. If the held device already is a firearm, or no -// firearm is in inventory, nothing happens. + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Leg* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetBGLeg"); + dynamic_cast(parent)->SetBGLeg(castedAttachable); + }}); -bool AHuman::EquipFirearm(bool doEquip) -{ - if (!(m_pFGArm && m_pFGArm->IsAttached())) { - return false; + if (m_pBGLeg->HasNoSetDamageMultiplier()) { + m_pBGLeg->SetDamageMultiplier(1.0F); + } + m_pBGLeg->SetDrawnAfterParent(false); + } } - if (HDFirearm *heldDeviceAsFirearm = dynamic_cast(m_pFGArm->GetHeldDevice()); heldDeviceAsFirearm && heldDeviceAsFirearm->IsWeapon()) { - return true; - } else { - UnequipBGArm(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + BITMAP* AHuman::GetGraphicalIcon() const { + return m_GraphicalIcon ? m_GraphicalIcon : (m_pHead ? m_pHead->GetSpriteFrame(0) : GetSpriteFrame(0)); } - // Go through the inventory looking for the proper device - for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - HDFirearm *pWeapon = dynamic_cast(*itr); - // Found proper device to equip, so make the switch! - if (pWeapon && pWeapon->IsWeapon()) - { - if (doEquip) - { - // Erase the inventory entry containing the device we now have switched to - *itr = 0; - m_Inventory.erase(itr); - - // Put back into the inventory what we had in our hands, if anything - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice()) { - heldDevice->Deactivate(); - AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); - } - - // Now put the device we were looking for and found into the hand - m_pFGArm->SetHeldDevice(pWeapon); - // Move the hand to a poisition so it looks like the new device was drawn from inventory - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - - // Equip shield in BG arm if applicable - EquipShieldInBGArm(); - - // Play the device switching sound - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - } - - return true; - } - } - - return false; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipDeviceInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first found device -// of the specified group in the inventory. If the held device already -// is of that group, or no device is in inventory, nothing happens. + bool AHuman::CollideAtPoint(HitData& hd) { + return Actor::CollideAtPoint(hd); -bool AHuman::EquipDeviceInGroup(std::string group, bool doEquip) -{ - if (!(m_pFGArm && m_pFGArm->IsAttached())) { - return false; + /* + hd.ResImpulse[HITOR].Reset(); + hd.ResImpulse[HITEE].Reset(); + hd.HitRadius[HITEE] = (hd.HitPoint - m_Pos) * c_MPP; + hd.mass[HITEE] = m_Mass; + hd.MomInertia[HITEE] = m_pAtomGroup->GetMomentOfInertia(); + hd.HitVel[HITEE] = m_Vel + hd.HitRadius[HITEE].GetPerpendicular() * m_AngularVel; + hd.VelDiff = hd.HitVel[HITOR] - hd.HitVel[HITEE]; + Vector hitAcc = -hd.VelDiff * (1 + hd.Body[HITOR]->GetMaterial().restitution * GetMaterial().restitution); + + float hittorLever = hd.HitRadius[HITOR].GetPerpendicular().Dot(hd.BitmapNormal); + float hitteeLever = hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.BitmapNormal); + hittorLever *= hittorLever; + hitteeLever *= hitteeLever; + float impulse = hitAcc.Dot(hd.BitmapNormal) / (((1 / hd.mass[HITOR]) + (1 / hd.mass[HITEE])) + + (hittorLever / hd.MomInertia[HITOR]) + (hitteeLever / hd.MomInertia[HITEE])); + + hd.ResImpulse[HITOR] = hd.BitmapNormal * impulse * hd.ImpulseFactor[HITOR]; + hd.ResImpulse[HITEE] = hd.BitmapNormal * -impulse * hd.ImpulseFactor[HITEE]; + + //////////////////////////////////////////////////////////////////////////////// + // If a particle, which does not penetrate, but bounces, do any additional + // effects of that bounce. + if (!ParticlePenetration()) + // TODO: Add blunt trauma effects here!") + ; + } + + m_Vel += hd.ResImpulse[HITEE] / hd.mass[HITEE]; + m_AngularVel += hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.ResImpulse[HITEE]) / + hd.MomInertia[HITEE]; + */ } - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice(); heldDevice && heldDevice->IsInGroup(group)) { - return true; - } - - // Go through the inventory looking for the proper device - for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - HeldDevice *pDevice = dynamic_cast(*itr); - // Found proper device to equip, so make the switch! - if (pDevice && pDevice->IsInGroup(group)) - { - if (doEquip) - { - // Erase the inventory entry containing the device we now have switched to - *itr = 0; - m_Inventory.erase(itr); - - // Put back into the inventory what we had in our hands, if anything - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice()) - { - heldDevice->Deactivate(); - MovableObject *previouslyHeldItem = m_pFGArm->RemoveAttachable(heldDevice); - if (previouslyHeldItem) { - // Note - This is a fix to deal with an edge case bug when this method is called by a global script. - // Because the global script runs before everything has finished traveling, the removed item needs to undraw itself from the MO layer, otherwise it can result in ghost collisions and crashes. - if (previouslyHeldItem->GetsHitByMOs()) { -#ifdef DRAW_MOID_LAYER - previouslyHeldItem->Draw(g_SceneMan.GetMOIDBitmap(), Vector(), g_DrawNoMOID, true); -#else - previouslyHeldItem->SetTraveling(true); -#endif - } - AddToInventoryBack(previouslyHeldItem); - } - } + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnBounce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits and then + // bounces off of something. This is called by the owned Atom/AtomGroup + // of this MovableObject during travel. - // Now put the device we were looking for and found into the hand - m_pFGArm->SetHeldDevice(pDevice); - // Move the hand to a poisition so it looks like the new device was drawn from inventory - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + bool AHuman::OnBounce(const Vector &pos) + { + return false; + } - // Equip shield in BG arm if applicable - EquipShieldInBGArm(); - // Play the device switching sound - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnSink + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits and then + // sink into something. This is called by the owned Atom/AtomGroup + // of this MovableObject during travel. + + bool AHuman::OnSink(const Vector &pos) + { + return false; + } + */ + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool AHuman::HandlePieCommand(PieSlice::SliceType pieSliceIndex) { + if (pieSliceIndex != PieSlice::SliceType::NoType) { + if (pieSliceIndex == PieSlice::SliceType::Pickup) { + m_Controller.SetState(WEAPON_PICKUP); + } else if (pieSliceIndex == PieSlice::SliceType::Drop) { + m_Controller.SetState(WEAPON_DROP); + } else if (pieSliceIndex == PieSlice::SliceType::Reload) { + m_Controller.SetState(WEAPON_RELOAD); + } else if (pieSliceIndex == PieSlice::SliceType::NextItem) { + m_Controller.SetState(WEAPON_CHANGE_NEXT, true); + } else if (pieSliceIndex == PieSlice::SliceType::PreviousItem) { + m_Controller.SetState(WEAPON_CHANGE_PREV, true); + } else if (pieSliceIndex == PieSlice::SliceType::Sentry) { + m_AIMode = AIMODE_SENTRY; + } else if (pieSliceIndex == PieSlice::SliceType::Patrol) { + m_AIMode = AIMODE_PATROL; + } else if (pieSliceIndex == PieSlice::SliceType::BrainHunt) { + m_AIMode = AIMODE_BRAINHUNT; + ClearAIWaypoints(); + } else if (pieSliceIndex == PieSlice::SliceType::GoTo) { + m_AIMode = AIMODE_GOTO; + ClearAIWaypoints(); + m_UpdateMovePath = true; + } else if (pieSliceIndex == PieSlice::SliceType::GoldDig) { + m_AIMode = AIMODE_GOLDDIG; + } else { + return Actor::HandlePieCommand(pieSliceIndex); } + } + return false; + } - return true; - } - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: AddInventoryItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an inventory item to this AHuman. This also puts that item + // directly in the hands of this if they are empty. + void AHuman::AddInventoryItem(MovableObject* pItemToAdd) { + // If we have nothing in inventory, and nothing in our hands, just grab this first thing added to us. + if (HeldDevice* itemToAddAsHeldDevice = dynamic_cast(pItemToAdd); itemToAddAsHeldDevice && m_Inventory.empty() && m_pFGArm && m_pFGArm->IsAttached() && !m_pFGArm->GetHeldDevice()) { + m_pFGArm->SetHeldDevice(itemToAddAsHeldDevice); + m_pFGArm->SetHandPos(m_HolsterOffset.GetXFlipped(m_HFlipped)); + } else { + Actor::AddInventoryItem(pItemToAdd); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipLoadedFirearmInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first loaded HDFirearm -// of the specified group in the inventory. If no such weapon is in the -// inventory, nothing happens. - -bool AHuman::EquipLoadedFirearmInGroup(std::string group, std::string excludeGroup, bool doEquip) -{ - if (!(m_pFGArm && m_pFGArm->IsAttached())) { - return false; + EquipShieldInBGArm(); } - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice(); heldDevice && !heldDevice->NeedsReloading() && heldDevice->IsInGroup(group) && !heldDevice->IsInGroup(excludeGroup)) { - return true; - } - - // Go through the inventory looking for the proper device - for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - HDFirearm *pFirearm = dynamic_cast(*itr); - // Found proper device to equip, so make the switch! - if (pFirearm && !pFirearm->NeedsReloading() && pFirearm->IsInGroup(group) && !pFirearm->IsInGroup(excludeGroup)) - { - if (doEquip) - { - // Erase the inventory entry containing the device we now have switched to - *itr = 0; - m_Inventory.erase(itr); - - // Put back into the inventory what we had in our hands, if anything - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice()) - { - m_pFGArm->GetHeldDevice()->Deactivate(); - AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Now put the device we were looking for and found into the hand - m_pFGArm->SetHeldDevice(pFirearm); - // Move the hand to a poisition so it looks like the new device was drawn from inventory - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + MovableObject* AHuman::SwapNextInventory(MovableObject* inventoryItemToSwapIn, bool muteSound) { + MovableObject* swappedInventoryItem = Actor::SwapNextInventory(inventoryItemToSwapIn, muteSound); + while (!dynamic_cast(swappedInventoryItem) && !m_Inventory.empty()) { + g_MovableMan.AddMO(swappedInventoryItem); + swappedInventoryItem = Actor::SwapNextInventory(nullptr, muteSound); + } - // Equip shield in BG arm if applicable - EquipShieldInBGArm(); + return swappedInventoryItem; + } - // Play the device switching sound - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return true; - } - } + MovableObject* AHuman::SwapPrevInventory(MovableObject* inventoryItemToSwapIn) { + MovableObject* swappedInventoryItem = Actor::SwapPrevInventory(inventoryItemToSwapIn); + while (!dynamic_cast(swappedInventoryItem) && !m_Inventory.empty()) { + g_MovableMan.AddMO(swappedInventoryItem); + swappedInventoryItem = Actor::SwapNextInventory(nullptr); + } - return false; -} + return swappedInventoryItem; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipNamedDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first found device -// of with the specified preset name in the inventory. If the held device already -// is of that preset name, or no device is in inventory, nothing happens. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipFirearm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first found firearm + // in the inventory. If the held device already is a firearm, or no + // firearm is in inventory, nothing happens. -bool AHuman::EquipNamedDevice(const std::string &moduleName, const std::string &presetName, bool doEquip) -{ - if (!(m_pFGArm && m_pFGArm->IsAttached())) { - return false; - } + bool AHuman::EquipFirearm(bool doEquip) { + if (!(m_pFGArm && m_pFGArm->IsAttached())) { + return false; + } - if (const HeldDevice *heldDevice = m_pFGArm->GetHeldDevice(); - heldDevice && (moduleName.empty() || heldDevice->GetModuleName() == moduleName) && heldDevice->GetPresetName() == presetName) { - return true; - } + if (HDFirearm* heldDeviceAsFirearm = dynamic_cast(m_pFGArm->GetHeldDevice()); heldDeviceAsFirearm && heldDeviceAsFirearm->IsWeapon()) { + return true; + } else { + UnequipBGArm(); + } - // Go through the inventory looking for the proper device - for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - HeldDevice *pDevice = dynamic_cast(*itr); - // Found proper device to equip, so make the switch! - if (pDevice && (moduleName.empty() || pDevice->GetModuleName() == moduleName) && pDevice->GetPresetName() == presetName) - { - if (doEquip) - { - // Erase the inventory entry containing the device we now have switched to - *itr = 0; - m_Inventory.erase(itr); + // Go through the inventory looking for the proper device + for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + HDFirearm* pWeapon = dynamic_cast(*itr); + // Found proper device to equip, so make the switch! + if (pWeapon && pWeapon->IsWeapon()) { + if (doEquip) { + // Erase the inventory entry containing the device we now have switched to + *itr = 0; + m_Inventory.erase(itr); + + // Put back into the inventory what we had in our hands, if anything + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice()) { + heldDevice->Deactivate(); + AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); + } - // Put back into the inventory what we had in our hands, if anything - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice()) - { - heldDevice->Deactivate(); - AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); - } + // Now put the device we were looking for and found into the hand + m_pFGArm->SetHeldDevice(pWeapon); + // Move the hand to a poisition so it looks like the new device was drawn from inventory + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - // Now put the device we were looking for and found into the hand - m_pFGArm->SetHeldDevice(pDevice); - // Move the hand to a poisition so it looks like the new device was drawn from inventory - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + // Equip shield in BG arm if applicable + EquipShieldInBGArm(); - // Equip shield in BG arm if applicable - EquipShieldInBGArm(); + // Play the device switching sound + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); + } + } - // Play the device switching sound - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } + return true; } + } - return true; - } - } - - return false; -} + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipDeviceInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first found device + // of the specified group in the inventory. If the held device already + // is of that group, or no device is in inventory, nothing happens. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipThrowable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first found ThrownDevice -// in the inventory. If the held device already is a ThrownDevice, or no -// ThrownDevice is in inventory, nothing happens. + bool AHuman::EquipDeviceInGroup(std::string group, bool doEquip) { + if (!(m_pFGArm && m_pFGArm->IsAttached())) { + return false; + } -bool AHuman::EquipThrowable(bool doEquip) -{ - if (!(m_pFGArm && m_pFGArm->IsAttached())) { - return false; - } + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice(); heldDevice && heldDevice->IsInGroup(group)) { + return true; + } - if (dynamic_cast(m_pFGArm->GetHeldDevice())) { - return true; - } - - // Go through the inventory looking for the proper device - for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - ThrownDevice *pThrown = dynamic_cast(*itr); - // Found proper device to equip, so make the switch! -// TODO: see if thrown is weapon or not, don't want to throw key items etc - if (pThrown)// && pThrown->IsWeapon()) - { - if (doEquip) - { - // Erase the inventory entry containing the device we now have switched to - *itr = 0; - m_Inventory.erase(itr); - - // Put back into the inventory what we had in our hands, if anything - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice()) - { - heldDevice->Deactivate(); - AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); - } - - // Now put the device we were looking for and found into the hand - m_pFGArm->SetHeldDevice(pThrown); - // Move the hand to a poisition so it looks like the new device was drawn from inventory - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - - // Equip shield in BG arm as applicable - EquipShieldInBGArm(); - - // Play the device switching sound - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - } - - return true; - } - } - - return false; -} + // Go through the inventory looking for the proper device + for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + HeldDevice* pDevice = dynamic_cast(*itr); + // Found proper device to equip, so make the switch! + if (pDevice && pDevice->IsInGroup(group)) { + if (doEquip) { + // Erase the inventory entry containing the device we now have switched to + *itr = 0; + m_Inventory.erase(itr); + + // Put back into the inventory what we had in our hands, if anything + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice()) { + heldDevice->Deactivate(); + MovableObject* previouslyHeldItem = m_pFGArm->RemoveAttachable(heldDevice); + if (previouslyHeldItem) { + // Note - This is a fix to deal with an edge case bug when this method is called by a global script. + // Because the global script runs before everything has finished traveling, the removed item needs to undraw itself from the MO layer, otherwise it can result in ghost collisions and crashes. + if (previouslyHeldItem->GetsHitByMOs()) { +#ifdef DRAW_MOID_LAYER + previouslyHeldItem->Draw(g_SceneMan.GetMOIDBitmap(), Vector(), g_DrawNoMOID, true); +#else + previouslyHeldItem->SetTraveling(true); +#endif + } + AddToInventoryBack(previouslyHeldItem); + } + } -////////////////////////////////////////////////////////////////////////////////////////// + // Now put the device we were looking for and found into the hand + m_pFGArm->SetHeldDevice(pDevice); + // Move the hand to a poisition so it looks like the new device was drawn from inventory + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); -bool AHuman::EquipDiggingTool(bool doEquip) { - if (!(m_pFGArm && m_pFGArm->IsAttached())) { - return false; - } + // Equip shield in BG arm if applicable + EquipShieldInBGArm(); - const HDFirearm *strongestDigger = nullptr; - float strongestDiggerDigStrength = 0; - bool strongestDiggerIsHeld = false; - if (const HDFirearm *heldDeviceAsFirearm = dynamic_cast(m_pFGArm->GetHeldDevice()); heldDeviceAsFirearm && heldDeviceAsFirearm->IsInGroup("Tools - Diggers")) { - strongestDigger = heldDeviceAsFirearm; - strongestDiggerDigStrength = heldDeviceAsFirearm->EstimateDigStrength(); - strongestDiggerIsHeld = true; - } + // Play the device switching sound + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); + } + } - if (doEquip || !strongestDigger) { - for (MovableObject *inventoryItem : m_Inventory) { - if (const HDFirearm *inventoryItemAsFirearm = dynamic_cast(inventoryItem); inventoryItemAsFirearm && inventoryItemAsFirearm->IsInGroup("Tools - Diggers") && inventoryItemAsFirearm->EstimateDigStrength() > strongestDiggerDigStrength) { - strongestDigger = inventoryItemAsFirearm; - strongestDiggerDigStrength = inventoryItemAsFirearm->EstimateDigStrength(); - strongestDiggerIsHeld = false; + return true; } } - } - if (doEquip && strongestDigger && !strongestDiggerIsHeld) { - EquipNamedDevice(strongestDigger->GetModuleName(), strongestDigger->GetPresetName(), true); + return false; } - return strongestDigger != nullptr; -} - -////////////////////////////////////////////////////////////////////////////////////////// - -float AHuman::EstimateDigStrength() const { - float maxPenetration = Actor::EstimateDigStrength(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipLoadedFirearmInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first loaded HDFirearm + // of the specified group in the inventory. If no such weapon is in the + // inventory, nothing happens. - if (!(m_pFGArm && m_pFGArm->IsAttached())) { - return maxPenetration; - } + bool AHuman::EquipLoadedFirearmInGroup(std::string group, std::string excludeGroup, bool doEquip) { + if (!(m_pFGArm && m_pFGArm->IsAttached())) { + return false; + } - if (const HDFirearm *heldDeviceAsHDFirearm = dynamic_cast(m_pFGArm->GetHeldDevice()); heldDeviceAsHDFirearm && heldDeviceAsHDFirearm->IsInGroup("Tools - Diggers")) { - maxPenetration = std::max(heldDeviceAsHDFirearm->EstimateDigStrength(), maxPenetration); - } + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice(); heldDevice && !heldDevice->NeedsReloading() && heldDevice->IsInGroup(group) && !heldDevice->IsInGroup(excludeGroup)) { + return true; + } - for (const MovableObject *inventoryItem : m_Inventory) { - if (const HDFirearm *inventoryItemAsFirearm = dynamic_cast(inventoryItem); inventoryItemAsFirearm && inventoryItemAsFirearm->IsInGroup("Tools - Diggers")) { - maxPenetration = std::max(inventoryItemAsFirearm->EstimateDigStrength(), maxPenetration); - } - } + // Go through the inventory looking for the proper device + for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + HDFirearm* pFirearm = dynamic_cast(*itr); + // Found proper device to equip, so make the switch! + if (pFirearm && !pFirearm->NeedsReloading() && pFirearm->IsInGroup(group) && !pFirearm->IsInGroup(excludeGroup)) { + if (doEquip) { + // Erase the inventory entry containing the device we now have switched to + *itr = 0; + m_Inventory.erase(itr); + + // Put back into the inventory what we had in our hands, if anything + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice()) { + m_pFGArm->GetHeldDevice()->Deactivate(); + AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); + } - return maxPenetration; -} + // Now put the device we were looking for and found into the hand + m_pFGArm->SetHeldDevice(pFirearm); + // Move the hand to a poisition so it looks like the new device was drawn from inventory + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); -////////////////////////////////////////////////////////////////////////////////////////// + // Equip shield in BG arm if applicable + EquipShieldInBGArm(); + // Play the device switching sound + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipShield -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first found shield -// in the inventory. If the held device already is a shield, or no -// shield is in inventory, nothing happens. + return true; + } + } -bool AHuman::EquipShield() -{ - if (!(m_pFGArm && m_pFGArm->IsAttached())) { return false; } - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice(); heldDevice && heldDevice->IsShield()) { - return true; - } - - // Go through the inventory looking for the proper device - for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - HeldDevice *pShield = dynamic_cast(*itr); - // Found proper device to equip, so make the switch! - if (pShield && pShield->IsShield()) - { - // Erase the inventory entry containing the device we now have switched to - *itr = 0; - m_Inventory.erase(itr); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipNamedDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first found device + // of with the specified preset name in the inventory. If the held device already + // is of that preset name, or no device is in inventory, nothing happens. - // Put back into the inventory what we had in our hands, if anything - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice()) - { - heldDevice->Deactivate(); - AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); - } - - // Now put the device we were looking for and found into the hand - m_pFGArm->SetHeldDevice(pShield); - // Move the hand to a poisition so it looks like the new device was drawn from inventory - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + bool AHuman::EquipNamedDevice(const std::string& moduleName, const std::string& presetName, bool doEquip) { + if (!(m_pFGArm && m_pFGArm->IsAttached())) { + return false; + } - // Equip shield in BG arm is applicable - EquipShieldInBGArm(); + if (const HeldDevice* heldDevice = m_pFGArm->GetHeldDevice(); + heldDevice && (moduleName.empty() || heldDevice->GetModuleName() == moduleName) && heldDevice->GetPresetName() == presetName) { + return true; + } - // Play the device switching sound - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } + // Go through the inventory looking for the proper device + for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + HeldDevice* pDevice = dynamic_cast(*itr); + // Found proper device to equip, so make the switch! + if (pDevice && (moduleName.empty() || pDevice->GetModuleName() == moduleName) && pDevice->GetPresetName() == presetName) { + if (doEquip) { + // Erase the inventory entry containing the device we now have switched to + *itr = 0; + m_Inventory.erase(itr); + + // Put back into the inventory what we had in our hands, if anything + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice()) { + heldDevice->Deactivate(); + AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); + } - return true; - } - } + // Now put the device we were looking for and found into the hand + m_pFGArm->SetHeldDevice(pDevice); + // Move the hand to a poisition so it looks like the new device was drawn from inventory + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - return false; -} + // Equip shield in BG arm if applicable + EquipShieldInBGArm(); + // Play the device switching sound + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipShieldInBGArm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tries to equip the first shield in inventory to the background arm; -// this only works if nothing is held at all, or the FG arm holds a -// one-handed device, or we're in inventory mode. + return true; + } + } -bool AHuman::EquipShieldInBGArm() -{ - if (!(m_pBGArm && m_pBGArm->IsAttached())) { return false; } - if (HeldDevice *heldDevice = m_pBGArm->GetHeldDevice(); heldDevice && (heldDevice->IsShield() || heldDevice->IsDualWieldable())) { - // If we're holding a shield, but aren't supposed to, because we need to support the FG hand's two-handed device, then let go of the shield and put it back in inventory. - if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice() && !m_pFGArm->GetHeldDevice()->IsOneHanded()) { - m_pBGArm->GetHeldDevice()->Deactivate(); - AddToInventoryBack(m_pBGArm->RemoveAttachable(heldDevice)); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipThrowable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first found ThrownDevice + // in the inventory. If the held device already is a ThrownDevice, or no + // ThrownDevice is in inventory, nothing happens. + + bool AHuman::EquipThrowable(bool doEquip) { + if (!(m_pFGArm && m_pFGArm->IsAttached())) { return false; } - return true; - } - - // Only equip if the BG hand isn't occupied with supporting a two handed device - if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice() && !m_pFGArm->GetHeldDevice()->IsOneHanded()) { - return false; - } - - // Go through the inventory looking for the proper device - for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - HeldDevice *pShield = dynamic_cast(*itr); - // Found proper device to equip, so make the switch! - if (pShield && (pShield->IsShield() || pShield->IsDualWieldable())) - { - // Erase the inventory entry containing the device we now have switched to - *itr = 0; - m_Inventory.erase(itr); - - // Put back into the inventory what we had in our hands, if anything - if (HeldDevice *heldDevice = m_pBGArm->GetHeldDevice()) - { - heldDevice->Deactivate(); - AddToInventoryBack(m_pBGArm->RemoveAttachable(heldDevice)); - } - - // Now put the device we were looking for and found into the hand - m_pBGArm->SetHeldDevice(pShield); - // Move the hand to a poisition so it looks like the new device was drawn from inventory - m_pBGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - - return true; - } - } - - return false; -} - -////////////////////////////////////////////////////////////////////////////////////////// -bool AHuman::UnequipFGArm() { - if (m_pFGArm) { - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice()) { - heldDevice->Deactivate(); - AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); - m_pFGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + if (dynamic_cast(m_pFGArm->GetHeldDevice())) { return true; } - } - return false; -} -////////////////////////////////////////////////////////////////////////////////////////// + // Go through the inventory looking for the proper device + for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + ThrownDevice* pThrown = dynamic_cast(*itr); + // Found proper device to equip, so make the switch! + // TODO: see if thrown is weapon or not, don't want to throw key items etc + if (pThrown) // && pThrown->IsWeapon()) + { + if (doEquip) { + // Erase the inventory entry containing the device we now have switched to + *itr = 0; + m_Inventory.erase(itr); + + // Put back into the inventory what we had in our hands, if anything + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice()) { + heldDevice->Deactivate(); + AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); + } -bool AHuman::UnequipBGArm() { - if (m_pBGArm) { - if (HeldDevice *heldDevice = m_pBGArm->GetHeldDevice()) { - heldDevice->Deactivate(); - AddToInventoryFront(m_pBGArm->RemoveAttachable(heldDevice)); - m_pBGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); - return true; - } - } - return false; -} + // Now put the device we were looking for and found into the hand + m_pFGArm->SetHeldDevice(pThrown); + // Move the hand to a poisition so it looks like the new device was drawn from inventory + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); -////////////////////////////////////////////////////////////////////////////////////////// + // Equip shield in BG arm as applicable + EquipShieldInBGArm(); -float AHuman::GetEquippedMass() const { - float equippedMass = 0; - if (MovableObject *fgDevice = GetEquippedItem()) { - equippedMass += fgDevice->GetMass(); - } - if (MovableObject *bgDevice = GetEquippedBGItem()) { - equippedMass += bgDevice->GetMass(); - } - return equippedMass; -} + // Play the device switching sound + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmIsReady -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held device's current mag is empty on -// ammo or not. + return true; + } + } -bool AHuman::FirearmIsReady() const -{ - // Check if the currently held device is already the desired type - if (m_pFGArm && m_pFGArm->IsAttached()) - { - const HDFirearm *pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); - if (pWeapon && pWeapon->GetRoundInMagCount() != 0) - return true; - } + return false; + } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + bool AHuman::EquipDiggingTool(bool doEquip) { + if (!(m_pFGArm && m_pFGArm->IsAttached())) { + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: ThrowableIsReady -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held ThrownDevice's is ready to go. + const HDFirearm* strongestDigger = nullptr; + float strongestDiggerDigStrength = 0; + bool strongestDiggerIsHeld = false; + if (const HDFirearm* heldDeviceAsFirearm = dynamic_cast(m_pFGArm->GetHeldDevice()); heldDeviceAsFirearm && heldDeviceAsFirearm->IsInGroup("Tools - Diggers")) { + strongestDigger = heldDeviceAsFirearm; + strongestDiggerDigStrength = heldDeviceAsFirearm->EstimateDigStrength(); + strongestDiggerIsHeld = true; + } -bool AHuman::ThrowableIsReady() const -{ - // Check if the currently held thrown device is already the desired type - if (m_pFGArm && m_pFGArm->IsAttached()) - { - const ThrownDevice *pThrown = dynamic_cast(m_pFGArm->GetHeldDevice()); - if (pThrown)// && pThrown->blah() > 0) - return true; - } + if (doEquip || !strongestDigger) { + for (MovableObject* inventoryItem: m_Inventory) { + if (const HDFirearm* inventoryItemAsFirearm = dynamic_cast(inventoryItem); inventoryItemAsFirearm && inventoryItemAsFirearm->IsInGroup("Tools - Diggers") && inventoryItemAsFirearm->EstimateDigStrength() > strongestDiggerDigStrength) { + strongestDigger = inventoryItemAsFirearm; + strongestDiggerDigStrength = inventoryItemAsFirearm->EstimateDigStrength(); + strongestDiggerIsHeld = false; + } + } + } - return false; -} + if (doEquip && strongestDigger && !strongestDiggerIsHeld) { + EquipNamedDevice(strongestDigger->GetModuleName(), strongestDigger->GetPresetName(), true); + } + return strongestDigger != nullptr; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmIsEmpty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is out of ammo. + ////////////////////////////////////////////////////////////////////////////////////////// -bool AHuman::FirearmIsEmpty() const -{ - if (m_pFGArm && m_pFGArm->IsAttached()) - { - const HDFirearm *pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); - if (pWeapon && pWeapon->GetRoundInMagCount() == 0) - return true; - } + float AHuman::EstimateDigStrength() const { + float maxPenetration = Actor::EstimateDigStrength(); - return false; -} + if (!(m_pFGArm && m_pFGArm->IsAttached())) { + return maxPenetration; + } + if (const HDFirearm* heldDeviceAsHDFirearm = dynamic_cast(m_pFGArm->GetHeldDevice()); heldDeviceAsHDFirearm && heldDeviceAsHDFirearm->IsInGroup("Tools - Diggers")) { + maxPenetration = std::max(heldDeviceAsHDFirearm->EstimateDigStrength(), maxPenetration); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmNeedsReload -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is almost out of ammo. + for (const MovableObject* inventoryItem: m_Inventory) { + if (const HDFirearm* inventoryItemAsFirearm = dynamic_cast(inventoryItem); inventoryItemAsFirearm && inventoryItemAsFirearm->IsInGroup("Tools - Diggers")) { + maxPenetration = std::max(inventoryItemAsFirearm->EstimateDigStrength(), maxPenetration); + } + } -bool AHuman::FirearmNeedsReload() const { - if (const HDFirearm *fgWeapon = dynamic_cast(GetEquippedItem()); fgWeapon && fgWeapon->NeedsReloading()) { - return true; - } - if (const HDFirearm *bgWeapon = dynamic_cast(GetEquippedBGItem()); bgWeapon && bgWeapon->NeedsReloading()) { - return true; + return maxPenetration; } - return false; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// -bool AHuman::FirearmsAreReloading(bool onlyIfAllFirearmsAreReloading) const { - int reloadingFirearmCount = 0; - int totalFirearmCount = 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipShield + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first found shield + // in the inventory. If the held device already is a shield, or no + // shield is in inventory, nothing happens. - if (const HDFirearm *fgWeapon = dynamic_cast(GetEquippedItem())) { - totalFirearmCount++; - if (fgWeapon->IsReloading()) { - reloadingFirearmCount++; + bool AHuman::EquipShield() { + if (!(m_pFGArm && m_pFGArm->IsAttached())) { + return false; } - } - if (reloadingFirearmCount > 0 && !onlyIfAllFirearmsAreReloading) { - return true; - } - if (const HDFirearm *bgWeapon = dynamic_cast(GetEquippedBGItem())) { - totalFirearmCount++; - if (bgWeapon->IsReloading()) { - reloadingFirearmCount++; + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice(); heldDevice && heldDevice->IsShield()) { + return true; } - } - return onlyIfAllFirearmsAreReloading ? reloadingFirearmCount == totalFirearmCount : reloadingFirearmCount > 0; -} + // Go through the inventory looking for the proper device + for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + HeldDevice* pShield = dynamic_cast(*itr); + // Found proper device to equip, so make the switch! + if (pShield && pShield->IsShield()) { + // Erase the inventory entry containing the device we now have switched to + *itr = 0; + m_Inventory.erase(itr); + + // Put back into the inventory what we had in our hands, if anything + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice()) { + heldDevice->Deactivate(); + AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Now put the device we were looking for and found into the hand + m_pFGArm->SetHeldDevice(pShield); + // Move the hand to a poisition so it looks like the new device was drawn from inventory + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmIsSemiAuto -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is semi or full auto. + // Equip shield in BG arm is applicable + EquipShieldInBGArm(); -bool AHuman::FirearmIsSemiAuto() const -{ - if (m_pFGArm && m_pFGArm->IsAttached()) - { - const HDFirearm *pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); - return pWeapon && !pWeapon->IsFullAuto(); - } - return false; -} + // Play the device switching sound + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); + } + return true; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: ReloadFirearm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reloads the currently held firearm, if any. -// Arguments: None. -// Return value: None. + return false; + } -void AHuman::ReloadFirearms(bool onlyReloadEmptyFirearms) { - for (Arm *arm : { m_pFGArm, m_pBGArm }) { - if (arm) { - HDFirearm *heldFirearm = dynamic_cast(arm->GetHeldDevice()); - if (!heldFirearm) { - continue; - } - bool heldFirearmCanReload = heldFirearm->IsReloadable() && !heldFirearm->IsFull() && !heldFirearm->IsReloading() && (!onlyReloadEmptyFirearms || heldFirearm->IsEmpty()); - if (!heldFirearmCanReload) { - continue; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipShieldInBGArm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tries to equip the first shield in inventory to the background arm; + // this only works if nothing is held at all, or the FG arm holds a + // one-handed device, or we're in inventory mode. + + bool AHuman::EquipShieldInBGArm() { + if (!(m_pBGArm && m_pBGArm->IsAttached())) { + return false; + } + + if (HeldDevice* heldDevice = m_pBGArm->GetHeldDevice(); heldDevice && (heldDevice->IsShield() || heldDevice->IsDualWieldable())) { + // If we're holding a shield, but aren't supposed to, because we need to support the FG hand's two-handed device, then let go of the shield and put it back in inventory. + if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice() && !m_pFGArm->GetHeldDevice()->IsOneHanded()) { + m_pBGArm->GetHeldDevice()->Deactivate(); + AddToInventoryBack(m_pBGArm->RemoveAttachable(heldDevice)); + return false; } + return true; + } - Arm *otherArm = arm == m_pFGArm ? m_pBGArm : m_pFGArm; - HDFirearm *otherHeldFirearm = otherArm ? dynamic_cast(otherArm->GetHeldDevice()) : nullptr; + // Only equip if the BG hand isn't occupied with supporting a two handed device + if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice() && !m_pFGArm->GetHeldDevice()->IsOneHanded()) { + return false; + } - bool reloadHeldFirearm = false; - if (otherHeldFirearm && otherHeldFirearm->IsReloadable()) { - if (heldFirearm->IsDualReloadable() && otherHeldFirearm->IsDualReloadable()) { - reloadHeldFirearm = true; - } else if (!otherHeldFirearm->IsReloading()) { - reloadHeldFirearm = true; - if (arm == m_pFGArm) { - m_WaitingToReloadOffhand = true; - } + // Go through the inventory looking for the proper device + for (std::deque::iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + HeldDevice* pShield = dynamic_cast(*itr); + // Found proper device to equip, so make the switch! + if (pShield && (pShield->IsShield() || pShield->IsDualWieldable())) { + // Erase the inventory entry containing the device we now have switched to + *itr = 0; + m_Inventory.erase(itr); + + // Put back into the inventory what we had in our hands, if anything + if (HeldDevice* heldDevice = m_pBGArm->GetHeldDevice()) { + heldDevice->Deactivate(); + AddToInventoryBack(m_pBGArm->RemoveAttachable(heldDevice)); } - } else { - reloadHeldFirearm = true; - } - if (reloadHeldFirearm) { - heldFirearm->Reload(); - if (m_DeviceSwitchSound) { - m_DeviceSwitchSound->Play(m_Pos); + // Now put the device we were looking for and found into the hand + m_pBGArm->SetHeldDevice(pShield); + // Move the hand to a poisition so it looks like the new device was drawn from inventory + m_pBGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); } - bool otherArmIsAvailable = otherArm && !otherArm->GetHeldDevice(); + return true; + } + } - // If using the support offset, other code in arm etc will handle where we should target - otherArmIsAvailable = otherArmIsAvailable && !heldFirearm->GetUseSupportOffsetWhileReloading(); + return false; + } - if (otherArmIsAvailable) { - float delayAtTarget = std::max(static_cast(heldFirearm->GetReloadTime() - 200), 0.0F); - otherArm->AddHandTarget("Magazine Pos", heldFirearm->GetMagazinePos()); - if (!m_ReloadOffset.IsZero()) { - otherArm->AddHandTarget("Reload Offset", m_Pos + RotateOffset(m_ReloadOffset), delayAtTarget); - } else { - otherArm->AddHandTarget("Holster Offset", m_Pos + RotateOffset(m_HolsterOffset), delayAtTarget); - } - } + ////////////////////////////////////////////////////////////////////////////////////////// + + bool AHuman::UnequipFGArm() { + if (m_pFGArm) { + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice()) { + heldDevice->Deactivate(); + AddToInventoryBack(m_pFGArm->RemoveAttachable(heldDevice)); + m_pFGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + return true; } } + return false; } -} + ////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmActivationDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the currently held device's delay between pulling the trigger -// and activating. + bool AHuman::UnequipBGArm() { + if (m_pBGArm) { + if (HeldDevice* heldDevice = m_pBGArm->GetHeldDevice()) { + heldDevice->Deactivate(); + AddToInventoryFront(m_pBGArm->RemoveAttachable(heldDevice)); + m_pBGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + return true; + } + } + return false; + } -int AHuman::FirearmActivationDelay() const -{ - // Check if the currently held device is already the desired type - if (m_pFGArm && m_pFGArm->IsAttached()) - { - const HDFirearm *pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); - if (pWeapon) - return pWeapon->GetActivationDelay(); - } + ////////////////////////////////////////////////////////////////////////////////////////// - return 0; -} + float AHuman::GetEquippedMass() const { + float equippedMass = 0; + if (MovableObject* fgDevice = GetEquippedItem()) { + equippedMass += fgDevice->GetMass(); + } + if (MovableObject* bgDevice = GetEquippedBGItem()) { + equippedMass += bgDevice->GetMass(); + } + return equippedMass; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmIsReady + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held device's current mag is empty on + // ammo or not. + + bool AHuman::FirearmIsReady() const { + // Check if the currently held device is already the desired type + if (m_pFGArm && m_pFGArm->IsAttached()) { + const HDFirearm* pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); + if (pWeapon && pWeapon->GetRoundInMagCount() != 0) + return true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsWithinRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a point on the scene is within range of the currently -// used device and aiming status, if applicable. + return false; + } -bool AHuman::IsWithinRange(Vector &point) const -{ - if (m_SharpAimMaxedOut) - return true; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: ThrowableIsReady + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held ThrownDevice's is ready to go. + + bool AHuman::ThrowableIsReady() const { + // Check if the currently held thrown device is already the desired type + if (m_pFGArm && m_pFGArm->IsAttached()) { + const ThrownDevice* pThrown = dynamic_cast(m_pFGArm->GetHeldDevice()); + if (pThrown) // && pThrown->blah() > 0) + return true; + } - Vector diff = g_SceneMan.ShortestDistance(m_Pos, point, false); - float sqrDistance = diff.GetSqrMagnitude(); + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmIsEmpty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is out of ammo. - // Really close! - if (sqrDistance <= (m_CharHeight * m_CharHeight)) { - return true; + bool AHuman::FirearmIsEmpty() const { + if (m_pFGArm && m_pFGArm->IsAttached()) { + const HDFirearm* pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); + if (pWeapon && pWeapon->GetRoundInMagCount() == 0) + return true; + } + + return false; } - float range = 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmNeedsReload + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is almost out of ammo. - if (FirearmIsReady()) - { - // Start with the default aim distance - range = m_AimDistance; + bool AHuman::FirearmNeedsReload() const { + if (const HDFirearm* fgWeapon = dynamic_cast(GetEquippedItem()); fgWeapon && fgWeapon->NeedsReloading()) { + return true; + } + if (const HDFirearm* bgWeapon = dynamic_cast(GetEquippedBGItem()); bgWeapon && bgWeapon->NeedsReloading()) { + return true; + } + return false; + } - // Add the sharp range of the equipped weapon - if (m_pFGArm && m_pFGArm->IsAttached()) - range += m_pFGArm->GetHeldDevice()->GetSharpLength() + 150; - } - else if (ThrowableIsReady()) - { -// TODO: make proper throw range calc based on the throwable's mass etc - range += m_CharHeight * 4; - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return sqrDistance <= (range * range); -} + bool AHuman::FirearmsAreReloading(bool onlyIfAllFirearmsAreReloading) const { + int reloadingFirearmCount = 0; + int totalFirearmCount = 0; + if (const HDFirearm* fgWeapon = dynamic_cast(GetEquippedItem())) { + totalFirearmCount++; + if (fgWeapon->IsReloading()) { + reloadingFirearmCount++; + } + } + if (reloadingFirearmCount > 0 && !onlyIfAllFirearmsAreReloading) { + return true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Look -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an unseen-revealing ray in the direction of where this is facing. -// Arguments: The degree angle to deviate from the current view point in the ray -// casting. A random ray will be chosen out of this +-range. - -bool AHuman::Look(float FOVSpread, float range) -{ - if (!g_SceneMan.AnythingUnseen(m_Team) || m_CanRevealUnseen == false) - return false; - - // Set the length of the look vector - float aimDistance = m_AimDistance + range; - Vector aimPos = m_Pos; - - // If aiming down the barrel, look through that - if (m_Controller.IsState(AIM_SHARP) && m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) - { - aimPos = m_pFGArm->GetHeldDevice()->GetPos(); - aimDistance += m_pFGArm->GetHeldDevice()->GetSharpLength(); - } - // If just looking, use the eyes on the head instead - else if (m_pHead && m_pHead->IsAttached()) - { - aimPos = GetEyePos(); - } - - // Create the vector to trace along - Vector lookVector(aimDistance, 0); - // Set the rotation to the actual aiming angle - Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); - aimMatrix.SetXFlipped(m_HFlipped); - lookVector *= aimMatrix; - // Add the spread - lookVector.DegRotate(FOVSpread * RandomNormalNum()); - - // TODO: generate an alarm event if we spot an enemy actor? - - Vector ignored; - // Cast the seeing ray, adjusting the skip to match the resolution of the unseen map - return g_SceneMan.CastSeeRay(m_Team, aimPos, lookVector, ignored, 25, (int)g_SceneMan.GetUnseenResolution(m_Team).GetSmallest() / 2); -} + if (const HDFirearm* bgWeapon = dynamic_cast(GetEquippedBGItem())) { + totalFirearmCount++; + if (bgWeapon->IsReloading()) { + reloadingFirearmCount++; + } + } + return onlyIfAllFirearmsAreReloading ? reloadingFirearmCount == totalFirearmCount : reloadingFirearmCount > 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LookForGold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts a material detecting ray in the direction of where this is facing. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool AHuman::LookForGold(float FOVSpread, float range, Vector &foundLocation) const -{ - Vector ray(m_HFlipped ? -range : range, 0); - ray.DegRotate(FOVSpread * RandomNormalNum()); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmIsSemiAuto + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is semi or full auto. - return g_SceneMan.CastMaterialRay(m_Pos, ray, g_MaterialGold, foundLocation, 4); -} + bool AHuman::FirearmIsSemiAuto() const { + if (m_pFGArm && m_pFGArm->IsAttached()) { + const HDFirearm* pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); + return pWeapon && !pWeapon->IsFullAuto(); + } + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: ReloadFirearm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reloads the currently held firearm, if any. + // Arguments: None. + // Return value: None. + + void AHuman::ReloadFirearms(bool onlyReloadEmptyFirearms) { + for (Arm* arm: {m_pFGArm, m_pBGArm}) { + if (arm) { + HDFirearm* heldFirearm = dynamic_cast(arm->GetHeldDevice()); + if (!heldFirearm) { + continue; + } + bool heldFirearmCanReload = heldFirearm->IsReloadable() && !heldFirearm->IsFull() && !heldFirearm->IsReloading() && (!onlyReloadEmptyFirearms || heldFirearm->IsEmpty()); + if (!heldFirearmCanReload) { + continue; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LookForMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an MO detecting ray in the direction of where the head is looking -// at the time. Factors including head rotation, sharp aim mode, and -// other variables determine how this ray is cast. - -MovableObject * AHuman::LookForMOs(float FOVSpread, unsigned char ignoreMaterial, bool ignoreAllTerrain) -{ - MovableObject *pSeenMO = 0; - Vector aimPos = m_Pos; - float aimDistance = m_AimDistance + g_FrameMan.GetPlayerScreenWidth() * 0.51; // Set the length of the look vector - - // If aiming down the barrel, look through that - if (m_Controller.IsState(AIM_SHARP) && m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) - { - aimPos = m_pFGArm->GetHeldDevice()->GetPos(); - aimDistance += m_pFGArm->GetHeldDevice()->GetSharpLength(); - } - // If just looking, use the eyes on the head instead - else if (m_pHead && m_pHead->IsAttached()) - { - aimPos = GetEyePos(); - } - - // Create the vector to trace along - Vector lookVector(aimDistance, 0); - // Set the rotation to the actual aiming angle - Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); - aimMatrix.SetXFlipped(m_HFlipped); - lookVector *= aimMatrix; - // Add the spread - lookVector.DegRotate(FOVSpread * RandomNormalNum()); - - MOID seenMOID = g_SceneMan.CastMORay(aimPos, lookVector, m_MOID, IgnoresWhichTeam(), ignoreMaterial, ignoreAllTerrain, 5); - pSeenMO = g_MovableMan.GetMOFromID(seenMOID); - if (pSeenMO) - return pSeenMO->GetRootParent(); - - return pSeenMO; -} + Arm* otherArm = arm == m_pFGArm ? m_pBGArm : m_pFGArm; + HDFirearm* otherHeldFirearm = otherArm ? dynamic_cast(otherArm->GetHeldDevice()) : nullptr; + + bool reloadHeldFirearm = false; + if (otherHeldFirearm && otherHeldFirearm->IsReloadable()) { + if (heldFirearm->IsDualReloadable() && otherHeldFirearm->IsDualReloadable()) { + reloadHeldFirearm = true; + } else if (!otherHeldFirearm->IsReloading()) { + reloadHeldFirearm = true; + if (arm == m_pFGArm) { + m_WaitingToReloadOffhand = true; + } + } + } else { + reloadHeldFirearm = true; + } + if (reloadHeldFirearm) { + heldFirearm->Reload(); + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetAllTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resest all the timers used by this. Can be emitters, etc. This is to -// prevent backed up emissions to come out all at once while this has been -// held dormant in an inventory. + bool otherArmIsAvailable = otherArm && !otherArm->GetHeldDevice(); -void AHuman::ResetAllTimers() -{ - Actor::ResetAllTimers(); + // If using the support offset, other code in arm etc will handle where we should target + otherArmIsAvailable = otherArmIsAvailable && !heldFirearm->GetUseSupportOffsetWhileReloading(); - if (m_pFGArm && m_pFGArm->GetHeldDevice()) { - m_pFGArm->GetHeldDevice()->ResetAllTimers(); - } -} + if (otherArmIsAvailable) { + float delayAtTarget = std::max(static_cast(heldFirearm->GetReloadTime() - 200), 0.0F); + otherArm->AddHandTarget("Magazine Pos", heldFirearm->GetMagazinePos()); + if (!m_ReloadOffset.IsZero()) { + otherArm->AddHandTarget("Reload Offset", m_Pos + RotateOffset(m_ReloadOffset), delayAtTarget); + } else { + otherArm->AddHandTarget("Holster Offset", m_Pos + RotateOffset(m_HolsterOffset), delayAtTarget); + } + } + } + } + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmActivationDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the currently held device's delay between pulling the trigger + // and activating. + + int AHuman::FirearmActivationDelay() const { + // Check if the currently held device is already the desired type + if (m_pFGArm && m_pFGArm->IsAttached()) { + const HDFirearm* pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); + if (pWeapon) + return pWeapon->GetActivationDelay(); + } + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsWithinRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a point on the scene is within range of the currently + // used device and aiming status, if applicable. -void AHuman::OnNewMovePath() -{ - Actor::OnNewMovePath(); - - // Process the new path we now have, if any - if (!m_MovePath.empty()) - { - // Smash all airborne waypoints down to just above the ground, except for when it makes the path intersect terrain or it is the final destination - std::list::iterator finalItr = m_MovePath.end(); - finalItr--; - Vector smashedPoint; - Vector previousPoint = *(m_MovePath.begin()); - std::list::iterator nextItr = m_MovePath.begin(); - for (std::list::iterator lItr = m_MovePath.begin(); lItr != finalItr; ++lItr) - { - nextItr++; - smashedPoint = g_SceneMan.MovePointToGround((*lItr), m_CharHeight*0.2, 7); - - // Only smash if the new location doesn't cause the path to intersect hard terrain ahead or behind of it - // Try three times to halve the height to see if that won't intersect - for (int i = 0; i < 3; i++) - { - Vector notUsed; - if (!g_SceneMan.CastStrengthRay(previousPoint, smashedPoint - previousPoint, 5, notUsed, 3, g_MaterialDoor) && - nextItr != m_MovePath.end() && !g_SceneMan.CastStrengthRay(smashedPoint, (*nextItr) - smashedPoint, 5, notUsed, 3, g_MaterialDoor)) - { - (*lItr) = smashedPoint; - break; - } - else - smashedPoint.m_Y -= ((smashedPoint.m_Y - (*lItr).m_Y) / 2); - } - - previousPoint = (*lItr); - } - } -} + bool AHuman::IsWithinRange(Vector& point) const { + if (m_SharpAimMaxedOut) + return true; -////////////////////////////////////////////////////////////////////////////////////////// + Vector diff = g_SceneMan.ShortestDistance(m_Pos, point, false); + float sqrDistance = diff.GetSqrMagnitude(); -void AHuman::UpdateWalkAngle(AHuman::Layer whichLayer) { - if (m_Controller.IsState(BODY_JUMP)) { - m_WalkAngle[whichLayer] = Matrix(c_QuarterPI * GetFlipFactor()); - } else { - float rayLength = 15.0F; - Vector hipPos = m_Pos; - if (whichLayer == AHuman::Layer::FGROUND && m_pFGLeg) { - rayLength += m_pFGLeg->GetMaxLength(); - hipPos += RotateOffset(m_pFGLeg->GetParentOffset()); - } else if (m_pBGLeg) { - rayLength += m_pBGLeg->GetMaxLength(); - hipPos += RotateOffset(m_pBGLeg->GetParentOffset()); - } - - // Cast a ray down from the left and right of us, to determine our angle of ascent - //TODO Don't use a magic number here, calculate something based on stride length and maybe footgroup width. - Vector hitPosLeft = hipPos + Vector(-10.0F, 0.0F); - Vector hitPosRight = hipPos + Vector(10.0F, 0.0F); - g_SceneMan.CastStrengthRay(hitPosLeft, Vector(0.0F, rayLength), 10.0F, hitPosLeft, 0, g_MaterialGrass); - g_SceneMan.CastStrengthRay(hitPosRight, Vector(0.0F, rayLength), 10.0F, hitPosRight, 0, g_MaterialGrass); - - // Clamp the max angle, so we don't end up trying to walk at a 80 degree angle up sheer walls - const float maxAngleDegrees = 40.0F; - float terrainRotationDegs = std::clamp((hitPosRight - hitPosLeft).GetAbsDegAngle(), -maxAngleDegrees, maxAngleDegrees); - - Matrix walkAngle; - walkAngle.SetDegAngle(terrainRotationDegs); - m_WalkAngle[whichLayer] = walkAngle; - } -} + // Really close! + if (sqrDistance <= (m_CharHeight * m_CharHeight)) { + return true; + } -////////////////////////////////////////////////////////////////////////////////////////// + float range = 0; -void AHuman::UpdateCrouching() { - if (!m_Controller.IsState(BODY_JUMP) && m_pHead) { - float desiredWalkPathYOffset = 0.0F; - if (m_CrouchAmountOverride == -1.0F) { - // Cast a ray above our head to either side to determine whether we need to crouch - float desiredCrouchHeadRoom = std::floor(m_pHead->GetRadius() + 2.0f); - float toPredicted = std::floor(m_Vel.m_X * m_pHead->GetRadius()); // Check where we'll be a second from now - Vector hitPosStart = (m_pHead->GetPos() + Vector(0.0F, m_SpriteRadius * 0.5F)).Floor(); - Vector hitPosPredictedStart = (m_pHead->GetPos() + Vector(toPredicted, m_SpriteRadius * 0.5F)).Floor(); - Vector hitPos, hitPosPredicted; - g_SceneMan.CastStrengthRay(hitPosStart, Vector(0.0F, -desiredCrouchHeadRoom + m_SpriteRadius * -0.5F), 1.0F, hitPos, 0, g_MaterialGrass); - g_SceneMan.CastStrengthRay(hitPosPredictedStart, Vector(0.0F, -desiredCrouchHeadRoom + m_SpriteRadius * -0.5F), 1.0F, hitPosPredicted, 0, g_MaterialGrass); + if (FirearmIsReady()) { + // Start with the default aim distance + range = m_AimDistance; - // Don't do it if we're already hitting, we're probably in a weird spot - if (hitPosStart.m_Y - hitPos.m_Y <= 2.0F) { - hitPos.m_Y = 0.0F; - } + // Add the sharp range of the equipped weapon + if (m_pFGArm && m_pFGArm->IsAttached()) + range += m_pFGArm->GetHeldDevice()->GetSharpLength() + 150; + } else if (ThrowableIsReady()) { + // TODO: make proper throw range calc based on the throwable's mass etc + range += m_CharHeight * 4; + } - if (hitPosPredictedStart.m_Y - hitPosPredicted.m_Y <= 2.0F) { - hitPosPredicted.m_Y = 0.0F; - } + return sqrDistance <= (range * range); + } - float headroom = m_pHead->GetPos().m_Y - std::max(hitPos.m_Y, hitPosPredicted.m_Y); - desiredWalkPathYOffset = desiredCrouchHeadRoom - headroom; - } else { - desiredWalkPathYOffset = m_CrouchAmountOverride * m_MaxWalkPathCrouchShift; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Look + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an unseen-revealing ray in the direction of where this is facing. + // Arguments: The degree angle to deviate from the current view point in the ray + // casting. A random ray will be chosen out of this +-range. + + bool AHuman::Look(float FOVSpread, float range) { + if (!g_SceneMan.AnythingUnseen(m_Team) || m_CanRevealUnseen == false) + return false; + + // Set the length of the look vector + float aimDistance = m_AimDistance + range; + Vector aimPos = m_Pos; + + // If aiming down the barrel, look through that + if (m_Controller.IsState(AIM_SHARP) && m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) { + aimPos = m_pFGArm->GetHeldDevice()->GetPos(); + aimDistance += m_pFGArm->GetHeldDevice()->GetSharpLength(); + } + // If just looking, use the eyes on the head instead + else if (m_pHead && m_pHead->IsAttached()) { + aimPos = GetEyePos(); } - float finalWalkPathYOffset = std::clamp(LERP(0.0F, 1.0F, -m_WalkPathOffset.m_Y, desiredWalkPathYOffset, 0.3F), 0.0F, m_MaxWalkPathCrouchShift); - m_WalkPathOffset.m_Y = -finalWalkPathYOffset; + // Create the vector to trace along + Vector lookVector(aimDistance, 0); + // Set the rotation to the actual aiming angle + Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); + aimMatrix.SetXFlipped(m_HFlipped); + lookVector *= aimMatrix; + // Add the spread + lookVector.DegRotate(FOVSpread * RandomNormalNum()); - // If crouching, move at reduced speed - const float crouchSpeedMultiplier = 0.5F; - float travelSpeedMultiplier = LERP(0.0F, m_MaxWalkPathCrouchShift, 1.0F, crouchSpeedMultiplier, -m_WalkPathOffset.m_Y); - m_Paths[FGROUND][WALK].SetTravelSpeedMultiplier(travelSpeedMultiplier); - m_Paths[BGROUND][WALK].SetTravelSpeedMultiplier(travelSpeedMultiplier); + // TODO: generate an alarm event if we spot an enemy actor? - // Adjust our X offset to try to keep our legs under our centre-of-mass - const float ratioBetweenBodyAndHeadToAimFor = 0.15F; - float predictedPosition = ((m_pHead->GetPos().m_X - m_Pos.m_X) * ratioBetweenBodyAndHeadToAimFor) + m_Vel.m_X; - m_WalkPathOffset.m_X = predictedPosition; - } else { - m_WalkPathOffset.Reset(); + Vector ignored; + // Cast the seeing ray, adjusting the skip to match the resolution of the unseen map + return g_SceneMan.CastSeeRay(m_Team, aimPos, lookVector, ignored, 25, (int)g_SceneMan.GetUnseenResolution(m_Team).GetSmallest() / 2); } -} -////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: LookForGold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts a material detecting ray in the direction of where this is facing. -void AHuman::PreControllerUpdate() -{ - ZoneScoped; + bool AHuman::LookForGold(float FOVSpread, float range, Vector& foundLocation) const { + Vector ray(m_HFlipped ? -range : range, 0); + ray.DegRotate(FOVSpread * RandomNormalNum()); - Actor::PreControllerUpdate(); + return g_SceneMan.CastMaterialRay(m_Pos, ray, g_MaterialGold, foundLocation, 4); + } - float deltaTime = g_TimerMan.GetDeltaTimeSecs(); - float rot = m_Rotation.GetRadAngle(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: LookForMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an MO detecting ray in the direction of where the head is looking + // at the time. Factors including head rotation, sharp aim mode, and + // other variables determine how this ray is cast. + + MovableObject* AHuman::LookForMOs(float FOVSpread, unsigned char ignoreMaterial, bool ignoreAllTerrain) { + MovableObject* pSeenMO = 0; + Vector aimPos = m_Pos; + float aimDistance = m_AimDistance + g_FrameMan.GetPlayerScreenWidth() * 0.51; // Set the length of the look vector + + // If aiming down the barrel, look through that + if (m_Controller.IsState(AIM_SHARP) && m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->GetHeldDevice()) { + aimPos = m_pFGArm->GetHeldDevice()->GetPos(); + aimDistance += m_pFGArm->GetHeldDevice()->GetSharpLength(); + } + // If just looking, use the eyes on the head instead + else if (m_pHead && m_pHead->IsAttached()) { + aimPos = GetEyePos(); + } - Vector analogAim = m_Controller.GetAnalogAim(); - const float analogDeadzone = 0.1F; + // Create the vector to trace along + Vector lookVector(aimDistance, 0); + // Set the rotation to the actual aiming angle + Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); + aimMatrix.SetXFlipped(m_HFlipped); + lookVector *= aimMatrix; + // Add the spread + lookVector.DegRotate(FOVSpread * RandomNormalNum()); + + MOID seenMOID = g_SceneMan.CastMORay(aimPos, lookVector, m_MOID, IgnoresWhichTeam(), ignoreMaterial, ignoreAllTerrain, 5); + pSeenMO = g_MovableMan.GetMOFromID(seenMOID); + if (pSeenMO) + return pSeenMO->GetRootParent(); + + return pSeenMO; + } - m_Paths[FGROUND][m_MoveState].SetHFlip(m_HFlipped); - m_Paths[BGROUND][m_MoveState].SetHFlip(m_HFlipped); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ResetAllTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resest all the timers used by this. Can be emitters, etc. This is to + // prevent backed up emissions to come out all at once while this has been + // held dormant in an inventory. - if (m_pJetpack && m_pJetpack->IsAttached()) { - m_pJetpack->UpdateBurstState(*this); + void AHuman::ResetAllTimers() { + Actor::ResetAllTimers(); - if (m_Controller.IsState(BODY_JUMP) && !m_pJetpack->IsOutOfFuel() && m_Status != INACTIVE) { - m_Paths[FGROUND][JUMP].Restart(); - m_Paths[BGROUND][JUMP].Restart(); - } + if (m_pFGArm && m_pFGArm->GetHeldDevice()) { + m_pFGArm->GetHeldDevice()->ResetAllTimers(); + } } - //////////////////////////////////// - // Movement direction - - const float movementThreshold = 1.0F; - bool isStill = (m_Vel + m_PrevVel).MagnitudeIsLessThan(movementThreshold); - bool isSharpAiming = m_Controller.IsState(AIM_SHARP); - - // If the pie menu is on, try to preserve whatever move state we had before it going into effect. - // This is only done for digital input, where the user needs to use the keyboard to choose pie slices. - // For analog input, this doesn't matter - the mouse or aiming analog stick controls the pie menu. - bool keepOldState = m_Controller.IsKeyboardOnlyControlled() && m_Controller.IsState(PIE_MENU_ACTIVE); - - if (!keepOldState) { - bool crouching = m_Controller.IsState(BODY_CROUCH); - if ((m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP) && m_Status != INACTIVE) { - for (int i = WALK; i < MOVEMENTSTATECOUNT; ++i) { - m_Paths[FGROUND][i].SetHFlip(m_HFlipped); - m_Paths[BGROUND][i].SetHFlip(m_HFlipped); - } - // Only if not jumping, OR if jumping, and apparently stuck on something - then help out with the limbs. - if (m_MoveState != JUMP || isStill) { - // Restart the stride if we're just starting to walk or crawl. - if ((m_MoveState != WALK && !crouching) || (m_MoveState != CRAWL && crouching)) { - m_StrideStart = true; - MoveOutOfTerrain(g_MaterialGrass); - } - - m_MoveState = crouching ? CRAWL : WALK; - - // Engage prone state, this makes the body's rotational spring pull it horizontal instead of upright. - if (m_MoveState == CRAWL && m_ProneState == NOTPRONE) { - m_ProneState = GOPRONE; - m_ProneTimer.Reset(); - } - - m_Paths[FGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); - m_Paths[BGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); - } - - // Walk backwards if the aiming is already focused in the opposite direction of travel. - // Note that we check against zero here rather than the deadzone, because using the deadzone makes jetpacking mouse players unable to fly one way and aim the other. - if (!analogAim.IsZero() || isSharpAiming) { - m_Paths[FGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); - m_Paths[BGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); - } else if ((m_Controller.IsState(MOVE_RIGHT) && m_HFlipped) || (m_Controller.IsState(MOVE_LEFT) && !m_HFlipped)) { - m_HFlipped = !m_HFlipped; - m_CheckTerrIntersection = true; - if (m_ProneState == NOTPRONE) { MoveOutOfTerrain(g_MaterialGrass); } - - for (int i = WALK; i < MOVEMENTSTATECOUNT; ++i) { - m_Paths[FGROUND][i].SetHFlip(m_HFlipped); - m_Paths[BGROUND][i].SetHFlip(m_HFlipped); - m_Paths[FGROUND][i].Terminate(); - m_Paths[BGROUND][i].Terminate(); - } - m_StrideStart = true; - // Stop the going prone spring. - if (m_ProneState == GOPRONE) { m_ProneState = PRONE; } - } - } else { - m_ArmClimbing[FGROUND] = false; - m_ArmClimbing[BGROUND] = false; - if (crouching) { - // Don't go back to crouching if we're already prone, the player has to let go of the crouch button first. If already laying down, just stay put. - m_MoveState = m_ProneState == NOTPRONE ? CROUCH : NOMOVE; - } else { - m_MoveState = STAND; - } - } - // Disengage the prone state as soon as crouch is released. - if (!crouching && m_ProneState != NOTPRONE) { - EquipShieldInBGArm(); - m_ProneState = NOTPRONE; + ////////////////////////////////////////////////////////////////////////////////////////// + + void AHuman::OnNewMovePath() { + Actor::OnNewMovePath(); + + // Process the new path we now have, if any + if (!m_MovePath.empty()) { + // Smash all airborne waypoints down to just above the ground, except for when it makes the path intersect terrain or it is the final destination + std::list::iterator finalItr = m_MovePath.end(); + finalItr--; + Vector smashedPoint; + Vector previousPoint = *(m_MovePath.begin()); + std::list::iterator nextItr = m_MovePath.begin(); + for (std::list::iterator lItr = m_MovePath.begin(); lItr != finalItr; ++lItr) { + nextItr++; + smashedPoint = g_SceneMan.MovePointToGround((*lItr), m_CharHeight * 0.2, 7); + + // Only smash if the new location doesn't cause the path to intersect hard terrain ahead or behind of it + // Try three times to halve the height to see if that won't intersect + for (int i = 0; i < 3; i++) { + Vector notUsed; + if (!g_SceneMan.CastStrengthRay(previousPoint, smashedPoint - previousPoint, 5, notUsed, 3, g_MaterialDoor) && + nextItr != m_MovePath.end() && !g_SceneMan.CastStrengthRay(smashedPoint, (*nextItr) - smashedPoint, 5, notUsed, 3, g_MaterialDoor)) { + (*lItr) = smashedPoint; + break; + } else + smashedPoint.m_Y -= ((smashedPoint.m_Y - (*lItr).m_Y) / 2); + } + + previousPoint = (*lItr); + } } - } + } - //////////////////////////////////// - // Standard Reloading + ////////////////////////////////////////////////////////////////////////////////////////// - for (const Arm *arm : { m_pFGArm, m_pBGArm }) { - if (arm) { - if (HDFirearm *heldFirearm = dynamic_cast(arm->GetHeldDevice())) { - Arm *otherArm = arm == m_pFGArm ? m_pBGArm : m_pFGArm; - bool otherArmIsAvailable = otherArm && !otherArm->GetHeldDevice(); - if (otherArmIsAvailable && heldFirearm->DoneReloading()) { otherArm->SetHandPos(heldFirearm->GetMagazinePos()); }; - heldFirearm->SetSupportAvailable(otherArmIsAvailable); + void AHuman::UpdateWalkAngle(AHuman::Layer whichLayer) { + if (m_Controller.IsState(BODY_JUMP)) { + m_WalkAngle[whichLayer] = Matrix(c_QuarterPI * GetFlipFactor()); + } else { + float rayLength = 15.0F; + Vector hipPos = m_Pos; + if (whichLayer == AHuman::Layer::FGROUND && m_pFGLeg) { + rayLength += m_pFGLeg->GetMaxLength(); + hipPos += RotateOffset(m_pFGLeg->GetParentOffset()); + } else if (m_pBGLeg) { + rayLength += m_pBGLeg->GetMaxLength(); + hipPos += RotateOffset(m_pBGLeg->GetParentOffset()); } + + // Cast a ray down from the left and right of us, to determine our angle of ascent + // TODO Don't use a magic number here, calculate something based on stride length and maybe footgroup width. + Vector hitPosLeft = hipPos + Vector(-10.0F, 0.0F); + Vector hitPosRight = hipPos + Vector(10.0F, 0.0F); + g_SceneMan.CastStrengthRay(hitPosLeft, Vector(0.0F, rayLength), 10.0F, hitPosLeft, 0, g_MaterialGrass); + g_SceneMan.CastStrengthRay(hitPosRight, Vector(0.0F, rayLength), 10.0F, hitPosRight, 0, g_MaterialGrass); + + // Clamp the max angle, so we don't end up trying to walk at a 80 degree angle up sheer walls + const float maxAngleDegrees = 40.0F; + float terrainRotationDegs = std::clamp((hitPosRight - hitPosLeft).GetAbsDegAngle(), -maxAngleDegrees, maxAngleDegrees); + + Matrix walkAngle; + walkAngle.SetDegAngle(terrainRotationDegs); + m_WalkAngle[whichLayer] = walkAngle; } } - if (m_Controller.IsState(ControlState::WEAPON_RELOAD)) { - ReloadFirearms(); - } - if (m_WaitingToReloadOffhand) { - if (HeldDevice *equippedItem = GetEquippedItem(); equippedItem && !equippedItem->IsReloading()) { - ReloadFirearms(); - m_WaitingToReloadOffhand = false; + + ////////////////////////////////////////////////////////////////////////////////////////// + + void AHuman::UpdateCrouching() { + if (!m_Controller.IsState(BODY_JUMP) && m_pHead) { + float desiredWalkPathYOffset = 0.0F; + if (m_CrouchAmountOverride == -1.0F) { + // Cast a ray above our head to either side to determine whether we need to crouch + float desiredCrouchHeadRoom = std::floor(m_pHead->GetRadius() + 2.0f); + float toPredicted = std::floor(m_Vel.m_X * m_pHead->GetRadius()); // Check where we'll be a second from now + Vector hitPosStart = (m_pHead->GetPos() + Vector(0.0F, m_SpriteRadius * 0.5F)).Floor(); + Vector hitPosPredictedStart = (m_pHead->GetPos() + Vector(toPredicted, m_SpriteRadius * 0.5F)).Floor(); + Vector hitPos, hitPosPredicted; + g_SceneMan.CastStrengthRay(hitPosStart, Vector(0.0F, -desiredCrouchHeadRoom + m_SpriteRadius * -0.5F), 1.0F, hitPos, 0, g_MaterialGrass); + g_SceneMan.CastStrengthRay(hitPosPredictedStart, Vector(0.0F, -desiredCrouchHeadRoom + m_SpriteRadius * -0.5F), 1.0F, hitPosPredicted, 0, g_MaterialGrass); + + // Don't do it if we're already hitting, we're probably in a weird spot + if (hitPosStart.m_Y - hitPos.m_Y <= 2.0F) { + hitPos.m_Y = 0.0F; + } + + if (hitPosPredictedStart.m_Y - hitPosPredicted.m_Y <= 2.0F) { + hitPosPredicted.m_Y = 0.0F; + } + + float headroom = m_pHead->GetPos().m_Y - std::max(hitPos.m_Y, hitPosPredicted.m_Y); + desiredWalkPathYOffset = desiredCrouchHeadRoom - headroom; + } else { + desiredWalkPathYOffset = m_CrouchAmountOverride * m_MaxWalkPathCrouchShift; + } + + float finalWalkPathYOffset = std::clamp(LERP(0.0F, 1.0F, -m_WalkPathOffset.m_Y, desiredWalkPathYOffset, 0.3F), 0.0F, m_MaxWalkPathCrouchShift); + m_WalkPathOffset.m_Y = -finalWalkPathYOffset; + + // If crouching, move at reduced speed + const float crouchSpeedMultiplier = 0.5F; + float travelSpeedMultiplier = LERP(0.0F, m_MaxWalkPathCrouchShift, 1.0F, crouchSpeedMultiplier, -m_WalkPathOffset.m_Y); + m_Paths[FGROUND][WALK].SetTravelSpeedMultiplier(travelSpeedMultiplier); + m_Paths[BGROUND][WALK].SetTravelSpeedMultiplier(travelSpeedMultiplier); + + // Adjust our X offset to try to keep our legs under our centre-of-mass + const float ratioBetweenBodyAndHeadToAimFor = 0.15F; + float predictedPosition = ((m_pHead->GetPos().m_X - m_Pos.m_X) * ratioBetweenBodyAndHeadToAimFor) + m_Vel.m_X; + m_WalkPathOffset.m_X = predictedPosition; + } else { + m_WalkPathOffset.Reset(); } } - //////////////////////////////////// - // Change held MovableObjects - - if (m_pFGArm && m_Status != INACTIVE) { - bool changeNext = m_Controller.IsState(WEAPON_CHANGE_NEXT); - bool changePrev = m_Controller.IsState(WEAPON_CHANGE_PREV); - if (changeNext || changePrev) { - if (changeNext && changePrev) { - UnequipArms(); - } else if (!m_Inventory.empty() || UnequipBGArm()) { - if (HDFirearm *firearm = dynamic_cast(m_pFGArm->GetHeldDevice())) { - firearm->StopActivationSound(); + ////////////////////////////////////////////////////////////////////////////////////////// + + void AHuman::PreControllerUpdate() { + ZoneScoped; + + Actor::PreControllerUpdate(); + + float deltaTime = g_TimerMan.GetDeltaTimeSecs(); + float rot = m_Rotation.GetRadAngle(); + + Vector analogAim = m_Controller.GetAnalogAim(); + const float analogDeadzone = 0.1F; + + m_Paths[FGROUND][m_MoveState].SetHFlip(m_HFlipped); + m_Paths[BGROUND][m_MoveState].SetHFlip(m_HFlipped); + + if (m_pJetpack && m_pJetpack->IsAttached()) { + m_pJetpack->UpdateBurstState(*this); + + if (m_Controller.IsState(BODY_JUMP) && !m_pJetpack->IsOutOfFuel() && m_Status != INACTIVE) { + m_Paths[FGROUND][JUMP].Restart(); + m_Paths[BGROUND][JUMP].Restart(); + } + } + + //////////////////////////////////// + // Movement direction + + const float movementThreshold = 1.0F; + bool isStill = (m_Vel + m_PrevVel).MagnitudeIsLessThan(movementThreshold); + bool isSharpAiming = m_Controller.IsState(AIM_SHARP); + + // If the pie menu is on, try to preserve whatever move state we had before it going into effect. + // This is only done for digital input, where the user needs to use the keyboard to choose pie slices. + // For analog input, this doesn't matter - the mouse or aiming analog stick controls the pie menu. + bool keepOldState = m_Controller.IsKeyboardOnlyControlled() && m_Controller.IsState(PIE_MENU_ACTIVE); + + if (!keepOldState) { + bool crouching = m_Controller.IsState(BODY_CROUCH); + if ((m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP) && m_Status != INACTIVE) { + for (int i = WALK; i < MOVEMENTSTATECOUNT; ++i) { + m_Paths[FGROUND][i].SetHFlip(m_HFlipped); + m_Paths[BGROUND][i].SetHFlip(m_HFlipped); + } + // Only if not jumping, OR if jumping, and apparently stuck on something - then help out with the limbs. + if (m_MoveState != JUMP || isStill) { + // Restart the stride if we're just starting to walk or crawl. + if ((m_MoveState != WALK && !crouching) || (m_MoveState != CRAWL && crouching)) { + m_StrideStart = true; + MoveOutOfTerrain(g_MaterialGrass); + } + + m_MoveState = crouching ? CRAWL : WALK; + + // Engage prone state, this makes the body's rotational spring pull it horizontal instead of upright. + if (m_MoveState == CRAWL && m_ProneState == NOTPRONE) { + m_ProneState = GOPRONE; + m_ProneTimer.Reset(); + } + + m_Paths[FGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); + m_Paths[BGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); + } + + // Walk backwards if the aiming is already focused in the opposite direction of travel. + // Note that we check against zero here rather than the deadzone, because using the deadzone makes jetpacking mouse players unable to fly one way and aim the other. + if (!analogAim.IsZero() || isSharpAiming) { + m_Paths[FGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); + m_Paths[BGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); + } else if ((m_Controller.IsState(MOVE_RIGHT) && m_HFlipped) || (m_Controller.IsState(MOVE_LEFT) && !m_HFlipped)) { + m_HFlipped = !m_HFlipped; + m_CheckTerrIntersection = true; + if (m_ProneState == NOTPRONE) { + MoveOutOfTerrain(g_MaterialGrass); + } + + for (int i = WALK; i < MOVEMENTSTATECOUNT; ++i) { + m_Paths[FGROUND][i].SetHFlip(m_HFlipped); + m_Paths[BGROUND][i].SetHFlip(m_HFlipped); + m_Paths[FGROUND][i].Terminate(); + m_Paths[BGROUND][i].Terminate(); + } + m_StrideStart = true; + // Stop the going prone spring. + if (m_ProneState == GOPRONE) { + m_ProneState = PRONE; + } } - if (changeNext) { - m_pFGArm->SetHeldDevice(dynamic_cast(SwapNextInventory(m_pFGArm->RemoveAttachable(m_pFGArm->GetHeldDevice())))); + } else { + m_ArmClimbing[FGROUND] = false; + m_ArmClimbing[BGROUND] = false; + if (crouching) { + // Don't go back to crouching if we're already prone, the player has to let go of the crouch button first. If already laying down, just stay put. + m_MoveState = m_ProneState == NOTPRONE ? CROUCH : NOMOVE; } else { - m_pFGArm->SetHeldDevice(dynamic_cast(SwapPrevInventory(m_pFGArm->RemoveAttachable(m_pFGArm->GetHeldDevice())))); + m_MoveState = STAND; } + } + // Disengage the prone state as soon as crouch is released. + if (!crouching && m_ProneState != NOTPRONE) { EquipShieldInBGArm(); - m_pFGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + m_ProneState = NOTPRONE; } - m_EquipHUDTimer.Reset(); - m_SharpAimProgress = 0; - // Reload empty firearms when we swap to them, for convenience. - ReloadFirearms(true); } - } - //////////////////////////////////// - // Aiming - - if (m_Controller.IsState(AIM_UP) && m_Status != INACTIVE) { - // Set the timer to a base number so we don't get a sluggish feeling at start. - if (m_AimState != AIMUP) { m_AimTmr.SetElapsedSimTimeMS(m_AimState == AIMSTILL ? 150 : 300); } - m_AimState = AIMUP; - m_AimAngle += isSharpAiming ? std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00005F, 0.05F) : std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00015F, 0.15F) * m_Controller.GetDigitalAimSpeed(); - if (m_AimAngle > m_AimRange) { m_AimAngle = m_AimRange; } - - } else if (m_Controller.IsState(AIM_DOWN) && m_Status != INACTIVE) { - // Set the timer to a base number so we don't get a sluggish feeling at start. - if (m_AimState != AIMDOWN) {m_AimTmr.SetElapsedSimTimeMS(m_AimState == AIMSTILL ? 150 : 300); } - m_AimState = AIMDOWN; - m_AimAngle -= isSharpAiming ? std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00005F, 0.05F) : std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00015F, 0.15F) * m_Controller.GetDigitalAimSpeed(); - if (m_AimAngle < -m_AimRange) { m_AimAngle = -m_AimRange; } - - } else if (analogAim.MagnitudeIsGreaterThan(analogDeadzone) && m_Status != INACTIVE) { - // Hack to avoid the GetAbsRadAngle from mangling an aim angle straight down. - if (analogAim.m_X == 0) { analogAim.m_X += 0.01F * GetFlipFactor(); } - m_AimAngle = analogAim.GetAbsRadAngle(); - - if ((analogAim.m_X > 0 && m_HFlipped) || (analogAim.m_X < 0 && !m_HFlipped)) { - m_HFlipped = !m_HFlipped; - m_CheckTerrIntersection = true; - if (m_ProneState == NOTPRONE) { MoveOutOfTerrain(g_MaterialGrass); } - for (int i = STAND; i < CLIMB; ++i) { - m_Paths[FGROUND][i].SetHFlip(m_HFlipped); - m_Paths[BGROUND][i].SetHFlip(m_HFlipped); - m_Paths[FGROUND][i].Terminate(); - m_Paths[BGROUND][i].Terminate(); - } - m_StrideStart = true; - // Stop the going prone spring. - if (m_ProneState == GOPRONE) { m_ProneState = PRONE; } - } - // Correct angle based on flip. - m_AimAngle = FacingAngle(m_AimAngle); - // Clamp so it's within the range. - Clamp(m_AimAngle, m_AimRange, -m_AimRange); - } else { - m_AimState = AIMSTILL; - } - float adjustedAimAngle = m_AimAngle * GetFlipFactor(); + //////////////////////////////////// + // Standard Reloading + + for (const Arm* arm: {m_pFGArm, m_pBGArm}) { + if (arm) { + if (HDFirearm* heldFirearm = dynamic_cast(arm->GetHeldDevice())) { + Arm* otherArm = arm == m_pFGArm ? m_pBGArm : m_pFGArm; + bool otherArmIsAvailable = otherArm && !otherArm->GetHeldDevice(); + if (otherArmIsAvailable && heldFirearm->DoneReloading()) { + otherArm->SetHandPos(heldFirearm->GetMagazinePos()); + }; + heldFirearm->SetSupportAvailable(otherArmIsAvailable); + } + } + } + if (m_Controller.IsState(ControlState::WEAPON_RELOAD)) { + ReloadFirearms(); + } + if (m_WaitingToReloadOffhand) { + if (HeldDevice* equippedItem = GetEquippedItem(); equippedItem && !equippedItem->IsReloading()) { + ReloadFirearms(); + m_WaitingToReloadOffhand = false; + } + } + + //////////////////////////////////// + // Change held MovableObjects + + if (m_pFGArm && m_Status != INACTIVE) { + bool changeNext = m_Controller.IsState(WEAPON_CHANGE_NEXT); + bool changePrev = m_Controller.IsState(WEAPON_CHANGE_PREV); + if (changeNext || changePrev) { + if (changeNext && changePrev) { + UnequipArms(); + } else if (!m_Inventory.empty() || UnequipBGArm()) { + if (HDFirearm* firearm = dynamic_cast(m_pFGArm->GetHeldDevice())) { + firearm->StopActivationSound(); + } + if (changeNext) { + m_pFGArm->SetHeldDevice(dynamic_cast(SwapNextInventory(m_pFGArm->RemoveAttachable(m_pFGArm->GetHeldDevice())))); + } else { + m_pFGArm->SetHeldDevice(dynamic_cast(SwapPrevInventory(m_pFGArm->RemoveAttachable(m_pFGArm->GetHeldDevice())))); + } + EquipShieldInBGArm(); + m_pFGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + } + m_EquipHUDTimer.Reset(); + m_SharpAimProgress = 0; + // Reload empty firearms when we swap to them, for convenience. + ReloadFirearms(true); + } + } + + //////////////////////////////////// + // Aiming - ////////////////////////////// - // Sharp aim calculation + if (m_Controller.IsState(AIM_UP) && m_Status != INACTIVE) { + // Set the timer to a base number so we don't get a sluggish feeling at start. + if (m_AimState != AIMUP) { + m_AimTmr.SetElapsedSimTimeMS(m_AimState == AIMSTILL ? 150 : 300); + } + m_AimState = AIMUP; + m_AimAngle += isSharpAiming ? std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00005F, 0.05F) : std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00015F, 0.15F) * m_Controller.GetDigitalAimSpeed(); + if (m_AimAngle > m_AimRange) { + m_AimAngle = m_AimRange; + } -// TODO: make the delay data driven by both the actor and the device! - // - if (isSharpAiming && m_Status == STABLE && (m_MoveState == STAND || m_MoveState == CROUCH || m_MoveState == NOMOVE || m_MoveState == WALK) && m_Vel.MagnitudeIsLessThan(5.0F) && GetEquippedItem()) { - float aimMag = analogAim.GetMagnitude(); + } else if (m_Controller.IsState(AIM_DOWN) && m_Status != INACTIVE) { + // Set the timer to a base number so we don't get a sluggish feeling at start. + if (m_AimState != AIMDOWN) { + m_AimTmr.SetElapsedSimTimeMS(m_AimState == AIMSTILL ? 150 : 300); + } + m_AimState = AIMDOWN; + m_AimAngle -= isSharpAiming ? std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00005F, 0.05F) : std::min(static_cast(m_AimTmr.GetElapsedSimTimeMS()) * 0.00015F, 0.15F) * m_Controller.GetDigitalAimSpeed(); + if (m_AimAngle < -m_AimRange) { + m_AimAngle = -m_AimRange; + } - // If aim sharp is being done digitally, then translate to full analog aim mag - if (aimMag < 0.1F) { aimMag = 1.0F; } - if (m_MoveState == WALK) { aimMag *= 0.3F; } + } else if (analogAim.MagnitudeIsGreaterThan(analogDeadzone) && m_Status != INACTIVE) { + // Hack to avoid the GetAbsRadAngle from mangling an aim angle straight down. + if (analogAim.m_X == 0) { + analogAim.m_X += 0.01F * GetFlipFactor(); + } + m_AimAngle = analogAim.GetAbsRadAngle(); - if (m_SharpAimTimer.IsPastSimMS(m_SharpAimDelay)) { - // Only go slower outward - if (m_SharpAimProgress < aimMag) { - m_SharpAimProgress += (aimMag - m_SharpAimProgress) * 0.035F; - } else { - m_SharpAimProgress = aimMag; + if ((analogAim.m_X > 0 && m_HFlipped) || (analogAim.m_X < 0 && !m_HFlipped)) { + m_HFlipped = !m_HFlipped; + m_CheckTerrIntersection = true; + if (m_ProneState == NOTPRONE) { + MoveOutOfTerrain(g_MaterialGrass); + } + for (int i = STAND; i < CLIMB; ++i) { + m_Paths[FGROUND][i].SetHFlip(m_HFlipped); + m_Paths[BGROUND][i].SetHFlip(m_HFlipped); + m_Paths[FGROUND][i].Terminate(); + m_Paths[BGROUND][i].Terminate(); + } + m_StrideStart = true; + // Stop the going prone spring. + if (m_ProneState == GOPRONE) { + m_ProneState = PRONE; + } } - m_SharpAimRevertTimer.Reset(); + // Correct angle based on flip. + m_AimAngle = FacingAngle(m_AimAngle); + // Clamp so it's within the range. + Clamp(m_AimAngle, m_AimRange, -m_AimRange); } else { - m_SharpAimProgress *= 0.95F; - m_SharpAimRevertTimer.SetElapsedSimTimeMS(m_SharpAimDelay - m_SharpAimTimer.GetElapsedSimTimeMS()); + m_AimState = AIMSTILL; } - } else { - m_SharpAimProgress = std::max(m_SharpAimProgress * 0.95F - 0.1F, 0.0F); - if (m_SharpAimRevertTimer.IsPastSimMS(m_SharpAimDelay)) { - m_SharpAimTimer.Reset(); + float adjustedAimAngle = m_AimAngle * GetFlipFactor(); + + ////////////////////////////// + // Sharp aim calculation + + // TODO: make the delay data driven by both the actor and the device! + // + if (isSharpAiming && m_Status == STABLE && (m_MoveState == STAND || m_MoveState == CROUCH || m_MoveState == NOMOVE || m_MoveState == WALK) && m_Vel.MagnitudeIsLessThan(5.0F) && GetEquippedItem()) { + float aimMag = analogAim.GetMagnitude(); + + // If aim sharp is being done digitally, then translate to full analog aim mag + if (aimMag < 0.1F) { + aimMag = 1.0F; + } + if (m_MoveState == WALK) { + aimMag *= 0.3F; + } + + if (m_SharpAimTimer.IsPastSimMS(m_SharpAimDelay)) { + // Only go slower outward + if (m_SharpAimProgress < aimMag) { + m_SharpAimProgress += (aimMag - m_SharpAimProgress) * 0.035F; + } else { + m_SharpAimProgress = aimMag; + } + m_SharpAimRevertTimer.Reset(); + } else { + m_SharpAimProgress *= 0.95F; + m_SharpAimRevertTimer.SetElapsedSimTimeMS(m_SharpAimDelay - m_SharpAimTimer.GetElapsedSimTimeMS()); + } } else { - m_SharpAimTimer.SetElapsedSimTimeMS(m_SharpAimDelay - m_SharpAimRevertTimer.GetElapsedSimTimeMS()); + m_SharpAimProgress = std::max(m_SharpAimProgress * 0.95F - 0.1F, 0.0F); + if (m_SharpAimRevertTimer.IsPastSimMS(m_SharpAimDelay)) { + m_SharpAimTimer.Reset(); + } else { + m_SharpAimTimer.SetElapsedSimTimeMS(m_SharpAimDelay - m_SharpAimRevertTimer.GetElapsedSimTimeMS()); + } } - } - //////////////////////////////////// - // Handle firing/activating/throwing HeldDevices and ThrownDevices. - // Also deal with certain reload cases and setting sharp aim progress for HeldDevices. + //////////////////////////////////// + // Handle firing/activating/throwing HeldDevices and ThrownDevices. + // Also deal with certain reload cases and setting sharp aim progress for HeldDevices. - ThrownDevice *thrownDevice = nullptr; - if (HeldDevice *device = GetEquippedItem(); device && m_Status != INACTIVE) { - if (!dynamic_cast(device)) { - device->SetSharpAim(m_SharpAimProgress); + ThrownDevice* thrownDevice = nullptr; + if (HeldDevice* device = GetEquippedItem(); device && m_Status != INACTIVE) { + if (!dynamic_cast(device)) { + device->SetSharpAim(m_SharpAimProgress); - if (HDFirearm *deviceAsFirearm = dynamic_cast(device)) { - if (m_Controller.IsState(WEAPON_FIRE)) { - if (!m_CanActivateBGItem) { - if (deviceAsFirearm->IsFullAuto()) { - deviceAsFirearm->Activate(); - m_CanActivateBGItem = deviceAsFirearm->FiredOnce() && deviceAsFirearm->HalfwayToNextRound(); - } else if (!m_TriggerPulled) { - deviceAsFirearm->Activate(); - if (deviceAsFirearm->FiredOnce()) { - m_CanActivateBGItem = true; - m_TriggerPulled = true; - } else { - m_CanActivateBGItem = !deviceAsFirearm->CanFire(); + if (HDFirearm* deviceAsFirearm = dynamic_cast(device)) { + if (m_Controller.IsState(WEAPON_FIRE)) { + if (!m_CanActivateBGItem) { + if (deviceAsFirearm->IsFullAuto()) { + deviceAsFirearm->Activate(); + m_CanActivateBGItem = deviceAsFirearm->FiredOnce() && deviceAsFirearm->HalfwayToNextRound(); + } else if (!m_TriggerPulled) { + deviceAsFirearm->Activate(); + if (deviceAsFirearm->FiredOnce()) { + m_CanActivateBGItem = true; + m_TriggerPulled = true; + } else { + m_CanActivateBGItem = !deviceAsFirearm->CanFire(); + } } } + } else { + deviceAsFirearm->Deactivate(); + m_TriggerPulled = false; + } + } else { + m_CanActivateBGItem = true; + if (m_Controller.IsState(WEAPON_FIRE)) { + device->Activate(); + if (device->IsEmpty()) { + ReloadFirearms(true); + } + } else { + device->Deactivate(); + } + } + // If reloading 2 guns one-at-a-time, the automatic reload when firing empty won't trigger, so this makes sure it happens automatically. + if (device->IsEmpty()) { + ReloadFirearms(true); + } + + if (device->IsReloading()) { + m_CanActivateBGItem = true; + m_SharpAimTimer.Reset(); + m_SharpAimProgress = 0; + device->SetSharpAim(m_SharpAimProgress); + } + } else { + m_CanActivateBGItem = true; + if (thrownDevice = dynamic_cast(device)) { + thrownDevice->SetSharpAim(isSharpAiming ? 1.0F : 0); + if (m_Controller.IsState(WEAPON_FIRE)) { + if (m_ArmsState != THROWING_PREP) { + m_ThrowTmr.Reset(); + if (!thrownDevice->ActivatesWhenReleased()) { + thrownDevice->Activate(); + } + } + float throwProgress = GetThrowProgress(); + m_ArmsState = THROWING_PREP; + m_pFGArm->SetHandPos(m_pFGArm->GetJointPos() + (thrownDevice->GetStartThrowOffset().GetXFlipped(m_HFlipped) * throwProgress + thrownDevice->GetStanceOffset() * (1.0F - throwProgress)).RadRotate(adjustedAimAngle)); + } else if (m_ArmsState == THROWING_PREP) { + m_ArmsState = THROWING_RELEASE; + m_pFGArm->SetHandPos(m_pFGArm->GetJointPos() + thrownDevice->GetEndThrowOffset().RadRotate(adjustedAimAngle).GetXFlipped(m_HFlipped)); + + float maxThrowVel = thrownDevice->GetCalculatedMaxThrowVelIncludingArmThrowStrength(); + if (MovableObject* pMO = m_pFGArm->RemoveAttachable(thrownDevice)) { + pMO->SetPos(m_pFGArm->GetJointPos() + Vector(m_pFGArm->GetMaxLength() * GetFlipFactor(), -m_pFGArm->GetMaxLength() * 0.5F).RadRotate(adjustedAimAngle)); + float minThrowVel = thrownDevice->GetMinThrowVel(); + if (minThrowVel == 0) { + minThrowVel = maxThrowVel * 0.2F; + } + + Vector tossVec(minThrowVel + (maxThrowVel - minThrowVel) * GetThrowProgress(), 0.5F * RandomNormalNum()); + pMO->SetVel(m_Vel * 0.5F + tossVec.RadRotate(m_AimAngle).GetXFlipped(m_HFlipped)); + pMO->SetAngularVel(m_AngularVel + RandomNum(-5.0F, 2.5F) * GetFlipFactor()); + pMO->SetRotAngle(adjustedAimAngle); + + if (HeldDevice* moAsHeldDevice = dynamic_cast(pMO)) { + moAsHeldDevice->SetTeam(m_Team); + moAsHeldDevice->SetIgnoresTeamHits(true); + g_MovableMan.AddItem(moAsHeldDevice); + } + pMO = 0; + } + if (thrownDevice->ActivatesWhenReleased()) { + thrownDevice->Activate(); + } + m_ThrowTmr.Reset(); + } + } else if (m_ArmsState == THROWING_RELEASE && m_ThrowTmr.GetElapsedSimTimeMS() > 100) { + m_pFGArm->SetHeldDevice(dynamic_cast(SwapNextInventory())); + m_pFGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + EquipShieldInBGArm(); + m_ArmsState = WEAPON_READY; + } else if (m_ArmsState == THROWING_RELEASE) { + m_pFGArm->AddHandTarget("Adjusted Aim Angle", m_Pos + Vector(m_pFGArm->GetMaxLength() * GetFlipFactor(), -m_pFGArm->GetMaxLength() * 0.5F).RadRotate(adjustedAimAngle)); + } + } + } else if (m_ArmsState == THROWING_RELEASE && m_ThrowTmr.GetElapsedSimTimeMS() > 100) { + if (m_pFGArm) { + m_pFGArm->SetHeldDevice(dynamic_cast(SwapNextInventory())); + m_pFGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + } + EquipShieldInBGArm(); + m_ArmsState = WEAPON_READY; + } else if (m_ArmsState == THROWING_RELEASE && m_pFGArm) { + m_pFGArm->AddHandTarget("Adjusted Aim Angle", m_Pos + Vector(m_pFGArm->GetMaxLength() * GetFlipFactor(), -m_pFGArm->GetMaxLength() * 0.5F).RadRotate(adjustedAimAngle)); + } else { + m_CanActivateBGItem = true; + } + + if (HeldDevice* device = GetEquippedBGItem(); device && m_Status != INACTIVE) { + if (HDFirearm* deviceAsFirearm = dynamic_cast(device)) { + if (m_Controller.IsState(WEAPON_FIRE)) { + if (m_CanActivateBGItem && (!m_TriggerPulled || (deviceAsFirearm->IsFullAuto() && deviceAsFirearm->HalfwayToNextRound()))) { + deviceAsFirearm->Activate(); + if (deviceAsFirearm->FiredOnce()) { + m_CanActivateBGItem = false; + m_TriggerPulled = true; + } else { + m_CanActivateBGItem = deviceAsFirearm->CanFire(); + } } } else { deviceAsFirearm->Deactivate(); m_TriggerPulled = false; } } else { - m_CanActivateBGItem = true; + m_CanActivateBGItem = false; if (m_Controller.IsState(WEAPON_FIRE)) { device->Activate(); - if (device->IsEmpty()) { - ReloadFirearms(true); - } } else { device->Deactivate(); } @@ -2046,1129 +2135,1075 @@ void AHuman::PreControllerUpdate() if (device->IsEmpty()) { ReloadFirearms(true); } + device->SetSharpAim(m_SharpAimProgress); if (device->IsReloading()) { - m_CanActivateBGItem = true; + m_CanActivateBGItem = false; m_SharpAimTimer.Reset(); m_SharpAimProgress = 0; device->SetSharpAim(m_SharpAimProgress); } } else { - m_CanActivateBGItem = true; - if (thrownDevice = dynamic_cast(device)) { - thrownDevice->SetSharpAim(isSharpAiming ? 1.0F : 0); - if (m_Controller.IsState(WEAPON_FIRE)) { - if (m_ArmsState != THROWING_PREP) { - m_ThrowTmr.Reset(); - if (!thrownDevice->ActivatesWhenReleased()) { thrownDevice->Activate(); } - } - float throwProgress = GetThrowProgress(); - m_ArmsState = THROWING_PREP; - m_pFGArm->SetHandPos(m_pFGArm->GetJointPos() + (thrownDevice->GetStartThrowOffset().GetXFlipped(m_HFlipped) * throwProgress + thrownDevice->GetStanceOffset() * (1.0F - throwProgress)).RadRotate(adjustedAimAngle)); - } else if (m_ArmsState == THROWING_PREP) { - m_ArmsState = THROWING_RELEASE; - m_pFGArm->SetHandPos(m_pFGArm->GetJointPos() + thrownDevice->GetEndThrowOffset().RadRotate(adjustedAimAngle).GetXFlipped(m_HFlipped)); - - float maxThrowVel = thrownDevice->GetCalculatedMaxThrowVelIncludingArmThrowStrength(); - if (MovableObject *pMO = m_pFGArm->RemoveAttachable(thrownDevice)) { - pMO->SetPos(m_pFGArm->GetJointPos() + Vector(m_pFGArm->GetMaxLength() * GetFlipFactor(), -m_pFGArm->GetMaxLength() * 0.5F).RadRotate(adjustedAimAngle)); - float minThrowVel = thrownDevice->GetMinThrowVel(); - if (minThrowVel == 0) { minThrowVel = maxThrowVel * 0.2F; } - - Vector tossVec(minThrowVel + (maxThrowVel - minThrowVel) * GetThrowProgress(), 0.5F * RandomNormalNum()); - pMO->SetVel(m_Vel * 0.5F + tossVec.RadRotate(m_AimAngle).GetXFlipped(m_HFlipped)); - pMO->SetAngularVel(m_AngularVel + RandomNum(-5.0F, 2.5F) * GetFlipFactor()); - pMO->SetRotAngle(adjustedAimAngle); - - if (HeldDevice *moAsHeldDevice = dynamic_cast(pMO)) { - moAsHeldDevice->SetTeam(m_Team); - moAsHeldDevice->SetIgnoresTeamHits(true); - g_MovableMan.AddItem(moAsHeldDevice); + m_CanActivateBGItem = false; + } + + if (m_ArmsState == THROWING_PREP && !thrownDevice) { + m_ArmsState = WEAPON_READY; + } + + // m_aSprite->SetAngle((m_AimAngle / 180) * 3.141592654); + // m_aSprite->SetScale(2.0); + + //////////////////////////////////////// + // Item dropping logic + + if (m_Controller.IsState(WEAPON_DROP) && m_Status != INACTIVE) { + Arm* dropperArm = nullptr; + for (Arm* arm: {m_pFGArm, m_pBGArm}) { + if (arm && arm->GetHeldDevice()) { + HeldDevice* heldDevice = arm->GetHeldDevice(); + arm->RemoveAttachable(heldDevice, true, false); + if (dropperArm) { + if (heldDevice) { + dropperArm->SetHeldDevice(heldDevice); + arm->SetHandPos(dropperArm->GetPos()); } - pMO = 0; + } else { + heldDevice->SetPos(arm->GetJointPos() + Vector(arm->GetMaxLength() * GetFlipFactor(), 0).RadRotate(adjustedAimAngle)); + Vector tossVec(1.0F + std::sqrt(std::abs(arm->GetThrowStrength()) / std::sqrt(std::abs(heldDevice->GetMass()) + 1.0F)), RandomNormalNum()); + heldDevice->SetVel(heldDevice->GetVel() * 0.5F + tossVec.RadRotate(m_AimAngle).GetXFlipped(m_HFlipped)); + heldDevice->SetAngularVel(heldDevice->GetAngularVel() + m_AngularVel * 0.5F + 3.0F * RandomNormalNum()); + + arm->SetHandPos(heldDevice->GetPos()); } - if (thrownDevice->ActivatesWhenReleased()) { thrownDevice->Activate(); } - m_ThrowTmr.Reset(); + dropperArm = arm; + } else if (dropperArm && !m_Inventory.empty()) { + dropperArm->SetHeldDevice(dynamic_cast(SwapNextInventory())); + dropperArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + } + } + if (!dropperArm && !m_Inventory.empty() && !m_pFGArm) { + DropAllInventory(); + if (m_pBGArm) { + m_pBGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); } - } else if (m_ArmsState == THROWING_RELEASE && m_ThrowTmr.GetElapsedSimTimeMS() > 100) { - m_pFGArm->SetHeldDevice(dynamic_cast(SwapNextInventory())); - m_pFGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); - EquipShieldInBGArm(); - m_ArmsState = WEAPON_READY; - } else if (m_ArmsState == THROWING_RELEASE) { - m_pFGArm->AddHandTarget("Adjusted Aim Angle", m_Pos + Vector(m_pFGArm->GetMaxLength() * GetFlipFactor(), -m_pFGArm->GetMaxLength() * 0.5F).RadRotate(adjustedAimAngle)); } + EquipShieldInBGArm(); + m_SharpAimProgress = 0; + m_EquipHUDTimer.Reset(); } - } else if (m_ArmsState == THROWING_RELEASE && m_ThrowTmr.GetElapsedSimTimeMS() > 100) { - if (m_pFGArm) { - m_pFGArm->SetHeldDevice(dynamic_cast(SwapNextInventory())); - m_pFGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + + //////////////////////////////////////// + // Item pickup logic + + float reach = m_SpriteRadius; + Vector reachPoint = m_Pos; + + // Try to detect a new item + if ((m_pFGArm || m_pBGArm) && m_Status == STABLE) { + reach += m_pFGArm ? m_pFGArm->GetMaxLength() : m_pBGArm->GetMaxLength(); + reachPoint = m_pFGArm ? m_pFGArm->GetJointPos() : m_pBGArm->GetJointPos(); + + MOID itemMOID = g_SceneMan.CastMORay(reachPoint, Vector(reach * RandomNum(0.5F, 1.0F) * GetFlipFactor(), 0).RadRotate(m_pItemInReach ? adjustedAimAngle : RandomNum(-(c_HalfPI + c_EighthPI), m_AimAngle * 0.75F + c_EighthPI) * GetFlipFactor()), m_MOID, m_Team, g_MaterialGrass, true, 3); + + if (MovableObject* foundMO = g_MovableMan.GetMOFromID(itemMOID)) { + if (HeldDevice* foundDevice = dynamic_cast(foundMO->GetRootParent())) { + m_pItemInReach = (m_pFGArm || foundDevice->IsOneHanded()) ? foundDevice : nullptr; + } + } } - EquipShieldInBGArm(); - m_ArmsState = WEAPON_READY; - } else if (m_ArmsState == THROWING_RELEASE && m_pFGArm) { - m_pFGArm->AddHandTarget("Adjusted Aim Angle", m_Pos + Vector(m_pFGArm->GetMaxLength() * GetFlipFactor(), -m_pFGArm->GetMaxLength() * 0.5F).RadRotate(adjustedAimAngle)); - } else { - m_CanActivateBGItem = true; - } - if (HeldDevice *device = GetEquippedBGItem(); device && m_Status != INACTIVE) { - if (HDFirearm *deviceAsFirearm = dynamic_cast(device)) { - if (m_Controller.IsState(WEAPON_FIRE)) { - if (m_CanActivateBGItem && (!m_TriggerPulled || (deviceAsFirearm->IsFullAuto() && deviceAsFirearm->HalfwayToNextRound()))) { - deviceAsFirearm->Activate(); - if (deviceAsFirearm->FiredOnce()) { - m_CanActivateBGItem = false; - m_TriggerPulled = true; - } else { - m_CanActivateBGItem = deviceAsFirearm->CanFire(); + // Item currently set to be within reach has expired or is now out of range + if (m_pItemInReach && (!m_pItemInReach->IsPickupableBy(this) || !g_MovableMan.IsDevice(m_pItemInReach) || g_SceneMan.ShortestDistance(reachPoint, m_pItemInReach->GetPos(), g_SceneMan.SceneWrapsX()).MagnitudeIsGreaterThan(reach + m_pItemInReach->GetRadius()))) { + m_pItemInReach = nullptr; + } + + if (m_pItemInReach && (m_pFGArm || m_pBGArm) && m_Controller.IsState(WEAPON_PICKUP) && m_Status != INACTIVE && g_MovableMan.RemoveMO(m_pItemInReach)) { + Arm* armToUse = m_pFGArm ? m_pFGArm : m_pBGArm; + Attachable* pMO = armToUse->RemoveAttachable(armToUse->GetHeldDevice()); + AddToInventoryBack(pMO); + armToUse->SetHandPos(m_pItemInReach->GetJointPos()); + armToUse->SetHeldDevice(m_pItemInReach); + m_pItemInReach = nullptr; + + if (armToUse != m_pBGArm) { + EquipShieldInBGArm(); + } + m_SharpAimProgress = 0; + if (m_DeviceSwitchSound) { + m_DeviceSwitchSound->Play(m_Pos); + } + + m_EquipHUDTimer.Reset(); + } + + /////////////////////////////////////////////////// + // Travel the limb AtomGroup:s + + m_StrideFrame = false; + + UpdateCrouching(); + + if (m_Status == STABLE && !m_LimbPushForcesAndCollisionsDisabled && m_MoveState != NOMOVE) { + // This exists to support disabling foot collisions if the limbpath has that flag set. + if ((m_pFGFootGroup->GetAtomCount() == 0 && m_BackupFGFootGroup->GetAtomCount() > 0) != m_Paths[FGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupFGFootGroup->SetLimbPos(m_pFGFootGroup->GetLimbPos()); + std::swap(m_pFGFootGroup, m_BackupFGFootGroup); + } + if ((m_pBGFootGroup->GetAtomCount() == 0 && m_BackupBGFootGroup->GetAtomCount() > 0) != m_Paths[BGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupBGFootGroup->SetLimbPos(m_pBGFootGroup->GetLimbPos()); + std::swap(m_pBGFootGroup, m_BackupBGFootGroup); + } + + if (m_pFGLeg) { + UpdateWalkAngle(FGROUND); + } + if (m_pBGLeg) { + UpdateWalkAngle(BGROUND); + } + + // WALKING, OR WE ARE JETPACKING AND STUCK + if (m_MoveState == WALK || (m_MoveState == JUMP && isStill)) { + m_Paths[FGROUND][STAND].Terminate(); + m_Paths[BGROUND][STAND].Terminate(); + + // float FGLegProg = MAX(m_Paths[FGROUND][WALK].GetRegularProgress(), m_Paths[FGROUND][WALK].GetTotalTimeProgress()); + // float BGLegProg = MAX(m_Paths[BGROUND][WALK].GetRegularProgress(), m_Paths[BGROUND][WALK].GetTotalTimeProgress()); + float FGLegProg = m_Paths[FGROUND][WALK].GetRegularProgress(); + float BGLegProg = m_Paths[BGROUND][WALK].GetRegularProgress(); + + bool restarted = false; + + // Make sure we are starting a stride if we're basically stopped. + if (isStill) { + m_StrideStart = true; + } + + if (m_pFGLeg && (!m_pBGLeg || !(m_Paths[FGROUND][WALK].PathEnded() && BGLegProg < 0.5F) || m_StrideStart)) { + // Reset the stride timer if the path is about to restart. + if (m_Paths[FGROUND][WALK].PathEnded() || m_Paths[FGROUND][WALK].PathIsAtStart()) { + m_StrideTimer.Reset(); + } + Vector jointPos = m_Pos + RotateOffset(m_pFGLeg->GetParentOffset()); + m_ArmClimbing[BGROUND] = !m_pFGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[FGROUND], m_Paths[FGROUND][WALK], deltaTime, &restarted, false, Vector(0.0F, m_Paths[FGROUND][WALK].GetLowestY()), m_WalkPathOffset); + } else { + m_ArmClimbing[BGROUND] = false; + } + if (m_pBGLeg && (!m_pFGLeg || !(m_Paths[BGROUND][WALK].PathEnded() && FGLegProg < 0.5F))) { + m_StrideStart = false; + // Reset the stride timer if the path is about to restart. + if (m_Paths[BGROUND][WALK].PathEnded() || m_Paths[BGROUND][WALK].PathIsAtStart()) { + m_StrideTimer.Reset(); + } + Vector jointPos = m_Pos + RotateOffset(m_pBGLeg->GetParentOffset()); + m_ArmClimbing[FGROUND] = !m_pBGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[BGROUND], m_Paths[BGROUND][WALK], deltaTime, &restarted, false, Vector(0.0F, m_Paths[BGROUND][WALK].GetLowestY()), m_WalkPathOffset); + } else { + if (m_pBGLeg) { + m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); + } + m_ArmClimbing[FGROUND] = false; + } + bool climbing = m_ArmClimbing[FGROUND] || m_ArmClimbing[BGROUND]; + + if (m_StrideSound) { + m_StrideSound->SetPosition(m_Pos); + if (m_StrideSound->GetLoopSetting() < 0) { + if (!m_StrideSound->IsBeingPlayed()) { + m_StrideSound->Play(); + } + } else if (restarted && !climbing) { + m_StrideSound->Play(); + } + } + if (restarted) { + if (climbing) { + m_WalkAngle[FGROUND] = Matrix(); + m_WalkAngle[BGROUND] = Matrix(); + } else { + m_StrideFrame = true; + RunScriptedFunctionInAppropriateScripts("OnStride"); + } + } + + //////////////////////////////////////// + // Arm Climbing if the leg paths failed to find clear spot to restart + + // float FGArmProg = MAX(m_Paths[FGROUND][CLIMB].GetRegularProgress(), m_Paths[FGROUND][CLIMB].GetTotalTimeProgress()); + // float BGArmProg = MAX(m_Paths[BGROUND][CLIMB].GetRegularProgress(), m_Paths[BGROUND][CLIMB].GetTotalTimeProgress()); + float FGArmProg = m_Paths[FGROUND][CLIMB].GetRegularProgress(); + float BGArmProg = m_Paths[BGROUND][CLIMB].GetRegularProgress(); + + // TODO: Figure out what this comment means, and then rephrase it better! + // Slightly negative BGArmProg makes sense because any progress on the starting segments are reported as negative, + // and there's many starting segments on properly formed climbing paths + if (climbing) { + if (m_pFGArm && !m_pFGArm->GetHeldDevice() && !(m_Paths[FGROUND][CLIMB].PathEnded() && BGArmProg > 0.1F)) { // < 0.5F + m_ArmClimbing[FGROUND] = true; + m_Paths[FGROUND][WALK].Terminate(); + m_StrideStart = true; + // Reset the stride timer if the path is about to restart. + if (m_Paths[FGROUND][CLIMB].PathEnded() || m_Paths[FGROUND][CLIMB].PathIsAtStart()) { + m_StrideTimer.Reset(); + } + m_pFGHandGroup->PushAsLimb(m_Pos + Vector(0, m_pFGArm->GetParentOffset().m_Y).RadRotate(-rot), m_Vel, Matrix(), m_Paths[FGROUND][CLIMB], deltaTime, 0, false); + } else { + m_ArmClimbing[FGROUND] = false; + m_Paths[FGROUND][CLIMB].Terminate(); + } + if (m_pBGArm) { + m_ArmClimbing[BGROUND] = true; + m_Paths[BGROUND][WALK].Terminate(); + m_StrideStart = true; + // Reset the stride timer if the path is about to restart. + if (m_Paths[BGROUND][CLIMB].PathEnded() || m_Paths[BGROUND][CLIMB].PathIsAtStart()) { + m_StrideTimer.Reset(); + } + m_pBGHandGroup->PushAsLimb(m_Pos + Vector(0, m_pBGArm->GetParentOffset().m_Y).RadRotate(-rot), m_Vel, Matrix(), m_Paths[BGROUND][CLIMB], deltaTime, 0, false); + } else { + m_ArmClimbing[BGROUND] = false; + m_Paths[BGROUND][CLIMB].Terminate(); + } + } + + // Restart the climbing stroke if the current one seems to be taking too long with no movement. + if (climbing && isStill && m_StrideTimer.IsPastSimMS(static_cast(m_Paths[BGROUND][CLIMB].GetTotalPathTime() * 0.5F))) { + m_StrideStart = true; + m_Paths[FGROUND][CLIMB].Terminate(); + m_Paths[BGROUND][CLIMB].Terminate(); + } else if (m_StrideTimer.IsPastSimMS(static_cast(m_Paths[FGROUND][WALK].GetTotalPathTime() * 1.1F))) { + // Reset the walking stride if it's taking longer than it should. + m_StrideStart = true; + m_Paths[FGROUND][WALK].Terminate(); + m_Paths[BGROUND][WALK].Terminate(); + } + } else if (m_MoveState == CRAWL) { + // Start crawling only once we are fully prone. + if (m_ProneState == PRONE) { + + float FGLegProg = m_Paths[FGROUND][CRAWL].GetRegularProgress(); + float BGLegProg = m_Paths[BGROUND][CRAWL].GetRegularProgress(); + + if (m_pFGLeg && (!m_pBGLeg || (!(m_Paths[FGROUND][CRAWL].PathEnded() && BGLegProg < 0.5F) || m_StrideStart))) { + if (m_Paths[FGROUND][CRAWL].PathEnded() || m_Paths[FGROUND][CRAWL].PathIsAtStart()) { + m_StrideTimer.Reset(); + } + m_pFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pFGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[FGROUND][CRAWL], deltaTime); + } else { + m_Paths[FGROUND][CRAWL].Terminate(); + } + if (m_pBGLeg && (!m_pFGLeg || !(m_Paths[BGROUND][CRAWL].PathEnded() && FGLegProg < 0.5F))) { + m_StrideStart = false; + if (m_Paths[BGROUND][CRAWL].PathEnded() || m_Paths[BGROUND][CRAWL].PathIsAtStart()) { + m_StrideTimer.Reset(); + } + m_pBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pBGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[BGROUND][CRAWL], deltaTime); + } else { + m_Paths[BGROUND][CRAWL].Terminate(); + } + if (m_pBGArm) { + m_ArmClimbing[BGROUND] = true; + m_pBGHandGroup->PushAsLimb(m_Pos + RotateOffset(Vector(0, m_pBGArm->GetParentOffset().m_Y)), m_Vel, m_Rotation, m_Paths[BGROUND][ARMCRAWL], deltaTime); + } + if (m_pFGArm && !m_pFGArm->GetHeldDevice() && !(m_Paths[FGROUND][ARMCRAWL].PathEnded() && m_Paths[BGROUND][ARMCRAWL].GetRegularProgress() < 0.5F)) { + m_ArmClimbing[FGROUND] = true; + m_pFGHandGroup->PushAsLimb(m_Pos + RotateOffset(Vector(0, m_pFGArm->GetParentOffset().m_Y)), m_Vel, m_Rotation, m_Paths[FGROUND][ARMCRAWL], deltaTime); + } + // Restart the stride if the current one seems to be taking too long. + if (m_StrideTimer.IsPastSimMS(m_Paths[FGROUND][CRAWL].GetTotalPathTime())) { + m_StrideStart = true; + m_Paths[FGROUND][CRAWL].Terminate(); + m_Paths[BGROUND][CRAWL].Terminate(); + } + } else { + if (m_pFGLeg) { + m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pFGLeg->GetMass(), deltaTime); + } + + if (m_pBGLeg) { + m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); + } + } + } else if (m_pFGLeg || m_pBGLeg) { + if (m_MoveState == JUMP) { + // TODO: Utilize jump paths in an intuitive way! + if (m_pFGLeg) { + m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pFGLeg->GetMass(), deltaTime); + } + + if (m_pBGLeg) { + m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); + } + } else { + m_Paths[FGROUND][JUMP].Terminate(); + m_Paths[BGROUND][JUMP].Terminate(); + if (m_MoveState == CROUCH) { + m_Paths[FGROUND][WALK].Terminate(); + m_Paths[BGROUND][WALK].Terminate(); + m_Paths[FGROUND][CRAWL].Terminate(); + m_Paths[BGROUND][CRAWL].Terminate(); + + if (m_pFGLeg) { + m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[FGROUND][CROUCH], deltaTime); + } + + if (m_pBGLeg) { + m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[BGROUND][CROUCH], deltaTime); + } + + } else { + m_Paths[FGROUND][WALK].Terminate(); + m_Paths[BGROUND][WALK].Terminate(); + m_Paths[FGROUND][CRAWL].Terminate(); + m_Paths[BGROUND][CRAWL].Terminate(); + m_Paths[FGROUND][ARMCRAWL].Terminate(); + m_Paths[BGROUND][ARMCRAWL].Terminate(); + + if (m_pFGLeg) { + Vector jointPos = m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped); + m_pFGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[FGROUND], m_Paths[FGROUND][STAND], deltaTime, nullptr, !m_pBGLeg, Vector(0.0F, m_Paths[FGROUND][STAND].GetLowestY()), m_WalkPathOffset); + } + + if (m_pBGLeg) { + Vector jointPos = m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped); + m_pBGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[BGROUND], m_Paths[BGROUND][STAND], deltaTime, nullptr, !m_pFGLeg, Vector(0.0F, m_Paths[FGROUND][STAND].GetLowestY()), m_WalkPathOffset); + } } } - } else { - deviceAsFirearm->Deactivate(); - m_TriggerPulled = false; } } else { - m_CanActivateBGItem = false; - if (m_Controller.IsState(WEAPON_FIRE)) { - device->Activate(); - } else { - device->Deactivate(); + // Not stable/standing, so make sure the end of limbs are moving around limply in a ragdoll fashion. + // TODO: Make the limb atom groups fly around and react to terrain, without getting stuck etc. + if (m_pFGArm) { + m_pFGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGArm->GetParentOffset()), m_pFGArm->GetMaxLength(), m_PrevVel * m_pFGArm->GetJointStiffness(), m_AngularVel, m_pFGArm->GetMass(), deltaTime); } - } - // If reloading 2 guns one-at-a-time, the automatic reload when firing empty won't trigger, so this makes sure it happens automatically. - if (device->IsEmpty()) { - ReloadFirearms(true); - } - device->SetSharpAim(m_SharpAimProgress); - - if (device->IsReloading()) { - m_CanActivateBGItem = false; - m_SharpAimTimer.Reset(); - m_SharpAimProgress = 0; - device->SetSharpAim(m_SharpAimProgress); - } - } else { - m_CanActivateBGItem = false; - } - if (m_ArmsState == THROWING_PREP && !thrownDevice) { - m_ArmsState = WEAPON_READY; - } + if (m_pBGArm) { + m_pBGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGArm->GetParentOffset()), m_pBGArm->GetMaxLength(), m_PrevVel * m_pBGArm->GetJointStiffness(), m_AngularVel, m_pBGArm->GetMass(), deltaTime); + } -// m_aSprite->SetAngle((m_AimAngle / 180) * 3.141592654); -// m_aSprite->SetScale(2.0); - - //////////////////////////////////////// - // Item dropping logic - - if (m_Controller.IsState(WEAPON_DROP) && m_Status != INACTIVE) { - Arm *dropperArm = nullptr; - for (Arm *arm : { m_pFGArm, m_pBGArm }) { - if (arm && arm->GetHeldDevice()) { - HeldDevice *heldDevice = arm->GetHeldDevice(); - arm->RemoveAttachable(heldDevice, true, false); - if (dropperArm) { - if (heldDevice) { - dropperArm->SetHeldDevice(heldDevice); - arm->SetHandPos(dropperArm->GetPos()); - } - } else { - heldDevice->SetPos(arm->GetJointPos() + Vector(arm->GetMaxLength() * GetFlipFactor(), 0).RadRotate(adjustedAimAngle)); - Vector tossVec(1.0F + std::sqrt(std::abs(arm->GetThrowStrength()) / std::sqrt(std::abs(heldDevice->GetMass()) + 1.0F)), RandomNormalNum()); - heldDevice->SetVel(heldDevice->GetVel() * 0.5F + tossVec.RadRotate(m_AimAngle).GetXFlipped(m_HFlipped)); - heldDevice->SetAngularVel(heldDevice->GetAngularVel() + m_AngularVel * 0.5F + 3.0F * RandomNormalNum()); + if (m_pFGLeg) { + m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), m_PrevVel * m_pFGLeg->GetJointStiffness(), m_AngularVel, m_pFGLeg->GetMass(), deltaTime); + } - arm->SetHandPos(heldDevice->GetPos()); - } - dropperArm = arm; - } else if (dropperArm && !m_Inventory.empty()) { - dropperArm->SetHeldDevice(dynamic_cast(SwapNextInventory())); - dropperArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); + if (m_pBGLeg) { + m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel * m_pBGLeg->GetJointStiffness(), m_AngularVel, m_pBGLeg->GetMass(), deltaTime); } } - if (!dropperArm && !m_Inventory.empty() && !m_pFGArm) { - DropAllInventory(); - if (m_pBGArm) { - m_pBGArm->SetHandPos(m_Pos + RotateOffset(m_HolsterOffset)); - } + if (m_MoveState != WALK && m_StrideSound && m_StrideSound->GetLoopSetting() < 0) { + m_StrideSound->Stop(); } - EquipShieldInBGArm(); - m_SharpAimProgress = 0; - m_EquipHUDTimer.Reset(); - } - //////////////////////////////////////// - // Item pickup logic + ///////////////////////////////// + // Manage Attachables - float reach = m_SpriteRadius; - Vector reachPoint = m_Pos; - - // Try to detect a new item - if ((m_pFGArm || m_pBGArm) && m_Status == STABLE) { - reach += m_pFGArm ? m_pFGArm->GetMaxLength() : m_pBGArm->GetMaxLength(); - reachPoint = m_pFGArm ? m_pFGArm->GetJointPos() : m_pBGArm->GetJointPos(); - - MOID itemMOID = g_SceneMan.CastMORay(reachPoint, Vector(reach * RandomNum(0.5F, 1.0F) * GetFlipFactor(), 0).RadRotate(m_pItemInReach ? adjustedAimAngle : RandomNum(-(c_HalfPI + c_EighthPI), m_AimAngle * 0.75F + c_EighthPI) * GetFlipFactor()), m_MOID, m_Team, g_MaterialGrass, true, 3); - - if (MovableObject *foundMO = g_MovableMan.GetMOFromID(itemMOID)) { - if (HeldDevice *foundDevice = dynamic_cast(foundMO->GetRootParent())) { - m_pItemInReach = (m_pFGArm || foundDevice->IsOneHanded()) ? foundDevice : nullptr; + if (m_pHead) { + float toRotate = 0; + // Only rotate the head to match the aim angle if body is stable and upright + if (m_Status == STABLE && std::abs(rot) < (c_HalfPI + c_QuarterPI)) { + toRotate = m_pHead->GetRotMatrix().GetRadAngleTo((adjustedAimAngle)*m_LookToAimRatio + rot * (0.9F - m_LookToAimRatio)) * 0.15F; + } else { + // Rotate the head loosely along with the body if upside down, unstable or dying. + toRotate = m_pHead->GetRotMatrix().GetRadAngleTo(rot) * m_pHead->GetJointStiffness() * c_QuarterPI; } + m_pHead->SetRotAngle(m_pHead->GetRotAngle() + toRotate); } - } - - // Item currently set to be within reach has expired or is now out of range - if (m_pItemInReach && (!m_pItemInReach->IsPickupableBy(this) || !g_MovableMan.IsDevice(m_pItemInReach) || g_SceneMan.ShortestDistance(reachPoint, m_pItemInReach->GetPos(), g_SceneMan.SceneWrapsX()).MagnitudeIsGreaterThan(reach + m_pItemInReach->GetRadius()))) { - m_pItemInReach = nullptr; - } - if (m_pItemInReach && (m_pFGArm || m_pBGArm) && m_Controller.IsState(WEAPON_PICKUP) && m_Status != INACTIVE && g_MovableMan.RemoveMO(m_pItemInReach)) { - Arm *armToUse = m_pFGArm ? m_pFGArm : m_pBGArm; - Attachable *pMO = armToUse->RemoveAttachable(armToUse->GetHeldDevice()); - AddToInventoryBack(pMO); - armToUse->SetHandPos(m_pItemInReach->GetJointPos()); - armToUse->SetHeldDevice(m_pItemInReach); - m_pItemInReach = nullptr; + if (m_pFGLeg) { + m_pFGLeg->EnableIdle(m_ProneState == NOTPRONE && m_Status != UNSTABLE); + m_pFGLeg->SetTargetPosition(m_pFGFootGroup->GetLimbPos(m_HFlipped)); + } - if (armToUse != m_pBGArm) { - EquipShieldInBGArm(); + if (m_pBGLeg) { + m_pBGLeg->EnableIdle(m_ProneState == NOTPRONE && m_Status != UNSTABLE); + m_pBGLeg->SetTargetPosition(m_pBGFootGroup->GetLimbPos(m_HFlipped)); } - m_SharpAimProgress = 0; - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - m_EquipHUDTimer.Reset(); - } - - /////////////////////////////////////////////////// - // Travel the limb AtomGroup:s - - m_StrideFrame = false; - - UpdateCrouching(); - - if (m_Status == STABLE && !m_LimbPushForcesAndCollisionsDisabled && m_MoveState != NOMOVE) - { - // This exists to support disabling foot collisions if the limbpath has that flag set. - if ((m_pFGFootGroup->GetAtomCount() == 0 && m_BackupFGFootGroup->GetAtomCount() > 0) != m_Paths[FGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { - m_BackupFGFootGroup->SetLimbPos(m_pFGFootGroup->GetLimbPos()); - std::swap(m_pFGFootGroup, m_BackupFGFootGroup); - } - if ((m_pBGFootGroup->GetAtomCount() == 0 && m_BackupBGFootGroup->GetAtomCount() > 0) != m_Paths[BGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { - m_BackupBGFootGroup->SetLimbPos(m_pBGFootGroup->GetLimbPos()); - std::swap(m_pBGFootGroup, m_BackupBGFootGroup); - } - - if (m_pFGLeg) { UpdateWalkAngle(FGROUND); } - if (m_pBGLeg) { UpdateWalkAngle(BGROUND); } - - // WALKING, OR WE ARE JETPACKING AND STUCK - if (m_MoveState == WALK || (m_MoveState == JUMP && isStill)) { - m_Paths[FGROUND][STAND].Terminate(); - m_Paths[BGROUND][STAND].Terminate(); - -// float FGLegProg = MAX(m_Paths[FGROUND][WALK].GetRegularProgress(), m_Paths[FGROUND][WALK].GetTotalTimeProgress()); -// float BGLegProg = MAX(m_Paths[BGROUND][WALK].GetRegularProgress(), m_Paths[BGROUND][WALK].GetTotalTimeProgress()); - float FGLegProg = m_Paths[FGROUND][WALK].GetRegularProgress(); - float BGLegProg = m_Paths[BGROUND][WALK].GetRegularProgress(); - - bool restarted = false; - - // Make sure we are starting a stride if we're basically stopped. - if (isStill) { m_StrideStart = true; } - - if (m_pFGLeg && (!m_pBGLeg || !(m_Paths[FGROUND][WALK].PathEnded() && BGLegProg < 0.5F) || m_StrideStart)) { - // Reset the stride timer if the path is about to restart. - if (m_Paths[FGROUND][WALK].PathEnded() || m_Paths[FGROUND][WALK].PathIsAtStart()) { m_StrideTimer.Reset(); } - Vector jointPos = m_Pos + RotateOffset(m_pFGLeg->GetParentOffset()); - m_ArmClimbing[BGROUND] = !m_pFGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[FGROUND], m_Paths[FGROUND][WALK], deltaTime, &restarted, false, Vector(0.0F, m_Paths[FGROUND][WALK].GetLowestY()), m_WalkPathOffset); - } else { - m_ArmClimbing[BGROUND] = false; - } - if (m_pBGLeg && (!m_pFGLeg || !(m_Paths[BGROUND][WALK].PathEnded() && FGLegProg < 0.5F))) { - m_StrideStart = false; - // Reset the stride timer if the path is about to restart. - if (m_Paths[BGROUND][WALK].PathEnded() || m_Paths[BGROUND][WALK].PathIsAtStart()) { m_StrideTimer.Reset(); } - Vector jointPos = m_Pos + RotateOffset(m_pBGLeg->GetParentOffset()); - m_ArmClimbing[FGROUND] = !m_pBGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[BGROUND], m_Paths[BGROUND][WALK], deltaTime, &restarted, false, Vector(0.0F, m_Paths[BGROUND][WALK].GetLowestY()), m_WalkPathOffset); - } else { - if (m_pBGLeg) { m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); } - m_ArmClimbing[FGROUND] = false; - } - bool climbing = m_ArmClimbing[FGROUND] || m_ArmClimbing[BGROUND]; + // FG Arm rotating and climbing + if (m_pFGArm) { + float affectingBodyAngle = m_Status < INACTIVE ? m_FGArmFlailScalar : 1.0F; + if (affectingBodyAngle != 0 && m_SharpAimDelay != 0) { + float aimScalar = std::min(static_cast(m_SharpAimTimer.GetElapsedSimTimeMS()) / static_cast(m_SharpAimDelay), 1.0F); + float revertScalar = std::min(static_cast(m_SharpAimRevertTimer.GetElapsedSimTimeMS()) / static_cast(m_SharpAimDelay), 1.0F); + aimScalar = (aimScalar > revertScalar) ? aimScalar : 1.0F - revertScalar; - if (m_StrideSound) { - m_StrideSound->SetPosition(m_Pos); - if (m_StrideSound->GetLoopSetting() < 0) { - if (!m_StrideSound->IsBeingPlayed()) { m_StrideSound->Play(); } - } else if (restarted && !climbing) { - m_StrideSound->Play(); - } - } - if (restarted) { - if (climbing) { - m_WalkAngle[FGROUND] = Matrix(); - m_WalkAngle[BGROUND] = Matrix(); - } else { - m_StrideFrame = true; - RunScriptedFunctionInAppropriateScripts("OnStride"); - } + affectingBodyAngle *= std::abs(std::sin(rot)) * rot * (1.0F - aimScalar); } + m_pFGArm->SetRotAngle(affectingBodyAngle + adjustedAimAngle); - //////////////////////////////////////// - // Arm Climbing if the leg paths failed to find clear spot to restart - -// float FGArmProg = MAX(m_Paths[FGROUND][CLIMB].GetRegularProgress(), m_Paths[FGROUND][CLIMB].GetTotalTimeProgress()); -// float BGArmProg = MAX(m_Paths[BGROUND][CLIMB].GetRegularProgress(), m_Paths[BGROUND][CLIMB].GetTotalTimeProgress()); - float FGArmProg = m_Paths[FGROUND][CLIMB].GetRegularProgress(); - float BGArmProg = m_Paths[BGROUND][CLIMB].GetRegularProgress(); - - // TODO: Figure out what this comment means, and then rephrase it better! - // Slightly negative BGArmProg makes sense because any progress on the starting segments are reported as negative, - // and there's many starting segments on properly formed climbing paths - if (climbing) { - if (m_pFGArm && !m_pFGArm->GetHeldDevice() && !(m_Paths[FGROUND][CLIMB].PathEnded() && BGArmProg > 0.1F)) { // < 0.5F - m_ArmClimbing[FGROUND] = true; - m_Paths[FGROUND][WALK].Terminate(); - m_StrideStart = true; - // Reset the stride timer if the path is about to restart. - if (m_Paths[FGROUND][CLIMB].PathEnded() || m_Paths[FGROUND][CLIMB].PathIsAtStart()) { m_StrideTimer.Reset(); } - m_pFGHandGroup->PushAsLimb(m_Pos + Vector(0, m_pFGArm->GetParentOffset().m_Y).RadRotate(-rot), m_Vel, Matrix(), m_Paths[FGROUND][CLIMB], deltaTime, 0, false); - } else { - m_ArmClimbing[FGROUND] = false; - m_Paths[FGROUND][CLIMB].Terminate(); - } - if (m_pBGArm) { - m_ArmClimbing[BGROUND] = true; - m_Paths[BGROUND][WALK].Terminate(); - m_StrideStart = true; - // Reset the stride timer if the path is about to restart. - if (m_Paths[BGROUND][CLIMB].PathEnded() || m_Paths[BGROUND][CLIMB].PathIsAtStart()) { m_StrideTimer.Reset(); } - m_pBGHandGroup->PushAsLimb(m_Pos + Vector(0, m_pBGArm->GetParentOffset().m_Y).RadRotate(-rot), m_Vel, Matrix(), m_Paths[BGROUND][CLIMB], deltaTime, 0, false); - } else { - m_ArmClimbing[BGROUND] = false; - m_Paths[BGROUND][CLIMB].Terminate(); + if (m_Status == STABLE) { + if (m_ArmClimbing[FGROUND]) { + m_pFGArm->AddHandTarget("Hand AtomGroup Limb Pos", m_pFGHandGroup->GetLimbPos(m_HFlipped)); } + } else if (!m_pFGArm->GetHeldDevice()) { + m_pFGArm->ClearHandTargets(); + m_pFGArm->AddHandTarget("Arm Flail", m_pFGHandGroup->GetLimbPos(m_HFlipped)); } + } - // Restart the climbing stroke if the current one seems to be taking too long with no movement. - if (climbing && isStill && m_StrideTimer.IsPastSimMS(static_cast(m_Paths[BGROUND][CLIMB].GetTotalPathTime() * 0.5F))) { - m_StrideStart = true; - m_Paths[FGROUND][CLIMB].Terminate(); - m_Paths[BGROUND][CLIMB].Terminate(); - } else if (m_StrideTimer.IsPastSimMS(static_cast(m_Paths[FGROUND][WALK].GetTotalPathTime() * 1.1F))) { - // Reset the walking stride if it's taking longer than it should. - m_StrideStart = true; - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - } - } else if (m_MoveState == CRAWL) { - // Start crawling only once we are fully prone. - if (m_ProneState == PRONE) { - - float FGLegProg = m_Paths[FGROUND][CRAWL].GetRegularProgress(); - float BGLegProg = m_Paths[BGROUND][CRAWL].GetRegularProgress(); - - if (m_pFGLeg && (!m_pBGLeg || (!(m_Paths[FGROUND][CRAWL].PathEnded() && BGLegProg < 0.5F) || m_StrideStart))) { - if (m_Paths[FGROUND][CRAWL].PathEnded() || m_Paths[FGROUND][CRAWL].PathIsAtStart()) { m_StrideTimer.Reset(); } - m_pFGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pFGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[FGROUND][CRAWL], deltaTime); - } else { - m_Paths[FGROUND][CRAWL].Terminate(); - } - if (m_pBGLeg && (!m_pFGLeg || !(m_Paths[BGROUND][CRAWL].PathEnded() && FGLegProg < 0.5F))) { - m_StrideStart = false; - if (m_Paths[BGROUND][CRAWL].PathEnded() || m_Paths[BGROUND][CRAWL].PathIsAtStart()) { m_StrideTimer.Reset(); } - m_pBGFootGroup->PushAsLimb(m_Pos + RotateOffset(m_pBGLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[BGROUND][CRAWL], deltaTime); + // BG Arm rotating, climbing, throw animations, supporting fg weapon + if (m_pBGArm) { + float affectingBodyAngle = m_Status < INACTIVE ? m_BGArmFlailScalar : 1.0F; + m_pBGArm->SetRotAngle(std::abs(std::sin(rot)) * rot * affectingBodyAngle + adjustedAimAngle); + + if (m_Status == STABLE) { + if (m_ArmClimbing[BGROUND]) { + // Can't climb or crawl with the shield + if (m_MoveState != CRAWL || m_ProneState == PRONE) { + UnequipBGArm(); + } + m_pBGArm->AddHandTarget("Hand AtomGroup Limb Pos", m_pBGHandGroup->GetLimbPos(m_HFlipped)); } else { - m_Paths[BGROUND][CRAWL].Terminate(); - } - if (m_pBGArm) { - m_ArmClimbing[BGROUND] = true; - m_pBGHandGroup->PushAsLimb(m_Pos + RotateOffset(Vector(0, m_pBGArm->GetParentOffset().m_Y)), m_Vel, m_Rotation, m_Paths[BGROUND][ARMCRAWL], deltaTime); - } - if (m_pFGArm && !m_pFGArm->GetHeldDevice() && !(m_Paths[FGROUND][ARMCRAWL].PathEnded() && m_Paths[BGROUND][ARMCRAWL].GetRegularProgress() < 0.5F)) { - m_ArmClimbing[FGROUND] = true; - m_pFGHandGroup->PushAsLimb(m_Pos + RotateOffset(Vector(0, m_pFGArm->GetParentOffset().m_Y)), m_Vel, m_Rotation, m_Paths[FGROUND][ARMCRAWL], deltaTime); - } - // Restart the stride if the current one seems to be taking too long. - if (m_StrideTimer.IsPastSimMS(m_Paths[FGROUND][CRAWL].GetTotalPathTime())) { - m_StrideStart = true; - m_Paths[FGROUND][CRAWL].Terminate(); - m_Paths[BGROUND][CRAWL].Terminate(); + HeldDevice* heldDevice = GetEquippedItem(); + ThrownDevice* thrownDevice = dynamic_cast(heldDevice); + if (thrownDevice && (m_ArmsState == THROWING_PREP || isSharpAiming)) { + m_pBGArm->AddHandTarget("End Throw Offset", m_pBGArm->GetJointPos() + thrownDevice->GetEndThrowOffset().GetXFlipped(m_HFlipped).RadRotate(adjustedAimAngle)); + } else if (heldDevice) { + if (HeldDevice* bgDevice = GetEquippedBGItem(); bgDevice && !heldDevice->IsOneHanded()) { + UnequipBGArm(); + } else if (!bgDevice && !heldDevice->IsReloading() && heldDevice->IsSupportable()) { + m_pBGArm->SetHeldDeviceThisArmIsTryingToSupport(heldDevice); + + if (!m_pBGArm->HasAnyHandTargets() && m_pBGArm->GetHandHasReachedCurrentTarget()) { + heldDevice->SetSupported(true); + m_pBGArm->SetRecoil(heldDevice->GetRecoilForce(), heldDevice->GetRecoilOffset(), heldDevice->IsRecoiled()); + } else { + // BGArm did not reach to support the device. Count device as supported anyway, if crouching or prone. + heldDevice->SetSupported(m_MoveState == CROUCH || m_ProneState == PRONE); + m_pBGArm->SetRecoil(Vector(), Vector(), false); + } + } + } } } else { - if (m_pFGLeg) { m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pFGLeg->GetMass(), deltaTime); } - - if (m_pBGLeg) { m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); } + m_pBGArm->ClearHandTargets(); + m_pBGArm->AddHandTarget("Arm Flail", m_pBGHandGroup->GetLimbPos(m_HFlipped)); } - } else if (m_pFGLeg || m_pBGLeg) { - if (m_MoveState == JUMP) { - // TODO: Utilize jump paths in an intuitive way! - if (m_pFGLeg) { m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pFGLeg->GetMass(), deltaTime); } - - if (m_pBGLeg) { m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); } - } else { - m_Paths[FGROUND][JUMP].Terminate(); - m_Paths[BGROUND][JUMP].Terminate(); - if (m_MoveState == CROUCH) { - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - m_Paths[FGROUND][CRAWL].Terminate(); - m_Paths[BGROUND][CRAWL].Terminate(); - - if (m_pFGLeg) { m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[FGROUND][CROUCH], deltaTime); } - - if (m_pBGLeg) { m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[BGROUND][CROUCH], deltaTime); } + } else if (HeldDevice* heldDevice = GetEquippedItem()) { + heldDevice->SetSupported(false); + } + // Make sure the bg arm doesn't think it's supporting something when it isn't. + if (m_pBGArm && (!m_pFGArm || !m_pFGArm->GetHeldDevice() || m_pBGArm->GetHeldDevice())) { + m_pBGArm->SetHeldDeviceThisArmIsTryingToSupport(nullptr); + } - } else { - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - m_Paths[FGROUND][CRAWL].Terminate(); - m_Paths[BGROUND][CRAWL].Terminate(); - m_Paths[FGROUND][ARMCRAWL].Terminate(); - m_Paths[BGROUND][ARMCRAWL].Terminate(); + ///////////////////////////////// + // Arm swinging or device swaying walking animations - if (m_pFGLeg) { - Vector jointPos = m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped); - m_pFGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[FGROUND], m_Paths[FGROUND][STAND], deltaTime, nullptr, !m_pBGLeg, Vector(0.0F, m_Paths[FGROUND][STAND].GetLowestY()), m_WalkPathOffset); + if (m_MoveState != MovementState::STAND && (m_ArmSwingRate != 0 || m_DeviceArmSwayRate != 0)) { + for (Arm* arm: {m_pFGArm, m_pBGArm}) { + if (arm && !arm->GetHeldDeviceThisArmIsTryingToSupport()) { + Leg* legToSwingWith = arm == m_pFGArm ? m_pBGLeg : m_pFGLeg; + Leg* otherLeg = legToSwingWith == m_pBGLeg ? m_pFGLeg : m_pBGLeg; + if (!legToSwingWith || m_MoveState == JUMP || m_MoveState == CROUCH) { + std::swap(legToSwingWith, otherLeg); } - if (m_pBGLeg) { - Vector jointPos = m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped); - m_pBGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[BGROUND], m_Paths[BGROUND][STAND], deltaTime, nullptr, !m_pFGLeg, Vector(0.0F, m_Paths[FGROUND][STAND].GetLowestY()), m_WalkPathOffset); + if (legToSwingWith) { + float armMovementRateToUse = m_ArmSwingRate; + if (HeldDevice* heldDevice = arm->GetHeldDevice()) { + armMovementRateToUse = m_DeviceArmSwayRate * (1.0F - m_SharpAimProgress) * std::sin(std::abs(heldDevice->GetStanceOffset().GetAbsRadAngle())); + } + float angleToSwingTo = std::sin(legToSwingWith->GetRotAngle() + (c_HalfPI * GetFlipFactor())); + arm->SetHandIdleRotation(angleToSwingTo * armMovementRateToUse); } } } } - } else { - // Not stable/standing, so make sure the end of limbs are moving around limply in a ragdoll fashion. - // TODO: Make the limb atom groups fly around and react to terrain, without getting stuck etc. - if (m_pFGArm) { m_pFGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGArm->GetParentOffset()), m_pFGArm->GetMaxLength(), m_PrevVel * m_pFGArm->GetJointStiffness(), m_AngularVel, m_pFGArm->GetMass(), deltaTime); } + } - if (m_pBGArm) { m_pBGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGArm->GetParentOffset()), m_pBGArm->GetMaxLength(), m_PrevVel * m_pBGArm->GetJointStiffness(), m_AngularVel, m_pBGArm->GetMass(), deltaTime); } + ////////////////////////////////////////////////////////////////////////////////////////// - if (m_pFGLeg) { m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), m_PrevVel * m_pFGLeg->GetJointStiffness(), m_AngularVel, m_pFGLeg->GetMass(), deltaTime); } + void AHuman::Update() { + ZoneScoped; - if (m_pBGLeg) { m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel * m_pBGLeg->GetJointStiffness(), m_AngularVel, m_pBGLeg->GetMass(), deltaTime); } - } - if (m_MoveState != WALK && m_StrideSound && m_StrideSound->GetLoopSetting() < 0) { - m_StrideSound->Stop(); - } - - ///////////////////////////////// - // Manage Attachables - - if (m_pHead) { - float toRotate = 0; - // Only rotate the head to match the aim angle if body is stable and upright - if (m_Status == STABLE && std::abs(rot) < (c_HalfPI + c_QuarterPI)) { - toRotate = m_pHead->GetRotMatrix().GetRadAngleTo((adjustedAimAngle) * m_LookToAimRatio + rot * (0.9F - m_LookToAimRatio)) * 0.15F; - } else { - // Rotate the head loosely along with the body if upside down, unstable or dying. - toRotate = m_pHead->GetRotMatrix().GetRadAngleTo(rot) * m_pHead->GetJointStiffness() * c_QuarterPI; - } - m_pHead->SetRotAngle(m_pHead->GetRotAngle() + toRotate); - } + float rot = m_Rotation.GetRadAngle(); // eugh, for backwards compat to be the same behaviour as with multithreaded AI - if (m_pFGLeg) { - m_pFGLeg->EnableIdle(m_ProneState == NOTPRONE && m_Status != UNSTABLE); - m_pFGLeg->SetTargetPosition(m_pFGFootGroup->GetLimbPos(m_HFlipped)); - } - - if (m_pBGLeg) { - m_pBGLeg->EnableIdle(m_ProneState == NOTPRONE && m_Status != UNSTABLE); - m_pBGLeg->SetTargetPosition(m_pBGFootGroup->GetLimbPos(m_HFlipped)); - } - - // FG Arm rotating and climbing - if (m_pFGArm) { - float affectingBodyAngle = m_Status < INACTIVE ? m_FGArmFlailScalar : 1.0F; - if (affectingBodyAngle != 0 && m_SharpAimDelay != 0) { - float aimScalar = std::min(static_cast(m_SharpAimTimer.GetElapsedSimTimeMS()) / static_cast(m_SharpAimDelay), 1.0F); - float revertScalar = std::min(static_cast(m_SharpAimRevertTimer.GetElapsedSimTimeMS()) / static_cast(m_SharpAimDelay), 1.0F); - aimScalar = (aimScalar > revertScalar) ? aimScalar : 1.0F - revertScalar; - - affectingBodyAngle *= std::abs(std::sin(rot)) * rot * (1.0F - aimScalar); - } - m_pFGArm->SetRotAngle(affectingBodyAngle + adjustedAimAngle); - - if (m_Status == STABLE) { - if (m_ArmClimbing[FGROUND]) { - m_pFGArm->AddHandTarget("Hand AtomGroup Limb Pos", m_pFGHandGroup->GetLimbPos(m_HFlipped)); - } - } else if (!m_pFGArm->GetHeldDevice()) { - m_pFGArm->ClearHandTargets(); - m_pFGArm->AddHandTarget("Arm Flail", m_pFGHandGroup->GetLimbPos(m_HFlipped)); - } - } - - // BG Arm rotating, climbing, throw animations, supporting fg weapon - if (m_pBGArm) { - float affectingBodyAngle = m_Status < INACTIVE ? m_BGArmFlailScalar : 1.0F; - m_pBGArm->SetRotAngle(std::abs(std::sin(rot)) * rot * affectingBodyAngle + adjustedAimAngle); - - if (m_Status == STABLE) { - if (m_ArmClimbing[BGROUND]) { - // Can't climb or crawl with the shield - if (m_MoveState != CRAWL || m_ProneState == PRONE) { - UnequipBGArm(); - } - m_pBGArm->AddHandTarget("Hand AtomGroup Limb Pos", m_pBGHandGroup->GetLimbPos(m_HFlipped)); - } else { - HeldDevice *heldDevice = GetEquippedItem(); - ThrownDevice *thrownDevice = dynamic_cast(heldDevice); - if (thrownDevice && (m_ArmsState == THROWING_PREP || isSharpAiming)) { - m_pBGArm->AddHandTarget("End Throw Offset", m_pBGArm->GetJointPos() + thrownDevice->GetEndThrowOffset().GetXFlipped(m_HFlipped).RadRotate(adjustedAimAngle)); - } else if (heldDevice) { - if (HeldDevice *bgDevice = GetEquippedBGItem(); bgDevice && !heldDevice->IsOneHanded()) { - UnequipBGArm(); - } else if (!bgDevice && !heldDevice->IsReloading() && heldDevice->IsSupportable()) { - m_pBGArm->SetHeldDeviceThisArmIsTryingToSupport(heldDevice); + Actor::Update(); - if (!m_pBGArm->HasAnyHandTargets() && m_pBGArm->GetHandHasReachedCurrentTarget()) { - heldDevice->SetSupported(true); - m_pBGArm->SetRecoil(heldDevice->GetRecoilForce(), heldDevice->GetRecoilOffset(), heldDevice->IsRecoiled()); - } else { - // BGArm did not reach to support the device. Count device as supported anyway, if crouching or prone. - heldDevice->SetSupported(m_MoveState == CROUCH || m_ProneState == PRONE); - m_pBGArm->SetRecoil(Vector(), Vector(), false); + //////////////////////////////////// + // Update viewpoint + + // Set viewpoint based on how we are aiming etc. + Vector aimSight(m_AimDistance, 0); + Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); + aimMatrix.SetXFlipped(m_HFlipped); + // Reset this each frame + m_SharpAimMaxedOut = false; + + if (HeldDevice* heldDevice = GetEquippedItem()) { + float maxLength = heldDevice->GetSharpLength(); + if (maxLength == 0) { + m_SharpAimProgress = 0; + m_SharpAimMaxedOut = true; + } else if (m_MoveState == WALK) { + maxLength *= 0.7F; + } + // Use a non-terrain check ray to cap the magnitude, so we can't see into objects etc + if (m_SharpAimProgress > 0) { + // TODO: make an uniform function to get the total GripStrength of an AHuman? + float totalGripStrength = m_pFGArm->GetGripStrength(); + if (m_pBGArm) { + if (m_pBGArm->GetHeldDevice()) { + HeldDevice* heldBGDevice = m_pBGArm->GetHeldDevice(); + if (heldBGDevice->IsRecoiled()) { + m_SharpAimProgress *= 1.0F - std::min(heldBGDevice->GetRecoilForce().GetMagnitude() / std::max(m_pBGArm->GetGripStrength() * heldBGDevice->GetGripStrengthMultiplier(), 1.0F), 1.0F); } + } else if (heldDevice->GetSupported()) { + totalGripStrength += m_pBGArm->GetGripStrength(); } } - } - } else { - m_pBGArm->ClearHandTargets(); - m_pBGArm->AddHandTarget("Arm Flail", m_pBGHandGroup->GetLimbPos(m_HFlipped)); - } - } else if (HeldDevice *heldDevice = GetEquippedItem()) { - heldDevice->SetSupported(false); - } - // Make sure the bg arm doesn't think it's supporting something when it isn't. - if (m_pBGArm && (!m_pFGArm || !m_pFGArm->GetHeldDevice() || m_pBGArm->GetHeldDevice())) { - m_pBGArm->SetHeldDeviceThisArmIsTryingToSupport(nullptr); - } - - ///////////////////////////////// - // Arm swinging or device swaying walking animations - - if (m_MoveState != MovementState::STAND && (m_ArmSwingRate != 0 || m_DeviceArmSwayRate != 0)) { - for (Arm *arm : { m_pFGArm, m_pBGArm }) { - if (arm && !arm->GetHeldDeviceThisArmIsTryingToSupport()) { - Leg *legToSwingWith = arm == m_pFGArm ? m_pBGLeg : m_pFGLeg; - Leg *otherLeg = legToSwingWith == m_pBGLeg ? m_pFGLeg : m_pBGLeg; - if (!legToSwingWith || m_MoveState == JUMP || m_MoveState == CROUCH) { - std::swap(legToSwingWith, otherLeg); + if (heldDevice->IsRecoiled()) { + m_SharpAimProgress *= 1.0F - std::min(heldDevice->GetRecoilForce().GetMagnitude() / std::max(totalGripStrength * heldDevice->GetGripStrengthMultiplier(), 1.0F), 1.0F); } - - if (legToSwingWith) { - float armMovementRateToUse = m_ArmSwingRate; - if (HeldDevice *heldDevice = arm->GetHeldDevice()) { - armMovementRateToUse = m_DeviceArmSwayRate * (1.0F - m_SharpAimProgress) * std::sin(std::abs(heldDevice->GetStanceOffset().GetAbsRadAngle())); - } - float angleToSwingTo = std::sin(legToSwingWith->GetRotAngle() + (c_HalfPI * GetFlipFactor())); - arm->SetHandIdleRotation(angleToSwingTo * armMovementRateToUse); + Vector notUsed; + Vector sharpAimVector(maxLength, 0); + sharpAimVector *= aimMatrix; + + // See how far along the sharp aim vector there is opaque air + float result = g_SceneMan.CastObstacleRay(heldDevice->GetMuzzlePos(), sharpAimVector, notUsed, notUsed, GetRootID(), IgnoresWhichTeam(), g_MaterialAir, 5); + // If we didn't find anything but air before the sharpdistance, then don't alter the sharp distance + if (result >= 0 && result < (maxLength * m_SharpAimProgress)) { + m_SharpAimProgress = result / maxLength; + m_SharpAimMaxedOut = true; } } + if (m_SharpAimProgress > 0.9F) { + m_SharpAimMaxedOut = true; + } + aimSight.m_X += maxLength * m_SharpAimProgress; } - } -} -////////////////////////////////////////////////////////////////////////////////////////// + // Rotate the aiming spot vector and add it to the view point + aimSight *= aimMatrix; + m_ViewPoint = m_Pos.GetFloored() + aimSight; -void AHuman::Update() -{ - ZoneScoped; + // Add velocity also so the viewpoint moves ahead at high speeds + if (m_Vel.MagnitudeIsGreaterThan(10.0F)) { + m_ViewPoint += m_Vel * std::sqrt(m_Vel.GetMagnitude() * 0.1F); + } - float rot = m_Rotation.GetRadAngle(); // eugh, for backwards compat to be the same behaviour as with multithreaded AI + //////////////////////////////////////// + // Balance stuff - Actor::Update(); + // Eliminate full rotations + while (std::abs(rot) > c_TwoPI) { + rot -= rot > 0 ? c_TwoPI : -c_TwoPI; + } + // Eliminate rotations over half a turn + if (std::abs(rot) > c_PI) { + rot = (rot > 0 ? -c_PI : c_PI) + (rot - (rot > 0 ? c_PI : -c_PI)); + // If we're upside down, we're unstable damnit + if (m_Status == STABLE) { + m_Status = UNSTABLE; + } + m_StableRecoverTimer.Reset(); + } - //////////////////////////////////// - // Update viewpoint + // Rotational balancing spring calc + if (m_Status == STABLE) { - // Set viewpoint based on how we are aiming etc. - Vector aimSight(m_AimDistance, 0); - Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); - aimMatrix.SetXFlipped(m_HFlipped); - // Reset this each frame - m_SharpAimMaxedOut = false; + // If we're supposed to be laying down on the ground, make the spring pull the body that way until we reach that angle + if (m_ProneState != NOTPRONE) { + float rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; + float rotDiff = rotTarget - rot; - if (HeldDevice *heldDevice = GetEquippedItem()) { - float maxLength = heldDevice->GetSharpLength(); - if (maxLength == 0) { - m_SharpAimProgress = 0; - m_SharpAimMaxedOut = true; - } else if (m_MoveState == WALK) { - maxLength *= 0.7F; - } - // Use a non-terrain check ray to cap the magnitude, so we can't see into objects etc - if (m_SharpAimProgress > 0) { - // TODO: make an uniform function to get the total GripStrength of an AHuman? - float totalGripStrength = m_pFGArm->GetGripStrength(); - if (m_pBGArm) { - if (m_pBGArm->GetHeldDevice()) { - HeldDevice *heldBGDevice = m_pBGArm->GetHeldDevice(); - if (heldBGDevice->IsRecoiled()) { - m_SharpAimProgress *= 1.0F - std::min(heldBGDevice->GetRecoilForce().GetMagnitude() / std::max(m_pBGArm->GetGripStrength() * heldBGDevice->GetGripStrengthMultiplier(), 1.0F), 1.0F); + if (m_ProneState == GOPRONE) { + if (!m_ProneTimer.IsPastSimMS(333)) { + if (std::abs(rotDiff) > 0.1F && std::abs(rotDiff) < c_PI) { + m_AngularVel += rotDiff * 0.4F; + m_Vel.m_X += (m_HFlipped ? -std::abs(rotDiff) : std::abs(rotDiff)) / std::max(m_Vel.GetMagnitude(), 4.0F); + } + } else { + // Done going down, now stay down without spring. + m_AngularVel *= 0.5F; + m_ProneState = PRONE; } - } else if (heldDevice->GetSupported()) { - totalGripStrength += m_pBGArm->GetGripStrength(); - } - } - if (heldDevice->IsRecoiled()) { - m_SharpAimProgress *= 1.0F - std::min(heldDevice->GetRecoilForce().GetMagnitude() / std::max(totalGripStrength * heldDevice->GetGripStrengthMultiplier(), 1.0F), 1.0F); - } - Vector notUsed; - Vector sharpAimVector(maxLength, 0); - sharpAimVector *= aimMatrix; - - // See how far along the sharp aim vector there is opaque air - float result = g_SceneMan.CastObstacleRay(heldDevice->GetMuzzlePos(), sharpAimVector, notUsed, notUsed, GetRootID(), IgnoresWhichTeam(), g_MaterialAir, 5); - // If we didn't find anything but air before the sharpdistance, then don't alter the sharp distance - if (result >= 0 && result < (maxLength * m_SharpAimProgress)) - { - m_SharpAimProgress = result / maxLength; - m_SharpAimMaxedOut = true; - } - } - if (m_SharpAimProgress > 0.9F) { m_SharpAimMaxedOut = true; } - aimSight.m_X += maxLength * m_SharpAimProgress; - } - - // Rotate the aiming spot vector and add it to the view point - aimSight *= aimMatrix; - m_ViewPoint = m_Pos.GetFloored() + aimSight; - - // Add velocity also so the viewpoint moves ahead at high speeds - if (m_Vel.MagnitudeIsGreaterThan(10.0F)) { m_ViewPoint += m_Vel * std::sqrt(m_Vel.GetMagnitude() * 0.1F); } - - //////////////////////////////////////// - // Balance stuff - - // Eliminate full rotations - while (std::abs(rot) > c_TwoPI) { - rot -= rot > 0 ? c_TwoPI : -c_TwoPI; - } - // Eliminate rotations over half a turn - if (std::abs(rot) > c_PI) { - rot = (rot > 0 ? -c_PI : c_PI) + (rot - (rot > 0 ? c_PI : -c_PI)); - // If we're upside down, we're unstable damnit - if (m_Status == STABLE) { m_Status = UNSTABLE; } - m_StableRecoverTimer.Reset(); - } - - // Rotational balancing spring calc - if (m_Status == STABLE) { - - // If we're supposed to be laying down on the ground, make the spring pull the body that way until we reach that angle - if (m_ProneState != NOTPRONE) - { - float rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; - float rotDiff = rotTarget - rot; - - if (m_ProneState == GOPRONE) { - if (!m_ProneTimer.IsPastSimMS(333)) { - if (std::abs(rotDiff) > 0.1F && std::abs(rotDiff) < c_PI) { - m_AngularVel += rotDiff * 0.4F; - m_Vel.m_X += (m_HFlipped ? -std::abs(rotDiff) : std::abs(rotDiff)) / std::max(m_Vel.GetMagnitude(), 4.0F); + } else if (m_ProneState == PRONE) { + // If down, try to keep flat against the ground. + if (std::abs(rotDiff) > c_SixteenthPI && std::abs(rotDiff) < c_HalfPI) { + m_AngularVel += rotDiff * 0.65F; + } else if (std::abs(m_AngularVel) > 0.3F) { + m_AngularVel *= 0.85F; } - } else { - // Done going down, now stay down without spring. - m_AngularVel *= 0.5F; - m_ProneState = PRONE; - } - } else if (m_ProneState == PRONE) { - // If down, try to keep flat against the ground. - if (std::abs(rotDiff) > c_SixteenthPI && std::abs(rotDiff) < c_HalfPI) { - m_AngularVel += rotDiff * 0.65F; - } else if (std::abs(m_AngularVel) > 0.3F) { - m_AngularVel *= 0.85F; } + } else { + // Upright body posture + float rotTarget = (GetRotAngleTarget(m_MoveState) * (m_AimAngle > 0 ? 1.0F - (m_AimAngle / c_HalfPI) : 1.0F) * GetFlipFactor()); + + // Lean forwards when crouching + float crouchAngleAdjust = m_HFlipped ? m_MaxCrouchRotation : -m_MaxCrouchRotation; + rotTarget += LERP(0.0F, m_MaxWalkPathCrouchShift, 0.0F, crouchAngleAdjust, m_WalkPathOffset.m_Y * -1.0F); + + float rotDiff = rot - rotTarget; + m_AngularVel = m_AngularVel * (0.98F - 0.06F * (m_Health / m_MaxHealth)) - (rotDiff * 0.5F); + } + } else if (m_Status == UNSTABLE) { + float rotTarget = 0; + // If traveling at speed, always start falling forward. + if (std::abs(m_Vel.m_X) > 1.0F) { + rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; + } else { + // Otherwise, go whichever way we're already rotated. + rotTarget = rot > 0 ? c_HalfPI : -c_HalfPI; + } + + float rotDiff = rotTarget - rot; + if (std::abs(rotDiff) > 0.1F && std::abs(rotDiff) < c_PI) { + m_AngularVel += rotDiff * 0.05F; + } + } else if (m_Status == DYING) { + float rotTarget = m_Vel.m_X - (rot + m_AngularVel) > 0 ? -c_HalfPI : c_HalfPI; + float rotDiff = rotTarget - rot; + if (!m_DeathTmr.IsPastSimMS(125) && std::abs(rotDiff) > 0.1F && std::abs(rotDiff) < c_PI) { + // TODO: finetune this for situations like low gravity! + float velScalar = 0.5F; //* (g_SceneMan.GetGlobalAcc().GetY() * m_GlobalAccScalar) / c_PPM; + m_AngularVel += rotDiff * velScalar; + m_Vel.m_X += (rotTarget > 0 ? -std::abs(rotDiff) : std::abs(rotDiff)) * velScalar * 0.5F; + } else { + m_Status = DEAD; } - } else { - // Upright body posture - float rotTarget = (GetRotAngleTarget(m_MoveState) * (m_AimAngle > 0 ? 1.0F - (m_AimAngle / c_HalfPI) : 1.0F) * GetFlipFactor()); - - // Lean forwards when crouching - float crouchAngleAdjust = m_HFlipped ? m_MaxCrouchRotation : -m_MaxCrouchRotation; - rotTarget += LERP(0.0F, m_MaxWalkPathCrouchShift, 0.0F, crouchAngleAdjust, m_WalkPathOffset.m_Y * -1.0F); - - float rotDiff = rot - rotTarget; - m_AngularVel = m_AngularVel * (0.98F - 0.06F * (m_Health / m_MaxHealth)) - (rotDiff * 0.5F); - } - } - else if (m_Status == UNSTABLE) { - float rotTarget = 0; - // If traveling at speed, always start falling forward. - if (std::abs(m_Vel.m_X) > 1.0F) { - rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; - } else { - // Otherwise, go whichever way we're already rotated. - rotTarget = rot > 0 ? c_HalfPI : -c_HalfPI; - } - - float rotDiff = rotTarget - rot; - if (std::abs(rotDiff) > 0.1F && std::abs(rotDiff) < c_PI) { - m_AngularVel += rotDiff * 0.05F; - } - } else if (m_Status == DYING) { - float rotTarget = m_Vel.m_X - (rot + m_AngularVel) > 0 ? -c_HalfPI : c_HalfPI; - float rotDiff = rotTarget - rot; - if (!m_DeathTmr.IsPastSimMS(125) && std::abs(rotDiff) > 0.1F && std::abs(rotDiff) < c_PI) { - // TODO: finetune this for situations like low gravity! - float velScalar = 0.5F; //* (g_SceneMan.GetGlobalAcc().GetY() * m_GlobalAccScalar) / c_PPM; - m_AngularVel += rotDiff * velScalar; - m_Vel.m_X += (rotTarget > 0 ? -std::abs(rotDiff) : std::abs(rotDiff)) * velScalar * 0.5F; - } else { - m_Status = DEAD; } - } - m_Rotation.SetRadAngle(rot); + m_Rotation.SetRadAngle(rot); - /////////////////////////////////////////////////// - // Death detection and handling + /////////////////////////////////////////////////// + // Death detection and handling - if (!m_pHead && m_Status != DYING && m_Status != DEAD) { - m_Health -= m_MaxHealth + 1.0F; - } else if (!m_pFGArm && !m_pBGArm && !m_pFGLeg && !m_pBGLeg && m_Status != DYING && m_Status != DEAD) { - m_Health -= 0.1F; - } + if (!m_pHead && m_Status != DYING && m_Status != DEAD) { + m_Health -= m_MaxHealth + 1.0F; + } else if (!m_pFGArm && !m_pBGArm && !m_pFGLeg && !m_pBGLeg && m_Status != DYING && m_Status != DEAD) { + m_Health -= 0.1F; + } - if (m_Status == DYING) { - if (m_pFGArm) { m_pFGArm->RemoveAttachable(m_pFGArm->GetHeldDevice(), true, false); } - if (m_pBGArm) { m_pBGArm->RemoveAttachable(m_pBGArm->GetHeldDevice(), true, false); } - } + if (m_Status == DYING) { + if (m_pFGArm) { + m_pFGArm->RemoveAttachable(m_pFGArm->GetHeldDevice(), true, false); + } + if (m_pBGArm) { + m_pBGArm->RemoveAttachable(m_pBGArm->GetHeldDevice(), true, false); + } + } - ///////////////////////////////////////// - // Misc. + ///////////////////////////////////////// + // Misc. -// m_DeepCheck = true/*m_Status == DEAD*/; -} + // m_DeepCheck = true/*m_Status == DEAD*/; + } + ////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// + void AHuman::DrawThrowingReticle(BITMAP* targetBitmap, const Vector& targetPos, float progressScalar) const { + const int pointCount = 9; + Vector points[pointCount]; -void AHuman::DrawThrowingReticle(BITMAP *targetBitmap, const Vector &targetPos, float progressScalar) const { - const int pointCount = 9; - Vector points[pointCount]; + for (int index = 0; index < pointCount; index++) { + points[index].SetXY(static_cast(index * 4), 0.0F); + } + Vector outOffset(m_pFGArm->GetMaxLength() * GetFlipFactor(), -m_pFGArm->GetMaxLength() * 0.5F); + float adjustedAimAngle = m_AimAngle * GetFlipFactor(); - for (int index = 0; index < pointCount; index++) { - points[index].SetXY(static_cast(index * 4), 0.0F); - } - Vector outOffset(m_pFGArm->GetMaxLength() * GetFlipFactor(), -m_pFGArm->GetMaxLength() * 0.5F); - float adjustedAimAngle = m_AimAngle * GetFlipFactor(); + acquire_bitmap(targetBitmap); - acquire_bitmap(targetBitmap); + for (int i = 0; i < pointCount * progressScalar; ++i) { + points[i].FlipX(m_HFlipped); + points[i] += outOffset; + points[i].RadRotate(adjustedAimAngle); + points[i] += m_pFGArm->GetJointPos(); - for (int i = 0; i < pointCount * progressScalar; ++i) { - points[i].FlipX(m_HFlipped); - points[i] += outOffset; - points[i].RadRotate(adjustedAimAngle); - points[i] += m_pFGArm->GetJointPos(); + g_PostProcessMan.RegisterGlowDotEffect(points[i], YellowDot, RandomNum(63, 127)); + putpixel(targetBitmap, points[i].GetFloorIntX() - targetPos.GetFloorIntX(), points[i].GetFloorIntY() - targetPos.GetFloorIntY(), g_YellowGlowColor); + } - g_PostProcessMan.RegisterGlowDotEffect(points[i], YellowDot, RandomNum(63, 127)); - putpixel(targetBitmap, points[i].GetFloorIntX() - targetPos.GetFloorIntX(), points[i].GetFloorIntY() - targetPos.GetFloorIntY(), g_YellowGlowColor); + release_bitmap(targetBitmap); } - release_bitmap(targetBitmap); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this AHuman's current graphical representation to a -// BITMAP of choice. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this AHuman's current graphical representation to a + // BITMAP of choice. -void AHuman::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { - Actor::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + void AHuman::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { + Actor::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - DrawMode realMode = (mode == g_DrawColor && !m_FlashWhiteTimer.IsPastRealTimeLimit()) ? g_DrawWhite : mode; - // Note: For some reason the ordering of the attachables list can get messed up. The most important thing here is that the FGArm is on top of everything else. - if (m_pHead && m_pHead->IsDrawnAfterParent()) { m_pHead->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); } - if (m_pFGArm) { m_pFGArm->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); } - - // Draw background Arm's hand after the HeldDevice of FGArm is drawn if the FGArm is holding a weapon. - if (m_pFGArm && m_pBGArm && !onlyPhysical && mode == g_DrawColor && m_pBGArm->GetHandHasReachedCurrentTarget() && !GetEquippedBGItem()) { - if (HeldDevice *heldDevice = m_pFGArm->GetHeldDevice(); heldDevice && !dynamic_cast(heldDevice) && !heldDevice->IsReloading() && !heldDevice->IsShield()) { - m_pBGArm->DrawHand(pTargetBitmap, targetPos, realMode); + DrawMode realMode = (mode == g_DrawColor && !m_FlashWhiteTimer.IsPastRealTimeLimit()) ? g_DrawWhite : mode; + // Note: For some reason the ordering of the attachables list can get messed up. The most important thing here is that the FGArm is on top of everything else. + if (m_pHead && m_pHead->IsDrawnAfterParent()) { + m_pHead->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); + } + if (m_pFGArm) { + m_pFGArm->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); } - } - if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawHandAndFootGroupVisualizations()) { - m_pFGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); - m_pBGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); - m_pFGHandGroup->Draw(pTargetBitmap, targetPos, true, 13); - m_pBGHandGroup->Draw(pTargetBitmap, targetPos, true, 13); - } + // Draw background Arm's hand after the HeldDevice of FGArm is drawn if the FGArm is holding a weapon. + if (m_pFGArm && m_pBGArm && !onlyPhysical && mode == g_DrawColor && m_pBGArm->GetHandHasReachedCurrentTarget() && !GetEquippedBGItem()) { + if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice(); heldDevice && !dynamic_cast(heldDevice) && !heldDevice->IsReloading() && !heldDevice->IsShield()) { + m_pBGArm->DrawHand(pTargetBitmap, targetPos, realMode); + } + } - if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawLimbPathVisualizations()) { - m_Paths[m_HFlipped][WALK].Draw(pTargetBitmap, targetPos, 122); - m_Paths[m_HFlipped][CRAWL].Draw(pTargetBitmap, targetPos, 122); - m_Paths[m_HFlipped][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13); - m_Paths[m_HFlipped][CLIMB].Draw(pTargetBitmap, targetPos, 165); - } -} + if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawHandAndFootGroupVisualizations()) { + m_pFGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); + m_pBGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); + m_pFGHandGroup->Draw(pTargetBitmap, targetPos, true, 13); + m_pBGHandGroup->Draw(pTargetBitmap, targetPos, true, 13); + } + if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawLimbPathVisualizations()) { + m_Paths[m_HFlipped][WALK].Draw(pTargetBitmap, targetPos, 122); + m_Paths[m_HFlipped][CRAWL].Draw(pTargetBitmap, targetPos, 122); + m_Paths[m_HFlipped][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13); + m_Paths[m_HFlipped][CLIMB].Draw(pTargetBitmap, targetPos, 165); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Actor's current graphical HUD overlay representation to a -// BITMAP of choice. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Actor's current graphical HUD overlay representation to a + // BITMAP of choice. -void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScreen, bool playerControlled) { - m_HUDStack = -m_CharHeight / 2; + void AHuman::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { + m_HUDStack = -m_CharHeight / 2; - // Only do HUD if on a team - if (m_Team < 0) - return; + // Only do HUD if on a team + if (m_Team < 0) + return; - // Only draw if the team viewing this is on the same team OR has seen the space where this is located. - int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); - if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam && (!g_SettingsMan.ShowEnemyHUD() || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam))) { - return; - } + // Only draw if the team viewing this is on the same team OR has seen the space where this is located. + int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); + if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam && (!g_SettingsMan.ShowEnemyHUD() || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam))) { + return; + } - Actor::DrawHUD(pTargetBitmap, targetPos, whichScreen); + Actor::DrawHUD(pTargetBitmap, targetPos, whichScreen); - if (!m_HUDVisible) { - return; - } + if (!m_HUDVisible) { + return; + } #ifdef DEBUG_BUILD - // Limbpath debug drawing - m_Paths[FGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); - m_Paths[FGROUND][CRAWL].Draw(pTargetBitmap, targetPos, 122); - m_Paths[FGROUND][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13); - m_Paths[FGROUND][CLIMB].Draw(pTargetBitmap, targetPos, 98); - - m_Paths[BGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); - m_Paths[BGROUND][CRAWL].Draw(pTargetBitmap, targetPos, 122); - m_Paths[BGROUND][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13); - m_Paths[BGROUND][CLIMB].Draw(pTargetBitmap, targetPos, 98); - - // Draw the AI paths - std::list::iterator last = m_MovePath.begin(); - Vector waypoint, lastPoint, lineVec; - for (std::list::iterator lItr = m_MovePath.begin(); lItr != m_MovePath.end(); ++lItr) - { - lastPoint = (*last) - targetPos; - waypoint = lastPoint + g_SceneMan.ShortestDistance(lastPoint, (*lItr) - targetPos); - line(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, waypoint.m_X, waypoint.m_Y, g_RedColor); - last = lItr; - } - waypoint = m_MoveTarget - targetPos; - circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 3, g_RedColor); - lastPoint = m_PrevPathTarget - targetPos; - circlefill(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, 2, g_YellowGlowColor); - lastPoint = m_DigTunnelEndPos - targetPos; - circlefill(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, 2, g_YellowGlowColor); - // Radius + // Limbpath debug drawing + m_Paths[FGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); + m_Paths[FGROUND][CRAWL].Draw(pTargetBitmap, targetPos, 122); + m_Paths[FGROUND][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13); + m_Paths[FGROUND][CLIMB].Draw(pTargetBitmap, targetPos, 98); + + m_Paths[BGROUND][WALK].Draw(pTargetBitmap, targetPos, 122); + m_Paths[BGROUND][CRAWL].Draw(pTargetBitmap, targetPos, 122); + m_Paths[BGROUND][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13); + m_Paths[BGROUND][CLIMB].Draw(pTargetBitmap, targetPos, 98); + + // Draw the AI paths + std::list::iterator last = m_MovePath.begin(); + Vector waypoint, lastPoint, lineVec; + for (std::list::iterator lItr = m_MovePath.begin(); lItr != m_MovePath.end(); ++lItr) { + lastPoint = (*last) - targetPos; + waypoint = lastPoint + g_SceneMan.ShortestDistance(lastPoint, (*lItr) - targetPos); + line(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, waypoint.m_X, waypoint.m_Y, g_RedColor); + last = lItr; + } + waypoint = m_MoveTarget - targetPos; + circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 3, g_RedColor); + lastPoint = m_PrevPathTarget - targetPos; + circlefill(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, 2, g_YellowGlowColor); + lastPoint = m_DigTunnelEndPos - targetPos; + circlefill(pTargetBitmap, lastPoint.m_X, lastPoint.m_Y, 2, g_YellowGlowColor); + // Radius // waypoint = m_Pos - targetPos; // circle(pTargetBitmap, waypoint.m_X, waypoint.m_Y, m_MoveProximityLimit, g_RedColor); #endif - // Player AI drawing - - if (m_pFGArm && m_pFGArm->GetHeldDevice()) { - // Draw the aiming dots for the currently held device. - if (m_ArmsState == THROWING_PREP) { - DrawThrowingReticle(pTargetBitmap, targetPos, GetThrowProgress()); - } else if (m_Controller.IsState(AIM_SHARP) || (m_Controller.IsPlayerControlled() && !m_Controller.IsState(PIE_MENU_ACTIVE))) { - m_pFGArm->GetHeldDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsState(AIM_SHARP) && m_Controller.IsPlayerControlled()); - } - } - - ////////////////////////////////////// - // Draw stat info HUD - char str[64]; - - GUIFont *pSymbolFont = g_FrameMan.GetLargeFont(); - GUIFont *pSmallFont = g_FrameMan.GetSmallFont(); + // Player AI drawing - // Only show extra HUD if this guy is controlled by the same player that this screen belongs to - if (m_Controller.IsPlayerControlled() && g_ActivityMan.GetActivity()->ScreenOfPlayer(m_Controller.GetPlayer()) == whichScreen && pSmallFont && pSymbolFont) - { - AllegroBitmap allegroBitmap(pTargetBitmap); - /* - // Device aiming reticle - if (m_Controller.IsState(AIM_SHARP) && - m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->HoldsHeldDevice()) - m_pFGArm->GetHeldDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen);*/ - - Vector drawPos = m_Pos - targetPos; - - // Adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam - if (!targetPos.IsZero()) - { - // Spans vertical scene seam - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) - { - if ((targetPos.m_X < 0) && (m_Pos.m_X > (sceneWidth - pTargetBitmap->w))) - drawPos.m_X -= sceneWidth; - else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_Pos.m_X < pTargetBitmap->w)) - drawPos.m_X += sceneWidth; - } - // Spans horizontal scene seam - int sceneHeight = g_SceneMan.GetSceneHeight(); - if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) - { - if ((targetPos.m_Y < 0) && (m_Pos.m_Y > (sceneHeight - pTargetBitmap->h))) - drawPos.m_Y -= sceneHeight; - else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_Pos.m_Y < pTargetBitmap->h)) - drawPos.m_Y += sceneHeight; + if (m_pFGArm && m_pFGArm->GetHeldDevice()) { + // Draw the aiming dots for the currently held device. + if (m_ArmsState == THROWING_PREP) { + DrawThrowingReticle(pTargetBitmap, targetPos, GetThrowProgress()); + } else if (m_Controller.IsState(AIM_SHARP) || (m_Controller.IsPlayerControlled() && !m_Controller.IsState(PIE_MENU_ACTIVE))) { + m_pFGArm->GetHeldDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsState(AIM_SHARP) && m_Controller.IsPlayerControlled()); } } - if (m_pFGArm || m_pBGArm) { - // Held-related GUI stuff - HDFirearm* fgHeldFirearm = dynamic_cast(GetEquippedItem()); - HDFirearm* bgHeldFirearm = dynamic_cast(GetEquippedBGItem()); - - if (fgHeldFirearm || bgHeldFirearm) { - str[0] = -56; - str[1] = 0; + ////////////////////////////////////// + // Draw stat info HUD + char str[64]; + + GUIFont* pSymbolFont = g_FrameMan.GetLargeFont(); + GUIFont* pSmallFont = g_FrameMan.GetSmallFont(); + + // Only show extra HUD if this guy is controlled by the same player that this screen belongs to + if (m_Controller.IsPlayerControlled() && g_ActivityMan.GetActivity()->ScreenOfPlayer(m_Controller.GetPlayer()) == whichScreen && pSmallFont && pSymbolFont) { + AllegroBitmap allegroBitmap(pTargetBitmap); + /* + // Device aiming reticle + if (m_Controller.IsState(AIM_SHARP) && + m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->HoldsHeldDevice()) + m_pFGArm->GetHeldDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen);*/ + + Vector drawPos = m_Pos - targetPos; + + // Adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam + if (!targetPos.IsZero()) { + // Spans vertical scene seam + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) { + if ((targetPos.m_X < 0) && (m_Pos.m_X > (sceneWidth - pTargetBitmap->w))) + drawPos.m_X -= sceneWidth; + else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_Pos.m_X < pTargetBitmap->w)) + drawPos.m_X += sceneWidth; + } + // Spans horizontal scene seam + int sceneHeight = g_SceneMan.GetSceneHeight(); + if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) { + if ((targetPos.m_Y < 0) && (m_Pos.m_Y > (sceneHeight - pTargetBitmap->h))) + drawPos.m_Y -= sceneHeight; + else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_Pos.m_Y < pTargetBitmap->h)) + drawPos.m_Y += sceneHeight; + } + } - std::string fgWeaponString = "EMPTY"; - if (fgHeldFirearm) { - if (fgHeldFirearm->IsReloading()) { - fgWeaponString = "Reloading"; - int barColorIndex = 77; - if (!fgHeldFirearm->GetSupportAvailable()) { - float reloadMultiplier = fgHeldFirearm->GetOneHandedReloadTimeMultiplier(); - if (reloadMultiplier != 1.0F) { - // Add a hand icon next to the ammo icon when reloading without supporting hand. - str[0] = -37; str[1] = -49; str[2] = -56; str[3] = 0; - if (reloadMultiplier > 1.0F) { - if (m_IconBlinkTimer.AlternateSim(250)) { barColorIndex = 13; } - } - else { - barColorIndex = 133; + if (m_pFGArm || m_pBGArm) { + // Held-related GUI stuff + HDFirearm* fgHeldFirearm = dynamic_cast(GetEquippedItem()); + HDFirearm* bgHeldFirearm = dynamic_cast(GetEquippedBGItem()); + + if (fgHeldFirearm || bgHeldFirearm) { + str[0] = -56; + str[1] = 0; + + std::string fgWeaponString = "EMPTY"; + if (fgHeldFirearm) { + if (fgHeldFirearm->IsReloading()) { + fgWeaponString = "Reloading"; + int barColorIndex = 77; + if (!fgHeldFirearm->GetSupportAvailable()) { + float reloadMultiplier = fgHeldFirearm->GetOneHandedReloadTimeMultiplier(); + if (reloadMultiplier != 1.0F) { + // Add a hand icon next to the ammo icon when reloading without supporting hand. + str[0] = -37; + str[1] = -49; + str[2] = -56; + str[3] = 0; + if (reloadMultiplier > 1.0F) { + if (m_IconBlinkTimer.AlternateSim(250)) { + barColorIndex = 13; + } + } else { + barColorIndex = 133; + } } } + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 13, drawPos.GetFloorIntX() + 29, drawPos.GetFloorIntY() + m_HUDStack + 14, 245); + rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 12, drawPos.GetFloorIntX() + static_cast(28.0F * fgHeldFirearm->GetReloadProgress() + 0.5F), drawPos.GetFloorIntY() + m_HUDStack + 13, barColorIndex); + } else { + fgWeaponString = fgHeldFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(fgHeldFirearm->GetRoundInMagCount()); } - rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 13, drawPos.GetFloorIntX() + 29, drawPos.GetFloorIntY() + m_HUDStack + 14, 245); - rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 12, drawPos.GetFloorIntX() + static_cast(28.0F * fgHeldFirearm->GetReloadProgress() + 0.5F), drawPos.GetFloorIntY() + m_HUDStack + 13, barColorIndex); - } - else { - fgWeaponString = fgHeldFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(fgHeldFirearm->GetRoundInMagCount()); } - } - std::string bgWeaponString; - if (bgHeldFirearm) { - if (bgHeldFirearm->IsReloading()) { - bgWeaponString = "Reloading"; - int barColorIndex = 77; - if (!bgHeldFirearm->GetSupportAvailable()) { - float reloadMultiplier = bgHeldFirearm->GetOneHandedReloadTimeMultiplier(); - if (reloadMultiplier != 1.0F) { - // Add a hand icon next to the ammo icon when reloading without supporting hand. - str[0] = -37; str[1] = -49; str[2] = -56; str[3] = 0; - if (reloadMultiplier > 1.0F) { - if (m_IconBlinkTimer.AlternateSim(250)) { barColorIndex = 13; } - } - else { - barColorIndex = 133; + std::string bgWeaponString; + if (bgHeldFirearm) { + if (bgHeldFirearm->IsReloading()) { + bgWeaponString = "Reloading"; + int barColorIndex = 77; + if (!bgHeldFirearm->GetSupportAvailable()) { + float reloadMultiplier = bgHeldFirearm->GetOneHandedReloadTimeMultiplier(); + if (reloadMultiplier != 1.0F) { + // Add a hand icon next to the ammo icon when reloading without supporting hand. + str[0] = -37; + str[1] = -49; + str[2] = -56; + str[3] = 0; + if (reloadMultiplier > 1.0F) { + if (m_IconBlinkTimer.AlternateSim(250)) { + barColorIndex = 13; + } + } else { + barColorIndex = 133; + } } } + int totalTextWidth = pSmallFont->CalculateWidth(fgWeaponString) + 6; + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1 + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, drawPos.GetFloorIntX() + 29 + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 14, 245); + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 12, drawPos.GetFloorIntX() + static_cast(28.0F * bgHeldFirearm->GetReloadProgress() + 0.5F) + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, barColorIndex); + } else { + bgWeaponString = bgHeldFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(bgHeldFirearm->GetRoundInMagCount()); } - int totalTextWidth = pSmallFont->CalculateWidth(fgWeaponString) + 6; - rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1 + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, drawPos.GetFloorIntX() + 29 + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 14, 245); - rectfill(pTargetBitmap, drawPos.GetFloorIntX() + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 12, drawPos.GetFloorIntX() + static_cast(28.0F * bgHeldFirearm->GetReloadProgress() + 0.5F) + totalTextWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, barColorIndex); - } - else { - bgWeaponString = bgHeldFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(bgHeldFirearm->GetRoundInMagCount()); } - } - pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - pSymbolFont->CalculateWidth(str) - 3, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Left); - std::snprintf(str, sizeof(str), bgHeldFirearm ? "%s | %s" : "%s", fgWeaponString.c_str(), bgWeaponString.c_str()); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 3, str, GUIFont::Left); - - m_HUDStack -= 9; - } - if (m_Controller.IsState(PIE_MENU_ACTIVE) || !m_EquipHUDTimer.IsPastRealMS(700)) { - HeldDevice* fgEquippedItem = GetEquippedItem(); - HeldDevice* bgEquippedItem = GetEquippedBGItem(); - std::string equippedItemsString = (fgEquippedItem ? fgEquippedItem->GetPresetName() : "EMPTY") + (bgEquippedItem ? " | " + bgEquippedItem->GetPresetName() : ""); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, equippedItemsString, GUIFont::Centre); - m_HUDStack -= 9; - } - } - else - { - std::snprintf(str, sizeof(str), "NO ARM!"); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X + 2, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Centre); - m_HUDStack -= 9; - } + pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - pSymbolFont->CalculateWidth(str) - 3, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Left); + std::snprintf(str, sizeof(str), bgHeldFirearm ? "%s | %s" : "%s", fgWeaponString.c_str(), bgWeaponString.c_str()); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 3, str, GUIFont::Left); - if (m_pJetpack && m_Status != INACTIVE && !m_Controller.IsState(PIE_MENU_ACTIVE) && (m_Controller.IsState(BODY_JUMP) || !m_pJetpack->IsFullyFueled())) { - if (m_pJetpack->GetJetTimeLeft() < 100.0F) { - str[0] = m_IconBlinkTimer.AlternateSim(100) ? -26 : -25; - } - else if (m_pJetpack->IsEmitting()) { - float acceleration = m_pJetpack->EstimateImpulse(false) / std::max(GetMass(), 0.1F); - if (acceleration > 0.41F) { - str[0] = acceleration > 0.47F ? -31 : -30; + m_HUDStack -= 9; } - else { - str[0] = acceleration > 0.35F ? -29 : -28; - if (m_IconBlinkTimer.AlternateSim(200)) { str[0] = -27; } + if (m_Controller.IsState(PIE_MENU_ACTIVE) || !m_EquipHUDTimer.IsPastRealMS(700)) { + HeldDevice* fgEquippedItem = GetEquippedItem(); + HeldDevice* bgEquippedItem = GetEquippedBGItem(); + std::string equippedItemsString = (fgEquippedItem ? fgEquippedItem->GetPresetName() : "EMPTY") + (bgEquippedItem ? " | " + bgEquippedItem->GetPresetName() : ""); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, equippedItemsString, GUIFont::Centre); + m_HUDStack -= 9; } + } else { + std::snprintf(str, sizeof(str), "NO ARM!"); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X + 2, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Centre); + m_HUDStack -= 9; } - else { - str[0] = -27; - } - str[1] = 0; - pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 7, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Centre); - rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 7, drawPos.GetFloorIntX() + 15, drawPos.GetFloorIntY() + m_HUDStack + 8, 245); - if (m_pJetpack->GetJetTimeTotal() > 0) { - float jetTimeRatio = m_pJetpack->GetJetTimeRatio(); - int gaugeColor; - if (jetTimeRatio > 0.75F) { - gaugeColor = 149; - } - else if (jetTimeRatio > 0.5F) { - gaugeColor = 133; - } - else if (jetTimeRatio > 0.375F) { - gaugeColor = 77; - } - else if (jetTimeRatio > 0.25F) { - gaugeColor = 48; + if (m_pJetpack && m_Status != INACTIVE && !m_Controller.IsState(PIE_MENU_ACTIVE) && (m_Controller.IsState(BODY_JUMP) || !m_pJetpack->IsFullyFueled())) { + if (m_pJetpack->GetJetTimeLeft() < 100.0F) { + str[0] = m_IconBlinkTimer.AlternateSim(100) ? -26 : -25; + } else if (m_pJetpack->IsEmitting()) { + float acceleration = m_pJetpack->EstimateImpulse(false) / std::max(GetMass(), 0.1F); + if (acceleration > 0.41F) { + str[0] = acceleration > 0.47F ? -31 : -30; + } else { + str[0] = acceleration > 0.35F ? -29 : -28; + if (m_IconBlinkTimer.AlternateSim(200)) { + str[0] = -27; + } + } + } else { + str[0] = -27; } - else { - gaugeColor = 13; + str[1] = 0; + pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 7, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Centre); + + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 7, drawPos.GetFloorIntX() + 15, drawPos.GetFloorIntY() + m_HUDStack + 8, 245); + if (m_pJetpack->GetJetTimeTotal() > 0) { + float jetTimeRatio = m_pJetpack->GetJetTimeRatio(); + int gaugeColor; + if (jetTimeRatio > 0.75F) { + gaugeColor = 149; + } else if (jetTimeRatio > 0.5F) { + gaugeColor = 133; + } else if (jetTimeRatio > 0.375F) { + gaugeColor = 77; + } else if (jetTimeRatio > 0.25F) { + gaugeColor = 48; + } else { + gaugeColor = 13; + } + rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 6, drawPos.GetFloorIntX() + static_cast(15.0F * jetTimeRatio), drawPos.GetFloorIntY() + m_HUDStack + 7, gaugeColor); } - rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 6, drawPos.GetFloorIntX() + static_cast(15.0F * jetTimeRatio), drawPos.GetFloorIntY() + m_HUDStack + 7, gaugeColor); + m_HUDStack -= 9; } - m_HUDStack -= 9; - } - // Pickup GUI - if (!m_Controller.IsState(PIE_MENU_ACTIVE) && m_pItemInReach) { - std::snprintf(str, sizeof(str), " %c %s", -49, m_pItemInReach->GetPresetName().c_str()); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 3, str, GUIFont::Centre); - m_HUDStack -= 9; + // Pickup GUI + if (!m_Controller.IsState(PIE_MENU_ACTIVE) && m_pItemInReach) { + std::snprintf(str, sizeof(str), " %c %s", -49, m_pItemInReach->GetPresetName().c_str()); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 3, str, GUIFont::Centre); + m_HUDStack -= 9; + } } } -} -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLimbPathSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get walking limb path speed for the specified preset. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLimbPathSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get walking limb path speed for the specified preset. -float AHuman::GetLimbPathSpeed(int speedPreset) const -{ - return m_Paths[FGROUND][WALK].GetSpeed(speedPreset); -} + float AHuman::GetLimbPathSpeed(int speedPreset) const { + return m_Paths[FGROUND][WALK].GetSpeed(speedPreset); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetLimbPathSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Set walking limb path speed for the specified preset. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetLimbPathSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Set walking limb path speed for the specified preset. -void AHuman::SetLimbPathSpeed(int speedPreset, float speed) -{ - m_Paths[FGROUND][WALK].OverrideSpeed(speedPreset, speed); - m_Paths[BGROUND][WALK].OverrideSpeed(speedPreset, speed); -} + void AHuman::SetLimbPathSpeed(int speedPreset, float speed) { + m_Paths[FGROUND][WALK].OverrideSpeed(speedPreset, speed); + m_Paths[BGROUND][WALK].OverrideSpeed(speedPreset, speed); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLimbPathPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the force that a limb traveling walking LimbPath can push against -// stuff in the scene with. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLimbPathPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the force that a limb traveling walking LimbPath can push against + // stuff in the scene with. -float AHuman::GetLimbPathPushForce() const -{ - return m_Paths[FGROUND][WALK].GetDefaultPushForce(); -} + float AHuman::GetLimbPathPushForce() const { + return m_Paths[FGROUND][WALK].GetDefaultPushForce(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetLimbPathPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the default force that a limb traveling walking LimbPath can push against -// stuff in the scene with. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetLimbPathPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the default force that a limb traveling walking LimbPath can push against + // stuff in the scene with. -void AHuman::SetLimbPathPushForce(float force) -{ - m_Paths[FGROUND][WALK].OverridePushForce(force); - m_Paths[BGROUND][WALK].OverridePushForce(force); -} + void AHuman::SetLimbPathPushForce(float force) { + m_Paths[FGROUND][WALK].OverridePushForce(force); + m_Paths[BGROUND][WALK].OverridePushForce(force); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int AHuman::WhilePieMenuOpenListener(const PieMenu *pieMenu) { - int result = Actor::WhilePieMenuOpenListener(pieMenu); + int AHuman::WhilePieMenuOpenListener(const PieMenu* pieMenu) { + int result = Actor::WhilePieMenuOpenListener(pieMenu); - for (PieSlice *pieSlice : GetPieMenu()->GetPieSlices()) { - switch (pieSlice->GetType()) { - case PieSlice::SliceType::Pickup: - case PieSlice::SliceType::Reload: - pieSlice->SetType(m_pItemInReach ? PieSlice::SliceType::Pickup : PieSlice::SliceType::Reload); - pieSlice->SetIcon(dynamic_cast(g_PresetMan.GetEntityPreset("Icon", m_pItemInReach ? "Pick Up" : "Refresh")->Clone())); + for (PieSlice* pieSlice: GetPieMenu()->GetPieSlices()) { + switch (pieSlice->GetType()) { + case PieSlice::SliceType::Pickup: + case PieSlice::SliceType::Reload: + pieSlice->SetType(m_pItemInReach ? PieSlice::SliceType::Pickup : PieSlice::SliceType::Reload); + pieSlice->SetIcon(dynamic_cast(g_PresetMan.GetEntityPreset("Icon", m_pItemInReach ? "Pick Up" : "Refresh")->Clone())); - if (pieSlice->GetType() == PieSlice::SliceType::Pickup) { - if (m_pFGArm || (m_pBGArm && m_pItemInReach->IsOneHanded())) { + if (pieSlice->GetType() == PieSlice::SliceType::Pickup) { + if (m_pFGArm || (m_pBGArm && m_pItemInReach->IsOneHanded())) { + pieSlice->SetEnabled(m_Status != INACTIVE); + pieSlice->SetDescription("Pick Up " + m_pItemInReach->GetPresetName()); + } else { + pieSlice->SetEnabled(false); + pieSlice->SetDescription("No Arm"); + } + } else { + const HeldDevice* fgHeldDevice = dynamic_cast(GetEquippedItem()); + const HeldDevice* bgHeldDevice = dynamic_cast(GetEquippedBGItem()); + if (fgHeldDevice || bgHeldDevice) { + pieSlice->SetEnabled(m_Status != INACTIVE && ((fgHeldDevice && !fgHeldDevice->IsFull()) || (bgHeldDevice && !bgHeldDevice->IsFull()))); + pieSlice->SetDescription("Reload"); + } else { + pieSlice->SetEnabled(false); + pieSlice->SetDescription(m_pFGArm ? "Not Holding Anything" : "No Arm"); + } + } + break; + case PieSlice::SliceType::NextItem: + if (!IsInventoryEmpty() && m_pFGArm) { pieSlice->SetEnabled(m_Status != INACTIVE); - pieSlice->SetDescription("Pick Up " + m_pItemInReach->GetPresetName()); + pieSlice->SetDescription("Next Item"); } else { pieSlice->SetEnabled(false); - pieSlice->SetDescription("No Arm"); + pieSlice->SetDescription(m_pFGArm ? "Not Holding Anything" : "No Arm"); } - } else { - const HeldDevice *fgHeldDevice = dynamic_cast(GetEquippedItem()); - const HeldDevice *bgHeldDevice = dynamic_cast(GetEquippedBGItem()); - if (fgHeldDevice || bgHeldDevice) { - pieSlice->SetEnabled(m_Status != INACTIVE && ((fgHeldDevice && !fgHeldDevice->IsFull()) || (bgHeldDevice && !bgHeldDevice->IsFull()))); - pieSlice->SetDescription("Reload"); + break; + case PieSlice::SliceType::PreviousItem: + if (!IsInventoryEmpty() && m_pFGArm) { + pieSlice->SetEnabled(m_Status != INACTIVE); + pieSlice->SetDescription("Prev Item"); } else { pieSlice->SetEnabled(false); pieSlice->SetDescription(m_pFGArm ? "Not Holding Anything" : "No Arm"); } - } - break; - case PieSlice::SliceType::NextItem: - if (!IsInventoryEmpty() && m_pFGArm) { - pieSlice->SetEnabled(m_Status != INACTIVE); - pieSlice->SetDescription("Next Item"); - } else { - pieSlice->SetEnabled(false); - pieSlice->SetDescription(m_pFGArm ? "Not Holding Anything" : "No Arm"); - } - break; - case PieSlice::SliceType::PreviousItem: - if (!IsInventoryEmpty() && m_pFGArm) { - pieSlice->SetEnabled(m_Status != INACTIVE); - pieSlice->SetDescription("Prev Item"); - } else { - pieSlice->SetEnabled(false); - pieSlice->SetDescription(m_pFGArm ? "Not Holding Anything" : "No Arm"); - } - break; - case PieSlice::SliceType::Drop: - if (const MovableObject *equippedFGItem = GetEquippedItem()) { - pieSlice->SetEnabled(m_Status != INACTIVE); - pieSlice->SetDescription("Drop " + equippedFGItem->GetPresetName()); - } else if (const MovableObject *equippedBGItem = GetEquippedBGItem()) { - pieSlice->SetDescription("Drop " + equippedBGItem->GetPresetName()); - pieSlice->SetEnabled(m_Status != INACTIVE); - } else if (!IsInventoryEmpty() && !m_pFGArm) { - pieSlice->SetEnabled(m_Status != INACTIVE); - pieSlice->SetDescription("Drop Inventory"); - } else { - pieSlice->SetEnabled(false); - pieSlice->SetDescription((m_pFGArm || m_pBGArm) ? "Not Holding Anything" : "No Arm"); - } - break; + break; + case PieSlice::SliceType::Drop: + if (const MovableObject* equippedFGItem = GetEquippedItem()) { + pieSlice->SetEnabled(m_Status != INACTIVE); + pieSlice->SetDescription("Drop " + equippedFGItem->GetPresetName()); + } else if (const MovableObject* equippedBGItem = GetEquippedBGItem()) { + pieSlice->SetDescription("Drop " + equippedBGItem->GetPresetName()); + pieSlice->SetEnabled(m_Status != INACTIVE); + } else if (!IsInventoryEmpty() && !m_pFGArm) { + pieSlice->SetEnabled(m_Status != INACTIVE); + pieSlice->SetDescription("Drop Inventory"); + } else { + pieSlice->SetEnabled(false); + pieSlice->SetDescription((m_pFGArm || m_pBGArm) ? "Not Holding Anything" : "No Arm"); + } + break; + } } + return result; } - return result; -} - } // namespace RTE diff --git a/Source/Entities/AHuman.h b/Source/Entities/AHuman.h index 22306412ec..9af2ebf22a 100644 --- a/Source/Entities/AHuman.h +++ b/Source/Entities/AHuman.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -21,1104 +20,1059 @@ struct BITMAP; -namespace RTE -{ - -class AEJetpack; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: AHuman -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A humanoid actor. -// Parent(s): Actor. -// Class history: 05/24/2001 AHuman created. - -class AHuman : public Actor { - friend struct EntityLuaBindings; - - -enum UpperBodyState -{ - WEAPON_READY = 0, - AIMING_SHARP, - HOLSTERING_BACK, - HOLSTERING_BELT, - DEHOLSTERING_BACK, - DEHOLSTERING_BELT, - THROWING_PREP, - THROWING_RELEASE -}; - -enum ProneState -{ - NOTPRONE = 0, - GOPRONE, - PRONE, - PRONESTATECOUNT -}; - -enum Layer -{ - FGROUND = 0, - BGROUND -}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(AHuman); -AddScriptFunctionNames(Actor, "OnStride"); -SerializableOverrideMethods; -ClassInfoGetters; -DefaultPieMenuNameGetter("Default Human Pie Menu"); - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: AHuman -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a AHuman object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - AHuman() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~AHuman -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a AHuman object before deletion -// from system memory. -// Arguments: None. - - ~AHuman() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AHuman object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a AHuman to be identical to another, by deep copy. -// Arguments: A reference to the AHuman to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const AHuman &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire AHuman, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Actor::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of this Actor and all its carried -// gold and inventory. -// Arguments: If this is supposed to be adjusted for a specific Tech's subjective -// value, then pass in the native DataModule ID of that tech. 0 means -// no Tech is specified and the base value is returned. -// How much to multiply the value if this happens to be a foreign Tech. -// Return value: The current value of this Actor and all his carried assets. - - float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically named object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. -// Arguments: The Preset name of the object to look for. -// Return value: Whetehr the object was found carried by this. - - bool HasObject(std::string objectName) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObjectInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically grouped object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. -// Arguments: The name of the group to look for. -// Return value: Whetehr the object in the group was found carried by this. - - bool HasObjectInGroup(std::string groupName) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetCPUPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of this' brain, or equivalent. -// Arguments: None. -// Return value: A Vector with the absolute position of this' brain. - - Vector GetCPUPos() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetEyePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of this' eye, or equivalent, where look -// vector starts from. -// Arguments: None. -// Return value: A Vector with the absolute position of this' eye or view point. - - Vector GetEyePos() const override; - - - /// - /// Gets the head of this AHuman. - /// - /// A pointer to the head of this AHuman. Ownership is NOT transferred. - Attachable * GetHead() const { return m_pHead; } - - /// - /// Sets the head for this AHuman. - /// - /// The new head to use. - void SetHead(Attachable *newHead); - - /// - /// Gets the jetpack of this AHuman. - /// - /// A pointer to the jetpack of this AHuman. Ownership is NOT transferred. - AEJetpack * GetJetpack() const { return m_pJetpack; } - - /// - /// Sets the jetpack for this AHuman. - /// - /// The new jetpack to use. - void SetJetpack(AEJetpack *newJetpack); - - /// - /// Gets the foreground Arm of this AHuman. - /// - /// A pointer to the foreground Arm of this AHuman. Ownership is NOT transferred. - Arm * GetFGArm() const { return m_pFGArm; } - - /// - /// Sets the foreground Arm for this AHuman. - /// - /// The new Arm to use. - void SetFGArm(Arm *newArm); - - /// - /// Gets the background arm of this AHuman. - /// - /// A pointer to the background arm of this AHuman. Ownership is NOT transferred. - Arm * GetBGArm() const { return m_pBGArm; } - - /// - /// Sets the background Arm for this AHuman. - /// - /// The new Arm to use. - void SetBGArm(Arm *newArm); - - /// - /// Gets the foreground Leg of this AHuman. - /// - /// A pointer to the foreground Leg of this AHuman. Ownership is NOT transferred. - Leg * GetFGLeg() const { return m_pFGLeg; } - - /// - /// Sets the foreground Leg for this AHuman. - /// - /// The new Leg to use. - void SetFGLeg(Leg *newLeg); - - /// - /// Gets the background Leg of this AHuman. - /// - /// A pointer to the background Leg of this AHuman. Ownership is NOT transferred. - Leg * GetBGLeg() const { return m_pBGLeg; } - - /// - /// Sets the background Leg for this AHuman. - /// - /// The new Leg to use. - void SetBGLeg(Leg *newLeg); - - /// - /// Gets the foot Attachable of this AHuman's foreground Leg. - /// - /// A pointer to the foot Attachable of this AHuman's foreground Leg. Ownership is NOT transferred! - Attachable * GetFGFoot() const { return m_pFGLeg ? m_pFGLeg->GetFoot() : nullptr; } - - /// - /// Sets the foot Attachable of this AHuman's foreground Leg. - /// - /// The new foot for this AHuman's foreground Leg to use. - void SetFGFoot(Attachable *newFoot) { if (m_pFGLeg && m_pFGLeg->IsAttached()) { m_pFGLeg->SetFoot(newFoot); } } - - /// - /// Gets the foot Attachable of this AHuman's background Leg. - /// - /// A pointer to the foot Attachable of this AHuman's background Leg. Ownership is NOT transferred! - Attachable * GetBGFoot() const { return m_pBGLeg ? m_pBGLeg->GetFoot() : nullptr; } - - /// - /// Sets the foot Attachable of this AHuman's background Leg. - /// - /// The new foot for this AHuman's background Leg to use. - void SetBGFoot(Attachable *newFoot) { if (m_pBGLeg && m_pBGLeg->IsAttached()) { m_pBGLeg->SetFoot(newFoot); } } - - /// Gets this AHuman's UpperBodyState. - /// - /// This AHuman's UpperBodyState. - UpperBodyState GetUpperBodyState() const { return m_ArmsState; } - - /// - /// Sets this AHuman's UpperBodyState to the new state. - /// - /// This AHuman's new UpperBodyState. - void SetUpperBodyState(UpperBodyState newUpperBodyState) { m_ArmsState = newUpperBodyState; } - - /// Gets this AHuman's ProneState. - /// - /// This AHuman's ProneState. - ProneState GetProneState() const { return m_ProneState; } - - /// - /// Sets this AHuman's ProneState to the new state. - /// - /// This AHuman's new ProneState. - void SetProneState(ProneState newProneState) { m_ProneState = newProneState; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. -// Arguments: Reference to the HitData struct which describes the collision. This -// will be modified to represent the results of the collision. -// Return value: Whether the collision has been deemed valid. If false, then disregard -// any impulses in the Hitdata. - - bool CollideAtPoint(HitData &hitData) override; - - /// - /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. - /// - /// The SliceType of the PieSlice being handled. - /// Whether or not the activated PieSlice SliceType was able to be handled. - bool HandlePieCommand(PieSlice::SliceType pieSliceType) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: AddInventoryItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an inventory item to this AHuman. This also puts that item -// directly in the hands of this if they are empty. -// Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! -// Return value: None. - - void AddInventoryItem(MovableObject *pItemToAdd) override; - - /// - /// Swaps the next MovableObject carried by this AHuman and puts one not currently carried into the back of the inventory of this. - /// For safety reasons, this will dump any non-HeldDevice inventory items it finds into MovableMan, ensuring the returned item is a HeldDevice (but not casted to one, for overload purposes). - /// - /// A pointer to the external MovableObject to swap in. Ownership IS transferred. - /// Whether or not to mute the sound on this event. - /// The next HeldDevice in this AHuman's inventory, if there are any. - MovableObject * SwapNextInventory(MovableObject *inventoryItemToSwapIn = nullptr, bool muteSound = false) override; - - /// - /// Swaps the previous MovableObject carried by this AHuman and puts one not currently carried into the back of the inventory of this. - /// For safety reasons, this will dump any non-HeldDevice inventory items it finds into MovableMan, ensuring the returned item is a HeldDevice (but not casted to one, for overload purposes). - /// - /// A pointer to the external MovableObject to swap in. Ownership IS transferred. - /// The previous HeldDevice in this AHuman's inventory, if there are any. - MovableObject * SwapPrevInventory(MovableObject *inventoryItemToSwapIn = nullptr) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipFirearm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first found firearm -// in the inventory. If the held device already is a firearm, or no -// firearm is in inventory, nothing happens. -// Arguments: Whether to actually equip any matching item found in the inventory, -// or just report that it's there or not. -// Return value: Whether a firearm was successfully switched to, or already held. - - bool EquipFirearm(bool doEquip = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipDeviceInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first found device -// of the specified group in the inventory. If the held device already -// is of that group, or no device is in inventory, nothing happens. -// Arguments: The group the device must belong to. -// Whether to actually equip any matching item found in the inventory, -// or just report that it's there or not. -// Return value: Whether a firearm was successfully switched to, or already held. - - bool EquipDeviceInGroup(std::string group, bool doEquip = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipLoadedFirearmInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first loaded HDFirearm -// of the specified group in the inventory. If no such weapon is in the -// inventory, nothing happens. -// Arguments: The group the HDFirearm must belong to. "Any" for all groups. -// The group the HDFirearm must *not* belong to. "None" for no group. -// Whether to actually equip any matching item found in the inventory, -// or just report that it's there or not. -// Return value: Whether a firearm was successfully switched to, or already held. - - bool EquipLoadedFirearmInGroup(std::string group, std::string exludeGroup, bool doEquip = true); - - /// - /// Switches the equipped HeldDevice (if any) to the first found device with the specified preset name in the inventory. - /// If the equipped HeldDevice is of that module and preset name, nothing happens. - /// - /// The preset name of the HeldDevice to equip. - /// Whether to actually equip any matching item found in the inventory, or just report whether or not it's there. - /// Whether a matching HeldDevice was successfully found/switched -o, or already held. - bool EquipNamedDevice(const std::string &presetName, bool doEquip) { return EquipNamedDevice("", presetName, doEquip); } - - /// - /// Switches the equipped HeldDevice (if any) to the first found device with the specified module and preset name in the inventory. - /// If the equipped HeldDevice is of that module and preset name, nothing happens. - /// - /// The module name of the HeldDevice to equip. - /// The preset name of the HeldDevice to equip. - /// Whether to actually equip any matching item found in the inventory, or just report whether or not it's there. - /// Whether a matching HeldDevice was successfully found/switched -o, or already held. - bool EquipNamedDevice(const std::string &moduleName, const std::string &presetName, bool doEquip); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipThrowable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first found ThrownDevice -// in the inventory. If the held device already is a ThrownDevice, or no -// ThrownDevice is in inventory, nothing happens. -// Arguments: Whether to actually equip any matching item found in the inventory, -// or just report that it's there or not. -// Return value: Whether a ThrownDevice was successfully switched to, or already held. - - bool EquipThrowable(bool doEquip = true); - - /// - /// Switches the currently held device (if any) to the strongest digging tool in the inventory. - /// - /// Whether to actually equip the strongest digging tool, or just report whether a digging tool was found. - /// Whether or not the strongest digging tool was successfully equipped. - bool EquipDiggingTool(bool doEquip = true); - - /// - /// Estimates what material strength any digger this AHuman is carrying can penetrate. - /// - /// The maximum material strength this AHuman's digger can penetrate, or a default dig strength if they don't have a digger. - float EstimateDigStrength() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipShield -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches the currently held device (if any) to the first found shield -// in the inventory. If the held device already is a shield, or no -// shield is in inventory, nothing happens. -// Arguments: None. -// Return value: Whether a shield was successfully switched to, or already held. - - bool EquipShield(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipShieldInBGArm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tries to equip the first shield in inventory to the background arm; -// this only works if nothing is held at all, or the FG arm holds a -// one-handed device, or we're in inventory mode. -// Arguments: None. -// Return value: Whether a shield was successfully equipped in the background arm. - - bool EquipShieldInBGArm(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: EquipDualWieldableInBGArm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tries to equip the first dual-wieldable in inventory to the background arm; -// this only works if nothing is held at all, or the FG arm holds a -// one-handed device, or we're in inventory mode. -// Arguments: None. -// Return value: Whether a shield was successfully equipped in the background arm. - -// bool EquipDualWieldableInBGArm(); - - /// - /// Gets the throw chargeup progress of this AHuman. - /// - /// The throw chargeup progress, as a scalar from 0 to 1. - float GetThrowProgress() const { return m_ThrowPrepTime > 0 ? static_cast(std::min(m_ThrowTmr.GetElapsedSimTimeMS() / static_cast(m_ThrowPrepTime), 1.0)) : 1.0F; } - - /// - /// Unequips whatever is in the FG arm and puts it into the inventory. - /// - /// Whether there was anything to unequip. - bool UnequipFGArm(); - - /// - /// Unequips whatever is in the BG arm and puts it into the inventory. - /// - /// Whether there was anything to unequip. - bool UnequipBGArm(); - - /// - /// Unequips whatever is in either of the arms and puts them into the inventory. - /// - void UnequipArms() { UnequipBGArm(); UnequipFGArm(); } - - /// - /// Gets the FG Arm's HeldDevice. Ownership is NOT transferred. - /// - /// The FG Arm's HeldDevice. - HeldDevice * GetEquippedItem() const { return m_pFGArm ? m_pFGArm->GetHeldDevice() : nullptr; } - - /// - /// Gets the BG Arm's HeldDevice. Ownership is NOT transferred. - /// - /// The BG Arm's HeldDevice. - HeldDevice * GetEquippedBGItem() const { return m_pBGArm ? m_pBGArm->GetHeldDevice() : nullptr; } - - /// - /// Gets the total mass of this AHuman's currently equipped devices. - /// - /// The mass of this AHuman's equipped devices. - float GetEquippedMass() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: FirearmIsReady -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is ready for use, and has -// ammo etc. -// Arguments: None. -// Return value: Whether a currently HDFirearm (if any) is ready for use. - - bool FirearmIsReady() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ThrowableIsReady -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held ThrownDevice's is ready to go. -// Arguments: None. -// Return value: Whether a currently held ThrownDevice (if any) is ready for use. - - bool ThrowableIsReady() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FirearmIsEmpty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is out of ammo. -// Arguments: None. -// Return value: Whether a currently HDFirearm (if any) is out of ammo. - - bool FirearmIsEmpty() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FirearmNeedsReload -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether any currently held HDFirearms are almost out of ammo. -// Arguments: None. -// Return value: Whether a currently HDFirearm (if any) has less than half of ammo left. - - bool FirearmNeedsReload() const; - - /// - /// Indicates whether currently held HDFirearms are reloading. If the parameter is true, it will only return true if all firearms are reloading, otherwise it will return whether any firearm is reloading. - /// - /// Whether or not currently held HDFirearms are reloading. - bool FirearmsAreReloading(bool onlyIfAllFirearmsAreReloading) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FirearmIsSemiAuto -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the currently held HDFirearm's is semi or full auto. -// Arguments: None. -// Return value: Whether a currently HDFirearm (if any) is a semi auto device. - - bool FirearmIsSemiAuto() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FirearmActivationDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the currently held device's delay between pulling the trigger -// and activating. -// Arguments: None. -// Return value: Delay in ms or zero if not a HDFirearm. - - int FirearmActivationDelay() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReloadFirearm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reloads the currently held firearm, if any. Will only reload the BG Firearm if the FG one is full already, to support reloading guns one at a time. -// Arguments: Whether or not to only reload empty fireams. -// Return value: None. - - void ReloadFirearms(bool onlyReloadEmptyFirearms = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsWithinRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a point on the scene is within close range of the currently -// used device and aiming status, if applicable. -// Arguments: A Vector with the aboslute coordinates of a point to check. -// Return value: Whether the point is within close range of this. - - bool IsWithinRange(Vector &point) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Look -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an unseen-revealing ray in the direction of where this is facing. -// Arguments: The degree angle to deviate from the current view point in the ray -// casting. A random ray will be chosen out of this +-range. -// The range, in pixels, beyond the actors sharp aim that the ray will have. -// Return value: Whether any unseen pixels were revealed by this look. - - bool Look(float FOVSpread, float range) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LookForGold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts a material detecting ray in the direction of where this is facing. -// Arguments: The degree angle to deviate from the current view point in the ray -// casting. A random ray will be chosen out of this +-range. -// The range, in pixels, that the ray will have. -// A Vector which will be filled with the absolute coordinates of any -// found gold. It will be unaltered if false is returned. -// Return value: Whether gold was spotted by this ray cast. If so, foundLocation -// has been filled out with the absolute location of the gold. - - bool LookForGold(float FOVSpread, float range, Vector &foundLocation) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LookForMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an MO detecting ray in the direction of where the head is looking -// at the time. Factors including head rotation, sharp aim mode, and -// other variables determine how this ray is cast. -// Arguments: The degree angle to deviate from the current view point in the ray -// casting. A random ray will be chosen out of this +-range. -// A specific material ID to ignore (see through) -// Whether to ignore all terrain or not (true means 'x-ray vision'). -// Return value: A pointer to the MO seen while looking. - - MovableObject * LookForMOs(float FOVSpread = 45, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false); - - - /// - /// Gets the GUI representation of this AHuman, only defaulting to its Head or body if no GraphicalIcon has been defined. - /// - /// The graphical representation of this AHuman as a BITMAP. - BITMAP * GetGraphicalIcon() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetAllTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resest all the timers used by this. Can be emitters, etc. This is to -// prevent backed up emissions to come out all at once while this has been -// held dormant in an inventory. -// Arguments: None. -// Return value: None. - - void ResetAllTimers() override; - - /// - /// Detects slopes in terrain and updates the walk path rotation for the corresponding Layer accordingly. - /// - /// The Layer in question. - void UpdateWalkAngle(AHuman::Layer whichLayer); - - /// - /// Detects overhead ceilings and crouches for them. - /// - void UpdateCrouching(); - - /// - /// Gets the walk path rotation for the specified Layer. - /// - /// The Layer in question. - /// The walk angle in radians. - float GetWalkAngle(AHuman::Layer whichLayer) const { return m_WalkAngle[whichLayer].GetRadAngle(); } - - /// - /// Sets the walk path rotation for the specified Layer. - /// - /// The Layer in question. - /// The angle to set. - void SetWalkAngle(AHuman::Layer whichLayer, float angle) { m_WalkAngle[whichLayer] = Matrix(angle); } - - /// - /// Gets whether this AHuman has just taken a stride this frame. - /// - /// Whether this AHuman has taken a stride this frame or not. - bool StrideFrame() const { return m_StrideFrame; } - - /// - /// Gets whether this AHuman is currently attempting to climb something, using arms. - /// - /// Whether this AHuman is currently climbing or not. - bool IsClimbing() const { return m_ArmClimbing[FGROUND] || m_ArmClimbing[BGROUND]; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreControllerUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void PreControllerUpdate() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this AHuman's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Actor's current graphical HUD overlay representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// Which player's screen this is being drawn to. May affect what HUD elements -// get drawn etc. -// Return value: None. - - void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - - - /// - /// Gets the LimbPath corresponding to the passed in Layer and MovementState values. - /// - /// Whether to get foreground or background LimbPath. - /// Which movement state to get the LimbPath for. - /// The LimbPath corresponding to the passed in Layer and MovementState values. - LimbPath * GetLimbPath(Layer layer, MovementState movementState) { return &m_Paths[layer][movementState]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLimbPathSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get walking limb path speed for the specified preset. -// Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST -// Return value: Limb path speed for the specified preset in m/s. - - float GetLimbPathSpeed(int speedPreset) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLimbPathSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Set walking limb path speed for the specified preset. -// Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST. New speed value in m/s. -// Return value: None. - - void SetLimbPathSpeed(int speedPreset, float speed); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLimbPathPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the default force that a limb traveling walking LimbPath can push against -// stuff in the scene with. -// Arguments: None. -// Return value: The default set force maximum, in kg * m/s^2. - - float GetLimbPathPushForce() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLimbPathPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the default force that a limb traveling walking LimbPath can push against -// stuff in the scene with. -// Arguments: The default set force maximum, in kg * m/s^2. -// Return value: None - - void SetLimbPathPushForce(float force); - - /// - /// Gets the target rot angle for the given MovementState. - /// - /// The MovementState to get the rot angle target for. - /// The target rot angle for the given MovementState. - float GetRotAngleTarget(MovementState movementState) { return m_RotAngleTargets[movementState]; } - - /// - /// Sets the target rot angle for the given MovementState. - /// - /// The MovementState to get the rot angle target for. - /// The new rot angle target to use. - void SetRotAngleTarget(MovementState movementState, float newRotAngleTarget) { m_RotAngleTargets[movementState] = newRotAngleTarget; } - - /// - /// Gets the duration it takes this AHuman to fully charge a throw. - /// - /// The duration it takes to fully charge a throw in MS. - long GetThrowPrepTime() const { return m_ThrowPrepTime; } - - /// - /// Sets the duration it takes this AHuman to fully charge a throw. - /// - /// New duration to fully charge a throw in MS. - void SetThrowPrepTime(long newPrepTime) { m_ThrowPrepTime = newPrepTime; } - - /// - /// Gets the rate at which this AHuman's Arms will swing with Leg movement, if they're not holding or supporting a HeldDevice. - /// - /// The arm swing rate of this AHuman. - float GetArmSwingRate() const { return m_ArmSwingRate; } - - /// - /// Sets the rate at which this AHuman's Arms will swing with Leg movement, if they're not holding or supporting a HeldDevice. - /// - /// The new arm swing rate for this AHuman. - void SetArmSwingRate(float newValue) { m_ArmSwingRate = newValue; } - - /// - /// Gets the rate at which this AHuman's Arms will sway with Leg movement, if they're holding or supporting a HeldDevice. - /// - /// The device arm sway rate of this AHuman. - float GetDeviceArmSwayRate() const { return m_DeviceArmSwayRate; } - - /// - /// Sets the rate at which this AHuman's Arms will sway with Leg movement, if they're holding or supporting a HeldDevice. - /// - /// The new device arm sway rate for this AHuman. - void SetDeviceArmSwayRate(float newValue) { m_DeviceArmSwayRate = newValue; } - - /// - /// Gets this AHuman's max walkpath adjustment upwards to crouch below low ceilings. - /// - /// This AHuman's max walkpath adjustment. - float GetMaxWalkPathCrouchShift() const { return m_MaxWalkPathCrouchShift; } - - /// - /// Sets this AHuman's max walkpath adjustment upwards to crouch below low ceilings. - /// - /// The new value for this AHuman's max walkpath adjustment. - void SetMaxWalkPathCrouchShift(float newValue) { m_MaxWalkPathCrouchShift = newValue; } - - /// - /// Gets this AHuman's max crouch rotation to duck below low ceilings. - /// - /// This AHuman's max crouch rotation adjustment. - float GetMaxCrouchRotation() const { return m_MaxCrouchRotation; } - - /// - /// Sets this AHuman's max crouch rotation to duck below low ceilings. - /// - /// The new value for this AHuman's max crouch rotation adjustment. - void SetMaxCrouchRotation(float newValue) { m_MaxCrouchRotation = newValue; } - - /// - /// Gets this AHuman's current crouch amount. 0.0 == fully standing, 1.0 == fully crouched. - /// - /// This AHuman's current crouch amount. - float GetCrouchAmount() const { return (m_WalkPathOffset.m_Y * -1.0F) / m_MaxWalkPathCrouchShift; } - - /// - /// Gets this AHuman's current crouch amount override. 0.0 == fully standing, 1.0 == fully crouched, -1 == no override. - /// - /// This AHuman's current crouch amount override. - float GetCrouchAmountOverride() const { return m_CrouchAmountOverride; } - - /// - /// Sets this AHuman's current crouch amount override. - /// - /// The new value for this AHuman's current crouch amount override. - void SetCrouchAmountOverride(float newValue) { m_CrouchAmountOverride = newValue; } - - /// - /// Gets this AHuman's stride sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this AHuman's stride sound. - SoundContainer * GetStrideSound() const { return m_StrideSound; } - - /// - /// Sets this AHuman's stride sound. Ownership IS transferred! - /// - /// The new SoundContainer for this AHuman's stride sound. - void SetStrideSound(SoundContainer *newSound) { m_StrideSound = newSound; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - /// - /// Function that is called when we get a new movepath. - /// This processes and cleans up the movepath. - /// - void OnNewMovePath() override; - - /// - /// Draws an aiming aid in front of this AHuman for throwing. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// A normalized scalar that determines the magnitude of the reticle, to indicate force in the throw. - void DrawThrowingReticle(BITMAP *targetBitmap, const Vector &targetPos = Vector(), float progressScalar = 1.0F) const; - - - // Member variables - static Entity::ClassInfo m_sClass; - // Articulated head. - Attachable *m_pHead; - // Ratio at which the head's rotation follows the aim angle - float m_LookToAimRatio; - // Foreground arm. - Arm *m_pFGArm; - // Background arm. - Arm *m_pBGArm; - // Foreground leg. - Leg *m_pFGLeg; - // Background leg. - Leg *m_pBGLeg; - // Limb AtomGroups. - AtomGroup *m_pFGHandGroup; - AtomGroup *m_pBGHandGroup; - AtomGroup *m_pFGFootGroup; - AtomGroup *m_BackupFGFootGroup; - AtomGroup *m_pBGFootGroup; - AtomGroup *m_BackupBGFootGroup; - // The sound of the actor taking a step (think robot servo) - SoundContainer *m_StrideSound; - // Jetpack booster. - AEJetpack *m_pJetpack; - bool m_CanActivateBGItem; //!< A flag for whether or not the BG item is waiting to be activated separately. Used for dual-wielding. TODO: Should this be able to be toggled off per actor, device, or controller? - bool m_TriggerPulled; //!< Internal flag for whether this AHuman is currently holding down the trigger of a HDFirearm. Used for dual-wielding. - bool m_WaitingToReloadOffhand; //!< A flag for whether or not the offhand HeldDevice is waiting to be reloaded. - // Blink timer - Timer m_IconBlinkTimer; - // Current upper body state. - UpperBodyState m_ArmsState; - // Current movement state. - MovementState m_MoveState; - // Whether the guy is currently lying down on the ground, rotational spring pulling him that way - // This is engaged if the player first crouches (still upright spring), and then presses left/right - // It is disengaged as soon as the crouch button/direction is released - ProneState m_ProneState; - // Timer for the going prone procedural animation - Timer m_ProneTimer; - // The maximum amount our walkpath can be shifted upwards to crouch and avoid ceilings above us - float m_MaxWalkPathCrouchShift; - // The maximum amount we will duck our head down to avoid obstacles above us. - float m_MaxCrouchRotation; - // The script-set forced crouching amount. 0.0 == fully standing, 1.0 == fully crouched, -1 == no override. - float m_CrouchAmountOverride; - // Limb paths for different movement states. - // [0] is for the foreground limbs, and [1] is for BG. - LimbPath m_Paths[2][MOVEMENTSTATECOUNT]; - std::array m_RotAngleTargets; //!< An array of rot angle targets for different movement states. - // Whether was aiming during the last frame too. - bool m_Aiming; - // Whether the BG Arm is helping with locomotion or not. - bool m_ArmClimbing[2]; - // Whether a stride was taken this frame or not. - bool m_StrideFrame = false; - // Controls the start of leg synch. - bool m_StrideStart; - // Times the stride to see if it is taking too long and needs restart - Timer m_StrideTimer; - // For timing throws - Timer m_ThrowTmr; - // The duration it takes this AHuman to fully charge a throw. - long m_ThrowPrepTime; - Timer m_SharpAimRevertTimer; //!< For timing the transition from sharp aim back to regular aim. - float m_FGArmFlailScalar; //!< The rate at which this AHuman's FG Arm follows the the bodily rotation. Best to keep this at 0 so it doesn't complicate aiming. - float m_BGArmFlailScalar; //!< The rate at which this AHuman's BG Arm follows the the bodily rotation. Set to a negative value for a "counterweight" effect. - Timer m_EquipHUDTimer; //!< Timer for showing the name of any newly equipped Device. - std::array m_WalkAngle; //!< An array of rot angle targets for different movement states. - Vector m_WalkPathOffset; - float m_ArmSwingRate; //!< Controls the rate at which this AHuman's Arms follow the movement of its Legs while they're not holding device(s). - float m_DeviceArmSwayRate; //!< Controls the rate at which this AHuman's Arms follow the movement of its Legs while they're holding device(s). One-handed devices sway half as much as two-handed ones. Defaults to three quarters of Arm swing rate. - - //////////////// - // AI States - - enum DeviceHandlingState - { - STILL = 0, - POINTING, - SCANNING, - AIMING, - FIRING, - THROWING, - DIGGING - }; - - enum SweepState - { - NOSWEEP = 0, - SWEEPINGUP, - SWEEPUPPAUSE, - SWEEPINGDOWN, - SWEEPDOWNPAUSE - }; - - enum DigState - { - NOTDIGGING = 0, - PREDIG, - STARTDIG, - TUNNELING, - FINISHINGDIG, - PAUSEDIGGER - }; - - enum JumpState - { - NOTJUMPING = 0, - FORWARDJUMP, - PREUPJUMP, - UPJUMP, - APEXJUMP, - LANDJUMP - }; - - // What the AI is doing with its held devices - DeviceHandlingState m_DeviceState; - // What we are doing with a device sweeping - SweepState m_SweepState; - // The current digging state - DigState m_DigState; - // The current jumping state - JumpState m_JumpState; - // Jumping target, overshoot this and the jump is completed - Vector m_JumpTarget; - // Jumping left or right - bool m_JumpingRight; - // AI is crawling - bool m_Crawling; - // The position of the end of the current tunnel being dug. When it is reached, digging can stop. - Vector m_DigTunnelEndPos; - // The center angle (in rads) for the sweeping motion done duing scannign and digging - float m_SweepCenterAimAngle; - // The range to each direction of the center that the sweeping motion will be done in - float m_SweepRange; - // The absolute coordinates of the last detected gold deposits - Vector m_DigTarget; - // Timer for how long to be shooting at a seen enemy target - Timer m_FireTimer; - // Timer for how long to be shooting at a seen enemy target - Timer m_SweepTimer; - // Timer for how long to be patrolling in a direction - Timer m_PatrolTimer; - // Timer for how long to be firing the jetpack in a direction - Timer m_JumpTimer; +namespace RTE { + + class AEJetpack; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: AHuman + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A humanoid actor. + // Parent(s): Actor. + // Class history: 05/24/2001 AHuman created. + + class AHuman : public Actor { + friend struct EntityLuaBindings; + + enum UpperBodyState { + WEAPON_READY = 0, + AIMING_SHARP, + HOLSTERING_BACK, + HOLSTERING_BELT, + DEHOLSTERING_BACK, + DEHOLSTERING_BELT, + THROWING_PREP, + THROWING_RELEASE + }; + + enum ProneState { + NOTPRONE = 0, + GOPRONE, + PRONE, + PRONESTATECOUNT + }; + + enum Layer { + FGROUND = 0, + BGROUND + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(AHuman); + AddScriptFunctionNames(Actor, "OnStride"); + SerializableOverrideMethods; + ClassInfoGetters; + DefaultPieMenuNameGetter("Default Human Pie Menu"); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: AHuman + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a AHuman object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + AHuman() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~AHuman + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a AHuman object before deletion + // from system memory. + // Arguments: None. + + ~AHuman() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the AHuman object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a AHuman to be identical to another, by deep copy. + // Arguments: A reference to the AHuman to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const AHuman& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire AHuman, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Actor::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of this Actor and all its carried + // gold and inventory. + // Arguments: If this is supposed to be adjusted for a specific Tech's subjective + // value, then pass in the native DataModule ID of that tech. 0 means + // no Tech is specified and the base value is returned. + // How much to multiply the value if this happens to be a foreign Tech. + // Return value: The current value of this Actor and all his carried assets. + + float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically named object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. + // Arguments: The Preset name of the object to look for. + // Return value: Whetehr the object was found carried by this. + + bool HasObject(std::string objectName) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObjectInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically grouped object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. + // Arguments: The name of the group to look for. + // Return value: Whetehr the object in the group was found carried by this. + + bool HasObjectInGroup(std::string groupName) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetCPUPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of this' brain, or equivalent. + // Arguments: None. + // Return value: A Vector with the absolute position of this' brain. + + Vector GetCPUPos() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetEyePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of this' eye, or equivalent, where look + // vector starts from. + // Arguments: None. + // Return value: A Vector with the absolute position of this' eye or view point. + + Vector GetEyePos() const override; + + /// + /// Gets the head of this AHuman. + /// + /// A pointer to the head of this AHuman. Ownership is NOT transferred. + Attachable* GetHead() const { return m_pHead; } + + /// + /// Sets the head for this AHuman. + /// + /// The new head to use. + void SetHead(Attachable* newHead); + + /// + /// Gets the jetpack of this AHuman. + /// + /// A pointer to the jetpack of this AHuman. Ownership is NOT transferred. + AEJetpack* GetJetpack() const { return m_pJetpack; } + + /// + /// Sets the jetpack for this AHuman. + /// + /// The new jetpack to use. + void SetJetpack(AEJetpack* newJetpack); + + /// + /// Gets the foreground Arm of this AHuman. + /// + /// A pointer to the foreground Arm of this AHuman. Ownership is NOT transferred. + Arm* GetFGArm() const { return m_pFGArm; } + + /// + /// Sets the foreground Arm for this AHuman. + /// + /// The new Arm to use. + void SetFGArm(Arm* newArm); + + /// + /// Gets the background arm of this AHuman. + /// + /// A pointer to the background arm of this AHuman. Ownership is NOT transferred. + Arm* GetBGArm() const { return m_pBGArm; } + + /// + /// Sets the background Arm for this AHuman. + /// + /// The new Arm to use. + void SetBGArm(Arm* newArm); + + /// + /// Gets the foreground Leg of this AHuman. + /// + /// A pointer to the foreground Leg of this AHuman. Ownership is NOT transferred. + Leg* GetFGLeg() const { return m_pFGLeg; } + + /// + /// Sets the foreground Leg for this AHuman. + /// + /// The new Leg to use. + void SetFGLeg(Leg* newLeg); + + /// + /// Gets the background Leg of this AHuman. + /// + /// A pointer to the background Leg of this AHuman. Ownership is NOT transferred. + Leg* GetBGLeg() const { return m_pBGLeg; } + + /// + /// Sets the background Leg for this AHuman. + /// + /// The new Leg to use. + void SetBGLeg(Leg* newLeg); + + /// + /// Gets the foot Attachable of this AHuman's foreground Leg. + /// + /// A pointer to the foot Attachable of this AHuman's foreground Leg. Ownership is NOT transferred! + Attachable* GetFGFoot() const { return m_pFGLeg ? m_pFGLeg->GetFoot() : nullptr; } + + /// + /// Sets the foot Attachable of this AHuman's foreground Leg. + /// + /// The new foot for this AHuman's foreground Leg to use. + void SetFGFoot(Attachable* newFoot) { + if (m_pFGLeg && m_pFGLeg->IsAttached()) { + m_pFGLeg->SetFoot(newFoot); + } + } + + /// + /// Gets the foot Attachable of this AHuman's background Leg. + /// + /// A pointer to the foot Attachable of this AHuman's background Leg. Ownership is NOT transferred! + Attachable* GetBGFoot() const { return m_pBGLeg ? m_pBGLeg->GetFoot() : nullptr; } + + /// + /// Sets the foot Attachable of this AHuman's background Leg. + /// + /// The new foot for this AHuman's background Leg to use. + void SetBGFoot(Attachable* newFoot) { + if (m_pBGLeg && m_pBGLeg->IsAttached()) { + m_pBGLeg->SetFoot(newFoot); + } + } + + /// Gets this AHuman's UpperBodyState. + /// + /// This AHuman's UpperBodyState. + UpperBodyState GetUpperBodyState() const { return m_ArmsState; } + + /// + /// Sets this AHuman's UpperBodyState to the new state. + /// + /// This AHuman's new UpperBodyState. + void SetUpperBodyState(UpperBodyState newUpperBodyState) { m_ArmsState = newUpperBodyState; } + + /// Gets this AHuman's ProneState. + /// + /// This AHuman's ProneState. + ProneState GetProneState() const { return m_ProneState; } + + /// + /// Sets this AHuman's ProneState to the new state. + /// + /// This AHuman's new ProneState. + void SetProneState(ProneState newProneState) { m_ProneState = newProneState; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. + // Arguments: Reference to the HitData struct which describes the collision. This + // will be modified to represent the results of the collision. + // Return value: Whether the collision has been deemed valid. If false, then disregard + // any impulses in the Hitdata. + + bool CollideAtPoint(HitData& hitData) override; + + /// + /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. + /// + /// The SliceType of the PieSlice being handled. + /// Whether or not the activated PieSlice SliceType was able to be handled. + bool HandlePieCommand(PieSlice::SliceType pieSliceType) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: AddInventoryItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an inventory item to this AHuman. This also puts that item + // directly in the hands of this if they are empty. + // Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! + // Return value: None. + + void AddInventoryItem(MovableObject* pItemToAdd) override; + + /// + /// Swaps the next MovableObject carried by this AHuman and puts one not currently carried into the back of the inventory of this. + /// For safety reasons, this will dump any non-HeldDevice inventory items it finds into MovableMan, ensuring the returned item is a HeldDevice (but not casted to one, for overload purposes). + /// + /// A pointer to the external MovableObject to swap in. Ownership IS transferred. + /// Whether or not to mute the sound on this event. + /// The next HeldDevice in this AHuman's inventory, if there are any. + MovableObject* SwapNextInventory(MovableObject* inventoryItemToSwapIn = nullptr, bool muteSound = false) override; + + /// + /// Swaps the previous MovableObject carried by this AHuman and puts one not currently carried into the back of the inventory of this. + /// For safety reasons, this will dump any non-HeldDevice inventory items it finds into MovableMan, ensuring the returned item is a HeldDevice (but not casted to one, for overload purposes). + /// + /// A pointer to the external MovableObject to swap in. Ownership IS transferred. + /// The previous HeldDevice in this AHuman's inventory, if there are any. + MovableObject* SwapPrevInventory(MovableObject* inventoryItemToSwapIn = nullptr) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipFirearm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first found firearm + // in the inventory. If the held device already is a firearm, or no + // firearm is in inventory, nothing happens. + // Arguments: Whether to actually equip any matching item found in the inventory, + // or just report that it's there or not. + // Return value: Whether a firearm was successfully switched to, or already held. + + bool EquipFirearm(bool doEquip = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipDeviceInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first found device + // of the specified group in the inventory. If the held device already + // is of that group, or no device is in inventory, nothing happens. + // Arguments: The group the device must belong to. + // Whether to actually equip any matching item found in the inventory, + // or just report that it's there or not. + // Return value: Whether a firearm was successfully switched to, or already held. + + bool EquipDeviceInGroup(std::string group, bool doEquip = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipLoadedFirearmInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first loaded HDFirearm + // of the specified group in the inventory. If no such weapon is in the + // inventory, nothing happens. + // Arguments: The group the HDFirearm must belong to. "Any" for all groups. + // The group the HDFirearm must *not* belong to. "None" for no group. + // Whether to actually equip any matching item found in the inventory, + // or just report that it's there or not. + // Return value: Whether a firearm was successfully switched to, or already held. + + bool EquipLoadedFirearmInGroup(std::string group, std::string exludeGroup, bool doEquip = true); + + /// + /// Switches the equipped HeldDevice (if any) to the first found device with the specified preset name in the inventory. + /// If the equipped HeldDevice is of that module and preset name, nothing happens. + /// + /// The preset name of the HeldDevice to equip. + /// Whether to actually equip any matching item found in the inventory, or just report whether or not it's there. + /// Whether a matching HeldDevice was successfully found/switched -o, or already held. + bool EquipNamedDevice(const std::string& presetName, bool doEquip) { return EquipNamedDevice("", presetName, doEquip); } + + /// + /// Switches the equipped HeldDevice (if any) to the first found device with the specified module and preset name in the inventory. + /// If the equipped HeldDevice is of that module and preset name, nothing happens. + /// + /// The module name of the HeldDevice to equip. + /// The preset name of the HeldDevice to equip. + /// Whether to actually equip any matching item found in the inventory, or just report whether or not it's there. + /// Whether a matching HeldDevice was successfully found/switched -o, or already held. + bool EquipNamedDevice(const std::string& moduleName, const std::string& presetName, bool doEquip); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipThrowable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first found ThrownDevice + // in the inventory. If the held device already is a ThrownDevice, or no + // ThrownDevice is in inventory, nothing happens. + // Arguments: Whether to actually equip any matching item found in the inventory, + // or just report that it's there or not. + // Return value: Whether a ThrownDevice was successfully switched to, or already held. + + bool EquipThrowable(bool doEquip = true); + + /// + /// Switches the currently held device (if any) to the strongest digging tool in the inventory. + /// + /// Whether to actually equip the strongest digging tool, or just report whether a digging tool was found. + /// Whether or not the strongest digging tool was successfully equipped. + bool EquipDiggingTool(bool doEquip = true); + + /// + /// Estimates what material strength any digger this AHuman is carrying can penetrate. + /// + /// The maximum material strength this AHuman's digger can penetrate, or a default dig strength if they don't have a digger. + float EstimateDigStrength() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipShield + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches the currently held device (if any) to the first found shield + // in the inventory. If the held device already is a shield, or no + // shield is in inventory, nothing happens. + // Arguments: None. + // Return value: Whether a shield was successfully switched to, or already held. + + bool EquipShield(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipShieldInBGArm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tries to equip the first shield in inventory to the background arm; + // this only works if nothing is held at all, or the FG arm holds a + // one-handed device, or we're in inventory mode. + // Arguments: None. + // Return value: Whether a shield was successfully equipped in the background arm. + + bool EquipShieldInBGArm(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: EquipDualWieldableInBGArm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tries to equip the first dual-wieldable in inventory to the background arm; + // this only works if nothing is held at all, or the FG arm holds a + // one-handed device, or we're in inventory mode. + // Arguments: None. + // Return value: Whether a shield was successfully equipped in the background arm. + + // bool EquipDualWieldableInBGArm(); + + /// + /// Gets the throw chargeup progress of this AHuman. + /// + /// The throw chargeup progress, as a scalar from 0 to 1. + float GetThrowProgress() const { return m_ThrowPrepTime > 0 ? static_cast(std::min(m_ThrowTmr.GetElapsedSimTimeMS() / static_cast(m_ThrowPrepTime), 1.0)) : 1.0F; } + + /// + /// Unequips whatever is in the FG arm and puts it into the inventory. + /// + /// Whether there was anything to unequip. + bool UnequipFGArm(); + + /// + /// Unequips whatever is in the BG arm and puts it into the inventory. + /// + /// Whether there was anything to unequip. + bool UnequipBGArm(); + + /// + /// Unequips whatever is in either of the arms and puts them into the inventory. + /// + void UnequipArms() { + UnequipBGArm(); + UnequipFGArm(); + } + + /// + /// Gets the FG Arm's HeldDevice. Ownership is NOT transferred. + /// + /// The FG Arm's HeldDevice. + HeldDevice* GetEquippedItem() const { return m_pFGArm ? m_pFGArm->GetHeldDevice() : nullptr; } + + /// + /// Gets the BG Arm's HeldDevice. Ownership is NOT transferred. + /// + /// The BG Arm's HeldDevice. + HeldDevice* GetEquippedBGItem() const { return m_pBGArm ? m_pBGArm->GetHeldDevice() : nullptr; } + + /// + /// Gets the total mass of this AHuman's currently equipped devices. + /// + /// The mass of this AHuman's equipped devices. + float GetEquippedMass() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: FirearmIsReady + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is ready for use, and has + // ammo etc. + // Arguments: None. + // Return value: Whether a currently HDFirearm (if any) is ready for use. + + bool FirearmIsReady() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ThrowableIsReady + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held ThrownDevice's is ready to go. + // Arguments: None. + // Return value: Whether a currently held ThrownDevice (if any) is ready for use. + + bool ThrowableIsReady() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FirearmIsEmpty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is out of ammo. + // Arguments: None. + // Return value: Whether a currently HDFirearm (if any) is out of ammo. + + bool FirearmIsEmpty() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FirearmNeedsReload + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether any currently held HDFirearms are almost out of ammo. + // Arguments: None. + // Return value: Whether a currently HDFirearm (if any) has less than half of ammo left. + + bool FirearmNeedsReload() const; + + /// + /// Indicates whether currently held HDFirearms are reloading. If the parameter is true, it will only return true if all firearms are reloading, otherwise it will return whether any firearm is reloading. + /// + /// Whether or not currently held HDFirearms are reloading. + bool FirearmsAreReloading(bool onlyIfAllFirearmsAreReloading) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FirearmIsSemiAuto + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the currently held HDFirearm's is semi or full auto. + // Arguments: None. + // Return value: Whether a currently HDFirearm (if any) is a semi auto device. + + bool FirearmIsSemiAuto() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FirearmActivationDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the currently held device's delay between pulling the trigger + // and activating. + // Arguments: None. + // Return value: Delay in ms or zero if not a HDFirearm. + + int FirearmActivationDelay() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReloadFirearm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reloads the currently held firearm, if any. Will only reload the BG Firearm if the FG one is full already, to support reloading guns one at a time. + // Arguments: Whether or not to only reload empty fireams. + // Return value: None. + + void ReloadFirearms(bool onlyReloadEmptyFirearms = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsWithinRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a point on the scene is within close range of the currently + // used device and aiming status, if applicable. + // Arguments: A Vector with the aboslute coordinates of a point to check. + // Return value: Whether the point is within close range of this. + + bool IsWithinRange(Vector& point) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Look + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an unseen-revealing ray in the direction of where this is facing. + // Arguments: The degree angle to deviate from the current view point in the ray + // casting. A random ray will be chosen out of this +-range. + // The range, in pixels, beyond the actors sharp aim that the ray will have. + // Return value: Whether any unseen pixels were revealed by this look. + + bool Look(float FOVSpread, float range) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LookForGold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts a material detecting ray in the direction of where this is facing. + // Arguments: The degree angle to deviate from the current view point in the ray + // casting. A random ray will be chosen out of this +-range. + // The range, in pixels, that the ray will have. + // A Vector which will be filled with the absolute coordinates of any + // found gold. It will be unaltered if false is returned. + // Return value: Whether gold was spotted by this ray cast. If so, foundLocation + // has been filled out with the absolute location of the gold. + + bool LookForGold(float FOVSpread, float range, Vector& foundLocation) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LookForMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an MO detecting ray in the direction of where the head is looking + // at the time. Factors including head rotation, sharp aim mode, and + // other variables determine how this ray is cast. + // Arguments: The degree angle to deviate from the current view point in the ray + // casting. A random ray will be chosen out of this +-range. + // A specific material ID to ignore (see through) + // Whether to ignore all terrain or not (true means 'x-ray vision'). + // Return value: A pointer to the MO seen while looking. + + MovableObject* LookForMOs(float FOVSpread = 45, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false); + + /// + /// Gets the GUI representation of this AHuman, only defaulting to its Head or body if no GraphicalIcon has been defined. + /// + /// The graphical representation of this AHuman as a BITMAP. + BITMAP* GetGraphicalIcon() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ResetAllTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resest all the timers used by this. Can be emitters, etc. This is to + // prevent backed up emissions to come out all at once while this has been + // held dormant in an inventory. + // Arguments: None. + // Return value: None. + + void ResetAllTimers() override; + + /// + /// Detects slopes in terrain and updates the walk path rotation for the corresponding Layer accordingly. + /// + /// The Layer in question. + void UpdateWalkAngle(AHuman::Layer whichLayer); + + /// + /// Detects overhead ceilings and crouches for them. + /// + void UpdateCrouching(); + + /// + /// Gets the walk path rotation for the specified Layer. + /// + /// The Layer in question. + /// The walk angle in radians. + float GetWalkAngle(AHuman::Layer whichLayer) const { return m_WalkAngle[whichLayer].GetRadAngle(); } + + /// + /// Sets the walk path rotation for the specified Layer. + /// + /// The Layer in question. + /// The angle to set. + void SetWalkAngle(AHuman::Layer whichLayer, float angle) { m_WalkAngle[whichLayer] = Matrix(angle); } + + /// + /// Gets whether this AHuman has just taken a stride this frame. + /// + /// Whether this AHuman has taken a stride this frame or not. + bool StrideFrame() const { return m_StrideFrame; } + + /// + /// Gets whether this AHuman is currently attempting to climb something, using arms. + /// + /// Whether this AHuman is currently climbing or not. + bool IsClimbing() const { return m_ArmClimbing[FGROUND] || m_ArmClimbing[BGROUND]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreControllerUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void PreControllerUpdate() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this AHuman's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Actor's current graphical HUD overlay representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // Which player's screen this is being drawn to. May affect what HUD elements + // get drawn etc. + // Return value: None. + + void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; + + /// + /// Gets the LimbPath corresponding to the passed in Layer and MovementState values. + /// + /// Whether to get foreground or background LimbPath. + /// Which movement state to get the LimbPath for. + /// The LimbPath corresponding to the passed in Layer and MovementState values. + LimbPath* GetLimbPath(Layer layer, MovementState movementState) { return &m_Paths[layer][movementState]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLimbPathSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get walking limb path speed for the specified preset. + // Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST + // Return value: Limb path speed for the specified preset in m/s. + + float GetLimbPathSpeed(int speedPreset) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLimbPathSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Set walking limb path speed for the specified preset. + // Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST. New speed value in m/s. + // Return value: None. + + void SetLimbPathSpeed(int speedPreset, float speed); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLimbPathPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the default force that a limb traveling walking LimbPath can push against + // stuff in the scene with. + // Arguments: None. + // Return value: The default set force maximum, in kg * m/s^2. + + float GetLimbPathPushForce() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLimbPathPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the default force that a limb traveling walking LimbPath can push against + // stuff in the scene with. + // Arguments: The default set force maximum, in kg * m/s^2. + // Return value: None + + void SetLimbPathPushForce(float force); + + /// + /// Gets the target rot angle for the given MovementState. + /// + /// The MovementState to get the rot angle target for. + /// The target rot angle for the given MovementState. + float GetRotAngleTarget(MovementState movementState) { return m_RotAngleTargets[movementState]; } + + /// + /// Sets the target rot angle for the given MovementState. + /// + /// The MovementState to get the rot angle target for. + /// The new rot angle target to use. + void SetRotAngleTarget(MovementState movementState, float newRotAngleTarget) { m_RotAngleTargets[movementState] = newRotAngleTarget; } + + /// + /// Gets the duration it takes this AHuman to fully charge a throw. + /// + /// The duration it takes to fully charge a throw in MS. + long GetThrowPrepTime() const { return m_ThrowPrepTime; } + + /// + /// Sets the duration it takes this AHuman to fully charge a throw. + /// + /// New duration to fully charge a throw in MS. + void SetThrowPrepTime(long newPrepTime) { m_ThrowPrepTime = newPrepTime; } + + /// + /// Gets the rate at which this AHuman's Arms will swing with Leg movement, if they're not holding or supporting a HeldDevice. + /// + /// The arm swing rate of this AHuman. + float GetArmSwingRate() const { return m_ArmSwingRate; } + + /// + /// Sets the rate at which this AHuman's Arms will swing with Leg movement, if they're not holding or supporting a HeldDevice. + /// + /// The new arm swing rate for this AHuman. + void SetArmSwingRate(float newValue) { m_ArmSwingRate = newValue; } + + /// + /// Gets the rate at which this AHuman's Arms will sway with Leg movement, if they're holding or supporting a HeldDevice. + /// + /// The device arm sway rate of this AHuman. + float GetDeviceArmSwayRate() const { return m_DeviceArmSwayRate; } + + /// + /// Sets the rate at which this AHuman's Arms will sway with Leg movement, if they're holding or supporting a HeldDevice. + /// + /// The new device arm sway rate for this AHuman. + void SetDeviceArmSwayRate(float newValue) { m_DeviceArmSwayRate = newValue; } + + /// + /// Gets this AHuman's max walkpath adjustment upwards to crouch below low ceilings. + /// + /// This AHuman's max walkpath adjustment. + float GetMaxWalkPathCrouchShift() const { return m_MaxWalkPathCrouchShift; } + + /// + /// Sets this AHuman's max walkpath adjustment upwards to crouch below low ceilings. + /// + /// The new value for this AHuman's max walkpath adjustment. + void SetMaxWalkPathCrouchShift(float newValue) { m_MaxWalkPathCrouchShift = newValue; } + + /// + /// Gets this AHuman's max crouch rotation to duck below low ceilings. + /// + /// This AHuman's max crouch rotation adjustment. + float GetMaxCrouchRotation() const { return m_MaxCrouchRotation; } + + /// + /// Sets this AHuman's max crouch rotation to duck below low ceilings. + /// + /// The new value for this AHuman's max crouch rotation adjustment. + void SetMaxCrouchRotation(float newValue) { m_MaxCrouchRotation = newValue; } + + /// + /// Gets this AHuman's current crouch amount. 0.0 == fully standing, 1.0 == fully crouched. + /// + /// This AHuman's current crouch amount. + float GetCrouchAmount() const { return (m_WalkPathOffset.m_Y * -1.0F) / m_MaxWalkPathCrouchShift; } + + /// + /// Gets this AHuman's current crouch amount override. 0.0 == fully standing, 1.0 == fully crouched, -1 == no override. + /// + /// This AHuman's current crouch amount override. + float GetCrouchAmountOverride() const { return m_CrouchAmountOverride; } + + /// + /// Sets this AHuman's current crouch amount override. + /// + /// The new value for this AHuman's current crouch amount override. + void SetCrouchAmountOverride(float newValue) { m_CrouchAmountOverride = newValue; } + + /// + /// Gets this AHuman's stride sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this AHuman's stride sound. + SoundContainer* GetStrideSound() const { return m_StrideSound; } + + /// + /// Sets this AHuman's stride sound. Ownership IS transferred! + /// + /// The new SoundContainer for this AHuman's stride sound. + void SetStrideSound(SoundContainer* newSound) { m_StrideSound = newSound; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Function that is called when we get a new movepath. + /// This processes and cleans up the movepath. + /// + void OnNewMovePath() override; + + /// + /// Draws an aiming aid in front of this AHuman for throwing. + /// + /// A pointer to a BITMAP to draw on. + /// The absolute position of the target bitmap's upper left corner in the Scene. + /// A normalized scalar that determines the magnitude of the reticle, to indicate force in the throw. + void DrawThrowingReticle(BITMAP* targetBitmap, const Vector& targetPos = Vector(), float progressScalar = 1.0F) const; + + // Member variables + static Entity::ClassInfo m_sClass; + // Articulated head. + Attachable* m_pHead; + // Ratio at which the head's rotation follows the aim angle + float m_LookToAimRatio; + // Foreground arm. + Arm* m_pFGArm; + // Background arm. + Arm* m_pBGArm; + // Foreground leg. + Leg* m_pFGLeg; + // Background leg. + Leg* m_pBGLeg; + // Limb AtomGroups. + AtomGroup* m_pFGHandGroup; + AtomGroup* m_pBGHandGroup; + AtomGroup* m_pFGFootGroup; + AtomGroup* m_BackupFGFootGroup; + AtomGroup* m_pBGFootGroup; + AtomGroup* m_BackupBGFootGroup; + // The sound of the actor taking a step (think robot servo) + SoundContainer* m_StrideSound; + // Jetpack booster. + AEJetpack* m_pJetpack; + bool m_CanActivateBGItem; //!< A flag for whether or not the BG item is waiting to be activated separately. Used for dual-wielding. TODO: Should this be able to be toggled off per actor, device, or controller? + bool m_TriggerPulled; //!< Internal flag for whether this AHuman is currently holding down the trigger of a HDFirearm. Used for dual-wielding. + bool m_WaitingToReloadOffhand; //!< A flag for whether or not the offhand HeldDevice is waiting to be reloaded. + // Blink timer + Timer m_IconBlinkTimer; + // Current upper body state. + UpperBodyState m_ArmsState; + // Current movement state. + MovementState m_MoveState; + // Whether the guy is currently lying down on the ground, rotational spring pulling him that way + // This is engaged if the player first crouches (still upright spring), and then presses left/right + // It is disengaged as soon as the crouch button/direction is released + ProneState m_ProneState; + // Timer for the going prone procedural animation + Timer m_ProneTimer; + // The maximum amount our walkpath can be shifted upwards to crouch and avoid ceilings above us + float m_MaxWalkPathCrouchShift; + // The maximum amount we will duck our head down to avoid obstacles above us. + float m_MaxCrouchRotation; + // The script-set forced crouching amount. 0.0 == fully standing, 1.0 == fully crouched, -1 == no override. + float m_CrouchAmountOverride; + // Limb paths for different movement states. + // [0] is for the foreground limbs, and [1] is for BG. + LimbPath m_Paths[2][MOVEMENTSTATECOUNT]; + std::array m_RotAngleTargets; //!< An array of rot angle targets for different movement states. + // Whether was aiming during the last frame too. + bool m_Aiming; + // Whether the BG Arm is helping with locomotion or not. + bool m_ArmClimbing[2]; + // Whether a stride was taken this frame or not. + bool m_StrideFrame = false; + // Controls the start of leg synch. + bool m_StrideStart; + // Times the stride to see if it is taking too long and needs restart + Timer m_StrideTimer; + // For timing throws + Timer m_ThrowTmr; + // The duration it takes this AHuman to fully charge a throw. + long m_ThrowPrepTime; + Timer m_SharpAimRevertTimer; //!< For timing the transition from sharp aim back to regular aim. + float m_FGArmFlailScalar; //!< The rate at which this AHuman's FG Arm follows the the bodily rotation. Best to keep this at 0 so it doesn't complicate aiming. + float m_BGArmFlailScalar; //!< The rate at which this AHuman's BG Arm follows the the bodily rotation. Set to a negative value for a "counterweight" effect. + Timer m_EquipHUDTimer; //!< Timer for showing the name of any newly equipped Device. + std::array m_WalkAngle; //!< An array of rot angle targets for different movement states. + Vector m_WalkPathOffset; + float m_ArmSwingRate; //!< Controls the rate at which this AHuman's Arms follow the movement of its Legs while they're not holding device(s). + float m_DeviceArmSwayRate; //!< Controls the rate at which this AHuman's Arms follow the movement of its Legs while they're holding device(s). One-handed devices sway half as much as two-handed ones. Defaults to three quarters of Arm swing rate. + + //////////////// + // AI States + + enum DeviceHandlingState { + STILL = 0, + POINTING, + SCANNING, + AIMING, + FIRING, + THROWING, + DIGGING + }; + + enum SweepState { + NOSWEEP = 0, + SWEEPINGUP, + SWEEPUPPAUSE, + SWEEPINGDOWN, + SWEEPDOWNPAUSE + }; + + enum DigState { + NOTDIGGING = 0, + PREDIG, + STARTDIG, + TUNNELING, + FINISHINGDIG, + PAUSEDIGGER + }; + + enum JumpState { + NOTJUMPING = 0, + FORWARDJUMP, + PREUPJUMP, + UPJUMP, + APEXJUMP, + LANDJUMP + }; + + // What the AI is doing with its held devices + DeviceHandlingState m_DeviceState; + // What we are doing with a device sweeping + SweepState m_SweepState; + // The current digging state + DigState m_DigState; + // The current jumping state + JumpState m_JumpState; + // Jumping target, overshoot this and the jump is completed + Vector m_JumpTarget; + // Jumping left or right + bool m_JumpingRight; + // AI is crawling + bool m_Crawling; + // The position of the end of the current tunnel being dug. When it is reached, digging can stop. + Vector m_DigTunnelEndPos; + // The center angle (in rads) for the sweeping motion done duing scannign and digging + float m_SweepCenterAimAngle; + // The range to each direction of the center that the sweeping motion will be done in + float m_SweepRange; + // The absolute coordinates of the last detected gold deposits + Vector m_DigTarget; + // Timer for how long to be shooting at a seen enemy target + Timer m_FireTimer; + // Timer for how long to be shooting at a seen enemy target + Timer m_SweepTimer; + // Timer for how long to be patrolling in a direction + Timer m_PatrolTimer; + // Timer for how long to be firing the jetpack in a direction + Timer m_JumpTimer; #pragma region Event Handling - /// - /// Event listener to be run while this AHuman's PieMenu is opened. - /// - /// The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int WhilePieMenuOpenListener(const PieMenu *pieMenu) override; + /// + /// Event listener to be run while this AHuman's PieMenu is opened. + /// + /// The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int WhilePieMenuOpenListener(const PieMenu* pieMenu) override; #pragma endregion + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AHuman, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this AHuman, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. - // Disallow the use of some implicit methods. - AHuman(const AHuman &reference) = delete; - AHuman & operator=(const AHuman &rhs) = delete; + void Clear(); -}; + // Disallow the use of some implicit methods. + AHuman(const AHuman& reference) = delete; + AHuman& operator=(const AHuman& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/Activity.cpp b/Source/Entities/Activity.cpp index 795ea63532..4166e47a37 100644 --- a/Source/Entities/Activity.cpp +++ b/Source/Entities/Activity.cpp @@ -22,9 +22,9 @@ namespace RTE { AbstractClassInfo(Activity, Entity); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void Activity::Clear() { + void Activity::Clear() { m_ActivityState = ActivityState::NotStarted; m_Paused = false; m_AllowsUserSaving = false; @@ -70,7 +70,7 @@ void Activity::Clear() { m_SavedValues.Reset(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Activity::Create() { if (Entity::Create() < 0) { @@ -78,15 +78,14 @@ void Activity::Clear() { } for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { std::string teamNum = std::to_string(team + 1); - m_TeamIcons[team] = *dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Team " + teamNum + " Default")); - + m_TeamIcons[team] = *dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Team " + teamNum + " Default")); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::Create(const Activity &reference) { + int Activity::Create(const Activity& reference) { Entity::Create(reference); m_ActivityState = reference.m_ActivityState; @@ -121,7 +120,9 @@ void Activity::Clear() { m_TeamFunds[team] = reference.m_TeamFunds[team]; m_FundsChanged[team] = reference.m_FundsChanged[team]; m_TeamNames[team] = reference.m_TeamNames[team]; - if (reference.m_TeamIcons[team].GetFrameCount() > 0) { m_TeamIcons[team] = reference.m_TeamIcons[team]; } + if (reference.m_TeamIcons[team].GetFrameCount() > 0) { + m_TeamIcons[team] = reference.m_TeamIcons[team]; + } m_TeamDeaths[team] = reference.m_TeamDeaths[team]; m_TeamAISkillLevels[team] = reference.m_TeamAISkillLevels[team]; } @@ -131,11 +132,11 @@ void Activity::Clear() { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::ReadProperty(const std::string_view &propName, Reader &reader) { + int Activity::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("Description", { reader >> m_Description; }); MatchProperty("SceneName", { reader >> m_SceneName; }); MatchProperty("MaxPlayerSupport", { reader >> m_MaxPlayerSupport; }); @@ -154,7 +155,9 @@ void Activity::Clear() { if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { m_Team[playerTeam] = team; m_IsActive[playerTeam] = true; - if (!m_TeamActive[team]) { m_TeamCount++; } + if (!m_TeamActive[team]) { + m_TeamCount++; + } m_TeamActive[team] = true; } else { m_IsActive[playerTeam] = false; @@ -162,7 +165,7 @@ void Activity::Clear() { break; } } - }); + }); MatchForwards("Player1IsHuman") MatchForwards("Player2IsHuman") MatchForwards("Player3IsHuman") MatchProperty("Player4IsHuman", { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { std::string playerNum = std::to_string(player + 1); @@ -171,18 +174,20 @@ void Activity::Clear() { break; } } - }); + }); MatchForwards("Team1Name") MatchForwards("Team2Name") MatchForwards("Team3Name") MatchProperty("Team4Name", { for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) { std::string teamNum = std::to_string(team + 1); if (propName == "Team" + teamNum + "Name") { reader >> m_TeamNames[team]; - if (!m_TeamActive[team]) { m_TeamCount++; } + if (!m_TeamActive[team]) { + m_TeamCount++; + } m_TeamActive[team] = true; break; } } - }); + }); MatchForwards("Team1Icon") MatchForwards("Team2Icon") MatchForwards("Team3Icon") MatchProperty("Team4Icon", { for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) { std::string teamNum = std::to_string(team + 1); @@ -191,7 +196,7 @@ void Activity::Clear() { break; } } - }); + }); MatchForwards("Team1Funds") MatchForwards("Team2Funds") MatchForwards("Team3Funds") MatchProperty("Team4Funds", { for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; team++) { std::string teamNum = std::to_string(team + 1); @@ -200,7 +205,7 @@ void Activity::Clear() { break; } } - }); + }); MatchForwards("TeamFundsShareOfPlayer1") MatchForwards("TeamFundsShareOfPlayer2") MatchForwards("TeamFundsShareOfPlayer3") MatchProperty("TeamFundsShareOfPlayer4", { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { std::string playerNum = std::to_string(player + 1); @@ -209,7 +214,7 @@ void Activity::Clear() { break; } } - }); + }); MatchForwards("FundsContributionOfPlayer1") MatchForwards("FundsContributionOfPlayer2") MatchForwards("FundsContributionOfPlayer3") MatchProperty("FundsContributionOfPlayer4", { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { std::string playerNum = std::to_string(player + 1); @@ -224,9 +229,9 @@ void Activity::Clear() { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::Save(Writer &writer) const { + int Activity::Save(Writer& writer) const { Entity::Save(writer); writer.NewProperty("Description"); @@ -283,7 +288,7 @@ void Activity::Clear() { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Activity::Start() { // Reseed the RNG for determinism @@ -330,9 +335,9 @@ void Activity::Clear() { } g_CameraMan.ResetAllScreenShake(); - //TODO currently this sets brains to players arbitrarily. We should save information on which brain is for which player in the scene so we can set them properly! + // TODO currently this sets brains to players arbitrarily. We should save information on which brain is for which player in the scene so we can set them properly! if (m_IsActive[player]) { - if (Actor *brain = g_MovableMan.GetUnassignedBrain(GetTeamOfPlayer(player))) { + if (Actor* brain = g_MovableMan.GetUnassignedBrain(GetTeamOfPlayer(player))) { SetPlayerBrain(brain, player); } } @@ -341,7 +346,7 @@ void Activity::Clear() { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Activity::End() { g_AudioMan.FinishIngameLoopingSounds(); @@ -352,7 +357,7 @@ void Activity::Clear() { m_ActivityState = ActivityState::Over; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Activity::SetupPlayers() { m_TeamCount = 0; @@ -365,7 +370,9 @@ void Activity::Clear() { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (m_IsActive[player]) { m_PlayerCount++; - if (!m_TeamActive[m_Team[player]]) { m_TeamCount++; } + if (!m_TeamActive[m_Team[player]]) { + m_TeamCount++; + } m_TeamActive[m_Team[player]] = true; } @@ -373,14 +380,16 @@ void Activity::Clear() { int screenIndex = -1; if (m_IsActive[player] && m_IsHuman[player]) { for (int playerToCheck = Players::PlayerOne; playerToCheck < Players::MaxPlayerCount && playerToCheck <= player; ++playerToCheck) { - if (m_IsActive[playerToCheck] && m_IsHuman[playerToCheck]) { screenIndex++; } + if (m_IsActive[playerToCheck] && m_IsHuman[playerToCheck]) { + screenIndex++; + } } } m_PlayerScreen[player] = screenIndex; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Activity::DeactivatePlayer(int playerToDeactivate) { if (playerToDeactivate < Players::PlayerOne || playerToDeactivate >= Players::MaxPlayerCount || !m_IsActive[playerToDeactivate] || !m_TeamActive[m_Team[playerToDeactivate]]) { @@ -406,9 +415,9 @@ void Activity::Clear() { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::AddPlayer(int playerToAdd, bool isHuman, int team, float funds, const Icon *teamIcon) { + int Activity::AddPlayer(int playerToAdd, bool isHuman, int team, float funds, const Icon* teamIcon) { if (playerToAdd < Players::PlayerOne || playerToAdd >= Players::MaxPlayerCount || team < Teams::TeamOne || team >= Teams::MaxTeamCount) { return m_PlayerCount; } @@ -421,15 +430,21 @@ void Activity::Clear() { float newRatio = 1.0F + (funds / totalFunds); for (int teamPlayer = Players::PlayerOne; teamPlayer < Players::MaxPlayerCount; ++teamPlayer) { - if (m_IsActive[teamPlayer] && m_Team[teamPlayer] == team) { m_TeamFundsShare[teamPlayer] /= newRatio; } + if (m_IsActive[teamPlayer] && m_Team[teamPlayer] == team) { + m_TeamFundsShare[teamPlayer] /= newRatio; + } } m_TeamFundsShare[playerToAdd] = funds / totalFunds; } m_TeamActive[team] = true; - if (teamIcon) { SetTeamIcon(team, *teamIcon); } + if (teamIcon) { + SetTeamIcon(team, *teamIcon); + } - if (!m_IsActive[playerToAdd]) { m_PlayerCount++; } + if (!m_IsActive[playerToAdd]) { + m_PlayerCount++; + } m_IsActive[playerToAdd] = true; m_IsHuman[playerToAdd] = isHuman; @@ -440,7 +455,7 @@ void Activity::Clear() { return m_PlayerCount; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Activity::ClearPlayers(bool resetFunds) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { @@ -455,23 +470,27 @@ void Activity::Clear() { for (int team = Teams::TeamOne; team < Teams::MaxTeamCount; ++team) { m_TeamActive[team] = false; - if (resetFunds) { m_TeamFunds[team] = 0; } + if (resetFunds) { + m_TeamFunds[team] = 0; + } } m_PlayerCount = m_TeamCount = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Activity::GetHumanCount() const { int humans = 0; for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - if (m_IsActive[player] && m_IsHuman[player]) { humans++; } + if (m_IsActive[player] && m_IsHuman[player]) { + humans++; + } } return humans; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Activity::SetTeamOfPlayer(int player, int team) { if (team < Teams::TeamOne || team >= Teams::MaxTeamCount || player < Players::PlayerOne || player >= Players::MaxPlayerCount) { @@ -483,7 +502,7 @@ void Activity::Clear() { m_IsActive[player] = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Activity::PlayerOfScreen(int screen) const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { @@ -494,7 +513,7 @@ void Activity::Clear() { return Players::NoPlayer; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string Activity::GetTeamName(int whichTeam) const { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { @@ -503,7 +522,7 @@ void Activity::Clear() { return ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Activity::IsHumanTeam(int whichTeam) const { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { @@ -516,17 +535,19 @@ void Activity::Clear() { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Activity::PlayersInTeamCount(int team) const { int count = 0; for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - if (m_IsActive[player] && m_Team[player] == team) { count++; } + if (m_IsActive[player] && m_Team[player] == team) { + count++; + } } return count; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Activity::ChangeTeamFunds(float howMuch, int whichTeam) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { @@ -534,13 +555,15 @@ void Activity::Clear() { m_FundsChanged[whichTeam] = true; if (IsHumanTeam(whichTeam)) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { - if (m_Team[player] == whichTeam) { g_GUISound.FundsChangedSound()->Play(player); } + if (m_Team[player] == whichTeam) { + g_GUISound.FundsChangedSound()->Play(player); + } } } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Activity::TeamFundsChanged(int whichTeam) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { @@ -551,7 +574,7 @@ void Activity::Clear() { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Activity::UpdatePlayerFundsContribution(int player, float newFunds) { if (player < Players::PlayerOne || player >= Players::MaxPlayerCount || !m_IsActive[player] || !m_TeamActive[m_Team[player]]) { @@ -567,17 +590,21 @@ void Activity::Clear() { // Tally up all the funds of all players on this guy's team for (int playerOnTeam = Players::PlayerOne; playerOnTeam < Players::MaxPlayerCount; ++playerOnTeam) { - if (m_IsActive[playerOnTeam] && m_Team[playerOnTeam] == m_Team[player]) { m_TeamFunds[m_Team[player]] += m_FundsContribution[playerOnTeam]; } + if (m_IsActive[playerOnTeam] && m_Team[playerOnTeam] == m_Team[player]) { + m_TeamFunds[m_Team[player]] += m_FundsContribution[playerOnTeam]; + } } // Now that we have the updated total, update the shares of all team players for (int playerOnTeam = Players::PlayerOne; playerOnTeam < Players::MaxPlayerCount; ++playerOnTeam) { - if (m_IsActive[playerOnTeam] && m_Team[playerOnTeam] == m_Team[player]) { m_TeamFundsShare[playerOnTeam] = m_FundsContribution[playerOnTeam] / m_TeamFunds[m_Team[player]]; } + if (m_IsActive[playerOnTeam] && m_Team[playerOnTeam] == m_Team[player]) { + m_TeamFundsShare[playerOnTeam] = m_FundsContribution[playerOnTeam] / m_TeamFunds[m_Team[player]]; + } } } return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Activity::GetPlayerFundsShare(int player) const { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { @@ -586,17 +613,19 @@ void Activity::Clear() { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::SetPlayerBrain(Actor *newBrain, int player) { + void Activity::SetPlayerBrain(Actor* newBrain, int player) { if ((player >= Players::PlayerOne || player < Players::MaxPlayerCount) && newBrain) { - if (newBrain->GetTeam() != m_Team[player]) { newBrain->SetTeam(m_Team[player]); } + if (newBrain->GetTeam() != m_Team[player]) { + newBrain->SetTeam(m_Team[player]); + } m_HadBrain[player] = true; } m_Brain[player] = newBrain; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Activity::AnyBrainWasEvacuated() const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { @@ -607,9 +636,9 @@ void Activity::Clear() { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::IsAssignedBrain(Actor *actor) const { + bool Activity::IsAssignedBrain(Actor* actor) const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (actor == m_Brain[player]) { return true; @@ -618,9 +647,9 @@ void Activity::Clear() { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::IsBrainOfWhichPlayer(Actor *actor) const { + int Activity::IsBrainOfWhichPlayer(Actor* actor) const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (actor == m_Brain[player]) { return player; @@ -629,9 +658,9 @@ void Activity::Clear() { return Players::NoPlayer; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::IsOtherPlayerBrain(Actor *actor, int player) const { + bool Activity::IsOtherPlayerBrain(Actor* actor, int player) const { for (int playerToCheck = Players::PlayerOne; playerToCheck < Players::MaxPlayerCount; ++playerToCheck) { if (m_IsActive[playerToCheck] && playerToCheck != player && actor == m_Brain[playerToCheck]) { return true; @@ -640,7 +669,7 @@ void Activity::Clear() { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string Activity::GetDifficultyString(int difficulty) { if (difficulty <= DifficultySetting::CakeDifficulty) { @@ -658,7 +687,7 @@ void Activity::Clear() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string Activity::GetAISkillString(int skill) { if (skill < AISkillSetting::InferiorSkill) { @@ -672,7 +701,7 @@ void Activity::Clear() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Activity::GetTeamAISkill(int team) const { if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { @@ -691,14 +720,14 @@ void Activity::Clear() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Activity::ReassignSquadLeader(const int player, const int team) { if (m_ControlledActor[player]->GetAIMode() == Actor::AIMODE_SQUAD) { MOID leaderID = m_ControlledActor[player]->GetAIMOWaypointID(); if (leaderID != g_NoMOID) { - Actor *actor = g_MovableMan.GetNextTeamActor(team, m_ControlledActor[player]); + Actor* actor = g_MovableMan.GetNextTeamActor(team, m_ControlledActor[player]); do { // Set the controlled actor as new leader if actor follow the old leader, and not player controlled and not brain @@ -715,8 +744,10 @@ void Activity::Clear() { if (m_ControlledActor[player]->GetAIMode() == Actor::AIMODE_GOTO) { // Copy the old leaders move orders if (actor->GetAIMOWaypointID() != g_NoMOID) { - const MovableObject *targetMO = g_MovableMan.GetMOFromID(actor->GetAIMOWaypointID()); - if (targetMO) { m_ControlledActor[player]->AddAIMOWaypoint(targetMO); } + const MovableObject* targetMO = g_MovableMan.GetMOFromID(actor->GetAIMOWaypointID()); + if (targetMO) { + m_ControlledActor[player]->AddAIMOWaypoint(targetMO); + } } else if ((actor->GetLastAIWaypoint() - actor->GetPos()).GetLargest() > 1) { m_ControlledActor[player]->AddAISceneWaypoint(actor->GetLastAIWaypoint()); } @@ -733,9 +764,9 @@ void Activity::Clear() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::SwitchToActor(Actor *actor, int player, int team) { + bool Activity::SwitchToActor(Actor* actor, int player, int team) { if (team < Teams::TeamOne || team >= Teams::MaxTeamCount || player < Players::PlayerOne || player >= Players::MaxPlayerCount || !m_IsHuman[player]) { return false; } @@ -747,7 +778,7 @@ void Activity::Clear() { return false; } - Actor *preSwitchActor = (m_ControlledActor[player] && g_MovableMan.IsActor(m_ControlledActor[player])) ? m_ControlledActor[player] : nullptr; + Actor* preSwitchActor = (m_ControlledActor[player] && g_MovableMan.IsActor(m_ControlledActor[player])) ? m_ControlledActor[player] : nullptr; if (preSwitchActor && preSwitchActor->GetController()->GetPlayer() == player) { preSwitchActor->SetControllerMode(Controller::CIM_AI); preSwitchActor->GetController()->SetDisabled(false); @@ -760,7 +791,7 @@ void Activity::Clear() { m_ControlledActor[player]->SetControllerMode(Controller::CIM_PLAYER, player); m_ControlledActor[player]->GetController()->SetDisabled(false); - SoundContainer *actorSwitchSoundToPlay = (m_ControlledActor[player] == m_Brain[player]) ? g_GUISound.BrainSwitchSound() : g_GUISound.ActorSwitchSound(); + SoundContainer* actorSwitchSoundToPlay = (m_ControlledActor[player] == m_Brain[player]) ? g_GUISound.BrainSwitchSound() : g_GUISound.ActorSwitchSound(); actorSwitchSoundToPlay->Play(player); // If out of frame from the POV of the preswitch actor, play the camera travel noise @@ -775,11 +806,11 @@ void Activity::Clear() { return true; } -////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// void Activity::LoseControlOfActor(int player) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { - if (Actor *actor = m_ControlledActor[player]; actor && g_MovableMan.IsActor(actor)) { + if (Actor* actor = m_ControlledActor[player]; actor && g_MovableMan.IsActor(actor)) { actor->SetControllerMode(Controller::CIM_AI); actor->GetController()->SetDisabled(false); } @@ -789,9 +820,9 @@ void Activity::Clear() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::HandleCraftEnteringOrbit(ACraft *orbitedCraft) { + void Activity::HandleCraftEnteringOrbit(ACraft* orbitedCraft) { if (!orbitedCraft) { return; } @@ -805,7 +836,7 @@ void Activity::Clear() { if (g_MetaMan.GameInProgress()) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { if (GetTeamOfPlayer(static_cast(player)) == orbitedCraftTeam) { - const MetaPlayer *metaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + const MetaPlayer* metaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); if (metaPlayer) { foreignCostMult = metaPlayer->GetForeignCostMultiplier(); nativeCostMult = metaPlayer->GetNativeCostMultiplier(); @@ -817,7 +848,7 @@ void Activity::Clear() { float totalValue = orbitedCraft->GetTotalValue(0, foreignCostMult, nativeCostMult); std::string craftText = "Returned craft"; - if (!orbitedCraft->IsInventoryEmpty()) { + if (!orbitedCraft->IsInventoryEmpty()) { craftText += " + cargo"; } if (totalValue > 0.0F) { @@ -845,7 +876,7 @@ void Activity::Clear() { m_TeamDeaths[orbitedCraftTeam]--; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Activity::GetBrainCount(bool getForHuman) const { int brainCount = 0; @@ -864,16 +895,16 @@ void Activity::Clear() { return brainCount; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::SwitchToPrevOrNextActor(bool nextActor, int player, int team, const Actor *actorToSkip) { + void Activity::SwitchToPrevOrNextActor(bool nextActor, int player, int team, const Actor* actorToSkip) { if (team < Teams::TeamOne || team >= Teams::MaxTeamCount || player < Players::PlayerOne || player >= Players::MaxPlayerCount || !m_IsHuman[player]) { return; } - Actor *preSwitchActor = m_ControlledActor[player]; - Actor *actorToSwitchTo = nextActor ? g_MovableMan.GetNextTeamActor(team, preSwitchActor) : g_MovableMan.GetPrevTeamActor(team, preSwitchActor); - const Actor *firstAttemptedSwitchActor = nullptr; + Actor* preSwitchActor = m_ControlledActor[player]; + Actor* actorToSwitchTo = nextActor ? g_MovableMan.GetNextTeamActor(team, preSwitchActor) : g_MovableMan.GetPrevTeamActor(team, preSwitchActor); + const Actor* firstAttemptedSwitchActor = nullptr; bool actorSwitchSucceedOrTriedAllActors = false; while (!actorSwitchSucceedOrTriedAllActors) { @@ -891,23 +922,29 @@ void Activity::Clear() { actorSwitchSucceedOrTriedAllActors = (actorToSwitchTo != actorToSkip && SwitchToActor(actorToSwitchTo, player, team)); } firstAttemptedSwitchActor = firstAttemptedSwitchActor ? firstAttemptedSwitchActor : actorToSwitchTo; - if (!actorSwitchSucceedOrTriedAllActors) { actorToSwitchTo = nextActor ? g_MovableMan.GetNextTeamActor(team, actorToSwitchTo) : g_MovableMan.GetPrevTeamActor(team, actorToSwitchTo); } + if (!actorSwitchSucceedOrTriedAllActors) { + actorToSwitchTo = nextActor ? g_MovableMan.GetNextTeamActor(team, actorToSwitchTo) : g_MovableMan.GetPrevTeamActor(team, actorToSwitchTo); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Activity::Update() { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - if (m_MessageTimer[player].IsPastSimMS(5000)) { g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); } - if (m_IsActive[player]) { m_PlayerController[player].Update(); } + if (m_MessageTimer[player].IsPastSimMS(5000)) { + g_FrameMan.ClearScreenText(ScreenOfPlayer(player)); + } + if (m_IsActive[player]) { + m_PlayerController[player].Update(); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Activity::CanBeUserSaved() const { - if (const Scene *scene = g_SceneMan.GetScene(); (scene && scene->IsMetagameInternal()) || g_MetaMan.GameInProgress()) { + if (const Scene* scene = g_SceneMan.GetScene(); (scene && scene->IsMetagameInternal()) || g_MetaMan.GameInProgress()) { return false; } @@ -917,4 +954,4 @@ void Activity::Clear() { return m_AllowsUserSaving; } -} +} // namespace RTE diff --git a/Source/Entities/Activity.h b/Source/Entities/Activity.h index 0daac3a9ee..9cf3a80c54 100644 --- a/Source/Entities/Activity.h +++ b/Source/Entities/Activity.h @@ -16,7 +16,6 @@ namespace RTE { class Activity : public Entity { public: - SerializableOverrideMethods; ClassInfoGetters; @@ -104,7 +103,7 @@ namespace RTE { /// /// A reference to the Activity to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Activity &reference); + int Create(const Activity& reference); #pragma endregion #pragma region Destruction @@ -117,7 +116,12 @@ namespace RTE { /// Destroys and resets (through Clear()) the Activity object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + Entity::Destroy(); + } + Clear(); + } #pragma endregion #pragma region Getters and Setters @@ -181,7 +185,7 @@ namespace RTE { /// The Scene to check if it supports this Activity. Ownership is NOT transferred! /// How many teams we're checking for. Some scenes may support and Activity but only for a limited number of teams. If -1, not applicable. /// Whether the Scene has the right stuff. - virtual bool SceneIsCompatible(Scene *scene, int teams = -1) { return scene && teams <= m_MinTeamsRequired; } + virtual bool SceneIsCompatible(Scene* scene, int teams = -1) { return scene && teams <= m_MinTeamsRequired; } /// /// Shows in which stage of the Campaign this appears. @@ -190,7 +194,7 @@ namespace RTE { int GetInCampaignStage() const { return m_InCampaignStage; } /// - /// + /// /// Sets in which stage of the Campaign this appears. /// /// The new stage to set. -1 means it doesn't appear in the campaign. @@ -244,14 +248,14 @@ namespace RTE { /// A pointer to a screen-sized BITMAP to draw on. /// The absolute position of the target bitmap's upper left corner in the scene. /// Which screen's GUI to draw onto the bitmap. - virtual void DrawGUI(BITMAP *targetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0) {} + virtual void DrawGUI(BITMAP* targetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0) {} /// /// Draws this Activity's current graphical representation to a BITMAP of choice. This includes all game-related graphics. /// /// A pointer to a BITMAP to draw on. /// The absolute position of the target bitmap's upper left corner in the scene. - virtual void Draw(BITMAP *targetBitmap, const Vector &targetPos = Vector()) {} + virtual void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector()) {} #pragma endregion #pragma region Player Handling @@ -284,7 +288,7 @@ namespace RTE { /// How many funds this player contributes to its Team's total funds. /// The team flag icon of this player. Ownership is NOT transferred! /// The new total number of active players in the current game. - int AddPlayer(int playerToAdd, bool isHuman, int team, float funds, const Icon *teamIcon = 0); + int AddPlayer(int playerToAdd, bool isHuman, int team, float funds, const Icon* teamIcon = 0); /// /// Sets all players as not active in the current Activity. @@ -357,14 +361,18 @@ namespace RTE { /// Resets the message timer for one player. /// /// The player to reset the message timer for. - void ResetMessageTimer(int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_MessageTimer[player].Reset(); } } + void ResetMessageTimer(int player = 0) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { + m_MessageTimer[player].Reset(); + } + } /// /// Gets a pointer to the GUI controller of the specified player. /// /// Which player to get the Controller of. /// A pointer to the player's Controller. Ownership is NOT transferred! - Controller * GetPlayerController(int player = 0) { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? &m_PlayerController[player] : nullptr; } + Controller* GetPlayerController(int player = 0) { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? &m_PlayerController[player] : nullptr; } #pragma endregion #pragma region Team Handling @@ -386,21 +394,29 @@ namespace RTE { /// /// Which team to set the name of. 0 = first team. /// The name to set it to. - void SetTeamName(int whichTeam, const std::string &newName) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { m_TeamNames[whichTeam] = newName; } } + void SetTeamName(int whichTeam, const std::string& newName) { + if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { + m_TeamNames[whichTeam] = newName; + } + } /// /// Gets the Icon of a specific team. /// /// Which team to get the Icon of. 0 = first team. /// The current Icon of that team. - const Icon * GetTeamIcon(int whichTeam = 0) const { return (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) ? &m_TeamIcons[whichTeam] : nullptr; } + const Icon* GetTeamIcon(int whichTeam = 0) const { return (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) ? &m_TeamIcons[whichTeam] : nullptr; } /// /// Sets the Icon of a specific team. /// /// Which team to set the Icon of. 0 = first team. /// The Icon to set it to. - void SetTeamIcon(int whichTeam, const Icon &newIcon) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { m_TeamIcons[whichTeam] = newIcon; } } + void SetTeamIcon(int whichTeam, const Icon& newIcon) { + if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { + m_TeamIcons[whichTeam] = newIcon; + } + } /// /// Indicates whether a specific team is active in the current game. @@ -413,7 +429,11 @@ namespace RTE { /// Sets the given team as active, even if it shouldn't be considered as such normally. Useful for Activities that don't want to define/show all used teams. /// /// The team to force as active. - void ForceSetTeamAsActive(int team) { if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { m_TeamActive[team] = true; } } + void ForceSetTeamAsActive(int team) { + if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { + m_TeamActive[team] = true; + } + } /// /// Indicates whether a team is player controlled or not. @@ -458,7 +478,11 @@ namespace RTE { /// /// Which team to set the fund count for. 0 = first team. /// A float with the funds tally for the requested team. - void SetTeamFunds(float newFunds, int whichTeam = 0) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { m_TeamFunds[whichTeam] = newFunds; } } + void SetTeamFunds(float newFunds, int whichTeam = 0) { + if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { + m_TeamFunds[whichTeam] = newFunds; + } + } /// /// Changes a team's funds level by a certain amount. @@ -515,14 +539,14 @@ namespace RTE { /// /// Which player to get the brain actor for. /// A pointer to the Brain Actor. Ownership is NOT transferred! - Actor * GetPlayerBrain(int player = 0) const { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_Brain[player] : nullptr; } + Actor* GetPlayerBrain(int player = 0) const { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_Brain[player] : nullptr; } /// /// Sets the current Brain actor for a specific player. /// /// A pointer to the new brain Actor. Ownership is NOT transferred! /// Which team to set the brain actor for. - void SetPlayerBrain(Actor *newBrain, int player = 0); + void SetPlayerBrain(Actor* newBrain, int player = 0); /// /// Shows whether a specific player ever had a Brain yet. @@ -536,7 +560,10 @@ namespace RTE { /// /// Which player to set whether he had a Brain or not. /// Whether he should be flagged as having had a Brain. - void SetPlayerHadBrain(int player, bool hadBrain = true) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_HadBrain[player] = hadBrain; } + void SetPlayerHadBrain(int player, bool hadBrain = true) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) + m_HadBrain[player] = hadBrain; + } /// /// Shows whether a specific player's Brain was evacuated into orbit so far. @@ -550,7 +577,11 @@ namespace RTE { /// /// Which player to check whether their Brain was evacuated. /// Whether it was evacuated yet. - void SetBrainEvacuated(int player = 0, bool evacuated = true) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_BrainEvacuated[player] = evacuated; } } + void SetBrainEvacuated(int player = 0, bool evacuated = true) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { + m_BrainEvacuated[player] = evacuated; + } + } /// /// Shows whether ANY player evacuated their Brain. @@ -563,14 +594,14 @@ namespace RTE { /// /// Which Actor to check for player braininess. /// Whether any player's Brain or not. - bool IsAssignedBrain(Actor *actor) const; + bool IsAssignedBrain(Actor* actor) const; /// /// Shows which player has a specific actor as a Brain, if any. /// /// Which Actor to check for player braininess. /// Which player has this assigned as a Brain, if any. - int IsBrainOfWhichPlayer(Actor *actor) const; + int IsBrainOfWhichPlayer(Actor* actor) const; /// /// Shows whether the passed in actor is the Brain of any other player. @@ -578,7 +609,7 @@ namespace RTE { /// Which Actor to check for other player braininess. /// From which player's perspective to check. /// Whether other player's Brain or not. - bool IsOtherPlayerBrain(Actor *actor, int player) const; + bool IsOtherPlayerBrain(Actor* actor, int player) const; #pragma endregion #pragma region Difficulty Handling @@ -622,7 +653,11 @@ namespace RTE { /// /// The team to set for. /// AI skill level, 1-100. - void SetTeamAISkill(int team, int skill) { if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { m_TeamAISkillLevels[team] = Limit(skill, AISkillSetting::UnfairSkill, AISkillSetting::MinSkill); } } + void SetTeamAISkill(int team, int skill) { + if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { + m_TeamAISkillLevels[team] = Limit(skill, AISkillSetting::UnfairSkill, AISkillSetting::MinSkill); + } + } #pragma endregion #pragma region Actor Handling @@ -631,7 +666,7 @@ namespace RTE { /// /// Which player to get the controlled actor of. /// A pointer to the controlled Actor. Ownership is NOT transferred! 0 If no actor is currently controlled by this player. - Actor * GetControlledActor(int player = 0) { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_ControlledActor[player] : nullptr; } + Actor* GetControlledActor(int player = 0) { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_ControlledActor[player] : nullptr; } /// /// Makes the player's ControlledActor the leader of any squad it is a member of. @@ -647,7 +682,7 @@ namespace RTE { /// Player to force for. /// Which team to switch to next actor on. /// Whether the focus switch was successful or not. - virtual bool SwitchToActor(Actor *actor, int player = 0, int team = 0); + virtual bool SwitchToActor(Actor* actor, int player = 0, int team = 0); /// /// Forces the Activity to focus player control to the previous Actor of a specific team, other than the current one focused on. @@ -655,7 +690,7 @@ namespace RTE { /// Player to force for. /// Which team to switch to next Actor on. /// An Actor pointer to skip in the sequence. - virtual void SwitchToPrevActor(int player, int team, Actor *actorToSkip = 0) { SwitchToPrevOrNextActor(false, player, team, actorToSkip); } + virtual void SwitchToPrevActor(int player, int team, Actor* actorToSkip = 0) { SwitchToPrevOrNextActor(false, player, team, actorToSkip); } /// /// Forces the Activity to focus player control to the next Actor of a specific team, other than the current one focused on. @@ -663,7 +698,7 @@ namespace RTE { /// Player to force for. /// Which team to switch to next Actor on. /// An Actor pointer to skip in the sequence. - virtual void SwitchToNextActor(int player, int team, Actor *actorToSkip = 0) { SwitchToPrevOrNextActor(true, player, team, actorToSkip); } + virtual void SwitchToNextActor(int player, int team, Actor* actorToSkip = 0) { SwitchToPrevOrNextActor(true, player, team, actorToSkip); } /// /// Forces player to lose control of the currently selected Actor, as if it had died. @@ -675,7 +710,7 @@ namespace RTE { /// Handles when an ACraft has left the game scene and entered orbit, though does not delete it. Ownership is NOT transferred, as the ACraft's inventory is just 'unloaded'. /// /// The ACraft instance that entered orbit. Ownership is NOT transferred! - virtual void HandleCraftEnteringOrbit (ACraft *orbitedCraft); + virtual void HandleCraftEnteringOrbit(ACraft* orbitedCraft); #pragma endregion #pragma region Save and Load Handling @@ -702,30 +737,29 @@ namespace RTE { /// /// The key of the saved string. /// The string to save. - void SaveString(const std::string &key, const std::string &value) { m_SavedValues.SaveString(key, value); }; + void SaveString(const std::string& key, const std::string& value) { m_SavedValues.SaveString(key, value); }; /// /// Loads and returns a previously saved string. /// /// The key of the string to load. - const std::string & LoadString(const std::string &key) { return m_SavedValues.LoadString(key); }; + const std::string& LoadString(const std::string& key) { return m_SavedValues.LoadString(key); }; /// /// Saves a number which will be stored in our ini. /// /// The key of the saved number. /// The number to save. - void SaveNumber(const std::string &key, float value) { m_SavedValues.SaveNumber(key, value); }; + void SaveNumber(const std::string& key, float value) { m_SavedValues.SaveNumber(key, value); }; /// /// Loads and returns a previously saved number. /// /// The key of the string to load. - float LoadNumber(const std::string &key) { return m_SavedValues.LoadNumber(key); }; + float LoadNumber(const std::string& key) { return m_SavedValues.LoadNumber(key); }; #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. ActivityState m_ActivityState; //!< Current state of this Activity. @@ -765,11 +799,11 @@ namespace RTE { bool m_FundsChanged[Teams::MaxTeamCount]; //!< Whether the team funds have changed during the current frame. float m_FundsContribution[Players::MaxPlayerCount]; //!< How much this player contributed to his team's funds at the start of the Activity. - Actor *m_Brain[Players::MaxPlayerCount]; //!< The Brain of each player. Not owned! + Actor* m_Brain[Players::MaxPlayerCount]; //!< The Brain of each player. Not owned! bool m_HadBrain[Players::MaxPlayerCount]; //!< Whether each player has yet had a Brain. If not, then their Activity doesn't end if no brain is found. bool m_BrainEvacuated[Players::MaxPlayerCount]; //!< Whether a player has evacuated his Brain into orbit. - Actor *m_ControlledActor[Players::MaxPlayerCount]; //!< Currently controlled actor, not owned. + Actor* m_ControlledActor[Players::MaxPlayerCount]; //!< Currently controlled actor, not owned. Controller m_PlayerController[Players::MaxPlayerCount]; //!< The Controllers of all the players for the GUIs. Timer m_MessageTimer[Players::MaxPlayerCount]; //!< Message timer for each player. @@ -782,7 +816,6 @@ namespace RTE { GenericSavedData m_SavedValues; private: - /// /// Shared method to get the amount of human or AI controlled brains that are left in this Activity. /// @@ -797,7 +830,7 @@ namespace RTE { /// Player to force for. /// Which team to switch to next Actor on. /// An Actor pointer to skip in the sequence. - void SwitchToPrevOrNextActor(bool nextActor, int player, int team, const Actor *skip = 0); + void SwitchToPrevOrNextActor(bool nextActor, int player, int team, const Actor* skip = 0); /// /// Clears all the member variables of this Activity, effectively resetting the members of this abstraction level only. @@ -805,8 +838,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - Activity(const Activity &reference) = delete; - Activity & operator=(const Activity &rhs) = delete; + Activity(const Activity& reference) = delete; + Activity& operator=(const Activity& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/Actor.cpp b/Source/Entities/Actor.cpp index a94e3f94c3..98cdc914b6 100644 --- a/Source/Entities/Actor.cpp +++ b/Source/Entities/Actor.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -41,905 +40,1004 @@ namespace RTE { -ConcreteClassInfo(Actor, MOSRotating, 20); + ConcreteClassInfo(Actor, MOSRotating, 20); -std::vector Actor::m_apNoTeamIcon; -BITMAP *Actor::m_apAIIcons[AIMODE_COUNT]; -std::vector Actor::m_apSelectArrow; -std::vector Actor::m_apAlarmExclamation; -bool Actor::m_sIconsLoaded = false; + std::vector Actor::m_apNoTeamIcon; + BITMAP* Actor::m_apAIIcons[AIMODE_COUNT]; + std::vector Actor::m_apSelectArrow; + std::vector Actor::m_apAlarmExclamation; + bool Actor::m_sIconsLoaded = false; #define ARROWTIME 1000 + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: LuaBindRegister + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Registration function for exposing this' members to a LuaBind module. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Actor, effectively + // resetting the members of this abstraction level only. + + void Actor::Clear() { + m_Controller.Reset(); + m_PlayerControllable = true; + m_BodyHitSound = nullptr; + m_AlarmSound = nullptr; + m_PainSound = nullptr; + m_DeathSound = nullptr; + m_DeviceSwitchSound = nullptr; + m_Status = STABLE; + m_Health = m_PrevHealth = m_MaxHealth = 100.0F; + m_pTeamIcon = nullptr; + m_pControllerIcon = nullptr; + m_LastSecondTimer.Reset(); + m_LastSecondPos.Reset(); + m_RecentMovement.Reset(); + m_TravelImpulseDamage = 750.0F; + m_StableVel.SetXY(15.0F, 25.0F); + m_StableRecoverDelay = 1000; + m_HeartBeat.Reset(); + m_NewControlTmr.Reset(); + m_DeathTmr.Reset(); + m_GoldCarried = 0; + m_GoldPicked = false; + m_AimState = AIMSTILL; + m_AimAngle = 0; + m_AimRange = c_HalfPI; + m_AimDistance = 0; + m_AimTmr.Reset(); + m_SharpAimTimer.Reset(); + m_SharpAimDelay = 250; + m_SharpAimProgress = 0; + m_SharpAimMaxedOut = false; + m_PointingTarget.Reset(); + m_SeenTargetPos.Reset(); + // Set the limit to soemthing reasonable, if the timer is over it, there's no alarm + m_AlarmTimer.SetSimTimeLimitMS(3000); + m_AlarmTimer.SetElapsedSimTimeMS(4000); + m_LastAlarmPos.Reset(); + m_SightDistance = 450.0F; + m_Perceptiveness = 0.5F; + m_PainThreshold = 15.0F; + m_CanRevealUnseen = true; + m_CharHeight = 0; + m_HolsterOffset.Reset(); + m_ReloadOffset.Reset(); + m_ViewPoint.Reset(); + m_Inventory.clear(); + m_MaxInventoryMass = -1.0F; + m_pItemInReach = nullptr; + m_HUDStack = 0; + m_DeploymentID = 0; + m_PassengerSlots = 1; + + m_AIMode = AIMODE_NONE; + m_Waypoints.clear(); + m_DrawWaypoints = false; + m_MoveTarget.Reset(); + m_pMOMoveTarget = nullptr; + m_PrevPathTarget.Reset(); + m_MoveVector.Reset(); + m_MovePath.clear(); + m_UpdateMovePath = true; + m_MoveProximityLimit = 20.0F; + m_AIBaseDigStrength = c_PathFindingDefaultDigStrength; + m_BaseMass = std::numeric_limits::infinity(); + + m_DamageMultiplier = 1.0F; + + m_Organic = false; + m_Mechanical = false; + + m_LimbPushForcesAndCollisionsDisabled = false; + + m_PieMenu.reset(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Actor object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: LuaBindRegister -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Registration function for exposing this' members to a LuaBind module. + int Actor::Create() { + if (MOSRotating::Create() < 0) { + return -1; + } + // Set MO Type. + m_MOType = MovableObject::TypeActor; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Actor, effectively -// resetting the members of this abstraction level only. - -void Actor::Clear() { - m_Controller.Reset(); - m_PlayerControllable = true; - m_BodyHitSound = nullptr; - m_AlarmSound = nullptr; - m_PainSound = nullptr; - m_DeathSound = nullptr; - m_DeviceSwitchSound = nullptr; - m_Status = STABLE; - m_Health = m_PrevHealth = m_MaxHealth = 100.0F; - m_pTeamIcon = nullptr; - m_pControllerIcon = nullptr; - m_LastSecondTimer.Reset(); - m_LastSecondPos.Reset(); - m_RecentMovement.Reset(); - m_TravelImpulseDamage = 750.0F; - m_StableVel.SetXY(15.0F, 25.0F); - m_StableRecoverDelay = 1000; - m_HeartBeat.Reset(); - m_NewControlTmr.Reset(); - m_DeathTmr.Reset(); - m_GoldCarried = 0; - m_GoldPicked = false; - m_AimState = AIMSTILL; - m_AimAngle = 0; - m_AimRange = c_HalfPI; - m_AimDistance = 0; - m_AimTmr.Reset(); - m_SharpAimTimer.Reset(); - m_SharpAimDelay = 250; - m_SharpAimProgress = 0; - m_SharpAimMaxedOut = false; - m_PointingTarget.Reset(); - m_SeenTargetPos.Reset(); - // Set the limit to soemthing reasonable, if the timer is over it, there's no alarm - m_AlarmTimer.SetSimTimeLimitMS(3000); - m_AlarmTimer.SetElapsedSimTimeMS(4000); - m_LastAlarmPos.Reset(); - m_SightDistance = 450.0F; - m_Perceptiveness = 0.5F; - m_PainThreshold = 15.0F; - m_CanRevealUnseen = true; - m_CharHeight = 0; - m_HolsterOffset.Reset(); - m_ReloadOffset.Reset(); - m_ViewPoint.Reset(); - m_Inventory.clear(); - m_MaxInventoryMass = -1.0F; - m_pItemInReach = nullptr; - m_HUDStack = 0; - m_DeploymentID = 0; - m_PassengerSlots = 1; - - m_AIMode = AIMODE_NONE; - m_Waypoints.clear(); - m_DrawWaypoints = false; - m_MoveTarget.Reset(); - m_pMOMoveTarget = nullptr; - m_PrevPathTarget.Reset(); - m_MoveVector.Reset(); - m_MovePath.clear(); - m_UpdateMovePath = true; - m_MoveProximityLimit = 20.0F; - m_AIBaseDigStrength = c_PathFindingDefaultDigStrength; - m_BaseMass = std::numeric_limits::infinity(); - - m_DamageMultiplier = 1.0F; - - m_Organic = false; - m_Mechanical = false; - - m_LimbPushForcesAndCollisionsDisabled = false; - - m_PieMenu.reset(); -} + // Default to an interesting AI controller mode + m_Controller.SetInputMode(Controller::CIM_AI); + m_Controller.SetControlledActor(this); + m_UpdateMovePath = true; + m_ViewPoint = m_Pos; + m_HUDStack = -m_CharHeight / 2; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Actor object ready for use. + // Sets up the team icon + SetTeam(m_Team); -int Actor::Create() -{ - if (MOSRotating::Create() < 0) { - return -1; - } + if (const Actor* presetActor = static_cast(GetPreset())) { + m_BaseMass = presetActor->GetMass(); + } - // Set MO Type. - m_MOType = MovableObject::TypeActor; + // All brain actors by default avoid hitting each other on the same team + if (IsInGroup("Brains")) { + m_IgnoresTeamHits = true; + } - // Default to an interesting AI controller mode - m_Controller.SetInputMode(Controller::CIM_AI); - m_Controller.SetControlledActor(this); - m_UpdateMovePath = true; + if (!m_PieMenu) { + SetPieMenu(static_cast(g_PresetMan.GetEntityPreset("PieMenu", GetDefaultPieMenuName())->Clone())); + } else { + m_PieMenu->SetOwner(this); + } - m_ViewPoint = m_Pos; - m_HUDStack = -m_CharHeight / 2; + return 0; + } - // Sets up the team icon - SetTeam(m_Team); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Actor to be identical to another, by deep copy. - if (const Actor *presetActor = static_cast(GetPreset())) { - m_BaseMass = presetActor->GetMass(); - } + int Actor::Create(const Actor& reference) { + MOSRotating::Create(reference); - // All brain actors by default avoid hitting each other on the same team - if (IsInGroup("Brains")) { - m_IgnoresTeamHits = true; - } + // Set MO Type. + m_MOType = MovableObject::TypeActor; + + m_Controller = reference.m_Controller; + m_Controller.SetInputMode(Controller::CIM_AI); + m_Controller.SetControlledActor(this); + m_PlayerControllable = reference.m_PlayerControllable; + + if (reference.m_BodyHitSound) { + m_BodyHitSound = dynamic_cast(reference.m_BodyHitSound->Clone()); + } + if (reference.m_AlarmSound) { + m_AlarmSound = dynamic_cast(reference.m_AlarmSound->Clone()); + } + if (reference.m_PainSound) { + m_PainSound = dynamic_cast(reference.m_PainSound->Clone()); + } + if (reference.m_DeathSound) { + m_DeathSound = dynamic_cast(reference.m_DeathSound->Clone()); + } + if (reference.m_DeviceSwitchSound) { + m_DeviceSwitchSound = dynamic_cast(reference.m_DeviceSwitchSound->Clone()); + } + // m_FacingRight = reference.m_FacingRight; + m_Status = reference.m_Status; + m_Health = m_PrevHealth = reference.m_Health; + m_MaxHealth = reference.m_MaxHealth; + m_pTeamIcon = reference.m_pTeamIcon; + // m_LastSecondTimer.Reset(); + // m_LastSecondPos.Reset(); + // m_RecentMovement.Reset(); + m_LastSecondPos = reference.m_LastSecondPos; + m_TravelImpulseDamage = reference.m_TravelImpulseDamage; + m_StableVel = reference.m_StableVel; + m_StableRecoverDelay = reference.m_StableRecoverDelay; + m_GoldCarried = reference.m_GoldCarried; + m_AimState = reference.m_AimState; + m_AimRange = reference.m_AimRange; + m_AimAngle = reference.m_AimAngle; + m_AimDistance = reference.m_AimDistance; + m_SharpAimDelay = reference.m_SharpAimDelay; + m_SharpAimProgress = reference.m_SharpAimProgress; + m_PointingTarget = reference.m_PointingTarget; + m_SeenTargetPos = reference.m_SeenTargetPos; + m_SightDistance = reference.m_SightDistance; + m_Perceptiveness = reference.m_Perceptiveness; + m_PainThreshold = reference.m_PainThreshold; + m_CanRevealUnseen = reference.m_CanRevealUnseen; + m_CharHeight = reference.m_CharHeight; + m_HolsterOffset = reference.m_HolsterOffset; + m_ReloadOffset = reference.m_ReloadOffset; + + for (std::deque::const_iterator itr = reference.m_Inventory.begin(); itr != reference.m_Inventory.end(); ++itr) { + m_Inventory.push_back(dynamic_cast((*itr)->Clone())); + } - if (!m_PieMenu) { - SetPieMenu(static_cast(g_PresetMan.GetEntityPreset("PieMenu", GetDefaultPieMenuName())->Clone())); - } else { - m_PieMenu->SetOwner(this); + m_MaxInventoryMass = reference.m_MaxInventoryMass; + + // Only load the static AI mode icons once + if (!m_sIconsLoaded) { + ContentFile("Base.rte/GUIs/TeamIcons/NoTeam.png").GetAsAnimation(m_apNoTeamIcon, 2); + + ContentFile iconFile("Base.rte/GUIs/PieMenus/PieIcons/Blank000.png"); + m_apAIIcons[AIMODE_NONE] = iconFile.GetAsBitmap(); + m_apAIIcons[AIMODE_BOMB] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Eye000.png"); + m_apAIIcons[AIMODE_SENTRY] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Cycle000.png"); + m_apAIIcons[AIMODE_PATROL] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/GoTo000.png"); + m_apAIIcons[AIMODE_GOTO] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Brain000.png"); + m_apAIIcons[AIMODE_BRAINHUNT] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Dig000.png"); + m_apAIIcons[AIMODE_GOLDDIG] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Return000.png"); + m_apAIIcons[AIMODE_RETURN] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Land000.png"); + m_apAIIcons[AIMODE_STAY] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Launch000.png"); + m_apAIIcons[AIMODE_DELIVER] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Death000.png"); + m_apAIIcons[AIMODE_SCUTTLE] = iconFile.GetAsBitmap(); + iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Follow000.png"); + m_apAIIcons[AIMODE_SQUAD] = iconFile.GetAsBitmap(); + + ContentFile("Base.rte/GUIs/Indicators/SelectArrow.png").GetAsAnimation(m_apSelectArrow, 4); + ContentFile("Base.rte/GUIs/Indicators/AlarmExclamation.png").GetAsAnimation(m_apAlarmExclamation, 2); + + m_sIconsLoaded = true; + } + m_DeploymentID = reference.m_DeploymentID; + m_PassengerSlots = reference.m_PassengerSlots; + + m_AIMode = reference.m_AIMode; + m_Waypoints = reference.m_Waypoints; + m_DrawWaypoints = reference.m_DrawWaypoints; + m_MoveTarget = reference.m_MoveTarget; + m_pMOMoveTarget = reference.m_pMOMoveTarget; + m_PrevPathTarget = reference.m_PrevPathTarget; + m_MoveVector = reference.m_MoveVector; + m_MovePath.clear(); + m_UpdateMovePath = reference.m_UpdateMovePath; + m_MoveProximityLimit = reference.m_MoveProximityLimit; + m_AIBaseDigStrength = reference.m_AIBaseDigStrength; + m_BaseMass = reference.m_BaseMass; + + m_Organic = reference.m_Organic; + m_Mechanical = reference.m_Mechanical; + + m_LimbPushForcesAndCollisionsDisabled = reference.m_LimbPushForcesAndCollisionsDisabled; + + RTEAssert(reference.m_PieMenu != nullptr, "Tried to clone actor with no pie menu."); + SetPieMenu(static_cast(reference.m_PieMenu->Clone())); + m_PieMenu->AddWhilePieMenuOpenListener(this, std::bind(&Actor::WhilePieMenuOpenListener, this, m_PieMenu.get())); + + return 0; } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int Actor::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return MOSRotating::ReadProperty(propName, reader)); + + MatchProperty("PlayerControllable", { reader >> m_PlayerControllable; }); + MatchProperty("BodyHitSound", { + m_BodyHitSound = new SoundContainer; + reader >> m_BodyHitSound; + }); + MatchProperty("AlarmSound", { + m_AlarmSound = new SoundContainer; + reader >> m_AlarmSound; + }); + MatchProperty("PainSound", { + m_PainSound = new SoundContainer; + reader >> m_PainSound; + }); + MatchProperty("DeathSound", { + m_DeathSound = new SoundContainer; + reader >> m_DeathSound; + }); + MatchProperty("DeviceSwitchSound", { + m_DeviceSwitchSound = new SoundContainer; + reader >> m_DeviceSwitchSound; + }); + MatchProperty("Status", { reader >> m_Status; }); + MatchProperty("DeploymentID", { reader >> m_DeploymentID; }); + MatchProperty("PassengerSlots", { reader >> m_PassengerSlots; }); + MatchProperty("Health", + { + reader >> m_Health; + m_PrevHealth = m_Health; + if (m_Health > m_MaxHealth) + m_MaxHealth = m_Health; + }); + MatchProperty("MaxHealth", + { + reader >> m_MaxHealth; + if (m_MaxHealth < m_Health) { + m_Health = m_MaxHealth; + m_PrevHealth = m_Health; + } + }); + MatchProperty("ImpulseDamageThreshold", { reader >> m_TravelImpulseDamage; }); + MatchProperty("StableVelocityThreshold", { reader >> m_StableVel; }); + MatchProperty("StableRecoveryDelay", { reader >> m_StableRecoverDelay; }); + MatchProperty("AimAngle", { reader >> m_AimAngle; }); + MatchProperty("AimRange", { reader >> m_AimRange; }); + MatchProperty("AimDistance", { reader >> m_AimDistance; }); + MatchProperty("SharpAimDelay", { reader >> m_SharpAimDelay; }); + MatchProperty("SightDistance", { reader >> m_SightDistance; }); + MatchProperty("Perceptiveness", { reader >> m_Perceptiveness; }); + MatchProperty("PainThreshold", { reader >> m_PainThreshold; }); + MatchProperty("CanRevealUnseen", { reader >> m_CanRevealUnseen; }); + MatchProperty("CharHeight", { reader >> m_CharHeight; }); + MatchProperty("HolsterOffset", { reader >> m_HolsterOffset; }); + MatchProperty("ReloadOffset", { reader >> m_ReloadOffset; }); + MatchForwards("AddInventoryDevice") MatchProperty("AddInventory", + { + MovableObject* pInvMO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); + if (!pInvMO) { + reader.ReportError("Object added to inventory is broken."); + } + AddToInventoryBack(pInvMO); + }); + MatchProperty("MaxInventoryMass", { reader >> m_MaxInventoryMass; }); + MatchProperty("AIMode", { + int mode; + reader >> mode; + m_AIMode = static_cast(mode); + }); + MatchProperty("SpecialBehaviour_AddAISceneWaypoint", { + Vector waypointToAdd; + reader >> waypointToAdd; + AddAISceneWaypoint(waypointToAdd); + }); + MatchProperty("PieMenu", { + m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); + if (!m_PieMenu) { + reader.ReportError("Failed to set Actor's pie menu. Doublecheck your name and everything is correct."); + } + m_PieMenu->Create(this); + }); + MatchProperty("Organic", { reader >> m_Organic; }); + MatchProperty("Mechanical", { reader >> m_Mechanical; }); + MatchProperty("AIBaseDigStrength", { reader >> m_AIBaseDigStrength; }); + EndPropertyList; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Actor to be identical to another, by deep copy. - -int Actor::Create(const Actor &reference) -{ - MOSRotating::Create(reference); - - // Set MO Type. - m_MOType = MovableObject::TypeActor; - - m_Controller = reference.m_Controller; - m_Controller.SetInputMode(Controller::CIM_AI); - m_Controller.SetControlledActor(this); - m_PlayerControllable = reference.m_PlayerControllable; - - if (reference.m_BodyHitSound) { m_BodyHitSound = dynamic_cast(reference.m_BodyHitSound->Clone()); } - if (reference.m_AlarmSound) { m_AlarmSound = dynamic_cast(reference.m_AlarmSound->Clone()); } - if (reference.m_PainSound) { m_PainSound = dynamic_cast(reference.m_PainSound->Clone()); } - if (reference.m_DeathSound) { m_DeathSound = dynamic_cast(reference.m_DeathSound->Clone()); } - if (reference.m_DeviceSwitchSound) { m_DeviceSwitchSound = dynamic_cast(reference.m_DeviceSwitchSound->Clone()); } -// m_FacingRight = reference.m_FacingRight; - m_Status = reference.m_Status; - m_Health = m_PrevHealth = reference.m_Health; - m_MaxHealth = reference.m_MaxHealth; - m_pTeamIcon = reference.m_pTeamIcon; -// m_LastSecondTimer.Reset(); -// m_LastSecondPos.Reset(); -// m_RecentMovement.Reset(); - m_LastSecondPos = reference.m_LastSecondPos; - m_TravelImpulseDamage = reference.m_TravelImpulseDamage; - m_StableVel = reference.m_StableVel; - m_StableRecoverDelay = reference.m_StableRecoverDelay; - m_GoldCarried = reference.m_GoldCarried; - m_AimState = reference.m_AimState; - m_AimRange = reference.m_AimRange; - m_AimAngle = reference.m_AimAngle; - m_AimDistance = reference.m_AimDistance; - m_SharpAimDelay = reference.m_SharpAimDelay; - m_SharpAimProgress = reference.m_SharpAimProgress; - m_PointingTarget = reference.m_PointingTarget; - m_SeenTargetPos = reference.m_SeenTargetPos; - m_SightDistance = reference.m_SightDistance; - m_Perceptiveness = reference.m_Perceptiveness; - m_PainThreshold = reference.m_PainThreshold; - m_CanRevealUnseen = reference.m_CanRevealUnseen; - m_CharHeight = reference.m_CharHeight; - m_HolsterOffset = reference.m_HolsterOffset; - m_ReloadOffset = reference.m_ReloadOffset; - - for (std::deque::const_iterator itr = reference.m_Inventory.begin(); itr != reference.m_Inventory.end(); ++itr) { - m_Inventory.push_back(dynamic_cast((*itr)->Clone())); - } - - m_MaxInventoryMass = reference.m_MaxInventoryMass; - - // Only load the static AI mode icons once - if (!m_sIconsLoaded) - { - ContentFile("Base.rte/GUIs/TeamIcons/NoTeam.png").GetAsAnimation(m_apNoTeamIcon, 2); - - ContentFile iconFile("Base.rte/GUIs/PieMenus/PieIcons/Blank000.png"); - m_apAIIcons[AIMODE_NONE] = iconFile.GetAsBitmap(); - m_apAIIcons[AIMODE_BOMB] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Eye000.png"); - m_apAIIcons[AIMODE_SENTRY] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Cycle000.png"); - m_apAIIcons[AIMODE_PATROL] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/GoTo000.png"); - m_apAIIcons[AIMODE_GOTO] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Brain000.png"); - m_apAIIcons[AIMODE_BRAINHUNT] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Dig000.png"); - m_apAIIcons[AIMODE_GOLDDIG] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Return000.png"); - m_apAIIcons[AIMODE_RETURN] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Land000.png"); - m_apAIIcons[AIMODE_STAY] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Launch000.png"); - m_apAIIcons[AIMODE_DELIVER] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Death000.png"); - m_apAIIcons[AIMODE_SCUTTLE] = iconFile.GetAsBitmap(); - iconFile.SetDataPath("Base.rte/GUIs/PieMenus/PieIcons/Follow000.png"); - m_apAIIcons[AIMODE_SQUAD] = iconFile.GetAsBitmap(); - - ContentFile("Base.rte/GUIs/Indicators/SelectArrow.png").GetAsAnimation(m_apSelectArrow, 4); - ContentFile("Base.rte/GUIs/Indicators/AlarmExclamation.png").GetAsAnimation(m_apAlarmExclamation, 2); - - m_sIconsLoaded = true; - } - m_DeploymentID = reference.m_DeploymentID; - m_PassengerSlots = reference.m_PassengerSlots; - - m_AIMode = reference.m_AIMode; - m_Waypoints = reference.m_Waypoints; - m_DrawWaypoints = reference.m_DrawWaypoints; - m_MoveTarget = reference.m_MoveTarget; - m_pMOMoveTarget = reference.m_pMOMoveTarget; - m_PrevPathTarget = reference.m_PrevPathTarget; - m_MoveVector = reference.m_MoveVector; - m_MovePath.clear(); - m_UpdateMovePath = reference.m_UpdateMovePath; - m_MoveProximityLimit = reference.m_MoveProximityLimit; - m_AIBaseDigStrength = reference.m_AIBaseDigStrength; - m_BaseMass = reference.m_BaseMass; - - m_Organic = reference.m_Organic; - m_Mechanical = reference.m_Mechanical; - - m_LimbPushForcesAndCollisionsDisabled = reference.m_LimbPushForcesAndCollisionsDisabled; - - RTEAssert(reference.m_PieMenu != nullptr, "Tried to clone actor with no pie menu."); - SetPieMenu(static_cast(reference.m_PieMenu->Clone())); - m_PieMenu->AddWhilePieMenuOpenListener(this, std::bind(&Actor::WhilePieMenuOpenListener, this, m_PieMenu.get())); - - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this Actor with a Writer for + // later recreation with Create(Reader &reader); + + int Actor::Save(Writer& writer) const { + MOSRotating::Save(writer); + + writer.NewPropertyWithValue("PlayerControllable", m_PlayerControllable); + writer.NewProperty("BodyHitSound"); + writer << m_BodyHitSound; + writer.NewProperty("AlarmSound"); + writer << m_AlarmSound; + writer.NewProperty("PainSound"); + writer << m_PainSound; + writer.NewProperty("DeathSound"); + writer << m_DeathSound; + writer.NewProperty("DeviceSwitchSound"); + writer << m_DeviceSwitchSound; + writer.NewProperty("Status"); + writer << m_Status; + writer.NewProperty("Health"); + writer << m_Health; + writer.NewProperty("MaxHealth"); + writer << m_MaxHealth; + if (m_DeploymentID) { + writer.NewProperty("DeploymentID"); + writer << m_DeploymentID; + } + writer.NewProperty("ImpulseDamageThreshold"); + writer << m_TravelImpulseDamage; + writer.NewProperty("StableVelocityThreshold"); + writer << m_StableVel; + writer.NewProperty("StableRecoveryDelay"); + writer << m_StableRecoverDelay; + writer.NewProperty("AimAngle"); + writer << m_AimAngle; + writer.NewProperty("AimRange"); + writer << m_AimRange; + writer.NewProperty("AimDistance"); + writer << m_AimDistance; + writer.NewProperty("SharpAimDelay"); + writer << m_SharpAimDelay; + writer.NewProperty("SightDistance"); + writer << m_SightDistance; + writer.NewProperty("Perceptiveness"); + writer << m_Perceptiveness; + writer.NewProperty("PainThreshold"); + writer << m_PainThreshold; + writer.NewProperty("CanRevealUnseen"); + writer << m_CanRevealUnseen; + writer.NewProperty("CharHeight"); + writer << m_CharHeight; + writer.NewProperty("HolsterOffset"); + writer << m_HolsterOffset; + writer.NewPropertyWithValue("ReloadOffset", m_ReloadOffset); + for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + writer.NewProperty("AddInventory"); + writer << **itr; + } + writer.NewProperty("MaxInventoryMass"); + writer << m_MaxInventoryMass; + writer.NewProperty("AIMode"); + writer << m_AIMode; + writer.NewProperty("PieMenu"); + writer << m_PieMenu.get(); + + writer.NewPropertyWithValue("Organic", m_Organic); + writer.NewPropertyWithValue("Mechanical", m_Mechanical); + writer.NewPropertyWithValue("AIBaseDigStrength", m_AIBaseDigStrength); + + return 0; + } + void Actor::DestroyScriptState() { + for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + (*itr)->DestroyScriptState(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int Actor::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return MOSRotating::ReadProperty(propName, reader)); - - MatchProperty("PlayerControllable", { reader >> m_PlayerControllable; }); - MatchProperty("BodyHitSound", { - m_BodyHitSound = new SoundContainer; - reader >> m_BodyHitSound; - }); - MatchProperty("AlarmSound", { - m_AlarmSound = new SoundContainer; - reader >> m_AlarmSound; - }); - MatchProperty("PainSound", { - m_PainSound = new SoundContainer; - reader >> m_PainSound; - }); - MatchProperty("DeathSound", { - m_DeathSound = new SoundContainer; - reader >> m_DeathSound; - }); - MatchProperty("DeviceSwitchSound", { - m_DeviceSwitchSound = new SoundContainer; - reader >> m_DeviceSwitchSound; - }); - MatchProperty("Status", { reader >> m_Status; }); - MatchProperty("DeploymentID", { reader >> m_DeploymentID; }); - MatchProperty("PassengerSlots", { reader >> m_PassengerSlots; }); - MatchProperty("Health", - { - reader >> m_Health; - m_PrevHealth = m_Health; - if (m_Health > m_MaxHealth) - m_MaxHealth = m_Health; - }); - MatchProperty("MaxHealth", - { - reader >> m_MaxHealth; - if (m_MaxHealth < m_Health) - { - m_Health = m_MaxHealth; - m_PrevHealth = m_Health; - } - }); - MatchProperty("ImpulseDamageThreshold", { reader >> m_TravelImpulseDamage; }); - MatchProperty("StableVelocityThreshold", { reader >> m_StableVel; }); - MatchProperty("StableRecoveryDelay", { reader >> m_StableRecoverDelay; }); - MatchProperty("AimAngle", { reader >> m_AimAngle; }); - MatchProperty("AimRange", { reader >> m_AimRange; }); - MatchProperty("AimDistance", { reader >> m_AimDistance; }); - MatchProperty("SharpAimDelay", { reader >> m_SharpAimDelay; }); - MatchProperty("SightDistance", { reader >> m_SightDistance; }); - MatchProperty("Perceptiveness", { reader >> m_Perceptiveness; }); - MatchProperty("PainThreshold", { reader >> m_PainThreshold; }); - MatchProperty("CanRevealUnseen", { reader >> m_CanRevealUnseen; }); - MatchProperty("CharHeight", { reader >> m_CharHeight; }); - MatchProperty("HolsterOffset", { reader >> m_HolsterOffset; }); - MatchProperty("ReloadOffset", { reader >> m_ReloadOffset; }); - MatchForwards("AddInventoryDevice") MatchProperty("AddInventory", - { - MovableObject *pInvMO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); - if (!pInvMO) { reader.ReportError("Object added to inventory is broken."); } - AddToInventoryBack(pInvMO); - }); - MatchProperty("MaxInventoryMass", { reader >> m_MaxInventoryMass; }); - MatchProperty("AIMode", { - int mode; - reader >> mode; - m_AIMode = static_cast(mode); - }); - MatchProperty("SpecialBehaviour_AddAISceneWaypoint", { - Vector waypointToAdd; - reader >> waypointToAdd; - AddAISceneWaypoint(waypointToAdd); - }); - MatchProperty("PieMenu", { - m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); - if (!m_PieMenu) { reader.ReportError("Failed to set Actor's pie menu. Doublecheck your name and everything is correct."); } - m_PieMenu->Create(this); - }); - MatchProperty("Organic", { reader >> m_Organic; }); - MatchProperty("Mechanical", { reader >> m_Mechanical; }); - MatchProperty("AIBaseDigStrength", { reader >> m_AIBaseDigStrength; }); - - - EndPropertyList; -} + MOSRotating::DestroyScriptState(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the Actor object. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Actor with a Writer for -// later recreation with Create(Reader &reader); - -int Actor::Save(Writer &writer) const -{ - MOSRotating::Save(writer); - - writer.NewPropertyWithValue("PlayerControllable", m_PlayerControllable); - writer.NewProperty("BodyHitSound"); - writer << m_BodyHitSound; - writer.NewProperty("AlarmSound"); - writer << m_AlarmSound; - writer.NewProperty("PainSound"); - writer << m_PainSound; - writer.NewProperty("DeathSound"); - writer << m_DeathSound; - writer.NewProperty("DeviceSwitchSound"); - writer << m_DeviceSwitchSound; - writer.NewProperty("Status"); - writer << m_Status; - writer.NewProperty("Health"); - writer << m_Health; - writer.NewProperty("MaxHealth"); - writer << m_MaxHealth; - if (m_DeploymentID) - { - writer.NewProperty("DeploymentID"); - writer << m_DeploymentID; + void Actor::Destroy(bool notInherited) { + delete m_DeviceSwitchSound; + delete m_BodyHitSound; + delete m_PainSound; + delete m_DeathSound; + delete m_AlarmSound; + + for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + delete (*itr); + } + + if (!notInherited) { + MOSRotating::Destroy(); + } + + Clear(); } - writer.NewProperty("ImpulseDamageThreshold"); - writer << m_TravelImpulseDamage; - writer.NewProperty("StableVelocityThreshold"); - writer << m_StableVel; - writer.NewProperty("StableRecoveryDelay"); - writer << m_StableRecoverDelay; - writer.NewProperty("AimAngle"); - writer << m_AimAngle; - writer.NewProperty("AimRange"); - writer << m_AimRange; - writer.NewProperty("AimDistance"); - writer << m_AimDistance; - writer.NewProperty("SharpAimDelay"); - writer << m_SharpAimDelay; - writer.NewProperty("SightDistance"); - writer << m_SightDistance; - writer.NewProperty("Perceptiveness"); - writer << m_Perceptiveness; - writer.NewProperty("PainThreshold"); - writer << m_PainThreshold; - writer.NewProperty("CanRevealUnseen"); - writer << m_CanRevealUnseen; - writer.NewProperty("CharHeight"); - writer << m_CharHeight; - writer.NewProperty("HolsterOffset"); - writer << m_HolsterOffset; - writer.NewPropertyWithValue("ReloadOffset", m_ReloadOffset); - for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - writer.NewProperty("AddInventory"); - writer << **itr; - } - writer.NewProperty("MaxInventoryMass"); - writer << m_MaxInventoryMass; - writer.NewProperty("AIMode"); - writer << m_AIMode; - writer.NewProperty("PieMenu"); - writer << m_PieMenu.get(); - - writer.NewPropertyWithValue("Organic", m_Organic); - writer.NewPropertyWithValue("Mechanical", m_Mechanical); - writer.NewPropertyWithValue("AIBaseDigStrength", m_AIBaseDigStrength); - - return 0; -} - -void Actor::DestroyScriptState() { - for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { - (*itr)->DestroyScriptState(); - } - - MOSRotating::DestroyScriptState(); -} -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Actor object. - -void Actor::Destroy(bool notInherited) -{ - delete m_DeviceSwitchSound; - delete m_BodyHitSound; - delete m_PainSound; - delete m_DeathSound; - delete m_AlarmSound; - - for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { - delete (*itr); - } - - if (!notInherited) { - MOSRotating::Destroy(); - } - - Clear(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float Actor::GetInventoryMass() const { - float inventoryMass = 0.0F; - for (const MovableObject *inventoryItem : m_Inventory) { - inventoryMass += inventoryItem->GetMass(); - } - return inventoryMass; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float Actor::GetBaseMass() { - if (m_BaseMass == std::numeric_limits::infinity()) { - if (const Actor* presetActor = static_cast(GetPreset())) { - m_BaseMass = presetActor->GetMass(); - } else { - m_BaseMass = GetMass(); - } - } - - return m_BaseMass; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float Actor::GetInventoryMass() const { + float inventoryMass = 0.0F; + for (const MovableObject* inventoryItem: m_Inventory) { + inventoryMass += inventoryItem->GetMass(); + } + return inventoryMass; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsPlayerControlled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a player is currently controlling this. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool Actor::IsPlayerControlled() const -{ - return m_Controller.GetInputMode() == Controller::CIM_PLAYER && m_Controller.GetPlayer() >= 0; -} + float Actor::GetBaseMass() { + if (m_BaseMass == std::numeric_limits::infinity()) { + if (const Actor* presetActor = static_cast(GetPreset())) { + m_BaseMass = presetActor->GetMass(); + } else { + m_BaseMass = GetMass(); + } + } + return m_BaseMass; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of this Actor and all its carried -// gold and inventory. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -float Actor::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const -{ - float totalValue = (GetGoldValue(nativeModule, foreignMult, nativeMult) / 2) + ((GetGoldValue(nativeModule, foreignMult, nativeMult) / 2) * (GetHealth() / GetMaxHealth())); - totalValue += GetGoldCarried(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsPlayerControlled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a player is currently controlling this. - MOSprite *pItem = 0; - for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - pItem = dynamic_cast(*itr); - if (pItem) - totalValue += pItem->GetTotalValue(nativeModule, foreignMult, nativeMult); - } + bool Actor::IsPlayerControlled() const { + return m_Controller.GetInputMode() == Controller::CIM_PLAYER && m_Controller.GetPlayer() >= 0; + } - return totalValue; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of this Actor and all its carried + // gold and inventory. + + float Actor::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const { + float totalValue = (GetGoldValue(nativeModule, foreignMult, nativeMult) / 2) + ((GetGoldValue(nativeModule, foreignMult, nativeMult) / 2) * (GetHealth() / GetMaxHealth())); + totalValue += GetGoldCarried(); + + MOSprite* pItem = 0; + for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + pItem = dynamic_cast(*itr); + if (pItem) + totalValue += pItem->GetTotalValue(nativeModule, foreignMult, nativeMult); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this carries a specifically named object in its inventory. -// Also looks through the inventories of potential passengers, as applicable. + return totalValue; + } -bool Actor::HasObject(std::string objectName) const -{ - if (MOSRotating::HasObject(objectName)) - return true; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this carries a specifically named object in its inventory. + // Also looks through the inventories of potential passengers, as applicable. - for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - if ((*itr) && (*itr)->HasObject(objectName)) - return true; - } + bool Actor::HasObject(std::string objectName) const { + if (MOSRotating::HasObject(objectName)) + return true; - return false; -} + for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + if ((*itr) && (*itr)->HasObject(objectName)) + return true; + } + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObjectInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically grouped object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObjectInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically grouped object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. -bool Actor::HasObjectInGroup(std::string groupName) const -{ - if (MOSRotating::HasObjectInGroup(groupName)) - return true; + bool Actor::HasObjectInGroup(std::string groupName) const { + if (MOSRotating::HasObjectInGroup(groupName)) + return true; - for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - if ((*itr) && (*itr)->HasObjectInGroup(groupName)) - return true; - } + for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + if ((*itr) && (*itr)->HasObjectInGroup(groupName)) + return true; + } - return false; -} + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this Actor belongs to. + + void Actor::SetTeam(int team) { + MovableObject::SetTeam(team); + + // Change the Team Icon to display + m_pTeamIcon = 0; + if (g_ActivityMan.GetActivity()) + m_pTeamIcon = g_ActivityMan.GetActivity()->GetTeamIcon(m_Team); + + // Also set all actors in the inventory + Actor* pActor = 0; + for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) { + pActor = dynamic_cast(*itr); + if (pActor) + pActor->SetTeam(team); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this Actor belongs to. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetControllerMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this Actor's new Controller input mode. -void Actor::SetTeam(int team) -{ - MovableObject::SetTeam(team); + void Actor::SetControllerMode(Controller::InputMode newMode, int newPlayer) { - // Change the Team Icon to display - m_pTeamIcon = 0; - if (g_ActivityMan.GetActivity()) - m_pTeamIcon = g_ActivityMan.GetActivity()->GetTeamIcon(m_Team); + Controller::InputMode previousControllerMode = m_Controller.GetInputMode(); + int previousControllingPlayer = m_Controller.GetPlayer(); - // Also set all actors in the inventory - Actor *pActor = 0; - for (std::deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - { - pActor = dynamic_cast(*itr); - if (pActor) - pActor->SetTeam(team); - } -} + m_Controller.SetInputMode(newMode); + m_Controller.SetPlayer(newPlayer); + RunScriptedFunctionInAppropriateScripts("OnControllerInputModeChange", false, false, {}, {std::to_string(previousControllerMode), std::to_string(previousControllingPlayer)}); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetControllerMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this Actor's new Controller input mode. + m_NewControlTmr.Reset(); + } -void Actor::SetControllerMode(Controller::InputMode newMode, int newPlayer) -{ + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SwapControllerModes + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this Actor's Controller mode and gives back what it used to be. - Controller::InputMode previousControllerMode = m_Controller.GetInputMode(); - int previousControllingPlayer = m_Controller.GetPlayer(); + Controller::InputMode Actor::SwapControllerModes(Controller::InputMode newMode, int newPlayer) { + Controller::InputMode returnMode = m_Controller.GetInputMode(); + SetControllerMode(newMode, newPlayer); + return returnMode; + } - m_Controller.SetInputMode(newMode); - m_Controller.SetPlayer(newPlayer); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Look + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an unseen-revealing ray in the direction of where this is facing. + + bool Actor::Look(float FOVSpread, float range) { + if (!g_SceneMan.AnythingUnseen(m_Team) || m_CanRevealUnseen == false) + return false; + + // Use the 'eyes' on the 'head', if applicable + Vector aimPos = GetEyePos(); + /* + Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); + aimMatrix.SetXFlipped(m_HFlipped); + // Get the langth of the look vector + Vector aimDistance = m_ViewPoint - aimPos; + // Add half the screen width + Vector lookVector(fabs(aimDistance.m_X) + range, 0); + // Set the rotation to the acutal aiming angle + lookVector *= aimMatrix; + // Add the spread + lookVector.DegRotate(FOVSpread * NormalRand()); + // TEST: Really need so far? + lookVector /= 2; + */ + Vector lookVector = m_Vel; + // If there is no vel, just look in all directions + if (lookVector.GetLargest() < 0.01) { + lookVector.SetXY(range, 0); + lookVector.DegRotate(RandomNum(-180.0F, 180.0F)); + } else { + // Set the distance in the look direction + lookVector.SetMagnitude(range); + // Add the spread from the directed look + lookVector.DegRotate(FOVSpread * RandomNormalNum()); + } - RunScriptedFunctionInAppropriateScripts("OnControllerInputModeChange", false, false, {}, {std::to_string(previousControllerMode), std::to_string(previousControllingPlayer) }); + Vector ignored; + return g_SceneMan.CastSeeRay(m_Team, aimPos, lookVector, ignored, 25, g_SceneMan.GetUnseenResolution(m_Team).GetSmallest() / 2); + } - m_NewControlTmr.Reset(); -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Actor::AddGold(float goldOz) { + bool isHumanTeam = g_ActivityMan.GetActivity()->IsHumanTeam(m_Team); + if (g_SettingsMan.GetAutomaticGoldDeposit() || !isHumanTeam) { + // TODO: Allow AI to reliably deliver gold via craft + g_ActivityMan.GetActivity()->ChangeTeamFunds(goldOz, m_Team); + } else { + m_GoldCarried += goldOz; + m_GoldPicked = true; + if (isHumanTeam) { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { + if (g_ActivityMan.GetActivity()->GetTeamOfPlayer(player) == m_Team) { + g_GUISound.FundsChangedSound()->Play(player); + } + } + } + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Actor::RestDetection() { + MOSRotating::RestDetection(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwapControllerModes -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this Actor's Controller mode and gives back what it used to be. + if (m_Status != DEAD) { + m_AngOscillations = 0; + m_VelOscillations = 0; + m_RestTimer.Reset(); + m_ToSettle = false; + } + } -Controller::InputMode Actor::SwapControllerModes(Controller::InputMode newMode, int newPlayer) -{ - Controller::InputMode returnMode = m_Controller.GetInputMode(); - SetControllerMode(newMode, newPlayer); - return returnMode; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: AddAIMOWaypoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an MO in the scene as the next waypoint for this to go to, in order + void Actor::AddAIMOWaypoint(const MovableObject* pMOWaypoint) { + if (g_MovableMan.ValidMO(pMOWaypoint)) + m_Waypoints.push_back(std::pair(pMOWaypoint->GetPos(), pMOWaypoint)); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Look -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an unseen-revealing ray in the direction of where this is facing. - -bool Actor::Look(float FOVSpread, float range) -{ - if (!g_SceneMan.AnythingUnseen(m_Team) || m_CanRevealUnseen == false) - return false; - - // Use the 'eyes' on the 'head', if applicable - Vector aimPos = GetEyePos(); -/* - Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); - aimMatrix.SetXFlipped(m_HFlipped); - // Get the langth of the look vector - Vector aimDistance = m_ViewPoint - aimPos; - // Add half the screen width - Vector lookVector(fabs(aimDistance.m_X) + range, 0); - // Set the rotation to the acutal aiming angle - lookVector *= aimMatrix; - // Add the spread - lookVector.DegRotate(FOVSpread * NormalRand()); -// TEST: Really need so far? - lookVector /= 2; -*/ - Vector lookVector = m_Vel; - // If there is no vel, just look in all directions - if (lookVector.GetLargest() < 0.01) - { - lookVector.SetXY(range, 0); - lookVector.DegRotate(RandomNum(-180.0F, 180.0F)); - } - else - { - // Set the distance in the look direction - lookVector.SetMagnitude(range); - // Add the spread from the directed look - lookVector.DegRotate(FOVSpread * RandomNormalNum()); - } - - Vector ignored; - return g_SceneMan.CastSeeRay(m_Team, aimPos, lookVector, ignored, 25, g_SceneMan.GetUnseenResolution(m_Team).GetSmallest() / 2); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void Actor::AddGold(float goldOz) { - bool isHumanTeam = g_ActivityMan.GetActivity()->IsHumanTeam(m_Team); - if (g_SettingsMan.GetAutomaticGoldDeposit() || !isHumanTeam) { - // TODO: Allow AI to reliably deliver gold via craft - g_ActivityMan.GetActivity()->ChangeTeamFunds(goldOz, m_Team); - } else { - m_GoldCarried += goldOz; - m_GoldPicked = true; - if (isHumanTeam) { - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { - if (g_ActivityMan.GetActivity()->GetTeamOfPlayer(player) == m_Team) { g_GUISound.FundsChangedSound()->Play(player); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SwapNextInventory + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Swaps the next MovableObject carried by this Actor and puts one not + // currently carried into the into the back of the inventory of this. + + MovableObject* Actor::SwapNextInventory(MovableObject* pSwapIn, bool muteSound) { + MovableObject* pRetDev = 0; + bool playSound = false; + if (!m_Inventory.empty()) { + pRetDev = m_Inventory.front(); + // Reset all the timers of the object being taken out of inventory so it doesn't emit a bunch of particles that have been backed up while dormant in inventory + pRetDev->ResetAllTimers(); + m_Inventory.pop_front(); + playSound = true; + } + if (pSwapIn) { + pSwapIn->SetAsNoID(); + AddToInventoryBack(pSwapIn); + playSound = true; + } + + if (m_DeviceSwitchSound && playSound && !muteSound) + m_DeviceSwitchSound->Play(m_Pos); + + return pRetDev; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Actor::RemoveInventoryItem(const std::string& moduleName, const std::string& presetName) { + for (std::deque::iterator inventoryIterator = m_Inventory.begin(); inventoryIterator != m_Inventory.end(); ++inventoryIterator) { + if ((moduleName.empty() || (*inventoryIterator)->GetModuleName() == moduleName) && (*inventoryIterator)->GetPresetName() == presetName) { + (*inventoryIterator)->DestroyScriptState(); + delete (*inventoryIterator); + m_Inventory.erase(inventoryIterator); + break; } } } -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MovableObject* Actor::RemoveInventoryItemAtIndex(int inventoryIndex) { + if (inventoryIndex >= 0 && inventoryIndex < m_Inventory.size()) { + MovableObject* itemAtIndex = m_Inventory[inventoryIndex]; + m_Inventory.erase(m_Inventory.begin() + inventoryIndex); + return itemAtIndex; + } + return nullptr; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SwapPrevInventory + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Swaps the prev MovableObject carried by this Actor and puts one not + // currently carried into the into the back of the inventory of this. + + MovableObject* Actor::SwapPrevInventory(MovableObject* pSwapIn) { + MovableObject* pRetDev = 0; + bool playSound = false; + if (!m_Inventory.empty()) { + pRetDev = m_Inventory.back(); + m_Inventory.pop_back(); + playSound = true; + } + if (pSwapIn) { + pSwapIn->SetAsNoID(); + AddToInventoryFront(pSwapIn); + playSound = true; + } -void Actor::RestDetection() { - MOSRotating::RestDetection(); + if (m_DeviceSwitchSound && playSound) + m_DeviceSwitchSound->Play(m_Pos); - if (m_Status != DEAD) { - m_AngOscillations = 0; - m_VelOscillations = 0; - m_RestTimer.Reset(); - m_ToSettle = false; + return pRetDev; } -} -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: AddAIMOWaypoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an MO in the scene as the next waypoint for this to go to, in order + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void Actor::AddAIMOWaypoint(const MovableObject *pMOWaypoint) -{ - if (g_MovableMan.ValidMO(pMOWaypoint)) - m_Waypoints.push_back(std::pair(pMOWaypoint->GetPos(), pMOWaypoint)); -} + bool Actor::SwapInventoryItemsByIndex(int inventoryIndex1, int inventoryIndex2) { + if (inventoryIndex1 < 0 || inventoryIndex2 < 0 || inventoryIndex1 >= m_Inventory.size() || inventoryIndex2 >= m_Inventory.size()) { + return false; + } + std::swap(m_Inventory.at(inventoryIndex1), m_Inventory.at(inventoryIndex2)); + return true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwapNextInventory -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Swaps the next MovableObject carried by this Actor and puts one not -// currently carried into the into the back of the inventory of this. - -MovableObject * Actor::SwapNextInventory(MovableObject *pSwapIn, bool muteSound) -{ - MovableObject *pRetDev = 0; - bool playSound = false; - if (!m_Inventory.empty()) { - pRetDev = m_Inventory.front(); - // Reset all the timers of the object being taken out of inventory so it doesn't emit a bunch of particles that have been backed up while dormant in inventory - pRetDev->ResetAllTimers(); - m_Inventory.pop_front(); - playSound = true; - } - if (pSwapIn) - { - pSwapIn->SetAsNoID(); - AddToInventoryBack(pSwapIn); - playSound = true; - } - - if (m_DeviceSwitchSound && playSound && !muteSound) - m_DeviceSwitchSound->Play(m_Pos); - - return pRetDev; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void Actor::RemoveInventoryItem(const std::string &moduleName, const std::string &presetName) { - for (std::deque::iterator inventoryIterator = m_Inventory.begin(); inventoryIterator != m_Inventory.end(); ++inventoryIterator) { - if ((moduleName.empty() || (*inventoryIterator)->GetModuleName() == moduleName) && (*inventoryIterator)->GetPresetName() == presetName) { - (*inventoryIterator)->DestroyScriptState(); - delete (*inventoryIterator); - m_Inventory.erase(inventoryIterator); - break; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MovableObject* Actor::SetInventoryItemAtIndex(MovableObject* newInventoryItem, int inventoryIndex) { + if (!newInventoryItem) { + return RemoveInventoryItemAtIndex(inventoryIndex); + } + newInventoryItem->SetAsNoID(); + + if (inventoryIndex < 0 || inventoryIndex >= m_Inventory.size()) { + AddToInventoryBack(newInventoryItem); + return nullptr; + } + MovableObject* currentInventoryItemAtIndex = m_Inventory.at(inventoryIndex); + m_Inventory.at(inventoryIndex) = newInventoryItem; + return currentInventoryItemAtIndex; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DropAllInventory + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Ejects all inventory items that this is carrying. It may not happen + // instantaneously, so check for ejection being complete with InventoryEmpty(). + + void Actor::DropAllInventory() { + MovableObject* pObject = 0; + Actor* pPassenger = 0; + float velMin, velMax, angularVel; + Vector gibROffset, gibVel; + for (std::deque::iterator gItr = m_Inventory.begin(); gItr != m_Inventory.end(); ++gItr) { + // Get handy handle to the object we're putting + pObject = *gItr; + if (pObject) { + // Generate the velocities procedurally + velMin = 3.0F; + velMax = velMin + std::sqrt(m_SpriteRadius); + + // Randomize the offset from center to be within the original object + gibROffset.SetXY(m_SpriteRadius * 0.35F * RandomNum(), 0); + gibROffset.RadRotate(c_PI * RandomNormalNum()); + // Set up its position and velocity according to the parameters of this AEmitter. + pObject->SetPos(m_Pos + gibROffset); + pObject->SetRotAngle(m_Rotation.GetRadAngle() + pObject->GetRotMatrix().GetRadAngle()); + // Rotational angle + pObject->SetAngularVel((pObject->GetAngularVel() * 0.35F) + (pObject->GetAngularVel() * 0.65F / (pObject->GetMass() != 0 ? pObject->GetMass() : 0.0001F)) * RandomNum()); + // Make it rotate away in the appropriate direction depending on which side of the object it is on + // If the object is far to the relft or right of the center, make it always rotate outwards to some degree + if (gibROffset.m_X > m_aSprite[0]->w / 3) { + float offCenterRatio = gibROffset.m_X / (m_aSprite[0]->w / 2); + angularVel = std::abs(pObject->GetAngularVel() * 0.5F); + angularVel += std::abs(pObject->GetAngularVel() * 0.5F * offCenterRatio); + pObject->SetAngularVel(angularVel * (gibROffset.m_X > 0.0F ? -1 : 1)); + } + // Gib is too close to center to always make it rotate in one direction, so give it a baseline rotation and then randomize + else { + pObject->SetAngularVel((pObject->GetAngularVel() * RandomNum(0.5F, 1.5F)) * (RandomNum() < 0.5F ? 1.0F : -1.0F)); + } + + // TODO: Optimize making the random angles!") + gibVel = gibROffset; + if (gibVel.IsZero()) { + gibVel.SetXY(RandomNum(velMin, velMax), 0.0F); + gibVel.RadRotate(c_PI * RandomNormalNum()); + } else { + gibVel.SetMagnitude(RandomNum(velMin, velMax)); + } + // Distribute any impact implse out over all the gibs + // gibVel += (impactImpulse / m_Gibs.size()) / pObject->GetMass(); + pObject->SetVel(m_Vel + gibVel); + // Reset all the timers of the object being shot out so it doesn't emit a bunch of particles that have been backed up while dormant in inventory + pObject->ResetAllTimers(); + + // Detect whether we're dealing with a passenger and add it as Actor instead + if (pPassenger = dynamic_cast(pObject)) { + pPassenger->SetRotAngle(c_HalfPI * RandomNormalNum()); + pPassenger->SetAngularVel(pPassenger->GetAngularVel() * 5.0F); + pPassenger->SetHFlipped(RandomNum() > 0.5F); + pPassenger->SetStatus(UNSTABLE); + g_MovableMan.AddActor(pPassenger); + } + // Add the gib to the scene, passing ownership from the inventory + else + g_MovableMan.AddParticle(pObject); + + pPassenger = 0; + pObject = 0; + } } + + // We have exhausted all teh inventory into the scene, passing ownership + m_Inventory.clear(); } -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// -MovableObject * Actor::RemoveInventoryItemAtIndex(int inventoryIndex) { - if (inventoryIndex >= 0 && inventoryIndex < m_Inventory.size()) { - MovableObject *itemAtIndex = m_Inventory[inventoryIndex]; - m_Inventory.erase(m_Inventory.begin() + inventoryIndex); - return itemAtIndex; - } - return nullptr; -} + void Actor::DropAllGold() { + const Material* goldMaterial = g_SceneMan.GetMaterialFromID(g_MaterialGold); + float velMin = 3.0F; + float velMax = velMin + std::sqrt(m_SpriteRadius); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + for (int i = 0; i < static_cast(std::floor(m_GoldCarried)); i++) { + Vector dropOffset(m_SpriteRadius * 0.3F * RandomNum(), 0); + dropOffset.RadRotate(c_PI * RandomNormalNum()); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwapPrevInventory -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Swaps the prev MovableObject carried by this Actor and puts one not -// currently carried into the into the back of the inventory of this. - -MovableObject * Actor::SwapPrevInventory(MovableObject *pSwapIn) -{ - MovableObject *pRetDev = 0; - bool playSound = false; - if (!m_Inventory.empty()) { - pRetDev = m_Inventory.back(); - m_Inventory.pop_back(); - playSound = true; - } - if (pSwapIn) - { - pSwapIn->SetAsNoID(); - AddToInventoryFront(pSwapIn); - playSound = true; - } - - if (m_DeviceSwitchSound && playSound) - m_DeviceSwitchSound->Play(m_Pos); - - return pRetDev; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool Actor::SwapInventoryItemsByIndex(int inventoryIndex1, int inventoryIndex2) { - if (inventoryIndex1 < 0 || inventoryIndex2 < 0 || inventoryIndex1 >= m_Inventory.size() || inventoryIndex2 >= m_Inventory.size()) { - return false; - } - - std::swap(m_Inventory.at(inventoryIndex1), m_Inventory.at(inventoryIndex2)); - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -MovableObject * Actor::SetInventoryItemAtIndex(MovableObject *newInventoryItem, int inventoryIndex) { - if (!newInventoryItem) { - return RemoveInventoryItemAtIndex(inventoryIndex); - } - newInventoryItem->SetAsNoID(); - - if (inventoryIndex < 0 || inventoryIndex >= m_Inventory.size()) { - AddToInventoryBack(newInventoryItem); - return nullptr; - } - MovableObject *currentInventoryItemAtIndex = m_Inventory.at(inventoryIndex); - m_Inventory.at(inventoryIndex) = newInventoryItem; - return currentInventoryItemAtIndex; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Vector dropVelocity(dropOffset); + dropVelocity.SetMagnitude(RandomNum(velMin, velMax)); + Atom* goldMOPixelAtom = new Atom(Vector(), g_MaterialGold, nullptr, goldMaterial->GetColor(), 2); + + MOPixel* goldMOPixel = new MOPixel(goldMaterial->GetColor(), goldMaterial->GetPixelDensity(), m_Pos + dropOffset, dropVelocity, goldMOPixelAtom); + goldMOPixel->SetToHitMOs(false); + g_MovableMan.AddParticle(goldMOPixel); + } + m_GoldCarried = 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + bool Actor::AddToInventoryFront(MovableObject* itemToAdd) { + // This function is called often to add stuff we just removed from our hands, which may be set to delete so we need to guard against that lest we crash. + if (!itemToAdd || itemToAdd->IsSetToDelete()) { + return false; + } + + m_Inventory.push_front(itemToAdd); + return true; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + bool Actor::AddToInventoryBack(MovableObject* itemToAdd) { + // This function is called often to add stuff we just removed from our hands, which may be set to delete so we need to guard against that lest we crash. + if (!itemToAdd || itemToAdd->IsSetToDelete()) { + return false; + } + + m_Inventory.push_back(itemToAdd); + return true; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GibThis + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gibs this, effectively destroying it and creating multiple gibs or + // pieces in its place. + + void Actor::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { + // Play death sound + // TODO: Don't attenuate since death is pretty important.. maybe only make this happen for teh brains + if (m_DeathSound) { + m_DeathSound->Play(m_Pos); + } + + // Gib all the regular gibs + MOSRotating::GibThis(impactImpulse, movableObjectToIgnore); + + // Throw out all the inventory with the appropriate force and directions + MovableObject* pObject = 0; + Actor* pPassenger = 0; + float velMin, velRange, angularVel; + Vector gibROffset, gibVel; + for (std::deque::iterator gItr = m_Inventory.begin(); gItr != m_Inventory.end(); ++gItr) { + // Get handy handle to the object we're putting + pObject = *gItr; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DropAllInventory -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Ejects all inventory items that this is carrying. It may not happen -// instantaneously, so check for ejection being complete with InventoryEmpty(). - -void Actor::DropAllInventory() -{ - MovableObject *pObject = 0; - Actor *pPassenger = 0; - float velMin, velMax, angularVel; - Vector gibROffset, gibVel; - for (std::deque::iterator gItr = m_Inventory.begin(); gItr != m_Inventory.end(); ++gItr) - { - // Get handy handle to the object we're putting - pObject = *gItr; - if (pObject) - { // Generate the velocities procedurally - velMin = 3.0F; - velMax = velMin + std::sqrt(m_SpriteRadius); + velMin = m_GibBlastStrength / (pObject->GetMass() != 0 ? pObject->GetMass() : 0.0001F); + velRange = 10.0F; // Randomize the offset from center to be within the original object - gibROffset.SetXY(m_SpriteRadius * 0.35F * RandomNum(), 0); - gibROffset.RadRotate(c_PI * RandomNormalNum()); + gibROffset.SetXY(m_SpriteRadius * 0.35F * RandomNormalNum(), m_SpriteRadius * 0.35F * RandomNormalNum()); // Set up its position and velocity according to the parameters of this AEmitter. - pObject->SetPos(m_Pos + gibROffset); + pObject->SetPos(m_Pos + gibROffset /*Vector(m_Pos.m_X + 5 * NormalRand(), m_Pos.m_Y + 5 * NormalRand())*/); pObject->SetRotAngle(m_Rotation.GetRadAngle() + pObject->GetRotMatrix().GetRadAngle()); // Rotational angle pObject->SetAngularVel((pObject->GetAngularVel() * 0.35F) + (pObject->GetAngularVel() * 0.65F / (pObject->GetMass() != 0 ? pObject->GetMass() : 0.0001F)) * RandomNum()); // Make it rotate away in the appropriate direction depending on which side of the object it is on // If the object is far to the relft or right of the center, make it always rotate outwards to some degree - if (gibROffset.m_X > m_aSprite[0]->w / 3) - { + if (gibROffset.m_X > m_aSprite[0]->w / 3) { float offCenterRatio = gibROffset.m_X / (m_aSprite[0]->w / 2); - angularVel = std::abs(pObject->GetAngularVel() * 0.5F); - angularVel += std::abs(pObject->GetAngularVel() * 0.5F * offCenterRatio); - pObject->SetAngularVel(angularVel * (gibROffset.m_X > 0.0F ? -1 : 1)); + angularVel = fabs(pObject->GetAngularVel() * 0.5F); + angularVel += fabs(pObject->GetAngularVel() * 0.5F * offCenterRatio); + pObject->SetAngularVel(angularVel * (gibROffset.m_X > 0 ? -1 : 1)); } // Gib is too close to center to always make it rotate in one direction, so give it a baseline rotation and then randomize - else - { - pObject->SetAngularVel((pObject->GetAngularVel() * RandomNum(0.5F, 1.5F)) * (RandomNum() < 0.5F ? 1.0F : -1.0F)); + else { + pObject->SetAngularVel((pObject->GetAngularVel() * 0.5F + pObject->GetAngularVel() * RandomNum()) * (RandomNormalNum() > 0.0F ? 1.0F : -1.0F)); } // TODO: Optimize making the random angles!") gibVel = gibROffset; - if (gibVel.IsZero()) { - gibVel.SetXY(RandomNum(velMin, velMax), 0.0F); - gibVel.RadRotate(c_PI * RandomNormalNum()); - } else { - gibVel.SetMagnitude(RandomNum(velMin, velMax)); - } + if (gibVel.IsZero()) + gibVel.SetXY(velMin + RandomNum(0.0F, velRange), 0.0F); + else + gibVel.SetMagnitude(velMin + RandomNum(0.0F, velRange)); + gibVel.RadRotate(impactImpulse.GetAbsRadAngle()); + // Don't! the offset was already rotated! + // gibVel = RotateOffset(gibVel); // Distribute any impact implse out over all the gibs // gibVel += (impactImpulse / m_Gibs.size()) / pObject->GetMass(); pObject->SetVel(m_Vel + gibVel); // Reset all the timers of the object being shot out so it doesn't emit a bunch of particles that have been backed up while dormant in inventory pObject->ResetAllTimers(); + // Set the gib to not hit a specific MO + if (movableObjectToIgnore) + pObject->SetWhichMOToNotHit(movableObjectToIgnore); + // Detect whether we're dealing with a passenger and add it as Actor instead - if (pPassenger = dynamic_cast(pObject)) - { + if (pPassenger = dynamic_cast(pObject)) { pPassenger->SetRotAngle(c_HalfPI * RandomNormalNum()); pPassenger->SetAngularVel(pPassenger->GetAngularVel() * 5.0F); pPassenger->SetHFlipped(RandomNum() > 0.5F); @@ -953,925 +1051,753 @@ void Actor::DropAllInventory() pPassenger = 0; pObject = 0; } - } - // We have exhausted all teh inventory into the scene, passing ownership - m_Inventory.clear(); -} - -////////////////////////////////////////////////////////////////////////////////////////// + // We have exhausted all teh inventory into the scene, passing ownership + m_Inventory.clear(); + + // If this is the actual brain of any player, flash that player's screen when he's now dead + if (g_SettingsMan.FlashOnBrainDamage() && g_ActivityMan.IsInActivity()) { + int brainOfPlayer = g_ActivityMan.GetActivity()->IsBrainOfWhichPlayer(this); + // Only flash if player is human (AI players don't have screens!) + if (brainOfPlayer != Players::NoPlayer && g_ActivityMan.GetActivity()->PlayerHuman(brainOfPlayer)) { + // Croaked.. flash for a longer period + if (m_ToDelete || m_Status == DEAD) + g_FrameMan.FlashScreen(g_ActivityMan.GetActivity()->ScreenOfPlayer(brainOfPlayer), g_WhiteColor, 500); + } + } + } -void Actor::DropAllGold() { - const Material *goldMaterial = g_SceneMan.GetMaterialFromID(g_MaterialGold); - float velMin = 3.0F; - float velMax = velMin + std::sqrt(m_SpriteRadius); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. + + bool Actor::CollideAtPoint(HitData& hd) { + return MOSRotating::CollideAtPoint(hd); + + // if (hd.ResImpulse[HITEE].MagnitudeIsGreaterThan(GetMaterial().strength)) { + // m_pParent-> + // } + /* Obsolete + // Set item as being reached if it collides with us + if (hd.Body[HITOR]->IsHeldDevice()) + m_pItemInReach = dynamic_cast(hd.Body[HITOR]); + */ + // if (Status != ACTIVE) + } - for (int i = 0; i < static_cast(std::floor(m_GoldCarried)); i++) { - Vector dropOffset(m_SpriteRadius * 0.3F * RandomNum(), 0); - dropOffset.RadRotate(c_PI * RandomNormalNum()); - - Vector dropVelocity(dropOffset); - dropVelocity.SetMagnitude(RandomNum(velMin, velMax)); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ParticlePenetration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Determines whether a particle which has hit this MO will penetrate, + // and if so, whether it gets lodged or exits on the other side of this + // MO. Appropriate effects will be determined and applied ONLY IF there + // was penetration! If not, nothing will be affected. + + bool Actor::ParticlePenetration(HitData& hd) { + bool penetrated = MOSRotating::ParticlePenetration(hd); + + MovableObject* hitor = hd.Body[HITOR]; + float damageToAdd = hitor->DamageOnCollision(); + damageToAdd += penetrated ? hitor->DamageOnPenetration() : 0; + if (hitor->GetApplyWoundDamageOnCollision()) { + damageToAdd += m_pEntryWound->GetEmitDamage() * hitor->WoundDamageMultiplier(); + } + if (hitor->GetApplyWoundBurstDamageOnCollision()) { + damageToAdd += m_pEntryWound->GetBurstDamage() * hitor->WoundDamageMultiplier(); + } - Atom *goldMOPixelAtom = new Atom(Vector(), g_MaterialGold, nullptr, goldMaterial->GetColor(), 2); + if (damageToAdd != 0) { + m_Health = std::min(m_Health - (damageToAdd * m_DamageMultiplier), m_MaxHealth); + } + if ((penetrated || damageToAdd != 0) && m_Perceptiveness > 0 && m_Health > 0) { + Vector extruded(hd.HitVel[HITOR]); + extruded.SetMagnitude(m_CharHeight); + extruded = m_Pos - extruded; + g_SceneMan.WrapPosition(extruded); + AlarmPoint(extruded); + } - MOPixel *goldMOPixel = new MOPixel(goldMaterial->GetColor(), goldMaterial->GetPixelDensity(), m_Pos + dropOffset, dropVelocity, goldMOPixelAtom); - goldMOPixel->SetToHitMOs(false); - g_MovableMan.AddParticle(goldMOPixel); + return penetrated; } - m_GoldCarried = 0; -} -////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAIModeIcon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the icon bitmap associated with this' current AI mode and team. -bool Actor::AddToInventoryFront(MovableObject *itemToAdd) { - // This function is called often to add stuff we just removed from our hands, which may be set to delete so we need to guard against that lest we crash. - if (!itemToAdd || itemToAdd->IsSetToDelete()) { - return false; + BITMAP* Actor::GetAIModeIcon() { + return m_apAIIcons[m_AIMode]; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLastMOWaypointID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the ID of the last set AI MO waypoint of this. If none, g_NoMOID is returned. + // Arguments: None. + // Return value: The furthest set AI MO waypoint of this. + + MOID Actor::GetAIMOWaypointID() const { + if (g_MovableMan.ValidMO(m_pMOMoveTarget)) + return m_pMOMoveTarget->GetID(); + else + return g_NoMOID; } - m_Inventory.push_front(itemToAdd); - return true; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateMovePath + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the path to move along to the currently set movetarget. -////////////////////////////////////////////////////////////////////////////////////////// + void Actor::UpdateMovePath() { + if (g_SceneMan.GetScene() == nullptr) { + return; + } -bool Actor::AddToInventoryBack(MovableObject *itemToAdd) { - // This function is called often to add stuff we just removed from our hands, which may be set to delete so we need to guard against that lest we crash. - if (!itemToAdd || itemToAdd->IsSetToDelete()) { - return false; + // Estimate how much material this actor can dig through + float digStrength = EstimateDigStrength(); + + // If we're following someone/thing, then never advance waypoints until that thing disappears + if (g_MovableMan.ValidMO(m_pMOMoveTarget)) { + m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(g_SceneMan.MovePointToGround(m_Pos, m_CharHeight * 0.2, 10), m_pMOMoveTarget->GetPos(), digStrength, static_cast(m_Team)); + } else { + // Do we currently have a path to a static target we would like to still pursue? + if (m_MovePath.empty()) { + // Ok no path going, so get a new path to the next waypoint, if there is a next waypoint + if (!m_Waypoints.empty()) { + // Make sure the path starts from the ground and not somewhere up in the air if/when dropped out of ship + m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(g_SceneMan.MovePointToGround(m_Pos, m_CharHeight * 0.2, 10), m_Waypoints.front().first, digStrength, static_cast(m_Team)); + + // If the waypoint was tied to an MO to pursue, then load it into the current MO target + if (g_MovableMan.ValidMO(m_Waypoints.front().second)) { + m_pMOMoveTarget = m_Waypoints.front().second; + } else { + m_pMOMoveTarget = 0; + } + + // We loaded the waypoint, no need to keep it + m_Waypoints.pop_front(); + } + // Just try to get to the last Move Target + else { + m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(g_SceneMan.MovePointToGround(m_Pos, m_CharHeight * 0.2, 10), m_MoveTarget, digStrength, static_cast(m_Team)); + } + } + // We had a path before trying to update, so use its last point as the final destination + else { + m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(g_SceneMan.MovePointToGround(m_Pos, m_CharHeight * 0.2, 10), Vector(m_MovePath.back()), digStrength, static_cast(m_Team)); + } + } + + m_UpdateMovePath = false; } - m_Inventory.push_back(itemToAdd); - return true; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. - -void Actor::GibThis(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) -{ - // Play death sound -// TODO: Don't attenuate since death is pretty important.. maybe only make this happen for teh brains - if (m_DeathSound) { m_DeathSound->Play(m_Pos); } - - // Gib all the regular gibs - MOSRotating::GibThis(impactImpulse, movableObjectToIgnore); - - // Throw out all the inventory with the appropriate force and directions - MovableObject *pObject = 0; - Actor *pPassenger = 0; - float velMin, velRange, angularVel; - Vector gibROffset, gibVel; - for (std::deque::iterator gItr = m_Inventory.begin(); gItr != m_Inventory.end(); ++gItr) - { - // Get handy handle to the object we're putting - pObject = *gItr; - - // Generate the velocities procedurally - velMin = m_GibBlastStrength / (pObject->GetMass() != 0 ? pObject->GetMass() : 0.0001F); - velRange = 10.0F; - - // Randomize the offset from center to be within the original object - gibROffset.SetXY(m_SpriteRadius * 0.35F * RandomNormalNum(), m_SpriteRadius * 0.35F * RandomNormalNum()); - // Set up its position and velocity according to the parameters of this AEmitter. - pObject->SetPos(m_Pos + gibROffset/*Vector(m_Pos.m_X + 5 * NormalRand(), m_Pos.m_Y + 5 * NormalRand())*/); - pObject->SetRotAngle(m_Rotation.GetRadAngle() + pObject->GetRotMatrix().GetRadAngle()); - // Rotational angle - pObject->SetAngularVel((pObject->GetAngularVel() * 0.35F) + (pObject->GetAngularVel() * 0.65F / (pObject->GetMass() != 0 ? pObject->GetMass() : 0.0001F)) * RandomNum()); - // Make it rotate away in the appropriate direction depending on which side of the object it is on - // If the object is far to the relft or right of the center, make it always rotate outwards to some degree - if (gibROffset.m_X > m_aSprite[0]->w / 3) - { - float offCenterRatio = gibROffset.m_X / (m_aSprite[0]->w / 2); - angularVel = fabs(pObject->GetAngularVel() * 0.5F); - angularVel += fabs(pObject->GetAngularVel() * 0.5F * offCenterRatio); - pObject->SetAngularVel(angularVel * (gibROffset.m_X > 0 ? -1 : 1)); - } - // Gib is too close to center to always make it rotate in one direction, so give it a baseline rotation and then randomize - else - { - pObject->SetAngularVel((pObject->GetAngularVel() * 0.5F + pObject->GetAngularVel() * RandomNum()) * (RandomNormalNum() > 0.0F ? 1.0F : -1.0F)); - } - -// TODO: Optimize making the random angles!") - gibVel = gibROffset; - if (gibVel.IsZero()) - gibVel.SetXY(velMin + RandomNum(0.0F, velRange), 0.0F); - else - gibVel.SetMagnitude(velMin + RandomNum(0.0F, velRange)); - gibVel.RadRotate(impactImpulse.GetAbsRadAngle()); -// Don't! the offset was already rotated! -// gibVel = RotateOffset(gibVel); - // Distribute any impact implse out over all the gibs -// gibVel += (impactImpulse / m_Gibs.size()) / pObject->GetMass(); - pObject->SetVel(m_Vel + gibVel); - // Reset all the timers of the object being shot out so it doesn't emit a bunch of particles that have been backed up while dormant in inventory - pObject->ResetAllTimers(); - - // Set the gib to not hit a specific MO - if (movableObjectToIgnore) - pObject->SetWhichMOToNotHit(movableObjectToIgnore); - - // Detect whether we're dealing with a passenger and add it as Actor instead - if (pPassenger = dynamic_cast(pObject)) - { - pPassenger->SetRotAngle(c_HalfPI * RandomNormalNum()); - pPassenger->SetAngularVel(pPassenger->GetAngularVel() * 5.0F); - pPassenger->SetHFlipped(RandomNum() > 0.5F); - pPassenger->SetStatus(UNSTABLE); - g_MovableMan.AddActor(pPassenger); - } - // Add the gib to the scene, passing ownership from the inventory - else - g_MovableMan.AddParticle(pObject); - - pPassenger = 0; - pObject = 0; - } - - // We have exhausted all teh inventory into the scene, passing ownership - m_Inventory.clear(); - - // If this is the actual brain of any player, flash that player's screen when he's now dead - if (g_SettingsMan.FlashOnBrainDamage() && g_ActivityMan.IsInActivity()) - { - int brainOfPlayer = g_ActivityMan.GetActivity()->IsBrainOfWhichPlayer(this); - // Only flash if player is human (AI players don't have screens!) - if (brainOfPlayer != Players::NoPlayer && g_ActivityMan.GetActivity()->PlayerHuman(brainOfPlayer)) - { - // Croaked.. flash for a longer period - if (m_ToDelete || m_Status == DEAD) - g_FrameMan.FlashScreen(g_ActivityMan.GetActivity()->ScreenOfPlayer(brainOfPlayer), g_WhiteColor, 500); + float Actor::EstimateDigStrength() const { + return m_AIBaseDigStrength; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: VerifyMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Verifieis whether all actor's MO has correct IDs. Should be used in Debug mode only. + + void Actor::VerifyMOIDs() { + std::vector MOIDs; + GetMOIDs(MOIDs); + + for (std::vector::iterator it = MOIDs.begin(); it != MOIDs.end(); it++) { + RTEAssert(*it == g_NoMOID || *it < g_MovableMan.GetMOIDCount(), "Invalid MOID in actor"); } } -} + ////////////////////////////////////////////////////////////////////////////////////////// + + void Actor::OnNewMovePath() { + if (!m_MovePath.empty()) { + // Remove the first one; it's our position + m_PrevPathTarget = m_MovePath.front(); + m_MovePath.pop_front(); + // Also remove the one after that; it may move in opposite direction since it heads to the nearest PathNode center + // Unless it is the last one, in which case it shouldn't be removed + if (m_MovePath.size() > 1) { + m_PrevPathTarget = m_MovePath.front(); + m_MovePath.pop_front(); + } + } else if (m_pMOMoveTarget) { + m_MoveTarget = m_pMOMoveTarget->GetPos(); + } else { + // Nowhere to gooooo + m_MoveTarget = m_PrevPathTarget = m_Pos; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. - -bool Actor::CollideAtPoint(HitData &hd) -{ - return MOSRotating::CollideAtPoint(hd); - -// if (hd.ResImpulse[HITEE].MagnitudeIsGreaterThan(GetMaterial().strength)) { -// m_pParent-> -// } -/* Obsolete - // Set item as being reached if it collides with us - if (hd.Body[HITOR]->IsHeldDevice()) - m_pItemInReach = dynamic_cast(hd.Body[HITOR]); -*/ -// if (Status != ACTIVE) -} + ////////////////////////////////////////////////////////////////////////////////////////// + void Actor::PreControllerUpdate() { + if (m_PathRequest && m_PathRequest->complete) { + m_MovePath = const_cast&>(m_PathRequest->path); + m_PathRequest.reset(); + OnNewMovePath(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ParticlePenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Determines whether a particle which has hit this MO will penetrate, -// and if so, whether it gets lodged or exits on the other side of this -// MO. Appropriate effects will be determined and applied ONLY IF there -// was penetration! If not, nothing will be affected. - -bool Actor::ParticlePenetration(HitData &hd) { - bool penetrated = MOSRotating::ParticlePenetration(hd); - - MovableObject *hitor = hd.Body[HITOR]; - float damageToAdd = hitor->DamageOnCollision(); - damageToAdd += penetrated ? hitor->DamageOnPenetration() : 0; - if (hitor->GetApplyWoundDamageOnCollision()) { damageToAdd += m_pEntryWound->GetEmitDamage() * hitor->WoundDamageMultiplier(); } - if (hitor->GetApplyWoundBurstDamageOnCollision()) { damageToAdd += m_pEntryWound->GetBurstDamage() * hitor->WoundDamageMultiplier(); } - - if (damageToAdd != 0) { m_Health = std::min(m_Health - (damageToAdd * m_DamageMultiplier), m_MaxHealth); } - if ((penetrated || damageToAdd != 0) && m_Perceptiveness > 0 && m_Health > 0) { - Vector extruded(hd.HitVel[HITOR]); - extruded.SetMagnitude(m_CharHeight); - extruded = m_Pos - extruded; - g_SceneMan.WrapPosition(extruded); - AlarmPoint(extruded); + // We update this after, because pathing requests are forced to take at least 1 frame for the sake of determinism for now. + // In future maybe we can move this back, but it doesn't make much difference (the threadpool submission overhead makes it extremely unlikely that it would complete in less time anyways) + if (m_UpdateMovePath) { + UpdateMovePath(); + } } - return penetrated; -} + ////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAIModeIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the icon bitmap associated with this' current AI mode and team. + void Actor::Update() { + ZoneScoped; -BITMAP * Actor::GetAIModeIcon() -{ - return m_apAIIcons[m_AIMode]; -} + ///////////////////////////////// + // Hit Body update and handling + MOSRotating::Update(); + m_PieMenu->Update(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLastMOWaypointID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the ID of the last set AI MO waypoint of this. If none, g_NoMOID is returned. -// Arguments: None. -// Return value: The furthest set AI MO waypoint of this. + // Update the viewpoint to be at least what the position is + m_ViewPoint = m_Pos; -MOID Actor::GetAIMOWaypointID() const -{ - if (g_MovableMan.ValidMO(m_pMOMoveTarget)) - return m_pMOMoveTarget->GetID(); - else - return g_NoMOID; -} + // "See" the location and surroundings of this actor on the unseen map + if (m_Status != Actor::INACTIVE) + Look(45 * m_Perceptiveness, g_FrameMan.GetPlayerScreenWidth() * 0.51 * m_Perceptiveness); + // Check if the MO we're following still exists, and if not, then clear the destination + if (m_pMOMoveTarget && !g_MovableMan.ValidMO(m_pMOMoveTarget)) + m_pMOMoveTarget = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateMovePath -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the path to move along to the currently set movetarget. + /////////////////////////////////////////////////////////////////////////////// + // Check for manual player-made progress made toward the set AI goal -void Actor::UpdateMovePath() -{ - if (g_SceneMan.GetScene() == nullptr) { - return; - } + if ((m_AIMode == AIMODE_GOTO || m_AIMode == AIMODE_SQUAD) && m_Controller.IsPlayerControlled() && !m_Controller.IsDisabled()) { + Vector notUsed; + // See if we are close enough to the next move target that we should grab the next in the path that is out of proximity range + Vector pathPointVec; + for (std::list::iterator lItr = m_MovePath.begin(); lItr != m_MovePath.end();) { + pathPointVec = g_SceneMan.ShortestDistance(m_Pos, *lItr); + // Make sure we are within range AND have a clear sight to the path point we're about to eliminate, or it might be around a corner + if (pathPointVec.MagnitudeIsLessThan(m_MoveProximityLimit) && !g_SceneMan.CastStrengthRay(m_Pos, pathPointVec, 5, notUsed, 0)) { + lItr++; + // Save the last one before being popped off so we can use it to check if we need to dig (if there's any material between last and current) + m_PrevPathTarget = m_MovePath.front(); + m_MovePath.pop_front(); + } else { + break; + } + } - // Estimate how much material this actor can dig through - float digStrength = EstimateDigStrength(); - - // If we're following someone/thing, then never advance waypoints until that thing disappears - if (g_MovableMan.ValidMO(m_pMOMoveTarget)) { - m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(g_SceneMan.MovePointToGround(m_Pos, m_CharHeight*0.2, 10), m_pMOMoveTarget->GetPos(), digStrength, static_cast(m_Team)); - } else { - // Do we currently have a path to a static target we would like to still pursue? - if (m_MovePath.empty()) - { - // Ok no path going, so get a new path to the next waypoint, if there is a next waypoint - if (!m_Waypoints.empty()) - { - // Make sure the path starts from the ground and not somewhere up in the air if/when dropped out of ship - m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(g_SceneMan.MovePointToGround(m_Pos, m_CharHeight*0.2, 10), m_Waypoints.front().first, digStrength, static_cast(m_Team)); - - // If the waypoint was tied to an MO to pursue, then load it into the current MO target - if (g_MovableMan.ValidMO(m_Waypoints.front().second)) { - m_pMOMoveTarget = m_Waypoints.front().second; - } else { - m_pMOMoveTarget = 0; - } - - // We loaded the waypoint, no need to keep it - m_Waypoints.pop_front(); - } - // Just try to get to the last Move Target - else { - m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(g_SceneMan.MovePointToGround(m_Pos, m_CharHeight*0.2, 10), m_MoveTarget, digStrength, static_cast(m_Team)); - } - } - // We had a path before trying to update, so use its last point as the final destination - else { - m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(g_SceneMan.MovePointToGround(m_Pos, m_CharHeight*0.2, 10), Vector(m_MovePath.back()), digStrength, static_cast(m_Team)); - } - } - - m_UpdateMovePath = false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float Actor::EstimateDigStrength() const { - return m_AIBaseDigStrength; -} + if (!m_MovePath.empty()) { + Vector notUsed; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: VerifyMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Verifieis whether all actor's MO has correct IDs. Should be used in Debug mode only. + // See if we are close enough to the last point in the current path, in which case we can toss teh whole current path and start ont he next + pathPointVec = g_SceneMan.ShortestDistance(m_Pos, m_MovePath.back()); + // Clear out the current path, the player apparently took a shortcut + if (pathPointVec.MagnitudeIsLessThan(m_MoveProximityLimit) && !g_SceneMan.CastStrengthRay(m_Pos, pathPointVec, 5, notUsed, 0, g_MaterialDoor)) { + m_MovePath.clear(); + } + } -void Actor::VerifyMOIDs() -{ - std::vector MOIDs; - GetMOIDs(MOIDs); + // If still stuff in the path, get the next point on it + if (!m_MovePath.empty()) + m_MoveTarget = m_MovePath.front(); + // No more path, so check if any more waypoints to make a new path to? This doesn't apply if we're following something + else if (m_MovePath.empty() && !m_Waypoints.empty() && !m_pMOMoveTarget) + UpdateMovePath(); + // Nope, so just conclude that we must have reached the ultimate AI target set and exit the goto mode + else if (!m_pMOMoveTarget) + m_AIMode = AIMODE_SENTRY; + } + // Save health state so we can compare next update + m_PrevHealth = m_Health; + ///////////////////////////////////// + // Take damage/heal from wounds and wounds on Attachables + for (AEmitter* wound: m_Wounds) { + m_Health -= wound->CollectDamage() * m_DamageMultiplier; + } + for (Attachable* attachable: m_Attachables) { + m_Health -= attachable->CollectDamage(); + } + m_Health = std::min(m_Health, m_MaxHealth); - for (std::vector::iterator it = MOIDs.begin(); it != MOIDs.end(); it++) - { - RTEAssert(*it == g_NoMOID || *it < g_MovableMan.GetMOIDCount(), "Invalid MOID in actor"); - } -} + ///////////////////////////// + // Stability logic -////////////////////////////////////////////////////////////////////////////////////////// + if (m_Status == STABLE) { + // If moving really fast, we're not able to be stable + if (std::abs(m_Vel.m_X) > std::abs(m_StableVel.m_X) || std::abs(m_Vel.m_Y) > std::abs(m_StableVel.m_Y)) { + m_Status = UNSTABLE; + } -void Actor::OnNewMovePath() { - if (!m_MovePath.empty()) { - // Remove the first one; it's our position - m_PrevPathTarget = m_MovePath.front(); - m_MovePath.pop_front(); - // Also remove the one after that; it may move in opposite direction since it heads to the nearest PathNode center - // Unless it is the last one, in which case it shouldn't be removed - if (m_MovePath.size() > 1) { - m_PrevPathTarget = m_MovePath.front(); - m_MovePath.pop_front(); - } - } else if (m_pMOMoveTarget) { - m_MoveTarget = m_pMOMoveTarget->GetPos(); - } else { - // Nowhere to gooooo - m_MoveTarget = m_PrevPathTarget = m_Pos; - } -} + m_StableRecoverTimer.Reset(); + } else if (m_Status == UNSTABLE) { + // Only regain stability if we're not moving too fast and it's been a while since we lost it + if (m_StableRecoverTimer.IsPastSimMS(m_StableRecoverDelay) && !(std::abs(m_Vel.m_X) > std::abs(m_StableVel.m_X) || std::abs(m_Vel.m_Y) > std::abs(m_StableVel.m_Y))) { + m_Status = STABLE; + } + } -////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////// + // Take damage from large hits during travel + + const float travelImpulseMagnitudeSqr = m_TravelImpulse.GetSqrMagnitude(); -void Actor::PreControllerUpdate() { - if (m_PathRequest && m_PathRequest->complete) { - m_MovePath = const_cast &>(m_PathRequest->path); - m_PathRequest.reset(); - OnNewMovePath(); - } + // If we're travelling at least half the speed to hurt ourselves, play the body hit noise + float halfTravelImpulseDamage = m_TravelImpulseDamage * 0.5F; + if (m_BodyHitSound && travelImpulseMagnitudeSqr > (halfTravelImpulseDamage * halfTravelImpulseDamage)) { + m_BodyHitSound->Play(m_Pos); + } - // We update this after, because pathing requests are forced to take at least 1 frame for the sake of determinism for now. - // In future maybe we can move this back, but it doesn't make much difference (the threadpool submission overhead makes it extremely unlikely that it would complete in less time anyways) - if (m_UpdateMovePath) { - UpdateMovePath(); - } -} + // But only actually damage ourselves if we're unstable + if (m_Status == Actor::UNSTABLE && travelImpulseMagnitudeSqr > (m_TravelImpulseDamage * m_TravelImpulseDamage)) { + const float impulse = std::sqrt(travelImpulseMagnitudeSqr) - m_TravelImpulseDamage; + const float damage = std::max(impulse / (m_GibImpulseLimit - m_TravelImpulseDamage) * m_MaxHealth, 0.0F); + m_Health -= damage; + m_ForceDeepCheck = true; + } -////////////////////////////////////////////////////////////////////////////////////////// + // Spread the carried items and gold around before death. + if (m_Status == DYING || m_Status == DEAD) { + // Actor may die for a long time, no need to call this more than once + if (m_Inventory.size() > 0) { + DropAllInventory(); + } + if (m_GoldCarried > 0) { + DropAllGold(); + } + } -void Actor::Update() -{ - ZoneScoped; - - ///////////////////////////////// - // Hit Body update and handling - MOSRotating::Update(); - - m_PieMenu->Update(); - - // Update the viewpoint to be at least what the position is - m_ViewPoint = m_Pos; - - // "See" the location and surroundings of this actor on the unseen map - if (m_Status != Actor::INACTIVE) - Look(45 * m_Perceptiveness, g_FrameMan.GetPlayerScreenWidth() * 0.51 * m_Perceptiveness); - - //Check if the MO we're following still exists, and if not, then clear the destination - if (m_pMOMoveTarget && !g_MovableMan.ValidMO(m_pMOMoveTarget)) - m_pMOMoveTarget = 0; - - /////////////////////////////////////////////////////////////////////////////// - // Check for manual player-made progress made toward the set AI goal - - if ((m_AIMode == AIMODE_GOTO || m_AIMode == AIMODE_SQUAD) && m_Controller.IsPlayerControlled() && !m_Controller.IsDisabled()) - { - Vector notUsed; - // See if we are close enough to the next move target that we should grab the next in the path that is out of proximity range - Vector pathPointVec; - for (std::list::iterator lItr = m_MovePath.begin(); lItr != m_MovePath.end();) - { - pathPointVec = g_SceneMan.ShortestDistance(m_Pos, *lItr); - // Make sure we are within range AND have a clear sight to the path point we're about to eliminate, or it might be around a corner - if (pathPointVec.MagnitudeIsLessThan(m_MoveProximityLimit) && !g_SceneMan.CastStrengthRay(m_Pos, pathPointVec, 5, notUsed, 0)) - { - lItr++; - // Save the last one before being popped off so we can use it to check if we need to dig (if there's any material between last and current) - m_PrevPathTarget = m_MovePath.front(); - m_MovePath.pop_front(); - } else { - break; - } - } - - if (!m_MovePath.empty()) - { - Vector notUsed; + //////////////////////////////// + // Death logic - // See if we are close enough to the last point in the current path, in which case we can toss teh whole current path and start ont he next - pathPointVec = g_SceneMan.ShortestDistance(m_Pos, m_MovePath.back()); - // Clear out the current path, the player apparently took a shortcut - if (pathPointVec.MagnitudeIsLessThan(m_MoveProximityLimit) && !g_SceneMan.CastStrengthRay(m_Pos, pathPointVec, 5, notUsed, 0, g_MaterialDoor)) { - m_MovePath.clear(); - } - } - - // If still stuff in the path, get the next point on it - if (!m_MovePath.empty()) - m_MoveTarget = m_MovePath.front(); - // No more path, so check if any more waypoints to make a new path to? This doesn't apply if we're following something - else if (m_MovePath.empty() && !m_Waypoints.empty() && !m_pMOMoveTarget) - UpdateMovePath(); - // Nope, so just conclude that we must have reached the ultimate AI target set and exit the goto mode - else if (!m_pMOMoveTarget) - m_AIMode = AIMODE_SENTRY; - } - // Save health state so we can compare next update - m_PrevHealth = m_Health; - ///////////////////////////////////// - // Take damage/heal from wounds and wounds on Attachables - for (AEmitter *wound : m_Wounds) { - m_Health -= wound->CollectDamage() * m_DamageMultiplier; - } - for (Attachable *attachable : m_Attachables) { - m_Health -= attachable->CollectDamage(); - } - m_Health = std::min(m_Health, m_MaxHealth); - - ///////////////////////////// - // Stability logic - - if (m_Status == STABLE) { - // If moving really fast, we're not able to be stable - if (std::abs(m_Vel.m_X) > std::abs(m_StableVel.m_X) || std::abs(m_Vel.m_Y) > std::abs(m_StableVel.m_Y)) { m_Status = UNSTABLE; } - - m_StableRecoverTimer.Reset(); - } - else if (m_Status == UNSTABLE) { - // Only regain stability if we're not moving too fast and it's been a while since we lost it - if (m_StableRecoverTimer.IsPastSimMS(m_StableRecoverDelay) && !(std::abs(m_Vel.m_X) > std::abs(m_StableVel.m_X) || std::abs(m_Vel.m_Y) > std::abs(m_StableVel.m_Y))) { m_Status = STABLE; } - } - - ///////////////////////////////////////////// - // Take damage from large hits during travel - - const float travelImpulseMagnitudeSqr = m_TravelImpulse.GetSqrMagnitude(); - - // If we're travelling at least half the speed to hurt ourselves, play the body hit noise - float halfTravelImpulseDamage = m_TravelImpulseDamage * 0.5F; - if (m_BodyHitSound && travelImpulseMagnitudeSqr > (halfTravelImpulseDamage * halfTravelImpulseDamage)) { m_BodyHitSound->Play(m_Pos); } - - // But only actually damage ourselves if we're unstable - if (m_Status == Actor::UNSTABLE && travelImpulseMagnitudeSqr > (m_TravelImpulseDamage * m_TravelImpulseDamage)) { - const float impulse = std::sqrt(travelImpulseMagnitudeSqr) - m_TravelImpulseDamage; - const float damage = std::max(impulse / (m_GibImpulseLimit - m_TravelImpulseDamage) * m_MaxHealth, 0.0F); - m_Health -= damage; - m_ForceDeepCheck = true; - } + if (m_Status != DYING && m_Status != DEAD && m_Health <= 0) { + if (m_DeathSound) { + m_DeathSound->Play(m_Pos); + } + DropAllInventory(); + m_Status = DYING; + m_DeathTmr.Reset(); + } - // Spread the carried items and gold around before death. - if (m_Status == DYING || m_Status == DEAD) { - // Actor may die for a long time, no need to call this more than once - if (m_Inventory.size() > 0) { DropAllInventory(); } - if (m_GoldCarried > 0) { DropAllGold(); } - } + // Prevent dead actors from rotating like mad + if (m_Status == DYING || m_Status == DEAD) { + m_AngularVel = m_AngularVel * 0.98F; + } - //////////////////////////////// - // Death logic + if (m_Status == DYING && m_DeathTmr.GetElapsedSimTimeMS() > 1000) { + m_Status = DEAD; + } - if (m_Status != DYING && m_Status != DEAD && m_Health <= 0) { - if (m_DeathSound) { m_DeathSound->Play(m_Pos); } - DropAllInventory(); - m_Status = DYING; - m_DeathTmr.Reset(); - } + ////////////////////////////////////////////////////// + // Save previous second's position so we can detect larger movement - // Prevent dead actors from rotating like mad - if (m_Status == DYING || m_Status == DEAD) { m_AngularVel = m_AngularVel * 0.98F; } - - if (m_Status == DYING && m_DeathTmr.GetElapsedSimTimeMS() > 1000) { m_Status = DEAD; } - - ////////////////////////////////////////////////////// - // Save previous second's position so we can detect larger movement - - if (m_LastSecondTimer.IsPastSimMS(1000)) - { - m_RecentMovement = m_Pos - m_LastSecondPos; - m_LastSecondPos = m_Pos; - m_LastSecondTimer.Reset(); - } - - //////////////////////////////////////// - // Animate the sprite, if applicable - - if (m_FrameCount > 1) - { - if (m_SpriteAnimMode == LOOPWHENACTIVE) - { - if (m_Controller.IsState(MOVE_LEFT) || m_Controller.IsState(MOVE_RIGHT) || m_Controller.GetAnalogMove().GetLargest() > 0.1) - { -// TODO: improve; make this - float cycleTime = ((long)m_SpriteAnimTimer.GetElapsedSimTimeMS()) % m_SpriteAnimDuration; - m_Frame = std::floor((cycleTime / (float)m_SpriteAnimDuration) * (float)m_FrameCount); - } - } - } - - ///////////////////////////////// - // Misc - - // If in AI setting mode prior to actor switch, made the team rosters get sorted so the lines are drawn correctly - if (m_Controller.IsState(PIE_MENU_ACTIVE)) - { - g_MovableMan.SortTeamRoster(m_Team); - } - - // Play PainSound if damage this frame exceeded PainThreshold - if (m_PainThreshold > 0 && m_PrevHealth - m_Health > m_PainThreshold && m_Health > 1 && m_PainSound) { m_PainSound->Play(m_Pos); } - - int brainOfPlayer = g_ActivityMan.GetActivity()->IsBrainOfWhichPlayer(this); - if (brainOfPlayer != Players::NoPlayer && g_ActivityMan.GetActivity()->PlayerHuman(brainOfPlayer)) { - if (m_PrevHealth - m_Health > 1.5F) { - // If this is a brain that's under attack, broadcast an alarm event so that the enemy AI won't dawdle in trying to kill it. - g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, 0.5F)); - if (g_SettingsMan.FlashOnBrainDamage()) { - g_FrameMan.FlashScreen(g_ActivityMan.GetActivity()->ScreenOfPlayer(brainOfPlayer), g_RedColor, 10); + if (m_LastSecondTimer.IsPastSimMS(1000)) { + m_RecentMovement = m_Pos - m_LastSecondPos; + m_LastSecondPos = m_Pos; + m_LastSecondTimer.Reset(); + } + + //////////////////////////////////////// + // Animate the sprite, if applicable + + if (m_FrameCount > 1) { + if (m_SpriteAnimMode == LOOPWHENACTIVE) { + if (m_Controller.IsState(MOVE_LEFT) || m_Controller.IsState(MOVE_RIGHT) || m_Controller.GetAnalogMove().GetLargest() > 0.1) { + // TODO: improve; make this + float cycleTime = ((long)m_SpriteAnimTimer.GetElapsedSimTimeMS()) % m_SpriteAnimDuration; + m_Frame = std::floor((cycleTime / (float)m_SpriteAnimDuration) * (float)m_FrameCount); + } } } - if ((m_ToDelete || m_Status == DEAD) && g_SettingsMan.FlashOnBrainDamage()) { - g_FrameMan.FlashScreen(g_ActivityMan.GetActivity()->ScreenOfPlayer(brainOfPlayer), g_WhiteColor, 500); + + ///////////////////////////////// + // Misc + + // If in AI setting mode prior to actor switch, made the team rosters get sorted so the lines are drawn correctly + if (m_Controller.IsState(PIE_MENU_ACTIVE)) { + g_MovableMan.SortTeamRoster(m_Team); } - } -// Do NOT mess witht he HUD stack in update... it should only be altered in DrawHUD, or it will jitter when multiple sim updates happen -// m_HUDStack = -m_CharHeight / 2; - -/* -// *** TEMP Hack for testing animation - int bajs = m_aSprite->GetVelX(); - bajs %= 5; - m_aSprite->SetVelX(++bajs); - - if (bajs == 1) - { - int frame = m_aSprite->GetFrame(); - if (++frame >= 7) - frame = 1; - m_aSprite->SetFrame(frame); - } -*/ -} - -void Actor::FullUpdate() { - PreControllerUpdate(); - m_Controller.Update(); - Update(); -} + // Play PainSound if damage this frame exceeded PainThreshold + if (m_PainThreshold > 0 && m_PrevHealth - m_Health > m_PainThreshold && m_Health > 1 && m_PainSound) { + m_PainSound->Play(m_Pos); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Actor's current graphical HUD overlay representation to a -// BITMAP of choice. - -void Actor::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScreen, bool playerControlled) -{ - // This should indeed be a local var and not alter a member one in a draw func! Can cause nasty jittering etc if multiple sim updates are done without a drawing in between etc - m_HUDStack = -m_CharHeight / 2; - - // Only do HUD if on a team - if (m_Team < 0) - return; - - // Only draw if the team viewing this is on the same team OR has seen the space where this is located. - int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); - if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam && (!g_SettingsMan.ShowEnemyHUD() || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam))) { - return; - } + int brainOfPlayer = g_ActivityMan.GetActivity()->IsBrainOfWhichPlayer(this); + if (brainOfPlayer != Players::NoPlayer && g_ActivityMan.GetActivity()->PlayerHuman(brainOfPlayer)) { + if (m_PrevHealth - m_Health > 1.5F) { + // If this is a brain that's under attack, broadcast an alarm event so that the enemy AI won't dawdle in trying to kill it. + g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, 0.5F)); + if (g_SettingsMan.FlashOnBrainDamage()) { + g_FrameMan.FlashScreen(g_ActivityMan.GetActivity()->ScreenOfPlayer(brainOfPlayer), g_RedColor, 10); + } + } + if ((m_ToDelete || m_Status == DEAD) && g_SettingsMan.FlashOnBrainDamage()) { + g_FrameMan.FlashScreen(g_ActivityMan.GetActivity()->ScreenOfPlayer(brainOfPlayer), g_WhiteColor, 500); + } + } - // Draw stat info HUD - char str[64]; - - GUIFont *pSymbolFont = g_FrameMan.GetLargeFont(); - GUIFont *pSmallFont = g_FrameMan.GetSmallFont(); - Vector drawPos = m_Pos - targetPos; - Vector cpuPos = GetCPUPos() - targetPos; - - // If we have something to draw, adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam - if ((m_HUDVisible || m_PieMenu->IsVisible()) && !targetPos.IsZero()) - { - // Spans vertical scene seam - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) - { - if ((targetPos.m_X < 0) && (m_Pos.m_X > (sceneWidth - pTargetBitmap->w))) - { - drawPos.m_X -= sceneWidth; - cpuPos.m_X -= sceneWidth; - } - else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_Pos.m_X < pTargetBitmap->w)) - { - drawPos.m_X += sceneWidth; - cpuPos.m_X += sceneWidth; - } - } - // Spans horizontal scene seam - int sceneHeight = g_SceneMan.GetSceneHeight(); - if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) - { - if ((targetPos.m_Y < 0) && (m_Pos.m_Y > (sceneHeight - pTargetBitmap->h))) - { - drawPos.m_Y -= sceneHeight; - cpuPos.m_Y -= sceneHeight; - } - else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_Pos.m_Y < pTargetBitmap->h)) - { - drawPos.m_Y += sceneHeight; - cpuPos.m_Y += sceneHeight; - } - } - } - - int actorScreen = g_ActivityMan.GetActivity() ? g_ActivityMan.GetActivity()->ScreenOfPlayer(m_Controller.GetPlayer()) : -1; - bool screenTeamIsSameAsActorTeam = g_ActivityMan.GetActivity() ? g_ActivityMan.GetActivity()->GetTeamOfPlayer(whichScreen) == m_Team : true; - if (m_PieMenu->IsVisible() && screenTeamIsSameAsActorTeam && (!m_PieMenu->IsInNormalAnimationMode() || (m_Controller.IsPlayerControlled() && actorScreen == whichScreen))) { - m_PieMenu->Draw(pTargetBitmap, targetPos); + // Do NOT mess witht he HUD stack in update... it should only be altered in DrawHUD, or it will jitter when multiple sim updates happen + // m_HUDStack = -m_CharHeight / 2; + + /* + // *** TEMP Hack for testing animation + int bajs = m_aSprite->GetVelX(); + bajs %= 5; + m_aSprite->SetVelX(++bajs); + + if (bajs == 1) + { + int frame = m_aSprite->GetFrame(); + if (++frame >= 7) + frame = 1; + m_aSprite->SetFrame(frame); + } + */ } - if (!m_HUDVisible) { - return; + void Actor::FullUpdate() { + PreControllerUpdate(); + m_Controller.Update(); + Update(); } - // Draw the selection arrow, if controlled and under the arrow's time limit - if (m_Controller.IsPlayerControlled() && m_NewControlTmr.GetElapsedSimTimeMS() < ARROWTIME) - { - // Draw the appropriate selection arrow color based on player team - draw_sprite(pTargetBitmap, m_apSelectArrow[m_Team], cpuPos.m_X, EaseOut(drawPos.m_Y + m_HUDStack - 60, drawPos.m_Y + m_HUDStack - 20, m_NewControlTmr.GetElapsedSimTimeMS() / (float)ARROWTIME)); - } - - // Draw the alarm exclamation mark if we are alarmed! - if (m_AlarmTimer.SimTimeLimitProgress() < 0.25) - draw_sprite(pTargetBitmap, m_apAlarmExclamation[m_AgeTimer.AlternateSim(100)], cpuPos.m_X - 3, EaseOut(drawPos.m_Y + m_HUDStack - 10, drawPos.m_Y + m_HUDStack - 25, m_AlarmTimer.SimTimeLimitProgress() / 0.25f)); - - if (pSmallFont && pSymbolFont) - { - AllegroBitmap bitmapInt(pTargetBitmap); - - if (!m_Controller.IsState(PIE_MENU_ACTIVE) || actorScreen != whichScreen) - { - // If we're still alive, show the team colors - if (m_Health > 0) - { - if (IsPlayerControlled() && g_FrameMan.IsInMultiplayerMode()) - { - m_pControllerIcon = 0; - if (m_Team == 0) - m_pControllerIcon = g_UInputMan.GetDeviceIcon(DEVICE_GAMEPAD_1); - else if (m_Team == 1) - m_pControllerIcon = g_UInputMan.GetDeviceIcon(DEVICE_GAMEPAD_2); - else if (m_Team == 2) - m_pControllerIcon = g_UInputMan.GetDeviceIcon(DEVICE_GAMEPAD_3); - else if (m_Team == 3) - m_pControllerIcon = g_UInputMan.GetDeviceIcon(DEVICE_GAMEPAD_4); - if (m_pControllerIcon) - { - std::vector apControllerBitmaps = m_pControllerIcon->GetBitmaps8(); - - masked_blit(apControllerBitmaps[0], pTargetBitmap, 0, 0, drawPos.m_X - apControllerBitmaps[0]->w - 2 + 10, drawPos.m_Y + m_HUDStack - (apControllerBitmaps[0]->h / 2) + 8, apControllerBitmaps[0]->w, apControllerBitmaps[0]->h); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Actor's current graphical HUD overlay representation to a + // BITMAP of choice. + + void Actor::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { + // This should indeed be a local var and not alter a member one in a draw func! Can cause nasty jittering etc if multiple sim updates are done without a drawing in between etc + m_HUDStack = -m_CharHeight / 2; + + // Only do HUD if on a team + if (m_Team < 0) + return; + + // Only draw if the team viewing this is on the same team OR has seen the space where this is located. + int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); + if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam && (!g_SettingsMan.ShowEnemyHUD() || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam))) { + return; + } + + // Draw stat info HUD + char str[64]; + + GUIFont* pSymbolFont = g_FrameMan.GetLargeFont(); + GUIFont* pSmallFont = g_FrameMan.GetSmallFont(); + Vector drawPos = m_Pos - targetPos; + Vector cpuPos = GetCPUPos() - targetPos; + + // If we have something to draw, adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam + if ((m_HUDVisible || m_PieMenu->IsVisible()) && !targetPos.IsZero()) { + // Spans vertical scene seam + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) { + if ((targetPos.m_X < 0) && (m_Pos.m_X > (sceneWidth - pTargetBitmap->w))) { + drawPos.m_X -= sceneWidth; + cpuPos.m_X -= sceneWidth; + } else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_Pos.m_X < pTargetBitmap->w)) { + drawPos.m_X += sceneWidth; + cpuPos.m_X += sceneWidth; + } + } + // Spans horizontal scene seam + int sceneHeight = g_SceneMan.GetSceneHeight(); + if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) { + if ((targetPos.m_Y < 0) && (m_Pos.m_Y > (sceneHeight - pTargetBitmap->h))) { + drawPos.m_Y -= sceneHeight; + cpuPos.m_Y -= sceneHeight; + } else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_Pos.m_Y < pTargetBitmap->h)) { + drawPos.m_Y += sceneHeight; + cpuPos.m_Y += sceneHeight; } + } + } + + int actorScreen = g_ActivityMan.GetActivity() ? g_ActivityMan.GetActivity()->ScreenOfPlayer(m_Controller.GetPlayer()) : -1; + bool screenTeamIsSameAsActorTeam = g_ActivityMan.GetActivity() ? g_ActivityMan.GetActivity()->GetTeamOfPlayer(whichScreen) == m_Team : true; + if (m_PieMenu->IsVisible() && screenTeamIsSameAsActorTeam && (!m_PieMenu->IsInNormalAnimationMode() || (m_Controller.IsPlayerControlled() && actorScreen == whichScreen))) { + m_PieMenu->Draw(pTargetBitmap, targetPos); + } + + if (!m_HUDVisible) { + return; + } - // Get the Icon bitmaps of this Actor's team, if any - std::vector apIconBitmaps; - if (m_pTeamIcon) - apIconBitmaps = m_pTeamIcon->GetBitmaps8(); - - // Team Icon could not be found, or of no team, so use the static noteam Icon instead - if (apIconBitmaps.empty()) - apIconBitmaps = m_apNoTeamIcon; - - // Now draw the Icon if we can - if (!apIconBitmaps.empty() && m_pTeamIcon && m_pTeamIcon->GetFrameCount() > 0) - { - // Make team icon blink faster as the health goes down - int f = m_HeartBeat.AlternateReal(200 + 800 * (m_Health / 100)) ? 0 : 1; - f = MIN(f, m_pTeamIcon ? m_pTeamIcon->GetFrameCount() - 1 : 1); - masked_blit(apIconBitmaps.at(f), pTargetBitmap, 0, 0, drawPos.m_X - apIconBitmaps.at(f)->w - 2, drawPos.m_Y + m_HUDStack - (apIconBitmaps.at(f)->h / 2) + 8, apIconBitmaps.at(f)->w, apIconBitmaps.at(f)->h); - } - } - // Draw death icon - else - { - str[0] = -39; - str[1] = 0; - pSymbolFont->DrawAligned(&bitmapInt, drawPos.m_X - 10, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); - } - -/* Obsolete red/gren heart Team icon - // Health - if (m_HeartBeat.GetElapsedSimTimeMS() > (m_Health > 90 ? 850 : (m_Health > 25 ? 350 : 100)) || m_Health <= 0) - { - str[0] = m_Health > 0 ? (m_Team == 0 ? -64 : -61) : -39; - str[1] = 0; - pSymbolFont->DrawAligned(&bitmapInt, drawPos.m_X - 10, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); - if (m_HeartBeat.GetElapsedSimTimeMS() > (m_Health > 90 ? 950 : (m_Health > 25 ? 500 : 175))) - m_HeartBeat.Reset(); - } - else - { - str[0] = m_Team == 0 ? -63 : -60; - str[1] = 0; - pSymbolFont->DrawAligned(&bitmapInt, drawPos.m_X - 11, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); - } -*/ - std::snprintf(str, sizeof(str), "%.0f", std::ceil(m_Health)); - pSymbolFont->DrawAligned(&bitmapInt, drawPos.m_X - 0, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); - - m_HUDStack += -12; - - if (IsPlayerControlled()) { - if (GetGoldCarried() > 0) { - str[0] = m_GoldPicked ? -57 : -58; str[1] = 0; - pSymbolFont->DrawAligned(&bitmapInt, drawPos.GetFloorIntX() - 11, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Left); - std::snprintf(str, sizeof(str), "%.0f oz", GetGoldCarried()); - pSmallFont->DrawAligned(&bitmapInt, drawPos.GetFloorIntX() - 0, drawPos.GetFloorIntY() + m_HUDStack + 2, str, GUIFont::Left); - - m_HUDStack -= 11; + // Draw the selection arrow, if controlled and under the arrow's time limit + if (m_Controller.IsPlayerControlled() && m_NewControlTmr.GetElapsedSimTimeMS() < ARROWTIME) { + // Draw the appropriate selection arrow color based on player team + draw_sprite(pTargetBitmap, m_apSelectArrow[m_Team], cpuPos.m_X, EaseOut(drawPos.m_Y + m_HUDStack - 60, drawPos.m_Y + m_HUDStack - 20, m_NewControlTmr.GetElapsedSimTimeMS() / (float)ARROWTIME)); + } + + // Draw the alarm exclamation mark if we are alarmed! + if (m_AlarmTimer.SimTimeLimitProgress() < 0.25) + draw_sprite(pTargetBitmap, m_apAlarmExclamation[m_AgeTimer.AlternateSim(100)], cpuPos.m_X - 3, EaseOut(drawPos.m_Y + m_HUDStack - 10, drawPos.m_Y + m_HUDStack - 25, m_AlarmTimer.SimTimeLimitProgress() / 0.25f)); + + if (pSmallFont && pSymbolFont) { + AllegroBitmap bitmapInt(pTargetBitmap); + + if (!m_Controller.IsState(PIE_MENU_ACTIVE) || actorScreen != whichScreen) { + // If we're still alive, show the team colors + if (m_Health > 0) { + if (IsPlayerControlled() && g_FrameMan.IsInMultiplayerMode()) { + m_pControllerIcon = 0; + if (m_Team == 0) + m_pControllerIcon = g_UInputMan.GetDeviceIcon(DEVICE_GAMEPAD_1); + else if (m_Team == 1) + m_pControllerIcon = g_UInputMan.GetDeviceIcon(DEVICE_GAMEPAD_2); + else if (m_Team == 2) + m_pControllerIcon = g_UInputMan.GetDeviceIcon(DEVICE_GAMEPAD_3); + else if (m_Team == 3) + m_pControllerIcon = g_UInputMan.GetDeviceIcon(DEVICE_GAMEPAD_4); + if (m_pControllerIcon) { + std::vector apControllerBitmaps = m_pControllerIcon->GetBitmaps8(); + + masked_blit(apControllerBitmaps[0], pTargetBitmap, 0, 0, drawPos.m_X - apControllerBitmaps[0]->w - 2 + 10, drawPos.m_Y + m_HUDStack - (apControllerBitmaps[0]->h / 2) + 8, apControllerBitmaps[0]->w, apControllerBitmaps[0]->h); + } + } + + // Get the Icon bitmaps of this Actor's team, if any + std::vector apIconBitmaps; + if (m_pTeamIcon) + apIconBitmaps = m_pTeamIcon->GetBitmaps8(); + + // Team Icon could not be found, or of no team, so use the static noteam Icon instead + if (apIconBitmaps.empty()) + apIconBitmaps = m_apNoTeamIcon; + + // Now draw the Icon if we can + if (!apIconBitmaps.empty() && m_pTeamIcon && m_pTeamIcon->GetFrameCount() > 0) { + // Make team icon blink faster as the health goes down + int f = m_HeartBeat.AlternateReal(200 + 800 * (m_Health / 100)) ? 0 : 1; + f = MIN(f, m_pTeamIcon ? m_pTeamIcon->GetFrameCount() - 1 : 1); + masked_blit(apIconBitmaps.at(f), pTargetBitmap, 0, 0, drawPos.m_X - apIconBitmaps.at(f)->w - 2, drawPos.m_Y + m_HUDStack - (apIconBitmaps.at(f)->h / 2) + 8, apIconBitmaps.at(f)->w, apIconBitmaps.at(f)->h); + } } - // Player name - if (g_FrameMan.IsInMultiplayerMode()) { - if (GameActivity * gameActivity = dynamic_cast(g_ActivityMan.GetActivity())) { - pSmallFont->DrawAligned(&bitmapInt, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 2, gameActivity->GetNetworkPlayerName(m_Controller.GetPlayer()).c_str(), GUIFont::Centre); + // Draw death icon + else { + str[0] = -39; + str[1] = 0; + pSymbolFont->DrawAligned(&bitmapInt, drawPos.m_X - 10, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); + } + + /* Obsolete red/gren heart Team icon + // Health + if (m_HeartBeat.GetElapsedSimTimeMS() > (m_Health > 90 ? 850 : (m_Health > 25 ? 350 : 100)) || m_Health <= 0) + { + str[0] = m_Health > 0 ? (m_Team == 0 ? -64 : -61) : -39; + str[1] = 0; + pSymbolFont->DrawAligned(&bitmapInt, drawPos.m_X - 10, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); + if (m_HeartBeat.GetElapsedSimTimeMS() > (m_Health > 90 ? 950 : (m_Health > 25 ? 500 : 175))) + m_HeartBeat.Reset(); + } + else + { + str[0] = m_Team == 0 ? -63 : -60; + str[1] = 0; + pSymbolFont->DrawAligned(&bitmapInt, drawPos.m_X - 11, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); + } + */ + std::snprintf(str, sizeof(str), "%.0f", std::ceil(m_Health)); + pSymbolFont->DrawAligned(&bitmapInt, drawPos.m_X - 0, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); + + m_HUDStack += -12; + + if (IsPlayerControlled()) { + if (GetGoldCarried() > 0) { + str[0] = m_GoldPicked ? -57 : -58; + str[1] = 0; + pSymbolFont->DrawAligned(&bitmapInt, drawPos.GetFloorIntX() - 11, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Left); + std::snprintf(str, sizeof(str), "%.0f oz", GetGoldCarried()); + pSmallFont->DrawAligned(&bitmapInt, drawPos.GetFloorIntX() - 0, drawPos.GetFloorIntY() + m_HUDStack + 2, str, GUIFont::Left); + m_HUDStack -= 11; } + // Player name + if (g_FrameMan.IsInMultiplayerMode()) { + if (GameActivity* gameActivity = dynamic_cast(g_ActivityMan.GetActivity())) { + pSmallFont->DrawAligned(&bitmapInt, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 2, gameActivity->GetNetworkPlayerName(m_Controller.GetPlayer()).c_str(), GUIFont::Centre); + m_HUDStack -= 11; + } + } } + /* Obsolete + // Draw the contol pointer, if controlled and under the icon's time limit + if (m_Controller.IsPlayetControlled() && m_NewControlTmr.GetElapsedSimTimeMS() < 1500) + { + std::snprintf(str, sizeof(str), "%c", -38); + pSymbolFont->DrawAligned(&bitmapInt, cpuPos.m_X - 0, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); + } + */ + } + } + + // Don't proceed to draw all the secret stuff below if this screen is for a player on the other team! + if (g_ActivityMan.GetActivity() && g_ActivityMan.GetActivity()->GetTeamOfPlayer(whichScreen) != m_Team) + return; + + // AI waypoints or points of interest + if (m_DrawWaypoints && m_PlayerControllable && (m_AIMode == AIMODE_GOTO || m_AIMode == AIMODE_SQUAD)) { + // Draw the AI paths, from the ultimate destination back up to the actor's position. + // We do this backwards so the lines won't crawl and the dots can be evenly spaced throughout + Vector waypoint; + std::list>::reverse_iterator vLast, vItr; + std::list::reverse_iterator lLast, lItr; + int skipPhase = 0; + + // Draw the line between the end of the movepath and the first waypoint after that, if any + if (!m_Waypoints.empty()) { + // Draw the first destination/waypoint point + // waypoint = m_MoveTarget - targetPos; + // circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 2, g_YellowGlowColor); + + // Draw the additional waypoint points beyond the first one + vLast = m_Waypoints.rbegin(); + vItr = m_Waypoints.rbegin(); + for (; vItr != m_Waypoints.rend(); ++vItr) { + // Draw the line + g_FrameMan.DrawLine(pTargetBitmap, (*vLast).first - targetPos, (*vItr).first - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, 0, true); + vLast = vItr; + + // Draw the points + waypoint = (*vItr).first - targetPos; + circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 2, g_YellowGlowColor); + // Add pixel glow area around it, in scene coordinates + g_PostProcessMan.RegisterGlowArea((*vItr).first, 5); + } + + // Draw line from the last movetarget on the current path to the first waypoint in queue after that + if (!m_MovePath.empty()) + g_FrameMan.DrawLine(pTargetBitmap, m_MovePath.back() - targetPos, m_Waypoints.front().first - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, 0, true); + else + g_FrameMan.DrawLine(pTargetBitmap, m_MoveTarget - targetPos, m_Waypoints.front().first - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, 0, true); } -/* Obsolete - // Draw the contol pointer, if controlled and under the icon's time limit - if (m_Controller.IsPlayetControlled() && m_NewControlTmr.GetElapsedSimTimeMS() < 1500) - { - std::snprintf(str, sizeof(str), "%c", -38); - pSymbolFont->DrawAligned(&bitmapInt, cpuPos.m_X - 0, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); - } -*/ - } - } - - // Don't proceed to draw all the secret stuff below if this screen is for a player on the other team! - if (g_ActivityMan.GetActivity() && g_ActivityMan.GetActivity()->GetTeamOfPlayer(whichScreen) != m_Team) - return; - - // AI waypoints or points of interest - if (m_DrawWaypoints && m_PlayerControllable && (m_AIMode == AIMODE_GOTO || m_AIMode == AIMODE_SQUAD)) - { - // Draw the AI paths, from the ultimate destination back up to the actor's position. - // We do this backwards so the lines won't crawl and the dots can be evenly spaced throughout - Vector waypoint; - std::list >::reverse_iterator vLast, vItr; - std::list::reverse_iterator lLast, lItr; - int skipPhase = 0; - - // Draw the line between the end of the movepath and the first waypoint after that, if any - if (!m_Waypoints.empty()) - { - // Draw the first destination/waypoint point -// waypoint = m_MoveTarget - targetPos; -// circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 2, g_YellowGlowColor); - - // Draw the additional waypoint points beyond the first one - vLast = m_Waypoints.rbegin(); - vItr = m_Waypoints.rbegin(); - for (; vItr != m_Waypoints.rend(); ++vItr) - { - // Draw the line - g_FrameMan.DrawLine(pTargetBitmap, (*vLast).first - targetPos, (*vItr).first - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, 0, true); - vLast = vItr; - - // Draw the points - waypoint = (*vItr).first - targetPos; - circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 2, g_YellowGlowColor); - // Add pixel glow area around it, in scene coordinates - g_PostProcessMan.RegisterGlowArea((*vItr).first, 5); - } - - // Draw line from the last movetarget on the current path to the first waypoint in queue after that - if (!m_MovePath.empty()) - g_FrameMan.DrawLine(pTargetBitmap, m_MovePath.back() - targetPos, m_Waypoints.front().first - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, 0, true); - else - g_FrameMan.DrawLine(pTargetBitmap, m_MoveTarget - targetPos, m_Waypoints.front().first - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, 0, true); - } - - // Draw the current movepath, but backwards so the dot spacing can be even and they don't crawl as the guy approaches - if (!m_MovePath.empty()) - { - lLast = m_MovePath.rbegin(); - lItr = m_MovePath.rbegin(); - for (; lItr != m_MovePath.rend(); ++lItr) - { - // Draw these backwards so the skip phase works - skipPhase = g_FrameMan.DrawLine(pTargetBitmap, (*lLast) - targetPos, (*lItr) - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, skipPhase, true); - lLast = lItr; - } - - // Draw the line between the current position and to the start of the movepath, backwards so the dotted lines doesn't crawl - skipPhase = g_FrameMan.DrawLine(pTargetBitmap, m_MovePath.front() - targetPos, m_Pos - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, skipPhase, true); - // Draw the first destination/waypoint point - waypoint = m_MovePath.back() - targetPos; - circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 2, g_YellowGlowColor); - // Add pixel glow area around it, in scene coordinates - g_PostProcessMan.RegisterGlowArea(m_MovePath.back(), 5); - } - // If no points left on movepath, then draw straight line to the movetarget - else - { - // Draw it backwards so the dotted lines doesn't crawl - skipPhase = g_FrameMan.DrawLine(pTargetBitmap, m_MoveTarget - targetPos, m_Pos - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, skipPhase, true); - // Draw the first destination/waypoint point - waypoint = m_MoveTarget - targetPos; - circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 2, g_YellowGlowColor); - // Add pixel glow area around it, in scene coordinates - g_PostProcessMan.RegisterGlowArea(m_MoveTarget, 5); - } - } - - // AI Mode team roster HUD lines - if (m_PlayerControllable && g_ActivityMan.GetActivity()->GetViewState(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)) == Activity::ViewState::ActorSelect && g_SceneMan.ShortestDistance(m_Pos, g_CameraMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).GetMagnitude() < 100) { - draw_sprite(pTargetBitmap, GetAIModeIcon(), cpuPos.m_X - 6, cpuPos.m_Y - 6); - } else if (m_Controller.IsState(ACTOR_NEXT_PREP) || m_Controller.IsState(ACTOR_PREV_PREP)) { - int prevColor = m_Controller.IsState(ACTOR_PREV_PREP) ? 122 : (m_Team == Activity::TeamOne ? 13 : 147); - int nextColor = m_Controller.IsState(ACTOR_NEXT_PREP) ? 122 : (m_Team == Activity::TeamOne ? 13 : 147); - int prevSpacing = m_Controller.IsState(ACTOR_PREV_PREP) ? 3 : 9; - int nextSpacing = m_Controller.IsState(ACTOR_NEXT_PREP) ? 3 : 9; - int altColor = m_Team == Activity::TeamOne ? 11 : 160; - - Actor *pPrevAdj = 0; - Actor *pNextAdj = 0; - std::list *pRoster = g_MovableMan.GetTeamRoster(m_Team); - - if (pRoster->size() > 1) - { - // Find this in the list, both ways - std::list::reverse_iterator selfRItr = find(pRoster->rbegin(), pRoster->rend(), this); - RTEAssert(selfRItr != pRoster->rend(), "Actor couldn't find self in Team roster!"); - std::list::iterator selfItr = find(pRoster->begin(), pRoster->end(), this); - RTEAssert(selfItr != pRoster->end(), "Actor couldn't find self in Team roster!"); - - // Find the adjacent actors - if (selfItr != pRoster->end()) - { - // Get the previous available actor in the list (not controlled by another player) - std::list::reverse_iterator prevItr = selfRItr; - do - { - if (++prevItr == pRoster->rend()) - prevItr = pRoster->rbegin(); - if ((*prevItr) == (*selfItr)) - break; - } - while(!(*prevItr)->IsPlayerControllable() || (*prevItr)->GetController()->IsPlayerControlled() || - g_ActivityMan.GetActivity()->IsOtherPlayerBrain((*prevItr), m_Controller.GetPlayer())); - - // Get the next actor in the list (not controlled by another player) - std::list::iterator nextItr = selfItr; - do - { - if (++nextItr == pRoster->end()) - nextItr = pRoster->begin(); - if ((*nextItr) == (*selfItr)) - break; - } - while(!(*nextItr)->IsPlayerControllable() || (*nextItr)->GetController()->IsPlayerControlled() || - g_ActivityMan.GetActivity()->IsOtherPlayerBrain((*prevItr), m_Controller.GetPlayer())); - - Vector iconPos = cpuPos; - // Only continue if there are available adjacent Actors - if ((*prevItr) != (*selfItr) && (*nextItr) != (*selfItr)) - { - pPrevAdj = *prevItr; - pNextAdj = *nextItr; - // Only draw both lines if they're not pointing to the same thing - if (pPrevAdj != pNextAdj) - { - g_FrameMan.DrawLine(pTargetBitmap, cpuPos, pPrevAdj->GetCPUPos() - targetPos, prevColor, prevColor, prevSpacing, 0, true); - g_FrameMan.DrawLine(pTargetBitmap, cpuPos, pNextAdj->GetCPUPos() - targetPos, nextColor, nextColor, nextSpacing, 0, true); - } - // If only one other available Actor, only draw one yellow line to it - else - g_FrameMan.DrawLine(pTargetBitmap, cpuPos, pNextAdj->GetCPUPos() - targetPos, 122, 122, 3, 0, true); - - // Prev selected icon - iconPos = pPrevAdj->GetCPUPos() - targetPos; - draw_sprite(pTargetBitmap, pPrevAdj->GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); - - // Next selected icon - iconPos = pNextAdj->GetCPUPos() - targetPos; - draw_sprite(pTargetBitmap, pNextAdj->GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); - } - - // Self selected icon - iconPos = cpuPos; - draw_sprite(pTargetBitmap, GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); -/* Too many lines, confusing! - // Again get the next and previous actors in the list - if (++prevItr == pRoster->rend()) - prevItr = pRoster->rbegin(); - if (++nextItr == pRoster->end()) - nextItr = pRoster->begin(); - g_FrameMan.DrawLine(pTargetBitmap, pPrevAdj->GetCPUPos() - targetPos, (*prevItr)->GetCPUPos() - targetPos, prevColor, prevColor, 12, 0, true); - g_FrameMan.DrawLine(pTargetBitmap, pNextAdj->GetCPUPos() - targetPos, (*nextItr)->GetCPUPos() - targetPos, nextColor, nextColor, 12, 0, true); - // Prev selected icon - iconPos = (*prevItr)->GetCPUPos(); - draw_sprite(pTargetBitmap, (*prevItr)->GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); - // Next selected icon - iconPos = (*nextItr)->GetCPUPos(); - draw_sprite(pTargetBitmap, (*nextItr)->GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); -*/ - } - } - } -} + // Draw the current movepath, but backwards so the dot spacing can be even and they don't crawl as the guy approaches + if (!m_MovePath.empty()) { + lLast = m_MovePath.rbegin(); + lItr = m_MovePath.rbegin(); + for (; lItr != m_MovePath.rend(); ++lItr) { + // Draw these backwards so the skip phase works + skipPhase = g_FrameMan.DrawLine(pTargetBitmap, (*lLast) - targetPos, (*lItr) - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, skipPhase, true); + lLast = lItr; + } + + // Draw the line between the current position and to the start of the movepath, backwards so the dotted lines doesn't crawl + skipPhase = g_FrameMan.DrawLine(pTargetBitmap, m_MovePath.front() - targetPos, m_Pos - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, skipPhase, true); + // Draw the first destination/waypoint point + waypoint = m_MovePath.back() - targetPos; + circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 2, g_YellowGlowColor); + // Add pixel glow area around it, in scene coordinates + g_PostProcessMan.RegisterGlowArea(m_MovePath.back(), 5); + } + // If no points left on movepath, then draw straight line to the movetarget + else { + // Draw it backwards so the dotted lines doesn't crawl + skipPhase = g_FrameMan.DrawLine(pTargetBitmap, m_MoveTarget - targetPos, m_Pos - targetPos, g_YellowGlowColor, 0, AILINEDOTSPACING, skipPhase, true); + // Draw the first destination/waypoint point + waypoint = m_MoveTarget - targetPos; + circlefill(pTargetBitmap, waypoint.m_X, waypoint.m_Y, 2, g_YellowGlowColor); + // Add pixel glow area around it, in scene coordinates + g_PostProcessMan.RegisterGlowArea(m_MoveTarget, 5); + } + } + + // AI Mode team roster HUD lines + if (m_PlayerControllable && g_ActivityMan.GetActivity()->GetViewState(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)) == Activity::ViewState::ActorSelect && g_SceneMan.ShortestDistance(m_Pos, g_CameraMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).GetMagnitude() < 100) { + draw_sprite(pTargetBitmap, GetAIModeIcon(), cpuPos.m_X - 6, cpuPos.m_Y - 6); + } else if (m_Controller.IsState(ACTOR_NEXT_PREP) || m_Controller.IsState(ACTOR_PREV_PREP)) { + int prevColor = m_Controller.IsState(ACTOR_PREV_PREP) ? 122 : (m_Team == Activity::TeamOne ? 13 : 147); + int nextColor = m_Controller.IsState(ACTOR_NEXT_PREP) ? 122 : (m_Team == Activity::TeamOne ? 13 : 147); + int prevSpacing = m_Controller.IsState(ACTOR_PREV_PREP) ? 3 : 9; + int nextSpacing = m_Controller.IsState(ACTOR_NEXT_PREP) ? 3 : 9; + int altColor = m_Team == Activity::TeamOne ? 11 : 160; + + Actor* pPrevAdj = 0; + Actor* pNextAdj = 0; + std::list* pRoster = g_MovableMan.GetTeamRoster(m_Team); + + if (pRoster->size() > 1) { + // Find this in the list, both ways + std::list::reverse_iterator selfRItr = find(pRoster->rbegin(), pRoster->rend(), this); + RTEAssert(selfRItr != pRoster->rend(), "Actor couldn't find self in Team roster!"); + std::list::iterator selfItr = find(pRoster->begin(), pRoster->end(), this); + RTEAssert(selfItr != pRoster->end(), "Actor couldn't find self in Team roster!"); + + // Find the adjacent actors + if (selfItr != pRoster->end()) { + // Get the previous available actor in the list (not controlled by another player) + std::list::reverse_iterator prevItr = selfRItr; + do { + if (++prevItr == pRoster->rend()) + prevItr = pRoster->rbegin(); + if ((*prevItr) == (*selfItr)) + break; + } while (!(*prevItr)->IsPlayerControllable() || (*prevItr)->GetController()->IsPlayerControlled() || + g_ActivityMan.GetActivity()->IsOtherPlayerBrain((*prevItr), m_Controller.GetPlayer())); + + // Get the next actor in the list (not controlled by another player) + std::list::iterator nextItr = selfItr; + do { + if (++nextItr == pRoster->end()) + nextItr = pRoster->begin(); + if ((*nextItr) == (*selfItr)) + break; + } while (!(*nextItr)->IsPlayerControllable() || (*nextItr)->GetController()->IsPlayerControlled() || + g_ActivityMan.GetActivity()->IsOtherPlayerBrain((*prevItr), m_Controller.GetPlayer())); + + Vector iconPos = cpuPos; + // Only continue if there are available adjacent Actors + if ((*prevItr) != (*selfItr) && (*nextItr) != (*selfItr)) { + pPrevAdj = *prevItr; + pNextAdj = *nextItr; + // Only draw both lines if they're not pointing to the same thing + if (pPrevAdj != pNextAdj) { + g_FrameMan.DrawLine(pTargetBitmap, cpuPos, pPrevAdj->GetCPUPos() - targetPos, prevColor, prevColor, prevSpacing, 0, true); + g_FrameMan.DrawLine(pTargetBitmap, cpuPos, pNextAdj->GetCPUPos() - targetPos, nextColor, nextColor, nextSpacing, 0, true); + } + // If only one other available Actor, only draw one yellow line to it + else + g_FrameMan.DrawLine(pTargetBitmap, cpuPos, pNextAdj->GetCPUPos() - targetPos, 122, 122, 3, 0, true); + + // Prev selected icon + iconPos = pPrevAdj->GetCPUPos() - targetPos; + draw_sprite(pTargetBitmap, pPrevAdj->GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); + + // Next selected icon + iconPos = pNextAdj->GetCPUPos() - targetPos; + draw_sprite(pTargetBitmap, pNextAdj->GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); + } + + // Self selected icon + iconPos = cpuPos; + draw_sprite(pTargetBitmap, GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); + /* Too many lines, confusing! + // Again get the next and previous actors in the list + if (++prevItr == pRoster->rend()) + prevItr = pRoster->rbegin(); + if (++nextItr == pRoster->end()) + nextItr = pRoster->begin(); + g_FrameMan.DrawLine(pTargetBitmap, pPrevAdj->GetCPUPos() - targetPos, (*prevItr)->GetCPUPos() - targetPos, prevColor, prevColor, 12, 0, true); + g_FrameMan.DrawLine(pTargetBitmap, pNextAdj->GetCPUPos() - targetPos, (*nextItr)->GetCPUPos() - targetPos, nextColor, nextColor, 12, 0, true); + // Prev selected icon + iconPos = (*prevItr)->GetCPUPos(); + draw_sprite(pTargetBitmap, (*prevItr)->GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); + // Next selected icon + iconPos = (*nextItr)->GetCPUPos(); + draw_sprite(pTargetBitmap, (*nextItr)->GetAIModeIcon(), iconPos.m_X - 6, iconPos.m_Y - 6); + */ + } + } + } + } } // namespace RTE diff --git a/Source/Entities/Actor.h b/Source/Entities/Actor.h index 1f1f0e6481..33c70e0e7f 100644 --- a/Source/Entities/Actor.h +++ b/Source/Entities/Actor.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,1624 +17,1599 @@ #include "PieMenu.h" #include "PathFinder.h" -namespace RTE -{ - +namespace RTE { #pragma region Global Macro Definitions - #define DefaultPieMenuNameGetter(DEFAULTPIEMENUNAME) \ - std::string GetDefaultPieMenuName() const override { return DEFAULTPIEMENUNAME; } +#define DefaultPieMenuNameGetter(DEFAULTPIEMENUNAME) \ + std::string GetDefaultPieMenuName() const override { return DEFAULTPIEMENUNAME; } #pragma endregion -class AtomGroup; -class HeldDevice; + class AtomGroup; + class HeldDevice; #define AILINEDOTSPACING 16 -////////////////////////////////////////////////////////////////////////////////////////// -// Class: Actor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A sprite movable object that is autonomous. -// Parent(s): MOSRotating. -// Class history: 04/13/2001 Actor created. - -class Actor : public MOSRotating { - friend struct EntityLuaBindings; - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - enum Status - { - STABLE = 0, - UNSTABLE, - INACTIVE, - DYING, - DEAD, - StatusCount - }; - - // TODO - move into ALocomotable intermediate class under ACrab/AHuman - enum MovementState - { - NOMOVE = 0, - STAND, - WALK, - JUMP, - DISLODGE, - CROUCH, - CRAWL, - ARMCRAWL, - CLIMB, - MOVEMENTSTATECOUNT - }; - - enum AIMode - { - AIMODE_NONE = 0, - AIMODE_SENTRY, - AIMODE_PATROL, - AIMODE_GOTO, - AIMODE_BRAINHUNT, - AIMODE_GOLDDIG, - AIMODE_RETURN, - AIMODE_STAY, - AIMODE_SCUTTLE, - AIMODE_DELIVER, - AIMODE_BOMB, - AIMODE_SQUAD, - AIMODE_COUNT - }; - -// Concrete allocation and cloning definitions -EntityAllocation(Actor); -AddScriptFunctionNames(MOSRotating, "ThreadedUpdateAI", "UpdateAI", "OnControllerInputModeChange"); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: Actor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a Actor object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - Actor() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~Actor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a Actor object before deletion -// from system memory. -// Arguments: None. - - ~Actor() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Actor object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Actor to be identical to another, by deep copy. -// Arguments: A reference to the Actor to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const Actor &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire Actor, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); MOSRotating::Reset(); m_MOType = MovableObject::TypeActor; } - - /// - /// Cleans up and destroys the script state of this object, calling the Destroy callback in lua - /// - void DestroyScriptState(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - /// - /// Gets the mass of this Actor's inventory. Does not include any equipped item (for actor subtypes that have that). - /// - /// The mass of this Actor's inventory. - float GetInventoryMass() const; - - /// - /// Gets the mass of this Actor, including the mass of its Attachables, wounds and inventory. - /// - /// The mass of this Actor, its inventory and all its Attachables and wounds in Kilograms (kg). - float GetMass() const override { return MOSRotating::GetMass() + GetInventoryMass() + (m_GoldCarried * g_SceneMan.GetKgPerOz()); } - - /// - /// Gets the mass that this actor had upon spawning, i.e with ini-defined inventory, gold and holding no items - /// - /// The base mass of this Actor, in Kilograms (kg). - float GetBaseMass(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets this Actor's Controller. Ownership IS NOT transferred! -// Arguments: None. -// Return value: A const pointer to this Actor's Controller. - - Controller * GetController() { return &m_Controller; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsPlayerControlled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a player is currently controlling this. -// Arguments: None. -// Return value: Whether a player is controlling this. - - bool IsPlayerControlled() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsControllable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells wheter the player can switch control to this at all -// Arguments: None. -// Return value: Whether a player can control this at all. - - virtual bool IsControllable() const { return true; } - - /// - /// Gets whether or not this Actor can be controlled by human players. Note that this does not protect the Actor's Controller from having its input mode forced to CIM_PLAYER (e.g. via Lua). - /// - /// Whether or not this Actor can be controlled by human players. - bool IsPlayerControllable() const { return m_PlayerControllable; } - - /// - /// Sets whether or not this Actor can be controlled by human players. - /// - /// Whether or not this Actor should be able to be controlled by human players. - void SetPlayerControllable(bool playerControllable) { m_PlayerControllable = playerControllable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetStatus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the current Status of this. -// Arguments: None. -// Return value: The status. - - int GetStatus() const { return m_Status; } - - - /// - /// Gets this Actor's health value. - /// - /// A float describing this Actor's health. - float GetHealth() const { return m_Health; } - - /// - /// Gets this Actor's previous health value, prior to this frame. - /// - /// A float describing this Actor's previous health. - float GetPrevHealth() const { return m_PrevHealth; } - - /// - /// Gets this Actor's maximum health value. - /// - /// A float describing this Actor's max health. - float GetMaxHealth() const { return m_MaxHealth; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMaxHealth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets this Actor's maximum health value. -// Arguments: New max health value. -// Return value: None. - - void SetMaxHealth(int newValue) { m_MaxHealth = newValue; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAimDistance -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the distance between the actor and the view point when not -// sharp aiming. -// Arguments: None. -// Return value: A const int describing how far this actor aims/looks by default. - - int GetAimDistance() const { return m_AimDistance; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAimDistance -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the distance between the actor and the view point when not -// sharp aiming. -// Arguments: None. -// Return value: A const int describing how far this actor aims/looks by default. - - void SetAimDistance( int newValue ) { m_AimDistance = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldCarried -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how many ounces of gold this Actor is carrying. -// Arguments: None. -// Return value: The current amount of carried gold, in Oz. - - float GetGoldCarried() const { return m_GoldCarried; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of this Actor and all its carried -// gold and inventory. -// Arguments: If this is supposed to be adjusted for a specific Tech's subjective -// value, then pass in the native DataModule ID of that tech. 0 means -// no Tech is specified and the base value is returned. -// How much to multiply the value if this happens to be a foreign Tech. -// Return value: The current value of this Actor and all his carried assets. - - float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically named object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. -// Arguments: The Preset name of the object to look for. -// Return value: Whetehr the object was found carried by this. - - bool HasObject(std::string objectName) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObjectInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically grouped object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. -// Arguments: The name of the group to look for. -// Return value: Whetehr the object in the group was found carried by this. - - bool HasObjectInGroup(std::string groupName) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAimAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets this Actor's aim angle. -// Arguments: Whether to adjust the angle for flipping or not. -// Return value: The angle, in radians. - - float GetAimAngle(bool adjustForFlipped = true) const { return adjustForFlipped ? FacingAngle(m_AimAngle) : m_AimAngle; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPassengerSlots -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets this Actor's passenger slots. -// Arguments: None. -// Return value: The Actor's passenger plots - - int GetPassengerSlots() const { return m_PassengerSlots; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetCPUPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of this' brain, or equivalent. -// Arguments: None. -// Return value: A Vector with the absolute position of this' brain. - - virtual Vector GetCPUPos() const { return m_Pos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetEyePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of this' eye, or equivalent, where look -// vector starts from. -// Arguments: None. -// Return value: A Vector with the absolute position of this' eye or view point. - - virtual Vector GetEyePos() const { return m_Pos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAboveHUDPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of the top of this' HUD stack. -// Arguments: None. -// Return value: A Vector with the absolute position of this' HUD stack top point. - - Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, m_HUDStack + 6); } - - - /// - /// Gets the offset position of the holster where this Actor draws his devices from. - /// - /// The offset position of the holster. - Vector GetHolsterOffset() const { return m_HolsterOffset; } - - /// - /// Sets the offset position of the holster where this Actor draws his devices from. - /// - /// A new holster offset. - void SetHolsterOffset(Vector newOffset) { m_HolsterOffset = newOffset; } - - /// - /// Gets the offset position of where this Actor reloads his devices from. - /// - /// The offset position of the where this Actor reloads his devices from. - Vector GetReloadOffset() const { return m_ReloadOffset; } - - /// - /// Sets the offset position of the where this Actor reloads his devices from. - /// - /// The new offset position of where this Actor reloads his devices from. - void SetReloadOffset(Vector newOffset) { m_ReloadOffset = newOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetViewPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the point at which this actor is viewing, or that the scene frame -// should be centered on if tracking this Actor's view. In absolute scene -// coordinates. -// Arguments: None. -// Return value: The point in absolute scene coordinates. - - Vector GetViewPoint() const { return m_ViewPoint.IsZero() ? m_Pos : m_ViewPoint; } - - - /// - /// Gets the item that is within reach of the Actor at this frame, ready to be be picked up. Ownership is NOT transferred! - /// - /// A pointer to the item that has been determined to be within reach of this Actor, if any. - HeldDevice * GetItemInReach() const { return m_pItemInReach; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLookVector -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the direction where this is looking/aiming. -// Arguments: None. -// Return value: A Vector with the direction in which this is looking along. - - Vector GetLookVector() const { return m_ViewPoint - GetEyePos(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSharpAimProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the normalized amount of sharp aim that has been achieved by this. -// Arguments: None. -// Return value: Sharp aim progress between 0 - 1.0. 1.0 is fully aimed. - - float GetSharpAimProgress() const { return m_SharpAimProgress; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the approximate height of this Actor, standing up. -// Arguments: None. -// Return value: A float with the approximate height, in pixels. - - float GetHeight() const { return m_CharHeight; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetControllerMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this Actor's new Controller input mode. -// Arguments: The new input mode. -// The player which will control this if the input mode was set to player. -// Return value: None. - - void SetControllerMode(Controller::InputMode newMode, int newPlayer = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwapControllerModes -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this Actor's Controller mode and gives back what it used to be. -// Arguments: The new mode to set to. -// The player which will control this if the input mode was set to player. -// Return value: The old mode that it had before. - - Controller::InputMode SwapControllerModes(Controller::InputMode newMode, int newPlayer = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetStatus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this Actor's status. -// Arguments: A Status enumeration. -// Return value: None. - - void SetStatus(Actor::Status newStatus) { m_Status = newStatus; if (newStatus == Actor::Status::UNSTABLE) { m_StableRecoverTimer.Reset(); } } - - /// Gets this Actor's MovementState. - /// - /// This Actor's MovementState. - MovementState GetMovementState() const { return m_MoveState; } - - /// - /// Sets this Actor's MovementState to the new state. - /// - /// This Actor's new MovementState. - void SetMovementState(MovementState newMovementState) { m_MoveState = newMovementState; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this Actor belongs to. -// Arguments: The assigned team number. -// Return value: None. - - void SetTeam(int team) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetGoldCarried -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets how many ounces of gold this Actor is carrying. -// Arguments: The new amount of carried gold, in Oz. -// Return value: None. - - void SetGoldCarried(float goldOz) { m_GoldCarried = goldOz; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAimAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this Actor's aim angle. -// Arguments: A new angle, in radians. -// Return value: None. - - void SetAimAngle(float newAngle) { m_AimAngle = newAngle; Clamp(m_AimAngle, m_AimRange, -m_AimRange); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPassengerSlots -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this Actor's passenger slots. -// Arguments: A new amount of passenger slots. -// Return value: None. - - void SetPassengerSlots(int newPassengerSlots) { m_PassengerSlots = newPassengerSlots; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetViewPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the point at which this actor is viewing, or that the scene frame -// should be centered on if tracking this Actor's view. In absolute scene -// coordinates. -// Arguments: A new point in absolute scene coords. -// Return value: None. - - void SetViewPoint(Vector newPoint) { m_ViewPoint = newPoint; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetItemInReach -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the item that is within reach of the Actor at this frame, so that -// it may be picked up. Ownership is NOT transferred! -// Arguments: A pointer to the item that has been determined to be within reach of -// this Actor. Ownership is NOT transferred! -// Return value: None. - - void SetItemInReach(HeldDevice *pItem) { m_pItemInReach = pItem; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsWithinRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a point on the scene is within range of the currently -// used device and aiming status, if applicable. -// Arguments: A Vector witht he aboslute coordinates of a point to check. -// Return value: Whether the point is within range of this. - - virtual bool IsWithinRange(Vector &point) const { return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Look -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Casts an unseen-revealing ray in the direction of where this is facing. -// Arguments: The degree angle to deviate from the current view point in the ray -// casting. A random ray will be chosen out of this +-range. -// The range, in pixels, that the ray will have. -// Return value: Whether any unseen pixels were revealed by this look. - - virtual bool Look(float FOVSpread, float range); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddGold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a certain amount of ounces of gold to this' team's total funds. -// Arguments: The amount in Oz with which to change this' team's gold tally. -// Return value: None. - - void AddGold(float goldOz); - - - /// - /// Does the calculations necessary to detect whether this Actor is at rest or not. IsAtRest() retrieves the answer. - /// - void RestDetection() override; - - /// - /// Adds health points to this Actor's current health value. - /// - /// A float specifying the value to add. - /// The resulting total health of this Actor. - const float AddHealth(const float addedHealth) { return m_Health += addedHealth; } - - /// - /// Sets this Actor's current health value. - /// - /// A float specifying the value to set to. - void SetHealth(const float setHealth) { m_Health = setHealth; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsStatus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if this Actor is in a specific status. -// Arguments: Which status to check for. -// Return value: A bool with the answer. - - bool IsStatus(Status which) const { return m_Status == which; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDead -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if this Actor is dead. -// Arguments: None. -// Return value: A const bool with the answer. - - bool IsDead() const { return m_Status == DEAD; } - - /// - /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. - /// - /// The SliceType of the PieSlice being handled. - /// Whether or not the activated PieSlice SliceType was able to be handled. - virtual bool HandlePieCommand(PieSlice::SliceType pieSliceType) { return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAIMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets this' AI mode. -// Arguments: None. -// Return value: The current AI mode. - - int GetAIMode() const { return m_AIMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAIModeIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the icon bitmap associated with this' current AI mode and team. -// Arguments: None. -// Return value: The current AI mode icon of this. Ownership is NOT transferred! - - BITMAP * GetAIModeIcon(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetAIMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this' AI mode. -// Arguments: The new AI mode. -// Return value: None. - - void SetAIMode(AIMode newMode = AIMODE_SENTRY) { m_AIMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: AddAISceneWaypoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an absolute scene point to the list of waypoints this is going to -// go to, in order -// Arguments: The new scene point this should try to get to after all other waypoints -// are reached. -// Return value: None. - - void AddAISceneWaypoint(const Vector &waypoint) { m_Waypoints.push_back(std::pair(waypoint, (MovableObject*)NULL)); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: AddAIMOWaypoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an MO in the scene as the next waypoint for this to go to, in order -// Arguments: The new MO this should try to get to after all other waypoints are reached. -// OWNERSHIP IS NOT TRANSFERRED! -// Return value: None. - - void AddAIMOWaypoint(const MovableObject *pMOWaypoint); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearAIWaypoints -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes all AI waypoints and clears the current path to the current -// waypoint. The AI Actor will stop in its tracks. -// Arguments: None. -// Return value: None. - - void ClearAIWaypoints() { m_pMOMoveTarget = 0; m_Waypoints.clear(); m_MovePath.clear(); m_MoveTarget = m_Pos; m_MoveVector.Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLastAIWaypoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the last or furthest set AI waypoint of this. If none, this' pos -// is returned. -// Arguments: None. -// Return value: The furthest set AI waypoint of this. - - Vector GetLastAIWaypoint() const { if (!m_Waypoints.empty()) { return m_Waypoints.back().first; } else if (!m_MovePath.empty()) { return m_MovePath.back(); } return m_Pos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetLastMOWaypointID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the ID of the last set AI MO waypoint of this. If none, g_NoMOID is returned. -// Arguments: None. -// Return value: The furthest set AI MO waypoint of this. - - MOID GetAIMOWaypointID() const; - - /// - /// Gets the list of waypoints for this Actor. - /// - /// The list of waypoints for this Actor. - const std::list> & GetWaypointList() const { return m_Waypoints; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetWaypointsSize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how many waypoints this actor have. -// Arguments: None. -// Return value: How many waypoints. - - int GetWaypointsSize() { return m_Waypoints.size(); }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearMovePath -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the list of coordinates in this' current MovePath, ie the path -// to the next Waypoint. -// Arguments: None. -// Return value: None. - - void ClearMovePath() { m_MovePath.clear(); m_MoveTarget = m_Pos; m_MoveVector.Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: AddToMovePathBeginning -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a coordinate to the beginning of the MovePath, meaning the one -// closest to this Actor. -// Arguments: The new coordinate to add to the front of the MovePath. -// Return value: None. - - void AddToMovePathBeginning(Vector newCoordinate) { m_MovePath.push_front(newCoordinate); m_MoveTarget = newCoordinate; m_MoveVector.Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: AddToMovePathEnd -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a coordinate to the end of the MovePath, meaning the one -// closest to this Actor's next waypoint. -// Arguments: The new coordinate to add to the end of the MovePath. -// Return value: None. - - void AddToMovePathEnd(Vector newCoordinate) { m_MovePath.push_back(newCoordinate); } - - /// - /// Gets the last position in this Actor's move path. - /// - /// The last position in this Actor's move path. - Vector GetMovePathEnd() const { return m_MovePath.back(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveMovePathBeginning -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a coordinate from the beginning of the MovePath, meaning the -// one closest to this Actor. -// Arguments: None. -// Return value: Whether there was any coordinate to remove. If false, the MovePath -// is empty. - - bool RemoveMovePathBeginning() { if (!m_MovePath.empty()) { m_MovePath.pop_front(); m_MoveTarget = m_MovePath.empty() ? m_Pos : m_MovePath.front(); m_MoveVector.Reset(); return true; } return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: RemoveMovePathEnd -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a coordinate from the end of the MovePath, meaning the -// one farthest from this Actor. -// Arguments: None. -// Return value: Whether there was any coordinate to remove. If false, the MovePath -// is empty. - - bool RemoveMovePathEnd() { if (!m_MovePath.empty()) { m_MovePath.pop_back(); return true; } return false; } - - /// - /// Gets a pointer to the MovableObject move target of this Actor. - /// - /// A pointer to the MovableObject move target of this Actor. - const MovableObject * GetMOMoveTarget() const { return m_pMOMoveTarget; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPerceptiveness -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this' perceptiveness to alarming events going on around him. -// Arguments: The current perceptiveness, 0.0 - 1.0 -// Return value: None. - - void SetPerceptiveness(float newPerceptiveness) { m_Perceptiveness = newPerceptiveness; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPerceptiveness -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets this' perceptiveness to alarming events going on around him. -// Arguments: None. -// Return value: The current perceptiveness, 0.0 - 1.0 - - float GetPerceptiveness() const { return m_Perceptiveness; } - - - /// - /// Gets whether this actor is able to reveal unseen areas by looking. - /// - /// Whether this actor can reveal unseen areas. - bool GetCanRevealUnseen() const { return m_CanRevealUnseen; } - - /// - /// Sets whether this actor can reveal unseen areas by looking. - /// - /// Whether this actor can reveal unseen areas. - void SetCanRevealUnseen(bool newCanRevealUnseen) { m_CanRevealUnseen = newCanRevealUnseen; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPainThreshold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this' PainThreshold value above which it will play PainSound -// Arguments: Desired PainThreshold value -// Return value: None. - - void SetPainThreshold(float newPainThreshold) { m_PainThreshold = newPainThreshold; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPainThreshold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets this' PainThreshold value above which it will play PainSound -// Arguments: None. -// Return value: The current PainThreshold - - float GetPainThreshold() const { return m_PainThreshold; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AlarmPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this alarmed about a certian point on in the scene, overriding -// the current AI mode until a certain time has passed. -// Arguments: The new scene point this should look at and see if anything dangerous -// is there. -// Return value: None. - - void AlarmPoint(const Vector &alarmPoint) { if (m_AlarmSound && m_AlarmTimer.IsPastSimTimeLimit()) { m_AlarmSound->Play(alarmPoint); } if (m_AlarmTimer.GetElapsedSimTimeMS() > 50) { m_AlarmTimer.Reset(); m_LastAlarmPos = m_PointingTarget = alarmPoint; } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAlarmPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets any point on the scene this actor should be alarmed about this frame. -// Arguments: None. -// Return value: The new scene point this should look at and see if anything dangerous -// is there or (0,0) if nothing is alarming. - - Vector GetAlarmPoint() { if (m_AlarmTimer.GetElapsedSimTimeMS() > g_TimerMan.GetDeltaTimeMS()) { return Vector(); } return m_LastAlarmPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: AddInventoryItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an inventory item to this Actor. -// Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! -// Return value: None.. - - virtual void AddInventoryItem(MovableObject *pItemToAdd) { AddToInventoryBack(pItemToAdd); } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveInventoryItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified item from the actor's inventory. Only one item is removed at a time. -// Arguments: Preset name of an item to remove. -// Return value: None. - - void RemoveInventoryItem(const std::string &presetName) { RemoveInventoryItem("", presetName); } - - /// - /// Removes the first inventory item with the given module name and preset name. - /// - /// The module name of the item to remove. - /// The preset name of the item to remove. - void RemoveInventoryItem(const std::string &moduleName, const std::string &presetName); - - /// - /// Removes and returns the inventory item at the given index. Ownership IS transferred. - /// - /// The index of the inventory item to remove. - /// An owning pointer to the removed inventory item. - MovableObject * RemoveInventoryItemAtIndex(int inventoryIndex); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwapNextInventory -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Swaps the next MovableObject carried by this Actor and puts one not -// currently carried into the into the back of the inventory of this. -// Arguments: A pointer to the external MovableObject to trade in. Ownership IS xferred! -// If 0 is passed in, nothing will be added to the inventory. -// Whether to mute the sound on this event. Override for the loading screen hack. -// Return value: The next MovableObject in this Actor's inventory. Ownership IS xferred! -// If there are no MovableObject:s in inventory, 0 will be returned. - - virtual MovableObject * SwapNextInventory(MovableObject *pSwapIn = nullptr, bool muteSound = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwapPrevInventory -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Swaps the prev MovableObject carried by this Actor and puts one not -// currently carried into the into the back of the inventory of this. -// Arguments: A pointer to the external MovableObject to trade in. Ownership IS xferred! -// If 0 is passed in, nothing will be added to the inventory. -// Return value: The prev MovableObject in this Actor's inventory. Ownership IS xferred! -// If there are no MovableObject:s in inventory, 0 will be returned. - - virtual MovableObject * SwapPrevInventory(MovableObject *pSwapIn = nullptr); - - /// - /// Swaps the inventory items at the given indices. Will return false if a given index is invalid. - /// - /// The index of one item. - /// The index of the other item. - /// Whether or not the swap was successful. - bool SwapInventoryItemsByIndex(int inventoryIndex1, int inventoryIndex2); - - /// - /// Sets the inventory item at the given index as the new inventory item, and gives back the one that was originally there. - /// If an invalid index is given, the new item will be put in the back of the inventory, and nullptr will be returned. - /// - /// The new item that should be at the given inventory index. Cannot be a nullptr. Ownership IS transferred. - /// The inventory index the new item should be placed at. - /// The inventory item that used to be at the inventory index. Ownership IS transferred. - MovableObject * SetInventoryItemAtIndex(MovableObject *newInventoryItem, int inventoryIndex); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DropAllInventory -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Ejects all inventory items that this is carrying. It may not happen -// instantaneously, so check for ejection being complete with -// IsInventoryEmpty(). -// Arguments: None. -// Return value: None. - - virtual void DropAllInventory(); - - /// - /// Converts all of the Gold carried by this Actor into MovableObjects and ejects them into the Scene. - /// - virtual void DropAllGold(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetInventorySize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells how many things are in the invetory -// Arguments: None. -// Return value: The number of things in the inventory - - int GetInventorySize() const { return m_Inventory.size(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsInventoryEmpty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether inventory is completely empty -// Arguments: None. -// Return value: Whether inventory is completely empty. - - bool IsInventoryEmpty() { return m_Inventory.empty(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetInventory -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the deque of inventory of this. Ownership is NOT transferred. -// Arguments: None. -// Return value: A const pointer to the inventory deque of this. OWNERSHIP IS NOT TRANSFERRED! - - const std::deque * GetInventory() const { return &m_Inventory; } - - /// - /// Returns the maximum total mass this Actor can carry in its inventory. - /// - /// The maximum carriable mass of this Actor. - float GetMaxInventoryMass() const { return m_MaxInventoryMass; } - - /// - /// Attempts to add an item to the front of our inventory. - /// - /// Whether we succeeded in adding the item. We may fail if the object doesn't exist or is set to delete. - bool AddToInventoryFront(MovableObject *itemToAdd); - - /// - /// Attempts to add an item to the back of our inventory. - /// - /// Whether we succeeded in adding the item. We may fail if the object doesn't exist or is set to delete. - bool AddToInventoryBack(MovableObject *itemToAdd); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAimRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The limit of this actors aiming angle, in each direction, in radians. -// Arguments: None. -// Return value: The arc range of the aiming angle in radians. -// Eg if HalfPI, it means full 180 degree range - - float GetAimRange() const { return m_AimRange; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetAimRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the limit of this actors aiming angle, in each direction, in radians. -// Arguments: The arc range of the aiming angle in radians. -// Eg if HalfPI, it means full 180 degree range -// Return value: None. - - void SetAimRange(float range) { m_AimRange = range; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawWaypoints -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this draw its current waypoints and related data on the scene in -// its HUD drawing stage. -// Arguments: Whether to enable or disable the drawing of the waypoints. -// Return value: None. - - void DrawWaypoints(bool drawWaypoints = true) { m_DrawWaypoints = drawWaypoints; } - - - /// - /// Destroys this MOSRotating and creates its specified Gibs in its place with appropriate velocities. - /// Any Attachables are removed and also given appropriate velocities. - /// - /// The impulse (kg * m/s) of the impact causing the gibbing to happen. - /// A pointer to an MO which the Gibs and Attachables should not be colliding with. - void GibThis(const Vector &impactImpulse = Vector(), MovableObject *movableObjectToIgnore = nullptr) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. -// Arguments: Reference to the HitData struct which describes the collision. This -// will be modified to represent the results of the collision. -// Return value: Whether the collision has been deemed valid. If false, then disregard -// any impulses in the Hitdata. - - bool CollideAtPoint(HitData &hitData) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ParticlePenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Determines whether a particle which has hit this MO will penetrate, -// and if so, whether it gets lodged or exits on the other side of this -// MO. Appropriate effects will be determined and applied ONLY IF there -// was penetration! If not, nothing will be affected. -// Arguments: The HitData describing the collision in detail, the impulses have to -// have been filled out! -// Return value: Whether the particle managed to penetrate into this MO or not. If -// somehting but a MOPixel or MOSParticle is being passed in as hitor, -// false will trivially be returned here. - - bool ParticlePenetration(HitData &hd) override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done before Travel(). Always call before -// calling Travel. -// Arguments: None. -// Return value: None. - - void PreTravel() override { MOSRotating::PreTravel(); m_GoldPicked = false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMovePathToUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this' AI's move path to be updated. Will update the path to the -// current waypoint, if any. -// Arguments: None. -// Return value: None. - - void SetMovePathToUpdate() { m_UpdateMovePath = true; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMovePathSize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how many waypoints there are in the MovePath currently -// Arguments: None. -// Return value: The number of waypoints in the MovePath. - - int GetMovePathSize() const { return m_MovePath.size(); } - - /// - /// Starts updating this Actor's movepath. - /// - virtual void UpdateMovePath(); - - /// - /// Returns whether we're waiting on a new pending movepath. - /// - /// Whether we're waiting on a new pending movepath. - bool IsWaitingOnNewMovePath() const { return m_PathRequest != nullptr || m_UpdateMovePath; } - - /// - /// Estimates what material strength this actor can penetrate. - /// - /// The actor's dig strength. - virtual float EstimateDigStrength() const; - - /// - /// Gets this Actor's base dig strength, or the strength of terrain they can expect to walk through without tools. - /// - /// The actors base dig strength. - float GetAIBaseDigStrength() const { return m_AIBaseDigStrength; } - - /// - /// Sets this Actor's base dig strength, or the strength of terrain they can expect to walk through without tools. - /// - /// The new base dig strength for this Actor. - void SetAIBaseDigStrength(float newAIBaseDigStrength) { m_AIBaseDigStrength = newAIBaseDigStrength; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreControllerUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - virtual void PreControllerUpdate(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: FullUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the full state of this object in one call. (PreControllerUpdate(), Controller::Update(), and Update()) -// Arguments: None. -// Return value: None. - - virtual void FullUpdate() override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetDeploymentID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets deployment ID for this actor -// Arguments: New deployment id. -// Return value: None. - - void SetDeploymentID(unsigned int newID) { m_DeploymentID = newID; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDeploymentID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets deployment ID of this actor -// Arguments: None. -// Return value: Returns deployment id of this actor. - - unsigned int GetDeploymentID() const { return m_DeploymentID; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetSightDistance -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns actor's sight distance. -// Arguments: None. -// Return value: Returns actor's sight distance. - - float GetSightDistance() const { return m_SightDistance; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetSightDistance -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets actor's sight distance. -// Arguments: New sight distance value. -// Return value: None. - - void SetSightDistance(float newValue) { m_SightDistance = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Actor's current graphical HUD overlay representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// Which player's screen this is being drawn to. May affect what HUD elements -// get drawn etc. -// Return value: None. - - void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: VerifyMOIDIndex -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Verifieis whether all actor's MO has correct IDs. Should be used in Debug mode only. -// Arguments: None. -// Return value: None. - - void VerifyMOIDs(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTravelImpulseDamage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns Threshold for taking damage from travel impulses, in kg * m/s -// Arguments: None. -// Return value: Threshold for taking damage from travel impulses, in kg * m/s - - float GetTravelImpulseDamage() const { return m_TravelImpulseDamage; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetTravelImpulseDamage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets Threshold for taking damage from travel impulses, in kg * m/s -// Arguments: Threshold for taking damage from travel impulses, in kg * m/s -// Return value: None. - - void SetTravelImpulseDamage(float value) { m_TravelImpulseDamage = value; } - - - /// - /// Gets this Actor's body hit sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's body hit sound. - SoundContainer * GetBodyHitSound() const { return m_BodyHitSound; } - - /// - /// Sets this Actor's body hit sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's body hit sound. - void SetBodyHitSound(SoundContainer *newSound) { m_BodyHitSound = newSound; } - - /// - /// Gets this Actor's alarm sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's alarm sound. - SoundContainer * GetAlarmSound() const { return m_AlarmSound; } - - /// - /// Sets this Actor's alarm sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's alarm sound. - void SetAlarmSound(SoundContainer *newSound) { m_AlarmSound = newSound; } - - /// - /// Gets this Actor's pain sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's pain sound. - SoundContainer * GetPainSound() const { return m_PainSound; } - - /// - /// Sets this Actor's pain sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's pain sound. - void SetPainSound(SoundContainer *newSound) { m_PainSound = newSound; } - - /// - /// Gets this Actor's death sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's death sound. - SoundContainer * GetDeathSound() const { return m_DeathSound; } - - /// - /// Sets this Actor's death sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's death sound. - void SetDeathSound(SoundContainer *newSound) { m_DeathSound = newSound; } - - /// - /// Gets this Actor's device switch sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's device switch sound. - SoundContainer * GetDeviceSwitchSound() const { return m_DeviceSwitchSound; } - - /// - /// Sets this Actor's device switch sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's device switch sound. - void SetDeviceSwitchSound(SoundContainer *newSound) { m_DeviceSwitchSound = newSound; } - - /// - /// Gets the X and Y thresholds for how fast the actor can travel before losing stability. - /// - /// A Vector with the X and Y thresholds for how fast the actor can travel before losing stability. - Vector GetStableVel() const { return m_StableVel; } - - /// - /// Sets the X and Y thresholds for how fast the actor can travel before losing stability. - /// - /// New value for how fast the actor can travel before losing stability on X axis. - /// New value for how fast the actor can travel before losing stability on Y axis. - void SetStableVel(float newVelX, float newVelY) { m_StableVel.SetXY(newVelX, newVelY); } - - /// - /// Sets the X and Y thresholds for how fast the actor can travel before losing stability. - /// - /// Vector with new values for how fast the actor can travel before losing stability on both axis. - void SetStableVel(Vector newVelVector) { m_StableVel = newVelVector; } - - /// - /// Gets the recovery delay from UNSTABLE to STABLE, in MS. - /// - /// The recovery delay, in MS. - int GetStableRecoverDelay() const { return m_StableRecoverDelay; } - - /// - /// Sets the recovery delay from UNSTABLE to STABLE, in MS. - /// - /// The recovery delay, in MS. - void SetStableRecoverDelay(int newRecoverDelay) { m_StableRecoverDelay = newRecoverDelay; } - - /// - /// Gets the distance in which the Actor will have considered itself to have reached it's waypoint. - /// - /// The move proximity limit. - float GetMoveProximityLimit() const { return m_MoveProximityLimit; } - - /// - /// Sets the distance in which the Actor will have considered itself to have reached it's waypoint. - /// - /// The move proximity limit. - void SetMoveProximityLimit(float newProximityLimit) { m_MoveProximityLimit = newProximityLimit; } - - /// - /// Gets whether or not this Actor has the organic flag set and should be considered as organic. - /// - /// Whether or not this Actor has the organic flag set and should be considered as organic. - bool IsOrganic() const { return m_Organic; } - - /// - /// Gets whether or not this Actor has the mechanical flag set and should be considered as mechanical. - /// - /// Whether or not this Actor has the mechanical flag set and should be considered as mechanical. - bool IsMechanical() const { return m_Mechanical; } - - /// - /// Gets whether or not this Actor's limb push forces have been disabled. - /// - /// Whether or not this Actor's limb push forces have been disabled. - bool GetLimbPushForcesAndCollisionsDisabled() const { return m_LimbPushForcesAndCollisionsDisabled; } - - /// - /// Sets whether or not this Actor's limb push forces should be disabled. - /// - /// Whether or not this Actor's limb push forces should be disabled. - void SetLimbPushForcesAndCollisionsDisabled(bool newLimbPushForcesAndCollisionsDisabled) { m_LimbPushForcesAndCollisionsDisabled = newLimbPushForcesAndCollisionsDisabled; } - - /// - /// Gets the default PieMenu name for this type. - /// - /// The default PieMenu name for this type. - virtual std::string GetDefaultPieMenuName() const { return "Default Actor Pie Menu"; } - - /// - /// Gets a pointer to the PieMenu for this Actor. Ownership is NOT transferred. - /// - /// The PieMenu for this Actor. - PieMenu * GetPieMenu() const { return m_PieMenu.get(); } - - /// - /// Sets the PieMenu for this Actor. Ownership IS transferred. - /// - /// The new PieMenu for this Actor. - void SetPieMenu(PieMenu *newPieMenu) { m_PieMenu = std::unique_ptr(newPieMenu); m_PieMenu->Create(this); m_PieMenu->AddWhilePieMenuOpenListener(this, std::bind(&Actor::WhilePieMenuOpenListener, this, m_PieMenu.get())); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - /// - /// Function that is called when we get a new movepath. - /// This processes and cleans up the movepath. - /// - virtual void OnNewMovePath(); - - // Member variables - static Entity::ClassInfo m_sClass; - - enum ActionState - { - MOVING = 0, - MOVING_FAST, - FIRING, - ActionStateCount - }; - - enum AimState - { - AIMSTILL = 0, - AIMUP, - AIMDOWN, - AimStateCount - }; - - AtomGroup *m_pHitBody; - Controller m_Controller; - bool m_PlayerControllable; //!< Whether or not this Actor can be controlled by human players. - - // Sounds - SoundContainer *m_BodyHitSound; - SoundContainer *m_AlarmSound; - SoundContainer *m_PainSound; - SoundContainer *m_DeathSound; - SoundContainer *m_DeviceSwitchSound; - - int m_Status; - float m_Health; - // Maximum health - float m_MaxHealth; - // The health of the previous frame, so we can track damage - float m_PrevHealth; - // Not owned by this! - const Icon *m_pTeamIcon; - // Not owned by this! - const Icon *m_pControllerIcon; - // Timing the last second to store the position each second so we can determine larger movement - Timer m_LastSecondTimer; - // This' position up to a second ago - Vector m_LastSecondPos; - // Movement since last whole second - Vector m_RecentMovement; - // Threshold for taking damage from travel impulses, in kg * m/s - float m_TravelImpulseDamage; - // Timer for timing the delay before regaining stability after losing it - Timer m_StableRecoverTimer; - // Thresholds in both x and y for how fast the actor can travel before losing stability. Meters per second (m/s). - Vector m_StableVel; - int m_StableRecoverDelay; //!< The delay before regaining stability after losing it, in MS - // Timer for the heartbeat of this Actor - Timer m_HeartBeat; - // Timer for timing how long this has been under Control - Timer m_NewControlTmr; - // Death timing timer - Timer m_DeathTmr; - // Amount of Gold carried, in ounces. - float m_GoldCarried; - // Whether or not any gold was picked up this frame. - bool m_GoldPicked; - // Aiming state - char m_AimState; - // The arc range of the aiming angle, in each direction, in radians. Eg if HalfPI, it means full 180 degree range - float m_AimRange; - // Current Aim angle within the AimRange - float m_AimAngle; - // How far the actor aims/looks by default - float m_AimDistance; - // Aiming timing timer - Timer m_AimTmr; - // For timing the transition from regular aim to sharp aim - Timer m_SharpAimTimer; - // The time it takes to achieve complete full sharp aiming - int m_SharpAimDelay; - // The velocity - float m_SharpAimSpeed; - // Normalzied scalar showing storing much sharp aim progress has been made - float m_SharpAimProgress; - // If sharp aim has been maxed out, ie it's either at its max, or being limited by some obstruction - bool m_SharpAimMaxedOut; - // Point at this target when devicestate is in POINTING mode - Vector m_PointingTarget; - // Last seen enemy target - Vector m_SeenTargetPos; - // Timer measuring how long this has been alarmed by a nearby gunshot etc. - Timer m_AlarmTimer; - // Position of the last thing that alarmed us - Vector m_LastAlarmPos; - // How far this guy's AI can see when he's just looking ahead - float m_SightDistance; - // How perceptive this is of alarming events going on around him, 0.0 - 1.0 - float m_Perceptiveness; - /// Damage value above which this will play PainSound - float m_PainThreshold; - // Whether or not this actor can reveal unseen areas by looking - bool m_CanRevealUnseen; - // About How tall is the Actor, in pixels? - float m_CharHeight; - // Speed at which the m_AimAngle will change, in radians/s. -// float - // The offset position of the holster where this Actor draws his devices from. - Vector m_HolsterOffset; - Vector m_ReloadOffset; //!< The offset position of where this Actor reloads his devices from. - // The point at which this actor is viewing, or the scene frame - // should be centered on if tracking this Actor's view. - // In absolute scene coordinates. - Vector m_ViewPoint; - // The inventory of carried MovableObjects of this Actor. They are also Owned by this. - std::deque m_Inventory; - float m_MaxInventoryMass; //!< The mass limit for this Actor's inventory. -1 means there's no limit. - // The device that can/will be picked up - HeldDevice *m_pItemInReach; - // HUD positioning aid - int m_HUDStack; - // ID of deployment which spawned this actor - unsigned int m_DeploymentID; - // How many passenger slots this actor will take in a craft - int m_PassengerSlots; - // Most actors can walk through stuff that's soft enough, so we start with a base penetration amount - float m_AIBaseDigStrength; - // The mass that this actor had upon spawning, i.e with no inventory, no gold and holding no items - float m_BaseMass; - - //////////////////// - // AI States - - enum LateralMoveState - { - LAT_STILL = 0, - LAT_LEFT, - LAT_RIGHT - }; - - enum ObstacleState - { - PROCEEDING = 0, - BACKSTEPPING, - DIGPAUSING, - JUMPING, - SOFTLANDING - }; - - enum TeamBlockState - { - NOTBLOCKED = 0, - BLOCKED, - IGNORINGBLOCK, - FOLLOWWAIT - }; - // Unknown team icon - static std::vector m_apNoTeamIcon; - // The AI mode icons - static BITMAP *m_apAIIcons[AIMODE_COUNT]; - // Selection arrow - static std::vector m_apSelectArrow; - // Selection arrow - static std::vector m_apAlarmExclamation; - // Whether the static icons have been loaded yet or not - static bool m_sIconsLoaded; - // The current mode the AI is set to perform as - AIMode m_AIMode; - // The list of waypoints remaining between which the paths are made. If this is empty, the last path is in teh MovePath - // The MO pointer in the pair is nonzero if the waypoint is tied to an MO in the scene, and gets updated each UpdateAI. This needs to be checked for validity/existence each UpdateAI - std::list > m_Waypoints; - // Whether to draw the waypoints or not in the HUD - bool m_DrawWaypoints; - // Absolute target to move to on the scene; this is usually the point at the front of the movepath list - Vector m_MoveTarget; - // The MO we're currently following, if any. If still valid, this' position will update the MoveTarget each UpdateAI. - const MovableObject *m_pMOMoveTarget; - // The point previous on the path to the one currently assigned the move target - Vector m_PrevPathTarget; - // The relative, scene-wrapped difference between the current m_Pos and the m_MoveTarget. - Vector m_MoveVector; - // The calculated path to get to that move-to target - std::list m_MovePath; - // The current pathfinding request - std::shared_ptr m_PathRequest; - // Whether it's time to update the path - bool m_UpdateMovePath; - // The minimum range to consider having reached a move target is considered - float m_MoveProximityLimit; - // Current movement state. - MovementState m_MoveState; - - bool m_Organic; //!< Flag for whether or not this Actor is organic. Useful for lua purposes and mod support. - bool m_Mechanical; //!< Flag for whether or not this Actor is robotic. Useful for lua purposes and mod support. - - bool m_LimbPushForcesAndCollisionsDisabled; // m_PieMenu; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Actor, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - // Disallow the use of some implicit methods. - Actor(const Actor &reference) = delete; - Actor & operator=(const Actor &rhs) = delete; - -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: Actor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A sprite movable object that is autonomous. + // Parent(s): MOSRotating. + // Class history: 04/13/2001 Actor created. + + class Actor : public MOSRotating { + friend struct EntityLuaBindings; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + enum Status { + STABLE = 0, + UNSTABLE, + INACTIVE, + DYING, + DEAD, + StatusCount + }; + + // TODO - move into ALocomotable intermediate class under ACrab/AHuman + enum MovementState { + NOMOVE = 0, + STAND, + WALK, + JUMP, + DISLODGE, + CROUCH, + CRAWL, + ARMCRAWL, + CLIMB, + MOVEMENTSTATECOUNT + }; + + enum AIMode { + AIMODE_NONE = 0, + AIMODE_SENTRY, + AIMODE_PATROL, + AIMODE_GOTO, + AIMODE_BRAINHUNT, + AIMODE_GOLDDIG, + AIMODE_RETURN, + AIMODE_STAY, + AIMODE_SCUTTLE, + AIMODE_DELIVER, + AIMODE_BOMB, + AIMODE_SQUAD, + AIMODE_COUNT + }; + + // Concrete allocation and cloning definitions + EntityAllocation(Actor); + AddScriptFunctionNames(MOSRotating, "ThreadedUpdateAI", "UpdateAI", "OnControllerInputModeChange"); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: Actor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a Actor object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + Actor() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~Actor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a Actor object before deletion + // from system memory. + // Arguments: None. + + ~Actor() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Actor object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Actor to be identical to another, by deep copy. + // Arguments: A reference to the Actor to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const Actor& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire Actor, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + MOSRotating::Reset(); + m_MOType = MovableObject::TypeActor; + } + + /// + /// Cleans up and destroys the script state of this object, calling the Destroy callback in lua + /// + void DestroyScriptState(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + /// + /// Gets the mass of this Actor's inventory. Does not include any equipped item (for actor subtypes that have that). + /// + /// The mass of this Actor's inventory. + float GetInventoryMass() const; + + /// + /// Gets the mass of this Actor, including the mass of its Attachables, wounds and inventory. + /// + /// The mass of this Actor, its inventory and all its Attachables and wounds in Kilograms (kg). + float GetMass() const override { return MOSRotating::GetMass() + GetInventoryMass() + (m_GoldCarried * g_SceneMan.GetKgPerOz()); } + + /// + /// Gets the mass that this actor had upon spawning, i.e with ini-defined inventory, gold and holding no items + /// + /// The base mass of this Actor, in Kilograms (kg). + float GetBaseMass(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetController + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets this Actor's Controller. Ownership IS NOT transferred! + // Arguments: None. + // Return value: A const pointer to this Actor's Controller. + + Controller* GetController() { return &m_Controller; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsPlayerControlled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a player is currently controlling this. + // Arguments: None. + // Return value: Whether a player is controlling this. + + bool IsPlayerControlled() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsControllable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells wheter the player can switch control to this at all + // Arguments: None. + // Return value: Whether a player can control this at all. + + virtual bool IsControllable() const { return true; } + + /// + /// Gets whether or not this Actor can be controlled by human players. Note that this does not protect the Actor's Controller from having its input mode forced to CIM_PLAYER (e.g. via Lua). + /// + /// Whether or not this Actor can be controlled by human players. + bool IsPlayerControllable() const { return m_PlayerControllable; } + + /// + /// Sets whether or not this Actor can be controlled by human players. + /// + /// Whether or not this Actor should be able to be controlled by human players. + void SetPlayerControllable(bool playerControllable) { m_PlayerControllable = playerControllable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetStatus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the current Status of this. + // Arguments: None. + // Return value: The status. + + int GetStatus() const { return m_Status; } + + /// + /// Gets this Actor's health value. + /// + /// A float describing this Actor's health. + float GetHealth() const { return m_Health; } + + /// + /// Gets this Actor's previous health value, prior to this frame. + /// + /// A float describing this Actor's previous health. + float GetPrevHealth() const { return m_PrevHealth; } + + /// + /// Gets this Actor's maximum health value. + /// + /// A float describing this Actor's max health. + float GetMaxHealth() const { return m_MaxHealth; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMaxHealth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets this Actor's maximum health value. + // Arguments: New max health value. + // Return value: None. + + void SetMaxHealth(int newValue) { m_MaxHealth = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAimDistance + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the distance between the actor and the view point when not + // sharp aiming. + // Arguments: None. + // Return value: A const int describing how far this actor aims/looks by default. + + int GetAimDistance() const { return m_AimDistance; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetAimDistance + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the distance between the actor and the view point when not + // sharp aiming. + // Arguments: None. + // Return value: A const int describing how far this actor aims/looks by default. + + void SetAimDistance(int newValue) { m_AimDistance = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldCarried + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how many ounces of gold this Actor is carrying. + // Arguments: None. + // Return value: The current amount of carried gold, in Oz. + + float GetGoldCarried() const { return m_GoldCarried; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of this Actor and all its carried + // gold and inventory. + // Arguments: If this is supposed to be adjusted for a specific Tech's subjective + // value, then pass in the native DataModule ID of that tech. 0 means + // no Tech is specified and the base value is returned. + // How much to multiply the value if this happens to be a foreign Tech. + // Return value: The current value of this Actor and all his carried assets. + + float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically named object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. + // Arguments: The Preset name of the object to look for. + // Return value: Whetehr the object was found carried by this. + + bool HasObject(std::string objectName) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObjectInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically grouped object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. + // Arguments: The name of the group to look for. + // Return value: Whetehr the object in the group was found carried by this. + + bool HasObjectInGroup(std::string groupName) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAimAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets this Actor's aim angle. + // Arguments: Whether to adjust the angle for flipping or not. + // Return value: The angle, in radians. + + float GetAimAngle(bool adjustForFlipped = true) const { return adjustForFlipped ? FacingAngle(m_AimAngle) : m_AimAngle; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPassengerSlots + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets this Actor's passenger slots. + // Arguments: None. + // Return value: The Actor's passenger plots + + int GetPassengerSlots() const { return m_PassengerSlots; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetCPUPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of this' brain, or equivalent. + // Arguments: None. + // Return value: A Vector with the absolute position of this' brain. + + virtual Vector GetCPUPos() const { return m_Pos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetEyePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of this' eye, or equivalent, where look + // vector starts from. + // Arguments: None. + // Return value: A Vector with the absolute position of this' eye or view point. + + virtual Vector GetEyePos() const { return m_Pos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAboveHUDPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of the top of this' HUD stack. + // Arguments: None. + // Return value: A Vector with the absolute position of this' HUD stack top point. + + Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, m_HUDStack + 6); } + + /// + /// Gets the offset position of the holster where this Actor draws his devices from. + /// + /// The offset position of the holster. + Vector GetHolsterOffset() const { return m_HolsterOffset; } + + /// + /// Sets the offset position of the holster where this Actor draws his devices from. + /// + /// A new holster offset. + void SetHolsterOffset(Vector newOffset) { m_HolsterOffset = newOffset; } + + /// + /// Gets the offset position of where this Actor reloads his devices from. + /// + /// The offset position of the where this Actor reloads his devices from. + Vector GetReloadOffset() const { return m_ReloadOffset; } + + /// + /// Sets the offset position of the where this Actor reloads his devices from. + /// + /// The new offset position of where this Actor reloads his devices from. + void SetReloadOffset(Vector newOffset) { m_ReloadOffset = newOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetViewPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the point at which this actor is viewing, or that the scene frame + // should be centered on if tracking this Actor's view. In absolute scene + // coordinates. + // Arguments: None. + // Return value: The point in absolute scene coordinates. + + Vector GetViewPoint() const { return m_ViewPoint.IsZero() ? m_Pos : m_ViewPoint; } + + /// + /// Gets the item that is within reach of the Actor at this frame, ready to be be picked up. Ownership is NOT transferred! + /// + /// A pointer to the item that has been determined to be within reach of this Actor, if any. + HeldDevice* GetItemInReach() const { return m_pItemInReach; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLookVector + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the direction where this is looking/aiming. + // Arguments: None. + // Return value: A Vector with the direction in which this is looking along. + + Vector GetLookVector() const { return m_ViewPoint - GetEyePos(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSharpAimProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the normalized amount of sharp aim that has been achieved by this. + // Arguments: None. + // Return value: Sharp aim progress between 0 - 1.0. 1.0 is fully aimed. + + float GetSharpAimProgress() const { return m_SharpAimProgress; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the approximate height of this Actor, standing up. + // Arguments: None. + // Return value: A float with the approximate height, in pixels. + + float GetHeight() const { return m_CharHeight; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetControllerMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this Actor's new Controller input mode. + // Arguments: The new input mode. + // The player which will control this if the input mode was set to player. + // Return value: None. + + void SetControllerMode(Controller::InputMode newMode, int newPlayer = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SwapControllerModes + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this Actor's Controller mode and gives back what it used to be. + // Arguments: The new mode to set to. + // The player which will control this if the input mode was set to player. + // Return value: The old mode that it had before. + + Controller::InputMode SwapControllerModes(Controller::InputMode newMode, int newPlayer = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetStatus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this Actor's status. + // Arguments: A Status enumeration. + // Return value: None. + + void SetStatus(Actor::Status newStatus) { + m_Status = newStatus; + if (newStatus == Actor::Status::UNSTABLE) { + m_StableRecoverTimer.Reset(); + } + } + + /// Gets this Actor's MovementState. + /// + /// This Actor's MovementState. + MovementState GetMovementState() const { return m_MoveState; } + + /// + /// Sets this Actor's MovementState to the new state. + /// + /// This Actor's new MovementState. + void SetMovementState(MovementState newMovementState) { m_MoveState = newMovementState; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this Actor belongs to. + // Arguments: The assigned team number. + // Return value: None. + + void SetTeam(int team) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetGoldCarried + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets how many ounces of gold this Actor is carrying. + // Arguments: The new amount of carried gold, in Oz. + // Return value: None. + + void SetGoldCarried(float goldOz) { m_GoldCarried = goldOz; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetAimAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this Actor's aim angle. + // Arguments: A new angle, in radians. + // Return value: None. + + void SetAimAngle(float newAngle) { + m_AimAngle = newAngle; + Clamp(m_AimAngle, m_AimRange, -m_AimRange); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPassengerSlots + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this Actor's passenger slots. + // Arguments: A new amount of passenger slots. + // Return value: None. + + void SetPassengerSlots(int newPassengerSlots) { m_PassengerSlots = newPassengerSlots; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetViewPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the point at which this actor is viewing, or that the scene frame + // should be centered on if tracking this Actor's view. In absolute scene + // coordinates. + // Arguments: A new point in absolute scene coords. + // Return value: None. + + void SetViewPoint(Vector newPoint) { m_ViewPoint = newPoint; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetItemInReach + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the item that is within reach of the Actor at this frame, so that + // it may be picked up. Ownership is NOT transferred! + // Arguments: A pointer to the item that has been determined to be within reach of + // this Actor. Ownership is NOT transferred! + // Return value: None. + + void SetItemInReach(HeldDevice* pItem) { m_pItemInReach = pItem; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsWithinRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a point on the scene is within range of the currently + // used device and aiming status, if applicable. + // Arguments: A Vector witht he aboslute coordinates of a point to check. + // Return value: Whether the point is within range of this. + + virtual bool IsWithinRange(Vector& point) const { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Look + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Casts an unseen-revealing ray in the direction of where this is facing. + // Arguments: The degree angle to deviate from the current view point in the ray + // casting. A random ray will be chosen out of this +-range. + // The range, in pixels, that the ray will have. + // Return value: Whether any unseen pixels were revealed by this look. + + virtual bool Look(float FOVSpread, float range); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddGold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a certain amount of ounces of gold to this' team's total funds. + // Arguments: The amount in Oz with which to change this' team's gold tally. + // Return value: None. + + void AddGold(float goldOz); + + /// + /// Does the calculations necessary to detect whether this Actor is at rest or not. IsAtRest() retrieves the answer. + /// + void RestDetection() override; + + /// + /// Adds health points to this Actor's current health value. + /// + /// A float specifying the value to add. + /// The resulting total health of this Actor. + const float AddHealth(const float addedHealth) { return m_Health += addedHealth; } + + /// + /// Sets this Actor's current health value. + /// + /// A float specifying the value to set to. + void SetHealth(const float setHealth) { m_Health = setHealth; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsStatus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if this Actor is in a specific status. + // Arguments: Which status to check for. + // Return value: A bool with the answer. + + bool IsStatus(Status which) const { return m_Status == which; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsDead + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if this Actor is dead. + // Arguments: None. + // Return value: A const bool with the answer. + + bool IsDead() const { return m_Status == DEAD; } + + /// + /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. + /// + /// The SliceType of the PieSlice being handled. + /// Whether or not the activated PieSlice SliceType was able to be handled. + virtual bool HandlePieCommand(PieSlice::SliceType pieSliceType) { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAIMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets this' AI mode. + // Arguments: None. + // Return value: The current AI mode. + + int GetAIMode() const { return m_AIMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAIModeIcon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the icon bitmap associated with this' current AI mode and team. + // Arguments: None. + // Return value: The current AI mode icon of this. Ownership is NOT transferred! + + BITMAP* GetAIModeIcon(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetAIMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this' AI mode. + // Arguments: The new AI mode. + // Return value: None. + + void SetAIMode(AIMode newMode = AIMODE_SENTRY) { m_AIMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: AddAISceneWaypoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an absolute scene point to the list of waypoints this is going to + // go to, in order + // Arguments: The new scene point this should try to get to after all other waypoints + // are reached. + // Return value: None. + + void AddAISceneWaypoint(const Vector& waypoint) { m_Waypoints.push_back(std::pair(waypoint, (MovableObject*)NULL)); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: AddAIMOWaypoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an MO in the scene as the next waypoint for this to go to, in order + // Arguments: The new MO this should try to get to after all other waypoints are reached. + // OWNERSHIP IS NOT TRANSFERRED! + // Return value: None. + + void AddAIMOWaypoint(const MovableObject* pMOWaypoint); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ClearAIWaypoints + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes all AI waypoints and clears the current path to the current + // waypoint. The AI Actor will stop in its tracks. + // Arguments: None. + // Return value: None. + + void ClearAIWaypoints() { + m_pMOMoveTarget = 0; + m_Waypoints.clear(); + m_MovePath.clear(); + m_MoveTarget = m_Pos; + m_MoveVector.Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLastAIWaypoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the last or furthest set AI waypoint of this. If none, this' pos + // is returned. + // Arguments: None. + // Return value: The furthest set AI waypoint of this. + + Vector GetLastAIWaypoint() const { + if (!m_Waypoints.empty()) { + return m_Waypoints.back().first; + } else if (!m_MovePath.empty()) { + return m_MovePath.back(); + } + return m_Pos; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetLastMOWaypointID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the ID of the last set AI MO waypoint of this. If none, g_NoMOID is returned. + // Arguments: None. + // Return value: The furthest set AI MO waypoint of this. + + MOID GetAIMOWaypointID() const; + + /// + /// Gets the list of waypoints for this Actor. + /// + /// The list of waypoints for this Actor. + const std::list>& GetWaypointList() const { return m_Waypoints; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetWaypointsSize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how many waypoints this actor have. + // Arguments: None. + // Return value: How many waypoints. + + int GetWaypointsSize() { return m_Waypoints.size(); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ClearMovePath + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the list of coordinates in this' current MovePath, ie the path + // to the next Waypoint. + // Arguments: None. + // Return value: None. + + void ClearMovePath() { + m_MovePath.clear(); + m_MoveTarget = m_Pos; + m_MoveVector.Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: AddToMovePathBeginning + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a coordinate to the beginning of the MovePath, meaning the one + // closest to this Actor. + // Arguments: The new coordinate to add to the front of the MovePath. + // Return value: None. + + void AddToMovePathBeginning(Vector newCoordinate) { + m_MovePath.push_front(newCoordinate); + m_MoveTarget = newCoordinate; + m_MoveVector.Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: AddToMovePathEnd + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a coordinate to the end of the MovePath, meaning the one + // closest to this Actor's next waypoint. + // Arguments: The new coordinate to add to the end of the MovePath. + // Return value: None. + + void AddToMovePathEnd(Vector newCoordinate) { m_MovePath.push_back(newCoordinate); } + + /// + /// Gets the last position in this Actor's move path. + /// + /// The last position in this Actor's move path. + Vector GetMovePathEnd() const { return m_MovePath.back(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveMovePathBeginning + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a coordinate from the beginning of the MovePath, meaning the + // one closest to this Actor. + // Arguments: None. + // Return value: Whether there was any coordinate to remove. If false, the MovePath + // is empty. + + bool RemoveMovePathBeginning() { + if (!m_MovePath.empty()) { + m_MovePath.pop_front(); + m_MoveTarget = m_MovePath.empty() ? m_Pos : m_MovePath.front(); + m_MoveVector.Reset(); + return true; + } + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: RemoveMovePathEnd + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a coordinate from the end of the MovePath, meaning the + // one farthest from this Actor. + // Arguments: None. + // Return value: Whether there was any coordinate to remove. If false, the MovePath + // is empty. + + bool RemoveMovePathEnd() { + if (!m_MovePath.empty()) { + m_MovePath.pop_back(); + return true; + } + return false; + } + + /// + /// Gets a pointer to the MovableObject move target of this Actor. + /// + /// A pointer to the MovableObject move target of this Actor. + const MovableObject* GetMOMoveTarget() const { return m_pMOMoveTarget; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPerceptiveness + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this' perceptiveness to alarming events going on around him. + // Arguments: The current perceptiveness, 0.0 - 1.0 + // Return value: None. + + void SetPerceptiveness(float newPerceptiveness) { m_Perceptiveness = newPerceptiveness; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPerceptiveness + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets this' perceptiveness to alarming events going on around him. + // Arguments: None. + // Return value: The current perceptiveness, 0.0 - 1.0 + + float GetPerceptiveness() const { return m_Perceptiveness; } + + /// + /// Gets whether this actor is able to reveal unseen areas by looking. + /// + /// Whether this actor can reveal unseen areas. + bool GetCanRevealUnseen() const { return m_CanRevealUnseen; } + + /// + /// Sets whether this actor can reveal unseen areas by looking. + /// + /// Whether this actor can reveal unseen areas. + void SetCanRevealUnseen(bool newCanRevealUnseen) { m_CanRevealUnseen = newCanRevealUnseen; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPainThreshold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this' PainThreshold value above which it will play PainSound + // Arguments: Desired PainThreshold value + // Return value: None. + + void SetPainThreshold(float newPainThreshold) { m_PainThreshold = newPainThreshold; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPainThreshold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets this' PainThreshold value above which it will play PainSound + // Arguments: None. + // Return value: The current PainThreshold + + float GetPainThreshold() const { return m_PainThreshold; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AlarmPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes this alarmed about a certian point on in the scene, overriding + // the current AI mode until a certain time has passed. + // Arguments: The new scene point this should look at and see if anything dangerous + // is there. + // Return value: None. + + void AlarmPoint(const Vector& alarmPoint) { + if (m_AlarmSound && m_AlarmTimer.IsPastSimTimeLimit()) { + m_AlarmSound->Play(alarmPoint); + } + if (m_AlarmTimer.GetElapsedSimTimeMS() > 50) { + m_AlarmTimer.Reset(); + m_LastAlarmPos = m_PointingTarget = alarmPoint; + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAlarmPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets any point on the scene this actor should be alarmed about this frame. + // Arguments: None. + // Return value: The new scene point this should look at and see if anything dangerous + // is there or (0,0) if nothing is alarming. + + Vector GetAlarmPoint() { + if (m_AlarmTimer.GetElapsedSimTimeMS() > g_TimerMan.GetDeltaTimeMS()) { + return Vector(); + } + return m_LastAlarmPos; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: AddInventoryItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an inventory item to this Actor. + // Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! + // Return value: None.. + + virtual void AddInventoryItem(MovableObject* pItemToAdd) { AddToInventoryBack(pItemToAdd); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveInventoryItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a specified item from the actor's inventory. Only one item is removed at a time. + // Arguments: Preset name of an item to remove. + // Return value: None. + + void RemoveInventoryItem(const std::string& presetName) { RemoveInventoryItem("", presetName); } + + /// + /// Removes the first inventory item with the given module name and preset name. + /// + /// The module name of the item to remove. + /// The preset name of the item to remove. + void RemoveInventoryItem(const std::string& moduleName, const std::string& presetName); + + /// + /// Removes and returns the inventory item at the given index. Ownership IS transferred. + /// + /// The index of the inventory item to remove. + /// An owning pointer to the removed inventory item. + MovableObject* RemoveInventoryItemAtIndex(int inventoryIndex); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SwapNextInventory + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Swaps the next MovableObject carried by this Actor and puts one not + // currently carried into the into the back of the inventory of this. + // Arguments: A pointer to the external MovableObject to trade in. Ownership IS xferred! + // If 0 is passed in, nothing will be added to the inventory. + // Whether to mute the sound on this event. Override for the loading screen hack. + // Return value: The next MovableObject in this Actor's inventory. Ownership IS xferred! + // If there are no MovableObject:s in inventory, 0 will be returned. + + virtual MovableObject* SwapNextInventory(MovableObject* pSwapIn = nullptr, bool muteSound = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SwapPrevInventory + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Swaps the prev MovableObject carried by this Actor and puts one not + // currently carried into the into the back of the inventory of this. + // Arguments: A pointer to the external MovableObject to trade in. Ownership IS xferred! + // If 0 is passed in, nothing will be added to the inventory. + // Return value: The prev MovableObject in this Actor's inventory. Ownership IS xferred! + // If there are no MovableObject:s in inventory, 0 will be returned. + + virtual MovableObject* SwapPrevInventory(MovableObject* pSwapIn = nullptr); + + /// + /// Swaps the inventory items at the given indices. Will return false if a given index is invalid. + /// + /// The index of one item. + /// The index of the other item. + /// Whether or not the swap was successful. + bool SwapInventoryItemsByIndex(int inventoryIndex1, int inventoryIndex2); + + /// + /// Sets the inventory item at the given index as the new inventory item, and gives back the one that was originally there. + /// If an invalid index is given, the new item will be put in the back of the inventory, and nullptr will be returned. + /// + /// The new item that should be at the given inventory index. Cannot be a nullptr. Ownership IS transferred. + /// The inventory index the new item should be placed at. + /// The inventory item that used to be at the inventory index. Ownership IS transferred. + MovableObject* SetInventoryItemAtIndex(MovableObject* newInventoryItem, int inventoryIndex); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DropAllInventory + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Ejects all inventory items that this is carrying. It may not happen + // instantaneously, so check for ejection being complete with + // IsInventoryEmpty(). + // Arguments: None. + // Return value: None. + + virtual void DropAllInventory(); + + /// + /// Converts all of the Gold carried by this Actor into MovableObjects and ejects them into the Scene. + /// + virtual void DropAllGold(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetInventorySize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells how many things are in the invetory + // Arguments: None. + // Return value: The number of things in the inventory + + int GetInventorySize() const { return m_Inventory.size(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsInventoryEmpty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether inventory is completely empty + // Arguments: None. + // Return value: Whether inventory is completely empty. + + bool IsInventoryEmpty() { return m_Inventory.empty(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetInventory + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the deque of inventory of this. Ownership is NOT transferred. + // Arguments: None. + // Return value: A const pointer to the inventory deque of this. OWNERSHIP IS NOT TRANSFERRED! + + const std::deque* GetInventory() const { return &m_Inventory; } + + /// + /// Returns the maximum total mass this Actor can carry in its inventory. + /// + /// The maximum carriable mass of this Actor. + float GetMaxInventoryMass() const { return m_MaxInventoryMass; } + + /// + /// Attempts to add an item to the front of our inventory. + /// + /// Whether we succeeded in adding the item. We may fail if the object doesn't exist or is set to delete. + bool AddToInventoryFront(MovableObject* itemToAdd); + + /// + /// Attempts to add an item to the back of our inventory. + /// + /// Whether we succeeded in adding the item. We may fail if the object doesn't exist or is set to delete. + bool AddToInventoryBack(MovableObject* itemToAdd); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAimRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The limit of this actors aiming angle, in each direction, in radians. + // Arguments: None. + // Return value: The arc range of the aiming angle in radians. + // Eg if HalfPI, it means full 180 degree range + + float GetAimRange() const { return m_AimRange; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetAimRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the limit of this actors aiming angle, in each direction, in radians. + // Arguments: The arc range of the aiming angle in radians. + // Eg if HalfPI, it means full 180 degree range + // Return value: None. + + void SetAimRange(float range) { m_AimRange = range; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawWaypoints + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes this draw its current waypoints and related data on the scene in + // its HUD drawing stage. + // Arguments: Whether to enable or disable the drawing of the waypoints. + // Return value: None. + + void DrawWaypoints(bool drawWaypoints = true) { m_DrawWaypoints = drawWaypoints; } + + /// + /// Destroys this MOSRotating and creates its specified Gibs in its place with appropriate velocities. + /// Any Attachables are removed and also given appropriate velocities. + /// + /// The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. + // Arguments: Reference to the HitData struct which describes the collision. This + // will be modified to represent the results of the collision. + // Return value: Whether the collision has been deemed valid. If false, then disregard + // any impulses in the Hitdata. + + bool CollideAtPoint(HitData& hitData) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ParticlePenetration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Determines whether a particle which has hit this MO will penetrate, + // and if so, whether it gets lodged or exits on the other side of this + // MO. Appropriate effects will be determined and applied ONLY IF there + // was penetration! If not, nothing will be affected. + // Arguments: The HitData describing the collision in detail, the impulses have to + // have been filled out! + // Return value: Whether the particle managed to penetrate into this MO or not. If + // somehting but a MOPixel or MOSParticle is being passed in as hitor, + // false will trivially be returned here. + + bool ParticlePenetration(HitData& hd) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreTravel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does stuff that needs to be done before Travel(). Always call before + // calling Travel. + // Arguments: None. + // Return value: None. + + void PreTravel() override { + MOSRotating::PreTravel(); + m_GoldPicked = false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMovePathToUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this' AI's move path to be updated. Will update the path to the + // current waypoint, if any. + // Arguments: None. + // Return value: None. + + void SetMovePathToUpdate() { m_UpdateMovePath = true; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMovePathSize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how many waypoints there are in the MovePath currently + // Arguments: None. + // Return value: The number of waypoints in the MovePath. + + int GetMovePathSize() const { return m_MovePath.size(); } + + /// + /// Starts updating this Actor's movepath. + /// + virtual void UpdateMovePath(); + + /// + /// Returns whether we're waiting on a new pending movepath. + /// + /// Whether we're waiting on a new pending movepath. + bool IsWaitingOnNewMovePath() const { return m_PathRequest != nullptr || m_UpdateMovePath; } + + /// + /// Estimates what material strength this actor can penetrate. + /// + /// The actor's dig strength. + virtual float EstimateDigStrength() const; + + /// + /// Gets this Actor's base dig strength, or the strength of terrain they can expect to walk through without tools. + /// + /// The actors base dig strength. + float GetAIBaseDigStrength() const { return m_AIBaseDigStrength; } + + /// + /// Sets this Actor's base dig strength, or the strength of terrain they can expect to walk through without tools. + /// + /// The new base dig strength for this Actor. + void SetAIBaseDigStrength(float newAIBaseDigStrength) { m_AIBaseDigStrength = newAIBaseDigStrength; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreControllerUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + virtual void PreControllerUpdate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: FullUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the full state of this object in one call. (PreControllerUpdate(), Controller::Update(), and Update()) + // Arguments: None. + // Return value: None. + + virtual void FullUpdate() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetDeploymentID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets deployment ID for this actor + // Arguments: New deployment id. + // Return value: None. + + void SetDeploymentID(unsigned int newID) { m_DeploymentID = newID; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDeploymentID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets deployment ID of this actor + // Arguments: None. + // Return value: Returns deployment id of this actor. + + unsigned int GetDeploymentID() const { return m_DeploymentID; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetSightDistance + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns actor's sight distance. + // Arguments: None. + // Return value: Returns actor's sight distance. + + float GetSightDistance() const { return m_SightDistance; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetSightDistance + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets actor's sight distance. + // Arguments: New sight distance value. + // Return value: None. + + void SetSightDistance(float newValue) { m_SightDistance = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Actor's current graphical HUD overlay representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // Which player's screen this is being drawn to. May affect what HUD elements + // get drawn etc. + // Return value: None. + + void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: VerifyMOIDIndex + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Verifieis whether all actor's MO has correct IDs. Should be used in Debug mode only. + // Arguments: None. + // Return value: None. + + void VerifyMOIDs(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTravelImpulseDamage + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns Threshold for taking damage from travel impulses, in kg * m/s + // Arguments: None. + // Return value: Threshold for taking damage from travel impulses, in kg * m/s + + float GetTravelImpulseDamage() const { return m_TravelImpulseDamage; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetTravelImpulseDamage + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets Threshold for taking damage from travel impulses, in kg * m/s + // Arguments: Threshold for taking damage from travel impulses, in kg * m/s + // Return value: None. + + void SetTravelImpulseDamage(float value) { m_TravelImpulseDamage = value; } + + /// + /// Gets this Actor's body hit sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this Actor's body hit sound. + SoundContainer* GetBodyHitSound() const { return m_BodyHitSound; } + + /// + /// Sets this Actor's body hit sound. Ownership IS transferred! + /// + /// The new SoundContainer for this Actor's body hit sound. + void SetBodyHitSound(SoundContainer* newSound) { m_BodyHitSound = newSound; } + + /// + /// Gets this Actor's alarm sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this Actor's alarm sound. + SoundContainer* GetAlarmSound() const { return m_AlarmSound; } + + /// + /// Sets this Actor's alarm sound. Ownership IS transferred! + /// + /// The new SoundContainer for this Actor's alarm sound. + void SetAlarmSound(SoundContainer* newSound) { m_AlarmSound = newSound; } + + /// + /// Gets this Actor's pain sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this Actor's pain sound. + SoundContainer* GetPainSound() const { return m_PainSound; } + + /// + /// Sets this Actor's pain sound. Ownership IS transferred! + /// + /// The new SoundContainer for this Actor's pain sound. + void SetPainSound(SoundContainer* newSound) { m_PainSound = newSound; } + + /// + /// Gets this Actor's death sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this Actor's death sound. + SoundContainer* GetDeathSound() const { return m_DeathSound; } + + /// + /// Sets this Actor's death sound. Ownership IS transferred! + /// + /// The new SoundContainer for this Actor's death sound. + void SetDeathSound(SoundContainer* newSound) { m_DeathSound = newSound; } + + /// + /// Gets this Actor's device switch sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this Actor's device switch sound. + SoundContainer* GetDeviceSwitchSound() const { return m_DeviceSwitchSound; } + + /// + /// Sets this Actor's device switch sound. Ownership IS transferred! + /// + /// The new SoundContainer for this Actor's device switch sound. + void SetDeviceSwitchSound(SoundContainer* newSound) { m_DeviceSwitchSound = newSound; } + + /// + /// Gets the X and Y thresholds for how fast the actor can travel before losing stability. + /// + /// A Vector with the X and Y thresholds for how fast the actor can travel before losing stability. + Vector GetStableVel() const { return m_StableVel; } + + /// + /// Sets the X and Y thresholds for how fast the actor can travel before losing stability. + /// + /// New value for how fast the actor can travel before losing stability on X axis. + /// New value for how fast the actor can travel before losing stability on Y axis. + void SetStableVel(float newVelX, float newVelY) { m_StableVel.SetXY(newVelX, newVelY); } + + /// + /// Sets the X and Y thresholds for how fast the actor can travel before losing stability. + /// + /// Vector with new values for how fast the actor can travel before losing stability on both axis. + void SetStableVel(Vector newVelVector) { m_StableVel = newVelVector; } + + /// + /// Gets the recovery delay from UNSTABLE to STABLE, in MS. + /// + /// The recovery delay, in MS. + int GetStableRecoverDelay() const { return m_StableRecoverDelay; } + + /// + /// Sets the recovery delay from UNSTABLE to STABLE, in MS. + /// + /// The recovery delay, in MS. + void SetStableRecoverDelay(int newRecoverDelay) { m_StableRecoverDelay = newRecoverDelay; } + + /// + /// Gets the distance in which the Actor will have considered itself to have reached it's waypoint. + /// + /// The move proximity limit. + float GetMoveProximityLimit() const { return m_MoveProximityLimit; } + + /// + /// Sets the distance in which the Actor will have considered itself to have reached it's waypoint. + /// + /// The move proximity limit. + void SetMoveProximityLimit(float newProximityLimit) { m_MoveProximityLimit = newProximityLimit; } + + /// + /// Gets whether or not this Actor has the organic flag set and should be considered as organic. + /// + /// Whether or not this Actor has the organic flag set and should be considered as organic. + bool IsOrganic() const { return m_Organic; } + + /// + /// Gets whether or not this Actor has the mechanical flag set and should be considered as mechanical. + /// + /// Whether or not this Actor has the mechanical flag set and should be considered as mechanical. + bool IsMechanical() const { return m_Mechanical; } + + /// + /// Gets whether or not this Actor's limb push forces have been disabled. + /// + /// Whether or not this Actor's limb push forces have been disabled. + bool GetLimbPushForcesAndCollisionsDisabled() const { return m_LimbPushForcesAndCollisionsDisabled; } + + /// + /// Sets whether or not this Actor's limb push forces should be disabled. + /// + /// Whether or not this Actor's limb push forces should be disabled. + void SetLimbPushForcesAndCollisionsDisabled(bool newLimbPushForcesAndCollisionsDisabled) { m_LimbPushForcesAndCollisionsDisabled = newLimbPushForcesAndCollisionsDisabled; } + + /// + /// Gets the default PieMenu name for this type. + /// + /// The default PieMenu name for this type. + virtual std::string GetDefaultPieMenuName() const { return "Default Actor Pie Menu"; } + + /// + /// Gets a pointer to the PieMenu for this Actor. Ownership is NOT transferred. + /// + /// The PieMenu for this Actor. + PieMenu* GetPieMenu() const { return m_PieMenu.get(); } + + /// + /// Sets the PieMenu for this Actor. Ownership IS transferred. + /// + /// The new PieMenu for this Actor. + void SetPieMenu(PieMenu* newPieMenu) { + m_PieMenu = std::unique_ptr(newPieMenu); + m_PieMenu->Create(this); + m_PieMenu->AddWhilePieMenuOpenListener(this, std::bind(&Actor::WhilePieMenuOpenListener, this, m_PieMenu.get())); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Function that is called when we get a new movepath. + /// This processes and cleans up the movepath. + /// + virtual void OnNewMovePath(); + + // Member variables + static Entity::ClassInfo m_sClass; + + enum ActionState { + MOVING = 0, + MOVING_FAST, + FIRING, + ActionStateCount + }; + + enum AimState { + AIMSTILL = 0, + AIMUP, + AIMDOWN, + AimStateCount + }; + + AtomGroup* m_pHitBody; + Controller m_Controller; + bool m_PlayerControllable; //!< Whether or not this Actor can be controlled by human players. + + // Sounds + SoundContainer* m_BodyHitSound; + SoundContainer* m_AlarmSound; + SoundContainer* m_PainSound; + SoundContainer* m_DeathSound; + SoundContainer* m_DeviceSwitchSound; + + int m_Status; + float m_Health; + // Maximum health + float m_MaxHealth; + // The health of the previous frame, so we can track damage + float m_PrevHealth; + // Not owned by this! + const Icon* m_pTeamIcon; + // Not owned by this! + const Icon* m_pControllerIcon; + // Timing the last second to store the position each second so we can determine larger movement + Timer m_LastSecondTimer; + // This' position up to a second ago + Vector m_LastSecondPos; + // Movement since last whole second + Vector m_RecentMovement; + // Threshold for taking damage from travel impulses, in kg * m/s + float m_TravelImpulseDamage; + // Timer for timing the delay before regaining stability after losing it + Timer m_StableRecoverTimer; + // Thresholds in both x and y for how fast the actor can travel before losing stability. Meters per second (m/s). + Vector m_StableVel; + int m_StableRecoverDelay; //!< The delay before regaining stability after losing it, in MS + // Timer for the heartbeat of this Actor + Timer m_HeartBeat; + // Timer for timing how long this has been under Control + Timer m_NewControlTmr; + // Death timing timer + Timer m_DeathTmr; + // Amount of Gold carried, in ounces. + float m_GoldCarried; + // Whether or not any gold was picked up this frame. + bool m_GoldPicked; + // Aiming state + char m_AimState; + // The arc range of the aiming angle, in each direction, in radians. Eg if HalfPI, it means full 180 degree range + float m_AimRange; + // Current Aim angle within the AimRange + float m_AimAngle; + // How far the actor aims/looks by default + float m_AimDistance; + // Aiming timing timer + Timer m_AimTmr; + // For timing the transition from regular aim to sharp aim + Timer m_SharpAimTimer; + // The time it takes to achieve complete full sharp aiming + int m_SharpAimDelay; + // The velocity + float m_SharpAimSpeed; + // Normalzied scalar showing storing much sharp aim progress has been made + float m_SharpAimProgress; + // If sharp aim has been maxed out, ie it's either at its max, or being limited by some obstruction + bool m_SharpAimMaxedOut; + // Point at this target when devicestate is in POINTING mode + Vector m_PointingTarget; + // Last seen enemy target + Vector m_SeenTargetPos; + // Timer measuring how long this has been alarmed by a nearby gunshot etc. + Timer m_AlarmTimer; + // Position of the last thing that alarmed us + Vector m_LastAlarmPos; + // How far this guy's AI can see when he's just looking ahead + float m_SightDistance; + // How perceptive this is of alarming events going on around him, 0.0 - 1.0 + float m_Perceptiveness; + /// Damage value above which this will play PainSound + float m_PainThreshold; + // Whether or not this actor can reveal unseen areas by looking + bool m_CanRevealUnseen; + // About How tall is the Actor, in pixels? + float m_CharHeight; + // Speed at which the m_AimAngle will change, in radians/s. + // float + // The offset position of the holster where this Actor draws his devices from. + Vector m_HolsterOffset; + Vector m_ReloadOffset; //!< The offset position of where this Actor reloads his devices from. + // The point at which this actor is viewing, or the scene frame + // should be centered on if tracking this Actor's view. + // In absolute scene coordinates. + Vector m_ViewPoint; + // The inventory of carried MovableObjects of this Actor. They are also Owned by this. + std::deque m_Inventory; + float m_MaxInventoryMass; //!< The mass limit for this Actor's inventory. -1 means there's no limit. + // The device that can/will be picked up + HeldDevice* m_pItemInReach; + // HUD positioning aid + int m_HUDStack; + // ID of deployment which spawned this actor + unsigned int m_DeploymentID; + // How many passenger slots this actor will take in a craft + int m_PassengerSlots; + // Most actors can walk through stuff that's soft enough, so we start with a base penetration amount + float m_AIBaseDigStrength; + // The mass that this actor had upon spawning, i.e with no inventory, no gold and holding no items + float m_BaseMass; + + //////////////////// + // AI States + + enum LateralMoveState { + LAT_STILL = 0, + LAT_LEFT, + LAT_RIGHT + }; + + enum ObstacleState { + PROCEEDING = 0, + BACKSTEPPING, + DIGPAUSING, + JUMPING, + SOFTLANDING + }; + + enum TeamBlockState { + NOTBLOCKED = 0, + BLOCKED, + IGNORINGBLOCK, + FOLLOWWAIT + }; + // Unknown team icon + static std::vector m_apNoTeamIcon; + // The AI mode icons + static BITMAP* m_apAIIcons[AIMODE_COUNT]; + // Selection arrow + static std::vector m_apSelectArrow; + // Selection arrow + static std::vector m_apAlarmExclamation; + // Whether the static icons have been loaded yet or not + static bool m_sIconsLoaded; + // The current mode the AI is set to perform as + AIMode m_AIMode; + // The list of waypoints remaining between which the paths are made. If this is empty, the last path is in teh MovePath + // The MO pointer in the pair is nonzero if the waypoint is tied to an MO in the scene, and gets updated each UpdateAI. This needs to be checked for validity/existence each UpdateAI + std::list> m_Waypoints; + // Whether to draw the waypoints or not in the HUD + bool m_DrawWaypoints; + // Absolute target to move to on the scene; this is usually the point at the front of the movepath list + Vector m_MoveTarget; + // The MO we're currently following, if any. If still valid, this' position will update the MoveTarget each UpdateAI. + const MovableObject* m_pMOMoveTarget; + // The point previous on the path to the one currently assigned the move target + Vector m_PrevPathTarget; + // The relative, scene-wrapped difference between the current m_Pos and the m_MoveTarget. + Vector m_MoveVector; + // The calculated path to get to that move-to target + std::list m_MovePath; + // The current pathfinding request + std::shared_ptr m_PathRequest; + // Whether it's time to update the path + bool m_UpdateMovePath; + // The minimum range to consider having reached a move target is considered + float m_MoveProximityLimit; + // Current movement state. + MovementState m_MoveState; + + bool m_Organic; //!< Flag for whether or not this Actor is organic. Useful for lua purposes and mod support. + bool m_Mechanical; //!< Flag for whether or not this Actor is robotic. Useful for lua purposes and mod support. + + bool m_LimbPushForcesAndCollisionsDisabled; // m_PieMenu; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Actor, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + Actor(const Actor& reference) = delete; + Actor& operator=(const Actor& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/Arm.cpp b/Source/Entities/Arm.cpp index dcf8a8c5fc..e1c6ebf32e 100644 --- a/Source/Entities/Arm.cpp +++ b/Source/Entities/Arm.cpp @@ -9,7 +9,7 @@ namespace RTE { ConcreteClassInfo(Arm, Attachable, 50); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Arm::Clear() { m_MaxLength = 0; @@ -35,7 +35,7 @@ namespace RTE { m_HeldDeviceThisArmIsTryingToSupport = nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Arm::Create() { if (Attachable::Create() < 0) { @@ -51,9 +51,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Arm::Create(const Arm &reference) { + int Arm::Create(const Arm& reference) { if (reference.m_HeldDevice) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_HeldDevice->GetUniqueID()); } @@ -80,17 +80,17 @@ namespace RTE { m_ThrowStrength = reference.m_ThrowStrength; if (reference.m_HeldDevice) { - SetHeldDevice(dynamic_cast(reference.m_HeldDevice->Clone())); + SetHeldDevice(dynamic_cast(reference.m_HeldDevice->Clone())); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Arm::ReadProperty(const std::string_view &propName, Reader &reader) { + int Arm::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Attachable::ReadProperty(propName, reader)); - + MatchProperty("MaxLength", { reader >> m_MaxLength; }); MatchProperty("MoveSpeed", { reader >> m_MoveSpeed; }); MatchForwards("HandIdleOffset") MatchProperty("IdleOffset", { reader >> m_HandIdleOffset; }); @@ -100,14 +100,14 @@ namespace RTE { }); MatchProperty("GripStrength", { reader >> m_GripStrength; }); MatchProperty("ThrowStrength", { reader >> m_ThrowStrength; }); - MatchProperty("HeldDevice", { SetHeldDevice(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("HeldDevice", { SetHeldDevice(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Arm::Save(Writer &writer) const { + int Arm::Save(Writer& writer) const { Attachable::Save(writer); writer.NewPropertyWithValue("MaxLength", m_MaxLength); @@ -121,45 +121,46 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::SetHandPos(const Vector &newHandPos) { + void Arm::SetHandPos(const Vector& newHandPos) { SetHandCurrentOffset(g_SceneMan.ShortestDistance(m_JointPos, newHandPos, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY())); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::AddHandTarget(const std::string &description, const Vector &handTargetPositionToAdd, float delayAtTarget) { + void Arm::AddHandTarget(const std::string& description, const Vector& handTargetPositionToAdd, float delayAtTarget) { Vector handTargetOffsetToAdd = g_SceneMan.ShortestDistance(m_JointPos, handTargetPositionToAdd, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()); if (!handTargetOffsetToAdd.IsZero()) { handTargetOffsetToAdd.ClampMagnitude(m_MaxLength / 2.0F, m_MaxLength); if (m_HandTargets.empty()) { m_HandHasReachedCurrentTarget = false; } else if (description == m_HandTargets.back().Description) { - m_HandTargets.back() = { description, handTargetOffsetToAdd, std::max(m_HandTargets.back().DelayAtTarget, delayAtTarget), m_HFlipped }; + m_HandTargets.back() = {description, handTargetOffsetToAdd, std::max(m_HandTargets.back().DelayAtTarget, delayAtTarget), m_HFlipped}; return; } m_HandTargets.emplace(description, handTargetOffsetToAdd, delayAtTarget, m_HFlipped); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::SetHeldDevice(HeldDevice *newHeldDevice) { - if (m_HeldDevice && m_HeldDevice->IsAttached()) { RemoveAndDeleteAttachable(m_HeldDevice); } + void Arm::SetHeldDevice(HeldDevice* newHeldDevice) { + if (m_HeldDevice && m_HeldDevice->IsAttached()) { + RemoveAndDeleteAttachable(m_HeldDevice); + } m_HeldDevice = newHeldDevice; if (newHeldDevice != nullptr) { AddAttachable(newHeldDevice); m_HardcodedAttachableUniqueIDsAndSetters.try_emplace( - newHeldDevice->GetUniqueID(), - [](MOSRotating *parent, Attachable *attachable) { - HeldDevice *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetHeldDevice."); - dynamic_cast(parent)->SetHeldDevice(castedAttachable); - } - ); + newHeldDevice->GetUniqueID(), + [](MOSRotating* parent, Attachable* attachable) { + HeldDevice* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetHeldDevice."); + dynamic_cast(parent)->SetHeldDevice(castedAttachable); + }); } // Reset our fire state, so that our activation of a prior device does not "leak" @@ -168,21 +169,21 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - HeldDevice * Arm::SwapHeldDevice(HeldDevice *newHeldDevice) { - Attachable *previousHeldDevice = RemoveAttachable(m_HeldDevice, false, false); + HeldDevice* Arm::SwapHeldDevice(HeldDevice* newHeldDevice) { + Attachable* previousHeldDevice = RemoveAttachable(m_HeldDevice, false, false); SetHeldDevice(newHeldDevice); - return dynamic_cast(previousHeldDevice); + return dynamic_cast(previousHeldDevice); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Arm::HandIsCloseToTargetOffset(const Vector &targetOffset) const { + bool Arm::HandIsCloseToTargetOffset(const Vector& targetOffset) const { return (m_HandCurrentOffset - targetOffset).MagnitudeIsLessThan(m_MaxLength / 10.0F); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Arm::Update() { Attachable::PreUpdate(); @@ -198,7 +199,7 @@ namespace RTE { m_HeldDeviceThisArmIsTryingToSupport = nullptr; } - bool heldDeviceIsAThrownDevice = m_HeldDevice && dynamic_cast(m_HeldDevice); + bool heldDeviceIsAThrownDevice = m_HeldDevice && dynamic_cast(m_HeldDevice); // If there's no HeldDevice, or it's a ThrownDevice, the Arm should rotate to match the hand's current offset for visuals/aiming (instead of using the AHuman's aim angle). if (heldDeviceIsAThrownDevice || !m_HeldDevice) { @@ -231,7 +232,7 @@ namespace RTE { m_HandIdleRotation = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Arm::UpdateHandCurrentOffset(bool armHasParent, bool heldDeviceIsAThrownDevice) { if (armHasParent) { @@ -239,7 +240,7 @@ namespace RTE { if (m_HandTargets.empty()) { if (m_HeldDevice) { targetOffset = m_HeldDevice->GetStanceOffset(); - if (HDFirearm *heldFirearm = dynamic_cast(m_HeldDevice); heldFirearm && heldFirearm->GetCurrentReloadAngle() != 0) { + if (HDFirearm* heldFirearm = dynamic_cast(m_HeldDevice); heldFirearm && heldFirearm->GetCurrentReloadAngle() != 0) { if (heldFirearm->IsReloading()) { float reloadProgressSin = std::sin(heldFirearm->GetReloadProgress() * c_PI); // TODO: There are a few values available for customization here, but they need clear property names. The following plays out well as a default. @@ -251,13 +252,13 @@ namespace RTE { float reloadAngle = (heldFirearm->GetCurrentReloadAngle() - inheritedBodyAngle * GetFlipFactor()) * reloadProgressSin; heldFirearm->SetInheritedRotAngleOffset(reloadAngle); targetOffset.RadRotate(reloadAngle * GetFlipFactor()); - float retractionRate = 0.5F * noSupportFactor; // Another value potentially open for customization. + float retractionRate = 0.5F * noSupportFactor; // Another value potentially open for customization. targetOffset.SetMagnitude(targetOffset.GetMagnitude() * (1.0F - reloadProgressSin * retractionRate)); } else if (heldFirearm->DoneReloading()) { heldFirearm->SetInheritedRotAngleOffset(0); } } - } else if (bool parentIsStable = dynamic_cast(m_Parent)->IsStatus(Actor::Status::STABLE); parentIsStable && m_HeldDeviceThisArmIsTryingToSupport) { + } else if (bool parentIsStable = dynamic_cast(m_Parent)->IsStatus(Actor::Status::STABLE); parentIsStable && m_HeldDeviceThisArmIsTryingToSupport) { targetOffset = g_SceneMan.ShortestDistance(m_JointPos, m_HeldDeviceThisArmIsTryingToSupport->GetSupportPos(), g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()); } else { targetOffset = m_HandIdleOffset.GetXFlipped(m_Parent->IsHFlipped()).GetRadRotatedCopy(m_Parent->GetRotAngle()); @@ -266,7 +267,7 @@ namespace RTE { targetOffset.RadRotate(m_HandIdleRotation); } } else { - const HandTarget &nextHandTarget = m_HandTargets.front(); + const HandTarget& nextHandTarget = m_HandTargets.front(); targetOffset = nextHandTarget.TargetOffset.GetXFlipped(nextHandTarget.HFlippedWhenTargetWasCreated != m_HFlipped); } @@ -302,18 +303,18 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::AccountForHeldDeviceRecoil(const HeldDevice *heldDevice, Vector &targetOffset) { + void Arm::AccountForHeldDeviceRecoil(const HeldDevice* heldDevice, Vector& targetOffset) { if (!heldDevice->GetRecoilForce().IsZero()) { float totalGripStrength = m_GripStrength * heldDevice->GetGripStrengthMultiplier(); - if (totalGripStrength == 0.0F) { - totalGripStrength = heldDevice->GetJointStrength(); + if (totalGripStrength == 0.0F) { + totalGripStrength = heldDevice->GetJointStrength(); } if (heldDevice->GetSupported()) { - const AHuman *rootParentAsAHuman = dynamic_cast(GetRootParent()); - const Arm *supportingArm = rootParentAsAHuman ? rootParentAsAHuman->GetBGArm() : nullptr; + const AHuman* rootParentAsAHuman = dynamic_cast(GetRootParent()); + const Arm* supportingArm = rootParentAsAHuman ? rootParentAsAHuman->GetBGArm() : nullptr; if (supportingArm) { if (supportingArm->GetGripStrength() < 0) { totalGripStrength = -1.0F; @@ -341,11 +342,11 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::AccountForHeldDeviceTerrainClipping(const HeldDevice *heldDevice, Vector &targetOffset) const { + void Arm::AccountForHeldDeviceTerrainClipping(const HeldDevice* heldDevice, Vector& targetOffset) const { Vector newMuzzlePos = (m_JointPos + targetOffset) - RotateOffset(heldDevice->GetJointOffset()) + RotateOffset(heldDevice->GetMuzzleOffset()); - Vector midToMuzzle = RotateOffset({ heldDevice->GetIndividualRadius(), 0 }); + Vector midToMuzzle = RotateOffset({heldDevice->GetIndividualRadius(), 0}); Vector midOfDevice = newMuzzlePos - midToMuzzle; Vector terrainOrMuzzlePosition; @@ -357,7 +358,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Arm::UpdateArmFrame() { float halfMaxLength = m_MaxLength / 2.0F; @@ -365,20 +366,22 @@ namespace RTE { m_Frame = static_cast(std::clamp(newFrame, 0.0F, static_cast(m_FrameCount - 1))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::Draw(BITMAP *targetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { + void Arm::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { Attachable::Draw(targetBitmap, targetPos, mode, onlyPhysical); if (!onlyPhysical && (mode == DrawMode::g_DrawColor || mode == DrawMode::g_DrawWhite || mode == DrawMode::g_DrawTrans)) { DrawHand(targetBitmap, targetPos, mode); - if (m_HeldDevice && m_HeldDevice->IsDrawnAfterParent()) { m_HeldDevice->Draw(targetBitmap, targetPos, mode, onlyPhysical); } + if (m_HeldDevice && m_HeldDevice->IsDrawnAfterParent()) { + m_HeldDevice->Draw(targetBitmap, targetPos, mode, onlyPhysical); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::DrawHand(BITMAP *targetBitmap, const Vector &targetPos, DrawMode mode) const { + void Arm::DrawHand(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode) const { Vector handPos(m_JointPos + m_HandCurrentOffset + (m_Recoiled ? m_RecoilOffset : Vector()) - targetPos); handPos -= Vector(static_cast(m_HandSpriteBitmap->w / 2), static_cast(m_HandSpriteBitmap->h / 2)); @@ -389,7 +392,7 @@ namespace RTE { draw_sprite(targetBitmap, m_HandSpriteBitmap, handPos.GetFloorIntX(), handPos.GetFloorIntY()); } } else { - //TODO this draw_character_ex won't draw flipped. It should draw onto a temp bitmap and then draw that flipped. Maybe it can reuse a temp bitmap from MOSR, maybe not? + // TODO this draw_character_ex won't draw flipped. It should draw onto a temp bitmap and then draw that flipped. Maybe it can reuse a temp bitmap from MOSR, maybe not? if (mode == DrawMode::g_DrawWhite) { draw_character_ex(targetBitmap, m_HandSpriteBitmap, handPos.GetFloorIntX(), handPos.GetFloorIntY(), g_WhiteColor, -1); } else { @@ -397,4 +400,4 @@ namespace RTE { } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/Arm.h b/Source/Entities/Arm.h index c9a9839805..ea82eeb528 100644 --- a/Source/Entities/Arm.h +++ b/Source/Entities/Arm.h @@ -13,7 +13,6 @@ namespace RTE { class Arm : public Attachable { public: - EntityAllocation(Arm); SerializableOverrideMethods; ClassInfoGetters; @@ -35,7 +34,7 @@ namespace RTE { /// /// A reference to the Arm to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Arm &reference); + int Create(const Arm& reference); #pragma endregion #pragma region Destruction @@ -48,12 +47,20 @@ namespace RTE { /// Destroys and resets (through Clear()) the Arm object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { Attachable::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + Attachable::Destroy(); + } + Clear(); + } /// /// Resets the entire Arm, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Attachable::Reset(); } + void Reset() override { + Clear(); + Attachable::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -85,7 +92,7 @@ namespace RTE { /// Sets the default idle offset of this Arm's hand, i.e. the default offset from the joint position that this Arm will try to move to when not moving towards a position. /// /// The new idle offset of this Arm's hand. - void SetHandIdleOffset(const Vector &newDefaultIdleOffset) { m_HandIdleOffset = newDefaultIdleOffset; } + void SetHandIdleOffset(const Vector& newDefaultIdleOffset) { m_HandIdleOffset = newDefaultIdleOffset; } /// /// Gets the rotation that is being applied to this Arm's hand, if it's using an idle offset. @@ -109,8 +116,11 @@ namespace RTE { /// Sets the current offset of this Arm's hand, i.e. its distance from the joint position. The value is capped to the max length of the Arm. /// /// The new current offset of this Arm's hand. - //TODO maybe don't want this in favor of SetHandPos? - void SetHandCurrentOffset(const Vector &newHandOffset) { m_HandCurrentOffset = newHandOffset; m_HandCurrentOffset.CapMagnitude(m_MaxLength); } + // TODO maybe don't want this in favor of SetHandPos? + void SetHandCurrentOffset(const Vector& newHandOffset) { + m_HandCurrentOffset = newHandOffset; + m_HandCurrentOffset.CapMagnitude(m_MaxLength); + } /// /// Gets the current position of this Arm's hand in absolute Scene coordinates. @@ -122,7 +132,7 @@ namespace RTE { /// Sets the current position of this Arm's hand to an absolute scene coordinate. If needed, the set position is modified so its distance from the joint position of the Arm is capped to the max length of the Arm. /// /// The new current position of this Arm's hand as absolute scene coordinate. - void SetHandPos(const Vector &newHandPos); + void SetHandPos(const Vector& newHandPos); /// /// Gets the the strength with which this Arm will grip its HeldDevice. @@ -155,7 +165,7 @@ namespace RTE { /// /// The description of this HandTarget, for easy identification. /// The position, in absolute scene coordinates, to add the queue of hand targets. - void AddHandTarget(const std::string &description, const Vector &handTargetPositionToAdd) { AddHandTarget(description, handTargetPositionToAdd, 0); } + void AddHandTarget(const std::string& description, const Vector& handTargetPositionToAdd) { AddHandTarget(description, handTargetPositionToAdd, 0); } /// /// Adds a HandTarget position, in absolute scene coordinates, to the queue for the Arm to move its hand towards. Target positions are removed from the queue when they're reached (or as close to reached as is possible). @@ -164,12 +174,17 @@ namespace RTE { /// The description of this HandTarget, for easy identification. /// The position, in absolute scene coordinates, to add the queue of hand targets. /// The amount of time, in MS, that the hand should wait when it reaches the newly added HandTarget. - void AddHandTarget(const std::string &description, const Vector &handTargetPositionToAdd, float delayAtTarget); + void AddHandTarget(const std::string& description, const Vector& handTargetPositionToAdd, float delayAtTarget); /// /// Removes this Arm's next HandTarget, if there is one. /// - void RemoveNextHandTarget() { if (!m_HandTargets.empty()) { m_HandTargets.pop(); m_HandHasReachedCurrentTarget = false; } } + void RemoveNextHandTarget() { + if (!m_HandTargets.empty()) { + m_HandTargets.pop(); + m_HandHasReachedCurrentTarget = false; + } + } /// /// Gets whether or not this Arm has any HandTargets. @@ -212,32 +227,32 @@ namespace RTE { /// Gets the HeldDevice currently held by this Arm. /// /// The HeldDevice currently held by this Arm. Ownership is NOT transferred. - HeldDevice * GetHeldDevice() const { return m_HeldDevice; } + HeldDevice* GetHeldDevice() const { return m_HeldDevice; } /// /// Sets the HeldDevice held by this Arm. /// /// The new HeldDevice to be held by this Arm. Ownership IS transferred. - void SetHeldDevice(HeldDevice *newHeldDevice); + void SetHeldDevice(HeldDevice* newHeldDevice); /// /// Gets the HeldDevice this Arm is trying to support. /// /// The HeldDevice this Arm is trying to support. Ownership is NOT transferred. - HeldDevice * GetHeldDeviceThisArmIsTryingToSupport() const { return m_HeldDeviceThisArmIsTryingToSupport; } + HeldDevice* GetHeldDeviceThisArmIsTryingToSupport() const { return m_HeldDeviceThisArmIsTryingToSupport; } /// /// Sets the HeldDevice being this Arm is trying to support. /// /// The new HeldDevice this Arm should try to support. Ownership is NOT transferred. - void SetHeldDeviceThisArmIsTryingToSupport(HeldDevice *newHeldDeviceThisArmShouldTryToSupport) { m_HeldDeviceThisArmIsTryingToSupport = newHeldDeviceThisArmShouldTryToSupport; } + void SetHeldDeviceThisArmIsTryingToSupport(HeldDevice* newHeldDeviceThisArmShouldTryToSupport) { m_HeldDeviceThisArmIsTryingToSupport = newHeldDeviceThisArmShouldTryToSupport; } /// /// Replaces the HeldDevice currently held by this Arm with a new one, and returns the old one. Ownership IS transferred both ways. /// /// The new HeldDevice to be held by this Arm. Ownership IS transferred. /// The HeldDevice that was held by this Arm. Ownership IS transferred. - HeldDevice * SwapHeldDevice(HeldDevice *newHeldDevice); + HeldDevice* SwapHeldDevice(HeldDevice* newHeldDevice); #pragma endregion #pragma region Concrete Methods @@ -247,14 +262,19 @@ namespace RTE { /// A pointer to a BITMAP to draw on. /// The absolute position of the target bitmap's upper left corner in the Scene. /// Which mode to draw in. See the DrawMode enumeration for available modes. - void DrawHand(BITMAP *targetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor) const; + void DrawHand(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor) const; #pragma endregion #pragma region Override Methods /// /// Does stuff that needs to be done after Update(). /// - void PostTravel() override { if (IsAttached()) { m_AngularVel = 0; } MOSRotating::PostTravel(); } + void PostTravel() override { + if (IsAttached()) { + m_AngularVel = 0; + } + MOSRotating::PostTravel(); + } /// /// Updates this Arm. Supposed to be done every frame. @@ -268,11 +288,10 @@ namespace RTE { /// The absolute position of the target bitmap's upper left corner in the Scene. /// Which mode to draw in. See the DrawMode enumeration for the modes. /// Whether to not draw any extra 'ghost' items of this Arm. In this case, that means the hand sprite. - void Draw(BITMAP *targetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion private: - /// /// Struct for storing data about each target in the Arm's queue of HandTargets. /// @@ -280,7 +299,8 @@ namespace RTE { /// /// Constructor method used to instantiate a HandTarget object in system memory. /// - HandTarget(const std::string_view &description, const Vector &targetOffset, float delayAtTarget, bool hFlippedWhenTargetWasCreated) : Description(description), TargetOffset(targetOffset), DelayAtTarget(delayAtTarget), HFlippedWhenTargetWasCreated(hFlippedWhenTargetWasCreated) {} + HandTarget(const std::string_view& description, const Vector& targetOffset, float delayAtTarget, bool hFlippedWhenTargetWasCreated) : + Description(description), TargetOffset(targetOffset), DelayAtTarget(delayAtTarget), HFlippedWhenTargetWasCreated(hFlippedWhenTargetWasCreated) {} std::string Description = ""; Vector TargetOffset; @@ -303,20 +323,20 @@ namespace RTE { bool m_HandHasReachedCurrentTarget; //!< A flag for whether or not the hand has reached its current target. The target is either the front of the HandTarget queue, or the appropriate target to move to if the queue is empty. ContentFile m_HandSpriteFile; //!< The ContentFile containing this Arm's hand bitmap. - BITMAP *m_HandSpriteBitmap; //!< An unowned pointer to the Bitmap held by the hand sprite ContentFile. + BITMAP* m_HandSpriteBitmap; //!< An unowned pointer to the Bitmap held by the hand sprite ContentFile. float m_GripStrength; //!< The strength with which this Arm will grip its HeldDevice. Effectively supersedes the HeldDevice's JointStrength. float m_ThrowStrength; //!< The strength with which this Arm will throw a ThrownDevice. Effectively supersedes the ThrownDevice's ThrowVelocity values. - HeldDevice *m_HeldDevice; //!< A pointer to the HeldDevice this Arm is currently holding. Owned in the MOSRotating Attachables list, kept here for convenience. - HeldDevice *m_HeldDeviceThisArmIsTryingToSupport; //!< A pointer to the HeldDevice being supported by this Arm (i.e. this is the background Arm for another HeldDevice). + HeldDevice* m_HeldDevice; //!< A pointer to the HeldDevice this Arm is currently holding. Owned in the MOSRotating Attachables list, kept here for convenience. + HeldDevice* m_HeldDeviceThisArmIsTryingToSupport; //!< A pointer to the HeldDevice being supported by this Arm (i.e. this is the background Arm for another HeldDevice). /// /// Gets whether or not the hand is close to the given offset. /// /// The offset to check for closeness to the hand. /// Whether or not the hand is close to the given offset. - bool HandIsCloseToTargetOffset(const Vector &targetOffset) const; + bool HandIsCloseToTargetOffset(const Vector& targetOffset) const; #pragma region Update Breakdown /// @@ -332,14 +352,14 @@ namespace RTE { /// /// The held MO as a HeldDevice, for convenience. /// The target offset to have recoil applied to it. - void AccountForHeldDeviceRecoil(const HeldDevice *heldDevice, Vector &targetOffset); + void AccountForHeldDeviceRecoil(const HeldDevice* heldDevice, Vector& targetOffset); /// /// To be used in UpdateHandCurrentOffset. Ensures the HeldDevice won't clip through terrain by modifying the passed in target offset. /// /// The held MO as a HeldDevice, for convenience. /// The target offset to be modified to avoid any terrain clipping. - void AccountForHeldDeviceTerrainClipping(const HeldDevice *heldDevice, Vector &targetOffset) const; + void AccountForHeldDeviceTerrainClipping(const HeldDevice* heldDevice, Vector& targetOffset) const; /// /// Updates the frame for this Arm. Should only be called from Update. @@ -353,8 +373,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - Arm(const Arm &reference) = delete; - Arm & operator=(const Arm &rhs) = delete; + Arm(const Arm& reference) = delete; + Arm& operator=(const Arm& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/AtomGroup.cpp b/Source/Entities/AtomGroup.cpp index 69990ae177..0433b3e839 100644 --- a/Source/Entities/AtomGroup.cpp +++ b/Source/Entities/AtomGroup.cpp @@ -13,12 +13,11 @@ namespace RTE { ConcreteClassInfo(AtomGroup, Entity, 500); const std::unordered_map AtomGroup::c_AreaDistributionTypeMap = { - {"Linear", AtomGroup::AreaDistributionType::Linear}, - {"Circle", AtomGroup::AreaDistributionType::Circle}, - {"Square", AtomGroup::AreaDistributionType::Square} - }; + {"Linear", AtomGroup::AreaDistributionType::Linear}, + {"Circle", AtomGroup::AreaDistributionType::Circle}, + {"Square", AtomGroup::AreaDistributionType::Square}}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AtomGroup::Clear() { m_Atoms.clear(); @@ -36,7 +35,7 @@ namespace RTE { m_IgnoreMOIDs.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int AtomGroup::Create() { if (Entity::Create() < 0) { @@ -48,7 +47,7 @@ namespace RTE { m_Resolution = 0; } if (m_Atoms.empty()) { - Atom *atom = new Atom(Vector(), m_Material, m_OwnerMOSR); + Atom* atom = new Atom(Vector(), m_Material, m_OwnerMOSR); m_Atoms.push_back(atom); } else if (m_Material->GetIndex() != m_Atoms.front()->GetMaterial()->GetIndex()) { m_Material = m_Atoms.front()->GetMaterial(); @@ -56,9 +55,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AtomGroup::Create(const AtomGroup &reference, bool onlyCopyOwnerAtoms) { + int AtomGroup::Create(const AtomGroup& reference, bool onlyCopyOwnerAtoms) { Entity::Create(reference); m_OwnerMOSR = nullptr; // Needs to be set manually by the new MO owner. @@ -73,16 +72,16 @@ namespace RTE { m_Atoms.clear(); m_SubGroups.clear(); - for (const Atom *atom : reference.m_Atoms) { + for (const Atom* atom: reference.m_Atoms) { if (!onlyCopyOwnerAtoms || atom->GetSubID() == 0) { - Atom *atomCopy = new Atom(*atom); + Atom* atomCopy = new Atom(*atom); atomCopy->SetIgnoreMOIDsByGroup(&m_IgnoreMOIDs); m_Atoms.push_back(atomCopy); long subgroupID = atomCopy->GetSubID(); if (subgroupID != 0) { - if (m_SubGroups.find(subgroupID) == m_SubGroups.end()) { - m_SubGroups.insert({ subgroupID, std::vector() }); + if (m_SubGroups.find(subgroupID) == m_SubGroups.end()) { + m_SubGroups.insert({subgroupID, std::vector()}); } m_SubGroups.find(subgroupID)->second.push_back(atomCopy); @@ -92,20 +91,20 @@ namespace RTE { m_IgnoreMOIDs.clear(); - for (const MOID moidToIgnore : reference.m_IgnoreMOIDs) { + for (const MOID moidToIgnore: reference.m_IgnoreMOIDs) { m_IgnoreMOIDs.push_back(moidToIgnore); } - if (!reference.m_Atoms.empty()) { - m_Material = reference.m_Atoms.front()->GetMaterial(); + if (!reference.m_Atoms.empty()) { + m_Material = reference.m_Atoms.front()->GetMaterial(); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AtomGroup::Create(MOSRotating *ownerMOSRotating, Material const *material, int resolution, int depth) { + int AtomGroup::Create(MOSRotating* ownerMOSRotating, Material const* material, int resolution, int depth) { RTEAssert(ownerMOSRotating, "Trying to generate an AtomGroup for a MOSRotating without a sprite!"); m_OwnerMOSR = ownerMOSRotating; @@ -119,11 +118,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AtomGroup::ReadProperty(const std::string_view &propName, Reader &reader) { + int AtomGroup::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("Material", { Material mat; mat.Reset(); @@ -140,7 +139,7 @@ namespace RTE { MatchProperty("Resolution", { reader >> m_Resolution; }); MatchProperty("Depth", { reader >> m_Depth; }); MatchProperty("AddAtom", { - Atom *atom = new Atom; + Atom* atom = new Atom; reader >> *atom; m_Atoms.push_back(atom); }); @@ -153,20 +152,19 @@ namespace RTE { } else { try { m_AreaDistributionType = static_cast(std::stoi(areaDistributionTypeString)); - } catch (const std::invalid_argument &) { + } catch (const std::invalid_argument&) { reader.ReportError("AreaDistributionType " + areaDistributionTypeString + " is invalid."); } } }); MatchProperty("AreaDistributionSurfaceAreaMultiplier", { reader >> m_AreaDistributionSurfaceAreaMultiplier; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AtomGroup::Save(Writer &writer) const { + int AtomGroup::Save(Writer& writer) const { Entity::Save(writer); writer.NewProperty("Material"); @@ -180,7 +178,7 @@ namespace RTE { // Only write out Atoms if they were manually specified if (!m_AutoGenerate) { - for (const Atom *atom : m_Atoms) { + for (const Atom* atom: m_Atoms) { writer.NewProperty("AddAtom"); writer << *atom; } @@ -197,54 +195,58 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AtomGroup::Destroy(bool notInherited) { - for (const Atom *atom : m_Atoms) { + for (const Atom* atom: m_Atoms) { delete atom; } - if (!notInherited) { Entity::Destroy(); } + if (!notInherited) { + Entity::Destroy(); + } Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::SetAtomList(const std::vector &newAtoms) { - for (const Atom *atom : m_Atoms) { + void AtomGroup::SetAtomList(const std::vector& newAtoms) { + for (const Atom* atom: m_Atoms) { delete atom; } m_Atoms = newAtoms; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float AtomGroup::CalculateMaxRadius() const { float sqrMagnitude = 0.0F; float sqrLongest = 0.0F; - for (const Atom *atom : m_Atoms) { + for (const Atom* atom: m_Atoms) { sqrMagnitude = atom->GetOffset().GetSqrMagnitude(); - if (sqrMagnitude > sqrLongest) { sqrLongest = sqrMagnitude; } + if (sqrMagnitude > sqrLongest) { + sqrLongest = sqrMagnitude; + } } return std::sqrt(sqrLongest); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::SetOwner(MOSRotating *newOwner) { + void AtomGroup::SetOwner(MOSRotating* newOwner) { m_OwnerMOSR = newOwner; - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { atom->SetOwner(m_OwnerMOSR); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector AtomGroup::GetAdjustedAtomOffset(const Atom *atom) const { + Vector AtomGroup::GetAdjustedAtomOffset(const Atom* atom) const { return atom->GetOffset().GetXFlipped(m_OwnerMOSR->m_HFlipped) * m_OwnerMOSR->GetRotMatrix(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float AtomGroup::GetMomentOfInertia() { float currentOwnerMass = (m_OwnerMOSR->GetMass() != 0 ? m_OwnerMOSR->GetMass() : 0.0001F); @@ -254,24 +256,28 @@ namespace RTE { m_StoredOwnerMass = currentOwnerMass; float distMass = m_StoredOwnerMass / static_cast(m_Atoms.size()); float radius = 0.0F; - for (const Atom *atom : m_Atoms) { + for (const Atom* atom: m_Atoms) { radius = atom->GetOffset().GetMagnitude() * c_MPP; m_MomentOfInertia += distMass * radius * radius; } } // Avoid zero (if radius is nonexistent, for example), will cause divide by zero problems otherwise. - if (m_MomentOfInertia == 0.0F) { m_MomentOfInertia = 1.0F; } + if (m_MomentOfInertia == 0.0F) { + m_MomentOfInertia = 1.0F; + } return m_MomentOfInertia; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::AddAtoms(const std::vector &atomList, long subgroupID, const Vector &offset, const Matrix &offsetRotation) { - if (m_SubGroups.count(subgroupID) == 0) { m_SubGroups.insert({ subgroupID, std::vector() }); } + void AtomGroup::AddAtoms(const std::vector& atomList, long subgroupID, const Vector& offset, const Matrix& offsetRotation) { + if (m_SubGroups.count(subgroupID) == 0) { + m_SubGroups.insert({subgroupID, std::vector()}); + } - Atom *atomToAdd; - for (const Atom * atom : atomList) { + Atom* atomToAdd; + for (const Atom* atom: atomList) { atomToAdd = new Atom(*atom); atomToAdd->SetSubID(subgroupID); atomToAdd->SetOffset(offset + (atomToAdd->GetOriginalOffset() * offsetRotation)); @@ -282,59 +288,61 @@ namespace RTE { } if (!atomList.empty()) { m_MomentOfInertia = 0.0F; - if (m_OwnerMOSR) { GetMomentOfInertia(); } + if (m_OwnerMOSR) { + GetMomentOfInertia(); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool AtomGroup::RemoveAtoms(long removeID) { std::size_t oldSize = m_Atoms.size(); m_Atoms.erase( - std::remove_if(m_Atoms.begin(), m_Atoms.end(), - [removeID](Atom *atom) { - return atom->GetSubID() == removeID; - }), - m_Atoms.end()); + std::remove_if(m_Atoms.begin(), m_Atoms.end(), + [removeID](Atom* atom) { + return atom->GetSubID() == removeID; + }), + m_Atoms.end()); m_SubGroups.erase(removeID); bool removedAny = oldSize != m_Atoms.size(); if (removedAny) { m_MomentOfInertia = 0.0F; - if (m_OwnerMOSR) { - GetMomentOfInertia(); + if (m_OwnerMOSR) { + GetMomentOfInertia(); } } return removedAny; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AtomGroup::UpdateSubAtoms(long subgroupID, const Vector &newOffset, const Matrix &newOffsetRotation) { + bool AtomGroup::UpdateSubAtoms(long subgroupID, const Vector& newOffset, const Matrix& newOffsetRotation) { if (m_SubGroups.empty() || m_SubGroups.count(subgroupID) == 0) { return false; } RTEAssert(!m_SubGroups.at(subgroupID).empty(), "Found an empty subgroup list in AtomGroup!?"); - for (Atom *subGroupAtom : m_SubGroups.at(subgroupID)) { + for (Atom* subGroupAtom: m_SubGroups.at(subgroupID)) { subGroupAtom->SetOffset(newOffset + (subGroupAtom->GetOriginalOffset() * newOffsetRotation)); } return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float AtomGroup::Travel(float travelTime, bool callOnBounce, bool callOnSink, bool scenePreLocked) { return Travel(m_OwnerMOSR->m_Pos, m_OwnerMOSR->m_Vel, m_OwnerMOSR->m_Rotation, m_OwnerMOSR->m_AngularVel, m_OwnerMOSR->m_DidWrap, m_OwnerMOSR->m_TravelImpulse, m_OwnerMOSR->GetMass(), travelTime, callOnBounce, callOnSink, scenePreLocked); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: Break down and rework this trainwreck. - float AtomGroup::Travel(Vector &position, Vector &velocity, Matrix &rotation, float &angularVel, bool &didWrap, Vector &totalImpulse, float mass, float travelTime, bool callOnBounce, bool callOnSink, bool scenePreLocked) { + float AtomGroup::Travel(Vector& position, Vector& velocity, Matrix& rotation, float& angularVel, bool& didWrap, Vector& totalImpulse, float mass, float travelTime, bool callOnBounce, bool callOnSink, bool scenePreLocked) { ZoneScoped; RTEAssert(m_OwnerMOSR, "Tried to travel an AtomGroup that has no parent!"); @@ -360,30 +368,30 @@ namespace RTE { bool halted = false; // TODO: Look into pre-hit stuff and how it can be used to improve collision. - //Vector preHitPos; - //float preHitRot; + // Vector preHitPos; + // float preHitRot; Vector linSegTraj; // The planned travel trajectory of this AtomGroup's origin in pixels. HitData hitData; // Thread locals for performance (avoid memory allocs) - thread_local std::unordered_map> hitMOAtoms; + thread_local std::unordered_map> hitMOAtoms; hitMOAtoms.clear(); - thread_local std::vector hitTerrAtoms; + thread_local std::vector hitTerrAtoms; hitTerrAtoms.clear(); - thread_local std::vector penetratingAtoms; + thread_local std::vector penetratingAtoms; penetratingAtoms.clear(); - thread_local std::vector hitResponseAtoms; + thread_local std::vector hitResponseAtoms; hitResponseAtoms.clear(); // Lock all bitmaps involved outside the loop - only relevant for video bitmaps so disabled at the moment. - //if (!scenePreLocked) { g_SceneMan.LockScene(); } + // if (!scenePreLocked) { g_SceneMan.LockScene(); } // Loop for all the different straight segments (between bounces etc) that have to be traveled during the travelTime. do { // First see what Atoms are inside either the terrain or another MO, and cause collisions responses before even starting the segment - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { const Vector startOff = m_OwnerMOSR->RotateOffset(atom->GetOffset()); if (atom->SetupPos(position + startOff)) { @@ -431,7 +439,7 @@ namespace RTE { #ifdef DEBUG_BUILD // TODO: Remove this once AtomGroup drawing in Material layer draw mode is implemented. // Draw the positions of the Atoms at the start of each segment, for visual debugging. - //putpixel(g_SceneMan.GetMOColorBitmap(), atom->GetCurrentPos().GetFloorIntX(), atom->GetCurrentPos().GetFloorIntY(), 122); + // putpixel(g_SceneMan.GetMOColorBitmap(), atom->GetCurrentPos().GetFloorIntX(), atom->GetCurrentPos().GetFloorIntY(), 122); #endif } @@ -454,16 +462,18 @@ namespace RTE { break; } - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { // Calculate the segment trajectory for each individual Atom, with rotations considered. const Vector startOff = m_OwnerMOSR->RotateOffset(atom->GetOffset()); const Vector trajFromAngularTravel = Vector(startOff).RadRotate(rotDelta) - startOff; // Set up the initial rasterized step for each Atom and save the longest trajectory. - if (atom->SetupSeg(position + startOff, linSegTraj + trajFromAngularTravel) > stepsOnSeg) { stepsOnSeg = atom->GetStepsLeft(); } + if (atom->SetupSeg(position + startOff, linSegTraj + trajFromAngularTravel) > stepsOnSeg) { + stepsOnSeg = atom->GetStepsLeft(); + } } - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { atom->SetStepRatio(static_cast(atom->GetStepsLeft()) / static_cast(stepsOnSeg)); } @@ -483,7 +493,7 @@ namespace RTE { int atomsHitMOsCount = 0; - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { if (atom->StepForward()) { // If something was hit, first check for terrain hit. if (atom->HitWhatTerrMaterial()) { @@ -494,13 +504,15 @@ namespace RTE { const MOID tempMOID = atom->HitWhatMOID(); if (tempMOID != g_NoMOID) { m_OwnerMOSR->m_MOIDHit = tempMOID; - MovableObject *moCollidedWith = g_MovableMan.GetMOFromID(tempMOID); - if (moCollidedWith && moCollidedWith->HitWhatMOID() == g_NoMOID) { moCollidedWith->SetHitWhatMOID(m_OwnerMOSR->m_MOID); } + MovableObject* moCollidedWith = g_MovableMan.GetMOFromID(tempMOID); + if (moCollidedWith && moCollidedWith->HitWhatMOID() == g_NoMOID) { + moCollidedWith->SetHitWhatMOID(m_OwnerMOSR->m_MOID); + } hitMOAtoms[tempMOID].push_back(atom); // Add the hit MO to the ignore list of ignored MOIDs - //AddMOIDToIgnore(tempMOID); + // AddMOIDToIgnore(tempMOID); // Count the number of Atoms of this group that hit MOs this step. Used to properly distribute the mass of the owner MO in later collision responses during this step. atomsHitMOsCount++; @@ -512,7 +524,7 @@ namespace RTE { Vector tNorm = m_OwnerMOSR->RotateOffset(atom->GetNormal()) * 7; line(g_SceneMan.GetMOColorBitmap(), tPos.GetFloorIntX(), tPos.GetFloorIntY(), tPos.GetFloorIntX() + tNorm.GetFloorIntX(), tPos.GetFloorIntY() + tNorm.GetFloorIntY(), 244); // Draw the positions of the hit points on screen for easy debugging. - //putpixel(g_SceneMan.GetMOColorBitmap(), tPos.GetFloorIntX(), tPos.GetFloorIntY(), 5); + // putpixel(g_SceneMan.GetMOColorBitmap(), tPos.GetFloorIntX(), tPos.GetFloorIntY(), 5); #endif } } @@ -555,7 +567,7 @@ namespace RTE { const float momentInertiaDistribution = m_MomentOfInertia / static_cast(hitTerrAtoms.size() * (m_Resolution ? m_Resolution : 1)); // Determine which of the colliding Atoms will penetrate the terrain. - for (std::vector::iterator atomItr = hitTerrAtoms.begin(); atomItr != hitTerrAtoms.end(); ) { + for (std::vector::iterator atomItr = hitTerrAtoms.begin(); atomItr != hitTerrAtoms.end();) { // Calculate and store the accurate hit radius of the Atom in relation to the CoM hitData.HitRadius[HITOR] = m_OwnerMOSR->RotateOffset((*atomItr)->GetOffset()) * c_MPP; // Figure out the pre-collision velocity of the hitting Atom due to body translation and rotation. @@ -584,30 +596,32 @@ namespace RTE { // If some Atoms could not penetrate even though all the impulse was on them, gather the bounce results and apply them to the owner. if (!hitTerrAtoms.empty()) { // Step back all Atoms that previously took one during this step iteration. This is so we aren't intersecting the hit MO anymore. - for (Atom *hitTerrAtom : hitTerrAtoms) { + for (Atom* hitTerrAtom: hitTerrAtoms) { hitTerrAtom->StepBack(); } // Calculate the distributed mass that each bouncing Atom has. - //massDistribution = mass /*/ GetSurfaceArea(hitTerrAtoms.size() * (m_Resolution ? m_Resolution : 1))*/; - //momentInertiaDistribution = m_MomentOfInertia/* / (hitTerrAtoms.size() * (m_Resolution ? m_Resolution : 1))*/; + // massDistribution = mass /*/ GetSurfaceArea(hitTerrAtoms.size() * (m_Resolution ? m_Resolution : 1))*/; + // momentInertiaDistribution = m_MomentOfInertia/* / (hitTerrAtoms.size() * (m_Resolution ? m_Resolution : 1))*/; const float hitFactor = 1.0F / static_cast(hitTerrAtoms.size()); // Gather the collision response effects so that the impulse force can be calculated. - for (Atom *hitTerrAtom : hitTerrAtoms) { + for (Atom* hitTerrAtom: hitTerrAtoms) { hitTerrAtom->GetHitData().TotalMass[HITOR] = mass; hitTerrAtom->GetHitData().MomInertia[HITOR] = m_MomentOfInertia; hitTerrAtom->GetHitData().ImpulseFactor[HITOR] = hitFactor; // Get the HitData so far gathered for this Atom. - //hitData = hitTerrAtom->GetHitData(); + // hitData = hitTerrAtom->GetHitData(); // Call the call-on-bounce function, if requested. - if (m_OwnerMOSR && callOnBounce) { halted = halted || m_OwnerMOSR->OnBounce(hitTerrAtom->GetHitData()); } + if (m_OwnerMOSR && callOnBounce) { + halted = halted || m_OwnerMOSR->OnBounce(hitTerrAtom->GetHitData()); + } // Copy back the new HitData with all the info we have so far. - //hitTerrAtom->SetHitData(hitData); + // hitTerrAtom->SetHitData(hitData); // Compute and store this Atom's collision response impulse force. hitTerrAtom->TerrHitResponse(); @@ -622,7 +636,7 @@ namespace RTE { const float hitFactor = 1.0F / static_cast(penetratingAtoms.size()); // Calculate and store the collision response effects. - for (Atom *penetratingAtom : penetratingAtoms) { + for (Atom* penetratingAtom: penetratingAtoms) { /* // This gets re-set later according to the ortho pixel edges hit. hitData.BitmapNormal = -(hitData.HitVel[HITOR].GetNormalized()); @@ -635,7 +649,7 @@ namespace RTE { // Get the HitData so far gathered for this Atom. hitData = penetratingAtom->GetHitData(); - if (g_SceneMan.TryPenetrate(penetratingAtom->GetCurrentPos().GetFloorIntX(), penetratingAtom->GetCurrentPos().GetFloorIntY(), hitData.PreImpulse[HITOR], hitData.HitVel[HITOR], retardation, 1.0F, 1/*(*penetratingAtom)->GetNumPenetrations()*/)) { + if (g_SceneMan.TryPenetrate(penetratingAtom->GetCurrentPos().GetFloorIntX(), penetratingAtom->GetCurrentPos().GetFloorIntY(), hitData.PreImpulse[HITOR], hitData.HitVel[HITOR], retardation, 1.0F, 1 /*(*penetratingAtom)->GetNumPenetrations()*/)) { // Recalculate these here without the distributed mass and MI. const float sqrRadMag = hitData.HitRadius[HITOR].GetSqrMagnitude(); hitData.HitDenominator = (1.0F / mass) + (sqrRadMag / m_MomentOfInertia); @@ -647,7 +661,9 @@ namespace RTE { hitData.ResImpulse[HITOR] = ((hitData.HitVel[HITOR] * retardation) / hitData.HitDenominator) * hitFactor; // Call the call-on-sink function, if requested. - if (m_OwnerMOSR && callOnSink) { halted = halted || m_OwnerMOSR->OnSink(hitData); } + if (m_OwnerMOSR && callOnSink) { + halted = halted || m_OwnerMOSR->OnSink(hitData); + } // Copy back the new HitData with all the info we have so far. penetratingAtom->SetHitData(hitData); @@ -665,14 +681,14 @@ namespace RTE { hitData.MomInertia[HITOR] = m_MomentOfInertia; hitData.ImpulseFactor[HITOR] = 1.0F / static_cast(atomsHitMOsCount); - for (auto &MOAtomMapEntry : hitMOAtoms) { + for (auto& MOAtomMapEntry: hitMOAtoms) { // The denominator that the MovableObject being hit should divide its mass with for each Atom of this AtomGroup that is colliding with it during this step. hitData.ImpulseFactor[HITEE] = 1.0F / static_cast(MOAtomMapEntry.second.size()); - for (Atom *hitMOAtom : MOAtomMapEntry.second) { + for (Atom* hitMOAtom: MOAtomMapEntry.second) { // Step back all Atoms that hit MOs during this step iteration. This is so we aren't intersecting the hit MO anymore. hitMOAtom->StepBack(); - //hitData.HitPoint = hitMOAtom->GetCurrentPos(); + // hitData.HitPoint = hitMOAtom->GetCurrentPos(); // Calculate and store the accurate hit radius of the Atom in relation to the CoM hitData.HitRadius[HITOR] = m_OwnerMOSR->RotateOffset(hitMOAtom->GetOffset()) * c_MPP; @@ -683,7 +699,7 @@ namespace RTE { // Let the Atom calculate the impulse force resulting from the collision, and only add it if collision is valid if (hitMOAtom->MOHitResponse()) { // Report the hit to both MO's in collision - HitData &hd = hitMOAtom->GetHitData(); + HitData& hd = hitMOAtom->GetHitData(); // Don't count collision if either says they got terminated if (!hd.RootBody[HITOR]->OnMOHit(hd) && !hd.RootBody[HITEE]->OnMOHit(hd)) { // Save the filled out Atom in the list for later application in this step. @@ -701,7 +717,7 @@ namespace RTE { // If we hit anything, during this almost completed segment, and are about to start a new one, apply the calculated response effects to the owning MO. if (hitStep) { // Apply all the collision response impulse forces to the linear and angular velocities of the owner MO. - for (Atom *hitResponseAtom : hitResponseAtoms) { + for (Atom* hitResponseAtom: hitResponseAtoms) { // TODO: Investigate damping! hitData = hitResponseAtom->GetHitData(); velocity += hitData.ResImpulse[HITOR] / mass; @@ -711,7 +727,7 @@ namespace RTE { } // Make sub-pixel progress if there was a hit on the very first step. - //if (segProgress == 0) { segProgress = 0.1 / (float)stepsOnSeg; } + // if (segProgress == 0) { segProgress = 0.1 / (float)stepsOnSeg; } // Now calculate the total time left to travel, according to the progress made. timeLeft -= timeLeft * (segProgress * segRatio); @@ -735,7 +751,7 @@ namespace RTE { ResolveMOSIntersection(position); - //if (!scenePreLocked) { g_SceneMan.UnlockScene(); } + // if (!scenePreLocked) { g_SceneMan.UnlockScene(); } ClearMOIDIgnoreList(); @@ -743,7 +759,7 @@ namespace RTE { int ignoreCount = 0; int maxIgnore = m_Atoms.size() / 2; - for (const Atom *atom : m_Atoms) { + for (const Atom* atom: m_Atoms) { if (atom->IsIgnoringTerrain()) { ++ignoreCount; @@ -769,10 +785,10 @@ namespace RTE { return timeLeft; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: Break down and rework this dumpsterfire. - Vector AtomGroup::PushTravel(Vector &position, const Vector &velocity, float pushForce, bool &didWrap, float travelTime, bool callOnBounce, bool callOnSink, bool scenePreLocked) { + Vector AtomGroup::PushTravel(Vector& position, const Vector& velocity, float pushForce, bool& didWrap, float travelTime, bool callOnBounce, bool callOnSink, bool scenePreLocked) { ZoneScoped; RTEAssert(m_OwnerMOSR, "Tried to push-travel an AtomGroup that has no parent!"); @@ -795,11 +811,11 @@ namespace RTE { bool halted = false; // TODO: Fix HitMOs issue!! - bool hitMOs = false /*m_OwnerMOSR->m_HitsMOs*/; + bool hitMOs = false /*m_OwnerMOSR->m_HitsMOs*/; - const Material *hitMaterial = nullptr; - const Material *domMaterial = nullptr; - const Material *subMaterial = nullptr; + const Material* hitMaterial = nullptr; + const Material* domMaterial = nullptr; + const Material* subMaterial = nullptr; Vector legProgress; Vector forceVel; @@ -810,17 +826,17 @@ namespace RTE { HitData hitData; // Thread locals for performance reasons (avoid memory allocs) - thread_local std::unordered_map> MOIgnoreMap; + thread_local std::unordered_map> MOIgnoreMap; MOIgnoreMap.clear(); - thread_local std::unordered_map>> hitMOAtoms; + thread_local std::unordered_map>> hitMOAtoms; hitMOAtoms.clear(); - thread_local std::deque> hitTerrAtoms; + thread_local std::deque> hitTerrAtoms; hitTerrAtoms.clear(); - thread_local std::deque> penetratingAtoms; + thread_local std::deque> penetratingAtoms; penetratingAtoms.clear(); // Lock all bitmaps involved outside the loop - only relevant for video bitmaps so disabled at the moment. - //if (!scenePreLocked) { g_SceneMan.LockScene(); } + // if (!scenePreLocked) { g_SceneMan.LockScene(); } // Before the very first step of the first leg of this travel, we find that we're already intersecting with another MO, then we completely ignore collisions with that MO for this entire travel. // This is to prevent MO's from getting stuck in each other. @@ -828,7 +844,7 @@ namespace RTE { intPos[X] = position.GetFloorIntX(); intPos[Y] = position.GetFloorIntY(); - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { const Vector flippedOffset = atom->GetOffset().GetXFlipped(m_OwnerMOSR->m_HFlipped); // See if the Atom is starting out on top of another MO MOID tempMOID = g_SceneMan.GetMOIDPixel(intPos[X] + flippedOffset.GetFloorIntX(), intPos[Y] + flippedOffset.GetFloorIntY(), m_OwnerMOSR->GetTeam()); @@ -896,12 +912,18 @@ namespace RTE { int error = delta2[sub] - delta[dom]; - if (delta[X] > 1000) { delta[X] = 1000; } - if (delta[Y] > 1000) { delta[Y] = 1000; } + if (delta[X] > 1000) { + delta[X] = 1000; + } + if (delta[Y] > 1000) { + delta[Y] = 1000; + } // Bresenham's line drawing algorithm execution for (int domSteps = 0; domSteps < delta[dom] && !(hit[X] || hit[Y]); ++domSteps) { - if (subStepped) { ++subSteps; } + if (subStepped) { + ++subSteps; + } subStepped = false; // Take one step forward along the leg. @@ -922,7 +944,7 @@ namespace RTE { hitMOAtoms.clear(); hitTerrAtoms.clear(); - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { const Vector flippedOffset = atom->GetOffset().GetXFlipped(m_OwnerMOSR->m_HFlipped); MOID tempMOID = g_NoMOID; @@ -931,23 +953,25 @@ namespace RTE { if (hitMOs) { tempMOID = g_SceneMan.GetMOIDPixel(intPos[X] + flippedOffset.GetFloorIntX(), intPos[Y] + flippedOffset.GetFloorIntY(), m_OwnerMOSR->GetTeam()); // Check the ignore map for Atoms that should ignore hits against certain MOs. - if (tempMOID != g_NoMOID && (MOIgnoreMap.count(tempMOID) != 0)) { ignoreHit = MOIgnoreMap.at(tempMOID).count(atom) != 0; } + if (tempMOID != g_NoMOID && (MOIgnoreMap.count(tempMOID) != 0)) { + ignoreHit = MOIgnoreMap.at(tempMOID).count(atom) != 0; + } } if (hitMOs && tempMOID && !ignoreHit) { - hitMOAtoms[tempMOID].push_back({ atom, flippedOffset }); + hitMOAtoms[tempMOID].push_back({atom, flippedOffset}); // Count the number of Atoms of this group that hit MOs this step. Used to properly distribute the mass of the owner MO in later collision responses during this step. atomsHitMOsCount++; - // If no MO has ever been hit yet during this step, then keep checking for terrain hits. + // If no MO has ever been hit yet during this step, then keep checking for terrain hits. } else if (atomsHitMOsCount == 0 && g_SceneMan.GetTerrMatter(intPos[X] + flippedOffset.GetFloorIntX(), intPos[Y] + flippedOffset.GetFloorIntY())) { - hitTerrAtoms.push_back({ atom, flippedOffset }); + hitTerrAtoms.push_back({atom, flippedOffset}); } #ifdef DEBUG_BUILD // TODO: Remove this once AtomGroup drawing in Material layer draw mode is implemented. // Draw the positions of the hit points on screen for easy debugging. - //putpixel(g_SceneMan.GetMOColorBitmap(), std::floor(position.GetFloorIntX() + flippedOffset.GetFloorIntX()), std::floor(position.GetFloorIntY() + flippedOffset.GetFloorIntY()), 122); + // putpixel(g_SceneMan.GetMOColorBitmap(), std::floor(position.GetFloorIntX() + flippedOffset.GetFloorIntX()), std::floor(position.GetFloorIntY() + flippedOffset.GetFloorIntY()), 122); #endif } @@ -976,7 +1000,9 @@ namespace RTE { if (hitMOs && !hitMOAtoms.empty()) { // Back up one step so that we're not intersecting the other MO(s) anymore intPos[dom] -= increment[dom]; - if (subStepped) { intPos[sub] -= increment[sub]; } + if (subStepped) { + intPos[sub] -= increment[sub]; + } // Undo wrap, if necessary. didWrap = !g_SceneMan.WrapPosition(intPos[X], intPos[Y]) && didWrap; @@ -988,16 +1014,16 @@ namespace RTE { hitData.HitVel[HITOR] = forceVel; // The distributed mass of one hitting Atom of the hitting (this AtomGroup's owner) MovableObject. - //float hitorMass = mass / ((atomsHitMOsCount/* + hitTerrAtoms.size()*/) * (m_Resolution ? m_Resolution : 1)); - //float hiteeMassDenom = 0; + // float hitorMass = mass / ((atomsHitMOsCount/* + hitTerrAtoms.size()*/) * (m_Resolution ? m_Resolution : 1)); + // float hiteeMassDenom = 0; - for (const auto &MOAtomMapEntry : hitMOAtoms) { + for (const auto& MOAtomMapEntry: hitMOAtoms) { // The denominator that the MovableObject being hit should divide its mass with for each Atom of this AtomGroup that is colliding with it during this step. hitData.ImpulseFactor[HITEE] = 1.0F / static_cast(MOAtomMapEntry.second.size()); - for (const std::pair &hitMOAtomEntry : MOAtomMapEntry.second) { + for (const std::pair& hitMOAtomEntry: MOAtomMapEntry.second) { // Bake in current Atom's offset into the int positions. - const Vector &atomOffset = hitMOAtomEntry.second; + const Vector& atomOffset = hitMOAtomEntry.second; intPos[X] += atomOffset.GetFloorIntX(); intPos[Y] += atomOffset.GetFloorIntY(); hitPos[X] += atomOffset.GetFloorIntX(); @@ -1019,13 +1045,13 @@ namespace RTE { if (subStepped && delta[sub] && ((sub == X && g_SceneMan.GetMOIDPixel(hitPos[X], intPos[Y], m_OwnerMOSR->GetTeam()) != g_NoMOID) || (sub == Y && g_SceneMan.GetMOIDPixel(intPos[X], hitPos[Y], m_OwnerMOSR->GetTeam()) != g_NoMOID))) { hit[sub] = true; - //if (hitData.HitPoint.IsZero()) { - // NOTE: THis can actually be wrong since there may not in fact be a corner pixel, but two pixels hit on X and Y directions - hitData.HitPoint = (sub == X) ? Vector(static_cast(hitPos[X]), static_cast(intPos[Y])) : Vector(static_cast(intPos[X]), static_cast(hitPos[Y])); + // if (hitData.HitPoint.IsZero()) { + // NOTE: THis can actually be wrong since there may not in fact be a corner pixel, but two pixels hit on X and Y directions + hitData.HitPoint = (sub == X) ? Vector(static_cast(hitPos[X]), static_cast(intPos[Y])) : Vector(static_cast(intPos[X]), static_cast(hitPos[Y])); /* // We hit pixels in both sub and dom directions on the other MO, a corner hit. } else { - hitData.HitPoint.SetXY(hitPos[X], hitPos[Y]); + hitData.HitPoint.SetXY(hitPos[X], hitPos[Y]); } */ hitData.BitmapNormal[sub] = static_cast(-increment[sub]); @@ -1078,10 +1104,10 @@ namespace RTE { float massDistribution = mass / GetSurfaceArea(hitTerrAtoms.size() * (m_Resolution ? m_Resolution : 1)); - for (std::deque>::iterator atomItr = hitTerrAtoms.begin(); atomItr != hitTerrAtoms.end(); ) { + for (std::deque>::iterator atomItr = hitTerrAtoms.begin(); atomItr != hitTerrAtoms.end();) { if (g_SceneMan.WillPenetrate(intPos[X] + (*atomItr).second.GetFloorIntX(), intPos[Y] + (*atomItr).second.GetFloorIntY(), forceVel, massDistribution)) { // Move the penetrating Atom to the penetrating list from the collision list. - penetratingAtoms.push_back({ (*atomItr).first, (*atomItr).second }); + penetratingAtoms.push_back({(*atomItr).first, (*atomItr).second}); atomItr = hitTerrAtoms.erase(atomItr); somethingPenetrated = true; } else { @@ -1096,20 +1122,22 @@ namespace RTE { if (!hitTerrAtoms.empty()) { // Back up one step so that we're not intersecting the terrain anymore intPos[dom] -= increment[dom]; - if (subStepped) { intPos[sub] -= increment[sub]; } + if (subStepped) { + intPos[sub] -= increment[sub]; + } // Undo wrap, if necessary. didWrap = !g_SceneMan.WrapPosition(intPos[X], intPos[Y]) && didWrap; // Call the call-on-bounce function, if requested. - //if (m_OwnerMOSR && callOnBounce) { halted = m_OwnerMOSR->OnBounce(position); } + // if (m_OwnerMOSR && callOnBounce) { halted = m_OwnerMOSR->OnBounce(position); } - float massDistribution = mass / GetSurfaceArea((hitTerrAtoms.size()/* + atomsHitMOsCount*/) * (m_Resolution ? m_Resolution : 1)); + float massDistribution = mass / GetSurfaceArea((hitTerrAtoms.size() /* + atomsHitMOsCount*/) * (m_Resolution ? m_Resolution : 1)); // Gather the collision response effects so that the impulse force can be calculated. - for (const std::pair &hitTerrAtomsEntry : hitTerrAtoms) { + for (const std::pair& hitTerrAtomsEntry: hitTerrAtoms) { // Bake in current Atom's offset into the int positions. - const Vector &atomOffset = hitTerrAtomsEntry.second; + const Vector& atomOffset = hitTerrAtomsEntry.second; intPos[X] += atomOffset.GetFloorIntX(); intPos[Y] += atomOffset.GetFloorIntY(); hitPos[X] += atomOffset.GetFloorIntX(); @@ -1169,17 +1197,17 @@ namespace RTE { // All Atoms must have penetrated and therefore the entire group has sunken into the terrain. Get the penetration resistance results and apply them to the owner. else if (!penetratingAtoms.empty()) { - //bool sinkHit = true; + // bool sinkHit = true; hit[dom] = true; hit[sub] = true; // Call the call-on-sink function, if requested. - //if (m_OwnerMOSR && callOnSink) { halted = m_OwnerMOSR->OnSink(position); } + // if (m_OwnerMOSR && callOnSink) { halted = m_OwnerMOSR->OnSink(position); } float massDistribution = mass / GetSurfaceArea(penetratingAtoms.size() * (m_Resolution ? m_Resolution : 1)); // Apply the collision response effects. - for (const std::pair &penetratingAtomsEntry : penetratingAtoms) { + for (const std::pair& penetratingAtomsEntry: penetratingAtoms) { if (g_SceneMan.TryPenetrate(intPos[X] + penetratingAtomsEntry.second.GetFloorIntX(), intPos[Y] + penetratingAtomsEntry.second.GetFloorIntY(), forceVel * massDistribution, forceVel, retardation, 1.0F, penetratingAtomsEntry.first->GetNumPenetrations())) { ownerVel += (forceVel * massDistribution * retardation) / mass; returnPush += forceVel * massDistribution * retardation; @@ -1196,7 +1224,9 @@ namespace RTE { didWrap = didWrap || g_SceneMan.WrapPosition(position); // Stunt travel time if there is no more velocity - if (ownerVel.IsZero()) { timeLeft = 0; } + if (ownerVel.IsZero()) { + timeLeft = 0; + } } ++stepCount; } @@ -1212,9 +1242,9 @@ namespace RTE { return returnPush; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AtomGroup::PushAsLimb(const Vector &jointPos, const Vector &velocity, const Matrix &rotation, LimbPath &limbPath, const float travelTime, bool *restarted, bool affectRotation, Vector rotationOffset, Vector positionOffset) { + bool AtomGroup::PushAsLimb(const Vector& jointPos, const Vector& velocity, const Matrix& rotation, LimbPath& limbPath, const float travelTime, bool* restarted, bool affectRotation, Vector rotationOffset, Vector positionOffset) { RTEAssert(m_OwnerMOSR, "Tried to push-as-limb an AtomGroup that has no parent!"); bool didWrap = false; @@ -1251,7 +1281,9 @@ namespace RTE { // TODO: Change this to a regular while loop if possible. do { if (limbPath.PathEnded()) { - if (restarted) { *restarted = true; } + if (restarted) { + *restarted = true; + } if (!limbPath.RestartFree(m_LimbPos, m_OwnerMOSR->GetRootID(), m_OwnerMOSR->IgnoresWhichTeam())) { return false; } @@ -1260,10 +1292,12 @@ namespace RTE { limbPath.ReportProgress(m_LimbPos); } while (!limbPath.FrameDone() && !limbPath.PathEnded()); - if (pushImpulse.GetLargest() > 10000.0F) { pushImpulse.Reset(); } + if (pushImpulse.GetLargest() > 10000.0F) { + pushImpulse.Reset(); + } - if (Actor *owner = dynamic_cast(m_OwnerMOSR)) { - bool againstTravelDirection = owner->GetController()->IsState(MOVE_LEFT) && pushImpulse.m_X > 0.0F || + if (Actor* owner = dynamic_cast(m_OwnerMOSR)) { + bool againstTravelDirection = owner->GetController()->IsState(MOVE_LEFT) && pushImpulse.m_X > 0.0F || owner->GetController()->IsState(MOVE_RIGHT) && pushImpulse.m_X < 0.0F; if (againstTravelDirection) { // Filter some of our impulse out. We're pushing against an obstacle, but we don't want to kick backwards! @@ -1283,9 +1317,9 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::FlailAsLimb(const Vector &ownerPos, const Vector &jointOffset, const float limbRadius, const Vector &velocity, const float angularVel, const float mass, const float travelTime) { + void AtomGroup::FlailAsLimb(const Vector& ownerPos, const Vector& jointOffset, const float limbRadius, const Vector& velocity, const float angularVel, const float mass, const float travelTime) { RTEAssert(m_OwnerMOSR, "Tried to flail an AtomGroup that has no parent!"); bool didWrap = false; @@ -1305,18 +1339,18 @@ namespace RTE { return; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool AtomGroup::InTerrain() const { RTEAssert(m_OwnerMOSR, "Tried to check overlap with terrain for an AtomGroup that has no parent!"); // Only relevant for video bitmaps so disabled at the moment. - //if (!g_SceneMan.SceneIsLocked()) { g_SceneMan.LockScene(); } + // if (!g_SceneMan.SceneIsLocked()) { g_SceneMan.LockScene(); } bool penetrates = false; Vector atomPos; - for (const Atom *atom : m_Atoms) { + for (const Atom* atom: m_Atoms) { atomPos = m_OwnerMOSR->GetPos() + GetAdjustedAtomOffset(atom); if (g_SceneMan.GetTerrMatter(atomPos.GetFloorIntX(), atomPos.GetFloorIntY()) != g_MaterialAir) { penetrates = true; @@ -1324,37 +1358,39 @@ namespace RTE { } } - //if (g_SceneMan.SceneIsLocked()) { g_SceneMan.UnlockScene(); } + // if (g_SceneMan.SceneIsLocked()) { g_SceneMan.UnlockScene(); } return penetrates; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float AtomGroup::RatioInTerrain() const { RTEAssert(m_OwnerMOSR, "Tried to check ratio in terrain for an AtomGroup that has no parent!"); // Only relevant for video bitmaps so disabled at the moment. - //if (!g_SceneMan.SceneIsLocked()) { g_SceneMan.LockScene(); } + // if (!g_SceneMan.SceneIsLocked()) { g_SceneMan.LockScene(); } Vector atomPos; int inTerrain = 0; - for (const Atom *atom : m_Atoms) { + for (const Atom* atom: m_Atoms) { atomPos = m_OwnerMOSR->GetPos() + GetAdjustedAtomOffset(atom); - if (g_SceneMan.GetTerrMatter(atomPos.GetFloorIntX(), atomPos.GetFloorIntY()) != g_MaterialAir) { inTerrain++; } + if (g_SceneMan.GetTerrMatter(atomPos.GetFloorIntX(), atomPos.GetFloorIntY()) != g_MaterialAir) { + inTerrain++; + } } - //if (g_SceneMan.SceneIsLocked()) { g_SceneMan.UnlockScene(); } + // if (g_SceneMan.SceneIsLocked()) { g_SceneMan.UnlockScene(); } return static_cast(inTerrain) / static_cast(m_Atoms.size()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: Look into breaking this into smaller methods. - bool AtomGroup::ResolveTerrainIntersection(Vector &position, unsigned char strongerThan) const { - thread_local std::vector intersectingAtoms; + bool AtomGroup::ResolveTerrainIntersection(Vector& position, unsigned char strongerThan) const { + thread_local std::vector intersectingAtoms; intersectingAtoms.clear(); MOID hitMaterial = g_MaterialAir; @@ -1365,7 +1401,7 @@ namespace RTE { Vector atomPos = Vector(); // First go through all Atoms to find the first intersection and get the intersected MO - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { atomOffset = m_OwnerMOSR->RotateOffset(atom->GetOffset()); atom->SetupPos(position + atomOffset); atomPos = atom->GetCurrentPos(); @@ -1384,7 +1420,7 @@ namespace RTE { Vector exitDirection = Vector(); // Go through all intersecting Atoms and find their average inverse normal - for (const Atom *intersectingAtom : intersectingAtoms) { + for (const Atom* intersectingAtom: intersectingAtoms) { exitDirection += m_OwnerMOSR->RotateOffset(intersectingAtom->GetNormal()); } @@ -1403,7 +1439,7 @@ namespace RTE { Vector atomExitVector = Vector(); Vector totalExitVector = Vector(); - for (const Atom *intersectingAtom : intersectingAtoms) { + for (const Atom* intersectingAtom: intersectingAtoms) { bool rayHit = false; atomPos = intersectingAtom->GetCurrentPos(); @@ -1435,15 +1471,15 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: Look into breaking this into smaller methods. - bool AtomGroup::ResolveMOSIntersection(Vector &position) { + bool AtomGroup::ResolveMOSIntersection(Vector& position) { if (!m_OwnerMOSR->m_HitsMOs) { return true; } - MovableObject *intersectedMO = nullptr; + MovableObject* intersectedMO = nullptr; MOID currentMOID = g_NoMOID; MOID hitMOID = g_NoMOID; @@ -1451,7 +1487,7 @@ namespace RTE { Vector atomPos = Vector(); // First go through all Atoms to find the first intersection and get the intersected MO - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { atomOffset = m_OwnerMOSR->RotateOffset(atom->GetOffset()); atom->SetupPos(position + atomOffset); atomPos = atom->GetCurrentPos(); @@ -1462,7 +1498,7 @@ namespace RTE { currentMOID = hitMOID; // Get the MO we seem to be intersecting - MovableObject *tempMO = g_MovableMan.GetMOFromID(hitMOID); + MovableObject* tempMO = g_MovableMan.GetMOFromID(hitMOID); RTEAssert(tempMO, "Intersected MOID couldn't be translated to a real MO!"); tempMO = tempMO->GetRootParent(); @@ -1480,10 +1516,10 @@ namespace RTE { return false; } - std::vector intersectingAtoms; + std::vector intersectingAtoms; // Restart and go through all Atoms to find all intersecting the specific intersected MO - for (Atom *atom : m_Atoms) { + for (Atom* atom: m_Atoms) { atomPos = atom->GetCurrentPos(); if (g_SceneMan.GetMOIDPixel(atomPos.GetFloorIntX(), atomPos.GetFloorIntY(), m_OwnerMOSR->GetTeam()) == currentMOID) { // Add atom to list of intersecting ones @@ -1496,7 +1532,7 @@ namespace RTE { Vector totalExitVector = Vector(); // Go through all intersecting Atoms and find their average inverse normal - for (const Atom *intersectingAtom : intersectingAtoms) { + for (const Atom* intersectingAtom: intersectingAtoms) { exitDirection += m_OwnerMOSR->RotateOffset(intersectingAtom->GetNormal()); } @@ -1512,7 +1548,7 @@ namespace RTE { // See which of the intersecting Atoms has the longest to travel along the exit direction before it clears float sqrLongestDistance = 0.0F; - for (const Atom *intersectingAtom : intersectingAtoms) { + for (const Atom* intersectingAtom: intersectingAtoms) { atomPos = intersectingAtom->GetCurrentPos(); if (g_SceneMan.CastFindMORay(atomPos, exitDirection, g_NoMOID, clearPos, 0, true, 0)) { atomExitVector = clearPos - atomPos.GetFloored(); @@ -1539,8 +1575,8 @@ namespace RTE { float normMassA = invMassA / (invMassA + invMassB); float normMassB = invMassB / (invMassA + invMassB); - //TODO investigate whether we should apply some (relatively small) amount of movement to the object even if it's a lot heavier, for more realistic physics - // If the intersected is much larger than this' MO, then only move this. Otherwise, apply the movements to both this and the intersected MO's, proportional to their respective masses. + // TODO investigate whether we should apply some (relatively small) amount of movement to the object even if it's a lot heavier, for more realistic physics + // If the intersected is much larger than this' MO, then only move this. Otherwise, apply the movements to both this and the intersected MO's, proportional to their respective masses. if (normMassB < 0.33F) { thisExit = totalExitVector; } else { @@ -1550,12 +1586,12 @@ namespace RTE { } // Now actually apply the exit vectors to both, but only if the jump isn't too jarring - if (thisExit.MagnitudeIsLessThan(m_OwnerMOSR->GetIndividualRadius())) { - position += thisExit; + if (thisExit.MagnitudeIsLessThan(m_OwnerMOSR->GetIndividualRadius())) { + position += thisExit; } - if (!intersectedExit.IsZero() && intersectedExit.MagnitudeIsLessThan(intersectedMO->GetRadius())) { - intersectedMO->SetPos(intersectedMO->GetPos() + intersectedExit); + if (!intersectedExit.IsZero() && intersectedExit.MagnitudeIsLessThan(intersectedMO->GetRadius())) { + intersectedMO->SetPos(intersectedMO->GetPos() + intersectedExit); } if (m_OwnerMOSR->CanBeSquished() && RatioInTerrain() > 0.75F) /* && totalExitVector.MagnitudeIsGreaterThan(m_OwnerMOSR->GetDiameter())) */ { @@ -1564,7 +1600,7 @@ namespace RTE { m_OwnerMOSR->GibThis(-totalExitVector); } - MOSRotating *intersectedMOS = dynamic_cast(intersectedMO); + MOSRotating* intersectedMOS = dynamic_cast(intersectedMO); if (intersectedMOS && intersectedMOS->CanBeSquished() && intersectedMOS->GetAtomGroup()->RatioInTerrain() > 0.75F) /* && totalExitVector.MagnitudeIsGreaterThan(intersectedMO->GetDiameter())) */ { // Move back before gibbing so gibs don't end up inside terrain @@ -1576,7 +1612,7 @@ namespace RTE { return intersectingAtoms.empty(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float AtomGroup::GetSurfaceArea(int pixelWidth) const { float distributionAmount; @@ -1586,10 +1622,10 @@ namespace RTE { distributionAmount = static_cast(pixelWidth); break; case AreaDistributionType::Circle: { - const float radius = static_cast(pixelWidth) * 0.5F; - distributionAmount = c_PI * radius * radius; - break; - } + const float radius = static_cast(pixelWidth) * 0.5F; + distributionAmount = c_PI * radius * radius; + break; + } case AreaDistributionType::Square: distributionAmount = static_cast(pixelWidth * pixelWidth); break; @@ -1601,15 +1637,15 @@ namespace RTE { return distributionAmount * m_AreaDistributionSurfaceAreaMultiplier; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::Draw(BITMAP *targetBitmap, const Vector &targetPos, bool useLimbPos, unsigned char color) const { + void AtomGroup::Draw(BITMAP* targetBitmap, const Vector& targetPos, bool useLimbPos, unsigned char color) const { Vector atomPos; Vector normal; acquire_bitmap(targetBitmap); - for (const Atom *atom : m_Atoms) { + for (const Atom* atom: m_Atoms) { if (!useLimbPos) { atomPos = m_OwnerMOSR->GetPos() + GetAdjustedAtomOffset(atom); } else { @@ -1624,11 +1660,11 @@ namespace RTE { release_bitmap(targetBitmap); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: dan pls. - void AtomGroup::GenerateAtomGroup(MOSRotating *ownerMOSRotating) { - BITMAP *refSprite = ownerMOSRotating->GetSpriteFrame(); + void AtomGroup::GenerateAtomGroup(MOSRotating* ownerMOSRotating) { + BITMAP* refSprite = ownerMOSRotating->GetSpriteFrame(); const Vector spriteOffset = ownerMOSRotating->GetSpriteOffset(); const int spriteWidth = refSprite->w * static_cast(m_OwnerMOSR->GetScale()); const int spriteHeight = refSprite->h * static_cast(m_OwnerMOSR->GetScale()); @@ -1639,7 +1675,7 @@ namespace RTE { int y; bool inside; - BITMAP *checkBitmap = create_bitmap_ex(8, spriteWidth, spriteHeight); + BITMAP* checkBitmap = create_bitmap_ex(8, spriteWidth, spriteHeight); clear_to_color(checkBitmap, g_MaskColor); acquire_bitmap(refSprite); @@ -1826,15 +1862,19 @@ namespace RTE { } // If no Atoms were made, just place a default one in the middle - if (m_Atoms.empty()) { AddAtomToGroup(ownerMOSRotating, spriteOffset, spriteWidth / 2, spriteHeight / 2, false); } + if (m_Atoms.empty()) { + AddAtomToGroup(ownerMOSRotating, spriteOffset, spriteWidth / 2, spriteHeight / 2, false); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::AddAtomToGroup(MOSRotating *ownerMOSRotating, const Vector &spriteOffset, int x, int y, bool calcNormal) { - Atom *atomToAdd = new Atom(Vector(static_cast(x) + spriteOffset.GetFloorIntX(), static_cast(y) + spriteOffset.GetFloorIntY()), m_Material, ownerMOSRotating); - if (calcNormal) { atomToAdd->CalculateNormal(ownerMOSRotating->GetSpriteFrame(), -ownerMOSRotating->GetSpriteOffset()); } + void AtomGroup::AddAtomToGroup(MOSRotating* ownerMOSRotating, const Vector& spriteOffset, int x, int y, bool calcNormal) { + Atom* atomToAdd = new Atom(Vector(static_cast(x) + spriteOffset.GetFloorIntX(), static_cast(y) + spriteOffset.GetFloorIntY()), m_Material, ownerMOSRotating); + if (calcNormal) { + atomToAdd->CalculateNormal(ownerMOSRotating->GetSpriteFrame(), -ownerMOSRotating->GetSpriteOffset()); + } atomToAdd->SetIgnoreMOIDsByGroup(&m_IgnoreMOIDs); m_Atoms.push_back(atomToAdd); } -} +} // namespace RTE diff --git a/Source/Entities/AtomGroup.h b/Source/Entities/AtomGroup.h index 403ed29017..c9328d79b9 100644 --- a/Source/Entities/AtomGroup.h +++ b/Source/Entities/AtomGroup.h @@ -14,7 +14,6 @@ namespace RTE { class AtomGroup : public Entity { public: - EntityAllocation(AtomGroup); SerializableOverrideMethods; ClassInfoGetters; @@ -29,7 +28,10 @@ namespace RTE { /// Copy constructor method used to instantiate an AtomGroup object identical to an already existing one. /// /// An AtomGroup object which is passed in by reference. - AtomGroup(const AtomGroup &reference) { Clear(); Create(reference); } + AtomGroup(const AtomGroup& reference) { + Clear(); + Create(reference); + } /// /// Makes the AtomGroup object ready for use. @@ -42,7 +44,7 @@ namespace RTE { /// /// A reference to the AtomGroup to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const AtomGroup &reference) { return Create(reference, false); } + int Create(const AtomGroup& reference) { return Create(reference, false); } /// /// Creates an AtomGroup to be identical to another, by deep copy, with the option to only copy Atoms that belong to the reference AtomGroup's owner thereby excluding any Atom subgroups. @@ -50,15 +52,15 @@ namespace RTE { /// A reference to the AtomGroup to deep copy. /// Whether or not to only copy Atoms that belong to the reference AtomGroup's owner directly. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const AtomGroup &reference, bool onlyCopyOwnerAtoms); + int Create(const AtomGroup& reference, bool onlyCopyOwnerAtoms); /// - /// Creates an AtomGroup after the silhouette shape of a passed in MOSRotating by dotting the outline of the sprite with Atoms. + /// Creates an AtomGroup after the silhouette shape of a passed in MOSRotating by dotting the outline of the sprite with Atoms. /// The passed in MOSRotating will also be made the owner of this AtomGroup! Ownership of the MOSRotating is NOT transferred! /// /// A pointer to a MOSRotating whose outline will be approximated by Atoms of this AtomGroup, and that will be set as the owner of this AtomGroup. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(MOSRotating *ownerMOSRotating) { return Create(ownerMOSRotating, m_Material, m_Resolution, m_Depth); } + int Create(MOSRotating* ownerMOSRotating) { return Create(ownerMOSRotating, m_Material, m_Resolution, m_Depth); } /// /// Creates an AtomGroup after the silhouette shape of a passed in MOSRotating by dotting the outline of the sprite with Atoms. @@ -69,7 +71,7 @@ namespace RTE { /// Resolution, or density of the Atoms in representing the MOSRotating's outline. Lower value equals higher density. /// The depth into the sprite that the Atoms should be placed. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(MOSRotating *ownerMOSRotating, Material const *material, int resolution = 1, int depth = 0); + int Create(MOSRotating* ownerMOSRotating, Material const* material, int resolution = 1, int depth = 0); #pragma endregion #pragma region Destruction @@ -87,7 +89,10 @@ namespace RTE { /// /// Resets the entire AtomGroup, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Entity::Reset(); } + void Reset() override { + Clear(); + Entity::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -95,13 +100,13 @@ namespace RTE { /// Gets the current list of Atoms that make up the group. /// /// A const reference to the Atom list. - const std::vector & GetAtomList() const { return m_Atoms; } + const std::vector& GetAtomList() const { return m_Atoms; } /// /// Sets the a new list of Atoms that make up the group. /// /// List of Atoms that make up the group. - void SetAtomList(const std::vector &newAtoms); + void SetAtomList(const std::vector& newAtoms); /// /// Gets the current number of Atoms that make up the group. @@ -119,19 +124,19 @@ namespace RTE { /// Gets the current owner MOSRotating of this AtomGroup. /// /// A pointer to the owner. - MOSRotating * GetOwner() const { return m_OwnerMOSR; } + MOSRotating* GetOwner() const { return m_OwnerMOSR; } /// /// Sets the current owner MOSRotating of this AtomGroup. /// /// A pointer to the new owner. Ownership is NOT transferred! - void SetOwner(MOSRotating *newOwner); + void SetOwner(MOSRotating* newOwner); /// /// Gets the Material of this AtomGroup. /// /// A const pointer to the Material. - const Material * GetMaterial() const { return (m_Material) ? m_Material : g_SceneMan.GetMaterialFromID(g_MaterialAir); } + const Material* GetMaterial() const { return (m_Material) ? m_Material : g_SceneMan.GetMaterialFromID(g_MaterialAir); } /// /// Gets whether this AtomGroup's Atoms are to be automatically generated based on a bitmap, or manually specified. @@ -156,7 +161,7 @@ namespace RTE { /// /// The individual Atom to get the offset for. /// The offset of an Atom in this AtomGroup adjusted to the Owner MOSRotating horizontal flip and rotation. - Vector GetAdjustedAtomOffset(const Atom *atom) const; + Vector GetAdjustedAtomOffset(const Atom* atom) const; /// /// Gets the current position of this AtomGroup as a limb. @@ -170,7 +175,7 @@ namespace RTE { /// /// The Vector with the new absolute position. /// Whether to adjust the new position for horizontal flip or not. - void SetLimbPos(const Vector &newPos, bool hFlipped = false) { m_LimbPos = newPos - m_JointOffset.GetXFlipped(hFlipped); } + void SetLimbPos(const Vector& newPos, bool hFlipped = false) { m_LimbPos = newPos - m_JointOffset.GetXFlipped(hFlipped); } /// /// Gets the current mass moment of inertia of this AtomGroup. @@ -182,7 +187,7 @@ namespace RTE { /// Sets the offset of the joint relative to this AtomGroup's origin when used as a limb. /// /// The new joint offset. - void SetJointOffset(const Vector &newOffset) { m_JointOffset = newOffset; } + void SetJointOffset(const Vector& newOffset) { m_JointOffset = newOffset; } #pragma endregion #pragma region Atom Management @@ -192,7 +197,11 @@ namespace RTE { /// /// A pointer to an Atom that will pushed onto the end of the list. Ownership IS transferred! /// The subgroup ID that the new Atom will have within the group. - void AddAtom(Atom *newAtom, long subgroupID = 0) { newAtom->SetSubID(subgroupID); m_Atoms.push_back(newAtom); m_MomentOfInertia = 0.0F; } + void AddAtom(Atom* newAtom, long subgroupID = 0) { + newAtom->SetSubID(subgroupID); + m_Atoms.push_back(newAtom); + m_MomentOfInertia = 0.0F; + } /// /// Adds a list of new Atoms to the internal list that makes up this AtomGroup. Ownership of all Atoms in the list IS NOT transferred! @@ -201,7 +210,7 @@ namespace RTE { /// The desired subgroup ID for the Atoms being added. /// An offset that should be applied to all added Atoms. /// The rotation of the placed Atoms around the specified offset. - void AddAtoms(const std::vector &atomList, long subgroupID = 0, const Vector &offset = Vector(), const Matrix &offsetRotation = Matrix()); + void AddAtoms(const std::vector& atomList, long subgroupID = 0, const Vector& offset = Vector(), const Matrix& offsetRotation = Matrix()); /// /// Removes all Atoms of a specific subgroup ID from this AtomGroup. @@ -213,7 +222,12 @@ namespace RTE { /// /// Removes all atoms in this AtomGroup, leaving it empty of Atoms. /// - void RemoveAllAtoms() { m_Atoms.clear(); m_SubGroups.clear(); m_MomentOfInertia = 0.0F; m_StoredOwnerMass = 0.0F; } + void RemoveAllAtoms() { + m_Atoms.clear(); + m_SubGroups.clear(); + m_MomentOfInertia = 0.0F; + m_StoredOwnerMass = 0.0F; + } /// /// Gets whether the AtomGroup contains a subgroup with the given subgroupID. @@ -229,7 +243,7 @@ namespace RTE { /// The change in offset for the Atoms of the specified subgroup. /// The rotation of the updated Atoms around the specified offset. /// Whether any Atoms were found and updated for the specified subgroup. - bool UpdateSubAtoms(long subgroupID = 0, const Vector &newOffset = Vector(), const Matrix& newOffsetRotation = Matrix()); + bool UpdateSubAtoms(long subgroupID = 0, const Vector& newOffset = Vector(), const Matrix& newOffsetRotation = Matrix()); #pragma endregion #pragma region Travel @@ -261,7 +275,7 @@ namespace RTE { /// /// Pseudocode explaining how this works can be found at: https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/wiki/Notes-on-AtomGroup::Travel. /// - float Travel(Vector &position, Vector &velocity, Matrix &rotation, float &angularVel, bool &didWrap, Vector &totalImpulse, float mass, float travelTime, bool callOnBounce = false, bool callOnSink = false, bool scenePreLocked = false); + float Travel(Vector& position, Vector& velocity, Matrix& rotation, float& angularVel, bool& didWrap, Vector& totalImpulse, float mass, float travelTime, bool callOnBounce = false, bool callOnSink = false, bool scenePreLocked = false); /// /// Makes this AtomGroup travel without rotation and react with the scene by pushing against it. @@ -275,7 +289,7 @@ namespace RTE { /// Whether to call the parent MOSR's OnSink function upon sinking into anything or not. /// Whether the Scene has been pre-locked or not. /// A Vector with the resulting push impulse force, in Newton-second (Ns). - Vector PushTravel(Vector &position, const Vector &velocity, float pushForce, bool &didWrap, float travelTime, bool callOnBounce = false, bool callOnSink = false, bool scenePreLocked = false); + Vector PushTravel(Vector& position, const Vector& velocity, float pushForce, bool& didWrap, float travelTime, bool callOnBounce = false, bool callOnSink = false, bool scenePreLocked = false); /// /// Makes this AtomGroup travel as a pushing entity relative to the position of the owning MOSRotating. @@ -291,7 +305,7 @@ namespace RTE { /// The position, relative to the owning actor's position, that we should rotate around. /// The positional offset to apply to our limb path. /// Whether the LimbPath passed in could start free of terrain or not. - bool PushAsLimb(const Vector &jointPos, const Vector &velocity, const Matrix &rotation, LimbPath &limbPath, const float travelTime, bool *restarted = nullptr, bool affectRotation = true, Vector rotationOffset = Vector(), Vector positionOffset = Vector()); + bool PushAsLimb(const Vector& jointPos, const Vector& velocity, const Matrix& rotation, LimbPath& limbPath, const float travelTime, bool* restarted = nullptr, bool affectRotation = true, Vector rotationOffset = Vector(), Vector positionOffset = Vector()); /// /// Makes this AtomGroup travel as a lifeless limb, constrained to a radius around the joint pin in the center. @@ -303,7 +317,7 @@ namespace RTE { /// A float with the angular velocity in rad/sec of the owning MOSRotating. /// The mass of this dead weight limb. /// The amount of time in seconds that this AtomGroup is supposed to travel. - void FlailAsLimb(const Vector &ownerPos, const Vector &jointOffset, const float limbRadius, const Vector &velocity, const float angularVel, const float mass, const float travelTime); + void FlailAsLimb(const Vector& ownerPos, const Vector& jointOffset, const float limbRadius, const Vector& velocity, const float angularVel, const float mass, const float travelTime); #pragma endregion #pragma region Collision @@ -344,14 +358,14 @@ namespace RTE { /// Current position of the owner MOSR. /// Only attempt to move out of materials stronger than this specific ID. /// Whether any intersection was successfully resolved. Will return true even if there wasn't any intersections to begin with. - bool ResolveTerrainIntersection(Vector &position, unsigned char strongerThan = 0) const; + bool ResolveTerrainIntersection(Vector& position, unsigned char strongerThan = 0) const; /// /// Checks whether any of the Atoms in this AtomGroup are on top of MOSprites, and if so, attempt to move the OwnerMO out so none of the Atoms are inside the other MOSprite's silhouette anymore. /// /// Current position of the owner MOSR.> /// Whether all intersections were successfully resolved. - bool ResolveMOSIntersection(Vector &position); + bool ResolveMOSIntersection(Vector& position); /// /// Returns the surface area for a given pixel width. @@ -368,14 +382,18 @@ namespace RTE { /// The absolute position of the target bitmap's upper left corner in the Scene. /// Whether to use the limb position of this AtomGroup, or the owner's position. /// The color to draw the Atoms' pixels as. - void Draw(BITMAP *targetBitmap, const Vector &targetPos, bool useLimbPos = false, unsigned char color = 34) const; + void Draw(BITMAP* targetBitmap, const Vector& targetPos, bool useLimbPos = false, unsigned char color = 34) const; #pragma endregion protected: /// /// Enumeration for how the AtomGroup's area is distributed. Linear means it acts a 2D line whereas Circle/Square acts as a pseudo-3d circle/square. /// - enum class AreaDistributionType { Linear, Circle, Square }; + enum class AreaDistributionType { + Linear, + Circle, + Square + }; static const std::unordered_map c_AreaDistributionTypeMap; //!< A map of strings to AreaDistributionTypes to support string parsing for the AreaDistributionType enum. @@ -383,12 +401,12 @@ namespace RTE { // TODO: It's probably worth trying out changing this from a list to a vector. m_Atoms is iterated over often and we could probably get some big gainz by doing this swap. // The downside is anytime attachables with atoms get added we may have the cost of resizing the vector but that's an uncommon use case while iterating over atoms happens multiple times per frame. - std::vector m_Atoms; //!< List of Atoms that constitute the group. Owned by this. - std::unordered_map> m_SubGroups; //!< Sub groupings of Atoms. Points to Atoms owned in m_Atoms. Not owned. + std::vector m_Atoms; //!< List of Atoms that constitute the group. Owned by this. + std::unordered_map> m_SubGroups; //!< Sub groupings of Atoms. Points to Atoms owned in m_Atoms. Not owned. - MOSRotating *m_OwnerMOSR; //!< The owner of this AtomGroup. The owner is obviously not owned by this AtomGroup. + MOSRotating* m_OwnerMOSR; //!< The owner of this AtomGroup. The owner is obviously not owned by this AtomGroup. float m_StoredOwnerMass; //!< The stored mass for the owner MOSR. Used to figure out when the moment of inertia needs to be recalculated due to significant mass changes. - const Material *m_Material; //!< Material of this AtomGroup. + const Material* m_Material; //!< Material of this AtomGroup. bool m_AutoGenerate; //!< Whether the Atoms in this AtomGroup were automatically generated based on a sprite, or manually defined. @@ -408,19 +426,18 @@ namespace RTE { float m_MomentOfInertia; //!< Moment of Inertia for this AtomGroup. std::vector m_IgnoreMOIDs; //!< List of MOIDs this AtomGroup will ignore collisions with. - + AreaDistributionType m_AreaDistributionType; //!< How this AtomGroup will distribute energy when it collides with something. - + float m_AreaDistributionSurfaceAreaMultiplier; //!< A multiplier for the AtomGroup's surface area, which affects how much it digs into terrain. 0.5 would halve the surface area so it would dig into terrain twice as much, 2.0 would make it dig into terrain half as much. private: - #pragma region Create Breakdown /// /// Generates an AtomGroup using the owner MOSRotating's sprite outline. /// /// MOSRotating whose outline will be approximated by Atoms of this AtomGroup. - void GenerateAtomGroup(MOSRotating *ownerMOSRotating); + void GenerateAtomGroup(MOSRotating* ownerMOSRotating); /// /// Create and add an Atom to this AtomGroup's list of Atoms. This is called during GenerateAtomGroup(). @@ -430,7 +447,7 @@ namespace RTE { /// X coordinate in the sprite frame. /// Y coordinate in the sprite frame. /// Whether to set a normal for the Atom. Should be true for surface Atoms. - void AddAtomToGroup(MOSRotating *ownerMOSRotating, const Vector &spriteOffset, int x, int y, bool calcNormal); + void AddAtomToGroup(MOSRotating* ownerMOSRotating, const Vector& spriteOffset, int x, int y, bool calcNormal); #pragma endregion /// @@ -438,5 +455,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/Attachable.cpp b/Source/Entities/Attachable.cpp index 53a77be3f8..9f43c75f00 100644 --- a/Source/Entities/Attachable.cpp +++ b/Source/Entities/Attachable.cpp @@ -11,7 +11,7 @@ namespace RTE { ConcreteClassInfo(Attachable, MOSRotating, 0); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Attachable::Clear() { m_Parent = nullptr; @@ -53,7 +53,7 @@ namespace RTE { m_PreUpdateHasRunThisFrame = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Attachable::Create() { MOSRotating::Create(); @@ -63,9 +63,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Attachable::Create(const Attachable &reference) { + int Attachable::Create(const Attachable& reference) { MOSRotating::Create(reference); m_ParentOffset = reference.m_ParentOffset; @@ -98,8 +98,8 @@ namespace RTE { m_CollidesWithTerrainWhileAttached = reference.m_CollidesWithTerrainWhileAttached; m_IgnoresParticlesWhileAttached = reference.m_IgnoresParticlesWhileAttached; - for (const std::unique_ptr &pieSlice : reference.m_PieSlices) { - m_PieSlices.emplace_back(std::unique_ptr(dynamic_cast(pieSlice->Clone()))); + for (const std::unique_ptr& pieSlice: reference.m_PieSlices) { + m_PieSlices.emplace_back(std::unique_ptr(dynamic_cast(pieSlice->Clone()))); } m_PrevRotAngleOffset = reference.m_PrevRotAngleOffset; @@ -107,11 +107,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Attachable::ReadProperty(const std::string_view &propName, Reader &reader) { + int Attachable::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MOSRotating::ReadProperty(propName, reader)); - + MatchProperty("ParentOffset", { reader >> m_ParentOffset; }); MatchProperty("DrawAfterParent", { reader >> m_DrawAfterParent; }); MatchProperty("DeleteWhenRemovedFromParent", { reader >> m_DeleteWhenRemovedFromParent; }); @@ -127,13 +127,17 @@ namespace RTE { }); MatchProperty("JointOffset", { reader >> m_JointOffset; }); MatchProperty("BreakWound", { - m_BreakWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); - if (!m_ParentBreakWound) { m_ParentBreakWound = m_BreakWound; } + m_BreakWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); + if (!m_ParentBreakWound) { + m_ParentBreakWound = m_BreakWound; + } }); - MatchProperty("ParentBreakWound", { m_ParentBreakWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); + MatchProperty("ParentBreakWound", { m_ParentBreakWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); MatchProperty("InheritsHFlipped", { reader >> m_InheritsHFlipped; - if (m_InheritsHFlipped != 0 && m_InheritsHFlipped != 1) { m_InheritsHFlipped = -1; } + if (m_InheritsHFlipped != 0 && m_InheritsHFlipped != 1) { + m_InheritsHFlipped = -1; + } }); MatchProperty("InheritsRotAngle", { reader >> m_InheritsRotAngle; }); MatchForwards("InheritedRotAngleRadOffset") MatchProperty("InheritedRotAngleOffset", { reader >> m_InheritedRotAngleOffset; }); @@ -141,14 +145,14 @@ namespace RTE { MatchProperty("InheritsFrame", { reader >> m_InheritsFrame; }); MatchProperty("CollidesWithTerrainWhileAttached", { reader >> m_CollidesWithTerrainWhileAttached; }); MatchProperty("IgnoresParticlesWhileAttached", { reader >> m_IgnoresParticlesWhileAttached; }); - MatchProperty("AddPieSlice", { m_PieSlices.emplace_back(std::unique_ptr(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)))); }); + MatchProperty("AddPieSlice", { m_PieSlices.emplace_back(std::unique_ptr(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)))); }); EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Attachable::Save(Writer &writer) const { + int Attachable::Save(Writer& writer) const { MOSRotating::Save(writer); writer.NewPropertyWithValue("ParentOffset", m_ParentOffset); @@ -171,16 +175,16 @@ namespace RTE { writer.NewPropertyWithValue("CollidesWithTerrainWhileAttached", m_CollidesWithTerrainWhileAttached); writer.NewPropertyWithValue("IgnoresParticlesWhileAttached", m_IgnoresParticlesWhileAttached); - for (const std::unique_ptr &pieSlice : m_PieSlices) { + for (const std::unique_ptr& pieSlice: m_PieSlices) { writer.NewPropertyWithValue("AddPieSlice", pieSlice.get()); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::TransferJointForces(Vector &jointForces) { + bool Attachable::TransferJointForces(Vector& jointForces) { if (!m_Parent) { return false; } @@ -193,9 +197,9 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::TransferJointImpulses(Vector &jointImpulses, float jointStiffnessValueToUse, float jointStrengthValueToUse, float gibImpulseLimitValueToUse) { + bool Attachable::TransferJointImpulses(Vector& jointImpulses, float jointStiffnessValueToUse, float jointStrengthValueToUse, float gibImpulseLimitValueToUse) { if (!m_Parent) { return false; } @@ -205,11 +209,15 @@ namespace RTE { jointStiffnessValueToUse = jointStiffnessValueToUse > 0 ? jointStiffnessValueToUse : m_JointStiffness; jointStrengthValueToUse = jointStrengthValueToUse > 0 ? jointStrengthValueToUse : m_JointStrength; gibImpulseLimitValueToUse = gibImpulseLimitValueToUse > 0 ? gibImpulseLimitValueToUse : m_GibImpulseLimit; - if (jointStrengthValueToUse == 0) { gibImpulseLimitValueToUse = 0; } - if (gibImpulseLimitValueToUse > 0) { gibImpulseLimitValueToUse = std::max(gibImpulseLimitValueToUse, jointStrengthValueToUse); } + if (jointStrengthValueToUse == 0) { + gibImpulseLimitValueToUse = 0; + } + if (gibImpulseLimitValueToUse > 0) { + gibImpulseLimitValueToUse = std::max(gibImpulseLimitValueToUse, jointStrengthValueToUse); + } Vector totalImpulseForce; - for (const auto &[impulseForce, impulseForceOffset] : m_ImpulseForces) { + for (const auto& [impulseForce, impulseForceOffset]: m_ImpulseForces) { totalImpulseForce += impulseForce; } totalImpulseForce *= jointStiffnessValueToUse; @@ -218,8 +226,10 @@ namespace RTE { // The first part is getting the Dot/Scalar product of the perpendicular of the offset vector for the force onto the force vector itself (dot product is the amount two vectors are pointing in the same direction). // The second part is dividing that Dot product by the moment of inertia, i.e. the torque needed to make it turn. All of this is multiplied by 1 - JointStiffness, because max stiffness joints transfer all force to parents (leaving none to affect the Attachable) and min stiffness transfer none. if (!m_InheritsRotAngle) { - for (const auto &[impulseForce, impulseForceOffset] : m_ImpulseForces) { - if (!impulseForceOffset.IsZero()) { m_AngularVel += (impulseForceOffset.GetPerpendicular().Dot(impulseForce) / m_pAtomGroup->GetMomentOfInertia()) * (1.0F - std::clamp(jointStiffnessValueToUse, 0.0F, 1.0F)); } + for (const auto& [impulseForce, impulseForceOffset]: m_ImpulseForces) { + if (!impulseForceOffset.IsZero()) { + m_AngularVel += (impulseForceOffset.GetPerpendicular().Dot(impulseForce) / m_pAtomGroup->GetMomentOfInertia()) * (1.0F - std::clamp(jointStiffnessValueToUse, 0.0F, 1.0F)); + } } } @@ -240,17 +250,17 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Attachable::CollectDamage() { if (m_DamageMultiplier != 0) { float totalDamage = m_DamageCount; m_DamageCount = 0; - for (AEmitter *wound : m_Wounds) { + for (AEmitter* wound: m_Wounds) { totalDamage += wound->CollectDamage(); } - for (Attachable *attachable : m_Attachables) { + for (Attachable* attachable: m_Attachables) { totalDamage += attachable->CollectDamage(); } return totalDamage * m_DamageMultiplier; @@ -258,7 +268,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Attachable::SetCollidesWithTerrainWhileAttached(bool collidesWithTerrainWhileAttached) { if (m_CollidesWithTerrainWhileAttached != collidesWithTerrainWhileAttached) { @@ -271,39 +281,47 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Attachable::CanCollideWithTerrain() const { if (m_CollidesWithTerrainWhileAttached && IsAttached() && GetParent() != GetRootParent()) { - if (const Attachable *parentAsAttachable = dynamic_cast(GetParent())) { return parentAsAttachable->CanCollideWithTerrain(); } + if (const Attachable* parentAsAttachable = dynamic_cast(GetParent())) { + return parentAsAttachable->CanCollideWithTerrain(); + } } return m_CollidesWithTerrainWhileAttached; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::CollideAtPoint(HitData &hd) { - if (m_IgnoresParticlesWhileAttached && m_Parent && !m_Parent->ToDelete() && !dynamic_cast(hd.Body[HITOR])) { + bool Attachable::CollideAtPoint(HitData& hd) { + if (m_IgnoresParticlesWhileAttached && m_Parent && !m_Parent->ToDelete() && !dynamic_cast(hd.Body[HITOR])) { return false; } return MOSRotating::CollideAtPoint(hd); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::ParticlePenetration(HitData &hd) { + bool Attachable::ParticlePenetration(HitData& hd) { bool penetrated = MOSRotating::ParticlePenetration(hd); if (m_Parent) { - MovableObject *hitor = hd.Body[HITOR]; + MovableObject* hitor = hd.Body[HITOR]; float damageToAdd = hitor->DamageOnCollision(); damageToAdd += penetrated ? hitor->DamageOnPenetration() : 0; - if (hitor->GetApplyWoundDamageOnCollision()) { damageToAdd += m_pEntryWound->GetEmitDamage() * hitor->WoundDamageMultiplier(); } - if (hitor->GetApplyWoundBurstDamageOnCollision()) { damageToAdd += m_pEntryWound->GetBurstDamage() * hitor->WoundDamageMultiplier(); } + if (hitor->GetApplyWoundDamageOnCollision()) { + damageToAdd += m_pEntryWound->GetEmitDamage() * hitor->WoundDamageMultiplier(); + } + if (hitor->GetApplyWoundBurstDamageOnCollision()) { + damageToAdd += m_pEntryWound->GetBurstDamage() * hitor->WoundDamageMultiplier(); + } - if (damageToAdd != 0) { AddDamage(damageToAdd); } + if (damageToAdd != 0) { + AddDamage(damageToAdd); + } if (penetrated || damageToAdd != 0) { - if (Actor *parentAsActor = dynamic_cast(GetRootParent()); parentAsActor && parentAsActor->GetPerceptiveness() > 0) { + if (Actor* parentAsActor = dynamic_cast(GetRootParent()); parentAsActor && parentAsActor->GetPerceptiveness() > 0) { Vector extruded(hd.HitVel[HITOR]); extruded.SetMagnitude(parentAsActor->GetHeight()); extruded = m_Pos - extruded; @@ -316,47 +334,51 @@ namespace RTE { return penetrated; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::GibThis(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { - if (m_Parent) { m_Parent->RemoveAttachable(this, true, true); } + void Attachable::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { + if (m_Parent) { + m_Parent->RemoveAttachable(this, true, true); + } MOSRotating::GibThis(impactImpulse, movableObjectToIgnore); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::HandlePotentialRadiusAffectingAttachable(const Attachable *attachable) { + bool Attachable::HandlePotentialRadiusAffectingAttachable(const Attachable* attachable) { if (MOSRotating::HandlePotentialRadiusAffectingAttachable(attachable)) { - if (IsAttached()) { m_Parent->HandlePotentialRadiusAffectingAttachable(this); } + if (IsAttached()) { + m_Parent->HandlePotentialRadiusAffectingAttachable(this); + } return true; } return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Attachable::UpdateScripts() { if (m_Parent && !m_AllLoadedScripts.empty() && !ObjectScriptsInitialized()) { - RunScriptedFunctionInAppropriateScripts("OnAttach", false, false, { m_Parent }, {}, {}); + RunScriptedFunctionInAppropriateScripts("OnAttach", false, false, {m_Parent}, {}, {}); } return MOSRotating::UpdateScripts(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Attachable::Update() { - if (!m_PreUpdateHasRunThisFrame) { - PreUpdate(); + if (!m_PreUpdateHasRunThisFrame) { + PreUpdate(); } UpdatePositionAndJointPositionBasedOnOffsets(); if (m_Parent) { - if (m_ParentOffset != m_PrevParentOffset || m_JointOffset != m_PrevJointOffset) { + if (m_ParentOffset != m_PrevParentOffset || m_JointOffset != m_PrevJointOffset) { m_PrevParentOffset = m_ParentOffset; m_PrevJointOffset = m_JointOffset; - m_Parent->HandlePotentialRadiusAffectingAttachable(this); + m_Parent->HandlePotentialRadiusAffectingAttachable(this); } m_PrevVel = m_Vel; @@ -364,11 +386,13 @@ namespace RTE { m_Team = m_Parent->GetTeam(); - MOSRotating *rootParentAsMOSR = dynamic_cast(GetRootParent()); + MOSRotating* rootParentAsMOSR = dynamic_cast(GetRootParent()); float currentRotAngleOffset = (GetRotAngle() * GetFlipFactor()) - rootParentAsMOSR->GetRotAngle(); if (rootParentAsMOSR && CanCollideWithTerrain()) { // Note: This safety check exists to ensure the parent's AtomGroup contains this Attachable's Atoms in a subgroup. Hardcoded Attachables need this in order to work, since they're cloned before their parent's AtomGroup exists. - if (!rootParentAsMOSR->GetAtomGroup()->ContainsSubGroup(m_AtomSubgroupID)) { AddOrRemoveAtomsFromRootParentAtomGroup(true, false); } + if (!rootParentAsMOSR->GetAtomGroup()->ContainsSubGroup(m_AtomSubgroupID)) { + AddOrRemoveAtomsFromRootParentAtomGroup(true, false); + } if (std::abs(currentRotAngleOffset - m_PrevRotAngleOffset) > 0.01745F) { // Update for 1 degree differences Matrix atomRotationForSubgroup(rootParentAsMOSR->FacingAngle(GetRotAngle()) - rootParentAsMOSR->FacingAngle(rootParentAsMOSR->GetRotAngle())); @@ -392,7 +416,7 @@ namespace RTE { if (m_Parent && GetRootParent()->HasEverBeenAddedToMovableMan()) { g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ScriptsUpdate); if (!m_AllLoadedScripts.empty() && !ObjectScriptsInitialized()) { - RunScriptedFunctionInAppropriateScripts("OnAttach", false, false, { m_Parent }, {}, {}); + RunScriptedFunctionInAppropriateScripts("OnAttach", false, false, {m_Parent}, {}, {}); } UpdateScripts(); g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ScriptsUpdate); @@ -401,12 +425,14 @@ namespace RTE { m_PreUpdateHasRunThisFrame = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Attachable::PreUpdate() { if (!m_PreUpdateHasRunThisFrame) { if (m_Parent) { - if (InheritsHFlipped() != 0) { m_HFlipped = m_InheritsHFlipped == 1 ? m_Parent->IsHFlipped() : !m_Parent->IsHFlipped(); } + if (InheritsHFlipped() != 0) { + m_HFlipped = m_InheritsHFlipped == 1 ? m_Parent->IsHFlipped() : !m_Parent->IsHFlipped(); + } if (InheritsRotAngle()) { SetRotAngle(m_Parent->GetRotAngle() + m_InheritedRotAngleOffset * m_Parent->GetFlipFactor()); m_AngularVel = 0.0F; @@ -416,85 +442,101 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Attachable::SetMass(const float newMass) { float currentMass = GetMass(); if (newMass != currentMass) { float previousMassForUpdatingParent = m_Parent ? currentMass : 0.0F; MovableObject::SetMass(newMass); - if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + if (m_Parent) { + m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Attachable::UpdateAttachableAndWoundMass(float oldAttachableOrWoundMass, float newAttachableOrWoundMass) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; MOSRotating::UpdateAttachableAndWoundMass(oldAttachableOrWoundMass, newAttachableOrWoundMass); - if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + if (m_Parent) { + m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::AddAttachable(Attachable *attachable, const Vector &parentOffsetToSet) { + void Attachable::AddAttachable(Attachable* attachable, const Vector& parentOffsetToSet) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; MOSRotating::AddAttachable(attachable, parentOffsetToSet); - if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + if (m_Parent) { + m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Attachable * Attachable::RemoveAttachable(Attachable *attachable, bool addToMovableMan, bool addBreakWounds) { + Attachable* Attachable::RemoveAttachable(Attachable* attachable, bool addToMovableMan, bool addBreakWounds) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; - Attachable *removedAttachable = MOSRotating::RemoveAttachable(attachable, addToMovableMan, addBreakWounds); - if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + Attachable* removedAttachable = MOSRotating::RemoveAttachable(attachable, addToMovableMan, addBreakWounds); + if (m_Parent) { + m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); + } return removedAttachable; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::AddWound(AEmitter *woundToAdd, const Vector &parentOffsetToSet, bool checkGibWoundLimit) { + void Attachable::AddWound(AEmitter* woundToAdd, const Vector& parentOffsetToSet, bool checkGibWoundLimit) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; MOSRotating::AddWound(woundToAdd, parentOffsetToSet, checkGibWoundLimit); - if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + if (m_Parent) { + m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Attachable::RemoveWounds(int numberOfWoundsToRemove, bool includeAttachablesWithAPositiveDamageMultiplier, bool includeAttachablesWithANegativeDamageMultiplier, bool includeAttachablesWithNoDamageMultiplier) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; float result = MOSRotating::RemoveWounds(numberOfWoundsToRemove, includeAttachablesWithAPositiveDamageMultiplier, includeAttachablesWithANegativeDamageMultiplier, includeAttachablesWithNoDamageMultiplier); - if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + if (m_Parent) { + m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); + } return result; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::SetParent(MOSRotating *newParent) { + void Attachable::SetParent(MOSRotating* newParent) { if (newParent == m_Parent) { return; } RTEAssert(!(m_Parent && newParent), "Tried to set an Attachable's " + GetModuleAndPresetName() + " parent without first unsetting its old parent, " + (IsAttached() ? GetParent()->GetModuleAndPresetName() : "ERROR") + "."); - MOSRotating *parentToUseForScriptCall = newParent ? newParent : m_Parent; + MOSRotating* parentToUseForScriptCall = newParent ? newParent : m_Parent; - //TODO Get rid of the need for calling ResetAllTimers, if something like inventory swapping needs timers reset it should do it itself! This blanket handling probably has side-effects. - // Timers are reset here as a precaution, so that if something was sitting in an inventory, it doesn't cause backed up emissions. + // TODO Get rid of the need for calling ResetAllTimers, if something like inventory swapping needs timers reset it should do it itself! This blanket handling probably has side-effects. + // Timers are reset here as a precaution, so that if something was sitting in an inventory, it doesn't cause backed up emissions. ResetAllTimers(); if (newParent) { m_Parent = newParent; m_Team = newParent->GetTeam(); - if (InheritsHFlipped() != 0) { m_HFlipped = m_InheritsHFlipped == 1 ? m_Parent->IsHFlipped() : !m_Parent->IsHFlipped(); } + if (InheritsHFlipped() != 0) { + m_HFlipped = m_InheritsHFlipped == 1 ? m_Parent->IsHFlipped() : !m_Parent->IsHFlipped(); + } if (InheritsRotAngle()) { SetRotAngle(m_Parent->GetRotAngle() + m_InheritedRotAngleOffset * m_Parent->GetFlipFactor()); m_AngularVel = 0.0F; } UpdatePositionAndJointPositionBasedOnOffsets(); - if (CanCollideWithTerrain()) { AddOrRemoveAtomsFromRootParentAtomGroup(true, true); } + if (CanCollideWithTerrain()) { + AddOrRemoveAtomsFromRootParentAtomGroup(true, true); + } - if (const Actor *rootParentAsActor = dynamic_cast(GetRootParent())) { - if (PieMenu *rootParentAsActorPieMenu = rootParentAsActor->GetPieMenu()) { + if (const Actor* rootParentAsActor = dynamic_cast(GetRootParent())) { + if (PieMenu* rootParentAsActorPieMenu = rootParentAsActor->GetPieMenu()) { AddOrRemovePieSlicesAndListenersFromPieMenu(rootParentAsActorPieMenu, true); } } @@ -504,9 +546,9 @@ namespace RTE { m_Team = -1; m_IsWound = false; - if (MovableObject *rootParent = GetRootParent()) { - const MovableObject *whichMOToNotHit = GetWhichMOToNotHit(); - const MovableObject *rootParentMOToNotHit = rootParent->GetWhichMOToNotHit(); + if (MovableObject* rootParent = GetRootParent()) { + const MovableObject* whichMOToNotHit = GetWhichMOToNotHit(); + const MovableObject* rootParentMOToNotHit = rootParent->GetWhichMOToNotHit(); if ((whichMOToNotHit && whichMOToNotHit != rootParent) || (rootParentMOToNotHit && rootParentMOToNotHit != this)) { m_pMOToNotHit = nullptr; } else { @@ -514,18 +556,22 @@ namespace RTE { rootParent->SetWhichMOToNotHit(this); } - if (const Actor *rootParentAsActor = dynamic_cast(rootParent)) { - if (PieMenu *rootParentAsActorPieMenu = rootParentAsActor->GetPieMenu()) { + if (const Actor* rootParentAsActor = dynamic_cast(rootParent)) { + if (PieMenu* rootParentAsActorPieMenu = rootParentAsActor->GetPieMenu()) { AddOrRemovePieSlicesAndListenersFromPieMenu(rootParentAsActorPieMenu, false); } } } - if (CanCollideWithTerrain()) { AddOrRemoveAtomsFromRootParentAtomGroup(false, true); } + if (CanCollideWithTerrain()) { + AddOrRemoveAtomsFromRootParentAtomGroup(false, true); + } m_Parent = newParent; - for (Attachable *attachable : m_Attachables) { - if (attachable->m_CollidesWithTerrainWhileAttached) { attachable->AddOrRemoveAtomsFromRootParentAtomGroup(true, true); } + for (Attachable* attachable: m_Attachables) { + if (attachable->m_CollidesWithTerrainWhileAttached) { + attachable->AddOrRemoveAtomsFromRootParentAtomGroup(true, true); + } } } @@ -534,7 +580,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Attachable::UpdatePositionAndJointPositionBasedOnOffsets() { if (m_Parent) { @@ -546,12 +592,12 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Attachable::AddOrRemoveAtomsFromRootParentAtomGroup(bool addAtoms, bool propagateToChildAttachables) { if (IsAttached()) { - MOSRotating *rootParentAsMOSR = dynamic_cast(GetRootParent()); - AtomGroup *rootParentAtomGroup = rootParentAsMOSR ? rootParentAsMOSR->GetAtomGroup() : nullptr; + MOSRotating* rootParentAsMOSR = dynamic_cast(GetRootParent()); + AtomGroup* rootParentAtomGroup = rootParentAsMOSR ? rootParentAsMOSR->GetAtomGroup() : nullptr; if (rootParentAtomGroup) { if (addAtoms && !rootParentAtomGroup->ContainsSubGroup(GetAtomSubgroupID())) { Vector atomOffsetForSubgroup = g_SceneMan.ShortestDistance(rootParentAsMOSR->GetPos(), m_Pos, g_SceneMan.SceneWrapsX()); @@ -563,34 +609,36 @@ namespace RTE { } if (propagateToChildAttachables) { - for (Attachable *attachable : m_Attachables) { - if (attachable->m_CollidesWithTerrainWhileAttached) { attachable->AddOrRemoveAtomsFromRootParentAtomGroup(addAtoms, propagateToChildAttachables); } + for (Attachable* attachable: m_Attachables) { + if (attachable->m_CollidesWithTerrainWhileAttached) { + attachable->AddOrRemoveAtomsFromRootParentAtomGroup(addAtoms, propagateToChildAttachables); + } } } } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::AddOrRemovePieSlicesAndListenersFromPieMenu(PieMenu *pieMenuToModify, bool addToPieMenu) { + void Attachable::AddOrRemovePieSlicesAndListenersFromPieMenu(PieMenu* pieMenuToModify, bool addToPieMenu) { RTEAssert(pieMenuToModify, "Cannot add or remove Attachable PieSlices and listeners from a non-existant PieMenu."); if (addToPieMenu) { if (m_FunctionsAndScripts.find("WhilePieMenuOpen") != m_FunctionsAndScripts.end() && !m_FunctionsAndScripts.find("WhilePieMenuOpen")->second.empty()) { pieMenuToModify->AddWhilePieMenuOpenListener(this, std::bind(&MovableObject::WhilePieMenuOpenListener, this, pieMenuToModify)); } - for (const std::unique_ptr &pieSlice : m_PieSlices) { - pieMenuToModify->AddPieSlice(dynamic_cast(pieSlice.get()->Clone()), this, true); + for (const std::unique_ptr& pieSlice: m_PieSlices) { + pieMenuToModify->AddPieSlice(dynamic_cast(pieSlice.get()->Clone()), this, true); } } else { pieMenuToModify->RemoveWhilePieMenuOpenListener(this); pieMenuToModify->RemovePieSlicesByOriginalSource(this); } - for (Attachable *attachable : m_Attachables) { + for (Attachable* attachable: m_Attachables) { attachable->AddOrRemovePieSlicesAndListenersFromPieMenu(pieMenuToModify, addToPieMenu); } - for (AEmitter *wound : m_Wounds) { + for (AEmitter* wound: m_Wounds) { wound->AddOrRemovePieSlicesAndListenersFromPieMenu(pieMenuToModify, addToPieMenu); } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/Attachable.h b/Source/Entities/Attachable.h index 7a3d702ed3..2b5358e1e4 100644 --- a/Source/Entities/Attachable.h +++ b/Source/Entities/Attachable.h @@ -15,7 +15,6 @@ namespace RTE { friend class MOSRotating; public: - EntityAllocation(Attachable); AddScriptFunctionNames(MOSRotating, "OnAttach", "OnDetach"); SerializableOverrideMethods; @@ -38,7 +37,7 @@ namespace RTE { /// /// A reference to the Attachable to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Attachable &reference); + int Create(const Attachable& reference); #pragma endregion #pragma region Destruction @@ -51,12 +50,20 @@ namespace RTE { /// Destroys and resets (through Clear()) the Attachable object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { MOSRotating::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + MOSRotating::Destroy(); + } + Clear(); + } /// /// Resets the entire Attachable, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); MOSRotating::Reset(); } + void Reset() override { + Clear(); + MOSRotating::Reset(); + } #pragma endregion #pragma region Parent Getters and Setters @@ -64,13 +71,13 @@ namespace RTE { /// Gets the MOSRotating which is the parent of this Attachable. /// /// A pointer to the parent of this Attachable. - MOSRotating * GetParent() override { return m_Parent; } + MOSRotating* GetParent() override { return m_Parent; } /// /// Gets the MOSRotating which is the parent of this Attachable. /// /// A pointer to the parent of this Attachable. - const MOSRotating * GetParent() const override { return m_Parent; } + const MOSRotating* GetParent() const override { return m_Parent; } /// /// Indicates whether this Attachable is attached to an MOSRotating parent or not. @@ -83,31 +90,31 @@ namespace RTE { /// /// A pointer to which MOSRotating you want to check for. /// Whether it's attached or not. - bool IsAttachedTo(const MOSRotating *parentToCheck) const { return m_Parent == parentToCheck; } + bool IsAttachedTo(const MOSRotating* parentToCheck) const { return m_Parent == parentToCheck; } /// /// Gets the MO which is the ultimate root parent of this Attachable and its parent. /// /// A pointer to the highest root parent of this Attachable. - MovableObject * GetRootParent() override { return m_Parent ? m_Parent->GetRootParent() : this; } + MovableObject* GetRootParent() override { return m_Parent ? m_Parent->GetRootParent() : this; } /// /// Gets the MO which is the ultimate root parent of this Attachable and its parent. /// /// A pointer to the highest root parent of this Attachable. - const MovableObject * GetRootParent() const override { return m_Parent ? m_Parent->GetRootParent() : this; } + const MovableObject* GetRootParent() const override { return m_Parent ? m_Parent->GetRootParent() : this; } /// /// Gets the stored offset between this Attachable's parent's position and the joint position. This should be maintained by the parent. /// /// A const reference Vector describing the offset from the parent's position to the joint position. - const Vector & GetParentOffset() const { return m_ParentOffset; } + const Vector& GetParentOffset() const { return m_ParentOffset; } /// /// Sets the stored offset between this Attachable's parent's Pos and the joint position. This should be maintained by the parent. /// /// A const reference to the new parent offset. - void SetParentOffset(const Vector &newParentOffset) { m_ParentOffset = newParentOffset; } + void SetParentOffset(const Vector& newParentOffset) { m_ParentOffset = newParentOffset; } /// /// Gets whether this Attachable is to be drawn after (in front of) or before (behind) its parent. @@ -243,19 +250,19 @@ namespace RTE { /// Gets the offset of the joint (the point around which this Attachable and its parent hinge) from this Attachable's center of mass/origin. /// /// A const reference Vector describing the offset of the joint relative to this Attachable's origin/center of mass position. - const Vector & GetJointOffset() const { return m_JointOffset; } + const Vector& GetJointOffset() const { return m_JointOffset; } /// /// Sets the offset of the joint (the point around which this Attachable and its parent hinge) from this Attachable's center of mass/origin. /// /// A Vector describing the offset of the joint relative to the this Attachable's origin/center of mass position. - void SetJointOffset(const Vector &newJointOffset) { m_JointOffset = newJointOffset; } + void SetJointOffset(const Vector& newJointOffset) { m_JointOffset = newJointOffset; } /// /// Gets the absolute position of the joint that the parent of this Attachable sets upon Update(). /// /// A Vector describing the current absolute position of the joint. - const Vector & GetJointPos() const { return m_JointPos; } + const Vector& GetJointPos() const { return m_JointPos; } #pragma endregion #pragma region Force Transferral @@ -266,7 +273,7 @@ namespace RTE { /// /// A vector that will have the forces affecting the joint ADDED to it. /// False if the Attachable has no parent or its accumulated forces are greater than its joint strength, otherwise true. - bool TransferJointForces(Vector &jointForces); + bool TransferJointForces(Vector& jointForces); /// /// Bundles up all the accumulated impulse forces of this Attachable and calculates how they transfer to the joint, and therefore to the parent. @@ -278,7 +285,7 @@ namespace RTE { /// An optional override for the Attachable's joint strength for this function call. Primarily used to allow subclasses to perform special behavior. /// An optional override for the Attachable's gib impulse limit for this function call. Primarily used to allow subclasses to perform special behavior. /// False if the Attachable has no parent or its accumulated forces are greater than its joint strength or gib impulse limit, otherwise true. - virtual bool TransferJointImpulses(Vector &jointImpulses, float jointStiffnessValueToUse = -1, float jointStrengthValueToUse = -1, float gibImpulseLimitValueToUse = -1); + virtual bool TransferJointImpulses(Vector& jointImpulses, float jointStiffnessValueToUse = -1, float jointStrengthValueToUse = -1, float gibImpulseLimitValueToUse = -1); #pragma endregion #pragma region Damage and Wound Management @@ -299,25 +306,25 @@ namespace RTE { /// Gets the AEmitter that represents the wound added to this Attachable when it gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! /// /// A const pointer to the break wound AEmitter. - const AEmitter * GetBreakWound() const { return m_BreakWound; } + const AEmitter* GetBreakWound() const { return m_BreakWound; } /// /// Sets the AEmitter that represents the wound added to this Attachable when it gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! /// /// The AEmitter to use for this Attachable's breakwound. - void SetBreakWound(AEmitter *breakWound) { m_BreakWound = breakWound; } + void SetBreakWound(AEmitter* breakWound) { m_BreakWound = breakWound; } /// /// Gets the AEmitter that represents the wound added to this Attachable's parent when this Attachable gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! /// /// A const pointer to the parent break wound AEmitter. - const AEmitter * GetParentBreakWound() const { return m_ParentBreakWound; } + const AEmitter* GetParentBreakWound() const { return m_ParentBreakWound; } /// /// Sets the AEmitter that represents the wound added to this Attachable's parent when this Attachable gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! /// /// The AEmitter to use for the parent's breakwound. - void SetParentBreakWound(AEmitter *breakWound) { m_ParentBreakWound = breakWound; } + void SetParentBreakWound(AEmitter* breakWound) { m_ParentBreakWound = breakWound; } #pragma endregion #pragma region Inherited Value Getters and Setters @@ -420,11 +427,11 @@ namespace RTE { #pragma region Override Methods /// /// Calculates the collision response when another MO's Atom collides with this MO's physical representation. - /// The effects will be applied directly to this MO, and also represented in the passed in HitData. + /// The effects will be applied directly to this MO, and also represented in the passed in HitData. /// /// Reference to the HitData struct which describes the collision. This will be modified to represent the results of the collision. /// Whether the collision has been deemed valid. If false, then disregard any impulses in the HitData. - bool CollideAtPoint(HitData &hitData) override; + bool CollideAtPoint(HitData& hitData) override; /// /// Determines whether a particle which has hit this MO will penetrate, and if so, whether it gets lodged or exits on the other side of this MO. @@ -435,7 +442,7 @@ namespace RTE { /// Whether the particle managed to penetrate into this MO or not. /// If something other than an MOPixel or MOSParticle is being passed in as the hitor, false will trivially be returned here. /// - bool ParticlePenetration(HitData &hitData) override; + bool ParticlePenetration(HitData& hitData) override; /// /// Destroys this Attachable and creates its specified Gibs in its place with appropriate velocities. @@ -443,14 +450,14 @@ namespace RTE { /// /// The impulse (kg * m/s) of the impact causing the gibbing to happen. /// A pointer to an MO which the Gibs and Attachables should not be colliding with. - void GibThis(const Vector &impactImpulse = Vector(), MovableObject *movableObjectToIgnore = nullptr) override; + void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr) override; /// /// Checks if the given Attachable should affect radius, and handles it if it should. /// /// The Attachable to check. /// Whether the radius affecting Attachable changed as a result of this call. - bool HandlePotentialRadiusAffectingAttachable(const Attachable *attachable) override; + bool HandlePotentialRadiusAffectingAttachable(const Attachable* attachable) override; /// /// Updates this Attachable's Lua scripts. @@ -488,21 +495,21 @@ namespace RTE { /// Adds the passed in Attachable the list of Attachables and sets its parent to this Attachable. /// /// The Attachable to add. - void AddAttachable(Attachable *attachable) final { MOSRotating::AddAttachable(attachable); } + void AddAttachable(Attachable* attachable) final { MOSRotating::AddAttachable(attachable); } /// /// Adds the passed in Attachable the list of Attachables, changes its parent offset to the passed in Vector, and sets its parent to this Attachable. /// /// The Attachable to add. /// The Vector to set as the Attachable's parent offset. - void AddAttachable(Attachable *attachable, const Vector &parentOffsetToSet) final; + void AddAttachable(Attachable* attachable, const Vector& parentOffsetToSet) final; /// /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. /// /// The UniqueID of the Attachable to remove. /// A pointer to the removed Attachable. Ownership IS transferred! - Attachable * RemoveAttachable(long attachableUniqueID) final { return MOSRotating::RemoveAttachable(attachableUniqueID); } + Attachable* RemoveAttachable(long attachableUniqueID) final { return MOSRotating::RemoveAttachable(attachableUniqueID); } /// /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. @@ -512,14 +519,14 @@ namespace RTE { /// Whether or not to add the Attachable to MovableMan once it has been removed. /// Whether or not to add break wounds to the removed Attachable and this Attachable. /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! - Attachable * RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds) final { return MOSRotating::RemoveAttachable(attachableUniqueID, addToMovableMan, addBreakWounds); } + Attachable* RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds) final { return MOSRotating::RemoveAttachable(attachableUniqueID, addToMovableMan, addBreakWounds); } /// /// Removes the passed in Attachable and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. /// /// The Attachable to remove. /// A pointer to the removed Attachable. Ownership IS transferred! - Attachable * RemoveAttachable(Attachable *attachable) final { return MOSRotating::RemoveAttachable(attachable); } + Attachable* RemoveAttachable(Attachable* attachable) final { return MOSRotating::RemoveAttachable(attachable); } /// /// Removes the passed in Attachable and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. @@ -529,7 +536,7 @@ namespace RTE { /// Whether or not to add the Attachable to MovableMan once it has been removed. /// Whether or not to add break wounds to the removed Attachable and this Attachable. /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! - Attachable * RemoveAttachable(Attachable *attachable, bool addToMovableMan, bool addBreakWounds) final; + Attachable* RemoveAttachable(Attachable* attachable, bool addToMovableMan, bool addBreakWounds) final; /// /// Adds the passed in wound AEmitter to the list of wounds and changes its parent offset to the passed in Vector. @@ -537,7 +544,7 @@ namespace RTE { /// The wound AEmitter to add. /// The vector to set as the wound AEmitter's parent offset. /// Whether to gib this Attachable if adding this wound raises its wound count past its gib wound limit. Defaults to true. - void AddWound(AEmitter *woundToAdd, const Vector &parentOffsetToSet, bool checkGibWoundLimit = true) final; + void AddWound(AEmitter* woundToAdd, const Vector& parentOffsetToSet, bool checkGibWoundLimit = true) final; /// /// Removes the specified number of wounds from this Attachable, and returns damage caused by these removed wounds. @@ -560,10 +567,9 @@ namespace RTE { #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. - MOSRotating *m_Parent; //!< Pointer to the MOSRotating this attachable is attached to. + MOSRotating* m_Parent; //!< Pointer to the MOSRotating this attachable is attached to. Vector m_ParentOffset; //!< The offset from the parent's Pos to the joint point this Attachable is attached with. bool m_DrawAfterParent; //!< Whether to draw this Attachable after (in front of) or before (behind) the parent. bool m_DrawnNormallyByParent; //!< Whether this Attachable will be drawn normally when attached, or will require special handling by some non-MOSR parent type. @@ -574,7 +580,7 @@ namespace RTE { float m_GibWithParentChance; //!< The percentage chance that this Attachable will gib when its parent does. 0 means never, 1 means always. float m_ParentGibBlastStrengthMultiplier; //!< The multiplier for how strongly this Attachable's parent's gib blast strength will be applied to it when its parent's gibs. - //TODO This is a stopgap for a dedicated Wound class, that would be helpful to simplify things like this and default damage multiplier handling. + // TODO This is a stopgap for a dedicated Wound class, that would be helpful to simplify things like this and default damage multiplier handling. bool m_IsWound; //!< Whether or not this Attachable has been added as a wound. Only set and applied for Attachables with parents. float m_JointStrength; //!< The amount of impulse force needed on this to detach it from the host Actor, in kg * m/s. A value of 0 means the join is infinitely strong and will never break naturally. @@ -583,8 +589,8 @@ namespace RTE { Vector m_JointPos; //!< The absolute position of the joint that the parent sets upon Update() if this Attachable is attached to it. float m_DamageCount; //!< The number of damage points that this Attachable has accumulated since the last time CollectDamage() was called. - const AEmitter *m_BreakWound; //!< The wound this Attachable will receive when it breaks from its parent. - const AEmitter *m_ParentBreakWound; //!< The wound this Attachable's parent will receive when the Attachable breaks from its parent. + const AEmitter* m_BreakWound; //!< The wound this Attachable will receive when it breaks from its parent. + const AEmitter* m_ParentBreakWound; //!< The wound this Attachable's parent will receive when the Attachable breaks from its parent. int m_InheritsHFlipped; //!< Whether this Attachable should inherit its parent's HFlipped. Defaults to 1 (normal inheritance). bool m_InheritsRotAngle; //!< Whether this Attachable should inherit its parent's RotAngle. Defaults to true. @@ -606,10 +612,9 @@ namespace RTE { /// Sets this Attachable's parent MOSRotating, and also sets its Team based on its parent and, if the Attachable is set to collide, adds/removes Atoms to its new/old parent. /// /// A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! - virtual void SetParent(MOSRotating *newParent); + virtual void SetParent(MOSRotating* newParent); private: - /// /// Updates the position of this Attachable based on its parent offset and joint offset. Used during update and when something sets these offsets through setters. /// @@ -628,7 +633,7 @@ namespace RTE { /// /// The PieMenu to modify, passed in to keep the recursion simple and clean. /// Whether to add this Attachable's PieSlices and listeners to, or remove them from, the root parent's PieMenu. - void AddOrRemovePieSlicesAndListenersFromPieMenu(PieMenu *pieMenuToModify, bool addToPieMenu); + void AddOrRemovePieSlicesAndListenersFromPieMenu(PieMenu* pieMenuToModify, bool addToPieMenu); /// /// Clears all the member variables of this Attachable, effectively resetting the members of this abstraction level only. @@ -636,8 +641,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - Attachable(const Attachable &reference) = delete; - Attachable & operator=(const Attachable &rhs) = delete; + Attachable(const Attachable& reference) = delete; + Attachable& operator=(const Attachable& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/BunkerAssembly.cpp b/Source/Entities/BunkerAssembly.cpp index 16ace93bc5..6e8c4fbd67 100644 --- a/Source/Entities/BunkerAssembly.cpp +++ b/Source/Entities/BunkerAssembly.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,513 +19,456 @@ namespace RTE { -ConcreteClassInfo(BunkerAssembly, SceneObject, 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this BunkerAssembly, effectively -// resetting the members of this abstraction level only. - -void BunkerAssembly::Clear() -{ - m_FGColorFile.Reset(); - m_MaterialFile.Reset(); - m_BGColorFile.Reset(); - m_FGColorBitmap = 0; - m_MaterialBitmap = 0; - m_BGColorBitmap = 0; - m_BitmapOffset.Reset(); - m_OffsetDefined = false; - m_ChildObjects.clear(); - m_PlacedObjects.clear(); - m_ParentAssemblyScheme.clear(); - m_pPresentationBitmap = 0; - m_SymmetricAssembly.clear(); - m_ParentSchemeGroup.clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BunkerAssembly object ready for use. + ConcreteClassInfo(BunkerAssembly, SceneObject, 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this BunkerAssembly, effectively + // resetting the members of this abstraction level only. + + void BunkerAssembly::Clear() { + m_FGColorFile.Reset(); + m_MaterialFile.Reset(); + m_BGColorFile.Reset(); + m_FGColorBitmap = 0; + m_MaterialBitmap = 0; + m_BGColorBitmap = 0; + m_BitmapOffset.Reset(); + m_OffsetDefined = false; + m_ChildObjects.clear(); + m_PlacedObjects.clear(); + m_ParentAssemblyScheme.clear(); + m_pPresentationBitmap = 0; + m_SymmetricAssembly.clear(); + m_ParentSchemeGroup.clear(); + } -int BunkerAssembly::Create() -{ - if (TerrainObject::Create() < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the BunkerAssembly object ready for use. - return 0; -} + int BunkerAssembly::Create() { + if (TerrainObject::Create() < 0) + return -1; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds placed object to the internallist of placed objects for this assembly, -// applies it's image to presentation bitmap and sets assembly price accordingly. -// Added scene object MUST have coordinates relative to this assembly. - -void BunkerAssembly::AddPlacedObject(SceneObject * pSO) -{ - m_PlacedObjects.push_back(pSO); - - //Increase gold value for every bunker module if it's not a deployment - Deployment * pDeployment = dynamic_cast(pSO); - //Set fixed price - //if (!pDeployment && !pSO->IsInGroup("Bunker Backgrounds")) - // m_OzValue += pSO->GetGoldValue(); - - // Draw this terrain object to presentaion bitmap - TerrainObject * pTObject = dynamic_cast(pSO); - if (pTObject) - { - Vector objectPos = pTObject->GetPos() + pTObject->GetBitmapOffset(); - - // Regular drawing - if (pTObject->HasMaterialBitmap()) { draw_sprite(m_MaterialBitmap, pTObject->GetMaterialBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); } - if (pTObject->HasBGColorBitmap()) { - draw_sprite(m_BGColorBitmap, pTObject->GetBGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); - draw_sprite(m_pPresentationBitmap, pTObject->GetBGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); - } - if (pTObject->HasFGColorBitmap()) { - draw_sprite(m_FGColorBitmap, pTObject->GetFGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); - draw_sprite(m_pPresentationBitmap, pTObject->GetFGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddPlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds placed object to the internallist of placed objects for this assembly, + // applies it's image to presentation bitmap and sets assembly price accordingly. + // Added scene object MUST have coordinates relative to this assembly. + + void BunkerAssembly::AddPlacedObject(SceneObject* pSO) { + m_PlacedObjects.push_back(pSO); + + // Increase gold value for every bunker module if it's not a deployment + Deployment* pDeployment = dynamic_cast(pSO); + // Set fixed price + // if (!pDeployment && !pSO->IsInGroup("Bunker Backgrounds")) + // m_OzValue += pSO->GetGoldValue(); + + // Draw this terrain object to presentaion bitmap + TerrainObject* pTObject = dynamic_cast(pSO); + if (pTObject) { + Vector objectPos = pTObject->GetPos() + pTObject->GetBitmapOffset(); + + // Regular drawing + if (pTObject->HasMaterialBitmap()) { + draw_sprite(m_MaterialBitmap, pTObject->GetMaterialBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); + } + if (pTObject->HasBGColorBitmap()) { + draw_sprite(m_BGColorBitmap, pTObject->GetBGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); + draw_sprite(m_pPresentationBitmap, pTObject->GetBGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); + } + if (pTObject->HasFGColorBitmap()) { + draw_sprite(m_FGColorBitmap, pTObject->GetFGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); + draw_sprite(m_pPresentationBitmap, pTObject->GetFGColorBitmap(), objectPos.GetFloorIntX(), objectPos.GetFloorIntY()); + } - // Read and add all child objects - pTObject->SetTeam(GetTeam()); + // Read and add all child objects + pTObject->SetTeam(GetTeam()); - for (const SceneObject::SOPlacer &childObject : pTObject->GetChildObjects()) { - SceneObject::SOPlacer newPlacer = childObject; - newPlacer.SetTeam(pTObject->GetTeam()); - // Explicitly set child object's offset, because it will be a part of a bigger 'terrain object' - newPlacer.SetOffset(newPlacer.GetOffset() + pTObject->GetPos() + m_BitmapOffset); - m_ChildObjects.push_back(newPlacer); + for (const SceneObject::SOPlacer& childObject: pTObject->GetChildObjects()) { + SceneObject::SOPlacer newPlacer = childObject; + newPlacer.SetTeam(pTObject->GetTeam()); + // Explicitly set child object's offset, because it will be a part of a bigger 'terrain object' + newPlacer.SetOffset(newPlacer.GetOffset() + pTObject->GetPos() + m_BitmapOffset); + m_ChildObjects.push_back(newPlacer); + } } } -} -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BunkerAssembly object ready for use. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the BunkerAssembly object ready for use. -int BunkerAssembly::Create(BunkerAssemblyScheme * pScheme) -{ - if (TerrainObject::Create() < 0) - return -1; + int BunkerAssembly::Create(BunkerAssemblyScheme* pScheme) { + if (TerrainObject::Create() < 0) + return -1; - m_pPresentationBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_pPresentationBitmap, g_MaskColor); + m_pPresentationBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth(), pScheme->GetBitmapHeight()); + clear_to_color(m_pPresentationBitmap, g_MaskColor); - m_FGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_FGColorBitmap, g_MaskColor); + m_FGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth(), pScheme->GetBitmapHeight()); + clear_to_color(m_FGColorBitmap, g_MaskColor); - m_MaterialBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_MaterialBitmap, g_MaskColor); + m_MaterialBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth(), pScheme->GetBitmapHeight()); + clear_to_color(m_MaterialBitmap, g_MaskColor); - m_BGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_BGColorBitmap, g_MaskColor); + m_BGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth(), pScheme->GetBitmapHeight()); + clear_to_color(m_BGColorBitmap, g_MaskColor); - m_BitmapOffset = pScheme->GetBitmapOffset(); - m_ParentAssemblyScheme = pScheme->GetPresetName(); - m_Pos = pScheme->GetPos(); + m_BitmapOffset = pScheme->GetBitmapOffset(); + m_ParentAssemblyScheme = pScheme->GetPresetName(); + m_Pos = pScheme->GetPos(); - AddToGroup("Assemblies"); - AddToGroup(m_ParentAssemblyScheme); - if (pScheme->GetAssemblyGroup().length() > 0) - { - AddToGroup(pScheme->GetAssemblyGroup()); - m_ParentSchemeGroup = pScheme->GetAssemblyGroup(); + AddToGroup("Assemblies"); + AddToGroup(m_ParentAssemblyScheme); + if (pScheme->GetAssemblyGroup().length() > 0) { + AddToGroup(pScheme->GetAssemblyGroup()); + m_ParentSchemeGroup = pScheme->GetAssemblyGroup(); + } + + return 0; } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MOPixel to be identical to another, by deep copy. + int BunkerAssembly::Create(const BunkerAssembly& reference) { + TerrainObject::Create(reference); + for (std::list::const_iterator oItr = reference.m_PlacedObjects.begin(); oItr != reference.m_PlacedObjects.end(); ++oItr) + m_PlacedObjects.push_back(dynamic_cast((*oItr)->Clone())); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MOPixel to be identical to another, by deep copy. + m_ParentAssemblyScheme = reference.m_ParentAssemblyScheme; + m_pPresentationBitmap = reference.m_pPresentationBitmap; + m_SymmetricAssembly = reference.m_SymmetricAssembly; + m_ParentSchemeGroup = reference.m_ParentSchemeGroup; -int BunkerAssembly::Create(const BunkerAssembly &reference) -{ - TerrainObject::Create(reference); + return 0; + } - for (std::list::const_iterator oItr = reference.m_PlacedObjects.begin(); oItr != reference.m_PlacedObjects.end(); ++oItr) - m_PlacedObjects.push_back(dynamic_cast((*oItr)->Clone())); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int BunkerAssembly::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return SceneObject::ReadProperty(propName, reader)); + + // Ignore TerrainObject's specific properties, but don't let parent class process them + MatchProperty("FGColorFile", {}); + MatchProperty("MaterialFile", {}); + MatchProperty("BGColorFile", {}); + MatchProperty("BitmapOffset", {}); + MatchProperty("Location", {}); + MatchProperty("AddChildObject", {}); + + MatchProperty("SymmetricAssembly", + { + reader >> m_SymmetricAssembly; + }); + MatchProperty("PlaceObject", + { + SceneObject* pSO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); + if (pSO) + AddPlacedObject(pSO); + }); + MatchProperty("ParentScheme", { + // Add to group like Entity::ReadProperty does + std::string parentScheme; + reader >> parentScheme; + AddToGroup(parentScheme); + g_PresetMan.RegisterGroup(parentScheme, reader.GetReadModuleID()); + + // Find the scheme's group, read it's dimensions and create presentation bitmap + const BunkerAssemblyScheme* pScheme = dynamic_cast(g_PresetMan.GetEntityPreset("BunkerAssemblyScheme", parentScheme, -1)); + if (pScheme) { + // Calculate fixed scheme price based on the scheme size + if (pScheme->GetGoldValue() == 0) + m_OzValue = pScheme->GetArea() * 3; + else + pScheme->GetGoldValue(); + + // Delete existing bitmaps to avoid leaks if someone adds assembly to multiple groups by mistake + delete m_pPresentationBitmap; + m_pPresentationBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth(), pScheme->GetBitmapHeight()); + clear_to_color(m_pPresentationBitmap, g_MaskColor); + + delete m_FGColorBitmap; + m_FGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth(), pScheme->GetBitmapHeight()); + clear_to_color(m_FGColorBitmap, g_MaskColor); + + delete m_MaterialBitmap; + m_MaterialBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth(), pScheme->GetBitmapHeight()); + clear_to_color(m_MaterialBitmap, g_MaskColor); + + delete m_BGColorBitmap; + m_BGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth(), pScheme->GetBitmapHeight()); + clear_to_color(m_BGColorBitmap, g_MaskColor); + + m_ParentAssemblyScheme = parentScheme; + m_BitmapOffset = pScheme->GetBitmapOffset(); + if (pScheme->GetAssemblyGroup().length() > 0) { + AddToGroup(pScheme->GetAssemblyGroup()); + m_ParentSchemeGroup = pScheme->GetAssemblyGroup(); + g_PresetMan.RegisterGroup(pScheme->GetAssemblyGroup(), reader.GetReadModuleID()); + } + + // Also add to Assemblies group + AddToGroup("Assemblies"); + g_PresetMan.RegisterGroup("Assemblies", reader.GetReadModuleID()); + } else { + // Do not allow to define assemblies prior to corresponding assembly scheme + char s[256]; + std::snprintf(s, sizeof(s), "Required BunkerAssemblyScheme '%s%' not found when trying to load BunkerAssembly '%s'! BunkerAssemblySchemes MUST be defined before dependent BunkerAssmeblies.", parentScheme.c_str(), m_PresetName.c_str()); + RTEAbort(s); + } + }); - m_ParentAssemblyScheme = reference.m_ParentAssemblyScheme; - m_pPresentationBitmap = reference.m_pPresentationBitmap; - m_SymmetricAssembly = reference.m_SymmetricAssembly; - m_ParentSchemeGroup = reference.m_ParentSchemeGroup; + EndPropertyList; + } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this BunkerAssembly with a Writer for + // later recreation with Create(Reader &reader); + int BunkerAssembly::Save(Writer& writer) const { + SceneObject::Save(writer); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int BunkerAssembly::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return SceneObject::ReadProperty(propName, reader)); - - // Ignore TerrainObject's specific properties, but don't let parent class process them - MatchProperty("FGColorFile", { }); - MatchProperty("MaterialFile", { }); - MatchProperty("BGColorFile", { }); - MatchProperty("BitmapOffset", { }); - MatchProperty("Location", { }); - MatchProperty("AddChildObject", { }); - - MatchProperty("SymmetricAssembly", - { - reader >> m_SymmetricAssembly; - }); - MatchProperty("PlaceObject", - { - SceneObject *pSO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); - if (pSO) - AddPlacedObject(pSO); - }); - MatchProperty("ParentScheme", { - //Add to group like Entity::ReadProperty does - std::string parentScheme; - reader >> parentScheme; - AddToGroup(parentScheme); - g_PresetMan.RegisterGroup(parentScheme, reader.GetReadModuleID()); - - // Find the scheme's group, read it's dimensions and create presentation bitmap - const BunkerAssemblyScheme * pScheme = dynamic_cast(g_PresetMan.GetEntityPreset("BunkerAssemblyScheme", parentScheme, -1)); - if (pScheme) - { - //Calculate fixed scheme price based on the scheme size - if (pScheme->GetGoldValue() == 0) - m_OzValue = pScheme->GetArea() * 3; - else - pScheme->GetGoldValue(); - - //Delete existing bitmaps to avoid leaks if someone adds assembly to multiple groups by mistake - delete m_pPresentationBitmap; - m_pPresentationBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_pPresentationBitmap, g_MaskColor); - - delete m_FGColorBitmap; - m_FGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_FGColorBitmap, g_MaskColor); - - delete m_MaterialBitmap; - m_MaterialBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_MaterialBitmap, g_MaskColor); - - delete m_BGColorBitmap; - m_BGColorBitmap = create_bitmap_ex(8, pScheme->GetBitmapWidth() , pScheme->GetBitmapHeight()); - clear_to_color(m_BGColorBitmap, g_MaskColor); - - m_ParentAssemblyScheme = parentScheme; - m_BitmapOffset = pScheme->GetBitmapOffset(); - if (pScheme->GetAssemblyGroup().length() > 0) - { - AddToGroup(pScheme->GetAssemblyGroup()); - m_ParentSchemeGroup = pScheme->GetAssemblyGroup(); - g_PresetMan.RegisterGroup(pScheme->GetAssemblyGroup(), reader.GetReadModuleID()); + // Groups are essential for BunkerAssemblies so save them, because entity seem to ignore them + for (auto itr = m_Groups.begin(); itr != m_Groups.end(); ++itr) { + if ((*itr) != m_ParentAssemblyScheme && (*itr) != m_ParentSchemeGroup) { + writer.NewProperty("AddToGroup"); + writer << *itr; } - - // Also add to Assemblies group - AddToGroup("Assemblies"); - g_PresetMan.RegisterGroup("Assemblies", reader.GetReadModuleID()); - } else { - // Do not allow to define assemblies prior to corresponding assembly scheme - char s[256]; - std::snprintf(s, sizeof(s), "Required BunkerAssemblyScheme '%s%' not found when trying to load BunkerAssembly '%s'! BunkerAssemblySchemes MUST be defined before dependent BunkerAssmeblies.", parentScheme.c_str(), m_PresetName.c_str()); - RTEAbort(s); } - }); - - EndPropertyList; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this BunkerAssembly with a Writer for -// later recreation with Create(Reader &reader); - -int BunkerAssembly::Save(Writer &writer) const -{ - SceneObject::Save(writer); - - // Groups are essential for BunkerAssemblies so save them, because entity seem to ignore them - for (auto itr = m_Groups.begin(); itr != m_Groups.end(); ++itr) - { - if ((*itr) != m_ParentAssemblyScheme && (*itr) != m_ParentSchemeGroup) - { - writer.NewProperty("AddToGroup"); - writer << *itr; + if (m_SymmetricAssembly.size() > 0) { + writer.NewProperty("SymmetricAssembly"); + writer << m_SymmetricAssembly; } - } - if (m_SymmetricAssembly.size() > 0) - { - writer.NewProperty("SymmetricAssembly"); - writer << m_SymmetricAssembly; - } - if (m_ParentAssemblyScheme.size() > 0) - { - writer.NewProperty("ParentScheme"); - writer << m_ParentAssemblyScheme; - } - - for (std::list::const_iterator oItr = m_PlacedObjects.begin(); oItr != m_PlacedObjects.end(); ++oItr) - { - writer.NewProperty("PlaceObject"); - writer.ObjectStart((*oItr)->GetClassName()); - writer.NewProperty("CopyOf"); - writer << (*oItr)->GetModuleAndPresetName(); - - writer.NewProperty("Position"); - writer << (*oItr)->GetPos(); - - // Only write certain properties if they are applicable to the type of SceneObject being written - MOSRotating *pSpriteObj = dynamic_cast(*oItr); - if (pSpriteObj) - { - writer.NewProperty("HFlipped"); - writer << pSpriteObj->IsHFlipped(); - Actor *pActor = dynamic_cast(pSpriteObj); - if (pActor) - { - writer.NewProperty("Team"); - writer << pActor->GetTeam(); - // Rotation of doors is important - ADoor *pDoor = dynamic_cast(pActor); - if (pDoor) - { - writer.NewProperty("Rotation"); - writer << pDoor->GetRotMatrix(); - } - } - } - TerrainObject *pTObject = dynamic_cast(*oItr); - if (pTObject && !pTObject->GetChildObjects().empty()) - { - writer.NewProperty("Team"); - writer << pTObject->GetTeam(); - } - Deployment *pDeployment = dynamic_cast(*oItr); - if (pDeployment) - { - writer.NewProperty("HFlipped"); - writer << pDeployment->IsHFlipped(); + if (m_ParentAssemblyScheme.size() > 0) { + writer.NewProperty("ParentScheme"); + writer << m_ParentAssemblyScheme; } + for (std::list::const_iterator oItr = m_PlacedObjects.begin(); oItr != m_PlacedObjects.end(); ++oItr) { + writer.NewProperty("PlaceObject"); + writer.ObjectStart((*oItr)->GetClassName()); + writer.NewProperty("CopyOf"); + writer << (*oItr)->GetModuleAndPresetName(); + + writer.NewProperty("Position"); + writer << (*oItr)->GetPos(); + + // Only write certain properties if they are applicable to the type of SceneObject being written + MOSRotating* pSpriteObj = dynamic_cast(*oItr); + if (pSpriteObj) { + writer.NewProperty("HFlipped"); + writer << pSpriteObj->IsHFlipped(); + Actor* pActor = dynamic_cast(pSpriteObj); + if (pActor) { + writer.NewProperty("Team"); + writer << pActor->GetTeam(); + // Rotation of doors is important + ADoor* pDoor = dynamic_cast(pActor); + if (pDoor) { + writer.NewProperty("Rotation"); + writer << pDoor->GetRotMatrix(); + } + } + } + TerrainObject* pTObject = dynamic_cast(*oItr); + if (pTObject && !pTObject->GetChildObjects().empty()) { + writer.NewProperty("Team"); + writer << pTObject->GetTeam(); + } + Deployment* pDeployment = dynamic_cast(*oItr); + if (pDeployment) { + writer.NewProperty("HFlipped"); + writer << pDeployment->IsHFlipped(); + } - writer.ObjectEnd(); - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the BunkerAssembly object. - -void BunkerAssembly::Destroy(bool notInherited) -{ - for (std::list::iterator oItr = m_PlacedObjects.begin(); oItr != m_PlacedObjects.end(); ++oItr) - { - delete (*oItr); - *oItr = 0; - } - - // Probably no need to delete those, as bitmaps are only created when preset is read from file - // and then they just copy pointers in via Clone() - //delete m_pPresentationBitmap; - //m_pPresentationBitmap = 0; - - //delete m_FGColorBitmap; - //m_FGColorBitmap = 0; - - //delete m_MaterialBitmap; - //m_MaterialBitmap = 0; - - //delete m_BGColorBitmap; - //m_BGColorBitmap = 0; - - if (!notInherited) - SceneObject::Destroy(); - Clear(); -} + writer.ObjectEnd(); + } + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the BunkerAssembly object. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetDeployments -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Retrieves the list of random deployemtns selected to be deployed by this assembly -// based on it's parent scheme MaxDeployments value. This list will always include all -// brain deployments so it can be longer that MaxDeployments. OWNERSHIP NOT TRANSFERRED. - -std::vector BunkerAssembly::GetDeployments() -{ - std::vector deploymentsList; - std::vector candidatesList; - - // Sort objects, brains are added by default, everything else are candidates - for (std::list::const_iterator itr = m_PlacedObjects.begin(); itr != m_PlacedObjects.end() ; ++itr) - { - Deployment * pDeployment = dynamic_cast(*itr); - - if (pDeployment) - { - if (pDeployment->IsInGroup("Brains")) - deploymentsList.push_back(pDeployment); - else - candidatesList.push_back(pDeployment); + void BunkerAssembly::Destroy(bool notInherited) { + for (std::list::iterator oItr = m_PlacedObjects.begin(); oItr != m_PlacedObjects.end(); ++oItr) { + delete (*oItr); + *oItr = 0; } - } - int maxDeployments = 0; + // Probably no need to delete those, as bitmaps are only created when preset is read from file + // and then they just copy pointers in via Clone() + // delete m_pPresentationBitmap; + // m_pPresentationBitmap = 0; - const BunkerAssemblyScheme * pScheme = dynamic_cast(g_PresetMan.GetEntityPreset("BunkerAssemblyScheme", m_ParentAssemblyScheme, -1)); - if (pScheme) - maxDeployments = pScheme->GetMaxDeployments(); + // delete m_FGColorBitmap; + // m_FGColorBitmap = 0; - int selected = 0; - for (int i = 0; i(0, candidatesList.size() - 1); - deploymentsList.push_back(candidatesList.at(selection)); - candidatesList.erase(candidatesList.begin() + selection); + // delete m_BGColorBitmap; + // m_BGColorBitmap = 0; + + if (!notInherited) + SceneObject::Destroy(); + Clear(); } - return deploymentsList; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetDeployments + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Retrieves the list of random deployemtns selected to be deployed by this assembly + // based on it's parent scheme MaxDeployments value. This list will always include all + // brain deployments so it can be longer that MaxDeployments. OWNERSHIP NOT TRANSFERRED. + + std::vector BunkerAssembly::GetDeployments() { + std::vector deploymentsList; + std::vector candidatesList; + + // Sort objects, brains are added by default, everything else are candidates + for (std::list::const_iterator itr = m_PlacedObjects.begin(); itr != m_PlacedObjects.end(); ++itr) { + Deployment* pDeployment = dynamic_cast(*itr); + + if (pDeployment) { + if (pDeployment->IsInGroup("Brains")) + deploymentsList.push_back(pDeployment); + else + candidatesList.push_back(pDeployment); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. + int maxDeployments = 0; -bool BunkerAssembly::IsOnScenePoint(Vector &scenePoint) const -{ - if (!m_pPresentationBitmap) - return false; + const BunkerAssemblyScheme* pScheme = dynamic_cast(g_PresetMan.GetEntityPreset("BunkerAssemblyScheme", m_ParentAssemblyScheme, -1)); + if (pScheme) + maxDeployments = pScheme->GetMaxDeployments(); - Vector bitmapPos = m_Pos + m_BitmapOffset; - if (WithinBox(scenePoint, bitmapPos, m_pPresentationBitmap->w, m_pPresentationBitmap->h)) - { - // Scene point on the bitmap - Vector bitmapPoint = scenePoint - bitmapPos; - if (getpixel(m_pPresentationBitmap, bitmapPoint.m_X, bitmapPoint.m_Y) != g_MaskColor) - return true; - } + int selected = 0; + for (int i = 0; i < maxDeployments; i++) { + if (candidatesList.size() == 0) + break; - return false; -} + int selection = RandomNum(0, candidatesList.size() - 1); + deploymentsList.push_back(candidatesList.at(selection)); + candidatesList.erase(candidatesList.begin() + selection); + } + return deploymentsList; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this Actor belongs to. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsOnScenePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this' current graphical representation overlaps + // a point in absolute scene coordinates. + + bool BunkerAssembly::IsOnScenePoint(Vector& scenePoint) const { + if (!m_pPresentationBitmap) + return false; + + Vector bitmapPos = m_Pos + m_BitmapOffset; + if (WithinBox(scenePoint, bitmapPos, m_pPresentationBitmap->w, m_pPresentationBitmap->h)) { + // Scene point on the bitmap + Vector bitmapPoint = scenePoint - bitmapPos; + if (getpixel(m_pPresentationBitmap, bitmapPoint.m_X, bitmapPoint.m_Y) != g_MaskColor) + return true; + } -void BunkerAssembly::SetTeam(int team) -{ - TerrainObject::SetTeam(team); + return false; + } - // Make sure all the objects to be placed will be of the same team - for (std::list::iterator itr = m_PlacedObjects.begin(); itr != m_PlacedObjects.end(); ++itr) - (*itr)->SetTeam(team); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this Actor belongs to. + void BunkerAssembly::SetTeam(int team) { + TerrainObject::SetTeam(team); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this TerrainObject's current graphical representation to a -// BITMAP of choice. - -void BunkerAssembly::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const -{ - if (!m_FGColorBitmap) - RTEAbort("TerrainObject's bitmaps are null when drawing!"); - - // Take care of wrapping situations - Vector aDrawPos[4]; - aDrawPos[0] = m_Pos + m_BitmapOffset - targetPos; - int passes = 1; - - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero() && g_SceneMan.GetSceneWidth() <= pTargetBitmap->w) - { - if (aDrawPos[0].m_X < m_FGColorBitmap->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += pTargetBitmap->w; - passes++; - } - else if (aDrawPos[0].m_X > pTargetBitmap->w - m_FGColorBitmap->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= pTargetBitmap->w; - passes++; - } - } - // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam - else - { - if (g_SceneMan.SceneWrapsX()) - { - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (targetPos.m_X < 0) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= sceneWidth; - passes++; - } - if (targetPos.m_X + pTargetBitmap->w > sceneWidth) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += sceneWidth; - passes++; - } - } - } - - // Draw all the passes needed - for (int i = 0; i < passes; ++i) - { - if (mode == g_DrawColor) - { - masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_BGColorBitmap->w, m_BGColorBitmap->h); - } - else if (mode == g_DrawMaterial) - { - masked_blit(m_MaterialBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_MaterialBitmap->w, m_MaterialBitmap->h); - } - else if (mode == g_DrawTrans) - { - draw_trans_sprite(pTargetBitmap, m_pPresentationBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); - } - } -} + // Make sure all the objects to be placed will be of the same team + for (std::list::iterator itr = m_PlacedObjects.begin(); itr != m_PlacedObjects.end(); ++itr) + (*itr)->SetTeam(team); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this TerrainObject's current graphical representation to a + // BITMAP of choice. + + void BunkerAssembly::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { + if (!m_FGColorBitmap) + RTEAbort("TerrainObject's bitmaps are null when drawing!"); + + // Take care of wrapping situations + Vector aDrawPos[4]; + aDrawPos[0] = m_Pos + m_BitmapOffset - targetPos; + int passes = 1; + + // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap + if (targetPos.IsZero() && g_SceneMan.GetSceneWidth() <= pTargetBitmap->w) { + if (aDrawPos[0].m_X < m_FGColorBitmap->w) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X += pTargetBitmap->w; + passes++; + } else if (aDrawPos[0].m_X > pTargetBitmap->w - m_FGColorBitmap->w) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X -= pTargetBitmap->w; + passes++; + } + } + // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam + else { + if (g_SceneMan.SceneWrapsX()) { + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (targetPos.m_X < 0) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X -= sceneWidth; + passes++; + } + if (targetPos.m_X + pTargetBitmap->w > sceneWidth) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X += sceneWidth; + passes++; + } + } + } + + // Draw all the passes needed + for (int i = 0; i < passes; ++i) { + if (mode == g_DrawColor) { + masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_BGColorBitmap->w, m_BGColorBitmap->h); + } else if (mode == g_DrawMaterial) { + masked_blit(m_MaterialBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_MaterialBitmap->w, m_MaterialBitmap->h); + } else if (mode == g_DrawTrans) { + draw_trans_sprite(pTargetBitmap, m_pPresentationBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); + } + } + } } // namespace RTE diff --git a/Source/Entities/BunkerAssembly.h b/Source/Entities/BunkerAssembly.h index ffa49fadb7..36852545c4 100644 --- a/Source/Entities/BunkerAssembly.h +++ b/Source/Entities/BunkerAssembly.h @@ -10,270 +10,245 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "TerrainObject.h" -namespace RTE -{ - -class BunkerAssemblyScheme; -class Deployment; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: BunkerAssembly -////////////////////////////////////////////////////////////////////////////////////////// -// Description: An assembly of a few terrain objects. -// material layer and optional background layer. -// Parent(s): SceneObject. -// Class history: 08/23/2002 BunkerAssembly created. - -class BunkerAssembly : public TerrainObject { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - -// Concrete allocation and cloning definitions -EntityAllocation(BunkerAssembly); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: BunkerAssembly -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a BunkerAssembly object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - BunkerAssembly() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~BunkerAssembly -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a BunkerAssembly object before deletion -// from system memory. -// Arguments: None. - - ~BunkerAssembly() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BunkerAssembly object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BunkerAssembly object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(BunkerAssemblyScheme * scheme); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a BunkerAssembly to be identical to another, by deep copy. -// Arguments: A reference to the BunkerAssembly to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const BunkerAssembly &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire BunkerAssembly, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); SceneObject::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the BunkerAssembly object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetParentAssemblySchemeName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// -// Arguments: None. -// Return value: -// - - std::string GetParentAssemblySchemeName() const { return m_ParentAssemblyScheme; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - - bool IsOnScenePoint(Vector &scenePoint) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDeployments -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Retrieves the list of random deployemtns selected to be deployed by this assembly -// based on it's parent scheme MaxDeployments value. This list will always include all -// brain deployments so it can be longer that MaxDeployments. -// Arguments: None. -// Return value: List of deployments. - - std::vector GetDeployments(); +namespace RTE { + class BunkerAssemblyScheme; + class Deployment; ////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this belongs to. -// Arguments: The assigned team number. -// Return value: None. - - void SetTeam(int team) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlacedObjects -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the list of SceneObject:s which are placed in this assembly on loading. -// Arguments: None. -// Return value: The list of of placed objects. Ownership is NOT transferred! - - const std::list * GetPlacedObjects() const { return &m_PlacedObjects; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds placed object to the internallist of placed objects for this assembly, -// applies it's image to presentation bitmap and sets assembly price accordingly. -// Added scene object MUST have coordinates relative to this assembly. -// Arguments: Object to add. -// Return value: None. - - void AddPlacedObject(SceneObject * pSO); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGraphicalIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a bitmap showing a good identifyable icon of this, for use in -// GUI lists etc. -// Arguments: None. -// Return value: A good identifyable graphical representation of this in a BITMAP, if -// available. If not, 0 is returned. Ownership is NOT TRANSFERRED! - - BITMAP * GetGraphicalIcon() const override { return m_pPresentationBitmap; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSymmetricAssemblyName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the name of an assembly symmetric to this one. -// Arguments: None. -// Return value: Symmetric assembly name. - - std::string GetSymmetricAssemblyName() const { return m_SymmetricAssembly; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSymmetricAssemblyName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the name of an assembly symmetric to this one. -// Arguments: Symmetric assembly name. -// Return value: None. - - void SetSymmetricAssemblyName(std::string newSymmetricAssembly) { m_SymmetricAssembly = newSymmetricAssembly; }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this TerrainObject's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// like indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - // SceneObject:s to be placed in the scene, OWNED HERE - std::list m_PlacedObjects; - // Parent bunker assembly scheme - std::string m_ParentAssemblyScheme; - // Group proveded by parent scheme to which this assembly was added - std::string m_ParentSchemeGroup; - // Bitmap shown during draw and icon creation - BITMAP * m_pPresentationBitmap; - //Assembly symmetric to this one - std::string m_SymmetricAssembly; - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this BunkerAssembly, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - BunkerAssembly(const BunkerAssembly &reference) = delete; - void operator=(const BunkerAssembly &rhs) = delete; - -}; + // Class: BunkerAssembly + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: An assembly of a few terrain objects. + // material layer and optional background layer. + // Parent(s): SceneObject. + // Class history: 08/23/2002 BunkerAssembly created. + + class BunkerAssembly : public TerrainObject { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(BunkerAssembly); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: BunkerAssembly + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a BunkerAssembly object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + BunkerAssembly() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~BunkerAssembly + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a BunkerAssembly object before deletion + // from system memory. + // Arguments: None. + + ~BunkerAssembly() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the BunkerAssembly object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the BunkerAssembly object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(BunkerAssemblyScheme* scheme); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a BunkerAssembly to be identical to another, by deep copy. + // Arguments: A reference to the BunkerAssembly to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const BunkerAssembly& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire BunkerAssembly, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + SceneObject::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the BunkerAssembly object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetParentAssemblySchemeName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // + // Arguments: None. + // Return value: + // + + std::string GetParentAssemblySchemeName() const { return m_ParentAssemblyScheme; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsOnScenePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this' current graphical representation overlaps + // a point in absolute scene coordinates. + // Arguments: The point in absolute scene coordinates. + // Return value: Whether this' graphical rep overlaps the scene point. + + bool IsOnScenePoint(Vector& scenePoint) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDeployments + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Retrieves the list of random deployemtns selected to be deployed by this assembly + // based on it's parent scheme MaxDeployments value. This list will always include all + // brain deployments so it can be longer that MaxDeployments. + // Arguments: None. + // Return value: List of deployments. + + std::vector GetDeployments(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this belongs to. + // Arguments: The assigned team number. + // Return value: None. + + void SetTeam(int team) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlacedObjects + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the list of SceneObject:s which are placed in this assembly on loading. + // Arguments: None. + // Return value: The list of of placed objects. Ownership is NOT transferred! + + const std::list* GetPlacedObjects() const { return &m_PlacedObjects; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddPlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds placed object to the internallist of placed objects for this assembly, + // applies it's image to presentation bitmap and sets assembly price accordingly. + // Added scene object MUST have coordinates relative to this assembly. + // Arguments: Object to add. + // Return value: None. + + void AddPlacedObject(SceneObject* pSO); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGraphicalIcon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a bitmap showing a good identifyable icon of this, for use in + // GUI lists etc. + // Arguments: None. + // Return value: A good identifyable graphical representation of this in a BITMAP, if + // available. If not, 0 is returned. Ownership is NOT TRANSFERRED! + + BITMAP* GetGraphicalIcon() const override { return m_pPresentationBitmap; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSymmetricAssemblyName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the name of an assembly symmetric to this one. + // Arguments: None. + // Return value: Symmetric assembly name. + + std::string GetSymmetricAssemblyName() const { return m_SymmetricAssembly; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSymmetricAssemblyName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the name of an assembly symmetric to this one. + // Arguments: Symmetric assembly name. + // Return value: None. + + void SetSymmetricAssemblyName(std::string newSymmetricAssembly) { m_SymmetricAssembly = newSymmetricAssembly; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this TerrainObject's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // like indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + // SceneObject:s to be placed in the scene, OWNED HERE + std::list m_PlacedObjects; + // Parent bunker assembly scheme + std::string m_ParentAssemblyScheme; + // Group proveded by parent scheme to which this assembly was added + std::string m_ParentSchemeGroup; + // Bitmap shown during draw and icon creation + BITMAP* m_pPresentationBitmap; + // Assembly symmetric to this one + std::string m_SymmetricAssembly; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this BunkerAssembly, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + BunkerAssembly(const BunkerAssembly& reference) = delete; + void operator=(const BunkerAssembly& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/BunkerAssemblyScheme.cpp b/Source/Entities/BunkerAssemblyScheme.cpp index 9676269bef..0252f7163b 100644 --- a/Source/Entities/BunkerAssemblyScheme.cpp +++ b/Source/Entities/BunkerAssemblyScheme.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,385 +19,346 @@ namespace RTE { -ConcreteClassInfo(BunkerAssemblyScheme, SceneObject, 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this BunkerAssemblyScheme, effectively -// resetting the members of this abstraction level only. - -void BunkerAssemblyScheme::Clear() -{ - m_pPresentationBitmap = 0; - m_ChildObjects.clear(); - m_BitmapOffset = Vector(0,0); - m_IsOneTypePerScene = false; - m_Limit = 0; - m_MaxDeployments = 1; - m_SymmetricScheme.clear(); - m_AssemblyGroup.clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BunkerAssemblyScheme object ready for use. - -int BunkerAssemblyScheme::Create() -{ - if (SceneObject::Create() < 0) - return -1; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MOPixel to be identical to another, by deep copy. - -int BunkerAssemblyScheme::Create(const BunkerAssemblyScheme &reference) -{ - SceneObject::Create(reference); - - m_pBitmap = reference.m_pBitmap; - m_pPresentationBitmap = reference.m_pPresentationBitmap; - m_pIconBitmap = reference.m_pIconBitmap; - - for (std::list::const_iterator itr = reference.m_ChildObjects.begin(); itr != reference.m_ChildObjects.end(); ++itr) - m_ChildObjects.push_back(*itr); - - m_BitmapOffset = reference.m_BitmapOffset; - - m_IsOneTypePerScene = reference.m_IsOneTypePerScene; - m_Limit = reference.m_Limit; - m_MaxDeployments = reference.m_MaxDeployments; - m_SymmetricScheme = reference.m_SymmetricScheme; - m_AssemblyGroup = reference.m_AssemblyGroup; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int BunkerAssemblyScheme::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return SceneObject::ReadProperty(propName, reader)); - - MatchProperty("BitmapFile", - { - reader >> m_BitmapFile; - m_pBitmap = m_BitmapFile.GetAsBitmap(); - - m_pPresentationBitmap = create_bitmap_ex(8, m_pBitmap->w * ScaleX, m_pBitmap->h * ScaleY); - clear_to_color(m_pPresentationBitmap, g_MaskColor); - - // Create internal presentation bitmap which will be drawn by editor - // Create horizontal outlines - for (int x = 0; x < m_pBitmap->w; ++x) - { - //Top to bottom - for (int y = 0; y < m_pBitmap->h ; ++y) - { - int px = getpixel(m_pBitmap, x, y); - int pxp = getpixel(m_pBitmap, x, y - 1) == -1 ? SCHEME_COLOR_EMPTY : getpixel(m_pBitmap, x, y - 1); - - if (px == SCHEME_COLOR_WALL && pxp != SCHEME_COLOR_WALL) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + w, x * ScaleX + ScaleX - 1, y * ScaleY + w, PAINT_COLOR_WALL); - - if (px == SCHEME_COLOR_PASSABLE && pxp != SCHEME_COLOR_PASSABLE) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + w, x * ScaleX + ScaleX -1, y * ScaleY + w, PAINT_COLOR_PASSABLE); - - if (px == SCHEME_COLOR_VARIABLE && pxp != SCHEME_COLOR_VARIABLE) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + w, x * ScaleX + ScaleX -1, y * ScaleY + w, PAINT_COLOR_VARIABLE); - } - - //Bottom to top - for (int y = m_pBitmap->h - 1; y >= 0 ; --y) - { - int px = getpixel(m_pBitmap, x, y); - int pxp = getpixel(m_pBitmap, x, y + 1) == -1 ? SCHEME_COLOR_EMPTY : getpixel(m_pBitmap, x, y + 1); - - if (px == SCHEME_COLOR_WALL && pxp != SCHEME_COLOR_WALL) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + ScaleX - 1 - w, x * ScaleX + ScaleX - 1, y * ScaleY + ScaleY - 1 - w, PAINT_COLOR_WALL); - - if (px == SCHEME_COLOR_PASSABLE && pxp != SCHEME_COLOR_PASSABLE) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + ScaleX - 1 - w, x * ScaleX + ScaleX - 1, y * ScaleY + ScaleY - 1 - w, PAINT_COLOR_PASSABLE); - - if (px == SCHEME_COLOR_VARIABLE && pxp != SCHEME_COLOR_VARIABLE) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + ScaleX - 1 - w, x * ScaleX + ScaleX - 1, y * ScaleY + ScaleY - 1 - w, PAINT_COLOR_VARIABLE); - } + ConcreteClassInfo(BunkerAssemblyScheme, SceneObject, 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this BunkerAssemblyScheme, effectively + // resetting the members of this abstraction level only. + + void BunkerAssemblyScheme::Clear() { + m_pPresentationBitmap = 0; + m_ChildObjects.clear(); + m_BitmapOffset = Vector(0, 0); + m_IsOneTypePerScene = false; + m_Limit = 0; + m_MaxDeployments = 1; + m_SymmetricScheme.clear(); + m_AssemblyGroup.clear(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the BunkerAssemblyScheme object ready for use. + + int BunkerAssemblyScheme::Create() { + if (SceneObject::Create() < 0) + return -1; + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MOPixel to be identical to another, by deep copy. + + int BunkerAssemblyScheme::Create(const BunkerAssemblyScheme& reference) { + SceneObject::Create(reference); + + m_pBitmap = reference.m_pBitmap; + m_pPresentationBitmap = reference.m_pPresentationBitmap; + m_pIconBitmap = reference.m_pIconBitmap; + + for (std::list::const_iterator itr = reference.m_ChildObjects.begin(); itr != reference.m_ChildObjects.end(); ++itr) + m_ChildObjects.push_back(*itr); + + m_BitmapOffset = reference.m_BitmapOffset; + + m_IsOneTypePerScene = reference.m_IsOneTypePerScene; + m_Limit = reference.m_Limit; + m_MaxDeployments = reference.m_MaxDeployments; + m_SymmetricScheme = reference.m_SymmetricScheme; + m_AssemblyGroup = reference.m_AssemblyGroup; + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int BunkerAssemblyScheme::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return SceneObject::ReadProperty(propName, reader)); + + MatchProperty("BitmapFile", + { + reader >> m_BitmapFile; + m_pBitmap = m_BitmapFile.GetAsBitmap(); + + m_pPresentationBitmap = create_bitmap_ex(8, m_pBitmap->w * ScaleX, m_pBitmap->h * ScaleY); + clear_to_color(m_pPresentationBitmap, g_MaskColor); + + // Create internal presentation bitmap which will be drawn by editor + // Create horizontal outlines + for (int x = 0; x < m_pBitmap->w; ++x) { + // Top to bottom + for (int y = 0; y < m_pBitmap->h; ++y) { + int px = getpixel(m_pBitmap, x, y); + int pxp = getpixel(m_pBitmap, x, y - 1) == -1 ? SCHEME_COLOR_EMPTY : getpixel(m_pBitmap, x, y - 1); + + if (px == SCHEME_COLOR_WALL && pxp != SCHEME_COLOR_WALL) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + w, x * ScaleX + ScaleX - 1, y * ScaleY + w, PAINT_COLOR_WALL); + + if (px == SCHEME_COLOR_PASSABLE && pxp != SCHEME_COLOR_PASSABLE) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + w, x * ScaleX + ScaleX - 1, y * ScaleY + w, PAINT_COLOR_PASSABLE); + + if (px == SCHEME_COLOR_VARIABLE && pxp != SCHEME_COLOR_VARIABLE) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + w, x * ScaleX + ScaleX - 1, y * ScaleY + w, PAINT_COLOR_VARIABLE); + } + + // Bottom to top + for (int y = m_pBitmap->h - 1; y >= 0; --y) { + int px = getpixel(m_pBitmap, x, y); + int pxp = getpixel(m_pBitmap, x, y + 1) == -1 ? SCHEME_COLOR_EMPTY : getpixel(m_pBitmap, x, y + 1); + + if (px == SCHEME_COLOR_WALL && pxp != SCHEME_COLOR_WALL) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + ScaleX - 1 - w, x * ScaleX + ScaleX - 1, y * ScaleY + ScaleY - 1 - w, PAINT_COLOR_WALL); + + if (px == SCHEME_COLOR_PASSABLE && pxp != SCHEME_COLOR_PASSABLE) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + ScaleX - 1 - w, x * ScaleX + ScaleX - 1, y * ScaleY + ScaleY - 1 - w, PAINT_COLOR_PASSABLE); + + if (px == SCHEME_COLOR_VARIABLE && pxp != SCHEME_COLOR_VARIABLE) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX, y * ScaleY + ScaleX - 1 - w, x * ScaleX + ScaleX - 1, y * ScaleY + ScaleY - 1 - w, PAINT_COLOR_VARIABLE); + } + } + + // Create vertical outlines + for (int y = 0; y < m_pBitmap->h; ++y) { + // Left + for (int x = 0; x < m_pBitmap->w; ++x) { + int px = getpixel(m_pBitmap, x, y); + int pxp = getpixel(m_pBitmap, x - 1, y) == -1 ? SCHEME_COLOR_EMPTY : getpixel(m_pBitmap, x - 1, y); + + if (px == SCHEME_COLOR_WALL && pxp != SCHEME_COLOR_WALL) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX + w, y * ScaleY, x * ScaleX + w, y * ScaleY + ScaleY - 1, PAINT_COLOR_WALL); + + if (px == SCHEME_COLOR_PASSABLE && pxp != SCHEME_COLOR_PASSABLE) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX + w, y * ScaleY, x * ScaleX + w, y * ScaleY + ScaleY - 1, PAINT_COLOR_PASSABLE); + + if (px == SCHEME_COLOR_VARIABLE && pxp != SCHEME_COLOR_VARIABLE) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX + w, y * ScaleY, x * ScaleX + w, y * ScaleY + ScaleY - 1, PAINT_COLOR_VARIABLE); + } + + for (int x = m_pBitmap->w - 1; x >= 0; --x) { + int px = getpixel(m_pBitmap, x, y); + int pxp = getpixel(m_pBitmap, x + 1, y) == -1 ? SCHEME_COLOR_EMPTY : getpixel(m_pBitmap, x + 1, y); + + if (px == SCHEME_COLOR_WALL && pxp != SCHEME_COLOR_WALL) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX + ScaleX - 1 - w, y * ScaleY, x * ScaleX + ScaleX - 1 - w, y * ScaleY + ScaleY - 1, PAINT_COLOR_WALL); + + if (px == SCHEME_COLOR_PASSABLE && pxp != SCHEME_COLOR_PASSABLE) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX + ScaleX - 1 - w, y * ScaleY, x * ScaleX + ScaleX - 1 - w, y * ScaleY + ScaleY - 1, PAINT_COLOR_PASSABLE); + + if (px == SCHEME_COLOR_VARIABLE && pxp != SCHEME_COLOR_VARIABLE) + for (int w = 0; w < SchemeWidth; w++) + line(m_pPresentationBitmap, x * ScaleX + ScaleX - 1 - w, y * ScaleY, x * ScaleX + ScaleX - 1 - w, y * ScaleY + ScaleY - 1, PAINT_COLOR_VARIABLE); + } + } + + // Print scheme name + GUIFont* pSmallFont = g_FrameMan.GetSmallFont(); + AllegroBitmap allegroBitmap(m_pPresentationBitmap); + pSmallFont->DrawAligned(&allegroBitmap, 4, 4, m_PresetName, GUIFont::Left); + + // Calculate bitmap offset + int width = m_pBitmap->w / 2; + int height = m_pBitmap->h / 2; + m_BitmapOffset = Vector(-width * ScaleX, -height * ScaleY); + + // Count max deployments if not set + if (m_MaxDeployments == 0) { + m_MaxDeployments = (int)((float)GetArea() / AREA_PER_DEPLOYMENT); + if (m_MaxDeployments == 0) + m_MaxDeployments = 1; + } + + float scale = (float)ICON_WIDTH / (float)m_pPresentationBitmap->w; + + m_pIconBitmap = create_bitmap_ex(8, m_pPresentationBitmap->w * scale, m_pPresentationBitmap->h * scale); + clear_to_color(m_pIconBitmap, g_MaskColor); + + for (int x = 0; x < m_pBitmap->w; ++x) + for (int y = 0; y < m_pBitmap->h; ++y) { + int px = getpixel(m_pBitmap, x, y); + + if (px == SCHEME_COLOR_WALL) + rectfill(m_pIconBitmap, x * ScaleX * scale, y * ScaleY * scale, x * ScaleX * scale + ScaleX - 1, y * ScaleY + ScaleY - 1, PAINT_COLOR_WALL); + else if (px == SCHEME_COLOR_PASSABLE) + rectfill(m_pIconBitmap, x * ScaleX * scale, y * ScaleY * scale, x * ScaleX * scale + ScaleX - 1, y * ScaleY + ScaleY - 1, PAINT_COLOR_PASSABLE); + else if (px == SCHEME_COLOR_VARIABLE) + rectfill(m_pIconBitmap, x * ScaleX * scale, y * ScaleY * scale, x * ScaleX * scale + ScaleX - 1, y * ScaleY + ScaleY - 1, PAINT_COLOR_VARIABLE); + } + }); + MatchProperty("AddChildObject", + { + SOPlacer newChild; + reader >> newChild; + newChild.SetTeam(m_Team); + m_ChildObjects.push_back(newChild); + }); + MatchProperty("Limit", { reader >> m_Limit; }); + MatchProperty("OneTypePerScene", { reader >> m_IsOneTypePerScene; }); + MatchProperty("MaxDeployments", { reader >> m_MaxDeployments; }); + MatchProperty("SymmetricScheme", { reader >> m_SymmetricScheme; }); + MatchProperty("AssemblyGroup", { reader >> m_AssemblyGroup; }); + + EndPropertyList; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this BunkerAssemblyScheme with a Writer for + // later recreation with Create(Reader &reader); + + int BunkerAssemblyScheme::Save(Writer& writer) const { + SceneObject::Save(writer); + + writer.NewProperty("BitmapFile"); + writer << m_BitmapFile; + for (std::list::const_iterator itr = m_ChildObjects.begin(); itr != m_ChildObjects.end(); ++itr) { + writer.NewProperty("AddChildObject"); + writer << (*itr); } - // Create vertical outlines - for (int y = 0; y < m_pBitmap->h; ++y) - { - // Left - for (int x = 0; x < m_pBitmap->w ; ++x) - { - int px = getpixel(m_pBitmap, x, y); - int pxp = getpixel(m_pBitmap, x - 1, y) == -1 ? SCHEME_COLOR_EMPTY : getpixel(m_pBitmap, x - 1, y); - - if (px == SCHEME_COLOR_WALL && pxp != SCHEME_COLOR_WALL) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX + w, y * ScaleY, x * ScaleX + w, y * ScaleY + ScaleY - 1, PAINT_COLOR_WALL); - - if (px == SCHEME_COLOR_PASSABLE && pxp != SCHEME_COLOR_PASSABLE) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX + w, y * ScaleY, x * ScaleX + w, y * ScaleY + ScaleY - 1, PAINT_COLOR_PASSABLE); - - if (px == SCHEME_COLOR_VARIABLE && pxp != SCHEME_COLOR_VARIABLE) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX + w, y * ScaleY, x * ScaleX + w, y * ScaleY + ScaleY - 1, PAINT_COLOR_VARIABLE); - } - - for (int x = m_pBitmap->w - 1; x >= 0 ; --x) - { - int px = getpixel(m_pBitmap, x, y); - int pxp = getpixel(m_pBitmap, x + 1, y) == -1 ? SCHEME_COLOR_EMPTY : getpixel(m_pBitmap, x + 1, y); - - if (px == SCHEME_COLOR_WALL && pxp != SCHEME_COLOR_WALL) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX + ScaleX - 1 - w, y * ScaleY, x * ScaleX + ScaleX - 1 - w, y * ScaleY + ScaleY - 1,PAINT_COLOR_WALL); - - if (px == SCHEME_COLOR_PASSABLE && pxp != SCHEME_COLOR_PASSABLE) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX + ScaleX - 1 - w, y * ScaleY, x * ScaleX + ScaleX - 1 - w, y * ScaleY + ScaleY - 1,PAINT_COLOR_PASSABLE); - - if (px == SCHEME_COLOR_VARIABLE && pxp != SCHEME_COLOR_VARIABLE) - for (int w = 0; w < SchemeWidth; w++) - line(m_pPresentationBitmap, x * ScaleX + ScaleX - 1 - w, y * ScaleY, x * ScaleX + ScaleX - 1 - w, y * ScaleY + ScaleY - 1,PAINT_COLOR_VARIABLE); - } + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the BunkerAssemblyScheme object. + + void BunkerAssemblyScheme::Destroy(bool notInherited) { + // Probably no need to delete those, as bitmaps are only created when preset is read from file + // and then they just copy pointers in via Clone() + // delete m_pPresentationBitmap; + // m_pPresentationBitmap = 0; + + if (!notInherited) + SceneObject::Destroy(); + Clear(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGraphicalIcon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a bitmap showing a good identifyable icon of this, for use in + // GUI lists etc. + + BITMAP* BunkerAssemblyScheme::GetGraphicalIcon() const { + return m_pIconBitmap; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsOnScenePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this' current graphical representation overlaps + // a point in absolute scene coordinates. + + bool BunkerAssemblyScheme::IsOnScenePoint(Vector& scenePoint) const { + if (!m_pBitmap) + return false; + + Vector bitmapPos = m_Pos + m_BitmapOffset; + if (WithinBox(scenePoint, bitmapPos, m_pPresentationBitmap->w, m_pPresentationBitmap->h)) { + // Scene point on the bitmap + Vector bitmapPoint = scenePoint - bitmapPos; + + int x = bitmapPoint.m_X / ScaleX; + int y = bitmapPoint.m_Y / ScaleY; + + if (getpixel(m_pBitmap, x, y) != SCHEME_COLOR_EMPTY) + return true; } - // Print scheme name - GUIFont *pSmallFont = g_FrameMan.GetSmallFont(); - AllegroBitmap allegroBitmap(m_pPresentationBitmap); - pSmallFont->DrawAligned(&allegroBitmap, 4, 4, m_PresetName, GUIFont::Left); - - // Calculate bitmap offset - int width = m_pBitmap->w / 2; - int height = m_pBitmap->h / 2; - m_BitmapOffset = Vector(-width * ScaleX, -height * ScaleY); - - // Count max deployments if not set - if (m_MaxDeployments == 0) - { - m_MaxDeployments = (int)((float)GetArea() / AREA_PER_DEPLOYMENT); - if (m_MaxDeployments == 0) - m_MaxDeployments = 1; + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this Actor belongs to. + + void BunkerAssemblyScheme::SetTeam(int team) { + SceneObject::SetTeam(team); + + // Make sure all the objects to be placed will be of the same team + for (std::list::iterator itr = m_ChildObjects.begin(); itr != m_ChildObjects.end(); ++itr) + (*itr).SetTeam(team); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this BunkerAssemblyScheme's current graphical representation to a + // BITMAP of choice. + + void BunkerAssemblyScheme::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { + if (!m_pPresentationBitmap) + RTEAbort("BunkerAssemblyScheme's bitmaps are null when drawing!"); + + // Take care of wrapping situations + Vector aDrawPos[4]; + aDrawPos[0] = m_Pos - targetPos + m_BitmapOffset; + int passes = 1; + + // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap + if (targetPos.IsZero() && g_SceneMan.GetSceneWidth() <= pTargetBitmap->w) { + if (aDrawPos[0].m_X < m_pPresentationBitmap->w) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X += pTargetBitmap->w; + passes++; + } else if (aDrawPos[0].m_X > pTargetBitmap->w - m_pPresentationBitmap->w) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X -= pTargetBitmap->w; + passes++; + } } - - float scale = (float)ICON_WIDTH / (float)m_pPresentationBitmap->w; - - m_pIconBitmap = create_bitmap_ex(8, m_pPresentationBitmap->w * scale, m_pPresentationBitmap->h * scale); - clear_to_color(m_pIconBitmap, g_MaskColor); - - for (int x = 0; x < m_pBitmap->w ; ++x) - for (int y = 0; y < m_pBitmap->h; ++y) - { - int px = getpixel(m_pBitmap, x, y); - - if (px == SCHEME_COLOR_WALL) - rectfill(m_pIconBitmap, x * ScaleX * scale, y * ScaleY * scale, x * ScaleX * scale + ScaleX-1, y * ScaleY + ScaleY-1, PAINT_COLOR_WALL); - else if (px == SCHEME_COLOR_PASSABLE) - rectfill(m_pIconBitmap, x * ScaleX * scale, y * ScaleY * scale, x * ScaleX * scale + ScaleX-1, y * ScaleY + ScaleY-1, PAINT_COLOR_PASSABLE); - else if (px == SCHEME_COLOR_VARIABLE) - rectfill(m_pIconBitmap, x * ScaleX * scale, y * ScaleY * scale, x * ScaleX * scale + ScaleX-1, y * ScaleY + ScaleY-1, PAINT_COLOR_VARIABLE); + // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam + else { + if (g_SceneMan.SceneWrapsX()) { + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (targetPos.m_X < 0) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X -= sceneWidth; + passes++; + } + if (targetPos.m_X + pTargetBitmap->w > sceneWidth) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X += sceneWidth; + passes++; + } } - }); - MatchProperty("AddChildObject", - { - SOPlacer newChild; - reader >> newChild; - newChild.SetTeam(m_Team); - m_ChildObjects.push_back(newChild); - }); - MatchProperty("Limit", { reader >> m_Limit; }); - MatchProperty("OneTypePerScene", { reader >> m_IsOneTypePerScene; }); - MatchProperty("MaxDeployments", { reader >> m_MaxDeployments; }); - MatchProperty("SymmetricScheme", { reader >> m_SymmetricScheme; }); - MatchProperty("AssemblyGroup", { reader >> m_AssemblyGroup; }); - - EndPropertyList; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this BunkerAssemblyScheme with a Writer for -// later recreation with Create(Reader &reader); - -int BunkerAssemblyScheme::Save(Writer &writer) const -{ - SceneObject::Save(writer); - - writer.NewProperty("BitmapFile"); - writer << m_BitmapFile; - for (std::list::const_iterator itr = m_ChildObjects.begin(); itr != m_ChildObjects.end(); ++itr) - { - writer.NewProperty("AddChildObject"); - writer << (*itr); - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the BunkerAssemblyScheme object. - -void BunkerAssemblyScheme::Destroy(bool notInherited) -{ - // Probably no need to delete those, as bitmaps are only created when preset is read from file - // and then they just copy pointers in via Clone() - //delete m_pPresentationBitmap; - //m_pPresentationBitmap = 0; - - if (!notInherited) - SceneObject::Destroy(); - Clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGraphicalIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a bitmap showing a good identifyable icon of this, for use in -// GUI lists etc. - -BITMAP * BunkerAssemblyScheme::GetGraphicalIcon() const -{ - return m_pIconBitmap; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. - -bool BunkerAssemblyScheme::IsOnScenePoint(Vector &scenePoint) const -{ - if (!m_pBitmap) - return false; - - Vector bitmapPos = m_Pos + m_BitmapOffset; - if (WithinBox(scenePoint, bitmapPos, m_pPresentationBitmap->w, m_pPresentationBitmap->h)) - { - // Scene point on the bitmap - Vector bitmapPoint = scenePoint - bitmapPos; - - int x = bitmapPoint.m_X / ScaleX; - int y = bitmapPoint.m_Y / ScaleY; - - if (getpixel(m_pBitmap, x, y) != SCHEME_COLOR_EMPTY) - return true; - } - - return false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this Actor belongs to. - -void BunkerAssemblyScheme::SetTeam(int team) -{ - SceneObject::SetTeam(team); - - // Make sure all the objects to be placed will be of the same team - for (std::list::iterator itr = m_ChildObjects.begin(); itr != m_ChildObjects.end(); ++itr) - (*itr).SetTeam(team); -} - + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this BunkerAssemblyScheme's current graphical representation to a -// BITMAP of choice. - -void BunkerAssemblyScheme::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const -{ - if (!m_pPresentationBitmap) - RTEAbort("BunkerAssemblyScheme's bitmaps are null when drawing!"); - - // Take care of wrapping situations - Vector aDrawPos[4]; - aDrawPos[0] = m_Pos - targetPos + m_BitmapOffset; - int passes = 1; - - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero() && g_SceneMan.GetSceneWidth() <= pTargetBitmap->w) - { - if (aDrawPos[0].m_X < m_pPresentationBitmap->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += pTargetBitmap->w; - passes++; - } - else if (aDrawPos[0].m_X > pTargetBitmap->w - m_pPresentationBitmap->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= pTargetBitmap->w; - passes++; - } - } - // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam - else - { - if (g_SceneMan.SceneWrapsX()) - { - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (targetPos.m_X < 0) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= sceneWidth; - passes++; - } - if (targetPos.m_X + pTargetBitmap->w > sceneWidth) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += sceneWidth; - passes++; - } - } - } - - // Draw all the passes needed - for (int i = 0; i < passes; ++i) - { - if (mode == g_DrawColor) - masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pPresentationBitmap->w, m_pPresentationBitmap->h); - else if (mode == g_DrawMaterial) - masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pPresentationBitmap->w, m_pPresentationBitmap->h); - else if (mode == g_DrawTrans) - draw_trans_sprite(pTargetBitmap, m_pPresentationBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); - } -} + // Draw all the passes needed + for (int i = 0; i < passes; ++i) { + if (mode == g_DrawColor) + masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pPresentationBitmap->w, m_pPresentationBitmap->h); + else if (mode == g_DrawMaterial) + masked_blit(m_pPresentationBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), m_pPresentationBitmap->w, m_pPresentationBitmap->h); + else if (mode == g_DrawTrans) + draw_trans_sprite(pTargetBitmap, m_pPresentationBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); + } + } } // namespace RTE diff --git a/Source/Entities/BunkerAssemblyScheme.h b/Source/Entities/BunkerAssemblyScheme.h index 0c08eb22b8..ea3b56ab01 100644 --- a/Source/Entities/BunkerAssemblyScheme.h +++ b/Source/Entities/BunkerAssemblyScheme.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,319 +19,295 @@ #define ICON_WIDTH 69 #define AREA_PER_DEPLOYMENT 64 -namespace RTE -{ - -class ContentFile; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: BunkerAssemblyScheme -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A feature of the terrain, which includes foreground color layer, -// material layer and optional background layer. -// Parent(s): SceneObject. -// Class history: 08/23/2002 BunkerAssemblyScheme created. - -class BunkerAssemblyScheme : public SceneObject { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - // Different scheme properties are encoded on colors of scheme bitmap - enum SchemeColor - { - SCHEME_COLOR_EMPTY = g_MaskColor, // Empty sections, MUST BE ALWAYS EMPTY - SCHEME_COLOR_PASSABLE = 5, // Passable sections, MUST BE ALWAYS PASSBLE, I.E. HAVE ONLY BACKGROUNDS - SCHEME_COLOR_VARIABLE = 4, // May be passable or not. Expect air. - SCHEME_COLOR_WALL = 3 // Always impassable, but may be empty. Expect terrain. - }; - - // Scheme properties, when drawed in game UIs - enum PresentationColor - { - PAINT_COLOR_PASSABLE = 5, - PAINT_COLOR_VARIABLE = 48, - PAINT_COLOR_WALL = 13 +namespace RTE { + + class ContentFile; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: BunkerAssemblyScheme + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A feature of the terrain, which includes foreground color layer, + // material layer and optional background layer. + // Parent(s): SceneObject. + // Class history: 08/23/2002 BunkerAssemblyScheme created. + + class BunkerAssemblyScheme : public SceneObject { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Different scheme properties are encoded on colors of scheme bitmap + enum SchemeColor { + SCHEME_COLOR_EMPTY = g_MaskColor, // Empty sections, MUST BE ALWAYS EMPTY + SCHEME_COLOR_PASSABLE = 5, // Passable sections, MUST BE ALWAYS PASSBLE, I.E. HAVE ONLY BACKGROUNDS + SCHEME_COLOR_VARIABLE = 4, // May be passable or not. Expect air. + SCHEME_COLOR_WALL = 3 // Always impassable, but may be empty. Expect terrain. + }; + + // Scheme properties, when drawed in game UIs + enum PresentationColor { + PAINT_COLOR_PASSABLE = 5, + PAINT_COLOR_VARIABLE = 48, + PAINT_COLOR_WALL = 13 + }; + + const static int ScaleX = 24; + const static int ScaleY = 24; + const static int SchemeWidth = 2; + + // Concrete allocation and cloning definitions + EntityAllocation(BunkerAssemblyScheme); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: BunkerAssemblyScheme + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a BunkerAssemblyScheme object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + BunkerAssemblyScheme() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~BunkerAssemblyScheme + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a BunkerAssemblyScheme object before deletion + // from system memory. + // Arguments: None. + + ~BunkerAssemblyScheme() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the BunkerAssemblyScheme object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a BunkerAssemblyScheme to be identical to another, by deep copy. + // Arguments: A reference to the BunkerAssemblyScheme to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const BunkerAssemblyScheme& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire BunkerAssemblyScheme, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + SceneObject::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the BunkerAssemblyScheme object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBitmapWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the width this' bitmap. + // Arguments: None. + // Return value: Width of bitmap. + + const int GetBitmapWidth() const { return m_pPresentationBitmap->w; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBitmapHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the height this' material bitmap. + // Arguments: None. + // Return value: Height of 'material' bitmap. + + const int GetBitmapHeight() const { return m_pPresentationBitmap->h; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBitmapOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the offset of presentation bitmap + // Arguments: None. + // Return value: Offset of bitmap + + const Vector GetBitmapOffset() const { return m_BitmapOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the BITMAP object that this BunkerAssemblyScheme uses for its fore- + // ground color representation. + // Arguments: None. + // Return value: A pointer to the foreground color BITMAP object. Ownership is not + // transferred. + + BITMAP* GetBitmap() const { return m_pPresentationBitmap; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the area of the structure bitmap. + // Arguments: None. + // Return value: None. + + int GetArea() const { return m_pBitmap->h * m_pBitmap->w; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGraphicalIcon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a bitmap showing a good identifyable icon of this, for use in + // GUI lists etc. + // Arguments: None. + // Return value: A good identifyable graphical representation of this in a BITMAP, if + // available. If not, 0 is returned. Ownership is NOT TRANSFERRED! + + BITMAP* GetGraphicalIcon() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this belongs to. + // Arguments: The assigned team number. + // Return value: None. + + void SetTeam(int team) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsOnScenePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this' current graphical representation overlaps + // a point in absolute scene coordinates. + // Arguments: The point in absolute scene coordinates. + // Return value: Whether this' graphical rep overlaps the scene point. + + bool IsOnScenePoint(Vector& scenePoint) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this BunkerAssemblyScheme's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // like indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOneTypePerScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether sceneman should select just a single assembly for this scheme + // and use it everywhere on the scene. + // Arguments: None. + // Return value: Whether we allowed to use just one type of assembly for this scheme + + bool IsOneTypePerScene() { return m_IsOneTypePerScene; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSymmetricSchemeName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the name of the scheme symmetric to this one. + // Arguments: None. + // Return value: Symmetric scheme name. + + std::string GetSymmetricSchemeName() const { return m_SymmetricScheme; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAssemblyGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the name of group to which assemblies linked with this scheme must be added. + // Arguments: None. + // Return value: Assembly group name. + + std::string GetAssemblyGroup() const { return m_AssemblyGroup; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLimit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the limit of these schemes per scene. 0 - no limit. + // Arguments: None. + // Return value: Scheme limit. + + int GetLimit() { return m_Limit; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMaxDeployments + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the number of deployments this scheme is allowed to place. + // Arguments: None. + // Return value: Deployments limit. + + int GetMaxDeployments() const { return m_MaxDeployments; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + + ContentFile m_BitmapFile; + + // Not owned by this + BITMAP* m_pBitmap; + BITMAP* m_pPresentationBitmap; + BITMAP* m_pIconBitmap; + + // The objects that are placed along with this in the scene + std::list m_ChildObjects; + + // If this is true then sceneman must select a single assembly for this scheme and use it everywhere on the scene + bool m_IsOneTypePerScene; + // How many assemblies can placed on one scene? + int m_Limit; + // Drawable bitmap offset of this bunker assembly + Vector m_BitmapOffset; + // How many deployments should be selected during placement + int m_MaxDeployments; + // Scheme symmetric to this one + std::string m_SymmetricScheme; + // To which group we should add assemblies linked to this scheme + std::string m_AssemblyGroup; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this BunkerAssemblyScheme, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + BunkerAssemblyScheme(const BunkerAssemblyScheme& reference) = delete; + void operator=(const BunkerAssemblyScheme& rhs) = delete; }; - const static int ScaleX = 24; - const static int ScaleY = 24; - const static int SchemeWidth = 2; - -// Concrete allocation and cloning definitions -EntityAllocation(BunkerAssemblyScheme); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: BunkerAssemblyScheme -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a BunkerAssemblyScheme object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - BunkerAssemblyScheme() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~BunkerAssemblyScheme -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a BunkerAssemblyScheme object before deletion -// from system memory. -// Arguments: None. - - ~BunkerAssemblyScheme() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BunkerAssemblyScheme object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a BunkerAssemblyScheme to be identical to another, by deep copy. -// Arguments: A reference to the BunkerAssemblyScheme to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const BunkerAssemblyScheme &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire BunkerAssemblyScheme, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); SceneObject::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the BunkerAssemblyScheme object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBitmapWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the width this' bitmap. -// Arguments: None. -// Return value: Width of bitmap. - - const int GetBitmapWidth() const { return m_pPresentationBitmap->w; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBitmapHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the height this' material bitmap. -// Arguments: None. -// Return value: Height of 'material' bitmap. - - const int GetBitmapHeight() const { return m_pPresentationBitmap->h; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBitmapOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the offset of presentation bitmap -// Arguments: None. -// Return value: Offset of bitmap - - const Vector GetBitmapOffset() const { return m_BitmapOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the BITMAP object that this BunkerAssemblyScheme uses for its fore- -// ground color representation. -// Arguments: None. -// Return value: A pointer to the foreground color BITMAP object. Ownership is not -// transferred. - - BITMAP * GetBitmap() const { return m_pPresentationBitmap; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the area of the structure bitmap. -// Arguments: None. -// Return value: None. - - int GetArea() const { return m_pBitmap->h * m_pBitmap->w; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGraphicalIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a bitmap showing a good identifyable icon of this, for use in -// GUI lists etc. -// Arguments: None. -// Return value: A good identifyable graphical representation of this in a BITMAP, if -// available. If not, 0 is returned. Ownership is NOT TRANSFERRED! - - BITMAP * GetGraphicalIcon() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this belongs to. -// Arguments: The assigned team number. -// Return value: None. - - void SetTeam(int team) override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - - bool IsOnScenePoint(Vector &scenePoint) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this BunkerAssemblyScheme's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// like indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOneTypePerScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether sceneman should select just a single assembly for this scheme -// and use it everywhere on the scene. -// Arguments: None. -// Return value: Whether we allowed to use just one type of assembly for this scheme - - bool IsOneTypePerScene() { return m_IsOneTypePerScene; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSymmetricSchemeName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the name of the scheme symmetric to this one. -// Arguments: None. -// Return value: Symmetric scheme name. - - std::string GetSymmetricSchemeName() const { return m_SymmetricScheme; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAssemblyGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the name of group to which assemblies linked with this scheme must be added. -// Arguments: None. -// Return value: Assembly group name. - - std::string GetAssemblyGroup() const { return m_AssemblyGroup; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the limit of these schemes per scene. 0 - no limit. -// Arguments: None. -// Return value: Scheme limit. - - int GetLimit() { return m_Limit; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaxDeployments -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the number of deployments this scheme is allowed to place. -// Arguments: None. -// Return value: Deployments limit. - - int GetMaxDeployments() const { return m_MaxDeployments; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - - ContentFile m_BitmapFile; - - // Not owned by this - BITMAP *m_pBitmap; - BITMAP *m_pPresentationBitmap; - BITMAP *m_pIconBitmap; - - // The objects that are placed along with this in the scene - std::list m_ChildObjects; - - // If this is true then sceneman must select a single assembly for this scheme and use it everywhere on the scene - bool m_IsOneTypePerScene; - // How many assemblies can placed on one scene? - int m_Limit; - // Drawable bitmap offset of this bunker assembly - Vector m_BitmapOffset; - // How many deployments should be selected during placement - int m_MaxDeployments; - // Scheme symmetric to this one - std::string m_SymmetricScheme; - // To which group we should add assemblies linked to this scheme - std::string m_AssemblyGroup; - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this BunkerAssemblyScheme, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - BunkerAssemblyScheme(const BunkerAssemblyScheme &reference) = delete; - void operator=(const BunkerAssemblyScheme &rhs) = delete; - -}; - } // namespace RTE #endif // File \ No newline at end of file diff --git a/Source/Entities/Deployment.cpp b/Source/Entities/Deployment.cpp index 5a3d7c0ec0..a9710c3fb0 100644 --- a/Source/Entities/Deployment.cpp +++ b/Source/Entities/Deployment.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -24,785 +23,692 @@ namespace RTE { -ConcreteClassInfo(Deployment, SceneObject, 0); - + ConcreteClassInfo(Deployment, SceneObject, 0); -std::vector Deployment::m_apArrowLeftBitmap; -std::vector Deployment::m_apArrowRightBitmap; + std::vector Deployment::m_apArrowLeftBitmap; + std::vector Deployment::m_apArrowRightBitmap; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Deployment, effectively + // resetting the members of this abstraction level only. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Deployment, effectively -// resetting the members of this abstraction level only. - -void Deployment::Clear() -{ - m_LoadoutName = "Default"; - m_Icon.Reset(); - m_SpawnRadius = 40; - m_WalkRadius = 250; - m_ID = 0; - m_HFlipped = false; -} + void Deployment::Clear() { + m_LoadoutName = "Default"; + m_Icon.Reset(); + m_SpawnRadius = 40; + m_WalkRadius = 250; + m_ID = 0; + m_HFlipped = false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Deployment object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Deployment object ready for use. + int Deployment::Create() { + if (SceneObject::Create() < 0) + return -1; -int Deployment::Create() -{ - if (SceneObject::Create() < 0) - return -1; + if (m_apArrowLeftBitmap.empty()) { + ContentFile("Base.rte/GUIs/DeploymentIcons/ArrowLeft.png").GetAsAnimation(m_apArrowLeftBitmap, 1); + } + if (m_apArrowRightBitmap.empty()) { + ContentFile("Base.rte/GUIs/DeploymentIcons/ArrowRight.png").GetAsAnimation(m_apArrowRightBitmap, 1); + } - if (m_apArrowLeftBitmap.empty()) - { - ContentFile("Base.rte/GUIs/DeploymentIcons/ArrowLeft.png").GetAsAnimation(m_apArrowLeftBitmap, 1); + return 0; } - if (m_apArrowRightBitmap.empty()) - { - ContentFile("Base.rte/GUIs/DeploymentIcons/ArrowRight.png").GetAsAnimation(m_apArrowRightBitmap, 1); - } - - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Deployment object ready for use. + + int Deployment::Create(std::string loadoutName, const Icon& icon, float spawnRadius) { + m_LoadoutName = loadoutName; + m_Icon = icon; + m_SpawnRadius = spawnRadius; + m_WalkRadius = 250; + m_ID = 0; + m_HFlipped = false; + + if (m_apArrowLeftBitmap.empty()) { + ContentFile("Base.rte/GUIs/DeploymentIcons/ArrowLeft.png").GetAsAnimation(m_apArrowLeftBitmap, 1); + } + if (m_apArrowRightBitmap.empty()) { + ContentFile("Base.rte/GUIs/DeploymentIcons/ArrowRight.png").GetAsAnimation(m_apArrowRightBitmap, 1); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Deployment object ready for use. - -int Deployment::Create(std::string loadoutName, const Icon &icon, float spawnRadius) -{ - m_LoadoutName = loadoutName; - m_Icon = icon; - m_SpawnRadius = spawnRadius; - m_WalkRadius = 250; - m_ID = 0; - m_HFlipped = false; - - if (m_apArrowLeftBitmap.empty()) - { - ContentFile("Base.rte/GUIs/DeploymentIcons/ArrowLeft.png").GetAsAnimation(m_apArrowLeftBitmap, 1); + return 0; } - if (m_apArrowRightBitmap.empty()) - { - ContentFile("Base.rte/GUIs/DeploymentIcons/ArrowRight.png").GetAsAnimation(m_apArrowRightBitmap, 1); - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MOPixel to be identical to another, by deep copy. - -int Deployment::Create(const Deployment &reference) -{ - SceneObject::Create(reference); - - m_LoadoutName = reference.m_LoadoutName; - m_Icon = reference.m_Icon; - m_SpawnRadius = reference.m_SpawnRadius; - m_WalkRadius = reference.m_WalkRadius; - m_ID = reference.m_ID; - m_HFlipped = reference.m_HFlipped; - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MOPixel to be identical to another, by deep copy. + int Deployment::Create(const Deployment& reference) { + SceneObject::Create(reference); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int Deployment::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return SceneObject::ReadProperty(propName, reader)); - - MatchProperty("LoadoutName", { reader >> m_LoadoutName; }); - MatchProperty("Icon", { reader >> m_Icon; }); - MatchProperty("SpawnRadius", { reader >> m_SpawnRadius; }); - MatchProperty("WalkRadius", { reader >> m_WalkRadius; }); - MatchProperty("ID", { reader >> m_ID; }); - MatchProperty("HFlipped", { reader >> m_HFlipped; }); - - EndPropertyList; -} + m_LoadoutName = reference.m_LoadoutName; + m_Icon = reference.m_Icon; + m_SpawnRadius = reference.m_SpawnRadius; + m_WalkRadius = reference.m_WalkRadius; + m_ID = reference.m_ID; + m_HFlipped = reference.m_HFlipped; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Deployment with a Writer for -// later recreation with Create(Reader &reader); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int Deployment::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return SceneObject::ReadProperty(propName, reader)); + + MatchProperty("LoadoutName", { reader >> m_LoadoutName; }); + MatchProperty("Icon", { reader >> m_Icon; }); + MatchProperty("SpawnRadius", { reader >> m_SpawnRadius; }); + MatchProperty("WalkRadius", { reader >> m_WalkRadius; }); + MatchProperty("ID", { reader >> m_ID; }); + MatchProperty("HFlipped", { reader >> m_HFlipped; }); + + EndPropertyList; + } -int Deployment::Save(Writer &writer) const -{ - SceneObject::Save(writer); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this Deployment with a Writer for + // later recreation with Create(Reader &reader); + + int Deployment::Save(Writer& writer) const { + SceneObject::Save(writer); + + writer.NewProperty("LoadoutName"); + writer << m_LoadoutName; + writer.NewProperty("Icon"); + writer << m_Icon; + writer.NewProperty("SpawnRadius"); + writer << m_SpawnRadius; + writer.NewProperty("WalkRadius"); + writer << m_WalkRadius; + writer.NewProperty("HFlipped"); + writer << m_HFlipped; + + return 0; + } - writer.NewProperty("LoadoutName"); - writer << m_LoadoutName; - writer.NewProperty("Icon"); - writer << m_Icon; - writer.NewProperty("SpawnRadius"); - writer << m_SpawnRadius; - writer.NewProperty("WalkRadius"); - writer << m_WalkRadius; - writer.NewProperty("HFlipped"); - writer << m_HFlipped; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the Deployment object. - return 0; -} + void Deployment::Destroy(bool notInherited) { + if (!notInherited) + SceneObject::Destroy(); + Clear(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Deployment object. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateDeployedActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the Actor that this Deployment dictates should + // spawn here. Ownership IS transferred!! All items of the Loadout of + // this Deployment will be added to the Actor's inventory as well (and + // also owned by it) + + Actor* Deployment::CreateDeployedActor() { + float cost; + return CreateDeployedActor(-1, cost); + } -void Deployment::Destroy(bool notInherited) -{ - + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateDeployedActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the Actor that this Deployment dictates should + // spawn here. Ownership IS transferred!! All items of the Loadout of + // this Deployment will be added to the Actor's inventory as well (and + // also owned by it) + + Actor* Deployment::CreateDeployedActor(int player, float& costTally) { + // The Actor instance we return and pass ownership of + Actor* pReturnActor = 0; + + // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn + int nativeModule = 0; + float foreignCostMult = 1.0; + float nativeCostMult = 1.0; + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + // Put + if (g_MetaMan.GameInProgress() && pMetaPlayer) { + nativeModule = pMetaPlayer->GetNativeTechModule(); + foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); + nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); + // Also set the team of this Deployable to match the player's + m_Team = pMetaPlayer->GetTeam(); + } else { + GameActivity* activity = dynamic_cast(g_ActivityMan.GetActivity()); + if (activity) { + // Also set the team of this Deployable to match the player's + // m_Team = activity->GetTeamOfPlayer(player); + nativeModule = g_PresetMan.GetModuleID(activity->GetTeamTech(m_Team)); + // Select some random module if player selected all or something else + if (nativeModule < 0) { + std::vector moduleList; + + for (int moduleID = 0; moduleID < g_PresetMan.GetTotalModuleCount(); ++moduleID) { + if (const DataModule* dataModule = g_PresetMan.GetDataModule(moduleID)) { + if (dataModule->IsFaction()) { + moduleList.emplace_back(dataModule->GetFileName()); + } + } + } + int selection = RandomNum(1, moduleList.size() - 1); + nativeModule = g_PresetMan.GetModuleID(moduleList.at(selection)); + } + foreignCostMult = 1.0; + nativeCostMult = 1.0; + } + } - if (!notInherited) - SceneObject::Destroy(); - Clear(); -} + // Find the Loadout that this Deployment is referring to + if (const Loadout* pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", m_LoadoutName, nativeModule))) { + if (std::unique_ptr rawLoadoutActor = std::unique_ptr(pLoadout->CreateFirstActor(nativeModule, foreignCostMult, nativeCostMult, costTally))) { + rawLoadoutActor->SetPos(m_Pos); + rawLoadoutActor->SetTeam(m_Team); + rawLoadoutActor->SetHFlipped(m_HFlipped); + rawLoadoutActor->SetControllerMode(Controller::CIM_AI); + rawLoadoutActor->SetAIMode(Actor::AIMODE_SENTRY); + rawLoadoutActor->SetDeploymentID(m_ID); + pReturnActor = dynamic_cast(rawLoadoutActor->Clone()); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateDeployedActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the Actor that this Deployment dictates should -// spawn here. Ownership IS transferred!! All items of the Loadout of -// this Deployment will be added to the Actor's inventory as well (and -// also owned by it) + // PASSING OWNERSHIP + return pReturnActor; + } -Actor * Deployment::CreateDeployedActor() -{ - float cost; - return CreateDeployedActor(-1, cost); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateDeployedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the first Device that Deployment dictates should + // spawn here. Ownership IS transferred!! Only the first Device is created. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateDeployedActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the Actor that this Deployment dictates should -// spawn here. Ownership IS transferred!! All items of the Loadout of -// this Deployment will be added to the Actor's inventory as well (and -// also owned by it) - -Actor * Deployment::CreateDeployedActor(int player, float &costTally) -{ - // The Actor instance we return and pass ownership of - Actor *pReturnActor = 0; - - // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn - int nativeModule = 0; - float foreignCostMult = 1.0; - float nativeCostMult = 1.0; - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - // Put - if (g_MetaMan.GameInProgress() && pMetaPlayer) - { - nativeModule = pMetaPlayer->GetNativeTechModule(); - foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); - nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); - // Also set the team of this Deployable to match the player's - m_Team = pMetaPlayer->GetTeam(); + SceneObject* Deployment::CreateDeployedObject() { + float cost; + return CreateDeployedObject(-1, cost); } - else - { - GameActivity * activity = dynamic_cast(g_ActivityMan.GetActivity()); - if (activity) - { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateDeployedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the first Device that Deployment dictates should + // spawn here. Ownership IS transferred!! Only the first Device is created. + + SceneObject* Deployment::CreateDeployedObject(int player, float& costTally) { + // The Actor instance we return and pass ownership of + SceneObject* pReturnObject = 0; + + // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn + int nativeModule = 0; + float foreignCostMult = 1.0; + float nativeCostMult = 1.0; + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + if (g_MetaMan.GameInProgress() && pMetaPlayer) { + nativeModule = pMetaPlayer->GetNativeTechModule(); + foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); + nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); // Also set the team of this Deployable to match the player's - //m_Team = activity->GetTeamOfPlayer(player); - nativeModule = g_PresetMan.GetModuleID(activity->GetTeamTech(m_Team)); - // Select some random module if player selected all or something else - if (nativeModule < 0) { - std::vector moduleList; - - for (int moduleID = 0; moduleID < g_PresetMan.GetTotalModuleCount(); ++moduleID) { - if (const DataModule *dataModule = g_PresetMan.GetDataModule(moduleID)) { - if (dataModule->IsFaction()) { moduleList.emplace_back(dataModule->GetFileName()); } + m_Team = pMetaPlayer->GetTeam(); + } else { + GameActivity* activity = dynamic_cast(g_ActivityMan.GetActivity()); + if (activity) { + // Also set the team of this Deployable to match the player's + // m_Team = activity->GetTeamOfPlayer(player); + nativeModule = g_PresetMan.GetModuleID(activity->GetTeamTech(m_Team)); + // Select some random module if player selected all or something else + if (nativeModule < 0) { + std::vector moduleList; + + for (int moduleID = 0; moduleID < g_PresetMan.GetTotalModuleCount(); ++moduleID) { + if (const DataModule* dataModule = g_PresetMan.GetDataModule(moduleID)) { + if (dataModule->IsFaction()) { + moduleList.emplace_back(dataModule->GetFileName()); + } + } } + int selection = RandomNum(1, moduleList.size() - 1); + nativeModule = g_PresetMan.GetModuleID(moduleList.at(selection)); } - int selection = RandomNum(1, moduleList.size() - 1); - nativeModule = g_PresetMan.GetModuleID(moduleList.at(selection)); + foreignCostMult = 1.0; + nativeCostMult = 1.0; } - foreignCostMult = 1.0; - nativeCostMult = 1.0; } - } - - // Find the Loadout that this Deployment is referring to - if (const Loadout *pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", m_LoadoutName, nativeModule))) { - if (std::unique_ptr rawLoadoutActor = std::unique_ptr(pLoadout->CreateFirstActor(nativeModule, foreignCostMult, nativeCostMult, costTally))) { - rawLoadoutActor->SetPos(m_Pos); - rawLoadoutActor->SetTeam(m_Team); - rawLoadoutActor->SetHFlipped(m_HFlipped); - rawLoadoutActor->SetControllerMode(Controller::CIM_AI); - rawLoadoutActor->SetAIMode(Actor::AIMODE_SENTRY); - rawLoadoutActor->SetDeploymentID(m_ID); - pReturnActor = dynamic_cast(rawLoadoutActor->Clone()); - } - } - - // PASSING OWNERSHIP - return pReturnActor; -} + // Find the Loadout that this Deployment is referring to + const Loadout* pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", m_LoadoutName, nativeModule)); + if (pLoadout) { + // Get the first object in the Loadout + pReturnObject = pLoadout->CreateFirstDevice(nativeModule, foreignCostMult, nativeCostMult, costTally); + // Set the position and team etc for the Actor we are prepping to spawn + pReturnObject->SetPos(m_Pos); + pReturnObject->SetHFlipped(m_HFlipped); + pReturnObject->SetTeam(m_Team); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateDeployedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the first Device that Deployment dictates should -// spawn here. Ownership IS transferred!! Only the first Device is created. - -SceneObject * Deployment::CreateDeployedObject() -{ - float cost; - return CreateDeployedObject(-1, cost); -} + // PASSING OWNERSHIP + return pReturnObject; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DeploymentBlocked + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tests whether the Object this is supposed to spawn/deploy is blocked + // by an already exiting object in the a list being positioned within the + // spawn radius of this. + + bool Deployment::DeploymentBlocked(int player, const std::list& existingObjects) { + bool blocked = false; + + // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn + int nativeModule = 0; + float foreignCostMult = 1.0; + float nativeCostMult = 1.0; + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + if (g_MetaMan.GameInProgress() && pMetaPlayer) { + nativeModule = pMetaPlayer->GetNativeTechModule(); + foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); + nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateDeployedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the first Device that Deployment dictates should -// spawn here. Ownership IS transferred!! Only the first Device is created. - -SceneObject * Deployment::CreateDeployedObject(int player, float &costTally) -{ - // The Actor instance we return and pass ownership of - SceneObject *pReturnObject = 0; - - // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn - int nativeModule = 0; - float foreignCostMult = 1.0; - float nativeCostMult = 1.0; - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - if (g_MetaMan.GameInProgress() && pMetaPlayer) - { - nativeModule = pMetaPlayer->GetNativeTechModule(); - foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); - nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); - // Also set the team of this Deployable to match the player's - m_Team = pMetaPlayer->GetTeam(); - } else { - GameActivity * activity = dynamic_cast(g_ActivityMan.GetActivity()); - if (activity) - { - // Also set the team of this Deployable to match the player's - //m_Team = activity->GetTeamOfPlayer(player); - nativeModule = g_PresetMan.GetModuleID(activity->GetTeamTech(m_Team)); - // Select some random module if player selected all or something else - if (nativeModule < 0) { - std::vector moduleList; - - for (int moduleID = 0; moduleID < g_PresetMan.GetTotalModuleCount(); ++moduleID) { - if (const DataModule *dataModule = g_PresetMan.GetDataModule(moduleID)) { - if (dataModule->IsFaction()) { moduleList.emplace_back(dataModule->GetFileName()); } + // First try to find an object via ID's, objects with ID can be far enough + // Go through all already-placed things in the Scene to see if there's anything with matching spawn ID + if (m_ID) { + for (std::list::const_iterator existingItr = existingObjects.begin(); existingItr != existingObjects.end(); ++existingItr) { + Actor* pActor = dynamic_cast(*existingItr); + if (pActor && pActor->GetDeploymentID() == m_ID) { + // Do ghetto distance calc between the thing we want to place and the similar thing we found already placed + // Note this doesn't take into account Scene wrapping, which is problematic when the Scene might not be loaded.. it's okay in this case though + Vector distance = (*existingItr)->GetPos() - m_Pos; + // If the same thing is within the spawn radius, then signal that this Deployment location is indeed BLOCKED + if (distance.MagnitudeIsLessThan(m_WalkRadius)) { + blocked = true; + break; } } - int selection = RandomNum(1, moduleList.size() - 1); - nativeModule = g_PresetMan.GetModuleID(moduleList.at(selection)); } - foreignCostMult = 1.0; - nativeCostMult = 1.0; } - } + // No need to do expensive search if it's already blocked + if (blocked) + return blocked; + + // The object we would spawn by this Deployment, IF it's not blocked by somehting already + const SceneObject* pSpawnObject = 0; + + // Find the Loadout that this Deployment is referring to + Loadout* pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", m_LoadoutName, nativeModule)->Clone()); + if (pLoadout) { + // Now go through the Loadout list of items and tally the cost of all devices that would go into inventory of the first Actor found in the list + const std::list* pMOList = pLoadout->GetCargoList(); + if (pMOList && !pMOList->empty()) { + // Go through the list of things ordered, and give any actors all the items that is present after them, + // until the next actor. Also, the first actor gets all stuff in the list above him. + const MovableObject* pInventoryObject = 0; + const Actor* pActor = 0; + for (std::list::const_iterator itr = pMOList->begin(); itr != pMOList->end(); ++itr) { + // Save pointer of the preset in the list + pInventoryObject = dynamic_cast(*itr); + // See if it's actually a passenger, as opposed to a regular item + pActor = dynamic_cast(pInventoryObject); + // If it's an actor, then that's the guy which would be spawned, so use him to check agianst blockage + if (pActor) { + pSpawnObject = pActor; + // We're done looking for the object that would be spawned + break; + } + // If not an Actor, then skip because we're still looking for one + } + pActor = 0; - // Find the Loadout that this Deployment is referring to - const Loadout *pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", m_LoadoutName, nativeModule)); - if (pLoadout) - { - // Get the first object in the Loadout - pReturnObject = pLoadout->CreateFirstDevice(nativeModule, foreignCostMult, nativeCostMult, costTally); - // Set the position and team etc for the Actor we are prepping to spawn - pReturnObject->SetPos(m_Pos); - pReturnObject->SetHFlipped(m_HFlipped); - pReturnObject->SetTeam(m_Team); - } + // If no Actor was found, then see if we can use the delivery Craft instead + if (!pSpawnObject && pLoadout->GetDeliveryCraft()) { + // The craft is now the Actor we are looking at spawning + pSpawnObject = pLoadout->GetDeliveryCraft(); + } - // PASSING OWNERSHIP - return pReturnObject; -} + // If there's no Actor in this Deployment's Loadout at all, then we should just count the first Item or device in the Loadout + if (!pSpawnObject) { + // Find the first non-actor + for (std::list::const_iterator itr = pMOList->begin(); itr != pMOList->end(); ++itr) { + // If not an Actor, then we should count it and then stop + if (!dynamic_cast(*itr)) { + pSpawnObject = *itr; + // We're done finding the spawning object + break; + } + } + } + } + // Delete the Loadout instance we have + delete pLoadout; + pLoadout = 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DeploymentBlocked -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tests whether the Object this is supposed to spawn/deploy is blocked -// by an already exiting object in the a list being positioned within the -// spawn radius of this. - -bool Deployment::DeploymentBlocked(int player, const std::list &existingObjects) -{ - bool blocked = false; - - // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn - int nativeModule = 0; - float foreignCostMult = 1.0; - float nativeCostMult = 1.0; - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - if (g_MetaMan.GameInProgress() && pMetaPlayer) - { - nativeModule = pMetaPlayer->GetNativeTechModule(); - foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); - nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); - } - - // First try to find an object via ID's, objects with ID can be far enough - // Go through all already-placed things in the Scene to see if there's anything with matching spawn ID - if (m_ID) - { - for (std::list::const_iterator existingItr = existingObjects.begin(); existingItr != existingObjects.end(); ++existingItr) - { - Actor *pActor = dynamic_cast(*existingItr); - if (pActor && pActor->GetDeploymentID() == m_ID) - { - // Do ghetto distance calc between the thing we want to place and the similar thing we found already placed - // Note this doesn't take into account Scene wrapping, which is problematic when the Scene might not be loaded.. it's okay in this case though - Vector distance = (*existingItr)->GetPos() - m_Pos; - // If the same thing is within the spawn radius, then signal that this Deployment location is indeed BLOCKED - if (distance.MagnitudeIsLessThan(m_WalkRadius)) - { - blocked = true; - break; + // Now check for whether the object that is going to be spawned is blocked by anyhting sufficently similar positioned within the spawn radius + if (pSpawnObject) { + // Go through all already-placed things in the Scene to see if there's anything similar/same + for (std::list::const_iterator existingItr = existingObjects.begin(); existingItr != existingObjects.end(); ++existingItr) { + if (((*existingItr)->GetClassName() == pSpawnObject->GetClassName()) && ((*existingItr)->GetPresetName() == pSpawnObject->GetPresetName())) { + // Do ghetto distance calc between the thing we want to place and the similar thing we found already placed + // Note this doesn't take into account Scene wrapping, which is problematic when the Scene might not be loaded.. it's okay in this case though + Vector distance = (*existingItr)->GetPos() - m_Pos; + // If the same thing is within the spawn radius, then signal that this Deployment location is indeed BLOCKED + if (distance.MagnitudeIsLessThan(m_SpawnRadius)) { + blocked = true; + break; + } } } } - } - // No need to do expensive search if it's already blocked - if (blocked) return blocked; + } - // The object we would spawn by this Deployment, IF it's not blocked by somehting already - const SceneObject *pSpawnObject = 0; - - // Find the Loadout that this Deployment is referring to - Loadout *pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", m_LoadoutName, nativeModule)->Clone()); - if (pLoadout) - { - // Now go through the Loadout list of items and tally the cost of all devices that would go into inventory of the first Actor found in the list - const std::list *pMOList = pLoadout->GetCargoList(); - if (pMOList && !pMOList->empty()) - { - // Go through the list of things ordered, and give any actors all the items that is present after them, - // until the next actor. Also, the first actor gets all stuff in the list above him. - const MovableObject *pInventoryObject = 0; - const Actor *pActor = 0; - for (std::list::const_iterator itr = pMOList->begin(); itr != pMOList->end(); ++itr) - { - // Save pointer of the preset in the list - pInventoryObject = dynamic_cast(*itr); - // See if it's actually a passenger, as opposed to a regular item - pActor = dynamic_cast(pInventoryObject); - // If it's an actor, then that's the guy which would be spawned, so use him to check agianst blockage - if (pActor) - { - pSpawnObject = pActor; - // We're done looking for the object that would be spawned - break; - } - // If not an Actor, then skip because we're still looking for one - } - pActor = 0; - - // If no Actor was found, then see if we can use the delivery Craft instead - if (!pSpawnObject && pLoadout->GetDeliveryCraft()) - { - // The craft is now the Actor we are looking at spawning - pSpawnObject = pLoadout->GetDeliveryCraft(); - } - - // If there's no Actor in this Deployment's Loadout at all, then we should just count the first Item or device in the Loadout - if (!pSpawnObject) - { - // Find the first non-actor - for (std::list::const_iterator itr = pMOList->begin(); itr != pMOList->end(); ++itr) - { - // If not an Actor, then we should count it and then stop - if (!dynamic_cast(*itr)) - { - pSpawnObject = *itr; - // We're done finding the spawning object - break; - } - } - } - } - - // Delete the Loadout instance we have - delete pLoadout; - pLoadout = 0; - } - - // Now check for whether the object that is going to be spawned is blocked by anyhting sufficently similar positioned within the spawn radius - if (pSpawnObject) - { - // Go through all already-placed things in the Scene to see if there's anything similar/same - for (std::list::const_iterator existingItr = existingObjects.begin(); existingItr != existingObjects.end(); ++existingItr) - { - if (((*existingItr)->GetClassName() == pSpawnObject->GetClassName()) && ((*existingItr)->GetPresetName() == pSpawnObject->GetPresetName())) - { - // Do ghetto distance calc between the thing we want to place and the similar thing we found already placed - // Note this doesn't take into account Scene wrapping, which is problematic when the Scene might not be loaded.. it's okay in this case though - Vector distance = (*existingItr)->GetPos() - m_Pos; - // If the same thing is within the spawn radius, then signal that this Deployment location is indeed BLOCKED - if (distance.MagnitudeIsLessThan(m_SpawnRadius)) - { - blocked = true; - break; - } - } - } - } - - return blocked; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of a spawn of this, including + // everything carried by it. + + float Deployment::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const { + float totalValue = 0; + const Actor* pFirstActor = 0; + + // Find the Loadout that this Deployment is referring to + Loadout* pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", m_LoadoutName, nativeModule)->Clone()); + if (pLoadout) { + // Now go through the Loadout list of items and tally the cost ofall devices that would go into inventory of the first Actor found in the list + const std::list* pMOList = pLoadout->GetCargoList(); + if (pMOList && !pMOList->empty()) { + // Go through the list of things ordered, and give any actors all the items that is present after them, + // until the next actor. Also, the first actor gets all stuff in the list above him. + const MovableObject* pInventoryObject = 0; + const Actor* pActor = 0; + std::list cargoItems; + for (std::list::const_iterator itr = pMOList->begin(); itr != pMOList->end(); ++itr) { + // Save pointer of the preset in the list + pInventoryObject = dynamic_cast(*itr); + // See if it's actually a passenger, as opposed to a regular item + pActor = dynamic_cast(pInventoryObject); + // If it's an actor, then that's the guy which will be spawned + if (pActor) { + // Add to the total cost tally + totalValue += (*itr)->GetGoldValue(nativeModule, foreignMult, nativeMult); + // If this is the first passenger, then give him all the shit found in the list before him + if (!pFirstActor) { + for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) + totalValue += (*iItr)->GetTotalValue(nativeModule, foreignMult); + } + // This isn't the first passenger, so give the previous guy all the stuff that was found since processing him + else { + for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) + totalValue += (*iItr)->GetTotalValue(nativeModule, foreignMult); + + // Also stop going through the list; we only need to count the value of ONE actor and his stuff + break; + } + // Clear out the temporary cargo list since we've assigned all the stuff in it to the return Actor + cargoItems.clear(); + + // Now set the current Actor as the one we found first + pFirstActor = pActor; + } + // If not an Actor, then add it to the temp list of items which will be added to the last passenger's inventory + else + cargoItems.push_back(pInventoryObject); + } + pActor = 0; + + // If there was a last passenger and only things after him, count the value of all the items in his inventory + if (pFirstActor) { + // Passing ownership + for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) + totalValue += (*iItr)->GetTotalValue(nativeModule, foreignMult); + cargoItems.clear(); + } + // If there wa NO actor in the Loadout's cargo list, then see if there's a craft we can stuff thigns into instead + else if (pLoadout->GetDeliveryCraft()) { + // The craft is now the Actor we are counting, so make an instance + pFirstActor = pLoadout->GetDeliveryCraft(); + // Add the cost of the ship + totalValue += pFirstActor->GetGoldValue(nativeModule, foreignMult); + // Count the stuff it would be filled with, passing ownership + for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) + totalValue += (*iItr)->GetTotalValue(nativeModule, foreignMult); + cargoItems.clear(); + } + // If there's no Actor in this Deployment's Loadout, then we should just count the first Item or device in the Loadout + if (!pFirstActor) { + // Start over the count; we might have only had items/devices in the Loadout list, but no Actors yet + totalValue = 0; + for (std::list::const_iterator itr = pMOList->begin(); itr != pMOList->end(); ++itr) { + // If not an Actor, then we should count it and then stop + if (!dynamic_cast(*itr)) { + // Add to the total cost tally + totalValue += (*itr)->GetGoldValue(nativeModule, foreignMult); + // We're done + break; + } + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of a spawn of this, including -// everything carried by it. - -float Deployment::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const -{ - float totalValue = 0; - const Actor *pFirstActor = 0; - - // Find the Loadout that this Deployment is referring to - Loadout *pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", m_LoadoutName, nativeModule)->Clone()); - if (pLoadout) - { - // Now go through the Loadout list of items and tally the cost ofall devices that would go into inventory of the first Actor found in the list - const std::list *pMOList = pLoadout->GetCargoList(); - if (pMOList && !pMOList->empty()) - { - // Go through the list of things ordered, and give any actors all the items that is present after them, - // until the next actor. Also, the first actor gets all stuff in the list above him. - const MovableObject *pInventoryObject = 0; - const Actor *pActor = 0; - std::list cargoItems; - for (std::list::const_iterator itr = pMOList->begin(); itr != pMOList->end(); ++itr) - { - // Save pointer of the preset in the list - pInventoryObject = dynamic_cast(*itr); - // See if it's actually a passenger, as opposed to a regular item - pActor = dynamic_cast(pInventoryObject); - // If it's an actor, then that's the guy which will be spawned - if (pActor) - { - // Add to the total cost tally - totalValue += (*itr)->GetGoldValue(nativeModule, foreignMult, nativeMult); - // If this is the first passenger, then give him all the shit found in the list before him - if (!pFirstActor) - { - for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) - totalValue += (*iItr)->GetTotalValue(nativeModule, foreignMult); - } - // This isn't the first passenger, so give the previous guy all the stuff that was found since processing him - else - { - for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) - totalValue += (*iItr)->GetTotalValue(nativeModule, foreignMult); - - // Also stop going through the list; we only need to count the value of ONE actor and his stuff - break; - } - // Clear out the temporary cargo list since we've assigned all the stuff in it to the return Actor - cargoItems.clear(); - - // Now set the current Actor as the one we found first - pFirstActor = pActor; - } - // If not an Actor, then add it to the temp list of items which will be added to the last passenger's inventory - else - cargoItems.push_back(pInventoryObject); - } - pActor = 0; - - // If there was a last passenger and only things after him, count the value of all the items in his inventory - if (pFirstActor) - { - // Passing ownership - for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) - totalValue += (*iItr)->GetTotalValue(nativeModule, foreignMult); - cargoItems.clear(); - } - // If there wa NO actor in the Loadout's cargo list, then see if there's a craft we can stuff thigns into instead - else if (pLoadout->GetDeliveryCraft()) - { - // The craft is now the Actor we are counting, so make an instance - pFirstActor = pLoadout->GetDeliveryCraft(); - // Add the cost of the ship - totalValue += pFirstActor->GetGoldValue(nativeModule, foreignMult); - // Count the stuff it would be filled with, passing ownership - for (std::list::iterator iItr = cargoItems.begin(); iItr != cargoItems.end(); ++iItr) - totalValue += (*iItr)->GetTotalValue(nativeModule, foreignMult); - cargoItems.clear(); - } - - // If there's no Actor in this Deployment's Loadout, then we should just count the first Item or device in the Loadout - if (!pFirstActor) - { - // Start over the count; we might have only had items/devices in the Loadout list, but no Actors yet - totalValue = 0; - for (std::list::const_iterator itr = pMOList->begin(); itr != pMOList->end(); ++itr) - { - // If not an Actor, then we should count it and then stop - if (!dynamic_cast(*itr)) - { - // Add to the total cost tally - totalValue += (*itr)->GetGoldValue(nativeModule, foreignMult); - // We're done - break; - } - } - } - } - - // Delete the Loadout instance we have - delete pLoadout; - pLoadout = 0; - } - - return totalValue; -} + // Delete the Loadout instance we have + delete pLoadout; + pLoadout = 0; + } + return totalValue; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. - -bool Deployment::IsOnScenePoint(Vector &scenePoint) const -{ - if (m_Icon.GetBitmaps8().empty() || !(m_Icon.GetBitmaps8().at(0))) - return false; -// TODO: TAKE CARE OF WRAPPING -/* - // Take care of wrapping situations - bitmapPos = m_Pos + m_BitmapOffset; - Vector aScenePoint[4]; - aScenePoint[0] = scenePoint; - int passes = 1; - - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero()) - { - if (g_SceneMan.SceneWrapsX()) - { - if (bitmapPos.m_X < m_pFGColor->w) - { - aScenePoint[passes] = aScenePoint[0]; - aScenePoint[passes].m_X += g_SceneMan.GetSceneWidth(); - passes++; - } - else if (aScenePoint[0].m_X > pTargetBitmap->w - m_pFGColor->w) - { - aScenePoint[passes] = aScenePoint[0]; - aScenePoint[passes].m_X -= g_SceneMan.GetSceneWidth(); - passes++; - } - } - if (g_SceneMan.SceneWrapsY()) - { - - } - } - - // Check all the passes needed - for (int i = 0; i < passes; ++i) - { - - if (IsWithinBox(aScenePoint[i], m_Pos + m_BitmapOffset, m_pFGColor->w, m_pFGColor->h)) - { - if (getpixel(m_pFGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor || - (m_pBGColor && getpixel(m_pBGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor) || - (m_pMaterial && getpixel(m_pMaterial, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaterialAir)) - return true; - } - } -*/ - BITMAP *pBitmap = m_Icon.GetBitmaps8().at(0); - Vector bitmapPos = m_Pos - Vector(pBitmap->w / 2, pBitmap->h / 2); - if (WithinBox(scenePoint, bitmapPos, pBitmap->w, pBitmap->h)) - { - // Scene point on the bitmap - Vector bitmapPoint = scenePoint - bitmapPos; - if (getpixel(pBitmap, bitmapPoint.m_X, bitmapPoint.m_Y) != g_MaskColor) - return true; - } - - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsOnScenePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this' current graphical representation overlaps + // a point in absolute scene coordinates. + + bool Deployment::IsOnScenePoint(Vector& scenePoint) const { + if (m_Icon.GetBitmaps8().empty() || !(m_Icon.GetBitmaps8().at(0))) + return false; + // TODO: TAKE CARE OF WRAPPING + /* + // Take care of wrapping situations + bitmapPos = m_Pos + m_BitmapOffset; + Vector aScenePoint[4]; + aScenePoint[0] = scenePoint; + int passes = 1; + + // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap + if (targetPos.IsZero()) + { + if (g_SceneMan.SceneWrapsX()) + { + if (bitmapPos.m_X < m_pFGColor->w) + { + aScenePoint[passes] = aScenePoint[0]; + aScenePoint[passes].m_X += g_SceneMan.GetSceneWidth(); + passes++; + } + else if (aScenePoint[0].m_X > pTargetBitmap->w - m_pFGColor->w) + { + aScenePoint[passes] = aScenePoint[0]; + aScenePoint[passes].m_X -= g_SceneMan.GetSceneWidth(); + passes++; + } + } + if (g_SceneMan.SceneWrapsY()) + { + + } + } + + // Check all the passes needed + for (int i = 0; i < passes; ++i) + { + + if (IsWithinBox(aScenePoint[i], m_Pos + m_BitmapOffset, m_pFGColor->w, m_pFGColor->h)) + { + if (getpixel(m_pFGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor || + (m_pBGColor && getpixel(m_pBGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor) || + (m_pMaterial && getpixel(m_pMaterial, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaterialAir)) + return true; + } + } + */ + BITMAP* pBitmap = m_Icon.GetBitmaps8().at(0); + Vector bitmapPos = m_Pos - Vector(pBitmap->w / 2, pBitmap->h / 2); + if (WithinBox(scenePoint, bitmapPos, pBitmap->w, pBitmap->h)) { + // Scene point on the bitmap + Vector bitmapPoint = scenePoint - bitmapPos; + if (getpixel(pBitmap, bitmapPoint.m_X, bitmapPoint.m_Y) != g_MaskColor) + return true; + } + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Deployment's current graphical representation to a -// BITMAP of choice. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Deployment's current graphical representation to a + // BITMAP of choice. -void Deployment::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const -{ - if (m_Icon.GetBitmaps8().empty() || !(m_Icon.GetBitmaps8().at(0))) - RTEAbort("Deployment's Icon bitmaps are null when drawing!"); + void Deployment::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { + if (m_Icon.GetBitmaps8().empty() || !(m_Icon.GetBitmaps8().at(0))) + RTEAbort("Deployment's Icon bitmaps are null when drawing!"); - if (m_apArrowLeftBitmap.empty() || m_apArrowRightBitmap.empty()) - RTEAbort("Deployment's Arrow bitmaps are null when drawing!"); + if (m_apArrowLeftBitmap.empty() || m_apArrowRightBitmap.empty()) + RTEAbort("Deployment's Arrow bitmaps are null when drawing!"); - { - BITMAP *pBitmap = m_Icon.GetBitmaps8().at(0); + { + BITMAP* pBitmap = m_Icon.GetBitmaps8().at(0); - // Take care of wrapping situations - Vector aDrawPos[4]; - aDrawPos[0] = m_Pos - Vector(pBitmap->w / 2, pBitmap->h / 2) - targetPos; - int passes = 1; + // Take care of wrapping situations + Vector aDrawPos[4]; + aDrawPos[0] = m_Pos - Vector(pBitmap->w / 2, pBitmap->h / 2) - targetPos; + int passes = 1; - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero()) - { - if (aDrawPos[0].m_X < pBitmap->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += pTargetBitmap->w; - passes++; - } - else if (aDrawPos[0].m_X > pTargetBitmap->w - pBitmap->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= pTargetBitmap->w; - passes++; - } - } - // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam - else - { - if (g_SceneMan.SceneWrapsX()) - { - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (targetPos.m_X < 0) - { + // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap + if (targetPos.IsZero()) { + if (aDrawPos[0].m_X < pBitmap->w) { aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= sceneWidth; + aDrawPos[passes].m_X += pTargetBitmap->w; passes++; - } - if (targetPos.m_X + pTargetBitmap->w > sceneWidth) - { + } else if (aDrawPos[0].m_X > pTargetBitmap->w - pBitmap->w) { aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += sceneWidth; + aDrawPos[passes].m_X -= pTargetBitmap->w; passes++; } } - } - - // Draw all the passes needed - for (int i = 0; i < passes; ++i) - { - if (mode == g_DrawColor) - { - masked_blit(pBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), pBitmap->w, pBitmap->h); - // Draw the spawn radius circle too - circle(pTargetBitmap, aDrawPos[i].GetFloorIntX() + (pBitmap->w / 2), aDrawPos[i].GetFloorIntY() + (pBitmap->h / 2), m_SpawnRadius, c_GUIColorGray); + // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam + else { + if (g_SceneMan.SceneWrapsX()) { + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (targetPos.m_X < 0) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X -= sceneWidth; + passes++; + } + if (targetPos.m_X + pTargetBitmap->w > sceneWidth) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X += sceneWidth; + passes++; + } + } } - else if (mode == g_DrawTrans) - { - draw_trans_sprite(pTargetBitmap, pBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); - // Draw the spawn radius circle too - circle(pTargetBitmap, aDrawPos[i].GetFloorIntX() + (pBitmap->w / 2), aDrawPos[i].GetFloorIntY() + (pBitmap->h / 2), m_SpawnRadius, c_GUIColorGray); + + // Draw all the passes needed + for (int i = 0; i < passes; ++i) { + if (mode == g_DrawColor) { + masked_blit(pBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), pBitmap->w, pBitmap->h); + // Draw the spawn radius circle too + circle(pTargetBitmap, aDrawPos[i].GetFloorIntX() + (pBitmap->w / 2), aDrawPos[i].GetFloorIntY() + (pBitmap->h / 2), m_SpawnRadius, c_GUIColorGray); + } else if (mode == g_DrawTrans) { + draw_trans_sprite(pTargetBitmap, pBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); + // Draw the spawn radius circle too + circle(pTargetBitmap, aDrawPos[i].GetFloorIntX() + (pBitmap->w / 2), aDrawPos[i].GetFloorIntY() + (pBitmap->h / 2), m_SpawnRadius, c_GUIColorGray); + } } } - } - { - // Draw direction arrow - BITMAP * pBitmap = 0; - Vector offset; - if (m_HFlipped) { - pBitmap = m_apArrowLeftBitmap[0]; - offset = Vector(-14, 0); - } - else { - pBitmap = m_apArrowRightBitmap[0]; - offset = Vector(14, 0); - } + // Draw direction arrow + BITMAP* pBitmap = 0; + Vector offset; + if (m_HFlipped) { + pBitmap = m_apArrowLeftBitmap[0]; + offset = Vector(-14, 0); + } else { + pBitmap = m_apArrowRightBitmap[0]; + offset = Vector(14, 0); + } - // Take care of wrapping situations - Vector aDrawPos[4]; - aDrawPos[0] = m_Pos - Vector(pBitmap->w / 2, pBitmap->h / 2) - targetPos + offset; - int passes = 1; + // Take care of wrapping situations + Vector aDrawPos[4]; + aDrawPos[0] = m_Pos - Vector(pBitmap->w / 2, pBitmap->h / 2) - targetPos + offset; + int passes = 1; - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero()) - { - if (aDrawPos[0].m_X < pBitmap->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += pTargetBitmap->w; - passes++; - } - else if (aDrawPos[0].m_X > pTargetBitmap->w - pBitmap->w) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= pTargetBitmap->w; - passes++; - } - } - // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam - else - { - if (g_SceneMan.SceneWrapsX()) - { - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (targetPos.m_X < 0) - { + // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap + if (targetPos.IsZero()) { + if (aDrawPos[0].m_X < pBitmap->w) { aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= sceneWidth; + aDrawPos[passes].m_X += pTargetBitmap->w; passes++; - } - if (targetPos.m_X + pTargetBitmap->w > sceneWidth) - { + } else if (aDrawPos[0].m_X > pTargetBitmap->w - pBitmap->w) { aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += sceneWidth; + aDrawPos[passes].m_X -= pTargetBitmap->w; passes++; } } - } - - // Draw all the passes needed - for (int i = 0; i < passes; ++i) - { - if (mode == g_DrawColor) - { - masked_blit(pBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), pBitmap->w, pBitmap->h); + // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam + else { + if (g_SceneMan.SceneWrapsX()) { + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (targetPos.m_X < 0) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X -= sceneWidth; + passes++; + } + if (targetPos.m_X + pTargetBitmap->w > sceneWidth) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X += sceneWidth; + passes++; + } + } } - else if (mode == g_DrawTrans) - { - draw_trans_sprite(pTargetBitmap, pBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); + + // Draw all the passes needed + for (int i = 0; i < passes; ++i) { + if (mode == g_DrawColor) { + masked_blit(pBitmap, pTargetBitmap, 0, 0, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY(), pBitmap->w, pBitmap->h); + } else if (mode == g_DrawTrans) { + draw_trans_sprite(pTargetBitmap, pBitmap, aDrawPos[i].GetFloorIntX(), aDrawPos[i].GetFloorIntY()); + } } } } -} } // namespace RTE diff --git a/Source/Entities/Deployment.h b/Source/Entities/Deployment.h index 4db9f67bf5..350b70303d 100644 --- a/Source/Entities/Deployment.h +++ b/Source/Entities/Deployment.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,388 +17,358 @@ #include "SceneObject.h" #include "Vector.h" #include "SceneMan.h" -//#include "MovableMan.h" - -namespace RTE -{ - -class ContentFile; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: Deployment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A special SceneObject that specifies a Loadout of whatever Tech is -// relevant to be placed in a specific location in a Scene. -// Parent(s): SceneObject. -// Class history: 02/27/2012 Deployment created. - -class Deployment : public SceneObject { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(Deployment); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: Deployment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a Deployment object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - Deployment() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~Deployment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a Deployment object before deletion -// from system memory. -// Arguments: None. - - ~Deployment() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Deployment object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Deployment object ready for use. -// Arguments: The name of the Loadout that this should invoke at this' position. -// Icon that represents this graphically. -// The radius around this deployment that gets checked if another -// actor/item of the same type and name already exists and will block -// re-spawning a new one by this. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(std::string loadoutName, const Icon &icon, float spawnRadius); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Deployment to be identical to another, by deep copy. -// Arguments: A reference to the Deployment to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const Deployment &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire Deployment, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); SceneObject::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Deployment object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGraphicalIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a bitmap showing a good identifyable icon of this, for use in -// GUI lists etc. -// Arguments: None. -// Return value: A good identifyable graphical representation of this in a BITMAP, if -// available. If not, 0 is returned. Ownership is NOT TRANSFERRED! - - BITMAP * GetGraphicalIcon() const override { return !m_Icon.GetBitmaps8().empty() ? m_Icon.GetBitmaps8()[0] : nullptr; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLoadoutName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the name of the Loadout that this Deployment spawns. -// Arguments: None. -// Return value: The name of the Loadout preset that this Deployment spawns. - - const std::string & GetLoadoutName() { return m_LoadoutName; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGraphicalIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a bitmap showing a good identifyable icon of this. -// Arguments: None. -// Return value: The Icon that represents this graphically. - - Icon GetIcon() { return m_Icon; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSpawnRadius -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the radius around this deployment that gets checked if another -// actor/item of the same type and name already exists and will block -// re-spawning a new one by this -// Arguments: None. -// Return value: The radius this Deployment will be checking within. - - float GetSpawnRadius() const { return m_SpawnRadius; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - - bool IsOnScenePoint(Vector &scenePoint) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateDeployedActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the Actor that this Deployment dictates should -// spawn here. Ownership IS transferred!! All items of the Loadout of -// this Deployment will be added to the Actor's inventory as well (and -// also owned by it) -// Arguments: Which in-game player to create the delivery for. -// A float which will be added to with the cost of the stuff returned here. -// Return value: The Actor instance, if any, that this Deployment is supposed to spawn. -// OWNERSHIP IS TRANSFERRED! - - Actor * CreateDeployedActor(int player, float &costTally); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateDeployedActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the Actor that this Deployment dictates should -// spawn here. Ownership IS transferred!! All items of the Loadout of -// this Deployment will be added to the Actor's inventory as well (and -// also owned by it) -// Arguments: Which in-game player to create the delivery for. -// Return value: The Actor instance, if any, that this Deployment is supposed to spawn. -// OWNERSHIP IS TRANSFERRED! - - Actor * CreateDeployedActor(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateDeployedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the first Device that Deployment dictates should -// spawn here. Ownership IS transferred!! Only the first Device is created. -// Arguments: Which in-game player to create the delivery for. -// A float which will be added to with the cost of the stuff returned here. -// Return value: The Actor instance, if any, that this Deployment is supposed to spawn. -// OWNERSHIP IS TRANSFERRED! - - SceneObject * CreateDeployedObject(int player, float &costTally); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateDeployedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the first Device that Deployment dictates should -// spawn here. Ownership IS transferred!! Only the first Device is created. -// Arguments: Which in-game player to create the delivery for. -// Return value: The Actor instance, if any, that this Deployment is supposed to spawn. -// OWNERSHIP IS TRANSFERRED! - - SceneObject * CreateDeployedObject(); - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DeploymentBlocked -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tests whether the Object this is supposed to spawn/deploy is blocked -// by an already exiting object in the a list being positioned within the -// spawn radius of this. -// Arguments: Which in-game player to create the delivery for. -// A list of SceneObject:s that will be tested against to see if any -// sufficiently similar Object is positioned within the spawn radius of -// this. -// Return value: Whether the deployment spawning is blocked by one of the Objects in -// the list. - - bool DeploymentBlocked(int player, const std::list &existingObjects); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the cost to purchase this item, in oz's of gold. -// Arguments: If this is supposed to be adjusted for a specific Tech's subjective -// value, then pass in the native DataModule ID of that tech. 0 means -// no Tech is specified and the base value is returned. -// How much to multiply the value if this happens to be a foreign Tech. -// Return value: The cost, in oz of gold. - - float GetGoldValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override { return GetTotalValue(nativeModule, foreignMult, nativeMult); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldValueOld -////////////////////////////////////////////////////////////////////////////////////////// -// Description: DOES THE SAME THING AS GetGoldValue, USED ONLY TO PRESERVE LUA COMPATIBILITY - - float GetGoldValueOld(int nativeModule = 0, float foreignMult = 1.0) const override { return GetTotalValue(nativeModule, foreignMult, 1.0); } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of a spawn of this, including -// everything carried by it. -// Arguments: If this is supposed to be adjusted for a specific Tech's subjective -// value, then pass in the native DataModule ID of that tech. 0 means -// no Tech is specified and the base value is returned. -// How much to multiply the value if this happens to be a foreign Tech. -// Return value: The current value of this and all contained assets. - - float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return this deployment's unique ID -// Arguments: None. -// Return value: This deployment's ID - - unsigned int GetID() const { return m_ID; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CloneID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clones id from the specified deployment -// Arguments: Deployment to clone Id from. -// Return value: None - - void CloneID(Deployment * from) { if (from) m_ID = from->GetID(); }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: NewID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Generates new random ID for this deployment. -// Arguments: None. -// Return value: None. - - void NewID() { m_ID = RandomNum(1, 0xFFFF); }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Deployment's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// like indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsHFlipped -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether this MOSprite is being drawn flipped horizontally -// (along the vertical axis), or not. -// Arguments: None. -// Return value: Whether flipped or not. - - bool IsHFlipped() const override { return m_HFlipped; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: SetHFlipped -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this should be drawn flipped horizontally (around the -// vertical axis). -// Arguments: A bool with the new value. -// Return value: None. - - void SetHFlipped(const bool flipped) override { m_HFlipped = flipped; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - - // Name of the Loadout that shuold be placed at this' location in the Scene. - std::string m_LoadoutName; - // The Icon that graphically represents this - Icon m_Icon; - // The radius around this deployment that gets checked if another actor/item of the same type and name already exists and will block re-spawning a new one by this - float m_SpawnRadius; - // The radius around this deployment that gets checked if an actor spawned by this deployment is present. If it is, deployment is blocked. - float m_WalkRadius; - // Unique deployment id, assigned to units deployed by this deployment - unsigned int m_ID; - // Whether the deployment and it's loadout is flipped - bool m_HFlipped; - // Shared HFlipped arrow bitmaps - static std::vector m_apArrowLeftBitmap; - static std::vector m_apArrowRightBitmap; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Deployment, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - Deployment(const Deployment &reference) = delete; - void operator=(const Deployment &rhs) = delete; - -}; +// #include "MovableMan.h" + +namespace RTE { + + class ContentFile; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: Deployment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A special SceneObject that specifies a Loadout of whatever Tech is + // relevant to be placed in a specific location in a Scene. + // Parent(s): SceneObject. + // Class history: 02/27/2012 Deployment created. + + class Deployment : public SceneObject { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(Deployment); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: Deployment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a Deployment object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + Deployment() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~Deployment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a Deployment object before deletion + // from system memory. + // Arguments: None. + + ~Deployment() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Deployment object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Deployment object ready for use. + // Arguments: The name of the Loadout that this should invoke at this' position. + // Icon that represents this graphically. + // The radius around this deployment that gets checked if another + // actor/item of the same type and name already exists and will block + // re-spawning a new one by this. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(std::string loadoutName, const Icon& icon, float spawnRadius); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Deployment to be identical to another, by deep copy. + // Arguments: A reference to the Deployment to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const Deployment& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire Deployment, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + SceneObject::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the Deployment object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGraphicalIcon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a bitmap showing a good identifyable icon of this, for use in + // GUI lists etc. + // Arguments: None. + // Return value: A good identifyable graphical representation of this in a BITMAP, if + // available. If not, 0 is returned. Ownership is NOT TRANSFERRED! + + BITMAP* GetGraphicalIcon() const override { return !m_Icon.GetBitmaps8().empty() ? m_Icon.GetBitmaps8()[0] : nullptr; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLoadoutName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the name of the Loadout that this Deployment spawns. + // Arguments: None. + // Return value: The name of the Loadout preset that this Deployment spawns. + + const std::string& GetLoadoutName() { return m_LoadoutName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGraphicalIcon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a bitmap showing a good identifyable icon of this. + // Arguments: None. + // Return value: The Icon that represents this graphically. + + Icon GetIcon() { return m_Icon; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSpawnRadius + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the radius around this deployment that gets checked if another + // actor/item of the same type and name already exists and will block + // re-spawning a new one by this + // Arguments: None. + // Return value: The radius this Deployment will be checking within. + + float GetSpawnRadius() const { return m_SpawnRadius; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsOnScenePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this' current graphical representation overlaps + // a point in absolute scene coordinates. + // Arguments: The point in absolute scene coordinates. + // Return value: Whether this' graphical rep overlaps the scene point. + + bool IsOnScenePoint(Vector& scenePoint) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateDeployedActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the Actor that this Deployment dictates should + // spawn here. Ownership IS transferred!! All items of the Loadout of + // this Deployment will be added to the Actor's inventory as well (and + // also owned by it) + // Arguments: Which in-game player to create the delivery for. + // A float which will be added to with the cost of the stuff returned here. + // Return value: The Actor instance, if any, that this Deployment is supposed to spawn. + // OWNERSHIP IS TRANSFERRED! + + Actor* CreateDeployedActor(int player, float& costTally); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateDeployedActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the Actor that this Deployment dictates should + // spawn here. Ownership IS transferred!! All items of the Loadout of + // this Deployment will be added to the Actor's inventory as well (and + // also owned by it) + // Arguments: Which in-game player to create the delivery for. + // Return value: The Actor instance, if any, that this Deployment is supposed to spawn. + // OWNERSHIP IS TRANSFERRED! + + Actor* CreateDeployedActor(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateDeployedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the first Device that Deployment dictates should + // spawn here. Ownership IS transferred!! Only the first Device is created. + // Arguments: Which in-game player to create the delivery for. + // A float which will be added to with the cost of the stuff returned here. + // Return value: The Actor instance, if any, that this Deployment is supposed to spawn. + // OWNERSHIP IS TRANSFERRED! + + SceneObject* CreateDeployedObject(int player, float& costTally); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateDeployedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the first Device that Deployment dictates should + // spawn here. Ownership IS transferred!! Only the first Device is created. + // Arguments: Which in-game player to create the delivery for. + // Return value: The Actor instance, if any, that this Deployment is supposed to spawn. + // OWNERSHIP IS TRANSFERRED! + + SceneObject* CreateDeployedObject(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DeploymentBlocked + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tests whether the Object this is supposed to spawn/deploy is blocked + // by an already exiting object in the a list being positioned within the + // spawn radius of this. + // Arguments: Which in-game player to create the delivery for. + // A list of SceneObject:s that will be tested against to see if any + // sufficiently similar Object is positioned within the spawn radius of + // this. + // Return value: Whether the deployment spawning is blocked by one of the Objects in + // the list. + + bool DeploymentBlocked(int player, const std::list& existingObjects); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the cost to purchase this item, in oz's of gold. + // Arguments: If this is supposed to be adjusted for a specific Tech's subjective + // value, then pass in the native DataModule ID of that tech. 0 means + // no Tech is specified and the base value is returned. + // How much to multiply the value if this happens to be a foreign Tech. + // Return value: The cost, in oz of gold. + + float GetGoldValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override { return GetTotalValue(nativeModule, foreignMult, nativeMult); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldValueOld + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: DOES THE SAME THING AS GetGoldValue, USED ONLY TO PRESERVE LUA COMPATIBILITY + + float GetGoldValueOld(int nativeModule = 0, float foreignMult = 1.0) const override { return GetTotalValue(nativeModule, foreignMult, 1.0); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of a spawn of this, including + // everything carried by it. + // Arguments: If this is supposed to be adjusted for a specific Tech's subjective + // value, then pass in the native DataModule ID of that tech. 0 means + // no Tech is specified and the base value is returned. + // How much to multiply the value if this happens to be a foreign Tech. + // Return value: The current value of this and all contained assets. + + float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Return this deployment's unique ID + // Arguments: None. + // Return value: This deployment's ID + + unsigned int GetID() const { return m_ID; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CloneID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clones id from the specified deployment + // Arguments: Deployment to clone Id from. + // Return value: None + + void CloneID(Deployment* from) { + if (from) + m_ID = from->GetID(); + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: NewID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Generates new random ID for this deployment. + // Arguments: None. + // Return value: None. + + void NewID() { m_ID = RandomNum(1, 0xFFFF); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Deployment's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // like indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsHFlipped + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether this MOSprite is being drawn flipped horizontally + // (along the vertical axis), or not. + // Arguments: None. + // Return value: Whether flipped or not. + + bool IsHFlipped() const override { return m_HFlipped; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: SetHFlipped + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this should be drawn flipped horizontally (around the + // vertical axis). + // Arguments: A bool with the new value. + // Return value: None. + + void SetHFlipped(const bool flipped) override { m_HFlipped = flipped; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + + // Name of the Loadout that shuold be placed at this' location in the Scene. + std::string m_LoadoutName; + // The Icon that graphically represents this + Icon m_Icon; + // The radius around this deployment that gets checked if another actor/item of the same type and name already exists and will block re-spawning a new one by this + float m_SpawnRadius; + // The radius around this deployment that gets checked if an actor spawned by this deployment is present. If it is, deployment is blocked. + float m_WalkRadius; + // Unique deployment id, assigned to units deployed by this deployment + unsigned int m_ID; + // Whether the deployment and it's loadout is flipped + bool m_HFlipped; + // Shared HFlipped arrow bitmaps + static std::vector m_apArrowLeftBitmap; + static std::vector m_apArrowRightBitmap; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Deployment, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + Deployment(const Deployment& reference) = delete; + void operator=(const Deployment& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/Emission.cpp b/Source/Entities/Emission.cpp index 76634abf9c..817ef6cfef 100644 --- a/Source/Entities/Emission.cpp +++ b/Source/Entities/Emission.cpp @@ -7,165 +7,157 @@ // data@datarealms.com // http://www.datarealms.com - #include "Emission.h" #include "PresetMan.h" namespace RTE { - -//const string Emission::m_sClassName = "Emission"; - -ConcreteClassInfo(Emission, Entity, 100); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Emission, effectively -// resetting the members of this abstraction level only. - -void Emission::Clear() -{ - m_pEmission = 0; - m_PPM = 0; - m_BurstSize = 0; - m_Accumulator = 0; - m_Spread = 0; - m_MinVelocity = 0; - m_MaxVelocity = 0; - m_LifeVariation = 0.1; - m_PushesEmitter = true; - m_InheritsVel = 0; - m_StartTimer.SetSimTimeLimitMS(0); - m_StartTimer.Reset(); - m_StopTimer.SetSimTimeLimitMS(1000000); - m_StopTimer.Reset(); - m_Offset.Reset(); -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Emission object ready for use. - -int AEmitter::Emission::Create() -{ -if (Serializable::Create() < 0) -return -1; - -return 0; -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Emission to be identical to another, by deep copy. - -int Emission::Create(const Emission &reference) -{ - m_pEmission = reference.m_pEmission; - m_PPM = reference.m_PPM; - m_BurstSize = reference.m_BurstSize; - m_Accumulator = reference.m_Accumulator; - m_Spread = reference.m_Spread; - m_MinVelocity = reference.m_MinVelocity; - m_MaxVelocity = reference.m_MaxVelocity; - m_LifeVariation = reference.m_LifeVariation; - m_PushesEmitter = reference.m_PushesEmitter; - m_InheritsVel = reference.m_InheritsVel; - m_StartTimer = reference.m_StartTimer; - m_StopTimer = reference.m_StopTimer; - m_Offset = reference.m_Offset; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int Emission::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Serializable::ReadProperty(propName, reader)); - - MatchProperty("EmittedParticle", - { - m_pEmission = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); - RTEAssert(m_pEmission, "Stream suggests allocating an unallocatable type in AEmitter::Emission::Create!"); - }); - MatchProperty("ParticlesPerMinute", { reader >> m_PPM; }); - MatchProperty("BurstSize", { reader >> m_BurstSize; }); - MatchProperty("Spread", { reader >> m_Spread; }); - MatchProperty("MinVelocity", { reader >> m_MinVelocity; }); - MatchProperty("MaxVelocity", { reader >> m_MaxVelocity; }); - MatchProperty("LifeVariation", { reader >> m_LifeVariation; }); - MatchProperty("PushesEmitter", { reader >> m_PushesEmitter; }); - MatchProperty("Offset", { reader >> m_Offset; }); - MatchProperty("InheritsVel", - { - reader >> m_InheritsVel; - Clamp(m_InheritsVel, 1, 0); - }); - MatchProperty("StartTimeMS", + // const string Emission::m_sClassName = "Emission"; + + ConcreteClassInfo(Emission, Entity, 100); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Emission, effectively + // resetting the members of this abstraction level only. + + void Emission::Clear() { + m_pEmission = 0; + m_PPM = 0; + m_BurstSize = 0; + m_Accumulator = 0; + m_Spread = 0; + m_MinVelocity = 0; + m_MaxVelocity = 0; + m_LifeVariation = 0.1; + m_PushesEmitter = true; + m_InheritsVel = 0; + m_StartTimer.SetSimTimeLimitMS(0); + m_StartTimer.Reset(); + m_StopTimer.SetSimTimeLimitMS(1000000); + m_StopTimer.Reset(); + m_Offset.Reset(); + } + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Emission object ready for use. + + int AEmitter::Emission::Create() { - double startTime; - reader >> startTime; - m_StartTimer.SetSimTimeLimitMS(startTime); - }); - MatchProperty("StopTimeMS", - { - double stopTime; - reader >> stopTime; - m_StopTimer.SetSimTimeLimitMS(stopTime); - }); - - EndPropertyList; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Emission with a Writer for -// later recreation with Create(Reader &reader); - -int Emission::Save(Writer &writer) const -{ - Serializable::Save(writer); - - writer.NewProperty("EmittedParticle"); - writer << m_pEmission; - writer.NewProperty("ParticlesPerMinute"); - writer << m_PPM; - writer.NewProperty("BurstSize"); - writer << m_BurstSize; - writer.NewProperty("Spread"); - writer << m_Spread; - writer.NewProperty("MinVelocity"); - writer << m_MinVelocity; - writer.NewProperty("MaxVelocity"); - writer << m_MaxVelocity; - writer.NewProperty("LifeVariation"); - writer << m_LifeVariation; - writer.NewProperty("PushesEmitter"); - writer << m_PushesEmitter; - writer.NewProperty("InheritsVel"); - writer << m_InheritsVel; - writer.NewProperty("Offset"); - writer << m_Offset; - writer.NewProperty("StartTimeMS"); - writer << m_StartTimer.GetSimTimeLimitMS(); - writer.NewProperty("StopTimeMS"); - writer << m_StopTimer.GetSimTimeLimitMS(); + if (Serializable::Create() < 0) + return -1; return 0; -} - -} \ No newline at end of file + } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Emission to be identical to another, by deep copy. + + int Emission::Create(const Emission& reference) { + m_pEmission = reference.m_pEmission; + m_PPM = reference.m_PPM; + m_BurstSize = reference.m_BurstSize; + m_Accumulator = reference.m_Accumulator; + m_Spread = reference.m_Spread; + m_MinVelocity = reference.m_MinVelocity; + m_MaxVelocity = reference.m_MaxVelocity; + m_LifeVariation = reference.m_LifeVariation; + m_PushesEmitter = reference.m_PushesEmitter; + m_InheritsVel = reference.m_InheritsVel; + m_StartTimer = reference.m_StartTimer; + m_StopTimer = reference.m_StopTimer; + m_Offset = reference.m_Offset; + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int Emission::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Serializable::ReadProperty(propName, reader)); + + MatchProperty("EmittedParticle", + { + m_pEmission = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); + RTEAssert(m_pEmission, "Stream suggests allocating an unallocatable type in AEmitter::Emission::Create!"); + }); + MatchProperty("ParticlesPerMinute", { reader >> m_PPM; }); + MatchProperty("BurstSize", { reader >> m_BurstSize; }); + MatchProperty("Spread", { reader >> m_Spread; }); + MatchProperty("MinVelocity", { reader >> m_MinVelocity; }); + MatchProperty("MaxVelocity", { reader >> m_MaxVelocity; }); + MatchProperty("LifeVariation", { reader >> m_LifeVariation; }); + MatchProperty("PushesEmitter", { reader >> m_PushesEmitter; }); + MatchProperty("Offset", { reader >> m_Offset; }); + MatchProperty("InheritsVel", + { + reader >> m_InheritsVel; + Clamp(m_InheritsVel, 1, 0); + }); + MatchProperty("StartTimeMS", + { + double startTime; + reader >> startTime; + m_StartTimer.SetSimTimeLimitMS(startTime); + }); + MatchProperty("StopTimeMS", + { + double stopTime; + reader >> stopTime; + m_StopTimer.SetSimTimeLimitMS(stopTime); + }); + + EndPropertyList; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this Emission with a Writer for + // later recreation with Create(Reader &reader); + + int Emission::Save(Writer& writer) const { + Serializable::Save(writer); + + writer.NewProperty("EmittedParticle"); + writer << m_pEmission; + writer.NewProperty("ParticlesPerMinute"); + writer << m_PPM; + writer.NewProperty("BurstSize"); + writer << m_BurstSize; + writer.NewProperty("Spread"); + writer << m_Spread; + writer.NewProperty("MinVelocity"); + writer << m_MinVelocity; + writer.NewProperty("MaxVelocity"); + writer << m_MaxVelocity; + writer.NewProperty("LifeVariation"); + writer << m_LifeVariation; + writer.NewProperty("PushesEmitter"); + writer << m_PushesEmitter; + writer.NewProperty("InheritsVel"); + writer << m_InheritsVel; + writer.NewProperty("Offset"); + writer << m_Offset; + writer.NewProperty("StartTimeMS"); + writer << m_StartTimer.GetSimTimeLimitMS(); + writer.NewProperty("StopTimeMS"); + writer << m_StopTimer.GetSimTimeLimitMS(); + + return 0; + } + +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/Emission.h b/Source/Entities/Emission.h index 7fa25a3619..9a7b73ce2b 100644 --- a/Source/Entities/Emission.h +++ b/Source/Entities/Emission.h @@ -12,326 +12,302 @@ #include "Serializable.h" #include "MovableObject.h" -namespace RTE -{ - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: Emission -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Something to bundle the properties of an emission together. -// Parent(s): Entity. - -class Emission : public Entity { - friend class AEmitter; - friend class PEmitter; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - -public: - - // Concrete allocation and cloning definitions - EntityAllocation(Emission); - SerializableOverrideMethods; - ClassInfoGetters; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Emission - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Emission object in system - // memory. Create() should be called before using the object. - // Arguments: None. - - Emission() { Clear(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Emission to be identical to another, by deep copy. - // Arguments: A reference to the Emission to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - - int Create(const Emission &reference); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Serializable, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - - void Reset() override { Clear(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmissionParticlePreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the reference particle to be emitted. Owenership is NOT transferred! - // Arguments: None. - // Return value: A pointer to the particle to be emitted. Not transferred! - - const MovableObject * GetEmissionParticlePreset() { return m_pEmission; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rate at which these emissions are made, in particles per minute. - // Arguments: None. - // Return value: The emission rate in PPM. - - float GetRate() const { return m_PPM; } - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetRate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the rate at which these emissions are made, in particles per minute. - // Arguments: The emission rate in PPM. - // Return value: None. - - void SetRate(float newPPM) { m_PPM = newPPM; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of extra particles that are bursted at the beginning of - // emission. - // Arguments: None. - // Return value: The burst size. - - int GetBurstSize() const { return m_BurstSize; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the number of extra particles that are bursted at the beginning of - // emission. - // Arguments: The burst size. - // Return value: None. - - void SetBurstSize(int newSize) { m_BurstSize = newSize; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpread - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the angle spread of velocity of the emitted MO's to each side of - // the m_EmitAngle angle. in radians. PI/2 would mean that MO's fly out to - // one side only, with the m_Rotation defining the middle of that half circle. - // Arguments: None. - // Return value: The emission spread in radians. - - float GetSpread() const { return m_Spread; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSpread - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the angle spread of velocity of the emitted MO's to each side of - // the m_EmitAngle angle. in radians. PI/2 would mean that MO's fly out to - // one side only, with the m_Rotation defining the middle of that half circle. - // Arguments: The emission spread in radians. - // Return value: None. - - void SetSpread(float newSpread) { m_Spread = newSpread; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMinVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified minimum velocity an emitted MO can have when emitted. - // Arguments: None. - // Return value: The min emission velocity in m/s. - - float GetMinVelocity() const { return m_MinVelocity; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMinVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the specified minimum velocity an emitted MO can have when emitted. - // Arguments: The min emission velocity in m/s. - // Return value: None. - - void SetMinVelocity(float newVel) { m_MinVelocity = newVel; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaxVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified maximum velocity an emitted MO can have when emitted. - // Arguments: None. - // Return value: The max emission velocity in m/s. - - float GetMaxVelocity() const { return m_MaxVelocity; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMaxVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified maximum velocity an emitted MO can have when emitted. - // Arguments: The max emission velocity in m/s. - // Return value: None. - - void SetMaxVelocity(float newVel) { m_MaxVelocity = newVel; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLifeVariation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified variation in lifetime of the emitted particles. - // Arguments: None. - // Return value: The life variation rationally expressed.. 0.1 = up to 10% varitaion. - - float GetLifeVariation() const { return m_LifeVariation; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLifeVariation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the specified variation in lifetime of the emitted particles. - // Arguments: The life variation rationally expressed.. 0.1 = up to 10% varitaion. - // Return value: None. - - void SetLifeVariation(float newVariation) { m_LifeVariation = newVariation; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PushesEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this emission is supposed to push its emitter back - // because of recoil. - // Arguments: None. - // Return value: Whether recoil pushing is enabled or not for this emitter. - - bool PushesEmitter() const { return m_PushesEmitter; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPushesEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this emission is supposed to push its emitter back - // because of recoil. - // Arguments: Whether recoil pushing is enabled or not for this emitter. - // Return value: None. - - void SetPushesEmitter(bool newValue) { m_PushesEmitter = newValue; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEmissionTime - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this should be emitting now or not, based on what its - // start and end timers are set to. - // Arguments: None. - // Return value: Whether this should be emitting right now. - - bool IsEmissionTime() { return m_StartTimer.IsPastSimTimeLimit() && !m_StopTimer.IsPastSimTimeLimit(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetEmissionTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the emission timers so they start counting time as to wheter - // emissions are clearer. - // Arguments: None. - // Return value: None. - - void ResetEmissionTimers() { m_StartTimer.Reset(); m_StopTimer.Reset(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: InheritsVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: How much of the root parent's velocity this emission inherit - // Arguments: None. - // Return value: The proportion of the velocity inherited. 0.1 = 10% inheritance. - - float InheritsVelocity() { return m_InheritsVel; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter - // Arguments: None. - // Return value: Returns emission offset. - - Vector GetOffset() const { return m_Offset; } - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter - // Arguments: New offset value. - // Return value: None. - - void SetOffset(Vector offset) { m_Offset = offset; } - - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - -protected: - - // Member variables - //static const std::string m_sClassName; - // Member variables - static Entity::ClassInfo m_sClass; - - // The pointer to the preset instance, that copies of which will be emitted - const MovableObject *m_pEmission; - // Emission rate in Particles Per Minute - float m_PPM; - // The number of particles in the first initial burst of emissions - // that this AEmitter will generate upon emitting. 0 means none (duh). - int m_BurstSize; - // The accumulator for decoupling emission rate from the physics update rate. - double m_Accumulator; - // The angle spread of velocity of the emitted MO's to each - // side of the m_EmitAngle angle. in radians. - // PI/2 would mean that MO's fly out to one side only, with the - // m_Rotation defining the middle of that half circle. - float m_Spread; - // The minimum velocity an emitted MO can have when emitted - float m_MinVelocity; - // The maximum velocity an emitted MO can have when emitted - float m_MaxVelocity; - // The variation in life time of each emitted aprticle, in percentage of the existing life time of the partilcle - float m_LifeVariation; - // Whether these emissions push the emitter around with recoil or not. - bool m_PushesEmitter; - // How much of the parents velocity this emission inherits - float m_InheritsVel; - // Timers for measuring when to start and stop this emission the actual times are the set time limits of these - Timer m_StartTimer; - Timer m_StopTimer; - // Offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter - Vector m_Offset; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - -private: - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Emission, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - - void Clear(); - -}; +namespace RTE { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: Emission + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Something to bundle the properties of an emission together. + // Parent(s): Entity. + + class Emission : public Entity { + friend class AEmitter; + friend class PEmitter; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(Emission); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: Emission + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a Emission object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + Emission() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Emission to be identical to another, by deep copy. + // Arguments: A reference to the Emission to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const Emission& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire Serializable, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmissionParticlePreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the reference particle to be emitted. Owenership is NOT transferred! + // Arguments: None. + // Return value: A pointer to the particle to be emitted. Not transferred! + + const MovableObject* GetEmissionParticlePreset() { return m_pEmission; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rate at which these emissions are made, in particles per minute. + // Arguments: None. + // Return value: The emission rate in PPM. + + float GetRate() const { return m_PPM; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetRate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the rate at which these emissions are made, in particles per minute. + // Arguments: The emission rate in PPM. + // Return value: None. + + void SetRate(float newPPM) { m_PPM = newPPM; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBurstSize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of extra particles that are bursted at the beginning of + // emission. + // Arguments: None. + // Return value: The burst size. + + int GetBurstSize() const { return m_BurstSize; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBurstSize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the number of extra particles that are bursted at the beginning of + // emission. + // Arguments: The burst size. + // Return value: None. + + void SetBurstSize(int newSize) { m_BurstSize = newSize; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSpread + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the angle spread of velocity of the emitted MO's to each side of + // the m_EmitAngle angle. in radians. PI/2 would mean that MO's fly out to + // one side only, with the m_Rotation defining the middle of that half circle. + // Arguments: None. + // Return value: The emission spread in radians. + + float GetSpread() const { return m_Spread; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSpread + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the angle spread of velocity of the emitted MO's to each side of + // the m_EmitAngle angle. in radians. PI/2 would mean that MO's fly out to + // one side only, with the m_Rotation defining the middle of that half circle. + // Arguments: The emission spread in radians. + // Return value: None. + + void SetSpread(float newSpread) { m_Spread = newSpread; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMinVelocity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the specified minimum velocity an emitted MO can have when emitted. + // Arguments: None. + // Return value: The min emission velocity in m/s. + + float GetMinVelocity() const { return m_MinVelocity; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMinVelocity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the specified minimum velocity an emitted MO can have when emitted. + // Arguments: The min emission velocity in m/s. + // Return value: None. + + void SetMinVelocity(float newVel) { m_MinVelocity = newVel; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMaxVelocity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the specified maximum velocity an emitted MO can have when emitted. + // Arguments: None. + // Return value: The max emission velocity in m/s. + + float GetMaxVelocity() const { return m_MaxVelocity; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMaxVelocity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the specified maximum velocity an emitted MO can have when emitted. + // Arguments: The max emission velocity in m/s. + // Return value: None. + + void SetMaxVelocity(float newVel) { m_MaxVelocity = newVel; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLifeVariation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the specified variation in lifetime of the emitted particles. + // Arguments: None. + // Return value: The life variation rationally expressed.. 0.1 = up to 10% varitaion. + + float GetLifeVariation() const { return m_LifeVariation; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLifeVariation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the specified variation in lifetime of the emitted particles. + // Arguments: The life variation rationally expressed.. 0.1 = up to 10% varitaion. + // Return value: None. + + void SetLifeVariation(float newVariation) { m_LifeVariation = newVariation; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PushesEmitter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this emission is supposed to push its emitter back + // because of recoil. + // Arguments: None. + // Return value: Whether recoil pushing is enabled or not for this emitter. + + bool PushesEmitter() const { return m_PushesEmitter; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPushesEmitter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this emission is supposed to push its emitter back + // because of recoil. + // Arguments: Whether recoil pushing is enabled or not for this emitter. + // Return value: None. + + void SetPushesEmitter(bool newValue) { m_PushesEmitter = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsEmissionTime + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this should be emitting now or not, based on what its + // start and end timers are set to. + // Arguments: None. + // Return value: Whether this should be emitting right now. + + bool IsEmissionTime() { return m_StartTimer.IsPastSimTimeLimit() && !m_StopTimer.IsPastSimTimeLimit(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ResetEmissionTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the emission timers so they start counting time as to wheter + // emissions are clearer. + // Arguments: None. + // Return value: None. + + void ResetEmissionTimers() { + m_StartTimer.Reset(); + m_StopTimer.Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: InheritsVelocity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: How much of the root parent's velocity this emission inherit + // Arguments: None. + // Return value: The proportion of the velocity inherited. 0.1 = 10% inheritance. + + float InheritsVelocity() { return m_InheritsVel; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter + // Arguments: None. + // Return value: Returns emission offset. + + Vector GetOffset() const { return m_Offset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter + // Arguments: New offset value. + // Return value: None. + + void SetOffset(Vector offset) { m_Offset = offset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + // static const std::string m_sClassName; + // Member variables + static Entity::ClassInfo m_sClass; + + // The pointer to the preset instance, that copies of which will be emitted + const MovableObject* m_pEmission; + // Emission rate in Particles Per Minute + float m_PPM; + // The number of particles in the first initial burst of emissions + // that this AEmitter will generate upon emitting. 0 means none (duh). + int m_BurstSize; + // The accumulator for decoupling emission rate from the physics update rate. + double m_Accumulator; + // The angle spread of velocity of the emitted MO's to each + // side of the m_EmitAngle angle. in radians. + // PI/2 would mean that MO's fly out to one side only, with the + // m_Rotation defining the middle of that half circle. + float m_Spread; + // The minimum velocity an emitted MO can have when emitted + float m_MinVelocity; + // The maximum velocity an emitted MO can have when emitted + float m_MaxVelocity; + // The variation in life time of each emitted aprticle, in percentage of the existing life time of the partilcle + float m_LifeVariation; + // Whether these emissions push the emitter around with recoil or not. + bool m_PushesEmitter; + // How much of the parents velocity this emission inherits + float m_InheritsVel; + // Timers for measuring when to start and stop this emission the actual times are the set time limits of these + Timer m_StartTimer; + Timer m_StopTimer; + // Offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter + Vector m_Offset; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Emission, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/Gib.cpp b/Source/Entities/Gib.cpp index 0b53ff1511..e24a03925b 100644 --- a/Source/Entities/Gib.cpp +++ b/Source/Entities/Gib.cpp @@ -6,7 +6,7 @@ namespace RTE { const std::string Gib::c_ClassName = "Gib"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Gib::Clear() { m_GibParticle = nullptr; @@ -21,9 +21,9 @@ namespace RTE { m_SpreadMode = SpreadMode::SpreadRandom; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Gib::Create(const Gib &reference) { + int Gib::Create(const Gib& reference) { m_GibParticle = reference.m_GibParticle; m_Offset = reference.m_Offset; m_Count = reference.m_Count; @@ -38,13 +38,13 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Gib::ReadProperty(const std::string_view &propName, Reader &reader) { + int Gib::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("GibParticle", { - m_GibParticle = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); + m_GibParticle = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); RTEAssert(m_GibParticle, "Stream suggests allocating an unallocable type in Gib::Create!"); }); MatchProperty("Offset", { reader >> m_Offset; }); @@ -60,9 +60,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Gib::Save(Writer &writer) const { + int Gib::Save(Writer& writer) const { Serializable::Save(writer); writer.NewProperty("GibParticle"); @@ -93,4 +93,4 @@ namespace RTE { return 0; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/Gib.h b/Source/Entities/Gib.h index 23ddf10f2b..0bc56615cd 100644 --- a/Source/Entities/Gib.h +++ b/Source/Entities/Gib.h @@ -15,14 +15,17 @@ namespace RTE { friend struct EntityLuaBindings; public: - SerializableClassNameGetter; SerializableOverrideMethods; /// /// Different types of logic for the Gib to use when applying velocity to its GibParticles. /// - enum SpreadMode { SpreadRandom, SpreadEven, SpreadSpiral }; + enum SpreadMode { + SpreadRandom, + SpreadEven, + SpreadSpiral + }; #pragma region Creation /// @@ -35,7 +38,7 @@ namespace RTE { /// /// A reference to the Gib to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Gib &reference); + int Create(const Gib& reference); #pragma endregion #pragma region Destruction @@ -55,13 +58,13 @@ namespace RTE { /// Gets the reference particle to be used as a Gib. Ownership is NOT transferred! /// /// A pointer to the particle to be used as a Gib. - const MovableObject * GetParticlePreset() const { return m_GibParticle; } + const MovableObject* GetParticlePreset() const { return m_GibParticle; } /// /// Sets the reference particle to be used as a Gib. Ownership is NOT transferred! /// /// A pointer to the new particle to be used as a Gib. - void SetParticlePreset(const MovableObject *newParticlePreset) { m_GibParticle = newParticlePreset; } + void SetParticlePreset(const MovableObject* newParticlePreset) { m_GibParticle = newParticlePreset; } /// /// Gets the spawn offset of this Gib from the parent's position. @@ -138,8 +141,7 @@ namespace RTE { #pragma endregion protected: - - const MovableObject *m_GibParticle; //!< The pointer to the preset instance that copies of will be created as this Gib. Not Owned. + const MovableObject* m_GibParticle; //!< The pointer to the preset instance that copies of will be created as this Gib. Not Owned. Vector m_Offset; //!< Offset spawn position from owner/parent's position. unsigned int m_Count; //!< The number of copies of the GibParticle that will be spawned. float m_Spread; //!< The angle spread of the spawned GibParticle objects to each side of the parent's angle in radians. @@ -151,7 +153,6 @@ namespace RTE { SpreadMode m_SpreadMode; //!< Determines what kind of logic is used when applying velocity to the GibParticle objects. private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. /// @@ -159,5 +160,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/GlobalScript.cpp b/Source/Entities/GlobalScript.cpp index ce18158f3d..60521a9be0 100644 --- a/Source/Entities/GlobalScript.cpp +++ b/Source/Entities/GlobalScript.cpp @@ -14,7 +14,7 @@ namespace RTE { ConcreteClassInfo(GlobalScript, Entity, 10); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GlobalScript::Clear() { m_ScriptPath.clear(); @@ -25,9 +25,9 @@ namespace RTE { m_PieSlicesToAdd.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::Create(const GlobalScript &reference) { + int GlobalScript::Create(const GlobalScript& reference) { Entity::Create(reference); m_ScriptPath = reference.m_ScriptPath; @@ -36,43 +36,43 @@ namespace RTE { m_HasStarted = reference.m_HasStarted; m_LateUpdate = reference.m_LateUpdate; - for (const std::unique_ptr &referencePieSliceToAdd : reference.m_PieSlicesToAdd) { - m_PieSlicesToAdd.emplace_back(std::unique_ptr(dynamic_cast(referencePieSliceToAdd->Clone()))); + for (const std::unique_ptr& referencePieSliceToAdd: reference.m_PieSlicesToAdd) { + m_PieSlicesToAdd.emplace_back(std::unique_ptr(dynamic_cast(referencePieSliceToAdd->Clone()))); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::ReadProperty(const std::string_view &propName, Reader &reader) { + int GlobalScript::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("ScriptPath", { m_ScriptPath = CorrectBackslashesInPath(reader.ReadPropValue()); }); MatchProperty("LuaClassName", { reader >> m_LuaClassName; }); MatchProperty("LateUpdate", { reader >> m_LateUpdate; }); - MatchProperty("AddPieSlice", { m_PieSlicesToAdd.emplace_back(std::unique_ptr(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)))); }); + MatchProperty("AddPieSlice", { m_PieSlicesToAdd.emplace_back(std::unique_ptr(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)))); }); EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::Save(Writer &writer) const { + int GlobalScript::Save(Writer& writer) const { Entity::Save(writer); writer.NewPropertyWithValue("ScriptPath", m_ScriptPath); writer.NewPropertyWithValue("LuaClassName", m_LuaClassName); writer.NewPropertyWithValue("LateUpdate", m_LateUpdate); - for (const std::unique_ptr &pieSliceToAdd : m_PieSlicesToAdd) { + for (const std::unique_ptr& pieSliceToAdd: m_PieSlicesToAdd) { writer.NewPropertyWithValue("AddPieSlice", pieSliceToAdd.get()); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const std::vector>& GlobalScript::GetPieSlicesToAdd() const { static const std::vector> emptyVector; @@ -83,7 +83,7 @@ namespace RTE { return m_PieSlicesToAdd; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int GlobalScript::ReloadScripts() { int error = 0; @@ -93,13 +93,15 @@ namespace RTE { g_LuaMan.GetMasterScriptState().SetTempEntity(this); error = g_LuaMan.GetMasterScriptState().RunScriptString(m_LuaClassName + " = ToGlobalScript(LuaMan.TempEntity);"); } - if (error == 0) { g_LuaMan.GetMasterScriptState().RunScriptFile(m_ScriptPath); } + if (error == 0) { + g_LuaMan.GetMasterScriptState().RunScriptFile(m_ScriptPath); + } } return error; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int GlobalScript::Start() { if (!g_SettingsMan.IsGlobalScriptEnabled(GetModuleAndPresetName())) { @@ -111,16 +113,16 @@ namespace RTE { } int error = ReloadScripts(); - if (error == 0) { + if (error == 0) { error = g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".StartScript then " + m_LuaClassName + ":StartScript(); end"); - m_HasStarted = true; + m_HasStarted = true; } m_IsActive = error == 0; return error; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int GlobalScript::Pause(bool pause) const { if (!m_IsActive || !m_HasStarted || !g_SettingsMan.IsGlobalScriptEnabled(GetModuleAndPresetName())) { @@ -130,7 +132,7 @@ namespace RTE { return g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".PauseScript then " + m_LuaClassName + ":PauseScript(" + (pause ? "true" : "false") + "); end"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int GlobalScript::End() const { if (!m_HasStarted) { @@ -144,20 +146,20 @@ namespace RTE { return g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".EndScript then " + m_LuaClassName + ":EndScript(); end"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GlobalScript::HandleCraftEnteringOrbit(const ACraft *orbitedCraft) { + void GlobalScript::HandleCraftEnteringOrbit(const ACraft* orbitedCraft) { if (!m_IsActive || !!m_HasStarted || orbitedCraft == nullptr || !g_MovableMan.IsActor(orbitedCraft) || !g_SettingsMan.IsGlobalScriptEnabled(GetModuleAndPresetName())) { return; } - int error = g_LuaMan.GetMasterScriptState().RunScriptFunctionString(m_LuaClassName + ".CraftEnteredOrbit", m_LuaClassName, { m_LuaClassName, m_LuaClassName + ".CraftEnteredOrbit" }, { orbitedCraft }); + int error = g_LuaMan.GetMasterScriptState().RunScriptFunctionString(m_LuaClassName + ".CraftEnteredOrbit", m_LuaClassName, {m_LuaClassName, m_LuaClassName + ".CraftEnteredOrbit"}, {orbitedCraft}); if (error) { m_IsActive = false; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GlobalScript::Update() { if (!m_IsActive) { @@ -176,8 +178,8 @@ namespace RTE { } int error = g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".UpdateScript then " + m_LuaClassName + ":UpdateScript(); end"); - if (error) { + if (error) { m_IsActive = false; } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/GlobalScript.h b/Source/Entities/GlobalScript.h index a298bb694a..c0876cf329 100644 --- a/Source/Entities/GlobalScript.h +++ b/Source/Entities/GlobalScript.h @@ -14,7 +14,6 @@ namespace RTE { friend struct EntityLuaBindings; public: - EntityAllocation(GlobalScript); SerializableOverrideMethods; ClassInfoGetters; @@ -36,7 +35,7 @@ namespace RTE { /// /// A reference to the GlobalScript to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const GlobalScript &reference); + int Create(const GlobalScript& reference); #pragma endregion #pragma region Destruction @@ -49,12 +48,20 @@ namespace RTE { /// Destroys and resets (through Clear()) the GlobalScript object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + Entity::Destroy(); + } + Clear(); + } /// /// Resets the entire GlobalScript, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Entity::Reset(); } + void Reset() override { + Clear(); + Entity::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -113,7 +120,7 @@ namespace RTE { /// Handles when an ACraft has left the game scene and entered orbit by running the appropriate Lua function. Ownership is NOT transferred! /// /// The ACraft instance that entered orbit. Ownership is NOT transferred! - void HandleCraftEnteringOrbit(const ACraft *orbitedCraft); + void HandleCraftEnteringOrbit(const ACraft* orbitedCraft); /// /// Updates the state of this GlobalScript every frame. @@ -122,7 +129,6 @@ namespace RTE { #pragma endregion private: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. std::string m_ScriptPath; //!< The path to the Lua script file that defines this' behaviors in update. @@ -139,8 +145,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - GlobalScript(const GlobalScript &reference) = delete; - GlobalScript &operator=(const GlobalScript &rhs) = delete; + GlobalScript(const GlobalScript& reference) = delete; + GlobalScript& operator=(const GlobalScript& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/HDFirearm.cpp b/Source/Entities/HDFirearm.cpp index e85e0713fe..72c11dda37 100644 --- a/Source/Entities/HDFirearm.cpp +++ b/Source/Entities/HDFirearm.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -26,1188 +25,1199 @@ namespace RTE { -ConcreteClassInfo(HDFirearm, HeldDevice, 50); + ConcreteClassInfo(HDFirearm, HeldDevice, 50); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this HDFirearm, effectively + // resetting the members of this abstraction level only. + + void HDFirearm::Clear() { + m_pMagazineReference = 0; + m_pMagazine = 0; + + m_pFlash = 0; + m_PreFireSound = nullptr; + m_FireSound = nullptr; + m_FireEchoSound = nullptr; + m_ActiveSound = nullptr; + m_DeactivationSound = nullptr; + m_EmptySound = nullptr; + m_ReloadStartSound = nullptr; + m_ReloadEndSound = nullptr; + m_ReloadEndOffset = -1.0F; + m_HasPlayedEndReloadSound = false; + m_RateOfFire = 0; + m_ActivationDelay = 0; + m_DeactivationDelay = 0; + m_Reloading = false; + m_DoneReloading = false; + m_BaseReloadTime = 0; + m_FullAuto = false; + m_FireIgnoresThis = true; + m_Reloadable = true; + m_DualReloadable = false; + m_OneHandedReloadTimeMultiplier = 1.5F; + m_ReloadAngle = -0.5F; + m_OneHandedReloadAngle = -1.0F; + m_ShakeRange = 0; + m_SharpShakeRange = 0; + m_NoSupportFactor = 0; + m_ParticleSpreadRange = 0; + m_ShellEjectAngle = 150; + m_ShellSpreadRange = 0; + m_ShellAngVelRange = 0; + m_ShellVelVariation = 0.1F; + m_RecoilScreenShakeAmount = 0.0F; + m_AIFireVel = -1; + m_AIBulletLifeTime = 0; + m_AIBulletAccScalar = -1; + m_LastFireTmr.Reset(); + m_ReloadTmr.Reset(); + m_MuzzleOff.Reset(); + m_EjectOff.Reset(); + m_MagOff.Reset(); + m_FiredOnce = false; + m_FireFrame = false; + m_FiredLastFrame = false; + m_AlreadyClicked = false; + m_RoundsFired = 0; + m_IsAnimatedManually = false; + + m_LegacyCompatibilityRoundsAlwaysFireUnflipped = false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Round object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this HDFirearm, effectively -// resetting the members of this abstraction level only. - -void HDFirearm::Clear() -{ - m_pMagazineReference = 0; - m_pMagazine = 0; - - m_pFlash = 0; - m_PreFireSound = nullptr; - m_FireSound = nullptr; - m_FireEchoSound = nullptr; - m_ActiveSound = nullptr; - m_DeactivationSound = nullptr; - m_EmptySound = nullptr; - m_ReloadStartSound = nullptr; - m_ReloadEndSound = nullptr; - m_ReloadEndOffset = -1.0F; - m_HasPlayedEndReloadSound = false; - m_RateOfFire = 0; - m_ActivationDelay = 0; - m_DeactivationDelay = 0; - m_Reloading = false; - m_DoneReloading = false; - m_BaseReloadTime = 0; - m_FullAuto = false; - m_FireIgnoresThis = true; - m_Reloadable = true; - m_DualReloadable = false; - m_OneHandedReloadTimeMultiplier = 1.5F; - m_ReloadAngle = -0.5F; - m_OneHandedReloadAngle = -1.0F; - m_ShakeRange = 0; - m_SharpShakeRange = 0; - m_NoSupportFactor = 0; - m_ParticleSpreadRange = 0; - m_ShellEjectAngle = 150; - m_ShellSpreadRange = 0; - m_ShellAngVelRange = 0; - m_ShellVelVariation = 0.1F; - m_RecoilScreenShakeAmount = 0.0F; - m_AIFireVel = -1; - m_AIBulletLifeTime = 0; - m_AIBulletAccScalar = -1; - m_LastFireTmr.Reset(); - m_ReloadTmr.Reset(); - m_MuzzleOff.Reset(); - m_EjectOff.Reset(); - m_MagOff.Reset(); - m_FiredOnce = false; - m_FireFrame = false; - m_FiredLastFrame = false; - m_AlreadyClicked = false; - m_RoundsFired = 0; - m_IsAnimatedManually = false; - - m_LegacyCompatibilityRoundsAlwaysFireUnflipped = false; -} + int HDFirearm::Create() { + if (HeldDevice::Create() < 0) + return -1; + if (m_pFlash) + m_pFlash->SetParentOffset(m_MuzzleOff); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Round object ready for use. + return 0; + } -int HDFirearm::Create() -{ - if (HeldDevice::Create() < 0) - return -1; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a HDFirearm to be identical to another, by deep copy. - if (m_pFlash) - m_pFlash->SetParentOffset(m_MuzzleOff); + int HDFirearm::Create(const HDFirearm& reference) { + if (reference.m_pMagazine) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pMagazine->GetUniqueID()); + } + if (reference.m_pFlash) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFlash->GetUniqueID()); + } - return 0; -} + HeldDevice::Create(reference); + if (reference.m_pMagazine) { + SetMagazine(dynamic_cast(reference.m_pMagazine->Clone())); + } + if (reference.m_pFlash) { + SetFlash(dynamic_cast(reference.m_pFlash->Clone())); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a HDFirearm to be identical to another, by deep copy. - -int HDFirearm::Create(const HDFirearm &reference) { - if (reference.m_pMagazine) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pMagazine->GetUniqueID()); } - if (reference.m_pFlash) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFlash->GetUniqueID()); } - - HeldDevice::Create(reference); - - if (reference.m_pMagazine) { SetMagazine(dynamic_cast(reference.m_pMagazine->Clone())); } - if (reference.m_pFlash) { SetFlash(dynamic_cast(reference.m_pFlash->Clone())); } - - m_pMagazineReference = reference.m_pMagazineReference; - if (reference.m_PreFireSound) { m_PreFireSound = dynamic_cast(reference.m_PreFireSound->Clone()); } - if (reference.m_FireSound) { m_FireSound = dynamic_cast(reference.m_FireSound->Clone()); } - if (reference.m_FireEchoSound) { m_FireEchoSound = dynamic_cast(reference.m_FireEchoSound->Clone()); } - if (reference.m_ActiveSound) { m_ActiveSound = dynamic_cast(reference.m_ActiveSound->Clone()); } - if (reference.m_DeactivationSound) { m_DeactivationSound = dynamic_cast(reference.m_DeactivationSound->Clone()); } - if (reference.m_EmptySound) { m_EmptySound = dynamic_cast(reference.m_EmptySound->Clone()); } - if (reference.m_ReloadStartSound) { m_ReloadStartSound = dynamic_cast(reference.m_ReloadStartSound->Clone()); } - if (reference.m_ReloadEndSound) { m_ReloadEndSound = dynamic_cast(reference.m_ReloadEndSound->Clone()); } - m_ReloadEndOffset = reference.m_ReloadEndOffset; - m_RateOfFire = reference.m_RateOfFire; - m_ActivationDelay = reference.m_ActivationDelay; - m_DeactivationDelay = reference.m_DeactivationDelay; - m_Reloading = reference.m_Reloading; - m_DoneReloading = reference.m_DoneReloading; - m_BaseReloadTime = reference.m_BaseReloadTime; - m_LastFireTmr = reference.m_LastFireTmr; - m_ReloadTmr = reference.m_ReloadTmr; - m_FullAuto = reference.m_FullAuto; - m_FireIgnoresThis = reference.m_FireIgnoresThis; - m_Reloadable = reference.m_Reloadable; - m_DualReloadable = reference.m_DualReloadable; - m_OneHandedReloadTimeMultiplier = reference.m_OneHandedReloadTimeMultiplier; - m_ReloadAngle = reference.m_ReloadAngle; - m_OneHandedReloadAngle = reference.m_OneHandedReloadAngle; - m_ShakeRange = reference.m_ShakeRange; - m_SharpShakeRange = reference.m_SharpShakeRange; - m_NoSupportFactor = reference.m_NoSupportFactor; - m_ParticleSpreadRange = reference.m_ParticleSpreadRange; - m_ShellEjectAngle = reference.m_ShellEjectAngle; - m_ShellSpreadRange = reference.m_ShellSpreadRange; - m_ShellAngVelRange = reference.m_ShellAngVelRange; - m_ShellVelVariation = reference.m_ShellVelVariation; - m_RecoilScreenShakeAmount = reference.m_RecoilScreenShakeAmount; - m_MuzzleOff = reference.m_MuzzleOff; - m_EjectOff = reference.m_EjectOff; - m_MagOff = reference.m_MagOff; - m_RoundsFired = reference.m_RoundsFired; - m_IsAnimatedManually = reference.m_IsAnimatedManually; - - m_LegacyCompatibilityRoundsAlwaysFireUnflipped = reference.m_LegacyCompatibilityRoundsAlwaysFireUnflipped; - - return 0; -} + m_pMagazineReference = reference.m_pMagazineReference; + if (reference.m_PreFireSound) { + m_PreFireSound = dynamic_cast(reference.m_PreFireSound->Clone()); + } + if (reference.m_FireSound) { + m_FireSound = dynamic_cast(reference.m_FireSound->Clone()); + } + if (reference.m_FireEchoSound) { + m_FireEchoSound = dynamic_cast(reference.m_FireEchoSound->Clone()); + } + if (reference.m_ActiveSound) { + m_ActiveSound = dynamic_cast(reference.m_ActiveSound->Clone()); + } + if (reference.m_DeactivationSound) { + m_DeactivationSound = dynamic_cast(reference.m_DeactivationSound->Clone()); + } + if (reference.m_EmptySound) { + m_EmptySound = dynamic_cast(reference.m_EmptySound->Clone()); + } + if (reference.m_ReloadStartSound) { + m_ReloadStartSound = dynamic_cast(reference.m_ReloadStartSound->Clone()); + } + if (reference.m_ReloadEndSound) { + m_ReloadEndSound = dynamic_cast(reference.m_ReloadEndSound->Clone()); + } + m_ReloadEndOffset = reference.m_ReloadEndOffset; + m_RateOfFire = reference.m_RateOfFire; + m_ActivationDelay = reference.m_ActivationDelay; + m_DeactivationDelay = reference.m_DeactivationDelay; + m_Reloading = reference.m_Reloading; + m_DoneReloading = reference.m_DoneReloading; + m_BaseReloadTime = reference.m_BaseReloadTime; + m_LastFireTmr = reference.m_LastFireTmr; + m_ReloadTmr = reference.m_ReloadTmr; + m_FullAuto = reference.m_FullAuto; + m_FireIgnoresThis = reference.m_FireIgnoresThis; + m_Reloadable = reference.m_Reloadable; + m_DualReloadable = reference.m_DualReloadable; + m_OneHandedReloadTimeMultiplier = reference.m_OneHandedReloadTimeMultiplier; + m_ReloadAngle = reference.m_ReloadAngle; + m_OneHandedReloadAngle = reference.m_OneHandedReloadAngle; + m_ShakeRange = reference.m_ShakeRange; + m_SharpShakeRange = reference.m_SharpShakeRange; + m_NoSupportFactor = reference.m_NoSupportFactor; + m_ParticleSpreadRange = reference.m_ParticleSpreadRange; + m_ShellEjectAngle = reference.m_ShellEjectAngle; + m_ShellSpreadRange = reference.m_ShellSpreadRange; + m_ShellAngVelRange = reference.m_ShellAngVelRange; + m_ShellVelVariation = reference.m_ShellVelVariation; + m_RecoilScreenShakeAmount = reference.m_RecoilScreenShakeAmount; + m_MuzzleOff = reference.m_MuzzleOff; + m_EjectOff = reference.m_EjectOff; + m_MagOff = reference.m_MagOff; + m_RoundsFired = reference.m_RoundsFired; + m_IsAnimatedManually = reference.m_IsAnimatedManually; + + m_LegacyCompatibilityRoundsAlwaysFireUnflipped = reference.m_LegacyCompatibilityRoundsAlwaysFireUnflipped; + + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int HDFirearm::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return HeldDevice::ReadProperty(propName, reader)); + + MatchProperty("Magazine", { SetMagazine(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("Flash", { SetFlash(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("PreFireSound", { + m_PreFireSound = new SoundContainer; + reader >> m_PreFireSound; + }); + MatchProperty("FireSound", { + m_FireSound = new SoundContainer; + reader >> m_FireSound; + }); + MatchProperty("FireEchoSound", { + m_FireEchoSound = new SoundContainer; + reader >> m_FireEchoSound; + m_FireEchoSound->SetSoundOverlapMode(SoundContainer::SoundOverlapMode::RESTART); + }); + MatchProperty("ActiveSound", { + m_ActiveSound = new SoundContainer; + reader >> m_ActiveSound; + }); + MatchProperty("DeactivationSound", { + m_DeactivationSound = new SoundContainer; + reader >> m_DeactivationSound; + }); + MatchProperty("EmptySound", { + m_EmptySound = new SoundContainer; + reader >> m_EmptySound; + }); + MatchProperty("ReloadStartSound", { + m_ReloadStartSound = new SoundContainer; + reader >> m_ReloadStartSound; + }); + MatchProperty("ReloadEndSound", { + m_ReloadEndSound = new SoundContainer; + reader >> m_ReloadEndSound; + }); + MatchProperty("ReloadEndOffset", { reader >> m_ReloadEndOffset; }); + MatchProperty("RateOfFire", { reader >> m_RateOfFire; }); + MatchProperty("ActivationDelay", { reader >> m_ActivationDelay; }); + MatchProperty("DeactivationDelay", { reader >> m_DeactivationDelay; }); + MatchForwards("BaseReloadTime") MatchProperty("ReloadTime", { reader >> m_BaseReloadTime; }); + MatchProperty("FullAuto", { reader >> m_FullAuto; }); + MatchProperty("FireIgnoresThis", { reader >> m_FireIgnoresThis; }); + MatchProperty("Reloadable", { reader >> m_Reloadable; }); + MatchProperty("DualReloadable", { reader >> m_DualReloadable; }); + MatchProperty("OneHandedReloadTimeMultiplier", { reader >> m_OneHandedReloadTimeMultiplier; }); + MatchProperty("ReloadAngle", { reader >> m_ReloadAngle; }); + MatchProperty("OneHandedReloadAngle", { reader >> m_OneHandedReloadAngle; }); + MatchProperty("RecoilTransmission", { reader >> m_JointStiffness; }); + MatchProperty("IsAnimatedManually", { reader >> m_IsAnimatedManually; }); + MatchProperty("ShakeRange", { + reader >> m_ShakeRange; + m_ShakeRange /= 2; + }); + MatchProperty("SharpShakeRange", { + reader >> m_SharpShakeRange; + m_SharpShakeRange /= 2; + }); + MatchProperty("NoSupportFactor", { reader >> m_NoSupportFactor; }); + MatchProperty("ParticleSpreadRange", { + reader >> m_ParticleSpreadRange; + m_ParticleSpreadRange /= 2; + }); + MatchProperty("ShellEjectAngle", { reader >> m_ShellEjectAngle; }); + MatchProperty("ShellSpreadRange", { + reader >> m_ShellSpreadRange; + m_ShellSpreadRange /= 2; + }); + MatchProperty("ShellAngVelRange", { + reader >> m_ShellAngVelRange; + m_ShellAngVelRange /= 2; + }); + MatchProperty("ShellVelVariation", { reader >> m_ShellVelVariation; }); + MatchProperty("RecoilScreenShakeAmount", { reader >> m_RecoilScreenShakeAmount; }); + MatchProperty("MuzzleOffset", { reader >> m_MuzzleOff; }); + MatchProperty("EjectionOffset", { reader >> m_EjectOff; }); + MatchProperty("LegacyCompatibilityRoundsAlwaysFireUnflipped", { reader >> m_LegacyCompatibilityRoundsAlwaysFireUnflipped; }); + + EndPropertyList; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int HDFirearm::ReadProperty(const std::string_view &propName, Reader &reader) { - StartPropertyList(return HeldDevice::ReadProperty(propName, reader)); - - MatchProperty("Magazine", { SetMagazine(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("Flash", { SetFlash(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("PreFireSound", { - m_PreFireSound = new SoundContainer; - reader >> m_PreFireSound; - }); - MatchProperty("FireSound", { - m_FireSound = new SoundContainer; - reader >> m_FireSound; - }); - MatchProperty("FireEchoSound", { - m_FireEchoSound = new SoundContainer; - reader >> m_FireEchoSound; - m_FireEchoSound->SetSoundOverlapMode(SoundContainer::SoundOverlapMode::RESTART); - }); - MatchProperty("ActiveSound", { - m_ActiveSound = new SoundContainer; - reader >> m_ActiveSound; - }); - MatchProperty("DeactivationSound", { - m_DeactivationSound = new SoundContainer; - reader >> m_DeactivationSound; - }); - MatchProperty("EmptySound", { - m_EmptySound = new SoundContainer; - reader >> m_EmptySound; - }); - MatchProperty("ReloadStartSound", { - m_ReloadStartSound = new SoundContainer; - reader >> m_ReloadStartSound; - }); - MatchProperty("ReloadEndSound", { - m_ReloadEndSound = new SoundContainer; - reader >> m_ReloadEndSound; - }); - MatchProperty("ReloadEndOffset", { reader >> m_ReloadEndOffset; }); - MatchProperty("RateOfFire", { reader >> m_RateOfFire; }); - MatchProperty("ActivationDelay", { reader >> m_ActivationDelay; }); - MatchProperty("DeactivationDelay", { reader >> m_DeactivationDelay; }); - MatchForwards("BaseReloadTime") MatchProperty("ReloadTime", { reader >> m_BaseReloadTime; }); - MatchProperty("FullAuto", { reader >> m_FullAuto; }); - MatchProperty("FireIgnoresThis", { reader >> m_FireIgnoresThis; }); - MatchProperty("Reloadable", { reader >> m_Reloadable; }); - MatchProperty("DualReloadable", { reader >> m_DualReloadable; }); - MatchProperty("OneHandedReloadTimeMultiplier", { reader >> m_OneHandedReloadTimeMultiplier; }); - MatchProperty("ReloadAngle", { reader >> m_ReloadAngle; }); - MatchProperty("OneHandedReloadAngle", { reader >> m_OneHandedReloadAngle; }); - MatchProperty("RecoilTransmission", { reader >> m_JointStiffness; }); - MatchProperty("IsAnimatedManually", { reader >> m_IsAnimatedManually; }); - MatchProperty("ShakeRange", { - reader >> m_ShakeRange; - m_ShakeRange /= 2; - }); - MatchProperty("SharpShakeRange", { - reader >> m_SharpShakeRange; - m_SharpShakeRange /= 2; - }); - MatchProperty("NoSupportFactor", { reader >> m_NoSupportFactor; }); - MatchProperty("ParticleSpreadRange", { - reader >> m_ParticleSpreadRange; - m_ParticleSpreadRange /= 2; - }); - MatchProperty("ShellEjectAngle", { reader >> m_ShellEjectAngle; }); - MatchProperty("ShellSpreadRange", { - reader >> m_ShellSpreadRange; - m_ShellSpreadRange /= 2; - }); - MatchProperty("ShellAngVelRange", { - reader >> m_ShellAngVelRange; - m_ShellAngVelRange /= 2; - }); - MatchProperty("ShellVelVariation", { reader >> m_ShellVelVariation; }); - MatchProperty("RecoilScreenShakeAmount", { reader >> m_RecoilScreenShakeAmount; }); - MatchProperty("MuzzleOffset", { reader >> m_MuzzleOff; }); - MatchProperty("EjectionOffset", { reader >> m_EjectOff; }); - MatchProperty("LegacyCompatibilityRoundsAlwaysFireUnflipped", { reader >> m_LegacyCompatibilityRoundsAlwaysFireUnflipped; }); - - - EndPropertyList; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this HDFirearm with a Writer for + // later recreation with Create(Reader &reader); + + int HDFirearm::Save(Writer& writer) const { + HeldDevice::Save(writer); + + writer.NewProperty("Magazine"); + writer << m_pMagazine; + writer.NewProperty("Flash"); + writer << m_pFlash; + writer.NewProperty("PreFireSound"); + writer << m_PreFireSound; + writer.NewProperty("FireSound"); + writer << m_FireSound; + writer.NewProperty("FireEchoSound"); + writer << m_FireEchoSound; + writer.NewProperty("ActiveSound"); + writer << m_ActiveSound; + writer.NewProperty("DeactivationSound"); + writer << m_DeactivationSound; + writer.NewProperty("EmptySound"); + writer << m_EmptySound; + writer.NewProperty("ReloadStartSound"); + writer << m_ReloadStartSound; + writer.NewProperty("ReloadEndSound"); + writer << m_ReloadEndSound; + writer.NewPropertyWithValue("ReloadEndOffset", m_ReloadEndOffset); + writer.NewProperty("RateOfFire"); + writer << m_RateOfFire; + writer.NewProperty("ActivationDelay"); + writer << m_ActivationDelay; + writer.NewProperty("DeactivationDelay"); + writer << m_DeactivationDelay; + writer.NewProperty("ReloadTime"); + writer << m_BaseReloadTime; + writer.NewProperty("FullAuto"); + writer << m_FullAuto; + writer.NewProperty("FireIgnoresThis"); + writer << m_FireIgnoresThis; + writer.NewProperty("Reloadable"); + writer.NewPropertyWithValue("DualReloadable", m_DualReloadable); + writer.NewPropertyWithValue("OneHandedReloadTimeMultiplier", m_OneHandedReloadTimeMultiplier); + writer.NewPropertyWithValue("ReloadAngle", m_ReloadAngle); + writer.NewPropertyWithValue("OneHandedReloadAngle", m_OneHandedReloadAngle); + writer << m_Reloadable; + writer.NewProperty("RecoilTransmission"); + writer << m_JointStiffness; + writer.NewProperty("IsAnimatedManually"); + writer << m_IsAnimatedManually; + writer.NewProperty("ShakeRange"); + writer << m_ShakeRange * 2; + writer.NewProperty("SharpShakeRange"); + writer << m_SharpShakeRange * 2; + writer.NewProperty("NoSupportFactor"); + writer << m_NoSupportFactor; + writer.NewProperty("ParticleSpreadRange"); + writer << m_ParticleSpreadRange * 2; + writer.NewProperty("ShellEjectAngle"); + writer << m_ShellEjectAngle; + writer.NewProperty("ShellSpreadRange"); + writer << m_ShellSpreadRange * 2; + writer.NewProperty("ShellAngVelRange"); + writer << m_ShellAngVelRange * 2; + writer.NewProperty("ShellVelVariation"); + writer << m_ShellVelVariation; + writer.NewProperty("RecoilScreenShakeAmount"); + writer << m_RecoilScreenShakeAmount; + writer.NewProperty("MuzzleOffset"); + writer << m_MuzzleOff; + writer.NewProperty("EjectionOffset"); + writer << m_EjectOff; + + writer.NewPropertyWithValue("LegacyCompatibilityRoundsAlwaysFireUnflipped", m_LegacyCompatibilityRoundsAlwaysFireUnflipped); + + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the HDFirearm object. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this HDFirearm with a Writer for -// later recreation with Create(Reader &reader); - -int HDFirearm::Save(Writer &writer) const -{ - HeldDevice::Save(writer); - - writer.NewProperty("Magazine"); - writer << m_pMagazine; - writer.NewProperty("Flash"); - writer << m_pFlash; - writer.NewProperty("PreFireSound"); - writer << m_PreFireSound; - writer.NewProperty("FireSound"); - writer << m_FireSound; - writer.NewProperty("FireEchoSound"); - writer << m_FireEchoSound; - writer.NewProperty("ActiveSound"); - writer << m_ActiveSound; - writer.NewProperty("DeactivationSound"); - writer << m_DeactivationSound; - writer.NewProperty("EmptySound"); - writer << m_EmptySound; - writer.NewProperty("ReloadStartSound"); - writer << m_ReloadStartSound; - writer.NewProperty("ReloadEndSound"); - writer << m_ReloadEndSound; - writer.NewPropertyWithValue("ReloadEndOffset", m_ReloadEndOffset); - writer.NewProperty("RateOfFire"); - writer << m_RateOfFire; - writer.NewProperty("ActivationDelay"); - writer << m_ActivationDelay; - writer.NewProperty("DeactivationDelay"); - writer << m_DeactivationDelay; - writer.NewProperty("ReloadTime"); - writer << m_BaseReloadTime; - writer.NewProperty("FullAuto"); - writer << m_FullAuto; - writer.NewProperty("FireIgnoresThis"); - writer << m_FireIgnoresThis; - writer.NewProperty("Reloadable"); - writer.NewPropertyWithValue("DualReloadable", m_DualReloadable); - writer.NewPropertyWithValue("OneHandedReloadTimeMultiplier", m_OneHandedReloadTimeMultiplier); - writer.NewPropertyWithValue("ReloadAngle", m_ReloadAngle); - writer.NewPropertyWithValue("OneHandedReloadAngle", m_OneHandedReloadAngle); - writer << m_Reloadable; - writer.NewProperty("RecoilTransmission"); - writer << m_JointStiffness; - writer.NewProperty("IsAnimatedManually"); - writer << m_IsAnimatedManually; - writer.NewProperty("ShakeRange"); - writer << m_ShakeRange * 2; - writer.NewProperty("SharpShakeRange"); - writer << m_SharpShakeRange * 2; - writer.NewProperty("NoSupportFactor"); - writer << m_NoSupportFactor; - writer.NewProperty("ParticleSpreadRange"); - writer << m_ParticleSpreadRange * 2; - writer.NewProperty("ShellEjectAngle"); - writer << m_ShellEjectAngle; - writer.NewProperty("ShellSpreadRange"); - writer << m_ShellSpreadRange * 2; - writer.NewProperty("ShellAngVelRange"); - writer << m_ShellAngVelRange * 2; - writer.NewProperty("ShellVelVariation"); - writer << m_ShellVelVariation; - writer.NewProperty("RecoilScreenShakeAmount"); - writer << m_RecoilScreenShakeAmount; - writer.NewProperty("MuzzleOffset"); - writer << m_MuzzleOff; - writer.NewProperty("EjectionOffset"); - writer << m_EjectOff; - - writer.NewPropertyWithValue("LegacyCompatibilityRoundsAlwaysFireUnflipped", m_LegacyCompatibilityRoundsAlwaysFireUnflipped); - - return 0; -} + void HDFirearm::Destroy(bool notInherited) { + if (m_PreFireSound) { + m_PreFireSound->Stop(); + } + if (m_FireSound) { + m_FireSound->Stop(); + } + if (m_FireEchoSound) { + m_FireEchoSound->Stop(); + } + if (m_ActiveSound) { + m_ActiveSound->Stop(); + } + if (m_DeactivationSound) { + m_DeactivationSound->Stop(); + } + if (m_EmptySound) { + m_EmptySound->Stop(); + } + if (m_ReloadStartSound) { + m_ReloadStartSound->Stop(); + } + if (m_ReloadEndSound) { + m_ReloadEndSound->Stop(); + } + delete m_PreFireSound; + delete m_FireSound; + delete m_FireEchoSound; + delete m_ActiveSound; + delete m_DeactivationSound; + delete m_EmptySound; + delete m_ReloadStartSound; + delete m_ReloadEndSound; + + if (!notInherited) + HeldDevice::Destroy(); + Clear(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the HDFirearm object. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void HDFirearm::Destroy(bool notInherited) -{ - if (m_PreFireSound) { - m_PreFireSound->Stop(); + void HDFirearm::SetMagazine(Magazine* newMagazine) { + if (m_pMagazine && m_pMagazine->IsAttached()) { + RemoveAndDeleteAttachable(m_pMagazine); + } + if (newMagazine == nullptr) { + m_pMagazine = nullptr; + } else { + m_pMagazine = newMagazine; + AddAttachable(newMagazine); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newMagazine->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + Magazine* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetMagazine"); + dynamic_cast(parent)->SetMagazine(castedAttachable); + }}); + + const Entity* newMagazineReference = g_PresetMan.GetEntityPreset(newMagazine->GetClassName(), newMagazine->GetPresetName(), newMagazine->GetModuleID()); + if (newMagazineReference) { + m_pMagazineReference = dynamic_cast(newMagazineReference); + } + } } - if (m_FireSound) { - m_FireSound->Stop(); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void HDFirearm::SetFlash(Attachable* newFlash) { + if (m_pFlash && m_pFlash->IsAttached()) { + RemoveAndDeleteAttachable(m_pFlash); + } + if (newFlash == nullptr) { + m_pFlash = nullptr; + } else { + // Note - this is done here because setting mass on attached Attachables causes values to be updated on the parent (and its parent, and so on), which isn't ideal. Better to do it before the new flash is attached, so there are fewer calculations. + newFlash->SetMass(0.0F); + + m_pFlash = newFlash; + AddAttachable(newFlash); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newFlash->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + dynamic_cast(parent)->SetFlash(attachable); + }}); + + m_pFlash->SetDrawnNormallyByParent(false); + m_pFlash->SetDeleteWhenRemovedFromParent(true); + m_pFlash->SetCollidesWithTerrainWhileAttached(false); + } } - if (m_FireEchoSound) { - m_FireEchoSound->Stop(); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + std::string HDFirearm::GetNextMagazineName() const { + return m_pMagazineReference->GetPresetName(); } - if (m_ActiveSound) { - m_ActiveSound->Stop(); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool HDFirearm::SetNextMagazineName(std::string magName) { + const Magazine* pNewMag = dynamic_cast(g_PresetMan.GetEntityPreset("Magazine", magName)); + if (pNewMag) { + m_pMagazineReference = pNewMag; + m_AIFireVel = -1; + m_AIBulletLifeTime = 0; + m_AIBulletAccScalar = -1; + + return true; + } + return false; } - if (m_DeactivationSound) { - m_DeactivationSound->Stop(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRoundInMagCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of rounds still in the loaded magazine. + + int HDFirearm::GetRoundInMagCount() const { + return m_pMagazine ? m_pMagazine->GetRoundCount() : 0; } - if (m_EmptySound) { - m_EmptySound->Stop(); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int HDFirearm::GetRoundInMagCapacity() const { + if (m_pMagazine) { + return m_pMagazine->GetCapacity(); + } else if (m_pMagazineReference) { + return m_pMagazineReference->GetCapacity(); + } + return 0; } - if (m_ReloadStartSound) { - m_ReloadStartSound->Stop(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIFireVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the velocity the AI use when aiming this weapon + + float HDFirearm::GetAIFireVel() { + if (m_AIFireVel < 0 && m_pMagazine) + m_AIFireVel = m_pMagazine->GetAIAimVel(); + + return m_AIFireVel; } - if (m_ReloadEndSound) { - m_ReloadEndSound->Stop(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIBulletLifeTime + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the bullet life time the AI use when aiming this weapon. + + unsigned long HDFirearm::GetAIBulletLifeTime() { + if (m_AIBulletLifeTime == 0 && m_pMagazine) { + const Round* pRound = m_pMagazine->GetNextRound(); + if (pRound) { + m_AIBulletLifeTime = pRound->GetAILifeTime(); + + // Set a default if the lifetime is zero (i.e. infinite) + if (m_AIBulletLifeTime == 0) + m_AIBulletLifeTime = 20000; + } + } + + return m_AIBulletLifeTime; } - delete m_PreFireSound; - delete m_FireSound; - delete m_FireEchoSound; - delete m_ActiveSound; - delete m_DeactivationSound; - delete m_EmptySound; - delete m_ReloadStartSound; - delete m_ReloadEndSound; - - if (!notInherited) - HeldDevice::Destroy(); - Clear(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void HDFirearm::SetMagazine(Magazine *newMagazine) { - if (m_pMagazine && m_pMagazine->IsAttached()) { RemoveAndDeleteAttachable(m_pMagazine); } - if (newMagazine == nullptr) { - m_pMagazine = nullptr; - } else { - m_pMagazine = newMagazine; - AddAttachable(newMagazine); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newMagazine->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - Magazine *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetMagazine"); - dynamic_cast(parent)->SetMagazine(castedAttachable); - }}); - - const Entity *newMagazineReference = g_PresetMan.GetEntityPreset(newMagazine->GetClassName(), newMagazine->GetPresetName(), newMagazine->GetModuleID()); - if (newMagazineReference) { m_pMagazineReference = dynamic_cast(newMagazineReference); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void HDFirearm::SetFlash(Attachable *newFlash) { - if (m_pFlash && m_pFlash->IsAttached()) { RemoveAndDeleteAttachable(m_pFlash); } - if (newFlash == nullptr) { - m_pFlash = nullptr; - } else { - // Note - this is done here because setting mass on attached Attachables causes values to be updated on the parent (and its parent, and so on), which isn't ideal. Better to do it before the new flash is attached, so there are fewer calculations. - newFlash->SetMass(0.0F); - - m_pFlash = newFlash; - AddAttachable(newFlash); - - m_HardcodedAttachableUniqueIDsAndSetters.insert({newFlash->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - dynamic_cast(parent)->SetFlash(attachable); - }}); - - m_pFlash->SetDrawnNormallyByParent(false); - m_pFlash->SetDeleteWhenRemovedFromParent(true); - m_pFlash->SetCollidesWithTerrainWhileAttached(false); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -std::string HDFirearm::GetNextMagazineName() const { - return m_pMagazineReference->GetPresetName(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool HDFirearm::SetNextMagazineName(std::string magName) -{ - const Magazine * pNewMag = dynamic_cast(g_PresetMan.GetEntityPreset("Magazine", magName)); - if (pNewMag) - { - m_pMagazineReference = pNewMag; - m_AIFireVel = -1; - m_AIBulletLifeTime = 0; - m_AIBulletAccScalar = -1; - - return true; - } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBulletAccScalar + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. + float HDFirearm::GetBulletAccScalar() { + if (m_AIBulletAccScalar < 0 && m_pMagazine) + m_AIBulletAccScalar = m_pMagazine->GetBulletAccScalar(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetRoundInMagCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of rounds still in the loaded magazine. + return m_AIBulletAccScalar; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIBlastRadius + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the blast radius the AI use when aiming this weapon + + float HDFirearm::GetAIBlastRadius() const { + int radius = -1; + if (m_pMagazine) + radius = m_pMagazine->GetAIAimBlastRadius(); + + if (radius < 0) { + // Set default value + if (m_IsExplosiveWeapon) + radius = 100; + else + radius = 0; + } + + return radius; + } -int HDFirearm::GetRoundInMagCount() const -{ - return m_pMagazine ? m_pMagazine->GetRoundCount() : 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIPenetration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how much material the projectiles from this weapon can destory. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float HDFirearm::GetAIPenetration() const { + if (m_pMagazine) + return m_pMagazine->GetAIAimPenetration(); -int HDFirearm::GetRoundInMagCapacity() const { - if (m_pMagazine) { - return m_pMagazine->GetCapacity(); - } else if (m_pMagazineReference) { - return m_pMagazineReference->GetCapacity(); + return 0; } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CompareTrajectories + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Estimates how close the projectiles from two weapons will land. + + float HDFirearm::CompareTrajectories(HDFirearm* pWeapon) { + if (pWeapon) { + // Get AI aim data and cap life time to one second + unsigned long LifeTime1 = GetAIBulletLifeTime(); + if (LifeTime1 == 0 || LifeTime1 > 1000) + LifeTime1 = 1000; + + unsigned long LifeTime2 = GetAIBulletLifeTime(); + if (LifeTime2 == 0 || LifeTime2 > 1000) + LifeTime2 = 1000; + + float time = std::max(std::min(LifeTime1, LifeTime2) / 1000.0f, 0.5f); + Vector Vel1 = Vector(GetAIFireVel(), 0); + Vector Vel2 = Vector(pWeapon->GetAIFireVel(), 0); + + // Estimate the hit pos according to: FuturePos=Pos+Vel+Accel*(t*t*0.5) + time = time * time * 0.5; + Vector FuturePos1 = GetMuzzlePos(); + g_SceneMan.WrapPosition(FuturePos1); + FuturePos1 = FuturePos1 * c_MPP + RotateOffset(Vel1) + g_SceneMan.GetGlobalAcc() * GetBulletAccScalar() * time; + + Vector FuturePos2 = GetMuzzlePos(); + g_SceneMan.WrapPosition(FuturePos2); + FuturePos2 = pWeapon->GetMuzzlePos() * c_MPP + RotateOffset(Vel2) + g_SceneMan.GetGlobalAcc() * pWeapon->GetBulletAccScalar() * time; + + return (FuturePos2 - FuturePos1).GetMagnitude() * c_PPM; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIFireVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the velocity the AI use when aiming this weapon + return 100000; + } -float HDFirearm::GetAIFireVel() -{ - if (m_AIFireVel < 0 && m_pMagazine) - m_AIFireVel = m_pMagazine->GetAIAimVel(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMagazinePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of the magazine or other equivalent point of + // this. - return m_AIFireVel; -} + Vector HDFirearm::GetMagazinePos() const { + return m_Pos + RotateOffset(m_MagOff); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: GetMuzzlePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of the muzzle or other equivalent point of + // this. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIBulletLifeTime -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the bullet life time the AI use when aiming this weapon. + Vector HDFirearm::GetMuzzlePos() const { + return m_Pos + RotateOffset(m_MuzzleOff); + } -unsigned long HDFirearm::GetAIBulletLifeTime() -{ - if (m_AIBulletLifeTime == 0 && m_pMagazine) - { - const Round * pRound = m_pMagazine->GetNextRound(); - if (pRound) - { - m_AIBulletLifeTime = pRound->GetAILifeTime(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Set a default if the lifetime is zero (i.e. infinite) - if (m_AIBulletLifeTime == 0) - m_AIBulletLifeTime = 20000; - } - } + void HDFirearm::RestDetection() { + HeldDevice::RestDetection(); - return m_AIBulletLifeTime; -} + if (m_FiredOnce) { + m_RestTimer.Reset(); + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Activate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activates one of this HDFirearm's features. Analogous to 'pulling + // the trigger'. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBulletAccScalar -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. + void HDFirearm::Activate() { + bool wasActivated = m_Activated; + HeldDevice::Activate(); -float HDFirearm::GetBulletAccScalar() -{ - if (m_AIBulletAccScalar < 0 && m_pMagazine) - m_AIBulletAccScalar = m_pMagazine->GetBulletAccScalar(); + if (!IsReloading()) { + if (m_DeactivationSound && m_DeactivationSound->IsBeingPlayed()) { + m_DeactivationSound->FadeOut(); + } + if (m_ActiveSound && !m_ActiveSound->IsBeingPlayed() && (m_ActiveSound->GetLoopSetting() == -1 || !wasActivated)) { + m_ActiveSound->Play(this->m_Pos); + } + if (m_PreFireSound && !wasActivated && !m_PreFireSound->IsBeingPlayed()) { + m_PreFireSound->Play(this->m_Pos); + } + } + } - return m_AIBulletAccScalar; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Deactivate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing + // the trigger'. + void HDFirearm::Deactivate() { + bool wasActivated = m_Activated; + HeldDevice::Deactivate(); + m_FiredOnce = false; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIBlastRadius -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the blast radius the AI use when aiming this weapon + if (m_PreFireSound) { + m_PreFireSound->Stop(); + } + if (m_FireSound && m_FireSound->GetLoopSetting() == -1) { + m_FireSound->Stop(); + } + if (m_DeactivationSound && wasActivated && m_FiredLastFrame) { + m_DeactivationSound->Play(m_Pos); + } + } -float HDFirearm::GetAIBlastRadius() const -{ - int radius = -1; - if (m_pMagazine) - radius = m_pMagazine->GetAIAimBlastRadius(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StopActivationSound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Aborts playing of active sound no matter what. Used to silence spinning + // weapons when weapons swapped + + void HDFirearm::StopActivationSound() { + if (m_ActiveSound && m_ActiveSound->IsBeingPlayed()) + m_ActiveSound->Stop(); + + // TODO: Also stop any animation + // Those don't work really, at least we stopped it from making noise + // m_Frame = 0; + // m_Activated = false; + // m_LastFireTmr.SetElapsedSimTimeMS(m_DeactivationDelay + 1); + } - if (radius < 0) - { - // Set default value - if (m_IsExplosiveWeapon) - radius = 100; - else - radius = 0; - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return radius; -} + void HDFirearm::Reload() { + if (!m_Reloading && m_Reloadable) { + bool hadMagazineBeforeReloading = m_pMagazine != nullptr; + if (hadMagazineBeforeReloading) { + Vector constrainedMagazineOffset = g_SceneMan.ShortestDistance(m_Pos, m_pMagazine->GetPos(), g_SceneMan.SceneWrapsX()).SetMagnitude(2.0F); + Vector ejectVector = Vector(2.0F * GetFlipFactor(), 0.0F) + constrainedMagazineOffset.RadRotate(RandomNum(-0.2F, 0.2F)); + m_pMagazine->SetVel(m_Vel + ejectVector); + m_pMagazine->SetAngularVel(RandomNum(-3.0F, 3.0F)); + if (!m_pMagazine->IsDiscardable()) { + m_pMagazine->SetToDelete(); + } + RemoveAttachable(m_pMagazine, m_pMagazine->IsDiscardable(), false); + m_pMagazine = 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIPenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how much material the projectiles from this weapon can destory. + Deactivate(); + if (m_ReloadStartSound) { + m_ReloadStartSound->Play(m_Pos); + } -float HDFirearm::GetAIPenetration() const -{ - if (m_pMagazine) - return m_pMagazine->GetAIAimPenetration(); + m_ReloadTmr.Reset(); + CorrectReloadTimerForSupportAvailable(); - return 0; -} + RunScriptedFunctionInAppropriateScripts("OnReload", false, false, {}, {hadMagazineBeforeReloading ? "true" : "false"}); + m_Reloading = true; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CompareTrajectories -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Estimates how close the projectiles from two weapons will land. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -float HDFirearm::CompareTrajectories(HDFirearm * pWeapon) -{ - if (pWeapon) - { - // Get AI aim data and cap life time to one second - unsigned long LifeTime1 = GetAIBulletLifeTime(); - if (LifeTime1 == 0 || LifeTime1 > 1000) - LifeTime1 = 1000; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: NeedsReloading + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is curtrently in need of being reloaded. - unsigned long LifeTime2 = GetAIBulletLifeTime(); - if (LifeTime2 == 0 || LifeTime2 > 1000) - LifeTime2 = 1000; + bool HDFirearm::NeedsReloading() const { + if (!m_Reloading && m_Reloadable) { + if (m_pMagazine) { + // If we've used over half the rounds, we can profitably go ahead and reload + return !m_pMagazine->IsOverHalfFull(); + } + return true; + } + // We're currently reloading + return false; + } - float time = std::max(std::min(LifeTime1, LifeTime2) / 1000.0f, 0.5f); - Vector Vel1 = Vector(GetAIFireVel(), 0); - Vector Vel2 = Vector(pWeapon->GetAIFireVel(), 0); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsFull + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is curtrently full and reloading won't have + // any effect. + + bool HDFirearm::IsFull() const { + if (!m_Reloading && m_Reloadable) { + if (m_pMagazine) { + // If we've used over half the rounds, we can profitably go ahead and reload + return m_pMagazine->GetRoundCount() == m_pMagazine->GetCapacity() || m_pMagazine->GetCapacity() < 0; + } + return false; + } + // We're currently reloading + return true; + } - // Estimate the hit pos according to: FuturePos=Pos+Vel+Accel*(t*t*0.5) - time = time * time * 0.5; - Vector FuturePos1 = GetMuzzlePos(); - g_SceneMan.WrapPosition(FuturePos1); - FuturePos1 = FuturePos1 * c_MPP + RotateOffset(Vel1) + g_SceneMan.GetGlobalAcc() * GetBulletAccScalar() * time; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector FuturePos2 = GetMuzzlePos(); - g_SceneMan.WrapPosition(FuturePos2); - FuturePos2 = pWeapon->GetMuzzlePos() * c_MPP + RotateOffset(Vel2) + g_SceneMan.GetGlobalAcc() * pWeapon->GetBulletAccScalar() * time; + bool HDFirearm::IsEmpty() const { + if (m_pMagazine) { + return m_pMagazine->IsEmpty(); + } + return true; + } - return (FuturePos2 - FuturePos1).GetMagnitude() * c_PPM; - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return 100000; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this HDFirearm. Supposed to be done every frame. + void HDFirearm::Update() { + HeldDevice::Update(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMagazinePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of the magazine or other equivalent point of -// this. + if (m_PreFireSound && m_PreFireSound->IsBeingPlayed()) { + m_PreFireSound->SetPosition(m_Pos); + } + if (m_FireSound && m_FireSound->IsBeingPlayed()) { + m_FireSound->SetPosition(m_Pos); + } + if (m_ActiveSound && m_ActiveSound->IsBeingPlayed()) { + m_ActiveSound->SetPosition(m_Pos); + } + if (m_DeactivationSound && m_DeactivationSound->IsBeingPlayed()) { + m_DeactivationSound->SetPosition(m_Pos); + } -Vector HDFirearm::GetMagazinePos() const -{ - return m_Pos + RotateOffset(m_MagOff); -} + Actor* pActor = dynamic_cast(GetRootParent()); + + ///////////////////////////////// + // Activation/firing logic + + int roundsFired = 0; + m_RoundsFired = 0; + float degAimAngle = m_Rotation.GetDegAngle(); + degAimAngle = m_HFlipped ? (180.0F + degAimAngle) : degAimAngle; + float totalFireForce = 0.0F; + m_FireFrame = false; + m_DoneReloading = false; + bool playedRoundFireSound = false; + + if (m_pMagazine && !m_pMagazine->IsEmpty()) { + if (m_Activated && !(m_PreFireSound && m_PreFireSound->IsBeingPlayed())) { + + double msPerRound = GetMSPerRound(); + if (m_FullAuto) { + // First round should fly as soon as activated and the delays are taken into account + if (!m_FiredOnce && (m_LastFireTmr.GetElapsedSimTimeMS() - m_DeactivationDelay - m_ActivationDelay) > msPerRound) { + roundsFired = 1; + // Wind back the last fire timer appropriately for the first round, but not farther back than 0 + m_LastFireTmr.SetElapsedSimTimeMS(std::max(m_LastFireTmr.GetElapsedSimTimeMS() - msPerRound, 0.0)); + } + // How many rounds are going to fly since holding down activation. Make sure gun can't be fired faster by tapping activation fast + if (m_LastFireTmr.GetElapsedSimTimeMS() > (m_ActivationTimer.GetElapsedSimTimeMS() - m_ActivationDelay)) { + roundsFired += (m_ActivationTimer.GetElapsedSimTimeMS() - m_ActivationDelay) / msPerRound; + } else { + roundsFired += m_LastFireTmr.GetElapsedSimTimeMS() / msPerRound; + } + } else { + // TODO: Confirm that the delays work properly in semi-auto! + roundsFired = !m_FiredOnce && (m_LastFireTmr.GetElapsedSimTimeMS() - m_ActivationDelay - m_DeactivationDelay) > msPerRound ? 1 : 0; + } + if (roundsFired >= 1) { + m_FiredOnce = true; + m_FireFrame = true; + m_LastFireTmr.Reset(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: GetMuzzlePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of the muzzle or other equivalent point of -// this. + Vector roundVel; + Vector shellVel; -Vector HDFirearm::GetMuzzlePos() const -{ - return m_Pos + RotateOffset(m_MuzzleOff); -} + Round* pRound = 0; + Vector tempNozzle; + Vector tempEject; + MOPixel* pPixel; + float shake, particleSpread, shellSpread, lethalRange; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + lethalRange = m_MaxSharpLength * m_SharpAim + std::max(g_FrameMan.GetPlayerFrameBufferWidth(-1), g_FrameMan.GetPlayerFrameBufferHeight(-1)) * 0.51F; + if (pActor) { + lethalRange += pActor->GetAimDistance(); + } -void HDFirearm::RestDetection() { - HeldDevice::RestDetection(); + // Fire all rounds that were fired this frame. + for (int i = 0; i < roundsFired && !m_pMagazine->IsEmpty(); ++i) { + m_RoundsFired++; + + pRound = m_pMagazine->PopNextRound(); + shake = (m_ShakeRange - ((m_ShakeRange - m_SharpShakeRange) * m_SharpAim)) * + (m_Supported ? 1.0F : m_NoSupportFactor) * RandomNormalNum(); + tempNozzle = m_MuzzleOff.GetYFlipped(m_HFlipped); + tempNozzle.DegRotate(degAimAngle + shake); + roundVel.SetXY(pRound->GetFireVel(), 0); + roundVel.DegRotate(degAimAngle + shake); + + Vector particlePos; + Vector particleVel; + int particleCountMax = pRound->ParticleCount(); + float lifeVariation = pRound->GetLifeVariation(); + + // Launch all particles in round + MovableObject* pParticle = 0; + while (!pRound->IsEmpty()) { + pParticle = pRound->PopNextParticle(); + + // Only make the particles separate back behind the nozzle, not in front. This is to avoid silly penetration firings + particlePos = tempNozzle + (roundVel.GetNormalized() * (-RandomNum()) * pRound->GetSeparation()); + pParticle->SetPos(m_Pos + particlePos); + + particleVel = roundVel; + particleSpread = m_ParticleSpreadRange * RandomNormalNum(); + particleVel.DegRotate(particleSpread); + pParticle->SetVel(pRound->GetInheritsFirerVelocity() ? (m_Vel + particleVel) : particleVel); + if (m_LegacyCompatibilityRoundsAlwaysFireUnflipped) { + pParticle->SetRotAngle(particleVel.GetAbsRadAngle()); + } else { + pParticle->SetRotAngle(particleVel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); + pParticle->SetHFlipped(m_HFlipped); + } + if (lifeVariation != 0 && pParticle->GetLifetime() != 0) { + pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + (particleCountMax > 1 ? lifeVariation - (lifeVariation * 2.0F * (static_cast(pRound->ParticleCount()) / static_cast(particleCountMax - 1))) : lifeVariation * RandomNormalNum()))), 1)); + } + // F = m * a + totalFireForce += pParticle->GetMass() * pParticle->GetVel().GetMagnitude(); + + // Remove from parent if it's an attachable + Attachable* pAttachable = dynamic_cast(pParticle); + if (pAttachable) { + if (pAttachable->IsAttached()) { + pAttachable->GetParent()->RemoveAttachable(pAttachable); + } + + // Activate if it is some kind of grenade or whatnot. + ThrownDevice* pTD = dynamic_cast(pAttachable); + if (pTD) { + pTD->Activate(); + } + } + + // Set the fired particle to not hit this HeldDevice's parent, if applicable + if (m_FireIgnoresThis) + pParticle->SetWhichMOToNotHit(this, 1.0f); + + // Set the team so alarm events that happen if these gib won't freak out the guy firing + pParticle->SetTeam(m_Team); + + // Also make this not hit team members + // TODO: Don't hardcode this??? + pParticle->SetIgnoresTeamHits(true); + + // Decide for how long until the bullet tumble and start to lose lethality + pPixel = dynamic_cast(pParticle); + if (pPixel) { + // Stray bullets heavily affected by bullet shake lose lethality quicker, as if missing on an imaginary "Z" axis + lethalRange *= std::max(1.0F - std::abs(shake) / 20.0F, 0.1F); + pPixel->SetLethalRange(lethalRange); + } + g_MovableMan.AddParticle(pParticle); + } + pParticle = 0; + + // Launch shell, if there is one. + MovableObject* pShell = pRound->GetShell() ? dynamic_cast(pRound->GetShell()->Clone()) : 0; + if (pShell) { + tempEject = m_EjectOff.GetYFlipped(m_HFlipped); + shellSpread = m_ShellSpreadRange * RandomNormalNum(); + tempEject.DegRotate(degAimAngle + shellSpread); + pShell->SetPos(m_Pos + tempEject); + + // ##@#@@$ TEMP + shellVel.SetXY(pRound->GetShellVel() * (1.0F - RandomNum(0.0F, m_ShellVelVariation)), 0); + shellVel.DegRotate(degAimAngle + m_ShellEjectAngle * (m_HFlipped ? -1 : 1) + shellSpread); + pShell->SetVel(m_Vel + shellVel); + pShell->SetRotAngle(m_Rotation.GetRadAngle()); + pShell->SetAngularVel(pShell->GetAngularVel() + (m_ShellAngVelRange * RandomNormalNum())); + pShell->SetHFlipped(m_HFlipped); + // Set the ejected shell to not hit this HeldDevice's parent, if applicable + if (m_FireIgnoresThis) + pShell->SetWhichMOToNotHit(this, 1.0f); + // Set the team so alarm events that happen if these gib won't freak out the guy firing + pShell->SetTeam(m_Team); + // Set this to ignore team hits in case it's lethal + // TODO: Don't hardcode this??? + pShell->SetIgnoresTeamHits(true); + g_MovableMan.AddParticle(pShell); + pShell = 0; + } - if (m_FiredOnce) { m_RestTimer.Reset(); } -} + // Sound the extra Round firing sound, if any is defined + if (!playedRoundFireSound && pRound->HasFireSound()) { + pRound->GetFireSound()->Play(m_Pos); + playedRoundFireSound = true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Activate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activates one of this HDFirearm's features. Analogous to 'pulling -// the trigger'. + delete pRound; + } + pRound = 0; -void HDFirearm::Activate() { - bool wasActivated = m_Activated; - HeldDevice::Activate(); + if (m_FireFrame) { + RunScriptedFunctionInAppropriateScripts("OnFire", false, false); + } + } else { + m_ActivationTimer.Reset(); + } + } else if (m_Activated && !m_AlreadyClicked) { + // Play empty pin click sound. + if (m_EmptySound) { + m_EmptySound->Play(m_Pos); + } + // Indicate that we have clicked once during the current activation. + m_AlreadyClicked = true; + } - if (!IsReloading()) { - if (m_DeactivationSound && m_DeactivationSound->IsBeingPlayed()) { m_DeactivationSound->FadeOut(); } - if (m_ActiveSound && !m_ActiveSound->IsBeingPlayed() && (m_ActiveSound->GetLoopSetting() == -1 || !wasActivated)) { m_ActiveSound->Play(this->m_Pos); } - if (m_PreFireSound && !wasActivated && !m_PreFireSound->IsBeingPlayed()) { m_PreFireSound->Play(this->m_Pos); } - } -} + if (m_Reloading && m_ReloadEndSound) { + // x0.5 the sound length generally just lines up better and leaves less dead air assuming a normal attempt at a ReloadEnd sound + float offsetMilliseconds = m_ReloadEndOffset == -1.0F ? m_ReloadEndSound->GetLength(SoundContainer::LengthOfSoundType::NextPlayed) * 0.5f : m_ReloadEndOffset; + bool shouldPlay = !m_HasPlayedEndReloadSound && m_ReloadTmr.LeftTillSimTimeLimitMS() <= offsetMilliseconds; + if (shouldPlay) { + m_ReloadEndSound->Play(m_Pos); + m_HasPlayedEndReloadSound = true; + } + } + if (m_Reloading && !m_pMagazine && m_pMagazineReference && m_ReloadTmr.IsPastSimTimeLimit()) { + SetMagazine(dynamic_cast(m_pMagazineReference->Clone())); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Deactivate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing -// the trigger'. + m_ActivationTimer.Reset(); + m_LastFireTmr.Reset(); -void HDFirearm::Deactivate() { - bool wasActivated = m_Activated; - HeldDevice::Deactivate(); - m_FiredOnce = false; + if (m_PreFireSound && m_Activated) { + m_PreFireSound->Play(); + } - if (m_PreFireSound) { m_PreFireSound->Stop(); } - if (m_FireSound && m_FireSound->GetLoopSetting() == -1) { m_FireSound->Stop(); } - if (m_DeactivationSound && wasActivated && m_FiredLastFrame) { m_DeactivationSound->Play(m_Pos); } -} + m_HasPlayedEndReloadSound = false; + m_Reloading = false; + m_DoneReloading = true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StopActivationSound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Aborts playing of active sound no matter what. Used to silence spinning -// weapons when weapons swapped - -void HDFirearm::StopActivationSound() -{ - if (m_ActiveSound && m_ActiveSound->IsBeingPlayed()) - m_ActiveSound->Stop(); - - //TODO: Also stop any animation - //Those don't work really, at least we stopped it from making noise - //m_Frame = 0; - //m_Activated = false; - //m_LastFireTmr.SetElapsedSimTimeMS(m_DeactivationDelay + 1); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void HDFirearm::Reload() { - if (!m_Reloading && m_Reloadable) { - bool hadMagazineBeforeReloading = m_pMagazine != nullptr; - if (hadMagazineBeforeReloading) { - Vector constrainedMagazineOffset = g_SceneMan.ShortestDistance(m_Pos, m_pMagazine->GetPos(), g_SceneMan.SceneWrapsX()).SetMagnitude(2.0F); - Vector ejectVector = Vector(2.0F * GetFlipFactor(), 0.0F) + constrainedMagazineOffset.RadRotate(RandomNum(-0.2F, 0.2F)); - m_pMagazine->SetVel(m_Vel + ejectVector); - m_pMagazine->SetAngularVel(RandomNum(-3.0F, 3.0F)); - - if (!m_pMagazine->IsDiscardable()) { m_pMagazine->SetToDelete(); } - RemoveAttachable(m_pMagazine, m_pMagazine->IsDiscardable(), false); - m_pMagazine = 0; - } - - Deactivate(); - if (m_ReloadStartSound) { m_ReloadStartSound->Play(m_Pos); } + // Do stuff to deactivate after being activated + if (!m_Activated) { + // Reset the click indicator. + m_AlreadyClicked = false; - m_ReloadTmr.Reset(); - CorrectReloadTimerForSupportAvailable(); + // Stop any looping activation sounds + if (m_FireSound && m_FireSound->GetLoopSetting() == -1) // && m_FireSound->IsBeingPlayed()) + m_FireSound->Stop(); + } - RunScriptedFunctionInAppropriateScripts("OnReload", false, false, {}, { hadMagazineBeforeReloading ? "true" : "false" }); + ////////////////////////////////////////////// + // Recoil and other activation effects logic. - m_Reloading = true; - } -} + // TODO: don't use arbitrary numbers? + m_RecoilForce.SetMagnitude(std::max(m_RecoilForce.GetMagnitude() * 0.7F - 1.0F, 0.0F)); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (roundsFired > 0) { + // Alternate to get that shake effect! + m_Recoiled = !m_Recoiled; + // Set up the recoil force and shake offsets + if (m_Recoiled) { + m_RecoilForce.SetXY(totalFireForce * m_JointStiffness, 0); + m_RecoilForce = RotateOffset(m_RecoilForce); + m_RecoilForce = -m_RecoilForce; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: NeedsReloading -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is curtrently in need of being reloaded. - -bool HDFirearm::NeedsReloading() const -{ - if (!m_Reloading && m_Reloadable) { - if (m_pMagazine) - { - // If we've used over half the rounds, we can profitably go ahead and reload - return !m_pMagazine->IsOverHalfFull(); - } - return true; - } - // We're currently reloading - return false; -} + // Set up the recoil shake offset + m_RecoilOffset = m_RecoilForce; + m_RecoilOffset.SetMagnitude(std::min(m_RecoilOffset.GetMagnitude(), 1.0F)); + } + // Screen shake + if (pActor) { + int controllingPlayer = pActor->GetController()->GetPlayer(); + int screenId = g_ActivityMan.GetActivity()->ScreenOfPlayer(controllingPlayer); + if (screenId != -1) { + const float shakiness = g_CameraMan.GetDefaultShakePerUnitOfRecoilEnergy(); + const float maxShakiness = g_CameraMan.GetDefaultShakeFromRecoilMaximum(); // Some weapons fire huge rounds, so restrict the amount + float screenShakeAmount = m_RecoilScreenShakeAmount == -1.0F ? std::min(totalFireForce * m_JointStiffness * shakiness, maxShakiness) : m_RecoilScreenShakeAmount; + g_CameraMan.ApplyScreenShake(screenShakeAmount, screenId); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsFull -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is curtrently full and reloading won't have -// any effect. - -bool HDFirearm::IsFull() const -{ - if (!m_Reloading && m_Reloadable) { - if (m_pMagazine) - { - // If we've used over half the rounds, we can profitably go ahead and reload - return m_pMagazine->GetRoundCount() == m_pMagazine->GetCapacity() || m_pMagazine->GetCapacity() < 0; - } - return false; - } - // We're currently reloading - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool HDFirearm::IsEmpty() const { - if (m_pMagazine) { - return m_pMagazine->IsEmpty(); - } - return true; -} + AddImpulseForce(m_RecoilForce, m_RecoilOffset); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Display gun animation + if (!m_IsAnimatedManually && m_FrameCount > 1) { + m_Frame = 1; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this HDFirearm. Supposed to be done every frame. - -void HDFirearm::Update() -{ - HeldDevice::Update(); - - if (m_PreFireSound && m_PreFireSound->IsBeingPlayed()) { m_PreFireSound->SetPosition(m_Pos); } - if (m_FireSound && m_FireSound->IsBeingPlayed()) { m_FireSound->SetPosition(m_Pos); } - if (m_ActiveSound && m_ActiveSound->IsBeingPlayed()) { m_ActiveSound->SetPosition(m_Pos); } - if (m_DeactivationSound && m_DeactivationSound->IsBeingPlayed()) { m_DeactivationSound->SetPosition(m_Pos); } - - Actor *pActor = dynamic_cast(GetRootParent()); - - ///////////////////////////////// - // Activation/firing logic - - int roundsFired = 0; - m_RoundsFired = 0; - float degAimAngle = m_Rotation.GetDegAngle(); - degAimAngle = m_HFlipped ? (180.0F + degAimAngle) : degAimAngle; - float totalFireForce = 0.0F; - m_FireFrame = false; - m_DoneReloading = false; - bool playedRoundFireSound = false; - - if (m_pMagazine && !m_pMagazine->IsEmpty()) - { - if (m_Activated && !(m_PreFireSound && m_PreFireSound->IsBeingPlayed())) { - - double msPerRound = GetMSPerRound(); - if (m_FullAuto) { - // First round should fly as soon as activated and the delays are taken into account - if (!m_FiredOnce && (m_LastFireTmr.GetElapsedSimTimeMS() - m_DeactivationDelay - m_ActivationDelay) > msPerRound) { - roundsFired = 1; - // Wind back the last fire timer appropriately for the first round, but not farther back than 0 - m_LastFireTmr.SetElapsedSimTimeMS(std::max(m_LastFireTmr.GetElapsedSimTimeMS() - msPerRound, 0.0)); + // Display gun flame frame. + if (m_pFlash) { + m_pFlash->SetParentOffset(m_MuzzleOff); + m_pFlash->SetFrame(RandomNum(0, m_pFlash->GetFrameCount() - 1)); + } + + // Play firing sound + // Only start playing if it's not a looping fire sound that is already playing, and if there's a mag + if (m_pMagazine) { + if (m_FireSound && !(m_FireSound->GetLoopSetting() == -1 && m_FireSound->IsBeingPlayed())) { + m_FireSound->Play(m_Pos); } - // How many rounds are going to fly since holding down activation. Make sure gun can't be fired faster by tapping activation fast - if (m_LastFireTmr.GetElapsedSimTimeMS() > (m_ActivationTimer.GetElapsedSimTimeMS() - m_ActivationDelay)) { - roundsFired += (m_ActivationTimer.GetElapsedSimTimeMS() - m_ActivationDelay) / msPerRound; - } else { - roundsFired += m_LastFireTmr.GetElapsedSimTimeMS() / msPerRound; + if (m_FireEchoSound) { + Scene::Area* noEchoArea = g_SceneMan.GetScene()->GetOptionalArea("IndoorArea"); + if (noEchoArea == nullptr || !noEchoArea->IsInside(m_Pos)) { + m_FireEchoSound->Play(m_Pos); + } } - } else { - // TODO: Confirm that the delays work properly in semi-auto! - roundsFired = !m_FiredOnce && (m_LastFireTmr.GetElapsedSimTimeMS() - m_ActivationDelay - m_DeactivationDelay) > msPerRound ? 1 : 0; } - if (roundsFired >= 1) - { - m_FiredOnce = true; - m_FireFrame = true; - m_LastFireTmr.Reset(); - } - - Vector roundVel; - Vector shellVel; - - Round *pRound = 0; - Vector tempNozzle; - Vector tempEject; - MOPixel *pPixel; - float shake, particleSpread, shellSpread, lethalRange; - - lethalRange = m_MaxSharpLength * m_SharpAim + std::max(g_FrameMan.GetPlayerFrameBufferWidth(-1), g_FrameMan.GetPlayerFrameBufferHeight(-1)) * 0.51F; - if (pActor) { - lethalRange += pActor->GetAimDistance(); - } - - // Fire all rounds that were fired this frame. - for (int i = 0; i < roundsFired && !m_pMagazine->IsEmpty(); ++i) - { - m_RoundsFired++; - - pRound = m_pMagazine->PopNextRound(); - shake = (m_ShakeRange - ((m_ShakeRange - m_SharpShakeRange) * m_SharpAim)) * - (m_Supported ? 1.0F : m_NoSupportFactor) * RandomNormalNum(); - tempNozzle = m_MuzzleOff.GetYFlipped(m_HFlipped); - tempNozzle.DegRotate(degAimAngle + shake); - roundVel.SetXY(pRound->GetFireVel(), 0); - roundVel.DegRotate(degAimAngle + shake); - - Vector particlePos; - Vector particleVel; - int particleCountMax = pRound->ParticleCount(); - float lifeVariation = pRound->GetLifeVariation(); - - // Launch all particles in round - MovableObject *pParticle = 0; - while (!pRound->IsEmpty()) - { - pParticle = pRound->PopNextParticle(); - - // Only make the particles separate back behind the nozzle, not in front. This is to avoid silly penetration firings - particlePos = tempNozzle + (roundVel.GetNormalized() * (-RandomNum()) * pRound->GetSeparation()); - pParticle->SetPos(m_Pos + particlePos); - - particleVel = roundVel; - particleSpread = m_ParticleSpreadRange * RandomNormalNum(); - particleVel.DegRotate(particleSpread); - pParticle->SetVel(pRound->GetInheritsFirerVelocity() ? (m_Vel + particleVel) : particleVel); - if (m_LegacyCompatibilityRoundsAlwaysFireUnflipped) { - pParticle->SetRotAngle(particleVel.GetAbsRadAngle()); - } else { - pParticle->SetRotAngle(particleVel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); - pParticle->SetHFlipped(m_HFlipped); - } - if (lifeVariation != 0 && pParticle->GetLifetime() != 0) { - pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + (particleCountMax > 1 ? lifeVariation - (lifeVariation * 2.0F * (static_cast(pRound->ParticleCount()) / static_cast(particleCountMax - 1))) : lifeVariation * RandomNormalNum()))), 1)); - } - // F = m * a - totalFireForce += pParticle->GetMass() * pParticle->GetVel().GetMagnitude(); - - // Remove from parent if it's an attachable - Attachable *pAttachable = dynamic_cast(pParticle); - if (pAttachable) { - if (pAttachable->IsAttached()) { - pAttachable->GetParent()->RemoveAttachable(pAttachable); - } - - // Activate if it is some kind of grenade or whatnot. - ThrownDevice *pTD = dynamic_cast(pAttachable); - if (pTD) { - pTD->Activate(); - } - } - - // Set the fired particle to not hit this HeldDevice's parent, if applicable - if (m_FireIgnoresThis) - pParticle->SetWhichMOToNotHit(this, 1.0f); - - // Set the team so alarm events that happen if these gib won't freak out the guy firing - pParticle->SetTeam(m_Team); - - // Also make this not hit team members - // TODO: Don't hardcode this??? - pParticle->SetIgnoresTeamHits(true); - - // Decide for how long until the bullet tumble and start to lose lethality - pPixel = dynamic_cast(pParticle); - if (pPixel) { - // Stray bullets heavily affected by bullet shake lose lethality quicker, as if missing on an imaginary "Z" axis - lethalRange *= std::max(1.0F - std::abs(shake) / 20.0F, 0.1F); - pPixel->SetLethalRange(lethalRange); - } - g_MovableMan.AddParticle(pParticle); - } - pParticle = 0; - - // Launch shell, if there is one. - MovableObject *pShell = pRound->GetShell() ? dynamic_cast(pRound->GetShell()->Clone()) : 0; - if (pShell) - { - tempEject = m_EjectOff.GetYFlipped(m_HFlipped); - shellSpread = m_ShellSpreadRange * RandomNormalNum(); - tempEject.DegRotate(degAimAngle + shellSpread); - pShell->SetPos(m_Pos + tempEject); - - // ##@#@@$ TEMP - shellVel.SetXY(pRound->GetShellVel() * (1.0F - RandomNum(0.0F, m_ShellVelVariation)), 0); - shellVel.DegRotate(degAimAngle + m_ShellEjectAngle * (m_HFlipped ? -1 : 1) + shellSpread); - pShell->SetVel(m_Vel + shellVel); - pShell->SetRotAngle(m_Rotation.GetRadAngle()); - pShell->SetAngularVel(pShell->GetAngularVel() + (m_ShellAngVelRange * RandomNormalNum())); - pShell->SetHFlipped(m_HFlipped); - // Set the ejected shell to not hit this HeldDevice's parent, if applicable - if (m_FireIgnoresThis) - pShell->SetWhichMOToNotHit(this, 1.0f); - // Set the team so alarm events that happen if these gib won't freak out the guy firing - pShell->SetTeam(m_Team); - // Set this to ignore team hits in case it's lethal - // TODO: Don't hardcode this??? - pShell->SetIgnoresTeamHits(true); - g_MovableMan.AddParticle(pShell); - pShell = 0; - } - - // Sound the extra Round firing sound, if any is defined - if (!playedRoundFireSound && pRound->HasFireSound()) - { - pRound->GetFireSound()->Play(m_Pos); - playedRoundFireSound = true; - } - - delete pRound; - } - pRound = 0; - - if (m_FireFrame) { RunScriptedFunctionInAppropriateScripts("OnFire", false, false); } + if (m_Loudness > 0) { + g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, m_Loudness)); + } } else { - m_ActivationTimer.Reset(); + m_Recoiled = false; + if (!m_IsAnimatedManually) { + m_Frame = 0; + } } - } else if (m_Activated && !m_AlreadyClicked) { - // Play empty pin click sound. - if (m_EmptySound) { m_EmptySound->Play(m_Pos); } - // Indicate that we have clicked once during the current activation. - m_AlreadyClicked = true; - } - - if (m_Reloading && m_ReloadEndSound) { - // x0.5 the sound length generally just lines up better and leaves less dead air assuming a normal attempt at a ReloadEnd sound - float offsetMilliseconds = m_ReloadEndOffset == -1.0F ? m_ReloadEndSound->GetLength(SoundContainer::LengthOfSoundType::NextPlayed) * 0.5f : m_ReloadEndOffset; - bool shouldPlay = !m_HasPlayedEndReloadSound && m_ReloadTmr.LeftTillSimTimeLimitMS() <= offsetMilliseconds; - if (shouldPlay) { - m_ReloadEndSound->Play(m_Pos); - m_HasPlayedEndReloadSound = true; - } - } - - if (m_Reloading && !m_pMagazine && m_pMagazineReference && m_ReloadTmr.IsPastSimTimeLimit()) { - SetMagazine(dynamic_cast(m_pMagazineReference->Clone())); - - m_ActivationTimer.Reset(); - m_LastFireTmr.Reset(); - - if (m_PreFireSound && m_Activated) { - m_PreFireSound->Play(); - } - m_HasPlayedEndReloadSound = false; - m_Reloading = false; - m_DoneReloading = true; - } + // Display and override gun animation if there's a special one + if (m_FrameCount > 1) { + if (m_SpriteAnimMode == LOOPWHENACTIVE) { + if (m_Activated || m_LastFireTmr.GetElapsedSimTimeMS() < m_DeactivationDelay) { + // Max rate of the animation when fully activated and firing + int animDuration = m_SpriteAnimDuration; + // Spin up - can only spin up if mag is inserted + if (m_Activated && !m_Reloading && m_ActivationTimer.GetElapsedSimTimeMS() < m_ActivationDelay) { + animDuration = (int)LERP(0, m_ActivationDelay, (float)(m_SpriteAnimDuration * 10), (float)m_SpriteAnimDuration, m_ActivationTimer.GetElapsedSimTimeMS()); + if (m_ActiveSound) { + m_ActiveSound->SetPitch(LERP(0, m_ActivationDelay, 0, 1.0, m_ActivationTimer.GetElapsedSimTimeMS())); + } + } + // Spin down + if ((!m_Activated || m_Reloading) && m_LastFireTmr.GetElapsedSimTimeMS() < m_DeactivationDelay) { + animDuration = (int)LERP(0, m_DeactivationDelay, (float)m_SpriteAnimDuration, (float)(m_SpriteAnimDuration * 10), m_LastFireTmr.GetElapsedSimTimeMS()); + if (m_ActiveSound) { + m_ActiveSound->SetPitch(LERP(0, m_DeactivationDelay, 1.0, 0, m_LastFireTmr.GetElapsedSimTimeMS())); + } + } - // Do stuff to deactivate after being activated - if (!m_Activated) - { - // Reset the click indicator. - m_AlreadyClicked = false; - - // Stop any looping activation sounds - if (m_FireSound && m_FireSound->GetLoopSetting() == -1)// && m_FireSound->IsBeingPlayed()) - m_FireSound->Stop(); - } - - ////////////////////////////////////////////// - // Recoil and other activation effects logic. - - // TODO: don't use arbitrary numbers? - m_RecoilForce.SetMagnitude(std::max(m_RecoilForce.GetMagnitude() * 0.7F - 1.0F, 0.0F)); - - if (roundsFired > 0) { - // Alternate to get that shake effect! - m_Recoiled = !m_Recoiled; - - // Set up the recoil force and shake offsets - if (m_Recoiled) - { - m_RecoilForce.SetXY(totalFireForce * m_JointStiffness, 0); - m_RecoilForce = RotateOffset(m_RecoilForce); - m_RecoilForce = -m_RecoilForce; - - // Set up the recoil shake offset - m_RecoilOffset = m_RecoilForce; - m_RecoilOffset.SetMagnitude(std::min(m_RecoilOffset.GetMagnitude(), 1.0F)); - } - - // Screen shake - if (pActor) { - int controllingPlayer = pActor->GetController()->GetPlayer(); - int screenId = g_ActivityMan.GetActivity()->ScreenOfPlayer(controllingPlayer); - if (screenId != -1) { - const float shakiness = g_CameraMan.GetDefaultShakePerUnitOfRecoilEnergy(); - const float maxShakiness = g_CameraMan.GetDefaultShakeFromRecoilMaximum(); // Some weapons fire huge rounds, so restrict the amount - float screenShakeAmount = m_RecoilScreenShakeAmount == -1.0F ? std::min(totalFireForce * m_JointStiffness * shakiness, maxShakiness) : m_RecoilScreenShakeAmount; - g_CameraMan.ApplyScreenShake(screenShakeAmount, screenId); - } - } - - AddImpulseForce(m_RecoilForce, m_RecoilOffset); - - // Display gun animation - if (!m_IsAnimatedManually && m_FrameCount > 1) { m_Frame = 1; } - - // Display gun flame frame. - if (m_pFlash) { - m_pFlash->SetParentOffset(m_MuzzleOff); - m_pFlash->SetFrame(RandomNum(0, m_pFlash->GetFrameCount() - 1)); - } - - // Play firing sound - // Only start playing if it's not a looping fire sound that is already playing, and if there's a mag - if (m_pMagazine) { - if (m_FireSound && !(m_FireSound->GetLoopSetting() == -1 && m_FireSound->IsBeingPlayed())) { - m_FireSound->Play(m_Pos); - } - if (m_FireEchoSound) { - Scene::Area* noEchoArea = g_SceneMan.GetScene()->GetOptionalArea("IndoorArea"); - if (noEchoArea == nullptr || !noEchoArea->IsInside(m_Pos)) { - m_FireEchoSound->Play(m_Pos); - } - } - } - - if (m_Loudness > 0) { g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, m_Loudness)); } - } else { - m_Recoiled = false; - if (!m_IsAnimatedManually) { m_Frame = 0; } - } - - // Display and override gun animation if there's a special one - if (m_FrameCount > 1) - { - if (m_SpriteAnimMode == LOOPWHENACTIVE) - { - if (m_Activated || m_LastFireTmr.GetElapsedSimTimeMS() < m_DeactivationDelay) { - // Max rate of the animation when fully activated and firing - int animDuration = m_SpriteAnimDuration; - // Spin up - can only spin up if mag is inserted - if (m_Activated && !m_Reloading && m_ActivationTimer.GetElapsedSimTimeMS() < m_ActivationDelay) - { - animDuration = (int)LERP(0, m_ActivationDelay, (float)(m_SpriteAnimDuration * 10), (float)m_SpriteAnimDuration, m_ActivationTimer.GetElapsedSimTimeMS()); - if (m_ActiveSound) { m_ActiveSound->SetPitch(LERP(0, m_ActivationDelay, 0, 1.0, m_ActivationTimer.GetElapsedSimTimeMS())); } - } - // Spin down - if ((!m_Activated || m_Reloading) && m_LastFireTmr.GetElapsedSimTimeMS() < m_DeactivationDelay) - { - animDuration = (int)LERP(0, m_DeactivationDelay, (float)m_SpriteAnimDuration, (float)(m_SpriteAnimDuration * 10), m_LastFireTmr.GetElapsedSimTimeMS()); - if (m_ActiveSound) { m_ActiveSound->SetPitch(LERP(0, m_DeactivationDelay, 1.0, 0, m_LastFireTmr.GetElapsedSimTimeMS())); } - } - - if (animDuration > 0 && !(m_Reloading && m_LastFireTmr.GetElapsedSimTimeMS() >= m_DeactivationDelay)) { - float cycleTime = ((long)m_SpriteAnimTimer.GetElapsedSimTimeMS()) % animDuration; - if (!m_IsAnimatedManually) - m_Frame = std::floor((cycleTime / (float)animDuration) * (float)m_FrameCount); + if (animDuration > 0 && !(m_Reloading && m_LastFireTmr.GetElapsedSimTimeMS() >= m_DeactivationDelay)) { + float cycleTime = ((long)m_SpriteAnimTimer.GetElapsedSimTimeMS()) % animDuration; + if (!m_IsAnimatedManually) + m_Frame = std::floor((cycleTime / (float)animDuration) * (float)m_FrameCount); + } else { + StopActivationSound(); + } } else { + if (!m_IsAnimatedManually) { + m_Frame = 0; + } StopActivationSound(); } - } else { - if (!m_IsAnimatedManually) { m_Frame = 0; } - StopActivationSound(); - } - } - } - - ///////////////////////////////// - // Update fitted Magazine. - - if (m_pMagazine) { - // Recoil offset has to be applied after the Update or it'll get reset within the update - m_pMagazine->SetRecoil(m_RecoilForce, m_RecoilOffset, m_Recoiled); - } + } + } - m_FiredLastFrame = m_FireFrame; + ///////////////////////////////// + // Update fitted Magazine. - // Set the screen flash effect to draw at the final post processing stage - if (m_FireFrame && m_pFlash && m_pFlash->GetScreenEffect()) { - Vector muzzlePos = m_Pos + RotateOffset(m_MuzzleOff + Vector(m_pFlash->GetSpriteWidth() * 0.3F, 0)); - if (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(muzzlePos)) { - g_PostProcessMan.RegisterPostEffect(muzzlePos, m_pFlash->GetScreenEffect(), m_pFlash->GetScreenEffectHash(), RandomNum(m_pFlash->GetEffectStopStrength(), m_pFlash->GetEffectStartStrength()), m_pFlash->GetEffectRotAngle()); - } - } -} + if (m_pMagazine) { + // Recoil offset has to be applied after the Update or it'll get reset within the update + m_pMagazine->SetRecoil(m_RecoilForce, m_RecoilOffset, m_Recoiled); + } + m_FiredLastFrame = m_FireFrame; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EstimateDigStrength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Estimates what material strength the rounds in the magazine can destroy. - -float HDFirearm::EstimateDigStrength() const { - return m_pMagazine ? m_pMagazine->EstimateDigStrength() : m_pMagazineReference->EstimateDigStrength(); -} + // Set the screen flash effect to draw at the final post processing stage + if (m_FireFrame && m_pFlash && m_pFlash->GetScreenEffect()) { + Vector muzzlePos = m_Pos + RotateOffset(m_MuzzleOff + Vector(m_pFlash->GetSpriteWidth() * 0.3F, 0)); + if (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(muzzlePos)) { + g_PostProcessMan.RegisterPostEffect(muzzlePos, m_pFlash->GetScreenEffect(), m_pFlash->GetScreenEffectHash(), RandomNum(m_pFlash->GetEffectStopStrength(), m_pFlash->GetEffectStartStrength()), m_pFlash->GetEffectRotAngle()); + } + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EstimateDigStrength + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Estimates what material strength the rounds in the magazine can destroy. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this HDFirearm's current graphical representation to a -// BITMAP of choice. + float HDFirearm::EstimateDigStrength() const { + return m_pMagazine ? m_pMagazine->EstimateDigStrength() : m_pMagazineReference->EstimateDigStrength(); + } -void HDFirearm::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { - if (m_pFlash && m_FireFrame && !m_pFlash->IsDrawnAfterParent() && mode == g_DrawColor && !onlyPhysical) { - m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this HDFirearm's current graphical representation to a + // BITMAP of choice. - HeldDevice::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + void HDFirearm::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { + if (m_pFlash && m_FireFrame && !m_pFlash->IsDrawnAfterParent() && mode == g_DrawColor && !onlyPhysical) { + m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } - if (m_pFlash && m_FireFrame && m_pFlash->IsDrawnAfterParent() && mode == g_DrawColor && !onlyPhysical) { - m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } -} + HeldDevice::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + if (m_pFlash && m_FireFrame && m_pFlash->IsDrawnAfterParent() && mode == g_DrawColor && !onlyPhysical) { + m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws an aiming aid in front of this HeldDevice. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws an aiming aid in front of this HeldDevice. -void HDFirearm::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScreen, bool playerControlled) -{ - if (!m_HUDVisible) - return; + void HDFirearm::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { + if (!m_HUDVisible) + return; - // Only draw if the team viewing this is on the same team OR has seen the space where this is located - int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); - if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam) - { - if (g_SceneMan.IsUnseen(m_Pos.m_X, m_Pos.m_Y, viewingTeam)) - return; - } + // Only draw if the team viewing this is on the same team OR has seen the space where this is located + int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); + if (viewingTeam != m_Team && viewingTeam != Activity::NoTeam) { + if (g_SceneMan.IsUnseen(m_Pos.m_X, m_Pos.m_Y, viewingTeam)) + return; + } - HeldDevice::DrawHUD(pTargetBitmap, targetPos, whichScreen); + HeldDevice::DrawHUD(pTargetBitmap, targetPos, whichScreen); - if (!m_Parent || IsReloading() || m_MaxSharpLength == 0) { - return; - } + if (!m_Parent || IsReloading() || m_MaxSharpLength == 0) { + return; + } - float sharpLength = std::max(m_MaxSharpLength * m_SharpAim, 20.0F); - int glowStrength = RandomNum(95, 159); - int pointCount; - if (playerControlled && sharpLength > 20.0F) { - pointCount = m_SharpAim > 0.5F ? 4 : 3; - } else { - pointCount = 2; - } - int pointSpacing = 10 - pointCount; - sharpLength -= static_cast(pointSpacing * pointCount) * 0.5F; - Vector muzzleOffset(std::max(m_MuzzleOff.m_X, m_SpriteRadius), m_MuzzleOff.m_Y); - - //acquire_bitmap(pTargetBitmap); - for (int i = 0; i < pointCount; ++i) { - Vector aimPoint(sharpLength + static_cast(pointSpacing * i), 0); - aimPoint = RotateOffset(aimPoint + muzzleOffset) + m_Pos; - - g_PostProcessMan.RegisterGlowDotEffect(aimPoint, YellowDot, glowStrength); - aimPoint -= targetPos; - g_SceneMan.WrapPosition(aimPoint); - putpixel(pTargetBitmap, aimPoint.GetFloorIntX(), aimPoint.GetFloorIntY(), g_YellowGlowColor); + float sharpLength = std::max(m_MaxSharpLength * m_SharpAim, 20.0F); + int glowStrength = RandomNum(95, 159); + int pointCount; + if (playerControlled && sharpLength > 20.0F) { + pointCount = m_SharpAim > 0.5F ? 4 : 3; + } else { + pointCount = 2; + } + int pointSpacing = 10 - pointCount; + sharpLength -= static_cast(pointSpacing * pointCount) * 0.5F; + Vector muzzleOffset(std::max(m_MuzzleOff.m_X, m_SpriteRadius), m_MuzzleOff.m_Y); + + // acquire_bitmap(pTargetBitmap); + for (int i = 0; i < pointCount; ++i) { + Vector aimPoint(sharpLength + static_cast(pointSpacing * i), 0); + aimPoint = RotateOffset(aimPoint + muzzleOffset) + m_Pos; + + g_PostProcessMan.RegisterGlowDotEffect(aimPoint, YellowDot, glowStrength); + aimPoint -= targetPos; + g_SceneMan.WrapPosition(aimPoint); + putpixel(pTargetBitmap, aimPoint.GetFloorIntX(), aimPoint.GetFloorIntY(), g_YellowGlowColor); + } + // release_bitmap(pTargetBitmap); } - //release_bitmap(pTargetBitmap); -} } // namespace RTE diff --git a/Source/Entities/HDFirearm.h b/Source/Entities/HDFirearm.h index 4bfa67256f..0c45c64837 100644 --- a/Source/Entities/HDFirearm.h +++ b/Source/Entities/HDFirearm.h @@ -10,1030 +10,988 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "HeldDevice.h" -namespace RTE -{ - -class Magazine; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: HDFirearm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A firearm device that fires projectile MO's and discharges shell MO's. -// Parent(s): HeldDevice. -// Class history: 07/1/2002 HDFirearm created. - -class HDFirearm : public HeldDevice { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(HDFirearm); -SerializableOverrideMethods; -ClassInfoGetters; -AddScriptFunctionNames(HeldDevice, "OnFire", "OnReload"); - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: HDFirearm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a HDFirearm object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - HDFirearm() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~HDFirearm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a HDFirearm object before deletion -// from system memory. -// Arguments: None. - - ~HDFirearm() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the HDFirearm object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a HDFirearm to be identical to another, by deep copy. -// Arguments: A reference to the HDFirearm to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const HDFirearm &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire HDFirearm, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); HeldDevice::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetReloadEndOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets reload end offset, in ms. This is how early the ReloadEnd -// sound is played compared to actual end of reload. -// Arguments: None. -// Return value: The reload end offset, in ms. - - int GetReloadEndOffset() const { return m_ReloadEndOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetReloadEndOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets reload end offset, in ms. This is how early the ReloadEnd -// sound is played compared to actual end of reload. -// Arguments: The new reload end offset, in ms. -// Return value: None. - - void SetReloadEndOffset(int newRate) { m_ReloadEndOffset = newRate; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRateOfFire -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rate of fire of this. This applies even if semi-auto. it -// limits how quickly a new round can be fired after the last. -// Arguments: None. -// Return value: The rate of fire, in rounds per min. - - int GetRateOfFire() const { return m_RateOfFire; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetRateOfFire -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the rate of fire of this. This applies even if semi-auto. it -// limits how quickly a new round can be fired after the last. -// Arguments: The new rate of fire, in rounds per min. -// Return value: None. - - void SetRateOfFire(int newRate) { m_RateOfFire = newRate; } - - - /// - /// Gets the minimum time in between shots, in MS. - /// - /// The minimum time in between shots, in MS. - double GetMSPerRound() const { return 60000.0 / static_cast(m_RateOfFire); } - - /// - /// Gets the Magazine of this HDFirearm. - /// - /// A pointer to Magazine of this HDFirearm. Ownership is NOT transferred! - Magazine * GetMagazine() const { return m_pMagazine; } - - /// - /// Sets the Magazine for this HDFirearm. Ownership IS transferred! - /// - /// The new Magazine to use. - void SetMagazine(Magazine *newMagazine); - - /// - /// Gets the flash of this HDFirearm. - /// - /// A pointer to flash of this HDFirearm. Ownership is NOT transferred! - Attachable * GetFlash() const { return m_pFlash; } - - /// - /// Sets the flash for this HDFirearm. Ownership IS transferred! - /// - /// The new flash to use. - void SetFlash(Attachable *newFlash); - - /// - /// Gets the preset name of the next Magazine that will be loaded into this gun. - /// - /// The preset name of the next Magazine that will be loaded into this gun. - std::string GetNextMagazineName() const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetNextMagazineName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the Preset name of the next Magazine that will be loaded into -// this gun. This changes all future mags that will be reloaded. -// Arguments: The preset name of the new Magazine to load into this from now on. -// Return value: Whether the specified magazine was found and successfully prepared. - - bool SetNextMagazineName(std::string magName); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRoundInMagCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of rounds still in the loaded magazine. Negative value -// means infinite ammo. -// Arguments: None. -// Return value: An int with the number of rounds in the magazine currently in this -// HDFirearm. Negative means infinite ammo. - - int GetRoundInMagCount() const; - - /// - /// Gets the maximum RoundCount a Magazine of this HDFirearm can hold. - /// If there is no Magazine, it gets the RoundCount of the reference Magazine. - /// - /// An int with the maximum RoundCount the magazine or magazine reference of this HDFirearm can hold. - int GetRoundInMagCapacity() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActivationDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the delay before firing. -// Arguments: None. -// Return value: An int with the activation delay in ms. - - int GetActivationDelay() const { return m_ActivationDelay; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetActivationDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the delay before firing. -// Arguments: An int with the activation delay in ms. -// Return value: None. - - void SetActivationDelay(int delay) { m_ActivationDelay = delay; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDeactivationDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the delay between release of activation and another can be started. -// Arguments: None. -// Return value: An int with the delay in ms. - - int GetDeactivationDelay() const { return m_DeactivationDelay; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDeactivationDelay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the delay between release of activation and another can be started. -// Arguments: An int with the delay in ms. -// Return value: None. - - void SetDeactivationDelay(int delay) { m_DeactivationDelay = delay; }; - - /// - /// Gets the base time this HDFirearm takes to reload, in milliseconds. - /// - /// The base time this HeldDevice takes to reload, in milliseconds. - int GetBaseReloadTime() const { return m_BaseReloadTime; }; - - /// - /// Sets the base time this HDFirearm takes to reload, in milliseconds. - /// - /// The base time this HDFirearm should take to reload, in milliseconds. - void SetBaseReloadTime(int newReloadTime) { m_BaseReloadTime = newReloadTime; CorrectReloadTimerForSupportAvailable(); }; - - /// - /// Gets how long this HDFirearm currently takes to reload, in milliseconds. - /// - /// How long this HDFirearm currently takes to reload, in milliseconds. - int GetReloadTime() const { return m_ReloadTmr.GetSimTimeLimitMS() <= 0 ? m_BaseReloadTime : static_cast(std::floor(m_ReloadTmr.GetSimTimeLimitMS())); }; - - /// - /// Gets whether or not this HDFirearm allows dual-reload, i.e. if it's one-handed and dual-wieldable, it can reload at the same time as another weapon that also allows dual-reload. - /// - /// Whether or not this HDFirearm allows dual-reload. - bool IsDualReloadable() const { return m_DualReloadable; } - - /// - /// Sets whether or not this HDFirearm allows dual-reloading. - /// - /// The new value for whether or not this HDFirearm should allow dual-reloading. - void SetDualReloadable(bool newDualReloadable) { m_DualReloadable = newDualReloadable; } - - /// - /// Gets the multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. - /// - /// The multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. - float GetOneHandedReloadTimeMultiplier() const { return m_OneHandedReloadTimeMultiplier; } - - /// - /// Sets the multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. - /// - /// The new multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. - void SetOneHandedReloadTimeMultiplier(float newOneHandedReloadTimeMultiplier) { m_OneHandedReloadTimeMultiplier = newOneHandedReloadTimeMultiplier; } - - /// - /// Gets the reload angle this HDFirearm will use when support is available. - /// - /// The reload angle this HDFirearm will use when support is available, in radians. - float GetReloadAngle() const { return m_ReloadAngle; } - - /// - /// Sets the reload angle this HDFirearm should use when support is available. - /// - /// The new reload angle this HDFirearm should use when support is available. - void SetReloadAngle(float newReloadAngle) { m_ReloadAngle = newReloadAngle; } - - /// - /// Gets the reload angle this HDFirearm will use when support is not available. - /// - /// The reload angle this HDFirearm will use when support is not available, in radians. - float GetOneHandedReloadAngle() const { return m_OneHandedReloadAngle; } - - /// - /// Sets the reload angle this HDFirearm should use when support is not available. - /// - /// The new reload angle this HDFirearm should use when support is not available. - void SetOneHandedReloadAngle(float newOneHandedReloadAngle) { m_OneHandedReloadAngle = newOneHandedReloadAngle; } - - /// - /// Gets the reload angle this HDFirearm is currently using, based on whether or not support is available. - /// - /// The current reload angle of this HDFirearm, in radians. - float GetCurrentReloadAngle() const { return m_SupportAvailable ? m_ReloadAngle : m_OneHandedReloadAngle; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetShakeRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the range of normal shaking of entire weapon. -// Arguments: None. -// Return value: A float with the range in degrees. - - float GetShakeRange() const { return m_ShakeRange; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetShakeRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the range of normal shaking of entire weapon. -// Arguments: A float with the range in degrees. -// Return value: None. - - void SetShakeRange(float range) { m_ShakeRange = range; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSharpShakeRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the range of shaking of entire weapon during sharp aiming. -// Arguments: None. -// Return value: A float with the range in degrees. - - float GetSharpShakeRange() const { return m_SharpShakeRange; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSharpShakeRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the range of shaking of entire weapon during sharp aiming. -// Arguments: A float with the range in degrees. -// Return value: None. - - void SetSharpShakeRange(float range) { m_SharpShakeRange = range; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNoSupportFactor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the factor for how much more weapon shakes if it isn't supported -// by a second hand. -// Arguments: None. -// Return value: A float with the factor. - - float GetNoSupportFactor() const { return m_NoSupportFactor; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetNoSupportFactor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the factor for how much more weapon shakes if it isn't supported -// by a second hand. -// Arguments: A float with the factor. -// Return value: None. - - void SetNoSupportFactor(float factor) { m_NoSupportFactor = factor; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetParticleSpreadRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the range of spread angle of fired particles, in one direction. -// Arguments: None. -// Return value: A float with the range in degrees. - - float GetParticleSpreadRange() const { return m_ParticleSpreadRange; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetParticleSpreadRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the range of spread angle of fired particles, in one direction. -// Arguments: A float with the range in degrees. -// Return value: None. - - void SetParticleSpreadRange(float range) { m_ParticleSpreadRange = range; }; - - /// - /// Gets the random velocity variation scalar at which this HDFirearm's shell is to be ejected. - /// - /// A float with the scalar value. - float GetShellVelVariation() const { return m_ShellVelVariation; } - - /// - /// Sets the random velocity variation scalar at which this HDFirearm's shell is to be ejected. - /// - /// The new velocity variation scalar. - void SetShellVelVariation(float newVariation) { m_ShellVelVariation = newVariation; } - - /// - /// Sets the stiffness scalar of the joint of this HDFirearm. Unlike Attachable::SetJointStiffness, there are no limitations on this value. - /// 1.0 means impulse forces on this attachable will be transferred to the parent with 100% strength, 0 means they will not transfer at all, negative values will apply negative force, which may behave oddly. - /// - /// A float describing the normalized stiffness scalar of this Attachable's joint. - void SetJointStiffness(float jointStiffness) override { m_JointStiffness = jointStiffness; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIFireVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the velocity the AI use when aiming this weapon. -// Arguments: None. -// Return value: A float with the velocity in m/s. - - float GetAIFireVel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIBulletLifeTime -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the bullet life time the AI use when aiming this weapon. -// Arguments: None. -// Return value: A float with the life time in ms. - - unsigned long GetAIBulletLifeTime(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBulletAccScalar -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. -// Arguments: None. -// Return value: A float with the scalar. - - float GetBulletAccScalar(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIBlastRadius -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the blast radius the AI use when aiming this weapon. -// Arguments: None. -// Return value: A float with the blast radius in pixels. - - float GetAIBlastRadius() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIPenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how much material the projectiles from this weapon can destory. -// Arguments: None. -// Return value: A float with the material strength. - - float GetAIPenetration() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CompareTrajectories -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Estimates how close the projectiles from two weapons will land. -// Arguments: A HDFirearm pointer to compare with. -// Return value: A float with the distance in pixels. - - float CompareTrajectories(HDFirearm * pWeapon); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMagazinePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of the magazine or other equivalent point of -// this. -// Arguments: None. -// Return value: A vector describing the absolute world coordinates for the magazine -// attachment point of this - - Vector GetMagazinePos() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMuzzlePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of the muzzle or other equivalent point of -// this. -// Arguments: None. -// Return value: A vector describing the absolute world coordinates for the muzzle point -// of this - - Vector GetMuzzlePos() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMuzzleOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the unrotated relative offset from the position to the muzzle or -// other equivalent point of this. -// Arguments: None. -// Return value: A unrotated vector describing the relative for the muzzle point of -// this from this' position. - - Vector GetMuzzleOffset() const override { return m_MuzzleOff; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetMuzzleOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the unrotated relative offset from the position to the muzzle or -// other equivalent point of this. -// Arguments: Bew ofsset value. -// Return value: None. - - void SetMuzzleOffset(Vector newOffset) override { m_MuzzleOff = newOffset; } - - /// - /// Gets this HDFirearm's pre fire sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's pre fire sound. - SoundContainer * GetPreFireSound() const { return m_PreFireSound; } - - /// - /// Sets this HDFirearm's pre fire sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's pre fire sound. - void SetPreFireSound(SoundContainer *newSound) { m_PreFireSound = newSound; } - - /// - /// Gets this HDFirearm's fire sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's fire sound. - SoundContainer * GetFireSound() const { return m_FireSound; } - - /// - /// Sets this HDFirearm's fire sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's fire sound. - void SetFireSound(SoundContainer *newSound) { m_FireSound = newSound; } - - /// - /// Gets this HDFirearm's fire echo sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's fire echo sound. - SoundContainer * GetFireEchoSound() const { return m_FireEchoSound; } - - /// - /// Sets this HDFirearm's fire echo sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's fire echo sound. - void SetFireEchoSound(SoundContainer *newSound) { m_FireEchoSound = newSound; } - - /// - /// Gets this HDFirearm's active sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's active sound. - SoundContainer * GetActiveSound() const { return m_ActiveSound; } - - /// - /// Sets this HDFirearm's active sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's active sound. - void SetActiveSound(SoundContainer *newSound) { m_ActiveSound = newSound; } - - /// - /// Gets this HDFirearm's deactivation sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's deactivation sound. - SoundContainer * GetDeactivationSound() const { return m_DeactivationSound; } - - /// - /// Sets this HDFirearm's deactivation sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's deactivation sound. - void SetDeactivationSound(SoundContainer *newSound) { m_DeactivationSound = newSound; } - - /// - /// Gets this HDFirearm's empty sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's empty sound. - SoundContainer * GetEmptySound() const { return m_EmptySound; } - - /// - /// Sets this HDFirearm's empty sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's empty sound. - void SetEmptySound(SoundContainer *newSound) { m_EmptySound = newSound; } - - /// - /// Gets this HDFirearm's reload start sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's reload start sound. - SoundContainer * GetReloadStartSound() const { return m_ReloadStartSound; } - - /// - /// Sets this HDFirearm's reload start sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's reload start sound. - void SetReloadStartSound(SoundContainer *newSound) { m_ReloadStartSound = newSound; } - - /// - /// Gets this HDFirearm's reload end sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's reload end sound. - SoundContainer * GetReloadEndSound() const { return m_ReloadEndSound; } - - /// - /// Sets this HDFirearm's reload end sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's reload end sound. - void SetReloadEndSound(SoundContainer *newSound) { m_ReloadEndSound = newSound; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetAllTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resest all the timers used by this. Can be emitters, etc. This is to -// prevent backed up emissions to come out all at once while this has been -// held dormant in an inventory. -// Arguments: None. -// Return value: None. - - void ResetAllTimers() override { HeldDevice::ResetAllTimers(); m_LastFireTmr.Reset(); m_ReloadTmr.Reset(); } - - /// - /// Gets this HDFirearm's reload progress as a scalar from 0 to 1. - /// - /// The reload progress as a scalar from 0 to 1. - float GetReloadProgress() const { return IsReloading() && m_BaseReloadTime > 0 ? static_cast(m_ReloadTmr.SimTimeLimitProgress()) : 1.0F; } - - /// - /// Does the calculations necessary to detect whether this HDFirearm is at rest or not. IsAtRest() retrieves the answer. - /// - void RestDetection() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Activate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activates one of this HDFirearm's features. Analogous to 'pulling -// the trigger'. -// Arguments: None. -// Return value: None. - - void Activate() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Deactivate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing -// the trigger'. -// Arguments: None. -// Return value: None. - - void Deactivate() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StopActivationSound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Aborts playing of active sound no matter what. Used to silence spinning -// weapons when weapons swapped -// Arguments: None. -// Return value: None. - - void StopActivationSound(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reload -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Throws out the currently used Magazine, if any, and puts in a new one -// after the reload delay is up. -// Arguments: None. -// Return value: None. - - void Reload() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsReloading -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is curtrently being reloaded. -// Arguments: None. -// Return value: Whetehr being reloaded. - - bool IsReloading() const override { return m_Reloading; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DoneReloading -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device just finished reloading this frame. -// Arguments: None. -// Return value: Whether just done reloading this frame. - - bool DoneReloading() const override { return m_DoneReloading; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: NeedsReloading -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is curtrently in need of being reloaded. -// Arguments: None. -// Return value: Whetehr in need of reloading (ie not full). - - bool NeedsReloading() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsFull -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is curtrently full and reloading won't have -// any effect. -// Arguments: None. -// Return value: Whetehr magazine is full or not. - - bool IsFull() const override; - - bool IsEmpty() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsFullAuto -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is fully automatic or not. -// Arguments: None. -// Return value: Whether the player can hold down fire and this will fire repeatedly. - - bool IsFullAuto() const { return m_FullAuto; } - - /// - /// Gets whether this HDFirearm is set to be reloadable or not. - /// - /// Whether this HDFirearm is reloadable. - bool IsReloadable() const { return m_Reloadable; } - - /// - /// Sets whether this HDFirearm is reloadable or not and halts the reloading process. - /// - /// Whether this HDFirearm is reloadable. - void SetReloadable(bool isReloadable) { m_Reloadable = isReloadable; m_Reloading = m_Reloading && m_Reloadable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFullAuto -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether the device is fully automatic or not. -// Arguments: New value. -// Return value: None. - - void SetFullAuto(bool newValue) { m_FullAuto = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this HDFirearm's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws an aiming aid in front of this HeldDevice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// Which player's screen this is being drawn to. May affect what HUD elements -// get drawn etc. -// Return value: None. - - void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EstimateDigStrength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Estimates what material strength one round in the magazine can destroy. -// Arguments: None. -// Return value: The maximum material strength the regular or the tracer round can destroy. - - float EstimateDigStrength() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FiredOnce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Whether at least one round has already been fired during the current activation. -// Arguments: None. -// Return value: Returns true if at least one round has already been fired during the current activation. - - bool FiredOnce() const { return m_FiredOnce; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FiredFrame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Whether at least one round has already been fired during the current frame. -// Arguments: None. -// Return value: Returns true at least one round has already been fired during the current frame. - - bool FiredFrame() const { return m_FireFrame; } - - /// - /// Gets whether this HDFirearm is ready to be fired. - /// - /// Whether this HDFirearm is ready to pop another Round. - bool CanFire() const { return m_LastFireTmr.IsPastSimMS(GetMSPerRound()); } - - /// - /// Gets whether this HDFirearm is halfway to be fired. Used for evenly spacing out dual-wielded fire. - /// - /// Whether this HDFirearm is halfway to pop another Round. - bool HalfwayToNextRound() const { return m_LastFireTmr.IsPastSimMS(GetMSPerRound() / 2.0); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RoundsFired -////////////////////////////////////////////////////////////////////////////////////////// -// Description: How many rounds were fired during this frame. -// Arguments: None. -// Return value: Returns the number of rounds fired during this frame. - - int RoundsFired() const { return m_RoundsFired; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsAnimatedManually -////////////////////////////////////////////////////////////////////////////////////////// -// Description: If true then the m_Frame property is not changed bye the Update function -// Arguments: None. -// Return value: Whether this HDFirearm is animated manually. - - bool IsAnimatedManually() const { return m_IsAnimatedManually; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAnimatedManually -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets Whether this HDFirearm is animated manually. -// Arguments: Manual animation flag value. -// Return value: None. - - void SetAnimatedManually(bool newValue) { m_IsAnimatedManually = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - /// - /// Sets this Attachable's parent MOSRotating, and also sets its Team based on its parent and, if the Attachable is set to collide, adds/removes Atoms to its new/old parent. - /// Additionally, sets this HDFirearm as not firing or reloading, and resets its reload timer. - /// - /// A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! - void SetParent(MOSRotating *newParent) override { HeldDevice::SetParent(newParent); Deactivate(); m_Reloading = false; m_ReloadTmr.Reset(); } - - // Member variables. - static Entity::ClassInfo m_sClass; - - // The mag reference which all current mags are generated from. - // NOT owned by this, owned by PresetMan - const Magazine *m_pMagazineReference; - // Magazine MovableObject. Owned - Magazine *m_pMagazine; - // Muzzle Flash Attachable. Owned - Attachable *m_pFlash; - - SoundContainer *m_PreFireSound; //!< The sound this HDFirearm should play before it starts firing. Distinct from activation sound in that it will play exactly once per trigger pull and not pitch up. - // The audio of this FireArm being fired. - SoundContainer *m_FireSound; - SoundContainer *m_FireEchoSound; //!< The audio that is played as the echo for the gun. Each shot will restart this sound, so it doesn't ever overlap. - // The audio that is played immediately upon activation, but perhaps before actual first firing, if there's a pre-delay - SoundContainer *m_ActiveSound; - // The audio that is played immediately upon cease of activation - SoundContainer *m_DeactivationSound; - // The audio of this FireArm being fired empty. - SoundContainer *m_EmptySound; - // The audio of this FireArm being reloaded. - SoundContainer *m_ReloadStartSound; - SoundContainer *m_ReloadEndSound; - // The offset of how long before the reload finishes the sound plays - float m_ReloadEndOffset; - // Whether or not the end-of-relaod sound has already been played or not. - bool m_HasPlayedEndReloadSound; - - // Rate of fire, in rounds per min. - // If 0, firearm is semi-automatic (ie only one discharge per activation). - int m_RateOfFire; - // Delay between activation and full round output is achieved, in ms - int m_ActivationDelay; - // Delay between release of activation and another can be started, in ms - int m_DeactivationDelay; - // Reloading or not - bool m_Reloading; - // Just done reloading this frame - bool m_DoneReloading; - // Base reload time in millisecs. - int m_BaseReloadTime; - // Whether this HDFirearm is full or semi-auto. - bool m_FullAuto; - // Whether particles fired from this HDFirearm will ignore hits with itself, - // and the root parent of this HDFirearm, regardless if they are set to hit MOs. - bool m_FireIgnoresThis; - bool m_Reloadable; //!< Whether this HDFirearm is reloadable by normal means. - float m_OneHandedReloadTimeMultiplier; //!< The multiplier for how long this weapon takes to reload when being used one-handed. Only relevant for one-handed weapons. - bool m_DualReloadable; //!< Whether or not this weapon can be dual-reloaded, i.e. both guns can reload at once instead of having to wait til the other dual-wielded gun isn't being reloaded. Only relevant for one-handed weapons. - float m_ReloadAngle; //!< The angle offset for the default reload animation, in radians. - float m_OneHandedReloadAngle; //!< The angle offset for one-handed reload animation, in radians. - - // Timer for timing how long ago the last round was fired. - Timer m_LastFireTmr; - // Timer for timing reload times. - Timer m_ReloadTmr; - - // The point from where the projectiles appear. - Vector m_MuzzleOff; - // The point from where the discharged shells appear. - Vector m_EjectOff; - // Offset to magazine. - Vector m_MagOff; - // Range of normal shaking of entire weapon. - float m_ShakeRange; - // Range of shaking of entire weapon during sharp aiming. - float m_SharpShakeRange; - // Factor for how much more weapon shakes if it isn't supported by a second hand. - float m_NoSupportFactor; - // Range of spread angle of fired particles, in one direction - float m_ParticleSpreadRange; - // Angle in which shells are ejected relative to this weapon - float m_ShellEjectAngle; - // Range of spread angle of ejected shells, in one direction - float m_ShellSpreadRange; - // Range of spread in ang vel of ejected shells, in one direction - float m_ShellAngVelRange; - float m_ShellVelVariation; //!< The velocity variation scalar of ejected shells. - // The amount of screenshake that recoil causes - float m_RecoilScreenShakeAmount; - // The muzzle velocity the AI use when aiming this weapon - float m_AIFireVel; - // The bullet life time the AI use when aiming this weapon - unsigned long m_AIBulletLifeTime; - // The bullet acc scalar the AI use when aiming this weapon - float m_AIBulletAccScalar; - - // Whether at least one round has already been - // fired during the current activation. - bool m_FiredOnce; - // Whether at least one round has already been - // fired during the current frame. - bool m_FireFrame; - // Whether at least one round was fired during the last frame - bool m_FiredLastFrame; - // Whether, if this HDFireArm is empty, pin has already clicked once during - // current acticvation. - bool m_AlreadyClicked; - // How many rounds were fired during this frame - int m_RoundsFired; - // If true m_Frame is not changed during an update hence the animation - // is done by external Lua code - bool m_IsAnimatedManually; - - - bool m_LegacyCompatibilityRoundsAlwaysFireUnflipped; // - /// Ensures the reload Timer's time limit is set accordingly, based on whether the HDFirearm has support available. - /// - void CorrectReloadTimerForSupportAvailable() { m_ReloadTmr.SetSimTimeLimitMS(static_cast(static_cast(m_BaseReloadTime) * (m_SupportAvailable ? 1.0F : m_OneHandedReloadTimeMultiplier))); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this HDFirearm, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - HDFirearm(const HDFirearm &reference) = delete; - HDFirearm & operator=(const HDFirearm &rhs) = delete; - -}; +namespace RTE { + + class Magazine; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: HDFirearm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A firearm device that fires projectile MO's and discharges shell MO's. + // Parent(s): HeldDevice. + // Class history: 07/1/2002 HDFirearm created. + + class HDFirearm : public HeldDevice { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(HDFirearm); + SerializableOverrideMethods; + ClassInfoGetters; + AddScriptFunctionNames(HeldDevice, "OnFire", "OnReload"); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: HDFirearm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a HDFirearm object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + HDFirearm() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~HDFirearm + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a HDFirearm object before deletion + // from system memory. + // Arguments: None. + + ~HDFirearm() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the HDFirearm object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a HDFirearm to be identical to another, by deep copy. + // Arguments: A reference to the HDFirearm to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const HDFirearm& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire HDFirearm, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + HeldDevice::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetReloadEndOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets reload end offset, in ms. This is how early the ReloadEnd + // sound is played compared to actual end of reload. + // Arguments: None. + // Return value: The reload end offset, in ms. + + int GetReloadEndOffset() const { return m_ReloadEndOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetReloadEndOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets reload end offset, in ms. This is how early the ReloadEnd + // sound is played compared to actual end of reload. + // Arguments: The new reload end offset, in ms. + // Return value: None. + + void SetReloadEndOffset(int newRate) { m_ReloadEndOffset = newRate; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRateOfFire + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rate of fire of this. This applies even if semi-auto. it + // limits how quickly a new round can be fired after the last. + // Arguments: None. + // Return value: The rate of fire, in rounds per min. + + int GetRateOfFire() const { return m_RateOfFire; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetRateOfFire + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the rate of fire of this. This applies even if semi-auto. it + // limits how quickly a new round can be fired after the last. + // Arguments: The new rate of fire, in rounds per min. + // Return value: None. + + void SetRateOfFire(int newRate) { m_RateOfFire = newRate; } + + /// + /// Gets the minimum time in between shots, in MS. + /// + /// The minimum time in between shots, in MS. + double GetMSPerRound() const { return 60000.0 / static_cast(m_RateOfFire); } + + /// + /// Gets the Magazine of this HDFirearm. + /// + /// A pointer to Magazine of this HDFirearm. Ownership is NOT transferred! + Magazine* GetMagazine() const { return m_pMagazine; } + + /// + /// Sets the Magazine for this HDFirearm. Ownership IS transferred! + /// + /// The new Magazine to use. + void SetMagazine(Magazine* newMagazine); + + /// + /// Gets the flash of this HDFirearm. + /// + /// A pointer to flash of this HDFirearm. Ownership is NOT transferred! + Attachable* GetFlash() const { return m_pFlash; } + + /// + /// Sets the flash for this HDFirearm. Ownership IS transferred! + /// + /// The new flash to use. + void SetFlash(Attachable* newFlash); + + /// + /// Gets the preset name of the next Magazine that will be loaded into this gun. + /// + /// The preset name of the next Magazine that will be loaded into this gun. + std::string GetNextMagazineName() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetNextMagazineName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the Preset name of the next Magazine that will be loaded into + // this gun. This changes all future mags that will be reloaded. + // Arguments: The preset name of the new Magazine to load into this from now on. + // Return value: Whether the specified magazine was found and successfully prepared. + + bool SetNextMagazineName(std::string magName); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRoundInMagCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of rounds still in the loaded magazine. Negative value + // means infinite ammo. + // Arguments: None. + // Return value: An int with the number of rounds in the magazine currently in this + // HDFirearm. Negative means infinite ammo. + + int GetRoundInMagCount() const; + + /// + /// Gets the maximum RoundCount a Magazine of this HDFirearm can hold. + /// If there is no Magazine, it gets the RoundCount of the reference Magazine. + /// + /// An int with the maximum RoundCount the magazine or magazine reference of this HDFirearm can hold. + int GetRoundInMagCapacity() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActivationDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the delay before firing. + // Arguments: None. + // Return value: An int with the activation delay in ms. + + int GetActivationDelay() const { return m_ActivationDelay; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetActivationDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the delay before firing. + // Arguments: An int with the activation delay in ms. + // Return value: None. + + void SetActivationDelay(int delay) { m_ActivationDelay = delay; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDeactivationDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the delay between release of activation and another can be started. + // Arguments: None. + // Return value: An int with the delay in ms. + + int GetDeactivationDelay() const { return m_DeactivationDelay; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDeactivationDelay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the delay between release of activation and another can be started. + // Arguments: An int with the delay in ms. + // Return value: None. + + void SetDeactivationDelay(int delay) { m_DeactivationDelay = delay; }; + + /// + /// Gets the base time this HDFirearm takes to reload, in milliseconds. + /// + /// The base time this HeldDevice takes to reload, in milliseconds. + int GetBaseReloadTime() const { return m_BaseReloadTime; }; + + /// + /// Sets the base time this HDFirearm takes to reload, in milliseconds. + /// + /// The base time this HDFirearm should take to reload, in milliseconds. + void SetBaseReloadTime(int newReloadTime) { + m_BaseReloadTime = newReloadTime; + CorrectReloadTimerForSupportAvailable(); + }; + + /// + /// Gets how long this HDFirearm currently takes to reload, in milliseconds. + /// + /// How long this HDFirearm currently takes to reload, in milliseconds. + int GetReloadTime() const { return m_ReloadTmr.GetSimTimeLimitMS() <= 0 ? m_BaseReloadTime : static_cast(std::floor(m_ReloadTmr.GetSimTimeLimitMS())); }; + + /// + /// Gets whether or not this HDFirearm allows dual-reload, i.e. if it's one-handed and dual-wieldable, it can reload at the same time as another weapon that also allows dual-reload. + /// + /// Whether or not this HDFirearm allows dual-reload. + bool IsDualReloadable() const { return m_DualReloadable; } + + /// + /// Sets whether or not this HDFirearm allows dual-reloading. + /// + /// The new value for whether or not this HDFirearm should allow dual-reloading. + void SetDualReloadable(bool newDualReloadable) { m_DualReloadable = newDualReloadable; } + + /// + /// Gets the multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. + /// + /// The multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. + float GetOneHandedReloadTimeMultiplier() const { return m_OneHandedReloadTimeMultiplier; } + + /// + /// Sets the multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. + /// + /// The new multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. + void SetOneHandedReloadTimeMultiplier(float newOneHandedReloadTimeMultiplier) { m_OneHandedReloadTimeMultiplier = newOneHandedReloadTimeMultiplier; } + + /// + /// Gets the reload angle this HDFirearm will use when support is available. + /// + /// The reload angle this HDFirearm will use when support is available, in radians. + float GetReloadAngle() const { return m_ReloadAngle; } + + /// + /// Sets the reload angle this HDFirearm should use when support is available. + /// + /// The new reload angle this HDFirearm should use when support is available. + void SetReloadAngle(float newReloadAngle) { m_ReloadAngle = newReloadAngle; } + + /// + /// Gets the reload angle this HDFirearm will use when support is not available. + /// + /// The reload angle this HDFirearm will use when support is not available, in radians. + float GetOneHandedReloadAngle() const { return m_OneHandedReloadAngle; } + + /// + /// Sets the reload angle this HDFirearm should use when support is not available. + /// + /// The new reload angle this HDFirearm should use when support is not available. + void SetOneHandedReloadAngle(float newOneHandedReloadAngle) { m_OneHandedReloadAngle = newOneHandedReloadAngle; } + + /// + /// Gets the reload angle this HDFirearm is currently using, based on whether or not support is available. + /// + /// The current reload angle of this HDFirearm, in radians. + float GetCurrentReloadAngle() const { return m_SupportAvailable ? m_ReloadAngle : m_OneHandedReloadAngle; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetShakeRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the range of normal shaking of entire weapon. + // Arguments: None. + // Return value: A float with the range in degrees. + + float GetShakeRange() const { return m_ShakeRange; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetShakeRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the range of normal shaking of entire weapon. + // Arguments: A float with the range in degrees. + // Return value: None. + + void SetShakeRange(float range) { m_ShakeRange = range; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSharpShakeRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the range of shaking of entire weapon during sharp aiming. + // Arguments: None. + // Return value: A float with the range in degrees. + + float GetSharpShakeRange() const { return m_SharpShakeRange; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSharpShakeRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the range of shaking of entire weapon during sharp aiming. + // Arguments: A float with the range in degrees. + // Return value: None. + + void SetSharpShakeRange(float range) { m_SharpShakeRange = range; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNoSupportFactor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the factor for how much more weapon shakes if it isn't supported + // by a second hand. + // Arguments: None. + // Return value: A float with the factor. + + float GetNoSupportFactor() const { return m_NoSupportFactor; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetNoSupportFactor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the factor for how much more weapon shakes if it isn't supported + // by a second hand. + // Arguments: A float with the factor. + // Return value: None. + + void SetNoSupportFactor(float factor) { m_NoSupportFactor = factor; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetParticleSpreadRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the range of spread angle of fired particles, in one direction. + // Arguments: None. + // Return value: A float with the range in degrees. + + float GetParticleSpreadRange() const { return m_ParticleSpreadRange; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetParticleSpreadRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the range of spread angle of fired particles, in one direction. + // Arguments: A float with the range in degrees. + // Return value: None. + + void SetParticleSpreadRange(float range) { m_ParticleSpreadRange = range; }; + + /// + /// Gets the random velocity variation scalar at which this HDFirearm's shell is to be ejected. + /// + /// A float with the scalar value. + float GetShellVelVariation() const { return m_ShellVelVariation; } + + /// + /// Sets the random velocity variation scalar at which this HDFirearm's shell is to be ejected. + /// + /// The new velocity variation scalar. + void SetShellVelVariation(float newVariation) { m_ShellVelVariation = newVariation; } + + /// + /// Sets the stiffness scalar of the joint of this HDFirearm. Unlike Attachable::SetJointStiffness, there are no limitations on this value. + /// 1.0 means impulse forces on this attachable will be transferred to the parent with 100% strength, 0 means they will not transfer at all, negative values will apply negative force, which may behave oddly. + /// + /// A float describing the normalized stiffness scalar of this Attachable's joint. + void SetJointStiffness(float jointStiffness) override { m_JointStiffness = jointStiffness; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIFireVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the velocity the AI use when aiming this weapon. + // Arguments: None. + // Return value: A float with the velocity in m/s. + + float GetAIFireVel(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIBulletLifeTime + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the bullet life time the AI use when aiming this weapon. + // Arguments: None. + // Return value: A float with the life time in ms. + + unsigned long GetAIBulletLifeTime(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBulletAccScalar + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. + // Arguments: None. + // Return value: A float with the scalar. + + float GetBulletAccScalar(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIBlastRadius + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the blast radius the AI use when aiming this weapon. + // Arguments: None. + // Return value: A float with the blast radius in pixels. + + float GetAIBlastRadius() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIPenetration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how much material the projectiles from this weapon can destory. + // Arguments: None. + // Return value: A float with the material strength. + + float GetAIPenetration() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CompareTrajectories + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Estimates how close the projectiles from two weapons will land. + // Arguments: A HDFirearm pointer to compare with. + // Return value: A float with the distance in pixels. + + float CompareTrajectories(HDFirearm* pWeapon); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMagazinePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of the magazine or other equivalent point of + // this. + // Arguments: None. + // Return value: A vector describing the absolute world coordinates for the magazine + // attachment point of this + + Vector GetMagazinePos() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMuzzlePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of the muzzle or other equivalent point of + // this. + // Arguments: None. + // Return value: A vector describing the absolute world coordinates for the muzzle point + // of this + + Vector GetMuzzlePos() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMuzzleOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the unrotated relative offset from the position to the muzzle or + // other equivalent point of this. + // Arguments: None. + // Return value: A unrotated vector describing the relative for the muzzle point of + // this from this' position. + + Vector GetMuzzleOffset() const override { return m_MuzzleOff; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetMuzzleOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the unrotated relative offset from the position to the muzzle or + // other equivalent point of this. + // Arguments: Bew ofsset value. + // Return value: None. + + void SetMuzzleOffset(Vector newOffset) override { m_MuzzleOff = newOffset; } + + /// + /// Gets this HDFirearm's pre fire sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this HDFirearm's pre fire sound. + SoundContainer* GetPreFireSound() const { return m_PreFireSound; } + + /// + /// Sets this HDFirearm's pre fire sound. Ownership IS transferred! + /// + /// The new SoundContainer for this HDFirearm's pre fire sound. + void SetPreFireSound(SoundContainer* newSound) { m_PreFireSound = newSound; } + + /// + /// Gets this HDFirearm's fire sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this HDFirearm's fire sound. + SoundContainer* GetFireSound() const { return m_FireSound; } + + /// + /// Sets this HDFirearm's fire sound. Ownership IS transferred! + /// + /// The new SoundContainer for this HDFirearm's fire sound. + void SetFireSound(SoundContainer* newSound) { m_FireSound = newSound; } + + /// + /// Gets this HDFirearm's fire echo sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this HDFirearm's fire echo sound. + SoundContainer* GetFireEchoSound() const { return m_FireEchoSound; } + + /// + /// Sets this HDFirearm's fire echo sound. Ownership IS transferred! + /// + /// The new SoundContainer for this HDFirearm's fire echo sound. + void SetFireEchoSound(SoundContainer* newSound) { m_FireEchoSound = newSound; } + + /// + /// Gets this HDFirearm's active sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this HDFirearm's active sound. + SoundContainer* GetActiveSound() const { return m_ActiveSound; } + + /// + /// Sets this HDFirearm's active sound. Ownership IS transferred! + /// + /// The new SoundContainer for this HDFirearm's active sound. + void SetActiveSound(SoundContainer* newSound) { m_ActiveSound = newSound; } + + /// + /// Gets this HDFirearm's deactivation sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this HDFirearm's deactivation sound. + SoundContainer* GetDeactivationSound() const { return m_DeactivationSound; } + + /// + /// Sets this HDFirearm's deactivation sound. Ownership IS transferred! + /// + /// The new SoundContainer for this HDFirearm's deactivation sound. + void SetDeactivationSound(SoundContainer* newSound) { m_DeactivationSound = newSound; } + + /// + /// Gets this HDFirearm's empty sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this HDFirearm's empty sound. + SoundContainer* GetEmptySound() const { return m_EmptySound; } + + /// + /// Sets this HDFirearm's empty sound. Ownership IS transferred! + /// + /// The new SoundContainer for this HDFirearm's empty sound. + void SetEmptySound(SoundContainer* newSound) { m_EmptySound = newSound; } + + /// + /// Gets this HDFirearm's reload start sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this HDFirearm's reload start sound. + SoundContainer* GetReloadStartSound() const { return m_ReloadStartSound; } + + /// + /// Sets this HDFirearm's reload start sound. Ownership IS transferred! + /// + /// The new SoundContainer for this HDFirearm's reload start sound. + void SetReloadStartSound(SoundContainer* newSound) { m_ReloadStartSound = newSound; } + + /// + /// Gets this HDFirearm's reload end sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this HDFirearm's reload end sound. + SoundContainer* GetReloadEndSound() const { return m_ReloadEndSound; } + + /// + /// Sets this HDFirearm's reload end sound. Ownership IS transferred! + /// + /// The new SoundContainer for this HDFirearm's reload end sound. + void SetReloadEndSound(SoundContainer* newSound) { m_ReloadEndSound = newSound; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ResetAllTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resest all the timers used by this. Can be emitters, etc. This is to + // prevent backed up emissions to come out all at once while this has been + // held dormant in an inventory. + // Arguments: None. + // Return value: None. + + void ResetAllTimers() override { + HeldDevice::ResetAllTimers(); + m_LastFireTmr.Reset(); + m_ReloadTmr.Reset(); + } + + /// + /// Gets this HDFirearm's reload progress as a scalar from 0 to 1. + /// + /// The reload progress as a scalar from 0 to 1. + float GetReloadProgress() const { return IsReloading() && m_BaseReloadTime > 0 ? static_cast(m_ReloadTmr.SimTimeLimitProgress()) : 1.0F; } + + /// + /// Does the calculations necessary to detect whether this HDFirearm is at rest or not. IsAtRest() retrieves the answer. + /// + void RestDetection() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Activate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activates one of this HDFirearm's features. Analogous to 'pulling + // the trigger'. + // Arguments: None. + // Return value: None. + + void Activate() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Deactivate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing + // the trigger'. + // Arguments: None. + // Return value: None. + + void Deactivate() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StopActivationSound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Aborts playing of active sound no matter what. Used to silence spinning + // weapons when weapons swapped + // Arguments: None. + // Return value: None. + + void StopActivationSound(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reload + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Throws out the currently used Magazine, if any, and puts in a new one + // after the reload delay is up. + // Arguments: None. + // Return value: None. + + void Reload() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsReloading + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is curtrently being reloaded. + // Arguments: None. + // Return value: Whetehr being reloaded. + + bool IsReloading() const override { return m_Reloading; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DoneReloading + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device just finished reloading this frame. + // Arguments: None. + // Return value: Whether just done reloading this frame. + + bool DoneReloading() const override { return m_DoneReloading; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: NeedsReloading + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is curtrently in need of being reloaded. + // Arguments: None. + // Return value: Whetehr in need of reloading (ie not full). + + bool NeedsReloading() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsFull + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is curtrently full and reloading won't have + // any effect. + // Arguments: None. + // Return value: Whetehr magazine is full or not. + + bool IsFull() const override; + + bool IsEmpty() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsFullAuto + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is fully automatic or not. + // Arguments: None. + // Return value: Whether the player can hold down fire and this will fire repeatedly. + + bool IsFullAuto() const { return m_FullAuto; } + + /// + /// Gets whether this HDFirearm is set to be reloadable or not. + /// + /// Whether this HDFirearm is reloadable. + bool IsReloadable() const { return m_Reloadable; } + + /// + /// Sets whether this HDFirearm is reloadable or not and halts the reloading process. + /// + /// Whether this HDFirearm is reloadable. + void SetReloadable(bool isReloadable) { + m_Reloadable = isReloadable; + m_Reloading = m_Reloading && m_Reloadable; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetFullAuto + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether the device is fully automatic or not. + // Arguments: New value. + // Return value: None. + + void SetFullAuto(bool newValue) { m_FullAuto = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this HDFirearm's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws an aiming aid in front of this HeldDevice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // Which player's screen this is being drawn to. May affect what HUD elements + // get drawn etc. + // Return value: None. + + void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EstimateDigStrength + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Estimates what material strength one round in the magazine can destroy. + // Arguments: None. + // Return value: The maximum material strength the regular or the tracer round can destroy. + + float EstimateDigStrength() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FiredOnce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Whether at least one round has already been fired during the current activation. + // Arguments: None. + // Return value: Returns true if at least one round has already been fired during the current activation. + + bool FiredOnce() const { return m_FiredOnce; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FiredFrame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Whether at least one round has already been fired during the current frame. + // Arguments: None. + // Return value: Returns true at least one round has already been fired during the current frame. + + bool FiredFrame() const { return m_FireFrame; } + + /// + /// Gets whether this HDFirearm is ready to be fired. + /// + /// Whether this HDFirearm is ready to pop another Round. + bool CanFire() const { return m_LastFireTmr.IsPastSimMS(GetMSPerRound()); } + + /// + /// Gets whether this HDFirearm is halfway to be fired. Used for evenly spacing out dual-wielded fire. + /// + /// Whether this HDFirearm is halfway to pop another Round. + bool HalfwayToNextRound() const { return m_LastFireTmr.IsPastSimMS(GetMSPerRound() / 2.0); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RoundsFired + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: How many rounds were fired during this frame. + // Arguments: None. + // Return value: Returns the number of rounds fired during this frame. + + int RoundsFired() const { return m_RoundsFired; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsAnimatedManually + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: If true then the m_Frame property is not changed bye the Update function + // Arguments: None. + // Return value: Whether this HDFirearm is animated manually. + + bool IsAnimatedManually() const { return m_IsAnimatedManually; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetAnimatedManually + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets Whether this HDFirearm is animated manually. + // Arguments: Manual animation flag value. + // Return value: None. + + void SetAnimatedManually(bool newValue) { m_IsAnimatedManually = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Sets this Attachable's parent MOSRotating, and also sets its Team based on its parent and, if the Attachable is set to collide, adds/removes Atoms to its new/old parent. + /// Additionally, sets this HDFirearm as not firing or reloading, and resets its reload timer. + /// + /// A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! + void SetParent(MOSRotating* newParent) override { + HeldDevice::SetParent(newParent); + Deactivate(); + m_Reloading = false; + m_ReloadTmr.Reset(); + } + + // Member variables. + static Entity::ClassInfo m_sClass; + + // The mag reference which all current mags are generated from. + // NOT owned by this, owned by PresetMan + const Magazine* m_pMagazineReference; + // Magazine MovableObject. Owned + Magazine* m_pMagazine; + // Muzzle Flash Attachable. Owned + Attachable* m_pFlash; + + SoundContainer* m_PreFireSound; //!< The sound this HDFirearm should play before it starts firing. Distinct from activation sound in that it will play exactly once per trigger pull and not pitch up. + // The audio of this FireArm being fired. + SoundContainer* m_FireSound; + SoundContainer* m_FireEchoSound; //!< The audio that is played as the echo for the gun. Each shot will restart this sound, so it doesn't ever overlap. + // The audio that is played immediately upon activation, but perhaps before actual first firing, if there's a pre-delay + SoundContainer* m_ActiveSound; + // The audio that is played immediately upon cease of activation + SoundContainer* m_DeactivationSound; + // The audio of this FireArm being fired empty. + SoundContainer* m_EmptySound; + // The audio of this FireArm being reloaded. + SoundContainer* m_ReloadStartSound; + SoundContainer* m_ReloadEndSound; + // The offset of how long before the reload finishes the sound plays + float m_ReloadEndOffset; + // Whether or not the end-of-relaod sound has already been played or not. + bool m_HasPlayedEndReloadSound; + + // Rate of fire, in rounds per min. + // If 0, firearm is semi-automatic (ie only one discharge per activation). + int m_RateOfFire; + // Delay between activation and full round output is achieved, in ms + int m_ActivationDelay; + // Delay between release of activation and another can be started, in ms + int m_DeactivationDelay; + // Reloading or not + bool m_Reloading; + // Just done reloading this frame + bool m_DoneReloading; + // Base reload time in millisecs. + int m_BaseReloadTime; + // Whether this HDFirearm is full or semi-auto. + bool m_FullAuto; + // Whether particles fired from this HDFirearm will ignore hits with itself, + // and the root parent of this HDFirearm, regardless if they are set to hit MOs. + bool m_FireIgnoresThis; + bool m_Reloadable; //!< Whether this HDFirearm is reloadable by normal means. + float m_OneHandedReloadTimeMultiplier; //!< The multiplier for how long this weapon takes to reload when being used one-handed. Only relevant for one-handed weapons. + bool m_DualReloadable; //!< Whether or not this weapon can be dual-reloaded, i.e. both guns can reload at once instead of having to wait til the other dual-wielded gun isn't being reloaded. Only relevant for one-handed weapons. + float m_ReloadAngle; //!< The angle offset for the default reload animation, in radians. + float m_OneHandedReloadAngle; //!< The angle offset for one-handed reload animation, in radians. + + // Timer for timing how long ago the last round was fired. + Timer m_LastFireTmr; + // Timer for timing reload times. + Timer m_ReloadTmr; + + // The point from where the projectiles appear. + Vector m_MuzzleOff; + // The point from where the discharged shells appear. + Vector m_EjectOff; + // Offset to magazine. + Vector m_MagOff; + // Range of normal shaking of entire weapon. + float m_ShakeRange; + // Range of shaking of entire weapon during sharp aiming. + float m_SharpShakeRange; + // Factor for how much more weapon shakes if it isn't supported by a second hand. + float m_NoSupportFactor; + // Range of spread angle of fired particles, in one direction + float m_ParticleSpreadRange; + // Angle in which shells are ejected relative to this weapon + float m_ShellEjectAngle; + // Range of spread angle of ejected shells, in one direction + float m_ShellSpreadRange; + // Range of spread in ang vel of ejected shells, in one direction + float m_ShellAngVelRange; + float m_ShellVelVariation; //!< The velocity variation scalar of ejected shells. + // The amount of screenshake that recoil causes + float m_RecoilScreenShakeAmount; + // The muzzle velocity the AI use when aiming this weapon + float m_AIFireVel; + // The bullet life time the AI use when aiming this weapon + unsigned long m_AIBulletLifeTime; + // The bullet acc scalar the AI use when aiming this weapon + float m_AIBulletAccScalar; + + // Whether at least one round has already been + // fired during the current activation. + bool m_FiredOnce; + // Whether at least one round has already been + // fired during the current frame. + bool m_FireFrame; + // Whether at least one round was fired during the last frame + bool m_FiredLastFrame; + // Whether, if this HDFireArm is empty, pin has already clicked once during + // current acticvation. + bool m_AlreadyClicked; + // How many rounds were fired during this frame + int m_RoundsFired; + // If true m_Frame is not changed during an update hence the animation + // is done by external Lua code + bool m_IsAnimatedManually; + + bool m_LegacyCompatibilityRoundsAlwaysFireUnflipped; // + /// Ensures the reload Timer's time limit is set accordingly, based on whether the HDFirearm has support available. + /// + void CorrectReloadTimerForSupportAvailable() { m_ReloadTmr.SetSimTimeLimitMS(static_cast(static_cast(m_BaseReloadTime) * (m_SupportAvailable ? 1.0F : m_OneHandedReloadTimeMultiplier))); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this HDFirearm, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + HDFirearm(const HDFirearm& reference) = delete; + HDFirearm& operator=(const HDFirearm& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/HeldDevice.cpp b/Source/Entities/HeldDevice.cpp index 4e3aa73d85..ce460701cf 100644 --- a/Source/Entities/HeldDevice.cpp +++ b/Source/Entities/HeldDevice.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -29,577 +28,559 @@ namespace RTE { -ConcreteClassInfo(HeldDevice, Attachable, 50); + ConcreteClassInfo(HeldDevice, Attachable, 50); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this HeldDevice, effectively + // resetting the members of this abstraction level only. + + void HeldDevice::Clear() { + m_HeldDeviceType = WEAPON; + m_Activated = false; + m_ActivationTimer.Reset(); + m_OneHanded = false; + m_DualWieldable = false; + m_StanceOffset.Reset(); + m_SharpStanceOffset.Reset(); + m_SharpAim = 0.0F; + m_MaxSharpLength = 0; + m_Supportable = true; + m_Supported = false; + m_SupportAvailable = false; + m_SupportOffset.Reset(); + m_UseSupportOffsetWhileReloading = true; + m_SeenByPlayer.fill(false); + m_IsUnPickupable = false; + m_PickupableByPresetNames.clear(); + m_GripStrengthMultiplier = 1.0F; + m_BlinkTimer.Reset(); + m_BlinkTimer.SetSimTimeLimitMS(1000); + m_Loudness = -1; + m_IsExplosiveWeapon = false; + m_GetsHitByMOsWhenHeld = false; + m_VisualRecoilMultiplier = 1.0F; + + // NOTE: This special override of a parent class member variable avoids needing an extra variable to avoid overwriting INI values. + m_CollidesWithTerrainWhileAttached = false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this HeldDevice, effectively -// resetting the members of this abstraction level only. - -void HeldDevice::Clear() -{ - m_HeldDeviceType = WEAPON; - m_Activated = false; - m_ActivationTimer.Reset(); - m_OneHanded = false; - m_DualWieldable = false; - m_StanceOffset.Reset(); - m_SharpStanceOffset.Reset(); - m_SharpAim = 0.0F; - m_MaxSharpLength = 0; - m_Supportable = true; - m_Supported = false; - m_SupportAvailable = false; - m_SupportOffset.Reset(); - m_UseSupportOffsetWhileReloading = true; - m_SeenByPlayer.fill(false); - m_IsUnPickupable = false; - m_PickupableByPresetNames.clear(); - m_GripStrengthMultiplier = 1.0F; - m_BlinkTimer.Reset(); - m_BlinkTimer.SetSimTimeLimitMS(1000); - m_Loudness = -1; - m_IsExplosiveWeapon = false; - m_GetsHitByMOsWhenHeld = false; - m_VisualRecoilMultiplier = 1.0F; - - // NOTE: This special override of a parent class member variable avoids needing an extra variable to avoid overwriting INI values. - m_CollidesWithTerrainWhileAttached = false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Round object ready for use. + + int HeldDevice::Create() { + if (Attachable::Create() < 0) + return -1; + + // Set MO Type. + m_MOType = MovableObject::TypeHeldDevice; + + // Set HeldDeviceType based on tags + if (IsInGroup("Weapons")) + m_HeldDeviceType = WEAPON; + else if (IsInGroup("Tools")) + m_HeldDeviceType = TOOL; + else if (IsInGroup("Shields")) + m_HeldDeviceType = SHIELD; + + if (IsInGroup("Weapons - Explosive")) + m_IsExplosiveWeapon = true; + else + m_IsExplosiveWeapon = false; + + // Backwards compatibility so that the tag is added for sure + if (m_HeldDeviceType == WEAPON) + AddToGroup("Weapons"); + else if (m_HeldDeviceType == TOOL) + AddToGroup("Tools"); + else if (m_HeldDeviceType == SHIELD) + AddToGroup("Shields"); + + // No Loudness set in the ini-file + if (m_Loudness < 0) { + if (m_HeldDeviceType == TOOL) + m_Loudness = 0.5; // Force tools to make less noise + else + m_Loudness = 1.0; + } + // Make it so held devices are dropped gently when their parent gibs + m_ParentGibBlastStrengthMultiplier = 0.0F; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Round object ready for use. - -int HeldDevice::Create() -{ - if (Attachable::Create() < 0) - return -1; - - // Set MO Type. - m_MOType = MovableObject::TypeHeldDevice; - - // Set HeldDeviceType based on tags - if (IsInGroup("Weapons")) - m_HeldDeviceType = WEAPON; - else if (IsInGroup("Tools")) - m_HeldDeviceType = TOOL; - else if (IsInGroup("Shields")) - m_HeldDeviceType = SHIELD; - - if (IsInGroup("Weapons - Explosive")) - m_IsExplosiveWeapon = true; - else - m_IsExplosiveWeapon = false; - - // Backwards compatibility so that the tag is added for sure - if (m_HeldDeviceType == WEAPON) - AddToGroup("Weapons"); - else if (m_HeldDeviceType == TOOL) - AddToGroup("Tools"); - else if (m_HeldDeviceType == SHIELD) - AddToGroup("Shields"); - - // No Loudness set in the ini-file - if (m_Loudness < 0) - { - if (m_HeldDeviceType == TOOL) - m_Loudness = 0.5; // Force tools to make less noise - else - m_Loudness = 1.0; - } - - // Make it so held devices are dropped gently when their parent gibs - m_ParentGibBlastStrengthMultiplier = 0.0F; - - // Make it so users can't accidentally set this to true for HeldDevices, since it'll cause crashes when swapping inventory items around. - m_DeleteWhenRemovedFromParent = false; - - // All HeldDevice:s by default avoid hitting and getting physically hit by AtomGoups when they are at rest - m_IgnoresAGHitsWhenSlowerThan = 1.0; - - // By default, held items should not be able to be squished and destroyed into the ground at all - m_CanBeSquished = false; - - return 0; -} + // Make it so users can't accidentally set this to true for HeldDevices, since it'll cause crashes when swapping inventory items around. + m_DeleteWhenRemovedFromParent = false; + // All HeldDevice:s by default avoid hitting and getting physically hit by AtomGoups when they are at rest + m_IgnoresAGHitsWhenSlowerThan = 1.0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a HeldDevice to be identical to another, by deep copy. - -int HeldDevice::Create(const HeldDevice &reference) -{ - Attachable::Create(reference); - - // Set MO Type. - m_MOType = MovableObject::TypeHeldDevice; - - m_HeldDeviceType = reference.m_HeldDeviceType; - - m_Activated = reference.m_Activated; - m_ActivationTimer = reference.m_ActivationTimer; - - m_OneHanded = reference.m_OneHanded; - m_DualWieldable = reference.m_DualWieldable; - m_StanceOffset = reference.m_StanceOffset; - m_SharpStanceOffset = reference.m_SharpStanceOffset; - m_SupportOffset = reference.m_SupportOffset; - m_UseSupportOffsetWhileReloading = reference.m_UseSupportOffsetWhileReloading; - m_Supportable = reference.m_Supportable; - m_IsUnPickupable = reference.m_IsUnPickupable; - for (std::string referenceActorWhoCanPickThisUp : reference.m_PickupableByPresetNames) { - m_PickupableByPresetNames.insert(referenceActorWhoCanPickThisUp); - } - m_GripStrengthMultiplier = reference.m_GripStrengthMultiplier; - - m_SharpAim = reference.m_SharpAim; - m_MaxSharpLength = reference.m_MaxSharpLength; - m_Supportable = reference.m_Supportable; - m_Supported = reference.m_Supported; - m_SupportAvailable = reference.m_SupportAvailable; - m_Loudness = reference.m_Loudness; - m_IsExplosiveWeapon = reference.m_IsExplosiveWeapon; - m_GetsHitByMOsWhenHeld = reference.m_GetsHitByMOsWhenHeld; - m_VisualRecoilMultiplier = reference.m_VisualRecoilMultiplier; - - return 0; -} + // By default, held items should not be able to be squished and destroyed into the ground at all + m_CanBeSquished = false; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int HeldDevice::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Attachable::ReadProperty(propName, reader)); - - MatchProperty("HeldDeviceType", { reader >> m_HeldDeviceType; }); - MatchProperty("OneHanded", { reader >> m_OneHanded; }); - MatchProperty("DualWieldable", { reader >> m_DualWieldable; }); - MatchProperty("StanceOffset", { reader >> m_StanceOffset; }); - MatchProperty("SharpStanceOffset", { reader >> m_SharpStanceOffset; }); - MatchProperty("Supportable", { reader >> m_Supportable; }); - MatchProperty("SupportOffset", { reader >> m_SupportOffset; }); - MatchProperty("UseSupportOffsetWhileReloading", { reader >> m_UseSupportOffsetWhileReloading; }); - MatchProperty("PickupableBy", { - std::string pickupableByValue = reader.ReadPropValue(); - if (pickupableByValue == "PickupableByEntries") { - while (reader.NextProperty()) { - std::string pickupableByEntryType = reader.ReadPropName(); - if (pickupableByEntryType == "AddPresetNameEntry") { - m_PickupableByPresetNames.insert(reader.ReadPropValue()); - } else if (pickupableByEntryType == "AddClassNameEntry ") { - reader.ReportError("AddClassNameEntry is not yet supported."); - } else if (pickupableByEntryType == "AddGroupEntry") { - reader.ReportError("AddGroupEntry is not yet supported."); - } else if (pickupableByEntryType == "AddDataModuleEntry ") { - reader.ReportError("AddDataModuleEntry is not yet supported."); - } else { - break; - } - } - } else if (pickupableByValue == "None") { - SetUnPickupable(true); - } - }); - MatchProperty("GripStrengthMultiplier", { reader >> m_GripStrengthMultiplier; }); - MatchProperty("SharpLength", { reader >> m_MaxSharpLength; }); - MatchProperty("Loudness", { reader >> m_Loudness; }); - MatchProperty("GetsHitByMOsWhenHeld", { reader >> m_GetsHitByMOsWhenHeld; }); - MatchProperty("VisualRecoilMultiplier", { reader >> m_VisualRecoilMultiplier; }); - MatchProperty("SpecialBehaviour_Activated", { reader >> m_Activated; }); - MatchProperty("SpecialBehaviour_ActivationTimerElapsedSimTimeMS", { - double elapsedSimTimeMS; - reader >> elapsedSimTimeMS; - m_ActivationTimer.SetElapsedSimTimeMS(elapsedSimTimeMS); - }); - - EndPropertyList; -} - + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a HeldDevice to be identical to another, by deep copy. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this HeldDevice with a Writer for -// later recreation with Create(Reader &reader); - -int HeldDevice::Save(Writer &writer) const -{ - Attachable::Save(writer); -/* - writer.NewLine(); - writer << "// 0 = Offensive Weapon, 1 = Tool, 2 = Shield"; - writer.NewProperty("HeldDeviceType"); - writer << m_HeldDeviceType; -*/ - writer.NewProperty("OneHanded"); - writer << m_OneHanded; - writer.NewProperty("StanceOffset"); - writer << m_StanceOffset; - writer.NewProperty("SharpStanceOffset"); - writer << m_SharpStanceOffset; - writer.NewPropertyWithValue("Supportable", m_Supportable); - writer.NewProperty("SupportOffset"); - writer << m_SupportOffset; - writer.NewPropertyWithValue("UseSupportOffsetWhileReloading", m_UseSupportOffsetWhileReloading); - writer.NewProperty("GripStrengthMultiplier"); - writer << m_GripStrengthMultiplier; - writer.NewProperty("SharpLength"); - writer << m_MaxSharpLength; - writer.NewProperty("Loudness"); - writer << m_Loudness; - writer.NewProperty("GetsHitByMOsWhenHeld"); - writer << m_GetsHitByMOsWhenHeld; - writer.NewProperty("VisualRecoilMultiplier"); - writer << m_VisualRecoilMultiplier; - - return 0; -} + int HeldDevice::Create(const HeldDevice& reference) { + Attachable::Create(reference); + // Set MO Type. + m_MOType = MovableObject::TypeHeldDevice; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the HeldDevice object. + m_HeldDeviceType = reference.m_HeldDeviceType; -void HeldDevice::Destroy(bool notInherited) -{ + m_Activated = reference.m_Activated; + m_ActivationTimer = reference.m_ActivationTimer; - if (!notInherited) - Attachable::Destroy(); - Clear(); -} + m_OneHanded = reference.m_OneHanded; + m_DualWieldable = reference.m_DualWieldable; + m_StanceOffset = reference.m_StanceOffset; + m_SharpStanceOffset = reference.m_SharpStanceOffset; + m_SupportOffset = reference.m_SupportOffset; + m_UseSupportOffsetWhileReloading = reference.m_UseSupportOffsetWhileReloading; + m_Supportable = reference.m_Supportable; + m_IsUnPickupable = reference.m_IsUnPickupable; + for (std::string referenceActorWhoCanPickThisUp: reference.m_PickupableByPresetNames) { + m_PickupableByPresetNames.insert(referenceActorWhoCanPickThisUp); + } + m_GripStrengthMultiplier = reference.m_GripStrengthMultiplier; + + m_SharpAim = reference.m_SharpAim; + m_MaxSharpLength = reference.m_MaxSharpLength; + m_Supportable = reference.m_Supportable; + m_Supported = reference.m_Supported; + m_SupportAvailable = reference.m_SupportAvailable; + m_Loudness = reference.m_Loudness; + m_IsExplosiveWeapon = reference.m_IsExplosiveWeapon; + m_GetsHitByMOsWhenHeld = reference.m_GetsHitByMOsWhenHeld; + m_VisualRecoilMultiplier = reference.m_VisualRecoilMultiplier; + + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int HeldDevice::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Attachable::ReadProperty(propName, reader)); + + MatchProperty("HeldDeviceType", { reader >> m_HeldDeviceType; }); + MatchProperty("OneHanded", { reader >> m_OneHanded; }); + MatchProperty("DualWieldable", { reader >> m_DualWieldable; }); + MatchProperty("StanceOffset", { reader >> m_StanceOffset; }); + MatchProperty("SharpStanceOffset", { reader >> m_SharpStanceOffset; }); + MatchProperty("Supportable", { reader >> m_Supportable; }); + MatchProperty("SupportOffset", { reader >> m_SupportOffset; }); + MatchProperty("UseSupportOffsetWhileReloading", { reader >> m_UseSupportOffsetWhileReloading; }); + MatchProperty("PickupableBy", { + std::string pickupableByValue = reader.ReadPropValue(); + if (pickupableByValue == "PickupableByEntries") { + while (reader.NextProperty()) { + std::string pickupableByEntryType = reader.ReadPropName(); + if (pickupableByEntryType == "AddPresetNameEntry") { + m_PickupableByPresetNames.insert(reader.ReadPropValue()); + } else if (pickupableByEntryType == "AddClassNameEntry ") { + reader.ReportError("AddClassNameEntry is not yet supported."); + } else if (pickupableByEntryType == "AddGroupEntry") { + reader.ReportError("AddGroupEntry is not yet supported."); + } else if (pickupableByEntryType == "AddDataModuleEntry ") { + reader.ReportError("AddDataModuleEntry is not yet supported."); + } else { + break; + } + } + } else if (pickupableByValue == "None") { + SetUnPickupable(true); + } + }); + MatchProperty("GripStrengthMultiplier", { reader >> m_GripStrengthMultiplier; }); + MatchProperty("SharpLength", { reader >> m_MaxSharpLength; }); + MatchProperty("Loudness", { reader >> m_Loudness; }); + MatchProperty("GetsHitByMOsWhenHeld", { reader >> m_GetsHitByMOsWhenHeld; }); + MatchProperty("VisualRecoilMultiplier", { reader >> m_VisualRecoilMultiplier; }); + MatchProperty("SpecialBehaviour_Activated", { reader >> m_Activated; }); + MatchProperty("SpecialBehaviour_ActivationTimerElapsedSimTimeMS", { + double elapsedSimTimeMS; + reader >> elapsedSimTimeMS; + m_ActivationTimer.SetElapsedSimTimeMS(elapsedSimTimeMS); + }); + + EndPropertyList; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetStanceOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current position offset of this HeldDevice's joint relative -// from the parent Actor's position, if attached. -// Arguments: None. -// Return value: A const reference to the current stance parent offset. - -Vector HeldDevice::GetStanceOffset() const -{ - if (m_SharpAim > 0) { - float rotAngleScalar = std::abs(std::sin(GetRootParent()->GetRotAngle())); - // Deviate the vertical axis towards regular StanceOffset based on the user's rotation so that sharp aiming doesn't look awkward when prone - return Vector(m_SharpStanceOffset.GetX(), m_SharpStanceOffset.GetY() * (1.0F - rotAngleScalar) + m_StanceOffset.GetY() * rotAngleScalar).GetXFlipped(m_HFlipped); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this HeldDevice with a Writer for + // later recreation with Create(Reader &reader); + + int HeldDevice::Save(Writer& writer) const { + Attachable::Save(writer); + /* + writer.NewLine(); + writer << "// 0 = Offensive Weapon, 1 = Tool, 2 = Shield"; + writer.NewProperty("HeldDeviceType"); + writer << m_HeldDeviceType; + */ + writer.NewProperty("OneHanded"); + writer << m_OneHanded; + writer.NewProperty("StanceOffset"); + writer << m_StanceOffset; + writer.NewProperty("SharpStanceOffset"); + writer << m_SharpStanceOffset; + writer.NewPropertyWithValue("Supportable", m_Supportable); + writer.NewProperty("SupportOffset"); + writer << m_SupportOffset; + writer.NewPropertyWithValue("UseSupportOffsetWhileReloading", m_UseSupportOffsetWhileReloading); + writer.NewProperty("GripStrengthMultiplier"); + writer << m_GripStrengthMultiplier; + writer.NewProperty("SharpLength"); + writer << m_MaxSharpLength; + writer.NewProperty("Loudness"); + writer << m_Loudness; + writer.NewProperty("GetsHitByMOsWhenHeld"); + writer << m_GetsHitByMOsWhenHeld; + writer.NewProperty("VisualRecoilMultiplier"); + writer << m_VisualRecoilMultiplier; + + return 0; } - else - return m_StanceOffset.GetXFlipped(m_HFlipped); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the HeldDevice object. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSupportPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of the support handhold that this HeldDevice -// offers. + void HeldDevice::Destroy(bool notInherited) { -Vector HeldDevice::GetSupportPos() const -{ -/* - Vector rotOff(m_SupportOffset.GetYFlipped(m_HFlipped)); - rotOff.RadRotate(m_HFlipped ? (c_PI + m_Rotation) : m_Rotation); - return m_Pos + rotOff; -*/ - return m_Pos + RotateOffset(m_SupportOffset); -} + if (!notInherited) + Attachable::Destroy(); + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetStanceOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current position offset of this HeldDevice's joint relative + // from the parent Actor's position, if attached. + // Arguments: None. + // Return value: A const reference to the current stance parent offset. + + Vector HeldDevice::GetStanceOffset() const { + if (m_SharpAim > 0) { + float rotAngleScalar = std::abs(std::sin(GetRootParent()->GetRotAngle())); + // Deviate the vertical axis towards regular StanceOffset based on the user's rotation so that sharp aiming doesn't look awkward when prone + return Vector(m_SharpStanceOffset.GetX(), m_SharpStanceOffset.GetY() * (1.0F - rotAngleScalar) + m_StanceOffset.GetY() * rotAngleScalar).GetXFlipped(m_HFlipped); + } else + return m_StanceOffset.GetXFlipped(m_HFlipped); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMagazinePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of the magazine or other equivalent point of -// this. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSupportPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of the support handhold that this HeldDevice + // offers. + + Vector HeldDevice::GetSupportPos() const { + /* + Vector rotOff(m_SupportOffset.GetYFlipped(m_HFlipped)); + rotOff.RadRotate(m_HFlipped ? (c_PI + m_Rotation) : m_Rotation); + return m_Pos + rotOff; + */ + return m_Pos + RotateOffset(m_SupportOffset); + } -Vector HeldDevice::GetMagazinePos() const -{ - return m_Pos; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMagazinePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of the magazine or other equivalent point of + // this. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Vector HeldDevice::GetMagazinePos() const { + return m_Pos; + } -bool HeldDevice::IsBeingHeld() const { - return dynamic_cast(m_Parent); -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool HeldDevice::IsBeingHeld() const { + return dynamic_cast(m_Parent); + } -void HeldDevice::RemovePickupableByPresetName(const std::string &actorPresetName) { - std::unordered_set::iterator pickupableByPresetNameEntry = m_PickupableByPresetNames.find(actorPresetName); - if (pickupableByPresetNameEntry != m_PickupableByPresetNames.end()) { m_PickupableByPresetNames.erase(pickupableByPresetNameEntry); } -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void HeldDevice::RemovePickupableByPresetName(const std::string& actorPresetName) { + std::unordered_set::iterator pickupableByPresetNameEntry = m_PickupableByPresetNames.find(actorPresetName); + if (pickupableByPresetNameEntry != m_PickupableByPresetNames.end()) { + m_PickupableByPresetNames.erase(pickupableByPresetNameEntry); + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. -bool HeldDevice::CollideAtPoint(HitData &hd) -{ - if (!m_GetsHitByMOsWhenHeld && IsBeingHeld()) { - return false; - } + bool HeldDevice::CollideAtPoint(HitData& hd) { + if (!m_GetsHitByMOsWhenHeld && IsBeingHeld()) { + return false; + } - return Attachable::CollideAtPoint(hd); -} + return Attachable::CollideAtPoint(hd); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Activate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activates this HDFirearm. Analogous to 'pulling the trigger'. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Activate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activates this HDFirearm. Analogous to 'pulling the trigger'. + void HeldDevice::Activate() { + if (!m_Activated) { + m_ActivationTimer.Reset(); + } + m_Activated = true; + } -void HeldDevice::Activate() -{ - if (!m_Activated) { m_ActivationTimer.Reset(); } - m_Activated = true; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Deactivate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing + // the trigger'. + void HeldDevice::Deactivate() { + m_Activated = false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Deactivate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing -// the trigger'. - -void HeldDevice::Deactivate() -{ - m_Activated = false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool HeldDevice::TransferJointImpulses(Vector &jointImpulses, float jointStiffnessValueToUse, float jointStrengthValueToUse, float gibImpulseLimitValueToUse) { - MovableObject *parent = m_Parent; - if (!parent) { - return false; - } - if (m_ImpulseForces.empty()) { - return true; - } - const Arm *parentAsArm = dynamic_cast(parent); - if (parentAsArm && parentAsArm->GetGripStrength() > 0 && jointStrengthValueToUse < 0) { - jointStrengthValueToUse = parentAsArm->GetGripStrength() * m_GripStrengthMultiplier; - if (m_Supported) { - if (const AHuman *rootParentAsAHuman = dynamic_cast(GetRootParent())) { jointStrengthValueToUse += rootParentAsAHuman->GetBGArm() ? rootParentAsAHuman->GetBGArm()->GetGripStrength() * m_GripStrengthMultiplier : 0.0F; } - } - } - bool intact = Attachable::TransferJointImpulses(jointImpulses, jointStiffnessValueToUse, jointStrengthValueToUse, gibImpulseLimitValueToUse); - if (!intact) { - Actor *rootParentAsActor = dynamic_cast(parent->GetRootParent()); - if (rootParentAsActor && rootParentAsActor->GetStatus() == Actor::STABLE) { rootParentAsActor->SetStatus(Actor::UNSTABLE); } - } - return intact; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Travel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Travels this, using its physical representation. -// Arguments: None. -// Return value: None. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void HeldDevice::Travel() -{ - Attachable::Travel(); -} -*/ + bool HeldDevice::TransferJointImpulses(Vector& jointImpulses, float jointStiffnessValueToUse, float jointStrengthValueToUse, float gibImpulseLimitValueToUse) { + MovableObject* parent = m_Parent; + if (!parent) { + return false; + } + if (m_ImpulseForces.empty()) { + return true; + } + const Arm* parentAsArm = dynamic_cast(parent); + if (parentAsArm && parentAsArm->GetGripStrength() > 0 && jointStrengthValueToUse < 0) { + jointStrengthValueToUse = parentAsArm->GetGripStrength() * m_GripStrengthMultiplier; + if (m_Supported) { + if (const AHuman* rootParentAsAHuman = dynamic_cast(GetRootParent())) { + jointStrengthValueToUse += rootParentAsAHuman->GetBGArm() ? rootParentAsAHuman->GetBGArm()->GetGripStrength() * m_GripStrengthMultiplier : 0.0F; + } + } + } + bool intact = Attachable::TransferJointImpulses(jointImpulses, jointStiffnessValueToUse, jointStrengthValueToUse, gibImpulseLimitValueToUse); + if (!intact) { + Actor* rootParentAsActor = dynamic_cast(parent->GetRootParent()); + if (rootParentAsActor && rootParentAsActor->GetStatus() == Actor::STABLE) { + rootParentAsActor->SetStatus(Actor::UNSTABLE); + } + } + return intact; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this HeldDevice. Supposed to be done every frame. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void HeldDevice::Update() -{ - Attachable::Update(); + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Travel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Travels this, using its physical representation. + // Arguments: None. + // Return value: None. - // Remove loose items that have completely disappeared into the terrain, unless they're pinned - if (!m_Parent && m_PinStrength <= 0 && m_RestTimer.IsPastSimMS(20000) && m_CanBeSquished && m_pAtomGroup->RatioInTerrain() > 0.9) - GibThis(); + void HeldDevice::Travel() + { + Attachable::Travel(); + } + */ - if (m_Activated) - m_RestTimer.Reset(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this HeldDevice. Supposed to be done every frame. - //////////////////////////////////////// - // Animate the sprite, if applicable + void HeldDevice::Update() { + Attachable::Update(); - if (m_FrameCount > 1) - { - if (m_SpriteAnimMode == LOOPWHENACTIVE && m_Activated) - { - float cycleTime = ((long)m_SpriteAnimTimer.GetElapsedSimTimeMS()) % m_SpriteAnimDuration; - m_Frame = std::floor((cycleTime / (float)m_SpriteAnimDuration) * (float)m_FrameCount); - } - } + // Remove loose items that have completely disappeared into the terrain, unless they're pinned + if (!m_Parent && m_PinStrength <= 0 && m_RestTimer.IsPastSimMS(20000) && m_CanBeSquished && m_pAtomGroup->RatioInTerrain() > 0.9) + GibThis(); - if (!m_Parent) { + if (m_Activated) + m_RestTimer.Reset(); - } - else { - ///////////////////////////////// - // Update and apply rotations and scale + //////////////////////////////////////// + // Animate the sprite, if applicable - // Taken care of by holder/owner Arm. -// m_Pos += m_ParentOffset; -// Don't apply state changes to BITMAP anywhere else than Draw(). -// m_aSprite->SetAngle(m_Rotation); -// m_aSprite->SetScale(m_Scale); - } + if (m_FrameCount > 1) { + if (m_SpriteAnimMode == LOOPWHENACTIVE && m_Activated) { + float cycleTime = ((long)m_SpriteAnimTimer.GetElapsedSimTimeMS()) % m_SpriteAnimDuration; + m_Frame = std::floor((cycleTime / (float)m_SpriteAnimDuration) * (float)m_FrameCount); + } + } - if (m_BlinkTimer.IsPastSimTimeLimit()) { m_BlinkTimer.Reset(); } -} + if (!m_Parent) { + } else { + ///////////////////////////////// + // Update and apply rotations and scale + + // Taken care of by holder/owner Arm. + // m_Pos += m_ParentOffset; + // Don't apply state changes to BITMAP anywhere else than Draw(). + // m_aSprite->SetAngle(m_Rotation); + // m_aSprite->SetScale(m_Scale); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this HeldDevice's current graphical representation to a -// BITMAP of choice. - -void HeldDevice::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); -/* - // Draw suporting hand if applicable. - if (m_Supported) { - Vector handPos(m_Pos.GetFloored() + - RotateOffset(m_SupportOffset) + - (m_Recoiled ? m_RecoilOffset : Vector()) - - targetPos); - handPos.m_X -= m_pSupportHand->GetWidth() >> 1; - handPos.m_Y -= m_pSupportHand->GetHeight() >> 1; - if (!m_HFlipped) - m_pSupportHand->DrawTrans(pTargetBitmap, handPos.m_X, handPos.m_Y); - else - m_pSupportHand->DrawTransHFlip(pTargetBitmap, handPos.m_X, handPos.m_Y); - } -*/ -/* -#ifdef DEBUG_BUILD - if (mode == g_DrawColor && !onlyPhysical) - { - m_pAtomGroup->Draw(pTargetBitmap, targetPos, false, 122); - m_pDeepGroup->Draw(pTargetBitmap, targetPos, false, 13); - } -#endif -*/ -} + if (m_BlinkTimer.IsPastSimTimeLimit()) { + m_BlinkTimer.Reset(); + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this HeldDevice's current graphical representation to a + // BITMAP of choice. + + void HeldDevice::Draw(BITMAP* pTargetBitmap, + const Vector& targetPos, + DrawMode mode, + bool onlyPhysical) const { + Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + /* + // Draw suporting hand if applicable. + if (m_Supported) { + Vector handPos(m_Pos.GetFloored() + + RotateOffset(m_SupportOffset) + + (m_Recoiled ? m_RecoilOffset : Vector()) - + targetPos); + handPos.m_X -= m_pSupportHand->GetWidth() >> 1; + handPos.m_Y -= m_pSupportHand->GetHeight() >> 1; + if (!m_HFlipped) + m_pSupportHand->DrawTrans(pTargetBitmap, handPos.m_X, handPos.m_Y); + else + m_pSupportHand->DrawTransHFlip(pTargetBitmap, handPos.m_X, handPos.m_Y); + } + */ + /* + #ifdef DEBUG_BUILD + if (mode == g_DrawColor && !onlyPhysical) + { + m_pAtomGroup->Draw(pTargetBitmap, targetPos, false, 122); + m_pDeepGroup->Draw(pTargetBitmap, targetPos, false, 13); + } + #endif + */ + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Actor's current graphical HUD overlay representation to a -// BITMAP of choice. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Actor's current graphical HUD overlay representation to a + // BITMAP of choice. -void HeldDevice::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScreen, bool playerControlled) { - if (!m_HUDVisible) { - return; - } + void HeldDevice::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { + if (!m_HUDVisible) { + return; + } - Attachable::DrawHUD(pTargetBitmap, targetPos, whichScreen); + Attachable::DrawHUD(pTargetBitmap, targetPos, whichScreen); - if (!IsUnPickupable()) { - if (m_Parent) { - m_SeenByPlayer.fill(false); - m_BlinkTimer.Reset(); - } else { - int viewingPlayer = g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen); - if (viewingPlayer == -1) { - return; - } - // Only draw if the team viewing this has seen the space where this is located. - int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(viewingPlayer); - if (viewingTeam == Activity::NoTeam || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam)) { - return; - } + if (!IsUnPickupable()) { + if (m_Parent) { + m_SeenByPlayer.fill(false); + m_BlinkTimer.Reset(); + } else { + int viewingPlayer = g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen); + if (viewingPlayer == -1) { + return; + } + // Only draw if the team viewing this has seen the space where this is located. + int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(viewingPlayer); + if (viewingTeam == Activity::NoTeam || g_SceneMan.IsUnseen(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), viewingTeam)) { + return; + } - Vector drawPos = m_Pos - targetPos; - // Adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam. - if (!targetPos.IsZero()) { - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) { - if ((targetPos.GetFloorIntX() < 0) && (m_Pos.GetFloorIntX() > (sceneWidth - pTargetBitmap->w))) { - drawPos.m_X -= static_cast(sceneWidth); - } else if ((targetPos.GetFloorIntX() + pTargetBitmap->w > sceneWidth) && (m_Pos.GetFloorIntX() < pTargetBitmap->w)) { - drawPos.m_X += static_cast(sceneWidth); + Vector drawPos = m_Pos - targetPos; + // Adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam. + if (!targetPos.IsZero()) { + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) { + if ((targetPos.GetFloorIntX() < 0) && (m_Pos.GetFloorIntX() > (sceneWidth - pTargetBitmap->w))) { + drawPos.m_X -= static_cast(sceneWidth); + } else if ((targetPos.GetFloorIntX() + pTargetBitmap->w > sceneWidth) && (m_Pos.GetFloorIntX() < pTargetBitmap->w)) { + drawPos.m_X += static_cast(sceneWidth); + } } - } - int sceneHeight = g_SceneMan.GetSceneHeight(); - if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) { - if ((targetPos.GetFloorIntY() < 0) && (m_Pos.GetFloorIntY() > (sceneHeight - pTargetBitmap->h))) { - drawPos.m_Y -= static_cast(sceneHeight); - } else if ((targetPos.GetFloorIntY() + pTargetBitmap->h > sceneHeight) && (m_Pos.GetFloorIntY() < pTargetBitmap->h)) { - drawPos.m_Y += static_cast(sceneHeight); + int sceneHeight = g_SceneMan.GetSceneHeight(); + if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) { + if ((targetPos.GetFloorIntY() < 0) && (m_Pos.GetFloorIntY() > (sceneHeight - pTargetBitmap->h))) { + drawPos.m_Y -= static_cast(sceneHeight); + } else if ((targetPos.GetFloorIntY() + pTargetBitmap->h > sceneHeight) && (m_Pos.GetFloorIntY() < pTargetBitmap->h)) { + drawPos.m_Y += static_cast(sceneHeight); + } } } - } - GUIFont *pSymbolFont = g_FrameMan.GetLargeFont(); - GUIFont *pTextFont = g_FrameMan.GetSmallFont(); - if (pSymbolFont && pTextFont) { - const Activity *activity = g_ActivityMan.GetActivity(); - float unheldItemDisplayRange = activity->GetActivityState() == Activity::ActivityState::Running ? g_SettingsMan.GetUnheldItemsHUDDisplayRange() : -1.0F; - if (g_SettingsMan.AlwaysDisplayUnheldItemsInStrategicMode()) { - const GameActivity *gameActivity = dynamic_cast(activity); - if (gameActivity && gameActivity->GetViewState(viewingPlayer) == GameActivity::ViewState::ActorSelect) { unheldItemDisplayRange = -1.0F; } - } - if (!m_SeenByPlayer[viewingPlayer]) { - m_SeenByPlayer[viewingPlayer] = unheldItemDisplayRange < 0 || (unheldItemDisplayRange > 0 && m_Vel.MagnitudeIsLessThan(2.0F) && g_SceneMan.ShortestDistance(m_Pos, g_CameraMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).MagnitudeIsLessThan(unheldItemDisplayRange)); - } else { - // Note - to avoid item HUDs flickering in and out, we need to add a little leeway when hiding them if they're already displayed. - if (unheldItemDisplayRange > 0) { unheldItemDisplayRange += 4.0F; } - m_SeenByPlayer.at(viewingPlayer) = unheldItemDisplayRange < 0 || (unheldItemDisplayRange > 0 && g_SceneMan.ShortestDistance(m_Pos, g_CameraMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).MagnitudeIsLessThan(unheldItemDisplayRange)); - - char pickupArrowString[64]; - pickupArrowString[0] = 0; - if (m_BlinkTimer.GetElapsedSimTimeMS() < 250) { + GUIFont* pSymbolFont = g_FrameMan.GetLargeFont(); + GUIFont* pTextFont = g_FrameMan.GetSmallFont(); + if (pSymbolFont && pTextFont) { + const Activity* activity = g_ActivityMan.GetActivity(); + float unheldItemDisplayRange = activity->GetActivityState() == Activity::ActivityState::Running ? g_SettingsMan.GetUnheldItemsHUDDisplayRange() : -1.0F; + if (g_SettingsMan.AlwaysDisplayUnheldItemsInStrategicMode()) { + const GameActivity* gameActivity = dynamic_cast(activity); + if (gameActivity && gameActivity->GetViewState(viewingPlayer) == GameActivity::ViewState::ActorSelect) { + unheldItemDisplayRange = -1.0F; + } + } + if (!m_SeenByPlayer[viewingPlayer]) { + m_SeenByPlayer[viewingPlayer] = unheldItemDisplayRange < 0 || (unheldItemDisplayRange > 0 && m_Vel.MagnitudeIsLessThan(2.0F) && g_SceneMan.ShortestDistance(m_Pos, g_CameraMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).MagnitudeIsLessThan(unheldItemDisplayRange)); + } else { + // Note - to avoid item HUDs flickering in and out, we need to add a little leeway when hiding them if they're already displayed. + if (unheldItemDisplayRange > 0) { + unheldItemDisplayRange += 4.0F; + } + m_SeenByPlayer.at(viewingPlayer) = unheldItemDisplayRange < 0 || (unheldItemDisplayRange > 0 && g_SceneMan.ShortestDistance(m_Pos, g_CameraMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).MagnitudeIsLessThan(unheldItemDisplayRange)); + + char pickupArrowString[64]; pickupArrowString[0] = 0; - } else if (m_BlinkTimer.GetElapsedSimTimeMS() < 500) { - pickupArrowString[0] = -42; - pickupArrowString[1] = 0; - } else if (m_BlinkTimer.GetElapsedSimTimeMS() < 750) { - pickupArrowString[0] = -41; - pickupArrowString[1] = 0; - } else if (m_BlinkTimer.GetElapsedSimTimeMS() < 1000) { - pickupArrowString[0] = -40; - pickupArrowString[1] = 0; + if (m_BlinkTimer.GetElapsedSimTimeMS() < 250) { + pickupArrowString[0] = 0; + } else if (m_BlinkTimer.GetElapsedSimTimeMS() < 500) { + pickupArrowString[0] = -42; + pickupArrowString[1] = 0; + } else if (m_BlinkTimer.GetElapsedSimTimeMS() < 750) { + pickupArrowString[0] = -41; + pickupArrowString[1] = 0; + } else if (m_BlinkTimer.GetElapsedSimTimeMS() < 1000) { + pickupArrowString[0] = -40; + pickupArrowString[1] = 0; + } + + AllegroBitmap targetAllegroBitmap(pTargetBitmap); + pSymbolFont->DrawAligned(&targetAllegroBitmap, drawPos.GetFloorIntX() - 1, drawPos.GetFloorIntY() - 20, pickupArrowString, GUIFont::Centre); + pTextFont->DrawAligned(&targetAllegroBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() - 29, m_PresetName, GUIFont::Centre); } - - AllegroBitmap targetAllegroBitmap(pTargetBitmap); - pSymbolFont->DrawAligned(&targetAllegroBitmap, drawPos.GetFloorIntX() - 1, drawPos.GetFloorIntY() - 20, pickupArrowString, GUIFont::Centre); - pTextFont->DrawAligned(&targetAllegroBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() - 29, m_PresetName, GUIFont::Centre); } } } } -} } // namespace RTE diff --git a/Source/Entities/HeldDevice.h b/Source/Entities/HeldDevice.h index 79e5727073..bae9996d25 100644 --- a/Source/Entities/HeldDevice.h +++ b/Source/Entities/HeldDevice.h @@ -10,714 +10,676 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "Attachable.h" #include "Actor.h" -namespace RTE -{ - -enum HeldDeviceType -{ - WEAPON = 0, - TOOL, - SHIELD, - BOMB, -}; - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: HeldDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: An articulated device that can be weilded by an Actor. -// Parent(s): Attachable. -// Class history: 06/2/2002 HeldDevice created. -// 01/31/2007 Made concrete so Shields can be jsut HeldDevice:s - -class HeldDevice : public Attachable { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(HeldDevice); -SerializableOverrideMethods; -ClassInfoGetters; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: HeldDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a HeldDevice object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - HeldDevice() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~HeldDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a HeldDevice object before deletion -// from system memory. -// Arguments: None. - - ~HeldDevice() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the HeldDevice object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a HeldDevice to be identical to another, by deep copy. -// Arguments: A reference to the HeldDevice to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const HeldDevice &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire HeldDevice, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Attachable::Reset(); m_MOType = MovableObject::TypeHeldDevice; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAboveHUDPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of the top of this' HUD stack. -// Arguments: None. -// Return value: A Vector with the absolute position of this' HUD stack top point. - - Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, -32); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSupportPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of the support handhold that this HeldDevice -// offers. -// Arguments: None. -// Return value: A vector describing the absolute world coordinates for the support -// position of this HeldDevice. - - Vector GetSupportPos() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMagazinePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of the magazine or other equivalent point of -// this. -// Arguments: None. -// Return value: A vector describing the absolute world coordinates for the magazine -// attachment point of this - - virtual Vector GetMagazinePos() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMuzzlePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of the muzzle or other equivalent point of -// this. -// Arguments: None. -// Return value: A vector describing the absolute world coordinates for the muzzle point -// of this - - virtual Vector GetMuzzlePos() const { return m_Pos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMuzzleOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the unrotated relative offset from the position to the muzzle or -// other equivalent point of this. -// Arguments: None. -// Return value: A unrotated vector describing the relative for the muzzle point of -// this from this' position. - - virtual Vector GetMuzzleOffset() const { return Vector(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetMuzzleOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the unrotated relative offset from the position to the muzzle or -// other equivalent point of this. -// Arguments: Bew ofsset value. -// Return value: None. - - virtual void SetMuzzleOffset(Vector newOffset) { /* Actually does something in inherited classes */ } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetStanceOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current position offset of this HeldDevice's joint relative -// from the parent Actor's position, if attached. -// Arguments: None. -// Return value: A const reference to the current stance parent offset. - - virtual Vector GetStanceOffset() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetStanceOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current position offset of this HeldDevice's joint relative -// from the parent Actor's position, if attached. -// Arguments: New value. -// Return value: None. - - void SetStanceOffset(Vector newValue) { m_StanceOffset = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetSharpStanceOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current position offset of this HeldDevice's joint relative -// from the parent Actor's position, if attached. -// Arguments: New value. -// Return value: None. - - Vector GetSharpStanceOffset() const { return m_SharpStanceOffset; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetSharpStanceOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current position offset of this HeldDevice's joint relative -// from the parent Actor's position, if attached. -// Arguments: New value. -// Return value: None. - - void SetSharpStanceOffset(Vector newValue) { m_SharpStanceOffset = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetSharpLength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how much farther an Actor which holds this device can see when -// aiming this HeldDevice sharply. -// Arguments: None. -// Return value: The length in world pixel units. - - float GetSharpLength() const { return m_MaxSharpLength; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetSharpLength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets how much farther an Actor which holds this device can see when -// aiming this HeldDevice sharply. -// Arguments: The length in world pixel units. -// Return value: None. - - void SetSharpLength(float newLength) { m_MaxSharpLength = newLength; } - - /// - /// Gets whether this HeldDevice can be supported when held. - /// - /// Whether this HeldDevice can be supported when held. - bool IsSupportable() const { return m_Supportable; } - - /// - /// Sets whether this HeldDevice can be supported when held. - /// - /// Whether this HeldDevice can be supported when held. - void SetSupportable(bool shouldBeSupportable) { m_Supportable = shouldBeSupportable; } - - /// - /// Gets whether this HeldDevice is currently supported by a second Arm. - /// - /// Whether this HeldDevice is supported or not. - bool GetSupported() const { return m_Supportable && m_Supported; } - - /// - /// Sets whether this HeldDevice is currently supported by a second Arm. - /// - /// Whether this HeldDevice is being supported. - void SetSupported(bool supported) { m_Supported = m_Supportable && supported; } - - /// - /// Gets whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). - /// - /// Whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). - bool GetSupportAvailable() const { return m_Supportable && m_SupportAvailable; } - - /// - /// Sets whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). - /// - /// Whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). - void SetSupportAvailable(bool supportAvailable) { m_SupportAvailable = m_Supportable && supportAvailable; } - - /// - /// Gets whether this HeldDevice while be held at the support offset with the off-hand when reloading. - /// - /// Whether this HeldDevice while be held at the support offset with the off-hand when reloading. - bool GetUseSupportOffsetWhileReloading() const { return m_UseSupportOffsetWhileReloading; } - - /// - /// Sets whether this HeldDevice while be held at the support offset with the off-hand when reloading. - /// - /// Whether this HeldDevice while be held at the support offset with the off-hand when reloading. - void SetUseSupportOffsetWhileReloading(bool value) { m_UseSupportOffsetWhileReloading = value; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetSupportOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns support offset. -// Arguments: None. -// Return value: Support offset value. - - Vector GetSupportOffset() const { return m_SupportOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetSupportOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets support offset. -// Arguments: New support offset value. -// Return value: None. - - void SetSupportOffset(Vector newOffset) { m_SupportOffset = newOffset; } - - /// - /// Gets whether this HeldDevice has any limitations on what can pick it up. - /// - /// Whether this HeldDevice has any limitations on what can pick it up. - bool HasPickupLimitations() const { return IsUnPickupable() || !m_PickupableByPresetNames.empty(); } - - /// - /// Gets whether this HeldDevice cannot be picked up at all. - /// - /// Whether this HeldDevice cannot be picked up at all. - bool IsUnPickupable() const { return m_IsUnPickupable; } - - /// - /// Sets whether this HeldDevice cannot be picked up at all. - /// - /// Whether this HeldDevice cannot be picked up at all. True means it cannot, false means any other limitations will apply normally. - void SetUnPickupable(bool shouldBeUnPickupable) { m_IsUnPickupable = shouldBeUnPickupable; } - - /// - /// Checks whether the given Actor can pick up this HeldDevice. - /// - /// The Actor to check. Ownership is NOT transferred. - /// Whether the given Actor can pick up this HeldDevice. - bool IsPickupableBy(const Actor *actor) const { return !HasPickupLimitations() || m_PickupableByPresetNames.find(actor->GetPresetName()) != m_PickupableByPresetNames.end(); } - - /// - /// Specify that objects with the given PresetName can pick up this HeldDevice. - /// - /// The PresetName of an object that should be able to pick up this HeldDevice. - void AddPickupableByPresetName(const std::string &presetName) { SetUnPickupable(false); m_PickupableByPresetNames.insert(presetName); } - - /// - /// Remove allowance for objects with the given PresetName to pick up this HeldDevice. - /// Note that if the last allowance is removed, the HeldDevice will no longer have pickup limitations, rather than setting itself as unpickupable. - /// - /// The PresetName of an object that should no longer be able to pick up this HeldDevice. - void RemovePickupableByPresetName(const std::string &actorPresetName); - - /// - /// Gets the multiplier for how well this HeldDevice can be gripped by Arms. - /// - /// The grip strength multiplier for this HeldDevice. - float GetGripStrengthMultiplier() const { return m_GripStrengthMultiplier; } - - /// - /// Sets the multiplier for how well this HeldDevice can be gripped by Arms. - /// - /// The new grip strength multiplier for this HeldDevice. - void SetGripStrengthMultiplier(float gripStrengthMultiplier) { m_GripStrengthMultiplier = gripStrengthMultiplier; } - - /// - /// Gets whether this can get hit by MOs when held. - /// - /// Whether this can get hit by MOs when held. - bool GetsHitByMOsWhenHeld() const { return m_GetsHitByMOsWhenHeld; } - - /// - /// Sets whether this can get hit by MOs when held. - /// - /// Whether this can get hit by MOs when held. - void SetGetsHitByMOsWhenHeld(bool value) { m_GetsHitByMOsWhenHeld = value; } - - /// - /// Gets whether this HeldDevice is currently being held or not. - /// - /// Whether this HeldDevice is currently being held or not. - bool IsBeingHeld() const; - - /// - /// Gets the visual recoil multiplier. - /// - /// A float with the scalar value. - float GetVisualRecoilMultiplier() const { return m_VisualRecoilMultiplier; } - - /// - /// Sets the visual recoil multiplier. - /// - /// The new recoil multiplier scalar. - void SetVisualRecoilMultiplier(float value) { m_VisualRecoilMultiplier = value; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSharpAim -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the degree to which this is being aimed sharp. This will -// affect the accuracy and what GetParentOffset returns. -// Arguments: A normalized scalar between 0 (no sharp aim) to 1.0 (best aim). -// Return value: None. - - void SetSharpAim(float sharpAim) { m_SharpAim = sharpAim; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsWeapon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this is an offensive weapon or not. -// Arguments: None. -// Return value: Offensive weapon or not. - - bool IsWeapon() { return m_HeldDeviceType == WEAPON; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsTool -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this is a tool or not. -// Arguments: None. -// Return value: Tool or not. - - bool IsTool() { return m_HeldDeviceType == TOOL; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsShield -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this is a shield or not. -// Arguments: None. -// Return value: Shield or not. - - bool IsShield() { return m_HeldDeviceType == SHIELD; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDualWieldable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this is a dual wieldable weapon or not. -// Arguments: None. -// Return value: Dual wieldable or not. - - bool IsDualWieldable() const { return m_DualWieldable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDualWieldable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this is a dual wieldable weapon or not. -// Arguments: Dual wieldable or not. -// Return value: None. - - void SetDualWieldable(bool isDualWieldable) { m_DualWieldable = isDualWieldable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsOneHanded -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this can be held and operated effectively with one -// hand or not. -// Arguments: None. -// Return value: One handed device or not. - - bool IsOneHanded() const { return m_OneHanded; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOneHanded -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this can be held and operated effectively with one -// hand or not. -// Arguments: New value. -// Return value: None. - - void SetOneHanded(bool newValue) { m_OneHanded = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. -// Arguments: Reference to the HitData struct which describes the collision. This -// will be modified to represent the results of the collision. -// Return value: Whether the collision has been deemed valid. If false, then disregard -// any impulses in the Hitdata. - - bool CollideAtPoint(HitData &hitData) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Activate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Activates one of this HDFirearm's features. Analogous to 'pulling -// the trigger'. -// Arguments: None. -// Return value: None. - - virtual void Activate(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Deactivate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing -// the trigger'. -// Arguments: None. -// Return value: None. - - virtual void Deactivate(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reload -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Throws out the currently used Magazine, if any, and puts in a new one -// after the reload delay is up. -// Arguments: None. -// Return value: None. - - virtual void Reload() {} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsActivated -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is curtrently being activated. -// Arguments: None. -// Return value: Whether being activated. - - virtual bool IsActivated() const { return m_Activated; } - - /// - /// Gets the activation Timer for this HeldDevice. - /// - /// The activation Timer for this HeldDevice. - const Timer & GetActivationTimer() const { return m_ActivationTimer;} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsReloading -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is curtrently being reloaded. -// Arguments: None. -// Return value: Whetehr being reloaded. - - virtual bool IsReloading() const { return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DoneReloading -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device just finished reloading this frame. -// Arguments: None. -// Return value: Whether just done reloading this frame. - - virtual bool DoneReloading() const { return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: NeedsReloading -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is curtrently in need of being reloaded. -// Arguments: None. -// Return value: Whetehr in need of reloading (ie not full). - - virtual bool NeedsReloading() const { return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsFull -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the device is curtrently full and reloading won't have -// any effect. -// Arguments: None. -// Return value: Whetehr magazine is full or not. - - virtual bool IsFull() const { return true; } - - /// - /// Tells whether this HeldDevice is currently empty of ammo. - /// - /// Whether this HeldDevice is empty. - virtual bool IsEmpty() const { return false; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this HeldDevice's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this' current graphical HUD overlay representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// Which player's screen this is being drawn to. May affect what HUD elements -// get drawn etc. -// Return value: None. - - void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - - /// - /// Resest all the timers used by this. Can be emitters, etc. This is to prevent backed up emissions to come out all at once while this has been held dormant in an inventory. - /// - void ResetAllTimers() override { Attachable::ResetAllTimers(); m_ActivationTimer.Reset(); } +namespace RTE { + + enum HeldDeviceType { + WEAPON = 0, + TOOL, + SHIELD, + BOMB, + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: HeldDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: An articulated device that can be weilded by an Actor. + // Parent(s): Attachable. + // Class history: 06/2/2002 HeldDevice created. + // 01/31/2007 Made concrete so Shields can be jsut HeldDevice:s + + class HeldDevice : public Attachable { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(HeldDevice); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: HeldDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a HeldDevice object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + HeldDevice() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~HeldDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a HeldDevice object before deletion + // from system memory. + // Arguments: None. + + ~HeldDevice() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the HeldDevice object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a HeldDevice to be identical to another, by deep copy. + // Arguments: A reference to the HeldDevice to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const HeldDevice& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire HeldDevice, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Attachable::Reset(); + m_MOType = MovableObject::TypeHeldDevice; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAboveHUDPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of the top of this' HUD stack. + // Arguments: None. + // Return value: A Vector with the absolute position of this' HUD stack top point. + + Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, -32); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSupportPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of the support handhold that this HeldDevice + // offers. + // Arguments: None. + // Return value: A vector describing the absolute world coordinates for the support + // position of this HeldDevice. + + Vector GetSupportPos() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMagazinePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of the magazine or other equivalent point of + // this. + // Arguments: None. + // Return value: A vector describing the absolute world coordinates for the magazine + // attachment point of this + + virtual Vector GetMagazinePos() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMuzzlePos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of the muzzle or other equivalent point of + // this. + // Arguments: None. + // Return value: A vector describing the absolute world coordinates for the muzzle point + // of this + + virtual Vector GetMuzzlePos() const { return m_Pos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMuzzleOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the unrotated relative offset from the position to the muzzle or + // other equivalent point of this. + // Arguments: None. + // Return value: A unrotated vector describing the relative for the muzzle point of + // this from this' position. + + virtual Vector GetMuzzleOffset() const { return Vector(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetMuzzleOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the unrotated relative offset from the position to the muzzle or + // other equivalent point of this. + // Arguments: Bew ofsset value. + // Return value: None. + + virtual void SetMuzzleOffset(Vector newOffset) { /* Actually does something in inherited classes */ + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetStanceOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current position offset of this HeldDevice's joint relative + // from the parent Actor's position, if attached. + // Arguments: None. + // Return value: A const reference to the current stance parent offset. + + virtual Vector GetStanceOffset() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetStanceOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current position offset of this HeldDevice's joint relative + // from the parent Actor's position, if attached. + // Arguments: New value. + // Return value: None. + + void SetStanceOffset(Vector newValue) { m_StanceOffset = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetSharpStanceOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current position offset of this HeldDevice's joint relative + // from the parent Actor's position, if attached. + // Arguments: New value. + // Return value: None. + + Vector GetSharpStanceOffset() const { return m_SharpStanceOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetSharpStanceOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current position offset of this HeldDevice's joint relative + // from the parent Actor's position, if attached. + // Arguments: New value. + // Return value: None. + + void SetSharpStanceOffset(Vector newValue) { m_SharpStanceOffset = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetSharpLength + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how much farther an Actor which holds this device can see when + // aiming this HeldDevice sharply. + // Arguments: None. + // Return value: The length in world pixel units. + + float GetSharpLength() const { return m_MaxSharpLength; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetSharpLength + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets how much farther an Actor which holds this device can see when + // aiming this HeldDevice sharply. + // Arguments: The length in world pixel units. + // Return value: None. + + void SetSharpLength(float newLength) { m_MaxSharpLength = newLength; } + + /// + /// Gets whether this HeldDevice can be supported when held. + /// + /// Whether this HeldDevice can be supported when held. + bool IsSupportable() const { return m_Supportable; } + + /// + /// Sets whether this HeldDevice can be supported when held. + /// + /// Whether this HeldDevice can be supported when held. + void SetSupportable(bool shouldBeSupportable) { m_Supportable = shouldBeSupportable; } + + /// + /// Gets whether this HeldDevice is currently supported by a second Arm. + /// + /// Whether this HeldDevice is supported or not. + bool GetSupported() const { return m_Supportable && m_Supported; } + + /// + /// Sets whether this HeldDevice is currently supported by a second Arm. + /// + /// Whether this HeldDevice is being supported. + void SetSupported(bool supported) { m_Supported = m_Supportable && supported; } + + /// + /// Gets whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). + /// + /// Whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). + bool GetSupportAvailable() const { return m_Supportable && m_SupportAvailable; } + + /// + /// Sets whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). + /// + /// Whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). + void SetSupportAvailable(bool supportAvailable) { m_SupportAvailable = m_Supportable && supportAvailable; } + + /// + /// Gets whether this HeldDevice while be held at the support offset with the off-hand when reloading. + /// + /// Whether this HeldDevice while be held at the support offset with the off-hand when reloading. + bool GetUseSupportOffsetWhileReloading() const { return m_UseSupportOffsetWhileReloading; } + + /// + /// Sets whether this HeldDevice while be held at the support offset with the off-hand when reloading. + /// + /// Whether this HeldDevice while be held at the support offset with the off-hand when reloading. + void SetUseSupportOffsetWhileReloading(bool value) { m_UseSupportOffsetWhileReloading = value; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetSupportOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns support offset. + // Arguments: None. + // Return value: Support offset value. + + Vector GetSupportOffset() const { return m_SupportOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetSupportOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets support offset. + // Arguments: New support offset value. + // Return value: None. + + void SetSupportOffset(Vector newOffset) { m_SupportOffset = newOffset; } + + /// + /// Gets whether this HeldDevice has any limitations on what can pick it up. + /// + /// Whether this HeldDevice has any limitations on what can pick it up. + bool HasPickupLimitations() const { return IsUnPickupable() || !m_PickupableByPresetNames.empty(); } + + /// + /// Gets whether this HeldDevice cannot be picked up at all. + /// + /// Whether this HeldDevice cannot be picked up at all. + bool IsUnPickupable() const { return m_IsUnPickupable; } + + /// + /// Sets whether this HeldDevice cannot be picked up at all. + /// + /// Whether this HeldDevice cannot be picked up at all. True means it cannot, false means any other limitations will apply normally. + void SetUnPickupable(bool shouldBeUnPickupable) { m_IsUnPickupable = shouldBeUnPickupable; } + + /// + /// Checks whether the given Actor can pick up this HeldDevice. + /// + /// The Actor to check. Ownership is NOT transferred. + /// Whether the given Actor can pick up this HeldDevice. + bool IsPickupableBy(const Actor* actor) const { return !HasPickupLimitations() || m_PickupableByPresetNames.find(actor->GetPresetName()) != m_PickupableByPresetNames.end(); } + + /// + /// Specify that objects with the given PresetName can pick up this HeldDevice. + /// + /// The PresetName of an object that should be able to pick up this HeldDevice. + void AddPickupableByPresetName(const std::string& presetName) { + SetUnPickupable(false); + m_PickupableByPresetNames.insert(presetName); + } + + /// + /// Remove allowance for objects with the given PresetName to pick up this HeldDevice. + /// Note that if the last allowance is removed, the HeldDevice will no longer have pickup limitations, rather than setting itself as unpickupable. + /// + /// The PresetName of an object that should no longer be able to pick up this HeldDevice. + void RemovePickupableByPresetName(const std::string& actorPresetName); + + /// + /// Gets the multiplier for how well this HeldDevice can be gripped by Arms. + /// + /// The grip strength multiplier for this HeldDevice. + float GetGripStrengthMultiplier() const { return m_GripStrengthMultiplier; } + + /// + /// Sets the multiplier for how well this HeldDevice can be gripped by Arms. + /// + /// The new grip strength multiplier for this HeldDevice. + void SetGripStrengthMultiplier(float gripStrengthMultiplier) { m_GripStrengthMultiplier = gripStrengthMultiplier; } + + /// + /// Gets whether this can get hit by MOs when held. + /// + /// Whether this can get hit by MOs when held. + bool GetsHitByMOsWhenHeld() const { return m_GetsHitByMOsWhenHeld; } + + /// + /// Sets whether this can get hit by MOs when held. + /// + /// Whether this can get hit by MOs when held. + void SetGetsHitByMOsWhenHeld(bool value) { m_GetsHitByMOsWhenHeld = value; } + + /// + /// Gets whether this HeldDevice is currently being held or not. + /// + /// Whether this HeldDevice is currently being held or not. + bool IsBeingHeld() const; + + /// + /// Gets the visual recoil multiplier. + /// + /// A float with the scalar value. + float GetVisualRecoilMultiplier() const { return m_VisualRecoilMultiplier; } + + /// + /// Sets the visual recoil multiplier. + /// + /// The new recoil multiplier scalar. + void SetVisualRecoilMultiplier(float value) { m_VisualRecoilMultiplier = value; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSharpAim + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the degree to which this is being aimed sharp. This will + // affect the accuracy and what GetParentOffset returns. + // Arguments: A normalized scalar between 0 (no sharp aim) to 1.0 (best aim). + // Return value: None. + + void SetSharpAim(float sharpAim) { m_SharpAim = sharpAim; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsWeapon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this is an offensive weapon or not. + // Arguments: None. + // Return value: Offensive weapon or not. + + bool IsWeapon() { return m_HeldDeviceType == WEAPON; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsTool + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this is a tool or not. + // Arguments: None. + // Return value: Tool or not. + + bool IsTool() { return m_HeldDeviceType == TOOL; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsShield + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this is a shield or not. + // Arguments: None. + // Return value: Shield or not. + + bool IsShield() { return m_HeldDeviceType == SHIELD; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsDualWieldable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this is a dual wieldable weapon or not. + // Arguments: None. + // Return value: Dual wieldable or not. + + bool IsDualWieldable() const { return m_DualWieldable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDualWieldable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this is a dual wieldable weapon or not. + // Arguments: Dual wieldable or not. + // Return value: None. + + void SetDualWieldable(bool isDualWieldable) { m_DualWieldable = isDualWieldable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsOneHanded + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this can be held and operated effectively with one + // hand or not. + // Arguments: None. + // Return value: One handed device or not. + + bool IsOneHanded() const { return m_OneHanded; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOneHanded + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this can be held and operated effectively with one + // hand or not. + // Arguments: New value. + // Return value: None. + + void SetOneHanded(bool newValue) { m_OneHanded = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. + // Arguments: Reference to the HitData struct which describes the collision. This + // will be modified to represent the results of the collision. + // Return value: Whether the collision has been deemed valid. If false, then disregard + // any impulses in the Hitdata. + + bool CollideAtPoint(HitData& hitData) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Activate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Activates one of this HDFirearm's features. Analogous to 'pulling + // the trigger'. + // Arguments: None. + // Return value: None. + + virtual void Activate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Deactivate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing + // the trigger'. + // Arguments: None. + // Return value: None. + + virtual void Deactivate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reload + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Throws out the currently used Magazine, if any, and puts in a new one + // after the reload delay is up. + // Arguments: None. + // Return value: None. + + virtual void Reload() {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsActivated + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is curtrently being activated. + // Arguments: None. + // Return value: Whether being activated. + + virtual bool IsActivated() const { return m_Activated; } + + /// + /// Gets the activation Timer for this HeldDevice. + /// + /// The activation Timer for this HeldDevice. + const Timer& GetActivationTimer() const { return m_ActivationTimer; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsReloading + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is curtrently being reloaded. + // Arguments: None. + // Return value: Whetehr being reloaded. + + virtual bool IsReloading() const { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DoneReloading + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device just finished reloading this frame. + // Arguments: None. + // Return value: Whether just done reloading this frame. + + virtual bool DoneReloading() const { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: NeedsReloading + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is curtrently in need of being reloaded. + // Arguments: None. + // Return value: Whetehr in need of reloading (ie not full). + + virtual bool NeedsReloading() const { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsFull + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the device is curtrently full and reloading won't have + // any effect. + // Arguments: None. + // Return value: Whetehr magazine is full or not. + + virtual bool IsFull() const { return true; } + + /// + /// Tells whether this HeldDevice is currently empty of ammo. + /// + /// Whether this HeldDevice is empty. + virtual bool IsEmpty() const { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this HeldDevice's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this' current graphical HUD overlay representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // Which player's screen this is being drawn to. May affect what HUD elements + // get drawn etc. + // Return value: None. + + void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; + + /// + /// Resest all the timers used by this. Can be emitters, etc. This is to prevent backed up emissions to come out all at once while this has been held dormant in an inventory. + /// + void ResetAllTimers() override { + Attachable::ResetAllTimers(); + m_ActivationTimer.Reset(); + } #pragma region Force Transferral - /// - /// Bundles up all the accumulated impulse forces of this HeldDevice and calculates how they transfer to the joint, and therefore to the parent. - /// If the accumulated impulse forces exceed the joint strength or gib impulse limit of this HeldDevice, the jointImpulses Vector will be filled up to that limit and false will be returned. - /// Additionally, in this case, the HeldDevice will remove itself from its parent, destabilizing said parent if it's an Actor, and gib itself if appropriate. - /// - /// A vector that will have the impulse forces affecting the joint ADDED to it. - /// An optional override for the HeldDevice's joint stiffness for this function call. Primarily used to allow subclasses to perform special behavior. - /// An optional override for the HeldDevice's joint strength for this function call. Primarily used to allow subclasses to perform special behavior. - /// An optional override for the HeldDevice's gib impulse limit for this function call. Primarily used to allow subclasses to perform special behavior. - /// False if the HeldDevice has no parent or its accumulated forces are greater than its joint strength or gib impulse limit, otherwise true. - bool TransferJointImpulses(Vector &jointImpulses, float jointStiffnessValueToUse = -1, float jointStrengthValueToUse = -1, float gibImpulseLimitValueToUse = -1) override; + /// + /// Bundles up all the accumulated impulse forces of this HeldDevice and calculates how they transfer to the joint, and therefore to the parent. + /// If the accumulated impulse forces exceed the joint strength or gib impulse limit of this HeldDevice, the jointImpulses Vector will be filled up to that limit and false will be returned. + /// Additionally, in this case, the HeldDevice will remove itself from its parent, destabilizing said parent if it's an Actor, and gib itself if appropriate. + /// + /// A vector that will have the impulse forces affecting the joint ADDED to it. + /// An optional override for the HeldDevice's joint stiffness for this function call. Primarily used to allow subclasses to perform special behavior. + /// An optional override for the HeldDevice's joint strength for this function call. Primarily used to allow subclasses to perform special behavior. + /// An optional override for the HeldDevice's gib impulse limit for this function call. Primarily used to allow subclasses to perform special behavior. + /// False if the HeldDevice has no parent or its accumulated forces are greater than its joint strength or gib impulse limit, otherwise true. + bool TransferJointImpulses(Vector& jointImpulses, float jointStiffnessValueToUse = -1, float jointStrengthValueToUse = -1, float gibImpulseLimitValueToUse = -1) override; #pragma endregion - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - // Indicates what kind of held device this is, see the HeldDeviceType enum - int m_HeldDeviceType; - // Is this HeldDevice that are currently activated? - bool m_Activated; - // Timer for timing how long a feature has been activated. - Timer m_ActivationTimer; - // Can be weilded well with one hand or not - bool m_OneHanded; - // Can be weilded with bg hand or not - bool m_DualWieldable; - // Position offset from the parent's own position to this HeldDevice's joint, which - // defines the normal stance that an arm that is holding this device should have. - Vector m_StanceOffset; - // The alternative parent offset stance that is used when the device is carefully aimed. - Vector m_SharpStanceOffset; - // The point at which the other arm of the holder can support this HeldDevice. - // Relative to the m_Pos. This is like a seconday handle position. - Vector m_SupportOffset; - // Whether the actor using this gun should keep hold of the support offset when reloading, instead of using their ReloadOffset/HolsterOffset - bool m_UseSupportOffsetWhileReloading; - // The degree as to this is being aimed carefully. 0 means no sharp aim, and 1.0 means best aim. - float m_SharpAim; - // How much farther the player can see when aiming this sharply. - float m_MaxSharpLength; - bool m_Supportable; //!< Whether or not this HeldDevice can be supported. - bool m_Supported; //!< Whether or not this HeldDevice is currently being supported by another Arm. - bool m_SupportAvailable; //!< Whether or not this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). - bool m_IsUnPickupable; //!< Whether or not this HeldDevice should be able to be picked up at all. - //TODO: move this smelly thing elsewhere - std::array m_SeenByPlayer; //!< An array of players that can currently see the pickup HUD of this HeldDevice. - std::unordered_set m_PickupableByPresetNames; //!< The unordered set of PresetNames that can pick up this HeldDevice if it's dropped. An empty set means there are no PresetName limitations. - float m_GripStrengthMultiplier; //!< The multiplier for how well this HeldDevice can be gripped by Arms. - // Blink timer for the icon - Timer m_BlinkTimer; - // How loud this device is when activated. 0 means perfectly quiet 0.5 means half of normal (normal equals audiable from ~half a screen) - float m_Loudness; - // If this weapon belongs to the "Explosive Weapons" group or not - bool m_IsExplosiveWeapon; - // If this device can be hit by MOs whenever it's held - bool m_GetsHitByMOsWhenHeld; - /// The multiplier for visual recoil - float m_VisualRecoilMultiplier; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this HeldDevice, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - HeldDevice(const HeldDevice &reference) = delete; - HeldDevice & operator=(const HeldDevice &rhs) = delete; - -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + // Indicates what kind of held device this is, see the HeldDeviceType enum + int m_HeldDeviceType; + // Is this HeldDevice that are currently activated? + bool m_Activated; + // Timer for timing how long a feature has been activated. + Timer m_ActivationTimer; + // Can be weilded well with one hand or not + bool m_OneHanded; + // Can be weilded with bg hand or not + bool m_DualWieldable; + // Position offset from the parent's own position to this HeldDevice's joint, which + // defines the normal stance that an arm that is holding this device should have. + Vector m_StanceOffset; + // The alternative parent offset stance that is used when the device is carefully aimed. + Vector m_SharpStanceOffset; + // The point at which the other arm of the holder can support this HeldDevice. + // Relative to the m_Pos. This is like a seconday handle position. + Vector m_SupportOffset; + // Whether the actor using this gun should keep hold of the support offset when reloading, instead of using their ReloadOffset/HolsterOffset + bool m_UseSupportOffsetWhileReloading; + // The degree as to this is being aimed carefully. 0 means no sharp aim, and 1.0 means best aim. + float m_SharpAim; + // How much farther the player can see when aiming this sharply. + float m_MaxSharpLength; + bool m_Supportable; //!< Whether or not this HeldDevice can be supported. + bool m_Supported; //!< Whether or not this HeldDevice is currently being supported by another Arm. + bool m_SupportAvailable; //!< Whether or not this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). + bool m_IsUnPickupable; //!< Whether or not this HeldDevice should be able to be picked up at all. + // TODO: move this smelly thing elsewhere + std::array m_SeenByPlayer; //!< An array of players that can currently see the pickup HUD of this HeldDevice. + std::unordered_set m_PickupableByPresetNames; //!< The unordered set of PresetNames that can pick up this HeldDevice if it's dropped. An empty set means there are no PresetName limitations. + float m_GripStrengthMultiplier; //!< The multiplier for how well this HeldDevice can be gripped by Arms. + // Blink timer for the icon + Timer m_BlinkTimer; + // How loud this device is when activated. 0 means perfectly quiet 0.5 means half of normal (normal equals audiable from ~half a screen) + float m_Loudness; + // If this weapon belongs to the "Explosive Weapons" group or not + bool m_IsExplosiveWeapon; + // If this device can be hit by MOs whenever it's held + bool m_GetsHitByMOsWhenHeld; + /// The multiplier for visual recoil + float m_VisualRecoilMultiplier; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this HeldDevice, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + HeldDevice(const HeldDevice& reference) = delete; + HeldDevice& operator=(const HeldDevice& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/Icon.cpp b/Source/Entities/Icon.cpp index 2bae7cab1e..3253c8a6ff 100644 --- a/Source/Entities/Icon.cpp +++ b/Source/Entities/Icon.cpp @@ -4,7 +4,7 @@ namespace RTE { ConcreteClassInfo(Icon, Entity, 80); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Icon::Clear() { m_BitmapFile.Reset(); @@ -13,11 +13,13 @@ namespace RTE { m_BitmapsTrueColor.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Icon::Create() { if (m_BitmapsIndexed.empty() || m_BitmapsTrueColor.empty()) { - if (m_BitmapFile.GetDataPath().empty()) { m_BitmapFile.SetDataPath("Base.rte/GUIs/DefaultIcon.png"); } + if (m_BitmapFile.GetDataPath().empty()) { + m_BitmapFile.SetDataPath("Base.rte/GUIs/DefaultIcon.png"); + } m_BitmapFile.GetAsAnimation(m_BitmapsIndexed, m_FrameCount, COLORCONV_REDUCE_TO_256); m_BitmapFile.GetAsAnimation(m_BitmapsTrueColor, m_FrameCount, COLORCONV_8_TO_32); @@ -25,9 +27,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Icon::Create(const Icon &reference) { + int Icon::Create(const Icon& reference) { Entity::Create(reference); m_BitmapFile = reference.m_BitmapFile; @@ -38,20 +40,20 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Icon::ReadProperty(const std::string_view &propName, Reader &reader) { + int Icon::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("BitmapFile", { reader >> m_BitmapFile; }); MatchProperty("FrameCount", { reader >> m_FrameCount; }); EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Icon::Save(Writer &writer) const { + int Icon::Save(Writer& writer) const { Entity::Save(writer); writer.NewProperty("BitmapFile"); writer << m_BitmapFile; @@ -61,10 +63,12 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Icon::Destroy(bool notInherited) { - if (!notInherited) { Entity::Destroy(); } + if (!notInherited) { + Entity::Destroy(); + } Clear(); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/Icon.h b/Source/Entities/Icon.h index 118d500105..8933377757 100644 --- a/Source/Entities/Icon.h +++ b/Source/Entities/Icon.h @@ -12,7 +12,6 @@ namespace RTE { class Icon : public Entity { public: - EntityAllocation(Icon); SerializableOverrideMethods; ClassInfoGetters; @@ -27,7 +26,12 @@ namespace RTE { /// Copy constructor method used to instantiate an Icon object identical to an already existing one. /// /// An Icon object which is passed in by reference. - Icon(const Icon &reference) { if (this != &reference) { Clear(); Create(reference); } } + Icon(const Icon& reference) { + if (this != &reference) { + Clear(); + Create(reference); + } + } /// /// Makes the Icon object ready for use. @@ -40,7 +44,7 @@ namespace RTE { /// /// A reference to the Icon to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Icon &reference); + int Create(const Icon& reference); #pragma endregion #pragma region Destruction @@ -58,7 +62,10 @@ namespace RTE { /// /// Resets the entire Icon, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Entity::Reset(); } + void Reset() override { + Clear(); + Entity::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -72,13 +79,13 @@ namespace RTE { /// Gets the array of 8-bit bitmaps of this Icon, as many as GetFrameCount says. Neither the array nor the BITMAPs are transferred ownership! /// /// The BITMAPs in 8bpp of this Icon. - std::vector GetBitmaps8() const { return m_BitmapsIndexed; } + std::vector GetBitmaps8() const { return m_BitmapsIndexed; } /// /// Gets the array of 32-bit bitmaps of this Icon, as many as GetFrameCount says. Neither the array nor the BITMAPs are transferred ownership! /// /// The BITMAPs in 32bpp of this Icon. - std::vector GetBitmaps32() const { return m_BitmapsTrueColor; } + std::vector GetBitmaps32() const { return m_BitmapsTrueColor; } #pragma endregion #pragma region Operator Overloads @@ -87,25 +94,29 @@ namespace RTE { /// /// An Icon reference. /// A reference to the changed Icon. - Icon & operator=(const Icon &rhs) { if (this != &rhs) { Destroy(); Create(rhs); } return *this; } + Icon& operator=(const Icon& rhs) { + if (this != &rhs) { + Destroy(); + Create(rhs); + } + return *this; + } #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. ContentFile m_BitmapFile; //!< ContentFile containing the bitmap file of this Icon. unsigned int m_FrameCount; //!< Number of frames in this Icon's animation. - std::vector m_BitmapsIndexed; //!< Vector containing the 8bpp BITMAPs of this Icon. BITMAPs are NOT owned! - std::vector m_BitmapsTrueColor; //!< Vector containing the 32bpp BITMAPs of this Icon. BITMAPs are NOT owned! + std::vector m_BitmapsIndexed; //!< Vector containing the 8bpp BITMAPs of this Icon. BITMAPs are NOT owned! + std::vector m_BitmapsTrueColor; //!< Vector containing the 32bpp BITMAPs of this Icon. BITMAPs are NOT owned! private: - /// /// Clears all the member variables of this Icon, effectively resetting the members of this abstraction level only. /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/Leg.cpp b/Source/Entities/Leg.cpp index 8b693d5137..8ca6b5da81 100644 --- a/Source/Entities/Leg.cpp +++ b/Source/Entities/Leg.cpp @@ -6,7 +6,7 @@ namespace RTE { ConcreteClassInfo(Leg, Attachable, 50); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Leg::Clear() { m_Foot = nullptr; @@ -27,7 +27,7 @@ namespace RTE { m_MoveSpeed = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Leg::Create() { if (Attachable::Create() < 0) { @@ -40,7 +40,9 @@ namespace RTE { // Ensure Legs don't collide with terrain when attached since their expansion/contraction is frame based so atom group doesn't know how to account for it. SetCollidesWithTerrainWhileAttached(false); - if (m_ContractedOffset.GetSqrMagnitude() > m_ExtendedOffset.GetSqrMagnitude()) { std::swap(m_ContractedOffset, m_ExtendedOffset); } + if (m_ContractedOffset.GetSqrMagnitude() > m_ExtendedOffset.GetSqrMagnitude()) { + std::swap(m_ContractedOffset, m_ExtendedOffset); + } m_MinExtension = m_ContractedOffset.GetMagnitude(); m_MaxExtension = m_ExtendedOffset.GetMagnitude(); @@ -48,12 +50,12 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Leg::Create(const Leg &reference) { + int Leg::Create(const Leg& reference) { if (reference.m_Foot) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_Foot->GetUniqueID()); - SetFoot(dynamic_cast(reference.m_Foot->Clone())); + SetFoot(dynamic_cast(reference.m_Foot->Clone())); } Attachable::Create(reference); @@ -75,12 +77,12 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Leg::ReadProperty(const std::string_view &propName, Reader &reader) { + int Leg::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Attachable::ReadProperty(propName, reader)); - - MatchProperty("Foot", { SetFoot(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + + MatchProperty("Foot", { SetFoot(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); MatchProperty("ContractedOffset", { reader >> m_ContractedOffset; m_MinExtension = m_ContractedOffset.GetMagnitude(); @@ -96,9 +98,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Leg::Save(Writer &writer) const { + int Leg::Save(Writer& writer) const { Attachable::Save(writer); writer.NewProperty("Foot"); @@ -117,41 +119,45 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Leg::SetFoot(Attachable *newFoot) { - if (m_Foot && m_Foot->IsAttached()) { RemoveAndDeleteAttachable(m_Foot); } + void Leg::SetFoot(Attachable* newFoot) { + if (m_Foot && m_Foot->IsAttached()) { + RemoveAndDeleteAttachable(m_Foot); + } if (newFoot == nullptr) { m_Foot = nullptr; } else { m_Foot = newFoot; AddAttachable(newFoot); - m_HardcodedAttachableUniqueIDsAndSetters.insert({newFoot->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - dynamic_cast(parent)->SetFoot(attachable); - }}); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newFoot->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + dynamic_cast(parent)->SetFoot(attachable); + }}); - if (m_Foot->HasNoSetDamageMultiplier()) { m_Foot->SetDamageMultiplier(1.0F); } + if (m_Foot->HasNoSetDamageMultiplier()) { + m_Foot->SetDamageMultiplier(1.0F); + } m_Foot->SetInheritsRotAngle(false); m_Foot->SetParentGibBlastStrengthMultiplier(0.0F); m_Foot->SetCollidesWithTerrainWhileAttached(false); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - AtomGroup * Leg::GetFootGroupFromFootAtomGroup() { + AtomGroup* Leg::GetFootGroupFromFootAtomGroup() { if (!m_Foot) { return nullptr; } - AtomGroup *footGroup = dynamic_cast(m_Foot->GetAtomGroup()->Clone()); + AtomGroup* footGroup = dynamic_cast(m_Foot->GetAtomGroup()->Clone()); int atomLeftMostOffset = m_Foot->GetSpriteWidth(); int atomTopMostOffset = m_Foot->GetSpriteHeight(); int atomBottomMostOffset = 0; - for (const Atom *atom : footGroup->GetAtomList()) { + for (const Atom* atom: footGroup->GetAtomList()) { int atomOffsetX = atom->GetOriginalOffset().GetFloorIntX(); int atomOffsetY = atom->GetOriginalOffset().GetFloorIntY(); // Auto-generated AtomGroups are created empty so a single Atom is added at 0,0. Ignore it and any others to not screw up detecting the left-most and top-most offsets. @@ -163,7 +169,7 @@ namespace RTE { } int groupCenterOffsetY = (atomTopMostOffset + atomBottomMostOffset) / 2; - std::vector filteredAtomList; + std::vector filteredAtomList; // We want the FootGroup to end up with an "L" shape, so filter all the top and right Atoms while taking into account the heel might be slant and the sole might not be flat. The extra Atoms are not necessary and might (further) screw up some walking physics. // Start from the bottom so we can filter any Atom that might be above the bottom-most one on the same X offset. @@ -172,7 +178,7 @@ namespace RTE { int atomOffsetY = (*atomItr)->GetOriginalOffset().GetFloorIntY(); bool haveBottomMostAtomOnThisXOffset = false; - for (const Atom *filteredAtom : filteredAtomList) { + for (const Atom* filteredAtom: filteredAtomList) { haveBottomMostAtomOnThisXOffset = (filteredAtom->GetOriginalOffset().GetFloorIntX() == atomOffsetX) && (filteredAtom->GetOriginalOffset().GetFloorIntY() > atomOffsetY); if (haveBottomMostAtomOnThisXOffset) { break; @@ -180,7 +186,7 @@ namespace RTE { } if (atomOffsetX == atomLeftMostOffset || atomOffsetY == atomBottomMostOffset || (!haveBottomMostAtomOnThisXOffset && atomOffsetX > atomLeftMostOffset && atomOffsetY >= groupCenterOffsetY)) { - Atom *newAtom = new Atom(*(*atomItr)); + Atom* newAtom = new Atom(*(*atomItr)); newAtom->SetMaterial(g_SceneMan.GetMaterialFromID(MaterialColorKeys::g_MaterialRubber)); filteredAtomList.emplace_back(newAtom); @@ -192,7 +198,7 @@ namespace RTE { return footGroup; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Leg::Update() { Attachable::PreUpdate(); @@ -219,13 +225,15 @@ namespace RTE { UpdateFootFrameAndRotation(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Leg::UpdateCurrentAnkleOffset() { if (IsAttached()) { Vector targetOffset = g_SceneMan.ShortestDistance(m_JointPos, m_TargetPosition, g_SceneMan.SceneWrapsX()); Vector rotatedTargetOffset = targetOffset.GetRadRotatedCopy(m_Parent->GetRotAngle()); - if (m_WillIdle && rotatedTargetOffset.m_Y < -std::abs(rotatedTargetOffset.m_X)) { targetOffset = m_Parent->RotateOffset(m_IdleOffset); } + if (m_WillIdle && rotatedTargetOffset.m_Y < -std::abs(rotatedTargetOffset.m_X)) { + targetOffset = m_Parent->RotateOffset(m_IdleOffset); + } Vector distanceFromTargetOffsetToAnkleOffset(targetOffset - m_AnkleOffset); m_AnkleOffset += distanceFromTargetOffsetToAnkleOffset * m_MoveSpeed; @@ -236,7 +244,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Leg::UpdateLegRotation() { if (IsAttached()) { @@ -257,7 +265,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Leg::UpdateFootFrameAndRotation() { if (m_Foot) { @@ -278,4 +286,4 @@ namespace RTE { } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/Leg.h b/Source/Entities/Leg.h index 8db84f1ce7..c459aa5146 100644 --- a/Source/Entities/Leg.h +++ b/Source/Entities/Leg.h @@ -13,7 +13,6 @@ namespace RTE { class Leg : public Attachable { public: - EntityAllocation(Leg); SerializableOverrideMethods; ClassInfoGetters; @@ -35,7 +34,7 @@ namespace RTE { /// /// A reference to the Leg to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Leg &reference); + int Create(const Leg& reference); #pragma endregion #pragma region Destruction @@ -48,12 +47,20 @@ namespace RTE { /// Destroys and resets (through Clear()) the Leg object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { Attachable::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + Attachable::Destroy(); + } + Clear(); + } /// /// Resets the entire Leg, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Attachable::Reset(); } + void Reset() override { + Clear(); + Attachable::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -61,13 +68,13 @@ namespace RTE { /// Gets the foot of this Leg. /// /// A pointer to foot of this Leg. Ownership is NOT transferred! - Attachable * GetFoot() const { return m_Foot; } + Attachable* GetFoot() const { return m_Foot; } /// /// Sets the foot for this Leg. Ownership IS transferred! /// /// The new foot to use. - void SetFoot(Attachable *newFoot); + void SetFoot(Attachable* newFoot); /// /// Gets the min length this of Leg, the minimum allowed length from its joint to its ankle's position. @@ -97,7 +104,7 @@ namespace RTE { /// Sets the position this Leg should move towards, in absolute coordinates. /// /// The position the Leg should move towards. - void SetTargetPosition(const Vector &targetPosition) { m_TargetPosition = targetPosition; } + void SetTargetPosition(const Vector& targetPosition) { m_TargetPosition = targetPosition; } /// /// Sets whether this Leg will go into idle offset mode if the target appears to be above the joint of the Leg. @@ -111,7 +118,7 @@ namespace RTE { /// Gets a copy of this Leg's foot AtomGroup to be used as an Actor's FootGroup. /// /// A copy of this Leg's foot AtomGroup to be used as an Actor's FootGroup. OWNERSHIP IS TRANSFERRED! - AtomGroup * GetFootGroupFromFootAtomGroup(); + AtomGroup* GetFootGroupFromFootAtomGroup(); #pragma endregion #pragma region Override Methods @@ -122,28 +129,26 @@ namespace RTE { #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. - Attachable *m_Foot; //!< Pointer to the foot attachable of this Leg. - + Attachable* m_Foot; //!< Pointer to the foot attachable of this Leg. + Vector m_ContractedOffset; //!< The offset from the joint where the ankle contracts to in the sprite. Vector m_ExtendedOffset; //!< The offset from the joint where the ankle extends to in the sprite. float m_MinExtension; //!< Precalculated min extension of the Leg (from the joint) based on the contracted offset. float m_MaxExtension; //!< Precalculated max extension of the Leg (from the joint) based on the extended offset. float m_NormalizedExtension; //!< Normalized scalar of where the ankle offset's magnitude is between the min and max extensions. - + Vector m_TargetPosition; //!< The absolute position that this Leg's foot is moving towards. Vector m_IdleOffset; //!< The target offset from m_Pos that this Leg's foot is moving towards when allowed to idle and the target position is not acceptable. Vector m_AnkleOffset; //!< Current offset from the joint to the ankle where the foot should be. - + bool m_WillIdle; //!< Whether the Leg will go to idle position if the target position is above the Leg's joint position. float m_MoveSpeed; //!< How fast the Leg moves to a reach target, 0 means it doesn't and 1 means it moves instantly. private: - #pragma region Update Breakdown /// /// Updates the current ankle offset for this Leg. Should only be called from Update. @@ -170,8 +175,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - Leg(const Leg &reference) = delete; - Leg & operator=(const Leg &rhs) = delete; + Leg(const Leg& reference) = delete; + Leg& operator=(const Leg& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/LimbPath.cpp b/Source/Entities/LimbPath.cpp index 599185403e..2214338000 100644 --- a/Source/Entities/LimbPath.cpp +++ b/Source/Entities/LimbPath.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,691 +19,628 @@ namespace RTE { -ConcreteClassInfo(LimbPath, Entity, 20); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this LimbPath, effectively -// resetting the members of this abstraction level only. - -void LimbPath::Clear() -{ - m_Start.Reset(); - m_StartSegCount = 0; - m_Segments.clear(); -// m_CurrentSegment = 0; - m_FootCollisionsDisabledSegment = -1; - m_SegProgress = 0.0; - for (int i = 0; i < SPEEDCOUNT; ++i) { - m_TravelSpeed[i] = 0.0; - } - m_TravelSpeedMultiplier = 1.0F; - m_WhichSpeed = NORMAL; - m_PushForce = 0.0; - m_JointPos.Reset(); - m_JointVel.Reset(); - m_Rotation.Reset(); - m_RotationOffset.Reset(); - m_PositionOffset.Reset(); - m_TimeLeft = 0.0; - m_PathTimer.Reset(); - m_SegTimer.Reset(); - m_TotalLength = 0.0; - m_RegularLength = 0.0; - m_SegmentDone = false; - m_Ended = true; - m_HFlipped = false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the LimbPath object ready for use. - -int LimbPath::Create() -{ - // Read all the properties - if (Entity::Create() < 0) - return -1; - - if (m_Segments.size() > 0) - m_CurrentSegment = m_Segments.begin(); - else - m_CurrentSegment = m_Segments.end(); - - Terminate(); - - return 0; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the LimbPath object ready for use. - -int LimbPath::Create(const Vector &startPoint, - const unsigned int segCount, - const Vector *aSegArray, - const float travelSpeed) -{ - m_StartPoint = startPoint; - m_SegCount = segCount; - m_TravelSpeed = travelSpeed; - - m_Segments = new Vector[m_SegCount]; - - if (aSegArray) - { - for (int i = 0; i < m_SegCount; ++i) - m_Segments[i] = aSegArray[i]; - } - - return 0; -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a LimbPath to be identical to another, by deep copy. - -int LimbPath::Create(const LimbPath &reference) -{ - Entity::Create(reference); - - m_Start = reference.m_Start; - m_StartSegCount = reference.m_StartSegCount; - - std::deque::const_iterator itr; - for (itr = reference.m_Segments.begin(); itr != reference.m_Segments.end(); ++itr) - m_Segments.push_back(*itr); - - if (m_Segments.size() > 0) - m_CurrentSegment = m_Segments.begin(); - else - m_CurrentSegment = m_Segments.end(); - - m_FootCollisionsDisabledSegment = reference.m_FootCollisionsDisabledSegment; - - m_SegProgress = reference.m_SegProgress; - for (int i = 0; i < SPEEDCOUNT; ++i) { - m_TravelSpeed[i] = reference.m_TravelSpeed[i]; - } - m_TravelSpeedMultiplier = reference.m_TravelSpeedMultiplier; - m_PushForce = reference.m_PushForce; - m_TimeLeft = reference.m_TimeLeft; - m_TotalLength = reference.m_TotalLength; - m_RegularLength = reference.m_RegularLength; - m_SegmentDone = reference.m_SegmentDone; - m_HFlipped = reference.m_HFlipped; - - Terminate(); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int LimbPath::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(Entity::ReadProperty(propName, reader)); - - MatchProperty("StartOffset", { reader >> m_Start; }); - MatchProperty("StartSegCount", { reader >> m_StartSegCount; }); - MatchProperty("AddSegment", - { - Vector segment; - reader >> segment; - m_Segments.push_back(segment); - m_TotalLength += segment.GetMagnitude(); - if (m_Segments.size() >= m_StartSegCount) { - m_RegularLength += segment.GetMagnitude(); - } - }); - MatchProperty("EndSegCount", { reader >> m_FootCollisionsDisabledSegment; }); - - MatchProperty("SlowTravelSpeed", { - reader >> m_TravelSpeed[SLOW]; - //m_TravelSpeed[SLOW] = m_TravelSpeed[SLOW] * 2; - }); - MatchProperty("NormalTravelSpeed", { - reader >> m_TravelSpeed[NORMAL]; - //m_TravelSpeed[NORMAL] = m_TravelSpeed[NORMAL] * 2; - }); - MatchProperty("FastTravelSpeed", { - reader >> m_TravelSpeed[FAST]; - //m_TravelSpeed[FAST] = m_TravelSpeed[FAST] * 2; - }); - MatchProperty("TravelSpeedMultiplier", { reader >> m_TravelSpeedMultiplier; }); - MatchProperty("PushForce", { - reader >> m_PushForce; - //m_PushForce = m_PushForce / 1.5; - }); - - EndPropertyList; -} - - -Vector LimbPath::RotatePoint(const Vector &point) const { - Vector offset = (m_RotationOffset).GetXFlipped(m_HFlipped); - return (((point - offset) * m_Rotation) + offset) + m_PositionOffset; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this LimbPath with a Writer for -// later recreation with Create(Reader &reader); - -int LimbPath::Save(Writer &writer) const -{ - Entity::Save(writer); - - writer.NewProperty("StartOffset"); - writer << m_Start; - writer.NewProperty("StartSegCount"); - writer << m_StartSegCount; - for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) - { - writer.NewProperty("AddSegment"); - writer << *itr; - } - writer.NewProperty("SlowTravelSpeed"); - writer << m_TravelSpeed[SLOW]; - writer.NewProperty("NormalTravelSpeed"); - writer << m_TravelSpeed[NORMAL]; - writer.NewProperty("FastTravelSpeed"); - writer << m_TravelSpeed[FAST]; - writer.NewProperty("TravelSpeedMultiplier"); - writer << m_TravelSpeedMultiplier; - writer.NewProperty("PushForce"); - writer << m_PushForce; - - return 0; -} + ConcreteClassInfo(LimbPath, Entity, 20); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this LimbPath, effectively + // resetting the members of this abstraction level only. + + void LimbPath::Clear() { + m_Start.Reset(); + m_StartSegCount = 0; + m_Segments.clear(); + // m_CurrentSegment = 0; + m_FootCollisionsDisabledSegment = -1; + m_SegProgress = 0.0; + for (int i = 0; i < SPEEDCOUNT; ++i) { + m_TravelSpeed[i] = 0.0; + } + m_TravelSpeedMultiplier = 1.0F; + m_WhichSpeed = NORMAL; + m_PushForce = 0.0; + m_JointPos.Reset(); + m_JointVel.Reset(); + m_Rotation.Reset(); + m_RotationOffset.Reset(); + m_PositionOffset.Reset(); + m_TimeLeft = 0.0; + m_PathTimer.Reset(); + m_SegTimer.Reset(); + m_TotalLength = 0.0; + m_RegularLength = 0.0; + m_SegmentDone = false; + m_Ended = true; + m_HFlipped = false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the LimbPath object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the LimbPath object. + int LimbPath::Create() { + // Read all the properties + if (Entity::Create() < 0) + return -1; -void LimbPath::Destroy(bool notInherited) -{ - - if (!notInherited) - Entity::Destroy(); - Clear(); -} + if (m_Segments.size() > 0) + m_CurrentSegment = m_Segments.begin(); + else + m_CurrentSegment = m_Segments.end(); + Terminate(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetProgressPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the APPROXIMATE scene position that the limb was reported to be -// last frame. - -Vector LimbPath::GetProgressPos() -{ - Vector returnVec(m_Start); - if (IsStaticPoint()) { - return m_JointPos + RotatePoint(returnVec); - } + return 0; + } - // Add all the segments before the current one - std::deque::const_iterator itr; - for (itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) { - returnVec += *itr; - } + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the LimbPath object ready for use. - // Add any from the progress made on the current one - if (itr != m_Segments.end()) { - returnVec += *m_CurrentSegment * m_SegProgress; - } + int LimbPath::Create(const Vector &startPoint, + const unsigned int segCount, + const Vector *aSegArray, + const float travelSpeed) + { + m_StartPoint = startPoint; + m_SegCount = segCount; + m_TravelSpeed = travelSpeed; - return m_JointPos + RotatePoint(returnVec); -} + m_Segments = new Vector[m_SegCount]; + if (aSegArray) + { + for (int i = 0; i < m_SegCount; ++i) + m_Segments[i] = aSegArray[i]; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentSegTarget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the scene/world position target that the current segment represents. + return 0; + } + */ -Vector LimbPath::GetCurrentSegTarget() -{ - Vector returnVec(m_Start); - if (IsStaticPoint()) { - return m_JointPos + RotatePoint(returnVec); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a LimbPath to be identical to another, by deep copy. - std::deque::const_iterator itr; + int LimbPath::Create(const LimbPath& reference) { + Entity::Create(reference); - for (itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) { - returnVec += *itr; - } + m_Start = reference.m_Start; + m_StartSegCount = reference.m_StartSegCount; - if (itr != m_Segments.end()) { - returnVec += *m_CurrentSegment; - } + std::deque::const_iterator itr; + for (itr = reference.m_Segments.begin(); itr != reference.m_Segments.end(); ++itr) + m_Segments.push_back(*itr); - return m_JointPos + RotatePoint(returnVec); -} + if (m_Segments.size() > 0) + m_CurrentSegment = m_Segments.begin(); + else + m_CurrentSegment = m_Segments.end(); + m_FootCollisionsDisabledSegment = reference.m_FootCollisionsDisabledSegment; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the velocity of the current position on the path. -// Note that this should be called BEFORE GetNextVec() if the -// appropriate matching velocity is to be returned here. If the limb -// doesn't hit the end of a segment before the time chunk runs out, -// the returned move vector is limited by the time chunk. + m_SegProgress = reference.m_SegProgress; + for (int i = 0; i < SPEEDCOUNT; ++i) { + m_TravelSpeed[i] = reference.m_TravelSpeed[i]; + } + m_TravelSpeedMultiplier = reference.m_TravelSpeedMultiplier; + m_PushForce = reference.m_PushForce; + m_TimeLeft = reference.m_TimeLeft; + m_TotalLength = reference.m_TotalLength; + m_RegularLength = reference.m_RegularLength; + m_SegmentDone = reference.m_SegmentDone; + m_HFlipped = reference.m_HFlipped; -Vector LimbPath::GetCurrentVel(const Vector &limbPos) -{ - Vector returnVel; - Vector distVect = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()); - float adjustedTravelSpeed = (m_TravelSpeed[m_WhichSpeed] / (1.0F + std::abs(m_JointVel.GetY()) * 0.1F)) * m_TravelSpeedMultiplier; + Terminate(); - if (IsStaticPoint()) - { - returnVel = distVect * c_MPP / 0.020/* + m_JointVel*/; - returnVel.CapMagnitude(adjustedTravelSpeed); - returnVel += m_JointVel; + return 0; + } -// if (distVect.MagnitudeIsLessThan(0.5F)) -// returnVel *= 0.1; - } - else - { - returnVel.SetXY(adjustedTravelSpeed, 0); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int LimbPath::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(Entity::ReadProperty(propName, reader)); + + MatchProperty("StartOffset", { reader >> m_Start; }); + MatchProperty("StartSegCount", { reader >> m_StartSegCount; }); + MatchProperty("AddSegment", + { + Vector segment; + reader >> segment; + m_Segments.push_back(segment); + m_TotalLength += segment.GetMagnitude(); + if (m_Segments.size() >= m_StartSegCount) { + m_RegularLength += segment.GetMagnitude(); + } + }); + MatchProperty("EndSegCount", { reader >> m_FootCollisionsDisabledSegment; }); + + MatchProperty("SlowTravelSpeed", { + reader >> m_TravelSpeed[SLOW]; + // m_TravelSpeed[SLOW] = m_TravelSpeed[SLOW] * 2; + }); + MatchProperty("NormalTravelSpeed", { + reader >> m_TravelSpeed[NORMAL]; + // m_TravelSpeed[NORMAL] = m_TravelSpeed[NORMAL] * 2; + }); + MatchProperty("FastTravelSpeed", { + reader >> m_TravelSpeed[FAST]; + // m_TravelSpeed[FAST] = m_TravelSpeed[FAST] * 2; + }); + MatchProperty("TravelSpeedMultiplier", { reader >> m_TravelSpeedMultiplier; }); + MatchProperty("PushForce", { + reader >> m_PushForce; + // m_PushForce = m_PushForce / 1.5; + }); + + EndPropertyList; + } - if (!distVect.IsZero()) - returnVel.AbsRotateTo(distVect); + Vector LimbPath::RotatePoint(const Vector& point) const { + Vector offset = (m_RotationOffset).GetXFlipped(m_HFlipped); + return (((point - offset) * m_Rotation) + offset) + m_PositionOffset; + } - returnVel += m_JointVel; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this LimbPath with a Writer for + // later recreation with Create(Reader &reader); + + int LimbPath::Save(Writer& writer) const { + Entity::Save(writer); + + writer.NewProperty("StartOffset"); + writer << m_Start; + writer.NewProperty("StartSegCount"); + writer << m_StartSegCount; + for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) { + writer.NewProperty("AddSegment"); + writer << *itr; + } + writer.NewProperty("SlowTravelSpeed"); + writer << m_TravelSpeed[SLOW]; + writer.NewProperty("NormalTravelSpeed"); + writer << m_TravelSpeed[NORMAL]; + writer.NewProperty("FastTravelSpeed"); + writer << m_TravelSpeed[FAST]; + writer.NewProperty("TravelSpeedMultiplier"); + writer << m_TravelSpeedMultiplier; + writer.NewProperty("PushForce"); + writer << m_PushForce; + + return 0; + } - return returnVel; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the LimbPath object. + void LimbPath::Destroy(bool notInherited) { -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextTimeChunk -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the time needed to get to the target waypoint of the current -// segment at the current speed, if there are no obstacles. The chunk -// will not exceed the remaining time left on the frame, and will deduct -// itself from the remaining frame time tally (originally set by -// SetFrameTime()). - -float LimbPath::GetNextTimeChunk(const Vector &limbPos) -{ - float timeChunk; - - if (IsStaticPoint()) - { - // Use all the time to get to the target point. - timeChunk = m_TimeLeft; - m_TimeLeft = 0.0; - } - else - { - Vector distance; - // Figure out the distance, in meters, between the limb position and the target. - distance = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()) * c_MPP; - // Add the distance needed to be traveled due to the joint velocity. -// distance += m_JointVel * m_TimeLeft; - - // Figure out the time needed to get to the target at the current speed, if - // there are no obstacles. - timeChunk = distance.GetMagnitude() / (GetSpeed() + m_JointVel.GetMagnitude()); - // Cap the time segment off to what we have left, if needed. - timeChunk = timeChunk > m_TimeLeft ? m_TimeLeft : timeChunk; - // Deduct the time used to pushtravel from the total time left. - m_TimeLeft -= timeChunk; - } - - return timeChunk; -} + if (!notInherited) + Entity::Destroy(); + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetProgressPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the APPROXIMATE scene position that the limb was reported to be + // last frame. + + Vector LimbPath::GetProgressPos() { + Vector returnVec(m_Start); + if (IsStaticPoint()) { + return m_JointPos + RotatePoint(returnVec); + } + + // Add all the segments before the current one + std::deque::const_iterator itr; + for (itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) { + returnVec += *itr; + } + + // Add any from the progress made on the current one + if (itr != m_Segments.end()) { + returnVec += *m_CurrentSegment * m_SegProgress; + } + + return m_JointPos + RotatePoint(returnVec); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReportProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Used to report how much progress was made to getting the limb close to -// the target (the next segment start). - -void LimbPath::ReportProgress(const Vector &limbPos) -{ - if (IsStaticPoint()) { - const float staticPointEndedThreshold = 1.0F; - m_Ended = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()).MagnitudeIsLessThan(staticPointEndedThreshold); - } else { - // Check if we are sufficiently close to the target to start going after the next one. - Vector distVec = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()); - float distance = distVec.GetMagnitude(); - float segMag = (*(m_CurrentSegment)).GetMagnitude(); - - // TODO: Don't hardcode this!") - const float segmentEndedThreshold = 1.5F; - if (distance < segmentEndedThreshold) - { - if (++(m_CurrentSegment) == m_Segments.end()) - { - --(m_CurrentSegment); - // Get normalized progress measure toward the target. - m_SegProgress = distance > segMag ? 0.0F : (1.0F - (distance / segMag)); - m_Ended = true; - } - // Next segment! - else - { - m_SegProgress = 0.0; - m_SegTimer.Reset(); - m_Ended = false; - } - } - else - { - m_SegProgress = distance > segMag ? 0.0F : (1.0F - (distance / segMag)); - m_Ended = false; - } - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentSegTarget + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the scene/world position target that the current segment represents. + Vector LimbPath::GetCurrentSegTarget() { + Vector returnVec(m_Start); + if (IsStaticPoint()) { + return m_JointPos + RotatePoint(returnVec); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a value representing the total progress that has been made on -// this entire path. If the path has ended, 0.0 is returned. + std::deque::const_iterator itr; -float LimbPath::GetTotalProgress() const -{ - if (m_Ended || IsStaticPoint()) - return 0.0; + for (itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) { + returnVec += *itr; + } - float prog = 0; - for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) - prog += itr->GetMagnitude(); + if (itr != m_Segments.end()) { + returnVec += *m_CurrentSegment; + } - prog += (*(m_CurrentSegment)).GetMagnitude() * m_SegProgress; - return prog / m_TotalLength; -} + return m_JointPos + RotatePoint(returnVec); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the velocity of the current position on the path. + // Note that this should be called BEFORE GetNextVec() if the + // appropriate matching velocity is to be returned here. If the limb + // doesn't hit the end of a segment before the time chunk runs out, + // the returned move vector is limited by the time chunk. + + Vector LimbPath::GetCurrentVel(const Vector& limbPos) { + Vector returnVel; + Vector distVect = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()); + float adjustedTravelSpeed = (m_TravelSpeed[m_WhichSpeed] / (1.0F + std::abs(m_JointVel.GetY()) * 0.1F)) * m_TravelSpeedMultiplier; + + if (IsStaticPoint()) { + returnVel = distVect * c_MPP / 0.020 /* + m_JointVel*/; + returnVel.CapMagnitude(adjustedTravelSpeed); + returnVel += m_JointVel; + + // if (distVect.MagnitudeIsLessThan(0.5F)) + // returnVel *= 0.1; + } else { + returnVel.SetXY(adjustedTravelSpeed, 0); + + if (!distVect.IsZero()) + returnVel.AbsRotateTo(distVect); + + returnVel += m_JointVel; + } + + return returnVel; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRegularProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a value representing the progress that has been made on the -// regular part of this path, ie everything except the starting segments. -// If progress has not been made past the starting segments, < 0 will -// be returned. If the path has ended, 0.0 is returned. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNextTimeChunk + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the time needed to get to the target waypoint of the current + // segment at the current speed, if there are no obstacles. The chunk + // will not exceed the remaining time left on the frame, and will deduct + // itself from the remaining frame time tally (originally set by + // SetFrameTime()). + + float LimbPath::GetNextTimeChunk(const Vector& limbPos) { + float timeChunk; + + if (IsStaticPoint()) { + // Use all the time to get to the target point. + timeChunk = m_TimeLeft; + m_TimeLeft = 0.0; + } else { + Vector distance; + // Figure out the distance, in meters, between the limb position and the target. + distance = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()) * c_MPP; + // Add the distance needed to be traveled due to the joint velocity. + // distance += m_JointVel * m_TimeLeft; + + // Figure out the time needed to get to the target at the current speed, if + // there are no obstacles. + timeChunk = distance.GetMagnitude() / (GetSpeed() + m_JointVel.GetMagnitude()); + // Cap the time segment off to what we have left, if needed. + timeChunk = timeChunk > m_TimeLeft ? m_TimeLeft : timeChunk; + // Deduct the time used to pushtravel from the total time left. + m_TimeLeft -= timeChunk; + } + + return timeChunk; + } -float LimbPath::GetRegularProgress() const -{ - if (m_Ended || IsStaticPoint()) - return 0.0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReportProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Used to report how much progress was made to getting the limb close to + // the target (the next segment start). + + void LimbPath::ReportProgress(const Vector& limbPos) { + if (IsStaticPoint()) { + const float staticPointEndedThreshold = 1.0F; + m_Ended = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()).MagnitudeIsLessThan(staticPointEndedThreshold); + } else { + // Check if we are sufficiently close to the target to start going after the next one. + Vector distVec = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()); + float distance = distVec.GetMagnitude(); + float segMag = (*(m_CurrentSegment)).GetMagnitude(); + + // TODO: Don't hardcode this!") + const float segmentEndedThreshold = 1.5F; + if (distance < segmentEndedThreshold) { + if (++(m_CurrentSegment) == m_Segments.end()) { + --(m_CurrentSegment); + // Get normalized progress measure toward the target. + m_SegProgress = distance > segMag ? 0.0F : (1.0F - (distance / segMag)); + m_Ended = true; + } + // Next segment! + else { + m_SegProgress = 0.0; + m_SegTimer.Reset(); + m_Ended = false; + } + } else { + m_SegProgress = distance > segMag ? 0.0F : (1.0F - (distance / segMag)); + m_Ended = false; + } + } + } - float prog = m_RegularLength - m_TotalLength; - for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) - prog += itr->GetMagnitude(); - prog += (*(m_CurrentSegment)).GetMagnitude() * m_SegProgress; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a value representing the total progress that has been made on + // this entire path. If the path has ended, 0.0 is returned. - return prog / m_RegularLength; -} + float LimbPath::GetTotalProgress() const { + if (m_Ended || IsStaticPoint()) + return 0.0; + float prog = 0; + for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) + prog += itr->GetMagnitude(); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + prog += (*(m_CurrentSegment)).GetMagnitude() * m_SegProgress; + return prog / m_TotalLength; + } -int LimbPath::GetCurrentSegmentNumber() const { - int progress = 0; - if (!m_Ended && !IsStaticPoint()) { - for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) { - progress++; - } - } - return progress; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRegularProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a value representing the progress that has been made on the + // regular part of this path, ie everything except the starting segments. + // If progress has not been made past the starting segments, < 0 will + // be returned. If the path has ended, 0.0 is returned. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float LimbPath::GetRegularProgress() const { + if (m_Ended || IsStaticPoint()) + return 0.0; + float prog = m_RegularLength - m_TotalLength; + for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) + prog += itr->GetMagnitude(); + prog += (*(m_CurrentSegment)).GetMagnitude() * m_SegProgress; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the speed that a limb traveling this LimbPath should have to one -// of the three predefined speed settings. - -void LimbPath::SetSpeed(int newSpeed) -{ - if (newSpeed <= SLOW) - m_WhichSpeed = SLOW; - else if (newSpeed >= FAST) - m_WhichSpeed = FAST; - else - m_WhichSpeed = NORMAL; -} + return prog / m_RegularLength; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OverrideSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the speed that a limb traveling this LimbPath with the specified preset should have. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void LimbPath::OverrideSpeed(int speedPreset, float newSpeed) -{ - if (speedPreset == SLOW || speedPreset == FAST || speedPreset == NORMAL) - { - m_TravelSpeed[m_WhichSpeed] = newSpeed; + int LimbPath::GetCurrentSegmentNumber() const { + int progress = 0; + if (!m_Ended && !IsStaticPoint()) { + for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) { + progress++; + } + } + return progress; } -} -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Terminate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this LimbPath's progress to its end. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the speed that a limb traveling this LimbPath should have to one + // of the three predefined speed settings. + + void LimbPath::SetSpeed(int newSpeed) { + if (newSpeed <= SLOW) + m_WhichSpeed = SLOW; + else if (newSpeed >= FAST) + m_WhichSpeed = FAST; + else + m_WhichSpeed = NORMAL; + } -void LimbPath::Terminate() -{ - if (IsStaticPoint()) { - m_Ended = true; - } - else { - m_CurrentSegment = --(m_Segments.end()); - m_SegProgress = 1.0; - m_Ended = true; - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OverrideSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the speed that a limb traveling this LimbPath with the specified preset should have. + void LimbPath::OverrideSpeed(int speedPreset, float newSpeed) { + if (speedPreset == SLOW || speedPreset == FAST || speedPreset == NORMAL) { + m_TravelSpeed[m_WhichSpeed] = newSpeed; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Restart -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Restarts the position tracking of the limb that travels along this -// LimbPath. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Terminate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this LimbPath's progress to its end. + + void LimbPath::Terminate() { + if (IsStaticPoint()) { + m_Ended = true; + } else { + m_CurrentSegment = --(m_Segments.end()); + m_SegProgress = 1.0; + m_Ended = true; + } + } -void LimbPath::Restart() -{ - m_CurrentSegment = m_Segments.begin(); - m_PathTimer.Reset(); - m_SegTimer.Reset(); - m_SegProgress = 0; - m_Ended = false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Restart + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Restarts the position tracking of the limb that travels along this + // LimbPath. + + void LimbPath::Restart() { + m_CurrentSegment = m_Segments.begin(); + m_PathTimer.Reset(); + m_SegTimer.Reset(); + m_SegProgress = 0; + m_Ended = false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RestartFree + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Restarts the position tracking of the limb that travels along this + // LimbPath at a point which does not contain terrain. In doing this, + // a list of potential starting segments are checked and the first to + // yield a starting position that is not in terrain will be picked. + // If none of the candidate starting segments are free of terrain, + // the last one in the list will be picked and false will be returned + // here. The passed in limbPos Vector will be set to the new position of + // the restarted path, if a free spot is found. + + bool LimbPath::RestartFree(Vector& limbPos, MOID MOIDToIgnore, int ignoreTeam) { + std::deque::iterator prevSeg = m_CurrentSegment; + float prevProg = m_SegProgress; + m_SegProgress = 0; + bool found = false; + float result = 0; + + if (IsStaticPoint()) { + Vector notUsed; + Vector targetPos = m_JointPos + RotatePoint(m_Start); + Vector beginPos = targetPos; + // TODO: don't hardcode the beginpos + beginPos.m_Y -= 24; + + result = g_SceneMan.CastObstacleRay(beginPos, targetPos - beginPos, notUsed, limbPos, MOIDToIgnore, ignoreTeam, g_MaterialGrass); + + // Only indicate that we found free position if there were any free pixels encountered + if (result < 0 || result > 0) + found = true; + } else { + Vector notUsed; + + // Start at the very beginning of the path + m_CurrentSegment = m_Segments.begin(); + + // Find the first start segment that has an obstacle on it + int i = 0; + for (; i < m_StartSegCount; ++i) { + Vector offsetSegment = (*m_CurrentSegment); + result = g_SceneMan.CastObstacleRay(GetProgressPos(), RotatePoint(offsetSegment), notUsed, limbPos, MOIDToIgnore, ignoreTeam, g_MaterialGrass); + + // If we found an obstacle after the first pixel, report the current segment as the starting one and that there is free space here + if (result > 0) { + // Set accurate segment progress + // TODO: See if this is a good idea, or if we should just set it to 0 and set limbPos to the start of current segment + m_SegProgress = g_SceneMan.ShortestDistance(GetProgressPos(), limbPos).GetMagnitude() / offsetSegment.GetMagnitude(); + limbPos = GetProgressPos(); + // m_SegProgress = 0; + m_Ended = false; + found = true; + break; + } + // If obstacle was found on first pixel, report last segment as restarting pos, if there was a last segment + else if (result == 0 && m_CurrentSegment != m_Segments.begin()) { + // Use last segment + --(m_CurrentSegment); + limbPos = GetProgressPos(); + m_SegProgress = 0; + m_Ended = false; + found = true; + break; + } + // If obstacle was found on the first pixel of the first segment, then just report that we couldn't find any free space + else if (result == 0 && m_CurrentSegment == m_Segments.begin()) { + found = false; + break; + } + + // Check next segment, and quit if it's the end + if (++(m_CurrentSegment) == m_Segments.end()) { + found = false; + break; + } + } + + // If we couldn't find any obstacles on the starting segments, then set it to the first non-starting seg and report success + if (!found && i == m_StartSegCount && m_CurrentSegment != m_Segments.end()) { + limbPos = GetProgressPos(); + m_SegProgress = 0; + m_Ended = false; + found = true; + } + } + + if (found) { + m_PathTimer.Reset(); + m_SegTimer.Reset(); + return true; + } + + // Failed to find free space, so set back to old state + m_CurrentSegment = prevSeg; + m_SegProgress = prevProg; + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RestartFree -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Restarts the position tracking of the limb that travels along this -// LimbPath at a point which does not contain terrain. In doing this, -// a list of potential starting segments are checked and the first to -// yield a starting position that is not in terrain will be picked. -// If none of the candidate starting segments are free of terrain, -// the last one in the list will be picked and false will be returned -// here. The passed in limbPos Vector will be set to the new position of -// the restarted path, if a free spot is found. - -bool LimbPath::RestartFree(Vector &limbPos, MOID MOIDToIgnore, int ignoreTeam) -{ - std::deque::iterator prevSeg = m_CurrentSegment; - float prevProg = m_SegProgress; - m_SegProgress = 0; - bool found = false; - float result = 0; - - if (IsStaticPoint()) - { - Vector notUsed; - Vector targetPos = m_JointPos + RotatePoint(m_Start); - Vector beginPos = targetPos; -// TODO: don't hardcode the beginpos - beginPos.m_Y -= 24; - - result = g_SceneMan.CastObstacleRay(beginPos, targetPos - beginPos, notUsed, limbPos, MOIDToIgnore, ignoreTeam, g_MaterialGrass); - - // Only indicate that we found free position if there were any free pixels encountered - if (result < 0 || result > 0) - found = true; - } - else - { - Vector notUsed; - - // Start at the very beginning of the path - m_CurrentSegment = m_Segments.begin(); - - // Find the first start segment that has an obstacle on it - int i = 0; - for (; i < m_StartSegCount; ++i) - { - Vector offsetSegment = (*m_CurrentSegment); - result = g_SceneMan.CastObstacleRay(GetProgressPos(), RotatePoint(offsetSegment), notUsed, limbPos, MOIDToIgnore, ignoreTeam, g_MaterialGrass); - - // If we found an obstacle after the first pixel, report the current segment as the starting one and that there is free space here - if (result > 0) - { - // Set accurate segment progress -// TODO: See if this is a good idea, or if we should just set it to 0 and set limbPos to the start of current segment - m_SegProgress = g_SceneMan.ShortestDistance(GetProgressPos(), limbPos).GetMagnitude() / offsetSegment.GetMagnitude(); - limbPos = GetProgressPos(); -// m_SegProgress = 0; - m_Ended = false; - found = true; - break; - } - // If obstacle was found on first pixel, report last segment as restarting pos, if there was a last segment - else if (result == 0 && m_CurrentSegment != m_Segments.begin()) - { - // Use last segment - --(m_CurrentSegment); - limbPos = GetProgressPos(); - m_SegProgress = 0; - m_Ended = false; - found = true; - break; - } - // If obstacle was found on the first pixel of the first segment, then just report that we couldn't find any free space - else if (result == 0 && m_CurrentSegment == m_Segments.begin()) - { - found = false; - break; - } - - // Check next segment, and quit if it's the end - if (++(m_CurrentSegment) == m_Segments.end()) - { - found = false; - break; - } - } - - // If we couldn't find any obstacles on the starting segments, then set it to the first non-starting seg and report success - if (!found && i == m_StartSegCount && m_CurrentSegment != m_Segments.end()) - { - limbPos = GetProgressPos(); - m_SegProgress = 0; - m_Ended = false; - found = true; - } - } - - if (found) - { - m_PathTimer.Reset(); - m_SegTimer.Reset(); - return true; - } - - // Failed to find free space, so set back to old state - m_CurrentSegment = prevSeg; - m_SegProgress = prevProg; - return false; -} - -// Todo - cache this instead of recalculating each time! -float LimbPath::GetLowestY() const -{ - float lowestY = m_Start.GetY(); - for (auto itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) { - lowestY = std::max(itr->GetY(), lowestY); - } - return lowestY; -} - -// Todo - cache this instead of recalculating each time! -float LimbPath::GetMiddleX() const -{ - float lowestX = m_Start.GetX(); - float highestX = m_Start.GetX(); - for (auto itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) { - lowestX = std::min(itr->GetX(), lowestX); - highestX = std::max(itr->GetX(), highestX); - } - float result = (lowestX + highestX) * 0.5F; - return m_HFlipped ? -result : result; -} + // Todo - cache this instead of recalculating each time! + float LimbPath::GetLowestY() const { + float lowestY = m_Start.GetY(); + for (auto itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) { + lowestY = std::max(itr->GetY(), lowestY); + } + return lowestY; + } + // Todo - cache this instead of recalculating each time! + float LimbPath::GetMiddleX() const { + float lowestX = m_Start.GetX(); + float highestX = m_Start.GetX(); + for (auto itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) { + lowestX = std::min(itr->GetX(), lowestX); + highestX = std::max(itr->GetX(), highestX); + } + float result = (lowestX + highestX) * 0.5F; + return m_HFlipped ? -result : result; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this LimbPath's current graphical debug representation to a -// BITMAP of choice. - -void LimbPath::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - unsigned char color) const -{ - Vector prevPoint = m_Start; - Vector nextPoint = prevPoint; - for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) - { - nextPoint += *itr; - - Vector prevWorldPosition = m_JointPos + RotatePoint(prevPoint); - Vector nextWorldPosition = m_JointPos + RotatePoint(nextPoint); - line(pTargetBitmap, prevWorldPosition.m_X, prevWorldPosition.m_Y, nextWorldPosition.m_X, nextWorldPosition.m_Y, color); - - Vector min(std::min(prevWorldPosition.m_X, nextWorldPosition.m_X), std::min(prevWorldPosition.m_Y, nextWorldPosition.m_Y)); - Vector max(std::max(prevWorldPosition.m_X, nextWorldPosition.m_X), std::max(prevWorldPosition.m_Y, nextWorldPosition.m_Y)); - g_SceneMan.RegisterDrawing(pTargetBitmap, g_NoMOID, min.m_X, max.m_Y, max.m_X, min.m_Y); - - prevPoint += *itr; - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this LimbPath's current graphical debug representation to a + // BITMAP of choice. + + void LimbPath::Draw(BITMAP* pTargetBitmap, + const Vector& targetPos, + unsigned char color) const { + Vector prevPoint = m_Start; + Vector nextPoint = prevPoint; + for (std::deque::const_iterator itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) { + nextPoint += *itr; + + Vector prevWorldPosition = m_JointPos + RotatePoint(prevPoint); + Vector nextWorldPosition = m_JointPos + RotatePoint(nextPoint); + line(pTargetBitmap, prevWorldPosition.m_X, prevWorldPosition.m_Y, nextWorldPosition.m_X, nextWorldPosition.m_Y, color); + + Vector min(std::min(prevWorldPosition.m_X, nextWorldPosition.m_X), std::min(prevWorldPosition.m_Y, nextWorldPosition.m_Y)); + Vector max(std::max(prevWorldPosition.m_X, nextWorldPosition.m_X), std::max(prevWorldPosition.m_Y, nextWorldPosition.m_Y)); + g_SceneMan.RegisterDrawing(pTargetBitmap, g_NoMOID, min.m_X, max.m_Y, max.m_X, min.m_Y); + + prevPoint += *itr; + } + } } // namespace RTE \ No newline at end of file diff --git a/Source/Entities/LimbPath.h b/Source/Entities/LimbPath.h index c84e409abd..c5dc9bcd72 100644 --- a/Source/Entities/LimbPath.h +++ b/Source/Entities/LimbPath.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -19,720 +18,684 @@ #include "ActivityMan.h" #include "Atom.h" -namespace RTE -{ +namespace RTE { #define SPEEDCOUNT 3 -enum Speed -{ - SLOW = 0, - NORMAL, - FAST -}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: LimbPath -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A set of Vector:s making up a motion path for a AtomGroup's limb. The -// path is continuous. -// Parent(s): Entity. -// Class history: 05/25/2001 LimbPath created. - -class LimbPath : public Entity { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(LimbPath); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: LimbPath -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a LimbPath object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - LimbPath() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~LimbPath -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a LimbPath object before deletion -// from system memory. -// Arguments: None. - - ~LimbPath() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the LimbPath object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the LimbPath object ready for use. -// Arguments: A Vector specifying starting point of this LimbPath, relative -// to the owning RTEActor's origin. -// An int specifying how many segments there are in the following -// segment array. This MUST match the actual size of the array! -// An array of Vectors that hold the desired path segments to use. -// A float specifying the constant travel speed the limb traveling this -// LimbPath should have, in m/s. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const Vector &startPoint, - const unsigned int segCount = 1, - const Vector *aSegArray = new Vector, - const float travelSpeed = 1.0); -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a LimbPath to be identical to another, by deep copy. -// Arguments: A reference to the LimbPath to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const LimbPath &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire LimbPath, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Entity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the LimbPath object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - /// - /// Gets the coordinates where the limb should start at the start of the LimbPath cycle, relative to the owning AtomGroup's local origin. - /// - /// A Vector with the start position. - const Vector & GetStartOffset() const { return m_Start; } - - /// - /// Sets the coordinates where the limb should start at the start of the LimbPath cycle, relative to the owning AtomGroup's local origin. - /// - /// A Vector with the new start offset. - void SetStartOffset(const Vector &newStartOffset) { m_Start = newStartOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSegCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of Vector:s the internal array of 'waypoints' or -// segments of this LimbPath. -// Arguments: None. -// Return value: An int with he count. - - unsigned int GetSegCount() const { return m_Segments.size(); } - - - /// - /// Gets a pointer to the segment at the given index. Ownership is NOT transferred. - /// - /// The index of the segment to get. - /// A pointer to the segment at the given index. Ownership is NOT transferred. - Vector *GetSegment(int segmentIndex) { if (segmentIndex >= 0 && segmentIndex < m_Segments.size()) { return &m_Segments.at(segmentIndex); } return nullptr;} - - /// - /// Gets whether or not foot collisions should be disabled, i.e. the limbpath's progress is greater than the FootCollisionsDisabledSegment value. - /// - /// Whether or not foot collisions should be disabled for this limbpath at its current progress. - bool FootCollisionsShouldBeDisabled() const { return m_FootCollisionsDisabledSegment >= 0 && GetSegCount() - GetCurrentSegmentNumber() <= m_FootCollisionsDisabledSegment; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSegProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how far the limb was last reported to be away form the current -// segment target/waypoint. -// Arguments: None. -// Return value: A normalized float describing the progress made toward the current -// segment last frame. 0.5 means it was half the length of the current -// segment away from it. - - float GetSegProgress() const { return m_SegProgress; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetProgressPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the APPROXIMATE scene position that the limb was reported to be -// last frame. This really shouldn't be used by external clients. -// Arguments: None. -// Return value: A Vector with the APPROXIAMTE scene/world coordinates of the limb as -// reported last. - - Vector GetProgressPos(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentSegTarget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the scene/world position target that the current segment represents. -// Arguments: None. -// Return value: A vector with the scene position of the current segment target. - - Vector GetCurrentSegTarget(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the velocity of the current position on the path. -// Arguments: The current world coordinate position of the Limb. -// Return value: A Vector with the current move velocity. - - Vector GetCurrentVel(const Vector &limbPos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the speed that a limb traveling this LimbPath should have. -// Arguments: None. -// Return value: A float describing the speed in m/s. - - float GetSpeed() const { return m_TravelSpeed[m_WhichSpeed] * m_TravelSpeedMultiplier; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the speed that a limb traveling this LimbPath should have for the specified preset. -// Arguments: Predefined speed preset to set the value for. -// Return value: A float describing the speed in m/s. - - float GetSpeed(int speedPreset) const { if (speedPreset == SLOW || speedPreset == NORMAL || speedPreset == FAST) return m_TravelSpeed[speedPreset]; else return 0; } - - /// - /// Sets the current travel speed multiplier. - /// - /// The new travel speed multiplier. - void SetTravelSpeedMultiplier(float newValue) { m_TravelSpeedMultiplier = newValue; } - - /// - /// Gets the current travel speed multiplier. - /// - /// The current travel speed multiplier. - float GetTravelSpeedMultiplier() const { return m_TravelSpeedMultiplier; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the force that a limb traveling this LimbPath can push against -// stuff in the scene with. It will increase to the double if progress -// isn't made on the segment. -// Arguments: None. -// Return value: The currently set force maximum, in kg * m/s^2. - - float GetPushForce() const { return m_PushForce + (m_PushForce * (m_SegTimer.GetElapsedSimTimeMS() / 500)); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets thedefault, unaltered force that a limb traveling this LimbPath can push against -// stuff in the scene with. -// Arguments: None. -// Return value: The default set force maximum, in kg * m/s^2. - - float GetDefaultPushForce() const { return m_PushForce; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextTimeChunk -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the time needed to get to the target waypoint of the current -// segment at the current speed, if there are no obstacles. The chunk -// will not exceed the remaining time left on the frame, and will deduct -// itself from the remaining frame time tally (originally set by -// SetFrameTime()). -// Arguments: The current world coordinate position of the Limb. -// Return value: A float describing the time chunk in seconds. - - float GetNextTimeChunk(const Vector &limbPos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalPathTime -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total time that this entire path should take to travel along -// with the current speed setting, including the start segments. -// Arguments: None. -// Return value: The total time (ms) this should take to travel along, if unobstructed. - - float GetTotalPathTime() const { return ((m_TotalLength * c_MPP) / (m_TravelSpeed[m_WhichSpeed] * m_TravelSpeedMultiplier)) * 1000; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRegularPathTime -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total time that this path should take to travel along -// with the current speed setting, NOT including the start segments. -// Arguments: None. -// Return value: The total time (ms) this should take to travel along, if unobstructed. - - float GetRegularPathTime() const { return ((m_RegularLength * c_MPP) / (m_TravelSpeed[m_WhichSpeed] * m_TravelSpeedMultiplier)) * 1000; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalTimeProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the ratio of time since the path was restarted and the total time -// it should take to travel along the path with the current speed setting, -// including the start segments. -// Arguments: None. -// Return value: A positive scalar ratio showing the progress. 0 - 1.0 and beyond. -// If the path has ended, but not been reset, 0 is returned. - - float GetTotalTimeProgress() const { return m_Ended ? 0 : (m_PathTimer.GetElapsedSimTimeMS() / GetTotalPathTime()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRegularTimeProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the ratio of time since the path was restarted and the total time -// it should take to travel along the path with the current speed setting, -// NOT including the start segments. -// Arguments: None. -// Return value: A positive scalar ratio showing the progress. 0 - 1.0 and beyond. -// If the path has ended, but not been reset, 0 is returned. - - float GetRegularTimeProgress() const { return m_Ended ? 0 : (m_PathTimer.GetElapsedSimTimeMS() / GetRegularPathTime()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReportProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Used to report how much progress was made to getting the limb close to -// the target (the current segment waypoint). -// Arguments: The new limb position in world coords. -// Return value: None. - - void ReportProgress(const Vector &limbPos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a value representing the total progress that has been made on -// this entire path. If the path has ended, 0.0 is returned. -// Arguments: None. -// Return value: A float indicating the total progress made on the entire path, from -// 0.0 to 1.0. If the path has ended, 0.0 is returned. - - float GetTotalProgress() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRegularProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a value representing the progress that has been made on the -// regular part of this path, ie averythign except the starting segments. -// If progress has not been made past the starting segments, < 0 will -// be returned. If the path has ended, 0.0 is returned. -// Arguments: None. -// Return value: A float indicating the total progress made on the regular path, from -// 0.0 to 1.0. If the path has ended, 0.0 is returned. - - float GetRegularProgress() const; - - /// - /// Gets the current segment as a number, rather than an iterator. - /// - /// The current segment as a number. - int GetCurrentSegmentNumber() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSegments -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a new array of 'waypoints' or segments of this LimbPath. -// Arguments: An int specifying how many segments there are in the following -// segment array. This MUST match the actual size of the array! -// A pointer to the new Vector array. -// Return value: None. - -// void SetSegments(const unsigned int segCount, const Vector *newSegments); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCurrentSeg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current seg pointer to whichever segment in the segment deque. -// Arguments: An int that is an index to a valid element of the internal segment array. -// Return value: None. - -// void SetCurrentSeg(unsigned int currentSeg) { m_CurrentSegment = currentSeg; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the speed that a limb traveling this LimbPath should have to one -// of the three predefined speed settings. -// Arguments: An int specifying which discrete speed setting to use from the Speed -// enumeration. -// Return value: None. - - void SetSpeed(int newSpeed); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OverrideSpeed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the speed that a limb traveling this LimbPath with the specified preset should have. -// Arguments: An int specifying which discrete speed setting to use from the Speed -// enumeration. New limb travel speed value. -// Return value: None. - - void OverrideSpeed(int speedPreset, float newSpeed); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OverridePushForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the force that a limb traveling this LimbPath can push against -// stuff in the scene with. -// Arguments: The new push force maximum, in kg * m/s^2. -// Return value: None. - - void OverridePushForce(float newForce) { m_PushForce = newForce; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFrameTime -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the amount of time that will be used by the limb to travel every -// frame. Defined in seconds. -// Arguments: A float describing the time in s. -// Return value: None. - - void SetFrameTime(float newFrameTime) { m_TimeLeft = newFrameTime; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetHFlip -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this path is flipped horizontally or not. If being -// flipped the path automatically restarts. -// Arguments: A bool telling this path to be flipped or not. -// Return value: None. - - void SetHFlip(bool hflipped) { m_HFlipped = hflipped; } - - /// - /// Gets the h flip. - /// - /// The h flip. - bool GetHFlip() { return m_HFlipped; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetJointPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Informs this LimbPath of the absolute world coordinates of its owning -// Actor's limb's joint for this frame. Needs to be done before -// travelling anyhting along this path each frame. -// Arguments: A Vector with the updated joint position info. -// Return value: None. - - void SetJointPos(const Vector &jointPos) { m_JointPos = jointPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetJointVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Informs this LimbPath of the current velocity of its owning Actor's -// limb's joint for this frame. Needs to be done before travelling -// anyhting along this path each frame. -// Arguments: A Vector with the updated joint velocity info. -// Return value: None. - - void SetJointVel(const Vector &jointVel) { m_JointVel = jointVel; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetRotation -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Informs this LimbPath of the current rotation of its owning Actor's -// for this frame. Needs to be done before travelling -// anything along this path each frame. -// Arguments: A Matrix with the updated rotation info. -// Return value: None. - - void SetRotation(const Matrix &rotation) { m_Rotation = rotation; m_Rotation.SetXFlipped(m_HFlipped); } - - /// - /// Sets the new rotation offset. - /// - /// The new rotation offset, in local space. - void SetRotationOffset(const Vector& rotationOffset) { m_RotationOffset = rotationOffset; } - - /// - /// Sets the new position offset. - /// - /// The new position offset, in local space. - void SetPositionOffset(const Vector& positionOffset) { m_PositionOffset = positionOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FrameDone -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns if GetNextMoveVec() have to be called again or not on this -// frame. If the last call didn't use up all the time moving on the -// current segment because it ended, this will return false. Then -// GetNextMoveVec() needs to be called at least one more time this frame. -// Arguments: None. -// Return value: A bool with the answer. - - bool FrameDone() const { return m_TimeLeft <= 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PathEnded -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the last call to ProgressMade() completed the -// entire path. Use Restart() to start the path over. -// Arguments: None. -// Return value: A bool with the answer. - - bool PathEnded() const { return m_Ended; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PathIsAtStart -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the path has been restarted without making any -// progress yet. -// Arguments: None. -// Return value: A bool with the answer. - - bool PathIsAtStart() const { return m_CurrentSegment == m_Segments.begin() && m_SegProgress == 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Terminate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this LimbPath's progress to its end. -// Arguments: None. -// Return value: None. - - void Terminate(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Restart -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Restarts the position tracking of the limb that travels along this -// LimbPath. -// Arguments: None. -// Return value: None. - - void Restart(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RestartFree -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Restarts the position tracking of the limb that travels along this -// LimbPath at a point which does not contain terrain. In doing this, -// a list of potential starting segments are checked and the first to -// yield a starting position that is not in terrain will be picked. -// If none of the candidate starting segments are free of terrain, -// the last one in the list will be picked and false will be returned -// here. The passed in limbPos Vector will be set to teh new position of -// the restarted path, if a free spot is found. -// Arguments: Limb scene pos which will be set to the new reset position if a free -// spot was found. -// The root MOID to ignore when looking for a free position. -// To enable ignoring of all MOIDs associated with an object of a specific -// team which also has team ignoring enabled itself. -// Return value: Whether a starting segment that yielded a starting pos free of terrain -// was found or not. - - bool RestartFree(Vector &limbPos, MOID MOIDToIgnore = g_NoMOID, int ignoreTeam = Activity::NoTeam); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsInitialized -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicated whether this path is has been created and had some data set -// yet. -// Arguments: None. -// Return value: Whether this has been Create:ed yet. - - bool IsInitialized() const { return !m_Start.IsZero() || !m_Segments.empty(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsStaticPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicated whether this path is in fact just a single point to where -// the limb will always be ordered to move toward. IE a standing still -// type limb movement. -// Arguments: None. -// Return value: None. - - bool IsStaticPoint() const { return m_Segments.empty(); } - - /// - /// Returns the lowest y position of this LimbPath. - /// - /// The lowest y position of this LimbPath. - float GetLowestY() const; - - /// - /// Returns the middle x position of this LimbPath. - /// - /// The middle x position of this LimbPath. - float GetMiddleX() const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this LimbPath's current graphical debug representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// The color to draw the path's pixels as. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), unsigned char color = 34) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - static Entity::ClassInfo m_sClass; - - // The starting point of the path. - Vector m_Start; - - // The number of starting segments, counting into the path from its beginning, - // that upon restart of this path will be tried in reverse order till one which - // yields a starting position that is clear of terrain is found. - int m_StartSegCount; - - // Array containing the actual 'waypoints' or segments for the path. - std::deque m_Segments; - - // The iterator to the segment of the path that the limb ended up on the end of - std::deque::iterator m_CurrentSegment; - - int m_FootCollisionsDisabledSegment; //!< The segment after which foot collisions will be disabled for this limbpath, if it's for legs. - - // Normalized measure of how far the limb has progressed toward the - // current segment's target. 0.0 means its farther away than the - // magnitude of the entire segment. 0.5 means it's half the mag of the segment - // away from the target. - float m_SegProgress; - - // The constant speed that the limb traveling this path has in m/s. - float m_TravelSpeed[SPEEDCOUNT]; - - // The current travel speed multiplier - float m_TravelSpeedMultiplier; - - // The current speed setting. - int m_WhichSpeed; - - // The max force that a limb travelling along this path can push. - // In kg * m/(s^2) - float m_PushForce; - - // The latest known position of the owning actor's joint in world coordinates. - Vector m_JointPos; - // The latest known velocity of the owning actor's joint in world coordinates. - Vector m_JointVel; - // The rotation applied to this walkpath. - Matrix m_Rotation; - // The point we should be rotated around, in local space. - Vector m_RotationOffset; - // The offset to apply to our walkpath position, in local space. - Vector m_PositionOffset; - - // If GetNextTimeSeg() couldn't use up all frame time because the current segment - // ended,this var stores the remainder of time that should be used to progress - // on the next segment during the same frame. - float m_TimeLeft; - - // Times the amount of sim time spent since the last path traversal was started - Timer m_PathTimer; - // Times the amount of sim time spent pursuing the current segment's target. - Timer m_SegTimer; - - // Total length of this LimbPath, including the alternative starting segments, in pixel units - float m_TotalLength; - // Length of this LimbPath, excluding the alternative starting segments. - float m_RegularLength; - bool m_SegmentDone; - bool m_Ended; - bool m_HFlipped; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this LimbPath, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - /// - /// Rotates a point to match our rotation and rotation offset. - /// - /// The point to rotate. - /// The rotated point. - Vector RotatePoint(const Vector &point) const; - -}; + enum Speed { + SLOW = 0, + NORMAL, + FAST + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: LimbPath + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A set of Vector:s making up a motion path for a AtomGroup's limb. The + // path is continuous. + // Parent(s): Entity. + // Class history: 05/25/2001 LimbPath created. + + class LimbPath : public Entity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(LimbPath); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: LimbPath + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a LimbPath object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + LimbPath() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~LimbPath + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a LimbPath object before deletion + // from system memory. + // Arguments: None. + + ~LimbPath() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the LimbPath object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the LimbPath object ready for use. + // Arguments: A Vector specifying starting point of this LimbPath, relative + // to the owning RTEActor's origin. + // An int specifying how many segments there are in the following + // segment array. This MUST match the actual size of the array! + // An array of Vectors that hold the desired path segments to use. + // A float specifying the constant travel speed the limb traveling this + // LimbPath should have, in m/s. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const Vector &startPoint, + const unsigned int segCount = 1, + const Vector *aSegArray = new Vector, + const float travelSpeed = 1.0); + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a LimbPath to be identical to another, by deep copy. + // Arguments: A reference to the LimbPath to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const LimbPath& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire LimbPath, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Entity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the LimbPath object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + /// + /// Gets the coordinates where the limb should start at the start of the LimbPath cycle, relative to the owning AtomGroup's local origin. + /// + /// A Vector with the start position. + const Vector& GetStartOffset() const { return m_Start; } + + /// + /// Sets the coordinates where the limb should start at the start of the LimbPath cycle, relative to the owning AtomGroup's local origin. + /// + /// A Vector with the new start offset. + void SetStartOffset(const Vector& newStartOffset) { m_Start = newStartOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSegCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of Vector:s the internal array of 'waypoints' or + // segments of this LimbPath. + // Arguments: None. + // Return value: An int with he count. + + unsigned int GetSegCount() const { return m_Segments.size(); } + + /// + /// Gets a pointer to the segment at the given index. Ownership is NOT transferred. + /// + /// The index of the segment to get. + /// A pointer to the segment at the given index. Ownership is NOT transferred. + Vector* GetSegment(int segmentIndex) { + if (segmentIndex >= 0 && segmentIndex < m_Segments.size()) { + return &m_Segments.at(segmentIndex); + } + return nullptr; + } + + /// + /// Gets whether or not foot collisions should be disabled, i.e. the limbpath's progress is greater than the FootCollisionsDisabledSegment value. + /// + /// Whether or not foot collisions should be disabled for this limbpath at its current progress. + bool FootCollisionsShouldBeDisabled() const { return m_FootCollisionsDisabledSegment >= 0 && GetSegCount() - GetCurrentSegmentNumber() <= m_FootCollisionsDisabledSegment; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSegProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how far the limb was last reported to be away form the current + // segment target/waypoint. + // Arguments: None. + // Return value: A normalized float describing the progress made toward the current + // segment last frame. 0.5 means it was half the length of the current + // segment away from it. + + float GetSegProgress() const { return m_SegProgress; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetProgressPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the APPROXIMATE scene position that the limb was reported to be + // last frame. This really shouldn't be used by external clients. + // Arguments: None. + // Return value: A Vector with the APPROXIAMTE scene/world coordinates of the limb as + // reported last. + + Vector GetProgressPos(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentSegTarget + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the scene/world position target that the current segment represents. + // Arguments: None. + // Return value: A vector with the scene position of the current segment target. + + Vector GetCurrentSegTarget(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the velocity of the current position on the path. + // Arguments: The current world coordinate position of the Limb. + // Return value: A Vector with the current move velocity. + + Vector GetCurrentVel(const Vector& limbPos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the speed that a limb traveling this LimbPath should have. + // Arguments: None. + // Return value: A float describing the speed in m/s. + + float GetSpeed() const { return m_TravelSpeed[m_WhichSpeed] * m_TravelSpeedMultiplier; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the speed that a limb traveling this LimbPath should have for the specified preset. + // Arguments: Predefined speed preset to set the value for. + // Return value: A float describing the speed in m/s. + + float GetSpeed(int speedPreset) const { + if (speedPreset == SLOW || speedPreset == NORMAL || speedPreset == FAST) + return m_TravelSpeed[speedPreset]; + else + return 0; + } + + /// + /// Sets the current travel speed multiplier. + /// + /// The new travel speed multiplier. + void SetTravelSpeedMultiplier(float newValue) { m_TravelSpeedMultiplier = newValue; } + + /// + /// Gets the current travel speed multiplier. + /// + /// The current travel speed multiplier. + float GetTravelSpeedMultiplier() const { return m_TravelSpeedMultiplier; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the force that a limb traveling this LimbPath can push against + // stuff in the scene with. It will increase to the double if progress + // isn't made on the segment. + // Arguments: None. + // Return value: The currently set force maximum, in kg * m/s^2. + + float GetPushForce() const { return m_PushForce + (m_PushForce * (m_SegTimer.GetElapsedSimTimeMS() / 500)); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets thedefault, unaltered force that a limb traveling this LimbPath can push against + // stuff in the scene with. + // Arguments: None. + // Return value: The default set force maximum, in kg * m/s^2. + + float GetDefaultPushForce() const { return m_PushForce; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNextTimeChunk + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the time needed to get to the target waypoint of the current + // segment at the current speed, if there are no obstacles. The chunk + // will not exceed the remaining time left on the frame, and will deduct + // itself from the remaining frame time tally (originally set by + // SetFrameTime()). + // Arguments: The current world coordinate position of the Limb. + // Return value: A float describing the time chunk in seconds. + + float GetNextTimeChunk(const Vector& limbPos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalPathTime + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total time that this entire path should take to travel along + // with the current speed setting, including the start segments. + // Arguments: None. + // Return value: The total time (ms) this should take to travel along, if unobstructed. + + float GetTotalPathTime() const { return ((m_TotalLength * c_MPP) / (m_TravelSpeed[m_WhichSpeed] * m_TravelSpeedMultiplier)) * 1000; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRegularPathTime + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total time that this path should take to travel along + // with the current speed setting, NOT including the start segments. + // Arguments: None. + // Return value: The total time (ms) this should take to travel along, if unobstructed. + + float GetRegularPathTime() const { return ((m_RegularLength * c_MPP) / (m_TravelSpeed[m_WhichSpeed] * m_TravelSpeedMultiplier)) * 1000; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalTimeProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the ratio of time since the path was restarted and the total time + // it should take to travel along the path with the current speed setting, + // including the start segments. + // Arguments: None. + // Return value: A positive scalar ratio showing the progress. 0 - 1.0 and beyond. + // If the path has ended, but not been reset, 0 is returned. + + float GetTotalTimeProgress() const { return m_Ended ? 0 : (m_PathTimer.GetElapsedSimTimeMS() / GetTotalPathTime()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRegularTimeProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the ratio of time since the path was restarted and the total time + // it should take to travel along the path with the current speed setting, + // NOT including the start segments. + // Arguments: None. + // Return value: A positive scalar ratio showing the progress. 0 - 1.0 and beyond. + // If the path has ended, but not been reset, 0 is returned. + + float GetRegularTimeProgress() const { return m_Ended ? 0 : (m_PathTimer.GetElapsedSimTimeMS() / GetRegularPathTime()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReportProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Used to report how much progress was made to getting the limb close to + // the target (the current segment waypoint). + // Arguments: The new limb position in world coords. + // Return value: None. + + void ReportProgress(const Vector& limbPos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a value representing the total progress that has been made on + // this entire path. If the path has ended, 0.0 is returned. + // Arguments: None. + // Return value: A float indicating the total progress made on the entire path, from + // 0.0 to 1.0. If the path has ended, 0.0 is returned. + + float GetTotalProgress() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRegularProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a value representing the progress that has been made on the + // regular part of this path, ie averythign except the starting segments. + // If progress has not been made past the starting segments, < 0 will + // be returned. If the path has ended, 0.0 is returned. + // Arguments: None. + // Return value: A float indicating the total progress made on the regular path, from + // 0.0 to 1.0. If the path has ended, 0.0 is returned. + + float GetRegularProgress() const; + + /// + /// Gets the current segment as a number, rather than an iterator. + /// + /// The current segment as a number. + int GetCurrentSegmentNumber() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSegments + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets a new array of 'waypoints' or segments of this LimbPath. + // Arguments: An int specifying how many segments there are in the following + // segment array. This MUST match the actual size of the array! + // A pointer to the new Vector array. + // Return value: None. + + // void SetSegments(const unsigned int segCount, const Vector *newSegments); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCurrentSeg + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current seg pointer to whichever segment in the segment deque. + // Arguments: An int that is an index to a valid element of the internal segment array. + // Return value: None. + + // void SetCurrentSeg(unsigned int currentSeg) { m_CurrentSegment = currentSeg; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the speed that a limb traveling this LimbPath should have to one + // of the three predefined speed settings. + // Arguments: An int specifying which discrete speed setting to use from the Speed + // enumeration. + // Return value: None. + + void SetSpeed(int newSpeed); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OverrideSpeed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the speed that a limb traveling this LimbPath with the specified preset should have. + // Arguments: An int specifying which discrete speed setting to use from the Speed + // enumeration. New limb travel speed value. + // Return value: None. + + void OverrideSpeed(int speedPreset, float newSpeed); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OverridePushForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the force that a limb traveling this LimbPath can push against + // stuff in the scene with. + // Arguments: The new push force maximum, in kg * m/s^2. + // Return value: None. + + void OverridePushForce(float newForce) { m_PushForce = newForce; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetFrameTime + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the amount of time that will be used by the limb to travel every + // frame. Defined in seconds. + // Arguments: A float describing the time in s. + // Return value: None. + + void SetFrameTime(float newFrameTime) { m_TimeLeft = newFrameTime; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetHFlip + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this path is flipped horizontally or not. If being + // flipped the path automatically restarts. + // Arguments: A bool telling this path to be flipped or not. + // Return value: None. + + void SetHFlip(bool hflipped) { m_HFlipped = hflipped; } + + /// + /// Gets the h flip. + /// + /// The h flip. + bool GetHFlip() { return m_HFlipped; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetJointPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Informs this LimbPath of the absolute world coordinates of its owning + // Actor's limb's joint for this frame. Needs to be done before + // travelling anyhting along this path each frame. + // Arguments: A Vector with the updated joint position info. + // Return value: None. + + void SetJointPos(const Vector& jointPos) { m_JointPos = jointPos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetJointVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Informs this LimbPath of the current velocity of its owning Actor's + // limb's joint for this frame. Needs to be done before travelling + // anyhting along this path each frame. + // Arguments: A Vector with the updated joint velocity info. + // Return value: None. + + void SetJointVel(const Vector& jointVel) { m_JointVel = jointVel; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetRotation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Informs this LimbPath of the current rotation of its owning Actor's + // for this frame. Needs to be done before travelling + // anything along this path each frame. + // Arguments: A Matrix with the updated rotation info. + // Return value: None. + + void SetRotation(const Matrix& rotation) { + m_Rotation = rotation; + m_Rotation.SetXFlipped(m_HFlipped); + } + + /// + /// Sets the new rotation offset. + /// + /// The new rotation offset, in local space. + void SetRotationOffset(const Vector& rotationOffset) { m_RotationOffset = rotationOffset; } + + /// + /// Sets the new position offset. + /// + /// The new position offset, in local space. + void SetPositionOffset(const Vector& positionOffset) { m_PositionOffset = positionOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FrameDone + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns if GetNextMoveVec() have to be called again or not on this + // frame. If the last call didn't use up all the time moving on the + // current segment because it ended, this will return false. Then + // GetNextMoveVec() needs to be called at least one more time this frame. + // Arguments: None. + // Return value: A bool with the answer. + + bool FrameDone() const { return m_TimeLeft <= 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PathEnded + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the last call to ProgressMade() completed the + // entire path. Use Restart() to start the path over. + // Arguments: None. + // Return value: A bool with the answer. + + bool PathEnded() const { return m_Ended; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PathIsAtStart + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the path has been restarted without making any + // progress yet. + // Arguments: None. + // Return value: A bool with the answer. + + bool PathIsAtStart() const { return m_CurrentSegment == m_Segments.begin() && m_SegProgress == 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Terminate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this LimbPath's progress to its end. + // Arguments: None. + // Return value: None. + + void Terminate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Restart + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Restarts the position tracking of the limb that travels along this + // LimbPath. + // Arguments: None. + // Return value: None. + + void Restart(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RestartFree + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Restarts the position tracking of the limb that travels along this + // LimbPath at a point which does not contain terrain. In doing this, + // a list of potential starting segments are checked and the first to + // yield a starting position that is not in terrain will be picked. + // If none of the candidate starting segments are free of terrain, + // the last one in the list will be picked and false will be returned + // here. The passed in limbPos Vector will be set to teh new position of + // the restarted path, if a free spot is found. + // Arguments: Limb scene pos which will be set to the new reset position if a free + // spot was found. + // The root MOID to ignore when looking for a free position. + // To enable ignoring of all MOIDs associated with an object of a specific + // team which also has team ignoring enabled itself. + // Return value: Whether a starting segment that yielded a starting pos free of terrain + // was found or not. + + bool RestartFree(Vector& limbPos, MOID MOIDToIgnore = g_NoMOID, int ignoreTeam = Activity::NoTeam); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsInitialized + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicated whether this path is has been created and had some data set + // yet. + // Arguments: None. + // Return value: Whether this has been Create:ed yet. + + bool IsInitialized() const { return !m_Start.IsZero() || !m_Segments.empty(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsStaticPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicated whether this path is in fact just a single point to where + // the limb will always be ordered to move toward. IE a standing still + // type limb movement. + // Arguments: None. + // Return value: None. + + bool IsStaticPoint() const { return m_Segments.empty(); } + + /// + /// Returns the lowest y position of this LimbPath. + /// + /// The lowest y position of this LimbPath. + float GetLowestY() const; + + /// + /// Returns the middle x position of this LimbPath. + /// + /// The middle x position of this LimbPath. + float GetMiddleX() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this LimbPath's current graphical debug representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // The color to draw the path's pixels as. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), unsigned char color = 34) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + static Entity::ClassInfo m_sClass; + + // The starting point of the path. + Vector m_Start; + + // The number of starting segments, counting into the path from its beginning, + // that upon restart of this path will be tried in reverse order till one which + // yields a starting position that is clear of terrain is found. + int m_StartSegCount; + + // Array containing the actual 'waypoints' or segments for the path. + std::deque m_Segments; + + // The iterator to the segment of the path that the limb ended up on the end of + std::deque::iterator m_CurrentSegment; + + int m_FootCollisionsDisabledSegment; //!< The segment after which foot collisions will be disabled for this limbpath, if it's for legs. + + // Normalized measure of how far the limb has progressed toward the + // current segment's target. 0.0 means its farther away than the + // magnitude of the entire segment. 0.5 means it's half the mag of the segment + // away from the target. + float m_SegProgress; + + // The constant speed that the limb traveling this path has in m/s. + float m_TravelSpeed[SPEEDCOUNT]; + + // The current travel speed multiplier + float m_TravelSpeedMultiplier; + + // The current speed setting. + int m_WhichSpeed; + + // The max force that a limb travelling along this path can push. + // In kg * m/(s^2) + float m_PushForce; + + // The latest known position of the owning actor's joint in world coordinates. + Vector m_JointPos; + // The latest known velocity of the owning actor's joint in world coordinates. + Vector m_JointVel; + // The rotation applied to this walkpath. + Matrix m_Rotation; + // The point we should be rotated around, in local space. + Vector m_RotationOffset; + // The offset to apply to our walkpath position, in local space. + Vector m_PositionOffset; + + // If GetNextTimeSeg() couldn't use up all frame time because the current segment + // ended,this var stores the remainder of time that should be used to progress + // on the next segment during the same frame. + float m_TimeLeft; + + // Times the amount of sim time spent since the last path traversal was started + Timer m_PathTimer; + // Times the amount of sim time spent pursuing the current segment's target. + Timer m_SegTimer; + + // Total length of this LimbPath, including the alternative starting segments, in pixel units + float m_TotalLength; + // Length of this LimbPath, excluding the alternative starting segments. + float m_RegularLength; + bool m_SegmentDone; + bool m_Ended; + bool m_HFlipped; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this LimbPath, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + /// + /// Rotates a point to match our rotation and rotation offset. + /// + /// The point to rotate. + /// The rotated point. + Vector RotatePoint(const Vector& point) const; + }; } // namespace RTE diff --git a/Source/Entities/Loadout.cpp b/Source/Entities/Loadout.cpp index 7acbb61f26..6833f5ed9b 100644 --- a/Source/Entities/Loadout.cpp +++ b/Source/Entities/Loadout.cpp @@ -7,7 +7,6 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,290 +17,268 @@ namespace RTE { -ConcreteClassInfo(Loadout, Entity, 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Loadout, effectively -// resetting the members of this abstraction level only. - -void Loadout::Clear() -{ - m_Complete = true; - m_pDeliveryCraft = 0; - m_CargoItems.clear(); -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Loadout object ready for use. - -int Loadout::Create() -{ - if (Entity::Create() < 0) - return -1; - - return 0; -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Loadout to be identical to another, by deep copy. - -int Loadout::Create(const Loadout &reference) -{ - Entity::Create(reference); - - m_Complete = reference.m_Complete; - // These are preset instances, not owned by the reference or this. - m_pDeliveryCraft = reference.m_pDeliveryCraft; - for (std::list::const_iterator itr = reference.m_CargoItems.begin(); itr != reference.m_CargoItems.end(); ++itr) - m_CargoItems.push_back(*itr); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int Loadout::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Entity::ReadProperty(propName, reader)); - - // Need to load all this stuff without the assumption that it all is available. Mods might have changed etc so things might still not be around, and that's ok. - MatchProperty("DeliveryCraft", - { - std::string className; - std::string presetName; - // Load class name and then preset instance name - reader >> className; - // Ignore the property name, just interested in the value - if (reader.ReadPropName() != "PresetName") - reader.ReportError("Expected property \"PresetName\" not found when reading BuyMenu Loadout settings!"); - // Read the preset's name and try to find it - presetName = reader.ReadPropValue(); - // It's OK if we can't find it.. just means we aren't a complete loadout - m_pDeliveryCraft = dynamic_cast(g_PresetMan.GetEntityPreset(className, presetName, -1)); - if (!m_pDeliveryCraft) - m_Complete = false; - // Artificially end reading this property since we got all we needed - reader.NextProperty(); - }); - MatchProperty("AddCargoItem", - { - std::string className; - std::string presetName; - // Load class name and then preset instance name - reader >> className; - // Ignore the property name, just interested in the value - if (reader.ReadPropName() != "PresetName") - reader.ReportError("Expected property \"PresetName\" not found when reading BuyMenu Loadout settings!"); - // Read the preset's name and try to find it - presetName = reader.ReadPropValue(); - // It's OK if we can't find it.. just means we aren't a complete loadout - const MovableObject *pCargo = dynamic_cast(g_PresetMan.GetEntityPreset(className, presetName, -1)); - if (!pCargo) - m_Complete = false; - // Add the found cargo item to the list - else - m_CargoItems.push_back(pCargo); - - // Artificially end reading this property since we got all we needed - reader.NextProperty(); - - pCargo = 0; - }); - - EndPropertyList; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Loadout with a Writer for -// later recreation with Create(Reader &reader); - -int Loadout::Save(Writer &writer) const -{ - Entity::Save(writer); - - if (m_pDeliveryCraft) - { - writer.NewProperty("DeliveryCraft"); - writer.ObjectStart(m_pDeliveryCraft->GetClassName()); - writer.NewProperty("PresetName"); - writer << m_pDeliveryCraft->GetModuleAndPresetName(); - writer.ObjectEnd(); - } - for (std::list::const_iterator itr = m_CargoItems.begin(); itr != m_CargoItems.end(); ++itr) - { - writer.NewProperty("AddCargoItem"); - writer.ObjectStart((*itr)->GetClassName()); - writer.NewProperty("PresetName"); - writer << (*itr)->GetModuleAndPresetName(); - writer.ObjectEnd(); - } - - return 0; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Icon object. - -void Loadout::Destroy(bool notInherited) -{ -// delete; - - if (!notInherited) - Entity::Destroy(); - Clear(); -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateFirstActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the first Actor that this Loadout has and equips. -// Ownership IS transferred!! All items of the Loadout of this Deployment -// will be added to the Actor's inventory as well (and also owned by it) - -Actor * Loadout::CreateFirstActor(int nativeModule, float foreignMult, float nativeMult, float &costTally) const -{ - // The Actor instance we return and pass ownership of - Actor *pReturnActor = 0; - - // Now go through the cargo list of items and load new instances of all - // Devices into inventory of an instance of the first Actor found in the list - if (!m_CargoItems.empty()) - { - // Go through the list of things ordered, and give any actors all the items that is present after them, - // until the next actor. Also, the first actor gets all stuff in the list above him. - MovableObject *pInventoryObject = 0; - Actor *pActor = nullptr; - std::list tempCargo; - for (std::list::const_iterator itr = m_CargoItems.begin(); itr != m_CargoItems.end(); ++itr) - { - // Add to the total cost tally - costTally += (*itr)->GetGoldValue(nativeModule, foreignMult, nativeMult); - // Make copy of the preset instance in the list - pInventoryObject = dynamic_cast((*itr)->Clone()); - // See if it's actually a passenger, as opposed to a regular item - pActor = dynamic_cast(pInventoryObject); - // If it's an actor, then set its team and add it to the Craft's inventory! - if (pActor) - { - // If this is the first passenger, then give him all the shit found in the list before him - if (!pReturnActor) - { - for (std::list::iterator iItr = tempCargo.begin(); iItr != tempCargo.end(); ++iItr) - pActor->AddInventoryItem(*iItr); - } - // This isn't the first passenger, so give the previous guy all the stuff that was found since processing him - else - { - // Go through the temporary list and give the previous, real first actor all the stuff - for (std::list::iterator iItr = tempCargo.begin(); iItr != tempCargo.end(); ++iItr) - pReturnActor->AddInventoryItem(*iItr); - // Clear out the temporary cargo list since we've assigned all the stuff in it to the return Actor - tempCargo.clear(); - - // REMOVE the value of this bookend actor from the total cost tally - he is not included! - costTally -= pActor->GetGoldValue(nativeModule, foreignMult, nativeMult); - // Also stop going through the list; we only need ONE Actor.. so destroy the instance we jsut created of a second one. - delete pActor; - pActor = nullptr; - // STOP! - break; - } - // Clear out the temporary cargo list since we've assigned all the stuff in it to the return Actor - tempCargo.clear(); - - // Now set the current Actor as the one we return; he'll eventually get everything found after him as well - pReturnActor = pActor; - // Set the position and team etc for the Actor we are prepping to spawn - pReturnActor->SetControllerMode(Controller::CIM_AI); - pReturnActor->SetAIMode(Actor::AIMODE_SENTRY); - } - // If not an Actor, then add it to the temp list of items which will be added to the last passenger's inventory - else - tempCargo.push_back(pInventoryObject); - } - pActor = 0; - - // If there was a last passenger and only things after him, stuff all the items into his inventory - if (pReturnActor) - { - // Passing ownership - for (std::list::iterator iItr = tempCargo.begin(); iItr != tempCargo.end(); ++iItr) - pReturnActor->AddInventoryItem(*iItr); - tempCargo.clear(); - } - // If there wa NO actor in the Loadout's cargo list, then see if there's a craft we can stuff thigns into instead - else if (m_pDeliveryCraft) - { - // The craft is now the Actor we are spawning, so make an instance - pReturnActor = dynamic_cast(m_pDeliveryCraft->Clone()); - // Add the cost of the ship - costTally += pReturnActor->GetGoldValue(nativeModule, foreignMult, nativeMult); - // Fill it with the stuff, passing ownership - for (std::list::iterator iItr = tempCargo.begin(); iItr != tempCargo.end(); ++iItr) - pReturnActor->AddInventoryItem(*iItr); - tempCargo.clear(); - } - } - - // PASSING OWNERSHIP - return pReturnActor; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CreateFirstDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the first Device that is defined in this Loadout. -// Ownership IS transferred!! Only the first Device is created. - -SceneObject * Loadout::CreateFirstDevice(int nativeModule, float foreignMult, float nativeMult, float &costTally) const -{ - // The Actor instance we return and pass ownership of - SceneObject *pReturnObject = 0; - - // Now go through the Loadout list of items and load new instances of all - // Devices into inventory of an instance of the first Actor found in the list - if (!m_CargoItems.empty()) - { - for (std::list::const_iterator itr = m_CargoItems.begin(); itr != m_CargoItems.end(); ++itr) - { - // If not an Actor, then we should create and return it. - if (!dynamic_cast(*itr)) - { - pReturnObject = dynamic_cast((*itr)->Clone()); - // Add to the total cost tally - costTally += pReturnObject->GetGoldValue(nativeModule, foreignMult, nativeMult); - // We're done - break; - } - } - } - - // PASSING OWNERSHIP - return pReturnObject; -} + ConcreteClassInfo(Loadout, Entity, 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Loadout, effectively + // resetting the members of this abstraction level only. + + void Loadout::Clear() { + m_Complete = true; + m_pDeliveryCraft = 0; + m_CargoItems.clear(); + } + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Loadout object ready for use. + + int Loadout::Create() + { + if (Entity::Create() < 0) + return -1; + + return 0; + } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Loadout to be identical to another, by deep copy. + + int Loadout::Create(const Loadout& reference) { + Entity::Create(reference); + + m_Complete = reference.m_Complete; + // These are preset instances, not owned by the reference or this. + m_pDeliveryCraft = reference.m_pDeliveryCraft; + for (std::list::const_iterator itr = reference.m_CargoItems.begin(); itr != reference.m_CargoItems.end(); ++itr) + m_CargoItems.push_back(*itr); + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int Loadout::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Entity::ReadProperty(propName, reader)); + + // Need to load all this stuff without the assumption that it all is available. Mods might have changed etc so things might still not be around, and that's ok. + MatchProperty("DeliveryCraft", + { + std::string className; + std::string presetName; + // Load class name and then preset instance name + reader >> className; + // Ignore the property name, just interested in the value + if (reader.ReadPropName() != "PresetName") + reader.ReportError("Expected property \"PresetName\" not found when reading BuyMenu Loadout settings!"); + // Read the preset's name and try to find it + presetName = reader.ReadPropValue(); + // It's OK if we can't find it.. just means we aren't a complete loadout + m_pDeliveryCraft = dynamic_cast(g_PresetMan.GetEntityPreset(className, presetName, -1)); + if (!m_pDeliveryCraft) + m_Complete = false; + // Artificially end reading this property since we got all we needed + reader.NextProperty(); + }); + MatchProperty("AddCargoItem", + { + std::string className; + std::string presetName; + // Load class name and then preset instance name + reader >> className; + // Ignore the property name, just interested in the value + if (reader.ReadPropName() != "PresetName") + reader.ReportError("Expected property \"PresetName\" not found when reading BuyMenu Loadout settings!"); + // Read the preset's name and try to find it + presetName = reader.ReadPropValue(); + // It's OK if we can't find it.. just means we aren't a complete loadout + const MovableObject* pCargo = dynamic_cast(g_PresetMan.GetEntityPreset(className, presetName, -1)); + if (!pCargo) + m_Complete = false; + // Add the found cargo item to the list + else + m_CargoItems.push_back(pCargo); + + // Artificially end reading this property since we got all we needed + reader.NextProperty(); + + pCargo = 0; + }); + + EndPropertyList; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this Loadout with a Writer for + // later recreation with Create(Reader &reader); + + int Loadout::Save(Writer& writer) const { + Entity::Save(writer); + + if (m_pDeliveryCraft) { + writer.NewProperty("DeliveryCraft"); + writer.ObjectStart(m_pDeliveryCraft->GetClassName()); + writer.NewProperty("PresetName"); + writer << m_pDeliveryCraft->GetModuleAndPresetName(); + writer.ObjectEnd(); + } + for (std::list::const_iterator itr = m_CargoItems.begin(); itr != m_CargoItems.end(); ++itr) { + writer.NewProperty("AddCargoItem"); + writer.ObjectStart((*itr)->GetClassName()); + writer.NewProperty("PresetName"); + writer << (*itr)->GetModuleAndPresetName(); + writer.ObjectEnd(); + } + + return 0; + } + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the Icon object. + + void Loadout::Destroy(bool notInherited) + { + // delete; + + if (!notInherited) + Entity::Destroy(); + Clear(); + } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateFirstActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the first Actor that this Loadout has and equips. + // Ownership IS transferred!! All items of the Loadout of this Deployment + // will be added to the Actor's inventory as well (and also owned by it) + + Actor* Loadout::CreateFirstActor(int nativeModule, float foreignMult, float nativeMult, float& costTally) const { + // The Actor instance we return and pass ownership of + Actor* pReturnActor = 0; + + // Now go through the cargo list of items and load new instances of all + // Devices into inventory of an instance of the first Actor found in the list + if (!m_CargoItems.empty()) { + // Go through the list of things ordered, and give any actors all the items that is present after them, + // until the next actor. Also, the first actor gets all stuff in the list above him. + MovableObject* pInventoryObject = 0; + Actor* pActor = nullptr; + std::list tempCargo; + for (std::list::const_iterator itr = m_CargoItems.begin(); itr != m_CargoItems.end(); ++itr) { + // Add to the total cost tally + costTally += (*itr)->GetGoldValue(nativeModule, foreignMult, nativeMult); + // Make copy of the preset instance in the list + pInventoryObject = dynamic_cast((*itr)->Clone()); + // See if it's actually a passenger, as opposed to a regular item + pActor = dynamic_cast(pInventoryObject); + // If it's an actor, then set its team and add it to the Craft's inventory! + if (pActor) { + // If this is the first passenger, then give him all the shit found in the list before him + if (!pReturnActor) { + for (std::list::iterator iItr = tempCargo.begin(); iItr != tempCargo.end(); ++iItr) + pActor->AddInventoryItem(*iItr); + } + // This isn't the first passenger, so give the previous guy all the stuff that was found since processing him + else { + // Go through the temporary list and give the previous, real first actor all the stuff + for (std::list::iterator iItr = tempCargo.begin(); iItr != tempCargo.end(); ++iItr) + pReturnActor->AddInventoryItem(*iItr); + // Clear out the temporary cargo list since we've assigned all the stuff in it to the return Actor + tempCargo.clear(); + + // REMOVE the value of this bookend actor from the total cost tally - he is not included! + costTally -= pActor->GetGoldValue(nativeModule, foreignMult, nativeMult); + // Also stop going through the list; we only need ONE Actor.. so destroy the instance we jsut created of a second one. + delete pActor; + pActor = nullptr; + // STOP! + break; + } + // Clear out the temporary cargo list since we've assigned all the stuff in it to the return Actor + tempCargo.clear(); + + // Now set the current Actor as the one we return; he'll eventually get everything found after him as well + pReturnActor = pActor; + // Set the position and team etc for the Actor we are prepping to spawn + pReturnActor->SetControllerMode(Controller::CIM_AI); + pReturnActor->SetAIMode(Actor::AIMODE_SENTRY); + } + // If not an Actor, then add it to the temp list of items which will be added to the last passenger's inventory + else + tempCargo.push_back(pInventoryObject); + } + pActor = 0; + + // If there was a last passenger and only things after him, stuff all the items into his inventory + if (pReturnActor) { + // Passing ownership + for (std::list::iterator iItr = tempCargo.begin(); iItr != tempCargo.end(); ++iItr) + pReturnActor->AddInventoryItem(*iItr); + tempCargo.clear(); + } + // If there wa NO actor in the Loadout's cargo list, then see if there's a craft we can stuff thigns into instead + else if (m_pDeliveryCraft) { + // The craft is now the Actor we are spawning, so make an instance + pReturnActor = dynamic_cast(m_pDeliveryCraft->Clone()); + // Add the cost of the ship + costTally += pReturnActor->GetGoldValue(nativeModule, foreignMult, nativeMult); + // Fill it with the stuff, passing ownership + for (std::list::iterator iItr = tempCargo.begin(); iItr != tempCargo.end(); ++iItr) + pReturnActor->AddInventoryItem(*iItr); + tempCargo.clear(); + } + } + + // PASSING OWNERSHIP + return pReturnActor; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CreateFirstDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the first Device that is defined in this Loadout. + // Ownership IS transferred!! Only the first Device is created. + + SceneObject* Loadout::CreateFirstDevice(int nativeModule, float foreignMult, float nativeMult, float& costTally) const { + // The Actor instance we return and pass ownership of + SceneObject* pReturnObject = 0; + + // Now go through the Loadout list of items and load new instances of all + // Devices into inventory of an instance of the first Actor found in the list + if (!m_CargoItems.empty()) { + for (std::list::const_iterator itr = m_CargoItems.begin(); itr != m_CargoItems.end(); ++itr) { + // If not an Actor, then we should create and return it. + if (!dynamic_cast(*itr)) { + pReturnObject = dynamic_cast((*itr)->Clone()); + // Add to the total cost tally + costTally += pReturnObject->GetGoldValue(nativeModule, foreignMult, nativeMult); + // We're done + break; + } + } + } + + // PASSING OWNERSHIP + return pReturnObject; + } } // namespace RTE \ No newline at end of file diff --git a/Source/Entities/Loadout.h b/Source/Entities/Loadout.h index 709694f8ec..5d9189e0c2 100644 --- a/Source/Entities/Loadout.h +++ b/Source/Entities/Loadout.h @@ -10,208 +10,205 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files -//#include "FrameMan.h" +// #include "FrameMan.h" #include "Entity.h" -namespace RTE -{ - -class SceneObject; -class MovableObject; -class ACraft; -class Actor; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Nested class: Loadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines a delivery of Actors, with all their equipment etc. -// Parent(s): Entity. -// Class history: 2/21/2012 Loadout created. - -class Loadout : public Entity { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(Loadout); -SerializableOverrideMethods; -ClassInfoGetters; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: Loadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a Loadout object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - Loadout() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: Loadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Copy constructor method used to instantiate a Loadout object -// identical to an already existing one. -// Arguments: A Loadout object which is passed in by reference. - - Loadout(const Loadout &reference) { if (this != &reference) { Clear(); Create(reference); } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Operator: Loadout assignment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: An assignment operator for setting one Loadout equal to another. -// Arguments: A Loadout reference. -// Return value: A reference to the changed Loadout. - - Loadout & operator=(const Loadout &rhs) { if (this != &rhs) { Destroy(); Create(rhs); } return *this; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Loadout to be identical to another, by deep copy. -// Arguments: A reference to the Loadout to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const Loadout &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire Entity, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Entity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsComplete -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this loadout is complete, or some Entity within could -// not be found on load. -// Arguments: None. -// Return value: Complete or not. - - bool IsComplete() { return m_Complete; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateFirstActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the first Actor that this Loadout has and equips. -// Ownership IS transferred!! All items of the Loadout of this Deployment -// will be added to the Actor's inventory as well (and also owned by it) -// Arguments: Which in-game player to create the Actor for. -// A float which will be added to with the cost of the stuff returned here. -// Return value: The Actor instance, if any, that is first defined in this Loadout. -// OWNERSHIP IS TRANSFERRED! - - Actor * CreateFirstActor(int nativeModule, float foreignMult, float nativeMult, float &costTally) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateFirstDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns the first Device that is defined in this Loadout. -// Ownership IS transferred!! Only the first Device is created. -// Arguments: Which in-game player to create the device for. -// A float which will be added to with the cost of the stuff returned here. -// Return value: The SceneObject instance, if any, that this Loadout defines first. -// OWNERSHIP IS TRANSFERRED! - - SceneObject * CreateFirstDevice(int nativeModule, float foreignMult, float nativeMult, float &costTally) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDeliveryCraft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the preset of the delivery craft set for this loadout. Owenership -// is NOT transferred! -// Arguments: None. -// Return value: A pointer to the ACraft preset instance. OWNERSHIP IS NOT TRANSFERRED! - - const ACraft * GetDeliveryCraft() const { return m_pDeliveryCraft; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDeliveryCraft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the preset of the delivery craft set for this loadout. Owenership -// is NOT transferred! -// Arguments: A pointer to the ACraft preset instance. OWNERSHIP IS NOT TRANSFERRED! -// Return value: None. - - void SetDeliveryCraft(const ACraft *pCraft) { m_pDeliveryCraft = pCraft; m_Complete = m_Complete && m_pDeliveryCraft; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCargoList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the list of cargo Entity items this Loadout represents. -// Arguments: None. -// Return value: A pointer to the list of cargo Entity items. OWNERSHIP IS NOT TRANSFERRED! - - std::list * GetCargoList() { return &m_CargoItems; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddToCargoList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a new Preset to the list of cargo items to be included in this. -// Arguments: A const pointer to the ScneObject preset we want to add to this loadout. -// Return value: None. - - void AddToCargoList(const SceneObject *pNewItem) { if (pNewItem) m_CargoItems.push_back(pNewItem); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - // Whether this Loadout is the full thing, or something is missing due to unavailable entities on load - bool m_Complete; - // Preset instance of the delivery craft, not owned by this. - const ACraft *m_pDeliveryCraft; - // The cargo of this loadout, all preset instances not owned by this - std::list m_CargoItems; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Loadout, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; +namespace RTE { + + class SceneObject; + class MovableObject; + class ACraft; + class Actor; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Nested class: Loadout + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines a delivery of Actors, with all their equipment etc. + // Parent(s): Entity. + // Class history: 2/21/2012 Loadout created. + + class Loadout : public Entity { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(Loadout); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: Loadout + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a Loadout object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + Loadout() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: Loadout + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Copy constructor method used to instantiate a Loadout object + // identical to an already existing one. + // Arguments: A Loadout object which is passed in by reference. + + Loadout(const Loadout& reference) { + if (this != &reference) { + Clear(); + Create(reference); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Operator: Loadout assignment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: An assignment operator for setting one Loadout equal to another. + // Arguments: A Loadout reference. + // Return value: A reference to the changed Loadout. + + Loadout& operator=(const Loadout& rhs) { + if (this != &rhs) { + Destroy(); + Create(rhs); + } + return *this; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Loadout to be identical to another, by deep copy. + // Arguments: A reference to the Loadout to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const Loadout& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire Entity, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Entity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsComplete + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this loadout is complete, or some Entity within could + // not be found on load. + // Arguments: None. + // Return value: Complete or not. + + bool IsComplete() { return m_Complete; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateFirstActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the first Actor that this Loadout has and equips. + // Ownership IS transferred!! All items of the Loadout of this Deployment + // will be added to the Actor's inventory as well (and also owned by it) + // Arguments: Which in-game player to create the Actor for. + // A float which will be added to with the cost of the stuff returned here. + // Return value: The Actor instance, if any, that is first defined in this Loadout. + // OWNERSHIP IS TRANSFERRED! + + Actor* CreateFirstActor(int nativeModule, float foreignMult, float nativeMult, float& costTally) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateFirstDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns the first Device that is defined in this Loadout. + // Ownership IS transferred!! Only the first Device is created. + // Arguments: Which in-game player to create the device for. + // A float which will be added to with the cost of the stuff returned here. + // Return value: The SceneObject instance, if any, that this Loadout defines first. + // OWNERSHIP IS TRANSFERRED! + + SceneObject* CreateFirstDevice(int nativeModule, float foreignMult, float nativeMult, float& costTally) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDeliveryCraft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the preset of the delivery craft set for this loadout. Owenership + // is NOT transferred! + // Arguments: None. + // Return value: A pointer to the ACraft preset instance. OWNERSHIP IS NOT TRANSFERRED! + + const ACraft* GetDeliveryCraft() const { return m_pDeliveryCraft; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDeliveryCraft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the preset of the delivery craft set for this loadout. Owenership + // is NOT transferred! + // Arguments: A pointer to the ACraft preset instance. OWNERSHIP IS NOT TRANSFERRED! + // Return value: None. + + void SetDeliveryCraft(const ACraft* pCraft) { + m_pDeliveryCraft = pCraft; + m_Complete = m_Complete && m_pDeliveryCraft; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCargoList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the list of cargo Entity items this Loadout represents. + // Arguments: None. + // Return value: A pointer to the list of cargo Entity items. OWNERSHIP IS NOT TRANSFERRED! + + std::list* GetCargoList() { return &m_CargoItems; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddToCargoList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a new Preset to the list of cargo items to be included in this. + // Arguments: A const pointer to the ScneObject preset we want to add to this loadout. + // Return value: None. + + void AddToCargoList(const SceneObject* pNewItem) { + if (pNewItem) + m_CargoItems.push_back(pNewItem); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + // Whether this Loadout is the full thing, or something is missing due to unavailable entities on load + bool m_Complete; + // Preset instance of the delivery craft, not owned by this. + const ACraft* m_pDeliveryCraft; + // The cargo of this loadout, all preset instances not owned by this + std::list m_CargoItems; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Loadout, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE -#endif // File \ No newline at end of file +#endif // File \ No newline at end of file diff --git a/Source/Entities/MOPixel.cpp b/Source/Entities/MOPixel.cpp index 27371996cd..1feefb8c06 100644 --- a/Source/Entities/MOPixel.cpp +++ b/Source/Entities/MOPixel.cpp @@ -8,7 +8,7 @@ namespace RTE { ConcreteClassInfo(MOPixel, MovableObject, 2000); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOPixel::Clear() { m_Atom = 0; @@ -20,36 +20,42 @@ namespace RTE { m_Staininess = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int MOPixel::Create() { if (MovableObject::Create() < 0) { return -1; } - if (!m_Atom) { m_Atom = new Atom; } + if (!m_Atom) { + m_Atom = new Atom; + } - if (m_MinLethalRange < m_MaxLethalRange) { m_LethalRange *= RandomNum(m_MinLethalRange, m_MaxLethalRange); } + if (m_MinLethalRange < m_MaxLethalRange) { + m_LethalRange *= RandomNum(m_MinLethalRange, m_MaxLethalRange); + } m_LethalSharpness = m_Sharpness * 0.5F; return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::Create(Color color, const float mass, const Vector &position, const Vector &velocity, Atom *atom, const unsigned long lifetime) { + int MOPixel::Create(Color color, const float mass, const Vector& position, const Vector& velocity, Atom* atom, const unsigned long lifetime) { m_Color = color; m_Atom = atom; m_Atom->SetOwner(this); - if (m_MinLethalRange < m_MaxLethalRange) { m_LethalRange *= RandomNum(m_MinLethalRange, m_MaxLethalRange); } + if (m_MinLethalRange < m_MaxLethalRange) { + m_LethalRange *= RandomNum(m_MinLethalRange, m_MaxLethalRange); + } m_LethalSharpness = m_Sharpness * 0.5F; return MovableObject::Create(mass, position, velocity, lifetime); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::Create(const MOPixel &reference) { + int MOPixel::Create(const MOPixel& reference) { MovableObject::Create(reference); m_Atom = new Atom(*(reference.m_Atom)); @@ -64,13 +70,15 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::ReadProperty(const std::string_view &propName, Reader &reader) { + int MOPixel::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MovableObject::ReadProperty(propName, reader)); - + MatchProperty("Atom", { - if (!m_Atom) { m_Atom = new Atom; } + if (!m_Atom) { + m_Atom = new Atom; + } reader >> m_Atom; m_Atom->SetOwner(this); }); @@ -82,9 +90,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::Save(Writer &writer) const { + int MOPixel::Save(Writer& writer) const { MovableObject::Save(writer); writer.NewProperty("Atom"); @@ -101,51 +109,55 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOPixel::Destroy(bool notInherited) { delete m_Atom; - if (!notInherited) { MovableObject::Destroy(); } + if (!notInherited) { + MovableObject::Destroy(); + } Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int MOPixel::GetDrawPriority() const { return m_Atom->GetMaterial()->GetPriority(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Material * MOPixel::GetMaterial() const { return m_Atom->GetMaterial(); } + const Material* MOPixel::GetMaterial() const { return m_Atom->GetMaterial(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::SetAtom(Atom *newAtom) { + void MOPixel::SetAtom(Atom* newAtom) { delete m_Atom; m_Atom = newAtom; m_Atom->SetOwner(this); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOPixel::SetLethalRange(float range) { m_LethalRange = range; - if (m_MinLethalRange < m_MaxLethalRange) { m_LethalRange *= RandomNum(m_MinLethalRange, m_MaxLethalRange); } + if (m_MinLethalRange < m_MaxLethalRange) { + m_LethalRange *= RandomNum(m_MinLethalRange, m_MaxLethalRange); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int MOPixel::GetTrailLength() const { return m_Atom->GetTrailLength(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOPixel::SetTrailLength(int trailLength) { m_Atom->SetTrailLength(trailLength); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool MOPixel::HitTestAtPixel(int pixelX, int pixelY) const { if (!GetsHitByMOs() || GetRootParent()->GetTraveling()) { @@ -155,7 +167,7 @@ namespace RTE { return m_Pos.GetFloorIntX() == pixelX && m_Pos.GetFloorIntY() == pixelY; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOPixel::Travel() { MovableObject::Travel(); @@ -168,20 +180,22 @@ namespace RTE { if (m_HitsMOs && m_pMOToNotHit && g_MovableMan.ValidMO(m_pMOToNotHit) && !m_MOIgnoreTimer.IsPastSimTimeLimit()) { std::vector MOIDsNotToHit; m_pMOToNotHit->GetMOIDs(MOIDsNotToHit); - for (const MOID &MOIDNotToHit : MOIDsNotToHit) { + for (const MOID& MOIDNotToHit: MOIDsNotToHit) { m_Atom->AddMOIDToIgnore(MOIDNotToHit); } } // Do static particle bounce calculations. int hitCount = 0; - if (!IsTooFast()) { hitCount = m_Atom->Travel(g_TimerMan.GetDeltaTimeSecs(), true, g_SceneMan.SceneIsLocked()); } + if (!IsTooFast()) { + hitCount = m_Atom->Travel(g_TimerMan.GetDeltaTimeSecs(), true, g_SceneMan.SceneIsLocked()); + } m_Atom->ClearMOIDIgnoreList(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MOPixel::CollideAtPoint(HitData &hd) { + bool MOPixel::CollideAtPoint(HitData& hd) { RTEAssert(hd.HitPoint.GetFloored() == m_Pos.GetFloored(), "Collision mismatch in MOPixel::CollideAtPoint!"); RTEAssert(hd.Body[HITOR], "Valid MO not passed into MOPixel::CollideAtPoint!"); @@ -197,7 +211,7 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOPixel::RestDetection() { MovableObject::RestDetection(); @@ -210,7 +224,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOPixel::Update() { MovableObject::Update(); @@ -237,9 +251,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::Draw(BITMAP *targetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { + void MOPixel::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { // Don't draw color if this isn't a drawing frame if (!g_TimerMan.DrawnSimUpdate() && mode == g_DrawColor) { return; @@ -277,7 +291,7 @@ namespace RTE { g_SceneMan.RegisterDrawing(targetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, pixelPos, 1.0F); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOPixel::SetPostScreenEffectToDraw() const { if (m_AgeTimer.GetElapsedSimTimeMS() >= m_EffectStartTime && (m_EffectStopTime == 0 || !m_AgeTimer.IsPastSimMS(m_EffectStopTime))) { @@ -286,4 +300,4 @@ namespace RTE { } } } -} +} // namespace RTE diff --git a/Source/Entities/MOPixel.h b/Source/Entities/MOPixel.h index 402fdc9ba5..0a9c4da548 100644 --- a/Source/Entities/MOPixel.h +++ b/Source/Entities/MOPixel.h @@ -13,7 +13,6 @@ namespace RTE { class MOPixel : public MovableObject { public: - EntityAllocation(MOPixel); ClassInfoGetters; SerializableOverrideMethods; @@ -33,7 +32,10 @@ namespace RTE { /// A Vector specifying the initial velocity. /// An Atom that will collide with the terrain. Ownership IS transferred! /// The amount of time in ms this MOPixel will exist. 0 means unlimited. - MOPixel(Color color, const float mass, const Vector &position, const Vector &velocity, Atom *atom, const unsigned long lifetime = 0) { Clear(); Create(color, mass, position, velocity, atom, lifetime); } + MOPixel(Color color, const float mass, const Vector& position, const Vector& velocity, Atom* atom, const unsigned long lifetime = 0) { + Clear(); + Create(color, mass, position, velocity, atom, lifetime); + } /// /// Makes the MOPixel object ready for use. @@ -51,21 +53,21 @@ namespace RTE { /// An Atom that will collide with the terrain. /// The amount of time in ms this MOPixel will exist. 0 means unlimited. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(Color color, const float mass, const Vector &position, const Vector &velocity, Atom *atom, const unsigned long lifetime = 0); + int Create(Color color, const float mass, const Vector& position, const Vector& velocity, Atom* atom, const unsigned long lifetime = 0); /// /// Creates a MOPixel to be identical to another, by deep copy. /// /// A reference to the MOPixel to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const MOPixel &reference); + int Create(const MOPixel& reference); #pragma endregion #pragma region Destruction /// /// Destructor method used to clean up a MOPixel object before deletion from system memory. /// - ~MOPixel() override { Destroy(true); } + ~MOPixel() override { Destroy(true); } /// /// Destroys and resets (through Clear()) the MOPixel object. @@ -76,7 +78,10 @@ namespace RTE { /// /// Resets the entire MOPixel, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); MovableObject::Reset(); } + void Reset() override { + Clear(); + MovableObject::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -90,19 +95,19 @@ namespace RTE { /// Gets the main Material of this MOPixel. /// /// The Material of this MOPixel. - const Material * GetMaterial() const override; + const Material* GetMaterial() const override; /// /// Gets the current Atom of this MOPixel. /// /// A const reference to the current Atom. - const Atom * GetAtom() const { return m_Atom; } + const Atom* GetAtom() const { return m_Atom; } /// /// Replaces the current Atom of this MOPixel with a new one. /// /// A reference to the new Atom. Ownership IS transferred! - void SetAtom(Atom *newAtom); + void SetAtom(Atom* newAtom); /// /// Gets the color of this MOPixel. @@ -141,13 +146,13 @@ namespace RTE { void SetTrailLength(int trailLength); /// - /// Gets this MOPixel's staininess, which defines how likely a pixel is to stain a surface when it collides with it. + /// Gets this MOPixel's staininess, which defines how likely a pixel is to stain a surface when it collides with it. /// /// This MOPixel's current staininess value. float GetStaininess() const { return m_Staininess; } /// - /// Sets this MOPixel's staininess, which defines how likely a pixel is to stain a surface when it collides with it. + /// Sets this MOPixel's staininess, which defines how likely a pixel is to stain a surface when it collides with it. /// /// The new staininess value. void SetStaininess(float staininess) { m_Staininess = staininess; } @@ -173,7 +178,7 @@ namespace RTE { /// /// Reference to the HitData struct which describes the collision. This will be modified to represent the results of the collision. /// Whether the collision has been deemed valid. If false, then disregard any impulses in the HitData. - bool CollideAtPoint(HitData &hitData) override; + bool CollideAtPoint(HitData& hitData) override; /// /// Does the calculations necessary to detect whether this MOPixel is at rest or not. IsAtRest() retrieves the answer. @@ -185,14 +190,14 @@ namespace RTE { /// /// The HitData describing the collision in detail. /// Whether the MOPixel should immediately halt any travel going on after this bounce. - bool OnBounce(HitData &hd) override { return false; } + bool OnBounce(HitData& hd) override { return false; } /// /// Defines what should happen when this MOPixel hits and then sink into something. This is called by the owned Atom/AtomGroup of this MOPixel during travel. /// /// The HitData describing the collision in detail. /// Whether the MOPixel should immediately halt any travel going on after this sinkage. - bool OnSink(HitData &hd) override { return false; } + bool OnSink(HitData& hd) override { return false; } /// /// Updates this MOPixel. Supposed to be done every frame. @@ -206,15 +211,14 @@ namespace RTE { /// The absolute position of the target bitmap's upper left corner in the Scene. /// In which mode to draw in. See the DrawMode enumeration for the modes. /// Whether to not draw any extra 'ghost' items of this MOPixel, indicator arrows or hovering HUD text and so on. - void Draw(BITMAP *targetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. - Atom *m_Atom; //!< The single Atom that is responsible for collisions of this MOPixel. - Color m_Color; //!< Color representation of this MOPixel. + Atom* m_Atom; //!< The single Atom that is responsible for collisions of this MOPixel. + Color m_Color; //!< Color representation of this MOPixel. float m_LethalRange; //!< After this distance in meters, the MO has a chance to no longer hit MOs, and its Lifetime decreases. Defaults to the length of a player's screen. float m_MinLethalRange; //!< Lower bound multiplier for setting LethalRange at random. By default, 1.0 equals one screen. @@ -223,7 +227,6 @@ namespace RTE { float m_Staininess; //!< How likely a pixel is to stain a surface when it collides with it. Defaults to 0 (never stain). private: - /// /// Sets the screen effect to draw at the final post-processing stage. /// @@ -234,5 +237,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/MOSParticle.cpp b/Source/Entities/MOSParticle.cpp index 7ab18f68a7..16bf673041 100644 --- a/Source/Entities/MOSParticle.cpp +++ b/Source/Entities/MOSParticle.cpp @@ -7,26 +7,28 @@ namespace RTE { ConcreteClassInfo(MOSParticle, MovableObject, 1000); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOSParticle::Clear() { m_Atom = nullptr; m_SpriteAnimMode = OVERLIFETIME; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int MOSParticle::Create() { if (MOSprite::Create() < 0) { return -1; } - if (!m_Atom) { m_Atom = new Atom(); } + if (!m_Atom) { + m_Atom = new Atom(); + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSParticle::Create(const MOSParticle &reference) { + int MOSParticle::Create(const MOSParticle& reference) { MOSprite::Create(reference); m_Atom = new Atom(*(reference.m_Atom)); @@ -35,23 +37,25 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSParticle::ReadProperty(const std::string_view &propName, Reader &reader) { + int MOSParticle::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MOSprite::ReadProperty(propName, reader)); - + MatchProperty("Atom", { - if (!m_Atom) { m_Atom = new Atom; } + if (!m_Atom) { + m_Atom = new Atom; + } reader >> *m_Atom; m_Atom->SetOwner(this); - }); - + }); + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSParticle::Save(Writer &writer) const { + int MOSParticle::Save(Writer& writer) const { MOSprite::Save(writer); // TODO: Make proper save system that knows not to save redundant data! @@ -63,32 +67,34 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOSParticle::Destroy(bool notInherited) { delete m_Atom; - if (!notInherited) { MOSprite::Destroy(); } + if (!notInherited) { + MOSprite::Destroy(); + } Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int MOSParticle::GetDrawPriority() const { return m_Atom->GetMaterial()->GetPriority(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Material * MOSParticle::GetMaterial() const { return m_Atom->GetMaterial(); } + const Material* MOSParticle::GetMaterial() const { return m_Atom->GetMaterial(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::SetAtom(Atom *newAtom) { + void MOSParticle::SetAtom(Atom* newAtom) { delete m_Atom; m_Atom = newAtom; m_Atom->SetOwner(this); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOSParticle::RestDetection() { MOSprite::RestDetection(); @@ -101,7 +107,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOSParticle::Travel() { MOSprite::Travel(); @@ -117,14 +123,14 @@ namespace RTE { if (m_HitsMOs && m_pMOToNotHit && g_MovableMan.ValidMO(m_pMOToNotHit) && !m_MOIgnoreTimer.IsPastSimTimeLimit()) { std::vector MOIDsNotToHit; m_pMOToNotHit->GetMOIDs(MOIDsNotToHit); - for (const MOID &MOIDNotToHit : MOIDsNotToHit) { + for (const MOID& MOIDNotToHit: MOIDsNotToHit) { m_Atom->AddMOIDToIgnore(MOIDNotToHit); } } // Do static particle bounce calculations. int hitCount = 0; - if (!IsTooFast()) { - m_Atom->Travel(g_TimerMan.GetDeltaTimeSecs(), true, g_SceneMan.SceneIsLocked()); + if (!IsTooFast()) { + m_Atom->Travel(g_TimerMan.GetDeltaTimeSecs(), true, g_SceneMan.SceneIsLocked()); } m_Atom->ClearMOIDIgnoreList(); @@ -145,21 +151,25 @@ namespace RTE { m_Frame = std::floor(newFrame); m_Rotation += m_AngularVel * deltaTime; - if (m_Frame >= m_FrameCount) { m_Frame = m_FrameCount - 1; } + if (m_Frame >= m_FrameCount) { + m_Frame = m_FrameCount - 1; + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOSParticle::Update() { MOSprite::Update(); - if (m_pScreenEffect) { SetPostScreenEffectToDraw(); } + if (m_pScreenEffect) { + SetPostScreenEffectToDraw(); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::Draw(BITMAP *targetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { + void MOSParticle::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { RTEAssert(!m_aSprite.empty(), "No sprite bitmaps loaded to draw " + GetPresetName()); RTEAssert(m_Frame >= 0 && m_Frame < m_FrameCount, "Frame is out of bounds for " + GetPresetName()); @@ -169,8 +179,8 @@ namespace RTE { Vector spritePos(m_Pos + m_SpriteOffset - targetPos); - //TODO I think this is an array with 4 elements to account for Y wrapping. Y wrapping is not really handled in this game, so this can probably be knocked down to 2 elements. Also, I'm sure this code can be simplified. - std::array drawPositions = { spritePos }; + // TODO I think this is an array with 4 elements to account for Y wrapping. Y wrapping is not really handled in this game, so this can probably be knocked down to 2 elements. Also, I'm sure this code can be simplified. + std::array drawPositions = {spritePos}; int drawPasses = 1; if (g_SceneMan.SceneWrapsX()) { if (targetPos.IsZero() && m_WrapDoubleDraw) { @@ -231,7 +241,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MOSParticle::SetPostScreenEffectToDraw() const { if (m_AgeTimer.GetElapsedSimTimeMS() >= m_EffectStartTime && (m_EffectStopTime == 0 || !m_AgeTimer.IsPastSimMS(m_EffectStopTime))) { @@ -240,4 +250,4 @@ namespace RTE { } } } -} +} // namespace RTE diff --git a/Source/Entities/MOSParticle.h b/Source/Entities/MOSParticle.h index 3d40c7dbbe..590c9024ca 100644 --- a/Source/Entities/MOSParticle.h +++ b/Source/Entities/MOSParticle.h @@ -13,7 +13,6 @@ namespace RTE { class MOSParticle : public MOSprite { public: - EntityAllocation(MOSParticle); ClassInfoGetters; SerializableOverrideMethods; @@ -40,7 +39,7 @@ namespace RTE { /// A Vector specifying the initial velocity. /// The amount of time in ms this MOSParticle will exist. 0 means unlimited. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(ContentFile spriteFile, const int frameCount = 1, const float mass = 1, const Vector &position = Vector(0, 0), const Vector &velocity = Vector(0, 0), const unsigned long lifetime = 0) { + int Create(ContentFile spriteFile, const int frameCount = 1, const float mass = 1, const Vector& position = Vector(0, 0), const Vector& velocity = Vector(0, 0), const unsigned long lifetime = 0) { MOSprite::Create(spriteFile, frameCount, mass, position, velocity, lifetime); return 0; } @@ -50,7 +49,7 @@ namespace RTE { /// /// A reference to the MOSParticle to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const MOSParticle &reference); + int Create(const MOSParticle& reference); #pragma endregion #pragma region Destruction @@ -68,7 +67,10 @@ namespace RTE { /// /// Resets the entire MOSParticle, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); MOSprite::Reset(); } + void Reset() override { + Clear(); + MOSprite::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -82,19 +84,19 @@ namespace RTE { /// Gets the main material of this MOSParticle. /// /// The material of this MOSParticle. - const Material * GetMaterial() const override; + const Material* GetMaterial() const override; /// /// Gets the current Atom of this MOSParticle. /// /// A const reference to the current Atom. - const Atom * GetAtom() const { return m_Atom; } + const Atom* GetAtom() const { return m_Atom; } /// /// Replaces the current Atom of this MOSParticle with a new one. /// /// A reference to the new Atom. - void SetAtom(Atom *newAtom); + void SetAtom(Atom* newAtom); #pragma endregion #pragma region Virtual Override Methods @@ -104,12 +106,12 @@ namespace RTE { void Travel() override; /// - /// Calculates the collision response when another MO's Atom collides with this MO's physical representation. + /// Calculates the collision response when another MO's Atom collides with this MO's physical representation. /// The effects will be applied directly to this MO, and also represented in the passed in HitData. /// /// Reference to the HitData struct which describes the collision. This will be modified to represent the results of the collision. /// Whether the collision has been deemed valid. If false, then disregard any impulses in the HitData. - bool CollideAtPoint(HitData &hitData) override { return true; } + bool CollideAtPoint(HitData& hitData) override { return true; } /// /// Does the calculations necessary to detect whether this MOSParticle is at rest or not. IsAtRest() retrieves the answer. @@ -121,14 +123,14 @@ namespace RTE { /// /// The HitData describing the collision in detail. /// Whether the MOSParticle should immediately halt any travel going on after this bounce. - bool OnBounce(HitData &hd) override { return false; } + bool OnBounce(HitData& hd) override { return false; } /// /// Defines what should happen when this MOSParticle hits and then sink into something. This is called by the owned Atom/AtomGroup of this MOSParticle during travel. /// /// The HitData describing the collision in detail. /// Whether the MOSParticle should immediately halt any travel going on after this sinkage. - bool OnSink(HitData &hd) override { return false; } + bool OnSink(HitData& hd) override { return false; } /// /// Updates this MOParticle. Supposed to be done every frame. @@ -142,18 +144,16 @@ namespace RTE { /// The absolute position of the target bitmap's upper left corner in the Scene. /// In which mode to draw in. See the DrawMode enumeration for the modes. /// Whether to not draw any extra 'ghost' items of this MOSParticle, indicator arrows or hovering HUD text and so on. - void Draw(BITMAP *targetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. - Atom *m_Atom; //!< The Atom that will be the physical representation of this MOSParticle. + Atom* m_Atom; //!< The Atom that will be the physical representation of this MOSParticle. float m_TimeRest; //!< Accumulated time in seconds that did not cause a frame change. private: - /// /// Sets the screen effect to draw at the final post-processing stage. /// @@ -165,8 +165,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - MOSParticle(const MOSParticle &reference) = delete; - MOSParticle & operator=(const MOSParticle &rhs) = delete; + MOSParticle(const MOSParticle& reference) = delete; + MOSParticle& operator=(const MOSParticle& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/MOSRotating.cpp b/Source/Entities/MOSRotating.cpp index f67f999f52..2521358760 100644 --- a/Source/Entities/MOSRotating.cpp +++ b/Source/Entities/MOSRotating.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -28,2069 +27,2071 @@ namespace RTE { -ConcreteClassInfo(MOSRotating, MOSprite, 500); + ConcreteClassInfo(MOSRotating, MOSprite, 500); + + BITMAP* MOSRotating::m_spTempBitmap16 = 0; + BITMAP* MOSRotating::m_spTempBitmap32 = 0; + BITMAP* MOSRotating::m_spTempBitmap64 = 0; + BITMAP* MOSRotating::m_spTempBitmap128 = 0; + BITMAP* MOSRotating::m_spTempBitmap256 = 0; + BITMAP* MOSRotating::m_spTempBitmap512 = 0; + + BITMAP* MOSRotating::m_spTempBitmapS16 = 0; + BITMAP* MOSRotating::m_spTempBitmapS32 = 0; + BITMAP* MOSRotating::m_spTempBitmapS64 = 0; + BITMAP* MOSRotating::m_spTempBitmapS128 = 0; + BITMAP* MOSRotating::m_spTempBitmapS256 = 0; + BITMAP* MOSRotating::m_spTempBitmapS512 = 0; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MOSRotating, effectively + // resetting the members of this abstraction level only. + + void MOSRotating::Clear() { + m_pAtomGroup = 0; + m_pDeepGroup = 0; + m_DeepCheck = false; + m_ForceDeepCheck = false; + m_DeepHardness = 0; + m_TravelImpulse.Reset(); + m_SpriteCenter.Reset(); + m_OrientToVel = 0; + m_Recoiled = false; + m_RecoilForce.Reset(); + m_RecoilOffset.Reset(); + m_Wounds.clear(); + m_Attachables.clear(); + m_ReferenceHardcodedAttachableUniqueIDs.clear(); + m_HardcodedAttachableUniqueIDsAndSetters.clear(); + m_HardcodedAttachableUniqueIDsAndRemovers.clear(); + m_RadiusAffectingAttachable = nullptr; + m_FarthestAttachableDistanceAndRadius = 0.0F; + m_AttachableAndWoundMass = 0.0F; + m_Gibs.clear(); + m_GibImpulseLimit = 0; + m_GibWoundLimit = 0; + m_GibBlastStrength = 10.0F; + m_GibScreenShakeAmount = -1.0F; + m_WoundCountAffectsImpulseLimitRatio = 0.25F; + m_DetachAttachablesBeforeGibbingFromWounds = true; + m_GibAtEndOfLifetime = false; + m_GibSound = nullptr; + m_EffectOnGib = true; + m_pFlipBitmap = 0; + m_pFlipBitmapS = 0; + m_pTempBitmap = 0; + m_pTempBitmapS = 0; + m_LoudnessOnGib = 1; + m_DamageMultiplier = 0; + m_NoSetDamageMultiplier = true; + m_FlashWhiteTimer.Reset(); + m_FlashWhiteTimer.SetRealTimeLimitMS(0); + } -BITMAP * MOSRotating::m_spTempBitmap16 = 0; -BITMAP * MOSRotating::m_spTempBitmap32 = 0; -BITMAP * MOSRotating::m_spTempBitmap64 = 0; -BITMAP * MOSRotating::m_spTempBitmap128 = 0; -BITMAP * MOSRotating::m_spTempBitmap256 = 0; -BITMAP * MOSRotating::m_spTempBitmap512 = 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MOSParticle object ready for use. -BITMAP * MOSRotating::m_spTempBitmapS16 = 0; -BITMAP * MOSRotating::m_spTempBitmapS32 = 0; -BITMAP * MOSRotating::m_spTempBitmapS64 = 0; -BITMAP * MOSRotating::m_spTempBitmapS128 = 0; -BITMAP * MOSRotating::m_spTempBitmapS256 = 0; -BITMAP * MOSRotating::m_spTempBitmapS512 = 0; + int MOSRotating::Create() { + if (MOSprite::Create() < 0) + return -1; + if (!m_pAtomGroup) { + RTEAbort("Encountered empty AtomGroup while trying to create preset \"" + this->GetPresetName() + "\"!\nAtomGroups must be defined for MOSRotating based presets!\n\nError happened " + this->GetFormattedReaderPosition() + "!"); + } else { + if (m_pAtomGroup->AutoGenerate() /* && m_pAtomGroup->GetAtomCount() == 0*/) { + m_pAtomGroup->Create(this); + } else { + m_pAtomGroup->SetOwner(this); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MOSRotating, effectively -// resetting the members of this abstraction level only. - -void MOSRotating::Clear() -{ - m_pAtomGroup = 0; - m_pDeepGroup = 0; - m_DeepCheck = false; - m_ForceDeepCheck = false; - m_DeepHardness = 0; - m_TravelImpulse.Reset(); - m_SpriteCenter.Reset(); - m_OrientToVel = 0; - m_Recoiled = false; - m_RecoilForce.Reset(); - m_RecoilOffset.Reset(); - m_Wounds.clear(); - m_Attachables.clear(); - m_ReferenceHardcodedAttachableUniqueIDs.clear(); - m_HardcodedAttachableUniqueIDsAndSetters.clear(); - m_HardcodedAttachableUniqueIDsAndRemovers.clear(); - m_RadiusAffectingAttachable = nullptr; - m_FarthestAttachableDistanceAndRadius = 0.0F; - m_AttachableAndWoundMass = 0.0F; - m_Gibs.clear(); - m_GibImpulseLimit = 0; - m_GibWoundLimit = 0; - m_GibBlastStrength = 10.0F; - m_GibScreenShakeAmount = -1.0F; - m_WoundCountAffectsImpulseLimitRatio = 0.25F; - m_DetachAttachablesBeforeGibbingFromWounds = true; - m_GibAtEndOfLifetime = false; - m_GibSound = nullptr; - m_EffectOnGib = true; - m_pFlipBitmap = 0; - m_pFlipBitmapS = 0; - m_pTempBitmap = 0; - m_pTempBitmapS = 0; - m_LoudnessOnGib = 1; - m_DamageMultiplier = 0; - m_NoSetDamageMultiplier = true; - m_FlashWhiteTimer.Reset(); - m_FlashWhiteTimer.SetRealTimeLimitMS(0); -} + if (m_pDeepGroup && m_pDeepGroup->AutoGenerate() /* && m_pDeepGroup->GetAtomCount() == 0*/) + m_pDeepGroup->Create(this); + else if (m_pDeepGroup) + m_pDeepGroup->SetOwner(this); + m_SpriteCenter.SetXY(m_aSprite[m_Frame]->w / 2, m_aSprite[m_Frame]->h / 2); + m_SpriteCenter += m_SpriteOffset; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MOSParticle object ready for use. - -int MOSRotating::Create() -{ - if (MOSprite::Create() < 0) - return -1; - - if (!m_pAtomGroup) { - RTEAbort("Encountered empty AtomGroup while trying to create preset \"" + this->GetPresetName() + "\"!\nAtomGroups must be defined for MOSRotating based presets!\n\nError happened " + this->GetFormattedReaderPosition() + "!"); - } else { - if (m_pAtomGroup->AutoGenerate() /* && m_pAtomGroup->GetAtomCount() == 0*/) { - m_pAtomGroup->Create(this); + if (!m_pFlipBitmap && m_aSprite[0]) { + m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); + } + if (!m_pFlipBitmapS && m_aSprite[0]) { + m_pFlipBitmapS = create_bitmap_ex(c_MOIDLayerBitDepth, m_aSprite[0]->w, m_aSprite[0]->h); + } + + /* Not anymore; points to shared static bitmaps + if (!m_pTempBitmap && m_aSprite[0]) + m_pTempBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); + */ + + // Can't create these earlier in the static declaration because allegro_init needs to be called before create_bitmap + if (!m_spTempBitmap16) + m_spTempBitmap16 = create_bitmap_ex(8, 16, 16); + if (!m_spTempBitmap32) + m_spTempBitmap32 = create_bitmap_ex(8, 32, 32); + if (!m_spTempBitmap64) + m_spTempBitmap64 = create_bitmap_ex(8, 64, 64); + if (!m_spTempBitmap128) + m_spTempBitmap128 = create_bitmap_ex(8, 128, 128); + if (!m_spTempBitmap256) + m_spTempBitmap256 = create_bitmap_ex(8, 256, 256); + if (!m_spTempBitmap512) + m_spTempBitmap512 = create_bitmap_ex(8, 512, 512); + + // Can't create these earlier in the static declaration because allegro_init needs to be called before create_bitmap + if (!m_spTempBitmapS16) + m_spTempBitmapS16 = create_bitmap_ex(c_MOIDLayerBitDepth, 16, 16); + if (!m_spTempBitmapS32) + m_spTempBitmapS32 = create_bitmap_ex(c_MOIDLayerBitDepth, 32, 32); + if (!m_spTempBitmapS64) + m_spTempBitmapS64 = create_bitmap_ex(c_MOIDLayerBitDepth, 64, 64); + if (!m_spTempBitmapS128) + m_spTempBitmapS128 = create_bitmap_ex(c_MOIDLayerBitDepth, 128, 128); + if (!m_spTempBitmapS256) + m_spTempBitmapS256 = create_bitmap_ex(c_MOIDLayerBitDepth, 256, 256); + if (!m_spTempBitmapS512) + m_spTempBitmapS512 = create_bitmap_ex(c_MOIDLayerBitDepth, 512, 512); + + // Choose an appropriate size for this' diameter + if (m_SpriteDiameter >= 256) { + m_pTempBitmap = m_spTempBitmap512; + m_pTempBitmapS = m_spTempBitmapS512; + } else if (m_SpriteDiameter >= 128) { + m_pTempBitmap = m_spTempBitmap256; + m_pTempBitmapS = m_spTempBitmapS256; + } else if (m_SpriteDiameter >= 64) { + m_pTempBitmap = m_spTempBitmap128; + m_pTempBitmapS = m_spTempBitmapS128; + } else if (m_SpriteDiameter >= 32) { + m_pTempBitmap = m_spTempBitmap64; + m_pTempBitmapS = m_spTempBitmapS64; + } else if (m_SpriteDiameter >= 16) { + m_pTempBitmap = m_spTempBitmap32; + m_pTempBitmapS = m_spTempBitmapS32; } else { - m_pAtomGroup->SetOwner(this); + m_pTempBitmap = m_spTempBitmap16; + m_pTempBitmapS = m_spTempBitmapS16; } - } - if (m_pDeepGroup && m_pDeepGroup->AutoGenerate()/* && m_pDeepGroup->GetAtomCount() == 0*/) - m_pDeepGroup->Create(this); - else if (m_pDeepGroup) - m_pDeepGroup->SetOwner(this); + return 0; + } - m_SpriteCenter.SetXY(m_aSprite[m_Frame]->w / 2, m_aSprite[m_Frame]->h / 2); - m_SpriteCenter += m_SpriteOffset; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MOSParticle object ready for use. + + int MOSRotating::Create(ContentFile spriteFile, + const int frameCount, + const float mass, + const Vector& position, + const Vector& velocity, + const unsigned long lifetime) { + MOSprite::Create(spriteFile, frameCount, mass, position, velocity, lifetime); + + if (!m_pFlipBitmap && m_aSprite[0]) { + m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); + } + if (!m_pFlipBitmapS && m_aSprite[0]) { + m_pFlipBitmapS = create_bitmap_ex(c_MOIDLayerBitDepth, m_aSprite[0]->w, m_aSprite[0]->h); + } - if (!m_pFlipBitmap && m_aSprite[0]) { - m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); - } - if (!m_pFlipBitmapS && m_aSprite[0]) { - m_pFlipBitmapS = create_bitmap_ex(c_MOIDLayerBitDepth, m_aSprite[0]->w, m_aSprite[0]->h); + return 0; } -/* Not anymore; points to shared static bitmaps - if (!m_pTempBitmap && m_aSprite[0]) - m_pTempBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); -*/ - - // Can't create these earlier in the static declaration because allegro_init needs to be called before create_bitmap - if (!m_spTempBitmap16) - m_spTempBitmap16 = create_bitmap_ex(8, 16, 16); - if (!m_spTempBitmap32) - m_spTempBitmap32 = create_bitmap_ex(8, 32, 32); - if (!m_spTempBitmap64) - m_spTempBitmap64 = create_bitmap_ex(8, 64, 64); - if (!m_spTempBitmap128) - m_spTempBitmap128 = create_bitmap_ex(8, 128, 128); - if (!m_spTempBitmap256) - m_spTempBitmap256 = create_bitmap_ex(8, 256, 256); - if (!m_spTempBitmap512) - m_spTempBitmap512 = create_bitmap_ex(8, 512, 512); - - // Can't create these earlier in the static declaration because allegro_init needs to be called before create_bitmap - if (!m_spTempBitmapS16) - m_spTempBitmapS16 = create_bitmap_ex(c_MOIDLayerBitDepth, 16, 16); - if (!m_spTempBitmapS32) - m_spTempBitmapS32 = create_bitmap_ex(c_MOIDLayerBitDepth, 32, 32); - if (!m_spTempBitmapS64) - m_spTempBitmapS64 = create_bitmap_ex(c_MOIDLayerBitDepth, 64, 64); - if (!m_spTempBitmapS128) - m_spTempBitmapS128 = create_bitmap_ex(c_MOIDLayerBitDepth, 128, 128); - if (!m_spTempBitmapS256) - m_spTempBitmapS256 = create_bitmap_ex(c_MOIDLayerBitDepth, 256, 256); - if (!m_spTempBitmapS512) - m_spTempBitmapS512 = create_bitmap_ex(c_MOIDLayerBitDepth, 512, 512); - - // Choose an appropriate size for this' diameter - if (m_SpriteDiameter >= 256) - { - m_pTempBitmap = m_spTempBitmap512; - m_pTempBitmapS = m_spTempBitmapS512; - } - else if (m_SpriteDiameter >= 128) - { - m_pTempBitmap = m_spTempBitmap256; - m_pTempBitmapS = m_spTempBitmapS256; - } - else if (m_SpriteDiameter >= 64) - { - m_pTempBitmap = m_spTempBitmap128; - m_pTempBitmapS = m_spTempBitmapS128; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MOSRotating to be identical to another, by deep copy. + + int MOSRotating::Create(const MOSRotating& reference) { + MOSprite::Create(reference); + + if (!reference.m_pAtomGroup) { + return -1; + } + + // THESE ATOMGROUP COPYING ARE A TIME SINK! + m_pAtomGroup = new AtomGroup(); + m_pAtomGroup->Create(*reference.m_pAtomGroup, true); + if (m_pAtomGroup) { + m_pAtomGroup->SetOwner(this); + } + + if (reference.m_pDeepGroup) { + m_pDeepGroup = dynamic_cast(reference.m_pDeepGroup->Clone()); + if (m_pDeepGroup) { + m_pDeepGroup->SetOwner(this); + } + } + + m_DeepCheck = reference.m_DeepCheck; + m_SpriteCenter = reference.m_SpriteCenter; + m_OrientToVel = reference.m_OrientToVel; + + m_Recoiled = reference.m_Recoiled; + m_RecoilForce = reference.m_RecoilForce; + m_RecoilOffset = reference.m_RecoilOffset; + + for (const AEmitter* wound: reference.m_Wounds) { + AddWound(dynamic_cast(wound->Clone()), wound->GetParentOffset(), false); + } + + // TODO This could probably be replaced with just using HardcodedAttachableUniqueIDs entirely. At this point in, these lists should have the same UIDs in them, so it should work. + for (const Attachable* referenceAttachable: reference.m_Attachables) { + if (m_ReferenceHardcodedAttachableUniqueIDs.find(referenceAttachable->GetUniqueID()) == m_ReferenceHardcodedAttachableUniqueIDs.end()) { + AddAttachable(dynamic_cast(referenceAttachable->Clone())); + } + } + m_ReferenceHardcodedAttachableUniqueIDs.clear(); + + for (const Gib& gib: reference.m_Gibs) { + m_Gibs.push_back(gib); + } + + m_GibImpulseLimit = reference.m_GibImpulseLimit; + m_GibWoundLimit = reference.m_GibWoundLimit; + m_GibBlastStrength = reference.m_GibBlastStrength; + m_GibScreenShakeAmount = reference.m_GibScreenShakeAmount; + m_WoundCountAffectsImpulseLimitRatio = reference.m_WoundCountAffectsImpulseLimitRatio; + m_DetachAttachablesBeforeGibbingFromWounds = reference.m_DetachAttachablesBeforeGibbingFromWounds; + m_GibAtEndOfLifetime = reference.m_GibAtEndOfLifetime; + if (reference.m_GibSound) { + m_GibSound = dynamic_cast(reference.m_GibSound->Clone()); + } + m_EffectOnGib = reference.m_EffectOnGib; + m_LoudnessOnGib = reference.m_LoudnessOnGib; + + m_DamageMultiplier = reference.m_DamageMultiplier; + m_NoSetDamageMultiplier = reference.m_NoSetDamageMultiplier; + + m_pTempBitmap = reference.m_pTempBitmap; + m_pTempBitmapS = reference.m_pTempBitmapS; + + if (!m_pFlipBitmap && m_aSprite[0]) { + m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); + } + if (!m_pFlipBitmapS && m_aSprite[0]) { + m_pFlipBitmapS = create_bitmap_ex(c_MOIDLayerBitDepth, m_aSprite[0]->w, m_aSprite[0]->h); + } + + return 0; } - else if (m_SpriteDiameter >= 32) - { - m_pTempBitmap = m_spTempBitmap64; - m_pTempBitmapS = m_spTempBitmapS64; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int MOSRotating::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return MOSprite::ReadProperty(propName, reader)); + + MatchProperty("AtomGroup", + { + delete m_pAtomGroup; + m_pAtomGroup = new AtomGroup(); + reader >> *m_pAtomGroup; + }); + MatchProperty("DeepGroup", + { + delete m_pDeepGroup; + m_pDeepGroup = new AtomGroup(); + reader >> *m_pDeepGroup; + }); + MatchProperty("DeepCheck", { reader >> m_DeepCheck; }); + MatchProperty("OrientToVel", { reader >> m_OrientToVel; }); + MatchProperty("SpecialBehaviour_ClearAllAttachables", { + // This special property is used to make Attachables work with our limited serialization system, when saving the game. Note that we discard the property value here, because all that matters is whether or not we have the property. + reader.ReadPropValue(); + for (auto attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end();) { + Attachable* attachable = *attachableIterator; + ++attachableIterator; + delete RemoveAttachable(attachable); + } + }); + MatchForwards("AddAttachable") MatchForwards("AddAEmitter") MatchProperty("AddEmitter", { + Entity* readerEntity = g_PresetMan.ReadReflectedPreset(reader); + if (Attachable* readerAttachable = dynamic_cast(readerEntity)) { + AddAttachable(readerAttachable); + } else { + reader.ReportError("Tried to AddAttachable a non-Attachable type!"); + } + }); + MatchProperty("SpecialBehaviour_AddWound", { + AEmitter* wound = new AEmitter; + reader >> wound; + AddWound(wound, wound->GetParentOffset()); + }); + MatchProperty("AddGib", + { + Gib gib; + reader >> gib; + m_Gibs.push_back(gib); + }); + MatchProperty("GibImpulseLimit", { reader >> m_GibImpulseLimit; }); + MatchForwards("GibWoundLimit") MatchProperty("WoundLimit", { reader >> m_GibWoundLimit; }); + MatchProperty("GibBlastStrength", { reader >> m_GibBlastStrength; }); + MatchProperty("GibScreenShakeAmount", { reader >> m_GibScreenShakeAmount; }); + MatchProperty("WoundCountAffectsImpulseLimitRatio", { reader >> m_WoundCountAffectsImpulseLimitRatio; }); + MatchProperty("DetachAttachablesBeforeGibbingFromWounds", { reader >> m_DetachAttachablesBeforeGibbingFromWounds; }); + MatchProperty("GibAtEndOfLifetime", { reader >> m_GibAtEndOfLifetime; }); + MatchProperty("GibSound", { + if (!m_GibSound) { + m_GibSound = new SoundContainer; + } + reader >> m_GibSound; + }); + MatchProperty("EffectOnGib", { reader >> m_EffectOnGib; }); + MatchProperty("LoudnessOnGib", { reader >> m_LoudnessOnGib; }); + MatchProperty("DamageMultiplier", { + reader >> m_DamageMultiplier; + m_NoSetDamageMultiplier = false; + }); + + EndPropertyList; } - else if (m_SpriteDiameter >= 16) - { - m_pTempBitmap = m_spTempBitmap32; - m_pTempBitmapS = m_spTempBitmapS32; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this MOSRotating with a Writer for + // later recreation with Create(Reader &reader); + + int MOSRotating::Save(Writer& writer) const { + MOSprite::Save(writer); + + // TODO: Make proper save system that knows not to save redundant data! + /* + writer.NewProperty("AtomGroup"); + writer << m_pAtomGroup; + writer.NewProperty("DeepGroup"); + writer << m_pDeepGroup; + writer.NewProperty("DeepCheck"); + writer << m_DeepCheck; + writer.NewProperty("OrientToVel"); + writer << m_OrientToVel; + + for (auto itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) + { + writer.NewProperty("AddEmitter"); + writer << (*itr); + } + for (auto aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ++aItr) + { + writer.NewProperty("AddAttachable"); + writer << (*aItr); + } + */ + for (auto gItr = m_Gibs.begin(); gItr != m_Gibs.end(); ++gItr) { + writer.NewProperty("AddGib"); + writer << (*gItr); + } + /* + writer.NewProperty("GibImpulseLimit"); + writer << m_GibImpulseLimit; + writer.NewProperty("GibWoundLimit"); + writer << m_GibWoundLimit; + writer.NewPropertyWithValue("GibAtEndOfLifetime", m_GibAtEndOfLifetime); + writer.NewProperty("GibSound"); + writer << m_GibSound; + writer.NewProperty("EffectOnGib"); + writer << m_EffectOnGib; + */ + return 0; } - else - { - m_pTempBitmap = m_spTempBitmap16; - m_pTempBitmapS = m_spTempBitmapS16; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MOSRotating::GetGibWoundLimit(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const { + int gibWoundLimit = m_GibWoundLimit; + if (includePositiveDamageAttachables || includeNegativeDamageAttachables || includeNoDamageAttachables) { + for (const Attachable* attachable: m_Attachables) { + bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || + (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || + (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); + + if (attachableSatisfiesConditions) { + gibWoundLimit += attachable->GetGibWoundLimit(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); + } + } + } + return gibWoundLimit; } - return 0; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int MOSRotating::GetWoundCount(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const { + int woundCount = m_Wounds.size(); + if (includePositiveDamageAttachables || includeNegativeDamageAttachables || includeNoDamageAttachables) { + for (const Attachable* attachable: m_Attachables) { + bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || + (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || + (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MOSParticle object ready for use. - -int MOSRotating::Create(ContentFile spriteFile, - const int frameCount, - const float mass, - const Vector &position, - const Vector &velocity, - const unsigned long lifetime) -{ - MOSprite::Create(spriteFile, frameCount, mass, position, velocity, lifetime); - - if (!m_pFlipBitmap && m_aSprite[0]) { - m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); + if (attachableSatisfiesConditions) { + woundCount += attachable->GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); + } + } + } + return woundCount; } - if (!m_pFlipBitmapS && m_aSprite[0]) { - m_pFlipBitmapS = create_bitmap_ex(c_MOIDLayerBitDepth, m_aSprite[0]->w, m_aSprite[0]->h); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Attachable* MOSRotating::GetNearestDetachableAttachableToOffset(const Vector& offset) const { + Attachable* nearestAttachable = nullptr; + float closestRadius = m_SpriteRadius; + for (Attachable* attachable: m_Attachables) { + if (attachable->GetsHitByMOs() && attachable->GetGibImpulseLimit() > 0 && attachable->GetJointStrength() > 0 && attachable->GetDamageMultiplier() > 0 && offset.Dot(attachable->GetParentOffset()) > 0) { + float radius = (offset - attachable->GetParentOffset()).GetMagnitude(); + if (radius < closestRadius) { + closestRadius = radius; + nearestAttachable = attachable; + } + } + } + return nearestAttachable; } - return 0; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MOSRotating::DetachAttachablesFromImpulse(Vector& impulseVector) { + float impulseRemainder = impulseVector.GetMagnitude(); + // Find the attachable closest to the impact point by using an inverted impulse vector. + Vector invertedImpulseOffset = Vector(impulseVector.GetX(), impulseVector.GetY()).SetMagnitude(-GetRadius()) * -m_Rotation; + Attachable* nearestAttachableToImpulse = GetNearestDetachableAttachableToOffset(invertedImpulseOffset); + while (nearestAttachableToImpulse) { + float attachableImpulseLimit = nearestAttachableToImpulse->GetGibImpulseLimit(); + float attachableJointStrength = nearestAttachableToImpulse->GetJointStrength(); + if (impulseRemainder > attachableImpulseLimit) { + nearestAttachableToImpulse->GibThis(impulseVector.SetMagnitude(attachableImpulseLimit)); + impulseRemainder -= attachableImpulseLimit; + } else if (impulseRemainder > attachableJointStrength) { + RemoveAttachable(nearestAttachableToImpulse, true, true); + impulseRemainder -= attachableJointStrength; + } else { + break; + } + nearestAttachableToImpulse = GetNearestDetachableAttachableToOffset(invertedImpulseOffset); + } + impulseVector.SetMagnitude(impulseRemainder); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MOSRotating to be identical to another, by deep copy. - -int MOSRotating::Create(const MOSRotating &reference) { - MOSprite::Create(reference); - - if (!reference.m_pAtomGroup) { - return -1; - } - - // THESE ATOMGROUP COPYING ARE A TIME SINK! - m_pAtomGroup = new AtomGroup(); - m_pAtomGroup->Create(*reference.m_pAtomGroup, true); - if (m_pAtomGroup) { m_pAtomGroup->SetOwner(this); } - - if (reference.m_pDeepGroup) { - m_pDeepGroup = dynamic_cast(reference.m_pDeepGroup->Clone()); - if (m_pDeepGroup) { m_pDeepGroup->SetOwner(this); } - } - - m_DeepCheck = reference.m_DeepCheck; - m_SpriteCenter = reference.m_SpriteCenter; - m_OrientToVel = reference.m_OrientToVel; - - m_Recoiled = reference.m_Recoiled; - m_RecoilForce = reference.m_RecoilForce; - m_RecoilOffset = reference.m_RecoilOffset; - - for (const AEmitter *wound : reference.m_Wounds) { - AddWound(dynamic_cast(wound->Clone()), wound->GetParentOffset(), false); - } - - //TODO This could probably be replaced with just using HardcodedAttachableUniqueIDs entirely. At this point in, these lists should have the same UIDs in them, so it should work. - for (const Attachable *referenceAttachable : reference.m_Attachables) { - if (m_ReferenceHardcodedAttachableUniqueIDs.find(referenceAttachable->GetUniqueID()) == m_ReferenceHardcodedAttachableUniqueIDs.end()) { - AddAttachable(dynamic_cast(referenceAttachable->Clone())); - } - } - m_ReferenceHardcodedAttachableUniqueIDs.clear(); - - for (const Gib &gib : reference.m_Gibs) { - m_Gibs.push_back(gib); - } - - m_GibImpulseLimit = reference.m_GibImpulseLimit; - m_GibWoundLimit = reference.m_GibWoundLimit; - m_GibBlastStrength = reference.m_GibBlastStrength; - m_GibScreenShakeAmount = reference.m_GibScreenShakeAmount; - m_WoundCountAffectsImpulseLimitRatio = reference.m_WoundCountAffectsImpulseLimitRatio; - m_DetachAttachablesBeforeGibbingFromWounds = reference.m_DetachAttachablesBeforeGibbingFromWounds; - m_GibAtEndOfLifetime = reference.m_GibAtEndOfLifetime; - if (reference.m_GibSound) { m_GibSound = dynamic_cast(reference.m_GibSound->Clone()); } - m_EffectOnGib = reference.m_EffectOnGib; - m_LoudnessOnGib = reference.m_LoudnessOnGib; - - m_DamageMultiplier = reference.m_DamageMultiplier; - m_NoSetDamageMultiplier = reference.m_NoSetDamageMultiplier; - - m_pTempBitmap = reference.m_pTempBitmap; - m_pTempBitmapS = reference.m_pTempBitmapS; - - if (!m_pFlipBitmap && m_aSprite[0]) { - m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); - } - if (!m_pFlipBitmapS && m_aSprite[0]) { - m_pFlipBitmapS = create_bitmap_ex(c_MOIDLayerBitDepth, m_aSprite[0]->w, m_aSprite[0]->h); + void MOSRotating::AddWound(AEmitter* woundToAdd, const Vector& parentOffsetToSet, bool checkGibWoundLimit) { + if (woundToAdd && !m_ToDelete) { + if (checkGibWoundLimit && m_GibWoundLimit > 0 && m_Wounds.size() + 1 >= m_GibWoundLimit) { + // Find and detach an attachable near the new wound before gibbing the object itself. TODO: Perhaps move this to Actor, since it's more relevant there? + if (Attachable* attachableToDetach = GetNearestDetachableAttachableToOffset(parentOffsetToSet); attachableToDetach && m_DetachAttachablesBeforeGibbingFromWounds) { + RemoveAttachable(attachableToDetach, true, true); + } else { + // TODO: Don't hardcode the blast strength! + GibThis(Vector(-5.0F, 0).RadRotate(woundToAdd->GetEmitAngle())); + woundToAdd->DestroyScriptState(); + delete woundToAdd; + return; + } + } + woundToAdd->SetCollidesWithTerrainWhileAttached(false); + woundToAdd->SetParentOffset(parentOffsetToSet); + woundToAdd->SetParent(this); + woundToAdd->SetIsWound(true); + if (woundToAdd->HasNoSetDamageMultiplier()) { + woundToAdd->SetDamageMultiplier(1.0F); + } + m_AttachableAndWoundMass += woundToAdd->GetMass(); + m_Wounds.push_back(woundToAdd); + } } - return 0; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float MOSRotating::RemoveWounds(int numberOfWoundsToRemove, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) { + float damage = 0; + int woundCount = GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int MOSRotating::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return MOSprite::ReadProperty(propName, reader)); - - MatchProperty("AtomGroup", - { - delete m_pAtomGroup; - m_pAtomGroup = new AtomGroup(); - reader >> *m_pAtomGroup; - }); - MatchProperty("DeepGroup", - { - delete m_pDeepGroup; - m_pDeepGroup = new AtomGroup(); - reader >> *m_pDeepGroup; - }); - MatchProperty("DeepCheck", { reader >> m_DeepCheck; }); - MatchProperty("OrientToVel", { reader >> m_OrientToVel; }); - MatchProperty("SpecialBehaviour_ClearAllAttachables", { - // This special property is used to make Attachables work with our limited serialization system, when saving the game. Note that we discard the property value here, because all that matters is whether or not we have the property. - reader.ReadPropValue(); - for (auto attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end(); ) { - Attachable *attachable = *attachableIterator; - ++attachableIterator; - delete RemoveAttachable(attachable); + std::vector> woundedParts; + if (woundCount > 0) { + woundedParts.push_back({this, woundCount}); } - }); - MatchForwards("AddAttachable") MatchForwards("AddAEmitter") MatchProperty("AddEmitter", { - Entity *readerEntity = g_PresetMan.ReadReflectedPreset(reader); - if (Attachable *readerAttachable = dynamic_cast(readerEntity)) { - AddAttachable(readerAttachable); - } else { - reader.ReportError("Tried to AddAttachable a non-Attachable type!"); - } - }); - MatchProperty("SpecialBehaviour_AddWound", { - AEmitter *wound = new AEmitter; - reader >> wound; - AddWound(wound, wound->GetParentOffset()); - }); - MatchProperty("AddGib", - { - Gib gib; - reader >> gib; - m_Gibs.push_back(gib); - }); - MatchProperty("GibImpulseLimit", { reader >> m_GibImpulseLimit; }); - MatchForwards("GibWoundLimit") MatchProperty("WoundLimit", { reader >> m_GibWoundLimit; }); - MatchProperty("GibBlastStrength", { reader >> m_GibBlastStrength; }); - MatchProperty("GibScreenShakeAmount", { reader >> m_GibScreenShakeAmount; }); - MatchProperty("WoundCountAffectsImpulseLimitRatio", { reader >> m_WoundCountAffectsImpulseLimitRatio; }); - MatchProperty("DetachAttachablesBeforeGibbingFromWounds", { reader >> m_DetachAttachablesBeforeGibbingFromWounds; }); - MatchProperty("GibAtEndOfLifetime", { reader >> m_GibAtEndOfLifetime; }); - MatchProperty("GibSound", { - if (!m_GibSound) { m_GibSound = new SoundContainer; } - reader >> m_GibSound; - }); - MatchProperty("EffectOnGib", { reader >> m_EffectOnGib; }); - MatchProperty("LoudnessOnGib", { reader >> m_LoudnessOnGib; }); - MatchProperty("DamageMultiplier", { - reader >> m_DamageMultiplier; - m_NoSetDamageMultiplier = false; - }); - - EndPropertyList; -} -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this MOSRotating with a Writer for -// later recreation with Create(Reader &reader); - -int MOSRotating::Save(Writer &writer) const -{ - MOSprite::Save(writer); - -// TODO: Make proper save system that knows not to save redundant data! -/* - writer.NewProperty("AtomGroup"); - writer << m_pAtomGroup; - writer.NewProperty("DeepGroup"); - writer << m_pDeepGroup; - writer.NewProperty("DeepCheck"); - writer << m_DeepCheck; - writer.NewProperty("OrientToVel"); - writer << m_OrientToVel; - - for (auto itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) - { - writer.NewProperty("AddEmitter"); - writer << (*itr); - } - for (auto aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ++aItr) - { - writer.NewProperty("AddAttachable"); - writer << (*aItr); - } -*/ - for (auto gItr = m_Gibs.begin(); gItr != m_Gibs.end(); ++gItr) - { - writer.NewProperty("AddGib"); - writer << (*gItr); - } -/* - writer.NewProperty("GibImpulseLimit"); - writer << m_GibImpulseLimit; - writer.NewProperty("GibWoundLimit"); - writer << m_GibWoundLimit; - writer.NewPropertyWithValue("GibAtEndOfLifetime", m_GibAtEndOfLifetime); - writer.NewProperty("GibSound"); - writer << m_GibSound; - writer.NewProperty("EffectOnGib"); - writer << m_EffectOnGib; -*/ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int MOSRotating::GetGibWoundLimit(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const { - int gibWoundLimit = m_GibWoundLimit; - if (includePositiveDamageAttachables || includeNegativeDamageAttachables || includeNoDamageAttachables) { - for (const Attachable *attachable : m_Attachables) { - bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || - (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || - (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); - - if (attachableSatisfiesConditions) { - gibWoundLimit += attachable->GetGibWoundLimit(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); - } - } - } - return gibWoundLimit; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int MOSRotating::GetWoundCount(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const { - int woundCount = m_Wounds.size(); - if (includePositiveDamageAttachables || includeNegativeDamageAttachables || includeNoDamageAttachables) { - for (const Attachable *attachable : m_Attachables) { - bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || - (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || - (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); - - if (attachableSatisfiesConditions) { - woundCount += attachable->GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); - } - } - } - return woundCount; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Attachable * MOSRotating::GetNearestDetachableAttachableToOffset(const Vector &offset) const { - Attachable *nearestAttachable = nullptr; - float closestRadius = m_SpriteRadius; - for (Attachable *attachable : m_Attachables) { - if (attachable->GetsHitByMOs() && attachable->GetGibImpulseLimit() > 0 && attachable->GetJointStrength() > 0 && attachable->GetDamageMultiplier() > 0 && offset.Dot(attachable->GetParentOffset()) > 0) { - float radius = (offset - attachable->GetParentOffset()).GetMagnitude(); - if (radius < closestRadius) { - closestRadius = radius; - nearestAttachable = attachable; + for (Attachable* attachable: m_Attachables) { + bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || + (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || + (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); + int attachableWoundCount = attachable->GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); + + if (attachableSatisfiesConditions && attachableWoundCount > 0) { + woundedParts.push_back({attachable, attachableWoundCount}); } } - } - return nearestAttachable; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MOSRotating::DetachAttachablesFromImpulse(Vector &impulseVector) { - float impulseRemainder = impulseVector.GetMagnitude(); - // Find the attachable closest to the impact point by using an inverted impulse vector. - Vector invertedImpulseOffset = Vector(impulseVector.GetX(), impulseVector.GetY()).SetMagnitude(-GetRadius()) * -m_Rotation; - Attachable *nearestAttachableToImpulse = GetNearestDetachableAttachableToOffset(invertedImpulseOffset); - while (nearestAttachableToImpulse) { - float attachableImpulseLimit = nearestAttachableToImpulse->GetGibImpulseLimit(); - float attachableJointStrength = nearestAttachableToImpulse->GetJointStrength(); - if (impulseRemainder > attachableImpulseLimit) { - nearestAttachableToImpulse->GibThis(impulseVector.SetMagnitude(attachableImpulseLimit)); - impulseRemainder -= attachableImpulseLimit; - } else if (impulseRemainder > attachableJointStrength) { - RemoveAttachable(nearestAttachableToImpulse, true, true); - impulseRemainder -= attachableJointStrength; - } else { - break; + + if (woundedParts.empty()) { + return damage; } - nearestAttachableToImpulse = GetNearestDetachableAttachableToOffset(invertedImpulseOffset); - } - impulseVector.SetMagnitude(impulseRemainder); -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// Internal lambda function to remove the first wound emitter from this MOSRotating. + /// + auto removeFirstWoundEmitter = [this]() { + if (m_Wounds.empty()) { + return 0.0F; + } + AEmitter* wound = m_Wounds.front(); + float woundDamage = wound->GetBurstDamage(); + m_AttachableAndWoundMass -= wound->GetMass(); + std::iter_swap(m_Wounds.begin(), m_Wounds.end() - 1); + m_Wounds.pop_back(); + wound->DestroyScriptState(); + delete wound; + return woundDamage; + }; + + for (int i = 0; i < numberOfWoundsToRemove; i++) { + if (woundedParts.empty()) { + break; + } -void MOSRotating::AddWound(AEmitter *woundToAdd, const Vector &parentOffsetToSet, bool checkGibWoundLimit) { - if (woundToAdd && !m_ToDelete) { - if (checkGibWoundLimit && m_GibWoundLimit > 0 && m_Wounds.size() + 1 >= m_GibWoundLimit) { - // Find and detach an attachable near the new wound before gibbing the object itself. TODO: Perhaps move this to Actor, since it's more relevant there? - if (Attachable *attachableToDetach = GetNearestDetachableAttachableToOffset(parentOffsetToSet); attachableToDetach && m_DetachAttachablesBeforeGibbingFromWounds) { - RemoveAttachable(attachableToDetach, true, true); + int woundedPartIndex = RandomNum(0, static_cast(woundedParts.size()) - 1); + MOSRotating* woundedPart = woundedParts[woundedPartIndex].first; + if (woundedPart == this) { + damage += removeFirstWoundEmitter() * GetDamageMultiplier(); } else { - // TODO: Don't hardcode the blast strength! - GibThis(Vector(-5.0F, 0).RadRotate(woundToAdd->GetEmitAngle())); - woundToAdd->DestroyScriptState(); - delete woundToAdd; - return; - } - } - woundToAdd->SetCollidesWithTerrainWhileAttached(false); - woundToAdd->SetParentOffset(parentOffsetToSet); - woundToAdd->SetParent(this); - woundToAdd->SetIsWound(true); - if (woundToAdd->HasNoSetDamageMultiplier()) { woundToAdd->SetDamageMultiplier(1.0F); } - m_AttachableAndWoundMass += woundToAdd->GetMass(); - m_Wounds.push_back(woundToAdd); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float MOSRotating::RemoveWounds(int numberOfWoundsToRemove, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) { - float damage = 0; - int woundCount = GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); - - std::vector> woundedParts; - if (woundCount > 0) { woundedParts.push_back({this, woundCount}); } - - for (Attachable *attachable : m_Attachables) { - bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || - (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || - (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); - int attachableWoundCount = attachable->GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); - - if (attachableSatisfiesConditions && attachableWoundCount > 0) { woundedParts.push_back({attachable, attachableWoundCount}); } - } - - if (woundedParts.empty()) { - return damage; - } - - /// - /// Internal lambda function to remove the first wound emitter from this MOSRotating. - /// - auto removeFirstWoundEmitter = [this]() { - if (m_Wounds.empty()) { - return 0.0F; - } - AEmitter *wound = m_Wounds.front(); - float woundDamage = wound->GetBurstDamage(); - m_AttachableAndWoundMass -= wound->GetMass(); - std::iter_swap(m_Wounds.begin(), m_Wounds.end() - 1); - m_Wounds.pop_back(); - wound->DestroyScriptState(); - delete wound; - return woundDamage; - }; - - for (int i = 0; i < numberOfWoundsToRemove; i++) { - if (woundedParts.empty()) { - break; - } - - int woundedPartIndex = RandomNum(0, static_cast(woundedParts.size()) - 1); - MOSRotating *woundedPart = woundedParts[woundedPartIndex].first; - if (woundedPart == this) { - damage += removeFirstWoundEmitter() * GetDamageMultiplier(); - } else { - //TODO This is less efficient than it should be. We already collected all wounded parts and their wounds above, we should pass that in (make another function overload) instead of collecting everything again. It might be wise to use a tree for this purpose. - damage += woundedPart->RemoveWounds(1, includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); - } - if (woundedParts[woundedPartIndex].second-- <= 0) { woundedParts.erase(woundedParts.begin() + woundedPartIndex); } - } - - return damage; -} - -void MOSRotating::DestroyScriptState() { - for (auto itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) { - (*itr)->DestroyScriptState(); - } - - for (auto itr = m_Attachables.begin(); itr != m_Attachables.end(); ++itr) { - (*itr)->DestroyScriptState(); - } - - MovableObject::DestroyScriptState(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // TODO This is less efficient than it should be. We already collected all wounded parts and their wounds above, we should pass that in (make another function overload) instead of collecting everything again. It might be wise to use a tree for this purpose. + damage += woundedPart->RemoveWounds(1, includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); + } + if (woundedParts[woundedPartIndex].second-- <= 0) { + woundedParts.erase(woundedParts.begin() + woundedPartIndex); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MOSRotating object. + return damage; + } -void MOSRotating::Destroy(bool notInherited) -{ - delete m_pAtomGroup; - delete m_pDeepGroup; + void MOSRotating::DestroyScriptState() { + for (auto itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) { + (*itr)->DestroyScriptState(); + } - for (auto itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) { - delete (*itr); - } + for (auto itr = m_Attachables.begin(); itr != m_Attachables.end(); ++itr) { + (*itr)->DestroyScriptState(); + } - for (auto aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ++aItr) { - if (m_HardcodedAttachableUniqueIDsAndRemovers.find((*aItr)->GetUniqueID()) == m_HardcodedAttachableUniqueIDsAndRemovers.end()) { - delete (*aItr); - } - } + MovableObject::DestroyScriptState(); + } - destroy_bitmap(m_pFlipBitmap); - destroy_bitmap(m_pFlipBitmapS); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Not anymore; point to shared static bitmaps -// destroy_bitmap(m_pTempBitmap); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the MOSRotating object. - delete m_GibSound; + void MOSRotating::Destroy(bool notInherited) { + delete m_pAtomGroup; + delete m_pDeepGroup; - if (!notInherited) - MOSprite::Destroy(); - Clear(); -} + for (auto itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) { + delete (*itr); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + for (auto aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ++aItr) { + if (m_HardcodedAttachableUniqueIDsAndRemovers.find((*aItr)->GetUniqueID()) == m_HardcodedAttachableUniqueIDsAndRemovers.end()) { + delete (*aItr); + } + } -void MOSRotating::SetAsNoID() { - MovableObject::SetAsNoID(); - for (Attachable *attachable : m_Attachables) { - attachable->SetAsNoID(); - } -} + destroy_bitmap(m_pFlipBitmap); + destroy_bitmap(m_pFlipBitmapS); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Not anymore; point to shared static bitmaps + // destroy_bitmap(m_pTempBitmap); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the main Material of this MOSRotating. + delete m_GibSound; -Material const * MOSRotating::GetMaterial() const -{ -// if (m_pAtomGroup) - return m_pAtomGroup->GetMaterial(); -// return 0; -} + if (!notInherited) + MOSprite::Destroy(); + Clear(); + } -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: HitsMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets whether this MovableObject is set to collide with other -// MovableObject:s during travel. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool MOSRotating::HitsMOs() const -{ - if (m_pAtomGroup) - return m_pAtomGroup->HitsMOs(); - return false; -} -*/ + void MOSRotating::SetAsNoID() { + MovableObject::SetAsNoID(); + for (Attachable* attachable: m_Attachables) { + attachable->SetAsNoID(); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetDrawPriority -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the drawing priority of this MovableObject, if two things were -// overlap when copying to the terrain, the higher priority MO would -// end up getting drawn. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int MOSRotating::GetDrawPriority() const -{ - return INT_MAX; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMaterial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the main Material of this MOSRotating. -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAtom -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Replaces the current AtomGroup of this MOSRotating with a new one. -// Arguments: A reference to the new AtomGroup. -// Return value: None. - -void MOSRotating::SetAtom(AtomGroup *newAtom) -{ - delete m_pAtomGroup; - m_pAtomGroup = newAtom; -} -*/ -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetToHitMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this MovableObject to collide with other MovableObjects during -// travel. + Material const* MOSRotating::GetMaterial() const { + // if (m_pAtomGroup) + return m_pAtomGroup->GetMaterial(); + // return 0; + } -void MOSRotating::SetToHitMOs(bool hitMOs) -{ - if (m_pAtomGroup) - m_pAtomGroup->SetToHitMOs(hitMOs); -} -*/ + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: HitsMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets whether this MovableObject is set to collide with other + // MovableObject:s during travel. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddRecoil -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds recoil effects to this MOSprite. + bool MOSRotating::HitsMOs() const + { + if (m_pAtomGroup) + return m_pAtomGroup->HitsMOs(); + return false; + } + */ -void MOSRotating::AddRecoil() -{ - m_RecoilOffset.SetXY(1, 0); - m_RecoilOffset.RadRotate(m_Rotation.GetRadAngle() + c_PI); - m_Recoiled = true; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetDrawPriority + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the drawing priority of this MovableObject, if two things were + // overlap when copying to the terrain, the higher priority MO would + // end up getting drawn. + int MOSRotating::GetDrawPriority() const { + return INT_MAX; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. - -bool MOSRotating::CollideAtPoint(HitData &hd) -{ - if (m_ToDelete) { - return false; // TODO: Add a settings flag to enable old school particle sponges! + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetAtom + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Replaces the current AtomGroup of this MOSRotating with a new one. + // Arguments: A reference to the new AtomGroup. + // Return value: None. + + void MOSRotating::SetAtom(AtomGroup *newAtom) + { + delete m_pAtomGroup; + m_pAtomGroup = newAtom; } + */ + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetToHitMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this MovableObject to collide with other MovableObjects during + // travel. + + void MOSRotating::SetToHitMOs(bool hitMOs) + { + if (m_pAtomGroup) + m_pAtomGroup->SetToHitMOs(hitMOs); + } + */ - hd.ResImpulse[HITOR].Reset(); - hd.ResImpulse[HITEE].Reset(); - -// if (m_AlreadyHitBy.find(hd.Body[HITOR]->GetID()) == m_AlreadyHitBy.end()) -// { -// m_AlreadyHitBy.insert(hd.Body[HITOR]->GetID()); - - hd.HitRadius[HITEE] = (hd.HitPoint - m_Pos) * c_MPP; -/* - // Cancel if both hitor and hitee's hitpoint radii are pointing int he same direction, meaning the objects are really tangled - if (!hd.HitRadius[HITOR].IsZero() && hd.HitRadius[HITOR].Dot(hd.HitRadius[HITEE]) >= 0) - return false; -*/ - hd.TotalMass[HITEE] = m_Mass; - hd.MomInertia[HITEE] = m_pAtomGroup->GetMomentOfInertia(); - hd.HitVel[HITEE] = m_Vel + hd.HitRadius[HITEE].GetPerpendicular() * m_AngularVel; - hd.VelDiff = hd.HitVel[HITOR] - hd.HitVel[HITEE]; - - // Only do collision response for this if it appears the collision is happening in the 'right' direction, meaning away from the hitee collision normal - // The wrong way happens when things are sunk into each other, and thus getting 'hooked' on each other - if (hd.VelDiff.Dot(hd.BitmapNormal) < 0) - { - Vector hitAcc = -hd.VelDiff * (1 + (hd.Body[HITOR]->GetMaterial()->GetRestitution() * GetMaterial()->GetRestitution())); - - float hittorLever = hd.HitRadius[HITOR].GetPerpendicular().Dot(hd.BitmapNormal); - float hitteeLever = hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.BitmapNormal); - hittorLever *= hittorLever; - hitteeLever *= hitteeLever; - float impulse = hitAcc.Dot(hd.BitmapNormal) / (((1 / hd.TotalMass[HITOR]) + (1 / hd.TotalMass[HITEE])) + - (hittorLever / hd.MomInertia[HITOR]) + (hitteeLever / hd.MomInertia[HITEE])); - // TODO: Should the impfactor not be swapped? -EE vs -OR?") - hd.ResImpulse[HITOR] = hd.BitmapNormal * impulse * hd.ImpulseFactor[HITOR]; - hd.ResImpulse[HITEE] = hd.BitmapNormal * -impulse * hd.ImpulseFactor[HITEE]; - - // If a particle, which does not penetrate, but bounces, do any additional - // effects of that bounce. - if (!ParticlePenetration(hd)) - { -// TODO: Add blunt trauma effects here!") - ; - } - - // If the hittee is pinned, see if the collision's impulse is enough to dislodge it. - float hiteePin = hd.Body[HITEE]->GetPinStrength(); - // See if it's pinned, and compare it to the impulse force from the collision - if (m_PinStrength > 0 && hd.ResImpulse[HITEE].MagnitudeIsGreaterThan(m_PinStrength)) - { - // Unpin and set the threshold to 0 - hd.Body[HITEE]->SetPinStrength(0); - } - // If not knocked loose, then move the impulse of the hitee to the hitor - else if (hiteePin) - { -// No good, causes crazy bounces -// hd.ResImpulse[HITOR] -= hd.ResImpulse[HITEE]; - hd.ResImpulse[HITEE].Reset(); - } - - AddImpulseForce(hd.ResImpulse[HITEE], hd.HitRadius[HITEE]); -// m_Vel += hd.ResImpulse[HITEE] / hd.mass[HITEE]; -// m_AngularVel += hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.ResImpulse[HITEE]) / hd.MomInertia[HITEE]; - } - else - return false; - - return true; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddRecoil + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds recoil effects to this MOSprite. + void MOSRotating::AddRecoil() { + m_RecoilOffset.SetXY(1, 0); + m_RecoilOffset.RadRotate(m_Rotation.GetRadAngle() + c_PI); + m_Recoiled = true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnBounce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits and then -// bounces off of something. This is called by the owned Atom/AtomGroup -// of this MovableObject during travel. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. -bool MOSRotating::OnBounce(HitData &hd) -{ + bool MOSRotating::CollideAtPoint(HitData& hd) { + if (m_ToDelete) { + return false; // TODO: Add a settings flag to enable old school particle sponges! + } - return false; -} + hd.ResImpulse[HITOR].Reset(); + hd.ResImpulse[HITEE].Reset(); + + // if (m_AlreadyHitBy.find(hd.Body[HITOR]->GetID()) == m_AlreadyHitBy.end()) + // { + // m_AlreadyHitBy.insert(hd.Body[HITOR]->GetID()); + + hd.HitRadius[HITEE] = (hd.HitPoint - m_Pos) * c_MPP; + /* + // Cancel if both hitor and hitee's hitpoint radii are pointing int he same direction, meaning the objects are really tangled + if (!hd.HitRadius[HITOR].IsZero() && hd.HitRadius[HITOR].Dot(hd.HitRadius[HITEE]) >= 0) + return false; + */ + hd.TotalMass[HITEE] = m_Mass; + hd.MomInertia[HITEE] = m_pAtomGroup->GetMomentOfInertia(); + hd.HitVel[HITEE] = m_Vel + hd.HitRadius[HITEE].GetPerpendicular() * m_AngularVel; + hd.VelDiff = hd.HitVel[HITOR] - hd.HitVel[HITEE]; + + // Only do collision response for this if it appears the collision is happening in the 'right' direction, meaning away from the hitee collision normal + // The wrong way happens when things are sunk into each other, and thus getting 'hooked' on each other + if (hd.VelDiff.Dot(hd.BitmapNormal) < 0) { + Vector hitAcc = -hd.VelDiff * (1 + (hd.Body[HITOR]->GetMaterial()->GetRestitution() * GetMaterial()->GetRestitution())); + + float hittorLever = hd.HitRadius[HITOR].GetPerpendicular().Dot(hd.BitmapNormal); + float hitteeLever = hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.BitmapNormal); + hittorLever *= hittorLever; + hitteeLever *= hitteeLever; + float impulse = hitAcc.Dot(hd.BitmapNormal) / (((1 / hd.TotalMass[HITOR]) + (1 / hd.TotalMass[HITEE])) + + (hittorLever / hd.MomInertia[HITOR]) + (hitteeLever / hd.MomInertia[HITEE])); + // TODO: Should the impfactor not be swapped? -EE vs -OR?") + hd.ResImpulse[HITOR] = hd.BitmapNormal * impulse * hd.ImpulseFactor[HITOR]; + hd.ResImpulse[HITEE] = hd.BitmapNormal * -impulse * hd.ImpulseFactor[HITEE]; + + // If a particle, which does not penetrate, but bounces, do any additional + // effects of that bounce. + if (!ParticlePenetration(hd)) { + // TODO: Add blunt trauma effects here!") + ; + } + // If the hittee is pinned, see if the collision's impulse is enough to dislodge it. + float hiteePin = hd.Body[HITEE]->GetPinStrength(); + // See if it's pinned, and compare it to the impulse force from the collision + if (m_PinStrength > 0 && hd.ResImpulse[HITEE].MagnitudeIsGreaterThan(m_PinStrength)) { + // Unpin and set the threshold to 0 + hd.Body[HITEE]->SetPinStrength(0); + } + // If not knocked loose, then move the impulse of the hitee to the hitor + else if (hiteePin) { + // No good, causes crazy bounces + // hd.ResImpulse[HITOR] -= hd.ResImpulse[HITEE]; + hd.ResImpulse[HITEE].Reset(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnSink -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits and then -// sink into something. This is called by the owned Atom/AtomGroup -// of this MovableObject during travel. + AddImpulseForce(hd.ResImpulse[HITEE], hd.HitRadius[HITEE]); + // m_Vel += hd.ResImpulse[HITEE] / hd.mass[HITEE]; + // m_AngularVel += hd.HitRadius[HITEE].GetPerpendicular().Dot(hd.ResImpulse[HITEE]) / hd.MomInertia[HITEE]; + } else + return false; -bool MOSRotating::OnSink(HitData &hd) -{ - return false; -} + return true; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnBounce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits and then + // bounces off of something. This is called by the owned Atom/AtomGroup + // of this MovableObject during travel. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ParticlePenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Determines whether a particle which has hit this MO will penetrate, -// and if so, whether it gets lodged or exits on the other side of this -// MO. Appropriate effects will be determined and applied ONLY IF there -// was penetration! If not, nothing will be affected. - -bool MOSRotating::ParticlePenetration(HitData &hd) -{ - // Only particles can penetrate. - if (!(dynamic_cast(hd.Body[HITOR]) || dynamic_cast(hd.Body[HITOR]))) - return false; - - float impulseForce = hd.ResImpulse[HITEE].GetMagnitude(); - Material const * myMat = GetMaterial(); - float myStrength = myMat->GetIntegrity() / hd.Body[HITOR]->GetSharpness(); - - // See if there is enough energy in the collision for the particle to penetrate - if (impulseForce * hd.Body[HITOR]->GetSharpness() > myMat->GetIntegrity()) - { - // Ok penetration happened, now figure out if and where the exit point - // would be by tracing a rasterized line through the sprite. - - // Declare all vars needed for good ole' Bresenham.. - int intPos[2], delta[2], delta2[2], increment[2], bounds[2]; - int error, dom, sub, domSteps, subSteps; - bool inside = false, exited = false, subStepped = false; - - // Lock all bitmaps involved outside the loop. - acquire_bitmap(m_aSprite[m_Frame]); - - bounds[X] = m_aSprite[m_Frame]->w; - bounds[Y] = m_aSprite[m_Frame]->h; - - // Figure out the entry position in the un-rotated sprite's coordinates. - Vector entryPos = (g_SceneMan.ShortestDistance(m_Pos, hd.HitPoint) / m_Rotation).GetXFlipped(m_HFlipped) - m_SpriteOffset; - intPos[X] = std::floor(entryPos.m_X); - intPos[Y] = std::floor(entryPos.m_Y); - - // Get the un-rotated direction and max possible - // travel length of the particle. - Vector dir(std::max(bounds[X], bounds[Y]), 0); - dir.AbsRotateTo(hd.HitVel[HITOR] / m_Rotation); - dir = dir.GetXFlipped(m_HFlipped); - - // Bresenham's line drawing algorithm preparation - delta[X] = std::floor(entryPos.m_X + dir.m_X) - intPos[X]; - delta[Y] = std::floor(entryPos.m_Y + dir.m_Y) - intPos[Y]; - domSteps = 0; - subSteps = 0; - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) - { - dom = X; - sub = Y; - } - else { - dom = Y; - sub = X; - } - error = delta2[sub] - delta[dom]; - - // Bresenham's line drawing algorithm execution - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - if (intPos[X] < 0 || intPos[X] >= bounds[X] || - intPos[Y] < 0 || intPos[Y] >= bounds[Y]) - { - exited = false; - break; - } - - RTEAssert(is_inside_bitmap(m_aSprite[m_Frame], intPos[X], intPos[Y], 0), "Particle penetration test is outside of sprite!"); - - // Check if we are inside the sprite. - if (_getpixel(m_aSprite[m_Frame], intPos[X], intPos[Y]) != g_MaskColor) - { - inside = true; - // Break if the particle can't force its way through any further. - if (impulseForce <= myStrength) { - exited = false; - break; - } - // Do the resistance calculation which is retarding the - // kinetic force of the penetrating particle by each pixel penetrated. - impulseForce -= myStrength; - } - // If we are inside and are now outside, we have just exited! - else if (inside) - { - impulseForce += myStrength; - exited = true; - break; - } - - // Advance to the next pixel - intPos[dom] += increment[dom]; - if (error >= 0) { - intPos[sub] += increment[sub]; - ++subSteps; - error -= delta2[dom]; - } - error += delta2[sub]; - } - // Unlock all bitmaps involved outside the loop. - release_bitmap(m_aSprite[m_Frame]); - - if (m_pEntryWound) - { - // Add entry wound AEmitter to actor where the particle penetrated. - AEmitter *pEntryWound = dynamic_cast(m_pEntryWound->Clone()); - pEntryWound->SetInheritedRotAngleOffset(dir.GetAbsRadAngle() + c_PI); - float damageMultiplier = pEntryWound->HasNoSetDamageMultiplier() ? 1.0F : pEntryWound->GetDamageMultiplier(); - pEntryWound->SetDamageMultiplier(damageMultiplier * hd.Body[HITOR]->WoundDamageMultiplier()); - // Adjust position so that it looks like the hole is actually *on* the Hitee. - entryPos[dom] += increment[dom] * (pEntryWound->GetSpriteWidth() / 2); - AddWound(pEntryWound, entryPos + m_SpriteOffset); - pEntryWound = 0; - } - - // Add exit wound AEmitter to actor, if applicable. - if (exited) - { - Vector exitPos; - exitPos[dom] = entryPos[dom] + (increment[dom] * domSteps); - exitPos[sub] = entryPos[sub] + (increment[sub] * subSteps); - if (m_pExitWound) - { - AEmitter *pExitWound = dynamic_cast(m_pExitWound->Clone()); - // Adjust position so that it looks like the hole is actually *on* the Hitee. - exitPos[dom] -= increment[dom] * (pExitWound->GetSpriteWidth() / 2); - pExitWound->SetInheritedRotAngleOffset(dir.GetAbsRadAngle()); - float damageMultiplier = pExitWound->HasNoSetDamageMultiplier() ? 1.0F : pExitWound->GetDamageMultiplier(); - pExitWound->SetDamageMultiplier(damageMultiplier * hd.Body[HITOR]->WoundDamageMultiplier()); - AddWound(pExitWound, exitPos + m_SpriteOffset); - pExitWound = 0; - } - - // Set the exiting particle's position to where the exit wound is, - // in world coordinates. - hd.Body[HITOR]->SetPos(((exitPos + m_SpriteOffset) / m_Rotation) + m_Pos); - - // Finally apply the forces imposed on both this MOSRotating - // and the hitting particle due to the penetrating and exiting hit. - // These don't need to be manipulated if there was no exit, since this - // absorbed all the energy, and the hittee gets deleted from the scene. - hd.ResImpulse[HITEE] -= hd.ResImpulse[HITEE] * (impulseForce / hd.ResImpulse[HITEE].GetMagnitude()); - hd.ResImpulse[HITOR] = -(hd.ResImpulse[HITEE]); - } - // Particle got lodged inside this MOSRotating, so stop it and delete it from scene. - else - { - // Set the exiting particle's position to where it looks lodged -// hd.Body[HITOR]->SetPos(m_Pos - m_SpriteOffset + entryPos); -// hd.Body[HITOR]->SetVel(Vector()); - hd.Body[HITOR]->SetToDelete(true); - hd.Terminate[HITOR] = true; - } - - // Report that penetration occured. - return true; - } - - // Report that penetration didn't occur and signal to client to - // calculate bounce effects instead. - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MOSRotating::GibThis(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { - if (m_MissionCritical || m_ToDelete) { - return; - } - - if (impactImpulse.MagnitudeIsGreaterThan(GetGibImpulseLimit())) { - // Add a counterforce equal to GibImpulseLimit to the impulse list in order to simulate the force spent on breaking the object apart - Vector counterForce = impactImpulse; - counterForce.SetMagnitude(GetGibImpulseLimit()); - m_ImpulseForces.emplace_back(-counterForce, Vector()); - MOSprite::ApplyImpulses(); - } - - CreateGibsWhenGibbing(impactImpulse, movableObjectToIgnore); - - RemoveAttachablesWhenGibbing(impactImpulse, movableObjectToIgnore); - - if (m_GibSound) { m_GibSound->Play(m_Pos); } - - if (m_pScreenEffect && m_EffectOnGib && (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY()))) { - g_PostProcessMan.RegisterPostEffect(m_Pos, m_pScreenEffect, m_ScreenEffectHash, 255, m_EffectRotAngle); - } - - if (m_LoudnessOnGib > 0) { g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, m_LoudnessOnGib)); } - - m_ToDelete = true; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { - if (m_GibScreenShakeAmount != -1.0F) { - g_CameraMan.AddScreenShake(m_GibScreenShakeAmount, m_Pos); - } - - for (const Gib &gibSettingsObject : m_Gibs) { - if (gibSettingsObject.GetCount() == 0) { - continue; - } - MovableObject *gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); - - int count = gibSettingsObject.GetCount(); - float lifeVariation = gibSettingsObject.GetLifeVariation(); - float spread = gibSettingsObject.GetSpread(); - float minVelocity = gibSettingsObject.GetMinVelocity(); - float maxVelocity = gibSettingsObject.GetMaxVelocity(); - - float mass = (gibParticleClone->GetMass() != 0 ? gibParticleClone->GetMass() : 0.0001F); - int lifetime = gibParticleClone->GetLifetime(); - - if (minVelocity == 0 && maxVelocity == 0) { - minVelocity = m_GibBlastStrength / mass; - maxVelocity = minVelocity + 10.0F; - } - - if (m_GibScreenShakeAmount == -1.0F) { - // Automatically calculate a value based on the amount of energy going on here - float averageSpeed = (minVelocity + maxVelocity) * 0.5F; - float energy = mass * averageSpeed * static_cast(count); - g_CameraMan.AddScreenShake(energy * g_CameraMan.GetDefaultShakePerUnitOfGibEnergy(), m_Pos); - } - - float velocityRange = maxVelocity - minVelocity; - Vector rotatedGibOffset = RotateOffset(gibSettingsObject.GetOffset()); - - // The "Spiral" spread mode uses the fermat spiral as means to determine the velocity of the gib particles, resulting in a evenly spaced out circle (or ring) of particles. - if (gibSettingsObject.GetSpreadMode() == Gib::SpreadMode::SpreadSpiral) { - float maxRadius = std::sqrt(static_cast(count)); - float scale = velocityRange / maxRadius; - float randAngle = c_PI * RandomNormalNum(); - float goldenAngle = 2.39996F; - - for (int i = 0; i < count; i++) { - if (i > 0) { gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); } - - float radius = std::sqrt(static_cast(count - i)); - gibParticleClone->SetPos(m_Pos + rotatedGibOffset); - gibParticleClone->SetHFlipped(m_HFlipped); - Vector gibVelocity(radius * scale + minVelocity, 0); - gibVelocity.RadRotate(randAngle + RandomNum(0.0F, spread) + static_cast(i) * goldenAngle); - if (lifetime != 0) { - gibParticleClone->SetLifetime(std::max(static_cast(static_cast(lifetime) * (1.0F - lifeVariation * ((radius / maxRadius) * 0.75F + RandomNormalNum() * 0.25F))), 1)); - } - gibParticleClone->SetRotAngle(gibVelocity.GetAbsRadAngle() + (m_HFlipped ? c_PI : 0)); - gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.35F) + (gibParticleClone->GetAngularVel() * 0.65F / mass) * RandomNum()); - gibParticleClone->SetVel(gibVelocity + ((m_PrevVel + m_Vel) / 2) * gibSettingsObject.InheritsVelocity()); - if (movableObjectToIgnore) { gibParticleClone->SetWhichMOToNotHit(movableObjectToIgnore); } - if (gibSettingsObject.IgnoresTeamHits()) { - gibParticleClone->SetTeam(m_Team); - gibParticleClone->SetIgnoresTeamHits(true); - } + bool MOSRotating::OnBounce(HitData& hd) { - g_MovableMan.AddParticle(gibParticleClone); + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnSink + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits and then + // sink into something. This is called by the owned Atom/AtomGroup + // of this MovableObject during travel. + + bool MOSRotating::OnSink(HitData& hd) { + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ParticlePenetration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Determines whether a particle which has hit this MO will penetrate, + // and if so, whether it gets lodged or exits on the other side of this + // MO. Appropriate effects will be determined and applied ONLY IF there + // was penetration! If not, nothing will be affected. + + bool MOSRotating::ParticlePenetration(HitData& hd) { + // Only particles can penetrate. + if (!(dynamic_cast(hd.Body[HITOR]) || dynamic_cast(hd.Body[HITOR]))) + return false; + + float impulseForce = hd.ResImpulse[HITEE].GetMagnitude(); + Material const* myMat = GetMaterial(); + float myStrength = myMat->GetIntegrity() / hd.Body[HITOR]->GetSharpness(); + + // See if there is enough energy in the collision for the particle to penetrate + if (impulseForce * hd.Body[HITOR]->GetSharpness() > myMat->GetIntegrity()) { + // Ok penetration happened, now figure out if and where the exit point + // would be by tracing a rasterized line through the sprite. + + // Declare all vars needed for good ole' Bresenham.. + int intPos[2], delta[2], delta2[2], increment[2], bounds[2]; + int error, dom, sub, domSteps, subSteps; + bool inside = false, exited = false, subStepped = false; + + // Lock all bitmaps involved outside the loop. + acquire_bitmap(m_aSprite[m_Frame]); + + bounds[X] = m_aSprite[m_Frame]->w; + bounds[Y] = m_aSprite[m_Frame]->h; + + // Figure out the entry position in the un-rotated sprite's coordinates. + Vector entryPos = (g_SceneMan.ShortestDistance(m_Pos, hd.HitPoint) / m_Rotation).GetXFlipped(m_HFlipped) - m_SpriteOffset; + intPos[X] = std::floor(entryPos.m_X); + intPos[Y] = std::floor(entryPos.m_Y); + + // Get the un-rotated direction and max possible + // travel length of the particle. + Vector dir(std::max(bounds[X], bounds[Y]), 0); + dir.AbsRotateTo(hd.HitVel[HITOR] / m_Rotation); + dir = dir.GetXFlipped(m_HFlipped); + + // Bresenham's line drawing algorithm preparation + delta[X] = std::floor(entryPos.m_X + dir.m_X) - intPos[X]; + delta[Y] = std::floor(entryPos.m_Y + dir.m_Y) - intPos[Y]; + domSteps = 0; + subSteps = 0; + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; } - } else { - for (int i = 0; i < count; i++) { - if (i > 0) { gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); } + error = delta2[sub] - delta[dom]; - if (gibParticleClone->GetLifetime() != 0) { - gibParticleClone->SetLifetime(std::max(static_cast(static_cast(gibParticleClone->GetLifetime()) * (1.0F + (lifeVariation * RandomNormalNum()))), 1)); + // Bresenham's line drawing algorithm execution + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + if (intPos[X] < 0 || intPos[X] >= bounds[X] || + intPos[Y] < 0 || intPos[Y] >= bounds[Y]) { + exited = false; + break; } - gibParticleClone->SetRotAngle(GetRotAngle() + gibParticleClone->GetRotAngle()); - gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.35F) + (gibParticleClone->GetAngularVel() * 0.65F / mass) * RandomNum()); - if (rotatedGibOffset.GetRoundIntX() > m_aSprite[0]->w / 3) { - float offCenterRatio = rotatedGibOffset.m_X / (static_cast(m_aSprite[0]->w) / 2.0F); - float angularVel = std::abs(gibParticleClone->GetAngularVel() * 0.5F) + std::abs(gibParticleClone->GetAngularVel() * 0.5F * offCenterRatio); - gibParticleClone->SetAngularVel(angularVel * (rotatedGibOffset.m_X > 0 ? -1 : 1)); - } else { - gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.5F + (gibParticleClone->GetAngularVel() * RandomNum())) * (RandomNormalNum() > 0.0F ? 1.0F : -1.0F)); + RTEAssert(is_inside_bitmap(m_aSprite[m_Frame], intPos[X], intPos[Y], 0), "Particle penetration test is outside of sprite!"); + + // Check if we are inside the sprite. + if (_getpixel(m_aSprite[m_Frame], intPos[X], intPos[Y]) != g_MaskColor) { + inside = true; + // Break if the particle can't force its way through any further. + if (impulseForce <= myStrength) { + exited = false; + break; + } + // Do the resistance calculation which is retarding the + // kinetic force of the penetrating particle by each pixel penetrated. + impulseForce -= myStrength; } - - gibParticleClone->SetPos(m_Pos + rotatedGibOffset); - gibParticleClone->SetHFlipped(m_HFlipped); - Vector gibVelocity = Vector(minVelocity + RandomNum(0.0F, velocityRange), 0.0F); - - // TODO: Figure out how much the magnitude of an offset should affect spread - float gibSpread = (rotatedGibOffset.IsZero() && spread == 0.1F) ? c_PI : spread; - // Determine the primary direction of the gib particles. - if (gibSettingsObject.InheritsVelocity() > 0 && !impactImpulse.IsZero()) { - gibVelocity.RadRotate(impactImpulse.GetAbsRadAngle()); - } else if (!rotatedGibOffset.IsZero()) { - gibVelocity.RadRotate(rotatedGibOffset.GetAbsRadAngle()); - } else { - gibVelocity.RadRotate(m_Rotation.GetRadAngle() + (m_HFlipped ? c_PI : 0)); + // If we are inside and are now outside, we have just exited! + else if (inside) { + impulseForce += myStrength; + exited = true; + break; } - // The "Even" spread will spread all gib particles evenly in an arc, while maintaining a randomized velocity magnitude. - if (gibSettingsObject.GetSpreadMode() == Gib::SpreadMode::SpreadEven) { - gibVelocity.RadRotate(gibSpread - (gibSpread * 2.0F * static_cast(i) / static_cast(count))); - } else { - gibVelocity.RadRotate(gibSpread * RandomNormalNum()); + + // Advance to the next pixel + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + ++subSteps; + error -= delta2[dom]; } - gibParticleClone->SetVel(gibVelocity + ((m_PrevVel + m_Vel) / 2) * gibSettingsObject.InheritsVelocity()); - if (movableObjectToIgnore) { gibParticleClone->SetWhichMOToNotHit(movableObjectToIgnore); } - if (gibSettingsObject.IgnoresTeamHits()) { - gibParticleClone->SetTeam(m_Team); - gibParticleClone->SetIgnoresTeamHits(true); + error += delta2[sub]; + } + // Unlock all bitmaps involved outside the loop. + release_bitmap(m_aSprite[m_Frame]); + + if (m_pEntryWound) { + // Add entry wound AEmitter to actor where the particle penetrated. + AEmitter* pEntryWound = dynamic_cast(m_pEntryWound->Clone()); + pEntryWound->SetInheritedRotAngleOffset(dir.GetAbsRadAngle() + c_PI); + float damageMultiplier = pEntryWound->HasNoSetDamageMultiplier() ? 1.0F : pEntryWound->GetDamageMultiplier(); + pEntryWound->SetDamageMultiplier(damageMultiplier * hd.Body[HITOR]->WoundDamageMultiplier()); + // Adjust position so that it looks like the hole is actually *on* the Hitee. + entryPos[dom] += increment[dom] * (pEntryWound->GetSpriteWidth() / 2); + AddWound(pEntryWound, entryPos + m_SpriteOffset); + pEntryWound = 0; + } + + // Add exit wound AEmitter to actor, if applicable. + if (exited) { + Vector exitPos; + exitPos[dom] = entryPos[dom] + (increment[dom] * domSteps); + exitPos[sub] = entryPos[sub] + (increment[sub] * subSteps); + if (m_pExitWound) { + AEmitter* pExitWound = dynamic_cast(m_pExitWound->Clone()); + // Adjust position so that it looks like the hole is actually *on* the Hitee. + exitPos[dom] -= increment[dom] * (pExitWound->GetSpriteWidth() / 2); + pExitWound->SetInheritedRotAngleOffset(dir.GetAbsRadAngle()); + float damageMultiplier = pExitWound->HasNoSetDamageMultiplier() ? 1.0F : pExitWound->GetDamageMultiplier(); + pExitWound->SetDamageMultiplier(damageMultiplier * hd.Body[HITOR]->WoundDamageMultiplier()); + AddWound(pExitWound, exitPos + m_SpriteOffset); + pExitWound = 0; } - g_MovableMan.AddParticle(gibParticleClone); + // Set the exiting particle's position to where the exit wound is, + // in world coordinates. + hd.Body[HITOR]->SetPos(((exitPos + m_SpriteOffset) / m_Rotation) + m_Pos); + + // Finally apply the forces imposed on both this MOSRotating + // and the hitting particle due to the penetrating and exiting hit. + // These don't need to be manipulated if there was no exit, since this + // absorbed all the energy, and the hittee gets deleted from the scene. + hd.ResImpulse[HITEE] -= hd.ResImpulse[HITEE] * (impulseForce / hd.ResImpulse[HITEE].GetMagnitude()); + hd.ResImpulse[HITOR] = -(hd.ResImpulse[HITEE]); } + // Particle got lodged inside this MOSRotating, so stop it and delete it from scene. + else { + // Set the exiting particle's position to where it looks lodged + // hd.Body[HITOR]->SetPos(m_Pos - m_SpriteOffset + entryPos); + // hd.Body[HITOR]->SetVel(Vector()); + hd.Body[HITOR]->SetToDelete(true); + hd.Terminate[HITOR] = true; + } + + // Report that penetration occured. + return true; } - } -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Report that penetration didn't occur and signal to client to + // calculate bounce effects instead. + return false; + } -void MOSRotating::RemoveAttachablesWhenGibbing(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { - const std::vector nonVolatileAttachablesVectorForLuaSafety { m_Attachables.begin(), m_Attachables.end() }; - for (Attachable *attachable : nonVolatileAttachablesVectorForLuaSafety) { - RTEAssert(attachable, "Broken Attachable when Gibbing!"); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (RandomNum() < attachable->GetGibWithParentChance() || attachable->GetGibWhenRemovedFromParent()) { - attachable->GibThis(); - continue; - } + void MOSRotating::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { + if (m_MissionCritical || m_ToDelete) { + return; + } - if (!attachable->GetDeleteWhenRemovedFromParent()) { - float attachableGibBlastStrength = (attachable->GetParentGibBlastStrengthMultiplier() * m_GibBlastStrength) / (1 + attachable->GetMass()); - attachable->SetAngularVel((attachable->GetAngularVel() * 0.5F) + (attachable->GetAngularVel() * 0.5F * attachableGibBlastStrength * RandomNormalNum())); - Vector gibBlastVel = Vector(attachable->GetParentOffset()).SetMagnitude(attachableGibBlastStrength * 0.5F + (attachableGibBlastStrength * RandomNum())); - attachable->SetVel(m_Vel + gibBlastVel); // Attachables have already had their velocity updated by ApplyImpulses(), no need to add impactImpulse again + if (impactImpulse.MagnitudeIsGreaterThan(GetGibImpulseLimit())) { + // Add a counterforce equal to GibImpulseLimit to the impulse list in order to simulate the force spent on breaking the object apart + Vector counterForce = impactImpulse; + counterForce.SetMagnitude(GetGibImpulseLimit()); + m_ImpulseForces.emplace_back(-counterForce, Vector()); + MOSprite::ApplyImpulses(); + } - if (movableObjectToIgnore) { attachable->SetWhichMOToNotHit(movableObjectToIgnore); } - } + CreateGibsWhenGibbing(impactImpulse, movableObjectToIgnore); - RemoveAttachable(attachable, true, true); - } - m_Attachables.clear(); -} + RemoveAttachablesWhenGibbing(impactImpulse, movableObjectToIgnore); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (m_GibSound) { + m_GibSound->Play(m_Pos); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: MoveOutOfTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether any of the Atom:s in this MovableObject are on top of -// terrain pixels, and if so, attempt to move this out so none of this' -// Atoms are on top of the terrain any more. + if (m_pScreenEffect && m_EffectOnGib && (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY()))) { + g_PostProcessMan.RegisterPostEffect(m_Pos, m_pScreenEffect, m_ScreenEffectHash, 255, m_EffectRotAngle); + } -bool MOSRotating::MoveOutOfTerrain(unsigned char strongerThan) -{ - return m_pAtomGroup->ResolveTerrainIntersection(m_Pos, strongerThan); -} + if (m_LoudnessOnGib > 0) { + g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, m_LoudnessOnGib)); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + m_ToDelete = true; + } -void MOSRotating::ApplyForces() { - float deltaTime = g_TimerMan.GetDeltaTimeSecs(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - for (const auto &[forceVector, forceOffset] : m_Forces) { - if (!forceOffset.IsZero()) { m_AngularVel += (forceOffset.GetPerpendicular().Dot(forceVector) / m_pAtomGroup->GetMomentOfInertia()) * deltaTime; } - } + void MOSRotating::CreateGibsWhenGibbing(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { + if (m_GibScreenShakeAmount != -1.0F) { + g_CameraMan.AddScreenShake(m_GibScreenShakeAmount, m_Pos); + } - MOSprite::ApplyForces(); -} + for (const Gib& gibSettingsObject: m_Gibs) { + if (gibSettingsObject.GetCount() == 0) { + continue; + } + MovableObject* gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int count = gibSettingsObject.GetCount(); + float lifeVariation = gibSettingsObject.GetLifeVariation(); + float spread = gibSettingsObject.GetSpread(); + float minVelocity = gibSettingsObject.GetMinVelocity(); + float maxVelocity = gibSettingsObject.GetMaxVelocity(); -void MOSRotating::ApplyImpulses() { - for (const auto &[impulseForceVector, impulseForceOffset] : m_ImpulseForces) { - if (!impulseForceOffset.IsZero()) { m_AngularVel += impulseForceOffset.GetPerpendicular().Dot(impulseForceVector) / m_pAtomGroup->GetMomentOfInertia(); } - } + float mass = (gibParticleClone->GetMass() != 0 ? gibParticleClone->GetMass() : 0.0001F); + int lifetime = gibParticleClone->GetLifetime(); - Vector totalImpulse; - Vector averagedImpulseForceOffset; - for (const auto &[impulseForceVector, impulseForceOffset] : m_ImpulseForces) { - totalImpulse += impulseForceVector; - averagedImpulseForceOffset += impulseForceOffset; - } - averagedImpulseForceOffset /= static_cast(m_ImpulseForces.size()); + if (minVelocity == 0 && maxVelocity == 0) { + minVelocity = m_GibBlastStrength / mass; + maxVelocity = minVelocity + 10.0F; + } - if (m_GibImpulseLimit > 0) { - float impulseLimit = m_GibImpulseLimit; - if (m_WoundCountAffectsImpulseLimitRatio != 0 && m_GibWoundLimit > 0) { - impulseLimit *= 1.0F - (static_cast(m_Wounds.size()) / static_cast(m_GibWoundLimit)) * m_WoundCountAffectsImpulseLimitRatio; - } - if (totalImpulse.MagnitudeIsGreaterThan(impulseLimit)) { - DetachAttachablesFromImpulse(totalImpulse); - // Use the remainder of the impulses left over from detaching to gib the parent object. - if (totalImpulse.MagnitudeIsGreaterThan(impulseLimit)) { GibThis(totalImpulse); } + if (m_GibScreenShakeAmount == -1.0F) { + // Automatically calculate a value based on the amount of energy going on here + float averageSpeed = (minVelocity + maxVelocity) * 0.5F; + float energy = mass * averageSpeed * static_cast(count); + g_CameraMan.AddScreenShake(energy * g_CameraMan.GetDefaultShakePerUnitOfGibEnergy(), m_Pos); + } + + float velocityRange = maxVelocity - minVelocity; + Vector rotatedGibOffset = RotateOffset(gibSettingsObject.GetOffset()); + + // The "Spiral" spread mode uses the fermat spiral as means to determine the velocity of the gib particles, resulting in a evenly spaced out circle (or ring) of particles. + if (gibSettingsObject.GetSpreadMode() == Gib::SpreadMode::SpreadSpiral) { + float maxRadius = std::sqrt(static_cast(count)); + float scale = velocityRange / maxRadius; + float randAngle = c_PI * RandomNormalNum(); + float goldenAngle = 2.39996F; + + for (int i = 0; i < count; i++) { + if (i > 0) { + gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); + } + + float radius = std::sqrt(static_cast(count - i)); + gibParticleClone->SetPos(m_Pos + rotatedGibOffset); + gibParticleClone->SetHFlipped(m_HFlipped); + Vector gibVelocity(radius * scale + minVelocity, 0); + gibVelocity.RadRotate(randAngle + RandomNum(0.0F, spread) + static_cast(i) * goldenAngle); + if (lifetime != 0) { + gibParticleClone->SetLifetime(std::max(static_cast(static_cast(lifetime) * (1.0F - lifeVariation * ((radius / maxRadius) * 0.75F + RandomNormalNum() * 0.25F))), 1)); + } + gibParticleClone->SetRotAngle(gibVelocity.GetAbsRadAngle() + (m_HFlipped ? c_PI : 0)); + gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.35F) + (gibParticleClone->GetAngularVel() * 0.65F / mass) * RandomNum()); + gibParticleClone->SetVel(gibVelocity + ((m_PrevVel + m_Vel) / 2) * gibSettingsObject.InheritsVelocity()); + if (movableObjectToIgnore) { + gibParticleClone->SetWhichMOToNotHit(movableObjectToIgnore); + } + if (gibSettingsObject.IgnoresTeamHits()) { + gibParticleClone->SetTeam(m_Team); + gibParticleClone->SetIgnoresTeamHits(true); + } + + g_MovableMan.AddParticle(gibParticleClone); + } + } else { + for (int i = 0; i < count; i++) { + if (i > 0) { + gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); + } + + if (gibParticleClone->GetLifetime() != 0) { + gibParticleClone->SetLifetime(std::max(static_cast(static_cast(gibParticleClone->GetLifetime()) * (1.0F + (lifeVariation * RandomNormalNum()))), 1)); + } + + gibParticleClone->SetRotAngle(GetRotAngle() + gibParticleClone->GetRotAngle()); + gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.35F) + (gibParticleClone->GetAngularVel() * 0.65F / mass) * RandomNum()); + if (rotatedGibOffset.GetRoundIntX() > m_aSprite[0]->w / 3) { + float offCenterRatio = rotatedGibOffset.m_X / (static_cast(m_aSprite[0]->w) / 2.0F); + float angularVel = std::abs(gibParticleClone->GetAngularVel() * 0.5F) + std::abs(gibParticleClone->GetAngularVel() * 0.5F * offCenterRatio); + gibParticleClone->SetAngularVel(angularVel * (rotatedGibOffset.m_X > 0 ? -1 : 1)); + } else { + gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.5F + (gibParticleClone->GetAngularVel() * RandomNum())) * (RandomNormalNum() > 0.0F ? 1.0F : -1.0F)); + } + + gibParticleClone->SetPos(m_Pos + rotatedGibOffset); + gibParticleClone->SetHFlipped(m_HFlipped); + Vector gibVelocity = Vector(minVelocity + RandomNum(0.0F, velocityRange), 0.0F); + + // TODO: Figure out how much the magnitude of an offset should affect spread + float gibSpread = (rotatedGibOffset.IsZero() && spread == 0.1F) ? c_PI : spread; + // Determine the primary direction of the gib particles. + if (gibSettingsObject.InheritsVelocity() > 0 && !impactImpulse.IsZero()) { + gibVelocity.RadRotate(impactImpulse.GetAbsRadAngle()); + } else if (!rotatedGibOffset.IsZero()) { + gibVelocity.RadRotate(rotatedGibOffset.GetAbsRadAngle()); + } else { + gibVelocity.RadRotate(m_Rotation.GetRadAngle() + (m_HFlipped ? c_PI : 0)); + } + // The "Even" spread will spread all gib particles evenly in an arc, while maintaining a randomized velocity magnitude. + if (gibSettingsObject.GetSpreadMode() == Gib::SpreadMode::SpreadEven) { + gibVelocity.RadRotate(gibSpread - (gibSpread * 2.0F * static_cast(i) / static_cast(count))); + } else { + gibVelocity.RadRotate(gibSpread * RandomNormalNum()); + } + gibParticleClone->SetVel(gibVelocity + ((m_PrevVel + m_Vel) / 2) * gibSettingsObject.InheritsVelocity()); + if (movableObjectToIgnore) { + gibParticleClone->SetWhichMOToNotHit(movableObjectToIgnore); + } + if (gibSettingsObject.IgnoresTeamHits()) { + gibParticleClone->SetTeam(m_Team); + gibParticleClone->SetIgnoresTeamHits(true); + } + + g_MovableMan.AddParticle(gibParticleClone); + } + } } } - MOSprite::ApplyImpulses(); -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void MOSRotating::RemoveAttachablesWhenGibbing(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { + const std::vector nonVolatileAttachablesVectorForLuaSafety{m_Attachables.begin(), m_Attachables.end()}; + for (Attachable* attachable: nonVolatileAttachablesVectorForLuaSafety) { + RTEAssert(attachable, "Broken Attachable when Gibbing!"); + if (RandomNum() < attachable->GetGibWithParentChance() || attachable->GetGibWhenRemovedFromParent()) { + attachable->GibThis(); + continue; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetAllTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resest all the timers used by this. Can be emitters, etc. This is to -// prevent backed up emissions to come out all at once while this has been -// held dormant in an inventory. + if (!attachable->GetDeleteWhenRemovedFromParent()) { + float attachableGibBlastStrength = (attachable->GetParentGibBlastStrengthMultiplier() * m_GibBlastStrength) / (1 + attachable->GetMass()); + attachable->SetAngularVel((attachable->GetAngularVel() * 0.5F) + (attachable->GetAngularVel() * 0.5F * attachableGibBlastStrength * RandomNormalNum())); + Vector gibBlastVel = Vector(attachable->GetParentOffset()).SetMagnitude(attachableGibBlastStrength * 0.5F + (attachableGibBlastStrength * RandomNum())); + attachable->SetVel(m_Vel + gibBlastVel); // Attachables have already had their velocity updated by ApplyImpulses(), no need to add impactImpulse again -void MOSRotating::ResetAllTimers() -{ - MovableObject::ResetAllTimers(); + if (movableObjectToIgnore) { + attachable->SetWhichMOToNotHit(movableObjectToIgnore); + } + } - for (auto emitter = m_Wounds.begin(); emitter != m_Wounds.end(); ++emitter) - (*emitter)->ResetAllTimers(); + RemoveAttachable(attachable, true, true); + } + m_Attachables.clear(); + } - for (auto attachable = m_Attachables.begin(); attachable != m_Attachables.end(); ++attachable) - (*attachable)->ResetAllTimers(); -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: MoveOutOfTerrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks whether any of the Atom:s in this MovableObject are on top of + // terrain pixels, and if so, attempt to move this out so none of this' + // Atoms are on top of the terrain any more. -void MOSRotating::RestDetection() { - MOSprite::RestDetection(); + bool MOSRotating::MoveOutOfTerrain(unsigned char strongerThan) { + return m_pAtomGroup->ResolveTerrainIntersection(m_Pos, strongerThan); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Rotational settling detection. - if ((m_AngularVel > 0 && m_PrevAngVel < 0) || (m_AngularVel < 0 && m_PrevAngVel > 0)) { - ++m_AngOscillations; - } else { - m_AngOscillations = 0; + void MOSRotating::ApplyForces() { + float deltaTime = g_TimerMan.GetDeltaTimeSecs(); + + for (const auto& [forceVector, forceOffset]: m_Forces) { + if (!forceOffset.IsZero()) { + m_AngularVel += (forceOffset.GetPerpendicular().Dot(forceVector) / m_pAtomGroup->GetMomentOfInertia()) * deltaTime; + } + } + + MOSprite::ApplyForces(); } - if (std::abs(m_Rotation.GetRadAngle() - m_PrevRotation.GetRadAngle()) >= 0.01) { m_RestTimer.Reset(); } - - // If about to settle, make sure the object isn't flying in the air. - // Note that this uses sprite radius to avoid possibly settling when it shouldn't (e.g. if there's a lopsided attachable enlarging the radius, using GetRadius might make it settle in the air). - if (m_ToSettle || IsAtRest()) { - bool resting = true; - if (g_SceneMan.OverAltitude(m_Pos, static_cast(m_SpriteRadius) + 4, 3)) { - resting = false; - for (const Attachable *attachable : m_Attachables) { - if (attachable->GetCollidesWithTerrainWhileAttached() && !g_SceneMan.OverAltitude(attachable->GetPos(), static_cast(attachable->GetIndividualRadius()) + 2, 3)) { - resting = true; - break; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MOSRotating::ApplyImpulses() { + for (const auto& [impulseForceVector, impulseForceOffset]: m_ImpulseForces) { + if (!impulseForceOffset.IsZero()) { + m_AngularVel += impulseForceOffset.GetPerpendicular().Dot(impulseForceVector) / m_pAtomGroup->GetMomentOfInertia(); + } + } + + Vector totalImpulse; + Vector averagedImpulseForceOffset; + for (const auto& [impulseForceVector, impulseForceOffset]: m_ImpulseForces) { + totalImpulse += impulseForceVector; + averagedImpulseForceOffset += impulseForceOffset; + } + averagedImpulseForceOffset /= static_cast(m_ImpulseForces.size()); + + if (m_GibImpulseLimit > 0) { + float impulseLimit = m_GibImpulseLimit; + if (m_WoundCountAffectsImpulseLimitRatio != 0 && m_GibWoundLimit > 0) { + impulseLimit *= 1.0F - (static_cast(m_Wounds.size()) / static_cast(m_GibWoundLimit)) * m_WoundCountAffectsImpulseLimitRatio; + } + if (totalImpulse.MagnitudeIsGreaterThan(impulseLimit)) { + DetachAttachablesFromImpulse(totalImpulse); + // Use the remainder of the impulses left over from detaching to gib the parent object. + if (totalImpulse.MagnitudeIsGreaterThan(impulseLimit)) { + GibThis(totalImpulse); } } } - if (!resting) { - m_VelOscillations = 0; + + MOSprite::ApplyImpulses(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ResetAllTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resest all the timers used by this. Can be emitters, etc. This is to + // prevent backed up emissions to come out all at once while this has been + // held dormant in an inventory. + + void MOSRotating::ResetAllTimers() { + MovableObject::ResetAllTimers(); + + for (auto emitter = m_Wounds.begin(); emitter != m_Wounds.end(); ++emitter) + (*emitter)->ResetAllTimers(); + + for (auto attachable = m_Attachables.begin(); attachable != m_Attachables.end(); ++attachable) + (*attachable)->ResetAllTimers(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MOSRotating::RestDetection() { + MOSprite::RestDetection(); + + // Rotational settling detection. + if ((m_AngularVel > 0 && m_PrevAngVel < 0) || (m_AngularVel < 0 && m_PrevAngVel > 0)) { + ++m_AngOscillations; + } else { m_AngOscillations = 0; + } + + if (std::abs(m_Rotation.GetRadAngle() - m_PrevRotation.GetRadAngle()) >= 0.01) { m_RestTimer.Reset(); - m_ToSettle = false; } + + // If about to settle, make sure the object isn't flying in the air. + // Note that this uses sprite radius to avoid possibly settling when it shouldn't (e.g. if there's a lopsided attachable enlarging the radius, using GetRadius might make it settle in the air). + if (m_ToSettle || IsAtRest()) { + bool resting = true; + if (g_SceneMan.OverAltitude(m_Pos, static_cast(m_SpriteRadius) + 4, 3)) { + resting = false; + for (const Attachable* attachable: m_Attachables) { + if (attachable->GetCollidesWithTerrainWhileAttached() && !g_SceneMan.OverAltitude(attachable->GetPos(), static_cast(attachable->GetIndividualRadius()) + 2, 3)) { + resting = true; + break; + } + } + } + if (!resting) { + m_VelOscillations = 0; + m_AngOscillations = 0; + m_RestTimer.Reset(); + m_ToSettle = false; + } + } + m_PrevRotation = m_Rotation; + m_PrevAngVel = m_AngularVel; } - m_PrevRotation = m_Rotation; - m_PrevAngVel = m_AngularVel; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool MOSRotating::IsAtRest() { - if (m_RestThreshold < 0 || m_PinStrength != 0) { - return false; - } else if (m_VelOscillations > 2 || m_AngOscillations > 2) { - return true; + bool MOSRotating::IsAtRest() { + if (m_RestThreshold < 0 || m_PinStrength != 0) { + return false; + } else if (m_VelOscillations > 2 || m_AngOscillations > 2) { + return true; + } + return m_RestTimer.IsPastSimMS(m_RestThreshold); } - return m_RestTimer.IsPastSimMS(m_RestThreshold); -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool MOSRotating::IsOnScenePoint(Vector &scenePoint) const { - if (!m_aSprite[m_Frame]) { - return false; - } + bool MOSRotating::IsOnScenePoint(Vector& scenePoint) const { + if (!m_aSprite[m_Frame]) { + return false; + } - //TODO this should really use GetRadius() instead of sprite radius, then check attachable's sprites directly here. It'd save some computation but I didn't wanna deal with it. - if (WithinBox(scenePoint, m_Pos.m_X - m_SpriteRadius, m_Pos.m_Y - m_SpriteRadius, m_Pos.m_X + m_SpriteRadius, m_Pos.m_Y + m_SpriteRadius)) { - Vector spritePoint = scenePoint - m_Pos; - spritePoint = UnRotateOffset(spritePoint); - int pixel = getpixel(m_aSprite[m_Frame], static_cast(spritePoint.m_X - m_SpriteOffset.m_X), static_cast(spritePoint.m_Y - m_SpriteOffset.m_Y)); - if (pixel != -1 && pixel != g_MaskColor) { - return true; - } - } + // TODO this should really use GetRadius() instead of sprite radius, then check attachable's sprites directly here. It'd save some computation but I didn't wanna deal with it. + if (WithinBox(scenePoint, m_Pos.m_X - m_SpriteRadius, m_Pos.m_Y - m_SpriteRadius, m_Pos.m_X + m_SpriteRadius, m_Pos.m_Y + m_SpriteRadius)) { + Vector spritePoint = scenePoint - m_Pos; + spritePoint = UnRotateOffset(spritePoint); + int pixel = getpixel(m_aSprite[m_Frame], static_cast(spritePoint.m_X - m_SpriteOffset.m_X), static_cast(spritePoint.m_Y - m_SpriteOffset.m_Y)); + if (pixel != -1 && pixel != g_MaskColor) { + return true; + } + } - for (const Attachable *attachable : m_Attachables) { - if (attachable->IsOnScenePoint(scenePoint)) { - return true; - } - } + for (const Attachable* attachable: m_Attachables) { + if (attachable->IsOnScenePoint(scenePoint)) { + return true; + } + } - return false; -} + return false; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: EraseFromTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Cuts this' silhouette out from the terrain's material and color layers. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: EraseFromTerrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Cuts this' silhouette out from the terrain's material and color layers. -void MOSRotating::EraseFromTerrain() -{ - Vector pivot = -m_SpriteOffset; + void MOSRotating::EraseFromTerrain() { + Vector pivot = -m_SpriteOffset; - if (m_HFlipped) - { - // Create intermediate flipping bitmap if there isn't one yet - // Don't size the intermediate bitmaps to teh m_Scale, because the scaling happens after they are done - if (!m_pFlipBitmap) - m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[m_Frame]->w, m_aSprite[m_Frame]->h); - clear_to_color(m_pFlipBitmap, g_MaskColor); + if (m_HFlipped) { + // Create intermediate flipping bitmap if there isn't one yet + // Don't size the intermediate bitmaps to teh m_Scale, because the scaling happens after they are done + if (!m_pFlipBitmap) + m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[m_Frame]->w, m_aSprite[m_Frame]->h); + clear_to_color(m_pFlipBitmap, g_MaskColor); - // Draw eitehr the source color bitmap or the intermediate material bitmap onto the intermediate flipping bitmap - draw_sprite_h_flip(m_pFlipBitmap, m_aSprite[m_Frame], 0, 0); + // Draw eitehr the source color bitmap or the intermediate material bitmap onto the intermediate flipping bitmap + draw_sprite_h_flip(m_pFlipBitmap, m_aSprite[m_Frame], 0, 0); - pivot.m_X = m_pFlipBitmap->w + m_SpriteOffset.m_X; - } + pivot.m_X = m_pFlipBitmap->w + m_SpriteOffset.m_X; + } - std::deque pixels = g_SceneMan.GetTerrain()->EraseSilhouette(m_HFlipped ? m_pFlipBitmap : m_aSprite[m_Frame], m_Pos, pivot, m_Rotation, m_Scale, false); -} + std::deque pixels = g_SceneMan.GetTerrain()->EraseSilhouette(m_HFlipped ? m_pFlipBitmap : m_aSprite[m_Frame], m_Pos, pivot, m_Rotation, m_Scale, false); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DeepCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if any of this' deep group atmos are on top of the terrain, and + // if so, erases this' silhouette from the terrain. + + bool MOSRotating::DeepCheck(bool makeMOPs, int skipMOP, int maxMOPs) { + // Check for deep penetration of the terrain and + // generate splash of MOPixels accordingly. + if (m_pDeepGroup && (m_pDeepGroup->InTerrain() || m_ForceDeepCheck)) { + m_ForceDeepCheck = false; + m_DeepHardness = true; + + // TODO: This stuff is just way too slow, EraseSilhouette is a hog + // Make particles fly at least somewhat + float velMag = MAX(10.0f, m_Vel.GetMagnitude()); + float splashDir = m_Vel.m_X >= 0 ? 1 : -1; + float splashRatio = g_MovableMan.GetSplashRatio(); + float tally = 0.0; + int depth = m_pDeepGroup->GetDepth() >> 1; + Vector pivot = -m_SpriteOffset; + + if (m_HFlipped) { + // Create intermediate flipping bitmap if there isn't one yet + // Don't size the intermediate bitmaps to teh m_Scale, because the scaling happens after they are done + if (!m_pFlipBitmap) + m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[m_Frame]->w, m_aSprite[m_Frame]->h); + clear_to_color(m_pFlipBitmap, g_MaskColor); + + // Draw eitehr the source color bitmap or the intermediate material bitmap onto the intermediate flipping bitmap + draw_sprite_h_flip(m_pFlipBitmap, m_aSprite[m_Frame], 0, 0); + + pivot.m_X = m_pFlipBitmap->w + m_SpriteOffset.m_X; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DeepCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if any of this' deep group atmos are on top of the terrain, and -// if so, erases this' silhouette from the terrain. - -bool MOSRotating::DeepCheck(bool makeMOPs, int skipMOP, int maxMOPs) -{ - // Check for deep penetration of the terrain and - // generate splash of MOPixels accordingly. - if (m_pDeepGroup && (m_pDeepGroup->InTerrain() || m_ForceDeepCheck)) - { - m_ForceDeepCheck = false; - m_DeepHardness = true; - -// TODO: This stuff is just way too slow, EraseSilhouette is a hog - // Make particles fly at least somewhat - float velMag = MAX(10.0f, m_Vel.GetMagnitude()); - float splashDir = m_Vel.m_X >= 0 ? 1 : -1; - float splashRatio = g_MovableMan.GetSplashRatio(); - float tally = 0.0; - int depth = m_pDeepGroup->GetDepth() >> 1; - Vector pivot = -m_SpriteOffset; - - if (m_HFlipped) - { - // Create intermediate flipping bitmap if there isn't one yet - // Don't size the intermediate bitmaps to teh m_Scale, because the scaling happens after they are done - if (!m_pFlipBitmap) - m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[m_Frame]->w, m_aSprite[m_Frame]->h); - clear_to_color(m_pFlipBitmap, g_MaskColor); - - // Draw eitehr the source color bitmap or the intermediate material bitmap onto the intermediate flipping bitmap - draw_sprite_h_flip(m_pFlipBitmap, m_aSprite[m_Frame], 0, 0); - - pivot.m_X = m_pFlipBitmap->w + m_SpriteOffset.m_X; - } - - { - // Particle generation - // Erase the silhouette and get all the pixels that were created as a result - std::deque pixels = g_SceneMan.GetTerrain()->EraseSilhouette(m_HFlipped ? m_pFlipBitmap : m_aSprite[m_Frame], m_Pos, pivot, m_Rotation, m_Scale, makeMOPs, skipMOP, maxMOPs); - - for (std::deque::iterator itr = pixels.begin(); itr != pixels.end(); ++itr) - { - tally += splashRatio; - if (tally >= 1.0) - { - tally -= 1.0; - (*itr)->SetPos((*itr)->GetPos() - m_Vel.GetNormalized() * depth); - (*itr)->SetVel(Vector(velMag * RandomNum(0.0F, splashDir), -RandomNum(0.0F, velMag))); - m_DeepHardness += (*itr)->GetMaterial()->GetIntegrity() * (*itr)->GetMaterial()->GetPixelDensity(); - g_MovableMan.AddParticle(*itr); - *itr = 0; - } - else - { - delete (*itr); - *itr = 0; - } - } - } -// EXPERIMENTAL - // Move the remaining out and away from teh terrain to help unsticking - MoveOutOfTerrain(g_MaterialSand); - - return true; - } - - return false; -} + { + // Particle generation + // Erase the silhouette and get all the pixels that were created as a result + std::deque pixels = g_SceneMan.GetTerrain()->EraseSilhouette(m_HFlipped ? m_pFlipBitmap : m_aSprite[m_Frame], m_Pos, pivot, m_Rotation, m_Scale, makeMOPs, skipMOP, maxMOPs); + + for (std::deque::iterator itr = pixels.begin(); itr != pixels.end(); ++itr) { + tally += splashRatio; + if (tally >= 1.0) { + tally -= 1.0; + (*itr)->SetPos((*itr)->GetPos() - m_Vel.GetNormalized() * depth); + (*itr)->SetVel(Vector(velMag * RandomNum(0.0F, splashDir), -RandomNum(0.0F, velMag))); + m_DeepHardness += (*itr)->GetMaterial()->GetIntegrity() * (*itr)->GetMaterial()->GetPixelDensity(); + g_MovableMan.AddParticle(*itr); + *itr = 0; + } else { + delete (*itr); + *itr = 0; + } + } + } + // EXPERIMENTAL + // Move the remaining out and away from teh terrain to help unsticking + MoveOutOfTerrain(g_MaterialSand); + return true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done before Travel(). Always call before -// calling Travel. -// Arguments: None. -// Return value: None. + return false; + } -void MOSRotating::PreTravel() { - MOSprite::PreTravel(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreTravel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does stuff that needs to be done before Travel(). Always call before + // calling Travel. + // Arguments: None. + // Return value: None. + + void MOSRotating::PreTravel() { + MOSprite::PreTravel(); #ifdef DRAW_MOID_LAYER - // If this is going slow enough, check for and redraw the MOID representations of any other MOSRotatings that may be overlapping this - if (m_GetsHitByMOs && m_HitsMOs && m_Vel.GetX() < 2.0F && m_Vel.GetY() < 2.0F) { g_MovableMan.RedrawOverlappingMOIDs(this); } + // If this is going slow enough, check for and redraw the MOID representations of any other MOSRotatings that may be overlapping this + if (m_GetsHitByMOs && m_HitsMOs && m_Vel.GetX() < 2.0F && m_Vel.GetY() < 2.0F) { + g_MovableMan.RedrawOverlappingMOIDs(this); + } #endif -} + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Travel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Travels this MovableObject, using its physical representation. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Travel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Travels this MovableObject, using its physical representation. + void MOSRotating::Travel() { + MOSprite::Travel(); -void MOSRotating::Travel() -{ - MOSprite::Travel(); + // Pinned objects don't travel! + if (m_PinStrength) + return; - // Pinned objects don't travel! - if (m_PinStrength) - return; + float deltaTime = g_TimerMan.GetDeltaTimeSecs(); - float deltaTime = g_TimerMan.GetDeltaTimeSecs(); + // Reset the travel impulse for this frame + m_TravelImpulse.Reset(); - // Reset the travel impulse for this frame - m_TravelImpulse.Reset(); + RTEAssert(m_pAtomGroup, "No AtomGroup defined for MOSRotating " + GetPresetName() + " in Travel!"); - RTEAssert(m_pAtomGroup, "No AtomGroup defined for MOSRotating " + GetPresetName() + " in Travel!"); + // Set the atom to ignore a certain MO, if set and applicable. + if (m_HitsMOs && m_pMOToNotHit && g_MovableMan.ValidMO(m_pMOToNotHit) && !m_MOIgnoreTimer.IsPastSimTimeLimit()) { + std::vector MOIDsNotToHit; + m_pMOToNotHit->GetMOIDs(MOIDsNotToHit); + for (const MOID& MOIDNotToHit: MOIDsNotToHit) { + m_pAtomGroup->AddMOIDToIgnore(MOIDNotToHit); + } + } - // Set the atom to ignore a certain MO, if set and applicable. - if (m_HitsMOs && m_pMOToNotHit && g_MovableMan.ValidMO(m_pMOToNotHit) && !m_MOIgnoreTimer.IsPastSimTimeLimit()) { - std::vector MOIDsNotToHit; - m_pMOToNotHit->GetMOIDs(MOIDsNotToHit); - for (const MOID &MOIDNotToHit : MOIDsNotToHit) { - m_pAtomGroup->AddMOIDToIgnore(MOIDNotToHit); - } - } + ///////////////////////////////// + // AtomGroup travel - ///////////////////////////////// - // AtomGroup travel + if (!IsTooFast()) + m_pAtomGroup->Travel(deltaTime, true, true, g_SceneMan.SceneIsLocked()); - if (!IsTooFast()) - m_pAtomGroup->Travel(deltaTime, true, true, g_SceneMan.SceneIsLocked()); + // Now clear out the ignore override for next frame + m_pAtomGroup->ClearMOIDIgnoreList(); + } - // Now clear out the ignore override for next frame - m_pAtomGroup->ClearMOIDIgnoreList(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PostTravel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does stuff that needs to be done after Update(). Always call after + // calling Update. + void MOSRotating::PostTravel() { + // Check for stupid velocities to gib instead of outright deletion that MOSprite::PostTravel() will do + if (IsTooFast()) { + GibThis(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PostTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done after Update(). Always call after -// calling Update. + // For some reason MovableObject lifetime death is in post travel rather than update, so this is done here too + if (m_GibAtEndOfLifetime && m_Lifetime && m_AgeTimer.GetElapsedSimTimeMS() > m_Lifetime) { + GibThis(); + } + + MOSprite::PostTravel(); -void MOSRotating::PostTravel() -{ - // Check for stupid velocities to gib instead of outright deletion that MOSprite::PostTravel() will do - if (IsTooFast()) { GibThis(); } + // Check if travel hits created enough impulse forces to gib this + if (m_GibImpulseLimit > 0) { + float impulseLimit = m_GibImpulseLimit; + if (m_WoundCountAffectsImpulseLimitRatio != 0 && m_GibWoundLimit > 0) { + impulseLimit *= 1.0F - (static_cast(m_Wounds.size()) / static_cast(m_GibWoundLimit)) * m_WoundCountAffectsImpulseLimitRatio; + } + if (m_TravelImpulse.MagnitudeIsGreaterThan(impulseLimit)) { + Vector totalImpulse(m_TravelImpulse.GetX(), m_TravelImpulse.GetY()); + DetachAttachablesFromImpulse(totalImpulse); + // Use the remainder of the impulses left over from detaching to gib the parent object. + if (totalImpulse.MagnitudeIsGreaterThan(impulseLimit)) { + GibThis(); + } + } + } + // Reset + m_DeepHardness = 0; + + // Check for deep penetration of the terrain and + // generate splash of MOPixels accordingly. + // TODO: don't hardcode the MOPixel limits! + if (g_MovableMan.IsMOSubtractionEnabled() && (m_ForceDeepCheck || m_DeepCheck)) + DeepCheck(true, 8, 50); + + Attachable* attachable; + for (auto attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end();) { + attachable = *attachableIterator; + RTEAssert(attachable, "Broken Attachable in PostTravel!"); + ++attachableIterator; + attachable->PostTravel(); + } + } - // For some reason MovableObject lifetime death is in post travel rather than update, so this is done here too - if (m_GibAtEndOfLifetime && m_Lifetime && m_AgeTimer.GetElapsedSimTimeMS() > m_Lifetime) { GibThis(); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MOSRotating. Supposed to be done every frame. - MOSprite::PostTravel(); + void MOSRotating::Update() { + MOSprite::Update(); - // Check if travel hits created enough impulse forces to gib this - if (m_GibImpulseLimit > 0) { - float impulseLimit = m_GibImpulseLimit; - if (m_WoundCountAffectsImpulseLimitRatio != 0 && m_GibWoundLimit > 0) { - impulseLimit *= 1.0F - (static_cast(m_Wounds.size()) / static_cast(m_GibWoundLimit)) * m_WoundCountAffectsImpulseLimitRatio; + if (m_InheritEffectRotAngle) { + m_EffectRotAngle = m_Rotation.GetRadAngle(); } - if (m_TravelImpulse.MagnitudeIsGreaterThan(impulseLimit)) { - Vector totalImpulse(m_TravelImpulse.GetX(), m_TravelImpulse.GetY()); - DetachAttachablesFromImpulse(totalImpulse); - // Use the remainder of the impulses left over from detaching to gib the parent object. - if (totalImpulse.MagnitudeIsGreaterThan(impulseLimit)) { GibThis(); } + + if (m_OrientToVel > 0 && m_Vel.GetLargest() > 5.0F) { + m_OrientToVel = std::clamp(m_OrientToVel, 0.0F, 1.0F); + + float velInfluence = std::clamp(m_OrientToVel < 1.0F ? m_Vel.GetMagnitude() / 100.0F : 1.0F, 0.0F, 1.0F); + float radsToGo = m_Rotation.GetRadAngleTo(m_Vel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); + m_Rotation += radsToGo * m_OrientToVel * velInfluence; + } + + for (auto woundItr = m_Wounds.begin(); woundItr != m_Wounds.end();) { + AEmitter* wound = *woundItr; + RTEAssert(wound && wound->IsAttachedTo(this), "Broken wound AEmitter in Update"); + wound->Update(); + + if (wound->IsSetToDelete() || (wound->GetLifetime() > 0 && wound->GetAge() > wound->GetLifetime())) { + std::iter_swap(woundItr, m_Wounds.end() - 1); + m_Wounds.pop_back(); + m_AttachableAndWoundMass -= wound->GetMass(); + delete wound; + } else { + Vector totalImpulseForce; + for (const std::pair& impulseForce: wound->GetImpulses()) { + totalImpulseForce += impulseForce.first; + } + totalImpulseForce *= wound->GetJointStiffness(); + + if (!totalImpulseForce.IsZero()) { + AddImpulseForce(totalImpulseForce, wound->GetApplyTransferredForcesAtOffset() ? wound->GetParentOffset() * m_Rotation * c_MPP : Vector()); + } + + wound->ClearImpulseForces(); + ++woundItr; + } + } + + for (auto attachableItr = m_Attachables.begin(); attachableItr != m_Attachables.end();) { + Attachable* attachable = *attachableItr; + ++attachableItr; + RTEAssert(attachable, "Broken Attachable in Update!"); + RTEAssert(attachable->IsAttached(), "Found Attachable on " + GetModuleAndPresetName() + " (" + attachable->GetModuleAndPresetName() + ") with no parent, this should never happen!"); + RTEAssert(attachable->IsAttachedTo(this), "Found Attachable on " + GetModuleAndPresetName() + " (" + attachable->GetModuleAndPresetName() + ") with another parent (" + attachable->GetParent()->GetModuleAndPresetName() + "), this should never happen!"); + attachable->Update(); + + if (attachable->IsAttachedTo(this) && attachable->IsSetToDelete()) { + RemoveAttachable(attachable, true, true); + } else if (attachable->IsAttachedTo(this) && !attachable->IsSetToDelete()) { + TransferForcesFromAttachable(attachable); + } } } - // Reset - m_DeepHardness = 0; - // Check for deep penetration of the terrain and - // generate splash of MOPixels accordingly. -// TODO: don't hardcode the MOPixel limits! - if (g_MovableMan.IsMOSubtractionEnabled() && (m_ForceDeepCheck || m_DeepCheck)) - DeepCheck(true, 8, 50); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void MOSRotating::PostUpdate() { + for (auto itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) { + (*itr)->PostUpdate(); + } - Attachable *attachable; - for (auto attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end(); ) { - attachable = *attachableIterator; - RTEAssert(attachable, "Broken Attachable in PostTravel!"); - ++attachableIterator; - attachable->PostTravel(); - } -} + for (auto itr = m_Attachables.begin(); itr != m_Attachables.end(); ++itr) { + (*itr)->PostUpdate(); + } + MovableObject::PostUpdate(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool MOSRotating::DrawMOIDIfOverlapping(MovableObject* pOverlapMO) { + if (pOverlapMO == this || !m_GetsHitByMOs || !pOverlapMO->GetsHitByMOs()) { + return false; + } + + if (m_IgnoresTeamHits && pOverlapMO->IgnoresTeamHits() && m_Team == pOverlapMO->GetTeam()) { + return false; + } + + if (g_SceneMan.ShortestDistance(m_Pos, pOverlapMO->GetPos(), g_SceneMan.SceneWrapsX()).MagnitudeIsLessThan(GetRadius() + pOverlapMO->GetRadius())) { + Draw(g_SceneMan.GetMOIDBitmap(), Vector(), g_DrawMOID, true); + return true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MOSRotating. Supposed to be done every frame. - -void MOSRotating::Update() { - MOSprite::Update(); - - if (m_InheritEffectRotAngle) { m_EffectRotAngle = m_Rotation.GetRadAngle(); } - - if (m_OrientToVel > 0 && m_Vel.GetLargest() > 5.0F) { - m_OrientToVel = std::clamp(m_OrientToVel, 0.0F, 1.0F); - - float velInfluence = std::clamp(m_OrientToVel < 1.0F ? m_Vel.GetMagnitude() / 100.0F : 1.0F, 0.0F, 1.0F); - float radsToGo = m_Rotation.GetRadAngleTo(m_Vel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); - m_Rotation += radsToGo * m_OrientToVel * velInfluence; - } - - for (auto woundItr = m_Wounds.begin(); woundItr != m_Wounds.end(); ) { - AEmitter* wound = *woundItr; - RTEAssert(wound && wound->IsAttachedTo(this), "Broken wound AEmitter in Update"); - wound->Update(); - - if (wound->IsSetToDelete() || (wound->GetLifetime() > 0 && wound->GetAge() > wound->GetLifetime())) { - std::iter_swap(woundItr, m_Wounds.end() - 1); - m_Wounds.pop_back(); - m_AttachableAndWoundMass -= wound->GetMass(); - delete wound; - } else { - Vector totalImpulseForce; - for (const std::pair& impulseForce : wound->GetImpulses()) { - totalImpulseForce += impulseForce.first; - } - totalImpulseForce *= wound->GetJointStiffness(); - - if (!totalImpulseForce.IsZero()) { - AddImpulseForce(totalImpulseForce, wound->GetApplyTransferredForcesAtOffset() ? wound->GetParentOffset() * m_Rotation * c_MPP : Vector()); - } - - wound->ClearImpulseForces(); - ++woundItr; - } - } - - for (auto attachableItr = m_Attachables.begin(); attachableItr != m_Attachables.end(); ) { - Attachable* attachable = *attachableItr; - ++attachableItr; - RTEAssert(attachable, "Broken Attachable in Update!"); - RTEAssert(attachable->IsAttached(), "Found Attachable on " + GetModuleAndPresetName() + " (" + attachable->GetModuleAndPresetName() + ") with no parent, this should never happen!"); - RTEAssert(attachable->IsAttachedTo(this), "Found Attachable on " + GetModuleAndPresetName() + " (" + attachable->GetModuleAndPresetName() + ") with another parent (" + attachable->GetParent()->GetModuleAndPresetName() + "), this should never happen!"); - attachable->Update(); - - if (attachable->IsAttachedTo(this) && attachable->IsSetToDelete()) { - RemoveAttachable(attachable, true, true); - } else if (attachable->IsAttachedTo(this) && !attachable->IsSetToDelete()) { - TransferForcesFromAttachable(attachable); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MOSRotating::PostUpdate() { - for (auto itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) { - (*itr)->PostUpdate(); - } - - for (auto itr = m_Attachables.begin(); itr != m_Attachables.end(); ++itr) { - (*itr)->PostUpdate(); - } - - MovableObject::PostUpdate(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool MOSRotating::DrawMOIDIfOverlapping(MovableObject *pOverlapMO) { - if (pOverlapMO == this || !m_GetsHitByMOs || !pOverlapMO->GetsHitByMOs()) { - return false; - } - - if (m_IgnoresTeamHits && pOverlapMO->IgnoresTeamHits() && m_Team == pOverlapMO->GetTeam()) { - return false; - } - - if (g_SceneMan.ShortestDistance(m_Pos, pOverlapMO->GetPos(), g_SceneMan.SceneWrapsX()).MagnitudeIsLessThan(GetRadius() + pOverlapMO->GetRadius())) { - Draw(g_SceneMan.GetMOIDBitmap(), Vector(), g_DrawMOID, true); - return true; - } - - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -//TODO This should just be defined in MOSR instead of having an empty definition in MO. MOSR would need to override UpdateMOID accordingly, but this would clean things up a little. -void MOSRotating::UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID, bool makeNewMOID) { - MOSprite::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); - - for (Attachable *attachable : m_Attachables) { - // Anything that doesn't get hit by MOs doesn't need an ID, since that's only actually used for collision stuff. - if (attachable->GetsHitByMOs()) { attachable->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool MOSRotating::AttachableIsHardcoded(const Attachable *attachableToCheck) const { - if (attachableToCheck->GetParent() != this) { return false; } - unsigned long attachableUniqueID = attachableToCheck->GetUniqueID(); - return m_HardcodedAttachableUniqueIDsAndRemovers.find(attachableUniqueID) != m_HardcodedAttachableUniqueIDsAndRemovers.end() || m_HardcodedAttachableUniqueIDsAndSetters.find(attachableUniqueID) != m_HardcodedAttachableUniqueIDsAndSetters.end(); -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // TODO This should just be defined in MOSR instead of having an empty definition in MO. MOSR would need to override UpdateMOID accordingly, but this would clean things up a little. + void MOSRotating::UpdateChildMOIDs(std::vector& MOIDIndex, MOID rootMOID, bool makeNewMOID) { + MOSprite::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); -void MOSRotating::AddAttachable(Attachable *attachable) { - if (attachable) { AddAttachable(attachable, attachable->GetParentOffset()); } -} + for (Attachable* attachable: m_Attachables) { + // Anything that doesn't get hit by MOs doesn't need an ID, since that's only actually used for collision stuff. + if (attachable->GetsHitByMOs()) { + attachable->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); + } + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MOSRotating::AddAttachable(Attachable *attachable, const Vector& parentOffsetToSet) { - if (attachable) { - RTEAssert(!attachable->IsAttached(), "Tried to add Attachable " + attachable->GetModuleAndPresetName() + " but it already has a parent, " + (attachable->IsAttached() ? attachable->GetParent()->GetModuleAndPresetName() : "ERROR") + "."); - if (g_MovableMan.ValidMO(attachable)) { g_MovableMan.RemoveMO(attachable); } - attachable->SetParentOffset(parentOffsetToSet); - attachable->SetParent(this); - m_AttachableAndWoundMass += attachable->GetMass(); - HandlePotentialRadiusAffectingAttachable(attachable); - m_Attachables.push_back(attachable); + bool MOSRotating::AttachableIsHardcoded(const Attachable* attachableToCheck) const { + if (attachableToCheck->GetParent() != this) { + return false; + } + + unsigned long attachableUniqueID = attachableToCheck->GetUniqueID(); + return m_HardcodedAttachableUniqueIDsAndRemovers.find(attachableUniqueID) != m_HardcodedAttachableUniqueIDsAndRemovers.end() || m_HardcodedAttachableUniqueIDsAndSetters.find(attachableUniqueID) != m_HardcodedAttachableUniqueIDsAndSetters.end(); } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Attachable * MOSRotating::RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds) { - if (MovableObject *attachableAsMovableObject = g_MovableMan.FindObjectByUniqueID(attachableUniqueID)) { - return RemoveAttachable(dynamic_cast(attachableAsMovableObject), addToMovableMan, addBreakWounds); - } - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -Attachable * MOSRotating::RemoveAttachable(Attachable *attachable, bool addToMovableMan, bool addBreakWounds) { - if (!attachable || !attachable->IsAttached()) { - return attachable; - } - RTEAssert(attachable->IsAttachedTo(this), "Tried to remove Attachable " + attachable->GetPresetNameAndUniqueID() + " from presumed parent " + GetPresetNameAndUniqueID() + ", but it had a different parent (" + (attachable->GetParent() ? attachable->GetParent()->GetPresetNameAndUniqueID() : "ERROR") + "). This should never happen!"); - - if (!m_Attachables.empty()) { m_Attachables.remove(attachable); } - attachable->SetParent(nullptr); - m_AttachableAndWoundMass -= attachable->GetMass(); - - std::unordered_map>::iterator hardcodedAttachableMapEntry = m_HardcodedAttachableUniqueIDsAndSetters.find(attachable->GetUniqueID()); - if (hardcodedAttachableMapEntry != m_HardcodedAttachableUniqueIDsAndSetters.end()) { - hardcodedAttachableMapEntry->second(this, nullptr); - m_HardcodedAttachableUniqueIDsAndSetters.erase(hardcodedAttachableMapEntry); - } - - // Note, this version handles cases where you can't pass null to a setter cause you're calling a remover function, i.e. when dealing with hardcoded Attachable lists. - hardcodedAttachableMapEntry = m_HardcodedAttachableUniqueIDsAndRemovers.find(attachable->GetUniqueID()); - if (hardcodedAttachableMapEntry != m_HardcodedAttachableUniqueIDsAndRemovers.end()) { - hardcodedAttachableMapEntry->second(this, attachable); - m_HardcodedAttachableUniqueIDsAndRemovers.erase(hardcodedAttachableMapEntry); - } - - if (addBreakWounds) { - if (!m_ToDelete && attachable->GetParentBreakWound()) { - AEmitter *parentBreakWound = dynamic_cast(attachable->GetParentBreakWound()->Clone()); - if (parentBreakWound) { - parentBreakWound->SetDrawnAfterParent(attachable->IsDrawnAfterParent()); - parentBreakWound->SetInheritedRotAngleOffset((attachable->GetParentOffset() * m_Rotation).GetAbsRadAngle()); - AddWound(parentBreakWound, attachable->GetParentOffset(), false); - parentBreakWound = nullptr; - } - } - if (!attachable->IsSetToDelete() && attachable->GetBreakWound()) { - AEmitter *childBreakWound = dynamic_cast(attachable->GetBreakWound()->Clone()); - if (childBreakWound) { - childBreakWound->SetInheritedRotAngleOffset(attachable->GetJointOffset().GetAbsRadAngle()); - attachable->AddWound(childBreakWound, attachable->GetJointOffset()); - childBreakWound = nullptr; - } - } - } - - if (attachable == m_RadiusAffectingAttachable) { - m_RadiusAffectingAttachable = nullptr; - m_FarthestAttachableDistanceAndRadius = 0; - std::for_each(m_Attachables.begin(), m_Attachables.end(), [this](const Attachable *attachableToCheck) { HandlePotentialRadiusAffectingAttachable(attachableToCheck); }); - - Attachable *thisAsAttachable = dynamic_cast(this); - if (thisAsAttachable && m_Attachables.empty() && thisAsAttachable->IsAttached()) { - thisAsAttachable->m_Parent->HandlePotentialRadiusAffectingAttachable(thisAsAttachable); - } - } - - if (attachable->GetDeleteWhenRemovedFromParent()) { attachable->SetToDelete(); } - if (addToMovableMan || attachable->IsSetToDelete()) { - g_MovableMan.AddMO(attachable); - if (attachable->GetGibWhenRemovedFromParent()) { attachable->GibThis(); } - return nullptr; - } - - return attachable; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MOSRotating::RemoveAndDeleteAttachable(Attachable *attachable) { - attachable->SetToDelete(); - RemoveAttachable(attachable); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MOSRotating::RemoveOrDestroyAllAttachables(bool destroy) { - Attachable *attachable; - for (auto attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end(); ) { - attachable = *attachableIterator; - RTEAssert(attachable, "Broken Attachable!"); - ++attachableIterator; - - if (destroy) { - RemoveAndDeleteAttachable(attachable); - } else { - RemoveAttachable(attachable, true, true); - } - } - m_Attachables.clear(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MOSRotating::GetMOIDs(std::vector &MOIDs) const { - MOIDs.reserve(GetMOIDFootprint()); - MOSprite::GetMOIDs(MOIDs); - for (const Attachable *attachable : m_Attachables) { - if (attachable->GetsHitByMOs()) { attachable->GetMOIDs(MOIDs); } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MOSRotating::SetWhichMOToNotHit(MovableObject *moToNotHit, float forHowLong) { - MOSprite::SetWhichMOToNotHit(moToNotHit, forHowLong); - for (Attachable *attachable : m_Attachables) { attachable->SetWhichMOToNotHit(moToNotHit, forHowLong); } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MOSRotating's current graphical representation to a -// BITMAP of choice. - -void MOSRotating::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { - RTEAssert(!m_aSprite.empty(), "No sprite bitmaps loaded to draw!"); - RTEAssert(m_Frame >= 0 && m_Frame < m_FrameCount, "Frame is out of bounds!"); - - // Only draw MOID if this has a valid MOID assigned to it - if (mode == g_DrawMOID && m_MOID == g_NoMOID) { - return; - } - - if (mode == g_DrawColor && !m_FlashWhiteTimer.IsPastRealTimeLimit()) { mode = g_DrawWhite; } - - // Draw all the attached wound emitters, and only if the mode is g_DrawColor and not onlyphysical - // Only draw attachables and emitters which are not drawn after parent, so we draw them before - if (mode == g_DrawColor || (!onlyPhysical && mode == g_DrawMaterial)) { - for (const AEmitter *woundToDraw : m_Wounds) { - if (!woundToDraw->IsDrawnAfterParent()) { woundToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } - } - } - - // Draw all the attached attachables - for (const Attachable *attachableToDraw : m_Attachables) { - if (!attachableToDraw->IsDrawnAfterParent() && attachableToDraw->IsDrawnNormallyByParent()) { attachableToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } - } - - BITMAP * pTempBitmap = m_pTempBitmap; - BITMAP * pFlipBitmap = m_pFlipBitmap; - int keyColor = g_MaskColor; - - // Switch to non 8-bit drawing mode if we're drawing onto MO layer - if (mode == g_DrawMOID || mode == g_DrawNoMOID) { - pTempBitmap = m_pTempBitmapS; - pFlipBitmap = m_pFlipBitmapS; - keyColor = g_MOIDMaskColor; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MOSRotating::AddAttachable(Attachable* attachable) { + if (attachable) { + AddAttachable(attachable, attachable->GetParentOffset()); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MOSRotating::AddAttachable(Attachable* attachable, const Vector& parentOffsetToSet) { + if (attachable) { + RTEAssert(!attachable->IsAttached(), "Tried to add Attachable " + attachable->GetModuleAndPresetName() + " but it already has a parent, " + (attachable->IsAttached() ? attachable->GetParent()->GetModuleAndPresetName() : "ERROR") + "."); + if (g_MovableMan.ValidMO(attachable)) { + g_MovableMan.RemoveMO(attachable); + } + attachable->SetParentOffset(parentOffsetToSet); + attachable->SetParent(this); + m_AttachableAndWoundMass += attachable->GetMass(); + HandlePotentialRadiusAffectingAttachable(attachable); + m_Attachables.push_back(attachable); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Attachable* MOSRotating::RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds) { + if (MovableObject* attachableAsMovableObject = g_MovableMan.FindObjectByUniqueID(attachableUniqueID)) { + return RemoveAttachable(dynamic_cast(attachableAsMovableObject), addToMovableMan, addBreakWounds); + } + return nullptr; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Attachable* MOSRotating::RemoveAttachable(Attachable* attachable, bool addToMovableMan, bool addBreakWounds) { + if (!attachable || !attachable->IsAttached()) { + return attachable; + } + RTEAssert(attachable->IsAttachedTo(this), "Tried to remove Attachable " + attachable->GetPresetNameAndUniqueID() + " from presumed parent " + GetPresetNameAndUniqueID() + ", but it had a different parent (" + (attachable->GetParent() ? attachable->GetParent()->GetPresetNameAndUniqueID() : "ERROR") + "). This should never happen!"); + + if (!m_Attachables.empty()) { + m_Attachables.remove(attachable); + } + attachable->SetParent(nullptr); + m_AttachableAndWoundMass -= attachable->GetMass(); + + std::unordered_map>::iterator hardcodedAttachableMapEntry = m_HardcodedAttachableUniqueIDsAndSetters.find(attachable->GetUniqueID()); + if (hardcodedAttachableMapEntry != m_HardcodedAttachableUniqueIDsAndSetters.end()) { + hardcodedAttachableMapEntry->second(this, nullptr); + m_HardcodedAttachableUniqueIDsAndSetters.erase(hardcodedAttachableMapEntry); + } + + // Note, this version handles cases where you can't pass null to a setter cause you're calling a remover function, i.e. when dealing with hardcoded Attachable lists. + hardcodedAttachableMapEntry = m_HardcodedAttachableUniqueIDsAndRemovers.find(attachable->GetUniqueID()); + if (hardcodedAttachableMapEntry != m_HardcodedAttachableUniqueIDsAndRemovers.end()) { + hardcodedAttachableMapEntry->second(this, attachable); + m_HardcodedAttachableUniqueIDsAndRemovers.erase(hardcodedAttachableMapEntry); + } + + if (addBreakWounds) { + if (!m_ToDelete && attachable->GetParentBreakWound()) { + AEmitter* parentBreakWound = dynamic_cast(attachable->GetParentBreakWound()->Clone()); + if (parentBreakWound) { + parentBreakWound->SetDrawnAfterParent(attachable->IsDrawnAfterParent()); + parentBreakWound->SetInheritedRotAngleOffset((attachable->GetParentOffset() * m_Rotation).GetAbsRadAngle()); + AddWound(parentBreakWound, attachable->GetParentOffset(), false); + parentBreakWound = nullptr; + } + } + if (!attachable->IsSetToDelete() && attachable->GetBreakWound()) { + AEmitter* childBreakWound = dynamic_cast(attachable->GetBreakWound()->Clone()); + if (childBreakWound) { + childBreakWound->SetInheritedRotAngleOffset(attachable->GetJointOffset().GetAbsRadAngle()); + attachable->AddWound(childBreakWound, attachable->GetJointOffset()); + childBreakWound = nullptr; + } + } + } + + if (attachable == m_RadiusAffectingAttachable) { + m_RadiusAffectingAttachable = nullptr; + m_FarthestAttachableDistanceAndRadius = 0; + std::for_each(m_Attachables.begin(), m_Attachables.end(), [this](const Attachable* attachableToCheck) { HandlePotentialRadiusAffectingAttachable(attachableToCheck); }); + + Attachable* thisAsAttachable = dynamic_cast(this); + if (thisAsAttachable && m_Attachables.empty() && thisAsAttachable->IsAttached()) { + thisAsAttachable->m_Parent->HandlePotentialRadiusAffectingAttachable(thisAsAttachable); + } + } + + if (attachable->GetDeleteWhenRemovedFromParent()) { + attachable->SetToDelete(); + } + if (addToMovableMan || attachable->IsSetToDelete()) { + g_MovableMan.AddMO(attachable); + if (attachable->GetGibWhenRemovedFromParent()) { + attachable->GibThis(); + } + return nullptr; + } + + return attachable; } - Vector spritePos(m_Pos.GetRounded() - targetPos); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (m_Recoiled) { - spritePos += m_RecoilOffset; - } + void MOSRotating::RemoveAndDeleteAttachable(Attachable* attachable) { + attachable->SetToDelete(); + RemoveAttachable(attachable); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MOSRotating::RemoveOrDestroyAllAttachables(bool destroy) { + Attachable* attachable; + for (auto attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end();) { + attachable = *attachableIterator; + RTEAssert(attachable, "Broken Attachable!"); + ++attachableIterator; - // If we're drawing a material silhouette, then create an intermediate material bitmap as well + if (destroy) { + RemoveAndDeleteAttachable(attachable); + } else { + RemoveAttachable(attachable, true, true); + } + } + m_Attachables.clear(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MOSRotating::GetMOIDs(std::vector& MOIDs) const { + MOIDs.reserve(GetMOIDFootprint()); + MOSprite::GetMOIDs(MOIDs); + for (const Attachable* attachable: m_Attachables) { + if (attachable->GetsHitByMOs()) { + attachable->GetMOIDs(MOIDs); + } + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MOSRotating::SetWhichMOToNotHit(MovableObject* moToNotHit, float forHowLong) { + MOSprite::SetWhichMOToNotHit(moToNotHit, forHowLong); + for (Attachable* attachable: m_Attachables) { + attachable->SetWhichMOToNotHit(moToNotHit, forHowLong); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this MOSRotating's current graphical representation to a + // BITMAP of choice. + + void MOSRotating::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { + RTEAssert(!m_aSprite.empty(), "No sprite bitmaps loaded to draw!"); + RTEAssert(m_Frame >= 0 && m_Frame < m_FrameCount, "Frame is out of bounds!"); + + // Only draw MOID if this has a valid MOID assigned to it + if (mode == g_DrawMOID && m_MOID == g_NoMOID) { + return; + } + + if (mode == g_DrawColor && !m_FlashWhiteTimer.IsPastRealTimeLimit()) { + mode = g_DrawWhite; + } + + // Draw all the attached wound emitters, and only if the mode is g_DrawColor and not onlyphysical + // Only draw attachables and emitters which are not drawn after parent, so we draw them before + if (mode == g_DrawColor || (!onlyPhysical && mode == g_DrawMaterial)) { + for (const AEmitter* woundToDraw: m_Wounds) { + if (!woundToDraw->IsDrawnAfterParent()) { + woundToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } + } + } + + // Draw all the attached attachables + for (const Attachable* attachableToDraw: m_Attachables) { + if (!attachableToDraw->IsDrawnAfterParent() && attachableToDraw->IsDrawnNormallyByParent()) { + attachableToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } + } + + BITMAP* pTempBitmap = m_pTempBitmap; + BITMAP* pFlipBitmap = m_pFlipBitmap; + int keyColor = g_MaskColor; + + // Switch to non 8-bit drawing mode if we're drawing onto MO layer + if (mode == g_DrawMOID || mode == g_DrawNoMOID) { + pTempBitmap = m_pTempBitmapS; + pFlipBitmap = m_pFlipBitmapS; + keyColor = g_MOIDMaskColor; + } + + Vector spritePos(m_Pos.GetRounded() - targetPos); + + if (m_Recoiled) { + spritePos += m_RecoilOffset; + } + + // If we're drawing a material silhouette, then create an intermediate material bitmap as well #ifdef DRAW_MOID_LAYER - bool intermediateBitmapUsed = mode != g_DrawColor && mode != g_DrawTrans; + bool intermediateBitmapUsed = mode != g_DrawColor && mode != g_DrawTrans; #else - bool intermediateBitmapUsed = mode != g_DrawColor && mode != g_DrawTrans && mode != g_DrawMOID; - RTEAssert(mode != g_DrawNoMOID, "DrawNoMOID drawing mode used with no MOID layer!"); + bool intermediateBitmapUsed = mode != g_DrawColor && mode != g_DrawTrans && mode != g_DrawMOID; + RTEAssert(mode != g_DrawNoMOID, "DrawNoMOID drawing mode used with no MOID layer!"); #endif - if (intermediateBitmapUsed) { - clear_to_color(pTempBitmap, keyColor); - - // TODO: Fix that MaterialAir and KeyColor don't work at all because they're drawing 0 to a field of 0's - // Draw the requested material silhouette on the material bitmap - if (mode == g_DrawMaterial) { - draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, m_SettleMaterialDisabled ? GetMaterial()->GetIndex() : GetMaterial()->GetSettleMaterial(), -1); - } else if (mode == g_DrawWhite) { - draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_WhiteColor, -1); - } else if (mode == g_DrawMOID) { - draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, m_MOID, -1); - } else if (mode == g_DrawNoMOID) { - draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_NoMOID, -1); - } else if (mode == g_DrawDoor) { - draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_MaterialDoor, -1); - } else { - RTEAbort("Unknown draw mode selected in MOSRotating::Draw()!"); - } - } + if (intermediateBitmapUsed) { + clear_to_color(pTempBitmap, keyColor); + + // TODO: Fix that MaterialAir and KeyColor don't work at all because they're drawing 0 to a field of 0's + // Draw the requested material silhouette on the material bitmap + if (mode == g_DrawMaterial) { + draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, m_SettleMaterialDisabled ? GetMaterial()->GetIndex() : GetMaterial()->GetSettleMaterial(), -1); + } else if (mode == g_DrawWhite) { + draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_WhiteColor, -1); + } else if (mode == g_DrawMOID) { + draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, m_MOID, -1); + } else if (mode == g_DrawNoMOID) { + draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_NoMOID, -1); + } else if (mode == g_DrawDoor) { + draw_character_ex(pTempBitmap, m_aSprite[m_Frame], 0, 0, g_MaterialDoor, -1); + } else { + RTEAbort("Unknown draw mode selected in MOSRotating::Draw()!"); + } + } #ifdef DRAW_MOID_LAYER - bool needsWrap = true; + bool needsWrap = true; #else - bool needsWrap = mode != g_DrawMOID; + bool needsWrap = mode != g_DrawMOID; #endif + // Take care of wrapping situations + Vector aDrawPos[4]; + int passes = 1; + + aDrawPos[0] = spritePos; + + if (needsWrap && g_SceneMan.SceneWrapsX()) { + // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap + if (targetPos.IsZero() && m_WrapDoubleDraw) { + if (spritePos.m_X < m_SpriteDiameter) { + aDrawPos[passes] = spritePos; + aDrawPos[passes].m_X += pTargetBitmap->w; + passes++; + } else if (spritePos.m_X > pTargetBitmap->w - m_SpriteDiameter) { + aDrawPos[passes] = spritePos; + aDrawPos[passes].m_X -= pTargetBitmap->w; + passes++; + } + } else if (m_WrapDoubleDraw) { + // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam + if (targetPos.m_X < 0) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X -= g_SceneMan.GetSceneWidth(); + passes++; + } + if (targetPos.m_X + pTargetBitmap->w > g_SceneMan.GetSceneWidth()) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X += g_SceneMan.GetSceneWidth(); + passes++; + } + } + } - // Take care of wrapping situations - Vector aDrawPos[4]; - int passes = 1; - - aDrawPos[0] = spritePos; - - if (needsWrap && g_SceneMan.SceneWrapsX()) { - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero() && m_WrapDoubleDraw) { - if (spritePos.m_X < m_SpriteDiameter) { - aDrawPos[passes] = spritePos; - aDrawPos[passes].m_X += pTargetBitmap->w; - passes++; - } else if (spritePos.m_X > pTargetBitmap->w - m_SpriteDiameter) { - aDrawPos[passes] = spritePos; - aDrawPos[passes].m_X -= pTargetBitmap->w; - passes++; - } - } else if (m_WrapDoubleDraw) { - // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam - if (targetPos.m_X < 0) { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= g_SceneMan.GetSceneWidth(); - passes++; - } - if (targetPos.m_X + pTargetBitmap->w > g_SceneMan.GetSceneWidth()) { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += g_SceneMan.GetSceneWidth(); - passes++; - } - } - } - - if (m_HFlipped && pFlipBitmap) { + if (m_HFlipped && pFlipBitmap) { #ifdef DRAW_MOID_LAYER - bool drawIntermediate = true; + bool drawIntermediate = true; #else - bool drawIntermediate = mode != g_DrawMOID; + bool drawIntermediate = mode != g_DrawMOID; #endif - if (drawIntermediate) { - // Don't size the intermediate bitmaps to the m_Scale, because the scaling happens after they are done - clear_to_color(pFlipBitmap, keyColor); + if (drawIntermediate) { + // Don't size the intermediate bitmaps to the m_Scale, because the scaling happens after they are done + clear_to_color(pFlipBitmap, keyColor); - // Draw either the source color bitmap or the intermediate material bitmap onto the intermediate flipping bitmap - if (mode == g_DrawColor || mode == g_DrawTrans) { - draw_sprite_h_flip(pFlipBitmap, m_aSprite[m_Frame], 0, 0); - } else { - // If using the temp bitmap (which is always larger than the sprite) make sure the flipped image ends up in the upper right corner as if it was just as small as the sprite bitmap - draw_sprite_h_flip(pFlipBitmap, pTempBitmap, -(pTempBitmap->w - m_aSprite[m_Frame]->w), 0); - } - } - - if (mode == g_DrawTrans) { - clear_to_color(pTempBitmap, keyColor); - - // Draw the rotated thing onto the intermediate bitmap so its COM position aligns with the middle of the temp bitmap. - // The temp bitmap should be able to hold the full size since it is larger than the max diameter. - // Take into account the h-flipped pivot point - pivot_scaled_sprite(pTempBitmap, pFlipBitmap, pTempBitmap->w / 2, pTempBitmap->h / 2, pFlipBitmap->w + m_SpriteOffset.m_X, -(m_SpriteOffset.m_Y), ftofix(m_Rotation.GetAllegroAngle()), ftofix(m_Scale)); - - // Draw the now rotated object's temporary bitmap onto the final drawing bitmap with transperency - // Do the passes loop in here so the intermediate drawing doesn't get done multiple times - for (int i = 0; i < passes; ++i) { - int spriteX = aDrawPos[i].GetFloorIntX() - (pTempBitmap->w / 2); - int spriteY = aDrawPos[i].GetFloorIntY() - (pTempBitmap->h / 2); - g_SceneMan.RegisterDrawing(pTargetBitmap, g_NoMOID, spriteX, spriteY, spriteX + pTempBitmap->w, spriteY + pTempBitmap->h); - draw_trans_sprite(pTargetBitmap, pTempBitmap, spriteX, spriteY); - } - } else { - // Do the passes loop in here so the flipping operation doesn't get done multiple times - for (int i = 0; i < passes; ++i) { - int spriteX = aDrawPos[i].GetFloorIntX(); - int spriteY = aDrawPos[i].GetFloorIntY(); - g_SceneMan.RegisterDrawing(pTargetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, spriteX + m_SpriteOffset.m_X - (m_SpriteRadius * m_Scale), spriteY + m_SpriteOffset.m_Y - (m_SpriteRadius * m_Scale), spriteX - m_SpriteOffset.m_X + (m_SpriteRadius * m_Scale), spriteY - m_SpriteOffset.m_Y + (m_SpriteRadius * m_Scale)); + // Draw either the source color bitmap or the intermediate material bitmap onto the intermediate flipping bitmap + if (mode == g_DrawColor || mode == g_DrawTrans) { + draw_sprite_h_flip(pFlipBitmap, m_aSprite[m_Frame], 0, 0); + } else { + // If using the temp bitmap (which is always larger than the sprite) make sure the flipped image ends up in the upper right corner as if it was just as small as the sprite bitmap + draw_sprite_h_flip(pFlipBitmap, pTempBitmap, -(pTempBitmap->w - m_aSprite[m_Frame]->w), 0); + } + } + + if (mode == g_DrawTrans) { + clear_to_color(pTempBitmap, keyColor); + + // Draw the rotated thing onto the intermediate bitmap so its COM position aligns with the middle of the temp bitmap. + // The temp bitmap should be able to hold the full size since it is larger than the max diameter. + // Take into account the h-flipped pivot point + pivot_scaled_sprite(pTempBitmap, pFlipBitmap, pTempBitmap->w / 2, pTempBitmap->h / 2, pFlipBitmap->w + m_SpriteOffset.m_X, -(m_SpriteOffset.m_Y), ftofix(m_Rotation.GetAllegroAngle()), ftofix(m_Scale)); + + // Draw the now rotated object's temporary bitmap onto the final drawing bitmap with transperency + // Do the passes loop in here so the intermediate drawing doesn't get done multiple times + for (int i = 0; i < passes; ++i) { + int spriteX = aDrawPos[i].GetFloorIntX() - (pTempBitmap->w / 2); + int spriteY = aDrawPos[i].GetFloorIntY() - (pTempBitmap->h / 2); + g_SceneMan.RegisterDrawing(pTargetBitmap, g_NoMOID, spriteX, spriteY, spriteX + pTempBitmap->w, spriteY + pTempBitmap->h); + draw_trans_sprite(pTargetBitmap, pTempBitmap, spriteX, spriteY); + } + } else { + // Do the passes loop in here so the flipping operation doesn't get done multiple times + for (int i = 0; i < passes; ++i) { + int spriteX = aDrawPos[i].GetFloorIntX(); + int spriteY = aDrawPos[i].GetFloorIntY(); + g_SceneMan.RegisterDrawing(pTargetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, spriteX + m_SpriteOffset.m_X - (m_SpriteRadius * m_Scale), spriteY + m_SpriteOffset.m_Y - (m_SpriteRadius * m_Scale), spriteX - m_SpriteOffset.m_X + (m_SpriteRadius * m_Scale), spriteY - m_SpriteOffset.m_Y + (m_SpriteRadius * m_Scale)); #ifndef DRAW_MOID_LAYER - if (mode == g_DrawMOID) { - continue; - } + if (mode == g_DrawMOID) { + continue; + } #endif - // Take into account the h-flipped pivot point - pivot_scaled_sprite(pTargetBitmap, pFlipBitmap, spriteX, spriteY, pFlipBitmap->w + m_SpriteOffset.GetFloorIntX(), -(m_SpriteOffset.GetFloorIntY()), ftofix(m_Rotation.GetAllegroAngle()), ftofix(m_Scale)); - } - } - } else { - if (mode == g_DrawTrans) { - clear_to_color(pTempBitmap, keyColor); - - // Draw the rotated thing onto the intermediate bitmap so its COM position aligns with the middle of the temp bitmap. - // The temp bitmap should be able to hold the full size since it is larger than the max diameter. - // Take into account the h-flipped pivot point - pivot_scaled_sprite(pTempBitmap, m_aSprite[m_Frame], pTempBitmap->w / 2, pTempBitmap->h / 2, -m_SpriteOffset.GetFloorIntX(), -m_SpriteOffset.GetFloorIntY(), ftofix(m_Rotation.GetAllegroAngle()), ftofix(m_Scale)); - - // Draw the now rotated object's temporary bitmap onto the final drawing bitmap with transperency - // Do the passes loop in here so the intermediate drawing doesn't get done multiple times - for (int i = 0; i < passes; ++i) { - int spriteX = aDrawPos[i].GetFloorIntX() - (pTempBitmap->w / 2); - int spriteY = aDrawPos[i].GetFloorIntY() - (pTempBitmap->h / 2); - g_SceneMan.RegisterDrawing(pTargetBitmap, g_NoMOID, spriteX, spriteY, spriteX + pTempBitmap->w, spriteY + pTempBitmap->h); - draw_trans_sprite(pTargetBitmap, pTempBitmap, spriteX, spriteY); - } - } else { - for (int i = 0; i < passes; ++i) { - int spriteX = aDrawPos[i].GetFloorIntX(); - int spriteY = aDrawPos[i].GetFloorIntY(); - g_SceneMan.RegisterDrawing(pTargetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, spriteX + m_SpriteOffset.m_X - (m_SpriteRadius * m_Scale), spriteY + m_SpriteOffset.m_Y - (m_SpriteRadius * m_Scale), spriteX - m_SpriteOffset.m_X + (m_SpriteRadius * m_Scale), spriteY - m_SpriteOffset.m_Y + (m_SpriteRadius * m_Scale)); + // Take into account the h-flipped pivot point + pivot_scaled_sprite(pTargetBitmap, pFlipBitmap, spriteX, spriteY, pFlipBitmap->w + m_SpriteOffset.GetFloorIntX(), -(m_SpriteOffset.GetFloorIntY()), ftofix(m_Rotation.GetAllegroAngle()), ftofix(m_Scale)); + } + } + } else { + if (mode == g_DrawTrans) { + clear_to_color(pTempBitmap, keyColor); + + // Draw the rotated thing onto the intermediate bitmap so its COM position aligns with the middle of the temp bitmap. + // The temp bitmap should be able to hold the full size since it is larger than the max diameter. + // Take into account the h-flipped pivot point + pivot_scaled_sprite(pTempBitmap, m_aSprite[m_Frame], pTempBitmap->w / 2, pTempBitmap->h / 2, -m_SpriteOffset.GetFloorIntX(), -m_SpriteOffset.GetFloorIntY(), ftofix(m_Rotation.GetAllegroAngle()), ftofix(m_Scale)); + + // Draw the now rotated object's temporary bitmap onto the final drawing bitmap with transperency + // Do the passes loop in here so the intermediate drawing doesn't get done multiple times + for (int i = 0; i < passes; ++i) { + int spriteX = aDrawPos[i].GetFloorIntX() - (pTempBitmap->w / 2); + int spriteY = aDrawPos[i].GetFloorIntY() - (pTempBitmap->h / 2); + g_SceneMan.RegisterDrawing(pTargetBitmap, g_NoMOID, spriteX, spriteY, spriteX + pTempBitmap->w, spriteY + pTempBitmap->h); + draw_trans_sprite(pTargetBitmap, pTempBitmap, spriteX, spriteY); + } + } else { + for (int i = 0; i < passes; ++i) { + int spriteX = aDrawPos[i].GetFloorIntX(); + int spriteY = aDrawPos[i].GetFloorIntY(); + g_SceneMan.RegisterDrawing(pTargetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, spriteX + m_SpriteOffset.m_X - (m_SpriteRadius * m_Scale), spriteY + m_SpriteOffset.m_Y - (m_SpriteRadius * m_Scale), spriteX - m_SpriteOffset.m_X + (m_SpriteRadius * m_Scale), spriteY - m_SpriteOffset.m_Y + (m_SpriteRadius * m_Scale)); #ifndef DRAW_MOID_LAYER - if (mode == g_DrawMOID) { - continue; - } + if (mode == g_DrawMOID) { + continue; + } #endif - pivot_scaled_sprite(pTargetBitmap, mode == g_DrawColor ? m_aSprite[m_Frame] : pTempBitmap, spriteX, spriteY, -m_SpriteOffset.GetFloorIntX(), -m_SpriteOffset.GetFloorIntY(), ftofix(m_Rotation.GetAllegroAngle()), ftofix(m_Scale)); - } - } - } - - // Draw all the attached wound emitters, and only if the mode is g_DrawColor and not onlyphysical - // Only draw attachables and emitters which are not drawn after parent, so we draw them before - if (mode == g_DrawColor || (!onlyPhysical && mode == g_DrawMaterial)) { - for (const AEmitter *woundToDraw : m_Wounds) { - if (woundToDraw->IsDrawnAfterParent()) { woundToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } - } - } - - // Draw all the attached attachables - for (const Attachable *attachableToDraw : m_Attachables) { - if (attachableToDraw->IsDrawnAfterParent() && attachableToDraw->IsDrawnNormallyByParent()) { attachableToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } - } - - if (mode == g_DrawColor && !onlyPhysical && m_pAtomGroup && g_SettingsMan.DrawAtomGroupVisualizations() && GetRootParent() == this) { - m_pAtomGroup->Draw(pTargetBitmap, targetPos, false, 122); - //m_pDeepGroup->Draw(pTargetBitmap, targetPos, false, 13); - } -} - -bool MOSRotating::HandlePotentialRadiusAffectingAttachable(const Attachable *attachable) { - if (!attachable->IsAttachedTo(this) && !attachable->IsWound()) { - return false; - } - const HDFirearm *thisAsFirearm = dynamic_cast(this); - const AEmitter *thisAsEmitter = dynamic_cast(this); - if ((thisAsFirearm && attachable == thisAsFirearm->GetFlash()) || (thisAsEmitter && attachable == thisAsEmitter->GetFlash())) { - return false; - } - float distanceAndRadiusFromParent = g_SceneMan.ShortestDistance(m_Pos, attachable->m_Pos, g_SceneMan.SceneWrapsX()).GetMagnitude() + attachable->GetRadius(); - if (attachable == m_RadiusAffectingAttachable && distanceAndRadiusFromParent < m_FarthestAttachableDistanceAndRadius) { - m_FarthestAttachableDistanceAndRadius = distanceAndRadiusFromParent; - if (m_Attachables.size() > 1) { - std::for_each(m_Attachables.begin(), m_Attachables.end(), [this](Attachable *attachableToCheck) { attachableToCheck->UpdatePositionAndJointPositionBasedOnOffsets(); HandlePotentialRadiusAffectingAttachable(attachableToCheck); }); - } - return true; - } else if (distanceAndRadiusFromParent > m_FarthestAttachableDistanceAndRadius) { - m_FarthestAttachableDistanceAndRadius = distanceAndRadiusFromParent; - m_RadiusAffectingAttachable = attachable; - return true; - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MOSRotating::CorrectAttachableAndWoundPositionsAndRotations() const { - for (Attachable *attachable : m_Attachables) { - attachable->PreUpdate(); - attachable->m_PreUpdateHasRunThisFrame = false; - attachable->UpdatePositionAndJointPositionBasedOnOffsets(); - attachable->CorrectAttachableAndWoundPositionsAndRotations(); + pivot_scaled_sprite(pTargetBitmap, mode == g_DrawColor ? m_aSprite[m_Frame] : pTempBitmap, spriteX, spriteY, -m_SpriteOffset.GetFloorIntX(), -m_SpriteOffset.GetFloorIntY(), ftofix(m_Rotation.GetAllegroAngle()), ftofix(m_Scale)); + } + } + } + + // Draw all the attached wound emitters, and only if the mode is g_DrawColor and not onlyphysical + // Only draw attachables and emitters which are not drawn after parent, so we draw them before + if (mode == g_DrawColor || (!onlyPhysical && mode == g_DrawMaterial)) { + for (const AEmitter* woundToDraw: m_Wounds) { + if (woundToDraw->IsDrawnAfterParent()) { + woundToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } + } + } + + // Draw all the attached attachables + for (const Attachable* attachableToDraw: m_Attachables) { + if (attachableToDraw->IsDrawnAfterParent() && attachableToDraw->IsDrawnNormallyByParent()) { + attachableToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } + } + + if (mode == g_DrawColor && !onlyPhysical && m_pAtomGroup && g_SettingsMan.DrawAtomGroupVisualizations() && GetRootParent() == this) { + m_pAtomGroup->Draw(pTargetBitmap, targetPos, false, 122); + // m_pDeepGroup->Draw(pTargetBitmap, targetPos, false, 13); + } } - for (Attachable *wound : m_Wounds) { - wound->PreUpdate(); - wound->m_PreUpdateHasRunThisFrame = false; - wound->UpdatePositionAndJointPositionBasedOnOffsets(); - wound->CorrectAttachableAndWoundPositionsAndRotations(); + + bool MOSRotating::HandlePotentialRadiusAffectingAttachable(const Attachable* attachable) { + if (!attachable->IsAttachedTo(this) && !attachable->IsWound()) { + return false; + } + const HDFirearm* thisAsFirearm = dynamic_cast(this); + const AEmitter* thisAsEmitter = dynamic_cast(this); + if ((thisAsFirearm && attachable == thisAsFirearm->GetFlash()) || (thisAsEmitter && attachable == thisAsEmitter->GetFlash())) { + return false; + } + float distanceAndRadiusFromParent = g_SceneMan.ShortestDistance(m_Pos, attachable->m_Pos, g_SceneMan.SceneWrapsX()).GetMagnitude() + attachable->GetRadius(); + if (attachable == m_RadiusAffectingAttachable && distanceAndRadiusFromParent < m_FarthestAttachableDistanceAndRadius) { + m_FarthestAttachableDistanceAndRadius = distanceAndRadiusFromParent; + if (m_Attachables.size() > 1) { + std::for_each(m_Attachables.begin(), m_Attachables.end(), [this](Attachable* attachableToCheck) { attachableToCheck->UpdatePositionAndJointPositionBasedOnOffsets(); HandlePotentialRadiusAffectingAttachable(attachableToCheck); }); + } + return true; + } else if (distanceAndRadiusFromParent > m_FarthestAttachableDistanceAndRadius) { + m_FarthestAttachableDistanceAndRadius = distanceAndRadiusFromParent; + m_RadiusAffectingAttachable = attachable; + return true; + } + return false; } -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MOSRotating::OnSave() { - for (AEmitter *wound : m_Wounds) { - wound->OnSave(); + void MOSRotating::CorrectAttachableAndWoundPositionsAndRotations() const { + for (Attachable* attachable: m_Attachables) { + attachable->PreUpdate(); + attachable->m_PreUpdateHasRunThisFrame = false; + attachable->UpdatePositionAndJointPositionBasedOnOffsets(); + attachable->CorrectAttachableAndWoundPositionsAndRotations(); + } + for (Attachable* wound: m_Wounds) { + wound->PreUpdate(); + wound->m_PreUpdateHasRunThisFrame = false; + wound->UpdatePositionAndJointPositionBasedOnOffsets(); + wound->CorrectAttachableAndWoundPositionsAndRotations(); + } } - for (Attachable *attachable : m_Attachables) { - attachable->OnSave(); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MOSRotating::OnSave() { + for (AEmitter* wound: m_Wounds) { + wound->OnSave(); + } + for (Attachable* attachable: m_Attachables) { + attachable->OnSave(); + } + MovableObject::OnSave(); } - MovableObject::OnSave(); -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool MOSRotating::TransferForcesFromAttachable(Attachable *attachable) { - bool intact = false; - Vector forces; - Vector impulses; - intact = attachable->TransferJointForces(forces) && attachable->TransferJointImpulses(impulses); + bool MOSRotating::TransferForcesFromAttachable(Attachable* attachable) { + bool intact = false; + Vector forces; + Vector impulses; + intact = attachable->TransferJointForces(forces) && attachable->TransferJointImpulses(impulses); - if (!forces.IsZero()) { AddForce(forces, attachable->GetApplyTransferredForcesAtOffset() ? attachable->GetParentOffset() * m_Rotation * c_MPP : Vector()); } - if (!impulses.IsZero()) { AddImpulseForce(impulses, attachable->GetApplyTransferredForcesAtOffset() ? attachable->GetParentOffset() * m_Rotation * c_MPP : Vector()); } - return intact; -} + if (!forces.IsZero()) { + AddForce(forces, attachable->GetApplyTransferredForcesAtOffset() ? attachable->GetParentOffset() * m_Rotation * c_MPP : Vector()); + } + if (!impulses.IsZero()) { + AddImpulseForce(impulses, attachable->GetApplyTransferredForcesAtOffset() ? attachable->GetParentOffset() * m_Rotation * c_MPP : Vector()); + } + return intact; + } } // namespace RTE diff --git a/Source/Entities/MOSRotating.h b/Source/Entities/MOSRotating.h index 6d98f00611..4975247826 100644 --- a/Source/Entities/MOSRotating.h +++ b/Source/Entities/MOSRotating.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -19,964 +18,926 @@ #include "PostProcessMan.h" #include "SoundContainer.h" -namespace RTE -{ - -class AtomGroup; -struct HitData; -class AEmitter; -class Attachable; - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: MOSRotating -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A sprite movable object that can rotate. -// Parent(s): MOSprite. -// Class history: 05/30/2002 MOSRotating created. - -class MOSRotating : public MOSprite { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -friend class AtomGroup; -friend class SLTerrain; -friend struct EntityLuaBindings; - - -// Concrete allocation and cloning definitions -EntityAllocation(MOSRotating); -SerializableOverrideMethods; -ClassInfoGetters; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: MOSRotating -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a MOSRotating object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - MOSRotating() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~MOSRotating -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a MOSRotating object before deletion -// from system memory. -// Arguments: None. - - ~MOSRotating() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MOSRotating object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MOSRotating object ready for use. -// Arguments: A pointer to ContentFile that represents the bitmap file that will be -// used to create the Sprite. -// The number of frames in the Sprite's animation. -// A float specifying the object's mass in Kilograms (kg). -// A Vector specifying the initial position. -// A Vector specifying the initial velocity. -// The amount of time in ms this MovableObject will exist. 0 means unlim. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(ContentFile spriteFile, const int frameCount = 1, const float mass = 1, const Vector &position = Vector(0, 0), const Vector &velocity = Vector(0, 0), const unsigned long lifetime = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MOSRotating to be identical to another, by deep copy. -// Arguments: A reference to the MOSRotating to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const MOSRotating &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire MOSRotating, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); MOSprite::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - /// - /// Gets the radius of this MOSRotating, not including any Attachables. - /// - /// - float GetIndividualRadius() const { return m_SpriteRadius; } - - /// - /// Gets the radius of this MOSRotating, including any Attachables. - /// - /// The radius of this MOSRotating, including any Attachables. - float GetRadius() const override { return std::max(m_SpriteRadius, m_FarthestAttachableDistanceAndRadius); } - - /// - /// Gets the diameter of this MOSRotating, not including any Attachables. - /// - /// - float GetIndividualDiameter() const { return m_SpriteDiameter; } - - /// - /// Gets the diameter of this MOSRotating, including any Attachables. - /// - /// The diameter of this MOSRotating, including any Attachables. - float GetDiameter() const override { return GetRadius() * 2.0F; } - - /// - /// Checks if the given Attachable should affect radius, and handles it if it should. - /// - /// The Attachable to check. - /// Whether the radius affecting Attachable changed as a result of this call. - virtual bool HandlePotentialRadiusAffectingAttachable(const Attachable *attachable); - - /// - /// Gets the mass value of this MOSRotating, not including any Attachables or wounds. - /// - /// The mass of this MOSRotating. - float GetIndividualMass() const { return MovableObject::GetMass(); } - - /// - /// Gets the mass value of this MOSRotating, including the mass of all its Attachables and wounds, and their Attachables and so on. - /// - /// The mass of this MOSRotating and all of its Attachables and wounds in Kilograms (kg). - float GetMass() const override { return MovableObject::GetMass() + m_AttachableAndWoundMass; } - - /// - /// Updates the total mass of Attachables and wounds for this MOSRotating, intended to be used when Attachables' masses get modified. Simply subtracts the old mass and adds the new one. - /// - /// The mass the Attachable or wound had before its mass was modified. - /// The up-to-date mass of the Attachable or wound after its mass was modified. - virtual void UpdateAttachableAndWoundMass(float oldAttachableOrWoundMass, float newAttachableOrWoundMass) { m_AttachableAndWoundMass += newAttachableOrWoundMass - oldAttachableOrWoundMass; } - - /// - /// Gets the MOIDs of this MOSRotating and all its Attachables and Wounds, putting them into the MOIDs vector. - /// - /// The vector that will store all the MOIDs of this MOSRotating. - void GetMOIDs(std::vector &MOIDs) const override; - - /// - /// Sets the MOID of this MOSRotating and any Attachables on it to be g_NoMOID (255) for this frame. - /// - void SetAsNoID() override; - - /// - /// Sets this MOSRotating to not hit a specific other MO and all its children even though MO hitting is enabled on this MOSRotating. - /// - /// A pointer to the MO to not be hitting. Null pointer means don't ignore anything. Ownership is NOT transferred! - /// How long, in seconds, to ignore the specified MO. A negative number means forever. - void SetWhichMOToNotHit(MovableObject *moToNotHit = nullptr, float forHowLong = -1) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAtomGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current AtomGroup of this MOSRotating. -// Arguments: None. -// Return value: A const reference to the current AtomGroup. - - AtomGroup * GetAtomGroup() { return m_pAtomGroup; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the main Material of this MOSRotating. -// Arguments: None. -// Return value: The the Material of this MOSRotating. - - Material const * GetMaterial() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetDrawPriority -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the drawing priority of this MovableObject, if two things were -// overlap when copying to the terrain, the higher priority MO would -// end up getting drawn. -// Arguments: None. -// Return value: The the priority of this MovableObject. Higher number, the higher -// priority. - - int GetDrawPriority() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRecoilForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current recoil impulse force Vector of this MOSprite. -// Arguments: None. -// Return value: A const reference to the current recoil impulse force in kg * m/s. - - const Vector & GetRecoilForce() const { return m_RecoilForce; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRecoilOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current recoil offset Vector of this MOSprite. -// Arguments: None. -// Return value: A const reference to the current recoil offset. - - const Vector & GetRecoilOffset() const { return m_RecoilOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGibList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets direct access to the list of object this is to generate upon gibbing. -// Arguments: None. -// Return value: A pointer to the list of gibs. Ownership is NOT transferred! - - std::list * GetGibList() { return &m_Gibs; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddRecoil -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds graphical recoil offset to this MOSprite according to its angle. -// Arguments: None. -// Return value: None. - - void AddRecoil(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetRecoil -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds recoil offset to this MOSprite. -// Arguments: A vector with the recoil impulse force in kg * m/s. -// A vector with the recoil offset in pixels. -// Whether recoil should be activated or not for the next Draw(). -// Return value: None. - - void SetRecoil(const Vector &force, const Vector &offset, bool recoil = true) - { - m_RecoilForce = force; - m_RecoilOffset = offset; - m_Recoiled = recoil; - } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsRecoiled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether this MOSprite is currently under the effects of -// recoil. -// Arguments: None. -// Return value: None. - - bool IsRecoiled() { return m_Recoiled; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnableDeepCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether or not this MOSRotating should check for deep penetrations -// the terrain or not. -// Arguments: Whether to enable deep penetration checking or not. -// Return value: None. - - void EnableDeepCheck(const bool enable = true) { m_DeepCheck = enable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ForceDeepCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets to force a deep checking of this' silhouette against the terrain -// and create an outline hole in the terrain, generating particles of the -// intersecting pixels in the terrain. -// Arguments: Whether to force a deep penetration check for this sim frame or not.. -// Return value: None. - - void ForceDeepCheck(const bool enable = true) { m_ForceDeepCheck = enable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. -// Arguments: Reference to the HitData struct which describes the collision. This -// will be modified to represent the results of the collision. -// Return value: Whether the collision has been deemed valid. If false, then disregard -// any impulses in the Hitdata. - - bool CollideAtPoint(HitData &hitData) override; - - - /// - /// Defines what should happen when this MovableObject hits and then bounces off of something. - /// This is called by the owned Atom/AtomGroup of this MovableObject during travel. - /// - /// The HitData describing the collision in detail. - /// Whether the MovableObject should immediately halt any travel going on after this bounce. - bool OnBounce(HitData &hd) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: OnSink -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits and then -// sink into something. This is called by the owned Atom/AtomGroup -// of this MovableObject during travel. -// Arguments: The HitData describing the collision in detail. -// Return value: Wheter the MovableObject should immediately halt any travel going on -// after this sinkage. - - bool OnSink(HitData &hd) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ParticlePenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Determines whether a particle which has hit this MO will penetrate, -// and if so, whether it gets lodged or exits on the other side of this -// MO. Appropriate effects will be determined and applied ONLY IF there -// was penetration! If not, nothing will be affected. -// Arguments: The HitData describing the collision in detail, the impulses have to -// have been filled out! -// Return value: Whether the particle managed to penetrate into this MO or not. If -// somehting but a MOPixel or MOSParticle is being passed in as hitor, -// false will trivially be returned here. - - virtual bool ParticlePenetration(HitData &hd); - - - /// - /// Destroys this MOSRotating and creates its specified Gibs in its place with appropriate velocities. Any Attachables are removed and also given appropriate velocities. - /// - /// The impulse (kg * m/s) of the impact causing the gibbing to happen. - /// A pointer to an MO which the Gibs and Attachables should not be colliding with. - virtual void GibThis(const Vector &impactImpulse = Vector(), MovableObject *movableObjectToIgnore = nullptr); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: MoveOutOfTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether any of the Atom:s in this MovableObject are on top of -// terrain pixels, and if so, attempt to move this out so none of this' -// Atoms are on top of the terrain any more. -// Arguments: Only consider materials stronger than this in the terrain for -// intersections. -// Return value: Whether any intersection was successfully resolved. Will return true -// even if there wasn't any intersections to begin with. - - bool MoveOutOfTerrain(unsigned char strongerThan = g_MaterialAir) override; - - /// - /// Gathers, clears and applies this MOSRotating's accumulated forces. - /// - void ApplyForces() override; - - /// - /// Gathers, clears and applies this MOSRotating's accumulated impulse forces, gibbing if appropriate. - /// - void ApplyImpulses() override; - - /// - /// Gets the list of Attachables on this MOSRotating. - /// - /// The list of Attachables on this MOSRotating. - const std::list & GetAttachables() const { return m_Attachables; } - - /// - /// Gets whether or not the given Attachable is a hardcoded Attachable (e.g. an Arm, Leg, Turret, etc.) - /// - /// The Attachable to check. - /// Whether or not the Attachable is hardcoded. - bool AttachableIsHardcoded(const Attachable *attachableToCheck) const; - - /// - /// Adds the passed in Attachable the list of Attachables and sets its parent to this MOSRotating. - /// - /// The Attachable to add. - virtual void AddAttachable(Attachable *attachable); - - /// - /// Adds the passed in Attachable the list of Attachables, changes its parent offset to the passed in Vector, and sets its parent to this MOSRotating. - /// - /// The Attachable to add. - /// The Vector to set as the Attachable's parent offset. - virtual void AddAttachable(Attachable *attachable, const Vector &parentOffsetToSet); - - /// - /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. - /// - /// The UniqueID of the Attachable to remove. - /// A pointer to the removed Attachable. Ownership IS transferred! - virtual Attachable * RemoveAttachable(long attachableUniqueID) { return RemoveAttachable(attachableUniqueID, false, false); } - - /// - /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. - /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. - /// - /// The UniqueID of the Attachable to remove. - /// Whether or not to add the Attachable to MovableMan once it has been removed. - /// Whether or not to add break wounds to the removed Attachable and this MOSRotating. - /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! - virtual Attachable * RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds); - - /// - /// Removes the passed in Attachable and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. - /// - /// The Attachable to remove. - /// A pointer to the removed Attachable. Ownership IS transferred! - virtual Attachable * RemoveAttachable(Attachable *attachable) { return RemoveAttachable(attachable, false, false); } - - /// - /// Removes the passed in Attachable and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. - /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. - /// - /// The Attachable to remove. - /// Whether or not to add the Attachable to MovableMan once it has been removed. - /// Whether or not to add break wounds to the removed Attachable and this MOSRotating. - /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! - virtual Attachable * RemoveAttachable(Attachable *attachable, bool addToMovableMan, bool addBreakWounds); - - /// - /// Removes the passed in Attachable, and sets it to delete so it will go straight to MovableMan and be handled there. - /// - /// The Attacahble to remove and delete. - void RemoveAndDeleteAttachable(Attachable *attachable); - - /// - /// Either removes or deletes all of this MOSRotating's Attachables. - /// - /// Whether to remove or delete the Attachables. Setting this to true deletes them, setting it to false removes them. - void RemoveOrDestroyAllAttachables(bool destroy); - - /// - /// Gets a damage-transferring, impulse-vulnerable Attachable nearest to the passed in offset. - /// - /// The offset that will be compared to each Attachable's ParentOffset. - /// The nearest detachable Attachable, or nullptr if none was found. - Attachable * GetNearestDetachableAttachableToOffset(const Vector &offset) const; - - /// - /// Gibs or detaches any Attachables that would normally gib or detach from the passed in impulses. - /// - /// The impulse vector which determines the Attachables to gib or detach. Will be filled out with the remainder of impulses. - void DetachAttachablesFromImpulse(Vector &impulseVector); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetAllTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resest all the timers used by this. Can be emitters, etc. This is to -// prevent backed up emissions to come out all at once while this has been -// held dormant in an inventory. -// Arguments: None. -// Return value: None. - - void ResetAllTimers() override; - - /// - /// Does the calculations necessary to detect whether this MOSRotating is at rest or not. IsAtRest() retrieves the answer. - /// - void RestDetection() override; - - /// - /// Indicates whether this MOSRotating has been at rest with no movement for longer than its RestThreshold. - /// - bool IsAtRest() override; - - /// - /// Indicates whether this MOSRotating's current graphical representation, including its Attachables, overlaps a point in absolute scene coordinates. - /// - /// The point in absolute scene coordinates to check for overlap with. - /// Whether or not this MOSRotating's graphical representation overlaps the given scene point. - bool IsOnScenePoint(Vector &scenePoint) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: EraseFromTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Cuts this' silhouette out from the terrain's material and color layers. -// Arguments: None. -// Return value: None. - - void EraseFromTerrain(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DeepCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if any of this' deep group atmos are on top of the terrain, and -// if so, erases this' silhouette from the terrain. -// Arguments: Whether to make any MOPixels from erased terrain pixels at all. -// The size of the gaps between MOPixels knocked loose by the terrain erasure. -// The max number of MOPixel:s to generate as dislodged particles from the -// erased terrain. -// Return value: Whether deep penetration was detected and erasure was done. - - bool DeepCheck(bool makeMOPs = true, int skipMOP = 2, int maxMOP = 100); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done before Travel(). Always call before -// calling Travel. -// Arguments: None. -// Return value: None. - - void PreTravel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Travel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Travels this MOSRotatin, using its physical representation. -// Arguments: None. -// Return value: None. - - void Travel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PostTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done after Update(). Always call after -// calling Update. -// Arguments: None. -// Return value: None. - - void PostTravel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - void PostUpdate() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawMOIDIfOverlapping -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the MOID representation of this to the SceneMan's MOID layer if -// this is found to potentially overlap another MovableObject. -// Arguments: The MovableObject to check this for overlap against. -// Return value: Whether it was drawn or not. - - bool DrawMOIDIfOverlapping(MovableObject *pOverlapMO) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MOSRotating's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - - /// - /// Gets the gib impulse limit for this MOSRotating, i.e. the amount of impulse force required in a frame to gib this MOSRotating. - /// - /// The gib impulse limit of this MOSRotating. - float GetGibImpulseLimit() const { return m_GibImpulseLimit; } - - /// - /// Sets the gib impulse limit for this MOSRotating, i.e. the amount of impulse force required in a frame to gib this MOSRotating. - /// - /// The new gib impulse limit to use. - void SetGibImpulseLimit(float newGibImpulseLimit) { m_GibImpulseLimit = newGibImpulseLimit; } - - /// - /// Gets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. Does not include any Attachables. - /// - /// - int GetGibWoundLimit() const { return GetGibWoundLimit(false, false, false); } - - /// - /// Gets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. - /// Optionally adds the gib wound limits of Attachables (and their Attachables, etc.) that match the conditions set by the provided parameters. - /// - /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. - /// The wound limit of this MOSRotating and, optionally, its Attachables. - int GetGibWoundLimit(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const; - - /// - /// Sets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. - /// This will not directly trigger gibbing, even if the limit is lower than the current number of wounds. - /// - /// The new gib wound limit to use. - void SetGibWoundLimit(int newGibWoundLimit) { m_GibWoundLimit = newGibWoundLimit; } - - /// - /// Gets the rate at which wound count of this MOSRotating will diminish the impulse limit. - /// - /// The rate at which wound count affects the impulse limit. - float GetWoundCountAffectsImpulseLimitRatio() const { return m_WoundCountAffectsImpulseLimitRatio; } - - /// - /// Gets whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. - /// - /// Whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. - bool GetGibAtEndOfLifetime() const { return m_GibAtEndOfLifetime; } - - /// - /// Sets whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. - /// - /// Whether or not this MOSRotating should gib at the end of its lifetime instead of just being deleted. - void SetGibAtEndOfLifetime(bool shouldGibAtEndOfLifetime) { m_GibAtEndOfLifetime = shouldGibAtEndOfLifetime; } - - /// - /// Gets the gib blast strength this MOSRotating, i.e. the strength with which Gibs and Attachables will be launched when this MOSRotating is gibbed. - /// - /// The gib blast strength of this MOSRotating. - float GetGibBlastStrength() const { return m_GibBlastStrength; } - - /// - /// Sets the gib blast strength this MOSRotating, i.e. the strength with which Gibs and Attachables will be launched when this MOSRotating is gibbed. - /// - /// The new gib blast strength to use. - void SetGibBlastStrength(float newGibBlastStrength) { m_GibBlastStrength = newGibBlastStrength; } - - /// - /// Gets the amount of screenshake this will cause upon gibbing. - /// - /// The amount of screenshake this will cause when gibbing. If -1, this is calculated automatically. - float GetGibScreenShakeAmount() const { return m_GibScreenShakeAmount; } - - /// - /// Gets a const reference to the list of Attachables on this MOSRotating. - /// - /// A const reference to the list of Attachables on this MOSRotating. - const std::list & GetAttachableList() const { return m_Attachables; } - - /// - /// Gets a const reference to the list of wounds on this MOSRotating. - /// - /// A const reference to the list of wounds on this MOSRotating. - const std::vector & GetWoundList() const { return m_Wounds; } - - /// - /// Gets the number of wounds attached to this MOSRotating. - /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. - /// The number of wounds on this MOSRotating. - /// - int GetWoundCount() const { return GetWoundCount(true, false, false); } - - /// - /// Gets the number of wounds attached to this MOSRotating. - /// Optionally adds the wound counts of Attachables (and their Attachables, etc.) that match the conditions set by the provided parameters. - /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. - /// The number of wounds on this MOSRotating and, optionally, its Attachables. - /// - int GetWoundCount(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const; - - /// - /// Adds the passed in wound AEmitter to the list of wounds and changes its parent offset to the passed in Vector. - /// - /// The wound AEmitter to add. - /// The vector to set as the wound AEmitter's parent offset. - /// Whether to gib this MOSRotating if adding this wound raises its wound count past its gib wound limit. Defaults to true. - virtual void AddWound(AEmitter *woundToAdd, const Vector &parentOffsetToSet, bool checkGibWoundLimit = true); - - /// - /// Removes the specified number of wounds from this MOSRotating, and returns damage caused by these removed wounds. - /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. - /// - /// The number of wounds that should be removed. - /// The amount of damage caused by these wounds, taking damage multipliers into account. - virtual float RemoveWounds(int numberOfWoundsToRemove) { return RemoveWounds(numberOfWoundsToRemove, true, false, false); } - - /// - /// Removes the specified number of wounds from this MOSRotating, and returns damage caused by these removed wounds. - /// Optionally removes wounds from Attachables (and their Attachables, etc.) that match the conditions set by the provided inclusion parameters. - /// - /// The number of wounds that should be removed. - /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. - /// The amount of damage caused by these wounds, taking damage multipliers into account. - virtual float RemoveWounds(int numberOfWoundsToRemove, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables); - - /// - /// Cleans up and destroys the script state of this object, calling the Destroy callback in lua - /// - void DestroyScriptState(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDamageMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets damage multiplier of this attachable. -// Arguments: New multiplier value. -// Return value: None. - - void SetDamageMultiplier(float newValue) { m_DamageMultiplier = newValue; m_NoSetDamageMultiplier = false; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDamageMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns damage multiplier of this attachable. -// Arguments: None. -// Return value: Current multiplier value. - - float GetDamageMultiplier() const { return m_DamageMultiplier; } - - /// - /// Gets whether the damage multiplier for this MOSRotating has been directly set, or is at its default value. - /// - /// Whether the damage multiplier for this MOSRotating has been set. - bool HasNoSetDamageMultiplier() const { return m_NoSetDamageMultiplier; } - - /// - /// Gets the velocity orientation scalar of this MOSRotating. - /// - /// New scalar value. - float GetOrientToVel() const { return m_OrientToVel; } - - /// - /// Sets the velocity orientation scalar of this MOSRotating. - /// - /// New scalar value. - void SetOrientToVel(float newValue) { m_OrientToVel = newValue; } - - /// - /// Sets this MOSRotating and all its children to drawn white for a specified amount of time. - /// - /// Duration of flash in real time MS. - void FlashWhite(int durationMS = 32) { m_FlashWhiteTimer.SetRealTimeLimitMS(durationMS); m_FlashWhiteTimer.Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTravelImpulse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Retrurns the amount of impulse force exerted on this during the last frame. -// Arguments: None. -// Return value: The amount of impulse force exerted on this during the last frame. - - Vector GetTravelImpulse() const { return m_TravelImpulse; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetTravelImpulse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the amount of impulse force exerted on this during the last frame. -// Arguments: New impulse value -// Return value: None. - - void SetTravelImpulse(Vector impulse) { m_TravelImpulse = impulse; } - - /// - /// Gets this MOSRotating's gib sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this MOSRotating's gib sound. - SoundContainer * GetGibSound() const { return m_GibSound; } - - /// - /// Sets this MOSRotating's gib sound. Ownership IS transferred! - /// - /// The new SoundContainer for this MOSRotating's gib sound. - void SetGibSound(SoundContainer *newSound) { m_GibSound = newSound; } - - /// - /// Ensures all attachables and wounds are positioned and rotated correctly. Must be run when this MOSRotating is added to MovableMan to avoid issues with Attachables spawning in at (0, 0). - /// - virtual void CorrectAttachableAndWoundPositionsAndRotations() const; - - /// - /// Method to be run when the game is saved via ActivityMan::SaveCurrentGame. Not currently used in metagame or editor saving. - /// - void OnSave() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - /// - /// Transfers forces and impulse forces from the given Attachable to this MOSRotating, gibbing and/or removing the Attachable if needed. - /// - /// A pointer to the Attachable to apply forces from. Ownership is NOT transferred! - /// Whether or not the Attachable has been removed, in which case it'll usually be passed to MovableMan. - bool TransferForcesFromAttachable(Attachable *attachable); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - - /// - /// Creates the particles specified by this MOSRotating's list of Gibs and adds them to MovableMan with appropriately randomized velocities, based on this MOSRotating's gib blast strength. - /// - /// The impulse (kg * m/s) of the impact that caused the gibbing to happen. - /// A pointer to an MO which the Attachables should not be colliding with. - void CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObject *movableObjectToIgnore); - - /// - /// Removes all Attachables from this MOSR, deleting them or adding them to MovableMan as appropriate, and giving them randomized velocities based on their properties and this MOSRotating's gib blast strength. - /// - /// The impulse (kg * m/s) of the impact that caused the gibbing to happen. - /// A pointer to an MO which the Attachables should not be colliding with. - void RemoveAttachablesWhenGibbing(const Vector &impactImpulse, MovableObject *movableObjectToIgnore); - - // Member variables - static Entity::ClassInfo m_sClass; -// float m_Torque; // In kg * r/s^2 (Newtons). -// float m_ImpulseTorque; // In kg * r/s. - // The group of Atom:s that will be the physical reperesentation of this MOSRotating. - AtomGroup *m_pAtomGroup; - // The group of Atom:s that will serve as a means to detect deep terrain penetration. - AtomGroup *m_pDeepGroup; - // Whether or not to check for deep penetrations. - bool m_DeepCheck; - // A trigger for forcing a deep check to happen - bool m_ForceDeepCheck; - // Whether deep penetration happaned in the last frame or not, and how hard it was. - float m_DeepHardness; - // The amount of impulse force exerted on this during the last frame. - Vector m_TravelImpulse; - // The precomupted center location of the sprite relative to the MovableObject::m_Pos. - Vector m_SpriteCenter; - // How much to orient the rotation of this to match the velocity vector each frame 0 = none, 1.0 = immediately align with vel vector - float m_OrientToVel; - // Whether the SpriteMO is currently pushed back by recoil or not. - bool m_Recoiled; - // The impulse force in kg * m/s that represents the recoil. - Vector m_RecoilForce; - // The vector that the recoil offsets the sprite when m_Recoiled is true. - Vector m_RecoilOffset; - // The list of wound AEmitters currently attached to this MOSRotating, and owned here as well. - std::vector m_Wounds; - // The list of Attachables currently attached and Owned by this. - std::list m_Attachables; - std::unordered_set m_ReferenceHardcodedAttachableUniqueIDs; //!< An unordered set is filled with the Unique IDs of all of the reference object's hardcoded Attachables when using the copy Create. - std::unordered_map> m_HardcodedAttachableUniqueIDsAndSetters; //!< An unordered map of Unique IDs to setter lambda functions, used to call the appropriate hardcoded Attachable setter when a hardcoded Attachable is removed. - std::unordered_map> m_HardcodedAttachableUniqueIDsAndRemovers; //!< An unordered map of Unique IDs to remove lambda functions, used to call the appropriate hardcoded Attachable remover when a hardcoded Attachable is removed and calling the setter with nullptr won't work. - const Attachable *m_RadiusAffectingAttachable; //!< A pointer to the Attachable that is currently affecting the radius. Used for some efficiency benefits. - float m_FarthestAttachableDistanceAndRadius; //!< The distance + radius of the radius affecting Attachable. - float m_AttachableAndWoundMass; //!< The mass of all Attachables and wounds on this MOSRotating. Used in combination with its actual mass and any other affecting factors to get its total mass. - // The list of Gib:s this will create when gibbed - std::list m_Gibs; - // The amount of impulse force required to gib this, in kg * (m/s). 0 means no limit - float m_GibImpulseLimit; - int m_GibWoundLimit; //!< The number of wounds that will gib this MOSRotating. 0 means that it can't be gibbed via wounds. - float m_GibBlastStrength; //!< The strength with which Gibs and Attachables will get launched when this MOSRotating is gibbed. - float m_GibScreenShakeAmount; //!< Determines how much screenshake this causes upon gibbing. - float m_WoundCountAffectsImpulseLimitRatio; //!< The rate at which this MOSRotating's wound count will diminish the impulse limit. - bool m_DetachAttachablesBeforeGibbingFromWounds; //!< Whether to detach any Attachables of this MOSRotating when it should gib from hitting its wound limit, instead of gibbing the MOSRotating itself. - bool m_GibAtEndOfLifetime; //!< Whether or not this MOSRotating should gib when it reaches the end of its lifetime, instead of just deleting. - // Gib sound effect - SoundContainer *m_GibSound; - // Whether to flash effect on gib - bool m_EffectOnGib; - // How far this is audiable (in screens) when gibbing - float m_LoudnessOnGib; - - float m_DamageMultiplier; //!< Damage multiplier for this MOSRotating. - bool m_NoSetDamageMultiplier; //!< Whether or not the damage multiplier for this MOSRotating was set. - - Timer m_FlashWhiteTimer; //!< The timer for timing white draw mode duration. - - // Intermediary drawing bitmap used to flip rotating bitmaps. Owned! - BITMAP *m_pFlipBitmap; - BITMAP *m_pFlipBitmapS; - // Intermediary drawing bitmap used to draw sihouettes and other effects. Not owned; points to the shared static bitmaps - BITMAP *m_pTempBitmap; - // Temp drawing bitmaps shared between all MOSRotatings - static BITMAP *m_spTempBitmap16; - static BITMAP *m_spTempBitmap32; - static BITMAP *m_spTempBitmap64; - static BITMAP *m_spTempBitmap128; - static BITMAP *m_spTempBitmap256; - static BITMAP *m_spTempBitmap512; - - // Intermediary drawing bitmap used to draw MO silhouettes. Not owned; points to the shared static bitmaps - BITMAP *m_pTempBitmapS; - // Temp drawing bitmaps shared between all MOSRotatings - static BITMAP *m_spTempBitmapS16; - static BITMAP *m_spTempBitmapS32; - static BITMAP *m_spTempBitmapS64; - static BITMAP *m_spTempBitmapS128; - static BITMAP *m_spTempBitmapS256; - static BITMAP *m_spTempBitmapS512; - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MOSRotating, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - // Disallow the use of some implicit methods. - MOSRotating(const MOSRotating &reference) = delete; - MOSRotating& operator=(const MOSRotating &rhs) = delete; - -}; +namespace RTE { + + class AtomGroup; + struct HitData; + class AEmitter; + class Attachable; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: MOSRotating + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A sprite movable object that can rotate. + // Parent(s): MOSprite. + // Class history: 05/30/2002 MOSRotating created. + + class MOSRotating : public MOSprite { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + friend class AtomGroup; + friend class SLTerrain; + friend struct EntityLuaBindings; + + // Concrete allocation and cloning definitions + EntityAllocation(MOSRotating); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: MOSRotating + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a MOSRotating object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + MOSRotating() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~MOSRotating + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a MOSRotating object before deletion + // from system memory. + // Arguments: None. + + ~MOSRotating() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MOSRotating object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MOSRotating object ready for use. + // Arguments: A pointer to ContentFile that represents the bitmap file that will be + // used to create the Sprite. + // The number of frames in the Sprite's animation. + // A float specifying the object's mass in Kilograms (kg). + // A Vector specifying the initial position. + // A Vector specifying the initial velocity. + // The amount of time in ms this MovableObject will exist. 0 means unlim. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(ContentFile spriteFile, const int frameCount = 1, const float mass = 1, const Vector& position = Vector(0, 0), const Vector& velocity = Vector(0, 0), const unsigned long lifetime = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MOSRotating to be identical to another, by deep copy. + // Arguments: A reference to the MOSRotating to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const MOSRotating& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire MOSRotating, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + MOSprite::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + /// + /// Gets the radius of this MOSRotating, not including any Attachables. + /// + /// + float GetIndividualRadius() const { return m_SpriteRadius; } + + /// + /// Gets the radius of this MOSRotating, including any Attachables. + /// + /// The radius of this MOSRotating, including any Attachables. + float GetRadius() const override { return std::max(m_SpriteRadius, m_FarthestAttachableDistanceAndRadius); } + + /// + /// Gets the diameter of this MOSRotating, not including any Attachables. + /// + /// + float GetIndividualDiameter() const { return m_SpriteDiameter; } + + /// + /// Gets the diameter of this MOSRotating, including any Attachables. + /// + /// The diameter of this MOSRotating, including any Attachables. + float GetDiameter() const override { return GetRadius() * 2.0F; } + + /// + /// Checks if the given Attachable should affect radius, and handles it if it should. + /// + /// The Attachable to check. + /// Whether the radius affecting Attachable changed as a result of this call. + virtual bool HandlePotentialRadiusAffectingAttachable(const Attachable* attachable); + + /// + /// Gets the mass value of this MOSRotating, not including any Attachables or wounds. + /// + /// The mass of this MOSRotating. + float GetIndividualMass() const { return MovableObject::GetMass(); } + + /// + /// Gets the mass value of this MOSRotating, including the mass of all its Attachables and wounds, and their Attachables and so on. + /// + /// The mass of this MOSRotating and all of its Attachables and wounds in Kilograms (kg). + float GetMass() const override { return MovableObject::GetMass() + m_AttachableAndWoundMass; } + + /// + /// Updates the total mass of Attachables and wounds for this MOSRotating, intended to be used when Attachables' masses get modified. Simply subtracts the old mass and adds the new one. + /// + /// The mass the Attachable or wound had before its mass was modified. + /// The up-to-date mass of the Attachable or wound after its mass was modified. + virtual void UpdateAttachableAndWoundMass(float oldAttachableOrWoundMass, float newAttachableOrWoundMass) { m_AttachableAndWoundMass += newAttachableOrWoundMass - oldAttachableOrWoundMass; } + + /// + /// Gets the MOIDs of this MOSRotating and all its Attachables and Wounds, putting them into the MOIDs vector. + /// + /// The vector that will store all the MOIDs of this MOSRotating. + void GetMOIDs(std::vector& MOIDs) const override; + + /// + /// Sets the MOID of this MOSRotating and any Attachables on it to be g_NoMOID (255) for this frame. + /// + void SetAsNoID() override; + + /// + /// Sets this MOSRotating to not hit a specific other MO and all its children even though MO hitting is enabled on this MOSRotating. + /// + /// A pointer to the MO to not be hitting. Null pointer means don't ignore anything. Ownership is NOT transferred! + /// How long, in seconds, to ignore the specified MO. A negative number means forever. + void SetWhichMOToNotHit(MovableObject* moToNotHit = nullptr, float forHowLong = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAtomGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current AtomGroup of this MOSRotating. + // Arguments: None. + // Return value: A const reference to the current AtomGroup. + + AtomGroup* GetAtomGroup() { return m_pAtomGroup; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMaterial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the main Material of this MOSRotating. + // Arguments: None. + // Return value: The the Material of this MOSRotating. + + Material const* GetMaterial() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetDrawPriority + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the drawing priority of this MovableObject, if two things were + // overlap when copying to the terrain, the higher priority MO would + // end up getting drawn. + // Arguments: None. + // Return value: The the priority of this MovableObject. Higher number, the higher + // priority. + + int GetDrawPriority() const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRecoilForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current recoil impulse force Vector of this MOSprite. + // Arguments: None. + // Return value: A const reference to the current recoil impulse force in kg * m/s. + + const Vector& GetRecoilForce() const { return m_RecoilForce; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRecoilOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current recoil offset Vector of this MOSprite. + // Arguments: None. + // Return value: A const reference to the current recoil offset. + + const Vector& GetRecoilOffset() const { return m_RecoilOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGibList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets direct access to the list of object this is to generate upon gibbing. + // Arguments: None. + // Return value: A pointer to the list of gibs. Ownership is NOT transferred! + + std::list* GetGibList() { return &m_Gibs; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddRecoil + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds graphical recoil offset to this MOSprite according to its angle. + // Arguments: None. + // Return value: None. + + void AddRecoil(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetRecoil + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds recoil offset to this MOSprite. + // Arguments: A vector with the recoil impulse force in kg * m/s. + // A vector with the recoil offset in pixels. + // Whether recoil should be activated or not for the next Draw(). + // Return value: None. + + void SetRecoil(const Vector& force, const Vector& offset, bool recoil = true) { + m_RecoilForce = force; + m_RecoilOffset = offset; + m_Recoiled = recoil; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsRecoiled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether this MOSprite is currently under the effects of + // recoil. + // Arguments: None. + // Return value: None. + + bool IsRecoiled() { return m_Recoiled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnableDeepCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether or not this MOSRotating should check for deep penetrations + // the terrain or not. + // Arguments: Whether to enable deep penetration checking or not. + // Return value: None. + + void EnableDeepCheck(const bool enable = true) { m_DeepCheck = enable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ForceDeepCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets to force a deep checking of this' silhouette against the terrain + // and create an outline hole in the terrain, generating particles of the + // intersecting pixels in the terrain. + // Arguments: Whether to force a deep penetration check for this sim frame or not.. + // Return value: None. + + void ForceDeepCheck(const bool enable = true) { m_ForceDeepCheck = enable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. + // Arguments: Reference to the HitData struct which describes the collision. This + // will be modified to represent the results of the collision. + // Return value: Whether the collision has been deemed valid. If false, then disregard + // any impulses in the Hitdata. + + bool CollideAtPoint(HitData& hitData) override; + + /// + /// Defines what should happen when this MovableObject hits and then bounces off of something. + /// This is called by the owned Atom/AtomGroup of this MovableObject during travel. + /// + /// The HitData describing the collision in detail. + /// Whether the MovableObject should immediately halt any travel going on after this bounce. + bool OnBounce(HitData& hd) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: OnSink + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits and then + // sink into something. This is called by the owned Atom/AtomGroup + // of this MovableObject during travel. + // Arguments: The HitData describing the collision in detail. + // Return value: Wheter the MovableObject should immediately halt any travel going on + // after this sinkage. + + bool OnSink(HitData& hd) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ParticlePenetration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Determines whether a particle which has hit this MO will penetrate, + // and if so, whether it gets lodged or exits on the other side of this + // MO. Appropriate effects will be determined and applied ONLY IF there + // was penetration! If not, nothing will be affected. + // Arguments: The HitData describing the collision in detail, the impulses have to + // have been filled out! + // Return value: Whether the particle managed to penetrate into this MO or not. If + // somehting but a MOPixel or MOSParticle is being passed in as hitor, + // false will trivially be returned here. + + virtual bool ParticlePenetration(HitData& hd); + + /// + /// Destroys this MOSRotating and creates its specified Gibs in its place with appropriate velocities. Any Attachables are removed and also given appropriate velocities. + /// + /// The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + virtual void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: MoveOutOfTerrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks whether any of the Atom:s in this MovableObject are on top of + // terrain pixels, and if so, attempt to move this out so none of this' + // Atoms are on top of the terrain any more. + // Arguments: Only consider materials stronger than this in the terrain for + // intersections. + // Return value: Whether any intersection was successfully resolved. Will return true + // even if there wasn't any intersections to begin with. + + bool MoveOutOfTerrain(unsigned char strongerThan = g_MaterialAir) override; + + /// + /// Gathers, clears and applies this MOSRotating's accumulated forces. + /// + void ApplyForces() override; + + /// + /// Gathers, clears and applies this MOSRotating's accumulated impulse forces, gibbing if appropriate. + /// + void ApplyImpulses() override; + + /// + /// Gets the list of Attachables on this MOSRotating. + /// + /// The list of Attachables on this MOSRotating. + const std::list& GetAttachables() const { return m_Attachables; } + + /// + /// Gets whether or not the given Attachable is a hardcoded Attachable (e.g. an Arm, Leg, Turret, etc.) + /// + /// The Attachable to check. + /// Whether or not the Attachable is hardcoded. + bool AttachableIsHardcoded(const Attachable* attachableToCheck) const; + + /// + /// Adds the passed in Attachable the list of Attachables and sets its parent to this MOSRotating. + /// + /// The Attachable to add. + virtual void AddAttachable(Attachable* attachable); + + /// + /// Adds the passed in Attachable the list of Attachables, changes its parent offset to the passed in Vector, and sets its parent to this MOSRotating. + /// + /// The Attachable to add. + /// The Vector to set as the Attachable's parent offset. + virtual void AddAttachable(Attachable* attachable, const Vector& parentOffsetToSet); + + /// + /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. + /// + /// The UniqueID of the Attachable to remove. + /// A pointer to the removed Attachable. Ownership IS transferred! + virtual Attachable* RemoveAttachable(long attachableUniqueID) { return RemoveAttachable(attachableUniqueID, false, false); } + + /// + /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. + /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. + /// + /// The UniqueID of the Attachable to remove. + /// Whether or not to add the Attachable to MovableMan once it has been removed. + /// Whether or not to add break wounds to the removed Attachable and this MOSRotating. + /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! + virtual Attachable* RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds); + + /// + /// Removes the passed in Attachable and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. + /// + /// The Attachable to remove. + /// A pointer to the removed Attachable. Ownership IS transferred! + virtual Attachable* RemoveAttachable(Attachable* attachable) { return RemoveAttachable(attachable, false, false); } + + /// + /// Removes the passed in Attachable and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. + /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. + /// + /// The Attachable to remove. + /// Whether or not to add the Attachable to MovableMan once it has been removed. + /// Whether or not to add break wounds to the removed Attachable and this MOSRotating. + /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! + virtual Attachable* RemoveAttachable(Attachable* attachable, bool addToMovableMan, bool addBreakWounds); + + /// + /// Removes the passed in Attachable, and sets it to delete so it will go straight to MovableMan and be handled there. + /// + /// The Attacahble to remove and delete. + void RemoveAndDeleteAttachable(Attachable* attachable); + + /// + /// Either removes or deletes all of this MOSRotating's Attachables. + /// + /// Whether to remove or delete the Attachables. Setting this to true deletes them, setting it to false removes them. + void RemoveOrDestroyAllAttachables(bool destroy); + + /// + /// Gets a damage-transferring, impulse-vulnerable Attachable nearest to the passed in offset. + /// + /// The offset that will be compared to each Attachable's ParentOffset. + /// The nearest detachable Attachable, or nullptr if none was found. + Attachable* GetNearestDetachableAttachableToOffset(const Vector& offset) const; + + /// + /// Gibs or detaches any Attachables that would normally gib or detach from the passed in impulses. + /// + /// The impulse vector which determines the Attachables to gib or detach. Will be filled out with the remainder of impulses. + void DetachAttachablesFromImpulse(Vector& impulseVector); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ResetAllTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resest all the timers used by this. Can be emitters, etc. This is to + // prevent backed up emissions to come out all at once while this has been + // held dormant in an inventory. + // Arguments: None. + // Return value: None. + + void ResetAllTimers() override; + + /// + /// Does the calculations necessary to detect whether this MOSRotating is at rest or not. IsAtRest() retrieves the answer. + /// + void RestDetection() override; + + /// + /// Indicates whether this MOSRotating has been at rest with no movement for longer than its RestThreshold. + /// + bool IsAtRest() override; + + /// + /// Indicates whether this MOSRotating's current graphical representation, including its Attachables, overlaps a point in absolute scene coordinates. + /// + /// The point in absolute scene coordinates to check for overlap with. + /// Whether or not this MOSRotating's graphical representation overlaps the given scene point. + bool IsOnScenePoint(Vector& scenePoint) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: EraseFromTerrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Cuts this' silhouette out from the terrain's material and color layers. + // Arguments: None. + // Return value: None. + + void EraseFromTerrain(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DeepCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if any of this' deep group atmos are on top of the terrain, and + // if so, erases this' silhouette from the terrain. + // Arguments: Whether to make any MOPixels from erased terrain pixels at all. + // The size of the gaps between MOPixels knocked loose by the terrain erasure. + // The max number of MOPixel:s to generate as dislodged particles from the + // erased terrain. + // Return value: Whether deep penetration was detected and erasure was done. + + bool DeepCheck(bool makeMOPs = true, int skipMOP = 2, int maxMOP = 100); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreTravel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does stuff that needs to be done before Travel(). Always call before + // calling Travel. + // Arguments: None. + // Return value: None. + + void PreTravel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Travel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Travels this MOSRotatin, using its physical representation. + // Arguments: None. + // Return value: None. + + void Travel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PostTravel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does stuff that needs to be done after Update(). Always call after + // calling Update. + // Arguments: None. + // Return value: None. + + void PostTravel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + void PostUpdate() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawMOIDIfOverlapping + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the MOID representation of this to the SceneMan's MOID layer if + // this is found to potentially overlap another MovableObject. + // Arguments: The MovableObject to check this for overlap against. + // Return value: Whether it was drawn or not. + + bool DrawMOIDIfOverlapping(MovableObject* pOverlapMO) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this MOSRotating's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + /// + /// Gets the gib impulse limit for this MOSRotating, i.e. the amount of impulse force required in a frame to gib this MOSRotating. + /// + /// The gib impulse limit of this MOSRotating. + float GetGibImpulseLimit() const { return m_GibImpulseLimit; } + + /// + /// Sets the gib impulse limit for this MOSRotating, i.e. the amount of impulse force required in a frame to gib this MOSRotating. + /// + /// The new gib impulse limit to use. + void SetGibImpulseLimit(float newGibImpulseLimit) { m_GibImpulseLimit = newGibImpulseLimit; } + + /// + /// Gets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. Does not include any Attachables. + /// + /// + int GetGibWoundLimit() const { return GetGibWoundLimit(false, false, false); } + + /// + /// Gets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. + /// Optionally adds the gib wound limits of Attachables (and their Attachables, etc.) that match the conditions set by the provided parameters. + /// + /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. + /// The wound limit of this MOSRotating and, optionally, its Attachables. + int GetGibWoundLimit(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const; + + /// + /// Sets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. + /// This will not directly trigger gibbing, even if the limit is lower than the current number of wounds. + /// + /// The new gib wound limit to use. + void SetGibWoundLimit(int newGibWoundLimit) { m_GibWoundLimit = newGibWoundLimit; } + + /// + /// Gets the rate at which wound count of this MOSRotating will diminish the impulse limit. + /// + /// The rate at which wound count affects the impulse limit. + float GetWoundCountAffectsImpulseLimitRatio() const { return m_WoundCountAffectsImpulseLimitRatio; } + + /// + /// Gets whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. + /// + /// Whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. + bool GetGibAtEndOfLifetime() const { return m_GibAtEndOfLifetime; } + + /// + /// Sets whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. + /// + /// Whether or not this MOSRotating should gib at the end of its lifetime instead of just being deleted. + void SetGibAtEndOfLifetime(bool shouldGibAtEndOfLifetime) { m_GibAtEndOfLifetime = shouldGibAtEndOfLifetime; } + + /// + /// Gets the gib blast strength this MOSRotating, i.e. the strength with which Gibs and Attachables will be launched when this MOSRotating is gibbed. + /// + /// The gib blast strength of this MOSRotating. + float GetGibBlastStrength() const { return m_GibBlastStrength; } + + /// + /// Sets the gib blast strength this MOSRotating, i.e. the strength with which Gibs and Attachables will be launched when this MOSRotating is gibbed. + /// + /// The new gib blast strength to use. + void SetGibBlastStrength(float newGibBlastStrength) { m_GibBlastStrength = newGibBlastStrength; } + + /// + /// Gets the amount of screenshake this will cause upon gibbing. + /// + /// The amount of screenshake this will cause when gibbing. If -1, this is calculated automatically. + float GetGibScreenShakeAmount() const { return m_GibScreenShakeAmount; } + + /// + /// Gets a const reference to the list of Attachables on this MOSRotating. + /// + /// A const reference to the list of Attachables on this MOSRotating. + const std::list& GetAttachableList() const { return m_Attachables; } + + /// + /// Gets a const reference to the list of wounds on this MOSRotating. + /// + /// A const reference to the list of wounds on this MOSRotating. + const std::vector& GetWoundList() const { return m_Wounds; } + + /// + /// Gets the number of wounds attached to this MOSRotating. + /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. + /// The number of wounds on this MOSRotating. + /// + int GetWoundCount() const { return GetWoundCount(true, false, false); } + + /// + /// Gets the number of wounds attached to this MOSRotating. + /// Optionally adds the wound counts of Attachables (and their Attachables, etc.) that match the conditions set by the provided parameters. + /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. + /// The number of wounds on this MOSRotating and, optionally, its Attachables. + /// + int GetWoundCount(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const; + + /// + /// Adds the passed in wound AEmitter to the list of wounds and changes its parent offset to the passed in Vector. + /// + /// The wound AEmitter to add. + /// The vector to set as the wound AEmitter's parent offset. + /// Whether to gib this MOSRotating if adding this wound raises its wound count past its gib wound limit. Defaults to true. + virtual void AddWound(AEmitter* woundToAdd, const Vector& parentOffsetToSet, bool checkGibWoundLimit = true); + + /// + /// Removes the specified number of wounds from this MOSRotating, and returns damage caused by these removed wounds. + /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. + /// + /// The number of wounds that should be removed. + /// The amount of damage caused by these wounds, taking damage multipliers into account. + virtual float RemoveWounds(int numberOfWoundsToRemove) { return RemoveWounds(numberOfWoundsToRemove, true, false, false); } + + /// + /// Removes the specified number of wounds from this MOSRotating, and returns damage caused by these removed wounds. + /// Optionally removes wounds from Attachables (and their Attachables, etc.) that match the conditions set by the provided inclusion parameters. + /// + /// The number of wounds that should be removed. + /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. + /// The amount of damage caused by these wounds, taking damage multipliers into account. + virtual float RemoveWounds(int numberOfWoundsToRemove, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables); + + /// + /// Cleans up and destroys the script state of this object, calling the Destroy callback in lua + /// + void DestroyScriptState(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDamageMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets damage multiplier of this attachable. + // Arguments: New multiplier value. + // Return value: None. + + void SetDamageMultiplier(float newValue) { + m_DamageMultiplier = newValue; + m_NoSetDamageMultiplier = false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDamageMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns damage multiplier of this attachable. + // Arguments: None. + // Return value: Current multiplier value. + + float GetDamageMultiplier() const { return m_DamageMultiplier; } + + /// + /// Gets whether the damage multiplier for this MOSRotating has been directly set, or is at its default value. + /// + /// Whether the damage multiplier for this MOSRotating has been set. + bool HasNoSetDamageMultiplier() const { return m_NoSetDamageMultiplier; } + + /// + /// Gets the velocity orientation scalar of this MOSRotating. + /// + /// New scalar value. + float GetOrientToVel() const { return m_OrientToVel; } + + /// + /// Sets the velocity orientation scalar of this MOSRotating. + /// + /// New scalar value. + void SetOrientToVel(float newValue) { m_OrientToVel = newValue; } + + /// + /// Sets this MOSRotating and all its children to drawn white for a specified amount of time. + /// + /// Duration of flash in real time MS. + void FlashWhite(int durationMS = 32) { + m_FlashWhiteTimer.SetRealTimeLimitMS(durationMS); + m_FlashWhiteTimer.Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTravelImpulse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Retrurns the amount of impulse force exerted on this during the last frame. + // Arguments: None. + // Return value: The amount of impulse force exerted on this during the last frame. + + Vector GetTravelImpulse() const { return m_TravelImpulse; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetTravelImpulse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the amount of impulse force exerted on this during the last frame. + // Arguments: New impulse value + // Return value: None. + + void SetTravelImpulse(Vector impulse) { m_TravelImpulse = impulse; } + + /// + /// Gets this MOSRotating's gib sound. Ownership is NOT transferred! + /// + /// The SoundContainer for this MOSRotating's gib sound. + SoundContainer* GetGibSound() const { return m_GibSound; } + + /// + /// Sets this MOSRotating's gib sound. Ownership IS transferred! + /// + /// The new SoundContainer for this MOSRotating's gib sound. + void SetGibSound(SoundContainer* newSound) { m_GibSound = newSound; } + + /// + /// Ensures all attachables and wounds are positioned and rotated correctly. Must be run when this MOSRotating is added to MovableMan to avoid issues with Attachables spawning in at (0, 0). + /// + virtual void CorrectAttachableAndWoundPositionsAndRotations() const; + + /// + /// Method to be run when the game is saved via ActivityMan::SaveCurrentGame. Not currently used in metagame or editor saving. + /// + void OnSave() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Transfers forces and impulse forces from the given Attachable to this MOSRotating, gibbing and/or removing the Attachable if needed. + /// + /// A pointer to the Attachable to apply forces from. Ownership is NOT transferred! + /// Whether or not the Attachable has been removed, in which case it'll usually be passed to MovableMan. + bool TransferForcesFromAttachable(Attachable* attachable); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateChildMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes this MO register itself and all its attached children in the + // MOID register and get ID:s for itself and its children for this frame. + // Arguments: The MOID index to register itself and its children in. + // The MOID of the root MO of this MO, ie the highest parent of this MO. + // 0 means that this MO is the root, ie it is owned by MovableMan. + // Whether this MO should make a new MOID to use for itself, or to use + // the same as the last one in the index (presumably its parent), + // Return value: None. + + void UpdateChildMOIDs(std::vector& MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; + + /// + /// Creates the particles specified by this MOSRotating's list of Gibs and adds them to MovableMan with appropriately randomized velocities, based on this MOSRotating's gib blast strength. + /// + /// The impulse (kg * m/s) of the impact that caused the gibbing to happen. + /// A pointer to an MO which the Attachables should not be colliding with. + void CreateGibsWhenGibbing(const Vector& impactImpulse, MovableObject* movableObjectToIgnore); + + /// + /// Removes all Attachables from this MOSR, deleting them or adding them to MovableMan as appropriate, and giving them randomized velocities based on their properties and this MOSRotating's gib blast strength. + /// + /// The impulse (kg * m/s) of the impact that caused the gibbing to happen. + /// A pointer to an MO which the Attachables should not be colliding with. + void RemoveAttachablesWhenGibbing(const Vector& impactImpulse, MovableObject* movableObjectToIgnore); + + // Member variables + static Entity::ClassInfo m_sClass; + // float m_Torque; // In kg * r/s^2 (Newtons). + // float m_ImpulseTorque; // In kg * r/s. + // The group of Atom:s that will be the physical reperesentation of this MOSRotating. + AtomGroup* m_pAtomGroup; + // The group of Atom:s that will serve as a means to detect deep terrain penetration. + AtomGroup* m_pDeepGroup; + // Whether or not to check for deep penetrations. + bool m_DeepCheck; + // A trigger for forcing a deep check to happen + bool m_ForceDeepCheck; + // Whether deep penetration happaned in the last frame or not, and how hard it was. + float m_DeepHardness; + // The amount of impulse force exerted on this during the last frame. + Vector m_TravelImpulse; + // The precomupted center location of the sprite relative to the MovableObject::m_Pos. + Vector m_SpriteCenter; + // How much to orient the rotation of this to match the velocity vector each frame 0 = none, 1.0 = immediately align with vel vector + float m_OrientToVel; + // Whether the SpriteMO is currently pushed back by recoil or not. + bool m_Recoiled; + // The impulse force in kg * m/s that represents the recoil. + Vector m_RecoilForce; + // The vector that the recoil offsets the sprite when m_Recoiled is true. + Vector m_RecoilOffset; + // The list of wound AEmitters currently attached to this MOSRotating, and owned here as well. + std::vector m_Wounds; + // The list of Attachables currently attached and Owned by this. + std::list m_Attachables; + std::unordered_set m_ReferenceHardcodedAttachableUniqueIDs; //!< An unordered set is filled with the Unique IDs of all of the reference object's hardcoded Attachables when using the copy Create. + std::unordered_map> m_HardcodedAttachableUniqueIDsAndSetters; //!< An unordered map of Unique IDs to setter lambda functions, used to call the appropriate hardcoded Attachable setter when a hardcoded Attachable is removed. + std::unordered_map> m_HardcodedAttachableUniqueIDsAndRemovers; //!< An unordered map of Unique IDs to remove lambda functions, used to call the appropriate hardcoded Attachable remover when a hardcoded Attachable is removed and calling the setter with nullptr won't work. + const Attachable* m_RadiusAffectingAttachable; //!< A pointer to the Attachable that is currently affecting the radius. Used for some efficiency benefits. + float m_FarthestAttachableDistanceAndRadius; //!< The distance + radius of the radius affecting Attachable. + float m_AttachableAndWoundMass; //!< The mass of all Attachables and wounds on this MOSRotating. Used in combination with its actual mass and any other affecting factors to get its total mass. + // The list of Gib:s this will create when gibbed + std::list m_Gibs; + // The amount of impulse force required to gib this, in kg * (m/s). 0 means no limit + float m_GibImpulseLimit; + int m_GibWoundLimit; //!< The number of wounds that will gib this MOSRotating. 0 means that it can't be gibbed via wounds. + float m_GibBlastStrength; //!< The strength with which Gibs and Attachables will get launched when this MOSRotating is gibbed. + float m_GibScreenShakeAmount; //!< Determines how much screenshake this causes upon gibbing. + float m_WoundCountAffectsImpulseLimitRatio; //!< The rate at which this MOSRotating's wound count will diminish the impulse limit. + bool m_DetachAttachablesBeforeGibbingFromWounds; //!< Whether to detach any Attachables of this MOSRotating when it should gib from hitting its wound limit, instead of gibbing the MOSRotating itself. + bool m_GibAtEndOfLifetime; //!< Whether or not this MOSRotating should gib when it reaches the end of its lifetime, instead of just deleting. + // Gib sound effect + SoundContainer* m_GibSound; + // Whether to flash effect on gib + bool m_EffectOnGib; + // How far this is audiable (in screens) when gibbing + float m_LoudnessOnGib; + + float m_DamageMultiplier; //!< Damage multiplier for this MOSRotating. + bool m_NoSetDamageMultiplier; //!< Whether or not the damage multiplier for this MOSRotating was set. + + Timer m_FlashWhiteTimer; //!< The timer for timing white draw mode duration. + + // Intermediary drawing bitmap used to flip rotating bitmaps. Owned! + BITMAP* m_pFlipBitmap; + BITMAP* m_pFlipBitmapS; + // Intermediary drawing bitmap used to draw sihouettes and other effects. Not owned; points to the shared static bitmaps + BITMAP* m_pTempBitmap; + // Temp drawing bitmaps shared between all MOSRotatings + static BITMAP* m_spTempBitmap16; + static BITMAP* m_spTempBitmap32; + static BITMAP* m_spTempBitmap64; + static BITMAP* m_spTempBitmap128; + static BITMAP* m_spTempBitmap256; + static BITMAP* m_spTempBitmap512; + + // Intermediary drawing bitmap used to draw MO silhouettes. Not owned; points to the shared static bitmaps + BITMAP* m_pTempBitmapS; + // Temp drawing bitmaps shared between all MOSRotatings + static BITMAP* m_spTempBitmapS16; + static BITMAP* m_spTempBitmapS32; + static BITMAP* m_spTempBitmapS64; + static BITMAP* m_spTempBitmapS128; + static BITMAP* m_spTempBitmapS256; + static BITMAP* m_spTempBitmapS512; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MOSRotating, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + MOSRotating(const MOSRotating& reference) = delete; + MOSRotating& operator=(const MOSRotating& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/MOSprite.cpp b/Source/Entities/MOSprite.cpp index 9851a8ee63..32b6f02e5d 100644 --- a/Source/Entities/MOSprite.cpp +++ b/Source/Entities/MOSprite.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,608 +17,570 @@ namespace RTE { -AbstractClassInfo(MOSprite, MovableObject); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MOSprite, effectively -// resetting the members of this abstraction level only. - -void MOSprite::Clear() -{ - m_SpriteFile.Reset(); - m_aSprite.clear(); - m_IconFile.Reset(); - m_GraphicalIcon = nullptr; - m_FrameCount = 1; - m_SpriteOffset.Reset(); - m_Frame = 0; - m_SpriteAnimMode = NOANIM; - m_SpriteAnimDuration = 500; - m_SpriteAnimTimer.Reset(); - m_SpriteAnimIsReversingFrames = false; - m_HFlipped = false; - m_SpriteRadius = 1.0F; - m_SpriteDiameter = 2.0F; - m_Rotation.Reset(); - m_PrevRotation.Reset(); - m_AngularVel = 0; - m_PrevAngVel = 0; - m_AngOscillations = 0; - m_SettleMaterialDisabled = false; - m_pEntryWound = 0; - m_pExitWound = 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MOSprite object ready for use. - -int MOSprite::Create() -{ - if (MovableObject::Create() < 0) - return -1; - - // Post-process reading - m_aSprite.clear(); - m_SpriteFile.GetAsAnimation(m_aSprite, m_FrameCount); - - if (!m_aSprite.empty() && m_aSprite[0]) - { - // Set default sprite offset - if (m_SpriteOffset.IsZero()) { m_SpriteOffset.SetXY(static_cast(-m_aSprite[0]->w) / 2.0F, static_cast(-m_aSprite[0]->h) / 2.0F); } - - // Calc maximum dimensions from the Pos, based on the sprite - float maxX = std::max(std::fabs(m_SpriteOffset.GetX()), std::fabs(static_cast(m_aSprite[0]->w) + m_SpriteOffset.GetX())); - float maxY = std::max(std::fabs(m_SpriteOffset.GetY()), std::fabs(static_cast(m_aSprite[0]->h) + m_SpriteOffset.GetY())); - m_SpriteRadius = std::sqrt((maxX * maxX) + (maxY * maxY)); - m_SpriteDiameter = m_SpriteRadius * 2.0F; - } - else - return -1; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MOSprite object ready for use. - -int MOSprite::Create(ContentFile spriteFile, - const int frameCount, - const float mass, - const Vector &position, - const Vector &velocity, - const unsigned long lifetime) -{ - MovableObject::Create(mass, position, velocity, 0, 0, lifetime); - - m_SpriteFile = spriteFile; - m_FrameCount = frameCount; - m_aSprite.clear(); - m_SpriteFile.GetAsAnimation(m_aSprite, m_FrameCount); - m_SpriteOffset.SetXY(static_cast(-m_aSprite[0]->w) / 2.0F, static_cast(-m_aSprite[0]->h) / 2.0F); - - m_HFlipped = false; - - // Calc maximum dimensions from the Pos, based on the sprite - float maxX = std::max(std::fabs(m_SpriteOffset.GetX()), std::fabs(static_cast(m_aSprite[0]->w) + m_SpriteOffset.GetX())); - float maxY = std::max(std::fabs(m_SpriteOffset.GetY()), std::fabs(static_cast(m_aSprite[0]->h) + m_SpriteOffset.GetY())); - m_SpriteRadius = std::sqrt((maxX * maxX) + (maxY * maxY)); - m_SpriteDiameter = m_SpriteRadius * 2.0F; + AbstractClassInfo(MOSprite, MovableObject); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MOSprite, effectively + // resetting the members of this abstraction level only. + + void MOSprite::Clear() { + m_SpriteFile.Reset(); + m_aSprite.clear(); + m_IconFile.Reset(); + m_GraphicalIcon = nullptr; + m_FrameCount = 1; + m_SpriteOffset.Reset(); + m_Frame = 0; + m_SpriteAnimMode = NOANIM; + m_SpriteAnimDuration = 500; + m_SpriteAnimTimer.Reset(); + m_SpriteAnimIsReversingFrames = false; + m_HFlipped = false; + m_SpriteRadius = 1.0F; + m_SpriteDiameter = 2.0F; + m_Rotation.Reset(); + m_PrevRotation.Reset(); + m_AngularVel = 0; + m_PrevAngVel = 0; + m_AngOscillations = 0; + m_SettleMaterialDisabled = false; + m_pEntryWound = 0; + m_pExitWound = 0; + } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MOSprite object ready for use. + int MOSprite::Create() { + if (MovableObject::Create() < 0) + return -1; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MOSprite to be identical to another, by deep copy. + // Post-process reading + m_aSprite.clear(); + m_SpriteFile.GetAsAnimation(m_aSprite, m_FrameCount); -int MOSprite::Create(const MOSprite &reference) -{ - MovableObject::Create(reference); + if (!m_aSprite.empty() && m_aSprite[0]) { + // Set default sprite offset + if (m_SpriteOffset.IsZero()) { + m_SpriteOffset.SetXY(static_cast(-m_aSprite[0]->w) / 2.0F, static_cast(-m_aSprite[0]->h) / 2.0F); + } - if (reference.m_aSprite.empty()) - return -1; + // Calc maximum dimensions from the Pos, based on the sprite + float maxX = std::max(std::fabs(m_SpriteOffset.GetX()), std::fabs(static_cast(m_aSprite[0]->w) + m_SpriteOffset.GetX())); + float maxY = std::max(std::fabs(m_SpriteOffset.GetY()), std::fabs(static_cast(m_aSprite[0]->h) + m_SpriteOffset.GetY())); + m_SpriteRadius = std::sqrt((maxX * maxX) + (maxY * maxY)); + m_SpriteDiameter = m_SpriteRadius * 2.0F; + } else + return -1; - m_SpriteFile = reference.m_SpriteFile; - m_IconFile = reference.m_IconFile; - m_GraphicalIcon = m_IconFile.GetAsBitmap(); + return 0; + } - m_FrameCount = reference.m_FrameCount; - m_Frame = reference.m_Frame; - m_aSprite = reference.m_aSprite; - m_SpriteOffset = reference.m_SpriteOffset; - m_SpriteAnimMode = reference.m_SpriteAnimMode; - m_SpriteAnimDuration = reference.m_SpriteAnimDuration; - m_HFlipped = reference.m_HFlipped; - m_SpriteRadius = reference.m_SpriteRadius; - m_SpriteDiameter = reference.m_SpriteDiameter; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MOSprite object ready for use. + + int MOSprite::Create(ContentFile spriteFile, + const int frameCount, + const float mass, + const Vector& position, + const Vector& velocity, + const unsigned long lifetime) { + MovableObject::Create(mass, position, velocity, 0, 0, lifetime); + + m_SpriteFile = spriteFile; + m_FrameCount = frameCount; + m_aSprite.clear(); + m_SpriteFile.GetAsAnimation(m_aSprite, m_FrameCount); + m_SpriteOffset.SetXY(static_cast(-m_aSprite[0]->w) / 2.0F, static_cast(-m_aSprite[0]->h) / 2.0F); + + m_HFlipped = false; + + // Calc maximum dimensions from the Pos, based on the sprite + float maxX = std::max(std::fabs(m_SpriteOffset.GetX()), std::fabs(static_cast(m_aSprite[0]->w) + m_SpriteOffset.GetX())); + float maxY = std::max(std::fabs(m_SpriteOffset.GetY()), std::fabs(static_cast(m_aSprite[0]->h) + m_SpriteOffset.GetY())); + m_SpriteRadius = std::sqrt((maxX * maxX) + (maxY * maxY)); + m_SpriteDiameter = m_SpriteRadius * 2.0F; + + return 0; + } - m_Rotation = reference.m_Rotation; - m_AngularVel = reference.m_AngularVel; - m_SettleMaterialDisabled = reference.m_SettleMaterialDisabled; - m_pEntryWound = reference.m_pEntryWound; - m_pExitWound = reference.m_pExitWound; -// if (reference.m_pExitWound) Not doing anymore since we're not owning -// m_pExitWound = dynamic_cast(reference.m_pExitWound->Clone()); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MOSprite to be identical to another, by deep copy. - return 0; -} + int MOSprite::Create(const MOSprite& reference) { + MovableObject::Create(reference); + if (reference.m_aSprite.empty()) + return -1; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int MOSprite::ReadProperty(const std::string_view &propName, Reader &reader) { - StartPropertyList(return MovableObject::ReadProperty(propName, reader)); - - MatchProperty("SpriteFile", { reader >> m_SpriteFile; }); - MatchProperty("IconFile", { - reader >> m_IconFile; + m_SpriteFile = reference.m_SpriteFile; + m_IconFile = reference.m_IconFile; m_GraphicalIcon = m_IconFile.GetAsBitmap(); - }); - MatchProperty("FrameCount", { - reader >> m_FrameCount; - m_aSprite.reserve(m_FrameCount); - }); - MatchProperty("SpriteOffset", { reader >> m_SpriteOffset; }); - MatchProperty("SpriteAnimMode", - { -// string mode; -// reader >> mode; - int mode; - reader >> mode; - m_SpriteAnimMode = (SpriteAnimMode)mode; -/* - if (mode == "NOANIM") - m_SpriteAnimMode = NOANIM; - else if (mode == "ALWAYSLOOP") - m_SpriteAnimMode = ALWAYSLOOP; - else if (mode == "ALWAYSPINGPONG") - m_SpriteAnimMode = ALWAYSPINGPONG; - else if (mode == "LOOPWHENACTIVE") - m_SpriteAnimMode = LOOPWHENACTIVE; - else - Abort -*/ - }); - MatchProperty("SpriteAnimDuration", { reader >> m_SpriteAnimDuration; }); - MatchProperty("HFlipped", { reader >> m_HFlipped; }); - MatchProperty("Rotation", { reader >> m_Rotation; }); - MatchProperty("AngularVel", { reader >> m_AngularVel; }); - MatchProperty("SettleMaterialDisabled", { reader >> m_SettleMaterialDisabled; }); - MatchProperty("EntryWound", { m_pEntryWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); - MatchProperty("ExitWound", { m_pExitWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); - - EndPropertyList; -} -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetEntryWound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets entry wound emitter for this MOSprite -void MOSprite::SetEntryWound(std::string presetName, std::string moduleName) -{ - if (presetName == "") - m_pEntryWound = 0; - else - m_pEntryWound = dynamic_cast(g_PresetMan.GetEntityPreset("AEmitter", presetName, moduleName)); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetExitWound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets exit wound emitter for this MOSprite -void MOSprite::SetExitWound(std::string presetName, std::string moduleName) -{ - if (presetName == "") - m_pExitWound = 0; - else - m_pExitWound = dynamic_cast(g_PresetMan.GetEntityPreset("AEmitter", presetName, moduleName)); -} + m_FrameCount = reference.m_FrameCount; + m_Frame = reference.m_Frame; + m_aSprite = reference.m_aSprite; + m_SpriteOffset = reference.m_SpriteOffset; + m_SpriteAnimMode = reference.m_SpriteAnimMode; + m_SpriteAnimDuration = reference.m_SpriteAnimDuration; + m_HFlipped = reference.m_HFlipped; + m_SpriteRadius = reference.m_SpriteRadius; + m_SpriteDiameter = reference.m_SpriteDiameter; + + m_Rotation = reference.m_Rotation; + m_AngularVel = reference.m_AngularVel; + m_SettleMaterialDisabled = reference.m_SettleMaterialDisabled; + m_pEntryWound = reference.m_pEntryWound; + m_pExitWound = reference.m_pExitWound; + // if (reference.m_pExitWound) Not doing anymore since we're not owning + // m_pExitWound = dynamic_cast(reference.m_pExitWound->Clone()); + + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int MOSprite::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return MovableObject::ReadProperty(propName, reader)); + + MatchProperty("SpriteFile", { reader >> m_SpriteFile; }); + MatchProperty("IconFile", { + reader >> m_IconFile; + m_GraphicalIcon = m_IconFile.GetAsBitmap(); + }); + MatchProperty("FrameCount", { + reader >> m_FrameCount; + m_aSprite.reserve(m_FrameCount); + }); + MatchProperty("SpriteOffset", { reader >> m_SpriteOffset; }); + MatchProperty("SpriteAnimMode", + { + // string mode; + // reader >> mode; + int mode; + reader >> mode; + m_SpriteAnimMode = (SpriteAnimMode)mode; + /* + if (mode == "NOANIM") + m_SpriteAnimMode = NOANIM; + else if (mode == "ALWAYSLOOP") + m_SpriteAnimMode = ALWAYSLOOP; + else if (mode == "ALWAYSPINGPONG") + m_SpriteAnimMode = ALWAYSPINGPONG; + else if (mode == "LOOPWHENACTIVE") + m_SpriteAnimMode = LOOPWHENACTIVE; + else + Abort + */ + }); + MatchProperty("SpriteAnimDuration", { reader >> m_SpriteAnimDuration; }); + MatchProperty("HFlipped", { reader >> m_HFlipped; }); + MatchProperty("Rotation", { reader >> m_Rotation; }); + MatchProperty("AngularVel", { reader >> m_AngularVel; }); + MatchProperty("SettleMaterialDisabled", { reader >> m_SettleMaterialDisabled; }); + MatchProperty("EntryWound", { m_pEntryWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); + MatchProperty("ExitWound", { m_pExitWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); + + EndPropertyList; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetEntryWoundPresetName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns entry wound emitter preset name for this MOSprite + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetEntryWound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets entry wound emitter for this MOSprite + void MOSprite::SetEntryWound(std::string presetName, std::string moduleName) { + if (presetName == "") + m_pEntryWound = 0; + else + m_pEntryWound = dynamic_cast(g_PresetMan.GetEntityPreset("AEmitter", presetName, moduleName)); + } -std::string MOSprite::GetEntryWoundPresetName() const -{ - return m_pEntryWound ? m_pEntryWound->GetPresetName() : ""; -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetExitWound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets exit wound emitter for this MOSprite + void MOSprite::SetExitWound(std::string presetName, std::string moduleName) { + if (presetName == "") + m_pExitWound = 0; + else + m_pExitWound = dynamic_cast(g_PresetMan.GetEntityPreset("AEmitter", presetName, moduleName)); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetExitWoundPresetName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns exit wound emitter preset name for this MOSprite + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetEntryWoundPresetName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns entry wound emitter preset name for this MOSprite + + std::string MOSprite::GetEntryWoundPresetName() const { + return m_pEntryWound ? m_pEntryWound->GetPresetName() : ""; + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetExitWoundPresetName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns exit wound emitter preset name for this MOSprite + + std::string MOSprite::GetExitWoundPresetName() const { + return m_pExitWound ? m_pExitWound->GetPresetName() : ""; + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this MOSprite with a Writer for + // later recreation with Create(Reader &reader); + + int MOSprite::Save(Writer& writer) const { + MovableObject::Save(writer); + // TODO: Make proper save system that knows not to save redundant data! + /* + writer.NewProperty("SpriteFile"); + writer << m_SpriteFile; + writer.NewProperty("FrameCount"); + writer << m_FrameCount; + writer.NewProperty("SpriteOffset"); + writer << m_SpriteOffset; + writer.NewProperty("SpriteAnimMode"); + writer << m_SpriteAnimMode; + writer.NewProperty("SpriteAnimDuration"); + writer << m_SpriteAnimDuration; + writer.NewProperty("HFlipped"); + writer << m_HFlipped; + writer.NewProperty("Rotation"); + writer << m_Rotation.GetRadAngle(); + writer.NewProperty("AngularVel"); + writer << m_AngularVel; + writer.NewProperty("SettleMaterialDisabled"); + writer << m_SettleMaterialDisabled; + writer.NewProperty("EntryWound"); + writer << m_pEntryWound; + writer.NewProperty("ExitWound"); + writer << m_pExitWound; + */ + return 0; + } -std::string MOSprite::GetExitWoundPresetName() const -{ - return m_pExitWound ? m_pExitWound->GetPresetName() : ""; -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the MOSprite object. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this MOSprite with a Writer for -// later recreation with Create(Reader &reader); - -int MOSprite::Save(Writer &writer) const -{ - MovableObject::Save(writer); -// TODO: Make proper save system that knows not to save redundant data! -/* - writer.NewProperty("SpriteFile"); - writer << m_SpriteFile; - writer.NewProperty("FrameCount"); - writer << m_FrameCount; - writer.NewProperty("SpriteOffset"); - writer << m_SpriteOffset; - writer.NewProperty("SpriteAnimMode"); - writer << m_SpriteAnimMode; - writer.NewProperty("SpriteAnimDuration"); - writer << m_SpriteAnimDuration; - writer.NewProperty("HFlipped"); - writer << m_HFlipped; - writer.NewProperty("Rotation"); - writer << m_Rotation.GetRadAngle(); - writer.NewProperty("AngularVel"); - writer << m_AngularVel; - writer.NewProperty("SettleMaterialDisabled"); - writer << m_SettleMaterialDisabled; - writer.NewProperty("EntryWound"); - writer << m_pEntryWound; - writer.NewProperty("ExitWound"); - writer << m_pExitWound; -*/ - return 0; -} + void MOSprite::Destroy(bool notInherited) { + // delete m_pEntryWound; Not doing this anymore since we're not owning + // delete m_pExitWound; + if (!notInherited) + MovableObject::Destroy(); + Clear(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MOSprite object. - -void MOSprite::Destroy(bool notInherited) -{ -// delete m_pEntryWound; Not doing this anymore since we're not owning -// delete m_pExitWound; - - if (!notInherited) - MovableObject::Destroy(); - Clear(); -} - -bool MOSprite::HitTestAtPixel(int pixelX, int pixelY) const { - if (!GetsHitByMOs() || GetRootParent()->GetTraveling()) { - return false; - } - - Vector distanceBetweenTestPositionAndMO = g_SceneMan.ShortestDistance(m_Pos, Vector(static_cast(pixelX), static_cast(pixelY))); - if (distanceBetweenTestPositionAndMO.MagnitudeIsGreaterThan(m_SpriteRadius)) { - return false; - } - - // Check the scene position in the current local space of the MO, accounting for Position, Sprite Offset, Angle and HFlipped. - //TODO Account for Scale as well someday, maybe. - Matrix rotation = m_Rotation; // <- Copy to non-const variable so / operator overload works. - Vector entryPos = (distanceBetweenTestPositionAndMO / rotation).GetXFlipped(m_HFlipped) - m_SpriteOffset; - int localX = entryPos.GetFloorIntX(); - int localY = entryPos.GetFloorIntY(); - - BITMAP* sprite = m_aSprite[m_Frame]; - return is_inside_bitmap(sprite, localX, localY, 0) && _getpixel(sprite, localX, localY) != ColorKeys::g_MaskColor; -} + bool MOSprite::HitTestAtPixel(int pixelX, int pixelY) const { + if (!GetsHitByMOs() || GetRootParent()->GetTraveling()) { + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFrame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Hard-sets the frame this sprite is supposed to show. + Vector distanceBetweenTestPositionAndMO = g_SceneMan.ShortestDistance(m_Pos, Vector(static_cast(pixelX), static_cast(pixelY))); + if (distanceBetweenTestPositionAndMO.MagnitudeIsGreaterThan(m_SpriteRadius)) { + return false; + } -void MOSprite::SetFrame(unsigned int newFrame) -{ - if (newFrame < 0) - newFrame = 0; - if (newFrame >= m_FrameCount) - newFrame = m_FrameCount - 1; + // Check the scene position in the current local space of the MO, accounting for Position, Sprite Offset, Angle and HFlipped. + // TODO Account for Scale as well someday, maybe. + Matrix rotation = m_Rotation; // <- Copy to non-const variable so / operator overload works. + Vector entryPos = (distanceBetweenTestPositionAndMO / rotation).GetXFlipped(m_HFlipped) - m_SpriteOffset; + int localX = entryPos.GetFloorIntX(); + int localY = entryPos.GetFloorIntY(); - m_Frame = newFrame; -} + BITMAP* sprite = m_aSprite[m_Frame]; + return is_inside_bitmap(sprite, localX, localY, 0) && _getpixel(sprite, localX, localY) != ColorKeys::g_MaskColor; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetFrame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Hard-sets the frame this sprite is supposed to show. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetNextFrame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Hard-sets the frame this sprite is supposed to show, to the -// consecutive one after the current one. If currently the last fame is -// this will set it to the be the first, looping the animation. + void MOSprite::SetFrame(unsigned int newFrame) { + if (newFrame < 0) + newFrame = 0; + if (newFrame >= m_FrameCount) + newFrame = m_FrameCount - 1; -bool MOSprite::SetNextFrame() -{ - if (++m_Frame >= m_FrameCount) - { - m_Frame = 0; - return true; - } - return false; -} + m_Frame = newFrame; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetNextFrame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Hard-sets the frame this sprite is supposed to show, to the + // consecutive one after the current one. If currently the last fame is + // this will set it to the be the first, looping the animation. + + bool MOSprite::SetNextFrame() { + if (++m_Frame >= m_FrameCount) { + m_Frame = 0; + return true; + } + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. - -bool MOSprite::IsOnScenePoint(Vector &scenePoint) const -{ - if (!m_aSprite[m_Frame]) - return false; -// TODO: TAKE CARE OF WRAPPING -/* - // Take care of wrapping situations - bitmapPos = m_Pos + m_BitmapOffset; - Vector aScenePoint[4]; - aScenePoint[0] = scenePoint; - int passes = 1; - - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero()) - { - if (g_SceneMan.SceneWrapsX()) - { - if (bitmapPos.m_X < m_pFGColor->w) - { - aScenePoint[passes] = aScenePoint[0]; - aScenePoint[passes].m_X += g_SceneMan.GetSceneWidth(); - passes++; - } - else if (aScenePoint[0].m_X > pTargetBitmap->w - m_pFGColor->w) - { - aScenePoint[passes] = aScenePoint[0]; - aScenePoint[passes].m_X -= g_SceneMan.GetSceneWidth(); - passes++; - } - } - if (g_SceneMan.SceneWrapsY()) - { - - } - } - - // Check all the passes needed - for (int i = 0; i < passes; ++i) - { - if (IsWithinBox(aScenePoint[i], m_Pos + m_BitmapOffset, m_pFGColor->w, m_pFGColor->h)) - { - if (getpixel(m_pFGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor || - (m_pBGColor && getpixel(m_pBGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor) || - (m_pMaterial && getpixel(m_pMaterial, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaterialAir)) - return true; - } - } -*/ - if (WithinBox(scenePoint, m_Pos.m_X - m_SpriteRadius, m_Pos.m_Y - m_SpriteRadius, m_Pos.m_X + m_SpriteRadius, m_Pos.m_Y + m_SpriteRadius)) - { - // Get scene point in object's relative space - Vector spritePoint = scenePoint - m_Pos; - spritePoint.FlipX(m_HFlipped); - // Check over overlap - int pixel = getpixel(m_aSprite[m_Frame], spritePoint.m_X - m_SpriteOffset.m_X, spritePoint.m_Y - m_SpriteOffset.m_Y); - // Check that it isn't outside the bitmap, and not of the key color - if (pixel != -1 && pixel != g_MaskColor) - return true; - } - - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsOnScenePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this' current graphical representation overlaps + // a point in absolute scene coordinates. + + bool MOSprite::IsOnScenePoint(Vector& scenePoint) const { + if (!m_aSprite[m_Frame]) + return false; + // TODO: TAKE CARE OF WRAPPING + /* + // Take care of wrapping situations + bitmapPos = m_Pos + m_BitmapOffset; + Vector aScenePoint[4]; + aScenePoint[0] = scenePoint; + int passes = 1; + + // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap + if (targetPos.IsZero()) + { + if (g_SceneMan.SceneWrapsX()) + { + if (bitmapPos.m_X < m_pFGColor->w) + { + aScenePoint[passes] = aScenePoint[0]; + aScenePoint[passes].m_X += g_SceneMan.GetSceneWidth(); + passes++; + } + else if (aScenePoint[0].m_X > pTargetBitmap->w - m_pFGColor->w) + { + aScenePoint[passes] = aScenePoint[0]; + aScenePoint[passes].m_X -= g_SceneMan.GetSceneWidth(); + passes++; + } + } + if (g_SceneMan.SceneWrapsY()) + { + + } + } + + // Check all the passes needed + for (int i = 0; i < passes; ++i) + { + if (IsWithinBox(aScenePoint[i], m_Pos + m_BitmapOffset, m_pFGColor->w, m_pFGColor->h)) + { + if (getpixel(m_pFGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor || + (m_pBGColor && getpixel(m_pBGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor) || + (m_pMaterial && getpixel(m_pMaterial, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaterialAir)) + return true; + } + } + */ + if (WithinBox(scenePoint, m_Pos.m_X - m_SpriteRadius, m_Pos.m_Y - m_SpriteRadius, m_Pos.m_X + m_SpriteRadius, m_Pos.m_Y + m_SpriteRadius)) { + // Get scene point in object's relative space + Vector spritePoint = scenePoint - m_Pos; + spritePoint.FlipX(m_HFlipped); + // Check over overlap + int pixel = getpixel(m_aSprite[m_Frame], spritePoint.m_X - m_SpriteOffset.m_X, spritePoint.m_Y - m_SpriteOffset.m_Y); + // Check that it isn't outside the bitmap, and not of the key color + if (pixel != -1 && pixel != g_MaskColor) + return true; + } + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: RotateOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Rotates a vector offset from this MORotating's position according to -// the rotate angle and flipping. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: RotateOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Rotates a vector offset from this MORotating's position according to + // the rotate angle and flipping. -Vector MOSprite::RotateOffset(const Vector &offset) const -{ - Vector rotOff(offset.GetXFlipped(m_HFlipped)); - rotOff *= const_cast(m_Rotation); - return rotOff; -} + Vector MOSprite::RotateOffset(const Vector& offset) const { + Vector rotOff(offset.GetXFlipped(m_HFlipped)); + rotOff *= const_cast(m_Rotation); + return rotOff; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UnRotateOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Rotates a vector offset from this MORotating's position according to + // the NEGATIVE rotate angle and takes flipping into account. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UnRotateOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Rotates a vector offset from this MORotating's position according to -// the NEGATIVE rotate angle and takes flipping into account. + Vector MOSprite::UnRotateOffset(const Vector& offset) const { + Vector rotOff(offset.GetXFlipped(m_HFlipped)); + rotOff /= const_cast(m_Rotation); + return rotOff; + } -Vector MOSprite::UnRotateOffset(const Vector &offset) const -{ - Vector rotOff(offset.GetXFlipped(m_HFlipped)); - rotOff /= const_cast(m_Rotation); - return rotOff; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure v. method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MOSprite. Supposed to be done every frame. + void MOSprite::Update() { + MovableObject::Update(); -////////////////////////////////////////////////////////////////////////////////////////// -// Pure v. method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MOSprite. Supposed to be done every frame. - -void MOSprite::Update() { - MovableObject::Update(); - - // First, check that the sprite has enough frames to even have an animation and override the setting if not - if (m_FrameCount > 1) { - // If animation mode is set to something other than ALWAYSLOOP but only has 2 frames, override it because it's pointless - if ((m_SpriteAnimMode == ALWAYSRANDOM || m_SpriteAnimMode == ALWAYSPINGPONG) && m_FrameCount == 2) { - m_SpriteAnimMode = ALWAYSLOOP; - } else if (m_SpriteAnimMode == OVERLIFETIME) { - // If animation mode is set to over lifetime but lifetime is unlimited, override to always loop otherwise it will never animate. - if (m_Lifetime == 0) { + // First, check that the sprite has enough frames to even have an animation and override the setting if not + if (m_FrameCount > 1) { + // If animation mode is set to something other than ALWAYSLOOP but only has 2 frames, override it because it's pointless + if ((m_SpriteAnimMode == ALWAYSRANDOM || m_SpriteAnimMode == ALWAYSPINGPONG) && m_FrameCount == 2) { m_SpriteAnimMode = ALWAYSLOOP; - } else { - double lifeTimeFrame = static_cast(m_FrameCount) * (m_AgeTimer.GetElapsedSimTimeMS() / static_cast(m_Lifetime)); - m_Frame = static_cast(std::floor(lifeTimeFrame)); - if (m_Frame >= m_FrameCount) { m_Frame = m_FrameCount - 1; } - return; + } else if (m_SpriteAnimMode == OVERLIFETIME) { + // If animation mode is set to over lifetime but lifetime is unlimited, override to always loop otherwise it will never animate. + if (m_Lifetime == 0) { + m_SpriteAnimMode = ALWAYSLOOP; + } else { + double lifeTimeFrame = static_cast(m_FrameCount) * (m_AgeTimer.GetElapsedSimTimeMS() / static_cast(m_Lifetime)); + m_Frame = static_cast(std::floor(lifeTimeFrame)); + if (m_Frame >= m_FrameCount) { + m_Frame = m_FrameCount - 1; + } + return; + } } + } else { + m_SpriteAnimMode = NOANIM; } - } else { - m_SpriteAnimMode = NOANIM; - } - // Animate the sprite, if applicable - unsigned int frameTime = m_SpriteAnimDuration / m_FrameCount; - unsigned int prevFrame = m_Frame; - - if (m_SpriteAnimTimer.GetElapsedSimTimeMS() > frameTime) { - switch (m_SpriteAnimMode) { - case ALWAYSLOOP: - m_Frame = ((m_Frame + 1) % m_FrameCount); - m_SpriteAnimTimer.Reset(); - break; - case ALWAYSRANDOM: - while (m_Frame == prevFrame) { - m_Frame = RandomNum(0, m_FrameCount - 1); - } - m_SpriteAnimTimer.Reset(); - break; - case ALWAYSPINGPONG: - if (m_Frame == m_FrameCount - 1) { - m_SpriteAnimIsReversingFrames = true; - } else if (m_Frame == 0) { - m_SpriteAnimIsReversingFrames = false; - } - m_SpriteAnimIsReversingFrames ? m_Frame-- : m_Frame++; - m_SpriteAnimTimer.Reset(); - break; - default: - break; + // Animate the sprite, if applicable + unsigned int frameTime = m_SpriteAnimDuration / m_FrameCount; + unsigned int prevFrame = m_Frame; + + if (m_SpriteAnimTimer.GetElapsedSimTimeMS() > frameTime) { + switch (m_SpriteAnimMode) { + case ALWAYSLOOP: + m_Frame = ((m_Frame + 1) % m_FrameCount); + m_SpriteAnimTimer.Reset(); + break; + case ALWAYSRANDOM: + while (m_Frame == prevFrame) { + m_Frame = RandomNum(0, m_FrameCount - 1); + } + m_SpriteAnimTimer.Reset(); + break; + case ALWAYSPINGPONG: + if (m_Frame == m_FrameCount - 1) { + m_SpriteAnimIsReversingFrames = true; + } else if (m_Frame == 0) { + m_SpriteAnimIsReversingFrames = false; + } + m_SpriteAnimIsReversingFrames ? m_Frame-- : m_Frame++; + m_SpriteAnimTimer.Reset(); + break; + default: + break; + } } } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this MOSprite's current graphical representation to a + // BITMAP of choice. + + void MOSprite::Draw(BITMAP* pTargetBitmap, + const Vector& targetPos, + DrawMode mode, + bool onlyPhysical) const { + if (!m_aSprite[m_Frame]) + RTEAbort("Sprite frame pointer is null when drawing MOSprite!"); + + // Apply offsets and positions. + Vector spriteOffset; + if (m_HFlipped) + spriteOffset.SetXY(-(m_aSprite[m_Frame]->w + m_SpriteOffset.m_X), m_SpriteOffset.m_Y); + else + spriteOffset = m_SpriteOffset; + + Vector spritePos(m_Pos + spriteOffset - targetPos); + + // Take care of wrapping situations + Vector aDrawPos[4]; + aDrawPos[0] = spritePos; + int passes = 1; + + // Only bother with wrap drawing if the scene actually wraps around + if (g_SceneMan.SceneWrapsX()) { + // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap + if (targetPos.IsZero() && m_WrapDoubleDraw) { + if (spritePos.m_X < m_aSprite[m_Frame]->w) { + aDrawPos[passes] = spritePos; + aDrawPos[passes].m_X += pTargetBitmap->w; + passes++; + } else if (spritePos.m_X > pTargetBitmap->w - m_aSprite[m_Frame]->w) { + aDrawPos[passes] = spritePos; + aDrawPos[passes].m_X -= pTargetBitmap->w; + passes++; + } + } + // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam + else if (m_WrapDoubleDraw) { + if (targetPos.m_X < 0) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X -= g_SceneMan.GetSceneWidth(); + passes++; + } + if (targetPos.m_X + pTargetBitmap->w > g_SceneMan.GetSceneWidth()) { + aDrawPos[passes] = aDrawPos[0]; + aDrawPos[passes].m_X += g_SceneMan.GetSceneWidth(); + passes++; + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MOSprite's current graphical representation to a -// BITMAP of choice. - -void MOSprite::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - if (!m_aSprite[m_Frame]) - RTEAbort("Sprite frame pointer is null when drawing MOSprite!"); - - // Apply offsets and positions. - Vector spriteOffset; - if (m_HFlipped) - spriteOffset.SetXY(-(m_aSprite[m_Frame]->w + m_SpriteOffset.m_X), m_SpriteOffset.m_Y); - else - spriteOffset = m_SpriteOffset; - - Vector spritePos(m_Pos + spriteOffset - targetPos); - - // Take care of wrapping situations - Vector aDrawPos[4]; - aDrawPos[0] = spritePos; - int passes = 1; - - // Only bother with wrap drawing if the scene actually wraps around - if (g_SceneMan.SceneWrapsX()) - { - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero() && m_WrapDoubleDraw) - { - if (spritePos.m_X < m_aSprite[m_Frame]->w) - { - aDrawPos[passes] = spritePos; - aDrawPos[passes].m_X += pTargetBitmap->w; - passes++; - } - else if (spritePos.m_X > pTargetBitmap->w - m_aSprite[m_Frame]->w) - { - aDrawPos[passes] = spritePos; - aDrawPos[passes].m_X -= pTargetBitmap->w; - passes++; - } - } - // Only screenwide target bitmap, so double draw within the screen if the screen is straddling a scene seam - else if (m_WrapDoubleDraw) - { - if (targetPos.m_X < 0) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X -= g_SceneMan.GetSceneWidth(); - passes++; - } - if (targetPos.m_X + pTargetBitmap->w > g_SceneMan.GetSceneWidth()) - { - aDrawPos[passes] = aDrawPos[0]; - aDrawPos[passes].m_X += g_SceneMan.GetSceneWidth(); - passes++; - } - } - } - - for (int i = 0; i < passes; ++i) - { - int spriteX = aDrawPos[i].GetFloorIntX(); - int spriteY = aDrawPos[i].GetFloorIntY(); - switch (mode) { - case g_DrawMaterial: - RTEAbort("Ordered to draw an MOSprite in its material, which is not possible!"); - break; - case g_DrawWhite: - draw_character_ex(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY, g_WhiteColor, -1); - break; - case g_DrawMOID: + for (int i = 0; i < passes; ++i) { + int spriteX = aDrawPos[i].GetFloorIntX(); + int spriteY = aDrawPos[i].GetFloorIntY(); + switch (mode) { + case g_DrawMaterial: + RTEAbort("Ordered to draw an MOSprite in its material, which is not possible!"); + break; + case g_DrawWhite: + draw_character_ex(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY, g_WhiteColor, -1); + break; + case g_DrawMOID: #ifdef DRAW_MOID_LAYER - draw_character_ex(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY, m_MOID, -1); + draw_character_ex(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY, m_MOID, -1); #endif - break; - case g_DrawNoMOID: - draw_character_ex(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY, g_NoMOID, -1); - break; - case g_DrawTrans: - draw_trans_sprite(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY); - break; - case g_DrawAlpha: - set_alpha_blender(); - draw_trans_sprite(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY); - break; - default: - if (!m_HFlipped) { - draw_sprite(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY); - } else { - draw_sprite_h_flip(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY); - } - } - - g_SceneMan.RegisterDrawing(pTargetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, spriteX, spriteY, spriteX + m_aSprite[m_Frame]->w, spriteY + m_aSprite[m_Frame]->h); - } -} + break; + case g_DrawNoMOID: + draw_character_ex(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY, g_NoMOID, -1); + break; + case g_DrawTrans: + draw_trans_sprite(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY); + break; + case g_DrawAlpha: + set_alpha_blender(); + draw_trans_sprite(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY); + break; + default: + if (!m_HFlipped) { + draw_sprite(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY); + } else { + draw_sprite_h_flip(pTargetBitmap, m_aSprite[m_Frame], spriteX, spriteY); + } + } + + g_SceneMan.RegisterDrawing(pTargetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, spriteX, spriteY, spriteX + m_aSprite[m_Frame]->w, spriteY + m_aSprite[m_Frame]->h); + } + } } // namespace RTE \ No newline at end of file diff --git a/Source/Entities/MOSprite.h b/Source/Entities/MOSprite.h index c21a52834e..adf79d404f 100644 --- a/Source/Entities/MOSprite.h +++ b/Source/Entities/MOSprite.h @@ -10,623 +10,582 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "MovableObject.h" #include "Box.h" -namespace RTE -{ - -class AEmitter; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Abstract class: MOSprite -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A movable object with mass that is graphically represented by a -// BITMAP. -// Parent(s): MovableObject. -// Class history: 03/18/2001 MOSprite created. - -class MOSprite : public MovableObject { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - SerializableOverrideMethods; - ClassInfoGetters; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: MOSprite -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a MOSprite object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - MOSprite() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~MOSprite -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a MOSprite object before deletion -// from system memory. -// Arguments: None. - - ~MOSprite() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MOSprite object ready for use. -// Arguments: A pointer to ContentFile that represents the bitmap file that will be -// used to create the Sprite. -// The number of frames in the Sprite's animation. -// A float specifying the object's mass in Kilograms (kg). -// A Vector specifying the initial position. -// A Vector specifying the initial velocity. -// The amount of time in ms this MovableObject will exist. 0 means unlim. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(ContentFile spriteFile, const int frameCount = 1, const float mass = 1, const Vector &position = Vector(0, 0), const Vector &velocity = Vector(0, 0), const unsigned long lifetime = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MOSprite to be identical to another, by deep copy. -// Arguments: A reference to the MOSprite to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const MOSprite &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MOSprite object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire MOSprite, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); MovableObject::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MOSprite object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetRadius -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the largest radius of this in pixels. -// Arguments: None. -// Return value: The radius from its center to the edge of its graphical representation. - - float GetRadius() const override { return m_SpriteRadius; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetDiameter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the largest diameter of this in pixels. -// Arguments: None. -// Return value: The largest diameter across its graphical representation. - - float GetDiameter() const override { return m_SpriteDiameter; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAboveHUDPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of the top of this' HUD stack. -// Arguments: None. -// Return value: A Vector with the absolute position of this' HUD stack top point. - - Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, -GetRadius()); } - -// TODO: Improve this one! Really crappy fit -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetBoundingBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the oriented bounding box which is guaranteed to contain this, -// taking rotation etc into account. It's not guaranteed to be fit -// perfectly though. TODO: MAKE FIT BETTER -// Arguments: None. -// Return value: A Box which is guaranteed to contain this. Does nto take wrapping into -// account, and parts of this box may be out of bounds! - - Box GetBoundingBox() const { return Box(m_Pos + Vector(-GetRadius(), -GetRadius()), GetDiameter(), GetDiameter()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSpriteFrame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a frame of this MOSprite's BITMAP array. -// Arguments: Which frame to get. -// Return value: A pointer to the requested frame of this MOSprite's BITMAP array. -// Ownership is NOT transferred! - - BITMAP * GetSpriteFrame(int whichFrame = 0) const { return (whichFrame >= 0 && whichFrame < m_FrameCount) ? m_aSprite[whichFrame] : 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSpriteWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the width of the bitmap of this MOSprite -// Arguments: 0. -// Return value: Sprite width if loaded. - - int GetSpriteWidth() const { return m_aSprite[0] ? m_aSprite[0]->w : 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSpriteHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the height of the bitmap of this MOSprite -// Arguments: 0. -// Return value: Sprite height if loaded. - - int GetSpriteHeight() const { return m_aSprite[0] ? m_aSprite[0]->h : 0; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFrameCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of frames in this MOSprite's animation. -// Arguments: None. -// Return value: The frame count. - - int GetFrameCount() const { return m_FrameCount; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSpriteOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the offset that the BITMAP has from the position of this -// MOSprite. -// Arguments: None. -// Return value: A vector with the offset. - - Vector GetSpriteOffset() const { return m_SpriteOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsHFlipped -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether this MOSprite is being drawn flipped horizontally -// (along the vertical axis), or not. -// Arguments: None. -// Return value: Whether flipped or not. - - bool IsHFlipped() const override { return m_HFlipped; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetRotMatrix -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current rotational Matrix of of this. -// Arguments: None. -// Return value: The rotational Matrix of this MovableObject. - - Matrix GetRotMatrix() const override { return m_Rotation; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetRotAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current rotational angle of of this, in radians. -// Arguments: None. -// Return value: The rotational angle of this, in radians. - - float GetRotAngle() const override { return m_Rotation.GetRadAngle(); } - - /// - /// Gets the previous rotational angle of this MOSprite, prior to this frame. - /// - /// The previous rotational angle in radians. - float GetPrevRotAngle() const { return m_PrevRotation.GetRadAngle(); } - - /// - /// Whether a set of X, Y coordinates overlap us (in world space). - /// - /// The given X coordinate, in world space. - /// The given Y coordinate, in world space. - /// Whether the given coordinate overlap us. - bool HitTestAtPixel(int pixelX, int pixelY) const override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAngularVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current angular velocity of this MovableObject. Positive is -// a counter-clockwise rotation. -// Arguments: None. -// Return value: The angular velocity in radians per second. - - float GetAngularVel() const override { return m_AngularVel; } - - -/* Can't do this since sprite is owned by ContentMan. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSprite -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Replaces this MOSprite's BITMAP and deletes the old one. -// Arguments: A pointer to a BITMAP. -// Return value: None. - - void SetSprite(BITMAP *pSprite) { delete m_aSprite; m_aSprite = pSprite; } -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSpriteOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the offset that the BITMAP has from the position of this -// MOSprite. -// Arguments: A vector with the new offset. -// Return value: None. - - void SetSpriteOffset(const Vector &newOffset) { m_SpriteOffset = newOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFrame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Hard-sets the frame this sprite is supposed to show. -// Arguments: An unsigned int pecifiying the new frame. -// Return value: None. - - void SetFrame(unsigned int newFrame); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetNextFrame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Hard-sets the frame this sprite is supposed to show, to the -// consecutive one after the current one. If currently the last fame is -// this will set it to the be the first, looping the animation. -// Arguments: None. -// Return value: Whether the animation looped or not with this setting. - - bool SetNextFrame(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFrame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells which frame is currently set to show. -// Arguments: None. -// Return value: An unsigned int describing the current frame. - - unsigned int GetFrame() const { return m_Frame; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSpriteAnimMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the animation mode. -// Arguments: The animation mode we want to set. -// Return value: None. - - void SetSpriteAnimMode(int animMode = NOANIM) { m_SpriteAnimMode = (SpriteAnimMode)animMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSpriteAnimMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the animation mode. -// Arguments: None. -// Return value: The animation mode currently in effect. - - int GetSpriteAnimMode() const { return m_SpriteAnimMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: SetHFlipped -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this MOSprite should be drawn flipped horizontally -// (along the vertical axis). -// Arguments: A bool with the new value. -// Return value: None. - - void SetHFlipped(const bool flipped) override { m_HFlipped = flipped; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetRotAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current absolute angle of rotation of this MovableObject. -// Arguments: The new absolute angle in radians. -// Return value: None. - - void SetRotAngle(float newAngle) override { m_Rotation.SetRadAngle(newAngle); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetAngularVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current angular velocity of this MovableObject. Positive is -// a counter clockwise rotation. -// Arguments: The new angular velocity in radians per second. -// Return value: None. - - void SetAngularVel(float newRotVel) override { m_AngularVel = newRotVel; } - - - /// - /// Gets the GUI representation of this MOSprite, either based on the first frame of its sprite or separately defined icon file. - /// - /// The graphical representation of this MOSprite as a BITMAP. - BITMAP * GetGraphicalIcon() const override { return m_GraphicalIcon != nullptr ? m_GraphicalIcon : m_aSprite[0]; } - - /// - /// Gets the width of this MOSprite's GUI icon. - /// - /// The width of the GUI icon bitmap. - int GetIconWidth() const { return GetGraphicalIcon()->w; } - - /// - /// Gets the height of this MOSprite's GUI icon. - /// - /// The height of the GUI icon bitmap. - int GetIconHeight() const { return GetGraphicalIcon()->h; } - - /// - /// Forces this MOSprite out of resting conditions. - /// - void NotResting() override { MovableObject::NotResting(); m_AngOscillations = 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsTooFast -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is moving or rotating stupidly fast in a way -// that will screw up the simulation. -// Arguments: None. -// Return value: Whether this is either moving or rotating too fast. - - bool IsTooFast() const override { return m_Vel.MagnitudeIsGreaterThan(500.0F) || std::fabs(m_AngularVel) > (2000.0F / (GetRadius() + 1.0F)); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: FixTooFast -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Slows the speed of anything that is deemed to be too fast to within -// acceptable rates. -// Arguments: None. -// Return value: None. - - void FixTooFast() override { while (IsTooFast()) { m_Vel *= 0.5F; m_AngularVel *= 0.5F; } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - - bool IsOnScenePoint(Vector &scenePoint) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: RotateOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes a vector which is offset from the center of this when not rotated -// or flipped, and then rotates and/or flips it to transform it into this' -// 'local space', if applicable. -// Arguments: A vector which is supposed to be offset from this' center when upright. -// Return value: The resulting vector whihch has been flipped and rotated as appropriate. - - Vector RotateOffset(const Vector &offset) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UnRotateOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes a vector which is offset from the center of this when not rotated -// or flipped, and then rotates and/or flips it to transform it into this' -// 'local space', but in REVERSE. -// Arguments: A vector which is supposed to be offset from this' center when upright. -// Return value: The resulting vector whihch has been flipped and rotated as appropriate. - - Vector UnRotateOffset(const Vector &offset) const; - - /// - /// Adjusts an absolute angle based on wether this MOSprite is flipped. - /// - /// The input angle in radians. - /// The output angle in radians, which will be unaltered if this MOSprite is not flipped. - float FacingAngle(float angle) const { return (m_HFlipped ? c_PI : 0) + (angle * GetFlipFactor()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetEntryWound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets entry wound emitter for this MOSprite -// Arguments: Emitter preset name and module name -// Return value: None - - void SetEntryWound(std::string presetName, std::string moduleName); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetExitWound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets exit wound emitter for this MOSprite -// Arguments: Emitter preset name and module name -// Return value: None - - void SetExitWound(std::string presetName, std::string moduleName); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetEntryWoundPresetName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns entry wound emitter preset name for this MOSprite -// Arguments: None -// Return value: Wound emitter preset name - - std::string GetEntryWoundPresetName() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetExitWoundPresetName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns exit wound emitter preset name for this MOSprite -// Arguments: None -// Return value: Wound emitter preset name - - std::string GetExitWoundPresetName() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetSpriteAnimDuration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns animation duration in ms -// Arguments: None -// Return value: Animation duration in ms - - int GetSpriteAnimDuration() const { return m_SpriteAnimDuration; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetSpriteAnimDuration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets animation duration in ms -// Arguments: Animation duration in ms -// Return value: Mone - - void SetSpriteAnimDuration(int newDuration) { m_SpriteAnimDuration = newDuration; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MOSprite's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetFlipFactor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a positive or negative number value to multiply with for external calculations. -// Arguments: None. -// Return value: 1 for not flipped, -1 for flipped. - - float GetFlipFactor() const { return m_HFlipped ? -1.0F : 1.0F; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - - Matrix m_Rotation; // Rotational matrix of this MovableObject. - Matrix m_PrevRotation; // Rotational matrix of this MovableObject, last frame. - float m_AngularVel; // The angular velocity by which this MovableObject rotates, in radians per second (r/s). - float m_PrevAngVel; // Previous frame's angular velocity. - ContentFile m_SpriteFile; - // Vector of pointers to BITMAPs representing the multiple frames of this sprite. - std::vector m_aSprite; - ContentFile m_IconFile; //!< The file containing the GUI icon. - BITMAP *m_GraphicalIcon; //!< The GUI representation of this MOSprite as a BITMAP. - // Number of frames, or elements in the m_aSprite array. - unsigned int m_FrameCount; - Vector m_SpriteOffset; - // State, which frame of sprite to be drawn. - unsigned int m_Frame; - // Which animation to use for the body - SpriteAnimMode m_SpriteAnimMode; - // Body animation duration, how long in ms that each full body animation cycle takes. - int m_SpriteAnimDuration; - // The timer to keep track of the body animation - Timer m_SpriteAnimTimer; - // Keep track of animation direction (mainly for ALWAYSPINGPONG), true is decreasing frame, false is increasing frame - bool m_SpriteAnimIsReversingFrames; - // Whether flipped horizontally or not. - bool m_HFlipped; - // The precalculated maximum possible radius and diameter of this, in pixels - float m_SpriteRadius; - float m_SpriteDiameter; - int m_AngOscillations; //!< A counter for oscillations in rotation, in order to detect settling. - // Whether to disable the settle material ID when this gets drawn as material - bool m_SettleMaterialDisabled; - // Entry wound template - const AEmitter *m_pEntryWound; - // Exit wound template - const AEmitter *m_pExitWound; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MOSprite, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - MOSprite(const MOSprite &reference) = delete; - MOSprite & operator=(const MOSprite &rhs) = delete; - -}; +namespace RTE { + + class AEmitter; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Abstract class: MOSprite + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A movable object with mass that is graphically represented by a + // BITMAP. + // Parent(s): MovableObject. + // Class history: 03/18/2001 MOSprite created. + + class MOSprite : public MovableObject { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: MOSprite + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a MOSprite object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + MOSprite() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~MOSprite + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a MOSprite object before deletion + // from system memory. + // Arguments: None. + + ~MOSprite() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MOSprite object ready for use. + // Arguments: A pointer to ContentFile that represents the bitmap file that will be + // used to create the Sprite. + // The number of frames in the Sprite's animation. + // A float specifying the object's mass in Kilograms (kg). + // A Vector specifying the initial position. + // A Vector specifying the initial velocity. + // The amount of time in ms this MovableObject will exist. 0 means unlim. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(ContentFile spriteFile, const int frameCount = 1, const float mass = 1, const Vector& position = Vector(0, 0), const Vector& velocity = Vector(0, 0), const unsigned long lifetime = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MOSprite to be identical to another, by deep copy. + // Arguments: A reference to the MOSprite to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const MOSprite& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MOSprite object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire MOSprite, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + MovableObject::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the MOSprite object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRadius + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the largest radius of this in pixels. + // Arguments: None. + // Return value: The radius from its center to the edge of its graphical representation. + + float GetRadius() const override { return m_SpriteRadius; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetDiameter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the largest diameter of this in pixels. + // Arguments: None. + // Return value: The largest diameter across its graphical representation. + + float GetDiameter() const override { return m_SpriteDiameter; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAboveHUDPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of the top of this' HUD stack. + // Arguments: None. + // Return value: A Vector with the absolute position of this' HUD stack top point. + + Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, -GetRadius()); } + + // TODO: Improve this one! Really crappy fit + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetBoundingBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the oriented bounding box which is guaranteed to contain this, + // taking rotation etc into account. It's not guaranteed to be fit + // perfectly though. TODO: MAKE FIT BETTER + // Arguments: None. + // Return value: A Box which is guaranteed to contain this. Does nto take wrapping into + // account, and parts of this box may be out of bounds! + + Box GetBoundingBox() const { return Box(m_Pos + Vector(-GetRadius(), -GetRadius()), GetDiameter(), GetDiameter()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSpriteFrame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a frame of this MOSprite's BITMAP array. + // Arguments: Which frame to get. + // Return value: A pointer to the requested frame of this MOSprite's BITMAP array. + // Ownership is NOT transferred! + + BITMAP* GetSpriteFrame(int whichFrame = 0) const { return (whichFrame >= 0 && whichFrame < m_FrameCount) ? m_aSprite[whichFrame] : 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSpriteWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the width of the bitmap of this MOSprite + // Arguments: 0. + // Return value: Sprite width if loaded. + + int GetSpriteWidth() const { return m_aSprite[0] ? m_aSprite[0]->w : 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSpriteHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the height of the bitmap of this MOSprite + // Arguments: 0. + // Return value: Sprite height if loaded. + + int GetSpriteHeight() const { return m_aSprite[0] ? m_aSprite[0]->h : 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFrameCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of frames in this MOSprite's animation. + // Arguments: None. + // Return value: The frame count. + + int GetFrameCount() const { return m_FrameCount; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSpriteOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the offset that the BITMAP has from the position of this + // MOSprite. + // Arguments: None. + // Return value: A vector with the offset. + + Vector GetSpriteOffset() const { return m_SpriteOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsHFlipped + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether this MOSprite is being drawn flipped horizontally + // (along the vertical axis), or not. + // Arguments: None. + // Return value: Whether flipped or not. + + bool IsHFlipped() const override { return m_HFlipped; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRotMatrix + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current rotational Matrix of of this. + // Arguments: None. + // Return value: The rotational Matrix of this MovableObject. + + Matrix GetRotMatrix() const override { return m_Rotation; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRotAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current rotational angle of of this, in radians. + // Arguments: None. + // Return value: The rotational angle of this, in radians. + + float GetRotAngle() const override { return m_Rotation.GetRadAngle(); } + + /// + /// Gets the previous rotational angle of this MOSprite, prior to this frame. + /// + /// The previous rotational angle in radians. + float GetPrevRotAngle() const { return m_PrevRotation.GetRadAngle(); } + + /// + /// Whether a set of X, Y coordinates overlap us (in world space). + /// + /// The given X coordinate, in world space. + /// The given Y coordinate, in world space. + /// Whether the given coordinate overlap us. + bool HitTestAtPixel(int pixelX, int pixelY) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAngularVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current angular velocity of this MovableObject. Positive is + // a counter-clockwise rotation. + // Arguments: None. + // Return value: The angular velocity in radians per second. + + float GetAngularVel() const override { return m_AngularVel; } + + /* Can't do this since sprite is owned by ContentMan. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSprite + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Replaces this MOSprite's BITMAP and deletes the old one. + // Arguments: A pointer to a BITMAP. + // Return value: None. + + void SetSprite(BITMAP *pSprite) { delete m_aSprite; m_aSprite = pSprite; } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSpriteOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the offset that the BITMAP has from the position of this + // MOSprite. + // Arguments: A vector with the new offset. + // Return value: None. + + void SetSpriteOffset(const Vector& newOffset) { m_SpriteOffset = newOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetFrame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Hard-sets the frame this sprite is supposed to show. + // Arguments: An unsigned int pecifiying the new frame. + // Return value: None. + + void SetFrame(unsigned int newFrame); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetNextFrame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Hard-sets the frame this sprite is supposed to show, to the + // consecutive one after the current one. If currently the last fame is + // this will set it to the be the first, looping the animation. + // Arguments: None. + // Return value: Whether the animation looped or not with this setting. + + bool SetNextFrame(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFrame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells which frame is currently set to show. + // Arguments: None. + // Return value: An unsigned int describing the current frame. + + unsigned int GetFrame() const { return m_Frame; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSpriteAnimMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the animation mode. + // Arguments: The animation mode we want to set. + // Return value: None. + + void SetSpriteAnimMode(int animMode = NOANIM) { m_SpriteAnimMode = (SpriteAnimMode)animMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSpriteAnimMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the animation mode. + // Arguments: None. + // Return value: The animation mode currently in effect. + + int GetSpriteAnimMode() const { return m_SpriteAnimMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: SetHFlipped + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this MOSprite should be drawn flipped horizontally + // (along the vertical axis). + // Arguments: A bool with the new value. + // Return value: None. + + void SetHFlipped(const bool flipped) override { m_HFlipped = flipped; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetRotAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current absolute angle of rotation of this MovableObject. + // Arguments: The new absolute angle in radians. + // Return value: None. + + void SetRotAngle(float newAngle) override { m_Rotation.SetRadAngle(newAngle); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetAngularVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current angular velocity of this MovableObject. Positive is + // a counter clockwise rotation. + // Arguments: The new angular velocity in radians per second. + // Return value: None. + + void SetAngularVel(float newRotVel) override { m_AngularVel = newRotVel; } + + /// + /// Gets the GUI representation of this MOSprite, either based on the first frame of its sprite or separately defined icon file. + /// + /// The graphical representation of this MOSprite as a BITMAP. + BITMAP* GetGraphicalIcon() const override { return m_GraphicalIcon != nullptr ? m_GraphicalIcon : m_aSprite[0]; } + + /// + /// Gets the width of this MOSprite's GUI icon. + /// + /// The width of the GUI icon bitmap. + int GetIconWidth() const { return GetGraphicalIcon()->w; } + + /// + /// Gets the height of this MOSprite's GUI icon. + /// + /// The height of the GUI icon bitmap. + int GetIconHeight() const { return GetGraphicalIcon()->h; } + + /// + /// Forces this MOSprite out of resting conditions. + /// + void NotResting() override { + MovableObject::NotResting(); + m_AngOscillations = 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsTooFast + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is moving or rotating stupidly fast in a way + // that will screw up the simulation. + // Arguments: None. + // Return value: Whether this is either moving or rotating too fast. + + bool IsTooFast() const override { return m_Vel.MagnitudeIsGreaterThan(500.0F) || std::fabs(m_AngularVel) > (2000.0F / (GetRadius() + 1.0F)); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: FixTooFast + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Slows the speed of anything that is deemed to be too fast to within + // acceptable rates. + // Arguments: None. + // Return value: None. + + void FixTooFast() override { + while (IsTooFast()) { + m_Vel *= 0.5F; + m_AngularVel *= 0.5F; + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsOnScenePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this' current graphical representation overlaps + // a point in absolute scene coordinates. + // Arguments: The point in absolute scene coordinates. + // Return value: Whether this' graphical rep overlaps the scene point. + + bool IsOnScenePoint(Vector& scenePoint) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: RotateOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes a vector which is offset from the center of this when not rotated + // or flipped, and then rotates and/or flips it to transform it into this' + // 'local space', if applicable. + // Arguments: A vector which is supposed to be offset from this' center when upright. + // Return value: The resulting vector whihch has been flipped and rotated as appropriate. + + Vector RotateOffset(const Vector& offset) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UnRotateOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes a vector which is offset from the center of this when not rotated + // or flipped, and then rotates and/or flips it to transform it into this' + // 'local space', but in REVERSE. + // Arguments: A vector which is supposed to be offset from this' center when upright. + // Return value: The resulting vector whihch has been flipped and rotated as appropriate. + + Vector UnRotateOffset(const Vector& offset) const; + + /// + /// Adjusts an absolute angle based on wether this MOSprite is flipped. + /// + /// The input angle in radians. + /// The output angle in radians, which will be unaltered if this MOSprite is not flipped. + float FacingAngle(float angle) const { return (m_HFlipped ? c_PI : 0) + (angle * GetFlipFactor()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetEntryWound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets entry wound emitter for this MOSprite + // Arguments: Emitter preset name and module name + // Return value: None + + void SetEntryWound(std::string presetName, std::string moduleName); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetExitWound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets exit wound emitter for this MOSprite + // Arguments: Emitter preset name and module name + // Return value: None + + void SetExitWound(std::string presetName, std::string moduleName); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetEntryWoundPresetName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns entry wound emitter preset name for this MOSprite + // Arguments: None + // Return value: Wound emitter preset name + + std::string GetEntryWoundPresetName() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetExitWoundPresetName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns exit wound emitter preset name for this MOSprite + // Arguments: None + // Return value: Wound emitter preset name + + std::string GetExitWoundPresetName() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetSpriteAnimDuration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns animation duration in ms + // Arguments: None + // Return value: Animation duration in ms + + int GetSpriteAnimDuration() const { return m_SpriteAnimDuration; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetSpriteAnimDuration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets animation duration in ms + // Arguments: Animation duration in ms + // Return value: Mone + + void SetSpriteAnimDuration(int newDuration) { m_SpriteAnimDuration = newDuration; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this MOSprite's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetFlipFactor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a positive or negative number value to multiply with for external calculations. + // Arguments: None. + // Return value: 1 for not flipped, -1 for flipped. + + float GetFlipFactor() const { return m_HFlipped ? -1.0F : 1.0F; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + + Matrix m_Rotation; // Rotational matrix of this MovableObject. + Matrix m_PrevRotation; // Rotational matrix of this MovableObject, last frame. + float m_AngularVel; // The angular velocity by which this MovableObject rotates, in radians per second (r/s). + float m_PrevAngVel; // Previous frame's angular velocity. + ContentFile m_SpriteFile; + // Vector of pointers to BITMAPs representing the multiple frames of this sprite. + std::vector m_aSprite; + ContentFile m_IconFile; //!< The file containing the GUI icon. + BITMAP* m_GraphicalIcon; //!< The GUI representation of this MOSprite as a BITMAP. + // Number of frames, or elements in the m_aSprite array. + unsigned int m_FrameCount; + Vector m_SpriteOffset; + // State, which frame of sprite to be drawn. + unsigned int m_Frame; + // Which animation to use for the body + SpriteAnimMode m_SpriteAnimMode; + // Body animation duration, how long in ms that each full body animation cycle takes. + int m_SpriteAnimDuration; + // The timer to keep track of the body animation + Timer m_SpriteAnimTimer; + // Keep track of animation direction (mainly for ALWAYSPINGPONG), true is decreasing frame, false is increasing frame + bool m_SpriteAnimIsReversingFrames; + // Whether flipped horizontally or not. + bool m_HFlipped; + // The precalculated maximum possible radius and diameter of this, in pixels + float m_SpriteRadius; + float m_SpriteDiameter; + int m_AngOscillations; //!< A counter for oscillations in rotation, in order to detect settling. + // Whether to disable the settle material ID when this gets drawn as material + bool m_SettleMaterialDisabled; + // Entry wound template + const AEmitter* m_pEntryWound; + // Exit wound template + const AEmitter* m_pExitWound; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MOSprite, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + MOSprite(const MOSprite& reference) = delete; + MOSprite& operator=(const MOSprite& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/Magazine.cpp b/Source/Entities/Magazine.cpp index db49161a53..4e01bfc2bb 100644 --- a/Source/Entities/Magazine.cpp +++ b/Source/Entities/Magazine.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -17,296 +16,263 @@ namespace RTE { -ConcreteClassInfo(Magazine, Attachable, 50); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Magazine, effectively -// resetting the members of this abstraction level only. - -void Magazine::Clear() -{ - m_RoundCount = 0; - m_FullCapacity = 0; - m_RTTRatio = 0; - m_pRegularRound = 0; - m_pTracerRound = 0; - m_Discardable = true; - m_AIAimVel = 100; - m_AIAimMaxDistance = -1; - m_AIAimPenetration = 0; - m_AIBlastRadius = -1; - - // NOTE: This special override of a parent class member variable avoids needing an extra variable to avoid overwriting INI values. - m_CollidesWithTerrainWhileAttached = false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Magazine object ready for use. - -int Magazine::Create() -{ - if (Attachable::Create() < 0) - return -1; - - // Read projectile properties for AI aim caluculations - const Round * pNextRound = GetNextRound(); - if (pNextRound) - { - // What muzzle velocity should the AI use when aiming? - m_AIAimVel = pNextRound->GetAIFireVel() < 0 ? pNextRound->GetFireVel() : pNextRound->GetAIFireVel(); - - // How much material can this projectile penetrate? - m_AIAimPenetration = pNextRound->GetAIPenetration(); - - if (pNextRound->GetAIFireVel() < 0) - { - const MovableObject * pBullet = pNextRound->GetNextParticle(); - if(pBullet) - { - // Also get FireVel on emitters from sharpness to assure backwards compability with mods - const AEmitter * pEmitter = dynamic_cast(pBullet); - if (pEmitter) - m_AIAimVel = std::max(m_AIAimVel, pEmitter->GetSharpness()); - } - } - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Magazine to be identical to another, by deep copy. - -int Magazine::Create(const Magazine &reference) -{ - Attachable::Create(reference); - - m_RoundCount = reference.m_RoundCount; - m_FullCapacity = reference.m_FullCapacity; - m_RTTRatio = reference.m_RTTRatio; - m_pRegularRound = reference.m_pRegularRound; - m_pTracerRound = reference.m_pTracerRound; - m_Discardable = reference.m_Discardable; - m_AIBlastRadius = reference.m_AIBlastRadius; - m_AIAimPenetration = reference.m_AIAimPenetration; - m_AIAimVel = reference.m_AIAimVel; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int Magazine::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Attachable::ReadProperty(propName, reader)); - - MatchProperty("RoundCount", { - reader >> m_RoundCount; - m_FullCapacity = m_RoundCount; - }); - MatchProperty("RTTRatio", { reader >> m_RTTRatio; }); - MatchProperty("RegularRound", { m_pRegularRound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); - MatchProperty("TracerRound", { m_pTracerRound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); - MatchProperty("Discardable", { reader >> m_Discardable; }); - MatchProperty("AIBlastRadius", { reader >> m_AIBlastRadius; }); - - EndPropertyList; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Magazine with a Writer for -// later recreation with Create(Reader &reader); - -int Magazine::Save(Writer &writer) const -{ - Attachable::Save(writer); - - writer.NewProperty("RoundCount"); - writer << m_RoundCount; - writer.NewProperty("RTTRatio"); - writer << m_RTTRatio; - writer.NewProperty("RegularRound"); - writer << m_pRegularRound; - writer.NewProperty("TracerRound"); - writer << m_pTracerRound; - writer.NewProperty("Discardable"); - writer << m_Discardable; - writer.NewProperty("AIBlastRadius"); - writer << m_AIBlastRadius; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Magazine object. - -void Magazine::Destroy(bool notInherited) -{ - - if (!notInherited) - Attachable::Destroy(); - Clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetNextRound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next Round preset of ammo in this Magazine, without removing -// it. Ownership IS NOT transferred! - -const Round * Magazine::GetNextRound() const -{ - const Round *tempRound = 0; - if (m_RoundCount != 0) - { - if (m_RTTRatio && m_pTracerRound && m_RoundCount % m_RTTRatio == 0) - tempRound = m_pTracerRound; - else - tempRound = m_pRegularRound; - } - return tempRound; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PopNextRound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next Round of ammo in this Magazine, and removes it from the -// stack. Ownership IS transferred! - -Round * Magazine::PopNextRound() -{ - Round *tempRound = 0; - if (m_RoundCount != 0) - { - if (m_RTTRatio && m_pTracerRound && m_RoundCount % m_RTTRatio == 0) - tempRound = dynamic_cast(m_pTracerRound->Clone()); - else - tempRound = dynamic_cast(m_pRegularRound->Clone()); - // Negative roundcount means infinite ammo - if (m_FullCapacity > 0) - m_RoundCount--; - } - return tempRound; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EstimateDigStrength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Estimates what material strength the rounds in the magazine can destroy. - -float Magazine::EstimateDigStrength() const { - float maxPenetration = 1; - if (m_pTracerRound) - { - // Find the next tracer - const MovableObject * pBullet = m_pTracerRound->GetNextParticle(); - if (pBullet) - { - if (m_pTracerRound->GetAIFireVel() > 0) - maxPenetration = std::max(maxPenetration, m_pTracerRound->GetAIFireVel() * abs(pBullet->GetMass()) * std::max(pBullet->GetSharpness(), 0.0f)); - else - maxPenetration = std::max(maxPenetration, m_pTracerRound->GetFireVel() * abs(pBullet->GetMass()) * std::max(pBullet->GetSharpness(), 0.0f)); - } - } - - if (m_pRegularRound) - { - // Find the next regular bullet - const MovableObject * pBullet = m_pRegularRound->GetNextParticle(); - if (pBullet) - { - if (m_pRegularRound->GetAIFireVel() > 0) - maxPenetration = std::max(maxPenetration, m_pRegularRound->GetAIFireVel() * abs(pBullet->GetMass()) * std::max(pBullet->GetSharpness(), 0.0f)); - else - maxPenetration = std::max(maxPenetration, m_pRegularRound->GetFireVel() * abs(pBullet->GetMass()) * std::max(pBullet->GetSharpness(), 0.0f)); - } - } - - return maxPenetration; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBulletAccScalar -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. - -float Magazine::GetBulletAccScalar() -{ - const Round * pRound = GetNextRound(); - if (pRound) - { - const MovableObject * pBullet = pRound->GetNextParticle(); - if (pBullet) - return pBullet->GetGlobalAccScalar(); - } - - return 1; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this Magazine. Supposed to be done every frame. - -void Magazine::Update() -{ - Attachable::Update(); - - /*if (!m_pParent) { - - } - else { - ///////////////////////////////// - // Update rotations and scale - - // Taken care of by holder/owner Arm. -// m_Pos += m_ParentOffset; -// Only apply in Draw(). -// m_aSprite->SetAngle(m_Rotation); -// m_aSprite->SetScale(m_Scale); - }*/ -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Magazine's current graphical representation to a -// BITMAP of choice. - -void Magazine::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); -} + ConcreteClassInfo(Magazine, Attachable, 50); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Magazine, effectively + // resetting the members of this abstraction level only. + + void Magazine::Clear() { + m_RoundCount = 0; + m_FullCapacity = 0; + m_RTTRatio = 0; + m_pRegularRound = 0; + m_pTracerRound = 0; + m_Discardable = true; + m_AIAimVel = 100; + m_AIAimMaxDistance = -1; + m_AIAimPenetration = 0; + m_AIBlastRadius = -1; + + // NOTE: This special override of a parent class member variable avoids needing an extra variable to avoid overwriting INI values. + m_CollidesWithTerrainWhileAttached = false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Magazine object ready for use. + + int Magazine::Create() { + if (Attachable::Create() < 0) + return -1; + + // Read projectile properties for AI aim caluculations + const Round* pNextRound = GetNextRound(); + if (pNextRound) { + // What muzzle velocity should the AI use when aiming? + m_AIAimVel = pNextRound->GetAIFireVel() < 0 ? pNextRound->GetFireVel() : pNextRound->GetAIFireVel(); + + // How much material can this projectile penetrate? + m_AIAimPenetration = pNextRound->GetAIPenetration(); + + if (pNextRound->GetAIFireVel() < 0) { + const MovableObject* pBullet = pNextRound->GetNextParticle(); + if (pBullet) { + // Also get FireVel on emitters from sharpness to assure backwards compability with mods + const AEmitter* pEmitter = dynamic_cast(pBullet); + if (pEmitter) + m_AIAimVel = std::max(m_AIAimVel, pEmitter->GetSharpness()); + } + } + } + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Magazine to be identical to another, by deep copy. + + int Magazine::Create(const Magazine& reference) { + Attachable::Create(reference); + + m_RoundCount = reference.m_RoundCount; + m_FullCapacity = reference.m_FullCapacity; + m_RTTRatio = reference.m_RTTRatio; + m_pRegularRound = reference.m_pRegularRound; + m_pTracerRound = reference.m_pTracerRound; + m_Discardable = reference.m_Discardable; + m_AIBlastRadius = reference.m_AIBlastRadius; + m_AIAimPenetration = reference.m_AIAimPenetration; + m_AIAimVel = reference.m_AIAimVel; + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int Magazine::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Attachable::ReadProperty(propName, reader)); + + MatchProperty("RoundCount", { + reader >> m_RoundCount; + m_FullCapacity = m_RoundCount; + }); + MatchProperty("RTTRatio", { reader >> m_RTTRatio; }); + MatchProperty("RegularRound", { m_pRegularRound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); + MatchProperty("TracerRound", { m_pTracerRound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); + MatchProperty("Discardable", { reader >> m_Discardable; }); + MatchProperty("AIBlastRadius", { reader >> m_AIBlastRadius; }); + + EndPropertyList; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this Magazine with a Writer for + // later recreation with Create(Reader &reader); + + int Magazine::Save(Writer& writer) const { + Attachable::Save(writer); + + writer.NewProperty("RoundCount"); + writer << m_RoundCount; + writer.NewProperty("RTTRatio"); + writer << m_RTTRatio; + writer.NewProperty("RegularRound"); + writer << m_pRegularRound; + writer.NewProperty("TracerRound"); + writer << m_pTracerRound; + writer.NewProperty("Discardable"); + writer << m_Discardable; + writer.NewProperty("AIBlastRadius"); + writer << m_AIBlastRadius; + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the Magazine object. + + void Magazine::Destroy(bool notInherited) { + + if (!notInherited) + Attachable::Destroy(); + Clear(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetNextRound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the next Round preset of ammo in this Magazine, without removing + // it. Ownership IS NOT transferred! + + const Round* Magazine::GetNextRound() const { + const Round* tempRound = 0; + if (m_RoundCount != 0) { + if (m_RTTRatio && m_pTracerRound && m_RoundCount % m_RTTRatio == 0) + tempRound = m_pTracerRound; + else + tempRound = m_pRegularRound; + } + return tempRound; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PopNextRound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the next Round of ammo in this Magazine, and removes it from the + // stack. Ownership IS transferred! + + Round* Magazine::PopNextRound() { + Round* tempRound = 0; + if (m_RoundCount != 0) { + if (m_RTTRatio && m_pTracerRound && m_RoundCount % m_RTTRatio == 0) + tempRound = dynamic_cast(m_pTracerRound->Clone()); + else + tempRound = dynamic_cast(m_pRegularRound->Clone()); + // Negative roundcount means infinite ammo + if (m_FullCapacity > 0) + m_RoundCount--; + } + return tempRound; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EstimateDigStrength + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Estimates what material strength the rounds in the magazine can destroy. + + float Magazine::EstimateDigStrength() const { + float maxPenetration = 1; + if (m_pTracerRound) { + // Find the next tracer + const MovableObject* pBullet = m_pTracerRound->GetNextParticle(); + if (pBullet) { + if (m_pTracerRound->GetAIFireVel() > 0) + maxPenetration = std::max(maxPenetration, m_pTracerRound->GetAIFireVel() * abs(pBullet->GetMass()) * std::max(pBullet->GetSharpness(), 0.0f)); + else + maxPenetration = std::max(maxPenetration, m_pTracerRound->GetFireVel() * abs(pBullet->GetMass()) * std::max(pBullet->GetSharpness(), 0.0f)); + } + } + + if (m_pRegularRound) { + // Find the next regular bullet + const MovableObject* pBullet = m_pRegularRound->GetNextParticle(); + if (pBullet) { + if (m_pRegularRound->GetAIFireVel() > 0) + maxPenetration = std::max(maxPenetration, m_pRegularRound->GetAIFireVel() * abs(pBullet->GetMass()) * std::max(pBullet->GetSharpness(), 0.0f)); + else + maxPenetration = std::max(maxPenetration, m_pRegularRound->GetFireVel() * abs(pBullet->GetMass()) * std::max(pBullet->GetSharpness(), 0.0f)); + } + } + + return maxPenetration; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBulletAccScalar + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. + + float Magazine::GetBulletAccScalar() { + const Round* pRound = GetNextRound(); + if (pRound) { + const MovableObject* pBullet = pRound->GetNextParticle(); + if (pBullet) + return pBullet->GetGlobalAccScalar(); + } + + return 1; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this Magazine. Supposed to be done every frame. + + void Magazine::Update() { + Attachable::Update(); + + /*if (!m_pParent) { + + } + else { + ///////////////////////////////// + // Update rotations and scale + + // Taken care of by holder/owner Arm. + // m_Pos += m_ParentOffset; + // Only apply in Draw(). + // m_aSprite->SetAngle(m_Rotation); + // m_aSprite->SetScale(m_Scale); + }*/ + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Magazine's current graphical representation to a + // BITMAP of choice. + + void Magazine::Draw(BITMAP* pTargetBitmap, + const Vector& targetPos, + DrawMode mode, + bool onlyPhysical) const { + Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } } // namespace RTE diff --git a/Source/Entities/Magazine.h b/Source/Entities/Magazine.h index 29f287ce31..0ec35c0393 100644 --- a/Source/Entities/Magazine.h +++ b/Source/Entities/Magazine.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -19,313 +18,286 @@ namespace RTE { - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: Magazine -////////////////////////////////////////////////////////////////////////////////////////// -// Description: An Attachable ammo magazine that can hold rounds that can be fired -// by HDFirearm:s. -// Parent(s): Attachable. -// Class history: 07/03/2002 Magazine created. - -class Magazine : public Attachable { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(Magazine); -SerializableOverrideMethods; -ClassInfoGetters; - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: Magazine -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a Magazine object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - Magazine() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~Magazine -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a Magazine object before deletion -// from system memory. -// Arguments: None. - - ~Magazine() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Magazine object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Magazine to be identical to another, by deep copy. -// Arguments: A reference to the Magazine to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const Magazine &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire Magazine, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Attachable::Reset(); m_CollidesWithTerrainWhileAttached = false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetNextRound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next Round preset of ammo in this Magazine, without removing -// it. Ownership IS NOT transferred! -// Arguments: None. -// Return value: A pointer to the next Round preset of ammo, or 0 if this Magazine is empty. - - const Round * GetNextRound() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PopNextRound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next Round of ammo in this Magazine, and removes it from the -// stack. Ownership IS transferred! -// Arguments: None. -// Return value: A pointer to the next Round of ammo, or 0 if this Magazine is empty. - - Round * PopNextRound(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns how many rounds are left in this Magazine. -// Arguments: None. -// Return value: The number of rounds left. Negative value means infinite ammo left! - - int GetRoundCount() const { return m_RoundCount; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetRoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets how many rounds are left in this Magazine. -// Arguments: The new number of rounds left. Negative value means infinite ammo! -// Return value: None. - - void SetRoundCount(int newCount) { m_RoundCount = newCount; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsEmpty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether this Magazine is out of rounds. -// Arguments: None. -// Return value: Whether this Magazine is out of rounds or not. - - bool IsEmpty() const { return m_FullCapacity >= 0 && m_RoundCount == 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsFull -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether this Magazine has not used up any rounds yet. -// Arguments: None. -// Return value: Whether this Magazine has not used any rounds yet. - - bool IsFull() const { return m_FullCapacity > 0 ? (m_RoundCount == m_FullCapacity || m_RoundCount < 0) : m_FullCapacity < 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsOverHalfFull -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether this Magazine has not used up half of the rounds yet. -// Arguments: None. -// Return value: Whether this Magazine has not used half of its rounds yet. - - bool IsOverHalfFull() const { return m_FullCapacity > 0 ? ((m_RoundCount > (m_FullCapacity / 2)) || m_RoundCount < 0) : m_FullCapacity < 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCapacity -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns teh number of rounds this can hold when it's full. -// Arguments: None. -// Return value: The number of rounds this can hold. Negative value means infinite ammo. - - int GetCapacity() const { return m_FullCapacity; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDiscardable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Whether this Magazine should be released into the scene when discarded -// or just deleted. -// Arguments: None. -// Return value: Whether this Magazine should be relesed into scene or deleted when released. - - bool IsDiscardable() const { return m_Discardable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EstimateDigStrength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Estimates what material strength the rounds in the magazine can destroy. -// Arguments: None. -// Return value: The material strength. - - float EstimateDigStrength() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAimVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells the AI what muzzle velocity to assume when aiming this weapon. -// Arguments: None. -// Return value: Velocity in m/s. - - float GetAIAimVel() const { return m_AIAimVel; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIAimBlastRadius -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells the AI what distance in pixels from the rounds in this mag round -// are mostly safe. -// Arguments: None. -// Return value: Distance in pixels. - - int GetAIAimBlastRadius() const { return m_AIBlastRadius; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAIAimPenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells the AI how much material this projectile can penetrate. -// Arguments: None. -// Return value: The material strenght. - - float GetAIAimPenetration() const { return m_AIAimPenetration; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBulletAccScalar -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. -// Arguments: None. -// Return value: A float with the scalar. - - float GetBulletAccScalar(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Magazine's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - - // How many rounds in mag. Negative value means infinite ammo - int m_RoundCount; - // The number of rounds that this mag holds. negative menas infinite ammo capacity - int m_FullCapacity; - // The ratio between regular and tracer rounds. 0 means no tracers. - // e.g. 3 means every third round will be a tracer. ie Round To Tracer (RTT) ratio. - int m_RTTRatio; - // Round reference instances. - const Round *m_pRegularRound; - const Round *m_pTracerRound; - // Whether this magazine should be released into the scene when discarded, or just be deleted instead - bool m_Discardable; - // The muzzle velocity the AI use when aiming this gun. calculated when the magazine is created - float m_AIAimVel; - // The estimated maximum distance in pixels the projectiles can hit from - float m_AIAimMaxDistance; - // The half of the theoretical upper limit for the amount of material strength this weapon can destroy with one projectile - float m_AIAimPenetration; - // Tells the AI what distance in pixels from this round is mostly safe. - int m_AIBlastRadius; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Magazine, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - Magazine(const Magazine &reference) = delete; - Magazine & operator=(const Magazine &rhs) = delete; - -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: Magazine + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: An Attachable ammo magazine that can hold rounds that can be fired + // by HDFirearm:s. + // Parent(s): Attachable. + // Class history: 07/03/2002 Magazine created. + + class Magazine : public Attachable { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Concrete allocation and cloning definitions + EntityAllocation(Magazine); + SerializableOverrideMethods; + ClassInfoGetters; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: Magazine + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a Magazine object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + Magazine() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~Magazine + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a Magazine object before deletion + // from system memory. + // Arguments: None. + + ~Magazine() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Magazine object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Magazine to be identical to another, by deep copy. + // Arguments: A reference to the Magazine to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const Magazine& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire Magazine, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Attachable::Reset(); + m_CollidesWithTerrainWhileAttached = false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetNextRound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the next Round preset of ammo in this Magazine, without removing + // it. Ownership IS NOT transferred! + // Arguments: None. + // Return value: A pointer to the next Round preset of ammo, or 0 if this Magazine is empty. + + const Round* GetNextRound() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PopNextRound + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the next Round of ammo in this Magazine, and removes it from the + // stack. Ownership IS transferred! + // Arguments: None. + // Return value: A pointer to the next Round of ammo, or 0 if this Magazine is empty. + + Round* PopNextRound(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRoundCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns how many rounds are left in this Magazine. + // Arguments: None. + // Return value: The number of rounds left. Negative value means infinite ammo left! + + int GetRoundCount() const { return m_RoundCount; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetRoundCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets how many rounds are left in this Magazine. + // Arguments: The new number of rounds left. Negative value means infinite ammo! + // Return value: None. + + void SetRoundCount(int newCount) { m_RoundCount = newCount; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsEmpty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether this Magazine is out of rounds. + // Arguments: None. + // Return value: Whether this Magazine is out of rounds or not. + + bool IsEmpty() const { return m_FullCapacity >= 0 && m_RoundCount == 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsFull + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether this Magazine has not used up any rounds yet. + // Arguments: None. + // Return value: Whether this Magazine has not used any rounds yet. + + bool IsFull() const { return m_FullCapacity > 0 ? (m_RoundCount == m_FullCapacity || m_RoundCount < 0) : m_FullCapacity < 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsOverHalfFull + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether this Magazine has not used up half of the rounds yet. + // Arguments: None. + // Return value: Whether this Magazine has not used half of its rounds yet. + + bool IsOverHalfFull() const { return m_FullCapacity > 0 ? ((m_RoundCount > (m_FullCapacity / 2)) || m_RoundCount < 0) : m_FullCapacity < 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCapacity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns teh number of rounds this can hold when it's full. + // Arguments: None. + // Return value: The number of rounds this can hold. Negative value means infinite ammo. + + int GetCapacity() const { return m_FullCapacity; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsDiscardable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Whether this Magazine should be released into the scene when discarded + // or just deleted. + // Arguments: None. + // Return value: Whether this Magazine should be relesed into scene or deleted when released. + + bool IsDiscardable() const { return m_Discardable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EstimateDigStrength + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Estimates what material strength the rounds in the magazine can destroy. + // Arguments: None. + // Return value: The material strength. + + float EstimateDigStrength() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAimVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells the AI what muzzle velocity to assume when aiming this weapon. + // Arguments: None. + // Return value: Velocity in m/s. + + float GetAIAimVel() const { return m_AIAimVel; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIAimBlastRadius + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells the AI what distance in pixels from the rounds in this mag round + // are mostly safe. + // Arguments: None. + // Return value: Distance in pixels. + + int GetAIAimBlastRadius() const { return m_AIBlastRadius; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAIAimPenetration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells the AI how much material this projectile can penetrate. + // Arguments: None. + // Return value: The material strenght. + + float GetAIAimPenetration() const { return m_AIAimPenetration; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBulletAccScalar + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. + // Arguments: None. + // Return value: A float with the scalar. + + float GetBulletAccScalar(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this Magazine's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + + // How many rounds in mag. Negative value means infinite ammo + int m_RoundCount; + // The number of rounds that this mag holds. negative menas infinite ammo capacity + int m_FullCapacity; + // The ratio between regular and tracer rounds. 0 means no tracers. + // e.g. 3 means every third round will be a tracer. ie Round To Tracer (RTT) ratio. + int m_RTTRatio; + // Round reference instances. + const Round* m_pRegularRound; + const Round* m_pTracerRound; + // Whether this magazine should be released into the scene when discarded, or just be deleted instead + bool m_Discardable; + // The muzzle velocity the AI use when aiming this gun. calculated when the magazine is created + float m_AIAimVel; + // The estimated maximum distance in pixels the projectiles can hit from + float m_AIAimMaxDistance; + // The half of the theoretical upper limit for the amount of material strength this weapon can destroy with one projectile + float m_AIAimPenetration; + // Tells the AI what distance in pixels from this round is mostly safe. + int m_AIBlastRadius; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Magazine, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + Magazine(const Magazine& reference) = delete; + Magazine& operator=(const Magazine& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/Material.cpp b/Source/Entities/Material.cpp index d3bc9d5397..0af3300309 100644 --- a/Source/Entities/Material.cpp +++ b/Source/Entities/Material.cpp @@ -5,7 +5,7 @@ namespace RTE { ConcreteClassInfo(Material, Entity, 0); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Material::Clear() { m_Index = 0; @@ -30,9 +30,9 @@ namespace RTE { m_TerrainBGTexture = nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Material::Create(const Material &reference) { + int Material::Create(const Material& reference) { Entity::Create(reference); m_Index = reference.m_Index; @@ -59,11 +59,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Material::ReadProperty(const std::string_view &propName, Reader &reader) { + int Material::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("Index", { // TODO: Check for index collisions here reader >> m_Index; @@ -106,9 +106,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Material::Save(Writer &writer) const { + int Material::Save(Writer& writer) const { Entity::Save(writer); // Materials should never be altered, so no point in saving additional properties when it's a copy if (m_IsOriginalPreset) { @@ -131,4 +131,4 @@ namespace RTE { } return 0; } -} +} // namespace RTE diff --git a/Source/Entities/Material.h b/Source/Entities/Material.h index 92a726306e..6e37a692af 100644 --- a/Source/Entities/Material.h +++ b/Source/Entities/Material.h @@ -13,7 +13,6 @@ namespace RTE { class Material : public Entity { public: - EntityAllocation(Material); SerializableOverrideMethods; ClassInfoGetters; @@ -28,21 +27,29 @@ namespace RTE { /// Copy constructor method used to instantiate a Material object identical to an already existing one. /// /// A Material object which is passed in by reference. - Material(const Material &reference) { if (this != &reference) { Clear(); Create(reference); } } + Material(const Material& reference) { + if (this != &reference) { + Clear(); + Create(reference); + } + } /// /// Creates a Material to be identical to another, by deep copy. /// /// A reference to the Material to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Material &reference); + int Create(const Material& reference); #pragma endregion #pragma region Destruction /// /// Resets the entire Material, including its inherited members, to it's default settings or values. /// - void Reset() override { Clear(); Entity::Reset(); } + void Reset() override { + Clear(); + Entity::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -50,13 +57,13 @@ namespace RTE { /// Gets the foreground texture bitmap of this Material, if any is associated with it. /// /// Pointer to the foreground texture bitmap of this Material. - BITMAP * GetFGTexture() const { return m_TerrainFGTexture; } + BITMAP* GetFGTexture() const { return m_TerrainFGTexture; } /// /// Gets the background texture bitmap of this Material, if any is associated with it. /// /// Pointer to the background texture bitmap of this Material. - BITMAP * GetBGTexture() const { return m_TerrainBGTexture; } + BITMAP* GetBGTexture() const { return m_TerrainBGTexture; } /// /// Gets the index of this Material in the material palette. @@ -155,11 +162,16 @@ namespace RTE { /// /// A Material reference. /// A reference to the changed Material. - Material & operator=(const Material &rhs) { if (this != &rhs) { Destroy(); Create(rhs); } return *this; } + Material& operator=(const Material& rhs) { + if (this != &rhs) { + Destroy(); + Create(rhs); + } + return *this; + } #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. unsigned char m_Index; //!< Index of this in the material palette. 0 - 255. @@ -187,15 +199,14 @@ namespace RTE { ContentFile m_FGTextureFile; //!< The file pointing to the terrain foreground texture of this Material. ContentFile m_BGTextureFile; //!< The file pointing to the terrain background texture of this Material. - BITMAP *m_TerrainFGTexture; //!< The foreground texture of this Material, used when building an SLTerrain. Not owned. - BITMAP *m_TerrainBGTexture; //!< The background texture of this Material, used when building an SLTerrain. Not owned. + BITMAP* m_TerrainFGTexture; //!< The foreground texture of this Material, used when building an SLTerrain. Not owned. + BITMAP* m_TerrainBGTexture; //!< The background texture of this Material, used when building an SLTerrain. Not owned. private: - /// /// Clears all the member variables of this Material, effectively resetting the members of this abstraction level only. /// void Clear(); }; -} +} // namespace RTE #endif diff --git a/Source/Entities/MetaPlayer.cpp b/Source/Entities/MetaPlayer.cpp index 2398a88440..f0d467fa39 100644 --- a/Source/Entities/MetaPlayer.cpp +++ b/Source/Entities/MetaPlayer.cpp @@ -7,7 +7,7 @@ namespace RTE { ConcreteClassInfo(MetaPlayer, Entity, 0); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MetaPlayer::Clear() { m_Name = ""; @@ -30,9 +30,9 @@ namespace RTE { m_OffensiveTarget = ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaPlayer::Create(const MetaPlayer &reference) { + int MetaPlayer::Create(const MetaPlayer& reference) { Entity::Create(reference); m_Name = reference.m_Name; @@ -54,11 +54,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaPlayer::ReadProperty(const std::string_view &propName, Reader &reader) { + int MetaPlayer::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("Name", { reader >> m_Name; }); MatchProperty("Team", { reader >> m_Team; }); MatchProperty("Human", { reader >> m_Human; }); @@ -66,12 +66,14 @@ namespace RTE { MatchProperty("Aggressiveness", { reader >> m_Aggressiveness; }); MatchProperty("GameOverRound", { reader >> m_GameOverRound; - // Need to match the name to the index + // Need to match the name to the index }); MatchProperty("NativeTechModule", { m_NativeTechModule = g_PresetMan.GetModuleID(reader.ReadPropValue()); // Default to no native tech if the one we're looking for couldn't be found - if (m_NativeTechModule < 0) { m_NativeTechModule = 0; } + if (m_NativeTechModule < 0) { + m_NativeTechModule = 0; + } }); MatchProperty("NativeCostMultiplier", { reader >> m_NativeCostMult; }); MatchProperty("ForeignCostMultiplier", { reader >> m_ForeignCostMult; }); @@ -79,14 +81,13 @@ namespace RTE { MatchProperty("Funds", { reader >> m_Funds; }); MatchProperty("OffensiveBudget", { reader >> m_OffensiveBudget; }); MatchProperty("OffensiveTarget", { reader >> m_OffensiveTarget; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaPlayer::Save(Writer &writer) const { + int MetaPlayer::Save(Writer& writer) const { Entity::Save(writer); writer.NewProperty("Name"); @@ -121,4 +122,4 @@ namespace RTE { return 0; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/MetaPlayer.h b/Source/Entities/MetaPlayer.h index 3671baa819..794f8ab0a5 100644 --- a/Source/Entities/MetaPlayer.h +++ b/Source/Entities/MetaPlayer.h @@ -12,7 +12,6 @@ namespace RTE { friend class MetagameGUI; public: - EntityAllocation(MetaPlayer); SerializableOverrideMethods; ClassInfoGetters; @@ -27,7 +26,12 @@ namespace RTE { /// Copy constructor method used to instantiate a MetaPlayer object identical to an already existing one. /// /// A MetaPlayer object which is passed in by reference. - MetaPlayer(const MetaPlayer &reference) { if (this != &reference) { Clear(); Create(reference); } } + MetaPlayer(const MetaPlayer& reference) { + if (this != &reference) { + Clear(); + Create(reference); + } + } /// /// Makes the MetaPlayer object ready for use. @@ -40,7 +44,7 @@ namespace RTE { /// /// A reference to the MetaPlayer to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const MetaPlayer &reference); + int Create(const MetaPlayer& reference); #pragma endregion #pragma region Destruction @@ -53,12 +57,20 @@ namespace RTE { /// Destroys and resets (through Clear()) the MetaPlayer object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + Entity::Destroy(); + } + Clear(); + } /// /// Resets the entire MetaPlayer, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Entity::Reset(); } + void Reset() override { + Clear(); + Entity::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -180,7 +192,11 @@ namespace RTE { /// /// The amount with which to change the funds balance. This should be a positive value to decrease the funds amount. /// The amount of funds that were spent. - float SpendFunds(float howMuch) { howMuch = std::min(m_Funds, howMuch); m_Funds -= howMuch; return howMuch; } + float SpendFunds(float howMuch) { + howMuch = std::min(m_Funds, howMuch); + m_Funds -= howMuch; + return howMuch; + } /// /// Gets the offensive budget of this MetaPlayer for this round, in oz. @@ -259,11 +275,16 @@ namespace RTE { /// /// A MetaPlayer reference. /// A reference to the changed MetaPlayer. - MetaPlayer & operator=(const MetaPlayer &rhs) { if (this != &rhs) { Destroy(); Create(rhs); } return *this; } + MetaPlayer& operator=(const MetaPlayer& rhs) { + if (this != &rhs) { + Destroy(); + Create(rhs); + } + return *this; + } #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. std::string m_Name; //!< The name of the player. @@ -289,11 +310,10 @@ namespace RTE { std::string m_OffensiveTarget; //!< Name of the Scene this player is targeting for its offensive this round. private: - /// /// Clears all the member variables of this MetaPlayer, effectively resetting the members of this abstraction level only. /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/MetaSave.cpp b/Source/Entities/MetaSave.cpp index d627fbb282..c812894e51 100644 --- a/Source/Entities/MetaSave.cpp +++ b/Source/Entities/MetaSave.cpp @@ -7,7 +7,7 @@ namespace RTE { ConcreteClassInfo(MetaSave, Entity, 0); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MetaSave::Clear() { m_SavePath.clear(); @@ -17,7 +17,7 @@ namespace RTE { m_SiteCount = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int MetaSave::Create(std::string savePath) { if (Entity::Create() < 0) { @@ -37,9 +37,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaSave::Create(const MetaSave &reference) { + int MetaSave::Create(const MetaSave& reference) { Entity::Create(reference); m_SavePath = reference.m_SavePath; @@ -51,11 +51,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaSave::ReadProperty(const std::string_view &propName, Reader &reader) { + int MetaSave::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("SavePath", { reader >> m_SavePath; }); MatchProperty("PlayerCount", { reader >> m_PlayerCount; }); MatchProperty("Difficulty", { reader >> m_Difficulty; }); @@ -65,9 +65,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaSave::Save(Writer &writer) const { + int MetaSave::Save(Writer& writer) const { Entity::Save(writer); writer.NewProperty("SavePath"); @@ -83,4 +83,4 @@ namespace RTE { return 0; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/MetaSave.h b/Source/Entities/MetaSave.h index 55b9c24dce..a554a8e0ac 100644 --- a/Source/Entities/MetaSave.h +++ b/Source/Entities/MetaSave.h @@ -11,7 +11,6 @@ namespace RTE { class MetaSave : public Entity { public: - EntityAllocation(MetaSave); SerializableOverrideMethods; ClassInfoGetters; @@ -34,7 +33,7 @@ namespace RTE { /// /// A reference to the MetaSave to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const MetaSave &reference); + int Create(const MetaSave& reference); #pragma endregion #pragma region Destruction @@ -47,7 +46,12 @@ namespace RTE { /// Destroys and resets (through Clear()) the MetaSave object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + Entity::Destroy(); + } + Clear(); + } #pragma endregion #pragma region Getters @@ -83,9 +87,8 @@ namespace RTE { #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. - + std::string m_SavePath; //!< The full path to the ini file which stores the stat of MetaMan this is associated with. int m_PlayerCount; //!< The number of players in this saved game. int m_Difficulty; //!< Game difficulty. @@ -93,15 +96,14 @@ namespace RTE { int m_SiteCount; //!< The site count of this game. private: - /// /// Clears all the member variables of this MetaSave, effectively resetting the members of this abstraction level only. /// void Clear(); // Disallow the use of some implicit methods. - MetaSave(const MetaSave &reference) = delete; - MetaSave & operator=(const MetaSave &rhs) = delete; + MetaSave(const MetaSave& reference) = delete; + MetaSave& operator=(const MetaSave& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/MovableObject.cpp b/Source/Entities/MovableObject.cpp index bbca9f0d14..35d49eb81f 100644 --- a/Source/Entities/MovableObject.cpp +++ b/Source/Entities/MovableObject.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -28,1309 +27,1265 @@ namespace RTE { -AbstractClassInfo(MovableObject, SceneObject); - -std::atomic MovableObject::m_UniqueIDCounter = 1; -std::string MovableObject::ms_EmptyString = ""; + AbstractClassInfo(MovableObject, SceneObject); + + std::atomic MovableObject::m_UniqueIDCounter = 1; + std::string MovableObject::ms_EmptyString = ""; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MovableObject, effectively + // resetting the members of this abstraction level only. + + void MovableObject::Clear() { + m_MOType = TypeGeneric; + m_Mass = 0; + m_Vel.Reset(); + m_PrevPos.Reset(); + m_PrevVel.Reset(); + m_DistanceTravelled = 0; + m_Scale = 1.0; + m_GlobalAccScalar = 1.0; + m_AirResistance = 0; + m_AirThreshold = 5; + m_PinStrength = 0; + m_RestThreshold = 500; + m_Forces.clear(); + m_ImpulseForces.clear(); + m_AgeTimer.Reset(); + m_RestTimer.Reset(); + m_Lifetime = 0; + m_Sharpness = 1.0; + // m_MaterialId = 0; + m_CheckTerrIntersection = false; + m_HitsMOs = false; + m_pMOToNotHit = 0; + m_MOIgnoreTimer.Reset(); + m_GetsHitByMOs = false; + m_IgnoresTeamHits = false; + m_IgnoresAtomGroupHits = false; + m_IgnoresAGHitsWhenSlowerThan = -1; + m_IgnoresActorHits = false; + m_MissionCritical = false; + m_CanBeSquished = true; + m_IsUpdated = false; + m_WrapDoubleDraw = true; + m_DidWrap = false; + m_MOID = g_NoMOID; + m_RootMOID = g_NoMOID; + m_HasEverBeenAddedToMovableMan = false; + m_MOIDFootprint = 0; + m_AlreadyHitBy.clear(); + m_VelOscillations = 0; + m_ToSettle = false; + m_ToDelete = false; + m_HUDVisible = true; + m_IsTraveling = false; + m_AllLoadedScripts.clear(); + m_FunctionsAndScripts.clear(); + m_StringValueMap.clear(); + m_NumberValueMap.clear(); + m_ObjectValueMap.clear(); + m_ThreadedLuaState = nullptr; + m_ForceIntoMasterLuaState = false; + m_ScriptObjectName.clear(); + m_ScreenEffectFile.Reset(); + m_pScreenEffect = 0; + m_EffectRotAngle = 0; + m_InheritEffectRotAngle = false; + m_RandomizeEffectRotAngle = false; + m_RandomizeEffectRotAngleEveryFrame = false; + m_ScreenEffectHash = 0; + m_EffectStartTime = 0; + m_EffectStopTime = 0; + m_EffectStartStrength = 128; + m_EffectStopStrength = 128; + m_EffectAlwaysShows = false; + + m_UniqueID = 0; + + m_RemoveOrphanTerrainRadius = 0; + m_RemoveOrphanTerrainMaxArea = 0; + m_RemoveOrphanTerrainRate = 0.0; + m_DamageOnCollision = 0.0; + m_DamageOnPenetration = 0.0; + m_WoundDamageMultiplier = 1.0; + m_ApplyWoundDamageOnCollision = false; + m_ApplyWoundBurstDamageOnCollision = false; + m_IgnoreTerrain = false; + + m_MOIDHit = g_NoMOID; + m_TerrainMatHit = g_MaterialAir; + m_ParticleUniqueIDHit = 0; + + m_LastCollisionSimFrameNumber = 0; + + m_SimUpdatesBetweenScriptedUpdates = 1; + m_SimUpdatesSinceLastScriptedUpdate = 0; + m_RequestedSyncedUpdate = false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MovableObject, effectively -// resetting the members of this abstraction level only. - -void MovableObject::Clear() -{ - m_MOType = TypeGeneric; - m_Mass = 0; - m_Vel.Reset(); - m_PrevPos.Reset(); - m_PrevVel.Reset(); - m_DistanceTravelled = 0; - m_Scale = 1.0; - m_GlobalAccScalar = 1.0; - m_AirResistance = 0; - m_AirThreshold = 5; - m_PinStrength = 0; - m_RestThreshold = 500; - m_Forces.clear(); - m_ImpulseForces.clear(); - m_AgeTimer.Reset(); - m_RestTimer.Reset(); - m_Lifetime = 0; - m_Sharpness = 1.0; -// m_MaterialId = 0; - m_CheckTerrIntersection = false; - m_HitsMOs = false; - m_pMOToNotHit = 0; - m_MOIgnoreTimer.Reset(); - m_GetsHitByMOs = false; - m_IgnoresTeamHits = false; - m_IgnoresAtomGroupHits = false; - m_IgnoresAGHitsWhenSlowerThan = -1; - m_IgnoresActorHits = false; - m_MissionCritical = false; - m_CanBeSquished = true; - m_IsUpdated = false; - m_WrapDoubleDraw = true; - m_DidWrap = false; - m_MOID = g_NoMOID; - m_RootMOID = g_NoMOID; - m_HasEverBeenAddedToMovableMan = false; - m_MOIDFootprint = 0; - m_AlreadyHitBy.clear(); - m_VelOscillations = 0; - m_ToSettle = false; - m_ToDelete = false; - m_HUDVisible = true; - m_IsTraveling = false; - m_AllLoadedScripts.clear(); - m_FunctionsAndScripts.clear(); - m_StringValueMap.clear(); - m_NumberValueMap.clear(); - m_ObjectValueMap.clear(); - m_ThreadedLuaState = nullptr; - m_ForceIntoMasterLuaState = false; - m_ScriptObjectName.clear(); - m_ScreenEffectFile.Reset(); - m_pScreenEffect = 0; - m_EffectRotAngle = 0; - m_InheritEffectRotAngle = false; - m_RandomizeEffectRotAngle = false; - m_RandomizeEffectRotAngleEveryFrame = false; - m_ScreenEffectHash = 0; - m_EffectStartTime = 0; - m_EffectStopTime = 0; - m_EffectStartStrength = 128; - m_EffectStopStrength = 128; - m_EffectAlwaysShows = false; - - m_UniqueID = 0; - - m_RemoveOrphanTerrainRadius = 0; - m_RemoveOrphanTerrainMaxArea = 0; - m_RemoveOrphanTerrainRate = 0.0; - m_DamageOnCollision = 0.0; - m_DamageOnPenetration = 0.0; - m_WoundDamageMultiplier = 1.0; - m_ApplyWoundDamageOnCollision = false; - m_ApplyWoundBurstDamageOnCollision = false; - m_IgnoreTerrain = false; - - m_MOIDHit = g_NoMOID; - m_TerrainMatHit = g_MaterialAir; - m_ParticleUniqueIDHit = 0; - - m_LastCollisionSimFrameNumber = 0; - - m_SimUpdatesBetweenScriptedUpdates = 1; - m_SimUpdatesSinceLastScriptedUpdate = 0; - m_RequestedSyncedUpdate = false; -} - -LuaStateWrapper & MovableObject::GetAndLockStateForScript(const std::string &scriptPath, const LuaFunction *function) { - if (m_ForceIntoMasterLuaState) { - m_ThreadedLuaState = &g_LuaMan.GetMasterScriptState(); - } - - if (m_ThreadedLuaState == nullptr) { - m_ThreadedLuaState = g_LuaMan.GetAndLockFreeScriptState(); - } else { - m_ThreadedLuaState->GetMutex().lock(); - } - - return *m_ThreadedLuaState; -} + LuaStateWrapper& MovableObject::GetAndLockStateForScript(const std::string& scriptPath, const LuaFunction* function) { + if (m_ForceIntoMasterLuaState) { + m_ThreadedLuaState = &g_LuaMan.GetMasterScriptState(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MovableObject object ready for use. + if (m_ThreadedLuaState == nullptr) { + m_ThreadedLuaState = g_LuaMan.GetAndLockFreeScriptState(); + } else { + m_ThreadedLuaState->GetMutex().lock(); + } -int MovableObject::Create() -{ - if (SceneObject::Create() < 0) - return -1; + return *m_ThreadedLuaState; + } - m_AgeTimer.Reset(); - m_RestTimer.Reset(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MovableObject object ready for use. - // If the stop time hasn't been assigned, just make the same as the life time. - if (m_EffectStopTime <= 0) - m_EffectStopTime = m_Lifetime; + int MovableObject::Create() { + if (SceneObject::Create() < 0) + return -1; - m_UniqueID = MovableObject::GetNextUniqueID(); + m_AgeTimer.Reset(); + m_RestTimer.Reset(); - m_MOIDHit = g_NoMOID; - m_TerrainMatHit = g_MaterialAir; - m_ParticleUniqueIDHit = 0; + // If the stop time hasn't been assigned, just make the same as the life time. + if (m_EffectStopTime <= 0) + m_EffectStopTime = m_Lifetime; - g_MovableMan.RegisterObject(this); + m_UniqueID = MovableObject::GetNextUniqueID(); - return 0; -} + m_MOIDHit = g_NoMOID; + m_TerrainMatHit = g_MaterialAir; + m_ParticleUniqueIDHit = 0; + g_MovableMan.RegisterObject(this); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MovableObject object ready for use. - -int MovableObject::Create(const float mass, - const Vector &position, - const Vector &velocity, - float rotAngle, - float angleVel, - unsigned long lifetime, - bool hitMOs, - bool getHitByMOs) -{ - m_Mass = mass; - m_Pos = position; - m_Vel = velocity; - m_AgeTimer.Reset(); - m_RestTimer.Reset(); - m_Lifetime = lifetime; -// m_MaterialId = matId; - m_HitsMOs = hitMOs; - m_GetsHitByMOs = getHitByMOs; - - m_UniqueID = MovableObject::GetNextUniqueID(); - - m_MOIDHit = g_NoMOID; - m_TerrainMatHit = g_MaterialAir; - m_ParticleUniqueIDHit = 0; - - g_MovableMan.RegisterObject(this); - - return 0; -} + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MovableObject object ready for use. + + int MovableObject::Create(const float mass, + const Vector& position, + const Vector& velocity, + float rotAngle, + float angleVel, + unsigned long lifetime, + bool hitMOs, + bool getHitByMOs) { + m_Mass = mass; + m_Pos = position; + m_Vel = velocity; + m_AgeTimer.Reset(); + m_RestTimer.Reset(); + m_Lifetime = lifetime; + // m_MaterialId = matId; + m_HitsMOs = hitMOs; + m_GetsHitByMOs = getHitByMOs; + + m_UniqueID = MovableObject::GetNextUniqueID(); + + m_MOIDHit = g_NoMOID; + m_TerrainMatHit = g_MaterialAir; + m_ParticleUniqueIDHit = 0; + + g_MovableMan.RegisterObject(this); + + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MovableObject to be identical to another, by deep copy. - -int MovableObject::Create(const MovableObject &reference) -{ - SceneObject::Create(reference); - - m_MOType = reference.m_MOType; - m_Mass = reference.m_Mass; - m_Pos = reference.m_Pos; - m_Vel = reference.m_Vel; - m_Scale = reference.m_Scale; - m_GlobalAccScalar = reference.m_GlobalAccScalar; - m_AirResistance = reference.m_AirResistance; - m_AirThreshold = reference.m_AirThreshold; - m_PinStrength = reference.m_PinStrength; - m_RestThreshold = reference.m_RestThreshold; -// m_Force = reference.m_Force; -// m_ImpulseForce = reference.m_ImpulseForce; - // Should reset age instead?? -// m_AgeTimer = reference.m_AgeTimer; - m_AgeTimer.Reset(); - m_RestTimer.Reset(); - m_Lifetime = reference.m_Lifetime; - m_Sharpness = reference.m_Sharpness; -// m_MaterialId = reference.m_MaterialId; - m_CheckTerrIntersection = reference.m_CheckTerrIntersection; - m_HitsMOs = reference.m_HitsMOs; - m_GetsHitByMOs = reference.m_GetsHitByMOs; - m_IgnoresTeamHits = reference.m_IgnoresTeamHits; - m_IgnoresAtomGroupHits = reference.m_IgnoresAtomGroupHits; - m_IgnoresAGHitsWhenSlowerThan = reference.m_IgnoresAGHitsWhenSlowerThan; - m_IgnoresActorHits = reference.m_IgnoresActorHits; - m_pMOToNotHit = reference.m_pMOToNotHit; - m_MOIgnoreTimer = reference.m_MOIgnoreTimer; - m_MissionCritical = reference.m_MissionCritical; - m_CanBeSquished = reference.m_CanBeSquished; - m_HUDVisible = reference.m_HUDVisible; - - m_ForceIntoMasterLuaState = reference.m_ForceIntoMasterLuaState; - for (auto &[scriptPath, scriptEnabled] : reference.m_AllLoadedScripts) { - LoadScript(scriptPath, scriptEnabled); - } - - if (reference.m_pScreenEffect) - { - m_ScreenEffectFile = reference.m_ScreenEffectFile; - m_pScreenEffect = m_ScreenEffectFile.GetAsBitmap(); - - } - m_EffectRotAngle = reference.m_EffectRotAngle; - m_InheritEffectRotAngle = reference.m_InheritEffectRotAngle; - m_RandomizeEffectRotAngle = reference.m_RandomizeEffectRotAngle; - m_RandomizeEffectRotAngleEveryFrame = reference.m_RandomizeEffectRotAngleEveryFrame; - - if (m_RandomizeEffectRotAngle) - m_EffectRotAngle = c_PI * RandomNum(-2.0F, 2.0F); - - m_ScreenEffectHash = reference.m_ScreenEffectHash; - m_EffectStartTime = reference.m_EffectStartTime; - m_EffectStopTime = reference.m_EffectStopTime; - m_EffectStartStrength = reference.m_EffectStartStrength; - m_EffectStopStrength = reference.m_EffectStopStrength; - m_EffectAlwaysShows = reference.m_EffectAlwaysShows; - m_RemoveOrphanTerrainRadius = reference.m_RemoveOrphanTerrainRadius; - m_RemoveOrphanTerrainMaxArea = reference.m_RemoveOrphanTerrainMaxArea; - m_RemoveOrphanTerrainRate = reference.m_RemoveOrphanTerrainRate; - m_DamageOnCollision = reference.m_DamageOnCollision; - m_DamageOnPenetration = reference.m_DamageOnPenetration; - m_WoundDamageMultiplier = reference.m_WoundDamageMultiplier; - m_IgnoreTerrain = reference.m_IgnoreTerrain; - - m_MOIDHit = reference.m_MOIDHit; - m_TerrainMatHit = reference.m_TerrainMatHit; - m_ParticleUniqueIDHit = reference.m_ParticleUniqueIDHit; - - m_SimUpdatesBetweenScriptedUpdates = reference.m_SimUpdatesBetweenScriptedUpdates; - m_SimUpdatesSinceLastScriptedUpdate = reference.m_SimUpdatesSinceLastScriptedUpdate; - - m_StringValueMap = reference.m_StringValueMap; - m_NumberValueMap = reference.m_NumberValueMap; - m_ObjectValueMap = reference.m_ObjectValueMap; - - m_UniqueID = MovableObject::GetNextUniqueID(); - g_MovableMan.RegisterObject(this); - - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MovableObject to be identical to another, by deep copy. + + int MovableObject::Create(const MovableObject& reference) { + SceneObject::Create(reference); + + m_MOType = reference.m_MOType; + m_Mass = reference.m_Mass; + m_Pos = reference.m_Pos; + m_Vel = reference.m_Vel; + m_Scale = reference.m_Scale; + m_GlobalAccScalar = reference.m_GlobalAccScalar; + m_AirResistance = reference.m_AirResistance; + m_AirThreshold = reference.m_AirThreshold; + m_PinStrength = reference.m_PinStrength; + m_RestThreshold = reference.m_RestThreshold; + // m_Force = reference.m_Force; + // m_ImpulseForce = reference.m_ImpulseForce; + // Should reset age instead?? + // m_AgeTimer = reference.m_AgeTimer; + m_AgeTimer.Reset(); + m_RestTimer.Reset(); + m_Lifetime = reference.m_Lifetime; + m_Sharpness = reference.m_Sharpness; + // m_MaterialId = reference.m_MaterialId; + m_CheckTerrIntersection = reference.m_CheckTerrIntersection; + m_HitsMOs = reference.m_HitsMOs; + m_GetsHitByMOs = reference.m_GetsHitByMOs; + m_IgnoresTeamHits = reference.m_IgnoresTeamHits; + m_IgnoresAtomGroupHits = reference.m_IgnoresAtomGroupHits; + m_IgnoresAGHitsWhenSlowerThan = reference.m_IgnoresAGHitsWhenSlowerThan; + m_IgnoresActorHits = reference.m_IgnoresActorHits; + m_pMOToNotHit = reference.m_pMOToNotHit; + m_MOIgnoreTimer = reference.m_MOIgnoreTimer; + m_MissionCritical = reference.m_MissionCritical; + m_CanBeSquished = reference.m_CanBeSquished; + m_HUDVisible = reference.m_HUDVisible; + + m_ForceIntoMasterLuaState = reference.m_ForceIntoMasterLuaState; + for (auto& [scriptPath, scriptEnabled]: reference.m_AllLoadedScripts) { + LoadScript(scriptPath, scriptEnabled); + } + if (reference.m_pScreenEffect) { + m_ScreenEffectFile = reference.m_ScreenEffectFile; + m_pScreenEffect = m_ScreenEffectFile.GetAsBitmap(); + } + m_EffectRotAngle = reference.m_EffectRotAngle; + m_InheritEffectRotAngle = reference.m_InheritEffectRotAngle; + m_RandomizeEffectRotAngle = reference.m_RandomizeEffectRotAngle; + m_RandomizeEffectRotAngleEveryFrame = reference.m_RandomizeEffectRotAngleEveryFrame; + + if (m_RandomizeEffectRotAngle) + m_EffectRotAngle = c_PI * RandomNum(-2.0F, 2.0F); + + m_ScreenEffectHash = reference.m_ScreenEffectHash; + m_EffectStartTime = reference.m_EffectStartTime; + m_EffectStopTime = reference.m_EffectStopTime; + m_EffectStartStrength = reference.m_EffectStartStrength; + m_EffectStopStrength = reference.m_EffectStopStrength; + m_EffectAlwaysShows = reference.m_EffectAlwaysShows; + m_RemoveOrphanTerrainRadius = reference.m_RemoveOrphanTerrainRadius; + m_RemoveOrphanTerrainMaxArea = reference.m_RemoveOrphanTerrainMaxArea; + m_RemoveOrphanTerrainRate = reference.m_RemoveOrphanTerrainRate; + m_DamageOnCollision = reference.m_DamageOnCollision; + m_DamageOnPenetration = reference.m_DamageOnPenetration; + m_WoundDamageMultiplier = reference.m_WoundDamageMultiplier; + m_IgnoreTerrain = reference.m_IgnoreTerrain; + + m_MOIDHit = reference.m_MOIDHit; + m_TerrainMatHit = reference.m_TerrainMatHit; + m_ParticleUniqueIDHit = reference.m_ParticleUniqueIDHit; + + m_SimUpdatesBetweenScriptedUpdates = reference.m_SimUpdatesBetweenScriptedUpdates; + m_SimUpdatesSinceLastScriptedUpdate = reference.m_SimUpdatesSinceLastScriptedUpdate; + + m_StringValueMap = reference.m_StringValueMap; + m_NumberValueMap = reference.m_NumberValueMap; + m_ObjectValueMap = reference.m_ObjectValueMap; + + m_UniqueID = MovableObject::GetNextUniqueID(); + g_MovableMan.RegisterObject(this); + + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int MovableObject::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return SceneObject::ReadProperty(propName, reader)); - - MatchProperty("Mass", { reader >> m_Mass; }); - MatchProperty("Velocity", { reader >> m_Vel; }); - MatchProperty("Scale", { reader >> m_Scale; }); - MatchProperty("GlobalAccScalar", { reader >> m_GlobalAccScalar; }); - MatchProperty("AirResistance", - { - reader >> m_AirResistance; - // Backwards compatibility after we made this value scaled over time - m_AirResistance /= 0.01666F; - }); - MatchProperty("AirThreshold", { reader >> m_AirThreshold; }); - MatchProperty("PinStrength", { reader >> m_PinStrength; }); - MatchProperty("RestThreshold", { reader >> m_RestThreshold; }); - MatchProperty("LifeTime", { reader >> m_Lifetime; }); - MatchProperty("Age", { - double age; - reader >> age; - m_AgeTimer.SetElapsedSimTimeMS(age); - }); - MatchProperty("Sharpness", { reader >> m_Sharpness; }); - MatchProperty("HitsMOs", { reader >> m_HitsMOs; }); - MatchProperty("GetsHitByMOs", { reader >> m_GetsHitByMOs; }); - MatchProperty("IgnoresTeamHits", { reader >> m_IgnoresTeamHits; }); - MatchProperty("IgnoresAtomGroupHits", { reader >> m_IgnoresAtomGroupHits; }); - MatchProperty("IgnoresAGHitsWhenSlowerThan", { reader >> m_IgnoresAGHitsWhenSlowerThan; }); - MatchProperty("IgnoresActorHits", { reader >> m_IgnoresActorHits; }); - MatchProperty("RemoveOrphanTerrainRadius", - { - reader >> m_RemoveOrphanTerrainRadius; - if (m_RemoveOrphanTerrainRadius > MAXORPHANRADIUS) - m_RemoveOrphanTerrainRadius = MAXORPHANRADIUS; - }); - MatchProperty("RemoveOrphanTerrainMaxArea", - { - reader >> m_RemoveOrphanTerrainMaxArea; - if (m_RemoveOrphanTerrainMaxArea > MAXORPHANRADIUS * MAXORPHANRADIUS) - m_RemoveOrphanTerrainMaxArea = MAXORPHANRADIUS * MAXORPHANRADIUS; - }); - MatchProperty("RemoveOrphanTerrainRate", { reader >> m_RemoveOrphanTerrainRate; }); - MatchProperty("MissionCritical", { reader >> m_MissionCritical; }); - MatchProperty("CanBeSquished", { reader >> m_CanBeSquished; }); - MatchProperty("HUDVisible", { reader >> m_HUDVisible; }); - MatchProperty("ScriptPath", { - std::string scriptPath = g_PresetMan.GetFullModulePath(reader.ReadPropValue()); - switch (LoadScript(scriptPath)) { - case 0: - break; - case -1: - reader.ReportError("The script path " + scriptPath + " was empty."); - break; - case -2: - reader.ReportError("The script path " + scriptPath + " did not point to a valid file."); - break; - case -3: - reader.ReportError("The script path " + scriptPath + " is already loaded onto this object."); - break; - case -4: - // Error in lua file, this'll pop up in the console so no need to report an error through the reader. - break; - default: - RTEAbort("Reached default case while adding script in INI. This should never happen!"); - break; - } - }); - MatchProperty("ScreenEffect", { - reader >> m_ScreenEffectFile; - m_pScreenEffect = m_ScreenEffectFile.GetAsBitmap(); - m_ScreenEffectHash = m_ScreenEffectFile.GetHash(); - }); - MatchProperty("EffectStartTime", { reader >> m_EffectStartTime; }); - MatchProperty("EffectRotAngle", { reader >> m_EffectRotAngle; }); - MatchProperty("InheritEffectRotAngle", { reader >> m_InheritEffectRotAngle; }); - MatchProperty("RandomizeEffectRotAngle", { reader >> m_RandomizeEffectRotAngle; }); - MatchProperty("RandomizeEffectRotAngleEveryFrame", { reader >> m_RandomizeEffectRotAngleEveryFrame; }); - MatchProperty("EffectStopTime", { reader >> m_EffectStopTime; }); - MatchProperty("EffectStartStrength", { - float strength; - reader >> strength; - m_EffectStartStrength = std::floor((float)255 * strength); - }); - MatchProperty("EffectStopStrength", { - float strength; - reader >> strength; - m_EffectStopStrength = std::floor((float)255 * strength); - }); - MatchProperty("EffectAlwaysShows", { reader >> m_EffectAlwaysShows; }); - MatchProperty("DamageOnCollision", { reader >> m_DamageOnCollision; }); - MatchProperty("DamageOnPenetration", { reader >> m_DamageOnPenetration; }); - MatchProperty("WoundDamageMultiplier", { reader >> m_WoundDamageMultiplier; }); - MatchProperty("ApplyWoundDamageOnCollision", { reader >> m_ApplyWoundDamageOnCollision; }); - MatchProperty("ApplyWoundBurstDamageOnCollision", { reader >> m_ApplyWoundBurstDamageOnCollision; }); - MatchProperty("IgnoreTerrain", { reader >> m_IgnoreTerrain; }); - MatchProperty("SimUpdatesBetweenScriptedUpdates", { reader >> m_SimUpdatesBetweenScriptedUpdates; }); - MatchProperty("AddCustomValue", { ReadCustomValueProperty(reader); }); - MatchProperty("ForceIntoMasterLuaState", { reader >> m_ForceIntoMasterLuaState; }); - - EndPropertyList; -} - -void MovableObject::ReadCustomValueProperty(Reader& reader) { - std::string customValueType; - reader >> customValueType; - std::string customKey = reader.ReadPropName(); - std::string customValue = reader.ReadPropValue(); - if (customValueType == "NumberValue") { - try { - SetNumberValue(customKey, std::stod(customValue)); - } catch (const std::invalid_argument) { - reader.ReportError("Tried to read a non-number value for SetNumberValue."); - } - } else if (customValueType == "StringValue") { - SetStringValue(customKey, customValue); - } else { - reader.ReportError("Invalid CustomValue type " + customValueType); - } - // Artificially end reading this property since we got all we needed - reader.NextProperty(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int MovableObject::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return SceneObject::ReadProperty(propName, reader)); + + MatchProperty("Mass", { reader >> m_Mass; }); + MatchProperty("Velocity", { reader >> m_Vel; }); + MatchProperty("Scale", { reader >> m_Scale; }); + MatchProperty("GlobalAccScalar", { reader >> m_GlobalAccScalar; }); + MatchProperty("AirResistance", + { + reader >> m_AirResistance; + // Backwards compatibility after we made this value scaled over time + m_AirResistance /= 0.01666F; + }); + MatchProperty("AirThreshold", { reader >> m_AirThreshold; }); + MatchProperty("PinStrength", { reader >> m_PinStrength; }); + MatchProperty("RestThreshold", { reader >> m_RestThreshold; }); + MatchProperty("LifeTime", { reader >> m_Lifetime; }); + MatchProperty("Age", { + double age; + reader >> age; + m_AgeTimer.SetElapsedSimTimeMS(age); + }); + MatchProperty("Sharpness", { reader >> m_Sharpness; }); + MatchProperty("HitsMOs", { reader >> m_HitsMOs; }); + MatchProperty("GetsHitByMOs", { reader >> m_GetsHitByMOs; }); + MatchProperty("IgnoresTeamHits", { reader >> m_IgnoresTeamHits; }); + MatchProperty("IgnoresAtomGroupHits", { reader >> m_IgnoresAtomGroupHits; }); + MatchProperty("IgnoresAGHitsWhenSlowerThan", { reader >> m_IgnoresAGHitsWhenSlowerThan; }); + MatchProperty("IgnoresActorHits", { reader >> m_IgnoresActorHits; }); + MatchProperty("RemoveOrphanTerrainRadius", + { + reader >> m_RemoveOrphanTerrainRadius; + if (m_RemoveOrphanTerrainRadius > MAXORPHANRADIUS) + m_RemoveOrphanTerrainRadius = MAXORPHANRADIUS; + }); + MatchProperty("RemoveOrphanTerrainMaxArea", + { + reader >> m_RemoveOrphanTerrainMaxArea; + if (m_RemoveOrphanTerrainMaxArea > MAXORPHANRADIUS * MAXORPHANRADIUS) + m_RemoveOrphanTerrainMaxArea = MAXORPHANRADIUS * MAXORPHANRADIUS; + }); + MatchProperty("RemoveOrphanTerrainRate", { reader >> m_RemoveOrphanTerrainRate; }); + MatchProperty("MissionCritical", { reader >> m_MissionCritical; }); + MatchProperty("CanBeSquished", { reader >> m_CanBeSquished; }); + MatchProperty("HUDVisible", { reader >> m_HUDVisible; }); + MatchProperty("ScriptPath", { + std::string scriptPath = g_PresetMan.GetFullModulePath(reader.ReadPropValue()); + switch (LoadScript(scriptPath)) { + case 0: + break; + case -1: + reader.ReportError("The script path " + scriptPath + " was empty."); + break; + case -2: + reader.ReportError("The script path " + scriptPath + " did not point to a valid file."); + break; + case -3: + reader.ReportError("The script path " + scriptPath + " is already loaded onto this object."); + break; + case -4: + // Error in lua file, this'll pop up in the console so no need to report an error through the reader. + break; + default: + RTEAbort("Reached default case while adding script in INI. This should never happen!"); + break; + } + }); + MatchProperty("ScreenEffect", { + reader >> m_ScreenEffectFile; + m_pScreenEffect = m_ScreenEffectFile.GetAsBitmap(); + m_ScreenEffectHash = m_ScreenEffectFile.GetHash(); + }); + MatchProperty("EffectStartTime", { reader >> m_EffectStartTime; }); + MatchProperty("EffectRotAngle", { reader >> m_EffectRotAngle; }); + MatchProperty("InheritEffectRotAngle", { reader >> m_InheritEffectRotAngle; }); + MatchProperty("RandomizeEffectRotAngle", { reader >> m_RandomizeEffectRotAngle; }); + MatchProperty("RandomizeEffectRotAngleEveryFrame", { reader >> m_RandomizeEffectRotAngleEveryFrame; }); + MatchProperty("EffectStopTime", { reader >> m_EffectStopTime; }); + MatchProperty("EffectStartStrength", { + float strength; + reader >> strength; + m_EffectStartStrength = std::floor((float)255 * strength); + }); + MatchProperty("EffectStopStrength", { + float strength; + reader >> strength; + m_EffectStopStrength = std::floor((float)255 * strength); + }); + MatchProperty("EffectAlwaysShows", { reader >> m_EffectAlwaysShows; }); + MatchProperty("DamageOnCollision", { reader >> m_DamageOnCollision; }); + MatchProperty("DamageOnPenetration", { reader >> m_DamageOnPenetration; }); + MatchProperty("WoundDamageMultiplier", { reader >> m_WoundDamageMultiplier; }); + MatchProperty("ApplyWoundDamageOnCollision", { reader >> m_ApplyWoundDamageOnCollision; }); + MatchProperty("ApplyWoundBurstDamageOnCollision", { reader >> m_ApplyWoundBurstDamageOnCollision; }); + MatchProperty("IgnoreTerrain", { reader >> m_IgnoreTerrain; }); + MatchProperty("SimUpdatesBetweenScriptedUpdates", { reader >> m_SimUpdatesBetweenScriptedUpdates; }); + MatchProperty("AddCustomValue", { ReadCustomValueProperty(reader); }); + MatchProperty("ForceIntoMasterLuaState", { reader >> m_ForceIntoMasterLuaState; }); + + EndPropertyList; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this MovableObject to an output stream for -// later recreation with Create(istream &stream); - -int MovableObject::Save(Writer &writer) const -{ - SceneObject::Save(writer); -// TODO: Make proper save system that knows not to save redundant data! -// Note - this function isn't even called when saving a scene. Turns out that scene special-cases this stuff, see Scene::Save() -// In future, perhaps we ought to not do that. Who knows? - - writer.NewProperty("Mass"); - writer << m_Mass; - writer.NewProperty("Velocity"); - writer << m_Vel; - writer.NewProperty("Scale"); - writer << m_Scale; - writer.NewProperty("GlobalAccScalar"); - writer << m_GlobalAccScalar; - writer.NewProperty("AirResistance"); - writer << (m_AirResistance * 0.01666F); // Backwards compatibility after we made this value scaled over time - writer.NewProperty("AirThreshold"); - writer << m_AirThreshold; - writer.NewProperty("PinStrength"); - writer << m_PinStrength; - writer.NewProperty("RestThreshold"); - writer << m_RestThreshold; - writer.NewProperty("LifeTime"); - writer << m_Lifetime; - writer.NewProperty("Sharpness"); - writer << m_Sharpness; - writer.NewProperty("HitsMOs"); - writer << m_HitsMOs; - writer.NewProperty("GetsHitByMOs"); - writer << m_GetsHitByMOs; - writer.NewProperty("IgnoresTeamHits"); - writer << m_IgnoresTeamHits; - writer.NewProperty("IgnoresAtomGroupHits"); - writer << m_IgnoresAtomGroupHits; - writer.NewProperty("IgnoresAGHitsWhenSlowerThan"); - writer << m_IgnoresAGHitsWhenSlowerThan; - writer.NewProperty("IgnoresActorHits"); - writer << m_IgnoresActorHits; - writer.NewProperty("MissionCritical"); - writer << m_MissionCritical; - writer.NewProperty("CanBeSquished"); - writer << m_CanBeSquished; - writer.NewProperty("HUDVisible"); - writer << m_HUDVisible; - for (const auto &[scriptPath, scriptEnabled] : m_AllLoadedScripts) { - if (!scriptPath.empty()) { - writer.NewProperty("ScriptPath"); - writer << scriptPath; - } - } - writer.NewProperty("ScreenEffect"); - writer << m_ScreenEffectFile; - writer.NewProperty("EffectStartTime"); - writer << m_EffectStartTime; - writer.NewProperty("EffectStopTime"); - writer << m_EffectStopTime; - writer.NewProperty("EffectStartStrength"); - writer << (float)m_EffectStartStrength / 255.0f; - writer.NewProperty("EffectStopStrength"); - writer << (float)m_EffectStopStrength / 255.0f; - writer.NewProperty("EffectAlwaysShows"); - writer << m_EffectAlwaysShows; - writer.NewProperty("DamageOnCollision"); - writer << m_DamageOnCollision; - writer.NewProperty("DamageOnPenetration"); - writer << m_DamageOnPenetration; - writer.NewProperty("WoundDamageMultiplier"); - writer << m_WoundDamageMultiplier; - writer.NewProperty("ApplyWoundDamageOnCollision"); - writer << m_ApplyWoundDamageOnCollision; - writer.NewProperty("ApplyWoundBurstDamageOnCollision"); - writer << m_ApplyWoundBurstDamageOnCollision; - writer.NewProperty("IgnoreTerrain"); - writer << m_IgnoreTerrain; - writer.NewProperty("SimUpdatesBetweenScriptedUpdates"); - writer << m_SimUpdatesBetweenScriptedUpdates; - - for (const auto &[key, value] : m_NumberValueMap) { - writer.ObjectStart("AddCustomValue = NumberValue"); - writer.NewPropertyWithValue(key, value); - } - - for (const auto &[key, value] : m_StringValueMap) { - writer.ObjectStart("AddCustomValue = StringValue"); - writer.NewPropertyWithValue(key, value); - } - - writer.NewProperty("ForceIntoMasterLuaState"); - writer << m_ForceIntoMasterLuaState; - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MovableObject::DestroyScriptState() { - if (m_ThreadedLuaState) { - std::lock_guard lock(m_ThreadedLuaState->GetMutex()); - - if (ObjectScriptsInitialized()) { - RunScriptedFunctionInAppropriateScripts("Destroy"); - m_ThreadedLuaState->RunScriptString(m_ScriptObjectName + " = nil;"); - m_ScriptObjectName.clear(); - } - - m_ThreadedLuaState->UnregisterMO(this); - m_ThreadedLuaState = nullptr; - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MovableObject::Destroy(bool notInherited) { - // Unfortunately, shit can still get destroyed at random from Lua states having ownership and their GC deciding to delete it. - // This skips the DestroyScriptState call... so there's leftover stale script state that we just can't do shit about. - // This means Destroy() doesn't get called, and the lua memory shit leaks because it never gets set to nil. But oh well. - // So.. we need to do this shit... I guess. Even though it's fucking awful. And it definitely results in possible deadlocks depending on how different lua states interact. - // TODO: try to make this at least reasonably workable - //DestroyScriptState(); - - if (m_ThreadedLuaState) { - m_ThreadedLuaState->UnregisterMO(this); - } - - g_MovableMan.UnregisterObject(this); - if (!notInherited) { - SceneObject::Destroy(); - } - Clear(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int MovableObject::LoadScript(const std::string &scriptPath, bool loadAsEnabledScript) { - if (scriptPath.empty()) { - return -1; - } else if (!System::PathExistsCaseSensitive(scriptPath)) { - return -2; - } else if (HasScript(scriptPath)) { - return -3; + void MovableObject::ReadCustomValueProperty(Reader& reader) { + std::string customValueType; + reader >> customValueType; + std::string customKey = reader.ReadPropName(); + std::string customValue = reader.ReadPropValue(); + if (customValueType == "NumberValue") { + try { + SetNumberValue(customKey, std::stod(customValue)); + } catch (const std::invalid_argument) { + reader.ReportError("Tried to read a non-number value for SetNumberValue."); + } + } else if (customValueType == "StringValue") { + SetStringValue(customKey, customValue); + } else { + reader.ReportError("Invalid CustomValue type " + customValueType); + } + // Artificially end reading this property since we got all we needed + reader.NextProperty(); } - LuaStateWrapper& usedState = GetAndLockStateForScript(scriptPath); - std::lock_guard lock(usedState.GetMutex(), std::adopt_lock); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this MovableObject to an output stream for + // later recreation with Create(istream &stream); + + int MovableObject::Save(Writer& writer) const { + SceneObject::Save(writer); + // TODO: Make proper save system that knows not to save redundant data! + // Note - this function isn't even called when saving a scene. Turns out that scene special-cases this stuff, see Scene::Save() + // In future, perhaps we ought to not do that. Who knows? + + writer.NewProperty("Mass"); + writer << m_Mass; + writer.NewProperty("Velocity"); + writer << m_Vel; + writer.NewProperty("Scale"); + writer << m_Scale; + writer.NewProperty("GlobalAccScalar"); + writer << m_GlobalAccScalar; + writer.NewProperty("AirResistance"); + writer << (m_AirResistance * 0.01666F); // Backwards compatibility after we made this value scaled over time + writer.NewProperty("AirThreshold"); + writer << m_AirThreshold; + writer.NewProperty("PinStrength"); + writer << m_PinStrength; + writer.NewProperty("RestThreshold"); + writer << m_RestThreshold; + writer.NewProperty("LifeTime"); + writer << m_Lifetime; + writer.NewProperty("Sharpness"); + writer << m_Sharpness; + writer.NewProperty("HitsMOs"); + writer << m_HitsMOs; + writer.NewProperty("GetsHitByMOs"); + writer << m_GetsHitByMOs; + writer.NewProperty("IgnoresTeamHits"); + writer << m_IgnoresTeamHits; + writer.NewProperty("IgnoresAtomGroupHits"); + writer << m_IgnoresAtomGroupHits; + writer.NewProperty("IgnoresAGHitsWhenSlowerThan"); + writer << m_IgnoresAGHitsWhenSlowerThan; + writer.NewProperty("IgnoresActorHits"); + writer << m_IgnoresActorHits; + writer.NewProperty("MissionCritical"); + writer << m_MissionCritical; + writer.NewProperty("CanBeSquished"); + writer << m_CanBeSquished; + writer.NewProperty("HUDVisible"); + writer << m_HUDVisible; + for (const auto& [scriptPath, scriptEnabled]: m_AllLoadedScripts) { + if (!scriptPath.empty()) { + writer.NewProperty("ScriptPath"); + writer << scriptPath; + } + } + writer.NewProperty("ScreenEffect"); + writer << m_ScreenEffectFile; + writer.NewProperty("EffectStartTime"); + writer << m_EffectStartTime; + writer.NewProperty("EffectStopTime"); + writer << m_EffectStopTime; + writer.NewProperty("EffectStartStrength"); + writer << (float)m_EffectStartStrength / 255.0f; + writer.NewProperty("EffectStopStrength"); + writer << (float)m_EffectStopStrength / 255.0f; + writer.NewProperty("EffectAlwaysShows"); + writer << m_EffectAlwaysShows; + writer.NewProperty("DamageOnCollision"); + writer << m_DamageOnCollision; + writer.NewProperty("DamageOnPenetration"); + writer << m_DamageOnPenetration; + writer.NewProperty("WoundDamageMultiplier"); + writer << m_WoundDamageMultiplier; + writer.NewProperty("ApplyWoundDamageOnCollision"); + writer << m_ApplyWoundDamageOnCollision; + writer.NewProperty("ApplyWoundBurstDamageOnCollision"); + writer << m_ApplyWoundBurstDamageOnCollision; + writer.NewProperty("IgnoreTerrain"); + writer << m_IgnoreTerrain; + writer.NewProperty("SimUpdatesBetweenScriptedUpdates"); + writer << m_SimUpdatesBetweenScriptedUpdates; + + for (const auto& [key, value]: m_NumberValueMap) { + writer.ObjectStart("AddCustomValue = NumberValue"); + writer.NewPropertyWithValue(key, value); + } - for (const std::string &functionName : GetSupportedScriptFunctionNames()) { - if (m_FunctionsAndScripts.find(functionName) == m_FunctionsAndScripts.end()) { - m_FunctionsAndScripts.try_emplace(functionName); + for (const auto& [key, value]: m_StringValueMap) { + writer.ObjectStart("AddCustomValue = StringValue"); + writer.NewPropertyWithValue(key, value); } - } - m_AllLoadedScripts.try_emplace(scriptPath, loadAsEnabledScript); + writer.NewProperty("ForceIntoMasterLuaState"); + writer << m_ForceIntoMasterLuaState; - std::unordered_map scriptFileFunctions; - if (usedState.RunScriptFileAndRetrieveFunctions(scriptPath, GetSupportedScriptFunctionNames(), scriptFileFunctions) < 0) { - return -4; + return 0; } - for (const auto &[functionName, functionObject] : scriptFileFunctions) { - LuaFunction& luaFunction = m_FunctionsAndScripts.at(functionName).emplace_back(); - luaFunction.m_ScriptIsEnabled = loadAsEnabledScript; - luaFunction.m_LuaFunction = std::unique_ptr(functionObject); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MovableObject::DestroyScriptState() { + if (m_ThreadedLuaState) { + std::lock_guard lock(m_ThreadedLuaState->GetMutex()); + + if (ObjectScriptsInitialized()) { + RunScriptedFunctionInAppropriateScripts("Destroy"); + m_ThreadedLuaState->RunScriptString(m_ScriptObjectName + " = nil;"); + m_ScriptObjectName.clear(); + } + + m_ThreadedLuaState->UnregisterMO(this); + m_ThreadedLuaState = nullptr; + } } - if (ObjectScriptsInitialized()) { - if (RunFunctionOfScript(scriptPath, "Create") < 0) { - return -5; - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MovableObject::Destroy(bool notInherited) { + // Unfortunately, shit can still get destroyed at random from Lua states having ownership and their GC deciding to delete it. + // This skips the DestroyScriptState call... so there's leftover stale script state that we just can't do shit about. + // This means Destroy() doesn't get called, and the lua memory shit leaks because it never gets set to nil. But oh well. + // So.. we need to do this shit... I guess. Even though it's fucking awful. And it definitely results in possible deadlocks depending on how different lua states interact. + // TODO: try to make this at least reasonably workable + // DestroyScriptState(); + + if (m_ThreadedLuaState) { + m_ThreadedLuaState->UnregisterMO(this); + } + + g_MovableMan.UnregisterObject(this); + if (!notInherited) { + SceneObject::Destroy(); + } + Clear(); } - return 0; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int MovableObject::LoadScript(const std::string& scriptPath, bool loadAsEnabledScript) { + if (scriptPath.empty()) { + return -1; + } else if (!System::PathExistsCaseSensitive(scriptPath)) { + return -2; + } else if (HasScript(scriptPath)) { + return -3; + } + + LuaStateWrapper& usedState = GetAndLockStateForScript(scriptPath); + std::lock_guard lock(usedState.GetMutex(), std::adopt_lock); -int MovableObject::ReloadScripts() { - if (m_AllLoadedScripts.empty()) { - return 0; - } + for (const std::string& functionName: GetSupportedScriptFunctionNames()) { + if (m_FunctionsAndScripts.find(functionName) == m_FunctionsAndScripts.end()) { + m_FunctionsAndScripts.try_emplace(functionName); + } + } - int status = 0; + m_AllLoadedScripts.try_emplace(scriptPath, loadAsEnabledScript); - //TODO consider getting rid of this const_cast. It would require either code duplication or creating some non-const methods (specifically of PresetMan::GetEntityPreset, which may be unsafe. Could be this gross exceptional handling is the best way to go. - MovableObject *movableObjectPreset = const_cast(dynamic_cast(g_PresetMan.GetEntityPreset(GetClassName(), GetPresetName(), GetModuleID()))); - if (this != movableObjectPreset) { - movableObjectPreset->ReloadScripts(); - } + std::unordered_map scriptFileFunctions; + if (usedState.RunScriptFileAndRetrieveFunctions(scriptPath, GetSupportedScriptFunctionNames(), scriptFileFunctions) < 0) { + return -4; + } + + for (const auto& [functionName, functionObject]: scriptFileFunctions) { + LuaFunction& luaFunction = m_FunctionsAndScripts.at(functionName).emplace_back(); + luaFunction.m_ScriptIsEnabled = loadAsEnabledScript; + luaFunction.m_LuaFunction = std::unique_ptr(functionObject); + } - std::unordered_map loadedScriptsCopy = m_AllLoadedScripts; - m_AllLoadedScripts.clear(); - m_FunctionsAndScripts.clear(); - for (const auto &[scriptPath, scriptEnabled] : loadedScriptsCopy) { - status = LoadScript(scriptPath, scriptEnabled); - // If the script fails to load because of an error in its Lua, we need to manually add the script path so it's not lost forever. - if (status == -4) { - m_AllLoadedScripts.try_emplace(scriptPath, scriptEnabled); - } else if (status < 0) { - break; + if (ObjectScriptsInitialized()) { + if (RunFunctionOfScript(scriptPath, "Create") < 0) { + return -5; + } } + + return 0; } - return status; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int MovableObject::ReloadScripts() { + if (m_AllLoadedScripts.empty()) { + return 0; + } -int MovableObject::InitializeObjectScripts() { - std::lock_guard lock(m_ThreadedLuaState->GetMutex()); - m_ScriptObjectName = "_ScriptedObjects[\"" + std::to_string(m_UniqueID) + "\"]"; - m_ThreadedLuaState->RegisterMO(this); - m_ThreadedLuaState->SetTempEntity(this); - if (m_ThreadedLuaState->RunScriptString("_ScriptedObjects = _ScriptedObjects or {}; " + m_ScriptObjectName + " = To" + GetClassName() + "(LuaMan.TempEntity); ") < 0) { - RTEAbort("Failed to initialize object scripts for " + GetModuleAndPresetName() + ". Please report this to a developer."); - } + int status = 0; - if (!m_FunctionsAndScripts.at("Create").empty() && RunScriptedFunctionInAppropriateScripts("Create", false, true) < 0) { - m_ScriptObjectName = "ERROR"; - return -1; + // TODO consider getting rid of this const_cast. It would require either code duplication or creating some non-const methods (specifically of PresetMan::GetEntityPreset, which may be unsafe. Could be this gross exceptional handling is the best way to go. + MovableObject* movableObjectPreset = const_cast(dynamic_cast(g_PresetMan.GetEntityPreset(GetClassName(), GetPresetName(), GetModuleID()))); + if (this != movableObjectPreset) { + movableObjectPreset->ReloadScripts(); + } + + std::unordered_map loadedScriptsCopy = m_AllLoadedScripts; + m_AllLoadedScripts.clear(); + m_FunctionsAndScripts.clear(); + for (const auto& [scriptPath, scriptEnabled]: loadedScriptsCopy) { + status = LoadScript(scriptPath, scriptEnabled); + // If the script fails to load because of an error in its Lua, we need to manually add the script path so it's not lost forever. + if (status == -4) { + m_AllLoadedScripts.try_emplace(scriptPath, scriptEnabled); + } else if (status < 0) { + break; + } + } + + return status; } - return 0; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MovableObject::InitializeObjectScripts() { + std::lock_guard lock(m_ThreadedLuaState->GetMutex()); + m_ScriptObjectName = "_ScriptedObjects[\"" + std::to_string(m_UniqueID) + "\"]"; + m_ThreadedLuaState->RegisterMO(this); + m_ThreadedLuaState->SetTempEntity(this); + if (m_ThreadedLuaState->RunScriptString("_ScriptedObjects = _ScriptedObjects or {}; " + m_ScriptObjectName + " = To" + GetClassName() + "(LuaMan.TempEntity); ") < 0) { + RTEAbort("Failed to initialize object scripts for " + GetModuleAndPresetName() + ". Please report this to a developer."); + } + + if (!m_FunctionsAndScripts.at("Create").empty() && RunScriptedFunctionInAppropriateScripts("Create", false, true) < 0) { + m_ScriptObjectName = "ERROR"; + return -1; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + return 0; + } -bool MovableObject::EnableOrDisableScript(const std::string &scriptPath, bool enableScript) { - if (m_AllLoadedScripts.empty()) { - return false; - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (auto scriptEntryIterator = m_AllLoadedScripts.find(scriptPath); scriptEntryIterator != m_AllLoadedScripts.end() && scriptEntryIterator->second == !enableScript) { - if (ObjectScriptsInitialized() && RunFunctionOfScript(scriptPath, enableScript ? "OnScriptEnable" : "OnScriptDisable") < 0) { + bool MovableObject::EnableOrDisableScript(const std::string& scriptPath, bool enableScript) { + if (m_AllLoadedScripts.empty()) { return false; } - scriptEntryIterator->second = enableScript; + if (auto scriptEntryIterator = m_AllLoadedScripts.find(scriptPath); scriptEntryIterator != m_AllLoadedScripts.end() && scriptEntryIterator->second == !enableScript) { + if (ObjectScriptsInitialized() && RunFunctionOfScript(scriptPath, enableScript ? "OnScriptEnable" : "OnScriptDisable") < 0) { + return false; + } - // Slow, but better to spend this time here in EnableOrDisableScript than every update hashing the script path as we used to do - for (auto &[functionName, functionObjects] : m_FunctionsAndScripts) { - for (LuaFunction &luaFunction : functionObjects) { - if (luaFunction.m_LuaFunction->GetFilePath() == scriptPath) { - luaFunction.m_ScriptIsEnabled = enableScript; - } - } - } + scriptEntryIterator->second = enableScript; - return true; - } - return false; -} + // Slow, but better to spend this time here in EnableOrDisableScript than every update hashing the script path as we used to do + for (auto& [functionName, functionObjects]: m_FunctionsAndScripts) { + for (LuaFunction& luaFunction: functionObjects) { + if (luaFunction.m_LuaFunction->GetFilePath() == scriptPath) { + luaFunction.m_ScriptIsEnabled = enableScript; + } + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + return true; + } + return false; + } -void MovableObject::EnableOrDisableAllScripts(bool enableScripts) { - for (const auto &[scriptPath, scriptIsEnabled] : m_AllLoadedScripts) { - if (enableScripts != scriptIsEnabled) { - EnableOrDisableScript(scriptPath, enableScripts); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MovableObject::EnableOrDisableAllScripts(bool enableScripts) { + for (const auto& [scriptPath, scriptIsEnabled]: m_AllLoadedScripts) { + if (enableScripts != scriptIsEnabled) { + EnableOrDisableScript(scriptPath, enableScripts); + } } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int MovableObject::RunScriptedFunctionInAppropriateScripts(const std::string &functionName, bool runOnDisabledScripts, bool stopOnError, const std::vector &functionEntityArguments, const std::vector &functionLiteralArguments, const std::vector &functionObjectArguments) { - int status = 0; - - auto itr = m_FunctionsAndScripts.find(functionName); - if (itr == m_FunctionsAndScripts.end() || itr->second.empty()) { - return -1; - } - - if (!ObjectScriptsInitialized()) { - status = InitializeObjectScripts(); - } - - if (status >= 0) { - ZoneScoped; - ZoneText(functionName.c_str(), functionName.length()); - for (const LuaFunction &luaFunction : itr->second) { - const LuabindObjectWrapper *luabindObjectWrapper = luaFunction.m_LuaFunction.get(); - if (runOnDisabledScripts || luaFunction.m_ScriptIsEnabled) { - LuaStateWrapper& usedState = GetAndLockStateForScript(luabindObjectWrapper->GetFilePath(), &luaFunction); - std::lock_guard lock(usedState.GetMutex(), std::adopt_lock); - status = usedState.RunScriptFunctionObject(luabindObjectWrapper, "_ScriptedObjects", std::to_string(m_UniqueID), functionEntityArguments, functionLiteralArguments, functionObjectArguments); - if (status < 0 && stopOnError) { - return status; - } - } - } - } - return status; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int MovableObject::RunFunctionOfScript(const std::string &scriptPath, const std::string &functionName, const std::vector &functionEntityArguments, const std::vector &functionLiteralArguments) { - if (m_AllLoadedScripts.empty() || !ObjectScriptsInitialized()) { - return -1; } - LuaStateWrapper& usedState = GetAndLockStateForScript(scriptPath); - std::lock_guard lock(usedState.GetMutex(), std::adopt_lock); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MovableObject::RunScriptedFunctionInAppropriateScripts(const std::string& functionName, bool runOnDisabledScripts, bool stopOnError, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { + int status = 0; - for (const LuaFunction &luaFunction : m_FunctionsAndScripts.at(functionName)) { - const LuabindObjectWrapper *luabindObjectWrapper = luaFunction.m_LuaFunction.get(); - if (scriptPath == luabindObjectWrapper->GetFilePath() && usedState.RunScriptFunctionObject(luabindObjectWrapper, "_ScriptedObjects", std::to_string(m_UniqueID), functionEntityArguments, functionLiteralArguments) < 0) { - if (m_AllLoadedScripts.size() > 1) { - g_ConsoleMan.PrintString("ERROR: An error occured while trying to run the " + functionName + " function for script at path " + scriptPath); + auto itr = m_FunctionsAndScripts.find(functionName); + if (itr == m_FunctionsAndScripts.end() || itr->second.empty()) { + return -1; + } + + if (!ObjectScriptsInitialized()) { + status = InitializeObjectScripts(); + } + + if (status >= 0) { + ZoneScoped; + ZoneText(functionName.c_str(), functionName.length()); + for (const LuaFunction& luaFunction: itr->second) { + const LuabindObjectWrapper* luabindObjectWrapper = luaFunction.m_LuaFunction.get(); + if (runOnDisabledScripts || luaFunction.m_ScriptIsEnabled) { + LuaStateWrapper& usedState = GetAndLockStateForScript(luabindObjectWrapper->GetFilePath(), &luaFunction); + std::lock_guard lock(usedState.GetMutex(), std::adopt_lock); + status = usedState.RunScriptFunctionObject(luabindObjectWrapper, "_ScriptedObjects", std::to_string(m_UniqueID), functionEntityArguments, functionLiteralArguments, functionObjectArguments); + if (status < 0 && stopOnError) { + return status; + } + } } - return -2; } + return status; } - return 0; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int MovableObject::RunFunctionOfScript(const std::string& scriptPath, const std::string& functionName, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments) { + if (m_AllLoadedScripts.empty() || !ObjectScriptsInitialized()) { + return -1; + } -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: MovableObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Copy constructor method used to instantiate a MovableObject object -// identical to an already existing one. + LuaStateWrapper& usedState = GetAndLockStateForScript(scriptPath); + std::lock_guard lock(usedState.GetMutex(), std::adopt_lock); + + for (const LuaFunction& luaFunction: m_FunctionsAndScripts.at(functionName)) { + const LuabindObjectWrapper* luabindObjectWrapper = luaFunction.m_LuaFunction.get(); + if (scriptPath == luabindObjectWrapper->GetFilePath() && usedState.RunScriptFunctionObject(luabindObjectWrapper, "_ScriptedObjects", std::to_string(m_UniqueID), functionEntityArguments, functionLiteralArguments) < 0) { + if (m_AllLoadedScripts.size() > 1) { + g_ConsoleMan.PrintString("ERROR: An error occured while trying to run the " + functionName + " function for script at path " + scriptPath); + } + return -2; + } + } -MovableObject::MovableObject(const MovableObject &reference): - m_Mass(reference.GetMass()), - m_Pos(reference.GetPos()), - m_Vel(reference.GetVel()), - m_AgeTimer(reference.GetAge()), - m_Lifetime(reference.GetLifetime()) -{ + return 0; + } -} -*/ + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: MovableObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Copy constructor method used to instantiate a MovableObject object + // identical to an already existing one. + + MovableObject::MovableObject(const MovableObject &reference): + m_Mass(reference.GetMass()), + m_Pos(reference.GetPos()), + m_Vel(reference.GetVel()), + m_AgeTimer(reference.GetAge()), + m_Lifetime(reference.GetLifetime()) + { -void MovableObject::SetTeam(int team) { - SceneObject::SetTeam(team); - if (Activity *activity = g_ActivityMan.GetActivity()) { activity->ForceSetTeamAsActive(team); } -} + } + */ -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAltitude -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the altitide of this' pos (or appropriate low point) over the -// terrain, in pixels. + void MovableObject::SetTeam(int team) { + SceneObject::SetTeam(team); + if (Activity* activity = g_ActivityMan.GetActivity()) { + activity->ForceSetTeamAsActive(team); + } + } -float MovableObject::GetAltitude(int max, int accuracy) -{ - return g_SceneMan.FindAltitude(m_Pos, max, accuracy); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAltitude + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the altitide of this' pos (or appropriate low point) over the + // terrain, in pixels. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float MovableObject::GetAltitude(int max, int accuracy) { + return g_SceneMan.FindAltitude(m_Pos, max, accuracy); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::AddAbsForce(const Vector &force, const Vector &absPos) -{ - m_Forces.push_back(std::make_pair(force, g_SceneMan.ShortestDistance(m_Pos, absPos) * c_MPP)); -} + void MovableObject::AddAbsForce(const Vector& force, const Vector& absPos) { + m_Forces.push_back(std::make_pair(force, g_SceneMan.ShortestDistance(m_Pos, absPos) * c_MPP)); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::AddAbsImpulseForce(const Vector &impulse, const Vector &absPos) -{ + void MovableObject::AddAbsImpulseForce(const Vector& impulse, const Vector& absPos) { #ifndef RELEASE_BUILD RTEAssert(impulse.GetLargest() < 500000, "HUEG IMPULSE FORCE"); #endif m_ImpulseForces.push_back(std::make_pair(impulse, g_SceneMan.ShortestDistance(m_Pos, absPos) * c_MPP)); -} + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::RestDetection() { - // Translational settling detection. - if (m_Vel.Dot(m_PrevVel) < 0) { - ++m_VelOscillations; - } else { - m_VelOscillations = 0; + void MovableObject::RestDetection() { + // Translational settling detection. + if (m_Vel.Dot(m_PrevVel) < 0) { + ++m_VelOscillations; + } else { + m_VelOscillations = 0; + } + if ((m_Pos - m_PrevPos).MagnitudeIsGreaterThan(1.0F)) { + m_RestTimer.Reset(); + } } - if ((m_Pos - m_PrevPos).MagnitudeIsGreaterThan(1.0F)) { m_RestTimer.Reset(); } -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool MovableObject::IsAtRest() { - if (m_RestThreshold < 0 || m_PinStrength) { - return false; - } else { - if (m_VelOscillations > 2) { - return true; + bool MovableObject::IsAtRest() { + if (m_RestThreshold < 0 || m_PinStrength) { + return false; + } else { + if (m_VelOscillations > 2) { + return true; + } + return m_RestTimer.IsPastSimMS(m_RestThreshold); } - return m_RestTimer.IsPastSimMS(m_RestThreshold); } -} -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: OnMOHit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits another MO. -// This is called by the owned Atom/AtomGroup of this MovableObject during -// travel. - -bool MovableObject::OnMOHit(HitData &hd) -{ - if (hd.RootBody[HITOR] != hd.RootBody[HITEE] && (hd.Body[HITOR] == this || hd.Body[HITEE] == this)) { - RunScriptedFunctionInAppropriateScripts("OnCollideWithMO", false, false, {hd.Body[hd.Body[HITOR] == this ? HITEE : HITOR], hd.RootBody[hd.Body[HITOR] == this ? HITEE : HITOR]}); - } - return hd.Terminate[hd.RootBody[HITOR] == this ? HITOR : HITEE] = false; -} - -unsigned char MovableObject::HitWhatTerrMaterial() const -{ - return m_LastCollisionSimFrameNumber == g_MovableMan.GetSimUpdateFrameNumber() ? m_TerrainMatHit : g_MaterialAir; -} - -void MovableObject::SetHitWhatTerrMaterial(unsigned char matID) -{ - m_TerrainMatHit = matID; - m_LastCollisionSimFrameNumber = g_MovableMan.GetSimUpdateFrameNumber(); - RunScriptedFunctionInAppropriateScripts("OnCollideWithTerrain", false, false, {}, {std::to_string(m_TerrainMatHit)}); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Vector MovableObject::GetTotalForce() { - Vector totalForceVector; - for (const auto &[force, forceOffset] : m_Forces) { - totalForceVector += force; - } - return totalForceVector; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: OnMOHit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits another MO. + // This is called by the owned Atom/AtomGroup of this MovableObject during + // travel. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gathers and applies the global and accumulated forces. Then it clears -// out the force list.Note that this does NOT apply the accumulated -// impulses (impulse forces)! - -void MovableObject::ApplyForces() -{ - // Don't apply forces to pinned objects - if (m_PinStrength > 0) - { - m_Forces.clear(); - return; - } - - float deltaTime = g_TimerMan.GetDeltaTimeSecs(); - -//// TODO: remove this!$@#$%#@%#@%#@^#@^#@^@#^@#") -// if (m_PresetName != "Test Player") - // Apply global acceleration (gravity), scaled by the scalar we have that can even be negative. - m_Vel += g_SceneMan.GetGlobalAcc() * m_GlobalAccScalar * deltaTime; - - // Calculate air resistance effects, only when something flies faster than a threshold - if (m_AirResistance > 0 && m_Vel.GetLargest() >= m_AirThreshold) - m_Vel *= 1.0 - (m_AirResistance * deltaTime); - - // Apply the translational effects of all the forces accumulated during the Update(). - if (m_Forces.size() > 0) { - // Continuous force application to transformational velocity (F = m * a -> a = F / m). - m_Vel += GetTotalForce() / (GetMass() != 0 ? GetMass() : 0.0001F) * deltaTime; + bool MovableObject::OnMOHit(HitData& hd) { + if (hd.RootBody[HITOR] != hd.RootBody[HITEE] && (hd.Body[HITOR] == this || hd.Body[HITEE] == this)) { + RunScriptedFunctionInAppropriateScripts("OnCollideWithMO", false, false, {hd.Body[hd.Body[HITOR] == this ? HITEE : HITOR], hd.RootBody[hd.Body[HITOR] == this ? HITEE : HITOR]}); + } + return hd.Terminate[hd.RootBody[HITOR] == this ? HITOR : HITEE] = false; } - // Clear out the forces list - m_Forces.clear(); -} + unsigned char MovableObject::HitWhatTerrMaterial() const { + return m_LastCollisionSimFrameNumber == g_MovableMan.GetSimUpdateFrameNumber() ? m_TerrainMatHit : g_MaterialAir; + } + void MovableObject::SetHitWhatTerrMaterial(unsigned char matID) { + m_TerrainMatHit = matID; + m_LastCollisionSimFrameNumber = g_MovableMan.GetSimUpdateFrameNumber(); + RunScriptedFunctionInAppropriateScripts("OnCollideWithTerrain", false, false, {}, {std::to_string(m_TerrainMatHit)}); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyImpulses -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gathers and applies the accumulated impulse forces. Then it clears -// out the impulse list.Note that this does NOT apply the accumulated -// regular forces (non-impulse forces)! + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::ApplyImpulses() -{ - // Don't apply forces to pinned objects - if (m_PinStrength > 0) - { - m_ImpulseForces.clear(); - return; - } + Vector MovableObject::GetTotalForce() { + Vector totalForceVector; + for (const auto& [force, forceOffset]: m_Forces) { + totalForceVector += force; + } + return totalForceVector; + } -// float totalImpulses. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ApplyForces + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gathers and applies the global and accumulated forces. Then it clears + // out the force list.Note that this does NOT apply the accumulated + // impulses (impulse forces)! + + void MovableObject::ApplyForces() { + // Don't apply forces to pinned objects + if (m_PinStrength > 0) { + m_Forces.clear(); + return; + } - // Apply the translational effects of all the impulses accumulated during the Update() - for (auto iItr = m_ImpulseForces.begin(); iItr != m_ImpulseForces.end(); ++iItr) { - // Impulse force application to the transformational velocity of this MO. - // Don't timescale these because they're already in kg * m/s (as opposed to kg * m/s^2). - m_Vel += (*iItr).first / (GetMass() != 0 ? GetMass() : 0.0001F); - } + float deltaTime = g_TimerMan.GetDeltaTimeSecs(); - // Clear out the impulses list - m_ImpulseForces.clear(); -} + //// TODO: remove this!$@#$%#@%#@%#@^#@^#@^@#^@#") + // if (m_PresetName != "Test Player") + // Apply global acceleration (gravity), scaled by the scalar we have that can even be negative. + m_Vel += g_SceneMan.GetGlobalAcc() * m_GlobalAccScalar * deltaTime; + // Calculate air resistance effects, only when something flies faster than a threshold + if (m_AirResistance > 0 && m_Vel.GetLargest() >= m_AirThreshold) + m_Vel *= 1.0 - (m_AirResistance * deltaTime); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done before Travel(). Always call before -// calling Travel. - -void MovableObject::PreTravel() -{ - // Temporarily remove the representation of this from the scene MO sampler - if (m_GetsHitByMOs) { - m_IsTraveling = true; -#ifdef DRAW_MOID_LAYER - if (!g_SettingsMan.SimplifiedCollisionDetection()) { - Draw(g_SceneMan.GetMOIDBitmap(), Vector(), DrawMode::g_DrawNoMOID, true); + // Apply the translational effects of all the forces accumulated during the Update(). + if (m_Forces.size() > 0) { + // Continuous force application to transformational velocity (F = m * a -> a = F / m). + m_Vel += GetTotalForce() / (GetMass() != 0 ? GetMass() : 0.0001F) * deltaTime; } -#endif - } - // Save previous position and velocities before moving - m_PrevPos = m_Pos; - m_PrevVel = m_Vel; - - m_MOIDHit = g_NoMOID; - m_TerrainMatHit = g_MaterialAir; - m_ParticleUniqueIDHit = 0; -} + // Clear out the forces list + m_Forces.clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ApplyImpulses + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gathers and applies the accumulated impulse forces. Then it clears + // out the impulse list.Note that this does NOT apply the accumulated + // regular forces (non-impulse forces)! + + void MovableObject::ApplyImpulses() { + // Don't apply forces to pinned objects + if (m_PinStrength > 0) { + m_ImpulseForces.clear(); + return; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Travel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Travels this MovableObject, using its physical representation. + // float totalImpulses. -void MovableObject::Travel() -{ + // Apply the translational effects of all the impulses accumulated during the Update() + for (auto iItr = m_ImpulseForces.begin(); iItr != m_ImpulseForces.end(); ++iItr) { + // Impulse force application to the transformational velocity of this MO. + // Don't timescale these because they're already in kg * m/s (as opposed to kg * m/s^2). + m_Vel += (*iItr).first / (GetMass() != 0 ? GetMass() : 0.0001F); + } -} + // Clear out the impulses list + m_ImpulseForces.clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreTravel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does stuff that needs to be done before Travel(). Always call before + // calling Travel. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PostTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done after Travel(). Always call after -// calling Travel. - -void MovableObject::PostTravel() -{ - // Toggle whether this gets hit by other AtomGroup MOs depending on whether it's going slower than a set threshold - if (m_IgnoresAGHitsWhenSlowerThan > 0) { - m_IgnoresAtomGroupHits = m_Vel.MagnitudeIsLessThan(m_IgnoresAGHitsWhenSlowerThan); - } - - if (m_GetsHitByMOs) { - if (!GetParent()) { - m_IsTraveling = false; + void MovableObject::PreTravel() { + // Temporarily remove the representation of this from the scene MO sampler + if (m_GetsHitByMOs) { + m_IsTraveling = true; #ifdef DRAW_MOID_LAYER if (!g_SettingsMan.SimplifiedCollisionDetection()) { - Draw(g_SceneMan.GetMOIDBitmap(), Vector(), DrawMode::g_DrawMOID, true); + Draw(g_SceneMan.GetMOIDBitmap(), Vector(), DrawMode::g_DrawNoMOID, true); } #endif } - m_AlreadyHitBy.clear(); - } - m_IsUpdated = true; - - // Check for age expiration - if (m_Lifetime && m_AgeTimer.GetElapsedSimTimeMS() > m_Lifetime) { - m_ToDelete = true; - } - // Check for stupid positions - if (!GetParent() && !g_SceneMan.IsWithinBounds(m_Pos.m_X, m_Pos.m_Y, 1000)) { - m_ToDelete = true; - } + // Save previous position and velocities before moving + m_PrevPos = m_Pos; + m_PrevVel = m_Vel; - // Fix speeds that are too high - FixTooFast(); + m_MOIDHit = g_NoMOID; + m_TerrainMatHit = g_MaterialAir; + m_ParticleUniqueIDHit = 0; + } - // Never let mission critical stuff settle or delete - if (m_MissionCritical) { - m_ToSettle = false; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Travel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Travels this MovableObject, using its physical representation. - // Reset the terrain intersection warning - m_CheckTerrIntersection = false; + void MovableObject::Travel() { + } - m_DistanceTravelled += m_Vel.GetMagnitude() * c_PPM * g_TimerMan.GetDeltaTimeSecs(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PostTravel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does stuff that needs to be done after Travel(). Always call after + // calling Travel. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void MovableObject::PostTravel() { + // Toggle whether this gets hit by other AtomGroup MOs depending on whether it's going slower than a set threshold + if (m_IgnoresAGHitsWhenSlowerThan > 0) { + m_IgnoresAtomGroupHits = m_Vel.MagnitudeIsLessThan(m_IgnoresAGHitsWhenSlowerThan); + } -void MovableObject::Update() { - if (m_RandomizeEffectRotAngleEveryFrame) { m_EffectRotAngle = c_PI * 2.0F * RandomNormalNum(); } -} + if (m_GetsHitByMOs) { + if (!GetParent()) { + m_IsTraveling = false; +#ifdef DRAW_MOID_LAYER + if (!g_SettingsMan.SimplifiedCollisionDetection()) { + Draw(g_SceneMan.GetMOIDBitmap(), Vector(), DrawMode::g_DrawMOID, true); + } +#endif + } + m_AlreadyHitBy.clear(); + } + m_IsUpdated = true; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Check for age expiration + if (m_Lifetime && m_AgeTimer.GetElapsedSimTimeMS() > m_Lifetime) { + m_ToDelete = true; + } -void MovableObject::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { - if (mode == g_DrawMOID && m_MOID == g_NoMOID) { - return; - } + // Check for stupid positions + if (!GetParent() && !g_SceneMan.IsWithinBounds(m_Pos.m_X, m_Pos.m_Y, 1000)) { + m_ToDelete = true; + } - g_SceneMan.RegisterDrawing(targetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, m_Pos - targetPos, 1.0F); -} + // Fix speeds that are too high + FixTooFast(); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Never let mission critical stuff settle or delete + if (m_MissionCritical) { + m_ToSettle = false; + } -int MovableObject::UpdateScripts() { - m_SimUpdatesSinceLastScriptedUpdate++; + // Reset the terrain intersection warning + m_CheckTerrIntersection = false; - if (m_AllLoadedScripts.empty()) { - return -1; + m_DistanceTravelled += m_Vel.GetMagnitude() * c_PPM * g_TimerMan.GetDeltaTimeSecs(); } - int status = 0; - if (!ObjectScriptsInitialized()) { - status = InitializeObjectScripts(); - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (m_SimUpdatesSinceLastScriptedUpdate < m_SimUpdatesBetweenScriptedUpdates) { - return 1; + void MovableObject::Update() { + if (m_RandomizeEffectRotAngleEveryFrame) { + m_EffectRotAngle = c_PI * 2.0F * RandomNormalNum(); + } } - m_SimUpdatesSinceLastScriptedUpdate = 0; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MovableObject::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { + if (mode == g_DrawMOID && m_MOID == g_NoMOID) { + return; + } - if (status >= 0) { - status = RunScriptedFunctionInAppropriateScripts("Update", false, true, {}, {}, {}); + g_SceneMan.RegisterDrawing(targetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, m_Pos - targetPos, 1.0F); } - return status; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MovableObject::UpdateScripts() { + m_SimUpdatesSinceLastScriptedUpdate++; + + if (m_AllLoadedScripts.empty()) { + return -1; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int status = 0; + if (!ObjectScriptsInitialized()) { + status = InitializeObjectScripts(); + } -const std::string & MovableObject::GetStringValue(const std::string &key) const -{ - auto itr = m_StringValueMap.find(key); - if (itr == m_StringValueMap.end()) { - return ms_EmptyString; - } - - return itr->second; -} + if (m_SimUpdatesSinceLastScriptedUpdate < m_SimUpdatesBetweenScriptedUpdates) { + return 1; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + m_SimUpdatesSinceLastScriptedUpdate = 0; -std::string MovableObject::GetEncodedStringValue(const std::string &key) const -{ - auto itr = m_StringValueMap.find(key); - if (itr == m_StringValueMap.end()) { - return ms_EmptyString; - } + if (status >= 0) { + status = RunScriptedFunctionInAppropriateScripts("Update", false, true, {}, {}, {}); + } - return base64_decode(itr->second); -} + return status; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -double MovableObject::GetNumberValue(const std::string& key) const -{ - auto itr = m_NumberValueMap.find(key); - if (itr == m_NumberValueMap.end()) { - return 0.0; - } + const std::string& MovableObject::GetStringValue(const std::string& key) const { + auto itr = m_StringValueMap.find(key); + if (itr == m_StringValueMap.end()) { + return ms_EmptyString; + } - return itr->second; -} + return itr->second; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Entity* MovableObject::GetObjectValue(const std::string &key) const -{ - auto itr = m_ObjectValueMap.find(key); - if (itr == m_ObjectValueMap.end()) { - return nullptr; - } + std::string MovableObject::GetEncodedStringValue(const std::string& key) const { + auto itr = m_StringValueMap.find(key); + if (itr == m_StringValueMap.end()) { + return ms_EmptyString; + } + + return base64_decode(itr->second); + } - return itr->second; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + double MovableObject::GetNumberValue(const std::string& key) const { + auto itr = m_NumberValueMap.find(key); + if (itr == m_NumberValueMap.end()) { + return 0.0; + } + + return itr->second; + } -void MovableObject::SetStringValue(const std::string &key, const std::string &value) -{ - m_StringValueMap[key] = value; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Entity* MovableObject::GetObjectValue(const std::string& key) const { + auto itr = m_ObjectValueMap.find(key); + if (itr == m_ObjectValueMap.end()) { + return nullptr; + } -void MovableObject::SetEncodedStringValue(const std::string &key, const std::string &value) -{ - m_StringValueMap[key] = base64_encode(value, true); -} + return itr->second; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::SetNumberValue(const std::string &key, double value) -{ - m_NumberValueMap[key] = value; -} + void MovableObject::SetStringValue(const std::string& key, const std::string& value) { + m_StringValueMap[key] = value; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::SetObjectValue(const std::string &key, Entity* value) -{ - m_ObjectValueMap[key] = value; -} + void MovableObject::SetEncodedStringValue(const std::string& key, const std::string& value) { + m_StringValueMap[key] = base64_encode(value, true); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::RemoveStringValue(const std::string &key) -{ - m_StringValueMap.erase(key); -} + void MovableObject::SetNumberValue(const std::string& key, double value) { + m_NumberValueMap[key] = value; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::RemoveNumberValue(const std::string &key) -{ - m_NumberValueMap.erase(key); -} + void MovableObject::SetObjectValue(const std::string& key, Entity* value) { + m_ObjectValueMap[key] = value; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::RemoveObjectValue(const std::string &key) -{ - m_ObjectValueMap.erase(key); -} + void MovableObject::RemoveStringValue(const std::string& key) { + m_StringValueMap.erase(key); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool MovableObject::StringValueExists(const std::string &key) const -{ - return m_StringValueMap.find(key) != m_StringValueMap.end(); -} + void MovableObject::RemoveNumberValue(const std::string& key) { + m_NumberValueMap.erase(key); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool MovableObject::NumberValueExists(const std::string &key) const -{ - return m_NumberValueMap.find(key) != m_NumberValueMap.end(); -} + void MovableObject::RemoveObjectValue(const std::string& key) { + m_ObjectValueMap.erase(key); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool MovableObject::ObjectValueExists(const std::string &key) const -{ - return m_ObjectValueMap.find(key) != m_ObjectValueMap.end(); -} + bool MovableObject::StringValueExists(const std::string& key) const { + return m_StringValueMap.find(key) != m_StringValueMap.end(); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int MovableObject::WhilePieMenuOpenListener(const PieMenu *pieMenu) { - return RunScriptedFunctionInAppropriateScripts("WhilePieMenuOpen", false, false, { pieMenu }); -} + bool MovableObject::NumberValueExists(const std::string& key) const { + return m_NumberValueMap.find(key) != m_NumberValueMap.end(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this' and its childrens MOID status. Supposed to be done every frame. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void MovableObject::UpdateMOID(std::vector &MOIDIndex, MOID rootMOID, bool makeNewMOID) -{ - // Register the own MOID - RegMOID(MOIDIndex, rootMOID, makeNewMOID); + bool MovableObject::ObjectValueExists(const std::string& key) const { + return m_ObjectValueMap.find(key) != m_ObjectValueMap.end(); + } - // Register all the attachaed children of this, going through the class hierarchy - UpdateChildMOIDs(MOIDIndex, rootMOID, makeNewMOID); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Figure out the total MOID footstep of this and all its children combined - m_MOIDFootprint = MOIDIndex.size() - m_MOID; -} + int MovableObject::WhilePieMenuOpenListener(const PieMenu* pieMenu) { + return RunScriptedFunctionInAppropriateScripts("WhilePieMenuOpen", false, false, {pieMenu}); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this' and its childrens MOID status. Supposed to be done every frame. + void MovableObject::UpdateMOID(std::vector& MOIDIndex, MOID rootMOID, bool makeNewMOID) { + // Register the own MOID + RegMOID(MOIDIndex, rootMOID, makeNewMOID); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector - -void MovableObject::GetMOIDs(std::vector &MOIDs) const -{ - if (m_MOID != g_NoMOID) { - MOIDs.push_back(m_MOID); - } -} - -MOID MovableObject::HitWhatMOID() const -{ - return m_LastCollisionSimFrameNumber == g_MovableMan.GetSimUpdateFrameNumber() ? m_MOIDHit : g_NoMOID; -} - -void MovableObject::SetHitWhatMOID(MOID id) -{ - m_MOIDHit = id; - m_LastCollisionSimFrameNumber = g_MovableMan.GetSimUpdateFrameNumber(); -} - -long int MovableObject::HitWhatParticleUniqueID() const -{ - return m_LastCollisionSimFrameNumber == g_MovableMan.GetSimUpdateFrameNumber() ? m_ParticleUniqueIDHit : 0; -} - -void MovableObject::SetHitWhatParticleUniqueID(long int id) -{ - m_ParticleUniqueIDHit = id; - m_LastCollisionSimFrameNumber = g_MovableMan.GetSimUpdateFrameNumber(); -} + // Register all the attachaed children of this, going through the class hierarchy + UpdateChildMOIDs(MOIDIndex, rootMOID, makeNewMOID); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: RegMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself in the MOID register and get ID:s for -// itself and its children for this frame. -// BITMAP of choice. + // Figure out the total MOID footstep of this and all its children combined + m_MOIDFootprint = MOIDIndex.size() - m_MOID; + } -void MovableObject::RegMOID(std::vector &MOIDIndex, MOID rootMOID, bool makeNewMOID) { - if (!makeNewMOID && GetParent()) { - m_MOID = GetParent()->GetID(); - } else { - if (MOIDIndex.size() == g_NoMOID) { MOIDIndex.push_back(0); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector - m_MOID = MOIDIndex.size(); - MOIDIndex.push_back(this); - } + void MovableObject::GetMOIDs(std::vector& MOIDs) const { + if (m_MOID != g_NoMOID) { + MOIDs.push_back(m_MOID); + } + } - m_RootMOID = rootMOID == g_NoMOID ? m_MOID : rootMOID; -} + MOID MovableObject::HitWhatMOID() const { + return m_LastCollisionSimFrameNumber == g_MovableMan.GetSimUpdateFrameNumber() ? m_MOIDHit : g_NoMOID; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void MovableObject::SetHitWhatMOID(MOID id) { + m_MOIDHit = id; + m_LastCollisionSimFrameNumber = g_MovableMan.GetSimUpdateFrameNumber(); + } -bool MovableObject::DrawToTerrain(SLTerrain *terrain) { - if (!terrain) { - return false; + long int MovableObject::HitWhatParticleUniqueID() const { + return m_LastCollisionSimFrameNumber == g_MovableMan.GetSimUpdateFrameNumber() ? m_ParticleUniqueIDHit : 0; } - if (dynamic_cast(this)) { - auto wrappedMaskedBlit = [](BITMAP *sourceBitmap, BITMAP *destinationBitmap, const Vector &bitmapPos, bool swapSourceWithDestination) { - std::array bitmaps = { sourceBitmap, destinationBitmap }; - std::array srcPos = { - Vector(bitmapPos.GetX(), bitmapPos.GetY()), - Vector(bitmapPos.GetX() + static_cast(g_SceneMan.GetSceneWidth()), bitmapPos.GetY()), - Vector(bitmapPos.GetX() - static_cast(g_SceneMan.GetSceneWidth()), bitmapPos.GetY()), - Vector(bitmapPos.GetX(), bitmapPos.GetY() + static_cast(g_SceneMan.GetSceneHeight())), - Vector(bitmapPos.GetX(), bitmapPos.GetY() - static_cast(g_SceneMan.GetSceneHeight())) - }; - std::array destPos; - destPos.fill(Vector()); - if (swapSourceWithDestination) { - std::swap(bitmaps[0], bitmaps[1]); - std::swap(srcPos, destPos); + void MovableObject::SetHitWhatParticleUniqueID(long int id) { + m_ParticleUniqueIDHit = id; + m_LastCollisionSimFrameNumber = g_MovableMan.GetSimUpdateFrameNumber(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: RegMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes this MO register itself in the MOID register and get ID:s for + // itself and its children for this frame. + // BITMAP of choice. + + void MovableObject::RegMOID(std::vector& MOIDIndex, MOID rootMOID, bool makeNewMOID) { + if (!makeNewMOID && GetParent()) { + m_MOID = GetParent()->GetID(); + } else { + if (MOIDIndex.size() == g_NoMOID) { + MOIDIndex.push_back(0); } - masked_blit(bitmaps[0], bitmaps[1], srcPos[0].GetFloorIntX(), srcPos[0].GetFloorIntY(), destPos[0].GetFloorIntX(), destPos[0].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); - if (g_SceneMan.SceneWrapsX()) { - if (bitmapPos.GetFloorIntX() < 0) { - masked_blit(bitmaps[0], bitmaps[1], srcPos[1].GetFloorIntX(), srcPos[1].GetFloorIntY(), destPos[1].GetFloorIntX(), destPos[1].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); - } else if (bitmapPos.GetFloorIntX() + destinationBitmap->w > g_SceneMan.GetSceneWidth()) { - masked_blit(bitmaps[0], bitmaps[1], srcPos[2].GetFloorIntX(), srcPos[2].GetFloorIntY(), destPos[2].GetFloorIntX(), destPos[2].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + + m_MOID = MOIDIndex.size(); + MOIDIndex.push_back(this); + } + + m_RootMOID = rootMOID == g_NoMOID ? m_MOID : rootMOID; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool MovableObject::DrawToTerrain(SLTerrain* terrain) { + if (!terrain) { + return false; + } + if (dynamic_cast(this)) { + auto wrappedMaskedBlit = [](BITMAP* sourceBitmap, BITMAP* destinationBitmap, const Vector& bitmapPos, bool swapSourceWithDestination) { + std::array bitmaps = {sourceBitmap, destinationBitmap}; + std::array srcPos = { + Vector(bitmapPos.GetX(), bitmapPos.GetY()), + Vector(bitmapPos.GetX() + static_cast(g_SceneMan.GetSceneWidth()), bitmapPos.GetY()), + Vector(bitmapPos.GetX() - static_cast(g_SceneMan.GetSceneWidth()), bitmapPos.GetY()), + Vector(bitmapPos.GetX(), bitmapPos.GetY() + static_cast(g_SceneMan.GetSceneHeight())), + Vector(bitmapPos.GetX(), bitmapPos.GetY() - static_cast(g_SceneMan.GetSceneHeight()))}; + std::array destPos; + destPos.fill(Vector()); + + if (swapSourceWithDestination) { + std::swap(bitmaps[0], bitmaps[1]); + std::swap(srcPos, destPos); } - } - if (g_SceneMan.SceneWrapsY()) { - if (bitmapPos.GetFloorIntY() < 0) { - masked_blit(bitmaps[0], bitmaps[1], srcPos[3].GetFloorIntX(), srcPos[3].GetFloorIntY(), destPos[3].GetFloorIntX(), destPos[3].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); - } else if (bitmapPos.GetFloorIntY() + destinationBitmap->h > g_SceneMan.GetSceneHeight()) { - masked_blit(bitmaps[0], bitmaps[1], srcPos[4].GetFloorIntX(), srcPos[4].GetFloorIntY(), destPos[4].GetFloorIntX(), destPos[4].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + masked_blit(bitmaps[0], bitmaps[1], srcPos[0].GetFloorIntX(), srcPos[0].GetFloorIntY(), destPos[0].GetFloorIntX(), destPos[0].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + if (g_SceneMan.SceneWrapsX()) { + if (bitmapPos.GetFloorIntX() < 0) { + masked_blit(bitmaps[0], bitmaps[1], srcPos[1].GetFloorIntX(), srcPos[1].GetFloorIntY(), destPos[1].GetFloorIntX(), destPos[1].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + } else if (bitmapPos.GetFloorIntX() + destinationBitmap->w > g_SceneMan.GetSceneWidth()) { + masked_blit(bitmaps[0], bitmaps[1], srcPos[2].GetFloorIntX(), srcPos[2].GetFloorIntY(), destPos[2].GetFloorIntX(), destPos[2].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + } + } + if (g_SceneMan.SceneWrapsY()) { + if (bitmapPos.GetFloorIntY() < 0) { + masked_blit(bitmaps[0], bitmaps[1], srcPos[3].GetFloorIntX(), srcPos[3].GetFloorIntY(), destPos[3].GetFloorIntX(), destPos[3].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + } else if (bitmapPos.GetFloorIntY() + destinationBitmap->h > g_SceneMan.GetSceneHeight()) { + masked_blit(bitmaps[0], bitmaps[1], srcPos[4].GetFloorIntX(), srcPos[4].GetFloorIntY(), destPos[4].GetFloorIntX(), destPos[4].GetFloorIntY(), destinationBitmap->w, destinationBitmap->h); + } } + }; + BITMAP* tempBitmap = g_SceneMan.GetIntermediateBitmapForSettlingIntoTerrain(static_cast(GetDiameter())); + Vector tempBitmapPos = m_Pos.GetFloored() - Vector(static_cast(tempBitmap->w / 2), static_cast(tempBitmap->w / 2)); + + clear_bitmap(tempBitmap); + // Draw the object to the temp bitmap, then draw the foreground layer on top of it, then draw it to the foreground layer. + Draw(tempBitmap, tempBitmapPos, DrawMode::g_DrawColor, true); + wrappedMaskedBlit(terrain->GetFGColorBitmap(), tempBitmap, tempBitmapPos, false); + wrappedMaskedBlit(terrain->GetFGColorBitmap(), tempBitmap, tempBitmapPos, true); + + clear_bitmap(tempBitmap); + // Draw the object to the temp bitmap, then draw the material layer on top of it, then draw it to the material layer. + Draw(tempBitmap, tempBitmapPos, DrawMode::g_DrawMaterial, true); + wrappedMaskedBlit(terrain->GetMaterialBitmap(), tempBitmap, tempBitmapPos, false); + wrappedMaskedBlit(terrain->GetMaterialBitmap(), tempBitmap, tempBitmapPos, true); + + terrain->AddUpdatedMaterialArea(Box(tempBitmapPos, static_cast(tempBitmap->w), static_cast(tempBitmap->h))); + g_SceneMan.RegisterTerrainChange(tempBitmapPos.GetFloorIntX(), tempBitmapPos.GetFloorIntY(), tempBitmap->w, tempBitmap->h, ColorKeys::g_MaskColor, false); + } else { + Draw(terrain->GetFGColorBitmap(), Vector(), DrawMode::g_DrawColor, true); + Material const* terrMat = g_SceneMan.GetMaterialFromID(g_SceneMan.GetTerrain()->GetMaterialPixel(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY())); + if (GetMaterial()->GetPriority() > terrMat->GetPriority()) { + Draw(terrain->GetMaterialBitmap(), Vector(), DrawMode::g_DrawMaterial, true); } - }; - BITMAP *tempBitmap = g_SceneMan.GetIntermediateBitmapForSettlingIntoTerrain(static_cast(GetDiameter())); - Vector tempBitmapPos = m_Pos.GetFloored() - Vector(static_cast(tempBitmap->w / 2), static_cast(tempBitmap->w / 2)); - - clear_bitmap(tempBitmap); - // Draw the object to the temp bitmap, then draw the foreground layer on top of it, then draw it to the foreground layer. - Draw(tempBitmap, tempBitmapPos, DrawMode::g_DrawColor, true); - wrappedMaskedBlit(terrain->GetFGColorBitmap(), tempBitmap, tempBitmapPos, false); - wrappedMaskedBlit(terrain->GetFGColorBitmap(), tempBitmap, tempBitmapPos, true); - - clear_bitmap(tempBitmap); - // Draw the object to the temp bitmap, then draw the material layer on top of it, then draw it to the material layer. - Draw(tempBitmap, tempBitmapPos, DrawMode::g_DrawMaterial, true); - wrappedMaskedBlit(terrain->GetMaterialBitmap(), tempBitmap, tempBitmapPos, false); - wrappedMaskedBlit(terrain->GetMaterialBitmap(), tempBitmap, tempBitmapPos, true); - - terrain->AddUpdatedMaterialArea(Box(tempBitmapPos, static_cast(tempBitmap->w), static_cast(tempBitmap->h))); - g_SceneMan.RegisterTerrainChange(tempBitmapPos.GetFloorIntX(), tempBitmapPos.GetFloorIntY(), tempBitmap->w, tempBitmap->h, ColorKeys::g_MaskColor, false); - } else { - Draw(terrain->GetFGColorBitmap(), Vector(), DrawMode::g_DrawColor, true); - Material const *terrMat = g_SceneMan.GetMaterialFromID(g_SceneMan.GetTerrain()->GetMaterialPixel(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY())); - if (GetMaterial()->GetPriority() > terrMat->GetPriority()) { - Draw(terrain->GetMaterialBitmap(), Vector(), DrawMode::g_DrawMaterial, true); + g_SceneMan.RegisterTerrainChange(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), 1, 1, DrawMode::g_DrawColor, false); } - g_SceneMan.RegisterTerrainChange(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY(), 1, 1, DrawMode::g_DrawColor, false); + return true; } - return true; -} } // namespace RTE \ No newline at end of file diff --git a/Source/Entities/MovableObject.h b/Source/Entities/MovableObject.h index 1944228656..201093b20e 100644 --- a/Source/Entities/MovableObject.h +++ b/Source/Entities/MovableObject.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files, forward declarations, namespace stuff @@ -24,2163 +23,2091 @@ struct BITMAP; - namespace RTE { -struct HitData; - -class MOSRotating; -class PieMenu; -class SLTerrain; -class LuaStateWrapper; - -////////////////////////////////////////////////////////////////////////////////////////// -// Abstract class: MovableObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A movable object with mass. -// Parent(s): SceneObject. -// Class history: 03/18/2001 MovableObject created. - -class MovableObject : public SceneObject { - -friend class Atom; -friend struct EntityLuaBindings; - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - ScriptFunctionNames("Create", "Destroy", "Update", "ThreadedUpdate", "SyncedUpdate", "OnScriptDisable", "OnScriptEnable", "OnCollideWithTerrain", "OnCollideWithMO", "WhilePieMenuOpen", "OnSave", "OnMessage", "OnGlobalMessage"); - SerializableOverrideMethods; - ClassInfoGetters; - -enum MOType -{ - TypeGeneric = 0, - TypeActor, - TypeHeldDevice, - TypeThrownDevice -}; - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: MovableObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a MovableObject object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - MovableObject() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~MovableObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a MovableObject object before deletion -// from system memory. -// Arguments: None. - - ~MovableObject() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MovableObject object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MovableObject object ready for use. -// Arguments: A float specifying the object's mass in Kilograms (kg). -// A Vector specifying the initial position. -// A Vector specifying the initial velocity. -// The rotation angle in r. -// The angular velocity in r/s. -// The amount of time in ms this MovableObject will exist. 0 means unlim. -// Whether or not this MO will collide with other MO's while travelling. -// Whether or not this MO be collided with bt other MO's during their travel. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(float mass, const Vector &position = Vector(0, 0), const Vector &velocity = Vector(0, 0), float rotAngle = 0, float angleVel = 0, unsigned long lifetime = 0, bool hitMOs = true, bool getHitByMOs = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MovableObject to be identical to another, by deep copy. -// Arguments: A reference to the MovableObject to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const MovableObject &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire MovableObject, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); SceneObject::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MovableObject object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; + struct HitData; + + class MOSRotating; + class PieMenu; + class SLTerrain; + class LuaStateWrapper; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Abstract class: MovableObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A movable object with mass. + // Parent(s): SceneObject. + // Class history: 03/18/2001 MovableObject created. + + class MovableObject : public SceneObject { + + friend class Atom; + friend struct EntityLuaBindings; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + ScriptFunctionNames("Create", "Destroy", "Update", "ThreadedUpdate", "SyncedUpdate", "OnScriptDisable", "OnScriptEnable", "OnCollideWithTerrain", "OnCollideWithMO", "WhilePieMenuOpen", "OnSave", "OnMessage", "OnGlobalMessage"); + SerializableOverrideMethods; + ClassInfoGetters; + + enum MOType { + TypeGeneric = 0, + TypeActor, + TypeHeldDevice, + TypeThrownDevice + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: MovableObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a MovableObject object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + MovableObject() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~MovableObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a MovableObject object before deletion + // from system memory. + // Arguments: None. + + ~MovableObject() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MovableObject object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MovableObject object ready for use. + // Arguments: A float specifying the object's mass in Kilograms (kg). + // A Vector specifying the initial position. + // A Vector specifying the initial velocity. + // The rotation angle in r. + // The angular velocity in r/s. + // The amount of time in ms this MovableObject will exist. 0 means unlim. + // Whether or not this MO will collide with other MO's while travelling. + // Whether or not this MO be collided with bt other MO's during their travel. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(float mass, const Vector& position = Vector(0, 0), const Vector& velocity = Vector(0, 0), float rotAngle = 0, float angleVel = 0, unsigned long lifetime = 0, bool hitMOs = true, bool getHitByMOs = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MovableObject to be identical to another, by deep copy. + // Arguments: A reference to the MovableObject to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const MovableObject& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire MovableObject, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + SceneObject::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the MovableObject object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; #pragma region Script Handling - /// - /// Loads the script at the given script path onto the object, checking for appropriately named functions within it. - /// If the script contains a Create function and this MO's scripts are running, the Create function will be run immediately. - /// - /// The path to the script to load. - /// Whether or not the script should load as enabled. Defaults to true. - /// 0 on success. -1 if scriptPath is empty. -2 if the script is already loaded. -3 if setup to load the script or modify the global lua state fails. -4 if the script fails to load. - virtual int LoadScript(const std::string &scriptPath, bool loadAsEnabledScript = true); - - /// - /// Reloads the all of the scripts on this object. This will also reload scripts for the original preset in PresetMan so future objects spawned will use the new scripts. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int ReloadScripts() final; - - /// - /// Gets whether or not the object has a script name, and there were no errors when initializing its Lua scripts. If there were, the object would need to be reloaded. - /// - /// Whether or not the object's scripts have been successfully initialized. - bool ObjectScriptsInitialized() const { return !m_ScriptObjectName.empty() && m_ScriptObjectName != "ERROR"; } - - /// - /// Checks if this MO has any scripts on it. - /// - /// Whether or not this MO has any scripts on it. - bool HasAnyScripts() const { return !m_AllLoadedScripts.empty(); } - - /// - /// Checks if the script at the given path is one of the scripts on this MO. - /// - /// The path to the script to check. - /// Whether or not the script is on this MO. - bool HasScript(const std::string &scriptPath) const { return m_AllLoadedScripts.find(scriptPath) != m_AllLoadedScripts.end(); } - - /// - /// Checks if the script at the given path is one of the enabled scripts on this MO. - /// - /// The path to the script to check. - /// Whether or not the script is enabled on this MO. - bool ScriptEnabled(const std::string &scriptPath) const { auto scriptPathIterator = m_AllLoadedScripts.find(scriptPath); return scriptPathIterator != m_AllLoadedScripts.end() && scriptPathIterator->second == true; } - - /// - /// Enables or dsiableds the script at the given path on this MO. - /// - /// The path to the script to enable or disable - /// Whether to enable the script, or disable it. - /// Whether or not the script was successfully eanbled/disabled. - bool EnableOrDisableScript(const std::string &scriptPath, bool enableScript); - - /// - /// Enables or disables all scripts on this MovableObject. - /// - /// Whether to enable (true) or disable (false) all scripts on this MovableObject. - void EnableOrDisableAllScripts(bool enableScripts); - - /// - /// Runs the given function in all scripts that have it, with the given arguments, with the ability to not run on disabled scripts and to cease running if there's an error. - /// The first argument to the function will always be 'self'. If either argument list is not empty, its entries will be passed into the Lua function in order, with entity arguments first. - /// - /// The name of the function to run. - /// Whether to run the function on disabled scripts. Defaults to false. - /// Whether to stop if there's an error running any script, or simply print it to the console and continue. Defaults to false. - /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. - /// Optional vector of strings, that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int RunScriptedFunctionInAppropriateScripts(const std::string &functionName, bool runOnDisabledScripts = false, bool stopOnError = false, const std::vector &functionEntityArguments = std::vector(), const std::vector &functionLiteralArguments = std::vector(), const std::vector &functionObjectArguments = std::vector()); - - /// - /// Cleans up and destroys the script state of this object, calling the Destroy callback in lua - /// - virtual void DestroyScriptState(); + /// + /// Loads the script at the given script path onto the object, checking for appropriately named functions within it. + /// If the script contains a Create function and this MO's scripts are running, the Create function will be run immediately. + /// + /// The path to the script to load. + /// Whether or not the script should load as enabled. Defaults to true. + /// 0 on success. -1 if scriptPath is empty. -2 if the script is already loaded. -3 if setup to load the script or modify the global lua state fails. -4 if the script fails to load. + virtual int LoadScript(const std::string& scriptPath, bool loadAsEnabledScript = true); + + /// + /// Reloads the all of the scripts on this object. This will also reload scripts for the original preset in PresetMan so future objects spawned will use the new scripts. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int ReloadScripts() final; + + /// + /// Gets whether or not the object has a script name, and there were no errors when initializing its Lua scripts. If there were, the object would need to be reloaded. + /// + /// Whether or not the object's scripts have been successfully initialized. + bool ObjectScriptsInitialized() const { return !m_ScriptObjectName.empty() && m_ScriptObjectName != "ERROR"; } + + /// + /// Checks if this MO has any scripts on it. + /// + /// Whether or not this MO has any scripts on it. + bool HasAnyScripts() const { return !m_AllLoadedScripts.empty(); } + + /// + /// Checks if the script at the given path is one of the scripts on this MO. + /// + /// The path to the script to check. + /// Whether or not the script is on this MO. + bool HasScript(const std::string& scriptPath) const { return m_AllLoadedScripts.find(scriptPath) != m_AllLoadedScripts.end(); } + + /// + /// Checks if the script at the given path is one of the enabled scripts on this MO. + /// + /// The path to the script to check. + /// Whether or not the script is enabled on this MO. + bool ScriptEnabled(const std::string& scriptPath) const { + auto scriptPathIterator = m_AllLoadedScripts.find(scriptPath); + return scriptPathIterator != m_AllLoadedScripts.end() && scriptPathIterator->second == true; + } + + /// + /// Enables or dsiableds the script at the given path on this MO. + /// + /// The path to the script to enable or disable + /// Whether to enable the script, or disable it. + /// Whether or not the script was successfully eanbled/disabled. + bool EnableOrDisableScript(const std::string& scriptPath, bool enableScript); + + /// + /// Enables or disables all scripts on this MovableObject. + /// + /// Whether to enable (true) or disable (false) all scripts on this MovableObject. + void EnableOrDisableAllScripts(bool enableScripts); + + /// + /// Runs the given function in all scripts that have it, with the given arguments, with the ability to not run on disabled scripts and to cease running if there's an error. + /// The first argument to the function will always be 'self'. If either argument list is not empty, its entries will be passed into the Lua function in order, with entity arguments first. + /// + /// The name of the function to run. + /// Whether to run the function on disabled scripts. Defaults to false. + /// Whether to stop if there's an error running any script, or simply print it to the console and continue. Defaults to false. + /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. + /// Optional vector of strings, that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int RunScriptedFunctionInAppropriateScripts(const std::string& functionName, bool runOnDisabledScripts = false, bool stopOnError = false, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); + + /// + /// Cleans up and destroys the script state of this object, calling the Destroy callback in lua + /// + virtual void DestroyScriptState(); #pragma endregion + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMOType + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the MO type code of this MO. Either Actor, Item, or Generic. + // Arguments: None. + // Return value: An int describing the MO Type code of this MovableObject. + + int GetMOType() const { return m_MOType; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMass + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the mass value of this MovableObject. + // Arguments: None. + // Return value: A float describing the mass value in Kilograms (kg). + + virtual float GetMass() const { return m_Mass; } + + /// + /// Gets the previous position vector of this MovableObject, prior to this frame. + /// + /// A Vector describing the previous position vector. + const Vector& GetPrevPos() const { return m_PrevPos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the velocity vector of this MovableObject. + // Arguments: None. + // Return value: A Vector describing the current velocity vector. + + const Vector& GetVel() const { return m_Vel; } + + /// + /// Gets the previous velocity vector of this MovableObject, prior to this frame. + /// + /// A Vector describing the previous velocity vector. + const Vector& GetPrevVel() const { return m_PrevVel; } + + /// + /// Gets the amount of distance this MO has travelled since its creation, in pixels. + /// + /// The amount of distance this MO has travelled, in pixels. + float GetDistanceTravelled() const { return m_DistanceTravelled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAngularVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current angular velocity of this MovableObject. Positive is + // a counter-clockwise rotation. + // Arguments: None. + // Return value: The angular velocity in radians per second. + + virtual float GetAngularVel() const { return 0.0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRadius + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the largest radius of this in pixels. + // Arguments: None. + // Return value: The radius from its center to the edge of its graphical representation. + + virtual float GetRadius() const { return 1.0f; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetDiameter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the largest diameter of this in pixels. + // Arguments: None. + // Return value: The largest diameter across its graphical representation. + + virtual float GetDiameter() const { return 2.0F; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetScale + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current scale of this MOSRotating. This is mostly for fun. + // Arguments: None. + // Return value: The normalized scale. + + float GetScale() const { return m_Scale; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGlobalAccScalar + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets How this is affected by global effects, from +1.0 to -1.0. + // Something with a negative value will 'float' upward. + // Arguments: None. + // Return value: The global acceleration scalar. + + float GetGlobalAccScalar() const { return m_GlobalAccScalar; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetGlobalAccScalar + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets How this is affected by global effects, from +1.0 to -1.0. + // Something with a negative value will 'float' upward. + // Arguments: The global acceleration scalar. + // Return value: None. + + void SetGlobalAccScalar(float newValue) { m_GlobalAccScalar = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAirResistance + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: How much this is affected by air resistance when traveling over a + // second, 0 to 1.0, with 0 as default + // Arguments: None. + // Return value: The air resistance coefficient. + + float GetAirResistance() const { return m_AirResistance; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetAirResistance + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets how much this is affected by air resistance when traveling over a + // second, 0 to 1.0, with 0 as default + // Arguments: The air resistance coefficient. + // Return value: None. + + void SetAirResistance(float newValue) { m_AirResistance = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAirThreshold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: At which threshold of velocity, in m/s, the effect of AirResistance + // kicks in. + // Arguments: None. + // Return value: The air threshold speed. + + float GetAirThreshold() const { return m_AirThreshold; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetAirThreshold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets at which threshold of velocity, in m/s, the effect of AirResistance + // kicks in. + // Arguments: The air threshold speed. + // Return value: None. + + void SetAirThreshold(float newValue) { m_AirThreshold = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAge + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets real time age of this MovableObject. + // Arguments: None. + // Return value: A unsigned long describing the current age in ms. + + unsigned long GetAge() const { return m_AgeTimer.GetElapsedSimTimeMS(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLifetime + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the amount of time this MovableObject will exist from creation. + // Arguments: None. + // Return value: A unsigned long describing the current lifetime in ms. 0 means unlimited. + + unsigned long GetLifetime() const { return m_Lifetime; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the MOID of this MovableObject for this frame. + // Arguments: None. + // Return value: An int specifying the MOID that this MovableObject is + // assigned for the current frame only. + + MOID GetID() const { return m_MOID; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRootID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the MOID of the MovableObject which is the root MO of this MO for + // this frame. If same as what GetID returns, then this is owned by + // MovableMan. + // Arguments: None. + // Return value: An int specifying the MOID of the MO that this MovableObject + // is owned by for the current frame only. + + MOID GetRootID() const { return m_RootMOID; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMOIDFootprint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how many total (subsequent) MOID's this MO and all its children + // are taking up this frame. ie if this MO has no children, this will + // likely be 1. Note this is only valid for this frame! + // Arguments: None. + // Return value: The number of MOID indices this MO and all its children are taking up. + + int GetMOIDFootprint() const { return m_MOIDFootprint; } + + /// + /// Returns whether or not this MovableObject has ever been added to MovableMan. Does not account for removal from MovableMan. + /// + /// Whether or not this MovableObject has ever been added to MovableMan. + bool HasEverBeenAddedToMovableMan() const { return m_HasEverBeenAddedToMovableMan; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetSharpness + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the sharpness factor of this MO. + // Arguments: None. + // Return value: The sharpness factor of this MO. 1.0 means normal sharpness, no alter- + // ation to any of the impulses. + + float GetSharpness() const { return m_Sharpness; } + + /// + /// Placeholder method to allow for ease of use with Attachables. Returns nullptr for classes that aren't derived from Attachable. + /// + /// Nothing. + virtual MOSRotating* GetParent() { return nullptr; } + + /// + /// Placeholder method to allow for ease of use with Attachables. Returns nullptr for classes that aren't derived from Attachable. + /// + /// Nothing. + virtual const MOSRotating* GetParent() const { return nullptr; } + + /// + /// Returns a pointer to this MO, this is to enable Attachables to get their root nodes. + /// + /// A pointer to this MovableObject. + virtual MovableObject* GetRootParent() { return this; } + + /// + /// Returns a pointer to this MO, this is to enable Attachables to get their root nodes. + /// + /// A pointer to this MovableObject. + virtual const MovableObject* GetRootParent() const { return this; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAltitude + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the altitide of this' pos (or appropriate low point) over the + // terrain, in pixels. + // Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. + // The accuracy within which measurement is acceptable. Higher number + // here means less calculation. + // Return value: The rough altitude over the terrain, in pixels. + + virtual float GetAltitude(int max = 0, int accuracy = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetAboveHUDPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absoltue position of the top of this' HUD stack. + // Arguments: None. + // Return value: A Vector with the absolute position of this' HUD stack top point. + + virtual Vector GetAboveHUDPos() const { return m_Pos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IntersectionWarning + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this may have started to intersect the terrain since the + // last frame, e g due to flipping. + // Arguments: None. + // Return value: Whether this may have started to intersect the terrain since last frame. + + bool IntersectionWarning() const { return m_CheckTerrIntersection; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HitsMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets whether this MovableObject is set to collide with other + // MovableObject:s during its travel. + // Arguments: None. + // Return value: Whether this hits other MO's during its travel, or not. + + bool HitsMOs() const { return m_HitsMOs; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetsHitByMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets whether this MovableObject is set to be able to get hit by other + // MovableObject:s during their travel. + // Arguments: None. + // Return value: Whether this can get hit by MO's, or not. + + bool GetsHitByMOs() const { return m_GetsHitByMOs; } + + /// + /// Sets the team of this MovableObject. + /// + /// The new team to assign. + void SetTeam(int team) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetIgnoresTeamHits + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this will collide with any other MO of the same team. + // Arguments: Whether this can hit or get hit by other MOs of the same team. + // Return value: None. + + void SetIgnoresTeamHits(bool ignoreTeam = true) { m_IgnoresTeamHits = ignoreTeam; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IgnoresTeamHits + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether this will collide with any other MO of the same team. + // Arguments: None. + // Return value: Whether this can hit or get hit by other MOs of the same team. + + bool IgnoresTeamHits() const { return m_IgnoresTeamHits; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IgnoresWhichTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells which team this would be ignoring hits with, if we're ignoring + // hits at all. + // Arguments: None. + // Return value: Which team this ignores hits with, if any. + + int IgnoresWhichTeam() const { return m_IgnoresTeamHits ? m_Team : Activity::NoTeam; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetIgnoresAtomGroupHits + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this will collide with any other MO that uses an + // AtomGroup as a physical representation. This also overrides the + // IgnoresAGHitsWhenSlowerThan property. + // Arguments: Whether this can hit or get hit by other MOs which use AGs. + // Return value: None. + + void SetIgnoresAtomGroupHits(bool ignoreAG = true) { + m_IgnoresAtomGroupHits = ignoreAG; + if (ignoreAG) + m_IgnoresAGHitsWhenSlowerThan = -1; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IgnoresAtomGroupHits + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether this will collide with any MO that uses an AtomGroup + // as physical representation. (as opposed to single-atom ones) + // Arguments: None. + // Return value: Whether this can hit or get hit by other MOs that use AGs. + + bool IgnoresAtomGroupHits() const { return m_IgnoresAtomGroupHits; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IgnoreTerrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether this will collide with any Terrain + // Arguments: None. + // Return value: Whether this can hit terrain. + + bool IgnoreTerrain() const { return m_IgnoreTerrain; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetIgnoreTerrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this will collide with any Terrain + // Arguments: Whether this can hit terrain. + // Return value: None. + + void SetIgnoreTerrain(bool ignores) { m_IgnoreTerrain = ignores; } + + /// + /// Gets whether this MO ignores collisions with actors. + /// + /// Whether this MO ignores collisions with actors. + bool GetIgnoresActorHits() const { return m_IgnoresActorHits; } + + /// + /// Sets whether this MO ignores collisions with actors. + /// + /// Whether this MO will ignore collisions with actors. + void SetIgnoresActorHits(bool value) { m_IgnoresActorHits = value; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: GetMaterial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the main material of this MovableObject. + // Arguments: None. + // Return value: The the material of this MovableObject. + + virtual Material const* GetMaterial() const = 0; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: GetDrawPriority + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the drawing priority of this MovableObject, if two things were + // overlap when copying to the terrain, the higher priority MO would + // end up getting drawn. + // Arguments: None. + // Return value: The the priority of this MovableObject. Higher number, the higher + // priority. + + virtual int GetDrawPriority() const = 0; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetScreenEffect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the screen effect this has loaded, which can be applied to post + // rendering. Ownership is NOT transferred! + // Arguments: None. + // Return value: The 32bpp screen effect BITMAP. Ownership is NOT transferred! + + BITMAP* GetScreenEffect() const { return m_pScreenEffect; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetScreenEffectHash + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the hash of the path of this object's screen effect file. Used to + // transfer glow effects over network. The hash itself is calculated during + // load. + // Arguments: None. + // Return value: This effect's unique hash. + + size_t GetScreenEffectHash() const { return m_ScreenEffectHash; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetMass + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the mass of this MovableObject. + // Arguments: A float specifying the new mass value in Kilograms (kg). + // Return value: None. + + virtual void SetMass(const float newMass) { m_Mass = newMass; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPrevPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the position at the start of the sim update. + // Arguments: A Vector specifying the new 'prev' pos. + // Return value: None. + + void SetPrevPos(const Vector& newPrevPos) { m_PrevPos = newPrevPos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the velocity vector of this MovableObject. + // Arguments: A Vector specifying the new velocity vector. + // Return value: None. + + void SetVel(const Vector& newVel) { m_Vel = newVel; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetRotAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current absolute angle of rotation of this MovableObject. + // Arguments: The new absolute angle in radians. + // Return value: None. + + void SetRotAngle(float newAngle) override {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetEffectRotAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current absolute angle of rotation of this MovableObject's effect. + // Arguments: The new absolute angle in radians. + // Return value: None. + + void SetEffectRotAngle(float newAngle) { m_EffectRotAngle = newAngle; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetEffectRotAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current absolute angle of rotation of this MovableObject's effect. + // Arguments: None. + // Return value: The absolute angle in radians. + + float GetEffectRotAngle() const { return m_EffectRotAngle; } + + /// + /// Gets the starting strength of this MovableObject's effect. + /// + /// The starting strength of the effect, 0-255. + int GetEffectStartStrength() const { return m_EffectStartStrength; } + + /// + /// Gets the stopping strength of this MovableObject's effect. + /// + /// The stopping strength of the effect, 0-255. + int GetEffectStopStrength() const { return m_EffectStopStrength; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetAngularVel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current angular velocity of this MovableObject. Positive is + // a counter clockwise rotation. + // Arguments: The new angular velocity in radians per second. + // Return value: None. + + virtual void SetAngularVel(float newRotVel) {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetScale + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current scale of this MOSRotating. This is mostly for fun. + // Arguments: The new normalized scale. + // Return value: None. + + void SetScale(float newScale) { m_Scale = newScale; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLifetime + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the amount of time this MovableObject will exist. + // Arguments: A unsigned long specifying amount of time in ms. 0 means unlimited life. + // Return value: None. + + void SetLifetime(const int newLifetime = 0) { m_Lifetime = newLifetime; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetAge + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this' age timer to a specific value, in ms. + // Arguments: The new age of this, in MS. + // Return value: None. + + void SetAge(double newAge = 0) { m_AgeTimer.SetElapsedSimTimeMS(newAge); } + + /// + /// Sets the MOID of this MovableObject to be g_NoMOID (255) for this frame. + /// + virtual void SetAsNoID() { m_MOID = g_NoMOID; } + + /// + /// Sets this MovableObject as having been added to MovableMan. Should only really be done in MovableMan::Add/Remove Actor/Item/Particle. + /// + /// Whether or not this MovableObject has been added to MovableMan. + void SetAsAddedToMovableMan(bool addedToMovableMan = true) { + if (addedToMovableMan) { + m_HasEverBeenAddedToMovableMan = true; + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetSharpness + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the sharpness factor of this MO. + // Arguments: The sharpness factor of this MO. 1.0 means normal sharpness, no alter- + // ation to any of the impulses. + // Return value: None. + + void SetSharpness(const float sharpness) { m_Sharpness = sharpness; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetToHitMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this MovableObject to collide with other MovableObjects during + // travel. + // Arguments: Whether to hit other MO's during travel, or not. + // Return value: None. + + void SetToHitMOs(bool hitMOs = true) { m_HitsMOs = hitMOs; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetToGetHitByMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this MovableObject to be able to be collided with by other + // MovableObjects during their travel. + // Arguments: Whether this should get hit by other MO's during travel, or not. + // Return value: None. + + void SetToGetHitByMOs(bool getHitByMOs = true) { m_GetsHitByMOs = getHitByMOs; } + + /// + /// Gets the MO this MO is set not to hit even when MO hitting is enabled on this MO. + /// + /// The MO this MO is set not to hit. + const MovableObject* GetWhichMOToNotHit() const { return m_pMOToNotHit; } + + /// + /// Sets this MO to not hit a specific other MO and all its children even when MO hitting is enabled on this MO. + /// + /// A pointer to the MO to not be hitting. Null pointer means don't ignore anyhting. Ownership is NOT transferred! + /// How long, in seconds, to ignore the specified MO. A negative number means forever. + virtual void SetWhichMOToNotHit(MovableObject* moToNotHit = nullptr, float forHowLong = -1) { + m_pMOToNotHit = moToNotHit; + m_MOIgnoreTimer.Reset(); + m_MOIgnoreTimer.SetSimTimeLimitS(forHowLong); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetWrapDoubleDrawing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Enables or disables double drawing of this across wrapping seams. + // Arguments: Wheter to enable or not. + // Return value: None. + + void SetWrapDoubleDrawing(bool wrapDraw = true) { m_WrapDoubleDraw = wrapDraw; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetToSettle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Marks this MovableObject for settling onto the terrain at the end of + // the MovableMan update. + // Arguments: Whether to mark this MO for settling or not. + // Return value: None. + + void SetToSettle(bool toSettle = true) { m_ToSettle = toSettle; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetToDelete + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Marks this MovableObject for deletion at the end of the MovableMan + // update. + // Arguments: Whether to mark this MO for deletion or not. + // Return value: None. + + void SetToDelete(bool toDelete = true) { m_ToDelete = toDelete; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsSetToDelete + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells if this MovableObject is marked for deletion at the end of the + // update. + // Arguments: None. + // Return value: Whether this is marked for deletion or not. + + bool IsSetToDelete() const { return m_ToDelete; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsMissionCritical + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is mission critical and should therefore NEVER be + // settled or otherwise destroyed during teh course of a mission. + // Arguments: None. + // Return value: Whetehr this should be immune to settling and destruction. + + bool IsMissionCritical() const { return m_MissionCritical; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMissionCritical + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this is mission critical and should therefore NEVER be + // settled or otherwise destroyed during teh course of a mission. + // Arguments: Whether this should be immune to settling and destruction. + // Return value: None. + + void SetMissionCritical(bool missionCritical) { m_MissionCritical = missionCritical; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CanBeSquished + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this can be squished by getting pushed into the ground. + // Arguments: None. + // Return value: Whetehr this should be immune to squishing or not. + + bool CanBeSquished() const { return m_CanBeSquished; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetHUDVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether this Actor's HUD is drawn or not. + // Arguments: None. + // Return value: Whether this' HUD gets drawn or not. + + void SetHUDVisible(bool visible) { m_HUDVisible = visible; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetHUDVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether this Actor's HUD is drawn or not. + // Arguments: None. + // Return value: Whether this' HUD gets drawn or not. + + bool GetHUDVisible() const { return m_HUDVisible; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsTooFast + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is moving or rotating stupidly fast in a way + // that will screw up the simulation. + // Arguments: None. + // Return value: Whether this is either moving or rotating too fast. + + virtual bool IsTooFast() const { return m_Vel.MagnitudeIsGreaterThan(500.0F); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: FixTooFast + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Slows the speed of anyhting that is deemed to be too fast to within + // acceptable rates. + // Arguments: None. + // Return value: None. + + virtual void FixTooFast() { + if (IsTooFast()) { + m_Vel.SetMagnitude(450.0F); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsGeneric + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is an Generic or not. + // Arguments: None. + // Return value: Whether this MovableObject is of Type Generic or not. + + bool IsGeneric() const { return m_MOType == TypeGeneric; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is an Actor or not. + // Arguments: None. + // Return value: Whether this MovableObject is of Type Actor or not. + + bool IsActor() const { return m_MOType == TypeActor; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is a Device or not. + // Arguments: None. + // Return value: Whether this MovableObject is of Type Device (Held or Thrown) or not. + + bool IsDevice() const { return m_MOType == TypeHeldDevice || m_MOType == TypeThrownDevice; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsHeldDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is a HeldDevice or not. + // Arguments: None. + // Return value: Whether this MovableObject is of Type HeldDevice or not. + + // LEGACY CRAP + bool IsHeldDevice() const { return m_MOType == TypeHeldDevice || m_MOType == TypeThrownDevice; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsThrownDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is a ThrownDevice or not. + // Arguments: None. + // Return value: Whether this MovableObject is of Type ThrownDevice or not. + + bool IsThrownDevice() const { return m_MOType == TypeThrownDevice; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsGold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is made of Gold or not. + // Arguments: None. + // Return value: Whether this MovableObject is of Gold or not. + + bool IsGold() const { return m_MOType == TypeGeneric && GetMaterial()->GetIndex() == c_GoldMaterialID; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsDrawnAfterParent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MovableObject is to be drawn after + // (in front of) or before (behind) the parent. + // Arguments: None. + // Return value: Whether it's to be drawn after parent or not. + + virtual bool IsDrawnAfterParent() const { return true; } + + /// + /// Whether a set of X, Y coordinates overlap us (in world space). + /// + /// The given X coordinate, in world space. + /// The given Y coordinate, in world space. + /// Whether the given coordinate overlap us. + virtual bool HitTestAtPixel(int pixelX, int pixelY) const { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically named object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. + // Arguments: The Preset name of the object to look for. + // Return value: Whetehr the object was found carried by this. + + virtual bool HasObject(std::string objectName) const { return m_PresetName == objectName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasObjectInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is or carries a specifically grouped object in its + // inventory. Also looks through the inventories of potential passengers, + // as applicable. + // Arguments: The name of the group to look for. + // Return value: Whetehr the object in the group was found carried by this. + + virtual bool HasObjectInGroup(std::string groupName) const { return const_cast(this)->IsInGroup(groupName); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds force to this MovableObject for the next time Update() is called. + // Arguments: An Vector with the external force vector that will be added to this + // MovableObject and affect its path next Update(). In N or kg * m/s^2. + // A Vector with the offset, in METERS, of where the force is being + // applied relative to the center of this MovableObject. + // Return value: None.A + + void AddForce(const Vector& force, const Vector& offset = Vector()) { m_Forces.push_back(std::make_pair(force, offset)); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddAbsForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds force to this MovableObject for the next time Update() is called. + // Arguments: An Vector with the external force vector that will be added to this + // MovableObject and affect its path next Update(). In N or kg * m/s^2. + // A Vector with the absolute world coordinates, in PIXELS, of where the + // force is being applied to the center of this MovableObject. + // Return value: None. + + void AddAbsForce(const Vector& force, const Vector& absPos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddImpulseForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds impulse force (or instant momentum) to this MovableObject for + // the next time Update() is called. + // Arguments: An Vector with the impulse force vector that will directly be added + // to this MovableObject's momentum next Update(). In kg * m/s. + // A Vector with the offset, in METERS, of where the impulse is being + // applied relative to the center of this MovableObject. + // Return value: None. + + void AddImpulseForce(const Vector& impulse, const Vector& offset = Vector()) { -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOType -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the MO type code of this MO. Either Actor, Item, or Generic. -// Arguments: None. -// Return value: An int describing the MO Type code of this MovableObject. - - int GetMOType() const { return m_MOType; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this MovableObject. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). - - virtual float GetMass() const { return m_Mass; } - - - /// - /// Gets the previous position vector of this MovableObject, prior to this frame. - /// - /// A Vector describing the previous position vector. - const Vector & GetPrevPos() const { return m_PrevPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the velocity vector of this MovableObject. -// Arguments: None. -// Return value: A Vector describing the current velocity vector. - - const Vector & GetVel() const { return m_Vel; } - - - /// - /// Gets the previous velocity vector of this MovableObject, prior to this frame. - /// - /// A Vector describing the previous velocity vector. - const Vector & GetPrevVel() const { return m_PrevVel; } - - /// - /// Gets the amount of distance this MO has travelled since its creation, in pixels. - /// - /// The amount of distance this MO has travelled, in pixels. - float GetDistanceTravelled() const { return m_DistanceTravelled; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAngularVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current angular velocity of this MovableObject. Positive is -// a counter-clockwise rotation. -// Arguments: None. -// Return value: The angular velocity in radians per second. - - virtual float GetAngularVel() const { return 0.0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetRadius -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the largest radius of this in pixels. -// Arguments: None. -// Return value: The radius from its center to the edge of its graphical representation. - - virtual float GetRadius() const { return 1.0f; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetDiameter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the largest diameter of this in pixels. -// Arguments: None. -// Return value: The largest diameter across its graphical representation. - - virtual float GetDiameter() const { return 2.0F; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetScale -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current scale of this MOSRotating. This is mostly for fun. -// Arguments: None. -// Return value: The normalized scale. - - float GetScale() const { return m_Scale; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGlobalAccScalar -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets How this is affected by global effects, from +1.0 to -1.0. -// Something with a negative value will 'float' upward. -// Arguments: None. -// Return value: The global acceleration scalar. - - float GetGlobalAccScalar() const { return m_GlobalAccScalar; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetGlobalAccScalar -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets How this is affected by global effects, from +1.0 to -1.0. -// Something with a negative value will 'float' upward. -// Arguments: The global acceleration scalar. -// Return value: None. - - void SetGlobalAccScalar(float newValue) { m_GlobalAccScalar = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAirResistance -////////////////////////////////////////////////////////////////////////////////////////// -// Description: How much this is affected by air resistance when traveling over a -// second, 0 to 1.0, with 0 as default -// Arguments: None. -// Return value: The air resistance coefficient. - - float GetAirResistance() const { return m_AirResistance; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAirResistance -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets how much this is affected by air resistance when traveling over a -// second, 0 to 1.0, with 0 as default -// Arguments: The air resistance coefficient. -// Return value: None. - - void SetAirResistance(float newValue) { m_AirResistance = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAirThreshold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: At which threshold of velocity, in m/s, the effect of AirResistance -// kicks in. -// Arguments: None. -// Return value: The air threshold speed. - - float GetAirThreshold() const { return m_AirThreshold; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAirThreshold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets at which threshold of velocity, in m/s, the effect of AirResistance -// kicks in. -// Arguments: The air threshold speed. -// Return value: None. - - void SetAirThreshold(float newValue) { m_AirThreshold = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAge -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets real time age of this MovableObject. -// Arguments: None. -// Return value: A unsigned long describing the current age in ms. - - unsigned long GetAge() const { return m_AgeTimer.GetElapsedSimTimeMS(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLifetime -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the amount of time this MovableObject will exist from creation. -// Arguments: None. -// Return value: A unsigned long describing the current lifetime in ms. 0 means unlimited. - - unsigned long GetLifetime() const { return m_Lifetime; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the MOID of this MovableObject for this frame. -// Arguments: None. -// Return value: An int specifying the MOID that this MovableObject is -// assigned for the current frame only. - - MOID GetID() const { return m_MOID; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRootID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the MOID of the MovableObject which is the root MO of this MO for -// this frame. If same as what GetID returns, then this is owned by -// MovableMan. -// Arguments: None. -// Return value: An int specifying the MOID of the MO that this MovableObject -// is owned by for the current frame only. - - MOID GetRootID() const { return m_RootMOID; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMOIDFootprint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how many total (subsequent) MOID's this MO and all its children -// are taking up this frame. ie if this MO has no children, this will -// likely be 1. Note this is only valid for this frame! -// Arguments: None. -// Return value: The number of MOID indices this MO and all its children are taking up. - - int GetMOIDFootprint() const { return m_MOIDFootprint; } - - /// - /// Returns whether or not this MovableObject has ever been added to MovableMan. Does not account for removal from MovableMan. - /// - /// Whether or not this MovableObject has ever been added to MovableMan. - bool HasEverBeenAddedToMovableMan() const { return m_HasEverBeenAddedToMovableMan; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetSharpness -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the sharpness factor of this MO. -// Arguments: None. -// Return value: The sharpness factor of this MO. 1.0 means normal sharpness, no alter- -// ation to any of the impulses. - - float GetSharpness() const { return m_Sharpness; } - - - /// - /// Placeholder method to allow for ease of use with Attachables. Returns nullptr for classes that aren't derived from Attachable. - /// - /// Nothing. - virtual MOSRotating * GetParent() { return nullptr; } - - /// - /// Placeholder method to allow for ease of use with Attachables. Returns nullptr for classes that aren't derived from Attachable. - /// - /// Nothing. - virtual const MOSRotating * GetParent() const { return nullptr; } - - /// - /// Returns a pointer to this MO, this is to enable Attachables to get their root nodes. - /// - /// A pointer to this MovableObject. - virtual MovableObject * GetRootParent() { return this; } - - /// - /// Returns a pointer to this MO, this is to enable Attachables to get their root nodes. - /// - /// A pointer to this MovableObject. - virtual const MovableObject * GetRootParent() const { return this; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAltitude -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the altitide of this' pos (or appropriate low point) over the -// terrain, in pixels. -// Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. -// The accuracy within which measurement is acceptable. Higher number -// here means less calculation. -// Return value: The rough altitude over the terrain, in pixels. - - virtual float GetAltitude(int max = 0, int accuracy = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetAboveHUDPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absoltue position of the top of this' HUD stack. -// Arguments: None. -// Return value: A Vector with the absolute position of this' HUD stack top point. - - virtual Vector GetAboveHUDPos() const { return m_Pos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IntersectionWarning -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this may have started to intersect the terrain since the -// last frame, e g due to flipping. -// Arguments: None. -// Return value: Whether this may have started to intersect the terrain since last frame. - - bool IntersectionWarning() const { return m_CheckTerrIntersection; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HitsMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets whether this MovableObject is set to collide with other -// MovableObject:s during its travel. -// Arguments: None. -// Return value: Whether this hits other MO's during its travel, or not. - - bool HitsMOs() const { return m_HitsMOs; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetsHitByMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets whether this MovableObject is set to be able to get hit by other -// MovableObject:s during their travel. -// Arguments: None. -// Return value: Whether this can get hit by MO's, or not. - - bool GetsHitByMOs() const { return m_GetsHitByMOs; } - - /// - /// Sets the team of this MovableObject. - /// - /// The new team to assign. - void SetTeam(int team) override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetIgnoresTeamHits -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this will collide with any other MO of the same team. -// Arguments: Whether this can hit or get hit by other MOs of the same team. -// Return value: None. - - void SetIgnoresTeamHits(bool ignoreTeam = true) { m_IgnoresTeamHits = ignoreTeam; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IgnoresTeamHits -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether this will collide with any other MO of the same team. -// Arguments: None. -// Return value: Whether this can hit or get hit by other MOs of the same team. - - bool IgnoresTeamHits() const { return m_IgnoresTeamHits; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IgnoresWhichTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells which team this would be ignoring hits with, if we're ignoring -// hits at all. -// Arguments: None. -// Return value: Which team this ignores hits with, if any. - - int IgnoresWhichTeam() const { return m_IgnoresTeamHits ? m_Team : Activity::NoTeam; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetIgnoresAtomGroupHits -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this will collide with any other MO that uses an -// AtomGroup as a physical representation. This also overrides the -// IgnoresAGHitsWhenSlowerThan property. -// Arguments: Whether this can hit or get hit by other MOs which use AGs. -// Return value: None. - - void SetIgnoresAtomGroupHits(bool ignoreAG = true) { m_IgnoresAtomGroupHits = ignoreAG; if (ignoreAG) m_IgnoresAGHitsWhenSlowerThan = -1; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IgnoresAtomGroupHits -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether this will collide with any MO that uses an AtomGroup -// as physical representation. (as opposed to single-atom ones) -// Arguments: None. -// Return value: Whether this can hit or get hit by other MOs that use AGs. - - bool IgnoresAtomGroupHits() const { return m_IgnoresAtomGroupHits; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IgnoreTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether this will collide with any Terrain -// Arguments: None. -// Return value: Whether this can hit terrain. - - bool IgnoreTerrain() const { return m_IgnoreTerrain; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetIgnoreTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this will collide with any Terrain -// Arguments: Whether this can hit terrain. -// Return value: None. - - void SetIgnoreTerrain(bool ignores) { m_IgnoreTerrain = ignores; } - - /// - /// Gets whether this MO ignores collisions with actors. - /// - /// Whether this MO ignores collisions with actors. - bool GetIgnoresActorHits() const { return m_IgnoresActorHits; } - - /// - /// Sets whether this MO ignores collisions with actors. - /// - /// Whether this MO will ignore collisions with actors. - void SetIgnoresActorHits(bool value) { m_IgnoresActorHits = value; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: GetMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the main material of this MovableObject. -// Arguments: None. -// Return value: The the material of this MovableObject. - - virtual Material const * GetMaterial() const = 0; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: GetDrawPriority -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the drawing priority of this MovableObject, if two things were -// overlap when copying to the terrain, the higher priority MO would -// end up getting drawn. -// Arguments: None. -// Return value: The the priority of this MovableObject. Higher number, the higher -// priority. - - virtual int GetDrawPriority() const = 0; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetScreenEffect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the screen effect this has loaded, which can be applied to post -// rendering. Ownership is NOT transferred! -// Arguments: None. -// Return value: The 32bpp screen effect BITMAP. Ownership is NOT transferred! - - BITMAP * GetScreenEffect() const { return m_pScreenEffect; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetScreenEffectHash -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the hash of the path of this object's screen effect file. Used to -// transfer glow effects over network. The hash itself is calculated during -// load. -// Arguments: None. -// Return value: This effect's unique hash. - - size_t GetScreenEffectHash() const { return m_ScreenEffectHash; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the mass of this MovableObject. -// Arguments: A float specifying the new mass value in Kilograms (kg). -// Return value: None. - - virtual void SetMass(const float newMass) { m_Mass = newMass; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPrevPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the position at the start of the sim update. -// Arguments: A Vector specifying the new 'prev' pos. -// Return value: None. - - void SetPrevPos(const Vector &newPrevPos) {m_PrevPos = newPrevPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the velocity vector of this MovableObject. -// Arguments: A Vector specifying the new velocity vector. -// Return value: None. - - void SetVel(const Vector &newVel) {m_Vel = newVel; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetRotAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current absolute angle of rotation of this MovableObject. -// Arguments: The new absolute angle in radians. -// Return value: None. - - void SetRotAngle(float newAngle) override {} +#ifndef RELEASE_BUILD + RTEAssert(impulse.MagnitudeIsLessThan(500000.0F), "HUEG IMPULSE FORCE"); + RTEAssert(offset.MagnitudeIsLessThan(5000.0F), "HUGE IMPULSE FORCE OFFSET"); +#endif -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetEffectRotAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current absolute angle of rotation of this MovableObject's effect. -// Arguments: The new absolute angle in radians. -// Return value: None. - - void SetEffectRotAngle(float newAngle) { m_EffectRotAngle = newAngle; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetEffectRotAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current absolute angle of rotation of this MovableObject's effect. -// Arguments: None. -// Return value: The absolute angle in radians. - - float GetEffectRotAngle() const { return m_EffectRotAngle; } - - /// - /// Gets the starting strength of this MovableObject's effect. - /// - /// The starting strength of the effect, 0-255. - int GetEffectStartStrength() const { return m_EffectStartStrength; } - - /// - /// Gets the stopping strength of this MovableObject's effect. - /// - /// The stopping strength of the effect, 0-255. - int GetEffectStopStrength() const { return m_EffectStopStrength; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetAngularVel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current angular velocity of this MovableObject. Positive is -// a counter clockwise rotation. -// Arguments: The new angular velocity in radians per second. -// Return value: None. - - virtual void SetAngularVel(float newRotVel) {} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetScale -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current scale of this MOSRotating. This is mostly for fun. -// Arguments: The new normalized scale. -// Return value: None. - - void SetScale(float newScale) { m_Scale = newScale; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLifetime -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the amount of time this MovableObject will exist. -// Arguments: A unsigned long specifying amount of time in ms. 0 means unlimited life. -// Return value: None. - - void SetLifetime(const int newLifetime = 0) { m_Lifetime = newLifetime; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAge -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this' age timer to a specific value, in ms. -// Arguments: The new age of this, in MS. -// Return value: None. - - void SetAge(double newAge = 0) { m_AgeTimer.SetElapsedSimTimeMS(newAge); } - - - /// - /// Sets the MOID of this MovableObject to be g_NoMOID (255) for this frame. - /// - virtual void SetAsNoID() { m_MOID = g_NoMOID; } - - /// - /// Sets this MovableObject as having been added to MovableMan. Should only really be done in MovableMan::Add/Remove Actor/Item/Particle. - /// - /// Whether or not this MovableObject has been added to MovableMan. - void SetAsAddedToMovableMan(bool addedToMovableMan = true) { if (addedToMovableMan) { m_HasEverBeenAddedToMovableMan = true; } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetSharpness -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the sharpness factor of this MO. -// Arguments: The sharpness factor of this MO. 1.0 means normal sharpness, no alter- -// ation to any of the impulses. -// Return value: None. - - void SetSharpness(const float sharpness) { m_Sharpness = sharpness; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetToHitMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this MovableObject to collide with other MovableObjects during -// travel. -// Arguments: Whether to hit other MO's during travel, or not. -// Return value: None. - - void SetToHitMOs(bool hitMOs = true) { m_HitsMOs = hitMOs; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetToGetHitByMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this MovableObject to be able to be collided with by other -// MovableObjects during their travel. -// Arguments: Whether this should get hit by other MO's during travel, or not. -// Return value: None. - - void SetToGetHitByMOs(bool getHitByMOs = true) { m_GetsHitByMOs = getHitByMOs; } - - - /// - /// Gets the MO this MO is set not to hit even when MO hitting is enabled on this MO. - /// - /// The MO this MO is set not to hit. - const MovableObject * GetWhichMOToNotHit() const { return m_pMOToNotHit; } - - /// - /// Sets this MO to not hit a specific other MO and all its children even when MO hitting is enabled on this MO. - /// - /// A pointer to the MO to not be hitting. Null pointer means don't ignore anyhting. Ownership is NOT transferred! - /// How long, in seconds, to ignore the specified MO. A negative number means forever. - virtual void SetWhichMOToNotHit(MovableObject *moToNotHit = nullptr, float forHowLong = -1) { m_pMOToNotHit = moToNotHit; m_MOIgnoreTimer.Reset(); m_MOIgnoreTimer.SetSimTimeLimitS(forHowLong); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetWrapDoubleDrawing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Enables or disables double drawing of this across wrapping seams. -// Arguments: Wheter to enable or not. -// Return value: None. - - void SetWrapDoubleDrawing(bool wrapDraw = true) { m_WrapDoubleDraw = wrapDraw; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetToSettle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Marks this MovableObject for settling onto the terrain at the end of -// the MovableMan update. -// Arguments: Whether to mark this MO for settling or not. -// Return value: None. - - void SetToSettle(bool toSettle = true) { m_ToSettle = toSettle; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetToDelete -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Marks this MovableObject for deletion at the end of the MovableMan -// update. -// Arguments: Whether to mark this MO for deletion or not. -// Return value: None. - - void SetToDelete(bool toDelete = true) { m_ToDelete = toDelete; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsSetToDelete -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells if this MovableObject is marked for deletion at the end of the -// update. -// Arguments: None. -// Return value: Whether this is marked for deletion or not. - - bool IsSetToDelete() const { return m_ToDelete; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsMissionCritical -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is mission critical and should therefore NEVER be -// settled or otherwise destroyed during teh course of a mission. -// Arguments: None. -// Return value: Whetehr this should be immune to settling and destruction. - - bool IsMissionCritical() const { return m_MissionCritical; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMissionCritical -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this is mission critical and should therefore NEVER be -// settled or otherwise destroyed during teh course of a mission. -// Arguments: Whether this should be immune to settling and destruction. -// Return value: None. - - void SetMissionCritical(bool missionCritical) { m_MissionCritical = missionCritical; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CanBeSquished -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this can be squished by getting pushed into the ground. -// Arguments: None. -// Return value: Whetehr this should be immune to squishing or not. - - bool CanBeSquished() const { return m_CanBeSquished; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetHUDVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether this Actor's HUD is drawn or not. -// Arguments: None. -// Return value: Whether this' HUD gets drawn or not. - - void SetHUDVisible(bool visible) { m_HUDVisible = visible; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetHUDVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether this Actor's HUD is drawn or not. -// Arguments: None. -// Return value: Whether this' HUD gets drawn or not. - - bool GetHUDVisible() const { return m_HUDVisible; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsTooFast -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is moving or rotating stupidly fast in a way -// that will screw up the simulation. -// Arguments: None. -// Return value: Whether this is either moving or rotating too fast. - - virtual bool IsTooFast() const { return m_Vel.MagnitudeIsGreaterThan(500.0F); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: FixTooFast -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Slows the speed of anyhting that is deemed to be too fast to within -// acceptable rates. -// Arguments: None. -// Return value: None. - - virtual void FixTooFast() { if (IsTooFast()) { m_Vel.SetMagnitude(450.0F); } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsGeneric -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is an Generic or not. -// Arguments: None. -// Return value: Whether this MovableObject is of Type Generic or not. - - bool IsGeneric() const { return m_MOType == TypeGeneric; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is an Actor or not. -// Arguments: None. -// Return value: Whether this MovableObject is of Type Actor or not. - - bool IsActor() const { return m_MOType == TypeActor; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is a Device or not. -// Arguments: None. -// Return value: Whether this MovableObject is of Type Device (Held or Thrown) or not. - - bool IsDevice() const { return m_MOType == TypeHeldDevice || m_MOType == TypeThrownDevice; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsHeldDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is a HeldDevice or not. -// Arguments: None. -// Return value: Whether this MovableObject is of Type HeldDevice or not. - -// LEGACY CRAP - bool IsHeldDevice() const { return m_MOType == TypeHeldDevice || m_MOType == TypeThrownDevice; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsThrownDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is a ThrownDevice or not. -// Arguments: None. -// Return value: Whether this MovableObject is of Type ThrownDevice or not. - - bool IsThrownDevice() const { return m_MOType == TypeThrownDevice; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsGold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is made of Gold or not. -// Arguments: None. -// Return value: Whether this MovableObject is of Gold or not. - - bool IsGold() const { return m_MOType == TypeGeneric && GetMaterial()->GetIndex() == c_GoldMaterialID; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDrawnAfterParent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MovableObject is to be drawn after -// (in front of) or before (behind) the parent. -// Arguments: None. -// Return value: Whether it's to be drawn after parent or not. - - virtual bool IsDrawnAfterParent() const { return true; } - - /// - /// Whether a set of X, Y coordinates overlap us (in world space). - /// - /// The given X coordinate, in world space. - /// The given Y coordinate, in world space. - /// Whether the given coordinate overlap us. - virtual bool HitTestAtPixel(int pixelX, int pixelY) const { return false; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically named object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. -// Arguments: The Preset name of the object to look for. -// Return value: Whetehr the object was found carried by this. - - virtual bool HasObject(std::string objectName) const { return m_PresetName == objectName; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasObjectInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is or carries a specifically grouped object in its -// inventory. Also looks through the inventories of potential passengers, -// as applicable. -// Arguments: The name of the group to look for. -// Return value: Whetehr the object in the group was found carried by this. - - virtual bool HasObjectInGroup(std::string groupName) const { return const_cast(this)->IsInGroup(groupName); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds force to this MovableObject for the next time Update() is called. -// Arguments: An Vector with the external force vector that will be added to this -// MovableObject and affect its path next Update(). In N or kg * m/s^2. -// A Vector with the offset, in METERS, of where the force is being -// applied relative to the center of this MovableObject. -// Return value: None.A - - void AddForce(const Vector &force, const Vector &offset = Vector()) - { m_Forces.push_back(std::make_pair(force, offset)); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddAbsForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds force to this MovableObject for the next time Update() is called. -// Arguments: An Vector with the external force vector that will be added to this -// MovableObject and affect its path next Update(). In N or kg * m/s^2. -// A Vector with the absolute world coordinates, in PIXELS, of where the -// force is being applied to the center of this MovableObject. -// Return value: None. - - void AddAbsForce(const Vector &force, const Vector &absPos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddImpulseForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds impulse force (or instant momentum) to this MovableObject for -// the next time Update() is called. -// Arguments: An Vector with the impulse force vector that will directly be added -// to this MovableObject's momentum next Update(). In kg * m/s. -// A Vector with the offset, in METERS, of where the impulse is being -// applied relative to the center of this MovableObject. -// Return value: None. - - void AddImpulseForce(const Vector &impulse, const Vector &offset = Vector()) { - -#ifndef RELEASE_BUILD - RTEAssert(impulse.MagnitudeIsLessThan(500000.0F), "HUEG IMPULSE FORCE"); - RTEAssert(offset.MagnitudeIsLessThan(5000.0F), "HUGE IMPULSE FORCE OFFSET"); -#endif - - m_ImpulseForces.push_back({impulse, offset}); - } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddAbsImpulseForce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds impulse force (or instant momentum) to this MovableObject for -// the next time Update() is called. -// Arguments: An Vector with the impulse force vector that will directly be added -// to this MovableObject's momentum next Update(). In kg * m/s. -// A Vector with the absolute world coordinates, in PIXELS, of where the -// force is being applied to the center of this MovableObject. -// Return value: None. - - void AddAbsImpulseForce(const Vector &impulse, const Vector &absPos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out all the forces this MO has accumulated during this frame. -// Arguments: None. -// Return value: None. - - void ClearForces() { m_Forces.clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearImpulseForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out all the impulses this MO has accumulated during this frame. -// Arguments: None. -// Return value: None. - - void ClearImpulseForces() { m_ImpulseForces.clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPinStrength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the impulse force threshold which has to be exceeded to -// 'shake loose' this from a 'pinned' state. Pinned MOs don't get moved -// by travel algos. If 0, this isn't pinned. -// Arguments: None. -// Return value: The impulse threshold in kg * (m/s). 0 means no pinning - - float GetPinStrength() const { return m_PinStrength; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPinStrength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a impulse force threshold which has to be exceeded to -// 'shake loose' this from a 'pinned' state. Pinned MOs don't get moved -// by travel algos. If 0, this isn't pinned. -// Arguments: The impulse threshold in kg * (m/s). 0 means no pinning -// Return value: None. - - void SetPinStrength(float pinStrength) { m_PinStrength = pinStrength; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetAllTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resest all the timers used by this. Can be emitters, etc. This is to -// prevent backed up emissions to come out all at once while this has been -// held dormant in an inventory. -// Arguments: None. -// Return value: None. - - virtual void ResetAllTimers() {} - - /// - /// Does the calculations necessary to detect whether this MovableObject is at rest or not. IsAtRest() retrieves the answer. - /// - virtual void RestDetection(); - - /// - /// Forces this MovableObject out of resting conditions. - /// - virtual void NotResting() { m_RestTimer.Reset(); m_ToSettle = false; m_VelOscillations = 0; } - - /// - /// Indicates whether this MovableObject has been at rest with no movement for longer than its RestThreshold. - /// - virtual bool IsAtRest(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsUpdated -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates wheter this MovableObject has been updated yet during this -// frame. -// Arguments: None. -// Return value: Wheter or not the MovableObject has been updated yet during this frame. - - bool IsUpdated() const { return m_IsUpdated; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: NewFrame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tell this MovableObject that a new frame has started. -// Arguments: None. -// Return value: None. - - void NewFrame() { m_IsUpdated = false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ToSettle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is marked for settling at the end of the -// MovableMan update. -// Arguments: None. -// Return value: Whether this MO is marked for settling ontot the terrain or not. - - bool ToSettle() const { return !m_MissionCritical && m_ToSettle; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ToDelete -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO is marked for deletion at the end of the -// MovableMan update. -// Arguments: None. -// Return value: Whether this MO is marked for deletion or not. - - bool ToDelete() const { return m_ToDelete; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DidWrap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this MO moved across the scene wrap seam during the -// last update. -// Arguments: None. -// Return value: Whether this MO wrapped or not. - - bool DidWrap() { return m_DidWrap; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure v. method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. -// Arguments: Reference to the HitData struct which describes the collision. This -// will be modified to represent the results of the collision. -// Return value: Whether the collision has been deemed valid. If false, then disregard -// any impulses in the Hitdata. - - virtual bool CollideAtPoint(HitData &hitData) = 0; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: OnMOHit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits another MO. -// This is called by the owned Atom/AtomGroup of this MovableObject during -// travel. -// Arguments: The HitData describing the collision in detail. -// Return value: Wheter the MovableObject should immediately halt any travel going on -// after this hit. - - bool OnMOHit(HitData &hd); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure v. method: OnBounce -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits and then -// bounces off of something. This is called by the owned Atom/AtomGroup -// of this MovableObject during travel. -// Arguments: The HitData describing the collision in detail. -// Return value: Wheter the MovableObject should immediately halt any travel going on -// after this bounce. - - virtual bool OnBounce(HitData &hd) = 0; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure v. method: OnSink -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Defines what should happen when this MovableObject hits and then -// sink into something. This is called by the owned Atom/AtomGroup -// of this MovableObject during travel. -// Arguments: The HitData describing the collision in detail. -// Return value: Wheter the MovableObject should immediately halt any travel going on -// after this sinkage. - - virtual bool OnSink(HitData &hd) = 0; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: MoveOutOfTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether any of the Atom:s in this MovableObject are on top of -// terrain pixels, and if so, attempt to move this out so none of this' -// Atoms are on top of the terrain any more. -// Arguments: Only consider materials stronger than this in the terrain for -// intersections. -// Return value: Whether any intersection was successfully resolved. Will return true -// even if there wasn't any intersections to begin with. - - virtual bool MoveOutOfTerrain(unsigned char strongerThan = g_MaterialAir) { return true; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: RotateOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Rotates a vector offset from this MORotating's position according to -// the rotate angle and flipping. -// Arguments: A const reference the offset Vector to rotate. -// Return value: A new vector that is the result of the rotation. - - virtual Vector RotateOffset(const Vector &offset) const { return offset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gathers and applies the global and accumulated forces. Then it clears -// out the force list.Note that this does NOT apply the accumulated -// impulses (impulse forces)! -// Arguments: None. -// Return value: None. - - virtual void ApplyForces(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ApplyImpulses -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gathers and applies the accumulated impulse forces. Then it clears -// out the impulse list.Note that this does NOT apply the accumulated -// regular forces (non-impulse forces)! -// Arguments: None. -// Return value: None. - - virtual void ApplyImpulses(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetForcesCount() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the number of Forces vectors to apply. -// Arguments: None. -// Return value: Number of entries in Forces list. - - int GetForcesCount() { return m_Forces.size(); }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetForceVector() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns force vector in newtons of the specified Force record. -// Arguments: Force record index to get data from. -// Return value: Force vector in newtons of the specified Force record. - - Vector GetForceVector(int n) { if (n > 0 && n < m_Forces.size()) return m_Forces[n].first; else return Vector(0, 0); } - - /// - /// Gets the total sum of all forces applied to this MovableObject in a single Vector. - /// - /// The total sum of all forces applied to this MovableObject. - virtual Vector GetTotalForce(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetForceOffset() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns offset vector in METERS (not pixels) of the specified Force record. -// Arguments: Force record index to get data from. -// Return value: Offset vector in meters of the specified Force record. - - Vector GetForceOffset(int n) { if (n > 0 && n < m_Forces.size()) return m_Forces[n].second; else return Vector(0, 0); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetForceVector() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets force vector in newtons of the specified Force record. -// Arguments: Force record index to get data from. New Vector force value in newtons. -// Return value: None. - - void SetForceVector(int n, Vector v) { if (n > 0 && n < m_Forces.size()) m_Forces[n].first = v; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetForceOffset() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets offset vector in METERS (not pixels) of the specified Force record. -// Arguments: Force record index to get data from. New Vector offset value in meters. -// Return value: None. - - void SetForceOffset(int n, Vector v) { if (n > 0 && n < m_Forces.size()) m_Forces[n].second = v; } - - /// - /// Gets the pairs of impulse forces and their offsets that have to be applied. - /// - /// A constant reference to the deque of impulses for this MovableObject. - const std::deque > &GetImpulses() { return m_ImpulseForces; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetImpulsesCount() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the number of ImpulseForces vectors to apply. -// Arguments: None. -// Return value: Number of entries in ImpulseForces list. - - int GetImpulsesCount() { return m_ImpulseForces.size(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetImpulseVector() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns Impulse vector in newtons of the specified Impulse record. -// Arguments: Impulse record index to get data from. -// Return value: Impulse vector in newtons of the specified Impulse record. - - Vector GetImpulseVector(int n) { if (n > 0 && n < m_ImpulseForces.size()) return m_ImpulseForces[n].first; else return Vector(0, 0); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetImpulseOffset() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns offset vector in METERS (not pixels) of the specified Impulse record. -// Arguments: Impulse record index to get data from. -// Return value: Offset vector in meters of the specified Impulse record. - - Vector GetImpulseOffset(int n) { if (n > 0 && n < m_ImpulseForces.size()) return m_ImpulseForces[n].second; else return Vector(0, 0); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetImpulseVector() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns offset vector in METERS (not pixels) of the specified Impulse record. -// Arguments: Impulse record index to get data from. -// Return value: Offset vector in meters of the specified Impulse record. - - void SetImpulseVector(int n, Vector v) { if (n > 0 && n < m_ImpulseForces.size()) m_ImpulseForces[n].first = v; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetImpulseOffset() -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets offset vector in METERS (not pixels) of the specified Impulse record. -// Arguments: Impulse record index to get data from. New Vector offset value in meters. -// Return value: None. - - void SetImpulseOffset(int n, Vector v) { if (n > 0 && n < m_ImpulseForces.size()) m_ImpulseForces[n].second = v; } - - /// - /// Gets the number of Sim updates that run between each script update for this MovableObject. - /// - /// The number of Sim updates that run between each script update for this MovableObject. - int GetSimUpdatesBetweenScriptedUpdates() const { return m_SimUpdatesBetweenScriptedUpdates; } - - /// - /// Sets the number of Sim updates that run between each script update for this MovableObject. - /// - /// The new number of Sim updates that run between each script update for this MovableObject. - void SetSimUpdatesBetweenScriptedUpdates(int newSimUpdatesBetweenScriptedUpdates) { m_SimUpdatesBetweenScriptedUpdates = std::max(1, newSimUpdatesBetweenScriptedUpdates); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done before Travel(). Always call before -// calling Travel. -// Arguments: None. -// Return value: None. - - virtual void PreTravel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Travel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Travels this MovableObject, using its physical representation. -// Arguments: None. -// Return value: None. - - virtual void Travel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PostTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done after Travel(). Always call after -// calling Travel. -// Arguments: None. -// Return value: None. - - virtual void PostTravel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PreControllerUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - virtual void PreControllerUpdate() { }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. This also -// applies and clear the accumulated impulse forces (impulses), and the -// transferred forces of MOs attached to this. -// Arguments: None. -// Return value: None. - - void Update() override; - - void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - /// - /// Updates this MovableObject's Lua scripts. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - virtual int UpdateScripts(); - - /// - /// Gets a const reference to this MOSRotating's map of string values. - /// - /// A const reference to this MOSRotating's map of string values. - const std::unordered_map & GetStringValueMap() const { return m_StringValueMap; } - - /// - /// Gets a const reference to this MOSRotating's map of number values. - /// - /// A const reference to this MOSRotating's map of number values. - const std::unordered_map & GetNumberValueMap() const { return m_NumberValueMap; } - - /// - /// Returns the string value associated with the specified key or "" if it does not exist. - /// - /// Key to retrieve value. - /// The value associated with the key. - const std::string & GetStringValue(const std::string &key) const; - - /// - /// Returns an encoded string value associated with the specified key or "" if it does not exist. - /// - /// Key to retrieve value. - /// The value associated with the key. - std::string GetEncodedStringValue(const std::string &key) const; - - /// - /// Returns the number value associated with the specified key or 0 if it does not exist. - /// - /// Key to retrieve value. - /// The value associated with the key. - double GetNumberValue(const std::string &key) const; - - /// - /// Returns the entity value associated with the specified key or nullptr if it does not exist. - /// - /// Key to retrieve value. - /// The value associated with the key. - Entity * GetObjectValue(const std::string &key) const; - - /// - /// Sets the string value associated with the specified key. - /// - /// Key to retrieve value. - /// The new value to be associated with the key. - void SetStringValue(const std::string &key, const std::string &value); - - /// - /// Sets the string value associated with the specified key. - /// - /// Key to retrieve value. - /// The new value to be associated with the key. - void SetEncodedStringValue(const std::string &key, const std::string &value); - - /// - /// Sets the number value associated with the specified key. - /// - /// Key to retrieve value. - /// The new value to be associated with the key. - void SetNumberValue(const std::string &key, double value); - - /// - /// Sets the entity value associated with the specified key. - /// - /// Key to retrieve value. - /// The new value to be associated with the key. - void SetObjectValue(const std::string &key, Entity *value); - - /// - /// Remove the string value associated with the specified key. - /// - /// The key to remove. - void RemoveStringValue(const std::string &key); - - /// - /// Remove the number value associated with the specified key. - /// - /// The key to remove. - void RemoveNumberValue(const std::string &key); - - /// - /// Remove the entity value associated with the specified key. - /// - /// The key to remove. - void RemoveObjectValue(const std::string &key); - - /// - /// Checks whether the string value associated with the specified key exists. - /// - /// The key to check. - /// Whether or not there is an associated value for this key. - bool StringValueExists(const std::string &key) const; - - /// - /// Checks whether the number value associated with the specified key exists. - /// - /// The key to check. - /// Whether or not there is an associated value for this key. - bool NumberValueExists(const std::string &key) const; - - /// - /// Checks whether the entity value associated with the specified key exists. - /// - /// The key to check. - /// Whether or not there is an associated value for this key. - bool ObjectValueExists(const std::string &key) const; - - /// - /// Event listener to be run while this MovableObject's PieMenu is opened. - /// - /// The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - virtual int WhilePieMenuOpenListener(const PieMenu *pieMenu); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this' and its its childrens' MOID's and foorprint. Should -// be done every frame. -// Arguments: None. -// Return value: None. - - void UpdateMOID(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawMOIDIfOverlapping -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the MOID representation of this to the SceneMan's MOID layer if -// this is found to potentially overlap another MovableObject. -// Arguments: The MovableObject to check this for overlap against. -// Return value: Whether it was drawn or not. - - virtual bool DrawMOIDIfOverlapping(MovableObject *pOverlapMO) { return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this' current graphical HUD overlay representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the draw bitmap's upper left corner in the Scene. -// Which player's screen this is being drawn to. May affect what HUD elements -// get drawn etc. -// Return value: None. - - virtual void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) { return; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetRestThreshold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns current rest threshold for this MO -// Arguments: None -// Return value: Rest threshold of this MO - - int GetRestThreshold() const { return m_RestThreshold; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetRestThreshold -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets current rest threshold for this MO -// Arguments: New rest threshold value -// Return value: None - - void SetRestThreshold(int newRestThreshold) { m_RestThreshold = newRestThreshold; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Static method: GetNextID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the next unique id for MO's and increments unique ID counter -// Arguments: None. -// Return value: Returns the next unique id. - - static unsigned long int GetNextUniqueID() { return ++m_UniqueIDCounter; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Static method: GetUniqueID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns this MO's unique persistent ID -// Arguments: None. -// Return value: Returns this MO's unique persistent ID - - unsigned long int const GetUniqueID() const { return m_UniqueID; } - - /// - /// Gets the preset name and unique ID of this MO, often useful for error messages. - /// - /// A string containing the unique ID and preset name of this MO. - std::string GetPresetNameAndUniqueID() const { return m_PresetName + ", UID: " + std::to_string(m_UniqueID); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DamageOnCollision -////////////////////////////////////////////////////////////////////////////////////////// -// Description: If not zero applyies specified ammount of damage points to actors on -// collision even without penetration. -// Arguments: None -// Return value: Amount of damage to apply. - - float DamageOnCollision() const { return m_DamageOnCollision; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetDamageOnCollision -////////////////////////////////////////////////////////////////////////////////////////// -// Description: If not zero applyies specified ammount of damage points to actors on -// collision even without penetration. -// Arguments: Amount of damage to apply. -// Return value: None. - - void SetDamageOnCollision(float value) { m_DamageOnCollision = value; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DamageOnPenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: If not zero applies specified ammount of damage points to actors on -// collision if penetration occured. -// Arguments: None -// Return value: Amount of damage to apply. - - float DamageOnPenetration() const { return m_DamageOnPenetration; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetDamageOnPenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: If not zero applies specified ammount of damage points to actors on -// collision if penetration occured. -// Arguments: Amount of damage to apply. -// Return value: None. - - void SetDamageOnPenetration(float value) { m_DamageOnPenetration = value; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: WoundDamageMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns damage multiplier transferred to wound inflicted by this object on penetration -// Arguments: None -// Return value: Damage multiplier to apply to wound. - - float WoundDamageMultiplier() const { return m_WoundDamageMultiplier; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetWoundDamageMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets damage multiplier transferred to wound inflicted by this object on penetration -// Arguments: New damage multiplier to apply to wound. -// Return value: None. - - void SetWoundDamageMultiplier(float value) { m_WoundDamageMultiplier = value; } - - /// - /// Gets whether or not this MovableObject should apply wound damage when it collides with another MovableObject. - /// - /// Whether or not this MovableObject should apply wound damage when it collides with another MovableObject. - bool GetApplyWoundDamageOnCollision() const { return m_ApplyWoundDamageOnCollision; } - - /// - /// Sets whether or not this MovableObject should apply wound damage when it collides with another MovableObject. - /// - /// Whether or not this MovableObject should apply wound damage on collision. - void SetApplyWoundDamageOnCollision(bool applyWoundDamageOnCollision) { m_ApplyWoundDamageOnCollision = applyWoundDamageOnCollision; } - - /// - /// Gets whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. - /// - /// Whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. - bool GetApplyWoundBurstDamageOnCollision() const { return m_ApplyWoundBurstDamageOnCollision; } - - /// - /// Sets whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. - /// - /// Whether or not this MovableObject should apply burst wound damage on collision. - void SetApplyWoundBurstDamageOnCollision(bool applyWoundBurstDamageOnCollision) { m_ApplyWoundBurstDamageOnCollision = applyWoundBurstDamageOnCollision; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - virtual void GetMOIDs(std::vector &MOIDs) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HitWhatMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the ID of the MO hit at the previously taken Travel -// This will only potentially return non-g_NoMOID if this object's Atom is set to -// hit MO's and the MO hit isn't marked to be ignored. -// Arguments: None. -// Return value: The ID of the non-ignored MO, if any, that this object's Atom or AtomGroup is now -// intersecting because of the last Travel taken. - - MOID HitWhatMOID() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetHitWhatMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the ID of the MO hit at the previously taken Travel -// This will only potentially return non-g_NoMOID if this object's Atom is set to -// hit MO's and the MO hit isn't marked to be ignored. -// Arguments: The ID of the non-ignored MO, if any, that this object's Atom or AtomGroup is now -// intersecting because of the last Travel taken. -// Return value: None. - - void SetHitWhatMOID(MOID id); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HitWhatMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the unique ID of the particle hit at the previously taken Travel -// Arguments: None. -// Return value: Unique ID of the particle hit at the previously taken Travel - - long int HitWhatParticleUniqueID() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HitWhatMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the unique ID of the particle hit at the previously taken Travel -// Arguments: Unique ID of the particle hit at the previously taken Travel. -// Return value: None. - - void SetHitWhatParticleUniqueID(long int id); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HitWhatTerrMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the terrain material the previously taken Tarvel -// hit, if any. -// Arguments: None. -// Return value: The ID of the material, if any, that this MO hit during the last Travel. - - unsigned char HitWhatTerrMaterial() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetHitWhatTerrMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the terrain material the previously taken Tarvel hit, if any. -// Arguments: The ID of the material, if any, that this MO hit during the last Travel. -// Return value: None. - - void SetHitWhatTerrMaterial(unsigned char matID); - - /// - /// Gets whether this MO's RootParent can GetHitByMOs and is currently traveling. - /// - /// Whether this MO's RootParent can GetHitByMOs and is currently traveling. - bool GetTraveling() const { return GetRootParent()->m_IsTraveling; } - - /// - /// Sets whether this MO's RootParent is currently traveling. - /// - /// Whether this MO's RootParent is currently traveling. - void SetTraveling(bool newValue) { GetRootParent()->m_IsTraveling = newValue; } - - /// - /// Draws this MovableObject's graphical and material representations to the specified SLTerrain's respective layers. - /// - /// The SLTerrain to draw this MovableObject to. Ownership is NOT transferred! - /// Whether the object was successfully drawn to the terrain. - bool DrawToTerrain(SLTerrain *terrain); - - /// - /// Used to get the Lua state that handles our scripts. - /// - /// Our lua state. Can potentially be nullptr if we're not setup yet. - LuaStateWrapper* GetLuaState() { return m_ThreadedLuaState; } - - /// - /// Method to be run when the game is saved via ActivityMan::SaveCurrentGame. Not currently used in metagame or editor saving. - /// - virtual void OnSave() { RunScriptedFunctionInAppropriateScripts("OnSave"); } - - /// - /// Requests a synced update for the MO this frame. - /// - virtual void RequestSyncedUpdate() { m_RequestedSyncedUpdate = true; } - - /// - /// Resets the requested update flag. - /// - virtual void ResetRequestedSyncedUpdateFlag() { m_RequestedSyncedUpdate = false; } - - /// - /// Returns whether this MO has requested a synced update this frame. - /// - /// Whether this MO has requested a synced update this frame. - virtual bool HasRequestedSyncedUpdate() { return m_RequestedSyncedUpdate; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - /// - /// Does necessary work to setup a script object name for this object, allowing it to be accessed in Lua, then runs all of the MO's scripts' Create functions in Lua. - /// - /// 0 on success, -2 if it fails to setup the script object in Lua, and -3 if it fails to run any Create function. - int InitializeObjectScripts(); - - /// - /// Runs the given function for the given script, with the given arguments. The first argument to the function will always be 'self'. - /// If either argument list is not empty, its entries will be passed into the Lua function in order, with entity arguments first. - /// - /// The path to the script to run. - /// The name of the function to run. - /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. - /// Optional vector of strings, that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int RunFunctionOfScript(const std::string &scriptPath, const std::string &functionName, const std::vector &functionEntityArguments = std::vector(), const std::vector &functionLiteralArguments = std::vector()); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - virtual void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) {} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: RegMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself in the MOID register and get ID:s for -// itself and its children for this frame. -// BITMAP of choice. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void RegMOID(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true); - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: MovableObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Copy constructor method used to instantiate a MovableObject object -// identical to an already existing one. -// Arguments: A MovableObject object which is passed in by reference. - - - // Member variables - static Entity::ClassInfo m_sClass; - // Global counter with unique ID's - static std::atomic m_UniqueIDCounter; - // The type of MO this is, either Actor, Item, or Particle - int m_MOType; - float m_Mass; // In metric kilograms (kg). - Vector m_Vel; // In meters per second (m/s). - Vector m_PrevPos; // Previous frame's position. - Vector m_PrevVel; // Previous frame's velocity. - float m_DistanceTravelled; //!< An estimate of how many pixels this MO has travelled since its creation. - float m_Scale; // The scale that this MovableObject's representation will be drawn in. 1.0 being 1:1; - // How this is affected by global effects, from +1.0 to -1.0. Something with a negative value will 'float' upward - float m_GlobalAccScalar; - // How much this is affected by air resistance when traveling over a second, 0 to 1.0, with 0 as default - float m_AirResistance; - // At which threshold of velocity, in m/s, the effect of AirResistance kicks in - float m_AirThreshold; - // The impulse force in kg * (m/s) needed to unpin this. Pinned MO's don't travel at all. - float m_PinStrength; - // The threshold in ms as to how long this MO should wait after being at rest - // to get flagged to be copied to the terrain. - int m_RestThreshold; - // The forces acting on this MovableObject, the first vector being the force in - // In kg * m/s^2 (Newtons), and the second one being the offset the force is being - // applied from the m_Pos, IN METERS (not pixels!). - std::deque > m_Forces; - std::deque > m_ImpulseForces; // First in kg * m/s, second vector in meters. - Timer m_AgeTimer; - Timer m_RestTimer; - - unsigned long m_Lifetime; - // The sharpness factor that gets added to single pixel hit impulses in - // applicable situations. - float m_Sharpness; - // This is to be set each frame that this may be intersecting the terrain, like when it has been flipped - bool m_CheckTerrIntersection; - // Whether or not this MovableObject will test for collisions against other MOs. - bool m_HitsMOs; - // Another MovableObject that this should not be hitting even if it is set to hit MOs. - MovableObject *m_pMOToNotHit; - // For how long to not hit specific MO above - Timer m_MOIgnoreTimer; - // Whether or not this MovableObject can get hit by other MOs. - bool m_GetsHitByMOs; - // Whether this ignores collisions with other MOs of the same Team as this. - bool m_IgnoresTeamHits; - // This currently ignores hits with other AtomGroup MOs. - bool m_IgnoresAtomGroupHits; - // This will flip the IgnoreAtomGroupHits on or off depending on whether this MO is travelling slower than the threshold here, in m/s - // This is disabled if set to negative value, and 0 means AG hits are never ignored - float m_IgnoresAGHitsWhenSlowerThan; - // Wehther this ignores collisions with actors - bool m_IgnoresActorHits; - // This is mission critical, which means it should NEVER be settled or destroyed by gibbing - bool m_MissionCritical; - // Whether this can be destroyed by being squished into the terrain - bool m_CanBeSquished; - // Whether or not this MovableObject has been updated yet this frame. - bool m_IsUpdated; - // Whether wrap drawing double across wrapping seams is enabled or not - bool m_WrapDoubleDraw; - // Whether the position of this object wrapped around the world this frame, or not. - // This is just run-time data, don't need to be saved. - bool m_DidWrap; - // This is only valid the same frame it was assigned! - MOID m_MOID; - // This is only valid the same frame it was assigned! - // MOID of the root MO, same as this' m_MOID if this is owned by MovableMan. - MOID m_RootMOID; - // How many total (subsequent) MOID's this MO and all its children are taking up this frame. - // ie if this MO has no children, this will likely be 1. - int m_MOIDFootprint; - // Whether or not this object has ever been added to MovableMan. Does not take into account the object being removed from MovableMan, though in practice it usually will, cause objects are usually only removed when they're deleted. - bool m_HasEverBeenAddedToMovableMan; - // A set of ID:s of MO:s that already have collided with this MO during this frame. - std::set m_AlreadyHitBy; - int m_VelOscillations; //!< A counter for oscillations in translational velocity, in order to detect settling. - // Mark to have the MovableMan copy this the terrain layers at the end - // of update. - bool m_ToSettle; - // Mark to delete at the end of MovableMan update - bool m_ToDelete; - // To draw this guy's HUD or not - bool m_HUDVisible; - - bool m_IsTraveling; //!< Prevents self-intersection while traveling. - - LuaStateWrapper *m_ThreadedLuaState; //!< The lua state that will runs our lua scripts. - bool m_ForceIntoMasterLuaState; //!< This is awful, and only exists for automovers because they mangle global state all over the place. TODO - change automovers to use messages. - - struct LuaFunction { - bool m_ScriptIsEnabled; //!< Whether this function is in an enabled script. - std::unique_ptr m_LuaFunction; //!< The lua function itself. - }; - - std::string m_ScriptObjectName; //!< The name of this object for script usage. - std::unordered_map m_AllLoadedScripts; //!< A map of script paths to the enabled state of the given script. - std::unordered_map> m_FunctionsAndScripts; //!< A map of function names to vectors of Lua functions. Used to maintain script execution order and avoid extraneous Lua calls. - - volatile bool m_RequestedSyncedUpdate; //!< For optimisation purposes, scripts explicitly request a synced update if they want one. - - std::unordered_map m_StringValueMap; // m_NumberValueMap; // m_ObjectValueMap; // - /// Clears all the member variables of this MovableObject, effectively resetting the members of this abstraction level only. - /// - void Clear(); - - /// - /// Handles reading for custom values, dealing with the various types of custom values. - /// - /// A Reader lined up to the custom value type to be read. - void ReadCustomValueProperty(Reader& reader); - - /// - /// Returns the script state to use for a given script path. - /// This will be locked to our thread and safe to use - ensure that it'll be unlocked after use! - /// - /// The path to the script to check for thread safety. - /// A LuaFunction, to use as an early-out check instead of redundantly hashing and checking the filepath string. - /// A script state. - LuaStateWrapper & GetAndLockStateForScript(const std::string &scriptPath, const LuaFunction *function = nullptr); - - // Disallow the use of some implicit methods. - MovableObject(const MovableObject &reference) = delete; - MovableObject& operator=(const MovableObject& ref) = delete; -}; + m_ImpulseForces.push_back({impulse, offset}); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddAbsImpulseForce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds impulse force (or instant momentum) to this MovableObject for + // the next time Update() is called. + // Arguments: An Vector with the impulse force vector that will directly be added + // to this MovableObject's momentum next Update(). In kg * m/s. + // A Vector with the absolute world coordinates, in PIXELS, of where the + // force is being applied to the center of this MovableObject. + // Return value: None. + + void AddAbsImpulseForce(const Vector& impulse, const Vector& absPos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ClearForces + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears out all the forces this MO has accumulated during this frame. + // Arguments: None. + // Return value: None. + + void ClearForces() { m_Forces.clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ClearImpulseForces + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears out all the impulses this MO has accumulated during this frame. + // Arguments: None. + // Return value: None. + + void ClearImpulseForces() { m_ImpulseForces.clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPinStrength + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the impulse force threshold which has to be exceeded to + // 'shake loose' this from a 'pinned' state. Pinned MOs don't get moved + // by travel algos. If 0, this isn't pinned. + // Arguments: None. + // Return value: The impulse threshold in kg * (m/s). 0 means no pinning + + float GetPinStrength() const { return m_PinStrength; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPinStrength + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets a impulse force threshold which has to be exceeded to + // 'shake loose' this from a 'pinned' state. Pinned MOs don't get moved + // by travel algos. If 0, this isn't pinned. + // Arguments: The impulse threshold in kg * (m/s). 0 means no pinning + // Return value: None. + + void SetPinStrength(float pinStrength) { m_PinStrength = pinStrength; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ResetAllTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resest all the timers used by this. Can be emitters, etc. This is to + // prevent backed up emissions to come out all at once while this has been + // held dormant in an inventory. + // Arguments: None. + // Return value: None. + + virtual void ResetAllTimers() {} + + /// + /// Does the calculations necessary to detect whether this MovableObject is at rest or not. IsAtRest() retrieves the answer. + /// + virtual void RestDetection(); + + /// + /// Forces this MovableObject out of resting conditions. + /// + virtual void NotResting() { + m_RestTimer.Reset(); + m_ToSettle = false; + m_VelOscillations = 0; + } + + /// + /// Indicates whether this MovableObject has been at rest with no movement for longer than its RestThreshold. + /// + virtual bool IsAtRest(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsUpdated + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates wheter this MovableObject has been updated yet during this + // frame. + // Arguments: None. + // Return value: Wheter or not the MovableObject has been updated yet during this frame. + + bool IsUpdated() const { return m_IsUpdated; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: NewFrame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tell this MovableObject that a new frame has started. + // Arguments: None. + // Return value: None. + + void NewFrame() { m_IsUpdated = false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ToSettle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is marked for settling at the end of the + // MovableMan update. + // Arguments: None. + // Return value: Whether this MO is marked for settling ontot the terrain or not. + + bool ToSettle() const { return !m_MissionCritical && m_ToSettle; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ToDelete + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO is marked for deletion at the end of the + // MovableMan update. + // Arguments: None. + // Return value: Whether this MO is marked for deletion or not. + + bool ToDelete() const { return m_ToDelete; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DidWrap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this MO moved across the scene wrap seam during the + // last update. + // Arguments: None. + // Return value: Whether this MO wrapped or not. + + bool DidWrap() { return m_DidWrap; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure v. method: CollideAtPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the collision response when another MO's Atom collides with + // this MO's physical representation. The effects will be applied + // directly to this MO, and also represented in the passed in HitData. + // Arguments: Reference to the HitData struct which describes the collision. This + // will be modified to represent the results of the collision. + // Return value: Whether the collision has been deemed valid. If false, then disregard + // any impulses in the Hitdata. + + virtual bool CollideAtPoint(HitData& hitData) = 0; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: OnMOHit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits another MO. + // This is called by the owned Atom/AtomGroup of this MovableObject during + // travel. + // Arguments: The HitData describing the collision in detail. + // Return value: Wheter the MovableObject should immediately halt any travel going on + // after this hit. + + bool OnMOHit(HitData& hd); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure v. method: OnBounce + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits and then + // bounces off of something. This is called by the owned Atom/AtomGroup + // of this MovableObject during travel. + // Arguments: The HitData describing the collision in detail. + // Return value: Wheter the MovableObject should immediately halt any travel going on + // after this bounce. + + virtual bool OnBounce(HitData& hd) = 0; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure v. method: OnSink + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Defines what should happen when this MovableObject hits and then + // sink into something. This is called by the owned Atom/AtomGroup + // of this MovableObject during travel. + // Arguments: The HitData describing the collision in detail. + // Return value: Wheter the MovableObject should immediately halt any travel going on + // after this sinkage. + + virtual bool OnSink(HitData& hd) = 0; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: MoveOutOfTerrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks whether any of the Atom:s in this MovableObject are on top of + // terrain pixels, and if so, attempt to move this out so none of this' + // Atoms are on top of the terrain any more. + // Arguments: Only consider materials stronger than this in the terrain for + // intersections. + // Return value: Whether any intersection was successfully resolved. Will return true + // even if there wasn't any intersections to begin with. + + virtual bool MoveOutOfTerrain(unsigned char strongerThan = g_MaterialAir) { return true; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: RotateOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Rotates a vector offset from this MORotating's position according to + // the rotate angle and flipping. + // Arguments: A const reference the offset Vector to rotate. + // Return value: A new vector that is the result of the rotation. + + virtual Vector RotateOffset(const Vector& offset) const { return offset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ApplyForces + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gathers and applies the global and accumulated forces. Then it clears + // out the force list.Note that this does NOT apply the accumulated + // impulses (impulse forces)! + // Arguments: None. + // Return value: None. + + virtual void ApplyForces(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ApplyImpulses + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gathers and applies the accumulated impulse forces. Then it clears + // out the impulse list.Note that this does NOT apply the accumulated + // regular forces (non-impulse forces)! + // Arguments: None. + // Return value: None. + + virtual void ApplyImpulses(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetForcesCount() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the number of Forces vectors to apply. + // Arguments: None. + // Return value: Number of entries in Forces list. + + int GetForcesCount() { return m_Forces.size(); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetForceVector() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns force vector in newtons of the specified Force record. + // Arguments: Force record index to get data from. + // Return value: Force vector in newtons of the specified Force record. + + Vector GetForceVector(int n) { + if (n > 0 && n < m_Forces.size()) + return m_Forces[n].first; + else + return Vector(0, 0); + } + + /// + /// Gets the total sum of all forces applied to this MovableObject in a single Vector. + /// + /// The total sum of all forces applied to this MovableObject. + virtual Vector GetTotalForce(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetForceOffset() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns offset vector in METERS (not pixels) of the specified Force record. + // Arguments: Force record index to get data from. + // Return value: Offset vector in meters of the specified Force record. + + Vector GetForceOffset(int n) { + if (n > 0 && n < m_Forces.size()) + return m_Forces[n].second; + else + return Vector(0, 0); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetForceVector() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets force vector in newtons of the specified Force record. + // Arguments: Force record index to get data from. New Vector force value in newtons. + // Return value: None. + + void SetForceVector(int n, Vector v) { + if (n > 0 && n < m_Forces.size()) + m_Forces[n].first = v; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetForceOffset() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets offset vector in METERS (not pixels) of the specified Force record. + // Arguments: Force record index to get data from. New Vector offset value in meters. + // Return value: None. + + void SetForceOffset(int n, Vector v) { + if (n > 0 && n < m_Forces.size()) + m_Forces[n].second = v; + } + + /// + /// Gets the pairs of impulse forces and their offsets that have to be applied. + /// + /// A constant reference to the deque of impulses for this MovableObject. + const std::deque>& GetImpulses() { return m_ImpulseForces; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetImpulsesCount() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the number of ImpulseForces vectors to apply. + // Arguments: None. + // Return value: Number of entries in ImpulseForces list. + + int GetImpulsesCount() { return m_ImpulseForces.size(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetImpulseVector() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns Impulse vector in newtons of the specified Impulse record. + // Arguments: Impulse record index to get data from. + // Return value: Impulse vector in newtons of the specified Impulse record. + + Vector GetImpulseVector(int n) { + if (n > 0 && n < m_ImpulseForces.size()) + return m_ImpulseForces[n].first; + else + return Vector(0, 0); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetImpulseOffset() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns offset vector in METERS (not pixels) of the specified Impulse record. + // Arguments: Impulse record index to get data from. + // Return value: Offset vector in meters of the specified Impulse record. + + Vector GetImpulseOffset(int n) { + if (n > 0 && n < m_ImpulseForces.size()) + return m_ImpulseForces[n].second; + else + return Vector(0, 0); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetImpulseVector() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns offset vector in METERS (not pixels) of the specified Impulse record. + // Arguments: Impulse record index to get data from. + // Return value: Offset vector in meters of the specified Impulse record. + + void SetImpulseVector(int n, Vector v) { + if (n > 0 && n < m_ImpulseForces.size()) + m_ImpulseForces[n].first = v; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetImpulseOffset() + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets offset vector in METERS (not pixels) of the specified Impulse record. + // Arguments: Impulse record index to get data from. New Vector offset value in meters. + // Return value: None. + + void SetImpulseOffset(int n, Vector v) { + if (n > 0 && n < m_ImpulseForces.size()) + m_ImpulseForces[n].second = v; + } + + /// + /// Gets the number of Sim updates that run between each script update for this MovableObject. + /// + /// The number of Sim updates that run between each script update for this MovableObject. + int GetSimUpdatesBetweenScriptedUpdates() const { return m_SimUpdatesBetweenScriptedUpdates; } + + /// + /// Sets the number of Sim updates that run between each script update for this MovableObject. + /// + /// The new number of Sim updates that run between each script update for this MovableObject. + void SetSimUpdatesBetweenScriptedUpdates(int newSimUpdatesBetweenScriptedUpdates) { m_SimUpdatesBetweenScriptedUpdates = std::max(1, newSimUpdatesBetweenScriptedUpdates); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreTravel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does stuff that needs to be done before Travel(). Always call before + // calling Travel. + // Arguments: None. + // Return value: None. + + virtual void PreTravel(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Travel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Travels this MovableObject, using its physical representation. + // Arguments: None. + // Return value: None. + + virtual void Travel(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PostTravel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does stuff that needs to be done after Travel(). Always call after + // calling Travel. + // Arguments: None. + // Return value: None. + + virtual void PostTravel(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PreControllerUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + virtual void PreControllerUpdate(){}; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. This also + // applies and clear the accumulated impulse forces (impulses), and the + // transferred forces of MOs attached to this. + // Arguments: None. + // Return value: None. + + void Update() override; + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + /// + /// Updates this MovableObject's Lua scripts. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + virtual int UpdateScripts(); + + /// + /// Gets a const reference to this MOSRotating's map of string values. + /// + /// A const reference to this MOSRotating's map of string values. + const std::unordered_map& GetStringValueMap() const { return m_StringValueMap; } + + /// + /// Gets a const reference to this MOSRotating's map of number values. + /// + /// A const reference to this MOSRotating's map of number values. + const std::unordered_map& GetNumberValueMap() const { return m_NumberValueMap; } + + /// + /// Returns the string value associated with the specified key or "" if it does not exist. + /// + /// Key to retrieve value. + /// The value associated with the key. + const std::string& GetStringValue(const std::string& key) const; + + /// + /// Returns an encoded string value associated with the specified key or "" if it does not exist. + /// + /// Key to retrieve value. + /// The value associated with the key. + std::string GetEncodedStringValue(const std::string& key) const; + + /// + /// Returns the number value associated with the specified key or 0 if it does not exist. + /// + /// Key to retrieve value. + /// The value associated with the key. + double GetNumberValue(const std::string& key) const; + + /// + /// Returns the entity value associated with the specified key or nullptr if it does not exist. + /// + /// Key to retrieve value. + /// The value associated with the key. + Entity* GetObjectValue(const std::string& key) const; + + /// + /// Sets the string value associated with the specified key. + /// + /// Key to retrieve value. + /// The new value to be associated with the key. + void SetStringValue(const std::string& key, const std::string& value); + + /// + /// Sets the string value associated with the specified key. + /// + /// Key to retrieve value. + /// The new value to be associated with the key. + void SetEncodedStringValue(const std::string& key, const std::string& value); + + /// + /// Sets the number value associated with the specified key. + /// + /// Key to retrieve value. + /// The new value to be associated with the key. + void SetNumberValue(const std::string& key, double value); + + /// + /// Sets the entity value associated with the specified key. + /// + /// Key to retrieve value. + /// The new value to be associated with the key. + void SetObjectValue(const std::string& key, Entity* value); + + /// + /// Remove the string value associated with the specified key. + /// + /// The key to remove. + void RemoveStringValue(const std::string& key); + + /// + /// Remove the number value associated with the specified key. + /// + /// The key to remove. + void RemoveNumberValue(const std::string& key); + + /// + /// Remove the entity value associated with the specified key. + /// + /// The key to remove. + void RemoveObjectValue(const std::string& key); + + /// + /// Checks whether the string value associated with the specified key exists. + /// + /// The key to check. + /// Whether or not there is an associated value for this key. + bool StringValueExists(const std::string& key) const; + + /// + /// Checks whether the number value associated with the specified key exists. + /// + /// The key to check. + /// Whether or not there is an associated value for this key. + bool NumberValueExists(const std::string& key) const; + + /// + /// Checks whether the entity value associated with the specified key exists. + /// + /// The key to check. + /// Whether or not there is an associated value for this key. + bool ObjectValueExists(const std::string& key) const; + + /// + /// Event listener to be run while this MovableObject's PieMenu is opened. + /// + /// The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + virtual int WhilePieMenuOpenListener(const PieMenu* pieMenu); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this' and its its childrens' MOID's and foorprint. Should + // be done every frame. + // Arguments: None. + // Return value: None. + + void UpdateMOID(std::vector& MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawMOIDIfOverlapping + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the MOID representation of this to the SceneMan's MOID layer if + // this is found to potentially overlap another MovableObject. + // Arguments: The MovableObject to check this for overlap against. + // Return value: Whether it was drawn or not. + + virtual bool DrawMOIDIfOverlapping(MovableObject* pOverlapMO) { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this' current graphical HUD overlay representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the draw bitmap's upper left corner in the Scene. + // Which player's screen this is being drawn to. May affect what HUD elements + // get drawn etc. + // Return value: None. + + virtual void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) { return; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRestThreshold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns current rest threshold for this MO + // Arguments: None + // Return value: Rest threshold of this MO + + int GetRestThreshold() const { return m_RestThreshold; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetRestThreshold + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets current rest threshold for this MO + // Arguments: New rest threshold value + // Return value: None + + void SetRestThreshold(int newRestThreshold) { m_RestThreshold = newRestThreshold; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Static method: GetNextID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the next unique id for MO's and increments unique ID counter + // Arguments: None. + // Return value: Returns the next unique id. + + static unsigned long int GetNextUniqueID() { return ++m_UniqueIDCounter; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Static method: GetUniqueID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns this MO's unique persistent ID + // Arguments: None. + // Return value: Returns this MO's unique persistent ID + + unsigned long int const GetUniqueID() const { return m_UniqueID; } + + /// + /// Gets the preset name and unique ID of this MO, often useful for error messages. + /// + /// A string containing the unique ID and preset name of this MO. + std::string GetPresetNameAndUniqueID() const { return m_PresetName + ", UID: " + std::to_string(m_UniqueID); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DamageOnCollision + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: If not zero applyies specified ammount of damage points to actors on + // collision even without penetration. + // Arguments: None + // Return value: Amount of damage to apply. + + float DamageOnCollision() const { return m_DamageOnCollision; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetDamageOnCollision + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: If not zero applyies specified ammount of damage points to actors on + // collision even without penetration. + // Arguments: Amount of damage to apply. + // Return value: None. + + void SetDamageOnCollision(float value) { m_DamageOnCollision = value; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DamageOnPenetration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: If not zero applies specified ammount of damage points to actors on + // collision if penetration occured. + // Arguments: None + // Return value: Amount of damage to apply. + + float DamageOnPenetration() const { return m_DamageOnPenetration; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetDamageOnPenetration + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: If not zero applies specified ammount of damage points to actors on + // collision if penetration occured. + // Arguments: Amount of damage to apply. + // Return value: None. + + void SetDamageOnPenetration(float value) { m_DamageOnPenetration = value; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: WoundDamageMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns damage multiplier transferred to wound inflicted by this object on penetration + // Arguments: None + // Return value: Damage multiplier to apply to wound. + + float WoundDamageMultiplier() const { return m_WoundDamageMultiplier; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetWoundDamageMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets damage multiplier transferred to wound inflicted by this object on penetration + // Arguments: New damage multiplier to apply to wound. + // Return value: None. + + void SetWoundDamageMultiplier(float value) { m_WoundDamageMultiplier = value; } + + /// + /// Gets whether or not this MovableObject should apply wound damage when it collides with another MovableObject. + /// + /// Whether or not this MovableObject should apply wound damage when it collides with another MovableObject. + bool GetApplyWoundDamageOnCollision() const { return m_ApplyWoundDamageOnCollision; } + + /// + /// Sets whether or not this MovableObject should apply wound damage when it collides with another MovableObject. + /// + /// Whether or not this MovableObject should apply wound damage on collision. + void SetApplyWoundDamageOnCollision(bool applyWoundDamageOnCollision) { m_ApplyWoundDamageOnCollision = applyWoundDamageOnCollision; } + + /// + /// Gets whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. + /// + /// Whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. + bool GetApplyWoundBurstDamageOnCollision() const { return m_ApplyWoundBurstDamageOnCollision; } + + /// + /// Sets whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. + /// + /// Whether or not this MovableObject should apply burst wound damage on collision. + void SetApplyWoundBurstDamageOnCollision(bool applyWoundBurstDamageOnCollision) { m_ApplyWoundBurstDamageOnCollision = applyWoundBurstDamageOnCollision; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector + // Arguments: Vector to store MOIDs + // Return value: None. + + virtual void GetMOIDs(std::vector& MOIDs) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HitWhatMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the ID of the MO hit at the previously taken Travel + // This will only potentially return non-g_NoMOID if this object's Atom is set to + // hit MO's and the MO hit isn't marked to be ignored. + // Arguments: None. + // Return value: The ID of the non-ignored MO, if any, that this object's Atom or AtomGroup is now + // intersecting because of the last Travel taken. + + MOID HitWhatMOID() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetHitWhatMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the ID of the MO hit at the previously taken Travel + // This will only potentially return non-g_NoMOID if this object's Atom is set to + // hit MO's and the MO hit isn't marked to be ignored. + // Arguments: The ID of the non-ignored MO, if any, that this object's Atom or AtomGroup is now + // intersecting because of the last Travel taken. + // Return value: None. + + void SetHitWhatMOID(MOID id); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HitWhatMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the unique ID of the particle hit at the previously taken Travel + // Arguments: None. + // Return value: Unique ID of the particle hit at the previously taken Travel + + long int HitWhatParticleUniqueID() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HitWhatMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the unique ID of the particle hit at the previously taken Travel + // Arguments: Unique ID of the particle hit at the previously taken Travel. + // Return value: None. + + void SetHitWhatParticleUniqueID(long int id); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HitWhatTerrMaterial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the terrain material the previously taken Tarvel + // hit, if any. + // Arguments: None. + // Return value: The ID of the material, if any, that this MO hit during the last Travel. + + unsigned char HitWhatTerrMaterial() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetHitWhatTerrMaterial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the terrain material the previously taken Tarvel hit, if any. + // Arguments: The ID of the material, if any, that this MO hit during the last Travel. + // Return value: None. + + void SetHitWhatTerrMaterial(unsigned char matID); + + /// + /// Gets whether this MO's RootParent can GetHitByMOs and is currently traveling. + /// + /// Whether this MO's RootParent can GetHitByMOs and is currently traveling. + bool GetTraveling() const { return GetRootParent()->m_IsTraveling; } + + /// + /// Sets whether this MO's RootParent is currently traveling. + /// + /// Whether this MO's RootParent is currently traveling. + void SetTraveling(bool newValue) { GetRootParent()->m_IsTraveling = newValue; } + + /// + /// Draws this MovableObject's graphical and material representations to the specified SLTerrain's respective layers. + /// + /// The SLTerrain to draw this MovableObject to. Ownership is NOT transferred! + /// Whether the object was successfully drawn to the terrain. + bool DrawToTerrain(SLTerrain* terrain); + + /// + /// Used to get the Lua state that handles our scripts. + /// + /// Our lua state. Can potentially be nullptr if we're not setup yet. + LuaStateWrapper* GetLuaState() { return m_ThreadedLuaState; } + + /// + /// Method to be run when the game is saved via ActivityMan::SaveCurrentGame. Not currently used in metagame or editor saving. + /// + virtual void OnSave() { RunScriptedFunctionInAppropriateScripts("OnSave"); } + + /// + /// Requests a synced update for the MO this frame. + /// + virtual void RequestSyncedUpdate() { m_RequestedSyncedUpdate = true; } + + /// + /// Resets the requested update flag. + /// + virtual void ResetRequestedSyncedUpdateFlag() { m_RequestedSyncedUpdate = false; } + + /// + /// Returns whether this MO has requested a synced update this frame. + /// + /// Whether this MO has requested a synced update this frame. + virtual bool HasRequestedSyncedUpdate() { return m_RequestedSyncedUpdate; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Does necessary work to setup a script object name for this object, allowing it to be accessed in Lua, then runs all of the MO's scripts' Create functions in Lua. + /// + /// 0 on success, -2 if it fails to setup the script object in Lua, and -3 if it fails to run any Create function. + int InitializeObjectScripts(); + + /// + /// Runs the given function for the given script, with the given arguments. The first argument to the function will always be 'self'. + /// If either argument list is not empty, its entries will be passed into the Lua function in order, with entity arguments first. + /// + /// The path to the script to run. + /// The name of the function to run. + /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. + /// Optional vector of strings, that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int RunFunctionOfScript(const std::string& scriptPath, const std::string& functionName, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector()); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: UpdateChildMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes this MO register itself and all its attached children in the + // MOID register and get ID:s for itself and its children for this frame. + // Arguments: The MOID index to register itself and its children in. + // The MOID of the root MO of this MO, ie the highest parent of this MO. + // 0 means that this MO is the root, ie it is owned by MovableMan. + // Whether this MO should make a new MOID to use for itself, or to use + // the same as the last one in the index (presumably its parent), + // Return value: None. + + virtual void UpdateChildMOIDs(std::vector& MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: RegMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes this MO register itself in the MOID register and get ID:s for + // itself and its children for this frame. + // BITMAP of choice. + // Arguments: The MOID index to register itself and its children in. + // The MOID of the root MO of this MO, ie the highest parent of this MO. + // 0 means that this MO is the root, ie it is owned by MovableMan. + // Whether this MO should make a new MOID to use for itself, or to use + // the same as the last one in the index (presumably its parent), + // Return value: None. + + void RegMOID(std::vector& MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: MovableObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Copy constructor method used to instantiate a MovableObject object + // identical to an already existing one. + // Arguments: A MovableObject object which is passed in by reference. + + // Member variables + static Entity::ClassInfo m_sClass; + // Global counter with unique ID's + static std::atomic m_UniqueIDCounter; + // The type of MO this is, either Actor, Item, or Particle + int m_MOType; + float m_Mass; // In metric kilograms (kg). + Vector m_Vel; // In meters per second (m/s). + Vector m_PrevPos; // Previous frame's position. + Vector m_PrevVel; // Previous frame's velocity. + float m_DistanceTravelled; //!< An estimate of how many pixels this MO has travelled since its creation. + float m_Scale; // The scale that this MovableObject's representation will be drawn in. 1.0 being 1:1; + // How this is affected by global effects, from +1.0 to -1.0. Something with a negative value will 'float' upward + float m_GlobalAccScalar; + // How much this is affected by air resistance when traveling over a second, 0 to 1.0, with 0 as default + float m_AirResistance; + // At which threshold of velocity, in m/s, the effect of AirResistance kicks in + float m_AirThreshold; + // The impulse force in kg * (m/s) needed to unpin this. Pinned MO's don't travel at all. + float m_PinStrength; + // The threshold in ms as to how long this MO should wait after being at rest + // to get flagged to be copied to the terrain. + int m_RestThreshold; + // The forces acting on this MovableObject, the first vector being the force in + // In kg * m/s^2 (Newtons), and the second one being the offset the force is being + // applied from the m_Pos, IN METERS (not pixels!). + std::deque> m_Forces; + std::deque> m_ImpulseForces; // First in kg * m/s, second vector in meters. + Timer m_AgeTimer; + Timer m_RestTimer; + + unsigned long m_Lifetime; + // The sharpness factor that gets added to single pixel hit impulses in + // applicable situations. + float m_Sharpness; + // This is to be set each frame that this may be intersecting the terrain, like when it has been flipped + bool m_CheckTerrIntersection; + // Whether or not this MovableObject will test for collisions against other MOs. + bool m_HitsMOs; + // Another MovableObject that this should not be hitting even if it is set to hit MOs. + MovableObject* m_pMOToNotHit; + // For how long to not hit specific MO above + Timer m_MOIgnoreTimer; + // Whether or not this MovableObject can get hit by other MOs. + bool m_GetsHitByMOs; + // Whether this ignores collisions with other MOs of the same Team as this. + bool m_IgnoresTeamHits; + // This currently ignores hits with other AtomGroup MOs. + bool m_IgnoresAtomGroupHits; + // This will flip the IgnoreAtomGroupHits on or off depending on whether this MO is travelling slower than the threshold here, in m/s + // This is disabled if set to negative value, and 0 means AG hits are never ignored + float m_IgnoresAGHitsWhenSlowerThan; + // Wehther this ignores collisions with actors + bool m_IgnoresActorHits; + // This is mission critical, which means it should NEVER be settled or destroyed by gibbing + bool m_MissionCritical; + // Whether this can be destroyed by being squished into the terrain + bool m_CanBeSquished; + // Whether or not this MovableObject has been updated yet this frame. + bool m_IsUpdated; + // Whether wrap drawing double across wrapping seams is enabled or not + bool m_WrapDoubleDraw; + // Whether the position of this object wrapped around the world this frame, or not. + // This is just run-time data, don't need to be saved. + bool m_DidWrap; + // This is only valid the same frame it was assigned! + MOID m_MOID; + // This is only valid the same frame it was assigned! + // MOID of the root MO, same as this' m_MOID if this is owned by MovableMan. + MOID m_RootMOID; + // How many total (subsequent) MOID's this MO and all its children are taking up this frame. + // ie if this MO has no children, this will likely be 1. + int m_MOIDFootprint; + // Whether or not this object has ever been added to MovableMan. Does not take into account the object being removed from MovableMan, though in practice it usually will, cause objects are usually only removed when they're deleted. + bool m_HasEverBeenAddedToMovableMan; + // A set of ID:s of MO:s that already have collided with this MO during this frame. + std::set m_AlreadyHitBy; + int m_VelOscillations; //!< A counter for oscillations in translational velocity, in order to detect settling. + // Mark to have the MovableMan copy this the terrain layers at the end + // of update. + bool m_ToSettle; + // Mark to delete at the end of MovableMan update + bool m_ToDelete; + // To draw this guy's HUD or not + bool m_HUDVisible; + + bool m_IsTraveling; //!< Prevents self-intersection while traveling. + + LuaStateWrapper* m_ThreadedLuaState; //!< The lua state that will runs our lua scripts. + bool m_ForceIntoMasterLuaState; //!< This is awful, and only exists for automovers because they mangle global state all over the place. TODO - change automovers to use messages. + + struct LuaFunction { + bool m_ScriptIsEnabled; //!< Whether this function is in an enabled script. + std::unique_ptr m_LuaFunction; //!< The lua function itself. + }; + + std::string m_ScriptObjectName; //!< The name of this object for script usage. + std::unordered_map m_AllLoadedScripts; //!< A map of script paths to the enabled state of the given script. + std::unordered_map> m_FunctionsAndScripts; //!< A map of function names to vectors of Lua functions. Used to maintain script execution order and avoid extraneous Lua calls. + + volatile bool m_RequestedSyncedUpdate; //!< For optimisation purposes, scripts explicitly request a synced update if they want one. + + std::unordered_map m_StringValueMap; // m_NumberValueMap; // m_ObjectValueMap; // + /// Clears all the member variables of this MovableObject, effectively resetting the members of this abstraction level only. + /// + void Clear(); + + /// + /// Handles reading for custom values, dealing with the various types of custom values. + /// + /// A Reader lined up to the custom value type to be read. + void ReadCustomValueProperty(Reader& reader); + + /// + /// Returns the script state to use for a given script path. + /// This will be locked to our thread and safe to use - ensure that it'll be unlocked after use! + /// + /// The path to the script to check for thread safety. + /// A LuaFunction, to use as an early-out check instead of redundantly hashing and checking the filepath string. + /// A script state. + LuaStateWrapper& GetAndLockStateForScript(const std::string& scriptPath, const LuaFunction* function = nullptr); + + // Disallow the use of some implicit methods. + MovableObject(const MovableObject& reference) = delete; + MovableObject& operator=(const MovableObject& ref) = delete; + }; } // namespace RTE diff --git a/Source/Entities/PEmitter.cpp b/Source/Entities/PEmitter.cpp index 9d66a8ae7f..574aea1ce9 100644 --- a/Source/Entities/PEmitter.cpp +++ b/Source/Entities/PEmitter.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -21,14 +20,13 @@ namespace RTE { ConcreteClassInfo(PEmitter, MOSParticle, 100); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this PEmitter, effectively - // resetting the members of this abstraction level only. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this PEmitter, effectively + // resetting the members of this abstraction level only. - void PEmitter::Clear() - { + void PEmitter::Clear() { m_EmissionList.clear(); m_EmissionSound.Reset(); m_BurstSound.Reset(); @@ -59,28 +57,24 @@ namespace RTE { m_LoudnessOnEmit = 1.0f; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the Emission object ready for use. - int PEmitter::Create() - { + int PEmitter::Create() { if (MOSParticle::Create() < 0) return -1; return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a PEmitter to be identical to another, by deep copy. - int PEmitter::Create(const PEmitter &reference) - { + int PEmitter::Create(const PEmitter& reference) { MOSParticle::Create(reference); for (auto itr = reference.m_EmissionList.begin(); itr != reference.m_EmissionList.end(); ++itr) { @@ -110,7 +104,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: ReadProperty ////////////////////////////////////////////////////////////////////////////////////////// @@ -119,16 +112,15 @@ namespace RTE { // is called. If the property isn't recognized by any of the base classes, // false is returned, and the reader's position is untouched. - int PEmitter::ReadProperty(const std::string_view &propName, Reader &reader) - { + int PEmitter::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MOSParticle::ReadProperty(propName, reader)); - + MatchProperty("AddEmission", - { - Emission emission; - reader >> emission; - m_EmissionList.push_back(emission); - }); + { + Emission emission; + reader >> emission; + m_EmissionList.push_back(emission); + }); MatchProperty("EmissionSound", { reader >> m_EmissionSound; }); MatchProperty("BurstSound", { reader >> m_BurstSound; }); MatchProperty("EndSound", { reader >> m_EndSound; }); @@ -136,25 +128,25 @@ namespace RTE { MatchProperty("EmissionCount", { reader >> m_EmitCount; }); MatchProperty("EmissionCountLimit", { reader >> m_EmitCountLimit; }); MatchProperty("ParticlesPerMinute", - { - float ppm; - reader >> ppm; - // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility - for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) - (*eItr).m_PPM = ppm / m_EmissionList.size(); - }); + { + float ppm; + reader >> ppm; + // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility + for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) + (*eItr).m_PPM = ppm / m_EmissionList.size(); + }); MatchProperty("NegativeThrottleMultiplier", { reader >> m_NegativeThrottleMultiplier; }); MatchProperty("PositiveThrottleMultiplier", { reader >> m_PositiveThrottleMultiplier; }); MatchProperty("Throttle", { reader >> m_Throttle; }); MatchProperty("EmissionsIgnoreThis", { reader >> m_EmissionsIgnoreThis; }); MatchProperty("BurstSize", - { - int burstSize; - reader >> burstSize; - // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility - for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) - (*eItr).m_BurstSize = std::ceil((float)burstSize / (float)m_EmissionList.size()); - }); + { + int burstSize; + reader >> burstSize; + // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility + for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) + (*eItr).m_BurstSize = std::ceil((float)burstSize / (float)m_EmissionList.size()); + }); MatchProperty("BurstScale", { reader >> m_BurstScale; }); MatchProperty("BurstSpacing", { reader >> m_BurstSpacing; }); MatchProperty("BurstTriggered", { reader >> m_BurstTriggered; }); @@ -165,23 +157,20 @@ namespace RTE { MatchProperty("SustainBurstSound", { reader >> m_SustainBurstSound; }); MatchProperty("BurstSoundFollowsEmitter", { reader >> m_BurstSoundFollowsEmitter; }); MatchProperty("LoudnessOnEmit", { reader >> m_LoudnessOnEmit; }); - + EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Save ////////////////////////////////////////////////////////////////////////////////////////// // Description: Saves the complete state of this PEmitter with a Writer for // later recreation with Create(Reader &reader); - int PEmitter::Save(Writer &writer) const - { + int PEmitter::Save(Writer& writer) const { MOSParticle::Save(writer); - for (auto itr = m_EmissionList.begin(); itr != m_EmissionList.end(); ++itr) - { + for (auto itr = m_EmissionList.begin(); itr != m_EmissionList.end(); ++itr) { writer.NewProperty("AddEmission"); writer << *itr; } @@ -229,14 +218,12 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the PEmitter object. - void PEmitter::Destroy(bool notInherited) - { + void PEmitter::Destroy(bool notInherited) { // Stop playback of sounds gracefully if (m_EmissionSound.IsBeingPlayed()) m_EndSound.Play(m_Pos); @@ -254,55 +241,46 @@ namespace RTE { ////////////////////////////////////////////////////////////////////////////////////////// // Method: ResetEmissionTimers ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reset the timers of all emissions so they will start/stop at the + // Description: Reset the timers of all emissions so they will start/stop at the // correct relative offsets from now. - void PEmitter::ResetEmissionTimers() - { + void PEmitter::ResetEmissionTimers() { m_LastEmitTmr.Reset(); for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) (*eItr).ResetEmissionTimers(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: EnableEmission ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets this PEmitter to start emitting at the set rate, or to stop. - void PEmitter::EnableEmission(bool enable) - { - if (!m_EmitEnabled && enable) - { + void PEmitter::EnableEmission(bool enable) { + if (!m_EmitEnabled && enable) { m_LastEmitTmr.Reset(); // Reset counter m_EmitCount = 0; // Reset animation - //m_Frame = 0; + // m_Frame = 0; } m_EmitEnabled = enable; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: EstimateImpulse ////////////////////////////////////////////////////////////////////////////////////////// // Description: Calculates the forces this emitter applies on any parent. - float PEmitter::EstimateImpulse(bool burst) - { + float PEmitter::EstimateImpulse(bool burst) { // Calculate the impulse generated by the emissions, once and store the result - if ((!burst && m_AvgImpulse < 0) || (burst && m_AvgBurstImpulse < 0)) - { + if ((!burst && m_AvgImpulse < 0) || (burst && m_AvgBurstImpulse < 0)) { float impulse = 0; float velMin, velMax, velRange, spread; // Go through all emissions and emit them according to their respective rates - for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) - { + for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) { // Only check emissions that push the emitter - if (eItr->PushesEmitter()) - { + if (eItr->PushesEmitter()) { double emissions = eItr->GetRate() * g_TimerMan.GetDeltaTimeSecs() / 60.0f; if (burst) emissions *= eItr->GetBurstSize(); @@ -310,7 +288,7 @@ namespace RTE { velMin = std::min(eItr->GetMinVelocity(), eItr->GetMaxVelocity()); velMax = std::max(eItr->GetMinVelocity(), eItr->GetMaxVelocity()); velRange = (velMax - velMin) * 0.5; - spread = std::max(static_cast(c_PI)-eItr->GetSpread(), .0f) / c_PI; // A large spread will cause the forces to cancel eachother out + spread = std::max(static_cast(c_PI) - eItr->GetSpread(), .0f) / c_PI; // A large spread will cause the forces to cancel eachother out // Add to accumulative recoil impulse generated, F = m * a. impulse += (velMin + velRange) * spread * eItr->m_pEmission->GetMass() * emissions; @@ -321,7 +299,6 @@ namespace RTE { m_AvgBurstImpulse = impulse; else m_AvgImpulse = impulse; - } // Scale the emission rate up or down according to the appropriate throttle multiplier. @@ -333,7 +310,6 @@ namespace RTE { return m_AvgImpulse * throttleFactor; } - /* ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GibThis @@ -354,23 +330,20 @@ namespace RTE { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates this PEmitter. Supposed to be done every frame. - void PEmitter::Update() - { + void PEmitter::Update() { MOSParticle::Update(); if (m_BurstSoundFollowsEmitter) { m_BurstSound.SetPosition(m_Pos); } - if (m_EmitEnabled) - { - if (!m_WasEmitting) - { + if (m_EmitEnabled) { + if (!m_WasEmitting) { // Start playing the sound m_EmissionSound.Play(m_Pos); // Reset the timers of all emissions so they will start/stop at the correct relative offsets from now - for (Emission& emission : m_EmissionList) { + for (Emission& emission: m_EmissionList) { emission.ResetEmissionTimers(); } } @@ -380,14 +353,13 @@ namespace RTE { // Get the parent root of this PEmitter // TODO: Potentially get this once outside instead, like in attach/detach") - MovableObject *pRootParent = GetRootParent(); + MovableObject* pRootParent = GetRootParent(); // Scale the emission rate up or down according to the appropriate throttle multiplier. float throttleFactor = GetThrottleFactor(); m_FlashScale = throttleFactor; // Check burst triggering against whether the spacing is fulfilled - if (m_BurstTriggered && (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing))) - { + if (m_BurstTriggered && (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing))) { // Play burst sound m_BurstSound.Play(m_Pos); // Start timing until next burst @@ -400,21 +372,18 @@ namespace RTE { int emissions = 0; float velMin, velRange, spread; double currentPPM, SPE; - MovableObject *pParticle = 0; + MovableObject* pParticle = 0; Vector parentVel, emitVel, pushImpulses; // Go through all emissions and emit them according to their respective rates - for (Emission &emission : m_EmissionList) - { + for (Emission& emission: m_EmissionList) { // Make sure the emissions only happen between the start time and end time - if (emission.IsEmissionTime()) - { + if (emission.IsEmissionTime()) { // Apply the throttle factor to the emission rate currentPPM = emission.GetRate() * throttleFactor; emissions = 0; // Only do all this if the PPM is acutally above zero - if (currentPPM > 0) - { + if (currentPPM > 0) { // Calculate secs per emission SPE = 60.0 / currentPPM; @@ -437,17 +406,16 @@ namespace RTE { emitVel.Reset(); parentVel = pRootParent->GetVel() * emission.InheritsVelocity(); - for (int i = 0; i < emissions; ++i) - { + for (int i = 0; i < emissions; ++i) { velMin = emission.GetMinVelocity() * (m_BurstTriggered ? m_BurstScale : 1.0); velRange = emission.GetMaxVelocity() - emission.GetMinVelocity() * (m_BurstTriggered ? m_BurstScale : 1.0); spread = emission.GetSpread() * (m_BurstTriggered ? m_BurstScale : 1.0); // Make a copy after the reference particle - pParticle = dynamic_cast(emission.GetEmissionParticlePreset()->Clone()); + pParticle = dynamic_cast(emission.GetEmissionParticlePreset()->Clone()); // Set up its position and velocity according to the parameters of this. // Emission point offset not set if (m_EmissionOffset.IsZero()) - pParticle->SetPos(m_Pos/*Vector(m_Pos.m_X + 5 * NormalRand(), m_Pos.m_Y + 5 * NormalRand())*/); + pParticle->SetPos(m_Pos /*Vector(m_Pos.m_X + 5 * NormalRand(), m_Pos.m_Y + 5 * NormalRand())*/); else pParticle->SetPos(m_Pos + RotateOffset(m_EmissionOffset)); // TODO: Optimize making the random angles!") @@ -456,7 +424,9 @@ namespace RTE { emitVel = RotateOffset(emitVel); pParticle->SetVel(parentVel + emitVel); - if (pParticle->GetLifetime() != 0) { pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + (emission.GetLifeVariation() * RandomNormalNum()))), 1)); } + if (pParticle->GetLifetime() != 0) { + pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + (emission.GetLifeVariation() * RandomNormalNum()))), 1)); + } pParticle->SetTeam(m_Team); pParticle->SetIgnoresTeamHits(true); @@ -494,27 +464,26 @@ namespace RTE { m_WasEmitting = true; } // Do stuff to stop emission - else if (m_WasEmitting) - { + else if (m_WasEmitting) { m_EmissionSound.Stop(); - if (!m_SustainBurstSound) { m_BurstSound.Stop(); } + if (!m_SustainBurstSound) { + m_BurstSound.Stop(); + } m_EndSound.Play(m_Pos); m_WasEmitting = false; } } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws this PEmitter's current graphical representation to a // BITMAP of choice. - void PEmitter::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const - { + void PEmitter::Draw(BITMAP* pTargetBitmap, + const Vector& targetPos, + DrawMode mode, + bool onlyPhysical) const { MOSParticle::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } diff --git a/Source/Entities/PEmitter.h b/Source/Entities/PEmitter.h index 3749780efb..74dea7b259 100644 --- a/Source/Entities/PEmitter.h +++ b/Source/Entities/PEmitter.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,32 +17,27 @@ #include "Emission.h" #include "SoundContainer.h" -namespace RTE -{ - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: PEmitter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A particle MO that creates and emits particle MOs. -// Parent(s): MOSParticle. -// Class history: 02/29/2004 PEmitter created. - -class PEmitter : public MOSParticle { - +namespace RTE { ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations + // Class: PEmitter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A particle MO that creates and emits particle MOs. + // Parent(s): MOSParticle. + // Class history: 02/29/2004 PEmitter created. -public: + class PEmitter : public MOSParticle { - friend struct EntityLuaBindings; + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations - // Concrete allocation and cloning definitions - EntityAllocation(PEmitter); - SerializableOverrideMethods; - ClassInfoGetters; + public: + friend struct EntityLuaBindings; + // Concrete allocation and cloning definitions + EntityAllocation(PEmitter); + SerializableOverrideMethods; + ClassInfoGetters; ////////////////////////////////////////////////////////////////////////////////////////// // Constructor: PEmitter @@ -54,544 +48,526 @@ class PEmitter : public MOSParticle { PEmitter() { Clear(); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~PEmitter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a PEmitter object before deletion + // from system memory. + // Arguments: None. - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~PEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a PEmitter object before deletion - // from system memory. - // Arguments: None. - - ~PEmitter() override { Destroy(true); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the PEmitter object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - - int Create() override; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a PEmitter to be identical to another, by deep copy. - // Arguments: A reference to the PEmitter to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - - int Create(const PEmitter &reference); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire PEmitter, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - - void Reset() override { Clear(); MOSParticle::Reset(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - - void Destroy(bool notInherited = false) override; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEmitting - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this PEmitter is currently enabled and emitting. - // Arguments: None. - // Return value: Whether it's emitting or not. - - bool IsEmitting() const { return m_EmitEnabled; } - - /// - /// Returns whether this emitter was emitting last frame. - /// - /// Whether this emitter was emitting last frame. - bool WasEmitting() const { return m_WasEmitting; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetEmissionTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reset the timers of all emissions so they will start/stop at the - // correct relative offsets from now. - // Arguments: None. - // Return value: None. - - void ResetEmissionTimers(); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableEmission - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this PEmitter to start emitting at the set rate, or to stop. - // Arguments: Whether to enable or disable emission. - // Return value: None. - - void EnableEmission(bool enable = true); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EstimateImpulse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the forces this emitter applies on any parent. - // Arguments: Whether to calculate a burst update or not. - // Return value: The approximate impulse generated by the emitter. - - float EstimateImpulse(bool burst = false); - - - /* - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitRate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rate at which this PEmitter emits its particles. - // Arguments: None. - // Return value: A float with the rate in #/min. - - float GetEmitRate() const { return m_PPM; } - + ~PEmitter() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of particles that will be emitted in one shot upon - // a triggered burst of this PEmitter. - // Arguments: None. - // Return value: The number of emitted particles a burst should have. 0 means burst - // are disabled. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the PEmitter object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. - int GetBurstCount() const { return m_BurstSize; } - */ + int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the scale factor that will be applied to the regular spread and - // emission velocity to get the burst particle parameters. - // Arguments: None. - // Return value: The scale factor. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a PEmitter to be identical to another, by deep copy. + // Arguments: A reference to the PEmitter to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. - float GetBurstScale() const { return m_BurstScale; } + int Create(const PEmitter& reference); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire PEmitter, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the angle of direction that the emitted particles will be shot at. - // Arguments: None. - // Return value: A float with the angle in radians. + void Reset() override { + Clear(); + MOSParticle::Reset(); + } - float GetEmitAngle() const { return m_EmitAngle.GetRadAngle(); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneLayer object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitVector - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A vector in the direction, including the rotation of the emitter, that - // the emitted particles will be shot at. - // Arguments: None. - // Return value: A unit vector. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsEmitting + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this PEmitter is currently enabled and emitting. + // Arguments: None. + // Return value: Whether it's emitting or not. - Vector GetEmitVector() const { return Vector(1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } + bool IsEmitting() const { return m_EmitEnabled; } + /// + /// Returns whether this emitter was emitting last frame. + /// + /// Whether this emitter was emitting last frame. + bool WasEmitting() const { return m_WasEmitting; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRecoilVector - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A vector in the opposite direction, including the rotation of the - // emitter, that the emitted particles will be shot at. - // Arguments: None. - // Return value: A unit vector. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ResetEmissionTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reset the timers of all emissions so they will start/stop at the + // correct relative offsets from now. + // Arguments: None. + // Return value: None. - Vector GetRecoilVector() const { return Vector(-1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } + void ResetEmissionTimers(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnableEmission + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this PEmitter to start emitting at the set rate, or to stop. + // Arguments: Whether to enable or disable emission. + // Return value: None. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstSpacing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the BurstSpacing for this emitter. - // Arguments: None. - // Return value: The BurstSpacing in ms. + void EnableEmission(bool enable = true); - float GetBurstSpacing() const { return m_BurstSpacing; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EstimateImpulse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the forces this emitter applies on any parent. + // Arguments: Whether to calculate a burst update or not. + // Return value: The approximate impulse generated by the emitter. - /* - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitSpread - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the angle spread of velocity of the emitted MO's to each side of - // the angle of emission of this PEmitter. - // Arguments: None. - // Return value: A float with the spread in r's. PI/2 would mean that MO's fly out to - // one side only, with the m_EmitAngle defining the middle of that half - // circle. + float EstimateImpulse(bool burst = false); - float GetEmitSpread() const { return m_Spread; } + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitRate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rate at which this PEmitter emits its particles. + // Arguments: None. + // Return value: A float with the rate in #/min. + float GetEmitRate() const { return m_PPM; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitVelMin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the min end of the range the velocity of a particle being emitted - // by this PEmitter can have. - // Arguments: None. - // Return value: A float with the min vel possible for an emitted particle. - float GetEmitVelMin() const { return m_MinVelocity; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBurstCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of particles that will be emitted in one shot upon + // a triggered burst of this PEmitter. + // Arguments: None. + // Return value: The number of emitted particles a burst should have. 0 means burst + // are disabled. + int GetBurstCount() const { return m_BurstSize; } + */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitVelMax - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the max end of the range the velocity of a particle being emitted - // by this PEmitter can have. - // Arguments: None. - // Return value: A float with the max vel possible for an emitted particle. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBurstScale + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the scale factor that will be applied to the regular spread and + // emission velocity to get the burst particle parameters. + // Arguments: None. + // Return value: The scale factor. - float GetEmitVelMax() const { return m_MaxVelocity; } - */ + float GetBurstScale() const { return m_BurstScale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetThrottle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the normalized throttle scalar which controls how to affect the - // emission rate as per the emisison rate range. Depricated for Lua, use - // the Throttle property instead. - // Arguments: None. - // Return value: A float with the normalized throttle scalar. 1.0 means max throttle, - // 0 means normal, -1.0 means least emission rate. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the angle of direction that the emitted particles will be shot at. + // Arguments: None. + // Return value: A float with the angle in radians. - float GetThrottle() const { return m_Throttle; } + float GetEmitAngle() const { return m_EmitAngle.GetRadAngle(); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitVector + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A vector in the direction, including the rotation of the emitter, that + // the emitted particles will be shot at. + // Arguments: None. + // Return value: A unit vector. - /// - /// Gets the adjusted throttle multiplier that is factored into the emission rate of this PEmitter. - /// - /// The throttle strength as a multiplier. - float GetThrottleFactor() const { return LERP(-1.0f, 1.0f, m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, m_Throttle); } + Vector GetEmitVector() const { return Vector(1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } - /// - /// Gets the throttle value that will achieve a given throttle factor that is factored into the emission rate of this AEmitter. - /// - /// The throttle value that will achieve the given throttle factor. - float GetThrottleForThrottleFactor(float throttleFactor) const { return LERP(m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, -1.0f, 1.0f, throttleFactor); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRecoilVector + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A vector in the opposite direction, including the rotation of the + // emitter, that the emitted particles will be shot at. + // Arguments: None. + // Return value: A unit vector. - /* - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitRate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the rate at which this PEmitter emits its particles. - // Arguments: A float with the rate in #/min. - // Return value: None. + Vector GetRecoilVector() const { return Vector(-1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } - void SetEmitRate(const float rate) { m_PPM = rate; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBurstSpacing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the BurstSpacing for this emitter. + // Arguments: None. + // Return value: The BurstSpacing in ms. + float GetBurstSpacing() const { return m_BurstSpacing; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the number of particles that will be emitted in one shot upon - // a triggered burst of this PEmitter. - // Arguments: The number of emitted particles a burst should have. 0 means burst - // are disabled. - // Return value: None. + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitSpread + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the angle spread of velocity of the emitted MO's to each side of + // the angle of emission of this PEmitter. + // Arguments: None. + // Return value: A float with the spread in r's. PI/2 would mean that MO's fly out to + // one side only, with the m_EmitAngle defining the middle of that half + // circle. - void SetBurstCount(const int count) { m_BurstSize = count; } - */ + float GetEmitSpread() const { return m_Spread; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the scale factor that will be applied to the regular spread and - // emission velocity to get the burst particle parameters. - // Arguments: The scale factor. - // Return value: None. - void SetBurstScale(const float scale) { m_BurstScale = scale; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitVelMin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the min end of the range the velocity of a particle being emitted + // by this PEmitter can have. + // Arguments: None. + // Return value: A float with the min vel possible for an emitted particle. + float GetEmitVelMin() const { return m_MinVelocity; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstSpacing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the BurstSpacing for this emitter. - // Arguments: The BurstSpacing in ms. - // Return value: None. - void SetBurstSpacing(const float spacing) { m_BurstSpacing = spacing; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEmitVelMax + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the max end of the range the velocity of a particle being emitted + // by this PEmitter can have. + // Arguments: None. + // Return value: A float with the max vel possible for an emitted particle. + float GetEmitVelMax() const { return m_MaxVelocity; } + */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFlashScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the display scale factor of the flash effect. This is purely - // visual. - // Arguments: The scale factor of the flash draw. - // Return value: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetThrottle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the normalized throttle scalar which controls how to affect the + // emission rate as per the emisison rate range. Depricated for Lua, use + // the Throttle property instead. + // Arguments: None. + // Return value: A float with the normalized throttle scalar. 1.0 means max throttle, + // 0 means normal, -1.0 means least emission rate. - void SetFlashScale(float flashScale = 1.0f) { m_FlashScale = flashScale; } + float GetThrottle() const { return m_Throttle; } + /// + /// Gets the adjusted throttle multiplier that is factored into the emission rate of this PEmitter. + /// + /// The throttle strength as a multiplier. + float GetThrottleFactor() const { return LERP(-1.0f, 1.0f, m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, m_Throttle); } - /// - /// Gets the display scale factor of the flash effect. This is purely visual. - /// - /// The scale factor of the flash draw. - float GetFlashScale() const { return m_FlashScale; } + /// + /// Gets the throttle value that will achieve a given throttle factor that is factored into the emission rate of this AEmitter. + /// + /// The throttle value that will achieve the given throttle factor. + float GetThrottleForThrottleFactor(float throttleFactor) const { return LERP(m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, -1.0f, 1.0f, throttleFactor); } + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitRate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the rate at which this PEmitter emits its particles. + // Arguments: A float with the rate in #/min. + // Return value: None. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the angle of direction that the emitted particles will be shot at. - // Arguments: A float with the angle in radians. - // Return value: None. + void SetEmitRate(const float rate) { m_PPM = rate; } - void SetEmitAngle(const float angle) { m_EmitAngle.SetRadAngle(angle); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBurstCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the number of particles that will be emitted in one shot upon + // a triggered burst of this PEmitter. + // Arguments: The number of emitted particles a burst should have. 0 means burst + // are disabled. + // Return value: None. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetThrottle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the normalized throttle scalar which controls how to affect the - // emission rate as per the emisison rate range. - // Arguments: A float with the normalized throttle scalar. 1.0 means max throttle, - // 0 means normal, -1.0 means least emission rate. - // Return value: None. + void SetBurstCount(const int count) { m_BurstSize = count; } + */ - void SetThrottle(float throttle) { m_Throttle = throttle > 1.0f ? 1.0f : (throttle < -1.0f ? -1.0f : throttle); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBurstScale + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the scale factor that will be applied to the regular spread and + // emission velocity to get the burst particle parameters. + // Arguments: The scale factor. + // Return value: None. - /* - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitSpread - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the angle spread of velocity of the emitted MO's to each side of - // angle of emission of this PEmitter. - // Arguments: A float with the spread in r's. PI/2 would mean that MO's fly out to - // one side only, with the m_EmitAngle defining the middle of that half - // circle. - // Return value: None. + void SetBurstScale(const float scale) { m_BurstScale = scale; } - void SetEmitSpread(const float spread) { m_Spread = spread; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBurstSpacing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the BurstSpacing for this emitter. + // Arguments: The BurstSpacing in ms. + // Return value: None. + void SetBurstSpacing(const float spacing) { m_BurstSpacing = spacing; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitVelMin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the min end of the range the velocity of a particle being emitted - // by this PEmitter can have. - // Arguments: A float with the min vel possible for an emitted particle. - // Return value: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetFlashScale + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the display scale factor of the flash effect. This is purely + // visual. + // Arguments: The scale factor of the flash draw. + // Return value: None. - void SetEmitVelMin(const float minVel) { m_MinVelocity = minVel; } + void SetFlashScale(float flashScale = 1.0f) { m_FlashScale = flashScale; } + /// + /// Gets the display scale factor of the flash effect. This is purely visual. + /// + /// The scale factor of the flash draw. + float GetFlashScale() const { return m_FlashScale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitVelMax - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the max end of the range the velocity of a particle being emitted - // by this PEmitter can have. - // Arguments: A float with the max vel possible for an emitted particle. - // Return value: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the angle of direction that the emitted particles will be shot at. + // Arguments: A float with the angle in radians. + // Return value: None. - void SetEmitVelMax(const float maxVel) { m_MaxVelocity = maxVel; } - */ + void SetEmitAngle(const float angle) { m_EmitAngle.SetRadAngle(angle); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TriggerBurst - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Triggers a one-shot burst of emissions in the number that has - // previously been set. The burst will happen during the next Update of - // this PEmitter. - // Arguments: None. - // Return value: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetThrottle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the normalized throttle scalar which controls how to affect the + // emission rate as per the emisison rate range. + // Arguments: A float with the normalized throttle scalar. 1.0 means max throttle, + // 0 means normal, -1.0 means least emission rate. + // Return value: None. - void TriggerBurst() { m_BurstTriggered = true; } + void SetThrottle(float throttle) { m_Throttle = throttle > 1.0f ? 1.0f : (throttle < -1.0f ? -1.0f : throttle); } + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitSpread + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the angle spread of velocity of the emitted MO's to each side of + // angle of emission of this PEmitter. + // Arguments: A float with the spread in r's. PI/2 would mean that MO's fly out to + // one side only, with the m_EmitAngle defining the middle of that half + // circle. + // Return value: None. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CanTriggerBurst - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if it is possible to trigger a one-shot burst of emissions during - // the next Update of this PEmitter. - // Arguments: None. - // Return value: If it is possible to trigger a burst. + void SetEmitSpread(const float spread) { m_Spread = spread; } - bool CanTriggerBurst() { if (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing)) return true; return false; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitVelMin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the min end of the range the velocity of a particle being emitted + // by this PEmitter can have. + // Arguments: A float with the min vel possible for an emitted particle. + // Return value: None. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsSetToBurst - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this PEmitter is set to burst next update or not. - // Arguments: None. - // Return value: Whether a burst is gonna happen or not.. + void SetEmitVelMin(const float minVel) { m_MinVelocity = minVel; } - bool IsSetToBurst() const { return m_BurstTriggered; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEmitVelMax + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the max end of the range the velocity of a particle being emitted + // by this PEmitter can have. + // Arguments: A float with the max vel possible for an emitted particle. + // Return value: None. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AlarmOnEmit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registers a new AlarmEvent if this emitter has a loudness above zero. - // Arguments: Team that will ignore this AlarmEvent. - // Return value: None. + void SetEmitVelMax(const float maxVel) { m_MaxVelocity = maxVel; } + */ - void AlarmOnEmit(int Team) const { if (m_LoudnessOnEmit > 0) g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, Team, m_LoudnessOnEmit)); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TriggerBurst + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Triggers a one-shot burst of emissions in the number that has + // previously been set. The burst will happen during the next Update of + // this PEmitter. + // Arguments: None. + // Return value: None. + void TriggerBurst() { m_BurstTriggered = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ResetAllTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resest all the timers used by this. Can be emitters, etc. This is to - // prevent backed up emissions to come out all at once while this has been - // held dormant in an inventory. - // Arguments: None. - // Return value: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CanTriggerBurst + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if it is possible to trigger a one-shot burst of emissions during + // the next Update of this PEmitter. + // Arguments: None. + // Return value: If it is possible to trigger a burst. - void ResetAllTimers() override { m_BurstTimer.Reset(); m_LastEmitTmr.Reset(); } + bool CanTriggerBurst() { + if (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing)) + return true; + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsSetToBurst + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this PEmitter is set to burst next update or not. + // Arguments: None. + // Return value: Whether a burst is gonna happen or not.. - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. + bool IsSetToBurst() const { return m_BurstTriggered; } - void Update() override; - void PostUpdate() override { MOSParticle::PostUpdate(); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AlarmOnEmit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Registers a new AlarmEvent if this emitter has a loudness above zero. + // Arguments: Team that will ignore this AlarmEvent. + // Return value: None. + void AlarmOnEmit(int Team) const { + if (m_LoudnessOnEmit > 0) + g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, Team, m_LoudnessOnEmit)); + } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this PEmitter's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - /// - /// Gets the number of emissions left before emitter is disabled. - /// - /// The number of emissions left before emitter is disabled. - long GetEmitCountLimit() const { return m_EmitCountLimit; } - - /// - /// Sets the number of emissions left before emitter is disabled. - /// - /// New number of emissions left. - void SetEmitCountLimit(long newValue) { m_EmitCountLimit = newValue; } - - /// - /// Returns whether this emitter just started emitting this frame. - /// - /// Whether this emitter just started emitting this frame. - bool JustStartedEmitting() const { return !m_WasEmitting && m_EmitEnabled; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ResetAllTimers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resest all the timers used by this. Can be emitters, etc. This is to + // prevent backed up emissions to come out all at once while this has been + // held dormant in an inventory. + // Arguments: None. + // Return value: None. - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - - // The list of MO instances that get emitted - std::vector m_EmissionList; - // Sounds - SoundContainer m_EmissionSound; - SoundContainer m_BurstSound; - SoundContainer m_EndSound; - // Whether emitting is currently enabled or not. - bool m_EmitEnabled; - // Whether or not the it was emitting last frame or not. - bool m_WasEmitting; - // The number of emissions emitted since emission was last enabled - long m_EmitCount; - // The max number of emissions to emit per emit being enabled - long m_EmitCountLimit; - float m_NegativeThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is negative. Relative to the absolute throttle value. - float m_PositiveThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is positive. Relative to the absolute throttle value. - // The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. - float m_Throttle; - // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. - bool m_EmissionsIgnoreThis; - // The scale factor that will be applied to the regular spread and emission - // velocity to get the the burst particle parameters. - float m_BurstScale; - // Indicates that a burst is set to happen during the next Update. - bool m_BurstTriggered; - // The shortest possible time between bursts, in ms - float m_BurstSpacing; - // Measures the shortest possible time between bursts - Timer m_BurstTimer; - // The angle of the direction the emitted particles will head in. - // The m_Roataion of this PEmitter will be added to this angle. - Matrix m_EmitAngle; - // Offset of the emission point from this' sprite center, which gets rotated with this - Vector m_EmissionOffset; - // Timer for timing how long ago the last particle was emitted. 0 means no limit. - Timer m_LastEmitTmr; - // Flash offset. - // Vector m_FlashOff; nah dont need it - // Flash display scale - float m_FlashScale; - // How large impulse this emitter generates when bursting - float m_AvgBurstImpulse; - // How large impulse this emitter generates when firing - float m_AvgImpulse; - // How far this is audiable (in screens) when emitting as a jetpack or craft engine - float m_LoudnessOnEmit; - // Whether to only display flash on bursts, and not on any emission frame. - bool m_FlashOnlyOnBurst; - // Whether the burst sound should always play until completion, or whether it stops when this emitter stops emitting - bool m_SustainBurstSound; - // Whether the burst sound follows the emitter - bool m_BurstSoundFollowsEmitter; + void ResetAllTimers() override { + m_BurstTimer.Reset(); + m_LastEmitTmr.Reset(); + } - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this MovableObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. -private: + void Update() override; + void PostUpdate() override { MOSParticle::PostUpdate(); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this PEmitter's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // indicator arrows or hovering HUD text and so on. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + + /// + /// Gets the number of emissions left before emitter is disabled. + /// + /// The number of emissions left before emitter is disabled. + long GetEmitCountLimit() const { return m_EmitCountLimit; } + + /// + /// Sets the number of emissions left before emitter is disabled. + /// + /// New number of emissions left. + void SetEmitCountLimit(long newValue) { m_EmitCountLimit = newValue; } + + /// + /// Returns whether this emitter just started emitting this frame. + /// + /// Whether this emitter just started emitting this frame. + bool JustStartedEmitting() const { return !m_WasEmitting && m_EmitEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this PEmitter, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + + // The list of MO instances that get emitted + std::vector m_EmissionList; + // Sounds + SoundContainer m_EmissionSound; + SoundContainer m_BurstSound; + SoundContainer m_EndSound; + // Whether emitting is currently enabled or not. + bool m_EmitEnabled; + // Whether or not the it was emitting last frame or not. + bool m_WasEmitting; + // The number of emissions emitted since emission was last enabled + long m_EmitCount; + // The max number of emissions to emit per emit being enabled + long m_EmitCountLimit; + float m_NegativeThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is negative. Relative to the absolute throttle value. + float m_PositiveThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is positive. Relative to the absolute throttle value. + // The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. + float m_Throttle; + // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. + bool m_EmissionsIgnoreThis; + // The scale factor that will be applied to the regular spread and emission + // velocity to get the the burst particle parameters. + float m_BurstScale; + // Indicates that a burst is set to happen during the next Update. + bool m_BurstTriggered; + // The shortest possible time between bursts, in ms + float m_BurstSpacing; + // Measures the shortest possible time between bursts + Timer m_BurstTimer; + // The angle of the direction the emitted particles will head in. + // The m_Roataion of this PEmitter will be added to this angle. + Matrix m_EmitAngle; + // Offset of the emission point from this' sprite center, which gets rotated with this + Vector m_EmissionOffset; + // Timer for timing how long ago the last particle was emitted. 0 means no limit. + Timer m_LastEmitTmr; + // Flash offset. + // Vector m_FlashOff; nah dont need it + // Flash display scale + float m_FlashScale; + // How large impulse this emitter generates when bursting + float m_AvgBurstImpulse; + // How large impulse this emitter generates when firing + float m_AvgImpulse; + // How far this is audiable (in screens) when emitting as a jetpack or craft engine + float m_LoudnessOnEmit; + // Whether to only display flash on bursts, and not on any emission frame. + bool m_FlashOnlyOnBurst; + // Whether the burst sound should always play until completion, or whether it stops when this emitter stops emitting + bool m_SustainBurstSound; + // Whether the burst sound follows the emitter + bool m_BurstSoundFollowsEmitter; - void Clear(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this PEmitter, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. - // Disallow the use of some implicit methods. - PEmitter(const PEmitter &reference) = delete; - PEmitter & operator=(const PEmitter &rhs) = delete; + void Clear(); -}; + // Disallow the use of some implicit methods. + PEmitter(const PEmitter& reference) = delete; + PEmitter& operator=(const PEmitter& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/PieMenu.cpp b/Source/Entities/PieMenu.cpp index 3246108465..d25f8bbbb7 100644 --- a/Source/Entities/PieMenu.cpp +++ b/Source/Entities/PieMenu.cpp @@ -17,35 +17,31 @@ namespace RTE { ConcreteClassInfo(PieMenu, Entity, 20); const std::unordered_map PieMenu::c_IconSeparatorModeMap = { - {"Line", IconSeparatorMode::Line}, - {"Circle", IconSeparatorMode::Circle}, - {"Square", IconSeparatorMode::Square} - }; + {"Line", IconSeparatorMode::Line}, + {"Circle", IconSeparatorMode::Circle}, + {"Square", IconSeparatorMode::Square}}; const std::unordered_map PieMenu::c_ControlStateDirections = { - {ControlState::PRESS_UP, Directions::Up}, - {ControlState::PRESS_DOWN, Directions::Down}, - {ControlState::PRESS_LEFT, Directions::Left}, - {ControlState::PRESS_RIGHT, Directions::Right} - }; + {ControlState::PRESS_UP, Directions::Up}, + {ControlState::PRESS_DOWN, Directions::Down}, + {ControlState::PRESS_LEFT, Directions::Left}, + {ControlState::PRESS_RIGHT, Directions::Right}}; const std::unordered_map PieMenu::c_OppositeDirections = { - {Directions::Up, Directions::Down}, - {Directions::Down, Directions::Up}, - {Directions::Left, Directions::Right}, - {Directions::Right, Directions::Left} - }; + {Directions::Up, Directions::Down}, + {Directions::Down, Directions::Up}, + {Directions::Left, Directions::Right}, + {Directions::Right, Directions::Left}}; const std::unordered_map PieMenu::c_CounterClockwiseDirections = { - {Directions::Up, Directions::Left}, - {Directions::Down, Directions::Right}, - {Directions::Left, Directions::Down}, - {Directions::Right, Directions::Up} - }; + {Directions::Up, Directions::Left}, + {Directions::Down, Directions::Right}, + {Directions::Left, Directions::Down}, + {Directions::Right, Directions::Up}}; - BITMAP * PieMenu::s_CursorBitmap = nullptr; + BITMAP* PieMenu::s_CursorBitmap = nullptr; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::Clear() { m_LargeFont = nullptr; @@ -97,21 +93,27 @@ namespace RTE { m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int PieMenu::Create() { - if (!s_CursorBitmap) { s_CursorBitmap = ContentFile("Base.rte/GUIs/PieMenus/PieCursor.png").GetAsBitmap(); } + if (!s_CursorBitmap) { + s_CursorBitmap = ContentFile("Base.rte/GUIs/PieMenus/PieCursor.png").GetAsBitmap(); + } - if (!m_LargeFont) { m_LargeFont = g_FrameMan.GetLargeFont(); } + if (!m_LargeFont) { + m_LargeFont = g_FrameMan.GetLargeFont(); + } - if (!m_BGBitmap) { RecreateBackgroundBitmaps(); } + if (!m_BGBitmap) { + RecreateBackgroundBitmaps(); + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieMenu::Create(const PieMenu &reference) { + int PieMenu::Create(const PieMenu& reference) { Entity::Create(reference); m_LargeFont = reference.m_LargeFont; @@ -140,7 +142,7 @@ namespace RTE { for (int i = 0; i < m_PieQuadrants.size(); i++) { m_PieQuadrants[i].Create(reference.m_PieQuadrants[i], &reference, this); - for (const PieSlice *pieSlice : m_PieQuadrants[i].GetFlattenedPieSlices()) { + for (const PieSlice* pieSlice: m_PieQuadrants[i].GetFlattenedPieSlices()) { if (pieSlice->GetOriginalSource() != this) { m_PieQuadrants[i].RemovePieSlice(pieSlice); } @@ -158,21 +160,23 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::Destroy(bool notInherited) { - if (!notInherited) { Entity::Destroy(); } + if (!notInherited) { + Entity::Destroy(); + } destroy_bitmap(m_BGBitmap); destroy_bitmap(m_BGRotationBitmap); destroy_bitmap(m_BGPieSlicesWithSubPieMenuBitmap); Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieMenu::ReadProperty(const std::string_view &propName, Reader &reader) { + int PieMenu::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("IconSeparatorMode", { std::string iconSeparatorModeString = reader.ReadPropValue(); auto itr = c_IconSeparatorModeMap.find(iconSeparatorModeString); @@ -181,7 +185,7 @@ namespace RTE { } else { try { m_IconSeparatorMode = static_cast(std::stoi(iconSeparatorModeString)); - } catch (const std::invalid_argument &) { + } catch (const std::invalid_argument&) { reader.ReportError("IconSeparatorMode " + iconSeparatorModeString + " is invalid."); } } @@ -197,7 +201,7 @@ namespace RTE { if (m_CurrentPieSlices.size() == 4 * PieQuadrant::c_PieQuadrantSlotCount) { reader.ReportError("Pie menus cannot have more than " + std::to_string(4 * PieQuadrant::c_PieQuadrantSlotCount) + " slices. Use sub-pie menus to better organize your pie slices."); } - if (!AddPieSlice(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)), this)) { + if (!AddPieSlice(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)), this)) { reader.ReportError("Tried to add pie slice but that direction was full. Set direction to None if you don't care where the pie slice ends up."); } }); @@ -205,9 +209,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieMenu::Save(Writer &writer) const { + int PieMenu::Save(Writer& writer) const { Entity::Save(writer); writer.NewPropertyWithValue("IconSeparatorMode", static_cast(m_IconSeparatorMode)); @@ -219,20 +223,24 @@ namespace RTE { writer.NewPropertyWithValue("BackgroundBorderColor", m_BackgroundBorderColor); writer.NewPropertyWithValue("SelectedItemBackgroundColor", m_SelectedItemBackgroundColor); - for (const PieSlice *pieSlice : m_CurrentPieSlices) { - if (pieSlice->GetOriginalSource() == m_Owner) { writer.NewPropertyWithValue("AddPieSlice", pieSlice); } + for (const PieSlice* pieSlice: m_CurrentPieSlices) { + if (pieSlice->GetOriginalSource() == m_Owner) { + writer.NewPropertyWithValue("AddPieSlice", pieSlice); + } } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::SetOwner(Actor *newOwner) { + void PieMenu::SetOwner(Actor* newOwner) { RTEAssert((newOwner == nullptr) ? true : (newOwner->GetPieMenu() == this || IsSubPieMenu()), "Tried to set Pie Menu owning Actor to Actor with different Pie Menu."); if (m_Owner) { - for (PieSlice *pieSlice : m_CurrentPieSlices) { - if (pieSlice->GetOriginalSource() == m_Owner) { pieSlice->SetOriginalSource(newOwner); } + for (PieSlice* pieSlice: m_CurrentPieSlices) { + if (pieSlice->GetOriginalSource() == m_Owner) { + pieSlice->SetOriginalSource(newOwner); + } } if (!IsSubPieMenu()) { RemoveWhilePieMenuOpenListener(m_Owner); @@ -244,9 +252,9 @@ namespace RTE { m_Owner = newOwner; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Controller * PieMenu::GetController() const { + Controller* PieMenu::GetController() const { if (m_MenuController) { return m_MenuController; } @@ -257,14 +265,15 @@ namespace RTE { return m_Owner->GetController(); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - void PieMenu::SetPos(const Vector &newPos) { - if (g_SceneMan.ShortestDistance(m_CenterPos, newPos, g_SceneMan.SceneWrapsX()).GetMagnitude() > 2.0F) { m_CenterPos = newPos; } + void PieMenu::SetPos(const Vector& newPos) { + if (g_SceneMan.ShortestDistance(m_CenterPos, newPos, g_SceneMan.SceneWrapsX()).GetMagnitude() > 2.0F) { + m_CenterPos = newPos; + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::SetEnabled(bool enable, bool playSounds) { m_MenuMode = MenuMode::Normal; @@ -277,7 +286,7 @@ namespace RTE { PrepareAnalogCursorForEnableOrDisable(enable); if (playSounds) { - SoundContainer *soundToPlay = enable ? g_GUISound.PieMenuEnterSound() : g_GUISound.PieMenuExitSound(); + SoundContainer* soundToPlay = enable ? g_GUISound.PieMenuEnterSound() : g_GUISound.PieMenuExitSound(); soundToPlay->Play(); } @@ -295,26 +304,26 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const PieSlice * PieMenu::GetActivatedPieSlice() const { + const PieSlice* PieMenu::GetActivatedPieSlice() const { if (m_ActiveSubPieMenu) { return m_ActiveSubPieMenu->GetActivatedPieSlice(); } return m_ActivatedPieSlice; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// PieSlice::SliceType PieMenu::GetPieCommand() const { - const PieSlice *activatedSlice = m_ActiveSubPieMenu ? m_ActiveSubPieMenu->GetActivatedPieSlice() : m_ActivatedPieSlice; + const PieSlice* activatedSlice = m_ActiveSubPieMenu ? m_ActiveSubPieMenu->GetActivatedPieSlice() : m_ActivatedPieSlice; return (activatedSlice == nullptr) ? PieSlice::SliceType::NoType : activatedSlice->GetType(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice * PieMenu::GetFirstPieSliceByPresetName(const std::string &presetName) const { - for (PieSlice *pieSlice : m_CurrentPieSlices) { + PieSlice* PieMenu::GetFirstPieSliceByPresetName(const std::string& presetName) const { + for (PieSlice* pieSlice: m_CurrentPieSlices) { if (pieSlice->GetPresetName() == presetName) { return pieSlice; } @@ -322,10 +331,10 @@ namespace RTE { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice * PieMenu::GetFirstPieSliceByType(PieSlice::SliceType pieSliceType) const { - for (PieSlice *pieSlice : m_CurrentPieSlices) { + PieSlice* PieMenu::GetFirstPieSliceByType(PieSlice::SliceType pieSliceType) const { + for (PieSlice* pieSlice: m_CurrentPieSlices) { if (pieSlice->GetType() == pieSliceType) { return pieSlice; } @@ -333,9 +342,9 @@ namespace RTE { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::AddPieSlice(PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource, bool allowQuadrantOverflow) { + bool PieMenu::AddPieSlice(PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool allowQuadrantOverflow) { Directions pieSliceDirection = pieSliceToAdd->GetDirection(); if (pieSliceDirection != Directions::Any && !m_PieQuadrants.at(pieSliceDirection).m_Enabled) { pieSliceToAdd->SetDirection(Directions::Any); @@ -344,8 +353,8 @@ namespace RTE { bool sliceWasAdded = false; if (pieSliceDirection == Directions::Any) { - PieQuadrant *leastFullPieQuadrant = nullptr; - for (PieQuadrant &pieQuadrant : m_PieQuadrants) { + PieQuadrant* leastFullPieQuadrant = nullptr; + for (PieQuadrant& pieQuadrant: m_PieQuadrants) { if (pieQuadrant.m_Enabled && (!leastFullPieQuadrant || pieQuadrant.GetFlattenedPieSlices().size() < leastFullPieQuadrant->GetFlattenedPieSlices().size())) { leastFullPieQuadrant = &pieQuadrant; } @@ -372,10 +381,10 @@ namespace RTE { return sliceWasAdded; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::AddPieSliceIfPresetNameIsUnique(PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource, bool allowQuadrantOverflow) { - const std::string &pieSlicePresetName = pieSliceToAdd->GetPresetName(); + bool PieMenu::AddPieSliceIfPresetNameIsUnique(PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource, bool allowQuadrantOverflow) { + const std::string& pieSlicePresetName = pieSliceToAdd->GetPresetName(); if (pieSlicePresetName == "None") { return AddPieSlice(dynamic_cast(pieSliceToAdd->Clone()), pieSliceOriginalSource, allowQuadrantOverflow); @@ -383,7 +392,7 @@ namespace RTE { bool pieSliceAlreadyExists = onlyCheckPieSlicesWithSameOriginalSource ? false : GetFirstPieSliceByPresetName(pieSlicePresetName) != nullptr; if (!pieSliceAlreadyExists) { - for (const PieSlice *pieSlice : m_CurrentPieSlices) { + for (const PieSlice* pieSlice: m_CurrentPieSlices) { if (pieSlice->GetOriginalSource() == pieSliceOriginalSource && pieSlice->GetPresetName() == pieSlicePresetName) { pieSliceAlreadyExists = true; break; @@ -398,14 +407,14 @@ namespace RTE { return AddPieSlice(dynamic_cast(pieSliceToAdd->Clone()), pieSliceOriginalSource, allowQuadrantOverflow); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice * PieMenu::RemovePieSlice(const PieSlice *pieSliceToRemove) { - PieSlice *removedPieSlice = nullptr; + PieSlice* PieMenu::RemovePieSlice(const PieSlice* pieSliceToRemove) { + PieSlice* removedPieSlice = nullptr; if (Directions sliceDirection = pieSliceToRemove->GetDirection(); sliceDirection > Directions::None) { if (sliceDirection == Directions::Any) { - for (PieQuadrant &pieQuadrant : m_PieQuadrants) { + for (PieQuadrant& pieQuadrant: m_PieQuadrants) { removedPieSlice = pieQuadrant.RemovePieSlice(pieSliceToRemove); if (removedPieSlice) { break; @@ -415,7 +424,7 @@ namespace RTE { removedPieSlice = m_PieQuadrants.at(sliceDirection).RemovePieSlice(pieSliceToRemove); } if (removedPieSlice) { - if (PieMenu *removedPieSliceSubPieMenu = removedPieSlice->GetSubPieMenu()) { + if (PieMenu* removedPieSliceSubPieMenu = removedPieSlice->GetSubPieMenu()) { removedPieSliceSubPieMenu->SetEnabled(false); removedPieSliceSubPieMenu->SetOwner(nullptr); } @@ -426,66 +435,66 @@ namespace RTE { return removedPieSlice; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::RemovePieSlicesByPresetName(const std::string &presetNameToRemoveBy) { + bool PieMenu::RemovePieSlicesByPresetName(const std::string& presetNameToRemoveBy) { bool anyPieSlicesRemoved = false; - std::vector pieSlicesToRemove; - for (const PieSlice *pieSlice : m_CurrentPieSlices) { + std::vector pieSlicesToRemove; + for (const PieSlice* pieSlice: m_CurrentPieSlices) { if (pieSlice->GetPresetName() == presetNameToRemoveBy) { pieSlicesToRemove.emplace_back(pieSlice); anyPieSlicesRemoved = true; } } - for (const PieSlice *pieSliceToRemove : pieSlicesToRemove) { + for (const PieSlice* pieSliceToRemove: pieSlicesToRemove) { delete RemovePieSlice(pieSliceToRemove); } return anyPieSlicesRemoved; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool PieMenu::RemovePieSlicesByType(PieSlice::SliceType pieSliceTypeToRemoveBy) { bool anyPieSlicesRemoved = false; - std::vector pieSlicesToRemove; - for (const PieSlice *pieSlice : m_CurrentPieSlices) { + std::vector pieSlicesToRemove; + for (const PieSlice* pieSlice: m_CurrentPieSlices) { if (pieSlice->GetType() == pieSliceTypeToRemoveBy) { pieSlicesToRemove.emplace_back(pieSlice); anyPieSlicesRemoved = true; } } - for (const PieSlice *pieSliceToRemove : pieSlicesToRemove) { + for (const PieSlice* pieSliceToRemove: pieSlicesToRemove) { delete RemovePieSlice(pieSliceToRemove); } return anyPieSlicesRemoved; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::RemovePieSlicesByOriginalSource(const Entity *originalSource) { + bool PieMenu::RemovePieSlicesByOriginalSource(const Entity* originalSource) { bool anyPieSlicesRemoved = false; - std::vector pieSlicesToRemove; - for (const PieSlice *pieSlice : m_CurrentPieSlices) { + std::vector pieSlicesToRemove; + for (const PieSlice* pieSlice: m_CurrentPieSlices) { if (pieSlice->GetOriginalSource() == originalSource) { pieSlicesToRemove.emplace_back(pieSlice); anyPieSlicesRemoved = true; } } - for (const PieSlice *pieSliceToRemove : pieSlicesToRemove) { + for (const PieSlice* pieSliceToRemove: pieSlicesToRemove) { delete RemovePieSlice(pieSliceToRemove); } return anyPieSlicesRemoved; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice * PieMenu::ReplacePieSlice(const PieSlice *pieSliceToReplace, PieSlice *replacementPieSlice) { + PieSlice* PieMenu::ReplacePieSlice(const PieSlice* pieSliceToReplace, PieSlice* replacementPieSlice) { if (pieSliceToReplace == nullptr) { return nullptr; } @@ -493,9 +502,9 @@ namespace RTE { return RemovePieSlice(pieSliceToReplace); } - PieSlice *replacedPieSlice = nullptr; + PieSlice* replacedPieSlice = nullptr; - auto DoPieSliceReplacementInPieQuadrant = [&replacedPieSlice, &pieSliceToReplace, &replacementPieSlice](PieQuadrant &pieQuadrant) { + auto DoPieSliceReplacementInPieQuadrant = [&replacedPieSlice, &pieSliceToReplace, &replacementPieSlice](PieQuadrant& pieQuadrant) { if (pieSliceToReplace == pieQuadrant.m_MiddlePieSlice.get()) { replacedPieSlice = pieQuadrant.m_MiddlePieSlice.release(); pieQuadrant.m_MiddlePieSlice = std::unique_ptr(replacementPieSlice); @@ -517,13 +526,13 @@ namespace RTE { if (Directions sliceDirection = pieSliceToReplace->GetDirection(); sliceDirection > Directions::None) { if (sliceDirection == Directions::Any) { - for (PieQuadrant &pieQuadrant : m_PieQuadrants) { + for (PieQuadrant& pieQuadrant: m_PieQuadrants) { if (pieQuadrant.ContainsPieSlice(pieSliceToReplace)) { DoPieSliceReplacementInPieQuadrant(pieQuadrant); break; } } - } else if (PieQuadrant &pieQuadrant = m_PieQuadrants[sliceDirection]; pieQuadrant.ContainsPieSlice(pieSliceToReplace)) { + } else if (PieQuadrant& pieQuadrant = m_PieQuadrants[sliceDirection]; pieQuadrant.ContainsPieSlice(pieSliceToReplace)) { DoPieSliceReplacementInPieQuadrant(pieQuadrant); } } @@ -550,19 +559,19 @@ namespace RTE { } return replacedPieSlice; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::Update() { - const Controller *controller = GetController(); + void PieMenu::Update() { + const Controller* controller = GetController(); m_ActivatedPieSlice = nullptr; if (m_Owner) { SetPos(m_Owner->GetCPUPos()); } else if (m_AffectedObject) { - const Actor *affectedObjectAsActor = dynamic_cast(m_AffectedObject); + const Actor* affectedObjectAsActor = dynamic_cast(m_AffectedObject); SetPos(affectedObjectAsActor ? affectedObjectAsActor->GetCPUPos() : m_AffectedObject->GetPos()); } @@ -579,11 +588,15 @@ namespace RTE { return; } - if (m_AffectedObject && !g_MovableMan.ValidMO(m_AffectedObject)) { m_AffectedObject = nullptr; } + if (m_AffectedObject && !g_MovableMan.ValidMO(m_AffectedObject)) { + m_AffectedObject = nullptr; + } if (m_MenuMode == MenuMode::Normal) { if (IsEnabled()) { - for (const auto &[listenerObject, listenerFunction] : m_WhilePieMenuOpenListeners) { listenerFunction(); } + for (const auto& [listenerObject, listenerFunction]: m_WhilePieMenuOpenListeners) { + listenerFunction(); + } bool anyInput = false; bool skipInputBecauseActiveSubPieMenuWasJustDisabled = false; @@ -607,7 +620,7 @@ namespace RTE { bool shouldClearHoveredSlice = controller->IsState(ControlState::PIE_MENU_ACTIVE_ANALOG); // If a keyboard-only sub-PieMenu is exited by going off the sides, the parent PieMenu should handle input so the next PieSlice can be naturally stepped to. if (activeSubPieMenuDirection != Directions::None) { - for (const auto &[controlState, controlStateDirection] : c_ControlStateDirections) { + for (const auto& [controlState, controlStateDirection]: c_ControlStateDirections) { if (controlStateDirection == c_OppositeDirections.at(activeSubPieMenuDirection) && controller->IsState(controlState)) { shouldClearHoveredSlice = true; break; @@ -638,17 +651,23 @@ namespace RTE { } } - if (m_HoveredPieSlice && m_EnabledState != EnabledState::Disabled && !m_ActiveSubPieMenu) { UpdateSliceActivation(); } + if (m_HoveredPieSlice && m_EnabledState != EnabledState::Disabled && !m_ActiveSubPieMenu) { + UpdateSliceActivation(); + } - if (!IsSubPieMenu()) { SetEnabled(controller->IsState(ControlState::PIE_MENU_ACTIVE)); } + if (!IsSubPieMenu()) { + SetEnabled(controller->IsState(ControlState::PIE_MENU_ACTIVE)); + } } - if (m_BGBitmapNeedsRedrawing && m_EnabledState != EnabledState::Disabled) { UpdatePredrawnMenuBackgroundBitmap(); } + if (m_BGBitmapNeedsRedrawing && m_EnabledState != EnabledState::Disabled) { + UpdatePredrawnMenuBackgroundBitmap(); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::Draw(BITMAP *targetBitmap, const Vector &targetPos) const { + void PieMenu::Draw(BITMAP* targetBitmap, const Vector& targetPos) const { Vector drawPos; CalculateDrawPosition(targetBitmap, targetPos, drawPos); @@ -663,13 +682,17 @@ namespace RTE { if (m_EnabledState == EnabledState::Enabled) { DrawPieIcons(targetBitmap, drawPos); - if (m_CursorInVisiblePosition) { DrawPieCursorAndPieSliceDescriptions(targetBitmap, drawPos); } + if (m_CursorInVisiblePosition) { + DrawPieCursorAndPieSliceDescriptions(targetBitmap, drawPos); + } } - if (m_ActiveSubPieMenu) { m_ActiveSubPieMenu->Draw(targetBitmap, targetPos); } + if (m_ActiveSubPieMenu) { + m_ActiveSubPieMenu->Draw(targetBitmap, targetPos); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::UpdateWobbling() { float innerRadiusChange = static_cast(m_EnableDisableAnimationTimer.GetElapsedRealTimeMS()) / 6.0F; @@ -687,7 +710,7 @@ namespace RTE { m_EnableDisableAnimationTimer.Reset(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::UpdateEnablingAndDisablingProgress() { m_BGBitmapNeedsRedrawing = true; @@ -703,23 +726,25 @@ namespace RTE { if (IsSubPieMenu() || m_EnableDisableAnimationTimer.IsPastRealMS(c_EnablingDelay)) { m_EnabledState = EnabledState::Disabled; m_CurrentInnerRadius = 0; - if (Actor *affectedObjectAsActor = dynamic_cast(m_AffectedObject)) { affectedObjectAsActor->FlashWhite(); } + if (Actor* affectedObjectAsActor = dynamic_cast(m_AffectedObject)) { + affectedObjectAsActor->FlashWhite(); + } } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::HandleAnalogInput(const Vector &input) { - const Controller *controller = GetController(); - const PieSlice *pieSliceToSelect = nullptr; + bool PieMenu::HandleAnalogInput(const Vector& input) { + const Controller* controller = GetController(); + const PieSlice* pieSliceToSelect = nullptr; if (input.MagnitudeIsGreaterThan(0.5F)) { m_CursorInVisiblePosition = true; float normalizedCursorAngle = NormalizeAngleBetween0And2PI(input.GetAbsRadAngle()); m_CursorAngle = normalizedCursorAngle; - for (const PieSlice *pieSlice : m_CurrentPieSlices) { + for (const PieSlice* pieSlice: m_CurrentPieSlices) { float absolutePieSliceStartAngle = pieSlice->GetStartAngle() + GetRotAngle(); if (AngleWithinRange(m_CursorAngle, absolutePieSliceStartAngle, absolutePieSliceStartAngle + (PieQuadrant::c_PieSliceSlotSize * static_cast(pieSlice->GetSlotCount())))) { pieSliceToSelect = pieSlice; @@ -736,10 +761,10 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool PieMenu::HandleDigitalInput() { - const Controller *controller = GetController(); + const Controller* controller = GetController(); if (!controller) { return false; } @@ -752,16 +777,16 @@ namespace RTE { auto GetPieQuadrantContainingHoveredSlice = [this]() { if (m_HoveredPieSlice) { - for (const PieQuadrant &pieQuadrant : m_PieQuadrants) { + for (const PieQuadrant& pieQuadrant: m_PieQuadrants) { if (pieQuadrant.ContainsPieSlice(m_HoveredPieSlice)) { return &pieQuadrant; } } } - return static_cast(nullptr); + return static_cast(nullptr); }; - auto MoveToPieQuadrant = [this](const PieQuadrant &pieQuadrant, MoveToPieQuadrantMode moveToPieQuadrantMode = MoveToPieQuadrantMode::Middle) { + auto MoveToPieQuadrant = [this](const PieQuadrant& pieQuadrant, MoveToPieQuadrantMode moveToPieQuadrantMode = MoveToPieQuadrantMode::Middle) { if (pieQuadrant.GetFlattenedPieSlices().empty()) { SetHoveredPieSlice(nullptr); m_CursorInVisiblePosition = true; @@ -775,8 +800,8 @@ namespace RTE { } }; - auto StepToNeighbouringPieSlice = [this, &MoveToPieQuadrant](const PieQuadrant *hoveredPieSlicePieQuadrant, Directions controlStateDirection, bool counterClockwiseMovement) { - std::vector flattenedPieSlices = hoveredPieSlicePieQuadrant->GetFlattenedPieSlices(true); + auto StepToNeighbouringPieSlice = [this, &MoveToPieQuadrant](const PieQuadrant* hoveredPieSlicePieQuadrant, Directions controlStateDirection, bool counterClockwiseMovement) { + std::vector flattenedPieSlices = hoveredPieSlicePieQuadrant->GetFlattenedPieSlices(true); auto hoveredSliceIterator = std::find(flattenedPieSlices.begin(), flattenedPieSlices.end(), m_HoveredPieSlice); if ((counterClockwiseMovement && hoveredSliceIterator == flattenedPieSlices.end() - 1) || (!counterClockwiseMovement && hoveredSliceIterator == flattenedPieSlices.begin())) { if (m_PieQuadrants.at(controlStateDirection).m_Enabled) { @@ -789,12 +814,12 @@ namespace RTE { } }; - for (const auto &[controlState, controlStateDirection] : c_ControlStateDirections) { + for (const auto& [controlState, controlStateDirection]: c_ControlStateDirections) { if (controller->IsState(controlState)) { - const PieQuadrant &pieQuadrantAtControlStateDirection = m_PieQuadrants.at(controlStateDirection); + const PieQuadrant& pieQuadrantAtControlStateDirection = m_PieQuadrants.at(controlStateDirection); if (!m_HoveredPieSlice && pieQuadrantAtControlStateDirection.m_Enabled) { MoveToPieQuadrant(pieQuadrantAtControlStateDirection); - } else if (const PieQuadrant *hoveredPieSlicePieQuadrant = GetPieQuadrantContainingHoveredSlice()) { + } else if (const PieQuadrant* hoveredPieSlicePieQuadrant = GetPieQuadrantContainingHoveredSlice()) { if (controlStateDirection == c_OppositeDirections.at(hoveredPieSlicePieQuadrant->m_Direction)) { if (IsSubPieMenu()) { SetEnabled(false); @@ -812,13 +837,13 @@ namespace RTE { } } else { bool hoveredPieSliceIsInPieQuadrantRightSide = (m_HoveredPieSlice == hoveredPieSlicePieQuadrant->m_RightPieSlices[0].get() || m_HoveredPieSlice == hoveredPieSlicePieQuadrant->m_RightPieSlices[1].get()); - std::vector flattenedPieSlices = hoveredPieSlicePieQuadrant->GetFlattenedPieSlices(true); + std::vector flattenedPieSlices = hoveredPieSlicePieQuadrant->GetFlattenedPieSlices(true); auto hoveredPieSliceIterator = std::find(flattenedPieSlices.begin(), flattenedPieSlices.end(), m_HoveredPieSlice); SetHoveredPieSlice(*(hoveredPieSliceIterator + (hoveredPieSliceIsInPieQuadrantRightSide ? 1 : -1)), true); } } else { bool counterClockwiseMovement = controlStateDirection == c_CounterClockwiseDirections.at(hoveredPieSlicePieQuadrant->m_Direction); - const std::array, PieQuadrant::c_PieQuadrantSlotCount / 2> &pieSlicesToMovePieQuadrantsFor = counterClockwiseMovement ? hoveredPieSlicePieQuadrant->m_RightPieSlices : hoveredPieSlicePieQuadrant->m_LeftPieSlices; + const std::array, PieQuadrant::c_PieQuadrantSlotCount / 2>& pieSlicesToMovePieQuadrantsFor = counterClockwiseMovement ? hoveredPieSlicePieQuadrant->m_RightPieSlices : hoveredPieSlicePieQuadrant->m_LeftPieSlices; if (pieQuadrantAtControlStateDirection.m_Enabled && (m_HoveredPieSlice == pieSlicesToMovePieQuadrantsFor[0].get() || m_HoveredPieSlice == pieSlicesToMovePieQuadrantsFor[1].get())) { MoveToPieQuadrant(pieQuadrantAtControlStateDirection); } else { @@ -834,10 +859,10 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::UpdateSliceActivation() { - const Controller *controller = GetController(); + const Controller* controller = GetController(); if (controller && (controller->IsState(ControlState::PRESS_PRIMARY) || (m_HoveredPieSlice != m_AlreadyActivatedPieSlice && controller->IsState(ControlState::RELEASE_SECONDARY)))) { m_HoverTimer.Reset(); m_ActivatedPieSlice = m_HoveredPieSlice->IsEnabled() ? m_HoveredPieSlice : m_ActivatedPieSlice; @@ -846,7 +871,7 @@ namespace RTE { if (m_HoveredPieSlice->GetSubPieMenu() && controller->IsState(ControlState::RELEASE_SECONDARY)) { g_GUISound.UserErrorSound()->Play(); } else { - SoundContainer *soundToPlay = m_HoveredPieSlice->IsEnabled() ? g_GUISound.SlicePickedSound() : g_GUISound.DisabledPickedSound(); + SoundContainer* soundToPlay = m_HoveredPieSlice->IsEnabled() ? g_GUISound.SlicePickedSound() : g_GUISound.DisabledPickedSound(); soundToPlay->Play(); } } @@ -866,29 +891,38 @@ namespace RTE { m_ActiveSubPieMenu->m_HoverTimer.SetRealTimeLimitMS(2000); m_ActiveSubPieMenu->m_HoverTimer.Reset(); } else if (m_ActivatedPieSlice && m_ActivatedPieSlice->GetLuabindFunctionObjectWrapper() && m_ActivatedPieSlice->GetLuabindFunctionObjectWrapper()->GetLuabindObject()) { - if (const MovableObject *scriptTarget = m_Owner ? m_Owner : m_AffectedObject) { - g_LuaMan.GetMasterScriptState().RunScriptFunctionObject(m_ActivatedPieSlice->GetLuabindFunctionObjectWrapper(), "", "", { scriptTarget, this, m_ActivatedPieSlice }); + if (const MovableObject* scriptTarget = m_Owner ? m_Owner : m_AffectedObject) { + g_LuaMan.GetMasterScriptState().RunScriptFunctionObject(m_ActivatedPieSlice->GetLuabindFunctionObjectWrapper(), "", "", {scriptTarget, this, m_ActivatedPieSlice}); } } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::UpdatePredrawnMenuBackgroundBitmap() { int centerX = IsSubPieMenu() ? 0 : m_BGBitmap->w / 2; int centerY = IsSubPieMenu() ? 0 : m_BGBitmap->h / 2; - if (m_DirectionIfSubPieMenu == Directions::Up || m_DirectionIfSubPieMenu == Directions::Left) { centerX = m_BGBitmap->w; } - if (m_DirectionIfSubPieMenu == Directions::Up || m_DirectionIfSubPieMenu == Directions::Right) { centerY = m_BGBitmap->h; } - if (m_DirectionIfSubPieMenu == Directions::Down) { centerX = -2; centerY = -2; } - if (m_DirectionIfSubPieMenu == Directions::Left) { centerY = -2; } + if (m_DirectionIfSubPieMenu == Directions::Up || m_DirectionIfSubPieMenu == Directions::Left) { + centerX = m_BGBitmap->w; + } + if (m_DirectionIfSubPieMenu == Directions::Up || m_DirectionIfSubPieMenu == Directions::Right) { + centerY = m_BGBitmap->h; + } + if (m_DirectionIfSubPieMenu == Directions::Down) { + centerX = -2; + centerY = -2; + } + if (m_DirectionIfSubPieMenu == Directions::Left) { + centerY = -2; + } float subPieMenuRotationOffset = IsSubPieMenu() ? c_QuarterPI : 0; bool pieMenuNeedsToBeDrawnRotated = GetRotAngle() - subPieMenuRotationOffset != 0; - BITMAP *bitmapToDrawTo = pieMenuNeedsToBeDrawnRotated ? m_BGRotationBitmap : m_BGBitmap; + BITMAP* bitmapToDrawTo = pieMenuNeedsToBeDrawnRotated ? m_BGRotationBitmap : m_BGBitmap; bool hasPieSliceWithSubPieMenu = false; - for (const PieSlice *pieSlice : m_CurrentPieSlices) { + for (const PieSlice* pieSlice: m_CurrentPieSlices) { if (pieSlice->GetSubPieMenu()) { hasPieSliceWithSubPieMenu = true; break; @@ -922,22 +956,22 @@ namespace RTE { m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::CalculateDrawPosition(const BITMAP *targetBitmap, const Vector &targetPos, Vector &drawPos) const { + void PieMenu::CalculateDrawPosition(const BITMAP* targetBitmap, const Vector& targetPos, Vector& drawPos) const { drawPos = m_CenterPos - targetPos; if (!targetPos.IsZero()) { - const Box *nearestBox = nullptr; + const Box* nearestBox = nullptr; Box screenBox(targetPos, static_cast(targetBitmap->w), static_cast(targetBitmap->h)); std::list wrappedBoxes; bool withinAnyBox = false; float distance = std::numeric_limits::max(); float shortestDist = std::numeric_limits::max(); - //TODO under what conditions would the pie menu not be on the screen and, if that's the case, would we still want to draw it? Try to remove this in next pass of PieMenu changes, or replace it with more standard wrapping handling. - // Note - offscreen piemenu is used for signaling selectable actors so it's currently desirable. Strategic mode won't want that clutter, so this can probably change then. + // TODO under what conditions would the pie menu not be on the screen and, if that's the case, would we still want to draw it? Try to remove this in next pass of PieMenu changes, or replace it with more standard wrapping handling. + // Note - offscreen piemenu is used for signaling selectable actors so it's currently desirable. Strategic mode won't want that clutter, so this can probably change then. g_SceneMan.WrapBox(screenBox, wrappedBoxes); - for (const Box &wrappedBox : wrappedBoxes) { + for (const Box& wrappedBox: wrappedBoxes) { if (wrappedBox.IsWithinBox(m_CenterPos)) { nearestBox = &wrappedBox; withinAnyBox = true; @@ -971,18 +1005,22 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::DrawPieIcons(BITMAP *targetBitmap, const Vector &drawPos) const { - for (const PieSlice *pieSlice : m_CurrentPieSlices) { - BITMAP *pieSliceIcon = pieSlice->GetAppropriateIcon(pieSlice == m_HoveredPieSlice); + void PieMenu::DrawPieIcons(BITMAP* targetBitmap, const Vector& drawPos) const { + for (const PieSlice* pieSlice: m_CurrentPieSlices) { + BITMAP* pieSliceIcon = pieSlice->GetAppropriateIcon(pieSlice == m_HoveredPieSlice); if (pieSliceIcon) { float pieSliceRotation = NormalizeAngleBetween0And2PI(pieSlice->GetMidAngle() + GetRotAngle()); Vector pieSliceCenteringOffset(1.0F - static_cast(pieSliceIcon->w / 2), 1.0F - static_cast(pieSliceIcon->h / 2)); - if (GetRotAngle() == 0 && pieSlice == m_PieQuadrants[Directions::Right].m_MiddlePieSlice.get()) { pieSliceCenteringOffset.SetX(pieSliceCenteringOffset.GetX() - 1.0F); } - if (GetRotAngle() == 0 && pieSlice == m_PieQuadrants[Directions::Down].m_MiddlePieSlice.get()) { pieSliceCenteringOffset.SetY(pieSliceCenteringOffset.GetY() - 1.0F); } + if (GetRotAngle() == 0 && pieSlice == m_PieQuadrants[Directions::Right].m_MiddlePieSlice.get()) { + pieSliceCenteringOffset.SetX(pieSliceCenteringOffset.GetX() - 1.0F); + } + if (GetRotAngle() == 0 && pieSlice == m_PieQuadrants[Directions::Down].m_MiddlePieSlice.get()) { + pieSliceCenteringOffset.SetY(pieSliceCenteringOffset.GetY() - 1.0F); + } Vector pieSliceIconOffset = Vector(static_cast(m_CurrentInnerRadius + (m_BackgroundThickness / 2) + (pieSlice->GetSubPieMenu() && m_IconSeparatorMode == IconSeparatorMode::Line ? 2 : 0)), 0).RadRotate(pieSliceRotation) + pieSliceCenteringOffset; @@ -999,9 +1037,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::DrawPieCursorAndPieSliceDescriptions(BITMAP *targetBitmap, const Vector &drawPos) const { + void PieMenu::DrawPieCursorAndPieSliceDescriptions(BITMAP* targetBitmap, const Vector& drawPos) const { int nonLineSeparatorCorrection = m_IconSeparatorMode != IconSeparatorMode::Line ? -(m_BackgroundSeparatorSize) : 0; Vector cursorPos = Vector(static_cast(m_CurrentInnerRadius + nonLineSeparatorCorrection), 0.0F).RadRotate(m_CursorAngle); pivot_sprite(targetBitmap, s_CursorBitmap, drawPos.GetFloorIntX() + cursorPos.GetFloorIntX(), drawPos.GetFloorIntY() + cursorPos.GetFloorIntY(), s_CursorBitmap->w / 2, s_CursorBitmap->h / 2, ftofix((m_CursorAngle / c_PI) * -128.0F)); @@ -1022,38 +1060,48 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::RepopulateAndRealignCurrentPieSlices() { m_CurrentPieSlices.clear(); - for (PieQuadrant &pieQuadrant : m_PieQuadrants) { + for (PieQuadrant& pieQuadrant: m_PieQuadrants) { pieQuadrant.RealignPieSlices(); - if (pieQuadrant.m_MiddlePieSlice) { m_CurrentPieSlices.emplace_back(pieQuadrant.m_MiddlePieSlice.get()); } + if (pieQuadrant.m_MiddlePieSlice) { + m_CurrentPieSlices.emplace_back(pieQuadrant.m_MiddlePieSlice.get()); + } } - for (PieQuadrant &pieQuadrant : m_PieQuadrants) { - if (pieQuadrant.m_LeftPieSlices[0]) { m_CurrentPieSlices.emplace_back(pieQuadrant.m_LeftPieSlices[0].get()); } + for (PieQuadrant& pieQuadrant: m_PieQuadrants) { + if (pieQuadrant.m_LeftPieSlices[0]) { + m_CurrentPieSlices.emplace_back(pieQuadrant.m_LeftPieSlices[0].get()); + } } - for (PieQuadrant &pieQuadrant : m_PieQuadrants) { - if (pieQuadrant.m_RightPieSlices[0]) { m_CurrentPieSlices.emplace_back(pieQuadrant.m_RightPieSlices[0].get()); } + for (PieQuadrant& pieQuadrant: m_PieQuadrants) { + if (pieQuadrant.m_RightPieSlices[0]) { + m_CurrentPieSlices.emplace_back(pieQuadrant.m_RightPieSlices[0].get()); + } } - for (PieQuadrant &pieQuadrant : m_PieQuadrants) { - if (pieQuadrant.m_LeftPieSlices[1]) { m_CurrentPieSlices.emplace_back(pieQuadrant.m_LeftPieSlices[1].get()); } + for (PieQuadrant& pieQuadrant: m_PieQuadrants) { + if (pieQuadrant.m_LeftPieSlices[1]) { + m_CurrentPieSlices.emplace_back(pieQuadrant.m_LeftPieSlices[1].get()); + } } - for (PieQuadrant &pieQuadrant : m_PieQuadrants) { - if (pieQuadrant.m_RightPieSlices[1]) { m_CurrentPieSlices.emplace_back(pieQuadrant.m_RightPieSlices[1].get()); } + for (PieQuadrant& pieQuadrant: m_PieQuadrants) { + if (pieQuadrant.m_RightPieSlices[1]) { + m_CurrentPieSlices.emplace_back(pieQuadrant.m_RightPieSlices[1].get()); + } } ExpandPieSliceIntoEmptySpaceIfPossible(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::ExpandPieSliceIntoEmptySpaceIfPossible() { - const std::unordered_map nextDirectionForGivenDirection{ {Directions::Right, Directions::Up}, {Directions::Up, Directions::Left}, {Directions::Left, Directions::Down}, {Directions::Down, Directions::Right} }; + const std::unordered_map nextDirectionForGivenDirection{{Directions::Right, Directions::Up}, {Directions::Up, Directions::Left}, {Directions::Left, Directions::Down}, {Directions::Down, Directions::Right}}; - for (const PieQuadrant &pieQuadrant : m_PieQuadrants) { + for (const PieQuadrant& pieQuadrant: m_PieQuadrants) { if (pieQuadrant.m_Enabled) { - const std::unique_ptr &leftMostPieSlice = !pieQuadrant.m_LeftPieSlices[1] ? pieQuadrant.m_LeftPieSlices[0] : pieQuadrant.m_LeftPieSlices[1]; - PieQuadrant &neighbouringPieQuadrant = m_PieQuadrants.at(nextDirectionForGivenDirection.at(pieQuadrant.m_Direction)); + const std::unique_ptr& leftMostPieSlice = !pieQuadrant.m_LeftPieSlices[1] ? pieQuadrant.m_LeftPieSlices[0] : pieQuadrant.m_LeftPieSlices[1]; + PieQuadrant& neighbouringPieQuadrant = m_PieQuadrants.at(nextDirectionForGivenDirection.at(pieQuadrant.m_Direction)); if (leftMostPieSlice && leftMostPieSlice->GetSlotCount() == 1 && neighbouringPieQuadrant.m_Enabled && neighbouringPieQuadrant.m_SlotsForPieSlices[0] == nullptr) { leftMostPieSlice->SetSlotCount(leftMostPieSlice->GetSlotCount() + 1); @@ -1065,7 +1113,7 @@ namespace RTE { m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::RecreateBackgroundBitmaps() { if (m_BGBitmap) { @@ -1094,16 +1142,16 @@ namespace RTE { m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::DrawBackgroundPieSliceSeparators(BITMAP *backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float subPieMenuRotationOffset) const { + void PieMenu::DrawBackgroundPieSliceSeparators(BITMAP* backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float subPieMenuRotationOffset) const { switch (m_IconSeparatorMode) { case IconSeparatorMode::Line: - for (const PieQuadrant &pieQuadrant : m_PieQuadrants) { + for (const PieQuadrant& pieQuadrant: m_PieQuadrants) { float currentAngle = c_DirectionsToRadiansMap.at(pieQuadrant.m_Direction) - c_QuarterPI + subPieMenuRotationOffset; bool pieQuadrantIsEmpty = !pieQuadrant.m_Enabled || pieQuadrant.GetFlattenedPieSlices().empty(); for (int currentSlot = 0; currentSlot < PieQuadrant::c_PieQuadrantSlotCount;) { - if (const PieSlice *pieSliceInSlot = pieQuadrant.m_SlotsForPieSlices.at(currentSlot)) { + if (const PieSlice* pieSliceInSlot = pieQuadrant.m_SlotsForPieSlices.at(currentSlot)) { // We don't draw lines if the current slot is the first slot, since a line will be drawn by the previous PieQuadrant's last PieSlice, or if it's a 2-size, since that means it's spreading over PieQuadrants and a line will be drawn at the next PieSlice's start. if (currentSlot > 0 || pieSliceInSlot->GetSlotCount() != 2) { currentAngle = pieSliceInSlot->GetStartAngle() + subPieMenuRotationOffset; @@ -1113,13 +1161,15 @@ namespace RTE { continue; } } else { - if (!pieQuadrantIsEmpty && currentSlot == 0) { DrawBackgroundPieSliceSeparator(backgroundBitmapToDrawTo, pieCircleCenterX, pieCircleCenterY, currentAngle, false, false); } + if (!pieQuadrantIsEmpty && currentSlot == 0) { + DrawBackgroundPieSliceSeparator(backgroundBitmapToDrawTo, pieCircleCenterX, pieCircleCenterY, currentAngle, false, false); + } currentAngle += PieQuadrant::c_PieSliceSlotSize; } currentSlot++; } } - for (const PieSlice *pieSlice : m_CurrentPieSlices) { + for (const PieSlice* pieSlice: m_CurrentPieSlices) { if (!pieSlice->GetSubPieMenu()) { Vector floodfillPosition = Vector(static_cast(m_CurrentInnerRadius + (m_BackgroundThickness / 2)), 0).RadRotate(pieSlice->GetMidAngle() + subPieMenuRotationOffset); floodfill(m_BGPieSlicesWithSubPieMenuBitmap, pieCircleCenterX + floodfillPosition.GetFloorIntX(), pieCircleCenterY + floodfillPosition.GetFloorIntY(), ColorKeys::g_MaskColor); @@ -1128,8 +1178,10 @@ namespace RTE { break; case IconSeparatorMode::Circle: case IconSeparatorMode::Square: - for (const PieSlice *pieSlice : m_CurrentPieSlices) { - if (pieSlice->GetType() != PieSlice::SliceType::NoType) { DrawBackgroundPieSliceSeparator(backgroundBitmapToDrawTo, pieCircleCenterX, pieCircleCenterY, pieSlice->GetMidAngle() + subPieMenuRotationOffset, pieSlice == m_HoveredPieSlice && pieSlice->IsEnabled(), pieSlice->GetSubPieMenu()); } + for (const PieSlice* pieSlice: m_CurrentPieSlices) { + if (pieSlice->GetType() != PieSlice::SliceType::NoType) { + DrawBackgroundPieSliceSeparator(backgroundBitmapToDrawTo, pieCircleCenterX, pieCircleCenterY, pieSlice->GetMidAngle() + subPieMenuRotationOffset, pieSlice == m_HoveredPieSlice && pieSlice->IsEnabled(), pieSlice->GetSubPieMenu()); + } } break; default: @@ -1137,9 +1189,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::DrawBackgroundPieSliceSeparator(BITMAP *backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float rotAngle, bool isHoveredPieSlice, bool pieSliceHasSubPieMenu, bool drawHalfSizedSeparator) const { + void PieMenu::DrawBackgroundPieSliceSeparator(BITMAP* backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float rotAngle, bool isHoveredPieSlice, bool pieSliceHasSubPieMenu, bool drawHalfSizedSeparator) const { Vector separatorOffset; int backgroundSeparatorSize = m_BackgroundSeparatorSize; @@ -1147,27 +1199,39 @@ namespace RTE { case IconSeparatorMode::Line: separatorOffset.SetXY(static_cast(m_CurrentInnerRadius + m_BackgroundThickness + backgroundSeparatorSize), 0).RadRotate(rotAngle); line(backgroundBitmapToDrawTo, pieCircleCenterX, pieCircleCenterY, pieCircleCenterX + separatorOffset.GetCeilingIntX(), pieCircleCenterY + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); - if (!drawHalfSizedSeparator) { line(backgroundBitmapToDrawTo, pieCircleCenterX + 1, pieCircleCenterY, pieCircleCenterX + 1 + separatorOffset.GetCeilingIntX(), pieCircleCenterY + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); } + if (!drawHalfSizedSeparator) { + line(backgroundBitmapToDrawTo, pieCircleCenterX + 1, pieCircleCenterY, pieCircleCenterX + 1 + separatorOffset.GetCeilingIntX(), pieCircleCenterY + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); + } line(backgroundBitmapToDrawTo, pieCircleCenterX, pieCircleCenterY + 1, pieCircleCenterX + separatorOffset.GetCeilingIntX(), pieCircleCenterY + 1 + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); - if (!drawHalfSizedSeparator) { line(backgroundBitmapToDrawTo, pieCircleCenterX + 1, pieCircleCenterY + 1, pieCircleCenterX + 1 + separatorOffset.GetCeilingIntX(), pieCircleCenterY + 1 + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); } + if (!drawHalfSizedSeparator) { + line(backgroundBitmapToDrawTo, pieCircleCenterX + 1, pieCircleCenterY + 1, pieCircleCenterX + 1 + separatorOffset.GetCeilingIntX(), pieCircleCenterY + 1 + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); + } if (pieSliceHasSubPieMenu) { separatorOffset.SetXY(static_cast(m_CurrentInnerRadius + m_BackgroundThickness + backgroundSeparatorSize + c_PieSliceWithSubPieMenuExtraThickness), 0).RadRotate(rotAngle); line(m_BGPieSlicesWithSubPieMenuBitmap, pieCircleCenterX, pieCircleCenterY, pieCircleCenterX + separatorOffset.GetCeilingIntX(), pieCircleCenterY + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); - if (!drawHalfSizedSeparator) { line(m_BGPieSlicesWithSubPieMenuBitmap, pieCircleCenterX + 1, pieCircleCenterY, pieCircleCenterX + 1 + separatorOffset.GetCeilingIntX(), pieCircleCenterY + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); } + if (!drawHalfSizedSeparator) { + line(m_BGPieSlicesWithSubPieMenuBitmap, pieCircleCenterX + 1, pieCircleCenterY, pieCircleCenterX + 1 + separatorOffset.GetCeilingIntX(), pieCircleCenterY + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); + } line(m_BGPieSlicesWithSubPieMenuBitmap, pieCircleCenterX, pieCircleCenterY + 1, pieCircleCenterX + separatorOffset.GetCeilingIntX(), pieCircleCenterY + 1 + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); - if (!drawHalfSizedSeparator) { line(m_BGPieSlicesWithSubPieMenuBitmap, pieCircleCenterX + 1, pieCircleCenterY + 1, pieCircleCenterX + 1 + separatorOffset.GetCeilingIntX(), pieCircleCenterY + 1 + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); } + if (!drawHalfSizedSeparator) { + line(m_BGPieSlicesWithSubPieMenuBitmap, pieCircleCenterX + 1, pieCircleCenterY + 1, pieCircleCenterX + 1 + separatorOffset.GetCeilingIntX(), pieCircleCenterY + 1 + separatorOffset.GetCeilingIntY(), m_BackgroundBorderColor); + } } if (isHoveredPieSlice) { separatorOffset.SetXY(static_cast(m_CurrentInnerRadius + (m_BackgroundThickness / 2)), 0).RadRotate(rotAngle - (PieQuadrant::c_PieSliceSlotSize / 2.0F)); floodfill(backgroundBitmapToDrawTo, pieCircleCenterX + separatorOffset.GetFloorIntX(), pieCircleCenterY + separatorOffset.GetFloorIntY(), m_SelectedItemBackgroundColor); - if (pieSliceHasSubPieMenu) { floodfill(m_BGPieSlicesWithSubPieMenuBitmap, pieCircleCenterX + separatorOffset.GetFloorIntX(), pieCircleCenterY + separatorOffset.GetFloorIntY(), m_SelectedItemBackgroundColor); } + if (pieSliceHasSubPieMenu) { + floodfill(m_BGPieSlicesWithSubPieMenuBitmap, pieCircleCenterX + separatorOffset.GetFloorIntX(), pieCircleCenterY + separatorOffset.GetFloorIntY(), m_SelectedItemBackgroundColor); + } } break; case IconSeparatorMode::Circle: case IconSeparatorMode::Square: - if (drawHalfSizedSeparator) { backgroundSeparatorSize /= 2; } + if (drawHalfSizedSeparator) { + backgroundSeparatorSize /= 2; + } separatorOffset.SetXY(static_cast(m_CurrentInnerRadius) + (static_cast(m_BackgroundThickness) / 2.0F), 0).RadRotate(rotAngle); if (pieSliceHasSubPieMenu) { if (m_IconSeparatorMode == IconSeparatorMode::Circle) { @@ -1191,9 +1255,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::SetHoveredPieSlice(const PieSlice *pieSliceToSelect, bool moveCursorIconToSlice) { + bool PieMenu::SetHoveredPieSlice(const PieSlice* pieSliceToSelect, bool moveCursorIconToSlice) { if (pieSliceToSelect == m_HoveredPieSlice) { return false; } @@ -1208,7 +1272,7 @@ namespace RTE { m_CursorAngle = GetRotAngle() + m_HoveredPieSlice->GetMidAngle(); } - SoundContainer *soundToPlay = pieSliceToSelect->IsEnabled() ? g_GUISound.HoverChangeSound() : g_GUISound.HoverDisabledSound(); + SoundContainer* soundToPlay = pieSliceToSelect->IsEnabled() ? g_GUISound.HoverChangeSound() : g_GUISound.HoverDisabledSound(); soundToPlay->Play(); } else { m_CursorInVisiblePosition = false; @@ -1216,15 +1280,15 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::PreparePieSliceSubPieMenuForUse(const PieSlice *pieSliceWithSubPieMenu) const { - PieMenu *subPieMenu = pieSliceWithSubPieMenu->GetSubPieMenu(); + bool PieMenu::PreparePieSliceSubPieMenuForUse(const PieSlice* pieSliceWithSubPieMenu) const { + PieMenu* subPieMenu = pieSliceWithSubPieMenu->GetSubPieMenu(); subPieMenu->m_ActivatedPieSlice = nullptr; if (!subPieMenu || subPieMenu->IsSubPieMenu()) { return false; } - for (const PieQuadrant &pieQuadrant : m_PieQuadrants) { + for (const PieQuadrant& pieQuadrant: m_PieQuadrants) { if (pieQuadrant.ContainsPieSlice(pieSliceWithSubPieMenu)) { subPieMenu->m_DirectionIfSubPieMenu = pieQuadrant.m_Direction; break; @@ -1232,38 +1296,42 @@ namespace RTE { } float subPieMenuRotAngle = NormalizeAngleBetween0And2PI(pieSliceWithSubPieMenu->GetMidAngle() - c_DirectionsToRadiansMap.at(subPieMenu->m_DirectionIfSubPieMenu) + GetRotAngle()); - if (subPieMenuRotAngle < 0.0001F) { subPieMenuRotAngle = 0; } + if (subPieMenuRotAngle < 0.0001F) { + subPieMenuRotAngle = 0; + } subPieMenu->SetRotAngle(subPieMenuRotAngle); subPieMenu->SetFullInnerRadius(m_FullInnerRadius + std::max(m_BackgroundThickness * 2, m_BackgroundSeparatorSize * 3) + c_PieSliceWithSubPieMenuExtraThickness); - for (PieQuadrant &pieQuadrant : subPieMenu->m_PieQuadrants) { - if (pieQuadrant.m_Direction != subPieMenu->m_DirectionIfSubPieMenu) { pieQuadrant.m_Enabled = false; } + for (PieQuadrant& pieQuadrant: subPieMenu->m_PieQuadrants) { + if (pieQuadrant.m_Direction != subPieMenu->m_DirectionIfSubPieMenu) { + pieQuadrant.m_Enabled = false; + } } - std::vector existingPieSlices = subPieMenu->GetPieSlices(); - std::vector pieSlicesToReadd; + std::vector existingPieSlices = subPieMenu->GetPieSlices(); + std::vector pieSlicesToReadd; pieSlicesToReadd.reserve(existingPieSlices.size()); - for (const PieSlice *existingPieSlice : existingPieSlices) { + for (const PieSlice* existingPieSlice: existingPieSlices) { pieSlicesToReadd.emplace_back(subPieMenu->RemovePieSlice(existingPieSlice)); pieSlicesToReadd.back()->SetDirection(Directions::Any); } - for (PieSlice *pieSlice : pieSlicesToReadd) { + for (PieSlice* pieSlice: pieSlicesToReadd) { subPieMenu->AddPieSlice(pieSlice, subPieMenu); } return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieMenu::PrepareAnalogCursorForEnableOrDisable(bool enable) const { - if (Controller *controller = GetController(); controller && (controller->IsMouseControlled() || controller->IsGamepadControlled())) { + if (Controller* controller = GetController(); controller && (controller->IsMouseControlled() || controller->IsGamepadControlled())) { if (!IsSubPieMenu()) { g_UInputMan.SetMouseValueMagnitude(0, controller->GetPlayer()); controller->m_AnalogCursor.Reset(); } else if (enable) { controller->SetAnalogCursorAngleLimits(GetRotAngle() + c_DirectionsToRadiansMap.at(m_DirectionIfSubPieMenu) - c_QuarterPI + (PieQuadrant::c_PieSliceSlotSize / 2.0F), GetRotAngle() + c_DirectionsToRadiansMap.at(m_DirectionIfSubPieMenu) + c_QuarterPI - (PieQuadrant::c_PieSliceSlotSize / 2.0F)); if (!controller->m_AnalogCursor.IsZero()) { - float mouseAngleToSet = GetRotAngle() + (m_HoveredPieSlice ? m_HoveredPieSlice->GetMidAngle() : c_DirectionsToRadiansMap.at(m_DirectionIfSubPieMenu)); + float mouseAngleToSet = GetRotAngle() + (m_HoveredPieSlice ? m_HoveredPieSlice->GetMidAngle() : c_DirectionsToRadiansMap.at(m_DirectionIfSubPieMenu)); g_UInputMan.SetMouseValueAngle(mouseAngleToSet, controller->GetPlayer()); g_UInputMan.SetMouseValueMagnitude(0.75F, controller->GetPlayer()); controller->m_AnalogCursor.SetAbsRadAngle(mouseAngleToSet); @@ -1274,4 +1342,4 @@ namespace RTE { } } } -} +} // namespace RTE diff --git a/Source/Entities/PieMenu.h b/Source/Entities/PieMenu.h index fe5b941b27..44ea5d8ff9 100644 --- a/Source/Entities/PieMenu.h +++ b/Source/Entities/PieMenu.h @@ -20,16 +20,17 @@ namespace RTE { friend class PieSlice; public: - EntityAllocation(PieMenu) - SerializableOverrideMethods - ClassInfoGetters + SerializableOverrideMethods + ClassInfoGetters #pragma region Creation - /// - /// Constructor method used to instantiate a PieMenu object in system memory. Create() should be called before using the object. - /// - PieMenu() { Clear(); } + /// + /// Constructor method used to instantiate a PieMenu object in system memory. Create() should be called before using the object. + /// + PieMenu() { + Clear(); + } /// /// Makes the PieMenu object ready for use. @@ -42,14 +43,17 @@ namespace RTE { /// /// The Actor which should act as the owner for this PieMenu. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(Actor *owner) { SetOwner(owner); return Create(); } + int Create(Actor* owner) { + SetOwner(owner); + return Create(); + } /// /// Creates a PieMenu to be identical to another, by deep copy. /// /// A reference to the Attachable to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const PieMenu &reference); + int Create(const PieMenu& reference); #pragma endregion #pragma region Destruction @@ -67,7 +71,10 @@ namespace RTE { /// /// Resets the entire PieMenu, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Entity::Reset(); } + void Reset() override { + Clear(); + Entity::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -75,37 +82,37 @@ namespace RTE { /// Gets the owner Actor of this PieMenu. Ownership is NOT transferred! /// /// The owner Actor of this PieMenu. Ownership is NOT transferred! - const Actor * GetOwner() const { return m_Owner; } + const Actor* GetOwner() const { return m_Owner; } /// /// Sets the owner Actor of this PieMenu, ensuring this is that Actor's PieMenu, and updates PieSlice sources accordingly. Ownership is NOT transferred! /// /// The new owner Actor for this PieMenu. Ownership is NOT transferred! - void SetOwner(Actor *newOwner); + void SetOwner(Actor* newOwner); /// /// Gets the currently in-use Controller for this PieMenu - either the menu Controller if there's one set, or the owning Actor's Controller. Ownership IS NOT transferred! /// /// The currently in-use Controller for this PieMenu. - Controller * GetController() const; + Controller* GetController() const; /// /// Sets the menu Controller used by this PieMenu, separate from any Controller on its owner Actor. Ownership is NOT transferred! /// /// The new Controller for this PieMenu. Ownership is NOT transferred! - void SetMenuController(Controller *menuController) { m_MenuController = menuController; } + void SetMenuController(Controller* menuController) { m_MenuController = menuController; } /// /// Gets the currently affected MovableObject. Ownership is NOT transferred! /// /// The MovableObject this PieMenu affects. Ownership is NOT transferred! - const MovableObject * GetAffectedObject() const { return m_AffectedObject; } + const MovableObject* GetAffectedObject() const { return m_AffectedObject; } /// /// Sets the MovableObject this PieMenu should affect. Ownership is NOT transferred! /// /// The new MovableObject affected by this PieMenu. Ownership is NOT transferred! - void SetAffectedObject(MovableObject *affectedObject) { m_AffectedObject = affectedObject; } + void SetAffectedObject(MovableObject* affectedObject) { m_AffectedObject = affectedObject; } /// /// Gets whether this PieMenu is a sub-PieMenu, i.e. it's owned by a PieSlice. @@ -117,19 +124,19 @@ namespace RTE { /// Gets the absolute center position of this PieMenu. /// /// A Vector describing the current absolute position of this PieMenu in pixels. - const Vector & GetPos() const { return m_CenterPos; } + const Vector& GetPos() const { return m_CenterPos; } /// /// Sets the absolute center position of this PieMenu in the scene. /// /// A Vector describing the new absolute position of this PieMenu in pixels, in the scene. - void SetPos(const Vector &newPos); + void SetPos(const Vector& newPos); /// /// Gets the absolute rotation of this PieMenu. /// /// A Matrix describing the current absolute rotation of this PieMenu. - const Matrix & GetRotation() const { return m_Rotation; } + const Matrix& GetRotation() const { return m_Rotation; } /// /// Gets the absolute rotation of this PieMenu in radians. @@ -141,7 +148,7 @@ namespace RTE { /// Sets the absolute rotation of this PieMenu. /// /// A Matrix describing the new rotation of this PieMenu. - void SetRotation(const Matrix &newRotation) { m_Rotation = newRotation; } + void SetRotation(const Matrix& newRotation) { m_Rotation = newRotation; } /// /// Sets the absolute rotation of this PieMenu to the specified rad angle. @@ -159,7 +166,15 @@ namespace RTE { /// Sets the full inner radius of this PieMenu and recreates the background bitmap if it's changed. /// /// The new full inner radius of this PieMenu. - void SetFullInnerRadius(int fullInnerRadius) { if (m_FullInnerRadius != fullInnerRadius) { if (m_CurrentInnerRadius == m_FullInnerRadius) { m_CurrentInnerRadius = fullInnerRadius; } m_FullInnerRadius = fullInnerRadius; RecreateBackgroundBitmaps(); } } + void SetFullInnerRadius(int fullInnerRadius) { + if (m_FullInnerRadius != fullInnerRadius) { + if (m_CurrentInnerRadius == m_FullInnerRadius) { + m_CurrentInnerRadius = fullInnerRadius; + } + m_FullInnerRadius = fullInnerRadius; + RecreateBackgroundBitmaps(); + } + } /// /// Gets whether or not the PieMenu is enabled or in the process of being enabled, and is not in wobble mode. @@ -215,12 +230,22 @@ namespace RTE { /// /// Sets this PieMenu to normal MenuMode. /// - void SetAnimationModeToNormal() { if (m_MenuMode != MenuMode::Normal) { m_MenuMode = MenuMode::Normal; m_EnabledState = EnabledState::Disabled; } } + void SetAnimationModeToNormal() { + if (m_MenuMode != MenuMode::Normal) { + m_MenuMode = MenuMode::Normal; + m_EnabledState = EnabledState::Disabled; + } + } /// /// Plays the disabling animation, regardless of whether the PieMenu was enabled or not. /// - void DoDisableAnimation() { m_CurrentInnerRadius = m_FullInnerRadius; m_MenuMode = MenuMode::Normal; m_EnableDisableAnimationTimer.Reset(); m_EnabledState = EnabledState::Disabling; } + void DoDisableAnimation() { + m_CurrentInnerRadius = m_FullInnerRadius; + m_MenuMode = MenuMode::Normal; + m_EnableDisableAnimationTimer.Reset(); + m_EnabledState = EnabledState::Disabling; + } /// /// Plays an animation of the background circle expanding and contracting continuously. The PieMenu is effectively disabled while doing this. @@ -232,7 +257,11 @@ namespace RTE { /// Makes the background circle freeze at a certain radius until SetEnabled is called. The PieMenu is effectively disabled while doing this. /// /// The radius to make the background circle freeze at. - void FreezeAtRadius(int radius) { m_MenuMode = MenuMode::Freeze; m_CurrentInnerRadius = radius; m_BGBitmapNeedsRedrawing = true; } + void FreezeAtRadius(int radius) { + m_MenuMode = MenuMode::Freeze; + m_CurrentInnerRadius = radius; + m_BGBitmapNeedsRedrawing = true; + } #pragma endregion #pragma region PieSlice Handling @@ -240,7 +269,7 @@ namespace RTE { /// Gets the activated PieSlice for this PieMenu in the last update. If there is a sub-PieMenu open for this PieMenu, it gets that activated PieSlice instead. /// /// The activated PieSlice for this PieMenu. - const PieSlice * GetActivatedPieSlice() const; + const PieSlice* GetActivatedPieSlice() const; /// /// Gets the command issued by this PieMenu in the last update, i.e. the PieSlice SliceType of the currently activated PieSlice, or None if no slice was activated. @@ -252,21 +281,21 @@ namespace RTE { /// Gets a const reference to the vector containing pointers to all the PieSlices in this PieMenu. /// /// A const reference to the vector containing pointers to all the PieSlices in this PieMenu. - const std::vector & GetPieSlices() const { return m_CurrentPieSlices; } + const std::vector& GetPieSlices() const { return m_CurrentPieSlices; } /// /// Gets the first found PieSlice with the passed in preset name, if there is one. Ownership is NOT transferred! /// /// The preset name to look for. /// The first found PieSlice with the passed in preset name, or nullptr if there are no PieSlices with that preset name in this PieMenu. - PieSlice * GetFirstPieSliceByPresetName(const std::string &presetName) const; + PieSlice* GetFirstPieSliceByPresetName(const std::string& presetName) const; /// /// Gets the first found PieSlice with the passed in PieSlice SliceType, if there is one. Ownership is NOT transferred! /// /// The type of PieSlice to look for. /// The first found PieSlice with the passed in PieSlice SliceType, or nullptr if there are no PieSlices with that SliceType in this PieMenu. - PieSlice * GetFirstPieSliceByType(PieSlice::SliceType pieSliceType) const; + PieSlice* GetFirstPieSliceByType(PieSlice::SliceType pieSliceType) const; /// /// Adds a PieSlice to the PieMenu, setting its original source to the specified sliceSource. Ownership IS transferred! @@ -278,7 +307,7 @@ namespace RTE { /// The source of the added PieSlice. Should be nullptr for slices not added by Entities. /// Whether the new PieSlice can be placed in PieQuadrants other than the one specified by its Direction, if that PieQuadrant is full. /// Whether or not the PieSlice was added successfully. - bool AddPieSlice(PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource, bool allowQuadrantOverflow = false); + bool AddPieSlice(PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool allowQuadrantOverflow = false); /// /// Adds a PieSlice to the PieMenu, with the same conditions as AddPieSlice above, but only if no PieSlice exists in this PieMenu with the same PresetName (optionally with the same original source). Ownership IS transferred! @@ -288,21 +317,21 @@ namespace RTE { /// Whether all PieSlices in the PieMenu should be checked to see if there are no duplicates, or only those with the same original source. /// Whether the new PieSlice can be placed in PieQuadrants other than the one specified by its Direction, if that PieQuadrant is full. /// Whether or not the PieSlice was added successfully. - bool AddPieSliceIfPresetNameIsUnique(PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource = false, bool allowQuadrantOverflow = false); + bool AddPieSliceIfPresetNameIsUnique(PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource = false, bool allowQuadrantOverflow = false); /// /// Removes and returns the passed in PieSlice from this PieMenu if it's in the PieMenu. Ownership IS transferred to the caller! /// /// The PieSlice to remove from this PieMenu. Ownership IS transferred to the caller! /// The removed PieSlice, if it was in the PieMenu. - PieSlice * RemovePieSlice(const PieSlice *pieSliceToRemove); + PieSlice* RemovePieSlice(const PieSlice* pieSliceToRemove); /// /// Removes any PieSlices in this PieMenu whose preset name matches the passed in preset name. /// /// The preset name to check against. /// Whether or not any PieSlices were removed from this PieMenu. - bool RemovePieSlicesByPresetName(const std::string &presetNameToRemoveBy); + bool RemovePieSlicesByPresetName(const std::string& presetNameToRemoveBy); /// /// Removes any PieSlices in this PieMenu whose PieSlice SliceType matches the passed in PieSlice SliceType. @@ -316,7 +345,7 @@ namespace RTE { /// /// The original source whose PieSlices should be removed. /// Whether or not any PieSlices were removed from this PieMenu. - bool RemovePieSlicesByOriginalSource(const Entity *originalSource); + bool RemovePieSlicesByOriginalSource(const Entity* originalSource); /// /// Replaces the first PieSlice with the second, ensuring original source, direction, middle slice eligibility, angles and slot count are maintained. @@ -325,7 +354,7 @@ namespace RTE { /// The PieSlice that will be replaced. /// The PieSlice that will replace the existing one. If this is nullptr, the existing one will just be removed. /// The removed PieSlice, if there is one. Ownership IS transferred! - PieSlice * ReplacePieSlice(const PieSlice *pieSliceToReplace, PieSlice *replacementPieSlice); + PieSlice* ReplacePieSlice(const PieSlice* pieSliceToReplace, PieSlice* replacementPieSlice); #pragma endregion #pragma region Updating @@ -339,7 +368,7 @@ namespace RTE { /// /// A pointer to a BITMAP to draw on. Generally a screen BITMAP. /// The absolute position of the target bitmap's upper left corner in the scene. - void Draw(BITMAP *targetBitmap, const Vector &targetPos = Vector()) const; + void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector()) const; #pragma endregion #pragma region Event Handling @@ -348,37 +377,57 @@ namespace RTE { /// /// The MovableObject listening. /// The function to be run on the MovableObject. - void AddWhilePieMenuOpenListener(const MovableObject *listeningObject, const std::function &listenerFunction) { if (listeningObject) { m_WhilePieMenuOpenListeners.try_emplace(listeningObject, listenerFunction); } } + void AddWhilePieMenuOpenListener(const MovableObject* listeningObject, const std::function& listenerFunction) { + if (listeningObject) { + m_WhilePieMenuOpenListeners.try_emplace(listeningObject, listenerFunction); + } + } /// /// Removes the passed in MovableObject and its listening function as a listener for when this PieMenu is opened. /// /// The MovableObject whose listening function should be removed. /// Whether or not the MovableObject was found and removed as a listener. - bool RemoveWhilePieMenuOpenListener(const MovableObject *objectToRemove) { return m_WhilePieMenuOpenListeners.erase(objectToRemove) == 1; } + bool RemoveWhilePieMenuOpenListener(const MovableObject* objectToRemove) { return m_WhilePieMenuOpenListeners.erase(objectToRemove) == 1; } #pragma endregion private: - /// /// Enumeration for enabled states when enabling/disabling the PieMenu. /// - enum class EnabledState { Enabling, Enabled, Disabling, Disabled }; + enum class EnabledState { + Enabling, + Enabled, + Disabling, + Disabled + }; /// /// Enumeration for the modes a PieMenu can have. /// - enum class MenuMode { Normal, Wobble, Freeze }; + enum class MenuMode { + Normal, + Wobble, + Freeze + }; /// /// Enumeration for the different item separator modes available to the PieMenu. /// - enum class IconSeparatorMode { Line, Circle, Square }; + enum class IconSeparatorMode { + Line, + Circle, + Square + }; /// /// Enumeration for helping keyboard PieMenu navigation. Specifies the ways the cursor should move from one PieQuadrant to another. /// - enum class MoveToPieQuadrantMode { Start, Middle, End }; + enum class MoveToPieQuadrantMode { + Start, + Middle, + End + }; static constexpr int c_EnablingDelay = 50; //!< Time in ms for how long it takes to enable/disable. static constexpr int c_DefaultFullRadius = 58; //!< The radius the menu should have when fully enabled, in pixels. @@ -390,13 +439,13 @@ namespace RTE { static const std::unordered_map c_CounterClockwiseDirections; //!< A map of Directions to the Direction that is CCW from them, for moving between PieQuadrants with the keyboard. static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. - static BITMAP *s_CursorBitmap; //!< A static pointer to the bitmap to use as the cursor in any menu. + static BITMAP* s_CursorBitmap; //!< A static pointer to the bitmap to use as the cursor in any menu. - GUIFont *m_LargeFont; //!< A pointer to the large font from FrameMan. Not owned here. + GUIFont* m_LargeFont; //!< A pointer to the large font from FrameMan. Not owned here. - Actor *m_Owner; //!< The owner Actor of this PieMenu. Note that PieMenus do not necessarily need to have a owner. - Controller *m_MenuController; //!< The Controller which controls this PieMenu. Separate from the Controller of the owner or affected object (if there is one). - MovableObject *m_AffectedObject; //!< The MovableObject this PieMenu affects, if any. Only applies if there's no owner. + Actor* m_Owner; //!< The owner Actor of this PieMenu. Note that PieMenus do not necessarily need to have a owner. + Controller* m_MenuController; //!< The Controller which controls this PieMenu. Separate from the Controller of the owner or affected object (if there is one). + MovableObject* m_AffectedObject; //!< The MovableObject this PieMenu affects, if any. Only applies if there's no owner. Directions m_DirectionIfSubPieMenu; //!< The direction this sub-PieMenu is facing in. None if this is not a sub-PieMenu of another PieMenu. MenuMode m_MenuMode; //!< The mode this PieMenu is in. See MenuMode enum for more details. Vector m_CenterPos; //!< The center position of this PieMenu in the scene. @@ -417,22 +466,22 @@ namespace RTE { int m_SelectedItemBackgroundColor; //!< The color used for drawing selected PieMenu items' backgrounds. std::array m_PieQuadrants; //!< The array of PieQuadrants that make up this PieMenu. Quadrants may be individually enabled or disabled, affecting what's drawn. - const PieSlice *m_HoveredPieSlice; //!< The PieSlice currently being hovered over. - const PieSlice *m_ActivatedPieSlice; //!< The currently activated PieSlice, if there is one, or 0 if there's not. - const PieSlice *m_AlreadyActivatedPieSlice; //!< The PieSlice that was most recently activated by pressing primary. Used to avoid duplicate activation when disabling. - std::vector m_CurrentPieSlices; //!< All the PieSlices in this PieMenu in INI order. Not owned here, just pointing to the ones above. + const PieSlice* m_HoveredPieSlice; //!< The PieSlice currently being hovered over. + const PieSlice* m_ActivatedPieSlice; //!< The currently activated PieSlice, if there is one, or 0 if there's not. + const PieSlice* m_AlreadyActivatedPieSlice; //!< The PieSlice that was most recently activated by pressing primary. Used to avoid duplicate activation when disabling. + std::vector m_CurrentPieSlices; //!< All the PieSlices in this PieMenu in INI order. Not owned here, just pointing to the ones above. - PieMenu *m_ActiveSubPieMenu; //!< The currently active sub-PieMenu, if any. + PieMenu* m_ActiveSubPieMenu; //!< The currently active sub-PieMenu, if any. - std::unordered_map> m_WhilePieMenuOpenListeners; //!< Unordered map of MovableObject pointers to functions to be called while the PieMenu is open. Pointers are NOT owned. + std::unordered_map> m_WhilePieMenuOpenListeners; //!< Unordered map of MovableObject pointers to functions to be called while the PieMenu is open. Pointers are NOT owned. int m_CurrentInnerRadius; //!< The current radius of the innermost circle of the pie menu, in pixels. bool m_CursorInVisiblePosition; //!< Whether or not this PieMenu's cursor is in a visible position and should be shown. float m_CursorAngle; //!< Position of the cursor on the circle, in radians, counterclockwise from straight out to the right. - BITMAP *m_BGBitmap; //!< The intermediary bitmap used to first draw the PieMenu background, which will be blitted to the final draw target surface. - BITMAP *m_BGRotationBitmap; //!< The intermediary bitmap used to allow the PieMenu background to rotate, which will be pivoted onto the BG bitmap. - BITMAP *m_BGPieSlicesWithSubPieMenuBitmap; //!< The intermediary bitmap used to support handling PieSlices with sub-PieMenus, which will be drawn onto the BG bitmap. + BITMAP* m_BGBitmap; //!< The intermediary bitmap used to first draw the PieMenu background, which will be blitted to the final draw target surface. + BITMAP* m_BGRotationBitmap; //!< The intermediary bitmap used to allow the PieMenu background to rotate, which will be pivoted onto the BG bitmap. + BITMAP* m_BGPieSlicesWithSubPieMenuBitmap; //!< The intermediary bitmap used to support handling PieSlices with sub-PieMenus, which will be drawn onto the BG bitmap. bool m_BGBitmapNeedsRedrawing; //!< Whether the BG bitmap should be redrawn during the next Update call. bool m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing; //!< Whether the BG bitmap for PieSlices with sub-PieMenus should be redrawn when the BGBitmap is redrawn. @@ -452,7 +501,7 @@ namespace RTE { /// /// The analog input vector. /// Whether or not enough input was received to do something. - bool HandleAnalogInput(const Vector &input); + bool HandleAnalogInput(const Vector& input); /// /// Handles the digital input when updating. @@ -478,21 +527,21 @@ namespace RTE { /// A pointer to the BITMAP to draw on. Generally a screen BITMAP. /// The absolute position of the target bitmap's upper left corner in the scene. /// Out parameter, a Vector to be filled in with the position at which the PieMenu should be drawn. - void CalculateDrawPosition(const BITMAP *targetBitmap, const Vector &targetPos, Vector &drawPos) const; + void CalculateDrawPosition(const BITMAP* targetBitmap, const Vector& targetPos, Vector& drawPos) const; /// /// Handles drawing icons for PieSlices' visual representation in the PieMenu. /// /// A pointer to the BITMAP to draw on. Generally a screen BITMAP. /// The seam corrected position at which the PieMenu is being drawn. - void DrawPieIcons(BITMAP *targetBitmap, const Vector &drawPos) const; + void DrawPieIcons(BITMAP* targetBitmap, const Vector& drawPos) const; /// /// Handles drawing the cursor and description text for selected PieSlices. /// /// A pointer to the BITMAP to draw on. Generally a screen BITMAP. /// The seam corrected position at which the PieMenu is being drawn. - void DrawPieCursorAndPieSliceDescriptions(BITMAP *targetBitmap, const Vector &drawPos) const; + void DrawPieCursorAndPieSliceDescriptions(BITMAP* targetBitmap, const Vector& drawPos) const; #pragma endregion /// @@ -517,7 +566,7 @@ namespace RTE { /// The center X position of the circle being separated. /// The center Y position of the circle being separated. /// The rotation offset used if this is a sub-PieMenu. - void DrawBackgroundPieSliceSeparators(BITMAP *backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float subPieMenuRotationOffset) const; + void DrawBackgroundPieSliceSeparators(BITMAP* backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float subPieMenuRotationOffset) const; /// /// Draws a background separator, based on this PieMenu's IconSeparatorMode, to the passed in bitmap. @@ -529,7 +578,7 @@ namespace RTE { /// Whether the separator is being drawn for the PieMenu's hovered PieSlice. /// Whether the PieSlice whose separator is being drawn has a sub-PieMenu. /// Whether to draw a half-sized separator or a full-sized one. Defaults to drawing a full-sized one. - void DrawBackgroundPieSliceSeparator(BITMAP *backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float rotAngle, bool isHoveredPieSlice, bool pieSliceHasSubPieMenu, bool drawHalfSizedSeparator = false) const; + void DrawBackgroundPieSliceSeparator(BITMAP* backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float rotAngle, bool isHoveredPieSlice, bool pieSliceHasSubPieMenu, bool drawHalfSizedSeparator = false) const; /// /// Sets the passed in PieSlice as the hovered PieSlice of this PieMenu. If nullptr is passed in, no PieSlice will be hovered. @@ -537,14 +586,14 @@ namespace RTE { /// The PieSlice to consider hovered, if any. Has to be a PieSlice currently in this PieMenu. /// Whether to also move the cursor icon to the center of the new hovered PieSlice and set it to be drawn (generally for non-mouse inputs). Defaults to false. /// Whether or not the PieSlice to set as hovered was different from the already hovered PieSlice. - bool SetHoveredPieSlice(const PieSlice *pieSliceToSetAsHovered, bool moveCursorToPieSlice = false); + bool SetHoveredPieSlice(const PieSlice* pieSliceToSetAsHovered, bool moveCursorToPieSlice = false); /// /// Prepares the passed in PieSlice's sub-PieMenu for use by setting flags, moving PieSlices around, and disabling PieQuadrants as appropriate. /// /// The PieSlice with a sub-PieMenu that needs to be prepared. /// Whether the sub-PieMenu was prepared. PieMenus with the SubPieMenu flag already set will not have action taken on them. - bool PreparePieSliceSubPieMenuForUse(const PieSlice *pieSliceWithSubPieMenu) const; + bool PreparePieSliceSubPieMenuForUse(const PieSlice* pieSliceWithSubPieMenu) const; /// /// If the Controller for this PieMenu is mouse or gamepad controlled, sets up analog cursor angle limits and positions for when the pie menu is enabled or disabled. Also used when a sub-PieMenu of this PieMenu is disabled. @@ -558,8 +607,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - PieMenu(const PieMenu &reference) = delete; - PieMenu & operator=(const PieMenu &rhs) = delete; + PieMenu(const PieMenu& reference) = delete; + PieMenu& operator=(const PieMenu& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Entities/PieSlice.cpp b/Source/Entities/PieSlice.cpp index 6ce6d176d4..4afa3a38fa 100644 --- a/Source/Entities/PieSlice.cpp +++ b/Source/Entities/PieSlice.cpp @@ -8,7 +8,7 @@ namespace RTE { ConcreteClassInfo(PieSlice, Entity, 80); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieSlice::Clear() { m_Type = SliceType::NoType; @@ -31,20 +31,22 @@ namespace RTE { m_DrawFlippedToMatchAbsoluteAngle = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int PieSlice::Create() { if (Entity::Create() < 0) { return -1; } - if (!HasIcon()) { m_Icon = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Blank")->Clone())); } + if (!HasIcon()) { + m_Icon = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Blank")->Clone())); + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieSlice::Create(const PieSlice &reference) { + int PieSlice::Create(const PieSlice& reference) { Entity::Create(reference); m_Type = reference.m_Type; @@ -52,7 +54,7 @@ namespace RTE { m_CanBeMiddleSlice = reference.m_CanBeMiddleSlice; m_Enabled = reference.m_Enabled; - m_Icon = std::unique_ptr(dynamic_cast(reference.m_Icon->Clone())); + m_Icon = std::unique_ptr(dynamic_cast(reference.m_Icon->Clone())); m_FunctionName = reference.m_FunctionName; if (reference.m_LuabindFunctionObject) { @@ -60,7 +62,9 @@ namespace RTE { ReloadScripts(); } - if (reference.m_SubPieMenu) { SetSubPieMenu(dynamic_cast(reference.m_SubPieMenu->Clone())); } + if (reference.m_SubPieMenu) { + SetSubPieMenu(dynamic_cast(reference.m_SubPieMenu->Clone())); + } m_StartAngle = reference.m_StartAngle; m_SlotCount = reference.m_SlotCount; @@ -71,11 +75,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieSlice::ReadProperty(const std::string_view &propName, Reader &reader) { + int PieSlice::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("Type", { m_Type = static_cast(std::stoi(reader.ReadPropValue())); }); MatchProperty("Direction", { if (std::string directionString = reader.ReadPropValue(); c_DirectionNameToDirectionsMap.find(directionString) != c_DirectionNameToDirectionsMap.end()) { @@ -87,15 +91,17 @@ namespace RTE { reader.ReportError("Direction " + directionString + " is invalid (Out of Bounds)."); } m_Direction = static_cast(direction); - } catch (const std::invalid_argument &) { + } catch (const std::invalid_argument&) { reader.ReportError("Direction " + directionString + " is invalid."); } } - if (m_Direction == Directions::None) { reader.ReportError("Pie Slices cannot have direction None."); } + if (m_Direction == Directions::None) { + reader.ReportError("Pie Slices cannot have direction None."); + } }); MatchProperty("CanBeMiddleSlice", { reader >> m_CanBeMiddleSlice; }); MatchProperty("Enabled", { reader >> m_Enabled; }); - MatchProperty("Icon", { SetIcon(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("Icon", { SetIcon(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); MatchProperty("ScriptPath", { std::string scriptPath; reader >> scriptPath; @@ -110,33 +116,41 @@ namespace RTE { ReloadScripts(); } }); - MatchProperty("SubPieMenu", { SetSubPieMenu(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("SubPieMenu", { SetSubPieMenu(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); MatchProperty("DrawFlippedToMatchAbsoluteAngle", { reader >> m_DrawFlippedToMatchAbsoluteAngle; }); EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieSlice::Save(Writer &writer) const { + int PieSlice::Save(Writer& writer) const { Entity::Save(writer); - if (m_Type != SliceType::NoType) { writer.NewPropertyWithValue("Type", m_Type); } - if (m_Direction != Directions::Any) { writer.NewPropertyWithValue("Direction", static_cast(m_Direction)); } - if (!m_Enabled) { writer.NewPropertyWithValue("Enabled", m_Enabled); } + if (m_Type != SliceType::NoType) { + writer.NewPropertyWithValue("Type", m_Type); + } + if (m_Direction != Directions::Any) { + writer.NewPropertyWithValue("Direction", static_cast(m_Direction)); + } + if (!m_Enabled) { + writer.NewPropertyWithValue("Enabled", m_Enabled); + } writer.NewPropertyWithValue("Icon", m_Icon.get()); if (m_LuabindFunctionObject && !m_FunctionName.empty()) { writer.NewPropertyWithValue("ScriptPath", m_LuabindFunctionObject->GetFilePath()); writer.NewPropertyWithValue("FunctionName", m_FunctionName); } - if (m_SubPieMenu) { writer.NewPropertyWithValue("SubPieMenu", m_SubPieMenu.get()); } + if (m_SubPieMenu) { + writer.NewPropertyWithValue("SubPieMenu", m_SubPieMenu.get()); + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP * RTE::PieSlice::GetAppropriateIcon(bool sliceIsSelected) const { + BITMAP* RTE::PieSlice::GetAppropriateIcon(bool sliceIsSelected) const { if (int iconFrameCount = m_Icon->GetFrameCount(); iconFrameCount > 0) { if (!IsEnabled() && iconFrameCount > 2) { return m_Icon->GetBitmaps8()[2]; @@ -149,45 +163,45 @@ namespace RTE { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieMenu *PieSlice::GetSubPieMenu() const { + PieMenu* PieSlice::GetSubPieMenu() const { return m_SubPieMenu.get(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieSlice::SetSubPieMenu(PieMenu *newSubPieMenu) { + void PieSlice::SetSubPieMenu(PieMenu* newSubPieMenu) { m_SubPieMenu = std::unique_ptr(newSubPieMenu); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieSlice::ReloadScripts() { + int PieSlice::ReloadScripts() { int status = 0; if (m_LuabindFunctionObject) { std::string filePath = m_LuabindFunctionObject->GetFilePath(); - std::unordered_map scriptFileFunctions; + std::unordered_map scriptFileFunctions; - status = g_LuaMan.GetMasterScriptState().RunScriptFileAndRetrieveFunctions(filePath, { m_FunctionName }, scriptFileFunctions, true); + status = g_LuaMan.GetMasterScriptState().RunScriptFileAndRetrieveFunctions(filePath, {m_FunctionName}, scriptFileFunctions, true); if (scriptFileFunctions.find(m_FunctionName) != scriptFileFunctions.end()) { m_LuabindFunctionObject = std::unique_ptr(scriptFileFunctions.at(m_FunctionName)); } } - return status; - } + return status; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieSlice::RecalculateMidAngle() { + void PieSlice::RecalculateMidAngle() { m_MidAngle = m_StartAngle + (static_cast(m_SlotCount) * PieQuadrant::c_PieSliceSlotSize / 2.0F); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieSlice::PieMenuCustomDeleter::operator()(PieMenu *pieMenu) const { + void PieSlice::PieMenuCustomDeleter::operator()(PieMenu* pieMenu) const { pieMenu->Destroy(true); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/PieSlice.h b/Source/Entities/PieSlice.h index 5ad43a25ac..bc3316834f 100644 --- a/Source/Entities/PieSlice.h +++ b/Source/Entities/PieSlice.h @@ -15,7 +15,6 @@ namespace RTE { class PieSlice : public Entity { public: - EntityAllocation(PieSlice); SerializableOverrideMethods; ClassInfoGetters; @@ -85,7 +84,7 @@ namespace RTE { /// /// A reference to the PieSlice to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const PieSlice &reference); + int Create(const PieSlice& reference); #pragma endregion #pragma region Destruction @@ -123,7 +122,11 @@ namespace RTE { /// Sets the Direction of this PieSlice. /// /// The new Direction of this PieSlice. - void SetDirection(Directions newDirection) { if (newDirection != Directions::None) { m_Direction = newDirection; } } + void SetDirection(Directions newDirection) { + if (newDirection != Directions::None) { + m_Direction = newDirection; + } + } /// /// Gets whether or not this PieSlice can be the middle PieSlice of a PieQuadrant. @@ -141,13 +144,13 @@ namespace RTE { /// Gets the original Entity source of this PieSlice, if there is one. /// /// A pointer to the original Entity source of this PieSlice, if there is one. - const Entity * GetOriginalSource() const { return m_OriginalSource; } + const Entity* GetOriginalSource() const { return m_OriginalSource; } /// /// Sets the original Entity source of this PieSlice. /// /// A pointer to the original Entity source of this PieSlice. - void SetOriginalSource(const Entity *originalSource) { m_OriginalSource = originalSource; } + void SetOriginalSource(const Entity* originalSource) { m_OriginalSource = originalSource; } /// /// Gets whether or not this PieSlice is enabled. @@ -172,19 +175,19 @@ namespace RTE { /// /// Whether or not this PieSlice is selected, which may affect which icon is appropriate. /// The icon for this PieSlice. - BITMAP * GetAppropriateIcon(bool sliceIsSelected = false) const; + BITMAP* GetAppropriateIcon(bool sliceIsSelected = false) const; /// /// Sets the new Icon for this PieSlice. Ownership IS transferred. /// /// The new Icon for this PieSlice. - void SetIcon(Icon *newIcon) { m_Icon = std::unique_ptr(newIcon); } + void SetIcon(Icon* newIcon) { m_Icon = std::unique_ptr(newIcon); } /// /// Gets the LuabindObjectWrapper for the function this PieSlice should run when activated. /// /// The LuabindObjectWrapper this PieSlice should run when activated. - const LuabindObjectWrapper * GetLuabindFunctionObjectWrapper() const { return m_LuabindFunctionObject.get(); } + const LuabindObjectWrapper* GetLuabindFunctionObjectWrapper() const { return m_LuabindFunctionObject.get(); } /// /// Gets the file path of the Lua file this PieSlice should run when activated, if any. @@ -196,32 +199,38 @@ namespace RTE { /// Sets the file path of the scripted file this PieSlice should run when activated. /// /// The file path of the Lua file this PieSlice should run when activated. - void SetScriptPath(const std::string &newScriptPath) { m_LuabindFunctionObject = std::make_unique(nullptr, newScriptPath); ReloadScripts(); } + void SetScriptPath(const std::string& newScriptPath) { + m_LuabindFunctionObject = std::make_unique(nullptr, newScriptPath); + ReloadScripts(); + } /// /// Gets the name of the Lua function to run when this PieSlice is activated. /// /// The name of the Lua function this PieSlice should execute when activated. - const std::string & GetFunctionName() const { return m_FunctionName; } + const std::string& GetFunctionName() const { return m_FunctionName; } /// /// Sets the name of the Lua function to run when this PieSlice is activated as a scripted pie menu option. /// /// The name of the Lua function to run when this PieSlice is activated. - void SetFunctionName(const std::string &newFunctionName) { m_FunctionName = newFunctionName; ReloadScripts(); } + void SetFunctionName(const std::string& newFunctionName) { + m_FunctionName = newFunctionName; + ReloadScripts(); + } - //TODO Ideally this would be done with a weak_ptr but I'm not sure how it'll go with LuaMan. Try it out and see + // TODO Ideally this would be done with a weak_ptr but I'm not sure how it'll go with LuaMan. Try it out and see /// /// Gets the sub-PieMenu for this PieSlice if there is one. Ownership is NOT transferred. /// /// The sub-PieMenu for this PieSlice if there is one. Ownership is NOT transferred. - PieMenu * GetSubPieMenu() const; + PieMenu* GetSubPieMenu() const; /// /// Sets the sub-PieMenu for this PieSlice. Ownership IS transferred. /// /// The new sub-PieMenu for this PieSlice. Ownership IS transferred. - void SetSubPieMenu(PieMenu *newSubPieMenu); + void SetSubPieMenu(PieMenu* newSubPieMenu); #pragma endregion #pragma region Angle Getter and Setters @@ -235,7 +244,10 @@ namespace RTE { /// Sets the start angle this PieSlice's area should be at in its pie menu. /// /// The start angle to set for the PieSlice's area. - void SetStartAngle(float startAngle) { m_StartAngle = startAngle; RecalculateMidAngle(); } + void SetStartAngle(float startAngle) { + m_StartAngle = startAngle; + RecalculateMidAngle(); + } /// /// Gets the number of slots this PieSlice takes up. @@ -247,7 +259,10 @@ namespace RTE { /// Sets the number of slots this PieSlice takes up. /// /// The number of slots this PieSlice should take up. - void SetSlotCount(int slotCount) { m_SlotCount = std::max(1, slotCount); RecalculateMidAngle(); } + void SetSlotCount(int slotCount) { + m_SlotCount = std::max(1, slotCount); + RecalculateMidAngle(); + } /// /// Gets the mid angle this PieSlice's area is set to be at in its pie menu. @@ -281,12 +296,11 @@ namespace RTE { int ReloadScripts() final; private: - /// /// Custom deleter for PieMenu to avoid include problems with unique_ptr. /// struct PieMenuCustomDeleter { - void operator()(PieMenu *pieMenu) const; + void operator()(PieMenu* pieMenu) const; }; static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. @@ -294,7 +308,7 @@ namespace RTE { SliceType m_Type; //!< The slice type, also used to determine the icon. Directions m_Direction; //!< The desired direction/location of this on the PieMenu. bool m_CanBeMiddleSlice; //!< Whether or not this PieSlice is allowed to be the middle slice. Defaults to true and should usually stay that way. - const Entity *m_OriginalSource; //!< A pointer to the original source of this PieSlice, normally filled in when PieSlices are added to PieMenus by objects other than the PieMenu's owner, and nullptr otherwise. + const Entity* m_OriginalSource; //!< A pointer to the original source of this PieSlice, normally filled in when PieSlices are added to PieMenus by objects other than the PieMenu's owner, and nullptr otherwise. bool m_Enabled; //!< Whether this PieSlice is enabled or disabled and grayed out. std::unique_ptr m_Icon; //!< The icon of this PieSlice. @@ -321,8 +335,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - PieSlice(const PieSlice &reference) = delete; - PieSlice &operator=(const PieSlice &rhs) = delete; + PieSlice(const PieSlice& reference) = delete; + PieSlice& operator=(const PieSlice& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/Round.cpp b/Source/Entities/Round.cpp index 6d51e067c1..0d457a1ef9 100644 --- a/Source/Entities/Round.cpp +++ b/Source/Entities/Round.cpp @@ -6,7 +6,7 @@ namespace RTE { ConcreteClassInfo(Round, Entity, 500); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Round::Clear() { m_Particle = 0; @@ -23,7 +23,7 @@ namespace RTE { m_AIPenetration = -1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Round::Create() { if (Entity::Create() < 0) { @@ -31,22 +31,26 @@ namespace RTE { } if (m_AILifeTime == 0) { - const MovableObject *bullet = GetNextParticle(); - if (bullet) { m_AILifeTime = bullet->GetLifetime(); } + const MovableObject* bullet = GetNextParticle(); + if (bullet) { + m_AILifeTime = bullet->GetLifetime(); + } + } + if (m_AIFireVel < 0) { + m_AIFireVel = m_FireVel; } - if (m_AIFireVel < 0) { m_AIFireVel = m_FireVel; } if (m_AIPenetration < 0) { - const MovableObject *bullet = GetNextParticle(); - m_AIPenetration = (bullet && dynamic_cast(bullet)) ? bullet->GetMass() * bullet->GetSharpness() * m_AIFireVel : 0; + const MovableObject* bullet = GetNextParticle(); + m_AIPenetration = (bullet && dynamic_cast(bullet)) ? bullet->GetMass() * bullet->GetSharpness() * m_AIFireVel : 0; } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Round::Create(const Round &reference) { + int Round::Create(const Round& reference) { Entity::Create(reference); m_Particle = reference.m_Particle; @@ -65,13 +69,13 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Round::ReadProperty(const std::string_view &propName, Reader &reader) { + int Round::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("Particle", { - m_Particle = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); + m_Particle = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); RTEAssert(m_Particle, "Stream suggests allocating an unallocable type in Round::Create!"); }); MatchProperty("ParticleCount", { reader >> m_ParticleCount; }); @@ -79,20 +83,19 @@ namespace RTE { MatchProperty("InheritsFirerVelocity", { reader >> m_InheritsFirerVelocity; }); MatchProperty("Separation", { reader >> m_Separation; }); MatchProperty("LifeVariation", { reader >> m_LifeVariation; }); - MatchProperty("Shell", { m_Shell = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); + MatchProperty("Shell", { m_Shell = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); MatchProperty("ShellVelocity", { reader >> m_ShellVel; }); MatchProperty("FireSound", { reader >> m_FireSound; }); MatchProperty("AILifeTime", { reader >> m_AILifeTime; }); MatchProperty("AIFireVel", { reader >> m_AIFireVel; }); MatchProperty("AIPenetration", { reader >> m_AIPenetration; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Round::Save(Writer &writer) const { + int Round::Save(Writer& writer) const { Entity::Save(writer); writer.NewProperty("Particle"); @@ -122,11 +125,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MovableObject * Round::PopNextParticle() { - MovableObject *tempParticle = (m_ParticleCount > 0) ? dynamic_cast(m_Particle->Clone()) : 0; + MovableObject* Round::PopNextParticle() { + MovableObject* tempParticle = (m_ParticleCount > 0) ? dynamic_cast(m_Particle->Clone()) : 0; m_ParticleCount--; return tempParticle; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/Round.h b/Source/Entities/Round.h index 0bd6cef187..fc294d5b3a 100644 --- a/Source/Entities/Round.h +++ b/Source/Entities/Round.h @@ -13,7 +13,6 @@ namespace RTE { class Round : public Entity { public: - EntityAllocation(Round); SerializableOverrideMethods; ClassInfoGetters; @@ -35,7 +34,7 @@ namespace RTE { /// /// A reference to the Round to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Round &reference); + int Create(const Round& reference); #pragma endregion #pragma region Destruction @@ -48,12 +47,20 @@ namespace RTE { /// Destroys and resets (through Clear()) the Round object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + Entity::Destroy(); + } + Clear(); + } /// /// Resets the entire Round, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Entity::Reset(); } + void Reset() override { + Clear(); + Entity::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -73,13 +80,13 @@ namespace RTE { /// Gets the next particle contained in this Round. Ownership is NOT transferred! /// /// A pointer to the next particle, or 0 if this Round is empty. - const MovableObject * GetNextParticle() const { return (m_ParticleCount > 0) ? m_Particle : 0; } + const MovableObject* GetNextParticle() const { return (m_ParticleCount > 0) ? m_Particle : 0; } /// /// Gets the next particle contained in this Round, and removes it from the stack. Ownership IS transferred! /// /// A pointer to the next particle, or 0 if this Round is empty. - MovableObject * PopNextParticle(); + MovableObject* PopNextParticle(); /// /// Gets the velocity at which this round is to be fired. @@ -109,7 +116,7 @@ namespace RTE { /// Gets the shell casing preset of this Round. Ownership IS NOT transferred! /// /// A pointer to the shell casing preset, or 0 if this Round has no shell. - const MovableObject * GetShell() const { return m_Shell; } + const MovableObject* GetShell() const { return m_Shell; } /// /// Gets the maximum velocity at which this round's shell is to be ejected. @@ -127,7 +134,7 @@ namespace RTE { /// Gets the extra firing sound of this Round, which can be played in addition to the weapon's own firing sound. OWNERSHIP IS NOT TRANSFERRED! /// /// A sound with the firing sample of this round. - SoundContainer * GetFireSound() { return &m_FireSound; } + SoundContainer* GetFireSound() { return &m_FireSound; } #pragma endregion #pragma region AI Properties @@ -151,17 +158,16 @@ namespace RTE { #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. - const MovableObject *m_Particle; //!< Round particle MovableObject preset instance. + const MovableObject* m_Particle; //!< Round particle MovableObject preset instance. int m_ParticleCount; //!< How many particle copies there are in this Round. float m_FireVel; //!< The velocity with which this Round is fired. bool m_InheritsFirerVelocity; //!< Whether or not this Round should inherit velocity from its firer. float m_Separation; //!< The range of separation between particles in this Round, in pixels. float m_LifeVariation; //!< The random variation in life time of each fired particle, in percentage of their life time. - const MovableObject *m_Shell; //!< Shell particle MovableObject preset instance. + const MovableObject* m_Shell; //!< Shell particle MovableObject preset instance. float m_ShellVel; //!< The maximum velocity with which this Round's shell/casing is launched. SoundContainer m_FireSound; //!< The extra firing audio of this Round being fired. @@ -171,15 +177,14 @@ namespace RTE { int m_AIPenetration; //!< For overriding the bullets ability to penetrate material when executing the AI shooting scripts. private: - /// /// Clears all the member variables of this Round, effectively resetting the members of this abstraction level only. /// void Clear(); // Disallow the use of some implicit methods. - Round(const Round &reference) = delete; - Round & operator=(const Round &rhs) = delete; + Round(const Round& reference) = delete; + Round& operator=(const Round& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/SLBackground.cpp b/Source/Entities/SLBackground.cpp index c9420260a6..a52b72426c 100644 --- a/Source/Entities/SLBackground.cpp +++ b/Source/Entities/SLBackground.cpp @@ -7,7 +7,7 @@ namespace RTE { ConcreteClassInfo(SLBackground, SceneLayer, 0); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SLBackground::Clear() { m_Bitmaps.clear(); @@ -32,7 +32,7 @@ namespace RTE { m_IgnoreAutoScale = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int SLBackground::Create() { SceneLayer::Create(); @@ -58,9 +58,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLBackground::Create(const SLBackground &reference) { + int SLBackground::Create(const SLBackground& reference) { SceneLayer::Create(reference); // The main bitmap is created and owned by SceneLayer because it can be modified. We need to destroy it to avoid a leak because the bitmaps we'll be using here are owned by ContentFile static maps and are unmodifiable. @@ -90,15 +90,17 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLBackground::ReadProperty(const std::string_view &propName, Reader &reader) { + int SLBackground::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneLayer::ReadProperty(propName, reader)); - + MatchProperty("FrameCount", { reader >> m_FrameCount; }); MatchProperty("SpriteAnimMode", { m_SpriteAnimMode = static_cast(std::stoi(reader.ReadPropValue())); - if (m_SpriteAnimMode < SpriteAnimMode::NOANIM || m_SpriteAnimMode > SpriteAnimMode::ALWAYSPINGPONG) { reader.ReportError("Invalid SLBackground sprite animation mode!"); } + if (m_SpriteAnimMode < SpriteAnimMode::NOANIM || m_SpriteAnimMode > SpriteAnimMode::ALWAYSPINGPONG) { + reader.ReportError("Invalid SLBackground sprite animation mode!"); + } }); MatchProperty("SpriteAnimDuration", { reader >> m_SpriteAnimDuration; }); MatchProperty("IsAnimatedManually", { reader >> m_IsAnimatedManually; }); @@ -117,14 +119,13 @@ namespace RTE { MatchProperty("CanAutoScrollY", { reader >> m_CanAutoScrollY; }); MatchProperty("AutoScrollStepInterval", { reader >> m_AutoScrollStepInterval; }); MatchProperty("AutoScrollStep", { reader >> m_AutoScrollStep; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLBackground::Save(Writer &writer) const { + int SLBackground::Save(Writer& writer) const { SceneLayer::Save(writer); writer.NewPropertyWithValue("FrameCount", m_FrameCount); @@ -144,7 +145,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SLBackground::InitScaleFactors() { if (!m_IgnoreAutoScale) { @@ -166,7 +167,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SLBackground::Update() { if (!m_IsAnimatedManually && m_SpriteAnimMode != SpriteAnimMode::NOANIM) { @@ -200,8 +201,12 @@ namespace RTE { if (IsAutoScrolling()) { if (m_AutoScrollStepTimer.GetElapsedSimTimeMS() > m_AutoScrollStepInterval) { - if (m_WrapX && m_CanAutoScrollX) { m_AutoScrollOffset.SetX(m_AutoScrollOffset.GetX() + m_AutoScrollStep.GetX()); } - if (m_WrapY && m_CanAutoScrollY) { m_AutoScrollOffset.SetY(m_AutoScrollOffset.GetY() + m_AutoScrollStep.GetY()); } + if (m_WrapX && m_CanAutoScrollX) { + m_AutoScrollOffset.SetX(m_AutoScrollOffset.GetX() + m_AutoScrollStep.GetX()); + } + if (m_WrapY && m_CanAutoScrollY) { + m_AutoScrollOffset.SetY(m_AutoScrollOffset.GetY() + m_AutoScrollStep.GetY()); + } WrapPosition(m_AutoScrollOffset); m_AutoScrollStepTimer.Reset(); } @@ -209,9 +214,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLBackground::Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment) { + void SLBackground::Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment) { SceneLayer::Draw(targetBitmap, targetBox, !IsAutoScrolling()); int bitmapWidth = m_ScaledDimensions.GetFloorIntX(); @@ -225,13 +230,21 @@ namespace RTE { // Detect if non-wrapping layer dimensions can't cover the whole target area with its main bitmap. If so, fill in the gap with appropriate solid color sampled from the hanging edge. if (!m_WrapX && bitmapWidth <= targetBoxWidth) { - if (m_FillColorLeft != ColorKeys::g_MaskColor && m_Offset.GetFloorIntX() != 0) { rectfill(targetBitmap, targetBoxCornerX, targetBoxCornerY, targetBoxCornerX - m_Offset.GetFloorIntX(), targetBoxCornerY + targetBoxHeight, m_FillColorLeft); } - if (m_FillColorRight != ColorKeys::g_MaskColor) { rectfill(targetBitmap, targetBoxCornerX + bitmapWidth - m_Offset.GetFloorIntX(), targetBoxCornerY, targetBoxCornerX + targetBoxWidth, targetBoxCornerY + targetBoxHeight, m_FillColorRight); } + if (m_FillColorLeft != ColorKeys::g_MaskColor && m_Offset.GetFloorIntX() != 0) { + rectfill(targetBitmap, targetBoxCornerX, targetBoxCornerY, targetBoxCornerX - m_Offset.GetFloorIntX(), targetBoxCornerY + targetBoxHeight, m_FillColorLeft); + } + if (m_FillColorRight != ColorKeys::g_MaskColor) { + rectfill(targetBitmap, targetBoxCornerX + bitmapWidth - m_Offset.GetFloorIntX(), targetBoxCornerY, targetBoxCornerX + targetBoxWidth, targetBoxCornerY + targetBoxHeight, m_FillColorRight); + } } if (!m_WrapY && bitmapHeight <= targetBoxHeight) { - if (m_FillColorUp != ColorKeys::g_MaskColor && m_Offset.GetFloorIntY() != 0) { rectfill(targetBitmap, targetBoxCornerX, targetBoxCornerY, targetBoxCornerX + targetBoxWidth, targetBoxCornerY - m_Offset.GetFloorIntY(), m_FillColorUp); } - if (m_FillColorDown != ColorKeys::g_MaskColor) { rectfill(targetBitmap, targetBoxCornerX, targetBoxCornerY + bitmapHeight - m_Offset.GetFloorIntY(), targetBoxCornerX + targetBoxWidth, targetBoxCornerY + targetBoxHeight, m_FillColorDown); } + if (m_FillColorUp != ColorKeys::g_MaskColor && m_Offset.GetFloorIntY() != 0) { + rectfill(targetBitmap, targetBoxCornerX, targetBoxCornerY, targetBoxCornerX + targetBoxWidth, targetBoxCornerY - m_Offset.GetFloorIntY(), m_FillColorUp); + } + if (m_FillColorDown != ColorKeys::g_MaskColor) { + rectfill(targetBitmap, targetBoxCornerX, targetBoxCornerY + bitmapHeight - m_Offset.GetFloorIntY(), targetBoxCornerX + targetBoxWidth, targetBoxCornerY + targetBoxHeight, m_FillColorDown); + } } set_clip_rect(targetBitmap, 0, 0, targetBitmap->w - 1, targetBitmap->h - 1); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/SLBackground.h b/Source/Entities/SLBackground.h index ffc122589b..e9a4e22820 100644 --- a/Source/Entities/SLBackground.h +++ b/Source/Entities/SLBackground.h @@ -13,7 +13,6 @@ namespace RTE { friend class NetworkServer; public: - EntityAllocation(SLBackground); SerializableOverrideMethods; ClassInfoGetters; @@ -35,7 +34,7 @@ namespace RTE { /// /// A reference to the SLBackground to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const SLBackground &reference); + int Create(const SLBackground& reference); #pragma endregion #pragma region Destruction @@ -48,7 +47,12 @@ namespace RTE { /// Destroys and resets (through Clear()) the SLBackground object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { SceneLayer::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + SceneLayer::Destroy(); + } + Clear(); + } #pragma endregion #pragma region Getters and Setters @@ -152,7 +156,7 @@ namespace RTE { /// Sets the auto-scroll step (pixels to advance per interval) values. /// /// A Vector with the new auto-scroll step values. - void SetAutoScrollStep(const Vector &newStep) { m_AutoScrollStep = newStep; } + void SetAutoScrollStep(const Vector& newStep) { m_AutoScrollStep = newStep; } /// /// Gets the auto-scroll step (pixels to advance per interval) value on the X axis. @@ -199,19 +203,23 @@ namespace RTE { /// The bitmap to draw to. /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. /// Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. - void Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment = false) override; + void Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment = false) override; #pragma endregion private: - /// /// Enumeration for the different modes of SLBackground auto-scaling. /// - enum LayerAutoScaleMode { AutoScaleOff, FitScreen, AlwaysUpscaled, LayerAutoScaleModeCount }; + enum LayerAutoScaleMode { + AutoScaleOff, + FitScreen, + AlwaysUpscaled, + LayerAutoScaleModeCount + }; static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. - std::vector m_Bitmaps; //!< Vector containing all the BITMAPs of this SLBackground. Not owned. + std::vector m_Bitmaps; //!< Vector containing all the BITMAPs of this SLBackground. Not owned. int m_FrameCount; //!< The total number of frames in this SLBackground's animation. int m_Frame; //!< The frame that is currently being shown/drawn. @@ -242,8 +250,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - SLBackground(const SLBackground &reference) = delete; - SLBackground & operator=(const SLBackground &rhs) = delete; + SLBackground(const SLBackground& reference) = delete; + SLBackground& operator=(const SLBackground& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/SLTerrain.cpp b/Source/Entities/SLTerrain.cpp index f44a040fe0..3d48367f96 100644 --- a/Source/Entities/SLTerrain.cpp +++ b/Source/Entities/SLTerrain.cpp @@ -13,7 +13,7 @@ namespace RTE { ConcreteClassInfo(SLTerrain, SceneLayer, 0); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SLTerrain::Clear() { m_Width = 0; @@ -29,7 +29,7 @@ namespace RTE { m_OrbitDirection = Directions::Up; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int SLTerrain::Create() { SceneLayer::Create(); @@ -37,36 +37,40 @@ namespace RTE { m_Width = m_BitmapFile.GetImageWidth(); m_Height = m_BitmapFile.GetImageHeight(); - if (!m_FGColorLayer.get()) { m_FGColorLayer = std::make_unique(); } - if (!m_BGColorLayer.get()) { m_BGColorLayer = std::make_unique(); } + if (!m_FGColorLayer.get()) { + m_FGColorLayer = std::make_unique(); + } + if (!m_BGColorLayer.get()) { + m_BGColorLayer = std::make_unique(); + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::Create(const SLTerrain &reference) { + int SLTerrain::Create(const SLTerrain& reference) { SceneLayer::Create(reference); m_Width = reference.m_Width; m_Height = reference.m_Height; // Copy the layers but not the layer BITMAPs because they will be loaded later by LoadData. - m_FGColorLayer.reset(dynamic_cast(reference.m_FGColorLayer->Clone())); - m_BGColorLayer.reset(dynamic_cast(reference.m_BGColorLayer->Clone())); + m_FGColorLayer.reset(dynamic_cast(reference.m_FGColorLayer->Clone())); + m_BGColorLayer.reset(dynamic_cast(reference.m_BGColorLayer->Clone())); m_DefaultBGTextureFile = reference.m_DefaultBGTextureFile; m_TerrainFrostings.clear(); - for (TerrainFrosting *terrainFrosting : reference.m_TerrainFrostings) { + for (TerrainFrosting* terrainFrosting: reference.m_TerrainFrostings) { m_TerrainFrostings.emplace_back(terrainFrosting); } m_TerrainDebris.clear(); - for (TerrainDebris *terrainDebris : reference.m_TerrainDebris) { + for (TerrainDebris* terrainDebris: reference.m_TerrainDebris) { m_TerrainDebris.emplace_back(terrainDebris); } m_TerrainObjects.clear(); - for (TerrainObject *terrainObject : reference.m_TerrainObjects) { + for (TerrainObject* terrainObject: reference.m_TerrainObjects) { m_TerrainObjects.emplace_back(terrainObject); } @@ -75,11 +79,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::ReadProperty(const std::string_view &propName, Reader &reader) { + int SLTerrain::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneLayer::ReadProperty(propName, reader)); - + MatchProperty("BackgroundTexture", { reader >> m_DefaultBGTextureFile; }); MatchProperty("FGColorLayer", { m_FGColorLayer = std::make_unique(); @@ -119,13 +123,13 @@ namespace RTE { reader.ReportError("Unknown OrbitDirection '" + orbitDirection + "'!"); } }); - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::Save(Writer &writer) const { + int SLTerrain::Save(Writer& writer) const { SceneLayer::Save(writer); // Only write the background texture info if the background itself is not saved out as a file already, since saved, pre-rendered bitmaps don't need texturing. @@ -139,13 +143,13 @@ namespace RTE { if (m_FGColorLayer->IsLoadedFromDisk()) { writer.NewPropertyWithValue("FGColorLayer", m_FGColorLayer.get()); } else { - for (const TerrainFrosting *terrainFrosting : m_TerrainFrostings) { + for (const TerrainFrosting* terrainFrosting: m_TerrainFrostings) { writer.NewPropertyWithValue("AddTerrainFrosting", terrainFrosting); } - for (const TerrainDebris *terrainDebris : m_TerrainDebris) { + for (const TerrainDebris* terrainDebris: m_TerrainDebris) { writer.NewPropertyWithValue("AddTerrainDebris", terrainDebris); } - for (const TerrainObject *terrainObject : m_TerrainObjects) { + for (const TerrainObject* terrainObject: m_TerrainObjects) { // Write out only what is needed to place a copy of this in the Terrain writer.NewProperty("PlaceTerrainObject"); writer.ObjectStart(terrainObject->GetClassName()); @@ -157,39 +161,39 @@ namespace RTE { writer.NewProperty("OrbitDirection"); switch (m_OrbitDirection) { - default: - case Directions::Up: - writer << "Up"; - break; - case Directions::Down: - writer << "Down"; - break; - case Directions::Left: - writer << "Left"; - break; - case Directions::Right: - writer << "Right"; - break; + default: + case Directions::Up: + writer << "Up"; + break; + case Directions::Down: + writer << "Down"; + break; + case Directions::Left: + writer << "Left"; + break; + case Directions::Right: + writer << "Right"; + break; } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: Break this down and refactor. void SLTerrain::TexturizeTerrain() { - BITMAP *defaultBGLayerTexture = m_DefaultBGTextureFile.GetAsBitmap(); + BITMAP* defaultBGLayerTexture = m_DefaultBGTextureFile.GetAsBitmap(); BITMAP* fgLayerTexture = m_FGColorLayer->GetBitmap(); BITMAP* bgLayerTexture = m_BGColorLayer->GetBitmap(); - const std::array &materialPalette = g_SceneMan.GetMaterialPalette(); - const std::array &materialMappings = g_PresetMan.GetDataModule(m_BitmapFile.GetDataModuleID())->GetAllMaterialMappings(); + const std::array& materialPalette = g_SceneMan.GetMaterialPalette(); + const std::array& materialMappings = g_PresetMan.GetDataModule(m_BitmapFile.GetDataModuleID())->GetAllMaterialMappings(); - std::array materialFGTextures; + std::array materialFGTextures; materialFGTextures.fill(nullptr); - std::array materialBGTextures; + std::array materialBGTextures; materialBGTextures.fill(nullptr); std::array materialColors; materialColors.fill(0); @@ -202,64 +206,64 @@ namespace RTE { // Go through each pixel on the main bitmap, which contains all the material pixels loaded from the bitmap. // Place texture pixels on the FG layer corresponding to the materials on the main material bitmap. std::for_each(std::execution::par_unseq, std::begin(rows), std::end(rows), - [&](int yPos) { - for (int xPos = 0; xPos < m_MainBitmap->w; ++xPos) { - int matIndex = _getpixel(m_MainBitmap, xPos, yPos); - - // Map any materials defined in this data module but initially collided with other material ID's and thus were displaced to other ID's. - if (materialMappings[matIndex] != 0) { - // Assign the mapping and put it onto the material bitmap too. - matIndex = materialMappings[matIndex]; - _putpixel(m_MainBitmap, xPos, yPos, matIndex); - } - - RTEAssert(matIndex >= 0 && matIndex < c_PaletteEntriesNumber, "Invalid material index!"); - - // Validate the material, or fallback to default material. - const Material* material = materialPalette[matIndex] ? materialPalette[matIndex] : materialPalette[MaterialColorKeys::g_MaterialOutOfBounds]; - - BITMAP* fgTexture = materialFGTextures[matIndex]; - BITMAP* bgTexture = materialBGTextures[matIndex]; - - // If haven't read a pixel of this material before, then get its texture so we can quickly access it. - if (!fgTexture && material->GetFGTexture()) { - fgTexture = materialFGTextures[matIndex] = material->GetFGTexture(); - } - - if (!bgTexture && material->GetBGTexture()) { - bgTexture = materialBGTextures[matIndex] = material->GetBGTexture(); - } - - int fgPixelColor = 0; - - // If actually no texture for the material, then use the material's solid color instead. - if (!fgTexture) { - if (materialColors[matIndex] == 0) { - materialColors[matIndex] = material->GetColor().GetIndex(); - } - fgPixelColor = materialColors[matIndex]; - } else { - fgPixelColor = _getpixel(fgTexture, xPos % fgTexture->w, yPos % fgTexture->h); - } - _putpixel(fgLayerTexture, xPos, yPos, fgPixelColor); - - int bgPixelColor = 0; - if (matIndex == 0) { - bgPixelColor = ColorKeys::g_MaskColor; - } else { - if (!bgTexture) { - bgPixelColor = _getpixel(defaultBGLayerTexture, xPos % defaultBGLayerTexture->w, yPos % defaultBGLayerTexture->h); - } else { - bgPixelColor = _getpixel(bgTexture, xPos % bgTexture->w, yPos% bgTexture->h); - } - } - - _putpixel(bgLayerTexture, xPos, yPos, bgPixelColor); - } - }); + [&](int yPos) { + for (int xPos = 0; xPos < m_MainBitmap->w; ++xPos) { + int matIndex = _getpixel(m_MainBitmap, xPos, yPos); + + // Map any materials defined in this data module but initially collided with other material ID's and thus were displaced to other ID's. + if (materialMappings[matIndex] != 0) { + // Assign the mapping and put it onto the material bitmap too. + matIndex = materialMappings[matIndex]; + _putpixel(m_MainBitmap, xPos, yPos, matIndex); + } + + RTEAssert(matIndex >= 0 && matIndex < c_PaletteEntriesNumber, "Invalid material index!"); + + // Validate the material, or fallback to default material. + const Material* material = materialPalette[matIndex] ? materialPalette[matIndex] : materialPalette[MaterialColorKeys::g_MaterialOutOfBounds]; + + BITMAP* fgTexture = materialFGTextures[matIndex]; + BITMAP* bgTexture = materialBGTextures[matIndex]; + + // If haven't read a pixel of this material before, then get its texture so we can quickly access it. + if (!fgTexture && material->GetFGTexture()) { + fgTexture = materialFGTextures[matIndex] = material->GetFGTexture(); + } + + if (!bgTexture && material->GetBGTexture()) { + bgTexture = materialBGTextures[matIndex] = material->GetBGTexture(); + } + + int fgPixelColor = 0; + + // If actually no texture for the material, then use the material's solid color instead. + if (!fgTexture) { + if (materialColors[matIndex] == 0) { + materialColors[matIndex] = material->GetColor().GetIndex(); + } + fgPixelColor = materialColors[matIndex]; + } else { + fgPixelColor = _getpixel(fgTexture, xPos % fgTexture->w, yPos % fgTexture->h); + } + _putpixel(fgLayerTexture, xPos, yPos, fgPixelColor); + + int bgPixelColor = 0; + if (matIndex == 0) { + bgPixelColor = ColorKeys::g_MaskColor; + } else { + if (!bgTexture) { + bgPixelColor = _getpixel(defaultBGLayerTexture, xPos % defaultBGLayerTexture->w, yPos % defaultBGLayerTexture->h); + } else { + bgPixelColor = _getpixel(bgTexture, xPos % bgTexture->w, yPos % bgTexture->h); + } + } + + _putpixel(bgLayerTexture, xPos, yPos, bgPixelColor); + } + }); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int SLTerrain::LoadData() { SceneLayer::LoadData(); @@ -279,13 +283,13 @@ namespace RTE { TexturizeTerrain(); - for (const TerrainFrosting *terrainFrosting : m_TerrainFrostings) { + for (const TerrainFrosting* terrainFrosting: m_TerrainFrostings) { terrainFrosting->FrostTerrain(this); } - for (TerrainDebris *terrainDebris : m_TerrainDebris) { + for (TerrainDebris* terrainDebris: m_TerrainDebris) { terrainDebris->ScatterOnTerrain(this); } - for (TerrainObject *terrainObject : m_TerrainObjects) { + for (TerrainObject* terrainObject: m_TerrainObjects) { terrainObject->PlaceOnTerrain(this); } CleanAir(); @@ -293,9 +297,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::SaveData(const std::string &pathBase, bool doAsyncSaves) { + int SLTerrain::SaveData(const std::string& pathBase, bool doAsyncSaves) { if (pathBase.empty()) { return -1; } @@ -305,7 +309,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int SLTerrain::ClearData() { RTEAssert(SceneLayer::ClearData() == 0, "Failed to clear material bitmap data of an SLTerrain!"); @@ -314,16 +318,16 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SLTerrain::IsAirPixel(const int pixelX, const int pixelY) const { int checkPixel = GetPixel(pixelX, pixelY); return checkPixel == MaterialColorKeys::g_MaterialAir || checkPixel == MaterialColorKeys::g_MaterialCavity; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SLTerrain::IsBoxBuried(const Box &checkBox) const { + bool SLTerrain::IsBoxBuried(const Box& checkBox) const { bool buried = true; buried = buried && !IsAirPixel(checkBox.GetCorner().GetFloorIntX(), checkBox.GetCorner().GetFloorIntY()); buried = buried && !IsAirPixel(static_cast(checkBox.GetCorner().GetX() + checkBox.GetWidth()), checkBox.GetCorner().GetFloorIntY()); @@ -332,28 +336,30 @@ namespace RTE { return buried; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SLTerrain::CleanAir() { std::vector rows(m_MainBitmap->h); // we loop through h first, because we want each thread to have sequential memory that they're touching std::iota(std::begin(rows), std::end(rows), 0); std::for_each(std::execution::par_unseq, std::begin(rows), std::end(rows), - [&](int yPos) { - for (int xPos = 0; xPos < m_MainBitmap->w; ++xPos) { - int matPixel = _getpixel(m_MainBitmap, xPos, yPos); - if (matPixel == MaterialColorKeys::g_MaterialCavity) { - _putpixel(m_MainBitmap, xPos, yPos, MaterialColorKeys::g_MaterialAir); - matPixel = MaterialColorKeys::g_MaterialAir; - } - if (matPixel == MaterialColorKeys::g_MaterialAir) { _putpixel(m_FGColorLayer->GetBitmap(), xPos, yPos, ColorKeys::g_MaskColor); } - } - }); + [&](int yPos) { + for (int xPos = 0; xPos < m_MainBitmap->w; ++xPos) { + int matPixel = _getpixel(m_MainBitmap, xPos, yPos); + if (matPixel == MaterialColorKeys::g_MaterialCavity) { + _putpixel(m_MainBitmap, xPos, yPos, MaterialColorKeys::g_MaterialAir); + matPixel = MaterialColorKeys::g_MaterialAir; + } + if (matPixel == MaterialColorKeys::g_MaterialAir) { + _putpixel(m_FGColorLayer->GetBitmap(), xPos, yPos, ColorKeys::g_MaskColor); + } + } + }); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLTerrain::CleanAirBox(const Box &box, bool wrapsX, bool wrapsY) { + void SLTerrain::CleanAirBox(const Box& box, bool wrapsX, bool wrapsY) { int width = m_MainBitmap->w; int height = m_MainBitmap->h; @@ -363,12 +369,20 @@ namespace RTE { int wrappedY = y; if (wrapsX) { - if (wrappedX < 0) { wrappedX += width; } - if (wrappedX >= width) { wrappedX -= width; } + if (wrappedX < 0) { + wrappedX += width; + } + if (wrappedX >= width) { + wrappedX -= width; + } } if (wrapsY) { - if (wrappedY < 0) { wrappedY += height; } - if (wrappedY >= height) { wrappedY -= height; } + if (wrappedY < 0) { + wrappedY += height; + } + if (wrappedY >= height) { + wrappedY -= height; + } } if (wrappedX >= 0 && wrappedX < width && wrappedY >= 0 && wrappedY < height) { int matPixel = _getpixel(m_MainBitmap, wrappedX, wrappedY); @@ -376,16 +390,18 @@ namespace RTE { _putpixel(m_MainBitmap, wrappedX, wrappedY, MaterialColorKeys::g_MaterialAir); matPixel = MaterialColorKeys::g_MaterialAir; } - if (matPixel == MaterialColorKeys::g_MaterialAir) { _putpixel(m_FGColorLayer->GetBitmap(), wrappedX, wrappedY, ColorKeys::g_MaskColor); } + if (matPixel == MaterialColorKeys::g_MaterialAir) { + _putpixel(m_FGColorLayer->GetBitmap(), wrappedX, wrappedY, ColorKeys::g_MaskColor); + } } } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: OPTIMIZE THIS, IT'S A TIME HOG. MAYBE JSUT STAMP THE OUTLINE AND SAMPLE SOME RANDOM PARTICLES? - std::deque SLTerrain::EraseSilhouette(BITMAP *sprite, const Vector &pos, const Vector &pivot, const Matrix &rotation, float scale, bool makeMOPs, int skipMOP, int maxMOPs) { + std::deque SLTerrain::EraseSilhouette(BITMAP* sprite, const Vector& pos, const Vector& pivot, const Matrix& rotation, float scale, bool makeMOPs, int skipMOP, int maxMOPs) { RTEAssert(sprite, "Null BITMAP passed to SLTerrain::EraseSilhouette"); int maxWidth = static_cast(static_cast(sprite->w + std::abs(pivot.GetFloorIntX() - (sprite->w / 2))) * scale); @@ -393,11 +409,11 @@ namespace RTE { int maxDiameter = static_cast(std::sqrt(static_cast(maxWidth * maxWidth + maxHeight * maxHeight)) * 2.0F); int skipCount = skipMOP; - BITMAP *tempBitmap = g_SceneMan.GetIntermediateBitmapForSettlingIntoTerrain(maxDiameter); + BITMAP* tempBitmap = g_SceneMan.GetIntermediateBitmapForSettlingIntoTerrain(maxDiameter); clear_bitmap(tempBitmap); pivot_scaled_sprite(tempBitmap, sprite, tempBitmap->w / 2, tempBitmap->h / 2, pivot.GetFloorIntX(), pivot.GetFloorIntY(), ftofix(rotation.GetAllegroAngle()), ftofix(scale)); - std::deque dislodgedMOPixels; + std::deque dislodgedMOPixels; // Test intersection between color pixels of the test bitmap and non-air pixels of the terrain, then generate and collect MOPixels that represent the terrain overlap and clear the same pixels out of the terrain. for (int testY = 0; testY < tempBitmap->h; ++testY) { @@ -444,8 +460,8 @@ namespace RTE { // Only add PixelMO if we're not due to skip any. if (makeMOPs && matPixel != MaterialColorKeys::g_MaterialAir && colorPixel != ColorKeys::g_MaskColor && ++skipCount > skipMOP && dislodgedMOPixels.size() < maxMOPs) { skipCount = 0; - const Material *sceneMat = g_SceneMan.GetMaterialFromID(matPixel); - const Material *spawnMat = sceneMat->GetSpawnMaterial() ? g_SceneMan.GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; + const Material* sceneMat = g_SceneMan.GetMaterialFromID(matPixel); + const Material* spawnMat = sceneMat->GetSpawnMaterial() ? g_SceneMan.GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; std::unique_ptr terrainPixelAtom = std::make_unique(Vector(), spawnMat->GetIndex(), nullptr, colorPixel, 2); std::unique_ptr terrainPixel = std::make_unique(colorPixel, spawnMat->GetPixelDensity(), Vector(static_cast(terrX), static_cast(terrY)), Vector(), terrainPixelAtom.release(), 0); @@ -457,7 +473,9 @@ namespace RTE { } // Clear the terrain pixels. - if (matPixel != MaterialColorKeys::g_MaterialAir) { putpixel(m_MainBitmap, terrX, terrY, MaterialColorKeys::g_MaterialAir); } + if (matPixel != MaterialColorKeys::g_MaterialAir) { + putpixel(m_MainBitmap, terrX, terrY, MaterialColorKeys::g_MaterialAir); + } if (colorPixel != ColorKeys::g_MaskColor) { putpixel(m_FGColorLayer->GetBitmap(), terrX, terrY, ColorKeys::g_MaskColor); g_SceneMan.RegisterTerrainChange(terrX, terrY, 1, 1, ColorKeys::g_MaskColor, false); @@ -471,7 +489,7 @@ namespace RTE { return dislodgedMOPixels; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SLTerrain::Update() { SceneLayer::Update(); @@ -480,9 +498,9 @@ namespace RTE { m_BGColorLayer->SetOffset(m_Offset); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLTerrain::Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment) { + void SLTerrain::Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment) { switch (m_LayerToDraw) { case LayerType::MaterialLayer: SceneLayer::Draw(targetBitmap, targetBox); @@ -498,4 +516,4 @@ namespace RTE { break; } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/SLTerrain.h b/Source/Entities/SLTerrain.h index 451188a5be..95b60f33e9 100644 --- a/Source/Entities/SLTerrain.h +++ b/Source/Entities/SLTerrain.h @@ -17,7 +17,6 @@ namespace RTE { class SLTerrain : public SceneLayer { public: - EntityAllocation(SLTerrain); SerializableOverrideMethods; ClassInfoGetters; @@ -25,7 +24,11 @@ namespace RTE { /// /// Enumeration for the different type of layers in the SLTerrain. /// - enum class LayerType { ForegroundLayer, BackgroundLayer, MaterialLayer }; + enum class LayerType { + ForegroundLayer, + BackgroundLayer, + MaterialLayer + }; #pragma region Creation /// @@ -44,7 +47,7 @@ namespace RTE { /// /// A reference to the SLTerrain to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const SLTerrain &reference); + int Create(const SLTerrain& reference); #pragma endregion #pragma region Destruction @@ -57,7 +60,12 @@ namespace RTE { /// Destroys and resets (through Clear()) the SLTerrain object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { SceneLayer::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + SceneLayer::Destroy(); + } + Clear(); + } #pragma endregion #pragma region Data Handling @@ -79,7 +87,7 @@ namespace RTE { /// The filepath base to the where to save the Bitmap data. This means everything up to the extension. "FG" and "Mat" etc will be added. /// Whether or not to save asynchronously. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int SaveData(const std::string &pathBase, bool doAsyncSaves = true) override; + int SaveData(const std::string& pathBase, bool doAsyncSaves = true) override; /// /// Clears out any previously loaded bitmap data from memory. @@ -111,19 +119,19 @@ namespace RTE { /// Gets the foreground color bitmap of this SLTerrain. /// /// A pointer to the foreground color bitmap. - BITMAP * GetFGColorBitmap() { return m_FGColorLayer->GetBitmap(); } + BITMAP* GetFGColorBitmap() { return m_FGColorLayer->GetBitmap(); } /// /// Gets the background color bitmap of this SLTerrain. /// /// A pointer to the background color bitmap. - BITMAP * GetBGColorBitmap() { return m_BGColorLayer->GetBitmap(); } + BITMAP* GetBGColorBitmap() { return m_BGColorLayer->GetBitmap(); } /// /// Gets the material bitmap of this SLTerrain. /// /// A pointer to the material bitmap. - BITMAP * GetMaterialBitmap() { return m_MainBitmap; } + BITMAP* GetMaterialBitmap() { return m_MainBitmap; } /// /// Gets a specific pixel from the foreground color bitmap of this. LockBitmaps() must be called before using this method. @@ -186,7 +194,7 @@ namespace RTE { /// /// The box to check. /// Whether the box is completely buried, i.e. no corner sticks out in the Air or Cavity. - bool IsBoxBuried(const Box &checkBox) const; + bool IsBoxBuried(const Box& checkBox) const; #pragma endregion #pragma region Concrete Methods @@ -194,13 +202,13 @@ namespace RTE { /// Gets a deque of unwrapped boxes which show the areas where the material layer has had objects applied to it since last call to ClearUpdatedMaterialAreas(). /// /// Reference to the deque that has been filled with Boxes which are unwrapped and may be out of bounds of the scene! - std::deque & GetUpdatedMaterialAreas() { return m_UpdatedMaterialAreas; } + std::deque& GetUpdatedMaterialAreas() { return m_UpdatedMaterialAreas; } /// /// Adds a notification that an area of the material terrain has been updated. /// /// The Box defining the newly updated material area that can be unwrapped and may be out of bounds of the scene. - void AddUpdatedMaterialArea(const Box &newArea) { m_UpdatedMaterialAreas.emplace_back(newArea); } + void AddUpdatedMaterialArea(const Box& newArea) { m_UpdatedMaterialAreas.emplace_back(newArea); } /// /// Removes any color pixel in the color layer of this SLTerrain wherever there is an air material pixel in the material layer. @@ -213,7 +221,7 @@ namespace RTE { /// Box to clean. /// Whether the scene is X-wrapped. /// Whether the scene is Y-wrapped. - void CleanAirBox(const Box &box, bool wrapsX, bool wrapsY); + void CleanAirBox(const Box& box, bool wrapsX, bool wrapsY); /// /// Takes a BITMAP and scans through the pixels on this terrain for pixels which overlap with it. Erases them from the terrain and can optionally generate MOPixels based on the erased or 'dislodged' terrain pixels. @@ -227,7 +235,7 @@ namespace RTE { /// How many pixels to skip making MOPixels from, between each that gets made. 0 means every pixel turns into an MOPixel. /// The max number of MOPixels to make, if they are to be made. /// A deque filled with the MOPixels of the terrain that are now dislodged. This will be empty if makeMOPs is false. Note that ownership of all the MOPixels in the deque IS transferred! - std::deque EraseSilhouette(BITMAP *sprite, const Vector &pos, const Vector &pivot, const Matrix &rotation, float scale, bool makeMOPs = true, int skipMOP = 2, int maxMOPs = 150); + std::deque EraseSilhouette(BITMAP* sprite, const Vector& pos, const Vector& pivot, const Matrix& rotation, float scale, bool makeMOPs = true, int skipMOP = 2, int maxMOPs = 150); /// /// Returns the direction of the out-of-bounds "orbit" for this scene, where the brain must path to and where dropships/rockets come from. @@ -248,11 +256,10 @@ namespace RTE { /// The bitmap to draw to. /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. /// Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. - void Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment = false) override; + void Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment = false) override; #pragma endregion private: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. int m_Width; //!< The width of this SLTerrain as determined by the main (material) bitmap, in pixels. @@ -265,9 +272,9 @@ namespace RTE { ContentFile m_DefaultBGTextureFile; //!< The background texture file that will be used to texturize Materials that have no defined background texture. - std::vector m_TerrainFrostings; //!< The TerrainFrostings that need to be placed on this SLTerrain. - std::vector m_TerrainDebris; //!< The TerrainDebris that need to be placed on this SLTerrain. - std::vector m_TerrainObjects; //!< The TerrainObjects that need to be placed on this SLTerrain. + std::vector m_TerrainFrostings; //!< The TerrainFrostings that need to be placed on this SLTerrain. + std::vector m_TerrainDebris; //!< The TerrainDebris that need to be placed on this SLTerrain. + std::vector m_TerrainObjects; //!< The TerrainObjects that need to be placed on this SLTerrain. std::deque m_UpdatedMaterialAreas; //!< List of areas of the material layer (main bitmap) which have been affected by new objects copied to it. These boxes are NOT wrapped, and can be out of bounds! @@ -284,8 +291,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - SLTerrain(const SLTerrain &reference) = delete; - SLTerrain & operator=(const SLTerrain &rhs) = delete; + SLTerrain(const SLTerrain& reference) = delete; + SLTerrain& operator=(const SLTerrain& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/Scene.cpp b/Source/Entities/Scene.cpp index 36f01ae673..ebf8644e6f 100644 --- a/Source/Entities/Scene.cpp +++ b/Source/Entities/Scene.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -49,3135 +48,2854 @@ namespace RTE { -ConcreteClassInfo(Scene, Entity, 0); -const std::string Scene::Area::c_ClassName = "Area"; + ConcreteClassInfo(Scene, Entity, 0); + const std::string Scene::Area::c_ClassName = "Area"; -// Holds the path calculated by CalculateScenePath -thread_local std::list s_ScenePath; + // Holds the path calculated by CalculateScenePath + thread_local std::list s_ScenePath; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Area, effectively + // resetting the members of this abstraction level only. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Area, effectively -// resetting the members of this abstraction level only. + void Scene::Area::Clear() { + m_BoxList.clear(); + m_Name.clear(); + } -void Scene::Area::Clear() -{ - m_BoxList.clear(); - m_Name.clear(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Area to be identical to another, by deep copy. + int Scene::Area::Create(const Area& reference) { + for (std::vector::const_iterator itr = reference.m_BoxList.begin(); itr != reference.m_BoxList.end(); ++itr) + m_BoxList.push_back(*itr); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Area to be identical to another, by deep copy. + m_Name = reference.m_Name; -int Scene::Area::Create(const Area &reference) -{ - for (std::vector::const_iterator itr = reference.m_BoxList.begin(); itr != reference.m_BoxList.end(); ++itr) - m_BoxList.push_back(*itr); + return 0; + } - m_Name = reference.m_Name; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Area object ready for use. - return 0; -} + int Scene::Area::Create() { + if (Serializable::Create() < 0) + return -1; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Area object ready for use. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. -int Scene::Area::Create() -{ - if (Serializable::Create() < 0) - return -1; + int Scene::Area::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Serializable::ReadProperty(propName, reader)); - return 0; -} + MatchProperty("AddBox", + Box box; + reader >> box; + m_BoxList.push_back(box);); + MatchProperty("Name", { reader >> m_Name; }); + EndPropertyList; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this Area with a Writer for + // later recreation with Create(Reader &reader); -int Scene::Area::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Serializable::ReadProperty(propName, reader)); - - MatchProperty("AddBox", - Box box; - reader >> box; - m_BoxList.push_back(box); ); - MatchProperty("Name", { reader >> m_Name; }); + int Scene::Area::Save(Writer& writer) const { + Serializable::Save(writer); - EndPropertyList; -} + for (std::vector::const_iterator itr = m_BoxList.begin(); itr != m_BoxList.end(); ++itr) { + writer.NewProperty("AddBox"); + writer << *itr; + } + writer.NewProperty("Name"); + writer << m_Name; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Area with a Writer for -// later recreation with Create(Reader &reader); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: AddBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a Box to this' area coverage. -int Scene::Area::Save(Writer &writer) const -{ - Serializable::Save(writer); + bool Scene::Area::AddBox(const Box& newBox) { + if (newBox.IsEmpty()) + return false; - for (std::vector::const_iterator itr = m_BoxList.begin(); itr != m_BoxList.end(); ++itr) - { - writer.NewProperty("AddBox"); - writer << *itr; - } - writer.NewProperty("Name"); - writer << m_Name; + m_BoxList.push_back(newBox); + return true; + } - return 0; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Scene::Area::RemoveBox(const Box& boxToRemove) { + std::vector::iterator boxToRemoveIterator = std::find(m_BoxList.begin(), m_BoxList.end(), boxToRemove); + if (boxToRemoveIterator != m_BoxList.end()) { + m_BoxList.erase(boxToRemoveIterator); + return true; + } + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: AddBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a Box to this' area coverage. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool Scene::Area::AddBox(const Box &newBox) -{ - if (newBox.IsEmpty()) - return false; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: HasNoArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this really has no Area at all, ie it doesn't have any + // Box:es with both width and height. - m_BoxList.push_back(newBox); - return true; -} + bool Scene::Area::HasNoArea() const { + // If no boxes, then yeah we don't have any area + if (m_BoxList.empty()) + return true; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Search through the boxes to see if we find any with both width and height + for (std::vector::const_iterator itr = m_BoxList.begin(); itr != m_BoxList.end(); ++itr) { + if (!itr->IsEmpty()) + return false; + } -bool Scene::Area::RemoveBox(const Box &boxToRemove) { - std::vector::iterator boxToRemoveIterator = std::find(m_BoxList.begin(), m_BoxList.end(), boxToRemove); - if (boxToRemoveIterator != m_BoxList.end()) { - m_BoxList.erase(boxToRemoveIterator); - return true; - } - return false; -} + return true; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsInside + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether a point is anywhere inside this Area's coverage. + + bool Scene::Area::IsInside(const Vector& point) const { + std::list wrappedBoxes; + for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { + // Handle wrapped boxes properly + wrappedBoxes.clear(); + g_SceneMan.WrapBox(*aItr, wrappedBoxes); + + // Iterate through the wrapped boxes - will only be one if there's no wrapping + for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + if (wItr->IsWithinBox(point)) + return true; + } + } + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsInsideX + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the + // X-axis only. + + bool Scene::Area::IsInsideX(float pointX) const { + std::list wrappedBoxes; + for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { + // Handle wrapped boxes properly + wrappedBoxes.clear(); + g_SceneMan.WrapBox(*aItr, wrappedBoxes); + + // Iterate through the wrapped boxes - will only be one if there's no wrapping + for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + if (wItr->IsWithinBoxX(pointX)) + return true; + } + } + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: HasNoArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this really has no Area at all, ie it doesn't have any -// Box:es with both width and height. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsInsideY + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the + // Y-axis only. + + bool Scene::Area::IsInsideY(float pointY) const { + std::list wrappedBoxes; + for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { + // Handle wrapped boxes properly + wrappedBoxes.clear(); + g_SceneMan.WrapBox(*aItr, wrappedBoxes); + + // Iterate through the wrapped boxes - will only be one if there's no wrapping + for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + if (wItr->IsWithinBoxY(pointY)) + return true; + } + } + return false; + } -bool Scene::Area::HasNoArea() const -{ - // If no boxes, then yeah we don't have any area - if (m_BoxList.empty()) - return true; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: MovePointInsideX + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Moves a coordinate to the closest value which is within any of this + // Area's Box:es, in the X axis only. + + bool Scene::Area::MovePointInsideX(float& pointX, int direction) const { + if (HasNoArea() || IsInsideX(pointX)) + return false; + + float notFoundValue = 10000000; + float shortest = notFoundValue; + float shortestConstrained = notFoundValue; + float testDistance = 0; + std::list wrappedBoxes; + for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { + // Handle wrapped boxes properly + wrappedBoxes.clear(); + g_SceneMan.WrapBox(*aItr, wrappedBoxes); + + // Iterate through the wrapped boxes - will only be one if there's no wrapping + for (std::list::const_iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + // Check against one edge of the box for the shortest distance + testDistance = g_SceneMan.ShortestDistanceX(pointX, (*wItr).GetCorner().m_X, false, direction); + // See if it's shorter than the shortest without constraints + if (fabs(testDistance) < fabs(shortest)) + shortest = testDistance; + // Also see if it's the shortest constrained distance + if (fabs(testDistance) < fabs(shortestConstrained) && (direction == 0 || (direction > 0 && testDistance > 0) || (direction < 0 && testDistance < 0))) + shortestConstrained = testDistance; + + // Then check against the other edge of the box + testDistance = g_SceneMan.ShortestDistanceX(pointX, (*wItr).GetCorner().m_X + (*wItr).GetWidth(), false, direction); + // See if it's shorter than the shortest without constraints + if (fabs(testDistance) < fabs(shortest)) + shortest = testDistance; + // Also see if it's the shortest constrained distance + if (fabs(testDistance) < fabs(shortestConstrained) && (direction == 0 || (direction > 0 && testDistance > 0) || (direction < 0 && testDistance < 0))) + shortestConstrained = testDistance; + } + } - // Search through the boxes to see if we find any with both width and height - for (std::vector::const_iterator itr = m_BoxList.begin(); itr != m_BoxList.end(); ++itr) - { - if (!itr->IsEmpty()) - return false; - } + // If we couldn't find any by adhering to the direction constraint, then use the shortest unconstrained found + if (shortestConstrained == notFoundValue) + pointX += shortest; + // Move the point the shortest distance we found, recognizing the direction constraints + else + pointX += shortestConstrained; - return true; -} + // Wrap it so it's inside the scene still + int x = pointX, crap = 0; + g_SceneMan.ForceBounds(x, crap); + pointX = x; + return true; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsInside -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether a point is anywhere inside this Area's coverage. - -bool Scene::Area::IsInside(const Vector &point) const -{ - std::list wrappedBoxes; - for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) - { - // Handle wrapped boxes properly - wrappedBoxes.clear(); - g_SceneMan.WrapBox(*aItr, wrappedBoxes); - - // Iterate through the wrapped boxes - will only be one if there's no wrapping - for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - if (wItr->IsWithinBox(point)) - return true; - } - } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetBoxInside + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the first Box encountered in this that contains a specific point. + + Box* Scene::Area::GetBoxInside(const Vector& point) { + std::list wrappedBoxes; + for (std::vector::iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { + // Handle wrapped boxes properly + wrappedBoxes.clear(); + g_SceneMan.WrapBox(*aItr, wrappedBoxes); + + // Iterate through the wrapped boxes - will only be one if there's no wrapping + for (std::list::const_iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + // Return the BoxList box, not the inconsequential wrapped copy + if (wItr->IsWithinBox(point)) + return &(*aItr); + } + } + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: RemoveBoxInside + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes the first Box encountered in this that contains a specific point. + + Box Scene::Area::RemoveBoxInside(const Vector& point) { + Box returnBox; + + std::list wrappedBoxes; + for (std::vector::iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { + // Handle wrapped boxes properly + wrappedBoxes.clear(); + g_SceneMan.WrapBox(*aItr, wrappedBoxes); + + // Iterate through the wrapped boxes - will only be one if there's no wrapping + for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + if (wItr->IsWithinBox(point)) { + // Remove the BoxList box, not the inconsequential wrapped copy + returnBox = (*aItr); + m_BoxList.erase(aItr); + return returnBox; + } + } + } + return returnBox; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsInsideX -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the -// X-axis only. - -bool Scene::Area::IsInsideX(float pointX) const -{ - std::list wrappedBoxes; - for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) - { - // Handle wrapped boxes properly - wrappedBoxes.clear(); - g_SceneMan.WrapBox(*aItr, wrappedBoxes); - - // Iterate through the wrapped boxes - will only be one if there's no wrapping - for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - if (wItr->IsWithinBoxX(pointX)) - return true; - } - } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetCenterPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a center point for this of all the boxes waeighted by their sizes. + // Arguments: None. + Vector Scene::Area::GetCenterPoint() const { + Vector areaCenter; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsInsideY -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the -// Y-axis only. - -bool Scene::Area::IsInsideY(float pointY) const -{ - std::list wrappedBoxes; - for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) - { - // Handle wrapped boxes properly - wrappedBoxes.clear(); - g_SceneMan.WrapBox(*aItr, wrappedBoxes); - - // Iterate through the wrapped boxes - will only be one if there's no wrapping - for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - if (wItr->IsWithinBoxY(pointY)) - return true; - } - } - return false; -} + if (!m_BoxList.empty()) { + if (m_BoxList.size() == 1) { + return m_BoxList[0].GetCenter(); + } + float totalWeight = 0; + for (std::vector::const_iterator itr = m_BoxList.begin(); itr != m_BoxList.end(); ++itr) { + // Doubly weighted + areaCenter += (*itr).GetCenter() * (*itr).GetArea() * 2; + totalWeight += (*itr).GetArea() * 2; + } + // Average center of the all the boxes, weighted by their respective areas + areaCenter /= totalWeight; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: MovePointInsideX -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Moves a coordinate to the closest value which is within any of this -// Area's Box:es, in the X axis only. - -bool Scene::Area::MovePointInsideX(float &pointX, int direction) const -{ - if (HasNoArea() || IsInsideX(pointX)) - return false; - - float notFoundValue = 10000000; - float shortest = notFoundValue; - float shortestConstrained = notFoundValue; - float testDistance = 0; - std::list wrappedBoxes; - for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) - { - // Handle wrapped boxes properly - wrappedBoxes.clear(); - g_SceneMan.WrapBox(*aItr, wrappedBoxes); - - // Iterate through the wrapped boxes - will only be one if there's no wrapping - for (std::list::const_iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - // Check against one edge of the box for the shortest distance - testDistance = g_SceneMan.ShortestDistanceX(pointX, (*wItr).GetCorner().m_X, false, direction); - // See if it's shorter than the shortest without constraints - if (fabs(testDistance) < fabs(shortest)) - shortest = testDistance; - // Also see if it's the shortest constrained distance - if (fabs(testDistance) < fabs(shortestConstrained) && (direction == 0 || (direction > 0 && testDistance > 0) || (direction < 0 && testDistance < 0))) - shortestConstrained = testDistance; - - // Then check against the other edge of the box - testDistance = g_SceneMan.ShortestDistanceX(pointX, (*wItr).GetCorner().m_X + (*wItr).GetWidth(), false, direction); - // See if it's shorter than the shortest without constraints - if (fabs(testDistance) < fabs(shortest)) - shortest = testDistance; - // Also see if it's the shortest constrained distance - if (fabs(testDistance) < fabs(shortestConstrained) && (direction == 0 || (direction > 0 && testDistance > 0) || (direction < 0 && testDistance < 0))) - shortestConstrained = testDistance; - } - } - - // If we couldn't find any by adhering to the direction constraint, then use the shortest unconstrained found - if (shortestConstrained == notFoundValue) - pointX += shortest; - // Move the point the shortest distance we found, recognizing the direction constraints - else - pointX += shortestConstrained; - - // Wrap it so it's inside the scene still - int x = pointX, crap = 0; - g_SceneMan.ForceBounds(x, crap); - pointX = x; - - return true; -} + return areaCenter; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRandomPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a random coordinate contained within any of this' Box:es. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetBoxInside -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the first Box encountered in this that contains a specific point. - -Box * Scene::Area::GetBoxInside(const Vector &point) -{ - std::list wrappedBoxes; - for (std::vector::iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) - { - // Handle wrapped boxes properly - wrappedBoxes.clear(); - g_SceneMan.WrapBox(*aItr, wrappedBoxes); - - // Iterate through the wrapped boxes - will only be one if there's no wrapping - for (std::list::const_iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - // Return the BoxList box, not the inconsequential wrapped copy - if (wItr->IsWithinBox(point)) - return &(*aItr); - } - } - return 0; -} + Vector Scene::Area::GetRandomPoint() const { + // If no boxes, then can't return valid point + if (m_BoxList.empty()) + return Vector(); + // Randomly choose a box, and a point within it + return m_BoxList[RandomNum(0, m_BoxList.size() - 1)].GetRandomPoint(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: RemoveBoxInside -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes the first Box encountered in this that contains a specific point. - -Box Scene::Area::RemoveBoxInside(const Vector &point) -{ - Box returnBox; - - std::list wrappedBoxes; - for (std::vector::iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) - { - // Handle wrapped boxes properly - wrappedBoxes.clear(); - g_SceneMan.WrapBox(*aItr, wrappedBoxes); - - // Iterate through the wrapped boxes - will only be one if there's no wrapping - for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - if (wItr->IsWithinBox(point)) - { - // Remove the BoxList box, not the inconsequential wrapped copy - returnBox = (*aItr); - m_BoxList.erase(aItr); - return returnBox; - } - } - } - return returnBox; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Scene, effectively + // resetting the members of this abstraction level only. + + void Scene::Clear() { + m_Location.Reset(); + m_LocationOffset.Reset(); + m_MetagamePlayable = false; // Let scenes be non-metagame playable by default, they need AI building plans anyway + m_Revealed = false; + m_OwnedByTeam = Activity::NoTeam; + m_RoundIncome = 1000; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + m_ResidentBrains[player] = 0; + m_BuildBudget[player] = 0; + m_BuildBudgetRatio[player] = 0; + } + m_AutoDesigned = true; + m_TotalInvestment = 0; + m_pTerrain = 0; + for (std::unique_ptr& pathFinder: m_pPathFinders) { + pathFinder.reset(); + } + m_PathfindingUpdated = false; + m_PartialPathUpdateTimer.Reset(); + + for (int set = PLACEONLOAD; set < PLACEDSETSCOUNT; ++set) + m_PlacedObjects[set].clear(); + m_BackLayerList.clear(); + m_Deployments.clear(); + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + m_UnseenPixelSize[team].Reset(); + m_apUnseenLayer[team] = 0; + m_SeenPixels[team].clear(); + m_CleanedPixels[team].clear(); + m_ScanScheduled[team] = false; + } + m_AreaList.clear(); + m_NavigatableAreas.clear(); + m_NavigatableAreasUpToDate = false; + m_Locked = false; + m_GlobalAcc.Reset(); + m_SelectedAssemblies.clear(); + m_AssembliesCounts.clear(); + m_pPreviewBitmap = 0; + m_MetasceneParent.clear(); + m_IsMetagameInternal = false; + m_IsSavedGameInternal = false; + } + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Scene object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetCenterPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a center point for this of all the boxes waeighted by their sizes. -// Arguments: None. + int Scene::Create() + { + if (Entity::Create() < 0) + return -1; -Vector Scene::Area::GetCenterPoint() const -{ - Vector areaCenter; + return 0; + } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Scene object ready for use. + // Arguments: The Terrain to use. Ownership IS transferred! + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Scene::Create(SLTerrain* pNewTerrain) { + m_pTerrain = pNewTerrain; + // TODO: allow setting of other stuff too + m_GlobalAcc = Vector(0, 20); + + return 0; + } - if (!m_BoxList.empty()) { - if (m_BoxList.size() == 1) { - return m_BoxList[0].GetCenter(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a MOPixel to be identical to another, by deep copy. + + int Scene::Create(const Scene& reference) { + Entity::Create(reference); + + m_Location = reference.m_Location; + m_LocationOffset = reference.m_LocationOffset; + m_MetagamePlayable = reference.m_MetagamePlayable; + m_Revealed = reference.m_Revealed; + m_OwnedByTeam = reference.m_OwnedByTeam; + m_RoundIncome = reference.m_RoundIncome; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (reference.m_ResidentBrains[player]) + m_ResidentBrains[player] = dynamic_cast(reference.m_ResidentBrains[player]->Clone()); + m_BuildBudget[player] = reference.m_BuildBudget[player]; + m_BuildBudgetRatio[player] = reference.m_BuildBudgetRatio[player]; } + m_AutoDesigned = reference.m_AutoDesigned; + m_TotalInvestment = reference.m_TotalInvestment; + m_pTerrain = dynamic_cast(reference.m_pTerrain->Clone()); - float totalWeight = 0; - for (std::vector::const_iterator itr = m_BoxList.begin(); itr != m_BoxList.end(); ++itr) - { - // Doubly weighted - areaCenter += (*itr).GetCenter() * (*itr).GetArea() * 2; - totalWeight += (*itr).GetArea() * 2; - } - // Average center of the all the boxes, weighted by their respective areas - areaCenter /= totalWeight; - } - - return areaCenter; -} - + for (int set = PLACEONLOAD; set < PLACEDSETSCOUNT; ++set) { + for (std::list::const_iterator oItr = reference.m_PlacedObjects[set].begin(); oItr != reference.m_PlacedObjects[set].end(); ++oItr) + m_PlacedObjects[set].push_back(dynamic_cast((*oItr)->Clone())); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetRandomPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a random coordinate contained within any of this' Box:es. + for (std::list::const_iterator lItr = reference.m_BackLayerList.begin(); lItr != reference.m_BackLayerList.end(); ++lItr) + m_BackLayerList.push_back(dynamic_cast((*lItr)->Clone())); -Vector Scene::Area::GetRandomPoint() const -{ - // If no boxes, then can't return valid point - if (m_BoxList.empty()) - return Vector(); + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + // If the Unseen layers are loaded, then copy them. If not, then copy the procedural param that is responsible for creating them + if (reference.m_apUnseenLayer[team]) + m_apUnseenLayer[team] = dynamic_cast(reference.m_apUnseenLayer[team]->Clone()); + else + m_UnseenPixelSize[team] = reference.m_UnseenPixelSize[team]; - // Randomly choose a box, and a point within it - return m_BoxList[RandomNum(0, m_BoxList.size() - 1)].GetRandomPoint(); -} + // Always copy the scan scheduling flags + m_ScanScheduled[team] = reference.m_ScanScheduled[team]; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Scene, effectively -// resetting the members of this abstraction level only. - -void Scene::Clear() -{ - m_Location.Reset(); - m_LocationOffset.Reset(); - m_MetagamePlayable = false; // Let scenes be non-metagame playable by default, they need AI building plans anyway - m_Revealed = false; - m_OwnedByTeam = Activity::NoTeam; - m_RoundIncome = 1000; - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - m_ResidentBrains[player] = 0; - m_BuildBudget[player] = 0; - m_BuildBudgetRatio[player] = 0; - } - m_AutoDesigned = true; - m_TotalInvestment = 0; - m_pTerrain = 0; - for (std::unique_ptr &pathFinder : m_pPathFinders) { - pathFinder.reset(); - } - m_PathfindingUpdated = false; - m_PartialPathUpdateTimer.Reset(); - - for (int set = PLACEONLOAD; set < PLACEDSETSCOUNT; ++set) - m_PlacedObjects[set].clear(); - m_BackLayerList.clear(); - m_Deployments.clear(); - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - m_UnseenPixelSize[team].Reset(); - m_apUnseenLayer[team] = 0; - m_SeenPixels[team].clear(); - m_CleanedPixels[team].clear(); - m_ScanScheduled[team] = false; - } - m_AreaList.clear(); - m_NavigatableAreas.clear(); - m_NavigatableAreasUpToDate = false; - m_Locked = false; - m_GlobalAcc.Reset(); - m_SelectedAssemblies.clear(); - m_AssembliesCounts.clear(); - m_pPreviewBitmap = 0; - m_MetasceneParent.clear(); - m_IsMetagameInternal = false; - m_IsSavedGameInternal = false; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Scene object ready for use. + // Copy areas + for (std::list::const_iterator aItr = reference.m_AreaList.begin(); aItr != reference.m_AreaList.end(); ++aItr) + m_AreaList.push_back(*aItr); -int Scene::Create() -{ - if (Entity::Create() < 0) - return -1; + m_GlobalAcc = reference.m_GlobalAcc; - return 0; -} -*/ + // Deep copy of the bitmap + if (reference.m_pPreviewBitmap) { + // Copy the bitmap from the ContentFile, because we're going to be changing it! + BITMAP* pCopyFrom = reference.m_pPreviewBitmap; + // Destination + m_pPreviewBitmap = create_bitmap_ex(8, pCopyFrom->w, pCopyFrom->h); + RTEAssert(m_pPreviewBitmap, "Failed to allocate BITMAP in Scene::Create"); + m_PreviewBitmapFile = reference.m_PreviewBitmapFile; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Scene object ready for use. -// Arguments: The Terrain to use. Ownership IS transferred! -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. + // Copy! + blit(pCopyFrom, m_pPreviewBitmap, 0, 0, 0, 0, pCopyFrom->w, pCopyFrom->h); + } -int Scene::Create(SLTerrain *pNewTerrain) -{ - m_pTerrain = pNewTerrain; -// TODO: allow setting of other stuff too - m_GlobalAcc = Vector(0, 20); + m_MetasceneParent = reference.m_MetasceneParent; + m_IsMetagameInternal = reference.m_IsMetagameInternal; + m_IsSavedGameInternal = reference.m_IsSavedGameInternal; + return 0; + } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: LoadData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Actually loads previously specified/created data into memory. Has + // to be done before using this SceneLayer. + int Scene::LoadData(bool placeObjects, bool initPathfinding, bool placeUnits) { + RTEAssert(m_pTerrain, "Terrain not instantiated before trying to load its data!"); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a MOPixel to be identical to another, by deep copy. - -int Scene::Create(const Scene &reference) -{ - Entity::Create(reference); - - m_Location = reference.m_Location; - m_LocationOffset = reference.m_LocationOffset; - m_MetagamePlayable = reference.m_MetagamePlayable; - m_Revealed = reference.m_Revealed; - m_OwnedByTeam = reference.m_OwnedByTeam; - m_RoundIncome = reference.m_RoundIncome; - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (reference.m_ResidentBrains[player]) - m_ResidentBrains[player] = dynamic_cast(reference.m_ResidentBrains[player]->Clone()); - m_BuildBudget[player] = reference.m_BuildBudget[player]; - m_BuildBudgetRatio[player] = reference.m_BuildBudgetRatio[player]; - } - m_AutoDesigned = reference.m_AutoDesigned; - m_TotalInvestment = reference.m_TotalInvestment; - m_pTerrain = dynamic_cast(reference.m_pTerrain->Clone()); - - for (int set = PLACEONLOAD; set < PLACEDSETSCOUNT; ++set) - { - for (std::list::const_iterator oItr = reference.m_PlacedObjects[set].begin(); oItr != reference.m_PlacedObjects[set].end(); ++oItr) - m_PlacedObjects[set].push_back(dynamic_cast((*oItr)->Clone())); - } - - for (std::list::const_iterator lItr = reference.m_BackLayerList.begin(); lItr != reference.m_BackLayerList.end(); ++lItr) - m_BackLayerList.push_back(dynamic_cast((*lItr)->Clone())); - - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - // If the Unseen layers are loaded, then copy them. If not, then copy the procedural param that is responsible for creating them - if (reference.m_apUnseenLayer[team]) - m_apUnseenLayer[team] = dynamic_cast(reference.m_apUnseenLayer[team]->Clone()); - else - m_UnseenPixelSize[team] = reference.m_UnseenPixelSize[team]; - - // Always copy the scan scheduling flags - m_ScanScheduled[team] = reference.m_ScanScheduled[team]; - } - - // Copy areas - for (std::list::const_iterator aItr = reference.m_AreaList.begin(); aItr != reference.m_AreaList.end(); ++aItr) - m_AreaList.push_back(*aItr); - - m_GlobalAcc = reference.m_GlobalAcc; - - // Deep copy of the bitmap - if (reference.m_pPreviewBitmap) - { - // Copy the bitmap from the ContentFile, because we're going to be changing it! - BITMAP *pCopyFrom = reference.m_pPreviewBitmap; - // Destination - m_pPreviewBitmap = create_bitmap_ex(8, pCopyFrom->w, pCopyFrom->h); - RTEAssert(m_pPreviewBitmap, "Failed to allocate BITMAP in Scene::Create"); - m_PreviewBitmapFile = reference.m_PreviewBitmapFile; - - // Copy! - blit(pCopyFrom, m_pPreviewBitmap, 0, 0, 0, 0, pCopyFrom->w, pCopyFrom->h); - } - - m_MetasceneParent = reference.m_MetasceneParent; - m_IsMetagameInternal = reference.m_IsMetagameInternal; - m_IsSavedGameInternal = reference.m_IsSavedGameInternal; - return 0; -} + /////////////////////////////////// + // Load Terrain's data + if (m_pTerrain->LoadData() < 0) { + RTEAbort("Loading Terrain " + m_pTerrain->GetPresetName() + "\'s data failed!"); + return -1; + } + for (SLBackground* backgroundLayer: m_BackLayerList) { + backgroundLayer->InitScaleFactors(); + } + /////////////////////////////////// + // Load Unseen layers before applying objects to the scene, + // so we can reveal around stuff that is getting placed for the appropriate team + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + // Specified to dynamically create the unseen layer? + if (!m_UnseenPixelSize[team].IsZero()) { + // Create the bitmap to make the unseen scene layer out of + BITMAP* pUnseenBitmap = create_bitmap_ex(8, GetWidth() / m_UnseenPixelSize[team].m_X, GetHeight() / m_UnseenPixelSize[team].m_Y); + clear_to_color(pUnseenBitmap, g_BlackColor); + // Replace any old unseen layer with the new one that is generated + delete m_apUnseenLayer[team]; + m_apUnseenLayer[team] = new SceneLayer(); + m_apUnseenLayer[team]->Create(pUnseenBitmap, true, Vector(), WrapsX(), WrapsY(), Vector(1.0, 1.0)); + m_apUnseenLayer[team]->SetScaleFactor(m_UnseenPixelSize[team]); + } + // If not dynamically generated, was it custom loaded? + else if (m_apUnseenLayer[team]) { + // Load unseen layer data from file + if (m_apUnseenLayer[team]->LoadData() < 0) { + g_ConsoleMan.PrintString("ERROR: Loading unseen layer " + m_apUnseenLayer[team]->GetPresetName() + "\'s data failed!"); + return -1; + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LoadData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually loads previously specified/created data into memory. Has -// to be done before using this SceneLayer. - -int Scene::LoadData(bool placeObjects, bool initPathfinding, bool placeUnits) -{ - RTEAssert(m_pTerrain, "Terrain not instantiated before trying to load its data!"); - - /////////////////////////////////// - // Load Terrain's data - if (m_pTerrain->LoadData() < 0) - { - RTEAbort("Loading Terrain " + m_pTerrain->GetPresetName() + "\'s data failed!"); - return -1; - } - for (SLBackground *backgroundLayer : m_BackLayerList) { - backgroundLayer->InitScaleFactors(); - } - - /////////////////////////////////// - // Load Unseen layers before applying objects to the scene, - // so we can reveal around stuff that is getting placed for the appropriate team - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - // Specified to dynamically create the unseen layer? - if (!m_UnseenPixelSize[team].IsZero()) - { - // Create the bitmap to make the unseen scene layer out of - BITMAP *pUnseenBitmap = create_bitmap_ex(8, GetWidth() / m_UnseenPixelSize[team].m_X, GetHeight() / m_UnseenPixelSize[team].m_Y); - clear_to_color(pUnseenBitmap, g_BlackColor); - // Replace any old unseen layer with the new one that is generated - delete m_apUnseenLayer[team]; - m_apUnseenLayer[team] = new SceneLayer(); - m_apUnseenLayer[team]->Create(pUnseenBitmap, true, Vector(), WrapsX(), WrapsY(), Vector(1.0, 1.0)); - m_apUnseenLayer[team]->SetScaleFactor(m_UnseenPixelSize[team]); - } - // If not dynamically generated, was it custom loaded? - else if (m_apUnseenLayer[team]) - { - // Load unseen layer data from file - if (m_apUnseenLayer[team]->LoadData() < 0) - { - g_ConsoleMan.PrintString("ERROR: Loading unseen layer " + m_apUnseenLayer[team]->GetPresetName() + "\'s data failed!"); - return -1; - } - } - } - - m_SelectedAssemblies.clear(); - m_AssembliesCounts.clear(); - - ////////////////////////////////// - // Place all the specified Scene Objects that are set up to be placed on load - // But don't if we are only loading the scene bitmap layer data - if (placeObjects) - { - Actor * pBrains[Activity::MaxTeamCount]; - for (int i = Activity::TeamOne; i < Activity::MaxTeamCount; i++) - pBrains[i] = 0; - - // Indicates whether we need to process static brain deployments or mobile - // whichever comes first is selected and used everywhere - std::string activeBrainDeployment[Activity::MaxTeamCount]; - - // Lists of found brain deployment locations used to place brain - std::vector brainLocations[Activity::MaxTeamCount]; - - //for (std::list::iterator oItr = m_PlacedObjects[AIPLAN].begin(); oItr != m_PlacedObjects[AIPLAN].end(); ++oItr) // I'm using this to dump AI plans with ctrl+w - for (std::list::iterator oItr = m_PlacedObjects[PLACEONLOAD].begin(); oItr != m_PlacedObjects[PLACEONLOAD].end(); ++oItr) - { - // MovableObject:s get added to the MovableMan - MovableObject *pMO = dynamic_cast(*oItr); - if (pMO) - { - // PASSING OWNERSHIP INTO the Add* ones - we are clearing out this list! - if (Actor *actor = dynamic_cast(pMO)) { - bool shouldPlace = placeUnits || actor->IsInGroup("Bunker Systems"); - - // Because we don't save/load all data yet and do a bit of a hack with scene loading, we can potentially save a dead actor that still technically exists. - // If we find one of these, just skip them! - //shouldPlace = shouldPlace && dynamic_cast(pMO)->GetHealth() > 0.0F; - - if (shouldPlace) { - g_MovableMan.AddActor(dynamic_cast(pMO)); + m_SelectedAssemblies.clear(); + m_AssembliesCounts.clear(); + + ////////////////////////////////// + // Place all the specified Scene Objects that are set up to be placed on load + // But don't if we are only loading the scene bitmap layer data + if (placeObjects) { + Actor* pBrains[Activity::MaxTeamCount]; + for (int i = Activity::TeamOne; i < Activity::MaxTeamCount; i++) + pBrains[i] = 0; + + // Indicates whether we need to process static brain deployments or mobile + // whichever comes first is selected and used everywhere + std::string activeBrainDeployment[Activity::MaxTeamCount]; + + // Lists of found brain deployment locations used to place brain + std::vector brainLocations[Activity::MaxTeamCount]; + + // for (std::list::iterator oItr = m_PlacedObjects[AIPLAN].begin(); oItr != m_PlacedObjects[AIPLAN].end(); ++oItr) // I'm using this to dump AI plans with ctrl+w + for (std::list::iterator oItr = m_PlacedObjects[PLACEONLOAD].begin(); oItr != m_PlacedObjects[PLACEONLOAD].end(); ++oItr) { + // MovableObject:s get added to the MovableMan + MovableObject* pMO = dynamic_cast(*oItr); + if (pMO) { + // PASSING OWNERSHIP INTO the Add* ones - we are clearing out this list! + if (Actor* actor = dynamic_cast(pMO)) { + bool shouldPlace = placeUnits || actor->IsInGroup("Bunker Systems"); + + // Because we don't save/load all data yet and do a bit of a hack with scene loading, we can potentially save a dead actor that still technically exists. + // If we find one of these, just skip them! + // shouldPlace = shouldPlace && dynamic_cast(pMO)->GetHealth() > 0.0F; + + if (shouldPlace) { + g_MovableMan.AddActor(dynamic_cast(pMO)); + } else { + delete pMO; + pMO = nullptr; + } } else { - delete pMO; - pMO = nullptr; + g_MovableMan.AddMO(pMO); } } else { - g_MovableMan.AddMO(pMO); - } - } - else - { - // Deployment:s are translated into the appropriate loadout and put in place in the scene - Deployment *pDep = dynamic_cast(*oItr); - if (pDep) - { - Deployment * pDepCopy = dynamic_cast(pDep->Clone()); - if (pDepCopy) - { - pDepCopy->CloneID(pDep); - m_Deployments.push_back(pDepCopy); - } + // Deployment:s are translated into the appropriate loadout and put in place in the scene + Deployment* pDep = dynamic_cast(*oItr); + if (pDep) { + Deployment* pDepCopy = dynamic_cast(pDep->Clone()); + if (pDepCopy) { + pDepCopy->CloneID(pDep); + m_Deployments.push_back(pDepCopy); + } - if (placeUnits) - { - int team = pDep->GetTeam(); - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) - team = 0; + if (placeUnits) { + int team = pDep->GetTeam(); + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) + team = 0; - // Select first brain deployemnt type as the only deployment type for this team + // Select first brain deployemnt type as the only deployment type for this team - bool toIgnore = false; - // Ignore brain deployment if it's not matching the previously selected deployment type, or save it's position if it does - if (pDep->IsInGroup("Brains") && pDep->GetPresetName() != "Brain Hideout") - { - if (activeBrainDeployment[team] == "") - activeBrainDeployment[team] = pDep->GetPresetName(); + bool toIgnore = false; + // Ignore brain deployment if it's not matching the previously selected deployment type, or save it's position if it does + if (pDep->IsInGroup("Brains") && pDep->GetPresetName() != "Brain Hideout") { + if (activeBrainDeployment[team] == "") + activeBrainDeployment[team] = pDep->GetPresetName(); - if (pDep->GetPresetName() != activeBrainDeployment[team]) - toIgnore = true; - else - brainLocations[team].push_back(pDep->GetPos()); - } + if (pDep->GetPresetName() != activeBrainDeployment[team]) + toIgnore = true; + else + brainLocations[team].push_back(pDep->GetPos()); + } - // Ignore brain hideouts, they are used only by metagame when applying build budget - if (pDep->GetPresetName() == "Brain Hideout") - toIgnore = true; + // Ignore brain hideouts, they are used only by metagame when applying build budget + if (pDep->GetPresetName() == "Brain Hideout") + toIgnore = true; - if (!toIgnore) - { - // Ownership IS transferred here; pass it along into the MovableMan - float cost = 0; - Actor *pActor = pDep->CreateDeployedActor(pDep->GetPlacedByPlayer(), cost); - if (pActor) - { - // Treat brain eployements the special way - if (pDep->GetPresetName() == activeBrainDeployment[team] && pActor->IsInGroup("Brains")) - { - // If it's the first created brain then place it, otherwise just delete - if (pBrains[team] == 0) - { + if (!toIgnore) { + // Ownership IS transferred here; pass it along into the MovableMan + float cost = 0; + Actor* pActor = pDep->CreateDeployedActor(pDep->GetPlacedByPlayer(), cost); + if (pActor) { + // Treat brain eployements the special way + if (pDep->GetPresetName() == activeBrainDeployment[team] && pActor->IsInGroup("Brains")) { + // If it's the first created brain then place it, otherwise just delete + if (pBrains[team] == 0) { + g_MovableMan.AddActor(pActor); + pBrains[team] = pActor; + } else { + delete pActor; + pActor = 0; + } + } else g_MovableMan.AddActor(pActor); - pBrains[team] = pActor; - } - else - { - delete pActor; - pActor = 0; - } } - else - g_MovableMan.AddActor(pActor); - } - // Just a simple Device in the Deployment? - else - { - // Get the Item/Device and add to scene, passing ownership - SceneObject *pObject = pDep->CreateDeployedObject(pDep->GetPlacedByPlayer(), cost); - pMO = dynamic_cast(pObject); - if (pMO) - g_MovableMan.AddMO(pMO); - else - { - delete pObject; - pObject = 0; + // Just a simple Device in the Deployment? + else { + // Get the Item/Device and add to scene, passing ownership + SceneObject* pObject = pDep->CreateDeployedObject(pDep->GetPlacedByPlayer(), cost); + pMO = dynamic_cast(pObject); + if (pMO) + g_MovableMan.AddMO(pMO); + else { + delete pObject; + pObject = 0; + } } } } } - } - // Has to be a TerrainObject then, so apply to terrain - else - { - //Place random BunkerAssembly instead of BunkerAssemblyScheme if this is a BunkerAssemblyScheme - TerrainObject *pTO = 0; - BunkerAssemblyScheme *pBAS = dynamic_cast(*oItr); - - if (pBAS) - { - const BunkerAssembly * pBAPreset = 0; - - // Try to select previousy selected assembly - if (pBAS->IsOneTypePerScene()) - { - auto itr = m_SelectedAssemblies.find(pBAS->GetModuleAndPresetName()); - if (itr != m_SelectedAssemblies.end()) - pBAPreset = (*itr).second; - } + // Has to be a TerrainObject then, so apply to terrain + else { + // Place random BunkerAssembly instead of BunkerAssemblyScheme if this is a BunkerAssemblyScheme + TerrainObject* pTO = 0; + BunkerAssemblyScheme* pBAS = dynamic_cast(*oItr); + + if (pBAS) { + const BunkerAssembly* pBAPreset = 0; + + // Try to select previousy selected assembly + if (pBAS->IsOneTypePerScene()) { + auto itr = m_SelectedAssemblies.find(pBAS->GetModuleAndPresetName()); + if (itr != m_SelectedAssemblies.end()) + pBAPreset = (*itr).second; + } + + // Find random bunker assembly and clone it + if (!pBAPreset) { + pBAPreset = dynamic_cast(g_PresetMan.GetRandomOfGroup(pBAS->GetPresetName(), "BunkerAssembly", -1)); + + if (pBAPreset) { + // Remember selected BunkerAssembly for this scheme to use it everywhere on the map. + if (pBAS->IsOneTypePerScene()) { + m_SelectedAssemblies.insert(std::pair(pBAS->GetModuleAndPresetName(), pBAPreset)); - //Find random bunker assembly and clone it - if (!pBAPreset) - { - pBAPreset = dynamic_cast(g_PresetMan.GetRandomOfGroup(pBAS->GetPresetName(), "BunkerAssembly", -1)); - - if (pBAPreset) - { - // Remember selected BunkerAssembly for this scheme to use it everywhere on the map. - if (pBAS->IsOneTypePerScene()) - { - m_SelectedAssemblies.insert(std::pair(pBAS->GetModuleAndPresetName(), pBAPreset)); - - // Find out if this scheme has symmetric scheme, and this assembly have symmetric version - // so we could set them to be used when we need to create symmetric versions of this scheme - std::string symmetricScheme = pBAS->GetSymmetricSchemeName(); - std::string symmetricAssembly = pBAPreset->GetSymmetricAssemblyName(); - - if (symmetricScheme.size() > 0 && symmetricAssembly.size() > 0) - { - const BunkerAssembly * pBAPresetSymmetric = dynamic_cast(g_PresetMan.GetEntityPreset("BunkerAssembly", symmetricAssembly)); - - if (pBAPresetSymmetric) - m_SelectedAssemblies.insert(std::pair(symmetricScheme, pBAPresetSymmetric)); + // Find out if this scheme has symmetric scheme, and this assembly have symmetric version + // so we could set them to be used when we need to create symmetric versions of this scheme + std::string symmetricScheme = pBAS->GetSymmetricSchemeName(); + std::string symmetricAssembly = pBAPreset->GetSymmetricAssemblyName(); + + if (symmetricScheme.size() > 0 && symmetricAssembly.size() > 0) { + const BunkerAssembly* pBAPresetSymmetric = dynamic_cast(g_PresetMan.GetEntityPreset("BunkerAssembly", symmetricAssembly)); + + if (pBAPresetSymmetric) + m_SelectedAssemblies.insert(std::pair(symmetricScheme, pBAPresetSymmetric)); + } } } } + // Finally make a copy of found or selected assembly to put it on scene later + if (pBAPreset) { + pTO = dynamic_cast(pBAPreset->Clone()); + pTO->SetPos(pBAS->GetPos()); + pTO->SetTeam(pBAS->GetTeam()); + } } - // Finally make a copy of found or selected assembly to put it on scene later - if (pBAPreset) - { - pTO = dynamic_cast(pBAPreset->Clone()); - pTO->SetPos(pBAS->GetPos()); - pTO->SetTeam(pBAS->GetTeam()); + + // Try to read as terrain object if it's not Assembly Scheme + // NOT passing ownership here, so have to delete it ourselves + if (!pTO) + pTO = dynamic_cast(*oItr); + + // Add deployments placed by bunker assemblies, but not in metagame, + // as they are spawned and placed during ApplyBuildBudget + if (placeUnits && !g_MetaMan.GameInProgress()) { + BunkerAssembly* pBA = dynamic_cast(pTO); + if (pBA) { + std::vector deployments = pBA->GetDeployments(); + for (std::vector::iterator itr = deployments.begin(); itr != deployments.end(); ++itr) { + // Create a copy of deployment because assemblies own their placed objects and scenes own theirs + SceneObject* so = (SceneObject*)(*itr)->Clone(); + so->SetPos(so->GetPos() + pBA->GetPos() + pBA->GetBitmapOffset()); + so->SetTeam(pBA->GetTeam()); + so->SetPlacedByPlayer(pBA->GetPlacedByPlayer()); + m_PlacedObjects[PLACEONLOAD].push_back(so); + } + } } - } - // Try to read as terrain object if it's not Assembly Scheme - // NOT passing ownership here, so have to delete it ourselves - if (!pTO) - pTO = dynamic_cast(*oItr); - - // Add deployments placed by bunker assemblies, but not in metagame, - // as they are spawned and placed during ApplyBuildBudget - if (placeUnits && !g_MetaMan.GameInProgress()) - { - BunkerAssembly * pBA = dynamic_cast(pTO); - if (pBA) - { - std::vectordeployments = pBA->GetDeployments(); - for (std::vector::iterator itr = deployments.begin(); itr != deployments.end() ; ++itr) - { - // Create a copy of deployment because assemblies own their placed objects and scenes own theirs - SceneObject * so = (SceneObject *)(*itr)->Clone(); - so->SetPos(so->GetPos() + pBA->GetPos() + pBA->GetBitmapOffset()); - so->SetTeam(pBA->GetTeam()); - so->SetPlacedByPlayer(pBA->GetPlacedByPlayer()); - m_PlacedObjects[PLACEONLOAD].push_back(so); + // Finally place TerrainObject + if (pTO) { + const BITMAP* terrainObjectBitmap = pTO->GetFGColorBitmap() ? pTO->GetFGColorBitmap() : pTO->GetBGColorBitmap(); + + // First clear out the box of unseen layer for whichever team placed this + if (terrainObjectBitmap && pTO->GetPlacedByPlayer() != Players::NoPlayer && g_ActivityMan.GetActivity()) { + // Learn which team placed this thing so we can reveal for them only + int ownerTeam = pTO->GetTeam(); + if (ownerTeam != Activity::NoTeam && m_apUnseenLayer[ownerTeam] && m_apUnseenLayer[ownerTeam]->GetBitmap()) { + // Translate to the scaled unseen layer's coordinates + Vector scale = m_apUnseenLayer[ownerTeam]->GetScaleFactor(); + int scaledX = std::floor((pTO->GetPos().m_X - (float)(terrainObjectBitmap->w / 2)) / scale.m_X); + int scaledY = std::floor((pTO->GetPos().m_Y - (float)(terrainObjectBitmap->h / 2)) / scale.m_Y); + int scaledW = std::ceil(terrainObjectBitmap->w / scale.m_X); + int scaledH = std::ceil(terrainObjectBitmap->h / scale.m_Y); + // Fill the box with key color for the owner ownerTeam, revealing the area that this thing is on + rectfill(m_apUnseenLayer[ownerTeam]->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_MaskColor); + // Expand the box a little so the whole placed object is going to be hidden + scaledX -= 1; + scaledY -= 1; + scaledW += 2; + scaledH += 2; + // Fill the box with BLACK for all the other teams so they can't see the new developments here! + for (int t = Activity::TeamOne; t < Activity::MaxTeamCount; ++t) { + if (t != ownerTeam && m_apUnseenLayer[t] && m_apUnseenLayer[t]->GetBitmap()) + rectfill(m_apUnseenLayer[t]->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_BlackColor); + } + } } + // Now actually stamp the terrain object onto the terrain's scene layers + pTO->PlaceOnTerrain(m_pTerrain); + // delete pTO; + // pTO = 0; } - } - // Finally place TerrainObject - if (pTO) - { - const BITMAP *terrainObjectBitmap = pTO->GetFGColorBitmap() ? pTO->GetFGColorBitmap() : pTO->GetBGColorBitmap(); - - // First clear out the box of unseen layer for whichever team placed this - if (terrainObjectBitmap && pTO->GetPlacedByPlayer() != Players::NoPlayer && g_ActivityMan.GetActivity()) - { - // Learn which team placed this thing so we can reveal for them only - int ownerTeam = pTO->GetTeam(); - if (ownerTeam != Activity::NoTeam && m_apUnseenLayer[ownerTeam] && m_apUnseenLayer[ownerTeam]->GetBitmap()) - { - // Translate to the scaled unseen layer's coordinates - Vector scale = m_apUnseenLayer[ownerTeam]->GetScaleFactor(); - int scaledX = std::floor((pTO->GetPos().m_X - (float)(terrainObjectBitmap->w / 2)) / scale.m_X); - int scaledY = std::floor((pTO->GetPos().m_Y - (float)(terrainObjectBitmap->h / 2)) / scale.m_Y); - int scaledW = std::ceil(terrainObjectBitmap->w / scale.m_X); - int scaledH = std::ceil(terrainObjectBitmap->h / scale.m_Y); - // Fill the box with key color for the owner ownerTeam, revealing the area that this thing is on - rectfill(m_apUnseenLayer[ownerTeam]->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_MaskColor); - // Expand the box a little so the whole placed object is going to be hidden - scaledX -= 1; - scaledY -= 1; - scaledW += 2; - scaledH += 2; - // Fill the box with BLACK for all the other teams so they can't see the new developments here! - for (int t = Activity::TeamOne; t < Activity::MaxTeamCount; ++t) - { - if (t != ownerTeam && m_apUnseenLayer[t] && m_apUnseenLayer[t]->GetBitmap()) - rectfill(m_apUnseenLayer[t]->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_BlackColor); - } - } - } - // Now actually stamp the terrain object onto the terrain's scene layers - pTO->PlaceOnTerrain(m_pTerrain); - //delete pTO; - //pTO = 0; - } - - // Finally delete the object retreived from the list - delete (*oItr); - (*oItr) = 0; - } - } - } - // Finally select random brain location from the list of found brain deployments - for (int t = Activity::TeamOne; t < Activity::MaxTeamCount; t++) - { - if (pBrains[t]) - { - int team = pBrains[t]->GetTeam(); - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) - team = 0; - if (brainLocations[team].size() > 0) - { - int selection = RandomNum(0, brainLocations[team].size() - 1); - pBrains[t]->SetPos(brainLocations[team].at(selection)); + // Finally delete the object retreived from the list + delete (*oItr); + (*oItr) = 0; + } + } + } + // Finally select random brain location from the list of found brain deployments + for (int t = Activity::TeamOne; t < Activity::MaxTeamCount; t++) { + if (pBrains[t]) { + int team = pBrains[t]->GetTeam(); + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) + team = 0; + if (brainLocations[team].size() > 0) { + int selection = RandomNum(0, brainLocations[team].size() - 1); + pBrains[t]->SetPos(brainLocations[team].at(selection)); + } } } - } - - // Clear out the list of things that were placed above.. the ownership of everything WAS transferred anyway - m_PlacedObjects[PLACEONLOAD].clear(); - //m_PlacedObjects[AIPLAN].clear(); // I'm using this to dump AI plans via CTRL+W - -// TODO: CLEAN AIR IN AN AFTER EACH OBJECT PLACED, becuase the items refuse to be placed in a hollowness otherwise? - // Clear the air out of objects placed - m_pTerrain->CleanAir(); - } - - ///////////////////////////////// - // Go through and init the teams on all remaining placed objects, so their flag icons etc are correct for the current Activity - for (int set = PLACEONLOAD; set < PLACEDSETSCOUNT; ++set) - { - for (std::list::iterator oItr = m_PlacedObjects[set].begin(); oItr != m_PlacedObjects[set].end(); ++oItr) - (*oItr)->SetTeam((*oItr)->GetTeam()); - } - /////////////////////////////// - // Pathfinding init - if (initPathfinding) - { - // Create the pathfinding stuff based on the current scene - int pathFinderGridNodeSize = g_SettingsMan.GetPathFinderGridNodeSize(); + // Clear out the list of things that were placed above.. the ownership of everything WAS transferred anyway + m_PlacedObjects[PLACEONLOAD].clear(); + // m_PlacedObjects[AIPLAN].clear(); // I'm using this to dump AI plans via CTRL+W - for (int i = 0; i < m_pPathFinders.size(); ++i) { - m_pPathFinders[i] = std::make_unique(pathFinderGridNodeSize); - } - ResetPathFinding(); - } + // TODO: CLEAN AIR IN AN AFTER EACH OBJECT PLACED, becuase the items refuse to be placed in a hollowness otherwise? + // Clear the air out of objects placed + m_pTerrain->CleanAir(); + } - return 0; -} + ///////////////////////////////// + // Go through and init the teams on all remaining placed objects, so their flag icons etc are correct for the current Activity + for (int set = PLACEONLOAD; set < PLACEDSETSCOUNT; ++set) { + for (std::list::iterator oItr = m_PlacedObjects[set].begin(); oItr != m_PlacedObjects[set].end(); ++oItr) + (*oItr)->SetTeam((*oItr)->GetTeam()); + } + /////////////////////////////// + // Pathfinding init + if (initPathfinding) { + // Create the pathfinding stuff based on the current scene + int pathFinderGridNodeSize = g_SettingsMan.GetPathFinderGridNodeSize(); + for (int i = 0; i < m_pPathFinders.size(); ++i) { + m_pPathFinders[i] = std::make_unique(pathFinderGridNodeSize); + } + ResetPathFinding(); + } + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ExpandAIPlanAssemblySchemes -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ExpandAIPlanAssemblySchemes + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: + // -int Scene::ExpandAIPlanAssemblySchemes() -{ - std::list newAIPlan; + int Scene::ExpandAIPlanAssemblySchemes() { + std::list newAIPlan; - for (std::list::iterator oItr = m_PlacedObjects[AIPLAN].begin(); oItr != m_PlacedObjects[AIPLAN].end(); ++oItr) - { - BunkerAssemblyScheme *pBAS = dynamic_cast(*oItr); + for (std::list::iterator oItr = m_PlacedObjects[AIPLAN].begin(); oItr != m_PlacedObjects[AIPLAN].end(); ++oItr) { + BunkerAssemblyScheme* pBAS = dynamic_cast(*oItr); - if (pBAS) - { - const BunkerAssembly * pBAPreset = 0; - //BunkerAssembly * pPA = 0; + if (pBAS) { + const BunkerAssembly* pBAPreset = 0; + // BunkerAssembly * pPA = 0; - // Try to select previousy selected assembly - if (pBAS->IsOneTypePerScene()) - { - auto itr = m_SelectedAssemblies.find(pBAS->GetModuleAndPresetName()); - if (itr != m_SelectedAssemblies.end()) - pBAPreset = (*itr).second; - } + // Try to select previousy selected assembly + if (pBAS->IsOneTypePerScene()) { + auto itr = m_SelectedAssemblies.find(pBAS->GetModuleAndPresetName()); + if (itr != m_SelectedAssemblies.end()) + pBAPreset = (*itr).second; + } - //Find random bunker assembly and clone it - if (!pBAPreset) - { - pBAPreset = dynamic_cast(g_PresetMan.GetRandomOfGroup(pBAS->GetPresetName(), "BunkerAssembly", -1)); + // Find random bunker assembly and clone it + if (!pBAPreset) { + pBAPreset = dynamic_cast(g_PresetMan.GetRandomOfGroup(pBAS->GetPresetName(), "BunkerAssembly", -1)); - // Remember selected BunkerAssembly for this scheme to use it everywhere on the map. - if (pBAS->IsOneTypePerScene()) - m_SelectedAssemblies.insert(std::pair(pBAS->GetModuleAndPresetName(), pBAPreset)); - } + // Remember selected BunkerAssembly for this scheme to use it everywhere on the map. + if (pBAS->IsOneTypePerScene()) + m_SelectedAssemblies.insert(std::pair(pBAS->GetModuleAndPresetName(), pBAPreset)); + } - // Add found assembly to the AI plan - if (pBAPreset) - { - BunkerAssembly * pBA = dynamic_cast(pBAPreset->Clone()); - pBA->SetPos(pBAS->GetPos()); - pBA->SetTeam(pBAS->GetTeam()); - pBA->SetPlacedByPlayer(pBAS->GetPlacedByPlayer()); - - newAIPlan.push_back(pBA); - - std::vectorpDeployments = pBA->GetDeployments(); - for (std::vector::iterator itr = pDeployments.begin(); itr != pDeployments.end() ; ++itr) - { - // Create a copy of deployment because assemblies own their placed objects and scenes own theirs - SceneObject * so = (SceneObject *)(*itr)->Clone(); - so->SetPos(so->GetPos() + pBA->GetPos() + pBA->GetBitmapOffset()); - so->SetTeam(pBA->GetTeam()); - so->SetPlacedByPlayer(pBA->GetPlacedByPlayer()); - newAIPlan.push_back(so); + // Add found assembly to the AI plan + if (pBAPreset) { + BunkerAssembly* pBA = dynamic_cast(pBAPreset->Clone()); + pBA->SetPos(pBAS->GetPos()); + pBA->SetTeam(pBAS->GetTeam()); + pBA->SetPlacedByPlayer(pBAS->GetPlacedByPlayer()); + + newAIPlan.push_back(pBA); + + std::vector pDeployments = pBA->GetDeployments(); + for (std::vector::iterator itr = pDeployments.begin(); itr != pDeployments.end(); ++itr) { + // Create a copy of deployment because assemblies own their placed objects and scenes own theirs + SceneObject* so = (SceneObject*)(*itr)->Clone(); + so->SetPos(so->GetPos() + pBA->GetPos() + pBA->GetBitmapOffset()); + so->SetTeam(pBA->GetTeam()); + so->SetPlacedByPlayer(pBA->GetPlacedByPlayer()); + newAIPlan.push_back(so); + } } + // Delete scheme as it was replaced by an assembly by now + delete (*oItr); + (*oItr) = 0; + } else { + // If it's not a scheme, then just put it in the new list + newAIPlan.push_back(*oItr); } - //Delete scheme as it was replaced by an assembly by now - delete (*oItr); - (*oItr) = 0; - } else { - // If it's not a scheme, then just put it in the new list - newAIPlan.push_back(*oItr); } - } - - // Copy new AI plan list to replace the original - m_PlacedObjects[AIPLAN].clear(); - for (std::list::iterator oItr = newAIPlan.begin(); oItr != newAIPlan.end(); ++oItr) - m_PlacedObjects[AIPLAN].push_back(*oItr); - - return 0; -} + // Copy new AI plan list to replace the original + m_PlacedObjects[AIPLAN].clear(); + for (std::list::iterator oItr = newAIPlan.begin(); oItr != newAIPlan.end(); ++oItr) + m_PlacedObjects[AIPLAN].push_back(*oItr); + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SaveData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves currently loaded bitmap data in memory to disk. + int Scene::SaveData(std::string pathBase, bool doAsyncSaves) { + const std::string fullPathBase = g_PresetMan.GetFullModulePath(pathBase); + if (fullPathBase.empty()) + return -1; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SaveData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves currently loaded bitmap data in memory to disk. - -int Scene::SaveData(std::string pathBase, bool doAsyncSaves) -{ - const std::string fullPathBase = g_PresetMan.GetFullModulePath(pathBase); - if (fullPathBase.empty()) - return -1; - - if (!m_pTerrain) - return 0; - - // Save Terrain's data - if (m_pTerrain->SaveData(fullPathBase, doAsyncSaves) < 0) - { - RTEAbort("Saving Terrain " + m_pTerrain->GetPresetName() + "\'s data failed!"); - return -1; - } - - // Don't bother saving background layers to disk, as they are never altered - - // Save unseen layers' data - char str[64]; - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - if (m_apUnseenLayer[team]) - { - std::snprintf(str, sizeof(str), "T%d", team); - // Save unseen layer data to disk - if (m_apUnseenLayer[team]->SaveData(fullPathBase + " US" + str + ".png", doAsyncSaves) < 0) - { - g_ConsoleMan.PrintString("ERROR: Saving unseen layer " + m_apUnseenLayer[team]->GetPresetName() + "\'s data failed!"); - return -1; - } - } - } - - return 0; -} - -std::vector Scene::GetCopiedSceneLayerBitmaps() { - std::vector layerInfos; - - /* - // Save Terrain's data - m_pTerrain - - // Don't bother saving background layers to disk, as they are never altered - - // Save unseen layers' data - char str[64]; - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - if (m_apUnseenLayer[team]) - { - m_apUnseenLayer[team] - } - } - */ - - return layerInfos; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SavePreview -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves preview bitmap for this scene. -// - -int Scene::SavePreview(const std::string &bitmapPath) { - // Do not save preview for MetaScenes! - if (!m_MetasceneParent.empty()) { - return -1; - } + if (!m_pTerrain) + return 0; - if (m_pPreviewBitmap && (m_pPreviewBitmap->w != c_ScenePreviewWidth || m_pPreviewBitmap->h != c_ScenePreviewHeight)) { - destroy_bitmap(m_pPreviewBitmap); - m_pPreviewBitmap = nullptr; - } - if (!m_pPreviewBitmap) { m_pPreviewBitmap = create_bitmap_ex(8, c_ScenePreviewWidth, c_ScenePreviewHeight); } - - ContentFile scenePreviewGradient("Base.rte/GUIs/PreviewSkyGradient.png"); - draw_sprite(m_pPreviewBitmap, scenePreviewGradient.GetAsBitmap(COLORCONV_NONE, true), 0, 0); - - int sceneWidth = m_pTerrain->GetBitmap()->w; - int sceneHeight = m_pTerrain->GetBitmap()->h; - BITMAP *previewBuffer = create_bitmap_ex(8, sceneWidth, sceneHeight); - clear_to_color(previewBuffer, ColorKeys::g_MaskColor); - - masked_blit(m_pTerrain->GetBGColorBitmap(), previewBuffer, 0, 0, 0, 0, sceneWidth, sceneHeight); - masked_blit(m_pTerrain->GetFGColorBitmap(), previewBuffer, 0, 0, 0, 0, sceneWidth, sceneHeight); - - for (SceneObject *sceneObject : m_PlacedObjects[PlacedObjectSets::PLACEONLOAD]) { - if (const TerrainObject *terrainObject = dynamic_cast(sceneObject)) { - Vector pos = terrainObject->GetPos() + terrainObject->GetBitmapOffset(); - BITMAP *terrainObjectBG = terrainObject->GetBGColorBitmap(); - BITMAP *terrainObjectFG = terrainObject->GetFGColorBitmap(); - - // Wrapped drawing - if (pos.GetFloorIntX() < 0) { - if (terrainObject->HasBGColorBitmap()) { masked_blit(terrainObjectBG, previewBuffer, 0, 0, pos.GetFloorIntX() + sceneWidth, pos.GetFloorIntY(), terrainObjectBG->w, terrainObjectBG->h); } - if (terrainObject->HasFGColorBitmap()) { masked_blit(terrainObjectFG, previewBuffer, 0, 0, pos.GetFloorIntX() + sceneWidth, pos.GetFloorIntY(), terrainObjectFG->w, terrainObjectFG->h); } - } else if (pos.GetFloorIntX() + terrainObject->GetBitmapWidth() >= sceneWidth) { - if (terrainObject->HasBGColorBitmap()) { masked_blit(terrainObjectBG, previewBuffer, 0, 0, pos.GetFloorIntX() - sceneWidth, pos.GetFloorIntY(), terrainObjectBG->w, terrainObjectBG->h); } - if (terrainObject->HasFGColorBitmap()) { masked_blit(terrainObjectFG, previewBuffer, 0, 0, pos.GetFloorIntX() - sceneWidth, pos.GetFloorIntY(), terrainObjectFG->w, terrainObjectFG->h); } - } - if (terrainObject->HasBGColorBitmap()) { masked_blit(terrainObjectBG, previewBuffer, 0, 0, pos.GetFloorIntX(), pos.GetFloorIntY(), terrainObjectBG->w, terrainObjectBG->h); } - if (terrainObject->HasFGColorBitmap()) { masked_blit(terrainObjectFG, previewBuffer, 0, 0, pos.GetFloorIntX(), pos.GetFloorIntY(), terrainObjectFG->w, terrainObjectFG->h); } + // Save Terrain's data + if (m_pTerrain->SaveData(fullPathBase, doAsyncSaves) < 0) { + RTEAbort("Saving Terrain " + m_pTerrain->GetPresetName() + "\'s data failed!"); + return -1; } - // TODO: Figure out how to draw the actual modules that compose an assembly that is part of the scheme. For now just disable. - /* - else if (const BunkerAssemblyScheme *assemblyScheme = dynamic_cast(sceneObject)) { - Vector pos = assemblyScheme->GetPos() + assemblyScheme->GetBitmapOffset(); - - // Wrapped drawing - if (pos.GetFloorIntX() < 0) { - rect(previewBuffer, pos.GetFloorIntX() + sceneWidth, pos.GetFloorIntY(), pos.GetFloorIntX() + sceneWidth + assemblyScheme->GetBitmapWidth(), pos.GetFloorIntY() + assemblyScheme->GetBitmapHeight(), 5); - } else if (pos.GetFloorIntX() + assemblyScheme->GetBitmapWidth() >= sceneWidth) { - rect(previewBuffer, pos.GetFloorIntX() - sceneWidth, pos.GetFloorIntY(), pos.GetFloorIntX() - sceneWidth + assemblyScheme->GetBitmapWidth(), pos.GetFloorIntY() + assemblyScheme->GetBitmapHeight(), 5); + // Don't bother saving background layers to disk, as they are never altered + + // Save unseen layers' data + char str[64]; + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + if (m_apUnseenLayer[team]) { + std::snprintf(str, sizeof(str), "T%d", team); + // Save unseen layer data to disk + if (m_apUnseenLayer[team]->SaveData(fullPathBase + " US" + str + ".png", doAsyncSaves) < 0) { + g_ConsoleMan.PrintString("ERROR: Saving unseen layer " + m_apUnseenLayer[team]->GetPresetName() + "\'s data failed!"); + return -1; + } } - rect(previewBuffer, pos.GetFloorIntX(), pos.GetFloorIntY(), pos.GetFloorIntX() + ((float)assemblyScheme->GetBitmapWidth()), pos.GetFloorIntY() + ((float)assemblyScheme->GetBitmapHeight()), 5); } - */ + + return 0; } - masked_stretch_blit(previewBuffer, m_pPreviewBitmap, 0, 0, previewBuffer->w, previewBuffer->h, 0, 0, m_pPreviewBitmap->w, m_pPreviewBitmap->h); - destroy_bitmap(previewBuffer); + std::vector Scene::GetCopiedSceneLayerBitmaps() { + std::vector layerInfos; - if (g_FrameMan.SaveBitmapToPNG(m_pPreviewBitmap, bitmapPath.c_str()) == 0) { m_PreviewBitmapFile.SetDataPath(bitmapPath); } + /* + // Save Terrain's data + m_pTerrain - return 0; -} + // Don't bother saving background layers to disk, as they are never altered -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out any previously loaded bitmap data from memory. - -int Scene::ClearData() -{ - if (!m_pTerrain) - return 0; - - // Clear Terrain's data - if (m_pTerrain->ClearData() < 0) - { - RTEAbort("Clearing Terrain " + m_pTerrain->GetPresetName() + "\'s data failed!"); - return -1; - } - - // Clear unseen layers' data - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - if (m_apUnseenLayer[team]) - { - // Clear unseen layer data from memory - if (m_apUnseenLayer[team]->ClearData() < 0) - { - g_ConsoleMan.PrintString("ERROR: Clearing unseen layer " + m_apUnseenLayer[team]->GetPresetName() + "\'s data failed!"); - return -1; - } - } - } - - return 0; -} + // Save unseen layers' data + char str[64]; + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) + { + if (m_apUnseenLayer[team]) + { + m_apUnseenLayer[team] + } + } + */ + return layerInfos; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int Scene::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Entity::ReadProperty(propName, reader)); - - MatchProperty("LocationOnPlanet", { reader >> m_Location; }); - MatchProperty("MetagamePlayable", { reader >> m_MetagamePlayable; }); - MatchProperty("Revealed", { reader >> m_Revealed; }); - MatchProperty("MetasceneParent", { reader >> m_MetasceneParent; }); - MatchProperty("MetagameInternal", { reader >> m_IsMetagameInternal; }); - MatchProperty("ScriptSave", { reader >> m_IsSavedGameInternal; }); - MatchProperty("OwnedByTeam", { reader >> m_OwnedByTeam; }); - MatchProperty("RoundIncome", { reader >> m_RoundIncome; }); - MatchProperty("P1ResidentBrain", { m_ResidentBrains[Players::PlayerOne] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); }); - MatchProperty("P2ResidentBrain", { m_ResidentBrains[Players::PlayerTwo] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); }); - MatchProperty("P3ResidentBrain", { m_ResidentBrains[Players::PlayerThree] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); }); - MatchProperty("P4ResidentBrain", { m_ResidentBrains[Players::PlayerFour] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); }); - MatchProperty("P1BuildBudget", { reader >> m_BuildBudget[Players::PlayerOne]; }); - MatchProperty("P2BuildBudget", { reader >> m_BuildBudget[Players::PlayerTwo]; }); - MatchProperty("P3BuildBudget", { reader >> m_BuildBudget[Players::PlayerThree]; }); - MatchProperty("P4BuildBudget", { reader >> m_BuildBudget[Players::PlayerFour]; }); - MatchProperty("P1BuildBudgetRatio", { reader >> m_BuildBudgetRatio[Players::PlayerOne]; }); - MatchProperty("P2BuildBudgetRatio", { reader >> m_BuildBudgetRatio[Players::PlayerTwo]; }); - MatchProperty("P3BuildBudgetRatio", { reader >> m_BuildBudgetRatio[Players::PlayerThree]; }); - MatchProperty("P4BuildBudgetRatio", { reader >> m_BuildBudgetRatio[Players::PlayerFour]; }); - MatchProperty("AutoDesigned", { reader >> m_AutoDesigned; }); - MatchProperty("TotalInvestment", { reader >> m_TotalInvestment; }); - MatchProperty("PreviewBitmapFile", - reader >> m_PreviewBitmapFile; - m_pPreviewBitmap = m_PreviewBitmapFile.GetAsBitmap(COLORCONV_NONE, false); ); - MatchProperty("Terrain", - delete m_pTerrain; - m_pTerrain = new SLTerrain(); - reader >> m_pTerrain; ); - MatchForwards("PlaceSceneObject") - MatchProperty("PlaceMovableObject", - SceneObject *pSO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); - if (pSO) { - m_PlacedObjects[PLACEONLOAD].push_back(pSO); - } ); - MatchProperty("BlueprintObject", - SceneObject *pSO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); - if (pSO) { - m_PlacedObjects[BLUEPRINT].push_back(pSO); - } ); - MatchProperty("PlaceAIPlanObject", - SceneObject *pSO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); - if (pSO) { - m_PlacedObjects[AIPLAN].push_back(pSO); - } ); - MatchProperty("AddBackgroundLayer", - SLBackground *pLayer = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); - RTEAssert(pLayer, "Something went wrong with reading SceneLayer"); - if (pLayer) { - m_BackLayerList.push_back(pLayer); - } ); - MatchProperty("AllUnseenPixelSizeTeam1", - // Read the desired pixel dimensions of the dynamically generated unseen map - reader >> m_UnseenPixelSize[Activity::TeamOne]; ); - MatchProperty("AllUnseenPixelSizeTeam2", - // Read the desired pixel dimensions of the dynamically generated unseen map - reader >> m_UnseenPixelSize[Activity::TeamTwo]; ); - MatchProperty("AllUnseenPixelSizeTeam3", - // Read the desired pixel dimensions of the dynamically generated unseen map - reader >> m_UnseenPixelSize[Activity::TeamThree]; ); - MatchProperty("AllUnseenPixelSizeTeam4", - // Read the desired pixel dimensions of the dynamically generated unseen map - reader >> m_UnseenPixelSize[Activity::TeamFour]; ); - MatchProperty("UnseenLayerTeam1", - delete m_apUnseenLayer[Activity::TeamOne]; - m_apUnseenLayer[Activity::TeamOne] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); ); - MatchProperty("UnseenLayerTeam2", - delete m_apUnseenLayer[Activity::TeamTwo]; - m_apUnseenLayer[Activity::TeamTwo] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); ); - MatchProperty("UnseenLayerTeam3", - delete m_apUnseenLayer[Activity::TeamThree]; - m_apUnseenLayer[Activity::TeamThree] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); ); - MatchProperty("UnseenLayerTeam4", - delete m_apUnseenLayer[Activity::TeamFour]; - m_apUnseenLayer[Activity::TeamFour] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); ); - MatchProperty("ScanScheduledTeam1", { reader >> m_ScanScheduled[Activity::TeamOne]; }); - MatchProperty("ScanScheduledTeam2", { reader >> m_ScanScheduled[Activity::TeamTwo]; }); - MatchProperty("ScanScheduledTeam3", { reader >> m_ScanScheduled[Activity::TeamThree]; }); - MatchProperty("ScanScheduledTeam4", { reader >> m_ScanScheduled[Activity::TeamFour]; }); - MatchProperty("AddArea", - Area area; - reader >> area; - // This replaces any existing ones - SetArea(area); ); - MatchProperty("GlobalAcceleration", { reader >> m_GlobalAcc; }); - - EndPropertyList; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SavePreview + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves preview bitmap for this scene. + // -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Scene with a Writer for -// later recreation with Create(Reader &reader); + int Scene::SavePreview(const std::string& bitmapPath) { + // Do not save preview for MetaScenes! + if (!m_MetasceneParent.empty()) { + return -1; + } -int Scene::Save(Writer &writer) const { - Entity::Save(writer); + if (m_pPreviewBitmap && (m_pPreviewBitmap->w != c_ScenePreviewWidth || m_pPreviewBitmap->h != c_ScenePreviewHeight)) { + destroy_bitmap(m_pPreviewBitmap); + m_pPreviewBitmap = nullptr; + } + if (!m_pPreviewBitmap) { + m_pPreviewBitmap = create_bitmap_ex(8, c_ScenePreviewWidth, c_ScenePreviewHeight); + } - bool doFullGameSave = !dynamic_cast(g_ActivityMan.GetActivity()); + ContentFile scenePreviewGradient("Base.rte/GUIs/PreviewSkyGradient.png"); + draw_sprite(m_pPreviewBitmap, scenePreviewGradient.GetAsBitmap(COLORCONV_NONE, true), 0, 0); - writer.NewPropertyWithValue("LocationOnPlanet", m_Location); - writer.NewPropertyWithValue("MetagamePlayable", m_MetagamePlayable); - //Do not save preview if it's path is empty, for example in metagame - if (m_PreviewBitmapFile.GetDataPath().length() > 0 && m_MetasceneParent.length() == 0) { - writer.NewPropertyWithValue("PreviewBitmapFile", m_PreviewBitmapFile); - } - if (m_MetasceneParent.length() > 0) { - writer.NewPropertyWithValue("MetasceneParent", m_MetasceneParent); - } - writer.NewPropertyWithValue("MetagameInternal", m_IsMetagameInternal); - writer.NewPropertyWithValue("ScriptSave", m_IsSavedGameInternal); - writer.NewPropertyWithValue("Revealed", m_Revealed); - writer.NewPropertyWithValue("OwnedByTeam", m_OwnedByTeam); - writer.NewPropertyWithValue("RoundIncome", m_RoundIncome); + int sceneWidth = m_pTerrain->GetBitmap()->w; + int sceneHeight = m_pTerrain->GetBitmap()->h; + BITMAP* previewBuffer = create_bitmap_ex(8, sceneWidth, sceneHeight); + clear_to_color(previewBuffer, ColorKeys::g_MaskColor); - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - std::string playerNumberString = "P" + std::to_string(player + 1); - writer.NewPropertyWithValue(playerNumberString + "BuildBudget", m_BuildBudget[player]); - writer.NewPropertyWithValue(playerNumberString + "BuildBudgetRatio", m_BuildBudgetRatio[player]); - if (m_ResidentBrains[player]) { - writer.NewProperty(playerNumberString + "ResidentBrain"); - SaveSceneObject(writer, m_ResidentBrains[player], false, doFullGameSave); - } - } + masked_blit(m_pTerrain->GetBGColorBitmap(), previewBuffer, 0, 0, 0, 0, sceneWidth, sceneHeight); + masked_blit(m_pTerrain->GetFGColorBitmap(), previewBuffer, 0, 0, 0, 0, sceneWidth, sceneHeight); - writer.NewPropertyWithValue("AutoDesigned", m_AutoDesigned); - writer.NewPropertyWithValue("TotalInvestment", m_TotalInvestment); - writer.NewPropertyWithValue("Terrain", m_pTerrain); + for (SceneObject* sceneObject: m_PlacedObjects[PlacedObjectSets::PLACEONLOAD]) { + if (const TerrainObject* terrainObject = dynamic_cast(sceneObject)) { + Vector pos = terrainObject->GetPos() + terrainObject->GetBitmapOffset(); + BITMAP* terrainObjectBG = terrainObject->GetBGColorBitmap(); + BITMAP* terrainObjectFG = terrainObject->GetFGColorBitmap(); - for (int set = PlacedObjectSets::PLACEONLOAD; set < PlacedObjectSets::PLACEDSETSCOUNT; ++set) { - for (const SceneObject *placedObject : m_PlacedObjects[set]) { - if (placedObject->GetPresetName().empty() || placedObject->GetPresetName() == "None") { - // We have no info about what we're placing. This is probably because it's some particle that was kicked off the terrain - // In future, we'll save all the data (uncomment out //writer << placedObject;), and will be able to save/load that stuff - // But for now, until we have a more effective writer that can remove redundant properties, we just skip this - continue; + // Wrapped drawing + if (pos.GetFloorIntX() < 0) { + if (terrainObject->HasBGColorBitmap()) { + masked_blit(terrainObjectBG, previewBuffer, 0, 0, pos.GetFloorIntX() + sceneWidth, pos.GetFloorIntY(), terrainObjectBG->w, terrainObjectBG->h); + } + if (terrainObject->HasFGColorBitmap()) { + masked_blit(terrainObjectFG, previewBuffer, 0, 0, pos.GetFloorIntX() + sceneWidth, pos.GetFloorIntY(), terrainObjectFG->w, terrainObjectFG->h); + } + } else if (pos.GetFloorIntX() + terrainObject->GetBitmapWidth() >= sceneWidth) { + if (terrainObject->HasBGColorBitmap()) { + masked_blit(terrainObjectBG, previewBuffer, 0, 0, pos.GetFloorIntX() - sceneWidth, pos.GetFloorIntY(), terrainObjectBG->w, terrainObjectBG->h); + } + if (terrainObject->HasFGColorBitmap()) { + masked_blit(terrainObjectFG, previewBuffer, 0, 0, pos.GetFloorIntX() - sceneWidth, pos.GetFloorIntY(), terrainObjectFG->w, terrainObjectFG->h); + } + } + if (terrainObject->HasBGColorBitmap()) { + masked_blit(terrainObjectBG, previewBuffer, 0, 0, pos.GetFloorIntX(), pos.GetFloorIntY(), terrainObjectBG->w, terrainObjectBG->h); + } + if (terrainObject->HasFGColorBitmap()) { + masked_blit(terrainObjectFG, previewBuffer, 0, 0, pos.GetFloorIntX(), pos.GetFloorIntY(), terrainObjectFG->w, terrainObjectFG->h); + } } - if (set == PlacedObjectSets::PLACEONLOAD) { - writer.NewProperty("PlaceSceneObject"); - } else if (set == PlacedObjectSets::BLUEPRINT) { - writer.NewProperty("BlueprintObject"); - } else if (set == PlacedObjectSets::AIPLAN) { - writer.NewProperty("PlaceAIPlanObject"); + // TODO: Figure out how to draw the actual modules that compose an assembly that is part of the scheme. For now just disable. + /* + else if (const BunkerAssemblyScheme *assemblyScheme = dynamic_cast(sceneObject)) { + Vector pos = assemblyScheme->GetPos() + assemblyScheme->GetBitmapOffset(); + + // Wrapped drawing + if (pos.GetFloorIntX() < 0) { + rect(previewBuffer, pos.GetFloorIntX() + sceneWidth, pos.GetFloorIntY(), pos.GetFloorIntX() + sceneWidth + assemblyScheme->GetBitmapWidth(), pos.GetFloorIntY() + assemblyScheme->GetBitmapHeight(), 5); + } else if (pos.GetFloorIntX() + assemblyScheme->GetBitmapWidth() >= sceneWidth) { + rect(previewBuffer, pos.GetFloorIntX() - sceneWidth, pos.GetFloorIntY(), pos.GetFloorIntX() - sceneWidth + assemblyScheme->GetBitmapWidth(), pos.GetFloorIntY() + assemblyScheme->GetBitmapHeight(), 5); + } + rect(previewBuffer, pos.GetFloorIntX(), pos.GetFloorIntY(), pos.GetFloorIntX() + ((float)assemblyScheme->GetBitmapWidth()), pos.GetFloorIntY() + ((float)assemblyScheme->GetBitmapHeight()), 5); } + */ + } - //writer << placedObject; - SaveSceneObject(writer, placedObject, false, doFullGameSave); - } - } - - for (std::list::const_iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) - { - writer.NewProperty("AddBackgroundLayer"); - (*slItr)->SavePresetCopy(writer); - } - if (!m_UnseenPixelSize[Activity::TeamOne].IsZero()) - { - writer.NewProperty("AllUnseenPixelSizeTeam1"); - writer << m_UnseenPixelSize[Activity::TeamOne]; - } - if (!m_UnseenPixelSize[Activity::TeamTwo].IsZero()) - { - writer.NewProperty("AllUnseenPixelSizeTeam2"); - writer << m_UnseenPixelSize[Activity::TeamTwo]; - } - if (!m_UnseenPixelSize[Activity::TeamThree].IsZero()) - { - writer.NewProperty("AllUnseenPixelSizeTeam3"); - writer << m_UnseenPixelSize[Activity::TeamThree]; - } - if (!m_UnseenPixelSize[Activity::TeamFour].IsZero()) - { - writer.NewProperty("AllUnseenPixelSizeTeam4"); - writer << m_UnseenPixelSize[Activity::TeamFour]; - } - if (m_apUnseenLayer[Activity::TeamOne]) - { - writer.NewProperty("UnseenLayerTeam1"); - writer << m_apUnseenLayer[Activity::TeamOne]; - } - if (m_apUnseenLayer[Activity::TeamTwo]) - { - writer.NewProperty("UnseenLayerTeam2"); - writer << m_apUnseenLayer[Activity::TeamTwo]; - } - if (m_apUnseenLayer[Activity::TeamThree]) - { - writer.NewProperty("UnseenLayerTeam3"); - writer << m_apUnseenLayer[Activity::TeamThree]; - } - if (m_apUnseenLayer[Activity::TeamFour]) - { - writer.NewProperty("UnseenLayerTeam4"); - writer << m_apUnseenLayer[Activity::TeamFour]; - } - if (m_ScanScheduled[Activity::TeamOne]) - { - writer.NewProperty("ScanScheduledTeam1"); - writer << m_ScanScheduled[Activity::TeamOne]; - } - if (m_ScanScheduled[Activity::TeamTwo]) - { - writer.NewProperty("ScanScheduledTeam2"); - writer << m_ScanScheduled[Activity::TeamTwo]; - } - if (m_ScanScheduled[Activity::TeamThree]) - { - writer.NewProperty("ScanScheduledTeam3"); - writer << m_ScanScheduled[Activity::TeamThree]; - } - if (m_ScanScheduled[Activity::TeamFour]) - { - writer.NewProperty("ScanScheduledTeam4"); - writer << m_ScanScheduled[Activity::TeamFour]; - } - for (std::list::const_iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) - { - // Only write the area if it has any boxes/area at all - if (doFullGameSave || !(*aItr).HasNoArea()) { - writer.NewProperty("AddArea"); - writer << *aItr; + masked_stretch_blit(previewBuffer, m_pPreviewBitmap, 0, 0, previewBuffer->w, previewBuffer->h, 0, 0, m_pPreviewBitmap->w, m_pPreviewBitmap->h); + destroy_bitmap(previewBuffer); + + if (g_FrameMan.SaveBitmapToPNG(m_pPreviewBitmap, bitmapPath.c_str()) == 0) { + m_PreviewBitmapFile.SetDataPath(bitmapPath); } + + return 0; } - writer.NewProperty("GlobalAcceleration"); - writer << m_GlobalAcc; - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ClearData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears out any previously loaded bitmap data from memory. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int Scene::ClearData() { + if (!m_pTerrain) + return 0; -void Scene::SaveSceneObject(Writer &writer, const SceneObject *sceneObjectToSave, bool isChildAttachable, bool saveFullData) const { - auto WriteHardcodedAttachableOrNone = [this, &writer, &saveFullData](const std::string &propertyName, const Attachable *harcodedAttachable) { - if (harcodedAttachable) { - writer.NewProperty(propertyName); - SaveSceneObject(writer, harcodedAttachable, true, saveFullData); - } else { - writer.NewPropertyWithValue(propertyName, "None"); + // Clear Terrain's data + if (m_pTerrain->ClearData() < 0) { + RTEAbort("Clearing Terrain " + m_pTerrain->GetPresetName() + "\'s data failed!"); + return -1; } - }; - writer.ObjectStart(sceneObjectToSave->GetClassName()); - writer.NewPropertyWithValue("CopyOf", sceneObjectToSave->GetModuleAndPresetName()); - - if (saveFullData) { - for (const std::string &group : *sceneObjectToSave->GetGroups()) { - writer.NewPropertyWithValue("AddToGroup", group); + // Clear unseen layers' data + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + if (m_apUnseenLayer[team]) { + // Clear unseen layer data from memory + if (m_apUnseenLayer[team]->ClearData() < 0) { + g_ConsoleMan.PrintString("ERROR: Clearing unseen layer " + m_apUnseenLayer[team]->GetPresetName() + "\'s data failed!"); + return -1; + } + } } - } - writer.NewPropertyWithValue("Position", sceneObjectToSave->GetPos()); - writer.NewPropertyWithValue("Team", sceneObjectToSave->GetTeam()); - if (!isChildAttachable) { - writer.NewPropertyWithValue("PlacedByPlayer", sceneObjectToSave->GetPlacedByPlayer()); - } - if (saveFullData) { - writer.NewPropertyWithValue("GoldValue", sceneObjectToSave->GetGoldValue()); + return 0; } - if (const Deployment *deploymentToSave = dynamic_cast(sceneObjectToSave); deploymentToSave && deploymentToSave->GetID() != 0) { - writer.NewPropertyWithValue("ID", deploymentToSave->GetID()); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int Scene::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Entity::ReadProperty(propName, reader)); + + MatchProperty("LocationOnPlanet", { reader >> m_Location; }); + MatchProperty("MetagamePlayable", { reader >> m_MetagamePlayable; }); + MatchProperty("Revealed", { reader >> m_Revealed; }); + MatchProperty("MetasceneParent", { reader >> m_MetasceneParent; }); + MatchProperty("MetagameInternal", { reader >> m_IsMetagameInternal; }); + MatchProperty("ScriptSave", { reader >> m_IsSavedGameInternal; }); + MatchProperty("OwnedByTeam", { reader >> m_OwnedByTeam; }); + MatchProperty("RoundIncome", { reader >> m_RoundIncome; }); + MatchProperty("P1ResidentBrain", { m_ResidentBrains[Players::PlayerOne] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); }); + MatchProperty("P2ResidentBrain", { m_ResidentBrains[Players::PlayerTwo] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); }); + MatchProperty("P3ResidentBrain", { m_ResidentBrains[Players::PlayerThree] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); }); + MatchProperty("P4ResidentBrain", { m_ResidentBrains[Players::PlayerFour] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); }); + MatchProperty("P1BuildBudget", { reader >> m_BuildBudget[Players::PlayerOne]; }); + MatchProperty("P2BuildBudget", { reader >> m_BuildBudget[Players::PlayerTwo]; }); + MatchProperty("P3BuildBudget", { reader >> m_BuildBudget[Players::PlayerThree]; }); + MatchProperty("P4BuildBudget", { reader >> m_BuildBudget[Players::PlayerFour]; }); + MatchProperty("P1BuildBudgetRatio", { reader >> m_BuildBudgetRatio[Players::PlayerOne]; }); + MatchProperty("P2BuildBudgetRatio", { reader >> m_BuildBudgetRatio[Players::PlayerTwo]; }); + MatchProperty("P3BuildBudgetRatio", { reader >> m_BuildBudgetRatio[Players::PlayerThree]; }); + MatchProperty("P4BuildBudgetRatio", { reader >> m_BuildBudgetRatio[Players::PlayerFour]; }); + MatchProperty("AutoDesigned", { reader >> m_AutoDesigned; }); + MatchProperty("TotalInvestment", { reader >> m_TotalInvestment; }); + MatchProperty("PreviewBitmapFile", + reader >> m_PreviewBitmapFile; + m_pPreviewBitmap = m_PreviewBitmapFile.GetAsBitmap(COLORCONV_NONE, false);); + MatchProperty("Terrain", + delete m_pTerrain; + m_pTerrain = new SLTerrain(); + reader >> m_pTerrain;); + MatchForwards("PlaceSceneObject") + MatchProperty( + "PlaceMovableObject", + SceneObject* pSO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); + if (pSO) { + m_PlacedObjects[PLACEONLOAD].push_back(pSO); + }); + MatchProperty( + "BlueprintObject", + SceneObject* pSO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); + if (pSO) { + m_PlacedObjects[BLUEPRINT].push_back(pSO); + }); + MatchProperty( + "PlaceAIPlanObject", + SceneObject* pSO = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); + if (pSO) { + m_PlacedObjects[AIPLAN].push_back(pSO); + }); + MatchProperty( + "AddBackgroundLayer", + SLBackground* pLayer = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)); + RTEAssert(pLayer, "Something went wrong with reading SceneLayer"); + if (pLayer) { + m_BackLayerList.push_back(pLayer); + }); + MatchProperty("AllUnseenPixelSizeTeam1", + // Read the desired pixel dimensions of the dynamically generated unseen map + reader >> m_UnseenPixelSize[Activity::TeamOne];); + MatchProperty("AllUnseenPixelSizeTeam2", + // Read the desired pixel dimensions of the dynamically generated unseen map + reader >> m_UnseenPixelSize[Activity::TeamTwo];); + MatchProperty("AllUnseenPixelSizeTeam3", + // Read the desired pixel dimensions of the dynamically generated unseen map + reader >> m_UnseenPixelSize[Activity::TeamThree];); + MatchProperty("AllUnseenPixelSizeTeam4", + // Read the desired pixel dimensions of the dynamically generated unseen map + reader >> m_UnseenPixelSize[Activity::TeamFour];); + MatchProperty("UnseenLayerTeam1", + delete m_apUnseenLayer[Activity::TeamOne]; + m_apUnseenLayer[Activity::TeamOne] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader));); + MatchProperty("UnseenLayerTeam2", + delete m_apUnseenLayer[Activity::TeamTwo]; + m_apUnseenLayer[Activity::TeamTwo] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader));); + MatchProperty("UnseenLayerTeam3", + delete m_apUnseenLayer[Activity::TeamThree]; + m_apUnseenLayer[Activity::TeamThree] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader));); + MatchProperty("UnseenLayerTeam4", + delete m_apUnseenLayer[Activity::TeamFour]; + m_apUnseenLayer[Activity::TeamFour] = dynamic_cast(g_PresetMan.ReadReflectedPreset(reader));); + MatchProperty("ScanScheduledTeam1", { reader >> m_ScanScheduled[Activity::TeamOne]; }); + MatchProperty("ScanScheduledTeam2", { reader >> m_ScanScheduled[Activity::TeamTwo]; }); + MatchProperty("ScanScheduledTeam3", { reader >> m_ScanScheduled[Activity::TeamThree]; }); + MatchProperty("ScanScheduledTeam4", { reader >> m_ScanScheduled[Activity::TeamFour]; }); + MatchProperty("AddArea", + Area area; + reader >> area; + // This replaces any existing ones + SetArea(area);); + MatchProperty("GlobalAcceleration", { reader >> m_GlobalAcc; }); + + EndPropertyList; } - if (const MovableObject *movableObjectToSave = dynamic_cast(sceneObjectToSave); movableObjectToSave && saveFullData) { - writer.NewPropertyWithValue("HUDVisible", movableObjectToSave->GetHUDVisible()); - writer.NewPropertyWithValue("Velocity", movableObjectToSave->GetVel()); - writer.NewPropertyWithValue("LifeTime", movableObjectToSave->GetLifetime()); - writer.NewPropertyWithValue("Age", movableObjectToSave->GetAge()); - writer.NewPropertyWithValue("PinStrength", movableObjectToSave->GetPinStrength()); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this Scene with a Writer for + // later recreation with Create(Reader &reader); - if (const MOSprite *moSpriteToSave = dynamic_cast(sceneObjectToSave)) { - writer.NewPropertyWithValue("HFlipped", moSpriteToSave->IsHFlipped()); - if (saveFullData || dynamic_cast(moSpriteToSave)) { - writer.NewPropertyWithValue("Rotation", moSpriteToSave->GetRotMatrix()); - } - if (saveFullData) { - writer.NewPropertyWithValue("AngularVel", moSpriteToSave->GetAngularVel()); - } - } + int Scene::Save(Writer& writer) const { + Entity::Save(writer); - if (const MOSRotating *mosRotatingToSave = dynamic_cast(sceneObjectToSave)) { - if (saveFullData) { - const std::list &attachablesToSave = mosRotatingToSave->GetAttachableList(); + bool doFullGameSave = !dynamic_cast(g_ActivityMan.GetActivity()); - // If this MOSRotating has any Attachables, we have to add a special behaviour property that'll delete them all so they can be re-read. This will allow us to handle Attachables with our limited serialization. - // Alternatively, if the MOSRotating has no Attachables but its preset does, we need to set the flag, because that means this is missing Attachables, and we don't want to magically regenerate them when a game is loaded. - if (!attachablesToSave.empty()) { - writer.NewPropertyWithValue("SpecialBehaviour_ClearAllAttachables", true); - } - else if (const MOSRotating *presetOfMOSRotatingToSave = dynamic_cast(g_PresetMan.GetEntityPreset(mosRotatingToSave->GetClassName(), mosRotatingToSave->GetPresetName(), mosRotatingToSave->GetModuleID())); presetOfMOSRotatingToSave && !presetOfMOSRotatingToSave->GetAttachableList().empty()) { - writer.NewPropertyWithValue("SpecialBehaviour_ClearAllAttachables", true); + writer.NewPropertyWithValue("LocationOnPlanet", m_Location); + writer.NewPropertyWithValue("MetagamePlayable", m_MetagamePlayable); + // Do not save preview if it's path is empty, for example in metagame + if (m_PreviewBitmapFile.GetDataPath().length() > 0 && m_MetasceneParent.length() == 0) { + writer.NewPropertyWithValue("PreviewBitmapFile", m_PreviewBitmapFile); + } + if (m_MetasceneParent.length() > 0) { + writer.NewPropertyWithValue("MetasceneParent", m_MetasceneParent); + } + writer.NewPropertyWithValue("MetagameInternal", m_IsMetagameInternal); + writer.NewPropertyWithValue("ScriptSave", m_IsSavedGameInternal); + writer.NewPropertyWithValue("Revealed", m_Revealed); + writer.NewPropertyWithValue("OwnedByTeam", m_OwnedByTeam); + writer.NewPropertyWithValue("RoundIncome", m_RoundIncome); + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + std::string playerNumberString = "P" + std::to_string(player + 1); + writer.NewPropertyWithValue(playerNumberString + "BuildBudget", m_BuildBudget[player]); + writer.NewPropertyWithValue(playerNumberString + "BuildBudgetRatio", m_BuildBudgetRatio[player]); + if (m_ResidentBrains[player]) { + writer.NewProperty(playerNumberString + "ResidentBrain"); + SaveSceneObject(writer, m_ResidentBrains[player], false, doFullGameSave); } + } - for (const Attachable *attachable : attachablesToSave) { - if (!mosRotatingToSave->AttachableIsHardcoded(attachable)) { - writer.NewProperty("AddAttachable"); - SaveSceneObject(writer, attachable, true, saveFullData); + writer.NewPropertyWithValue("AutoDesigned", m_AutoDesigned); + writer.NewPropertyWithValue("TotalInvestment", m_TotalInvestment); + writer.NewPropertyWithValue("Terrain", m_pTerrain); + + for (int set = PlacedObjectSets::PLACEONLOAD; set < PlacedObjectSets::PLACEDSETSCOUNT; ++set) { + for (const SceneObject* placedObject: m_PlacedObjects[set]) { + if (placedObject->GetPresetName().empty() || placedObject->GetPresetName() == "None") { + // We have no info about what we're placing. This is probably because it's some particle that was kicked off the terrain + // In future, we'll save all the data (uncomment out //writer << placedObject;), and will be able to save/load that stuff + // But for now, until we have a more effective writer that can remove redundant properties, we just skip this + continue; + } + + if (set == PlacedObjectSets::PLACEONLOAD) { + writer.NewProperty("PlaceSceneObject"); + } else if (set == PlacedObjectSets::BLUEPRINT) { + writer.NewProperty("BlueprintObject"); + } else if (set == PlacedObjectSets::AIPLAN) { + writer.NewProperty("PlaceAIPlanObject"); } - } - for (const AEmitter *wound : mosRotatingToSave->GetWoundList()) { - writer.NewProperty("SpecialBehaviour_AddWound"); - SaveSceneObject(writer, wound, true, saveFullData); - } - } - for (auto &[key, value] : mosRotatingToSave->GetStringValueMap()) { - writer.NewProperty("AddCustomValue"); - writer.ObjectStart("StringValue"); - writer.NewPropertyWithValue(key, value); - writer.ObjectEnd(); - } - - for (auto &[key, value] : mosRotatingToSave->GetNumberValueMap()) { - writer.NewProperty("AddCustomValue"); - writer.ObjectStart("NumberValue"); - writer.NewPropertyWithValue(key, value); - writer.ObjectEnd(); - } - } - - if (const Attachable *attachableToSave = dynamic_cast(sceneObjectToSave); attachableToSave && saveFullData) { - writer.NewPropertyWithValue("ParentOffset", attachableToSave->GetParentOffset()); - writer.NewPropertyWithValue("DrawAfterParent", attachableToSave->IsDrawnAfterParent()); - writer.NewPropertyWithValue("DeleteWhenRemovedFromParent", attachableToSave->GetDeleteWhenRemovedFromParent()); - writer.NewPropertyWithValue("GibWhenRemovedFromParent", attachableToSave->GetGibWhenRemovedFromParent()); - writer.NewPropertyWithValue("JointStrength", attachableToSave->GetJointStrength()); - writer.NewPropertyWithValue("JointStiffness", attachableToSave->GetJointStiffness()); - writer.NewPropertyWithValue("JointOffset", attachableToSave->GetJointOffset()); - writer.NewPropertyWithValue("InheritsHFlipped", attachableToSave->InheritsHFlipped()); - writer.NewPropertyWithValue("InheritsRotAngle", attachableToSave->InheritsRotAngle()); - writer.NewPropertyWithValue("InheritedRotAngleOffset", attachableToSave->GetInheritedRotAngleOffset()); - writer.NewPropertyWithValue("InheritsFrame", attachableToSave->InheritsFrame()); - writer.NewPropertyWithValue("CollidesWithTerrainWhileAttached", attachableToSave->GetCollidesWithTerrainWhileAttached()); - - if (const AEmitter *aemitterToSave = dynamic_cast(sceneObjectToSave)) { - writer.NewPropertyWithValue("EmissionEnabled", aemitterToSave->IsEmitting()); - writer.NewPropertyWithValue("EmissionCount", aemitterToSave->GetEmitCount()); - writer.NewPropertyWithValue("EmissionCountLimit", aemitterToSave->GetEmitCountLimit()); - writer.NewPropertyWithValue("NegativeThrottleMultiplier", aemitterToSave->GetNegativeThrottleMultiplier()); - writer.NewPropertyWithValue("PositiveThrottleMultiplier", aemitterToSave->GetPositiveThrottleMultiplier()); - writer.NewPropertyWithValue("Throttle", aemitterToSave->GetThrottle()); - writer.NewPropertyWithValue("BurstScale", aemitterToSave->GetBurstScale()); - writer.NewPropertyWithValue("BurstDamage", aemitterToSave->GetBurstDamage()); - writer.NewPropertyWithValue("EmitterDamageMultiplier", aemitterToSave->GetEmitterDamageMultiplier()); - writer.NewPropertyWithValue("BurstSpacing", aemitterToSave->GetBurstSpacing()); - writer.NewPropertyWithValue("BurstTriggered", aemitterToSave->IsSetToBurst()); - writer.NewPropertyWithValue("EmissionAngle", aemitterToSave->GetEmitAngleMatrix()); - writer.NewPropertyWithValue("EmissionOffset", aemitterToSave->GetEmitOffset()); - writer.NewPropertyWithValue("EmissionDamage", aemitterToSave->GetEmitDamage()); - WriteHardcodedAttachableOrNone("Flash", aemitterToSave->GetFlash()); - } - - if (const AEJetpack *jetpackToSave = dynamic_cast(sceneObjectToSave)) { - writer.NewProperty("JetpackType"); - switch (jetpackToSave->GetJetpackType()) { - default: - case AEJetpack::JetpackType::Standard: - writer << "Standard"; - break; - case AEJetpack::JetpackType::JumpPack: - writer << "JumpPack"; - break; - } - - writer.NewPropertyWithValue("JumpTime", jetpackToSave->GetJetTimeTotal() / 1000.0f); // Convert to seconds - writer.NewPropertyWithValue("JumpReplenishRate", jetpackToSave->GetJetReplenishRate()); - writer.NewPropertyWithValue("MinimumFuelRatio", jetpackToSave->GetMinimumFuelRatio()); - writer.NewPropertyWithValue("JumpAngleRange", jetpackToSave->GetJetAngleRange()); - writer.NewPropertyWithValue("CanAdjustAngleWhileFiring", jetpackToSave->GetCanAdjustAngleWhileFiring()); - } - - if (const Arm *armToSave = dynamic_cast(sceneObjectToSave)) { - WriteHardcodedAttachableOrNone("HeldDevice", armToSave->GetHeldDevice()); - } - - if (const Leg *legToSave = dynamic_cast(sceneObjectToSave)) { - WriteHardcodedAttachableOrNone("Foot", legToSave->GetFoot()); - } - - if (const Turret *turretToSave = dynamic_cast(sceneObjectToSave)) { - for (const HeldDevice *heldDeviceToSave : turretToSave->GetMountedDevices()) { - WriteHardcodedAttachableOrNone("AddMountedDevice", heldDeviceToSave); + // writer << placedObject; + SaveSceneObject(writer, placedObject, false, doFullGameSave); } } - if (const HeldDevice *heldDeviceToSave = dynamic_cast(sceneObjectToSave)) { - writer.NewPropertyWithValue("SpecialBehaviour_Activated", heldDeviceToSave->IsActivated()); - writer.NewPropertyWithValue("SpecialBehaviour_ActivationTimerElapsedSimTimeMS", heldDeviceToSave->GetActivationTimer().GetElapsedSimTimeMS()); + for (std::list::const_iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) { + writer.NewProperty("AddBackgroundLayer"); + (*slItr)->SavePresetCopy(writer); } - - if (const HDFirearm *hdFirearmToSave = dynamic_cast(sceneObjectToSave)) { - WriteHardcodedAttachableOrNone("Magazine", hdFirearmToSave->GetMagazine()); - WriteHardcodedAttachableOrNone("Flash", hdFirearmToSave->GetFlash()); + if (!m_UnseenPixelSize[Activity::TeamOne].IsZero()) { + writer.NewProperty("AllUnseenPixelSizeTeam1"); + writer << m_UnseenPixelSize[Activity::TeamOne]; } - - if (const Magazine *magazineToSave = dynamic_cast(sceneObjectToSave)) { - writer.NewPropertyWithValue("RoundCount", magazineToSave->GetRoundCount()); + if (!m_UnseenPixelSize[Activity::TeamTwo].IsZero()) { + writer.NewProperty("AllUnseenPixelSizeTeam2"); + writer << m_UnseenPixelSize[Activity::TeamTwo]; + } + if (!m_UnseenPixelSize[Activity::TeamThree].IsZero()) { + writer.NewProperty("AllUnseenPixelSizeTeam3"); + writer << m_UnseenPixelSize[Activity::TeamThree]; + } + if (!m_UnseenPixelSize[Activity::TeamFour].IsZero()) { + writer.NewProperty("AllUnseenPixelSizeTeam4"); + writer << m_UnseenPixelSize[Activity::TeamFour]; + } + if (m_apUnseenLayer[Activity::TeamOne]) { + writer.NewProperty("UnseenLayerTeam1"); + writer << m_apUnseenLayer[Activity::TeamOne]; + } + if (m_apUnseenLayer[Activity::TeamTwo]) { + writer.NewProperty("UnseenLayerTeam2"); + writer << m_apUnseenLayer[Activity::TeamTwo]; + } + if (m_apUnseenLayer[Activity::TeamThree]) { + writer.NewProperty("UnseenLayerTeam3"); + writer << m_apUnseenLayer[Activity::TeamThree]; + } + if (m_apUnseenLayer[Activity::TeamFour]) { + writer.NewProperty("UnseenLayerTeam4"); + writer << m_apUnseenLayer[Activity::TeamFour]; } + if (m_ScanScheduled[Activity::TeamOne]) { + writer.NewProperty("ScanScheduledTeam1"); + writer << m_ScanScheduled[Activity::TeamOne]; + } + if (m_ScanScheduled[Activity::TeamTwo]) { + writer.NewProperty("ScanScheduledTeam2"); + writer << m_ScanScheduled[Activity::TeamTwo]; + } + if (m_ScanScheduled[Activity::TeamThree]) { + writer.NewProperty("ScanScheduledTeam3"); + writer << m_ScanScheduled[Activity::TeamThree]; + } + if (m_ScanScheduled[Activity::TeamFour]) { + writer.NewProperty("ScanScheduledTeam4"); + writer << m_ScanScheduled[Activity::TeamFour]; + } + for (std::list::const_iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) { + // Only write the area if it has any boxes/area at all + if (doFullGameSave || !(*aItr).HasNoArea()) { + writer.NewProperty("AddArea"); + writer << *aItr; + } + } + writer.NewProperty("GlobalAcceleration"); + writer << m_GlobalAcc; + + return 0; } - if (const Actor *actorToSave = dynamic_cast(sceneObjectToSave)) { - writer.NewPropertyWithValue("Health", actorToSave->GetHealth()); - writer.NewPropertyWithValue("MaxHealth", actorToSave->GetMaxHealth()); - if (saveFullData) { - writer.NewPropertyWithValue("Status", actorToSave->GetStatus()); - writer.NewPropertyWithValue("PlayerControllable", actorToSave->IsPlayerControllable()); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int aiModeToSave = actorToSave->GetAIMode() == Actor::AIMode::AIMODE_SQUAD ? Actor::AIMode::AIMODE_GOTO : actorToSave->GetAIMode(); - if (aiModeToSave == Actor::AIMode::AIMODE_GOTO && (!actorToSave->GetMOMoveTarget() && g_SceneMan.ShortestDistance(actorToSave->GetMovePathEnd(), actorToSave->GetPos(), g_SceneMan.SceneWrapsX()).MagnitudeIsLessThan(1.0F))) { - aiModeToSave = Actor::AIMode::AIMODE_SENTRY; + void Scene::SaveSceneObject(Writer& writer, const SceneObject* sceneObjectToSave, bool isChildAttachable, bool saveFullData) const { + auto WriteHardcodedAttachableOrNone = [this, &writer, &saveFullData](const std::string& propertyName, const Attachable* harcodedAttachable) { + if (harcodedAttachable) { + writer.NewProperty(propertyName); + SaveSceneObject(writer, harcodedAttachable, true, saveFullData); + } else { + writer.NewPropertyWithValue(propertyName, "None"); } - writer.NewPropertyWithValue("AIMode", aiModeToSave); - if (aiModeToSave == Actor::AIMode::AIMODE_GOTO) { - const std::string addWaypointPropertyName = "SpecialBehaviour_AddAISceneWaypoint"; - if (const MovableObject *actorToSaveMOMoveTarget = actorToSave->GetMOMoveTarget()) { - writer.NewPropertyWithValue(addWaypointPropertyName, actorToSaveMOMoveTarget->GetPos()); - } else { - writer.NewPropertyWithValue(addWaypointPropertyName, actorToSave->GetMovePathEnd()); - for (auto &[waypointPosition, waypointObject] : actorToSave->GetWaypointList()) { - writer.NewPropertyWithValue(addWaypointPropertyName, waypointPosition); - } - } + }; + + writer.ObjectStart(sceneObjectToSave->GetClassName()); + writer.NewPropertyWithValue("CopyOf", sceneObjectToSave->GetModuleAndPresetName()); + + if (saveFullData) { + for (const std::string& group: *sceneObjectToSave->GetGroups()) { + writer.NewPropertyWithValue("AddToGroup", group); } } - if (actorToSave->GetDeploymentID()) { - writer.NewPropertyWithValue("DeploymentID", actorToSave->GetDeploymentID()); + writer.NewPropertyWithValue("Position", sceneObjectToSave->GetPos()); + writer.NewPropertyWithValue("Team", sceneObjectToSave->GetTeam()); + if (!isChildAttachable) { + writer.NewPropertyWithValue("PlacedByPlayer", sceneObjectToSave->GetPlacedByPlayer()); + } + if (saveFullData) { + writer.NewPropertyWithValue("GoldValue", sceneObjectToSave->GetGoldValue()); + } + + if (const Deployment* deploymentToSave = dynamic_cast(sceneObjectToSave); deploymentToSave && deploymentToSave->GetID() != 0) { + writer.NewPropertyWithValue("ID", deploymentToSave->GetID()); } - for (const MovableObject *inventoryItem : *actorToSave->GetInventory()) { - writer.NewProperty("AddInventory"); - SaveSceneObject(writer, inventoryItem, true, saveFullData); + if (const MovableObject* movableObjectToSave = dynamic_cast(sceneObjectToSave); movableObjectToSave && saveFullData) { + writer.NewPropertyWithValue("HUDVisible", movableObjectToSave->GetHUDVisible()); + writer.NewPropertyWithValue("Velocity", movableObjectToSave->GetVel()); + writer.NewPropertyWithValue("LifeTime", movableObjectToSave->GetLifetime()); + writer.NewPropertyWithValue("Age", movableObjectToSave->GetAge()); + writer.NewPropertyWithValue("PinStrength", movableObjectToSave->GetPinStrength()); } - if (saveFullData) { - if (const ADoor *aDoorToSave = dynamic_cast(sceneObjectToSave)) { - WriteHardcodedAttachableOrNone("Door", aDoorToSave->GetDoor()); - } else if (const AHuman *aHumanToSave = dynamic_cast(sceneObjectToSave)) { - WriteHardcodedAttachableOrNone("Head", aHumanToSave->GetHead()); - WriteHardcodedAttachableOrNone("Jetpack", aHumanToSave->GetJetpack()); - WriteHardcodedAttachableOrNone("FGArm", aHumanToSave->GetFGArm()); - WriteHardcodedAttachableOrNone("BGArm", aHumanToSave->GetBGArm()); - WriteHardcodedAttachableOrNone("FGLeg", aHumanToSave->GetFGLeg()); - WriteHardcodedAttachableOrNone("BGLeg", aHumanToSave->GetBGLeg()); - } else if (const ACrab *aCrabToSave = dynamic_cast(sceneObjectToSave)) { - WriteHardcodedAttachableOrNone("Turret", aCrabToSave->GetTurret()); - WriteHardcodedAttachableOrNone("Jetpack", aCrabToSave->GetJetpack()); - WriteHardcodedAttachableOrNone("LeftFGLeg", aCrabToSave->GetLeftFGLeg()); - WriteHardcodedAttachableOrNone("LeftBGLeg", aCrabToSave->GetLeftBGLeg()); - WriteHardcodedAttachableOrNone("RightFGLeg", aCrabToSave->GetRightFGLeg()); - WriteHardcodedAttachableOrNone("RightBGLeg", aCrabToSave->GetRightBGLeg()); - } else if (const ACRocket *acRocketToSave = dynamic_cast(sceneObjectToSave)) { - WriteHardcodedAttachableOrNone("RightLeg", acRocketToSave->GetRightLeg()); - WriteHardcodedAttachableOrNone("LeftLeg", acRocketToSave->GetLeftLeg()); - WriteHardcodedAttachableOrNone("MainThruster", acRocketToSave->GetMainThruster()); - WriteHardcodedAttachableOrNone("RightThruster", acRocketToSave->GetRightThruster()); - WriteHardcodedAttachableOrNone("LeftThruster", acRocketToSave->GetLeftThruster()); - WriteHardcodedAttachableOrNone("UpRightThruster", acRocketToSave->GetURightThruster()); - WriteHardcodedAttachableOrNone("UpRightThruster", acRocketToSave->GetULeftThruster()); - } else if (const ACDropShip *acDropShipToSave = dynamic_cast(sceneObjectToSave)) { - WriteHardcodedAttachableOrNone("RightThruster", acDropShipToSave->GetRightThruster()); - WriteHardcodedAttachableOrNone("LeftThruster", acDropShipToSave->GetLeftThruster()); - WriteHardcodedAttachableOrNone("UpRightThruster", acDropShipToSave->GetURightThruster()); - WriteHardcodedAttachableOrNone("UpLeftThruster", acDropShipToSave->GetULeftThruster()); - WriteHardcodedAttachableOrNone("RightHatchDoor", acDropShipToSave->GetRightHatch()); - WriteHardcodedAttachableOrNone("LeftHatchDoor", acDropShipToSave->GetLeftHatch()); - } - } else if (const AHuman *aHumanToSave = dynamic_cast(sceneObjectToSave)) { - if (const HeldDevice *equippedItem = aHumanToSave->GetEquippedItem()) { - writer.NewProperty("AddInventory"); - SaveSceneObject(writer, equippedItem, true, saveFullData); + if (const MOSprite* moSpriteToSave = dynamic_cast(sceneObjectToSave)) { + writer.NewPropertyWithValue("HFlipped", moSpriteToSave->IsHFlipped()); + if (saveFullData || dynamic_cast(moSpriteToSave)) { + writer.NewPropertyWithValue("Rotation", moSpriteToSave->GetRotMatrix()); } - if (const HeldDevice *bgEquippedItem = aHumanToSave->GetEquippedBGItem()) { - writer.NewProperty("AddInventory"); - SaveSceneObject(writer, bgEquippedItem, true, saveFullData); + if (saveFullData) { + writer.NewPropertyWithValue("AngularVel", moSpriteToSave->GetAngularVel()); } } - } - writer.ObjectEnd(); -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (const MOSRotating* mosRotatingToSave = dynamic_cast(sceneObjectToSave)) { + if (saveFullData) { + const std::list& attachablesToSave = mosRotatingToSave->GetAttachableList(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Scene object. - -void Scene::Destroy(bool notInherited) -{ - delete m_pTerrain; - - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - delete m_ResidentBrains[player]; - } - - for (int set = PLACEONLOAD; set < PLACEDSETSCOUNT; ++set) - { - for (std::list::iterator oItr = m_PlacedObjects[set].begin(); oItr != m_PlacedObjects[set].end(); ++oItr) - { - delete (*oItr); - *oItr = 0; - } - } - - for (std::list::iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) - { - delete (*slItr); - *slItr = 0; - } - - for (std::list::iterator slItr = m_Deployments.begin(); slItr != m_Deployments.end(); ++slItr) - { - delete (*slItr); - *slItr = 0; - } - - delete m_apUnseenLayer[Activity::TeamOne]; - delete m_apUnseenLayer[Activity::TeamTwo]; - delete m_apUnseenLayer[Activity::TeamThree]; - delete m_apUnseenLayer[Activity::TeamFour]; + // If this MOSRotating has any Attachables, we have to add a special behaviour property that'll delete them all so they can be re-read. This will allow us to handle Attachables with our limited serialization. + // Alternatively, if the MOSRotating has no Attachables but its preset does, we need to set the flag, because that means this is missing Attachables, and we don't want to magically regenerate them when a game is loaded. + if (!attachablesToSave.empty()) { + writer.NewPropertyWithValue("SpecialBehaviour_ClearAllAttachables", true); + } else if (const MOSRotating* presetOfMOSRotatingToSave = dynamic_cast(g_PresetMan.GetEntityPreset(mosRotatingToSave->GetClassName(), mosRotatingToSave->GetPresetName(), mosRotatingToSave->GetModuleID())); presetOfMOSRotatingToSave && !presetOfMOSRotatingToSave->GetAttachableList().empty()) { + writer.NewPropertyWithValue("SpecialBehaviour_ClearAllAttachables", true); + } - //if (m_PreviewBitmapOwned) - destroy_bitmap(m_pPreviewBitmap); - m_pPreviewBitmap = 0; + for (const Attachable* attachable: attachablesToSave) { + if (!mosRotatingToSave->AttachableIsHardcoded(attachable)) { + writer.NewProperty("AddAttachable"); + SaveSceneObject(writer, attachable, true, saveFullData); + } + } + for (const AEmitter* wound: mosRotatingToSave->GetWoundList()) { + writer.NewProperty("SpecialBehaviour_AddWound"); + SaveSceneObject(writer, wound, true, saveFullData); + } + } - if (!notInherited) - Entity::Destroy(); - Clear(); -} + for (auto& [key, value]: mosRotatingToSave->GetStringValueMap()) { + writer.NewProperty("AddCustomValue"); + writer.ObjectStart("StringValue"); + writer.NewPropertyWithValue(key, value); + writer.ObjectEnd(); + } + for (auto& [key, value]: mosRotatingToSave->GetNumberValueMap()) { + writer.NewProperty("AddCustomValue"); + writer.ObjectStart("NumberValue"); + writer.NewPropertyWithValue(key, value); + writer.ObjectEnd(); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: MigrateToModule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this an original Preset in a different module than it was before. -// It severs ties deeply to the old module it was saved in. + if (const Attachable* attachableToSave = dynamic_cast(sceneObjectToSave); attachableToSave && saveFullData) { + writer.NewPropertyWithValue("ParentOffset", attachableToSave->GetParentOffset()); + writer.NewPropertyWithValue("DrawAfterParent", attachableToSave->IsDrawnAfterParent()); + writer.NewPropertyWithValue("DeleteWhenRemovedFromParent", attachableToSave->GetDeleteWhenRemovedFromParent()); + writer.NewPropertyWithValue("GibWhenRemovedFromParent", attachableToSave->GetGibWhenRemovedFromParent()); + writer.NewPropertyWithValue("JointStrength", attachableToSave->GetJointStrength()); + writer.NewPropertyWithValue("JointStiffness", attachableToSave->GetJointStiffness()); + writer.NewPropertyWithValue("JointOffset", attachableToSave->GetJointOffset()); + writer.NewPropertyWithValue("InheritsHFlipped", attachableToSave->InheritsHFlipped()); + writer.NewPropertyWithValue("InheritsRotAngle", attachableToSave->InheritsRotAngle()); + writer.NewPropertyWithValue("InheritedRotAngleOffset", attachableToSave->GetInheritedRotAngleOffset()); + writer.NewPropertyWithValue("InheritsFrame", attachableToSave->InheritsFrame()); + writer.NewPropertyWithValue("CollidesWithTerrainWhileAttached", attachableToSave->GetCollidesWithTerrainWhileAttached()); + + if (const AEmitter* aemitterToSave = dynamic_cast(sceneObjectToSave)) { + writer.NewPropertyWithValue("EmissionEnabled", aemitterToSave->IsEmitting()); + writer.NewPropertyWithValue("EmissionCount", aemitterToSave->GetEmitCount()); + writer.NewPropertyWithValue("EmissionCountLimit", aemitterToSave->GetEmitCountLimit()); + writer.NewPropertyWithValue("NegativeThrottleMultiplier", aemitterToSave->GetNegativeThrottleMultiplier()); + writer.NewPropertyWithValue("PositiveThrottleMultiplier", aemitterToSave->GetPositiveThrottleMultiplier()); + writer.NewPropertyWithValue("Throttle", aemitterToSave->GetThrottle()); + writer.NewPropertyWithValue("BurstScale", aemitterToSave->GetBurstScale()); + writer.NewPropertyWithValue("BurstDamage", aemitterToSave->GetBurstDamage()); + writer.NewPropertyWithValue("EmitterDamageMultiplier", aemitterToSave->GetEmitterDamageMultiplier()); + writer.NewPropertyWithValue("BurstSpacing", aemitterToSave->GetBurstSpacing()); + writer.NewPropertyWithValue("BurstTriggered", aemitterToSave->IsSetToBurst()); + writer.NewPropertyWithValue("EmissionAngle", aemitterToSave->GetEmitAngleMatrix()); + writer.NewPropertyWithValue("EmissionOffset", aemitterToSave->GetEmitOffset()); + writer.NewPropertyWithValue("EmissionDamage", aemitterToSave->GetEmitDamage()); + WriteHardcodedAttachableOrNone("Flash", aemitterToSave->GetFlash()); + } -bool Scene::MigrateToModule(int whichModule) -{ - if (!Entity::MigrateToModule(whichModule)) - return false; + if (const AEJetpack* jetpackToSave = dynamic_cast(sceneObjectToSave)) { + writer.NewProperty("JetpackType"); + switch (jetpackToSave->GetJetpackType()) { + default: + case AEJetpack::JetpackType::Standard: + writer << "Standard"; + break; + case AEJetpack::JetpackType::JumpPack: + writer << "JumpPack"; + break; + } - m_pTerrain->MigrateToModule(whichModule); + writer.NewPropertyWithValue("JumpTime", jetpackToSave->GetJetTimeTotal() / 1000.0f); // Convert to seconds + writer.NewPropertyWithValue("JumpReplenishRate", jetpackToSave->GetJetReplenishRate()); + writer.NewPropertyWithValue("MinimumFuelRatio", jetpackToSave->GetMinimumFuelRatio()); + writer.NewPropertyWithValue("JumpAngleRange", jetpackToSave->GetJetAngleRange()); + writer.NewPropertyWithValue("CanAdjustAngleWhileFiring", jetpackToSave->GetCanAdjustAngleWhileFiring()); + } - return true; -} + if (const Arm* armToSave = dynamic_cast(sceneObjectToSave)) { + WriteHardcodedAttachableOrNone("HeldDevice", armToSave->GetHeldDevice()); + } + if (const Leg* legToSave = dynamic_cast(sceneObjectToSave)) { + WriteHardcodedAttachableOrNone("Foot", legToSave->GetFoot()); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FillUnseenLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a new SceneLayer for a specific team and fills it with black -// pixels that end up being a specific size on the screen. - -void Scene::FillUnseenLayer(Vector pixelSize, int team, bool createNow) -{ - if (team == Activity::NoTeam || !(pixelSize.m_X >= 1.0 && pixelSize.m_Y >= 1.0)) - return; - - m_UnseenPixelSize[team] = pixelSize; - - // Dynamically create the unseen layer - // Create the bitmap to make the unseen scene layer out of - if (createNow) - { - BITMAP *pUnseenBitmap = create_bitmap_ex(8, GetWidth() / m_UnseenPixelSize[team].m_X, GetHeight() / m_UnseenPixelSize[team].m_Y); - clear_to_color(pUnseenBitmap, g_BlackColor); - // Replace any old unseen layer with the new one that is generated - delete m_apUnseenLayer[team]; - m_apUnseenLayer[team] = new SceneLayer(); - m_apUnseenLayer[team]->Create(pUnseenBitmap, true, Vector(), WrapsX(), WrapsY(), Vector(1.0, 1.0)); - // Calculate how many times smaller the unseen map is compared to the entire terrain's dimensions, and set it as the scale factor on the Unseen layer - m_apUnseenLayer[team]->SetScaleFactor(Vector((float)GetTerrain()->GetBitmap()->w / (float)m_apUnseenLayer[team]->GetBitmap()->w, (float)GetTerrain()->GetBitmap()->h / (float)m_apUnseenLayer[team]->GetBitmap()->h)); - } -} + if (const Turret* turretToSave = dynamic_cast(sceneObjectToSave)) { + for (const HeldDevice* heldDeviceToSave: turretToSave->GetMountedDevices()) { + WriteHardcodedAttachableOrNone("AddMountedDevice", heldDeviceToSave); + } + } + if (const HeldDevice* heldDeviceToSave = dynamic_cast(sceneObjectToSave)) { + writer.NewPropertyWithValue("SpecialBehaviour_Activated", heldDeviceToSave->IsActivated()); + writer.NewPropertyWithValue("SpecialBehaviour_ActivationTimerElapsedSimTimeMS", heldDeviceToSave->GetActivationTimer().GetElapsedSimTimeMS()); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetUnseenLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the unseen layer of a specific team. + if (const HDFirearm* hdFirearmToSave = dynamic_cast(sceneObjectToSave)) { + WriteHardcodedAttachableOrNone("Magazine", hdFirearmToSave->GetMagazine()); + WriteHardcodedAttachableOrNone("Flash", hdFirearmToSave->GetFlash()); + } -void Scene::SetUnseenLayer(SceneLayer *pNewLayer, int team) -{ - if (team == Activity::NoTeam || !pNewLayer) - return; + if (const Magazine* magazineToSave = dynamic_cast(sceneObjectToSave)) { + writer.NewPropertyWithValue("RoundCount", magazineToSave->GetRoundCount()); + } + } - // Replace any old unseen layer with the new one that is generated - delete m_apUnseenLayer[team]; - m_apUnseenLayer[team] = pNewLayer; - // Calculate how many times smaller the unseen map is compared to the entire terrain's dimensions, and set it as the scale factor on the Unseen layer - m_apUnseenLayer[team]->SetScaleFactor(Vector((float)GetTerrain()->GetBitmap()->w / (float)m_apUnseenLayer[team]->GetBitmap()->w, (float)GetTerrain()->GetBitmap()->h / (float)m_apUnseenLayer[team]->GetBitmap()->h)); -} + if (const Actor* actorToSave = dynamic_cast(sceneObjectToSave)) { + writer.NewPropertyWithValue("Health", actorToSave->GetHealth()); + writer.NewPropertyWithValue("MaxHealth", actorToSave->GetMaxHealth()); + if (saveFullData) { + writer.NewPropertyWithValue("Status", actorToSave->GetStatus()); + writer.NewPropertyWithValue("PlayerControllable", actorToSave->IsPlayerControllable()); + int aiModeToSave = actorToSave->GetAIMode() == Actor::AIMode::AIMODE_SQUAD ? Actor::AIMode::AIMODE_GOTO : actorToSave->GetAIMode(); + if (aiModeToSave == Actor::AIMode::AIMODE_GOTO && (!actorToSave->GetMOMoveTarget() && g_SceneMan.ShortestDistance(actorToSave->GetMovePathEnd(), actorToSave->GetPos(), g_SceneMan.SceneWrapsX()).MagnitudeIsLessThan(1.0F))) { + aiModeToSave = Actor::AIMode::AIMODE_SENTRY; + } + writer.NewPropertyWithValue("AIMode", aiModeToSave); + if (aiModeToSave == Actor::AIMode::AIMODE_GOTO) { + const std::string addWaypointPropertyName = "SpecialBehaviour_AddAISceneWaypoint"; + if (const MovableObject* actorToSaveMOMoveTarget = actorToSave->GetMOMoveTarget()) { + writer.NewPropertyWithValue(addWaypointPropertyName, actorToSaveMOMoveTarget->GetPos()); + } else { + writer.NewPropertyWithValue(addWaypointPropertyName, actorToSave->GetMovePathEnd()); + for (auto& [waypointPosition, waypointObject]: actorToSave->GetWaypointList()) { + writer.NewPropertyWithValue(addWaypointPropertyName, waypointPosition); + } + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearSeenPixels -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the pixels that have been seen on a team's unseen layer. - -void Scene::ClearSeenPixels(int team) -{ - if (team != Activity::NoTeam) - { - // Clear all the pixels off the map, set them to key color - if (m_apUnseenLayer[team]) - { - for (std::list::iterator itr = m_SeenPixels[team].begin(); itr != m_SeenPixels[team].end(); ++itr) - { - putpixel(m_apUnseenLayer[team]->GetBitmap(), (*itr).m_X, (*itr).m_Y, g_MaskColor); - - // Clean up around the removed pixels too - CleanOrphanPixel((*itr).m_X + 1, (*itr).m_Y, W, team); - CleanOrphanPixel((*itr).m_X - 1, (*itr).m_Y, E, team); - CleanOrphanPixel((*itr).m_X, (*itr).m_Y + 1, N, team); - CleanOrphanPixel((*itr).m_X, (*itr).m_Y - 1, S, team); - CleanOrphanPixel((*itr).m_X + 1, (*itr).m_Y + 1, NW, team); - CleanOrphanPixel((*itr).m_X - 1, (*itr).m_Y + 1, NE, team); - CleanOrphanPixel((*itr).m_X - 1, (*itr).m_Y - 1, SE, team); - CleanOrphanPixel((*itr).m_X + 1, (*itr).m_Y - 1, SW, team); - } - } - - // Now actually clear the list too - m_SeenPixels[team].clear(); - - // Transfer all cleaned pixels from orphans to the seen pixels for next frame - for (std::list::iterator itr = m_CleanedPixels[team].begin(); itr != m_CleanedPixels[team].end(); ++itr) - m_SeenPixels[team].push_back(*itr); - - // We have moved the cleaned pixels to the seen pixels list, now clean up the list for next frame - m_CleanedPixels[team].clear(); - } -} + if (actorToSave->GetDeploymentID()) { + writer.NewPropertyWithValue("DeploymentID", actorToSave->GetDeploymentID()); + } + for (const MovableObject* inventoryItem: *actorToSave->GetInventory()) { + writer.NewProperty("AddInventory"); + SaveSceneObject(writer, inventoryItem, true, saveFullData); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CleanOrphanPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks a specific unseen pixel for only having two or less unseen -// neighbors, and if so, makes it seen. - -bool Scene::CleanOrphanPixel(int posX, int posY, NeighborDirection checkingFrom, int team) -{ - if (team == Activity::NoTeam || !m_apUnseenLayer[team]) - return false; - - // Do any necessary wrapping - m_apUnseenLayer[team]->WrapPosition(posX, posY); - - // First check the actual position of the checked pixel, it may already been seen. - if (getpixel(m_apUnseenLayer[team]->GetBitmap(), posX, posY) == g_MaskColor) - return false; - - // Ok, not seen, so check surrounding pixels for 'support', ie unseen ones that will keep this also unseen - float support = 0; - int testPosX, testPosY; - if (checkingFrom != E) - { - testPosX = posX + 1; - testPosY = posY; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); - support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; - } - if (checkingFrom != W) - { - testPosX = posX - 1; - testPosY = posY; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); - support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; - } - if (checkingFrom != S) - { - testPosX = posX; - testPosY = posY + 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); - support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; - } - if (checkingFrom != N) - { - testPosX = posX; - testPosY = posY - 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); - support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; - } - if (checkingFrom != SE) - { - testPosX = posX + 1; - testPosY = posY + 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); - support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; - } - if (checkingFrom != SW) - { - testPosX = posX - 1; - testPosY = posY + 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); - support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; - } - if (checkingFrom != NW) - { - testPosX = posX - 1; - testPosY = posY - 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); - support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; - } - if (checkingFrom != NE) - { - testPosX = posX + 1; - testPosY = posY - 1; - m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); - support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; - } - - // Orphaned enough to remove? - if (support <= 2.5) - { - putpixel(m_apUnseenLayer[team]->GetBitmap(), posX, posY, g_MaskColor); - m_CleanedPixels[team].push_back(Vector(posX, posY)); - return true; - } - - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Vector Scene::GetDimensions() const { - if (m_pTerrain) { - if (const BITMAP *terrainBitmap = m_pTerrain->GetBitmap()) { - return Vector(static_cast(terrainBitmap->w), static_cast(terrainBitmap->h)); - } - return Vector(static_cast(m_pTerrain->GetWidth()), static_cast(m_pTerrain->GetHeight())); - } - return Vector(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int Scene::GetWidth() const { - if (m_pTerrain) { - if (const BITMAP *terrainBitmap = m_pTerrain->GetBitmap()) { - return terrainBitmap->w; - } - return m_pTerrain->GetWidth(); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int Scene::GetHeight() const { - if (m_pTerrain) { - if (const BITMAP *terrainBitmap = m_pTerrain->GetBitmap()) { - return terrainBitmap->h; - } - return m_pTerrain->GetHeight(); - } - return 0; -} + if (saveFullData) { + if (const ADoor* aDoorToSave = dynamic_cast(sceneObjectToSave)) { + WriteHardcodedAttachableOrNone("Door", aDoorToSave->GetDoor()); + } else if (const AHuman* aHumanToSave = dynamic_cast(sceneObjectToSave)) { + WriteHardcodedAttachableOrNone("Head", aHumanToSave->GetHead()); + WriteHardcodedAttachableOrNone("Jetpack", aHumanToSave->GetJetpack()); + WriteHardcodedAttachableOrNone("FGArm", aHumanToSave->GetFGArm()); + WriteHardcodedAttachableOrNone("BGArm", aHumanToSave->GetBGArm()); + WriteHardcodedAttachableOrNone("FGLeg", aHumanToSave->GetFGLeg()); + WriteHardcodedAttachableOrNone("BGLeg", aHumanToSave->GetBGLeg()); + } else if (const ACrab* aCrabToSave = dynamic_cast(sceneObjectToSave)) { + WriteHardcodedAttachableOrNone("Turret", aCrabToSave->GetTurret()); + WriteHardcodedAttachableOrNone("Jetpack", aCrabToSave->GetJetpack()); + WriteHardcodedAttachableOrNone("LeftFGLeg", aCrabToSave->GetLeftFGLeg()); + WriteHardcodedAttachableOrNone("LeftBGLeg", aCrabToSave->GetLeftBGLeg()); + WriteHardcodedAttachableOrNone("RightFGLeg", aCrabToSave->GetRightFGLeg()); + WriteHardcodedAttachableOrNone("RightBGLeg", aCrabToSave->GetRightBGLeg()); + } else if (const ACRocket* acRocketToSave = dynamic_cast(sceneObjectToSave)) { + WriteHardcodedAttachableOrNone("RightLeg", acRocketToSave->GetRightLeg()); + WriteHardcodedAttachableOrNone("LeftLeg", acRocketToSave->GetLeftLeg()); + WriteHardcodedAttachableOrNone("MainThruster", acRocketToSave->GetMainThruster()); + WriteHardcodedAttachableOrNone("RightThruster", acRocketToSave->GetRightThruster()); + WriteHardcodedAttachableOrNone("LeftThruster", acRocketToSave->GetLeftThruster()); + WriteHardcodedAttachableOrNone("UpRightThruster", acRocketToSave->GetURightThruster()); + WriteHardcodedAttachableOrNone("UpRightThruster", acRocketToSave->GetULeftThruster()); + } else if (const ACDropShip* acDropShipToSave = dynamic_cast(sceneObjectToSave)) { + WriteHardcodedAttachableOrNone("RightThruster", acDropShipToSave->GetRightThruster()); + WriteHardcodedAttachableOrNone("LeftThruster", acDropShipToSave->GetLeftThruster()); + WriteHardcodedAttachableOrNone("UpRightThruster", acDropShipToSave->GetURightThruster()); + WriteHardcodedAttachableOrNone("UpLeftThruster", acDropShipToSave->GetULeftThruster()); + WriteHardcodedAttachableOrNone("RightHatchDoor", acDropShipToSave->GetRightHatch()); + WriteHardcodedAttachableOrNone("LeftHatchDoor", acDropShipToSave->GetLeftHatch()); + } + } else if (const AHuman* aHumanToSave = dynamic_cast(sceneObjectToSave)) { + if (const HeldDevice* equippedItem = aHumanToSave->GetEquippedItem()) { + writer.NewProperty("AddInventory"); + SaveSceneObject(writer, equippedItem, true, saveFullData); + } + if (const HeldDevice* bgEquippedItem = aHumanToSave->GetEquippedBGItem()) { + writer.NewProperty("AddInventory"); + SaveSceneObject(writer, bgEquippedItem, true, saveFullData); + } + } + } + writer.ObjectEnd(); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapsX -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the scene wraps its scrolling around the X axis. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the Scene object. -bool Scene::WrapsX() const { return m_pTerrain ? m_pTerrain->WrapsX() : false; } + void Scene::Destroy(bool notInherited) { + delete m_pTerrain; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + delete m_ResidentBrains[player]; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapsY -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the scene wraps its scrolling around the Y axis. + for (int set = PLACEONLOAD; set < PLACEDSETSCOUNT; ++set) { + for (std::list::iterator oItr = m_PlacedObjects[set].begin(); oItr != m_PlacedObjects[set].end(); ++oItr) { + delete (*oItr); + *oItr = 0; + } + } -bool Scene::WrapsY() const { return m_pTerrain ? m_pTerrain->WrapsY() : false; } + for (std::list::iterator slItr = m_BackLayerList.begin(); slItr != m_BackLayerList.end(); ++slItr) { + delete (*slItr); + *slItr = 0; + } + for (std::list::iterator slItr = m_Deployments.begin(); slItr != m_Deployments.end(); ++slItr) { + delete (*slItr); + *slItr = 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PlaceResidentBrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Places the individual brain of a single player which may be stationed -// on this Scene, and registers them as such in an Activity. + delete m_apUnseenLayer[Activity::TeamOne]; + delete m_apUnseenLayer[Activity::TeamTwo]; + delete m_apUnseenLayer[Activity::TeamThree]; + delete m_apUnseenLayer[Activity::TeamFour]; -bool Scene::PlaceResidentBrain(int player, Activity &newActivity) -{ - if (m_ResidentBrains[player]) - { -#ifdef DEBUG_BUILD - RTEAssert(m_ResidentBrains[player]->GetTeam() == newActivity.GetTeamOfPlayer(player), "Resident Brain is of the wrong team!!"); -#endif + // if (m_PreviewBitmapOwned) + destroy_bitmap(m_pPreviewBitmap); + m_pPreviewBitmap = 0; - Actor *pBrainActor = dynamic_cast(m_ResidentBrains[player]); - if (pBrainActor)// && pBrainActor->IsActor()) - { - // Set the team before adding it to the MovableMan - pBrainActor->SetTeam(newActivity.GetTeamOfPlayer(player)); - // Passing in ownership of the brain here - g_MovableMan.AddActor(pBrainActor); - // Register it with the Activity too - newActivity.SetPlayerBrain(pBrainActor, player); - // Clear the resident brain slot.. it may be set again at the end of the game, if this fella survives - m_ResidentBrains[player] = 0; - return true; - } -// TODO: Handle brains being inside other things as residents?? - } - - return false; -} + if (!notInherited) + Entity::Destroy(); + Clear(); + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: MigrateToModule + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes this an original Preset in a different module than it was before. + // It severs ties deeply to the old module it was saved in. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PlaceResidentBrains -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Places the individual brains of the various players which may be -// stationed on this Scene, and registers them as such in an Activity. + bool Scene::MigrateToModule(int whichModule) { + if (!Entity::MigrateToModule(whichModule)) + return false; -int Scene::PlaceResidentBrains(Activity &newActivity) -{ - int found = 0; + m_pTerrain->MigrateToModule(whichModule); - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (PlaceResidentBrain(player, newActivity)) - ++found; - } + return true; + } - return found; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FillUnseenLayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a new SceneLayer for a specific team and fills it with black + // pixels that end up being a specific size on the screen. + + void Scene::FillUnseenLayer(Vector pixelSize, int team, bool createNow) { + if (team == Activity::NoTeam || !(pixelSize.m_X >= 1.0 && pixelSize.m_Y >= 1.0)) + return; + + m_UnseenPixelSize[team] = pixelSize; + + // Dynamically create the unseen layer + // Create the bitmap to make the unseen scene layer out of + if (createNow) { + BITMAP* pUnseenBitmap = create_bitmap_ex(8, GetWidth() / m_UnseenPixelSize[team].m_X, GetHeight() / m_UnseenPixelSize[team].m_Y); + clear_to_color(pUnseenBitmap, g_BlackColor); + // Replace any old unseen layer with the new one that is generated + delete m_apUnseenLayer[team]; + m_apUnseenLayer[team] = new SceneLayer(); + m_apUnseenLayer[team]->Create(pUnseenBitmap, true, Vector(), WrapsX(), WrapsY(), Vector(1.0, 1.0)); + // Calculate how many times smaller the unseen map is compared to the entire terrain's dimensions, and set it as the scale factor on the Unseen layer + m_apUnseenLayer[team]->SetScaleFactor(Vector((float)GetTerrain()->GetBitmap()->w / (float)m_apUnseenLayer[team]->GetBitmap()->w, (float)GetTerrain()->GetBitmap()->h / (float)m_apUnseenLayer[team]->GetBitmap()->h)); + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetUnseenLayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the unseen layer of a specific team. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RetrieveResidentBrains -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Looks at the Activity and its players' registered brain Actors, and -// saves them as resident brains for this Scene. Done when a fight is over -// and the survivors remain! + void Scene::SetUnseenLayer(SceneLayer* pNewLayer, int team) { + if (team == Activity::NoTeam || !pNewLayer) + return; -int Scene::RetrieveResidentBrains(Activity &oldActivity) -{ - int found = 0; + // Replace any old unseen layer with the new one that is generated + delete m_apUnseenLayer[team]; + m_apUnseenLayer[team] = pNewLayer; + // Calculate how many times smaller the unseen map is compared to the entire terrain's dimensions, and set it as the scale factor on the Unseen layer + m_apUnseenLayer[team]->SetScaleFactor(Vector((float)GetTerrain()->GetBitmap()->w / (float)m_apUnseenLayer[team]->GetBitmap()->w, (float)GetTerrain()->GetBitmap()->h / (float)m_apUnseenLayer[team]->GetBitmap()->h)); + } - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { -// RTEAssert(oldActivity.GetPlayerBrain(player) && oldActivity.GetPlayerBrain(player)->GetTeam() == oldActivity.GetTeamOfPlayer(player), "Resident Brain is of the wrong team BEFORE being retrieved!!"); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearSeenPixels + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the pixels that have been seen on a team's unseen layer. + + void Scene::ClearSeenPixels(int team) { + if (team != Activity::NoTeam) { + // Clear all the pixels off the map, set them to key color + if (m_apUnseenLayer[team]) { + for (std::list::iterator itr = m_SeenPixels[team].begin(); itr != m_SeenPixels[team].end(); ++itr) { + putpixel(m_apUnseenLayer[team]->GetBitmap(), (*itr).m_X, (*itr).m_Y, g_MaskColor); + + // Clean up around the removed pixels too + CleanOrphanPixel((*itr).m_X + 1, (*itr).m_Y, W, team); + CleanOrphanPixel((*itr).m_X - 1, (*itr).m_Y, E, team); + CleanOrphanPixel((*itr).m_X, (*itr).m_Y + 1, N, team); + CleanOrphanPixel((*itr).m_X, (*itr).m_Y - 1, S, team); + CleanOrphanPixel((*itr).m_X + 1, (*itr).m_Y + 1, NW, team); + CleanOrphanPixel((*itr).m_X - 1, (*itr).m_Y + 1, NE, team); + CleanOrphanPixel((*itr).m_X - 1, (*itr).m_Y - 1, SE, team); + CleanOrphanPixel((*itr).m_X + 1, (*itr).m_Y - 1, SW, team); + } + } - // Replace existing brain residencies - delete m_ResidentBrains[player]; - // Slurp up any brains and save em, transferring ownership when we release below - m_ResidentBrains[player] = oldActivity.GetPlayerBrain(player); - // Nullify the Activity brain - oldActivity.SetPlayerBrain(0, player); - // Try to find and remove the activity's brain actors in the MO pools, releasing ownership - if (g_MovableMan.RemoveActor(dynamic_cast(m_ResidentBrains[player]))) - ++found; - // If failed to find, then we didn't retrieve it - else - m_ResidentBrains[player] = 0; + // Now actually clear the list too + m_SeenPixels[team].clear(); -// RTEAssert(m_ResidentBrains[player] && m_ResidentBrains[player]->GetTeam() == oldActivity.GetTeamOfPlayer(player), "Resident Brain is of the wrong team AFTER being retrieved!!"); - } + // Transfer all cleaned pixels from orphans to the seen pixels for next frame + for (std::list::iterator itr = m_CleanedPixels[team].begin(); itr != m_CleanedPixels[team].end(); ++itr) + m_SeenPixels[team].push_back(*itr); - return found; -} + // We have moved the cleaned pixels to the seen pixels list, now clean up the list for next frame + m_CleanedPixels[team].clear(); + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CleanOrphanPixel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks a specific unseen pixel for only having two or less unseen + // neighbors, and if so, makes it seen. + + bool Scene::CleanOrphanPixel(int posX, int posY, NeighborDirection checkingFrom, int team) { + if (team == Activity::NoTeam || !m_apUnseenLayer[team]) + return false; + + // Do any necessary wrapping + m_apUnseenLayer[team]->WrapPosition(posX, posY); + + // First check the actual position of the checked pixel, it may already been seen. + if (getpixel(m_apUnseenLayer[team]->GetBitmap(), posX, posY) == g_MaskColor) + return false; + + // Ok, not seen, so check surrounding pixels for 'support', ie unseen ones that will keep this also unseen + float support = 0; + int testPosX, testPosY; + if (checkingFrom != E) { + testPosX = posX + 1; + testPosY = posY; + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); + support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; + } + if (checkingFrom != W) { + testPosX = posX - 1; + testPosY = posY; + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); + support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; + } + if (checkingFrom != S) { + testPosX = posX; + testPosY = posY + 1; + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); + support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; + } + if (checkingFrom != N) { + testPosX = posX; + testPosY = posY - 1; + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); + support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 1 : 0; + } + if (checkingFrom != SE) { + testPosX = posX + 1; + testPosY = posY + 1; + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); + support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; + } + if (checkingFrom != SW) { + testPosX = posX - 1; + testPosY = posY + 1; + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); + support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; + } + if (checkingFrom != NW) { + testPosX = posX - 1; + testPosY = posY - 1; + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); + support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; + } + if (checkingFrom != NE) { + testPosX = posX + 1; + testPosY = posY - 1; + m_apUnseenLayer[team]->WrapPosition(testPosX, testPosY); + support += getpixel(m_apUnseenLayer[team]->GetBitmap(), testPosX, testPosY) != g_MaskColor ? 0.5f : 0; + } -int Scene::RetrieveSceneObjects(bool transferOwnership, int onlyTeam, bool noBrains) { - int found = 0; + // Orphaned enough to remove? + if (support <= 2.5) { + putpixel(m_apUnseenLayer[team]->GetBitmap(), posX, posY, g_MaskColor); + m_CleanedPixels[team].push_back(Vector(posX, posY)); + return true; + } - found += g_MovableMan.GetAllActors(transferOwnership, m_PlacedObjects[PlacedObjectSets::PLACEONLOAD], onlyTeam, noBrains); - found += g_MovableMan.GetAllItems(transferOwnership, m_PlacedObjects[PlacedObjectSets::PLACEONLOAD]); - found += g_MovableMan.GetAllParticles(transferOwnership, m_PlacedObjects[PlacedObjectSets::PLACEONLOAD]); + return false; + } - return found; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Vector Scene::GetDimensions() const { + if (m_pTerrain) { + if (const BITMAP* terrainBitmap = m_pTerrain->GetBitmap()) { + return Vector(static_cast(terrainBitmap->w), static_cast(terrainBitmap->h)); + } + return Vector(static_cast(m_pTerrain->GetWidth()), static_cast(m_pTerrain->GetHeight())); + } + return Vector(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a SceneObject to be placed in this scene. Ownership IS transferred! + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void Scene::AddPlacedObject(int whichSet, SceneObject *pObjectToAdd, int listOrder) -{ - if (!pObjectToAdd) - return; + int Scene::GetWidth() const { + if (m_pTerrain) { + if (const BITMAP* terrainBitmap = m_pTerrain->GetBitmap()) { + return terrainBitmap->w; + } + return m_pTerrain->GetWidth(); + } + return 0; + } - // Create unique ID for this deployment - Deployment * pDeployment = dynamic_cast(pObjectToAdd); - if (pDeployment) - pDeployment->NewID(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (listOrder < 0 || listOrder >= m_PlacedObjects[whichSet].size()) - m_PlacedObjects[whichSet].push_back(pObjectToAdd); - else - { - // Find the spot - std::list::iterator itr = m_PlacedObjects[whichSet].begin(); - for (int i = 0; i != listOrder && itr != m_PlacedObjects[whichSet].end(); ++i, ++itr) - ; + int Scene::GetHeight() const { + if (m_pTerrain) { + if (const BITMAP* terrainBitmap = m_pTerrain->GetBitmap()) { + return terrainBitmap->h; + } + return m_pTerrain->GetHeight(); + } + return 0; + } - // Put 'er in - m_PlacedObjects[whichSet].insert(itr, pObjectToAdd); - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WrapsX + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the scene wraps its scrolling around the X axis. + bool Scene::WrapsX() const { return m_pTerrain ? m_pTerrain->WrapsX() : false; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemovePlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a SceneObject placed in this scene. - -void Scene::RemovePlacedObject(int whichSet, int whichToRemove) -{ - if (m_PlacedObjects[whichSet].empty()) - return; - - if (whichToRemove < 0 || whichToRemove >= m_PlacedObjects[whichSet].size()) - { - delete (m_PlacedObjects[whichSet].back()); - m_PlacedObjects[whichSet].pop_back(); - } - else - { - // Find the spot - std::list::iterator itr = m_PlacedObjects[whichSet].begin(); - for (int i = 0; i != whichToRemove && itr != m_PlacedObjects[whichSet].end(); ++i, ++itr) - ; - - delete (*itr); - m_PlacedObjects[whichSet].erase(itr); - } -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WrapsY + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the scene wraps its scrolling around the Y axis. + bool Scene::WrapsY() const { return m_pTerrain ? m_pTerrain->WrapsY() : false; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PickPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the last placed object that graphically overlaps an absolute -// point in the scene. - -const SceneObject * Scene::PickPlacedObject(int whichSet, Vector &scenePoint, int *pListOrderPlace) const -{ - // REVERSE! - int i = m_PlacedObjects[whichSet].size() - 1; - for (std::list::const_reverse_iterator itr = m_PlacedObjects[whichSet].rbegin(); itr != m_PlacedObjects[whichSet].rend(); ++itr, --i) - { - if ((*itr)->IsOnScenePoint(scenePoint)) - { - if (pListOrderPlace) - *pListOrderPlace = i; - return *itr; - } - } - - if (pListOrderPlace) - *pListOrderPlace = -1; - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PlaceResidentBrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Places the individual brain of a single player which may be stationed + // on this Scene, and registers them as such in an Activity. + bool Scene::PlaceResidentBrain(int player, Activity& newActivity) { + if (m_ResidentBrains[player]) { +#ifdef DEBUG_BUILD + RTEAssert(m_ResidentBrains[player]->GetTeam() == newActivity.GetTeamOfPlayer(player), "Resident Brain is of the wrong team!!"); +#endif -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PickPlacedActorInRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the last placed actor object that is closer than range to scenePoint -// -// Arguments: Which set of placed objects to pick from. See the PlacedObjectSets enum. -// The point in absolute scene coordinates that will be used to pick the -// closest placed SceneObject near it. -// The range to check for nearby objects. -// An int which will be filled out with the order place of any found object -// in the list. if nothing is found, it will get a value of -1. -// -// Return value: The closest actor SceneObject, if any. Ownership is NOT transferred! - -const SceneObject * Scene::PickPlacedActorInRange(int whichSet, Vector &scenePoint, int range, int *pListOrderPlace) const -{ - SceneObject * pFoundObject = 0; - float sqrDistance = static_cast(range * range); - - // REVERSE! - int i = m_PlacedObjects[whichSet].size() - 1; - for (std::list::const_reverse_iterator itr = m_PlacedObjects[whichSet].rbegin(); itr != m_PlacedObjects[whichSet].rend(); ++itr, --i) - { - if (dynamic_cast(*itr)) - { - float sqrCheckDistance = g_SceneMan.ShortestDistance((*itr)->GetPos(), scenePoint, true).GetSqrMagnitude(); - if (sqrCheckDistance < sqrDistance) + Actor* pBrainActor = dynamic_cast(m_ResidentBrains[player]); + if (pBrainActor) // && pBrainActor->IsActor()) { - if (pListOrderPlace) - *pListOrderPlace = i; - sqrDistance = sqrCheckDistance; - pFoundObject = *itr; + // Set the team before adding it to the MovableMan + pBrainActor->SetTeam(newActivity.GetTeamOfPlayer(player)); + // Passing in ownership of the brain here + g_MovableMan.AddActor(pBrainActor); + // Register it with the Activity too + newActivity.SetPlayerBrain(pBrainActor, player); + // Clear the resident brain slot.. it may be set again at the end of the game, if this fella survives + m_ResidentBrains[player] = 0; + return true; } + // TODO: Handle brains being inside other things as residents?? } - } - if (pFoundObject) - return pFoundObject; + return false; + } - if (pListOrderPlace) - *pListOrderPlace = -1; - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PlaceResidentBrains + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Places the individual brains of the various players which may be + // stationed on this Scene, and registers them as such in an Activity. + int Scene::PlaceResidentBrains(Activity& newActivity) { + int found = 0; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (PlaceResidentBrain(player, newActivity)) + ++found; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlacedObjects -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updated the objects in the placed scene objects list of this. This is -// mostly for the editor to represent the items correctly. + return found; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RetrieveResidentBrains + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Looks at the Activity and its players' registered brain Actors, and + // saves them as resident brains for this Scene. Done when a fight is over + // and the survivors remain! + + int Scene::RetrieveResidentBrains(Activity& oldActivity) { + int found = 0; + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + // RTEAssert(oldActivity.GetPlayerBrain(player) && oldActivity.GetPlayerBrain(player)->GetTeam() == oldActivity.GetTeamOfPlayer(player), "Resident Brain is of the wrong team BEFORE being retrieved!!"); + + // Replace existing brain residencies + delete m_ResidentBrains[player]; + // Slurp up any brains and save em, transferring ownership when we release below + m_ResidentBrains[player] = oldActivity.GetPlayerBrain(player); + // Nullify the Activity brain + oldActivity.SetPlayerBrain(0, player); + // Try to find and remove the activity's brain actors in the MO pools, releasing ownership + if (g_MovableMan.RemoveActor(dynamic_cast(m_ResidentBrains[player]))) + ++found; + // If failed to find, then we didn't retrieve it + else + m_ResidentBrains[player] = 0; + + // RTEAssert(m_ResidentBrains[player] && m_ResidentBrains[player]->GetTeam() == oldActivity.GetTeamOfPlayer(player), "Resident Brain is of the wrong team AFTER being retrieved!!"); + } -void Scene::UpdatePlacedObjects(int whichSet) -{ - if (whichSet == PLACEONLOAD) - { - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - if (m_ResidentBrains[player]) - m_ResidentBrains[player]->FullUpdate(); - } + return found; + } - for (std::list::iterator itr = m_PlacedObjects[whichSet].begin(); itr != m_PlacedObjects[whichSet].end(); ++itr) - { - (*itr)->FullUpdate(); - } -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int Scene::RetrieveSceneObjects(bool transferOwnership, int onlyTeam, bool noBrains) { + int found = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearPlacedObjectSet -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes all entries in a specific set of placed Objects. + found += g_MovableMan.GetAllActors(transferOwnership, m_PlacedObjects[PlacedObjectSets::PLACEONLOAD], onlyTeam, noBrains); + found += g_MovableMan.GetAllItems(transferOwnership, m_PlacedObjects[PlacedObjectSets::PLACEONLOAD]); + found += g_MovableMan.GetAllParticles(transferOwnership, m_PlacedObjects[PlacedObjectSets::PLACEONLOAD]); -int Scene::ClearPlacedObjectSet(int whichSet, bool weHaveOwnership) -{ - if (weHaveOwnership) { - for (std::list::iterator itr = m_PlacedObjects[whichSet].begin(); itr != m_PlacedObjects[whichSet].end(); ++itr) { - delete *itr; - } - } + return found; + } - int count = m_PlacedObjects[whichSet].size(); - m_PlacedObjects[whichSet].clear(); - return count; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddPlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a SceneObject to be placed in this scene. Ownership IS transferred! -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetResidentBrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the resident brain Actor of a specific player from this scene, -// if there is any. OWNERSHIP IS NOT TRANSFERRED! + void Scene::AddPlacedObject(int whichSet, SceneObject* pObjectToAdd, int listOrder) { + if (!pObjectToAdd) + return; -SceneObject * Scene::GetResidentBrain(int player) const -{ -// if (m_ResidentBrains[player]) - return m_ResidentBrains[player]; + // Create unique ID for this deployment + Deployment* pDeployment = dynamic_cast(pObjectToAdd); + if (pDeployment) + pDeployment->NewID(); -// for (std::list::iterator itr = m_PlacedObjects[PLACEONLOAD].begin(); itr != m_PlacedObjects[PLACEONLOAD].end(); ++itr) -// { -// (*itr)->teamUpdate(); -// } -} + if (listOrder < 0 || listOrder >= m_PlacedObjects[whichSet].size()) + m_PlacedObjects[whichSet].push_back(pObjectToAdd); + else { + // Find the spot + std::list::iterator itr = m_PlacedObjects[whichSet].begin(); + for (int i = 0; i != listOrder && itr != m_PlacedObjects[whichSet].end(); ++i, ++itr) + ; + // Put 'er in + m_PlacedObjects[whichSet].insert(itr, pObjectToAdd); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetResidentBrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the resident brain Actor of a specific player from this scene, -// if there is any. Ownership IS transferred! + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemovePlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a SceneObject placed in this scene. -void Scene::SetResidentBrain(int player, SceneObject *pNewBrain) -{ - if (MovableObject* asMo = dynamic_cast(m_ResidentBrains[player])) { - asMo->DestroyScriptState(); - } - delete m_ResidentBrains[player]; - m_ResidentBrains[player] = pNewBrain; -} + void Scene::RemovePlacedObject(int whichSet, int whichToRemove) { + if (m_PlacedObjects[whichSet].empty()) + return; + if (whichToRemove < 0 || whichToRemove >= m_PlacedObjects[whichSet].size()) { + delete (m_PlacedObjects[whichSet].back()); + m_PlacedObjects[whichSet].pop_back(); + } else { + // Find the spot + std::list::iterator itr = m_PlacedObjects[whichSet].begin(); + for (int i = 0; i != whichToRemove && itr != m_PlacedObjects[whichSet].end(); ++i, ++itr) + ; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetResidentBrainCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of brains currently residing in this scene. + delete (*itr); + m_PlacedObjects[whichSet].erase(itr); + } + } -int Scene::GetResidentBrainCount() const -{ - int count = 0; - for (int p = Players::PlayerOne; p < Players::MaxPlayerCount; ++p) - { - if (m_ResidentBrains[p]) - count++; - } - return count; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PickPlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the last placed object that graphically overlaps an absolute + // point in the scene. + + const SceneObject* Scene::PickPlacedObject(int whichSet, Vector& scenePoint, int* pListOrderPlace) const { + // REVERSE! + int i = m_PlacedObjects[whichSet].size() - 1; + for (std::list::const_reverse_iterator itr = m_PlacedObjects[whichSet].rbegin(); itr != m_PlacedObjects[whichSet].rend(); ++itr, --i) { + if ((*itr)->IsOnScenePoint(scenePoint)) { + if (pListOrderPlace) + *pListOrderPlace = i; + return *itr; + } + } + if (pListOrderPlace) + *pListOrderPlace = -1; + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds or modifies an existing area of this Scene. - -bool Scene::SetArea(Area &newArea) -{ - for (std::list::iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) - { - // Try to find an existing area of the same name - if ((*aItr).GetName() == newArea.GetName()) - { - // Deep copy into the existing area - (*aItr).Reset(); - (*aItr).Create(newArea); - return true; - } - } - // Couldn't find one, so just add the new Area - m_AreaList.push_back(newArea); - - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PickPlacedActorInRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the last placed actor object that is closer than range to scenePoint + // + // Arguments: Which set of placed objects to pick from. See the PlacedObjectSets enum. + // The point in absolute scene coordinates that will be used to pick the + // closest placed SceneObject near it. + // The range to check for nearby objects. + // An int which will be filled out with the order place of any found object + // in the list. if nothing is found, it will get a value of -1. + // + // Return value: The closest actor SceneObject, if any. Ownership is NOT transferred! + + const SceneObject* Scene::PickPlacedActorInRange(int whichSet, Vector& scenePoint, int range, int* pListOrderPlace) const { + SceneObject* pFoundObject = 0; + float sqrDistance = static_cast(range * range); + + // REVERSE! + int i = m_PlacedObjects[whichSet].size() - 1; + for (std::list::const_reverse_iterator itr = m_PlacedObjects[whichSet].rbegin(); itr != m_PlacedObjects[whichSet].rend(); ++itr, --i) { + if (dynamic_cast(*itr)) { + float sqrCheckDistance = g_SceneMan.ShortestDistance((*itr)->GetPos(), scenePoint, true).GetSqrMagnitude(); + if (sqrCheckDistance < sqrDistance) { + if (pListOrderPlace) + *pListOrderPlace = i; + sqrDistance = sqrCheckDistance; + pFoundObject = *itr; + } + } + } + if (pFoundObject) + return pFoundObject; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks for the existence of a specific Area identified by a name. -// This won't throw any errors to the console if the Area isn't found. + if (pListOrderPlace) + *pListOrderPlace = -1; + return 0; + } -bool Scene::HasArea(std::string areaName) -{ - for (std::list::iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) - { - if ((*aItr).GetName() == areaName) - return true; - } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePlacedObjects + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updated the objects in the placed scene objects list of this. This is + // mostly for the editor to represent the items correctly. + + void Scene::UpdatePlacedObjects(int whichSet) { + if (whichSet == PLACEONLOAD) { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) + if (m_ResidentBrains[player]) + m_ResidentBrains[player]->FullUpdate(); + } + for (std::list::iterator itr = m_PlacedObjects[whichSet].begin(); itr != m_PlacedObjects[whichSet].end(); ++itr) { + (*itr)->FullUpdate(); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific area box identified by a name. Ownership is NOT transferred! + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearPlacedObjectSet + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes all entries in a specific set of placed Objects. -Scene::Area * Scene::GetArea(const std::string_view &areaName, bool required) { - for (Scene::Area &area : m_AreaList) { - if (area.GetName() == areaName) { - return &area; + int Scene::ClearPlacedObjectSet(int whichSet, bool weHaveOwnership) { + if (weHaveOwnership) { + for (std::list::iterator itr = m_PlacedObjects[whichSet].begin(); itr != m_PlacedObjects[whichSet].end(); ++itr) { + delete *itr; + } } + + int count = m_PlacedObjects[whichSet].size(); + m_PlacedObjects[whichSet].clear(); + return count; } - if (required) { - g_ConsoleMan.PrintString("WARNING: Could not find the requested Scene Area named : " + std::string(areaName)); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetResidentBrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the resident brain Actor of a specific player from this scene, + // if there is any. OWNERSHIP IS NOT TRANSFERRED! - return nullptr; -} + SceneObject* Scene::GetResidentBrain(int player) const { + // if (m_ResidentBrains[player]) + return m_ResidentBrains[player]; + // for (std::list::iterator itr = m_PlacedObjects[PLACEONLOAD].begin(); itr != m_PlacedObjects[PLACEONLOAD].end(); ++itr) + // { + // (*itr)->teamUpdate(); + // } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specific Area identified by a name. - -bool Scene::RemoveArea(std::string areaName) -{ - for (std::list::iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) - { - if ((*aItr).GetName() == areaName) - { - m_AreaList.erase(aItr); - return true; - } - } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetResidentBrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the resident brain Actor of a specific player from this scene, + // if there is any. Ownership IS transferred! + void Scene::SetResidentBrain(int player, SceneObject* pNewBrain) { + if (MovableObject* asMo = dynamic_cast(m_ResidentBrains[player])) { + asMo->DestroyScriptState(); + } + delete m_ResidentBrains[player]; + m_ResidentBrains[player] = pNewBrain; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WithinArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if a point is within a specific named Area of this Scene. If -// no Area of the name is found, this just returns false without error. -// Arguments: The name of the Area to try to check against. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetResidentBrainCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of brains currently residing in this scene. -bool Scene::WithinArea(std::string areaName, const Vector &point) const -{ - if (areaName.empty()) - return false; + int Scene::GetResidentBrainCount() const { + int count = 0; + for (int p = Players::PlayerOne; p < Players::MaxPlayerCount; ++p) { + if (m_ResidentBrains[p]) + count++; + } + return count; + } - for (std::list::const_iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) - { - if ((*aItr).GetName() == areaName && (*aItr).IsInside(point)) - return true; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds or modifies an existing area of this Scene. + + bool Scene::SetArea(Area& newArea) { + for (std::list::iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) { + // Try to find an existing area of the same name + if ((*aItr).GetName() == newArea.GetName()) { + // Deep copy into the existing area + (*aItr).Reset(); + (*aItr).Create(newArea); + return true; + } + } + // Couldn't find one, so just add the new Area + m_AreaList.push_back(newArea); - return false; -} + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks for the existence of a specific Area identified by a name. + // This won't throw any errors to the console if the Area isn't found. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetTeamOwnership -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the team who owns this Scene in a Metagame + bool Scene::HasArea(std::string areaName) { + for (std::list::iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) { + if ((*aItr).GetName() == areaName) + return true; + } + return false; + } -void Scene::SetTeamOwnership(int newTeam) -{ - m_OwnedByTeam = newTeam; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a specific area box identified by a name. Ownership is NOT transferred! - // Go through all the things placed and make sure they are all set to the new owner team - for (int set = PLACEONLOAD; set <= AIPLAN; ++set) - { - for (std::list::const_iterator bpItr = m_PlacedObjects[set].begin(); bpItr != m_PlacedObjects[set].end(); ++bpItr) - { - if (*bpItr) - (*bpItr)->SetTeam(m_OwnedByTeam); - } - } -} + Scene::Area* Scene::GetArea(const std::string_view& areaName, bool required) { + for (Scene::Area& area: m_AreaList) { + if (area.GetName() == areaName) { + return &area; + } + } + if (required) { + g_ConsoleMan.PrintString("WARNING: Could not find the requested Scene Area named : " + std::string(areaName)); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalcBuildBudgetUse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Figure out exactly how much of the build budget would be used if -// as many blueprint objects as can be afforded and exists would be built. - -float Scene::CalcBuildBudgetUse(int player, int *pAffordCount, int *pAffordAIPlanCount) const -{ - if (pAffordCount) - *pAffordCount = 0; - - if (player < Players::PlayerOne || player >= Players::MaxPlayerCount) - return 0; - - // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn - int nativeModule = 0; - float foreignCostMult = 1.0; - float nativeCostMult = 1.0; - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - if (g_MetaMan.GameInProgress() && pMetaPlayer) - { - nativeModule = pMetaPlayer->GetNativeTechModule(); - foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); - nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); - } - - // Go through the list of blueprint objects and move as many as can be afforded by the budget to the list that is placed on next scene load - int objCount = 0; - float fundsAfforded = 0; - float budget = m_BuildBudget[player]; - float objectCost = 0; - Deployment *pDeployment = 0; - SceneObject *pObjectToPlace = 0; - // The last resident brain that is encountered in the building list, starting with the preexisting resident brain. Not owned here - SceneObject *pLastBrain = m_ResidentBrains[player]; - // The total list of objects that WILL be placed as the building phase goes on for real - nothing is owned by this! - std::list virtualPlacedList; - // Add all the already placed objects int he scene to it; then we'll add the objects that would be placed this round - ownership is NOT passed - for (std::list::const_iterator placedItr = m_PlacedObjects[PLACEONLOAD].begin(); placedItr != m_PlacedObjects[PLACEONLOAD].end(); ++placedItr) - virtualPlacedList.push_back(*placedItr); - - // First go through the blueprints that are already placed, THEN go through the AI plan objects if we are specified to - for (int set = BLUEPRINT; set <= AIPLAN; ++set) - { - // Skip the AI plan set if we're not asked to consider it - if (set == AIPLAN && !pAffordAIPlanCount) - continue; - - // Two passes, one for only things placed by this player, second to see if we can still afford any placed by teammates - for (int pass = 0; pass < 2; ++pass) - { - for (std::list::const_iterator bpItr = m_PlacedObjects[set].begin(); bpItr != m_PlacedObjects[set].end(); ++bpItr) - { - // Skip objects on the first pass that aren't placed by this player - // Skip objects on the second pass that WERE placed by this player.. because we already counted them - if ((pass == 0 && (*bpItr)->GetPlacedByPlayer() != player) || - (pass == 1 && (*bpItr)->GetPlacedByPlayer() == player)) - continue; - - // If Deployment, we need to check if we're going to be spawning something this building round or not - pDeployment = dynamic_cast(*bpItr); - if (pDeployment) - { - // Reset the object cost because the creating of the Deployment spawn only adds to the passed-in tally - objectCost = 0; - // See if we can spawn an Actor from this Deployment - pObjectToPlace = pDeployment->CreateDeployedActor(player, objectCost); - // If not an Actor, at least an item? - if (!pObjectToPlace) - pObjectToPlace = pDeployment->CreateDeployedObject(player, objectCost); - - // Only place things if there isn't somehting similar of the same team within a radius - if (pObjectToPlace) - { - // If there's already similar placed in the scene that is close enough to this Deployment to block it, then abort placing the new spawn! - // We're passing in the virtual list of things that already were placed before, and that have been placed up til now in this build phase - if (pDeployment->DeploymentBlocked(player, virtualPlacedList)) - { - delete pObjectToPlace; - pObjectToPlace = 0; - } - } - - // If we didn't end up spawning anything, just continue to the next thing in the blueprint queue - if (!pObjectToPlace) - continue; - } - // Regular object, will always be bought - else - { - pObjectToPlace = *bpItr; - if (pObjectToPlace) - objectCost = pObjectToPlace->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); - } - - // If this is a brain, then we will replace any previous/existing resident brain with this one, and adjust the difference in cost - if (pObjectToPlace && pObjectToPlace->IsInGroup("Brains") && pLastBrain) - { - objectCost = pObjectToPlace->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult) - pLastBrain->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); - pLastBrain = pObjectToPlace; - } - - // Check if the remaining budget allows for this item - if (budget >= objectCost) - { - // Add it to the virtual list of things we're comparing against for spawn blockages - ownership is NOT passed - virtualPlacedList.push_back(pObjectToPlace); - fundsAfforded += objectCost; - budget -= objectCost; - objCount++; - // Count the number of AI Plan objects we can afford, if we're asked to do so - if (set == AIPLAN && pAffordAIPlanCount) - (*pAffordAIPlanCount)++; - } - // That's it, we can't afford the next item in the queue - else - break; - } - } - } - - // Report the number of objects actually built - if (pAffordCount) - *pAffordCount = objCount; - - return fundsAfforded; -} + return nullptr; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a specific Area identified by a name. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyAIPlan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts the pre-built AI base plan into effect by transferring as many -// pieces as the current base budget allows from the AI plan to the actual -// blueprints to be built at this Scene. - -float Scene::ApplyAIPlan(int player, int *pObjectsApplied) -{ - if (pObjectsApplied) - *pObjectsApplied = 0; - - if (player < Players::PlayerOne || player >= Players::MaxPlayerCount) - return 0; - - // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn - int nativeModule = 0; - float foreignCostMult = 1.0; - float nativeCostMult = 1.0; - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - if (g_MetaMan.GameInProgress() && pMetaPlayer) - { - nativeModule = pMetaPlayer->GetNativeTechModule(); - foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); - nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); - } - - float valueOfApplied = 0; - int totalToBuildCount = 0; - int affordAIPlanObjectsCount = 0; - // Figure out how many objects in the bluprints we can afford to build already, without adding AI plans - float bpTotalValue = CalcBuildBudgetUse(player, &totalToBuildCount, &affordAIPlanObjectsCount); - - // If we have budget enough to build everything we have in blueprints already, AND dip into AI plans, - // then we should move over as many Objects from the AI plan queue to the blueprint as we have been told we can afford - float objValue = 0; - for (int i = 0; i < affordAIPlanObjectsCount && !m_PlacedObjects[AIPLAN].empty(); ++i) - { - // Get the object off the AI plan - SceneObject *pObject = m_PlacedObjects[AIPLAN].front(); - if (pObject) - { - m_PlacedObjects[AIPLAN].pop_front(); + bool Scene::RemoveArea(std::string areaName) { + for (std::list::iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) { + if ((*aItr).GetName() == areaName) { + m_AreaList.erase(aItr); + return true; + } + } + return false; + } - // How much does it cost? Add it to the total blueprint tally - objValue = pObject->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); - bpTotalValue += objValue; - valueOfApplied += objValue; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WithinArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if a point is within a specific named Area of this Scene. If + // no Area of the name is found, this just returns false without error. + // Arguments: The name of the Area to try to check against. - // Mark this as having been placed by this player - pObject->SetPlacedByPlayer(player); + bool Scene::WithinArea(std::string areaName, const Vector& point) const { + if (areaName.empty()) + return false; - // Create unique ID for this deployment - Deployment * pDeployment = dynamic_cast(pObject); - if (pDeployment) - pDeployment->NewID(); + for (std::list::const_iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) { + if ((*aItr).GetName() == areaName && (*aItr).IsInside(point)) + return true; + } + + return false; + } - // Now add it to the blueprint queue - m_PlacedObjects[BLUEPRINT].push_back(pObject); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetTeamOwnership + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the team who owns this Scene in a Metagame - // Count it - if (pObjectsApplied) - ++(*pObjectsApplied); + void Scene::SetTeamOwnership(int newTeam) { + m_OwnedByTeam = newTeam; + + // Go through all the things placed and make sure they are all set to the new owner team + for (int set = PLACEONLOAD; set <= AIPLAN; ++set) { + for (std::list::const_iterator bpItr = m_PlacedObjects[set].begin(); bpItr != m_PlacedObjects[set].end(); ++bpItr) { + if (*bpItr) + (*bpItr)->SetTeam(m_OwnedByTeam); + } } - } + } - return valueOfApplied; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalcBuildBudgetUse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Figure out exactly how much of the build budget would be used if + // as many blueprint objects as can be afforded and exists would be built. + + float Scene::CalcBuildBudgetUse(int player, int* pAffordCount, int* pAffordAIPlanCount) const { + if (pAffordCount) + *pAffordCount = 0; + + if (player < Players::PlayerOne || player >= Players::MaxPlayerCount) + return 0; + + // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn + int nativeModule = 0; + float foreignCostMult = 1.0; + float nativeCostMult = 1.0; + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + if (g_MetaMan.GameInProgress() && pMetaPlayer) { + nativeModule = pMetaPlayer->GetNativeTechModule(); + foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); + nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); + } + // Go through the list of blueprint objects and move as many as can be afforded by the budget to the list that is placed on next scene load + int objCount = 0; + float fundsAfforded = 0; + float budget = m_BuildBudget[player]; + float objectCost = 0; + Deployment* pDeployment = 0; + SceneObject* pObjectToPlace = 0; + // The last resident brain that is encountered in the building list, starting with the preexisting resident brain. Not owned here + SceneObject* pLastBrain = m_ResidentBrains[player]; + // The total list of objects that WILL be placed as the building phase goes on for real - nothing is owned by this! + std::list virtualPlacedList; + // Add all the already placed objects int he scene to it; then we'll add the objects that would be placed this round - ownership is NOT passed + for (std::list::const_iterator placedItr = m_PlacedObjects[PLACEONLOAD].begin(); placedItr != m_PlacedObjects[PLACEONLOAD].end(); ++placedItr) + virtualPlacedList.push_back(*placedItr); + + // First go through the blueprints that are already placed, THEN go through the AI plan objects if we are specified to + for (int set = BLUEPRINT; set <= AIPLAN; ++set) { + // Skip the AI plan set if we're not asked to consider it + if (set == AIPLAN && !pAffordAIPlanCount) + continue; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyBuildBudget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually builds as many objects in the specific player's Blueprint -// list as can be afforded by his build budget. The budget is deducted -// accordingly. - -float Scene::ApplyBuildBudget(int player, int *pObjectsBuilt) -{ - if (pObjectsBuilt) - *pObjectsBuilt = 0; - - if (player < Players::PlayerOne || player >= Players::MaxPlayerCount) - return 0; - - // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn - int nativeModule = 0; - float foreignCostMult = 1.0; - float nativeCostMult = 1.0; - int team = Activity::NoTeam; - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - if (g_MetaMan.GameInProgress() && pMetaPlayer) - { - nativeModule = pMetaPlayer->GetNativeTechModule(); - foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); - nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); - - // Also find out the team so we can apply it to the things we are building - team = pMetaPlayer->GetTeam(); - } - - // Go through the list of blueprint objects and move as many as can be afforded by the budget to the list that is placed on next scene load - bool remove = false; - int placedCount = 0; - float fundsSpent = 0; - float objectCost = 0; - std::list::iterator bpItr; - std::list::iterator delItr; - SceneObject *pObjectToPlace = 0; - Deployment *pDeployment = 0; - // The last resident brain that is encountered in the building list, starting with the preexisting resident brain. Not owned here - SceneObject *pLastBrain = m_ResidentBrains[player]; - // Two passes, one for only things placed by this player, second to see if we can still afford any placed by teammates - for (int pass = 0; pass < 2; ++pass) - { - for (bpItr = m_PlacedObjects[BLUEPRINT].begin(); bpItr != m_PlacedObjects[BLUEPRINT].end();) - { - // Skip objects on the first pass that aren't placed by this player - // Skip objects on the second pass that WERE placed by this player.. because we already went through them on first pass - if ((pass == 0 && (*bpItr)->GetPlacedByPlayer() != player) || - (pass == 1 && (*bpItr)->GetPlacedByPlayer() == player)) - { - // Increment since the for loop header doesn't do it - ++bpItr; - continue; - } - - // If Deployment, just add the new instances of whatever we're spawning - // and LEAVE the blueprint Deployment where it is in the queue, so it can keep spitting out new stuff each buying round - pDeployment = dynamic_cast(*bpItr); - if (pDeployment) - { - // Set the team - pDeployment->SetTeam(team); - // If there's already similar placed in the scene that is close enough to this Deployment to block it, then don't count it! - if (pDeployment->DeploymentBlocked(player, m_PlacedObjects[PLACEONLOAD])) - { - ++bpItr; - continue; - } - // Okay there's a valid deployment happening here, so count how much it costs - else - { - // Reset the object cost because the creating of the Deployment spawn only adds to the passed-in tally - objectCost = 0; - // See if we can spawn an Actor from this Deployment - pObjectToPlace = pDeployment->CreateDeployedActor(player, objectCost); - - // Assign deployment ID to placed actor - if (pObjectToPlace) - { - if (!pDeployment->GetID()) - pDeployment->NewID(); + // Two passes, one for only things placed by this player, second to see if we can still afford any placed by teammates + for (int pass = 0; pass < 2; ++pass) { + for (std::list::const_iterator bpItr = m_PlacedObjects[set].begin(); bpItr != m_PlacedObjects[set].end(); ++bpItr) { + // Skip objects on the first pass that aren't placed by this player + // Skip objects on the second pass that WERE placed by this player.. because we already counted them + if ((pass == 0 && (*bpItr)->GetPlacedByPlayer() != player) || + (pass == 1 && (*bpItr)->GetPlacedByPlayer() == player)) + continue; + + // If Deployment, we need to check if we're going to be spawning something this building round or not + pDeployment = dynamic_cast(*bpItr); + if (pDeployment) { + // Reset the object cost because the creating of the Deployment spawn only adds to the passed-in tally + objectCost = 0; + // See if we can spawn an Actor from this Deployment + pObjectToPlace = pDeployment->CreateDeployedActor(player, objectCost); + // If not an Actor, at least an item? + if (!pObjectToPlace) + pObjectToPlace = pDeployment->CreateDeployedObject(player, objectCost); + + // Only place things if there isn't somehting similar of the same team within a radius + if (pObjectToPlace) { + // If there's already similar placed in the scene that is close enough to this Deployment to block it, then abort placing the new spawn! + // We're passing in the virtual list of things that already were placed before, and that have been placed up til now in this build phase + if (pDeployment->DeploymentBlocked(player, virtualPlacedList)) { + delete pObjectToPlace; + pObjectToPlace = 0; + } + } + + // If we didn't end up spawning anything, just continue to the next thing in the blueprint queue + if (!pObjectToPlace) + continue; + } + // Regular object, will always be bought + else { + pObjectToPlace = *bpItr; + if (pObjectToPlace) + objectCost = pObjectToPlace->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); + } + + // If this is a brain, then we will replace any previous/existing resident brain with this one, and adjust the difference in cost + if (pObjectToPlace && pObjectToPlace->IsInGroup("Brains") && pLastBrain) { + objectCost = pObjectToPlace->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult) - pLastBrain->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); + pLastBrain = pObjectToPlace; + } + + // Check if the remaining budget allows for this item + if (budget >= objectCost) { + // Add it to the virtual list of things we're comparing against for spawn blockages - ownership is NOT passed + virtualPlacedList.push_back(pObjectToPlace); + fundsAfforded += objectCost; + budget -= objectCost; + objCount++; + // Count the number of AI Plan objects we can afford, if we're asked to do so + if (set == AIPLAN && pAffordAIPlanCount) + (*pAffordAIPlanCount)++; + } + // That's it, we can't afford the next item in the queue + else + break; + } + } + } + + // Report the number of objects actually built + if (pAffordCount) + *pAffordCount = objCount; + + return fundsAfforded; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyAIPlan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Puts the pre-built AI base plan into effect by transferring as many + // pieces as the current base budget allows from the AI plan to the actual + // blueprints to be built at this Scene. + + float Scene::ApplyAIPlan(int player, int* pObjectsApplied) { + if (pObjectsApplied) + *pObjectsApplied = 0; + + if (player < Players::PlayerOne || player >= Players::MaxPlayerCount) + return 0; + + // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn + int nativeModule = 0; + float foreignCostMult = 1.0; + float nativeCostMult = 1.0; + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + if (g_MetaMan.GameInProgress() && pMetaPlayer) { + nativeModule = pMetaPlayer->GetNativeTechModule(); + foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); + nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); + } + + float valueOfApplied = 0; + int totalToBuildCount = 0; + int affordAIPlanObjectsCount = 0; + // Figure out how many objects in the bluprints we can afford to build already, without adding AI plans + float bpTotalValue = CalcBuildBudgetUse(player, &totalToBuildCount, &affordAIPlanObjectsCount); + + // If we have budget enough to build everything we have in blueprints already, AND dip into AI plans, + // then we should move over as many Objects from the AI plan queue to the blueprint as we have been told we can afford + float objValue = 0; + for (int i = 0; i < affordAIPlanObjectsCount && !m_PlacedObjects[AIPLAN].empty(); ++i) { + // Get the object off the AI plan + SceneObject* pObject = m_PlacedObjects[AIPLAN].front(); + if (pObject) { + m_PlacedObjects[AIPLAN].pop_front(); + + // How much does it cost? Add it to the total blueprint tally + objValue = pObject->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); + bpTotalValue += objValue; + valueOfApplied += objValue; + + // Mark this as having been placed by this player + pObject->SetPlacedByPlayer(player); + + // Create unique ID for this deployment + Deployment* pDeployment = dynamic_cast(pObject); + if (pDeployment) + pDeployment->NewID(); + + // Now add it to the blueprint queue + m_PlacedObjects[BLUEPRINT].push_back(pObject); + + // Count it + if (pObjectsApplied) + ++(*pObjectsApplied); + } + } - Actor *pActor = dynamic_cast(pObjectToPlace); - if (pActor) - pActor->SetDeploymentID(pDeployment->GetID()); + return valueOfApplied; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyBuildBudget + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Actually builds as many objects in the specific player's Blueprint + // list as can be afforded by his build budget. The budget is deducted + // accordingly. + + float Scene::ApplyBuildBudget(int player, int* pObjectsBuilt) { + if (pObjectsBuilt) + *pObjectsBuilt = 0; + + if (player < Players::PlayerOne || player >= Players::MaxPlayerCount) + return 0; + + // Take metaplayer tech modifiers into account when calculating costs of this Deployment spawn + int nativeModule = 0; + float foreignCostMult = 1.0; + float nativeCostMult = 1.0; + int team = Activity::NoTeam; + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + if (g_MetaMan.GameInProgress() && pMetaPlayer) { + nativeModule = pMetaPlayer->GetNativeTechModule(); + foreignCostMult = pMetaPlayer->GetForeignCostMultiplier(); + nativeCostMult = pMetaPlayer->GetNativeCostMultiplier(); + + // Also find out the team so we can apply it to the things we are building + team = pMetaPlayer->GetTeam(); + } + + // Go through the list of blueprint objects and move as many as can be afforded by the budget to the list that is placed on next scene load + bool remove = false; + int placedCount = 0; + float fundsSpent = 0; + float objectCost = 0; + std::list::iterator bpItr; + std::list::iterator delItr; + SceneObject* pObjectToPlace = 0; + Deployment* pDeployment = 0; + // The last resident brain that is encountered in the building list, starting with the preexisting resident brain. Not owned here + SceneObject* pLastBrain = m_ResidentBrains[player]; + // Two passes, one for only things placed by this player, second to see if we can still afford any placed by teammates + for (int pass = 0; pass < 2; ++pass) { + for (bpItr = m_PlacedObjects[BLUEPRINT].begin(); bpItr != m_PlacedObjects[BLUEPRINT].end();) { + // Skip objects on the first pass that aren't placed by this player + // Skip objects on the second pass that WERE placed by this player.. because we already went through them on first pass + if ((pass == 0 && (*bpItr)->GetPlacedByPlayer() != player) || + (pass == 1 && (*bpItr)->GetPlacedByPlayer() == player)) { + // Increment since the for loop header doesn't do it + ++bpItr; + continue; + } + + // If Deployment, just add the new instances of whatever we're spawning + // and LEAVE the blueprint Deployment where it is in the queue, so it can keep spitting out new stuff each buying round + pDeployment = dynamic_cast(*bpItr); + if (pDeployment) { + // Set the team + pDeployment->SetTeam(team); + // If there's already similar placed in the scene that is close enough to this Deployment to block it, then don't count it! + if (pDeployment->DeploymentBlocked(player, m_PlacedObjects[PLACEONLOAD])) { + ++bpItr; + continue; } + // Okay there's a valid deployment happening here, so count how much it costs + else { + // Reset the object cost because the creating of the Deployment spawn only adds to the passed-in tally + objectCost = 0; + // See if we can spawn an Actor from this Deployment + pObjectToPlace = pDeployment->CreateDeployedActor(player, objectCost); + + // Assign deployment ID to placed actor + if (pObjectToPlace) { + if (!pDeployment->GetID()) + pDeployment->NewID(); + + Actor* pActor = dynamic_cast(pObjectToPlace); + if (pActor) + pActor->SetDeploymentID(pDeployment->GetID()); + } - // If this is a BRAIN, replace the old resident brain with this new one - if (pObjectToPlace->IsInGroup("Brains")) - { - // Get a refund for the previous brain we are replacing - if (m_ResidentBrains[player]) - { - float refund = m_ResidentBrains[player]->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); - m_BuildBudget[player] += refund; - fundsSpent -= refund; - delete m_ResidentBrains[player]; - } - // Deduct the cost of the new brain from the budget and tally the total - m_BuildBudget[player] -= objectCost; - fundsSpent += objectCost; - // Transfer ownership of the brain to the scene.. we are done with this object now - m_ResidentBrains[player] = pObjectToPlace; - // We are done with this since it's a special brain placement; continue to next item - pObjectToPlace = 0; - } - else - { - // If not an Actor, at least an item? - if (!pObjectToPlace) - pObjectToPlace = pDeployment->CreateDeployedObject(player, objectCost); - } - } - } - // A regular blueprint object which only gets placed once and then removed from blueprints - else - { - pObjectToPlace = *bpItr; - if (pObjectToPlace) - // Get the cost here since a deployment spawn above already gets it - objectCost = pObjectToPlace->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); - } - - // If we didn't end up spawning anything, just continue to the next thing in the blueprint queue - if (!pObjectToPlace) - { - ++bpItr; - continue; - } - - // If this is a brain, then we will replace any previous/existing resident brain with this one, and adjust the difference in cost - if (pObjectToPlace && pObjectToPlace->IsInGroup("Brains") && pLastBrain) - { - objectCost = pObjectToPlace->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult) - pLastBrain->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); - pLastBrain = pObjectToPlace; - } - - // Check if the remaining budget allows for this thing to be placed - if (m_BuildBudget[player] >= objectCost) - { - // Deduct the cost from the budget and tally the total - m_BuildBudget[player] -= objectCost; - fundsSpent += objectCost; - // Set the team of the thing we're building - pObjectToPlace->SetTeam(team); - // Mark this as having been placed by this player - pObjectToPlace->SetPlacedByPlayer(player); - // If applicable, replace the old resident brain with this new one - if (pObjectToPlace->IsInGroup("Brains")) - { - // Get a refund for the previous brain we are replacing - if (m_ResidentBrains[player]) - { - float refund = m_ResidentBrains[player]->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); - m_BuildBudget[player] += refund; - fundsSpent -= refund; - delete m_ResidentBrains[player]; - // Don't count the 'replacing' of a brain as an item - } - // If there wasn't a brain before, then count this as a new placement - else - placedCount++; - m_ResidentBrains[player] = pObjectToPlace; - } - // Regular non-brain object; simply move it to the list of objects to place on next load, TRANSFERRING OWNERSHIP - else - { - m_PlacedObjects[PLACEONLOAD].push_back(pObjectToPlace); - // Always count regular objects placed - placedCount++; - - // Add placed object's locations as boxes to the specified 'MetaBase' area - TerrainObject * pTO = dynamic_cast(pObjectToPlace); - if (pTO) - { - if (HasArea(METABASE_AREA_NAME)) - { - Scene::Area * metaBase = GetArea(METABASE_AREA_NAME); - if (metaBase) - { - float x1 = pTO->GetPos().m_X + pTO->GetBitmapOffset().m_X; - float y1 = pTO->GetPos().m_Y + pTO->GetBitmapOffset().m_Y; - float x2 = x1 + pTO->GetBitmapWidth(); - float y2 = y1 + pTO->GetBitmapHeight(); - - metaBase->AddBox(Box(x1, y1, x2, y2)); + // If this is a BRAIN, replace the old resident brain with this new one + if (pObjectToPlace->IsInGroup("Brains")) { + // Get a refund for the previous brain we are replacing + if (m_ResidentBrains[player]) { + float refund = m_ResidentBrains[player]->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); + m_BuildBudget[player] += refund; + fundsSpent -= refund; + delete m_ResidentBrains[player]; } + // Deduct the cost of the new brain from the budget and tally the total + m_BuildBudget[player] -= objectCost; + fundsSpent += objectCost; + // Transfer ownership of the brain to the scene.. we are done with this object now + m_ResidentBrains[player] = pObjectToPlace; + // We are done with this since it's a special brain placement; continue to next item + pObjectToPlace = 0; + } else { + // If not an Actor, at least an item? + if (!pObjectToPlace) + pObjectToPlace = pDeployment->CreateDeployedObject(player, objectCost); } } - } - - // Save the iterator so we can remove its entry after we increment - delItr = bpItr; - // Don't remove Deployment blueprints; they remain in the blueprints and re-spawn their Loadouts each building round - remove = pDeployment ? false : true; - } - // Ok we can't afford any more stuff in the queue - else - break; - - // Increment to next placed object - ++bpItr; - // Remove the previous entry from the blueprint list if it was built and added to the PLACEONLOAD list - // DON'T remove any Deployment objects; they will re-spawn their Loadout things anew each building round! - if (remove) - m_PlacedObjects[BLUEPRINT].erase(delItr); - remove = false; - } - } - - // Traverse through all brain hideouts to always move the brain to the last available brain hideout no matter what - if (m_ResidentBrains[player] && dynamic_cast(m_ResidentBrains[player])) - for (bpItr = m_PlacedObjects[BLUEPRINT].begin(); bpItr != m_PlacedObjects[BLUEPRINT].end(); bpItr++) - { - pDeployment = dynamic_cast(*bpItr); - if (pDeployment) - { - // If this is a brain hideout, then move curent brain to new hideout or infantry brain deployment - if (pDeployment->GetPresetName() == "Brain Hideout" || pDeployment->GetPresetName() == "Infantry Brain") - { - // Get a refund for the previous brain we are replacing - if (m_ResidentBrains[player]) - { - m_ResidentBrains[player]->SetPos(pDeployment->GetPos()); + } + // A regular blueprint object which only gets placed once and then removed from blueprints + else { + pObjectToPlace = *bpItr; + if (pObjectToPlace) + // Get the cost here since a deployment spawn above already gets it + objectCost = pObjectToPlace->GetGoldValue(nativeModule, foreignCostMult, nativeCostMult); + } + + // If we didn't end up spawning anything, just continue to the next thing in the blueprint queue + if (!pObjectToPlace) { + ++bpItr; + continue; + } + + // If this is a brain, then we will replace any previous/existing resident brain with this one, and adjust the difference in cost + if (pObjectToPlace && pObjectToPlace->IsInGroup("Brains") && pLastBrain) { + objectCost = pObjectToPlace->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult) - pLastBrain->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); + pLastBrain = pObjectToPlace; + } + + // Check if the remaining budget allows for this thing to be placed + if (m_BuildBudget[player] >= objectCost) { + // Deduct the cost from the budget and tally the total + m_BuildBudget[player] -= objectCost; + fundsSpent += objectCost; + // Set the team of the thing we're building + pObjectToPlace->SetTeam(team); + // Mark this as having been placed by this player + pObjectToPlace->SetPlacedByPlayer(player); + // If applicable, replace the old resident brain with this new one + if (pObjectToPlace->IsInGroup("Brains")) { + // Get a refund for the previous brain we are replacing + if (m_ResidentBrains[player]) { + float refund = m_ResidentBrains[player]->GetTotalValue(nativeModule, foreignCostMult, nativeCostMult); + m_BuildBudget[player] += refund; + fundsSpent -= refund; + delete m_ResidentBrains[player]; + // Don't count the 'replacing' of a brain as an item + } + // If there wasn't a brain before, then count this as a new placement + else + placedCount++; + m_ResidentBrains[player] = pObjectToPlace; + } + // Regular non-brain object; simply move it to the list of objects to place on next load, TRANSFERRING OWNERSHIP + else { + m_PlacedObjects[PLACEONLOAD].push_back(pObjectToPlace); + // Always count regular objects placed + placedCount++; + + // Add placed object's locations as boxes to the specified 'MetaBase' area + TerrainObject* pTO = dynamic_cast(pObjectToPlace); + if (pTO) { + if (HasArea(METABASE_AREA_NAME)) { + Scene::Area* metaBase = GetArea(METABASE_AREA_NAME); + if (metaBase) { + float x1 = pTO->GetPos().m_X + pTO->GetBitmapOffset().m_X; + float y1 = pTO->GetPos().m_Y + pTO->GetBitmapOffset().m_Y; + float x2 = x1 + pTO->GetBitmapWidth(); + float y2 = y1 + pTO->GetBitmapHeight(); + + metaBase->AddBox(Box(x1, y1, x2, y2)); + } + } + } } + + // Save the iterator so we can remove its entry after we increment + delItr = bpItr; + // Don't remove Deployment blueprints; they remain in the blueprints and re-spawn their Loadouts each building round + remove = pDeployment ? false : true; } + // Ok we can't afford any more stuff in the queue + else + break; + + // Increment to next placed object + ++bpItr; + // Remove the previous entry from the blueprint list if it was built and added to the PLACEONLOAD list + // DON'T remove any Deployment objects; they will re-spawn their Loadout things anew each building round! + if (remove) + m_PlacedObjects[BLUEPRINT].erase(delItr); + remove = false; } } - // Report the number of objects actually built - if (pObjectsBuilt) - *pObjectsBuilt = placedCount; + // Traverse through all brain hideouts to always move the brain to the last available brain hideout no matter what + if (m_ResidentBrains[player] && dynamic_cast(m_ResidentBrains[player])) + for (bpItr = m_PlacedObjects[BLUEPRINT].begin(); bpItr != m_PlacedObjects[BLUEPRINT].end(); bpItr++) { + pDeployment = dynamic_cast(*bpItr); + if (pDeployment) { + // If this is a brain hideout, then move curent brain to new hideout or infantry brain deployment + if (pDeployment->GetPresetName() == "Brain Hideout" || pDeployment->GetPresetName() == "Infantry Brain") { + // Get a refund for the previous brain we are replacing + if (m_ResidentBrains[player]) { + m_ResidentBrains[player]->SetPos(pDeployment->GetPos()); + } + } + } + } - // Add the spent amount to the tally of total investments in this scene - m_TotalInvestment += fundsSpent; + // Report the number of objects actually built + if (pObjectsBuilt) + *pObjectsBuilt = placedCount; - return fundsSpent; -} + // Add the spent amount to the tally of total investments in this scene + m_TotalInvestment += fundsSpent; + return fundsSpent; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAllPlacedActors -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Remove all actors that are in the placed set of objects to load for -// this scene. All except for an optionally specified team, that is. - -int Scene::RemoveAllPlacedActors(int exceptTeam) -{ - int removedCount = 0; - - bool remove = false; - Actor *pActor = 0; - std::list::iterator soItr; - std::list::iterator delItr; - - // Scrub both blueprints and the stuff that is already bought and about to be placed on loading the scene - for (int set = PLACEONLOAD; set <= BLUEPRINT; ++set) - { - for (soItr = m_PlacedObjects[set].begin(); soItr != m_PlacedObjects[set].end();) - { - remove = false; - // Only look for actors of any team except the specified exception team - pActor = dynamic_cast(*soItr); - if (pActor && pActor->GetTeam() != exceptTeam) - { - // Mark for removal - remove = true; - delItr = soItr; - } - - // Increment to next placed object - ++soItr; - - // Remove the previous entry from the set list now after we incremented so our soItr can remain valid - if (remove) - { - // Properly destroy and remove the entry from the set list - delete (*delItr); - m_PlacedObjects[set].erase(delItr); - removedCount++; - } - } - } - - return removedCount; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveAllPlacedActors + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Remove all actors that are in the placed set of objects to load for + // this scene. All except for an optionally specified team, that is. + + int Scene::RemoveAllPlacedActors(int exceptTeam) { + int removedCount = 0; + + bool remove = false; + Actor* pActor = 0; + std::list::iterator soItr; + std::list::iterator delItr; + + // Scrub both blueprints and the stuff that is already bought and about to be placed on loading the scene + for (int set = PLACEONLOAD; set <= BLUEPRINT; ++set) { + for (soItr = m_PlacedObjects[set].begin(); soItr != m_PlacedObjects[set].end();) { + remove = false; + // Only look for actors of any team except the specified exception team + pActor = dynamic_cast(*soItr); + if (pActor && pActor->GetTeam() != exceptTeam) { + // Mark for removal + remove = true; + delItr = soItr; + } + // Increment to next placed object + ++soItr; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOwnerOfAllDoors -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the ownership of all doors placed in this scene to a specific team -// Arguments: The team to change the ownership to - -int Scene::SetOwnerOfAllDoors(int team, int player) -{ - int changedCount = 0; - - ADoor *pDoor = 0; - std::list::iterator soItr; - - // Affect both blueprints and the stuff that is already bought and about to be placed on loading the scene - for (int set = PLACEONLOAD; set <= BLUEPRINT; ++set) - { - for (soItr = m_PlacedObjects[set].begin(); soItr != m_PlacedObjects[set].end(); ++soItr) - { - // Only mess with doors - if (pDoor = dynamic_cast(*soItr)) - { - // Update team - pDoor->SetTeam(team); - // Update which player placed this - pDoor->SetPlacedByPlayer(player); - ++changedCount; - } - } - } - - return changedCount; -} + // Remove the previous entry from the set list now after we incremented so our soItr can remain valid + if (remove) { + // Properly destroy and remove the entry from the set list + delete (*delItr); + m_PlacedObjects[set].erase(delItr); + removedCount++; + } + } + } + return removedCount; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResetPathFinding -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Recalculates all of the pathfinding data. This is very expensive, so -// do very rarely! + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOwnerOfAllDoors + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the ownership of all doors placed in this scene to a specific team + // Arguments: The team to change the ownership to + + int Scene::SetOwnerOfAllDoors(int team, int player) { + int changedCount = 0; + + ADoor* pDoor = 0; + std::list::iterator soItr; + + // Affect both blueprints and the stuff that is already bought and about to be placed on loading the scene + for (int set = PLACEONLOAD; set <= BLUEPRINT; ++set) { + for (soItr = m_PlacedObjects[set].begin(); soItr != m_PlacedObjects[set].end(); ++soItr) { + // Only mess with doors + if (pDoor = dynamic_cast(*soItr)) { + // Update team + pDoor->SetTeam(team); + // Update which player placed this + pDoor->SetPlacedByPlayer(player); + ++changedCount; + } + } + } -void Scene::ResetPathFinding() { - GetPathFinder(Activity::Teams::NoTeam)->RecalculateAllCosts(); - for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { - g_MovableMan.OverrideMaterialDoors(true, team); - GetPathFinder(static_cast(team))->RecalculateAllCosts(); - g_MovableMan.OverrideMaterialDoors(false, team); + return changedCount; } -} -////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ResetPathFinding + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Recalculates all of the pathfinding data. This is very expensive, so + // do very rarely! + + void Scene::ResetPathFinding() { + GetPathFinder(Activity::Teams::NoTeam)->RecalculateAllCosts(); + for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { + g_MovableMan.OverrideMaterialDoors(true, team); + GetPathFinder(static_cast(team))->RecalculateAllCosts(); + g_MovableMan.OverrideMaterialDoors(false, team); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// -void Scene::BlockUntilAllPathingRequestsComplete() { - for (int team = Activity::Teams::NoTeam; team < Activity::Teams::MaxTeamCount; ++team) { - while (GetPathFinder(static_cast(team))->GetCurrentPathingRequests() != 0) {}; + void Scene::BlockUntilAllPathingRequestsComplete() { + for (int team = Activity::Teams::NoTeam; team < Activity::Teams::MaxTeamCount; ++team) { + while (GetPathFinder(static_cast(team))->GetCurrentPathingRequests() != 0) {}; + } } -} -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePathFinding -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Recalculates only the areas of the pathfinding data that have been -// marked as outdated. - -void Scene::UpdatePathFinding() -{ - ZoneScoped; - - constexpr int nodeUpdatesPerCall = 100; - constexpr int maxUnupdatedMaterialAreas = 1000; - - // If any pathing requests are active, don't update things yet, wait till they're finished - // TODO: this can indefinitely block updates if pathing requests are made every frame. Figure out a solution for this - // Either force-complete pathing requests occasionally, or delay starting new pathing requests if we've not updated in a while - for (int team = Activity::Teams::NoTeam; team < Activity::Teams::MaxTeamCount; ++team) { - if (GetPathFinder(static_cast(team))->GetCurrentPathingRequests() != 0) { - return; - }; - } - - int nodesToUpdate = nodeUpdatesPerCall / g_ActivityMan.GetActivity()->GetTeamCount(); - if (m_pTerrain->GetUpdatedMaterialAreas().size() > maxUnupdatedMaterialAreas) { - // Our list of boxes is getting too big and a bit out of hand, so clear everything. - nodesToUpdate = std::numeric_limits::max(); - } - - // Update our shared pathFinder - std::vector updatedNodes = GetPathFinder(Activity::Teams::NoTeam)->RecalculateAreaCosts(m_pTerrain->GetUpdatedMaterialAreas(), nodesToUpdate); - if (!updatedNodes.empty()) { - // Update each team's pathFinder - for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { - if (!g_ActivityMan.ActivityRunning() || !g_ActivityMan.GetActivity()->TeamActive(team)) { - continue; - } - - // Remove the material representation of all doors of this team so we can navigate through them (they'll open for us). - g_MovableMan.OverrideMaterialDoors(true, team); - - GetPathFinder(static_cast(team))->UpdateNodeList(updatedNodes); - - // Place back the material representation of all doors of this team so they are as we found them. - g_MovableMan.OverrideMaterialDoors(false, team); - } - } - - m_PartialPathUpdateTimer.Reset(); - m_PathfindingUpdated = true; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePathFinding + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Recalculates only the areas of the pathfinding data that have been + // marked as outdated. + + void Scene::UpdatePathFinding() { + ZoneScoped; + + constexpr int nodeUpdatesPerCall = 100; + constexpr int maxUnupdatedMaterialAreas = 1000; + + // If any pathing requests are active, don't update things yet, wait till they're finished + // TODO: this can indefinitely block updates if pathing requests are made every frame. Figure out a solution for this + // Either force-complete pathing requests occasionally, or delay starting new pathing requests if we've not updated in a while + for (int team = Activity::Teams::NoTeam; team < Activity::Teams::MaxTeamCount; ++team) { + if (GetPathFinder(static_cast(team))->GetCurrentPathingRequests() != 0) { + return; + }; + } + int nodesToUpdate = nodeUpdatesPerCall / g_ActivityMan.GetActivity()->GetTeamCount(); + if (m_pTerrain->GetUpdatedMaterialAreas().size() > maxUnupdatedMaterialAreas) { + // Our list of boxes is getting too big and a bit out of hand, so clear everything. + nodesToUpdate = std::numeric_limits::max(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculatePath -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates and returns the least difficult path between two points on -// the current scene. Takes both distance and materials into account. + // Update our shared pathFinder + std::vector updatedNodes = GetPathFinder(Activity::Teams::NoTeam)->RecalculateAreaCosts(m_pTerrain->GetUpdatedMaterialAreas(), nodesToUpdate); + if (!updatedNodes.empty()) { + // Update each team's pathFinder + for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { + if (!g_ActivityMan.ActivityRunning() || !g_ActivityMan.GetActivity()->TeamActive(team)) { + continue; + } + + // Remove the material representation of all doors of this team so we can navigate through them (they'll open for us). + g_MovableMan.OverrideMaterialDoors(true, team); -float Scene::CalculatePath(const Vector &start, const Vector &end, std::list &pathResult, float digStrength, Activity::Teams team) { - float totalCostResult = -1; + GetPathFinder(static_cast(team))->UpdateNodeList(updatedNodes); - if (const std::unique_ptr &pathFinder = GetPathFinder(team)) { - int result = pathFinder->CalculatePath(start, end, pathResult, totalCostResult, digStrength); + // Place back the material representation of all doors of this team so they are as we found them. + g_MovableMan.OverrideMaterialDoors(false, team); + } + } - // It's ok if start and end nodes happen to be the same, the exact pixel locations are added at the front and end of the result regardless - return (result == micropather::MicroPather::SOLVED || result == micropather::MicroPather::START_END_SAME) ? totalCostResult : -1; - } + m_PartialPathUpdateTimer.Reset(); + m_PathfindingUpdated = true; + } - return false; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculatePath + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates and returns the least difficult path between two points on + // the current scene. Takes both distance and materials into account. -std::shared_ptr Scene::CalculatePathAsync(const Vector &start, const Vector &end, float digStrength, Activity::Teams team, PathCompleteCallback callback) { - if (const std::unique_ptr &pathFinder = GetPathFinder(team)) { - return pathFinder->CalculatePathAsync(start, end, digStrength, callback); - } + float Scene::CalculatePath(const Vector& start, const Vector& end, std::list& pathResult, float digStrength, Activity::Teams team) { + float totalCostResult = -1; - return nullptr; -} + if (const std::unique_ptr& pathFinder = GetPathFinder(team)) { + int result = pathFinder->CalculatePath(start, end, pathResult, totalCostResult, digStrength); -int Scene::GetScenePathSize() const { - return s_ScenePath.size(); -} + // It's ok if start and end nodes happen to be the same, the exact pixel locations are added at the front and end of the result regardless + return (result == micropather::MicroPather::SOLVED || result == micropather::MicroPather::START_END_SAME) ? totalCostResult : -1; + } -std::list& Scene::GetScenePath() { - return s_ScenePath; -} + return false; + } -bool Scene::PositionsAreTheSamePathNode(const Vector &pos1, const Vector &pos2) const { - if (const std::unique_ptr &pathFinder = const_cast(this)->GetPathFinder(Activity::Teams::NoTeam)) { - return pathFinder->PositionsAreTheSamePathNode(pos1, pos2); - } - - return false; -} + std::shared_ptr Scene::CalculatePathAsync(const Vector& start, const Vector& end, float digStrength, Activity::Teams team, PathCompleteCallback callback) { + if (const std::unique_ptr& pathFinder = GetPathFinder(team)) { + return pathFinder->CalculatePathAsync(start, end, digStrength, callback); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Lock -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Locks all dynamic internal scene bitmaps so that manipulaitons of the -// scene's color and matter representations can take place. -// Doing it in a separate method like this is more efficient because -// many bitmap manipulaitons can be performed between a lock and unlock. -// UnlockScene() should always be called after accesses are completed. - -void Scene::Lock() -{ -// RTEAssert(!m_Locked, "Hey, locking already locked scene!"); - if (!m_Locked) - { - m_pTerrain->LockBitmaps(); - m_Locked = true; - } -} + return nullptr; + } + int Scene::GetScenePathSize() const { + return s_ScenePath.size(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Unlock -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Unlocks the scene's bitmaps and prevents access to display memory. -// Doing it in a separate method like this is more efficient because -// many bitmap accesses can be performed between a lock and an unlock. -// UnlockScene() should only be called after LockScene(). - -void Scene::Unlock() -{ -// RTEAssert(m_Locked, "Hey, unlocking already unlocked scene!"); - if (m_Locked) - { - m_pTerrain->UnlockBitmaps(); - m_Locked = false; - } -} + std::list& Scene::GetScenePath() { + return s_ScenePath; + } + bool Scene::PositionsAreTheSamePathNode(const Vector& pos1, const Vector& pos2) const { + if (const std::unique_ptr& pathFinder = const_cast(this)->GetPathFinder(Activity::Teams::NoTeam)) { + return pathFinder->PositionsAreTheSamePathNode(pos1, pos2); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Scene. Supposed to be done every frame -// before drawing. + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Lock + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Locks all dynamic internal scene bitmaps so that manipulaitons of the + // scene's color and matter representations can take place. + // Doing it in a separate method like this is more efficient because + // many bitmap manipulaitons can be performed between a lock and unlock. + // UnlockScene() should always be called after accesses are completed. + + void Scene::Lock() { + // RTEAssert(!m_Locked, "Hey, locking already locked scene!"); + if (!m_Locked) { + m_pTerrain->LockBitmaps(); + m_Locked = true; + } + } -void Scene::Update() -{ - ZoneScoped; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Unlock + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Unlocks the scene's bitmaps and prevents access to display memory. + // Doing it in a separate method like this is more efficient because + // many bitmap accesses can be performed between a lock and an unlock. + // UnlockScene() should only be called after LockScene(). + + void Scene::Unlock() { + // RTEAssert(m_Locked, "Hey, unlocking already unlocked scene!"); + if (m_Locked) { + m_pTerrain->UnlockBitmaps(); + m_Locked = false; + } + } - m_PathfindingUpdated = false; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this Scene. Supposed to be done every frame + // before drawing. - if (g_SettingsMan.BlipOnRevealUnseen()) - { - // Highlight the pixels that have been revealed on the unseen maps - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - if (m_apUnseenLayer[team]) - { - for (std::list::iterator itr = m_SeenPixels[team].begin(); itr != m_SeenPixels[team].end(); ++itr) - { - putpixel(m_apUnseenLayer[team]->GetBitmap(), (*itr).m_X, (*itr).m_Y, g_WhiteColor); + void Scene::Update() { + ZoneScoped; + + m_PathfindingUpdated = false; + + if (g_SettingsMan.BlipOnRevealUnseen()) { + // Highlight the pixels that have been revealed on the unseen maps + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + if (m_apUnseenLayer[team]) { + for (std::list::iterator itr = m_SeenPixels[team].begin(); itr != m_SeenPixels[team].end(); ++itr) { + putpixel(m_apUnseenLayer[team]->GetBitmap(), (*itr).m_X, (*itr).m_Y, g_WhiteColor); + } } } } - } - if (m_NavigatableAreasUpToDate == false) { - // Need to block until all current pathfinding requests are finished. Ugh, if only we had a better way (interrupt/cancel a path request to start a new one?) - // TODO: Make the PathRequest struct more capable and maybe we can delay starting or cancel mid-request? - BlockUntilAllPathingRequestsComplete(); + if (m_NavigatableAreasUpToDate == false) { + // Need to block until all current pathfinding requests are finished. Ugh, if only we had a better way (interrupt/cancel a path request to start a new one?) + // TODO: Make the PathRequest struct more capable and maybe we can delay starting or cancel mid-request? + BlockUntilAllPathingRequestsComplete(); - m_NavigatableAreasUpToDate = true; - for (int team = Activity::Teams::NoTeam; team < Activity::Teams::MaxTeamCount; ++team) { - PathFinder& pathFinder = *GetPathFinder(static_cast(team)); + m_NavigatableAreasUpToDate = true; + for (int team = Activity::Teams::NoTeam; team < Activity::Teams::MaxTeamCount; ++team) { + PathFinder& pathFinder = *GetPathFinder(static_cast(team)); - pathFinder.MarkAllNodesNavigatable(m_NavigatableAreas.empty()); + pathFinder.MarkAllNodesNavigatable(m_NavigatableAreas.empty()); - for (const std::string &navigatableArea : m_NavigatableAreas) { - if (HasArea(navigatableArea)) { - for (const Box &navigatableBox : GetArea(navigatableArea)->GetBoxes()) { - pathFinder.MarkBoxNavigatable(navigatableBox, true); - } - } - } - } - } + for (const std::string& navigatableArea: m_NavigatableAreas) { + if (HasArea(navigatableArea)) { + for (const Box& navigatableBox: GetArea(navigatableArea)->GetBoxes()) { + pathFinder.MarkBoxNavigatable(navigatableBox, true); + } + } + } + } + } - // Occasionally update pathfinding. There's a tradeoff between how often updates occur vs how big the multithreaded batched node lists to update are. - if (m_PartialPathUpdateTimer.IsPastRealMS(100)) { - UpdatePathFinding(); - } -} + // Occasionally update pathfinding. There's a tradeoff between how often updates occur vs how big the multithreaded batched node lists to update are. + if (m_PartialPathUpdateTimer.IsPastRealMS(100)) { + UpdatePathFinding(); + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -std::unique_ptr& Scene::GetPathFinder(Activity::Teams team) { - // Note - we use + 1 when getting pathfinders by index, because our shared NoTeam pathfinder occupies index 0, and the rest come after that. - return m_pPathFinders[static_cast(team) + 1]; -} + std::unique_ptr& Scene::GetPathFinder(Activity::Teams team) { + // Note - we use + 1 when getting pathfinders by index, because our shared NoTeam pathfinder occupies index 0, and the rest come after that. + return m_pPathFinders[static_cast(team) + 1]; + } } // namespace RTE diff --git a/Source/Entities/Scene.h b/Source/Entities/Scene.h index b5c7fe632c..e2bdc348b0 100644 --- a/Source/Entities/Scene.h +++ b/Source/Entities/Scene.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -19,1459 +18,1358 @@ #include "Activity.h" #include "PathFinder.h" -namespace RTE -{ - -class ContentFile; -class MovableObject; -class PathFinder; -class SLBackground; -class SLTerrain; -class SceneLayer; -class BunkerAssembly; -class SceneObject; -class Deployment; - -struct SceneLayerInfo { - std::string name; - std::unique_ptr bitmap; -}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: Scene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Contains everything that defines a complete scene. -// Parent(s): Entity. -// Class history: 08/02/2006 Scene created. - -class Scene : public Entity { - - friend struct EntityLuaBindings; - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - SerializableOverrideMethods; - ClassInfoGetters; - - //Available placed objects sets - enum PlacedObjectSets - { - PLACEONLOAD = 0, - BLUEPRINT, - AIPLAN, - PLACEDSETSCOUNT +namespace RTE { + + class ContentFile; + class MovableObject; + class PathFinder; + class SLBackground; + class SLTerrain; + class SceneLayer; + class BunkerAssembly; + class SceneObject; + class Deployment; + + struct SceneLayerInfo { + std::string name; + std::unique_ptr bitmap; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Nested class: Area - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Something to bundle the properties of scene areas together - // Parent(s): Serializable. - // Class history: 07/18/2008 Area created. - - class Area: - public Serializable - { - - friend class Scene; - friend class AreaEditorGUI; - friend class AreaPickerGUI; + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: Scene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Contains everything that defines a complete scene. + // Parent(s): Entity. + // Class history: 08/02/2006 Scene created. - friend struct EntityLuaBindings; + class Scene : public Entity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations + friend struct EntityLuaBindings; - public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations - SerializableClassNameGetter; + public: SerializableOverrideMethods; + ClassInfoGetters; + + // Available placed objects sets + enum PlacedObjectSets { + PLACEONLOAD = 0, + BLUEPRINT, + AIPLAN, + PLACEDSETSCOUNT + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Nested class: Area + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Something to bundle the properties of scene areas together + // Parent(s): Serializable. + // Class history: 07/18/2008 Area created. + + class Area : + public Serializable { + + friend class Scene; + friend class AreaEditorGUI; + friend class AreaPickerGUI; + + friend struct EntityLuaBindings; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + SerializableClassNameGetter; + SerializableOverrideMethods; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: Area + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a Area object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + Area() { + Clear(); + Create(); + } + Area(std::string name) { + Clear(); + m_Name = name; + Create(); + } + Area(const Area& reference) { + Clear(); + Create(reference); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Area object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Area to be identical to another, by deep copy. + // Arguments: A reference to the Area to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const Area& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire Serializable, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: AddBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a Box to this' area coverage. + // Arguments: The Box to add. A copy will be made and added. + // Return value: Whether the Box was successfully added or not. + + bool AddBox(const Box& newBox); + + /// + /// Removes the first Box in the Area that has the same Corner, Width and Height of the passed-in Box. + /// + /// A Box whose values are used to determine what Box to remove. + /// Whether or not a Box was removed. + bool RemoveBox(const Box& boxToRemove); + + /// + /// Gets the first Box in this Area. + /// + /// The first Box in this Area. + const Box* GetFirstBox() const { return m_BoxList.empty() ? nullptr : &m_BoxList[0]; } + + /// + /// Gets the boxes for this area. + /// + /// The boxes in this Area. + const std::vector& GetBoxes() const { return m_BoxList; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: HasNoArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this really has no Area at all, ie it doesn't have any + // Box:es with both width and height. + // Arguments: None. + // Return value: Whether this Area actually covers any area. + + bool HasNoArea() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsInside + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether a point is anywhere inside this Area's coverage. + // Arguments: The point to check if it's inside the Area, in absolute scene coordinates. + // Return value: Whether the point is inside any of this Area's Box:es. + + bool IsInside(const Vector& point) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsInsideX + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the + // X-axis only. + // Arguments: The x coord to check if it's inside the Area, in absolute scene units. + // Return value: Whether the point is inside any of this Area's Box:es in the X axis. + + bool IsInsideX(float pointX) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsInsideY + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the + // Y-axis only. + // Arguments: The x coord to check if it's inside the Area, in absolute scene units. + // Return value: Whether the point is inside any of this Area's Box:es in the Y axis. + + bool IsInsideY(float pointY) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: MovePointInsideX + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Moves a coordinate to the closest value which is within any of this + // Area's Box:es, in the X axis only. + // Arguments: The x coord to transform to the closest inside poistion in the x-axis. + // Which direction to limit the search to. < 0 means can only look in the + // negative dir, 0 means can look in both directions. + // Return value: Whether the point was moved at all to get inside this' x-space. + + bool MovePointInsideX(float& pointX, int direction = 0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetBoxInside + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the first Box encountered in this that contains a specific point. + // Arguments: The point to check for Box collision, in absolute scene coordinates. + // Return value: Pointer to the first Box which was found to contain the point. 0 if + // none was found. OWNERSHIP IS NOT TRANSFERRED! + + Box* GetBoxInside(const Vector& point); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: RemoveBoxInside + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes the first Box encountered in this that contains a specific point. + // Arguments: The point to check for Box collision, in absolute scene coordinates. + // Return value: Copy of the Box that was removed. Will be NoArea Box if none was found. + + Box RemoveBoxInside(const Vector& point); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetCenterPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a center point for this of all the boxes waeighted by their sizes. + // Arguments: None. + // Return value: A center point of this area, can be outside the actual area though, if + // pulled apart by two separate boxes, for example. 0,0 if this has no Area + + Vector GetCenterPoint() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRandomPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a random coordinate contained within any of this' Box:es. + // Arguments: None. + // Return value: A random point that is within this Area. 0,0 if this has no Area + + Vector GetRandomPoint() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the name of the Area + // Arguments: None. + // Return value: The name used to ID this Area. + + std::string GetName() const { return m_Name; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // The list of Box:es defining the Area in the owner Scene + std::vector m_BoxList; + // The name tag of this Area + std::string m_Name; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Exit, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; + + friend class AreaEditor; + friend class AreaEditorGUI; + friend class AreaPickerGUI; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Area - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Area object in system - // memory. Create() should be called before using the object. - // Arguments: None. - - Area() { Clear(); Create(); } - Area(std::string name) { Clear(); m_Name = name; Create(); } - Area(const Area &reference) { Clear(); Create(reference); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Area object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - - int Create() override; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Area to be identical to another, by deep copy. - // Arguments: A reference to the Area to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - - int Create(const Area &reference); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Serializable, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - - void Reset() override { Clear(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: AddBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a Box to this' area coverage. - // Arguments: The Box to add. A copy will be made and added. - // Return value: Whether the Box was successfully added or not. - - bool AddBox(const Box &newBox); +#define METABASE_AREA_NAME "MetabaseServiceArea" - /// - /// Removes the first Box in the Area that has the same Corner, Width and Height of the passed-in Box. - /// - /// A Box whose values are used to determine what Box to remove. - /// Whether or not a Box was removed. - bool RemoveBox(const Box &boxToRemove); + enum NeighborDirection { + NODIR = -1, + E = 0, + SE, + S, + SW, + W, + NW, + N, + NE + }; + + // Concrete allocation and cloning definitions + EntityAllocation(Scene) + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: Scene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a Scene object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + Scene() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~Scene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a Scene object before deletion + // from system memory. + // Arguments: None. + + ~Scene() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the Scene object ready for use. + // Arguments: The Terrain to use. Ownership IS transferred! + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(SLTerrain* pNewTerrain); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a Scene to be identical to another, by deep copy. + // Arguments: A reference to the Scene to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const Scene& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: LoadData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Actually loads previously specified/created data into memory. Has + // to be done before using this Scene. + // Arguments: Whetehr to actually place out all the sceneobjects associated with the + // Scene's definition. If not, they still remain in the internal placed + // objects list. This avoids messing with the MovableMan at all. + // Whether to do pathfinding init, which should be avoided if we are only + // loading and saving purposes of MetaMan, for example. + // Whether to place actors and deployments (doors not affected). + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int LoadData(bool placeObjects = true, bool initPathfinding = true, bool placeUnits = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ExpandAIPlanAssemblySchemes + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Replace all assembly shemes by corresponding bunker assemblies in + // AI plan objects set. + // Arguments: None. + // Return value: None. + + int ExpandAIPlanAssemblySchemes(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SaveData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves data currently in memory to disk. + // Arguments: The filepath base to the where to save the Bitmap data. This means + // everything up to the extension. "FG" and "Mat" etc will be added. + // Whether or not to save asynchronously. + // Return value: An error return value signaling success or any particular failure. + // Anything below 0 is an error signal. + + int SaveData(std::string pathBase, bool doAsyncSaves = true); + + std::vector GetCopiedSceneLayerBitmaps(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SavePreview + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves preview bitmap for this scene. + // + // Arguments: The full filepath the where to save the Bitmap data. + // Return value: None. + + int SavePreview(const std::string& bitmapPath); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ClearData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears out any previously loaded bitmap data from memory. + // Arguments: None. + // Return value: An error return value signaling success or any particular failure. + // Anything below 0 is an error signal. + + int ClearData(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire Scene, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Entity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the Scene object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: MigrateToModule + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes this an original Preset in a different module than it was before. + // It severs ties deeply to the old module it was saved in. + // Arguments: The ID of the new module. + // Return value: Whether the migration was successful. If you tried to migrate to the + // same module it already was in, this would return false. + + bool MigrateToModule(int whichModule) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLocation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the specified location of this scene on the planet view + // Arguments: None. + // Return value: A Vector showing the location of this scene on the planet view. + + Vector GetLocation() const { return m_Location; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLocationOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the specified temporary location offset of this scene on the planet view. + // Arguments: None. + // Return value: A Vector showing the temporary location offset of this scene on the planet view. + + Vector GetLocationOffset() const { return m_LocationOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLocationOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the specified temporary location offset of this scene on the planet view. + // Arguments: A Vector showing the temporary location offset of this scene on the planet view. + // Return value: None. + + void SetLocationOffset(Vector newOffset) { m_LocationOffset = newOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsMetagamePlayable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is compatible with metagame play at all. + // Arguments: None. + // Return value: Whether this can be used on the metagame map. + + bool IsMetagamePlayable() const { return m_MetagamePlayable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsRevealed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this is revealed on the metagame map. + // Arguments: None. + // Return value: Whether this can be seen on the metagame map yet. + + bool IsRevealed() const { return m_Revealed; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTeamOwnership + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows which team owns this Scene in a Metagame, if any + // Arguments: None. + // Return value: The team that owns this site in a Metagame, if any + + int GetTeamOwnership() const { return m_OwnedByTeam; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRoundIncome + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows how much income this Scene pulls in for its owning team each + // round of a metagame. + // Arguments: None. + // Return value: The income in oz that this generates each metagame round. + + float GetRoundIncome() const { return m_RoundIncome; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBuildBudget + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows how much gold this Scene is budgeted to be built for this round. + // Arguments: Which player's set budget to show. + // Return value: The budget in oz that this is allocated to have built for this round. + + float GetBuildBudget(int player) const { return m_BuildBudget[player]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBuildBudgetRatio + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows how much of a player's budget this Scene is allocated to be + // built for this round. + // Arguments: Which player's set budget ratio to show. + // Return value: The budget in normalized ratio that this is allocated last round. + + float GetBuildBudgetRatio(int player) const { return m_BuildBudgetRatio[player]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetAutoDesigned + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this should be automatically designed by the AI plan + // even if owned by human players. + // Arguments: What to set the setting to. + // Return value: None. + + void SetAutoDesigned(bool autoDesigned = true) { m_AutoDesigned = autoDesigned; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAutoDesigned + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether this should be automatically designed by the AI plan + // even if owned by human players. + // Arguments: None. + // Return value: Whether this should be autodesigned or not. + + bool GetAutoDesigned() const { return m_AutoDesigned; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetTotalInvestment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the total defense investment this scene has experienced by all + // teams since the metagame started. + // Arguments: What to set the total investment in gold oz) to. + // Return value: None. + + void SetTotalInvestment(float totalInvestment) { m_TotalInvestment = totalInvestment; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalInvestment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total defense investment this scene has experienced by all + // teams since the metagame started. + // Arguments: None. + // Return value: The total investment in this scene. + + float GetTotalInvestment() const { return m_TotalInvestment; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTerrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the SLTerrain. + // Arguments: None. + // Return value: A pointer to the SLTerrain. Ownership is NOT transferred! + + SLTerrain* GetTerrain() { return m_pTerrain; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBackLayers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets access to the background layer list. + // Arguments: None. + // Return value: A reference to the std::list containing all the background layers. + // Ownership is NOT transferred! + + std::list& GetBackLayers() { return m_BackLayerList; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds area to the list if this scene's areas. + // Arguments: Area to add. + // Return value: None. + + void AddArea(Scene::Area& newArea) { m_AreaList.push_back(newArea); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FillUnseenLayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a new SceneLayer for a specific team and fills it with black + // pixels that end up being a specific size on the screen. + // Arguments: A Vector with the desired dimensions of the unseen layer's chunky pixels. + // Which team to get the unseen layer for. + // Whether to create the unseen layers now, or wait until next time + // LoadData is called on this. + // Return value: None. + + void FillUnseenLayer(Vector pixelSize, int team = Activity::TeamOne, bool createNow = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetUnseenLayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the unseen layer of a specific team. + // Arguments: The new SceneLayer to use as the new unseen layer, Ownership IS XFERRED! + // Which team to get the unseen layer for. + // Return value: None. + + void SetUnseenLayer(SceneLayer* pNewLayer, int team = Activity::TeamOne); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetUnseenLayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the unseen layer of a specific team. + // Arguments: Which team to get the unseen layer for. + // Return value: A pointer to the SceneLayer representing what hasn't been seen by a + // specific team yet. Ownership is NOT transferred! + + SceneLayer* GetUnseenLayer(int team = Activity::TeamOne) const { return team != Activity::NoTeam ? m_apUnseenLayer[team] : 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSeenPixels + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the list of pixels that have been seen on a team's unseen layer. + // Arguments: Which team to get the unseen layer for. + // Return value: The list of pixel coordinates in the unseen layer's scale. + + std::list& GetSeenPixels(int team = Activity::TeamOne) { return m_SeenPixels[team]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearSeenPixels + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the pixels that have been seen on a team's unseen layer. + // Arguments: Which team to get the unseen layer for. + // Return value: None. + + void ClearSeenPixels(int team = Activity::TeamOne); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CleanOrphanPixel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks a specific unseen pixel for only having two or less unseen + // neighbors, and if so, makes it seen. + // Arguments: Coordinates to the pixel to check for orphaness. + // The direction we might be checking 'from', ie the neighbor we can + // already assume is seen without poking at the unseen map. + // Which team's unseen layer to check the pixel on. + // Return value: Whether the pixel was deemed to be orphan and thus cleaned up. + + bool CleanOrphanPixel(int posX, int posY, NeighborDirection checkingFrom = NODIR, int team = Activity::TeamOne); /// - /// Gets the first Box in this Area. + /// Gets the total dimensions (width and height) of the scene, in pixels. /// - /// The first Box in this Area. - const Box * GetFirstBox() const { return m_BoxList.empty() ? nullptr : &m_BoxList[0]; } - - /// - /// Gets the boxes for this area. - /// - /// The boxes in this Area. - const std::vector & GetBoxes() const { return m_BoxList; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: HasNoArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this really has no Area at all, ie it doesn't have any - // Box:es with both width and height. - // Arguments: None. - // Return value: Whether this Area actually covers any area. - - bool HasNoArea() const; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a point is anywhere inside this Area's coverage. - // Arguments: The point to check if it's inside the Area, in absolute scene coordinates. - // Return value: Whether the point is inside any of this Area's Box:es. - - bool IsInside(const Vector &point) const; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsInsideX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the - // X-axis only. - // Arguments: The x coord to check if it's inside the Area, in absolute scene units. - // Return value: Whether the point is inside any of this Area's Box:es in the X axis. - - bool IsInsideX(float pointX) const; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsInsideY - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the - // Y-axis only. - // Arguments: The x coord to check if it's inside the Area, in absolute scene units. - // Return value: Whether the point is inside any of this Area's Box:es in the Y axis. - - bool IsInsideY(float pointY) const; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: MovePointInsideX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Moves a coordinate to the closest value which is within any of this - // Area's Box:es, in the X axis only. - // Arguments: The x coord to transform to the closest inside poistion in the x-axis. - // Which direction to limit the search to. < 0 means can only look in the - // negative dir, 0 means can look in both directions. - // Return value: Whether the point was moved at all to get inside this' x-space. - - bool MovePointInsideX(float &pointX, int direction = 0) const; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetBoxInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the first Box encountered in this that contains a specific point. - // Arguments: The point to check for Box collision, in absolute scene coordinates. - // Return value: Pointer to the first Box which was found to contain the point. 0 if - // none was found. OWNERSHIP IS NOT TRANSFERRED! - - Box * GetBoxInside(const Vector &point); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: RemoveBoxInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes the first Box encountered in this that contains a specific point. - // Arguments: The point to check for Box collision, in absolute scene coordinates. - // Return value: Copy of the Box that was removed. Will be NoArea Box if none was found. - - Box RemoveBoxInside(const Vector &point); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetCenterPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a center point for this of all the boxes waeighted by their sizes. - // Arguments: None. - // Return value: A center point of this area, can be outside the actual area though, if - // pulled apart by two separate boxes, for example. 0,0 if this has no Area - - Vector GetCenterPoint() const; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRandomPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a random coordinate contained within any of this' Box:es. - // Arguments: None. - // Return value: A random point that is within this Area. 0,0 if this has no Area - - Vector GetRandomPoint() const; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the name of the Area - // Arguments: None. - // Return value: The name used to ID this Area. - - std::string GetName() const { return m_Name; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - - protected: - - // The list of Box:es defining the Area in the owner Scene - std::vector m_BoxList; - // The name tag of this Area - std::string m_Name; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - - private: - - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Exit, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - - void Clear(); - - }; - -friend class AreaEditor; -friend class AreaEditorGUI; -friend class AreaPickerGUI; - -#define METABASE_AREA_NAME "MetabaseServiceArea" - -enum NeighborDirection -{ - NODIR = -1, - E = 0, - SE, - S, - SW, - W, - NW, - N, - NE -}; - -// Concrete allocation and cloning definitions -EntityAllocation(Scene) - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: Scene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a Scene object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - Scene() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~Scene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a Scene object before deletion -// from system memory. -// Arguments: None. - - ~Scene() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Scene object ready for use. -// Arguments: The Terrain to use. Ownership IS transferred! -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(SLTerrain *pNewTerrain); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Scene to be identical to another, by deep copy. -// Arguments: A reference to the Scene to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const Scene &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: LoadData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually loads previously specified/created data into memory. Has -// to be done before using this Scene. -// Arguments: Whetehr to actually place out all the sceneobjects associated with the -// Scene's definition. If not, they still remain in the internal placed -// objects list. This avoids messing with the MovableMan at all. -// Whether to do pathfinding init, which should be avoided if we are only -// loading and saving purposes of MetaMan, for example. -// Whether to place actors and deployments (doors not affected). -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int LoadData(bool placeObjects = true, bool initPathfinding = true, bool placeUnits = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ExpandAIPlanAssemblySchemes -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Replace all assembly shemes by corresponding bunker assemblies in -// AI plan objects set. -// Arguments: None. -// Return value: None. - - int ExpandAIPlanAssemblySchemes(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SaveData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves data currently in memory to disk. -// Arguments: The filepath base to the where to save the Bitmap data. This means -// everything up to the extension. "FG" and "Mat" etc will be added. -// Whether or not to save asynchronously. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int SaveData(std::string pathBase, bool doAsyncSaves = true); - - std::vector GetCopiedSceneLayerBitmaps(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SavePreview -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves preview bitmap for this scene. -// -// Arguments: The full filepath the where to save the Bitmap data. -// Return value: None. - - int SavePreview(const std::string &bitmapPath); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ClearData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out any previously loaded bitmap data from memory. -// Arguments: None. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int ClearData(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire Scene, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Entity::Reset(); } + /// A Vector describing the scene dimensions. + Vector GetDimensions() const; + /// + /// Gets the total width of the scene, in pixels. + /// + /// An int describing the scene width. + int GetWidth() const; -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Scene object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: MigrateToModule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this an original Preset in a different module than it was before. -// It severs ties deeply to the old module it was saved in. -// Arguments: The ID of the new module. -// Return value: Whether the migration was successful. If you tried to migrate to the -// same module it already was in, this would return false. - - bool MigrateToModule(int whichModule) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLocation -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the specified location of this scene on the planet view -// Arguments: None. -// Return value: A Vector showing the location of this scene on the planet view. - - Vector GetLocation() const { return m_Location; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLocationOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the specified temporary location offset of this scene on the planet view. -// Arguments: None. -// Return value: A Vector showing the temporary location offset of this scene on the planet view. - - Vector GetLocationOffset() const { return m_LocationOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLocationOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the specified temporary location offset of this scene on the planet view. -// Arguments: A Vector showing the temporary location offset of this scene on the planet view. -// Return value: None. - - void SetLocationOffset(Vector newOffset) { m_LocationOffset = newOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsMetagamePlayable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is compatible with metagame play at all. -// Arguments: None. -// Return value: Whether this can be used on the metagame map. - - bool IsMetagamePlayable() const { return m_MetagamePlayable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsRevealed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this is revealed on the metagame map. -// Arguments: None. -// Return value: Whether this can be seen on the metagame map yet. - - bool IsRevealed() const { return m_Revealed; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTeamOwnership -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows which team owns this Scene in a Metagame, if any -// Arguments: None. -// Return value: The team that owns this site in a Metagame, if any - - int GetTeamOwnership() const { return m_OwnedByTeam; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRoundIncome -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows how much income this Scene pulls in for its owning team each -// round of a metagame. -// Arguments: None. -// Return value: The income in oz that this generates each metagame round. - - float GetRoundIncome() const { return m_RoundIncome; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBuildBudget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows how much gold this Scene is budgeted to be built for this round. -// Arguments: Which player's set budget to show. -// Return value: The budget in oz that this is allocated to have built for this round. - - float GetBuildBudget(int player) const { return m_BuildBudget[player]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBuildBudgetRatio -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows how much of a player's budget this Scene is allocated to be -// built for this round. -// Arguments: Which player's set budget ratio to show. -// Return value: The budget in normalized ratio that this is allocated last round. - - float GetBuildBudgetRatio(int player) const { return m_BuildBudgetRatio[player]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAutoDesigned -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this should be automatically designed by the AI plan -// even if owned by human players. -// Arguments: What to set the setting to. -// Return value: None. - - void SetAutoDesigned(bool autoDesigned = true) { m_AutoDesigned = autoDesigned; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAutoDesigned -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether this should be automatically designed by the AI plan -// even if owned by human players. -// Arguments: None. -// Return value: Whether this should be autodesigned or not. - - bool GetAutoDesigned() const { return m_AutoDesigned; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetTotalInvestment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the total defense investment this scene has experienced by all -// teams since the metagame started. -// Arguments: What to set the total investment in gold oz) to. -// Return value: None. - - void SetTotalInvestment(float totalInvestment) { m_TotalInvestment = totalInvestment; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalInvestment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total defense investment this scene has experienced by all -// teams since the metagame started. -// Arguments: None. -// Return value: The total investment in this scene. - - float GetTotalInvestment() const { return m_TotalInvestment; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the SLTerrain. -// Arguments: None. -// Return value: A pointer to the SLTerrain. Ownership is NOT transferred! - - SLTerrain * GetTerrain() { return m_pTerrain; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBackLayers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets access to the background layer list. -// Arguments: None. -// Return value: A reference to the std::list containing all the background layers. -// Ownership is NOT transferred! - - std::list & GetBackLayers() { return m_BackLayerList; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds area to the list if this scene's areas. -// Arguments: Area to add. -// Return value: None. - - void AddArea(Scene::Area & newArea) { m_AreaList.push_back(newArea); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FillUnseenLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a new SceneLayer for a specific team and fills it with black -// pixels that end up being a specific size on the screen. -// Arguments: A Vector with the desired dimensions of the unseen layer's chunky pixels. -// Which team to get the unseen layer for. -// Whether to create the unseen layers now, or wait until next time -// LoadData is called on this. -// Return value: None. - - void FillUnseenLayer(Vector pixelSize, int team = Activity::TeamOne, bool createNow = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetUnseenLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the unseen layer of a specific team. -// Arguments: The new SceneLayer to use as the new unseen layer, Ownership IS XFERRED! -// Which team to get the unseen layer for. -// Return value: None. - - void SetUnseenLayer(SceneLayer *pNewLayer, int team = Activity::TeamOne); + /// + /// Gets the total height of the scene, in pixels. + /// + /// An int describing the scene height. + int GetHeight() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WrapsX + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the scene wraps its scrolling around the X axis. + // Arguments: None. + // Return value: Whether the scene wraps around the X axis or not. + + bool WrapsX() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WrapsY + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the scene wraps its scrolling around the Y axis. + // Arguments: None. + // Return value: Whether the scene wraps around the Y axis or not. + + bool WrapsY() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PlaceResidentBrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Places the individual brain of a single player which may be stationed + // on this Scene, and registers them as such in an Activity. + // Arguments: The player's brain to place. + // The Activity to register the placed brains with. OWNERSHIP IS NOT TRANSFERRED! + // Return value: If the brain was successfully found as resident and placed. + + bool PlaceResidentBrain(int player, Activity& newActivity); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PlaceResidentBrains + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Places the individual brains of the various players which may be + // stationed on this Scene, and registers them as such in an Activity. + // Arguments: The Activity to register the placed brains with. OWNERSHIP IS NOT TRANSFERRED! + // Return value: How many brains were finally placed. + + int PlaceResidentBrains(Activity& newActivity); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RetrieveResidentBrains + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Looks at the Activity and its players' registered brain Actors, and + // saves them as resident brains for this Scene. Done when a fight is over + // and the survivors remain! + // Arguments: The Activity to check for registered brains. OWNERSHIP IS NOT TRANSFERRED! + // Return value: How many brains were found registered with the passed in Activity. + + int RetrieveResidentBrains(Activity& oldActivity); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RetrieveSceneObjects + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sucks up all the Actors, Items and Particles currently active in MovableMan and + // puts them into this' list of objects to place on next load. + // Arguments: The team to only retrieve Actors of. If NoTeam, then all will be grabbed. + // Whether to not get any brains at all. + // Return value: How many objects were found knocking about in the world, and stored. + + int RetrieveSceneObjects(bool transferOwnership, int onlyTeam = -1, bool noBrains = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlacedObjects + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the list of SceneObject:s which are placed in this scene on loading. + // Arguments: Which set of placed objects to get. See the PlacedObjectSets enum. + // Return value: The list of of placed objects. Ownership is NOT transferred! + + const std::list* GetPlacedObjects(int whichSet) const { return &m_PlacedObjects[whichSet]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddPlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a SceneObject to be placed in this scene. Ownership IS transferred! + // Arguments: Which set of placed objects to add to. See the PlacedObjectSets enum. + // The SceneObject instance to add, OIT! + // Where in the list the object should be inserted. -1 means at the end + // of the list. + // Return value: None. + + void AddPlacedObject(int whichSet, SceneObject* pObjectToAdd, int listOrder = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemovePlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a SceneObject placed in this scene. + // Arguments: Which set of placed objects to rem from. See the PlacedObjectSets enum. + // The list order number of the object to remove. If -1, the last one is removed. + // Return value: None. + + void RemovePlacedObject(int whichSet, int whichToRemove = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PickPlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the last placed object that graphically overlaps an absolute + // point in the scene. + // Arguments: Which set of placed objects to pick from. See the PlacedObjectSets enum. + // The point in absolute scene coordinates that will be used to pick the + // last placed SceneObject which overlaps it. + // An int which will be filled out with the order place of any found object + // in the list. if nothing is found, it will get a value of -1. + // Return value: The last hit SceneObject, if any. Ownership is NOT transferred! + + const SceneObject* PickPlacedObject(int whichSet, Vector& scenePoint, int* pListOrderPlace = 0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PickPlacedActorInRange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the last placed actor object that is closer than range to scenePoint + // + // Arguments: Which set of placed objects to pick from. See the PlacedObjectSets enum. + // The point in absolute scene coordinates that will be used to pick the + // closest placed SceneObject near it. + // The range to check for nearby objects. + // An int which will be filled out with the order place of any found object + // in the list. if nothing is found, it will get a value of -1. + // + // Return value: The closest actor SceneObject, if any. Ownership is NOT transferred! + + const SceneObject* PickPlacedActorInRange(int whichSet, Vector& scenePoint, int range, int* pListOrderPlace) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePlacedObjects + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updated the objects in the placed scene objects list of this. This is + // mostly for the editor to represent the items correctly. + // Arguments: Which set of placed objects to update. See the PlacedObjectSets enum. + // Return value: None. + + void UpdatePlacedObjects(int whichSet); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearPlacedObjectSet + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes all entries in a specific set of placed Objects. + // Arguments: Which set of placed objects to clear. See the PlacedObjectSets enum. + // Whether or not we have ownership of these items, and should delete them. + // Return value: How many things were removed in teh process of clearing that set. + + int ClearPlacedObjectSet(int whichSet, bool weHaveOwnership = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetResidentBrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the resident brain Actor of a specific player from this scene, + // if there is any. OWNERSHIP IS NOT TRANSFERRED! + // Arguments: Which player to get the resident brain of. + // Return value: The SO containing the brain, or 0 if there aren't any of that player. + + SceneObject* GetResidentBrain(int player) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetResidentBrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the resident brain Actor of a specific player from this scene, + // if there is any. Ownership IS transferred! + // Arguments: Which player to set the resident brain of. + // The Actor to set as the resident brain of the specified player. + // Return value: None. + + void SetResidentBrain(int player, SceneObject* pNewBrain); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetResidentBrainCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of brains currently residing in this scene. + // Arguments: None. + // Return value: The number of resident brains who are installed here. + + int GetResidentBrainCount() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds one or modifies an existing area of this Scene. + // Arguments: The area to add or modify of the same name in this Scene. Ownership is + // NOT transferred! + // Return value: Whether the specified area was previously defined in this scene. + + bool SetArea(Area& newArea); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks for the existence of a specific Area identified by a name. + // This won't throw any errors to the console if the Area isn't found. + // Arguments: The name of the Area to try to find in this Scene. + // Return value: Whether the specified area is defined in this Scene. + + bool HasArea(std::string areaName); + /// + /// Gets a specified Area identified by name. Ownership is NOT transferred! + /// + /// The name of the Area to try to get. + /// Whether the area is required, and should throw an error if not found. + /// A pointer to the Area asked for, or nullptr if no Area of that name was found. + Area* GetArea(const std::string_view& areaName, bool required); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetUnseenLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the unseen layer of a specific team. -// Arguments: Which team to get the unseen layer for. -// Return value: A pointer to the SceneLayer representing what hasn't been seen by a -// specific team yet. Ownership is NOT transferred! + /// + /// Gets a specified Area identified by name. Ownership is NOT transferred! + /// + /// The name of the Area to try to get. + /// A pointer to the Area asked for, or nullptr if no Area of that name was found. + Area* GetArea(const std::string& areaName) { return GetArea(areaName, true); } - SceneLayer * GetUnseenLayer(int team = Activity::TeamOne) const { return team != Activity::NoTeam ? m_apUnseenLayer[team] : 0; } + /// + /// Gets a specified Area identified by name, showing a Lua warning if it's not found. Ownership is NOT transferred! + /// Using this function will not add the area to the list of required areas which Scenario GUI uses to show compatible areas. + /// + /// The name of the Area to try to get. + /// A pointer to the Area asked for, or nullptr if no Area of that name was found. + Area* GetOptionalArea(const std::string& areaName) { return GetArea(areaName, false); } + + void AddNavigatableArea(const std::string& areaName) { + m_NavigatableAreas.push_back(areaName); + m_NavigatableAreasUpToDate = false; + } + void ClearNavigatableAreas(const std::string& areaName) { + m_NavigatableAreas.clear(); + m_NavigatableAreasUpToDate = false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a specific Area identified by a name. + // Arguments: The name of the Area to try to remove. + // Return value: Whether an Area of that name was found, and subsequently removed. + + bool RemoveArea(std::string areaName); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WithinArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if a point is within a specific named Area of this Scene. If + // no Area of the name is found, this just returns false without error. + // Arguments: The name of the Area to try to check against. + // The point to see if it's within the specified Area. + // Return value: Whether any Area of that name was found, AND the point falls within it. + + bool WithinArea(std::string areaName, const Vector& point) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGlobalAcc + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the global acceleration (in m/s^2) that is applied to all movable + // objects' velocities during every frame. Typically models gravity. + // Arguments: None. + // Return value: A Vector describing the global acceleration. + + Vector GetGlobalAcc() const { return m_GlobalAcc; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetGlobalAcc + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the global acceleration (in m/s^2) that is applied to all movable + // objects' velocities during every frame. Typically models gravity. + // Arguments: A Vector describing the global acceleration. + // Return value: None. + + void SetGlobalAcc(Vector newValue) { m_GlobalAcc = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMetasceneParent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns parent scene name of this metascene. + // Arguments: None. + // Return value: Name of a parent scene. + + std::string GetMetasceneParent() const { return m_MetasceneParent; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLocation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the specified location of this Scene in the scene + // Arguments: A Vector with the desired location of this Scene in the scene. + // Return value: None. + + void SetLocation(const Vector& newLocation) { m_Location = newLocation; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMetagamePlayable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this can be played in the Metagame map at all. + // Arguments: Whether this is compatible with metagame play at all. + // Return value: None. + + void SetMetagamePlayable(bool isPlayable) { m_MetagamePlayable = isPlayable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetRevealed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this should show up on the Metagame map yet. + // Arguments: Whether to reveal this on the metagame map or not. + // Return value: None. + + void SetRevealed(bool isRevealed) { m_Revealed = isRevealed; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetTeamOwnership + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the team who owns this Scene in a Metagame + // Arguments: The team who should now own this Scene + // Return value: None. + + void SetTeamOwnership(int newTeam); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBuildBudget + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets how much gold this Scene is budgeted to be built for this round. + // Arguments: The player whom is setting the budget. + // The budget in oz that this is allocated to have built for this round. + // Return value: None. + + void SetBuildBudget(int player, float budget) { m_BuildBudget[player] = budget; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetBuildBudgetRatio + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets how much of a player's budget this Scene is budgeted to be build + // for each turn. + // Arguments: The player whom is setting the budget ratio. + // The budget in normalized ratio that this is allocated of the total. + // Return value: None. + + void SetBuildBudgetRatio(int player, float budgetRatio) { m_BuildBudgetRatio[player] = budgetRatio; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalcBuildBudgetUse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Figure out exactly how much of the build budget would be used if + // as many blueprint objects as can be afforded and exists would be built. + // Arguments: The player for whom we are calculating this budget use. + // An optional int that will be filled with number of objects that can + // acutally be built. + // An optional int that will be filled with number of objects that can + // built out of the AI plan set, AFTER the blueprints are built. + // Return value: The amount of funds that would be applied to the building of objects. + + float CalcBuildBudgetUse(int player, int* pAffordCount = 0, int* pAffordAIPlanCount = 0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyAIPlan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Puts the pre-built AI base plan into effect by transferring as many + // pieces as the current base budget allows from the AI plan to the actual + // blueprints to be built at this Scene. + // Arguments: The AI player whom is putting his plans into motion. + // An optional int that will be filled with number of objects that were + // acutally moved from the AI plan to the blueprints. + // Return value: The value of the AI plan objects that were put onto the blueprints. + + float ApplyAIPlan(int player, int* pObjectsApplied = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyBuildBudget + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Actually builds as many objects in the specific player's Blueprint + // list as can be afforded by his build budget. The budget is deducted + // accordingly. + // Arguments: The player whom is using his budget. + // An optional int that will be filled with number of objects that were + // acutally built. + // Return value: The amount of funds that were applied to the building of objects. + + float ApplyBuildBudget(int player, int* pObjectsBuilt = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveAllPlacedActors + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Remove all actors that are in the placed set of objects to load for + // this scene. All except for an optionally specified team, that is. + // Arguments: Remove all actors but of this team. + // Return value: How many actors were actually removed. + + int RemoveAllPlacedActors(int exceptTeam = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOwnerOfAllDoors + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the ownership of all doors placed in this scene to a specific team + // Arguments: The team to change the ownership to + // The player which placed these doors. + // Return value: How many doors were actually affected. + + int SetOwnerOfAllDoors(int team, int player); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsScanScheduled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a specific team has scheduled an orbital Scan of this. + // Arguments: The team to check for. + // Return value: Whether the scan has been scheduled and paid for. + + bool IsScanScheduled(int team) const { return m_ScanScheduled[team]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetScheduledScan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this to be orbitally scanned by a specific team on next load. + // Arguments: The team to schedule the scan for. + // Whether to actually schedule the scan or clear it. + // Return value: None. + + void SetScheduledScan(int team, bool scan = true) { m_ScanScheduled[team] = scan; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ResetPathFinding + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Recalculates all of the pathfinding data. This is very expensive, so + // do very rarely! + // Arguments: None. + // Return value: None. + + void ResetPathFinding(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BlockUntilAllPathingRequestsComplete + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Blocks this thread until all pathing requests are completed. + // Arguments: None. + // Return value: None. + + void BlockUntilAllPathingRequestsComplete(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePathFinding + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Recalculates only the areas of the pathfinding data that have been + // marked as outdated. + // Arguments: None. + // Return value: None. + + void UpdatePathFinding(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PathFindingUpdated + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether the pathfinding data has been updated in the last frame. + // Arguments: None. + // Return value: Whether the pathfinding data was recalculated fully or partially. + + bool PathFindingUpdated() { return m_PathfindingUpdated; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculatePath + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates and returns the least difficult path between two points on + // the current scene. Takes both distance and materials into account. + // When pathing using the NoTeam pathFinder, no doors are considered passable. + // Arguments: Start and end positions on the scene to find the path between. + // A list which will be filled out with waypoints between the start and end. + // The maximum material strength any actor traveling along the path can dig through. + // The team we're pathing for (doors for this team will be considered passable) + // Return value: The total minimum difficulty cost calculated between the two points on + // the scene. + + float CalculatePath(const Vector& start, const Vector& end, std::list& pathResult, float digStrength = c_PathFindingDefaultDigStrength, Activity::Teams team = Activity::Teams::NoTeam); + /// + /// Asynchronously calculates the least difficult path between two points on the current Scene. Takes both distance and materials into account. + /// When pathing using the NoTeam pathFinder, no doors are considered passable. + /// + /// Start position of the pathfinding request. + /// End position of the pathfinding request. + /// The maximum material strength any actor traveling along the path can dig through. + /// The team we're pathing for (doors for this team will be considered passable) + /// A shared pointer to the volatile PathRequest to be used to track whehter the asynchrnous path calculation has been completed, and check its results. + std::shared_ptr CalculatePathAsync(const Vector& start, const Vector& end, float digStrength = c_PathFindingDefaultDigStrength, Activity::Teams team = Activity::Teams::NoTeam, PathCompleteCallback callback = nullptr); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSeenPixels -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the list of pixels that have been seen on a team's unseen layer. -// Arguments: Which team to get the unseen layer for. -// Return value: The list of pixel coordinates in the unseen layer's scale. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetScenePathSize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how many waypoints there are in the ScenePath currently + // Arguments: None. + // Return value: The number of waypoints in the ScenePath. - std::list & GetSeenPixels(int team = Activity::TeamOne) { return m_SeenPixels[team]; } + int GetScenePathSize() const; + std::list& GetScenePath(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearSeenPixels -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the pixels that have been seen on a team's unseen layer. -// Arguments: Which team to get the unseen layer for. -// Return value: None. + /// + /// Returns whether two position represent the same path nodes. + /// + /// First coordinates to compare. + /// Second coordinates to compare. + /// Whether both coordinates represent the same path node. + bool PositionsAreTheSamePathNode(const Vector& pos1, const Vector& pos2) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Lock + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Locks all dynamic internal scene bitmaps so that manipulaitons of the + // scene's color and matter representations can take place. + // Doing it in a separate method like this is more efficient because + // many bitmap manipulaitons can be performed between a lock and unlock. + // UnlockScene() should always be called after accesses are completed. + // Arguments: None. + // Return value: None. + + void Lock(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Unlock + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Unlocks the scene's bitmaps and prevents access to display memory. + // Doing it in a separate method like this is more efficient because + // many bitmap accesses can be performed between a lock and an unlock. + // UnlockScene() should only be called after LockScene(). + // Arguments: None. + // Return value: None. + + void Unlock(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsLocked + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the entire scene is currently locked or not. + // Arguments: None. + // Return value: Whether the entire scene is currently locked or not. + + bool IsLocked() const { return m_Locked; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this Scene. Supposed to be done every frame + // before drawing. + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsMetagameInternal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Whether this scene is a temprorary metagame scene and should + // not be used anywhere except in metagame. + // Arguments: None. + // Return value: Whether scene belongs to metagame or not. + + bool IsMetagameInternal() const { return m_IsMetagameInternal; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMetagameInternal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this scene is a temprorary metagame scene and should + // not be used anywhere except in metagame. + // Arguments: New value. + // Return value: None. + + void SetMetagameInternal(bool newValue) { m_IsMetagameInternal = newValue; } - void ClearSeenPixels(int team = Activity::TeamOne); + /// + /// Gets whether this Scene is a saved game Scene copy and should not be used anywhere except for game saving and loading. + /// + /// Whether this Scene is a saved game Scene copy. + bool IsSavedGameInternal() const { return m_IsSavedGameInternal; } + /// + /// Sets whether this Scene is a saved game Scene copy and should not be used anywhere except for game saving and loading. + /// + /// Whether this Scene is a saved game Scene copy. + void SetSavedGameInternal(bool newValue) { m_IsSavedGameInternal = newValue; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPreviewBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns preview bitmap pointer for this scene. + // Arguments: None. + // Return value: Pointer to preview bitmap. + + BITMAP* GetPreviewBitmap() const { return m_pPreviewBitmap; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Member variables + static Entity::ClassInfo m_sClass; + + // Position of the site/scene on the planet in the site selection menu view, relative to the center of the planet + Vector m_Location; + // Temporary location offset used to correct scene position when scene dots overlap. + Vector m_LocationOffset; + // Whether at all eligible for the Metagame + bool m_MetagamePlayable; + // Whether this is revealed on the metagame planet map yet or not + bool m_Revealed; + // Owned by which Team, if any (<0 if none) + int m_OwnedByTeam; + // Total income this place generates per Metagame round for its owner team + float m_RoundIncome; + // The special placed brain actors of each player that inhabit this Scene, OWNED here + SceneObject* m_ResidentBrains[Players::MaxPlayerCount]; + // Budget in oz this place is allocated per player for a metagame round for building (applying) blueprint objects. + float m_BuildBudget[Players::MaxPlayerCount]; + // Budget in ratio of the player for a metagame round. This is used to re-set the BuildBudget to match the ratio + // that a player budgeted to this site in the previous turn. + float m_BuildBudgetRatio[Players::MaxPlayerCount]; + // Whether this should be automatically designed by the AI Plan even if it's owned by a human player + bool m_AutoDesigned; + // The total amount of gold (in oz) that has been invested in the defenses of this site, by all teams + float m_TotalInvestment; + // Terrain definition + SLTerrain* m_pTerrain; + + // Pathfinding graph and logic. Owned by this + // The array of PathFinders for each team. Because we also have a shared pathfinder using index 0, we need to use MaxTeamCount + 1 to handle all the Teams' PathFinders. + std::array, Activity::Teams::MaxTeamCount + 1> m_pPathFinders; + // Is set to true on any frame the pathfinding data has been updated + bool m_PathfindingUpdated; + // Timer for when to do an update of the pathfinding data + Timer m_PartialPathUpdateTimer; + + // SceneObject:s to be placed in the scene, divided up by different sets - OWNED HERE + std::list m_PlacedObjects[PLACEDSETSCOUNT]; + // List of background layers, first is the closest to the terrain, last is closest to the back + std::list m_BackLayerList; + // Dimensions of the pixels of the unseen layers, when they are dynamically generated. If 0, the layer was not generated + Vector m_UnseenPixelSize[Activity::MaxTeamCount]; + // Layers representing the unknown areas for each team + SceneLayer* m_apUnseenLayer[Activity::MaxTeamCount]; + // Which pixels of the unseen map have just been revealed this frame, in the coordinates of the unseen map + std::list m_SeenPixels[Activity::MaxTeamCount]; + // Pixels on the unseen map deemed to be orphans and cleaned up, will be moved to seen pixels next update + std::list m_CleanedPixels[Activity::MaxTeamCount]; + // Whether this Scene is scheduled to be orbitally scanned by any team + bool m_ScanScheduled[Activity::MaxTeamCount]; + + // List of all the specified Area's of the scene + std::list m_AreaList; + + // List of navigatable areas in the scene. If this list is empty, the entire scene is assumed to be navigatable + std::vector m_NavigatableAreas; + bool m_NavigatableAreasUpToDate; + + // Whether the scene's bitmaps are locked or not. + bool m_Locked; + // The global acceleration vector in m/s^2. (think gravity/wind) + Vector m_GlobalAcc; + // Names of all Schemes and selected assemblies for them + std::map m_SelectedAssemblies; + // Amounts of limited assemblies + std::map m_AssembliesCounts; + // Scene preview bitmap + BITMAP* m_pPreviewBitmap; + // Scene preview source file + ContentFile m_PreviewBitmapFile; + // Name of a scene which can be replaced by this scene in MetaGame + // Scenes with m_MetaSceneParent field set will be invisible for editors and activities unless + // ShowMetaScenes flag in settings.ini is set + std::string m_MetasceneParent; + + // Whether this scene must be shown anywhere in UIs + bool m_IsMetagameInternal; + bool m_IsSavedGameInternal; + + std::list m_Deployments; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + /// + /// Gets the pathfinder for a given team. + /// + /// The team to get the pathfinder for. NoTeam is valid, and will give a shared pathfinder. + /// A pointer to the pathfinder for the given team. + std::unique_ptr& GetPathFinder(Activity::Teams team); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CleanOrphanPixel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks a specific unseen pixel for only having two or less unseen -// neighbors, and if so, makes it seen. -// Arguments: Coordinates to the pixel to check for orphaness. -// The direction we might be checking 'from', ie the neighbor we can -// already assume is seen without poking at the unseen map. -// Which team's unseen layer to check the pixel on. -// Return value: Whether the pixel was deemed to be orphan and thus cleaned up. - - bool CleanOrphanPixel(int posX, int posY, NeighborDirection checkingFrom = NODIR, int team = Activity::TeamOne); - - - /// - /// Gets the total dimensions (width and height) of the scene, in pixels. - /// - /// A Vector describing the scene dimensions. - Vector GetDimensions() const; - - /// - /// Gets the total width of the scene, in pixels. - /// - /// An int describing the scene width. - int GetWidth() const; - - /// - /// Gets the total height of the scene, in pixels. - /// - /// An int describing the scene height. - int GetHeight() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapsX -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the scene wraps its scrolling around the X axis. -// Arguments: None. -// Return value: Whether the scene wraps around the X axis or not. - - bool WrapsX() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapsY -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the scene wraps its scrolling around the Y axis. -// Arguments: None. -// Return value: Whether the scene wraps around the Y axis or not. - - bool WrapsY() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PlaceResidentBrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Places the individual brain of a single player which may be stationed -// on this Scene, and registers them as such in an Activity. -// Arguments: The player's brain to place. -// The Activity to register the placed brains with. OWNERSHIP IS NOT TRANSFERRED! -// Return value: If the brain was successfully found as resident and placed. - - bool PlaceResidentBrain(int player, Activity &newActivity); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PlaceResidentBrains -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Places the individual brains of the various players which may be -// stationed on this Scene, and registers them as such in an Activity. -// Arguments: The Activity to register the placed brains with. OWNERSHIP IS NOT TRANSFERRED! -// Return value: How many brains were finally placed. - - int PlaceResidentBrains(Activity &newActivity); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RetrieveResidentBrains -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Looks at the Activity and its players' registered brain Actors, and -// saves them as resident brains for this Scene. Done when a fight is over -// and the survivors remain! -// Arguments: The Activity to check for registered brains. OWNERSHIP IS NOT TRANSFERRED! -// Return value: How many brains were found registered with the passed in Activity. - - int RetrieveResidentBrains(Activity &oldActivity); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RetrieveSceneObjects -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sucks up all the Actors, Items and Particles currently active in MovableMan and -// puts them into this' list of objects to place on next load. -// Arguments: The team to only retrieve Actors of. If NoTeam, then all will be grabbed. -// Whether to not get any brains at all. -// Return value: How many objects were found knocking about in the world, and stored. - - int RetrieveSceneObjects(bool transferOwnership, int onlyTeam = -1, bool noBrains = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlacedObjects -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the list of SceneObject:s which are placed in this scene on loading. -// Arguments: Which set of placed objects to get. See the PlacedObjectSets enum. -// Return value: The list of of placed objects. Ownership is NOT transferred! - - const std::list * GetPlacedObjects(int whichSet) const { return &m_PlacedObjects[whichSet]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a SceneObject to be placed in this scene. Ownership IS transferred! -// Arguments: Which set of placed objects to add to. See the PlacedObjectSets enum. -// The SceneObject instance to add, OIT! -// Where in the list the object should be inserted. -1 means at the end -// of the list. -// Return value: None. - - void AddPlacedObject(int whichSet, SceneObject *pObjectToAdd, int listOrder = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemovePlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a SceneObject placed in this scene. -// Arguments: Which set of placed objects to rem from. See the PlacedObjectSets enum. -// The list order number of the object to remove. If -1, the last one is removed. -// Return value: None. - - void RemovePlacedObject(int whichSet, int whichToRemove = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PickPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the last placed object that graphically overlaps an absolute -// point in the scene. -// Arguments: Which set of placed objects to pick from. See the PlacedObjectSets enum. -// The point in absolute scene coordinates that will be used to pick the -// last placed SceneObject which overlaps it. -// An int which will be filled out with the order place of any found object -// in the list. if nothing is found, it will get a value of -1. -// Return value: The last hit SceneObject, if any. Ownership is NOT transferred! - - const SceneObject * PickPlacedObject(int whichSet, Vector &scenePoint, int *pListOrderPlace = 0) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PickPlacedActorInRange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the last placed actor object that is closer than range to scenePoint -// -// Arguments: Which set of placed objects to pick from. See the PlacedObjectSets enum. -// The point in absolute scene coordinates that will be used to pick the -// closest placed SceneObject near it. -// The range to check for nearby objects. -// An int which will be filled out with the order place of any found object -// in the list. if nothing is found, it will get a value of -1. -// -// Return value: The closest actor SceneObject, if any. Ownership is NOT transferred! - -const SceneObject * PickPlacedActorInRange(int whichSet, Vector &scenePoint, int range, int *pListOrderPlace) const; - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlacedObjects -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updated the objects in the placed scene objects list of this. This is -// mostly for the editor to represent the items correctly. -// Arguments: Which set of placed objects to update. See the PlacedObjectSets enum. -// Return value: None. - - void UpdatePlacedObjects(int whichSet); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearPlacedObjectSet -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes all entries in a specific set of placed Objects. -// Arguments: Which set of placed objects to clear. See the PlacedObjectSets enum. -// Whether or not we have ownership of these items, and should delete them. -// Return value: How many things were removed in teh process of clearing that set. - - int ClearPlacedObjectSet(int whichSet, bool weHaveOwnership = true); - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetResidentBrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the resident brain Actor of a specific player from this scene, -// if there is any. OWNERSHIP IS NOT TRANSFERRED! -// Arguments: Which player to get the resident brain of. -// Return value: The SO containing the brain, or 0 if there aren't any of that player. - - SceneObject * GetResidentBrain(int player) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetResidentBrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the resident brain Actor of a specific player from this scene, -// if there is any. Ownership IS transferred! -// Arguments: Which player to set the resident brain of. -// The Actor to set as the resident brain of the specified player. -// Return value: None. - - void SetResidentBrain(int player, SceneObject *pNewBrain); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetResidentBrainCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of brains currently residing in this scene. -// Arguments: None. -// Return value: The number of resident brains who are installed here. - - int GetResidentBrainCount() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds one or modifies an existing area of this Scene. -// Arguments: The area to add or modify of the same name in this Scene. Ownership is -// NOT transferred! -// Return value: Whether the specified area was previously defined in this scene. - - bool SetArea(Area &newArea); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks for the existence of a specific Area identified by a name. -// This won't throw any errors to the console if the Area isn't found. -// Arguments: The name of the Area to try to find in this Scene. -// Return value: Whether the specified area is defined in this Scene. - - bool HasArea(std::string areaName); - - /// - /// Gets a specified Area identified by name. Ownership is NOT transferred! - /// - /// The name of the Area to try to get. - /// Whether the area is required, and should throw an error if not found. - /// A pointer to the Area asked for, or nullptr if no Area of that name was found. - Area * GetArea(const std::string_view &areaName, bool required); - - /// - /// Gets a specified Area identified by name. Ownership is NOT transferred! - /// - /// The name of the Area to try to get. - /// A pointer to the Area asked for, or nullptr if no Area of that name was found. - Area* GetArea(const std::string &areaName) { return GetArea(areaName, true); } - - /// - /// Gets a specified Area identified by name, showing a Lua warning if it's not found. Ownership is NOT transferred! - /// Using this function will not add the area to the list of required areas which Scenario GUI uses to show compatible areas. - /// - /// The name of the Area to try to get. - /// A pointer to the Area asked for, or nullptr if no Area of that name was found. - Area * GetOptionalArea(const std::string &areaName) { return GetArea(areaName, false); } - - void AddNavigatableArea(const std::string &areaName) { m_NavigatableAreas.push_back(areaName); m_NavigatableAreasUpToDate = false; } - void ClearNavigatableAreas(const std::string &areaName) { m_NavigatableAreas.clear(); m_NavigatableAreasUpToDate = false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specific Area identified by a name. -// Arguments: The name of the Area to try to remove. -// Return value: Whether an Area of that name was found, and subsequently removed. - - bool RemoveArea(std::string areaName); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WithinArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if a point is within a specific named Area of this Scene. If -// no Area of the name is found, this just returns false without error. -// Arguments: The name of the Area to try to check against. -// The point to see if it's within the specified Area. -// Return value: Whether any Area of that name was found, AND the point falls within it. - - bool WithinArea(std::string areaName, const Vector &point) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGlobalAcc -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the global acceleration (in m/s^2) that is applied to all movable -// objects' velocities during every frame. Typically models gravity. -// Arguments: None. -// Return value: A Vector describing the global acceleration. - - Vector GetGlobalAcc() const { return m_GlobalAcc; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetGlobalAcc -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the global acceleration (in m/s^2) that is applied to all movable -// objects' velocities during every frame. Typically models gravity. -// Arguments: A Vector describing the global acceleration. -// Return value: None. - - void SetGlobalAcc(Vector newValue) { m_GlobalAcc = newValue; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMetasceneParent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns parent scene name of this metascene. -// Arguments: None. -// Return value: Name of a parent scene. - - std::string GetMetasceneParent() const { return m_MetasceneParent; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLocation -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the specified location of this Scene in the scene -// Arguments: A Vector with the desired location of this Scene in the scene. -// Return value: None. - - void SetLocation(const Vector& newLocation) { m_Location = newLocation; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMetagamePlayable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this can be played in the Metagame map at all. -// Arguments: Whether this is compatible with metagame play at all. -// Return value: None. - - void SetMetagamePlayable(bool isPlayable) { m_MetagamePlayable = isPlayable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetRevealed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this should show up on the Metagame map yet. -// Arguments: Whether to reveal this on the metagame map or not. -// Return value: None. - - void SetRevealed(bool isRevealed) { m_Revealed = isRevealed; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetTeamOwnership -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the team who owns this Scene in a Metagame -// Arguments: The team who should now own this Scene -// Return value: None. - - void SetTeamOwnership(int newTeam); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBuildBudget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets how much gold this Scene is budgeted to be built for this round. -// Arguments: The player whom is setting the budget. -// The budget in oz that this is allocated to have built for this round. -// Return value: None. - - void SetBuildBudget(int player, float budget) { m_BuildBudget[player] = budget; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetBuildBudgetRatio -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets how much of a player's budget this Scene is budgeted to be build -// for each turn. -// Arguments: The player whom is setting the budget ratio. -// The budget in normalized ratio that this is allocated of the total. -// Return value: None. - - void SetBuildBudgetRatio(int player, float budgetRatio) { m_BuildBudgetRatio[player] = budgetRatio; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalcBuildBudgetUse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Figure out exactly how much of the build budget would be used if -// as many blueprint objects as can be afforded and exists would be built. -// Arguments: The player for whom we are calculating this budget use. -// An optional int that will be filled with number of objects that can -// acutally be built. -// An optional int that will be filled with number of objects that can -// built out of the AI plan set, AFTER the blueprints are built. -// Return value: The amount of funds that would be applied to the building of objects. - - float CalcBuildBudgetUse(int player, int *pAffordCount = 0, int *pAffordAIPlanCount = 0) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyAIPlan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts the pre-built AI base plan into effect by transferring as many -// pieces as the current base budget allows from the AI plan to the actual -// blueprints to be built at this Scene. -// Arguments: The AI player whom is putting his plans into motion. -// An optional int that will be filled with number of objects that were -// acutally moved from the AI plan to the blueprints. -// Return value: The value of the AI plan objects that were put onto the blueprints. - - float ApplyAIPlan(int player, int *pObjectsApplied = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyBuildBudget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually builds as many objects in the specific player's Blueprint -// list as can be afforded by his build budget. The budget is deducted -// accordingly. -// Arguments: The player whom is using his budget. -// An optional int that will be filled with number of objects that were -// acutally built. -// Return value: The amount of funds that were applied to the building of objects. - - float ApplyBuildBudget(int player, int *pObjectsBuilt = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAllPlacedActors -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Remove all actors that are in the placed set of objects to load for -// this scene. All except for an optionally specified team, that is. -// Arguments: Remove all actors but of this team. -// Return value: How many actors were actually removed. - - int RemoveAllPlacedActors(int exceptTeam = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOwnerOfAllDoors -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the ownership of all doors placed in this scene to a specific team -// Arguments: The team to change the ownership to -// The player which placed these doors. -// Return value: How many doors were actually affected. - - int SetOwnerOfAllDoors(int team, int player); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsScanScheduled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a specific team has scheduled an orbital Scan of this. -// Arguments: The team to check for. -// Return value: Whether the scan has been scheduled and paid for. - - bool IsScanScheduled(int team) const { return m_ScanScheduled[team]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetScheduledScan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this to be orbitally scanned by a specific team on next load. -// Arguments: The team to schedule the scan for. -// Whether to actually schedule the scan or clear it. -// Return value: None. - - void SetScheduledScan(int team, bool scan = true) { m_ScanScheduled[team] = scan; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResetPathFinding -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Recalculates all of the pathfinding data. This is very expensive, so -// do very rarely! -// Arguments: None. -// Return value: None. - - void ResetPathFinding(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BlockUntilAllPathingRequestsComplete -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Blocks this thread until all pathing requests are completed. -// Arguments: None. -// Return value: None. - - void BlockUntilAllPathingRequestsComplete(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePathFinding -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Recalculates only the areas of the pathfinding data that have been -// marked as outdated. -// Arguments: None. -// Return value: None. - - void UpdatePathFinding(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PathFindingUpdated -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether the pathfinding data has been updated in the last frame. -// Arguments: None. -// Return value: Whether the pathfinding data was recalculated fully or partially. - - bool PathFindingUpdated() { return m_PathfindingUpdated; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculatePath -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates and returns the least difficult path between two points on -// the current scene. Takes both distance and materials into account. -// When pathing using the NoTeam pathFinder, no doors are considered passable. -// Arguments: Start and end positions on the scene to find the path between. -// A list which will be filled out with waypoints between the start and end. -// The maximum material strength any actor traveling along the path can dig through. -// The team we're pathing for (doors for this team will be considered passable) -// Return value: The total minimum difficulty cost calculated between the two points on -// the scene. - - float CalculatePath(const Vector &start, const Vector &end, std::list &pathResult, float digStrength = c_PathFindingDefaultDigStrength, Activity::Teams team = Activity::Teams::NoTeam); - - /// - /// Asynchronously calculates the least difficult path between two points on the current Scene. Takes both distance and materials into account. - /// When pathing using the NoTeam pathFinder, no doors are considered passable. - /// - /// Start position of the pathfinding request. - /// End position of the pathfinding request. - /// The maximum material strength any actor traveling along the path can dig through. - /// The team we're pathing for (doors for this team will be considered passable) - /// A shared pointer to the volatile PathRequest to be used to track whehter the asynchrnous path calculation has been completed, and check its results. - std::shared_ptr CalculatePathAsync(const Vector &start, const Vector &end, float digStrength = c_PathFindingDefaultDigStrength, Activity::Teams team = Activity::Teams::NoTeam, PathCompleteCallback callback = nullptr); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetScenePathSize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how many waypoints there are in the ScenePath currently -// Arguments: None. -// Return value: The number of waypoints in the ScenePath. - - int GetScenePathSize() const; - - std::list& GetScenePath(); - - /// - /// Returns whether two position represent the same path nodes. - /// - /// First coordinates to compare. - /// Second coordinates to compare. - /// Whether both coordinates represent the same path node. - bool PositionsAreTheSamePathNode(const Vector &pos1, const Vector &pos2) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Lock -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Locks all dynamic internal scene bitmaps so that manipulaitons of the -// scene's color and matter representations can take place. -// Doing it in a separate method like this is more efficient because -// many bitmap manipulaitons can be performed between a lock and unlock. -// UnlockScene() should always be called after accesses are completed. -// Arguments: None. -// Return value: None. - - void Lock(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Unlock -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Unlocks the scene's bitmaps and prevents access to display memory. -// Doing it in a separate method like this is more efficient because -// many bitmap accesses can be performed between a lock and an unlock. -// UnlockScene() should only be called after LockScene(). -// Arguments: None. -// Return value: None. - - void Unlock(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsLocked -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the entire scene is currently locked or not. -// Arguments: None. -// Return value: Whether the entire scene is currently locked or not. - - bool IsLocked() const { return m_Locked; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Scene. Supposed to be done every frame -// before drawing. -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsMetagameInternal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Whether this scene is a temprorary metagame scene and should -// not be used anywhere except in metagame. -// Arguments: None. -// Return value: Whether scene belongs to metagame or not. - - bool IsMetagameInternal() const { return m_IsMetagameInternal; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMetagameInternal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this scene is a temprorary metagame scene and should -// not be used anywhere except in metagame. -// Arguments: New value. -// Return value: None. - - void SetMetagameInternal(bool newValue) { m_IsMetagameInternal = newValue; } - - - /// - /// Gets whether this Scene is a saved game Scene copy and should not be used anywhere except for game saving and loading. - /// - /// Whether this Scene is a saved game Scene copy. - bool IsSavedGameInternal() const { return m_IsSavedGameInternal; } - - /// - /// Sets whether this Scene is a saved game Scene copy and should not be used anywhere except for game saving and loading. - /// - /// Whether this Scene is a saved game Scene copy. - void SetSavedGameInternal(bool newValue) { m_IsSavedGameInternal = newValue; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPreviewBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns preview bitmap pointer for this scene. -// Arguments: None. -// Return value: Pointer to preview bitmap. - - BITMAP * GetPreviewBitmap() const { return m_pPreviewBitmap; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - - // Position of the site/scene on the planet in the site selection menu view, relative to the center of the planet - Vector m_Location; - // Temporary location offset used to correct scene position when scene dots overlap. - Vector m_LocationOffset; - // Whether at all eligible for the Metagame - bool m_MetagamePlayable; - // Whether this is revealed on the metagame planet map yet or not - bool m_Revealed; - // Owned by which Team, if any (<0 if none) - int m_OwnedByTeam; - // Total income this place generates per Metagame round for its owner team - float m_RoundIncome; - // The special placed brain actors of each player that inhabit this Scene, OWNED here - SceneObject *m_ResidentBrains[Players::MaxPlayerCount]; - // Budget in oz this place is allocated per player for a metagame round for building (applying) blueprint objects. - float m_BuildBudget[Players::MaxPlayerCount]; - // Budget in ratio of the player for a metagame round. This is used to re-set the BuildBudget to match the ratio - // that a player budgeted to this site in the previous turn. - float m_BuildBudgetRatio[Players::MaxPlayerCount]; - // Whether this should be automatically designed by the AI Plan even if it's owned by a human player - bool m_AutoDesigned; - // The total amount of gold (in oz) that has been invested in the defenses of this site, by all teams - float m_TotalInvestment; - // Terrain definition - SLTerrain *m_pTerrain; - - // Pathfinding graph and logic. Owned by this - // The array of PathFinders for each team. Because we also have a shared pathfinder using index 0, we need to use MaxTeamCount + 1 to handle all the Teams' PathFinders. - std::array, Activity::Teams::MaxTeamCount + 1> m_pPathFinders; - // Is set to true on any frame the pathfinding data has been updated - bool m_PathfindingUpdated; - // Timer for when to do an update of the pathfinding data - Timer m_PartialPathUpdateTimer; - - // SceneObject:s to be placed in the scene, divided up by different sets - OWNED HERE - std::list m_PlacedObjects[PLACEDSETSCOUNT]; - // List of background layers, first is the closest to the terrain, last is closest to the back - std::list m_BackLayerList; - // Dimensions of the pixels of the unseen layers, when they are dynamically generated. If 0, the layer was not generated - Vector m_UnseenPixelSize[Activity::MaxTeamCount]; - // Layers representing the unknown areas for each team - SceneLayer *m_apUnseenLayer[Activity::MaxTeamCount]; - // Which pixels of the unseen map have just been revealed this frame, in the coordinates of the unseen map - std::list m_SeenPixels[Activity::MaxTeamCount]; - // Pixels on the unseen map deemed to be orphans and cleaned up, will be moved to seen pixels next update - std::list m_CleanedPixels[Activity::MaxTeamCount]; - // Whether this Scene is scheduled to be orbitally scanned by any team - bool m_ScanScheduled[Activity::MaxTeamCount]; - - // List of all the specified Area's of the scene - std::list m_AreaList; - - // List of navigatable areas in the scene. If this list is empty, the entire scene is assumed to be navigatable - std::vector m_NavigatableAreas; - bool m_NavigatableAreasUpToDate; - - // Whether the scene's bitmaps are locked or not. - bool m_Locked; - // The global acceleration vector in m/s^2. (think gravity/wind) - Vector m_GlobalAcc; - // Names of all Schemes and selected assemblies for them - std::map m_SelectedAssemblies; - // Amounts of limited assemblies - std::map m_AssembliesCounts; - // Scene preview bitmap - BITMAP * m_pPreviewBitmap; - // Scene preview source file - ContentFile m_PreviewBitmapFile; - // Name of a scene which can be replaced by this scene in MetaGame - // Scenes with m_MetaSceneParent field set will be invisible for editors and activities unless - // ShowMetaScenes flag in settings.ini is set - std::string m_MetasceneParent; - - // Whether this scene must be shown anywhere in UIs - bool m_IsMetagameInternal; - bool m_IsSavedGameInternal; - - std::listm_Deployments; - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - /// - /// Gets the pathfinder for a given team. - /// - /// The team to get the pathfinder for. NoTeam is valid, and will give a shared pathfinder. - /// A pointer to the pathfinder for the given team. - std::unique_ptr & GetPathFinder(Activity::Teams team); - - /// - /// Serializes the SceneObject via the Writer. Necessary because full serialization doesn't know how to deal with duplicate properties. - /// - /// The Writer being used for serialization. - /// The SceneObject to save. - /// Convenience flag for whether or not this SceneObject is a child Attachable, and certain properties shouldn't be saved. - /// Whether or not to save most data. Turned off for stuff like SceneEditor saves. - void SaveSceneObject(Writer &writer, const SceneObject *sceneObjectToSave, bool isChildAttachable, bool saveFullData) const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Scene, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - // Disallow the use of some implicit methods. - Scene(const Scene &reference) = delete; - void operator=(const Scene &rhs) = delete; - -}; + /// + /// Serializes the SceneObject via the Writer. Necessary because full serialization doesn't know how to deal with duplicate properties. + /// + /// The Writer being used for serialization. + /// The SceneObject to save. + /// Convenience flag for whether or not this SceneObject is a child Attachable, and certain properties shouldn't be saved. + /// Whether or not to save most data. Turned off for stuff like SceneEditor saves. + void SaveSceneObject(Writer& writer, const SceneObject* sceneObjectToSave, bool isChildAttachable, bool saveFullData) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this Scene, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + Scene(const Scene& reference) = delete; + void operator=(const Scene& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Entities/SceneLayer.cpp b/Source/Entities/SceneLayer.cpp index c1757689af..18ea2c29de 100644 --- a/Source/Entities/SceneLayer.cpp +++ b/Source/Entities/SceneLayer.cpp @@ -13,7 +13,7 @@ namespace RTE { ConcreteClassInfo(SceneLayerTracked, Entity, 0); ConcreteClassInfo(SceneLayer, Entity, 0); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void SceneLayerImpl::Clear() { @@ -34,10 +34,10 @@ namespace RTE { m_ScaledDimensions.SetXY(1.0F, 1.0F); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - int SceneLayerImpl::Create(const ContentFile &bitmapFile, bool drawMasked, const Vector &offset, bool wrapX, bool wrapY, const Vector &scrollInfo) { + int SceneLayerImpl::Create(const ContentFile& bitmapFile, bool drawMasked, const Vector& offset, bool wrapX, bool wrapY, const Vector& scrollInfo) { m_BitmapFile = bitmapFile; m_MainBitmap = m_BitmapFile.GetAsBitmap(); Create(m_MainBitmap, drawMasked, offset, wrapX, wrapY, scrollInfo); @@ -47,10 +47,10 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - int SceneLayerImpl::Create(BITMAP *bitmap, bool drawMasked, const Vector &offset, bool wrapX, bool wrapY, const Vector &scrollInfo) { + int SceneLayerImpl::Create(BITMAP* bitmap, bool drawMasked, const Vector& offset, bool wrapX, bool wrapY, const Vector& scrollInfo) { m_MainBitmap = bitmap; RTEAssert(m_MainBitmap, "Null bitmap passed in when creating SceneLayerImpl!"); @@ -70,10 +70,10 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - int SceneLayerImpl::Create(const SceneLayerImpl &reference) { + int SceneLayerImpl::Create(const SceneLayerImpl& reference) { Entity::Create(reference); m_BitmapFile = reference.m_BitmapFile; @@ -88,7 +88,7 @@ namespace RTE { if (reference.m_MainBitmap) { // Make a copy of the bitmap because it can be modified in some use cases. - BITMAP *bitmapToCopy = reference.m_MainBitmap; + BITMAP* bitmapToCopy = reference.m_MainBitmap; RTEAssert(bitmapToCopy, "Couldn't load the bitmap file specified for SceneLayerImpl!"); m_MainBitmap = create_bitmap_ex(bitmap_color_depth(bitmapToCopy), bitmapToCopy->w, bitmapToCopy->h); @@ -108,24 +108,23 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - int SceneLayerImpl::ReadProperty(const std::string_view &propName, Reader &reader) { + int SceneLayerImpl::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("WrapX", { reader >> m_WrapX; }); MatchProperty("WrapY", { reader >> m_WrapY; }); MatchProperty("BitmapFile", { reader >> m_BitmapFile; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - int SceneLayerImpl::Save(Writer &writer) const { + int SceneLayerImpl::Save(Writer& writer) const { Entity::Save(writer); writer.NewPropertyWithValue("WrapX", m_WrapX); @@ -135,17 +134,23 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void SceneLayerImpl::Destroy(bool notInherited) { - if (m_MainBitmapOwned) { destroy_bitmap(m_MainBitmap); } - if (m_BackBitmap) { destroy_bitmap(m_BackBitmap); } - if (!notInherited) { Entity::Destroy(); } + if (m_MainBitmapOwned) { + destroy_bitmap(m_MainBitmap); + } + if (m_BackBitmap) { + destroy_bitmap(m_BackBitmap); + } + if (!notInherited) { + Entity::Destroy(); + } Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void SceneLayerImpl::InitScrollRatios(bool initForNetworkPlayer, int player) { @@ -183,7 +188,7 @@ namespace RTE { m_ScaledDimensions.SetXY(mainBitmapWidth * m_ScaleFactor.GetX(), mainBitmapHeight * m_ScaleFactor.GetY()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template int SceneLayerImpl::LoadData() { @@ -198,20 +203,20 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - int SceneLayerImpl::SaveData(const std::string &bitmapPath, bool doAsyncSaves) { + int SceneLayerImpl::SaveData(const std::string& bitmapPath, bool doAsyncSaves) { if (bitmapPath.empty()) { return -1; } if (m_MainBitmap) { // Make a copy of the bitmap to pass to the thread because the bitmap may be offloaded mid thread and everything will be on fire. - BITMAP *outputBitmap = create_bitmap_ex(bitmap_color_depth(m_MainBitmap), m_MainBitmap->w, m_MainBitmap->h); + BITMAP* outputBitmap = create_bitmap_ex(bitmap_color_depth(m_MainBitmap), m_MainBitmap->w, m_MainBitmap->h); blit(m_MainBitmap, outputBitmap, 0, 0, 0, 0, m_MainBitmap->w, m_MainBitmap->h); - auto saveLayerBitmap = [bitmapPath, doAsyncSaves](BITMAP *bitmapToSave) { + auto saveLayerBitmap = [bitmapPath, doAsyncSaves](BITMAP* bitmapToSave) { PALETTE palette; get_palette(palette); if (save_png(bitmapPath.c_str(), bitmapToSave, palette) != 0) { @@ -222,7 +227,7 @@ namespace RTE { m_BitmapFile.SetDataPath(bitmapPath); if (doAsyncSaves) { - g_ActivityMan.GetSaveGameTask().push_back( g_ThreadMan.GetBackgroundThreadPool().submit(saveLayerBitmap, outputBitmap) ); + g_ActivityMan.GetSaveGameTask().push_back(g_ThreadMan.GetBackgroundThreadPool().submit(saveLayerBitmap, outputBitmap)); } else { saveLayerBitmap(outputBitmap); } @@ -230,7 +235,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template std::unique_ptr SceneLayerImpl::CopyBitmap() { @@ -242,30 +247,36 @@ namespace RTE { return std::unique_ptr(outputBitmap); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template int SceneLayerImpl::ClearData() { - if (m_MainBitmap && m_MainBitmapOwned) { destroy_bitmap(m_MainBitmap); } + if (m_MainBitmap && m_MainBitmapOwned) { + destroy_bitmap(m_MainBitmap); + } m_MainBitmap = nullptr; m_MainBitmapOwned = false; - if (m_BackBitmap) { destroy_bitmap(m_BackBitmap); } + if (m_BackBitmap) { + destroy_bitmap(m_BackBitmap); + } m_BackBitmap = nullptr; m_LastClearColor = ColorKeys::g_InvalidColor; return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - void SceneLayerImpl::SetScaleFactor(const Vector &newScale) { + void SceneLayerImpl::SetScaleFactor(const Vector& newScale) { m_ScaleFactor = newScale; - if (m_MainBitmap) { m_ScaledDimensions.SetXY(static_cast(m_MainBitmap->w) * newScale.GetX(), static_cast(m_MainBitmap->h) * newScale.GetY()); } + if (m_MainBitmap) { + m_ScaledDimensions.SetXY(static_cast(m_MainBitmap->w) * newScale.GetX(), static_cast(m_MainBitmap->h) * newScale.GetY()); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template int SceneLayerImpl::GetPixel(int pixelX, int pixelY) const { @@ -273,7 +284,7 @@ namespace RTE { return (pixelX < 0 || pixelX >= m_MainBitmap->w || pixelY < 0 || pixelY >= m_MainBitmap->h) ? MaterialColorKeys::g_MaterialAir : _getpixel(m_MainBitmap, pixelX, pixelY); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void SceneLayerImpl::SetPixel(int pixelX, int pixelY, int materialID) { @@ -289,14 +300,14 @@ namespace RTE { RegisterDrawing(pixelX, pixelY, pixelX, pixelY); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template bool SceneLayerImpl::IsWithinBounds(const int pixelX, const int pixelY, const int margin) const { return (m_WrapX || (pixelX >= -margin && pixelX < m_MainBitmap->w + margin)) && (m_WrapY || (pixelY >= -margin && pixelY < m_MainBitmap->h + margin)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void SceneLayerImpl::ClearBitmap(ColorKeys clearTo) { @@ -315,18 +326,19 @@ namespace RTE { std::swap(m_MainBitmap, m_BackBitmap); // Start a new thread to clear the backbuffer bitmap asynchronously. - m_BitmapClearTask = g_ThreadMan.GetPriorityThreadPool().submit([this, clearTo](BITMAP *bitmap, std::vector drawings) { + m_BitmapClearTask = g_ThreadMan.GetPriorityThreadPool().submit([this, clearTo](BITMAP* bitmap, std::vector drawings) { ZoneScopedN("Clear Tracked Backbuffer"); ClearDrawings(bitmap, drawings, clearTo); - }, m_BackBitmap, m_Drawings); + }, + m_BackBitmap, m_Drawings); m_Drawings.clear(); // This was copied into the new thread, so can be safely deleted. } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - bool SceneLayerImpl::WrapPosition(int &posX, int &posY) const { + bool SceneLayerImpl::WrapPosition(int& posX, int& posY) const { int oldX = posX; int oldY = posY; @@ -349,10 +361,10 @@ namespace RTE { return oldX != posX || oldY != posY; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - bool SceneLayerImpl::ForceBounds(int &posX, int &posY) const { + bool SceneLayerImpl::ForceBounds(int& posX, int& posY) const { bool wrapped = false; int width = m_ScaledDimensions.GetFloorIntX(); int height = m_ScaledDimensions.GetFloorIntY(); @@ -396,10 +408,10 @@ namespace RTE { return wrapped; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - bool SceneLayerImpl::ForceBoundsOrWrapPosition(Vector &pos, bool forceBounds) const { + bool SceneLayerImpl::ForceBoundsOrWrapPosition(Vector& pos, bool forceBounds) const { int posX = pos.GetFloorIntX(); int posY = pos.GetFloorIntY(); bool wrapped = forceBounds ? ForceBounds(posX, posY) : WrapPosition(posX, posY); @@ -408,7 +420,7 @@ namespace RTE { return wrapped; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void SceneLayerImpl::RegisterDrawing(int left, int top, int right, int bottom) { @@ -417,25 +429,33 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - void SceneLayerImpl::RegisterDrawing(const Vector ¢er, float radius) { + void SceneLayerImpl::RegisterDrawing(const Vector& center, float radius) { if (radius != 0.0F) { RegisterDrawing(static_cast(center.GetX() - radius), static_cast(center.GetY() - radius), static_cast(center.GetX() + radius), static_cast(center.GetY() + radius)); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - void SceneLayerImpl::Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment) { + void SceneLayerImpl::Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment) { RTEAssert(m_MainBitmap, "Data of this SceneLayerImpl has not been loaded before trying to draw!"); - if (offsetNeedsScrollRatioAdjustment) { m_Offset.SetXY(std::floor(m_Offset.GetX() * m_ScrollRatio.GetX()), std::floor(m_Offset.GetY() * m_ScrollRatio.GetY())); } - if (targetBox.IsEmpty()) { targetBox = Box(Vector(), static_cast(targetBitmap->w), static_cast(targetBitmap->h)); } - if (!m_WrapX && static_cast(targetBitmap->w) > targetBox.GetWidth()) { m_Offset.SetX(0); } - if (!m_WrapY && static_cast(targetBitmap->h) > targetBox.GetHeight()) { m_Offset.SetY(0); } + if (offsetNeedsScrollRatioAdjustment) { + m_Offset.SetXY(std::floor(m_Offset.GetX() * m_ScrollRatio.GetX()), std::floor(m_Offset.GetY() * m_ScrollRatio.GetY())); + } + if (targetBox.IsEmpty()) { + targetBox = Box(Vector(), static_cast(targetBitmap->w), static_cast(targetBitmap->h)); + } + if (!m_WrapX && static_cast(targetBitmap->w) > targetBox.GetWidth()) { + m_Offset.SetX(0); + } + if (!m_WrapY && static_cast(targetBitmap->h) > targetBox.GetHeight()) { + m_Offset.SetY(0); + } m_Offset -= m_OriginOffset; WrapPosition(m_Offset); @@ -451,17 +471,17 @@ namespace RTE { set_clip_rect(targetBitmap, 0, 0, targetBitmap->w - 1, targetBitmap->h - 1); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - void SceneLayerImpl::DrawWrapped(BITMAP *targetBitmap, const Box &targetBox, bool drawScaled) const { + void SceneLayerImpl::DrawWrapped(BITMAP* targetBitmap, const Box& targetBox, bool drawScaled) const { if (!drawScaled) { - std::array sourcePosX = { m_Offset.GetFloorIntX(), 0 }; - std::array sourcePosY = { m_Offset.GetFloorIntY(), 0 }; - std::array sourceWidth = { m_MainBitmap->w - m_Offset.GetFloorIntX(), m_Offset.GetFloorIntX() }; - std::array sourceHeight = { m_MainBitmap->h - m_Offset.GetFloorIntY(), m_Offset.GetFloorIntY() }; - std::array destPosX = { targetBox.GetCorner().GetFloorIntX(), targetBox.GetCorner().GetFloorIntX() + m_MainBitmap->w - m_Offset.GetFloorIntX() }; - std::array destPosY = { targetBox.GetCorner().GetFloorIntY(), targetBox.GetCorner().GetFloorIntY() + m_MainBitmap->h - m_Offset.GetFloorIntY() }; + std::array sourcePosX = {m_Offset.GetFloorIntX(), 0}; + std::array sourcePosY = {m_Offset.GetFloorIntY(), 0}; + std::array sourceWidth = {m_MainBitmap->w - m_Offset.GetFloorIntX(), m_Offset.GetFloorIntX()}; + std::array sourceHeight = {m_MainBitmap->h - m_Offset.GetFloorIntY(), m_Offset.GetFloorIntY()}; + std::array destPosX = {targetBox.GetCorner().GetFloorIntX(), targetBox.GetCorner().GetFloorIntX() + m_MainBitmap->w - m_Offset.GetFloorIntX()}; + std::array destPosY = {targetBox.GetCorner().GetFloorIntY(), targetBox.GetCorner().GetFloorIntY() + m_MainBitmap->h - m_Offset.GetFloorIntY()}; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { @@ -473,10 +493,10 @@ namespace RTE { } } } else { - std::array sourceWidth = { m_MainBitmap->w, m_Offset.GetFloorIntX() / m_ScaleFactor.GetFloorIntX() }; - std::array sourceHeight = { m_MainBitmap->h, m_Offset.GetFloorIntY() / m_ScaleFactor.GetFloorIntY() }; - std::array destPosX = { targetBox.GetCorner().GetFloorIntX() - m_Offset.GetFloorIntX(), targetBox.GetCorner().GetFloorIntX() + m_ScaledDimensions.GetFloorIntX() - m_Offset.GetFloorIntX() }; - std::array destPosY = { targetBox.GetCorner().GetFloorIntY() - m_Offset.GetFloorIntY(), targetBox.GetCorner().GetFloorIntY() + m_ScaledDimensions.GetFloorIntY() - m_Offset.GetFloorIntY() }; + std::array sourceWidth = {m_MainBitmap->w, m_Offset.GetFloorIntX() / m_ScaleFactor.GetFloorIntX()}; + std::array sourceHeight = {m_MainBitmap->h, m_Offset.GetFloorIntY() / m_ScaleFactor.GetFloorIntY()}; + std::array destPosX = {targetBox.GetCorner().GetFloorIntX() - m_Offset.GetFloorIntX(), targetBox.GetCorner().GetFloorIntX() + m_ScaledDimensions.GetFloorIntX() - m_Offset.GetFloorIntX()}; + std::array destPosY = {targetBox.GetCorner().GetFloorIntY() - m_Offset.GetFloorIntY(), targetBox.GetCorner().GetFloorIntY() + m_ScaledDimensions.GetFloorIntY() - m_Offset.GetFloorIntY()}; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { @@ -490,10 +510,10 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - void SceneLayerImpl::DrawTiled(BITMAP *targetBitmap, const Box &targetBox, bool drawScaled) const { + void SceneLayerImpl::DrawTiled(BITMAP* targetBitmap, const Box& targetBox, bool drawScaled) const { int bitmapWidth = m_ScaledDimensions.GetFloorIntX(); int bitmapHeight = m_ScaledDimensions.GetFloorIntY(); int areaToCoverX = m_Offset.GetFloorIntX() + targetBox.GetCorner().GetFloorIntX() + std::min(targetBitmap->w, static_cast(targetBox.GetWidth())); @@ -530,12 +550,12 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - void SceneLayerImpl::ClearDrawings(BITMAP* bitmap, const std::vector &drawings, ColorKeys clearTo) const { + void SceneLayerImpl::ClearDrawings(BITMAP* bitmap, const std::vector& drawings, ColorKeys clearTo) const { if constexpr (TRACK_DRAWINGS) { - for (const IntRect &rect : drawings) { + for (const IntRect& rect: drawings) { int left = rect.m_Left; int top = rect.m_Top; int bottom = rect.m_Bottom; @@ -576,9 +596,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Force instantiation template class SceneLayerImpl; template class SceneLayerImpl; -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/SceneLayer.h b/Source/Entities/SceneLayer.h index 4739cce61f..018f699ecc 100644 --- a/Source/Entities/SceneLayer.h +++ b/Source/Entities/SceneLayer.h @@ -15,7 +15,6 @@ namespace RTE { friend class NetworkServer; public: - EntityAllocation(SceneLayerImpl); SerializableOverrideMethods; @@ -46,7 +45,7 @@ namespace RTE { /// A special command is if wrap is false and the corresponding component is -1.0, that signals that the own width or height should be used as scrollInfo input. /// /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const ContentFile &bitmapFile, bool drawMasked, const Vector &offset, bool wrapX, bool wrapY, const Vector &scrollInfo); + int Create(const ContentFile& bitmapFile, bool drawMasked, const Vector& offset, bool wrapX, bool wrapY, const Vector& scrollInfo); /// /// Makes the SceneLayer object ready for use. @@ -63,14 +62,14 @@ namespace RTE { /// A special command is if wrap is false and the corresponding component is -1.0, that signals that the own width or height should be used as scrollInfo input. /// /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(BITMAP *bitmap, bool drawMasked, const Vector &offset, bool wrapX, bool wrapY, const Vector &scrollInfo); + int Create(BITMAP* bitmap, bool drawMasked, const Vector& offset, bool wrapX, bool wrapY, const Vector& scrollInfo); /// /// Creates a SceneLayer to be identical to another, by deep copy. /// /// A reference to the SceneLayer to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const SceneLayerImpl &reference); + int Create(const SceneLayerImpl& reference); #pragma endregion #pragma region Destruction @@ -105,7 +104,7 @@ namespace RTE { /// The filepath to the where to save the bitmap data. /// Whether or not to save asynchronously. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - virtual int SaveData(const std::string &bitmapPath, bool doAsyncSaves = true); + virtual int SaveData(const std::string& bitmapPath, bool doAsyncSaves = true); /// /// Clears out any previously loaded bitmap data from memory. @@ -131,7 +130,7 @@ namespace RTE { /// Sets the scroll offset of this SceneLayer. Observe that this offset will be modified by the scroll ratio before applied. /// /// The new offset Vector. - void SetOffset(const Vector &newOffset) { m_Offset = newOffset; } + void SetOffset(const Vector& newOffset) { m_Offset = newOffset; } /// /// Gets the scroll ratio that modifies the offset. @@ -143,7 +142,7 @@ namespace RTE { /// Sets the scroll ratio of this SceneLayer. This modifies the offset before any actual scrolling occurs. /// /// The new scroll ratio vector. - void SetScrollRatio(const Vector &newRatio) { m_ScrollRatio = newRatio; } + void SetScrollRatio(const Vector& newRatio) { m_ScrollRatio = newRatio; } /// /// Gets the scale factor that this is drawn in. @@ -155,7 +154,7 @@ namespace RTE { /// Sets the scale that this should be drawn at when using DrawScaled. /// /// The new scale factor vector. - void SetScaleFactor(const Vector &newScale); + void SetScaleFactor(const Vector& newScale); /// /// Indicates whether the layer is set to wrap around the X axis when scrolled out of bounds. @@ -200,13 +199,15 @@ namespace RTE { /// Lock the internal bitmap so it can be accessed by GetPixel() etc. UnlockBitmaps() should always be called after accesses are completed. /// Doing it in a separate method like this is more efficient because many bitmap accesses can be performed between a lock and unlock. /// - void LockBitmaps() { /*acquire_bitmap(m_MainBitmap);*/ } + void LockBitmaps() { /*acquire_bitmap(m_MainBitmap);*/ + } /// /// Unlocks the internal bitmaps and prevents access to display memory. UnlockBitmaps() should only be called after LockBitmaps(). /// Doing it in a separate method like this is more efficient because many bitmap accesses can be performed between a lock and an unlock. /// - void UnlockBitmaps() { /*release_bitmap(m_MainBitmap);*/ } + void UnlockBitmaps() { /*release_bitmap(m_MainBitmap);*/ + } /// /// Clears our BITMAP. @@ -221,7 +222,7 @@ namespace RTE { /// The X coordinates of the position to wrap. /// The Y coordinates of the position to wrap. /// Whether wrapping was performed or not. - bool WrapPosition(int &posX, int &posY) const; + bool WrapPosition(int& posX, int& posY) const; /// /// Wraps the given position Vector if it is out of bounds of this SceneLayer and wrapping is enabled on the appropriate axes. @@ -229,7 +230,7 @@ namespace RTE { /// /// The vector coordinates of the position to wrap. /// Whether wrapping was performed or not. - bool WrapPosition(Vector &pos) const { return ForceBoundsOrWrapPosition(pos, false); } + bool WrapPosition(Vector& pos) const { return ForceBoundsOrWrapPosition(pos, false); } /// /// Wraps or bounds a position coordinate if it is out of bounds of the SceneLayer, depending on the wrap settings of this SceneLayer. @@ -237,14 +238,14 @@ namespace RTE { /// The X coordinates of the position to wrap. /// The Y coordinates of the position to wrap. /// Whether wrapping was performed or not. Does not report on bounding. - bool ForceBounds(int &posX, int &posY) const; + bool ForceBounds(int& posX, int& posY) const; /// /// Wraps or bounds a position coordinate if it is out of bounds of the SceneLayer, depending on the wrap settings of this SceneLayer. /// /// The Vector coordinates of the position to wrap. /// Whether wrapping was performed or not. Does not report on bounding. - bool ForceBounds(Vector &pos) const { return ForceBoundsOrWrapPosition(pos, true); } + bool ForceBounds(Vector& pos) const { return ForceBoundsOrWrapPosition(pos, true); } #pragma endregion #pragma region Drawing Tracking @@ -262,7 +263,7 @@ namespace RTE { /// /// The position of the center of the area to be drawn upon. /// The radius of the area to be drawn upon. - void RegisterDrawing(const Vector ¢er, float radius); + void RegisterDrawing(const Vector& center, float radius); #pragma endregion #pragma region Virtual Methods @@ -277,15 +278,14 @@ namespace RTE { /// The bitmap to draw to. /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. /// Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. - virtual void Draw(BITMAP *targetBitmap, Box &targetBox, bool offsetNeedsScrollRatioAdjustment = false); + virtual void Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment = false); #pragma endregion protected: - ContentFile m_BitmapFile; //!< ContentFile containing the path to this SceneLayer's sprite file. - BITMAP *m_MainBitmap; //!< The main BITMAP of this SceneLayer. - BITMAP *m_BackBitmap; //!< The backbuffer BITMAP of this SceneLayer. + BITMAP* m_MainBitmap; //!< The main BITMAP of this SceneLayer. + BITMAP* m_BackBitmap; //!< The backbuffer BITMAP of this SceneLayer. // We use two bitmaps, as a backbuffer. While the main bitmap is being used, the secondary bitmap will be cleared on a separate thread. This is because we tend to want to clear some scene layers every frame and that is costly. std::future m_BitmapClearTask; //!< Task for clearing BITMAP async in background. @@ -319,7 +319,7 @@ namespace RTE { /// The Vector coordinates of the position to wrap. /// Whether to attempt bounding or wrapping, or just wrapping. /// Whether wrapping was performed or not. Does not report on bounding. - bool ForceBoundsOrWrapPosition(Vector &pos, bool forceBounds) const; + bool ForceBoundsOrWrapPosition(Vector& pos, bool forceBounds) const; #pragma region Draw Breakdown /// @@ -328,7 +328,7 @@ namespace RTE { /// The bitmap to draw to. /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. /// Whether to use scaled drawing routines or not. - void DrawWrapped(BITMAP *targetBitmap, const Box &targetBox, bool drawScaled) const; + void DrawWrapped(BITMAP* targetBitmap, const Box& targetBox, bool drawScaled) const; /// /// Performs tiled drawing of this SceneLayer's bitmap to the screen in cases where the target bitmap is larger in some dimension. @@ -336,16 +336,15 @@ namespace RTE { /// The bitmap to draw to. /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. /// Whether to use scaled drawing routines or not. - void DrawTiled(BITMAP *targetBitmap, const Box &targetBox, bool drawScaled) const; + void DrawTiled(BITMAP* targetBitmap, const Box& targetBox, bool drawScaled) const; #pragma endregion private: - /// /// Clears any tracked and drawn-to areas. /// /// Color to clear to. - void ClearDrawings(BITMAP *bitmap, const std::vector &drawings, ColorKeys clearTo) const; + void ClearDrawings(BITMAP* bitmap, const std::vector& drawings, ColorKeys clearTo) const; /// /// Clears all the member variables of this SceneLayer, effectively resetting the members of this abstraction level only. @@ -353,8 +352,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - SceneLayerImpl(const SceneLayerImpl &reference) = delete; - void operator=(const SceneLayerImpl &rhs) = delete; + SceneLayerImpl(const SceneLayerImpl& reference) = delete; + void operator=(const SceneLayerImpl& rhs) = delete; }; /// @@ -363,14 +362,14 @@ namespace RTE { class SceneLayerTracked : public SceneLayerImpl { public: - EntityAllocation(SceneLayerTracked); ClassInfoGetters; /// /// Constructor method used to instantiate a SceneLayerTracked object in system memory. Create() should be called before using the object. /// - SceneLayerTracked() : SceneLayerImpl() {} + SceneLayerTracked() : + SceneLayerImpl() {} // TODO: We shouldn't let external users access a non-const version of our bitmap. We should do all drawing to it internally, and track registering our MOID drawings internally too. // However, in the interest of time (and my own sanity), given that the old code already does this, we're not doing that yet. @@ -378,34 +377,32 @@ namespace RTE { /// Gets the BITMAP that this SceneLayer uses. /// /// A pointer to the BITMAP of this SceneLayer. Ownership is NOT transferred! - BITMAP * GetBitmap() const { return m_MainBitmap; } + BITMAP* GetBitmap() const { return m_MainBitmap; } protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. }; class SceneLayer : public SceneLayerImpl { public: - EntityAllocation(SceneLayer); ClassInfoGetters; /// /// Constructor method used to instantiate a SceneLayer object in system memory. Create() should be called before using the object. /// - SceneLayer() : SceneLayerImpl() {} + SceneLayer() : + SceneLayerImpl() {} /// /// Gets the BITMAP that this SceneLayer uses. /// /// A pointer to the BITMAP of this SceneLayer. Ownership is NOT transferred! - BITMAP * GetBitmap() const { return m_MainBitmap; } + BITMAP* GetBitmap() const { return m_MainBitmap; } protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/SceneObject.cpp b/Source/Entities/SceneObject.cpp index 1fd3df4dd9..dbcb50ed8e 100644 --- a/Source/Entities/SceneObject.cpp +++ b/Source/Entities/SceneObject.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -19,388 +18,351 @@ namespace RTE { -AbstractClassInfo(SceneObject, Entity); -const std::string SceneObject::SOPlacer::c_ClassName = "SOPlacer"; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SOPlacer, effectively -// resetting the members of this abstraction level only. - -void SceneObject::SOPlacer::Clear() -{ - m_pObjectReference = 0; - m_Offset.Reset(); - m_RotAngle = 0; - m_HFlipped = false; - m_Team = Activity::NoTeam; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SOPlacer object ready for use. - -int SceneObject::SOPlacer::Create() -{ - if (Serializable::Create() < 0) - return -1; - - return 0; -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a SOPlacer to be identical to another, by deep copy. - -int SceneObject::SOPlacer::Create(const SOPlacer &reference) -{ - m_pObjectReference = reference.m_pObjectReference; - m_Offset = reference.m_Offset; - m_RotAngle = reference.m_RotAngle; - m_HFlipped = reference.m_HFlipped; - m_Team = reference.m_Team; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int SceneObject::SOPlacer::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Serializable::ReadProperty(propName, reader)); - - MatchProperty("PlacedObject", - { - m_pObjectReference = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); - RTEAssert(m_pObjectReference, "Stream suggests allocating an unallocatable type in SOPlacer::Create!"); - }); - MatchProperty("Offset", { reader >> m_Offset; }); - MatchProperty("Rotation", - { - Matrix rot; - reader >> rot; - m_RotAngle = rot.GetRadAngle(); - }); - MatchProperty("HFlipped", { reader >> m_HFlipped; }); - MatchProperty("Team", { reader >> m_Team; }); - - EndPropertyList; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this SOPlacer with a Writer for -// later recreation with Create(Reader &reader); - -int SceneObject::SOPlacer::Save(Writer &writer) const -{ - Serializable::Save(writer); - - writer.NewProperty("PlacedObject"); - writer << m_pObjectReference; - writer.NewProperty("Offset"); - writer << m_Offset; -// TODO: make generalized way of detecting defaults - if (m_RotAngle != 0) - { - writer.NewProperty("Rotation"); - Matrix rot; - rot.SetRadAngle(m_RotAngle); - writer << rot; - } - if (m_HFlipped) - { - writer.NewProperty("HFlipped"); - writer << m_HFlipped; - } - if (m_Team >= Activity::TeamOne) - { - writer.NewProperty("Team"); - writer << m_Team; - } - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlacedCopy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes a copy of the preset instance, and applies the placement -// properties of this to it, finally returning it WITH OWNERSHIP. - -SceneObject * SceneObject::SOPlacer::GetPlacedCopy(const SceneObject *pParent) const -{ - RTEAssert(m_pObjectReference, "No Object reference to make copy from!"); - - SceneObject *pCopy = dynamic_cast(m_pObjectReference->Clone()); - if (pCopy) - { - // Make relative to the parent - if (pParent) - { - // Relative flipping (XOR) - bool placedFlip = (pParent->IsHFlipped() && m_HFlipped) ? false : (pParent->IsHFlipped() || m_HFlipped); - pCopy->SetHFlipped(placedFlip); - - // Relative rotation - Matrix placedRot(pParent->GetRotAngle() + m_RotAngle); - pCopy->SetRotAngle(placedRot.GetRadAngle()); - - // Relative position - pCopy->SetPos(pParent->GetPos() + m_Offset.GetXFlipped(pParent->IsHFlipped()) * pParent->GetRotMatrix()); - - // Relative team (parent overrides, if it has a set team) - if (pParent->GetTeam() >= 0) - pCopy->SetTeam(pParent->GetTeam()); - else - pCopy->SetTeam(m_Team); - } - // No parent to make relative from, so apply everything as aboslutes. - else - { - pCopy->SetHFlipped(m_HFlipped); - pCopy->SetRotAngle(m_RotAngle);//m_HFlipped ? -m_RotAngle : m_RotAngle); - pCopy->SetPos(m_Offset); - pCopy->SetTeam(m_Team); - } - } - - // Transfer ownership here - return pCopy; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SceneObject, effectively -// resetting the members of this abstraction level only. - -void SceneObject::Clear() -{ - m_Pos.Reset(); - m_OzValue = 0; - m_Buyable = true; - m_BuyableMode = BuyableMode::NoRestrictions; - m_Team = Activity::NoTeam; - m_PlacedByPlayer = Players::NoPlayer; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneObject object ready for use. - -int SceneObject::Create() -{ - if (Entity::Create() < 0) - return -1; - - return 0; -} -*/ -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneObject object ready for use. - -int SceneObject::Create() -{ - - return 0; -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates an SceneObject object to be identical to another, by deep copy. - -int SceneObject::Create(const SceneObject &reference) -{ - Entity::Create(reference); - - m_Pos = reference.m_Pos; - m_OzValue = reference.m_OzValue; - m_Buyable = reference.m_Buyable; - m_BuyableMode = reference.m_BuyableMode; - m_Team = reference.m_Team; - m_PlacedByPlayer = reference.m_PlacedByPlayer; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a Reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the Reader's position is untouched. - -int SceneObject::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Entity::ReadProperty(propName, reader)); - - MatchProperty("Position", { reader >> m_Pos; }); - MatchForwards("GoldValue") MatchProperty("GoldCost", { reader >> m_OzValue; }); - MatchProperty("Buyable", { reader >> m_Buyable; }); - MatchProperty("BuyableMode", { m_BuyableMode = static_cast(std::stoi(reader.ReadPropValue())); }); - - MatchProperty("Team", - { - reader >> m_Team; - // Necessary to properly init (flag icons) some derived classes - // (actually, this rarely matters since tehre won't be an activity going when this is read!) - SetTeam(m_Team); - }); - MatchProperty("PlacedByPlayer", { reader >> m_PlacedByPlayer; }); - - EndPropertyList; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this SceneObject to an output stream for -// later recreation with Create(istream &stream); - -int SceneObject::Save(Writer &writer) const -{ - Entity::Save(writer); -// TODO: Make proper save system that knows not to save redundant data! -/* - writer.NewProperty("Position"); - writer << m_Pos; - writer.NewProperty("GoldValue"); - writer << m_OzValue; - writer.NewProperty("Buyable"); - writer << m_Buyable; - writer.NewProperty("BuyableMode"); - writer << static_cast(m_BuyableMode); - writer.NewProperty("Team"); - writer << m_Team; - writer.NewProperty("PlacedByPlayer"); - writer << m_PlacedByPlayer; -*/ - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneObject object. - -void SceneObject::Destroy(bool notInherited) -{ - - if (!notInherited) - Entity::Destroy(); - Clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the cost to purchase this item, in oz's of gold. - -float SceneObject::GetGoldValue(int nativeModule, float foreignMult, float nativeMult) const -{ - // Multiply the value of this according to whether its Tech is native or not to the specified DataModule - return m_OzValue * ((m_DefinedInModule > 0 && nativeModule > 0 && m_DefinedInModule != nativeModule) ? foreignMult : nativeMult); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldValueString -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a descriptive string describing the cost to purchase this item, -// in oz's of gold. - -std::string SceneObject::GetGoldValueString(int nativeModule, float foreignMult, float nativeMult) const -{ - float subjValue = GetGoldValue(nativeModule, foreignMult, nativeMult); - - char returnString[64]; - if (subjValue != 0) - { - // Just show number since adding oz at the end takes up too much space - std::snprintf(returnString, sizeof(returnString), "%.0f", subjValue); - } - else - return "FREE"; - - return returnString; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawTeamMark -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws team sign this terrain object belongs to. -void SceneObject::DrawTeamMark(BITMAP *pTargetBitmap, const Vector &targetPos) const -{ - // Only do HUD if on a team - if (m_Team < 0) - return; - - Vector drawPos = m_Pos - targetPos; - - // Adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam - if (!targetPos.IsZero()) - { - // Spans vertical scene seam - int sceneWidth = g_SceneMan.GetSceneWidth(); - if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) - { - if ((targetPos.m_X < 0) && (m_Pos.m_X > (sceneWidth - pTargetBitmap->w))) - drawPos.m_X -= sceneWidth; - else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_Pos.m_X < pTargetBitmap->w)) - drawPos.m_X += sceneWidth; - } - // Spans horizontal scene seam - int sceneHeight = g_SceneMan.GetSceneHeight(); - if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) - { - if ((targetPos.m_Y < 0) && (m_Pos.m_Y > (sceneHeight - pTargetBitmap->h))) - drawPos.m_Y -= sceneHeight; - else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_Pos.m_Y < pTargetBitmap->h)) - drawPos.m_Y += sceneHeight; - } - } - - // Get the Icon bitmaps of this Actor's team, if any - BITMAP * teamIcon = g_ActivityMan.GetActivity()->GetTeamIcon(m_Team)->GetBitmaps8()[0]; - - // Now draw the Icon if we can - if (teamIcon) - { - // Make team icon blink faster as the health goes down - masked_blit(teamIcon, pTargetBitmap, 0, 0, drawPos.m_X - teamIcon->h / 2, drawPos.m_Y - teamIcon->h * 2, teamIcon->w, teamIcon->h); - } -} - + AbstractClassInfo(SceneObject, Entity); + const std::string SceneObject::SOPlacer::c_ClassName = "SOPlacer"; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this SOPlacer, effectively + // resetting the members of this abstraction level only. + + void SceneObject::SOPlacer::Clear() { + m_pObjectReference = 0; + m_Offset.Reset(); + m_RotAngle = 0; + m_HFlipped = false; + m_Team = Activity::NoTeam; + } + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the SOPlacer object ready for use. + + int SceneObject::SOPlacer::Create() + { + if (Serializable::Create() < 0) + return -1; + + return 0; + } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a SOPlacer to be identical to another, by deep copy. + + int SceneObject::SOPlacer::Create(const SOPlacer& reference) { + m_pObjectReference = reference.m_pObjectReference; + m_Offset = reference.m_Offset; + m_RotAngle = reference.m_RotAngle; + m_HFlipped = reference.m_HFlipped; + m_Team = reference.m_Team; + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. + + int SceneObject::SOPlacer::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Serializable::ReadProperty(propName, reader)); + + MatchProperty("PlacedObject", + { + m_pObjectReference = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); + RTEAssert(m_pObjectReference, "Stream suggests allocating an unallocatable type in SOPlacer::Create!"); + }); + MatchProperty("Offset", { reader >> m_Offset; }); + MatchProperty("Rotation", + { + Matrix rot; + reader >> rot; + m_RotAngle = rot.GetRadAngle(); + }); + MatchProperty("HFlipped", { reader >> m_HFlipped; }); + MatchProperty("Team", { reader >> m_Team; }); + + EndPropertyList; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this SOPlacer with a Writer for + // later recreation with Create(Reader &reader); + + int SceneObject::SOPlacer::Save(Writer& writer) const { + Serializable::Save(writer); + + writer.NewProperty("PlacedObject"); + writer << m_pObjectReference; + writer.NewProperty("Offset"); + writer << m_Offset; + // TODO: make generalized way of detecting defaults + if (m_RotAngle != 0) { + writer.NewProperty("Rotation"); + Matrix rot; + rot.SetRadAngle(m_RotAngle); + writer << rot; + } + if (m_HFlipped) { + writer.NewProperty("HFlipped"); + writer << m_HFlipped; + } + if (m_Team >= Activity::TeamOne) { + writer.NewProperty("Team"); + writer << m_Team; + } + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlacedCopy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes a copy of the preset instance, and applies the placement + // properties of this to it, finally returning it WITH OWNERSHIP. + + SceneObject* SceneObject::SOPlacer::GetPlacedCopy(const SceneObject* pParent) const { + RTEAssert(m_pObjectReference, "No Object reference to make copy from!"); + + SceneObject* pCopy = dynamic_cast(m_pObjectReference->Clone()); + if (pCopy) { + // Make relative to the parent + if (pParent) { + // Relative flipping (XOR) + bool placedFlip = (pParent->IsHFlipped() && m_HFlipped) ? false : (pParent->IsHFlipped() || m_HFlipped); + pCopy->SetHFlipped(placedFlip); + + // Relative rotation + Matrix placedRot(pParent->GetRotAngle() + m_RotAngle); + pCopy->SetRotAngle(placedRot.GetRadAngle()); + + // Relative position + pCopy->SetPos(pParent->GetPos() + m_Offset.GetXFlipped(pParent->IsHFlipped()) * pParent->GetRotMatrix()); + + // Relative team (parent overrides, if it has a set team) + if (pParent->GetTeam() >= 0) + pCopy->SetTeam(pParent->GetTeam()); + else + pCopy->SetTeam(m_Team); + } + // No parent to make relative from, so apply everything as aboslutes. + else { + pCopy->SetHFlipped(m_HFlipped); + pCopy->SetRotAngle(m_RotAngle); // m_HFlipped ? -m_RotAngle : m_RotAngle); + pCopy->SetPos(m_Offset); + pCopy->SetTeam(m_Team); + } + } + + // Transfer ownership here + return pCopy; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this SceneObject, effectively + // resetting the members of this abstraction level only. + + void SceneObject::Clear() { + m_Pos.Reset(); + m_OzValue = 0; + m_Buyable = true; + m_BuyableMode = BuyableMode::NoRestrictions; + m_Team = Activity::NoTeam; + m_PlacedByPlayer = Players::NoPlayer; + } + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the SceneObject object ready for use. + + int SceneObject::Create() + { + if (Entity::Create() < 0) + return -1; + + return 0; + } + */ + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the SceneObject object ready for use. + + int SceneObject::Create() + { + + return 0; + } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates an SceneObject object to be identical to another, by deep copy. + + int SceneObject::Create(const SceneObject& reference) { + Entity::Create(reference); + + m_Pos = reference.m_Pos; + m_OzValue = reference.m_OzValue; + m_Buyable = reference.m_Buyable; + m_BuyableMode = reference.m_BuyableMode; + m_Team = reference.m_Team; + m_PlacedByPlayer = reference.m_PlacedByPlayer; + + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a Reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the Reader's position is untouched. + + int SceneObject::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Entity::ReadProperty(propName, reader)); + + MatchProperty("Position", { reader >> m_Pos; }); + MatchForwards("GoldValue") MatchProperty("GoldCost", { reader >> m_OzValue; }); + MatchProperty("Buyable", { reader >> m_Buyable; }); + MatchProperty("BuyableMode", { m_BuyableMode = static_cast(std::stoi(reader.ReadPropValue())); }); + + MatchProperty("Team", + { + reader >> m_Team; + // Necessary to properly init (flag icons) some derived classes + // (actually, this rarely matters since tehre won't be an activity going when this is read!) + SetTeam(m_Team); + }); + MatchProperty("PlacedByPlayer", { reader >> m_PlacedByPlayer; }); + + EndPropertyList; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this SceneObject to an output stream for + // later recreation with Create(istream &stream); + + int SceneObject::Save(Writer& writer) const { + Entity::Save(writer); + // TODO: Make proper save system that knows not to save redundant data! + /* + writer.NewProperty("Position"); + writer << m_Pos; + writer.NewProperty("GoldValue"); + writer << m_OzValue; + writer.NewProperty("Buyable"); + writer << m_Buyable; + writer.NewProperty("BuyableMode"); + writer << static_cast(m_BuyableMode); + writer.NewProperty("Team"); + writer << m_Team; + writer.NewProperty("PlacedByPlayer"); + writer << m_PlacedByPlayer; + */ + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneObject object. + + void SceneObject::Destroy(bool notInherited) { + + if (!notInherited) + Entity::Destroy(); + Clear(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the cost to purchase this item, in oz's of gold. + + float SceneObject::GetGoldValue(int nativeModule, float foreignMult, float nativeMult) const { + // Multiply the value of this according to whether its Tech is native or not to the specified DataModule + return m_OzValue * ((m_DefinedInModule > 0 && nativeModule > 0 && m_DefinedInModule != nativeModule) ? foreignMult : nativeMult); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldValueString + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a descriptive string describing the cost to purchase this item, + // in oz's of gold. + + std::string SceneObject::GetGoldValueString(int nativeModule, float foreignMult, float nativeMult) const { + float subjValue = GetGoldValue(nativeModule, foreignMult, nativeMult); + + char returnString[64]; + if (subjValue != 0) { + // Just show number since adding oz at the end takes up too much space + std::snprintf(returnString, sizeof(returnString), "%.0f", subjValue); + } else + return "FREE"; + + return returnString; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawTeamMark + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws team sign this terrain object belongs to. + void SceneObject::DrawTeamMark(BITMAP* pTargetBitmap, const Vector& targetPos) const { + // Only do HUD if on a team + if (m_Team < 0) + return; + + Vector drawPos = m_Pos - targetPos; + + // Adjust the draw position to work if drawn to a target screen bitmap that is straddling a scene seam + if (!targetPos.IsZero()) { + // Spans vertical scene seam + int sceneWidth = g_SceneMan.GetSceneWidth(); + if (g_SceneMan.SceneWrapsX() && pTargetBitmap->w < sceneWidth) { + if ((targetPos.m_X < 0) && (m_Pos.m_X > (sceneWidth - pTargetBitmap->w))) + drawPos.m_X -= sceneWidth; + else if (((targetPos.m_X + pTargetBitmap->w) > sceneWidth) && (m_Pos.m_X < pTargetBitmap->w)) + drawPos.m_X += sceneWidth; + } + // Spans horizontal scene seam + int sceneHeight = g_SceneMan.GetSceneHeight(); + if (g_SceneMan.SceneWrapsY() && pTargetBitmap->h < sceneHeight) { + if ((targetPos.m_Y < 0) && (m_Pos.m_Y > (sceneHeight - pTargetBitmap->h))) + drawPos.m_Y -= sceneHeight; + else if (((targetPos.m_Y + pTargetBitmap->h) > sceneHeight) && (m_Pos.m_Y < pTargetBitmap->h)) + drawPos.m_Y += sceneHeight; + } + } + + // Get the Icon bitmaps of this Actor's team, if any + BITMAP* teamIcon = g_ActivityMan.GetActivity()->GetTeamIcon(m_Team)->GetBitmaps8()[0]; + + // Now draw the Icon if we can + if (teamIcon) { + // Make team icon blink faster as the health goes down + masked_blit(teamIcon, pTargetBitmap, 0, 0, drawPos.m_X - teamIcon->h / 2, drawPos.m_Y - teamIcon->h * 2, teamIcon->w, teamIcon->h); + } + } } // namespace RTE diff --git a/Source/Entities/SceneObject.h b/Source/Entities/SceneObject.h index 5667c4719f..1ee5a2ae86 100644 --- a/Source/Entities/SceneObject.h +++ b/Source/Entities/SceneObject.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files, forward declarations, namespace stuff @@ -19,594 +18,547 @@ struct BITMAP; -namespace RTE -{ - -////////////////////////////////////////////////////////////////////////////////////////// -// Abstract class: SceneObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The base class shared by Both TerrainObject:s and MovableObject:s, ie -// anything that can be places in a scene. -// Parent(s): Entity. -// Class history: 8/6/2007 SceneObject created. - -class SceneObject : public Entity { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - SerializableOverrideMethods; - ClassInfoGetters; - - /// - /// Enumeration for the different buyable modes of this SceneObject. - /// - enum class BuyableMode { NoRestrictions, BuyMenuOnly, ObjectPickerOnly, ScriptOnly }; +namespace RTE { + ////////////////////////////////////////////////////////////////////////////////////////// + // Abstract class: SceneObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The base class shared by Both TerrainObject:s and MovableObject:s, ie + // anything that can be places in a scene. + // Parent(s): Entity. + // Class history: 8/6/2007 SceneObject created. - ////////////////////////////////////////////////////////////////////////////////////////// - // Nested class: SOPlacer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Acts as a small memory object that only holds a pointer to a reference - // instance and the most essential properties to eventually place a copy - // of that reference when needed. - // Parent(s): Serializable. - // Class history: 11/25/2007 SOPlacer created. + class SceneObject : public Entity { - class SOPlacer: - public Serializable - { + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - - public: - - SerializableClassNameGetter; + public: SerializableOverrideMethods; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: SOPlacer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a SOPlacer object in system - // memory. Create() should be called before using the object. - // Arguments: None. - - SOPlacer() { Clear(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a SOPlacer to be identical to another, by deep copy. - // Arguments: A reference to the SOPlacer to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - - int Create(const SOPlacer &reference); - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Serializable, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - - void Reset() override { Clear(); } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetObjectReference - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the object reference to be placed. Owenership is NOT transferred! - // Arguments: None. - // Return value: A pointer to the reference object to be copied and placed. Not transferred! - - const SceneObject * GetObjectReference() { return m_pObjectReference; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the place offset from the parent's position/origin. If in a scene - // this will yield the absolute scene coordinates. - // Arguments: None. - // Return value: The offset in pixels from the parent's position where this gets spawned. - - Vector GetOffset() const { return m_Offset; } - - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the place offset from the parent's position/origin. - // Arguments: New offset. - // Return value: None. - - void SetOffset(Vector newOffset) { m_Offset = newOffset; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRotation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rotation angle of the object to be placed, in radians. - // Arguments: None. - // Return value: The placement rotational angle, in radians. - - float GetRotation() const { return m_RotAngle; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetHFlipped - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets whether the placement is horizontally flipped or not. - // Arguments: None. - // Return value: The horizontal flipping of the placement. - - bool GetHFlipped() const { return m_HFlipped; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets which team this is to be assigned to when placed. - // Arguments: None. - // Return value: The team number this is to be assigned to when placed. - - int GetTeam() const { return m_Team; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this is to be assigned to when placed. - // Arguments: The team number this is to be assigned to when placed. - // Return value: None. - - void SetTeam(int team) { m_Team = team; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlacedCopy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes a copy of the preset instance, and applies the placement - // properties of this to it, finally returning it WITH OWNERSHIP. - // Arguments: The parent to place as offset from. If 0 is passed, the placement - // properties will be applied as absolutes instead of relative. - // Return value: The new copy with correct placement applied. OWNERSHIP IS TRANSFERRED! - - SceneObject * GetPlacedCopy(const SceneObject *pParent = 0) const; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - - protected: - - // The pointer to the preset instance, that copies of which will be placed. Not Owned! - const SceneObject *m_pObjectReference; - // Offset placement position from owner/parent's position/origin. - Vector m_Offset; - // The placement's rotational angle in radians. - float m_RotAngle; - // Whether horizontal flipping is part of the placement. - bool m_HFlipped; - // The team of the placed object - int m_Team; - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - - private: - - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this SOPlacer, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - - void Clear(); - - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: SceneObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a SceneObject object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - SceneObject() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~SceneObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a SceneObject object before deletion -// from system memory. -// Arguments: None. - - ~SceneObject() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneObject object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override { return 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates an SceneObject to be identical to another, by deep copy. -// Arguments: A reference to the SceneObject to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const SceneObject &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire SceneObject, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Entity::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Pure V. method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneObject object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the absolute position of this SceneObject. -// Arguments: None. -// Return value: A Vector describing the current absolute position in pixels. - - const Vector & GetPos() const { return m_Pos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the absolute position of this SceneObject in the scene. -// Arguments: A Vector describing the current absolute position in pixels. -// Return value: None. - - void SetPos(const Vector &newPos) { m_Pos = newPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsHFlipped -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether this is being drawn flipped horizontally (around the -// vertical axis), or not. -// Arguments: None. -// Return value: Whether flipped or not. - - virtual bool IsHFlipped() const { return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetRotMatrix -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current rotational Matrix of of this MovableObject. -// Arguments: None. -// Return value: The rotational Matrix of this MovableObject. - - virtual Matrix GetRotMatrix() const { return Matrix(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetRotAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current rotational angle of of this, in radians. -// Arguments: None. -// Return value: The rotational angle of this, in radians. - - virtual float GetRotAngle() const { return 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virutal method: SetHFlipped -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this should be drawn flipped horizontally (around the -// vertical axis). -// Arguments: A bool with the new value. -// Return value: None. - - virtual void SetHFlipped(const bool flipped) {} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetRotAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current absolute angle of rotation of this. -// Arguments: The new absolute angle in radians. -// Return value: None. - - virtual void SetRotAngle(float newAngle) {} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which team this belongs to. -// Arguments: The assigned team number. -// Return value: None. - - virtual void SetTeam(int team) { m_Team = team; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets which team this belongs to. -// Arguments: None. -// Return value: The currently assigned team number. - - int GetTeam() const { return m_Team; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetPlacedByPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which player placed this object in the scene, if any. -// Arguments: The player responsible for placing this is in the scene, if any. -// Return value: None. - - void SetPlacedByPlayer(int player) { m_PlacedByPlayer = player; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetPlacedByPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets which player placed this object in the scene, if any. -// Arguments: None. -// Return value: The player responsible for placing this is in the scene, if any. - - int GetPlacedByPlayer() const { return m_PlacedByPlayer; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the cost to purchase this item, in oz's of gold. -// Arguments: If this is supposed to be adjusted for a specific Tech's subjective -// value, then pass in the native DataModule ID of that tech. 0 means -// no Tech is specified and the base value is returned. -// How much to multiply the value if this happens to be a foreign Tech. -// How much to multiply the value if this happens to be a native Tech. -// Return value: The cost, in oz of gold. - - virtual float GetGoldValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetGoldValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the cost to purchase this item, in oz's of gold. -// Arguments: The cost, in oz of gold. -// Return value: None. - - void SetGoldValue(float value) { m_OzValue = value; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldValueOld -////////////////////////////////////////////////////////////////////////////////////////// -// Description: DOES THE SAME THING AS GetGoldValue, USED ONLY TO PRESERVE LUA COMPATIBILITY - - virtual float GetGoldValueOld(int nativeModule, float foreignMult) const { return GetGoldValue(nativeModule, foreignMult, 1.0); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldValueString -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a descriptive string describing the cost to purchase this item, -// in oz's of gold. -// Arguments: If this is supposed to be adjusted for a specific Tech's subjective -// value, then pass in the native DataModule ID of that tech. 0 means -// no Tech is specified and the base value is returned. -// How much to multiply the value if this happens to be a foreign Tech. -// Return value: The cost, described in a friendly to read string: "100oz", or "Free" - - std::string GetGoldValueString(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total liquidation value of this, including everything inside. -// Arguments: If this is supposed to be adjusted for a specific Tech's subjective -// value, then pass in the native DataModule ID of that tech. 0 means -// no Tech is specified and the base value is returned. -// How much to multiply the value if this happens to be a foreign Tech. -// Return value: The current value of this and all contained assets. - - virtual float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const { return GetGoldValue(nativeModule, foreignMult, nativeMult); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsBuyable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether this should appear in teh buy menus at all. -// Arguments: None. -// Return value: Buyable or not. - - bool IsBuyable() const { return m_Buyable; } - - /// - /// Gets the BuyableMode of this SceneObject. - /// - /// The BuyableMode of this SceneObject - BuyableMode GetBuyableMode() const { return m_BuyableMode; } - - /// - /// Gets whether this SceneObject is available only in the BuyMenu list when buyable. - /// - /// Whether this SceneObject is available only in the BuyMenu list when buyable. - bool IsBuyableInBuyMenuOnly() const { return m_BuyableMode == BuyableMode::BuyMenuOnly; } - - /// - /// Gets whether this SceneObject is available only in the ObjectPicker list when buyable. - /// - /// Whether this SceneObject is available only in the ObjectPicker list when buyable. - bool IsBuyableInObjectPickerOnly() const { return m_BuyableMode == BuyableMode::ObjectPickerOnly; } - - /// - /// Gets whether this SceneObject is available only by lua functions like CreateRandom - /// - /// Whether this SceneObject is available only in the AI list when buyable. - bool IsBuyableInScriptOnly() const { return m_BuyableMode == BuyableMode::ScriptOnly; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGraphicalIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a bitmap showing a good identifyable icon of this, for use in -// GUI lists etc. -// Arguments: None. -// Return value: A good identifyable graphical representation of this in a BITMAP, if -// available. If not, 0 is returned. Ownership is NOT TRANSFERRED! - - virtual BITMAP * GetGraphicalIcon() const { return nullptr; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - - virtual bool IsOnScenePoint(Vector &scenePoint) const { return false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this SceneObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - virtual void Update() { } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: FullUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the full state of this object in one call. -// Arguments: None. -// Return value: None. - virtual void FullUpdate() { Update(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PostUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Function called after everything has finished updating. -// Arguments: None. -// Return value: None. - virtual void PostUpdate() { }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SceneObject's current graphical representation to a BITMAP of -// choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// like indicator arrows or hovering HUD text and so on. -// Return value: None. - - virtual void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const = 0; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawTeamMark -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws team sign this terrain object belongs to. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// Return value: None. - - void DrawTeamMark(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Forbidding copying - SceneObject(const SceneObject &reference) = delete; - SceneObject & operator=(const SceneObject &rhs) { return *this; } - - - // Member variables - static Entity::ClassInfo m_sClass; - // Absolute position of the center of this in the scene, in pixels - Vector m_Pos; - // How much this SceneObject costs to purchase, in oz's of gold. - float m_OzValue; - // Whether this shows up in the buy menu at all - bool m_Buyable; - - BuyableMode m_BuyableMode; //!< In which buy lists this SceneObject is available when buyable. - - // The team this object belongs to. -1 if none. - int m_Team; - // The player this was placed by in edit mode - int m_PlacedByPlayer; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SceneObject, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - -}; + ClassInfoGetters; + + /// + /// Enumeration for the different buyable modes of this SceneObject. + /// + enum class BuyableMode { + NoRestrictions, + BuyMenuOnly, + ObjectPickerOnly, + ScriptOnly + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Nested class: SOPlacer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Acts as a small memory object that only holds a pointer to a reference + // instance and the most essential properties to eventually place a copy + // of that reference when needed. + // Parent(s): Serializable. + // Class history: 11/25/2007 SOPlacer created. + + class SOPlacer : + public Serializable { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + SerializableClassNameGetter; + SerializableOverrideMethods; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: SOPlacer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a SOPlacer object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + SOPlacer() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a SOPlacer to be identical to another, by deep copy. + // Arguments: A reference to the SOPlacer to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const SOPlacer& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire Serializable, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetObjectReference + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the object reference to be placed. Owenership is NOT transferred! + // Arguments: None. + // Return value: A pointer to the reference object to be copied and placed. Not transferred! + + const SceneObject* GetObjectReference() { return m_pObjectReference; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the place offset from the parent's position/origin. If in a scene + // this will yield the absolute scene coordinates. + // Arguments: None. + // Return value: The offset in pixels from the parent's position where this gets spawned. + + Vector GetOffset() const { return m_Offset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the place offset from the parent's position/origin. + // Arguments: New offset. + // Return value: None. + + void SetOffset(Vector newOffset) { m_Offset = newOffset; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRotation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rotation angle of the object to be placed, in radians. + // Arguments: None. + // Return value: The placement rotational angle, in radians. + + float GetRotation() const { return m_RotAngle; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetHFlipped + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets whether the placement is horizontally flipped or not. + // Arguments: None. + // Return value: The horizontal flipping of the placement. + + bool GetHFlipped() const { return m_HFlipped; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets which team this is to be assigned to when placed. + // Arguments: None. + // Return value: The team number this is to be assigned to when placed. + + int GetTeam() const { return m_Team; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this is to be assigned to when placed. + // Arguments: The team number this is to be assigned to when placed. + // Return value: None. + + void SetTeam(int team) { m_Team = team; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlacedCopy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes a copy of the preset instance, and applies the placement + // properties of this to it, finally returning it WITH OWNERSHIP. + // Arguments: The parent to place as offset from. If 0 is passed, the placement + // properties will be applied as absolutes instead of relative. + // Return value: The new copy with correct placement applied. OWNERSHIP IS TRANSFERRED! + + SceneObject* GetPlacedCopy(const SceneObject* pParent = 0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // The pointer to the preset instance, that copies of which will be placed. Not Owned! + const SceneObject* m_pObjectReference; + // Offset placement position from owner/parent's position/origin. + Vector m_Offset; + // The placement's rotational angle in radians. + float m_RotAngle; + // Whether horizontal flipping is part of the placement. + bool m_HFlipped; + // The team of the placed object + int m_Team; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this SOPlacer, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: SceneObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a SceneObject object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + SceneObject() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~SceneObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a SceneObject object before deletion + // from system memory. + // Arguments: None. + + ~SceneObject() override { Destroy(true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the SceneObject object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override { return 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates an SceneObject to be identical to another, by deep copy. + // Arguments: A reference to the SceneObject to deep copy. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(const SceneObject& reference); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire SceneObject, including its inherited members, to their + // default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { + Clear(); + Entity::Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Pure V. method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneObject object. + // Arguments: Whether to only destroy the members defined in this derived class, or + // to destroy all inherited members also. + // Return value: None. + + void Destroy(bool notInherited = false) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the absolute position of this SceneObject. + // Arguments: None. + // Return value: A Vector describing the current absolute position in pixels. + + const Vector& GetPos() const { return m_Pos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the absolute position of this SceneObject in the scene. + // Arguments: A Vector describing the current absolute position in pixels. + // Return value: None. + + void SetPos(const Vector& newPos) { m_Pos = newPos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsHFlipped + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether this is being drawn flipped horizontally (around the + // vertical axis), or not. + // Arguments: None. + // Return value: Whether flipped or not. + + virtual bool IsHFlipped() const { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRotMatrix + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current rotational Matrix of of this MovableObject. + // Arguments: None. + // Return value: The rotational Matrix of this MovableObject. + + virtual Matrix GetRotMatrix() const { return Matrix(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetRotAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current rotational angle of of this, in radians. + // Arguments: None. + // Return value: The rotational angle of this, in radians. + + virtual float GetRotAngle() const { return 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virutal method: SetHFlipped + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether this should be drawn flipped horizontally (around the + // vertical axis). + // Arguments: A bool with the new value. + // Return value: None. + + virtual void SetHFlipped(const bool flipped) {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetRotAngle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current absolute angle of rotation of this. + // Arguments: The new absolute angle in radians. + // Return value: None. + + virtual void SetRotAngle(float newAngle) {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which team this belongs to. + // Arguments: The assigned team number. + // Return value: None. + + virtual void SetTeam(int team) { m_Team = team; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets which team this belongs to. + // Arguments: None. + // Return value: The currently assigned team number. + + int GetTeam() const { return m_Team; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: SetPlacedByPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which player placed this object in the scene, if any. + // Arguments: The player responsible for placing this is in the scene, if any. + // Return value: None. + + void SetPlacedByPlayer(int player) { m_PlacedByPlayer = player; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: GetPlacedByPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets which player placed this object in the scene, if any. + // Arguments: None. + // Return value: The player responsible for placing this is in the scene, if any. + + int GetPlacedByPlayer() const { return m_PlacedByPlayer; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the cost to purchase this item, in oz's of gold. + // Arguments: If this is supposed to be adjusted for a specific Tech's subjective + // value, then pass in the native DataModule ID of that tech. 0 means + // no Tech is specified and the base value is returned. + // How much to multiply the value if this happens to be a foreign Tech. + // How much to multiply the value if this happens to be a native Tech. + // Return value: The cost, in oz of gold. + + virtual float GetGoldValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetGoldValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the cost to purchase this item, in oz's of gold. + // Arguments: The cost, in oz of gold. + // Return value: None. + + void SetGoldValue(float value) { m_OzValue = value; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldValueOld + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: DOES THE SAME THING AS GetGoldValue, USED ONLY TO PRESERVE LUA COMPATIBILITY + + virtual float GetGoldValueOld(int nativeModule, float foreignMult) const { return GetGoldValue(nativeModule, foreignMult, 1.0); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldValueString + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a descriptive string describing the cost to purchase this item, + // in oz's of gold. + // Arguments: If this is supposed to be adjusted for a specific Tech's subjective + // value, then pass in the native DataModule ID of that tech. 0 means + // no Tech is specified and the base value is returned. + // How much to multiply the value if this happens to be a foreign Tech. + // Return value: The cost, described in a friendly to read string: "100oz", or "Free" + + std::string GetGoldValueString(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total liquidation value of this, including everything inside. + // Arguments: If this is supposed to be adjusted for a specific Tech's subjective + // value, then pass in the native DataModule ID of that tech. 0 means + // no Tech is specified and the base value is returned. + // How much to multiply the value if this happens to be a foreign Tech. + // Return value: The current value of this and all contained assets. + + virtual float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const { return GetGoldValue(nativeModule, foreignMult, nativeMult); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsBuyable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether this should appear in teh buy menus at all. + // Arguments: None. + // Return value: Buyable or not. + + bool IsBuyable() const { return m_Buyable; } + + /// + /// Gets the BuyableMode of this SceneObject. + /// + /// The BuyableMode of this SceneObject + BuyableMode GetBuyableMode() const { return m_BuyableMode; } + + /// + /// Gets whether this SceneObject is available only in the BuyMenu list when buyable. + /// + /// Whether this SceneObject is available only in the BuyMenu list when buyable. + bool IsBuyableInBuyMenuOnly() const { return m_BuyableMode == BuyableMode::BuyMenuOnly; } + + /// + /// Gets whether this SceneObject is available only in the ObjectPicker list when buyable. + /// + /// Whether this SceneObject is available only in the ObjectPicker list when buyable. + bool IsBuyableInObjectPickerOnly() const { return m_BuyableMode == BuyableMode::ObjectPickerOnly; } + + /// + /// Gets whether this SceneObject is available only by lua functions like CreateRandom + /// + /// Whether this SceneObject is available only in the AI list when buyable. + bool IsBuyableInScriptOnly() const { return m_BuyableMode == BuyableMode::ScriptOnly; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGraphicalIcon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a bitmap showing a good identifyable icon of this, for use in + // GUI lists etc. + // Arguments: None. + // Return value: A good identifyable graphical representation of this in a BITMAP, if + // available. If not, 0 is returned. Ownership is NOT TRANSFERRED! + + virtual BITMAP* GetGraphicalIcon() const { return nullptr; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: IsOnScenePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether this' current graphical representation overlaps + // a point in absolute scene coordinates. + // Arguments: The point in absolute scene coordinates. + // Return value: Whether this' graphical rep overlaps the scene point. + + virtual bool IsOnScenePoint(Vector& scenePoint) const { return false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates this SceneObject. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + virtual void Update() {} + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: FullUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the full state of this object in one call. + // Arguments: None. + // Return value: None. + virtual void FullUpdate() { Update(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: PostUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Function called after everything has finished updating. + // Arguments: None. + // Return value: None. + virtual void PostUpdate(){}; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this SceneObject's current graphical representation to a BITMAP of + // choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // In which mode to draw in. See the DrawMode enumeration for the modes. + // Whether to not draw any extra 'ghost' items of this MovableObject, + // like indicator arrows or hovering HUD text and so on. + // Return value: None. + + virtual void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const = 0; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: DrawTeamMark + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws team sign this terrain object belongs to. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the Scene. + // Return value: None. + + void DrawTeamMark(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Forbidding copying + SceneObject(const SceneObject& reference) = delete; + SceneObject& operator=(const SceneObject& rhs) { return *this; } + + // Member variables + static Entity::ClassInfo m_sClass; + // Absolute position of the center of this in the scene, in pixels + Vector m_Pos; + // How much this SceneObject costs to purchase, in oz's of gold. + float m_OzValue; + // Whether this shows up in the buy menu at all + bool m_Buyable; + + BuyableMode m_BuyableMode; //!< In which buy lists this SceneObject is available when buyable. + + // The team this object belongs to. -1 if none. + int m_Team; + // The player this was placed by in edit mode + int m_PlacedByPlayer; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this SceneObject, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + }; } // namespace RTE diff --git a/Source/Entities/SoundContainer.cpp b/Source/Entities/SoundContainer.cpp index 7aaae44025..f71b53dcb8 100644 --- a/Source/Entities/SoundContainer.cpp +++ b/Source/Entities/SoundContainer.cpp @@ -6,18 +6,16 @@ namespace RTE { ConcreteClassInfo(SoundContainer, Entity, 50); const std::unordered_map SoundContainer::c_SoundOverlapModeMap = { - {"Overlap", SoundContainer::SoundOverlapMode::OVERLAP}, - {"Restart", SoundContainer::SoundOverlapMode::RESTART}, - {"Ignore Play", SoundContainer::SoundOverlapMode::IGNORE_PLAY} - }; + {"Overlap", SoundContainer::SoundOverlapMode::OVERLAP}, + {"Restart", SoundContainer::SoundOverlapMode::RESTART}, + {"Ignore Play", SoundContainer::SoundOverlapMode::IGNORE_PLAY}}; const std::unordered_map SoundContainer::c_BusRoutingMap = { - {"SFX", SoundContainer::BusRouting::SFX}, - {"UI", SoundContainer::BusRouting::UI}, - {"Music", SoundContainer::BusRouting::MUSIC} - }; + {"SFX", SoundContainer::BusRouting::SFX}, + {"UI", SoundContainer::BusRouting::UI}, + {"Music", SoundContainer::BusRouting::MUSIC}}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SoundContainer::Clear() { m_TopLevelSoundSet.Destroy(); @@ -42,9 +40,9 @@ namespace RTE { m_PitchVariation = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundContainer::Create(const SoundContainer &reference) { + int SoundContainer::Create(const SoundContainer& reference) { Entity::Create(reference); m_TopLevelSoundSet.Create(reference.m_TopLevelSoundSet); @@ -70,11 +68,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundContainer::ReadProperty(const std::string_view &propName, Reader &reader) { + int SoundContainer::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("SpecialBehaviour_TopLevelSoundSet", { reader >> m_TopLevelSoundSet; }); MatchProperty("AddSound", { m_TopLevelSoundSet.AddSoundData(SoundSet::ReadAndGetSoundData(reader)); }); MatchProperty("AddSoundSet", { @@ -90,7 +88,7 @@ namespace RTE { } else { try { m_SoundOverlapMode = static_cast(std::stoi(soundOverlapModeString)); - } catch (const std::exception &) { + } catch (const std::exception&) { reader.ReportError("Cycle mode " + soundOverlapModeString + " is invalid."); } } @@ -102,7 +100,7 @@ namespace RTE { } else { try { m_BusRouting = static_cast(std::stoi(busRoutingString)); - } catch (const std::exception &) { + } catch (const std::exception&) { reader.ReportError("Tried to route to non-existent sound bus " + busRoutingString); } } @@ -111,13 +109,17 @@ namespace RTE { MatchProperty("AttenuationStartDistance", { reader >> m_AttenuationStartDistance; }); MatchProperty("CustomPanValue", { reader >> m_CustomPanValue; - if (m_CustomPanValue < -1.0f || m_CustomPanValue > 1.0f) { reader.ReportError("SoundContainer CustomPanValue must be between -1 and 1."); } + if (m_CustomPanValue < -1.0f || m_CustomPanValue > 1.0f) { + reader.ReportError("SoundContainer CustomPanValue must be between -1 and 1."); + } }); MatchProperty("PanningStrengthMultiplier", { reader >> m_PanningStrengthMultiplier; }); MatchProperty("LoopSetting", { reader >> m_Loops; }); MatchProperty("Priority", { reader >> m_Priority; - if (m_Priority < 0 || m_Priority > 256) { reader.ReportError("SoundContainer priority must be between 256 (lowest priority) and 0 (highest priority)."); } + if (m_Priority < 0 || m_Priority > 256) { + reader.ReportError("SoundContainer priority must be between 256 (lowest priority) and 0 (highest priority)."); + } }); MatchProperty("AffectedByGlobalPitch", { reader >> m_AffectedByGlobalPitch; }); MatchProperty("Position", { reader >> m_Pos; }); @@ -128,9 +130,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - int SoundContainer::Save(Writer &writer) const { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SoundContainer::Save(Writer& writer) const { Entity::Save(writer); // Due to writer limitations, the top level SoundSet has to be explicitly written out, even though SoundContainer standard behaviour is to hide it in INI and just have properties be part of the SoundContainer. @@ -176,12 +178,12 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float SoundContainer::GetLength(LengthOfSoundType type) const { if (!m_SoundPropertiesUpToDate) { // Todo - use a post-load fixup stage instead of lazily initializing shit everywhere... Eugh. - const_cast(this)->UpdateSoundProperties(); + const_cast(this)->UpdateSoundProperties(); const_cast(this)->m_TopLevelSoundSet.SelectNextSounds(); } @@ -189,7 +191,7 @@ namespace RTE { m_TopLevelSoundSet.GetFlattenedSoundData(flattenedSoundData, type == LengthOfSoundType::NextPlayed); float lengthMilliseconds = 0.0f; - for (const SoundSet::SoundData *selectedSoundData : flattenedSoundData) { + for (const SoundSet::SoundData* selectedSoundData: flattenedSoundData) { unsigned int length; selectedSoundData->SoundObject->getLength(&length, FMOD_TIMEUNIT_MS); lengthMilliseconds = std::max(lengthMilliseconds, static_cast(length)); @@ -198,24 +200,24 @@ namespace RTE { return lengthMilliseconds; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::vector SoundContainer::GetSelectedSoundHashes() const { std::vector soundHashes; - std::vector flattenedSoundData; + std::vector flattenedSoundData; m_TopLevelSoundSet.GetFlattenedSoundData(flattenedSoundData, false); - for (const SoundSet::SoundData *selectedSoundData : flattenedSoundData) { + for (const SoundSet::SoundData* selectedSoundData: flattenedSoundData) { soundHashes.push_back(selectedSoundData->SoundFile.GetHash()); } return soundHashes; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - const SoundSet::SoundData * SoundContainer::GetSoundDataForSound(const FMOD::Sound *sound) const { - std::vector flattenedSoundData; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + const SoundSet::SoundData* SoundContainer::GetSoundDataForSound(const FMOD::Sound* sound) const { + std::vector flattenedSoundData; m_TopLevelSoundSet.GetFlattenedSoundData(flattenedSoundData, false); - for (const SoundSet::SoundData *soundData : flattenedSoundData) { + for (const SoundSet::SoundData* soundData: flattenedSoundData) { if (sound == soundData->SoundObject) { return soundData; } @@ -223,7 +225,7 @@ namespace RTE { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SoundContainer::Play(int player) { if (HasAnySounds()) { @@ -235,19 +237,18 @@ namespace RTE { } } return g_AudioMan.PlaySoundContainer(this, player); - } return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FMOD_RESULT SoundContainer::UpdateSoundProperties() { FMOD_RESULT result = FMOD_OK; - std::vector flattenedSoundData; + std::vector flattenedSoundData; m_TopLevelSoundSet.GetFlattenedSoundData(flattenedSoundData, false); - for (SoundSet::SoundData *soundData : flattenedSoundData) { + for (SoundSet::SoundData* soundData: flattenedSoundData) { FMOD_MODE soundMode = (m_Loops == 0) ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL; if (m_Immobile) { soundMode |= FMOD_2D; @@ -267,4 +268,4 @@ namespace RTE { return result; } -} +} // namespace RTE diff --git a/Source/Entities/SoundContainer.h b/Source/Entities/SoundContainer.h index ff6c381466..73322533e5 100644 --- a/Source/Entities/SoundContainer.h +++ b/Source/Entities/SoundContainer.h @@ -12,9 +12,8 @@ namespace RTE { /// A container for sounds that represent a specific sound effect. /// class SoundContainer : public Entity { - - public: + public: EntityAllocation(SoundContainer); SerializableOverrideMethods; ClassInfoGetters; @@ -47,14 +46,17 @@ namespace RTE { /// Copy constructor method used to instantiate a SoundContainer object identical to an already existing one. /// /// A reference to the SoundContainer to deep copy. - SoundContainer(const SoundContainer &reference) { Clear(); Create(reference); } + SoundContainer(const SoundContainer& reference) { + Clear(); + Create(reference); + } /// /// Creates a SoundContainer to be identical to another, by deep copy. /// /// A reference to the SoundContainer to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const SoundContainer &reference); + int Create(const SoundContainer& reference); /// /// Creates a SoundContainer and adds a sound, optionally setting immobility, being affected by global pitch, and bus routing. @@ -64,7 +66,13 @@ namespace RTE { /// Whether this SoundContainer's sounds' frequency will be affected by the global pitch. /// Bus to route this sound to. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const std::string &soundFilePath, bool immobile = false, bool affectedByGlobalPitch = true, BusRouting busRouting = BusRouting::SFX) { m_TopLevelSoundSet.AddSound(soundFilePath, true); SetImmobile(immobile); SetAffectedByGlobalPitch(affectedByGlobalPitch); SetBusRouting(busRouting); return 0; } + int Create(const std::string& soundFilePath, bool immobile = false, bool affectedByGlobalPitch = true, BusRouting busRouting = BusRouting::SFX) { + m_TopLevelSoundSet.AddSound(soundFilePath, true); + SetImmobile(immobile); + SetAffectedByGlobalPitch(affectedByGlobalPitch); + SetBusRouting(busRouting); + return 0; + } #pragma endregion #pragma region Destruction @@ -77,12 +85,20 @@ namespace RTE { /// Destroys and resets (through Clear()) the SoundContainer object. It doesn't delete the Sound files, since they're owned by ContentFile static maps. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + Entity::Destroy(); + } + Clear(); + } /// /// Resets the entire SoundContainer, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Entity::Reset(); } + void Reset() override { + Clear(); + Entity::Reset(); + } #pragma endregion #pragma region Sound Management Getters and Setters @@ -108,13 +124,16 @@ namespace RTE { /// Gets a reference to the top level SoundSet of this SoundContainer, to which all SoundData and sub SoundSets belong. /// /// A reference to the top level SoundSet of this SoundContainer. - SoundSet & GetTopLevelSoundSet() { return m_TopLevelSoundSet; } + SoundSet& GetTopLevelSoundSet() { return m_TopLevelSoundSet; } /// /// Copies the passed in SoundSet reference into the top level SoundSet of this SoundContainer, effectively making that the new top level SoundSet. /// /// A reference to the new top level SoundSet for this SoundContainer. - void SetTopLevelSoundSet(const SoundSet &newTopLevelSoundSet) { m_TopLevelSoundSet = newTopLevelSoundSet; m_SoundPropertiesUpToDate = false; } + void SetTopLevelSoundSet(const SoundSet& newTopLevelSoundSet) { + m_TopLevelSoundSet = newTopLevelSoundSet; + m_SoundPropertiesUpToDate = false; + } /// /// Gets a vector of hashes of the sounds selected to be played next in this SoundContainer. @@ -127,13 +146,13 @@ namespace RTE { /// /// The FMOD::Sound to search for. /// A pointer to the corresponding SoundData or a null pointer. - const SoundSet::SoundData * GetSoundDataForSound(const FMOD::Sound *sound) const; + const SoundSet::SoundData* GetSoundDataForSound(const FMOD::Sound* sound) const; /// /// Gets the channels playing sounds from this SoundContainer. /// /// The channels currently being used. - std::unordered_set const * GetPlayingChannels() const { return &m_PlayingChannels; } + std::unordered_set const* GetPlayingChannels() const { return &m_PlayingChannels; } /// /// Indicates whether any sound in this SoundContainer is currently being played. @@ -160,7 +179,7 @@ namespace RTE { SoundOverlapMode GetSoundOverlapMode() const { return m_SoundOverlapMode; } /// - /// Sets the SoundOverlapMode of this SoundContainer, which is used to determine how it should behave when it's told to play while already playing. + /// Sets the SoundOverlapMode of this SoundContainer, which is used to determine how it should behave when it's told to play while already playing. /// /// The new SoundOverlapMode this SoundContainer should use. void SetSoundOverlapMode(SoundOverlapMode newSoundOverlapMode) { m_SoundOverlapMode = newSoundOverlapMode; } @@ -179,7 +198,7 @@ namespace RTE { /// /// The new bus for this sound to route to. void SetBusRouting(BusRouting newBusRoute) { m_BusRouting = newBusRoute; } - + /// /// Gets whether the sounds in this SoundContainer should be considered immobile, i.e. always play at the listener's position. /// @@ -190,7 +209,10 @@ namespace RTE { /// Sets whether the sounds in this SoundContainer should be considered immobile, i.e. always play at the listener's position. Does not affect currently playing sounds. /// /// The new immobile setting. - void SetImmobile(bool immobile) { m_Immobile = immobile; m_SoundPropertiesUpToDate = false; } + void SetImmobile(bool immobile) { + m_Immobile = immobile; + m_SoundPropertiesUpToDate = false; + } /// /// Gets the attenuation start distance of this SoundContainer. @@ -202,7 +224,10 @@ namespace RTE { /// Sets the attenuation start distance of this SoundContainer. Values < 0 set it to default. Does not affect currently playing sounds. /// /// The new attenuation start distance. - void SetAttenuationStartDistance(float attenuationStartDistance) { m_AttenuationStartDistance = (attenuationStartDistance < 0) ? c_DefaultAttenuationStartDistance : attenuationStartDistance; m_SoundPropertiesUpToDate = false; } + void SetAttenuationStartDistance(float attenuationStartDistance) { + m_AttenuationStartDistance = (attenuationStartDistance < 0) ? c_DefaultAttenuationStartDistance : attenuationStartDistance; + m_SoundPropertiesUpToDate = false; + } /// /// Gets the custom pan value of this SoundContainer. @@ -214,8 +239,13 @@ namespace RTE { /// Sets the custom pan value of this SoundContainer. Clamped between -1 and 1. /// /// The new custom pan value. - void SetCustomPanValue(float customPanValue) { m_CustomPanValue = std::clamp(customPanValue, -1.0f, 1.0f); if (IsBeingPlayed()) { g_AudioMan.ChangeSoundContainerPlayingChannelsCustomPanValue(this); } } - + void SetCustomPanValue(float customPanValue) { + m_CustomPanValue = std::clamp(customPanValue, -1.0f, 1.0f); + if (IsBeingPlayed()) { + g_AudioMan.ChangeSoundContainerPlayingChannelsCustomPanValue(this); + } + } + /// /// Gets the panning strength multiplier of this SoundContainer. /// @@ -226,8 +256,11 @@ namespace RTE { /// Sets the panning strength multiplier of this SoundContainer. /// /// The new panning strength multiplier. - void SetPanningStrengthMultiplier(float panningStrengthMultiplier) { m_PanningStrengthMultiplier = panningStrengthMultiplier; m_SoundPropertiesUpToDate = false; } - + void SetPanningStrengthMultiplier(float panningStrengthMultiplier) { + m_PanningStrengthMultiplier = panningStrengthMultiplier; + m_SoundPropertiesUpToDate = false; + } + /// /// Gets the looping setting of this SoundContainer. /// @@ -239,7 +272,10 @@ namespace RTE { /// 0 means the sound is set to only play once. -1 means it loops indefinitely. /// /// The new loop count. - void SetLoopSetting(int loops) { m_Loops = loops; m_SoundPropertiesUpToDate = false; } + void SetLoopSetting(int loops) { + m_Loops = loops; + m_SoundPropertiesUpToDate = false; + } /// /// Gets whether the sounds in this SoundContainer have all had all their properties set appropriately. Used to account for issues with ordering in INI loading. @@ -275,14 +311,21 @@ namespace RTE { /// Gets the position at which this SoundContainer's sound will be played. Note that its individual sounds can be offset from this. /// /// The position of this SoundContainer. - const Vector & GetPosition() const { return m_Pos; } + const Vector& GetPosition() const { return m_Pos; } /// /// Sets the position of the SoundContainer's sounds while they're playing. /// /// The new position to play the SoundContainer's sounds. /// Whether this SoundContainer's attenuation setting was successful. - void SetPosition(const Vector &newPosition) { if (!m_Immobile && newPosition != m_Pos) { m_Pos = newPosition; if (IsBeingPlayed()) { g_AudioMan.ChangeSoundContainerPlayingChannelsPosition(this); } } } + void SetPosition(const Vector& newPosition) { + if (!m_Immobile && newPosition != m_Pos) { + m_Pos = newPosition; + if (IsBeingPlayed()) { + g_AudioMan.ChangeSoundContainerPlayingChannelsPosition(this); + } + } + } /// /// Gets the volume the sounds in this SoundContainer are played at. Note that this does not factor volume changes due to the SoundContainer's position. @@ -294,7 +337,13 @@ namespace RTE { /// Sets the volume sounds in this SoundContainer should be played at. Note that this does not factor volume changes due to the SoundContainer's position. Does not affect currently playing sounds. /// /// The new volume sounds in this SoundContainer should be played at. Limited between 0 and 10. - void SetVolume(float newVolume) { newVolume = std::clamp(newVolume, 0.0F, 10.0F); if (IsBeingPlayed()) { g_AudioMan.ChangeSoundContainerPlayingChannelsVolume(this, newVolume); } m_Volume = newVolume; } + void SetVolume(float newVolume) { + newVolume = std::clamp(newVolume, 0.0F, 10.0F); + if (IsBeingPlayed()) { + g_AudioMan.ChangeSoundContainerPlayingChannelsVolume(this, newVolume); + } + m_Volume = newVolume; + } /// /// Gets the pitch the sounds in this SoundContainer are played at. Note that this does not factor in global pitch. @@ -306,7 +355,12 @@ namespace RTE { /// Sets the pitch sounds in this SoundContainer should be played at and updates any playing instances accordingly. /// /// The new pitch sounds in this SoundContainer should be played at. Limited between 0.125 and 8 (8 octaves up or down). - void SetPitch(float newPitch) { m_Pitch = std::clamp(newPitch, 0.125F, 8.0F); if (IsBeingPlayed()) { g_AudioMan.ChangeSoundContainerPlayingChannelsPitch(this); } } + void SetPitch(float newPitch) { + m_Pitch = std::clamp(newPitch, 0.125F, 8.0F); + if (IsBeingPlayed()) { + g_AudioMan.ChangeSoundContainerPlayingChannelsPitch(this); + } + } /// /// Gets the pitch variation the sounds in this SoundContainer are played at. @@ -340,7 +394,7 @@ namespace RTE { /// /// The position at which to play the SoundContainer's sounds. /// Whether this SoundContainer successfully started playing on any channels. - bool Play(const Vector &position) { return Play(position, -1); } + bool Play(const Vector& position) { return Play(position, -1); } /// /// Plays the next sound of this SoundContainer with the given attenuation for a specific player. @@ -348,7 +402,10 @@ namespace RTE { /// The position at which to play the SoundContainer's sounds. /// The player to start playback of this SoundContainer's sounds for. /// Whether this SoundContainer successfully started playing on any channels. - bool Play(const Vector &position, int player) { SetPosition(position); return Play(player); } + bool Play(const Vector& position, int player) { + SetPosition(position); + return Play(player); + } /// /// Stops playback of this SoundContainer for all players. @@ -380,7 +437,11 @@ namespace RTE { /// Fades out playback of the SoundContainer to 0 volume. /// /// How long the fadeout should take. - void FadeOut(int fadeOutTime = 1000) { if (IsBeingPlayed()) { return g_AudioMan.FadeOutSoundContainerPlayingChannels(this, fadeOutTime); } } + void FadeOut(int fadeOutTime = 1000) { + if (IsBeingPlayed()) { + return g_AudioMan.FadeOutSoundContainerPlayingChannels(this, fadeOutTime); + } + } #pragma endregion #pragma region Miscellaneous @@ -393,28 +454,27 @@ namespace RTE { #pragma endregion private: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. static const std::unordered_map c_SoundOverlapModeMap; //!< A map of strings to SoundOverlapModes to support string parsing for the SoundOverlapMode enum. Populated in the implementing cpp file. static const std::unordered_map c_BusRoutingMap; //!< A map of strings to BusRoutings to support string parsing for the BusRouting enum. Populated in the implementing cpp file. - - SoundSet m_TopLevelSoundSet; //The top level SoundSet that handles all SoundData and sub SoundSets in this SoundContainer. + + SoundSet m_TopLevelSoundSet; // The top level SoundSet that handles all SoundData and sub SoundSets in this SoundContainer. std::unordered_set m_PlayingChannels; //!< The channels this SoundContainer is currently using. SoundOverlapMode m_SoundOverlapMode; //!< The SoundOverlapMode for this SoundContainer, used to determine how it should handle overlapping play calls. - + BusRouting m_BusRouting; //!< What bus this sound routes to. - + bool m_Immobile; //!< Whether this SoundContainer's sounds should be treated as immobile, i.e. not affected by 3D sound effects. float m_AttenuationStartDistance; //!< The distance away from the AudioSystem listener to start attenuating this sound. Attenuation follows FMOD 3D Inverse roll-off model. float m_CustomPanValue; //!< Custom stereo pan value using a Pan DSP on top of the basic spatialization. float m_PanningStrengthMultiplier; //!< Multiplier for panning strength. int m_Loops; //!< Number of loops (repeats) the SoundContainer's sounds should play when played. 0 means it plays once, -1 means it plays until stopped. bool m_SoundPropertiesUpToDate = false; //!< Whether this SoundContainer's sounds' modes and properties are up to date. Used primarily to handle discrepancies that can occur when loading from ini if the line ordering isn't ideal. - + int m_Priority; //!< The mixing priority of this SoundContainer's sounds. Higher values are more likely to be heard. bool m_AffectedByGlobalPitch; //!< Whether this SoundContainer's sounds should be able to be altered by global pitch changes. - + Vector m_Pos; //!< The current position of this SoundContainer's sounds. float m_Pitch; //!< The current natural pitch of this SoundContainer's sounds. float m_PitchVariation; //!< The randomized pitch variation of this SoundContainer's sounds. 1 means the sound will vary a full octave both ways. @@ -425,5 +485,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif diff --git a/Source/Entities/SoundSet.cpp b/Source/Entities/SoundSet.cpp index 0f1b306ec5..1bdb6c999c 100644 --- a/Source/Entities/SoundSet.cpp +++ b/Source/Entities/SoundSet.cpp @@ -8,12 +8,11 @@ namespace RTE { const std::string SoundSet::m_sClassName = "SoundSet"; const std::unordered_map SoundSet::c_SoundSelectionCycleModeMap = { - {"random", SoundSelectionCycleMode::RANDOM}, - {"forwards", SoundSelectionCycleMode::FORWARDS}, - {"all", SoundSelectionCycleMode::ALL} - }; + {"random", SoundSelectionCycleMode::RANDOM}, + {"forwards", SoundSelectionCycleMode::FORWARDS}, + {"all", SoundSelectionCycleMode::ALL}}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SoundSet::Clear() { m_SoundSelectionCycleMode = SoundSelectionCycleMode::RANDOM; @@ -23,15 +22,15 @@ namespace RTE { m_SubSoundSets.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundSet::Create(const SoundSet &reference) { + int SoundSet::Create(const SoundSet& reference) { m_SoundSelectionCycleMode = reference.m_SoundSelectionCycleMode; m_CurrentSelection = reference.m_CurrentSelection; - for (SoundData referenceSoundData : reference.m_SoundData) { + for (SoundData referenceSoundData: reference.m_SoundData) { m_SoundData.push_back(referenceSoundData); } - for (const SoundSet &referenceSoundSet : reference.m_SubSoundSets) { + for (const SoundSet& referenceSoundSet: reference.m_SubSoundSets) { SoundSet soundSet; soundSet.Create(referenceSoundSet); m_SubSoundSets.push_back(soundSet); @@ -40,11 +39,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundSet::ReadProperty(const std::string_view &propName, Reader &reader) { + int SoundSet::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("SoundSelectionCycleMode", { SetSoundSelectionCycleMode(ReadSoundSelectionCycleMode(reader)); }); MatchProperty("AddSound", { AddSoundData(ReadAndGetSoundData(reader)); }); MatchProperty("AddSoundSet", { @@ -52,27 +51,29 @@ namespace RTE { reader >> soundSetToAdd; AddSoundSet(soundSetToAdd); }); - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SoundSet::SoundData SoundSet::ReadAndGetSoundData(Reader &reader) { + SoundSet::SoundData SoundSet::ReadAndGetSoundData(Reader& reader) { SoundSet::SoundData soundData; /// /// Internal lambda function to load an audio file by path in as a ContentFile, which in turn loads it into FMOD, then returns SoundData for it in the outParam outSoundData. /// /// The path to the sound file. - auto readSoundFromPath = [&soundData, &reader](const std::string &soundPath) { + auto readSoundFromPath = [&soundData, &reader](const std::string& soundPath) { ContentFile soundFile(soundPath.c_str()); /// As Serializable::Create(&reader) isn't being used here, we need to set our formatted reader position manually. soundFile.SetFormattedReaderPosition("in file " + reader.GetCurrentFilePath() + " on line " + reader.GetCurrentFileLine()); - FMOD::Sound *soundObject = soundFile.GetAsSound(); - if (g_AudioMan.IsAudioEnabled() && !soundObject) { reader.ReportError(std::string("Failed to load the sound from the file")); } + FMOD::Sound* soundObject = soundFile.GetAsSound(); + if (g_AudioMan.IsAudioEnabled() && !soundObject) { + reader.ReportError(std::string("Failed to load the sound from the file")); + } soundData.SoundFile = soundFile; soundData.SoundObject = soundObject; @@ -100,13 +101,15 @@ namespace RTE { return soundData; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SoundSet::SoundSelectionCycleMode SoundSet::ReadSoundSelectionCycleMode(Reader &reader) { + SoundSet::SoundSelectionCycleMode SoundSet::ReadSoundSelectionCycleMode(Reader& reader) { SoundSelectionCycleMode soundSelectionCycleModeToReturn; std::string soundSelectionCycleModeString = reader.ReadPropValue(); std::locale locale; - for (char &character : soundSelectionCycleModeString) { character = std::tolower(character, locale); } + for (char& character: soundSelectionCycleModeString) { + character = std::tolower(character, locale); + } std::unordered_map::const_iterator soundSelectionCycleMode = c_SoundSelectionCycleModeMap.find(soundSelectionCycleModeString); if (soundSelectionCycleMode != c_SoundSelectionCycleModeMap.end()) { @@ -114,23 +117,23 @@ namespace RTE { } else { try { soundSelectionCycleModeToReturn = static_cast(std::stoi(soundSelectionCycleModeString)); - } catch (const std::exception &) { + } catch (const std::exception&) { reader.ReportError("Sound selection cycle mode " + soundSelectionCycleModeString + " is invalid."); } } - + return soundSelectionCycleModeToReturn; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundSet::Save(Writer &writer) const { + int SoundSet::Save(Writer& writer) const { Serializable::Save(writer); writer.NewProperty("SoundSelectionCycleMode"); SaveSoundSelectionCycleMode(writer, m_SoundSelectionCycleMode); - for (const SoundData &soundData : m_SoundData) { + for (const SoundData& soundData: m_SoundData) { writer.NewProperty("AddSound"); writer.ObjectStart("ContentFile"); @@ -146,7 +149,7 @@ namespace RTE { writer.ObjectEnd(); } - for (const SoundSet &subSoundSet : m_SubSoundSets) { + for (const SoundSet& subSoundSet: m_SubSoundSets) { writer.NewProperty("AddSoundSet"); writer.ObjectStart("SoundSet"); writer << subSoundSet; @@ -156,9 +159,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - void SoundSet::SaveSoundSelectionCycleMode(Writer &writer, SoundSelectionCycleMode soundSelectionCycleMode) { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SoundSet::SaveSoundSelectionCycleMode(Writer& writer, SoundSelectionCycleMode soundSelectionCycleMode) { auto cycleModeMapEntry = std::find_if(c_SoundSelectionCycleModeMap.begin(), c_SoundSelectionCycleModeMap.end(), [&soundSelectionCycleMode = soundSelectionCycleMode](auto element) { return element.second == soundSelectionCycleMode; }); if (cycleModeMapEntry != c_SoundSelectionCycleModeMap.end()) { writer << cycleModeMapEntry->first; @@ -167,11 +170,11 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SoundSet::AddSound(const std::string &soundFilePath, const Vector &offset, float minimumAudibleDistance, float attenuationStartDistance, bool abortGameForInvalidSound) { + void SoundSet::AddSound(const std::string& soundFilePath, const Vector& offset, float minimumAudibleDistance, float attenuationStartDistance, bool abortGameForInvalidSound) { ContentFile soundFile(soundFilePath.c_str()); - FMOD::Sound *soundObject = soundFile.GetAsSound(abortGameForInvalidSound, false); + FMOD::Sound* soundObject = soundFile.GetAsSound(abortGameForInvalidSound, false); if (!soundObject) { return; } @@ -179,24 +182,28 @@ namespace RTE { m_SoundData.push_back({soundFile, soundObject, offset, minimumAudibleDistance, attenuationStartDistance}); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SoundSet::RemoveSound(const std::string &soundFilePath, bool removeFromSubSoundSets) { - auto soundsToRemove = std::remove_if(m_SoundData.begin(), m_SoundData.end(), [&soundFilePath](const SoundSet::SoundData &soundData) { return soundData.SoundFile.GetDataPath() == soundFilePath; }); + bool SoundSet::RemoveSound(const std::string& soundFilePath, bool removeFromSubSoundSets) { + auto soundsToRemove = std::remove_if(m_SoundData.begin(), m_SoundData.end(), [&soundFilePath](const SoundSet::SoundData& soundData) { return soundData.SoundFile.GetDataPath() == soundFilePath; }); bool anySoundsToRemove = soundsToRemove != m_SoundData.end(); - if (anySoundsToRemove) { m_SoundData.erase(soundsToRemove, m_SoundData.end()); } + if (anySoundsToRemove) { + m_SoundData.erase(soundsToRemove, m_SoundData.end()); + } if (removeFromSubSoundSets) { - for (SoundSet subSoundSet : m_SubSoundSets) { anySoundsToRemove |= RemoveSound(soundFilePath, removeFromSubSoundSets); } + for (SoundSet subSoundSet: m_SubSoundSets) { + anySoundsToRemove |= RemoveSound(soundFilePath, removeFromSubSoundSets); + } } return anySoundsToRemove; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SoundSet::HasAnySounds(bool includeSubSoundSets) const { bool hasAnySounds = !m_SoundData.empty(); if (!hasAnySounds && includeSubSoundSets) { - for (const SoundSet &subSoundSet : m_SubSoundSets) { + for (const SoundSet& subSoundSet: m_SubSoundSets) { hasAnySounds = subSoundSet.HasAnySounds(); if (hasAnySounds) { break; @@ -206,12 +213,16 @@ namespace RTE { return hasAnySounds; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SoundSet::GetFlattenedSoundData(std::vector &flattenedSoundData, bool onlyGetSelectedSoundData) { + void SoundSet::GetFlattenedSoundData(std::vector& flattenedSoundData, bool onlyGetSelectedSoundData) { if (!onlyGetSelectedSoundData || m_SoundSelectionCycleMode == SoundSelectionCycleMode::ALL) { - for (SoundData &soundData : m_SoundData) { flattenedSoundData.push_back(&soundData); } - for (SoundSet &subSoundSet : m_SubSoundSets) { subSoundSet.GetFlattenedSoundData(flattenedSoundData, onlyGetSelectedSoundData); } + for (SoundData& soundData: m_SoundData) { + flattenedSoundData.push_back(&soundData); + } + for (SoundSet& subSoundSet: m_SubSoundSets) { + subSoundSet.GetFlattenedSoundData(flattenedSoundData, onlyGetSelectedSoundData); + } } else { if (m_CurrentSelection.first == false) { flattenedSoundData.push_back(&m_SoundData[m_CurrentSelection.second]); @@ -221,12 +232,16 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SoundSet::GetFlattenedSoundData(std::vector &flattenedSoundData, bool onlyGetSelectedSoundData) const { + void SoundSet::GetFlattenedSoundData(std::vector& flattenedSoundData, bool onlyGetSelectedSoundData) const { if (!onlyGetSelectedSoundData || m_SoundSelectionCycleMode == SoundSelectionCycleMode::ALL) { - for (const SoundData &soundData : m_SoundData) { flattenedSoundData.push_back(&soundData); } - for (const SoundSet &subSoundSet : m_SubSoundSets) { subSoundSet.GetFlattenedSoundData(flattenedSoundData, onlyGetSelectedSoundData); } + for (const SoundData& soundData: m_SoundData) { + flattenedSoundData.push_back(&soundData); + } + for (const SoundSet& subSoundSet: m_SubSoundSets) { + subSoundSet.GetFlattenedSoundData(flattenedSoundData, onlyGetSelectedSoundData); + } } else { if (m_CurrentSelection.first == false) { flattenedSoundData.push_back(&m_SoundData[m_CurrentSelection.second]); @@ -236,11 +251,11 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SoundSet::SelectNextSounds() { if (m_SoundSelectionCycleMode == SoundSelectionCycleMode::ALL) { - for (SoundSet &subSoundSet : m_SubSoundSets) { + for (SoundSet& subSoundSet: m_SubSoundSets) { if (!subSoundSet.SelectNextSounds()) { return false; } @@ -311,4 +326,4 @@ namespace RTE { return true; } -} +} // namespace RTE diff --git a/Source/Entities/SoundSet.h b/Source/Entities/SoundSet.h index c4affe429c..0ceb915b9c 100644 --- a/Source/Entities/SoundSet.h +++ b/Source/Entities/SoundSet.h @@ -14,7 +14,6 @@ namespace RTE { friend struct EntityLuaBindings; public: - SerializableOverrideMethods; /// @@ -31,7 +30,7 @@ namespace RTE { /// struct SoundData { ContentFile SoundFile; - FMOD::Sound *SoundObject; + FMOD::Sound* SoundObject; Vector Offset = Vector(); float MinimumAudibleDistance = 0.0F; float AttenuationStartDistance = -1.0F; @@ -48,7 +47,7 @@ namespace RTE { /// /// A reference to the SoundSet to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const SoundSet &reference); + int Create(const SoundSet& reference); #pragma endregion #pragma region Destruction @@ -70,21 +69,21 @@ namespace RTE { /// /// A Reader lined up to the value of the property to be read. /// SoundData for the newly read sound. - static SoundData ReadAndGetSoundData(Reader &reader); + static SoundData ReadAndGetSoundData(Reader& reader); /// /// Handles turning a SoundCelectionCycleMode from its user-friendly name in INI to its enum value, using the static SoundSelectionCycleMap. /// /// A Reader lined up to the value of the property to be read. /// The appropriate SoundSelectionCycleMode for the given INI value. - static SoundSelectionCycleMode ReadSoundSelectionCycleMode(Reader &reader); + static SoundSelectionCycleMode ReadSoundSelectionCycleMode(Reader& reader); /// /// Handles writing the given SoundSelectionCycleMode out to the given Writer, using the static SoundSelectionCycleMap. /// /// A Writer filled in with the property to write to. /// The SoundSelectionCycleMode to write. - static void SaveSoundSelectionCycleMode(Writer &writer, SoundSelectionCycleMode soundSelectionCycleMode); + static void SaveSoundSelectionCycleMode(Writer& writer, SoundSelectionCycleMode soundSelectionCycleMode); #pragma endregion #pragma region SoundData and SoundSet Addition @@ -92,14 +91,14 @@ namespace RTE { /// Adds a new sound to this SoundSet, spitting out a Lua error if it fails. The sound will have default configuration. /// /// A path to the new sound to add. This will be handled through PresetMan. - void AddSound(const std::string &soundFilePath) { AddSound(soundFilePath, false); } + void AddSound(const std::string& soundFilePath) { AddSound(soundFilePath, false); } /// /// Adds a new sound to this SoundSet, either spitting out a Lua error or aborting if it fails. The sound will have default configuration. /// /// A path to the new sound to add. This will be handled through PresetMan. /// Whether to abort the game if the sound couldn't be added, or just show a console error. - void AddSound(const std::string &soundFilePath, bool abortGameForInvalidSound) { AddSound(soundFilePath, Vector(), 0, -1, abortGameForInvalidSound); } + void AddSound(const std::string& soundFilePath, bool abortGameForInvalidSound) { AddSound(soundFilePath, Vector(), 0, -1, abortGameForInvalidSound); } /// /// Adds a new sound to this SoundSet, spitting out a Lua error if it fails. The sound will be configured based on parameters. @@ -108,7 +107,7 @@ namespace RTE { /// The offset position to play this sound at, where (0, 0) is no offset. /// The minimum distance at which this sound will be audible. 0 means there is none, which is normally the case. /// The attenuation start distance for this sound, -1 sets it to default. - void AddSound(const std::string &soundFilePath, const Vector &offset, float minimumAudibleDistance, float attenuationStartDistance) { AddSound(soundFilePath, offset, minimumAudibleDistance, attenuationStartDistance, false); } + void AddSound(const std::string& soundFilePath, const Vector& offset, float minimumAudibleDistance, float attenuationStartDistance) { AddSound(soundFilePath, offset, minimumAudibleDistance, attenuationStartDistance, false); } /// /// Adds a new sound to this SoundSet, either spitting out a Lua error or aborting if it fails. The sound will be configured based on parameters. @@ -118,14 +117,14 @@ namespace RTE { /// The minimum distance at which this sound will be audible. 0 means there is none, which is normally the case. /// The attenuation start distance for this sound, -1 sets it to default. /// Whether to abort the game if the sound couldn't be added, or just show a console error. - void AddSound(const std::string &soundFilePath, const Vector &offset, float minimumAudibleDistance, float attenuationStartDistance, bool abortGameForInvalidSound); + void AddSound(const std::string& soundFilePath, const Vector& offset, float minimumAudibleDistance, float attenuationStartDistance, bool abortGameForInvalidSound); /// /// Removes all instances of the sound with the given filepath from this SoundSet. Does not remove it from any sub-SoundSets. /// /// The path to the sound to be removed from this SoundSet. /// Whether or not a sound with the given filepath was found in this SoundSet. - bool RemoveSound(const std::string &soundFilePath) { return RemoveSound(soundFilePath, false); } + bool RemoveSound(const std::string& soundFilePath) { return RemoveSound(soundFilePath, false); } /// /// Removes all instances of the sound with the given filepath from this SoundSet, optionally removing it from all sub-SoundSets as well. @@ -133,19 +132,19 @@ namespace RTE { /// The path to the sound to be removed from this SoundSet. /// Whether or not to remove the sound from any sub-SoundSets as well as this SoundSet. /// Whether or not a sound with the given filepath was found in this SoundSet or, if set to remove from sub-SoundSets, any of its sub-SoundSets. - bool RemoveSound(const std::string &soundFilePath, bool removeFromSubSoundSets); + bool RemoveSound(const std::string& soundFilePath, bool removeFromSubSoundSets); /// /// Adds a copy of the given SoundData to this SoundSet. /// /// The SoundData to copy to this SoundSet. - void AddSoundData(const SoundData &soundDataToAdd) { m_SoundData.push_back(soundDataToAdd); } + void AddSoundData(const SoundData& soundDataToAdd) { m_SoundData.push_back(soundDataToAdd); } /// /// Adds a copy of the passed in SoundSet as a sub SoundSet of this SoundSet. Ownership IS transferred! /// /// A reference to the SoundSet to be copied in as a sub SoundSet of this SoundSet. Ownership IS transferred! - void AddSoundSet(const SoundSet &soundSetToAdd) { m_SubSoundSets.push_back(soundSetToAdd); } + void AddSoundSet(const SoundSet& soundSetToAdd) { m_SubSoundSets.push_back(soundSetToAdd); } #pragma endregion #pragma region Getters and Setters @@ -165,27 +164,32 @@ namespace RTE { /// Sets the SoundSelectionCycleMode for this SoundSet, which is used to determine what SoundSet to select next time SelectNextSounds is called. /// /// The new SoundSelectionCycleMode for this SoundSet. - void SetSoundSelectionCycleMode(SoundSelectionCycleMode newSoundSelectionCycleMode) { m_SoundSelectionCycleMode = newSoundSelectionCycleMode; if (m_SoundSelectionCycleMode == SoundSelectionCycleMode::FORWARDS) { m_CurrentSelection.second = -1; } } + void SetSoundSelectionCycleMode(SoundSelectionCycleMode newSoundSelectionCycleMode) { + m_SoundSelectionCycleMode = newSoundSelectionCycleMode; + if (m_SoundSelectionCycleMode == SoundSelectionCycleMode::FORWARDS) { + m_CurrentSelection.second = -1; + } + } /// /// Fills the passed in vector with the flattened SoundData in the SoundSet, optionally only getting currently selected SoundData. /// /// A reference vector of SoundData references to be filled with this SoundSet's flattened SoundData. /// Whether to only get SoundData that is currently selected, or to get all SoundData in this SoundSet. - void GetFlattenedSoundData(std::vector &flattenedSoundData, bool onlyGetSelectedSoundData); + void GetFlattenedSoundData(std::vector& flattenedSoundData, bool onlyGetSelectedSoundData); /// /// Fills the passed in vector with the flattened SoundData in the SoundSet, optionally only getting currently selected SoundData. /// /// A reference vector of SoundData references to be filled with this SoundSet's flattened SoundData. /// Whether to only get SoundData that is currently selected, or to get all SoundData in this SoundSet. - void GetFlattenedSoundData(std::vector &flattenedSoundData, bool onlyGetSelectedSoundData) const; + void GetFlattenedSoundData(std::vector& flattenedSoundData, bool onlyGetSelectedSoundData) const; /// /// Gets the vector of SubSoundSets for this SoundSet. /// /// The vector of SubSoundSets for this SoundSet. - std::vector & GetSubSoundSets() { return m_SubSoundSets; } + std::vector& GetSubSoundSets() { return m_SubSoundSets; } #pragma endregion #pragma region Miscellaneous @@ -201,11 +205,10 @@ namespace RTE { /// Gets the class name of this Serializable. /// /// A string with the friendly-formatted type name of this object. - const std::string &GetClassName() const override { return m_sClassName; } + const std::string& GetClassName() const override { return m_sClassName; } #pragma endregion private: - static const std::string m_sClassName; //!< A string with the friendly-formatted type name of this object. static const std::unordered_map c_SoundSelectionCycleModeMap; //!< A map of strings to SoundSelectionCycleModes to support string parsing for the SoundCycleMode enum. Populated in the implementing cpp file. @@ -220,5 +223,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/TDExplosive.cpp b/Source/Entities/TDExplosive.cpp index 90f21614da..3920ae4fad 100644 --- a/Source/Entities/TDExplosive.cpp +++ b/Source/Entities/TDExplosive.cpp @@ -4,27 +4,29 @@ namespace RTE { ConcreteClassInfo(TDExplosive, ThrownDevice, 50); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TDExplosive::Clear() { m_IsAnimatedManually = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int TDExplosive::Create() { if (ThrownDevice::Create() < 0) { return -1; } - if (IsInGroup("Bombs - Payloads")) { m_HUDVisible = false; } + if (IsInGroup("Bombs - Payloads")) { + m_HUDVisible = false; + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TDExplosive::Create(const TDExplosive &reference) { + int TDExplosive::Create(const TDExplosive& reference) { if (ThrownDevice::Create(reference) < 0) { return -1; } @@ -33,14 +35,16 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TDExplosive::ReadProperty(const std::string_view &propName, Reader &reader) { + int TDExplosive::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return ThrownDevice::ReadProperty(propName, reader)); - + // TODO: Consider removing DetonationSound as GibSound already exists and could be used in its place MatchProperty("DetonationSound", { - if (!m_GibSound) { m_GibSound = new SoundContainer; } + if (!m_GibSound) { + m_GibSound = new SoundContainer; + } reader >> m_GibSound; }); MatchProperty("IsAnimatedManually", { reader >> m_IsAnimatedManually; }); @@ -48,32 +52,38 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TDExplosive::Save(Writer &writer) const { + int TDExplosive::Save(Writer& writer) const { ThrownDevice::Save(writer); writer.NewProperty("IsAnimatedManually"); writer << m_IsAnimatedManually; return 0; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TDExplosive::Update() { ThrownDevice::Update(); if (m_Activated) { // Display active frame if no animation mode has been defined - if (!m_IsAnimatedManually && m_SpriteAnimMode == NOANIM && m_FrameCount > 1) { m_Frame = 1; } + if (!m_IsAnimatedManually && m_SpriteAnimMode == NOANIM && m_FrameCount > 1) { + m_Frame = 1; + } m_RestTimer.Reset(); m_ToSettle = false; } - if (m_Activated && m_ActivationTimer.GetElapsedSimTimeMS() >= m_TriggerDelay) { GibThis(); } + if (m_Activated && m_ActivationTimer.GetElapsedSimTimeMS() >= m_TriggerDelay) { + GibThis(); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TDExplosive::DrawHUD(BITMAP *targetBitmap, const Vector &targetPos, int whichScreen, bool playerControlled) { - if (m_HUDVisible && !m_Activated) { ThrownDevice::DrawHUD(targetBitmap, targetPos, whichScreen); } + void TDExplosive::DrawHUD(BITMAP* targetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { + if (m_HUDVisible && !m_Activated) { + ThrownDevice::DrawHUD(targetBitmap, targetPos, whichScreen); + } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/TDExplosive.h b/Source/Entities/TDExplosive.h index 9f6dcb7acd..cba784c83d 100644 --- a/Source/Entities/TDExplosive.h +++ b/Source/Entities/TDExplosive.h @@ -11,7 +11,6 @@ namespace RTE { class TDExplosive : public ThrownDevice { public: - EntityAllocation(TDExplosive); SerializableOverrideMethods; ClassInfoGetters; @@ -33,7 +32,7 @@ namespace RTE { /// /// A reference to the TDExplosive to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const TDExplosive &reference); + int Create(const TDExplosive& reference); #pragma endregion #pragma region Destruction @@ -46,12 +45,20 @@ namespace RTE { /// Destroys and resets (through Clear()) the SceneLayer object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { ThrownDevice::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + ThrownDevice::Destroy(); + } + Clear(); + } /// /// Resets the entire TDExplosive, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); ThrownDevice::Reset(); } + void Reset() override { + Clear(); + ThrownDevice::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -81,26 +88,24 @@ namespace RTE { /// The absolute position of the target bitmap's upper left corner in the Scene. /// Which player's screen this is being drawn to. May affect what HUD elements get drawn etc. /// Whether or not this MovableObject is currently player controlled (not applicable for TDExplosive). - void DrawHUD(BITMAP *targetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; + void DrawHUD(BITMAP* targetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. bool m_IsAnimatedManually; //!< If true m_Frame is not changed during an update hence the animation is done by external Lua code. private: - /// /// Clears all the member variables of this TDExplosive, effectively resetting the members of this abstraction level only. /// void Clear(); // Disallow the use of some implicit methods. - TDExplosive(const TDExplosive &reference) = delete; - TDExplosive & operator=(const TDExplosive &rhs) = delete; + TDExplosive(const TDExplosive& reference) = delete; + TDExplosive& operator=(const TDExplosive& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/TerrainDebris.cpp b/Source/Entities/TerrainDebris.cpp index e470867b30..d9307888b4 100644 --- a/Source/Entities/TerrainDebris.cpp +++ b/Source/Entities/TerrainDebris.cpp @@ -5,7 +5,7 @@ namespace RTE { ConcreteClassInfo(TerrainDebris, Entity, 0); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TerrainDebris::Clear() { m_DebrisFile.Reset(); @@ -25,7 +25,7 @@ namespace RTE { m_Density = 0.01F; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int TerrainDebris::Create() { Entity::Create(); @@ -34,9 +34,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainDebris::Create(const TerrainDebris &reference) { + int TerrainDebris::Create(const TerrainDebris& reference) { Entity::Create(reference); m_DebrisFile = reference.m_DebrisFile; @@ -59,11 +59,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainDebris::ReadProperty(const std::string_view &propName, Reader &reader) { + int TerrainDebris::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); - + MatchProperty("DebrisFile", { reader >> m_DebrisFile; }); MatchProperty("DebrisPieceCount", { reader >> m_BitmapCount; @@ -73,7 +73,9 @@ namespace RTE { MatchProperty("TargetMaterial", { reader >> m_TargetMaterial; }); MatchProperty("DebrisPlacementMode", { m_DebrisPlacementMode = static_cast(std::stoi(reader.ReadPropValue())); - if (m_DebrisPlacementMode < DebrisPlacementMode::NoPlacementRestrictions || m_DebrisPlacementMode > DebrisPlacementMode::OnOverhangAndCavityOverhang) { reader.ReportError("Invalid TerrainDebris placement mode!"); } + if (m_DebrisPlacementMode < DebrisPlacementMode::NoPlacementRestrictions || m_DebrisPlacementMode > DebrisPlacementMode::OnOverhangAndCavityOverhang) { + reader.ReportError("Invalid TerrainDebris placement mode!"); + } }); MatchProperty("OnlyBuried", { reader >> m_OnlyBuried; }); MatchProperty("MinDepth", { reader >> m_MinDepth; }); @@ -84,14 +86,13 @@ namespace RTE { MatchProperty("CanVFlip", { reader >> m_CanVFlip; }); MatchProperty("FlipChance", { reader >> m_FlipChance; }); MatchProperty("DensityPerMeter", { reader >> m_Density; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainDebris::Save(Writer &writer) const { + int TerrainDebris::Save(Writer& writer) const { Entity::Save(writer); writer.NewPropertyWithValue("DebrisFile", m_DebrisFile); @@ -112,10 +113,10 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool TerrainDebris::GetPiecePlacementPosition(SLTerrain *terrain, Box &possiblePiecePosition) const { - BITMAP *matBitmap = terrain->GetMaterialBitmap(); + bool TerrainDebris::GetPiecePlacementPosition(SLTerrain* terrain, Box& possiblePiecePosition) const { + BITMAP* matBitmap = terrain->GetMaterialBitmap(); int posX = RandomNum(0, matBitmap->w); int depth = RandomNum(m_MinDepth, m_MaxDepth); int buriedDepthOffset = m_OnlyBuried ? static_cast(possiblePiecePosition.GetHeight() * 0.6F) : 0; @@ -129,10 +130,10 @@ namespace RTE { int materialCheckPixel = _getpixel(matBitmap, posX, posY); if (materialCheckPixel != MaterialColorKeys::g_MaterialAir) { // TODO: Adding depth here will properly place debris only on target material, rather than any random pixel within depth range from the original target material pixel. - int depthAdjustedPosY = posY;// + (depth * (scanForOverhang ? -1 : 1)); + int depthAdjustedPosY = posY; // + (depth * (scanForOverhang ? -1 : 1)); if (scanForOverhang ? (depthAdjustedPosY > 0) : (depthAdjustedPosY < matBitmap->h)) { // TODO: Enable the depth check once multi TargetMaterial debris is supported. - //materialCheckPixel = _getpixel(matBitmap, posX, depthAdjustedPosY); + // materialCheckPixel = _getpixel(matBitmap, posX, depthAdjustedPosY); if (MaterialPixelIsValidTarget(materialCheckPixel, prevMaterialCheckPixel)) { surfacePosY += (depth + buriedDepthOffset); overhangPosY -= (depth + buriedDepthOffset); @@ -148,7 +149,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool TerrainDebris::MaterialPixelIsValidTarget(int materialCheckPixel, int prevMaterialCheckPixel) const { bool checkResult = true; @@ -160,15 +161,21 @@ namespace RTE { switch (m_DebrisPlacementMode) { case DebrisPlacementMode::OnSurfaceOnly: case DebrisPlacementMode::OnOverhangOnly: - if (prevMaterialCheckPixel != MaterialColorKeys::g_MaterialAir) { checkResult = false; } + if (prevMaterialCheckPixel != MaterialColorKeys::g_MaterialAir) { + checkResult = false; + } break; case DebrisPlacementMode::OnCavitySurfaceOnly: case DebrisPlacementMode::OnCavityOverhangOnly: - if (prevMaterialCheckPixel != MaterialColorKeys::g_MaterialCavity) { checkResult = false; } + if (prevMaterialCheckPixel != MaterialColorKeys::g_MaterialCavity) { + checkResult = false; + } break; case DebrisPlacementMode::OnSurfaceAndCavitySurface: case DebrisPlacementMode::OnOverhangAndCavityOverhang: - if (prevMaterialCheckPixel != MaterialColorKeys::g_MaterialAir && prevMaterialCheckPixel != MaterialColorKeys::g_MaterialCavity) { checkResult = false; } + if (prevMaterialCheckPixel != MaterialColorKeys::g_MaterialAir && prevMaterialCheckPixel != MaterialColorKeys::g_MaterialCavity) { + checkResult = false; + } break; default: // No placement restrictions, pixel just needs to be of target material. @@ -178,12 +185,12 @@ namespace RTE { return checkResult; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainDebris::DrawToTerrain(SLTerrain *terrain, BITMAP *bitmapToDraw, const Vector &position) const { + void TerrainDebris::DrawToTerrain(SLTerrain* terrain, BITMAP* bitmapToDraw, const Vector& position) const { // Create a square temp bitmap that is larger than the original to avoid clipping if rotating. int dimensions = 10 + std::max(bitmapToDraw->w, bitmapToDraw->h); - BITMAP *tempDrawBitmap = create_bitmap_ex(8, dimensions, dimensions); + BITMAP* tempDrawBitmap = create_bitmap_ex(8, dimensions, dimensions); clear_bitmap(tempDrawBitmap); // Offset the original bitmap on the temp bitmap so it's centered, otherwise will be positioned incorrectly and can clip if rotated or flipped. @@ -191,7 +198,7 @@ namespace RTE { int offsetY = (dimensions - bitmapToDraw->h) / 2; blit(bitmapToDraw, tempDrawBitmap, 0, 0, offsetX, offsetY, bitmapToDraw->w, bitmapToDraw->h); - BITMAP *tempFlipAndRotBitmap = nullptr; + BITMAP* tempFlipAndRotBitmap = nullptr; if (m_CanHFlip || m_CanVFlip || m_MinRotation != 0 || m_MaxRotation != 0) { tempFlipAndRotBitmap = create_bitmap_ex(8, dimensions, dimensions); @@ -214,28 +221,32 @@ namespace RTE { draw_sprite(terrain->GetFGColorBitmap(), tempDrawBitmap, position.GetFloorIntX() - offsetX, position.GetFloorIntY() - offsetY); draw_character_ex(terrain->GetMaterialBitmap(), tempDrawBitmap, position.GetFloorIntX() - offsetX, position.GetFloorIntY() - offsetY, m_Material.GetIndex(), -1); - if (tempFlipAndRotBitmap) { destroy_bitmap(tempFlipAndRotBitmap); } + if (tempFlipAndRotBitmap) { + destroy_bitmap(tempFlipAndRotBitmap); + } destroy_bitmap(tempDrawBitmap); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainDebris::ScatterOnTerrain(SLTerrain *terrain) { + void TerrainDebris::ScatterOnTerrain(SLTerrain* terrain) { RTEAssert(!m_Bitmaps.empty() && m_BitmapCount > 0, "No bitmaps loaded for terrain debris during TerrainDebris::ScatterOnTerrain!"); // Reference. Do not remove. - //acquire_bitmap(terrain->GetFGColorBitmap()); - //acquire_bitmap(terrain->GetMaterialBitmap()); + // acquire_bitmap(terrain->GetFGColorBitmap()); + // acquire_bitmap(terrain->GetMaterialBitmap()); int possiblePieceToPlaceCount = static_cast((static_cast(terrain->GetMaterialBitmap()->w) * c_MPP) * m_Density); for (int piece = 0; piece < possiblePieceToPlaceCount; ++piece) { int pieceBitmapIndex = RandomNum(0, m_BitmapCount - 1); RTEAssert(pieceBitmapIndex >= 0 && pieceBitmapIndex < m_BitmapCount, "Bitmap index was out of bounds during TerrainDebris::ScatterOnTerrain!"); Box possiblePiecePosition(Vector(), static_cast(m_Bitmaps[pieceBitmapIndex]->w), static_cast(m_Bitmaps.at(pieceBitmapIndex)->h)); - if (GetPiecePlacementPosition(terrain, possiblePiecePosition)) { DrawToTerrain(terrain, m_Bitmaps[pieceBitmapIndex], possiblePiecePosition.GetCorner()); } + if (GetPiecePlacementPosition(terrain, possiblePiecePosition)) { + DrawToTerrain(terrain, m_Bitmaps[pieceBitmapIndex], possiblePiecePosition.GetCorner()); + } } // Reference. Do not remove. - //release_bitmap(terrain->GetMaterialBitmap()); - //release_bitmap(terrain->GetFGColorBitmap()); + // release_bitmap(terrain->GetMaterialBitmap()); + // release_bitmap(terrain->GetFGColorBitmap()); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/TerrainDebris.h b/Source/Entities/TerrainDebris.h index ddcf01a29f..90c9d65b8f 100644 --- a/Source/Entities/TerrainDebris.h +++ b/Source/Entities/TerrainDebris.h @@ -16,7 +16,6 @@ namespace RTE { class TerrainDebris : public Entity { public: - EntityAllocation(TerrainDebris); SerializableOverrideMethods; ClassInfoGetters; @@ -38,7 +37,7 @@ namespace RTE { /// /// A reference to the TerrainDebris to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const TerrainDebris &reference); + int Create(const TerrainDebris& reference); #pragma endregion #pragma region Destruction @@ -51,7 +50,12 @@ namespace RTE { /// Destroys and resets (through Clear()) the TerrainDebris object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + Entity::Destroy(); + } + Clear(); + } #pragma endregion #pragma region Concrete Methods @@ -59,11 +63,10 @@ namespace RTE { /// Places random pieces of this TerrainDebris's at random positions on the specified SLTerrain. /// /// The SLTerrain to scatter this TerrainDebris on. Ownership is NOT transferred! - void ScatterOnTerrain(SLTerrain *terrain); + void ScatterOnTerrain(SLTerrain* terrain); #pragma endregion private: - /// /// Enumeration for the different debris placement modes. /// @@ -80,7 +83,7 @@ namespace RTE { static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. ContentFile m_DebrisFile; //!< ContentFile containing the path to the debris sprites. - std::vector m_Bitmaps; //!< All the different bitmaps of this debris. Not owned. + std::vector m_Bitmaps; //!< All the different bitmaps of this debris. Not owned. int m_BitmapCount; //!< How many individual pieces this debris has. Material m_Material; //!< The Material of the debris. @@ -108,7 +111,7 @@ namespace RTE { /// Pointer to the SLTerrain to check debris placement on. Ownership is NOT transferred! /// A Box that holds the debris piece's dimensions. The center position of the Box will be modified during checking. /// True if a valid placement position was found, which means the passed in Box's center or corner positions are good to be used as the piece's drawing position. - bool GetPiecePlacementPosition(SLTerrain *terrain, Box &possiblePiecePosition) const; + bool GetPiecePlacementPosition(SLTerrain* terrain, Box& possiblePiecePosition) const; /// /// Checks whether the passed in pixel color value is of target Material, and if extra conditions apply for it to be valid for placement, depending on DebrisPlacementMode. @@ -124,7 +127,7 @@ namespace RTE { /// Pointer to the SLTerrain to draw the debris piece on. Ownership is NOT transferred! /// The BITMAP to draw. Ownership is NOT transferred! /// The position to draw the debris piece on the terrain. - void DrawToTerrain(SLTerrain *terrain, BITMAP *bitmapToDraw, const Vector &position) const; + void DrawToTerrain(SLTerrain* terrain, BITMAP* bitmapToDraw, const Vector& position) const; #pragma endregion /// @@ -133,8 +136,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - TerrainDebris(const TerrainDebris &reference) = delete; - void operator=(const TerrainDebris &rhs) = delete; + TerrainDebris(const TerrainDebris& reference) = delete; + void operator=(const TerrainDebris& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/TerrainFrosting.cpp b/Source/Entities/TerrainFrosting.cpp index 260bf29cd0..82ea45d6d6 100644 --- a/Source/Entities/TerrainFrosting.cpp +++ b/Source/Entities/TerrainFrosting.cpp @@ -5,7 +5,7 @@ namespace RTE { const std::string TerrainFrosting::c_ClassName = "TerrainFrosting"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TerrainFrosting::Clear() { m_FrostingMaterial.Reset(); @@ -15,24 +15,23 @@ namespace RTE { m_InAirOnly = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainFrosting::ReadProperty(const std::string_view &propName, Reader &reader) { + int TerrainFrosting::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("FrostingMaterial", { reader >> m_FrostingMaterial; }); MatchProperty("TargetMaterial", { reader >> m_TargetMaterial; }); MatchProperty("MinThickness", { reader >> m_MinThickness; }); MatchProperty("MaxThickness", { reader >> m_MaxThickness; }); MatchProperty("InAirOnly", { reader >> m_InAirOnly; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainFrosting::Save(Writer &writer) const { + int TerrainFrosting::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("FrostingMaterial", m_FrostingMaterial); @@ -44,21 +43,21 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainFrosting::FrostTerrain(SLTerrain *terrain) const { - BITMAP *frostingTexture = m_FrostingMaterial.GetFGTexture(); - BITMAP *fgColorBitmap = terrain->GetFGColorBitmap(); - BITMAP *matBitmap = terrain->GetBitmap(); + void TerrainFrosting::FrostTerrain(SLTerrain* terrain) const { + BITMAP* frostingTexture = m_FrostingMaterial.GetFGTexture(); + BITMAP* fgColorBitmap = terrain->GetFGColorBitmap(); + BITMAP* matBitmap = terrain->GetBitmap(); bool targetMatFound = false; bool applyingFrosting = false; int appliedThickness = 0; // Reference. Do not remove. - //acquire_bitmap(matBitmap); - //acquire_bitmap(fgColorBitmap); - //if (frostingTexture) { acquire_bitmap(frostingTexture); } + // acquire_bitmap(matBitmap); + // acquire_bitmap(fgColorBitmap); + // if (frostingTexture) { acquire_bitmap(frostingTexture); } for (int xPos = 0; xPos < matBitmap->w; ++xPos) { int thicknessGoal = RandomNum(m_MinThickness, m_MaxThickness); @@ -83,8 +82,8 @@ namespace RTE { } } // Reference. Do not remove. - //if (frostingTexture) { release_bitmap(frostingTexture); } - //release_bitmap(fgColorBitmap); - //release_bitmap(matBitmap); + // if (frostingTexture) { release_bitmap(frostingTexture); } + // release_bitmap(fgColorBitmap); + // release_bitmap(matBitmap); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/TerrainFrosting.h b/Source/Entities/TerrainFrosting.h index c2cb37a26a..3bf6defaaf 100644 --- a/Source/Entities/TerrainFrosting.h +++ b/Source/Entities/TerrainFrosting.h @@ -13,15 +13,16 @@ namespace RTE { class TerrainFrosting : public Serializable { public: - SerializableClassNameGetter - SerializableOverrideMethods + SerializableOverrideMethods #pragma region Creation - /// - /// Constructor method used to instantiate a TerrainFrosting object in system memory and make it ready for use. - /// - TerrainFrosting() { Clear(); } + /// + /// Constructor method used to instantiate a TerrainFrosting object in system memory and make it ready for use. + /// + TerrainFrosting() { + Clear(); + } #pragma endregion #pragma region Concrete Methods @@ -29,11 +30,10 @@ namespace RTE { /// Draws the frosting layer to the specified SLTerrain according to the read-in parameters. /// /// The SLTerrain to frost. Ownership is NOT transferred! - void FrostTerrain(SLTerrain *terrain) const; + void FrostTerrain(SLTerrain* terrain) const; #pragma endregion private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. Material m_FrostingMaterial; //!< The Material this frosting is made of. @@ -48,8 +48,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - TerrainFrosting(const TerrainFrosting &reference) = delete; - void operator=(const TerrainFrosting &rhs) = delete; + TerrainFrosting(const TerrainFrosting& reference) = delete; + void operator=(const TerrainFrosting& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/TerrainObject.cpp b/Source/Entities/TerrainObject.cpp index 515843a4a5..97274fcdca 100644 --- a/Source/Entities/TerrainObject.cpp +++ b/Source/Entities/TerrainObject.cpp @@ -6,7 +6,7 @@ namespace RTE { ConcreteClassInfo(TerrainObject, SceneObject, 0); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TerrainObject::Clear() { m_FGColorFile.Reset(); @@ -20,7 +20,7 @@ namespace RTE { m_ChildObjects.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int TerrainObject::Create() { SceneObject::Create(); @@ -29,15 +29,19 @@ namespace RTE { int bitmapWidth = GetBitmapWidth(); int bitmapHeight = GetBitmapHeight(); - if (bitmapWidth > 24) { m_BitmapOffset.SetX(-(static_cast(bitmapWidth / 2))); } - if (bitmapHeight > 24) { m_BitmapOffset.SetY(-(static_cast(bitmapHeight / 2))); } + if (bitmapWidth > 24) { + m_BitmapOffset.SetX(-(static_cast(bitmapWidth / 2))); + } + if (bitmapHeight > 24) { + m_BitmapOffset.SetY(-(static_cast(bitmapHeight / 2))); + } } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainObject::Create(const TerrainObject &reference) { + int TerrainObject::Create(const TerrainObject& reference) { SceneObject::Create(reference); m_FGColorFile = reference.m_FGColorFile; @@ -49,17 +53,17 @@ namespace RTE { m_BitmapOffset = reference.m_BitmapOffset; m_OffsetDefined = reference.m_OffsetDefined; - for (const SceneObject::SOPlacer &childObject : reference.m_ChildObjects) { + for (const SceneObject::SOPlacer& childObject: reference.m_ChildObjects) { m_ChildObjects.emplace_back(childObject); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainObject::ReadProperty(const std::string_view &propName, Reader &reader) { + int TerrainObject::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneObject::ReadProperty(propName, reader)); - + MatchProperty("FGColorFile", { reader >> m_FGColorFile; m_FGColorBitmap = m_FGColorFile.GetAsBitmap(); @@ -93,26 +97,34 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainObject::Save(Writer &writer) const { + int TerrainObject::Save(Writer& writer) const { SceneObject::Save(writer); - if (!m_FGColorFile.GetDataPath().empty()) { writer.NewPropertyWithValue("FGColorFile", m_FGColorFile); } - if (!m_BGColorFile.GetDataPath().empty()) { writer.NewPropertyWithValue("BGColorFile", m_BGColorFile); } - if (!m_MaterialFile.GetDataPath().empty()) { writer.NewPropertyWithValue("MaterialFile", m_MaterialFile); } + if (!m_FGColorFile.GetDataPath().empty()) { + writer.NewPropertyWithValue("FGColorFile", m_FGColorFile); + } + if (!m_BGColorFile.GetDataPath().empty()) { + writer.NewPropertyWithValue("BGColorFile", m_BGColorFile); + } + if (!m_MaterialFile.GetDataPath().empty()) { + writer.NewPropertyWithValue("MaterialFile", m_MaterialFile); + } - if (m_OffsetDefined) { writer.NewPropertyWithValue("BitmapOffset", m_BitmapOffset); } + if (m_OffsetDefined) { + writer.NewPropertyWithValue("BitmapOffset", m_BitmapOffset); + } - for (const SceneObject::SOPlacer &childObject : m_ChildObjects) { + for (const SceneObject::SOPlacer& childObject: m_ChildObjects) { writer.NewPropertyWithValue("AddChildObject", childObject); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP * TerrainObject::GetGraphicalIcon() const { + BITMAP* TerrainObject::GetGraphicalIcon() const { if (m_FGColorBitmap) { // Check several spots on the FG bitmap, to be sure it has parts that aren't transparent. If not, show the background layer instead. int piece = m_FGColorBitmap->w / 10; @@ -123,18 +135,18 @@ namespace RTE { return m_BGColorBitmap; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TerrainObject::SetTeam(int team) { SceneObject::SetTeam(team); - for (SceneObject::SOPlacer &childObject : m_ChildObjects) { + for (SceneObject::SOPlacer& childObject: m_ChildObjects) { childObject.SetTeam(team); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool TerrainObject::IsOnScenePoint(Vector &scenePoint) const { + bool TerrainObject::IsOnScenePoint(Vector& scenePoint) const { // TODO: TAKE CARE OF WRAPPING Vector bitmapPos = m_Pos + m_BitmapOffset; if (WithinBox(scenePoint, bitmapPos, static_cast(GetBitmapWidth()), static_cast(GetBitmapHeight()))) { @@ -152,9 +164,9 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool TerrainObject::PlaceOnTerrain(SLTerrain *terrain) { + bool TerrainObject::PlaceOnTerrain(SLTerrain* terrain) { if (!terrain) { return false; } @@ -163,17 +175,17 @@ namespace RTE { // Reapply the team so all children are guaranteed to be on the same team. SetTeam(GetTeam()); - for (const SceneObject::SOPlacer &childObject : GetChildObjects()) { + for (const SceneObject::SOPlacer& childObject: GetChildObjects()) { // TODO: check if we're placing a brain, and have it replace the resident brain of the scene! g_SceneMan.AddSceneObject(childObject.GetPlacedCopy(this)); } return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainObject::Draw(BITMAP *targetBitmap, const Vector &targetPos, DrawMode drawMode, bool onlyPhysical) const { - std::array drawPos = { m_Pos + m_BitmapOffset - targetPos }; + void TerrainObject::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode drawMode, bool onlyPhysical) const { + std::array drawPos = {m_Pos + m_BitmapOffset - targetPos}; int wrapPasses = 1; // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap. @@ -206,15 +218,25 @@ namespace RTE { for (int i = 0; i < wrapPasses; ++i) { switch (drawMode) { case DrawMode::g_DrawColor: - if (HasBGColorBitmap()) { masked_blit(m_BGColorBitmap, targetBitmap, 0, 0, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY(), m_BGColorBitmap->w, m_BGColorBitmap->h); } - if (HasFGColorBitmap()) { masked_blit(m_FGColorBitmap, targetBitmap, 0, 0, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY(), m_FGColorBitmap->w, m_FGColorBitmap->h); } + if (HasBGColorBitmap()) { + masked_blit(m_BGColorBitmap, targetBitmap, 0, 0, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY(), m_BGColorBitmap->w, m_BGColorBitmap->h); + } + if (HasFGColorBitmap()) { + masked_blit(m_FGColorBitmap, targetBitmap, 0, 0, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY(), m_FGColorBitmap->w, m_FGColorBitmap->h); + } break; case DrawMode::g_DrawMaterial: - if (HasMaterialBitmap()) { masked_blit(m_MaterialBitmap, targetBitmap, 0, 0, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY(), m_MaterialBitmap->w, m_MaterialBitmap->h); } + if (HasMaterialBitmap()) { + masked_blit(m_MaterialBitmap, targetBitmap, 0, 0, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY(), m_MaterialBitmap->w, m_MaterialBitmap->h); + } break; case DrawMode::g_DrawTrans: - if (HasFGColorBitmap()) { draw_trans_sprite(targetBitmap, m_FGColorBitmap, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY()); } - if (HasBGColorBitmap()) { draw_trans_sprite(targetBitmap, m_BGColorBitmap, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY()); } + if (HasFGColorBitmap()) { + draw_trans_sprite(targetBitmap, m_FGColorBitmap, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY()); + } + if (HasBGColorBitmap()) { + draw_trans_sprite(targetBitmap, m_BGColorBitmap, drawPos.at(i).GetFloorIntX(), drawPos.at(i).GetFloorIntY()); + } break; default: break; @@ -222,35 +244,59 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainObject::DrawToTerrain(SLTerrain *terrain) { - BITMAP *terrainMatBitmap = terrain->GetMaterialBitmap(); - BITMAP *terrainBGBitmap = terrain->GetBGColorBitmap(); - BITMAP *terrainFGBitmap = terrain->GetFGColorBitmap(); + void TerrainObject::DrawToTerrain(SLTerrain* terrain) { + BITMAP* terrainMatBitmap = terrain->GetMaterialBitmap(); + BITMAP* terrainBGBitmap = terrain->GetBGColorBitmap(); + BITMAP* terrainFGBitmap = terrain->GetFGColorBitmap(); Vector posOnScene = m_Pos + m_BitmapOffset; if (terrain->WrapsX()) { if (posOnScene.GetFloorIntX() < 0) { - if (HasMaterialBitmap()) { draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX() + terrainMatBitmap->w, posOnScene.GetFloorIntY()); } - if (HasBGColorBitmap()) { draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX() + terrainBGBitmap->w, posOnScene.GetFloorIntY()); } - if (HasFGColorBitmap()) { draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX() + terrainFGBitmap->w, posOnScene.GetFloorIntY()); } + if (HasMaterialBitmap()) { + draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX() + terrainMatBitmap->w, posOnScene.GetFloorIntY()); + } + if (HasBGColorBitmap()) { + draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX() + terrainBGBitmap->w, posOnScene.GetFloorIntY()); + } + if (HasFGColorBitmap()) { + draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX() + terrainFGBitmap->w, posOnScene.GetFloorIntY()); + } } else if (posOnScene.GetFloorIntX() >= terrainMatBitmap->w - GetBitmapWidth()) { - if (HasMaterialBitmap()) { draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX() - terrainMatBitmap->w, posOnScene.GetFloorIntY()); } - if (HasBGColorBitmap()) { draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX() - terrainBGBitmap->w, posOnScene.GetFloorIntY()); } - if (HasFGColorBitmap()) { draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX() - terrainFGBitmap->w, posOnScene.GetFloorIntY()); } + if (HasMaterialBitmap()) { + draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX() - terrainMatBitmap->w, posOnScene.GetFloorIntY()); + } + if (HasBGColorBitmap()) { + draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX() - terrainBGBitmap->w, posOnScene.GetFloorIntY()); + } + if (HasFGColorBitmap()) { + draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX() - terrainFGBitmap->w, posOnScene.GetFloorIntY()); + } } } if (terrain->WrapsY()) { if (posOnScene.GetFloorIntY() < 0) { - if (HasMaterialBitmap()) { draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() + terrainMatBitmap->h); } - if (HasBGColorBitmap()) { draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() + terrainBGBitmap->h); } - if (HasFGColorBitmap()) { draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() + terrainFGBitmap->h); } + if (HasMaterialBitmap()) { + draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() + terrainMatBitmap->h); + } + if (HasBGColorBitmap()) { + draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() + terrainBGBitmap->h); + } + if (HasFGColorBitmap()) { + draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() + terrainFGBitmap->h); + } } else if (posOnScene.GetFloorIntY() >= terrainMatBitmap->h - GetBitmapHeight()) { - if (HasMaterialBitmap()) { draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() - terrainMatBitmap->h); } - if (HasBGColorBitmap()) { draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() - terrainBGBitmap->h); } - if (HasFGColorBitmap()) { draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() - terrainFGBitmap->h); } + if (HasMaterialBitmap()) { + draw_sprite(terrainMatBitmap, m_MaterialBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() - terrainMatBitmap->h); + } + if (HasBGColorBitmap()) { + draw_sprite(terrainBGBitmap, m_BGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() - terrainBGBitmap->h); + } + if (HasFGColorBitmap()) { + draw_sprite(terrainFGBitmap, m_FGColorBitmap, posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY() - terrainFGBitmap->h); + } } } if (HasMaterialBitmap()) { @@ -266,4 +312,4 @@ namespace RTE { g_SceneMan.RegisterTerrainChange(posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY(), m_FGColorBitmap->w, m_FGColorBitmap->h, ColorKeys::g_MaskColor, false); } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/TerrainObject.h b/Source/Entities/TerrainObject.h index c043afcd35..88d0c4b9af 100644 --- a/Source/Entities/TerrainObject.h +++ b/Source/Entities/TerrainObject.h @@ -14,7 +14,6 @@ namespace RTE { class TerrainObject : public SceneObject { public: - EntityAllocation(TerrainObject); SerializableOverrideMethods; ClassInfoGetters; @@ -36,7 +35,7 @@ namespace RTE { /// /// A reference to the TerrainObject to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const TerrainObject &reference); + int Create(const TerrainObject& reference); #pragma endregion #pragma region Destruction @@ -49,7 +48,12 @@ namespace RTE { /// Destroys and resets (through Clear()) the TerrainObject object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { SceneObject::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + SceneObject::Destroy(); + } + Clear(); + } #pragma endregion #pragma region Getters and Setters @@ -63,7 +67,7 @@ namespace RTE { /// Gets the BITMAP that this TerrainObject uses for its foreground color representation. /// /// A pointer to the foreground color BITMAP object. Ownership is NOT transferred! - BITMAP * GetFGColorBitmap() const { return m_FGColorBitmap; } + BITMAP* GetFGColorBitmap() const { return m_FGColorBitmap; } /// /// Returns whether this TerrainObject has any background color data. @@ -75,7 +79,7 @@ namespace RTE { /// Gets the BITMAP that this TerrainObject uses for its background color representation, if any. /// /// A pointer to the background color BITMAP object. This may be nullptr if there is no BG bitmap. Ownership is NOT transferred! - BITMAP * GetBGColorBitmap() const { return m_BGColorBitmap; } + BITMAP* GetBGColorBitmap() const { return m_BGColorBitmap; } /// /// Returns whether this TerrainObject has any material data. @@ -87,37 +91,37 @@ namespace RTE { /// Gets the BITMAP that this TerrainObject uses for its material representation, if any. /// /// A pointer to the material BITMAP object. Ownership is NOT transferred! - BITMAP * GetMaterialBitmap() const { return m_MaterialBitmap; } + BITMAP* GetMaterialBitmap() const { return m_MaterialBitmap; } /// /// Gets the offset from the position to the upper left corner of this TerrainObject's BITMAPs. /// /// A Vector describing the bitmap offset, in pixels. - const Vector & GetBitmapOffset() const { return m_BitmapOffset; } + const Vector& GetBitmapOffset() const { return m_BitmapOffset; } /// /// Gets the width of the widest BITMAP of this TerrainObject's layers. /// /// The width of this TerrainObject. - int GetBitmapWidth() const { return std::max({ m_MaterialBitmap ? m_MaterialBitmap->w : 0, m_BGColorBitmap ? m_BGColorBitmap->w : 0, m_FGColorBitmap ? m_FGColorBitmap->w : 0 }); } + int GetBitmapWidth() const { return std::max({m_MaterialBitmap ? m_MaterialBitmap->w : 0, m_BGColorBitmap ? m_BGColorBitmap->w : 0, m_FGColorBitmap ? m_FGColorBitmap->w : 0}); } /// /// Gets the height of the highest BITMAP of this TerrainObject's layers. /// /// The height of this TerrainObject. - int GetBitmapHeight() const { return std::max({ m_MaterialBitmap ? m_MaterialBitmap->h : 0, m_BGColorBitmap ? m_BGColorBitmap->h : 0, m_FGColorBitmap ? m_FGColorBitmap->h : 0 }); } + int GetBitmapHeight() const { return std::max({m_MaterialBitmap ? m_MaterialBitmap->h : 0, m_BGColorBitmap ? m_BGColorBitmap->h : 0, m_FGColorBitmap ? m_FGColorBitmap->h : 0}); } /// /// Gets the list of child objects that should be placed when this TerrainObject is placed. /// /// A reference to the list of child objects. Ownership of the list is NOT transferred! - const std::vector & GetChildObjects() const { return m_ChildObjects; } + const std::vector& GetChildObjects() const { return m_ChildObjects; } /// /// Gets a BITMAP showing a good identifiable icon of this, for use in GUI lists. /// /// A good identifiable graphical representation of this in a BITMAP, if available. If not, nullptr is returned. Ownership is NOT transferred! - BITMAP * GetGraphicalIcon() const override; + BITMAP* GetGraphicalIcon() const override; /// /// Sets which team this TerrainObject belongs to. @@ -132,7 +136,7 @@ namespace RTE { /// /// The SLTerrain to place this TerrainObject on. Ownership is NOT transferred! /// Whether the object was successfully placed on the terrain. - bool PlaceOnTerrain(SLTerrain *terrain); + bool PlaceOnTerrain(SLTerrain* terrain); #pragma endregion #pragma region Virtual Override Methods @@ -141,7 +145,7 @@ namespace RTE { /// /// The point in absolute scene coordinates. /// Whether this' graphical rep overlaps the scene point. - bool IsOnScenePoint(Vector &scenePoint) const override; + bool IsOnScenePoint(Vector& scenePoint) const override; /// /// Draws this TerrainObject's current graphical representation to a BITMAP of choice. @@ -150,21 +154,20 @@ namespace RTE { /// The absolute position of the target bitmap's upper left corner in the Scene. /// In which mode to draw in. See the DrawMode enumeration for the modes. /// Whether to not draw any extra 'ghost' items of this TerrainObject, like indicator arrows or hovering HUD text and so on. - void Draw(BITMAP *targetBitmap, const Vector &targetPos = Vector(), DrawMode drawMode = DrawMode::g_DrawColor, bool onlyPhysical = false) const override; + void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode drawMode = DrawMode::g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. ContentFile m_FGColorFile; //!< ContentFile containing the path to this TerrainObject's foreground color layer representation. - BITMAP *m_FGColorBitmap; //!< Foreground color BITMAP of this TerrainObject. + BITMAP* m_FGColorBitmap; //!< Foreground color BITMAP of this TerrainObject. ContentFile m_BGColorFile; //!< ContentFile containing the path to this TerrainObject's background color layer representation. - BITMAP *m_BGColorBitmap; //!< Background color BITMAP of this TerrainObject. + BITMAP* m_BGColorBitmap; //!< Background color BITMAP of this TerrainObject. ContentFile m_MaterialFile; //!< ContentFile containing the path to this TerrainObject's Material layer representation. - BITMAP *m_MaterialBitmap; //!< Material BITMAP of this TerrainObject. + BITMAP* m_MaterialBitmap; //!< Material BITMAP of this TerrainObject. Vector m_BitmapOffset; //!< Offset from the position of this to the top left corner of the bitmap. The inversion of this should point to a corner or pattern in the bitmaps which will snap well with a 24px grid. bool m_OffsetDefined; //!< Whether the offset has been defined and shouldn't be automatically set. @@ -172,12 +175,11 @@ namespace RTE { std::vector m_ChildObjects; //!< The objects that are placed along with this TerrainObject on the Scene. private: - /// /// Draws this TerrainObject's graphical and material representations to the specified SLTerrain's respective layers. /// /// The SLTerrain to draw this TerrainObject to. Ownership is NOT transferred! - void DrawToTerrain(SLTerrain *terrain); + void DrawToTerrain(SLTerrain* terrain); /// /// Clears all the member variables of this TerrainObject, effectively resetting the members of this abstraction level only. @@ -185,8 +187,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - TerrainObject(const TerrainObject &reference) = delete; - void operator=(const TerrainObject &rhs) = delete; + TerrainObject(const TerrainObject& reference) = delete; + void operator=(const TerrainObject& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/ThrownDevice.cpp b/Source/Entities/ThrownDevice.cpp index 007f4a8cb9..e5b527c17e 100644 --- a/Source/Entities/ThrownDevice.cpp +++ b/Source/Entities/ThrownDevice.cpp @@ -7,7 +7,7 @@ namespace RTE { ConcreteClassInfo(ThrownDevice, HeldDevice, 50); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ThrownDevice::Clear() { m_ActivationSound.Reset(); @@ -20,7 +20,7 @@ namespace RTE { m_StrikerLever = nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int ThrownDevice::Create() { if (HeldDevice::Create() < 0) { @@ -31,9 +31,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ThrownDevice::Create(const ThrownDevice &reference) { + int ThrownDevice::Create(const ThrownDevice& reference) { HeldDevice::Create(reference); m_MOType = MovableObject::TypeThrownDevice; @@ -51,11 +51,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ThrownDevice::ReadProperty(const std::string_view &propName, Reader &reader) { + int ThrownDevice::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return HeldDevice::ReadProperty(propName, reader)); - + MatchProperty("ActivationSound", { reader >> m_ActivationSound; }); MatchProperty("StartThrowOffset", { reader >> m_StartThrowOffset; }); MatchProperty("EndThrowOffset", { reader >> m_EndThrowOffset; }); @@ -63,14 +63,14 @@ namespace RTE { MatchProperty("MaxThrowVel", { reader >> m_MaxThrowVel; }); MatchProperty("TriggerDelay", { reader >> m_TriggerDelay; }); MatchProperty("ActivatesWhenReleased", { reader >> m_ActivatesWhenReleased; }); - MatchProperty("StrikerLever", { m_StrikerLever = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); + MatchProperty("StrikerLever", { m_StrikerLever = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); }); EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ThrownDevice::Save(Writer &writer) const { + int ThrownDevice::Save(Writer& writer) const { HeldDevice::Save(writer); writer.NewProperty("ActivationSound"); @@ -93,32 +93,34 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float ThrownDevice::GetCalculatedMaxThrowVelIncludingArmThrowStrength() { if (m_MaxThrowVel > 0) { return m_MaxThrowVel; - } else if (const Arm *parentAsArm = dynamic_cast(GetParent())) { + } else if (const Arm* parentAsArm = dynamic_cast(GetParent())) { return (parentAsArm->GetThrowStrength() + std::abs(GetRootParent()->GetAngularVel() * 0.5F)) / std::sqrt(std::abs(GetMass()) + 1.0F); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ThrownDevice::ResetAllTimers() { double elapsedTime = m_Activated ? m_ActivationTimer.GetElapsedSimTimeMS() : 0; HeldDevice::ResetAllTimers(); - if (m_Activated) { m_ActivationTimer.SetElapsedSimTimeMS(elapsedTime); } + if (m_Activated) { + m_ActivationTimer.SetElapsedSimTimeMS(elapsedTime); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ThrownDevice::Activate() { if (!m_Activated) { m_ActivationTimer.Reset(); m_ActivationSound.Play(m_Pos); - if (MovableObject *strikerLever = m_StrikerLever ? dynamic_cast(m_StrikerLever->Clone()) : nullptr) { + if (MovableObject* strikerLever = m_StrikerLever ? dynamic_cast(m_StrikerLever->Clone()) : nullptr) { Vector randomVel(m_Vel.GetMagnitude() * 0.25F + 1.0F, 0); strikerLever->SetVel(m_Vel * 0.5F + randomVel.RadRotate(c_PI * RandomNormalNum())); @@ -133,4 +135,4 @@ namespace RTE { m_Activated = true; } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/ThrownDevice.h b/Source/Entities/ThrownDevice.h index af6fa43b20..6314e832c0 100644 --- a/Source/Entities/ThrownDevice.h +++ b/Source/Entities/ThrownDevice.h @@ -11,7 +11,6 @@ namespace RTE { class ThrownDevice : public HeldDevice { public: - EntityAllocation(ThrownDevice); SerializableOverrideMethods; ClassInfoGetters; @@ -33,7 +32,7 @@ namespace RTE { /// /// A reference to the ThrownDevice to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const ThrownDevice &reference); + int Create(const ThrownDevice& reference); #pragma endregion #pragma region Destruction @@ -46,12 +45,20 @@ namespace RTE { /// Destroys and resets (through Clear()) the SceneLayer object. /// /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. - void Destroy(bool notInherited = false) override { if (!notInherited) { HeldDevice::Destroy(); } Clear(); } + void Destroy(bool notInherited = false) override { + if (!notInherited) { + HeldDevice::Destroy(); + } + Clear(); + } /// /// Resets the entire ThrownDevice, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Attachable::Reset(); } + void Reset() override { + Clear(); + Attachable::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -131,11 +138,15 @@ namespace RTE { /// /// Does the calculations necessary to detect whether this ThrownDevice is at rest or not. IsAtRest() retrieves the answer. /// - void RestDetection() override { HeldDevice::RestDetection(); if (m_Activated) { m_RestTimer.Reset(); } } + void RestDetection() override { + HeldDevice::RestDetection(); + if (m_Activated) { + m_RestTimer.Reset(); + } + } #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. SoundContainer m_ActivationSound; //!< Activation sound. @@ -146,18 +157,17 @@ namespace RTE { float m_MaxThrowVel; //!< The maximum throw velocity this gets when thrown. long m_TriggerDelay; //!< Time in millisecs from the time of being thrown to triggering whatever it is that this ThrownDevice does. bool m_ActivatesWhenReleased; //!< Whether this activates when its throw is started, or waits until it is released from the arm that is throwing it. - const MovableObject *m_StrikerLever; //!< Striker lever particle MovableObject preset instance. + const MovableObject* m_StrikerLever; //!< Striker lever particle MovableObject preset instance. private: - /// /// Clears all the member variables of this ThrownDevice, effectively resetting the members of this abstraction level only. /// void Clear(); // Disallow the use of some implicit methods. - ThrownDevice(const ThrownDevice &reference) = delete; - ThrownDevice & operator=(const ThrownDevice &rhs) = delete; + ThrownDevice(const ThrownDevice& reference) = delete; + ThrownDevice& operator=(const ThrownDevice& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Entities/Turret.cpp b/Source/Entities/Turret.cpp index b020845ae6..d981ed105f 100644 --- a/Source/Entities/Turret.cpp +++ b/Source/Entities/Turret.cpp @@ -7,20 +7,20 @@ namespace RTE { ConcreteClassInfo(Turret, Attachable, 20); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Turret::Clear() { m_MountedDevices.clear(); m_MountedDeviceRotationOffset = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Turret::Create(const Turret &reference) { + int Turret::Create(const Turret& reference) { if (!reference.m_MountedDevices.empty()) { - for (const HeldDevice *referenceMountedDevice : reference.m_MountedDevices) { + for (const HeldDevice* referenceMountedDevice: reference.m_MountedDevices) { m_ReferenceHardcodedAttachableUniqueIDs.insert(referenceMountedDevice->GetUniqueID()); - AddMountedDevice(dynamic_cast(referenceMountedDevice->Clone())); + AddMountedDevice(dynamic_cast(referenceMountedDevice->Clone())); } } Attachable::Create(reference); @@ -30,33 +30,36 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Turret::Destroy(bool notInherited) { - if (!notInherited) { Attachable::Destroy(); } - for (const HeldDevice *mountedDevice : m_MountedDevices) { m_HardcodedAttachableUniqueIDsAndRemovers.erase(mountedDevice->GetUniqueID()); } + if (!notInherited) { + Attachable::Destroy(); + } + for (const HeldDevice* mountedDevice: m_MountedDevices) { + m_HardcodedAttachableUniqueIDsAndRemovers.erase(mountedDevice->GetUniqueID()); + } Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Turret::ReadProperty(const std::string_view &propName, Reader &reader) { + int Turret::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Attachable::ReadProperty(propName, reader)); - - MatchProperty("MountedDevice", { SetFirstMountedDevice(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); - MatchProperty("AddMountedDevice", { AddMountedDevice(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + + MatchProperty("MountedDevice", { SetFirstMountedDevice(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); + MatchProperty("AddMountedDevice", { AddMountedDevice(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader))); }); MatchProperty("MountedDeviceRotationOffset", { reader >> m_MountedDeviceRotationOffset; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Turret::Save(Writer &writer) const { + int Turret::Save(Writer& writer) const { Attachable::Save(writer); - for (const HeldDevice *mountedDevice : m_MountedDevices) { + for (const HeldDevice* mountedDevice: m_MountedDevices) { writer.NewProperty("AddMountedDevice"); writer << mountedDevice; } @@ -66,88 +69,92 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::SetFirstMountedDevice(HeldDevice *newMountedDevice) { - if (HasMountedDevice()) { RemoveAndDeleteAttachable(m_MountedDevices[0]); } + void Turret::SetFirstMountedDevice(HeldDevice* newMountedDevice) { + if (HasMountedDevice()) { + RemoveAndDeleteAttachable(m_MountedDevices[0]); + } if (newMountedDevice != nullptr) { m_MountedDevices.emplace(m_MountedDevices.begin(), newMountedDevice); AddAttachable(newMountedDevice); - m_HardcodedAttachableUniqueIDsAndRemovers.insert({ newMountedDevice->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - HeldDevice *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to RemoveMountedDevice."); - dynamic_cast(parent)->RemoveMountedDevice(castedAttachable); - }}); + m_HardcodedAttachableUniqueIDsAndRemovers.insert({newMountedDevice->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + HeldDevice* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to RemoveMountedDevice."); + dynamic_cast(parent)->RemoveMountedDevice(castedAttachable); + }}); m_MountedDevices[0]->SetInheritsRotAngle(false); m_MountedDevices[0]->SetUnPickupable(true); m_MountedDevices[0]->SetGibWithParentChance(1.0F); - //Force weapons mounted on turrets to never be removed due to forces. This doesn't affect them gibbing from hitting their impulse limits though. + // Force weapons mounted on turrets to never be removed due to forces. This doesn't affect them gibbing from hitting their impulse limits though. m_MountedDevices[0]->SetJointStrength(0.0F); m_MountedDevices[0]->SetSupportable(true); m_MountedDevices[0]->SetSupportAvailable(true); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::AddMountedDevice(HeldDevice *newMountedDevice) { + void Turret::AddMountedDevice(HeldDevice* newMountedDevice) { if (newMountedDevice == nullptr) { return; } m_MountedDevices.emplace_back(newMountedDevice); AddAttachable(newMountedDevice); - m_HardcodedAttachableUniqueIDsAndRemovers.insert({ newMountedDevice->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { - HeldDevice *castedAttachable = dynamic_cast(attachable); - RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to RemoveMountedDevice."); - dynamic_cast(parent)->RemoveMountedDevice(castedAttachable); - }}); + m_HardcodedAttachableUniqueIDsAndRemovers.insert({newMountedDevice->GetUniqueID(), [](MOSRotating* parent, Attachable* attachable) { + HeldDevice* castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to RemoveMountedDevice."); + dynamic_cast(parent)->RemoveMountedDevice(castedAttachable); + }}); newMountedDevice->SetInheritsRotAngle(false); newMountedDevice->SetUnPickupable(true); newMountedDevice->SetGibWithParentChance(1.0F); - //Force weapons mounted on turrets to never be removed due to forces. This doesn't affect them gibbing from hitting their impulse limits though. + // Force weapons mounted on turrets to never be removed due to forces. This doesn't affect them gibbing from hitting their impulse limits though. newMountedDevice->SetJointStrength(0.0F); newMountedDevice->SetSupportable(true); newMountedDevice->SetSupportAvailable(true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Turret::Update() { - for (HeldDevice *mountedDevice : m_MountedDevices) { + for (HeldDevice* mountedDevice: m_MountedDevices) { mountedDevice->SetRotAngle(m_Rotation.GetRadAngle() + m_MountedDeviceRotationOffset); } Attachable::Update(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { + void Turret::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - //TODO replace this with a relative draw order property or something that lets you organize attachable drawing so it doesn't need special hardcoding crap. Use this for ahuman limbs and arm held mo if possible. - for (HeldDevice *mountedDevice : m_MountedDevices) { - if (mountedDevice->IsDrawnAfterParent()) { mountedDevice->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } + // TODO replace this with a relative draw order property or something that lets you organize attachable drawing so it doesn't need special hardcoding crap. Use this for ahuman limbs and arm held mo if possible. + for (HeldDevice* mountedDevice: m_MountedDevices) { + if (mountedDevice->IsDrawnAfterParent()) { + mountedDevice->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::SetParent(MOSRotating *newParent) { + void Turret::SetParent(MOSRotating* newParent) { Attachable::SetParent(newParent); - for (HeldDevice *mountedDevice : m_MountedDevices) { + for (HeldDevice* mountedDevice: m_MountedDevices) { mountedDevice->Deactivate(); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::RemoveMountedDevice(const HeldDevice *mountedDeviceToRemove) { - std::vector::iterator mountedDeviceIterator = std::find_if(m_MountedDevices.begin(), m_MountedDevices.end(), [&mountedDeviceToRemove](const HeldDevice *mountedDevice) { + void Turret::RemoveMountedDevice(const HeldDevice* mountedDeviceToRemove) { + std::vector::iterator mountedDeviceIterator = std::find_if(m_MountedDevices.begin(), m_MountedDevices.end(), [&mountedDeviceToRemove](const HeldDevice* mountedDevice) { return mountedDevice == mountedDeviceToRemove; }); m_MountedDevices.erase(mountedDeviceIterator); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Entities/Turret.h b/Source/Entities/Turret.h index 558a567173..d1c925cb42 100644 --- a/Source/Entities/Turret.h +++ b/Source/Entities/Turret.h @@ -13,7 +13,6 @@ namespace RTE { class Turret : public Attachable { public: - EntityAllocation(Turret); SerializableOverrideMethods; ClassInfoGetters; @@ -29,7 +28,7 @@ namespace RTE { /// /// A reference to the Turret to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Turret &reference); + int Create(const Turret& reference); #pragma endregion #pragma region Destruction @@ -47,7 +46,10 @@ namespace RTE { /// /// Resets the entire Turret, including its inherited members, to their default settings or values. /// - void Reset() override { Clear(); Attachable::Reset(); } + void Reset() override { + Clear(); + Attachable::Reset(); + } #pragma endregion #pragma region Getters and Setters @@ -61,27 +63,27 @@ namespace RTE { /// Gets the first mounted HeldDevice of this Turret, mostly here for Lua convenience. /// /// A pointer to mounted HeldDevice of this Turret. Ownership is NOT transferred! - HeldDevice * GetFirstMountedDevice() const { return m_MountedDevices[0]; } + HeldDevice* GetFirstMountedDevice() const { return m_MountedDevices[0]; } /// /// Sets the first mounted HeldDevice for this Turret, mostly here for Lua convenience. Ownership IS transferred! /// The current first mounted HeldDevice (if there is one) will be dropped and added to MovableMan. /// /// The new HeldDevice to use. - void SetFirstMountedDevice(HeldDevice *newMountedDevice); + void SetFirstMountedDevice(HeldDevice* newMountedDevice); /// /// Gets the vector of mounted HeldDevices for this Turret. /// /// The vector of mounted HeldDevices for this Turret. - const std::vector & GetMountedDevices() const { return m_MountedDevices; } + const std::vector& GetMountedDevices() const { return m_MountedDevices; } /// /// Adds a HeldDevice to be mounted on this Turret. Ownership IS transferred! /// Will not remove any other HeldDevices mounted on this Turret. /// /// The new HeldDevice to be mounted on this Turret. - void AddMountedDevice(HeldDevice *newMountedDevice); + void AddMountedDevice(HeldDevice* newMountedDevice); /// /// Gets the current rotational offset of the mounted HeldDevice from the rest of the Turret. @@ -109,31 +111,29 @@ namespace RTE { /// The absolute position of the target bitmap's upper left corner in the Scene. /// In which mode to draw in. See the DrawMode enumeration for the modes. /// Whether to not draw any extra 'ghost' items of this MovableObject, indicator arrows or hovering HUD text and so on. - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion protected: - /// /// Sets this Attachable's parent MOSRotating, and also sets its Team based on its parent and, if the Attachable is set to collide, adds/removes Atoms to its new/old parent. /// Additionally, deactivates all MountedDevices. /// /// A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! - void SetParent(MOSRotating *newParent) override; + void SetParent(MOSRotating* newParent) override; static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. private: - - //TODO I think things would be cleaner if this (and all hardcoded attachable pointers) used weak_ptrs. It would solve some weird ownership stuff, particularly with this. However, for that to be possible, m_Attachables has to be shared_ptrs though. - std::vector m_MountedDevices; //!< Vector of pointers to the mounted HeldDevices of this Turret, if any. Owned here. + // TODO I think things would be cleaner if this (and all hardcoded attachable pointers) used weak_ptrs. It would solve some weird ownership stuff, particularly with this. However, for that to be possible, m_Attachables has to be shared_ptrs though. + std::vector m_MountedDevices; //!< Vector of pointers to the mounted HeldDevices of this Turret, if any. Owned here. float m_MountedDeviceRotationOffset; //!< The relative offset angle (in radians) of the mounted HeldDevice from this Turret's rotation. /// /// Removes the HeldDevice from this turret's vector of mounted devices if it's in there. This releases the unique_ptr for it, leaving the caller to take care of it. /// /// A pointer to the mounted device to remove. - void RemoveMountedDevice(const HeldDevice *mountedDeviceToRemove); + void RemoveMountedDevice(const HeldDevice* mountedDeviceToRemove); /// /// Clears all the member variables of this Turret, effectively resetting the members of this abstraction level only. @@ -141,8 +141,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - Turret(const Turret &reference) = delete; - Turret & operator=(const Turret &rhs) = delete; + Turret(const Turret& reference) = delete; + Turret& operator=(const Turret& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUI.h b/Source/GUI/GUI.h index 6406e41075..00f6e3031d 100644 --- a/Source/GUI/GUI.h +++ b/Source/GUI/GUI.h @@ -13,7 +13,12 @@ /// /// The GUIRect structure defines a rectangle by the coordinates of its upper-left and lower-right corners. /// -struct GUIRect { long left; long top; long right; long bottom; }; +struct GUIRect { + long left; + long top; + long right; + long bottom; +}; /// /// Sets the bounds of a GUIRect. @@ -23,7 +28,12 @@ struct GUIRect { long left; long top; long right; long bottom; }; /// Position of top left corner on Y axis. /// Position of bottom right corner on X axis. /// Position of bottom right corner on Y axis. -inline void SetRect(GUIRect *rect, int left, int top, int right, int bottom) { rect->left = left; rect->top = top; rect->right = right; rect->bottom = bottom; } +inline void SetRect(GUIRect* rect, int left, int top, int right, int bottom) { + rect->left = left; + rect->top = top; + rect->right = right; + rect->bottom = bottom; +} #pragma endregion #ifndef GUI_STANDALONE diff --git a/Source/GUI/GUIBanner.cpp b/Source/GUI/GUIBanner.cpp index 00b355d862..920dc5f902 100644 --- a/Source/GUI/GUIBanner.cpp +++ b/Source/GUI/GUIBanner.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -16,495 +15,440 @@ namespace RTE { -std::map GUIBanner::m_sFontCache; -std::map GUIBanner::m_sCharCapCache; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIBanner -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIBanner object in system -// memory. - -GUIBanner::GUIBanner() -{ - m_pFontImage[REGULAR] = 0; - m_pFontImage[BLURRED] = 0; - memset(m_aaFontChars[REGULAR], 0, sizeof(FontChar) * MAXBANNERFONTCHARS); - memset(m_aaFontChars[BLURRED], 0, sizeof(FontChar) * MAXBANNERFONTCHARS); - m_CharIndexCap = MAXBANNERFONTCHARS; - m_FontHeight = 0; - m_Kerning = 0; - m_BannerText.clear(); - m_BannerChars.clear(); - m_TargetSize.Reset(); - m_BannerPosY = 240; - m_FlySpeed = 1500; - m_FlySpacing = 100; - m_BannerChars.clear(); - m_AnimMode = BLINKING; - m_AnimState = NOTSTARTED; - m_TotalAnimTimer.Reset(); - m_DisplayTimer.Reset(); - m_SpacingTimer.Reset(); - m_FrameTimer.Reset(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the font from an image file. - -bool GUIBanner::Create(const std::string fontFilePath, const std::string fontBlurFilePath, int bitDepth) -{ - // Package the font bitmap paths o they are more easily processed below - std::string filePaths[2] = {fontFilePath, fontBlurFilePath}; - - // Now process them and extract the character data from each - ContentFile fontFile; - std::map::iterator fontItr; - std::map::iterator indexItr; - int y, dotColor; - for (int mode = REGULAR; mode < FONTMODECOUNT; ++mode) - { - // Load the font images - fontFile.SetDataPath(filePaths[mode].c_str()); - m_pFontImage[mode] = fontFile.GetAsBitmap(bitDepth == 8 ? COLORCONV_REDUCE_TO_256 : COLORCONV_8_TO_32); - RTEAssert(m_pFontImage[mode], "Couldn't load font bitmap for banner font from this file:\n" + fontFilePath); - - // Check the color key to be the same color as the Bottom-Right hand corner pixel - int keyColor = getpixel(m_pFontImage[mode], m_pFontImage[mode]->w - 1, m_pFontImage[mode]->h - 1); -// No need (or way!) to actually set it; it's assumed to be bright pink (255, 255, 0) -// m_pFontImage[mode]->SetColorKey(keyColor); - - // Check if these files have already been read and loaded from disk before. - fontItr = m_sFontCache.find(filePaths[mode]); - - if (fontItr != m_sFontCache.end()) - { - // Yes, has been loaded previously, then use that data from memory. - memcpy(m_aaFontChars[mode], (*fontItr).second, sizeof(FontChar) * MAXBANNERFONTCHARS); - // Also retrieve the font max number of characters if we can - indexItr = m_sCharCapCache.find(filePaths[mode]); - if (indexItr != m_sCharCapCache.end()) - m_CharIndexCap = (*indexItr).second; - else - m_CharIndexCap = MAXBANNERFONTCHARS; - - // Calc the font height - the horizontally blurred one should be the same - if (mode == REGULAR) - { - // The red separator MUST be on the Top-Left hand corner - dotColor = getpixel(m_pFontImage[mode], 0, 0); - m_FontHeight = 0; - for (y = 1; y < m_pFontImage[mode]->h; y++) - { - if (getpixel(m_pFontImage[mode], 0, y) == dotColor) - { - m_FontHeight = y; - break; - } - } - } - } - // Hasn't been loaded previously, so go ahead and do so now. - else - { - // The red separator MUST be on the Top-Left hand corner - dotColor = getpixel(m_pFontImage[mode], 0, 0); - // Find the separating gap of the font lines - // Calc the font height - the horizontally blurred one should be the same - if (mode == REGULAR) - { - m_FontHeight = 0; - for (y = 1; y < m_pFontImage[mode]->h; y++) - { - if (getpixel(m_pFontImage[mode], 0, y) == dotColor) - { - m_FontHeight = y; - break; - } - } - } - - // Pre-calculate all the font character details - memset(m_aaFontChars[mode], 0, sizeof(FontChar) * MAXBANNERFONTCHARS); - - int x = 1; - y = 0; - int charOnLine = 0; - for (int chr = 32; chr < MAXBANNERFONTCHARS; chr++) - { - m_aaFontChars[mode][chr].m_Offset = x; - - // Find the next red pixel - int w = 0; - int n; - for (n = x; n < m_pFontImage[mode]->w; n++, w++) - { - if (getpixel(m_pFontImage[mode], n, y) == dotColor) - { - break; - } - } - - // Calculate the maximum height of the character - int Height = 0; - for (int j = y; j < y + m_FontHeight; j++) - { - for (int i = x; i < x + w; i++) - { - int Pixel = getpixel(m_pFontImage[mode], i, j); - if (Pixel != dotColor && Pixel != keyColor) - Height = MAX(Height, j - y); - } - } - - m_aaFontChars[mode][chr].m_Height = Height; - m_aaFontChars[mode][chr].m_Width = w - 1; - x += w + 1; - - m_CharIndexCap = chr + 1; - - charOnLine++; - if (charOnLine >= 16) - { - charOnLine = 0; - x = 1; - y += m_FontHeight; - // Stop if we run out of bitmap - if ((y + m_FontHeight) > m_pFontImage[mode]->h) - break; - } - } - } - - // Add the calculated charIndexcap to the cache so we can use it in other banner instances - // that use the same font bitmap files. - m_sCharCapCache.insert(std::pair(filePaths[mode], m_CharIndexCap)); - // Also add the now calculated font char data to the cache - // Allocate a dynamic array to throw into the map.. probably until app close - FontChar *aNewCache = new FontChar[MAXBANNERFONTCHARS]; - // Copy the font data into the cache - memcpy(aNewCache, m_aaFontChars[mode], sizeof(FontChar) * MAXBANNERFONTCHARS); - // Now put it into the cache map - m_sFontCache.insert(std::pair(filePaths[mode], aNewCache)); - } - - return true; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys the font data - -void GUIBanner::Destroy() -{ - m_BannerText.clear(); - m_BannerChars.clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SpaceBetween -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells how much space, in pixels, currently exists between two flying -// characters. - -int GUIBanner::SpaceBetween(const FlyingChar &first, FontMode firstMode, const FlyingChar &second, FontMode secondMode) const -{ - if (first.m_PosX < second.m_PosX) - return second.m_PosX - first.m_PosX - CalculateWidth(first.m_Character, firstMode); - else - return first.m_PosX - second.m_PosX - CalculateWidth(second.m_Character, secondMode); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ShowText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Starts the display animation of a text string in this banner's font. - -void GUIBanner::ShowText(const std::string &text, AnimMode mode, long duration, Vector targetSize, float yOnTarget, int flySpeed, int flySpacing) -{ - m_BannerText = text; - m_AnimMode = mode; - m_AnimState = SHOWING; - m_TotalAnimTimer.Reset(); - m_SpacingTimer.Reset(); - m_DisplayTimer.SetRealTimeLimitMS(duration); - m_DisplayTimer.Reset(); - m_TargetSize = targetSize; - m_BannerPosY = (m_TargetSize.m_Y * yOnTarget) - (m_FontHeight / 2); - m_FlySpeed = flySpeed; - m_FlySpacing = flySpacing; - - // Clear out the old text string so we can reload it with the new text string - m_BannerChars.clear(); - // Starting position of the first character - int startPosX = m_AnimMode == FLYBYLEFTWARD ? m_TargetSize.m_X : -(CalculateWidth(text, BLURRED) + ((int)(text.length() - 1) * m_FlySpacing)); - // The showing position for the first character - int showPosX = (m_TargetSize.m_X / 2) - (CalculateWidth(text, REGULAR) / 2); - int whichChar = 0; - for (std::string::const_iterator tItr = text.begin(); tItr != text.end(); ++tItr) - { - whichChar++; - // Create the flying character entry - m_BannerChars.push_back(FlyingChar((*tItr), NOTSTARTED, showPosX, m_FlySpeed)); - - if (m_AnimMode == FLYBYLEFTWARD) - { - // Set up the starting and end positions - m_BannerChars.back().m_StartPosX = startPosX; - m_BannerChars.back().m_HidePosX = -CalculateWidth((*tItr), BLURRED); - } - // Now the characters are going reverse - else if (m_AnimMode == FLYBYRIGHTWARD) - { - // Set up the starting and end positions - again, opposite of above - m_BannerChars.back().m_StartPosX = startPosX; - m_BannerChars.back().m_HidePosX = m_TargetSize.m_X; - } - // Blinking, they stay put, so no different start/end positions - else - { - - } - - // Set the current position to match the starting pos - m_BannerChars.back().m_PosX = m_BannerChars.back().m_StartPosX; - // Set up the next character's starting position - startPosX += CalculateWidth((*tItr), BLURRED) + m_FlySpacing; - // Set up the next character's showing position - showPosX += CalculateWidth((*tItr), REGULAR) + GetKerning(); - } - - // If we're flying to the right we need to reverse the order of the characters - if (m_AnimMode == FLYBYRIGHTWARD) - m_BannerChars.reverse(); - - m_FrameTimer.Reset(); - m_SpacingTimer.Reset(); -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HideText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells the banner to animate away elegantly. Especially useful when -// a ShowText is waiting with a negative duration. - -void GUIBanner::HideText() -{ - -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the position of the flying characters of this banner. - -void GUIBanner::Update() -{ - double deltaS = m_FrameTimer.GetElapsedRealTimeS(); - - // Only bother updating if things are visible at all - if (m_AnimState < SHOWING || m_AnimState > HIDING) - return; - - // Time out the showing as set - if (m_AnimState == SHOW && m_DisplayTimer.IsPastRealTimeLimit()) - m_AnimState = HIDING; - - int flyDirection = m_AnimMode == FLYBYLEFTWARD ? -1 : 1; - int whichChar = 0; - // Go through every character, updating their positions and states - std::list::iterator prevItr = m_BannerChars.end(); - for (std::list::iterator cItr = m_BannerChars.begin(); cItr != m_BannerChars.end(); ++cItr) - { - whichChar++; - // Start off each character's motion at the appropriate order and timing - if ((*cItr).m_MoveState == NOTSTARTED) - { -// // Get the very first character going, or any consecutive one after the previous has cleared off enough -// if (cItr == m_BannerChars.begin() || ((*prevItr).m_MoveState >= SHOWING && SpaceBetween(*prevItr, BLURRED, *cItr, BLURRED) >= m_FlySpacing)) - // All the spacings are now set up in the starting positions, so just let them all fly at the same time - { -// TODO: Play a starting sound? - (*cItr).m_MoveState = SHOWING; - (*cItr).m_Speed = m_FlySpeed * flyDirection; - } - } - // Move each character along that is currently in flight - if ((*cItr).m_MoveState == SHOWING) - { - (*cItr).m_PosX += (*cItr).m_Speed * deltaS; - - // Stop each character that is at or past its showing destination OR - // before going too far into its stopped predecessor, taking blurring into account - if ((m_AnimMode == FLYBYLEFTWARD && (*cItr).m_PosX <= (*cItr).m_ShowPosX) || - (m_AnimMode == FLYBYRIGHTWARD && (*cItr).m_PosX >= (*cItr).m_ShowPosX) || - (cItr != m_BannerChars.begin() && (*prevItr).m_MoveState == SHOW && SpaceBetween(*prevItr, REGULAR, *cItr, BLURRED) < m_Kerning)) - { -// TODO: Play a stopping sound? - (*cItr).m_PosX = (*cItr).m_ShowPosX; - (*cItr).m_MoveState = SHOW; - (*cItr).m_Speed = 0; - // If all characters are now showing, update the overall animation state of the banner to reflect that - if (whichChar == m_BannerChars.size() && m_BannerChars.front().m_MoveState == SHOW) - { - m_AnimState = SHOW; - m_DisplayTimer.Reset(); - } - } - } - // If we're supposed to be hiding the shown chars, then do so in an orderly fashion - if ((*cItr).m_MoveState == SHOW && m_AnimState >= HIDING) - { - // Get the very first character going, or any consecutive one after the previous has cleared off enough - if (cItr == m_BannerChars.begin() || ((*prevItr).m_MoveState >= HIDING && SpaceBetween(*prevItr, BLURRED, *cItr, BLURRED) >= m_FlySpacing)) - { - (*cItr).m_MoveState = HIDING; - (*cItr).m_Speed = m_FlySpeed * flyDirection; - } - } - // If we're hiding chars, check if they have gone off the screen completely and can now be retired - if ((*cItr).m_MoveState == HIDING) - { - (*cItr).m_PosX += (*cItr).m_Speed * deltaS; - - // Stop each character that has now traveled off the screen - if ((m_AnimMode == FLYBYLEFTWARD && (*cItr).m_PosX <= (*cItr).m_HidePosX) || - (m_AnimMode == FLYBYRIGHTWARD && (*cItr).m_PosX >= (*cItr).m_HidePosX)) - { - (*cItr).m_PosX = (*cItr).m_HidePosX; - (*cItr).m_MoveState = OVER; -// Let them travel off screen -// (*cItr).m_Speed = 0; - // If this was the last character to fly off screen, the the banner showing is over - if (whichChar == m_BannerChars.size()) - m_AnimState = OVER; - } - } - // Keep traveling these suckers even off screen, because char spacings larger than half the screen will cause problems otherwise - if ((*cItr).m_MoveState == OVER) - { - (*cItr).m_PosX += (*cItr).m_Speed * deltaS; - } - - // Update the iterator we'll use on the next char to check distances to etc - prevItr = cItr; - } - - m_FrameTimer.Reset(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws text to a bitmap. - -void GUIBanner::Draw(BITMAP *pTargetBitmap) -{ - // Only bother drawing if things are visible at all - if (m_AnimState < SHOWING || m_AnimState > HIDING) - return; - - // Go through every character in the banner, drawing the ones that are showing - unsigned char c; - int mode, charWidth, offX, offY; - for (std::list::iterator cItr = m_BannerChars.begin(); cItr != m_BannerChars.end(); ++cItr) - { - // Only draw anything if the character is even visible - if ((*cItr).m_MoveState >= SHOWING && (*cItr).m_MoveState <= HIDING) - { - // Validate the character - c = (*cItr).m_Character; - if (c == '\n') - { - - } - if (c == '\t') - { - - } - if (c < 0) - c += m_CharIndexCap; - if (c < 32 || c >= m_CharIndexCap) - continue; - - // Figure out where on the font bitmap we're cutting out the character from - mode = (*cItr).m_MoveState == SHOW ? REGULAR : BLURRED; - charWidth = m_aaFontChars[mode][c].m_Width; - offX = m_aaFontChars[mode][c].m_Offset; - offY = ((c - 32) / 16) * m_FontHeight; - - // Draw the character onto the target - masked_blit(m_pFontImage[mode], pTargetBitmap, offX, offY, (*cItr).m_PosX, m_BannerPosY, charWidth, m_FontHeight); - } - } -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculateWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the width of a piece of text. - -int GUIBanner::CalculateWidth(const std::string text, FontMode mode) const -{ - unsigned char c; - int Width = 0; - - // Go through every character - for(int i = 0; i < text.length(); i++) - { - c = text.at(i); - - // Reset line counting if newline encountered - if (c == '\n') - { -/* No newlines allowed in banners - if (Width > WidestLine) - WidestLine = Width; - Width = 0; -*/ - continue; - } - if (c < 0) - c += m_CharIndexCap; - if (c < 32 || c >= m_CharIndexCap) - continue; - - Width += m_aaFontChars[mode][c].m_Width; - - // Add kerning, IF NOT BLURRED - //if (i < Text.length() - 1) - if (mode != BLURRED) - Width += m_Kerning; - } - - return Width; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculateWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the width of a piece of text. - -int GUIBanner::CalculateWidth(const char Character, FontMode mode) const -{ - unsigned char c = Character; - if (c >= 32 && c < m_CharIndexCap) - return m_aaFontChars[mode][c].m_Width + m_Kerning; - - return 0; -} + std::map GUIBanner::m_sFontCache; + std::map GUIBanner::m_sCharCapCache; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIBanner + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIBanner object in system + // memory. + + GUIBanner::GUIBanner() { + m_pFontImage[REGULAR] = 0; + m_pFontImage[BLURRED] = 0; + memset(m_aaFontChars[REGULAR], 0, sizeof(FontChar) * MAXBANNERFONTCHARS); + memset(m_aaFontChars[BLURRED], 0, sizeof(FontChar) * MAXBANNERFONTCHARS); + m_CharIndexCap = MAXBANNERFONTCHARS; + m_FontHeight = 0; + m_Kerning = 0; + m_BannerText.clear(); + m_BannerChars.clear(); + m_TargetSize.Reset(); + m_BannerPosY = 240; + m_FlySpeed = 1500; + m_FlySpacing = 100; + m_BannerChars.clear(); + m_AnimMode = BLINKING; + m_AnimState = NOTSTARTED; + m_TotalAnimTimer.Reset(); + m_DisplayTimer.Reset(); + m_SpacingTimer.Reset(); + m_FrameTimer.Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the font from an image file. + + bool GUIBanner::Create(const std::string fontFilePath, const std::string fontBlurFilePath, int bitDepth) { + // Package the font bitmap paths o they are more easily processed below + std::string filePaths[2] = {fontFilePath, fontBlurFilePath}; + + // Now process them and extract the character data from each + ContentFile fontFile; + std::map::iterator fontItr; + std::map::iterator indexItr; + int y, dotColor; + for (int mode = REGULAR; mode < FONTMODECOUNT; ++mode) { + // Load the font images + fontFile.SetDataPath(filePaths[mode].c_str()); + m_pFontImage[mode] = fontFile.GetAsBitmap(bitDepth == 8 ? COLORCONV_REDUCE_TO_256 : COLORCONV_8_TO_32); + RTEAssert(m_pFontImage[mode], "Couldn't load font bitmap for banner font from this file:\n" + fontFilePath); + + // Check the color key to be the same color as the Bottom-Right hand corner pixel + int keyColor = getpixel(m_pFontImage[mode], m_pFontImage[mode]->w - 1, m_pFontImage[mode]->h - 1); + // No need (or way!) to actually set it; it's assumed to be bright pink (255, 255, 0) + // m_pFontImage[mode]->SetColorKey(keyColor); + + // Check if these files have already been read and loaded from disk before. + fontItr = m_sFontCache.find(filePaths[mode]); + + if (fontItr != m_sFontCache.end()) { + // Yes, has been loaded previously, then use that data from memory. + memcpy(m_aaFontChars[mode], (*fontItr).second, sizeof(FontChar) * MAXBANNERFONTCHARS); + // Also retrieve the font max number of characters if we can + indexItr = m_sCharCapCache.find(filePaths[mode]); + if (indexItr != m_sCharCapCache.end()) + m_CharIndexCap = (*indexItr).second; + else + m_CharIndexCap = MAXBANNERFONTCHARS; + + // Calc the font height - the horizontally blurred one should be the same + if (mode == REGULAR) { + // The red separator MUST be on the Top-Left hand corner + dotColor = getpixel(m_pFontImage[mode], 0, 0); + m_FontHeight = 0; + for (y = 1; y < m_pFontImage[mode]->h; y++) { + if (getpixel(m_pFontImage[mode], 0, y) == dotColor) { + m_FontHeight = y; + break; + } + } + } + } + // Hasn't been loaded previously, so go ahead and do so now. + else { + // The red separator MUST be on the Top-Left hand corner + dotColor = getpixel(m_pFontImage[mode], 0, 0); + // Find the separating gap of the font lines + // Calc the font height - the horizontally blurred one should be the same + if (mode == REGULAR) { + m_FontHeight = 0; + for (y = 1; y < m_pFontImage[mode]->h; y++) { + if (getpixel(m_pFontImage[mode], 0, y) == dotColor) { + m_FontHeight = y; + break; + } + } + } + + // Pre-calculate all the font character details + memset(m_aaFontChars[mode], 0, sizeof(FontChar) * MAXBANNERFONTCHARS); + + int x = 1; + y = 0; + int charOnLine = 0; + for (int chr = 32; chr < MAXBANNERFONTCHARS; chr++) { + m_aaFontChars[mode][chr].m_Offset = x; + + // Find the next red pixel + int w = 0; + int n; + for (n = x; n < m_pFontImage[mode]->w; n++, w++) { + if (getpixel(m_pFontImage[mode], n, y) == dotColor) { + break; + } + } + + // Calculate the maximum height of the character + int Height = 0; + for (int j = y; j < y + m_FontHeight; j++) { + for (int i = x; i < x + w; i++) { + int Pixel = getpixel(m_pFontImage[mode], i, j); + if (Pixel != dotColor && Pixel != keyColor) + Height = MAX(Height, j - y); + } + } + + m_aaFontChars[mode][chr].m_Height = Height; + m_aaFontChars[mode][chr].m_Width = w - 1; + x += w + 1; + + m_CharIndexCap = chr + 1; + + charOnLine++; + if (charOnLine >= 16) { + charOnLine = 0; + x = 1; + y += m_FontHeight; + // Stop if we run out of bitmap + if ((y + m_FontHeight) > m_pFontImage[mode]->h) + break; + } + } + } + + // Add the calculated charIndexcap to the cache so we can use it in other banner instances + // that use the same font bitmap files. + m_sCharCapCache.insert(std::pair(filePaths[mode], m_CharIndexCap)); + // Also add the now calculated font char data to the cache + // Allocate a dynamic array to throw into the map.. probably until app close + FontChar* aNewCache = new FontChar[MAXBANNERFONTCHARS]; + // Copy the font data into the cache + memcpy(aNewCache, m_aaFontChars[mode], sizeof(FontChar) * MAXBANNERFONTCHARS); + // Now put it into the cache map + m_sFontCache.insert(std::pair(filePaths[mode], aNewCache)); + } + + return true; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys the font data + + void GUIBanner::Destroy() { + m_BannerText.clear(); + m_BannerChars.clear(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SpaceBetween + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells how much space, in pixels, currently exists between two flying + // characters. + + int GUIBanner::SpaceBetween(const FlyingChar& first, FontMode firstMode, const FlyingChar& second, FontMode secondMode) const { + if (first.m_PosX < second.m_PosX) + return second.m_PosX - first.m_PosX - CalculateWidth(first.m_Character, firstMode); + else + return first.m_PosX - second.m_PosX - CalculateWidth(second.m_Character, secondMode); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ShowText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Starts the display animation of a text string in this banner's font. + + void GUIBanner::ShowText(const std::string& text, AnimMode mode, long duration, Vector targetSize, float yOnTarget, int flySpeed, int flySpacing) { + m_BannerText = text; + m_AnimMode = mode; + m_AnimState = SHOWING; + m_TotalAnimTimer.Reset(); + m_SpacingTimer.Reset(); + m_DisplayTimer.SetRealTimeLimitMS(duration); + m_DisplayTimer.Reset(); + m_TargetSize = targetSize; + m_BannerPosY = (m_TargetSize.m_Y * yOnTarget) - (m_FontHeight / 2); + m_FlySpeed = flySpeed; + m_FlySpacing = flySpacing; + + // Clear out the old text string so we can reload it with the new text string + m_BannerChars.clear(); + // Starting position of the first character + int startPosX = m_AnimMode == FLYBYLEFTWARD ? m_TargetSize.m_X : -(CalculateWidth(text, BLURRED) + ((int)(text.length() - 1) * m_FlySpacing)); + // The showing position for the first character + int showPosX = (m_TargetSize.m_X / 2) - (CalculateWidth(text, REGULAR) / 2); + int whichChar = 0; + for (std::string::const_iterator tItr = text.begin(); tItr != text.end(); ++tItr) { + whichChar++; + // Create the flying character entry + m_BannerChars.push_back(FlyingChar((*tItr), NOTSTARTED, showPosX, m_FlySpeed)); + + if (m_AnimMode == FLYBYLEFTWARD) { + // Set up the starting and end positions + m_BannerChars.back().m_StartPosX = startPosX; + m_BannerChars.back().m_HidePosX = -CalculateWidth((*tItr), BLURRED); + } + // Now the characters are going reverse + else if (m_AnimMode == FLYBYRIGHTWARD) { + // Set up the starting and end positions - again, opposite of above + m_BannerChars.back().m_StartPosX = startPosX; + m_BannerChars.back().m_HidePosX = m_TargetSize.m_X; + } + // Blinking, they stay put, so no different start/end positions + else { + } + + // Set the current position to match the starting pos + m_BannerChars.back().m_PosX = m_BannerChars.back().m_StartPosX; + // Set up the next character's starting position + startPosX += CalculateWidth((*tItr), BLURRED) + m_FlySpacing; + // Set up the next character's showing position + showPosX += CalculateWidth((*tItr), REGULAR) + GetKerning(); + } + + // If we're flying to the right we need to reverse the order of the characters + if (m_AnimMode == FLYBYRIGHTWARD) + m_BannerChars.reverse(); + + m_FrameTimer.Reset(); + m_SpacingTimer.Reset(); + } + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HideText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells the banner to animate away elegantly. Especially useful when + // a ShowText is waiting with a negative duration. + + void GUIBanner::HideText() + { + + } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the position of the flying characters of this banner. + + void GUIBanner::Update() { + double deltaS = m_FrameTimer.GetElapsedRealTimeS(); + + // Only bother updating if things are visible at all + if (m_AnimState < SHOWING || m_AnimState > HIDING) + return; + + // Time out the showing as set + if (m_AnimState == SHOW && m_DisplayTimer.IsPastRealTimeLimit()) + m_AnimState = HIDING; + + int flyDirection = m_AnimMode == FLYBYLEFTWARD ? -1 : 1; + int whichChar = 0; + // Go through every character, updating their positions and states + std::list::iterator prevItr = m_BannerChars.end(); + for (std::list::iterator cItr = m_BannerChars.begin(); cItr != m_BannerChars.end(); ++cItr) { + whichChar++; + // Start off each character's motion at the appropriate order and timing + if ((*cItr).m_MoveState == NOTSTARTED) { + // // Get the very first character going, or any consecutive one after the previous has cleared off enough + // if (cItr == m_BannerChars.begin() || ((*prevItr).m_MoveState >= SHOWING && SpaceBetween(*prevItr, BLURRED, *cItr, BLURRED) >= m_FlySpacing)) + // All the spacings are now set up in the starting positions, so just let them all fly at the same time + { + // TODO: Play a starting sound? + (*cItr).m_MoveState = SHOWING; + (*cItr).m_Speed = m_FlySpeed * flyDirection; + } + } + // Move each character along that is currently in flight + if ((*cItr).m_MoveState == SHOWING) { + (*cItr).m_PosX += (*cItr).m_Speed * deltaS; + + // Stop each character that is at or past its showing destination OR + // before going too far into its stopped predecessor, taking blurring into account + if ((m_AnimMode == FLYBYLEFTWARD && (*cItr).m_PosX <= (*cItr).m_ShowPosX) || + (m_AnimMode == FLYBYRIGHTWARD && (*cItr).m_PosX >= (*cItr).m_ShowPosX) || + (cItr != m_BannerChars.begin() && (*prevItr).m_MoveState == SHOW && SpaceBetween(*prevItr, REGULAR, *cItr, BLURRED) < m_Kerning)) { + // TODO: Play a stopping sound? + (*cItr).m_PosX = (*cItr).m_ShowPosX; + (*cItr).m_MoveState = SHOW; + (*cItr).m_Speed = 0; + // If all characters are now showing, update the overall animation state of the banner to reflect that + if (whichChar == m_BannerChars.size() && m_BannerChars.front().m_MoveState == SHOW) { + m_AnimState = SHOW; + m_DisplayTimer.Reset(); + } + } + } + // If we're supposed to be hiding the shown chars, then do so in an orderly fashion + if ((*cItr).m_MoveState == SHOW && m_AnimState >= HIDING) { + // Get the very first character going, or any consecutive one after the previous has cleared off enough + if (cItr == m_BannerChars.begin() || ((*prevItr).m_MoveState >= HIDING && SpaceBetween(*prevItr, BLURRED, *cItr, BLURRED) >= m_FlySpacing)) { + (*cItr).m_MoveState = HIDING; + (*cItr).m_Speed = m_FlySpeed * flyDirection; + } + } + // If we're hiding chars, check if they have gone off the screen completely and can now be retired + if ((*cItr).m_MoveState == HIDING) { + (*cItr).m_PosX += (*cItr).m_Speed * deltaS; + + // Stop each character that has now traveled off the screen + if ((m_AnimMode == FLYBYLEFTWARD && (*cItr).m_PosX <= (*cItr).m_HidePosX) || + (m_AnimMode == FLYBYRIGHTWARD && (*cItr).m_PosX >= (*cItr).m_HidePosX)) { + (*cItr).m_PosX = (*cItr).m_HidePosX; + (*cItr).m_MoveState = OVER; + // Let them travel off screen + // (*cItr).m_Speed = 0; + // If this was the last character to fly off screen, the the banner showing is over + if (whichChar == m_BannerChars.size()) + m_AnimState = OVER; + } + } + // Keep traveling these suckers even off screen, because char spacings larger than half the screen will cause problems otherwise + if ((*cItr).m_MoveState == OVER) { + (*cItr).m_PosX += (*cItr).m_Speed * deltaS; + } + + // Update the iterator we'll use on the next char to check distances to etc + prevItr = cItr; + } + + m_FrameTimer.Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws text to a bitmap. + + void GUIBanner::Draw(BITMAP* pTargetBitmap) { + // Only bother drawing if things are visible at all + if (m_AnimState < SHOWING || m_AnimState > HIDING) + return; + + // Go through every character in the banner, drawing the ones that are showing + unsigned char c; + int mode, charWidth, offX, offY; + for (std::list::iterator cItr = m_BannerChars.begin(); cItr != m_BannerChars.end(); ++cItr) { + // Only draw anything if the character is even visible + if ((*cItr).m_MoveState >= SHOWING && (*cItr).m_MoveState <= HIDING) { + // Validate the character + c = (*cItr).m_Character; + if (c == '\n') { + } + if (c == '\t') { + } + if (c < 0) + c += m_CharIndexCap; + if (c < 32 || c >= m_CharIndexCap) + continue; + + // Figure out where on the font bitmap we're cutting out the character from + mode = (*cItr).m_MoveState == SHOW ? REGULAR : BLURRED; + charWidth = m_aaFontChars[mode][c].m_Width; + offX = m_aaFontChars[mode][c].m_Offset; + offY = ((c - 32) / 16) * m_FontHeight; + + // Draw the character onto the target + masked_blit(m_pFontImage[mode], pTargetBitmap, offX, offY, (*cItr).m_PosX, m_BannerPosY, charWidth, m_FontHeight); + } + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculateWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the width of a piece of text. + + int GUIBanner::CalculateWidth(const std::string text, FontMode mode) const { + unsigned char c; + int Width = 0; + + // Go through every character + for (int i = 0; i < text.length(); i++) { + c = text.at(i); + + // Reset line counting if newline encountered + if (c == '\n') { + /* No newlines allowed in banners + if (Width > WidestLine) + WidestLine = Width; + Width = 0; + */ + continue; + } + if (c < 0) + c += m_CharIndexCap; + if (c < 32 || c >= m_CharIndexCap) + continue; + + Width += m_aaFontChars[mode][c].m_Width; + + // Add kerning, IF NOT BLURRED + // if (i < Text.length() - 1) + if (mode != BLURRED) + Width += m_Kerning; + } + + return Width; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculateWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the width of a piece of text. + + int GUIBanner::CalculateWidth(const char Character, FontMode mode) const { + unsigned char c = Character; + if (c >= 32 && c < m_CharIndexCap) + return m_aaFontChars[mode][c].m_Width + m_Kerning; + + return 0; + } } // namespace RTE diff --git a/Source/GUI/GUIBanner.h b/Source/GUI/GUIBanner.h index 98448bed7e..6ea692716e 100644 --- a/Source/GUI/GUIBanner.h +++ b/Source/GUI/GUIBanner.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - #include "Vector.h" #include "Timer.h" #include "allegro.h" @@ -19,311 +18,300 @@ struct BITMAP; #define MAXBANNERFONTCHARS 256 -namespace RTE -{ - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: GUIBanner -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A class to handle the drawing of LARGE text banners that fly across -// the screen, grabbing the player's attention. -// Parent(s): None. -// Class history: 5/7/2011 GUIBanner Created. - -class GUIBanner { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - enum FontMode - { - REGULAR = 0, - BLURRED, - FONTMODECOUNT - }; - - enum AnimMode - { - BLINKING = 0, - FLYBYLEFTWARD, - FLYBYRIGHTWARD, - ANIMMODECOUNT - }; - - enum AnimState - { - NOTSTARTED = 0, - SHOWING, - SHOW, - HIDING, - OVER, - ANIMSTATECOUNT - }; - - // Font character - struct FontChar { - int m_Width; - int m_Height; - int m_Offset; - }; - - // Flying characters - struct FlyingChar { - FlyingChar(char character, AnimState state, int showPosX, float speed) {m_Character = character; m_MoveState = state; m_PosX = m_StartPosX = m_ShowPosX = m_HidePosX = showPosX; m_Speed = speed; } - char m_Character; - AnimState m_MoveState; - int m_PosX; - int m_StartPosX; - int m_ShowPosX; - int m_HidePosX; - float m_Speed; - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIBanner -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIBanner object in system -// memory. -// Arguments: None. -// Return value: None. - - GUIBanner(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the font from an image file. -// Arguments: Path to the font bitmap file. -// Path to the blurred font bitmap file. -// At which color bit depth to load the font files as. -// Return value: None. - - bool Create(const std::string fontFilePath, const std::string fontBlurFilePath, int bitDepth); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys the font data -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBannerText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the currently displayed text string. -// Arguments: None. -// Return value: The currently displayed text string. - - std::string GetBannerText() const { return m_BannerText; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAnimState -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current state of the overall animation of this banner. -// Arguments: None. -// Return value: The current state of the animation. - - AnimState GetAnimState() const { return m_AnimState; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether this banner is currently showing anything on screen. -// Arguments: None. -// Return value: Whether this is showing anything presently. - - bool IsVisible() const { return m_AnimState >= SHOWING && m_AnimState <= HIDING; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculateWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the width of a piece of text. -// Arguments: Text. -// Return value: None. - - int CalculateWidth(const std::string Text, FontMode mode) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculateWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the width of a piece of text. -// Arguments: Character. -// Return value: None. - - int CalculateWidth(const char Character, FontMode mode) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFontHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the font height. -// Arguments: None. -// Return value: The font height in pixels. - - int GetFontHeight() const { return m_FontHeight; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetKerning -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get the character kerning (spacing) -// Arguments: None. -// Return value: Spacing between characters, in pixels. 1 = one empty pixel -// between chars, 0 = chars are touching. - - int GetKerning() const { return m_Kerning; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetKerning -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Set the character kerning (spacing), in pixels. 1 = one empty pixel -// between chars, 0 = chars are touching. -// Arguments: The new kerning value. -// Return value: None. - - void SetKerning(int newKerning = 1) { m_Kerning = newKerning; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SpaceBetween -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells how much space, in pixels, currently exists between two flying -// characters. -// Arguments: The first FlyingChar. -// The font mode of the first character. -// The second FlyingChar. -// The font mode of the second character. -// Return value: The space, in pixels. - - int SpaceBetween(const FlyingChar &first, FontMode firstMode, const FlyingChar &second, FontMode secondMode) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ShowText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Starts the display animation of a text string in this banner's font. -// This only needs to be called once per display animation. Any currently -// played animation will be interrupted and the banner restarted. -// Arguments: The text to display. -// The animation mode to display the text in. -// The duration of the animation the text is displayed in. Negative value -// means the text pauses at the display/center until HideText is called. -// The width and height of the bitmap target this will be displayed on. -// The Y position the banner should appear on the target, in normalized -// value. 0.5 = banner midline is centered on halfway down the target. -// The speed at which the characters will fly, in pixels per second. -// The spacing between the flying characters, in pixels. -// Return value: None. - - void ShowText(const std::string &text, AnimMode mode, long duration, Vector targetSize, float yOnTarget, int flySpeed = 1500, int flySpacing = 100); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HideText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells the banner to animate away elegantly. Especially useful when -// a ShowText is waiting with a negative duration. -// Arguments: The speed at which the characters will fly, in pixels per second. -// The spacing between the flying characters, in pixels. -// Return value: None. - - void HideText(int flySpeed = 1500, int flySpacing = 100) { if (m_AnimState <= SHOW) { m_AnimState = HIDING; } m_FlySpeed = flySpeed; m_FlySpacing = flySpacing; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Abruptly clears any text without animating it away. Resets this thing. -// Arguments: None. -// Return value: None. - - void ClearText() { m_BannerText.clear(); m_BannerChars.clear(); m_AnimState = NOTSTARTED; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the position of the flying characters of this banner. -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws text to a bitmap. -// Arguments: The target bitmap to draw to. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - // Font bitmap files - not owned - BITMAP *m_pFontImage[FONTMODECOUNT]; - - // The loaded font information for each filepath to a font bitmap - static std::map m_sFontCache; - // Cache of the highest indices of valid characters that was read in from the file - static std::map m_sCharCapCache; - - // The actual character info for this specific banner font - FontChar m_aaFontChars[FONTMODECOUNT][MAXBANNERFONTCHARS]; - // The highest index of valid characters that was read in from the file - int m_CharIndexCap; - - // Height of the font - int m_FontHeight; - // Spacing between characters, in pixels - int m_Kerning; - - // The text string currently being displayed - std::string m_BannerText; - // The actual characters of this banner and their positions etc - std::list m_BannerChars; - // The dimensions of the screen area that this banner is displayed on - Vector m_TargetSize; - // The pixel Y position that the banner has on the target - int m_BannerPosY; - // The speed at which the characters will fly, in pixels per second - int m_FlySpeed; - // The spacing between characters when they fly, in pixels - int m_FlySpacing; - // The mode of animation to show the above text in - AnimMode m_AnimMode; - // The current state of the animation - AnimState m_AnimState; - // The timer that keeps track of the total display animation. - Timer m_TotalAnimTimer; - // The timer that keeps track of how long to wait with the banner displaying before animating it away. - Timer m_DisplayTimer; - // Timer for keeping track of how long between individual characters are shown - Timer m_SpacingTimer; - // Timer for keeping track of how long passed since last call to Update - Timer m_FrameTimer; -}; +namespace RTE { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: GUIBanner + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A class to handle the drawing of LARGE text banners that fly across + // the screen, grabbing the player's attention. + // Parent(s): None. + // Class history: 5/7/2011 GUIBanner Created. + + class GUIBanner { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + enum FontMode { + REGULAR = 0, + BLURRED, + FONTMODECOUNT + }; + + enum AnimMode { + BLINKING = 0, + FLYBYLEFTWARD, + FLYBYRIGHTWARD, + ANIMMODECOUNT + }; + + enum AnimState { + NOTSTARTED = 0, + SHOWING, + SHOW, + HIDING, + OVER, + ANIMSTATECOUNT + }; + + // Font character + struct FontChar { + int m_Width; + int m_Height; + int m_Offset; + }; + + // Flying characters + struct FlyingChar { + FlyingChar(char character, AnimState state, int showPosX, float speed) { + m_Character = character; + m_MoveState = state; + m_PosX = m_StartPosX = m_ShowPosX = m_HidePosX = showPosX; + m_Speed = speed; + } + char m_Character; + AnimState m_MoveState; + int m_PosX; + int m_StartPosX; + int m_ShowPosX; + int m_HidePosX; + float m_Speed; + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIBanner + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIBanner object in system + // memory. + // Arguments: None. + // Return value: None. + + GUIBanner(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the font from an image file. + // Arguments: Path to the font bitmap file. + // Path to the blurred font bitmap file. + // At which color bit depth to load the font files as. + // Return value: None. + + bool Create(const std::string fontFilePath, const std::string fontBlurFilePath, int bitDepth); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys the font data + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBannerText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the currently displayed text string. + // Arguments: None. + // Return value: The currently displayed text string. + + std::string GetBannerText() const { return m_BannerText; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAnimState + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current state of the overall animation of this banner. + // Arguments: None. + // Return value: The current state of the animation. + + AnimState GetAnimState() const { return m_AnimState; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether this banner is currently showing anything on screen. + // Arguments: None. + // Return value: Whether this is showing anything presently. + + bool IsVisible() const { return m_AnimState >= SHOWING && m_AnimState <= HIDING; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculateWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the width of a piece of text. + // Arguments: Text. + // Return value: None. + + int CalculateWidth(const std::string Text, FontMode mode) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculateWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the width of a piece of text. + // Arguments: Character. + // Return value: None. + + int CalculateWidth(const char Character, FontMode mode) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFontHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the font height. + // Arguments: None. + // Return value: The font height in pixels. + + int GetFontHeight() const { return m_FontHeight; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetKerning + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get the character kerning (spacing) + // Arguments: None. + // Return value: Spacing between characters, in pixels. 1 = one empty pixel + // between chars, 0 = chars are touching. + + int GetKerning() const { return m_Kerning; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetKerning + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Set the character kerning (spacing), in pixels. 1 = one empty pixel + // between chars, 0 = chars are touching. + // Arguments: The new kerning value. + // Return value: None. + + void SetKerning(int newKerning = 1) { m_Kerning = newKerning; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SpaceBetween + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells how much space, in pixels, currently exists between two flying + // characters. + // Arguments: The first FlyingChar. + // The font mode of the first character. + // The second FlyingChar. + // The font mode of the second character. + // Return value: The space, in pixels. + + int SpaceBetween(const FlyingChar& first, FontMode firstMode, const FlyingChar& second, FontMode secondMode) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ShowText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Starts the display animation of a text string in this banner's font. + // This only needs to be called once per display animation. Any currently + // played animation will be interrupted and the banner restarted. + // Arguments: The text to display. + // The animation mode to display the text in. + // The duration of the animation the text is displayed in. Negative value + // means the text pauses at the display/center until HideText is called. + // The width and height of the bitmap target this will be displayed on. + // The Y position the banner should appear on the target, in normalized + // value. 0.5 = banner midline is centered on halfway down the target. + // The speed at which the characters will fly, in pixels per second. + // The spacing between the flying characters, in pixels. + // Return value: None. + + void ShowText(const std::string& text, AnimMode mode, long duration, Vector targetSize, float yOnTarget, int flySpeed = 1500, int flySpacing = 100); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HideText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells the banner to animate away elegantly. Especially useful when + // a ShowText is waiting with a negative duration. + // Arguments: The speed at which the characters will fly, in pixels per second. + // The spacing between the flying characters, in pixels. + // Return value: None. + + void HideText(int flySpeed = 1500, int flySpacing = 100) { + if (m_AnimState <= SHOW) { + m_AnimState = HIDING; + } + m_FlySpeed = flySpeed; + m_FlySpacing = flySpacing; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Abruptly clears any text without animating it away. Resets this thing. + // Arguments: None. + // Return value: None. + + void ClearText() { + m_BannerText.clear(); + m_BannerChars.clear(); + m_AnimState = NOTSTARTED; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the position of the flying characters of this banner. + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws text to a bitmap. + // Arguments: The target bitmap to draw to. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + // Font bitmap files - not owned + BITMAP* m_pFontImage[FONTMODECOUNT]; + + // The loaded font information for each filepath to a font bitmap + static std::map m_sFontCache; + // Cache of the highest indices of valid characters that was read in from the file + static std::map m_sCharCapCache; + + // The actual character info for this specific banner font + FontChar m_aaFontChars[FONTMODECOUNT][MAXBANNERFONTCHARS]; + // The highest index of valid characters that was read in from the file + int m_CharIndexCap; + + // Height of the font + int m_FontHeight; + // Spacing between characters, in pixels + int m_Kerning; + + // The text string currently being displayed + std::string m_BannerText; + // The actual characters of this banner and their positions etc + std::list m_BannerChars; + // The dimensions of the screen area that this banner is displayed on + Vector m_TargetSize; + // The pixel Y position that the banner has on the target + int m_BannerPosY; + // The speed at which the characters will fly, in pixels per second + int m_FlySpeed; + // The spacing between characters when they fly, in pixels + int m_FlySpacing; + // The mode of animation to show the above text in + AnimMode m_AnimMode; + // The current state of the animation + AnimState m_AnimState; + // The timer that keeps track of the total display animation. + Timer m_TotalAnimTimer; + // The timer that keeps track of how long to wait with the banner displaying before animating it away. + Timer m_DisplayTimer; + // Timer for keeping track of how long between individual characters are shown + Timer m_SpacingTimer; + // Timer for keeping track of how long passed since last call to Update + Timer m_FrameTimer; + }; }; // namespace RTE -#endif // _GUIBanner_ \ No newline at end of file +#endif // _GUIBanner_ \ No newline at end of file diff --git a/Source/GUI/GUIButton.cpp b/Source/GUI/GUIButton.cpp index e6b0993b0d..01e825ec23 100644 --- a/Source/GUI/GUIButton.cpp +++ b/Source/GUI/GUIButton.cpp @@ -7,7 +7,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIButton::GUIButton(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUIButton::GUIButton(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "BUTTON"; m_DrawBitmap = nullptr; m_ControlManager = ControlManager; @@ -20,7 +21,7 @@ GUIButton::GUIButton(GUIManager *Manager, GUIControlManager *ControlManager) : G ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIButton::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUIButton::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -37,8 +38,12 @@ void GUIButton::Create(const std::string &Name, int X, int Y, int Width, int Hei m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Make sure the button isn't too small m_Width = std::max(m_Width, m_MinWidth); @@ -53,13 +58,17 @@ void GUIButton::Create(const std::string &Name, int X, int Y, int Width, int Hei m_Text->SetEnabled(false); GUIPanel::AddChild(m_Text.get()); } - if (!m_Icon) { m_Icon = std::make_unique(); } - if (!m_BorderSizes) { m_BorderSizes = std::make_unique(); } + if (!m_Icon) { + m_Icon = std::make_unique(); + } + if (!m_BorderSizes) { + m_BorderSizes = std::make_unique(); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIButton::Create(GUIProperties *Props) { +void GUIButton::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -86,8 +95,12 @@ void GUIButton::Create(GUIProperties *Props) { m_Text->SetEnabled(false); GUIPanel::AddChild(m_Text.get()); } - if (!m_Icon) { m_Icon = std::make_unique(); } - if (!m_BorderSizes) { m_BorderSizes = std::make_unique(); } + if (!m_Icon) { + m_Icon = std::make_unique(); + } + if (!m_BorderSizes) { + m_BorderSizes = std::make_unique(); + } // Load the values std::string text; @@ -114,7 +127,7 @@ void GUIButton::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIButton::ChangeSkin(GUISkin *Skin) { +void GUIButton::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Build the button bitmap @@ -144,10 +157,12 @@ void GUIButton::BuildBitmap() { m_FontColor = m_Skin->ConvertColor(m_FontColor, m_DrawBitmap->GetColorDepth()); - //TODO consider getting rid of this line. Because of it GuiButton::SetFont does nothing, which is weird, but maybe there's a good reason for it. Test and investigate. - // Also, with the change of m_Text from a std::string to a GUILabel this seems maybe even sillier. SetFont should probably set the label's font + // TODO consider getting rid of this line. Because of it GuiButton::SetFont does nothing, which is weird, but maybe there's a good reason for it. Test and investigate. + // Also, with the change of m_Text from a std::string to a GUILabel this seems maybe even sillier. SetFont should probably set the label's font m_Font = m_Skin->GetFont(Filename); - if (m_Font) { m_Font->CacheColor(m_FontColor); } + if (m_Font) { + m_Font->CacheColor(m_FontColor); + } // Create the button image GUIRect buttonBorders; @@ -156,7 +171,7 @@ void GUIButton::BuildBitmap() { m_Skin->BuildStandardRect(m_DrawBitmap, "Button_Over", 0, m_Height, m_Width, m_Height); m_Skin->BuildStandardRect(m_DrawBitmap, "Button_Down", 0, m_Height * 2, m_Width, m_Height); - //TODO this should be 1 pixel ideally, to give space between content and the border. However, the green skin, which this is primarly used for, has padding built-in and doesn't work properly without it. + // TODO this should be 1 pixel ideally, to give space between content and the border. However, the green skin, which this is primarly used for, has padding built-in and doesn't work properly without it. const int buttonContentPadding = 0; const int contentMaxWidth = m_Width - m_BorderSizes->left - m_BorderSizes->right - (buttonContentPadding * 2) - 1; const int contentMaxHeight = m_Height - m_BorderSizes->top - m_BorderSizes->bottom - (buttonContentPadding * 2) - 1; @@ -172,7 +187,7 @@ void GUIButton::BuildBitmap() { if (hasIcon && !hasText) { iconYPos = centerY - (m_Icon->GetHeight() / 2); } else if (!hasIcon && hasText) { - //int y = m_Height/2-m_Font->CalculateHeight(m_Text)+2; //Note this commented line was here from CCOSS, it was here so that the idea of using the full height of the text stays around. + // int y = m_Height/2-m_Font->CalculateHeight(m_Text)+2; //Note this commented line was here from CCOSS, it was here so that the idea of using the full height of the text stays around. textYPos = centerY - (m_Font->GetFontHeight() / 2) - 1; } else if (hasIcon && hasText) { int iconAndTextHeight = m_Text->GetHorizontalOverflowScroll() ? m_Font->GetFontHeight() : m_Font->CalculateHeight(m_Text->GetText(), contentMaxWidth); @@ -221,7 +236,7 @@ void GUIButton::BuildBitmap() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIButton::Draw(GUIScreen *Screen) { +void GUIButton::Draw(GUIScreen* Screen) { GUIRect Rect; int y = 0; if (m_Pushed) { @@ -231,7 +246,9 @@ void GUIButton::Draw(GUIScreen *Screen) { } SetRect(&Rect, 0, y, m_Width, y + m_Height); - if (m_Text->OverflowScrollIsActivated() && m_Font->CalculateWidth(m_Text->GetText()) > m_Width - m_BorderSizes->left - m_BorderSizes->right) { BuildBitmap(); } + if (m_Text->OverflowScrollIsActivated() && m_Font->CalculateWidth(m_Text->GetText()) > m_Width - m_BorderSizes->left - m_BorderSizes->right) { + BuildBitmap(); + } m_DrawBitmap->DrawTrans(Screen->GetBitmap(), m_X, m_Y, &Rect); @@ -253,7 +270,9 @@ void GUIButton::OnMouseDown(int X, int Y, int Buttons, int Modifier) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIButton::OnMouseUp(int X, int Y, int Buttons, int Modifier) { - if (PointInside(X, Y)) { AddEvent(GUIEvent::Notification, Clicked, Buttons); } + if (PointInside(X, Y)) { + AddEvent(GUIEvent::Notification, Clicked, Buttons); + } if (!IsCaptured()) { return; @@ -263,7 +282,9 @@ void GUIButton::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); // If the mouse is over the button, add the command to the event queue - if (PointInside(X, Y)) { AddEvent(GUIEvent::Command, 0, 0); } + if (PointInside(X, Y)) { + AddEvent(GUIEvent::Command, 0, 0); + } AddEvent(GUIEvent::Notification, UnPushed, 0); } @@ -328,18 +349,17 @@ void GUIButton::OnMouseMove(int X, int Y, int Buttons, int Modifier) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIButton::OnKeyDown(int KeyCode, int Modifier) { - } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIButton::GetPanel() { +GUIPanel* GUIButton::GetPanel() { return this; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIButton::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUIButton::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } @@ -383,16 +403,18 @@ void GUIButton::SetPushed(bool pushed) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIButton::SetText(const std::string_view &newText, bool noBitmapRebuild) { +void GUIButton::SetText(const std::string_view& newText, bool noBitmapRebuild) { if (GetText() != newText) { m_Text->SetText(newText); - if (!noBitmapRebuild) { BuildBitmap(); } + if (!noBitmapRebuild) { + BuildBitmap(); + } } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -const std::string & GUIButton::GetText() const { +const std::string& GUIButton::GetText() const { return m_Text->GetText(); } @@ -400,37 +422,45 @@ const std::string & GUIButton::GetText() const { void GUIButton::SetHorizontalOverflowScroll(bool newOverflowScroll) { m_Text->SetHorizontalOverflowScroll(newOverflowScroll); - if (m_Text->GetHorizontalOverflowScroll()) { BuildBitmap(); } + if (m_Text->GetHorizontalOverflowScroll()) { + BuildBitmap(); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIButton::SetVerticalOverflowScroll(bool newOverflowScroll) { m_Text->SetVerticalOverflowScroll(newOverflowScroll); - if (m_Text->GetVerticalOverflowScroll()) { BuildBitmap(); } + if (m_Text->GetVerticalOverflowScroll()) { + BuildBitmap(); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIButton::SetIcon(BITMAP *newIcon, bool noBitmapRebuild) { +void GUIButton::SetIcon(BITMAP* newIcon, bool noBitmapRebuild) { if (m_Icon && m_Icon->GetBitmap() != newIcon) { m_Icon->SetBitmap(newIcon); - if (!noBitmapRebuild) { BuildBitmap(); } + if (!noBitmapRebuild) { + BuildBitmap(); + } } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIButton::SetIconAndText(BITMAP *newIcon, const std::string_view &newText) { +void GUIButton::SetIconAndText(BITMAP* newIcon, const std::string_view& newText) { bool bitmapNeedsRebuild = (m_Icon && m_Icon->GetBitmap() != newIcon) || (m_Text && m_Text->GetText() != newText); SetIcon(newIcon, true); SetText(newText, true); - if (bitmapNeedsRebuild) { BuildBitmap(); } + if (bitmapNeedsRebuild) { + BuildBitmap(); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIButton::ApplyProperties(GUIProperties *Props) { +void GUIButton::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); std::string text; diff --git a/Source/GUI/GUIButton.h b/Source/GUI/GUIButton.h index 658adcebbc..5ee89962e9 100644 --- a/Source/GUI/GUIButton.h +++ b/Source/GUI/GUIButton.h @@ -3,293 +3,270 @@ namespace RTE { - class GUILabel; - -/// -/// A button control class. -/// -class GUIButton : public GUIControl, public GUIPanel { - -public: - - // Button Notifications - enum { - Pushed = 0, - UnPushed, - Clicked, - Focused - } Notification; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIButton -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIButton object in -// system memory. -// Arguments: GUIManager, GUIControlManager. - - GUIButton(GUIManager *Manager, GUIControlManager *ControlManager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. - - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. - - void Destroy() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseMove -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse moves (over the panel, or when captured). -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseEnter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse enters the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseLeave -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse leaves the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - - ////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnGainFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the panel gains focus. -// Arguments: None. - - void OnGainFocus() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnLoseFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the panel looses focus. -// Arguments: None. - - void OnLoseFocus() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnKeyDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when a key goes down. -// Arguments: KeyCode, Modifier. - - void OnKeyDown(int KeyCode, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "BUTTON"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - /// - /// Gets whether or not this button is currently pushed. - /// - /// Whether or not this button is currently pushed. - bool IsPushed() const { return m_Pushed; } - - /// - /// Sets whether or not this button is currently pushed. - /// - /// Whether or not this button should be pushed. - void SetPushed(bool pushed = false); - - /// - /// Gets whether or not this button is currently being moused over. - /// - /// Whether or not this button is currently being moused over. - bool IsMousedOver() const { return m_Over; } - - /// - /// Gets the text of this GUIButton's GUILabel. - /// - /// The text of this GUIButton's GUILabel. - const std::string & GetText() const; - - /// - /// Sets the text of this GUIButton's GUILabel. - /// - /// The new text for this GUIButton's GUILabel. - /// Lets this method NOT rebuild the button bitmap, even if the icon has changed. Defaults to false and should almost always stay that way. - void SetText(const std::string_view &newText, bool noBitmapRebuild = false); - - /// - /// Sets whether or not this GUIButton's text should scroll horizontally (right) when it overflows the button. - /// - /// Whether or not this GUIButton's text should scroll horizontally when it overflows. - void SetHorizontalOverflowScroll(bool newOverflowScroll); - - /// - /// Sets whether or not this GUIButton's text should scroll vertically (down) when it overflows the button. - /// - /// Whether or not this GUIButton's text should scroll vertically when it overflows. - void SetVerticalOverflowScroll(bool newOverflowScroll); - - /// - /// Gets whether or not this GUIButton has an icon with a Bitmap. - /// - bool HasIcon() const { return m_Icon->GetBitmap(); } - - /// - /// Sets the icon for this GUIButton. Ownership is NOT transferred. - /// - /// A pointer to the new icon BITMAP for this GUIButton. - /// Lets this method NOT rebuild the button bitmap, even if the icon has changed. Defaults to false and should almost always stay that way. - void SetIcon(BITMAP *newIcon, bool noBitmapRebuild = false); - - /// - /// Helper method to set both text and icon for this GUIButton at the same time. - /// - /// A pointer to the new icon BITMAP for this GUIButton. - /// The new text for this GUIButton's GUILabel. - void SetIconAndText(BITMAP *newIcon, const std::string_view &newText); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - -private: - - GUIBitmap *m_DrawBitmap; - - bool m_Pushed; - bool m_Over; - std::unique_ptr m_Text; - std::unique_ptr m_Icon; - std::unique_ptr m_BorderSizes; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the button bitmap to draw. -// Arguments: None. - - void BuildBitmap(); -}; -}; + class GUILabel; + + /// + /// A button control class. + /// + class GUIButton : public GUIControl, public GUIPanel { + + public: + // Button Notifications + enum { + Pushed = 0, + UnPushed, + Clicked, + Focused + } Notification; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIButton + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIButton object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUIButton(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseMove + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse moves (over the panel, or when captured). + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseEnter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse enters the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseLeave + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse leaves the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnGainFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the panel gains focus. + // Arguments: None. + + void OnGainFocus() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnLoseFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the panel looses focus. + // Arguments: None. + + void OnLoseFocus() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnKeyDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when a key goes down. + // Arguments: KeyCode, Modifier. + + void OnKeyDown(int KeyCode, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "BUTTON"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; + + /// + /// Gets whether or not this button is currently pushed. + /// + /// Whether or not this button is currently pushed. + bool IsPushed() const { return m_Pushed; } + + /// + /// Sets whether or not this button is currently pushed. + /// + /// Whether or not this button should be pushed. + void SetPushed(bool pushed = false); + + /// + /// Gets whether or not this button is currently being moused over. + /// + /// Whether or not this button is currently being moused over. + bool IsMousedOver() const { return m_Over; } + + /// + /// Gets the text of this GUIButton's GUILabel. + /// + /// The text of this GUIButton's GUILabel. + const std::string& GetText() const; + + /// + /// Sets the text of this GUIButton's GUILabel. + /// + /// The new text for this GUIButton's GUILabel. + /// Lets this method NOT rebuild the button bitmap, even if the icon has changed. Defaults to false and should almost always stay that way. + void SetText(const std::string_view& newText, bool noBitmapRebuild = false); + + /// + /// Sets whether or not this GUIButton's text should scroll horizontally (right) when it overflows the button. + /// + /// Whether or not this GUIButton's text should scroll horizontally when it overflows. + void SetHorizontalOverflowScroll(bool newOverflowScroll); + + /// + /// Sets whether or not this GUIButton's text should scroll vertically (down) when it overflows the button. + /// + /// Whether or not this GUIButton's text should scroll vertically when it overflows. + void SetVerticalOverflowScroll(bool newOverflowScroll); + + /// + /// Gets whether or not this GUIButton has an icon with a Bitmap. + /// + bool HasIcon() const { return m_Icon->GetBitmap(); } + + /// + /// Sets the icon for this GUIButton. Ownership is NOT transferred. + /// + /// A pointer to the new icon BITMAP for this GUIButton. + /// Lets this method NOT rebuild the button bitmap, even if the icon has changed. Defaults to false and should almost always stay that way. + void SetIcon(BITMAP* newIcon, bool noBitmapRebuild = false); + + /// + /// Helper method to set both text and icon for this GUIButton at the same time. + /// + /// A pointer to the new icon BITMAP for this GUIButton. + /// The new text for this GUIButton's GUILabel. + void SetIconAndText(BITMAP* newIcon, const std::string_view& newText); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; + + private: + GUIBitmap* m_DrawBitmap; + + bool m_Pushed; + bool m_Over; + std::unique_ptr m_Text; + std::unique_ptr m_Icon; + std::unique_ptr m_BorderSizes; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the button bitmap to draw. + // Arguments: None. + + void BuildBitmap(); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUICheckbox.cpp b/Source/GUI/GUICheckbox.cpp index a43ac02797..dc8991bcdf 100644 --- a/Source/GUI/GUICheckbox.cpp +++ b/Source/GUI/GUICheckbox.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUICheckbox::GUICheckbox(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUICheckbox::GUICheckbox(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "CHECKBOX"; m_Image = nullptr; m_ControlManager = ControlManager; @@ -15,7 +16,7 @@ GUICheckbox::GUICheckbox(GUIManager *Manager, GUIControlManager *ControlManager) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICheckbox::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUICheckbox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -32,8 +33,12 @@ void GUICheckbox::Create(const std::string &Name, int X, int Y, int Width, int H m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Make sure the button isn't too small m_Width = std::max(m_Width, m_MinWidth); @@ -42,7 +47,7 @@ void GUICheckbox::Create(const std::string &Name, int X, int Y, int Width, int H ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICheckbox::Create(GUIProperties *Props) { +void GUICheckbox::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -60,7 +65,6 @@ void GUICheckbox::Create(GUIProperties *Props) { m_Width = std::max(m_Width, m_MinWidth); m_Height = std::max(m_Height, m_MinHeight); - // Grab the check value m_Check = Unchecked; std::string value; @@ -79,7 +83,7 @@ void GUICheckbox::Destroy() {} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICheckbox::ChangeSkin(GUISkin *Skin) { +void GUICheckbox::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Build the checkbox bitmap @@ -134,7 +138,7 @@ void GUICheckbox::BuildBitmap() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICheckbox::Draw(GUIScreen *Screen) { +void GUICheckbox::Draw(GUIScreen* Screen) { if (!m_Image) { return; } @@ -224,7 +228,7 @@ void GUICheckbox::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUICheckbox::GetPanel() { +GUIPanel* GUICheckbox::GetPanel() { return this; } @@ -248,7 +252,7 @@ void GUICheckbox::Resize(int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICheckbox::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUICheckbox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } @@ -267,7 +271,7 @@ void GUICheckbox::StoreProperties() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICheckbox::SetText(const std::string &Text) { +void GUICheckbox::SetText(const std::string& Text) { m_Text = Text; } @@ -291,7 +295,7 @@ int GUICheckbox::GetCheck() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICheckbox::ApplyProperties(GUIProperties *Props) { +void GUICheckbox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); m_Check = Unchecked; diff --git a/Source/GUI/GUICheckbox.h b/Source/GUI/GUICheckbox.h index 44ec6a9744..2942f7efef 100644 --- a/Source/GUI/GUICheckbox.h +++ b/Source/GUI/GUICheckbox.h @@ -3,233 +3,211 @@ namespace RTE { -/// -/// A checkbox control class. -/// -class GUICheckbox : public GUIControl, public GUIPanel { + /// + /// A checkbox control class. + /// + class GUICheckbox : public GUIControl, public GUIPanel { + + public: + // Check types + enum { + Unchecked = 0, + Checked, + Greycheck + }; + + // Checkbox Notifications + enum { + Pushed = 0, + UnPushed, + Changed, + } Notification; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUICheckbox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUICheckbox object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUICheckbox(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseEnter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse enters the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseLeave + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse leaves the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "CHECKBOX"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; -public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; - // Check types - enum { - Unchecked = 0, - Checked, - Greycheck - }; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - // Checkbox Notifications - enum { - Pushed = 0, - UnPushed, - Changed, - } Notification; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUICheckbox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUICheckbox object in -// system memory. -// Arguments: GUIManager, GUIControlManager. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the text. + // Arguments: Text. + + void SetText(const std::string& Text); - GUICheckbox(GUIManager *Manager, GUIControlManager *ControlManager); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the text. + // Arguments: None. + + std::string GetText() const; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the check state. + // Arguments: Check state. + + void SetCheck(int Check); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the check state. + // Arguments: None. + + int GetCheck() const; - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; + private: + GUIBitmap* m_Image; + GUIRect m_ImageRects[4]; + + int m_Check; + std::string m_Text; + int m_Mouseover; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. - - void Destroy() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseEnter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse enters the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseLeave -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse leaves the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "CHECKBOX"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the text. -// Arguments: Text. - - void SetText(const std::string &Text); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the text. -// Arguments: None. - - std::string GetText() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the check state. -// Arguments: Check state. - - void SetCheck(int Check); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the check state. -// Arguments: None. - - int GetCheck() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - -private: - - GUIBitmap *m_Image; - GUIRect m_ImageRects[4]; - - int m_Check; - std::string m_Text; - int m_Mouseover; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the checkbox bitmap to draw. -// Arguments: None. - - void BuildBitmap(); -}; -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the checkbox bitmap to draw. + // Arguments: None. + + void BuildBitmap(); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUICollectionBox.cpp b/Source/GUI/GUICollectionBox.cpp index 16803e4394..448478a948 100644 --- a/Source/GUI/GUICollectionBox.cpp +++ b/Source/GUI/GUICollectionBox.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUICollectionBox::GUICollectionBox(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUICollectionBox::GUICollectionBox(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "COLLECTIONBOX"; m_Background = nullptr; m_ControlManager = ControlManager; @@ -19,7 +20,7 @@ GUICollectionBox::GUICollectionBox(GUIManager *Manager, GUIControlManager *Contr ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICollectionBox::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUICollectionBox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -36,8 +37,12 @@ void GUICollectionBox::Create(const std::string &Name, int X, int Y, int Width, m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Make sure the box isn't too small m_Width = std::max(m_Width, m_MinWidth); @@ -46,7 +51,7 @@ void GUICollectionBox::Create(const std::string &Name, int X, int Y, int Width, ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICollectionBox::Create(GUIProperties *Props) { +void GUICollectionBox::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -87,7 +92,7 @@ void GUICollectionBox::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICollectionBox::ChangeSkin(GUISkin *Skin) { +void GUICollectionBox::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Build the panel bitmap @@ -109,7 +114,7 @@ void GUICollectionBox::BuildBitmap() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICollectionBox::Draw(GUIScreen *Screen) { +void GUICollectionBox::Draw(GUIScreen* Screen) { if (m_DrawBackground) { if (m_DrawType == Color) { Screen->GetBitmap()->DrawRectangle(m_X, m_Y, m_Width, m_Height, m_Skin->ConvertColor(m_DrawColor, Screen->GetBitmap()->GetColorDepth()), true); @@ -157,7 +162,7 @@ void GUICollectionBox::OnMouseMove(int X, int Y, int Buttons, int Modifier) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUICollectionBox::GetPanel() { +GUIPanel* GUICollectionBox::GetPanel() { return this; } @@ -171,9 +176,9 @@ void GUICollectionBox::Move(int X, int Y) { m_Y = Y; // Go through all my children moving them - std::vector::iterator it; + std::vector::iterator it; for (it = m_ControlChildren.begin(); it != m_ControlChildren.end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; int CX; int CY; int CW; @@ -194,9 +199,9 @@ void GUICollectionBox::Resize(int Width, int Height) { m_Height = Height; // Go through all my children moving them - std::vector::iterator it; + std::vector::iterator it; for (it = m_ControlChildren.begin(); it != m_ControlChildren.end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; int CX, CY, CW, CH; int Anchor = C->GetAnchor(); @@ -208,32 +213,44 @@ void GUICollectionBox::Resize(int Width, int Height) { int H = CH; // Attached to Right and/or Bottom edges - if ((Anchor & GUIControl::Anchor_Right) && !(Anchor & GUIControl::Anchor_Left)) { DX = m_Width - (OldWidth - (CX - m_X)) + m_X; } - if ((Anchor & GUIControl::Anchor_Bottom) && !(Anchor & GUIControl::Anchor_Top)) { DY = m_Height - (OldHeight - (CY - m_Y)) + m_Y; } + if ((Anchor & GUIControl::Anchor_Right) && !(Anchor & GUIControl::Anchor_Left)) { + DX = m_Width - (OldWidth - (CX - m_X)) + m_X; + } + if ((Anchor & GUIControl::Anchor_Bottom) && !(Anchor & GUIControl::Anchor_Top)) { + DY = m_Height - (OldHeight - (CY - m_Y)) + m_Y; + } - if (DX != CX || DY != CY) { C->Move(DX, DY); } + if (DX != CX || DY != CY) { + C->Move(DX, DY); + } CX -= m_X; CY -= m_Y; // Attached to opposing edges - if (Anchor & GUIControl::Anchor_Left && Anchor & GUIControl::Anchor_Right) { W = (m_Width - (OldWidth - (CX + CW))) - CX; } - if (Anchor & GUIControl::Anchor_Top && Anchor & GUIControl::Anchor_Bottom) { H = (m_Height - (OldHeight - (CY + CH))) - CY; } + if (Anchor & GUIControl::Anchor_Left && Anchor & GUIControl::Anchor_Right) { + W = (m_Width - (OldWidth - (CX + CW))) - CX; + } + if (Anchor & GUIControl::Anchor_Top && Anchor & GUIControl::Anchor_Bottom) { + H = (m_Height - (OldHeight - (CY + CH))) - CY; + } - if (W != CW || H != CH) { C->Resize(W, H); } + if (W != CW || H != CH) { + C->Resize(W, H); + } } BuildBitmap(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICollectionBox::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUICollectionBox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICollectionBox::SetDrawImage(GUIBitmap *Bitmap) { +void GUICollectionBox::SetDrawImage(GUIBitmap* Bitmap) { // Free any old bitmap delete m_DrawBitmap; @@ -268,7 +285,7 @@ void GUICollectionBox::StoreProperties() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUICollectionBox::ApplyProperties(GUIProperties *Props) { +void GUICollectionBox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); // Get the values diff --git a/Source/GUI/GUICollectionBox.h b/Source/GUI/GUICollectionBox.h index 25a2c1bd59..9b96b49a06 100644 --- a/Source/GUI/GUICollectionBox.h +++ b/Source/GUI/GUICollectionBox.h @@ -3,262 +3,235 @@ namespace RTE { -/// -/// A collection box control class that contains child controls. -/// -class GUICollectionBox : public GUIControl, public GUIPanel { + /// + /// A collection box control class that contains child controls. + /// + class GUICollectionBox : public GUIControl, public GUIPanel { + + public: + // CollectionBox Notifications + enum { + Clicked = 0, + MouseMove // Mouse moved over the panel + } Notification; + + // Drawing type + enum { + Color, + Image, + Panel + } DrawType; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUICollectionBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUICollectionBox object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUICollectionBox(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~GUICollectionBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up this before deletion from memory. + // Arguments: None. + + ~GUICollectionBox() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and frees this' allocated data + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseMove + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse moves (over the panel, or when captured). + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. -public: + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - // CollectionBox Notifications - enum { - Clicked = 0, - MouseMove // Mouse moved over the panel - } Notification; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; - // Drawing type - enum { - Color, - Image, - Panel - } DrawType; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "COLLECTIONBOX"; }; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUICollectionBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUICollectionBox object in -// system memory. -// Arguments: GUIManager, GUIControlManager. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDrawImage + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the drawing image bitmap to draw + // Arguments: Bitmap, ownership IS transferred! + + void SetDrawImage(GUIBitmap* Bitmap); - GUICollectionBox(GUIManager *Manager, GUIControlManager *ControlManager); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDrawImage + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the drawing image bitmap that is being drawn + // Arguments: Bitmap, ownership IS NOT transferred! + + GUIBitmap* GetDrawImage() { return m_DrawBitmap; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDrawBackground + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether to draw the background. + // Arguments: Draw. + + void SetDrawBackground(bool DrawBack); -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~GUICollectionBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up this before deletion from memory. -// Arguments: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDrawType + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the drawing type. + // Arguments: Type. + + void SetDrawType(int Type); - ~GUICollectionBox() { Destroy(); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDrawType + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current drawing type. + // Arguments: None. + // Returns: Type. + + int GetDrawType() const { return m_DrawType; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDrawColor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the drawing color. + // Arguments: Color. + + void SetDrawColor(unsigned long Color); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDrawColor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the drawing color. + // Returns: Color. + + unsigned long GetDrawColor() const { return m_DrawColor; } - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; + private: + GUIBitmap* m_Background; + + bool m_DrawBackground; + int m_DrawType; + unsigned long m_DrawColor; + GUIBitmap* m_DrawBitmap; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and frees this' allocated data -// Arguments: None. - - void Destroy() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseMove -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse moves (over the panel, or when captured). -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "COLLECTIONBOX"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDrawImage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the drawing image bitmap to draw -// Arguments: Bitmap, ownership IS transferred! - - void SetDrawImage(GUIBitmap *Bitmap); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDrawImage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the drawing image bitmap that is being drawn -// Arguments: Bitmap, ownership IS NOT transferred! - - GUIBitmap * GetDrawImage() { return m_DrawBitmap; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDrawBackground -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether to draw the background. -// Arguments: Draw. - - void SetDrawBackground(bool DrawBack); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDrawType -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the drawing type. -// Arguments: Type. - - void SetDrawType(int Type); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDrawType -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current drawing type. -// Arguments: None. -// Returns: Type. - - int GetDrawType() const { return m_DrawType; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDrawColor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the drawing color. -// Arguments: Color. - - void SetDrawColor(unsigned long Color); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDrawColor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the drawing color. -// Returns: Color. - - unsigned long GetDrawColor() const { return m_DrawColor; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - - -private: - - GUIBitmap *m_Background; - - bool m_DrawBackground; - int m_DrawType; - unsigned long m_DrawColor; - GUIBitmap *m_DrawBitmap; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the button bitmap to draw. -// Arguments: None. - - void BuildBitmap(); -}; -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the button bitmap to draw. + // Arguments: None. + + void BuildBitmap(); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIComboBox.cpp b/Source/GUI/GUIComboBox.cpp index 8f3e898130..111d1256f2 100644 --- a/Source/GUI/GUIComboBox.cpp +++ b/Source/GUI/GUIComboBox.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIComboBox::GUIComboBox(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUIComboBox::GUIComboBox(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "COMBOBOX"; m_ControlManager = ControlManager; m_DrawBitmap = nullptr; @@ -26,7 +27,7 @@ GUIComboBox::GUIComboBox(GUIManager *Manager, GUIControlManager *ControlManager) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBox::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUIComboBox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -43,8 +44,12 @@ void GUIComboBox::Create(const std::string &Name, int X, int Y, int Width, int H m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Make sure the textbox isn't too small m_Width = std::max(m_Width, m_MinWidth); @@ -71,7 +76,7 @@ void GUIComboBox::Create(const std::string &Name, int X, int Y, int Width, int H ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBox::Create(GUIProperties *Props) { +void GUIComboBox::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -101,7 +106,6 @@ void GUIComboBox::Create(GUIProperties *Props) { m_ListPanel->EnableScrollbars(false, true); m_ListPanel->SetMouseScrolling(true); - // Create the button m_Button->Create(m_Width - 17, 0, 17, m_Height); m_Button->SetSignalTarget(this); @@ -163,7 +167,7 @@ void GUIComboBox::Activate() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBox::ChangeSkin(GUISkin *Skin) { +void GUIComboBox::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Free any old bitmap @@ -182,26 +186,30 @@ void GUIComboBox::ChangeSkin(GUISkin *Skin) { // Setup the skin in the panels too m_TextPanel->ChangeSkin(Skin); - if (m_CreatedList) { m_ListPanel->ChangeSkin(Skin); } + if (m_CreatedList) { + m_ListPanel->ChangeSkin(Skin); + } m_Button->ChangeSkin(Skin); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBox::Draw(GUIScreen *Screen) { +void GUIComboBox::Draw(GUIScreen* Screen) { // Draw the background m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, nullptr); // If selected item has a bitmap AND no text to show, just show the bitmap as the selected thing - if (m_ListPanel->GetSelected() && m_ListPanel->GetSelected()->m_Name.empty() && m_ListPanel->GetSelected()->m_pBitmap) { m_ListPanel->GetSelected()->m_pBitmap->DrawTrans(Screen->GetBitmap(), m_X + 4, m_Y + 4, nullptr); } + if (m_ListPanel->GetSelected() && m_ListPanel->GetSelected()->m_Name.empty() && m_ListPanel->GetSelected()->m_pBitmap) { + m_ListPanel->GetSelected()->m_pBitmap->DrawTrans(Screen->GetBitmap(), m_X + 4, m_Y + 4, nullptr); + } GUIPanel::Draw(Screen); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIComboBox::GetPanel() { +GUIPanel* GUIComboBox::GetPanel() { return this; } @@ -227,7 +235,9 @@ void GUIComboBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { m_ListPanel->ChangeZPosition(TopMost); // Save the current selection - if (m_ListPanel->GetSelectedIndex() >= 0 && m_ListPanel->GetSelectedIndex() < m_ListPanel->GetItemList()->size()) { m_OldSelection = m_ListPanel->GetSelectedIndex(); } + if (m_ListPanel->GetSelectedIndex() >= 0 && m_ListPanel->GetSelectedIndex() < m_ListPanel->GetItemList()->size()) { + m_OldSelection = m_ListPanel->GetSelectedIndex(); + } AddEvent(GUIEvent::Notification, Dropped, 0); } @@ -247,7 +257,9 @@ void GUIComboBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { m_ListPanel->ChangeZPosition(TopMost); // Save the current selection - if (m_ListPanel->GetSelectedIndex() >= 0 && m_ListPanel->GetSelectedIndex() < m_ListPanel->GetItemList()->size()) { m_OldSelection = m_ListPanel->GetSelectedIndex(); } + if (m_ListPanel->GetSelectedIndex() >= 0 && m_ListPanel->GetSelectedIndex() < m_ListPanel->GetItemList()->size()) { + m_OldSelection = m_ListPanel->GetSelectedIndex(); + } AddEvent(GUIEvent::Notification, Dropped, 0); } @@ -297,7 +309,9 @@ void GUIComboBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { if (m_DropDownStyle == DropDownList) { // Set the text to the item in the list panel - if (const GUIListPanel::Item* Item = m_ListPanel->GetSelected()) { m_TextPanel->SetText(Item->m_Name); } + if (const GUIListPanel::Item* Item = m_ListPanel->GetSelected()) { + m_TextPanel->SetText(Item->m_Name); + } } } } @@ -316,7 +330,7 @@ void GUIComboBox::EndUpdate() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBox::AddItem(const std::string &Name, const std::string &ExtraText, GUIBitmap *pBitmap, const Entity *pEntity) { +void GUIComboBox::AddItem(const std::string& Name, const std::string& ExtraText, GUIBitmap* pBitmap, const Entity* pEntity) { m_ListPanel->AddItem(Name, ExtraText, pBitmap, pEntity); } @@ -359,7 +373,7 @@ void GUIComboBox::Resize(int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBox::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUIComboBox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } @@ -369,7 +383,7 @@ void GUIComboBox::DeleteItem(int Index) { m_ListPanel->DeleteItem(Index); // Update the selection - const GUIListPanel::Item *Item = m_ListPanel->GetSelected(); + const GUIListPanel::Item* Item = m_ListPanel->GetSelected(); if (!Item) { m_ListPanel->SetSelectedIndex(0); Item = m_ListPanel->GetSelected(); @@ -401,7 +415,9 @@ void GUIComboBox::SetSelectedIndex(int Index) { m_OldSelection = Index; // Set the text to the item in the list panel - if (const GUIListPanel::Item *Item = m_ListPanel->GetSelected()) { m_TextPanel->SetText(Item->m_Name); } + if (const GUIListPanel::Item* Item = m_ListPanel->GetSelected()) { + m_TextPanel->SetText(Item->m_Name); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -411,7 +427,7 @@ bool GUIComboBox::RollbackSelection() { if (m_OldSelection >= 0 && m_OldSelection < m_ListPanel->GetItemList()->size() && m_OldSelection != m_ListPanel->GetSelectedIndex()) { m_ListPanel->SetSelectedIndex(m_OldSelection); // Set the text to the item in the list panel - if (const GUIListPanel::Item *Item = m_ListPanel->GetSelected()) { + if (const GUIListPanel::Item* Item = m_ListPanel->GetSelected()) { m_TextPanel->SetText(Item->m_Name); return true; } @@ -422,7 +438,7 @@ bool GUIComboBox::RollbackSelection() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIListPanel::Item * GUIComboBox::GetItem(int Index) { +GUIListPanel::Item* GUIComboBox::GetItem(int Index) { return m_ListPanel->GetItem(Index); } @@ -450,7 +466,9 @@ void GUIComboBox::StoreProperties() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIComboBox::SetDropDownStyle(int Style) { - if (Style == DropDown || Style == DropDownList) { m_DropDownStyle = Style; } + if (Style == DropDown || Style == DropDownList) { + m_DropDownStyle = Style; + } m_TextPanel->SetLocked(m_DropDownStyle == DropDownList); } @@ -465,7 +483,9 @@ int GUIComboBox::GetDropDownStyle() const { void GUIComboBox::SetVisible(bool Visible) { _SetVisible(Visible); - if (!Visible && m_ListPanel) { m_ListPanel->_SetVisible(false); } + if (!Visible && m_ListPanel) { + m_ListPanel->_SetVisible(false); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -478,7 +498,9 @@ bool GUIComboBox::GetVisible() { void GUIComboBox::SetEnabled(bool Enabled) { _SetEnabled(Enabled); - if (m_ListPanel) { m_ListPanel->_SetEnabled(Enabled); } + if (m_ListPanel) { + m_ListPanel->_SetEnabled(Enabled); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -501,13 +523,15 @@ std::string GUIComboBox::GetText() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBox::SetText(const std::string &Text) { - if (m_DropDownStyle == DropDown && m_TextPanel) { m_TextPanel->SetText(Text); } +void GUIComboBox::SetText(const std::string& Text) { + if (m_DropDownStyle == DropDown && m_TextPanel) { + m_TextPanel->SetText(Text); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBox::ApplyProperties(GUIProperties *Props) { +void GUIComboBox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); m_Properties.GetValue("Dropheight", &m_DropHeight); @@ -528,14 +552,15 @@ void GUIComboBox::ApplyProperties(GUIProperties *Props) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIComboBoxButton::GUIComboBoxButton(GUIManager *Manager) : GUIPanel(Manager) { +GUIComboBoxButton::GUIComboBoxButton(GUIManager* Manager) : + GUIPanel(Manager) { m_DrawBitmap = nullptr; m_Pushed = false; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBoxButton::ChangeSkin(GUISkin *Skin) { +void GUIComboBoxButton::ChangeSkin(GUISkin* Skin) { // Free any old bitmap if (m_DrawBitmap) { m_DrawBitmap->Destroy(); @@ -554,7 +579,7 @@ void GUIComboBoxButton::ChangeSkin(GUISkin *Skin) { // Draw the arrow std::string Filename; Skin->GetValue("ComboBox_Arrow", "Filename", &Filename); - GUIBitmap *Arrow = Skin->CreateBitmap(Filename); + GUIBitmap* Arrow = Skin->CreateBitmap(Filename); if (!Arrow) { return; } @@ -576,7 +601,7 @@ void GUIComboBoxButton::ChangeSkin(GUISkin *Skin) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIComboBoxButton::Draw(GUIScreen *Screen) { +void GUIComboBoxButton::Draw(GUIScreen* Screen) { GUIRect Rect; SetRect(&Rect, 0, m_Pushed ? m_Height : 0, m_Width, m_Pushed ? m_Height * 2 : m_Height); diff --git a/Source/GUI/GUIComboBox.h b/Source/GUI/GUIComboBox.h index 71013ae872..4e4ba8a133 100644 --- a/Source/GUI/GUIComboBox.h +++ b/Source/GUI/GUIComboBox.h @@ -6,492 +6,441 @@ namespace RTE { -class GUIComboBoxButton; - -/// -/// A ComboBox control class. -/// -class GUIComboBox : public GUIControl, public GUIPanel { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - // Notifications - enum { - Dropped, // When listpanel has dropped - Closed, // When listpanel has closed - } Notifications; - - // Combo Style - enum { - DropDown, - DropDownList, - } DropDownStyles; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIComboBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIComboBox object in -// system memory. -// Arguments: GUIManager, GUIControlManager. - - GUIComboBox(GUIManager *Manager, GUIControlManager *ControlManager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. - - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. - - void Destroy() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Activate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control is activated and ready for use. -// Arguments: None. - - void Activate() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetListPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the ListPanel component of the control. -// Arguments: None. -// Returns: The ListPanel component of this ComboBox. - - GUIListPanel * GetListPanel() { return m_ListPanel; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "COMBOBOX"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReceiveSignal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when receiving a signal. -// Arguments: Signal source, Signal code, Signal data. - - void ReceiveSignal(GUIPanel *Source, int Code, int Data) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BeginUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Locks the control from updating every time a new item is added. -// Arguments: None. - - void BeginUpdate(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EndUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: UnLocks the control from updating every time a new item is added. -// Will automatically update the control. -// Arguments: None. - - void EndUpdate(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Add an item to the list. -// Arguments: Name, Extra text, bitmap to show in the list, extra entity data - - void AddItem(const std::string &Name, const std::string &ExtraText = "", GUIBitmap *pBitmap = nullptr, const Entity *pEntity = nullptr); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DeleteItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Delete an item from the list. -// Arguments: Item Index. - - void DeleteItem(int Index); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the list. -// Arguments: None. - - void ClearList(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get the item count. -// Arguments: None. - - int GetCount(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSelectedIndex -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get the index of the selected item. -// Arguments: None. - - int GetSelectedIndex(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOldSelectionIndex -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get the index of the previously selected item before the selection is -// made. -// Arguments: None. - - int GetOldSelectionIndex() const { return m_OldSelection; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSelectedIndex -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the index of the selected item. -// Arguments: None. - - void SetSelectedIndex(int Index); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RollbackSelection -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Rolls back the selection to the previous selected item. -// Arguments: None. -// Returns: Whether the rollback worked and was performed. - - bool RollbackSelection(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the Item structure at the index. -// Arguments: Index. -// Returns: Pointer to the item structure. 0 if the index was invalid. - - GUIListPanel::Item * GetItem(int Index); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSelectedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the Item structure at the currently selected index. -// Arguments: Index. -// Returns: Pointer to the item structure. 0 if nothing valid is selected. - - GUIListPanel::Item * GetSelectedItem() { return GetItem(GetSelectedIndex()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDropHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the drop height of the list. -// Arguments: Height. - - void SetDropHeight(int Drop); + class GUIComboBoxButton; /// - /// Gets the drop height of the list. + /// A ComboBox control class. /// - /// The drop height of the list. - int GetDropHeight() const { return m_DropHeight; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDropDownStyle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the drop down style of the combo box. -// Arguments: Style. - - void SetDropDownStyle(int Style); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDropDownStyle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the drop down style of the combo box. -// Arguments: None. - - int GetDropDownStyle() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the visibility of the control. -// Arguments: Visible. - - void SetVisible(bool Visible) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the visibility of the control. -// Arguments: None. - - bool GetVisible() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the enabled state of the control. -// Arguments: Enabled. - - void SetEnabled(bool Enabled) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the enabled state of the control. -// Arguments: None. - - bool GetEnabled() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets text (only if style is DropDown). -// Arguments: None. -// Returns: Text. Returns empty string is style is not DropDown. - - std::string GetText(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets text (only if style is DropDown). -// Arguments: Text. + class GUIComboBox : public GUIControl, public GUIPanel { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Notifications + enum { + Dropped, // When listpanel has dropped + Closed, // When listpanel has closed + } Notifications; + + // Combo Style + enum { + DropDown, + DropDownList, + } DropDownStyles; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIComboBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIComboBox object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUIComboBox(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Activate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control is activated and ready for use. + // Arguments: None. + + void Activate() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetListPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the ListPanel component of the control. + // Arguments: None. + // Returns: The ListPanel component of this ComboBox. + + GUIListPanel* GetListPanel() { return m_ListPanel; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "COMBOBOX"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReceiveSignal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when receiving a signal. + // Arguments: Signal source, Signal code, Signal data. + + void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BeginUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Locks the control from updating every time a new item is added. + // Arguments: None. + + void BeginUpdate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EndUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: UnLocks the control from updating every time a new item is added. + // Will automatically update the control. + // Arguments: None. + + void EndUpdate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Add an item to the list. + // Arguments: Name, Extra text, bitmap to show in the list, extra entity data + + void AddItem(const std::string& Name, const std::string& ExtraText = "", GUIBitmap* pBitmap = nullptr, const Entity* pEntity = nullptr); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DeleteItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Delete an item from the list. + // Arguments: Item Index. + + void DeleteItem(int Index); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the list. + // Arguments: None. + + void ClearList(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get the item count. + // Arguments: None. + + int GetCount(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSelectedIndex + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get the index of the selected item. + // Arguments: None. + + int GetSelectedIndex(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOldSelectionIndex + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get the index of the previously selected item before the selection is + // made. + // Arguments: None. + + int GetOldSelectionIndex() const { return m_OldSelection; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSelectedIndex + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the index of the selected item. + // Arguments: None. + + void SetSelectedIndex(int Index); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RollbackSelection + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Rolls back the selection to the previous selected item. + // Arguments: None. + // Returns: Whether the rollback worked and was performed. + + bool RollbackSelection(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the Item structure at the index. + // Arguments: Index. + // Returns: Pointer to the item structure. 0 if the index was invalid. + + GUIListPanel::Item* GetItem(int Index); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSelectedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the Item structure at the currently selected index. + // Arguments: Index. + // Returns: Pointer to the item structure. 0 if nothing valid is selected. + + GUIListPanel::Item* GetSelectedItem() { return GetItem(GetSelectedIndex()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDropHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the drop height of the list. + // Arguments: Height. + + void SetDropHeight(int Drop); + + /// + /// Gets the drop height of the list. + /// + /// The drop height of the list. + int GetDropHeight() const { return m_DropHeight; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDropDownStyle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the drop down style of the combo box. + // Arguments: Style. + + void SetDropDownStyle(int Style); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDropDownStyle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the drop down style of the combo box. + // Arguments: None. + + int GetDropDownStyle() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the visibility of the control. + // Arguments: Visible. + + void SetVisible(bool Visible) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the visibility of the control. + // Arguments: None. + + bool GetVisible() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the enabled state of the control. + // Arguments: Enabled. + + void SetEnabled(bool Enabled) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the enabled state of the control. + // Arguments: None. + + bool GetEnabled() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets text (only if style is DropDown). + // Arguments: None. + // Returns: Text. Returns empty string is style is not DropDown. + + std::string GetText(); - void SetText(const std::string &Text); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets text (only if style is DropDown). + // Arguments: Text. + + void SetText(const std::string& Text); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsDropped + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether the list is currently dropped down or not. + // Arguments: None. + // Returns: Whether this is currently dropped down and showing the list. + + bool IsDropped() { return m_ListPanel->_GetVisible(); } - void ApplyProperties(GUIProperties *Props) override; + private: + GUIBitmap* m_DrawBitmap; + int m_OldSelection; + bool m_CreatedList; + + int m_DropHeight; + int m_DropDownStyle; + GUITextPanel* m_TextPanel; + GUIListPanel* m_ListPanel; + GUIComboBoxButton* m_Button; + }; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDropped -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether the list is currently dropped down or not. -// Arguments: None. -// Returns: Whether this is currently dropped down and showing the list. - - bool IsDropped() { return m_ListPanel->_GetVisible(); } - -private: - - GUIBitmap *m_DrawBitmap; - int m_OldSelection; - bool m_CreatedList; - - int m_DropHeight; - int m_DropDownStyle; - - GUITextPanel *m_TextPanel; - GUIListPanel *m_ListPanel; - GUIComboBoxButton *m_Button; -}; - - -/// -/// A ComboBoxButton control class. -/// -class GUIComboBoxButton : public GUIPanel { - -public: - - // Signals - enum { - Clicked - } Signals; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIComboBoxButton -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIComboBoxButton object in -// system memory. -// Arguments: GUIManager. - - explicit GUIComboBoxButton(GUIManager *Manager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the panel. -// Arguments: Position, Size. - - void Create(int X, int Y, int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys the button. -// Arguments: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPushed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the pushed state of the button. -// Arguments: Pushed. - - void SetPushed(bool Pushed); - -private: - - GUIBitmap *m_DrawBitmap; - bool m_Pushed; -}; -}; + /// + /// A ComboBoxButton control class. + /// + class GUIComboBoxButton : public GUIPanel { + + public: + // Signals + enum { + Clicked + } Signals; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIComboBoxButton + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIComboBoxButton object in + // system memory. + // Arguments: GUIManager. + + explicit GUIComboBoxButton(GUIManager* Manager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the panel. + // Arguments: Position, Size. + + void Create(int X, int Y, int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys the button. + // Arguments: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPushed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the pushed state of the button. + // Arguments: Pushed. + + void SetPushed(bool Pushed); + + private: + GUIBitmap* m_DrawBitmap; + bool m_Pushed; + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIControl.cpp b/Source/GUI/GUIControl.cpp index 1776cf6922..59eec68991 100644 --- a/Source/GUI/GUIControl.cpp +++ b/Source/GUI/GUIControl.cpp @@ -15,7 +15,7 @@ GUIControl::GUIControl() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControl::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUIControl::Create(const std::string& Name, int X, int Y, int Width, int Height) { m_Properties.Clear(); m_Properties.AddVariable("Name", Name); m_Properties.AddVariable("Anchor", "Left, Top"); @@ -24,7 +24,7 @@ void GUIControl::Create(const std::string &Name, int X, int Y, int Width, int He ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControl::Create(GUIProperties *Props) { +void GUIControl::Create(GUIProperties* Props) { assert(Props); // Add the default variables @@ -45,7 +45,7 @@ void GUIControl::Activate() {} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControl::ChangeSkin(GUISkin *Skin) { +void GUIControl::ChangeSkin(GUISkin* Skin) { m_Skin = Skin; } @@ -81,17 +81,19 @@ std::string GUIControl::GetID() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIControl::GetPanel() { +GUIPanel* GUIControl::GetPanel() { return nullptr; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControl::AddChild(GUIControl *Control) { +void GUIControl::AddChild(GUIControl* Control) { assert(Control); // Remove the control from any previous parent - if (Control->GetParent()) { Control->GetParent()->GUIControl::RemoveChild(Control->GetName()); } + if (Control->GetParent()) { + Control->GetParent()->GUIControl::RemoveChild(Control->GetName()); + } Control->m_ControlParent = this; m_ControlChildren.push_back(Control); @@ -99,13 +101,13 @@ void GUIControl::AddChild(GUIControl *Control) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -std::vector * GUIControl::GetChildren() { +std::vector* GUIControl::GetChildren() { return &m_ControlChildren; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIControl::Save(GUIWriter *W) { +bool GUIControl::Save(GUIWriter* W) { std::string OutString = ""; std::string Name; @@ -134,8 +136,10 @@ bool GUIControl::Save(GUIWriter *W) { OutString += "\n"; // Get the main panel and write its location - GUIPanel *Pan = GetPanel(); - if (Pan) { OutString.append(Pan->ToString()); } + GUIPanel* Pan = GetPanel(); + if (Pan) { + OutString.append(Pan->ToString()); + } // Write out the properties OutString.append(m_Properties.ToString()); @@ -156,12 +160,20 @@ void GUIControl::Resize(int Width, int Height) {} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControl::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUIControl::GetControlRect(int* X, int* Y, int* Width, int* Height) { // Zero the values for controls that don't override this - if (X) { *X = 0; } - if (Y) { *Y = 0; } - if (Width) { *Width = 0; } - if (Height) { *Height = 0; } + if (X) { + *X = 0; + } + if (Y) { + *Y = 0; + } + if (Width) { + *Width = 0; + } + if (Height) { + *Height = 0; + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -173,15 +185,27 @@ int GUIControl::GetAnchor() { int Count = m_Properties.GetValue("Anchor", Value, 4); for (int i = 0; i < Count; i++) { - if (stricmp(Value[i].c_str(), "left") == 0) { Anchor |= Anchor_Left; } - if (stricmp(Value[i].c_str(), "top") == 0) { Anchor |= Anchor_Top; } - if (stricmp(Value[i].c_str(), "right") == 0) { Anchor |= Anchor_Right; } - if (stricmp(Value[i].c_str(), "bottom") == 0) { Anchor |= Anchor_Bottom; } + if (stricmp(Value[i].c_str(), "left") == 0) { + Anchor |= Anchor_Left; + } + if (stricmp(Value[i].c_str(), "top") == 0) { + Anchor |= Anchor_Top; + } + if (stricmp(Value[i].c_str(), "right") == 0) { + Anchor |= Anchor_Right; + } + if (stricmp(Value[i].c_str(), "bottom") == 0) { + Anchor |= Anchor_Bottom; + } } // The anchor cannot have both sides missing, so we default to Left, Top is that is the case - if (!(Anchor & Anchor_Left) && !(Anchor & Anchor_Right)) { Anchor |= Anchor_Left; } - if (!(Anchor & Anchor_Top) && !(Anchor & Anchor_Bottom)) { Anchor |= Anchor_Top; } + if (!(Anchor & Anchor_Left) && !(Anchor & Anchor_Right)) { + Anchor |= Anchor_Left; + } + if (!(Anchor & Anchor_Top) && !(Anchor & Anchor_Bottom)) { + Anchor |= Anchor_Top; + } return Anchor; } @@ -194,15 +218,17 @@ void GUIControl::StoreProperties() {} void GUIControl::SetVisible(bool Visible) { // Default method is the grab the main panel and directly set its state. Controls that use multiple panels on the same layer will need to override this function - GUIPanel *Panel = GetPanel(); - if (Panel) { Panel->_SetVisible(Visible); } + GUIPanel* Panel = GetPanel(); + if (Panel) { + Panel->_SetVisible(Visible); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIControl::GetVisible() { // See SetVisible() comment - GUIPanel *Panel = GetPanel(); + GUIPanel* Panel = GetPanel(); if (Panel) { return Panel->_GetVisible(); } @@ -213,35 +239,39 @@ bool GUIControl::GetVisible() { void GUIControl::SetEnabled(bool Enabled) { // See SetVisible() comment - GUIPanel *Panel = GetPanel(); - if (Panel) { Panel->_SetEnabled(Enabled); } + GUIPanel* Panel = GetPanel(); + if (Panel) { + Panel->_SetEnabled(Enabled); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIControl::GetEnabled() { // See SetVisible() comment - GUIPanel *Panel = GetPanel(); - if (Panel) { return Panel->_GetEnabled(); } + GUIPanel* Panel = GetPanel(); + if (Panel) { + return Panel->_GetEnabled(); + } return false; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIControl * GUIControl::GetParent() { +GUIControl* GUIControl::GetParent() { return m_ControlParent; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIProperties * GUIControl::GetProperties() { +GUIProperties* GUIControl::GetProperties() { return &m_Properties; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControl::ApplyProperties(GUIProperties *Props) { +void GUIControl::ApplyProperties(GUIProperties* Props) { assert(Props); m_Properties.Update(Props); @@ -258,7 +288,7 @@ void GUIControl::ApplyProperties(GUIProperties *Props) { Props->GetValue("Visible", &Visible); // Adjust position from parent - GUIPanel *P = GetPanel(); + GUIPanel* P = GetPanel(); if (P && P->GetParentPanel()) { int px; int py; @@ -286,10 +316,10 @@ bool GUIControl::IsContainer() { void GUIControl::RemoveChild(const std::string Name) { // Note: We do NOT free the children because they are still linked in through their panels. This merely removes the control from the list. // This will cause a small memory leak, but this is only designed for the GUI Editor and is a bit of a hack. - std::vector::iterator it; + std::vector::iterator it; for (it = m_ControlChildren.begin(); it != m_ControlChildren.end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; if (C && C->GetName().compare(Name) == 0) { m_ControlChildren.erase(it); break; @@ -302,11 +332,13 @@ void GUIControl::RemoveChild(const std::string Name) { void GUIControl::RemoveChildren() { // Note: We do NOT free the children because they are still linked in through their panels. This merely removes the control from the list. // This will cause a small memory leak, but this is only designed for the GUI Editor and is a bit of a hack. - std::vector::iterator it; + std::vector::iterator it; for (it = m_ControlChildren.begin(); it != m_ControlChildren.end(); it++) { - GUIControl *C = *it; - if (C) { m_ControlManager->RemoveControl(C->GetName(), false); } + GUIControl* C = *it; + if (C) { + m_ControlManager->RemoveControl(C->GetName(), false); + } } m_ControlChildren.clear(); diff --git a/Source/GUI/GUIControl.h b/Source/GUI/GUIControl.h index 084ab2fc37..d1f05d1955 100644 --- a/Source/GUI/GUIControl.h +++ b/Source/GUI/GUIControl.h @@ -5,315 +5,283 @@ namespace RTE { -class GUIControlManager; + class GUIControlManager; + + /// + /// A base class inherited by all controls. + /// + class GUIControl { + + public: + // Anchor points + enum { + Anchor_Left = 0x01, + Anchor_Top = 0x02, + Anchor_Right = 0x04, + Anchor_Bottom = 0x08 + } Anchor; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIControl + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIControl object in + // system memory. + // Arguments: None. + + GUIControl(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position, Size + + virtual void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + virtual void Create(GUIProperties* Props); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + virtual void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: Activate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control is activated and ready for use. + // Arguments: None. + + virtual void Activate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + virtual void ChangeSkin(GUISkin* Skin); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddEvent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Add a new event to the queue. + // Arguments: Type, Message, Data. + + void AddEvent(int Type, int Msg, int Data); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control's name. + // Arguments: None. + + std::string GetName(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetToolTip + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the control's tooltip string. + // Arguments: The new ToolTip for this. + + void SetToolTip(const std::string& tip) { m_Properties.SetValue("ToolTip", tip); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetToolTip + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control's tooltip string. + // Arguments: None. + + std::string GetToolTip(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + std::string GetID() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAnchor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the anchor flags. + // Arguments: None. + + int GetAnchor(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddChild + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a child to this control + // Arguments: Control. -/// -/// A base class inherited by all controls. -/// -class GUIControl { + void AddChild(GUIControl* Control); -public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetChildren + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the children lst + // Arguments: None. - // Anchor points - enum { - Anchor_Left = 0x01, - Anchor_Top = 0x02, - Anchor_Right = 0x04, - Anchor_Bottom = 0x08 - } Anchor; + std::vector* GetChildren(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIControl -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIControl object in -// system memory. -// Arguments: None. + virtual GUIPanel* GetPanel(); - GUIControl(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the control properties. + // Arguments: Writer. + // Returns: True if sucessful + bool Save(GUIWriter* W); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position, Size + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. - virtual void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1); + virtual void StoreProperties(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. + virtual void Move(int X, int Y); - virtual void Create(GUIProperties *Props); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + virtual void Resize(int Width, int Height); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. - virtual void Destroy(); + virtual void GetControlRect(int* X, int* Y, int* Width, int* Height); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: SetVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the visibility of the control. + // Arguments: Visible. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Activate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control is activated and ready for use. -// Arguments: None. + virtual void SetVisible(bool Visible); - virtual void Activate(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: GetVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the visibility of the control. + // Arguments: None. + virtual bool GetVisible(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: SetEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the enabled state of the control. + // Arguments: Enabled. - virtual void ChangeSkin(GUISkin *Skin); + virtual void SetEnabled(bool Enabled); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: GetEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the enabled state of the control. + // Arguments: None. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddEvent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Add a new event to the queue. -// Arguments: Type, Message, Data. + virtual bool GetEnabled(); - void AddEvent(int Type, int Msg, int Data); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetParent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the parent of this control. + // Arguments: None. + GUIControl* GetParent(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control's name. -// Arguments: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control properties. + // Arguments: None. - std::string GetName(); + GUIProperties* GetProperties(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetToolTip -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the control's tooltip string. -// Arguments: The new ToolTip for this. + virtual void ApplyProperties(GUIProperties* Props); - void SetToolTip(const std::string &tip) { m_Properties.SetValue("ToolTip", tip); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the IsContainer value. + // Arguments: None. + bool IsContainer(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetToolTip -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control's tooltip string. -// Arguments: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveChild + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a child based on name. + // Arguments: Child Name. - std::string GetToolTip(); + void RemoveChild(const std::string Name); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveChildren + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes all the children. + // Arguments: None. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. + void RemoveChildren(); - std::string GetID() const; + protected: + GUISkin* m_Skin; + int m_SkinPreset; + GUIProperties m_Properties; + GUIControl* m_ControlParent; + std::vector m_ControlChildren; + std::string m_ControlID; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAnchor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the anchor flags. -// Arguments: None. + bool m_IsContainer; - int GetAnchor(); + // For the GUI editor + int m_MinWidth; + int m_MinHeight; + int m_DefWidth; + int m_DefHeight; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddChild -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a child to this control -// Arguments: Control. - - void AddChild(GUIControl *Control); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetChildren -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the children lst -// Arguments: None. - - std::vector * GetChildren(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - virtual GUIPanel * GetPanel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the control properties. -// Arguments: Writer. -// Returns: True if sucessful - - bool Save(GUIWriter *W); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - virtual void StoreProperties(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - virtual void Move(int X, int Y); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - virtual void Resize(int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - virtual void GetControlRect(int *X, int *Y, int *Width, int *Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: SetVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the visibility of the control. -// Arguments: Visible. - - virtual void SetVisible(bool Visible); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: GetVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the visibility of the control. -// Arguments: None. - - virtual bool GetVisible(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: SetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the enabled state of the control. -// Arguments: Enabled. - - virtual void SetEnabled(bool Enabled); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: GetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the enabled state of the control. -// Arguments: None. - - virtual bool GetEnabled(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetParent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the parent of this control. -// Arguments: None. - - GUIControl * GetParent(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control properties. -// Arguments: None. - - GUIProperties * GetProperties(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - virtual void ApplyProperties(GUIProperties *Props); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the IsContainer value. -// Arguments: None. - - bool IsContainer(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveChild -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a child based on name. -// Arguments: Child Name. - - void RemoveChild(const std::string Name); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveChildren -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes all the children. -// Arguments: None. - - void RemoveChildren(); - -protected: - - GUISkin *m_Skin; - int m_SkinPreset; - GUIProperties m_Properties; - GUIControl *m_ControlParent; - std::vector m_ControlChildren; - - std::string m_ControlID; - - bool m_IsContainer; - - // For the GUI editor - int m_MinWidth; - int m_MinHeight; - int m_DefWidth; - int m_DefHeight; - - GUIControlManager *m_ControlManager; -}; -}; + GUIControlManager* m_ControlManager; + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIControlFactory.cpp b/Source/GUI/GUIControlFactory.cpp index 9e93289219..12568b4041 100644 --- a/Source/GUI/GUIControlFactory.cpp +++ b/Source/GUI/GUIControlFactory.cpp @@ -18,7 +18,7 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIControl * GUIControlFactory::CreateControl(GUIManager *Manager, GUIControlManager *ControlManager, const std::string &ControlName) { +GUIControl* GUIControlFactory::CreateControl(GUIManager* Manager, GUIControlManager* ControlManager, const std::string& ControlName) { // Button if (ControlName.compare(GUIButton::GetControlID()) == 0) { return new GUIButton(Manager, ControlManager); diff --git a/Source/GUI/GUIControlFactory.h b/Source/GUI/GUIControlFactory.h index 4ace52a2bb..0c566d088c 100644 --- a/Source/GUI/GUIControlFactory.h +++ b/Source/GUI/GUIControlFactory.h @@ -3,21 +3,19 @@ namespace RTE { -/// -/// A class used to create the different controls based on name. -/// -class GUIControlFactory { + /// + /// A class used to create the different controls based on name. + /// + class GUIControlFactory { -public: + public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateControl + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Method used for creating controls + // Arguments: Control Type Name. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateControl -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Method used for creating controls -// Arguments: Control Type Name. - - static GUIControl *CreateControl(GUIManager *Manager, GUIControlManager *ControlManager, const std::string &ControlName); - -}; -}; + static GUIControl* CreateControl(GUIManager* Manager, GUIControlManager* ControlManager, const std::string& ControlName); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIControlManager.cpp b/Source/GUI/GUIControlManager.cpp index f1adacd1e9..6f93571321 100644 --- a/Source/GUI/GUIControlManager.cpp +++ b/Source/GUI/GUIControlManager.cpp @@ -18,7 +18,7 @@ GUIControlManager::GUIControlManager() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIControlManager::Create(GUIScreen *Screen, GUIInput *Input, const std::string &SkinDir, const std::string &SkinFilename) { +bool GUIControlManager::Create(GUIScreen* Screen, GUIInput* Input, const std::string& SkinDir, const std::string& SkinFilename) { assert(Screen && Input); m_Screen = Screen; @@ -68,11 +68,11 @@ void GUIControlManager::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIControlManager::Clear() { - std::vector::iterator it; + std::vector::iterator it; // Destroy every control for (it = m_ControlList.begin(); it != m_ControlList.end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; C->Destroy(); delete C; @@ -83,25 +83,27 @@ void GUIControlManager::Clear() { m_GUIManager->Clear(); // Destroy the event queue - std::vector::iterator ite; + std::vector::iterator ite; for (ite = m_EventQueue.begin(); ite != m_EventQueue.end(); ite++) { - GUIEvent *E = *ite; - if (E) { delete E; } + GUIEvent* E = *ite; + if (E) { + delete E; + } } m_EventQueue.clear(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControlManager::ChangeSkin(const std::string &SkinDir, const std::string &SkinFilename) { - std::vector::iterator it; +void GUIControlManager::ChangeSkin(const std::string& SkinDir, const std::string& SkinFilename) { + std::vector::iterator it; m_Skin->Destroy(); m_Skin->Load(SkinDir, SkinFilename); // Go through every control and change its skin for (it = m_ControlList.begin(); it != m_ControlList.end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; C->ChangeSkin(m_Skin); } @@ -109,14 +111,14 @@ void GUIControlManager::ChangeSkin(const std::string &SkinDir, const std::string ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIControl * GUIControlManager::AddControl(const std::string &Name, const std::string &Type, GUIControl *Parent, int X, int Y, int Width, int Height) { +GUIControl* GUIControlManager::AddControl(const std::string& Name, const std::string& Type, GUIControl* Parent, int X, int Y, int Width, int Height) { // Skip if we already have a control of this name if (GetControl(Name)) { return nullptr; } // Create the control - GUIControl *Control = GUIControlFactory::CreateControl(m_GUIManager, this, Type); + GUIControl* Control = GUIControlFactory::CreateControl(m_GUIManager, this, Type); if (!Control) { return nullptr; } @@ -124,7 +126,7 @@ GUIControl * GUIControlManager::AddControl(const std::string &Name, const std::s Control->Create(Name, X, Y, Width, Height); Control->ChangeSkin(m_Skin); - GUIPanel *Pan = nullptr; + GUIPanel* Pan = nullptr; if (Parent) { Pan = Parent->GetPanel(); Parent->AddChild(Control); @@ -145,7 +147,7 @@ GUIControl * GUIControlManager::AddControl(const std::string &Name, const std::s ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIControl * GUIControlManager::AddControl(GUIProperties *Property) { +GUIControl* GUIControlManager::AddControl(GUIProperties* Property) { assert(Property); // Get the control type and name @@ -159,7 +161,7 @@ GUIControl * GUIControlManager::AddControl(GUIProperties *Property) { return nullptr; } // Create the control - GUIControl *Control = GUIControlFactory::CreateControl(m_GUIManager, this, Type); + GUIControl* Control = GUIControlFactory::CreateControl(m_GUIManager, this, Type); if (!Control) { return nullptr; } @@ -171,8 +173,8 @@ GUIControl * GUIControlManager::AddControl(GUIProperties *Property) { std::string Parent; Property->GetValue("Parent", &Parent); - GUIControl *Par = GetControl(Parent); - GUIPanel *Pan = nullptr; + GUIControl* Par = GetControl(Parent); + GUIPanel* Pan = nullptr; if (Par && Parent.compare("None") != 0) { Pan = Par->GetPanel(); Par->AddChild(Control); @@ -195,11 +197,11 @@ GUIControl * GUIControlManager::AddControl(GUIProperties *Property) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIControl * GUIControlManager::GetControl(const std::string &Name) { - std::vector::iterator it; +GUIControl* GUIControlManager::GetControl(const std::string& Name) { + std::vector::iterator it; for (it = m_ControlList.begin(); it != m_ControlList.end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; if (C->GetName().compare(Name) == 0) { return C; } @@ -211,15 +213,17 @@ GUIControl * GUIControlManager::GetControl(const std::string &Name) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -std::vector * GUIControlManager::GetControlList() { +std::vector* GUIControlManager::GetControlList() { return &m_ControlList; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIControl * GUIControlManager::GetControlUnderPoint(int pointX, int pointY, GUIControl *pParent, int depth) { +GUIControl* GUIControlManager::GetControlUnderPoint(int pointX, int pointY, GUIControl* pParent, int depth) { // Default to the root object if no parent specified - if (!pParent) { pParent = m_ControlList.front(); } + if (!pParent) { + pParent = m_ControlList.front(); + } if (!pParent) { return nullptr; } @@ -240,8 +244,8 @@ GUIControl * GUIControlManager::GetControlUnderPoint(int pointX, int pointY, GUI } // Check children - std::vector *List = pParent->GetChildren(); - std::vector::reverse_iterator it; + std::vector* List = pParent->GetChildren(); + std::vector::reverse_iterator it; assert(List); @@ -250,7 +254,7 @@ GUIControl * GUIControlManager::GetControlUnderPoint(int pointX, int pointY, GUI for (it = List->rbegin(); it != List->rend(); it++) { // Only check visible controls if ((*it)->GetVisible()) { - GUIControl *C = GetControlUnderPoint(pointX, pointY, *it, depth - 1); + GUIControl* C = GetControlUnderPoint(pointX, pointY, *it, depth - 1); if (C) { return C; } @@ -264,13 +268,13 @@ GUIControl * GUIControlManager::GetControlUnderPoint(int pointX, int pointY, GUI ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControlManager::RemoveControl(const std::string &Name, bool RemoveFromParent) { +void GUIControlManager::RemoveControl(const std::string& Name, bool RemoveFromParent) { // NOTE: We can't simply remove it because some controls need to remove extra panels and it's silly to add 'remove' to every control to remove their extra panels (ie. Combobox). // Signals and stuff are also linked in so we just remove the controls from the list and not from memory. - std::vector::iterator it; + std::vector::iterator it; for (it = m_ControlList.begin(); it != m_ControlList.end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; if (C->GetName().compare(Name) == 0) { // Just remove it from the list @@ -281,7 +285,9 @@ void GUIControlManager::RemoveControl(const std::string &Name, bool RemoveFromPa C->RemoveChildren(); // Remove me from my parent - if (C->GetParent() && RemoveFromParent) { C->GetParent()->RemoveChild(Name); } + if (C->GetParent() && RemoveFromParent) { + C->GetParent()->RemoveChild(Name); + } break; } @@ -296,7 +302,6 @@ void GUIControlManager::Update(bool ignoreKeyboardEvents) { // Process the manager m_GUIManager->Update(ignoreKeyboardEvents); - } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -307,13 +312,13 @@ void GUIControlManager::Draw() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControlManager::Draw(GUIScreen *pScreen) { +void GUIControlManager::Draw(GUIScreen* pScreen) { m_GUIManager->Draw(pScreen); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControlManager::DrawMouse(GUIScreen *guiScreen) { +void GUIControlManager::DrawMouse(GUIScreen* guiScreen) { int MouseX; int MouseY; m_Input->GetMousePosition(&MouseX, &MouseY); @@ -340,14 +345,16 @@ void GUIControlManager::DrawMouse(GUIScreen *guiScreen) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIControlManager::GetEvent(GUIEvent *Event) { +bool GUIControlManager::GetEvent(GUIEvent* Event) { if (Event && !m_EventQueue.empty()) { // Copy the event *Event = *m_EventQueue.back(); // Free the event - if (GUIEvent *ptr = m_EventQueue.at(m_EventQueue.size() - 1)) { delete ptr; } + if (GUIEvent* ptr = m_EventQueue.at(m_EventQueue.size() - 1)) { + delete ptr; + } m_EventQueue.pop_back(); return true; @@ -358,9 +365,11 @@ bool GUIControlManager::GetEvent(GUIEvent *Event) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIControlManager::AddEvent(GUIEvent *Event) { +void GUIControlManager::AddEvent(GUIEvent* Event) { // Add the event to the queue - if (Event) { m_EventQueue.push_back(Event); } + if (Event) { + m_EventQueue.push_back(Event); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -371,7 +380,7 @@ void GUIControlManager::SetCursor(int CursorType) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIControlManager::Save(const std::string &Filename) { +bool GUIControlManager::Save(const std::string& Filename) { GUIWriter W; if (W.Create(Filename) != 0) { return false; @@ -385,14 +394,14 @@ bool GUIControlManager::Save(const std::string &Filename) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIControlManager::Save(GUIWriter *W) { +bool GUIControlManager::Save(GUIWriter* W) { assert(W); // Go through each control - std::vector::iterator it; + std::vector::iterator it; for (it = m_ControlList.begin(); it != m_ControlList.end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; C->Save(W); // Separate controls by one line W->NewLine(); @@ -403,7 +412,7 @@ bool GUIControlManager::Save(GUIWriter *W) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIControlManager::Load(const std::string &Filename, bool keepOld) { +bool GUIControlManager::Load(const std::string& Filename, bool keepOld) { GUIReader reader; const std::string pathFile = g_PresetMan.GetFullModulePath(Filename); if (reader.Create(pathFile.c_str()) != 0) { @@ -411,12 +420,14 @@ bool GUIControlManager::Load(const std::string &Filename, bool keepOld) { } // Clear the current layout, IF directed to - if (!keepOld) { Clear(); } + if (!keepOld) { + Clear(); + } - std::vector ControlList; + std::vector ControlList; ControlList.clear(); - GUIProperties *CurProp = nullptr; + GUIProperties* CurProp = nullptr; while (!reader.GetStream()->eof()) { std::string line = reader.ReadLine(); @@ -427,7 +438,7 @@ bool GUIControlManager::Load(const std::string &Filename, bool keepOld) { // Is the line a section? if (line.front() == '[' && line.back() == ']') { - GUIProperties *p = new GUIProperties(line.substr(1, line.size() - 2)); + GUIProperties* p = new GUIProperties(line.substr(1, line.size() - 2)); CurProp = p; ControlList.push_back(p); continue; @@ -450,9 +461,9 @@ bool GUIControlManager::Load(const std::string &Filename, bool keepOld) { } // Go through each control item and create it - std::vector::iterator it; + std::vector::iterator it; for (it = ControlList.begin(); it != ControlList.end(); it++) { - GUIProperties *Prop = *it; + GUIProperties* Prop = *it; AddControl(Prop); // Free the property class delete Prop; diff --git a/Source/GUI/GUIControlManager.h b/Source/GUI/GUIControlManager.h index edbf0693bc..1a659de2b0 100644 --- a/Source/GUI/GUIControlManager.h +++ b/Source/GUI/GUIControlManager.h @@ -6,294 +6,266 @@ namespace RTE { -/// -/// A class used to manage the GUI as a whole and provide the interface between the GUI and the rest of the system. -/// -class GUIControlManager { - friend class GUIControl; - -public: - - // Cursor types - enum { - Pointer, - Text, - HorSize - } CursorType; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIControlmanager -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIControlManager object in -// system memory. -// Arguments: None. - - GUIControlManager(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: GUIControlmanager -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a GUIControlManager object in -// system memory. -// Arguments: None. - - ~GUIControlManager() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates the data for the control manager -// Arguments: Screen and Input Interfaces, Skin directory - - bool Create(GUIScreen *Screen, GUIInput *Input, const std::string &SkinDir, const std::string &SkinFilename = "skin.ini"); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Frees all the allocated resources. -// Arguments: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the controls. -// Arguments: None. - - void Clear(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes the skin of the controls. -// Arguments: Skin directory. - - void ChangeSkin(const std::string &SkinDir, const std::string &SkinFilename = "skin.ini"); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the Skin object currently in use. -// Arguments: None. -// Returns: A pointer to the currently used skin. Please don't mess it up. - - GUISkin * GetSkin() { return m_Skin; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the GUI every frame -// Arguments: Whether keyboard events should be ignored or not. Used to avoid conflicts when custom keyboard handling for GUI elements is preset. - - void Update(bool ignoreKeyboardEvents = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the GUI to the back buffer. -// Arguments: None. - - void Draw(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the GUI to the back buffer. -// Arguments: The GUIScreen to draw to, overriding the one passed in on construction - - void Draw(GUIScreen *pScreen); - - - /// - /// Draws the mouse to the backbuffer. - /// - /// The GUIScreen to draw to, overriding the one passed in on construction. - void DrawMouse(GUIScreen *guiScreen = nullptr); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnableMouse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Enables and disables the mouse completely for this. -// Arguments: Enable? - - void EnableMouse(bool enable = true) { m_GUIManager->EnableMouse(enable); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the absolute position of this entire GUI on the screen. This is -// useful if the UI's are being drawn in a different area of the screen -// than the top left corner. This will adjust the mouse input to match -// the offset screen location. -// Arguments: The position. - - void SetPosOnScreen(int screenPosX, int screenPosY) { m_Input->SetMouseOffset(-screenPosX, -screenPosY); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetManager -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control manager -// Arguments: Name. -// Returns: The manager, ownership is NOT transferred! - - GUIManager * GetManager() { return m_GUIManager; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddControl -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Manually creates a control. -// Arguments: Name, Type, Position, Size, Parent. -// Returns: GUIControl class created. 0 if not created. - - GUIControl * AddControl(const std::string &Name, const std::string &Type, GUIControl *Parent, int X, int Y, int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddControl -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Manually creates a control. -// Arguments: Properties. -// Returns: GUIControl class created. 0 if not created. - - GUIControl * AddControl(GUIProperties *Property); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControl -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a control -// Arguments: Name. -// Returns: GUIControl class, or 0 if not found. - - GUIControl * GetControl(const std::string &Name); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control list -// Arguments: None. -// Returns: vector Pointer. - - std::vector * GetControlList(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlUnderPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if a control is under a specific point -// Arguments: The absolute point coordinates to check under. -// Parent to check under. Pass null to default to the root control. -// How many levels of children under the parent to look at. If negative, -// goes as far as it can. -// Returns: GUIControl. NULL if no control under the point - - GUIControl * GetControlUnderPoint(int pointX, int pointY, GUIControl *pParent = nullptr, int depth = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveControl -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a control by name -// Arguments: Name, RemoveFromParent. -// Returns: None. - - void RemoveControl(const std::string &Name, bool RemoveFromParent); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEvent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets an event from the queue. -// Arguments: Pointer to variable receiving the Event. -// Returns: Returns true when an event was grabbed. -// Returns false when there was no more events in the queue -// OR the Event pointer is 0. - - bool GetEvent(GUIEvent *Event); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCursor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the cursor type. -// Arguments: Cursor type. - - void SetCursor(int CursorType); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the layout to a file. -// Arguments: Filename. -// Returns: True if successful. - - bool Save(const std::string &Filename); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the layout to a Writer class. -// Arguments: Writer class. -// Returns: True if successful. - - bool Save(GUIWriter *W); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Load -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads the layout from a file. -// Arguments: Filename. -// Whether to NOT clear out the manager, but just add the controls loaded -// to the existing layout. -// Returns: True if successful. - - bool Load(const std::string &Filename, bool keepOld = false); - - /// - /// Gets the GUIScreen that this GUIControlManager is drawing itself to. + /// A class used to manage the GUI as a whole and provide the interface between the GUI and the rest of the system. /// - /// Pointer to the GUIScreen that this GUIControlManager is drawing itself to. - GUIScreen * GetScreen() const { return m_Screen; } - -private: - - GUIScreen *m_Screen; // Not owned. - GUIInput *m_Input; // Not owned. - GUISkin *m_Skin; - GUIManager *m_GUIManager; - - std::vector m_ControlList; - std::vector m_EventQueue; - - int m_CursorType; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddEvent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Add a new event to the queue. -// Arguments: Event point. - - void AddEvent(GUIEvent *Event); -}; -}; -#endif // _GUICONTROLMANAGER_ \ No newline at end of file + class GUIControlManager { + friend class GUIControl; + + public: + // Cursor types + enum { + Pointer, + Text, + HorSize + } CursorType; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIControlmanager + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIControlManager object in + // system memory. + // Arguments: None. + + GUIControlManager(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: GUIControlmanager + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a GUIControlManager object in + // system memory. + // Arguments: None. + + ~GUIControlManager() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates the data for the control manager + // Arguments: Screen and Input Interfaces, Skin directory + + bool Create(GUIScreen* Screen, GUIInput* Input, const std::string& SkinDir, const std::string& SkinFilename = "skin.ini"); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Frees all the allocated resources. + // Arguments: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the controls. + // Arguments: None. + + void Clear(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes the skin of the controls. + // Arguments: Skin directory. + + void ChangeSkin(const std::string& SkinDir, const std::string& SkinFilename = "skin.ini"); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the Skin object currently in use. + // Arguments: None. + // Returns: A pointer to the currently used skin. Please don't mess it up. + + GUISkin* GetSkin() { return m_Skin; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the GUI every frame + // Arguments: Whether keyboard events should be ignored or not. Used to avoid conflicts when custom keyboard handling for GUI elements is preset. + + void Update(bool ignoreKeyboardEvents = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the GUI to the back buffer. + // Arguments: None. + + void Draw(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the GUI to the back buffer. + // Arguments: The GUIScreen to draw to, overriding the one passed in on construction + + void Draw(GUIScreen* pScreen); + + /// + /// Draws the mouse to the backbuffer. + /// + /// The GUIScreen to draw to, overriding the one passed in on construction. + void DrawMouse(GUIScreen* guiScreen = nullptr); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnableMouse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Enables and disables the mouse completely for this. + // Arguments: Enable? + + void EnableMouse(bool enable = true) { m_GUIManager->EnableMouse(enable); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPosOnScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the absolute position of this entire GUI on the screen. This is + // useful if the UI's are being drawn in a different area of the screen + // than the top left corner. This will adjust the mouse input to match + // the offset screen location. + // Arguments: The position. + + void SetPosOnScreen(int screenPosX, int screenPosY) { m_Input->SetMouseOffset(-screenPosX, -screenPosY); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetManager + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control manager + // Arguments: Name. + // Returns: The manager, ownership is NOT transferred! + + GUIManager* GetManager() { return m_GUIManager; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddControl + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Manually creates a control. + // Arguments: Name, Type, Position, Size, Parent. + // Returns: GUIControl class created. 0 if not created. + + GUIControl* AddControl(const std::string& Name, const std::string& Type, GUIControl* Parent, int X, int Y, int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddControl + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Manually creates a control. + // Arguments: Properties. + // Returns: GUIControl class created. 0 if not created. + + GUIControl* AddControl(GUIProperties* Property); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControl + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a control + // Arguments: Name. + // Returns: GUIControl class, or 0 if not found. + + GUIControl* GetControl(const std::string& Name); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control list + // Arguments: None. + // Returns: vector Pointer. + + std::vector* GetControlList(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlUnderPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if a control is under a specific point + // Arguments: The absolute point coordinates to check under. + // Parent to check under. Pass null to default to the root control. + // How many levels of children under the parent to look at. If negative, + // goes as far as it can. + // Returns: GUIControl. NULL if no control under the point + + GUIControl* GetControlUnderPoint(int pointX, int pointY, GUIControl* pParent = nullptr, int depth = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveControl + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a control by name + // Arguments: Name, RemoveFromParent. + // Returns: None. + + void RemoveControl(const std::string& Name, bool RemoveFromParent); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEvent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets an event from the queue. + // Arguments: Pointer to variable receiving the Event. + // Returns: Returns true when an event was grabbed. + // Returns false when there was no more events in the queue + // OR the Event pointer is 0. + + bool GetEvent(GUIEvent* Event); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCursor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the cursor type. + // Arguments: Cursor type. + + void SetCursor(int CursorType); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the layout to a file. + // Arguments: Filename. + // Returns: True if successful. + + bool Save(const std::string& Filename); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the layout to a Writer class. + // Arguments: Writer class. + // Returns: True if successful. + + bool Save(GUIWriter* W); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Load + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads the layout from a file. + // Arguments: Filename. + // Whether to NOT clear out the manager, but just add the controls loaded + // to the existing layout. + // Returns: True if successful. + + bool Load(const std::string& Filename, bool keepOld = false); + + /// + /// Gets the GUIScreen that this GUIControlManager is drawing itself to. + /// + /// Pointer to the GUIScreen that this GUIControlManager is drawing itself to. + GUIScreen* GetScreen() const { return m_Screen; } + + private: + GUIScreen* m_Screen; // Not owned. + GUIInput* m_Input; // Not owned. + GUISkin* m_Skin; + GUIManager* m_GUIManager; + + std::vector m_ControlList; + std::vector m_EventQueue; + + int m_CursorType; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddEvent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Add a new event to the queue. + // Arguments: Event point. + + void AddEvent(GUIEvent* Event); + }; +}; // namespace RTE +#endif // _GUICONTROLMANAGER_ \ No newline at end of file diff --git a/Source/GUI/GUIEvent.cpp b/Source/GUI/GUIEvent.cpp index 0a6dc56e94..1a3904694c 100644 --- a/Source/GUI/GUIEvent.cpp +++ b/Source/GUI/GUIEvent.cpp @@ -13,7 +13,7 @@ GUIEvent::GUIEvent() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIEvent::GUIEvent(GUIControl *Control, int Type, int Msg, int Data) { +GUIEvent::GUIEvent(GUIControl* Control, int Type, int Msg, int Data) { assert(Control); m_Control = Control; m_Type = Type; @@ -29,7 +29,7 @@ int GUIEvent::GetType() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIControl * GUIEvent::GetControl() { +GUIControl* GUIEvent::GetControl() { return m_Control; } diff --git a/Source/GUI/GUIEvent.h b/Source/GUI/GUIEvent.h index 28cd041998..e900742e42 100644 --- a/Source/GUI/GUIEvent.h +++ b/Source/GUI/GUIEvent.h @@ -3,82 +3,73 @@ namespace RTE { -/// -/// A class to hold event information. -/// -class GUIEvent { - -public: - - // Event Types - enum { - Command = 0, - Notification - } EventType; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIEvent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIEvent object in system -// memory. -// Arguments: None. - - GUIEvent(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIEvent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIEvent object in system -// memory. -// Arguments: Control, Event type, Msg, Data. - - GUIEvent(GUIControl *Control, int Type, int Msg, int Data); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetType -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the event type -// Arguments: None. - - int GetType() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMsg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the msg. -// Arguments: None. - - int GetMsg() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the data. -// Arguments: None. - - int GetData() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControl -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the event control. -// Arguments: None. - - GUIControl * GetControl(); - -private: - - GUIControl *m_Control; - int m_Type; - int m_Msg; - int m_Data; - -}; -}; + /// + /// A class to hold event information. + /// + class GUIEvent { + + public: + // Event Types + enum { + Command = 0, + Notification + } EventType; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIEvent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIEvent object in system + // memory. + // Arguments: None. + + GUIEvent(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIEvent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIEvent object in system + // memory. + // Arguments: Control, Event type, Msg, Data. + + GUIEvent(GUIControl* Control, int Type, int Msg, int Data); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetType + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the event type + // Arguments: None. + + int GetType() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMsg + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the msg. + // Arguments: None. + + int GetMsg() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the data. + // Arguments: None. + + int GetData() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControl + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the event control. + // Arguments: None. + + GUIControl* GetControl(); + + private: + GUIControl* m_Control; + int m_Type; + int m_Msg; + int m_Data; + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIFont.cpp b/Source/GUI/GUIFont.cpp index ebfa840346..18b489c338 100644 --- a/Source/GUI/GUIFont.cpp +++ b/Source/GUI/GUIFont.cpp @@ -4,7 +4,7 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIFont::GUIFont(const std::string &Name) { +GUIFont::GUIFont(const std::string& Name) { m_Screen = nullptr; m_Font = nullptr; m_FontHeight = 0; @@ -22,7 +22,7 @@ GUIFont::GUIFont(const std::string &Name) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIFont::Load(GUIScreen *Screen, const std::string &Filename) { +bool GUIFont::Load(GUIScreen* Screen, const std::string& Filename) { assert(Screen); m_Screen = Screen; @@ -80,7 +80,9 @@ bool GUIFont::Load(GUIScreen *Screen, const std::string &Filename) { for (int j = y; j < y + m_FontHeight; j++) { for (int i = x; i < x + w; i++) { unsigned long Pixel = m_Font->GetPixel(i, j); - if (Pixel != Red && Pixel != BackG) { Height = std::max(Height, j - y); } + if (Pixel != Red && Pixel != BackG) { + Height = std::max(Height, j - y); + } } } @@ -107,16 +109,16 @@ bool GUIFont::Load(GUIScreen *Screen, const std::string &Filename) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIFont::Draw(GUIBitmap *Bitmap, int X, int Y, const std::string &Text, unsigned long Shadow) { +void GUIFont::Draw(GUIBitmap* Bitmap, int X, int Y, const std::string& Text, unsigned long Shadow) { unsigned char c; GUIRect Rect; - GUIBitmap *Surf = m_CurrentBitmap; + GUIBitmap* Surf = m_CurrentBitmap; int initX = X; assert(Surf); // Make the shadow color - FontColor *FSC = nullptr; + FontColor* FSC = nullptr; if (Shadow) { FSC = GetFontColor(Shadow); if (!FSC) { @@ -133,8 +135,12 @@ void GUIFont::Draw(GUIBitmap *Bitmap, int X, int Y, const std::string &Text, uns Y += m_FontHeight; X = initX; } - if (c == '\t') { X += m_Characters[' '].m_Width * 4; } - if (c < 0) { c += m_CharIndexCap; } + if (c == '\t') { + X += m_Characters[' '].m_Width * 4; + } + if (c < 0) { + c += m_CharIndexCap; + } if (c < 32 || c >= m_CharIndexCap) { continue; } @@ -145,7 +151,9 @@ void GUIFont::Draw(GUIBitmap *Bitmap, int X, int Y, const std::string &Text, uns SetRect(&Rect, offX, offY, offX + CharWidth, offY + m_FontHeight); // Draw the shadow - if (Shadow && FSC) { FSC->m_Bitmap->DrawTrans(Bitmap, X + 1, Y + 1, &Rect); } + if (Shadow && FSC) { + FSC->m_Bitmap->DrawTrans(Bitmap, X + 1, Y + 1, &Rect); + } // Draw the main color Surf->DrawTrans(Bitmap, X, Y, &Rect); @@ -157,7 +165,7 @@ void GUIFont::Draw(GUIBitmap *Bitmap, int X, int Y, const std::string &Text, uns ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIFont::DrawAligned(GUIBitmap *Bitmap, int X, int Y, const std::string &Text, int HAlign, int VAlign, int MaxWidth, unsigned long Shadow) { +void GUIFont::DrawAligned(GUIBitmap* Bitmap, int X, int Y, const std::string& Text, int HAlign, int VAlign, int MaxWidth, unsigned long Shadow) { std::string TextLine = Text; int lineStartPos = 0; int lineEndPos = 0; @@ -244,7 +252,7 @@ void GUIFont::SetColor(unsigned long Color) { if (Color != m_CurrentColor) { // Find the cached color - FontColor *FC = GetFontColor(Color); + FontColor* FC = GetFontColor(Color); // Use the cached color, otherwise just draw the default bitmap if (FC) { @@ -256,7 +264,7 @@ void GUIFont::SetColor(unsigned long Color) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int GUIFont::CalculateWidth(const std::string &Text) { +int GUIFont::CalculateWidth(const std::string& Text) { unsigned char c; int Width = 0; int WidestLine = 0; @@ -266,11 +274,15 @@ int GUIFont::CalculateWidth(const std::string &Text) { c = Text.at(i); // Reset line counting if newline encountered if (c == '\n') { - if (Width > WidestLine) { WidestLine = Width; } + if (Width > WidestLine) { + WidestLine = Width; + } Width = 0; continue; } - if (c < 0) { c += m_CharIndexCap; } + if (c < 0) { + c += m_CharIndexCap; + } if (c < 32 || c >= m_CharIndexCap) { continue; @@ -281,7 +293,9 @@ int GUIFont::CalculateWidth(const std::string &Text) { // Add kerning Width += m_Kerning; } - if (Width > WidestLine) { WidestLine = Width; } + if (Width > WidestLine) { + WidestLine = Width; + } return WidestLine; } @@ -297,7 +311,7 @@ int GUIFont::CalculateWidth(const char Character) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int GUIFont::CalculateHeight(const std::string &Text, int MaxWidth) { +int GUIFont::CalculateHeight(const std::string& Text, int MaxWidth) { if (Text.empty()) { return 0; } @@ -319,7 +333,9 @@ int GUIFont::CalculateHeight(const std::string &Text, int MaxWidth) { if (c < 32 || c >= m_CharIndexCap) { continue; } - if (c == ' ') { lastSpacePos = i; } + if (c == ' ') { + lastSpacePos = i; + } Width += m_Characters[c].m_Width + m_Kerning; if (MaxWidth > 0 && Width > MaxWidth) { @@ -365,7 +381,9 @@ void GUIFont::CacheColor(unsigned long Color) { // Go through the bitmap and change the pixels for (int y = 0; y < FC.m_Bitmap->GetHeight(); y++) { for (int x = 0; x < FC.m_Bitmap->GetWidth(); x++) { - if (FC.m_Bitmap->GetPixel(x, y) == m_MainColor) { FC.m_Bitmap->SetPixel(x, y, Color); } + if (FC.m_Bitmap->GetPixel(x, y) == m_MainColor) { + FC.m_Bitmap->SetPixel(x, y, Color); + } } } @@ -375,9 +393,9 @@ void GUIFont::CacheColor(unsigned long Color) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIFont::FontColor * GUIFont::GetFontColor(unsigned long Color) { +GUIFont::FontColor* GUIFont::GetFontColor(unsigned long Color) { std::vector::iterator it; - FontColor *F = nullptr; + FontColor* F = nullptr; for (it = m_ColorCache.begin(); it != m_ColorCache.end(); it++) { F = &(*it); @@ -419,7 +437,7 @@ void GUIFont::Destroy() { // Go through the color cache and destroy the bitmaps std::vector::iterator it; - FontColor *FC = 0; + FontColor* FC = 0; for (it = m_ColorCache.begin(); it != m_ColorCache.end(); it++) { FC = &(*it); if (FC && FC->m_Bitmap) { diff --git a/Source/GUI/GUIFont.h b/Source/GUI/GUIFont.h index 6434f95319..6628bdd6a2 100644 --- a/Source/GUI/GUIFont.h +++ b/Source/GUI/GUIFont.h @@ -3,195 +3,178 @@ namespace RTE { -/// -/// A class to handle the drawing of text. -/// -class GUIFont { + /// + /// A class to handle the drawing of text. + /// + class GUIFont { + + public: + // Horizontal Text Alignment, + enum { + Left = 0, + Centre, + Right + } HAlignment; + + // Vertical Text Alignment, + enum { + Top = 0, + Middle, + Bottom + } VAlignment; + + // Character structure + typedef struct { + int m_Width; + int m_Height; + int m_Offset; + } Character; + + // Font Color structure + typedef struct { + unsigned long m_Color; + GUIBitmap* m_Bitmap; + } FontColor; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIFont + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIFont object in system + // memory. + // Arguments: None. + + explicit GUIFont(const std::string& Name); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Load + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads the font from an image file. + // Arguments: Screen class, Filename of image. + + bool Load(GUIScreen* Screen, const std::string& Filename); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CacheColor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Pre-Calculates the font using a specific color. + // Arguments: Color. + + void CacheColor(unsigned long Color); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFontColor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Finds a font color structure from the cache. + // Arguments: Color. + + FontColor* GetFontColor(unsigned long Color); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws text to a bitmap. + // Arguments: Bitmap, Position, Text, Color, Drop-shadow, 0 = none. + + void Draw(GUIBitmap* Bitmap, int X, int Y, const std::string& Text, unsigned long Shadow = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawAligned + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws text to a bitmap aligned. + // Arguments: Bitmap, Position, Text. + + void DrawAligned(GUIBitmap* Bitmap, int X, int Y, const std::string& Text, int HAlign, int VAlign = Top, int maxWidth = 0, unsigned long Shadow = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetColor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current color. + // Arguments: Color. + + void SetColor(unsigned long Color); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculateWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the width of a piece of text. + // Arguments: Text. + + int CalculateWidth(const std::string& Text); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculateWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the width of a piece of text. + // Arguments: Character. + + int CalculateWidth(const char Character); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculateHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the height of a piece of text, if it's wrapped within a + // max width. + // Arguments: Text, and the max width. If 0, no wrapping is done. + + int CalculateHeight(const std::string& Text, int MaxWidth = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFontHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the font height. + // Arguments: None. + + int GetFontHeight() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the name of the font + // Arguments: None. + + std::string GetName() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys the font data + // Arguments: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetKerning + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get the character kerning (spacing) + // Arguments: None. + + int GetKerning() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetKerning + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Set the character kerning (spacing), in pixels. 1 = one empty pixel + // between chars, 0 = chars are touching. + // Arguments: None. + + void SetKerning(int newKerning = 1) { m_Kerning = newKerning; } -public: - - // Horizontal Text Alignment, - enum { - Left = 0, - Centre, - Right - } HAlignment; - - // Vertical Text Alignment, - enum { - Top = 0, - Middle, - Bottom - } VAlignment; - - // Character structure - typedef struct { - int m_Width; - int m_Height; - int m_Offset; - } Character; - - // Font Color structure - typedef struct { - unsigned long m_Color; - GUIBitmap *m_Bitmap; - } FontColor; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIFont -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIFont object in system -// memory. -// Arguments: None. - - explicit GUIFont(const std::string &Name); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Load -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads the font from an image file. -// Arguments: Screen class, Filename of image. - - bool Load(GUIScreen *Screen, const std::string &Filename); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CacheColor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Pre-Calculates the font using a specific color. -// Arguments: Color. - - void CacheColor(unsigned long Color); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFontColor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Finds a font color structure from the cache. -// Arguments: Color. - - FontColor * GetFontColor(unsigned long Color); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws text to a bitmap. -// Arguments: Bitmap, Position, Text, Color, Drop-shadow, 0 = none. - - void Draw(GUIBitmap *Bitmap, int X, int Y, const std::string &Text, unsigned long Shadow = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawAligned -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws text to a bitmap aligned. -// Arguments: Bitmap, Position, Text. - - void DrawAligned(GUIBitmap *Bitmap, int X, int Y, const std::string &Text, int HAlign, int VAlign = Top, int maxWidth = 0, unsigned long Shadow = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetColor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current color. -// Arguments: Color. - - void SetColor(unsigned long Color); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculateWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the width of a piece of text. -// Arguments: Text. - - int CalculateWidth(const std::string &Text); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculateWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the width of a piece of text. -// Arguments: Character. - - int CalculateWidth(const char Character); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculateHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the height of a piece of text, if it's wrapped within a -// max width. -// Arguments: Text, and the max width. If 0, no wrapping is done. - - int CalculateHeight(const std::string &Text, int MaxWidth = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFontHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the font height. -// Arguments: None. - - int GetFontHeight() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the name of the font -// Arguments: None. - - std::string GetName() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys the font data -// Arguments: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetKerning -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get the character kerning (spacing) -// Arguments: None. - - int GetKerning() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetKerning -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Set the character kerning (spacing), in pixels. 1 = one empty pixel -// between chars, 0 = chars are touching. -// Arguments: None. - - void SetKerning(int newKerning = 1) { m_Kerning = newKerning; } - -private: - - GUIBitmap *m_Font; - GUIScreen *m_Screen; - std::vector m_ColorCache; - - int m_FontHeight; - unsigned long m_MainColor; - unsigned long m_CurrentColor; - GUIBitmap *m_CurrentBitmap; - std::string m_Name; - Character m_Characters[256]; - - int m_CharIndexCap; // The highest index of valid characters that was read in from the file - - int m_Kerning; // Spacing between characters - int m_Leading; // Spacing between lines -}; -}; + private: + GUIBitmap* m_Font; + GUIScreen* m_Screen; + std::vector m_ColorCache; + + int m_FontHeight; + unsigned long m_MainColor; + unsigned long m_CurrentColor; + GUIBitmap* m_CurrentBitmap; + std::string m_Name; + Character m_Characters[256]; + + int m_CharIndexCap; // The highest index of valid characters that was read in from the file + + int m_Kerning; // Spacing between characters + int m_Leading; // Spacing between lines + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIInput.cpp b/Source/GUI/GUIInput.cpp index 6099b1100a..bac777c994 100644 --- a/Source/GUI/GUIInput.cpp +++ b/Source/GUI/GUIInput.cpp @@ -6,12 +6,12 @@ using namespace RTE; bool GUIInput::m_OverrideInput = false; -int GUIInput::m_NetworkMouseButtonsEvents[4][3] = { { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 } }; -int GUIInput::m_NetworkMouseButtonsStates[4][3] = { { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 } }; -int GUIInput::m_PrevNetworkMouseButtonsStates[4][3] = { { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 } }; +int GUIInput::m_NetworkMouseButtonsEvents[4][3] = {{-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}}; +int GUIInput::m_NetworkMouseButtonsStates[4][3] = {{-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}}; +int GUIInput::m_PrevNetworkMouseButtonsStates[4][3] = {{-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}}; -int GUIInput::m_NetworkMouseX[4] = { 0, 0, 0, 0 }; -int GUIInput::m_NetworkMouseY[4] = { 0, 0, 0, 0 }; +int GUIInput::m_NetworkMouseX[4] = {0, 0, 0, 0}; +int GUIInput::m_NetworkMouseY[4] = {0, 0, 0, 0}; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -25,8 +25,8 @@ GUIInput::GUIInput(int whichPlayer, bool keyJoyMouseCursor) { m_TextInput.clear(); m_HasTextInput = false; - //memset(m_NetworkMouseButtonsEvents, -1, sizeof(int) * 3); - //memset(m_NetworkMouseButtonsStates, -1, sizeof(int) * 3); + // memset(m_NetworkMouseButtonsEvents, -1, sizeof(int) * 3); + // memset(m_NetworkMouseButtonsStates, -1, sizeof(int) * 3); m_MouseX = 0; m_MouseY = 0; @@ -50,8 +50,10 @@ void GUIInput::Destroy() {} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIInput::GetKeyboard(unsigned char *Buffer) const { - if (Buffer) { memcpy(Buffer, m_KeyboardBuffer, sizeof(unsigned char) * KEYBOARD_BUFFER_SIZE); } +void GUIInput::GetKeyboard(unsigned char* Buffer) const { + if (Buffer) { + memcpy(Buffer, m_KeyboardBuffer, sizeof(unsigned char) * KEYBOARD_BUFFER_SIZE); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -68,10 +70,14 @@ unsigned char GUIInput::GetScanCodeState(unsigned char scancode) const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIInput::GetMouseButtons(int *Buttons, int *States) const { +void GUIInput::GetMouseButtons(int* Buttons, int* States) const { if (!m_OverrideInput) { - if (Buttons) { memcpy(Buttons, m_MouseButtonsEvents, sizeof(int) * 3); } - if (States) { memcpy(States, m_MouseButtonsStates, sizeof(int) * 3); } + if (Buttons) { + memcpy(Buttons, m_MouseButtonsEvents, sizeof(int) * 3); + } + if (States) { + memcpy(States, m_MouseButtonsStates, sizeof(int) * 3); + } } else { for (int i = 0; i < 3; i++) { Buttons[i] = -1; @@ -109,18 +115,30 @@ void GUIInput::SetNetworkMouseButton(int whichPlayer, int state1, int state2, in ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIInput::GetMousePosition(int *X, int *Y) const { +void GUIInput::GetMousePosition(int* X, int* Y) const { if (m_OverrideInput) { if (m_Player >= 0 && m_Player < 4) { - if (X) { *X = (m_NetworkMouseX[m_Player] + m_MouseOffsetX); } - if (Y) { *Y = (m_NetworkMouseY[m_Player] + m_MouseOffsetY); } + if (X) { + *X = (m_NetworkMouseX[m_Player] + m_MouseOffsetX); + } + if (Y) { + *Y = (m_NetworkMouseY[m_Player] + m_MouseOffsetY); + } } else { - if (X) { *X = (m_NetworkMouseX[0] + m_MouseOffsetX); } - if (Y) { *Y = (m_NetworkMouseY[0] + m_MouseOffsetY); } + if (X) { + *X = (m_NetworkMouseX[0] + m_MouseOffsetX); + } + if (Y) { + *Y = (m_NetworkMouseY[0] + m_MouseOffsetY); + } } } else { - if (X) { *X = (m_MouseX + m_MouseOffsetX); } - if (Y) { *Y = (m_MouseY + m_MouseOffsetY); } + if (X) { + *X = (m_MouseX + m_MouseOffsetX); + } + if (Y) { + *Y = (m_MouseY + m_MouseOffsetY); + } } } diff --git a/Source/GUI/GUIInput.h b/Source/GUI/GUIInput.h index 63102b0b9c..9327132844 100644 --- a/Source/GUI/GUIInput.h +++ b/Source/GUI/GUIInput.h @@ -3,219 +3,211 @@ namespace RTE { -/// -/// An interface class inherited by the different types of input methods. -/// -class GUIInput { - -public: - - // Mouse & Key events - enum { - None, - Released, // Has just been released - Pushed, // Has just been pushed down - Repeat // Is repeating - } Event; - - // Mouse & Key states - enum { - Up, - Down - } State; - - // Modifiers - enum { - ModNone = 0x00, - ModShift = 0x01, - ModCtrl = 0x02, - ModAlt = 0x04, - ModCommand = 0x08 - } Modifier; - - // Extra keys - enum { - Key_None = 0, - Key_Backspace = 0x00000008, - Key_Tab = 0x00000009, - Key_Enter = 0x0000000D, - Key_Escape = 0x0000001B, - Key_LeftArrow = 0x00000086, - Key_RightArrow = 0x00000087, - Key_UpArrow = 0x00000088, - Key_DownArrow = 0x00000089, - Key_Insert = 0x00000095, - Key_Delete = 0x00000096, - Key_Home = 0x00000097, - Key_End = 0x00000098, - Key_PageUp = 0x00000099, - Key_PageDown = 0x0000009A - } Keys; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIInput -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIInput object in system -// memory. -// Arguments: Whether the keyboard and joysticks also can control the mouse cursor. - - GUIInput(int whichPlayer, bool keyJoyMouseCursor = false); - - virtual ~GUIInput() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroy the screen -// Arguments: None. - - virtual void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMouseOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the offset for the mouse input to be adjusted by. This should -// be used when the GUI is being drawn somewhere else on the screen than -// the upper left corner. These values should be from the GUI to the upper -// left corner. -// Arguments: The new offset. - - void SetMouseOffset(int mouseOffsetX, int mouseOffsetY) { m_MouseOffsetX = mouseOffsetX; m_MouseOffsetY = mouseOffsetY; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMouseOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the offset for the mouse input to be adjusted by. This should -// These values should be from the GUI to the upper of the screen. -// left corner. -// Arguments: The new offset. - - void GetMouseOffset(int &mouseOffsetX, int &mouseOffsetY) const { mouseOffsetX = m_MouseOffsetX; mouseOffsetY = m_MouseOffsetY; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Input. -// Arguments: None. - - virtual void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetKeyboard -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Copies the keyboard buffer into an array. The keyboard buffer is -// ordered by ascii code and each entry contains a GUInput::Event enum -// state. -// Arguments: Buffer array. - - void GetKeyboard(unsigned char *Buffer) const; - - unsigned char GetAsciiState(unsigned char ascii) const; - - unsigned char GetScanCodeState(unsigned char scancode) const; - - bool GetTextInput(std::string_view &text) const { text = m_TextInput; return !m_TextInput.empty(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMouseButtons -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Copies the mouse button states into an array -// Arguments: State array. - - void GetMouseButtons(int *Events, int *States) const; - - - static void SetNetworkMouseButton(int whichPlayer, int state1, int state2, int state3); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMousePosition -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mouse position -// Arguments: Pointers to store the X and Y coordinates in - - void GetMousePosition(int *X, int *Y) const; - - - static void SetNetworkMouseMovement(int whichPlayer, int x, int y); - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetModifier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the key modifiers. -// Arguments: None. - - int GetModifier() const; - - - /// - /// This function returns how much the mouse scroll wheel has moved. Positive integer is scroll up, negative is scroll down. - /// - /// Mouse scroll wheel movement in integer value. - int GetMouseWheelChange() const { - return m_MouseWheelChange; - } - /// - /// Sets whether the keyboard and joysticks also control the mouse. + /// An interface class inherited by the different types of input methods. /// - /// Whether the keyboard and joysticks also control the mouse or not. - void SetKeyJoyMouseCursor(bool enableKeyJoyMouseCursor) { m_KeyJoyMouseCursor = enableKeyJoyMouseCursor; } - + class GUIInput { + + public: + // Mouse & Key events + enum { + None, + Released, // Has just been released + Pushed, // Has just been pushed down + Repeat // Is repeating + } Event; + + // Mouse & Key states + enum { + Up, + Down + } State; + + // Modifiers + enum { + ModNone = 0x00, + ModShift = 0x01, + ModCtrl = 0x02, + ModAlt = 0x04, + ModCommand = 0x08 + } Modifier; + + // Extra keys + enum { + Key_None = 0, + Key_Backspace = 0x00000008, + Key_Tab = 0x00000009, + Key_Enter = 0x0000000D, + Key_Escape = 0x0000001B, + Key_LeftArrow = 0x00000086, + Key_RightArrow = 0x00000087, + Key_UpArrow = 0x00000088, + Key_DownArrow = 0x00000089, + Key_Insert = 0x00000095, + Key_Delete = 0x00000096, + Key_Home = 0x00000097, + Key_End = 0x00000098, + Key_PageUp = 0x00000099, + Key_PageDown = 0x0000009A + } Keys; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIInput + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIInput object in system + // memory. + // Arguments: Whether the keyboard and joysticks also can control the mouse cursor. + + GUIInput(int whichPlayer, bool keyJoyMouseCursor = false); + + virtual ~GUIInput() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroy the screen + // Arguments: None. + + virtual void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMouseOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the offset for the mouse input to be adjusted by. This should + // be used when the GUI is being drawn somewhere else on the screen than + // the upper left corner. These values should be from the GUI to the upper + // left corner. + // Arguments: The new offset. + + void SetMouseOffset(int mouseOffsetX, int mouseOffsetY) { + m_MouseOffsetX = mouseOffsetX; + m_MouseOffsetY = mouseOffsetY; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMouseOffset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the offset for the mouse input to be adjusted by. This should + // These values should be from the GUI to the upper of the screen. + // left corner. + // Arguments: The new offset. + + void GetMouseOffset(int& mouseOffsetX, int& mouseOffsetY) const { + mouseOffsetX = m_MouseOffsetX; + mouseOffsetY = m_MouseOffsetY; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Input. + // Arguments: None. + + virtual void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetKeyboard + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Copies the keyboard buffer into an array. The keyboard buffer is + // ordered by ascii code and each entry contains a GUInput::Event enum + // state. + // Arguments: Buffer array. + + void GetKeyboard(unsigned char* Buffer) const; + + unsigned char GetAsciiState(unsigned char ascii) const; + + unsigned char GetScanCodeState(unsigned char scancode) const; + + bool GetTextInput(std::string_view& text) const { + text = m_TextInput; + return !m_TextInput.empty(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMouseButtons + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Copies the mouse button states into an array + // Arguments: State array. + + void GetMouseButtons(int* Events, int* States) const; + + static void SetNetworkMouseButton(int whichPlayer, int state1, int state2, int state3); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMousePosition + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the mouse position + // Arguments: Pointers to store the X and Y coordinates in + + void GetMousePosition(int* X, int* Y) const; + + static void SetNetworkMouseMovement(int whichPlayer, int x, int y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetModifier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the key modifiers. + // Arguments: None. + + int GetModifier() const; + + /// + /// This function returns how much the mouse scroll wheel has moved. Positive integer is scroll up, negative is scroll down. + /// + /// Mouse scroll wheel movement in integer value. + int GetMouseWheelChange() const { + return m_MouseWheelChange; + } + + /// + /// Sets whether the keyboard and joysticks also control the mouse. + /// + /// Whether the keyboard and joysticks also control the mouse or not. + void SetKeyJoyMouseCursor(bool enableKeyJoyMouseCursor) { m_KeyJoyMouseCursor = enableKeyJoyMouseCursor; } + + protected: + enum Constants { + KEYBOARD_BUFFER_SIZE = 256 + }; + + // Keyboard buffer holding the key states + unsigned char m_KeyboardBuffer[KEYBOARD_BUFFER_SIZE]; + unsigned char m_ScanCodeState[KEYBOARD_BUFFER_SIZE]; + std::string m_TextInput; + bool m_HasTextInput; + + // Mouse button states + // Order: Left, Middle, Right + int m_MouseButtonsEvents[3]; + int m_MouseButtonsStates[3]; -protected: + static int m_NetworkMouseButtonsEvents[4][3]; + static int m_NetworkMouseButtonsStates[4][3]; + static int m_PrevNetworkMouseButtonsStates[4][3]; - enum Constants - { - KEYBOARD_BUFFER_SIZE = 256 - }; - - // Keyboard buffer holding the key states - unsigned char m_KeyboardBuffer[KEYBOARD_BUFFER_SIZE]; - unsigned char m_ScanCodeState[KEYBOARD_BUFFER_SIZE]; - std::string m_TextInput; - bool m_HasTextInput; - - // Mouse button states - // Order: Left, Middle, Right - int m_MouseButtonsEvents[3]; - int m_MouseButtonsStates[3]; + static bool m_OverrideInput; - static int m_NetworkMouseButtonsEvents[4][3]; - static int m_NetworkMouseButtonsStates[4][3]; - static int m_PrevNetworkMouseButtonsStates[4][3]; + int m_MouseX; + int m_MouseY; + int m_LastFrameMouseX; + int m_LastFrameMouseY; - static bool m_OverrideInput; + static int m_NetworkMouseX[4]; + static int m_NetworkMouseY[4]; - int m_MouseX; - int m_MouseY; - int m_LastFrameMouseX; - int m_LastFrameMouseY; + int m_Player; - static int m_NetworkMouseX[4]; - static int m_NetworkMouseY[4]; + int m_MouseWheelChange; //!< the amount and direction that the mouse wheel has moved. - int m_Player; + // These offset the mouse positions so that the cursor is shifted for all events + int m_MouseOffsetX; + int m_MouseOffsetY; - int m_MouseWheelChange; //!< the amount and direction that the mouse wheel has moved. + int m_Modifier; - // These offset the mouse positions so that the cursor is shifted for all events - int m_MouseOffsetX; - int m_MouseOffsetY; - - int m_Modifier; - - // Whether the keyboard and joysticks also control the mouse - bool m_KeyJoyMouseCursor; -}; -}; + // Whether the keyboard and joysticks also control the mouse + bool m_KeyJoyMouseCursor; + }; +}; // namespace RTE #endif diff --git a/Source/GUI/GUIInterface.h b/Source/GUI/GUIInterface.h index 718b5f5304..e9d10d3ef8 100644 --- a/Source/GUI/GUIInterface.h +++ b/Source/GUI/GUIInterface.h @@ -14,7 +14,6 @@ namespace RTE { class GUIBitmap { public: - #pragma region Creation /// /// Constructor method used to instantiate a GUIBitmap object in system memory. @@ -45,13 +44,13 @@ namespace RTE { /// Gets the underlying BITMAP of this GUIBitmap. /// /// The underlying BITMAP of this GUIBitmap. - virtual BITMAP * GetBitmap() const = 0; + virtual BITMAP* GetBitmap() const = 0; /// /// Sets the underlying BITMAP for this GUIBitmap. /// /// A pointer to the new BITMAP for this GUIBitmap. - virtual void SetBitmap(BITMAP *newBitmap) = 0; + virtual void SetBitmap(BITMAP* newBitmap) = 0; /// /// Gets the width of the bitmap. @@ -104,19 +103,19 @@ namespace RTE { /// Gets the clipping rectangle of the bitmap. /// /// Pointer to a GUIRect to fill out. - virtual void GetClipRect(GUIRect *clippingRect) const = 0; + virtual void GetClipRect(GUIRect* clippingRect) const = 0; /// /// Sets the clipping rectangle of the bitmap. /// /// Pointer to a GUIRect to use as the clipping rectangle, or nullptr for no clipping. - virtual void SetClipRect(GUIRect *clippingRect) = 0; + virtual void SetClipRect(GUIRect* clippingRect) = 0; /// - /// Sets the clipping rectangle of the specified bitmap as the intersection of its current clipping rectangle and the rectangle described by the passed-in GUIRect. + /// Sets the clipping rectangle of the specified bitmap as the intersection of its current clipping rectangle and the rectangle described by the passed-in GUIRect. /// /// Rectangle pointer. - virtual void AddClipRect(GUIRect *rect) = 0; + virtual void AddClipRect(GUIRect* rect) = 0; #pragma endregion #pragma region Drawing @@ -127,7 +126,7 @@ namespace RTE { /// Destination X position. /// Destination Y position. /// Source bitmap position and size rectangle. - virtual void Draw(GUIBitmap *destBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) = 0; + virtual void Draw(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) = 0; /// /// Draw a section of this bitmap onto another bitmap ignoring color-keyed pixels. @@ -136,7 +135,7 @@ namespace RTE { /// Destination X position. /// Destination Y position. /// Source bitmap position and size rectangle. - virtual void DrawTrans(GUIBitmap *destBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) = 0; + virtual void DrawTrans(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) = 0; /// /// Draw this bitmap scaled onto another bitmap ignoring color-keyed pixels. @@ -146,7 +145,7 @@ namespace RTE { /// Destination Y position. /// Target width of the bitmap. /// Target height of the bitmap. - virtual void DrawTransScaled(GUIBitmap *destBitmap, int destX, int destY, int width, int height) = 0; + virtual void DrawTransScaled(GUIBitmap* destBitmap, int destX, int destY, int width, int height) = 0; #pragma endregion #pragma region Primitive Drawing @@ -173,7 +172,7 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - GUIBitmap & operator=(const GUIBitmap &rhs) = delete; + GUIBitmap& operator=(const GUIBitmap& rhs) = delete; }; #pragma endregion @@ -184,7 +183,6 @@ namespace RTE { class GUIScreen { public: - #pragma region Creation /// /// Constructor method used to instantiate a GUIScreen object in system memory. @@ -196,7 +194,7 @@ namespace RTE { /// /// File name to create bitmap from. /// Pointer to the created bitmap. - virtual GUIBitmap * CreateBitmap(const std::string &fileName) = 0; + virtual GUIBitmap* CreateBitmap(const std::string& fileName) = 0; /// /// Creates an empty bitmap. @@ -204,7 +202,7 @@ namespace RTE { /// Bitmap width. /// Bitmap height. /// Pointer to the created bitmap. - virtual GUIBitmap * CreateBitmap(int width, int height) = 0; + virtual GUIBitmap* CreateBitmap(int width, int height) = 0; #pragma endregion #pragma region Destruction @@ -224,7 +222,7 @@ namespace RTE { /// Gets the bitmap representing the screen. /// /// Pointer to the bitmap representing the screen. - virtual GUIBitmap * GetBitmap() const = 0; + virtual GUIBitmap* GetBitmap() const = 0; #pragma endregion #pragma region Pure Virtual Methods @@ -235,7 +233,7 @@ namespace RTE { /// Destination X position /// Destination Y position /// Source bitmap position and size rectangle. - virtual void DrawBitmap(GUIBitmap *guiBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) = 0; + virtual void DrawBitmap(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) = 0; /// /// Draws a bitmap onto the back buffer ignoring color-keyed pixels. @@ -244,7 +242,7 @@ namespace RTE { /// Destination X position /// Destination Y position /// Source bitmap position and size rectangle. - virtual void DrawBitmapTrans(GUIBitmap *guiBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) = 0; + virtual void DrawBitmapTrans(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) = 0; /// /// Converts an 8bit palette index to a valid pixel format color. @@ -256,8 +254,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - GUIScreen & operator=(const GUIScreen &rhs) = delete; + GUIScreen& operator=(const GUIScreen& rhs) = delete; }; #pragma endregion -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUILabel.cpp b/Source/GUI/GUILabel.cpp index 72ff036418..96e9b8e6c5 100644 --- a/Source/GUI/GUILabel.cpp +++ b/Source/GUI/GUILabel.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUILabel::GUILabel(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUILabel::GUILabel(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "LABEL"; m_ControlManager = ControlManager; m_Font = nullptr; @@ -21,7 +22,7 @@ GUILabel::GUILabel(GUIManager *Manager, GUIControlManager *ControlManager) : GUI ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUILabel::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUILabel::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -38,8 +39,12 @@ void GUILabel::Create(const std::string &Name, int X, int Y, int Width, int Heig m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Make sure the label isn't too small m_Width = std::max(m_Width, m_MinWidth); @@ -48,7 +53,7 @@ void GUILabel::Create(const std::string &Name, int X, int Y, int Width, int Heig ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUILabel::Create(GUIProperties *Props) { +void GUILabel::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -71,14 +76,26 @@ void GUILabel::Create(GUIProperties *Props) { std::string alignString; Props->GetValue("HAlignment", &alignString); - if (stricmp(alignString.c_str(), "left") == 0) { m_HAlignment = GUIFont::Left; } - if (stricmp(alignString.c_str(), "centre") == 0 || stricmp(alignString.c_str(), "center") == 0) { m_HAlignment = GUIFont::Centre; } - if (stricmp(alignString.c_str(), "right") == 0) { m_HAlignment = GUIFont::Right; } + if (stricmp(alignString.c_str(), "left") == 0) { + m_HAlignment = GUIFont::Left; + } + if (stricmp(alignString.c_str(), "centre") == 0 || stricmp(alignString.c_str(), "center") == 0) { + m_HAlignment = GUIFont::Centre; + } + if (stricmp(alignString.c_str(), "right") == 0) { + m_HAlignment = GUIFont::Right; + } Props->GetValue("VAlignment", &alignString); - if (stricmp(alignString.c_str(), "top") == 0) { m_VAlignment = GUIFont::Top; } - if (stricmp(alignString.c_str(), "middle") == 0) { m_VAlignment = GUIFont::Middle; } - if (stricmp(alignString.c_str(), "bottom") == 0) { m_VAlignment = GUIFont::Bottom; } + if (stricmp(alignString.c_str(), "top") == 0) { + m_VAlignment = GUIFont::Top; + } + if (stricmp(alignString.c_str(), "middle") == 0) { + m_VAlignment = GUIFont::Middle; + } + if (stricmp(alignString.c_str(), "bottom") == 0) { + m_VAlignment = GUIFont::Bottom; + } Props->GetValue("HorizontalOverflowScroll", &m_HorizontalOverflowScroll); Props->GetValue("VerticalOverflowScroll", &m_VerticalOverflowScroll); @@ -86,7 +103,7 @@ void GUILabel::Create(GUIProperties *Props) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUILabel::ChangeSkin(GUISkin *Skin) { +void GUILabel::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Load the font @@ -103,14 +120,14 @@ void GUILabel::ChangeSkin(GUISkin *Skin) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUILabel::Draw(GUIScreen *Screen) { +void GUILabel::Draw(GUIScreen* Screen) { Draw(Screen->GetBitmap()); GUIPanel::Draw(Screen); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUILabel::Draw(GUIBitmap *Bitmap, bool overwiteFontColorAndKerning) { +void GUILabel::Draw(GUIBitmap* Bitmap, bool overwiteFontColorAndKerning) { // Setup the clipping Bitmap->AddClipRect(GetRect()); @@ -156,8 +173,8 @@ void GUILabel::Draw(GUIBitmap *Bitmap, bool overwiteFontColorAndKerning) { break; case OverflowScrollState::Scrolling: if (m_OverflowScrollTimer.GetRealTimeLimitMS() == -1) { - //TODO Maybe time limits should account for extra size vs width, so it scrolls slower on small labels, since it can be harder to read fast text on smaller areas. I think it's fine as-is though. - // Note - time limits set so 5 characters of fatfont horizontal overflow or one line of fatfont vertical overflow will take 1 second. + // TODO Maybe time limits should account for extra size vs width, so it scrolls slower on small labels, since it can be harder to read fast text on smaller areas. I think it's fine as-is though. + // Note - time limits set so 5 characters of fatfont horizontal overflow or one line of fatfont vertical overflow will take 1 second. if (modifyXPos) { m_OverflowScrollTimer.SetRealTimeLimitMS((1000.0 / 30.0) * static_cast(textFullWidth - m_Width)); } else if (modifyYPos) { @@ -208,14 +225,16 @@ void GUILabel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { void GUILabel::OnMouseUp(int X, int Y, int Buttons, int Modifier) { // If the mouse is over the button, add the clicked notification to the event queue - if (PointInside(X, Y) && (Buttons & MOUSE_LEFT) && IsCaptured()) { AddEvent(GUIEvent::Notification, Clicked, Buttons); } + if (PointInside(X, Y) && (Buttons & MOUSE_LEFT) && IsCaptured()) { + AddEvent(GUIEvent::Notification, Clicked, Buttons); + } ReleaseMouse(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUILabel::GetPanel() { +GUIPanel* GUILabel::GetPanel() { return this; } @@ -246,7 +265,7 @@ int GUILabel::ResizeHeightToFit() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUILabel::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUILabel::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } @@ -312,21 +331,33 @@ void GUILabel::StoreProperties() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUILabel::ApplyProperties(GUIProperties *Props) { +void GUILabel::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); m_Properties.GetValue("Text", &m_Text); std::string alignString; m_Properties.GetValue("HAlignment", &alignString); - if (stricmp(alignString.c_str(), "left") == 0) { m_HAlignment = GUIFont::Left; } - if (stricmp(alignString.c_str(), "centre") == 0) { m_HAlignment = GUIFont::Centre; } - if (stricmp(alignString.c_str(), "right") == 0) { m_HAlignment = GUIFont::Right; } + if (stricmp(alignString.c_str(), "left") == 0) { + m_HAlignment = GUIFont::Left; + } + if (stricmp(alignString.c_str(), "centre") == 0) { + m_HAlignment = GUIFont::Centre; + } + if (stricmp(alignString.c_str(), "right") == 0) { + m_HAlignment = GUIFont::Right; + } m_Properties.GetValue("VAlignment", &alignString); - if (stricmp(alignString.c_str(), "top") == 0) { m_VAlignment = GUIFont::Top; } - if (stricmp(alignString.c_str(), "middle") == 0) { m_VAlignment = GUIFont::Middle; } - if (stricmp(alignString.c_str(), "bottom") == 0) { m_VAlignment = GUIFont::Bottom; } + if (stricmp(alignString.c_str(), "top") == 0) { + m_VAlignment = GUIFont::Top; + } + if (stricmp(alignString.c_str(), "middle") == 0) { + m_VAlignment = GUIFont::Middle; + } + if (stricmp(alignString.c_str(), "bottom") == 0) { + m_VAlignment = GUIFont::Bottom; + } m_Properties.GetValue("HorizontalOverflowScroll", &m_HorizontalOverflowScroll); m_Properties.GetValue("VerticalOverflowScroll", &m_VerticalOverflowScroll); diff --git a/Source/GUI/GUILabel.h b/Source/GUI/GUILabel.h index d6d7c1dddb..98febc49fb 100644 --- a/Source/GUI/GUILabel.h +++ b/Source/GUI/GUILabel.h @@ -7,286 +7,262 @@ namespace RTE { -/// -/// A label control class. -/// -class GUILabel : public GUIControl, public GUIPanel { - -public: - - // Label Notifications - enum { - Clicked = 0, - } Notification; - - enum class OverflowScrollState { - Deactivated = 0, - WaitAtStart, - Scrolling, - WaitAtEnd - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUILabel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUILabel object in -// system memory. -// Arguments: GUIManager, GUIControlManager. - - GUILabel(GUIManager *Manager, GUIControlManager *ControlManager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. - - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - /// - /// Draws the Label to the given GUIBitmap. - /// - /// The GUIBitmap to draw the label to. - /// Whether to overwrite the font's color and kerning with the stored values. Defaults to true, which is usually what you want. - void Draw(GUIBitmap *Bitmap, bool overwiteFontColorAndKerning = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "LABEL"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResizeHeightToFit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resize the height of the label to fit the amount of text it has to -// display. -// Arguments: None. -// Returns: The new height in pixels. - - int ResizeHeightToFit(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the text of the label. -// Arguments: text. - - void SetText(const std::string_view &text) { m_Text = text; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the text of the label. -// Arguments: None. - - const std::string & GetText() const { return m_Text; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTextHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows how tall the current text is with the current width and font etc. -// Arguments: None. -// Returns: The text height, in pixels - - int GetTextHeight(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetHAlignment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the horizontal alignment of the text of this label. -// Arguments: The desired alignment. - - void SetHAlignment(int HAlignment = GUIFont::Left) { m_HAlignment = HAlignment; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetVAlignment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the vertical alignment of the text of this label. -// Arguments: The desired alignment. - - void SetVAlignment(int VAlignment = GUIFont::Top) { m_VAlignment = VAlignment; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetHAlignment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the horizontal alignment of the text of this label. -// Arguments: None. - - int GetHAlignment() const { return m_HAlignment; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetVAlignment -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the vertical alignment of the text of this label. -// Arguments: The desired alignment. - - int GetVAlignment() const { return m_VAlignment; } - - /// - /// Gets whether or not this GUILabel should scroll horizontally (right) when it overflows. - /// - /// Whether or not this GUILabel should scroll horizontally when it overflows. - bool GetHorizontalOverflowScroll() const { return m_HorizontalOverflowScroll; } - - /// - /// Sets whether or not this GUILabel should scroll horizontally (right) when it overflows. Mutually exclusive with horizontal overflow scrolling. - /// - /// Whether or not this GUILabel should scroll horizontally when it overflows. - void SetHorizontalOverflowScroll(bool newOverflowScroll); - - /// - /// Gets whether or not this GUILabel should scroll vertically (down) when it overflows. - /// - /// Whether or not this GUILabel should scroll vertically when it overflows. - bool GetVerticalOverflowScroll() const { return m_VerticalOverflowScroll; } - - /// - /// Sets whether or not this GUILabel should scroll vertically (down) when it overflows. Mutually exclusive with horizontal overflow scrolling. - /// - /// Whether or not this GUILabel should scroll vertically when it overflows. - void SetVerticalOverflowScroll(bool newOverflowScroll); - - /// - /// Gets whether or not horizontal or vertical overflow scrolling is turned on. - /// - /// Whether or not horizontal or vertical overflow scrolling is turned on. - bool OverflowScrollIsEnabled() const { return m_HorizontalOverflowScroll || m_VerticalOverflowScroll; } - - /// - /// Gets whether or not horizontal/vertical scrolling is happening. - /// - /// Whether or not horizontal/vertical scrolling is happening. - bool OverflowScrollIsActivated() const { return OverflowScrollIsEnabled() && m_OverflowScrollState != OverflowScrollState::Deactivated; } - - /// - /// Sets whether or not horizontal/vertical scrolling should be happening. When it's deactivated, text will instantly go back to un-scrolled. - /// - /// Whether the overflow scrolling should activate (true) or deactivate (false). - void ActivateDeactivateOverflowScroll(bool activateScroll); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - -private: - - std::string m_Text; - int m_HAlignment; - int m_VAlignment; - bool m_HorizontalOverflowScroll; //!< Note that horizontal overflow scrolling means text will always be on one line. - bool m_VerticalOverflowScroll; - OverflowScrollState m_OverflowScrollState; - Timer m_OverflowScrollTimer; -}; -}; + /// + /// A label control class. + /// + class GUILabel : public GUIControl, public GUIPanel { + + public: + // Label Notifications + enum { + Clicked = 0, + } Notification; + + enum class OverflowScrollState { + Deactivated = 0, + WaitAtStart, + Scrolling, + WaitAtEnd + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUILabel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUILabel object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUILabel(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + /// + /// Draws the Label to the given GUIBitmap. + /// + /// The GUIBitmap to draw the label to. + /// Whether to overwrite the font's color and kerning with the stored values. Defaults to true, which is usually what you want. + void Draw(GUIBitmap* Bitmap, bool overwiteFontColorAndKerning = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "LABEL"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ResizeHeightToFit + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resize the height of the label to fit the amount of text it has to + // display. + // Arguments: None. + // Returns: The new height in pixels. + + int ResizeHeightToFit(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the text of the label. + // Arguments: text. + + void SetText(const std::string_view& text) { m_Text = text; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the text of the label. + // Arguments: None. + + const std::string& GetText() const { return m_Text; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTextHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows how tall the current text is with the current width and font etc. + // Arguments: None. + // Returns: The text height, in pixels + + int GetTextHeight(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetHAlignment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the horizontal alignment of the text of this label. + // Arguments: The desired alignment. + + void SetHAlignment(int HAlignment = GUIFont::Left) { m_HAlignment = HAlignment; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetVAlignment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the vertical alignment of the text of this label. + // Arguments: The desired alignment. + + void SetVAlignment(int VAlignment = GUIFont::Top) { m_VAlignment = VAlignment; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetHAlignment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the horizontal alignment of the text of this label. + // Arguments: None. + + int GetHAlignment() const { return m_HAlignment; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetVAlignment + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the vertical alignment of the text of this label. + // Arguments: The desired alignment. + + int GetVAlignment() const { return m_VAlignment; } + + /// + /// Gets whether or not this GUILabel should scroll horizontally (right) when it overflows. + /// + /// Whether or not this GUILabel should scroll horizontally when it overflows. + bool GetHorizontalOverflowScroll() const { return m_HorizontalOverflowScroll; } + + /// + /// Sets whether or not this GUILabel should scroll horizontally (right) when it overflows. Mutually exclusive with horizontal overflow scrolling. + /// + /// Whether or not this GUILabel should scroll horizontally when it overflows. + void SetHorizontalOverflowScroll(bool newOverflowScroll); + + /// + /// Gets whether or not this GUILabel should scroll vertically (down) when it overflows. + /// + /// Whether or not this GUILabel should scroll vertically when it overflows. + bool GetVerticalOverflowScroll() const { return m_VerticalOverflowScroll; } + + /// + /// Sets whether or not this GUILabel should scroll vertically (down) when it overflows. Mutually exclusive with horizontal overflow scrolling. + /// + /// Whether or not this GUILabel should scroll vertically when it overflows. + void SetVerticalOverflowScroll(bool newOverflowScroll); + + /// + /// Gets whether or not horizontal or vertical overflow scrolling is turned on. + /// + /// Whether or not horizontal or vertical overflow scrolling is turned on. + bool OverflowScrollIsEnabled() const { return m_HorizontalOverflowScroll || m_VerticalOverflowScroll; } + + /// + /// Gets whether or not horizontal/vertical scrolling is happening. + /// + /// Whether or not horizontal/vertical scrolling is happening. + bool OverflowScrollIsActivated() const { return OverflowScrollIsEnabled() && m_OverflowScrollState != OverflowScrollState::Deactivated; } + + /// + /// Sets whether or not horizontal/vertical scrolling should be happening. When it's deactivated, text will instantly go back to un-scrolled. + /// + /// Whether the overflow scrolling should activate (true) or deactivate (false). + void ActivateDeactivateOverflowScroll(bool activateScroll); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; + + private: + std::string m_Text; + int m_HAlignment; + int m_VAlignment; + bool m_HorizontalOverflowScroll; //!< Note that horizontal overflow scrolling means text will always be on one line. + bool m_VerticalOverflowScroll; + OverflowScrollState m_OverflowScrollState; + Timer m_OverflowScrollTimer; + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIListBox.cpp b/Source/GUI/GUIListBox.cpp index 5ac9c58701..a95819ff64 100644 --- a/Source/GUI/GUIListBox.cpp +++ b/Source/GUI/GUIListBox.cpp @@ -5,14 +5,15 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIListBox::GUIListBox(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIListPanel(Manager) { +GUIListBox::GUIListBox(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIListPanel(Manager) { m_ControlID = "LISTBOX"; m_ControlManager = ControlManager; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListBox::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUIListBox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -26,8 +27,12 @@ void GUIListBox::Create(const std::string &Name, int X, int Y, int Width, int He // Create the ListPanel int w = m_DefWidth; int h = m_DefHeight; - if (Width != -1) { w = Width; } - if (Height != -1) { h = Height; } + if (Width != -1) { + w = Width; + } + if (Height != -1) { + h = Height; + } // Make sure the control isn't too small w = std::max(w, m_MinWidth); @@ -38,7 +43,7 @@ void GUIListBox::Create(const std::string &Name, int X, int Y, int Width, int He ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListBox::Create(GUIProperties *Props) { +void GUIListBox::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -72,7 +77,7 @@ void GUIListBox::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListBox::ChangeSkin(GUISkin *Skin) { +void GUIListBox::ChangeSkin(GUISkin* Skin) { GUIListPanel::ChangeSkin(Skin); } @@ -94,13 +99,13 @@ void GUIListBox::Resize(int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIListBox::GetPanel() { +GUIPanel* GUIListBox::GetPanel() { return this; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListBox::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUIListBox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIListPanel::GetRect(X, Y, Width, Height); } @@ -112,7 +117,7 @@ void GUIListBox::StoreProperties() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListBox::ReceiveSignal(GUIPanel *Source, int Code, int Data) { +void GUIListBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { if (Source->GetPanelID() == GetPanelID()) { if (Code == GUIListPanel::MouseMove) { AddEvent(GUIEvent::Notification, MouseMove, Data); @@ -140,7 +145,7 @@ void GUIListBox::ReceiveSignal(GUIPanel *Source, int Code, int Data) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListBox::ApplyProperties(GUIProperties *Props) { +void GUIListBox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); bool Multi = false; diff --git a/Source/GUI/GUIListBox.h b/Source/GUI/GUIListBox.h index 4b496d551b..a89f4dbe5b 100644 --- a/Source/GUI/GUIListBox.h +++ b/Source/GUI/GUIListBox.h @@ -5,133 +5,119 @@ namespace RTE { -/// -/// A ListBox control class. -/// -class GUIListBox : public GUIControl, public GUIListPanel { + /// + /// A ListBox control class. + /// + class GUIListBox : public GUIControl, public GUIListPanel { + + public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIListBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIListBox object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUIListBox(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "LISTBOX"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReceiveSignal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when receiving a signal. + // Arguments: Signal source, Signal code, Signal data. + + void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. -public: + void ApplyProperties(GUIProperties* Props) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIListBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIListBox object in -// system memory. -// Arguments: GUIManager, GUIControlManager. - - GUIListBox(GUIManager *Manager, GUIControlManager *ControlManager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. - - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. - - void Destroy() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "LISTBOX"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReceiveSignal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when receiving a signal. -// Arguments: Signal source, Signal code, Signal data. - - void ReceiveSignal(GUIPanel *Source, int Code, int Data) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - -private: - -}; -}; + private: + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIListPanel.cpp b/Source/GUI/GUIListPanel.cpp index ed9b0fb66c..af891151cc 100644 --- a/Source/GUI/GUIListPanel.cpp +++ b/Source/GUI/GUIListPanel.cpp @@ -7,7 +7,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIListPanel::GUIListPanel(GUIManager *Manager) : GUIPanel(Manager) { +GUIListPanel::GUIListPanel(GUIManager* Manager) : + GUIPanel(Manager) { m_BaseBitmap = nullptr; m_DrawBitmap = nullptr; m_FrameBitmap = nullptr; @@ -37,7 +38,8 @@ GUIListPanel::GUIListPanel(GUIManager *Manager) : GUIPanel(Manager) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIListPanel::GUIListPanel() : GUIPanel() { +GUIListPanel::GUIListPanel() : + GUIPanel() { m_BaseBitmap = nullptr; m_DrawBitmap = nullptr; m_FrameBitmap = nullptr; @@ -100,15 +102,16 @@ void GUIListPanel::Create(int X, int Y, int Width, int Height) { void GUIListPanel::Destroy() { // Destroy the items - std::vector::iterator it; + std::vector::iterator it; for (it = m_Items.begin(); it != m_Items.end(); it++) { - Item *I = *it; - if (I) { delete I; } + Item* I = *it; + if (I) { + delete I; + } } m_Items.clear(); - // Destroy the horizontal scroll panel if (m_HorzScroll) { m_HorzScroll->Destroy(); @@ -149,11 +152,13 @@ void GUIListPanel::Destroy() { void GUIListPanel::ClearList() { // Destroy the items - std::vector::iterator it; + std::vector::iterator it; for (it = m_Items.begin(); it != m_Items.end(); it++) { - Item *I = *it; - if (I) { delete I; } + Item* I = *it; + if (I) { + delete I; + } } m_Items.clear(); @@ -169,8 +174,8 @@ void GUIListPanel::ClearList() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListPanel::AddItem(const std::string &Name, const std::string &rightText, GUIBitmap *pBitmap, const Entity *pEntity, const int extraIndex, const int offsetX) { - Item *I = new Item; +void GUIListPanel::AddItem(const std::string& Name, const std::string& rightText, GUIBitmap* pBitmap, const Entity* pEntity, const int extraIndex, const int offsetX) { + Item* I = new Item; I->m_Name = Name; I->m_RightText = rightText; I->m_ExtraIndex = extraIndex; @@ -198,7 +203,7 @@ void GUIListPanel::AddItem(const std::string &Name, const std::string &rightText ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListPanel::ChangeSkin(GUISkin *Skin) { +void GUIListPanel::ChangeSkin(GUISkin* Skin) { assert(Skin); m_Skin = Skin; @@ -290,10 +295,12 @@ void GUIListPanel::BuildBitmap(bool UpdateBase, bool UpdateText) { void GUIListPanel::BuildDrawBitmap() { // Draw the items - std::vector::iterator it; + std::vector::iterator it; int Count = 0; int Height = m_Height; - if (m_HorzScroll->_GetVisible()) { Height -= m_HorzScroll->GetHeight(); } + if (m_HorzScroll->_GetVisible()) { + Height -= m_HorzScroll->GetHeight(); + } int x = m_HorzScroll->GetValue(); int y = 1 + (m_VertScroll->_GetVisible() ? -m_VertScroll->GetValue() : 0); @@ -308,7 +315,7 @@ void GUIListPanel::BuildDrawBitmap() { continue; } - Item *I = *it; + Item* I = *it; int itemX = x - I->m_OffsetX; int itemY = y; @@ -319,7 +326,7 @@ void GUIListPanel::BuildDrawBitmap() { // Alternate drawing mode // TODO: REMOVE MORE H-CODING if (m_AlternateDrawMode) { - int rightTextWidth = I->m_RightText.empty() ? 0 : RIGHTTEXTWIDTH;//thirdWidth / 2; + int rightTextWidth = I->m_RightText.empty() ? 0 : RIGHTTEXTWIDTH; // thirdWidth / 2; int mainTextWidth = ((thirdWidth * 2) - rightTextWidth) - I->m_OffsetX; int bitmapWidth = I->m_pBitmap ? I->m_pBitmap->GetWidth() : 0; int bitmapHeight = I->m_pBitmap ? I->m_pBitmap->GetHeight() : 0; @@ -366,7 +373,9 @@ void GUIListPanel::BuildDrawBitmap() { } // Draw another line to make sure the last item has two - if (it == m_Items.end() - 1) { m_DrawBitmap->DrawLine(4, itemY + itemHeight + 1, m_Width - 5, itemY + itemHeight + 1, m_UnselectedColorIndex); } + if (it == m_Items.end() - 1) { + m_DrawBitmap->DrawLine(4, itemY + itemHeight + 1, m_Width - 5, itemY + itemHeight + 1, m_UnselectedColorIndex); + } // Save the item height for later use in selection routines etc I->m_Height = itemHeight; @@ -403,7 +412,7 @@ void GUIListPanel::BuildDrawBitmap() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListPanel::Draw(GUIScreen *Screen) { +void GUIListPanel::Draw(GUIScreen* Screen) { // Draw the base m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, nullptr); @@ -446,7 +455,7 @@ void GUIListPanel::OnMouseWheelChange(int x, int y, int modifier, int mouseWheel } else if ((PointInsideList(x, y) && !m_MultiSelect) || (m_VertScroll->_GetVisible() && m_VertScroll->PointInside(x, y))) { ScrollBarScrolling(mouseWheelChange); } - + if (m_HotTracking && GetItem(x, y) != nullptr && (GetItem(x, y) != GetSelected())) { SelectItem(x, y, modifier); } @@ -455,7 +464,7 @@ void GUIListPanel::OnMouseWheelChange(int x, int y, int modifier, int mouseWheel ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIListPanel::SelectItem(int X, int Y, int Modifier) { - std::vector::iterator it; + std::vector::iterator it; bool Shift = Modifier & MODI_SHIFT; bool Ctrl = Modifier & MODI_CTRL; @@ -472,34 +481,36 @@ void GUIListPanel::SelectItem(int X, int Y, int Modifier) { // Clear the selected items if (ClearSelected) { for (it = m_Items.begin(); it != m_Items.end(); it++) { - Item *I = *it; + Item* I = *it; I->m_Selected = false; } m_SelectedList.clear(); } int Height = m_Height; - if (m_HorzScroll->_GetVisible()) { Height -= m_HorzScroll->GetHeight(); } + if (m_HorzScroll->_GetVisible()) { + Height -= m_HorzScroll->GetHeight(); + } int y = m_Y + 1; if (m_VertScroll->_GetVisible()) y -= m_VertScroll->GetValue(); int Count = 0; - //int stackHeight = 0; + // int stackHeight = 0; for (it = m_Items.begin(); it != m_Items.end(); it++, Count++) { /* stackHeight += GetItemHeight(*it); // Only check the items after the scroll value if (m_VertScroll->_GetVisible() && m_VertScroll->GetValue() > y) { - y += GetItemHeight(*it); - continue; + y += GetItemHeight(*it); + continue; } if (Count < m_VertScroll->GetValue()) {continue;} */ - Item *I = *it; + Item* I = *it; // Select/Deselect the item under the mouse - //if (Y >= y && Y < y+m_Font->GetFontHeight()) { + // if (Y >= y && Y < y+m_Font->GetFontHeight()) { if (Y >= y && Y < y + GetItemHeight(I)) { // If CTRL is down and not shift, AND the item is already selected // Unselect it @@ -523,7 +534,9 @@ void GUIListPanel::SelectItem(int X, int Y, int Modifier) { I->m_Selected = true; m_SelectedList.push_back(I); SendSignal(Select, 0); - if (!Shift) { m_LastSelected = Count; } + if (!Shift) { + m_LastSelected = Count; + } } // Shift key down if (Shift) { @@ -534,19 +547,19 @@ void GUIListPanel::SelectItem(int X, int Y, int Modifier) { m_LastSelected = Count; } else { // Select a list of items - std::vector::iterator sel; + std::vector::iterator sel; int Num = 0; for (sel = m_Items.begin(); sel != m_Items.end(); sel++, Num++) { if (m_LastSelected <= Count) { if (Num >= m_LastSelected && Num <= Count) { - Item *SelItem = *sel; + Item* SelItem = *sel; SelItem->m_Selected = true; m_SelectedList.push_back(SelItem); SendSignal(Select, 0); } } else { if (Num >= Count && Num <= m_LastSelected) { - Item *SelItem = *sel; + Item* SelItem = *sel; SelItem->m_Selected = true; m_SelectedList.push_back(SelItem); SendSignal(Select, 0); @@ -558,7 +571,7 @@ void GUIListPanel::SelectItem(int X, int Y, int Modifier) { } break; } - //y += m_Font->GetFontHeight(); + // y += m_Font->GetFontHeight(); y += GetItemHeight(I); // End of viewable region @@ -578,8 +591,8 @@ void GUIListPanel::OnMouseUp(int X, int Y, int Buttons, int Modifier) { m_HorzScroll->OnMouseUp(X, Y, Buttons, Modifier); } else { if (PointInside(X, Y)) { - //SendSignal(Select, 0); - //} else { + // SendSignal(Select, 0); + //} else { SendSignal(MouseUp, Buttons); } } @@ -594,7 +607,9 @@ void GUIListPanel::OnMouseMove(int X, int Y, int Buttons, int Modifier) { m_HorzScroll->OnMouseMove(X, Y, Buttons, Modifier); } else if (PointInsideList(X, Y)) { // Using Hot-Tracking - if (m_HotTracking && GetItem(X, Y) != nullptr && (GetItem(X, Y) != GetSelected())) { SelectItem(X, Y, Modifier); } + if (m_HotTracking && GetItem(X, Y) != nullptr && (GetItem(X, Y) != GetSelected())) { + SelectItem(X, Y, Modifier); + } SendSignal(MouseMove, Buttons); } } @@ -614,7 +629,9 @@ void GUIListPanel::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIListPanel::OnDoubleClick(int X, int Y, int Buttons, int Modifier) { - if (PointInside(X, Y)) { SendSignal(DoubleClick, Buttons); } + if (PointInside(X, Y)) { + SendSignal(DoubleClick, Buttons); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -634,13 +651,17 @@ void GUIListPanel::EndUpdate() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListPanel::ScrollToItem(Item *pItem) { +void GUIListPanel::ScrollToItem(Item* pItem) { if (pItem && m_VertScroll->_GetVisible()) { int stackHeight = GetStackHeight(pItem); int itemHeight = GetItemHeight(pItem); // Adjust the vertical scroll bar to show the specified item - if (stackHeight < m_VertScroll->GetValue()) { m_VertScroll->SetValue(stackHeight); } - if (stackHeight + itemHeight > m_VertScroll->GetValue() + m_VertScroll->GetPageSize()) { m_VertScroll->SetValue(stackHeight + itemHeight - m_VertScroll->GetPageSize()); } + if (stackHeight < m_VertScroll->GetValue()) { + m_VertScroll->SetValue(stackHeight); + } + if (stackHeight + itemHeight > m_VertScroll->GetValue() + m_VertScroll->GetPageSize()) { + m_VertScroll->SetValue(stackHeight + itemHeight - m_VertScroll->GetPageSize()); + } } BuildBitmap(false, true); } @@ -649,7 +670,9 @@ void GUIListPanel::ScrollToItem(Item *pItem) { void GUIListPanel::ScrollUp() { const int sensitivity = 80; - if (m_VertScroll->_GetVisible()) { m_VertScroll->SetValue(m_VertScroll->GetValue() - sensitivity); } + if (m_VertScroll->_GetVisible()) { + m_VertScroll->SetValue(m_VertScroll->GetValue() - sensitivity); + } BuildBitmap(false, true); } @@ -660,7 +683,7 @@ void GUIListPanel::ScrollDown() { if (m_VertScroll->_GetVisible()) { int scrollValueToAdd = sensitivity; if (!m_Items.empty()) { - RTE::GUIListPanel::Item *item = m_Items.back(); + RTE::GUIListPanel::Item* item = m_Items.back(); int maximumScrollDistance = GetStackHeight(item) + GetItemHeight(item) - (m_VertScroll->GetPageSize() + m_VertScroll->GetValue()); scrollValueToAdd = std::clamp(maximumScrollDistance, 0, scrollValueToAdd); } @@ -675,11 +698,13 @@ void GUIListPanel::ScrollTo(int position) { if (m_VertScroll->_GetVisible()) { m_VertScroll->SetValue(position); - //TODO this was copied from MaxShadow's work. I'm not quite sure of the point of it tbh. + // TODO this was copied from MaxShadow's work. I'm not quite sure of the point of it tbh. if (!m_Items.empty()) { - RTE::GUIListPanel::Item *item = m_Items.back(); + RTE::GUIListPanel::Item* item = m_Items.back(); int allItemsHeight = GetStackHeight(item) + GetItemHeight(item); - if (position + m_VertScroll->GetPageSize() > allItemsHeight) { m_VertScroll->SetValue(allItemsHeight - m_VertScroll->GetPageSize()); } + if (position + m_VertScroll->GetPageSize() > allItemsHeight) { + m_VertScroll->SetValue(allItemsHeight - m_VertScroll->GetPageSize()); + } } } BuildBitmap(false, true); @@ -781,8 +806,12 @@ void GUIListPanel::AdjustScrollbars() { if (!m_Font) { return; } - if (!m_HorzScrollEnabled) { m_HorzScroll->_SetVisible(false); } - if (!m_VertScrollEnabled) { m_VertScroll->_SetVisible(false); } + if (!m_HorzScrollEnabled) { + m_HorzScroll->_SetVisible(false); + } + if (!m_VertScrollEnabled) { + m_VertScroll->_SetVisible(false); + } // Re-adjust the scrollbar positions & sizes, just to be safe m_HorzScroll->SetPositionAbs(m_X + m_ScrollBarPadding, m_Y + m_Height - m_ScrollBarThickness - m_ScrollBarPadding); @@ -792,14 +821,16 @@ void GUIListPanel::AdjustScrollbars() { // If there are items wider than the listpanel, make the horizontal scrollpanel visible int Width = m_Width - 4; - if (m_VertScroll->_GetVisible()) { Width -= m_VertScroll->GetWidth(); } + if (m_VertScroll->_GetVisible()) { + Width -= m_VertScroll->GetWidth(); + } m_HorzScroll->_SetVisible(false); if (m_LargestWidth > Width && m_HorzScrollEnabled) { m_HorzScroll->_SetVisible(true); m_HorzScroll->SetMaximum(m_LargestWidth); m_HorzScroll->SetMinimum(0); m_HorzScroll->SetPageSize(Width); - m_HorzScroll->SetSmallChange(m_Font->CalculateWidth("W")); // W is one of the largest characters + m_HorzScroll->SetSmallChange(m_Font->CalculateWidth("W")); // W is one of the largest characters } if (m_Items.size() > 0) { @@ -809,9 +840,9 @@ void GUIListPanel::AdjustScrollbars() { int Height = m_Height - (m_HorzScroll->_GetVisible() ? m_HorzScroll->GetHeight() : 0); // TODO: remove the page subtraciton? - // Subtract the frame size + // Subtract the frame size int Page = Height - 4; - int Max = itemStackHeight/* - Page*/; + int Max = itemStackHeight /* - Page*/; Max = std::max(Max, 0); // Setup the vertical scrollbar @@ -820,7 +851,7 @@ void GUIListPanel::AdjustScrollbars() { m_VertScroll->SetMinimum(0); // m_VertScroll->SetSmallChange(itemHeight); - // If there is too many items, make the scrollbar visible + // If there is too many items, make the scrollbar visible m_VertScroll->_SetVisible(false); if (itemStackHeight > Height && m_VertScrollEnabled) { m_VertScroll->_SetVisible(true); @@ -834,7 +865,7 @@ void GUIListPanel::AdjustScrollbars() { m_HorzScroll->SetMaximum(m_LargestWidth); m_HorzScroll->SetMinimum(0); m_HorzScroll->SetPageSize(Width); - m_HorzScroll->SetSmallChange(m_Font->CalculateWidth("W")); // W is one of the largest characters + m_HorzScroll->SetSmallChange(m_Font->CalculateWidth("W")); // W is one of the largest characters } } } else { @@ -848,8 +879,12 @@ void GUIListPanel::AdjustScrollbars() { m_HorzScroll->SetSize(m_Width - m_VertScroll->GetWidth() - (m_ScrollBarPadding * 2), m_HorzScroll->GetHeight()); } else { // Normal size - if (m_VertScroll->_GetVisible()) { m_VertScroll->SetSize(m_VertScroll->GetWidth(), m_Height - (m_ScrollBarPadding * 2)); } - if (m_HorzScroll->_GetVisible()) { m_HorzScroll->SetSize(m_Width - (m_ScrollBarPadding * 2), m_HorzScroll->GetHeight()); } + if (m_VertScroll->_GetVisible()) { + m_VertScroll->SetSize(m_VertScroll->GetWidth(), m_Height - (m_ScrollBarPadding * 2)); + } + if (m_HorzScroll->_GetVisible()) { + m_HorzScroll->SetSize(m_Width - (m_ScrollBarPadding * 2), m_HorzScroll->GetHeight()); + } } } @@ -899,9 +934,9 @@ void GUIListPanel::OnKeyPress(int KeyCode, int Modifier) { } // Clear all the items - std::vector::iterator it; + std::vector::iterator it; for (it = m_Items.begin(); it != m_Items.end(); it++) { - Item *I = *it; + Item* I = *it; I->m_Selected = false; } m_SelectedList.clear(); @@ -910,9 +945,8 @@ void GUIListPanel::OnKeyPress(int KeyCode, int Modifier) { m_LastSelected = std::max(m_LastSelected, 0); m_LastSelected = std::min(m_LastSelected, static_cast(m_Items.size() - 1)); - // Select the new item - Item *I = m_Items[m_LastSelected]; + Item* I = m_Items[m_LastSelected]; I->m_Selected = true; m_SelectedList.push_back(I); SendSignal(Select, 0); @@ -937,7 +971,9 @@ void GUIListPanel::OnKeyDown(int KeyCode, int Modifier) { bool GUIListPanel::PointInsideList(int X, int Y) { bool inside = PointInside(X, Y); // Exclude the scrollbars if we are meant to - if (inside && m_HorzScroll->PointInside(X, Y) || m_VertScroll->PointInside(X, Y)) { inside = false; } + if (inside && m_HorzScroll->PointInside(X, Y) || m_VertScroll->PointInside(X, Y)) { + inside = false; + } return inside; } @@ -990,7 +1026,9 @@ void GUIListPanel::SetMultiSelect(bool MultiSelect) { m_MultiSelect = MultiSelect; // Multi-select & Hot-Tracking are mutually exclusive - if (m_MultiSelect) { m_HotTracking = false; } + if (m_MultiSelect) { + m_HotTracking = false; + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1005,12 +1043,14 @@ void GUIListPanel::SetHotTracking(bool HotTrack) { m_HotTracking = HotTrack; // Multi-select & Hot-Tracking are mutually exclusive - if (m_HotTracking) { m_MultiSelect = false; } + if (m_HotTracking) { + m_MultiSelect = false; + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIListPanel::Item * GUIListPanel::GetSelected() { +GUIListPanel::Item* GUIListPanel::GetSelected() { // Nothing in the list if (m_SelectedList.empty()) { return nullptr; @@ -1021,19 +1061,19 @@ GUIListPanel::Item * GUIListPanel::GetSelected() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -std::vector * GUIListPanel::GetSelectionList() { +std::vector* GUIListPanel::GetSelectionList() { return &m_SelectedList; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -std::vector * GUIListPanel::GetItemList() { +std::vector* GUIListPanel::GetItemList() { return &m_Items; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIListPanel::Item * GUIListPanel::GetItem(int Index) { +GUIListPanel::Item* GUIListPanel::GetItem(int Index) { if (Index >= 0 && Index < m_Items.size()) { return m_Items.at(Index); } @@ -1042,15 +1082,19 @@ GUIListPanel::Item * GUIListPanel::GetItem(int Index) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIListPanel::Item * GUIListPanel::GetItem(int X, int Y) { +GUIListPanel::Item* GUIListPanel::GetItem(int X, int Y) { int Height = m_Height; - if (m_HorzScroll->_GetVisible()) { Height -= m_HorzScroll->GetHeight(); } + if (m_HorzScroll->_GetVisible()) { + Height -= m_HorzScroll->GetHeight(); + } int y = m_Y + 1; - if (m_VertScroll->_GetVisible()) { y -= m_VertScroll->GetValue(); } + if (m_VertScroll->_GetVisible()) { + y -= m_VertScroll->GetValue(); + } int Count = 0; - for (std::vector::iterator it = m_Items.begin(); it != m_Items.end(); it++, Count++) { - Item *pItem = *it; + for (std::vector::iterator it = m_Items.begin(); it != m_Items.end(); it++, Count++) { + Item* pItem = *it; // Return the item under the mouse if (Y >= y && Y < y + GetItemHeight(pItem)) { @@ -1068,7 +1112,7 @@ GUIListPanel::Item * GUIListPanel::GetItem(int X, int Y) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int GUIListPanel::GetItemHeight(Item *pItem) { +int GUIListPanel::GetItemHeight(Item* pItem) { int height = 0; if (pItem && m_Font) { @@ -1078,7 +1122,7 @@ int GUIListPanel::GetItemHeight(Item *pItem) { } else if (m_AlternateDrawMode && pItem->m_pBitmap) { // Have to calculate the height if in advanced drawing mode, since the bitmap can be of different heights int thirdWidth = m_Width / 3; - int rightTextWidth = pItem->m_RightText.empty() ? 0 : RIGHTTEXTWIDTH;//thirdWidth / 2; + int rightTextWidth = pItem->m_RightText.empty() ? 0 : RIGHTTEXTWIDTH; // thirdWidth / 2; int mainTextWidth = (thirdWidth * 2) - rightTextWidth; int bitmapWidth = pItem->m_pBitmap ? pItem->m_pBitmap->GetWidth() : 0; int bitmapHeight = pItem->m_pBitmap ? pItem->m_pBitmap->GetHeight() : 0; @@ -1098,10 +1142,10 @@ int GUIListPanel::GetItemHeight(Item *pItem) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int GUIListPanel::GetStackHeight(Item *pItem) { +int GUIListPanel::GetStackHeight(Item* pItem) { int height = 0; - for (std::vector::iterator iitr = m_Items.begin(); iitr != m_Items.end(); ++iitr) { + for (std::vector::iterator iitr = m_Items.begin(); iitr != m_Items.end(); ++iitr) { if ((*iitr) == pItem) { break; } @@ -1112,8 +1156,10 @@ int GUIListPanel::GetStackHeight(Item *pItem) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIListPanel::SetItemValues(int Index, Item &item) { - if (Index >= 0 && Index < m_Items.size()) { *(m_Items.at(Index)) = item; } +void GUIListPanel::SetItemValues(int Index, Item& item) { + if (Index >= 0 && Index < m_Items.size()) { + *(m_Items.at(Index)) = item; + } BuildBitmap(false, true); } @@ -1125,7 +1171,7 @@ int GUIListPanel::GetSelectedIndex() { return -1; } // Get the first item - const Item *I = m_SelectedList.at(0); + const Item* I = m_SelectedList.at(0); return I->m_ID; } @@ -1134,16 +1180,16 @@ int GUIListPanel::GetSelectedIndex() { void GUIListPanel::SetSelectedIndex(int Index) { // Clear the old selection - std::vector::iterator it; + std::vector::iterator it; for (it = m_Items.begin(); it != m_Items.end(); it++) { - Item *I = *it; + Item* I = *it; I->m_Selected = false; } m_SelectedList.clear(); // Select the item if (Index >= 0 && Index < m_Items.size()) { - Item *I = m_Items.at(Index); + Item* I = m_Items.at(Index); I->m_Selected = true; m_SelectedList.push_back(I); SendSignal(Select, 0); @@ -1161,12 +1207,12 @@ void GUIListPanel::SetSelectedIndex(int Index) { void GUIListPanel::DeleteItem(int Index) { if (Index >= 0 && Index < m_Items.size()) { - const Item *I = m_Items.at(Index); + const Item* I = m_Items.at(Index); // If this item was selected, remove it from the selection list if (I->m_Selected) { // Find the item - std::vector::iterator it; + std::vector::iterator it; for (it = m_SelectedList.begin(); it != m_SelectedList.end(); it++) { if (I->m_ID == (*it)->m_ID) { m_SelectedList.erase(it); @@ -1180,10 +1226,10 @@ void GUIListPanel::DeleteItem(int Index) { m_Items.erase(m_Items.begin() + Index); // Reset the id's - std::vector::iterator it; + std::vector::iterator it; int Count = 0; for (it = m_Items.begin(); it != m_Items.end(); it++) { - Item *item = *it; + Item* item = *it; item->m_ID = Count++; } diff --git a/Source/GUI/GUIListPanel.h b/Source/GUI/GUIListPanel.h index ce972b5940..5a9c1744dc 100644 --- a/Source/GUI/GUIListPanel.h +++ b/Source/GUI/GUIListPanel.h @@ -4,613 +4,565 @@ #include "GUIScrollPanel.h" #include "GUIInterface.h" - namespace RTE { -class Entity; - -/// -/// A listbox panel class used for controls requiring a listbox. -/// -class GUIListPanel : public GUIPanel { - -public: - - // Signals - enum { - Click, // Mouse click. Not just inside the panel - MouseDown, // Mouse down inside the panel - MouseUp, // Mouse up inside the panel - Select, // Selected (on mouse down) an item - MouseMove, // Mouse moved over the panel - MouseEnter, // Mouse left the panel - MouseLeave, // Mouse left the panel - DoubleClick,// Double click - KeyDown, // Key Down - EdgeHit //!< Tried scrolling the selection past the first or last item. data = 0 for top edge, data = 1 for bottom edge. - } Signal; - - // Item structure - struct Item - { - int m_ID = 0; - std::string m_Name; - // Extra text field displayed right-justified in the item - std::string m_RightText; - // Extra index for special indexing or reference that the item is associated with. Menu-specific - int m_ExtraIndex = 0; - bool m_Selected = false; - // Can contain a bitmap to display in the list - // This is OWNED - GUIBitmap *m_pBitmap = nullptr; - // Extra data associated with the item - // This is NOT OWNED - const Entity *m_pEntity = nullptr; - int m_Height = 0; - // An x offset to apply to the item (to allow nesting) - int m_OffsetX = 0; - - Item() { } - ~Item() { delete m_pBitmap; m_pBitmap = 0; } - - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIListPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIListPanel object in -// system memory. -// Arguments: GUIManager. - - GUIListPanel(GUIManager *Manager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIListPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIListPanel object in -// system memory. -// Arguments: None. - - GUIListPanel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the listpanel -// Arguments: Position, Size. - - void Create(int X, int Y, int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the panel has been destroyed. -// Arguments: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Add an item to the list. -// Arguments: Name, extraText, Text which will be displayed right-justified in the item. -// a Bitmap object pointer alternatively pointing to a bitmap to display -// in the list. Ownership IS transferred! -// An Extra menu-specific index that this item may be associated with. -// Object instance associated with the item. Ownership is NOT TRANSFERRED! - - void AddItem(const std::string &Name, const std::string &rightText = "", GUIBitmap *pBitmap = nullptr, const Entity *pEntity = 0, const int extraIndex = -1, const int offsetX = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the list. -// Arguments: None. - - void ClearList(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseMove -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse moves (over the panel, or when captured). -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseEnter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse enters the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseLeave -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse leaves the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnDoubleClick -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse has double-clicked on the pane. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnDoubleClick(int X, int Y, int Buttons, int Modifier) override; - - - /// - /// Called when the mouse scroll wheel is moved. - /// - /// Mouse X position. - /// Mouse Y position. - /// Activated modifier buttons. - /// The amount of wheel movement. Positive is scroll up, negative is scroll down. - void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnKeyPress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when a key is pressed (OnDown & repeating). -// Arguments: KeyCode, Modifier. - - void OnKeyPress(int KeyCode, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnKeyDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when a key goes down. -// Arguments: KeyCode, Modifier. - - void OnKeyDown(int KeyCode, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PointInsideList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if a point is inside the panel, but not on the scrollbars if -// they are visible -// Arguments: X, Y Coordinates of point -// Return value: A boolean of the check result - - bool PointInsideList(int X, int Y); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnGainFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the panel gains focus. -// Arguments: None. - - void OnGainFocus() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnLoseFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the panel looses focus. -// Arguments: None. - - void OnLoseFocus() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReceiveSignal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when receiving a signal. -// Arguments: Signal source, Signal code, Signal data. - - void ReceiveSignal(GUIPanel *Source, int Code, int Data) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BeginUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Locks the control from updating every time a new item is added. -// Arguments: None. - - void BeginUpdate(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EndUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: UnLocks the control from updating every time a new item is added. -// Will automatically update the control. -// Arguments: None. - - void EndUpdate(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMultiSelect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the multi-selection value. -// Arguments: MultiSelect - - void SetMultiSelect(bool MultiSelect); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMultiSelect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the multi-selection value. -// Arguments: None. - - bool GetMultiSelect() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetHotTracking -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the hot tracking value. -// Arguments: HotTrack. - - void SetHotTracking(bool HotTrack); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSelected -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the selected (or first in the selected list) item. -// Arguments: None. - - Item * GetSelected(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetItemList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the item list. -// Arguments: None. - - std::vector * GetItemList(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets an item at the index. -// Arguments: Index. - - Item * GetItem(int Index); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets an item at a specific absolute screen coordinate, if any. -// Arguments: The absolute screen coordinates to get the item from. - - Item * GetItem(int X, int Y); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetItemHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the drawing height of the item passed in. -// Arguments: Pointer to the Item to get the height of. Ownership is NOT transferred! - - int GetItemHeight(Item *pItem); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetStackHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the height, in pixels, of the stack of items up to a specific one. -// E.g. If the specified one is the first (top) in the list, 0 is returned. -// If the second one is specified, the height of the first is returned. -// If the third is specified, the sum of the first and second items' heights -// is returned. If 0 is passed, the entire stack's height is returned. -// Arguments: Pointer to the Item to get the height up to. Ownership is NOT transferred! -// If 0 is passed, the entire stack's height is returned. - - int GetStackHeight(Item *pItem = nullptr); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetScrollVerticalValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the scroll value, in pixels, of the vertical axis. -// Arguments: The scroll value in pixels. - - int GetScrollVerticalValue() const { return m_VertScroll->GetValue(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetItemValues -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the values of a specific Item. -// Arguments: Index and Item reference. - - void SetItemValues(int Index, Item &item); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSelectedIndex -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the selected (or first in the selected list) index. -// Arguments: None. -// Returns: Index, or -1 if there is no items selected. - - int GetSelectedIndex(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSelectedIndex -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Selects the item at the index. -// Arguments: Index. - - void SetSelectedIndex(int Index); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAlternateDrawMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Selects this to draw its items differently, with lines instead of -// rectangles, etc -// Arguments: The new mode setting. - - void SetAlternateDrawMode(bool enableAltDrawMode = true) { m_AlternateDrawMode = enableAltDrawMode; } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSelectionList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the selection list. -// Arguments: None. - - std::vector * GetSelectionList(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DeleteItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Deletes an item at the index. -// Arguments: Index. - - void DeleteItem(int Index); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the size of the panel. -// Arguments: Width, Height. - - void SetSize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPositionAbs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the position of the panel. -// Arguments: X, Y. - - void SetPositionAbs(int X, int Y); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnableScrollbars -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the enabled state of both scrollbars. -// Arguments: Horz, Vert - - void EnableScrollbars(bool Horizontal, bool Vertical); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ScrollToItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the vertical scrollbar to show the specific item in the list. -// Arguments: The item you want to show. - - void ScrollToItem(Item *pItem); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ScrollToSelected -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the vertical scrollbar to show the first selected item in the -// list. -// Arguments: None. - - void ScrollToSelected(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ScrollToTop -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the vertical scrollbar to show the top of the list -// Arguments: None. - - void ScrollToTop(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ScrollToBottom -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the vertical scrollbar to show the bottom of the list -// Arguments: None. - - void ScrollToBottom(); - - /// - /// Scrolls the GUIListPanel up. - /// - void ScrollUp(); - - /// - /// Scrolls the GUIListPanel down. - /// - void ScrollDown(); - - /// - /// Scrolls the the GUIListPanel to a specific position - /// - /// The position to scroll to. - void ScrollTo(int position); - - /// - /// Sets whether the scroll panel scrolls in a loop or not. - /// - /// True to scroll in a loop, false to scroll with edge stopping. - void SetSelectionScrollingLoop(bool scrollLoop); - - - /// - /// Sets whether the list panel can be scrolled with the mouse scroll wheel. - /// - /// True to enable scrolling, false to disable. - void SetMouseScrolling(bool mouseScroll); - - /// - /// Sets the thickness (width on vertical, height on horizontal) of the ListPanel's scroll bars and adjusts them to the new thickness. - /// - /// The new scroll bar thickness, in pixels. - void SetScrollBarThickness(int newThickness) { m_ScrollBarThickness = newThickness; AdjustScrollbars(); } - - /// - /// Sets the padding around the ListPanel's scrollbars and adjusts them to the new padding. Used to better size and position scrollbars within panel bounds, allowing to not overdraw on panel borders. - /// - /// The new scrollbar padding, in pixels. - void SetScrollBarPadding(int newPadding) { m_ScrollBarPadding = newPadding; AdjustScrollbars(); } - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Build the bitmap. - // Arguments: UpdateBase, UpdateText. - - void BuildBitmap(bool UpdateBase, bool UpdateText); - -private: - - GUISkin *m_Skin; - GUIBitmap *m_BaseBitmap; - GUIBitmap *m_DrawBitmap; - GUIBitmap *m_FrameBitmap; - unsigned long m_FontSelectColor; - - bool m_UpdateLocked; - - GUIScrollPanel *m_HorzScroll; - GUIScrollPanel *m_VertScroll; - bool m_HorzScrollEnabled; - bool m_VertScrollEnabled; - int m_ScrollBarThickness; //!< The thickness (width on vertical, height on horizontal) of the ListPanel's scroll bars, in pixels. - int m_ScrollBarPadding; //!< The padding around the scrollbars, in pixels. Used to better size and position scrollbars within panel bounds, allowing to not overdraw on panel borders. - - bool m_CapturedHorz; - bool m_CapturedVert; - bool m_ExternalCapture; - - int m_LargestWidth; - bool m_MultiSelect; - bool m_HotTracking; - int m_LastSelected; - bool m_LoopSelectionScroll; //!< Whether the list panel scrolls in a loop or not, while scrolling the selection list (as opposed to the scrollbar). - bool m_MouseScroll; //!< Whether the list panel enables scrolling with the mouse scroll wheel. - - bool m_AlternateDrawMode; // This draws items differently, not with boxes etc. - - std::vector m_Items; - std::vector m_SelectedList; - unsigned long m_SelectedColorIndex; - unsigned long m_UnselectedColorIndex; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildDrawBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Build the drawing bitmap. -// Arguments: None. - - void BuildDrawBitmap(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AdjustScrollbars -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the scrollbars. -// Arguments: None. - - void AdjustScrollbars(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SelectItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Selects an item based on mouse position. -// Arguments: Mouse Position, Modifier. - - void SelectItem(int X, int Y, int Modifier); - - - /// - /// Perform list scrolling through the scrollbar. - /// - /// Amount and direction of scrolling. Positive to scroll up, negative to scroll down. - void ScrollBarScrolling(int mouseWheelChange); - + class Entity; /// - /// Perform list scrolling by changing the currently selected list item. + /// A listbox panel class used for controls requiring a listbox. /// - /// Amount and direction of scrolling. Positive to scroll up, negative to scroll down. - void SelectionListScrolling(int mouseWheelChange); -}; -}; + class GUIListPanel : public GUIPanel { + + public: + // Signals + enum { + Click, // Mouse click. Not just inside the panel + MouseDown, // Mouse down inside the panel + MouseUp, // Mouse up inside the panel + Select, // Selected (on mouse down) an item + MouseMove, // Mouse moved over the panel + MouseEnter, // Mouse left the panel + MouseLeave, // Mouse left the panel + DoubleClick, // Double click + KeyDown, // Key Down + EdgeHit //!< Tried scrolling the selection past the first or last item. data = 0 for top edge, data = 1 for bottom edge. + } Signal; + + // Item structure + struct Item { + int m_ID = 0; + std::string m_Name; + // Extra text field displayed right-justified in the item + std::string m_RightText; + // Extra index for special indexing or reference that the item is associated with. Menu-specific + int m_ExtraIndex = 0; + bool m_Selected = false; + // Can contain a bitmap to display in the list + // This is OWNED + GUIBitmap* m_pBitmap = nullptr; + // Extra data associated with the item + // This is NOT OWNED + const Entity* m_pEntity = nullptr; + int m_Height = 0; + // An x offset to apply to the item (to allow nesting) + int m_OffsetX = 0; + + Item() {} + ~Item() { + delete m_pBitmap; + m_pBitmap = 0; + } + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIListPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIListPanel object in + // system memory. + // Arguments: GUIManager. + + GUIListPanel(GUIManager* Manager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIListPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIListPanel object in + // system memory. + // Arguments: None. + + GUIListPanel(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the listpanel + // Arguments: Position, Size. + + void Create(int X, int Y, int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the panel has been destroyed. + // Arguments: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Add an item to the list. + // Arguments: Name, extraText, Text which will be displayed right-justified in the item. + // a Bitmap object pointer alternatively pointing to a bitmap to display + // in the list. Ownership IS transferred! + // An Extra menu-specific index that this item may be associated with. + // Object instance associated with the item. Ownership is NOT TRANSFERRED! + + void AddItem(const std::string& Name, const std::string& rightText = "", GUIBitmap* pBitmap = nullptr, const Entity* pEntity = 0, const int extraIndex = -1, const int offsetX = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the list. + // Arguments: None. + + void ClearList(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseMove + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse moves (over the panel, or when captured). + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseEnter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse enters the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseLeave + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse leaves the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnDoubleClick + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse has double-clicked on the pane. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnDoubleClick(int X, int Y, int Buttons, int Modifier) override; + + /// + /// Called when the mouse scroll wheel is moved. + /// + /// Mouse X position. + /// Mouse Y position. + /// Activated modifier buttons. + /// The amount of wheel movement. Positive is scroll up, negative is scroll down. + void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnKeyPress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when a key is pressed (OnDown & repeating). + // Arguments: KeyCode, Modifier. + + void OnKeyPress(int KeyCode, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnKeyDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when a key goes down. + // Arguments: KeyCode, Modifier. + + void OnKeyDown(int KeyCode, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PointInsideList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if a point is inside the panel, but not on the scrollbars if + // they are visible + // Arguments: X, Y Coordinates of point + // Return value: A boolean of the check result + + bool PointInsideList(int X, int Y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnGainFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the panel gains focus. + // Arguments: None. + + void OnGainFocus() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnLoseFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the panel looses focus. + // Arguments: None. + + void OnLoseFocus() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReceiveSignal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when receiving a signal. + // Arguments: Signal source, Signal code, Signal data. + + void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BeginUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Locks the control from updating every time a new item is added. + // Arguments: None. + + void BeginUpdate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EndUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: UnLocks the control from updating every time a new item is added. + // Will automatically update the control. + // Arguments: None. + + void EndUpdate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMultiSelect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the multi-selection value. + // Arguments: MultiSelect + + void SetMultiSelect(bool MultiSelect); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMultiSelect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the multi-selection value. + // Arguments: None. + + bool GetMultiSelect() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetHotTracking + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the hot tracking value. + // Arguments: HotTrack. + + void SetHotTracking(bool HotTrack); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSelected + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the selected (or first in the selected list) item. + // Arguments: None. + + Item* GetSelected(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetItemList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the item list. + // Arguments: None. + + std::vector* GetItemList(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets an item at the index. + // Arguments: Index. + + Item* GetItem(int Index); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets an item at a specific absolute screen coordinate, if any. + // Arguments: The absolute screen coordinates to get the item from. + + Item* GetItem(int X, int Y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetItemHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the drawing height of the item passed in. + // Arguments: Pointer to the Item to get the height of. Ownership is NOT transferred! + + int GetItemHeight(Item* pItem); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetStackHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the height, in pixels, of the stack of items up to a specific one. + // E.g. If the specified one is the first (top) in the list, 0 is returned. + // If the second one is specified, the height of the first is returned. + // If the third is specified, the sum of the first and second items' heights + // is returned. If 0 is passed, the entire stack's height is returned. + // Arguments: Pointer to the Item to get the height up to. Ownership is NOT transferred! + // If 0 is passed, the entire stack's height is returned. + + int GetStackHeight(Item* pItem = nullptr); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetScrollVerticalValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the scroll value, in pixels, of the vertical axis. + // Arguments: The scroll value in pixels. + + int GetScrollVerticalValue() const { return m_VertScroll->GetValue(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetItemValues + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the values of a specific Item. + // Arguments: Index and Item reference. + + void SetItemValues(int Index, Item& item); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSelectedIndex + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the selected (or first in the selected list) index. + // Arguments: None. + // Returns: Index, or -1 if there is no items selected. + + int GetSelectedIndex(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSelectedIndex + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Selects the item at the index. + // Arguments: Index. + + void SetSelectedIndex(int Index); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetAlternateDrawMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Selects this to draw its items differently, with lines instead of + // rectangles, etc + // Arguments: The new mode setting. + + void SetAlternateDrawMode(bool enableAltDrawMode = true) { m_AlternateDrawMode = enableAltDrawMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSelectionList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the selection list. + // Arguments: None. + + std::vector* GetSelectionList(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DeleteItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Deletes an item at the index. + // Arguments: Index. + + void DeleteItem(int Index); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the size of the panel. + // Arguments: Width, Height. + + void SetSize(int Width, int Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPositionAbs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the position of the panel. + // Arguments: X, Y. + + void SetPositionAbs(int X, int Y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnableScrollbars + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the enabled state of both scrollbars. + // Arguments: Horz, Vert + + void EnableScrollbars(bool Horizontal, bool Vertical); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ScrollToItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the vertical scrollbar to show the specific item in the list. + // Arguments: The item you want to show. + + void ScrollToItem(Item* pItem); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ScrollToSelected + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the vertical scrollbar to show the first selected item in the + // list. + // Arguments: None. + + void ScrollToSelected(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ScrollToTop + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the vertical scrollbar to show the top of the list + // Arguments: None. + + void ScrollToTop(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ScrollToBottom + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the vertical scrollbar to show the bottom of the list + // Arguments: None. + + void ScrollToBottom(); + + /// + /// Scrolls the GUIListPanel up. + /// + void ScrollUp(); + + /// + /// Scrolls the GUIListPanel down. + /// + void ScrollDown(); + + /// + /// Scrolls the the GUIListPanel to a specific position + /// + /// The position to scroll to. + void ScrollTo(int position); + + /// + /// Sets whether the scroll panel scrolls in a loop or not. + /// + /// True to scroll in a loop, false to scroll with edge stopping. + void SetSelectionScrollingLoop(bool scrollLoop); + + /// + /// Sets whether the list panel can be scrolled with the mouse scroll wheel. + /// + /// True to enable scrolling, false to disable. + void SetMouseScrolling(bool mouseScroll); + + /// + /// Sets the thickness (width on vertical, height on horizontal) of the ListPanel's scroll bars and adjusts them to the new thickness. + /// + /// The new scroll bar thickness, in pixels. + void SetScrollBarThickness(int newThickness) { + m_ScrollBarThickness = newThickness; + AdjustScrollbars(); + } + + /// + /// Sets the padding around the ListPanel's scrollbars and adjusts them to the new padding. Used to better size and position scrollbars within panel bounds, allowing to not overdraw on panel borders. + /// + /// The new scrollbar padding, in pixels. + void SetScrollBarPadding(int newPadding) { + m_ScrollBarPadding = newPadding; + AdjustScrollbars(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Build the bitmap. + // Arguments: UpdateBase, UpdateText. + + void BuildBitmap(bool UpdateBase, bool UpdateText); + + private: + GUISkin* m_Skin; + GUIBitmap* m_BaseBitmap; + GUIBitmap* m_DrawBitmap; + GUIBitmap* m_FrameBitmap; + unsigned long m_FontSelectColor; + + bool m_UpdateLocked; + + GUIScrollPanel* m_HorzScroll; + GUIScrollPanel* m_VertScroll; + bool m_HorzScrollEnabled; + bool m_VertScrollEnabled; + int m_ScrollBarThickness; //!< The thickness (width on vertical, height on horizontal) of the ListPanel's scroll bars, in pixels. + int m_ScrollBarPadding; //!< The padding around the scrollbars, in pixels. Used to better size and position scrollbars within panel bounds, allowing to not overdraw on panel borders. + + bool m_CapturedHorz; + bool m_CapturedVert; + bool m_ExternalCapture; + + int m_LargestWidth; + bool m_MultiSelect; + bool m_HotTracking; + int m_LastSelected; + bool m_LoopSelectionScroll; //!< Whether the list panel scrolls in a loop or not, while scrolling the selection list (as opposed to the scrollbar). + bool m_MouseScroll; //!< Whether the list panel enables scrolling with the mouse scroll wheel. + + bool m_AlternateDrawMode; // This draws items differently, not with boxes etc. + + std::vector m_Items; + std::vector m_SelectedList; + unsigned long m_SelectedColorIndex; + unsigned long m_UnselectedColorIndex; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildDrawBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Build the drawing bitmap. + // Arguments: None. + + void BuildDrawBitmap(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AdjustScrollbars + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the scrollbars. + // Arguments: None. + + void AdjustScrollbars(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SelectItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Selects an item based on mouse position. + // Arguments: Mouse Position, Modifier. + + void SelectItem(int X, int Y, int Modifier); + + /// + /// Perform list scrolling through the scrollbar. + /// + /// Amount and direction of scrolling. Positive to scroll up, negative to scroll down. + void ScrollBarScrolling(int mouseWheelChange); + + /// + /// Perform list scrolling by changing the currently selected list item. + /// + /// Amount and direction of scrolling. Positive to scroll up, negative to scroll down. + void SelectionListScrolling(int mouseWheelChange); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIManager.cpp b/Source/GUI/GUIManager.cpp index bf0f26f963..97195ec318 100644 --- a/Source/GUI/GUIManager.cpp +++ b/Source/GUI/GUIManager.cpp @@ -5,7 +5,7 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIManager::GUIManager(GUIInput *input) { +GUIManager::GUIManager(GUIInput* input) { m_Input = input; m_MouseEnabled = true; m_UseValidation = false; @@ -14,9 +14,9 @@ GUIManager::GUIManager(GUIInput *input) { // Maximum time allowed between two clicks for a double click // In milliseconds - //m_DoubleClickTime = GetDoubleClickTime(); // Use windows' system value + // m_DoubleClickTime = GetDoubleClickTime(); // Use windows' system value m_DoubleClickTime = 500; - //m_DoubleClickSize = GetSystemMetrics(SM_CXDOUBLECLK)/2; // Use windows' system value + // m_DoubleClickSize = GetSystemMetrics(SM_CXDOUBLECLK)/2; // Use windows' system value m_DoubleClickSize = 2; m_DoubleClickButtons = GUIPanel::MOUSE_NONE; @@ -52,13 +52,13 @@ void GUIManager::Clear() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIManager::AddPanel(GUIPanel *panel) { +void GUIManager::AddPanel(GUIPanel* panel) { if (panel) { int Z = 0; // Get the last panel in the list if (!m_PanelList.empty()) { - const GUIPanel *p = m_PanelList.at(m_PanelList.size() - 1); + const GUIPanel* p = m_PanelList.at(m_PanelList.size() - 1); Z = p->GetZPos() + 1; } @@ -95,10 +95,18 @@ void GUIManager::Update(bool ignoreKeyboardEvents) { // Build the modifier state Modifier = m_Input->GetModifier(); - if (Modifier & GUIInput::ModShift) { Mod |= GUIPanel::MODI_SHIFT; } - if (Modifier & GUIInput::ModCtrl) { Mod |= GUIPanel::MODI_CTRL; } - if (Modifier & GUIInput::ModAlt) { Mod |= GUIPanel::MODI_ALT; } - if (Modifier & GUIInput::ModCommand) { Mod |= GUIPanel::MODI_COMMAND; } + if (Modifier & GUIInput::ModShift) { + Mod |= GUIPanel::MODI_SHIFT; + } + if (Modifier & GUIInput::ModCtrl) { + Mod |= GUIPanel::MODI_CTRL; + } + if (Modifier & GUIInput::ModAlt) { + Mod |= GUIPanel::MODI_ALT; + } + if (Modifier & GUIInput::ModCommand) { + Mod |= GUIPanel::MODI_COMMAND; + } // Get the mouse data if (m_MouseEnabled) { @@ -114,10 +122,12 @@ void GUIManager::Update(bool ignoreKeyboardEvents) { // Panels that have captured the mouse get the events over anything else // Regardless where the mouse currently is - GUIPanel *CurPanel = m_CapturedPanel; + GUIPanel* CurPanel = m_CapturedPanel; // Find the lowest panel in the tree that the mouse is over - if (!CurPanel) { CurPanel = FindTopPanel(MouseX, MouseY); } + if (!CurPanel) { + CurPanel = FindTopPanel(MouseX, MouseY); + } // Build the states for (i = 0; i < 3; i++) { @@ -138,7 +148,9 @@ void GUIManager::Update(bool ignoreKeyboardEvents) { // Double click (on the mouse up) if (Released != GUIPanel::MOUSE_NONE && m_DoubleClickButtons != GUIPanel::MOUSE_NONE) { - if (CurPanel) { CurPanel->OnDoubleClick(MouseX, MouseY, m_DoubleClickButtons, Mod); } + if (CurPanel) { + CurPanel->OnDoubleClick(MouseX, MouseY, m_DoubleClickButtons, Mod); + } m_LastMouseDown[0] = m_LastMouseDown[1] = m_LastMouseDown[2] = -99999.0f; } @@ -166,7 +178,9 @@ void GUIManager::Update(bool ignoreKeyboardEvents) { } // OnMouseDown event - if (CurPanel) { CurPanel->OnMouseDown(MouseX, MouseY, Pushed, Mod); } + if (CurPanel) { + CurPanel->OnMouseDown(MouseX, MouseY, Pushed, Mod); + } } // Mouse move @@ -179,30 +193,39 @@ void GUIManager::Update(bool ignoreKeyboardEvents) { // Disable it (panel will have to re-enable it if it wants to continue) m_HoverTrack = false; - if (m_HoverPanel && m_HoverPanel->PointInside(MouseX, MouseY)/*GetPanelID() == CurPanel->GetPanelID()*/) { + if (m_HoverPanel && m_HoverPanel->PointInside(MouseX, MouseY) /*GetPanelID() == CurPanel->GetPanelID()*/) { // call the OnMouseHover event m_HoverPanel->OnMouseHover(MouseX, MouseY, Buttons, Mod); } } - // Mouse enter & leave bool Enter = false; bool Leave = false; - if (!m_MouseOverPanel && CurPanel) { Enter = true; } - if (!CurPanel && m_MouseOverPanel) { Leave = true; } + if (!m_MouseOverPanel && CurPanel) { + Enter = true; + } + if (!CurPanel && m_MouseOverPanel) { + Leave = true; + } if (m_MouseOverPanel && CurPanel && m_MouseOverPanel->GetPanelID() != CurPanel->GetPanelID()) { Enter = true; Leave = true; } // OnMouseEnter - if (Enter && CurPanel) { CurPanel->OnMouseEnter(MouseX, MouseY, Buttons, Mod); } + if (Enter && CurPanel) { + CurPanel->OnMouseEnter(MouseX, MouseY, Buttons, Mod); + } // OnMouseLeave - if (Leave &&m_MouseOverPanel) { m_MouseOverPanel->OnMouseLeave(MouseX, MouseY, Buttons, Mod); } + if (Leave && m_MouseOverPanel) { + m_MouseOverPanel->OnMouseLeave(MouseX, MouseY, Buttons, Mod); + } - if (MouseWheelChange &&CurPanel) { CurPanel->OnMouseWheelChange(MouseX, MouseY, Mod, MouseWheelChange); } + if (MouseWheelChange && CurPanel) { + CurPanel->OnMouseWheelChange(MouseX, MouseY, Mod, MouseWheelChange); + } m_MouseOverPanel = CurPanel; } @@ -221,7 +244,6 @@ void GUIManager::Update(bool ignoreKeyboardEvents) { return; } - for (i = 1; i < 256; i++) { switch (KeyboardBuffer[i]) { // KeyDown & KeyPress @@ -252,21 +274,23 @@ void GUIManager::Update(bool ignoreKeyboardEvents) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIManager::Draw(GUIScreen *Screen) { +void GUIManager::Draw(GUIScreen* Screen) { // Go through drawing panels that are invalid - std::vector::iterator it; + std::vector::iterator it; for (it = m_PanelList.begin(); it != m_PanelList.end(); it++) { - GUIPanel *p = *it; + GUIPanel* p = *it; // Draw the panel - if ((!p->IsValid() || !m_UseValidation) && p->_GetVisible()) { p->Draw(Screen); } + if ((!p->IsValid() || !m_UseValidation) && p->_GetVisible()) { + p->Draw(Screen); + } } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIManager::CaptureMouse(GUIPanel *Panel) { +void GUIManager::CaptureMouse(GUIPanel* Panel) { assert(Panel); // Release any old capture @@ -280,20 +304,22 @@ void GUIManager::CaptureMouse(GUIPanel *Panel) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIManager::ReleaseMouse() { - if (m_CapturedPanel) { m_CapturedPanel->SetCaptureState(false); } + if (m_CapturedPanel) { + m_CapturedPanel->SetCaptureState(false); + } m_CapturedPanel = nullptr; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIManager::FindBottomPanel(int X, int Y) { - std::vector::iterator it; +GUIPanel* GUIManager::FindBottomPanel(int X, int Y) { + std::vector::iterator it; for (it = m_PanelList.begin(); it != m_PanelList.end(); it++) { - GUIPanel *P = *it; + GUIPanel* P = *it; if (P) { - GUIPanel *CurPanel = P->BottomPanelUnderPoint(X, Y); + GUIPanel* CurPanel = P->BottomPanelUnderPoint(X, Y); if (CurPanel) { return CurPanel; } @@ -305,13 +331,13 @@ GUIPanel * GUIManager::FindBottomPanel(int X, int Y) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIManager::FindTopPanel(int X, int Y) { - std::vector::reverse_iterator it; +GUIPanel* GUIManager::FindTopPanel(int X, int Y) { + std::vector::reverse_iterator it; for (it = m_PanelList.rbegin(); it != m_PanelList.rend(); it++) { - GUIPanel *P = *it; + GUIPanel* P = *it; if (P) { - GUIPanel *CurPanel = P->TopPanelUnderPoint(X, Y); + GUIPanel* CurPanel = P->TopPanelUnderPoint(X, Y); if (CurPanel) { return CurPanel; } @@ -329,7 +355,7 @@ int GUIManager::GetPanelID() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIManager::MouseInRect(const GUIRect *Rect, int X, int Y) { +bool GUIManager::MouseInRect(const GUIRect* Rect, int X, int Y) { if (!Rect) { return false; } @@ -341,21 +367,27 @@ bool GUIManager::MouseInRect(const GUIRect *Rect, int X, int Y) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIManager::TrackMouseHover(GUIPanel *Pan, bool Enabled, int Delay) { +void GUIManager::TrackMouseHover(GUIPanel* Pan, bool Enabled, int Delay) { assert(Pan); m_HoverTrack = Enabled; m_HoverPanel = Pan; - if (m_HoverTrack) { m_HoverTime = m_pTimer->GetElapsedRealTimeMS() + ((float)Delay / 1000.0F); } + if (m_HoverTrack) { + m_HoverTime = m_pTimer->GetElapsedRealTimeMS() + ((float)Delay / 1000.0F); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIManager::SetFocus(GUIPanel *Pan) { +void GUIManager::SetFocus(GUIPanel* Pan) { // Send the LoseFocus event to the old panel (if there is one) - if (m_FocusPanel) { m_FocusPanel->OnLoseFocus(); } + if (m_FocusPanel) { + m_FocusPanel->OnLoseFocus(); + } m_FocusPanel = Pan; // Send the GainFocus event to the new panel - if (m_FocusPanel) { m_FocusPanel->OnGainFocus(); } + if (m_FocusPanel) { + m_FocusPanel->OnGainFocus(); + } } diff --git a/Source/GUI/GUIManager.h b/Source/GUI/GUIManager.h index e3a5f4bb4d..772aa40f8f 100644 --- a/Source/GUI/GUIManager.h +++ b/Source/GUI/GUIManager.h @@ -3,189 +3,170 @@ namespace RTE { -class Timer; + class Timer; + + /// + /// The main manager that handles all the panels and inputs. + /// + class GUIManager { -/// -/// The main manager that handles all the panels and inputs. -/// -class GUIManager { + public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIManager + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIManager object in system + // memory. + // Arguments: Input Interface -public: + explicit GUIManager(GUIInput* input); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: GUIManager + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a GUIManager object. + // Arguments: None. + + ~GUIManager(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the manager. + // Arguments: None. + + void Clear(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a panel to the list. + // Arguments: Pointer to a panel. - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIManager -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIManager object in system -// memory. -// Arguments: Input Interface - - explicit GUIManager(GUIInput *input); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: GUIManager -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a GUIManager object. -// Arguments: None. - - ~GUIManager(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the manager. -// Arguments: None. - - void Clear(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a panel to the list. -// Arguments: Pointer to a panel. - - void AddPanel(GUIPanel *panel); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the GUI. -// Arguments: Whether keyboard events should be ignored or not. Used to avoid conflicts when custom keyboard handling for GUI elements is preset. - - void Update(bool ignoreKeyboardEvents = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draw all the panels -// Arguments: Screen. - - void Draw(GUIScreen *Screen); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnableMouse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Enables and disables the mouse completely for this. -// Arguments: Enable? - - void EnableMouse(bool enable = true) { m_MouseEnabled = enable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CaptureMouse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets up capturing a mouse for a panel. -// Arguments: Panel. - - void CaptureMouse(GUIPanel *Panel); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReleaseMouse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Releases a mouse capture. -// Arguments: None. - - void ReleaseMouse(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanelID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a unique ID for a panel. -// Arguments: None. - - int GetPanelID(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetInputController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the input controller object -// Arguments: None. - - GUIInput * GetInputController() { return m_Input; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TrackMouseHover -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets up the manager to enable/disable hover tracking of this panel -// Arguments: Panel, Enabled, Delay (milliseconds) - - void TrackMouseHover(GUIPanel *Pan, bool Enabled, int Delay); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Give focus to a panel. -// Arguments: Panel. - - void SetFocus(GUIPanel *Pan); - - -private: - - std::vector m_PanelList; - GUIPanel *m_CapturedPanel; - GUIPanel *m_FocusPanel; - GUIPanel *m_MouseOverPanel; - - GUIInput *m_Input; - bool m_MouseEnabled; - int m_OldMouseX; - int m_OldMouseY; - - int m_DoubleClickTime; - int m_DoubleClickSize; - int m_DoubleClickButtons; - float m_LastMouseDown[3]; - GUIRect m_DoubleClickRect; - - bool m_HoverTrack; - GUIPanel *m_HoverPanel; - float m_HoverTime; - - bool m_UseValidation; - int m_UniqueIDCount; - - Timer *m_pTimer; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FindBottomPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through the panel list and selects the bottommost -// ('first', render wise) panel on a specific point. -// Arguments: Mouse Position. - - GUIPanel *FindBottomPanel(int X, int Y); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FindTopPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through the panel list and selects the topmost ('last', render -// wise) panel on a specific point. -// Arguments: Mouse Position. - - GUIPanel *FindTopPanel(int X, int Y); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: MouseInRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if the mouse point is inside a rectangle. -// Arguments: Rectangle, Mouse position. - - bool MouseInRect(const GUIRect *Rect, int X, int Y); - -}; -}; + void AddPanel(GUIPanel* panel); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the GUI. + // Arguments: Whether keyboard events should be ignored or not. Used to avoid conflicts when custom keyboard handling for GUI elements is preset. + + void Update(bool ignoreKeyboardEvents = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draw all the panels + // Arguments: Screen. + + void Draw(GUIScreen* Screen); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnableMouse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Enables and disables the mouse completely for this. + // Arguments: Enable? + + void EnableMouse(bool enable = true) { m_MouseEnabled = enable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CaptureMouse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets up capturing a mouse for a panel. + // Arguments: Panel. + + void CaptureMouse(GUIPanel* Panel); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReleaseMouse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Releases a mouse capture. + // Arguments: None. + + void ReleaseMouse(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanelID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a unique ID for a panel. + // Arguments: None. + + int GetPanelID(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetInputController + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the input controller object + // Arguments: None. + + GUIInput* GetInputController() { return m_Input; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TrackMouseHover + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets up the manager to enable/disable hover tracking of this panel + // Arguments: Panel, Enabled, Delay (milliseconds) + + void TrackMouseHover(GUIPanel* Pan, bool Enabled, int Delay); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Give focus to a panel. + // Arguments: Panel. + + void SetFocus(GUIPanel* Pan); + + private: + std::vector m_PanelList; + GUIPanel* m_CapturedPanel; + GUIPanel* m_FocusPanel; + GUIPanel* m_MouseOverPanel; + + GUIInput* m_Input; + bool m_MouseEnabled; + int m_OldMouseX; + int m_OldMouseY; + + int m_DoubleClickTime; + int m_DoubleClickSize; + int m_DoubleClickButtons; + float m_LastMouseDown[3]; + GUIRect m_DoubleClickRect; + + bool m_HoverTrack; + GUIPanel* m_HoverPanel; + float m_HoverTime; + + bool m_UseValidation; + int m_UniqueIDCount; + + Timer* m_pTimer; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FindBottomPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through the panel list and selects the bottommost + // ('first', render wise) panel on a specific point. + // Arguments: Mouse Position. + + GUIPanel* FindBottomPanel(int X, int Y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FindTopPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through the panel list and selects the topmost ('last', render + // wise) panel on a specific point. + // Arguments: Mouse Position. + + GUIPanel* FindTopPanel(int X, int Y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: MouseInRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if the mouse point is inside a rectangle. + // Arguments: Rectangle, Mouse position. + + bool MouseInRect(const GUIRect* Rect, int X, int Y); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIPanel.cpp b/Source/GUI/GUIPanel.cpp index d10e8faada..6bde68721f 100644 --- a/Source/GUI/GUIPanel.cpp +++ b/Source/GUI/GUIPanel.cpp @@ -4,7 +4,7 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel::GUIPanel(GUIManager *Manager) { +GUIPanel::GUIPanel(GUIManager* Manager) { Clear(); m_Manager = Manager; m_Font = nullptr; @@ -48,7 +48,7 @@ void GUIPanel::Clear() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::Setup(GUIManager *manager, int ZPos) { +void GUIPanel::Setup(GUIManager* manager, int ZPos) { m_Manager = manager; m_ZPos = ZPos; @@ -57,9 +57,9 @@ void GUIPanel::Setup(GUIManager *manager, int ZPos) { // Set the manager for all the children int Z = 0; - std::vector::iterator it; + std::vector::iterator it; for (it = m_Children.begin(); it != m_Children.end(); it++) { - GUIPanel *P = *it; + GUIPanel* P = *it; if (P) { Z++; P->Setup(manager, Z); @@ -69,7 +69,7 @@ void GUIPanel::Setup(GUIManager *manager, int ZPos) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::AddChild(GUIPanel *child, bool convertToAbsolutePos) { +void GUIPanel::AddChild(GUIPanel* child, bool convertToAbsolutePos) { if (child) { // Convert the child's coordinates into absolute coordinates if (convertToAbsolutePos) { @@ -83,12 +83,14 @@ void GUIPanel::AddChild(GUIPanel *child, bool convertToAbsolutePos) { int zPos = 0; if (!m_Children.empty()) { - const GUIPanel *lastChild = m_Children.back(); + const GUIPanel* lastChild = m_Children.back(); zPos = lastChild->GetZPos() + 1; } // Remove the child from any previous parent - if (child->GetParentPanel()) { child->GetParentPanel()->GUIPanel::RemoveChild(child); } + if (child->GetParentPanel()) { + child->GetParentPanel()->GUIPanel::RemoveChild(child); + } // Setup the inherited values child->m_Parent = this; @@ -101,12 +103,12 @@ void GUIPanel::AddChild(GUIPanel *child, bool convertToAbsolutePos) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::RemoveChild(const GUIPanel *pChild) { +void GUIPanel::RemoveChild(const GUIPanel* pChild) { // Note: We do NOT free the children because they are still linked in through their controls. This merely removes the panel from the list. // This will cause a small memory leak, but this is only designed for the GUI Editor and is a bit of a hack - for (std::vector::iterator itr = m_Children.begin(); itr != m_Children.end(); itr++) { - const GUIPanel *pPanel = *itr; + for (std::vector::iterator itr = m_Children.begin(); itr != m_Children.end(); itr++) { + const GUIPanel* pPanel = *itr; if (pPanel && pPanel == pChild) { m_Children.erase(itr); break; @@ -116,7 +118,7 @@ void GUIPanel::RemoveChild(const GUIPanel *pChild) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::LoadProperties(GUIProperties *Props) { +void GUIPanel::LoadProperties(GUIProperties* Props) { assert(Props); Props->GetValue("X", &m_X); @@ -142,7 +144,7 @@ bool GUIPanel::IsValid() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::Draw(GUIScreen *Screen) { +void GUIPanel::Draw(GUIScreen* Screen) { // Validate this panel m_ValidRegion = true; @@ -154,9 +156,9 @@ void GUIPanel::Draw(GUIScreen *Screen) { Screen->GetBitmap()->GetClipRect(&thisClip); // Draw children - std::vector::iterator it; + std::vector::iterator it; for (it = m_Children.begin(); it != m_Children.end(); it++) { - GUIPanel *P = *it; + GUIPanel* P = *it; if (P->_GetVisible()) { // Re-set the clipping rect of this panel since the last child has messed with it @@ -239,7 +241,7 @@ void GUIPanel::ReleaseMouse() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIPanel::BottomPanelUnderPoint(int x, int y) { +GUIPanel* GUIPanel::BottomPanelUnderPoint(int x, int y) { if (!PointInside(x, y)) { return nullptr; } @@ -249,10 +251,10 @@ GUIPanel * GUIPanel::BottomPanelUnderPoint(int x, int y) { } // Go through the children - GUIPanel *CurPanel = nullptr; - std::vector::iterator it; + GUIPanel* CurPanel = nullptr; + std::vector::iterator it; for (it = m_Children.begin(); it != m_Children.end(); it++) { - GUIPanel *P = *it; + GUIPanel* P = *it; if (P) { CurPanel = P->BottomPanelUnderPoint(x, y); if (CurPanel != nullptr) { @@ -266,7 +268,7 @@ GUIPanel * GUIPanel::BottomPanelUnderPoint(int x, int y) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIPanel::TopPanelUnderPoint(int x, int y) { +GUIPanel* GUIPanel::TopPanelUnderPoint(int x, int y) { if (!PointInside(x, y)) { return nullptr; } @@ -276,10 +278,10 @@ GUIPanel * GUIPanel::TopPanelUnderPoint(int x, int y) { } // Go through the children - GUIPanel *CurPanel = nullptr; - std::vector::reverse_iterator it; + GUIPanel* CurPanel = nullptr; + std::vector::reverse_iterator it; for (it = m_Children.rbegin(); it != m_Children.rend(); it++) { - GUIPanel *P = *it; + GUIPanel* P = *it; if (P) { CurPanel = P->TopPanelUnderPoint(x, y); if (CurPanel != nullptr) { @@ -327,9 +329,9 @@ void GUIPanel::SetPositionAbs(int X, int Y, bool moveChildren) { // Move children if (moveChildren) { - std::vector::iterator it; + std::vector::iterator it; for (it = m_Children.begin(); it != m_Children.end(); it++) { - GUIPanel *P = *it; + GUIPanel* P = *it; P->SetPositionAbs(P->m_X + DX, P->m_Y + DY); } } @@ -348,9 +350,9 @@ void GUIPanel::SetPositionRel(int X, int Y) { m_Y = Y; // Move children - std::vector::iterator it; + std::vector::iterator it; for (it = m_Children.begin(); it != m_Children.end(); it++) { - GUIPanel *P = *it; + GUIPanel* P = *it; P->SetPositionAbs(P->m_X + DX, P->m_Y + DY); } } @@ -362,9 +364,9 @@ void GUIPanel::MoveRelative(int dX, int dY) { m_Y += dY; // Move children - std::vector::iterator it; + std::vector::iterator it; for (it = m_Children.begin(); it != m_Children.end(); it++) { - GUIPanel *P = *it; + GUIPanel* P = *it; P->SetPositionAbs(P->m_X + dX, P->m_Y + dY); } } @@ -375,8 +377,12 @@ void GUIPanel::CenterInParent(bool centerX, bool centerY) { int newRelX = m_X - m_Parent->GetXPos(); int newRelY = m_Y - m_Parent->GetYPos(); - if (centerX) { newRelX = (m_Parent->GetWidth() / 2) - (GetWidth() / 2); } - if (centerY) { newRelY = (m_Parent->GetHeight() / 2) - (GetHeight() / 2); } + if (centerX) { + newRelX = (m_Parent->GetWidth() / 2) - (GetWidth() / 2); + } + if (centerY) { + newRelY = (m_Parent->GetHeight() / 2) - (GetHeight() / 2); + } SetPositionRel(newRelX, newRelY); } @@ -419,7 +425,7 @@ int GUIPanel::GetHeight() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIRect * GUIPanel::GetRect() { +GUIRect* GUIPanel::GetRect() { SetRect(&m_Rect, m_X, m_Y, m_X + m_Width, m_Y + m_Height); return &m_Rect; @@ -427,11 +433,19 @@ GUIRect * GUIPanel::GetRect() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::GetRect(int *X, int *Y, int *Width, int *Height) const { - if (X) { *X = m_X; } - if (Y) { *Y = m_Y; } - if (Width) { *Width = m_Width; } - if (Height) { *Height = m_Height; } +void GUIPanel::GetRect(int* X, int* Y, int* Width, int* Height) const { + if (X) { + *X = m_X; + } + if (Y) { + *Y = m_Y; + } + if (Width) { + *Width = m_Width; + } + if (Height) { + *Height = m_Height; + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -479,22 +493,26 @@ bool GUIPanel::IsEnabled() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIPanel::SendSignal(int Code, int Data) { - if (m_SignalTarget) { m_SignalTarget->ReceiveSignal(this, Code, Data); } + if (m_SignalTarget) { + m_SignalTarget->ReceiveSignal(this, Code, Data); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::ReceiveSignal(GUIPanel *Source, int Code, int Data) {} +void GUIPanel::ReceiveSignal(GUIPanel* Source, int Code, int Data) {} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::SetSignalTarget(GUIPanel *Target) { - if (Target) { m_SignalTarget = Target; } +void GUIPanel::SetSignalTarget(GUIPanel* Target) { + if (Target) { + m_SignalTarget = Target; + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIPanel::GetParentPanel() { +GUIPanel* GUIPanel::GetParentPanel() { return m_Parent; } @@ -515,7 +533,7 @@ int GUIPanel::GetZPos() const { void GUIPanel::ChangeZPosition(int Type) { // If we don't have a parent, get the manager to alter the Z Position if (!m_Parent) { - //m_Manager->ChangeZPosition(this, Type); + // m_Manager->ChangeZPosition(this, Type); return; } @@ -525,16 +543,16 @@ void GUIPanel::ChangeZPosition(int Type) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::_ChangeZ(GUIPanel *Child, int Type) { +void GUIPanel::_ChangeZ(GUIPanel* Child, int Type) { assert(Child); int Index = -1; // Find the child in our children list - std::vector::iterator it; + std::vector::iterator it; int Count = 0; for (it = m_Children.begin(); it != m_Children.end(); it++, Count++) { - const GUIPanel *P = *it; + const GUIPanel* P = *it; if (P && P->GetPanelID() == Child->GetPanelID()) { Index = Count; break; @@ -565,8 +583,10 @@ void GUIPanel::_ChangeZ(GUIPanel *Child, int Type) { // Go through and re-order the Z positions Count = 0; for (it = m_Children.begin(); it != m_Children.end(); it++, Count++) { - GUIPanel *P = *it; - if (P) { P->SetZPos(Count); } + GUIPanel* P = *it; + if (P) { + P->SetZPos(Count); + } } } @@ -596,7 +616,7 @@ std::string GUIPanel::ToString() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::BuildProperties(GUIProperties *Prop) { +void GUIPanel::BuildProperties(GUIProperties* Prop) { assert(Prop); // Subtract the position from the parent @@ -618,7 +638,7 @@ void GUIPanel::BuildProperties(GUIProperties *Prop) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -std::string GUIPanel::WriteValue(const std::string &Name, int Value) { +std::string GUIPanel::WriteValue(const std::string& Name, int Value) { char buf[32]; std::string OutString = Name; @@ -633,7 +653,7 @@ std::string GUIPanel::WriteValue(const std::string &Name, int Value) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -std::string GUIPanel::WriteValue(const std::string &Name, bool Value) { +std::string GUIPanel::WriteValue(const std::string& Name, bool Value) { std::string OutString = Name; OutString += " = "; OutString += (Value ? "True" : "False"); @@ -644,7 +664,7 @@ std::string GUIPanel::WriteValue(const std::string &Name, bool Value) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPanel::_ApplyProperties(GUIProperties *Props) { +void GUIPanel::_ApplyProperties(GUIProperties* Props) { assert(Props); Props->GetValue("Visible", &m_Visible); diff --git a/Source/GUI/GUIPanel.h b/Source/GUI/GUIPanel.h index 2331242f14..73136196ef 100644 --- a/Source/GUI/GUIPanel.h +++ b/Source/GUI/GUIPanel.h @@ -3,674 +3,608 @@ namespace RTE { -class GUIPanel; -class GUIManager; - -/// -/// A rectangle 'window' in the GUI that recieves mouse and keyboard events. -/// -class GUIPanel { - -public: - - // Mouse buttons - enum { - MOUSE_NONE = 0x00, - MOUSE_LEFT = 0x01, - MOUSE_MIDDLE = 0x02, - MOUSE_RIGHT = 0x04, - } MouseButtons; - - // Mouse Modifiers - enum { - MODI_NONE = 0x00, - MODI_SHIFT = 0x01, - MODI_CTRL = 0x02, - MODI_ALT = 0x04, - MODI_COMMAND = 0x08 - } MouseModifiers; - - // Z Change - enum { - TopMost=0, - BottomMost, - } ZChange; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIPanel object in system -// memory. -// Arguments: Manager. - - explicit GUIPanel(GUIManager *Manager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIPanel object in system -// memory. -// Arguments: None. - - GUIPanel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the settings. -// Arguments: None. - - void Clear(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BottomPanelUnderPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Recursively goes down the tree to check the last panel under a point -// Arguments: X, Y Coordinates of point -// Return value: A pointer to the panel. 0 if no panel is under the point - - GUIPanel *BottomPanelUnderPoint(int x, int y); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TopPanelUnderPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Recursively goes up the tree from to check the first panel under a point -// Arguments: X, Y Coordinates of point -// Return value: A pointer to the panel. 0 if no panel is under the point - - GUIPanel *TopPanelUnderPoint(int x, int y); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddChild -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a child to this panel -// Arguments: Pointer to the panel to add - - void AddChild(GUIPanel *child, bool convertToAbsolutePos = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveChild -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a child based on name. -// Arguments: Child Name. - - void RemoveChild(const GUIPanel *pChild); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Setup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets up the panel for use with the manager. -// Arguments: Pointer to the manager to use, ZPosition. - - void Setup(GUIManager *manager, int ZPos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads the base data from a properties page -// Arguments: Pointer to the properties class - - void LoadProperties(GUIProperties *Props); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Invalidate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Invalidates the panel -// Arguments: None. - - void Invalidate(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsValid -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if the panel is valid -// Arguments: None. - - bool IsValid() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - virtual void Draw(GUIScreen *Screen); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - virtual void OnMouseDown(int X, int Y, int Buttons, int Modifier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - virtual void OnMouseUp(int X, int Y, int Buttons, int Modifier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnDoubleClick -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse has double-clicked on the pane. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - virtual void OnDoubleClick(int X, int Y, int Buttons, int Modifier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnMouseMove -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse moves (over the panel, or when captured). -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - virtual void OnMouseMove(int X, int Y, int Buttons, int Modifier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnMouseEnter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse enters the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - virtual void OnMouseEnter(int X, int Y, int Buttons, int Modifier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnMouseLeave -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse leaves the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - virtual void OnMouseLeave(int X, int Y, int Buttons, int Modifier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnMouseHover -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse is hovering over the panel (has to be enabled) -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - virtual void OnMouseHover(int X, int Y, int Buttons, int Modifier); - - - /// - /// Called when the mouse scroll wheel is moved. - /// - /// Mouse X position. - /// Mouse Y position. - /// Activated modifier buttons. - /// The amount of wheel movement. Positive is scroll up, negative is scroll down. - virtual void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) {}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnKeyDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when a key goes down. -// Arguments: KeyCode, Modifier. - - virtual void OnKeyDown(int KeyCode, int Modifier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnKeyUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when a key goes up. -// Arguments: KeyCode, Modifier. - - virtual void OnKeyUp(int KeyCode, int Modifier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnKeyPress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when a key is pressed (OnDown & repeating). -// Arguments: KeyCode, Modifier. - - virtual void OnKeyPress(int KeyCode, int Modifier); + class GUIPanel; + class GUIManager; /// - /// Called when text input is received. + /// A rectangle 'window' in the GUI that recieves mouse and keyboard events. /// - /// The input text being received. - virtual void OnTextInput(std::string_view inputText); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnGainFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the panel gains focus. -// Arguments: None. - - virtual void OnGainFocus(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: OnLoseFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the panel looses focus. -// Arguments: None. - - virtual void OnLoseFocus(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: SetSize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the size of the panel. -// Arguments: Width, Height. - - virtual void SetSize(int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: SetPositionAbs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the absolute position of the panel. -// Arguments: X, Y, and whether to move the children too - - virtual void SetPositionAbs(int X, int Y, bool moveChildren = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: SetPositionRel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the position of the panel, relative to its parent. -// Arguments: X, Y. - - virtual void SetPositionRel(int X, int Y); + class GUIPanel { + + public: + // Mouse buttons + enum { + MOUSE_NONE = 0x00, + MOUSE_LEFT = 0x01, + MOUSE_MIDDLE = 0x02, + MOUSE_RIGHT = 0x04, + } MouseButtons; + + // Mouse Modifiers + enum { + MODI_NONE = 0x00, + MODI_SHIFT = 0x01, + MODI_CTRL = 0x02, + MODI_ALT = 0x04, + MODI_COMMAND = 0x08 + } MouseModifiers; + + // Z Change + enum { + TopMost = 0, + BottomMost, + } ZChange; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIPanel object in system + // memory. + // Arguments: Manager. + + explicit GUIPanel(GUIManager* Manager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIPanel object in system + // memory. + // Arguments: None. + + GUIPanel(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the settings. + // Arguments: None. + + void Clear(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BottomPanelUnderPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Recursively goes down the tree to check the last panel under a point + // Arguments: X, Y Coordinates of point + // Return value: A pointer to the panel. 0 if no panel is under the point + + GUIPanel* BottomPanelUnderPoint(int x, int y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TopPanelUnderPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Recursively goes up the tree from to check the first panel under a point + // Arguments: X, Y Coordinates of point + // Return value: A pointer to the panel. 0 if no panel is under the point + + GUIPanel* TopPanelUnderPoint(int x, int y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddChild + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a child to this panel + // Arguments: Pointer to the panel to add + + void AddChild(GUIPanel* child, bool convertToAbsolutePos = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveChild + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a child based on name. + // Arguments: Child Name. + + void RemoveChild(const GUIPanel* pChild); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Setup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets up the panel for use with the manager. + // Arguments: Pointer to the manager to use, ZPosition. + + void Setup(GUIManager* manager, int ZPos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads the base data from a properties page + // Arguments: Pointer to the properties class + + void LoadProperties(GUIProperties* Props); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Invalidate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Invalidates the panel + // Arguments: None. + + void Invalidate(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsValid + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if the panel is valid + // Arguments: None. + + bool IsValid() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + virtual void Draw(GUIScreen* Screen); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + virtual void OnMouseDown(int X, int Y, int Buttons, int Modifier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + virtual void OnMouseUp(int X, int Y, int Buttons, int Modifier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnDoubleClick + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse has double-clicked on the pane. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + virtual void OnDoubleClick(int X, int Y, int Buttons, int Modifier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnMouseMove + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse moves (over the panel, or when captured). + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + virtual void OnMouseMove(int X, int Y, int Buttons, int Modifier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnMouseEnter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse enters the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + virtual void OnMouseEnter(int X, int Y, int Buttons, int Modifier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnMouseLeave + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse leaves the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + virtual void OnMouseLeave(int X, int Y, int Buttons, int Modifier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnMouseHover + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse is hovering over the panel (has to be enabled) + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + virtual void OnMouseHover(int X, int Y, int Buttons, int Modifier); + + /// + /// Called when the mouse scroll wheel is moved. + /// + /// Mouse X position. + /// Mouse Y position. + /// Activated modifier buttons. + /// The amount of wheel movement. Positive is scroll up, negative is scroll down. + virtual void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange){}; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnKeyDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when a key goes down. + // Arguments: KeyCode, Modifier. + + virtual void OnKeyDown(int KeyCode, int Modifier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnKeyUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when a key goes up. + // Arguments: KeyCode, Modifier. + + virtual void OnKeyUp(int KeyCode, int Modifier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnKeyPress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when a key is pressed (OnDown & repeating). + // Arguments: KeyCode, Modifier. + + virtual void OnKeyPress(int KeyCode, int Modifier); + + /// + /// Called when text input is received. + /// + /// The input text being received. + virtual void OnTextInput(std::string_view inputText); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnGainFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the panel gains focus. + // Arguments: None. + + virtual void OnGainFocus(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: OnLoseFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the panel looses focus. + // Arguments: None. + + virtual void OnLoseFocus(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: SetSize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the size of the panel. + // Arguments: Width, Height. + + virtual void SetSize(int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: SetPositionAbs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the absolute position of the panel. + // Arguments: X, Y, and whether to move the children too + + virtual void SetPositionAbs(int X, int Y, bool moveChildren = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: SetPositionRel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the position of the panel, relative to its parent. + // Arguments: X, Y. + + virtual void SetPositionRel(int X, int Y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: MoveRelative + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Moves the position of the panel by a relative amount. + // Arguments: X, Y, relative. + + virtual void MoveRelative(int dX, int dY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: CenterInParent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Centers this in its parent, taking this' dimensions into consideration. + // Arguments: Which axes to center. + + virtual void CenterInParent(bool centerX, bool centerY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: ReceiveSignal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when receiving a signal. + // Arguments: Signal source, Signal code, Signal data. + + virtual void ReceiveSignal(GUIPanel* Source, int Code, int Data); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CaptureMouse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Captures the mouse. + // Arguments: None. + + void CaptureMouse(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReleaseMouse + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Releases the mouse. + // Arguments: None. + + void ReleaseMouse(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual Method: SetFont + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the font this panel will be using + // Arguments: The new font, ownership is NOT transferred! + + virtual void SetFont(GUIFont* pFont) { + m_Font = pFont; + m_ValidRegion = false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: _SetVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the visibility of the panel. + // Arguments: Visible. + + void _SetVisible(bool Visible); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: _GetVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the visibility of the panel. + // Arguments: None. + bool _GetVisible() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: _SetEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the enabled state of the panel. + // Arguments: Enabled. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: MoveRelative -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Moves the position of the panel by a relative amount. -// Arguments: X, Y, relative. + void _SetEnabled(bool Enabled); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: _GetEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the enabled state of the panel. + // Arguments: None. - virtual void MoveRelative(int dX, int dY); + bool _GetEnabled() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the width of the panel. + // Arguments: None. + int GetWidth() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the height of the panel. + // Arguments: None. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: CenterInParent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Centers this in its parent, taking this' dimensions into consideration. -// Arguments: Which axes to center. + int GetHeight() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetXPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the x position of the panel + // Arguments: None. - virtual void CenterInParent(bool centerX, bool centerY); + int GetXPos() const { return m_X; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetYPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the y position of the panel + // Arguments: None. + int GetYPos() const { return m_Y; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRelXPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the x position of the panel, relative to its parent + // Arguments: None. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: ReceiveSignal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when receiving a signal. -// Arguments: Signal source, Signal code, Signal data. + int GetRelXPos() const { return m_X - m_Parent->GetXPos(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRelYPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the y position of the panel, relative to its parent + // Arguments: None. - virtual void ReceiveSignal(GUIPanel *Source, int Code, int Data); + int GetRelYPos() const { return m_Y - m_Parent->GetYPos(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the panel. + // Arguments: None. + GUIRect* GetRect(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the panel. + // Arguments: X, Y, Width, Height -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CaptureMouse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Captures the mouse. -// Arguments: None. + void GetRect(int* X, int* Y, int* Width, int* Height) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetParentPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the parent of this panel. + // Arguments: None. - void CaptureMouse(); + GUIPanel* GetParentPanel(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanelID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the panel's ID. + // Arguments: None. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReleaseMouse -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Releases the mouse. -// Arguments: None. + int GetPanelID() const; - void ReleaseMouse(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCaptureState + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the panel's captured state. + // Arguments: Captured. + + void SetCaptureState(bool Captured); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsCaptured + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the panel's captured state. + // Arguments: None. + + bool IsCaptured() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the panel's enabled state. + // Arguments: None. + + bool IsEnabled() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSignalTarget + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the target panel to receive signals. + // Arguments: Target panel. + + void SetSignalTarget(GUIPanel* Target); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PointInside + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if a point is inside the panel + // Arguments: X, Y Coordinates of point + // Return value: A boolean of the check result + + virtual bool PointInside(int X, int Y); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the focus of this panel. + // Arguments: None. + + void SetFocus(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the focus value of the panel. + // Arguments: None. + + bool HasFocus() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetZPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the Z index of the panel. + // Arguments: ZPos. + + void SetZPos(int Z); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetZPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the Z index of the panel. + // Arguments: None. + + int GetZPos() const; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: SetFont -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the font this panel will be using -// Arguments: The new font, ownership is NOT transferred! + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeZPosition + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes the Z Position of the panel. + // Arguments: Change type. + + void ChangeZPosition(int Type); - virtual void SetFont(GUIFont *pFont) { m_Font = pFont; m_ValidRegion = false; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ToString + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Convert the properties in the panel to a string. + // Arguments: None. + + std::string ToString(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds this panels properties to a properties class. + // Arguments: GUIProperties. + + void BuildProperties(GUIProperties* Prop); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: _SetVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the visibility of the panel. -// Arguments: Visible. + protected: + int m_X; // absolute coordinates + int m_Y; + int m_Width; + int m_Height; + + bool m_Visible; + bool m_Enabled; + bool m_GotFocus; + bool m_Captured; + GUIManager* m_Manager; + GUIPanel* m_Parent; + + GUIFont* m_Font; + unsigned long m_FontColor; + unsigned long m_FontShadow; + int m_FontKerning; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SendSignal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sends a signal to the target. + // Arguments: Signal code, Data. - void _SetVisible(bool Visible); + void SendSignal(int Code, int Data); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TrackMouseHover + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets up the manager to enable/disable hover tracking of this panel + // Arguments: Enabled, Delay (milliseconds) + void TrackMouseHover(bool Enabled, int Delay); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: _ChangeZ + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes the Z position of a child panel. + // Arguments: Child panel, Change type. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: _GetVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the visibility of the panel. -// Arguments: None. + void _ChangeZ(GUIPanel* Child, int Type); - bool _GetVisible() const; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WriteValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Writes a single value to string. + // Arguments: Value name, Value. + std::string WriteValue(const std::string& Name, int Value); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: _SetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the enabled state of the panel. -// Arguments: Enabled. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WriteValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Writes a single value to string. + // Arguments: Value name, Value. - void _SetEnabled(bool Enabled); + std::string WriteValue(const std::string& Name, bool Value); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: _ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the panel. + // Arguments: GUIProperties. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: _GetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the enabled state of the panel. -// Arguments: None. - - bool _GetEnabled() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the width of the panel. -// Arguments: None. - - int GetWidth() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the height of the panel. -// Arguments: None. - - int GetHeight() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetXPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the x position of the panel -// Arguments: None. - - int GetXPos() const { return m_X; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetYPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the y position of the panel -// Arguments: None. - - int GetYPos() const { return m_Y; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRelXPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the x position of the panel, relative to its parent -// Arguments: None. - - int GetRelXPos() const { return m_X - m_Parent->GetXPos(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRelYPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the y position of the panel, relative to its parent -// Arguments: None. - - int GetRelYPos() const { return m_Y - m_Parent->GetYPos(); } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the panel. -// Arguments: None. - - GUIRect * GetRect(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the panel. -// Arguments: X, Y, Width, Height - - void GetRect(int *X, int *Y, int *Width, int *Height) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetParentPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the parent of this panel. -// Arguments: None. - - GUIPanel * GetParentPanel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanelID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the panel's ID. -// Arguments: None. - - int GetPanelID() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCaptureState -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the panel's captured state. -// Arguments: Captured. - - void SetCaptureState(bool Captured); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsCaptured -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the panel's captured state. -// Arguments: None. - - bool IsCaptured() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the panel's enabled state. -// Arguments: None. - - bool IsEnabled() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSignalTarget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the target panel to receive signals. -// Arguments: Target panel. - - void SetSignalTarget(GUIPanel *Target); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PointInside -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if a point is inside the panel -// Arguments: X, Y Coordinates of point -// Return value: A boolean of the check result - - virtual bool PointInside(int X, int Y); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the focus of this panel. -// Arguments: None. - - void SetFocus(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the focus value of the panel. -// Arguments: None. - - bool HasFocus() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetZPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the Z index of the panel. -// Arguments: ZPos. - - void SetZPos(int Z); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetZPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the Z index of the panel. -// Arguments: None. - - int GetZPos() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeZPosition -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes the Z Position of the panel. -// Arguments: Change type. - - void ChangeZPosition(int Type); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ToString -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Convert the properties in the panel to a string. -// Arguments: None. - - std::string ToString(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds this panels properties to a properties class. -// Arguments: GUIProperties. - - void BuildProperties(GUIProperties *Prop); - -protected: - - int m_X; // absolute coordinates - int m_Y; - int m_Width; - int m_Height; - - bool m_Visible; - bool m_Enabled; - bool m_GotFocus; - bool m_Captured; - GUIManager *m_Manager; - GUIPanel *m_Parent; - - GUIFont *m_Font; - unsigned long m_FontColor; - unsigned long m_FontShadow; - int m_FontKerning; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SendSignal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sends a signal to the target. -// Arguments: Signal code, Data. - - void SendSignal(int Code, int Data); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TrackMouseHover -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets up the manager to enable/disable hover tracking of this panel -// Arguments: Enabled, Delay (milliseconds) - - void TrackMouseHover(bool Enabled, int Delay); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: _ChangeZ -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes the Z position of a child panel. -// Arguments: Child panel, Change type. - - void _ChangeZ(GUIPanel *Child, int Type); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WriteValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Writes a single value to string. -// Arguments: Value name, Value. - - std::string WriteValue(const std::string &Name, int Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WriteValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Writes a single value to string. -// Arguments: Value name, Value. - - std::string WriteValue(const std::string &Name, bool Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: _ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the panel. -// Arguments: GUIProperties. - - void _ApplyProperties(GUIProperties *Props); + void _ApplyProperties(GUIProperties* Props); private: - - std::vector m_Children; + std::vector m_Children; GUIRect m_Rect; int m_ID; bool m_ValidRegion; int m_ZPos; - GUIPanel *m_SignalTarget; -}; -}; + GUIPanel* m_SignalTarget; + }; +}; // namespace RTE #endif diff --git a/Source/GUI/GUIProgressBar.cpp b/Source/GUI/GUIProgressBar.cpp index 0e0b340dfe..68c60ecda3 100644 --- a/Source/GUI/GUIProgressBar.cpp +++ b/Source/GUI/GUIProgressBar.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIProgressBar::GUIProgressBar(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUIProgressBar::GUIProgressBar(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "PROGRESSBAR"; m_DrawBitmap = nullptr; m_IndicatorImage = nullptr; @@ -18,7 +19,7 @@ GUIProgressBar::GUIProgressBar(GUIManager *Manager, GUIControlManager *ControlMa ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProgressBar::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUIProgressBar::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -35,8 +36,12 @@ void GUIProgressBar::Create(const std::string &Name, int X, int Y, int Width, in m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Make sure the control isn't too small m_Width = std::max(m_Width, m_MinWidth); @@ -45,7 +50,7 @@ void GUIProgressBar::Create(const std::string &Name, int X, int Y, int Width, in ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProgressBar::Create(GUIProperties *Props) { +void GUIProgressBar::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -92,7 +97,7 @@ void GUIProgressBar::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProgressBar::ChangeSkin(GUISkin *Skin) { +void GUIProgressBar::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Build the progressbar bitmap @@ -123,7 +128,7 @@ void GUIProgressBar::BuildBitmap() { // Build the indicator std::string Filename; m_Skin->GetValue("ProgressBar_Indicator", "Filename", &Filename); - GUIBitmap *Src = m_Skin->CreateBitmap(Filename); + GUIBitmap* Src = m_Skin->CreateBitmap(Filename); if (!Src) { return; } @@ -160,7 +165,7 @@ void GUIProgressBar::BuildBitmap() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProgressBar::Draw(GUIScreen *Screen) { +void GUIProgressBar::Draw(GUIScreen* Screen) { // Draw the base Screen->DrawBitmap(m_DrawBitmap, m_X, m_Y, nullptr); @@ -172,9 +177,11 @@ void GUIProgressBar::Draw(GUIScreen *Screen) { float Count = 0; if (m_Maximum - m_Minimum > 0) { float V = (float)(m_Value - m_Minimum) / (float)(m_Maximum - m_Minimum); - Count = (float)m_Width*V; + Count = (float)m_Width * V; + } + if (m_IndicatorImage->GetWidth() + m_Spacing > 0) { + Count = Count / (float)(m_IndicatorImage->GetWidth() + m_Spacing); } - if (m_IndicatorImage->GetWidth() + m_Spacing > 0) { Count = Count / (float)(m_IndicatorImage->GetWidth() + m_Spacing); } // Setup the clipping GUIRect Rect = *GetRect(); @@ -204,7 +211,9 @@ void GUIProgressBar::OnMouseDown(int X, int Y, int Buttons, int Modifier) { void GUIProgressBar::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); - if (PointInside(X, Y)) { AddEvent(GUIEvent::Notification, Clicked, Buttons); } + if (PointInside(X, Y)) { + AddEvent(GUIEvent::Notification, Clicked, Buttons); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -213,7 +222,7 @@ void GUIProgressBar::OnMouseMove(int X, int Y, int Buttons, int Modifier) {} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIProgressBar::GetPanel() { +GUIPanel* GUIProgressBar::GetPanel() { return this; } @@ -238,7 +247,7 @@ void GUIProgressBar::Resize(int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProgressBar::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUIProgressBar::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } @@ -261,7 +270,9 @@ void GUIProgressBar::SetValue(int Value) { m_Value = std::max(m_Value, m_Minimum); // Changed? - if (m_Value != OldValue) { AddEvent(GUIEvent::Notification, Changed, 0); } + if (m_Value != OldValue) { + AddEvent(GUIEvent::Notification, Changed, 0); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -296,7 +307,7 @@ int GUIProgressBar::GetMaximum() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProgressBar::ApplyProperties(GUIProperties *Props) { +void GUIProgressBar::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); m_Properties.GetValue("Minimum", &m_Minimum); diff --git a/Source/GUI/GUIProgressBar.h b/Source/GUI/GUIProgressBar.h index 4df813a6ea..7d1597da4e 100644 --- a/Source/GUI/GUIProgressBar.h +++ b/Source/GUI/GUIProgressBar.h @@ -3,236 +3,212 @@ namespace RTE { -/// -/// A progressbar control class. -/// -class GUIProgressBar : public GUIControl, public GUIPanel { + /// + /// A progressbar control class. + /// + class GUIProgressBar : public GUIControl, public GUIPanel { + + public: + // Progressbar Notifications + enum { + Clicked = 0, + Changed + } Notification; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIProgressBar + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIProgressBar object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUIProgressBar(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseMove + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse moves (over the panel, or when captured). + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "PROGRESSBAR"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; -public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - // Progressbar Notifications - enum { - Clicked = 0, - Changed - } Notification; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the value. + // Arguments: Value. + + void SetValue(int Value); -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIProgressBar -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIProgressBar object in -// system memory. -// Arguments: GUIManager, GUIControlManager. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the value. + // Arguments: None. + + int GetValue() const; - GUIProgressBar(GUIManager *Manager, GUIControlManager *ControlManager); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMinimum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the minimum. + // Arguments: Minimum. + + void SetMinimum(int Minimum); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMinimum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the minimum. + // Arguments: None. + + int GetMinimum() const; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMaximum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the maximum. + // Arguments: Maximum. + + void SetMaximum(int Maximum); - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMaximum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the maximum. + // Arguments: None. + + int GetMaximum() const; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. + private: + GUIBitmap* m_DrawBitmap; + GUIBitmap* m_IndicatorImage; - void Create(GUIProperties *Props) override; + int m_Minimum; + int m_Maximum; + int m_Value; + int m_Spacing; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. - - void Destroy() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseMove -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse moves (over the panel, or when captured). -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "PROGRESSBAR"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the value. -// Arguments: Value. - - void SetValue(int Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the value. -// Arguments: None. - - int GetValue() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMinimum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the minimum. -// Arguments: Minimum. - - void SetMinimum(int Minimum); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMinimum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the minimum. -// Arguments: None. - - int GetMinimum() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMaximum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the maximum. -// Arguments: Maximum. - - void SetMaximum(int Maximum); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaximum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the maximum. -// Arguments: None. - - int GetMaximum() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - -private: - - GUIBitmap *m_DrawBitmap; - GUIBitmap *m_IndicatorImage; - - int m_Minimum; - int m_Maximum; - int m_Value; - int m_Spacing; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the progressbar bitmap to draw. -// Arguments: None. - - void BuildBitmap(); -}; -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the progressbar bitmap to draw. + // Arguments: None. + + void BuildBitmap(); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIProperties.cpp b/Source/GUI/GUIProperties.cpp index 406abdf727..622320d54a 100644 --- a/Source/GUI/GUIProperties.cpp +++ b/Source/GUI/GUIProperties.cpp @@ -4,7 +4,7 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIProperties::GUIProperties(const std::string &Name) { +GUIProperties::GUIProperties(const std::string& Name) { m_Name = Name; m_VariableList.clear(); } @@ -26,10 +26,10 @@ GUIProperties::~GUIProperties() { void GUIProperties::Clear() { // Free the list - std::vector ::iterator it; + std::vector::iterator it; for (it = m_VariableList.begin(); it != m_VariableList.end(); it++) { - PropVariable *p = *it; + PropVariable* p = *it; // Free the property delete p; @@ -39,7 +39,7 @@ void GUIProperties::Clear() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProperties::AddVariable(const std::string &Variable, const std::string &Value) { +void GUIProperties::AddVariable(const std::string& Variable, const std::string& Value) { // If this property already exists, just update it std::string Val; if (GetValue(Variable, &Val)) { @@ -47,7 +47,7 @@ void GUIProperties::AddVariable(const std::string &Variable, const std::string & return; } - PropVariable *Prop = new PropVariable; + PropVariable* Prop = new PropVariable; Prop->m_Name = Variable; Prop->m_Value = Value; @@ -57,7 +57,7 @@ void GUIProperties::AddVariable(const std::string &Variable, const std::string & ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProperties::AddVariable(const std::string &Variable, char *Value) { +void GUIProperties::AddVariable(const std::string& Variable, char* Value) { std::string Val = Value; AddVariable(Variable, Val); @@ -65,7 +65,7 @@ void GUIProperties::AddVariable(const std::string &Variable, char *Value) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProperties::AddVariable(const std::string &Variable, int Value) { +void GUIProperties::AddVariable(const std::string& Variable, int Value) { char buf[32]; std::snprintf(buf, sizeof(buf), "%i", Value); std::string Val(buf); @@ -74,19 +74,19 @@ void GUIProperties::AddVariable(const std::string &Variable, int Value) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProperties::AddVariable(const std::string &Variable, bool Value) { +void GUIProperties::AddVariable(const std::string& Variable, bool Value) { std::string Val = Value ? "True" : "False"; AddVariable(Variable, Val); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIProperties::SetValue(const std::string &Variable, const std::string &Value) { +bool GUIProperties::SetValue(const std::string& Variable, const std::string& Value) { // Find the property - std::vector ::iterator it; + std::vector::iterator it; for (it = m_VariableList.begin(); it != m_VariableList.end(); it++) { - PropVariable *p = *it; + PropVariable* p = *it; // Matching name? if (stricmp(p->m_Name.c_str(), Variable.c_str()) == 0) { @@ -101,7 +101,7 @@ bool GUIProperties::SetValue(const std::string &Variable, const std::string &Val ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIProperties::SetValue(const std::string &Variable, int Value) { +bool GUIProperties::SetValue(const std::string& Variable, int Value) { char buf[64]; std::snprintf(buf, sizeof(buf), "%i", Value); @@ -110,27 +110,29 @@ bool GUIProperties::SetValue(const std::string &Variable, int Value) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIProperties::Update(GUIProperties *Props, bool Add) { +void GUIProperties::Update(GUIProperties* Props, bool Add) { assert(Props); - std::vector ::iterator it1; + std::vector::iterator it1; for (it1 = Props->m_VariableList.begin(); it1 != Props->m_VariableList.end(); it1++) { - const PropVariable *Src = *it1; + const PropVariable* Src = *it1; // Set the variable - if (!SetValue(Src->m_Name, Src->m_Value) && Add) { AddVariable(Src->m_Name, Src->m_Value); } + if (!SetValue(Src->m_Name, Src->m_Value) && Add) { + AddVariable(Src->m_Name, Src->m_Value); + } } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIProperties::GetValue(const std::string &Variable, std::string *Value) { +bool GUIProperties::GetValue(const std::string& Variable, std::string* Value) { // Find the property - std::vector ::iterator it; + std::vector::iterator it; for (it = m_VariableList.begin(); it != m_VariableList.end(); it++) { - const PropVariable *p = *it; + const PropVariable* p = *it; // Matching name? if (stricmp(p->m_Name.c_str(), Variable.c_str()) == 0) { @@ -145,7 +147,7 @@ bool GUIProperties::GetValue(const std::string &Variable, std::string *Value) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int GUIProperties::GetValue(const std::string &Variable, std::string *Array, int MaxArraySize) { +int GUIProperties::GetValue(const std::string& Variable, std::string* Array, int MaxArraySize) { assert(Array); std::string Value; @@ -155,13 +157,13 @@ int GUIProperties::GetValue(const std::string &Variable, std::string *Array, int return 0; } // Create a c string version of the value for tokenizing - char *str = new char[Value.length() + 1]; + char* str = new char[Value.length() + 1]; if (!str) { return 0; } // Tokenize the string strcpy(str, Value.c_str()); - char *tok = strtok(str, ","); + char* tok = strtok(str, ","); int count = 0; while (tok && count < MaxArraySize) { @@ -177,7 +179,7 @@ int GUIProperties::GetValue(const std::string &Variable, std::string *Array, int ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int GUIProperties::GetValue(const std::string &Variable, int *Array, int MaxArraySize) { +int GUIProperties::GetValue(const std::string& Variable, int* Array, int MaxArraySize) { assert(Array); std::string Value; @@ -187,13 +189,13 @@ int GUIProperties::GetValue(const std::string &Variable, int *Array, int MaxArra return 0; } // Create a c string version of the value for tokenizing - char *str = new char[Value.length() + 1]; + char* str = new char[Value.length() + 1]; if (!str) { return 0; } // Tokenize the string strcpy(str, Value.c_str()); - char *tok = strtok(str, ","); + char* tok = strtok(str, ","); int count = 0; while (tok && count < MaxArraySize) { @@ -209,7 +211,7 @@ int GUIProperties::GetValue(const std::string &Variable, int *Array, int MaxArra ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIProperties::GetValue(const std::string &Variable, int *Value) { +bool GUIProperties::GetValue(const std::string& Variable, int* Value) { assert(Value); std::string val; @@ -226,7 +228,7 @@ bool GUIProperties::GetValue(const std::string &Variable, int *Value) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIProperties::GetValue(const std::string &Variable, unsigned long *Value) { +bool GUIProperties::GetValue(const std::string& Variable, unsigned long* Value) { assert(Value); std::string val; @@ -243,7 +245,7 @@ bool GUIProperties::GetValue(const std::string &Variable, unsigned long *Value) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIProperties::GetValue(const std::string &Variable, bool *Value) { +bool GUIProperties::GetValue(const std::string& Variable, bool* Value) { assert(Value); std::string val; @@ -256,7 +258,9 @@ bool GUIProperties::GetValue(const std::string &Variable, bool *Value) { *Value = false; // Convert the string into a boolean - if (stricmp(val.c_str(), "true") == 0) { *Value = true; } + if (stricmp(val.c_str(), "true") == 0) { + *Value = true; + } // Found the value return true; @@ -274,9 +278,9 @@ std::string GUIProperties::ToString() { std::string OutString = ""; // Go through each value - std::vector ::iterator it; + std::vector::iterator it; for (it = m_VariableList.begin(); it != m_VariableList.end(); it++) { - const PropVariable *V = *it; + const PropVariable* V = *it; OutString += V->m_Name; OutString.append(" = "); @@ -294,27 +298,31 @@ int GUIProperties::GetCount() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIProperties::GetVariable(int Index, std::string *Name, std::string *Value) { +bool GUIProperties::GetVariable(int Index, std::string* Name, std::string* Value) { // Check for a bad index if (Index < 0 || Index >= m_VariableList.size()) { return false; } - const PropVariable *P = (PropVariable *)m_VariableList.at(Index); - if (Name) { *Name = P->m_Name; } - if (Value) { *Value = P->m_Value; } + const PropVariable* P = (PropVariable*)m_VariableList.at(Index); + if (Name) { + *Name = P->m_Name; + } + if (Value) { + *Value = P->m_Value; + } return true; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUIProperties::SetVariable(int Index, const std::string &Name, const std::string &Value) { +bool GUIProperties::SetVariable(int Index, const std::string& Name, const std::string& Value) { // Check for a bad index if (Index < 0 || Index >= m_VariableList.size()) { return false; } - PropVariable *P = m_VariableList.at(Index); + PropVariable* P = m_VariableList.at(Index); P->m_Name = Name; P->m_Value = Value; @@ -330,8 +338,8 @@ void GUIProperties::Sort(bool Ascending) { for (int j = 0; j < m_VariableList.size() - 1 - i; j++) { - PropVariable *V = m_VariableList.at(j); - PropVariable *V2 = m_VariableList.at(j + 1); + PropVariable* V = m_VariableList.at(j); + PropVariable* V2 = m_VariableList.at(j + 1); if ((V->m_Name.compare(V2->m_Name) > 0 && Ascending) || (V->m_Name.compare(V2->m_Name) < 0 && !Ascending)) { // Swap em diff --git a/Source/GUI/GUIProperties.h b/Source/GUI/GUIProperties.h index 3086a8c5eb..6e44a6b67b 100644 --- a/Source/GUI/GUIProperties.h +++ b/Source/GUI/GUIProperties.h @@ -3,233 +3,208 @@ namespace RTE { -/// -/// A class containing properties for controls and skins. -/// -class GUIProperties { + /// + /// A class containing properties for controls and skins. + /// + class GUIProperties { + + public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIProperties object in + // system memory. + // Arguments: Name of section. + + explicit GUIProperties(const std::string& Name); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIProperties object in + // system memory. + // Arguments: None. + + GUIProperties(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: GUIProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to free a GUIProperties object in system + // memory. + // Arguments: None. + + ~GUIProperties(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the properties. + // Arguments: None. + + void Clear(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddVariable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a new variable to the properties + // Arguments: Variable, Value + + void AddVariable(const std::string& Variable, const std::string& Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddVariable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a new variable to the properties + // Arguments: Variable, Value + + void AddVariable(const std::string& Variable, char* Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddVariable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a new variable to the properties + // Arguments: Variable, Value + + void AddVariable(const std::string& Variable, int Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddVariable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a new variable to the properties + // Arguments: Variable, Value + + void AddVariable(const std::string& Variable, bool Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes the value of a property. + // Arguments: Variable, Value + // Returns: True if the variable was set. Otherwise false. + + bool SetValue(const std::string& Variable, const std::string& Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes the value of a property. + // Arguments: Variable, Value + // Returns: True if the variable was set. Otherwise false. + + bool SetValue(const std::string& Variable, int Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the properties with properties from another instance. + // Arguments: Pointer to a Properties class, whether to add variables. + + void Update(GUIProperties* Props, bool Add = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a string value + // Arguments: Variable, String pointer + + bool GetValue(const std::string& Variable, std::string* Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a string array of values + // Arguments: Variable, String array, max size of array + // Returns: Number of elements read -public: + int GetValue(const std::string& Variable, std::string* Array, int MaxArraySize); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets an integer array of values + // Arguments: Variable, Integer array, max size of array + // Returns: Number of elements read + int GetValue(const std::string& Variable, int* Array, int MaxArraySize); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a single interger + // Arguments: Variable, Integer pointer -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIProperties object in -// system memory. -// Arguments: Name of section. + bool GetValue(const std::string& Variable, int* Value); - explicit GUIProperties(const std::string &Name); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a single unsigned interger + // Arguments: Variable, Unsigned Integer pointer + bool GetValue(const std::string& Variable, unsigned long* Value); -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIProperties object in -// system memory. -// Arguments: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a boolean value + // Arguments: Variable, Boolean pointer - GUIProperties(); + bool GetValue(const std::string& Variable, bool* Value); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the property name -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: GUIProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to free a GUIProperties object in system -// memory. -// Arguments: None. + std::string GetName() const; - ~GUIProperties(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ToString + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Converts the properties to a string + std::string ToString(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the properties. -// Arguments: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the variable count in the properties - void Clear(); + int GetCount() const; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetVariable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a variable based on index -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddVariable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a new variable to the properties -// Arguments: Variable, Value + bool GetVariable(int Index, std::string* Name, std::string* Value); - void AddVariable(const std::string &Variable, const std::string &Value); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetVariable + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets a variable based on index + bool SetVariable(int Index, const std::string& Name, const std::string& Value); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddVariable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a new variable to the properties -// Arguments: Variable, Value + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Sort + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sorts the list by variable name + // Arguments: True for ascending, False for descending - void AddVariable(const std::string &Variable, char *Value); + void Sort(bool Ascending); + private: + // Variable structure + typedef struct { + std::string m_Name; + std::string m_Value; + } PropVariable; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddVariable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a new variable to the properties -// Arguments: Variable, Value + std::string m_Name; - void AddVariable(const std::string &Variable, int Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddVariable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a new variable to the properties -// Arguments: Variable, Value - - void AddVariable(const std::string &Variable, bool Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes the value of a property. -// Arguments: Variable, Value -// Returns: True if the variable was set. Otherwise false. - - bool SetValue(const std::string &Variable, const std::string &Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes the value of a property. -// Arguments: Variable, Value -// Returns: True if the variable was set. Otherwise false. - - bool SetValue(const std::string &Variable, int Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the properties with properties from another instance. -// Arguments: Pointer to a Properties class, whether to add variables. - - void Update(GUIProperties *Props, bool Add = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a string value -// Arguments: Variable, String pointer - - bool GetValue(const std::string &Variable, std::string *Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a string array of values -// Arguments: Variable, String array, max size of array -// Returns: Number of elements read - - int GetValue(const std::string &Variable, std::string *Array, int MaxArraySize); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets an integer array of values -// Arguments: Variable, Integer array, max size of array -// Returns: Number of elements read - - int GetValue(const std::string &Variable, int *Array, int MaxArraySize); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a single interger -// Arguments: Variable, Integer pointer - - bool GetValue(const std::string &Variable, int *Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a single unsigned interger -// Arguments: Variable, Unsigned Integer pointer - - bool GetValue(const std::string &Variable, unsigned long *Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a boolean value -// Arguments: Variable, Boolean pointer - - bool GetValue(const std::string &Variable, bool *Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the property name - - std::string GetName() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ToString -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Converts the properties to a string - - std::string ToString(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the variable count in the properties - - int GetCount() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetVariable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a variable based on index - - bool GetVariable(int Index, std::string *Name, std::string *Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetVariable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a variable based on index - - bool SetVariable(int Index, const std::string &Name, const std::string &Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Sort -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sorts the list by variable name -// Arguments: True for ascending, False for descending - - void Sort(bool Ascending); - -private: - - // Variable structure - typedef struct { - std::string m_Name; - std::string m_Value; - } PropVariable; - - std::string m_Name; - - std::vector m_VariableList; -}; -}; + std::vector m_VariableList; + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIPropertyPage.cpp b/Source/GUI/GUIPropertyPage.cpp index a2d90e1a02..f26d85ff13 100644 --- a/Source/GUI/GUIPropertyPage.cpp +++ b/Source/GUI/GUIPropertyPage.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPropertyPage::GUIPropertyPage(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUIPropertyPage::GUIPropertyPage(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "PROPERTYPAGE"; m_DrawBitmap = nullptr; m_ControlManager = ControlManager; @@ -19,7 +20,7 @@ GUIPropertyPage::GUIPropertyPage(GUIManager *Manager, GUIControlManager *Control ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPropertyPage::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUIPropertyPage::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -36,8 +37,12 @@ void GUIPropertyPage::Create(const std::string &Name, int X, int Y, int Width, i m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Make sure the control isn't too small m_Width = std::max(m_Width, m_MinWidth); @@ -59,8 +64,8 @@ void GUIPropertyPage::Create(const std::string &Name, int X, int Y, int Width, i int Spacer = 0; int Size = m_Height / H; for (int i = 0; i < Size; i++) { - GUITextPanel *T = new GUITextPanel(m_Manager); - T->Create(m_Width / 2, i*H + Spacer, m_Width / 2, H); + GUITextPanel* T = new GUITextPanel(m_Manager); + T->Create(m_Width / 2, i * H + Spacer, m_Width / 2, H); T->_SetVisible(false); T->SetSignalTarget(this); GUIPanel::AddChild(T); @@ -71,7 +76,7 @@ void GUIPropertyPage::Create(const std::string &Name, int X, int Y, int Width, i ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPropertyPage::Create(GUIProperties *Props) { +void GUIPropertyPage::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -105,8 +110,8 @@ void GUIPropertyPage::Create(GUIProperties *Props) { int Spacer = 0; int Size = m_Height / H; for (int i = 0; i < Size; i++) { - GUITextPanel *T = new GUITextPanel(m_Manager); - T->Create(m_Width / 2, i*H + Spacer, m_Width / 2, H); + GUITextPanel* T = new GUITextPanel(m_Manager); + T->Create(m_Width / 2, i * H + Spacer, m_Width / 2, H); T->_SetVisible(false); T->SetSignalTarget(this); GUIPanel::AddChild(T); @@ -135,11 +140,11 @@ void GUIPropertyPage::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPropertyPage::ChangeSkin(GUISkin *Skin) { +void GUIPropertyPage::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Change the skin of the text panels - for (GUITextPanel *textPanel : m_TextPanelList) { + for (GUITextPanel* textPanel: m_TextPanelList) { textPanel->ChangeSkin(Skin); } @@ -162,8 +167,6 @@ void GUIPropertyPage::BuildBitmap() { m_Skin->BuildStandardRect(m_DrawBitmap, "PropertyPage", 0, 0, m_Width, m_Height); - - // Pre-cache the font std::string Filename; m_Skin->GetValue("PropertyPage", "Font", &Filename); @@ -174,7 +177,9 @@ void GUIPropertyPage::BuildBitmap() { m_FontColor = m_Skin->ConvertColor(m_FontColor, m_DrawBitmap->GetColorDepth()); m_Font = m_Skin->GetFont(Filename); - if (m_Font) { m_Font->CacheColor(m_FontColor); } + if (m_Font) { + m_Font->CacheColor(m_FontColor); + } m_Skin->GetValue("PropertyPage", "LineColor", &m_LineColor); m_LineColor = m_Skin->ConvertColor(m_LineColor, m_DrawBitmap->GetColorDepth()); @@ -182,8 +187,10 @@ void GUIPropertyPage::BuildBitmap() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPropertyPage::Draw(GUIScreen *Screen) { - if (m_DrawBitmap) { m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, nullptr); } +void GUIPropertyPage::Draw(GUIScreen* Screen) { + if (m_DrawBitmap) { + m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, nullptr); + } // Check the font first if (!m_Font) { @@ -217,10 +224,10 @@ void GUIPropertyPage::Draw(GUIScreen *Screen) { void GUIPropertyPage::OnMouseDown(int X, int Y, int Buttons, int Modifier) { if (Buttons & MOUSE_LEFT) { // Push the button down - //m_Pushed = true; - //CaptureMouse(); + // m_Pushed = true; + // CaptureMouse(); - //AddEvent(GUIEvent::Notification, Pushed, 0); + // AddEvent(GUIEvent::Notification, Pushed, 0); } SetFocus(); } @@ -249,13 +256,13 @@ void GUIPropertyPage::OnMouseMove(int X, int Y, int Buttons, int Modifier) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIPropertyPage::GetPanel() { +GUIPanel* GUIPropertyPage::GetPanel() { return this; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPropertyPage::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUIPropertyPage::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } @@ -287,13 +294,13 @@ void GUIPropertyPage::StoreProperties() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPropertyPage::SetPropertyValues(GUIProperties *Props) { +void GUIPropertyPage::SetPropertyValues(GUIProperties* Props) { m_PageValues.Clear(); m_PageValues.Update(Props, true); // Update the text panels for (int i = 0; i < m_TextPanelList.size(); i++) { - GUITextPanel *T = m_TextPanelList.at(i); + GUITextPanel* T = m_TextPanelList.at(i); T->_SetVisible(false); T->SetText(""); @@ -301,28 +308,30 @@ void GUIPropertyPage::SetPropertyValues(GUIProperties *Props) { T->_SetVisible(true); std::string Name; std::string Value; - if (m_PageValues.GetVariable(i, &Name, &Value)) { T->SetText(Value); } + if (m_PageValues.GetVariable(i, &Name, &Value)) { + T->SetText(Value); + } } } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIProperties * GUIPropertyPage::GetPropertyValues() { +GUIProperties* GUIPropertyPage::GetPropertyValues() { return &m_PageValues; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIPropertyPage::ReceiveSignal(GUIPanel *Source, int Code, int Data) { +void GUIPropertyPage::ReceiveSignal(GUIPanel* Source, int Code, int Data) { assert(Source); bool TextSignal = false; // Is this a text panel? - std::vector::iterator it; + std::vector::iterator it; for (it = m_TextPanelList.begin(); it != m_TextPanelList.end(); it++) { - const GUITextPanel *T = *it; + const GUITextPanel* T = *it; if (Source->GetPanelID() == T->GetPanelID()) { TextSignal = true; @@ -350,13 +359,15 @@ bool GUIPropertyPage::InvokeUpdate() { bool Changed = false; for (int i = 0; i < m_TextPanelList.size(); i++) { - const GUITextPanel *T = m_TextPanelList.at(i); + const GUITextPanel* T = m_TextPanelList.at(i); if (i < m_PageValues.GetCount()) { std::string Name; std::string Value; if (m_PageValues.GetVariable(i, &Name, &Value)) { - if (T->GetText().compare(Value) != 0) { Changed = true; } + if (T->GetText().compare(Value) != 0) { + Changed = true; + } // Set the value m_PageValues.SetVariable(i, Name, T->GetText()); } @@ -372,9 +383,9 @@ void GUIPropertyPage::ClearValues() { m_PageValues.Clear(); // Hide the text panels - std::vector::iterator it; + std::vector::iterator it; for (it = m_TextPanelList.begin(); it != m_TextPanelList.end(); it++) { - GUITextPanel *T = *it; + GUITextPanel* T = *it; T->_SetVisible(false); } } @@ -382,9 +393,9 @@ void GUIPropertyPage::ClearValues() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIPropertyPage::HasTextFocus() { - std::vector::iterator it; + std::vector::iterator it; for (it = m_TextPanelList.begin(); it != m_TextPanelList.end(); it++) { - const GUITextPanel *T = *it; + const GUITextPanel* T = *it; // Visible & has focus?? if (T->_GetVisible() && T->HasFocus()) { diff --git a/Source/GUI/GUIPropertyPage.h b/Source/GUI/GUIPropertyPage.h index da59102a87..2f09a2b42d 100644 --- a/Source/GUI/GUIPropertyPage.h +++ b/Source/GUI/GUIPropertyPage.h @@ -6,245 +6,220 @@ namespace RTE { -/// -/// A property page control class. -/// -class GUIPropertyPage : public GUIControl, public GUIPanel { + /// + /// A property page control class. + /// + class GUIPropertyPage : public GUIControl, public GUIPanel { + + public: + // PropertyPage Notifications + enum { + Changed = 0, // Any text panel has changed. Property values are NOT updated + Enter // A text panel has lost focus or the enter key was hit + } Notification; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIPropertyPage + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIPropertyPage object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUIPropertyPage(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseMove + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse moves (over the panel, or when captured). + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseEnter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse enters the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseLeave + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse leaves the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "PROPERTYPAGE"; }; -public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - // PropertyPage Notifications - enum { - Changed = 0, // Any text panel has changed. Property values are NOT updated - Enter // A text panel has lost focus or the enter key was hit - } Notification; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIPropertyPage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIPropertyPage object in -// system memory. -// Arguments: GUIManager, GUIControlManager. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; - GUIPropertyPage(GUIManager *Manager, GUIControlManager *ControlManager); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPropertyValues + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Refreshes the page with new variables & values. + // Arguments: GUIProperties. + + void SetPropertyValues(GUIProperties* Props); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPropertyValues + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the properties in the page. + // Arguments: None. + + GUIProperties* GetPropertyValues(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReceiveSignal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when receiving a signal. + // Arguments: Signal source, Signal code, Signal data. + + void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearValues + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the property page values. + // Arguments: None. + + void ClearValues(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: InvokeUpdate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Invokes an explicit update on text panels to property page. + // Arguments: None. + // Returns: Boolean whether or not any values have changed. + + bool InvokeUpdate(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HasTextFocus + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks if any of the visible text panels have focus. + // Arguments: None. + + bool HasTextFocus(); - void Destroy() override; + private: + GUIBitmap* m_DrawBitmap; + unsigned long m_LineColor; + + GUIProperties m_PageValues; + std::vector m_TextPanelList; + GUIScrollPanel* m_VertScroll; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseMove -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse moves (over the panel, or when captured). -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseEnter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse enters the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseLeave -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse leaves the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "PROPERTYPAGE"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPropertyValues -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Refreshes the page with new variables & values. -// Arguments: GUIProperties. - - void SetPropertyValues(GUIProperties *Props); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPropertyValues -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the properties in the page. -// Arguments: None. - - GUIProperties * GetPropertyValues(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReceiveSignal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when receiving a signal. -// Arguments: Signal source, Signal code, Signal data. - - void ReceiveSignal(GUIPanel *Source, int Code, int Data) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearValues -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the property page values. -// Arguments: None. - - void ClearValues(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: InvokeUpdate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Invokes an explicit update on text panels to property page. -// Arguments: None. -// Returns: Boolean whether or not any values have changed. - - bool InvokeUpdate(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HasTextFocus -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks if any of the visible text panels have focus. -// Arguments: None. - - bool HasTextFocus(); - -private: - - GUIBitmap *m_DrawBitmap; - unsigned long m_LineColor; - - GUIProperties m_PageValues; - std::vector m_TextPanelList; - GUIScrollPanel *m_VertScroll; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the property page bitmap to draw. - // Arguments: None. - - void BuildBitmap(); -}; -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the property page bitmap to draw. + // Arguments: None. + + void BuildBitmap(); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIRadioButton.cpp b/Source/GUI/GUIRadioButton.cpp index 5ba54eb5d6..f641fc4907 100644 --- a/Source/GUI/GUIRadioButton.cpp +++ b/Source/GUI/GUIRadioButton.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIRadioButton::GUIRadioButton(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUIRadioButton::GUIRadioButton(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "RADIOBUTTON"; m_Image = nullptr; m_ControlManager = ControlManager; @@ -18,7 +19,7 @@ GUIRadioButton::GUIRadioButton(GUIManager *Manager, GUIControlManager *ControlMa ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIRadioButton::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUIRadioButton::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -35,8 +36,12 @@ void GUIRadioButton::Create(const std::string &Name, int X, int Y, int Width, in m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Make sure the button isn't too small m_Width = std::max(m_Width, m_MinWidth); @@ -45,7 +50,7 @@ void GUIRadioButton::Create(const std::string &Name, int X, int Y, int Width, in ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIRadioButton::Create(GUIProperties *Props) { +void GUIRadioButton::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -70,7 +75,7 @@ void GUIRadioButton::Create(GUIProperties *Props) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIRadioButton::ChangeSkin(GUISkin *Skin) { +void GUIRadioButton::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Build the checkbox bitmap @@ -126,7 +131,7 @@ void GUIRadioButton::BuildBitmap() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIRadioButton::Draw(GUIScreen *Screen) { +void GUIRadioButton::Draw(GUIScreen* Screen) { if (!m_Image) { return; } @@ -147,13 +152,15 @@ void GUIRadioButton::Draw(GUIScreen *Screen) { if (m_Checked) { if (m_Enabled) { m_Image->DrawTrans(Screen->GetBitmap(), m_X, YPos, &m_ImageRects[2]); - } //else { - //m_Image->DrawTrans(Screen->GetBitmap(), m_X, YPos, &m_ImageRects[3]); + } // else { + // m_Image->DrawTrans(Screen->GetBitmap(), m_X, YPos, &m_ImageRects[3]); //} } // Should show as grayed out and disabled when it is, regardless of checked or not - if (!m_Enabled) { m_Image->DrawTrans(Screen->GetBitmap(), m_X, YPos, &m_ImageRects[3]); } + if (!m_Enabled) { + m_Image->DrawTrans(Screen->GetBitmap(), m_X, YPos, &m_ImageRects[3]); + } // Draw the text @@ -190,7 +197,9 @@ void GUIRadioButton::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); // If the mouse is over the button, add the command to the event queue - if (PointInside(X, Y) && Buttons & MOUSE_LEFT) { SetCheck(true); } + if (PointInside(X, Y) && Buttons & MOUSE_LEFT) { + SetCheck(true); + } AddEvent(GUIEvent::Notification, UnPushed, 0); } @@ -209,7 +218,7 @@ void GUIRadioButton::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIRadioButton::GetPanel() { +GUIPanel* GUIRadioButton::GetPanel() { return this; } @@ -234,7 +243,7 @@ void GUIRadioButton::Resize(int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIRadioButton::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUIRadioButton::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } @@ -264,11 +273,11 @@ void GUIRadioButton::SetCheck(bool Check) { // Go through all my RadioButton siblings and un-check them if (m_ControlParent) { - std::vector::iterator it; - std::vector *Children = m_ControlParent->GetChildren(); + std::vector::iterator it; + std::vector* Children = m_ControlParent->GetChildren(); for (it = Children->begin(); it != Children->end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; if (C) { // Make sure this is not me if (C->GetPanel() && GetPanel() && C->GetPanel()->GetPanelID() == GetPanel()->GetPanelID()) { @@ -277,7 +286,7 @@ void GUIRadioButton::SetCheck(bool Check) { // Make sure the control is a radio button if (C->GetID().compare(GetID()) == 0) { - GUIRadioButton *R = (GUIRadioButton *)C; + GUIRadioButton* R = (GUIRadioButton*)C; R->SetCheck(false); } } @@ -293,7 +302,7 @@ bool GUIRadioButton::GetCheck() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIRadioButton::SetText(const std::string &Text) { +void GUIRadioButton::SetText(const std::string& Text) { m_Text = Text; } @@ -305,7 +314,7 @@ std::string GUIRadioButton::GetText() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIRadioButton::ApplyProperties(GUIProperties *Props) { +void GUIRadioButton::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); m_Properties.GetValue("Text", &m_Text); diff --git a/Source/GUI/GUIRadioButton.h b/Source/GUI/GUIRadioButton.h index 855cf2831e..f6b0bc3dd5 100644 --- a/Source/GUI/GUIRadioButton.h +++ b/Source/GUI/GUIRadioButton.h @@ -3,218 +3,196 @@ namespace RTE { -/// -/// A radiobutton control class. -/// -class GUIRadioButton : public GUIControl, public GUIPanel { + /// + /// A radiobutton control class. + /// + class GUIRadioButton : public GUIControl, public GUIPanel { + + public: + // RadioButton Notifications + enum { + Pushed = 0, + UnPushed, + Changed, + } Notification; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIRadioButton + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIRadioButton object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUIRadioButton(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseEnter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse enters the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseLeave + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse leaves the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "RADIOBUTTON"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; -public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - // RadioButton Notifications - enum { - Pushed = 0, - UnPushed, - Changed, - } Notification; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the check state. + // Arguments: State. + + void SetCheck(bool Check); -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIRadioButton -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIRadioButton object in -// system memory. -// Arguments: GUIManager, GUIControlManager. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the check state. + // Arguments: None. + + bool GetCheck() const; - GUIRadioButton(GUIManager *Manager, GUIControlManager *ControlManager); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the text. + // Arguments: Text. + + void SetText(const std::string& Text); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the text. + // Arguments: None. + + std::string GetText() const; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; + private: + GUIBitmap* m_Image; + GUIRect m_ImageRects[4]; + + bool m_Checked; + int m_Mouseover; + std::string m_Text; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseEnter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse enters the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseLeave -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse leaves the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "RADIOBUTTON"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the check state. -// Arguments: State. - - void SetCheck(bool Check); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the check state. -// Arguments: None. - - bool GetCheck() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the text. -// Arguments: Text. - - void SetText(const std::string &Text); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the text. -// Arguments: None. - - std::string GetText() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - -private: - - GUIBitmap *m_Image; - GUIRect m_ImageRects[4]; - - bool m_Checked; - int m_Mouseover; - std::string m_Text; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the checkbox bitmap to draw. - // Arguments: None. - - void BuildBitmap(); -}; -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the checkbox bitmap to draw. + // Arguments: None. + + void BuildBitmap(); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIReader.cpp b/Source/GUI/GUIReader.cpp index a47e33e0a1..4b26e02f38 100644 --- a/Source/GUI/GUIReader.cpp +++ b/Source/GUI/GUIReader.cpp @@ -3,11 +3,12 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader::StreamInfo::StreamInfo(std::ifstream *stream, const std::string &filePath, int currentLine, int prevIndent) : Stream(stream), FilePath(filePath), CurrentLine(currentLine), PreviousIndent(prevIndent) {} + GUIReader::StreamInfo::StreamInfo(std::ifstream* stream, const std::string& filePath, int currentLine, int prevIndent) : + Stream(stream), FilePath(filePath), CurrentLine(currentLine), PreviousIndent(prevIndent) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIReader::Clear() { m_Stream = nullptr; @@ -22,15 +23,15 @@ namespace RTE { m_SkipIncludes = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// GUIReader::GUIReader() { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIReader::Create(const std::string &fileName) { + int GUIReader::Create(const std::string& fileName) { m_FilePath = std::filesystem::path(fileName).generic_string(); if (m_FilePath.empty()) { @@ -43,37 +44,37 @@ namespace RTE { return m_Stream->good() ? 0 : -1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::istream * GUIReader::GetStream() const { + std::istream* GUIReader::GetStream() const { return m_Stream.get(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string GUIReader::GetCurrentFilePath() const { return m_FilePath; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string GUIReader::GetCurrentFileLine() const { return std::to_string(m_CurrentLine); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIReader::GetSkipIncludes() const { return m_SkipIncludes; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIReader::SetSkipIncludes(bool skip) { m_SkipIncludes = skip; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string GUIReader::ReadLine() { DiscardEmptySpace(); @@ -91,8 +92,12 @@ namespace RTE { break; } - if (m_Stream->eof()) { break; } - if (!m_Stream->good()) { ReportError("Stream failed for some reason"); } + if (m_Stream->eof()) { + break; + } + if (!m_Stream->good()) { + ReportError("Stream failed for some reason"); + } retString.append(1, temp); peek = static_cast(m_Stream->peek()); @@ -100,7 +105,7 @@ namespace RTE { return TrimString(retString); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string GUIReader::ReadPropName() { DiscardEmptySpace(); @@ -123,7 +128,9 @@ namespace RTE { EndIncludeFile(); break; } - if (!m_Stream->good()) { ReportError("Stream failed for some reason"); } + if (!m_Stream->good()) { + ReportError("Stream failed for some reason"); + } retString.append(1, temp); } // Trim the string of whitespace @@ -146,7 +153,7 @@ namespace RTE { return retString; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string GUIReader::ReadPropValue() { std::string fullLine = ReadLine(); @@ -155,7 +162,7 @@ namespace RTE { return TrimString(propValue); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIReader::NextProperty() { if (!DiscardEmptySpace() || m_EndOfStreams) { @@ -170,9 +177,9 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIReader::TrimString(const std::string &stringToTrim) const { + std::string GUIReader::TrimString(const std::string& stringToTrim) const { if (stringToTrim.empty()) { return ""; } @@ -182,7 +189,7 @@ namespace RTE { return stringToTrim.substr(start, (end - start + 1)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIReader::DiscardEmptySpace() { char peek; @@ -197,42 +204,52 @@ namespace RTE { return EndIncludeFile(); } // Not end-of-file but still got junk back... something went to shit - if (peek == -1) { ReportError("Something went wrong reading the line; make sure it is providing the expected type"); } + if (peek == -1) { + ReportError("Something went wrong reading the line; make sure it is providing the expected type"); + } // Discard spaces if (peek == ' ') { m_Stream->ignore(1); - // Discard tabs, and count them + // Discard tabs, and count them } else if (peek == '\t') { indent++; m_Stream->ignore(1); - // Discard newlines and reset the tab count for the new line, also count the lines + // Discard newlines and reset the tab count for the new line, also count the lines } else if (peek == '\n' || peek == '\r') { // So we don't count lines twice when there are both newline and carriage return at the end of lines - if (peek == '\n') { m_CurrentLine++; } + if (peek == '\n') { + m_CurrentLine++; + } indent = 0; discardedLine = true; m_Stream->ignore(1); - // Comment line? + // Comment line? } else if (m_Stream->peek() == '/') { char temp = static_cast(m_Stream->get()); char temp2; // Confirm that it's a comment line, if so discard it and continue if (m_Stream->peek() == '/') { - while (m_Stream->peek() != '\n' && m_Stream->peek() != '\r' && !m_Stream->eof()) { m_Stream->ignore(1); } - // Block comment + while (m_Stream->peek() != '\n' && m_Stream->peek() != '\r' && !m_Stream->eof()) { + m_Stream->ignore(1); + } + // Block comment } else if (m_Stream->peek() == '*') { // Find the matching "*/" while (!((temp2 = static_cast(m_Stream->get())) == '*' && m_Stream->peek() == '/') && !m_Stream->eof()) { // Count the lines within the comment though - if (temp2 == '\n') { ++m_CurrentLine; } + if (temp2 == '\n') { + ++m_CurrentLine; + } } // Discard that final '/' - if (!m_Stream->eof()) { m_Stream->ignore(1); } + if (!m_Stream->eof()) { + m_Stream->ignore(1); + } - // Not a comment, so it's data, so quit. + // Not a comment, so it's data, so quit. } else { m_Stream->putback(temp); break; @@ -252,19 +269,19 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIReader::ReaderOK() const { return m_Stream.get() && !m_Stream->fail() && m_Stream->is_open(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIReader::ReportError(const std::string &errorDesc) const { + void GUIReader::ReportError(const std::string& errorDesc) const { GUIAbort(errorDesc + "\nError happened in " + m_FilePath + " at line " + std::to_string(m_CurrentLine) + "!"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIReader::StartIncludeFile() { // Get the file path from the current stream before pushing it into the StreamStack, otherwise we can't open a new stream after releasing it because we can't read. @@ -298,7 +315,7 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIReader::EndIncludeFile() { if (m_StreamStack.empty()) { @@ -322,25 +339,25 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(bool &var) { + GUIReader& GUIReader::operator>>(bool& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(char &var) { + GUIReader& GUIReader::operator>>(char& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(unsigned char &var) { + GUIReader& GUIReader::operator>>(unsigned char& var) { DiscardEmptySpace(); int temp; *m_Stream >> temp; @@ -348,59 +365,59 @@ namespace RTE { return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(short &var) { + GUIReader& GUIReader::operator>>(short& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(unsigned short &var) { + GUIReader& GUIReader::operator>>(unsigned short& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(int &var) { + GUIReader& GUIReader::operator>>(int& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(unsigned int &var) { + GUIReader& GUIReader::operator>>(unsigned int& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(long &var) { + GUIReader& GUIReader::operator>>(long& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(unsigned long &var) { + GUIReader& GUIReader::operator>>(unsigned long& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Yeah, this is dumb - read as double and cast. + // Yeah, this is dumb - read as double and cast. // This is because, for whatever fucking reason, iostream can save out floats at a precision that it's then unable to read... - GUIReader & GUIReader::operator>>(float &var) { + GUIReader& GUIReader::operator>>(float& var) { DiscardEmptySpace(); double var2; *m_Stream >> var2; @@ -408,18 +425,18 @@ namespace RTE { return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(double &var) { + GUIReader& GUIReader::operator>>(double& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader & GUIReader::operator>>(std::string &var) { + GUIReader& GUIReader::operator>>(std::string& var) { var.assign(ReadLine()); return *this; } -} +} // namespace RTE diff --git a/Source/GUI/GUIReader.h b/Source/GUI/GUIReader.h index 12362ef811..579ed6ddd3 100644 --- a/Source/GUI/GUIReader.h +++ b/Source/GUI/GUIReader.h @@ -9,7 +9,6 @@ namespace RTE { class GUIReader { public: - #pragma region Creation /// /// Constructor method used to instantiate a GUIReader object in system memory. Create() should be called before using the object. @@ -21,7 +20,7 @@ namespace RTE { /// /// Path to the file to open for reading. If the file doesn't exist the stream will fail to open. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const std::string &fileName); + int Create(const std::string& fileName); #pragma endregion #pragma region Getters and Setters @@ -29,7 +28,7 @@ namespace RTE { /// Gets a pointer to the istream of this reader. /// /// A pointer to the istream object for this reader. - std::istream * GetStream() const; + std::istream* GetStream() const; /// /// Gets the path of the current file this reader is reading from. @@ -88,7 +87,7 @@ namespace RTE { /// /// String to remove whitespace from. /// The string that was passed in, sans whitespace in the front and end. - std::string TrimString(const std::string &stringToTrim) const; + std::string TrimString(const std::string& stringToTrim) const; /// /// Discards all whitespace, newlines and comment lines (which start with '//') so that the next thing to be read will be actual data. @@ -108,7 +107,7 @@ namespace RTE { /// Makes an error message box pop up for the user that tells them something went wrong with the reading, and where. /// /// The message describing what's wrong. - void ReportError(const std::string &errorDesc) const; + void ReportError(const std::string& errorDesc) const; #pragma endregion #pragma region Operator Overloads @@ -117,22 +116,21 @@ namespace RTE { /// /// A reference to the variable that will be filled by the extracted data. /// A GUIReader reference for further use in an expression. - GUIReader & operator>>(bool &var); - GUIReader & operator>>(char &var); - GUIReader & operator>>(unsigned char &var); - GUIReader & operator>>(short &var); - GUIReader & operator>>(unsigned short &var); - GUIReader & operator>>(int &var); - GUIReader & operator>>(unsigned int &var); - GUIReader & operator>>(long &var); - GUIReader & operator>>(unsigned long &var); - GUIReader & operator>>(float &var); - GUIReader & operator>>(double &var); - GUIReader & operator>>(std::string &var); + GUIReader& operator>>(bool& var); + GUIReader& operator>>(char& var); + GUIReader& operator>>(unsigned char& var); + GUIReader& operator>>(short& var); + GUIReader& operator>>(unsigned short& var); + GUIReader& operator>>(int& var); + GUIReader& operator>>(unsigned int& var); + GUIReader& operator>>(long& var); + GUIReader& operator>>(unsigned long& var); + GUIReader& operator>>(float& var); + GUIReader& operator>>(double& var); + GUIReader& operator>>(std::string& var); #pragma endregion protected: - /// /// A struct containing information from the currently used stream. /// @@ -140,10 +138,10 @@ namespace RTE { /// /// Constructor method used to instantiate a StreamInfo object in system memory. /// - StreamInfo(std::ifstream *stream, const std::string &filePath, int currentLine, int prevIndent); + StreamInfo(std::ifstream* stream, const std::string& filePath, int currentLine, int prevIndent); // NOTE: These members are owned by the reader that owns this struct, so are not deleted when this is destroyed. - std::ifstream *Stream; //!< Currently used stream, is not on the StreamStack until a new stream is opened. + std::ifstream* Stream; //!< Currently used stream, is not on the StreamStack until a new stream is opened. std::string FilePath; //!< Currently used stream's filepath. int CurrentLine; //!< The line number the stream is on. int PreviousIndent; //!< Count of tabs encountered on the last line DiscardEmptySpace() discarded. @@ -170,7 +168,6 @@ namespace RTE { int m_ObjectEndings; private: - #pragma region Reading Operations /// /// When ReadPropName encounters the property name "IncludeFile", it will automatically call this function to get started reading on that file. @@ -193,8 +190,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - GUIReader(const GUIReader &reference) = delete; - GUIReader & operator=(const GUIReader &rhs) = delete; + GUIReader(const GUIReader& reference) = delete; + GUIReader& operator=(const GUIReader& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/GUI/GUIScrollPanel.cpp b/Source/GUI/GUIScrollPanel.cpp index e6563739e0..64735b42b1 100644 --- a/Source/GUI/GUIScrollPanel.cpp +++ b/Source/GUI/GUIScrollPanel.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIScrollPanel::GUIScrollPanel(GUIManager *Manager) : GUIPanel(Manager) { +GUIScrollPanel::GUIScrollPanel(GUIManager* Manager) : + GUIPanel(Manager) { m_Skin = nullptr; m_DrawBitmap[0] = m_DrawBitmap[1] = m_DrawBitmap[2] = nullptr; m_ButtonSize = 17; @@ -21,7 +22,8 @@ GUIScrollPanel::GUIScrollPanel(GUIManager *Manager) : GUIPanel(Manager) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIScrollPanel::GUIScrollPanel() : GUIPanel() { +GUIScrollPanel::GUIScrollPanel() : + GUIPanel() { m_Skin = nullptr; m_DrawBitmap[0] = m_DrawBitmap[1] = m_DrawBitmap[2] = nullptr; m_ButtonSize = 17; @@ -69,26 +71,30 @@ void GUIScrollPanel::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollPanel::LoadProps(GUIProperties *Props) { +void GUIScrollPanel::LoadProps(GUIProperties* Props) { assert(Props); std::string Ori; Props->GetValue("Orientation", &Ori); m_Orientation = Horizontal; - if (stricmp(Ori.c_str(), "Vertical") == 0) { m_Orientation = Vertical; } + if (stricmp(Ori.c_str(), "Vertical") == 0) { + m_Orientation = Vertical; + } Props->GetValue("Minimum", &m_Minimum); Props->GetValue("Maximum", &m_Maximum); Props->GetValue("Value", &m_Value); Props->GetValue("PageSize", &m_PageSize); Props->GetValue("SmallChange", &m_SmallChange); - if (!Props->GetValue("ValueResolution", &m_ValueResolution)) { m_ValueResolution = std::max((m_Maximum - m_Minimum) / 100, 1); } + if (!Props->GetValue("ValueResolution", &m_ValueResolution)) { + m_ValueResolution = std::max((m_Maximum - m_Minimum) / 100, 1); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollPanel::ChangeSkin(GUISkin *Skin) { +void GUIScrollPanel::ChangeSkin(GUISkin* Skin) { assert(Skin); m_Skin = Skin; @@ -106,7 +112,9 @@ void GUIScrollPanel::BuildBitmap(bool UpdateSize, bool UpdateKnob) { } // If we update the size, the knob is automatically updated - if (UpdateSize) { UpdateKnob = true; } + if (UpdateSize) { + UpdateKnob = true; + } // Free the old bitmaps if (UpdateSize) { @@ -132,7 +140,6 @@ void GUIScrollPanel::BuildBitmap(bool UpdateSize, bool UpdateKnob) { // Calculate the knob size & position CalculateKnob(); - // Create the 3 bitmaps if (m_Orientation == Vertical) { // Vertical @@ -140,14 +147,18 @@ void GUIScrollPanel::BuildBitmap(bool UpdateSize, bool UpdateKnob) { m_DrawBitmap[ButtonStates] = m_Skin->CreateBitmap(m_Width * 2, m_ButtonSize * 2); m_DrawBitmap[Back] = m_Skin->CreateBitmap(m_Width, m_Height); } - if (UpdateKnob) { m_DrawBitmap[KnobStates] = m_Skin->CreateBitmap(m_Width * 2, m_KnobLength); } + if (UpdateKnob) { + m_DrawBitmap[KnobStates] = m_Skin->CreateBitmap(m_Width * 2, m_KnobLength); + } } else { // Horizontal if (UpdateSize) { m_DrawBitmap[ButtonStates] = m_Skin->CreateBitmap(m_ButtonSize * 2, m_Height * 2); m_DrawBitmap[Back] = m_Skin->CreateBitmap(m_Width, m_Height); } - if (UpdateKnob) { m_DrawBitmap[KnobStates] = m_Skin->CreateBitmap(m_KnobLength, m_Height * 2); } + if (UpdateKnob) { + m_DrawBitmap[KnobStates] = m_Skin->CreateBitmap(m_KnobLength, m_Height * 2); + } } // Update the buttons and the background @@ -165,7 +176,6 @@ void GUIScrollPanel::BuildBitmap(bool UpdateSize, bool UpdateKnob) { BuildBackground(); } - // Update the knob if (UpdateKnob && m_KnobLength > 0) { if (m_Orientation == Vertical) { @@ -186,7 +196,7 @@ void GUIScrollPanel::BuildBitmap(bool UpdateSize, bool UpdateKnob) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollPanel::BuildButton(const std::string &ArrowName, int Y, int Width, int Height) { +void GUIScrollPanel::BuildButton(const std::string& ArrowName, int Y, int Width, int Height) { // Create the buttons m_Skin->BuildStandardRect(m_DrawBitmap[ButtonStates], "ScrollButton_Up", 0, Y, Width, Height); @@ -199,7 +209,7 @@ void GUIScrollPanel::BuildButton(const std::string &ArrowName, int Y, int Width, // Load the image file std::string Filename; m_Skin->GetValue(ArrowName, "Filename", &Filename); - GUIBitmap *Arrow = m_Skin->CreateBitmap(Filename); + GUIBitmap* Arrow = m_Skin->CreateBitmap(Filename); if (!Arrow) { return; } @@ -224,7 +234,7 @@ void GUIScrollPanel::BuildBackground() { std::string Filename; m_Skin->GetValue("ScrollBackground", "Filename", &Filename); - GUIBitmap *Background = m_Skin->CreateBitmap(Filename); + GUIBitmap* Background = m_Skin->CreateBitmap(Filename); if (!Background) { return; } @@ -244,8 +254,10 @@ void GUIScrollPanel::BuildBackground() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollPanel::BuildKnob(const std::string &Section, int X, int Y, int Width, int Height) { - if (m_DrawBitmap[KnobStates]) { m_Skin->BuildStandardRect(m_DrawBitmap[KnobStates], Section, X, Y, Width, Height); } +void GUIScrollPanel::BuildKnob(const std::string& Section, int X, int Y, int Width, int Height) { + if (m_DrawBitmap[KnobStates]) { + m_Skin->BuildStandardRect(m_DrawBitmap[KnobStates], Section, X, Y, Width, Height); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -316,13 +328,15 @@ void GUIScrollPanel::SetSmallChange(int SmallChange) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollPanel::Draw(GUIScreen *Screen) { +void GUIScrollPanel::Draw(GUIScreen* Screen) { GUIRect Rect; // Do we need to rebuild? - if (m_RebuildKnob || m_RebuildSize) { BuildBitmap(m_RebuildSize, m_RebuildKnob); } + if (m_RebuildKnob || m_RebuildSize) { + BuildBitmap(m_RebuildSize, m_RebuildKnob); + } - GUIBitmap *Dest = Screen->GetBitmap(); + GUIBitmap* Dest = Screen->GetBitmap(); // Draw the background m_DrawBitmap[Back]->Draw(Dest, m_X, m_Y, 0); @@ -528,7 +542,9 @@ void GUIScrollPanel::OnMouseMove(int X, int Y, int Buttons, int Modifier) { m_Value = std::max(m_Value, m_Minimum); m_Value = std::min(m_Value, m_Maximum - m_PageSize); - if (OldValue != m_Value) { SendSignal(ChangeValue, 0); } + if (OldValue != m_Value) { + SendSignal(ChangeValue, 0); + } } } } @@ -638,8 +654,12 @@ void GUIScrollPanel::CalculateKnob() { int MoveLength = 1; // Calculate the length of the movable area (panel minus buttons) - if (m_Orientation == Vertical) { MoveLength = m_Height - m_ButtonSize * 2; } - if (m_Orientation == Horizontal) { MoveLength = m_Width - m_ButtonSize * 2; } + if (m_Orientation == Vertical) { + MoveLength = m_Height - m_ButtonSize * 2; + } + if (m_Orientation == Horizontal) { + MoveLength = m_Width - m_ButtonSize * 2; + } // Calculate the knob length m_KnobLength = 0; @@ -650,8 +670,12 @@ void GUIScrollPanel::CalculateKnob() { } // Make sure the knob is not too small m_KnobLength = std::max(m_KnobLength, m_MinimumKnobSize); - if (MoveLength > 0) { m_KnobLength = std::min(m_KnobLength, MoveLength); } - if (m_KnobLength < 0) { m_KnobLength = 0; } + if (MoveLength > 0) { + m_KnobLength = std::min(m_KnobLength, MoveLength); + } + if (m_KnobLength < 0) { + m_KnobLength = 0; + } // Calculate the knob position m_KnobPosition = 0; @@ -681,12 +705,14 @@ void GUIScrollPanel::AdjustValue(int Delta) { // Calculate the new knob position CalculateKnob(); - if (OldValue != m_Value) { SendSignal(ChangeValue, 0); } + if (OldValue != m_Value) { + SendSignal(ChangeValue, 0); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollPanel::SaveProps(GUIProperties *Props) const { +void GUIScrollPanel::SaveProps(GUIProperties* Props) const { assert(Props); Props->AddVariable("Orientation", m_Orientation == Horizontal ? "Horizontal" : "Vertical"); diff --git a/Source/GUI/GUIScrollPanel.h b/Source/GUI/GUIScrollPanel.h index 1e484c545c..758abce276 100644 --- a/Source/GUI/GUIScrollPanel.h +++ b/Source/GUI/GUIScrollPanel.h @@ -3,346 +3,314 @@ namespace RTE { -/// -/// A scrollbar panel class used for controls requiring a scrollbar. -/// -class GUIScrollPanel : public GUIPanel { - -public: - - // Scroll panel orientation - enum { - Horizontal, - Vertical - }; - - // Pre-built draw bitmaps - enum { - ButtonStates=0, - KnobStates, - Back - }; - - // Signals - enum { - ChangeValue=0, - Grab, - Release - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIScrollPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIScrollPanel object in -// system memory. -// Arguments: GUIManager. - - explicit GUIScrollPanel(GUIManager *Manager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIScrollPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIScrollPanel object in -// system memory. -// Arguments: None. - - GUIScrollPanel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the scrollpanel -// Arguments: Position, Size. - - void Create(int X, int Y, int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the panel has been destroyed. -// Arguments: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseMove -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse moves (over the panel, or when captured). -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseHover -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse is hovering over the panel (has to be enabled) -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseHover(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the size of the panel. -// Arguments: Width, Height. - - void SetSize(int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMinimum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the minimum value for the scrollpanel -// Arguments: Minimum value. - - void SetMinimum(int Min); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMinimum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the minimum value for the scrollpanel -// Arguments: None. - - int GetMinimum() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMaximum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the maximum value for the scrollpanel -// Arguments: Maximum value. - - void SetMaximum(int Max); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaximum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the maximum value for the scrollpanel -// Arguments: None. - - int GetMaximum() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current value for the scrollpanel -// Arguments: Value. - - void SetValue(int Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current value of the scrollpanel. -// Arguments: None. - - int GetValue() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPageSize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the page size value for the scrollpanel. -// Arguments: PageSize. - - void SetPageSize(int PageSize); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPageSize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the size of the page. -// Arguments: None. - - int GetPageSize() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOrientation -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the orientation of the scrollpanel. -// Arguments: Orientation. - - void SetOrientation(int Orientation); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOrientation -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the orientation of the scrollpanel. -// Arguments: None. - - int GetOrientation() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSmallChange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the small change value. -// Arguments: SmallChange. - - void SetSmallChange(int SmallChange); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSmallChange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the small change value. -// Arguments: None. - - int GetSmallChange() const; - /// - /// Gets the value resolution for this scroll panel. + /// A scrollbar panel class used for controls requiring a scrollbar. /// - /// The value resolution - int GetValueResolution() const; - -protected: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadProps -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Load values from a property class. -// Arguments: Properties. - - void LoadProps(GUIProperties *Props); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveProps -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Save values to a property class. -// Arguments: Properties. - - void SaveProps(GUIProperties *Props) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Build the bitmap. -// Arguments: None. - - void BuildBitmap(bool UpdateSize, bool UpdateKnob); - -private: - - GUISkin *m_Skin; - GUIBitmap *m_DrawBitmap[3]; - - // User attributes - int m_Orientation; - int m_Minimum; - int m_Maximum; - int m_Value; - int m_PageSize; - int m_SmallChange; - - // Internal attributes - bool m_RebuildSize; - bool m_RebuildKnob; - int m_ButtonSize; - int m_MinimumKnobSize; - int m_KnobPosition; - int m_KnobLength; - bool m_ButtonPushed[2]; - bool m_GrabbedKnob; - bool m_GrabbedBackg; - int m_GrabbedPos; - int m_GrabbedSide; - int m_ValueResolution; //!< How much the value increases/decreases on each mouse wheel change when scrolling. - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildButton -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Build a button. -// Arguments: ArrowName, Width, Height. - - void BuildButton(const std::string &ArrowName, int Y, int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildBackground -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Build the background. -// Arguments: None. - - void BuildBackground(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildKnob -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Build the knob. -// Arguments: None. - - void BuildKnob(const std::string &Section, int X, int Y, int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculateKnob -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculate the knob size and position. -// Arguments: None. - - void CalculateKnob(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AdjustValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts the value. -// Arguments: Delta movement. + class GUIScrollPanel : public GUIPanel { + + public: + // Scroll panel orientation + enum { + Horizontal, + Vertical + }; + + // Pre-built draw bitmaps + enum { + ButtonStates = 0, + KnobStates, + Back + }; + + // Signals + enum { + ChangeValue = 0, + Grab, + Release + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIScrollPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIScrollPanel object in + // system memory. + // Arguments: GUIManager. + + explicit GUIScrollPanel(GUIManager* Manager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIScrollPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIScrollPanel object in + // system memory. + // Arguments: None. + + GUIScrollPanel(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the scrollpanel + // Arguments: Position, Size. + + void Create(int X, int Y, int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the panel has been destroyed. + // Arguments: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseMove + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse moves (over the panel, or when captured). + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseHover + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse is hovering over the panel (has to be enabled) + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseHover(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the size of the panel. + // Arguments: Width, Height. + + void SetSize(int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMinimum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the minimum value for the scrollpanel + // Arguments: Minimum value. + + void SetMinimum(int Min); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMinimum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the minimum value for the scrollpanel + // Arguments: None. + + int GetMinimum() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMaximum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the maximum value for the scrollpanel + // Arguments: Maximum value. + + void SetMaximum(int Max); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMaximum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the maximum value for the scrollpanel + // Arguments: None. + + int GetMaximum() const; - void AdjustValue(int Delta); -}; -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current value for the scrollpanel + // Arguments: Value. + + void SetValue(int Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current value of the scrollpanel. + // Arguments: None. + + int GetValue() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPageSize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the page size value for the scrollpanel. + // Arguments: PageSize. + + void SetPageSize(int PageSize); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPageSize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the size of the page. + // Arguments: None. + + int GetPageSize() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOrientation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the orientation of the scrollpanel. + // Arguments: Orientation. + + void SetOrientation(int Orientation); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOrientation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the orientation of the scrollpanel. + // Arguments: None. + + int GetOrientation() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSmallChange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the small change value. + // Arguments: SmallChange. + + void SetSmallChange(int SmallChange); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSmallChange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the small change value. + // Arguments: None. + + int GetSmallChange() const; + + /// + /// Gets the value resolution for this scroll panel. + /// + /// The value resolution + int GetValueResolution() const; + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadProps + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Load values from a property class. + // Arguments: Properties. + + void LoadProps(GUIProperties* Props); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SaveProps + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Save values to a property class. + // Arguments: Properties. + + void SaveProps(GUIProperties* Props) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Build the bitmap. + // Arguments: None. + + void BuildBitmap(bool UpdateSize, bool UpdateKnob); + + private: + GUISkin* m_Skin; + GUIBitmap* m_DrawBitmap[3]; + + // User attributes + int m_Orientation; + int m_Minimum; + int m_Maximum; + int m_Value; + int m_PageSize; + int m_SmallChange; + + // Internal attributes + bool m_RebuildSize; + bool m_RebuildKnob; + int m_ButtonSize; + int m_MinimumKnobSize; + int m_KnobPosition; + int m_KnobLength; + bool m_ButtonPushed[2]; + bool m_GrabbedKnob; + bool m_GrabbedBackg; + int m_GrabbedPos; + int m_GrabbedSide; + int m_ValueResolution; //!< How much the value increases/decreases on each mouse wheel change when scrolling. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildButton + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Build a button. + // Arguments: ArrowName, Width, Height. + + void BuildButton(const std::string& ArrowName, int Y, int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBackground + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Build the background. + // Arguments: None. + + void BuildBackground(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildKnob + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Build the knob. + // Arguments: None. + + void BuildKnob(const std::string& Section, int X, int Y, int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculateKnob + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculate the knob size and position. + // Arguments: None. + + void CalculateKnob(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AdjustValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adjusts the value. + // Arguments: Delta movement. + + void AdjustValue(int Delta); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIScrollbar.cpp b/Source/GUI/GUIScrollbar.cpp index 1154f72776..1f7d8bfbfb 100644 --- a/Source/GUI/GUIScrollbar.cpp +++ b/Source/GUI/GUIScrollbar.cpp @@ -5,14 +5,15 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIScrollbar::GUIScrollbar(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIScrollPanel(Manager) { +GUIScrollbar::GUIScrollbar(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIScrollPanel(Manager) { m_ControlID = "SCROLLBAR"; m_ControlManager = ControlManager; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollbar::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUIScrollbar::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -27,8 +28,12 @@ void GUIScrollbar::Create(const std::string &Name, int X, int Y, int Width, int // Create the ListPanel int w = m_DefWidth; int h = m_DefHeight; - if (Width != -1) { w = Width; } - if (Height != -1) { h = Height; } + if (Width != -1) { + w = Width; + } + if (Height != -1) { + h = Height; + } // Make sure the scroll panel isn't too small w = std::max(w, m_MinWidth); @@ -39,7 +44,7 @@ void GUIScrollbar::Create(const std::string &Name, int X, int Y, int Width, int ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollbar::Create(GUIProperties *Props) { +void GUIScrollbar::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -70,19 +75,19 @@ void GUIScrollbar::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollbar::ChangeSkin(GUISkin *Skin) { +void GUIScrollbar::ChangeSkin(GUISkin* Skin) { GUIScrollPanel::ChangeSkin(Skin); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUIScrollbar::GetPanel() { +GUIPanel* GUIScrollbar::GetPanel() { return this; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollbar::ReceiveSignal(GUIPanel *Source, int Code, int Data) { +void GUIScrollbar::ReceiveSignal(GUIPanel* Source, int Code, int Data) { assert(Source); // Should be our scroll panel @@ -138,7 +143,7 @@ void GUIScrollbar::Resize(int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollbar::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUIScrollbar::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIScrollPanel::GetRect(X, Y, Width, Height); } @@ -150,7 +155,7 @@ void GUIScrollbar::StoreProperties() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUIScrollbar::ApplyProperties(GUIProperties *Props) { +void GUIScrollbar::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); GUIScrollPanel::LoadProps(&m_Properties); diff --git a/Source/GUI/GUIScrollbar.h b/Source/GUI/GUIScrollbar.h index 2c57b01684..ed06ed3a82 100644 --- a/Source/GUI/GUIScrollbar.h +++ b/Source/GUI/GUIScrollbar.h @@ -5,167 +5,150 @@ namespace RTE { -/// -/// A Scrollbar control class. -/// -class GUIScrollbar : public GUIControl, public GUIScrollPanel { - -public: - - // Notifications - enum { - ChangeValue=0, - - } Notifications; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUIScrollbar -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUIScrollbar object in -// system memory. -// Arguments: GUIManager, GUIControlManager. - - GUIScrollbar(GUIManager *Manager, GUIControlManager *ControlManager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. - - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. - - void Destroy() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - /// - /// Called when the mouse scroll wheel is moved. + /// A Scrollbar control class. /// - /// Mouse X position. - /// Mouse Y position. - /// Activated modifier buttons. - /// The amount of wheel movement. Positive is scroll up, negative is scroll down. - void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "SCROLLBAR"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReceiveSignal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when receiving a signal. -// Arguments: Signal source, Signal code, Signal data. - - void ReceiveSignal(GUIPanel *Source, int Code, int Data) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - -private: - -}; -}; + class GUIScrollbar : public GUIControl, public GUIScrollPanel { + + public: + // Notifications + enum { + ChangeValue = 0, + + } Notifications; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUIScrollbar + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUIScrollbar object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUIScrollbar(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + /// + /// Called when the mouse scroll wheel is moved. + /// + /// Mouse X position. + /// Mouse Y position. + /// Activated modifier buttons. + /// The amount of wheel movement. Positive is scroll up, negative is scroll down. + void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "SCROLLBAR"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReceiveSignal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when receiving a signal. + // Arguments: Signal source, Signal code, Signal data. + + void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; + + private: + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUISkin.cpp b/Source/GUI/GUISkin.cpp index afc5c4ee4a..2c441d4120 100644 --- a/Source/GUI/GUISkin.cpp +++ b/Source/GUI/GUISkin.cpp @@ -6,7 +6,7 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUISkin::GUISkin(GUIScreen *Screen) { +GUISkin::GUISkin(GUIScreen* Screen) { m_Screen = Screen; m_FontCache.clear(); @@ -34,7 +34,7 @@ void GUISkin::Clear() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUISkin::Load(const std::string &directory, const std::string &fileName) { +bool GUISkin::Load(const std::string& directory, const std::string& fileName) { // Destroy any previous instances Destroy(); @@ -57,7 +57,7 @@ bool GUISkin::Load(const std::string &directory, const std::string &fileName) { } // Go through the skin file adding the sections and properties - GUIProperties *CurProp = nullptr; + GUIProperties* CurProp = nullptr; while (!skinFile.GetStream()->eof()) { std::string line = skinFile.ReadLine(); @@ -68,7 +68,7 @@ bool GUISkin::Load(const std::string &directory, const std::string &fileName) { // Is the line a section? if (line.front() == '[' && line.back() == ']') { - GUIProperties *p = new GUIProperties(line.substr(1, line.size() - 2)); + GUIProperties* p = new GUIProperties(line.substr(1, line.size() - 2)); CurProp = p; m_PropList.push_back(p); continue; @@ -100,12 +100,12 @@ bool GUISkin::Load(const std::string &directory, const std::string &fileName) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUISkin::GetValue(const std::string &Section, const std::string &Variable, std::string *Value) { - std::vector ::iterator it; +bool GUISkin::GetValue(const std::string& Section, const std::string& Variable, std::string* Value) { + std::vector::iterator it; // Find the property for (it = m_PropList.begin(); it != m_PropList.end(); it++) { - GUIProperties *p = *it; + GUIProperties* p = *it; if (stricmp(p->GetName().c_str(), Section.c_str()) == 0 && p->GetValue(Variable, Value)) { return true; @@ -118,12 +118,12 @@ bool GUISkin::GetValue(const std::string &Section, const std::string &Variable, ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int GUISkin::GetValue(const std::string &Section, const std::string &Variable, int *Array, int MaxArraySize) { - std::vector ::iterator it; +int GUISkin::GetValue(const std::string& Section, const std::string& Variable, int* Array, int MaxArraySize) { + std::vector::iterator it; // Find the property for (it = m_PropList.begin(); it != m_PropList.end(); it++) { - GUIProperties *p = *it; + GUIProperties* p = *it; if (stricmp(p->GetName().c_str(), Section.c_str()) == 0 && p->GetValue(Variable, Array, MaxArraySize)) { return true; @@ -136,12 +136,12 @@ int GUISkin::GetValue(const std::string &Section, const std::string &Variable, i ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUISkin::GetValue(const std::string &Section, const std::string &Variable, int *Value) { - std::vector ::iterator it; +bool GUISkin::GetValue(const std::string& Section, const std::string& Variable, int* Value) { + std::vector::iterator it; // Find the property for (it = m_PropList.begin(); it != m_PropList.end(); it++) { - GUIProperties *p = *it; + GUIProperties* p = *it; if (stricmp(p->GetName().c_str(), Section.c_str()) == 0 && p->GetValue(Variable, Value)) { return true; @@ -154,12 +154,12 @@ bool GUISkin::GetValue(const std::string &Section, const std::string &Variable, ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool GUISkin::GetValue(const std::string &Section, const std::string &Variable, unsigned long *Value) { - std::vector ::iterator it; +bool GUISkin::GetValue(const std::string& Section, const std::string& Variable, unsigned long* Value) { + std::vector::iterator it; // Find the property for (it = m_PropList.begin(); it != m_PropList.end(); it++) { - GUIProperties *p = *it; + GUIProperties* p = *it; if (stricmp(p->GetName().c_str(), Section.c_str()) == 0 && p->GetValue(Variable, Value)) { return true; @@ -173,22 +173,24 @@ bool GUISkin::GetValue(const std::string &Section, const std::string &Variable, ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUISkin::Destroy() { - std::vector ::iterator it; + std::vector::iterator it; // Free the properties for (it = m_PropList.begin(); it != m_PropList.end(); it++) { - GUIProperties *p = *it; + GUIProperties* p = *it; - if (p) { delete p; } + if (p) { + delete p; + } } m_PropList.clear(); // Destroy the fonts in the list - std::vector::iterator itf; + std::vector::iterator itf; for (itf = m_FontCache.begin(); itf != m_FontCache.end(); itf++) { - GUIFont *F = *itf; + GUIFont* F = *itf; if (F) { F->Destroy(); delete F; @@ -197,12 +199,11 @@ void GUISkin::Destroy() { m_FontCache.clear(); - // Destroy the images in the image cache - std::vector::iterator iti; + std::vector::iterator iti; for (iti = m_ImageCache.begin(); iti != m_ImageCache.end(); iti++) { - GUIBitmap *Surf = *iti; + GUIBitmap* Surf = *iti; if (Surf) { Surf->Destroy(); delete Surf; @@ -214,20 +215,20 @@ void GUISkin::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIBitmap * GUISkin::CreateBitmap(int Width, int Height) { +GUIBitmap* GUISkin::CreateBitmap(int Width, int Height) { return m_Screen->CreateBitmap(Width, Height); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIBitmap * GUISkin::CreateBitmap(const std::string &Filename) { +GUIBitmap* GUISkin::CreateBitmap(const std::string& Filename) { // Add the filename onto the current directory std::string File = m_Directory + Filename; // Check if the image is in our cache - std::vector::iterator it; + std::vector::iterator it; for (it = m_ImageCache.begin(); it != m_ImageCache.end(); it++) { - GUIBitmap *Surf = *it; + GUIBitmap* Surf = *it; if (stricmp(File.c_str(), Surf->GetDataPath().c_str()) == 0) { return Surf; @@ -235,7 +236,7 @@ GUIBitmap * GUISkin::CreateBitmap(const std::string &Filename) { } // Not found in cache, so we create a new bitmap from the file - GUIBitmap *Bitmap = m_Screen->CreateBitmap(File); + GUIBitmap* Bitmap = m_Screen->CreateBitmap(File); if (!Bitmap) { return nullptr; } @@ -247,19 +248,19 @@ GUIBitmap * GUISkin::CreateBitmap(const std::string &Filename) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIFont * GUISkin::GetFont(const std::string &Name) { +GUIFont* GUISkin::GetFont(const std::string& Name) { // Check if the font is already in the list - std::vector::iterator it; + std::vector::iterator it; for (it = m_FontCache.begin(); it != m_FontCache.end(); it++) { - GUIFont *F = *it; + GUIFont* F = *it; if (stricmp(F->GetName().c_str(), Name.c_str()) == 0) { return F; } } // Not found, so we create the font - GUIFont *Font = new GUIFont(Name); + GUIFont* Font = new GUIFont(Name); if (!Font->Load(m_Screen, m_Directory + Name)) { delete Font; return nullptr; @@ -272,7 +273,7 @@ GUIFont * GUISkin::GetFont(const std::string &Name) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIBitmap * GUISkin::LoadMousePointer(const std::string &Section) { +GUIBitmap* GUISkin::LoadMousePointer(const std::string& Section) { std::string File; int ColorKey; @@ -282,7 +283,7 @@ GUIBitmap * GUISkin::LoadMousePointer(const std::string &Section) { if (!GetValue(Section, "ColorKeyIndex", &ColorKey)) { return nullptr; } - GUIBitmap *Bitmap = CreateBitmap(File); + GUIBitmap* Bitmap = CreateBitmap(File); if (!Bitmap) { return nullptr; } @@ -292,17 +293,19 @@ GUIBitmap * GUISkin::LoadMousePointer(const std::string &Section) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUISkin::DrawMouse(int Image, int X, int Y, GUIScreen *guiScreenOverride) { +void GUISkin::DrawMouse(int Image, int X, int Y, GUIScreen* guiScreenOverride) { assert(Image >= 0 && Image <= 2); - GUIScreen *targetScreen = guiScreenOverride ? guiScreenOverride : m_Screen; + GUIScreen* targetScreen = guiScreenOverride ? guiScreenOverride : m_Screen; - if (m_MousePointers[Image]) { targetScreen->DrawBitmapTrans(m_MousePointers[Image], X - 1, Y - 1, nullptr); } + if (m_MousePointers[Image]) { + targetScreen->DrawBitmapTrans(m_MousePointers[Image], X - 1, Y - 1, nullptr); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUISkin::BuildStandardRect(GUIBitmap *Dest, const std::string &Section, int X, int Y, int Width, int Height, bool buildBG, bool buildFrame, GUIRect *borderSizes) { +void GUISkin::BuildStandardRect(GUIBitmap* Dest, const std::string& Section, int X, int Y, int Width, int Height, bool buildBG, bool buildFrame, GUIRect* borderSizes) { // Note: For a control to use a 'Standard Rect' it must use the 8 side names, a filler name and a filename property. int VTopLeft[4]; @@ -321,7 +324,7 @@ void GUISkin::BuildStandardRect(GUIBitmap *Dest, const std::string &Section, int // Load the filename std::string Filename; GetValue(Section, "Filename", &Filename); - GUIBitmap *SrcBitmap = CreateBitmap(Filename); + GUIBitmap* SrcBitmap = CreateBitmap(Filename); // Set the color key to be the same color as the Top-Right hand corner pixel SrcBitmap->SetColorKey(SrcBitmap->GetPixel(SrcBitmap->GetWidth() - 1, 0)); @@ -393,7 +396,9 @@ void GUISkin::BuildStandardRect(GUIBitmap *Dest, const std::string &Section, int SrcBitmap->DrawTrans(Dest, X, Y + Height - VBottomLeft[3], &Rect); } - if (borderSizes) { SetRect(borderSizes, VLeft[2], VTop[3], VRight[2], VBottom[3]); } + if (borderSizes) { + SetRect(borderSizes, VLeft[2], VTop[3], VRight[2], VBottom[3]); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Source/GUI/GUISkin.h b/Source/GUI/GUISkin.h index 6deda8f3c5..42b4219cda 100644 --- a/Source/GUI/GUISkin.h +++ b/Source/GUI/GUISkin.h @@ -3,175 +3,157 @@ namespace RTE { -/// -/// Skin class used for the controls to get skin details. -/// -class GUISkin { - -public: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUISkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUISkin object in system -// memory. -// Arguments: GUIScreen Interface. - - explicit GUISkin(GUIScreen *Screen); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: GUISkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to free a GUISkin object in system -// memory. -// Arguments: None. - - ~GUISkin(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Load -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads a skin for a directory -// Arguments: Skin directory and the file within to use - - bool Load(const std::string &directory, const std::string &fileName = "skin.ini"); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the data -// Arguments: None. - - void Clear(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Frees the allocated data. -// Arguments: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a string value -// Arguments: Section, Variable, String pointer - - bool GetValue(const std::string &Section, const std::string &Variable, std::string *Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets an integer array of values -// Arguments: Section, Variable, Integer array, max size of array -// Returns: Number of elements read - - int GetValue(const std::string &Section, const std::string &Variable, int *Array, int MaxArraySize); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a single integer value. -// Arguments: Section, Variable, Integer pointer - - bool GetValue(const std::string &Section, const std::string &Variable, int *Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a single unsigned integer value. -// Arguments: Section, Variable, Unsigned Integer pointer - - bool GetValue(const std::string &Section, const std::string &Variable, unsigned long *Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a blank bitmap. -// Arguments: Width, Height. - - GUIBitmap * CreateBitmap(int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CreateBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a bitmap from a filename. -// Arguments: Filename. - - GUIBitmap * CreateBitmap(const std::string &Filename); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFont -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a cached copy of a font. Ownership is NOT transferred! -// Arguments: None. - - GUIFont * GetFont(const std::string &Name); - - - /// - /// Draws the mouse onto the screen. - /// - /// Mouse image ID. - /// Horizontal position on the screen. - /// Vertical position on the screen. - /// The GUIScreen to draw to, overriding the one passed in on construction. - void DrawMouse(int Image, int X, int Y, GUIScreen *guiScreenOverride = nullptr); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildStandardRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Builds a bitmap from a standard skin property section. -// Arguments: Destination bitmap, Section name, Position, Size. Whether to draw the -// background and frame, a GUIRect to be filled in with the border sizes of the four sides of the built standard rect. - - void BuildStandardRect(GUIBitmap *Dest, const std::string &Section, int X, int Y, int Width, int Height, bool buildBG = true, bool buildFrame = true, GUIRect *borderSizes = nullptr); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ConvertColor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Converts an 8bit palette index to a valid pixel format. -// Primarily used for development in windowed mode. -// Arguments: Color value in any bit depth. Will be converted to the format specified. -// An optional target color depth that will determine what format the color -// should be converted to. If this is 0, then the current video color depth -// will be used as target. - - unsigned long ConvertColor(unsigned long color, int targetDepth = 0); - - -private: - - std::string m_Directory; - GUIScreen *m_Screen; - GUIBitmap *m_MousePointers[3]; - - std::vector m_PropList; - std::vector m_ImageCache; - std::vector m_FontCache; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadMousePointer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads a mouse pointer image & details - // Arguments: Section name. - - GUIBitmap * LoadMousePointer(const std::string &Section); -}; -}; + /// + /// Skin class used for the controls to get skin details. + /// + class GUISkin { + + public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUISkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUISkin object in system + // memory. + // Arguments: GUIScreen Interface. + + explicit GUISkin(GUIScreen* Screen); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: GUISkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to free a GUISkin object in system + // memory. + // Arguments: None. + + ~GUISkin(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Load + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads a skin for a directory + // Arguments: Skin directory and the file within to use + + bool Load(const std::string& directory, const std::string& fileName = "skin.ini"); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the data + // Arguments: None. + + void Clear(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Frees the allocated data. + // Arguments: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a string value + // Arguments: Section, Variable, String pointer + + bool GetValue(const std::string& Section, const std::string& Variable, std::string* Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets an integer array of values + // Arguments: Section, Variable, Integer array, max size of array + // Returns: Number of elements read + + int GetValue(const std::string& Section, const std::string& Variable, int* Array, int MaxArraySize); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a single integer value. + // Arguments: Section, Variable, Integer pointer + + bool GetValue(const std::string& Section, const std::string& Variable, int* Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a single unsigned integer value. + // Arguments: Section, Variable, Unsigned Integer pointer + + bool GetValue(const std::string& Section, const std::string& Variable, unsigned long* Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a blank bitmap. + // Arguments: Width, Height. + + GUIBitmap* CreateBitmap(int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CreateBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a bitmap from a filename. + // Arguments: Filename. + + GUIBitmap* CreateBitmap(const std::string& Filename); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFont + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a cached copy of a font. Ownership is NOT transferred! + // Arguments: None. + + GUIFont* GetFont(const std::string& Name); + + /// + /// Draws the mouse onto the screen. + /// + /// Mouse image ID. + /// Horizontal position on the screen. + /// Vertical position on the screen. + /// The GUIScreen to draw to, overriding the one passed in on construction. + void DrawMouse(int Image, int X, int Y, GUIScreen* guiScreenOverride = nullptr); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildStandardRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Builds a bitmap from a standard skin property section. + // Arguments: Destination bitmap, Section name, Position, Size. Whether to draw the + // background and frame, a GUIRect to be filled in with the border sizes of the four sides of the built standard rect. + + void BuildStandardRect(GUIBitmap* Dest, const std::string& Section, int X, int Y, int Width, int Height, bool buildBG = true, bool buildFrame = true, GUIRect* borderSizes = nullptr); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ConvertColor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Converts an 8bit palette index to a valid pixel format. + // Primarily used for development in windowed mode. + // Arguments: Color value in any bit depth. Will be converted to the format specified. + // An optional target color depth that will determine what format the color + // should be converted to. If this is 0, then the current video color depth + // will be used as target. + + unsigned long ConvertColor(unsigned long color, int targetDepth = 0); + + private: + std::string m_Directory; + GUIScreen* m_Screen; + GUIBitmap* m_MousePointers[3]; + + std::vector m_PropList; + std::vector m_ImageCache; + std::vector m_FontCache; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadMousePointer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads a mouse pointer image & details + // Arguments: Section name. + + GUIBitmap* LoadMousePointer(const std::string& Section); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUISlider.cpp b/Source/GUI/GUISlider.cpp index 2afb49cc3c..e9e784b6b2 100644 --- a/Source/GUI/GUISlider.cpp +++ b/Source/GUI/GUISlider.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUISlider::GUISlider(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUISlider::GUISlider(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "SLIDER"; m_DrawBitmap = nullptr; m_KnobImage = nullptr; @@ -23,7 +24,7 @@ GUISlider::GUISlider(GUIManager *Manager, GUIControlManager *ControlManager) : G ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUISlider::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUISlider::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -40,8 +41,12 @@ void GUISlider::Create(const std::string &Name, int X, int Y, int Width, int Hei m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Re-Calculate the knob info CalculateKnob(); @@ -49,7 +54,7 @@ void GUISlider::Create(const std::string &Name, int X, int Y, int Width, int Hei ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUISlider::Create(GUIProperties *Props) { +void GUISlider::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -67,7 +72,6 @@ void GUISlider::Create(GUIProperties *Props) { m_Width = std::max(m_Width, m_MinWidth); m_Height = std::max(m_Height, m_MinHeight); - // Get the values std::string ori; Props->GetValue("Orientation", &ori); @@ -87,7 +91,9 @@ void GUISlider::Create(GUIProperties *Props) { Props->GetValue("Minimum", &m_Minimum); Props->GetValue("Maximum", &m_Maximum); Props->GetValue("Value", &m_Value); - if (!Props->GetValue("ValueResolution", &m_ValueResolution)) { m_ValueResolution = std::max((m_Maximum - m_Minimum) / 100, 1); } + if (!Props->GetValue("ValueResolution", &m_ValueResolution)) { + m_ValueResolution = std::max((m_Maximum - m_Minimum) / 100, 1); + } m_Value = std::clamp(m_Value, m_Minimum, m_Maximum); @@ -115,7 +121,7 @@ void GUISlider::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUISlider::ChangeSkin(GUISkin *Skin) { +void GUISlider::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Build the progressbar bitmap @@ -147,7 +153,7 @@ void GUISlider::BuildBitmap() { // Load the source image std::string Filename; m_Skin->GetValue(Section, "Filename", &Filename); - GUIBitmap *SrcImage = m_Skin->CreateBitmap(Filename); + GUIBitmap* SrcImage = m_Skin->CreateBitmap(Filename); if (!SrcImage) { return; } @@ -184,7 +190,7 @@ void GUISlider::BuildBitmap() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUISlider::BuildLine(const std::string &Section, GUIBitmap *SrcImage) { +void GUISlider::BuildLine(const std::string& Section, GUIBitmap* SrcImage) { int Values[4]; GUIRect Rect; @@ -231,7 +237,7 @@ void GUISlider::BuildLine(const std::string &Section, GUIBitmap *SrcImage) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUISlider::Draw(GUIScreen *Screen) { +void GUISlider::Draw(GUIScreen* Screen) { int X = 0; int Y = 0; @@ -306,7 +312,9 @@ void GUISlider::OnMouseDown(int X, int Y, int Buttons, int Modifier) { m_Value = std::clamp(m_Value, m_Minimum, m_Maximum); // If the value has changed, add the "Changed" notification - if (m_Value != m_OldValue) { AddEvent(GUIEvent::Notification, Changed, 0); } + if (m_Value != m_OldValue) { + AddEvent(GUIEvent::Notification, Changed, 0); + } AddEvent(GUIEvent::Notification, Clicked, 0); } @@ -319,7 +327,9 @@ void GUISlider::OnMouseUp(int X, int Y, int Buttons, int Modifier) { m_KnobGrabbed = false; // If the value has changed, add the "Changed" notification - if (m_Value != m_OldValue) { AddEvent(GUIEvent::Notification, Changed, 0); } + if (m_Value != m_OldValue) { + AddEvent(GUIEvent::Notification, Changed, 0); + } AddEvent(GUIEvent::Notification, Clicked, 0); } @@ -371,7 +381,9 @@ void GUISlider::OnMouseMove(int X, int Y, int Buttons, int Modifier) { m_KnobPosition = std::min(m_KnobPosition, Size - m_KnobSize - m_EndThickness); // If the value has changed, add the "Changed" notification - if (m_Value != m_OldValue) { AddEvent(GUIEvent::Notification, Changed, 0); } + if (m_Value != m_OldValue) { + AddEvent(GUIEvent::Notification, Changed, 0); + } m_OldValue = m_Value; } @@ -396,7 +408,7 @@ void GUISlider::OnMouseWheelChange(int x, int y, int modifier, int mouseWheelCha ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUISlider::GetPanel() { +GUIPanel* GUISlider::GetPanel() { return this; } @@ -437,7 +449,7 @@ void GUISlider::Resize(int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUISlider::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUISlider::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } @@ -536,7 +548,7 @@ void GUISlider::StoreProperties() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUISlider::ApplyProperties(GUIProperties *Props) { +void GUISlider::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); // Get the values @@ -569,5 +581,7 @@ void GUISlider::ApplyProperties(GUIProperties *Props) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUISlider::SetValueResolution(int valueRes) { - if (valueRes >= 1 && valueRes <= m_Maximum - m_Minimum) { m_ValueResolution = valueRes; } + if (valueRes >= 1 && valueRes <= m_Maximum - m_Minimum) { + m_ValueResolution = valueRes; + } } \ No newline at end of file diff --git a/Source/GUI/GUISlider.h b/Source/GUI/GUISlider.h index 34acc0470d..aa29f79ac1 100644 --- a/Source/GUI/GUISlider.h +++ b/Source/GUI/GUISlider.h @@ -3,331 +3,298 @@ namespace RTE { -/// -/// A slider control class. -/// -class GUISlider : public GUIControl, public GUIPanel { - -public: - - // Slider orientation - enum { - Horizontal, - Vertical - } Orientation; - - // Tick Direction - enum { - TopLeft, - BottomRight - } TickDirection; - - // Slider Notifications - enum { - Changed = 0, - Clicked - } Notification; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUISlider -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUISlider object in -// system memory. -// Arguments: GUIManager, GUIControlManager. - - GUISlider(GUIManager *Manager, GUIControlManager *ControlManager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. - - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. - - void Destroy() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseMove -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse moves (over the panel, or when captured). -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - - /// - /// Called when the mouse scroll wheel is moved. + /// A slider control class. /// - /// Mouse X position. - /// Mouse Y position. - /// Activated modifier buttons. - /// The amount of wheel movement. Positive is scroll up, negative is scroll down. - void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "SLIDER"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOrientation -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the orientation of the slider. -// Arguments: Orientation. - - void SetOrientation(int Orientation); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOrientation -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the orientation of the slider. -// Arguments: None. - - int GetOrientation() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetTickDirection -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the direction of the ticks. -// Arguments: TickDir. - - void SetTickDirection(int TickDir); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTickDirection -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the direction of the ticks. -// Arguments: None. - - int GetTickDirection() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMinimum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the minimum value. -// Arguments: Minimum. - - void SetMinimum(int Minimum); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMinimum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the minimum value. -// Arguments: None. - - int GetMinimum() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMaximum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the maximum value. -// Arguments: Maximum. - - void SetMaximum(int Maximum); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaximum -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the maximum value. -// Arguments: None. - - int GetMaximum() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the value. -// Arguments: Value. - - void SetValue(int Value); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetValue -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the value. -// Arguments: None. - - int GetValue() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - - - /// - /// Sets the value resolution for this slider. - /// - /// The new value resolution - void SetValueResolution(int valueRes); - -private: - - GUIBitmap *m_DrawBitmap; - GUIBitmap *m_KnobImage; - - // Properties - int m_Orientation; - int m_TickDirection; - int m_Minimum; - int m_Maximum; - int m_Value; - int m_ValueResolution; - - // Internal variables - int m_KnobPosition; - int m_KnobSize; - bool m_KnobGrabbed; - int m_KnobGrabPos; - int m_EndThickness; - int m_OldValue; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the slider bitmap to draw. -// Arguments: None. - - void BuildBitmap(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildLine -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Builds the background line for the slider -// Arguments: Section, SrcImage. - - void BuildLine(const std::string &Section, GUIBitmap *SrcImage); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CalculateKnob -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the knob position and size. -// Arguments: None. - - void CalculateKnob(); -}; -}; + class GUISlider : public GUIControl, public GUIPanel { + + public: + // Slider orientation + enum { + Horizontal, + Vertical + } Orientation; + + // Tick Direction + enum { + TopLeft, + BottomRight + } TickDirection; + + // Slider Notifications + enum { + Changed = 0, + Clicked + } Notification; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUISlider + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUISlider object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUISlider(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseMove + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse moves (over the panel, or when captured). + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; + + /// + /// Called when the mouse scroll wheel is moved. + /// + /// Mouse X position. + /// Mouse Y position. + /// Activated modifier buttons. + /// The amount of wheel movement. Positive is scroll up, negative is scroll down. + void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "SLIDER"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOrientation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the orientation of the slider. + // Arguments: Orientation. + + void SetOrientation(int Orientation); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOrientation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the orientation of the slider. + // Arguments: None. + + int GetOrientation() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetTickDirection + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the direction of the ticks. + // Arguments: TickDir. + + void SetTickDirection(int TickDir); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTickDirection + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the direction of the ticks. + // Arguments: None. + + int GetTickDirection() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMinimum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the minimum value. + // Arguments: Minimum. + + void SetMinimum(int Minimum); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMinimum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the minimum value. + // Arguments: None. + + int GetMinimum() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMaximum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the maximum value. + // Arguments: Maximum. + + void SetMaximum(int Maximum); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMaximum + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the maximum value. + // Arguments: None. + + int GetMaximum() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the value. + // Arguments: Value. + + void SetValue(int Value); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetValue + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the value. + // Arguments: None. + + int GetValue() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; + + /// + /// Sets the value resolution for this slider. + /// + /// The new value resolution + void SetValueResolution(int valueRes); + + private: + GUIBitmap* m_DrawBitmap; + GUIBitmap* m_KnobImage; + + // Properties + int m_Orientation; + int m_TickDirection; + int m_Minimum; + int m_Maximum; + int m_Value; + int m_ValueResolution; + + // Internal variables + int m_KnobPosition; + int m_KnobSize; + bool m_KnobGrabbed; + int m_KnobGrabPos; + int m_EndThickness; + int m_OldValue; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the slider bitmap to draw. + // Arguments: None. + + void BuildBitmap(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildLine + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Builds the background line for the slider + // Arguments: Section, SrcImage. + + void BuildLine(const std::string& Section, GUIBitmap* SrcImage); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CalculateKnob + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the knob position and size. + // Arguments: None. + + void CalculateKnob(); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUISound.cpp b/Source/GUI/GUISound.cpp index c96a959188..5974129870 100644 --- a/Source/GUI/GUISound.cpp +++ b/Source/GUI/GUISound.cpp @@ -2,7 +2,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUISound::Clear() { m_SplashSound.Reset(); @@ -34,7 +34,7 @@ namespace RTE { m_PlacementGravel.Reset(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUISound::Initialize() { // Interface sounds should not be pitched to reinforce the appearance of time decoupling between simulation and UI. @@ -111,4 +111,4 @@ namespace RTE { m_PlacementGravel.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/PlacementGravel3.flac", true); m_PlacementGravel.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/PlacementGravel4.flac", true); } -} +} // namespace RTE diff --git a/Source/GUI/GUISound.h b/Source/GUI/GUISound.h index de17ad9fa9..d1af0093db 100644 --- a/Source/GUI/GUISound.h +++ b/Source/GUI/GUISound.h @@ -12,9 +12,8 @@ namespace RTE { /// The singleton loader for all GUI sound effects. /// class GUISound : public Singleton { - - public: + public: #pragma region Creation /// /// Constructor method used to instantiate a GUISound object in system memory. Create() should be called before using the object. @@ -44,163 +43,163 @@ namespace RTE { /// Gets juicy logo signature jingle Sound. /// /// Juicy logo signature jingle Sound. - SoundContainer *SplashSound() { return &m_SplashSound; } + SoundContainer* SplashSound() { return &m_SplashSound; } /// /// Gets SoundContainer for enabling menu. /// /// SoundContainer for enabling menu. - SoundContainer *EnterMenuSound() { return &m_EnterMenuSound; } + SoundContainer* EnterMenuSound() { return &m_EnterMenuSound; } /// /// Gets SoundContainer for disabling menu. /// /// SoundContainer for disabling menu. - SoundContainer *ExitMenuSound() { return &m_ExitMenuSound; } + SoundContainer* ExitMenuSound() { return &m_ExitMenuSound; } /// /// Gets SoundContainer for changing focus. /// /// SoundContainer for changing focus. - SoundContainer *FocusChangeSound() { return &m_FocusChangeSound; } + SoundContainer* FocusChangeSound() { return &m_FocusChangeSound; } /// /// Gets SoundContainer for selecting items in list, etc. /// /// SoundContainer for selecting items in list, etc. - SoundContainer *SelectionChangeSound() { return &m_SelectionChangeSound; } + SoundContainer* SelectionChangeSound() { return &m_SelectionChangeSound; } /// /// Gets SoundContainer for adding or deleting items in list. /// /// SoundContainer for adding or deleting items in list. - SoundContainer *ItemChangeSound() { return &m_ItemChangeSound; } + SoundContainer* ItemChangeSound() { return &m_ItemChangeSound; } /// /// Gets SoundContainer for button press. /// /// SoundContainer for button press. - SoundContainer *ButtonPressSound() { return &m_ButtonPressSound; } + SoundContainer* ButtonPressSound() { return &m_ButtonPressSound; } /// /// Gets SoundContainer for button press of going back button. /// /// SoundContainer for button press of going back button. - SoundContainer *BackButtonPressSound() { return &m_BackButtonPressSound; } + SoundContainer* BackButtonPressSound() { return &m_BackButtonPressSound; } /// /// Gets SoundContainer for confirming a selection. /// /// SoundContainer for confirming a selection. - SoundContainer *ConfirmSound() { return &m_ConfirmSound; } + SoundContainer* ConfirmSound() { return &m_ConfirmSound; } /// /// Gets SoundContainer for erroneous input. /// /// SoundContainer for erroneous input. - SoundContainer *UserErrorSound() { return &m_UserErrorSound; } + SoundContainer* UserErrorSound() { return &m_UserErrorSound; } /// /// Gets SoundContainer for testing volume when adjusting volume sliders. /// /// SoundContainer for testing volume when adjusting volume sliders. - SoundContainer *TestSound() { return &m_TestSound; } + SoundContainer* TestSound() { return &m_TestSound; } /// /// Gets SoundContainer for opening pie menu. /// /// SoundContainer for opening pie menu. - SoundContainer *PieMenuEnterSound() { return &m_PieMenuEnterSound; } + SoundContainer* PieMenuEnterSound() { return &m_PieMenuEnterSound; } /// /// Gets SoundContainer for closing pie menu. /// /// SoundContainer for closing pie menu. - SoundContainer *PieMenuExitSound() { return &m_PieMenuExitSound; } + SoundContainer* PieMenuExitSound() { return &m_PieMenuExitSound; } /// /// Gets SoundContainer for when PieMenu hover arrow appears or changes slice. /// /// SoundContainer for when PieMenu hover arrow appears or changes slice. - SoundContainer *HoverChangeSound() { return &m_HoverChangeSound; } + SoundContainer* HoverChangeSound() { return &m_HoverChangeSound; } /// /// Gets SoundContainer for when PieMenu hover arrow appears or changes to a disabled slice. /// /// SoundContainer for when PieMenu hover arrow appears or changes to a disabled slice. - SoundContainer *HoverDisabledSound() { return &m_HoverDisabledSound; } + SoundContainer* HoverDisabledSound() { return &m_HoverDisabledSound; } /// /// Gets SoundContainer for picking a valid PieMenu slice. /// /// SoundContainer for picking a valid PieMenu slice. - SoundContainer *SlicePickedSound() { return &m_SlicePickedSound; } + SoundContainer* SlicePickedSound() { return &m_SlicePickedSound; } /// /// Gets SoundContainer for erroneous input in PieMenu. /// /// SoundContainer for erroneous input in PieMenu. - SoundContainer *DisabledPickedSound() { return &m_DisabledPickedSound; } + SoundContainer* DisabledPickedSound() { return &m_DisabledPickedSound; } /// /// Gets SoundContainer for when the funds of a team changes. /// /// SoundContainer for when the funds of a team changes. - SoundContainer *FundsChangedSound() { return &m_FundsChangedSound; } + SoundContainer* FundsChangedSound() { return &m_FundsChangedSound; } /// /// Gets SoundContainer for switching between regular (non-brain) actors. /// /// SoundContainer for switching between regular (non-brain) actors. - SoundContainer *ActorSwitchSound() { return &m_ActorSwitchSound; } + SoundContainer* ActorSwitchSound() { return &m_ActorSwitchSound; } /// /// Gets SoundContainer for switching to the brain shortcut. /// /// SoundContainer for switching to the brain shortcut. - SoundContainer *BrainSwitchSound() { return &m_BrainSwitchSound; } + SoundContainer* BrainSwitchSound() { return &m_BrainSwitchSound; } /// /// Gets SoundContainer when camera is traveling between actors. /// /// SoundContainer when camera is traveling between actors. - SoundContainer *CameraTravelSound() { return &m_CameraTravelSound; } + SoundContainer* CameraTravelSound() { return &m_CameraTravelSound; } /// /// Gets SoundContainer for making an area focus. /// /// SoundContainer for making an area focus. - SoundContainer *AreaPickedSound() { return &m_AreaPickedSound; } + SoundContainer* AreaPickedSound() { return &m_AreaPickedSound; } /// /// Gets SoundContainer for making an object focus. /// /// SoundContainer for making an object focus. - SoundContainer *ObjectPickedSound() { return &m_ObjectPickedSound; } + SoundContainer* ObjectPickedSound() { return &m_ObjectPickedSound; } /// /// Gets SoundContainer for making a purchase. /// /// SoundContainer for making a purchase. - SoundContainer *PurchaseMadeSound() { return &m_PurchaseMadeSound; } + SoundContainer* PurchaseMadeSound() { return &m_PurchaseMadeSound; } /// /// Gets SoundContainer for placement of object to scene. /// /// SoundContainer for placement of object to scene. - SoundContainer *PlacementBlip() { return &m_PlacementBlip; } + SoundContainer* PlacementBlip() { return &m_PlacementBlip; } /// /// Gets SoundContainer for placement of object to scene. /// /// SoundContainer for placement of object to scene. - SoundContainer *PlacementThud() { return &m_PlacementThud; } + SoundContainer* PlacementThud() { return &m_PlacementThud; } /// /// Gets SoundContainer for gravely placement of object to scene. /// /// SoundContainer for gravely placement of object to scene. - SoundContainer *PlacementGravel() { return &m_PlacementGravel; } + SoundContainer* PlacementGravel() { return &m_PlacementGravel; } #pragma endregion protected: @@ -248,5 +247,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUITab.cpp b/Source/GUI/GUITab.cpp index f81a38d094..af44cea7b2 100644 --- a/Source/GUI/GUITab.cpp +++ b/Source/GUI/GUITab.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUITab::GUITab(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUIPanel(Manager) { +GUITab::GUITab(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUIPanel(Manager) { m_ControlID = "TAB"; m_Image = nullptr; m_ControlManager = ControlManager; @@ -18,7 +19,7 @@ GUITab::GUITab(GUIManager *Manager, GUIControlManager *ControlManager) : GUICont ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITab::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUITab::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -35,8 +36,12 @@ void GUITab::Create(const std::string &Name, int X, int Y, int Width, int Height m_Width = m_DefWidth; m_Height = m_DefHeight; - if (Width != -1) { m_Width = Width; } - if (Height != -1) { m_Height = Height; } + if (Width != -1) { + m_Width = Width; + } + if (Height != -1) { + m_Height = Height; + } // Make sure the button isn't too small m_Width = std::max(m_Width, m_MinWidth); @@ -45,7 +50,7 @@ void GUITab::Create(const std::string &Name, int X, int Y, int Width, int Height ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITab::Create(GUIProperties *Props) { +void GUITab::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -70,7 +75,7 @@ void GUITab::Create(GUIProperties *Props) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITab::ChangeSkin(GUISkin *Skin) { +void GUITab::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Build the checkbox bitmap @@ -127,7 +132,7 @@ void GUITab::BuildBitmap() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITab::Draw(GUIScreen *Screen) { +void GUITab::Draw(GUIScreen* Screen) { if (!m_Image) { return; } @@ -147,7 +152,7 @@ void GUITab::Draw(GUIScreen *Screen) { if (m_Enabled) { m_Image->DrawTrans(Screen->GetBitmap(), m_X, YPos, &m_ImageRects[2]); } // else - //m_Image->DrawTrans(Screen->GetBitmap(), m_X, YPos, &m_ImageRects[3]); + // m_Image->DrawTrans(Screen->GetBitmap(), m_X, YPos, &m_ImageRects[3]); //} } @@ -195,7 +200,9 @@ void GUITab::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); // If the mouse is over the button, add the command to the event queue - if (PointInside(X, Y) && Buttons & MOUSE_LEFT) { SetCheck(true); } + if (PointInside(X, Y) && Buttons & MOUSE_LEFT) { + SetCheck(true); + } AddEvent(GUIEvent::Notification, UnPushed, 0); } @@ -215,7 +222,7 @@ void GUITab::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUITab::GetPanel() { +GUIPanel* GUITab::GetPanel() { return this; } @@ -240,7 +247,7 @@ void GUITab::Resize(int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITab::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUITab::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } @@ -269,11 +276,11 @@ void GUITab::SetCheck(bool Check) { // Go through all my RadioButton siblings and un-check them if (m_ControlParent) { - std::vector::iterator it; - std::vector *Children = m_ControlParent->GetChildren(); + std::vector::iterator it; + std::vector* Children = m_ControlParent->GetChildren(); for (it = Children->begin(); it != Children->end(); it++) { - GUIControl *C = *it; + GUIControl* C = *it; if (C) { // Make sure this is not me if (C->GetPanel() && GetPanel() && C->GetPanel()->GetPanelID() == GetPanel()->GetPanelID()) { @@ -281,7 +288,7 @@ void GUITab::SetCheck(bool Check) { } // Make sure the control is a radio button if (C->GetID().compare(GetID()) == 0) { - GUITab *R = (GUITab *)C; + GUITab* R = (GUITab*)C; R->SetCheck(false); } } @@ -297,7 +304,7 @@ bool GUITab::GetCheck() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITab::SetText(const std::string &Text) { +void GUITab::SetText(const std::string& Text) { m_Text = Text; } @@ -309,7 +316,7 @@ std::string GUITab::GetText() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITab::ApplyProperties(GUIProperties *Props) { +void GUITab::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); m_Properties.GetValue("Text", &m_Text); diff --git a/Source/GUI/GUITab.h b/Source/GUI/GUITab.h index e1fd9cffcb..b356550cef 100644 --- a/Source/GUI/GUITab.h +++ b/Source/GUI/GUITab.h @@ -3,219 +3,197 @@ namespace RTE { -/// -/// A tab control class. -/// -class GUITab : public GUIControl, public GUIPanel { + /// + /// A tab control class. + /// + class GUITab : public GUIControl, public GUIPanel { + + public: + // Tab Notifications + enum { + Hovered = 0, + Pushed, + UnPushed, + Changed, + } Notification; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUITab + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUITab object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUITab(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseEnter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse enters the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseLeave + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse leaves the panel. + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "TAB"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; -public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - // Tab Notifications - enum { - Hovered = 0, - Pushed, - UnPushed, - Changed, - } Notification; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StoreProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the control to store the values into properties. + // Arguments: None. + + void StoreProperties() override; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the check state. + // Arguments: State. + + void SetCheck(bool Check); -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUITab -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUITab object in -// system memory. -// Arguments: GUIManager, GUIControlManager. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the check state. + // Arguments: None. + + bool GetCheck() const; - GUITab(GUIManager *Manager, GUIControlManager *ControlManager); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the text. + // Arguments: Text. + + void SetText(const std::string& Text); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the text. + // Arguments: None. + + std::string GetText() const; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; + private: + GUIBitmap* m_Image; + GUIRect m_ImageRects[4]; + + bool m_Selected; + int m_Mouseover; + std::string m_Text; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseEnter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse enters the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseLeave -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse leaves the panel. -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "TAB"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StoreProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the control to store the values into properties. -// Arguments: None. - - void StoreProperties() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the check state. -// Arguments: State. - - void SetCheck(bool Check); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the check state. -// Arguments: None. - - bool GetCheck() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the text. -// Arguments: Text. - - void SetText(const std::string &Text); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the text. -// Arguments: None. - - std::string GetText() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - -private: - - GUIBitmap *m_Image; - GUIRect m_ImageRects[4]; - - bool m_Selected; - int m_Mouseover; - std::string m_Text; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BuildBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the checkbox bitmap to draw. -// Arguments: None. - - void BuildBitmap(); -}; -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BuildBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the checkbox bitmap to draw. + // Arguments: None. + + void BuildBitmap(); + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUITextBox.cpp b/Source/GUI/GUITextBox.cpp index 152e7835e2..158b659780 100644 --- a/Source/GUI/GUITextBox.cpp +++ b/Source/GUI/GUITextBox.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUITextBox::GUITextBox(GUIManager *Manager, GUIControlManager *ControlManager) : GUIControl(), GUITextPanel(Manager) { +GUITextBox::GUITextBox(GUIManager* Manager, GUIControlManager* ControlManager) : + GUIControl(), GUITextPanel(Manager) { m_ControlID = "TEXTBOX"; m_ControlManager = ControlManager; m_DrawBitmap = nullptr; @@ -15,7 +16,7 @@ GUITextBox::GUITextBox(GUIManager *Manager, GUIControlManager *ControlManager) : ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextBox::Create(const std::string &Name, int X, int Y, int Width, int Height) { +void GUITextBox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); // Minimum size of the control @@ -29,15 +30,19 @@ void GUITextBox::Create(const std::string &Name, int X, int Y, int Width, int He // Create the ListPanel int w = m_DefWidth; int h = m_DefHeight; - if (Width != -1) { w = Width; } - if (Height != -1) { h = Height; } + if (Width != -1) { + w = Width; + } + if (Height != -1) { + h = Height; + } GUITextPanel::Create(X, Y, w, h); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextBox::Create(GUIProperties *Props) { +void GUITextBox::Create(GUIProperties* Props) { GUIControl::Create(Props); // Minimum size of the control @@ -59,14 +64,26 @@ void GUITextBox::Create(GUIProperties *Props) { // Alignment values - these don't affect anything as of yet std::string alignString; Props->GetValue("HAlignment", &alignString); - if (stricmp(alignString.c_str(), "left") == 0) { m_HAlignment = GUIFont::Left; } - if (stricmp(alignString.c_str(), "centre") == 0 || stricmp(alignString.c_str(), "center") == 0) { m_HAlignment = GUIFont::Centre; } - if (stricmp(alignString.c_str(), "right") == 0) { m_HAlignment = GUIFont::Right; } + if (stricmp(alignString.c_str(), "left") == 0) { + m_HAlignment = GUIFont::Left; + } + if (stricmp(alignString.c_str(), "centre") == 0 || stricmp(alignString.c_str(), "center") == 0) { + m_HAlignment = GUIFont::Centre; + } + if (stricmp(alignString.c_str(), "right") == 0) { + m_HAlignment = GUIFont::Right; + } Props->GetValue("VAlignment", &alignString); - if (stricmp(alignString.c_str(), "top") == 0) { m_VAlignment = GUIFont::Top; } - if (stricmp(alignString.c_str(), "middle") == 0) { m_VAlignment = GUIFont::Middle; } - if (stricmp(alignString.c_str(), "bottom") == 0) { m_VAlignment = GUIFont::Bottom; } + if (stricmp(alignString.c_str(), "top") == 0) { + m_VAlignment = GUIFont::Top; + } + if (stricmp(alignString.c_str(), "middle") == 0) { + m_VAlignment = GUIFont::Middle; + } + if (stricmp(alignString.c_str(), "bottom") == 0) { + m_VAlignment = GUIFont::Bottom; + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -82,7 +99,7 @@ void GUITextBox::Destroy() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextBox::ChangeSkin(GUISkin *Skin) { +void GUITextBox::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); // Free any old bitmap @@ -104,7 +121,7 @@ void GUITextBox::ChangeSkin(GUISkin *Skin) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextBox::Draw(GUIScreen *Screen) { +void GUITextBox::Draw(GUIScreen* Screen) { // Draw the background m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, nullptr); @@ -113,7 +130,7 @@ void GUITextBox::Draw(GUIScreen *Screen) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUIPanel * GUITextBox::GetPanel() { +GUIPanel* GUITextBox::GetPanel() { return this; } @@ -138,26 +155,32 @@ void GUITextBox::Resize(int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextBox::GetControlRect(int *X, int *Y, int *Width, int *Height) { +void GUITextBox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUITextPanel::GetRect(X, Y, Width, Height); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextBox::ReceiveSignal(GUIPanel *Source, int Code, int Data) { +void GUITextBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { // Clicked - if (Code == GUITextPanel::Clicked) { AddEvent(GUIEvent::Notification, Clicked, Data); } + if (Code == GUITextPanel::Clicked) { + AddEvent(GUIEvent::Notification, Clicked, Data); + } // Changed - if (Code == GUITextPanel::Changed) { AddEvent(GUIEvent::Notification, Changed, 0); } + if (Code == GUITextPanel::Changed) { + AddEvent(GUIEvent::Notification, Changed, 0); + } // Enter - if (Code == GUITextPanel::Enter) { AddEvent(GUIEvent::Notification, Enter, 0); } + if (Code == GUITextPanel::Enter) { + AddEvent(GUIEvent::Notification, Enter, 0); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextBox::ApplyProperties(GUIProperties *Props) { +void GUITextBox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); // Force a rebuild of the bitmap diff --git a/Source/GUI/GUITextBox.h b/Source/GUI/GUITextBox.h index 4af4030382..ed09532ba6 100644 --- a/Source/GUI/GUITextBox.h +++ b/Source/GUI/GUITextBox.h @@ -5,144 +5,129 @@ namespace RTE { -/// -/// A TextBox control class. -/// -class GUITextBox : public GUIControl, public GUITextPanel { + /// + /// A TextBox control class. + /// + class GUITextBox : public GUIControl, public GUITextPanel { + + public: + // Notifications + enum { + Changed = 0, + Clicked, + Enter + } Notification; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUITextBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUITextBox object in + // system memory. + // Arguments: GUIManager, GUIControlManager. + + GUITextBox(GUIManager* Manager, GUIControlManager* ControlManager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Name, Position. + + void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been created. + // Arguments: Properties. + + void Create(GUIProperties* Props) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control has been destroyed. + // Arguments: None. + + void Destroy() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the panel of the control. + // Arguments: None. + // Returns: 0 if the control does not have a panel, otherwise the topmost panel. + + GUIPanel* GetPanel() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a string representing the control's ID + // Arguments: None. + + static std::string GetControlID() { return "TEXTBOX"; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Move + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be moved. + // Arguments: New position. + + void Move(int X, int Y) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Resize + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the control needs to be resized. + // Arguments: New size. + + void Resize(int Width, int Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetControlRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the rectangle of the control. + // Arguments: Position, Size. + + void GetControlRect(int* X, int* Y, int* Width, int* Height) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReceiveSignal + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when receiving a signal. + // Arguments: Signal source, Signal code, Signal data. + + void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ApplyProperties + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Applies new properties to the control. + // Arguments: GUIProperties. + + void ApplyProperties(GUIProperties* Props) override; -public: - - // Notifications - enum { - Changed = 0, - Clicked, - Enter - } Notification; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUITextBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUITextBox object in -// system memory. -// Arguments: GUIManager, GUIControlManager. - - GUITextBox(GUIManager *Manager, GUIControlManager *ControlManager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Name, Position. - - void Create(const std::string &Name, int X, int Y, int Width = -1, int Height = -1) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been created. -// Arguments: Properties. - - void Create(GUIProperties *Props) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control has been destroyed. -// Arguments: None. - - void Destroy() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the panel of the control. -// Arguments: None. -// Returns: 0 if the control does not have a panel, otherwise the topmost panel. - - GUIPanel * GetPanel() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a string representing the control's ID -// Arguments: None. - - static std::string GetControlID() { return "TEXTBOX"; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Move -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be moved. -// Arguments: New position. - - void Move(int X, int Y) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Resize -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the control needs to be resized. -// Arguments: New size. - - void Resize(int Width, int Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetControlRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the rectangle of the control. -// Arguments: Position, Size. - - void GetControlRect(int *X, int *Y, int *Width, int *Height) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReceiveSignal -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when receiving a signal. -// Arguments: Signal source, Signal code, Signal data. - - void ReceiveSignal(GUIPanel *Source, int Code, int Data) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyProperties -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Applies new properties to the control. -// Arguments: GUIProperties. - - void ApplyProperties(GUIProperties *Props) override; - -private: - - GUIBitmap *m_DrawBitmap; - int m_HAlignment; - int m_VAlignment; -}; -}; + private: + GUIBitmap* m_DrawBitmap; + int m_HAlignment; + int m_VAlignment; + }; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUITextPanel.cpp b/Source/GUI/GUITextPanel.cpp index 6f70d8373a..f34b85ea7a 100644 --- a/Source/GUI/GUITextPanel.cpp +++ b/Source/GUI/GUITextPanel.cpp @@ -5,7 +5,8 @@ using namespace RTE; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUITextPanel::GUITextPanel(GUIManager *Manager) : GUIPanel(Manager) { +GUITextPanel::GUITextPanel(GUIManager* Manager) : + GUIPanel(Manager) { m_Font = nullptr; m_CursorX = m_CursorY = 0; m_CursorIndex = 0; @@ -30,7 +31,8 @@ GUITextPanel::GUITextPanel(GUIManager *Manager) : GUIPanel(Manager) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -GUITextPanel::GUITextPanel() : GUIPanel() { +GUITextPanel::GUITextPanel() : + GUIPanel() { m_Font = nullptr; m_Text = ""; m_CursorX = m_CursorY = 0; @@ -65,7 +67,7 @@ void GUITextPanel::Create(int X, int Y, int Width, int Height) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextPanel::ChangeSkin(GUISkin *Skin) { +void GUITextPanel::ChangeSkin(GUISkin* Skin) { // Load the font std::string Filename; Skin->GetValue("TextBox", "Font", &Filename); @@ -91,12 +93,11 @@ void GUITextPanel::ChangeSkin(GUISkin *Skin) { // Get the cursor color Skin->GetValue("TextBox", "CursorColorIndex", &m_CursorColor); m_CursorColor = Skin->ConvertColor(m_CursorColor); - } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextPanel::Draw(GUIScreen *Screen) { +void GUITextPanel::Draw(GUIScreen* Screen) { if (!m_Font) return; @@ -130,15 +131,18 @@ void GUITextPanel::Draw(GUIScreen *Screen) { int End = std::max(m_StartSelection, m_EndSelection); // Selection - if (m_StartIndex > Start) { Start = m_StartIndex; } + if (m_StartIndex > Start) { + Start = m_StartIndex; + } m_Font->Draw(Screen->GetBitmap(), m_X + wSpacer + m_SelectionX, m_Y + hSpacer, Text.substr(Start - m_StartIndex, End - Start)); } - // If we have focus, draw the blinking cursor const int blinkInterval = 250; bool shouldBlink = static_cast(m_BlinkTimer.GetElapsedRealTimeMS()) % (blinkInterval * 2) > blinkInterval; - if (m_GotFocus && shouldBlink) { Screen->GetBitmap()->DrawRectangle(m_X + m_CursorX + 2, m_Y + hSpacer + m_CursorY + 2, 1, FontHeight - 3, m_CursorColor, true); } + if (m_GotFocus && shouldBlink) { + Screen->GetBitmap()->DrawRectangle(m_X + m_CursorX + 2, m_Y + hSpacer + m_CursorY + 2, 1, FontHeight - 3, m_CursorColor, true); + } // Restore normal clipping Screen->GetBitmap()->SetClipRect(nullptr); @@ -253,7 +257,9 @@ void GUITextPanel::OnKeyPress(int KeyCode, int Modifier) { // ModKey-C (Copy) if (KeyCode == 'c' && ModKey) { - if (m_GotSelection) { GUIUtil::SetClipboardText(GetSelectionText()); } + if (m_GotSelection) { + GUIUtil::SetClipboardText(GetSelectionText()); + } return; } @@ -293,7 +299,7 @@ void GUITextPanel::OnTextInput(std::string_view inputText) { maxValidKeyCode = 57; } - for (auto characterIterator = inputText.begin(); characterIterator < inputText.end(); ++characterIterator){ + for (auto characterIterator = inputText.begin(); characterIterator < inputText.end(); ++characterIterator) { char character = *characterIterator; if (character >= minValidKeyCode && character <= maxValidKeyCode) { RemoveSelectionText(); @@ -336,7 +342,9 @@ void GUITextPanel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { std::string Text = m_Text.substr(m_StartIndex, m_Text.size() - m_StartIndex); m_CursorIndex = m_Text.size(); - if (!(Modifier & MODI_SHIFT)) { m_GotSelection = false; } + if (!(Modifier & MODI_SHIFT)) { + m_GotSelection = false; + } // Go through each character until we to the mouse point int TX = m_X; @@ -404,10 +412,14 @@ void GUITextPanel::UpdateText(bool Typing, bool DoIncrement) { int Increment = 4; int Spacer = 2; - if (Typing) { Increment = 1; } + if (Typing) { + Increment = 1; + } // Make sure the cursor is greater or equal to the start index - if (m_CursorIndex <= m_StartIndex && DoIncrement) { m_StartIndex = m_CursorIndex - Increment; } + if (m_CursorIndex <= m_StartIndex && DoIncrement) { + m_StartIndex = m_CursorIndex - Increment; + } // Clamp it m_StartIndex = std::max(m_StartIndex, 0); @@ -426,7 +438,9 @@ void GUITextPanel::UpdateText(bool Typing, bool DoIncrement) { m_CursorX = m_Font->CalculateWidth(m_Text.substr(m_StartIndex, m_CursorIndex - m_StartIndex)); // Update the selection - if (m_GotSelection) { DoSelection(m_StartSelection, m_EndSelection); } + if (m_GotSelection) { + DoSelection(m_StartSelection, m_EndSelection); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -467,7 +481,7 @@ void GUITextPanel::DoSelection(int Start, int End) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int RTE::GUITextPanel::GetStartOfNextCharacterGroup(const std::string_view &stringToCheck, int currentIndex) const { +int RTE::GUITextPanel::GetStartOfNextCharacterGroup(const std::string_view& stringToCheck, int currentIndex) const { auto isNormalCharacter = [](char charToCheck) { return (std::isalnum(charToCheck) || charToCheck == '_'); }; @@ -479,17 +493,17 @@ int RTE::GUITextPanel::GetStartOfNextCharacterGroup(const std::string_view &stri }; std::string_view::const_iterator currentIterator = stringToCheck.cbegin() + currentIndex; - currentIterator = isNormalCharacter(*currentIterator) ? - std::find_if(currentIterator, stringToCheck.cend(), isSpecialCharacterOrSpace) : - std::find_if(currentIterator, stringToCheck.cend(), isNormalCharacterOrSpace); + currentIterator = isNormalCharacter(*currentIterator) ? std::find_if(currentIterator, stringToCheck.cend(), isSpecialCharacterOrSpace) : std::find_if(currentIterator, stringToCheck.cend(), isNormalCharacterOrSpace); - if (currentIterator != stringToCheck.cend() && std::isspace(*currentIterator)) { currentIterator = std::find_if_not(currentIterator, stringToCheck.cend(), isspace); } + if (currentIterator != stringToCheck.cend() && std::isspace(*currentIterator)) { + currentIterator = std::find_if_not(currentIterator, stringToCheck.cend(), isspace); + } return std::distance(stringToCheck.cbegin(), currentIterator); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int RTE::GUITextPanel::GetStartOfPreviousCharacterGroup(const std::string_view &stringToCheck, int currentIndex) const { +int RTE::GUITextPanel::GetStartOfPreviousCharacterGroup(const std::string_view& stringToCheck, int currentIndex) const { auto isNormalCharacter = [](char charToCheck) { return (std::isalnum(charToCheck) || charToCheck == '_'); }; @@ -501,12 +515,12 @@ int RTE::GUITextPanel::GetStartOfPreviousCharacterGroup(const std::string_view & }; std::string_view::reverse_iterator currentIterator = stringToCheck.crbegin() + (m_Text.size() - currentIndex); - if (std::isspace(*currentIterator)) { currentIterator = std::find_if_not(currentIterator, stringToCheck.crend(), isspace); } + if (std::isspace(*currentIterator)) { + currentIterator = std::find_if_not(currentIterator, stringToCheck.crend(), isspace); + } if (currentIterator != stringToCheck.crend()) { - currentIterator = isNormalCharacter(*currentIterator) ? - std::find_if(currentIterator, stringToCheck.crend(), isSpecialCharacterOrSpace) : - std::find_if(currentIterator, stringToCheck.crend(), isNormalCharacterOrSpace); + currentIterator = isNormalCharacter(*currentIterator) ? std::find_if(currentIterator, stringToCheck.crend(), isSpecialCharacterOrSpace) : std::find_if(currentIterator, stringToCheck.crend(), isNormalCharacterOrSpace); } return std::distance(stringToCheck.cbegin(), currentIterator.base()); } @@ -538,8 +552,12 @@ void GUITextPanel::RemoveSelectionText() { void GUITextPanel::SetCursorPos(int cursorPos) { m_GotSelection = false; - if (cursorPos <= 0) { cursorPos = 0; } - if (cursorPos > m_Text.size()) { cursorPos = m_Text.size(); } + if (cursorPos <= 0) { + cursorPos = 0; + } + if (cursorPos > m_Text.size()) { + cursorPos = m_Text.size(); + } m_CursorIndex = m_Text.size(); @@ -563,7 +581,7 @@ std::string GUITextPanel::GetSelectionText() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextPanel::SetText(const std::string &Text) { +void GUITextPanel::SetText(const std::string& Text) { m_Text = Text; // Clear the selection @@ -581,7 +599,7 @@ void GUITextPanel::SetText(const std::string &Text) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void GUITextPanel::SetRightText(const std::string &rightText) { +void GUITextPanel::SetRightText(const std::string& rightText) { m_RightText = rightText; SendSignal(Changed, 0); } @@ -632,7 +650,9 @@ void GUITextPanel::SetLocked(bool Locked) { m_Locked = Locked; // Clear the selection if we are now locked - if (m_Locked) { ClearSelection(); } + if (m_Locked) { + ClearSelection(); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Source/GUI/GUITextPanel.h b/Source/GUI/GUITextPanel.h index 413ce0ddad..dbd42a31d0 100644 --- a/Source/GUI/GUITextPanel.h +++ b/Source/GUI/GUITextPanel.h @@ -3,311 +3,286 @@ namespace RTE { -/// -/// A text panel class. -/// -class GUITextPanel : public GUIPanel { - -public: - - // Text panel signals - enum { - Clicked = 0, - MouseDown, - Changed, - Enter - } Signals; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUITextPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUITextPanel object in -// system memory. -// Arguments: GUIManager. - - explicit GUITextPanel(GUIManager *Manager); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GUITextPanel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GUITextPanel object in -// system memory. -// Arguments: None. - - GUITextPanel(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Create the panel. -// Arguments: Position, Size. - - void Create(int X, int Y, int Width, int Height); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeSkin -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the skin has been changed. -// Arguments: New skin pointer. - - void ChangeSkin(GUISkin *Skin); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the panel -// Arguments: Screen class - - void Draw(GUIScreen *Screen) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseDown -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes down on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseUp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse goes up on the panel -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnMouseMove -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when the mouse moves (over the panel, or when captured). -// Arguments: Mouse Position, Mouse Buttons, Modifier. - - void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnKeyPress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Called when a key is pressed (OnDown & repeating). -// Arguments: KeyCode, Modifier. - - void OnKeyPress(int KeyCode, int Modifier) override; - - void OnTextInput(std::string_view inputText) override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the text in the textpanel. -// Arguments: Text. - - void SetText(const std::string &Text); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetRightText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the extra text which appears right-justified in the textpanel. -// Arguments: Text. - - void SetRightText(const std::string &rightText); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the text in the textpanel. -// Arguments: None. - - std::string GetText() const { return m_Text; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRightText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the extra text which appears right-justified in the textpanel. -// Arguments: None. - - std::string GetRightText() const { return m_RightText; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSelection -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the start and end indexes of the selection text. -// Arguments: Start, End. - - void SetSelection(int Start, int End); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSelectionStart -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the start index of the selection. -// Arguments: None. -// Returns: Index of the start of the selection. -1 if no selection - - int GetSelectionStart() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSelectionEnd -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the end index of the selection. -// Arguments: None. -// Returns: Index of the end of the selection. -1 if no selection - - int GetSelectionEnd() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearSelection -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the selection. Does NOT remove the selection text though. -// Arguments: None. - - void ClearSelection(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSelectionText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the selection text. -// Arguments: None. - - std::string GetSelectionText() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveSelectionText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes the characters in the selection. -// Arguments: None. - - void RemoveSelectionText(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCursorPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where the cursor should be. This will clear any selection. -// Arguments: The index of the new cursor position. - - void SetCursorPos(int cursorPos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLocked -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the locked state on the textbox. -// Arguments: Locked. - - void SetLocked(bool Locked); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLocked -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the locked state on the textbox. -// Arguments: None. - - bool GetLocked() const; - /// - /// Sets this text panel to accept numeric symbols only. + /// A text panel class. /// - /// Whether to accept numeric symbols only or not. - void SetNumericOnly(bool numericOnly) { m_NumericOnly = numericOnly; } + class GUITextPanel : public GUIPanel { + + public: + // Text panel signals + enum { + Clicked = 0, + MouseDown, + Changed, + Enter + } Signals; - /// - /// Sets this text panel's maximum numeric value when in numeric only mode. - /// - /// The maximum numeric value. 0 means no maximum value. - void SetMaxNumericValue(int maxValue) { m_MaxNumericValue = maxValue; } + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUITextPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUITextPanel object in + // system memory. + // Arguments: GUIManager. + + explicit GUITextPanel(GUIManager* Manager); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GUITextPanel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GUITextPanel object in + // system memory. + // Arguments: None. - /// - /// Sets the maximum length of the text this text panel can contain. - /// - /// The maximum length of the text this text panel can contain. - void SetMaxTextLength(int maxLength) { m_MaxTextLength = maxLength; } - - -private: - - unsigned long m_FontSelectColor; - - std::string m_Text; - std::string m_RightText; // Appears right-justified in the text field - bool m_Focus; - bool m_Locked; - - // The distance from the side and top of the text box, to the side and top of the first line of text - int m_WidthMargin; - int m_HeightMargin; - - // Cursor - int m_CursorX; - int m_CursorY; - int m_CursorIndex; - unsigned long m_CursorColor; - Timer m_BlinkTimer; - - int m_StartIndex; - - // Selection - bool m_GotSelection; - int m_StartSelection; - int m_EndSelection; - unsigned long m_SelectedColorIndex; - int m_SelectionX; - int m_SelectionWidth; - - int m_MaxTextLength; //!< The maximum length of the text this text panel can contain. - bool m_NumericOnly; //!< Whether this text panel only accepts numeric symbols. - int m_MaxNumericValue; //!< The maximum numeric value when in numeric only mode. 0 means no maximum value. - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateText -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the cursor and start positions. -// Arguments: Typing, Increment. - - void UpdateText(bool Typing = false, bool DoIncrement = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DoSelection -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Update the selection. -// Arguments: Start, End. - - void DoSelection(int Start, int End); - - /// - /// Gets the index of the start of the next contiguous group of letters or special characters in the given string, or the end of the string if there is none. - /// Generally used to deal with ctrl + arrows style behavior. - /// - /// A string_view of the string to look for the next word in. - /// The index in the string to start looking from. - /// The index of the start of the next contiguous group of letters or special characters in the given string, or the end of the string if there is none. - int GetStartOfNextCharacterGroup(const std::string_view &stringToCheck, int currentIndex) const; - - /// - /// Gets the index of the start of the previous contiguous group of letters or special characters in the given string, or the end of the string if there is none. - /// Generally used to deal with ctrl + arrows style behavior. - /// - /// A string_view of the string to look for the next word in. - /// The index in the string to start looking from. - /// The index of the start of the previous contiguous group of letters or special characters in the given string, or the end of the string if there is none. - int GetStartOfPreviousCharacterGroup(const std::string_view &stringToCheck, int currentIndex) const; -}; -}; + GUITextPanel(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Create the panel. + // Arguments: Position, Size. + + void Create(int X, int Y, int Width, int Height); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeSkin + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the skin has been changed. + // Arguments: New skin pointer. + + void ChangeSkin(GUISkin* Skin); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the panel + // Arguments: Screen class + + void Draw(GUIScreen* Screen) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseDown + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes down on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseUp + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse goes up on the panel + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnMouseMove + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when the mouse moves (over the panel, or when captured). + // Arguments: Mouse Position, Mouse Buttons, Modifier. + + void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnKeyPress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Called when a key is pressed (OnDown & repeating). + // Arguments: KeyCode, Modifier. + + void OnKeyPress(int KeyCode, int Modifier) override; + + void OnTextInput(std::string_view inputText) override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the text in the textpanel. + // Arguments: Text. + + void SetText(const std::string& Text); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetRightText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the extra text which appears right-justified in the textpanel. + // Arguments: Text. + + void SetRightText(const std::string& rightText); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the text in the textpanel. + // Arguments: None. + + std::string GetText() const { return m_Text; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRightText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the extra text which appears right-justified in the textpanel. + // Arguments: None. + + std::string GetRightText() const { return m_RightText; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSelection + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the start and end indexes of the selection text. + // Arguments: Start, End. + + void SetSelection(int Start, int End); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSelectionStart + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the start index of the selection. + // Arguments: None. + // Returns: Index of the start of the selection. -1 if no selection + + int GetSelectionStart() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSelectionEnd + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the end index of the selection. + // Arguments: None. + // Returns: Index of the end of the selection. -1 if no selection + + int GetSelectionEnd() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearSelection + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the selection. Does NOT remove the selection text though. + // Arguments: None. + + void ClearSelection(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSelectionText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the selection text. + // Arguments: None. + + std::string GetSelectionText() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveSelectionText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes the characters in the selection. + // Arguments: None. + + void RemoveSelectionText(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCursorPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets where the cursor should be. This will clear any selection. + // Arguments: The index of the new cursor position. + + void SetCursorPos(int cursorPos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLocked + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the locked state on the textbox. + // Arguments: Locked. + + void SetLocked(bool Locked); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLocked + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the locked state on the textbox. + // Arguments: None. + + bool GetLocked() const; + + /// + /// Sets this text panel to accept numeric symbols only. + /// + /// Whether to accept numeric symbols only or not. + void SetNumericOnly(bool numericOnly) { m_NumericOnly = numericOnly; } + + /// + /// Sets this text panel's maximum numeric value when in numeric only mode. + /// + /// The maximum numeric value. 0 means no maximum value. + void SetMaxNumericValue(int maxValue) { m_MaxNumericValue = maxValue; } + + /// + /// Sets the maximum length of the text this text panel can contain. + /// + /// The maximum length of the text this text panel can contain. + void SetMaxTextLength(int maxLength) { m_MaxTextLength = maxLength; } + + private: + unsigned long m_FontSelectColor; + + std::string m_Text; + std::string m_RightText; // Appears right-justified in the text field + bool m_Focus; + bool m_Locked; + + // The distance from the side and top of the text box, to the side and top of the first line of text + int m_WidthMargin; + int m_HeightMargin; + + // Cursor + int m_CursorX; + int m_CursorY; + int m_CursorIndex; + unsigned long m_CursorColor; + Timer m_BlinkTimer; + + int m_StartIndex; + + // Selection + bool m_GotSelection; + int m_StartSelection; + int m_EndSelection; + unsigned long m_SelectedColorIndex; + int m_SelectionX; + int m_SelectionWidth; + + int m_MaxTextLength; //!< The maximum length of the text this text panel can contain. + bool m_NumericOnly; //!< Whether this text panel only accepts numeric symbols. + int m_MaxNumericValue; //!< The maximum numeric value when in numeric only mode. 0 means no maximum value. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateText + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the cursor and start positions. + // Arguments: Typing, Increment. + + void UpdateText(bool Typing = false, bool DoIncrement = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DoSelection + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Update the selection. + // Arguments: Start, End. + + void DoSelection(int Start, int End); + + /// + /// Gets the index of the start of the next contiguous group of letters or special characters in the given string, or the end of the string if there is none. + /// Generally used to deal with ctrl + arrows style behavior. + /// + /// A string_view of the string to look for the next word in. + /// The index in the string to start looking from. + /// The index of the start of the next contiguous group of letters or special characters in the given string, or the end of the string if there is none. + int GetStartOfNextCharacterGroup(const std::string_view& stringToCheck, int currentIndex) const; + + /// + /// Gets the index of the start of the previous contiguous group of letters or special characters in the given string, or the end of the string if there is none. + /// Generally used to deal with ctrl + arrows style behavior. + /// + /// A string_view of the string to look for the next word in. + /// The index in the string to start looking from. + /// The index of the start of the previous contiguous group of letters or special characters in the given string, or the end of the string if there is none. + int GetStartOfPreviousCharacterGroup(const std::string_view& stringToCheck, int currentIndex) const; + }; +}; // namespace RTE #endif diff --git a/Source/GUI/GUIUtil.cpp b/Source/GUI/GUIUtil.cpp index ba0a38e866..1a57857468 100644 --- a/Source/GUI/GUIUtil.cpp +++ b/Source/GUI/GUIUtil.cpp @@ -4,10 +4,10 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - char * GUIUtil::TrimString(char *String) { - char *ptr = String; + char* GUIUtil::TrimString(char* String) { + char* ptr = String; // Find the first non-space character while (*ptr) { if (*ptr != ' ') { @@ -25,9 +25,9 @@ namespace RTE { return ptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIUtil::GetClipboardText(std::string *text) { + bool GUIUtil::GetClipboardText(std::string* text) { if (SDL_HasClipboardText()) { *text = SDL_GetClipboardText(); return true; @@ -35,10 +35,10 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIUtil::SetClipboardText(const std::string &text) { + bool GUIUtil::SetClipboardText(const std::string& text) { int result = SDL_SetClipboardText(text.c_str()); return result == 0; } -} +} // namespace RTE diff --git a/Source/GUI/GUIUtil.h b/Source/GUI/GUIUtil.h index c5f5106495..69a1c82c41 100644 --- a/Source/GUI/GUIUtil.h +++ b/Source/GUI/GUIUtil.h @@ -9,27 +9,26 @@ namespace RTE { class GUIUtil { public: - /// /// Removes the preceding and ending spaces from a c type string. /// /// String to trim. /// Trimmed string. - static char * TrimString(char *String); + static char* TrimString(char* String); /// /// Gets the text from the clipboard. /// /// Pointer to string receiving the text. /// True if text was available in the clipboard. - static bool GetClipboardText(std::string *text); + static bool GetClipboardText(std::string* text); /// /// Sets the text in the clipboard. /// /// String to put into the clipboard. /// True if text was added to the clipboard. - static bool SetClipboardText(const std::string &text); + static bool SetClipboardText(const std::string& text); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/GUIWriter.cpp b/Source/GUI/GUIWriter.cpp index ef3f2d2d50..3a087fb1af 100644 --- a/Source/GUI/GUIWriter.cpp +++ b/Source/GUI/GUIWriter.cpp @@ -2,7 +2,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIWriter::Clear() { m_Stream = nullptr; @@ -12,15 +12,15 @@ namespace RTE { m_IndentCount = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// GUIWriter::GUIWriter() { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIWriter::Create(const std::string &fileName, bool append) { + int GUIWriter::Create(const std::string& fileName, bool append) { m_FilePath = fileName; // Extract filename and folder path @@ -36,183 +36,188 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string GUIWriter::GetFilePath() const { return m_FilePath; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string GUIWriter::GetFileName() const { return m_FileName; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string GUIWriter::GetFolderPath() const { return m_FolderPath; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::ObjectStart(const std::string &className) { - *m_Stream << className; ++m_IndentCount; + void GUIWriter::ObjectStart(const std::string& className) { + *m_Stream << className; + ++m_IndentCount; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIWriter::ObjectEnd() { --m_IndentCount; - if (m_IndentCount == 0) { NewLine(false, 2); } + if (m_IndentCount == 0) { + NewLine(false, 2); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIWriter::NewLine(bool toIndent, int lineCount) const { for (int lines = 0; lines < lineCount; ++lines) { *m_Stream << "\n"; - if (toIndent) { *m_Stream << std::string(m_IndentCount, '\t'); } + if (toIndent) { + *m_Stream << std::string(m_IndentCount, '\t'); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::NewLineString(const std::string &textString, bool toIndent) const { + void GUIWriter::NewLineString(const std::string& textString, bool toIndent) const { NewLine(toIndent); *m_Stream << textString; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIWriter::NewDivider(bool toIndent, int dividerLength) const { NewLine(toIndent); *m_Stream << std::string(dividerLength, '/'); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::NewProperty(const std::string &propName) const { + void GUIWriter::NewProperty(const std::string& propName) const { NewLine(); *m_Stream << propName + " = "; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool GUIWriter::WriterOK() const { return m_Stream.get() && !m_Stream->fail() && m_Stream->is_open(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIWriter::EndWrite() const { m_Stream->flush(); m_Stream->close(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const bool &var) { + GUIWriter& GUIWriter::operator<<(const bool& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const char &var) { + GUIWriter& GUIWriter::operator<<(const char& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const unsigned char &var) { + GUIWriter& GUIWriter::operator<<(const unsigned char& var) { int temp = var; *m_Stream << temp; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const short &var) { + GUIWriter& GUIWriter::operator<<(const short& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const unsigned short &var) { + GUIWriter& GUIWriter::operator<<(const unsigned short& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const int &var) { + GUIWriter& GUIWriter::operator<<(const int& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const unsigned int &var) { + GUIWriter& GUIWriter::operator<<(const unsigned int& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const long &var) { + GUIWriter& GUIWriter::operator<<(const long& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const long long &var) { + GUIWriter& GUIWriter::operator<<(const long long& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const unsigned long &var) { + GUIWriter& GUIWriter::operator<<(const unsigned long& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const unsigned long long &var) { + GUIWriter& GUIWriter::operator<<(const unsigned long long& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const float &var) { + GUIWriter& GUIWriter::operator<<(const float& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const double &var) { + GUIWriter& GUIWriter::operator<<(const double& var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const char *var) { + GUIWriter& GUIWriter::operator<<(const char* var) { *m_Stream << var; return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter & GUIWriter::operator<<(const std::string &var) { + GUIWriter& GUIWriter::operator<<(const std::string& var) { *m_Stream << var; return *this; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/GUI/GUIWriter.h b/Source/GUI/GUIWriter.h index 522e1022a6..b979877961 100644 --- a/Source/GUI/GUIWriter.h +++ b/Source/GUI/GUIWriter.h @@ -9,7 +9,6 @@ namespace RTE { class GUIWriter { public: - #pragma region Creation /// /// Constructor method used to instantiate a GUIWriter object in system memory. Create() should be called before using the object. @@ -22,7 +21,7 @@ namespace RTE { /// Path to the file to open for writing. If the directory doesn't exist the stream will fail to open. /// Whether to append to the file if it exists, or to overwrite it. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const std::string &fileName, bool append = false); + int Create(const std::string& fileName, bool append = false); #pragma endregion #pragma region Getters @@ -50,7 +49,7 @@ namespace RTE { /// Used to specify the start of an object to be written. /// /// The class name of the object about to be written. - void ObjectStart(const std::string &className); + void ObjectStart(const std::string& className); /// /// Used to specify the end of an object that has just been written. @@ -69,7 +68,7 @@ namespace RTE { /// /// The text string to write to the new line. /// Whether to indent the new line or not. - void NewLineString(const std::string &textString, bool toIndent = true) const; + void NewLineString(const std::string& textString, bool toIndent = true) const; /// /// Creates a new line and fills it with slashes to create a divider line for INI. @@ -82,7 +81,7 @@ namespace RTE { /// Creates a new line and writes the name of the property in preparation to writing it's value. /// /// The name of the property to be written. - void NewProperty(const std::string &propName) const; + void NewProperty(const std::string& propName) const; #pragma endregion #pragma region Writer Status @@ -104,25 +103,24 @@ namespace RTE { /// /// A reference to the variable that will be written to the ostream. /// A GUIWriter reference for further use in an expression. - GUIWriter & operator<<(const bool &var); - GUIWriter & operator<<(const char &var); - GUIWriter & operator<<(const unsigned char &var); - GUIWriter & operator<<(const short &var); - GUIWriter & operator<<(const unsigned short &var); - GUIWriter & operator<<(const int &var); - GUIWriter & operator<<(const unsigned int &var); - GUIWriter & operator<<(const long &var); - GUIWriter & operator<<(const long long &var); - GUIWriter & operator<<(const unsigned long &var); - GUIWriter & operator<<(const unsigned long long &var); - GUIWriter & operator<<(const float &var); - GUIWriter & operator<<(const double &var); - GUIWriter & operator<<(const char *var); - GUIWriter & operator<<(const std::string &var); + GUIWriter& operator<<(const bool& var); + GUIWriter& operator<<(const char& var); + GUIWriter& operator<<(const unsigned char& var); + GUIWriter& operator<<(const short& var); + GUIWriter& operator<<(const unsigned short& var); + GUIWriter& operator<<(const int& var); + GUIWriter& operator<<(const unsigned int& var); + GUIWriter& operator<<(const long& var); + GUIWriter& operator<<(const long long& var); + GUIWriter& operator<<(const unsigned long& var); + GUIWriter& operator<<(const unsigned long long& var); + GUIWriter& operator<<(const float& var); + GUIWriter& operator<<(const double& var); + GUIWriter& operator<<(const char* var); + GUIWriter& operator<<(const std::string& var); #pragma endregion protected: - std::unique_ptr m_Stream; //!< Stream used for writing to files. std::string m_FilePath; //!< Currently used stream's filepath. std::string m_FolderPath; //!< Only the path to the folder that we are writing a file in, excluding the filename. @@ -130,15 +128,14 @@ namespace RTE { int m_IndentCount; //!< Indentation counter. private: - /// /// Clears all the member variables of this GUIWriter, effectively resetting the members of this abstraction level only. /// void Clear(); // Disallow the use of some implicit methods. - GUIWriter(const GUIWriter &reference) = delete; - GUIWriter & operator=(const GUIWriter &rhs) = delete; + GUIWriter(const GUIWriter& reference) = delete; + GUIWriter& operator=(const GUIWriter& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/Wrappers/AllegroBitmap.cpp b/Source/GUI/Wrappers/AllegroBitmap.cpp index e2b7790579..a3f422eb88 100644 --- a/Source/GUI/Wrappers/AllegroBitmap.cpp +++ b/Source/GUI/Wrappers/AllegroBitmap.cpp @@ -4,7 +4,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AllegroBitmap::Clear() { m_Bitmap = nullptr; @@ -12,9 +12,9 @@ namespace RTE { m_SelfCreated = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::Create(const std::string &fileName) { + void AllegroBitmap::Create(const std::string& fileName) { m_BitmapFile.Create(fileName.c_str()); m_Bitmap = m_BitmapFile.GetAsBitmap(); @@ -23,7 +23,7 @@ namespace RTE { m_SelfCreated = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AllegroBitmap::Create(int width, int height, int colorDepth) { m_BitmapFile.Reset(); @@ -35,47 +35,49 @@ namespace RTE { m_SelfCreated = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AllegroBitmap::Destroy() { - if (m_SelfCreated && m_Bitmap) { destroy_bitmap(m_Bitmap); } + if (m_SelfCreated && m_Bitmap) { + destroy_bitmap(m_Bitmap); + } Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int AllegroBitmap::GetWidth() const { return m_Bitmap ? m_Bitmap->w : 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int AllegroBitmap::GetHeight() const { return m_Bitmap ? m_Bitmap->h : 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int AllegroBitmap::GetColorDepth() const { return m_Bitmap ? bitmap_color_depth(m_Bitmap) : 8; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// unsigned long AllegroBitmap::GetPixel(int posX, int posY) const { return m_Bitmap ? getpixel(m_Bitmap, posX, posY) : 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AllegroBitmap::SetPixel(int posX, int posY, unsigned long pixelColor) { RTEAssert(m_Bitmap, "Trying to set a pixel on a null bitmap!"); putpixel(m_Bitmap, posX, posY, pixelColor); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::GetClipRect(GUIRect *clippingRect) const { + void AllegroBitmap::GetClipRect(GUIRect* clippingRect) const { if (m_Bitmap && clippingRect) { int x1; int y1; @@ -89,9 +91,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::SetClipRect(GUIRect *clippingRect) { + void AllegroBitmap::SetClipRect(GUIRect* clippingRect) { if (!m_Bitmap) { return; } @@ -104,9 +106,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::AddClipRect(GUIRect *clippingRect) { + void AllegroBitmap::AddClipRect(GUIRect* clippingRect) { if (!m_Bitmap) { return; } @@ -119,48 +121,48 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::Draw(GUIBitmap *destBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) { + void AllegroBitmap::Draw(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) { if (!m_Bitmap) { return; } - RTEAssert(destBitmap && dynamic_cast(destBitmap)->GetBitmap(), "Null destination bitmap passed when trying to draw AllegroBitmap"); + RTEAssert(destBitmap && dynamic_cast(destBitmap)->GetBitmap(), "Null destination bitmap passed when trying to draw AllegroBitmap"); if (srcPosAndSizeRect) { - blit(m_Bitmap, dynamic_cast(destBitmap)->GetBitmap(), srcPosAndSizeRect->left, srcPosAndSizeRect->top, destX, destY, srcPosAndSizeRect->right - srcPosAndSizeRect->left, srcPosAndSizeRect->bottom - srcPosAndSizeRect->top); + blit(m_Bitmap, dynamic_cast(destBitmap)->GetBitmap(), srcPosAndSizeRect->left, srcPosAndSizeRect->top, destX, destY, srcPosAndSizeRect->right - srcPosAndSizeRect->left, srcPosAndSizeRect->bottom - srcPosAndSizeRect->top); } else { - blit(m_Bitmap, dynamic_cast(destBitmap)->GetBitmap(), 0, 0, destX, destY, destBitmap->GetWidth(), destBitmap->GetHeight()); + blit(m_Bitmap, dynamic_cast(destBitmap)->GetBitmap(), 0, 0, destX, destY, destBitmap->GetWidth(), destBitmap->GetHeight()); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::DrawTrans(GUIBitmap *destBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) { + void AllegroBitmap::DrawTrans(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) { if (!m_Bitmap) { return; } - RTEAssert(destBitmap && dynamic_cast(destBitmap)->GetBitmap(), "Null destination bitmap passed when trying to draw AllegroBitmap"); + RTEAssert(destBitmap && dynamic_cast(destBitmap)->GetBitmap(), "Null destination bitmap passed when trying to draw AllegroBitmap"); if (srcPosAndSizeRect) { - masked_blit(m_Bitmap, dynamic_cast(destBitmap)->GetBitmap(), srcPosAndSizeRect->left, srcPosAndSizeRect->top, destX, destY, srcPosAndSizeRect->right - srcPosAndSizeRect->left, srcPosAndSizeRect->bottom - srcPosAndSizeRect->top); + masked_blit(m_Bitmap, dynamic_cast(destBitmap)->GetBitmap(), srcPosAndSizeRect->left, srcPosAndSizeRect->top, destX, destY, srcPosAndSizeRect->right - srcPosAndSizeRect->left, srcPosAndSizeRect->bottom - srcPosAndSizeRect->top); } else { - masked_blit(m_Bitmap, dynamic_cast(destBitmap)->GetBitmap(), 0, 0, destX, destY, destBitmap->GetWidth(), destBitmap->GetHeight()); + masked_blit(m_Bitmap, dynamic_cast(destBitmap)->GetBitmap(), 0, 0, destX, destY, destBitmap->GetWidth(), destBitmap->GetHeight()); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::DrawTransScaled(GUIBitmap *destBitmap, int destX, int destY, int width, int height) { + void AllegroBitmap::DrawTransScaled(GUIBitmap* destBitmap, int destX, int destY, int width, int height) { if (!m_Bitmap) { return; } - RTEAssert(destBitmap && dynamic_cast(destBitmap)->GetBitmap(), "Null destination bitmap passed when trying to draw AllegroBitmap"); + RTEAssert(destBitmap && dynamic_cast(destBitmap)->GetBitmap(), "Null destination bitmap passed when trying to draw AllegroBitmap"); - stretch_sprite(dynamic_cast(destBitmap)->GetBitmap(), m_Bitmap, destX, destY, width, height); + stretch_sprite(dynamic_cast(destBitmap)->GetBitmap(), m_Bitmap, destX, destY, width, height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AllegroBitmap::DrawLine(int x1, int y1, int x2, int y2, unsigned long color) { if (!m_Bitmap) { @@ -169,7 +171,7 @@ namespace RTE { line(m_Bitmap, x1, y1, x2, y2, color); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AllegroBitmap::DrawRectangle(int posX, int posY, int width, int height, unsigned long color, bool filled) { if (!m_Bitmap) { @@ -181,4 +183,4 @@ namespace RTE { rect(m_Bitmap, posX, posY, posX + width - 1, posY + height - 1, color); } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/GUI/Wrappers/AllegroBitmap.h b/Source/GUI/Wrappers/AllegroBitmap.h index aaa4f5c418..7c0cfd9110 100644 --- a/Source/GUI/Wrappers/AllegroBitmap.h +++ b/Source/GUI/Wrappers/AllegroBitmap.h @@ -12,7 +12,6 @@ namespace RTE { class AllegroBitmap : public GUIBitmap { public: - #pragma region Creation /// /// Constructor method used to instantiate an AllegroBitmap object in system memory. @@ -23,13 +22,16 @@ namespace RTE { /// Constructor method used to instantiate an AllegroBitmap object in system memory and make it ready for use. /// /// The underlaying BITMAP of this AllegroBitmap. Ownership is NOT transferred! - explicit AllegroBitmap(BITMAP *bitmap) { Clear(); m_Bitmap = bitmap; } + explicit AllegroBitmap(BITMAP* bitmap) { + Clear(); + m_Bitmap = bitmap; + } /// /// Creates an AllegroBitmap from a file. /// /// File name to get the underlaying BITMAP from. Ownership is NOT transferred! - void Create(const std::string &fileName); + void Create(const std::string& fileName); /// /// Creates an empty BITMAP that is owned by this AllegroBitmap. @@ -63,13 +65,16 @@ namespace RTE { /// Gets the underlying BITMAP of this AllegroBitmap. /// /// The underlying BITMAP of this AllegroBitmap. - BITMAP * GetBitmap() const override { return m_Bitmap; } + BITMAP* GetBitmap() const override { return m_Bitmap; } /// /// Sets the underlying BITMAP for this AllegroBitmap. Ownership is NOT transferred. /// /// A pointer to the new BITMAP for this AllegroBitmap. - void SetBitmap(BITMAP *newBitmap) override { Destroy(); m_Bitmap = newBitmap; } + void SetBitmap(BITMAP* newBitmap) override { + Destroy(); + m_Bitmap = newBitmap; + } /// /// Gets the width of the bitmap. @@ -111,19 +116,19 @@ namespace RTE { /// Gets the clipping rectangle of the bitmap. /// /// Pointer to a GUIRect to fill out. - void GetClipRect(GUIRect *clippingRect) const override; + void GetClipRect(GUIRect* clippingRect) const override; /// /// Sets the clipping rectangle of the bitmap. /// /// Pointer to a GUIRect to use as the clipping rectangle, or nullptr for no clipping. - void SetClipRect(GUIRect *clippingRect) override; + void SetClipRect(GUIRect* clippingRect) override; /// /// Sets the clipping rectangle of the bitmap as the intersection of its current clipping rectangle and the passed-in rectangle. /// /// Pointer to a GUIRect to add to the existing clipping rectangle. - void AddClipRect(GUIRect *clippingRect) override; + void AddClipRect(GUIRect* clippingRect) override; #pragma endregion #pragma region Drawing @@ -134,7 +139,7 @@ namespace RTE { /// Destination X position. /// Destination Y position. /// Source bitmap position and size rectangle. - void Draw(GUIBitmap *destBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) override; + void Draw(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) override; /// /// Draw a section of this bitmap onto another bitmap ignoring color-keyed pixels. @@ -143,7 +148,7 @@ namespace RTE { /// Destination X position. /// Destination Y position. /// Source bitmap position and size rectangle. - void DrawTrans(GUIBitmap *destBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) override; + void DrawTrans(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) override; /// /// Draw this bitmap scaled onto another bitmap ignoring color-keyed pixels. @@ -153,7 +158,7 @@ namespace RTE { /// Destination Y position. /// Target width of the bitmap. /// Target height of the bitmap. - void DrawTransScaled(GUIBitmap *destBitmap, int destX, int destY, int width, int height) override; + void DrawTransScaled(GUIBitmap* destBitmap, int destX, int destY, int width, int height) override; #pragma endregion #pragma region Primitive Drawing @@ -180,8 +185,7 @@ namespace RTE { #pragma endregion private: - - BITMAP *m_Bitmap; //!< The underlaying BITMAP. + BITMAP* m_Bitmap; //!< The underlaying BITMAP. ContentFile m_BitmapFile; //!< The ContentFile the underlaying BITMAP was created from, if created from a file. bool m_SelfCreated; //!< Whether the underlaying BITMAP was created by this and is owned. @@ -191,7 +195,7 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - AllegroBitmap & operator=(const AllegroBitmap &rhs) = delete; + AllegroBitmap& operator=(const AllegroBitmap& rhs) = delete; }; -}; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/Wrappers/AllegroScreen.cpp b/Source/GUI/Wrappers/AllegroScreen.cpp index 842d4a5c46..9d373460e1 100644 --- a/Source/GUI/Wrappers/AllegroScreen.cpp +++ b/Source/GUI/Wrappers/AllegroScreen.cpp @@ -5,9 +5,9 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIBitmap * AllegroScreen::CreateBitmap(const std::string &fileName) { + GUIBitmap* AllegroScreen::CreateBitmap(const std::string& fileName) { std::unique_ptr newAllegroBitmap; newAllegroBitmap.reset(new AllegroBitmap()); @@ -15,9 +15,9 @@ namespace RTE { return newAllegroBitmap.release(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIBitmap * AllegroScreen::CreateBitmap(int width, int height) { + GUIBitmap* AllegroScreen::CreateBitmap(int width, int height) { std::unique_ptr newAllegroBitmap; newAllegroBitmap.reset(new AllegroBitmap()); @@ -25,13 +25,13 @@ namespace RTE { return newAllegroBitmap.release(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroScreen::DrawBitmap(GUIBitmap *guiBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) { + void AllegroScreen::DrawBitmap(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) { if (!guiBitmap) { return; } - if (BITMAP *sourceBitmap = dynamic_cast(guiBitmap)->GetBitmap()) { + if (BITMAP* sourceBitmap = dynamic_cast(guiBitmap)->GetBitmap()) { if (srcPosAndSizeRect) { blit(sourceBitmap, m_BackBufferBitmap->GetBitmap(), srcPosAndSizeRect->left, srcPosAndSizeRect->top, destX, destY, srcPosAndSizeRect->right - srcPosAndSizeRect->left, srcPosAndSizeRect->bottom - srcPosAndSizeRect->top); } else { @@ -40,13 +40,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroScreen::DrawBitmapTrans(GUIBitmap *guiBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) { + void AllegroScreen::DrawBitmapTrans(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) { if (!guiBitmap) { return; } - if (BITMAP *sourceBitmap = dynamic_cast(guiBitmap)->GetBitmap()) { + if (BITMAP* sourceBitmap = dynamic_cast(guiBitmap)->GetBitmap()) { if (srcPosAndSizeRect) { masked_blit(sourceBitmap, m_BackBufferBitmap->GetBitmap(), srcPosAndSizeRect->left, srcPosAndSizeRect->top, destX, destY, srcPosAndSizeRect->right - srcPosAndSizeRect->left, srcPosAndSizeRect->bottom - srcPosAndSizeRect->top); } else { @@ -55,14 +55,18 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// unsigned long AllegroScreen::ConvertColor(unsigned long color, int targetColorDepth) { - if (targetColorDepth == 0) { targetColorDepth = get_color_depth(); } + if (targetColorDepth == 0) { + targetColorDepth = get_color_depth(); + } if (targetColorDepth == 8) { // Isn't indexed, don't convert - if (!(color >= 0 && color <= 255)) { color = makecol8(getr32(color), getg32(color), getb32(color)); } + if (!(color >= 0 && color <= 255)) { + color = makecol8(getr32(color), getg32(color), getb32(color)); + } } else { if (color >= 0 && color <= 255) { RGB rgbEntry; @@ -73,4 +77,4 @@ namespace RTE { } return color; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/GUI/Wrappers/AllegroScreen.h b/Source/GUI/Wrappers/AllegroScreen.h index 21729c5d2a..22e8e01ccc 100644 --- a/Source/GUI/Wrappers/AllegroScreen.h +++ b/Source/GUI/Wrappers/AllegroScreen.h @@ -11,20 +11,19 @@ namespace RTE { class AllegroScreen : public GUIScreen { public: - #pragma region Creation /// /// Constructor method used to instantiate an AllegroScreen object in system memory and make it ready for use. /// /// A bitmap that represents the back buffer. Ownership is NOT transferred! - explicit AllegroScreen(BITMAP *backBuffer) { m_BackBufferBitmap = std::make_unique(backBuffer); } + explicit AllegroScreen(BITMAP* backBuffer) { m_BackBufferBitmap = std::make_unique(backBuffer); } /// /// Creates a bitmap from a file. /// /// File name to create bitmap from. /// Pointer to the created bitmap. Ownership IS transferred! - GUIBitmap * CreateBitmap(const std::string &fileName) override; + GUIBitmap* CreateBitmap(const std::string& fileName) override; /// /// Creates an empty bitmap. @@ -32,7 +31,7 @@ namespace RTE { /// Bitmap width. /// Bitmap height. /// Pointer to the created bitmap. Ownership IS transferred! - GUIBitmap * CreateBitmap(int width, int height) override; + GUIBitmap* CreateBitmap(int width, int height) override; #pragma endregion #pragma region Destruction @@ -52,7 +51,7 @@ namespace RTE { /// Gets the bitmap representing the screen. /// /// Pointer to the bitmap representing the screen. Ownership is NOT transferred! - GUIBitmap * GetBitmap() const override { return m_BackBufferBitmap.get(); } + GUIBitmap* GetBitmap() const override { return m_BackBufferBitmap.get(); } #pragma endregion #pragma region Drawing @@ -63,7 +62,7 @@ namespace RTE { /// Destination X position /// Destination Y position /// Source bitmap position and size rectangle. - void DrawBitmap(GUIBitmap *guiBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) override; + void DrawBitmap(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) override; /// /// Draws a bitmap onto the back buffer ignoring color-keyed pixels. @@ -72,7 +71,7 @@ namespace RTE { /// Destination X position /// Destination Y position /// Source bitmap position and size rectangle. - void DrawBitmapTrans(GUIBitmap *guiBitmap, int destX, int destY, GUIRect *srcPosAndSizeRect) override; + void DrawBitmapTrans(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) override; #pragma endregion #pragma region Virtual Override Methods @@ -86,11 +85,10 @@ namespace RTE { #pragma endregion private: - std::unique_ptr m_BackBufferBitmap; //!< The AllegroBitmap that makes this AllegroScreen. // Disallow the use of some implicit methods. - AllegroScreen & operator=(const AllegroScreen &rhs) = delete; + AllegroScreen& operator=(const AllegroScreen& rhs) = delete; }; -}; +}; // namespace RTE #endif \ No newline at end of file diff --git a/Source/GUI/Wrappers/GUIInputWrapper.cpp b/Source/GUI/Wrappers/GUIInputWrapper.cpp index 05675f7db9..d763dc4076 100644 --- a/Source/GUI/Wrappers/GUIInputWrapper.cpp +++ b/Source/GUI/Wrappers/GUIInputWrapper.cpp @@ -8,9 +8,10 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIInputWrapper::GUIInputWrapper(int whichPlayer, bool keyJoyMouseCursor) : GUIInput(whichPlayer, keyJoyMouseCursor) { + GUIInputWrapper::GUIInputWrapper(int whichPlayer, bool keyJoyMouseCursor) : + GUIInput(whichPlayer, keyJoyMouseCursor) { m_KeyTimer = std::make_unique(); m_CursorAccelTimer = std::make_unique(); @@ -20,11 +21,11 @@ namespace RTE { m_KeyHoldDuration.fill(-1); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIInputWrapper::ConvertKeyEvent(SDL_Scancode sdlKey, int guilibKey, float elapsedS) { int nKeys; - const Uint8 *sdlKeyState = SDL_GetKeyboardState(&nKeys); + const Uint8* sdlKeyState = SDL_GetKeyboardState(&nKeys); if (sdlKeyState[sdlKey]) { if (m_KeyHoldDuration[guilibKey] < 0) { m_KeyboardBuffer[guilibKey] = GUIInput::Pushed; @@ -46,7 +47,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIInputWrapper::Update() { float keyElapsedTime = static_cast(m_KeyTimer->GetElapsedRealTimeS()); @@ -56,7 +57,9 @@ namespace RTE { UpdateMouseInput(); // If joysticks and keyboard can control the mouse cursor too. - if (m_KeyJoyMouseCursor) { UpdateKeyJoyMouseInput(keyElapsedTime); } + if (m_KeyJoyMouseCursor) { + UpdateKeyJoyMouseInput(keyElapsedTime); + } // Update the mouse position of this GUIInput, based on the SDL mouse vars (which may have been altered by joystick or keyboard input). Vector mousePos = g_UInputMan.GetAbsoluteMousePosition(); @@ -64,7 +67,7 @@ namespace RTE { m_MouseY = static_cast(mousePos.GetY() / static_cast(g_WindowMan.GetResMultiplier())); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIInputWrapper::UpdateKeyboardInput(float keyElapsedTime) { // Clear the keyboard buffer, we need it to check for changes. @@ -100,13 +103,21 @@ namespace RTE { m_Modifier = GUIInput::ModNone; SDL_Keymod keyShifts = SDL_GetModState(); - if (keyShifts & KMOD_SHIFT) { m_Modifier |= GUIInput::ModShift; } - if (keyShifts & KMOD_ALT) { m_Modifier |= GUIInput::ModAlt; } - if (keyShifts & KMOD_CTRL) { m_Modifier |= GUIInput::ModCtrl; } - if (keyShifts & KMOD_GUI) { m_Modifier |= GUIInput::ModCommand; } + if (keyShifts & KMOD_SHIFT) { + m_Modifier |= GUIInput::ModShift; + } + if (keyShifts & KMOD_ALT) { + m_Modifier |= GUIInput::ModAlt; + } + if (keyShifts & KMOD_CTRL) { + m_Modifier |= GUIInput::ModCtrl; + } + if (keyShifts & KMOD_GUI) { + m_Modifier |= GUIInput::ModCommand; + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIInputWrapper::UpdateMouseInput() { int discard; @@ -209,15 +220,17 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GUIInputWrapper::UpdateKeyJoyMouseInput(float keyElapsedTime) { - //TODO Try to not use magic numbers throughout this method. + // TODO Try to not use magic numbers throughout this method. float mouseDenominator = g_WindowMan.GetResMultiplier(); Vector joyKeyDirectional = g_UInputMan.GetMenuDirectional() * 5; // See how much to accelerate the joystick input based on how long the stick has been pushed around. - if (joyKeyDirectional.MagnitudeIsLessThan(0.95F)) { m_CursorAccelTimer->Reset(); } + if (joyKeyDirectional.MagnitudeIsLessThan(0.95F)) { + m_CursorAccelTimer->Reset(); + } float acceleration = 0.25F + static_cast(std::min(m_CursorAccelTimer->GetElapsedRealTimeS(), 0.5)) * 20.0F; Vector newMousePos = g_UInputMan.GetAbsoluteMousePosition(); @@ -248,4 +261,4 @@ namespace RTE { m_MouseButtonsEvents[0] = GUIInput::None; } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/GUI/Wrappers/GUIInputWrapper.h b/Source/GUI/Wrappers/GUIInputWrapper.h index 54af485d01..cc458d1c0a 100644 --- a/Source/GUI/Wrappers/GUIInputWrapper.h +++ b/Source/GUI/Wrappers/GUIInputWrapper.h @@ -14,7 +14,6 @@ namespace RTE { class GUIInputWrapper : public GUIInput { public: - #pragma region Creation /// /// Constructor method used to instantiate a GUIInputWrapper object in system memory. @@ -39,7 +38,6 @@ namespace RTE { #pragma endregion private: - const float m_KeyRepeatDelay = 0.10F; //!< The delay a key needs to be held to be considered a repeating input. TODO: Make this use proper OS repeating instead of this shit... std::array m_KeyHoldDuration; //!< How long each key has been held in order to set repeating inputs. @@ -72,8 +70,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - GUIInputWrapper(const GUIInputWrapper &reference) = delete; - GUIInputWrapper & operator=(const GUIInputWrapper &rhs) = delete; + GUIInputWrapper(const GUIInputWrapper& reference) = delete; + GUIInputWrapper& operator=(const GUIInputWrapper& rhs) = delete; }; -}; +}; // namespace RTE #endif diff --git a/Source/Lua/LuaAdapterDefinitions.h b/Source/Lua/LuaAdapterDefinitions.h index 1b7d7526ab..5d5690b4ea 100644 --- a/Source/Lua/LuaAdapterDefinitions.h +++ b/Source/Lua/LuaAdapterDefinitions.h @@ -91,18 +91,18 @@ namespace RTE { #pragma region Entity Lua Adapter Macros struct LuaAdaptersEntityCreate { - /// - /// Convenience macro to generate preset clone-create adapter functions that will return the exact pre-cast types, so we don't have to do: myNewActor = ToActor(PresetMan:GetPreset("AHuman", "Soldier Light", "All")):Clone() - /// But can instead do: myNewActor = CreateActor("Soldier Light", "All"); - /// Or even: myNewActor = CreateActor("Soldier Light"); - /// Or for a randomly selected Preset within a group: myNewActor = RandomActor("Light Troops"); - /// - #define LuaEntityCreateFunctionsDeclarationsForType(TYPE) \ - static TYPE * Create##TYPE(std::string preseName, std::string moduleName); \ - static TYPE * Create##TYPE(std::string preset); \ - static TYPE * Random##TYPE(std::string groupName, int moduleSpaceID); \ - static TYPE * Random##TYPE(std::string groupName, std::string dataModuleName); \ - static TYPE * Random##TYPE(std::string groupName) +/// +/// Convenience macro to generate preset clone-create adapter functions that will return the exact pre-cast types, so we don't have to do: myNewActor = ToActor(PresetMan:GetPreset("AHuman", "Soldier Light", "All")):Clone() +/// But can instead do: myNewActor = CreateActor("Soldier Light", "All"); +/// Or even: myNewActor = CreateActor("Soldier Light"); +/// Or for a randomly selected Preset within a group: myNewActor = RandomActor("Light Troops"); +/// +#define LuaEntityCreateFunctionsDeclarationsForType(TYPE) \ + static TYPE* Create##TYPE(std::string preseName, std::string moduleName); \ + static TYPE* Create##TYPE(std::string preset); \ + static TYPE* Random##TYPE(std::string groupName, int moduleSpaceID); \ + static TYPE* Random##TYPE(std::string groupName, std::string dataModuleName); \ + static TYPE* Random##TYPE(std::string groupName) LuaEntityCreateFunctionsDeclarationsForType(SoundContainer); LuaEntityCreateFunctionsDeclarationsForType(Attachable); @@ -135,11 +135,11 @@ namespace RTE { }; struct LuaAdaptersEntityClone { - /// - /// Convenience macro to generate a preset clone adapter function for a type. - /// - #define LuaEntityCloneFunctionDeclarationForType(TYPE) \ - static TYPE * Clone##TYPE(const TYPE *thisEntity) +/// +/// Convenience macro to generate a preset clone adapter function for a type. +/// +#define LuaEntityCloneFunctionDeclarationForType(TYPE) \ + static TYPE* Clone##TYPE(const TYPE* thisEntity) LuaEntityCloneFunctionDeclarationForType(Entity); LuaEntityCloneFunctionDeclarationForType(SoundContainer); @@ -176,16 +176,16 @@ namespace RTE { }; struct LuaAdaptersEntityCast { - /// - /// Convenience macro to generate type casting adapter functions for a type. - /// - #define LuaEntityCastFunctionsDeclarationsForType(TYPE) \ - static TYPE * To##TYPE(Entity *entity); \ - static const TYPE * ToConst##TYPE(const Entity *entity); \ - static bool Is##TYPE(Entity *entity); \ - static LuabindObjectWrapper * ToLuabindObject##TYPE(Entity *entity, lua_State *luaState) +/// +/// Convenience macro to generate type casting adapter functions for a type. +/// +#define LuaEntityCastFunctionsDeclarationsForType(TYPE) \ + static TYPE* To##TYPE(Entity* entity); \ + static const TYPE* ToConst##TYPE(const Entity* entity); \ + static bool Is##TYPE(Entity* entity); \ + static LuabindObjectWrapper* ToLuabindObject##TYPE(Entity* entity, lua_State* luaState) - static std::unordered_map> s_EntityToLuabindObjectCastFunctions; //!< Map of preset names to casting methods for ensuring objects are downcast properly when passed into Lua. + static std::unordered_map> s_EntityToLuabindObjectCastFunctions; //!< Map of preset names to casting methods for ensuring objects are downcast properly when passed into Lua. LuaEntityCastFunctionsDeclarationsForType(Entity); LuaEntityCastFunctionsDeclarationsForType(SoundContainer); @@ -227,12 +227,12 @@ namespace RTE { }; struct LuaAdaptersPropertyOwnershipSafetyFaker { - /// - /// Special handling for passing ownership through properties. If you try to pass null to this normally, LuaJIT crashes. - /// This handling avoids that, and is a bit safer since there's no actual ownership transfer from Lua to C++. - /// - #define LuaPropertyOwnershipSafetyFakerFunctionDeclaration(OBJECTTYPE, PROPERTYTYPE, SETTERFUNCTION) \ - static void OBJECTTYPE##SETTERFUNCTION(OBJECTTYPE *luaSelfObject, PROPERTYTYPE *objectToSet) +/// +/// Special handling for passing ownership through properties. If you try to pass null to this normally, LuaJIT crashes. +/// This handling avoids that, and is a bit safer since there's no actual ownership transfer from Lua to C++. +/// +#define LuaPropertyOwnershipSafetyFakerFunctionDeclaration(OBJECTTYPE, PROPERTYTYPE, SETTERFUNCTION) \ + static void OBJECTTYPE##SETTERFUNCTION(OBJECTTYPE* luaSelfObject, PROPERTYTYPE* objectToSet) LuaPropertyOwnershipSafetyFakerFunctionDeclaration(MOSRotating, SoundContainer, SetGibSound); LuaPropertyOwnershipSafetyFakerFunctionDeclaration(Attachable, AEmitter, SetBreakWound); @@ -303,98 +303,98 @@ namespace RTE { #pragma region Entity Lua Adapters struct LuaAdaptersEntity { // TODO this is a temporary fix for lua PresetName setting causing scripts to have to rerun. It should be replaced with a DisplayName property someday. - static void SetPresetName(Entity *luaSelfObject, const std::string &presetName); + static void SetPresetName(Entity* luaSelfObject, const std::string& presetName); }; #pragma endregion #pragma region Scene Lua Adapters struct LuaAdaptersScene { - static int CalculatePath1(Scene *luaSelfObject, const Vector &start, const Vector &end, bool movePathToGround, float digStrength) { return CalculatePath2(luaSelfObject, start, end, movePathToGround, digStrength, Activity::Teams::NoTeam); } - static int CalculatePath2(Scene *luaSelfObject, const Vector &start, const Vector &end, bool movePathToGround, float digStrength, Activity::Teams team); + static int CalculatePath1(Scene* luaSelfObject, const Vector& start, const Vector& end, bool movePathToGround, float digStrength) { return CalculatePath2(luaSelfObject, start, end, movePathToGround, digStrength, Activity::Teams::NoTeam); } + static int CalculatePath2(Scene* luaSelfObject, const Vector& start, const Vector& end, bool movePathToGround, float digStrength, Activity::Teams team); - static void CalculatePathAsync1(Scene *luaSelfObject, const luabind::object &callback, const Vector &start, const Vector &end, bool movePathToGround, float digStrength) { return CalculatePathAsync2(luaSelfObject, callback, start, end, movePathToGround, digStrength, Activity::Teams::NoTeam); } - static void CalculatePathAsync2(Scene *luaSelfObject, const luabind::object &callback, const Vector &start, const Vector &end, bool movePathToGround, float digStrength, Activity::Teams team); + static void CalculatePathAsync1(Scene* luaSelfObject, const luabind::object& callback, const Vector& start, const Vector& end, bool movePathToGround, float digStrength) { return CalculatePathAsync2(luaSelfObject, callback, start, end, movePathToGround, digStrength, Activity::Teams::NoTeam); } + static void CalculatePathAsync2(Scene* luaSelfObject, const luabind::object& callback, const Vector& start, const Vector& end, bool movePathToGround, float digStrength, Activity::Teams team); }; #pragma endregion #pragma region Actor Lua Adapters struct LuaAdaptersActor { - static std::vector * GetSceneWaypoints(Actor *luaSelfObject); + static std::vector* GetSceneWaypoints(Actor* luaSelfObject); }; #pragma endregion #pragma region AHuman Lua Adapters struct LuaAdaptersAHuman { - static void ReloadFirearms(AHuman *luaSelfObject); + static void ReloadFirearms(AHuman* luaSelfObject); }; #pragma endregion #pragma region Attachable Lua Adapters struct LuaAdaptersAttachable { - static Attachable * RemoveFromParent1(Attachable *luaSelfObject); - static Attachable * RemoveFromParent2(Attachable *luaSelfObject, bool addToMovableMan, bool addBreakWounds); + static Attachable* RemoveFromParent1(Attachable* luaSelfObject); + static Attachable* RemoveFromParent2(Attachable* luaSelfObject, bool addToMovableMan, bool addBreakWounds); }; #pragma endregion #pragma region GlobalScript Lua Adapters struct LuaAdaptersGlobalScript { - static void Deactivate(GlobalScript *luaSelfObject); + static void Deactivate(GlobalScript* luaSelfObject); }; #pragma endregion #pragma region Activity Lua Adapters struct LuaAdaptersActivity { - static void SendMessage1(Activity *luaSelfObject, const std::string &message); - static void SendMessage2(Activity *luaSelfObject, const std::string &message, luabind::object context); + static void SendMessage1(Activity* luaSelfObject, const std::string& message); + static void SendMessage2(Activity* luaSelfObject, const std::string& message, luabind::object context); }; #pragma endregion #pragma region MovableObject Lua Adapters struct LuaAdaptersMovableObject { - static bool HasScript(MovableObject *luaSelfObject, const std::string &scriptPath); - static bool AddScript(MovableObject *luaSelfObject, const std::string &scriptPath); - static bool EnableScript(MovableObject *luaSelfObject, const std::string &scriptPath); - static bool DisableScript1(MovableObject *luaSelfObject); - static bool DisableScript2(MovableObject *luaSelfObject, const std::string &scriptPath); - static void SendMessage1(MovableObject *luaSelfObject, const std::string &message); - static void SendMessage2(MovableObject *luaSelfObject, const std::string &message, luabind::object context); + static bool HasScript(MovableObject* luaSelfObject, const std::string& scriptPath); + static bool AddScript(MovableObject* luaSelfObject, const std::string& scriptPath); + static bool EnableScript(MovableObject* luaSelfObject, const std::string& scriptPath); + static bool DisableScript1(MovableObject* luaSelfObject); + static bool DisableScript2(MovableObject* luaSelfObject, const std::string& scriptPath); + static void SendMessage1(MovableObject* luaSelfObject, const std::string& message); + static void SendMessage2(MovableObject* luaSelfObject, const std::string& message, luabind::object context); }; #pragma endregion #pragma region MOSRotating Lua Adapters struct LuaAdaptersMOSRotating { - static void GibThis(MOSRotating *luaSelfObject); - static std::vector * GetWounds1(const MOSRotating *luaSelfObject); - static std::vector * GetWounds2(const MOSRotating *luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables); + static void GibThis(MOSRotating* luaSelfObject); + static std::vector* GetWounds1(const MOSRotating* luaSelfObject); + static std::vector* GetWounds2(const MOSRotating* luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables); // Need a seperate implementation function without the return so we can safely recurse. - static void GetWoundsImpl(const MOSRotating *luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables, std::vector &wounds); + static void GetWoundsImpl(const MOSRotating* luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables, std::vector& wounds); }; #pragma endregion #pragma region BuyMenuGUI Lua Adapters struct LuaAdaptersBuyMenuGUI { - static std::list * GetOrderList(const BuyMenuGUI *luaSelfObject); + static std::list* GetOrderList(const BuyMenuGUI* luaSelfObject); }; #pragma endregion #pragma region PieMenu Lua Adapters struct LuaAdaptersPieMenu { - static bool AddPieSlice(PieMenu *luaSelfObject, PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource); - static bool AddPieSliceIfPresetNameIsUnique1(PieMenu *luaSelfObject, PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource); - static bool AddPieSliceIfPresetNameIsUnique2(PieMenu *luaSelfObject, PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource); + static bool AddPieSlice(PieMenu* luaSelfObject, PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource); + static bool AddPieSliceIfPresetNameIsUnique1(PieMenu* luaSelfObject, PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource); + static bool AddPieSliceIfPresetNameIsUnique2(PieMenu* luaSelfObject, PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource); }; #pragma endregion #pragma region SceneObject Lua Adapters struct LuaAdaptersSceneObject { - static float GetTotalValue(const SceneObject *luaSelfObject, int nativeModule, float foreignMult); - static int GetBuyableMode(const SceneObject *luaSelfObject); + static float GetTotalValue(const SceneObject* luaSelfObject, int nativeModule, float foreignMult); + static int GetBuyableMode(const SceneObject* luaSelfObject); }; #pragma endregion #pragma region Turret Lua Adapters struct LuaAdaptersTurret { - static void AddMountedFirearm(Turret *luaSelfObject, HDFirearm *newMountedDevice); + static void AddMountedFirearm(Turret* luaSelfObject, HDFirearm* newMountedDevice); }; #pragma endregion @@ -405,31 +405,31 @@ namespace RTE { /// /// A reference to MovableMan, provided by Lua. /// A pointer to the MovableObject to be added. - static void AddMO(MovableMan &movableMan, MovableObject *movableObject); + static void AddMO(MovableMan& movableMan, MovableObject* movableObject); /// /// Adds the given Actor to MovableMan if it doesn't already exist in there, or prints an error if it does. /// /// A reference to MovableMan, provided by Lua. /// A pointer to the Actor to be added. - static void AddActor(MovableMan &movableMan, Actor *actor); + static void AddActor(MovableMan& movableMan, Actor* actor); /// /// Adds the given item MovableObject (generally a HeldDevice) to MovableMan if it doesn't already exist in there, or prints an error if it does. /// /// A reference to MovableMan, provided by Lua. /// A pointer to the item to be added. - static void AddItem(MovableMan &movableMan, HeldDevice *item); + static void AddItem(MovableMan& movableMan, HeldDevice* item); /// /// Adds the given particle MovableObject to MovableMan if it doesn't already exist in there, or prints an error if it does. /// /// A reference to MovableMan, provided by Lua. /// A pointer to the particle to be added. - static void AddParticle(MovableMan &movableMan, MovableObject *particle); + static void AddParticle(MovableMan& movableMan, MovableObject* particle); - static void SendGlobalMessage1(MovableMan &movableMan, const std::string& message); - static void SendGlobalMessage2(MovableMan &movableMan, const std::string& message, luabind::object context); + static void SendGlobalMessage1(MovableMan& movableMan, const std::string& message); + static void SendGlobalMessage2(MovableMan& movableMan, const std::string& message, luabind::object context); }; #pragma endregion @@ -439,13 +439,13 @@ namespace RTE { /// Gets the current number of ticks that the simulation should be updating with. Lua can't handle int64 (or long long apparently) so we'll expose this specialized function. /// /// The current fixed delta time that the simulation should be updating with, in ticks. - static double GetDeltaTimeTicks(const TimerMan &timerMan); + static double GetDeltaTimeTicks(const TimerMan& timerMan); /// /// Gets the number of ticks per second. Lua can't handle int64 (or long long apparently) so we'll expose this specialized function. /// /// The number of ticks per second. - static double GetTicksPerSecond(const TimerMan &timerMan); + static double GetTicksPerSecond(const TimerMan& timerMan); }; #pragma endregion @@ -456,21 +456,21 @@ namespace RTE { /// /// Which button to check for. /// Whether the mouse button is held or not. - static bool MouseButtonHeld(const UInputMan &uinputMan, int whichButton); + static bool MouseButtonHeld(const UInputMan& uinputMan, int whichButton); /// /// Gets whether a mouse button was pressed between the last update and the one previous to it. /// /// Which button to check for. /// Whether the mouse button is pressed or not. - static bool MouseButtonPressed(const UInputMan &uinputMan, int whichButton); + static bool MouseButtonPressed(const UInputMan& uinputMan, int whichButton); /// /// Gets whether a mouse button was released between the last update and the one previous to it. /// /// Which button to check for. /// Whether the mouse button is released or not. - static bool MouseButtonReleased(const UInputMan &uinputMan, int whichButton); + static bool MouseButtonReleased(const UInputMan& uinputMan, int whichButton); }; #pragma endregion @@ -483,7 +483,7 @@ namespace RTE { /// The class name of the Entity to reload. /// The module name of the Entity to reload. /// Whether or not the Entity was reloaded. - static bool ReloadEntityPreset1(PresetMan &presetMan, const std::string &presetName, const std::string &className, const std::string &moduleName); + static bool ReloadEntityPreset1(PresetMan& presetMan, const std::string& presetName, const std::string& className, const std::string& moduleName); /// /// Reloads the specified Entity preset in PresetMan. @@ -491,7 +491,7 @@ namespace RTE { /// The preset name of the Entity to reload. /// The class name of the Entity to reload. /// Whether or not the Entity was reloaded. - static bool ReloadEntityPreset2(PresetMan &presetMan, const std::string &presetName, const std::string &className); + static bool ReloadEntityPreset2(PresetMan& presetMan, const std::string& presetName, const std::string& className); /// /// Gets a list all previously read in (defined) Entities which are associated with a specific group. @@ -500,15 +500,15 @@ namespace RTE { /// The name of the least common denominator type of the Entities you want. "All" will look at all types. /// Whether to only get those of one specific DataModule (0-n), or all (-1). /// The list of all Entities with the given group and type in the module. - static std::list * GetAllEntitiesOfGroup(PresetMan &presetMan, const std::string &group, const std::string &type, int whichModule); - static std::list * GetAllEntitiesOfGroup2(PresetMan &presetMan, const std::string &group, const std::string &type) { return GetAllEntitiesOfGroup(presetMan, group, type, -1); } - static std::list * GetAllEntitiesOfGroup3(PresetMan &presetMan, const std::string &group) { return GetAllEntitiesOfGroup2(presetMan, group, "All"); } - + static std::list* GetAllEntitiesOfGroup(PresetMan& presetMan, const std::string& group, const std::string& type, int whichModule); + static std::list* GetAllEntitiesOfGroup2(PresetMan& presetMan, const std::string& group, const std::string& type) { return GetAllEntitiesOfGroup(presetMan, group, type, -1); } + static std::list* GetAllEntitiesOfGroup3(PresetMan& presetMan, const std::string& group) { return GetAllEntitiesOfGroup2(presetMan, group, "All"); } + /// /// Gets a list all previously read in (defined) Entities. /// /// The list of all Entities. - static std::list * GetAllEntities(PresetMan &presetMan) { return GetAllEntitiesOfGroup3(presetMan, "All"); } + static std::list* GetAllEntities(PresetMan& presetMan) { return GetAllEntitiesOfGroup3(presetMan, "All"); } }; #pragma endregion @@ -519,7 +519,7 @@ namespace RTE { /// /// The Box to wrap. /// A list of Boxes that make up the Box to wrap, wrapped appropriately for the current Scene. - static const std::list * WrapBoxes(SceneMan &sceneMan, const Box &boxToWrap); + static const std::list* WrapBoxes(SceneMan& sceneMan, const Box& boxToWrap); }; #pragma endregion @@ -532,7 +532,7 @@ namespace RTE { /// Position of primitive's center in Scene coordinates. /// Color to draw primitive with. /// A Lua table that contains the positions of the primitive's vertices, relative to the center position. - static void DrawPolygonPrimitive(PrimitiveMan &primitiveMan, const Vector ¢erPos, int color, const luabind::object &verticesTable); + static void DrawPolygonPrimitive(PrimitiveMan& primitiveMan, const Vector& centerPos, int color, const luabind::object& verticesTable); /// /// Schedule to draw a polygon primitive visible only to a specified player. @@ -542,7 +542,7 @@ namespace RTE { /// Position of primitive's center in Scene coordinates. /// Color to draw primitive with. /// A Lua table that contains the positions of the primitive's vertices, relative to the center position. - static void DrawPolygonPrimitiveForPlayer(PrimitiveMan &primitiveMan, int player, const Vector ¢erPos, int color, const luabind::object &verticesTable); + static void DrawPolygonPrimitiveForPlayer(PrimitiveMan& primitiveMan, int player, const Vector& centerPos, int color, const luabind::object& verticesTable); /// /// Schedule to draw a filled polygon primitive. @@ -551,7 +551,7 @@ namespace RTE { /// Start position of the primitive in Scene coordinates. /// Color to draw primitive with. /// A Lua table that contains the positions of the primitive's vertices, relative to the center position. - static void DrawPolygonFillPrimitive(PrimitiveMan &primitiveMan, const Vector &startPos, int color, const luabind::object &verticesTable); + static void DrawPolygonFillPrimitive(PrimitiveMan& primitiveMan, const Vector& startPos, int color, const luabind::object& verticesTable); /// /// Schedule to draw a filled polygon primitive visible only to a specified player. @@ -561,7 +561,7 @@ namespace RTE { /// Start position of the primitive in Scene coordinates. /// Color to draw primitive with. /// A Lua table that contains the positions of the primitive's vertices, relative to the center position. - static void DrawPolygonFillPrimitiveForPlayer(PrimitiveMan &primitiveMan, int player, const Vector &startPos, int color, const luabind::object &verticesTable); + static void DrawPolygonFillPrimitiveForPlayer(PrimitiveMan& primitiveMan, int player, const Vector& startPos, int color, const luabind::object& verticesTable); /// /// Schedules to draw multiple primitives of varying type with transparency enabled. @@ -569,7 +569,7 @@ namespace RTE { /// A reference to PrimitiveMan, provided by Lua. /// The transparency value the primitives should be drawn at. From 0 (opaque) to 100 (transparent). /// A Lua table of primitives to schedule drawing for. - static void DrawPrimitivesWithTransparency(PrimitiveMan &primitiveMan, int transValue, const luabind::object &primitivesTable); + static void DrawPrimitivesWithTransparency(PrimitiveMan& primitiveMan, int transValue, const luabind::object& primitivesTable); /// /// Schedule to draw multiple primitives of varying type with blending enabled. @@ -578,7 +578,7 @@ namespace RTE { /// The blending mode the primitives should be drawn with. See DrawBlendMode enumeration. /// The blending amount for all the channels. 0-100. /// A Lua table of primitives to schedule drawing for. - static void DrawPrimitivesWithBlending(PrimitiveMan &primitiveMan, int blendMode, int blendAmount, const luabind::object &primitivesTable); + static void DrawPrimitivesWithBlending(PrimitiveMan& primitiveMan, int blendMode, int blendAmount, const luabind::object& primitivesTable); /// /// Schedule to draw multiple primitives of varying type with blending enabled. @@ -590,7 +590,7 @@ namespace RTE { /// The blending amount for the Blue channel. 0-100. /// The blending amount for the Alpha channel. 0-100. /// A Lua table of primitives to schedule drawing for. - static void DrawPrimitivesWithBlendingPerChannel(PrimitiveMan &primitiveMan, int blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const luabind::object &primitivesTable); + static void DrawPrimitivesWithBlendingPerChannel(PrimitiveMan& primitiveMan, int blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const luabind::object& primitivesTable); }; #pragma endregion @@ -630,10 +630,10 @@ namespace RTE { /// Explicit deletion of any Entity instance that Lua owns. It will probably be handled by the GC, but this makes it instantaneous. /// /// The Entity to delete. - static void DeleteEntity(Entity *entityToDelete); + static void DeleteEntity(Entity* entityToDelete); }; #pragma endregion -} +} // namespace RTE #ifndef _MSC_VER #pragma GCC diagnostic pop diff --git a/Source/Lua/LuaAdapters.cpp b/Source/Lua/LuaAdapters.cpp index 57d421115f..46c7e768eb 100644 --- a/Source/Lua/LuaAdapters.cpp +++ b/Source/Lua/LuaAdapters.cpp @@ -5,44 +5,52 @@ namespace RTE { - std::unordered_map> LuaAdaptersEntityCast::s_EntityToLuabindObjectCastFunctions = {}; + std::unordered_map> LuaAdaptersEntityCast::s_EntityToLuabindObjectCastFunctions = {}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define LuaEntityCreateFunctionsDefinitionsForType(TYPE) \ - TYPE * LuaAdaptersEntityCreate::Create##TYPE(std::string preseName, std::string moduleName) { \ - const Entity *entityPreset = g_PresetMan.GetEntityPreset(#TYPE, preseName, moduleName); \ + TYPE* LuaAdaptersEntityCreate::Create##TYPE(std::string preseName, std::string moduleName) { \ + const Entity* entityPreset = g_PresetMan.GetEntityPreset(#TYPE, preseName, moduleName); \ if (!entityPreset) { \ g_ConsoleMan.PrintString(std::string("ERROR: There is no ") + std::string(#TYPE) + std::string(" of the Preset name \"") + preseName + std::string("\" defined in the \"") + moduleName + std::string("\" Data Module!")); \ return nullptr; \ } \ - return dynamic_cast(entityPreset->Clone()); \ + return dynamic_cast(entityPreset->Clone()); \ } \ - TYPE * LuaAdaptersEntityCreate::Create##TYPE(std::string preset) { \ + TYPE* LuaAdaptersEntityCreate::Create##TYPE(std::string preset) { \ return Create##TYPE(preset, "All"); \ } \ - TYPE * LuaAdaptersEntityCreate::Random##TYPE(std::string groupName, int moduleSpaceID) { \ - const Entity *entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech(groupName, #TYPE, moduleSpaceID); \ - if (!entityPreset) { entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech(groupName, #TYPE, g_PresetMan.GetModuleID("Base.rte")); } \ - if (!entityPreset) { entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech("Any", #TYPE, moduleSpaceID); } \ + TYPE* LuaAdaptersEntityCreate::Random##TYPE(std::string groupName, int moduleSpaceID) { \ + const Entity* entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech(groupName, #TYPE, moduleSpaceID); \ if (!entityPreset) { \ - g_ConsoleMan.PrintString(std::string("WARNING: Could not find any ") + std::string(#TYPE) + std::string(" defined in a Group called \"") + groupName + std::string("\" in module ") + g_PresetMan.GetDataModuleName(moduleSpaceID) + "!"); \ + entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech(groupName, #TYPE, g_PresetMan.GetModuleID("Base.rte")); \ + } \ + if (!entityPreset) { \ + entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech("Any", #TYPE, moduleSpaceID); \ + } \ + if (!entityPreset) { \ + g_ConsoleMan.PrintString(std::string("WARNING: Could not find any ") + std::string(#TYPE) + std::string(" defined in a Group called \"") + groupName + std::string("\" in module ") + g_PresetMan.GetDataModuleName(moduleSpaceID) + "!"); \ return nullptr; \ } \ - return dynamic_cast(entityPreset->Clone()); \ + return dynamic_cast(entityPreset->Clone()); \ } \ - TYPE * LuaAdaptersEntityCreate::Random##TYPE(std::string groupName, std::string dataModuleName) { \ + TYPE* LuaAdaptersEntityCreate::Random##TYPE(std::string groupName, std::string dataModuleName) { \ int moduleSpaceID = g_PresetMan.GetModuleID(dataModuleName); \ - const Entity *entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech(groupName, #TYPE, moduleSpaceID); \ - if (!entityPreset) { entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech(groupName, #TYPE, g_PresetMan.GetModuleID("Base.rte")); } \ - if (!entityPreset) { entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech("Any", #TYPE, moduleSpaceID); } \ + const Entity* entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech(groupName, #TYPE, moduleSpaceID); \ + if (!entityPreset) { \ + entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech(groupName, #TYPE, g_PresetMan.GetModuleID("Base.rte")); \ + } \ + if (!entityPreset) { \ + entityPreset = g_PresetMan.GetRandomBuyableOfGroupFromTech("Any", #TYPE, moduleSpaceID); \ + } \ if (!entityPreset) { \ g_ConsoleMan.PrintString(std::string("WARNING: Could not find any ") + std::string(#TYPE) + std::string(" defined in a Group called \"") + groupName + std::string("\" in module ") + dataModuleName + "!"); \ return nullptr; \ } \ - return dynamic_cast(entityPreset->Clone()); \ + return dynamic_cast(entityPreset->Clone()); \ } \ - TYPE * LuaAdaptersEntityCreate::Random##TYPE(std::string groupName) { \ + TYPE* LuaAdaptersEntityCreate::Random##TYPE(std::string groupName) { \ return Random##TYPE(groupName, "All"); \ } @@ -75,12 +83,12 @@ namespace RTE { LuaEntityCreateFunctionsDefinitionsForType(PieSlice); LuaEntityCreateFunctionsDefinitionsForType(PieMenu); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define LuaEntityCloneFunctionDefinitionForType(TYPE) \ - TYPE * LuaAdaptersEntityClone::Clone##TYPE(const TYPE *thisEntity) { \ + TYPE* LuaAdaptersEntityClone::Clone##TYPE(const TYPE* thisEntity) { \ if (thisEntity) { \ - return dynamic_cast(thisEntity->Clone()); \ + return dynamic_cast(thisEntity->Clone()); \ } \ g_ConsoleMan.PrintString(std::string("ERROR: Tried to clone a ") + std::string(#TYPE) + std::string(" reference that is nil!")); \ return nullptr; \ @@ -119,24 +127,28 @@ namespace RTE { LuaEntityCloneFunctionDefinitionForType(PieSlice); LuaEntityCloneFunctionDefinitionForType(PieMenu); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define LuaEntityCastFunctionsDefinitionsForType(TYPE) \ - TYPE * LuaAdaptersEntityCast::To##TYPE(Entity *entity) { \ - TYPE *targetType = dynamic_cast(entity); \ - if (!targetType) { g_ConsoleMan.PrintString(std::string("ERROR: Tried to convert a non-") + std::string(#TYPE) + std::string(" Entity reference to an ") + std::string(#TYPE) + std::string(" reference! Entity was ") + (entity ? entity->GetPresetName() : "nil")); } \ + TYPE* LuaAdaptersEntityCast::To##TYPE(Entity* entity) { \ + TYPE* targetType = dynamic_cast(entity); \ + if (!targetType) { \ + g_ConsoleMan.PrintString(std::string("ERROR: Tried to convert a non-") + std::string(#TYPE) + std::string(" Entity reference to an ") + std::string(#TYPE) + std::string(" reference! Entity was ") + (entity ? entity->GetPresetName() : "nil")); \ + } \ return targetType; \ } \ - const TYPE * LuaAdaptersEntityCast::ToConst##TYPE(const Entity *entity) { \ - const TYPE *targetType = dynamic_cast(entity); \ - if (!targetType) { g_ConsoleMan.PrintString(std::string("ERROR: Tried to convert a non-") + std::string(#TYPE) + std::string(" Entity reference to an ") + std::string(#TYPE) + std::string(" reference! Entity was ") + (entity ? entity->GetPresetName() : "nil")); } \ + const TYPE* LuaAdaptersEntityCast::ToConst##TYPE(const Entity* entity) { \ + const TYPE* targetType = dynamic_cast(entity); \ + if (!targetType) { \ + g_ConsoleMan.PrintString(std::string("ERROR: Tried to convert a non-") + std::string(#TYPE) + std::string(" Entity reference to an ") + std::string(#TYPE) + std::string(" reference! Entity was ") + (entity ? entity->GetPresetName() : "nil")); \ + } \ return targetType; \ } \ - bool LuaAdaptersEntityCast::Is##TYPE(Entity *entity) { \ - return dynamic_cast(entity) ? true : false; \ + bool LuaAdaptersEntityCast::Is##TYPE(Entity* entity) { \ + return dynamic_cast(entity) ? true : false; \ } \ - LuabindObjectWrapper * LuaAdaptersEntityCast::ToLuabindObject##TYPE (Entity *entity, lua_State *luaState) { \ - return new LuabindObjectWrapper(new luabind::object(luaState, dynamic_cast(entity)), ""); \ + LuabindObjectWrapper* LuaAdaptersEntityCast::ToLuabindObject##TYPE(Entity* entity, lua_State* luaState) { \ + return new LuabindObjectWrapper(new luabind::object(luaState, dynamic_cast(entity)), ""); \ } \ /* Bullshit semi-hack to automatically populate the Luabind Object cast function map that is used in LuaMan::RunScriptFunctionObject */ \ static const bool EntityToLuabindObjectCastMapAutoInserterForType##TYPE = []() { \ @@ -182,11 +194,11 @@ namespace RTE { LuaEntityCastFunctionsDefinitionsForType(PieSlice); LuaEntityCastFunctionsDefinitionsForType(PieMenu); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define LuaPropertyOwnershipSafetyFakerFunctionDefinition(OBJECTTYPE, PROPERTYTYPE, SETTERFUNCTION) \ - void LuaAdaptersPropertyOwnershipSafetyFaker::OBJECTTYPE##SETTERFUNCTION(OBJECTTYPE *luaSelfObject, PROPERTYTYPE *objectToSet) { \ - luaSelfObject->SETTERFUNCTION(objectToSet ? dynamic_cast(objectToSet->Clone()) : nullptr); \ + void LuaAdaptersPropertyOwnershipSafetyFaker::OBJECTTYPE##SETTERFUNCTION(OBJECTTYPE* luaSelfObject, PROPERTYTYPE* objectToSet) { \ + luaSelfObject->SETTERFUNCTION(objectToSet ? dynamic_cast(objectToSet->Clone()) : nullptr); \ } LuaPropertyOwnershipSafetyFakerFunctionDefinition(MOSRotating, SoundContainer, SetGibSound); @@ -253,18 +265,18 @@ namespace RTE { LuaPropertyOwnershipSafetyFakerFunctionDefinition(HDFirearm, SoundContainer, SetReloadStartSound); LuaPropertyOwnershipSafetyFakerFunctionDefinition(HDFirearm, SoundContainer, SetReloadEndSound); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersEntity::SetPresetName(Entity *luaSelfObject, const std::string &presetName) { + void LuaAdaptersEntity::SetPresetName(Entity* luaSelfObject, const std::string& presetName) { luaSelfObject->SetPresetName(presetName, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector * LuaAdaptersActor::GetSceneWaypoints(Actor *luaSelfObject) { - std::vector *sceneWaypoints = new std::vector(); + std::vector* LuaAdaptersActor::GetSceneWaypoints(Actor* luaSelfObject) { + std::vector* sceneWaypoints = new std::vector(); sceneWaypoints->reserve(luaSelfObject->GetWaypointsSize()); - for (auto &[sceneWaypoint, movableObjectWaypoint] : luaSelfObject->GetWaypointList()) { + for (auto& [sceneWaypoint, movableObjectWaypoint]: luaSelfObject->GetWaypointList()) { if (movableObjectWaypoint == nullptr) { sceneWaypoints->emplace_back(sceneWaypoint); } @@ -272,15 +284,15 @@ namespace RTE { return sceneWaypoints; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaAdaptersScene::CalculatePath2(Scene *luaSelfObject, const Vector &start, const Vector &end, bool movePathToGround, float digStrength, Activity::Teams team) { + int LuaAdaptersScene::CalculatePath2(Scene* luaSelfObject, const Vector& start, const Vector& end, bool movePathToGround, float digStrength, Activity::Teams team) { std::list& threadScenePath = luaSelfObject->GetScenePath(); team = std::clamp(team, Activity::Teams::NoTeam, Activity::Teams::TeamFour); luaSelfObject->CalculatePath(start, end, threadScenePath, digStrength, team); if (!threadScenePath.empty()) { if (movePathToGround) { - for (Vector &scenePathPoint : threadScenePath) { + for (Vector& scenePathPoint: threadScenePath) { scenePathPoint = g_SceneMan.MovePointToGround(scenePathPoint, 20, 15); } } @@ -290,11 +302,11 @@ namespace RTE { return -1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersScene::CalculatePathAsync2(Scene *luaSelfObject, const luabind::object &callbackParam, const Vector &start, const Vector &end, bool movePathToGround, float digStrength, Activity::Teams team) { + void LuaAdaptersScene::CalculatePathAsync2(Scene* luaSelfObject, const luabind::object& callbackParam, const Vector& start, const Vector& end, bool movePathToGround, float digStrength, Activity::Teams team) { team = std::clamp(team, Activity::Teams::NoTeam, Activity::Teams::TeamFour); - + // So, luabind::object is a weak reference, holding just a stack and a position in the stack // This means it's unsafe to store on the C++ side if we do basically anything with the lua state before using it // As such, we need to store this function somewhere safely within our Lua state for us to access later when we need it @@ -312,13 +324,13 @@ namespace RTE { auto callLuaCallback = [luaState, thisCallbackId, movePathToGround](std::shared_ptr pathRequestVol) { // This callback is called from the async pathing thread, so we need to further delay this logic into the main thread (via AddLuaScriptCallback) g_LuaMan.AddLuaScriptCallback([luaState, thisCallbackId, movePathToGround, pathRequestVol]() { - PathRequest pathRequest = const_cast(*pathRequestVol); // erh, to work with luabind etc + PathRequest pathRequest = const_cast(*pathRequestVol); // erh, to work with luabind etc if (movePathToGround) { - for (Vector &scenePathPoint : pathRequest.path) { + for (Vector& scenePathPoint: pathRequest.path) { scenePathPoint = g_SceneMan.MovePointToGround(scenePathPoint, 20, 15); } } - + luabind::call_function(luaState->GetLuaState(), "_TriggerAsyncPathCallback", thisCallbackId, pathRequest); }); }; @@ -326,50 +338,50 @@ namespace RTE { luaSelfObject->CalculatePathAsync(start, end, digStrength, team, callLuaCallback); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersAHuman::ReloadFirearms(AHuman *luaSelfObject) { + void LuaAdaptersAHuman::ReloadFirearms(AHuman* luaSelfObject) { luaSelfObject->ReloadFirearms(false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float LuaAdaptersSceneObject::GetTotalValue(const SceneObject *luaSelfObject, int nativeModule, float foreignMult) { + float LuaAdaptersSceneObject::GetTotalValue(const SceneObject* luaSelfObject, int nativeModule, float foreignMult) { return luaSelfObject->GetTotalValue(nativeModule, foreignMult, 1.0F); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaAdaptersSceneObject::GetBuyableMode(const SceneObject *luaSelfObject) { + int LuaAdaptersSceneObject::GetBuyableMode(const SceneObject* luaSelfObject) { return static_cast(luaSelfObject->GetBuyableMode()); } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersActivity::SendMessage1(Activity *luaSelfObject, const std::string &message) { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void LuaAdaptersActivity::SendMessage1(Activity* luaSelfObject, const std::string& message) { luabind::object context; SendMessage2(luaSelfObject, message, context); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersActivity::SendMessage2(Activity *luaSelfObject, const std::string &message, luabind::object context) { + void LuaAdaptersActivity::SendMessage2(Activity* luaSelfObject, const std::string& message, luabind::object context) { GAScripted* scriptedActivity = dynamic_cast(luaSelfObject); if (scriptedActivity) { LuabindObjectWrapper wrapper(&context, "", false); - scriptedActivity->RunLuaFunction("OnMessage", {}, { message }, { &wrapper }); + scriptedActivity->RunLuaFunction("OnMessage", {}, {message}, {&wrapper}); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersMovableObject::HasScript(MovableObject *luaSelfObject, const std::string &scriptPath) { + bool LuaAdaptersMovableObject::HasScript(MovableObject* luaSelfObject, const std::string& scriptPath) { return luaSelfObject->HasScript(g_PresetMan.GetFullModulePath(scriptPath)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersMovableObject::AddScript(MovableObject *luaSelfObject, const std::string &scriptPath) { + bool LuaAdaptersMovableObject::AddScript(MovableObject* luaSelfObject, const std::string& scriptPath) { switch (std::string correctedScriptPath = g_PresetMan.GetFullModulePath(scriptPath); luaSelfObject->LoadScript(correctedScriptPath)) { case 0: return true; @@ -395,66 +407,65 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersMovableObject::EnableScript(MovableObject *luaSelfObject, const std::string &scriptPath) { + bool LuaAdaptersMovableObject::EnableScript(MovableObject* luaSelfObject, const std::string& scriptPath) { return luaSelfObject->EnableOrDisableScript(g_PresetMan.GetFullModulePath(scriptPath), true); } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool LuaAdaptersMovableObject::DisableScript1(MovableObject* luaSelfObject) { std::string currentScriptFilePath(g_LuaMan.GetThreadCurrentLuaState()->GetCurrentlyRunningScriptFilePath()); return luaSelfObject->EnableOrDisableScript(currentScriptFilePath, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersMovableObject::DisableScript2(MovableObject *luaSelfObject, const std::string &scriptPath) { + bool LuaAdaptersMovableObject::DisableScript2(MovableObject* luaSelfObject, const std::string& scriptPath) { return luaSelfObject->EnableOrDisableScript(g_PresetMan.GetFullModulePath(scriptPath), false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableObject::SendMessage1(MovableObject *luaSelfObject, const std::string &message) { - luaSelfObject->RunScriptedFunctionInAppropriateScripts("OnMessage", false, false, {}, { message }); + void LuaAdaptersMovableObject::SendMessage1(MovableObject* luaSelfObject, const std::string& message) { + luaSelfObject->RunScriptedFunctionInAppropriateScripts("OnMessage", false, false, {}, {message}); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableObject::SendMessage2(MovableObject *luaSelfObject, const std::string &message, luabind::object context) { + void LuaAdaptersMovableObject::SendMessage2(MovableObject* luaSelfObject, const std::string& message, luabind::object context) { LuabindObjectWrapper wrapper(&context, "", false); - luaSelfObject->RunScriptedFunctionInAppropriateScripts("OnMessage", false, false, {}, { message }, { &wrapper }); + luaSelfObject->RunScriptedFunctionInAppropriateScripts("OnMessage", false, false, {}, {message}, {&wrapper}); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMOSRotating::GibThis(MOSRotating *luaSelfObject) { + void LuaAdaptersMOSRotating::GibThis(MOSRotating* luaSelfObject) { luaSelfObject->GibThis(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector * LuaAdaptersMOSRotating::GetWounds1(const MOSRotating *luaSelfObject) { + std::vector* LuaAdaptersMOSRotating::GetWounds1(const MOSRotating* luaSelfObject) { return GetWounds2(luaSelfObject, true, false, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector * LuaAdaptersMOSRotating::GetWounds2(const MOSRotating *luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) { - auto *wounds = new std::vector(); + std::vector* LuaAdaptersMOSRotating::GetWounds2(const MOSRotating* luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) { + auto* wounds = new std::vector(); GetWoundsImpl(luaSelfObject, includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables, *wounds); return wounds; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMOSRotating::GetWoundsImpl(const MOSRotating *luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables, std::vector &wounds) { + void LuaAdaptersMOSRotating::GetWoundsImpl(const MOSRotating* luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables, std::vector& wounds) { wounds.insert(wounds.end(), luaSelfObject->GetWoundList().begin(), luaSelfObject->GetWoundList().end()); if (includePositiveDamageAttachables || includeNegativeDamageAttachables || includeNoDamageAttachables) { - for (const Attachable *attachable : luaSelfObject->GetAttachables()) { + for (const Attachable* attachable: luaSelfObject->GetAttachables()) { bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); @@ -466,74 +477,74 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::list * LuaAdaptersBuyMenuGUI::GetOrderList(const BuyMenuGUI *luaSelfObject) { - std::list constOrderList; + std::list* LuaAdaptersBuyMenuGUI::GetOrderList(const BuyMenuGUI* luaSelfObject) { + std::list constOrderList; luaSelfObject->GetOrderList(constOrderList); // Previously I tried to push back a cloned object for const-correctness (and giving unique ptr so luabind would clean it up after) // This is needed cause lua doesn't really enjoy being given a const SceneObject* // But it didn't like that. So eh auto* orderList = new std::list(); - for (const SceneObject *constObjectInOrderList : constOrderList) { - orderList->push_back( const_cast(constObjectInOrderList) ); + for (const SceneObject* constObjectInOrderList: constOrderList) { + orderList->push_back(const_cast(constObjectInOrderList)); } return orderList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Attachable * LuaAdaptersAttachable::RemoveFromParent1(Attachable *luaSelfObject) { + Attachable* LuaAdaptersAttachable::RemoveFromParent1(Attachable* luaSelfObject) { if (luaSelfObject->IsAttached()) { return luaSelfObject->GetParent()->RemoveAttachable(luaSelfObject); } return luaSelfObject; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Attachable * LuaAdaptersAttachable::RemoveFromParent2(Attachable *luaSelfObject, bool addToMovableMan, bool addBreakWounds) { + Attachable* LuaAdaptersAttachable::RemoveFromParent2(Attachable* luaSelfObject, bool addToMovableMan, bool addBreakWounds) { if (luaSelfObject->IsAttached()) { return luaSelfObject->GetParent()->RemoveAttachable(luaSelfObject, addToMovableMan, addBreakWounds); } return luaSelfObject; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersTurret::AddMountedFirearm(Turret *luaSelfObject, HDFirearm *newMountedDevice) { + void LuaAdaptersTurret::AddMountedFirearm(Turret* luaSelfObject, HDFirearm* newMountedDevice) { luaSelfObject->AddMountedDevice(newMountedDevice); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersGlobalScript::Deactivate(GlobalScript *luaSelfObject) { + void LuaAdaptersGlobalScript::Deactivate(GlobalScript* luaSelfObject) { luaSelfObject->SetActive(false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPieMenu::AddPieSlice(PieMenu *luaSelfObject, PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource) { + bool LuaAdaptersPieMenu::AddPieSlice(PieMenu* luaSelfObject, PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource) { return luaSelfObject->AddPieSlice(pieSliceToAdd, pieSliceOriginalSource, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique1(PieMenu *luaSelfObject, PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource) { + bool LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique1(PieMenu* luaSelfObject, PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource) { return luaSelfObject->AddPieSliceIfPresetNameIsUnique(pieSliceToAdd, pieSliceOriginalSource, false, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique2(PieMenu *luaSelfObject, PieSlice *pieSliceToAdd, const Entity *pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource) { + bool LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique2(PieMenu* luaSelfObject, PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource) { return luaSelfObject->AddPieSliceIfPresetNameIsUnique(pieSliceToAdd, pieSliceOriginalSource, onlyCheckPieSlicesWithSameOriginalSource, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::AddMO(MovableMan &movableMan, MovableObject *movableObject) { + void LuaAdaptersMovableMan::AddMO(MovableMan& movableMan, MovableObject* movableObject) { if (movableMan.ValidMO(movableObject)) { g_ConsoleMan.PrintString("ERROR: Tried to add a MovableObject that already exists in the simulation! " + movableObject->GetPresetName()); } else { @@ -541,9 +552,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::AddActor(MovableMan &movableMan, Actor *actor) { + void LuaAdaptersMovableMan::AddActor(MovableMan& movableMan, Actor* actor) { if (movableMan.IsActor(actor)) { g_ConsoleMan.PrintString("ERROR: Tried to add an Actor that already exists in the simulation!" + actor->GetPresetName()); } else { @@ -551,19 +562,19 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::AddItem(MovableMan &movableMan, HeldDevice *item) { - if (movableMan.ValidMO(dynamic_cast(item))) { + void LuaAdaptersMovableMan::AddItem(MovableMan& movableMan, HeldDevice* item) { + if (movableMan.ValidMO(dynamic_cast(item))) { g_ConsoleMan.PrintString("ERROR: Tried to add an Item that already exists in the simulation!" + item->GetPresetName()); } else { movableMan.AddItem(item); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::AddParticle(MovableMan &movableMan, MovableObject *particle) { + void LuaAdaptersMovableMan::AddParticle(MovableMan& movableMan, MovableObject* particle) { if (movableMan.ValidMO(particle)) { g_ConsoleMan.PrintString("ERROR: Tried to add a Particle that already exists in the simulation!" + particle->GetPresetName()); } else { @@ -571,165 +582,165 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::SendGlobalMessage1(MovableMan &movableMan, const std::string &message) { + void LuaAdaptersMovableMan::SendGlobalMessage1(MovableMan& movableMan, const std::string& message) { GAScripted* scriptedActivity = dynamic_cast(g_ActivityMan.GetActivity()); if (scriptedActivity) { - scriptedActivity->RunLuaFunction("OnGlobalMessage", {}, { message }); + scriptedActivity->RunLuaFunction("OnGlobalMessage", {}, {message}); } - movableMan.RunLuaFunctionOnAllMOs("OnGlobalMessage", true, {}, { message }); + movableMan.RunLuaFunctionOnAllMOs("OnGlobalMessage", true, {}, {message}); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::SendGlobalMessage2(MovableMan &movableMan, const std::string &message, luabind::object context) { + void LuaAdaptersMovableMan::SendGlobalMessage2(MovableMan& movableMan, const std::string& message, luabind::object context) { LuabindObjectWrapper wrapper(&context, "", false); GAScripted* scriptedActivity = dynamic_cast(g_ActivityMan.GetActivity()); if (scriptedActivity) { - scriptedActivity->RunLuaFunction("OnGlobalMessage", {}, { message }, { &wrapper }); + scriptedActivity->RunLuaFunction("OnGlobalMessage", {}, {message}, {&wrapper}); } - movableMan.RunLuaFunctionOnAllMOs("OnGlobalMessage", true, {}, { message }, { &wrapper }); + movableMan.RunLuaFunctionOnAllMOs("OnGlobalMessage", true, {}, {message}, {&wrapper}); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - double LuaAdaptersTimerMan::GetDeltaTimeTicks(const TimerMan &timerMan) { + double LuaAdaptersTimerMan::GetDeltaTimeTicks(const TimerMan& timerMan) { return static_cast(timerMan.GetDeltaTimeTicks()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - double LuaAdaptersTimerMan::GetTicksPerSecond(const TimerMan &timerMan) { + double LuaAdaptersTimerMan::GetTicksPerSecond(const TimerMan& timerMan) { return static_cast(timerMan.GetTicksPerSecond()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersUInputMan::MouseButtonHeld(const UInputMan &uinputMan, int whichButton) { + bool LuaAdaptersUInputMan::MouseButtonHeld(const UInputMan& uinputMan, int whichButton) { return uinputMan.MouseButtonHeld(whichButton, Players::PlayerOne); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersUInputMan::MouseButtonPressed(const UInputMan &uinputMan, int whichButton) { + bool LuaAdaptersUInputMan::MouseButtonPressed(const UInputMan& uinputMan, int whichButton) { return uinputMan.MouseButtonPressed(whichButton, Players::PlayerOne); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersUInputMan::MouseButtonReleased(const UInputMan &uinputMan, int whichButton) { + bool LuaAdaptersUInputMan::MouseButtonReleased(const UInputMan& uinputMan, int whichButton) { return uinputMan.MouseButtonReleased(whichButton, Players::PlayerOne); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPresetMan::ReloadEntityPreset1(PresetMan &presetMan, const std::string &presetName, const std::string &className, const std::string &moduleName) { + bool LuaAdaptersPresetMan::ReloadEntityPreset1(PresetMan& presetMan, const std::string& presetName, const std::string& className, const std::string& moduleName) { return presetMan.ReloadEntityPreset(presetName, className, moduleName); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPresetMan::ReloadEntityPreset2(PresetMan &presetMan, const std::string &presetName, const std::string &className) { + bool LuaAdaptersPresetMan::ReloadEntityPreset2(PresetMan& presetMan, const std::string& presetName, const std::string& className) { return ReloadEntityPreset1(presetMan, presetName, className, ""); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::list * LuaAdaptersPresetMan::GetAllEntitiesOfGroup(PresetMan &presetMan, const std::string &group, const std::string &type, int whichModule) { - std::list *entityList = new std::list(); + std::list* LuaAdaptersPresetMan::GetAllEntitiesOfGroup(PresetMan& presetMan, const std::string& group, const std::string& type, int whichModule) { + std::list* entityList = new std::list(); presetMan.GetAllOfGroup(*entityList, group, type, whichModule); return entityList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::list * LuaAdaptersSceneMan::WrapBoxes(SceneMan &sceneMan, const Box &boxToWrap) { - std::list *wrappedBoxes = new std::list(); + const std::list* LuaAdaptersSceneMan::WrapBoxes(SceneMan& sceneMan, const Box& boxToWrap) { + std::list* wrappedBoxes = new std::list(); sceneMan.WrapBox(boxToWrap, *wrappedBoxes); return wrappedBoxes; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPolygonPrimitive(PrimitiveMan &primitiveMan, const Vector ¢erPos, int color, const luabind::object &verticesTable) { - primitiveMan.DrawPolygonOrPolygonFillPrimitive(-1, centerPos, color, ConvertLuaTableToVectorOfType(verticesTable), false); + void LuaAdaptersPrimitiveMan::DrawPolygonPrimitive(PrimitiveMan& primitiveMan, const Vector& centerPos, int color, const luabind::object& verticesTable) { + primitiveMan.DrawPolygonOrPolygonFillPrimitive(-1, centerPos, color, ConvertLuaTableToVectorOfType(verticesTable), false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPolygonPrimitiveForPlayer(PrimitiveMan &primitiveMan, int player, const Vector ¢erPos, int color, const luabind::object &verticesTable) { - primitiveMan.DrawPolygonOrPolygonFillPrimitive(player, centerPos, color, ConvertLuaTableToVectorOfType(verticesTable), false); + void LuaAdaptersPrimitiveMan::DrawPolygonPrimitiveForPlayer(PrimitiveMan& primitiveMan, int player, const Vector& centerPos, int color, const luabind::object& verticesTable) { + primitiveMan.DrawPolygonOrPolygonFillPrimitive(player, centerPos, color, ConvertLuaTableToVectorOfType(verticesTable), false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitive(PrimitiveMan &primitiveMan, const Vector &startPos, int color, const luabind::object &verticesTable) { - primitiveMan.DrawPolygonOrPolygonFillPrimitive(-1, startPos, color, ConvertLuaTableToVectorOfType(verticesTable), true); + void LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitive(PrimitiveMan& primitiveMan, const Vector& startPos, int color, const luabind::object& verticesTable) { + primitiveMan.DrawPolygonOrPolygonFillPrimitive(-1, startPos, color, ConvertLuaTableToVectorOfType(verticesTable), true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitiveForPlayer(PrimitiveMan &primitiveMan, int player, const Vector &startPos, int color, const luabind::object &verticesTable) { - primitiveMan.DrawPolygonOrPolygonFillPrimitive(player, startPos, color, ConvertLuaTableToVectorOfType(verticesTable), true); + void LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitiveForPlayer(PrimitiveMan& primitiveMan, int player, const Vector& startPos, int color, const luabind::object& verticesTable) { + primitiveMan.DrawPolygonOrPolygonFillPrimitive(player, startPos, color, ConvertLuaTableToVectorOfType(verticesTable), true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPrimitivesWithTransparency(PrimitiveMan &primitiveMan, int transValue, const luabind::object &primitivesTable) { - primitiveMan.SchedulePrimitivesForBlendedDrawing(DrawBlendMode::BlendTransparency, transValue, transValue, transValue, BlendAmountLimits::MinBlend, ConvertLuaTableToVectorOfType(primitivesTable)); + void LuaAdaptersPrimitiveMan::DrawPrimitivesWithTransparency(PrimitiveMan& primitiveMan, int transValue, const luabind::object& primitivesTable) { + primitiveMan.SchedulePrimitivesForBlendedDrawing(DrawBlendMode::BlendTransparency, transValue, transValue, transValue, BlendAmountLimits::MinBlend, ConvertLuaTableToVectorOfType(primitivesTable)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlending(PrimitiveMan &primitiveMan, int blendMode, int blendAmount, const luabind::object &primitivesTable) { - primitiveMan.SchedulePrimitivesForBlendedDrawing(static_cast(blendMode), blendAmount, blendAmount, blendAmount, blendAmount, ConvertLuaTableToVectorOfType(primitivesTable)); + void LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlending(PrimitiveMan& primitiveMan, int blendMode, int blendAmount, const luabind::object& primitivesTable) { + primitiveMan.SchedulePrimitivesForBlendedDrawing(static_cast(blendMode), blendAmount, blendAmount, blendAmount, blendAmount, ConvertLuaTableToVectorOfType(primitivesTable)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlendingPerChannel(PrimitiveMan &primitiveMan, int blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const luabind::object &primitivesTable) { - primitiveMan.SchedulePrimitivesForBlendedDrawing(static_cast(blendMode), blendAmountR, blendAmountG, blendAmountB, blendAmountA, ConvertLuaTableToVectorOfType(primitivesTable)); + void LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlendingPerChannel(PrimitiveMan& primitiveMan, int blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const luabind::object& primitivesTable) { + primitiveMan.SchedulePrimitivesForBlendedDrawing(static_cast(blendMode), blendAmountR, blendAmountG, blendAmountB, blendAmountA, ConvertLuaTableToVectorOfType(primitivesTable)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float LuaAdaptersUtility::GetMPP() { return c_MPP; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float LuaAdaptersUtility::GetPPM() { return c_PPM; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float LuaAdaptersUtility::GetLPP() { return c_LPP; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float LuaAdaptersUtility::GetPPL() { return c_PPL; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float LuaAdaptersUtility::GetPathFindingDefaultDigStrength() { return c_PathFindingDefaultDigStrength; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersUtility::DeleteEntity(Entity *entityToDelete) { + void LuaAdaptersUtility::DeleteEntity(Entity* entityToDelete) { delete entityToDelete; entityToDelete = nullptr; } #pragma endregion -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Lua/LuaBindingRegisterDefinitions.h b/Source/Lua/LuaBindingRegisterDefinitions.h index 3459f7c69c..b1006a52b4 100644 --- a/Source/Lua/LuaBindingRegisterDefinitions.h +++ b/Source/Lua/LuaBindingRegisterDefinitions.h @@ -7,185 +7,185 @@ namespace RTE { // Should be ordered with most-derived classes first -#define LIST_OF_LUABOUND_OBJECTS \ - /* SystemLuaBindings */ \ - PER_LUA_BINDING(Box) \ - PER_LUA_BINDING(Controller) \ - PER_LUA_BINDING(DataModule) \ - PER_LUA_BINDING(Timer) \ - PER_LUA_BINDING(Vector) \ - PER_LUA_BINDING(PathRequest) \ - /* ManagerLuaBindings */ \ - PER_LUA_BINDING(ActivityMan) \ - PER_LUA_BINDING(AudioMan) \ - PER_LUA_BINDING(CameraMan) \ - PER_LUA_BINDING(ConsoleMan) \ - PER_LUA_BINDING(FrameMan) \ - PER_LUA_BINDING(MetaMan) \ - PER_LUA_BINDING(MovableMan) \ - PER_LUA_BINDING(PerformanceMan) \ - PER_LUA_BINDING(PostProcessMan) \ - PER_LUA_BINDING(PresetMan) \ - PER_LUA_BINDING(PrimitiveMan) \ - PER_LUA_BINDING(SceneMan) \ - PER_LUA_BINDING(SettingsMan) \ - PER_LUA_BINDING(TimerMan) \ - PER_LUA_BINDING(UInputMan) \ - /* EntityLuaBindings */ \ - /* ThrownDevice-Derived */ \ - PER_LUA_BINDING(TDExplosive) \ - /* HeldDevice-Derived */ \ - PER_LUA_BINDING(HDFirearm) \ - PER_LUA_BINDING(ThrownDevice) \ - /* AEmitter-Derived */ \ - PER_LUA_BINDING(AEJetpack) \ - /* ACraft-Derived */ \ - PER_LUA_BINDING(ACDropShip) \ - PER_LUA_BINDING(ACRocket) \ - /* Attachable-Derived */ \ - PER_LUA_BINDING(Arm) \ - PER_LUA_BINDING(Leg) \ - PER_LUA_BINDING(AEmitter) \ - PER_LUA_BINDING(HeldDevice) \ - PER_LUA_BINDING(Magazine) \ - PER_LUA_BINDING(Turret) \ - /* Actor-Derived */ \ - PER_LUA_BINDING(ACrab) \ - PER_LUA_BINDING(ACraft) \ - PER_LUA_BINDING(AHuman) \ - PER_LUA_BINDING(ADoor) \ - /* MOSRotating-Derived */ \ - PER_LUA_BINDING(Actor) \ - PER_LUA_BINDING(Attachable) \ - /* MOSParticle-Derived */ \ - PER_LUA_BINDING(PEmitter) \ - /* MOSprite-Derived */ \ - PER_LUA_BINDING(MOSRotating) \ - PER_LUA_BINDING(MOSParticle) \ - /* MovableObject-Derived */ \ - PER_LUA_BINDING(MOPixel) \ - PER_LUA_BINDING(MOSprite) \ - /* SceneObject-Derived */ \ - PER_LUA_BINDING(TerrainObject) \ - PER_LUA_BINDING(Deployment) \ - PER_LUA_BINDING(MovableObject) \ - /* Entity-Derived */ \ - PER_LUA_BINDING(SoundContainer) \ - PER_LUA_BINDING(PieSlice) \ - PER_LUA_BINDING(GlobalScript) \ - PER_LUA_BINDING(Emission) \ - PER_LUA_BINDING(LimbPath) \ - PER_LUA_BINDING(PieMenu) \ - PER_LUA_BINDING(Round) \ - PER_LUA_BINDING(Scene) \ - PER_LUA_BINDING(Scene::Area) \ - PER_LUA_BINDING(Material) \ - PER_LUA_BINDING(MetaPlayer) \ - PER_LUA_BINDING(SceneObject) \ - /* SceneLayer-Derived */ \ - PER_LUA_BINDING(SLBackground) \ - /* Base Classes */ \ - PER_LUA_BINDING(SoundSet) \ - PER_LUA_BINDING(Gib) \ - PER_LUA_BINDING(SceneLayer) \ - PER_LUA_BINDING(Entity) \ - /* ActivityLuaBindings */ \ - PER_LUA_BINDING(GameActivity) \ - PER_LUA_BINDING(Activity) \ - /* GUILuaBindings */ \ - PER_LUA_BINDING(BuyMenuGUI) \ - PER_LUA_BINDING(SceneEditorGUI) \ - PER_LUA_BINDING(GUIBanner) \ - /* PrimitiveLuaBindings */ \ - /* GraphicalPrimitive-Derived */ \ - PER_LUA_BINDING(LinePrimitive) \ - PER_LUA_BINDING(ArcPrimitive) \ - PER_LUA_BINDING(SplinePrimitive) \ - PER_LUA_BINDING(BoxPrimitive) \ - PER_LUA_BINDING(BoxFillPrimitive) \ - PER_LUA_BINDING(RoundedBoxPrimitive) \ - PER_LUA_BINDING(RoundedBoxFillPrimitive) \ - PER_LUA_BINDING(CirclePrimitive) \ - PER_LUA_BINDING(CircleFillPrimitive) \ - PER_LUA_BINDING(EllipsePrimitive) \ - PER_LUA_BINDING(EllipseFillPrimitive) \ - PER_LUA_BINDING(TrianglePrimitive) \ - PER_LUA_BINDING(TriangleFillPrimitive) \ - PER_LUA_BINDING(TextPrimitive) \ - PER_LUA_BINDING(BitmapPrimitive) \ - /* Base Classes */ \ - PER_LUA_BINDING(GraphicalPrimitive) \ - /* InputLuaBindings */ \ - PER_LUA_BINDING(InputDevice) \ - PER_LUA_BINDING(InputElements) \ - PER_LUA_BINDING(MouseButtons) \ - PER_LUA_BINDING(JoyButtons) \ - PER_LUA_BINDING(JoyDirections) \ - PER_LUA_BINDING(SDL_Scancode) \ - PER_LUA_BINDING(SDL_Keycode) \ - PER_LUA_BINDING(SDL_GameControllerButton) \ - PER_LUA_BINDING(SDL_GameControllerAxis) \ - /* MiscLuaBindings */ \ - PER_LUA_BINDING(AlarmEvent) \ - PER_LUA_BINDING(Directions) \ - PER_LUA_BINDING(DrawBlendMode) - +#define LIST_OF_LUABOUND_OBJECTS \ + /* SystemLuaBindings */ \ + PER_LUA_BINDING(Box) \ + PER_LUA_BINDING(Controller) \ + PER_LUA_BINDING(DataModule) \ + PER_LUA_BINDING(Timer) \ + PER_LUA_BINDING(Vector) \ + PER_LUA_BINDING(PathRequest) \ + /* ManagerLuaBindings */ \ + PER_LUA_BINDING(ActivityMan) \ + PER_LUA_BINDING(AudioMan) \ + PER_LUA_BINDING(CameraMan) \ + PER_LUA_BINDING(ConsoleMan) \ + PER_LUA_BINDING(FrameMan) \ + PER_LUA_BINDING(MetaMan) \ + PER_LUA_BINDING(MovableMan) \ + PER_LUA_BINDING(PerformanceMan) \ + PER_LUA_BINDING(PostProcessMan) \ + PER_LUA_BINDING(PresetMan) \ + PER_LUA_BINDING(PrimitiveMan) \ + PER_LUA_BINDING(SceneMan) \ + PER_LUA_BINDING(SettingsMan) \ + PER_LUA_BINDING(TimerMan) \ + PER_LUA_BINDING(UInputMan) \ + /* EntityLuaBindings */ \ + /* ThrownDevice-Derived */ \ + PER_LUA_BINDING(TDExplosive) \ + /* HeldDevice-Derived */ \ + PER_LUA_BINDING(HDFirearm) \ + PER_LUA_BINDING(ThrownDevice) \ + /* AEmitter-Derived */ \ + PER_LUA_BINDING(AEJetpack) \ + /* ACraft-Derived */ \ + PER_LUA_BINDING(ACDropShip) \ + PER_LUA_BINDING(ACRocket) \ + /* Attachable-Derived */ \ + PER_LUA_BINDING(Arm) \ + PER_LUA_BINDING(Leg) \ + PER_LUA_BINDING(AEmitter) \ + PER_LUA_BINDING(HeldDevice) \ + PER_LUA_BINDING(Magazine) \ + PER_LUA_BINDING(Turret) \ + /* Actor-Derived */ \ + PER_LUA_BINDING(ACrab) \ + PER_LUA_BINDING(ACraft) \ + PER_LUA_BINDING(AHuman) \ + PER_LUA_BINDING(ADoor) \ + /* MOSRotating-Derived */ \ + PER_LUA_BINDING(Actor) \ + PER_LUA_BINDING(Attachable) \ + /* MOSParticle-Derived */ \ + PER_LUA_BINDING(PEmitter) \ + /* MOSprite-Derived */ \ + PER_LUA_BINDING(MOSRotating) \ + PER_LUA_BINDING(MOSParticle) \ + /* MovableObject-Derived */ \ + PER_LUA_BINDING(MOPixel) \ + PER_LUA_BINDING(MOSprite) \ + /* SceneObject-Derived */ \ + PER_LUA_BINDING(TerrainObject) \ + PER_LUA_BINDING(Deployment) \ + PER_LUA_BINDING(MovableObject) \ + /* Entity-Derived */ \ + PER_LUA_BINDING(SoundContainer) \ + PER_LUA_BINDING(PieSlice) \ + PER_LUA_BINDING(GlobalScript) \ + PER_LUA_BINDING(Emission) \ + PER_LUA_BINDING(LimbPath) \ + PER_LUA_BINDING(PieMenu) \ + PER_LUA_BINDING(Round) \ + PER_LUA_BINDING(Scene) \ + PER_LUA_BINDING(Scene::Area) \ + PER_LUA_BINDING(Material) \ + PER_LUA_BINDING(MetaPlayer) \ + PER_LUA_BINDING(SceneObject) \ + /* SceneLayer-Derived */ \ + PER_LUA_BINDING(SLBackground) \ + /* Base Classes */ \ + PER_LUA_BINDING(SoundSet) \ + PER_LUA_BINDING(Gib) \ + PER_LUA_BINDING(SceneLayer) \ + PER_LUA_BINDING(Entity) \ + /* ActivityLuaBindings */ \ + PER_LUA_BINDING(GameActivity) \ + PER_LUA_BINDING(Activity) \ + /* GUILuaBindings */ \ + PER_LUA_BINDING(BuyMenuGUI) \ + PER_LUA_BINDING(SceneEditorGUI) \ + PER_LUA_BINDING(GUIBanner) \ + /* PrimitiveLuaBindings */ \ + /* GraphicalPrimitive-Derived */ \ + PER_LUA_BINDING(LinePrimitive) \ + PER_LUA_BINDING(ArcPrimitive) \ + PER_LUA_BINDING(SplinePrimitive) \ + PER_LUA_BINDING(BoxPrimitive) \ + PER_LUA_BINDING(BoxFillPrimitive) \ + PER_LUA_BINDING(RoundedBoxPrimitive) \ + PER_LUA_BINDING(RoundedBoxFillPrimitive) \ + PER_LUA_BINDING(CirclePrimitive) \ + PER_LUA_BINDING(CircleFillPrimitive) \ + PER_LUA_BINDING(EllipsePrimitive) \ + PER_LUA_BINDING(EllipseFillPrimitive) \ + PER_LUA_BINDING(TrianglePrimitive) \ + PER_LUA_BINDING(TriangleFillPrimitive) \ + PER_LUA_BINDING(TextPrimitive) \ + PER_LUA_BINDING(BitmapPrimitive) \ + /* Base Classes */ \ + PER_LUA_BINDING(GraphicalPrimitive) \ + /* InputLuaBindings */ \ + PER_LUA_BINDING(InputDevice) \ + PER_LUA_BINDING(InputElements) \ + PER_LUA_BINDING(MouseButtons) \ + PER_LUA_BINDING(JoyButtons) \ + PER_LUA_BINDING(JoyDirections) \ + PER_LUA_BINDING(SDL_Scancode) \ + PER_LUA_BINDING(SDL_Keycode) \ + PER_LUA_BINDING(SDL_GameControllerButton) \ + PER_LUA_BINDING(SDL_GameControllerAxis) \ + /* MiscLuaBindings */ \ + PER_LUA_BINDING(AlarmEvent) \ + PER_LUA_BINDING(Directions) \ + PER_LUA_BINDING(DrawBlendMode) + #pragma region Lua Binding Registration Macros - /// - /// Convenience macro for declaring a binding register function. - /// - #define LuaBindingRegisterFunctionDeclarationForType(TYPE) \ - static luabind::scope Register##TYPE##LuaBindings() +/// +/// Convenience macro for declaring a binding register function. +/// +#define LuaBindingRegisterFunctionDeclarationForType(TYPE) \ + static luabind::scope Register##TYPE##LuaBindings() - /// - /// Convenience macro for defining a binding register function. - /// - #define LuaBindingRegisterFunctionDefinitionForType(OWNINGSCOPE, TYPE) \ - luabind::scope OWNINGSCOPE::Register##TYPE##LuaBindings() +/// +/// Convenience macro for defining a binding register function. +/// +#define LuaBindingRegisterFunctionDefinitionForType(OWNINGSCOPE, TYPE) \ + luabind::scope OWNINGSCOPE::Register##TYPE##LuaBindings() - /// - /// Convenience macro for a LuaBind scope definition of an abstract type. - /// - #define AbstractTypeLuaClassDefinition(TYPE, PARENTTYPE) \ - luabind::class_(#TYPE) \ - .property("ClassName", &TYPE::GetClassName) +/// +/// Convenience macro for a LuaBind scope definition of an abstract type. +/// +#define AbstractTypeLuaClassDefinition(TYPE, PARENTTYPE) \ + luabind::class_(#TYPE) \ + .property("ClassName", &TYPE::GetClassName) - /// - /// Convenience macro for a LuaBind scope definition of a concrete type. - /// - #define ConcreteTypeLuaClassDefinition(TYPE, PARENTTYPE) \ - luabind::class_(#TYPE) \ - .def("Clone", &LuaAdaptersEntityClone::Clone##TYPE, luabind::adopt(luabind::result)) \ - .property("ClassName", &TYPE::GetClassName) +/// +/// Convenience macro for a LuaBind scope definition of a concrete type. +/// +#define ConcreteTypeLuaClassDefinition(TYPE, PARENTTYPE) \ + luabind::class_(#TYPE) \ + .def("Clone", &LuaAdaptersEntityClone::Clone##TYPE, luabind::adopt(luabind::result)) \ + .property("ClassName", &TYPE::GetClassName) - /// - /// Convenience macro for calling a register function of a type. - /// - #define RegisterLuaBindingsOfType(OWNINGSCOPE, TYPE) \ - OWNINGSCOPE::Register##TYPE##LuaBindings() +/// +/// Convenience macro for calling a register function of a type. +/// +#define RegisterLuaBindingsOfType(OWNINGSCOPE, TYPE) \ + OWNINGSCOPE::Register##TYPE##LuaBindings() - /// - /// Convenience macro for calling a register function of an abstract type, along with registering global bindings for adapters relevant to the type. - /// - #define RegisterLuaBindingsOfAbstractType(OWNINGSCOPE, TYPE) \ - luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (TYPE *(*)(Entity *))&LuaAdaptersEntityCast::To##TYPE), \ - luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (const TYPE *(*)(const Entity *))&LuaAdaptersEntityCast::ToConst##TYPE), \ - luabind::def((std::string("Is") + std::string(#TYPE)).c_str(), (bool(*)(const Entity *))&LuaAdaptersEntityCast::Is##TYPE), \ - OWNINGSCOPE::Register##TYPE##LuaBindings() +/// +/// Convenience macro for calling a register function of an abstract type, along with registering global bindings for adapters relevant to the type. +/// +#define RegisterLuaBindingsOfAbstractType(OWNINGSCOPE, TYPE) \ + luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (TYPE * (*)(Entity*)) & LuaAdaptersEntityCast::To##TYPE), \ + luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (const TYPE* (*)(const Entity*)) & LuaAdaptersEntityCast::ToConst##TYPE), \ + luabind::def((std::string("Is") + std::string(#TYPE)).c_str(), (bool (*)(const Entity*)) & LuaAdaptersEntityCast::Is##TYPE), \ + OWNINGSCOPE::Register##TYPE##LuaBindings() - /// - /// Convenience macro for calling a register function of a concrete type, along with registering global bindings for adapters relevant to the type. - /// - #define RegisterLuaBindingsOfConcreteType(OWNINGSCOPE, TYPE) \ - luabind::def((std::string("Create") + std::string(#TYPE)).c_str(), (TYPE *(*)(std::string, std::string))&LuaAdaptersEntityCreate::Create##TYPE, luabind::adopt(luabind::result)), \ - luabind::def((std::string("Create") + std::string(#TYPE)).c_str(), (TYPE *(*)(std::string))&LuaAdaptersEntityCreate::Create##TYPE, luabind::adopt(luabind::result)), \ - luabind::def((std::string("Random") + std::string(#TYPE)).c_str(), (TYPE *(*)(std::string, int))&LuaAdaptersEntityCreate::Random##TYPE, luabind::adopt(luabind::result)), \ - luabind::def((std::string("Random") + std::string(#TYPE)).c_str(), (TYPE *(*)(std::string, std::string))&LuaAdaptersEntityCreate::Random##TYPE, luabind::adopt(luabind::result)), \ - luabind::def((std::string("Random") + std::string(#TYPE)).c_str(), (TYPE *(*)(std::string))&LuaAdaptersEntityCreate::Random##TYPE, luabind::adopt(luabind::result)), \ - luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (TYPE *(*)(Entity *))&LuaAdaptersEntityCast::To##TYPE), \ - luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (const TYPE *(*)(const Entity *))&LuaAdaptersEntityCast::ToConst##TYPE), \ - luabind::def((std::string("Is") + std::string(#TYPE)).c_str(), (bool(*)(const Entity *))&LuaAdaptersEntityCast::Is##TYPE), \ - OWNINGSCOPE::Register##TYPE##LuaBindings() +/// +/// Convenience macro for calling a register function of a concrete type, along with registering global bindings for adapters relevant to the type. +/// +#define RegisterLuaBindingsOfConcreteType(OWNINGSCOPE, TYPE) \ + luabind::def((std::string("Create") + std::string(#TYPE)).c_str(), (TYPE * (*)(std::string, std::string)) & LuaAdaptersEntityCreate::Create##TYPE, luabind::adopt(luabind::result)), \ + luabind::def((std::string("Create") + std::string(#TYPE)).c_str(), (TYPE * (*)(std::string)) & LuaAdaptersEntityCreate::Create##TYPE, luabind::adopt(luabind::result)), \ + luabind::def((std::string("Random") + std::string(#TYPE)).c_str(), (TYPE * (*)(std::string, int)) & LuaAdaptersEntityCreate::Random##TYPE, luabind::adopt(luabind::result)), \ + luabind::def((std::string("Random") + std::string(#TYPE)).c_str(), (TYPE * (*)(std::string, std::string)) & LuaAdaptersEntityCreate::Random##TYPE, luabind::adopt(luabind::result)), \ + luabind::def((std::string("Random") + std::string(#TYPE)).c_str(), (TYPE * (*)(std::string)) & LuaAdaptersEntityCreate::Random##TYPE, luabind::adopt(luabind::result)), \ + luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (TYPE * (*)(Entity*)) & LuaAdaptersEntityCast::To##TYPE), \ + luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (const TYPE* (*)(const Entity*)) & LuaAdaptersEntityCast::ToConst##TYPE), \ + luabind::def((std::string("Is") + std::string(#TYPE)).c_str(), (bool (*)(const Entity*)) & LuaAdaptersEntityCast::Is##TYPE), \ + OWNINGSCOPE::Register##TYPE##LuaBindings() #pragma endregion /// @@ -332,5 +332,5 @@ namespace RTE { LuaBindingRegisterFunctionDeclarationForType(Directions); LuaBindingRegisterFunctionDeclarationForType(DrawBlendMode); }; -} +} // namespace RTE #endif diff --git a/Source/Lua/LuaBindingsActivities.cpp b/Source/Lua/LuaBindingsActivities.cpp index aef4b0fd98..c86d17e86f 100644 --- a/Source/Lua/LuaBindingsActivities.cpp +++ b/Source/Lua/LuaBindingsActivities.cpp @@ -4,191 +4,177 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ActivityLuaBindings, Activity) { return AbstractTypeLuaClassDefinition(Activity, Entity) - .def(luabind::constructor<>()) + .def(luabind::constructor<>()) - .property("Description", &Activity::GetDescription) - .property("InCampaignStage", &Activity::GetInCampaignStage, &Activity::SetInCampaignStage) - .property("ActivityState", &Activity::GetActivityState, &Activity::SetActivityState) - .property("AllowsUserSaving", &Activity::GetAllowsUserSaving, &Activity::SetAllowsUserSaving) - .property("SceneName", &Activity::GetSceneName, &Activity::SetSceneName) - .property("PlayerCount", &Activity::GetPlayerCount) - .property("HumanCount", &Activity::GetHumanCount) - .property("TeamCount", &Activity::GetTeamCount) - .property("Difficulty", &Activity::GetDifficulty, &Activity::SetDifficulty) + .property("Description", &Activity::GetDescription) + .property("InCampaignStage", &Activity::GetInCampaignStage, &Activity::SetInCampaignStage) + .property("ActivityState", &Activity::GetActivityState, &Activity::SetActivityState) + .property("AllowsUserSaving", &Activity::GetAllowsUserSaving, &Activity::SetAllowsUserSaving) + .property("SceneName", &Activity::GetSceneName, &Activity::SetSceneName) + .property("PlayerCount", &Activity::GetPlayerCount) + .property("HumanCount", &Activity::GetHumanCount) + .property("TeamCount", &Activity::GetTeamCount) + .property("Difficulty", &Activity::GetDifficulty, &Activity::SetDifficulty) - .def("DeactivatePlayer", &Activity::DeactivatePlayer) - .def("PlayerActive", &Activity::PlayerActive) - .def("PlayerHuman", &Activity::PlayerHuman) - .def("TeamActive", &Activity::TeamActive) - .def("GetTeamOfPlayer", &Activity::GetTeamOfPlayer) - .def("SetTeamOfPlayer", &Activity::SetTeamOfPlayer) - .def("PlayersInTeamCount", &Activity::PlayersInTeamCount) - .def("ScreenOfPlayer", &Activity::ScreenOfPlayer) - .def("GetViewState", &Activity::GetViewState) - .def("SetViewState", &Activity::SetViewState) - .def("GetPlayerBrain", &Activity::GetPlayerBrain) - .def("SetPlayerBrain", &Activity::SetPlayerBrain) - .def("PlayerHadBrain", &Activity::PlayerHadBrain) - .def("SetPlayerHadBrain", &Activity::SetPlayerHadBrain) - .def("SetBrainEvacuated", &Activity::SetBrainEvacuated) - .def("BrainWasEvacuated", &Activity::BrainWasEvacuated) - .def("IsAssignedBrain", &Activity::IsAssignedBrain) - .def("IsBrainOfWhichPlayer", &Activity::IsBrainOfWhichPlayer) - .def("IsOtherPlayerBrain", &Activity::IsOtherPlayerBrain) - .def("HumanBrainCount", &Activity::HumanBrainCount) - .def("AIBrainCount", &Activity::AIBrainCount) - .def("GetControlledActor", &Activity::GetControlledActor) - .def("GetPlayerController", &Activity::GetPlayerController) - .def("SetTeamFunds", &Activity::SetTeamFunds) - .def("GetTeamFunds", &Activity::GetTeamFunds) - .def("SetTeamAISkill", &Activity::SetTeamAISkill) - .def("GetTeamAISkill", &Activity::GetTeamAISkill) - .def("ChangeTeamFunds", &Activity::ChangeTeamFunds) - .def("TeamFundsChanged", &Activity::TeamFundsChanged) - .def("ReportDeath", &Activity::ReportDeath) - .def("GetTeamDeathCount", &Activity::GetTeamDeathCount) - .def("IsRunning", &Activity::IsRunning) - .def("IsPaused", &Activity::IsPaused) - .def("IsOver", &Activity::IsOver) - .def("SwitchToActor", &Activity::SwitchToActor) - .def("SwitchToNextActor", &Activity::SwitchToNextActor) - .def("SwitchToPrevActor", &Activity::SwitchToPrevActor) - .def("IsHumanTeam", &Activity::IsHumanTeam) - .def("ResetMessageTimer", &Activity::ResetMessageTimer) - .def("SaveString", &Activity::SaveString) - .def("LoadString", &Activity::LoadString) - .def("SaveNumber", &Activity::SaveNumber) - .def("LoadNumber", &Activity::LoadNumber) - .def("SendMessage", &LuaAdaptersActivity::SendMessage1) - .def("SendMessage", &LuaAdaptersActivity::SendMessage2) + .def("DeactivatePlayer", &Activity::DeactivatePlayer) + .def("PlayerActive", &Activity::PlayerActive) + .def("PlayerHuman", &Activity::PlayerHuman) + .def("TeamActive", &Activity::TeamActive) + .def("GetTeamOfPlayer", &Activity::GetTeamOfPlayer) + .def("SetTeamOfPlayer", &Activity::SetTeamOfPlayer) + .def("PlayersInTeamCount", &Activity::PlayersInTeamCount) + .def("ScreenOfPlayer", &Activity::ScreenOfPlayer) + .def("GetViewState", &Activity::GetViewState) + .def("SetViewState", &Activity::SetViewState) + .def("GetPlayerBrain", &Activity::GetPlayerBrain) + .def("SetPlayerBrain", &Activity::SetPlayerBrain) + .def("PlayerHadBrain", &Activity::PlayerHadBrain) + .def("SetPlayerHadBrain", &Activity::SetPlayerHadBrain) + .def("SetBrainEvacuated", &Activity::SetBrainEvacuated) + .def("BrainWasEvacuated", &Activity::BrainWasEvacuated) + .def("IsAssignedBrain", &Activity::IsAssignedBrain) + .def("IsBrainOfWhichPlayer", &Activity::IsBrainOfWhichPlayer) + .def("IsOtherPlayerBrain", &Activity::IsOtherPlayerBrain) + .def("HumanBrainCount", &Activity::HumanBrainCount) + .def("AIBrainCount", &Activity::AIBrainCount) + .def("GetControlledActor", &Activity::GetControlledActor) + .def("GetPlayerController", &Activity::GetPlayerController) + .def("SetTeamFunds", &Activity::SetTeamFunds) + .def("GetTeamFunds", &Activity::GetTeamFunds) + .def("SetTeamAISkill", &Activity::SetTeamAISkill) + .def("GetTeamAISkill", &Activity::GetTeamAISkill) + .def("ChangeTeamFunds", &Activity::ChangeTeamFunds) + .def("TeamFundsChanged", &Activity::TeamFundsChanged) + .def("ReportDeath", &Activity::ReportDeath) + .def("GetTeamDeathCount", &Activity::GetTeamDeathCount) + .def("IsRunning", &Activity::IsRunning) + .def("IsPaused", &Activity::IsPaused) + .def("IsOver", &Activity::IsOver) + .def("SwitchToActor", &Activity::SwitchToActor) + .def("SwitchToNextActor", &Activity::SwitchToNextActor) + .def("SwitchToPrevActor", &Activity::SwitchToPrevActor) + .def("IsHumanTeam", &Activity::IsHumanTeam) + .def("ResetMessageTimer", &Activity::ResetMessageTimer) + .def("SaveString", &Activity::SaveString) + .def("LoadString", &Activity::LoadString) + .def("SaveNumber", &Activity::SaveNumber) + .def("LoadNumber", &Activity::LoadNumber) + .def("SendMessage", &LuaAdaptersActivity::SendMessage1) + .def("SendMessage", &LuaAdaptersActivity::SendMessage2) - .enum_("Players")[ - luabind::value("PLAYER_NONE", Players::NoPlayer), - luabind::value("PLAYER_1", Players::PlayerOne), - luabind::value("PLAYER_2", Players::PlayerTwo), - luabind::value("PLAYER_3", Players::PlayerThree), - luabind::value("PLAYER_4", Players::PlayerFour), - luabind::value("MAXPLAYERCOUNT", Players::MaxPlayerCount) - ] - .enum_("ActivityState")[ - luabind::value("NOACTIVITY", Activity::ActivityState::NoActivity), - luabind::value("NOTSTARTED", Activity::ActivityState::NotStarted), - luabind::value("STARTING", Activity::ActivityState::Starting), - luabind::value("EDITING", Activity::ActivityState::Editing), - luabind::value("PREGAME", Activity::ActivityState::PreGame), - luabind::value("RUNNING", Activity::ActivityState::Running), - luabind::value("INERROR", Activity::ActivityState::HasError), - luabind::value("OVER", Activity::ActivityState::Over) - ] - .enum_("Team")[ - luabind::value("NOTEAM", Activity::Teams::NoTeam), - luabind::value("TEAM_1", Activity::Teams::TeamOne), - luabind::value("TEAM_2", Activity::Teams::TeamTwo), - luabind::value("TEAM_3", Activity::Teams::TeamThree), - luabind::value("TEAM_4", Activity::Teams::TeamFour), - luabind::value("MAXTEAMCOUNT", Activity::Teams::MaxTeamCount) - ] - .enum_("ViewState")[ - luabind::value("NORMAL", Activity::ViewState::Normal), - luabind::value("OBSERVE", Activity::ViewState::Observe), - luabind::value("DEATHWATCH", Activity::ViewState::DeathWatch), - luabind::value("ACTORSELECT", Activity::ViewState::ActorSelect), - luabind::value("AISENTRYPOINT", Activity::ViewState::AISentryPoint), - luabind::value("AIPATROLPOINTS", Activity::ViewState::AIPatrolPoints), - luabind::value("AIGOLDDIGPOINT", Activity::ViewState::AIGoldDigPoint), - luabind::value("AIGOTOPOINT", Activity::ViewState::AIGoToPoint), - luabind::value("LZSELECT", Activity::ViewState::LandingZoneSelect) - ] - .enum_("DifficultySetting")[ - luabind::value("MINDIFFICULTY", Activity::DifficultySetting::MinDifficulty), - luabind::value("CAKEDIFFICULTY", Activity::DifficultySetting::CakeDifficulty), - luabind::value("EASYDIFFICULTY", Activity::DifficultySetting::EasyDifficulty), - luabind::value("MEDIUMDIFFICULTY", Activity::DifficultySetting::MediumDifficulty), - luabind::value("HARDDIFFICULTY", Activity::DifficultySetting::HardDifficulty), - luabind::value("NUTSDIFFICULTY", Activity::DifficultySetting::NutsDifficulty), - luabind::value("MAXDIFFICULTY", Activity::DifficultySetting::MaxDifficulty) - ] - .enum_("AISkillSetting")[ - luabind::value("MINSKILL", Activity::AISkillSetting::MinSkill), - luabind::value("INFERIORSKILL", Activity::AISkillSetting::InferiorSkill), - luabind::value("DEFAULTSKILL", Activity::AISkillSetting::DefaultSkill), - luabind::value("AVERAGESKILL", Activity::AISkillSetting::AverageSkill), - luabind::value("GOODSKILL", Activity::AISkillSetting::GoodSkill), - luabind::value("UNFAIRSKILL", Activity::AISkillSetting::UnfairSkill) - ]; + .enum_("Players")[luabind::value("PLAYER_NONE", Players::NoPlayer), + luabind::value("PLAYER_1", Players::PlayerOne), + luabind::value("PLAYER_2", Players::PlayerTwo), + luabind::value("PLAYER_3", Players::PlayerThree), + luabind::value("PLAYER_4", Players::PlayerFour), + luabind::value("MAXPLAYERCOUNT", Players::MaxPlayerCount)] + .enum_("ActivityState")[luabind::value("NOACTIVITY", Activity::ActivityState::NoActivity), + luabind::value("NOTSTARTED", Activity::ActivityState::NotStarted), + luabind::value("STARTING", Activity::ActivityState::Starting), + luabind::value("EDITING", Activity::ActivityState::Editing), + luabind::value("PREGAME", Activity::ActivityState::PreGame), + luabind::value("RUNNING", Activity::ActivityState::Running), + luabind::value("INERROR", Activity::ActivityState::HasError), + luabind::value("OVER", Activity::ActivityState::Over)] + .enum_("Team")[luabind::value("NOTEAM", Activity::Teams::NoTeam), + luabind::value("TEAM_1", Activity::Teams::TeamOne), + luabind::value("TEAM_2", Activity::Teams::TeamTwo), + luabind::value("TEAM_3", Activity::Teams::TeamThree), + luabind::value("TEAM_4", Activity::Teams::TeamFour), + luabind::value("MAXTEAMCOUNT", Activity::Teams::MaxTeamCount)] + .enum_("ViewState")[luabind::value("NORMAL", Activity::ViewState::Normal), + luabind::value("OBSERVE", Activity::ViewState::Observe), + luabind::value("DEATHWATCH", Activity::ViewState::DeathWatch), + luabind::value("ACTORSELECT", Activity::ViewState::ActorSelect), + luabind::value("AISENTRYPOINT", Activity::ViewState::AISentryPoint), + luabind::value("AIPATROLPOINTS", Activity::ViewState::AIPatrolPoints), + luabind::value("AIGOLDDIGPOINT", Activity::ViewState::AIGoldDigPoint), + luabind::value("AIGOTOPOINT", Activity::ViewState::AIGoToPoint), + luabind::value("LZSELECT", Activity::ViewState::LandingZoneSelect)] + .enum_("DifficultySetting")[luabind::value("MINDIFFICULTY", Activity::DifficultySetting::MinDifficulty), + luabind::value("CAKEDIFFICULTY", Activity::DifficultySetting::CakeDifficulty), + luabind::value("EASYDIFFICULTY", Activity::DifficultySetting::EasyDifficulty), + luabind::value("MEDIUMDIFFICULTY", Activity::DifficultySetting::MediumDifficulty), + luabind::value("HARDDIFFICULTY", Activity::DifficultySetting::HardDifficulty), + luabind::value("NUTSDIFFICULTY", Activity::DifficultySetting::NutsDifficulty), + luabind::value("MAXDIFFICULTY", Activity::DifficultySetting::MaxDifficulty)] + .enum_("AISkillSetting")[luabind::value("MINSKILL", Activity::AISkillSetting::MinSkill), + luabind::value("INFERIORSKILL", Activity::AISkillSetting::InferiorSkill), + luabind::value("DEFAULTSKILL", Activity::AISkillSetting::DefaultSkill), + luabind::value("AVERAGESKILL", Activity::AISkillSetting::AverageSkill), + luabind::value("GOODSKILL", Activity::AISkillSetting::GoodSkill), + luabind::value("UNFAIRSKILL", Activity::AISkillSetting::UnfairSkill)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ActivityLuaBindings, GameActivity) { return luabind::class_("GameActivity") - .def(luabind::constructor<>()) + .def(luabind::constructor<>()) - .property("WinnerTeam", &GameActivity::GetWinnerTeam, &GameActivity::SetWinnerTeam) - .property("CPUTeam", &GameActivity::GetCPUTeam, &GameActivity::SetCPUTeam) - .property("DeliveryDelay", &GameActivity::GetDeliveryDelay, &GameActivity::SetDeliveryDelay) - .property("BuyMenuEnabled", &GameActivity::GetBuyMenuEnabled, &GameActivity::SetBuyMenuEnabled) - .property("CraftsOrbitAtTheEdge", &GameActivity::GetCraftOrbitAtTheEdge, &GameActivity::SetCraftOrbitAtTheEdge) + .property("WinnerTeam", &GameActivity::GetWinnerTeam, &GameActivity::SetWinnerTeam) + .property("CPUTeam", &GameActivity::GetCPUTeam, &GameActivity::SetCPUTeam) + .property("DeliveryDelay", &GameActivity::GetDeliveryDelay, &GameActivity::SetDeliveryDelay) + .property("BuyMenuEnabled", &GameActivity::GetBuyMenuEnabled, &GameActivity::SetBuyMenuEnabled) + .property("CraftsOrbitAtTheEdge", &GameActivity::GetCraftOrbitAtTheEdge, &GameActivity::SetCraftOrbitAtTheEdge) - //.def_readwrite("ActorCursor", &GameActivity::m_ActorCursor) - .def_readwrite("CursorTimer", &GameActivity::m_CursorTimer) - .def_readwrite("GameTimer", &GameActivity::m_GameTimer) - .def_readwrite("GameOverTimer", &GameActivity::m_GameOverTimer) - .def_readwrite("GameOverPeriod", &GameActivity::m_GameOverPeriod) + //.def_readwrite("ActorCursor", &GameActivity::m_ActorCursor) + .def_readwrite("CursorTimer", &GameActivity::m_CursorTimer) + .def_readwrite("GameTimer", &GameActivity::m_GameTimer) + .def_readwrite("GameOverTimer", &GameActivity::m_GameOverTimer) + .def_readwrite("GameOverPeriod", &GameActivity::m_GameOverPeriod) - .def("SetObservationTarget", &GameActivity::SetObservationTarget) - .def("SetDeathViewTarget", &GameActivity::SetDeathViewTarget) - .def("SetLandingZone", &GameActivity::SetLandingZone) - .def("GetLandingZone", &GameActivity::GetLandingZone) - .def("SetActorSelectCursor", &GameActivity::SetActorSelectCursor) - .def("GetBuyGUI", &GameActivity::GetBuyGUI) - .def("GetEditorGUI", &GameActivity::GetEditorGUI) - .def("LockControlledActor", &GameActivity::LockControlledActor) - .def("OtherTeam", &GameActivity::OtherTeam) - .def("OneOrNoneTeamsLeft", &GameActivity::OneOrNoneTeamsLeft) - .def("WhichTeamLeft", &GameActivity::WhichTeamLeft) - .def("NoTeamLeft", &GameActivity::NoTeamLeft) - .def("OnlyOneTeamLeft", &GameActivity::OneOrNoneTeamsLeft) // Backwards compat - .def("GetBanner", &GameActivity::GetBanner) - .def("SetLZArea", &GameActivity::SetLZArea) - .def("GetLZArea", &GameActivity::GetLZArea) - .def("SetBrainLZWidth", &GameActivity::SetBrainLZWidth) - .def("GetBrainLZWidth", &GameActivity::GetBrainLZWidth) - .def("GetActiveCPUTeamCount", &GameActivity::GetActiveCPUTeamCount) - .def("GetActiveHumanTeamCount", &GameActivity::GetActiveHumanTeamCount) - .def("AddObjectivePoint", &GameActivity::AddObjectivePoint) - .def("YSortObjectivePoints", &GameActivity::YSortObjectivePoints) - .def("ClearObjectivePoints", &GameActivity::ClearObjectivePoints) - .def("AddOverridePurchase", &GameActivity::AddOverridePurchase) - .def("SetOverridePurchaseList", (int (GameActivity::*)(const Loadout *, int))&GameActivity::SetOverridePurchaseList) - .def("SetOverridePurchaseList", (int (GameActivity::*)(std::string, int))&GameActivity::SetOverridePurchaseList) - .def("ClearOverridePurchase", &GameActivity::ClearOverridePurchase) - .def("CreateDelivery", (bool (GameActivity::*)(int))&GameActivity::CreateDelivery) - .def("CreateDelivery", (bool (GameActivity::*)(int, int))&GameActivity::CreateDelivery) - .def("CreateDelivery", (bool (GameActivity::*)(int, int, Vector&))&GameActivity::CreateDelivery) - .def("CreateDelivery", (bool (GameActivity::*)(int, int, Actor*))&GameActivity::CreateDelivery) - .def("GetDeliveryCount", &GameActivity::GetDeliveryCount) - .def("GetTeamTech", &GameActivity::GetTeamTech) - .def("SetTeamTech", &GameActivity::SetTeamTech) - .def("GetCrabToHumanSpawnRatio", &GameActivity::GetCrabToHumanSpawnRatio) - .def("TeamIsCPU", &GameActivity::TeamIsCPU) - .def("GetStartingGold", &GameActivity::GetStartingGold) - .def("GetFogOfWarEnabled", &GameActivity::GetFogOfWarEnabled) - .def("UpdateEditing", &GameActivity::UpdateEditing) - .def("DisableAIs", &GameActivity::DisableAIs) - .def("InitAIs", &GameActivity::InitAIs) + .def("SetObservationTarget", &GameActivity::SetObservationTarget) + .def("SetDeathViewTarget", &GameActivity::SetDeathViewTarget) + .def("SetLandingZone", &GameActivity::SetLandingZone) + .def("GetLandingZone", &GameActivity::GetLandingZone) + .def("SetActorSelectCursor", &GameActivity::SetActorSelectCursor) + .def("GetBuyGUI", &GameActivity::GetBuyGUI) + .def("GetEditorGUI", &GameActivity::GetEditorGUI) + .def("LockControlledActor", &GameActivity::LockControlledActor) + .def("OtherTeam", &GameActivity::OtherTeam) + .def("OneOrNoneTeamsLeft", &GameActivity::OneOrNoneTeamsLeft) + .def("WhichTeamLeft", &GameActivity::WhichTeamLeft) + .def("NoTeamLeft", &GameActivity::NoTeamLeft) + .def("OnlyOneTeamLeft", &GameActivity::OneOrNoneTeamsLeft) // Backwards compat + .def("GetBanner", &GameActivity::GetBanner) + .def("SetLZArea", &GameActivity::SetLZArea) + .def("GetLZArea", &GameActivity::GetLZArea) + .def("SetBrainLZWidth", &GameActivity::SetBrainLZWidth) + .def("GetBrainLZWidth", &GameActivity::GetBrainLZWidth) + .def("GetActiveCPUTeamCount", &GameActivity::GetActiveCPUTeamCount) + .def("GetActiveHumanTeamCount", &GameActivity::GetActiveHumanTeamCount) + .def("AddObjectivePoint", &GameActivity::AddObjectivePoint) + .def("YSortObjectivePoints", &GameActivity::YSortObjectivePoints) + .def("ClearObjectivePoints", &GameActivity::ClearObjectivePoints) + .def("AddOverridePurchase", &GameActivity::AddOverridePurchase) + .def("SetOverridePurchaseList", (int(GameActivity::*)(const Loadout*, int)) & GameActivity::SetOverridePurchaseList) + .def("SetOverridePurchaseList", (int(GameActivity::*)(std::string, int)) & GameActivity::SetOverridePurchaseList) + .def("ClearOverridePurchase", &GameActivity::ClearOverridePurchase) + .def("CreateDelivery", (bool(GameActivity::*)(int)) & GameActivity::CreateDelivery) + .def("CreateDelivery", (bool(GameActivity::*)(int, int)) & GameActivity::CreateDelivery) + .def("CreateDelivery", (bool(GameActivity::*)(int, int, Vector&)) & GameActivity::CreateDelivery) + .def("CreateDelivery", (bool(GameActivity::*)(int, int, Actor*)) & GameActivity::CreateDelivery) + .def("GetDeliveryCount", &GameActivity::GetDeliveryCount) + .def("GetTeamTech", &GameActivity::GetTeamTech) + .def("SetTeamTech", &GameActivity::SetTeamTech) + .def("GetCrabToHumanSpawnRatio", &GameActivity::GetCrabToHumanSpawnRatio) + .def("TeamIsCPU", &GameActivity::TeamIsCPU) + .def("GetStartingGold", &GameActivity::GetStartingGold) + .def("GetFogOfWarEnabled", &GameActivity::GetFogOfWarEnabled) + .def("UpdateEditing", &GameActivity::UpdateEditing) + .def("DisableAIs", &GameActivity::DisableAIs) + .def("InitAIs", &GameActivity::InitAIs) - .enum_("ObjectiveArrowDir")[ - luabind::value("ARROWDOWN", GameActivity::ObjectiveArrowDir::ARROWDOWN), - luabind::value("ARROWLEFT", GameActivity::ObjectiveArrowDir::ARROWLEFT), - luabind::value("ARROWRIGHT", GameActivity::ObjectiveArrowDir::ARROWRIGHT), - luabind::value("ARROWUP", GameActivity::ObjectiveArrowDir::ARROWUP) - ]; + .enum_("ObjectiveArrowDir")[luabind::value("ARROWDOWN", GameActivity::ObjectiveArrowDir::ARROWDOWN), + luabind::value("ARROWLEFT", GameActivity::ObjectiveArrowDir::ARROWLEFT), + luabind::value("ARROWRIGHT", GameActivity::ObjectiveArrowDir::ARROWRIGHT), + luabind::value("ARROWUP", GameActivity::ObjectiveArrowDir::ARROWUP)]; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Lua/LuaBindingsEntities.cpp b/Source/Lua/LuaBindingsEntities.cpp index 1c1a04d090..48f9dd7adf 100644 --- a/Source/Lua/LuaBindingsEntities.cpp +++ b/Source/Lua/LuaBindingsEntities.cpp @@ -4,1501 +4,1429 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Entity) { return luabind::class_("Entity") - .def(luabind::tostring(luabind::const_self)) - - .property("ClassName", &Entity::GetClassName) - .property("PresetName", &Entity::GetPresetName, &LuaAdaptersEntity::SetPresetName) - .property("Description", &Entity::GetDescription, &Entity::SetDescription) - .property("IsOriginalPreset", &Entity::IsOriginalPreset) - .property("ModuleID", &Entity::GetModuleID) - .property("ModuleName", &Entity::GetModuleName) - .property("RandomWeight", &Entity::GetRandomWeight) - .property("Groups", &Entity::GetGroups, luabind::return_stl_iterator) - - .def("Clone", &LuaAdaptersEntityClone::CloneEntity) - .def("Reset", &Entity::Reset) - .def("GetModuleAndPresetName", &Entity::GetModuleAndPresetName) - .def("AddToGroup", &Entity::AddToGroup) - .def("RemoveFromGroup", &Entity::RemoveFromGroup) - .def("IsInGroup", &Entity::IsInGroup); + .def(luabind::tostring(luabind::const_self)) + + .property("ClassName", &Entity::GetClassName) + .property("PresetName", &Entity::GetPresetName, &LuaAdaptersEntity::SetPresetName) + .property("Description", &Entity::GetDescription, &Entity::SetDescription) + .property("IsOriginalPreset", &Entity::IsOriginalPreset) + .property("ModuleID", &Entity::GetModuleID) + .property("ModuleName", &Entity::GetModuleName) + .property("RandomWeight", &Entity::GetRandomWeight) + .property("Groups", &Entity::GetGroups, luabind::return_stl_iterator) + + .def("Clone", &LuaAdaptersEntityClone::CloneEntity) + .def("Reset", &Entity::Reset) + .def("GetModuleAndPresetName", &Entity::GetModuleAndPresetName) + .def("AddToGroup", &Entity::AddToGroup) + .def("RemoveFromGroup", &Entity::RemoveFromGroup) + .def("IsInGroup", &Entity::IsInGroup); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ACDropShip) { return ConcreteTypeLuaClassDefinition(ACDropShip, ACraft) - .property("RightEngine", &ACDropShip::GetRightThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetRightThruster) - .property("LeftEngine", &ACDropShip::GetLeftThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetLeftThruster) - .property("RightThruster", &ACDropShip::GetURightThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetURightThruster) - .property("LeftThruster", &ACDropShip::GetULeftThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetULeftThruster) - .property("RightHatch", &ACDropShip::GetRightHatch, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetRightHatch) - .property("LeftHatch", &ACDropShip::GetLeftHatch, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetLeftHatch) - .property("MaxEngineAngle", &ACDropShip::GetMaxEngineAngle, &ACDropShip::SetMaxEngineAngle) - .property("LateralControlSpeed", &ACDropShip::GetLateralControlSpeed, &ACDropShip::SetLateralControlSpeed) - .property("LateralControl", &ACDropShip::GetLateralControl) - .property("HoverHeightModifier", &ACDropShip::GetHoverHeightModifier, &ACDropShip::SetHoverHeightModifier) - - .def("DetectObstacle", &ACDropShip::DetectObstacle) - .def("GetAltitude", &ACDropShip::GetAltitude); + .property("RightEngine", &ACDropShip::GetRightThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetRightThruster) + .property("LeftEngine", &ACDropShip::GetLeftThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetLeftThruster) + .property("RightThruster", &ACDropShip::GetURightThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetURightThruster) + .property("LeftThruster", &ACDropShip::GetULeftThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetULeftThruster) + .property("RightHatch", &ACDropShip::GetRightHatch, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetRightHatch) + .property("LeftHatch", &ACDropShip::GetLeftHatch, &LuaAdaptersPropertyOwnershipSafetyFaker::ACDropShipSetLeftHatch) + .property("MaxEngineAngle", &ACDropShip::GetMaxEngineAngle, &ACDropShip::SetMaxEngineAngle) + .property("LateralControlSpeed", &ACDropShip::GetLateralControlSpeed, &ACDropShip::SetLateralControlSpeed) + .property("LateralControl", &ACDropShip::GetLateralControl) + .property("HoverHeightModifier", &ACDropShip::GetHoverHeightModifier, &ACDropShip::SetHoverHeightModifier) + + .def("DetectObstacle", &ACDropShip::DetectObstacle) + .def("GetAltitude", &ACDropShip::GetAltitude); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ACrab) { return ConcreteTypeLuaClassDefinition(ACrab, Actor) - .def(luabind::constructor<>()) - - .property("Turret", &ACrab::GetTurret, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetTurret) - .property("Jetpack", &ACrab::GetJetpack, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetJetpack) - .property("LeftFGLeg", &ACrab::GetLeftFGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetLeftFGLeg) - .property("LeftBGLeg", &ACrab::GetLeftBGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetLeftBGLeg) - .property("RightFGLeg", &ACrab::GetRightFGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetRightFGLeg) - .property("RightBGLeg", &ACrab::GetRightBGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetRightBGLeg) - .property("StrideSound", &ACrab::GetStrideSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetStrideSound) - .property("StrideFrame", &ACrab::StrideFrame) - .property("EquippedItem", &ACrab::GetEquippedItem) - .property("FirearmIsReady", &ACrab::FirearmIsReady) - .property("FirearmIsEmpty", &ACrab::FirearmIsEmpty) - .property("FirearmNeedsReload", &ACrab::FirearmNeedsReload) - .property("FirearmIsSemiAuto", &ACrab::FirearmIsSemiAuto) - .property("FirearmActivationDelay", &ACrab::FirearmActivationDelay) - .property("LimbPathPushForce", &ACrab::GetLimbPathPushForce, &ACrab::SetLimbPathPushForce) - .property("AimRangeUpperLimit", &ACrab::GetAimRangeUpperLimit, &ACrab::SetAimRangeUpperLimit) - .property("AimRangeLowerLimit", &ACrab::GetAimRangeLowerLimit, &ACrab::SetAimRangeLowerLimit) - - .def("ReloadFirearms", &ACrab::ReloadFirearms) - .def("IsWithinRange", &ACrab::IsWithinRange) - .def("Look", &ACrab::Look) - .def("LookForMOs", &ACrab::LookForMOs) - .def("GetLimbPath", &ACrab::GetLimbPath) - .def("GetLimbPathSpeed", &ACrab::GetLimbPathSpeed) - .def("SetLimbPathSpeed", &ACrab::SetLimbPathSpeed) - - .enum_("Side")[ - luabind::value("LEFTSIDE", ACrab::Side::LEFTSIDE), - luabind::value("RIGHTSIDE", ACrab::Side::RIGHTSIDE), - luabind::value("SIDECOUNT", ACrab::Side::SIDECOUNT) - ] - .enum_("Layer")[ - luabind::value("FGROUND", ACrab::Layer::FGROUND), - luabind::value("BGROUND", ACrab::Layer::BGROUND) - ] - .enum_("DeviceHandlingState")[ - luabind::value("STILL", ACrab::DeviceHandlingState::STILL), - luabind::value("POINTING", ACrab::DeviceHandlingState::POINTING), - luabind::value("SCANNING", ACrab::DeviceHandlingState::SCANNING), - luabind::value("AIMING", ACrab::DeviceHandlingState::AIMING), - luabind::value("FIRING", ACrab::DeviceHandlingState::FIRING), - luabind::value("THROWING", ACrab::DeviceHandlingState::THROWING), - luabind::value("DIGGING", ACrab::DeviceHandlingState::DIGGING) - ] - .enum_("SweepState")[ - luabind::value("NOSWEEP", ACrab::SweepState::NOSWEEP), - luabind::value("SWEEPINGUP", ACrab::SweepState::SWEEPINGUP), - luabind::value("SWEEPUPPAUSE", ACrab::SweepState::SWEEPUPPAUSE), - luabind::value("SWEEPINGDOWN", ACrab::SweepState::SWEEPINGDOWN), - luabind::value("SWEEPDOWNPAUSE", ACrab::SweepState::SWEEPDOWNPAUSE) - ] - .enum_("DigState")[ - luabind::value("NOTDIGGING", ACrab::DigState::NOTDIGGING), - luabind::value("PREDIG", ACrab::DigState::PREDIG), - luabind::value("STARTDIG", ACrab::DigState::STARTDIG), - luabind::value("TUNNELING", ACrab::DigState::TUNNELING), - luabind::value("FINISHINGDIG", ACrab::DigState::FINISHINGDIG), - luabind::value("PAUSEDIGGER", ACrab::DigState::PAUSEDIGGER) - ] - .enum_("JumpState")[ - luabind::value("NOTJUMPING", ACrab::JumpState::NOTJUMPING), - luabind::value("FORWARDJUMP", ACrab::JumpState::FORWARDJUMP), - luabind::value("PREJUMP", ACrab::JumpState::PREUPJUMP), - luabind::value("UPJUMP", ACrab::JumpState::UPJUMP), - luabind::value("APEXJUMP", ACrab::JumpState::APEXJUMP), - luabind::value("LANDJUMP", ACrab::JumpState::LANDJUMP) - ]; + .def(luabind::constructor<>()) + + .property("Turret", &ACrab::GetTurret, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetTurret) + .property("Jetpack", &ACrab::GetJetpack, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetJetpack) + .property("LeftFGLeg", &ACrab::GetLeftFGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetLeftFGLeg) + .property("LeftBGLeg", &ACrab::GetLeftBGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetLeftBGLeg) + .property("RightFGLeg", &ACrab::GetRightFGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetRightFGLeg) + .property("RightBGLeg", &ACrab::GetRightBGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetRightBGLeg) + .property("StrideSound", &ACrab::GetStrideSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ACrabSetStrideSound) + .property("StrideFrame", &ACrab::StrideFrame) + .property("EquippedItem", &ACrab::GetEquippedItem) + .property("FirearmIsReady", &ACrab::FirearmIsReady) + .property("FirearmIsEmpty", &ACrab::FirearmIsEmpty) + .property("FirearmNeedsReload", &ACrab::FirearmNeedsReload) + .property("FirearmIsSemiAuto", &ACrab::FirearmIsSemiAuto) + .property("FirearmActivationDelay", &ACrab::FirearmActivationDelay) + .property("LimbPathPushForce", &ACrab::GetLimbPathPushForce, &ACrab::SetLimbPathPushForce) + .property("AimRangeUpperLimit", &ACrab::GetAimRangeUpperLimit, &ACrab::SetAimRangeUpperLimit) + .property("AimRangeLowerLimit", &ACrab::GetAimRangeLowerLimit, &ACrab::SetAimRangeLowerLimit) + + .def("ReloadFirearms", &ACrab::ReloadFirearms) + .def("IsWithinRange", &ACrab::IsWithinRange) + .def("Look", &ACrab::Look) + .def("LookForMOs", &ACrab::LookForMOs) + .def("GetLimbPath", &ACrab::GetLimbPath) + .def("GetLimbPathSpeed", &ACrab::GetLimbPathSpeed) + .def("SetLimbPathSpeed", &ACrab::SetLimbPathSpeed) + + .enum_("Side")[luabind::value("LEFTSIDE", ACrab::Side::LEFTSIDE), + luabind::value("RIGHTSIDE", ACrab::Side::RIGHTSIDE), + luabind::value("SIDECOUNT", ACrab::Side::SIDECOUNT)] + .enum_("Layer")[luabind::value("FGROUND", ACrab::Layer::FGROUND), + luabind::value("BGROUND", ACrab::Layer::BGROUND)] + .enum_("DeviceHandlingState")[luabind::value("STILL", ACrab::DeviceHandlingState::STILL), + luabind::value("POINTING", ACrab::DeviceHandlingState::POINTING), + luabind::value("SCANNING", ACrab::DeviceHandlingState::SCANNING), + luabind::value("AIMING", ACrab::DeviceHandlingState::AIMING), + luabind::value("FIRING", ACrab::DeviceHandlingState::FIRING), + luabind::value("THROWING", ACrab::DeviceHandlingState::THROWING), + luabind::value("DIGGING", ACrab::DeviceHandlingState::DIGGING)] + .enum_("SweepState")[luabind::value("NOSWEEP", ACrab::SweepState::NOSWEEP), + luabind::value("SWEEPINGUP", ACrab::SweepState::SWEEPINGUP), + luabind::value("SWEEPUPPAUSE", ACrab::SweepState::SWEEPUPPAUSE), + luabind::value("SWEEPINGDOWN", ACrab::SweepState::SWEEPINGDOWN), + luabind::value("SWEEPDOWNPAUSE", ACrab::SweepState::SWEEPDOWNPAUSE)] + .enum_("DigState")[luabind::value("NOTDIGGING", ACrab::DigState::NOTDIGGING), + luabind::value("PREDIG", ACrab::DigState::PREDIG), + luabind::value("STARTDIG", ACrab::DigState::STARTDIG), + luabind::value("TUNNELING", ACrab::DigState::TUNNELING), + luabind::value("FINISHINGDIG", ACrab::DigState::FINISHINGDIG), + luabind::value("PAUSEDIGGER", ACrab::DigState::PAUSEDIGGER)] + .enum_("JumpState")[luabind::value("NOTJUMPING", ACrab::JumpState::NOTJUMPING), + luabind::value("FORWARDJUMP", ACrab::JumpState::FORWARDJUMP), + luabind::value("PREJUMP", ACrab::JumpState::PREUPJUMP), + luabind::value("UPJUMP", ACrab::JumpState::UPJUMP), + luabind::value("APEXJUMP", ACrab::JumpState::APEXJUMP), + luabind::value("LANDJUMP", ACrab::JumpState::LANDJUMP)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ACraft) { return AbstractTypeLuaClassDefinition(ACraft, Actor) - .property("HatchState", &ACraft::GetHatchState) - .property("HatchOpenSound", &ACraft::GetHatchOpenSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ACraftSetHatchOpenSound) - .property("HatchCloseSound", &ACraft::GetHatchCloseSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ACraftSetHatchCloseSound) - .property("CrashSound", &ACraft::GetCrashSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ACraftSetCrashSound) - .property("MaxPassengers", &ACraft::GetMaxPassengers) - .property("DeliveryDelayMultiplier", &ACraft::GetDeliveryDelayMultiplier) - .property("ScuttleOnDeath", &ACraft::GetScuttleOnDeath, &ACraft::SetScuttleOnDeath) - .property("HatchDelay", &ACraft::GetHatchDelay, &ACraft::SetHatchDelay) - - .def("OpenHatch", &ACraft::OpenHatch) - .def("CloseHatch", &ACraft::CloseHatch) - - .enum_("HatchState")[ - luabind::value("CLOSED", ACraft::HatchState::CLOSED), - luabind::value("OPENING", ACraft::HatchState::OPENING), - luabind::value("OPEN", ACraft::HatchState::OPEN), - luabind::value("CLOSING", ACraft::HatchState::CLOSING), - luabind::value("HatchStateCount", ACraft::HatchState::HatchStateCount) - ] - .enum_("Side")[ - luabind::value("RIGHT", ACraft::Side::RIGHT), - luabind::value("LEFT", ACraft::Side::LEFT) - ] - - .enum_("CraftDeliverySequence")[ - luabind::value("FALL", ACraft::CraftDeliverySequence::FALL), - luabind::value("LAND", ACraft::CraftDeliverySequence::LAND), - luabind::value("STANDBY", ACraft::CraftDeliverySequence::STANDBY), - luabind::value("UNLOAD", ACraft::CraftDeliverySequence::UNLOAD), - luabind::value("LAUNCH", ACraft::CraftDeliverySequence::LAUNCH), - luabind::value("UNSTICK", ACraft::CraftDeliverySequence::UNSTICK) - ] - .enum_("AltitudeMoveState")[ - luabind::value("HOVER", ACraft::AltitudeMoveState::HOVER), - luabind::value("DESCEND", ACraft::AltitudeMoveState::DESCEND), - luabind::value("ASCEND", ACraft::AltitudeMoveState::ASCEND) - ]; + .property("HatchState", &ACraft::GetHatchState) + .property("HatchOpenSound", &ACraft::GetHatchOpenSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ACraftSetHatchOpenSound) + .property("HatchCloseSound", &ACraft::GetHatchCloseSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ACraftSetHatchCloseSound) + .property("CrashSound", &ACraft::GetCrashSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ACraftSetCrashSound) + .property("MaxPassengers", &ACraft::GetMaxPassengers) + .property("DeliveryDelayMultiplier", &ACraft::GetDeliveryDelayMultiplier) + .property("ScuttleOnDeath", &ACraft::GetScuttleOnDeath, &ACraft::SetScuttleOnDeath) + .property("HatchDelay", &ACraft::GetHatchDelay, &ACraft::SetHatchDelay) + + .def("OpenHatch", &ACraft::OpenHatch) + .def("CloseHatch", &ACraft::CloseHatch) + + .enum_("HatchState")[luabind::value("CLOSED", ACraft::HatchState::CLOSED), + luabind::value("OPENING", ACraft::HatchState::OPENING), + luabind::value("OPEN", ACraft::HatchState::OPEN), + luabind::value("CLOSING", ACraft::HatchState::CLOSING), + luabind::value("HatchStateCount", ACraft::HatchState::HatchStateCount)] + .enum_("Side")[luabind::value("RIGHT", ACraft::Side::RIGHT), + luabind::value("LEFT", ACraft::Side::LEFT)] + + .enum_("CraftDeliverySequence")[luabind::value("FALL", ACraft::CraftDeliverySequence::FALL), + luabind::value("LAND", ACraft::CraftDeliverySequence::LAND), + luabind::value("STANDBY", ACraft::CraftDeliverySequence::STANDBY), + luabind::value("UNLOAD", ACraft::CraftDeliverySequence::UNLOAD), + luabind::value("LAUNCH", ACraft::CraftDeliverySequence::LAUNCH), + luabind::value("UNSTICK", ACraft::CraftDeliverySequence::UNSTICK)] + .enum_("AltitudeMoveState")[luabind::value("HOVER", ACraft::AltitudeMoveState::HOVER), + luabind::value("DESCEND", ACraft::AltitudeMoveState::DESCEND), + luabind::value("ASCEND", ACraft::AltitudeMoveState::ASCEND)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ACRocket) { return ConcreteTypeLuaClassDefinition(ACRocket, ACraft) - .property("RightLeg", &ACRocket::GetRightLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetRightLeg) - .property("LeftLeg", &ACRocket::GetLeftLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetLeftLeg) - .property("MainEngine", &ACRocket::GetMainThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetMainThruster) - .property("LeftEngine", &ACRocket::GetLeftThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetLeftThruster) - .property("RightEngine", &ACRocket::GetRightThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetRightThruster) - .property("LeftThruster", &ACRocket::GetULeftThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetULeftThruster) - .property("RightThruster", &ACRocket::GetURightThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetURightThruster) - .property("GearState", &ACRocket::GetGearState) - - .enum_("LandingGearState")[ - luabind::value("RAISED", ACRocket::LandingGearState::RAISED), - luabind::value("LOWERED", ACRocket::LandingGearState::LOWERED), - luabind::value("LOWERING", ACRocket::LandingGearState::LOWERING), - luabind::value("RAISING", ACRocket::LandingGearState::RAISING), - luabind::value("GearStateCount", ACRocket::LandingGearState::GearStateCount) - ]; + .property("RightLeg", &ACRocket::GetRightLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetRightLeg) + .property("LeftLeg", &ACRocket::GetLeftLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetLeftLeg) + .property("MainEngine", &ACRocket::GetMainThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetMainThruster) + .property("LeftEngine", &ACRocket::GetLeftThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetLeftThruster) + .property("RightEngine", &ACRocket::GetRightThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetRightThruster) + .property("LeftThruster", &ACRocket::GetULeftThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetULeftThruster) + .property("RightThruster", &ACRocket::GetURightThruster, &LuaAdaptersPropertyOwnershipSafetyFaker::ACRocketSetURightThruster) + .property("GearState", &ACRocket::GetGearState) + + .enum_("LandingGearState")[luabind::value("RAISED", ACRocket::LandingGearState::RAISED), + luabind::value("LOWERED", ACRocket::LandingGearState::LOWERED), + luabind::value("LOWERING", ACRocket::LandingGearState::LOWERING), + luabind::value("RAISING", ACRocket::LandingGearState::RAISING), + luabind::value("GearStateCount", ACRocket::LandingGearState::GearStateCount)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Actor) { return ConcreteTypeLuaClassDefinition(Actor, MOSRotating) - .def(luabind::constructor<>()) - - .property("PlayerControllable", &Actor::IsPlayerControllable, &Actor::SetPlayerControllable) - .property("BodyHitSound", &Actor::GetBodyHitSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetBodyHitSound) - .property("AlarmSound", &Actor::GetAlarmSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetAlarmSound) - .property("PainSound", &Actor::GetPainSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetPainSound) - .property("DeathSound", &Actor::GetDeathSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetDeathSound) - .property("DeviceSwitchSound", &Actor::GetDeviceSwitchSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetDeviceSwitchSound) - .property("ImpulseDamageThreshold", &Actor::GetTravelImpulseDamage, &Actor::SetTravelImpulseDamage) - .property("StableRecoveryDelay", &Actor::GetStableRecoverDelay, &Actor::SetStableRecoverDelay) - .property("Status", &Actor::GetStatus, &Actor::SetStatus) - .property("Health", &Actor::GetHealth, &Actor::SetHealth) - .property("PrevHealth", &Actor::GetPrevHealth) - .property("MaxHealth", &Actor::GetMaxHealth, &Actor::SetMaxHealth) - .property("InventoryMass", &Actor::GetInventoryMass) - .property("GoldCarried", &Actor::GetGoldCarried, &Actor::SetGoldCarried) - .property("AimRange", &Actor::GetAimRange, &Actor::SetAimRange) - .property("CPUPos", &Actor::GetCPUPos) - .property("EyePos", &Actor::GetEyePos) - .property("HolsterOffset", &Actor::GetHolsterOffset, &Actor::SetHolsterOffset) - .property("ReloadOffset", &Actor::GetReloadOffset, &Actor::SetReloadOffset) - .property("ViewPoint", &Actor::GetViewPoint, &Actor::SetViewPoint) - .property("ItemInReach", &Actor::GetItemInReach, &Actor::SetItemInReach) - .property("SharpAimProgress", &Actor::GetSharpAimProgress) - .property("Height", &Actor::GetHeight) - .property("AIMode", &Actor::GetAIMode, &Actor::SetAIMode) - .property("DeploymentID", &Actor::GetDeploymentID) - .property("PassengerSlots", &Actor::GetPassengerSlots, &Actor::SetPassengerSlots) - .property("Perceptiveness", &Actor::GetPerceptiveness, &Actor::SetPerceptiveness) - .property("PainThreshold", &Actor::GetPainThreshold, &Actor::SetPainThreshold) - .property("CanRevealUnseen", &Actor::GetCanRevealUnseen, &Actor::SetCanRevealUnseen) - .property("InventorySize", &Actor::GetInventorySize) - .property("MaxInventoryMass", &Actor::GetMaxInventoryMass) - .property("MovePathSize", &Actor::GetMovePathSize) - .property("MovePathEnd", &Actor::GetMovePathEnd) - .property("IsWaitingOnNewMovePath", &Actor::IsWaitingOnNewMovePath) - .property("AimDistance", &Actor::GetAimDistance, &Actor::SetAimDistance) - .property("SightDistance", &Actor::GetSightDistance, &Actor::SetSightDistance) - .property("PieMenu", &Actor::GetPieMenu, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetPieMenu) - .property("AIBaseDigStrength", &Actor::GetAIBaseDigStrength, &Actor::SetAIBaseDigStrength) - .property("DigStrength", &Actor::EstimateDigStrength) - .property("SceneWaypoints", &LuaAdaptersActor::GetSceneWaypoints, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .property("LimbPushForcesAndCollisionsDisabled", &Actor::GetLimbPushForcesAndCollisionsDisabled, &Actor::SetLimbPushForcesAndCollisionsDisabled) - .property("MoveProximityLimit", &Actor::GetMoveProximityLimit, &Actor::SetMoveProximityLimit) - - .def_readwrite("MOMoveTarget", &Actor::m_pMOMoveTarget) - .def_readwrite("MovePath", &Actor::m_MovePath, luabind::return_stl_iterator) - .def_readwrite("Inventory", &Actor::m_Inventory, luabind::return_stl_iterator) - - .def("GetController", &Actor::GetController) - .def("IsPlayerControlled", &Actor::IsPlayerControlled) - .def("IsControllable", &Actor::IsControllable) - .def("SetControllerMode", &Actor::SetControllerMode) - .def("SwapControllerModes", &Actor::SwapControllerModes) - .def("GetStableVelocityThreshold", &Actor::GetStableVel) - .def("SetStableVelocityThreshold", (void (Actor::*)(float, float))&Actor::SetStableVel) - .def("SetStableVelocityThreshold", (void (Actor::*)(Vector))&Actor::SetStableVel) - .def("GetAimAngle", &Actor::GetAimAngle) - .def("SetAimAngle", &Actor::SetAimAngle) - .def("HasObject", &Actor::HasObject) - .def("HasObjectInGroup", &Actor::HasObjectInGroup) - .def("IsWithinRange", &Actor::IsWithinRange) - .def("AddGold", &Actor::AddGold) - .def("AddHealth", &Actor::AddHealth) - .def("IsStatus", &Actor::IsStatus) - .def("IsDead", &Actor::IsDead) - .def("AddAISceneWaypoint", &Actor::AddAISceneWaypoint) - .def("AddAIMOWaypoint", &Actor::AddAIMOWaypoint) - .def("ClearAIWaypoints", &Actor::ClearAIWaypoints) - .def("GetLastAIWaypoint", &Actor::GetLastAIWaypoint) - .def("GetAIMOWaypointID", &Actor::GetAIMOWaypointID) - .def("GetWaypointListSize", &Actor::GetWaypointsSize) - .def("ClearMovePath", &Actor::ClearMovePath) - .def("AddToMovePathBeginning", &Actor::AddToMovePathBeginning) - .def("AddToMovePathEnd", &Actor::AddToMovePathEnd) - .def("RemoveMovePathBeginning", &Actor::RemoveMovePathBeginning) - .def("RemoveMovePathEnd", &Actor::RemoveMovePathEnd) - .def("AddInventoryItem", &Actor::AddInventoryItem, luabind::adopt(_2)) - .def("RemoveInventoryItem", (void (Actor::*)(const std::string &))&Actor::RemoveInventoryItem) - .def("RemoveInventoryItem", (void (Actor::*)(const std::string &, const std::string &))&Actor::RemoveInventoryItem) - .def("RemoveInventoryItemAtIndex", &Actor::RemoveInventoryItemAtIndex, luabind::adopt(luabind::return_value)) - .def("SwapNextInventory", &Actor::SwapNextInventory) - .def("SwapPrevInventory", &Actor::SwapPrevInventory) - .def("DropAllInventory", &Actor::DropAllInventory) - .def("DropAllGold", &Actor::DropAllGold) - .def("IsInventoryEmpty", &Actor::IsInventoryEmpty) - .def("DrawWaypoints", &Actor::DrawWaypoints) - .def("SetMovePathToUpdate", &Actor::SetMovePathToUpdate) - .def("UpdateMovePath", &Actor::UpdateMovePath) - .def("SetAlarmPoint", &Actor::AlarmPoint) - .def("GetAlarmPoint", &Actor::GetAlarmPoint) - .def("IsOrganic", &Actor::IsOrganic) - .def("IsMechanical", &Actor::IsMechanical) - - .enum_("Status")[ - luabind::value("STABLE", Actor::Status::STABLE), - luabind::value("UNSTABLE", Actor::Status::UNSTABLE), - luabind::value("INACTIVE", Actor::Status::INACTIVE), - luabind::value("DYING", Actor::Status::DYING), - luabind::value("DEAD", Actor::Status::DEAD) - ] - .enum_("MovementState")[ - luabind::value("NOMOVE", Actor::MovementState::NOMOVE), - luabind::value("STAND", Actor::MovementState::STAND), - luabind::value("WALK", Actor::MovementState::WALK), - luabind::value("JUMP", Actor::MovementState::JUMP), - luabind::value("DISLODGE", Actor::MovementState::DISLODGE), - luabind::value("CROUCH", Actor::MovementState::CROUCH), - luabind::value("CRAWL", Actor::MovementState::CRAWL), - luabind::value("ARMCRAWL", Actor::MovementState::ARMCRAWL), - luabind::value("CLIMB", Actor::MovementState::CLIMB), - luabind::value("MOVEMENTSTATECOUNT", Actor::MovementState::MOVEMENTSTATECOUNT) - ] - .enum_("AIMode")[ - luabind::value("AIMODE_NONE", Actor::AIMode::AIMODE_NONE), - luabind::value("AIMODE_SENTRY", Actor::AIMode::AIMODE_SENTRY), - luabind::value("AIMODE_PATROL", Actor::AIMode::AIMODE_PATROL), - luabind::value("AIMODE_GOTO", Actor::AIMode::AIMODE_GOTO), - luabind::value("AIMODE_BRAINHUNT", Actor::AIMode::AIMODE_BRAINHUNT), - luabind::value("AIMODE_GOLDDIG", Actor::AIMode::AIMODE_GOLDDIG), - luabind::value("AIMODE_RETURN", Actor::AIMode::AIMODE_RETURN), - luabind::value("AIMODE_STAY", Actor::AIMode::AIMODE_STAY), - luabind::value("AIMODE_SCUTTLE", Actor::AIMode::AIMODE_SCUTTLE), - luabind::value("AIMODE_DELIVER", Actor::AIMode::AIMODE_DELIVER), - luabind::value("AIMODE_BOMB", Actor::AIMode::AIMODE_BOMB), - luabind::value("AIMODE_SQUAD", Actor::AIMode::AIMODE_SQUAD), - luabind::value("AIMODE_COUNT", Actor::AIMode::AIMODE_COUNT) - ] - .enum_("ActionState")[ - luabind::value("MOVING", Actor::ActionState::MOVING), - luabind::value("MOVING_FAST", Actor::ActionState::MOVING_FAST), - luabind::value("FIRING", Actor::ActionState::FIRING), - luabind::value("ActionStateCount", Actor::ActionState::ActionStateCount) - ] - .enum_("AimState")[ - luabind::value("AIMSTILL", Actor::AimState::AIMSTILL), - luabind::value("AIMUP", Actor::AimState::AIMUP), - luabind::value("AIMDOWN", Actor::AimState::AIMDOWN), - luabind::value("AimStateCount", Actor::AimState::AimStateCount) - ] - .enum_("LateralMoveState")[ - luabind::value("LAT_STILL", Actor::LateralMoveState::LAT_STILL), - luabind::value("LAT_LEFT", Actor::LateralMoveState::LAT_LEFT), - luabind::value("LAT_RIGHT", Actor::LateralMoveState::LAT_RIGHT) - ] - .enum_("ObstacleState")[ - luabind::value("PROCEEDING", Actor::ObstacleState::PROCEEDING), - luabind::value("BACKSTEPPING", Actor::ObstacleState::BACKSTEPPING), - luabind::value("DIGPAUSING", Actor::ObstacleState::DIGPAUSING), - luabind::value("JUMPING", Actor::ObstacleState::JUMPING), - luabind::value("SOFTLANDING", Actor::ObstacleState::SOFTLANDING) - ] - .enum_("TeamBlockState")[ - luabind::value("NOTBLOCKED", Actor::TeamBlockState::NOTBLOCKED), - luabind::value("BLOCKED", Actor::TeamBlockState::BLOCKED), - luabind::value("IGNORINGBLOCK", Actor::TeamBlockState::IGNORINGBLOCK), - luabind::value("FOLLOWWAIT", Actor::TeamBlockState::FOLLOWWAIT) - ]; + .def(luabind::constructor<>()) + + .property("PlayerControllable", &Actor::IsPlayerControllable, &Actor::SetPlayerControllable) + .property("BodyHitSound", &Actor::GetBodyHitSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetBodyHitSound) + .property("AlarmSound", &Actor::GetAlarmSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetAlarmSound) + .property("PainSound", &Actor::GetPainSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetPainSound) + .property("DeathSound", &Actor::GetDeathSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetDeathSound) + .property("DeviceSwitchSound", &Actor::GetDeviceSwitchSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetDeviceSwitchSound) + .property("ImpulseDamageThreshold", &Actor::GetTravelImpulseDamage, &Actor::SetTravelImpulseDamage) + .property("StableRecoveryDelay", &Actor::GetStableRecoverDelay, &Actor::SetStableRecoverDelay) + .property("Status", &Actor::GetStatus, &Actor::SetStatus) + .property("Health", &Actor::GetHealth, &Actor::SetHealth) + .property("PrevHealth", &Actor::GetPrevHealth) + .property("MaxHealth", &Actor::GetMaxHealth, &Actor::SetMaxHealth) + .property("InventoryMass", &Actor::GetInventoryMass) + .property("GoldCarried", &Actor::GetGoldCarried, &Actor::SetGoldCarried) + .property("AimRange", &Actor::GetAimRange, &Actor::SetAimRange) + .property("CPUPos", &Actor::GetCPUPos) + .property("EyePos", &Actor::GetEyePos) + .property("HolsterOffset", &Actor::GetHolsterOffset, &Actor::SetHolsterOffset) + .property("ReloadOffset", &Actor::GetReloadOffset, &Actor::SetReloadOffset) + .property("ViewPoint", &Actor::GetViewPoint, &Actor::SetViewPoint) + .property("ItemInReach", &Actor::GetItemInReach, &Actor::SetItemInReach) + .property("SharpAimProgress", &Actor::GetSharpAimProgress) + .property("Height", &Actor::GetHeight) + .property("AIMode", &Actor::GetAIMode, &Actor::SetAIMode) + .property("DeploymentID", &Actor::GetDeploymentID) + .property("PassengerSlots", &Actor::GetPassengerSlots, &Actor::SetPassengerSlots) + .property("Perceptiveness", &Actor::GetPerceptiveness, &Actor::SetPerceptiveness) + .property("PainThreshold", &Actor::GetPainThreshold, &Actor::SetPainThreshold) + .property("CanRevealUnseen", &Actor::GetCanRevealUnseen, &Actor::SetCanRevealUnseen) + .property("InventorySize", &Actor::GetInventorySize) + .property("MaxInventoryMass", &Actor::GetMaxInventoryMass) + .property("MovePathSize", &Actor::GetMovePathSize) + .property("MovePathEnd", &Actor::GetMovePathEnd) + .property("IsWaitingOnNewMovePath", &Actor::IsWaitingOnNewMovePath) + .property("AimDistance", &Actor::GetAimDistance, &Actor::SetAimDistance) + .property("SightDistance", &Actor::GetSightDistance, &Actor::SetSightDistance) + .property("PieMenu", &Actor::GetPieMenu, &LuaAdaptersPropertyOwnershipSafetyFaker::ActorSetPieMenu) + .property("AIBaseDigStrength", &Actor::GetAIBaseDigStrength, &Actor::SetAIBaseDigStrength) + .property("DigStrength", &Actor::EstimateDigStrength) + .property("SceneWaypoints", &LuaAdaptersActor::GetSceneWaypoints, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .property("LimbPushForcesAndCollisionsDisabled", &Actor::GetLimbPushForcesAndCollisionsDisabled, &Actor::SetLimbPushForcesAndCollisionsDisabled) + .property("MoveProximityLimit", &Actor::GetMoveProximityLimit, &Actor::SetMoveProximityLimit) + + .def_readwrite("MOMoveTarget", &Actor::m_pMOMoveTarget) + .def_readwrite("MovePath", &Actor::m_MovePath, luabind::return_stl_iterator) + .def_readwrite("Inventory", &Actor::m_Inventory, luabind::return_stl_iterator) + + .def("GetController", &Actor::GetController) + .def("IsPlayerControlled", &Actor::IsPlayerControlled) + .def("IsControllable", &Actor::IsControllable) + .def("SetControllerMode", &Actor::SetControllerMode) + .def("SwapControllerModes", &Actor::SwapControllerModes) + .def("GetStableVelocityThreshold", &Actor::GetStableVel) + .def("SetStableVelocityThreshold", (void(Actor::*)(float, float)) & Actor::SetStableVel) + .def("SetStableVelocityThreshold", (void(Actor::*)(Vector)) & Actor::SetStableVel) + .def("GetAimAngle", &Actor::GetAimAngle) + .def("SetAimAngle", &Actor::SetAimAngle) + .def("HasObject", &Actor::HasObject) + .def("HasObjectInGroup", &Actor::HasObjectInGroup) + .def("IsWithinRange", &Actor::IsWithinRange) + .def("AddGold", &Actor::AddGold) + .def("AddHealth", &Actor::AddHealth) + .def("IsStatus", &Actor::IsStatus) + .def("IsDead", &Actor::IsDead) + .def("AddAISceneWaypoint", &Actor::AddAISceneWaypoint) + .def("AddAIMOWaypoint", &Actor::AddAIMOWaypoint) + .def("ClearAIWaypoints", &Actor::ClearAIWaypoints) + .def("GetLastAIWaypoint", &Actor::GetLastAIWaypoint) + .def("GetAIMOWaypointID", &Actor::GetAIMOWaypointID) + .def("GetWaypointListSize", &Actor::GetWaypointsSize) + .def("ClearMovePath", &Actor::ClearMovePath) + .def("AddToMovePathBeginning", &Actor::AddToMovePathBeginning) + .def("AddToMovePathEnd", &Actor::AddToMovePathEnd) + .def("RemoveMovePathBeginning", &Actor::RemoveMovePathBeginning) + .def("RemoveMovePathEnd", &Actor::RemoveMovePathEnd) + .def("AddInventoryItem", &Actor::AddInventoryItem, luabind::adopt(_2)) + .def("RemoveInventoryItem", (void(Actor::*)(const std::string&)) & Actor::RemoveInventoryItem) + .def("RemoveInventoryItem", (void(Actor::*)(const std::string&, const std::string&)) & Actor::RemoveInventoryItem) + .def("RemoveInventoryItemAtIndex", &Actor::RemoveInventoryItemAtIndex, luabind::adopt(luabind::return_value)) + .def("SwapNextInventory", &Actor::SwapNextInventory) + .def("SwapPrevInventory", &Actor::SwapPrevInventory) + .def("DropAllInventory", &Actor::DropAllInventory) + .def("DropAllGold", &Actor::DropAllGold) + .def("IsInventoryEmpty", &Actor::IsInventoryEmpty) + .def("DrawWaypoints", &Actor::DrawWaypoints) + .def("SetMovePathToUpdate", &Actor::SetMovePathToUpdate) + .def("UpdateMovePath", &Actor::UpdateMovePath) + .def("SetAlarmPoint", &Actor::AlarmPoint) + .def("GetAlarmPoint", &Actor::GetAlarmPoint) + .def("IsOrganic", &Actor::IsOrganic) + .def("IsMechanical", &Actor::IsMechanical) + + .enum_("Status")[luabind::value("STABLE", Actor::Status::STABLE), + luabind::value("UNSTABLE", Actor::Status::UNSTABLE), + luabind::value("INACTIVE", Actor::Status::INACTIVE), + luabind::value("DYING", Actor::Status::DYING), + luabind::value("DEAD", Actor::Status::DEAD)] + .enum_("MovementState")[luabind::value("NOMOVE", Actor::MovementState::NOMOVE), + luabind::value("STAND", Actor::MovementState::STAND), + luabind::value("WALK", Actor::MovementState::WALK), + luabind::value("JUMP", Actor::MovementState::JUMP), + luabind::value("DISLODGE", Actor::MovementState::DISLODGE), + luabind::value("CROUCH", Actor::MovementState::CROUCH), + luabind::value("CRAWL", Actor::MovementState::CRAWL), + luabind::value("ARMCRAWL", Actor::MovementState::ARMCRAWL), + luabind::value("CLIMB", Actor::MovementState::CLIMB), + luabind::value("MOVEMENTSTATECOUNT", Actor::MovementState::MOVEMENTSTATECOUNT)] + .enum_("AIMode")[luabind::value("AIMODE_NONE", Actor::AIMode::AIMODE_NONE), + luabind::value("AIMODE_SENTRY", Actor::AIMode::AIMODE_SENTRY), + luabind::value("AIMODE_PATROL", Actor::AIMode::AIMODE_PATROL), + luabind::value("AIMODE_GOTO", Actor::AIMode::AIMODE_GOTO), + luabind::value("AIMODE_BRAINHUNT", Actor::AIMode::AIMODE_BRAINHUNT), + luabind::value("AIMODE_GOLDDIG", Actor::AIMode::AIMODE_GOLDDIG), + luabind::value("AIMODE_RETURN", Actor::AIMode::AIMODE_RETURN), + luabind::value("AIMODE_STAY", Actor::AIMode::AIMODE_STAY), + luabind::value("AIMODE_SCUTTLE", Actor::AIMode::AIMODE_SCUTTLE), + luabind::value("AIMODE_DELIVER", Actor::AIMode::AIMODE_DELIVER), + luabind::value("AIMODE_BOMB", Actor::AIMode::AIMODE_BOMB), + luabind::value("AIMODE_SQUAD", Actor::AIMode::AIMODE_SQUAD), + luabind::value("AIMODE_COUNT", Actor::AIMode::AIMODE_COUNT)] + .enum_("ActionState")[luabind::value("MOVING", Actor::ActionState::MOVING), + luabind::value("MOVING_FAST", Actor::ActionState::MOVING_FAST), + luabind::value("FIRING", Actor::ActionState::FIRING), + luabind::value("ActionStateCount", Actor::ActionState::ActionStateCount)] + .enum_("AimState")[luabind::value("AIMSTILL", Actor::AimState::AIMSTILL), + luabind::value("AIMUP", Actor::AimState::AIMUP), + luabind::value("AIMDOWN", Actor::AimState::AIMDOWN), + luabind::value("AimStateCount", Actor::AimState::AimStateCount)] + .enum_("LateralMoveState")[luabind::value("LAT_STILL", Actor::LateralMoveState::LAT_STILL), + luabind::value("LAT_LEFT", Actor::LateralMoveState::LAT_LEFT), + luabind::value("LAT_RIGHT", Actor::LateralMoveState::LAT_RIGHT)] + .enum_("ObstacleState")[luabind::value("PROCEEDING", Actor::ObstacleState::PROCEEDING), + luabind::value("BACKSTEPPING", Actor::ObstacleState::BACKSTEPPING), + luabind::value("DIGPAUSING", Actor::ObstacleState::DIGPAUSING), + luabind::value("JUMPING", Actor::ObstacleState::JUMPING), + luabind::value("SOFTLANDING", Actor::ObstacleState::SOFTLANDING)] + .enum_("TeamBlockState")[luabind::value("NOTBLOCKED", Actor::TeamBlockState::NOTBLOCKED), + luabind::value("BLOCKED", Actor::TeamBlockState::BLOCKED), + luabind::value("IGNORINGBLOCK", Actor::TeamBlockState::IGNORINGBLOCK), + luabind::value("FOLLOWWAIT", Actor::TeamBlockState::FOLLOWWAIT)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ADoor) { return ConcreteTypeLuaClassDefinition(ADoor, Actor) - .property("Door", &ADoor::GetDoor, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoor) - .property("DoorMoveStartSound", &ADoor::GetDoorMoveStartSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoorMoveStartSound) - .property("DoorMoveSound", &ADoor::GetDoorMoveSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoorMoveSound) - .property("DoorDirectionChangeSound", &ADoor::GetDoorDirectionChangeSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoorDirectionChangeSound) - .property("DoorMoveEndSound", &ADoor::GetDoorMoveEndSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoorMoveEndSound) - - .def("GetDoorState", &ADoor::GetDoorState) - .def("OpenDoor", &ADoor::OpenDoor) - .def("CloseDoor", &ADoor::CloseDoor) - .def("StopDoor", &ADoor::StopDoor) - .def("ResetSensorTimer", &ADoor::ResetSensorTimer) - .def("SetClosedByDefault", &ADoor::SetClosedByDefault) - - .enum_("DoorState")[ - luabind::value("CLOSED", ADoor::DoorState::CLOSED), - luabind::value("OPENING", ADoor::DoorState::OPENING), - luabind::value("OPEN", ADoor::DoorState::OPEN), - luabind::value("CLOSING", ADoor::DoorState::CLOSING), - luabind::value("STOPPED", ADoor::DoorState::STOPPED) - ]; + .property("Door", &ADoor::GetDoor, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoor) + .property("DoorMoveStartSound", &ADoor::GetDoorMoveStartSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoorMoveStartSound) + .property("DoorMoveSound", &ADoor::GetDoorMoveSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoorMoveSound) + .property("DoorDirectionChangeSound", &ADoor::GetDoorDirectionChangeSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoorDirectionChangeSound) + .property("DoorMoveEndSound", &ADoor::GetDoorMoveEndSound, &LuaAdaptersPropertyOwnershipSafetyFaker::ADoorSetDoorMoveEndSound) + + .def("GetDoorState", &ADoor::GetDoorState) + .def("OpenDoor", &ADoor::OpenDoor) + .def("CloseDoor", &ADoor::CloseDoor) + .def("StopDoor", &ADoor::StopDoor) + .def("ResetSensorTimer", &ADoor::ResetSensorTimer) + .def("SetClosedByDefault", &ADoor::SetClosedByDefault) + + .enum_("DoorState")[luabind::value("CLOSED", ADoor::DoorState::CLOSED), + luabind::value("OPENING", ADoor::DoorState::OPENING), + luabind::value("OPEN", ADoor::DoorState::OPEN), + luabind::value("CLOSING", ADoor::DoorState::CLOSING), + luabind::value("STOPPED", ADoor::DoorState::STOPPED)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, AEmitter) { return ConcreteTypeLuaClassDefinition(AEmitter, Attachable) - .property("EmissionSound", &AEmitter::GetEmissionSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AEmitterSetEmissionSound) - .property("BurstSound", &AEmitter::GetBurstSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AEmitterSetBurstSound) - .property("EndSound", &AEmitter::GetEndSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AEmitterSetEndSound) - .property("BurstScale", &AEmitter::GetBurstScale, &AEmitter::SetBurstScale) - .property("EmitAngle", &AEmitter::GetEmitAngle, &AEmitter::SetEmitAngle) - .property("GetThrottle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) - .property("Throttle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) - .property("ThrottleFactor", &AEmitter::GetThrottleFactor) - .property("NegativeThrottleMultiplier", &AEmitter::GetNegativeThrottleMultiplier, &AEmitter::SetNegativeThrottleMultiplier) - .property("PositiveThrottleMultiplier", &AEmitter::GetPositiveThrottleMultiplier, &AEmitter::SetPositiveThrottleMultiplier) - .property("BurstSpacing", &AEmitter::GetBurstSpacing, &AEmitter::SetBurstSpacing) - .property("BurstDamage", &AEmitter::GetBurstDamage, &AEmitter::SetBurstDamage) - .property("EmitterDamageMultiplier", &AEmitter::GetEmitterDamageMultiplier, &AEmitter::SetEmitterDamageMultiplier) - .property("EmitCount", &AEmitter::GetEmitCount) - .property("EmitCountLimit", &AEmitter::GetEmitCountLimit, &AEmitter::SetEmitCountLimit) - .property("EmitDamage", &AEmitter::GetEmitDamage, &AEmitter::SetEmitDamage) - .property("EmitOffset", &AEmitter::GetEmitOffset, &AEmitter::SetEmitOffset) - .property("Flash", &AEmitter::GetFlash, &LuaAdaptersPropertyOwnershipSafetyFaker::AEmitterSetFlash) - .property("FlashScale", &AEmitter::GetFlashScale, &AEmitter::SetFlashScale) - .property("TotalParticlesPerMinute", &AEmitter::GetTotalParticlesPerMinute) - .property("TotalBurstSize", &AEmitter::GetTotalBurstSize) - - .def_readwrite("Emissions", &AEmitter::m_EmissionList, luabind::return_stl_iterator) - - .def("IsEmitting", &AEmitter::IsEmitting) - .def("WasEmitting", &AEmitter::WasEmitting) - .def("EnableEmission", &AEmitter::EnableEmission) - .def("GetEmitVector", &AEmitter::GetEmitVector) - .def("GetRecoilVector", &AEmitter::GetRecoilVector) - .def("EstimateImpulse", &AEmitter::EstimateImpulse) - .def("TriggerBurst", &AEmitter::TriggerBurst) - .def("IsSetToBurst", &AEmitter::IsSetToBurst) - .def("CanTriggerBurst", &AEmitter::CanTriggerBurst) - .def("GetScaledThrottle", &AEmitter::GetScaledThrottle) - .def("JustStartedEmitting", &AEmitter::JustStartedEmitting); + .property("EmissionSound", &AEmitter::GetEmissionSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AEmitterSetEmissionSound) + .property("BurstSound", &AEmitter::GetBurstSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AEmitterSetBurstSound) + .property("EndSound", &AEmitter::GetEndSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AEmitterSetEndSound) + .property("BurstScale", &AEmitter::GetBurstScale, &AEmitter::SetBurstScale) + .property("EmitAngle", &AEmitter::GetEmitAngle, &AEmitter::SetEmitAngle) + .property("GetThrottle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) + .property("Throttle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) + .property("ThrottleFactor", &AEmitter::GetThrottleFactor) + .property("NegativeThrottleMultiplier", &AEmitter::GetNegativeThrottleMultiplier, &AEmitter::SetNegativeThrottleMultiplier) + .property("PositiveThrottleMultiplier", &AEmitter::GetPositiveThrottleMultiplier, &AEmitter::SetPositiveThrottleMultiplier) + .property("BurstSpacing", &AEmitter::GetBurstSpacing, &AEmitter::SetBurstSpacing) + .property("BurstDamage", &AEmitter::GetBurstDamage, &AEmitter::SetBurstDamage) + .property("EmitterDamageMultiplier", &AEmitter::GetEmitterDamageMultiplier, &AEmitter::SetEmitterDamageMultiplier) + .property("EmitCount", &AEmitter::GetEmitCount) + .property("EmitCountLimit", &AEmitter::GetEmitCountLimit, &AEmitter::SetEmitCountLimit) + .property("EmitDamage", &AEmitter::GetEmitDamage, &AEmitter::SetEmitDamage) + .property("EmitOffset", &AEmitter::GetEmitOffset, &AEmitter::SetEmitOffset) + .property("Flash", &AEmitter::GetFlash, &LuaAdaptersPropertyOwnershipSafetyFaker::AEmitterSetFlash) + .property("FlashScale", &AEmitter::GetFlashScale, &AEmitter::SetFlashScale) + .property("TotalParticlesPerMinute", &AEmitter::GetTotalParticlesPerMinute) + .property("TotalBurstSize", &AEmitter::GetTotalBurstSize) + + .def_readwrite("Emissions", &AEmitter::m_EmissionList, luabind::return_stl_iterator) + + .def("IsEmitting", &AEmitter::IsEmitting) + .def("WasEmitting", &AEmitter::WasEmitting) + .def("EnableEmission", &AEmitter::EnableEmission) + .def("GetEmitVector", &AEmitter::GetEmitVector) + .def("GetRecoilVector", &AEmitter::GetRecoilVector) + .def("EstimateImpulse", &AEmitter::EstimateImpulse) + .def("TriggerBurst", &AEmitter::TriggerBurst) + .def("IsSetToBurst", &AEmitter::IsSetToBurst) + .def("CanTriggerBurst", &AEmitter::CanTriggerBurst) + .def("GetScaledThrottle", &AEmitter::GetScaledThrottle) + .def("JustStartedEmitting", &AEmitter::JustStartedEmitting); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, AEJetpack) { return ConcreteTypeLuaClassDefinition(AEJetpack, AEmitter) - .property("JetpackType", &AEJetpack::GetJetpackType, &AEJetpack::SetJetpackType) - .property("JetTimeTotal", &AEJetpack::GetJetTimeTotal, &AEJetpack::SetJetTimeTotal) - .property("JetTimeLeft", &AEJetpack::GetJetTimeLeft) - .property("JetReplenishRate", &AEJetpack::GetJetReplenishRate, &AEJetpack::SetJetReplenishRate) - .property("MinimumFuelRatio", &AEJetpack::GetMinimumFuelRatio, &AEJetpack::SetMinimumFuelRatio) - .property("JetAngleRange", &AEJetpack::GetJetAngleRange, &AEJetpack::SetJetAngleRange) - .property("CanAdjustAngleWhileFiring", &AEJetpack::GetCanAdjustAngleWhileFiring, &AEJetpack::SetCanAdjustAngleWhileFiring) - .property("AdjustsThrottleForWeight", &AEJetpack::GetAdjustsThrottleForWeight, &AEJetpack::SetAdjustsThrottleForWeight) - - .enum_("JetpackType")[ - luabind::value("Standard", AEJetpack::Standard), - luabind::value("JumpPack", AEJetpack::JumpPack) - ]; + .property("JetpackType", &AEJetpack::GetJetpackType, &AEJetpack::SetJetpackType) + .property("JetTimeTotal", &AEJetpack::GetJetTimeTotal, &AEJetpack::SetJetTimeTotal) + .property("JetTimeLeft", &AEJetpack::GetJetTimeLeft) + .property("JetReplenishRate", &AEJetpack::GetJetReplenishRate, &AEJetpack::SetJetReplenishRate) + .property("MinimumFuelRatio", &AEJetpack::GetMinimumFuelRatio, &AEJetpack::SetMinimumFuelRatio) + .property("JetAngleRange", &AEJetpack::GetJetAngleRange, &AEJetpack::SetJetAngleRange) + .property("CanAdjustAngleWhileFiring", &AEJetpack::GetCanAdjustAngleWhileFiring, &AEJetpack::SetCanAdjustAngleWhileFiring) + .property("AdjustsThrottleForWeight", &AEJetpack::GetAdjustsThrottleForWeight, &AEJetpack::SetAdjustsThrottleForWeight) + + .enum_("JetpackType")[luabind::value("Standard", AEJetpack::Standard), + luabind::value("JumpPack", AEJetpack::JumpPack)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, AHuman) { return ConcreteTypeLuaClassDefinition(AHuman, Actor) - .def(luabind::constructor<>()) - - .property("Head", &AHuman::GetHead, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetHead) - .property("Jetpack", &AHuman::GetJetpack, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetJetpack) - .property("FGArm", &AHuman::GetFGArm, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetFGArm) - .property("BGArm", &AHuman::GetBGArm, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetBGArm) - .property("FGLeg", &AHuman::GetFGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetFGLeg) - .property("BGLeg", &AHuman::GetBGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetBGLeg) - .property("FGFoot", &AHuman::GetFGFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetFGFoot) - .property("BGFoot", &AHuman::GetBGFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetBGFoot) - .property("MaxWalkPathCrouchShift", &AHuman::GetMaxWalkPathCrouchShift, &AHuman::SetMaxWalkPathCrouchShift) - .property("MaxCrouchRotation", &AHuman::GetMaxCrouchRotation, &AHuman::SetMaxCrouchRotation) - .property("CrouchAmount", &AHuman::GetCrouchAmount) - .property("CrouchAmountOverride", &AHuman::GetCrouchAmountOverride, &AHuman::SetCrouchAmountOverride) - .property("StrideSound", &AHuman::GetStrideSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetStrideSound) - .property("UpperBodyState", &AHuman::GetUpperBodyState, &AHuman::SetUpperBodyState) - .property("MovementState", &AHuman::GetMovementState, &AHuman::SetMovementState) - .property("ProneState", &AHuman::GetProneState, &AHuman::SetProneState) - .property("ThrowPrepTime", &AHuman::GetThrowPrepTime, &AHuman::SetThrowPrepTime) - .property("ThrowProgress", &AHuman::GetThrowProgress) - .property("EquippedItem", &AHuman::GetEquippedItem) - .property("EquippedBGItem", &AHuman::GetEquippedBGItem) - .property("EquippedMass", &AHuman::GetEquippedMass) - .property("FirearmIsReady", &AHuman::FirearmIsReady) - .property("ThrowableIsReady", &AHuman::ThrowableIsReady) - .property("FirearmIsEmpty", &AHuman::FirearmIsEmpty) - .property("FirearmNeedsReload", &AHuman::FirearmNeedsReload) - .property("FirearmIsSemiAuto", &AHuman::FirearmIsSemiAuto) - .property("FirearmActivationDelay", &AHuman::FirearmActivationDelay) - .property("LimbPathPushForce", &AHuman::GetLimbPathPushForce, &AHuman::SetLimbPathPushForce) - .property("IsClimbing", &AHuman::IsClimbing) - .property("StrideFrame", &AHuman::StrideFrame) - .property("ArmSwingRate", &AHuman::GetArmSwingRate, &AHuman::SetArmSwingRate) - .property("DeviceArmSwayRate", &AHuman::GetDeviceArmSwayRate, &AHuman::SetDeviceArmSwayRate) - - .def("EquipFirearm", &AHuman::EquipFirearm) - .def("EquipThrowable", &AHuman::EquipThrowable) - .def("EquipDiggingTool", &AHuman::EquipDiggingTool) - .def("EquipShield", &AHuman::EquipShield) - .def("EquipShieldInBGArm", &AHuman::EquipShieldInBGArm) - .def("EquipDeviceInGroup", &AHuman::EquipDeviceInGroup) - .def("EquipNamedDevice", (bool (AHuman::*)(const std::string &, bool))&AHuman::EquipNamedDevice) - .def("EquipNamedDevice", (bool (AHuman::*)(const std::string &, const std::string &, bool))&AHuman::EquipNamedDevice) - .def("EquipLoadedFirearmInGroup", &AHuman::EquipLoadedFirearmInGroup) - .def("UnequipFGArm", &AHuman::UnequipFGArm) - .def("UnequipBGArm", &AHuman::UnequipBGArm) - .def("UnequipArms", &AHuman::UnequipArms) - .def("ReloadFirearms", &LuaAdaptersAHuman::ReloadFirearms) - .def("ReloadFirearms", &AHuman::ReloadFirearms) - .def("FirearmsAreReloading", &AHuman::FirearmsAreReloading) - .def("IsWithinRange", &AHuman::IsWithinRange) - .def("Look", &AHuman::Look) - .def("LookForGold", &AHuman::LookForGold) - .def("LookForMOs", &AHuman::LookForMOs) - .def("GetLimbPath", &AHuman::GetLimbPath) - .def("GetLimbPathSpeed", &AHuman::GetLimbPathSpeed) - .def("SetLimbPathSpeed", &AHuman::SetLimbPathSpeed) - .def("GetRotAngleTarget", &AHuman::GetRotAngleTarget) - .def("SetRotAngleTarget", &AHuman::SetRotAngleTarget) - .def("GetWalkAngle", &AHuman::GetWalkAngle) - .def("SetWalkAngle", &AHuman::SetWalkAngle) - - .enum_("UpperBodyState")[ - luabind::value("WEAPON_READY", AHuman::UpperBodyState::WEAPON_READY), - luabind::value("AIMING_SHARP", AHuman::UpperBodyState::AIMING_SHARP), - luabind::value("HOLSTERING_BACK", AHuman::UpperBodyState::HOLSTERING_BACK), - luabind::value("HOLSTERING_BELT", AHuman::UpperBodyState::HOLSTERING_BELT), - luabind::value("DEHOLSTERING_BACK", AHuman::UpperBodyState::DEHOLSTERING_BACK), - luabind::value("DEHOLSTERING_BELT", AHuman::UpperBodyState::DEHOLSTERING_BELT), - luabind::value("THROWING_PREP", AHuman::UpperBodyState::THROWING_PREP), - luabind::value("THROWING_RELEASE",AHuman::UpperBodyState::THROWING_RELEASE) - ] - .enum_("ProneState")[ - luabind::value("NOTPRONE", AHuman::ProneState::NOTPRONE), - luabind::value("GOPRONE", AHuman::ProneState::GOPRONE), - luabind::value("PRONE", AHuman::ProneState::PRONE), - luabind::value("PRONESTATECOUNT", AHuman::ProneState::PRONESTATECOUNT) - ] - .enum_("Layer")[ - luabind::value("FGROUND", AHuman::Layer::FGROUND), - luabind::value("BGROUND", AHuman::Layer::BGROUND) - ] - .enum_("DeviceHandlingState")[ - luabind::value("STILL", AHuman::DeviceHandlingState::STILL), - luabind::value("POINTING", AHuman::DeviceHandlingState::POINTING), - luabind::value("SCANNING", AHuman::DeviceHandlingState::SCANNING), - luabind::value("AIMING", AHuman::DeviceHandlingState::AIMING), - luabind::value("FIRING", AHuman::DeviceHandlingState::FIRING), - luabind::value("THROWING", AHuman::DeviceHandlingState::THROWING), - luabind::value("DIGGING", AHuman::DeviceHandlingState::DIGGING) - ] - .enum_("SweepState")[ - luabind::value("NOSWEEP", AHuman::SweepState::NOSWEEP), - luabind::value("SWEEPINGUP", AHuman::SweepState::SWEEPINGUP), - luabind::value("SWEEPUPPAUSE", AHuman::SweepState::SWEEPUPPAUSE), - luabind::value("SWEEPINGDOWN", AHuman::SweepState::SWEEPINGDOWN), - luabind::value("SWEEPDOWNPAUSE", AHuman::SweepState::SWEEPDOWNPAUSE) - ] - .enum_("DigState")[ - luabind::value("NOTDIGGING", AHuman::DigState::NOTDIGGING), - luabind::value("PREDIG", AHuman::DigState::PREDIG), - luabind::value("STARTDIG", AHuman::DigState::STARTDIG), - luabind::value("TUNNELING", AHuman::DigState::TUNNELING), - luabind::value("FINISHINGDIG", AHuman::DigState::FINISHINGDIG), - luabind::value("PAUSEDIGGER", AHuman::DigState::PAUSEDIGGER) - ] - .enum_("JumpState")[ - luabind::value("NOTJUMPING", AHuman::JumpState::NOTJUMPING), - luabind::value("FORWARDJUMP", AHuman::JumpState::FORWARDJUMP), - luabind::value("PREJUMP", AHuman::JumpState::PREUPJUMP), - luabind::value("UPJUMP", AHuman::JumpState::UPJUMP), - luabind::value("APEXJUMP", AHuman::JumpState::APEXJUMP), - luabind::value("LANDJUMP", AHuman::JumpState::LANDJUMP) - ]; + .def(luabind::constructor<>()) + + .property("Head", &AHuman::GetHead, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetHead) + .property("Jetpack", &AHuman::GetJetpack, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetJetpack) + .property("FGArm", &AHuman::GetFGArm, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetFGArm) + .property("BGArm", &AHuman::GetBGArm, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetBGArm) + .property("FGLeg", &AHuman::GetFGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetFGLeg) + .property("BGLeg", &AHuman::GetBGLeg, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetBGLeg) + .property("FGFoot", &AHuman::GetFGFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetFGFoot) + .property("BGFoot", &AHuman::GetBGFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetBGFoot) + .property("MaxWalkPathCrouchShift", &AHuman::GetMaxWalkPathCrouchShift, &AHuman::SetMaxWalkPathCrouchShift) + .property("MaxCrouchRotation", &AHuman::GetMaxCrouchRotation, &AHuman::SetMaxCrouchRotation) + .property("CrouchAmount", &AHuman::GetCrouchAmount) + .property("CrouchAmountOverride", &AHuman::GetCrouchAmountOverride, &AHuman::SetCrouchAmountOverride) + .property("StrideSound", &AHuman::GetStrideSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetStrideSound) + .property("UpperBodyState", &AHuman::GetUpperBodyState, &AHuman::SetUpperBodyState) + .property("MovementState", &AHuman::GetMovementState, &AHuman::SetMovementState) + .property("ProneState", &AHuman::GetProneState, &AHuman::SetProneState) + .property("ThrowPrepTime", &AHuman::GetThrowPrepTime, &AHuman::SetThrowPrepTime) + .property("ThrowProgress", &AHuman::GetThrowProgress) + .property("EquippedItem", &AHuman::GetEquippedItem) + .property("EquippedBGItem", &AHuman::GetEquippedBGItem) + .property("EquippedMass", &AHuman::GetEquippedMass) + .property("FirearmIsReady", &AHuman::FirearmIsReady) + .property("ThrowableIsReady", &AHuman::ThrowableIsReady) + .property("FirearmIsEmpty", &AHuman::FirearmIsEmpty) + .property("FirearmNeedsReload", &AHuman::FirearmNeedsReload) + .property("FirearmIsSemiAuto", &AHuman::FirearmIsSemiAuto) + .property("FirearmActivationDelay", &AHuman::FirearmActivationDelay) + .property("LimbPathPushForce", &AHuman::GetLimbPathPushForce, &AHuman::SetLimbPathPushForce) + .property("IsClimbing", &AHuman::IsClimbing) + .property("StrideFrame", &AHuman::StrideFrame) + .property("ArmSwingRate", &AHuman::GetArmSwingRate, &AHuman::SetArmSwingRate) + .property("DeviceArmSwayRate", &AHuman::GetDeviceArmSwayRate, &AHuman::SetDeviceArmSwayRate) + + .def("EquipFirearm", &AHuman::EquipFirearm) + .def("EquipThrowable", &AHuman::EquipThrowable) + .def("EquipDiggingTool", &AHuman::EquipDiggingTool) + .def("EquipShield", &AHuman::EquipShield) + .def("EquipShieldInBGArm", &AHuman::EquipShieldInBGArm) + .def("EquipDeviceInGroup", &AHuman::EquipDeviceInGroup) + .def("EquipNamedDevice", (bool(AHuman::*)(const std::string&, bool)) & AHuman::EquipNamedDevice) + .def("EquipNamedDevice", (bool(AHuman::*)(const std::string&, const std::string&, bool)) & AHuman::EquipNamedDevice) + .def("EquipLoadedFirearmInGroup", &AHuman::EquipLoadedFirearmInGroup) + .def("UnequipFGArm", &AHuman::UnequipFGArm) + .def("UnequipBGArm", &AHuman::UnequipBGArm) + .def("UnequipArms", &AHuman::UnequipArms) + .def("ReloadFirearms", &LuaAdaptersAHuman::ReloadFirearms) + .def("ReloadFirearms", &AHuman::ReloadFirearms) + .def("FirearmsAreReloading", &AHuman::FirearmsAreReloading) + .def("IsWithinRange", &AHuman::IsWithinRange) + .def("Look", &AHuman::Look) + .def("LookForGold", &AHuman::LookForGold) + .def("LookForMOs", &AHuman::LookForMOs) + .def("GetLimbPath", &AHuman::GetLimbPath) + .def("GetLimbPathSpeed", &AHuman::GetLimbPathSpeed) + .def("SetLimbPathSpeed", &AHuman::SetLimbPathSpeed) + .def("GetRotAngleTarget", &AHuman::GetRotAngleTarget) + .def("SetRotAngleTarget", &AHuman::SetRotAngleTarget) + .def("GetWalkAngle", &AHuman::GetWalkAngle) + .def("SetWalkAngle", &AHuman::SetWalkAngle) + + .enum_("UpperBodyState")[luabind::value("WEAPON_READY", AHuman::UpperBodyState::WEAPON_READY), + luabind::value("AIMING_SHARP", AHuman::UpperBodyState::AIMING_SHARP), + luabind::value("HOLSTERING_BACK", AHuman::UpperBodyState::HOLSTERING_BACK), + luabind::value("HOLSTERING_BELT", AHuman::UpperBodyState::HOLSTERING_BELT), + luabind::value("DEHOLSTERING_BACK", AHuman::UpperBodyState::DEHOLSTERING_BACK), + luabind::value("DEHOLSTERING_BELT", AHuman::UpperBodyState::DEHOLSTERING_BELT), + luabind::value("THROWING_PREP", AHuman::UpperBodyState::THROWING_PREP), + luabind::value("THROWING_RELEASE", AHuman::UpperBodyState::THROWING_RELEASE)] + .enum_("ProneState")[luabind::value("NOTPRONE", AHuman::ProneState::NOTPRONE), + luabind::value("GOPRONE", AHuman::ProneState::GOPRONE), + luabind::value("PRONE", AHuman::ProneState::PRONE), + luabind::value("PRONESTATECOUNT", AHuman::ProneState::PRONESTATECOUNT)] + .enum_("Layer")[luabind::value("FGROUND", AHuman::Layer::FGROUND), + luabind::value("BGROUND", AHuman::Layer::BGROUND)] + .enum_("DeviceHandlingState")[luabind::value("STILL", AHuman::DeviceHandlingState::STILL), + luabind::value("POINTING", AHuman::DeviceHandlingState::POINTING), + luabind::value("SCANNING", AHuman::DeviceHandlingState::SCANNING), + luabind::value("AIMING", AHuman::DeviceHandlingState::AIMING), + luabind::value("FIRING", AHuman::DeviceHandlingState::FIRING), + luabind::value("THROWING", AHuman::DeviceHandlingState::THROWING), + luabind::value("DIGGING", AHuman::DeviceHandlingState::DIGGING)] + .enum_("SweepState")[luabind::value("NOSWEEP", AHuman::SweepState::NOSWEEP), + luabind::value("SWEEPINGUP", AHuman::SweepState::SWEEPINGUP), + luabind::value("SWEEPUPPAUSE", AHuman::SweepState::SWEEPUPPAUSE), + luabind::value("SWEEPINGDOWN", AHuman::SweepState::SWEEPINGDOWN), + luabind::value("SWEEPDOWNPAUSE", AHuman::SweepState::SWEEPDOWNPAUSE)] + .enum_("DigState")[luabind::value("NOTDIGGING", AHuman::DigState::NOTDIGGING), + luabind::value("PREDIG", AHuman::DigState::PREDIG), + luabind::value("STARTDIG", AHuman::DigState::STARTDIG), + luabind::value("TUNNELING", AHuman::DigState::TUNNELING), + luabind::value("FINISHINGDIG", AHuman::DigState::FINISHINGDIG), + luabind::value("PAUSEDIGGER", AHuman::DigState::PAUSEDIGGER)] + .enum_("JumpState")[luabind::value("NOTJUMPING", AHuman::JumpState::NOTJUMPING), + luabind::value("FORWARDJUMP", AHuman::JumpState::FORWARDJUMP), + luabind::value("PREJUMP", AHuman::JumpState::PREUPJUMP), + luabind::value("UPJUMP", AHuman::JumpState::UPJUMP), + luabind::value("APEXJUMP", AHuman::JumpState::APEXJUMP), + luabind::value("LANDJUMP", AHuman::JumpState::LANDJUMP)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Arm) { return ConcreteTypeLuaClassDefinition(Arm, Attachable) - .property("MaxLength", &Arm::GetMaxLength) - .property("MoveSpeed", &Arm::GetMoveSpeed, &Arm::SetMoveSpeed) + .property("MaxLength", &Arm::GetMaxLength) + .property("MoveSpeed", &Arm::GetMoveSpeed, &Arm::SetMoveSpeed) - .property("HandIdleOffset", &Arm::GetHandIdleOffset, &Arm::SetHandIdleOffset) + .property("HandIdleOffset", &Arm::GetHandIdleOffset, &Arm::SetHandIdleOffset) - .property("HandPos", &Arm::GetHandPos, &Arm::SetHandPos) - .property("HasAnyHandTargets", &Arm::HasAnyHandTargets) - .property("NumberOfHandTargets", &Arm::GetNumberOfHandTargets) - .property("NextHandTargetDescription", &Arm::GetNextHandTargetDescription) - .property("NextHandTargetPosition", &Arm::GetNextHandTargetPosition) - .property("HandHasReachedCurrentTarget", &Arm::GetHandHasReachedCurrentTarget) + .property("HandPos", &Arm::GetHandPos, &Arm::SetHandPos) + .property("HasAnyHandTargets", &Arm::HasAnyHandTargets) + .property("NumberOfHandTargets", &Arm::GetNumberOfHandTargets) + .property("NextHandTargetDescription", &Arm::GetNextHandTargetDescription) + .property("NextHandTargetPosition", &Arm::GetNextHandTargetPosition) + .property("HandHasReachedCurrentTarget", &Arm::GetHandHasReachedCurrentTarget) - .property("GripStrength", &Arm::GetGripStrength, &Arm::SetGripStrength) - .property("ThrowStrength", &Arm::GetThrowStrength, &Arm::SetThrowStrength) + .property("GripStrength", &Arm::GetGripStrength, &Arm::SetGripStrength) + .property("ThrowStrength", &Arm::GetThrowStrength, &Arm::SetThrowStrength) - .property("HeldDevice", &Arm::GetHeldDevice, &LuaAdaptersPropertyOwnershipSafetyFaker::ArmSetHeldDevice) - .property("SupportedHeldDevice", &Arm::GetHeldDeviceThisArmIsTryingToSupport) + .property("HeldDevice", &Arm::GetHeldDevice, &LuaAdaptersPropertyOwnershipSafetyFaker::ArmSetHeldDevice) + .property("SupportedHeldDevice", &Arm::GetHeldDeviceThisArmIsTryingToSupport) - .def("AddHandTarget", (void (Arm::*)(const std::string &description, const Vector &handTargetPositionToAdd))&Arm::AddHandTarget) - .def("AddHandTarget", (void (Arm::*)(const std::string &description, const Vector &handTargetPositionToAdd, float delayAtTarget))&Arm::AddHandTarget) - .def("RemoveNextHandTarget", &Arm::RemoveNextHandTarget) - .def("ClearHandTargets", &Arm::ClearHandTargets); + .def("AddHandTarget", (void(Arm::*)(const std::string& description, const Vector& handTargetPositionToAdd)) & Arm::AddHandTarget) + .def("AddHandTarget", (void(Arm::*)(const std::string& description, const Vector& handTargetPositionToAdd, float delayAtTarget)) & Arm::AddHandTarget) + .def("RemoveNextHandTarget", &Arm::RemoveNextHandTarget) + .def("ClearHandTargets", &Arm::ClearHandTargets); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Attachable) { return ConcreteTypeLuaClassDefinition(Attachable, MOSRotating) - .property("ParentOffset", &Attachable::GetParentOffset, &Attachable::SetParentOffset) - .property("JointStrength", &Attachable::GetJointStrength, &Attachable::SetJointStrength) - .property("JointStiffness", &Attachable::GetJointStiffness, &Attachable::SetJointStiffness) - .property("JointOffset", &Attachable::GetJointOffset, &Attachable::SetJointOffset) - .property("JointPos", &Attachable::GetJointPos) - .property("DeleteWhenRemovedFromParent", &Attachable::GetDeleteWhenRemovedFromParent, &Attachable::SetDeleteWhenRemovedFromParent) - .property("GibWhenRemovedFromParent", &Attachable::GetGibWhenRemovedFromParent, &Attachable::SetGibWhenRemovedFromParent) - .property("ApplyTransferredForcesAtOffset", &Attachable::GetApplyTransferredForcesAtOffset, &Attachable::SetApplyTransferredForcesAtOffset) - .property("BreakWound", &Attachable::GetBreakWound, &LuaAdaptersPropertyOwnershipSafetyFaker::AttachableSetBreakWound) - .property("ParentBreakWound", &Attachable::GetParentBreakWound, &LuaAdaptersPropertyOwnershipSafetyFaker::AttachableSetParentBreakWound) - .property("InheritsHFlipped", &Attachable::InheritsHFlipped, &Attachable::SetInheritsHFlipped) - .property("InheritsRotAngle", &Attachable::InheritsRotAngle, &Attachable::SetInheritsRotAngle) - .property("InheritedRotAngleOffset", &Attachable::GetInheritedRotAngleOffset, &Attachable::SetInheritedRotAngleOffset) - .property("AtomSubgroupID", &Attachable::GetAtomSubgroupID) - .property("CollidesWithTerrainWhileAttached", &Attachable::GetCollidesWithTerrainWhileAttached, &Attachable::SetCollidesWithTerrainWhileAttached) - .property("IgnoresParticlesWhileAttached", &Attachable::GetIgnoresParticlesWhileAttached, &Attachable::SetIgnoresParticlesWhileAttached) - .property("CanCollideWithTerrain", &Attachable::CanCollideWithTerrain) - .property("DrawnAfterParent", &Attachable::IsDrawnAfterParent, &Attachable::SetDrawnAfterParent) - .property("InheritsFrame", &Attachable::InheritsFrame, &Attachable::SetInheritsFrame) - - .def("IsAttached", &Attachable::IsAttached) - .def("IsAttachedTo", &Attachable::IsAttachedTo) - - .def("RemoveFromParent", &LuaAdaptersAttachable::RemoveFromParent1, luabind::adopt(luabind::return_value)) - .def("RemoveFromParent", &LuaAdaptersAttachable::RemoveFromParent2, luabind::adopt(luabind::return_value)); + .property("ParentOffset", &Attachable::GetParentOffset, &Attachable::SetParentOffset) + .property("JointStrength", &Attachable::GetJointStrength, &Attachable::SetJointStrength) + .property("JointStiffness", &Attachable::GetJointStiffness, &Attachable::SetJointStiffness) + .property("JointOffset", &Attachable::GetJointOffset, &Attachable::SetJointOffset) + .property("JointPos", &Attachable::GetJointPos) + .property("DeleteWhenRemovedFromParent", &Attachable::GetDeleteWhenRemovedFromParent, &Attachable::SetDeleteWhenRemovedFromParent) + .property("GibWhenRemovedFromParent", &Attachable::GetGibWhenRemovedFromParent, &Attachable::SetGibWhenRemovedFromParent) + .property("ApplyTransferredForcesAtOffset", &Attachable::GetApplyTransferredForcesAtOffset, &Attachable::SetApplyTransferredForcesAtOffset) + .property("BreakWound", &Attachable::GetBreakWound, &LuaAdaptersPropertyOwnershipSafetyFaker::AttachableSetBreakWound) + .property("ParentBreakWound", &Attachable::GetParentBreakWound, &LuaAdaptersPropertyOwnershipSafetyFaker::AttachableSetParentBreakWound) + .property("InheritsHFlipped", &Attachable::InheritsHFlipped, &Attachable::SetInheritsHFlipped) + .property("InheritsRotAngle", &Attachable::InheritsRotAngle, &Attachable::SetInheritsRotAngle) + .property("InheritedRotAngleOffset", &Attachable::GetInheritedRotAngleOffset, &Attachable::SetInheritedRotAngleOffset) + .property("AtomSubgroupID", &Attachable::GetAtomSubgroupID) + .property("CollidesWithTerrainWhileAttached", &Attachable::GetCollidesWithTerrainWhileAttached, &Attachable::SetCollidesWithTerrainWhileAttached) + .property("IgnoresParticlesWhileAttached", &Attachable::GetIgnoresParticlesWhileAttached, &Attachable::SetIgnoresParticlesWhileAttached) + .property("CanCollideWithTerrain", &Attachable::CanCollideWithTerrain) + .property("DrawnAfterParent", &Attachable::IsDrawnAfterParent, &Attachable::SetDrawnAfterParent) + .property("InheritsFrame", &Attachable::InheritsFrame, &Attachable::SetInheritsFrame) + + .def("IsAttached", &Attachable::IsAttached) + .def("IsAttachedTo", &Attachable::IsAttachedTo) + + .def("RemoveFromParent", &LuaAdaptersAttachable::RemoveFromParent1, luabind::adopt(luabind::return_value)) + .def("RemoveFromParent", &LuaAdaptersAttachable::RemoveFromParent2, luabind::adopt(luabind::return_value)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Deployment) { return AbstractTypeLuaClassDefinition(Deployment, SceneObject) - .property("ID", &Deployment::GetID) - .property("HFlipped", &Deployment::IsHFlipped) - .property("SpawnRadius", &Deployment::GetSpawnRadius) + .property("ID", &Deployment::GetID) + .property("HFlipped", &Deployment::IsHFlipped) + .property("SpawnRadius", &Deployment::GetSpawnRadius) - .def("GetLoadoutName", &Deployment::GetLoadoutName) - .def("CreateDeployedActor", (Actor * (Deployment::*)())&Deployment::CreateDeployedActor, luabind::adopt(luabind::result)) - .def("CreateDeployedObject", (SceneObject * (Deployment::*)())&Deployment::CreateDeployedObject, luabind::adopt(luabind::result)); + .def("GetLoadoutName", &Deployment::GetLoadoutName) + .def("CreateDeployedActor", (Actor * (Deployment::*)()) & Deployment::CreateDeployedActor, luabind::adopt(luabind::result)) + .def("CreateDeployedObject", (SceneObject * (Deployment::*)()) & Deployment::CreateDeployedObject, luabind::adopt(luabind::result)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Emission) { return AbstractTypeLuaClassDefinition(Emission, Entity) - .property("ParticlesPerMinute", &Emission::GetRate, &Emission::SetRate) - .property("MinVelocity", &Emission::GetMinVelocity, &Emission::SetMinVelocity) - .property("MaxVelocity", &Emission::GetMaxVelocity, &Emission::SetMaxVelocity) - .property("PushesEmitter", &Emission::PushesEmitter, &Emission::SetPushesEmitter) - .property("LifeVariation", &Emission::GetLifeVariation, &Emission::SetLifeVariation) - .property("BurstSize", &Emission::GetBurstSize, &Emission::SetBurstSize) - .property("Spread", &Emission::GetSpread, &Emission::SetSpread) - .property("Offset", &Emission::GetOffset, &Emission::SetOffset) + .property("ParticlesPerMinute", &Emission::GetRate, &Emission::SetRate) + .property("MinVelocity", &Emission::GetMinVelocity, &Emission::SetMinVelocity) + .property("MaxVelocity", &Emission::GetMaxVelocity, &Emission::SetMaxVelocity) + .property("PushesEmitter", &Emission::PushesEmitter, &Emission::SetPushesEmitter) + .property("LifeVariation", &Emission::GetLifeVariation, &Emission::SetLifeVariation) + .property("BurstSize", &Emission::GetBurstSize, &Emission::SetBurstSize) + .property("Spread", &Emission::GetSpread, &Emission::SetSpread) + .property("Offset", &Emission::GetOffset, &Emission::SetOffset) - .def("ResetEmissionTimers", &Emission::ResetEmissionTimers); + .def("ResetEmissionTimers", &Emission::ResetEmissionTimers); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Gib) { return luabind::class_("Gib") - .property("ParticlePreset", &Gib::GetParticlePreset, &Gib::SetParticlePreset) - .property("MinVelocity", &Gib::GetMinVelocity, &Gib::SetMinVelocity) - .property("MaxVelocity", &Gib::GetMaxVelocity, &Gib::SetMaxVelocity) - .property("SpreadMode", &Gib::GetSpreadMode, &Gib::SetSpreadMode) - - .def_readwrite("Offset", &Gib::m_Offset) - .def_readwrite("Count", &Gib::m_Count) - .def_readwrite("Spread", &Gib::m_Spread) - .def_readwrite("LifeVariation", &Gib::m_LifeVariation) - .def_readwrite("InheritsVel", &Gib::m_InheritsVel) - .def_readwrite("IgnoresTeamHits", &Gib::m_IgnoresTeamHits) - - .enum_("SpreadMode")[ - luabind::value("SpreadRandom", Gib::SpreadMode::SpreadRandom), - luabind::value("SpreadEven", Gib::SpreadMode::SpreadEven), - luabind::value("SpreadSpiral", Gib::SpreadMode::SpreadSpiral) - ]; + .property("ParticlePreset", &Gib::GetParticlePreset, &Gib::SetParticlePreset) + .property("MinVelocity", &Gib::GetMinVelocity, &Gib::SetMinVelocity) + .property("MaxVelocity", &Gib::GetMaxVelocity, &Gib::SetMaxVelocity) + .property("SpreadMode", &Gib::GetSpreadMode, &Gib::SetSpreadMode) + + .def_readwrite("Offset", &Gib::m_Offset) + .def_readwrite("Count", &Gib::m_Count) + .def_readwrite("Spread", &Gib::m_Spread) + .def_readwrite("LifeVariation", &Gib::m_LifeVariation) + .def_readwrite("InheritsVel", &Gib::m_InheritsVel) + .def_readwrite("IgnoresTeamHits", &Gib::m_IgnoresTeamHits) + + .enum_("SpreadMode")[luabind::value("SpreadRandom", Gib::SpreadMode::SpreadRandom), + luabind::value("SpreadEven", Gib::SpreadMode::SpreadEven), + luabind::value("SpreadSpiral", Gib::SpreadMode::SpreadSpiral)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, GlobalScript) { return AbstractTypeLuaClassDefinition(GlobalScript, Entity) - .def("Deactivate", &LuaAdaptersGlobalScript::Deactivate); + .def("Deactivate", &LuaAdaptersGlobalScript::Deactivate); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, HDFirearm) { return ConcreteTypeLuaClassDefinition(HDFirearm, HeldDevice) - .property("ReloadEndOffset", &HDFirearm::GetReloadEndOffset, &HDFirearm::SetReloadEndOffset) - .property("RateOfFire", &HDFirearm::GetRateOfFire, &HDFirearm::SetRateOfFire) - .property("MSPerRound", &HDFirearm::GetMSPerRound) - .property("FullAuto", &HDFirearm::IsFullAuto, &HDFirearm::SetFullAuto) - .property("Reloadable", &HDFirearm::IsReloadable, &HDFirearm::SetReloadable) - .property("DualReloadable", &HDFirearm::IsDualReloadable, &HDFirearm::SetDualReloadable) - .property("OneHandedReloadTimeMultiplier", &HDFirearm::GetOneHandedReloadTimeMultiplier, &HDFirearm::SetOneHandedReloadTimeMultiplier) - .property("ReloadAngle", &HDFirearm::GetReloadAngle, &HDFirearm::SetReloadAngle) - .property("OneHandedReloadAngle", &HDFirearm::GetOneHandedReloadAngle, &HDFirearm::SetOneHandedReloadAngle) - .property("CurrentReloadAngle", &HDFirearm::GetCurrentReloadAngle) - .property("RoundInMagCount", &HDFirearm::GetRoundInMagCount) - .property("RoundInMagCapacity", &HDFirearm::GetRoundInMagCapacity) - .property("Magazine", &HDFirearm::GetMagazine, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetMagazine) - .property("Flash", &HDFirearm::GetFlash, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetFlash) - .property("PreFireSound", &HDFirearm::GetPreFireSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetPreFireSound) - .property("FireSound", &HDFirearm::GetFireSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetFireSound) - .property("FireEchoSound", &HDFirearm::GetFireEchoSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetFireEchoSound) - .property("ActiveSound", &HDFirearm::GetActiveSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetActiveSound) - .property("DeactivationSound", &HDFirearm::GetDeactivationSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetDeactivationSound) - .property("EmptySound", &HDFirearm::GetEmptySound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetEmptySound) - .property("ReloadStartSound", &HDFirearm::GetReloadStartSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetReloadStartSound) - .property("ReloadEndSound", &HDFirearm::GetReloadEndSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetReloadEndSound) - .property("ActivationDelay", &HDFirearm::GetActivationDelay, &HDFirearm::SetActivationDelay) - .property("DeactivationDelay", &HDFirearm::GetDeactivationDelay, &HDFirearm::SetDeactivationDelay) - .property("BaseReloadTime", &HDFirearm::GetBaseReloadTime, &HDFirearm::SetBaseReloadTime) - .property("ReloadTime", &HDFirearm::GetReloadTime) - .property("ReloadProgress", &HDFirearm::GetReloadProgress) - .property("ShakeRange", &HDFirearm::GetShakeRange, &HDFirearm::SetShakeRange) - .property("SharpShakeRange", &HDFirearm::GetSharpShakeRange, &HDFirearm::SetSharpShakeRange) - .property("NoSupportFactor", &HDFirearm::GetNoSupportFactor, &HDFirearm::SetNoSupportFactor) - .property("ParticleSpreadRange", &HDFirearm::GetParticleSpreadRange, &HDFirearm::SetParticleSpreadRange) - .property("ShellVelVariation", &HDFirearm::GetShellVelVariation, &HDFirearm::SetShellVelVariation) - .property("FiredOnce", &HDFirearm::FiredOnce) - .property("FiredFrame", &HDFirearm::FiredFrame) - .property("CanFire", &HDFirearm::CanFire) - .property("RoundsFired", &HDFirearm::RoundsFired) - .property("IsAnimatedManually", &HDFirearm::IsAnimatedManually, &HDFirearm::SetAnimatedManually) - .property("RecoilTransmission", &HDFirearm::GetJointStiffness, &HDFirearm::SetJointStiffness) - - .def("GetAIFireVel", &HDFirearm::GetAIFireVel) - .def("GetAIBulletLifeTime", &HDFirearm::GetAIBulletLifeTime) - .def("GetBulletAccScalar", &HDFirearm::GetBulletAccScalar) - .def("GetAIBlastRadius", &HDFirearm::GetAIBlastRadius) - .def("GetAIPenetration", &HDFirearm::GetAIPenetration) - .def("CompareTrajectories", &HDFirearm::CompareTrajectories) - .def("GetNextMagazineName", &HDFirearm::GetNextMagazineName) - .def("SetNextMagazineName", &HDFirearm::SetNextMagazineName); + .property("ReloadEndOffset", &HDFirearm::GetReloadEndOffset, &HDFirearm::SetReloadEndOffset) + .property("RateOfFire", &HDFirearm::GetRateOfFire, &HDFirearm::SetRateOfFire) + .property("MSPerRound", &HDFirearm::GetMSPerRound) + .property("FullAuto", &HDFirearm::IsFullAuto, &HDFirearm::SetFullAuto) + .property("Reloadable", &HDFirearm::IsReloadable, &HDFirearm::SetReloadable) + .property("DualReloadable", &HDFirearm::IsDualReloadable, &HDFirearm::SetDualReloadable) + .property("OneHandedReloadTimeMultiplier", &HDFirearm::GetOneHandedReloadTimeMultiplier, &HDFirearm::SetOneHandedReloadTimeMultiplier) + .property("ReloadAngle", &HDFirearm::GetReloadAngle, &HDFirearm::SetReloadAngle) + .property("OneHandedReloadAngle", &HDFirearm::GetOneHandedReloadAngle, &HDFirearm::SetOneHandedReloadAngle) + .property("CurrentReloadAngle", &HDFirearm::GetCurrentReloadAngle) + .property("RoundInMagCount", &HDFirearm::GetRoundInMagCount) + .property("RoundInMagCapacity", &HDFirearm::GetRoundInMagCapacity) + .property("Magazine", &HDFirearm::GetMagazine, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetMagazine) + .property("Flash", &HDFirearm::GetFlash, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetFlash) + .property("PreFireSound", &HDFirearm::GetPreFireSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetPreFireSound) + .property("FireSound", &HDFirearm::GetFireSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetFireSound) + .property("FireEchoSound", &HDFirearm::GetFireEchoSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetFireEchoSound) + .property("ActiveSound", &HDFirearm::GetActiveSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetActiveSound) + .property("DeactivationSound", &HDFirearm::GetDeactivationSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetDeactivationSound) + .property("EmptySound", &HDFirearm::GetEmptySound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetEmptySound) + .property("ReloadStartSound", &HDFirearm::GetReloadStartSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetReloadStartSound) + .property("ReloadEndSound", &HDFirearm::GetReloadEndSound, &LuaAdaptersPropertyOwnershipSafetyFaker::HDFirearmSetReloadEndSound) + .property("ActivationDelay", &HDFirearm::GetActivationDelay, &HDFirearm::SetActivationDelay) + .property("DeactivationDelay", &HDFirearm::GetDeactivationDelay, &HDFirearm::SetDeactivationDelay) + .property("BaseReloadTime", &HDFirearm::GetBaseReloadTime, &HDFirearm::SetBaseReloadTime) + .property("ReloadTime", &HDFirearm::GetReloadTime) + .property("ReloadProgress", &HDFirearm::GetReloadProgress) + .property("ShakeRange", &HDFirearm::GetShakeRange, &HDFirearm::SetShakeRange) + .property("SharpShakeRange", &HDFirearm::GetSharpShakeRange, &HDFirearm::SetSharpShakeRange) + .property("NoSupportFactor", &HDFirearm::GetNoSupportFactor, &HDFirearm::SetNoSupportFactor) + .property("ParticleSpreadRange", &HDFirearm::GetParticleSpreadRange, &HDFirearm::SetParticleSpreadRange) + .property("ShellVelVariation", &HDFirearm::GetShellVelVariation, &HDFirearm::SetShellVelVariation) + .property("FiredOnce", &HDFirearm::FiredOnce) + .property("FiredFrame", &HDFirearm::FiredFrame) + .property("CanFire", &HDFirearm::CanFire) + .property("RoundsFired", &HDFirearm::RoundsFired) + .property("IsAnimatedManually", &HDFirearm::IsAnimatedManually, &HDFirearm::SetAnimatedManually) + .property("RecoilTransmission", &HDFirearm::GetJointStiffness, &HDFirearm::SetJointStiffness) + + .def("GetAIFireVel", &HDFirearm::GetAIFireVel) + .def("GetAIBulletLifeTime", &HDFirearm::GetAIBulletLifeTime) + .def("GetBulletAccScalar", &HDFirearm::GetBulletAccScalar) + .def("GetAIBlastRadius", &HDFirearm::GetAIBlastRadius) + .def("GetAIPenetration", &HDFirearm::GetAIPenetration) + .def("CompareTrajectories", &HDFirearm::CompareTrajectories) + .def("GetNextMagazineName", &HDFirearm::GetNextMagazineName) + .def("SetNextMagazineName", &HDFirearm::SetNextMagazineName); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, HeldDevice) { return ConcreteTypeLuaClassDefinition(HeldDevice, Attachable) - .property("SupportPos", &HeldDevice::GetSupportPos) - .property("MagazinePos", &HeldDevice::GetMagazinePos) - .property("MuzzlePos", &HeldDevice::GetMuzzlePos) - .property("MuzzleOffset", &HeldDevice::GetMuzzleOffset, &HeldDevice::SetMuzzleOffset) - .property("StanceOffset", &HeldDevice::GetStanceOffset, &HeldDevice::SetStanceOffset) - .property("SharpStanceOffset", &HeldDevice::GetSharpStanceOffset, &HeldDevice::SetSharpStanceOffset) - .property("SharpLength", &HeldDevice::GetSharpLength, &HeldDevice::SetSharpLength) - .property("SharpLength", &HeldDevice::GetSharpLength, &HeldDevice::SetSharpLength) - .property("Supportable", &HeldDevice::IsSupportable, &HeldDevice::SetSupportable) - .property("SupportOffset", &HeldDevice::GetSupportOffset, &HeldDevice::SetSupportOffset) - .property("UseSupportOffsetWhileReloading", &HeldDevice::GetUseSupportOffsetWhileReloading, &HeldDevice::SetUseSupportOffsetWhileReloading) - .property("HasPickupLimitations", &HeldDevice::HasPickupLimitations) - .property("UnPickupable", &HeldDevice::IsUnPickupable, &HeldDevice::SetUnPickupable) - .property("GripStrengthMultiplier", &HeldDevice::GetGripStrengthMultiplier, &HeldDevice::SetGripStrengthMultiplier) - .property("Supported", &HeldDevice::GetSupported, &HeldDevice::SetSupported) - .property("GetsHitByMOsWhenHeld", &HeldDevice::GetsHitByMOsWhenHeld, &HeldDevice::SetGetsHitByMOsWhenHeld) - .property("VisualRecoilMultiplier", &HeldDevice::GetVisualRecoilMultiplier, &HeldDevice::SetVisualRecoilMultiplier) - - .def("IsBeingHeld", &HeldDevice::IsBeingHeld) - .def("IsWeapon", &HeldDevice::IsWeapon) - .def("IsTool", &HeldDevice::IsTool) - .def("IsShield", &HeldDevice::IsShield) - .def("IsDualWieldable", &HeldDevice::IsDualWieldable) - .def("SetDualWieldable", &HeldDevice::SetDualWieldable) - .def("IsOneHanded", &HeldDevice::IsOneHanded) - .def("SetOneHanded", &HeldDevice::SetOneHanded) - .def("Activate", &HeldDevice::Activate) - .def("Deactivate", &HeldDevice::Deactivate) - .def("Reload", &HeldDevice::Reload) - .def("IsActivated", &HeldDevice::IsActivated) - .def("IsReloading", &HeldDevice::IsReloading) - .def("DoneReloading", &HeldDevice::DoneReloading) - .def("NeedsReloading", &HeldDevice::NeedsReloading) - .def("IsFull", &HeldDevice::IsFull) - .def("IsEmpty", &HeldDevice::IsEmpty) - .def("IsPickupableBy", &HeldDevice::IsPickupableBy) - .def("AddPickupableByPresetName", &HeldDevice::AddPickupableByPresetName) - .def("RemovePickupableByPresetName", &HeldDevice::RemovePickupableByPresetName); + .property("SupportPos", &HeldDevice::GetSupportPos) + .property("MagazinePos", &HeldDevice::GetMagazinePos) + .property("MuzzlePos", &HeldDevice::GetMuzzlePos) + .property("MuzzleOffset", &HeldDevice::GetMuzzleOffset, &HeldDevice::SetMuzzleOffset) + .property("StanceOffset", &HeldDevice::GetStanceOffset, &HeldDevice::SetStanceOffset) + .property("SharpStanceOffset", &HeldDevice::GetSharpStanceOffset, &HeldDevice::SetSharpStanceOffset) + .property("SharpLength", &HeldDevice::GetSharpLength, &HeldDevice::SetSharpLength) + .property("SharpLength", &HeldDevice::GetSharpLength, &HeldDevice::SetSharpLength) + .property("Supportable", &HeldDevice::IsSupportable, &HeldDevice::SetSupportable) + .property("SupportOffset", &HeldDevice::GetSupportOffset, &HeldDevice::SetSupportOffset) + .property("UseSupportOffsetWhileReloading", &HeldDevice::GetUseSupportOffsetWhileReloading, &HeldDevice::SetUseSupportOffsetWhileReloading) + .property("HasPickupLimitations", &HeldDevice::HasPickupLimitations) + .property("UnPickupable", &HeldDevice::IsUnPickupable, &HeldDevice::SetUnPickupable) + .property("GripStrengthMultiplier", &HeldDevice::GetGripStrengthMultiplier, &HeldDevice::SetGripStrengthMultiplier) + .property("Supported", &HeldDevice::GetSupported, &HeldDevice::SetSupported) + .property("GetsHitByMOsWhenHeld", &HeldDevice::GetsHitByMOsWhenHeld, &HeldDevice::SetGetsHitByMOsWhenHeld) + .property("VisualRecoilMultiplier", &HeldDevice::GetVisualRecoilMultiplier, &HeldDevice::SetVisualRecoilMultiplier) + + .def("IsBeingHeld", &HeldDevice::IsBeingHeld) + .def("IsWeapon", &HeldDevice::IsWeapon) + .def("IsTool", &HeldDevice::IsTool) + .def("IsShield", &HeldDevice::IsShield) + .def("IsDualWieldable", &HeldDevice::IsDualWieldable) + .def("SetDualWieldable", &HeldDevice::SetDualWieldable) + .def("IsOneHanded", &HeldDevice::IsOneHanded) + .def("SetOneHanded", &HeldDevice::SetOneHanded) + .def("Activate", &HeldDevice::Activate) + .def("Deactivate", &HeldDevice::Deactivate) + .def("Reload", &HeldDevice::Reload) + .def("IsActivated", &HeldDevice::IsActivated) + .def("IsReloading", &HeldDevice::IsReloading) + .def("DoneReloading", &HeldDevice::DoneReloading) + .def("NeedsReloading", &HeldDevice::NeedsReloading) + .def("IsFull", &HeldDevice::IsFull) + .def("IsEmpty", &HeldDevice::IsEmpty) + .def("IsPickupableBy", &HeldDevice::IsPickupableBy) + .def("AddPickupableByPresetName", &HeldDevice::AddPickupableByPresetName) + .def("RemovePickupableByPresetName", &HeldDevice::RemovePickupableByPresetName); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Leg) { return ConcreteTypeLuaClassDefinition(Leg, Attachable) - .property("Foot", &Leg::GetFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::LegSetFoot) - .property("MoveSpeed", &Leg::GetMoveSpeed, &Leg::SetMoveSpeed); + .property("Foot", &Leg::GetFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::LegSetFoot) + .property("MoveSpeed", &Leg::GetMoveSpeed, &Leg::SetMoveSpeed); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, LimbPath) { return luabind::class_("LimbPath") - .property("StartOffset", &LimbPath::GetStartOffset, &LimbPath::SetStartOffset) - .property("SegmentCount", &LimbPath::GetSegCount) - .property("TravelSpeedMultiplier", &LimbPath::GetTravelSpeedMultiplier, &LimbPath::SetTravelSpeedMultiplier) + .property("StartOffset", &LimbPath::GetStartOffset, &LimbPath::SetStartOffset) + .property("SegmentCount", &LimbPath::GetSegCount) + .property("TravelSpeedMultiplier", &LimbPath::GetTravelSpeedMultiplier, &LimbPath::SetTravelSpeedMultiplier) - .def("GetSegment", &LimbPath::GetSegment); + .def("GetSegment", &LimbPath::GetSegment); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Magazine) { return ConcreteTypeLuaClassDefinition(Magazine, Attachable) - .property("NextRound", &Magazine::GetNextRound) - .property("RoundCount", &Magazine::GetRoundCount, &Magazine::SetRoundCount) - .property("IsEmpty", &Magazine::IsEmpty) - .property("IsFull", &Magazine::IsFull) - .property("IsOverHalfFull", &Magazine::IsOverHalfFull) - .property("Capacity", &Magazine::GetCapacity) - .property("Discardable", &Magazine::IsDiscardable); + .property("NextRound", &Magazine::GetNextRound) + .property("RoundCount", &Magazine::GetRoundCount, &Magazine::SetRoundCount) + .property("IsEmpty", &Magazine::IsEmpty) + .property("IsFull", &Magazine::IsFull) + .property("IsOverHalfFull", &Magazine::IsOverHalfFull) + .property("Capacity", &Magazine::GetCapacity) + .property("Discardable", &Magazine::IsDiscardable); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Material) { return luabind::class_("Material") - .property("ID", &Material::GetIndex) - .property("Restitution", &Material::GetRestitution) - .property("Bounce", &Material::GetRestitution) - .property("Friction", &Material::GetFriction) - .property("Stickiness", &Material::GetStickiness) - .property("Strength", &Material::GetIntegrity) - .property("StructuralIntegrity", &Material::GetIntegrity) - .property("DensityKGPerVolumeL", &Material::GetVolumeDensity) - .property("DensityKGPerPixel", &Material::GetPixelDensity) - .property("SettleMaterial", &Material::GetSettleMaterial) - .property("SpawnMaterial", &Material::GetSpawnMaterial) - .property("TransformsInto", &Material::GetSpawnMaterial) - .property("IsScrap", &Material::IsScrap); + .property("ID", &Material::GetIndex) + .property("Restitution", &Material::GetRestitution) + .property("Bounce", &Material::GetRestitution) + .property("Friction", &Material::GetFriction) + .property("Stickiness", &Material::GetStickiness) + .property("Strength", &Material::GetIntegrity) + .property("StructuralIntegrity", &Material::GetIntegrity) + .property("DensityKGPerVolumeL", &Material::GetVolumeDensity) + .property("DensityKGPerPixel", &Material::GetPixelDensity) + .property("SettleMaterial", &Material::GetSettleMaterial) + .property("SpawnMaterial", &Material::GetSpawnMaterial) + .property("TransformsInto", &Material::GetSpawnMaterial) + .property("IsScrap", &Material::IsScrap); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MetaPlayer) { return luabind::class_("MetaPlayer") - .def(luabind::constructor<>()) + .def(luabind::constructor<>()) - .property("NativeTechModule", &MetaPlayer::GetNativeTechModule) - .property("ForeignCostMultiplier", &MetaPlayer::GetForeignCostMultiplier) - .property("NativeCostMultiplier", &MetaPlayer::GetNativeCostMultiplier) - .property("InGamePlayer", &MetaPlayer::GetInGamePlayer) - .property("BrainPoolCount", &MetaPlayer::GetBrainPoolCount, &MetaPlayer::SetBrainPoolCount) + .property("NativeTechModule", &MetaPlayer::GetNativeTechModule) + .property("ForeignCostMultiplier", &MetaPlayer::GetForeignCostMultiplier) + .property("NativeCostMultiplier", &MetaPlayer::GetNativeCostMultiplier) + .property("InGamePlayer", &MetaPlayer::GetInGamePlayer) + .property("BrainPoolCount", &MetaPlayer::GetBrainPoolCount, &MetaPlayer::SetBrainPoolCount) - .def("ChangeBrainPoolCount", &MetaPlayer::ChangeBrainPoolCount); + .def("ChangeBrainPoolCount", &MetaPlayer::ChangeBrainPoolCount); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MOPixel) { return ConcreteTypeLuaClassDefinition(MOPixel, MovableObject) - .property("TrailLength", &MOPixel::GetTrailLength, &MOPixel::SetTrailLength) - .property("Staininess", &MOPixel::GetStaininess, &MOPixel::SetStaininess); + .property("TrailLength", &MOPixel::GetTrailLength, &MOPixel::SetTrailLength) + .property("Staininess", &MOPixel::GetStaininess, &MOPixel::SetStaininess); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MOSParticle) { return ConcreteTypeLuaClassDefinition(MOSParticle, MOSprite); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MOSprite) { return AbstractTypeLuaClassDefinition(MOSprite, MovableObject) - .property("Diameter", &MOSprite::GetDiameter) - .property("BoundingBox", &MOSprite::GetBoundingBox) - .property("FrameCount", &MOSprite::GetFrameCount) - .property("SpriteOffset", &MOSprite::GetSpriteOffset, &MOSprite::SetSpriteOffset) - .property("HFlipped", &MOSprite::IsHFlipped, &MOSprite::SetHFlipped) - .property("FlipFactor", &MOSprite::GetFlipFactor) - .property("RotAngle", &MOSprite::GetRotAngle, &MOSprite::SetRotAngle) - .property("PrevRotAngle", &MOSprite::GetPrevRotAngle) - .property("AngularVel", &MOSprite::GetAngularVel, &MOSprite::SetAngularVel) - .property("Frame", &MOSprite::GetFrame, &MOSprite::SetFrame) - .property("SpriteAnimMode", &MOSprite::GetSpriteAnimMode, &MOSprite::SetSpriteAnimMode) - .property("SpriteAnimDuration", &MOSprite::GetSpriteAnimDuration, &MOSprite::SetSpriteAnimDuration) - - .def("SetNextFrame", &MOSprite::SetNextFrame) - .def("IsTooFast", &MOSprite::IsTooFast) - .def("IsOnScenePoint", &MOSprite::IsOnScenePoint) - .def("RotateOffset", &MOSprite::RotateOffset) - .def("UnRotateOffset", &MOSprite::UnRotateOffset) - .def("FacingAngle", &MOSprite::FacingAngle) - .def("GetSpriteWidth", &MOSprite::GetSpriteWidth) - .def("GetSpriteHeight", &MOSprite::GetSpriteHeight) - .def("GetIconWidth", &MOSprite::GetIconWidth) - .def("GetIconHeight", &MOSprite::GetIconHeight) - .def("SetEntryWound", &MOSprite::SetEntryWound) - .def("SetExitWound", &MOSprite::SetExitWound) - .def("GetEntryWoundPresetName", &MOSprite::GetEntryWoundPresetName) - .def("GetExitWoundPresetName", &MOSprite::GetExitWoundPresetName) - - .enum_("SpriteAnimMode")[ - luabind::value("NOANIM", SpriteAnimMode::NOANIM), - luabind::value("ALWAYSLOOP", SpriteAnimMode::ALWAYSLOOP), - luabind::value("ALWAYSRANDOM", SpriteAnimMode::ALWAYSRANDOM), - luabind::value("ALWAYSPINGPONG", SpriteAnimMode::ALWAYSPINGPONG), - luabind::value("LOOPWHENACTIVE", SpriteAnimMode::LOOPWHENACTIVE), - luabind::value("LOOPWHENOPENCLOSE", SpriteAnimMode::LOOPWHENOPENCLOSE), - luabind::value("PINGPONGOPENCLOSE", SpriteAnimMode::PINGPONGOPENCLOSE), - luabind::value("OVERLIFETIME", SpriteAnimMode::OVERLIFETIME), - luabind::value("ONCOLLIDE", SpriteAnimMode::ONCOLLIDE) - ]; + .property("Diameter", &MOSprite::GetDiameter) + .property("BoundingBox", &MOSprite::GetBoundingBox) + .property("FrameCount", &MOSprite::GetFrameCount) + .property("SpriteOffset", &MOSprite::GetSpriteOffset, &MOSprite::SetSpriteOffset) + .property("HFlipped", &MOSprite::IsHFlipped, &MOSprite::SetHFlipped) + .property("FlipFactor", &MOSprite::GetFlipFactor) + .property("RotAngle", &MOSprite::GetRotAngle, &MOSprite::SetRotAngle) + .property("PrevRotAngle", &MOSprite::GetPrevRotAngle) + .property("AngularVel", &MOSprite::GetAngularVel, &MOSprite::SetAngularVel) + .property("Frame", &MOSprite::GetFrame, &MOSprite::SetFrame) + .property("SpriteAnimMode", &MOSprite::GetSpriteAnimMode, &MOSprite::SetSpriteAnimMode) + .property("SpriteAnimDuration", &MOSprite::GetSpriteAnimDuration, &MOSprite::SetSpriteAnimDuration) + + .def("SetNextFrame", &MOSprite::SetNextFrame) + .def("IsTooFast", &MOSprite::IsTooFast) + .def("IsOnScenePoint", &MOSprite::IsOnScenePoint) + .def("RotateOffset", &MOSprite::RotateOffset) + .def("UnRotateOffset", &MOSprite::UnRotateOffset) + .def("FacingAngle", &MOSprite::FacingAngle) + .def("GetSpriteWidth", &MOSprite::GetSpriteWidth) + .def("GetSpriteHeight", &MOSprite::GetSpriteHeight) + .def("GetIconWidth", &MOSprite::GetIconWidth) + .def("GetIconHeight", &MOSprite::GetIconHeight) + .def("SetEntryWound", &MOSprite::SetEntryWound) + .def("SetExitWound", &MOSprite::SetExitWound) + .def("GetEntryWoundPresetName", &MOSprite::GetEntryWoundPresetName) + .def("GetExitWoundPresetName", &MOSprite::GetExitWoundPresetName) + + .enum_("SpriteAnimMode")[luabind::value("NOANIM", SpriteAnimMode::NOANIM), + luabind::value("ALWAYSLOOP", SpriteAnimMode::ALWAYSLOOP), + luabind::value("ALWAYSRANDOM", SpriteAnimMode::ALWAYSRANDOM), + luabind::value("ALWAYSPINGPONG", SpriteAnimMode::ALWAYSPINGPONG), + luabind::value("LOOPWHENACTIVE", SpriteAnimMode::LOOPWHENACTIVE), + luabind::value("LOOPWHENOPENCLOSE", SpriteAnimMode::LOOPWHENOPENCLOSE), + luabind::value("PINGPONGOPENCLOSE", SpriteAnimMode::PINGPONGOPENCLOSE), + luabind::value("OVERLIFETIME", SpriteAnimMode::OVERLIFETIME), + luabind::value("ONCOLLIDE", SpriteAnimMode::ONCOLLIDE)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MOSRotating) { return ConcreteTypeLuaClassDefinition(MOSRotating, MOSprite) - //.property("Material", &MOSRotating::GetMaterial) - .property("IndividualRadius", &MOSRotating::GetIndividualRadius) - .property("IndividualDiameter", &MOSRotating::GetIndividualDiameter) - .property("IndividualMass", &MOSRotating::GetIndividualMass) - .property("RecoilForce", &MOSRotating::GetRecoilForce) - .property("RecoilOffset", &MOSRotating::GetRecoilOffset) - .property("TravelImpulse", &MOSRotating::GetTravelImpulse, &MOSRotating::SetTravelImpulse) - .property("GibWoundLimit", (int (MOSRotating:: *)() const) &MOSRotating::GetGibWoundLimit, &MOSRotating::SetGibWoundLimit) - .property("GibSound", &MOSRotating::GetGibSound, &LuaAdaptersPropertyOwnershipSafetyFaker::MOSRotatingSetGibSound) - .property("GibImpulseLimit", &MOSRotating::GetGibImpulseLimit, &MOSRotating::SetGibImpulseLimit) - .property("WoundCountAffectsImpulseLimitRatio", &MOSRotating::GetWoundCountAffectsImpulseLimitRatio) - .property("GibAtEndOfLifetime", &MOSRotating::GetGibAtEndOfLifetime, &MOSRotating::SetGibAtEndOfLifetime) - .property("DamageMultiplier", &MOSRotating::GetDamageMultiplier, &MOSRotating::SetDamageMultiplier) - .property("WoundCount", (int (MOSRotating:: *)() const) &MOSRotating::GetWoundCount) - .property("OrientToVel", &MOSRotating::GetOrientToVel, &MOSRotating::SetOrientToVel) - - .def_readonly("Attachables", &MOSRotating::m_Attachables, luabind::return_stl_iterator) - .def_readonly("Wounds", &MOSRotating::m_Wounds, luabind::return_stl_iterator) - .def_readonly("Gibs", &MOSRotating::m_Gibs, luabind::return_stl_iterator) - - .def("AddRecoil", &MOSRotating::AddRecoil) - .def("SetRecoil", &MOSRotating::SetRecoil) - .def("IsRecoiled", &MOSRotating::IsRecoiled) - .def("EnableDeepCheck", &MOSRotating::EnableDeepCheck) - .def("ForceDeepCheck", &MOSRotating::ForceDeepCheck) - .def("GibThis", &MOSRotating::GibThis) - .def("MoveOutOfTerrain", &MOSRotating::MoveOutOfTerrain) - .def("FlashWhite", &MOSRotating::FlashWhite) - .def("GetGibWoundLimit", (int (MOSRotating:: *)() const) &MOSRotating::GetGibWoundLimit) - .def("GetGibWoundLimit", (int (MOSRotating:: *)(bool positiveDamage, bool negativeDamage, bool noDamage) const) &MOSRotating::GetGibWoundLimit) - .def("GetWoundCount", (int (MOSRotating:: *)() const) &MOSRotating::GetWoundCount) - .def("GetWoundCount", (int (MOSRotating:: *)(bool positiveDamage, bool negativeDamage, bool noDamage) const) &MOSRotating::GetWoundCount) - .def("GetWounds", &LuaAdaptersMOSRotating::GetWounds1, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("GetWounds", &LuaAdaptersMOSRotating::GetWounds2, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("AddWound", &MOSRotating::AddWound, luabind::adopt(_2)) - .def("RemoveWounds", (float (MOSRotating:: *)(int numberOfWoundsToRemove)) &MOSRotating::RemoveWounds) - .def("RemoveWounds", (float (MOSRotating:: *)(int numberOfWoundsToRemove, bool positiveDamage, bool negativeDamage, bool noDamage)) &MOSRotating::RemoveWounds) - .def("IsOnScenePoint", &MOSRotating::IsOnScenePoint) - .def("EraseFromTerrain", &MOSRotating::EraseFromTerrain) - .def("AddAttachable", (void (MOSRotating::*)(Attachable *attachableToAdd))&MOSRotating::AddAttachable, luabind::adopt(_2)) - .def("AddAttachable", (void (MOSRotating::*)(Attachable *attachableToAdd, const Vector &parentOffset))&MOSRotating::AddAttachable, luabind::adopt(_2)) - .def("RemoveAttachable", (Attachable *(MOSRotating:: *)(long uniqueIDOfAttachableToRemove)) &MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) - .def("RemoveAttachable", (Attachable *(MOSRotating:: *)(long uniqueIDOfAttachableToRemove, bool addToMovableMan, bool addBreakWounds)) &MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) - .def("RemoveAttachable", (Attachable *(MOSRotating:: *)(Attachable *attachableToRemove))&MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) - .def("RemoveAttachable", (Attachable *(MOSRotating:: *)(Attachable *attachableToRemove, bool addToMovableMan, bool addBreakWounds)) &MOSRotating::RemoveAttachable) - .def("AddEmitter", (void (MOSRotating::*)(Attachable *attachableToAdd))&MOSRotating::AddAttachable, luabind::adopt(_2)) - .def("AddEmitter", (void (MOSRotating::*)(Attachable *attachableToAdd, const Vector &parentOffset))&MOSRotating::AddAttachable, luabind::adopt(_2)) - .def("RemoveEmitter", (Attachable *(MOSRotating:: *)(long uniqueIDOfAttachableToRemove)) &MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) - .def("RemoveEmitter", (Attachable *(MOSRotating:: *)(long uniqueIDOfAttachableToRemove, bool addToMovableMan, bool addBreakWounds)) &MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) - .def("RemoveEmitter", (Attachable *(MOSRotating:: *)(Attachable *attachableToRemove)) &MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) - .def("RemoveEmitter", (Attachable *(MOSRotating:: *)(Attachable *attachableToRemove, bool addToMovableMan, bool addBreakWounds)) &MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) - - .def("GibThis", &LuaAdaptersMOSRotating::GibThis); + //.property("Material", &MOSRotating::GetMaterial) + .property("IndividualRadius", &MOSRotating::GetIndividualRadius) + .property("IndividualDiameter", &MOSRotating::GetIndividualDiameter) + .property("IndividualMass", &MOSRotating::GetIndividualMass) + .property("RecoilForce", &MOSRotating::GetRecoilForce) + .property("RecoilOffset", &MOSRotating::GetRecoilOffset) + .property("TravelImpulse", &MOSRotating::GetTravelImpulse, &MOSRotating::SetTravelImpulse) + .property("GibWoundLimit", (int(MOSRotating::*)() const) & MOSRotating::GetGibWoundLimit, &MOSRotating::SetGibWoundLimit) + .property("GibSound", &MOSRotating::GetGibSound, &LuaAdaptersPropertyOwnershipSafetyFaker::MOSRotatingSetGibSound) + .property("GibImpulseLimit", &MOSRotating::GetGibImpulseLimit, &MOSRotating::SetGibImpulseLimit) + .property("WoundCountAffectsImpulseLimitRatio", &MOSRotating::GetWoundCountAffectsImpulseLimitRatio) + .property("GibAtEndOfLifetime", &MOSRotating::GetGibAtEndOfLifetime, &MOSRotating::SetGibAtEndOfLifetime) + .property("DamageMultiplier", &MOSRotating::GetDamageMultiplier, &MOSRotating::SetDamageMultiplier) + .property("WoundCount", (int(MOSRotating::*)() const) & MOSRotating::GetWoundCount) + .property("OrientToVel", &MOSRotating::GetOrientToVel, &MOSRotating::SetOrientToVel) + + .def_readonly("Attachables", &MOSRotating::m_Attachables, luabind::return_stl_iterator) + .def_readonly("Wounds", &MOSRotating::m_Wounds, luabind::return_stl_iterator) + .def_readonly("Gibs", &MOSRotating::m_Gibs, luabind::return_stl_iterator) + + .def("AddRecoil", &MOSRotating::AddRecoil) + .def("SetRecoil", &MOSRotating::SetRecoil) + .def("IsRecoiled", &MOSRotating::IsRecoiled) + .def("EnableDeepCheck", &MOSRotating::EnableDeepCheck) + .def("ForceDeepCheck", &MOSRotating::ForceDeepCheck) + .def("GibThis", &MOSRotating::GibThis) + .def("MoveOutOfTerrain", &MOSRotating::MoveOutOfTerrain) + .def("FlashWhite", &MOSRotating::FlashWhite) + .def("GetGibWoundLimit", (int(MOSRotating::*)() const) & MOSRotating::GetGibWoundLimit) + .def("GetGibWoundLimit", (int(MOSRotating::*)(bool positiveDamage, bool negativeDamage, bool noDamage) const) & MOSRotating::GetGibWoundLimit) + .def("GetWoundCount", (int(MOSRotating::*)() const) & MOSRotating::GetWoundCount) + .def("GetWoundCount", (int(MOSRotating::*)(bool positiveDamage, bool negativeDamage, bool noDamage) const) & MOSRotating::GetWoundCount) + .def("GetWounds", &LuaAdaptersMOSRotating::GetWounds1, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("GetWounds", &LuaAdaptersMOSRotating::GetWounds2, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("AddWound", &MOSRotating::AddWound, luabind::adopt(_2)) + .def("RemoveWounds", (float(MOSRotating::*)(int numberOfWoundsToRemove)) & MOSRotating::RemoveWounds) + .def("RemoveWounds", (float(MOSRotating::*)(int numberOfWoundsToRemove, bool positiveDamage, bool negativeDamage, bool noDamage)) & MOSRotating::RemoveWounds) + .def("IsOnScenePoint", &MOSRotating::IsOnScenePoint) + .def("EraseFromTerrain", &MOSRotating::EraseFromTerrain) + .def("AddAttachable", (void(MOSRotating::*)(Attachable * attachableToAdd)) & MOSRotating::AddAttachable, luabind::adopt(_2)) + .def("AddAttachable", (void(MOSRotating::*)(Attachable * attachableToAdd, const Vector& parentOffset)) & MOSRotating::AddAttachable, luabind::adopt(_2)) + .def("RemoveAttachable", (Attachable * (MOSRotating::*)(long uniqueIDOfAttachableToRemove)) & MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) + .def("RemoveAttachable", (Attachable * (MOSRotating::*)(long uniqueIDOfAttachableToRemove, bool addToMovableMan, bool addBreakWounds)) & MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) + .def("RemoveAttachable", (Attachable * (MOSRotating::*)(Attachable * attachableToRemove)) & MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) + .def("RemoveAttachable", (Attachable * (MOSRotating::*)(Attachable * attachableToRemove, bool addToMovableMan, bool addBreakWounds)) & MOSRotating::RemoveAttachable) + .def("AddEmitter", (void(MOSRotating::*)(Attachable * attachableToAdd)) & MOSRotating::AddAttachable, luabind::adopt(_2)) + .def("AddEmitter", (void(MOSRotating::*)(Attachable * attachableToAdd, const Vector& parentOffset)) & MOSRotating::AddAttachable, luabind::adopt(_2)) + .def("RemoveEmitter", (Attachable * (MOSRotating::*)(long uniqueIDOfAttachableToRemove)) & MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) + .def("RemoveEmitter", (Attachable * (MOSRotating::*)(long uniqueIDOfAttachableToRemove, bool addToMovableMan, bool addBreakWounds)) & MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) + .def("RemoveEmitter", (Attachable * (MOSRotating::*)(Attachable * attachableToRemove)) & MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) + .def("RemoveEmitter", (Attachable * (MOSRotating::*)(Attachable * attachableToRemove, bool addToMovableMan, bool addBreakWounds)) & MOSRotating::RemoveAttachable, luabind::adopt(luabind::return_value)) + + .def("GibThis", &LuaAdaptersMOSRotating::GibThis); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MovableObject) { return AbstractTypeLuaClassDefinition(MovableObject, SceneObject) - .property("Material", &MovableObject::GetMaterial) - .property("Mass", &MovableObject::GetMass, &MovableObject::SetMass) - .property("Pos", &MovableObject::GetPos, &MovableObject::SetPos) - .property("Vel", &MovableObject::GetVel, &MovableObject::SetVel) - .property("PrevPos", &MovableObject::GetPrevPos) - .property("PrevVel", &MovableObject::GetPrevVel) - .property("DistanceTravelled", &MovableObject::GetDistanceTravelled) - .property("AngularVel", &MovableObject::GetAngularVel, &MovableObject::SetAngularVel) - .property("Radius", &MovableObject::GetRadius) - .property("Diameter", &MovableObject::GetDiameter) - .property("Scale", &MovableObject::GetScale, &MovableObject::SetScale) - .property("EffectRotAngle", &MovableObject::GetEffectRotAngle, &MovableObject::SetEffectRotAngle) - .property("GlobalAccScalar", &MovableObject::GetGlobalAccScalar, &MovableObject::SetGlobalAccScalar) - .property("AirResistance", &MovableObject::GetAirResistance, &MovableObject::SetAirResistance) - .property("AirThreshold", &MovableObject::GetAirThreshold, &MovableObject::SetAirThreshold) - .property("Age", &MovableObject::GetAge, &MovableObject::SetAge) - .property("Lifetime", &MovableObject::GetLifetime, &MovableObject::SetLifetime) - .property("ID", &MovableObject::GetID) - .property("UniqueID", &MovableObject::GetUniqueID) - .property("RootID", &MovableObject::GetRootID) - .property("MOIDFootprint", &MovableObject::GetMOIDFootprint) - .property("Sharpness", &MovableObject::GetSharpness, &MovableObject::SetSharpness) - .property("HasEverBeenAddedToMovableMan", &MovableObject::HasEverBeenAddedToMovableMan) - .property("AboveHUDPos", &MovableObject::GetAboveHUDPos) - .property("HitsMOs", &MovableObject::HitsMOs, &MovableObject::SetToHitMOs) - .property("GetsHitByMOs", &MovableObject::GetsHitByMOs, &MovableObject::SetToGetHitByMOs) - .property("IgnoresTeamHits", &MovableObject::IgnoresTeamHits, &MovableObject::SetIgnoresTeamHits) - .property("IgnoresWhichTeam", &MovableObject::IgnoresWhichTeam) - .property("IgnoreTerrain", &MovableObject::IgnoreTerrain, &MovableObject::SetIgnoreTerrain) - .property("IgnoresActorHits", &MovableObject::GetIgnoresActorHits, &MovableObject::SetIgnoresActorHits) - .property("ToSettle", &MovableObject::ToSettle, &MovableObject::SetToSettle) - .property("ToDelete", &MovableObject::ToDelete, &MovableObject::SetToDelete) - .property("MissionCritical", &MovableObject::IsMissionCritical, &MovableObject::SetMissionCritical) - .property("HUDVisible", &MovableObject::GetHUDVisible, &MovableObject::SetHUDVisible) - .property("PinStrength", &MovableObject::GetPinStrength, &MovableObject::SetPinStrength) - .property("RestThreshold", &MovableObject::GetRestThreshold, &MovableObject::SetRestThreshold) - .property("DamageOnCollision", &MovableObject::DamageOnCollision, &MovableObject::SetDamageOnCollision) - .property("DamageOnPenetration", &MovableObject::DamageOnPenetration, &MovableObject::SetDamageOnPenetration) - .property("WoundDamageMultiplier", &MovableObject::WoundDamageMultiplier, &MovableObject::SetWoundDamageMultiplier) - .property("HitWhatMOID", &MovableObject::HitWhatMOID) - .property("HitWhatTerrMaterial", &MovableObject::HitWhatTerrMaterial) - .property("HitWhatParticleUniqueID", &MovableObject::HitWhatParticleUniqueID) - .property("ApplyWoundDamageOnCollision", &MovableObject::GetApplyWoundDamageOnCollision, &MovableObject::SetApplyWoundDamageOnCollision) - .property("ApplyWoundBurstDamageOnCollision", &MovableObject::GetApplyWoundBurstDamageOnCollision, &MovableObject::SetApplyWoundBurstDamageOnCollision) - .property("SimUpdatesBetweenScriptedUpdates", &MovableObject::GetSimUpdatesBetweenScriptedUpdates, &MovableObject::SetSimUpdatesBetweenScriptedUpdates) - - .def("GetParent", (MOSRotating * (MovableObject::*)())&MovableObject::GetParent) - .def("GetParent", (const MOSRotating * (MovableObject::*)() const)&MovableObject::GetParent) - .def("GetRootParent", (MovableObject * (MovableObject::*)())&MovableObject::GetRootParent) - .def("GetRootParent", (const MovableObject * (MovableObject::*)() const)&MovableObject::GetRootParent) - .def("ReloadScripts", &MovableObject::ReloadScripts) - .def("HasScript", &LuaAdaptersMovableObject::HasScript) - .def("AddScript", &LuaAdaptersMovableObject::AddScript) - .def("ScriptEnabled", &MovableObject::ScriptEnabled) - .def("EnableScript", &LuaAdaptersMovableObject::EnableScript) - .def("DisableScript", &LuaAdaptersMovableObject::DisableScript1) - .def("DisableScript", &LuaAdaptersMovableObject::DisableScript2) - .def("EnableOrDisableAllScripts", &MovableObject::EnableOrDisableAllScripts) - .def("GetStringValue", &MovableObject::GetStringValue) - .def("GetEncodedStringValue", &MovableObject::GetEncodedStringValue) - .def("GetNumberValue", &MovableObject::GetNumberValue) - .def("GetObjectValue", &MovableObject::GetObjectValue) - .def("SetStringValue", &MovableObject::SetStringValue) - .def("SetEncodedStringValue", &MovableObject::SetEncodedStringValue) - .def("SetNumberValue", &MovableObject::SetNumberValue) - .def("SetObjectValue", &MovableObject::SetObjectValue) - .def("RemoveStringValue", &MovableObject::RemoveStringValue) - .def("RemoveNumberValue", &MovableObject::RemoveNumberValue) - .def("RemoveObjectValue", &MovableObject::RemoveObjectValue) - .def("StringValueExists", &MovableObject::StringValueExists) - .def("NumberValueExists", &MovableObject::NumberValueExists) - .def("ObjectValueExists", &MovableObject::ObjectValueExists) - .def("GetAltitude", &MovableObject::GetAltitude) - .def("GetWhichMOToNotHit", &MovableObject::GetWhichMOToNotHit) - .def("SetWhichMOToNotHit", &MovableObject::SetWhichMOToNotHit) - .def("IsSetToDelete", &MovableObject::IsSetToDelete) - .def("IsMissionCritical", &MovableObject::IsMissionCritical) - .def("IsGeneric", &MovableObject::IsGeneric) - .def("IsActor", &MovableObject::IsActor) - .def("IsDevice", &MovableObject::IsDevice) - .def("IsHeldDevice", &MovableObject::IsHeldDevice) - .def("IsThrownDevice", &MovableObject::IsThrownDevice) - .def("IsGold", &MovableObject::IsGold) - .def("IsThrownDevice", &MovableObject::IsThrownDevice) - .def("HasObject", &MovableObject::HasObject) - .def("HasObjectInGroup", &MovableObject::HasObjectInGroup) - .def("AddForce", &MovableObject::AddForce) - .def("AddAbsForce", &MovableObject::AddAbsForce) - .def("AddImpulseForce", &MovableObject::AddImpulseForce) - .def("AddAbsImpulseForce", &MovableObject::AddAbsImpulseForce) - .def("ClearForces", &MovableObject::ClearForces) - .def("ClearImpulseForces", &MovableObject::ClearImpulseForces) - .def("GetForcesCount", &MovableObject::GetForcesCount) - .def("GetForceVector", &MovableObject::GetForceVector) - .def("GetForceOffset", &MovableObject::GetForceOffset) - .def("SetForceVector", &MovableObject::SetForceVector) - .def("SetForceOffset", &MovableObject::SetForceOffset) - .def("GetImpulsesCount", &MovableObject::GetImpulsesCount) - .def("GetImpulseVector", &MovableObject::GetImpulseVector) - .def("GetImpulseOffset", &MovableObject::GetImpulseOffset) - .def("SetImpulseVector", &MovableObject::SetImpulseVector) - .def("SetImpulseOffset", &MovableObject::SetImpulseOffset) - .def("RestDetection", &MovableObject::RestDetection) - .def("NotResting", &MovableObject::NotResting) - .def("IsAtRest", &MovableObject::IsAtRest) - .def("MoveOutOfTerrain", &MovableObject::MoveOutOfTerrain) - .def("RotateOffset", &MovableObject::RotateOffset) - .def("SendMessage", &LuaAdaptersMovableObject::SendMessage1) - .def("SendMessage", &LuaAdaptersMovableObject::SendMessage2) - .def("RequestSyncedUpdate", &MovableObject::RequestSyncedUpdate); + .property("Material", &MovableObject::GetMaterial) + .property("Mass", &MovableObject::GetMass, &MovableObject::SetMass) + .property("Pos", &MovableObject::GetPos, &MovableObject::SetPos) + .property("Vel", &MovableObject::GetVel, &MovableObject::SetVel) + .property("PrevPos", &MovableObject::GetPrevPos) + .property("PrevVel", &MovableObject::GetPrevVel) + .property("DistanceTravelled", &MovableObject::GetDistanceTravelled) + .property("AngularVel", &MovableObject::GetAngularVel, &MovableObject::SetAngularVel) + .property("Radius", &MovableObject::GetRadius) + .property("Diameter", &MovableObject::GetDiameter) + .property("Scale", &MovableObject::GetScale, &MovableObject::SetScale) + .property("EffectRotAngle", &MovableObject::GetEffectRotAngle, &MovableObject::SetEffectRotAngle) + .property("GlobalAccScalar", &MovableObject::GetGlobalAccScalar, &MovableObject::SetGlobalAccScalar) + .property("AirResistance", &MovableObject::GetAirResistance, &MovableObject::SetAirResistance) + .property("AirThreshold", &MovableObject::GetAirThreshold, &MovableObject::SetAirThreshold) + .property("Age", &MovableObject::GetAge, &MovableObject::SetAge) + .property("Lifetime", &MovableObject::GetLifetime, &MovableObject::SetLifetime) + .property("ID", &MovableObject::GetID) + .property("UniqueID", &MovableObject::GetUniqueID) + .property("RootID", &MovableObject::GetRootID) + .property("MOIDFootprint", &MovableObject::GetMOIDFootprint) + .property("Sharpness", &MovableObject::GetSharpness, &MovableObject::SetSharpness) + .property("HasEverBeenAddedToMovableMan", &MovableObject::HasEverBeenAddedToMovableMan) + .property("AboveHUDPos", &MovableObject::GetAboveHUDPos) + .property("HitsMOs", &MovableObject::HitsMOs, &MovableObject::SetToHitMOs) + .property("GetsHitByMOs", &MovableObject::GetsHitByMOs, &MovableObject::SetToGetHitByMOs) + .property("IgnoresTeamHits", &MovableObject::IgnoresTeamHits, &MovableObject::SetIgnoresTeamHits) + .property("IgnoresWhichTeam", &MovableObject::IgnoresWhichTeam) + .property("IgnoreTerrain", &MovableObject::IgnoreTerrain, &MovableObject::SetIgnoreTerrain) + .property("IgnoresActorHits", &MovableObject::GetIgnoresActorHits, &MovableObject::SetIgnoresActorHits) + .property("ToSettle", &MovableObject::ToSettle, &MovableObject::SetToSettle) + .property("ToDelete", &MovableObject::ToDelete, &MovableObject::SetToDelete) + .property("MissionCritical", &MovableObject::IsMissionCritical, &MovableObject::SetMissionCritical) + .property("HUDVisible", &MovableObject::GetHUDVisible, &MovableObject::SetHUDVisible) + .property("PinStrength", &MovableObject::GetPinStrength, &MovableObject::SetPinStrength) + .property("RestThreshold", &MovableObject::GetRestThreshold, &MovableObject::SetRestThreshold) + .property("DamageOnCollision", &MovableObject::DamageOnCollision, &MovableObject::SetDamageOnCollision) + .property("DamageOnPenetration", &MovableObject::DamageOnPenetration, &MovableObject::SetDamageOnPenetration) + .property("WoundDamageMultiplier", &MovableObject::WoundDamageMultiplier, &MovableObject::SetWoundDamageMultiplier) + .property("HitWhatMOID", &MovableObject::HitWhatMOID) + .property("HitWhatTerrMaterial", &MovableObject::HitWhatTerrMaterial) + .property("HitWhatParticleUniqueID", &MovableObject::HitWhatParticleUniqueID) + .property("ApplyWoundDamageOnCollision", &MovableObject::GetApplyWoundDamageOnCollision, &MovableObject::SetApplyWoundDamageOnCollision) + .property("ApplyWoundBurstDamageOnCollision", &MovableObject::GetApplyWoundBurstDamageOnCollision, &MovableObject::SetApplyWoundBurstDamageOnCollision) + .property("SimUpdatesBetweenScriptedUpdates", &MovableObject::GetSimUpdatesBetweenScriptedUpdates, &MovableObject::SetSimUpdatesBetweenScriptedUpdates) + + .def("GetParent", (MOSRotating * (MovableObject::*)()) & MovableObject::GetParent) + .def("GetParent", (const MOSRotating* (MovableObject::*)() const) & MovableObject::GetParent) + .def("GetRootParent", (MovableObject * (MovableObject::*)()) & MovableObject::GetRootParent) + .def("GetRootParent", (const MovableObject* (MovableObject::*)() const) & MovableObject::GetRootParent) + .def("ReloadScripts", &MovableObject::ReloadScripts) + .def("HasScript", &LuaAdaptersMovableObject::HasScript) + .def("AddScript", &LuaAdaptersMovableObject::AddScript) + .def("ScriptEnabled", &MovableObject::ScriptEnabled) + .def("EnableScript", &LuaAdaptersMovableObject::EnableScript) + .def("DisableScript", &LuaAdaptersMovableObject::DisableScript1) + .def("DisableScript", &LuaAdaptersMovableObject::DisableScript2) + .def("EnableOrDisableAllScripts", &MovableObject::EnableOrDisableAllScripts) + .def("GetStringValue", &MovableObject::GetStringValue) + .def("GetEncodedStringValue", &MovableObject::GetEncodedStringValue) + .def("GetNumberValue", &MovableObject::GetNumberValue) + .def("GetObjectValue", &MovableObject::GetObjectValue) + .def("SetStringValue", &MovableObject::SetStringValue) + .def("SetEncodedStringValue", &MovableObject::SetEncodedStringValue) + .def("SetNumberValue", &MovableObject::SetNumberValue) + .def("SetObjectValue", &MovableObject::SetObjectValue) + .def("RemoveStringValue", &MovableObject::RemoveStringValue) + .def("RemoveNumberValue", &MovableObject::RemoveNumberValue) + .def("RemoveObjectValue", &MovableObject::RemoveObjectValue) + .def("StringValueExists", &MovableObject::StringValueExists) + .def("NumberValueExists", &MovableObject::NumberValueExists) + .def("ObjectValueExists", &MovableObject::ObjectValueExists) + .def("GetAltitude", &MovableObject::GetAltitude) + .def("GetWhichMOToNotHit", &MovableObject::GetWhichMOToNotHit) + .def("SetWhichMOToNotHit", &MovableObject::SetWhichMOToNotHit) + .def("IsSetToDelete", &MovableObject::IsSetToDelete) + .def("IsMissionCritical", &MovableObject::IsMissionCritical) + .def("IsGeneric", &MovableObject::IsGeneric) + .def("IsActor", &MovableObject::IsActor) + .def("IsDevice", &MovableObject::IsDevice) + .def("IsHeldDevice", &MovableObject::IsHeldDevice) + .def("IsThrownDevice", &MovableObject::IsThrownDevice) + .def("IsGold", &MovableObject::IsGold) + .def("IsThrownDevice", &MovableObject::IsThrownDevice) + .def("HasObject", &MovableObject::HasObject) + .def("HasObjectInGroup", &MovableObject::HasObjectInGroup) + .def("AddForce", &MovableObject::AddForce) + .def("AddAbsForce", &MovableObject::AddAbsForce) + .def("AddImpulseForce", &MovableObject::AddImpulseForce) + .def("AddAbsImpulseForce", &MovableObject::AddAbsImpulseForce) + .def("ClearForces", &MovableObject::ClearForces) + .def("ClearImpulseForces", &MovableObject::ClearImpulseForces) + .def("GetForcesCount", &MovableObject::GetForcesCount) + .def("GetForceVector", &MovableObject::GetForceVector) + .def("GetForceOffset", &MovableObject::GetForceOffset) + .def("SetForceVector", &MovableObject::SetForceVector) + .def("SetForceOffset", &MovableObject::SetForceOffset) + .def("GetImpulsesCount", &MovableObject::GetImpulsesCount) + .def("GetImpulseVector", &MovableObject::GetImpulseVector) + .def("GetImpulseOffset", &MovableObject::GetImpulseOffset) + .def("SetImpulseVector", &MovableObject::SetImpulseVector) + .def("SetImpulseOffset", &MovableObject::SetImpulseOffset) + .def("RestDetection", &MovableObject::RestDetection) + .def("NotResting", &MovableObject::NotResting) + .def("IsAtRest", &MovableObject::IsAtRest) + .def("MoveOutOfTerrain", &MovableObject::MoveOutOfTerrain) + .def("RotateOffset", &MovableObject::RotateOffset) + .def("SendMessage", &LuaAdaptersMovableObject::SendMessage1) + .def("SendMessage", &LuaAdaptersMovableObject::SendMessage2) + .def("RequestSyncedUpdate", &MovableObject::RequestSyncedUpdate); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, PEmitter) { return ConcreteTypeLuaClassDefinition(PEmitter, MOSParticle) - .property("BurstScale", &PEmitter::GetBurstScale, &PEmitter::SetBurstScale) - .property("EmitAngle", &PEmitter::GetEmitAngle, &PEmitter::SetEmitAngle) - .property("GetThrottle", &PEmitter::GetThrottle, &PEmitter::SetThrottle) - .property("Throttle", &PEmitter::GetThrottle, &PEmitter::SetThrottle) - .property("ThrottleFactor", &PEmitter::GetThrottleFactor) - .property("BurstSpacing", &PEmitter::GetBurstSpacing, &PEmitter::SetBurstSpacing) - .property("EmitCountLimit", &PEmitter::GetEmitCountLimit, &PEmitter::SetEmitCountLimit) - .property("FlashScale", &PEmitter::GetFlashScale, &PEmitter::SetFlashScale) - - .def_readwrite("Emissions", &PEmitter::m_EmissionList, luabind::return_stl_iterator) - - .def("IsEmitting", &PEmitter::IsEmitting) - .def("WasEmitting", &PEmitter::WasEmitting) - .def("EnableEmission", &PEmitter::EnableEmission) - .def("GetEmitVector", &PEmitter::GetEmitVector) - .def("GetRecoilVector", &PEmitter::GetRecoilVector) - .def("EstimateImpulse", &PEmitter::EstimateImpulse) - .def("TriggerBurst", &PEmitter::TriggerBurst) - .def("IsSetToBurst", &PEmitter::IsSetToBurst) - .def("CanTriggerBurst", &PEmitter::CanTriggerBurst) - .def("JustStartedEmitting", &PEmitter::JustStartedEmitting); + .property("BurstScale", &PEmitter::GetBurstScale, &PEmitter::SetBurstScale) + .property("EmitAngle", &PEmitter::GetEmitAngle, &PEmitter::SetEmitAngle) + .property("GetThrottle", &PEmitter::GetThrottle, &PEmitter::SetThrottle) + .property("Throttle", &PEmitter::GetThrottle, &PEmitter::SetThrottle) + .property("ThrottleFactor", &PEmitter::GetThrottleFactor) + .property("BurstSpacing", &PEmitter::GetBurstSpacing, &PEmitter::SetBurstSpacing) + .property("EmitCountLimit", &PEmitter::GetEmitCountLimit, &PEmitter::SetEmitCountLimit) + .property("FlashScale", &PEmitter::GetFlashScale, &PEmitter::SetFlashScale) + + .def_readwrite("Emissions", &PEmitter::m_EmissionList, luabind::return_stl_iterator) + + .def("IsEmitting", &PEmitter::IsEmitting) + .def("WasEmitting", &PEmitter::WasEmitting) + .def("EnableEmission", &PEmitter::EnableEmission) + .def("GetEmitVector", &PEmitter::GetEmitVector) + .def("GetRecoilVector", &PEmitter::GetRecoilVector) + .def("EstimateImpulse", &PEmitter::EstimateImpulse) + .def("TriggerBurst", &PEmitter::TriggerBurst) + .def("IsSetToBurst", &PEmitter::IsSetToBurst) + .def("CanTriggerBurst", &PEmitter::CanTriggerBurst) + .def("JustStartedEmitting", &PEmitter::JustStartedEmitting); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, PieSlice) { return ConcreteTypeLuaClassDefinition(PieSlice, Entity) - .property("Type", &PieSlice::GetType, &PieSlice::SetType) - .property("Direction", &PieSlice::GetDirection, &PieSlice::SetDirection) - .property("CanBeMiddleSlice", &PieSlice::GetCanBeMiddleSlice, &PieSlice::SetCanBeMiddleSlice) - .property("OriginalSource", &PieSlice::GetOriginalSource) - .property("Enabled", &PieSlice::IsEnabled, &PieSlice::SetEnabled) - - .property("ScriptPath", &PieSlice::GetScriptPath, &PieSlice::SetScriptPath) - .property("FunctionName", &PieSlice::GetFunctionName, &PieSlice::SetFunctionName) - .property("SubPieMenu", &PieSlice::GetSubPieMenu, &PieSlice::SetSubPieMenu) - - .property("DrawFlippedToMatchAbsoluteAngle", &PieSlice::GetDrawFlippedToMatchAbsoluteAngle, &PieSlice::SetDrawFlippedToMatchAbsoluteAngle) - - .def("ReloadScripts", &PieSlice::ReloadScripts) - - .enum_("SliceType")[ - luabind::value("NoType", PieSlice::SliceType::NoType), - luabind::value("Pickup", PieSlice::SliceType::Pickup), - luabind::value("Drop", PieSlice::SliceType::Drop), - luabind::value("NextItem", PieSlice::SliceType::NextItem), - luabind::value("PreviousItem", PieSlice::SliceType::PreviousItem), - luabind::value("Reload", PieSlice::SliceType::Reload), - luabind::value("BuyMenu", PieSlice::SliceType::BuyMenu), - luabind::value("Stats", PieSlice::SliceType::Stats), - luabind::value("Map", PieSlice::SliceType::Map), - luabind::value("FormSquad", PieSlice::SliceType::FormSquad), - luabind::value("Ceasefire", PieSlice::SliceType::Ceasefire), - luabind::value("Sentry", PieSlice::SliceType::Sentry), - luabind::value("Patrol", PieSlice::SliceType::Patrol), - luabind::value("BrainHunt", PieSlice::SliceType::BrainHunt), - luabind::value("GoldDig", PieSlice::SliceType::GoldDig), - luabind::value("GoTo", PieSlice::SliceType::GoTo), - luabind::value("Return", PieSlice::SliceType::Return), - luabind::value("Stay", PieSlice::SliceType::Stay), - luabind::value("Deliver", PieSlice::SliceType::Deliver), - luabind::value("Scuttle", PieSlice::SliceType::Scuttle), - luabind::value("Done", PieSlice::SliceType::EditorDone), - luabind::value("Load", PieSlice::SliceType::EditorLoad), - luabind::value("Save", PieSlice::SliceType::EditorSave), - luabind::value("New", PieSlice::SliceType::EditorNew), - luabind::value("Pick", PieSlice::SliceType::EditorPick), - luabind::value("Move", PieSlice::SliceType::EditorMove), - luabind::value("Remove", PieSlice::SliceType::EditorRemove), - luabind::value("InFront", PieSlice::SliceType::EditorInFront), - luabind::value("Behind", PieSlice::SliceType::EditorBehind), - luabind::value("ZoomIn", PieSlice::SliceType::EditorZoomIn), - luabind::value("ZoomOut", PieSlice::SliceType::EditorZoomOut), - luabind::value("Team1", PieSlice::SliceType::EditorTeam1), - luabind::value("Team2", PieSlice::SliceType::EditorTeam2), - luabind::value("Team3", PieSlice::SliceType::EditorTeam3), - luabind::value("Team4", PieSlice::SliceType::EditorTeam4) - ]; + .property("Type", &PieSlice::GetType, &PieSlice::SetType) + .property("Direction", &PieSlice::GetDirection, &PieSlice::SetDirection) + .property("CanBeMiddleSlice", &PieSlice::GetCanBeMiddleSlice, &PieSlice::SetCanBeMiddleSlice) + .property("OriginalSource", &PieSlice::GetOriginalSource) + .property("Enabled", &PieSlice::IsEnabled, &PieSlice::SetEnabled) + + .property("ScriptPath", &PieSlice::GetScriptPath, &PieSlice::SetScriptPath) + .property("FunctionName", &PieSlice::GetFunctionName, &PieSlice::SetFunctionName) + .property("SubPieMenu", &PieSlice::GetSubPieMenu, &PieSlice::SetSubPieMenu) + + .property("DrawFlippedToMatchAbsoluteAngle", &PieSlice::GetDrawFlippedToMatchAbsoluteAngle, &PieSlice::SetDrawFlippedToMatchAbsoluteAngle) + + .def("ReloadScripts", &PieSlice::ReloadScripts) + + .enum_("SliceType")[luabind::value("NoType", PieSlice::SliceType::NoType), + luabind::value("Pickup", PieSlice::SliceType::Pickup), + luabind::value("Drop", PieSlice::SliceType::Drop), + luabind::value("NextItem", PieSlice::SliceType::NextItem), + luabind::value("PreviousItem", PieSlice::SliceType::PreviousItem), + luabind::value("Reload", PieSlice::SliceType::Reload), + luabind::value("BuyMenu", PieSlice::SliceType::BuyMenu), + luabind::value("Stats", PieSlice::SliceType::Stats), + luabind::value("Map", PieSlice::SliceType::Map), + luabind::value("FormSquad", PieSlice::SliceType::FormSquad), + luabind::value("Ceasefire", PieSlice::SliceType::Ceasefire), + luabind::value("Sentry", PieSlice::SliceType::Sentry), + luabind::value("Patrol", PieSlice::SliceType::Patrol), + luabind::value("BrainHunt", PieSlice::SliceType::BrainHunt), + luabind::value("GoldDig", PieSlice::SliceType::GoldDig), + luabind::value("GoTo", PieSlice::SliceType::GoTo), + luabind::value("Return", PieSlice::SliceType::Return), + luabind::value("Stay", PieSlice::SliceType::Stay), + luabind::value("Deliver", PieSlice::SliceType::Deliver), + luabind::value("Scuttle", PieSlice::SliceType::Scuttle), + luabind::value("Done", PieSlice::SliceType::EditorDone), + luabind::value("Load", PieSlice::SliceType::EditorLoad), + luabind::value("Save", PieSlice::SliceType::EditorSave), + luabind::value("New", PieSlice::SliceType::EditorNew), + luabind::value("Pick", PieSlice::SliceType::EditorPick), + luabind::value("Move", PieSlice::SliceType::EditorMove), + luabind::value("Remove", PieSlice::SliceType::EditorRemove), + luabind::value("InFront", PieSlice::SliceType::EditorInFront), + luabind::value("Behind", PieSlice::SliceType::EditorBehind), + luabind::value("ZoomIn", PieSlice::SliceType::EditorZoomIn), + luabind::value("ZoomOut", PieSlice::SliceType::EditorZoomOut), + luabind::value("Team1", PieSlice::SliceType::EditorTeam1), + luabind::value("Team2", PieSlice::SliceType::EditorTeam2), + luabind::value("Team3", PieSlice::SliceType::EditorTeam3), + luabind::value("Team4", PieSlice::SliceType::EditorTeam4)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, PieMenu) { return ConcreteTypeLuaClassDefinition(PieMenu, Entity) - .property("Owner", &PieMenu::GetOwner) - .property("Controller", &PieMenu::GetController) - .property("AffectedObject", &PieMenu::GetAffectedObject) - .property("Pos", &PieMenu::GetPos) - .property("RotAngle", &PieMenu::GetRotAngle, &PieMenu::SetRotAngle) - .property("FullInnerRadius", &PieMenu::GetFullInnerRadius, &PieMenu::SetFullInnerRadius) - - .property("PieSlices", &PieMenu::GetPieSlices, luabind::return_stl_iterator) - - .def("IsSubPieMenu", &PieMenu::IsSubPieMenu) - - .def("IsEnabled", &PieMenu::IsEnabled) - .def("IsEnabling", &PieMenu::IsEnabling) - .def("IsDisabling", &PieMenu::IsDisabling) - .def("IsEnablingOrDisabling", &PieMenu::IsEnablingOrDisabling) - .def("IsVisible", &PieMenu::IsVisible) - .def("HasSubPieMenuOpen", &PieMenu::HasSubPieMenuOpen) - - .def("SetAnimationModeToNormal", &PieMenu::SetAnimationModeToNormal) - .def("DoDisableAnimation", &PieMenu::DoDisableAnimation) - .def("Wobble", &PieMenu::Wobble) - .def("FreezeAtRadius", &PieMenu::FreezeAtRadius) - - .def("GetPieCommand", &PieMenu::GetPieCommand) - .def("GetFirstPieSliceByPresetName", &PieMenu::GetFirstPieSliceByPresetName) - .def("GetFirstPieSliceByType", &PieMenu::GetFirstPieSliceByType) - .def("AddPieSlice", &PieMenu::AddPieSlice, luabind::adopt(_2)) - .def("AddPieSlice", &LuaAdaptersPieMenu::AddPieSlice, luabind::adopt(_2)) - .def("AddPieSliceIfPresetNameIsUnique", &PieMenu::AddPieSliceIfPresetNameIsUnique) - .def("AddPieSliceIfPresetNameIsUnique", &LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique1) - .def("AddPieSliceIfPresetNameIsUnique", &LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique2) - .def("RemovePieSlice", &PieMenu::RemovePieSlice, luabind::adopt(luabind::return_value)) - .def("RemovePieSlicesByPresetName", &PieMenu::RemovePieSlicesByPresetName) - .def("RemovePieSlicesByType", &PieMenu::RemovePieSlicesByType) - .def("RemovePieSlicesByOriginalSource", &PieMenu::RemovePieSlicesByOriginalSource) - .def("ReplacePieSlice", &PieMenu::ReplacePieSlice, luabind::adopt(luabind::result) + luabind::adopt(_3)); + .property("Owner", &PieMenu::GetOwner) + .property("Controller", &PieMenu::GetController) + .property("AffectedObject", &PieMenu::GetAffectedObject) + .property("Pos", &PieMenu::GetPos) + .property("RotAngle", &PieMenu::GetRotAngle, &PieMenu::SetRotAngle) + .property("FullInnerRadius", &PieMenu::GetFullInnerRadius, &PieMenu::SetFullInnerRadius) + + .property("PieSlices", &PieMenu::GetPieSlices, luabind::return_stl_iterator) + + .def("IsSubPieMenu", &PieMenu::IsSubPieMenu) + + .def("IsEnabled", &PieMenu::IsEnabled) + .def("IsEnabling", &PieMenu::IsEnabling) + .def("IsDisabling", &PieMenu::IsDisabling) + .def("IsEnablingOrDisabling", &PieMenu::IsEnablingOrDisabling) + .def("IsVisible", &PieMenu::IsVisible) + .def("HasSubPieMenuOpen", &PieMenu::HasSubPieMenuOpen) + + .def("SetAnimationModeToNormal", &PieMenu::SetAnimationModeToNormal) + .def("DoDisableAnimation", &PieMenu::DoDisableAnimation) + .def("Wobble", &PieMenu::Wobble) + .def("FreezeAtRadius", &PieMenu::FreezeAtRadius) + + .def("GetPieCommand", &PieMenu::GetPieCommand) + .def("GetFirstPieSliceByPresetName", &PieMenu::GetFirstPieSliceByPresetName) + .def("GetFirstPieSliceByType", &PieMenu::GetFirstPieSliceByType) + .def("AddPieSlice", &PieMenu::AddPieSlice, luabind::adopt(_2)) + .def("AddPieSlice", &LuaAdaptersPieMenu::AddPieSlice, luabind::adopt(_2)) + .def("AddPieSliceIfPresetNameIsUnique", &PieMenu::AddPieSliceIfPresetNameIsUnique) + .def("AddPieSliceIfPresetNameIsUnique", &LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique1) + .def("AddPieSliceIfPresetNameIsUnique", &LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique2) + .def("RemovePieSlice", &PieMenu::RemovePieSlice, luabind::adopt(luabind::return_value)) + .def("RemovePieSlicesByPresetName", &PieMenu::RemovePieSlicesByPresetName) + .def("RemovePieSlicesByType", &PieMenu::RemovePieSlicesByType) + .def("RemovePieSlicesByOriginalSource", &PieMenu::RemovePieSlicesByOriginalSource) + .def("ReplacePieSlice", &PieMenu::ReplacePieSlice, luabind::adopt(luabind::result) + luabind::adopt(_3)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Round) { return ConcreteTypeLuaClassDefinition(Round, Entity) - .property("NextParticle", &Round::GetNextParticle) - .property("Shell", &Round::GetShell) - .property("FireVel", &Round::GetFireVel) - .property("InheritsFirerVelocity", &Round::GetInheritsFirerVelocity) - .property("ShellVel", &Round::GetShellVel) - .property("Separation", &Round::GetSeparation) - .property("ParticleCount", &Round::ParticleCount) - .property("AILifeTime", &Round::GetAILifeTime) - .property("AIFireVel", &Round::GetAIFireVel) - .property("IsEmpty", &Round::IsEmpty); + .property("NextParticle", &Round::GetNextParticle) + .property("Shell", &Round::GetShell) + .property("FireVel", &Round::GetFireVel) + .property("InheritsFirerVelocity", &Round::GetInheritsFirerVelocity) + .property("ShellVel", &Round::GetShellVel) + .property("Separation", &Round::GetSeparation) + .property("ParticleCount", &Round::ParticleCount) + .property("AILifeTime", &Round::GetAILifeTime) + .property("AIFireVel", &Round::GetAIFireVel) + .property("IsEmpty", &Round::IsEmpty); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Scene) { return ConcreteTypeLuaClassDefinition(Scene, Entity) - .property("Location", &Scene::GetLocation, &Scene::SetLocation) - //.property("Terrain", &Scene::GetTerrain) - .property("Dimensions", &Scene::GetDimensions) - .property("Width", &Scene::GetWidth) - .property("Height", &Scene::GetHeight) - .property("WrapsX", &Scene::WrapsX) - .property("WrapsY", &Scene::WrapsY) - .property("TeamOwnership", &Scene::GetTeamOwnership, &Scene::SetTeamOwnership) - .property("GlobalAcc", &Scene::GetGlobalAcc, &Scene::SetGlobalAcc) - .property("ScenePathSize", &Scene::GetScenePathSize) - - .def_readwrite("Deployments", &Scene::m_Deployments, luabind::return_stl_iterator) - - .def_readonly("BackgroundLayers", &Scene::m_BackLayerList, luabind::return_stl_iterator) - - .def("GetScenePath", &Scene::GetScenePath, luabind::return_stl_iterator) - .def("GetBuildBudget", &Scene::GetBuildBudget) - .def("SetBuildBudget", &Scene::SetBuildBudget) - .def("IsScanScheduled", &Scene::IsScanScheduled) - .def("SetScheduledScan", &Scene::SetScheduledScan) - .def("ClearPlacedObjectSet", &Scene::ClearPlacedObjectSet) - .def("PlaceResidentBrain", &Scene::PlaceResidentBrain) - .def("PlaceResidentBrains", &Scene::PlaceResidentBrains) - .def("RetrieveResidentBrains", &Scene::RetrieveResidentBrains) - .def("GetResidentBrain", &Scene::GetResidentBrain) - .def("SetResidentBrain", &Scene::SetResidentBrain) - .def_readwrite("Areas", &Scene::m_AreaList, luabind::return_stl_iterator) - .def("SetArea", &Scene::SetArea) - .def("HasArea", &Scene::HasArea) - .def("GetArea", (Scene::Area * (Scene:: *)(const std::string &areaName)) &Scene::GetArea) - .def("GetOptionalArea", &Scene::GetOptionalArea) - .def("WithinArea", &Scene::WithinArea) - .def("AddNavigatableArea", &Scene::AddNavigatableArea) - .def("ClearNavigatableAreas", &Scene::ClearNavigatableAreas) - .def("ResetPathFinding", &Scene::ResetPathFinding) - .def("UpdatePathFinding", &Scene::UpdatePathFinding) - .def("PathFindingUpdated", &Scene::PathFindingUpdated) - .def("CalculatePath", &LuaAdaptersScene::CalculatePath1) - .def("CalculatePath", &LuaAdaptersScene::CalculatePath2) - .def("CalculatePathAsync", &LuaAdaptersScene::CalculatePathAsync1) - .def("CalculatePathAsync", &LuaAdaptersScene::CalculatePathAsync2) - - .enum_("PlacedObjectSets")[ - luabind::value("PLACEONLOAD", Scene::PlacedObjectSets::PLACEONLOAD), - luabind::value("BLUEPRINT", Scene::PlacedObjectSets::BLUEPRINT), - luabind::value("AIPLAN", Scene::PlacedObjectSets::AIPLAN), - luabind::value("PLACEDSETSCOUNT", Scene::PlacedObjectSets::PLACEDSETSCOUNT) - ]; + .property("Location", &Scene::GetLocation, &Scene::SetLocation) + //.property("Terrain", &Scene::GetTerrain) + .property("Dimensions", &Scene::GetDimensions) + .property("Width", &Scene::GetWidth) + .property("Height", &Scene::GetHeight) + .property("WrapsX", &Scene::WrapsX) + .property("WrapsY", &Scene::WrapsY) + .property("TeamOwnership", &Scene::GetTeamOwnership, &Scene::SetTeamOwnership) + .property("GlobalAcc", &Scene::GetGlobalAcc, &Scene::SetGlobalAcc) + .property("ScenePathSize", &Scene::GetScenePathSize) + + .def_readwrite("Deployments", &Scene::m_Deployments, luabind::return_stl_iterator) + + .def_readonly("BackgroundLayers", &Scene::m_BackLayerList, luabind::return_stl_iterator) + + .def("GetScenePath", &Scene::GetScenePath, luabind::return_stl_iterator) + .def("GetBuildBudget", &Scene::GetBuildBudget) + .def("SetBuildBudget", &Scene::SetBuildBudget) + .def("IsScanScheduled", &Scene::IsScanScheduled) + .def("SetScheduledScan", &Scene::SetScheduledScan) + .def("ClearPlacedObjectSet", &Scene::ClearPlacedObjectSet) + .def("PlaceResidentBrain", &Scene::PlaceResidentBrain) + .def("PlaceResidentBrains", &Scene::PlaceResidentBrains) + .def("RetrieveResidentBrains", &Scene::RetrieveResidentBrains) + .def("GetResidentBrain", &Scene::GetResidentBrain) + .def("SetResidentBrain", &Scene::SetResidentBrain) + .def_readwrite("Areas", &Scene::m_AreaList, luabind::return_stl_iterator) + .def("SetArea", &Scene::SetArea) + .def("HasArea", &Scene::HasArea) + .def("GetArea", (Scene::Area * (Scene::*)(const std::string& areaName)) & Scene::GetArea) + .def("GetOptionalArea", &Scene::GetOptionalArea) + .def("WithinArea", &Scene::WithinArea) + .def("AddNavigatableArea", &Scene::AddNavigatableArea) + .def("ClearNavigatableAreas", &Scene::ClearNavigatableAreas) + .def("ResetPathFinding", &Scene::ResetPathFinding) + .def("UpdatePathFinding", &Scene::UpdatePathFinding) + .def("PathFindingUpdated", &Scene::PathFindingUpdated) + .def("CalculatePath", &LuaAdaptersScene::CalculatePath1) + .def("CalculatePath", &LuaAdaptersScene::CalculatePath2) + .def("CalculatePathAsync", &LuaAdaptersScene::CalculatePathAsync1) + .def("CalculatePathAsync", &LuaAdaptersScene::CalculatePathAsync2) + + .enum_("PlacedObjectSets")[luabind::value("PLACEONLOAD", Scene::PlacedObjectSets::PLACEONLOAD), + luabind::value("BLUEPRINT", Scene::PlacedObjectSets::BLUEPRINT), + luabind::value("AIPLAN", Scene::PlacedObjectSets::AIPLAN), + luabind::value("PLACEDSETSCOUNT", Scene::PlacedObjectSets::PLACEDSETSCOUNT)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SceneArea) { return luabind::class_("Area") - .def(luabind::constructor<>()) - .def(luabind::constructor()) - .def(luabind::constructor()) - - .property("ClassName", &Scene::Area::GetClassName) - .property("Name", &Scene::Area::GetName) - .property("FirstBox", &Scene::Area::GetFirstBox) - .property("Center", &Scene::Area::GetCenterPoint) - .property("RandomPoint", &Scene::Area::GetRandomPoint) - - .def("Reset", &Scene::Area::Reset) - .def_readwrite("Boxes", &Scene::Area::m_BoxList, luabind::return_stl_iterator) - .def("AddBox", &Scene::Area::AddBox) - .def("RemoveBox", &Scene::Area::RemoveBox) - .def("HasNoArea", &Scene::Area::HasNoArea) - .def("IsInside", &Scene::Area::IsInside) - .def("IsInsideX", &Scene::Area::IsInsideX) - .def("IsInsideY", &Scene::Area::IsInsideY) - .def("GetBoxInside", &Scene::Area::GetBoxInside) - .def("RemoveBoxInside", &Scene::Area::RemoveBoxInside) - .def("GetCenterPoint", &Scene::Area::GetCenterPoint) - .def("GetRandomPoint", &Scene::Area::GetRandomPoint); + .def(luabind::constructor<>()) + .def(luabind::constructor()) + .def(luabind::constructor()) + + .property("ClassName", &Scene::Area::GetClassName) + .property("Name", &Scene::Area::GetName) + .property("FirstBox", &Scene::Area::GetFirstBox) + .property("Center", &Scene::Area::GetCenterPoint) + .property("RandomPoint", &Scene::Area::GetRandomPoint) + + .def("Reset", &Scene::Area::Reset) + .def_readwrite("Boxes", &Scene::Area::m_BoxList, luabind::return_stl_iterator) + .def("AddBox", &Scene::Area::AddBox) + .def("RemoveBox", &Scene::Area::RemoveBox) + .def("HasNoArea", &Scene::Area::HasNoArea) + .def("IsInside", &Scene::Area::IsInside) + .def("IsInsideX", &Scene::Area::IsInsideX) + .def("IsInsideY", &Scene::Area::IsInsideY) + .def("GetBoxInside", &Scene::Area::GetBoxInside) + .def("RemoveBoxInside", &Scene::Area::RemoveBoxInside) + .def("GetCenterPoint", &Scene::Area::GetCenterPoint) + .def("GetRandomPoint", &Scene::Area::GetRandomPoint); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SceneLayer) { return luabind::class_("SceneLayer"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SceneObject) { return AbstractTypeLuaClassDefinition(SceneObject, Entity) - .property("Pos", &SceneObject::GetPos, &SceneObject::SetPos) - .property("HFlipped", &SceneObject::IsHFlipped, &SceneObject::SetHFlipped) - .property("RotAngle", &SceneObject::GetRotAngle, &SceneObject::SetRotAngle) - .property("Team", &SceneObject::GetTeam, &SceneObject::SetTeam) - .property("PlacedByPlayer", &SceneObject::GetPlacedByPlayer, &SceneObject::SetPlacedByPlayer) - .property("Buyable", &SceneObject::IsBuyable) - - .property("BuyableMode", &LuaAdaptersSceneObject::GetBuyableMode) - - .def("IsOnScenePoint", &SceneObject::IsOnScenePoint) - .def("GetGoldValue", &SceneObject::GetGoldValueOld) - .def("GetGoldValue", &SceneObject::GetGoldValue) - .def("SetGoldValue", &SceneObject::SetGoldValue) - .def("GetGoldValueString", &SceneObject::GetGoldValueString) - - .def("GetTotalValue", &SceneObject::GetTotalValue) - .def("GetTotalValue", &LuaAdaptersSceneObject::GetTotalValue) - - .enum_("BuyableMode")[ - luabind::value("NORESTRICTIONS", static_cast(SceneObject::BuyableMode::NoRestrictions)), - luabind::value("BUYMENUONLY", static_cast(SceneObject::BuyableMode::BuyMenuOnly)), - luabind::value("OBJECTPICKERONLY", static_cast(SceneObject::BuyableMode::ObjectPickerOnly)), - luabind::value("SCRIPTONLY", static_cast(SceneObject::BuyableMode::ScriptOnly))]; - + .property("Pos", &SceneObject::GetPos, &SceneObject::SetPos) + .property("HFlipped", &SceneObject::IsHFlipped, &SceneObject::SetHFlipped) + .property("RotAngle", &SceneObject::GetRotAngle, &SceneObject::SetRotAngle) + .property("Team", &SceneObject::GetTeam, &SceneObject::SetTeam) + .property("PlacedByPlayer", &SceneObject::GetPlacedByPlayer, &SceneObject::SetPlacedByPlayer) + .property("Buyable", &SceneObject::IsBuyable) + + .property("BuyableMode", &LuaAdaptersSceneObject::GetBuyableMode) + + .def("IsOnScenePoint", &SceneObject::IsOnScenePoint) + .def("GetGoldValue", &SceneObject::GetGoldValueOld) + .def("GetGoldValue", &SceneObject::GetGoldValue) + .def("SetGoldValue", &SceneObject::SetGoldValue) + .def("GetGoldValueString", &SceneObject::GetGoldValueString) + + .def("GetTotalValue", &SceneObject::GetTotalValue) + .def("GetTotalValue", &LuaAdaptersSceneObject::GetTotalValue) + + .enum_("BuyableMode")[luabind::value("NORESTRICTIONS", static_cast(SceneObject::BuyableMode::NoRestrictions)), + luabind::value("BUYMENUONLY", static_cast(SceneObject::BuyableMode::BuyMenuOnly)), + luabind::value("OBJECTPICKERONLY", static_cast(SceneObject::BuyableMode::ObjectPickerOnly)), + luabind::value("SCRIPTONLY", static_cast(SceneObject::BuyableMode::ScriptOnly))]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SLBackground) { return luabind::class_("SLBackground") - .property("Frame", &SLBackground::GetFrame, &SLBackground::SetFrame) - .property("SpriteAnimMode", &SLBackground::GetSpriteAnimMode, &SLBackground::SetSpriteAnimMode) - .property("SpriteAnimDuration", &SLBackground::GetSpriteAnimDuration, &SLBackground::SetSpriteAnimDuration) - .property("IsAnimatedManually", &SLBackground::IsAnimatedManually, &SLBackground::SetAnimatedManually) - .property("AutoScrollX", &SLBackground::GetAutoScrollX, &SLBackground::SetAutoScrollX) - .property("AutoScrollY", &SLBackground::GetAutoScrollY, &SLBackground::SetAutoScrollY) - .property("AutoScrollInterval", &SLBackground::GetAutoScrollStepInterval, &SLBackground::SetAutoScrollStepInterval) - .property("AutoScrollStep", &SLBackground::GetAutoScrollStep, &SLBackground::SetAutoScrollStep) - .property("AutoScrollStepX", &SLBackground::GetAutoScrollStepX, &SLBackground::SetAutoScrollStepX) - .property("AutoScrollStepY", &SLBackground::GetAutoScrollStepY, &SLBackground::SetAutoScrollStepY) - - .def("IsAutoScrolling", &SLBackground::IsAutoScrolling); + .property("Frame", &SLBackground::GetFrame, &SLBackground::SetFrame) + .property("SpriteAnimMode", &SLBackground::GetSpriteAnimMode, &SLBackground::SetSpriteAnimMode) + .property("SpriteAnimDuration", &SLBackground::GetSpriteAnimDuration, &SLBackground::SetSpriteAnimDuration) + .property("IsAnimatedManually", &SLBackground::IsAnimatedManually, &SLBackground::SetAnimatedManually) + .property("AutoScrollX", &SLBackground::GetAutoScrollX, &SLBackground::SetAutoScrollX) + .property("AutoScrollY", &SLBackground::GetAutoScrollY, &SLBackground::SetAutoScrollY) + .property("AutoScrollInterval", &SLBackground::GetAutoScrollStepInterval, &SLBackground::SetAutoScrollStepInterval) + .property("AutoScrollStep", &SLBackground::GetAutoScrollStep, &SLBackground::SetAutoScrollStep) + .property("AutoScrollStepX", &SLBackground::GetAutoScrollStepX, &SLBackground::SetAutoScrollStepX) + .property("AutoScrollStepY", &SLBackground::GetAutoScrollStepY, &SLBackground::SetAutoScrollStepY) + + .def("IsAutoScrolling", &SLBackground::IsAutoScrolling); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SoundContainer) { return ConcreteTypeLuaClassDefinition(SoundContainer, Entity) - .def(luabind::constructor<>()) - - .property("SoundOverlapMode", &SoundContainer::GetSoundOverlapMode, &SoundContainer::SetSoundOverlapMode) - .property("BusRouting", &SoundContainer::GetBusRouting, &SoundContainer::SetBusRouting) - .property("Immobile", &SoundContainer::IsImmobile, &SoundContainer::SetImmobile) - .property("AttenuationStartDistance", &SoundContainer::GetAttenuationStartDistance, &SoundContainer::SetAttenuationStartDistance) - .property("CustomPanValue", &SoundContainer::GetCustomPanValue, &SoundContainer::SetCustomPanValue) - .property("PanningStrengthMultiplier", &SoundContainer::GetPanningStrengthMultiplier, &SoundContainer::SetPanningStrengthMultiplier) - .property("Loops", &SoundContainer::GetLoopSetting, &SoundContainer::SetLoopSetting) - .property("Priority", &SoundContainer::GetPriority, &SoundContainer::SetPriority) - .property("AffectedByGlobalPitch", &SoundContainer::IsAffectedByGlobalPitch, &SoundContainer::SetAffectedByGlobalPitch) - .property("Pos", &SoundContainer::GetPosition, &SoundContainer::SetPosition) - .property("Volume", &SoundContainer::GetVolume, &SoundContainer::SetVolume) - .property("Pitch", &SoundContainer::GetPitch, &SoundContainer::SetPitch) - .property("PitchVariation", &SoundContainer::GetPitchVariation, &SoundContainer::SetPitchVariation) - - .def("HasAnySounds", &SoundContainer::HasAnySounds) - .def("GetTopLevelSoundSet", &SoundContainer::GetTopLevelSoundSet) - .def("SetTopLevelSoundSet", &SoundContainer::SetTopLevelSoundSet) - .def("IsBeingPlayed", &SoundContainer::IsBeingPlayed) - .def("Play", (bool (SoundContainer:: *)()) &SoundContainer::Play) - .def("Play", (bool (SoundContainer:: *)(const int player)) &SoundContainer::Play) - .def("Play", (bool (SoundContainer:: *)(const Vector &position)) &SoundContainer::Play) - .def("Play", (bool (SoundContainer:: *)(const Vector &position, int player)) &SoundContainer::Play) - .def("Stop", (bool (SoundContainer:: *)()) &SoundContainer::Stop) - .def("Stop", (bool (SoundContainer:: *)(int player)) &SoundContainer::Stop) - .def("Restart", (bool (SoundContainer:: *)()) &SoundContainer::Restart) - .def("Restart", (bool (SoundContainer:: *)(int player)) &SoundContainer::Restart) - .def("FadeOut", &SoundContainer::FadeOut) - - .enum_("BusRouting")[ - luabind::value("SFX", SoundContainer::BusRouting::SFX), - luabind::value("UI", SoundContainer::BusRouting::UI), - luabind::value("MUSIC", SoundContainer::BusRouting::MUSIC) - ] - - .enum_("SoundOverlapMode")[ - luabind::value("OVERLAP", SoundContainer::SoundOverlapMode::OVERLAP), - luabind::value("RESTART", SoundContainer::SoundOverlapMode::RESTART), - luabind::value("IGNORE_PLAY", SoundContainer::SoundOverlapMode::IGNORE_PLAY) - ]; + .def(luabind::constructor<>()) + + .property("SoundOverlapMode", &SoundContainer::GetSoundOverlapMode, &SoundContainer::SetSoundOverlapMode) + .property("BusRouting", &SoundContainer::GetBusRouting, &SoundContainer::SetBusRouting) + .property("Immobile", &SoundContainer::IsImmobile, &SoundContainer::SetImmobile) + .property("AttenuationStartDistance", &SoundContainer::GetAttenuationStartDistance, &SoundContainer::SetAttenuationStartDistance) + .property("CustomPanValue", &SoundContainer::GetCustomPanValue, &SoundContainer::SetCustomPanValue) + .property("PanningStrengthMultiplier", &SoundContainer::GetPanningStrengthMultiplier, &SoundContainer::SetPanningStrengthMultiplier) + .property("Loops", &SoundContainer::GetLoopSetting, &SoundContainer::SetLoopSetting) + .property("Priority", &SoundContainer::GetPriority, &SoundContainer::SetPriority) + .property("AffectedByGlobalPitch", &SoundContainer::IsAffectedByGlobalPitch, &SoundContainer::SetAffectedByGlobalPitch) + .property("Pos", &SoundContainer::GetPosition, &SoundContainer::SetPosition) + .property("Volume", &SoundContainer::GetVolume, &SoundContainer::SetVolume) + .property("Pitch", &SoundContainer::GetPitch, &SoundContainer::SetPitch) + .property("PitchVariation", &SoundContainer::GetPitchVariation, &SoundContainer::SetPitchVariation) + + .def("HasAnySounds", &SoundContainer::HasAnySounds) + .def("GetTopLevelSoundSet", &SoundContainer::GetTopLevelSoundSet) + .def("SetTopLevelSoundSet", &SoundContainer::SetTopLevelSoundSet) + .def("IsBeingPlayed", &SoundContainer::IsBeingPlayed) + .def("Play", (bool(SoundContainer::*)()) & SoundContainer::Play) + .def("Play", (bool(SoundContainer::*)(const int player)) & SoundContainer::Play) + .def("Play", (bool(SoundContainer::*)(const Vector& position)) & SoundContainer::Play) + .def("Play", (bool(SoundContainer::*)(const Vector& position, int player)) & SoundContainer::Play) + .def("Stop", (bool(SoundContainer::*)()) & SoundContainer::Stop) + .def("Stop", (bool(SoundContainer::*)(int player)) & SoundContainer::Stop) + .def("Restart", (bool(SoundContainer::*)()) & SoundContainer::Restart) + .def("Restart", (bool(SoundContainer::*)(int player)) & SoundContainer::Restart) + .def("FadeOut", &SoundContainer::FadeOut) + + .enum_("BusRouting")[luabind::value("SFX", SoundContainer::BusRouting::SFX), + luabind::value("UI", SoundContainer::BusRouting::UI), + luabind::value("MUSIC", SoundContainer::BusRouting::MUSIC)] + + .enum_("SoundOverlapMode")[luabind::value("OVERLAP", SoundContainer::SoundOverlapMode::OVERLAP), + luabind::value("RESTART", SoundContainer::SoundOverlapMode::RESTART), + luabind::value("IGNORE_PLAY", SoundContainer::SoundOverlapMode::IGNORE_PLAY)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SoundSet) { return luabind::class_("SoundSet") - .def(luabind::constructor<>()) + .def(luabind::constructor<>()) - .property("SoundSelectionCycleMode", &SoundSet::GetSoundSelectionCycleMode, &SoundSet::SetSoundSelectionCycleMode) + .property("SoundSelectionCycleMode", &SoundSet::GetSoundSelectionCycleMode, &SoundSet::SetSoundSelectionCycleMode) - .def_readonly("SubSoundSets", &SoundSet::m_SubSoundSets, luabind::return_stl_iterator) + .def_readonly("SubSoundSets", &SoundSet::m_SubSoundSets, luabind::return_stl_iterator) - .def("HasAnySounds", &SoundSet::HasAnySounds) - .def("SelectNextSounds", &SoundSet::SelectNextSounds) - .def("AddSound", (void (SoundSet:: *)(const std::string &soundFilePath)) &SoundSet::AddSound) - .def("AddSound", (void (SoundSet:: *)(const std::string &soundFilePath, const Vector &offset, float minimumAudibleDistance, float attenuationStartDistance)) &SoundSet::AddSound) - .def("RemoveSound", (bool (SoundSet:: *)(const std::string &soundFilePath)) &SoundSet::RemoveSound) - .def("RemoveSound", (bool (SoundSet:: *)(const std::string &soundFilePath, bool removeFromSubSoundSets)) &SoundSet::RemoveSound) - .def("AddSoundSet", &SoundSet::AddSoundSet) + .def("HasAnySounds", &SoundSet::HasAnySounds) + .def("SelectNextSounds", &SoundSet::SelectNextSounds) + .def("AddSound", (void(SoundSet::*)(const std::string& soundFilePath)) & SoundSet::AddSound) + .def("AddSound", (void(SoundSet::*)(const std::string& soundFilePath, const Vector& offset, float minimumAudibleDistance, float attenuationStartDistance)) & SoundSet::AddSound) + .def("RemoveSound", (bool(SoundSet::*)(const std::string& soundFilePath)) & SoundSet::RemoveSound) + .def("RemoveSound", (bool(SoundSet::*)(const std::string& soundFilePath, bool removeFromSubSoundSets)) & SoundSet::RemoveSound) + .def("AddSoundSet", &SoundSet::AddSoundSet) - .enum_("SoundSelectionCycleMode")[ - luabind::value("RANDOM", SoundSet::SoundSelectionCycleMode::RANDOM), - luabind::value("FORWARDS", SoundSet::SoundSelectionCycleMode::FORWARDS), - luabind::value("ALL", SoundSet::SoundSelectionCycleMode::ALL) - ]; + .enum_("SoundSelectionCycleMode")[luabind::value("RANDOM", SoundSet::SoundSelectionCycleMode::RANDOM), + luabind::value("FORWARDS", SoundSet::SoundSelectionCycleMode::FORWARDS), + luabind::value("ALL", SoundSet::SoundSelectionCycleMode::ALL)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, TDExplosive) { return ConcreteTypeLuaClassDefinition(TDExplosive, ThrownDevice) - .property("IsAnimatedManually", &TDExplosive::IsAnimatedManually, &TDExplosive::SetAnimatedManually); + .property("IsAnimatedManually", &TDExplosive::IsAnimatedManually, &TDExplosive::SetAnimatedManually); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, TerrainObject) { return ConcreteTypeLuaClassDefinition(TerrainObject, SceneObject) - .def("GetBitmapOffset", &TerrainObject::GetBitmapOffset) - .def("GetBitmapWidth", &TerrainObject::GetBitmapWidth) - .def("GetBitmapHeight", &TerrainObject::GetBitmapHeight); + .def("GetBitmapOffset", &TerrainObject::GetBitmapOffset) + .def("GetBitmapWidth", &TerrainObject::GetBitmapWidth) + .def("GetBitmapHeight", &TerrainObject::GetBitmapHeight); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ThrownDevice) { return ConcreteTypeLuaClassDefinition(ThrownDevice, HeldDevice) - .property("MinThrowVel", &ThrownDevice::GetMinThrowVel, &ThrownDevice::SetMinThrowVel) - .property("MaxThrowVel", &ThrownDevice::GetMaxThrowVel, &ThrownDevice::SetMaxThrowVel) - .property("StartThrowOffset", &ThrownDevice::GetStartThrowOffset, &ThrownDevice::SetStartThrowOffset) - .property("EndThrowOffset", &ThrownDevice::GetEndThrowOffset, &ThrownDevice::SetEndThrowOffset) + .property("MinThrowVel", &ThrownDevice::GetMinThrowVel, &ThrownDevice::SetMinThrowVel) + .property("MaxThrowVel", &ThrownDevice::GetMaxThrowVel, &ThrownDevice::SetMaxThrowVel) + .property("StartThrowOffset", &ThrownDevice::GetStartThrowOffset, &ThrownDevice::SetStartThrowOffset) + .property("EndThrowOffset", &ThrownDevice::GetEndThrowOffset, &ThrownDevice::SetEndThrowOffset) - .def("GetCalculatedMaxThrowVelIncludingArmThrowStrength", &ThrownDevice::GetCalculatedMaxThrowVelIncludingArmThrowStrength); + .def("GetCalculatedMaxThrowVelIncludingArmThrowStrength", &ThrownDevice::GetCalculatedMaxThrowVelIncludingArmThrowStrength); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Turret) { return ConcreteTypeLuaClassDefinition(Turret, Attachable) - .property("MountedDevice", &Turret::GetFirstMountedDevice, &LuaAdaptersPropertyOwnershipSafetyFaker::TurretSetFirstMountedDevice) - .property("MountedDeviceRotationOffset", &Turret::GetMountedDeviceRotationOffset, &Turret::SetMountedDeviceRotationOffset) + .property("MountedDevice", &Turret::GetFirstMountedDevice, &LuaAdaptersPropertyOwnershipSafetyFaker::TurretSetFirstMountedDevice) + .property("MountedDeviceRotationOffset", &Turret::GetMountedDeviceRotationOffset, &Turret::SetMountedDeviceRotationOffset) - .def("GetMountedDevices", &Turret::GetMountedDevices, luabind::return_stl_iterator) - .def("AddMountedDevice", &Turret::AddMountedDevice, luabind::adopt(_2)) - .def("AddMountedDevice", &LuaAdaptersTurret::AddMountedFirearm, luabind::adopt(_2)); + .def("GetMountedDevices", &Turret::GetMountedDevices, luabind::return_stl_iterator) + .def("AddMountedDevice", &Turret::AddMountedDevice, luabind::adopt(_2)) + .def("AddMountedDevice", &LuaAdaptersTurret::AddMountedFirearm, luabind::adopt(_2)); } -} +} // namespace RTE diff --git a/Source/Lua/LuaBindingsGUI.cpp b/Source/Lua/LuaBindingsGUI.cpp index 17bcecb05e..e680c8f0cf 100644 --- a/Source/Lua/LuaBindingsGUI.cpp +++ b/Source/Lua/LuaBindingsGUI.cpp @@ -4,105 +4,97 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(GUILuaBindings, GUIBanner) { return luabind::class_("GUIBanner") - .property("BannerText", &GUIBanner::GetBannerText) - .property("AnimState", &GUIBanner::GetAnimState) - .property("Kerning", &GUIBanner::GetKerning, &GUIBanner::SetKerning) - - .def("IsVisible", &GUIBanner::IsVisible) - .def("ShowText", &GUIBanner::ShowText) - .def("HideText", &GUIBanner::HideText) - .def("ClearText", &GUIBanner::ClearText) - - .enum_("AnimMode")[ - luabind::value("BLINKING", GUIBanner::AnimMode::BLINKING), - luabind::value("FLYBYLEFTWARD", GUIBanner::AnimMode::FLYBYLEFTWARD), - luabind::value("FLYBYRIGHTWARD", GUIBanner::AnimMode::FLYBYRIGHTWARD), - luabind::value("ANIMMODECOUNT", GUIBanner::AnimMode::ANIMMODECOUNT) - ] - .enum_("AnimState")[ - luabind::value("NOTSTARTED", GUIBanner::AnimState::NOTSTARTED), - luabind::value("SHOWING", GUIBanner::AnimState::SHOWING), - luabind::value("SHOW", GUIBanner::AnimState::SHOW), - luabind::value("HIDING", GUIBanner::AnimState::HIDING), - luabind::value("OVER", GUIBanner::AnimState::OVER), - luabind::value("ANIMSTATECOUNT", GUIBanner::AnimState::ANIMSTATECOUNT) - ] - .enum_("BannerColor")[ - luabind::value("RED", GameActivity::BannerColor::RED), - luabind::value("YELLOW", GameActivity::BannerColor::YELLOW) - ]; + .property("BannerText", &GUIBanner::GetBannerText) + .property("AnimState", &GUIBanner::GetAnimState) + .property("Kerning", &GUIBanner::GetKerning, &GUIBanner::SetKerning) + + .def("IsVisible", &GUIBanner::IsVisible) + .def("ShowText", &GUIBanner::ShowText) + .def("HideText", &GUIBanner::HideText) + .def("ClearText", &GUIBanner::ClearText) + + .enum_("AnimMode")[luabind::value("BLINKING", GUIBanner::AnimMode::BLINKING), + luabind::value("FLYBYLEFTWARD", GUIBanner::AnimMode::FLYBYLEFTWARD), + luabind::value("FLYBYRIGHTWARD", GUIBanner::AnimMode::FLYBYRIGHTWARD), + luabind::value("ANIMMODECOUNT", GUIBanner::AnimMode::ANIMMODECOUNT)] + .enum_("AnimState")[luabind::value("NOTSTARTED", GUIBanner::AnimState::NOTSTARTED), + luabind::value("SHOWING", GUIBanner::AnimState::SHOWING), + luabind::value("SHOW", GUIBanner::AnimState::SHOW), + luabind::value("HIDING", GUIBanner::AnimState::HIDING), + luabind::value("OVER", GUIBanner::AnimState::OVER), + luabind::value("ANIMSTATECOUNT", GUIBanner::AnimState::ANIMSTATECOUNT)] + .enum_("BannerColor")[luabind::value("RED", GameActivity::BannerColor::RED), + luabind::value("YELLOW", GameActivity::BannerColor::YELLOW)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(GUILuaBindings, BuyMenuGUI) { return luabind::class_("BuyMenuGUI") - .property("ShowOnlyOwnedItems", &BuyMenuGUI::GetOnlyShowOwnedItems, &BuyMenuGUI::SetOnlyShowOwnedItems) - .property("EnforceMaxPassengersConstraint", &BuyMenuGUI::EnforceMaxPassengersConstraint, &BuyMenuGUI::SetEnforceMaxPassengersConstraint) - .property("EnforceMaxMassConstraint", &BuyMenuGUI::EnforceMaxMassConstraint, &BuyMenuGUI::SetEnforceMaxMassConstraint) - - .def("SetMetaPlayer", &BuyMenuGUI::SetMetaPlayer) - .def("SetNativeTechModule", &BuyMenuGUI::SetNativeTechModule) - .def("SetForeignCostMultiplier", &BuyMenuGUI::SetForeignCostMultiplier) - .def("SetModuleExpanded", &BuyMenuGUI::SetModuleExpanded) - .def("LoadAllLoadoutsFromFile", &BuyMenuGUI::LoadAllLoadoutsFromFile) - .def("AddAllowedItem", &BuyMenuGUI::AddAllowedItem) - .def("RemoveAllowedItem", &BuyMenuGUI::RemoveAllowedItem) - .def("ClearAllowedItems", &BuyMenuGUI::ClearAllowedItems) - .def("AddAlwaysAllowedItem", &BuyMenuGUI::AddAlwaysAllowedItem) - .def("RemoveAlwaysAllowedItem", &BuyMenuGUI::RemoveAlwaysAllowedItem) - .def("ClearAlwaysAllowedItems", &BuyMenuGUI::ClearAlwaysAllowedItems) - .def("AddProhibitedItem", &BuyMenuGUI::AddProhibitedItem) - .def("RemoveProhibitedItem", &BuyMenuGUI::RemoveProhibitedItem) - .def("ClearProhibitedItems", &BuyMenuGUI::ClearProhibitedItems) - .def("ForceRefresh", &BuyMenuGUI::ForceRefresh) - .def("SetOwnedItemsAmount", &BuyMenuGUI::SetOwnedItemsAmount) - .def("GetOwnedItemsAmount", &BuyMenuGUI::GetOwnedItemsAmount) - .def("SetBannerImage", &BuyMenuGUI::SetBannerImage) - .def("SetLogoImage", &BuyMenuGUI::SetLogoImage) - .def("ClearCartList", &BuyMenuGUI::ClearCartList) - .def("LoadDefaultLoadoutToCart", &BuyMenuGUI::LoadDefaultLoadoutToCart) - .def("GetOrderList", &LuaAdaptersBuyMenuGUI::GetOrderList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("GetTotalCartCost", &BuyMenuGUI::GetTotalCartCost) - .def("GetTotalOrderCost", &BuyMenuGUI::GetTotalOrderCost) - .def("GetTotalOrderMass", &BuyMenuGUI::GetTotalOrderMass) - .def("GetTotalOrderPassengers", &BuyMenuGUI::GetTotalOrderPassengers); + .property("ShowOnlyOwnedItems", &BuyMenuGUI::GetOnlyShowOwnedItems, &BuyMenuGUI::SetOnlyShowOwnedItems) + .property("EnforceMaxPassengersConstraint", &BuyMenuGUI::EnforceMaxPassengersConstraint, &BuyMenuGUI::SetEnforceMaxPassengersConstraint) + .property("EnforceMaxMassConstraint", &BuyMenuGUI::EnforceMaxMassConstraint, &BuyMenuGUI::SetEnforceMaxMassConstraint) + + .def("SetMetaPlayer", &BuyMenuGUI::SetMetaPlayer) + .def("SetNativeTechModule", &BuyMenuGUI::SetNativeTechModule) + .def("SetForeignCostMultiplier", &BuyMenuGUI::SetForeignCostMultiplier) + .def("SetModuleExpanded", &BuyMenuGUI::SetModuleExpanded) + .def("LoadAllLoadoutsFromFile", &BuyMenuGUI::LoadAllLoadoutsFromFile) + .def("AddAllowedItem", &BuyMenuGUI::AddAllowedItem) + .def("RemoveAllowedItem", &BuyMenuGUI::RemoveAllowedItem) + .def("ClearAllowedItems", &BuyMenuGUI::ClearAllowedItems) + .def("AddAlwaysAllowedItem", &BuyMenuGUI::AddAlwaysAllowedItem) + .def("RemoveAlwaysAllowedItem", &BuyMenuGUI::RemoveAlwaysAllowedItem) + .def("ClearAlwaysAllowedItems", &BuyMenuGUI::ClearAlwaysAllowedItems) + .def("AddProhibitedItem", &BuyMenuGUI::AddProhibitedItem) + .def("RemoveProhibitedItem", &BuyMenuGUI::RemoveProhibitedItem) + .def("ClearProhibitedItems", &BuyMenuGUI::ClearProhibitedItems) + .def("ForceRefresh", &BuyMenuGUI::ForceRefresh) + .def("SetOwnedItemsAmount", &BuyMenuGUI::SetOwnedItemsAmount) + .def("GetOwnedItemsAmount", &BuyMenuGUI::GetOwnedItemsAmount) + .def("SetBannerImage", &BuyMenuGUI::SetBannerImage) + .def("SetLogoImage", &BuyMenuGUI::SetLogoImage) + .def("ClearCartList", &BuyMenuGUI::ClearCartList) + .def("LoadDefaultLoadoutToCart", &BuyMenuGUI::LoadDefaultLoadoutToCart) + .def("GetOrderList", &LuaAdaptersBuyMenuGUI::GetOrderList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("GetTotalCartCost", &BuyMenuGUI::GetTotalCartCost) + .def("GetTotalOrderCost", &BuyMenuGUI::GetTotalOrderCost) + .def("GetTotalOrderMass", &BuyMenuGUI::GetTotalOrderMass) + .def("GetTotalOrderPassengers", &BuyMenuGUI::GetTotalOrderPassengers); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(GUILuaBindings, SceneEditorGUI) { return luabind::class_("SceneEditorGUI") - .property("EditorMode", &SceneEditorGUI::GetEditorGUIMode, &SceneEditorGUI::SetEditorGUIMode) - - .def("SetCursorPos", &SceneEditorGUI::SetCursorPos) - .def("GetCurrentObject", &SceneEditorGUI::GetCurrentObject) - .def("SetCurrentObject", &SceneEditorGUI::SetCurrentObject) - .def("SetModuleSpace", &SceneEditorGUI::SetModuleSpace) - .def("SetNativeTechModule", &SceneEditorGUI::SetNativeTechModule) - .def("SetForeignCostMultiplier", &SceneEditorGUI::SetForeignCostMultiplier) - .def("TestBrainResidence", &SceneEditorGUI::TestBrainResidence) - .def("Update", &SceneEditorGUI::Update) //Gacyr Note: I hate this being here but it's necessary for some metagame bullshit. - - .enum_("EditorGUIMode")[ - luabind::value("INACTIVE", SceneEditorGUI::EditorGUIMode::INACTIVE), - luabind::value("PICKINGOBJECT", SceneEditorGUI::EditorGUIMode::PICKINGOBJECT), - luabind::value("ADDINGOBJECT", SceneEditorGUI::EditorGUIMode::ADDINGOBJECT), - luabind::value("INSTALLINGBRAIN", SceneEditorGUI::EditorGUIMode::INSTALLINGBRAIN), - luabind::value("PLACINGOBJECT", SceneEditorGUI::EditorGUIMode::PLACINGOBJECT), - luabind::value("MOVINGOBJECT", SceneEditorGUI::EditorGUIMode::MOVINGOBJECT), - luabind::value("DELETINGOBJECT", SceneEditorGUI::EditorGUIMode::DELETINGOBJECT), - luabind::value("PLACEINFRONT", SceneEditorGUI::EditorGUIMode::PLACEINFRONT), - luabind::value("PLACEBEHIND", SceneEditorGUI::EditorGUIMode::PLACEBEHIND), - luabind::value("DONEEDITING", SceneEditorGUI::EditorGUIMode::DONEEDITING), - luabind::value("EDITORGUIMODECOUNT", SceneEditorGUI::EditorGUIMode::EDITORGUIMODECOUNT) - ]; + .property("EditorMode", &SceneEditorGUI::GetEditorGUIMode, &SceneEditorGUI::SetEditorGUIMode) + + .def("SetCursorPos", &SceneEditorGUI::SetCursorPos) + .def("GetCurrentObject", &SceneEditorGUI::GetCurrentObject) + .def("SetCurrentObject", &SceneEditorGUI::SetCurrentObject) + .def("SetModuleSpace", &SceneEditorGUI::SetModuleSpace) + .def("SetNativeTechModule", &SceneEditorGUI::SetNativeTechModule) + .def("SetForeignCostMultiplier", &SceneEditorGUI::SetForeignCostMultiplier) + .def("TestBrainResidence", &SceneEditorGUI::TestBrainResidence) + .def("Update", &SceneEditorGUI::Update) // Gacyr Note: I hate this being here but it's necessary for some metagame bullshit. + + .enum_("EditorGUIMode")[luabind::value("INACTIVE", SceneEditorGUI::EditorGUIMode::INACTIVE), + luabind::value("PICKINGOBJECT", SceneEditorGUI::EditorGUIMode::PICKINGOBJECT), + luabind::value("ADDINGOBJECT", SceneEditorGUI::EditorGUIMode::ADDINGOBJECT), + luabind::value("INSTALLINGBRAIN", SceneEditorGUI::EditorGUIMode::INSTALLINGBRAIN), + luabind::value("PLACINGOBJECT", SceneEditorGUI::EditorGUIMode::PLACINGOBJECT), + luabind::value("MOVINGOBJECT", SceneEditorGUI::EditorGUIMode::MOVINGOBJECT), + luabind::value("DELETINGOBJECT", SceneEditorGUI::EditorGUIMode::DELETINGOBJECT), + luabind::value("PLACEINFRONT", SceneEditorGUI::EditorGUIMode::PLACEINFRONT), + luabind::value("PLACEBEHIND", SceneEditorGUI::EditorGUIMode::PLACEBEHIND), + luabind::value("DONEEDITING", SceneEditorGUI::EditorGUIMode::DONEEDITING), + luabind::value("EDITORGUIMODECOUNT", SceneEditorGUI::EditorGUIMode::EDITORGUIMODECOUNT)]; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Lua/LuaBindingsInput.cpp b/Source/Lua/LuaBindingsInput.cpp index b93c6656e2..1e1a1c9c4e 100644 --- a/Source/Lua/LuaBindingsInput.cpp +++ b/Source/Lua/LuaBindingsInput.cpp @@ -8,642 +8,624 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, InputDevice) { return luabind::class_("InputDevice") - .enum_("InputDevice")[ - luabind::value("DEVICE_KEYB_ONLY", InputDevice::DEVICE_KEYB_ONLY), - luabind::value("DEVICE_MOUSE_KEYB", InputDevice::DEVICE_MOUSE_KEYB), - luabind::value("DEVICE_GAMEPAD_1", InputDevice::DEVICE_GAMEPAD_1), - luabind::value("DEVICE_GAMEPAD_2", InputDevice::DEVICE_GAMEPAD_2), - luabind::value("DEVICE_GAMEPAD_3", InputDevice::DEVICE_GAMEPAD_3), - luabind::value("DEVICE_GAMEPAD_4", InputDevice::DEVICE_GAMEPAD_4), - luabind::value("DEVICE_COUNT", InputDevice::DEVICE_COUNT) - ]; + .enum_("InputDevice")[luabind::value("DEVICE_KEYB_ONLY", InputDevice::DEVICE_KEYB_ONLY), + luabind::value("DEVICE_MOUSE_KEYB", InputDevice::DEVICE_MOUSE_KEYB), + luabind::value("DEVICE_GAMEPAD_1", InputDevice::DEVICE_GAMEPAD_1), + luabind::value("DEVICE_GAMEPAD_2", InputDevice::DEVICE_GAMEPAD_2), + luabind::value("DEVICE_GAMEPAD_3", InputDevice::DEVICE_GAMEPAD_3), + luabind::value("DEVICE_GAMEPAD_4", InputDevice::DEVICE_GAMEPAD_4), + luabind::value("DEVICE_COUNT", InputDevice::DEVICE_COUNT)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, InputElements) { return luabind::class_("InputElements") - .enum_("InputElements")[ - luabind::value("INPUT_L_UP", InputElements::INPUT_L_UP), - luabind::value("INPUT_L_DOWN", InputElements::INPUT_L_DOWN), - luabind::value("INPUT_L_LEFT", InputElements::INPUT_L_LEFT), - luabind::value("INPUT_L_RIGHT", InputElements::INPUT_L_RIGHT), - luabind::value("INPUT_R_UP", InputElements::INPUT_R_UP), - luabind::value("INPUT_R_DOWN", InputElements::INPUT_R_DOWN), - luabind::value("INPUT_R_LEFT", InputElements::INPUT_R_LEFT), - luabind::value("INPUT_R_RIGHT", InputElements::INPUT_R_RIGHT), - luabind::value("INPUT_FIRE", InputElements::INPUT_FIRE), - luabind::value("INPUT_AIM", InputElements::INPUT_AIM), - luabind::value("INPUT_AIM_UP", InputElements::INPUT_AIM_UP), - luabind::value("INPUT_AIM_DOWN", InputElements::INPUT_AIM_DOWN), - luabind::value("INPUT_AIM_LEFT", InputElements::INPUT_AIM_LEFT), - luabind::value("INPUT_AIM_RIGHT", InputElements::INPUT_AIM_RIGHT), - luabind::value("INPUT_PIEMENU_ANALOG", InputElements::INPUT_PIEMENU_ANALOG), - luabind::value("INPUT_PIEMENU_DIGITAL", InputElements::INPUT_PIEMENU_DIGITAL), - luabind::value("INPUT_JUMP", InputElements::INPUT_JUMP), - luabind::value("INPUT_CROUCH", InputElements::INPUT_CROUCH), - luabind::value("INPUT_NEXT", InputElements::INPUT_NEXT), - luabind::value("INPUT_PREV", InputElements::INPUT_PREV), - luabind::value("INPUT_START", InputElements::INPUT_START), - luabind::value("INPUT_BACK", InputElements::INPUT_BACK), - luabind::value("INPUT_COUNT", InputElements::INPUT_COUNT) - ]; + .enum_("InputElements")[luabind::value("INPUT_L_UP", InputElements::INPUT_L_UP), + luabind::value("INPUT_L_DOWN", InputElements::INPUT_L_DOWN), + luabind::value("INPUT_L_LEFT", InputElements::INPUT_L_LEFT), + luabind::value("INPUT_L_RIGHT", InputElements::INPUT_L_RIGHT), + luabind::value("INPUT_R_UP", InputElements::INPUT_R_UP), + luabind::value("INPUT_R_DOWN", InputElements::INPUT_R_DOWN), + luabind::value("INPUT_R_LEFT", InputElements::INPUT_R_LEFT), + luabind::value("INPUT_R_RIGHT", InputElements::INPUT_R_RIGHT), + luabind::value("INPUT_FIRE", InputElements::INPUT_FIRE), + luabind::value("INPUT_AIM", InputElements::INPUT_AIM), + luabind::value("INPUT_AIM_UP", InputElements::INPUT_AIM_UP), + luabind::value("INPUT_AIM_DOWN", InputElements::INPUT_AIM_DOWN), + luabind::value("INPUT_AIM_LEFT", InputElements::INPUT_AIM_LEFT), + luabind::value("INPUT_AIM_RIGHT", InputElements::INPUT_AIM_RIGHT), + luabind::value("INPUT_PIEMENU_ANALOG", InputElements::INPUT_PIEMENU_ANALOG), + luabind::value("INPUT_PIEMENU_DIGITAL", InputElements::INPUT_PIEMENU_DIGITAL), + luabind::value("INPUT_JUMP", InputElements::INPUT_JUMP), + luabind::value("INPUT_CROUCH", InputElements::INPUT_CROUCH), + luabind::value("INPUT_NEXT", InputElements::INPUT_NEXT), + luabind::value("INPUT_PREV", InputElements::INPUT_PREV), + luabind::value("INPUT_START", InputElements::INPUT_START), + luabind::value("INPUT_BACK", InputElements::INPUT_BACK), + luabind::value("INPUT_COUNT", InputElements::INPUT_COUNT)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, MouseButtons) { return luabind::class_("MouseButtons") - .enum_("MouseButtons")[ - luabind::value("MOUSE_NONE", MouseButtons::MOUSE_NONE), - luabind::value("MOUSE_LEFT", MouseButtons::MOUSE_LEFT), - luabind::value("MOUSE_RIGHT", MouseButtons::MOUSE_RIGHT), - luabind::value("MOUSE_MIDDLE", MouseButtons::MOUSE_MIDDLE), - luabind::value("MAX_MOUSE_BUTTONS", MouseButtons::MAX_MOUSE_BUTTONS) - ]; + .enum_("MouseButtons")[luabind::value("MOUSE_NONE", MouseButtons::MOUSE_NONE), + luabind::value("MOUSE_LEFT", MouseButtons::MOUSE_LEFT), + luabind::value("MOUSE_RIGHT", MouseButtons::MOUSE_RIGHT), + luabind::value("MOUSE_MIDDLE", MouseButtons::MOUSE_MIDDLE), + luabind::value("MAX_MOUSE_BUTTONS", MouseButtons::MAX_MOUSE_BUTTONS)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, JoyButtons) { return luabind::class_("JoyButtons") - .enum_("JoyButtons")[ - luabind::value("JOY_NONE", JoyButtons::JOY_NONE), - luabind::value("JOY_1", JoyButtons::JOY_1), - luabind::value("JOY_2", JoyButtons::JOY_2), - luabind::value("JOY_3", JoyButtons::JOY_3), - luabind::value("JOY_4", JoyButtons::JOY_4), - luabind::value("JOY_5", JoyButtons::JOY_5), - luabind::value("JOY_6", JoyButtons::JOY_6), - luabind::value("JOY_7", JoyButtons::JOY_7), - luabind::value("JOY_8", JoyButtons::JOY_8), - luabind::value("JOY_9", JoyButtons::JOY_9), - luabind::value("JOY_10", JoyButtons::JOY_10), - luabind::value("JOY_11", JoyButtons::JOY_11), - luabind::value("JOY_12", JoyButtons::JOY_12), - luabind::value("MAX_JOY_BUTTONS", JoyButtons::MAX_JOY_BUTTONS) - ]; + .enum_("JoyButtons")[luabind::value("JOY_NONE", JoyButtons::JOY_NONE), + luabind::value("JOY_1", JoyButtons::JOY_1), + luabind::value("JOY_2", JoyButtons::JOY_2), + luabind::value("JOY_3", JoyButtons::JOY_3), + luabind::value("JOY_4", JoyButtons::JOY_4), + luabind::value("JOY_5", JoyButtons::JOY_5), + luabind::value("JOY_6", JoyButtons::JOY_6), + luabind::value("JOY_7", JoyButtons::JOY_7), + luabind::value("JOY_8", JoyButtons::JOY_8), + luabind::value("JOY_9", JoyButtons::JOY_9), + luabind::value("JOY_10", JoyButtons::JOY_10), + luabind::value("JOY_11", JoyButtons::JOY_11), + luabind::value("JOY_12", JoyButtons::JOY_12), + luabind::value("MAX_JOY_BUTTONS", JoyButtons::MAX_JOY_BUTTONS)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, JoyDirections) { return luabind::class_("JoyDirections") - .enum_("JoyDirections")[ - luabind::value("JOYDIR_ONE", JoyDirections::JOYDIR_ONE), - luabind::value("JOYDIR_TWO", JoyDirections::JOYDIR_TWO) - ]; + .enum_("JoyDirections")[luabind::value("JOYDIR_ONE", JoyDirections::JOYDIR_ONE), + luabind::value("JOYDIR_TWO", JoyDirections::JOYDIR_TWO)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, SDL_Keycode) { return luabind::class_("Key") - // Make sure to update https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/wiki/SDL-Keycode-and-Scancode-enum-values-in-Lua#key-codes if any changes are made. - .enum_("Key")[ - luabind::value("UNKNOWN", SDLK_UNKNOWN), - luabind::value("RETURN", SDLK_RETURN), - luabind::value("ESCAPE", SDLK_ESCAPE), - luabind::value("BACKSPACE", SDLK_BACKSPACE), - luabind::value("TAB", SDLK_TAB), - luabind::value("SPACE", SDLK_SPACE), - luabind::value("EXCLAIM", SDLK_EXCLAIM), - luabind::value("QUOTEDBL", SDLK_QUOTEDBL), - luabind::value("HASH", SDLK_HASH), - luabind::value("PERCENT", SDLK_PERCENT), - luabind::value("DOLLAR", SDLK_DOLLAR), - luabind::value("AMPERSAND", SDLK_AMPERSAND), - luabind::value("QUOTE", SDLK_QUOTE), - luabind::value("LEFTPAREN", SDLK_LEFTPAREN), - luabind::value("RIGHTPAREN", SDLK_RIGHTPAREN), - luabind::value("ASTERISK", SDLK_ASTERISK), - luabind::value("PLUS", SDLK_PLUS), - luabind::value("COMMA", SDLK_COMMA), - luabind::value("MINUS", SDLK_MINUS), - luabind::value("PERIOD", SDLK_PERIOD), - luabind::value("SLASH", SDLK_SLASH), - luabind::value("K_0", SDLK_0), - luabind::value("K_1", SDLK_1), - luabind::value("K_2", SDLK_2), - luabind::value("K_3", SDLK_3), - luabind::value("K_4", SDLK_4), - luabind::value("K_5", SDLK_5), - luabind::value("K_6", SDLK_6), - luabind::value("K_7", SDLK_7), - luabind::value("K_8", SDLK_8), - luabind::value("K_9", SDLK_9), - luabind::value("COLON", SDLK_COLON), - luabind::value("SEMICOLON", SDLK_SEMICOLON), - luabind::value("LESS", SDLK_LESS), - luabind::value("EQUALS", SDLK_EQUALS), - luabind::value("GREATER", SDLK_GREATER), - luabind::value("QUESTION", SDLK_QUESTION), - luabind::value("AT", SDLK_AT), - luabind::value("LEFTBRACKET", SDLK_LEFTBRACKET), - luabind::value("BACKSLASH", SDLK_BACKSLASH), - luabind::value("RIGHTBRACKET", SDLK_RIGHTBRACKET), - luabind::value("CARET", SDLK_CARET), - luabind::value("UNDERSCORE", SDLK_UNDERSCORE), - luabind::value("BACKQUOTE", SDLK_BACKQUOTE), - luabind::value("A", SDLK_a), - luabind::value("B", SDLK_b), - luabind::value("C", SDLK_c), - luabind::value("D", SDLK_d), - luabind::value("E", SDLK_e), - luabind::value("F", SDLK_f), - luabind::value("G", SDLK_g), - luabind::value("H", SDLK_h), - luabind::value("I", SDLK_i), - luabind::value("J", SDLK_j), - luabind::value("K", SDLK_k), - luabind::value("L", SDLK_l), - luabind::value("M", SDLK_m), - luabind::value("N", SDLK_n), - luabind::value("O", SDLK_o), - luabind::value("P", SDLK_p), - luabind::value("Q", SDLK_q), - luabind::value("R", SDLK_r), - luabind::value("S", SDLK_s), - luabind::value("T", SDLK_t), - luabind::value("U", SDLK_u), - luabind::value("V", SDLK_v), - luabind::value("W", SDLK_w), - luabind::value("X", SDLK_x), - luabind::value("Y", SDLK_y), - luabind::value("Z", SDLK_z), - luabind::value("CAPSLOCK", SDLK_CAPSLOCK), - luabind::value("F1", SDLK_F1), - luabind::value("F2", SDLK_F2), - luabind::value("F3", SDLK_F3), - luabind::value("F4", SDLK_F4), - luabind::value("F5", SDLK_F5), - luabind::value("F6", SDLK_F6), - luabind::value("F7", SDLK_F7), - luabind::value("F8", SDLK_F8), - luabind::value("F9", SDLK_F9), - luabind::value("F10", SDLK_F10), - luabind::value("F11", SDLK_F11), - luabind::value("F12", SDLK_F12), - luabind::value("PRINTSCREEN", SDLK_PRINTSCREEN), - luabind::value("SCROLLLOCK", SDLK_SCROLLLOCK), - luabind::value("PAUSE", SDLK_PAUSE), - luabind::value("INSERT", SDLK_INSERT), - luabind::value("HOME", SDLK_HOME), - luabind::value("PAGEUP", SDLK_PAGEUP), - luabind::value("DELETE", SDLK_DELETE), - luabind::value("END", SDLK_END), - luabind::value("PAGEDOWN", SDLK_PAGEDOWN), - luabind::value("RIGHT", SDLK_RIGHT), - luabind::value("LEFT", SDLK_LEFT), - luabind::value("DOWN", SDLK_DOWN), - luabind::value("UP", SDLK_UP), - luabind::value("NUMLOCKCLEAR", SDLK_NUMLOCKCLEAR), - luabind::value("KP_DIVIDE", SDLK_KP_DIVIDE), - luabind::value("KP_MULTIPLY", SDLK_KP_MULTIPLY), - luabind::value("KP_MINUS", SDLK_KP_MINUS), - luabind::value("KP_PLUS", SDLK_KP_PLUS), - luabind::value("KP_ENTER", SDLK_KP_ENTER), - luabind::value("KP_1", SDLK_KP_1), - luabind::value("KP_2", SDLK_KP_2), - luabind::value("KP_3", SDLK_KP_3), - luabind::value("KP_4", SDLK_KP_4), - luabind::value("KP_5", SDLK_KP_5), - luabind::value("KP_6", SDLK_KP_6), - luabind::value("KP_7", SDLK_KP_7), - luabind::value("KP_8", SDLK_KP_8), - luabind::value("KP_9", SDLK_KP_9), - luabind::value("KP_0", SDLK_KP_0), - luabind::value("KP_PERIOD", SDLK_KP_PERIOD), - luabind::value("APPLICATION", SDLK_APPLICATION), - luabind::value("POWER", SDLK_POWER), - luabind::value("KP_EQUALS", SDLK_KP_EQUALS), - luabind::value("F13", SDLK_F13), - luabind::value("F14", SDLK_F14), - luabind::value("F15", SDLK_F15), - luabind::value("F16", SDLK_F16), - luabind::value("F17", SDLK_F17), - luabind::value("F18", SDLK_F18), - luabind::value("F19", SDLK_F19), - luabind::value("F20", SDLK_F20), - luabind::value("F21", SDLK_F21), - luabind::value("F22", SDLK_F22), - luabind::value("F23", SDLK_F23), - luabind::value("F24", SDLK_F24), - luabind::value("EXECUTE", SDLK_EXECUTE), - luabind::value("HELP", SDLK_HELP), - luabind::value("MENU", SDLK_MENU), - luabind::value("SELECT", SDLK_SELECT), - luabind::value("STOP", SDLK_STOP), - luabind::value("AGAIN", SDLK_AGAIN), - luabind::value("UNDO", SDLK_UNDO), - luabind::value("CUT", SDLK_CUT), - luabind::value("COPY", SDLK_COPY), - luabind::value("PASTE", SDLK_PASTE), - luabind::value("FIND", SDLK_FIND), - luabind::value("MUTE", SDLK_MUTE), - luabind::value("VOLUMEUP", SDLK_VOLUMEUP), - luabind::value("VOLUMEDOWN", SDLK_VOLUMEDOWN), - luabind::value("KP_COMMA", SDLK_KP_COMMA), - luabind::value("KP_EQUALSAS400", SDLK_KP_EQUALSAS400), - luabind::value("ALTERASE", SDLK_ALTERASE), - luabind::value("SYSREQ", SDLK_SYSREQ), - luabind::value("CANCEL", SDLK_CANCEL), - luabind::value("CLEAR", SDLK_CLEAR), - luabind::value("PRIOR", SDLK_PRIOR), - luabind::value("RETURN2", SDLK_RETURN2), - luabind::value("SEPARATOR", SDLK_SEPARATOR), - luabind::value("OUT", SDLK_OUT), - luabind::value("OPER", SDLK_OPER), - luabind::value("CLEARAGAIN", SDLK_CLEARAGAIN), - luabind::value("CRSEL", SDLK_CRSEL), - luabind::value("EXSEL", SDLK_EXSEL), - luabind::value("KP_00", SDLK_KP_00), - luabind::value("KP_000", SDLK_KP_000), - luabind::value("THOUSANDSSEPARATOR", SDLK_THOUSANDSSEPARATOR), - luabind::value("DECIMALSEPARATOR", SDLK_DECIMALSEPARATOR), - luabind::value("CURRENCYUNIT", SDLK_CURRENCYUNIT), - luabind::value("CURRENCYSUBUNIT", SDLK_CURRENCYSUBUNIT), - luabind::value("KP_LEFTPAREN", SDLK_KP_LEFTPAREN), - luabind::value("KP_RIGHTPAREN", SDLK_KP_RIGHTPAREN), - luabind::value("KP_LEFTBRACE", SDLK_KP_LEFTBRACE), - luabind::value("KP_RIGHTBRACE", SDLK_KP_RIGHTBRACE), - luabind::value("KP_TAB", SDLK_KP_TAB), - luabind::value("KP_BACKSPACE", SDLK_KP_BACKSPACE), - luabind::value("KP_A", SDLK_KP_A), - luabind::value("KP_B", SDLK_KP_B), - luabind::value("KP_C", SDLK_KP_C), - luabind::value("KP_D", SDLK_KP_D), - luabind::value("KP_E", SDLK_KP_E), - luabind::value("KP_F", SDLK_KP_F), - luabind::value("KP_XOR", SDLK_KP_XOR), - luabind::value("KP_POWER", SDLK_KP_POWER), - luabind::value("KP_PERCENT", SDLK_KP_PERCENT), - luabind::value("KP_LESS", SDLK_KP_LESS), - luabind::value("KP_GREATER", SDLK_KP_GREATER), - luabind::value("KP_AMPERSAND", SDLK_KP_AMPERSAND), - luabind::value("KP_DBLAMPERSAND", SDLK_KP_DBLAMPERSAND), - luabind::value("KP_VERTICALBAR", SDLK_KP_VERTICALBAR), - luabind::value("KP_DBLVERTICALBAR", SDLK_KP_DBLVERTICALBAR), - luabind::value("KP_COLON", SDLK_KP_COLON), - luabind::value("KP_HASH", SDLK_KP_HASH), - luabind::value("KP_SPACE", SDLK_KP_SPACE), - luabind::value("KP_AT", SDLK_KP_AT), - luabind::value("KP_EXCLAM", SDLK_KP_EXCLAM), - luabind::value("KP_MEMSTORE", SDLK_KP_MEMSTORE), - luabind::value("KP_MEMRECALL", SDLK_KP_MEMRECALL), - luabind::value("KP_MEMCLEAR", SDLK_KP_MEMCLEAR), - luabind::value("KP_MEMADD", SDLK_KP_MEMADD), - luabind::value("KP_MEMSUBTRACT", SDLK_KP_MEMSUBTRACT), - luabind::value("KP_MEMMULTIPLY", SDLK_KP_MEMMULTIPLY), - luabind::value("KP_MEMDIVIDE", SDLK_KP_MEMDIVIDE), - luabind::value("KP_PLUSMINUS", SDLK_KP_PLUSMINUS), - luabind::value("KP_CLEAR", SDLK_KP_CLEAR), - luabind::value("KP_CLEARENTRY", SDLK_KP_CLEARENTRY), - luabind::value("KP_BINARY", SDLK_KP_BINARY), - luabind::value("KP_OCTAL", SDLK_KP_OCTAL), - luabind::value("KP_DECIMAL", SDLK_KP_DECIMAL), - luabind::value("KP_HEXADECIMAL", SDLK_KP_HEXADECIMAL), - luabind::value("LCTRL", SDLK_LCTRL), - luabind::value("LSHIFT", SDLK_LSHIFT), - luabind::value("LALT", SDLK_LALT), - luabind::value("LGUI", SDLK_LGUI), - luabind::value("RCTRL", SDLK_RCTRL), - luabind::value("RSHIFT", SDLK_RSHIFT), - luabind::value("RALT", SDLK_RALT), - luabind::value("RGUI", SDLK_RGUI), - luabind::value("MODE", SDLK_MODE), - luabind::value("AUDIONEXT", SDLK_AUDIONEXT), - luabind::value("AUDIOPREV", SDLK_AUDIOPREV), - luabind::value("AUDIOSTOP", SDLK_AUDIOSTOP), - luabind::value("AUDIOPLAY", SDLK_AUDIOPLAY), - luabind::value("AUDIOMUTE", SDLK_AUDIOMUTE), - luabind::value("MEDIASELECT", SDLK_MEDIASELECT), - luabind::value("WWW", SDLK_WWW), - luabind::value("MAIL", SDLK_MAIL), - luabind::value("CALCULATOR", SDLK_CALCULATOR), - luabind::value("COMPUTER", SDLK_COMPUTER), - luabind::value("AC_SEARCH", SDLK_AC_SEARCH), - luabind::value("AC_HOME", SDLK_AC_HOME), - luabind::value("AC_BACK", SDLK_AC_BACK), - luabind::value("AC_FORWARD", SDLK_AC_FORWARD), - luabind::value("AC_STOP", SDLK_AC_STOP), - luabind::value("AC_REFRESH", SDLK_AC_REFRESH), - luabind::value("AC_BOOKMARKS", SDLK_AC_BOOKMARKS), - luabind::value("BRIGHTNESSDOWN", SDLK_BRIGHTNESSDOWN), - luabind::value("BRIGHTNESSUP", SDLK_BRIGHTNESSUP), - luabind::value("DISPLAYSWITCH", SDLK_DISPLAYSWITCH), - luabind::value("KBDILLUMTOGGLE", SDLK_KBDILLUMTOGGLE), - luabind::value("KBDILLUMDOWN", SDLK_KBDILLUMDOWN), - luabind::value("KBDILLUMUP", SDLK_KBDILLUMUP), - luabind::value("EJECT", SDLK_EJECT), - luabind::value("SLEEP", SDLK_SLEEP) - ]; + // Make sure to update https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/wiki/SDL-Keycode-and-Scancode-enum-values-in-Lua#key-codes if any changes are made. + .enum_("Key")[luabind::value("UNKNOWN", SDLK_UNKNOWN), + luabind::value("RETURN", SDLK_RETURN), + luabind::value("ESCAPE", SDLK_ESCAPE), + luabind::value("BACKSPACE", SDLK_BACKSPACE), + luabind::value("TAB", SDLK_TAB), + luabind::value("SPACE", SDLK_SPACE), + luabind::value("EXCLAIM", SDLK_EXCLAIM), + luabind::value("QUOTEDBL", SDLK_QUOTEDBL), + luabind::value("HASH", SDLK_HASH), + luabind::value("PERCENT", SDLK_PERCENT), + luabind::value("DOLLAR", SDLK_DOLLAR), + luabind::value("AMPERSAND", SDLK_AMPERSAND), + luabind::value("QUOTE", SDLK_QUOTE), + luabind::value("LEFTPAREN", SDLK_LEFTPAREN), + luabind::value("RIGHTPAREN", SDLK_RIGHTPAREN), + luabind::value("ASTERISK", SDLK_ASTERISK), + luabind::value("PLUS", SDLK_PLUS), + luabind::value("COMMA", SDLK_COMMA), + luabind::value("MINUS", SDLK_MINUS), + luabind::value("PERIOD", SDLK_PERIOD), + luabind::value("SLASH", SDLK_SLASH), + luabind::value("K_0", SDLK_0), + luabind::value("K_1", SDLK_1), + luabind::value("K_2", SDLK_2), + luabind::value("K_3", SDLK_3), + luabind::value("K_4", SDLK_4), + luabind::value("K_5", SDLK_5), + luabind::value("K_6", SDLK_6), + luabind::value("K_7", SDLK_7), + luabind::value("K_8", SDLK_8), + luabind::value("K_9", SDLK_9), + luabind::value("COLON", SDLK_COLON), + luabind::value("SEMICOLON", SDLK_SEMICOLON), + luabind::value("LESS", SDLK_LESS), + luabind::value("EQUALS", SDLK_EQUALS), + luabind::value("GREATER", SDLK_GREATER), + luabind::value("QUESTION", SDLK_QUESTION), + luabind::value("AT", SDLK_AT), + luabind::value("LEFTBRACKET", SDLK_LEFTBRACKET), + luabind::value("BACKSLASH", SDLK_BACKSLASH), + luabind::value("RIGHTBRACKET", SDLK_RIGHTBRACKET), + luabind::value("CARET", SDLK_CARET), + luabind::value("UNDERSCORE", SDLK_UNDERSCORE), + luabind::value("BACKQUOTE", SDLK_BACKQUOTE), + luabind::value("A", SDLK_a), + luabind::value("B", SDLK_b), + luabind::value("C", SDLK_c), + luabind::value("D", SDLK_d), + luabind::value("E", SDLK_e), + luabind::value("F", SDLK_f), + luabind::value("G", SDLK_g), + luabind::value("H", SDLK_h), + luabind::value("I", SDLK_i), + luabind::value("J", SDLK_j), + luabind::value("K", SDLK_k), + luabind::value("L", SDLK_l), + luabind::value("M", SDLK_m), + luabind::value("N", SDLK_n), + luabind::value("O", SDLK_o), + luabind::value("P", SDLK_p), + luabind::value("Q", SDLK_q), + luabind::value("R", SDLK_r), + luabind::value("S", SDLK_s), + luabind::value("T", SDLK_t), + luabind::value("U", SDLK_u), + luabind::value("V", SDLK_v), + luabind::value("W", SDLK_w), + luabind::value("X", SDLK_x), + luabind::value("Y", SDLK_y), + luabind::value("Z", SDLK_z), + luabind::value("CAPSLOCK", SDLK_CAPSLOCK), + luabind::value("F1", SDLK_F1), + luabind::value("F2", SDLK_F2), + luabind::value("F3", SDLK_F3), + luabind::value("F4", SDLK_F4), + luabind::value("F5", SDLK_F5), + luabind::value("F6", SDLK_F6), + luabind::value("F7", SDLK_F7), + luabind::value("F8", SDLK_F8), + luabind::value("F9", SDLK_F9), + luabind::value("F10", SDLK_F10), + luabind::value("F11", SDLK_F11), + luabind::value("F12", SDLK_F12), + luabind::value("PRINTSCREEN", SDLK_PRINTSCREEN), + luabind::value("SCROLLLOCK", SDLK_SCROLLLOCK), + luabind::value("PAUSE", SDLK_PAUSE), + luabind::value("INSERT", SDLK_INSERT), + luabind::value("HOME", SDLK_HOME), + luabind::value("PAGEUP", SDLK_PAGEUP), + luabind::value("DELETE", SDLK_DELETE), + luabind::value("END", SDLK_END), + luabind::value("PAGEDOWN", SDLK_PAGEDOWN), + luabind::value("RIGHT", SDLK_RIGHT), + luabind::value("LEFT", SDLK_LEFT), + luabind::value("DOWN", SDLK_DOWN), + luabind::value("UP", SDLK_UP), + luabind::value("NUMLOCKCLEAR", SDLK_NUMLOCKCLEAR), + luabind::value("KP_DIVIDE", SDLK_KP_DIVIDE), + luabind::value("KP_MULTIPLY", SDLK_KP_MULTIPLY), + luabind::value("KP_MINUS", SDLK_KP_MINUS), + luabind::value("KP_PLUS", SDLK_KP_PLUS), + luabind::value("KP_ENTER", SDLK_KP_ENTER), + luabind::value("KP_1", SDLK_KP_1), + luabind::value("KP_2", SDLK_KP_2), + luabind::value("KP_3", SDLK_KP_3), + luabind::value("KP_4", SDLK_KP_4), + luabind::value("KP_5", SDLK_KP_5), + luabind::value("KP_6", SDLK_KP_6), + luabind::value("KP_7", SDLK_KP_7), + luabind::value("KP_8", SDLK_KP_8), + luabind::value("KP_9", SDLK_KP_9), + luabind::value("KP_0", SDLK_KP_0), + luabind::value("KP_PERIOD", SDLK_KP_PERIOD), + luabind::value("APPLICATION", SDLK_APPLICATION), + luabind::value("POWER", SDLK_POWER), + luabind::value("KP_EQUALS", SDLK_KP_EQUALS), + luabind::value("F13", SDLK_F13), + luabind::value("F14", SDLK_F14), + luabind::value("F15", SDLK_F15), + luabind::value("F16", SDLK_F16), + luabind::value("F17", SDLK_F17), + luabind::value("F18", SDLK_F18), + luabind::value("F19", SDLK_F19), + luabind::value("F20", SDLK_F20), + luabind::value("F21", SDLK_F21), + luabind::value("F22", SDLK_F22), + luabind::value("F23", SDLK_F23), + luabind::value("F24", SDLK_F24), + luabind::value("EXECUTE", SDLK_EXECUTE), + luabind::value("HELP", SDLK_HELP), + luabind::value("MENU", SDLK_MENU), + luabind::value("SELECT", SDLK_SELECT), + luabind::value("STOP", SDLK_STOP), + luabind::value("AGAIN", SDLK_AGAIN), + luabind::value("UNDO", SDLK_UNDO), + luabind::value("CUT", SDLK_CUT), + luabind::value("COPY", SDLK_COPY), + luabind::value("PASTE", SDLK_PASTE), + luabind::value("FIND", SDLK_FIND), + luabind::value("MUTE", SDLK_MUTE), + luabind::value("VOLUMEUP", SDLK_VOLUMEUP), + luabind::value("VOLUMEDOWN", SDLK_VOLUMEDOWN), + luabind::value("KP_COMMA", SDLK_KP_COMMA), + luabind::value("KP_EQUALSAS400", SDLK_KP_EQUALSAS400), + luabind::value("ALTERASE", SDLK_ALTERASE), + luabind::value("SYSREQ", SDLK_SYSREQ), + luabind::value("CANCEL", SDLK_CANCEL), + luabind::value("CLEAR", SDLK_CLEAR), + luabind::value("PRIOR", SDLK_PRIOR), + luabind::value("RETURN2", SDLK_RETURN2), + luabind::value("SEPARATOR", SDLK_SEPARATOR), + luabind::value("OUT", SDLK_OUT), + luabind::value("OPER", SDLK_OPER), + luabind::value("CLEARAGAIN", SDLK_CLEARAGAIN), + luabind::value("CRSEL", SDLK_CRSEL), + luabind::value("EXSEL", SDLK_EXSEL), + luabind::value("KP_00", SDLK_KP_00), + luabind::value("KP_000", SDLK_KP_000), + luabind::value("THOUSANDSSEPARATOR", SDLK_THOUSANDSSEPARATOR), + luabind::value("DECIMALSEPARATOR", SDLK_DECIMALSEPARATOR), + luabind::value("CURRENCYUNIT", SDLK_CURRENCYUNIT), + luabind::value("CURRENCYSUBUNIT", SDLK_CURRENCYSUBUNIT), + luabind::value("KP_LEFTPAREN", SDLK_KP_LEFTPAREN), + luabind::value("KP_RIGHTPAREN", SDLK_KP_RIGHTPAREN), + luabind::value("KP_LEFTBRACE", SDLK_KP_LEFTBRACE), + luabind::value("KP_RIGHTBRACE", SDLK_KP_RIGHTBRACE), + luabind::value("KP_TAB", SDLK_KP_TAB), + luabind::value("KP_BACKSPACE", SDLK_KP_BACKSPACE), + luabind::value("KP_A", SDLK_KP_A), + luabind::value("KP_B", SDLK_KP_B), + luabind::value("KP_C", SDLK_KP_C), + luabind::value("KP_D", SDLK_KP_D), + luabind::value("KP_E", SDLK_KP_E), + luabind::value("KP_F", SDLK_KP_F), + luabind::value("KP_XOR", SDLK_KP_XOR), + luabind::value("KP_POWER", SDLK_KP_POWER), + luabind::value("KP_PERCENT", SDLK_KP_PERCENT), + luabind::value("KP_LESS", SDLK_KP_LESS), + luabind::value("KP_GREATER", SDLK_KP_GREATER), + luabind::value("KP_AMPERSAND", SDLK_KP_AMPERSAND), + luabind::value("KP_DBLAMPERSAND", SDLK_KP_DBLAMPERSAND), + luabind::value("KP_VERTICALBAR", SDLK_KP_VERTICALBAR), + luabind::value("KP_DBLVERTICALBAR", SDLK_KP_DBLVERTICALBAR), + luabind::value("KP_COLON", SDLK_KP_COLON), + luabind::value("KP_HASH", SDLK_KP_HASH), + luabind::value("KP_SPACE", SDLK_KP_SPACE), + luabind::value("KP_AT", SDLK_KP_AT), + luabind::value("KP_EXCLAM", SDLK_KP_EXCLAM), + luabind::value("KP_MEMSTORE", SDLK_KP_MEMSTORE), + luabind::value("KP_MEMRECALL", SDLK_KP_MEMRECALL), + luabind::value("KP_MEMCLEAR", SDLK_KP_MEMCLEAR), + luabind::value("KP_MEMADD", SDLK_KP_MEMADD), + luabind::value("KP_MEMSUBTRACT", SDLK_KP_MEMSUBTRACT), + luabind::value("KP_MEMMULTIPLY", SDLK_KP_MEMMULTIPLY), + luabind::value("KP_MEMDIVIDE", SDLK_KP_MEMDIVIDE), + luabind::value("KP_PLUSMINUS", SDLK_KP_PLUSMINUS), + luabind::value("KP_CLEAR", SDLK_KP_CLEAR), + luabind::value("KP_CLEARENTRY", SDLK_KP_CLEARENTRY), + luabind::value("KP_BINARY", SDLK_KP_BINARY), + luabind::value("KP_OCTAL", SDLK_KP_OCTAL), + luabind::value("KP_DECIMAL", SDLK_KP_DECIMAL), + luabind::value("KP_HEXADECIMAL", SDLK_KP_HEXADECIMAL), + luabind::value("LCTRL", SDLK_LCTRL), + luabind::value("LSHIFT", SDLK_LSHIFT), + luabind::value("LALT", SDLK_LALT), + luabind::value("LGUI", SDLK_LGUI), + luabind::value("RCTRL", SDLK_RCTRL), + luabind::value("RSHIFT", SDLK_RSHIFT), + luabind::value("RALT", SDLK_RALT), + luabind::value("RGUI", SDLK_RGUI), + luabind::value("MODE", SDLK_MODE), + luabind::value("AUDIONEXT", SDLK_AUDIONEXT), + luabind::value("AUDIOPREV", SDLK_AUDIOPREV), + luabind::value("AUDIOSTOP", SDLK_AUDIOSTOP), + luabind::value("AUDIOPLAY", SDLK_AUDIOPLAY), + luabind::value("AUDIOMUTE", SDLK_AUDIOMUTE), + luabind::value("MEDIASELECT", SDLK_MEDIASELECT), + luabind::value("WWW", SDLK_WWW), + luabind::value("MAIL", SDLK_MAIL), + luabind::value("CALCULATOR", SDLK_CALCULATOR), + luabind::value("COMPUTER", SDLK_COMPUTER), + luabind::value("AC_SEARCH", SDLK_AC_SEARCH), + luabind::value("AC_HOME", SDLK_AC_HOME), + luabind::value("AC_BACK", SDLK_AC_BACK), + luabind::value("AC_FORWARD", SDLK_AC_FORWARD), + luabind::value("AC_STOP", SDLK_AC_STOP), + luabind::value("AC_REFRESH", SDLK_AC_REFRESH), + luabind::value("AC_BOOKMARKS", SDLK_AC_BOOKMARKS), + luabind::value("BRIGHTNESSDOWN", SDLK_BRIGHTNESSDOWN), + luabind::value("BRIGHTNESSUP", SDLK_BRIGHTNESSUP), + luabind::value("DISPLAYSWITCH", SDLK_DISPLAYSWITCH), + luabind::value("KBDILLUMTOGGLE", SDLK_KBDILLUMTOGGLE), + luabind::value("KBDILLUMDOWN", SDLK_KBDILLUMDOWN), + luabind::value("KBDILLUMUP", SDLK_KBDILLUMUP), + luabind::value("EJECT", SDLK_EJECT), + luabind::value("SLEEP", SDLK_SLEEP)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, SDL_Scancode) { return luabind::class_("Scancode") - // Make sure to update https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/wiki/SDL-Keycode-and-Scancode-enum-values-in-Lua#scan-codes if any changes are made. - .enum_("Scancode")[ - luabind::value("UNKNOWN", SDL_SCANCODE_UNKNOWN), - luabind::value("A", SDL_SCANCODE_A), - luabind::value("B", SDL_SCANCODE_B), - luabind::value("C", SDL_SCANCODE_C), - luabind::value("D", SDL_SCANCODE_D), - luabind::value("E", SDL_SCANCODE_E), - luabind::value("F", SDL_SCANCODE_F), - luabind::value("G", SDL_SCANCODE_G), - luabind::value("H", SDL_SCANCODE_H), - luabind::value("I", SDL_SCANCODE_I), - luabind::value("J", SDL_SCANCODE_J), - luabind::value("K", SDL_SCANCODE_K), - luabind::value("L", SDL_SCANCODE_L), - luabind::value("M", SDL_SCANCODE_M), - luabind::value("N", SDL_SCANCODE_N), - luabind::value("O", SDL_SCANCODE_O), - luabind::value("P", SDL_SCANCODE_P), - luabind::value("Q", SDL_SCANCODE_Q), - luabind::value("R", SDL_SCANCODE_R), - luabind::value("S", SDL_SCANCODE_S), - luabind::value("T", SDL_SCANCODE_T), - luabind::value("U", SDL_SCANCODE_U), - luabind::value("V", SDL_SCANCODE_V), - luabind::value("W", SDL_SCANCODE_W), - luabind::value("X", SDL_SCANCODE_X), - luabind::value("Y", SDL_SCANCODE_Y), - luabind::value("Z", SDL_SCANCODE_Z), - luabind::value("K_1", SDL_SCANCODE_1), - luabind::value("K_2", SDL_SCANCODE_2), - luabind::value("K_3", SDL_SCANCODE_3), - luabind::value("K_4", SDL_SCANCODE_4), - luabind::value("K_5", SDL_SCANCODE_5), - luabind::value("K_6", SDL_SCANCODE_6), - luabind::value("K_7", SDL_SCANCODE_7), - luabind::value("K_8", SDL_SCANCODE_8), - luabind::value("K_9", SDL_SCANCODE_9), - luabind::value("K_0", SDL_SCANCODE_0), - luabind::value("RETURN", SDL_SCANCODE_RETURN), - luabind::value("ESCAPE", SDL_SCANCODE_ESCAPE), - luabind::value("BACKSPACE", SDL_SCANCODE_BACKSPACE), - luabind::value("TAB", SDL_SCANCODE_TAB), - luabind::value("SPACE", SDL_SCANCODE_SPACE), - luabind::value("MINUS", SDL_SCANCODE_MINUS), - luabind::value("EQUALS", SDL_SCANCODE_EQUALS), - luabind::value("LEFTBRACKET", SDL_SCANCODE_LEFTBRACKET), - luabind::value("RIGHTBRACKET", SDL_SCANCODE_RIGHTBRACKET), - luabind::value("BACKSLASH", SDL_SCANCODE_BACKSLASH), - luabind::value("NONUSHASH", SDL_SCANCODE_NONUSHASH), - luabind::value("SEMICOLON", SDL_SCANCODE_SEMICOLON), - luabind::value("APOSTROPHE", SDL_SCANCODE_APOSTROPHE), - luabind::value("GRAVE", SDL_SCANCODE_GRAVE), - luabind::value("COMMA", SDL_SCANCODE_COMMA), - luabind::value("PERIOD", SDL_SCANCODE_PERIOD), - luabind::value("SLASH", SDL_SCANCODE_SLASH), - luabind::value("CAPSLOCK", SDL_SCANCODE_CAPSLOCK), - luabind::value("F1", SDL_SCANCODE_F1), - luabind::value("F2", SDL_SCANCODE_F2), - luabind::value("F3", SDL_SCANCODE_F3), - luabind::value("F4", SDL_SCANCODE_F4), - luabind::value("F5", SDL_SCANCODE_F5), - luabind::value("F6", SDL_SCANCODE_F6), - luabind::value("F7", SDL_SCANCODE_F7), - luabind::value("F8", SDL_SCANCODE_F8), - luabind::value("F9", SDL_SCANCODE_F9), - luabind::value("F10", SDL_SCANCODE_F10), - luabind::value("F11", SDL_SCANCODE_F11), - luabind::value("F12", SDL_SCANCODE_F12), - luabind::value("PRINTSCREEN", SDL_SCANCODE_PRINTSCREEN), - luabind::value("SCROLLLOCK", SDL_SCANCODE_SCROLLLOCK), - luabind::value("PAUSE", SDL_SCANCODE_PAUSE), - luabind::value("INSERT", SDL_SCANCODE_INSERT), - luabind::value("HOME", SDL_SCANCODE_HOME), - luabind::value("PAGEUP", SDL_SCANCODE_PAGEUP), - luabind::value("DELETE", SDL_SCANCODE_DELETE), - luabind::value("END", SDL_SCANCODE_END), - luabind::value("PAGEDOWN", SDL_SCANCODE_PAGEDOWN), - luabind::value("RIGHT", SDL_SCANCODE_RIGHT), - luabind::value("LEFT", SDL_SCANCODE_LEFT), - luabind::value("DOWN", SDL_SCANCODE_DOWN), - luabind::value("UP", SDL_SCANCODE_UP), - luabind::value("NUMLOCKCLEAR", SDL_SCANCODE_NUMLOCKCLEAR), - luabind::value("KP_DIVIDE", SDL_SCANCODE_KP_DIVIDE), - luabind::value("KP_MULTIPLY", SDL_SCANCODE_KP_MULTIPLY), - luabind::value("KP_MINUS", SDL_SCANCODE_KP_MINUS), - luabind::value("KP_PLUS", SDL_SCANCODE_KP_PLUS), - luabind::value("KP_ENTER", SDL_SCANCODE_KP_ENTER), - luabind::value("KP_1", SDL_SCANCODE_KP_1), - luabind::value("KP_2", SDL_SCANCODE_KP_2), - luabind::value("KP_3", SDL_SCANCODE_KP_3), - luabind::value("KP_4", SDL_SCANCODE_KP_4), - luabind::value("KP_5", SDL_SCANCODE_KP_5), - luabind::value("KP_6", SDL_SCANCODE_KP_6), - luabind::value("KP_7", SDL_SCANCODE_KP_7), - luabind::value("KP_8", SDL_SCANCODE_KP_8), - luabind::value("KP_9", SDL_SCANCODE_KP_9), - luabind::value("KP_0", SDL_SCANCODE_KP_0), - luabind::value("KP_PERIOD", SDL_SCANCODE_KP_PERIOD), - luabind::value("NONUSBACKSLASH", SDL_SCANCODE_NONUSBACKSLASH), - luabind::value("APPLICATION", SDL_SCANCODE_APPLICATION), - luabind::value("POWER", SDL_SCANCODE_POWER), - luabind::value("KP_EQUALS", SDL_SCANCODE_KP_EQUALS), - luabind::value("F13", SDL_SCANCODE_F13), - luabind::value("F14", SDL_SCANCODE_F14), - luabind::value("F15", SDL_SCANCODE_F15), - luabind::value("F16", SDL_SCANCODE_F16), - luabind::value("F17", SDL_SCANCODE_F17), - luabind::value("F18", SDL_SCANCODE_F18), - luabind::value("F19", SDL_SCANCODE_F19), - luabind::value("F20", SDL_SCANCODE_F20), - luabind::value("F21", SDL_SCANCODE_F21), - luabind::value("F22", SDL_SCANCODE_F22), - luabind::value("F23", SDL_SCANCODE_F23), - luabind::value("F24", SDL_SCANCODE_F24), - luabind::value("EXECUTE", SDL_SCANCODE_EXECUTE), - luabind::value("HELP", SDL_SCANCODE_HELP), - luabind::value("MENU", SDL_SCANCODE_MENU), - luabind::value("SELECT", SDL_SCANCODE_SELECT), - luabind::value("STOP", SDL_SCANCODE_STOP), - luabind::value("AGAIN", SDL_SCANCODE_AGAIN), - luabind::value("UNDO", SDL_SCANCODE_UNDO), - luabind::value("CUT", SDL_SCANCODE_CUT), - luabind::value("COPY", SDL_SCANCODE_COPY), - luabind::value("PASTE", SDL_SCANCODE_PASTE), - luabind::value("FIND", SDL_SCANCODE_FIND), - luabind::value("MUTE", SDL_SCANCODE_MUTE), - luabind::value("VOLUMEUP", SDL_SCANCODE_VOLUMEUP), - luabind::value("VOLUMEDOWN", SDL_SCANCODE_VOLUMEDOWN), - luabind::value("KP_COMMA", SDL_SCANCODE_KP_COMMA), - luabind::value("KP_EQUALSAS400", SDL_SCANCODE_KP_EQUALSAS400), - luabind::value("INTERNATIONAL1", SDL_SCANCODE_INTERNATIONAL1), - luabind::value("INTERNATIONAL2", SDL_SCANCODE_INTERNATIONAL2), - luabind::value("INTERNATIONAL3", SDL_SCANCODE_INTERNATIONAL3), - luabind::value("INTERNATIONAL4", SDL_SCANCODE_INTERNATIONAL4), - luabind::value("INTERNATIONAL5", SDL_SCANCODE_INTERNATIONAL5), - luabind::value("INTERNATIONAL6", SDL_SCANCODE_INTERNATIONAL6), - luabind::value("INTERNATIONAL7", SDL_SCANCODE_INTERNATIONAL7), - luabind::value("INTERNATIONAL8", SDL_SCANCODE_INTERNATIONAL8), - luabind::value("INTERNATIONAL9", SDL_SCANCODE_INTERNATIONAL9), - luabind::value("LANG1", SDL_SCANCODE_LANG1), - luabind::value("LANG2", SDL_SCANCODE_LANG2), - luabind::value("LANG3", SDL_SCANCODE_LANG3), - luabind::value("LANG4", SDL_SCANCODE_LANG4), - luabind::value("LANG5", SDL_SCANCODE_LANG5), - luabind::value("LANG6", SDL_SCANCODE_LANG6), - luabind::value("LANG7", SDL_SCANCODE_LANG7), - luabind::value("LANG8", SDL_SCANCODE_LANG8), - luabind::value("LANG9", SDL_SCANCODE_LANG9), - luabind::value("ALTERASE", SDL_SCANCODE_ALTERASE), - luabind::value("SYSREQ", SDL_SCANCODE_SYSREQ), - luabind::value("CANCEL", SDL_SCANCODE_CANCEL), - luabind::value("CLEAR", SDL_SCANCODE_CLEAR), - luabind::value("PRIOR", SDL_SCANCODE_PRIOR), - luabind::value("RETURN2", SDL_SCANCODE_RETURN2), - luabind::value("SEPARATOR", SDL_SCANCODE_SEPARATOR), - luabind::value("OUT", SDL_SCANCODE_OUT), - luabind::value("OPER", SDL_SCANCODE_OPER), - luabind::value("CLEARAGAIN", SDL_SCANCODE_CLEARAGAIN), - luabind::value("CRSEL", SDL_SCANCODE_CRSEL), - luabind::value("EXSEL", SDL_SCANCODE_EXSEL), - luabind::value("KP_00", SDL_SCANCODE_KP_00), - luabind::value("KP_000", SDL_SCANCODE_KP_000), - luabind::value("THOUSANDSSEPARATOR", SDL_SCANCODE_THOUSANDSSEPARATOR), - luabind::value("DECIMALSEPARATOR", SDL_SCANCODE_DECIMALSEPARATOR), - luabind::value("CURRENCYUNIT", SDL_SCANCODE_CURRENCYUNIT), - luabind::value("CURRENCYSUBUNIT", SDL_SCANCODE_CURRENCYSUBUNIT), - luabind::value("KP_LEFTPAREN", SDL_SCANCODE_KP_LEFTPAREN), - luabind::value("KP_RIGHTPAREN", SDL_SCANCODE_KP_RIGHTPAREN), - luabind::value("KP_LEFTBRACE", SDL_SCANCODE_KP_LEFTBRACE), - luabind::value("KP_RIGHTBRACE", SDL_SCANCODE_KP_RIGHTBRACE), - luabind::value("KP_TAB", SDL_SCANCODE_KP_TAB), - luabind::value("KP_BACKSPACE", SDL_SCANCODE_KP_BACKSPACE), - luabind::value("KP_A", SDL_SCANCODE_KP_A), - luabind::value("KP_B", SDL_SCANCODE_KP_B), - luabind::value("KP_C", SDL_SCANCODE_KP_C), - luabind::value("KP_D", SDL_SCANCODE_KP_D), - luabind::value("KP_E", SDL_SCANCODE_KP_E), - luabind::value("KP_F", SDL_SCANCODE_KP_F), - luabind::value("KP_XOR", SDL_SCANCODE_KP_XOR), - luabind::value("KP_POWER", SDL_SCANCODE_KP_POWER), - luabind::value("KP_PERCENT", SDL_SCANCODE_KP_PERCENT), - luabind::value("KP_LESS", SDL_SCANCODE_KP_LESS), - luabind::value("KP_GREATER", SDL_SCANCODE_KP_GREATER), - luabind::value("KP_AMPERSAND", SDL_SCANCODE_KP_AMPERSAND), - luabind::value("KP_DBLAMPERSAND", SDL_SCANCODE_KP_DBLAMPERSAND), - luabind::value("KP_VERTICALBAR", SDL_SCANCODE_KP_VERTICALBAR), - luabind::value("KP_DBLVERTICALBAR", SDL_SCANCODE_KP_DBLVERTICALBAR), - luabind::value("KP_COLON", SDL_SCANCODE_KP_COLON), - luabind::value("KP_HASH", SDL_SCANCODE_KP_HASH), - luabind::value("KP_SPACE", SDL_SCANCODE_KP_SPACE), - luabind::value("KP_AT", SDL_SCANCODE_KP_AT), - luabind::value("KP_EXCLAM", SDL_SCANCODE_KP_EXCLAM), - luabind::value("KP_MEMSTORE", SDL_SCANCODE_KP_MEMSTORE), - luabind::value("KP_MEMRECALL", SDL_SCANCODE_KP_MEMRECALL), - luabind::value("KP_MEMCLEAR", SDL_SCANCODE_KP_MEMCLEAR), - luabind::value("KP_MEMADD", SDL_SCANCODE_KP_MEMADD), - luabind::value("KP_MEMSUBTRACT", SDL_SCANCODE_KP_MEMSUBTRACT), - luabind::value("KP_MEMMULTIPLY", SDL_SCANCODE_KP_MEMMULTIPLY), - luabind::value("KP_MEMDIVIDE", SDL_SCANCODE_KP_MEMDIVIDE), - luabind::value("KP_PLUSMINUS", SDL_SCANCODE_KP_PLUSMINUS), - luabind::value("KP_CLEAR", SDL_SCANCODE_KP_CLEAR), - luabind::value("KP_CLEARENTRY", SDL_SCANCODE_KP_CLEARENTRY), - luabind::value("KP_BINARY", SDL_SCANCODE_KP_BINARY), - luabind::value("KP_OCTAL", SDL_SCANCODE_KP_OCTAL), - luabind::value("KP_DECIMAL", SDL_SCANCODE_KP_DECIMAL), - luabind::value("KP_HEXADECIMAL", SDL_SCANCODE_KP_HEXADECIMAL), - luabind::value("LCTRL", SDL_SCANCODE_LCTRL), - luabind::value("LSHIFT", SDL_SCANCODE_LSHIFT), - luabind::value("LALT", SDL_SCANCODE_LALT), - luabind::value("LGUI", SDL_SCANCODE_LGUI), - luabind::value("RCTRL", SDL_SCANCODE_RCTRL), - luabind::value("RSHIFT", SDL_SCANCODE_RSHIFT), - luabind::value("RALT", SDL_SCANCODE_RALT), - luabind::value("RGUI", SDL_SCANCODE_RGUI), - luabind::value("MODE", SDL_SCANCODE_MODE), - luabind::value("AUDIONEXT", SDL_SCANCODE_AUDIONEXT), - luabind::value("AUDIOPREV", SDL_SCANCODE_AUDIOPREV), - luabind::value("AUDIOSTOP", SDL_SCANCODE_AUDIOSTOP), - luabind::value("AUDIOPLAY", SDL_SCANCODE_AUDIOPLAY), - luabind::value("AUDIOMUTE", SDL_SCANCODE_AUDIOMUTE), - luabind::value("MEDIASELECT", SDL_SCANCODE_MEDIASELECT), - luabind::value("WWW", SDL_SCANCODE_WWW), - luabind::value("MAIL", SDL_SCANCODE_MAIL), - luabind::value("CALCULATOR", SDL_SCANCODE_CALCULATOR), - luabind::value("COMPUTER", SDL_SCANCODE_COMPUTER), - luabind::value("AC_SEARCH", SDL_SCANCODE_AC_SEARCH), - luabind::value("AC_HOME", SDL_SCANCODE_AC_HOME), - luabind::value("AC_BACK", SDL_SCANCODE_AC_BACK), - luabind::value("AC_FORWARD", SDL_SCANCODE_AC_FORWARD), - luabind::value("AC_STOP", SDL_SCANCODE_AC_STOP), - luabind::value("AC_REFRESH", SDL_SCANCODE_AC_REFRESH), - luabind::value("AC_BOOKMARKS", SDL_SCANCODE_AC_BOOKMARKS), - luabind::value("BRIGHTNESSDOWN", SDL_SCANCODE_BRIGHTNESSDOWN), - luabind::value("BRIGHTNESSUP", SDL_SCANCODE_BRIGHTNESSUP), - luabind::value("DISPLAYSWITCH", SDL_SCANCODE_DISPLAYSWITCH), - luabind::value("KBDILLUMTOGGLE", SDL_SCANCODE_KBDILLUMTOGGLE), - luabind::value("KBDILLUMDOWN", SDL_SCANCODE_KBDILLUMDOWN), - luabind::value("KBDILLUMUP", SDL_SCANCODE_KBDILLUMUP), - luabind::value("EJECT", SDL_SCANCODE_EJECT), - luabind::value("SLEEP", SDL_SCANCODE_SLEEP), - luabind::value("APP1", SDL_SCANCODE_APP1), - luabind::value("APP2", SDL_SCANCODE_APP2), - luabind::value("AUDIOREWIND", SDL_SCANCODE_AUDIOREWIND), - luabind::value("AUDIOFASTFORWARD", SDL_SCANCODE_AUDIOFASTFORWARD), - luabind::value("NUM_SCANCODES", SDL_NUM_SCANCODES) - ]; + // Make sure to update https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/wiki/SDL-Keycode-and-Scancode-enum-values-in-Lua#scan-codes if any changes are made. + .enum_("Scancode")[luabind::value("UNKNOWN", SDL_SCANCODE_UNKNOWN), + luabind::value("A", SDL_SCANCODE_A), + luabind::value("B", SDL_SCANCODE_B), + luabind::value("C", SDL_SCANCODE_C), + luabind::value("D", SDL_SCANCODE_D), + luabind::value("E", SDL_SCANCODE_E), + luabind::value("F", SDL_SCANCODE_F), + luabind::value("G", SDL_SCANCODE_G), + luabind::value("H", SDL_SCANCODE_H), + luabind::value("I", SDL_SCANCODE_I), + luabind::value("J", SDL_SCANCODE_J), + luabind::value("K", SDL_SCANCODE_K), + luabind::value("L", SDL_SCANCODE_L), + luabind::value("M", SDL_SCANCODE_M), + luabind::value("N", SDL_SCANCODE_N), + luabind::value("O", SDL_SCANCODE_O), + luabind::value("P", SDL_SCANCODE_P), + luabind::value("Q", SDL_SCANCODE_Q), + luabind::value("R", SDL_SCANCODE_R), + luabind::value("S", SDL_SCANCODE_S), + luabind::value("T", SDL_SCANCODE_T), + luabind::value("U", SDL_SCANCODE_U), + luabind::value("V", SDL_SCANCODE_V), + luabind::value("W", SDL_SCANCODE_W), + luabind::value("X", SDL_SCANCODE_X), + luabind::value("Y", SDL_SCANCODE_Y), + luabind::value("Z", SDL_SCANCODE_Z), + luabind::value("K_1", SDL_SCANCODE_1), + luabind::value("K_2", SDL_SCANCODE_2), + luabind::value("K_3", SDL_SCANCODE_3), + luabind::value("K_4", SDL_SCANCODE_4), + luabind::value("K_5", SDL_SCANCODE_5), + luabind::value("K_6", SDL_SCANCODE_6), + luabind::value("K_7", SDL_SCANCODE_7), + luabind::value("K_8", SDL_SCANCODE_8), + luabind::value("K_9", SDL_SCANCODE_9), + luabind::value("K_0", SDL_SCANCODE_0), + luabind::value("RETURN", SDL_SCANCODE_RETURN), + luabind::value("ESCAPE", SDL_SCANCODE_ESCAPE), + luabind::value("BACKSPACE", SDL_SCANCODE_BACKSPACE), + luabind::value("TAB", SDL_SCANCODE_TAB), + luabind::value("SPACE", SDL_SCANCODE_SPACE), + luabind::value("MINUS", SDL_SCANCODE_MINUS), + luabind::value("EQUALS", SDL_SCANCODE_EQUALS), + luabind::value("LEFTBRACKET", SDL_SCANCODE_LEFTBRACKET), + luabind::value("RIGHTBRACKET", SDL_SCANCODE_RIGHTBRACKET), + luabind::value("BACKSLASH", SDL_SCANCODE_BACKSLASH), + luabind::value("NONUSHASH", SDL_SCANCODE_NONUSHASH), + luabind::value("SEMICOLON", SDL_SCANCODE_SEMICOLON), + luabind::value("APOSTROPHE", SDL_SCANCODE_APOSTROPHE), + luabind::value("GRAVE", SDL_SCANCODE_GRAVE), + luabind::value("COMMA", SDL_SCANCODE_COMMA), + luabind::value("PERIOD", SDL_SCANCODE_PERIOD), + luabind::value("SLASH", SDL_SCANCODE_SLASH), + luabind::value("CAPSLOCK", SDL_SCANCODE_CAPSLOCK), + luabind::value("F1", SDL_SCANCODE_F1), + luabind::value("F2", SDL_SCANCODE_F2), + luabind::value("F3", SDL_SCANCODE_F3), + luabind::value("F4", SDL_SCANCODE_F4), + luabind::value("F5", SDL_SCANCODE_F5), + luabind::value("F6", SDL_SCANCODE_F6), + luabind::value("F7", SDL_SCANCODE_F7), + luabind::value("F8", SDL_SCANCODE_F8), + luabind::value("F9", SDL_SCANCODE_F9), + luabind::value("F10", SDL_SCANCODE_F10), + luabind::value("F11", SDL_SCANCODE_F11), + luabind::value("F12", SDL_SCANCODE_F12), + luabind::value("PRINTSCREEN", SDL_SCANCODE_PRINTSCREEN), + luabind::value("SCROLLLOCK", SDL_SCANCODE_SCROLLLOCK), + luabind::value("PAUSE", SDL_SCANCODE_PAUSE), + luabind::value("INSERT", SDL_SCANCODE_INSERT), + luabind::value("HOME", SDL_SCANCODE_HOME), + luabind::value("PAGEUP", SDL_SCANCODE_PAGEUP), + luabind::value("DELETE", SDL_SCANCODE_DELETE), + luabind::value("END", SDL_SCANCODE_END), + luabind::value("PAGEDOWN", SDL_SCANCODE_PAGEDOWN), + luabind::value("RIGHT", SDL_SCANCODE_RIGHT), + luabind::value("LEFT", SDL_SCANCODE_LEFT), + luabind::value("DOWN", SDL_SCANCODE_DOWN), + luabind::value("UP", SDL_SCANCODE_UP), + luabind::value("NUMLOCKCLEAR", SDL_SCANCODE_NUMLOCKCLEAR), + luabind::value("KP_DIVIDE", SDL_SCANCODE_KP_DIVIDE), + luabind::value("KP_MULTIPLY", SDL_SCANCODE_KP_MULTIPLY), + luabind::value("KP_MINUS", SDL_SCANCODE_KP_MINUS), + luabind::value("KP_PLUS", SDL_SCANCODE_KP_PLUS), + luabind::value("KP_ENTER", SDL_SCANCODE_KP_ENTER), + luabind::value("KP_1", SDL_SCANCODE_KP_1), + luabind::value("KP_2", SDL_SCANCODE_KP_2), + luabind::value("KP_3", SDL_SCANCODE_KP_3), + luabind::value("KP_4", SDL_SCANCODE_KP_4), + luabind::value("KP_5", SDL_SCANCODE_KP_5), + luabind::value("KP_6", SDL_SCANCODE_KP_6), + luabind::value("KP_7", SDL_SCANCODE_KP_7), + luabind::value("KP_8", SDL_SCANCODE_KP_8), + luabind::value("KP_9", SDL_SCANCODE_KP_9), + luabind::value("KP_0", SDL_SCANCODE_KP_0), + luabind::value("KP_PERIOD", SDL_SCANCODE_KP_PERIOD), + luabind::value("NONUSBACKSLASH", SDL_SCANCODE_NONUSBACKSLASH), + luabind::value("APPLICATION", SDL_SCANCODE_APPLICATION), + luabind::value("POWER", SDL_SCANCODE_POWER), + luabind::value("KP_EQUALS", SDL_SCANCODE_KP_EQUALS), + luabind::value("F13", SDL_SCANCODE_F13), + luabind::value("F14", SDL_SCANCODE_F14), + luabind::value("F15", SDL_SCANCODE_F15), + luabind::value("F16", SDL_SCANCODE_F16), + luabind::value("F17", SDL_SCANCODE_F17), + luabind::value("F18", SDL_SCANCODE_F18), + luabind::value("F19", SDL_SCANCODE_F19), + luabind::value("F20", SDL_SCANCODE_F20), + luabind::value("F21", SDL_SCANCODE_F21), + luabind::value("F22", SDL_SCANCODE_F22), + luabind::value("F23", SDL_SCANCODE_F23), + luabind::value("F24", SDL_SCANCODE_F24), + luabind::value("EXECUTE", SDL_SCANCODE_EXECUTE), + luabind::value("HELP", SDL_SCANCODE_HELP), + luabind::value("MENU", SDL_SCANCODE_MENU), + luabind::value("SELECT", SDL_SCANCODE_SELECT), + luabind::value("STOP", SDL_SCANCODE_STOP), + luabind::value("AGAIN", SDL_SCANCODE_AGAIN), + luabind::value("UNDO", SDL_SCANCODE_UNDO), + luabind::value("CUT", SDL_SCANCODE_CUT), + luabind::value("COPY", SDL_SCANCODE_COPY), + luabind::value("PASTE", SDL_SCANCODE_PASTE), + luabind::value("FIND", SDL_SCANCODE_FIND), + luabind::value("MUTE", SDL_SCANCODE_MUTE), + luabind::value("VOLUMEUP", SDL_SCANCODE_VOLUMEUP), + luabind::value("VOLUMEDOWN", SDL_SCANCODE_VOLUMEDOWN), + luabind::value("KP_COMMA", SDL_SCANCODE_KP_COMMA), + luabind::value("KP_EQUALSAS400", SDL_SCANCODE_KP_EQUALSAS400), + luabind::value("INTERNATIONAL1", SDL_SCANCODE_INTERNATIONAL1), + luabind::value("INTERNATIONAL2", SDL_SCANCODE_INTERNATIONAL2), + luabind::value("INTERNATIONAL3", SDL_SCANCODE_INTERNATIONAL3), + luabind::value("INTERNATIONAL4", SDL_SCANCODE_INTERNATIONAL4), + luabind::value("INTERNATIONAL5", SDL_SCANCODE_INTERNATIONAL5), + luabind::value("INTERNATIONAL6", SDL_SCANCODE_INTERNATIONAL6), + luabind::value("INTERNATIONAL7", SDL_SCANCODE_INTERNATIONAL7), + luabind::value("INTERNATIONAL8", SDL_SCANCODE_INTERNATIONAL8), + luabind::value("INTERNATIONAL9", SDL_SCANCODE_INTERNATIONAL9), + luabind::value("LANG1", SDL_SCANCODE_LANG1), + luabind::value("LANG2", SDL_SCANCODE_LANG2), + luabind::value("LANG3", SDL_SCANCODE_LANG3), + luabind::value("LANG4", SDL_SCANCODE_LANG4), + luabind::value("LANG5", SDL_SCANCODE_LANG5), + luabind::value("LANG6", SDL_SCANCODE_LANG6), + luabind::value("LANG7", SDL_SCANCODE_LANG7), + luabind::value("LANG8", SDL_SCANCODE_LANG8), + luabind::value("LANG9", SDL_SCANCODE_LANG9), + luabind::value("ALTERASE", SDL_SCANCODE_ALTERASE), + luabind::value("SYSREQ", SDL_SCANCODE_SYSREQ), + luabind::value("CANCEL", SDL_SCANCODE_CANCEL), + luabind::value("CLEAR", SDL_SCANCODE_CLEAR), + luabind::value("PRIOR", SDL_SCANCODE_PRIOR), + luabind::value("RETURN2", SDL_SCANCODE_RETURN2), + luabind::value("SEPARATOR", SDL_SCANCODE_SEPARATOR), + luabind::value("OUT", SDL_SCANCODE_OUT), + luabind::value("OPER", SDL_SCANCODE_OPER), + luabind::value("CLEARAGAIN", SDL_SCANCODE_CLEARAGAIN), + luabind::value("CRSEL", SDL_SCANCODE_CRSEL), + luabind::value("EXSEL", SDL_SCANCODE_EXSEL), + luabind::value("KP_00", SDL_SCANCODE_KP_00), + luabind::value("KP_000", SDL_SCANCODE_KP_000), + luabind::value("THOUSANDSSEPARATOR", SDL_SCANCODE_THOUSANDSSEPARATOR), + luabind::value("DECIMALSEPARATOR", SDL_SCANCODE_DECIMALSEPARATOR), + luabind::value("CURRENCYUNIT", SDL_SCANCODE_CURRENCYUNIT), + luabind::value("CURRENCYSUBUNIT", SDL_SCANCODE_CURRENCYSUBUNIT), + luabind::value("KP_LEFTPAREN", SDL_SCANCODE_KP_LEFTPAREN), + luabind::value("KP_RIGHTPAREN", SDL_SCANCODE_KP_RIGHTPAREN), + luabind::value("KP_LEFTBRACE", SDL_SCANCODE_KP_LEFTBRACE), + luabind::value("KP_RIGHTBRACE", SDL_SCANCODE_KP_RIGHTBRACE), + luabind::value("KP_TAB", SDL_SCANCODE_KP_TAB), + luabind::value("KP_BACKSPACE", SDL_SCANCODE_KP_BACKSPACE), + luabind::value("KP_A", SDL_SCANCODE_KP_A), + luabind::value("KP_B", SDL_SCANCODE_KP_B), + luabind::value("KP_C", SDL_SCANCODE_KP_C), + luabind::value("KP_D", SDL_SCANCODE_KP_D), + luabind::value("KP_E", SDL_SCANCODE_KP_E), + luabind::value("KP_F", SDL_SCANCODE_KP_F), + luabind::value("KP_XOR", SDL_SCANCODE_KP_XOR), + luabind::value("KP_POWER", SDL_SCANCODE_KP_POWER), + luabind::value("KP_PERCENT", SDL_SCANCODE_KP_PERCENT), + luabind::value("KP_LESS", SDL_SCANCODE_KP_LESS), + luabind::value("KP_GREATER", SDL_SCANCODE_KP_GREATER), + luabind::value("KP_AMPERSAND", SDL_SCANCODE_KP_AMPERSAND), + luabind::value("KP_DBLAMPERSAND", SDL_SCANCODE_KP_DBLAMPERSAND), + luabind::value("KP_VERTICALBAR", SDL_SCANCODE_KP_VERTICALBAR), + luabind::value("KP_DBLVERTICALBAR", SDL_SCANCODE_KP_DBLVERTICALBAR), + luabind::value("KP_COLON", SDL_SCANCODE_KP_COLON), + luabind::value("KP_HASH", SDL_SCANCODE_KP_HASH), + luabind::value("KP_SPACE", SDL_SCANCODE_KP_SPACE), + luabind::value("KP_AT", SDL_SCANCODE_KP_AT), + luabind::value("KP_EXCLAM", SDL_SCANCODE_KP_EXCLAM), + luabind::value("KP_MEMSTORE", SDL_SCANCODE_KP_MEMSTORE), + luabind::value("KP_MEMRECALL", SDL_SCANCODE_KP_MEMRECALL), + luabind::value("KP_MEMCLEAR", SDL_SCANCODE_KP_MEMCLEAR), + luabind::value("KP_MEMADD", SDL_SCANCODE_KP_MEMADD), + luabind::value("KP_MEMSUBTRACT", SDL_SCANCODE_KP_MEMSUBTRACT), + luabind::value("KP_MEMMULTIPLY", SDL_SCANCODE_KP_MEMMULTIPLY), + luabind::value("KP_MEMDIVIDE", SDL_SCANCODE_KP_MEMDIVIDE), + luabind::value("KP_PLUSMINUS", SDL_SCANCODE_KP_PLUSMINUS), + luabind::value("KP_CLEAR", SDL_SCANCODE_KP_CLEAR), + luabind::value("KP_CLEARENTRY", SDL_SCANCODE_KP_CLEARENTRY), + luabind::value("KP_BINARY", SDL_SCANCODE_KP_BINARY), + luabind::value("KP_OCTAL", SDL_SCANCODE_KP_OCTAL), + luabind::value("KP_DECIMAL", SDL_SCANCODE_KP_DECIMAL), + luabind::value("KP_HEXADECIMAL", SDL_SCANCODE_KP_HEXADECIMAL), + luabind::value("LCTRL", SDL_SCANCODE_LCTRL), + luabind::value("LSHIFT", SDL_SCANCODE_LSHIFT), + luabind::value("LALT", SDL_SCANCODE_LALT), + luabind::value("LGUI", SDL_SCANCODE_LGUI), + luabind::value("RCTRL", SDL_SCANCODE_RCTRL), + luabind::value("RSHIFT", SDL_SCANCODE_RSHIFT), + luabind::value("RALT", SDL_SCANCODE_RALT), + luabind::value("RGUI", SDL_SCANCODE_RGUI), + luabind::value("MODE", SDL_SCANCODE_MODE), + luabind::value("AUDIONEXT", SDL_SCANCODE_AUDIONEXT), + luabind::value("AUDIOPREV", SDL_SCANCODE_AUDIOPREV), + luabind::value("AUDIOSTOP", SDL_SCANCODE_AUDIOSTOP), + luabind::value("AUDIOPLAY", SDL_SCANCODE_AUDIOPLAY), + luabind::value("AUDIOMUTE", SDL_SCANCODE_AUDIOMUTE), + luabind::value("MEDIASELECT", SDL_SCANCODE_MEDIASELECT), + luabind::value("WWW", SDL_SCANCODE_WWW), + luabind::value("MAIL", SDL_SCANCODE_MAIL), + luabind::value("CALCULATOR", SDL_SCANCODE_CALCULATOR), + luabind::value("COMPUTER", SDL_SCANCODE_COMPUTER), + luabind::value("AC_SEARCH", SDL_SCANCODE_AC_SEARCH), + luabind::value("AC_HOME", SDL_SCANCODE_AC_HOME), + luabind::value("AC_BACK", SDL_SCANCODE_AC_BACK), + luabind::value("AC_FORWARD", SDL_SCANCODE_AC_FORWARD), + luabind::value("AC_STOP", SDL_SCANCODE_AC_STOP), + luabind::value("AC_REFRESH", SDL_SCANCODE_AC_REFRESH), + luabind::value("AC_BOOKMARKS", SDL_SCANCODE_AC_BOOKMARKS), + luabind::value("BRIGHTNESSDOWN", SDL_SCANCODE_BRIGHTNESSDOWN), + luabind::value("BRIGHTNESSUP", SDL_SCANCODE_BRIGHTNESSUP), + luabind::value("DISPLAYSWITCH", SDL_SCANCODE_DISPLAYSWITCH), + luabind::value("KBDILLUMTOGGLE", SDL_SCANCODE_KBDILLUMTOGGLE), + luabind::value("KBDILLUMDOWN", SDL_SCANCODE_KBDILLUMDOWN), + luabind::value("KBDILLUMUP", SDL_SCANCODE_KBDILLUMUP), + luabind::value("EJECT", SDL_SCANCODE_EJECT), + luabind::value("SLEEP", SDL_SCANCODE_SLEEP), + luabind::value("APP1", SDL_SCANCODE_APP1), + luabind::value("APP2", SDL_SCANCODE_APP2), + luabind::value("AUDIOREWIND", SDL_SCANCODE_AUDIOREWIND), + luabind::value("AUDIOFASTFORWARD", SDL_SCANCODE_AUDIOFASTFORWARD), + luabind::value("NUM_SCANCODES", SDL_NUM_SCANCODES)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, SDL_GameControllerButton) { return luabind::class_("GamepadButton") - .enum_("GamepadButton")[ - luabind::value("INVALID", SDL_CONTROLLER_BUTTON_INVALID), - luabind::value("A", SDL_CONTROLLER_BUTTON_A), - luabind::value("B", SDL_CONTROLLER_BUTTON_B), - luabind::value("X", SDL_CONTROLLER_BUTTON_X), - luabind::value("Y", SDL_CONTROLLER_BUTTON_Y), - luabind::value("BACK", SDL_CONTROLLER_BUTTON_BACK), - luabind::value("GUIDE", SDL_CONTROLLER_BUTTON_GUIDE), - luabind::value("START", SDL_CONTROLLER_BUTTON_START), - luabind::value("LEFTSTICK", SDL_CONTROLLER_BUTTON_LEFTSTICK), - luabind::value("RIGHTSTICK", SDL_CONTROLLER_BUTTON_RIGHTSTICK), - luabind::value("LEFTSHOULDER", SDL_CONTROLLER_BUTTON_LEFTSHOULDER), - luabind::value("RIGHTSHOULDER", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), - luabind::value("DPAD_UP", SDL_CONTROLLER_BUTTON_DPAD_UP), - luabind::value("DPAD_DOWN", SDL_CONTROLLER_BUTTON_DPAD_DOWN), - luabind::value("DPAD_LEFT", SDL_CONTROLLER_BUTTON_DPAD_LEFT), - luabind::value("DPAD_RIGHT", SDL_CONTROLLER_BUTTON_DPAD_RIGHT), - luabind::value("MAX", SDL_CONTROLLER_BUTTON_MAX) - ]; + .enum_("GamepadButton")[luabind::value("INVALID", SDL_CONTROLLER_BUTTON_INVALID), + luabind::value("A", SDL_CONTROLLER_BUTTON_A), + luabind::value("B", SDL_CONTROLLER_BUTTON_B), + luabind::value("X", SDL_CONTROLLER_BUTTON_X), + luabind::value("Y", SDL_CONTROLLER_BUTTON_Y), + luabind::value("BACK", SDL_CONTROLLER_BUTTON_BACK), + luabind::value("GUIDE", SDL_CONTROLLER_BUTTON_GUIDE), + luabind::value("START", SDL_CONTROLLER_BUTTON_START), + luabind::value("LEFTSTICK", SDL_CONTROLLER_BUTTON_LEFTSTICK), + luabind::value("RIGHTSTICK", SDL_CONTROLLER_BUTTON_RIGHTSTICK), + luabind::value("LEFTSHOULDER", SDL_CONTROLLER_BUTTON_LEFTSHOULDER), + luabind::value("RIGHTSHOULDER", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), + luabind::value("DPAD_UP", SDL_CONTROLLER_BUTTON_DPAD_UP), + luabind::value("DPAD_DOWN", SDL_CONTROLLER_BUTTON_DPAD_DOWN), + luabind::value("DPAD_LEFT", SDL_CONTROLLER_BUTTON_DPAD_LEFT), + luabind::value("DPAD_RIGHT", SDL_CONTROLLER_BUTTON_DPAD_RIGHT), + luabind::value("MAX", SDL_CONTROLLER_BUTTON_MAX)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, SDL_GameControllerAxis) { return luabind::class_("GamepadAxis") - .enum_("GamepadAxis")[ - luabind::value("INVALID", SDL_CONTROLLER_AXIS_INVALID), - luabind::value("LEFTX", SDL_CONTROLLER_AXIS_LEFTX), - luabind::value("LEFTY", SDL_CONTROLLER_AXIS_LEFTY), - luabind::value("RIGHTX", SDL_CONTROLLER_AXIS_RIGHTX), - luabind::value("RIGHTY", SDL_CONTROLLER_AXIS_RIGHTY), - luabind::value("TRIGGERLEFT", SDL_CONTROLLER_AXIS_TRIGGERLEFT), - luabind::value("TRIGGERRIGHT", SDL_CONTROLLER_AXIS_TRIGGERRIGHT), - luabind::value("MAX", SDL_CONTROLLER_AXIS_MAX) - ]; + .enum_("GamepadAxis")[luabind::value("INVALID", SDL_CONTROLLER_AXIS_INVALID), + luabind::value("LEFTX", SDL_CONTROLLER_AXIS_LEFTX), + luabind::value("LEFTY", SDL_CONTROLLER_AXIS_LEFTY), + luabind::value("RIGHTX", SDL_CONTROLLER_AXIS_RIGHTX), + luabind::value("RIGHTY", SDL_CONTROLLER_AXIS_RIGHTY), + luabind::value("TRIGGERLEFT", SDL_CONTROLLER_AXIS_TRIGGERLEFT), + luabind::value("TRIGGERRIGHT", SDL_CONTROLLER_AXIS_TRIGGERRIGHT), + luabind::value("MAX", SDL_CONTROLLER_AXIS_MAX)]; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Lua/LuaBindingsManagers.cpp b/Source/Lua/LuaBindingsManagers.cpp index 5ec1706fb4..47226dbd3f 100644 --- a/Source/Lua/LuaBindingsManagers.cpp +++ b/Source/Lua/LuaBindingsManagers.cpp @@ -4,372 +4,372 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, ActivityMan) { return luabind::class_("ActivityManager") - .property("DefaultActivityType", &ActivityMan::GetDefaultActivityType, &ActivityMan::SetDefaultActivityType) - .property("DefaultActivityName", &ActivityMan::GetDefaultActivityName, &ActivityMan::SetDefaultActivityName) - - .def("SetStartActivity", &ActivityMan::SetStartActivity, luabind::adopt(_2)) // Transfers ownership of the Activity to start into the ActivityMan, adopts ownership (_1 is the this ptr) - .def("GetStartActivity", &ActivityMan::GetStartActivity) - .def("GetActivity", &ActivityMan::GetActivity) - .def("StartActivity", (int (ActivityMan::*)(Activity *))&ActivityMan::StartActivity, luabind::adopt(_2)) // Transfers ownership of the Activity to start into the ActivityMan, adopts ownership (_1 is the this ptr) - .def("StartActivity", (int (ActivityMan::*)(const std::string &, const std::string &))&ActivityMan::StartActivity) - .def("RestartActivity", &ActivityMan::RestartActivity) - .def("PauseActivity", &ActivityMan::PauseActivity) - .def("EndActivity", &ActivityMan::EndActivity) - .def("ActivityRunning", &ActivityMan::ActivityRunning) - .def("ActivityPaused", &ActivityMan::ActivityPaused) - .def("SaveGame", &ActivityMan::SaveCurrentGame) - .def("LoadGame", &ActivityMan::LoadAndLaunchGame); + .property("DefaultActivityType", &ActivityMan::GetDefaultActivityType, &ActivityMan::SetDefaultActivityType) + .property("DefaultActivityName", &ActivityMan::GetDefaultActivityName, &ActivityMan::SetDefaultActivityName) + + .def("SetStartActivity", &ActivityMan::SetStartActivity, luabind::adopt(_2)) // Transfers ownership of the Activity to start into the ActivityMan, adopts ownership (_1 is the this ptr) + .def("GetStartActivity", &ActivityMan::GetStartActivity) + .def("GetActivity", &ActivityMan::GetActivity) + .def("StartActivity", (int(ActivityMan::*)(Activity*)) & ActivityMan::StartActivity, luabind::adopt(_2)) // Transfers ownership of the Activity to start into the ActivityMan, adopts ownership (_1 is the this ptr) + .def("StartActivity", (int(ActivityMan::*)(const std::string&, const std::string&)) & ActivityMan::StartActivity) + .def("RestartActivity", &ActivityMan::RestartActivity) + .def("PauseActivity", &ActivityMan::PauseActivity) + .def("EndActivity", &ActivityMan::EndActivity) + .def("ActivityRunning", &ActivityMan::ActivityRunning) + .def("ActivityPaused", &ActivityMan::ActivityPaused) + .def("SaveGame", &ActivityMan::SaveCurrentGame) + .def("LoadGame", &ActivityMan::LoadAndLaunchGame); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, AudioMan) { return luabind::class_("AudioManager") - .property("MusicVolume", &AudioMan::GetMusicVolume, &AudioMan::SetMusicVolume) - .property("SoundsVolume", &AudioMan::GetSoundsVolume, &AudioMan::SetSoundsVolume) - - .def("StopAll", &AudioMan::StopAll) - .def("GetGlobalPitch", &AudioMan::GetGlobalPitch) - .def("IsMusicPlaying", &AudioMan::IsMusicPlaying) - .def("SetTempMusicVolume", &AudioMan::SetTempMusicVolume) - .def("GetMusicPosition", &AudioMan::GetMusicPosition) - .def("SetMusicPosition", &AudioMan::SetMusicPosition) - .def("SetMusicPitch", &AudioMan::SetMusicPitch) - .def("StopMusic", &AudioMan::StopMusic) - .def("PlayMusic", &AudioMan::PlayMusic) - .def("PlayNextStream", &AudioMan::PlayNextStream) - .def("StopMusic", &AudioMan::StopMusic) - .def("QueueMusicStream", &AudioMan::QueueMusicStream) - .def("QueueSilence", &AudioMan::QueueSilence) - .def("ClearMusicQueue", &AudioMan::ClearMusicQueue) - .def("PlaySound", (SoundContainer *(AudioMan:: *)(const std::string &filePath)) &AudioMan::PlaySound, luabind::adopt(luabind::result)) - .def("PlaySound", (SoundContainer *(AudioMan:: *)(const std::string &filePath, const Vector &position)) &AudioMan::PlaySound, luabind::adopt(luabind::result)) - .def("PlaySound", (SoundContainer *(AudioMan:: *)(const std::string &filePath, const Vector &position, int player)) &AudioMan::PlaySound, luabind::adopt(luabind::result)); + .property("MusicVolume", &AudioMan::GetMusicVolume, &AudioMan::SetMusicVolume) + .property("SoundsVolume", &AudioMan::GetSoundsVolume, &AudioMan::SetSoundsVolume) + + .def("StopAll", &AudioMan::StopAll) + .def("GetGlobalPitch", &AudioMan::GetGlobalPitch) + .def("IsMusicPlaying", &AudioMan::IsMusicPlaying) + .def("SetTempMusicVolume", &AudioMan::SetTempMusicVolume) + .def("GetMusicPosition", &AudioMan::GetMusicPosition) + .def("SetMusicPosition", &AudioMan::SetMusicPosition) + .def("SetMusicPitch", &AudioMan::SetMusicPitch) + .def("StopMusic", &AudioMan::StopMusic) + .def("PlayMusic", &AudioMan::PlayMusic) + .def("PlayNextStream", &AudioMan::PlayNextStream) + .def("StopMusic", &AudioMan::StopMusic) + .def("QueueMusicStream", &AudioMan::QueueMusicStream) + .def("QueueSilence", &AudioMan::QueueSilence) + .def("ClearMusicQueue", &AudioMan::ClearMusicQueue) + .def("PlaySound", (SoundContainer * (AudioMan::*)(const std::string& filePath)) & AudioMan::PlaySound, luabind::adopt(luabind::result)) + .def("PlaySound", (SoundContainer * (AudioMan::*)(const std::string& filePath, const Vector& position)) & AudioMan::PlaySound, luabind::adopt(luabind::result)) + .def("PlaySound", (SoundContainer * (AudioMan::*)(const std::string& filePath, const Vector& position, int player)) & AudioMan::PlaySound, luabind::adopt(luabind::result)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, ConsoleMan) { return luabind::class_("ConsoleManager") - .def("PrintString", &ConsoleMan::PrintString) - .def("SaveInputLog", &ConsoleMan::SaveInputLog) - .def("SaveAllText", &ConsoleMan::SaveAllText) - .def("Clear", &ConsoleMan::ClearLog); + .def("PrintString", &ConsoleMan::PrintString) + .def("SaveInputLog", &ConsoleMan::SaveInputLog) + .def("SaveAllText", &ConsoleMan::SaveAllText) + .def("Clear", &ConsoleMan::ClearLog); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, FrameMan) { return luabind::class_("FrameManager") - .property("PlayerScreenWidth", &FrameMan::GetPlayerScreenWidth) - .property("PlayerScreenHeight", &FrameMan::GetPlayerScreenHeight) - .property("ScreenCount", &FrameMan::GetScreenCount) - - .def("IsHudDisabled", &FrameMan::IsHudDisabled) - .def("SetHudDisabled", &FrameMan::SetHudDisabled) - .def("LoadPalette", &FrameMan::LoadPalette) - .def("SetScreenText", &FrameMan::SetScreenText) - .def("ClearScreenText", &FrameMan::ClearScreenText) - .def("FadeInPalette", &FrameMan::FadeInPalette) - .def("FadeOutPalette", &FrameMan::FadeOutPalette) - .def("SaveScreenToPNG", &FrameMan::SaveScreenToPNG) - .def("SaveBitmapToPNG", &FrameMan::SaveBitmapToPNG) - .def("FlashScreen", &FrameMan::FlashScreen) - .def("CalculateTextHeight", &FrameMan::CalculateTextHeight) - .def("CalculateTextWidth", &FrameMan::CalculateTextWidth) - .def("SplitStringToFitWidth", &FrameMan::SplitStringToFitWidth); + .property("PlayerScreenWidth", &FrameMan::GetPlayerScreenWidth) + .property("PlayerScreenHeight", &FrameMan::GetPlayerScreenHeight) + .property("ScreenCount", &FrameMan::GetScreenCount) + + .def("IsHudDisabled", &FrameMan::IsHudDisabled) + .def("SetHudDisabled", &FrameMan::SetHudDisabled) + .def("LoadPalette", &FrameMan::LoadPalette) + .def("SetScreenText", &FrameMan::SetScreenText) + .def("ClearScreenText", &FrameMan::ClearScreenText) + .def("FadeInPalette", &FrameMan::FadeInPalette) + .def("FadeOutPalette", &FrameMan::FadeOutPalette) + .def("SaveScreenToPNG", &FrameMan::SaveScreenToPNG) + .def("SaveBitmapToPNG", &FrameMan::SaveBitmapToPNG) + .def("FlashScreen", &FrameMan::FlashScreen) + .def("CalculateTextHeight", &FrameMan::CalculateTextHeight) + .def("CalculateTextWidth", &FrameMan::CalculateTextWidth) + .def("SplitStringToFitWidth", &FrameMan::SplitStringToFitWidth); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, MetaMan) { return luabind::class_("MetaManager") - .property("GameName", &MetaMan::GetGameName, &MetaMan::SetGameName) - .property("PlayerTurn", &MetaMan::GetPlayerTurn) - .property("PlayerCount", &MetaMan::GetPlayerCount) + .property("GameName", &MetaMan::GetGameName, &MetaMan::SetGameName) + .property("PlayerTurn", &MetaMan::GetPlayerTurn) + .property("PlayerCount", &MetaMan::GetPlayerCount) - .def_readwrite("Players", &MetaMan::m_Players, luabind::return_stl_iterator) + .def_readwrite("Players", &MetaMan::m_Players, luabind::return_stl_iterator) - .def("GetTeamOfPlayer", &MetaMan::GetTeamOfPlayer) - .def("GetPlayer", &MetaMan::GetPlayer) - .def("GetMetaPlayerOfInGamePlayer", &MetaMan::GetMetaPlayerOfInGamePlayer); + .def("GetTeamOfPlayer", &MetaMan::GetTeamOfPlayer) + .def("GetPlayer", &MetaMan::GetPlayer) + .def("GetMetaPlayerOfInGamePlayer", &MetaMan::GetMetaPlayerOfInGamePlayer); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, MovableMan) { return luabind::class_("MovableManager") - .property("MaxDroppedItems", &MovableMan::GetMaxDroppedItems, &MovableMan::SetMaxDroppedItems) - - .def_readwrite("Actors", &MovableMan::m_Actors, luabind::return_stl_iterator) - .def_readwrite("Items", &MovableMan::m_Items, luabind::return_stl_iterator) - .def_readwrite("Particles", &MovableMan::m_Particles, luabind::return_stl_iterator) - .def_readwrite("AddedActors", &MovableMan::m_AddedActors, luabind::return_stl_iterator) - .def_readwrite("AddedItems", &MovableMan::m_AddedItems, luabind::return_stl_iterator) - .def_readwrite("AddedParticles", &MovableMan::m_AddedParticles, luabind::return_stl_iterator) - .def_readwrite("AlarmEvents", &MovableMan::m_AlarmEvents, luabind::return_stl_iterator) - .def_readwrite("AddedAlarmEvents", &MovableMan::m_AddedAlarmEvents, luabind::return_stl_iterator) - - .def("GetMOFromID", &MovableMan::GetMOFromID) - .def("FindObjectByUniqueID", &MovableMan::FindObjectByUniqueID) - .def("GetMOIDCount", &MovableMan::GetMOIDCount) - .def("GetTeamMOIDCount", &MovableMan::GetTeamMOIDCount) - .def("PurgeAllMOs", &MovableMan::PurgeAllMOs) - .def("GetNextActorInGroup", &MovableMan::GetNextActorInGroup) - .def("GetPrevActorInGroup", &MovableMan::GetPrevActorInGroup) - .def("GetNextTeamActor", &MovableMan::GetNextTeamActor) - .def("GetPrevTeamActor", &MovableMan::GetPrevTeamActor) - .def("GetClosestTeamActor", (Actor * (MovableMan::*)(int team, int player, const Vector &scenePoint, int maxRadius, Vector &getDistance, const Actor *excludeThis))&MovableMan::GetClosestTeamActor) - .def("GetClosestTeamActor", (Actor * (MovableMan::*)(int team, int player, const Vector &scenePoint, int maxRadius, Vector &getDistance, bool onlyPlayerControllableActors, const Actor *excludeThis))&MovableMan::GetClosestTeamActor) - .def("GetClosestEnemyActor", &MovableMan::GetClosestEnemyActor) - .def("GetFirstTeamActor", &MovableMan::GetFirstTeamActor) - .def("GetClosestActor", &MovableMan::GetClosestActor) - .def("GetClosestBrainActor", &MovableMan::GetClosestBrainActor) - .def("GetFirstBrainActor", &MovableMan::GetFirstBrainActor) - .def("GetClosestOtherBrainActor", &MovableMan::GetClosestOtherBrainActor) - .def("GetFirstOtherBrainActor", &MovableMan::GetFirstOtherBrainActor) - .def("GetUnassignedBrain", &MovableMan::GetUnassignedBrain) - .def("GetParticleCount", &MovableMan::GetParticleCount) - .def("GetSplashRatio", &MovableMan::GetSplashRatio) - .def("SortTeamRoster", &MovableMan::SortTeamRoster) - .def("ChangeActorTeam", &MovableMan::ChangeActorTeam) - .def("RemoveActor", &MovableMan::RemoveActor, luabind::adopt(luabind::return_value)) - .def("RemoveItem", &MovableMan::RemoveItem, luabind::adopt(luabind::return_value)) - .def("RemoveParticle", &MovableMan::RemoveParticle, luabind::adopt(luabind::return_value)) - .def("ValidMO", &MovableMan::ValidMO) - .def("IsActor", &MovableMan::IsActor) - .def("IsDevice", &MovableMan::IsDevice) - .def("IsParticle", &MovableMan::IsParticle) - .def("IsOfActor", &MovableMan::IsOfActor) - .def("GetRootMOID", &MovableMan::GetRootMOID) - .def("RemoveMO", &MovableMan::RemoveMO) - .def("KillAllTeamActors", &MovableMan::KillAllTeamActors) - .def("KillAllEnemyActors", &MovableMan::KillAllEnemyActors) - .def("OpenAllDoors", &MovableMan::OpenAllDoors) - .def("IsParticleSettlingEnabled", &MovableMan::IsParticleSettlingEnabled) - .def("EnableParticleSettling", &MovableMan::EnableParticleSettling) - .def("IsMOSubtractionEnabled", &MovableMan::IsMOSubtractionEnabled) - .def("GetMOsInBox", (const std::vector * (MovableMan::*)(const Box &box) const)&MovableMan::GetMOsInBox, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("GetMOsInBox", (const std::vector * (MovableMan::*)(const Box &box, int ignoreTeam) const)&MovableMan::GetMOsInBox, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("GetMOsInBox", (const std::vector * (MovableMan::*)(const Box &box, int ignoreTeam, bool getsHitByMOsOnly) const)&MovableMan::GetMOsInBox, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("GetMOsInRadius", (const std::vector * (MovableMan::*)(const Vector ¢re, float radius) const)&MovableMan::GetMOsInRadius, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("GetMOsInRadius", (const std::vector * (MovableMan::*)(const Vector ¢re, float radius, int ignoreTeam) const)&MovableMan::GetMOsInRadius, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("GetMOsInRadius", (const std::vector * (MovableMan::*)(const Vector ¢re, float radius, int ignoreTeam, bool getsHitByMOsOnly) const)&MovableMan::GetMOsInRadius, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - - .def("SendGlobalMessage", &LuaAdaptersMovableMan::SendGlobalMessage1) - .def("SendGlobalMessage", &LuaAdaptersMovableMan::SendGlobalMessage2) - .def("AddMO", &LuaAdaptersMovableMan::AddMO, luabind::adopt(_2)) - .def("AddActor", &LuaAdaptersMovableMan::AddActor, luabind::adopt(_2)) - .def("AddItem", &LuaAdaptersMovableMan::AddItem, luabind::adopt(_2)) - .def("AddParticle", &LuaAdaptersMovableMan::AddParticle, luabind::adopt(_2)); + .property("MaxDroppedItems", &MovableMan::GetMaxDroppedItems, &MovableMan::SetMaxDroppedItems) + + .def_readwrite("Actors", &MovableMan::m_Actors, luabind::return_stl_iterator) + .def_readwrite("Items", &MovableMan::m_Items, luabind::return_stl_iterator) + .def_readwrite("Particles", &MovableMan::m_Particles, luabind::return_stl_iterator) + .def_readwrite("AddedActors", &MovableMan::m_AddedActors, luabind::return_stl_iterator) + .def_readwrite("AddedItems", &MovableMan::m_AddedItems, luabind::return_stl_iterator) + .def_readwrite("AddedParticles", &MovableMan::m_AddedParticles, luabind::return_stl_iterator) + .def_readwrite("AlarmEvents", &MovableMan::m_AlarmEvents, luabind::return_stl_iterator) + .def_readwrite("AddedAlarmEvents", &MovableMan::m_AddedAlarmEvents, luabind::return_stl_iterator) + + .def("GetMOFromID", &MovableMan::GetMOFromID) + .def("FindObjectByUniqueID", &MovableMan::FindObjectByUniqueID) + .def("GetMOIDCount", &MovableMan::GetMOIDCount) + .def("GetTeamMOIDCount", &MovableMan::GetTeamMOIDCount) + .def("PurgeAllMOs", &MovableMan::PurgeAllMOs) + .def("GetNextActorInGroup", &MovableMan::GetNextActorInGroup) + .def("GetPrevActorInGroup", &MovableMan::GetPrevActorInGroup) + .def("GetNextTeamActor", &MovableMan::GetNextTeamActor) + .def("GetPrevTeamActor", &MovableMan::GetPrevTeamActor) + .def("GetClosestTeamActor", (Actor * (MovableMan::*)(int team, int player, const Vector& scenePoint, int maxRadius, Vector& getDistance, const Actor* excludeThis)) & MovableMan::GetClosestTeamActor) + .def("GetClosestTeamActor", (Actor * (MovableMan::*)(int team, int player, const Vector& scenePoint, int maxRadius, Vector& getDistance, bool onlyPlayerControllableActors, const Actor* excludeThis)) & MovableMan::GetClosestTeamActor) + .def("GetClosestEnemyActor", &MovableMan::GetClosestEnemyActor) + .def("GetFirstTeamActor", &MovableMan::GetFirstTeamActor) + .def("GetClosestActor", &MovableMan::GetClosestActor) + .def("GetClosestBrainActor", &MovableMan::GetClosestBrainActor) + .def("GetFirstBrainActor", &MovableMan::GetFirstBrainActor) + .def("GetClosestOtherBrainActor", &MovableMan::GetClosestOtherBrainActor) + .def("GetFirstOtherBrainActor", &MovableMan::GetFirstOtherBrainActor) + .def("GetUnassignedBrain", &MovableMan::GetUnassignedBrain) + .def("GetParticleCount", &MovableMan::GetParticleCount) + .def("GetSplashRatio", &MovableMan::GetSplashRatio) + .def("SortTeamRoster", &MovableMan::SortTeamRoster) + .def("ChangeActorTeam", &MovableMan::ChangeActorTeam) + .def("RemoveActor", &MovableMan::RemoveActor, luabind::adopt(luabind::return_value)) + .def("RemoveItem", &MovableMan::RemoveItem, luabind::adopt(luabind::return_value)) + .def("RemoveParticle", &MovableMan::RemoveParticle, luabind::adopt(luabind::return_value)) + .def("ValidMO", &MovableMan::ValidMO) + .def("IsActor", &MovableMan::IsActor) + .def("IsDevice", &MovableMan::IsDevice) + .def("IsParticle", &MovableMan::IsParticle) + .def("IsOfActor", &MovableMan::IsOfActor) + .def("GetRootMOID", &MovableMan::GetRootMOID) + .def("RemoveMO", &MovableMan::RemoveMO) + .def("KillAllTeamActors", &MovableMan::KillAllTeamActors) + .def("KillAllEnemyActors", &MovableMan::KillAllEnemyActors) + .def("OpenAllDoors", &MovableMan::OpenAllDoors) + .def("IsParticleSettlingEnabled", &MovableMan::IsParticleSettlingEnabled) + .def("EnableParticleSettling", &MovableMan::EnableParticleSettling) + .def("IsMOSubtractionEnabled", &MovableMan::IsMOSubtractionEnabled) + .def("GetMOsInBox", (const std::vector* (MovableMan::*)(const Box& box) const) & MovableMan::GetMOsInBox, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("GetMOsInBox", (const std::vector* (MovableMan::*)(const Box& box, int ignoreTeam) const) & MovableMan::GetMOsInBox, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("GetMOsInBox", (const std::vector* (MovableMan::*)(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const) & MovableMan::GetMOsInBox, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("GetMOsInRadius", (const std::vector* (MovableMan::*)(const Vector& centre, float radius) const) & MovableMan::GetMOsInRadius, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("GetMOsInRadius", (const std::vector* (MovableMan::*)(const Vector& centre, float radius, int ignoreTeam) const) & MovableMan::GetMOsInRadius, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("GetMOsInRadius", (const std::vector* (MovableMan::*)(const Vector& centre, float radius, int ignoreTeam, bool getsHitByMOsOnly) const) & MovableMan::GetMOsInRadius, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + + .def("SendGlobalMessage", &LuaAdaptersMovableMan::SendGlobalMessage1) + .def("SendGlobalMessage", &LuaAdaptersMovableMan::SendGlobalMessage2) + .def("AddMO", &LuaAdaptersMovableMan::AddMO, luabind::adopt(_2)) + .def("AddActor", &LuaAdaptersMovableMan::AddActor, luabind::adopt(_2)) + .def("AddItem", &LuaAdaptersMovableMan::AddItem, luabind::adopt(_2)) + .def("AddParticle", &LuaAdaptersMovableMan::AddParticle, luabind::adopt(_2)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, PerformanceMan) { return luabind::class_("PerformanceManager") - .property("ShowPerformanceStats", &PerformanceMan::IsShowingPerformanceStats, &PerformanceMan::ShowPerformanceStats); + .property("ShowPerformanceStats", &PerformanceMan::IsShowingPerformanceStats, &PerformanceMan::ShowPerformanceStats); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, PostProcessMan) { return luabind::class_("PostProcessManager") - .def("RegisterPostEffect", &PostProcessMan::RegisterPostEffect); + .def("RegisterPostEffect", &PostProcessMan::RegisterPostEffect); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, PresetMan) { return luabind::class_("PresetManager") - .def_readwrite("Modules", &PresetMan::m_pDataModules, luabind::return_stl_iterator) - - .def("LoadDataModule", (bool (PresetMan::*)(const std::string &))&PresetMan::LoadDataModule) - .def("GetDataModule", &PresetMan::GetDataModule) - .def("GetModuleID", &PresetMan::GetModuleID) - .def("GetModuleIDFromPath", &PresetMan::GetModuleIDFromPath) - .def("GetTotalModuleCount", &PresetMan::GetTotalModuleCount) - .def("GetOfficialModuleCount", &PresetMan::GetOfficialModuleCount) - .def("AddPreset", &PresetMan::AddEntityPreset) - .def("GetPreset", (const Entity *(PresetMan::*)(std::string, std::string, int))&PresetMan::GetEntityPreset) - .def("GetPreset", (const Entity *(PresetMan::*)(std::string, std::string, std::string))&PresetMan::GetEntityPreset) - .def("GetLoadout", (Actor * (PresetMan::*)(std::string, std::string, bool))&PresetMan::GetLoadout, luabind::adopt(luabind::result)) - .def("GetLoadout", (Actor * (PresetMan::*)(std::string, int, bool))&PresetMan::GetLoadout, luabind::adopt(luabind::result)) - .def("GetRandomOfGroup", &PresetMan::GetRandomOfGroup) - .def("GetRandomOfGroupInModuleSpace", &PresetMan::GetRandomOfGroupInModuleSpace) - .def("GetEntityDataLocation", &PresetMan::GetEntityDataLocation) - .def("ReadReflectedPreset", &PresetMan::ReadReflectedPreset) - .def("ReloadEntityPreset", &LuaAdaptersPresetMan::ReloadEntityPreset1) - .def("ReloadEntityPreset", &LuaAdaptersPresetMan::ReloadEntityPreset2) - .def("GetAllEntities", &LuaAdaptersPresetMan::GetAllEntities, luabind::adopt(luabind::result) + luabind::return_stl_iterator) - .def("GetAllEntitiesOfGroup", &LuaAdaptersPresetMan::GetAllEntitiesOfGroup, luabind::adopt(luabind::result) + luabind::return_stl_iterator) - .def("GetAllEntitiesOfGroup", &LuaAdaptersPresetMan::GetAllEntitiesOfGroup2, luabind::adopt(luabind::result) + luabind::return_stl_iterator) - .def("GetAllEntitiesOfGroup", &LuaAdaptersPresetMan::GetAllEntitiesOfGroup3, luabind::adopt(luabind::result) + luabind::return_stl_iterator) - .def("ReloadAllScripts", &PresetMan::ReloadAllScripts) - .def("IsModuleOfficial", &PresetMan::IsModuleOfficial) - .def("IsModuleUserdata", &PresetMan::IsModuleUserdata) - .def("GetFullModulePath", &PresetMan::GetFullModulePath); + .def_readwrite("Modules", &PresetMan::m_pDataModules, luabind::return_stl_iterator) + + .def("LoadDataModule", (bool(PresetMan::*)(const std::string&)) & PresetMan::LoadDataModule) + .def("GetDataModule", &PresetMan::GetDataModule) + .def("GetModuleID", &PresetMan::GetModuleID) + .def("GetModuleIDFromPath", &PresetMan::GetModuleIDFromPath) + .def("GetTotalModuleCount", &PresetMan::GetTotalModuleCount) + .def("GetOfficialModuleCount", &PresetMan::GetOfficialModuleCount) + .def("AddPreset", &PresetMan::AddEntityPreset) + .def("GetPreset", (const Entity* (PresetMan::*)(std::string, std::string, int)) & PresetMan::GetEntityPreset) + .def("GetPreset", (const Entity* (PresetMan::*)(std::string, std::string, std::string)) & PresetMan::GetEntityPreset) + .def("GetLoadout", (Actor * (PresetMan::*)(std::string, std::string, bool)) & PresetMan::GetLoadout, luabind::adopt(luabind::result)) + .def("GetLoadout", (Actor * (PresetMan::*)(std::string, int, bool)) & PresetMan::GetLoadout, luabind::adopt(luabind::result)) + .def("GetRandomOfGroup", &PresetMan::GetRandomOfGroup) + .def("GetRandomOfGroupInModuleSpace", &PresetMan::GetRandomOfGroupInModuleSpace) + .def("GetEntityDataLocation", &PresetMan::GetEntityDataLocation) + .def("ReadReflectedPreset", &PresetMan::ReadReflectedPreset) + .def("ReloadEntityPreset", &LuaAdaptersPresetMan::ReloadEntityPreset1) + .def("ReloadEntityPreset", &LuaAdaptersPresetMan::ReloadEntityPreset2) + .def("GetAllEntities", &LuaAdaptersPresetMan::GetAllEntities, luabind::adopt(luabind::result) + luabind::return_stl_iterator) + .def("GetAllEntitiesOfGroup", &LuaAdaptersPresetMan::GetAllEntitiesOfGroup, luabind::adopt(luabind::result) + luabind::return_stl_iterator) + .def("GetAllEntitiesOfGroup", &LuaAdaptersPresetMan::GetAllEntitiesOfGroup2, luabind::adopt(luabind::result) + luabind::return_stl_iterator) + .def("GetAllEntitiesOfGroup", &LuaAdaptersPresetMan::GetAllEntitiesOfGroup3, luabind::adopt(luabind::result) + luabind::return_stl_iterator) + .def("ReloadAllScripts", &PresetMan::ReloadAllScripts) + .def("IsModuleOfficial", &PresetMan::IsModuleOfficial) + .def("IsModuleUserdata", &PresetMan::IsModuleUserdata) + .def("GetFullModulePath", &PresetMan::GetFullModulePath); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, PrimitiveMan) { return luabind::class_("PrimitiveManager") - .def("DrawLinePrimitive", (void (PrimitiveMan::*)(const Vector &start, const Vector &end, unsigned char color))&PrimitiveMan::DrawLinePrimitive) - .def("DrawLinePrimitive", (void (PrimitiveMan::*)(const Vector &start, const Vector &end, unsigned char color, int thickness))&PrimitiveMan::DrawLinePrimitive) - .def("DrawLinePrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const Vector &end, unsigned char color))&PrimitiveMan::DrawLinePrimitive) - .def("DrawLinePrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const Vector &end, unsigned char color, int thickness))&PrimitiveMan::DrawLinePrimitive) - .def("DrawArcPrimitive", (void (PrimitiveMan::*)(const Vector &pos, float startAngle, float endAngle, int radius, unsigned char color))&PrimitiveMan::DrawArcPrimitive) - .def("DrawArcPrimitive", (void (PrimitiveMan::*)(const Vector &pos, float startAngle, float endAngle, int radius, unsigned char color, int thickness))&PrimitiveMan::DrawArcPrimitive) - .def("DrawArcPrimitive", (void (PrimitiveMan::*)(int player, const Vector &pos, float startAngle, float endAngle, int radius, unsigned char color))&PrimitiveMan::DrawArcPrimitive) - .def("DrawArcPrimitive", (void (PrimitiveMan::*)(int player, const Vector &pos, float startAngle, float endAngle, int radius, unsigned char color, int thickness))&PrimitiveMan::DrawArcPrimitive) - .def("DrawSplinePrimitive", (void (PrimitiveMan::*)(const Vector &start, const Vector &guideA, const Vector &guideB, const Vector &end, unsigned char color))&PrimitiveMan::DrawSplinePrimitive) - .def("DrawSplinePrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const Vector &guideA, const Vector &guideB, const Vector &end, unsigned char color))&PrimitiveMan::DrawSplinePrimitive) - .def("DrawBoxPrimitive", (void (PrimitiveMan::*)(const Vector &start, const Vector &end, unsigned char color))&PrimitiveMan::DrawBoxPrimitive) - .def("DrawBoxPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const Vector &end, unsigned char color))&PrimitiveMan::DrawBoxPrimitive) - .def("DrawBoxFillPrimitive", (void (PrimitiveMan::*)(const Vector &start, const Vector &end, unsigned char color))&PrimitiveMan::DrawBoxFillPrimitive) - .def("DrawBoxFillPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const Vector &end, unsigned char color))&PrimitiveMan::DrawBoxFillPrimitive) - .def("DrawRoundedBoxPrimitive", (void (PrimitiveMan::*)(const Vector &start, const Vector &end, int cornerRadius, unsigned char color))&PrimitiveMan::DrawRoundedBoxPrimitive) - .def("DrawRoundedBoxPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const Vector &end, int cornerRadius, unsigned char color))&PrimitiveMan::DrawRoundedBoxPrimitive) - .def("DrawRoundedBoxFillPrimitive", (void (PrimitiveMan::*)(const Vector &start, const Vector &end, int cornerRadius, unsigned char color))&PrimitiveMan::DrawRoundedBoxFillPrimitive) - .def("DrawRoundedBoxFillPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const Vector &end, int cornerRadius, unsigned char color))&PrimitiveMan::DrawRoundedBoxFillPrimitive) - .def("DrawCirclePrimitive", (void (PrimitiveMan::*)(const Vector & pos, int radius, unsigned char color))&PrimitiveMan::DrawCirclePrimitive) - .def("DrawCirclePrimitive", (void (PrimitiveMan::*)(int player, const Vector &pos, int radius, unsigned char color))&PrimitiveMan::DrawCirclePrimitive) - .def("DrawCircleFillPrimitive", (void (PrimitiveMan::*)(const Vector &pos, int radius, unsigned char color))&PrimitiveMan::DrawCircleFillPrimitive) - .def("DrawCircleFillPrimitive", (void (PrimitiveMan::*)(int player, const Vector &pos, int radius, unsigned char color))&PrimitiveMan::DrawCircleFillPrimitive) - .def("DrawEllipsePrimitive", (void (PrimitiveMan::*)(const Vector &pos, int horizRadius, int vertRadius, unsigned char color))&PrimitiveMan::DrawEllipsePrimitive) - .def("DrawEllipsePrimitive", (void (PrimitiveMan::*)(int player, const Vector &pos, int horizRadius, int vertRadius, unsigned char color))&PrimitiveMan::DrawEllipsePrimitive) - .def("DrawEllipseFillPrimitive", (void (PrimitiveMan::*)(const Vector &pos, int horizRadius, int vertRadius, unsigned char color))&PrimitiveMan::DrawEllipseFillPrimitive) - .def("DrawEllipseFillPrimitive", (void (PrimitiveMan::*)(int player, const Vector &pos, int horizRadius, int vertRadius, unsigned char color))&PrimitiveMan::DrawEllipseFillPrimitive) - .def("DrawTrianglePrimitive", (void (PrimitiveMan::*)(const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color))&PrimitiveMan::DrawTrianglePrimitive) - .def("DrawTrianglePrimitive", (void (PrimitiveMan::*)(int player, const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color))&PrimitiveMan::DrawTrianglePrimitive) - .def("DrawTriangleFillPrimitive", (void (PrimitiveMan::*)(const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color))&PrimitiveMan::DrawTriangleFillPrimitive) - .def("DrawTriangleFillPrimitive", (void (PrimitiveMan::*)(int player, const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color))&PrimitiveMan::DrawTriangleFillPrimitive) - .def("DrawTextPrimitive", (void (PrimitiveMan::*)(const Vector &start, const std::string &text, bool isSmall, int alignment))&PrimitiveMan::DrawTextPrimitive) - .def("DrawTextPrimitive", (void (PrimitiveMan::*)(const Vector &start, const std::string &text, bool isSmall, int alignment, float rotAngle))&PrimitiveMan::DrawTextPrimitive) - .def("DrawTextPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const std::string &text, bool isSmall, int alignment))&PrimitiveMan::DrawTextPrimitive) - .def("DrawTextPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const std::string &text, bool isSmall, int alignment, float rotAngle))&PrimitiveMan::DrawTextPrimitive) - .def("DrawBitmapPrimitive", (void (PrimitiveMan::*)(const Vector &start, const MOSprite *moSprite, float rotAngle, int frame))&PrimitiveMan::DrawBitmapPrimitive) - .def("DrawBitmapPrimitive", (void (PrimitiveMan::*)(const Vector &start, const MOSprite *moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped))&PrimitiveMan::DrawBitmapPrimitive) - .def("DrawBitmapPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const MOSprite *moSprite, float rotAngle, int frame))&PrimitiveMan::DrawBitmapPrimitive) - .def("DrawBitmapPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const MOSprite *moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped))&PrimitiveMan::DrawBitmapPrimitive) - .def("DrawBitmapPrimitive", (void (PrimitiveMan::*)(const Vector &start, const std::string &filePath, float rotAngle))&PrimitiveMan::DrawBitmapPrimitive) - .def("DrawBitmapPrimitive", (void (PrimitiveMan::*)(const Vector &start, const std::string &filePath, float rotAngle, bool hFlipped, bool vFlipped))&PrimitiveMan::DrawBitmapPrimitive) - .def("DrawBitmapPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const std::string &filePath, float rotAngle))&PrimitiveMan::DrawBitmapPrimitive) - .def("DrawBitmapPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, const std::string &filePath, float rotAngle, bool hFlipped, bool vFlipped))&PrimitiveMan::DrawBitmapPrimitive) - .def("DrawIconPrimitive", (void (PrimitiveMan::*)(const Vector &start, Entity *entity))&PrimitiveMan::DrawIconPrimitive) - .def("DrawIconPrimitive", (void (PrimitiveMan::*)(int player, const Vector &start, Entity *entity))&PrimitiveMan::DrawIconPrimitive) - - .def("DrawPolygonPrimitive", &LuaAdaptersPrimitiveMan::DrawPolygonPrimitive) - .def("DrawPolygonPrimitive", &LuaAdaptersPrimitiveMan::DrawPolygonPrimitiveForPlayer) - .def("DrawPolygonFillPrimitive", &LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitive) - .def("DrawPolygonFillPrimitive", &LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitiveForPlayer) - .def("DrawPrimitives", &LuaAdaptersPrimitiveMan::DrawPrimitivesWithTransparency) - .def("DrawPrimitives", &LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlending) - .def("DrawPrimitives", &LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlendingPerChannel); + .def("DrawLinePrimitive", (void(PrimitiveMan::*)(const Vector& start, const Vector& end, unsigned char color)) & PrimitiveMan::DrawLinePrimitive) + .def("DrawLinePrimitive", (void(PrimitiveMan::*)(const Vector& start, const Vector& end, unsigned char color, int thickness)) & PrimitiveMan::DrawLinePrimitive) + .def("DrawLinePrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const Vector& end, unsigned char color)) & PrimitiveMan::DrawLinePrimitive) + .def("DrawLinePrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const Vector& end, unsigned char color, int thickness)) & PrimitiveMan::DrawLinePrimitive) + .def("DrawArcPrimitive", (void(PrimitiveMan::*)(const Vector& pos, float startAngle, float endAngle, int radius, unsigned char color)) & PrimitiveMan::DrawArcPrimitive) + .def("DrawArcPrimitive", (void(PrimitiveMan::*)(const Vector& pos, float startAngle, float endAngle, int radius, unsigned char color, int thickness)) & PrimitiveMan::DrawArcPrimitive) + .def("DrawArcPrimitive", (void(PrimitiveMan::*)(int player, const Vector& pos, float startAngle, float endAngle, int radius, unsigned char color)) & PrimitiveMan::DrawArcPrimitive) + .def("DrawArcPrimitive", (void(PrimitiveMan::*)(int player, const Vector& pos, float startAngle, float endAngle, int radius, unsigned char color, int thickness)) & PrimitiveMan::DrawArcPrimitive) + .def("DrawSplinePrimitive", (void(PrimitiveMan::*)(const Vector& start, const Vector& guideA, const Vector& guideB, const Vector& end, unsigned char color)) & PrimitiveMan::DrawSplinePrimitive) + .def("DrawSplinePrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const Vector& guideA, const Vector& guideB, const Vector& end, unsigned char color)) & PrimitiveMan::DrawSplinePrimitive) + .def("DrawBoxPrimitive", (void(PrimitiveMan::*)(const Vector& start, const Vector& end, unsigned char color)) & PrimitiveMan::DrawBoxPrimitive) + .def("DrawBoxPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const Vector& end, unsigned char color)) & PrimitiveMan::DrawBoxPrimitive) + .def("DrawBoxFillPrimitive", (void(PrimitiveMan::*)(const Vector& start, const Vector& end, unsigned char color)) & PrimitiveMan::DrawBoxFillPrimitive) + .def("DrawBoxFillPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const Vector& end, unsigned char color)) & PrimitiveMan::DrawBoxFillPrimitive) + .def("DrawRoundedBoxPrimitive", (void(PrimitiveMan::*)(const Vector& start, const Vector& end, int cornerRadius, unsigned char color)) & PrimitiveMan::DrawRoundedBoxPrimitive) + .def("DrawRoundedBoxPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const Vector& end, int cornerRadius, unsigned char color)) & PrimitiveMan::DrawRoundedBoxPrimitive) + .def("DrawRoundedBoxFillPrimitive", (void(PrimitiveMan::*)(const Vector& start, const Vector& end, int cornerRadius, unsigned char color)) & PrimitiveMan::DrawRoundedBoxFillPrimitive) + .def("DrawRoundedBoxFillPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const Vector& end, int cornerRadius, unsigned char color)) & PrimitiveMan::DrawRoundedBoxFillPrimitive) + .def("DrawCirclePrimitive", (void(PrimitiveMan::*)(const Vector& pos, int radius, unsigned char color)) & PrimitiveMan::DrawCirclePrimitive) + .def("DrawCirclePrimitive", (void(PrimitiveMan::*)(int player, const Vector& pos, int radius, unsigned char color)) & PrimitiveMan::DrawCirclePrimitive) + .def("DrawCircleFillPrimitive", (void(PrimitiveMan::*)(const Vector& pos, int radius, unsigned char color)) & PrimitiveMan::DrawCircleFillPrimitive) + .def("DrawCircleFillPrimitive", (void(PrimitiveMan::*)(int player, const Vector& pos, int radius, unsigned char color)) & PrimitiveMan::DrawCircleFillPrimitive) + .def("DrawEllipsePrimitive", (void(PrimitiveMan::*)(const Vector& pos, int horizRadius, int vertRadius, unsigned char color)) & PrimitiveMan::DrawEllipsePrimitive) + .def("DrawEllipsePrimitive", (void(PrimitiveMan::*)(int player, const Vector& pos, int horizRadius, int vertRadius, unsigned char color)) & PrimitiveMan::DrawEllipsePrimitive) + .def("DrawEllipseFillPrimitive", (void(PrimitiveMan::*)(const Vector& pos, int horizRadius, int vertRadius, unsigned char color)) & PrimitiveMan::DrawEllipseFillPrimitive) + .def("DrawEllipseFillPrimitive", (void(PrimitiveMan::*)(int player, const Vector& pos, int horizRadius, int vertRadius, unsigned char color)) & PrimitiveMan::DrawEllipseFillPrimitive) + .def("DrawTrianglePrimitive", (void(PrimitiveMan::*)(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color)) & PrimitiveMan::DrawTrianglePrimitive) + .def("DrawTrianglePrimitive", (void(PrimitiveMan::*)(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color)) & PrimitiveMan::DrawTrianglePrimitive) + .def("DrawTriangleFillPrimitive", (void(PrimitiveMan::*)(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color)) & PrimitiveMan::DrawTriangleFillPrimitive) + .def("DrawTriangleFillPrimitive", (void(PrimitiveMan::*)(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color)) & PrimitiveMan::DrawTriangleFillPrimitive) + .def("DrawTextPrimitive", (void(PrimitiveMan::*)(const Vector& start, const std::string& text, bool isSmall, int alignment)) & PrimitiveMan::DrawTextPrimitive) + .def("DrawTextPrimitive", (void(PrimitiveMan::*)(const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle)) & PrimitiveMan::DrawTextPrimitive) + .def("DrawTextPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const std::string& text, bool isSmall, int alignment)) & PrimitiveMan::DrawTextPrimitive) + .def("DrawTextPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle)) & PrimitiveMan::DrawTextPrimitive) + .def("DrawBitmapPrimitive", (void(PrimitiveMan::*)(const Vector& start, const MOSprite* moSprite, float rotAngle, int frame)) & PrimitiveMan::DrawBitmapPrimitive) + .def("DrawBitmapPrimitive", (void(PrimitiveMan::*)(const Vector& start, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped)) & PrimitiveMan::DrawBitmapPrimitive) + .def("DrawBitmapPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const MOSprite* moSprite, float rotAngle, int frame)) & PrimitiveMan::DrawBitmapPrimitive) + .def("DrawBitmapPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped)) & PrimitiveMan::DrawBitmapPrimitive) + .def("DrawBitmapPrimitive", (void(PrimitiveMan::*)(const Vector& start, const std::string& filePath, float rotAngle)) & PrimitiveMan::DrawBitmapPrimitive) + .def("DrawBitmapPrimitive", (void(PrimitiveMan::*)(const Vector& start, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped)) & PrimitiveMan::DrawBitmapPrimitive) + .def("DrawBitmapPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const std::string& filePath, float rotAngle)) & PrimitiveMan::DrawBitmapPrimitive) + .def("DrawBitmapPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped)) & PrimitiveMan::DrawBitmapPrimitive) + .def("DrawIconPrimitive", (void(PrimitiveMan::*)(const Vector& start, Entity* entity)) & PrimitiveMan::DrawIconPrimitive) + .def("DrawIconPrimitive", (void(PrimitiveMan::*)(int player, const Vector& start, Entity* entity)) & PrimitiveMan::DrawIconPrimitive) + + .def("DrawPolygonPrimitive", &LuaAdaptersPrimitiveMan::DrawPolygonPrimitive) + .def("DrawPolygonPrimitive", &LuaAdaptersPrimitiveMan::DrawPolygonPrimitiveForPlayer) + .def("DrawPolygonFillPrimitive", &LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitive) + .def("DrawPolygonFillPrimitive", &LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitiveForPlayer) + .def("DrawPrimitives", &LuaAdaptersPrimitiveMan::DrawPrimitivesWithTransparency) + .def("DrawPrimitives", &LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlending) + .def("DrawPrimitives", &LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlendingPerChannel); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, SceneMan) { return luabind::class_("SceneManager") - .property("Scene", &SceneMan::GetScene) - .property("SceneDim", &SceneMan::GetSceneDim) - .property("SceneWidth", &SceneMan::GetSceneWidth) - .property("SceneHeight", &SceneMan::GetSceneHeight) - .property("SceneWrapsX", &SceneMan::SceneWrapsX) - .property("SceneWrapsY", &SceneMan::SceneWrapsY) - .property("SceneOrbitDirection", &SceneMan::GetSceneOrbitDirection) - .property("LayerDrawMode", &SceneMan::GetLayerDrawMode, &SceneMan::SetLayerDrawMode) - .property("GlobalAcc", &SceneMan::GetGlobalAcc) - .property("OzPerKg", &SceneMan::GetOzPerKg) - .property("KgPerOz", &SceneMan::GetKgPerOz) - .property("ScrapCompactingHeight", &SceneMan::GetScrapCompactingHeight, &SceneMan::SetScrapCompactingHeight) - - .def("LoadScene", (int (SceneMan::*)(std::string, bool, bool))&SceneMan::LoadScene) - .def("LoadScene", (int (SceneMan::*)(std::string, bool))&SceneMan::LoadScene) - - .def("GetTerrain", &SceneMan::GetTerrain) - .def("GetMaterial", &SceneMan::GetMaterial) - .def("GetMaterialFromID", &SceneMan::GetMaterialFromID) - .def("GetTerrMatter", &SceneMan::GetTerrMatter) - .def("GetMOIDPixel", (MOID (SceneMan::*)(int, int))&SceneMan::GetMOIDPixel) - .def("GetMOIDPixel", (MOID (SceneMan::*)(int, int, int))&SceneMan::GetMOIDPixel) - .def("SetLayerDrawMode", &SceneMan::SetLayerDrawMode) - .def("LoadUnseenLayer", &SceneMan::LoadUnseenLayer) - .def("MakeAllUnseen", &SceneMan::MakeAllUnseen) - .def("AnythingUnseen", &SceneMan::AnythingUnseen) - .def("GetUnseenResolution", &SceneMan::GetUnseenResolution) - .def("IsUnseen", &SceneMan::IsUnseen) - .def("RevealUnseen", &SceneMan::RevealUnseen) - .def("RevealUnseenBox", &SceneMan::RevealUnseenBox) - .def("RestoreUnseen", &SceneMan::RestoreUnseen) - .def("RestoreUnseenBox", &SceneMan::RestoreUnseenBox) - .def("CastSeeRay", &SceneMan::CastSeeRay) - .def("CastUnseeRay", &SceneMan::CastUnseeRay) - .def("CastUnseenRay", &SceneMan::CastUnseenRay) - .def("CastMaterialRay", (bool (SceneMan::*)(const Vector &, const Vector &, unsigned char, Vector &, int, bool))&SceneMan::CastMaterialRay) - .def("CastMaterialRay", (float (SceneMan::*)(const Vector &, const Vector &, unsigned char, int))&SceneMan::CastMaterialRay) - .def("CastNotMaterialRay", (bool (SceneMan::*)(const Vector &, const Vector &, unsigned char, Vector &, int, bool))&SceneMan::CastNotMaterialRay) - .def("CastNotMaterialRay", (float (SceneMan::*)(const Vector &, const Vector &, unsigned char, int, bool))&SceneMan::CastNotMaterialRay) - .def("CastStrengthSumRay", &SceneMan::CastStrengthSumRay) - .def("CastMaxStrengthRay", (float (SceneMan::*) (const Vector &, const Vector &, int, unsigned char))&SceneMan::CastMaxStrengthRay) - .def("CastMaxStrengthRay", (float (SceneMan::*) (const Vector &, const Vector &, int))&SceneMan::CastMaxStrengthRay) - .def("CastStrengthRay", &SceneMan::CastStrengthRay) - .def("CastWeaknessRay", &SceneMan::CastWeaknessRay) - .def("CastMORay", &SceneMan::CastMORay) - .def("CastFindMORay", &SceneMan::CastFindMORay) - .def("CastObstacleRay", &SceneMan::CastObstacleRay) - .def("GetLastRayHitPos", &SceneMan::GetLastRayHitPos) - .def("FindAltitude", (float (SceneMan::*) (const Vector&, int, int)) &SceneMan::FindAltitude) - .def("FindAltitude", (float (SceneMan::*) (const Vector&, int, int, bool)) &SceneMan::FindAltitude) - .def("MovePointToGround", &SceneMan::MovePointToGround) - .def("IsWithinBounds", &SceneMan::IsWithinBounds) - .def("ForceBounds", (bool (SceneMan::*)(int &, int &))&SceneMan::ForceBounds) - .def("ForceBounds", (bool (SceneMan::*)(Vector &))&SceneMan::ForceBounds)//, out_value(_2)) - .def("WrapPosition", (bool (SceneMan::*)(int &, int &))&SceneMan::WrapPosition) - .def("WrapPosition", (bool (SceneMan::*)(Vector &))&SceneMan::WrapPosition)//, out_value(_2)) - .def("SnapPosition", &SceneMan::SnapPosition) - .def("ShortestDistance", &SceneMan::ShortestDistance) - .def("WrapBox", &LuaAdaptersSceneMan::WrapBoxes, luabind::return_stl_iterator) - .def("ObscuredPoint", (bool (SceneMan::*)(Vector &, int))&SceneMan::ObscuredPoint)//, out_value(_2)) - .def("ObscuredPoint", (bool (SceneMan::*)(int, int, int))&SceneMan::ObscuredPoint) - .def("AddSceneObject", &SceneMan::AddSceneObject, luabind::adopt(_2)) - .def("CheckAndRemoveOrphans", (int (SceneMan::*)(int, int, int, int, bool))&SceneMan::RemoveOrphans) - .def("DislodgePixel", &SceneMan::DislodgePixel); + .property("Scene", &SceneMan::GetScene) + .property("SceneDim", &SceneMan::GetSceneDim) + .property("SceneWidth", &SceneMan::GetSceneWidth) + .property("SceneHeight", &SceneMan::GetSceneHeight) + .property("SceneWrapsX", &SceneMan::SceneWrapsX) + .property("SceneWrapsY", &SceneMan::SceneWrapsY) + .property("SceneOrbitDirection", &SceneMan::GetSceneOrbitDirection) + .property("LayerDrawMode", &SceneMan::GetLayerDrawMode, &SceneMan::SetLayerDrawMode) + .property("GlobalAcc", &SceneMan::GetGlobalAcc) + .property("OzPerKg", &SceneMan::GetOzPerKg) + .property("KgPerOz", &SceneMan::GetKgPerOz) + .property("ScrapCompactingHeight", &SceneMan::GetScrapCompactingHeight, &SceneMan::SetScrapCompactingHeight) + + .def("LoadScene", (int(SceneMan::*)(std::string, bool, bool)) & SceneMan::LoadScene) + .def("LoadScene", (int(SceneMan::*)(std::string, bool)) & SceneMan::LoadScene) + + .def("GetTerrain", &SceneMan::GetTerrain) + .def("GetMaterial", &SceneMan::GetMaterial) + .def("GetMaterialFromID", &SceneMan::GetMaterialFromID) + .def("GetTerrMatter", &SceneMan::GetTerrMatter) + .def("GetMOIDPixel", (MOID(SceneMan::*)(int, int)) & SceneMan::GetMOIDPixel) + .def("GetMOIDPixel", (MOID(SceneMan::*)(int, int, int)) & SceneMan::GetMOIDPixel) + .def("SetLayerDrawMode", &SceneMan::SetLayerDrawMode) + .def("LoadUnseenLayer", &SceneMan::LoadUnseenLayer) + .def("MakeAllUnseen", &SceneMan::MakeAllUnseen) + .def("AnythingUnseen", &SceneMan::AnythingUnseen) + .def("GetUnseenResolution", &SceneMan::GetUnseenResolution) + .def("IsUnseen", &SceneMan::IsUnseen) + .def("RevealUnseen", &SceneMan::RevealUnseen) + .def("RevealUnseenBox", &SceneMan::RevealUnseenBox) + .def("RestoreUnseen", &SceneMan::RestoreUnseen) + .def("RestoreUnseenBox", &SceneMan::RestoreUnseenBox) + .def("CastSeeRay", &SceneMan::CastSeeRay) + .def("CastUnseeRay", &SceneMan::CastUnseeRay) + .def("CastUnseenRay", &SceneMan::CastUnseenRay) + .def("CastMaterialRay", (bool(SceneMan::*)(const Vector&, const Vector&, unsigned char, Vector&, int, bool)) & SceneMan::CastMaterialRay) + .def("CastMaterialRay", (float(SceneMan::*)(const Vector&, const Vector&, unsigned char, int)) & SceneMan::CastMaterialRay) + .def("CastNotMaterialRay", (bool(SceneMan::*)(const Vector&, const Vector&, unsigned char, Vector&, int, bool)) & SceneMan::CastNotMaterialRay) + .def("CastNotMaterialRay", (float(SceneMan::*)(const Vector&, const Vector&, unsigned char, int, bool)) & SceneMan::CastNotMaterialRay) + .def("CastStrengthSumRay", &SceneMan::CastStrengthSumRay) + .def("CastMaxStrengthRay", (float(SceneMan::*)(const Vector&, const Vector&, int, unsigned char)) & SceneMan::CastMaxStrengthRay) + .def("CastMaxStrengthRay", (float(SceneMan::*)(const Vector&, const Vector&, int)) & SceneMan::CastMaxStrengthRay) + .def("CastStrengthRay", &SceneMan::CastStrengthRay) + .def("CastWeaknessRay", &SceneMan::CastWeaknessRay) + .def("CastMORay", &SceneMan::CastMORay) + .def("CastFindMORay", &SceneMan::CastFindMORay) + .def("CastObstacleRay", &SceneMan::CastObstacleRay) + .def("GetLastRayHitPos", &SceneMan::GetLastRayHitPos) + .def("FindAltitude", (float(SceneMan::*)(const Vector&, int, int)) & SceneMan::FindAltitude) + .def("FindAltitude", (float(SceneMan::*)(const Vector&, int, int, bool)) & SceneMan::FindAltitude) + .def("MovePointToGround", &SceneMan::MovePointToGround) + .def("IsWithinBounds", &SceneMan::IsWithinBounds) + .def("ForceBounds", (bool(SceneMan::*)(int&, int&)) & SceneMan::ForceBounds) + .def("ForceBounds", (bool(SceneMan::*)(Vector&)) & SceneMan::ForceBounds) //, out_value(_2)) + .def("WrapPosition", (bool(SceneMan::*)(int&, int&)) & SceneMan::WrapPosition) + .def("WrapPosition", (bool(SceneMan::*)(Vector&)) & SceneMan::WrapPosition) //, out_value(_2)) + .def("SnapPosition", &SceneMan::SnapPosition) + .def("ShortestDistance", &SceneMan::ShortestDistance) + .def("WrapBox", &LuaAdaptersSceneMan::WrapBoxes, luabind::return_stl_iterator) + .def("ObscuredPoint", (bool(SceneMan::*)(Vector&, int)) & SceneMan::ObscuredPoint) //, out_value(_2)) + .def("ObscuredPoint", (bool(SceneMan::*)(int, int, int)) & SceneMan::ObscuredPoint) + .def("AddSceneObject", &SceneMan::AddSceneObject, luabind::adopt(_2)) + .def("CheckAndRemoveOrphans", (int(SceneMan::*)(int, int, int, int, bool)) & SceneMan::RemoveOrphans) + .def("DislodgePixel", &SceneMan::DislodgePixel); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, CameraMan) { return luabind::class_("CameraManager") - .def("GetOffset", &CameraMan::GetOffset) - .def("SetOffset", &CameraMan::SetOffset) - .def("GetScreenOcclusion", &CameraMan::GetScreenOcclusion) - .def("SetScreenOcclusion", &CameraMan::SetScreenOcclusion) - .def("GetScrollTarget", &CameraMan::GetScrollTarget) - .def("SetScrollTarget", &CameraMan::SetScrollTarget) - .def("TargetDistanceScalar", &CameraMan::TargetDistanceScalar) - .def("CheckOffset", &CameraMan::CheckOffset) - .def("SetScroll", &CameraMan::SetScroll) - .def("AddScreenShake", (void (CameraMan::*)(float, int))&CameraMan::AddScreenShake) - .def("AddScreenShake", (void (CameraMan::*)(float, const Vector &))&CameraMan::AddScreenShake); + .def("GetOffset", &CameraMan::GetOffset) + .def("SetOffset", &CameraMan::SetOffset) + .def("GetScreenOcclusion", &CameraMan::GetScreenOcclusion) + .def("SetScreenOcclusion", &CameraMan::SetScreenOcclusion) + .def("GetScrollTarget", &CameraMan::GetScrollTarget) + .def("SetScrollTarget", &CameraMan::SetScrollTarget) + .def("TargetDistanceScalar", &CameraMan::TargetDistanceScalar) + .def("CheckOffset", &CameraMan::CheckOffset) + .def("SetScroll", &CameraMan::SetScroll) + .def("AddScreenShake", (void(CameraMan::*)(float, int)) & CameraMan::AddScreenShake) + .def("AddScreenShake", (void(CameraMan::*)(float, const Vector&)) & CameraMan::AddScreenShake); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -377,85 +377,85 @@ namespace RTE { LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, SettingsMan) { return luabind::class_("SettingsManager") - .property("PrintDebugInfo", &SettingsMan::PrintDebugInfo, &SettingsMan::SetPrintDebugInfo) - .property("RecommendedMOIDCount", &SettingsMan::RecommendedMOIDCount) - .property("AIUpdateInterval", &SettingsMan::GetAIUpdateInterval, &SettingsMan::SetAIUpdateInterval) - .property("ShowEnemyHUD", &SettingsMan::ShowEnemyHUD) - .property("AutomaticGoldDeposit", &SettingsMan::GetAutomaticGoldDeposit); + .property("PrintDebugInfo", &SettingsMan::PrintDebugInfo, &SettingsMan::SetPrintDebugInfo) + .property("RecommendedMOIDCount", &SettingsMan::RecommendedMOIDCount) + .property("AIUpdateInterval", &SettingsMan::GetAIUpdateInterval, &SettingsMan::SetAIUpdateInterval) + .property("ShowEnemyHUD", &SettingsMan::ShowEnemyHUD) + .property("AutomaticGoldDeposit", &SettingsMan::GetAutomaticGoldDeposit); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, TimerMan) { return luabind::class_("TimerManager") - .property("TimeScale", &TimerMan::GetTimeScale, &TimerMan::SetTimeScale) - .property("RealToSimCap", &TimerMan::GetRealToSimCap) - .property("DeltaTimeTicks", &LuaAdaptersTimerMan::GetDeltaTimeTicks, &TimerMan::SetDeltaTimeTicks) - .property("DeltaTimeSecs", &TimerMan::GetDeltaTimeSecs, &TimerMan::SetDeltaTimeSecs) - .property("DeltaTimeMS", &TimerMan::GetDeltaTimeMS) - .property("AIDeltaTimeSecs", &TimerMan::GetAIDeltaTimeSecs) - .property("AIDeltaTimeMS", &TimerMan::GetAIDeltaTimeMS) + .property("TimeScale", &TimerMan::GetTimeScale, &TimerMan::SetTimeScale) + .property("RealToSimCap", &TimerMan::GetRealToSimCap) + .property("DeltaTimeTicks", &LuaAdaptersTimerMan::GetDeltaTimeTicks, &TimerMan::SetDeltaTimeTicks) + .property("DeltaTimeSecs", &TimerMan::GetDeltaTimeSecs, &TimerMan::SetDeltaTimeSecs) + .property("DeltaTimeMS", &TimerMan::GetDeltaTimeMS) + .property("AIDeltaTimeSecs", &TimerMan::GetAIDeltaTimeSecs) + .property("AIDeltaTimeMS", &TimerMan::GetAIDeltaTimeMS) - .property("TicksPerSecond", &LuaAdaptersTimerMan::GetTicksPerSecond) + .property("TicksPerSecond", &LuaAdaptersTimerMan::GetTicksPerSecond) - .def("TimeForSimUpdate", &TimerMan::TimeForSimUpdate) - .def("DrawnSimUpdate", &TimerMan::DrawnSimUpdate); + .def("TimeForSimUpdate", &TimerMan::TimeForSimUpdate) + .def("DrawnSimUpdate", &TimerMan::DrawnSimUpdate); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, UInputMan) { return luabind::class_("UInputManager") - .property("FlagAltState", &UInputMan::FlagAltState) - .property("FlagCtrlState", &UInputMan::FlagCtrlState) - .property("FlagShiftState", &UInputMan::FlagShiftState) - - .def("GetInputDevice", &UInputMan::GetInputDevice) - .def("ElementPressed", &UInputMan::ElementPressed) - .def("ElementReleased", &UInputMan::ElementReleased) - .def("ElementHeld", &UInputMan::ElementHeld) - .def("KeyPressed", (bool(UInputMan::*)(SDL_Keycode) const) &UInputMan::KeyPressed) - .def("KeyReleased", (bool(UInputMan::*)(SDL_Keycode) const) &UInputMan::KeyReleased) - .def("KeyHeld", (bool(UInputMan::*)(SDL_Keycode) const) &UInputMan::KeyHeld) - .def("ScancodePressed", (bool(UInputMan::*)(SDL_Scancode) const) &UInputMan::KeyPressed) - .def("ScancodeReleased", (bool(UInputMan::*)(SDL_Scancode) const) &UInputMan::KeyReleased) - .def("ScancodeHeld", (bool(UInputMan::*)(SDL_Scancode) const) &UInputMan::KeyHeld) - .def("MouseButtonPressed", &UInputMan::MouseButtonPressed) - .def("MouseButtonReleased", &UInputMan::MouseButtonReleased) - .def("MouseButtonHeld", &UInputMan::MouseButtonHeld) - .def("GetMousePos", &UInputMan::GetAbsoluteMousePosition) - .def("MouseWheelMoved", &UInputMan::MouseWheelMoved) - .def("JoyButtonPressed", &UInputMan::JoyButtonPressed) - .def("JoyButtonReleased", &UInputMan::JoyButtonReleased) - .def("JoyButtonHeld", &UInputMan::JoyButtonHeld) - .def("WhichJoyButtonPressed", &UInputMan::WhichJoyButtonPressed) - .def("JoyDirectionPressed", &UInputMan::JoyDirectionPressed) - .def("JoyDirectionReleased", &UInputMan::JoyDirectionReleased) - .def("JoyDirectionHeld", &UInputMan::JoyDirectionHeld) - .def("AnalogMoveValues", &UInputMan::AnalogMoveValues) - .def("AnalogAimValues", &UInputMan::AnalogAimValues) - .def("SetMouseValueMagnitude", &UInputMan::SetMouseValueMagnitude) - .def("AnalogAxisValue", &UInputMan::AnalogAxisValue) - .def("MouseUsedByPlayer", &UInputMan::MouseUsedByPlayer) - .def("AnyMouseButtonPress", &UInputMan::AnyMouseButtonPress) - .def("GetMouseMovement", &UInputMan::GetMouseMovement) - .def("DisableMouseMoving", &UInputMan::DisableMouseMoving) - .def("SetMousePos", &UInputMan::SetMousePos) - .def("ForceMouseWithinBox", &UInputMan::ForceMouseWithinBox) - .def("AnyKeyPress", &UInputMan::AnyKeyPress) - .def("AnyJoyInput", &UInputMan::AnyJoyInput) - .def("AnyJoyPress", &UInputMan::AnyJoyPress) - .def("AnyJoyButtonPress", &UInputMan::AnyJoyButtonPress) - .def("AnyInput", &UInputMan::AnyKeyOrJoyInput) - .def("AnyPress", &UInputMan::AnyPress) - .def("AnyStartPress", &UInputMan::AnyStartPress) - .def("HasTextInput", &UInputMan::HasTextInput) - .def("GetTextInput", (const std::string& (UInputMan::*)() const) &UInputMan::GetTextInput) - - .def("MouseButtonPressed", &LuaAdaptersUInputMan::MouseButtonPressed) - .def("MouseButtonReleased", &LuaAdaptersUInputMan::MouseButtonReleased) - .def("MouseButtonHeld", &LuaAdaptersUInputMan::MouseButtonHeld); + .property("FlagAltState", &UInputMan::FlagAltState) + .property("FlagCtrlState", &UInputMan::FlagCtrlState) + .property("FlagShiftState", &UInputMan::FlagShiftState) + + .def("GetInputDevice", &UInputMan::GetInputDevice) + .def("ElementPressed", &UInputMan::ElementPressed) + .def("ElementReleased", &UInputMan::ElementReleased) + .def("ElementHeld", &UInputMan::ElementHeld) + .def("KeyPressed", (bool(UInputMan::*)(SDL_Keycode) const) & UInputMan::KeyPressed) + .def("KeyReleased", (bool(UInputMan::*)(SDL_Keycode) const) & UInputMan::KeyReleased) + .def("KeyHeld", (bool(UInputMan::*)(SDL_Keycode) const) & UInputMan::KeyHeld) + .def("ScancodePressed", (bool(UInputMan::*)(SDL_Scancode) const) & UInputMan::KeyPressed) + .def("ScancodeReleased", (bool(UInputMan::*)(SDL_Scancode) const) & UInputMan::KeyReleased) + .def("ScancodeHeld", (bool(UInputMan::*)(SDL_Scancode) const) & UInputMan::KeyHeld) + .def("MouseButtonPressed", &UInputMan::MouseButtonPressed) + .def("MouseButtonReleased", &UInputMan::MouseButtonReleased) + .def("MouseButtonHeld", &UInputMan::MouseButtonHeld) + .def("GetMousePos", &UInputMan::GetAbsoluteMousePosition) + .def("MouseWheelMoved", &UInputMan::MouseWheelMoved) + .def("JoyButtonPressed", &UInputMan::JoyButtonPressed) + .def("JoyButtonReleased", &UInputMan::JoyButtonReleased) + .def("JoyButtonHeld", &UInputMan::JoyButtonHeld) + .def("WhichJoyButtonPressed", &UInputMan::WhichJoyButtonPressed) + .def("JoyDirectionPressed", &UInputMan::JoyDirectionPressed) + .def("JoyDirectionReleased", &UInputMan::JoyDirectionReleased) + .def("JoyDirectionHeld", &UInputMan::JoyDirectionHeld) + .def("AnalogMoveValues", &UInputMan::AnalogMoveValues) + .def("AnalogAimValues", &UInputMan::AnalogAimValues) + .def("SetMouseValueMagnitude", &UInputMan::SetMouseValueMagnitude) + .def("AnalogAxisValue", &UInputMan::AnalogAxisValue) + .def("MouseUsedByPlayer", &UInputMan::MouseUsedByPlayer) + .def("AnyMouseButtonPress", &UInputMan::AnyMouseButtonPress) + .def("GetMouseMovement", &UInputMan::GetMouseMovement) + .def("DisableMouseMoving", &UInputMan::DisableMouseMoving) + .def("SetMousePos", &UInputMan::SetMousePos) + .def("ForceMouseWithinBox", &UInputMan::ForceMouseWithinBox) + .def("AnyKeyPress", &UInputMan::AnyKeyPress) + .def("AnyJoyInput", &UInputMan::AnyJoyInput) + .def("AnyJoyPress", &UInputMan::AnyJoyPress) + .def("AnyJoyButtonPress", &UInputMan::AnyJoyButtonPress) + .def("AnyInput", &UInputMan::AnyKeyOrJoyInput) + .def("AnyPress", &UInputMan::AnyPress) + .def("AnyStartPress", &UInputMan::AnyStartPress) + .def("HasTextInput", &UInputMan::HasTextInput) + .def("GetTextInput", (const std::string& (UInputMan::*)() const) & UInputMan::GetTextInput) + + .def("MouseButtonPressed", &LuaAdaptersUInputMan::MouseButtonPressed) + .def("MouseButtonReleased", &LuaAdaptersUInputMan::MouseButtonReleased) + .def("MouseButtonHeld", &LuaAdaptersUInputMan::MouseButtonHeld); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Lua/LuaBindingsMisc.cpp b/Source/Lua/LuaBindingsMisc.cpp index 55bbaa64ef..d78e870f41 100644 --- a/Source/Lua/LuaBindingsMisc.cpp +++ b/Source/Lua/LuaBindingsMisc.cpp @@ -4,53 +4,49 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(MiscLuaBindings, AlarmEvent) { return luabind::class_("AlarmEvent") - .def(luabind::constructor<>()) - .def(luabind::constructor()) + .def(luabind::constructor<>()) + .def(luabind::constructor()) - .def_readwrite("ScenePos", &AlarmEvent::m_ScenePos) - .def_readwrite("Team", &AlarmEvent::m_Team) - .def_readwrite("Range", &AlarmEvent::m_Range); + .def_readwrite("ScenePos", &AlarmEvent::m_ScenePos) + .def_readwrite("Team", &AlarmEvent::m_Team) + .def_readwrite("Range", &AlarmEvent::m_Range); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(MiscLuaBindings, Directions) { return luabind::class_("Directions") - .enum_("Directions")[ - luabind::value("None", Directions::None), - luabind::value("Up", Directions::Up), - luabind::value("Down", Directions::Down), - luabind::value("Left", Directions::Left), - luabind::value("Right", Directions::Right), - luabind::value("Any", Directions::Any) - ]; + .enum_("Directions")[luabind::value("None", Directions::None), + luabind::value("Up", Directions::Up), + luabind::value("Down", Directions::Down), + luabind::value("Left", Directions::Left), + luabind::value("Right", Directions::Right), + luabind::value("Any", Directions::Any)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(MiscLuaBindings, DrawBlendMode) { return luabind::class_("DrawBlendMode") - .enum_("DrawBlendMode")[ - luabind::value("NoBlend", DrawBlendMode::NoBlend), - luabind::value("Burn", DrawBlendMode::BlendBurn), - luabind::value("Color", DrawBlendMode::BlendColor), - luabind::value("Difference", DrawBlendMode::BlendDifference), - luabind::value("Dissolve", DrawBlendMode::BlendDissolve), - luabind::value("Dodge", DrawBlendMode::BlendDodge), - luabind::value("Invert", DrawBlendMode::BlendInvert), - luabind::value("Luminance", DrawBlendMode::BlendLuminance), - luabind::value("Multiply", DrawBlendMode::BlendMultiply), - luabind::value("Saturation", DrawBlendMode::BlendSaturation), - luabind::value("Screen", DrawBlendMode::BlendScreen), - luabind::value("Transparency", DrawBlendMode::BlendTransparency), - luabind::value("BlendModeCount", DrawBlendMode::BlendModeCount) - ]; + .enum_("DrawBlendMode")[luabind::value("NoBlend", DrawBlendMode::NoBlend), + luabind::value("Burn", DrawBlendMode::BlendBurn), + luabind::value("Color", DrawBlendMode::BlendColor), + luabind::value("Difference", DrawBlendMode::BlendDifference), + luabind::value("Dissolve", DrawBlendMode::BlendDissolve), + luabind::value("Dodge", DrawBlendMode::BlendDodge), + luabind::value("Invert", DrawBlendMode::BlendInvert), + luabind::value("Luminance", DrawBlendMode::BlendLuminance), + luabind::value("Multiply", DrawBlendMode::BlendMultiply), + luabind::value("Saturation", DrawBlendMode::BlendSaturation), + luabind::value("Screen", DrawBlendMode::BlendScreen), + luabind::value("Transparency", DrawBlendMode::BlendTransparency), + luabind::value("BlendModeCount", DrawBlendMode::BlendModeCount)]; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Lua/LuaBindingsPrimitives.cpp b/Source/Lua/LuaBindingsPrimitives.cpp index ae3ac4726b..b6b291ae9d 100644 --- a/Source/Lua/LuaBindingsPrimitives.cpp +++ b/Source/Lua/LuaBindingsPrimitives.cpp @@ -4,130 +4,130 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, GraphicalPrimitive) { return luabind::class_("GraphicalPrimitive"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, LinePrimitive) { return luabind::class_("LinePrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, ArcPrimitive) { return luabind::class_("ArcPrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, SplinePrimitive) { return luabind::class_("SplinePrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, BoxPrimitive) { return luabind::class_("BoxPrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, BoxFillPrimitive) { return luabind::class_("BoxFillPrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, RoundedBoxPrimitive) { return luabind::class_("RoundedBoxPrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, RoundedBoxFillPrimitive) { return luabind::class_("RoundedBoxFillPrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, CirclePrimitive) { return luabind::class_("CirclePrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, CircleFillPrimitive) { return luabind::class_("CircleFillPrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, EllipsePrimitive) { return luabind::class_("EllipsePrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, EllipseFillPrimitive) { return luabind::class_("EllipseFillPrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, TrianglePrimitive) { return luabind::class_("TrianglePrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, TriangleFillPrimitive) { return luabind::class_("TriangleFillPrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, TextPrimitive) { return luabind::class_("TextPrimitive") - .def(luabind::constructor()); + .def(luabind::constructor()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, BitmapPrimitive) { return luabind::class_("BitmapPrimitive") - .def(luabind::constructor()) - .def(luabind::constructor()); + .def(luabind::constructor()) + .def(luabind::constructor()); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Lua/LuaBindingsSystem.cpp b/Source/Lua/LuaBindingsSystem.cpp index 03a9eb55f3..0e464cd007 100644 --- a/Source/Lua/LuaBindingsSystem.cpp +++ b/Source/Lua/LuaBindingsSystem.cpp @@ -4,252 +4,246 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, Box) { return luabind::class_("Box") - .def(luabind::constructor<>()) - .def(luabind::constructor()) - .def(luabind::constructor()) - .def(luabind::constructor()) - .def(luabind::constructor()) - .def(luabind::self == luabind::other()) - - .property("ClassName", &Box::GetClassName) - .property("Corner", &Box::GetCorner, &Box::SetCorner) - .property("Width", &Box::GetWidth, &Box::SetWidth) - .property("Height", &Box::GetHeight, &Box::SetHeight) - .property("Center", &Box::GetCenter, &Box::SetCenter) - .property("Area", &Box::GetArea) - - .def("GetRandomPoint", &Box::GetRandomPoint) - .def("Unflip", &Box::Unflip) - .def("IsWithinBox", &Box::IsWithinBox) - .def("IsWithinBoxX", &Box::IsWithinBoxX) - .def("IsWithinBoxY", &Box::IsWithinBoxY) - .def("GetWithinBoxX", &Box::GetWithinBoxX) - .def("GetWithinBoxY", &Box::GetWithinBoxY) - .def("GetWithinBox", &Box::GetWithinBox) - .def("IntersectsBox", &Box::IntersectsBox); + .def(luabind::constructor<>()) + .def(luabind::constructor()) + .def(luabind::constructor()) + .def(luabind::constructor()) + .def(luabind::constructor()) + .def(luabind::self == luabind::other()) + + .property("ClassName", &Box::GetClassName) + .property("Corner", &Box::GetCorner, &Box::SetCorner) + .property("Width", &Box::GetWidth, &Box::SetWidth) + .property("Height", &Box::GetHeight, &Box::SetHeight) + .property("Center", &Box::GetCenter, &Box::SetCenter) + .property("Area", &Box::GetArea) + + .def("GetRandomPoint", &Box::GetRandomPoint) + .def("Unflip", &Box::Unflip) + .def("IsWithinBox", &Box::IsWithinBox) + .def("IsWithinBoxX", &Box::IsWithinBoxX) + .def("IsWithinBoxY", &Box::IsWithinBoxY) + .def("GetWithinBoxX", &Box::GetWithinBoxX) + .def("GetWithinBoxY", &Box::GetWithinBoxY) + .def("GetWithinBox", &Box::GetWithinBox) + .def("IntersectsBox", &Box::IntersectsBox); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, Controller) { return luabind::class_("Controller") - .def(luabind::constructor<>()) - - .property("InputMode", &Controller::GetInputMode, &Controller::SetInputMode) - .property("ControlledActor", &Controller::GetControlledActor, &Controller::SetControlledActor) - .property("Team", &Controller::GetTeam, &Controller::SetTeam) - .property("AnalogMove", &Controller::GetAnalogMove, &Controller::SetAnalogMove) - .property("AnalogAim", &Controller::GetAnalogAim, &Controller::SetAnalogAim) - .property("AnalogCursor", &Controller::GetAnalogCursor, &Controller::SetAnalogCursor) - .property("Player", &Controller::GetPlayer, &Controller::SetPlayer) - .property("MouseMovement", &Controller::GetMouseMovement) - .property("Disabled", &Controller::IsDisabled, &Controller::SetDisabled) - - .def("IsPlayerControlled", &Controller::IsPlayerControlled) - .def("RelativeCursorMovement", &Controller::RelativeCursorMovement) - .def("IsMouseControlled", &Controller::IsMouseControlled) - .def("IsKeyboardOnlyControlled", &Controller::IsKeyboardOnlyControlled) - .def("IsGamepadControlled", &Controller::IsGamepadControlled) - .def("SetState", &Controller::SetState) - .def("IsState", &Controller::IsState) - .def("Override", &Controller::Override) - - .enum_("ControlState")[ - luabind::value("PRIMARY_ACTION", ControlState::PRIMARY_ACTION), - luabind::value("SECONDARY_ACTION", ControlState::SECONDARY_ACTION), - luabind::value("MOVE_IDLE", ControlState::MOVE_IDLE), - luabind::value("MOVE_RIGHT", ControlState::MOVE_RIGHT), - luabind::value("MOVE_LEFT", ControlState::MOVE_LEFT), - luabind::value("MOVE_UP", ControlState::MOVE_UP), - luabind::value("MOVE_DOWN", ControlState::MOVE_DOWN), - luabind::value("MOVE_FAST", ControlState::MOVE_FAST), - luabind::value("BODY_JUMPSTART", ControlState::BODY_JUMPSTART), - luabind::value("BODY_JUMP", ControlState::BODY_JUMP), - luabind::value("BODY_CROUCH", ControlState::BODY_CROUCH), - luabind::value("AIM_UP", ControlState::AIM_UP), - luabind::value("AIM_DOWN", ControlState::AIM_DOWN), - luabind::value("AIM_SHARP", ControlState::AIM_SHARP), - luabind::value("WEAPON_FIRE", ControlState::WEAPON_FIRE), - luabind::value("WEAPON_RELOAD", ControlState::WEAPON_RELOAD), - luabind::value("PIE_MENU_OPENED", ControlState::PIE_MENU_OPENED), - luabind::value("PIE_MENU_ACTIVE", ControlState::PIE_MENU_ACTIVE), - luabind::value("WEAPON_CHANGE_NEXT", ControlState::WEAPON_CHANGE_NEXT), - luabind::value("WEAPON_CHANGE_PREV", ControlState::WEAPON_CHANGE_PREV), - luabind::value("WEAPON_PICKUP", ControlState::WEAPON_PICKUP), - luabind::value("WEAPON_DROP", ControlState::WEAPON_DROP), - luabind::value("ACTOR_NEXT", ControlState::ACTOR_NEXT), - luabind::value("ACTOR_PREV", ControlState::ACTOR_PREV), - luabind::value("ACTOR_BRAIN", ControlState::ACTOR_BRAIN), - luabind::value("ACTOR_NEXT_PREP", ControlState::ACTOR_NEXT_PREP), - luabind::value("ACTOR_PREV_PREP", ControlState::ACTOR_PREV_PREP), - luabind::value("HOLD_RIGHT", ControlState::HOLD_RIGHT), - luabind::value("HOLD_LEFT", ControlState::HOLD_LEFT), - luabind::value("HOLD_UP", ControlState::HOLD_UP), - luabind::value("HOLD_DOWN", ControlState::HOLD_DOWN), - luabind::value("PRESS_PRIMARY", ControlState::PRESS_PRIMARY), - luabind::value("PRESS_SECONDARY", ControlState::PRESS_SECONDARY), - luabind::value("PRESS_RIGHT", ControlState::PRESS_RIGHT), - luabind::value("PRESS_LEFT", ControlState::PRESS_LEFT), - luabind::value("PRESS_UP", ControlState::PRESS_UP), - luabind::value("PRESS_DOWN", ControlState::PRESS_DOWN), - luabind::value("RELEASE_PRIMARY", ControlState::RELEASE_PRIMARY), - luabind::value("RELEASE_SECONDARY", ControlState::RELEASE_SECONDARY), - luabind::value("PRESS_FACEBUTTON", ControlState::PRESS_FACEBUTTON), - luabind::value("RELEASE_FACEBUTTON", ControlState::RELEASE_FACEBUTTON), - luabind::value("SCROLL_UP", ControlState::SCROLL_UP), - luabind::value("SCROLL_DOWN", ControlState::SCROLL_DOWN), - luabind::value("DEBUG_ONE", ControlState::DEBUG_ONE), - luabind::value("CONTROLSTATECOUNT", ControlState::CONTROLSTATECOUNT) - ] - .enum_("InputMode")[ - luabind::value("CIM_DISABLED", Controller::InputMode::CIM_DISABLED), - luabind::value("CIM_PLAYER", Controller::InputMode::CIM_PLAYER), - luabind::value("CIM_AI", Controller::InputMode::CIM_AI), - luabind::value("CIM_NETWORK", Controller::InputMode::CIM_NETWORK), - luabind::value("CIM_INPUTMODECOUNT", Controller::InputMode::CIM_INPUTMODECOUNT) - ]; + .def(luabind::constructor<>()) + + .property("InputMode", &Controller::GetInputMode, &Controller::SetInputMode) + .property("ControlledActor", &Controller::GetControlledActor, &Controller::SetControlledActor) + .property("Team", &Controller::GetTeam, &Controller::SetTeam) + .property("AnalogMove", &Controller::GetAnalogMove, &Controller::SetAnalogMove) + .property("AnalogAim", &Controller::GetAnalogAim, &Controller::SetAnalogAim) + .property("AnalogCursor", &Controller::GetAnalogCursor, &Controller::SetAnalogCursor) + .property("Player", &Controller::GetPlayer, &Controller::SetPlayer) + .property("MouseMovement", &Controller::GetMouseMovement) + .property("Disabled", &Controller::IsDisabled, &Controller::SetDisabled) + + .def("IsPlayerControlled", &Controller::IsPlayerControlled) + .def("RelativeCursorMovement", &Controller::RelativeCursorMovement) + .def("IsMouseControlled", &Controller::IsMouseControlled) + .def("IsKeyboardOnlyControlled", &Controller::IsKeyboardOnlyControlled) + .def("IsGamepadControlled", &Controller::IsGamepadControlled) + .def("SetState", &Controller::SetState) + .def("IsState", &Controller::IsState) + .def("Override", &Controller::Override) + + .enum_("ControlState")[luabind::value("PRIMARY_ACTION", ControlState::PRIMARY_ACTION), + luabind::value("SECONDARY_ACTION", ControlState::SECONDARY_ACTION), + luabind::value("MOVE_IDLE", ControlState::MOVE_IDLE), + luabind::value("MOVE_RIGHT", ControlState::MOVE_RIGHT), + luabind::value("MOVE_LEFT", ControlState::MOVE_LEFT), + luabind::value("MOVE_UP", ControlState::MOVE_UP), + luabind::value("MOVE_DOWN", ControlState::MOVE_DOWN), + luabind::value("MOVE_FAST", ControlState::MOVE_FAST), + luabind::value("BODY_JUMPSTART", ControlState::BODY_JUMPSTART), + luabind::value("BODY_JUMP", ControlState::BODY_JUMP), + luabind::value("BODY_CROUCH", ControlState::BODY_CROUCH), + luabind::value("AIM_UP", ControlState::AIM_UP), + luabind::value("AIM_DOWN", ControlState::AIM_DOWN), + luabind::value("AIM_SHARP", ControlState::AIM_SHARP), + luabind::value("WEAPON_FIRE", ControlState::WEAPON_FIRE), + luabind::value("WEAPON_RELOAD", ControlState::WEAPON_RELOAD), + luabind::value("PIE_MENU_OPENED", ControlState::PIE_MENU_OPENED), + luabind::value("PIE_MENU_ACTIVE", ControlState::PIE_MENU_ACTIVE), + luabind::value("WEAPON_CHANGE_NEXT", ControlState::WEAPON_CHANGE_NEXT), + luabind::value("WEAPON_CHANGE_PREV", ControlState::WEAPON_CHANGE_PREV), + luabind::value("WEAPON_PICKUP", ControlState::WEAPON_PICKUP), + luabind::value("WEAPON_DROP", ControlState::WEAPON_DROP), + luabind::value("ACTOR_NEXT", ControlState::ACTOR_NEXT), + luabind::value("ACTOR_PREV", ControlState::ACTOR_PREV), + luabind::value("ACTOR_BRAIN", ControlState::ACTOR_BRAIN), + luabind::value("ACTOR_NEXT_PREP", ControlState::ACTOR_NEXT_PREP), + luabind::value("ACTOR_PREV_PREP", ControlState::ACTOR_PREV_PREP), + luabind::value("HOLD_RIGHT", ControlState::HOLD_RIGHT), + luabind::value("HOLD_LEFT", ControlState::HOLD_LEFT), + luabind::value("HOLD_UP", ControlState::HOLD_UP), + luabind::value("HOLD_DOWN", ControlState::HOLD_DOWN), + luabind::value("PRESS_PRIMARY", ControlState::PRESS_PRIMARY), + luabind::value("PRESS_SECONDARY", ControlState::PRESS_SECONDARY), + luabind::value("PRESS_RIGHT", ControlState::PRESS_RIGHT), + luabind::value("PRESS_LEFT", ControlState::PRESS_LEFT), + luabind::value("PRESS_UP", ControlState::PRESS_UP), + luabind::value("PRESS_DOWN", ControlState::PRESS_DOWN), + luabind::value("RELEASE_PRIMARY", ControlState::RELEASE_PRIMARY), + luabind::value("RELEASE_SECONDARY", ControlState::RELEASE_SECONDARY), + luabind::value("PRESS_FACEBUTTON", ControlState::PRESS_FACEBUTTON), + luabind::value("RELEASE_FACEBUTTON", ControlState::RELEASE_FACEBUTTON), + luabind::value("SCROLL_UP", ControlState::SCROLL_UP), + luabind::value("SCROLL_DOWN", ControlState::SCROLL_DOWN), + luabind::value("DEBUG_ONE", ControlState::DEBUG_ONE), + luabind::value("CONTROLSTATECOUNT", ControlState::CONTROLSTATECOUNT)] + .enum_("InputMode")[luabind::value("CIM_DISABLED", Controller::InputMode::CIM_DISABLED), + luabind::value("CIM_PLAYER", Controller::InputMode::CIM_PLAYER), + luabind::value("CIM_AI", Controller::InputMode::CIM_AI), + luabind::value("CIM_NETWORK", Controller::InputMode::CIM_NETWORK), + luabind::value("CIM_INPUTMODECOUNT", Controller::InputMode::CIM_INPUTMODECOUNT)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, DataModule) { return luabind::class_("DataModule") - .property("FileName", &DataModule::GetFileName) - .property("FriendlyName", &DataModule::GetFriendlyName) - .property("Author", &DataModule::GetAuthor) - .property("Description", &DataModule::GetDescription) - .property("Version", &DataModule::GetVersionNumber) - .property("IsFaction", &DataModule::IsFaction) - .property("IsMerchant", &DataModule::IsMerchant) + .property("FileName", &DataModule::GetFileName) + .property("FriendlyName", &DataModule::GetFriendlyName) + .property("Author", &DataModule::GetAuthor) + .property("Description", &DataModule::GetDescription) + .property("Version", &DataModule::GetVersionNumber) + .property("IsFaction", &DataModule::IsFaction) + .property("IsMerchant", &DataModule::IsMerchant) - .def_readwrite("Presets", &DataModule::m_EntityList, luabind::return_stl_iterator); + .def_readwrite("Presets", &DataModule::m_EntityList, luabind::return_stl_iterator); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, Timer) { return luabind::class_("Timer") - .def(luabind::constructor<>()) - .def(luabind::constructor()) - .def(luabind::constructor()) - - .property("StartRealTimeMS", &Timer::GetStartRealTimeMS, &Timer::SetStartRealTimeMS) - .property("ElapsedRealTimeS", &Timer::GetElapsedRealTimeS, &Timer::SetElapsedRealTimeS) - .property("ElapsedRealTimeMS", &Timer::GetElapsedRealTimeMS, &Timer::SetElapsedRealTimeMS) - .property("StartSimTimeMS", &Timer::GetStartSimTimeMS, &Timer::SetStartSimTimeMS) - .property("ElapsedSimTimeS", &Timer::GetElapsedSimTimeS, &Timer::SetElapsedSimTimeS) - .property("ElapsedSimTimeMS", &Timer::GetElapsedSimTimeMS, &Timer::SetElapsedSimTimeMS) - .property("RealTimeLimitProgress", &Timer::RealTimeLimitProgress) - .property("SimTimeLimitProgress", &Timer::SimTimeLimitProgress) - - .def("Reset", &Timer::Reset) - .def("SetRealTimeLimitMS", &Timer::SetRealTimeLimitMS) - .def("SetRealTimeLimitS", &Timer::SetRealTimeLimitS) - .def("IsPastRealTimeLimit", &Timer::IsPastRealTimeLimit) - .def("LeftTillRealTimeLimitMS", &Timer::LeftTillRealTimeLimitMS) - .def("LeftTillRealTimeLimitS", &Timer::LeftTillRealTimeLimitS) - .def("LeftTillRealMS", &Timer::LeftTillRealMS) - .def("IsPastRealMS", &Timer::IsPastRealMS) - .def("AlternateReal", &Timer::AlternateReal) - .def("GetSimTimeLimitMS", &Timer::GetSimTimeLimitMS) - .def("SetSimTimeLimitMS", &Timer::SetSimTimeLimitMS) - .def("GetSimTimeLimitS", &Timer::GetSimTimeLimitS) - .def("SetSimTimeLimitS", &Timer::SetSimTimeLimitS) - .def("IsPastSimTimeLimit", &Timer::IsPastSimTimeLimit) - .def("LeftTillSimTimeLimitMS", &Timer::LeftTillSimTimeLimitMS) - .def("LeftTillSimTimeLimitS", &Timer::LeftTillSimTimeLimitS) - .def("LeftTillSimMS", &Timer::LeftTillSimMS) - .def("IsPastSimMS", &Timer::IsPastSimMS) - .def("AlternateSim", &Timer::AlternateSim); + .def(luabind::constructor<>()) + .def(luabind::constructor()) + .def(luabind::constructor()) + + .property("StartRealTimeMS", &Timer::GetStartRealTimeMS, &Timer::SetStartRealTimeMS) + .property("ElapsedRealTimeS", &Timer::GetElapsedRealTimeS, &Timer::SetElapsedRealTimeS) + .property("ElapsedRealTimeMS", &Timer::GetElapsedRealTimeMS, &Timer::SetElapsedRealTimeMS) + .property("StartSimTimeMS", &Timer::GetStartSimTimeMS, &Timer::SetStartSimTimeMS) + .property("ElapsedSimTimeS", &Timer::GetElapsedSimTimeS, &Timer::SetElapsedSimTimeS) + .property("ElapsedSimTimeMS", &Timer::GetElapsedSimTimeMS, &Timer::SetElapsedSimTimeMS) + .property("RealTimeLimitProgress", &Timer::RealTimeLimitProgress) + .property("SimTimeLimitProgress", &Timer::SimTimeLimitProgress) + + .def("Reset", &Timer::Reset) + .def("SetRealTimeLimitMS", &Timer::SetRealTimeLimitMS) + .def("SetRealTimeLimitS", &Timer::SetRealTimeLimitS) + .def("IsPastRealTimeLimit", &Timer::IsPastRealTimeLimit) + .def("LeftTillRealTimeLimitMS", &Timer::LeftTillRealTimeLimitMS) + .def("LeftTillRealTimeLimitS", &Timer::LeftTillRealTimeLimitS) + .def("LeftTillRealMS", &Timer::LeftTillRealMS) + .def("IsPastRealMS", &Timer::IsPastRealMS) + .def("AlternateReal", &Timer::AlternateReal) + .def("GetSimTimeLimitMS", &Timer::GetSimTimeLimitMS) + .def("SetSimTimeLimitMS", &Timer::SetSimTimeLimitMS) + .def("GetSimTimeLimitS", &Timer::GetSimTimeLimitS) + .def("SetSimTimeLimitS", &Timer::SetSimTimeLimitS) + .def("IsPastSimTimeLimit", &Timer::IsPastSimTimeLimit) + .def("LeftTillSimTimeLimitMS", &Timer::LeftTillSimTimeLimitMS) + .def("LeftTillSimTimeLimitS", &Timer::LeftTillSimTimeLimitS) + .def("LeftTillSimMS", &Timer::LeftTillSimMS) + .def("IsPastSimMS", &Timer::IsPastSimMS) + .def("AlternateSim", &Timer::AlternateSim); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, Vector) { return luabind::class_("Vector") - .def(luabind::constructor<>()) - .def(luabind::constructor()) - .def(luabind::self == luabind::other()) - .def(luabind::const_self + luabind::other()) - .def(luabind::const_self - luabind::other()) - .def(luabind::const_self * float()) - .def(luabind::const_self / float()) - .def(luabind::tostring(luabind::const_self)) - - .property("ClassName", &Vector::GetClassName) - .property("RoundedX", &Vector::GetRoundIntX) - .property("RoundedY", &Vector::GetRoundIntY) - .property("Rounded", &Vector::GetRounded) - .property("FlooredX", &Vector::GetFloorIntX) - .property("FlooredY", &Vector::GetFloorIntY) - .property("Floored", &Vector::GetFloored) - .property("CeilingedX", &Vector::GetCeilingIntX) - .property("CeilingedY", &Vector::GetCeilingIntY) - .property("Ceilinged", &Vector::GetCeilinged) - .property("Magnitude", &Vector::GetMagnitude) - .property("SqrMagnitude", &Vector::GetSqrMagnitude) - .property("Largest", &Vector::GetLargest) - .property("Smallest", &Vector::GetSmallest) - .property("Normalized", &Vector::GetNormalized) - .property("Perpendicular", &Vector::GetPerpendicular) - .property("AbsRadAngle", &Vector::GetAbsRadAngle, &Vector::SetAbsRadAngle) - .property("AbsDegAngle", &Vector::GetAbsDegAngle, &Vector::SetAbsDegAngle) - - .def_readwrite("X", &Vector::m_X) - .def_readwrite("Y", &Vector::m_Y) - - .def("MagnitudeIsGreaterThan", &Vector::MagnitudeIsGreaterThan) - .def("MagnitudeIsLessThan", &Vector::MagnitudeIsLessThan) - .def("SetMagnitude", &Vector::SetMagnitude) - .def("GetXFlipped", &Vector::GetXFlipped) - .def("GetYFlipped", &Vector::GetYFlipped) - .def("CapMagnitude", &Vector::CapMagnitude) - .def("ClampMagnitude", &Vector::ClampMagnitude) - .def("FlipX", &Vector::FlipX) - .def("FlipY", &Vector::FlipY) - .def("IsZero", &Vector::IsZero) - .def("IsOpposedTo", &Vector::IsOpposedTo) - .def("Dot", &Vector::Dot) - .def("Cross", &Vector::Cross) - .def("Round", &Vector::Round) - .def("ToHalf", &Vector::ToHalf) - .def("Floor", &Vector::Floor) - .def("Ceiling", &Vector::Ceiling) - .def("Normalize", &Vector::Normalize) - .def("Perpendicularize", &Vector::Perpendicularize) - .def("Reset", &Vector::Reset) - .def("RadRotate", &Vector::RadRotate) - .def("DegRotate", &Vector::DegRotate) - .def("GetRadRotatedCopy", &Vector::GetRadRotatedCopy) - .def("GetDegRotatedCopy", &Vector::GetDegRotatedCopy) - .def("AbsRotateTo", &Vector::AbsRotateTo) - .def("SetXY", &Vector::SetXY); + .def(luabind::constructor<>()) + .def(luabind::constructor()) + .def(luabind::self == luabind::other()) + .def(luabind::const_self + luabind::other()) + .def(luabind::const_self - luabind::other()) + .def(luabind::const_self * float()) + .def(luabind::const_self / float()) + .def(luabind::tostring(luabind::const_self)) + + .property("ClassName", &Vector::GetClassName) + .property("RoundedX", &Vector::GetRoundIntX) + .property("RoundedY", &Vector::GetRoundIntY) + .property("Rounded", &Vector::GetRounded) + .property("FlooredX", &Vector::GetFloorIntX) + .property("FlooredY", &Vector::GetFloorIntY) + .property("Floored", &Vector::GetFloored) + .property("CeilingedX", &Vector::GetCeilingIntX) + .property("CeilingedY", &Vector::GetCeilingIntY) + .property("Ceilinged", &Vector::GetCeilinged) + .property("Magnitude", &Vector::GetMagnitude) + .property("SqrMagnitude", &Vector::GetSqrMagnitude) + .property("Largest", &Vector::GetLargest) + .property("Smallest", &Vector::GetSmallest) + .property("Normalized", &Vector::GetNormalized) + .property("Perpendicular", &Vector::GetPerpendicular) + .property("AbsRadAngle", &Vector::GetAbsRadAngle, &Vector::SetAbsRadAngle) + .property("AbsDegAngle", &Vector::GetAbsDegAngle, &Vector::SetAbsDegAngle) + + .def_readwrite("X", &Vector::m_X) + .def_readwrite("Y", &Vector::m_Y) + + .def("MagnitudeIsGreaterThan", &Vector::MagnitudeIsGreaterThan) + .def("MagnitudeIsLessThan", &Vector::MagnitudeIsLessThan) + .def("SetMagnitude", &Vector::SetMagnitude) + .def("GetXFlipped", &Vector::GetXFlipped) + .def("GetYFlipped", &Vector::GetYFlipped) + .def("CapMagnitude", &Vector::CapMagnitude) + .def("ClampMagnitude", &Vector::ClampMagnitude) + .def("FlipX", &Vector::FlipX) + .def("FlipY", &Vector::FlipY) + .def("IsZero", &Vector::IsZero) + .def("IsOpposedTo", &Vector::IsOpposedTo) + .def("Dot", &Vector::Dot) + .def("Cross", &Vector::Cross) + .def("Round", &Vector::Round) + .def("ToHalf", &Vector::ToHalf) + .def("Floor", &Vector::Floor) + .def("Ceiling", &Vector::Ceiling) + .def("Normalize", &Vector::Normalize) + .def("Perpendicularize", &Vector::Perpendicularize) + .def("Reset", &Vector::Reset) + .def("RadRotate", &Vector::RadRotate) + .def("DegRotate", &Vector::DegRotate) + .def("GetRadRotatedCopy", &Vector::GetRadRotatedCopy) + .def("GetDegRotatedCopy", &Vector::GetDegRotatedCopy) + .def("AbsRotateTo", &Vector::AbsRotateTo) + .def("SetXY", &Vector::SetXY); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, PathRequest) { using namespace micropather; return luabind::class_("PathRequest") - .def_readonly("Path", &PathRequest::path, luabind::return_stl_iterator) - .def_readonly("PathLength", &PathRequest::pathLength) - .def_readonly("Status", &PathRequest::status) - .def_readonly("TotalCost", &PathRequest::totalCost) + .def_readonly("Path", &PathRequest::path, luabind::return_stl_iterator) + .def_readonly("PathLength", &PathRequest::pathLength) + .def_readonly("Status", &PathRequest::status) + .def_readonly("TotalCost", &PathRequest::totalCost) - .enum_("Status")[ - luabind::value("Solved", micropather::MicroPather::SOLVED), - luabind::value("NoSolution", micropather::MicroPather::NO_SOLUTION), - luabind::value("StartEndSame", micropather::MicroPather::START_END_SAME) - ]; + .enum_("Status")[luabind::value("Solved", micropather::MicroPather::SOLVED), + luabind::value("NoSolution", micropather::MicroPather::NO_SOLUTION), + luabind::value("StartEndSame", micropather::MicroPather::START_END_SAME)]; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Lua/LuabindDefinitions.h b/Source/Lua/LuabindDefinitions.h index 31507a3869..d4fae1b310 100644 --- a/Source/Lua/LuabindDefinitions.h +++ b/Source/Lua/LuabindDefinitions.h @@ -4,7 +4,7 @@ #define BOOST_BIND_GLOBAL_PLACEHOLDERS 1 // Todo - figure out luabind error callbacks and blah-de-blah -//#define LUABIND_NO_EXCEPTIONS +// #define LUABIND_NO_EXCEPTIONS #include "lua.hpp" #include "luabind.hpp" @@ -22,7 +22,7 @@ namespace luabind { /// /// The smart pointer to get raw pointer for. /// Raw pointer of the passed in smart pointer. - template Type * get_pointer(boost::shared_ptr &ptr) { + template Type* get_pointer(boost::shared_ptr& ptr) { return ptr.get(); } @@ -30,7 +30,7 @@ namespace luabind { /// Can't have global enums in the master state so we use this dummy struct as a class and register the enums under it. /// struct enum_wrapper {}; -} +} // namespace luabind namespace RTE { /// @@ -55,7 +55,7 @@ namespace RTE { /// /// The Lua master state. /// An error signal, 1, so Lua correctly reports that there's been an error. - static int AddFileAndLineToError(lua_State *luaState) { + static int AddFileAndLineToError(lua_State* luaState) { lua_Debug luaDebug; if (lua_getstack(luaState, 2, &luaDebug) > 0) { lua_getinfo(luaState, "Sln", &luaDebug); @@ -79,7 +79,7 @@ namespace RTE { /// The Lua table object to convert to vector. /// A C++ vector containing all the objects from the Lua table. Ownership is transferred! /// In case of type mismatch (by specifying wrong type or a mix of types in the Lua table) object_cast will print an error to the console and throw, so no need to check what it returns before emplacing. - template static std::vector ConvertLuaTableToVectorOfType(const luabind::object &luaObject) { + template static std::vector ConvertLuaTableToVectorOfType(const luabind::object& luaObject) { std::vector outVector = {}; if (luaObject.is_valid() && luabind::type(luaObject) == LUA_TTABLE) { for (luabind::iterator tableItr(luaObject), tableEnd; tableItr != tableEnd; ++tableItr) { @@ -88,5 +88,5 @@ namespace RTE { } return outVector; } -} +} // namespace RTE #endif diff --git a/Source/Lua/LuabindObjectWrapper.cpp b/Source/Lua/LuabindObjectWrapper.cpp index 5ab34c37c3..2241458588 100644 --- a/Source/Lua/LuabindObjectWrapper.cpp +++ b/Source/Lua/LuabindObjectWrapper.cpp @@ -7,77 +7,72 @@ namespace RTE { -// With multithreaded Lua, objects can be destructed from multiple threads at once -// This is okay, but LuaBind wants to do some management on the lua state when one of it's objects is deleted -// This means that potentially an object being deleted by one lua state actually exists in another lua state -// And upon deletion, it's unsafe for LuaBind to poke at the state until we're out the multithreaded context -// As such, we don't actually delete the object until we're in a safe environment outside the multithreaded parts -// Note - this is required even though we force objects in multithreaded environments to be within our Lua state -// This is because we may assign an object to another state in a singlethreaded context, before the GC runs in the multithreaded context -static std::vector s_QueuedDeletions; - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void LuabindObjectWrapper::ApplyQueuedDeletions() { - for (luabind::adl::object *obj : s_QueuedDeletions) { - delete obj; - } - - s_QueuedDeletions.clear(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // With multithreaded Lua, objects can be destructed from multiple threads at once + // This is okay, but LuaBind wants to do some management on the lua state when one of it's objects is deleted + // This means that potentially an object being deleted by one lua state actually exists in another lua state + // And upon deletion, it's unsafe for LuaBind to poke at the state until we're out the multithreaded context + // As such, we don't actually delete the object until we're in a safe environment outside the multithreaded parts + // Note - this is required even though we force objects in multithreaded environments to be within our Lua state + // This is because we may assign an object to another state in a singlethreaded context, before the GC runs in the multithreaded context + static std::vector s_QueuedDeletions; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void LuabindObjectWrapper::ApplyQueuedDeletions() { + for (luabind::adl::object* obj: s_QueuedDeletions) { + delete obj; + } -LuabindObjectWrapper::~LuabindObjectWrapper() { - if (m_OwnsObject) { - static std::mutex mut; - std::lock_guard guard(mut); - s_QueuedDeletions.push_back(m_LuabindObject); + s_QueuedDeletions.clear(); } -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -luabind::adl::object GetCopyForStateInternal(const luabind::adl::object& obj, lua_State& targetState) { - if (obj.is_valid()) { - int type = luabind::type(obj); - if (type == LUA_TNUMBER) { - return luabind::adl::object(&targetState, luabind::object_cast(obj)); - } - else if (type == LUA_TBOOLEAN) { - return luabind::adl::object(&targetState, luabind::object_cast(obj)); - } - else if (type == LUA_TSTRING) { - return luabind::adl::object(&targetState, luabind::object_cast(obj)); - } - else if (type == LUA_TTABLE) { - luabind::object table = luabind::newtable(&targetState); - for (luabind::iterator itr(obj), itrEnd; itr != itrEnd; ++itr) { - table[GetCopyForStateInternal(itr.key(), targetState)] = GetCopyForStateInternal(*itr, targetState); - } - return table; + LuabindObjectWrapper::~LuabindObjectWrapper() { + if (m_OwnsObject) { + static std::mutex mut; + std::lock_guard guard(mut); + s_QueuedDeletions.push_back(m_LuabindObject); } - else if (type == LUA_TUSERDATA) { -#define PER_LUA_BINDING(Type) \ - if (boost::optional boundObject = luabind::object_cast_nothrow(obj)) { \ - return luabind::adl::object(&targetState, boundObject.get()); \ - } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + luabind::adl::object GetCopyForStateInternal(const luabind::adl::object& obj, lua_State& targetState) { + if (obj.is_valid()) { + int type = luabind::type(obj); + if (type == LUA_TNUMBER) { + return luabind::adl::object(&targetState, luabind::object_cast(obj)); + } else if (type == LUA_TBOOLEAN) { + return luabind::adl::object(&targetState, luabind::object_cast(obj)); + } else if (type == LUA_TSTRING) { + return luabind::adl::object(&targetState, luabind::object_cast(obj)); + } else if (type == LUA_TTABLE) { + luabind::object table = luabind::newtable(&targetState); + for (luabind::iterator itr(obj), itrEnd; itr != itrEnd; ++itr) { + table[GetCopyForStateInternal(itr.key(), targetState)] = GetCopyForStateInternal(*itr, targetState); + } + return table; + } else if (type == LUA_TUSERDATA) { +#define PER_LUA_BINDING(Type) \ + if (boost::optional boundObject = luabind::object_cast_nothrow(obj)) { \ + return luabind::adl::object(&targetState, boundObject.get()); \ + } - LIST_OF_LUABOUND_OBJECTS + LIST_OF_LUABOUND_OBJECTS #undef PER_LUA_BINDING + } } + // Dear god, I hope this is safe and equivalent to nil, because I can't find another way of doing it. + return luabind::adl::object(); } - // Dear god, I hope this is safe and equivalent to nil, because I can't find another way of doing it. - return luabind::adl::object(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -LuabindObjectWrapper LuabindObjectWrapper::GetCopyForState(lua_State& targetState) const { - luabind::adl::object* copy = new luabind::adl::object(GetCopyForStateInternal(*m_LuabindObject, targetState)); - return LuabindObjectWrapper(copy, m_FilePath, true); -} + LuabindObjectWrapper LuabindObjectWrapper::GetCopyForState(lua_State& targetState) const { + luabind::adl::object* copy = new luabind::adl::object(GetCopyForStateInternal(*m_LuabindObject, targetState)); + return LuabindObjectWrapper(copy, m_FilePath, true); + } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Lua/LuabindObjectWrapper.h b/Source/Lua/LuabindObjectWrapper.h index dc5b56b32f..aef459eaa9 100644 --- a/Source/Lua/LuabindObjectWrapper.h +++ b/Source/Lua/LuabindObjectWrapper.h @@ -11,14 +11,14 @@ namespace RTE { #pragma region Global Macro Definitions #define ScriptFunctionNames(...) \ - virtual std::vector GetSupportedScriptFunctionNames() const { return {__VA_ARGS__}; } + virtual std::vector GetSupportedScriptFunctionNames() const { return {__VA_ARGS__}; } #define AddScriptFunctionNames(PARENT, ...) \ - std::vector GetSupportedScriptFunctionNames() const override { \ - std::vector functionNames = PARENT::GetSupportedScriptFunctionNames(); \ - functionNames.insert(functionNames.end(), {__VA_ARGS__}); \ - return functionNames; \ - } + std::vector GetSupportedScriptFunctionNames() const override { \ + std::vector functionNames = PARENT::GetSupportedScriptFunctionNames(); \ + functionNames.insert(functionNames.end(), {__VA_ARGS__}); \ + return functionNames; \ + } #pragma endregion /// @@ -27,7 +27,6 @@ namespace RTE { class LuabindObjectWrapper { public: - #pragma region Creation /// /// Constructor method used for LuabindObjectWrapper. @@ -37,7 +36,8 @@ namespace RTE { /// /// Constructor method used to instantiate a LuabindObjectWrapper object in system memory. /// - explicit LuabindObjectWrapper(luabind::adl::object* luabindObject, const std::string_view& filePath, bool ownsObject = true) : m_LuabindObject(luabindObject), m_FilePath(filePath), m_OwnsObject(ownsObject) {} + explicit LuabindObjectWrapper(luabind::adl::object* luabindObject, const std::string_view& filePath, bool ownsObject = true) : + m_LuabindObject(luabindObject), m_FilePath(filePath), m_OwnsObject(ownsObject) {} #pragma endregion #pragma region Destruction @@ -59,24 +59,23 @@ namespace RTE { /// Gets the LuabindObjectWrapper's luabind object. Ownership is NOT transferred! /// /// The LuabindObjectWrapper's luabind object. - luabind::adl::object * GetLuabindObject() const { return m_LuabindObject; } + luabind::adl::object* GetLuabindObject() const { return m_LuabindObject; } /// /// Gets the LuabindObjectWrapper's file path. /// /// The LuabindObjectWrapper's file path. - const std::string & GetFilePath() const { return m_FilePath; } + const std::string& GetFilePath() const { return m_FilePath; } #pragma endregion private: - bool m_OwnsObject; //!< Whether or not we own the luabind object this is wrapping. - luabind::adl::object *m_LuabindObject; //!< The luabind object this is wrapping. + luabind::adl::object* m_LuabindObject; //!< The luabind object this is wrapping. std::string m_FilePath; //!< The filepath the wrapped luabind object represents, if it's a function. // Disallow the use of some implicit methods. - LuabindObjectWrapper(const LuabindObjectWrapper &reference) = delete; - LuabindObjectWrapper &operator=(const LuabindObjectWrapper &rhs) = delete; + LuabindObjectWrapper(const LuabindObjectWrapper& reference) = delete; + LuabindObjectWrapper& operator=(const LuabindObjectWrapper& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Main.cpp b/Source/Main.cpp index f96bcfa943..cc338b0495 100644 --- a/Source/Main.cpp +++ b/Source/Main.cpp @@ -2,7 +2,7 @@ /\ ___\ /\ __ \ /\ == \/\__ _\/\ ___\/\_\_\_\ /\ ___\ /\ __ \ /\ "-./ \ /\ "-./ \ /\ __ \ /\ "-.\ \ /\ __-. \ \ \____\ \ \/\ \\ \ __<\/_/\ \/\ \ __\\/_/\_\/_ \ \ \____\ \ \/\ \\ \ \-./\ \\ \ \-./\ \\ \ __ \\ \ \-. \\ \ \/\ \ \ \_____\\ \_____\\ \_\ \_\ \ \_\ \ \_____\/\_\/\_\ \ \_____\\ \_____\\ \_\ \ \_\\ \_\ \ \_\\ \_\ \_\\ \_\\"\_\\ \____- - \/_____/ \/_____/ \/_/ /_/ \/_/ \/_____/\/_/\/_/ \/_____/ \/_____/ \/_/ \/_/ \/_/ \/_/ \/_/\/_/ \/_/ \/_/ \/____/ + \/_____/ \/_____/ \/_/ /_/ \/_/ \/_____/\/_/\/_/ \/_____/ \/_____/ \/_/ \/_/ \/_/ \/_/ \/_/\/_/ \/_/ \/_/ \/____/ ______ ______ __ __ __ __ __ __ __ __ __ ______ __ __ ______ ______ ______ __ ______ ______ ______ /\ ___\ /\ __ \ /\ "-./ \ /\ "-./ \ /\ \/\ \ /\ "-.\ \ /\ \ /\__ _\/\ \_\ \ /\ == \/\ == \ /\ __ \ /\ \ /\ ___\ /\ ___\ /\__ _\ \ \ \____\ \ \/\ \\ \ \-./\ \\ \ \-./\ \\ \ \_\ \\ \ \-. \\ \ \\/_/\ \/\ \____ \ \ \ _-/\ \ __< \ \ \/\ \ _\_\ \\ \ __\ \ \ \____\/_/\ \/ @@ -50,13 +50,15 @@ #include "tracy/Tracy.hpp" -extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; } +extern "C" { +FILE __iob_func[3] = {*stdin, *stdout, *stderr}; +} using namespace RTE; namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Initializes all the essential managers. @@ -98,7 +100,9 @@ namespace RTE { g_PostProcessMan.Initialize(); g_PerformanceMan.Initialize(); - if (g_AudioMan.Initialize()) { g_GUISound.Initialize(); } + if (g_AudioMan.Initialize()) { + g_GUISound.Initialize(); + } g_UInputMan.Initialize(); g_ConsoleMan.Initialize(); @@ -109,10 +113,12 @@ namespace RTE { // Overwrite Settings.ini after all the managers are created to fully populate the file. Up until this moment Settings.ini is populated only with minimal required properties to run. // If Settings.ini already exists and is fully populated, this will deal with overwriting it to apply any overrides performed by the managers at boot (e.g resolution validation). - if (g_SettingsMan.SettingsNeedOverwrite()) { g_SettingsMan.UpdateSettingsFile(); } + if (g_SettingsMan.SettingsNeedOverwrite()) { + g_SettingsMan.UpdateSettingsFile(); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Destroys all the managers and frees all loaded data before termination. @@ -142,14 +148,14 @@ namespace RTE { #endif } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Command-line argument handling. /// /// Argument count. /// Argument values. - void HandleMainArgs(int argCount, char **argValue) { + void HandleMainArgs(int argCount, char** argValue) { // Discard the first argument because it's always the executable path/name argCount--; argValue++; @@ -163,9 +169,13 @@ namespace RTE { std::string currentArg = argValue[i]; bool lastArg = i + 1 == argCount; - if (currentArg == "-cout") { System::EnableLoggingToCLI(); } + if (currentArg == "-cout") { + System::EnableLoggingToCLI(); + } - if (currentArg == "-ext-validate") { System::EnableExternalModuleValidationMode(); } + if (currentArg == "-ext-validate") { + System::EnableExternalModuleValidationMode(); + } if (!lastArg && !singleModuleSet && currentArg == "-module") { std::string moduleToLoad = argValue[++i]; @@ -186,10 +196,12 @@ namespace RTE { } ++i; } - if (launchModeSet) { g_SettingsMan.SetSkipIntro(true); } + if (launchModeSet) { + g_SettingsMan.SetSkipIntro(true); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Polls the SDL event queue and passes events to be handled by the relevant managers. @@ -231,7 +243,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Game menus loop. @@ -270,7 +282,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Game simulation loop. @@ -377,7 +389,9 @@ namespace RTE { // Pause sim while we're waiting for scene transmission or scene will start changing before clients receive them and those changes will be lost. g_TimerMan.PauseSim(!(g_NetworkServer.ReadyForSimulation() && g_ActivityMan.IsInActivity())); - if (!serverUpdated) { g_NetworkServer.Update(); } + if (!serverUpdated) { + g_NetworkServer.Update(); + } if (g_NetworkServer.GetServerSimSleepWhenIdle()) { long long ticksToSleep = g_TimerMan.GetTimeToSleep(); @@ -400,7 +414,7 @@ namespace RTE { g_PerformanceMan.UpdateMSPF(updateTotalTime, drawTotalTime); } } -} +} // namespace RTE ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -417,7 +431,7 @@ static const bool RTESetExceptionHandlers = []() { /// /// Implementation of the main function. /// -int main(int argc, char **argv) { +int main(int argc, char** argv) { install_allegro(SYSTEM_NONE, &errno, std::atexit); loadpng_init(); @@ -439,7 +453,7 @@ int main(int argc, char **argv) { // TODO: use a better thread system that'll do what we want ASAP instead of letting the OS schedule all over us // Disabled for now because windows is great and this means when the game lags out it freezes the entire computer. Which we wouldn't expect with anything but REALTIME priority. // Because apparently high priority class is preferred over "processing mouse input"?! - //SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); + // SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); #endif // WIN32 // argv[0] actually unreliable for exe path and name, because of course, why would it be, why would anything be simple and make sense. @@ -468,7 +482,9 @@ int main(int argc, char **argv) { g_ConsoleMan.SetEnabled(true); } else { // Delete an existing log if there are no warnings so there's less junk in the root folder. - if (std::filesystem::exists(System::GetWorkingDirectory() + "LogLoadingWarning.txt")) { std::remove("LogLoadingWarning.txt"); } + if (std::filesystem::exists(System::GetWorkingDirectory() + "LogLoadingWarning.txt")) { + std::remove("LogLoadingWarning.txt"); + } } if (!g_ActivityMan.Initialize()) { diff --git a/Source/Managers/AchievementMan.cpp b/Source/Managers/AchievementMan.cpp index 0effceef19..6526b1058f 100644 --- a/Source/Managers/AchievementMan.cpp +++ b/Source/Managers/AchievementMan.cpp @@ -15,24 +15,24 @@ AchievementMan::AchievementMan(): AchievementMan::AchievementMan() #endif // STEAM_BUILD { - m_Initialized = false; + m_Initialized = false; #ifdef STEAM_BUILD - m_pSteamUserStats = SteamUserStats(); - - if (m_pSteamUserStats) - { - m_pSteamUserStats->RequestCurrentStats(); - } + m_pSteamUserStats = SteamUserStats(); + + if (m_pSteamUserStats) + { + m_pSteamUserStats->RequestCurrentStats(); + } #endif // STEAM_BUILD } int AchievementMan::Create() { #ifdef STEAM_BUILD - if (!m_pSteamUserStats || !m_pSteamUserStats->RequestCurrentStats()) - { - g_ConsoleMan.PrintString("STEAM API: Failed to download stats!"); - } + if (!m_pSteamUserStats || !m_pSteamUserStats->RequestCurrentStats()) + { + g_ConsoleMan.PrintString("STEAM API: Failed to download stats!"); + } #endif // STEAM_BUILD return 0; @@ -40,114 +40,114 @@ int AchievementMan::Create() bool AchievementMan::IsAchievementUnlocked(const char *achievementName) { - bool unlocked = false; + bool unlocked = false; #ifdef STEAM_BUILD - if (m_pSteamUserStats && !m_pSteamUserStats->GetAchievement(achievementName, &unlocked)) - { - g_ConsoleMan.PrintString("STEAM API: Failed to get achievement status!"); - } + if (m_pSteamUserStats && !m_pSteamUserStats->GetAchievement(achievementName, &unlocked)) + { + g_ConsoleMan.PrintString("STEAM API: Failed to get achievement status!"); + } #endif // STEAM_BUILD - return unlocked; + return unlocked; } void AchievementMan::LockAchievement(const char *achievementName) { #ifdef STEAM_BUILD - if (m_pSteamUserStats) - { - m_pSteamUserStats->ClearAchievement(achievementName); - Sync(); - } + if (m_pSteamUserStats) + { + m_pSteamUserStats->ClearAchievement(achievementName); + Sync(); + } #endif // STEAM_BUILD } void AchievementMan::UnlockAchievement(const char *achievementName) { #ifdef STEAM_BUILD - if (m_pSteamUserStats) - { - m_pSteamUserStats->SetAchievement(achievementName); - Sync(); - } + if (m_pSteamUserStats) + { + m_pSteamUserStats->SetAchievement(achievementName); + Sync(); + } #endif // STEAM_BUILD } #ifdef STEAM_BUILD void AchievementMan::OnUserStatsReceived(UserStatsReceived_t *data) { - if (data->m_nGameID && data->m_eResult == k_EResultOK) - { - g_ConsoleMan.PrintString("STEAM API: Achievement status received!"); - m_Initialized = true; - Sync(); - - UnlockAchievement( "CC_BOOTUP" ); - } + if (data->m_nGameID && data->m_eResult == k_EResultOK) + { + g_ConsoleMan.PrintString("STEAM API: Achievement status received!"); + m_Initialized = true; + Sync(); + + UnlockAchievement( "CC_BOOTUP" ); + } } #endif // STEAM_BUILD void AchievementMan::ProgressAchievement(const char *achievementName, int progress, int max) { #ifdef STEAM_BUILD - if (!IsReady() || !m_pSteamUserStats) - { - g_ConsoleMan.PrintString("ACHIEVEMENTS: Tried to increase progress, but AchievementMan isn't ready!"); - return; - } - - std::string name = achievementName + std::string("_PROGRESS"); - const char *statName = name.c_str(); - - int current = 0; - m_pSteamUserStats->GetStat(statName, ¤t); - - int val = progress + current; - if (val >= max) - { - UnlockAchievement(achievementName); - val = max; - } - - m_pSteamUserStats->SetStat(statName, val); + if (!IsReady() || !m_pSteamUserStats) + { + g_ConsoleMan.PrintString("ACHIEVEMENTS: Tried to increase progress, but AchievementMan isn't ready!"); + return; + } + + std::string name = achievementName + std::string("_PROGRESS"); + const char *statName = name.c_str(); + + int current = 0; + m_pSteamUserStats->GetStat(statName, ¤t); + + int val = progress + current; + if (val >= max) + { + UnlockAchievement(achievementName); + val = max; + } + + m_pSteamUserStats->SetStat(statName, val); #endif // STEAM_BUILD } void AchievementMan::SetAchievementBit(const char *achievementName, int bit, int max) { #ifdef STEAM_BUILD - if (!IsReady() || !m_pSteamUserStats) - { - g_ConsoleMan.PrintString("ACHIEVEMENTS: Tried to increase progress, but AchievementMan isn't ready!"); - return; - } + if (!IsReady() || !m_pSteamUserStats) + { + g_ConsoleMan.PrintString("ACHIEVEMENTS: Tried to increase progress, but AchievementMan isn't ready!"); + return; + } - bit--; + bit--; - std::string name = achievementName + std::string("_BITFIELD"); - const char *statName = name.c_str(); + std::string name = achievementName + std::string("_BITFIELD"); + const char *statName = name.c_str(); - int current = 0; - m_pSteamUserStats->GetStat(statName, ¤t); + int current = 0; + m_pSteamUserStats->GetStat(statName, ¤t); - int val = current | 1 << bit; + int val = current | 1 << bit; - if (val == ~((~0) << max)) - { - UnlockAchievement(achievementName); - } + if (val == ~((~0) << max)) + { + UnlockAchievement(achievementName); + } - m_pSteamUserStats->SetStat(statName, val); + m_pSteamUserStats->SetStat(statName, val); #endif // STEAM_BUILD } void AchievementMan::Sync() { #ifdef STEAM_BUILD - if (m_pSteamUserStats) - { - m_pSteamUserStats->StoreStats(); - } + if (m_pSteamUserStats) + { + m_pSteamUserStats->StoreStats(); + } #endif // STEAM_BUILD } diff --git a/Source/Managers/AchievementMan.h b/Source/Managers/AchievementMan.h index cfa82fa75a..f298385674 100644 --- a/Source/Managers/AchievementMan.h +++ b/Source/Managers/AchievementMan.h @@ -26,7 +26,7 @@ class AchievementMan : public Singleton ~AchievementMan() { Sync(); }; - int Create(); + int Create(); ////////////////////////////////////////////////////////////////////////////////////////// // Method: IsAchievementUnlocked @@ -66,7 +66,7 @@ class AchievementMan : public Singleton // The maximum progress of this achievement. // Return value: None. - void ProgressAchievement(const char *achievementName, int progress, int max); + void ProgressAchievement(const char *achievementName, int progress, int max); ////////////////////////////////////////////////////////////////////////////////////////// @@ -79,7 +79,7 @@ class AchievementMan : public Singleton // The amount of bits required to unlock this achievement. // Return value: None. - void SetAchievementBit(const char *achievementName, int bit, int max); + void SetAchievementBit(const char *achievementName, int bit, int max); ////////////////////////////////////////////////////////////////////////////////////////// @@ -89,7 +89,7 @@ class AchievementMan : public Singleton // Arguments: None. // Return value: Whether this manager is ready. - bool IsReady() { return m_Initialized; }; + bool IsReady() { return m_Initialized; }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -101,10 +101,10 @@ class AchievementMan : public Singleton void Sync(); - void Destroy() { Sync(); }; + void Destroy() { Sync(); }; #ifdef STEAM_BUILD - STEAM_CALLBACK(AchievementMan, OnUserStatsReceived, UserStatsReceived_t, m_OnUserStatsReceived); + STEAM_CALLBACK(AchievementMan, OnUserStatsReceived, UserStatsReceived_t, m_OnUserStatsReceived); #endif // STEAM_BUILD private: @@ -112,7 +112,7 @@ class AchievementMan : public Singleton #ifdef STEAM_BUILD ISteamUserStats *m_pSteamUserStats; #endif // STEAM_BUILD - bool m_Initialized; + bool m_Initialized; }; diff --git a/Source/Managers/ActivityMan.cpp b/Source/Managers/ActivityMan.cpp index 1da992c284..38ae2edc62 100644 --- a/Source/Managers/ActivityMan.cpp +++ b/Source/Managers/ActivityMan.cpp @@ -32,7 +32,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ActivityMan::Clear() { m_DefaultActivityType = "GATutorial"; @@ -51,7 +51,7 @@ namespace RTE { m_LaunchIntoEditor = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ActivityMan::Initialize() { if (g_NetworkServer.IsServerModeEnabled()) { @@ -66,7 +66,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ActivityMan::ForceAbortSave() { // Just a utility function we can call in the debugger quickwatch window to force an abort save to occur (great for force-saving the game when it crashes) @@ -74,14 +74,14 @@ namespace RTE { return SaveCurrentGame("AbortSave"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::SaveCurrentGame(const std::string &fileName) { + bool ActivityMan::SaveCurrentGame(const std::string& fileName) { m_SaveGameTask.wait(); m_SaveGameTask = BS::multi_future(); - Scene *scene = g_SceneMan.GetScene(); - GAScripted *activity = dynamic_cast(GetActivity()); + Scene* scene = g_SceneMan.GetScene(); + GAScripted* activity = dynamic_cast(GetActivity()); if (!scene || !activity || (activity && activity->GetActivityState() == Activity::ActivityState::Over)) { g_ConsoleMan.PrintString("ERROR: Cannot save when there's no game running, or the game is finished!"); @@ -128,8 +128,8 @@ namespace RTE { // Pull all stuff from MovableMan into the Scene for saving, so existing Actors/ADoors are saved, without transferring ownership, so the game can continue. // This is done after the activity is saved, in case the activity wants to add anything to the scene while saving. modifiableScene->RetrieveSceneObjects(false); - for (SceneObject *objectToSave : *modifiableScene->GetPlacedObjects(Scene::PlacedObjectSets::PLACEONLOAD)) { - if (MovableObject *objectToSaveAsMovableObject = dynamic_cast(objectToSave)) { + for (SceneObject* objectToSave: *modifiableScene->GetPlacedObjects(Scene::PlacedObjectSets::PLACEONLOAD)) { + if (MovableObject* objectToSaveAsMovableObject = dynamic_cast(objectToSave)) { objectToSaveAsMovableObject->OnSave(); } } @@ -140,13 +140,13 @@ namespace RTE { writer->NewPropertyWithValue("Scene", modifiableScene.get()); auto saveWriterData = [&](Writer* writerToSave) { - std::stringstream *stream = static_cast(writerToSave->GetStream()); + std::stringstream* stream = static_cast(writerToSave->GetStream()); stream->flush(); // Ugly copies, but eh. todo - use a string stream that just gives us a raw buffer to grab at std::string streamAsString = stream->str(); - zip_fileinfo zfi = { 0 }; + zip_fileinfo zfi = {0}; const int defaultCompression = 6; zipOpenNewFileInZip(zippedSaveFile, (fileName + ".ini").c_str(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, defaultCompression); @@ -159,7 +159,7 @@ namespace RTE { }; // For some reason I can't std::move a unique ptr in, so just releasing and deleting manually... - m_SaveGameTask.push_back( g_ThreadMan.GetBackgroundThreadPool().submit(saveWriterData, writer.release()) ); + m_SaveGameTask.push_back(g_ThreadMan.GetBackgroundThreadPool().submit(saveWriterData, writer.release())); // We didn't transfer ownership, so we must be very careful that sceneAltered's deletion doesn't touch the stuff we got from MovableMan. modifiableScene->ClearPlacedObjectSet(Scene::PlacedObjectSets::PLACEONLOAD, false); @@ -168,9 +168,9 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::LoadAndLaunchGame(const std::string &fileName) { + bool ActivityMan::LoadAndLaunchGame(const std::string& fileName) { m_SaveGameTask.wait(); std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + fileName + "/Save.ini"; @@ -217,24 +217,26 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::SetStartActivity(Activity *newActivity) { + void ActivityMan::SetStartActivity(Activity* newActivity) { RTEAssert(newActivity, "Trying to replace an activity with a null one!"); m_StartActivity.reset(newActivity); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ActivityMan::SetStartTutorialActivity() { - SetStartActivity(dynamic_cast(g_PresetMan.GetEntityPreset("GATutorial", "Tutorial Mission")->Clone())); - if (GameActivity * gameActivity = dynamic_cast(GetStartActivity())) { gameActivity->SetStartingGold(10000); } + SetStartActivity(dynamic_cast(g_PresetMan.GetEntityPreset("GATutorial", "Tutorial Mission")->Clone())); + if (GameActivity* gameActivity = dynamic_cast(GetStartActivity())) { + gameActivity->SetStartingGold(10000); + } g_SceneMan.SetSceneToLoad("Tutorial Bunker"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::SetStartEditorActivity(const std::string_view &editorToLaunch) { + void ActivityMan::SetStartEditorActivity(const std::string_view& editorToLaunch) { std::unique_ptr editorActivityToStart = nullptr; if (editorToLaunch == "ActorEditor") { @@ -249,7 +251,9 @@ namespace RTE { editorActivityToStart = std::make_unique(); } if (editorActivityToStart) { - if (g_MetaMan.GameInProgress()) { g_MetaMan.EndGame(); } + if (g_MetaMan.GameInProgress()) { + g_MetaMan.EndGame(); + } g_SceneMan.SetSceneToLoad("Editor Scene"); editorActivityToStart->Create(); editorActivityToStart->SetEditorMode(EditorActivity::LOADDIALOG); @@ -260,10 +264,10 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ActivityMan::SetStartEditorActivitySetToLaunchInto() { - std::array validEditorNames = { "ActorEditor", "GibEditor", "SceneEditor", "AreaEditor", "AssemblyEditor" }; + std::array validEditorNames = {"ActorEditor", "GibEditor", "SceneEditor", "AreaEditor", "AssemblyEditor"}; if (std::find(validEditorNames.begin(), validEditorNames.end(), m_EditorToLaunch) != validEditorNames.end()) { // Force mouse + keyboard with default mapping so we won't need to change manually if player 1 is set to keyboard only or gamepad. @@ -279,11 +283,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ActivityMan::SetStartMultiplayerActivity() { if (std::unique_ptr multiplayerGame = std::make_unique()) { - if (g_MetaMan.GameInProgress()) { g_MetaMan.EndGame(); } + if (g_MetaMan.GameInProgress()) { + g_MetaMan.EndGame(); + } g_SceneMan.SetSceneToLoad("Multiplayer Scene"); multiplayerGame->Create(); SetStartActivity(multiplayerGame.release()); @@ -293,7 +299,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ActivityMan::SetStartMultiplayerServerOverview() { g_NetworkServer.Start(); @@ -317,9 +323,9 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ActivityMan::StartActivity(Activity *activity) { + int ActivityMan::StartActivity(Activity* activity) { RTEAssert(activity, "Trying to start a null activity!"); g_ThreadMan.GetPriorityThreadPool().wait_for_tasks(); @@ -329,7 +335,7 @@ namespace RTE { g_AudioMan.StopMusic(); m_StartActivity.reset(activity); - m_Activity.reset(dynamic_cast(m_StartActivity->Clone())); + m_Activity.reset(dynamic_cast(m_StartActivity->Clone())); m_Activity->SetupPlayers(); int error = m_Activity->Start(); @@ -363,12 +369,12 @@ namespace RTE { return error; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ActivityMan::StartActivity(const std::string &className, const std::string &presetName) { - if (const Entity *entity = g_PresetMan.GetEntityPreset(className, presetName)) { - Activity *newActivity = dynamic_cast(entity->Clone()); - if (GameActivity *newActivityAsGameActivity = dynamic_cast(newActivity)) { + int ActivityMan::StartActivity(const std::string& className, const std::string& presetName) { + if (const Entity* entity = g_PresetMan.GetEntityPreset(className, presetName)) { + Activity* newActivity = dynamic_cast(entity->Clone()); + if (GameActivity* newActivityAsGameActivity = dynamic_cast(newActivity)) { newActivityAsGameActivity->SetStartingGold(newActivityAsGameActivity->GetDefaultGoldMediumDifficulty()); if (newActivityAsGameActivity->GetStartingGold() <= 0) { newActivityAsGameActivity->SetStartingGold(static_cast(newActivityAsGameActivity->GetTeamFunds(0))); @@ -383,7 +389,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ActivityMan::PauseActivity(bool pause, bool skipPauseMenu) { if (!m_Activity) { @@ -418,7 +424,7 @@ namespace RTE { g_ConsoleMan.PrintString("SYSTEM: Activity \"" + m_Activity->GetPresetName() + "\" was " + (pause ? "paused" : "resumed")); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ActivityMan::ResumeActivity() { if (GetActivity()->GetActivityState() != Activity::NotStarted) { @@ -431,7 +437,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ActivityMan::RestartActivity() { m_ActivityNeedsRestart = false; @@ -447,7 +453,7 @@ namespace RTE { int activityStarted; if (m_StartActivity) { // Need to pass in a clone of the activity because the original will be deleted and re-set during StartActivity. - Activity *startActivityToUse = dynamic_cast(m_StartActivity->Clone()); + Activity* startActivityToUse = dynamic_cast(m_StartActivity->Clone()); startActivityToUse->SetActivityState(Activity::ActivityState::NotStarted); activityStarted = StartActivity(startActivityToUse); } else { @@ -465,7 +471,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ActivityMan::EndActivity() const { // TODO: Set the activity pointer to nullptr so it doesn't return junk after being destructed. Do it here, or wherever works without crashing. @@ -480,20 +486,21 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ActivityMan::LateUpdateGlobalScripts() const { - if (GAScripted *scriptedActivity = dynamic_cast(m_Activity.get())) { scriptedActivity->UpdateGlobalScripts(true); } + if (GAScripted* scriptedActivity = dynamic_cast(m_Activity.get())) { + scriptedActivity->UpdateGlobalScripts(true); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::Update() - { + void ActivityMan::Update() { g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActivityUpdate); - if (m_Activity) { - m_Activity->Update(); + if (m_Activity) { + m_Activity->Update(); } g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActivityUpdate); - } -} + } +} // namespace RTE diff --git a/Source/Managers/ActivityMan.h b/Source/Managers/ActivityMan.h index d8f4da50ad..9db040229e 100644 --- a/Source/Managers/ActivityMan.h +++ b/Source/Managers/ActivityMan.h @@ -17,7 +17,6 @@ namespace RTE { friend class SettingsMan; public: - #pragma region Creation /// /// Constructor method used to instantiate an ActivityMan object in system memory. Create() should be called before using the object. @@ -48,7 +47,7 @@ namespace RTE { /// Gets the currently active Activity. Won't be what has been set by SetStartActivity unless RestartActivity has been called since. /// /// The currently active Activity. Will be nullptr if no Activity is going. - Activity * GetActivity() const { return m_Activity.get(); } + Activity* GetActivity() const { return m_Activity.get(); } /// /// Gets the async save game task. @@ -102,7 +101,10 @@ namespace RTE { /// Sets the game simulation to be started back up after the current Activity was unpaused. /// /// Whether the game simulation is being resumed from the pause menu. - void SetResumeActivity(bool resumingFromPauseMenu = false) { m_ActivityNeedsResume = true; m_ResumingActivityFromPauseMenu = resumingFromPauseMenu; } + void SetResumeActivity(bool resumingFromPauseMenu = false) { + m_ActivityNeedsResume = true; + m_ResumingActivityFromPauseMenu = resumingFromPauseMenu; + } /// /// Gets whether the pause menu should be skipped when the game simulation is paused. @@ -122,7 +124,7 @@ namespace RTE { /// Sets the type name of the default Activity to be loaded if nothing else is available. /// /// The default Activity type name. - void SetDefaultActivityType(const std::string_view &defaultActivityType) { m_DefaultActivityType = defaultActivityType; } + void SetDefaultActivityType(const std::string_view& defaultActivityType) { m_DefaultActivityType = defaultActivityType; } /// /// Gets the name of the default Activity to be loaded if nothing else is available. @@ -134,7 +136,7 @@ namespace RTE { /// Sets the preset name of the default Activity to be loaded if nothing else is available. /// /// The default Activity preset name. - void SetDefaultActivityName(const std::string_view &defaultActivityName) { m_DefaultActivityName = defaultActivityName; } + void SetDefaultActivityName(const std::string_view& defaultActivityName) { m_DefaultActivityName = defaultActivityName; } /// /// Gets whether the intro and main menu should be skipped on game start and launch directly into the set default Activity instead. @@ -152,7 +154,12 @@ namespace RTE { /// Sets the name of the editor to launch directly into. /// /// - void SetEditorToLaunch(const std::string_view &editorName) { if (!editorName.empty()) { m_EditorToLaunch = editorName; m_LaunchIntoEditor = true; } } + void SetEditorToLaunch(const std::string_view& editorName) { + if (!editorName.empty()) { + m_EditorToLaunch = editorName; + m_LaunchIntoEditor = true; + } + } #pragma endregion #pragma region Saving and Loading @@ -167,14 +174,14 @@ namespace RTE { /// /// Path to the file. /// Whether the game was successfully saved. - bool SaveCurrentGame(const std::string &fileName); + bool SaveCurrentGame(const std::string& fileName); /// /// Loads a saved game, and launches its Scene and Activity. /// /// Path to the file. /// Whether or not the saved game was successfully loaded. - bool LoadAndLaunchGame(const std::string &fileName); + bool LoadAndLaunchGame(const std::string& fileName); #pragma endregion #pragma region Activity Start Handling @@ -184,13 +191,13 @@ namespace RTE { /// Gets the Activity that will be used in the next restart. Ownership is NOT transferred! /// /// The Activity to put into effect next time ResetActivity is called. - Activity * GetStartActivity() const { return m_StartActivity.get(); } + Activity* GetStartActivity() const { return m_StartActivity.get(); } /// /// Sets a new Activity to copy for next restart. You have to use RestartActivity to get it going. Ownership IS transferred! /// /// The new Activity to put into effect next time ResetActivity is called. - void SetStartActivity(Activity *newActivity); + void SetStartActivity(Activity* newActivity); /// /// Loads the "Tutorial Mission" Scene and starts the Tutorial Activity. @@ -201,7 +208,7 @@ namespace RTE { /// Loads "Editor Scene" and starts the given editor Activity. /// /// The editor name to put into effect next time ResetActivity is called. - void SetStartEditorActivity(const std::string_view &editorToLaunch); + void SetStartEditorActivity(const std::string_view& editorToLaunch); /// /// Launch editor Activity specified in command-line argument. @@ -228,7 +235,7 @@ namespace RTE { /// /// The new activity to start. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int StartActivity(Activity *activity); + int StartActivity(Activity* activity); /// /// Officially gets and starts the Activity described. @@ -236,7 +243,7 @@ namespace RTE { /// The class name of the Activity to start. /// The PresetName of the Activity to start. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int StartActivity(const std::string &className, const std::string &presetName); + int StartActivity(const std::string& className, const std::string& presetName); /// /// Pauses/unpauses the game and saving/resuming in-game music if possible, or queuing default music if not. @@ -273,7 +280,6 @@ namespace RTE { #pragma endregion private: - std::string m_DefaultActivityType; //!< The type name of the default Activity to be loaded if nothing else is available. std::string m_DefaultActivityName; //!< The preset name of the default Activity to be loaded if nothing else is available. @@ -301,8 +307,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - ActivityMan(const ActivityMan &reference) = delete; - ActivityMan & operator=(const ActivityMan &rhs) = delete; + ActivityMan(const ActivityMan& reference) = delete; + ActivityMan& operator=(const ActivityMan& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Managers/AudioMan.cpp b/Source/Managers/AudioMan.cpp index 5377f76621..28b26cf31b 100644 --- a/Source/Managers/AudioMan.cpp +++ b/Source/Managers/AudioMan.cpp @@ -12,7 +12,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::Clear() { m_AudioEnabled = false; @@ -30,7 +30,7 @@ namespace RTE { m_SoundPanningEffectStrength = 0.5F; ////////////////////////////////////////////////// - //TODO These need to be removed when our soundscape is sorted out. They're only here temporarily to allow for easier tweaking by pawnis. + // TODO These need to be removed when our soundscape is sorted out. They're only here temporarily to allow for easier tweaking by pawnis. m_ListenerZOffset = 400; m_MinimumDistanceForPanning = 30.0F; ////////////////////////////////////////////////// @@ -47,7 +47,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool AudioMan::Initialize() { FMOD_RESULT audioSystemSetupResult = FMOD::System_Create(&m_AudioSystem); @@ -64,17 +64,16 @@ namespace RTE { audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->setSoftwareChannels(c_MaxSoftwareChannels) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->init(c_MaxVirtualChannels, FMOD_INIT_VOL0_BECOMES_VIRTUAL, 0) : audioSystemSetupResult; - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->getMasterChannelGroup(&m_MasterChannelGroup) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("SFX", &m_SFXChannelGroup) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("UI", &m_UIChannelGroup) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("Music", &m_MusicChannelGroup) : audioSystemSetupResult; // Add a safety limiter to the master channel group - FMOD::DSP *dsp_limiter; + FMOD::DSP* dsp_limiter; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createDSPByType(FMOD_DSP_TYPE_LIMITER, &dsp_limiter) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_MasterChannelGroup->addDSP(0, dsp_limiter) : audioSystemSetupResult; - + // Add a compressor to the SFX channel group // This is pretty heavy-handed, but it sounds great. Might need to be changed once we have sidechaining and fancier things going on. FMOD::DSP* dsp_compressor; @@ -85,7 +84,7 @@ namespace RTE { audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? dsp_compressor->setParameterFloat(3, 250.0f) : audioSystemSetupResult; // Release time audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? dsp_compressor->setParameterFloat(4, 10.0f) : audioSystemSetupResult; // Make-up gain audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_SFXChannelGroup->addDSP(0, dsp_compressor) : audioSystemSetupResult; - + audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_MasterChannelGroup->addGroup(m_SFXChannelGroup) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_MasterChannelGroup->addGroup(m_UIChannelGroup) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_MasterChannelGroup->addGroup(m_MusicChannelGroup) : audioSystemSetupResult; @@ -96,9 +95,15 @@ namespace RTE { return false; } - if (m_MuteSounds) { SetSoundsMuted(); } - if (m_MuteMusic) { SetMusicMuted(); } - if (m_MuteMaster) { SetMasterMuted(); } + if (m_MuteSounds) { + SetSoundsMuted(); + } + if (m_MuteMusic) { + SetMusicMuted(); + } + if (m_MuteMaster) { + SetMasterMuted(); + } SetGlobalPitch(m_GlobalPitch, false, false); SetSoundsVolume(m_SoundsVolume); @@ -108,7 +113,7 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::Destroy() { if (m_AudioEnabled) { @@ -118,7 +123,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::Update() { if (m_AudioEnabled) { @@ -131,27 +136,31 @@ namespace RTE { // Soften the ratio of the pitch adjustment so it's not such an extreme effect on the audio. // TODO: This coefficient should probably move to SettingsMan and be loaded from ini. That way this effect can be lessened or even turned off entirely by users. 0.35 is a good default value though. globalPitch = timeScale + (1.0F - timeScale) * 0.35F; - + SetGlobalPitch(globalPitch); if (!g_ActivityMan.ActivityPaused()) { - const Activity *currentActivity = g_ActivityMan.GetActivity(); + const Activity* currentActivity = g_ActivityMan.GetActivity(); int currentActivityHumanCount = m_IsInMultiplayerMode ? 1 : currentActivity->GetHumanCount(); - if (m_CurrentActivityHumanPlayerPositions.size() != currentActivityHumanCount) { status = status == FMOD_OK ? m_AudioSystem->set3DNumListeners(currentActivityHumanCount) : status; } + if (m_CurrentActivityHumanPlayerPositions.size() != currentActivityHumanCount) { + status = status == FMOD_OK ? m_AudioSystem->set3DNumListeners(currentActivityHumanCount) : status; + } m_CurrentActivityHumanPlayerPositions.clear(); for (int player = Players::PlayerOne; player < Players::MaxPlayerCount && m_CurrentActivityHumanPlayerPositions.size() < currentActivityHumanCount; player++) { if (currentActivity->PlayerActive(player) && currentActivity->PlayerHuman(player)) { int screen = currentActivity->ScreenOfPlayer(player); Vector humanPlayerPosition = g_CameraMan.GetScrollTarget(screen); - if (IsInMultiplayerMode()) { humanPlayerPosition += (Vector(static_cast(g_FrameMan.GetPlayerFrameBufferWidth(screen)), static_cast(g_FrameMan.GetPlayerFrameBufferHeight(screen))) / 2); } + if (IsInMultiplayerMode()) { + humanPlayerPosition += (Vector(static_cast(g_FrameMan.GetPlayerFrameBufferWidth(screen)), static_cast(g_FrameMan.GetPlayerFrameBufferHeight(screen))) / 2); + } m_CurrentActivityHumanPlayerPositions.push_back(std::make_unique(humanPlayerPosition)); } } int listenerNumber = 0; - for (const std::unique_ptr & humanPlayerPosition : m_CurrentActivityHumanPlayerPositions) { + for (const std::unique_ptr& humanPlayerPosition: m_CurrentActivityHumanPlayerPositions) { if (status == FMOD_OK) { FMOD_VECTOR playerPosition = GetAsFMODVector(*(humanPlayerPosition.get()), m_ListenerZOffset); status = m_AudioSystem->set3DListenerAttributes(listenerNumber, &playerPosition, nullptr, &c_FMODForward, &c_FMODUp); @@ -173,79 +182,95 @@ namespace RTE { status = status == FMOD_OK ? m_AudioSystem->update() : status; - if (!IsMusicPlaying() && m_SilenceTimer.IsPastRealTimeLimit()) { PlayNextStream(); } - if (status != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Could not update AudioMan due to FMOD error: " + std::string(FMOD_ErrorString(status))); } + if (!IsMusicPlaying() && m_SilenceTimer.IsPastRealTimeLimit()) { + PlayNextStream(); + } + if (status != FMOD_OK) { + g_ConsoleMan.PrintString("ERROR: Could not update AudioMan due to FMOD error: " + std::string(FMOD_ErrorString(status))); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::SetGlobalPitch(float pitch, bool includeImmobileSounds, bool includeMusic) { if (!m_AudioEnabled) { return; } - if (m_IsInMultiplayerMode) { RegisterSoundEvent(-1, SOUND_SET_GLOBAL_PITCH, nullptr); } + if (m_IsInMultiplayerMode) { + RegisterSoundEvent(-1, SOUND_SET_GLOBAL_PITCH, nullptr); + } m_GlobalPitch = std::clamp(pitch, 0.125F, 8.0F); - if (includeMusic) { m_MusicChannelGroup->setPitch(m_GlobalPitch); } - + if (includeMusic) { + m_MusicChannelGroup->setPitch(m_GlobalPitch); + } + m_SFXChannelGroup->setPitch(m_GlobalPitch); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::SetTempMusicVolume(float volume) { if (m_AudioEnabled && IsMusicPlaying()) { - FMOD::Channel *musicChannel; + FMOD::Channel* musicChannel; FMOD_RESULT result = m_MusicChannelGroup->getChannel(0, &musicChannel); result = (result == FMOD_OK) ? musicChannel->setVolume(std::clamp(volume, 0.0F, 1.0F)) : result; - if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Could not set temporary volume for current music track: " + std::string(FMOD_ErrorString(result))); } + if (result != FMOD_OK) { + g_ConsoleMan.PrintString("ERROR: Could not set temporary volume for current music track: " + std::string(FMOD_ErrorString(result))); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool AudioMan::SetMusicPitch(float pitch) { if (!m_AudioEnabled) { return false; } - if (m_IsInMultiplayerMode) { RegisterMusicEvent(-1, MUSIC_SET_PITCH, 0, 0, 0.0, pitch); } + if (m_IsInMultiplayerMode) { + RegisterMusicEvent(-1, MUSIC_SET_PITCH, 0, 0, 0.0, pitch); + } - pitch = Limit(pitch, 8, 0.125); //Limit pitch change to 8 octaves up or down + pitch = Limit(pitch, 8, 0.125); // Limit pitch change to 8 octaves up or down FMOD_RESULT result = m_MusicChannelGroup->setPitch(pitch); - if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Could not set music pitch: " + std::string(FMOD_ErrorString(result))); } + if (result != FMOD_OK) { + g_ConsoleMan.PrintString("ERROR: Could not set music pitch: " + std::string(FMOD_ErrorString(result))); + } return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float AudioMan::GetMusicPosition() const { if (m_AudioEnabled && IsMusicPlaying()) { FMOD_RESULT result; - FMOD::Channel *musicChannel; + FMOD::Channel* musicChannel; unsigned int position; result = m_MusicChannelGroup->getChannel(0, &musicChannel); result = (result == FMOD_OK) ? musicChannel->getPosition(&position, FMOD_TIMEUNIT_MS) : result; - if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Could not get music position: " + std::string(FMOD_ErrorString(result))); } + if (result != FMOD_OK) { + g_ConsoleMan.PrintString("ERROR: Could not get music position: " + std::string(FMOD_ErrorString(result))); + } return (result == FMOD_OK) ? (static_cast(position)) / 1000.0F : 0; } return 0.0F; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::SetMusicPosition(float position) { if (m_AudioEnabled && IsMusicPlaying()) { - FMOD::Channel *musicChannel; + FMOD::Channel* musicChannel; FMOD_RESULT result = m_MusicChannelGroup->getChannel(0, &musicChannel); - FMOD::Sound *musicSound; + FMOD::Sound* musicSound; result = (result == FMOD_OK) ? musicChannel->getCurrentSound(&musicSound) : result; unsigned int musicLength = 0; @@ -253,16 +278,18 @@ namespace RTE { result = (result == FMOD_OK) ? musicChannel->setPosition(std::clamp(static_cast(position * 1000.0F), 0U, musicLength), FMOD_TIMEUNIT_MS) : result; - if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Could not set music position: " + std::string(FMOD_ErrorString(result))); } + if (result != FMOD_OK) { + g_ConsoleMan.PrintString("ERROR: Could not set music position: " + std::string(FMOD_ErrorString(result))); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::FinishIngameLoopingSounds() { + void AudioMan::FinishIngameLoopingSounds() { if (m_AudioEnabled) { int numberOfPlayingChannels; - FMOD::Channel *soundChannel; + FMOD::Channel* soundChannel; FMOD_RESULT result = m_SFXChannelGroup->getNumChannels(&numberOfPlayingChannels); if (result != FMOD_OK) { @@ -278,13 +305,12 @@ namespace RTE { } soundChannel->setLoopCount(0); } - } - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::PlayMusic(const char *filePath, int loops, float volumeOverrideIfNotMuted) { + void AudioMan::PlayMusic(const char* filePath, int loops, float volumeOverrideIfNotMuted) { if (m_AudioEnabled) { const std::string fullFilePath = g_PresetMan.GetFullModulePath(filePath); if (m_IsInMultiplayerMode) { @@ -304,7 +330,7 @@ namespace RTE { return; } - FMOD::Sound *musicStream; + FMOD::Sound* musicStream; result = m_AudioSystem->createStream(fullFilePath.c_str(), ((loops == 0 || loops == 1) ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL), nullptr, &musicStream); @@ -318,7 +344,7 @@ namespace RTE { g_ConsoleMan.PrintString("ERROR: Failed to set looping for music file: " + fullFilePath + ". This means it will only play 1 time, instead of " + (loops == 0 ? "looping endlessly." : loops + " times.") + std::string(FMOD_ErrorString(result))); } - FMOD::Channel *musicChannel; + FMOD::Channel* musicChannel; result = musicStream->set3DMinMaxDistance(c_SoundMaxAudibleDistance, c_SoundMaxAudibleDistance); result = (result == FMOD_OK) ? m_AudioSystem->playSound(musicStream, m_MusicChannelGroup, true, &musicChannel) : result; if (result != FMOD_OK) { @@ -326,7 +352,9 @@ namespace RTE { return; } result = musicChannel->setPriority(PRIORITY_HIGH); - if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Failed to set music as high priority when playing music file."); } + if (result != FMOD_OK) { + g_ConsoleMan.PrintString("ERROR: Failed to set music as high priority when playing music file."); + } if (volumeOverrideIfNotMuted >= 0.0F && m_MusicVolume > 0.0F) { volumeOverrideIfNotMuted = std::clamp((volumeOverrideIfNotMuted > 1.0F ? volumeOverrideIfNotMuted / 100.0F : volumeOverrideIfNotMuted), 0.0F, 1.0F); @@ -352,7 +380,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::PlayNextStream() { if (m_AudioEnabled && !m_MusicPlayList.empty()) { @@ -368,11 +396,15 @@ namespace RTE { bool isPlaying; FMOD_RESULT result = m_MusicChannelGroup->isPlaying(&isPlaying); if (result == FMOD_OK && isPlaying) { - if (m_IsInMultiplayerMode) { RegisterMusicEvent(-1, MUSIC_SILENCE, nullptr, seconds); } + if (m_IsInMultiplayerMode) { + RegisterMusicEvent(-1, MUSIC_SILENCE, nullptr, seconds); + } result = m_MusicChannelGroup->stop(); } - if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Could not play silence as specified in music queue, when trying to play next stream: " + std::string(FMOD_ErrorString(result))); } - } catch (const std::invalid_argument &) { + if (result != FMOD_OK) { + g_ConsoleMan.PrintString("ERROR: Could not play silence as specified in music queue, when trying to play next stream: " + std::string(FMOD_ErrorString(result))); + } + } catch (const std::invalid_argument&) { g_ConsoleMan.PrintString("ERROR: Could invalid silence specification when trying to play next stream."); } } else { @@ -381,21 +413,25 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::StopMusic() { if (m_AudioEnabled) { - if (m_IsInMultiplayerMode) { RegisterMusicEvent(-1, MUSIC_STOP, 0, 0, 0.0, 0.0); } + if (m_IsInMultiplayerMode) { + RegisterMusicEvent(-1, MUSIC_STOP, 0, 0, 0.0, 0.0); + } FMOD_RESULT result = m_MusicChannelGroup->stop(); - if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Could not stop music: " + std::string(FMOD_ErrorString(result))); } + if (result != FMOD_OK) { + g_ConsoleMan.PrintString("ERROR: Could not stop music: " + std::string(FMOD_ErrorString(result))); + } m_MusicPlayList.clear(); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::QueueMusicStream(const char *filepath) { + void AudioMan::QueueMusicStream(const char* filepath) { if (m_AudioEnabled) { bool isPlaying; FMOD_RESULT result = m_MusicChannelGroup->isPlaying(&isPlaying); @@ -410,14 +446,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SoundContainer *AudioMan::PlaySound(const std::string &filePath, const Vector &position, int player) { + SoundContainer* AudioMan::PlaySound(const std::string& filePath, const Vector& position, int player) { if (m_IsInMultiplayerMode) { return nullptr; } - SoundContainer *newSoundContainer = new SoundContainer(); + SoundContainer* newSoundContainer = new SoundContainer(); newSoundContainer->SetPosition(position); newSoundContainer->GetTopLevelSoundSet().AddSound(filePath); if (newSoundContainer->HasAnySounds()) { @@ -426,25 +462,29 @@ namespace RTE { return newSoundContainer; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::GetMusicEvents(int player, std::list &list) { + void AudioMan::GetMusicEvents(int player, std::list& list) { if (player < 0 || player >= c_MaxClients) { return; } list.clear(); g_SoundEventsListMutex[player].lock(); - for (const NetworkMusicData &musicEvent : m_MusicEvents[player]) { list.push_back(musicEvent); } + for (const NetworkMusicData& musicEvent: m_MusicEvents[player]) { + list.push_back(musicEvent); + } m_MusicEvents[player].clear(); g_SoundEventsListMutex[player].unlock(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::RegisterMusicEvent(int player, NetworkMusicState state, const char *filepath, int loopsOrSilence, float position, float pitch) { + void AudioMan::RegisterMusicEvent(int player, NetworkMusicState state, const char* filepath, int loopsOrSilence, float position, float pitch) { if (player == -1) { - for (int i = 0; i < c_MaxClients; i++) { RegisterMusicEvent(i, state, filepath, loopsOrSilence, position, pitch); } + for (int i = 0; i < c_MaxClients; i++) { + RegisterMusicEvent(i, state, filepath, loopsOrSilence, position, pitch); + } } else { NetworkMusicData musicData; musicData.State = state; @@ -462,11 +502,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::ClearMusicEvents(int player) { if (player == -1 || player >= c_MaxClients) { - for (int i = 0; i < c_MaxClients; i++) { ClearMusicEvents(i); } + for (int i = 0; i < c_MaxClients; i++) { + ClearMusicEvents(i); + } } else { g_SoundEventsListMutex[player].lock(); m_MusicEvents[player].clear(); @@ -474,34 +516,37 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::GetSoundEvents(int player, std::list &list) { + void AudioMan::GetSoundEvents(int player, std::list& list) { if (player < 0 || player >= c_MaxClients) { return; } list.clear(); g_SoundEventsListMutex[player].lock(); - const NetworkSoundData *lastSetGlobalPitchEvent = nullptr; - for (const NetworkSoundData &soundEvent : m_SoundEvents[player]) { + const NetworkSoundData* lastSetGlobalPitchEvent = nullptr; + for (const NetworkSoundData& soundEvent: m_SoundEvents[player]) { if (soundEvent.State == SOUND_SET_GLOBAL_PITCH) { lastSetGlobalPitchEvent = &soundEvent; } else { list.push_back(soundEvent); } } - if (lastSetGlobalPitchEvent) { list.push_back(*lastSetGlobalPitchEvent); } + if (lastSetGlobalPitchEvent) { + list.push_back(*lastSetGlobalPitchEvent); + } m_SoundEvents[player].clear(); g_SoundEventsListMutex[player].unlock(); - } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::RegisterSoundEvent(int player, NetworkSoundState state, const SoundContainer *soundContainer, int fadeoutTime) { + void AudioMan::RegisterSoundEvent(int player, NetworkSoundState state, const SoundContainer* soundContainer, int fadeoutTime) { if (player == -1) { - for (int i = 0; i < c_MaxClients; i++) { RegisterSoundEvent(i, state, soundContainer, fadeoutTime); } + for (int i = 0; i < c_MaxClients; i++) { + RegisterSoundEvent(i, state, soundContainer, fadeoutTime); + } } else { FMOD_RESULT result = FMOD_OK; std::vector soundDataVector; @@ -512,10 +557,10 @@ namespace RTE { soundData.Pitch = m_GlobalPitch; soundDataVector.push_back(soundData); } else { - for (int playingChannel : *soundContainer->GetPlayingChannels()) { - FMOD::Channel *soundChannel; + for (int playingChannel: *soundContainer->GetPlayingChannels()) { + FMOD::Channel* soundChannel; result = m_AudioSystem->getChannel(playingChannel, &soundChannel); - FMOD::Sound *sound; + FMOD::Sound* sound; result = (result == FMOD_OK) ? soundChannel->getCurrentSound(&sound) : result; if (result != FMOD_OK) { @@ -545,11 +590,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::ClearSoundEvents(int player) { if (player == -1 || player >= c_MaxClients) { - for (int i = 0; i < c_MaxClients; i++) { ClearSoundEvents(i); } + for (int i = 0; i < c_MaxClients; i++) { + ClearSoundEvents(i); + } } else { g_SoundEventsListMutex[player].lock(); m_SoundEvents[player].clear(); @@ -557,9 +604,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::PlaySoundContainer(SoundContainer *soundContainer, int player) { + bool AudioMan::PlaySoundContainer(SoundContainer* soundContainer, int player) { if (!m_AudioEnabled || !soundContainer || soundContainer->GetPlayingChannels()->size() >= c_MaxPlayingSoundsPerContainer) { return false; } @@ -574,9 +621,9 @@ namespace RTE { } } - FMOD::ChannelGroup *channelGroupToPlayIn = m_SFXChannelGroup; + FMOD::ChannelGroup* channelGroupToPlayIn = m_SFXChannelGroup; - switch (soundContainer->GetBusRouting()){ + switch (soundContainer->GetBusRouting()) { case SoundContainer::UI: channelGroupToPlayIn = m_UIChannelGroup; break; @@ -588,15 +635,15 @@ namespace RTE { break; } - FMOD::Channel *channel; + FMOD::Channel* channel; int channelIndex; - std::vector selectedSoundData; + std::vector selectedSoundData; soundContainer->GetTopLevelSoundSet().GetFlattenedSoundData(selectedSoundData, true); float pitchVariationFactor = 1.0F + std::abs(soundContainer->GetPitchVariation()); - for (const SoundSet::SoundData *soundData : selectedSoundData) { + for (const SoundSet::SoundData* soundData: selectedSoundData) { result = (result == FMOD_OK) ? m_AudioSystem->playSound(soundData->SoundObject, channelGroupToPlayIn, true, &channel) : result; result = (result == FMOD_OK) ? channel->getIndex(&channelIndex) : result; - + result = (result == FMOD_OK) ? channel->setUserData(soundContainer) : result; result = (result == FMOD_OK) ? channel->setCallback(SoundChannelEndedCallback) : result; result = (result == FMOD_OK) ? channel->setPriority(soundContainer->GetPriority()) : result; @@ -606,19 +653,19 @@ namespace RTE { if (soundContainer->GetCustomPanValue() != 0.0f) { result = (result == FMOD_OK) ? channel->setPan(soundContainer->GetCustomPanValue()) : result; } - + if (soundContainer->IsImmobile()) { result = (result == FMOD_OK) ? channel->setVolume(soundContainer->GetVolume()) : result; } else { - FMOD::DSP *dsp_multibandeq; + FMOD::DSP* dsp_multibandeq; result = (result == FMOD_OK) ? m_AudioSystem->createDSPByType(FMOD_DSP_TYPE_MULTIBAND_EQ, &dsp_multibandeq) : result; result = (result == FMOD_OK) ? dsp_multibandeq->setParameterFloat(1, 22000.0f) : result; // Functionally inactive lowpass filter - result = (result == FMOD_OK) ? channel->addDSP(0, dsp_multibandeq) : result; - + result = (result == FMOD_OK) ? channel->addDSP(0, dsp_multibandeq) : result; + { std::scoped_lock lock(m_SoundChannelMinimumAudibleDistancesMutex); - m_SoundChannelMinimumAudibleDistances.insert({ channelIndex, soundData->MinimumAudibleDistance }); + m_SoundChannelMinimumAudibleDistances.insert({channelIndex, soundData->MinimumAudibleDistance}); } result = (result == FMOD_OK) ? channel->set3DLevel(m_SoundPanningEffectStrength * soundContainer->GetPanningStrengthMultiplier()) : result; @@ -641,8 +688,8 @@ namespace RTE { soundContainer->AddPlayingChannel(channelIndex); } - if (m_IsInMultiplayerMode) { - RegisterSoundEvent(player, SOUND_PLAY, soundContainer); + if (m_IsInMultiplayerMode) { + RegisterSoundEvent(player, SOUND_PLAY, soundContainer); } // Choose the sounds for next time @@ -652,23 +699,25 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::ChangeSoundContainerPlayingChannelsPosition(const SoundContainer *soundContainer) { + bool AudioMan::ChangeSoundContainerPlayingChannelsPosition(const SoundContainer* soundContainer) { if (!m_AudioEnabled || !soundContainer) { return false; } - if (m_IsInMultiplayerMode) { RegisterSoundEvent(-1, SOUND_SET_POSITION, soundContainer); } + if (m_IsInMultiplayerMode) { + RegisterSoundEvent(-1, SOUND_SET_POSITION, soundContainer); + } FMOD_RESULT result = FMOD_OK; - FMOD::Channel *soundChannel; - FMOD::Sound *sound; + FMOD::Channel* soundChannel; + FMOD::Sound* sound; - const std::unordered_set *playingChannels = soundContainer->GetPlayingChannels(); - for (int channelIndex : *playingChannels) { + const std::unordered_set* playingChannels = soundContainer->GetPlayingChannels(); + for (int channelIndex: *playingChannels) { result = m_AudioSystem->getChannel(channelIndex, &soundChannel); result = (result == FMOD_OK) ? soundChannel->getCurrentSound(&sound) : result; - const SoundSet::SoundData *soundData = soundContainer->GetSoundDataForSound(sound); + const SoundSet::SoundData* soundData = soundContainer->GetSoundDataForSound(sound); FMOD_VECTOR soundPosition = GetAsFMODVector(soundContainer->GetPosition() + ((soundData == nullptr) ? Vector() : soundData->Offset)); result = (result == FMOD_OK) ? UpdatePositionalEffectsForSoundChannel(soundChannel, &soundPosition) : result; @@ -679,21 +728,23 @@ namespace RTE { return result == FMOD_OK; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::ChangeSoundContainerPlayingChannelsVolume(const SoundContainer *soundContainer, float newVolume) { + bool AudioMan::ChangeSoundContainerPlayingChannelsVolume(const SoundContainer* soundContainer, float newVolume) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return false; } - if (m_IsInMultiplayerMode) { RegisterSoundEvent(-1, SOUND_SET_VOLUME, soundContainer); } + if (m_IsInMultiplayerMode) { + RegisterSoundEvent(-1, SOUND_SET_VOLUME, soundContainer); + } FMOD_RESULT result = FMOD_OK; - FMOD::Channel *soundChannel; + FMOD::Channel* soundChannel; float soundContainerOldVolume = soundContainer->GetVolume() == 0 ? 1.0F : soundContainer->GetVolume(); float soundChannelCurrentVolume; - const std::unordered_set *playingChannels = soundContainer->GetPlayingChannels(); - for (int channelIndex : *playingChannels) { + const std::unordered_set* playingChannels = soundContainer->GetPlayingChannels(); + for (int channelIndex: *playingChannels) { result = m_AudioSystem->getChannel(channelIndex, &soundChannel); result = result == FMOD_OK ? soundChannel->getVolume(&soundChannelCurrentVolume) : result; @@ -711,19 +762,21 @@ namespace RTE { return result == FMOD_OK; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::ChangeSoundContainerPlayingChannelsPitch(const SoundContainer *soundContainer) { + bool AudioMan::ChangeSoundContainerPlayingChannelsPitch(const SoundContainer* soundContainer) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return false; } - if (m_IsInMultiplayerMode) { RegisterSoundEvent(-1, SOUND_SET_PITCH, soundContainer); } + if (m_IsInMultiplayerMode) { + RegisterSoundEvent(-1, SOUND_SET_PITCH, soundContainer); + } FMOD_RESULT result = FMOD_OK; - FMOD::Channel *soundChannel; + FMOD::Channel* soundChannel; - const std::unordered_set *playingChannels = soundContainer->GetPlayingChannels(); - for (int channelIndex : *playingChannels) { + const std::unordered_set* playingChannels = soundContainer->GetPlayingChannels(); + for (int channelIndex: *playingChannels) { result = m_AudioSystem->getChannel(channelIndex, &soundChannel); result = result == FMOD_OK ? soundChannel->setPitch(soundContainer->GetPitch()) : result; if (result != FMOD_OK) { @@ -735,17 +788,19 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::ChangeSoundContainerPlayingChannelsCustomPanValue(const SoundContainer *soundContainer) { + bool AudioMan::ChangeSoundContainerPlayingChannelsCustomPanValue(const SoundContainer* soundContainer) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return false; } - if (m_IsInMultiplayerMode) { RegisterSoundEvent(-1, SOUND_SET_PITCH, soundContainer); } + if (m_IsInMultiplayerMode) { + RegisterSoundEvent(-1, SOUND_SET_PITCH, soundContainer); + } FMOD_RESULT result = FMOD_OK; - FMOD::Channel *soundChannel; + FMOD::Channel* soundChannel; - const std::unordered_set *playingChannels = soundContainer->GetPlayingChannels(); - for (int channelIndex : *playingChannels) { + const std::unordered_set* playingChannels = soundContainer->GetPlayingChannels(); + for (int channelIndex: *playingChannels) { result = m_AudioSystem->getChannel(channelIndex, &soundChannel); result = result == FMOD_OK ? soundChannel->setPan(soundContainer->GetCustomPanValue()) : result; if (result != FMOD_OK) { @@ -754,62 +809,70 @@ namespace RTE { } return result == FMOD_OK; } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::StopSoundContainerPlayingChannels(SoundContainer *soundContainer, int player) { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool AudioMan::StopSoundContainerPlayingChannels(SoundContainer* soundContainer, int player) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return false; } - if (m_IsInMultiplayerMode) { RegisterSoundEvent(player, SOUND_STOP, soundContainer); } + if (m_IsInMultiplayerMode) { + RegisterSoundEvent(player, SOUND_STOP, soundContainer); + } FMOD_RESULT result; - FMOD::Channel *soundChannel; + FMOD::Channel* soundChannel; - const std::unordered_set *channels = soundContainer->GetPlayingChannels(); + const std::unordered_set* channels = soundContainer->GetPlayingChannels(); for (std::unordered_set::const_iterator channelIterator = channels->begin(); channelIterator != channels->end();) { result = m_AudioSystem->getChannel((*channelIterator), &soundChannel); ++channelIterator; // NOTE - stopping the sound will remove the channel, screwing things up if we don't move to the next iterator preemptively result = (result == FMOD_OK) ? soundChannel->stop() : result; - if (result != FMOD_OK) { g_ConsoleMan.PrintString("Error: Failed to stop playing channel in SoundContainer " + soundContainer->GetPresetName() + ": " + std::string(FMOD_ErrorString(result))); } + if (result != FMOD_OK) { + g_ConsoleMan.PrintString("Error: Failed to stop playing channel in SoundContainer " + soundContainer->GetPresetName() + ": " + std::string(FMOD_ErrorString(result))); + } } return result == FMOD_OK; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::FadeOutSoundContainerPlayingChannels(SoundContainer *soundContainer, int fadeOutTime) { + void AudioMan::FadeOutSoundContainerPlayingChannels(SoundContainer* soundContainer, int fadeOutTime) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return; } - if (m_IsInMultiplayerMode) { RegisterSoundEvent(-1, SOUND_FADE_OUT, soundContainer, fadeOutTime); } + if (m_IsInMultiplayerMode) { + RegisterSoundEvent(-1, SOUND_FADE_OUT, soundContainer, fadeOutTime); + } int sampleRate; m_AudioSystem->getSoftwareFormat(&sampleRate, nullptr, nullptr); int fadeOutTimeAsSamples = fadeOutTime * sampleRate / 1000; FMOD_RESULT result; - FMOD::Channel *soundChannel; + FMOD::Channel* soundChannel; unsigned long long parentClock; float currentVolume; const std::unordered_set channels = *soundContainer->GetPlayingChannels(); - for (int channel : channels) { + for (int channel: channels) { result = m_AudioSystem->getChannel(channel, &soundChannel); result = (result == FMOD_OK) ? soundChannel->getDSPClock(nullptr, &parentClock) : result; result = (result == FMOD_OK) ? soundChannel->getVolume(¤tVolume) : result; result = (result == FMOD_OK) ? soundChannel->addFadePoint(parentClock, currentVolume) : result; result = (result == FMOD_OK) ? soundChannel->addFadePoint(parentClock + fadeOutTimeAsSamples, 0) : result; - if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Could not fade out sounds in SoundContainer " + soundContainer->GetPresetName() + ": " + std::string(FMOD_ErrorString(result))); } + if (result != FMOD_OK) { + g_ConsoleMan.PrintString("ERROR: Could not fade out sounds in SoundContainer " + soundContainer->GetPresetName() + ": " + std::string(FMOD_ErrorString(result))); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AudioMan::Update3DEffectsForSFXChannels() { int numberOfPlayingChannels; - FMOD::Channel *soundChannel; + FMOD::Channel* soundChannel; FMOD_RESULT result = m_SFXChannelGroup->getNumChannels(&numberOfPlayingChannels); if (result != FMOD_OK) { @@ -822,7 +885,7 @@ namespace RTE { FMOD_MODE mode; result = (result == FMOD_OK) ? soundChannel->getMode(&mode) : result; unsigned modeResult = mode & FMOD_2D; - if (modeResult == 0){ + if (modeResult == 0) { FMOD_VECTOR channelPosition; result = result == FMOD_OK ? soundChannel->get3DAttributes(&channelPosition, nullptr) : result; result = result == FMOD_OK ? UpdatePositionalEffectsForSoundChannel(soundChannel, &channelPosition) : result; @@ -831,9 +894,9 @@ namespace RTE { if (result == FMOD_OK && m_CurrentActivityHumanPlayerPositions.size() == 1) { float sqrDistanceToPlayer = (*(m_CurrentActivityHumanPlayerPositions[0].get()) - GetAsVector(channelPosition)).GetSqrMagnitude(); float doubleMinimumDistanceForPanning = m_MinimumDistanceForPanning * 2.0F; - void *userData; + void* userData; result = result == FMOD_OK ? soundChannel->getUserData(&userData) : result; - const SoundContainer *soundContainer = static_cast(userData); + const SoundContainer* soundContainer = static_cast(userData); if (sqrDistanceToPlayer < (m_MinimumDistanceForPanning * m_MinimumDistanceForPanning) || soundContainer->GetCustomPanValue() != 0.0f) { soundChannel->set3DLevel(0); } else if (sqrDistanceToPlayer < (doubleMinimumDistanceForPanning * doubleMinimumDistanceForPanning)) { @@ -843,7 +906,7 @@ namespace RTE { } } } - + if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: An error occurred updating calculated sound effects for playing channel with index " + std::to_string(i) + ": " + std::string(FMOD_ErrorString(result))); continue; @@ -851,22 +914,22 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD_RESULT AudioMan::UpdatePositionalEffectsForSoundChannel(FMOD::Channel *soundChannel, const FMOD_VECTOR *positionOverride) const { + FMOD_RESULT AudioMan::UpdatePositionalEffectsForSoundChannel(FMOD::Channel* soundChannel, const FMOD_VECTOR* positionOverride) const { FMOD_RESULT result = FMOD_OK; - - void *userData; + + void* userData; result = result == FMOD_OK ? soundChannel->getUserData(&userData) : result; - const SoundContainer *channelSoundContainer = static_cast(userData); - + const SoundContainer* channelSoundContainer = static_cast(userData); + bool sceneWraps = g_SceneMan.SceneWrapsX(); FMOD_VECTOR channelPosition; if (positionOverride) { channelPosition = *positionOverride; } else if (sceneWraps) { - //NOTE If the scene doesn't wrap and the position hasn't changed, this method doesn't set the channel position below, so there's no need to get it here. + // NOTE If the scene doesn't wrap and the position hasn't changed, this method doesn't set the channel position below, so there's no need to get it here. result = soundChannel->get3DAttributes(&channelPosition, nullptr); if (result != FMOD_OK) { return result; @@ -878,21 +941,21 @@ namespace RTE { if (!sceneWraps) { wrappedChannelPositions = {channelPosition}; } else { - wrappedChannelPositions = (channelPosition.x <= halfSceneWidth) ? - wrappedChannelPositions = {channelPosition, {channelPosition.x + g_SceneMan.GetSceneWidth(), channelPosition.y}} : - wrappedChannelPositions = {FMOD_VECTOR({channelPosition.x - g_SceneMan.GetSceneWidth(), channelPosition.y}), channelPosition}; + wrappedChannelPositions = (channelPosition.x <= halfSceneWidth) ? wrappedChannelPositions = {channelPosition, {channelPosition.x + g_SceneMan.GetSceneWidth(), channelPosition.y}} : wrappedChannelPositions = {FMOD_VECTOR({channelPosition.x - g_SceneMan.GetSceneWidth(), channelPosition.y}), channelPosition}; } float sqrShortestDistance = c_SoundMaxAudibleDistance * c_SoundMaxAudibleDistance; float sqrLongestDistance = 0.0F; - for (const std::unique_ptr & humanPlayerPosition : m_CurrentActivityHumanPlayerPositions) { - for (const FMOD_VECTOR &wrappedChannelPosition : wrappedChannelPositions) { + for (const std::unique_ptr& humanPlayerPosition: m_CurrentActivityHumanPlayerPositions) { + for (const FMOD_VECTOR& wrappedChannelPosition: wrappedChannelPositions) { float sqrDistanceToChannelPosition = (*(humanPlayerPosition.get()) - GetAsVector(wrappedChannelPosition)).GetSqrMagnitude(); if (sqrDistanceToChannelPosition < sqrShortestDistance) { sqrShortestDistance = sqrDistanceToChannelPosition; channelPosition = wrappedChannelPosition; } - if (sqrDistanceToChannelPosition > sqrLongestDistance) { sqrLongestDistance = sqrDistanceToChannelPosition; } + if (sqrDistanceToChannelPosition > sqrLongestDistance) { + sqrLongestDistance = sqrDistanceToChannelPosition; + } if (!sceneWraps) { break; } @@ -906,21 +969,21 @@ namespace RTE { float attenuationStartDistance = c_DefaultAttenuationStartDistance; float soundMaxDistance = 0.0F; result = result == FMOD_OK ? soundChannel->get3DMinMaxDistance(&attenuationStartDistance, &soundMaxDistance) : result; - + float attenuatedVolume = (shortestDistance <= attenuationStartDistance) ? 1.0F : attenuationStartDistance / shortestDistance; - + // Lowpass as distance increases - FMOD::DSP *dsp_multibandeq; + FMOD::DSP* dsp_multibandeq; result = (result == FMOD_OK) ? soundChannel->getDSP(0, &dsp_multibandeq) : result; float factor = 1 - pow(1 - attenuatedVolume, 3); float lowpassFrequency = 22000.0f * factor; lowpassFrequency = std::clamp(lowpassFrequency, 350.0f, 22000.0f); result = (result == FMOD_OK) ? dsp_multibandeq->setParameterFloat(1, lowpassFrequency) : result; - + if (channelSoundContainer->GetCustomPanValue() != 0.0f) { result = (result == FMOD_OK) ? soundChannel->setPan(channelSoundContainer->GetCustomPanValue()) : result; } - + float minimumAudibleDistance = m_SoundChannelMinimumAudibleDistances.at(soundChannelIndex); if (shortestDistance >= soundMaxDistance) { attenuatedVolume = 0.0F; @@ -929,7 +992,7 @@ namespace RTE { } else if (sqrLongestDistance < (minimumAudibleDistance * minimumAudibleDistance)) { attenuatedVolume = 0.0F; } - + float panLevel; result = result == FMOD_OK ? soundChannel->get3DLevel(&panLevel) : result; if (result == FMOD_OK && (panLevel < 1.0F || attenuatedVolume == 0.0F)) { @@ -941,34 +1004,40 @@ namespace RTE { return result; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD_RESULT F_CALLBACK AudioMan::MusicChannelEndedCallback(FMOD_CHANNELCONTROL *channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void *unusedCommandData1, void *unusedCommandData2) { + FMOD_RESULT F_CALLBACK AudioMan::MusicChannelEndedCallback(FMOD_CHANNELCONTROL* channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void* unusedCommandData1, void* unusedCommandData2) { if (channelControlType == FMOD_CHANNELCONTROL_CHANNEL && callbackType == FMOD_CHANNELCONTROL_CALLBACK_END) { - void *userData; + void* userData; FMOD_RESULT result = g_AudioMan.m_MusicChannelGroup->getUserData(&userData); - if (userData == nullptr) { g_AudioMan.PlayNextStream(); } + if (userData == nullptr) { + g_AudioMan.PlayNextStream(); + } } return FMOD_OK; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD_RESULT F_CALLBACK AudioMan::SoundChannelEndedCallback(FMOD_CHANNELCONTROL *channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void *unusedCommandData1, void *unusedCommandData2) { + FMOD_RESULT F_CALLBACK AudioMan::SoundChannelEndedCallback(FMOD_CHANNELCONTROL* channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void* unusedCommandData1, void* unusedCommandData2) { if (channelControlType == FMOD_CHANNELCONTROL_CHANNEL && callbackType == FMOD_CHANNELCONTROL_CALLBACK_END) { - FMOD::Channel *channel = reinterpret_cast(channelControl); + FMOD::Channel* channel = reinterpret_cast(channelControl); int channelIndex; FMOD_RESULT result = channel->getIndex(&channelIndex); // Remove this playing sound index from the SoundContainer if it has any playing sounds, i.e. it hasn't been reset before this callback happened. - void *userData; + void* userData; result = (result == FMOD_OK) ? channel->getUserData(&userData) : result; if (result == FMOD_OK) { - SoundContainer *channelSoundContainer = static_cast(userData); - if (channelSoundContainer->IsBeingPlayed()) { channelSoundContainer->RemovePlayingChannel(channelIndex); } + SoundContainer* channelSoundContainer = static_cast(userData); + if (channelSoundContainer->IsBeingPlayed()) { + channelSoundContainer->RemovePlayingChannel(channelIndex); + } result = (result == FMOD_OK) ? channel->setUserData(nullptr) : result; - if (g_AudioMan.m_SoundChannelMinimumAudibleDistances.find(channelIndex) != g_AudioMan.m_SoundChannelMinimumAudibleDistances.end()) { g_AudioMan.m_SoundChannelMinimumAudibleDistances.erase(channelIndex); } + if (g_AudioMan.m_SoundChannelMinimumAudibleDistances.find(channelIndex) != g_AudioMan.m_SoundChannelMinimumAudibleDistances.end()) { + g_AudioMan.m_SoundChannelMinimumAudibleDistances.erase(channelIndex); + } if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: An error occurred when Ending a sound in SoundContainer " + channelSoundContainer->GetPresetName() + ": " + std::string(FMOD_ErrorString(result))); @@ -981,17 +1050,17 @@ namespace RTE { return FMOD_OK; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD_VECTOR AudioMan::GetAsFMODVector(const Vector &vector, float zValue) const { + FMOD_VECTOR AudioMan::GetAsFMODVector(const Vector& vector, float zValue) const { Vector sceneDimensions = g_SceneMan.GetScene() ? g_SceneMan.GetSceneDim() : Vector(); return sceneDimensions.IsZero() ? FMOD_VECTOR{0, 0, zValue} : FMOD_VECTOR{vector.m_X, sceneDimensions.m_Y - vector.m_Y, zValue}; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector AudioMan::GetAsVector(FMOD_VECTOR fmodVector) const { Vector sceneDimensions = g_SceneMan.GetScene() ? g_SceneMan.GetSceneDim() : Vector(); return sceneDimensions.IsZero() ? Vector() : Vector(fmodVector.x, sceneDimensions.m_Y - fmodVector.y); } -} +} // namespace RTE diff --git a/Source/Managers/AudioMan.h b/Source/Managers/AudioMan.h index f606d1e1f4..d112319832 100644 --- a/Source/Managers/AudioMan.h +++ b/Source/Managers/AudioMan.h @@ -24,7 +24,6 @@ namespace RTE { friend class SoundContainer; public: - /// /// Hardcoded playback priorities for sounds. Note that sounds don't have to use these specifically; their priority can be anywhere between high and low. /// @@ -128,7 +127,7 @@ namespace RTE { /// Gets the audio management system object used for playing all audio. /// /// The audio management system object used by AudioMan for playing audio. - FMOD::System *GetAudioSystem() const { return m_AudioSystem; } + FMOD::System* GetAudioSystem() const { return m_AudioSystem; } /// /// Reports whether audio is enabled. @@ -142,7 +141,7 @@ namespace RTE { /// The out-parameter that will hold the virtual channel count. /// The out-parameter that will hold the real channel count. /// Whether or not the playing channel count was succesfully gotten. - bool GetPlayingChannelCount(int *outVirtualChannelCount, int *outRealChannelCount) const { return m_AudioSystem->getChannelsPlaying(outVirtualChannelCount, outRealChannelCount) == FMOD_OK; } + bool GetPlayingChannelCount(int* outVirtualChannelCount, int* outRealChannelCount) const { return m_AudioSystem->getChannelsPlaying(outVirtualChannelCount, outRealChannelCount) == FMOD_OK; } /// /// Returns the total number of virtual audio channels available. @@ -154,7 +153,10 @@ namespace RTE { /// Returns the total number of real audio channels available. /// /// The number of real audio channels available. - int GetTotalRealChannelCount() const { int channelCount; return m_AudioSystem->getSoftwareChannels(&channelCount) == FMOD_OK ? channelCount : 0; } + int GetTotalRealChannelCount() const { + int channelCount; + return m_AudioSystem->getSoftwareChannels(&channelCount) == FMOD_OK ? channelCount : 0; + } /// /// Gets whether all audio is muted or not. @@ -166,7 +168,12 @@ namespace RTE { /// Mutes or unmutes all audio. /// /// Whether to mute or unmute all the audio. - void SetMasterMuted(bool muteOrUnmute = true) { m_MuteMaster = muteOrUnmute; if (m_AudioEnabled) { m_MasterChannelGroup->setMute(m_MuteMaster); } } + void SetMasterMuted(bool muteOrUnmute = true) { + m_MuteMaster = muteOrUnmute; + if (m_AudioEnabled) { + m_MasterChannelGroup->setMute(m_MuteMaster); + } + } /// /// Gets the volume of all audio. Does not get music or sounds individual volumes. @@ -178,7 +185,12 @@ namespace RTE { /// Sets all the audio to a specific volume. Does not affect music or sounds individual volumes. /// /// The desired volume scalar. 0.0-1.0. - void SetMasterVolume(float volume = 1.0F) { m_MasterVolume = std::clamp(volume, 0.0F, 1.0F); if (m_AudioEnabled) { m_MasterChannelGroup->setVolume(m_MasterVolume); } } + void SetMasterVolume(float volume = 1.0F) { + m_MasterVolume = std::clamp(volume, 0.0F, 1.0F); + if (m_AudioEnabled) { + m_MasterChannelGroup->setVolume(m_MasterVolume); + } + } /// /// Gets the global pitch scalar value for all sounds and music. @@ -206,7 +218,10 @@ namespace RTE { /// Reports whether any music stream is currently playing. /// /// Whether any music stream is currently playing. - bool IsMusicPlaying() const { bool isPlayingMusic; return m_AudioEnabled && m_MusicChannelGroup->isPlaying(&isPlayingMusic) == FMOD_OK ? isPlayingMusic : false; } + bool IsMusicPlaying() const { + bool isPlayingMusic; + return m_AudioEnabled && m_MusicChannelGroup->isPlaying(&isPlayingMusic) == FMOD_OK ? isPlayingMusic : false; + } /// /// Gets whether the music channel is muted or not. @@ -218,7 +233,12 @@ namespace RTE { /// Mutes or unmutes the music channel. /// /// Whether to mute or unmute the music channel. - void SetMusicMuted(bool muteOrUnmute = true) { m_MuteMusic = muteOrUnmute; if (m_AudioEnabled) { m_MusicChannelGroup->setMute(m_MuteMusic); } } + void SetMusicMuted(bool muteOrUnmute = true) { + m_MuteMusic = muteOrUnmute; + if (m_AudioEnabled) { + m_MusicChannelGroup->setMute(m_MuteMusic); + } + } /// /// Gets the volume of music. Does not get volume of sounds. @@ -230,7 +250,12 @@ namespace RTE { /// Sets the music to a specific volume. Does not affect sounds. /// /// The desired volume scalar. 0.0-1.0. - void SetMusicVolume(float volume = 1.0F) { m_MusicVolume = std::clamp(volume, 0.0F, 1.0F); if (m_AudioEnabled) { m_MusicChannelGroup->setVolume(m_MusicVolume); } } + void SetMusicVolume(float volume = 1.0F) { + m_MusicVolume = std::clamp(volume, 0.0F, 1.0F); + if (m_AudioEnabled) { + m_MusicChannelGroup->setVolume(m_MusicVolume); + } + } /// /// Sets the music to a specific volume, but it will only last until a new song is played. Useful for fading etc. @@ -275,12 +300,14 @@ namespace RTE { /// Mutes or unmutes all the sound effects channels. /// /// Whether to mute or unmute all the sound effects channels. - void SetSoundsMuted(bool muteOrUnmute = true) { m_MuteSounds = muteOrUnmute; if (m_AudioEnabled) - { - // TODO: We may or may not wanna separate this out and add a UI sound slider - m_SFXChannelGroup->setMute(m_MuteSounds); - m_UIChannelGroup->setMute(m_MuteSounds); - } } + void SetSoundsMuted(bool muteOrUnmute = true) { + m_MuteSounds = muteOrUnmute; + if (m_AudioEnabled) { + // TODO: We may or may not wanna separate this out and add a UI sound slider + m_SFXChannelGroup->setMute(m_MuteSounds); + m_UIChannelGroup->setMute(m_MuteSounds); + } + } /// /// Gets the volume of all sounds. Does not get volume of music. @@ -292,18 +319,25 @@ namespace RTE { /// Sets the volume of all sounds to a specific volume. Does not affect music. /// /// The desired volume scalar. 0.0-1.0. - void SetSoundsVolume(float volume = 1.0F) { m_SoundsVolume = volume; if (m_AudioEnabled) - { - m_SFXChannelGroup->setVolume(m_SoundsVolume); - m_UIChannelGroup->setVolume(m_SoundsVolume); - } } + void SetSoundsVolume(float volume = 1.0F) { + m_SoundsVolume = volume; + if (m_AudioEnabled) { + m_SFXChannelGroup->setVolume(m_SoundsVolume); + m_UIChannelGroup->setVolume(m_SoundsVolume); + } + } #pragma endregion #pragma region Global Playback and Handling /// /// Stops all playback and clears the music playlist. /// - void StopAll() { if (m_AudioEnabled) { m_MasterChannelGroup->stop(); } m_MusicPlayList.clear(); } + void StopAll() { + if (m_AudioEnabled) { + m_MasterChannelGroup->stop(); + } + m_MusicPlayList.clear(); + } /// /// Makes all sounds that are looping stop looping, allowing them to play once more then be finished. @@ -314,7 +348,11 @@ namespace RTE { /// Pauses all ingame sounds. /// Whether to pause sounds or resume them. /// - void PauseIngameSounds(bool pause = true) { if (m_AudioEnabled) { m_SFXChannelGroup->setPaused(pause); } } + void PauseIngameSounds(bool pause = true) { + if (m_AudioEnabled) { + m_SFXChannelGroup->setPaused(pause); + } + } #pragma endregion #pragma region Music Playback and Handling @@ -324,7 +362,7 @@ namespace RTE { /// The path to the music file to play. /// The number of times to loop the song. 0 means play once. -1 means play infinitely until stopped. /// The volume override for music for this song only, if volume is not muted. < 0 means no override. - void PlayMusic(const char *filePath, int loops = -1, float volumeOverrideIfNotMuted = -1.0F); + void PlayMusic(const char* filePath, int loops = -1, float volumeOverrideIfNotMuted = -1.0F); /// /// Plays the next music stream in the queue, if any is queued. @@ -337,17 +375,21 @@ namespace RTE { void StopMusic(); /// - /// Queues up another path to a stream that will be played after the current one is done. + /// Queues up another path to a stream that will be played after the current one is done. /// Can be done several times to queue up many tracks. The last track in the queue will be looped infinitely. /// /// The path to the music file to play after the current one. - void QueueMusicStream(const char *filePath); + void QueueMusicStream(const char* filePath); /// /// Queues up a period of silence in the music stream playlist. /// /// The number of secs to wait before going to the next stream. - void QueueSilence(int seconds) { if (m_AudioEnabled && seconds > 0) { m_MusicPlayList.push_back("@" + std::to_string(seconds)); } } + void QueueSilence(int seconds) { + if (m_AudioEnabled && seconds > 0) { + m_MusicPlayList.push_back("@" + std::to_string(seconds)); + } + } /// /// Clears the music queue. @@ -361,14 +403,14 @@ namespace RTE { /// /// The path to the sound file to play. /// The new SoundContainer being played. OWNERSHIP IS TRANSFERRED! - SoundContainer *PlaySound(const std::string &filePath) { return PlaySound(filePath, Vector(), -1); } + SoundContainer* PlaySound(const std::string& filePath) { return PlaySound(filePath, Vector(), -1); } /// /// Starts playing a certain sound file at a certain position for all players. /// /// The path to the sound file to play. /// The new SoundContainer being played. OWNERSHIP IS TRANSFERRED! - SoundContainer *PlaySound(const std::string &filePath, const Vector &position) { return PlaySound(filePath, position, -1); } + SoundContainer* PlaySound(const std::string& filePath, const Vector& position) { return PlaySound(filePath, position, -1); } /// /// Starts playing a certain sound file at a certain position for a certain player. @@ -377,7 +419,7 @@ namespace RTE { /// The position at which to play the SoundContainer's sounds. /// Which player to play the SoundContainer's sounds for, -1 means all players. /// The new SoundContainer being played. OWNERSHIP IS TRANSFERRED! - SoundContainer *PlaySound(const std::string &filePath, const Vector &position, int player); + SoundContainer* PlaySound(const std::string& filePath, const Vector& position, int player); #pragma endregion #pragma region Network Audio Handling @@ -398,7 +440,7 @@ namespace RTE { /// /// Player to get events for. /// List with events for this player. - void GetMusicEvents(int player, std::list &list); + void GetMusicEvents(int player, std::list& list); /// /// Adds the music event to internal list of music events for the specified player. @@ -409,7 +451,7 @@ namespace RTE { /// LoopsOrSilence counter or, if state is silence, the length of the silence. /// Music playback position. /// Pitch value. - void RegisterMusicEvent(int player, NetworkMusicState state, const char *filepath, int loopsOrSilence = 0, float position = 0, float pitch = 1.0F); + void RegisterMusicEvent(int player, NetworkMusicState state, const char* filepath, int loopsOrSilence = 0, float position = 0, float pitch = 1.0F); /// /// Clears the list of current Music events for the target player. @@ -422,7 +464,7 @@ namespace RTE { /// /// Player to get events for. /// List with events for this player. - void GetSoundEvents(int player, std::list &list); + void GetSoundEvents(int player, std::list& list); /// /// Adds the sound event to the internal list of sound events for the specified player. @@ -431,7 +473,7 @@ namespace RTE { /// NetworkSoundState for the event. /// A pointer to the SoundContainer this event is happening to, or a null pointer for global events. /// THe amount of time, in MS, to fade out over. This data isn't contained in SoundContainer, so it needs to be passed in separately. - void RegisterSoundEvent(int player, NetworkSoundState state, const SoundContainer *soundContainer, int fadeOutTime = 0); + void RegisterSoundEvent(int player, NetworkSoundState state, const SoundContainer* soundContainer, int fadeOutTime = 0); /// /// Clears the list of current Sound events for the target player. @@ -441,15 +483,14 @@ namespace RTE { #pragma endregion protected: - const FMOD_VECTOR c_FMODForward = FMOD_VECTOR{0, 0, 1}; //!< An FMOD_VECTOR defining the Forwards direction. Necessary for 3D Sounds. const FMOD_VECTOR c_FMODUp = FMOD_VECTOR{0, 1, 0}; //!< An FMOD_VECTOR defining the Up direction. Necessary for 3D Sounds. - FMOD::System *m_AudioSystem; //!< The FMOD Sound management object. - FMOD::ChannelGroup *m_MasterChannelGroup; //!< The top-level FMOD ChannelGroup that holds everything. - FMOD::ChannelGroup *m_SFXChannelGroup; //!< The FMOD ChannelGroup for diegetic gameplay sounds. - FMOD::ChannelGroup *m_UIChannelGroup; //!< The FMOD ChannelGroup for UI sounds. - FMOD::ChannelGroup *m_MusicChannelGroup; //!< The FMOD ChannelGroup for music. + FMOD::System* m_AudioSystem; //!< The FMOD Sound management object. + FMOD::ChannelGroup* m_MasterChannelGroup; //!< The top-level FMOD ChannelGroup that holds everything. + FMOD::ChannelGroup* m_SFXChannelGroup; //!< The FMOD ChannelGroup for diegetic gameplay sounds. + FMOD::ChannelGroup* m_UIChannelGroup; //!< The FMOD ChannelGroup for UI sounds. + FMOD::ChannelGroup* m_MusicChannelGroup; //!< The FMOD ChannelGroup for music. bool m_AudioEnabled; //!< Bool to tell whether audio is enabled or not. std::vector> m_CurrentActivityHumanPlayerPositions; //!< The stored positions of each human player in the current activity. Only filled when there's an activity running. @@ -466,7 +507,7 @@ namespace RTE { float m_SoundPanningEffectStrength; //!< The strength of the sound panning effect, 0 (no panning) - 1 (full panning). ////////////////////////////////////////////////// - //TODO These need to be removed when our soundscape is sorted out. They're only here temporarily to allow for easier tweaking by pawnis. + // TODO These need to be removed when our soundscape is sorted out. They're only here temporarily to allow for easier tweaking by pawnis. float m_ListenerZOffset; float m_MinimumDistanceForPanning; ////////////////////////////////////////////////// @@ -483,7 +524,6 @@ namespace RTE { std::mutex m_SoundChannelMinimumAudibleDistancesMutex; //!, As above but for m_SoundChannelMinimumAudibleDistances private: - #pragma region Sound Container Actions and Modifications /// /// Starts playing the next SoundSet of the given SoundContainer for the give player. @@ -491,14 +531,14 @@ namespace RTE { /// Pointer to the SoundContainer to start playing. Ownership is NOT transferred! /// Which player to play the SoundContainer's sounds for, -1 means all players. Defaults to -1. /// Whether or not playback of the Sound was successful. - bool PlaySoundContainer(SoundContainer *soundContainer, int player = -1); + bool PlaySoundContainer(SoundContainer* soundContainer, int player = -1); /// /// Sets/updates the position of a SoundContainer's playing sounds. /// /// A pointer to a SoundContainer object. Ownership IS NOT transferred! /// Whether the position was successfully set. - bool ChangeSoundContainerPlayingChannelsPosition(const SoundContainer *soundContainer); + bool ChangeSoundContainerPlayingChannelsPosition(const SoundContainer* soundContainer); /// /// Changes the volume of a SoundContainer's playing sounds. @@ -506,21 +546,21 @@ namespace RTE { /// A pointer to a SoundContainer object. Ownership IS NOT transferred! /// The new volume to play sounds at, between 0 and 1. /// Whether the volume was successfully updated. - bool ChangeSoundContainerPlayingChannelsVolume(const SoundContainer *soundContainer, float newVolume); + bool ChangeSoundContainerPlayingChannelsVolume(const SoundContainer* soundContainer, float newVolume); /// /// Changes the frequency/pitch of a SoundContainer's playing sounds. /// /// A pointer to a SoundContainer object. Ownership IS NOT transferred! /// Whether the pitch was successfully updated. - bool ChangeSoundContainerPlayingChannelsPitch(const SoundContainer *soundContainer); + bool ChangeSoundContainerPlayingChannelsPitch(const SoundContainer* soundContainer); /// /// Updates the custom pan value of a SoundContainer's playing sounds. /// /// A pointer to a SoundContainer object. Ownership IS NOT transferred! /// Whether the custom pan value was successfully updated. - bool ChangeSoundContainerPlayingChannelsCustomPanValue(const SoundContainer *soundContainer); + bool ChangeSoundContainerPlayingChannelsCustomPanValue(const SoundContainer* soundContainer); /// /// Stops playing a SoundContainer's playing sounds for a certain player. @@ -528,14 +568,14 @@ namespace RTE { /// A pointer to a SoundContainer object6. Ownership is NOT transferred! /// Which player to stop playing the SoundContainer for. /// - bool StopSoundContainerPlayingChannels(SoundContainer *soundContainer, int player); + bool StopSoundContainerPlayingChannels(SoundContainer* soundContainer, int player); /// /// Fades out playback a SoundContainer. /// /// A pointer to a SoundContainer object. Ownership is NOT transferred! /// The amount of time, in ms, to fade out over. - void FadeOutSoundContainerPlayingChannels(SoundContainer *soundContainer, int fadeOutTime); + void FadeOutSoundContainerPlayingChannels(SoundContainer* soundContainer, int fadeOutTime); #pragma endregion #pragma region 3D Effect Handling @@ -550,19 +590,19 @@ namespace RTE { /// The channel whose position should be set or updated. /// An optional position to set for this sound channel. Done this way to save setting and resetting data in FMOD. /// Whether the channel's position was succesfully set. - FMOD_RESULT UpdatePositionalEffectsForSoundChannel(FMOD::Channel *soundChannel, const FMOD_VECTOR *positionToUse = nullptr) const; + FMOD_RESULT UpdatePositionalEffectsForSoundChannel(FMOD::Channel* soundChannel, const FMOD_VECTOR* positionToUse = nullptr) const; #pragma endregion #pragma region FMOD Callbacks /// /// A static callback function for FMOD to invoke when the music channel finishes playing. See fmod docs - FMOD_CHANNELCONTROL_CALLBACK for details /// - static FMOD_RESULT F_CALLBACK MusicChannelEndedCallback(FMOD_CHANNELCONTROL *channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void *commandData1, void *commandData2); + static FMOD_RESULT F_CALLBACK MusicChannelEndedCallback(FMOD_CHANNELCONTROL* channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void* commandData1, void* commandData2); /// /// A static callback function for FMOD to invoke when a sound channel finished playing. See fmod docs - FMOD_CHANNELCONTROL_CALLBACK for details /// - static FMOD_RESULT F_CALLBACK SoundChannelEndedCallback(FMOD_CHANNELCONTROL *channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void *commandData1, void *commandData2); + static FMOD_RESULT F_CALLBACK SoundChannelEndedCallback(FMOD_CHANNELCONTROL* channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void* commandData1, void* commandData2); #pragma endregion #pragma region Utility Methods @@ -571,7 +611,7 @@ namespace RTE { /// /// The RTE Vector to get as an FMOD_VECTOR. /// The FMOD_VECTOR that corresponds to the given RTE Vector. - FMOD_VECTOR GetAsFMODVector(const Vector &vector, float zValue = 0) const; + FMOD_VECTOR GetAsFMODVector(const Vector& vector, float zValue = 0) const; /// /// Gets the corresponding RTE Vector for a given FMOD_VECTOR. @@ -587,8 +627,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - AudioMan(const AudioMan &reference) = delete; - AudioMan & operator=(const AudioMan &rhs) = delete; + AudioMan(const AudioMan& reference) = delete; + AudioMan& operator=(const AudioMan& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Managers/CameraMan.cpp b/Source/Managers/CameraMan.cpp index 2e640be938..37ceaaf4b8 100644 --- a/Source/Managers/CameraMan.cpp +++ b/Source/Managers/CameraMan.cpp @@ -10,7 +10,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void CameraMan::Clear() { m_ScreenShakeStrength = 1.0F; @@ -20,7 +20,7 @@ namespace RTE { m_DefaultShakePerUnitOfRecoilEnergy = 0.5F; m_DefaultShakeFromRecoilMaximum = 0.0F; - for (Screen &screen : m_Screens) { + for (Screen& screen: m_Screens) { screen.Offset.Reset(); screen.DeltaOffset.Reset(); screen.ScrollTarget.Reset(); @@ -36,25 +36,25 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::SetOffset(const Vector &offset, int screenId) { + void CameraMan::SetOffset(const Vector& offset, int screenId) { m_Screens[screenId].Offset = offset.GetFloored(); CheckOffset(screenId); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector CameraMan::GetUnwrappedOffset(int screenId) const { - const Screen &screen = m_Screens[screenId]; - const SLTerrain *terrain = g_SceneMan.GetScene()->GetTerrain(); + const Screen& screen = m_Screens[screenId]; + const SLTerrain* terrain = g_SceneMan.GetScene()->GetTerrain(); return Vector(screen.Offset.GetX() + static_cast(terrain->GetBitmap()->w * screen.SeamCrossCount[Axes::X]), screen.Offset.GetY() + static_cast(terrain->GetBitmap()->h * screen.SeamCrossCount[Axes::Y])); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::SetScroll(const Vector ¢er, int screenId) { - Screen &screen = m_Screens[screenId]; + void CameraMan::SetScroll(const Vector& center, int screenId) { + Screen& screen = m_Screens[screenId]; if (g_FrameMan.IsInMultiplayerMode()) { screen.Offset.SetXY(static_cast(center.GetFloorIntX() - (g_FrameMan.GetPlayerFrameBufferWidth(screenId) / 2)), static_cast(center.GetFloorIntY() - (g_FrameMan.GetPlayerFrameBufferHeight(screenId) / 2))); } else { @@ -63,19 +63,19 @@ namespace RTE { CheckOffset(screenId); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector CameraMan::GetScrollTarget(int screenId) const { return g_NetworkClient.IsConnectedAndRegistered() ? g_NetworkClient.GetFrameTarget() : m_Screens[screenId].ScrollTarget; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::SetScrollTarget(const Vector &targetCenter, float speed, int screenId) { - Screen &screen = m_Screens[screenId]; + void CameraMan::SetScrollTarget(const Vector& targetCenter, float speed, int screenId) { + Screen& screen = m_Screens[screenId]; // See if it would make sense to automatically wrap. - const SLTerrain *terrain = g_SceneMan.GetScene()->GetTerrain(); + const SLTerrain* terrain = g_SceneMan.GetScene()->GetTerrain(); float targetXWrapped = terrain->WrapsX() && (std::fabs(targetCenter.GetX() - screen.ScrollTarget.GetX()) > static_cast(terrain->GetBitmap()->w / 2)); float targetYWrapped = terrain->WrapsY() && (std::fabs(targetCenter.GetY() - screen.ScrollTarget.GetY()) > static_cast(terrain->GetBitmap()->h / 2)); @@ -87,9 +87,9 @@ namespace RTE { screen.TargetYWrapped = screen.TargetYWrapped || targetYWrapped; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float CameraMan::TargetDistanceScalar(const Vector &point) const { + float CameraMan::TargetDistanceScalar(const Vector& point) const { if (!g_SceneMan.GetScene()) { return 0.0F; } @@ -103,7 +103,7 @@ namespace RTE { } float closestScalar = 1.0F; - for (const Screen &screen : m_Screens) { + for (const Screen& screen: m_Screens) { float distance = g_SceneMan.ShortestDistance(point, screen.ScrollTarget).GetMagnitude(); float scalar = 0.0F; @@ -123,15 +123,15 @@ namespace RTE { return closestScalar; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void CameraMan::CheckOffset(int screenId) { RTEAssert(g_SceneMan.GetScene(), "Trying to check offset before there is a scene or terrain!"); - const SLTerrain *terrain = g_SceneMan.GetScene()->GetTerrain(); + const SLTerrain* terrain = g_SceneMan.GetScene()->GetTerrain(); RTEAssert(terrain, "Trying to get terrain matter before there is a scene or terrain!"); - Screen &screen = m_Screens[screenId]; + Screen& screen = m_Screens[screenId]; if (!terrain->WrapsX() && screen.Offset.GetX() < 0) { screen.Offset.SetX(0.0F); @@ -153,7 +153,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector CameraMan::GetFrameSize(int screenId) { int frameWidth = g_WindowMan.GetResX(); @@ -170,21 +170,21 @@ namespace RTE { return Vector(static_cast(frameWidth), static_cast(frameHeight)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void CameraMan::ResetAllScreenShake() { for (int screenId = 0; screenId < g_FrameMan.GetScreenCount(); ++screenId) { - Screen &screen = m_Screens[screenId]; + Screen& screen = m_Screens[screenId]; screen.ScreenShakeMagnitude = 0; screen.ScrollTimer.Reset(); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::AddScreenShake(float magnitude, const Vector &position) { + void CameraMan::AddScreenShake(float magnitude, const Vector& position) { for (int screenId = 0; screenId < g_FrameMan.GetScreenCount(); ++screenId) { - Screen &screen = m_Screens[screenId]; + Screen& screen = m_Screens[screenId]; Vector frameSize = GetFrameSize(screenId); @@ -193,7 +193,7 @@ namespace RTE { g_SceneMan.WrapBox(screenBox, wrappedBoxes); float closestDistanceFromScreen = std::numeric_limits::max(); - for (const Box &box : wrappedBoxes) { + for (const Box& box: wrappedBoxes) { // Determine how far the position is from the box. Vector closestPointOnBox = box.GetWithinBox(position); Vector distanceFromBoxToPosition = closestPointOnBox - position; @@ -209,11 +209,11 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void CameraMan::Update(int screenId) { - Screen &screen = m_Screens[screenId]; - const SLTerrain *terrain = g_SceneMan.GetScene()->GetTerrain(); + Screen& screen = m_Screens[screenId]; + const SLTerrain* terrain = g_SceneMan.GetScene()->GetTerrain(); if (g_TimerMan.DrawnSimUpdate()) { // Adjust for wrapping if the scroll target jumped a seam this frame, as reported by whatever screen set it (the scroll target) this frame. This is to avoid big, scene-wide jumps in scrolling when traversing the seam. @@ -283,4 +283,4 @@ namespace RTE { screen.DeltaOffset = screen.Offset - oldOffset; screen.ScrollTimer.Reset(); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Managers/CameraMan.h b/Source/Managers/CameraMan.h index 2019b53365..4fe669d3c1 100644 --- a/Source/Managers/CameraMan.h +++ b/Source/Managers/CameraMan.h @@ -16,7 +16,6 @@ namespace RTE { friend class SettingsMan; public: - #pragma region Creation /// /// Constructor method used to instantiate a CameraMan object in system memory. Create() should be called before using the object. @@ -60,7 +59,7 @@ namespace RTE { /// /// The new offset value. /// Which screen you want to set the offset of. - void SetOffset(const Vector &offset, int screenId = 0); + void SetOffset(const Vector& offset, int screenId = 0); /// /// Gets the difference in current offset and that of the Update() before. @@ -81,7 +80,7 @@ namespace RTE { /// /// The coordinates to center the terrain scroll on. /// Which screen you want to set the offset of. - void SetScroll(const Vector ¢er, int screenId = 0); + void SetScroll(const Vector& center, int screenId = 0); /// /// Gets the team associated with a specific screen. @@ -103,7 +102,7 @@ namespace RTE { /// /// Which screen you want to get the team of. /// A vector indicating the screen occlusion amount. - Vector & GetScreenOcclusion(int screenId = 0) { return m_Screens[screenId].ScreenOcclusion; } + Vector& GetScreenOcclusion(int screenId = 0) { return m_Screens[screenId].ScreenOcclusion; } /// /// Sets the amount that a specific screen is occluded by a GUI panel or something of the sort. @@ -111,7 +110,7 @@ namespace RTE { /// /// The amount of occlusion of the screen. /// Which screen you want to set the occlusion of. - void SetScreenOcclusion(const Vector &occlusion, int screenId = 0) { m_Screens[screenId].ScreenOcclusion = occlusion; } + void SetScreenOcclusion(const Vector& occlusion, int screenId = 0) { m_Screens[screenId].ScreenOcclusion = occlusion; } /// /// Gets the currently set scroll target, i.e. where the center of the specific screen is trying to line up with. @@ -126,7 +125,7 @@ namespace RTE { /// The new target vector in Scene coordinates. /// The normalized speed at screen the view scrolls. 0 being no movement, and 1.0 being instant movement to the target in one frame. /// Which screen you want to set the scroll offset of. - void SetScrollTarget(const Vector &targetCenter, float speed = 0.1F, int screenId = 0); + void SetScrollTarget(const Vector& targetCenter, float speed = 0.1F, int screenId = 0); /// /// Calculates a scalar of how distant a certain point in the world is from the currently closest scroll target of all active screens. @@ -136,7 +135,7 @@ namespace RTE { /// A normalized scalar representing the distance between the closest scroll target of all active screens, to the passed in point. /// 0 means it's the point is within half a screen's width of the target, and 1.0 means it's on the clear opposite side of the scene. /// - float TargetDistanceScalar(const Vector &point) const; + float TargetDistanceScalar(const Vector& point) const; /// /// Makes sure the current offset won't create a view of outside the scene. @@ -211,7 +210,7 @@ namespace RTE { /// /// The amount of screen shake. /// The spatial location of the screen-shake event. - void AddScreenShake(float magnitude, const Vector &position); + void AddScreenShake(float magnitude, const Vector& position); /// /// Increases the magnitude of screen shake. @@ -243,7 +242,6 @@ namespace RTE { #pragma endregion private: - /// /// A screen. Each player should have one of these. /// @@ -261,7 +259,7 @@ namespace RTE { bool TargetXWrapped = false; //!< Whether the ScrollTarget got x wrapped around the world this frame or not. bool TargetYWrapped = false; //!< Whether the ScrollTarget got y wrapped around the world this frame or not. - std::array SeamCrossCount = { 0, 0 }; //!< Keeps track of how many times and in screen directions the wrapping seam has been crossed. This is used for keeping the background layers' scroll from jumping when wrapping around. X and Y. + std::array SeamCrossCount = {0, 0}; //!< Keeps track of how many times and in screen directions the wrapping seam has been crossed. This is used for keeping the background layers' scroll from jumping when wrapping around. X and Y. Vector ScreenOcclusion; //!< The amount a screen is occluded or covered by GUI, etc. @@ -283,8 +281,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - CameraMan(const CameraMan &reference) = delete; - CameraMan & operator=(const CameraMan &rhs) = delete; + CameraMan(const CameraMan& reference) = delete; + CameraMan& operator=(const CameraMan& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Managers/ConsoleMan.cpp b/Source/Managers/ConsoleMan.cpp index 0f25bb8523..f59812ceea 100644 --- a/Source/Managers/ConsoleMan.cpp +++ b/Source/Managers/ConsoleMan.cpp @@ -16,7 +16,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::Clear() { m_ConsoleState = ConsoleState::Disabled; @@ -38,12 +38,18 @@ namespace RTE { m_ConsoleUseMonospaceFont = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int ConsoleMan::Initialize() { - if (!m_GUIScreen) { m_GUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer32()); } - if (!m_GUIInput) { m_GUIInput = new GUIInputWrapper(-1); } - if (!m_GUIControlManager) { m_GUIControlManager = new GUIControlManager(); } + if (!m_GUIScreen) { + m_GUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer32()); + } + if (!m_GUIInput) { + m_GUIInput = new GUIInputWrapper(-1); + } + if (!m_GUIControlManager) { + m_GUIControlManager = new GUIControlManager(); + } if (!m_GUIControlManager->Create(m_GUIScreen, m_GUIInput, "Base.rte/GUIs/Skins/Menus", m_ConsoleUseMonospaceFont ? "ConsoleMonospaceSkin.ini" : "ConsoleSkin.ini")) { RTEAbort("Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/ConsoleSkin.ini"); @@ -53,14 +59,14 @@ namespace RTE { m_GUIControlManager->EnableMouse(false); // Stretch the invisible root box to fill the screen - dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); if (!m_ParentBox) { - m_ParentBox = dynamic_cast(m_GUIControlManager->GetControl("ConsoleGUIBox")); + m_ParentBox = dynamic_cast(m_GUIControlManager->GetControl("ConsoleGUIBox")); m_ParentBox->SetDrawType(GUICollectionBox::Color); } - m_ConsoleText = dynamic_cast(m_GUIControlManager->GetControl("ConsoleLabel")); - m_InputTextBox = dynamic_cast(m_GUIControlManager->GetControl("InputTB")); + m_ConsoleText = dynamic_cast(m_GUIControlManager->GetControl("ConsoleLabel")); + m_InputTextBox = dynamic_cast(m_GUIControlManager->GetControl("InputTB")); SetConsoleScreenSize(m_ConsoleScreenRatio); @@ -68,15 +74,19 @@ namespace RTE { m_ParentBox->SetEnabled(false); m_ParentBox->SetVisible(false); - if (!g_WindowMan.ResolutionChanged()) { m_OutputLog.emplace_back("- RTE Lua Console -\nSee the Data Realms Wiki for commands: http://www.datarealms.com/wiki/\nPress F1 for a list of helpful shortcuts\n-------------------------------------"); } + if (!g_WindowMan.ResolutionChanged()) { + m_OutputLog.emplace_back("- RTE Lua Console -\nSee the Data Realms Wiki for commands: http://www.datarealms.com/wiki/\nPress F1 for a list of helpful shortcuts\n-------------------------------------"); + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::Destroy() { - if (!g_WindowMan.ResolutionChanged()) { SaveAllText("LogConsole.txt"); } + if (!g_WindowMan.ResolutionChanged()) { + SaveAllText("LogConsole.txt"); + } delete m_GUIControlManager; delete m_GUIInput; @@ -92,7 +102,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::SetEnabled(bool enable) { if (enable && m_ConsoleState != ConsoleState::Enabled && m_ConsoleState != ConsoleState::Enabling) { @@ -104,7 +114,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::SetReadOnly() { if (!m_ReadOnly) { @@ -116,7 +126,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::SetConsoleScreenSize(float screenRatio) { m_ConsoleScreenRatio = Limit(screenRatio, 1.0F, 0.1F); @@ -132,61 +142,67 @@ namespace RTE { m_InputTextBox->Resize(m_ParentBox->GetWidth() - 3, m_InputTextBox->GetHeight()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::SetConsoleUseMonospaceFont(bool useFont) { m_ConsoleUseMonospaceFont = useFont; - if (m_GUIControlManager) { m_GUIControlManager->ChangeSkin("Base.rte/GUIs/Skins/Menus", m_ConsoleUseMonospaceFont ? "ConsoleMonospaceSkin.ini" : "ConsoleSkin.ini"); } + if (m_GUIControlManager) { + m_GUIControlManager->ChangeSkin("Base.rte/GUIs/Skins/Menus", m_ConsoleUseMonospaceFont ? "ConsoleMonospaceSkin.ini" : "ConsoleSkin.ini"); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::AddLoadWarningLogExtensionMismatchEntry(const std::string &pathToLog, const std::string &readerPosition, const std::string &altFileExtension) { + void ConsoleMan::AddLoadWarningLogExtensionMismatchEntry(const std::string& pathToLog, const std::string& readerPosition, const std::string& altFileExtension) { const std::string pathAndAccessLocation = "\"" + pathToLog + "\" referenced " + readerPosition + ". "; std::string newEntry = pathAndAccessLocation + (!altFileExtension.empty() ? "Found and loaded a file with \"" + altFileExtension + "\" extension." : "The file was not loaded."); if (g_PresetMan.GetReloadEntityPresetCalledThisUpdate()) { PrintString(newEntry); } else { std::transform(newEntry.begin(), newEntry.end(), newEntry.begin(), ::tolower); - if (m_LoadWarningLog.find(newEntry) == m_LoadWarningLog.end()) { m_LoadWarningLog.emplace(newEntry); } + if (m_LoadWarningLog.find(newEntry) == m_LoadWarningLog.end()) { + m_LoadWarningLog.emplace(newEntry); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::SaveLoadWarningLog(const std::string &filePath) { + void ConsoleMan::SaveLoadWarningLog(const std::string& filePath) { Writer logWriter(filePath.c_str()); if (logWriter.WriterOK()) { logWriter << "// Warnings produced during loading:"; logWriter.NewLine(false); - for (const std::string &logEntry : m_LoadWarningLog) { + for (const std::string& logEntry: m_LoadWarningLog) { logWriter.NewLineString(logEntry, false); } PrintString("SYSTEM: Loading warning log saved to " + filePath); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::SaveInputLog(const std::string &filePath) { + void ConsoleMan::SaveInputLog(const std::string& filePath) { Writer logWriter(filePath.c_str()); if (logWriter.WriterOK()) { for (std::deque::reverse_iterator logItr = m_InputLog.rbegin(); logItr != m_InputLog.rend(); ++logItr) { logWriter << *logItr; // Add semicolon so the line input becomes a statement - if (!logItr->empty() && (*logItr)[logItr->length() - 1] != ';') { logWriter << ";"; } + if (!logItr->empty() && (*logItr)[logItr->length() - 1] != ';') { + logWriter << ";"; + } logWriter << "\n"; } PrintString("SYSTEM: Console input log saved to " + filePath); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ConsoleMan::SaveAllText(const std::string &filePath) { + bool ConsoleMan::SaveAllText(const std::string& filePath) { Writer logWriter(filePath.c_str()); if (logWriter.WriterOK()) { - for (const std::string &loggedString : m_OutputLog) { + for (const std::string& loggedString: m_OutputLog) { logWriter << loggedString; } logWriter.EndWrite(); @@ -196,7 +212,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::ClearLog() { m_InputLog.clear(); @@ -204,51 +220,52 @@ namespace RTE { m_OutputLog.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::PrintString(const std::string &stringToPrint) { + void ConsoleMan::PrintString(const std::string& stringToPrint) { static std::mutex printStringMutex; std::scoped_lock printStringLock(printStringMutex); m_OutputLog.emplace_back("\n" + stringToPrint); - if (System::IsLoggingToCLI()) { - System::PrintToCLI(stringToPrint); + if (System::IsLoggingToCLI()) { + System::PrintToCLI(stringToPrint); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::ShowShortcuts() { - if (!IsEnabled()) { SetEnabled(); } + if (!IsEnabled()) { + SetEnabled(); + } PrintString( - "\n--- SHORTCUTS ---\n" - "CTRL + ~ - Console in read-only mode without input capture\n" - "CTRL + DOWN / UP - Increase/decrease console size (Only while console is open)\n" - "CTRL + S - Make continuous screenshots while the keys are held\n" - "CTRL + W - Make a screenshot of the entire level\n" - "ALT + W - Make a miniature preview image of the entire level\n" - "CTRL + P - Show performance stats\n" - "ALT + P - Show advanced performance stats (Only while performance stats are visible)\n" - "CTRL + R - Reset activity\n" - "CTRL + M - Switch display mode: Draw -> Material -> MO\n" - "CTRL + O - Toggle one sim update per frame\n" - "SHIFT + ESC - Skip pause menu when pausing activity (straight to scenario/conquest menu)\n" - "----------------\n" - "F2 - Reload all Lua scripts\n" - "ALT + F2 - Reload all sprites\n" - "CTRL + F2 - Quick reload Entity preset previously reloaded with PresetMan:ReloadEntityPreset\n" - "F3 - Save console log\n" - "F4 - Save console user input log\n" - "F5 - Quick save\n" - "F9 - Load latest quick-save\n" - "CTRL + F9 - Load latest auto-save\n" - "F10 - Clear Console log\n" - "F12 - Make a single screenshot" - ); + "\n--- SHORTCUTS ---\n" + "CTRL + ~ - Console in read-only mode without input capture\n" + "CTRL + DOWN / UP - Increase/decrease console size (Only while console is open)\n" + "CTRL + S - Make continuous screenshots while the keys are held\n" + "CTRL + W - Make a screenshot of the entire level\n" + "ALT + W - Make a miniature preview image of the entire level\n" + "CTRL + P - Show performance stats\n" + "ALT + P - Show advanced performance stats (Only while performance stats are visible)\n" + "CTRL + R - Reset activity\n" + "CTRL + M - Switch display mode: Draw -> Material -> MO\n" + "CTRL + O - Toggle one sim update per frame\n" + "SHIFT + ESC - Skip pause menu when pausing activity (straight to scenario/conquest menu)\n" + "----------------\n" + "F2 - Reload all Lua scripts\n" + "ALT + F2 - Reload all sprites\n" + "CTRL + F2 - Quick reload Entity preset previously reloaded with PresetMan:ReloadEntityPreset\n" + "F3 - Save console log\n" + "F4 - Save console user input log\n" + "F5 - Quick save\n" + "F9 - Load latest quick-save\n" + "CTRL + F9 - Load latest auto-save\n" + "F10 - Clear Console log\n" + "F12 - Make a single screenshot"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::Update() { if (g_UInputMan.FlagCtrlState() && g_UInputMan.KeyPressed(SDL_SCANCODE_GRAVE)) { @@ -276,7 +293,9 @@ namespace RTE { } } - if (m_ConsoleState != ConsoleState::Enabled && m_ConsoleState != ConsoleState::Disabled) { ConsoleOpenClose(); } + if (m_ConsoleState != ConsoleState::Enabled && m_ConsoleState != ConsoleState::Disabled) { + ConsoleOpenClose(); + } std::stringstream consoleText; for (std::deque::iterator logIterator = (m_OutputLog.size() < m_ConsoleTextMaxNumLines) ? m_OutputLog.begin() : m_OutputLog.end() - m_ConsoleTextMaxNumLines; logIterator != m_OutputLog.end(); ++logIterator) { @@ -320,7 +339,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::ConsoleOpenClose() { float travelCompletionDistance; @@ -332,7 +351,9 @@ namespace RTE { travelCompletionDistance = std::floor(static_cast(m_ParentBox->GetYPos()) * 0.5F); m_ParentBox->SetPositionAbs(0, m_ParentBox->GetYPos() - static_cast(travelCompletionDistance)); - if (m_ParentBox->GetYPos() >= 0) { m_ConsoleState = ConsoleState::Enabled; } + if (m_ParentBox->GetYPos() >= 0) { + m_ConsoleState = ConsoleState::Enabled; + } } else if (m_ConsoleState == ConsoleState::Disabling) { travelCompletionDistance = std::ceil((static_cast(m_ParentBox->GetHeight()) + static_cast(m_ParentBox->GetYPos())) * 0.5F); m_ParentBox->SetPositionAbs(0, m_ParentBox->GetYPos() - static_cast(travelCompletionDistance)); @@ -354,7 +375,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::FeedString(bool feedEmptyString) { char strLine[1024]; @@ -370,8 +391,12 @@ namespace RTE { m_OutputLog.emplace_back("\n" + line); g_LuaMan.GetMasterScriptState().RunScriptString(line, false); - if (g_LuaMan.GetMasterScriptState().ErrorExists()) { m_OutputLog.emplace_back("\nERROR: " + g_LuaMan.GetMasterScriptState().GetLastError()); } - if (m_InputLog.empty() || m_InputLog.front() != line) { m_InputLog.push_front(line); } + if (g_LuaMan.GetMasterScriptState().ErrorExists()) { + m_OutputLog.emplace_back("\nERROR: " + g_LuaMan.GetMasterScriptState().GetLastError()); + } + if (m_InputLog.empty() || m_InputLog.front() != line) { + m_InputLog.push_front(line); + } m_InputLogPosition = m_InputLog.begin(); m_LastLogMove = 0; @@ -384,12 +409,14 @@ namespace RTE { m_InputTextBox->SetText(""); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::LoadLoggedInput(bool nextEntry) { if (nextEntry) { // See if we should decrement doubly because the last move was in the opposite direction - if (m_LastLogMove > 0 && m_InputLogPosition != m_InputLog.begin()) { --m_InputLogPosition; } + if (m_LastLogMove > 0 && m_InputLogPosition != m_InputLog.begin()) { + --m_InputLogPosition; + } if (m_InputLogPosition == m_InputLog.begin()) { m_InputTextBox->SetText(""); @@ -402,7 +429,9 @@ namespace RTE { } } else { // See if we should increment doubly because the last move was in the opposite direction - if (m_LastLogMove < 0 && m_InputLogPosition != m_InputLog.end() - 1) { ++m_InputLogPosition; } + if (m_LastLogMove < 0 && m_InputLogPosition != m_InputLog.end() - 1) { + ++m_InputLogPosition; + } m_InputTextBox->SetText(*m_InputLogPosition); m_InputTextBox->SetCursorPos(m_InputTextBox->GetText().length()); @@ -417,7 +446,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ConsoleMan::RemoveGraveAccents() const { std::string textBoxString = m_InputTextBox->GetText(); @@ -428,12 +457,12 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::Draw(BITMAP *targetBitmap) const { + void ConsoleMan::Draw(BITMAP* targetBitmap) const { if (m_ConsoleState != ConsoleState::Disabled) { AllegroScreen drawScreen(targetBitmap); m_GUIControlManager->Draw(&drawScreen); } } -} +} // namespace RTE diff --git a/Source/Managers/ConsoleMan.h b/Source/Managers/ConsoleMan.h index 4b293db6eb..bf71f7523a 100644 --- a/Source/Managers/ConsoleMan.h +++ b/Source/Managers/ConsoleMan.h @@ -21,7 +21,6 @@ namespace RTE { friend class SettingsMan; public: - #pragma region Creation /// /// Constructor method used to instantiate a ConsoleMan object in system memory. Create() should be called before using the object. @@ -104,26 +103,26 @@ namespace RTE { /// The path that produced the warning. /// The file and line currently being loaded. /// The alternative file extension to the path that produced the warning (e.g. if file is ".bmp", alternative extension is ".png"). - void AddLoadWarningLogExtensionMismatchEntry(const std::string &pathToLog, const std::string &readerPosition = "", const std::string &altFileExtension = "" ); + void AddLoadWarningLogExtensionMismatchEntry(const std::string& pathToLog, const std::string& readerPosition = "", const std::string& altFileExtension = ""); /// /// Writes the entire loading warning log to a file. /// /// The filename of the file to write to. - void SaveLoadWarningLog(const std::string &filePath); + void SaveLoadWarningLog(const std::string& filePath); /// /// Writes all the input strings to a log in the order they were entered. /// /// The filename of the file to write to. - void SaveInputLog(const std::string &filePath); + void SaveInputLog(const std::string& filePath); /// /// Writes the entire console buffer to a file. /// /// The filename of the file to write to. /// Whether writing to the file was successful. - bool SaveAllText(const std::string &filePath); + bool SaveAllText(const std::string& filePath); /// /// Clears all previous input. @@ -136,7 +135,7 @@ namespace RTE { /// Prints a string into the console. /// /// The string to print. - void PrintString(const std::string &stringToPrint); + void PrintString(const std::string& stringToPrint); /// /// Opens the console and prints the shortcut help text. @@ -152,26 +151,30 @@ namespace RTE { /// Draws this ConsoleMan's current graphical representation to a BITMAP of choice. /// /// A pointer to a BITMAP to draw on. - void Draw(BITMAP *targetBitmap) const; + void Draw(BITMAP* targetBitmap) const; #pragma endregion protected: - /// /// Enumeration for console states when enabling/disabling the console. NOTE: This can't be lower down because m_ConsoleState relies on this definition. /// - enum ConsoleState { Enabling = 0, Enabled, Disabling, Disabled }; + enum ConsoleState { + Enabling = 0, + Enabled, + Disabling, + Disabled + }; ConsoleState m_ConsoleState; //!< Current state of the console. bool m_ReadOnly; //!< Read-only mode where console text input is disabled and controller input should be preserved. float m_ConsoleScreenRatio; //!< The ratio of the screen that the console should take up, from 0.1 to 1.0 (whole screen). - GUIScreen *m_GUIScreen; //!< GUI Screen for use by the in-game GUI. - GUIInput *m_GUIInput; //!< GUI Input controller. - GUIControlManager *m_GUIControlManager; //!< Manager of the console GUI elements. - GUICollectionBox *m_ParentBox; //!< Collection box of the console GUI. - GUILabel *m_ConsoleText; //!< The label which presents the console output. - GUITextBox *m_InputTextBox; //!< The TextBox which the user types in the edited line. + GUIScreen* m_GUIScreen; //!< GUI Screen for use by the in-game GUI. + GUIInput* m_GUIInput; //!< GUI Input controller. + GUIControlManager* m_GUIControlManager; //!< Manager of the console GUI elements. + GUICollectionBox* m_ParentBox; //!< Collection box of the console GUI. + GUILabel* m_ConsoleText; //!< The label which presents the console output. + GUITextBox* m_InputTextBox; //!< The TextBox which the user types in the edited line. int m_ConsoleTextMaxNumLines; //!< Maximum number of lines to display in the console text label. @@ -184,7 +187,6 @@ namespace RTE { short m_LastLogMove; //!< The last direction the log marker was moved. Needed so that changing directions won't need double tapping. private: - bool m_ConsoleUseMonospaceFont; //!< Whether the console text is using the monospace font. /// @@ -223,8 +225,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - ConsoleMan(const ConsoleMan &reference) = delete; - ConsoleMan & operator=(const ConsoleMan &rhs) = delete; + ConsoleMan(const ConsoleMan& reference) = delete; + ConsoleMan& operator=(const ConsoleMan& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Managers/FrameMan.cpp b/Source/Managers/FrameMan.cpp index 63a047ddee..cb8e15e4bb 100644 --- a/Source/Managers/FrameMan.cpp +++ b/Source/Managers/FrameMan.cpp @@ -26,24 +26,24 @@ namespace RTE { - void BitmapDeleter::operator()(BITMAP *bitmap) const { destroy_bitmap(bitmap); } + void BitmapDeleter::operator()(BITMAP* bitmap) const { destroy_bitmap(bitmap); } const std::array, DrawBlendMode::BlendModeCount> FrameMan::c_BlenderSetterFunctions = { - nullptr, // NoBlend obviously has no blender, but we want to keep the indices matching with the enum. - &set_burn_blender, - &set_color_blender, - &set_difference_blender, - &set_dissolve_blender, - &set_dodge_blender, - &set_invert_blender, - &set_luminance_blender, - &set_multiply_blender, - &set_saturation_blender, - &set_screen_blender, - nullptr // Transparency does not rely on the blender setting, it creates a map with the dedicated function instead of with the generic one. + nullptr, // NoBlend obviously has no blender, but we want to keep the indices matching with the enum. + &set_burn_blender, + &set_color_blender, + &set_difference_blender, + &set_dissolve_blender, + &set_dodge_blender, + &set_invert_blender, + &set_luminance_blender, + &set_multiply_blender, + &set_saturation_blender, + &set_screen_blender, + nullptr // Transparency does not rely on the blender setting, it creates a map with the dedicated function instead of with the generic one. }; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::Clear() { m_HSplit = false; @@ -92,7 +92,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int FrameMan::Initialize() { set_color_depth(c_BPP); @@ -120,7 +120,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int FrameMan::CreateBackBuffers() { int resX = g_WindowMan.GetResX(); @@ -173,7 +173,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::CreatePresetColorTables() { // Create RGB lookup table that supposedly speeds up calculation of other color tables. @@ -184,7 +184,7 @@ namespace RTE { int transparencyPresetCount = BlendAmountLimits::MaxBlend / c_BlendAmountStep; for (int index = 0; index <= transparencyPresetCount; ++index) { int presetBlendAmount = index * c_BlendAmountStep; - std::array colorChannelBlendAmounts = { presetBlendAmount, presetBlendAmount, presetBlendAmount, BlendAmountLimits::MinBlend }; + std::array colorChannelBlendAmounts = {presetBlendAmount, presetBlendAmount, presetBlendAmount, BlendAmountLimits::MinBlend}; int adjustedBlendAmount = 255 - (static_cast(255.0F * (1.0F / static_cast(transparencyPresetCount) * static_cast(index)))); m_ColorTables.at(DrawBlendMode::BlendTransparency).try_emplace(colorChannelBlendAmounts); @@ -193,22 +193,22 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::Destroy() { - for (const GUIScreen *guiScreen : m_GUIScreens) { + for (const GUIScreen* guiScreen: m_GUIScreens) { delete guiScreen; } - for (const GUIFont *guiFont : m_LargeFonts) { + for (const GUIFont* guiFont: m_LargeFonts) { delete guiFont; } - for (const GUIFont *guiFont : m_SmallFonts) { + for (const GUIFont* guiFont: m_SmallFonts) { delete guiFont; } Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::Update() { // Remove all scheduled primitives, those will be re-added by updates from other entities. @@ -218,16 +218,18 @@ namespace RTE { // Prune unused color tables every 5 real minutes to prevent ridiculous memory usage over time. if (m_ColorTablePruneTimer.IsPastRealMS(300000)) { long long currentTime = g_TimerMan.GetAbsoluteTime() / 10000; - for (std::unordered_map, std::pair> &colorTableMap : m_ColorTables) { + for (std::unordered_map, std::pair>& colorTableMap: m_ColorTables) { if (colorTableMap.size() >= 100) { std::vector> markedForDelete; markedForDelete.reserve(colorTableMap.size()); - for (const auto &[tableKey, tableData] : colorTableMap) { + for (const auto& [tableKey, tableData]: colorTableMap) { long long lastAccessTime = tableData.second; // Mark tables that haven't been accessed in the last minute for deletion. Avoid marking the transparency table presets, those will have lastAccessTime set to -1. - if (lastAccessTime != -1 && (currentTime - lastAccessTime > 60)) { markedForDelete.emplace_back(tableKey); } + if (lastAccessTime != -1 && (currentTime - lastAccessTime > 60)) { + markedForDelete.emplace_back(tableKey); + } } - for (const std::array &keyToDelete : markedForDelete) { + for (const std::array& keyToDelete: markedForDelete) { colorTableMap.erase(keyToDelete); } } @@ -242,10 +244,12 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::ResetSplitScreens(bool hSplit, bool vSplit) { - if (m_PlayerScreen) { release_bitmap(m_PlayerScreen.get()); } + if (m_PlayerScreen) { + release_bitmap(m_PlayerScreen.get()); + } // Override screen splitting according to settings if needed if ((hSplit || vSplit) && !(hSplit && vSplit) && m_TwoPlayerVSplit) { @@ -274,7 +278,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector FrameMan::GetMiddleOfPlayerScreen(int whichPlayer) { Vector middleOfPlayerScreen; @@ -295,7 +299,7 @@ namespace RTE { return middleOfPlayerScreen; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int FrameMan::GetPlayerFrameBufferWidth(int whichPlayer) const { if (IsInMultiplayerMode()) { @@ -316,7 +320,7 @@ namespace RTE { return m_PlayerScreenWidth; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int FrameMan::GetPlayerFrameBufferHeight(int whichPlayer) const { if (IsInMultiplayerMode()) { @@ -337,17 +341,17 @@ namespace RTE { return m_PlayerScreenHeight; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::CalculateTextHeight(const std::string &text, int maxWidth, bool isSmall) { + int FrameMan::CalculateTextHeight(const std::string& text, int maxWidth, bool isSmall) { return isSmall ? GetSmallFont()->CalculateHeight(text, maxWidth) : GetLargeFont()->CalculateHeight(text, maxWidth); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string FrameMan::SplitStringToFitWidth(const std::string &stringToSplit, int widthLimit, bool useSmallFont) { - GUIFont *fontToUse = GetFont(useSmallFont, false); - auto SplitSingleLineAsNeeded = [this, &widthLimit, &fontToUse](std::string &lineToSplitAsNeeded) { + std::string FrameMan::SplitStringToFitWidth(const std::string& stringToSplit, int widthLimit, bool useSmallFont) { + GUIFont* fontToUse = GetFont(useSmallFont, false); + auto SplitSingleLineAsNeeded = [this, &widthLimit, &fontToUse](std::string& lineToSplitAsNeeded) { int numberOfScreenWidthsForText = static_cast(std::ceil(static_cast(fontToUse->CalculateWidth(lineToSplitAsNeeded)) / static_cast(widthLimit))); if (numberOfScreenWidthsForText > 1) { int splitInterval = static_cast(std::ceil(static_cast(lineToSplitAsNeeded.size()) / static_cast(numberOfScreenWidthsForText))); @@ -384,15 +388,15 @@ namespace RTE { return splitString; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::CalculateTextWidth(const std::string &text, bool isSmall) { + int FrameMan::CalculateTextWidth(const std::string& text, bool isSmall) { return isSmall ? GetSmallFont()->CalculateWidth(text) : GetLargeFont()->CalculateWidth(text); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::SetScreenText(const std::string &message, int whichScreen, int blinkInterval, int displayDuration, bool centered) { + void FrameMan::SetScreenText(const std::string& message, int whichScreen, int blinkInterval, int displayDuration, bool centered) { // See if we can overwrite the previous message if (whichScreen >= 0 && whichScreen < c_MaxScreenCount && m_TextDurationTimer[whichScreen].IsPastRealMS(m_TextDuration[whichScreen])) { m_ScreenText[whichScreen] = message; @@ -403,7 +407,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::ClearScreenText(int whichScreen) { if (whichScreen >= 0 && whichScreen < c_MaxScreenCount) { @@ -414,12 +418,12 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::SetColorTable(DrawBlendMode blendMode, std::array colorChannelBlendAmounts) { RTEAssert(blendMode > DrawBlendMode::NoBlend && blendMode < DrawBlendMode::BlendModeCount, "Invalid DrawBlendMode or DrawBlendMode::NoBlend passed into FrameMan::SetColorTable. See DrawBlendMode enumeration for defined values."); - for (int &colorChannelBlendAmount : colorChannelBlendAmounts) { + for (int& colorChannelBlendAmount: colorChannelBlendAmounts) { colorChannelBlendAmount = RoundToNearestMultiple(std::clamp(colorChannelBlendAmount, static_cast(BlendAmountLimits::MinBlend), static_cast(BlendAmountLimits::MaxBlend)), c_BlendAmountStep); } @@ -447,7 +451,7 @@ namespace RTE { if (m_ColorTables[blendMode].find(colorChannelBlendAmounts) == m_ColorTables[blendMode].end()) { m_ColorTables[blendMode].try_emplace(colorChannelBlendAmounts); - std::array adjustedColorChannelBlendAmounts = { BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend }; + std::array adjustedColorChannelBlendAmounts = {BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend}; for (int index = 0; index < adjustedColorChannelBlendAmounts.size(); ++index) { adjustedColorChannelBlendAmounts[index] = 255 - (static_cast(255.0F * 0.01F * static_cast(colorChannelBlendAmounts[index]))); } @@ -466,18 +470,18 @@ namespace RTE { m_ColorTables[blendMode].at(colorChannelBlendAmounts).second = usedPresetTransparencyTable ? -1 : (g_TimerMan.GetAbsoluteTime() / 10000); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::SetTransTableFromPreset(TransparencyPreset transPreset) { RTEAssert(transPreset == TransparencyPreset::LessTrans || transPreset == TransparencyPreset::HalfTrans || transPreset == TransparencyPreset::MoreTrans, "Undefined transparency preset value passed in. See TransparencyPreset enumeration for defined values."); - std::array colorChannelBlendAmounts = { transPreset, transPreset, transPreset, BlendAmountLimits::MinBlend }; + std::array colorChannelBlendAmounts = {transPreset, transPreset, transPreset, BlendAmountLimits::MinBlend}; if (m_ColorTables[DrawBlendMode::BlendTransparency].find(colorChannelBlendAmounts) != m_ColorTables[DrawBlendMode::BlendTransparency].end()) { color_map = &m_ColorTables[DrawBlendMode::BlendTransparency].at(colorChannelBlendAmounts).first; m_ColorTables[DrawBlendMode::BlendTransparency].at(colorChannelBlendAmounts).second = -1; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::CreateNewNetworkPlayerBackBuffer(int player, int width, int height) { for (int f = 0; f < 2; f++) { @@ -490,11 +494,11 @@ namespace RTE { m_PlayerScreenHeight = height; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool FrameMan::LoadPalette(const std::string &palettePath) { + bool FrameMan::LoadPalette(const std::string& palettePath) { const std::string fullPalettePath = g_PresetMan.GetFullModulePath(palettePath); - BITMAP *tempBitmap = load_bitmap(fullPalettePath.c_str(), m_Palette); + BITMAP* tempBitmap = load_bitmap(fullPalettePath.c_str(), m_Palette); RTEAssert(tempBitmap, ("Failed to load palette from bitmap with following path:\n\n" + fullPalettePath).c_str()); set_palette(m_Palette); @@ -508,9 +512,9 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::SaveBitmap(SaveBitmapMode modeToSave, const std::string &nameBase, BITMAP *bitmapToSave) { + int FrameMan::SaveBitmap(SaveBitmapMode modeToSave, const std::string& nameBase, BITMAP* bitmapToSave) { if ((modeToSave == WorldDump || modeToSave == ScenePreviewDump) && !g_ActivityMan.ActivityRunning()) { return 0; } @@ -522,7 +526,7 @@ namespace RTE { #if defined(__GNUC__) && __GNUC__ < 13 std::chrono::time_point now = std::chrono::system_clock::now(); time_t currentTime = std::chrono::system_clock::to_time_t(now); - tm *localCurrentTime = std::localtime(¤tTime); + tm* localCurrentTime = std::localtime(¤tTime); std::array formattedTimeAndDate = {}; std::strftime(formattedTimeAndDate.data(), sizeof(formattedTimeAndDate), "%F_%H-%M-%S", localCurrentTime); @@ -549,10 +553,10 @@ namespace RTE { SaveScreenToBitmap(); // Make a copy of the buffer because it may be overwritten mid thread and everything will be on fire. - BITMAP *outputBitmap = create_bitmap_ex(bitmap_color_depth(m_ScreenDumpBuffer.get()), m_ScreenDumpBuffer->w, m_ScreenDumpBuffer->h); + BITMAP* outputBitmap = create_bitmap_ex(bitmap_color_depth(m_ScreenDumpBuffer.get()), m_ScreenDumpBuffer->w, m_ScreenDumpBuffer->h); stretch_blit(m_ScreenDumpBuffer.get(), outputBitmap, 0, 0, m_ScreenDumpBuffer->w, m_ScreenDumpBuffer->h, 0, 0, outputBitmap->w, outputBitmap->h); - auto saveScreenDump = [fullFileName](BITMAP *bitmapToSaveCopy) { + auto saveScreenDump = [fullFileName](BITMAP* bitmapToSaveCopy) { // nullptr for the PALETTE parameter here because we're saving a 24bpp file and it's irrelevant. if (save_png(fullFileName.c_str(), bitmapToSaveCopy, nullptr) == 0) { g_ConsoleMan.PrintString("SYSTEM: Screen was dumped to: " + fullFileName); @@ -575,7 +579,7 @@ namespace RTE { if (modeToSave == ScenePreviewDump) { DrawWorldDump(true); - BITMAP *scenePreviewDumpBuffer = create_bitmap_ex(c_BPP, c_ScenePreviewWidth, c_ScenePreviewHeight); + BITMAP* scenePreviewDumpBuffer = create_bitmap_ex(c_BPP, c_ScenePreviewWidth, c_ScenePreviewHeight); blit(m_ScenePreviewDumpGradient.get(), scenePreviewDumpBuffer, 0, 0, 0, 0, scenePreviewDumpBuffer->w, scenePreviewDumpBuffer->h); masked_stretch_blit(m_WorldDumpBuffer.get(), scenePreviewDumpBuffer, 0, 0, m_WorldDumpBuffer->w, m_WorldDumpBuffer->h, 0, 0, scenePreviewDumpBuffer->w, scenePreviewDumpBuffer->h); @@ -587,7 +591,7 @@ namespace RTE { } else { DrawWorldDump(); - BITMAP *depthConvertBitmap = create_bitmap_ex(24, m_WorldDumpBuffer->w, m_WorldDumpBuffer->h); + BITMAP* depthConvertBitmap = create_bitmap_ex(24, m_WorldDumpBuffer->w, m_WorldDumpBuffer->h); blit(m_WorldDumpBuffer.get(), depthConvertBitmap, 0, 0, 0, 0, m_WorldDumpBuffer->w, m_WorldDumpBuffer->h); if (save_png(fullFileName.c_str(), depthConvertBitmap, nullptr) == 0) { @@ -616,22 +620,21 @@ namespace RTE { glBindTexture(GL_TEXTURE_2D, g_WindowMan.GetScreenBufferTexture()); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, m_ScreenDumpBuffer->line[0]); - } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::SaveIndexedPNG(const char *fileName, BITMAP *bitmapToSave) const { + int FrameMan::SaveIndexedPNG(const char* fileName, BITMAP* bitmapToSave) const { // nullptr for the PALETTE parameter here because the bitmap is 32bpp and whatever we index it with will end up wrong anyway. save_png(fileName, bitmapToSave, nullptr); int lastColorConversionMode = get_color_conversion(); set_color_conversion(COLORCONV_REDUCE_TO_256); // nullptr for the PALETTE parameter here because we don't need the bad palette from it and don't want it to overwrite anything. - BITMAP *tempLoadBitmap = load_bitmap(fileName, nullptr); + BITMAP* tempLoadBitmap = load_bitmap(fileName, nullptr); std::remove(fileName); - BITMAP *tempConvertingBitmap = create_bitmap_ex(8, bitmapToSave->w, bitmapToSave->h); + BITMAP* tempConvertingBitmap = create_bitmap_ex(8, bitmapToSave->w, bitmapToSave->h); blit(tempLoadBitmap, tempConvertingBitmap, 0, 0, 0, 0, tempConvertingBitmap->w, tempConvertingBitmap->h); int saveResult = save_png(fileName, tempConvertingBitmap, m_Palette); @@ -643,11 +646,13 @@ namespace RTE { return saveResult; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::SharedDrawLine(BITMAP *bitmap, const Vector &start, const Vector &end, int color, int altColor, int skip, int skipStart, bool shortestWrap, bool drawDot, BITMAP *dot) const { + int FrameMan::SharedDrawLine(BITMAP* bitmap, const Vector& start, const Vector& end, int color, int altColor, int skip, int skipStart, bool shortestWrap, bool drawDot, BITMAP* dot) const { RTEAssert(bitmap, "Trying to draw line to null Bitmap"); - if (drawDot) { RTEAssert(dot, "Trying to draw line of dots without specifying a dot Bitmap"); } + if (drawDot) { + RTEAssert(dot, "Trying to draw line of dots without specifying a dot Bitmap"); + } int error = 0; int dom = 0; @@ -663,10 +668,12 @@ namespace RTE { int dotHeight = drawDot ? dot->h : 0; int dotWidth = drawDot ? dot->w : 0; - //acquire_bitmap(bitmap); + // acquire_bitmap(bitmap); // Just make the alt the same color as the main one if no one was specified - if (altColor == 0) { altColor = color; } + if (altColor == 0) { + altColor = color; + } intPos[X] = start.GetFloorIntX(); intPos[Y] = start.GetFloorIntY(); @@ -736,15 +743,15 @@ namespace RTE { } } - //release_bitmap(bitmap); + // release_bitmap(bitmap); // Return the end phase state of the skipping return skipped; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIFont * FrameMan::GetFont(bool isSmall, bool trueColor) { + GUIFont* FrameMan::GetFont(bool isSmall, bool trueColor) { size_t colorIndex = trueColor ? 1 : 0; if (!m_GUIScreens[colorIndex]) { @@ -779,9 +786,9 @@ namespace RTE { return m_LargeFonts[colorIndex]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::UpdateScreenOffsetForSplitScreen(int playerScreen, Vector &screenOffset) const { + void FrameMan::UpdateScreenOffsetForSplitScreen(int playerScreen, Vector& screenOffset) const { switch (playerScreen) { case Players::PlayerTwo: // If both splits, or just VSplit, then in upper right quadrant @@ -807,7 +814,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::Draw() { ZoneScopedN("Draw"); @@ -822,14 +829,14 @@ namespace RTE { std::list screenRelativeEffects; std::list screenRelativeGlowBoxes; - const Activity *pActivity = g_ActivityMan.GetActivity(); + const Activity* pActivity = g_ActivityMan.GetActivity(); for (int playerScreen = 0; playerScreen < screenCount; ++playerScreen) { screenRelativeEffects.clear(); screenRelativeGlowBoxes.clear(); - BITMAP *drawScreen = (screenCount == 1) ? m_BackBuffer8.get() : m_PlayerScreen.get(); - BITMAP *drawScreenGUI = drawScreen; + BITMAP* drawScreen = (screenCount == 1) ? m_BackBuffer8.get() : m_PlayerScreen.get(); + BITMAP* drawScreenGUI = drawScreen; if (IsInMultiplayerMode()) { drawScreen = m_NetworkBackBufferIntermediate8[m_NetworkFrameCurrent][playerScreen].get(); drawScreenGUI = m_NetworkBackBufferIntermediateGUI8[m_NetworkFrameCurrent][playerScreen].get(); @@ -848,7 +855,7 @@ namespace RTE { if (IsInMultiplayerMode()) { int layerCount = 0; - for (const SceneLayer *sceneLayer : g_SceneMan.GetScene()->GetBackLayers()) { + for (const SceneLayer* sceneLayer: g_SceneMan.GetScene()->GetBackLayers()) { SLOffset[playerScreen][layerCount] = sceneLayer->GetOffset(); layerCount++; @@ -862,8 +869,12 @@ namespace RTE { // Adjust the drawing position on the target screen for if the target screen is larger than the scene in non-wrapping dimension. // Scene needs to be displayed centered on the target bitmap then, and that has to be adjusted for when drawing to the screen - if (!g_SceneMan.SceneWrapsX() && drawScreen->w > g_SceneMan.GetSceneWidth()) { targetPos.m_X += (drawScreen->w - g_SceneMan.GetSceneWidth()) / 2; } - if (!g_SceneMan.SceneWrapsY() && drawScreen->h > g_SceneMan.GetSceneHeight()) { targetPos.m_Y += (drawScreen->h - g_SceneMan.GetSceneHeight()) / 2; } + if (!g_SceneMan.SceneWrapsX() && drawScreen->w > g_SceneMan.GetSceneWidth()) { + targetPos.m_X += (drawScreen->w - g_SceneMan.GetSceneWidth()) / 2; + } + if (!g_SceneMan.SceneWrapsY() && drawScreen->h > g_SceneMan.GetSceneHeight()) { + targetPos.m_Y += (drawScreen->h - g_SceneMan.GetSceneHeight()) / 2; + } // Try to move at the frame buffer copy time to maybe prevent wonkyness m_TargetPos[m_NetworkFrameCurrent][playerScreen] = targetPos; @@ -880,7 +891,9 @@ namespace RTE { g_PostProcessMan.GetPostScreenEffectsWrapped(targetPos, drawScreen->w, drawScreen->h, screenRelativeEffects, pActivity->GetTeamOfPlayer(pActivity->PlayerOfScreen(playerScreen))); g_PostProcessMan.GetGlowAreasWrapped(targetPos, drawScreen->w, drawScreen->h, screenRelativeGlowBoxes); - if (IsInMultiplayerMode()) { g_PostProcessMan.SetNetworkPostEffectsList(playerScreen, screenRelativeEffects); } + if (IsInMultiplayerMode()) { + g_PostProcessMan.SetNetworkPostEffectsList(playerScreen, screenRelativeEffects); + } } // TODO: Find out what keeps disabling the clipping on the draw bitmap @@ -893,7 +906,9 @@ namespace RTE { Vector screenOffset; // If we are dealing with split screens, then deal with the fact that we need to draw the player screens to different locations on the final buffer - if (screenCount > 1) { UpdateScreenOffsetForSplitScreen(playerScreen, screenOffset); } + if (screenCount > 1) { + UpdateScreenOffsetForSplitScreen(playerScreen, screenOffset); + } DrawScreenFlash(playerScreen, drawScreenGUI); @@ -926,15 +941,21 @@ namespace RTE { blit(m_NetworkBackBufferFinal8[m_NetworkFrameReady][0].get(), m_BackBuffer8.get(), 0, 0, 0, 0, m_BackBuffer8->w, m_BackBuffer8->h); masked_blit(m_NetworkBackBufferFinalGUI8[m_NetworkFrameReady][0].get(), m_BackBuffer8.get(), 0, 0, 0, 0, m_BackBuffer8->w, m_BackBuffer8->h); - if (g_UInputMan.FlagAltState() || g_UInputMan.FlagCtrlState() || g_UInputMan.FlagShiftState()) { g_PerformanceMan.DrawCurrentPing(); } + if (g_UInputMan.FlagAltState() || g_UInputMan.FlagCtrlState() || g_UInputMan.FlagShiftState()) { + g_PerformanceMan.DrawCurrentPing(); + } m_NetworkBitmapLock[0].unlock(); } } - if (IsInMultiplayerMode()) { PrepareFrameForNetwork(); } + if (IsInMultiplayerMode()) { + PrepareFrameForNetwork(); + } - if (g_ActivityMan.IsInActivity()) { g_PostProcessMan.PostProcess(); } + if (g_ActivityMan.IsInActivity()) { + g_PostProcessMan.PostProcess(); + } // Draw the performance stats and console on top of everything. g_PerformanceMan.Draw(m_BackBuffer32.get()); @@ -946,7 +967,7 @@ namespace RTE { #endif } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::DrawScreenText(int playerScreen, AllegroBitmap playerGUIBitmap) { int textPosY = 0; @@ -958,11 +979,15 @@ namespace RTE { int bufferOrScreenWidth = IsInMultiplayerMode() ? GetPlayerFrameBufferWidth(playerScreen) : GetPlayerScreenWidth(); int bufferOrScreenHeight = IsInMultiplayerMode() ? GetPlayerFrameBufferHeight(playerScreen) : GetPlayerScreenHeight(); - if (m_TextCentered[playerScreen]) { textPosY = (bufferOrScreenHeight / 2) - 52; } + if (m_TextCentered[playerScreen]) { + textPosY = (bufferOrScreenHeight / 2) - 52; + } int screenOcclusionOffsetX = g_CameraMan.GetScreenOcclusion(playerScreen).GetRoundIntX(); // If there's really no room to offset the text into, then don't - if (GetPlayerScreenWidth() <= g_WindowMan.GetResX() / 2) { screenOcclusionOffsetX = 0; } + if (GetPlayerScreenWidth() <= g_WindowMan.GetResX() / 2) { + screenOcclusionOffsetX = 0; + } std::string screenTextToDraw = m_ScreenText[playerScreen]; if (m_TextBlinking[playerScreen] && m_TextBlinkTimer.AlternateReal(m_TextBlinking[playerScreen])) { @@ -992,9 +1017,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::DrawScreenFlash(int playerScreen, BITMAP *playerGUIBitmap) { + void FrameMan::DrawScreenFlash(int playerScreen, BITMAP* playerGUIBitmap) { if (m_FlashScreenColor[playerScreen] != -1) { // If set to flash for a period of time, first be solid and then start flashing slower double timeTillLimit = m_FlashTimer[playerScreen].LeftTillRealTimeLimitMS(); @@ -1007,11 +1032,13 @@ namespace RTE { m_FlashedLastFrame[playerScreen] = true; } } - if (m_FlashTimer[playerScreen].IsPastRealTimeLimit()) { m_FlashScreenColor[playerScreen] = -1; } + if (m_FlashTimer[playerScreen].IsPastRealTimeLimit()) { + m_FlashScreenColor[playerScreen] = -1; + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::DrawWorldDump(bool drawForScenePreview) const { float worldBitmapWidth = static_cast(m_WorldDumpBuffer->w); @@ -1035,7 +1062,7 @@ namespace RTE { // If we're not dumping a scene preview, draw objects and post-effects. if (!drawForScenePreview) { std::list postEffectsList; - BITMAP *effectBitmap = nullptr; + BITMAP* effectBitmap = nullptr; int effectPosX = 0; int effectPosY = 0; int effectStrength = 0; @@ -1047,7 +1074,7 @@ namespace RTE { // Draw post-effects g_PostProcessMan.GetPostScreenEffectsWrapped(targetPos, worldBitmapWidth, worldBitmapHeight, postEffectsList, -1); - for (const PostEffect &postEffect : postEffectsList) { + for (const PostEffect& postEffect: postEffectsList) { effectBitmap = postEffect.m_Bitmap; effectStrength = postEffect.m_Strength; set_screen_blender(effectStrength, effectStrength, effectStrength, effectStrength); @@ -1057,7 +1084,7 @@ namespace RTE { if (postEffect.m_Angle == 0.0F) { draw_trans_sprite(m_WorldDumpBuffer.get(), effectBitmap, effectPosX, effectPosY); } else { - BITMAP *targetBitmap = g_PostProcessMan.GetTempEffectBitmap(effectBitmap); + BITMAP* targetBitmap = g_PostProcessMan.GetTempEffectBitmap(effectBitmap); clear_to_color(targetBitmap, 0); Matrix newAngle(postEffect.m_Angle); @@ -1068,7 +1095,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FrameMan::PrepareFrameForNetwork() { int dx = 0; @@ -1112,4 +1139,4 @@ namespace RTE { m_NetworkFrameReady = m_NetworkFrameCurrent; m_NetworkFrameCurrent = (m_NetworkFrameCurrent == 0) ? 1 : 0; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Managers/FrameMan.h b/Source/Managers/FrameMan.h index 55d74eb619..757469a9a1 100644 --- a/Source/Managers/FrameMan.h +++ b/Source/Managers/FrameMan.h @@ -15,7 +15,7 @@ namespace RTE { class ScreenShader; struct BitmapDeleter { - void operator() (BITMAP *bitmap) const; + void operator()(BITMAP* bitmap) const; }; /// @@ -26,7 +26,6 @@ namespace RTE { friend class WindowMan; public: - static constexpr int c_BPP = 32; //!< Color depth (bits per pixel). Vector SLOffset[c_MaxScreenCount][c_MaxLayersStoredForNetwork]; //!< SceneLayer offsets for each screen in online multiplayer. @@ -73,19 +72,19 @@ namespace RTE { /// Gets the 8bpp backbuffer bitmap. /// /// A pointer to the BITMAP 8bpp backbuffer. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetBackBuffer8() const { return m_BackBuffer8.get(); } + BITMAP* GetBackBuffer8() const { return m_BackBuffer8.get(); } /// /// Gets the 32bpp backbuffer bitmap. Make sure you don't do any blending stuff to the 8bpp one! /// /// A pointer to the BITMAP 32bpp backbuffer. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetBackBuffer32() const { return m_BackBuffer32.get(); } + BITMAP* GetBackBuffer32() const { return m_BackBuffer32.get(); } /// /// Gets the 32bpp bitmap that is used for overlaying the screen. /// /// A pointer to the overlay BITMAP. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetOverlayBitmap32() const { return m_OverlayBitmap32.get(); } + BITMAP* GetOverlayBitmap32() const { return m_OverlayBitmap32.get(); } #pragma endregion #pragma region Split-Screen Handling @@ -178,14 +177,14 @@ namespace RTE { /// /// Whether to get the 32bpp color version of the font. /// A pointer to the requested font, or 0 if no small font was found. - GUIFont * GetSmallFont(bool trueColor = false) { return GetFont(true, trueColor); } + GUIFont* GetSmallFont(bool trueColor = false) { return GetFont(true, trueColor); } /// /// Gets the large font from the GUI engine's current skin. Ownership is NOT transferred! /// /// Whether to get the 32bpp color version of the font. /// A pointer to the requested font, or 0 if no large font was found. - GUIFont * GetLargeFont(bool trueColor = false) { return GetFont(false, trueColor); } + GUIFont* GetLargeFont(bool trueColor = false) { return GetFont(false, trueColor); } /// /// Calculates the width of a text string using the given font size. @@ -193,7 +192,7 @@ namespace RTE { /// Text string. /// Whether to use small or large font. /// Width of the text string. - int CalculateTextWidth(const std::string &text, bool isSmall); + int CalculateTextWidth(const std::string& text, bool isSmall); /// /// Calculates the height of a text string using the given font size. @@ -202,7 +201,7 @@ namespace RTE { /// Maximum width of the text string. /// Whether to use small or large font. /// Height of the text string. - int CalculateTextHeight(const std::string &text, int maxWidth, bool isSmall); + int CalculateTextHeight(const std::string& text, int maxWidth, bool isSmall); /// /// Gets a copy of the passed in string, split into multiple lines as needed to fit within the specified width limit, based on the font to use. @@ -211,7 +210,7 @@ namespace RTE { /// The maximum width each line of the string can be. /// The font the string will use for calculating the string's width. /// A copy of the passed in string, split into multiple lines as needed. - std::string SplitStringToFitWidth(const std::string &stringToSplit, int widthLimit, bool useSmallFont); + std::string SplitStringToFitWidth(const std::string& stringToSplit, int widthLimit, bool useSmallFont); /// /// Gets the message to be displayed on top of each player's screen. @@ -228,7 +227,7 @@ namespace RTE { /// The interval with which the screen will be blinking, in ms. 0 means no blinking. /// The duration, in MS to force this message to display. No other message can be displayed before this expires. ClearScreenText overrides it though. /// Vertically centered on the screen. - void SetScreenText(const std::string &message, int whichScreen = 0, int blinkInterval = 0, int displayDuration = -1, bool centered = false); + void SetScreenText(const std::string& message, int whichScreen = 0, int blinkInterval = 0, int displayDuration = -1, bool centered = false); /// /// Clears the message to be displayed on top of each player's screen. @@ -267,7 +266,11 @@ namespace RTE { /// Which screen to flash. /// What color to flash it. -1 means no color or flash. /// How long a period to fill the frame with color. If 0, a single-frame flash will happen. - void FlashScreen(int screen, int color, float periodMS = 0) { m_FlashScreenColor[screen] = color; m_FlashTimer[screen].SetRealTimeLimitMS(periodMS); m_FlashTimer[screen].Reset(); } + void FlashScreen(int screen, int color, float periodMS = 0) { + m_FlashScreenColor[screen] = color; + m_FlashTimer[screen].SetRealTimeLimitMS(periodMS); + m_FlashTimer[screen].Reset(); + } /// /// Draws a line that can be dotted or with other effects. @@ -281,7 +284,7 @@ namespace RTE { /// The start of the skipping phase. If skip is 10 and this is 5, the first dot will be drawn after 5 pixels. /// Whether the line should take the shortest possible route across scene wraps. /// The end state of the skipping phase. Eg if 4 is returned here the last dot was placed 4 pixels ago. - int DrawLine(BITMAP *bitmap, const Vector &start, const Vector &end, int color, int altColor = 0, int skip = 0, int skipStart = 0, bool shortestWrap = false) const { + int DrawLine(BITMAP* bitmap, const Vector& start, const Vector& end, int color, int altColor = 0, int skip = 0, int skipStart = 0, bool shortestWrap = false) const { return SharedDrawLine(bitmap, start, end, color, altColor, skip, skipStart, shortestWrap, false, nullptr); } @@ -296,7 +299,7 @@ namespace RTE { /// The start of the skipping phase. If skip is 10 and this is 5, the first dot will be drawn after 5 pixels. /// Whether the line should take the shortest possible route across scene wraps. /// The end state of the skipping phase. Eg if 4 is returned here the last dot was placed 4 pixels ago. - int DrawDotLine(BITMAP *bitmap, const Vector &start, const Vector &end, BITMAP *dot, int skip = 0, int skipStart = 0, bool shortestWrap = false) const { + int DrawDotLine(BITMAP* bitmap, const Vector& start, const Vector& end, BITMAP* dot, int skip = 0, int skipStart = 0, bool shortestWrap = false) const { return SharedDrawLine(bitmap, start, end, 0, 0, skip, skipStart, shortestWrap, true, dot); } #pragma endregion @@ -333,56 +336,56 @@ namespace RTE { /// /// Which player screen to get backbuffer bitmap for. /// A pointer to the 8bpp backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetNetworkBackBuffer8Ready(int player) const { return m_NetworkBackBufferFinal8[m_NetworkFrameReady][player].get(); } + BITMAP* GetNetworkBackBuffer8Ready(int player) const { return m_NetworkBackBufferFinal8[m_NetworkFrameReady][player].get(); } /// /// Gets the ready 8bpp backbuffer GUI bitmap used to draw network transmitted image on top of everything. /// /// Which player screen to get GUI backbuffer bitmap for. /// A pointer to the 8bpp GUI backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetNetworkBackBufferGUI8Ready(int player) const { return m_NetworkBackBufferFinalGUI8[m_NetworkFrameReady][player].get(); } + BITMAP* GetNetworkBackBufferGUI8Ready(int player) const { return m_NetworkBackBufferFinalGUI8[m_NetworkFrameReady][player].get(); } /// /// Gets the current 8bpp backbuffer bitmap used to draw network transmitted image on top of everything. /// /// Which player screen to get backbuffer bitmap for. /// A pointer to the 8bpp backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetNetworkBackBuffer8Current(int player) const { return m_NetworkBackBufferFinal8[m_NetworkFrameCurrent][player].get(); } + BITMAP* GetNetworkBackBuffer8Current(int player) const { return m_NetworkBackBufferFinal8[m_NetworkFrameCurrent][player].get(); } /// /// Gets the current 8bpp backbuffer GUI bitmap used to draw network transmitted image on top of everything. /// /// Which player screen to get backbuffer bitmap for. /// A pointer to the 8bpp GUI backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetNetworkBackBufferGUI8Current(int player) const { return m_NetworkBackBufferFinalGUI8[m_NetworkFrameCurrent][player].get(); } + BITMAP* GetNetworkBackBufferGUI8Current(int player) const { return m_NetworkBackBufferFinalGUI8[m_NetworkFrameCurrent][player].get(); } /// /// Gets the ready 8bpp intermediate backbuffer bitmap used to copy network transmitted image to before sending. /// /// Which player screen to get intermediate bitmap for. /// A pointer to the 8bpp intermediate BITMAP. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetNetworkBackBufferIntermediate8Ready(int player) const { return m_NetworkBackBufferIntermediate8[m_NetworkFrameReady][player].get(); } + BITMAP* GetNetworkBackBufferIntermediate8Ready(int player) const { return m_NetworkBackBufferIntermediate8[m_NetworkFrameReady][player].get(); } /// /// Gets the ready 8bpp intermediate backbuffer GUI bitmap used to copy network transmitted image to before sending. /// /// Which player screen to get intermediate GUI bitmap for. /// A pointer to the 8bpp intermediate GUI BITMAP. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetNetworkBackBufferIntermediate8Current(int player) const { return m_NetworkBackBufferIntermediate8[m_NetworkFrameCurrent][player].get(); } + BITMAP* GetNetworkBackBufferIntermediate8Current(int player) const { return m_NetworkBackBufferIntermediate8[m_NetworkFrameCurrent][player].get(); } /// /// Gets the current 8bpp intermediate backbuffer bitmap used to copy network transmitted image to before sending. /// /// Which player screen to get intermediate bitmap for. /// A pointer to the 8bpp intermediate BITMAP. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetNetworkBackBufferIntermediateGUI8Ready(int player) const { return m_NetworkBackBufferIntermediateGUI8[m_NetworkFrameReady][player].get(); } + BITMAP* GetNetworkBackBufferIntermediateGUI8Ready(int player) const { return m_NetworkBackBufferIntermediateGUI8[m_NetworkFrameReady][player].get(); } /// /// Gets the current 8bpp intermediate backbuffer GUI bitmap used to copy network transmitted image to before sending. /// /// Which player screen to get intermediate GUI bitmap for. /// A pointer to the 8bpp intermediate GUI BITMAP. OWNERSHIP IS NOT TRANSFERRED! - BITMAP * GetNetworkBackBufferIntermediateGUI8Current(int player) const { return m_NetworkBackBufferIntermediateGUI8[m_NetworkFrameCurrent][player].get(); } + BITMAP* GetNetworkBackBufferIntermediateGUI8Current(int player) const { return m_NetworkBackBufferIntermediateGUI8[m_NetworkFrameCurrent][player].get(); } // TODO: Figure out. /// @@ -425,19 +428,23 @@ namespace RTE { /// /// String with the path to the palette bitmap file. /// Whether palette loaded successfully or not. - bool LoadPalette(const std::string &palettePath); + bool LoadPalette(const std::string& palettePath); /// /// Gets the ContentFile describing the location of the color palette. /// /// An reference to a ContentFile which described the palette location. - const ContentFile & GetPaletteFile() const { return m_PaletteFile; } + const ContentFile& GetPaletteFile() const { return m_PaletteFile; } /// /// Fades the palette in from black at a specified speed. /// /// Speed specified from (slowest) 1 - 64 (fastest). - void FadeInPalette(int fadeSpeed = 1) { PALETTE pal; get_palette(pal); fade_in(pal, Limit(fadeSpeed, 64, 1)); } + void FadeInPalette(int fadeSpeed = 1) { + PALETTE pal; + get_palette(pal); + fade_in(pal, Limit(fadeSpeed, 64, 1)); + } /// /// Fades the palette out to black at a specified speed. @@ -453,36 +460,40 @@ namespace RTE { /// The individual bitmap that will be dumped. /// The filename of the file to save to, WITHOUT EXTENSION. /// 0 for success, anything below 0 is a sign of failure. - int SaveBitmapToPNG(BITMAP *bitmap, const char *nameBase) { return SaveBitmap(SingleBitmap, nameBase, bitmap); } + int SaveBitmapToPNG(BITMAP* bitmap, const char* nameBase) { return SaveBitmap(SingleBitmap, nameBase, bitmap); } /// /// Dumps a bitmap of the screen backbuffer to a 8bpp PNG file. /// /// The filename of the file to save to, WITHOUT EXTENSION. /// 0 for success, anything below 0 is a sign of failure. - int SaveScreenToPNG(const char *nameBase) { return SaveBitmap(ScreenDump, nameBase); } + int SaveScreenToPNG(const char* nameBase) { return SaveBitmap(ScreenDump, nameBase); } /// /// Dumps a bitmap of everything on the scene to a PNG file. /// /// The filename of the file to save to, WITHOUT EXTENSION. /// 0 for success, anything below 0 is a sign of failure. - int SaveWorldToPNG(const char *nameBase) { return SaveBitmap(WorldDump, nameBase); } + int SaveWorldToPNG(const char* nameBase) { return SaveBitmap(WorldDump, nameBase); } /// /// Dumps a miniature screenshot of the whole scene to be used as a preview to a PNG file. /// /// The filename of the file to save to, WITHOUT EXTENSION. /// 0 for success, anything below 0 is a sign of failure. - int SaveWorldPreviewToPNG(const char *nameBase) { return SaveBitmap(ScenePreviewDump, nameBase); } + int SaveWorldPreviewToPNG(const char* nameBase) { return SaveBitmap(ScenePreviewDump, nameBase); } #pragma endregion private: - /// /// Enumeration with different settings for the SaveBitmap() method. /// - enum SaveBitmapMode { SingleBitmap, ScreenDump, WorldDump, ScenePreviewDump }; + enum SaveBitmapMode { + SingleBitmap, + ScreenDump, + WorldDump, + ScenePreviewDump + }; static const std::array, DrawBlendMode::BlendModeCount> c_BlenderSetterFunctions; //!< Array of function references to Allegro blender setters for convenient access when creating new color tables. @@ -509,9 +520,9 @@ namespace RTE { int m_PlayerScreenWidth; //!< Width of the screen of each player. Will be smaller than resolution only if the screen is split. int m_PlayerScreenHeight; //!< Height of the screen of each player. Will be smaller than resolution only if the screen is split. - std::array m_GUIScreens; //!< GUI screen objects kept and owned just for the fonts. - std::array m_SmallFonts; //!< Pointers to the standard small font for quick access. - std::array m_LargeFonts; //!< Pointers to the standard large font for quick access. + std::array m_GUIScreens; //!< GUI screen objects kept and owned just for the fonts. + std::array m_SmallFonts; //!< Pointers to the standard small font for quick access. + std::array m_LargeFonts; //!< Pointers to the standard large font for quick access. std::string m_ScreenText[c_MaxScreenCount]; //!< The text to be displayed on each player's screen. bool m_TextCentered[c_MaxScreenCount]; //!< Whether screen text is centered vertically. @@ -569,7 +580,7 @@ namespace RTE { /// /// The player screen to update offset for. /// Vector representing the screen offset. - void UpdateScreenOffsetForSplitScreen(int playerScreen, Vector &screenOffset) const; + void UpdateScreenOffsetForSplitScreen(int playerScreen, Vector& screenOffset) const; /// /// Draws all the text messages to the specified player screen. This is called during Draw(). @@ -583,7 +594,7 @@ namespace RTE { /// /// The player screen the flash effect will be shown to. /// The bitmap the flash effect will be drawn on. - void DrawScreenFlash(int playerScreen, BITMAP *playerGUIBitmap); + void DrawScreenFlash(int playerScreen, BITMAP* playerGUIBitmap); /// /// Renders current frame and marks it ready for network transmission. This is called during Draw(). @@ -608,7 +619,7 @@ namespace RTE { /// /// The individual bitmap that will be dumped. 0 or nullptr if not in SingleBitmap mode. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int SaveBitmap(SaveBitmapMode modeToSave, const std::string &nameBase, BITMAP *bitmapToSave = nullptr); + int SaveBitmap(SaveBitmapMode modeToSave, const std::string& nameBase, BITMAP* bitmapToSave = nullptr); /// /// Saves the front buffer to the screen dump buffer. @@ -626,7 +637,7 @@ namespace RTE { /// It works by first saving the 32bpp bitmap as is, then loading it back under the REDUCE_TO_256 color conversion mode, blitting it to a fresh bitmap and saving it again with the passed in palette. /// The re-blitted bitmap is properly 8bpp and will be indexed correctly. The old saved file is deleted in the process before the new one is saved. /// - int SaveIndexedPNG(const char *fileName, BITMAP *bitmapToSave) const; + int SaveIndexedPNG(const char* fileName, BITMAP* bitmapToSave) const; #pragma endregion /// @@ -643,7 +654,7 @@ namespace RTE { /// Whether to draw a regular line or a dot line. True for dot line. /// The bitmap to be used for dots (will be centered). /// The end state of the skipping phase. Eg if 4 is returned here the last dot was placed 4 pixels ago. - int SharedDrawLine(BITMAP *bitmap, const Vector &start, const Vector &end, int color, int altColor = 0, int skip = 0, int skipStart = 0, bool shortestWrap = false, bool drawDot = false, BITMAP *dot = nullptr) const; + int SharedDrawLine(BITMAP* bitmap, const Vector& start, const Vector& end, int color, int altColor = 0, int skip = 0, int skipStart = 0, bool shortestWrap = false, bool drawDot = false, BITMAP* dot = nullptr) const; /// /// Gets the requested font from the GUI engine's current skin. Ownership is NOT transferred! @@ -651,7 +662,7 @@ namespace RTE { /// Size of font to get. True for small font, false for large font. /// Whether to get the 32bpp color version of the font. /// A pointer to the requested font, or 0 if no font was found. - GUIFont * GetFont(bool isSmall, bool trueColor); + GUIFont* GetFont(bool isSmall, bool trueColor); /// /// Clears all the member variables of this FrameMan, effectively resetting the members of this abstraction level only. @@ -659,8 +670,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - FrameMan(const FrameMan &reference) = delete; - FrameMan & operator=(const FrameMan &rhs) = delete; + FrameMan(const FrameMan& reference) = delete; + FrameMan& operator=(const FrameMan& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index 33e68b48a7..0e6aa9734d 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -9,9 +9,9 @@ namespace RTE { - const std::unordered_set LuaMan::c_FileAccessModes = { "r", "r+", "w", "w+", "a", "a+", "rt", "wt"}; + const std::unordered_set LuaMan::c_FileAccessModes = {"r", "r+", "w", "w+", "a", "a+", "rt", "wt"}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaStateWrapper::Clear() { m_State = nullptr; @@ -21,7 +21,7 @@ namespace RTE { m_CurrentlyRunningScriptPath = ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaStateWrapper::Initialize() { m_State = luaL_newstate(); @@ -32,17 +32,17 @@ namespace RTE { lua_gc(m_State, LUA_GCSTOP, 0); const luaL_Reg libsToLoad[] = { - { LUA_COLIBNAME, luaopen_base }, - { LUA_LOADLIBNAME, luaopen_package }, - { LUA_TABLIBNAME, luaopen_table }, - { LUA_STRLIBNAME, luaopen_string }, - { LUA_MATHLIBNAME, luaopen_math }, - { LUA_DBLIBNAME, luaopen_debug }, - { LUA_JITLIBNAME, luaopen_jit }, - { NULL, NULL } // End of array + {LUA_COLIBNAME, luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_DBLIBNAME, luaopen_debug}, + {LUA_JITLIBNAME, luaopen_jit}, + {NULL, NULL} // End of array }; - for (const luaL_Reg *lib = libsToLoad; lib->func; lib++) { + for (const luaL_Reg* lib = libsToLoad; lib->func; lib++) { if (g_SettingsMan.DisableLuaJIT() && strcmp(lib->name, LUA_JITLIBNAME) == 0) { continue; } @@ -52,156 +52,156 @@ namespace RTE { } // LuaJIT should start automatically after we load the library (if we loaded it) but we're making sure it did anyway. - if (!g_SettingsMan.DisableLuaJIT() && !luaJIT_setmode(m_State, 0, LUAJIT_MODE_ENGINE | LUAJIT_MODE_ON)) { RTEAbort("Failed to initialize LuaJIT!\nIf this error persists, please disable LuaJIT with \"Settings.ini\" property \"DisableLuaJIT\"."); } + if (!g_SettingsMan.DisableLuaJIT() && !luaJIT_setmode(m_State, 0, LUAJIT_MODE_ENGINE | LUAJIT_MODE_ON)) { + RTEAbort("Failed to initialize LuaJIT!\nIf this error persists, please disable LuaJIT with \"Settings.ini\" property \"DisableLuaJIT\"."); + } // From LuaBind documentation: // As mentioned in the Lua documentation, it is possible to pass an error handler function to lua_pcall(). LuaBind makes use of lua_pcall() internally when calling member functions and free functions. // It is possible to set the error handler function that LuaBind will use globally: - //set_pcall_callback(&AddFileAndLineToError); // NOTE: this seems to do nothing because retrieving the error from the lua stack wasn't done correctly. The current error handling works just fine but might look into doing this properly sometime later. + // set_pcall_callback(&AddFileAndLineToError); // NOTE: this seems to do nothing because retrieving the error from the lua stack wasn't done correctly. The current error handling works just fine but might look into doing this properly sometime later. // Register all relevant bindings to the state. Note that the order of registration is important, as bindings can't derive from an unregistered type (inheritance and all that). - luabind::module(m_State)[ - luabind::class_("LuaManager") - .property("TempEntity", &LuaStateWrapper::GetTempEntity) - .property("TempEntities", &LuaStateWrapper::GetTempEntityVector, luabind::return_stl_iterator) - .def("SelectRand", &LuaStateWrapper::SelectRand) - .def("RangeRand", &LuaStateWrapper::RangeRand) - .def("PosRand", &LuaStateWrapper::PosRand) - .def("NormalRand", &LuaStateWrapper::NormalRand) - .def("GetDirectoryList", &LuaStateWrapper::DirectoryList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("GetFileList", &LuaStateWrapper::FileList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) - .def("FileExists", &LuaStateWrapper::FileExists) - .def("DirectoryExists", &LuaStateWrapper::DirectoryExists) - .def("IsValidModulePath", &LuaStateWrapper::IsValidModulePath) - .def("FileOpen", &LuaStateWrapper::FileOpen) - .def("FileClose", &LuaStateWrapper::FileClose) - .def("FileRemove", &LuaStateWrapper::FileRemove) - .def("DirectoryCreate", &LuaStateWrapper::DirectoryCreate1) - .def("DirectoryCreate", &LuaStateWrapper::DirectoryCreate2) - .def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove1) - .def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove2) - .def("FileRename", &LuaStateWrapper::FileRename) - .def("DirectoryRename", &LuaStateWrapper::DirectoryRename) - .def("FileReadLine", &LuaStateWrapper::FileReadLine) - .def("FileWriteLine", &LuaStateWrapper::FileWriteLine) - .def("FileEOF", &LuaStateWrapper::FileEOF), - - luabind::def("DeleteEntity", &LuaAdaptersUtility::DeleteEntity, luabind::adopt(_1)), // NOT a member function, so adopting _1 instead of the _2 for the first param, since there's no "this" pointer!! - luabind::def("LERP", &LERP), - luabind::def("EaseIn", &EaseIn), - luabind::def("EaseOut", &EaseOut), - luabind::def("EaseInOut", &EaseInOut), - luabind::def("Clamp", &Limit), - luabind::def("NormalizeAngleBetween0And2PI", &NormalizeAngleBetween0And2PI), - luabind::def("NormalizeAngleBetweenNegativePIAndPI", &NormalizeAngleBetweenNegativePIAndPI), - luabind::def("AngleWithinRange", &AngleWithinRange), - luabind::def("ClampAngle", &ClampAngle), - luabind::def("GetPPM", &LuaAdaptersUtility::GetPPM), - luabind::def("GetMPP", &LuaAdaptersUtility::GetMPP), - luabind::def("GetPPL", &LuaAdaptersUtility::GetPPL), - luabind::def("GetLPP", &LuaAdaptersUtility::GetLPP), - luabind::def("GetPathFindingDefaultDigStrength", &LuaAdaptersUtility::GetPathFindingDefaultDigStrength), - luabind::def("RoundFloatToPrecision", &RoundFloatToPrecision), - luabind::def("RoundToNearestMultiple", &RoundToNearestMultiple), - - RegisterLuaBindingsOfType(SystemLuaBindings, Vector), - RegisterLuaBindingsOfType(SystemLuaBindings, Box), - RegisterLuaBindingsOfType(EntityLuaBindings, Entity), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, SoundContainer), - RegisterLuaBindingsOfType(EntityLuaBindings, SoundSet), - RegisterLuaBindingsOfType(EntityLuaBindings, LimbPath), - RegisterLuaBindingsOfAbstractType(EntityLuaBindings, SceneObject), - RegisterLuaBindingsOfAbstractType(EntityLuaBindings, MovableObject), - RegisterLuaBindingsOfType(EntityLuaBindings, Material), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, MOPixel), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, TerrainObject), - RegisterLuaBindingsOfAbstractType(EntityLuaBindings, MOSprite), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, MOSParticle), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, MOSRotating), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Attachable), - RegisterLuaBindingsOfAbstractType(EntityLuaBindings, Emission), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, AEmitter), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, AEJetpack), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, PEmitter), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Actor), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ADoor), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Arm), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Leg), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, AHuman), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ACrab), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Turret), - RegisterLuaBindingsOfAbstractType(EntityLuaBindings, ACraft), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ACDropShip), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ACRocket), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, HeldDevice), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Magazine), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Round), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, HDFirearm), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ThrownDevice), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, TDExplosive), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, PieSlice), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, PieMenu), - RegisterLuaBindingsOfType(EntityLuaBindings, Gib), - RegisterLuaBindingsOfType(SystemLuaBindings, Controller), - RegisterLuaBindingsOfType(SystemLuaBindings, Timer), - RegisterLuaBindingsOfType(SystemLuaBindings, PathRequest), - RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Scene), - RegisterLuaBindingsOfType(EntityLuaBindings, SceneArea), - RegisterLuaBindingsOfType(EntityLuaBindings, SceneLayer), - RegisterLuaBindingsOfType(EntityLuaBindings, SLBackground), - RegisterLuaBindingsOfAbstractType(EntityLuaBindings, Deployment), - RegisterLuaBindingsOfType(SystemLuaBindings, DataModule), - RegisterLuaBindingsOfType(ActivityLuaBindings, Activity), - RegisterLuaBindingsOfAbstractType(ActivityLuaBindings, GameActivity), - RegisterLuaBindingsOfAbstractType(EntityLuaBindings, GlobalScript), - RegisterLuaBindingsOfType(EntityLuaBindings, MetaPlayer), - RegisterLuaBindingsOfType(GUILuaBindings, GUIBanner), - RegisterLuaBindingsOfType(GUILuaBindings, BuyMenuGUI), - RegisterLuaBindingsOfType(GUILuaBindings, SceneEditorGUI), - RegisterLuaBindingsOfType(ManagerLuaBindings, ActivityMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, AudioMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, CameraMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, ConsoleMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, FrameMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, MetaMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, MovableMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, PerformanceMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, PostProcessMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, PresetMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, PrimitiveMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, SceneMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, SettingsMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, TimerMan), - RegisterLuaBindingsOfType(ManagerLuaBindings, UInputMan), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, GraphicalPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, LinePrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, ArcPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, SplinePrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, BoxPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, BoxFillPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, RoundedBoxPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, RoundedBoxFillPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, CirclePrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, CircleFillPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, EllipsePrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, EllipseFillPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, TrianglePrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, TriangleFillPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, TextPrimitive), - RegisterLuaBindingsOfType(PrimitiveLuaBindings, BitmapPrimitive), - RegisterLuaBindingsOfType(InputLuaBindings, InputDevice), - RegisterLuaBindingsOfType(InputLuaBindings, InputElements), - RegisterLuaBindingsOfType(InputLuaBindings, JoyButtons), - RegisterLuaBindingsOfType(InputLuaBindings, JoyDirections), - RegisterLuaBindingsOfType(InputLuaBindings, MouseButtons), - RegisterLuaBindingsOfType(InputLuaBindings, SDL_Keycode), - RegisterLuaBindingsOfType(InputLuaBindings, SDL_Scancode), - RegisterLuaBindingsOfType(InputLuaBindings, SDL_GameControllerButton), - RegisterLuaBindingsOfType(InputLuaBindings, SDL_GameControllerAxis), - RegisterLuaBindingsOfType(MiscLuaBindings, AlarmEvent), - RegisterLuaBindingsOfType(MiscLuaBindings, Directions), - RegisterLuaBindingsOfType(MiscLuaBindings, DrawBlendMode) - ]; + luabind::module(m_State)[luabind::class_("LuaManager") + .property("TempEntity", &LuaStateWrapper::GetTempEntity) + .property("TempEntities", &LuaStateWrapper::GetTempEntityVector, luabind::return_stl_iterator) + .def("SelectRand", &LuaStateWrapper::SelectRand) + .def("RangeRand", &LuaStateWrapper::RangeRand) + .def("PosRand", &LuaStateWrapper::PosRand) + .def("NormalRand", &LuaStateWrapper::NormalRand) + .def("GetDirectoryList", &LuaStateWrapper::DirectoryList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("GetFileList", &LuaStateWrapper::FileList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) + .def("FileExists", &LuaStateWrapper::FileExists) + .def("DirectoryExists", &LuaStateWrapper::DirectoryExists) + .def("IsValidModulePath", &LuaStateWrapper::IsValidModulePath) + .def("FileOpen", &LuaStateWrapper::FileOpen) + .def("FileClose", &LuaStateWrapper::FileClose) + .def("FileRemove", &LuaStateWrapper::FileRemove) + .def("DirectoryCreate", &LuaStateWrapper::DirectoryCreate1) + .def("DirectoryCreate", &LuaStateWrapper::DirectoryCreate2) + .def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove1) + .def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove2) + .def("FileRename", &LuaStateWrapper::FileRename) + .def("DirectoryRename", &LuaStateWrapper::DirectoryRename) + .def("FileReadLine", &LuaStateWrapper::FileReadLine) + .def("FileWriteLine", &LuaStateWrapper::FileWriteLine) + .def("FileEOF", &LuaStateWrapper::FileEOF), + + luabind::def("DeleteEntity", &LuaAdaptersUtility::DeleteEntity, luabind::adopt(_1)), // NOT a member function, so adopting _1 instead of the _2 for the first param, since there's no "this" pointer!! + luabind::def("LERP", &LERP), + luabind::def("EaseIn", &EaseIn), + luabind::def("EaseOut", &EaseOut), + luabind::def("EaseInOut", &EaseInOut), + luabind::def("Clamp", &Limit), + luabind::def("NormalizeAngleBetween0And2PI", &NormalizeAngleBetween0And2PI), + luabind::def("NormalizeAngleBetweenNegativePIAndPI", &NormalizeAngleBetweenNegativePIAndPI), + luabind::def("AngleWithinRange", &AngleWithinRange), + luabind::def("ClampAngle", &ClampAngle), + luabind::def("GetPPM", &LuaAdaptersUtility::GetPPM), + luabind::def("GetMPP", &LuaAdaptersUtility::GetMPP), + luabind::def("GetPPL", &LuaAdaptersUtility::GetPPL), + luabind::def("GetLPP", &LuaAdaptersUtility::GetLPP), + luabind::def("GetPathFindingDefaultDigStrength", &LuaAdaptersUtility::GetPathFindingDefaultDigStrength), + luabind::def("RoundFloatToPrecision", &RoundFloatToPrecision), + luabind::def("RoundToNearestMultiple", &RoundToNearestMultiple), + + RegisterLuaBindingsOfType(SystemLuaBindings, Vector), + RegisterLuaBindingsOfType(SystemLuaBindings, Box), + RegisterLuaBindingsOfType(EntityLuaBindings, Entity), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, SoundContainer), + RegisterLuaBindingsOfType(EntityLuaBindings, SoundSet), + RegisterLuaBindingsOfType(EntityLuaBindings, LimbPath), + RegisterLuaBindingsOfAbstractType(EntityLuaBindings, SceneObject), + RegisterLuaBindingsOfAbstractType(EntityLuaBindings, MovableObject), + RegisterLuaBindingsOfType(EntityLuaBindings, Material), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, MOPixel), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, TerrainObject), + RegisterLuaBindingsOfAbstractType(EntityLuaBindings, MOSprite), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, MOSParticle), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, MOSRotating), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Attachable), + RegisterLuaBindingsOfAbstractType(EntityLuaBindings, Emission), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, AEmitter), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, AEJetpack), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, PEmitter), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Actor), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ADoor), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Arm), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Leg), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, AHuman), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ACrab), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Turret), + RegisterLuaBindingsOfAbstractType(EntityLuaBindings, ACraft), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ACDropShip), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ACRocket), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, HeldDevice), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Magazine), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Round), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, HDFirearm), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, ThrownDevice), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, TDExplosive), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, PieSlice), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, PieMenu), + RegisterLuaBindingsOfType(EntityLuaBindings, Gib), + RegisterLuaBindingsOfType(SystemLuaBindings, Controller), + RegisterLuaBindingsOfType(SystemLuaBindings, Timer), + RegisterLuaBindingsOfType(SystemLuaBindings, PathRequest), + RegisterLuaBindingsOfConcreteType(EntityLuaBindings, Scene), + RegisterLuaBindingsOfType(EntityLuaBindings, SceneArea), + RegisterLuaBindingsOfType(EntityLuaBindings, SceneLayer), + RegisterLuaBindingsOfType(EntityLuaBindings, SLBackground), + RegisterLuaBindingsOfAbstractType(EntityLuaBindings, Deployment), + RegisterLuaBindingsOfType(SystemLuaBindings, DataModule), + RegisterLuaBindingsOfType(ActivityLuaBindings, Activity), + RegisterLuaBindingsOfAbstractType(ActivityLuaBindings, GameActivity), + RegisterLuaBindingsOfAbstractType(EntityLuaBindings, GlobalScript), + RegisterLuaBindingsOfType(EntityLuaBindings, MetaPlayer), + RegisterLuaBindingsOfType(GUILuaBindings, GUIBanner), + RegisterLuaBindingsOfType(GUILuaBindings, BuyMenuGUI), + RegisterLuaBindingsOfType(GUILuaBindings, SceneEditorGUI), + RegisterLuaBindingsOfType(ManagerLuaBindings, ActivityMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, AudioMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, CameraMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, ConsoleMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, FrameMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, MetaMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, MovableMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, PerformanceMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, PostProcessMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, PresetMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, PrimitiveMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, SceneMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, SettingsMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, TimerMan), + RegisterLuaBindingsOfType(ManagerLuaBindings, UInputMan), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, GraphicalPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, LinePrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, ArcPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, SplinePrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, BoxPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, BoxFillPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, RoundedBoxPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, RoundedBoxFillPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, CirclePrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, CircleFillPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, EllipsePrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, EllipseFillPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, TrianglePrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, TriangleFillPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, TextPrimitive), + RegisterLuaBindingsOfType(PrimitiveLuaBindings, BitmapPrimitive), + RegisterLuaBindingsOfType(InputLuaBindings, InputDevice), + RegisterLuaBindingsOfType(InputLuaBindings, InputElements), + RegisterLuaBindingsOfType(InputLuaBindings, JoyButtons), + RegisterLuaBindingsOfType(InputLuaBindings, JoyDirections), + RegisterLuaBindingsOfType(InputLuaBindings, MouseButtons), + RegisterLuaBindingsOfType(InputLuaBindings, SDL_Keycode), + RegisterLuaBindingsOfType(InputLuaBindings, SDL_Scancode), + RegisterLuaBindingsOfType(InputLuaBindings, SDL_GameControllerButton), + RegisterLuaBindingsOfType(InputLuaBindings, SDL_GameControllerAxis), + RegisterLuaBindingsOfType(MiscLuaBindings, AlarmEvent), + RegisterLuaBindingsOfType(MiscLuaBindings, Directions), + RegisterLuaBindingsOfType(MiscLuaBindings, DrawBlendMode)]; // Assign the manager instances to globals in the lua master state luabind::globals(m_State)["TimerMan"] = &g_TimerMan; @@ -225,68 +225,67 @@ namespace RTE { m_RandomGenerator.Seed(seed); luaL_dostring(m_State, - // Add cls() as a shortcut to ConsoleMan:Clear(). - "cls = function() ConsoleMan:Clear(); end" - "\n" - // Override "print" in the lua state to output to the console. - "print = function(stringToPrint) ConsoleMan:PrintString(\"PRINT: \" .. tostring(stringToPrint)); end" - "\n" - // Override random functions to appear global instead of under LuaMan - "SelectRand = function(lower, upper) return LuaMan:SelectRand(lower, upper); end;\n" - "RangeRand = function(lower, upper) return LuaMan:RangeRand(lower, upper); end;\n" - "PosRand = function() return LuaMan:PosRand(); end;\n" - "NormalRand = function() return LuaMan:NormalRand(); end;\n" - // Override "math.random" in the lua state to use RTETools MT19937 implementation. Preserve return types of original to not break all the things. - "math.random = function(lower, upper) if lower ~= nil and upper ~= nil then return LuaMan:SelectRand(lower, upper); elseif lower ~= nil then return LuaMan:SelectRand(1, lower); else return LuaMan:PosRand(); end end" - "\n" - // Override "dofile"/"loadfile" to be able to account for Data/ or Mods/ directory. - "OriginalDoFile = dofile; dofile = function(filePath) filePath = PresetMan:GetFullModulePath(filePath); if filePath ~= '' then return OriginalDoFile(filePath); end end;" - "OriginalLoadFile = loadfile; loadfile = function(filePath) filePath = PresetMan:GetFullModulePath(filePath); if filePath ~= '' then return OriginalLoadFile(filePath); end end;" - // Internal helper functions to add callbacks for async pathing requests - "_AsyncPathCallbacks = {};" - "_AddAsyncPathCallback = function(id, callback) _AsyncPathCallbacks[id] = callback; end\n" - "_TriggerAsyncPathCallback = function(id, param) if _AsyncPathCallbacks[id] ~= nil then _AsyncPathCallbacks[id](param); _AsyncPathCallbacks[id] = nil; end end\n" - ); + // Add cls() as a shortcut to ConsoleMan:Clear(). + "cls = function() ConsoleMan:Clear(); end" + "\n" + // Override "print" in the lua state to output to the console. + "print = function(stringToPrint) ConsoleMan:PrintString(\"PRINT: \" .. tostring(stringToPrint)); end" + "\n" + // Override random functions to appear global instead of under LuaMan + "SelectRand = function(lower, upper) return LuaMan:SelectRand(lower, upper); end;\n" + "RangeRand = function(lower, upper) return LuaMan:RangeRand(lower, upper); end;\n" + "PosRand = function() return LuaMan:PosRand(); end;\n" + "NormalRand = function() return LuaMan:NormalRand(); end;\n" + // Override "math.random" in the lua state to use RTETools MT19937 implementation. Preserve return types of original to not break all the things. + "math.random = function(lower, upper) if lower ~= nil and upper ~= nil then return LuaMan:SelectRand(lower, upper); elseif lower ~= nil then return LuaMan:SelectRand(1, lower); else return LuaMan:PosRand(); end end" + "\n" + // Override "dofile"/"loadfile" to be able to account for Data/ or Mods/ directory. + "OriginalDoFile = dofile; dofile = function(filePath) filePath = PresetMan:GetFullModulePath(filePath); if filePath ~= '' then return OriginalDoFile(filePath); end end;" + "OriginalLoadFile = loadfile; loadfile = function(filePath) filePath = PresetMan:GetFullModulePath(filePath); if filePath ~= '' then return OriginalLoadFile(filePath); end end;" + // Internal helper functions to add callbacks for async pathing requests + "_AsyncPathCallbacks = {};" + "_AddAsyncPathCallback = function(id, callback) _AsyncPathCallbacks[id] = callback; end\n" + "_TriggerAsyncPathCallback = function(id, param) if _AsyncPathCallbacks[id] ~= nil then _AsyncPathCallbacks[id](param); _AsyncPathCallbacks[id] = nil; end end\n"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaStateWrapper::Destroy() { lua_close(m_State); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int LuaStateWrapper::SelectRand(int minInclusive, int maxInclusive) { return m_RandomGenerator.RandomNum(minInclusive, maxInclusive); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// double LuaStateWrapper::RangeRand(double minInclusive, double maxInclusive) { return m_RandomGenerator.RandomNum(minInclusive, maxInclusive); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// double LuaStateWrapper::NormalRand() { return m_RandomGenerator.RandomNormalNum(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// double LuaStateWrapper::PosRand() { return m_RandomGenerator.RandomNum(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Passthrough LuaMan Functions const std::vector* LuaStateWrapper::DirectoryList(const std::string& path) { return g_LuaMan.DirectoryList(path); } const std::vector* LuaStateWrapper::FileList(const std::string& path) { return g_LuaMan.FileList(path); } - bool LuaStateWrapper::FileExists(const std::string &path) { return g_LuaMan.FileExists(path); } - bool LuaStateWrapper::DirectoryExists(const std::string &path) { return g_LuaMan.DirectoryExists(path); } - bool LuaStateWrapper::IsValidModulePath(const std::string &path) { return g_LuaMan.IsValidModulePath(path); } + bool LuaStateWrapper::FileExists(const std::string& path) { return g_LuaMan.FileExists(path); } + bool LuaStateWrapper::DirectoryExists(const std::string& path) { return g_LuaMan.DirectoryExists(path); } + bool LuaStateWrapper::IsValidModulePath(const std::string& path) { return g_LuaMan.IsValidModulePath(path); } int LuaStateWrapper::FileOpen(const std::string& path, const std::string& accessMode) { return g_LuaMan.FileOpen(path, accessMode); } void LuaStateWrapper::FileClose(int fileIndex) { return g_LuaMan.FileClose(fileIndex); } void LuaStateWrapper::FileCloseAll() { return g_LuaMan.FileCloseAll(); } @@ -301,13 +300,13 @@ namespace RTE { void LuaStateWrapper::FileWriteLine(int fileIndex, const std::string& line) { return g_LuaMan.FileWriteLine(fileIndex, line); } bool LuaStateWrapper::FileEOF(int fileIndex) { return g_LuaMan.FileEOF(fileIndex); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaMan::Clear() { m_OpenedFiles.fill(nullptr); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaMan::Initialize() { m_MasterScriptState.Initialize(); @@ -318,57 +317,56 @@ namespace RTE { } m_ScriptStates = std::vector(luaStateCount); - for (LuaStateWrapper &luaState : m_ScriptStates) { + for (LuaStateWrapper& luaState: m_ScriptStates) { luaState.Initialize(); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaStateWrapper & LuaMan::GetMasterScriptState() { - return m_MasterScriptState; - } + LuaStateWrapper& LuaMan::GetMasterScriptState() { + return m_MasterScriptState; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaStatesArray & LuaMan::GetThreadedScriptStates() { + LuaStatesArray& LuaMan::GetThreadedScriptStates() { return m_ScriptStates; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - thread_local LuaStateWrapper * s_luaStateOverride = nullptr; - LuaStateWrapper * LuaMan::GetThreadLuaStateOverride() const { + thread_local LuaStateWrapper* s_luaStateOverride = nullptr; + LuaStateWrapper* LuaMan::GetThreadLuaStateOverride() const { return s_luaStateOverride; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::SetThreadLuaStateOverride(LuaStateWrapper * luaState) { + void LuaMan::SetThreadLuaStateOverride(LuaStateWrapper* luaState) { s_luaStateOverride = luaState; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// thread_local LuaStateWrapper* s_currentLuaState = nullptr; LuaStateWrapper* LuaMan::GetThreadCurrentLuaState() const { return s_currentLuaState; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaStateWrapper * LuaMan::GetAndLockFreeScriptState() { + LuaStateWrapper* LuaMan::GetAndLockFreeScriptState() { if (s_luaStateOverride) { // We're creating this object in a multithreaded environment, ensure that it's assigned to the same script state as us bool success = s_luaStateOverride->GetMutex().try_lock(); - RTEAssert(success, "Our lua state override for our thread already belongs to another thread!") - return s_luaStateOverride; + RTEAssert(success, "Our lua state override for our thread already belongs to another thread!") return s_luaStateOverride; } // TODO // It would be nice to assign to least-saturated state, but that's a bit tricky with MO registering... /*auto itr = std::min_element(m_ScriptStates.begin(), m_ScriptStates.end(), - [](const LuaStateWrapper& lhs, const LuaStateWrapper& rhs) { return lhs.GetRegisteredMOs().size() < rhs.GetRegisteredMOs().size(); } + [](const LuaStateWrapper& lhs, const LuaStateWrapper& rhs) { return lhs.GetRegisteredMOs().size() < rhs.GetRegisteredMOs().size(); } ); bool success = itr->GetMutex().try_lock(); @@ -385,32 +383,32 @@ namespace RTE { return &m_ScriptStates[ourState]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::ClearUserModuleCache() { + void LuaMan::ClearUserModuleCache() { m_GarbageCollectionTask.wait(); m_MasterScriptState.ClearLuaScriptCache(); - for (LuaStateWrapper& luaState : m_ScriptStates) { + for (LuaStateWrapper& luaState: m_ScriptStates) { luaState.ClearLuaScriptCache(); } m_MasterScriptState.ClearUserModuleCache(); - for (LuaStateWrapper& luaState : m_ScriptStates) { + for (LuaStateWrapper& luaState: m_ScriptStates) { luaState.ClearUserModuleCache(); } - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::AddLuaScriptCallback(const std::function &callback) { + void LuaMan::AddLuaScriptCallback(const std::function& callback) { std::scoped_lock lock(m_ScriptCallbacksMutex); m_ScriptCallbacks.emplace_back(callback); - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::ExecuteLuaScriptCallbacks() { + void LuaMan::ExecuteLuaScriptCallbacks() { std::vector> callbacks; // Move our functions into the local buffer to clear the existing callbacks and to lock for as little time as possible @@ -419,17 +417,17 @@ namespace RTE { callbacks.swap(m_ScriptCallbacks); } - for (const std::function &callback : callbacks) { + for (const std::function& callback: callbacks) { callback(); } - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const std::unordered_map LuaMan::GetScriptTimings() const { std::unordered_map timings = m_MasterScriptState.GetScriptTimings(); - for (const LuaStateWrapper &luaState : m_ScriptStates) { - for (auto&& [functionName, timing] : luaState.GetScriptTimings()) { + for (const LuaStateWrapper& luaState: m_ScriptStates) { + for (auto&& [functionName, timing]: luaState.GetScriptTimings()) { auto& existing = timings[functionName]; existing.m_CallCount += timing.m_CallCount; existing.m_Time = std::max(existing.m_Time, timing.m_Time); @@ -438,7 +436,7 @@ namespace RTE { return timings; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaMan::Destroy() { for (int i = 0; i < c_MaxOpenFiles; ++i) { @@ -447,48 +445,48 @@ namespace RTE { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaStateWrapper::ClearUserModuleCache() { luaL_dostring(m_State, "for m, n in pairs(package.loaded) do if type(n) == \"boolean\" then package.loaded[m] = nil; end; end;"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaStateWrapper::ClearLuaScriptCache() { m_ScriptCache.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Entity *LuaStateWrapper::GetTempEntity() const { - return m_TempEntity; - } + Entity* LuaStateWrapper::GetTempEntity() const { + return m_TempEntity; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::SetTempEntity(Entity *entity) { + void LuaStateWrapper::SetTempEntity(Entity* entity) { m_TempEntity = entity; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector & LuaStateWrapper::GetTempEntityVector() const { - return m_TempEntityVector; - } + const std::vector& LuaStateWrapper::GetTempEntityVector() const { + return m_TempEntityVector; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::SetTempEntityVector(const std::vector &entityVector) { + void LuaStateWrapper::SetTempEntityVector(const std::vector& entityVector) { m_TempEntityVector.reserve(entityVector.size()); - for (const Entity *entity : entityVector) { - m_TempEntityVector.push_back(const_cast(entity)); + for (const Entity* entity: entityVector) { + m_TempEntityVector.push_back(const_cast(entity)); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::SetLuaPath(const std::string &filePath) { + void LuaStateWrapper::SetLuaPath(const std::string& filePath) { const std::string moduleName = g_PresetMan.GetModuleNameFromPath(filePath); const std::string moduleFolder = g_PresetMan.IsModuleOfficial(moduleName) ? System::GetDataDirectory() : System::GetModDirectory(); const std::string scriptPath = moduleFolder + moduleName + "/?.lua"; @@ -508,60 +506,72 @@ namespace RTE { lua_pop(m_State, 1); // get rid of package table from top of stack. } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::unordered_map & LuaStateWrapper::GetScriptTimings() const { + const std::unordered_map& LuaStateWrapper::GetScriptTimings() const { return m_ScriptTimings; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFunctionString(const std::string &functionName, const std::string &selfObjectName, const std::vector &variablesToSafetyCheck, const std::vector &functionEntityArguments, const std::vector &functionLiteralArguments) { + int LuaStateWrapper::RunScriptFunctionString(const std::string& functionName, const std::string& selfObjectName, const std::vector& variablesToSafetyCheck, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments) { std::stringstream scriptString; if (!variablesToSafetyCheck.empty()) { scriptString << "if "; - for (const std::string_view &variableToSafetyCheck : variablesToSafetyCheck) { - if (&variableToSafetyCheck != &variablesToSafetyCheck[0]) { scriptString << " and "; } + for (const std::string_view& variableToSafetyCheck: variablesToSafetyCheck) { + if (&variableToSafetyCheck != &variablesToSafetyCheck[0]) { + scriptString << " and "; + } scriptString << variableToSafetyCheck; } scriptString << " then "; } - if (!functionEntityArguments.empty()) { scriptString << "local entityArguments = LuaMan.TempEntities; "; } + if (!functionEntityArguments.empty()) { + scriptString << "local entityArguments = LuaMan.TempEntities; "; + } // Lock here, even though we also lock in RunScriptString(), to ensure that the temp entity vector isn't stomped by separate threads. std::lock_guard lock(m_Mutex); s_currentLuaState = this; scriptString << functionName + "("; - if (!selfObjectName.empty()) { scriptString << selfObjectName; } + if (!selfObjectName.empty()) { + scriptString << selfObjectName; + } bool isFirstFunctionArgument = selfObjectName.empty(); if (!functionEntityArguments.empty()) { SetTempEntityVector(functionEntityArguments); - for (const Entity *functionEntityArgument : functionEntityArguments) { - if (!isFirstFunctionArgument) { scriptString << ", "; } + for (const Entity* functionEntityArgument: functionEntityArguments) { + if (!isFirstFunctionArgument) { + scriptString << ", "; + } scriptString << "(To" + functionEntityArgument->GetClassName() + " and To" + functionEntityArgument->GetClassName() + "(entityArguments()) or entityArguments())"; isFirstFunctionArgument = false; } } if (!functionLiteralArguments.empty()) { - for (const std::string_view &functionLiteralArgument : functionLiteralArguments) { - if (!isFirstFunctionArgument) { scriptString << ", "; } + for (const std::string_view& functionLiteralArgument: functionLiteralArguments) { + if (!isFirstFunctionArgument) { + scriptString << ", "; + } scriptString << std::string(functionLiteralArgument); isFirstFunctionArgument = false; } } scriptString << ");"; - if (!variablesToSafetyCheck.empty()) { scriptString << " end;"; } + if (!variablesToSafetyCheck.empty()) { + scriptString << " end;"; + } int result = RunScriptString(scriptString.str()); m_TempEntityVector.clear(); return result; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptString(const std::string &scriptString, bool consoleErrors) { + int LuaStateWrapper::RunScriptString(const std::string& scriptString, bool consoleErrors) { if (scriptString.empty()) { return -1; } @@ -588,9 +598,9 @@ namespace RTE { return error; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFunctionObject(const LuabindObjectWrapper *functionObject, const std::string &selfGlobalTableName, const std::string &selfGlobalTableKey, const std::vector &functionEntityArguments, const std::vector &functionLiteralArguments, const std::vector &functionObjectArguments) { + int LuaStateWrapper::RunScriptFunctionObject(const LuabindObjectWrapper* functionObject, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { int status = 0; std::lock_guard lock(m_Mutex); @@ -608,13 +618,13 @@ namespace RTE { argumentCount++; } - for (const Entity *functionEntityArgument : functionEntityArguments) { - std::unique_ptr downCastEntityAsLuabindObjectWrapper(LuaAdaptersEntityCast::s_EntityToLuabindObjectCastFunctions.at(functionEntityArgument->GetClassName())(const_cast(functionEntityArgument), m_State)); + for (const Entity* functionEntityArgument: functionEntityArguments) { + std::unique_ptr downCastEntityAsLuabindObjectWrapper(LuaAdaptersEntityCast::s_EntityToLuabindObjectCastFunctions.at(functionEntityArgument->GetClassName())(const_cast(functionEntityArgument), m_State)); downCastEntityAsLuabindObjectWrapper->GetLuabindObject()->push(m_State); } - for (const std::string_view &functionLiteralArgument : functionLiteralArguments) { - char *stringToDoubleConversionFailed = nullptr; + for (const std::string_view& functionLiteralArgument: functionLiteralArguments) { + char* stringToDoubleConversionFailed = nullptr; if (functionLiteralArgument == "nil") { lua_pushnil(m_State); } else if (functionLiteralArgument == "true" || functionLiteralArgument == "false") { @@ -626,7 +636,7 @@ namespace RTE { } } - for (const LuabindObjectWrapper *functionObjectArgument : functionObjectArguments) { + for (const LuabindObjectWrapper* functionObjectArgument: functionObjectArguments) { if (functionObjectArgument->GetLuabindObject()->interpreter() != m_State) { LuabindObjectWrapper copy = functionObjectArgument->GetCopyForState(*m_State); copy.GetLuabindObject()->push(m_State); @@ -663,9 +673,9 @@ namespace RTE { return status; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFile(const std::string &filePath, bool consoleErrors, bool doInSandboxedEnvironment) { + int LuaStateWrapper::RunScriptFile(const std::string& filePath, bool consoleErrors, bool doInSandboxedEnvironment) { const std::string fullScriptPath = g_PresetMan.GetFullModulePath(filePath); if (fullScriptPath.empty()) { m_LastError = "Can't run a script file with an empty filepath!"; @@ -741,7 +751,7 @@ namespace RTE { return error; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool LuaStateWrapper::RetrieveFunctions(const std::string& funcObjectName, const std::vector& functionNamesToLookFor, std::unordered_map& outFunctionNamesAndObjects) { std::lock_guard lock(m_Mutex); @@ -753,11 +763,11 @@ namespace RTE { } auto& newScript = m_ScriptCache[funcObjectName.c_str()]; - for (auto& pair : newScript.functionNamesAndObjects) { + for (auto& pair: newScript.functionNamesAndObjects) { delete pair.second; } newScript.functionNamesAndObjects.clear(); - for (const std::string& functionName : functionNamesToLookFor) { + for (const std::string& functionName: functionNamesToLookFor) { luabind::object functionObject = funcHoldingObject[functionName]; if (luabind::type(functionObject) == LUA_TFUNCTION) { luabind::object* functionObjectCopyForStoring = new luabind::object(functionObject); @@ -765,7 +775,7 @@ namespace RTE { } } - for (auto& pair : newScript.functionNamesAndObjects) { + for (auto& pair: newScript.functionNamesAndObjects) { luabind::object* functionObjectCopyForStoring = new luabind::object(*pair.second->GetLuabindObject()); outFunctionNamesAndObjects.try_emplace(pair.first, new LuabindObjectWrapper(functionObjectCopyForStoring, funcObjectName)); } @@ -773,9 +783,9 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFileAndRetrieveFunctions(const std::string &filePath, const std::vector &functionNamesToLookFor, std::unordered_map &outFunctionNamesAndObjects, bool forceReload) { + int LuaStateWrapper::RunScriptFileAndRetrieveFunctions(const std::string& filePath, const std::vector& functionNamesToLookFor, std::unordered_map& outFunctionNamesAndObjects, bool forceReload) { static bool disableCaching = false; forceReload = forceReload || disableCaching; @@ -783,7 +793,7 @@ namespace RTE { // TODO - fix activity restarting needing to force reload auto cachedScript = m_ScriptCache.find(filePath); if (!forceReload && cachedScript != m_ScriptCache.end()) { - for (auto& pair : cachedScript->second.functionNamesAndObjects) { + for (auto& pair: cachedScript->second.functionNamesAndObjects) { luabind::object* functionObjectCopyForStoring = new luabind::object(*pair.second->GetLuabindObject()); outFunctionNamesAndObjects.try_emplace(pair.first, new LuabindObjectWrapper(functionObjectCopyForStoring, filePath)); } @@ -805,25 +815,25 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::Update() { - for (MovableObject* mo : m_AddedRegisteredMOs) { + void LuaStateWrapper::Update() { + for (MovableObject* mo: m_AddedRegisteredMOs) { m_RegisteredMOs.insert(mo); } m_AddedRegisteredMOs.clear(); - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaStateWrapper::ClearScriptTimings() { m_ScriptTimings.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaStateWrapper::ExpressionIsTrue(const std::string &expression, bool consoleErrors) { - if (expression.empty()) { + bool LuaStateWrapper::ExpressionIsTrue(const std::string& expression, bool consoleErrors) { + if (expression.empty()) { return false; } bool result = false; @@ -846,11 +856,11 @@ namespace RTE { lua_pop(m_State, 1); return result; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::SavePointerAsGlobal(void *objectToSave, const std::string &globalName) { + void LuaStateWrapper::SavePointerAsGlobal(void* objectToSave, const std::string& globalName) { std::lock_guard lock(m_Mutex); // Push the pointer onto the Lua stack. @@ -859,9 +869,9 @@ namespace RTE { lua_setglobal(m_State, globalName.c_str()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaStateWrapper::GlobalIsDefined(const std::string &globalName) { + bool LuaStateWrapper::GlobalIsDefined(const std::string& globalName) { std::lock_guard lock(m_Mutex); // Get the var you want onto the stack so we can check it. @@ -874,9 +884,9 @@ namespace RTE { return isDefined; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaStateWrapper::TableEntryIsDefined(const std::string &tableName, const std::string &indexName) { + bool LuaStateWrapper::TableEntryIsDefined(const std::string& tableName, const std::string& indexName) { std::lock_guard lock(m_Mutex); // Push the table onto the stack, checking if it even exists. @@ -896,25 +906,26 @@ namespace RTE { return isDefined; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaStateWrapper::ErrorExists() const { - return !m_LastError.empty();; - } + bool LuaStateWrapper::ErrorExists() const { + return !m_LastError.empty(); + ; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string LuaStateWrapper::GetLastError() const { - return m_LastError; - } + std::string LuaStateWrapper::GetLastError() const { + return m_LastError; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::ClearErrors() { + void LuaStateWrapper::ClearErrors() { m_LastError.clear(); - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string LuaStateWrapper::DescribeLuaStack() { int indexOfTopOfStack = lua_gettop(m_State); @@ -939,54 +950,58 @@ namespace RTE { stackDescription << lua_typename(m_State, type); break; } - if (i - 1 > 0) { stackDescription << "\n"; } + if (i - 1 > 0) { + stackDescription << "\n"; + } } return stackDescription.str(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector * LuaMan::DirectoryList(const std::string &path) { + const std::vector* LuaMan::DirectoryList(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); - auto *directoryPaths = new std::vector(); + auto* directoryPaths = new std::vector(); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 fullPath = GetCaseInsensitiveFullPath(fullPath); #endif - if (std::filesystem::exists(fullPath)) - { - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath)) { - if (directoryEntry.is_directory()) { directoryPaths->emplace_back(directoryEntry.path().filename().generic_string()); } + if (std::filesystem::exists(fullPath)) { + for (const std::filesystem::directory_entry& directoryEntry: std::filesystem::directory_iterator(fullPath)) { + if (directoryEntry.is_directory()) { + directoryPaths->emplace_back(directoryEntry.path().filename().generic_string()); + } } } } return directoryPaths; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector * LuaMan::FileList(const std::string &path) { + const std::vector* LuaMan::FileList(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); - auto *filePaths = new std::vector(); + auto* filePaths = new std::vector(); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 fullPath = GetCaseInsensitiveFullPath(fullPath); #endif - if (std::filesystem::exists(fullPath)) - { - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath)) { - if (directoryEntry.is_regular_file()) { filePaths->emplace_back(directoryEntry.path().filename().generic_string()); } + if (std::filesystem::exists(fullPath)) { + for (const std::filesystem::directory_entry& directoryEntry: std::filesystem::directory_iterator(fullPath)) { + if (directoryEntry.is_regular_file()) { + filePaths->emplace_back(directoryEntry.path().filename().generic_string()); + } } } } return filePaths; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::FileExists(const std::string &path) { + bool LuaMan::FileExists(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 @@ -997,9 +1012,9 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::DirectoryExists(const std::string &path) { + bool LuaMan::DirectoryExists(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 @@ -1010,16 +1025,16 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: Move to ModuleMan, once the ModuleMan PR has been merged - bool LuaMan::IsValidModulePath(const std::string &path) { + bool LuaMan::IsValidModulePath(const std::string& path) { return (path.find("..") == std::string::npos) && (path.find(System::GetModulePackageExtension()) != std::string::npos); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaMan::FileOpen(const std::string &path, const std::string &accessMode) { + int LuaMan::FileOpen(const std::string& path, const std::string& accessMode) { if (c_FileAccessModes.find(accessMode) == c_FileAccessModes.end()) { g_ConsoleMan.PrintString("ERROR: Cannot open file, invalid file access mode specified."); return -1; @@ -1040,9 +1055,9 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifdef _WIN32 - FILE *file = fopen(fullPath.c_str(), accessMode.c_str()); + FILE* file = fopen(fullPath.c_str(), accessMode.c_str()); #else - FILE *file = [&fullPath, &accessMode]() -> FILE* { + FILE* file = [&fullPath, &accessMode]() -> FILE* { if (std::filesystem::exists(fullPath)) { return fopen(fullPath.c_str(), accessMode.c_str()); } @@ -1056,7 +1071,7 @@ namespace RTE { // Iterate over all entries in the path part's directory, // to check if the path part is in there case insensitively - for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { + for (const std::filesystem::path& filesystemEntryPath: std::filesystem::directory_iterator(inspectedPath)) { if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { inspectedPath = filesystemEntryPath; @@ -1090,7 +1105,7 @@ namespace RTE { return -1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaMan::FileClose(int fileIndex) { if (fileIndex > -1 && fileIndex < c_MaxOpenFiles && m_OpenedFiles.at(fileIndex)) { @@ -1099,7 +1114,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaMan::FileCloseAll() { for (int file = 0; file < c_MaxOpenFiles; ++file) { @@ -1107,7 +1122,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool LuaMan::FileRemove(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); @@ -1123,7 +1138,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool LuaMan::DirectoryCreate(const std::string& path, bool recursive) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); @@ -1137,13 +1152,13 @@ namespace RTE { } else { return std::filesystem::create_directory(fullPath); } - } catch (const std::filesystem::filesystem_error &e) {} + } catch (const std::filesystem::filesystem_error& e) {} } g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path); return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool LuaMan::DirectoryRemove(const std::string& path, bool recursive) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); @@ -1158,14 +1173,14 @@ namespace RTE { } else { return std::filesystem::remove(fullPath); } - } catch (const std::filesystem::filesystem_error &e) {} + } catch (const std::filesystem::filesystem_error& e) {} } } g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path); return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool LuaMan::FileRename(const std::string& oldPath, const std::string& newPath) { std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath); @@ -1177,19 +1192,18 @@ namespace RTE { #endif // Ensures parity between Linux which can overwrite an empty directory, while Windows can't // Ensures parity between Linux which can't rename a directory to a newPath that is a file in order to overwrite it, while Windows can - if (std::filesystem::is_regular_file(fullOldPath) && !std::filesystem::exists(fullNewPath)) - { + if (std::filesystem::is_regular_file(fullOldPath) && !std::filesystem::exists(fullNewPath)) { try { std::filesystem::rename(fullOldPath, fullNewPath); return true; - } catch (const std::filesystem::filesystem_error &e) {} + } catch (const std::filesystem::filesystem_error& e) {} } } g_ConsoleMan.PrintString("ERROR: Failed to rename oldPath " + oldPath + " to newPath " + newPath); return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool LuaMan::DirectoryRename(const std::string& oldPath, const std::string& newPath) { std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath); @@ -1201,19 +1215,18 @@ namespace RTE { #endif // Ensures parity between Linux which can overwrite an empty directory, while Windows can't // Ensures parity between Linux which can't rename a directory to a newPath that is a file in order to overwrite it, while Windows can - if (std::filesystem::is_directory(fullOldPath) && !std::filesystem::exists(fullNewPath)) - { + if (std::filesystem::is_directory(fullOldPath) && !std::filesystem::exists(fullNewPath)) { try { std::filesystem::rename(fullOldPath, fullNewPath); return true; - } catch (const std::filesystem::filesystem_error &e) {} + } catch (const std::filesystem::filesystem_error& e) {} } } g_ConsoleMan.PrintString("ERROR: Failed to rename oldPath " + oldPath + " to newPath " + newPath); return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string LuaMan::FileReadLine(int fileIndex) { if (fileIndex > -1 && fileIndex < c_MaxOpenFiles && m_OpenedFiles.at(fileIndex)) { @@ -1227,9 +1240,9 @@ namespace RTE { return ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::FileWriteLine(int fileIndex, const std::string &line) { + void LuaMan::FileWriteLine(int fileIndex, const std::string& line) { if (fileIndex > -1 && fileIndex < c_MaxOpenFiles && m_OpenedFiles.at(fileIndex)) { if (fputs(line.c_str(), m_OpenedFiles[fileIndex]) == EOF) { g_ConsoleMan.PrintString("ERROR: Failed to write to file. File might have been opened without writing permissions or is corrupt."); @@ -1239,7 +1252,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool LuaMan::FileEOF(int fileIndex) { if (fileIndex > -1 && fileIndex < c_MaxOpenFiles && m_OpenedFiles.at(fileIndex)) { @@ -1249,13 +1262,13 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaMan::Update() { ZoneScoped; m_MasterScriptState.Update(); - for (LuaStateWrapper &luaState : m_ScriptStates) { + for (LuaStateWrapper& luaState: m_ScriptStates) { luaState.Update(); } @@ -1263,10 +1276,10 @@ namespace RTE { m_GarbageCollectionTask.wait(); // Apply all deletions queued from lua - LuabindObjectWrapper::ApplyQueuedDeletions(); + LuabindObjectWrapper::ApplyQueuedDeletions(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaMan::StartAsyncGarbageCollection() { ZoneScoped; @@ -1275,30 +1288,29 @@ namespace RTE { allStates.reserve(m_ScriptStates.size() + 1); allStates.push_back(&m_MasterScriptState); - for (LuaStateWrapper& wrapper : m_ScriptStates) { + for (LuaStateWrapper& wrapper: m_ScriptStates) { allStates.push_back(&wrapper); } m_GarbageCollectionTask = BS::multi_future(); - for (LuaStateWrapper* luaState : allStates) { + for (LuaStateWrapper* luaState: allStates) { m_GarbageCollectionTask.push_back( - g_ThreadMan.GetPriorityThreadPool().submit([luaState]() { - ZoneScopedN("Lua Garbage Collection"); - std::lock_guard lock(luaState->GetMutex()); - lua_gc(luaState->GetLuaState(), LUA_GCCOLLECT, 0); // we'd use GCSTEP but fuck lua it's trash - lua_gc(luaState->GetLuaState(), LUA_GCSTOP, 0); - }) - ); + g_ThreadMan.GetPriorityThreadPool().submit([luaState]() { + ZoneScopedN("Lua Garbage Collection"); + std::lock_guard lock(luaState->GetMutex()); + lua_gc(luaState->GetLuaState(), LUA_GCCOLLECT, 0); // we'd use GCSTEP but fuck lua it's trash + lua_gc(luaState->GetLuaState(), LUA_GCSTOP, 0); + })); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LuaMan::ClearScriptTimings() { m_MasterScriptState.ClearScriptTimings(); - for (LuaStateWrapper& luaState : m_ScriptStates) { + for (LuaStateWrapper& luaState: m_ScriptStates) { luaState.ClearScriptTimings(); } } -} +} // namespace RTE diff --git a/Source/Managers/LuaMan.h b/Source/Managers/LuaMan.h index d6358c615d..d38bbdecbc 100644 --- a/Source/Managers/LuaMan.h +++ b/Source/Managers/LuaMan.h @@ -51,31 +51,31 @@ namespace RTE { /// Gets a temporary Entity that can be accessed in the Lua state. /// /// The temporary entity. Ownership is NOT transferred! - Entity * GetTempEntity() const; + Entity* GetTempEntity() const; /// /// Sets a temporary Entity that can be accessed in the Lua state. /// /// The temporary entity. Ownership is NOT transferred! - void SetTempEntity(Entity *entity); + void SetTempEntity(Entity* entity); /// /// Gets the temporary vector of Entities that can be accessed in the Lua state. /// /// The temporary vector of entities. Ownership is NOT transferred! - const std::vector & GetTempEntityVector() const; + const std::vector& GetTempEntityVector() const; /// /// Sets a temporary vector of Entities that can be accessed in the Lua state. These Entities are const_cast so they're non-const, for ease-of-use in Lua. /// /// The temporary vector of entities. Ownership is NOT transferred! - void SetTempEntityVector(const std::vector &entityVector); + void SetTempEntityVector(const std::vector& entityVector); /// /// Sets the proper package.path for the script to run. /// /// The path to the file to load and run. - void SetLuaPath(const std::string &filePath); + void SetLuaPath(const std::string& filePath); /// /// Gets this LuaStateWrapper's internal lua state. @@ -87,7 +87,7 @@ namespace RTE { /// Gets m_ScriptTimings. /// /// m_ScriptTimings. - const std::unordered_map & GetScriptTimings() const; + const std::unordered_map& GetScriptTimings() const; /// /// Gets the currently running script filepath, if applicable. @@ -107,13 +107,16 @@ namespace RTE { /// Unregisters an MO as using us. /// /// The MO to unregister as using us. Ownership is NOT transferred! - void UnregisterMO(MovableObject *moToUnregister) { m_RegisteredMOs.erase(moToUnregister); m_AddedRegisteredMOs.erase(moToUnregister); } + void UnregisterMO(MovableObject* moToUnregister) { + m_RegisteredMOs.erase(moToUnregister); + m_AddedRegisteredMOs.erase(moToUnregister); + } /// /// Gets a list of the MOs registed as using us. /// /// The MOs registed as using us. - const std::unordered_set & GetRegisteredMOs() const { return m_RegisteredMOs; } + const std::unordered_set& GetRegisteredMOs() const { return m_RegisteredMOs; } #pragma endregion #pragma region Script Execution Handling @@ -127,7 +130,7 @@ namespace RTE { /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. /// Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc. Defaults to empty. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int RunScriptFunctionString(const std::string &functionName, const std::string &selfObjectName, const std::vector &variablesToSafetyCheck = std::vector(), const std::vector &functionEntityArguments = std::vector(), const std::vector &functionLiteralArguments = std::vector()); + int RunScriptFunctionString(const std::string& functionName, const std::string& selfObjectName, const std::vector& variablesToSafetyCheck = std::vector(), const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector()); /// /// Takes a string containing a script snippet and runs it on the state. @@ -135,7 +138,7 @@ namespace RTE { /// The string with the script snippet. /// Whether to report any errors to the console immediately. /// Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. - int RunScriptString(const std::string &scriptString, bool consoleErrors = true); + int RunScriptString(const std::string& scriptString, bool consoleErrors = true); /// /// Runs the given Lua function object. The first argument to the function will always be the self object. @@ -147,7 +150,7 @@ namespace RTE { /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. /// Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int RunScriptFunctionObject(const LuabindObjectWrapper *functionObjectWrapper, const std::string &selfGlobalTableName, const std::string &selfGlobalTableKey, const std::vector &functionEntityArguments = std::vector(), const std::vector &functionLiteralArguments = std::vector(), const std::vector &functionObjectArguments = std::vector()); + int RunScriptFunctionObject(const LuabindObjectWrapper* functionObjectWrapper, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); /// /// Opens and loads a file containing a script and runs it on the state. @@ -156,7 +159,7 @@ namespace RTE { /// Whether to report any errors to the console immediately. /// Whether to do it in a sandboxed environment, or the global environment. /// Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. - int RunScriptFile(const std::string &filePath, bool consoleErrors = true, bool doInSandboxedEnvironment = true); + int RunScriptFile(const std::string& filePath, bool consoleErrors = true, bool doInSandboxedEnvironment = true); /// /// Retrieves all of the specified functions that exist into the output map, and refreshes the cache. @@ -175,7 +178,7 @@ namespace RTE { /// The map of function names to LuabindObjectWrappers to be retrieved from the script that was run. /// Whether caching shouldn't be used. /// Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. - int RunScriptFileAndRetrieveFunctions(const std::string &filePath, const std::vector &functionNamesToLookFor, std::unordered_map &outFunctionNamesAndObjects, bool forceReload = false); + int RunScriptFileAndRetrieveFunctions(const std::string& filePath, const std::vector& functionNamesToLookFor, std::unordered_map& outFunctionNamesAndObjects, bool forceReload = false); #pragma endregion #pragma region Concrete Methods @@ -204,21 +207,21 @@ namespace RTE { /// The string with the expression to evaluate. /// Whether to report any errors to the console immediately. /// Whether the expression was true. - bool ExpressionIsTrue(const std::string &expression, bool consoleErrors); + bool ExpressionIsTrue(const std::string& expression, bool consoleErrors); /// /// Takes a pointer to an object and saves it in the Lua state as a global of a specified variable name. /// /// The pointer to the object to save. Ownership is NOT transferred! /// The name of the global var in the Lua state to save the pointer to. - void SavePointerAsGlobal(void *objectToSave, const std::string &globalName); + void SavePointerAsGlobal(void* objectToSave, const std::string& globalName); /// /// Checks if there is anything defined on a specific global var in Lua. /// /// The name of the global var in the Lua state to check. /// Whether that global var has been defined yet in the Lua state. - bool GlobalIsDefined(const std::string &globalName); + bool GlobalIsDefined(const std::string& globalName); /// /// Checks if there is anything defined in a specific index of a table. @@ -226,7 +229,7 @@ namespace RTE { /// The name of the table to look inside. /// The name of the index to check inside that table. /// Whether that table var has been defined yet in the Lua state. - bool TableEntryIsDefined(const std::string &tableName, const std::string &indexName); + bool TableEntryIsDefined(const std::string& tableName, const std::string& indexName); /// /// Clears internal Lua package tables from all user-defined modules. Those must be reloaded with ReloadAllScripts(). @@ -286,9 +289,9 @@ namespace RTE { #pragma region Passthrough LuaMan Functions const std::vector* DirectoryList(const std::string& path); const std::vector* FileList(const std::string& path); - bool FileExists(const std::string &path); - bool DirectoryExists(const std::string &path); - bool IsValidModulePath(const std::string &path); + bool FileExists(const std::string& path); + bool DirectoryExists(const std::string& path); + bool IsValidModulePath(const std::string& path); int FileOpen(const std::string& path, const std::string& accessMode); void FileClose(int fileIndex); void FileCloseAll(); @@ -315,12 +318,12 @@ namespace RTE { /// void Clear(); - std::unordered_set m_RegisteredMOs; //!< The objects using our lua state. - std::unordered_set m_AddedRegisteredMOs; //!< The objects using our lua state that were recently added. + std::unordered_set m_RegisteredMOs; //!< The objects using our lua state. + std::unordered_set m_AddedRegisteredMOs; //!< The objects using our lua state that were recently added. - lua_State *m_State; - Entity *m_TempEntity; //!< Temporary holder for an Entity object that we want to pass into the Lua state without fuss. Lets you export objects to lua easily. - std::vector m_TempEntityVector; //!< Temporary holder for a vector of Entities that we want to pass into the Lua state without a fuss. Usually used to pass arguments to special Lua functions. + lua_State* m_State; + Entity* m_TempEntity; //!< Temporary holder for an Entity object that we want to pass into the Lua state without fuss. Lets you export objects to lua easily. + std::vector m_TempEntityVector; //!< Temporary holder for a vector of Entities that we want to pass into the Lua state without a fuss. Usually used to pass arguments to special Lua functions. std::string m_LastError; //!< Description of the last error that occurred in the script execution. std::string_view m_CurrentlyRunningScriptPath; //!< The currently running script filepath. @@ -348,7 +351,6 @@ namespace RTE { friend class LuaStateWrapper; public: - #pragma region Creation /// /// Constructor method used to instantiate a LuaMan object in system memory. Initialize() should be called before using the object. @@ -378,19 +380,19 @@ namespace RTE { /// Returns our master script state (where activies, global scripts etc run). /// /// The master script state. - LuaStateWrapper & GetMasterScriptState(); + LuaStateWrapper& GetMasterScriptState(); /// /// Returns our threaded script states which movable objects use. /// /// A list of threaded script states. - LuaStatesArray & GetThreadedScriptStates(); + LuaStatesArray& GetThreadedScriptStates(); /// /// Gets the current thread lua state override that new objects created will be assigned to. /// /// The current lua state to force objects to be assigned to. - LuaStateWrapper * GetThreadLuaStateOverride() const; + LuaStateWrapper* GetThreadLuaStateOverride() const; /// /// Forces all new MOs created in this thread to be assigned to a particular lua state. @@ -410,7 +412,7 @@ namespace RTE { /// This will be locked to our thread and safe to use - ensure that it'll be unlocked after use! /// /// A script state. - LuaStateWrapper * GetAndLockFreeScriptState(); + LuaStateWrapper* GetAndLockFreeScriptState(); /// /// Clears internal Lua package tables from all user-defined modules. Those must be reloaded with ReloadAllScripts(). @@ -421,7 +423,7 @@ namespace RTE { /// Adds a function to be called prior to executing lua scripts. This is used to callback into lua from other threads safely. /// /// The callback function that will be executed. - void AddLuaScriptCallback(const std::function &callback); + void AddLuaScriptCallback(const std::function& callback); /// /// Executes and clears all pending script callbacks. @@ -441,35 +443,35 @@ namespace RTE { /// /// Directory path relative to the working directory. /// A vector of the directories in path. - const std::vector * DirectoryList(const std::string &path); + const std::vector* DirectoryList(const std::string& path); /// /// Returns a vector of all the files in path, which is relative to the working directory. /// /// Directory path relative to the working directory. /// A vector of the files in path. - const std::vector * FileList(const std::string &path); + const std::vector* FileList(const std::string& path); /// /// Returns whether or not the specified file exists. You can only check for files inside .rte folders in the working directory. /// /// Path to the file. All paths are made absolute by adding current working directory to the specified path. /// Whether or not the specified file exists. - bool FileExists(const std::string &path); + bool FileExists(const std::string& path); /// /// Returns whether or not the specified directory exists. You can only check for directories inside .rte folders in the working directory. /// /// Path to the directory. All paths are made absolute by adding current working directory to the specified path. /// Whether or not the specified file exists. - bool DirectoryExists(const std::string &path); + bool DirectoryExists(const std::string& path); /// /// Returns whether or not the path refers to an accessible file or directory. You can only check for files or directories inside .rte directories in the working directory. /// /// Path to the file or directory. All paths are made absolute by adding current working directory to the specified path. /// Whether or not the specified file exists. - bool IsValidModulePath(const std::string &path); + bool IsValidModulePath(const std::string& path); /// /// Opens a file or creates one if it does not exist, depending on access mode. You can open files only inside .rte folders in the working directory. You can't open more that c_MaxOpenFiles file simultaneously. @@ -478,7 +480,7 @@ namespace RTE { /// Path to the file. All paths are made absolute by adding current working directory to the specified path. /// File access mode. See 'fopen' for list of modes. /// File index in the opened files array. - int FileOpen(const std::string &path, const std::string &accessMode); + int FileOpen(const std::string& path, const std::string& accessMode); /// /// Closes a previously opened file. @@ -496,7 +498,7 @@ namespace RTE { /// /// Path to the file. All paths are made absolute by adding current working directory to the specified path. /// Whether or not the file was removed. - bool FileRemove(const std::string &path); + bool FileRemove(const std::string& path); /// /// Creates a directory, optionally recursively. @@ -504,7 +506,7 @@ namespace RTE { /// Path to the directory to be created. All paths are made absolute by adding current working directory to the specified path. /// Whether to recursively create parent directories. /// Whether or not the directory was removed. - bool DirectoryCreate(const std::string &path, bool recursive); + bool DirectoryCreate(const std::string& path, bool recursive); /// /// Removes a directory, optionally recursively. @@ -512,7 +514,7 @@ namespace RTE { /// Path to the directory to be removed. All paths are made absolute by adding current working directory to the specified path. /// Whether to recursively remove files and directories. /// Whether or not the directory was removed. - bool DirectoryRemove(const std::string &path, bool recursive); + bool DirectoryRemove(const std::string& path, bool recursive); /// /// Moves or renames the file oldPath to newPath. @@ -522,7 +524,7 @@ namespace RTE { /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. /// Whether or not renaming succeeded. - bool FileRename(const std::string &oldPath, const std::string &newPath); + bool FileRename(const std::string& oldPath, const std::string& newPath); /// /// Moves or renames the directory oldPath to newPath. @@ -532,7 +534,7 @@ namespace RTE { /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. /// Whether or not renaming succeeded. - bool DirectoryRename(const std::string &oldPath, const std::string &newPath); + bool DirectoryRename(const std::string& oldPath, const std::string& newPath); /// /// Reads a line from a file. @@ -546,7 +548,7 @@ namespace RTE { /// /// File index in the opened files array. /// String to write. - void FileWriteLine(int fileIndex, const std::string &line); + void FileWriteLine(int fileIndex, const std::string& line); /// /// Returns true if end of file was reached. @@ -574,11 +576,10 @@ namespace RTE { void ClearScriptTimings(); private: - static constexpr int c_MaxOpenFiles = 10; //!< The maximum number of files that can be opened with FileOpen at runtime. static const std::unordered_set c_FileAccessModes; //!< Valid file access modes when opening files with FileOpen. - std::array m_OpenedFiles; //!< Internal list of opened files used by File functions. + std::array m_OpenedFiles; //!< Internal list of opened files used by File functions. LuaStateWrapper m_MasterScriptState; LuaStatesArray m_ScriptStates; @@ -596,8 +597,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - LuaMan(const LuaMan &reference) = delete; - LuaMan & operator=(const LuaMan &rhs) = delete; + LuaMan(const LuaMan& reference) = delete; + LuaMan& operator=(const LuaMan& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Managers/MenuMan.cpp b/Source/Managers/MenuMan.cpp index 12dc74fe29..0308254725 100644 --- a/Source/Managers/MenuMan.cpp +++ b/Source/Managers/MenuMan.cpp @@ -21,7 +21,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MenuMan::Initialize(bool firstTimeInit) { m_ActiveMenu = ActiveMenu::MenusDisabled; @@ -29,7 +29,9 @@ namespace RTE { m_GUIScreen = std::make_unique(g_FrameMan.GetBackBuffer32()); m_GUIInput = std::make_unique(-1, g_UInputMan.GetJoystickCount() > 0); - if (firstTimeInit) { g_LoadingScreen.Create(m_GUIScreen.get(), m_GUIInput.get(), g_SettingsMan.GetLoadingScreenProgressReportDisabled()); } + if (firstTimeInit) { + g_LoadingScreen.Create(m_GUIScreen.get(), m_GUIInput.get(), g_SettingsMan.GetLoadingScreenProgressReportDisabled()); + } m_TitleScreen = std::make_unique(m_GUIScreen.get()); m_MainMenu = std::make_unique(m_GUIScreen.get(), m_GUIInput.get()); @@ -41,7 +43,7 @@ namespace RTE { g_MetaMan.GetGUI()->Create(m_MenuController.get()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MenuMan::Reinitialize() { g_MetaMan.GetGUI()->Destroy(); @@ -55,7 +57,7 @@ namespace RTE { Initialize(false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MenuMan::SetActiveMenu() { ActiveMenu newActiveMenu = ActiveMenu::MenusDisabled; @@ -93,7 +95,7 @@ namespace RTE { if (g_MetaMan.GameInProgress()) { m_PauseMenu->SetBackButtonTargetName("Conquest"); } else { - if (const Activity *activity = g_ActivityMan.GetActivity(); activity && activity->GetPresetName() == "None") { + if (const Activity* activity = g_ActivityMan.GetActivity(); activity && activity->GetPresetName() == "None") { m_PauseMenu->SetBackButtonTargetName("Main"); } else { m_PauseMenu->SetBackButtonTargetName("Scenario"); @@ -106,7 +108,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MenuMan::HandleTransitionIntoMenuLoop() { if (g_MetaMan.GameInProgress()) { @@ -116,7 +118,7 @@ namespace RTE { m_TitleScreen->SetTitleTransitionState(TitleScreen::TitleTransition::PauseMenu); } } else if (!g_ActivityMan.ActivitySetToRestart()) { - if (const Activity *activity = g_ActivityMan.GetActivity(); activity) { + if (const Activity* activity = g_ActivityMan.GetActivity(); activity) { if (activity->GetPresetName() == "None") { // If we're in the editors or in online multiplayer then return to main menu instead of scenario menu. m_TitleScreen->SetTitleTransitionState(TitleScreen::TitleTransition::ScrollingFadeIn); @@ -133,7 +135,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool MenuMan::Update() { m_TitleScreen->Update(); @@ -171,7 +173,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool MenuMan::UpdateMainMenu() const { switch (m_MainMenu->Update()) { @@ -203,7 +205,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MenuMan::UpdateScenarioMenu() const { switch (m_ScenarioMenu->Update()) { @@ -216,7 +218,9 @@ namespace RTE { break; case ScenarioGUI::ScenarioMenuUpdateResult::ActivityStarted: m_TitleScreen->SetTitleTransitionState(TitleScreen::TitleTransition::FadeOut); - if (g_MetaMan.GameInProgress()) { g_MetaMan.EndGame(); } + if (g_MetaMan.GameInProgress()) { + g_MetaMan.EndGame(); + } g_ActivityMan.SetRestartActivity(); break; default: @@ -224,7 +228,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool MenuMan::UpdateMetaGameMenu() const { g_MetaMan.GetGUI()->SetStationOrbitPos(m_TitleScreen->GetStationPos()); @@ -243,7 +247,7 @@ namespace RTE { return g_MetaMan.GetGUI()->QuitProgram(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MenuMan::UpdatePauseMenu() const { switch (m_PauseMenu->Update()) { @@ -259,7 +263,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MenuMan::Draw() const { g_FrameMan.ClearBackBuffer32(); @@ -297,19 +301,23 @@ namespace RTE { int mouseX = 0; int mouseY = 0; m_GUIInput->GetMousePosition(&mouseX, &mouseY); - BITMAP *deviceIcon = g_UInputMan.GetDeviceIcon(device)->GetBitmaps32()[0]; - if (deviceIcon) { draw_sprite(g_FrameMan.GetBackBuffer32(), deviceIcon, mouseX + (deviceIcon->w / 2), mouseY - (deviceIcon->h / 5)); } + BITMAP* deviceIcon = g_UInputMan.GetDeviceIcon(device)->GetBitmaps32()[0]; + if (deviceIcon) { + draw_sprite(g_FrameMan.GetBackBuffer32(), deviceIcon, mouseX + (deviceIcon->w / 2), mouseY - (deviceIcon->h / 5)); + } } // Show which joysticks are detected by the game. for (int playerIndex = Players::PlayerOne; playerIndex < Players::MaxPlayerCount; playerIndex++) { if (g_UInputMan.JoystickActive(playerIndex)) { int matchedDevice = InputDevice::DEVICE_GAMEPAD_1 + playerIndex; if (matchedDevice != device) { - BITMAP *deviceIcon = g_UInputMan.GetDeviceIcon(matchedDevice)->GetBitmaps32()[0]; - if (deviceIcon) { draw_sprite(g_FrameMan.GetBackBuffer32(), deviceIcon, g_WindowMan.GetResX() - 30 * g_UInputMan.GetJoystickCount() + 30 * playerIndex, g_WindowMan.GetResY() - 25); } + BITMAP* deviceIcon = g_UInputMan.GetDeviceIcon(matchedDevice)->GetBitmaps32()[0]; + if (deviceIcon) { + draw_sprite(g_FrameMan.GetBackBuffer32(), deviceIcon, g_WindowMan.GetResX() - 30 * g_UInputMan.GetJoystickCount() + 30 * playerIndex, g_WindowMan.GetResY() - 25); + } } } } } } -} +} // namespace RTE diff --git a/Source/Managers/MenuMan.h b/Source/Managers/MenuMan.h index 80d45da938..3fbde9c719 100644 --- a/Source/Managers/MenuMan.h +++ b/Source/Managers/MenuMan.h @@ -21,7 +21,6 @@ namespace RTE { class MenuMan : public Singleton { public: - #pragma region Creation /// /// Constructor method used to instantiate a MenuMan object in system memory. Initialize() should be called before using the object. @@ -59,7 +58,6 @@ namespace RTE { #pragma endregion private: - /// /// Enumeration for the different menu screens that are active based on transition states. /// @@ -112,8 +110,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - MenuMan(const MenuMan &reference) = delete; - MenuMan & operator=(const MenuMan &rhs) = delete; + MenuMan(const MenuMan& reference) = delete; + MenuMan& operator=(const MenuMan& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Managers/MetaMan.cpp b/Source/Managers/MetaMan.cpp index 0805c3bab9..b793e85364 100644 --- a/Source/Managers/MetaMan.cpp +++ b/Source/Managers/MetaMan.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -28,1474 +27,1314 @@ namespace RTE { -const std::string MetaMan::c_ClassName = "MetaMan"; + const std::string MetaMan::c_ClassName = "MetaMan"; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MetaMan, effectively + // resetting the members of this abstraction level only. + + void MetaMan::Clear() { + m_pMetaGUI = 0; + m_GameState = NOGAME; + m_StateChanged = true; + m_Suspended = false; + m_GameSaved = false; + m_GameName = DEFAULTGAMENAME; + m_Players.clear(); + m_TeamCount = 0; + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) + m_TeamIcons[team].Reset(); + m_CurrentRound = 0; + m_Scenes.clear(); + m_RevealedScenes = 0; + m_RevealRate = 0.5; + m_RevealExtra = 3.0; + m_RoundOffensives.clear(); + m_CurrentOffensive = 0; + m_PhaseTimer.Reset(); + m_Difficulty = Activity::MediumDifficulty; + + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; team++) + m_TeamAISkill[team] = Activity::DefaultSkill; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MetaMan object ready for use. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MetaMan, effectively -// resetting the members of this abstraction level only. - -void MetaMan::Clear() -{ - m_pMetaGUI = 0; - m_GameState = NOGAME; - m_StateChanged = true; - m_Suspended = false; - m_GameSaved = false; - m_GameName = DEFAULTGAMENAME; - m_Players.clear(); - m_TeamCount = 0; - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - m_TeamIcons[team].Reset(); - m_CurrentRound = 0; - m_Scenes.clear(); - m_RevealedScenes = 0; - m_RevealRate = 0.5; - m_RevealExtra = 3.0; - m_RoundOffensives.clear(); - m_CurrentOffensive = 0; - m_PhaseTimer.Reset(); - m_Difficulty = Activity::MediumDifficulty; - - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; team++) - m_TeamAISkill[team] = Activity::DefaultSkill; -} + int MetaMan::Initialize() { + // if (Serializable::Create() < 0) + // return -1; + // Allocate the metagame interface; it gets created manually later in Main + if (!m_pMetaGUI) + m_pMetaGUI = new MetagameGUI(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MetaMan object ready for use. - -int MetaMan::Initialize() -{ -// if (Serializable::Create() < 0) -// return -1; - - // Allocate the metagame interface; it gets created manually later in Main - if (!m_pMetaGUI) - m_pMetaGUI = new MetagameGUI(); + return 0; + } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: NewGame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Wipes any current and sets up a new game based on a size parameter. + int MetaMan::NewGame(int gameSize) { + // Grab a random selection of Scene presets from all available + std::list scenePresets; + SelectScenePresets(gameSize, &scenePresets); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: NewGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wipes any current and sets up a new game based on a size parameter. - -int MetaMan::NewGame(int gameSize) -{ - // Grab a random selection of Scene presets from all available - std::list scenePresets; - SelectScenePresets(gameSize, &scenePresets); - - // Destroy and clear any pre-existing scenes from previous games - for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) - { - delete *sItr; - *sItr = 0; - } - m_Scenes.clear(); - - // Make deep copies of the selected Scene presets for use in the actual game about to start - for (std::list::iterator pItr = scenePresets.begin(); pItr != scenePresets.end(); ++pItr) - { - // List for found metascenes to choose from - std::vector metascenesList; - // Add our actual scene to the list as it should always be there to be selected later - metascenesList.push_back(*pItr); - - // Look for additional metascenes which can be used instead of this scene - std::list sceneList; - g_PresetMan.GetAllOfType(sceneList, "Scene"); - - // Go through the list and add all compatible metascenes to the list - for (std::list::iterator itr = sceneList.begin(); itr != sceneList.end(); ++itr) - { - Scene * pScene = dynamic_cast(*itr); - if (pScene) - { - if (pScene->GetMetasceneParent() == (*pItr)->GetModuleAndPresetName()) - metascenesList.push_back(pScene); - } + // Destroy and clear any pre-existing scenes from previous games + for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { + delete *sItr; + *sItr = 0; } - // Finally select some random metascene - int selection = RandomNum(0, metascenesList.size() - 1); - Scene * pSelectedScene = metascenesList.at(selection); - - //Copy selected scene - m_Scenes.push_back(dynamic_cast(pSelectedScene->Clone())); - // So we're messing with metascene, change it's name to conseal it's true nature - if (selection > 0) - { - m_Scenes.back()->SetPresetName((*pItr)->GetPresetName()); - m_Scenes.back()->SetDescription((*pItr)->GetDescription()); - m_Scenes.back()->SetLocation((*pItr)->GetLocation()); - m_Scenes.back()->SetLocationOffset((*pItr)->GetLocationOffset()); - m_Scenes.back()->SetMetagameInternal(true); + m_Scenes.clear(); + + // Make deep copies of the selected Scene presets for use in the actual game about to start + for (std::list::iterator pItr = scenePresets.begin(); pItr != scenePresets.end(); ++pItr) { + // List for found metascenes to choose from + std::vector metascenesList; + // Add our actual scene to the list as it should always be there to be selected later + metascenesList.push_back(*pItr); + + // Look for additional metascenes which can be used instead of this scene + std::list sceneList; + g_PresetMan.GetAllOfType(sceneList, "Scene"); + + // Go through the list and add all compatible metascenes to the list + for (std::list::iterator itr = sceneList.begin(); itr != sceneList.end(); ++itr) { + Scene* pScene = dynamic_cast(*itr); + if (pScene) { + if (pScene->GetMetasceneParent() == (*pItr)->GetModuleAndPresetName()) + metascenesList.push_back(pScene); + } + } + // Finally select some random metascene + int selection = RandomNum(0, metascenesList.size() - 1); + Scene* pSelectedScene = metascenesList.at(selection); + + // Copy selected scene + m_Scenes.push_back(dynamic_cast(pSelectedScene->Clone())); + // So we're messing with metascene, change it's name to conseal it's true nature + if (selection > 0) { + m_Scenes.back()->SetPresetName((*pItr)->GetPresetName()); + m_Scenes.back()->SetDescription((*pItr)->GetDescription()); + m_Scenes.back()->SetLocation((*pItr)->GetLocation()); + m_Scenes.back()->SetLocationOffset((*pItr)->GetLocationOffset()); + m_Scenes.back()->SetMetagameInternal(true); + } + // Add a metagame base area + Scene::Area newArea(METABASE_AREA_NAME); + newArea.AddBox(Box(0, 0, 1, 1)); // Add an empty box, or the area won't be saved + m_Scenes.back()->AddArea(newArea); + + // Make sure they are all hidden at game start + m_Scenes.back()->SetRevealed(false); + // Make them unique presets in their own Data Module so they don't get referenced from the original presets they were made from + m_Scenes.back()->MigrateToModule(g_PresetMan.GetModuleID(METASAVEMODULENAME)); + // Make them unexplored by all teams + for (int team = Activity::TeamOne; team < m_TeamCount; ++team) + m_Scenes.back()->FillUnseenLayer(Vector(25, 25), team, false); + + // Go through all AI plan elements and expand all bunker schemes to concrete assemblies + // with fixed prices and place deployments + m_Scenes.back()->ExpandAIPlanAssemblySchemes(); } - // Add a metagame base area - Scene::Area newArea(METABASE_AREA_NAME); - newArea.AddBox(Box(0,0,1,1)); // Add an empty box, or the area won't be saved - m_Scenes.back()->AddArea(newArea); - - - // Make sure they are all hidden at game start - m_Scenes.back()->SetRevealed(false); - // Make them unique presets in their own Data Module so they don't get referenced from the original presets they were made from - m_Scenes.back()->MigrateToModule(g_PresetMan.GetModuleID(METASAVEMODULENAME)); - // Make them unexplored by all teams - for (int team = Activity::TeamOne; team < m_TeamCount; ++team) - m_Scenes.back()->FillUnseenLayer(Vector(25, 25), team, false); - - // Go through all AI plan elements and expand all bunker schemes to concrete assemblies - // with fixed prices and place deployments - m_Scenes.back()->ExpandAIPlanAssemblySchemes(); - } - - // The game that is currently being played is known as - m_GameName = DEFAULTGAMENAME; - - // Start the game/intro - m_CurrentRound = 0; - m_RevealedScenes = 0; -// TODO: Refine these, taking team numbers and total game size into account - // Set the reveal rate of new sites each round - m_RevealRate = 0.3; - // Set the number of sites initially found at the start of the game - m_RevealExtra = m_Players.size(); - m_GameState = GAMEINTRO; - m_StateChanged = true; - SetSuspend(false); - - ClearActivities(); - - return 0; -} + // The game that is currently being played is known as + m_GameName = DEFAULTGAMENAME; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: EndGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wipes any current metagame and sets things back to as if program start. -// Arguments: None. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. + // Start the game/intro + m_CurrentRound = 0; + m_RevealedScenes = 0; + // TODO: Refine these, taking team numbers and total game size into account + // Set the reveal rate of new sites each round + m_RevealRate = 0.3; + // Set the number of sites initially found at the start of the game + m_RevealExtra = m_Players.size(); + m_GameState = GAMEINTRO; + m_StateChanged = true; + SetSuspend(false); -int MetaMan::EndGame() -{ - //Reset metagame UI - m_pMetaGUI->SetToStartNewGame(); + ClearActivities(); - // Destroy and clear any pre-existing scenes from previous games - for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) - { - delete *sItr; - *sItr = 0; - } - m_Scenes.clear(); + return 0; + } - // The game that is currently being played is known as - m_GameName = DEFAULTGAMENAME; + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: EndGame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Wipes any current metagame and sets things back to as if program start. + // Arguments: None. + // Return value: An error return value signaling success or any particular failure. + // Anything below 0 is an error signal. + + int MetaMan::EndGame() { + // Reset metagame UI + m_pMetaGUI->SetToStartNewGame(); + + // Destroy and clear any pre-existing scenes from previous games + for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { + delete *sItr; + *sItr = 0; + } + m_Scenes.clear(); - m_GameState = NOGAME; - m_StateChanged = true; - SetSuspend(false); + // The game that is currently being played is known as + m_GameName = DEFAULTGAMENAME; - g_ActivityMan.EndActivity(); + m_GameState = NOGAME; + m_StateChanged = true; + SetSuspend(false); - g_MetaMan.m_Players.clear(); + g_ActivityMan.EndActivity(); - ClearActivities(); + g_MetaMan.m_Players.clear(); - return 0; -} + ClearActivities(); + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Load -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Load a MetaMan from disk out of the special MetaMan.rte data module + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Load + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Load a MetaMan from disk out of the special MetaMan.rte data module -int MetaMan::Load(const MetaSave *pSave) -{ - if (!pSave) - return -1; + int MetaMan::Load(const MetaSave* pSave) { + if (!pSave) + return -1; - // Reset Metaman's AI skill in case those won't be loaded from older saves - for (int team = Activity::TeamOne ; team < Activity::MaxTeamCount; team++) - m_TeamAISkill[team] = Activity::DefaultSkill; + // Reset Metaman's AI skill in case those won't be loaded from older saves + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; team++) + m_TeamAISkill[team] = Activity::DefaultSkill; - // Create the reader to read the metagame state from - Reader reader(pSave->GetSavePath().c_str(), false, 0, false); - if (!reader.ReaderOK()) - return -1; + // Create the reader to read the metagame state from + Reader reader(pSave->GetSavePath().c_str(), false, 0, false); + if (!reader.ReaderOK()) + return -1; - // Clear off players, scenes, and offensive activiies before filling up on new ones read from disk - m_Players.clear(); - for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) - delete (*sItr); - m_Scenes.clear(); - for (std::vector::iterator aItr = m_RoundOffensives.begin(); aItr != m_RoundOffensives.end(); ++aItr) - delete (*aItr); - m_RoundOffensives.clear(); + // Clear off players, scenes, and offensive activiies before filling up on new ones read from disk + m_Players.clear(); + for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) + delete (*sItr); + m_Scenes.clear(); + for (std::vector::iterator aItr = m_RoundOffensives.begin(); aItr != m_RoundOffensives.end(); ++aItr) + delete (*aItr); + m_RoundOffensives.clear(); - // Now actually do the reading/loading from file - Serializable::Create(reader, true, false); + // Now actually do the reading/loading from file + Serializable::Create(reader, true, false); - // Make sure all labels etc get set properly after load - m_StateChanged = true; + // Make sure all labels etc get set properly after load + m_StateChanged = true; - return 0; -} + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a Reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the Reader's position is untouched. + + int MetaMan::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Serializable::ReadProperty(propName, reader)); + + MatchProperty("GameState", { reader >> m_GameState; }); + MatchProperty("GameName", { reader >> m_GameName; }); + MatchProperty("AddPlayer", + { + MetaPlayer player; + reader >> player; + m_Players.push_back(player); + }); + MatchProperty("TeamCount", { reader >> m_TeamCount; }); + MatchProperty("Team1Icon", { reader >> m_TeamIcons[Activity::TeamOne]; }); + MatchProperty("Team2Icon", { reader >> m_TeamIcons[Activity::TeamTwo]; }); + MatchProperty("Team3Icon", { reader >> m_TeamIcons[Activity::TeamThree]; }); + MatchProperty("Team4Icon", { reader >> m_TeamIcons[Activity::TeamFour]; }); + MatchProperty("CurrentRound", { reader >> m_CurrentRound; }); + MatchProperty("AddScene", { + Scene* pScene = new Scene; + reader >> pScene; + m_Scenes.push_back(pScene); + }); + MatchProperty("RevealedScenes", { reader >> m_RevealedScenes; }); + MatchProperty("RevealRate", { reader >> m_RevealRate; }); + MatchProperty("RevealExtra", { reader >> m_RevealExtra; }); + MatchProperty("AddOffensive", { + GAScripted* pOffensive = new GAScripted(); + reader >> pOffensive; + m_RoundOffensives.push_back(pOffensive); + }); + MatchProperty("CurrentOffensive", { reader >> m_CurrentOffensive; }); + MatchProperty("Difficulty", { reader >> m_Difficulty; }); + MatchProperty("Team1AISkill", { reader >> m_TeamAISkill[Activity::TeamOne]; }); + MatchProperty("Team2AISkill", { reader >> m_TeamAISkill[Activity::TeamTwo]; }); + MatchProperty("Team3AISkill", { reader >> m_TeamAISkill[Activity::TeamThree]; }); + MatchProperty("Team4AISkill", { reader >> m_TeamAISkill[Activity::TeamFour]; }); + MatchProperty("MetaGUI", { reader >> m_pMetaGUI; }); + + EndPropertyList; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a Reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the Reader's position is untouched. - -int MetaMan::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Serializable::ReadProperty(propName, reader)); - - MatchProperty("GameState", { reader >> m_GameState; }); - MatchProperty("GameName", { reader >> m_GameName; }); - MatchProperty("AddPlayer", - { - MetaPlayer player; - reader >> player; - m_Players.push_back(player); - }); - MatchProperty("TeamCount", { reader >> m_TeamCount; }); - MatchProperty("Team1Icon", { reader >> m_TeamIcons[Activity::TeamOne]; }); - MatchProperty("Team2Icon", { reader >> m_TeamIcons[Activity::TeamTwo]; }); - MatchProperty("Team3Icon", { reader >> m_TeamIcons[Activity::TeamThree]; }); - MatchProperty("Team4Icon", { reader >> m_TeamIcons[Activity::TeamFour]; }); - MatchProperty("CurrentRound", { reader >> m_CurrentRound; }); - MatchProperty("AddScene", { - Scene *pScene = new Scene; - reader >> pScene; - m_Scenes.push_back(pScene); - }); - MatchProperty("RevealedScenes", { reader >> m_RevealedScenes; }); - MatchProperty("RevealRate", { reader >> m_RevealRate; }); - MatchProperty("RevealExtra", { reader >> m_RevealExtra; }); - MatchProperty("AddOffensive", { - GAScripted *pOffensive = new GAScripted(); - reader >> pOffensive; - m_RoundOffensives.push_back(pOffensive); - }); - MatchProperty("CurrentOffensive", { reader >> m_CurrentOffensive; }); - MatchProperty("Difficulty", { reader >> m_Difficulty; }); - MatchProperty("Team1AISkill", { reader >> m_TeamAISkill[Activity::TeamOne]; }); - MatchProperty("Team2AISkill", { reader >> m_TeamAISkill[Activity::TeamTwo]; }); - MatchProperty("Team3AISkill", { reader >> m_TeamAISkill[Activity::TeamThree]; }); - MatchProperty("Team4AISkill", { reader >> m_TeamAISkill[Activity::TeamFour]; }); - MatchProperty("MetaGUI", { reader >> m_pMetaGUI; }); - - EndPropertyList; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this MetaMan to an output stream for + // later recreation with Create(Reader &reader); + + int MetaMan::Save(Writer& writer) const { + Serializable::Save(writer); + + writer.NewPropertyWithValue("GameState", m_GameState); + writer.NewPropertyWithValue("GameName", m_GameName); + writer.NewPropertyWithValue("Difficulty", m_Difficulty); + writer.NewPropertyWithValue("Team1AISkill", m_TeamAISkill[Activity::TeamOne]); + writer.NewPropertyWithValue("Team2AISkill", m_TeamAISkill[Activity::TeamTwo]); + writer.NewPropertyWithValue("Team3AISkill", m_TeamAISkill[Activity::TeamThree]); + writer.NewPropertyWithValue("Team4AISkill", m_TeamAISkill[Activity::TeamFour]); + + for (const MetaPlayer& metaPlayer: m_Players) { + writer.NewPropertyWithValue("AddPlayer", metaPlayer); + } + writer.NewPropertyWithValue("TeamCount", m_TeamCount); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this MetaMan to an output stream for -// later recreation with Create(Reader &reader); - -int MetaMan::Save(Writer &writer) const { - Serializable::Save(writer); - - writer.NewPropertyWithValue("GameState", m_GameState); - writer.NewPropertyWithValue("GameName", m_GameName); - writer.NewPropertyWithValue("Difficulty", m_Difficulty); - writer.NewPropertyWithValue("Team1AISkill", m_TeamAISkill[Activity::TeamOne]); - writer.NewPropertyWithValue("Team2AISkill", m_TeamAISkill[Activity::TeamTwo]); - writer.NewPropertyWithValue("Team3AISkill", m_TeamAISkill[Activity::TeamThree]); - writer.NewPropertyWithValue("Team4AISkill", m_TeamAISkill[Activity::TeamFour]); - - for (const MetaPlayer &metaPlayer : m_Players) { - writer.NewPropertyWithValue("AddPlayer", metaPlayer); - } + if (m_TeamCount >= 1) { + writer.NewProperty("Team1Icon"); + m_TeamIcons[Activity::TeamOne].SavePresetCopy(writer); + } + if (m_TeamCount >= 2) { + writer.NewProperty("Team2Icon"); + m_TeamIcons[Activity::TeamTwo].SavePresetCopy(writer); + } + if (m_TeamCount >= 3) { + writer.NewProperty("Team3Icon"); + m_TeamIcons[Activity::TeamThree].SavePresetCopy(writer); + } + if (m_TeamCount >= 4) { + writer.NewProperty("Team4Icon"); + m_TeamIcons[Activity::TeamFour].SavePresetCopy(writer); + } + + writer.NewPropertyWithValue("CurrentRound", m_CurrentRound); + + for (const Scene* metaScene: m_Scenes) { + // Save the scene data to a good unique prefix for the Scene's layers' bitmap files as they are saved + // This should be handled separately from any .ini writing + //(*sItr)->SaveData(writer.GetFolderPath() + m_GameName + " - " + (*sItr)->GetPresetName(), false); + writer.NewPropertyWithValue("AddScene", metaScene); + } + + writer.NewPropertyWithValue("RevealedScenes", m_RevealedScenes); + writer.NewPropertyWithValue("RevealRate", m_RevealRate); + writer.NewPropertyWithValue("RevealExtra", m_RevealExtra); - writer.NewPropertyWithValue("TeamCount", m_TeamCount); + for (const GAScripted* metaOffensive: m_RoundOffensives) { + writer.NewPropertyWithValue("AddOffensive", metaOffensive); + } - if (m_TeamCount >= 1) { - writer.NewProperty("Team1Icon"); - m_TeamIcons[Activity::TeamOne].SavePresetCopy(writer); + writer.NewPropertyWithValue("CurrentOffensive", m_CurrentOffensive); + writer.NewPropertyWithValue("MetaGUI", m_pMetaGUI); + + return 0; } - if (m_TeamCount >= 2) { - writer.NewProperty("Team2Icon"); - m_TeamIcons[Activity::TeamTwo].SavePresetCopy(writer); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SaveSceneData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the bitmap data of all Scenes of this Metagame that are currently + // loaded. + + int MetaMan::SaveSceneData(std::string pathBase) { + for (std::vector::const_iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { + // Only save the data of revealed scenes that have already had their layers built and saved into files + if ((*sItr)->IsRevealed() && (*sItr)->GetTerrain() && (*sItr)->GetTerrain()->IsLoadedFromDisk()) { + // Save the scene data to a good unique prefix for the Scene's layers' bitmap files as they are saved + if ((*sItr)->SaveData(pathBase + " - " + (*sItr)->GetPresetName(), false) < 0) + return -1; + } + } + return 0; } - if (m_TeamCount >= 3) { - writer.NewProperty("Team3Icon"); - m_TeamIcons[Activity::TeamThree].SavePresetCopy(writer); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadSceneData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads the bitmap data of all Scenes of this Metagame that have once + // been saved to files. + + int MetaMan::LoadSceneData() { + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Only load the data of revealed scenes that have already had their layers built and saved into files + if ((*sItr)->IsRevealed() && (*sItr)->GetTerrain() && (*sItr)->GetTerrain()->IsLoadedFromDisk()) { + // Only load the scene layer data, don't place objects or do any init for actually playing the scene + if ((*sItr)->LoadData(false, false) < 0) + return -1; + } + } + return 0; } - if (m_TeamCount >= 4) { - writer.NewProperty("Team4Icon"); - m_TeamIcons[Activity::TeamFour].SavePresetCopy(writer); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearSceneData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the bitmap data of all Scenes of this Metagame that have once + // been saved to files. + + int MetaMan::ClearSceneData() { + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + if ((*sItr)->ClearData() < 0) + return -1; + } + return 0; } - writer.NewPropertyWithValue("CurrentRound", m_CurrentRound); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the MetaMan object. + + void MetaMan::Destroy() { + delete m_pMetaGUI; + + for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) + delete (*sItr); + for (std::vector::iterator aItr = m_RoundOffensives.begin(); aItr != m_RoundOffensives.end(); ++aItr) + delete (*aItr); - for (const Scene *metaScene : m_Scenes) { - // Save the scene data to a good unique prefix for the Scene's layers' bitmap files as they are saved - // This should be handled separately from any .ini writing - //(*sItr)->SaveData(writer.GetFolderPath() + m_GameName + " - " + (*sItr)->GetPresetName(), false); - writer.NewPropertyWithValue("AddScene", metaScene); + Clear(); } - writer.NewPropertyWithValue("RevealedScenes", m_RevealedScenes); - writer.NewPropertyWithValue("RevealRate", m_RevealRate); - writer.NewPropertyWithValue("RevealExtra", m_RevealExtra); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlayerTurn + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows which player's turn is now or coming up. + + int MetaMan::GetPlayerTurn() const { + // Player 1's turn is coming up on this round + if (g_MetaMan.m_GameState <= PLAYER1TURN) + return Players::PlayerOne; + // we're past the player turns on this round, so player 1 is up next again + else if ((g_MetaMan.m_GameState - PLAYER1TURN) > (m_Players.size() - 1)) + return Players::PlayerOne; + + // Return whos player's turn it is + return g_MetaMan.m_GameState - PLAYER1TURN; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMetaPlayerOfInGamePlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the MetaPlayer playing a specific in-game player, if any. + + MetaPlayer* MetaMan::GetMetaPlayerOfInGamePlayer(int inGamePlayer) { + for (std::vector::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { + if ((*itr).GetInGamePlayer() == inGamePlayer) + return &(*itr); + } - for (const GAScripted *metaOffensive : m_RoundOffensives) { - writer.NewPropertyWithValue("AddOffensive", metaOffensive); + // Didn't find any metaplayer that is using that in-game player + return 0; } - writer.NewPropertyWithValue("CurrentOffensive", m_CurrentOffensive); - writer.NewPropertyWithValue("MetaGUI", m_pMetaGUI); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNextSceneOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the next Scene in play that is owned by a specific player. - return 0; -} + const Scene* MetaMan::GetNextSceneOfPlayer(int player, const Scene* pStartScene) const { + if (m_Scenes.empty()) + return 0; + const Scene* pFoundScene = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveSceneData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the bitmap data of all Scenes of this Metagame that are currently -// loaded. - -int MetaMan::SaveSceneData(std::string pathBase) -{ - for (std::vector::const_iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) - { - // Only save the data of revealed scenes that have already had their layers built and saved into files - if ((*sItr)->IsRevealed() && (*sItr)->GetTerrain() && (*sItr)->GetTerrain()->IsLoadedFromDisk()) - { - // Save the scene data to a good unique prefix for the Scene's layers' bitmap files as they are saved - if ((*sItr)->SaveData(pathBase + " - " + (*sItr)->GetPresetName(), false) < 0) - return -1; - } - } - return 0; -} + // If no valid start scene specified, just start search immediately + bool foundStart = !(pStartScene && pStartScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(player)); + // Search for the next scene owned by the currently animated player + int scenesSearched = 0; + for (std::vector::const_iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Don't search beyond what has been revealed already + if (scenesSearched >= std::floor(m_RevealedScenes)) + break; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadSceneData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads the bitmap data of all Scenes of this Metagame that have once -// been saved to files. - -int MetaMan::LoadSceneData() -{ - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Only load the data of revealed scenes that have already had their layers built and saved into files - if ((*sItr)->IsRevealed() && (*sItr)->GetTerrain() && (*sItr)->GetTerrain()->IsLoadedFromDisk()) - { - // Only load the scene layer data, don't place objects or do any init for actually playing the scene - if ((*sItr)->LoadData(false, false) < 0) - return -1; - } - } - return 0; -} + // Find the place where to start the actual search for the next owned Scene from + if (!foundStart) { + // Ok, found the start, now begin actual search + if (*sItr == pStartScene) + foundStart = true; + } + // Now find the actual next owned Scene of this player + else if ((*sItr)->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(player)) { + pFoundScene = *sItr; + break; + } + ++scenesSearched; + } + return pFoundScene; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearSceneData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the bitmap data of all Scenes of this Metagame that have once -// been saved to files. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalBrainCountOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total number of brains that a player has, including ones of + // his that are resident down on sites. + + int MetaMan::GetTotalBrainCountOfPlayer(int metaPlayer, bool countPoolsOnly) const { + if (metaPlayer <= Players::NoPlayer || metaPlayer >= Players::MaxPlayerCount) + return 0; + + // Count the pool first + int brainCount = m_Players[metaPlayer].GetBrainPoolCount(); + // Plus any that are out travelling between sites + brainCount += m_Players[metaPlayer].GetBrainsInTransit(); + + if (!countPoolsOnly) { + for (std::vector::const_iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { + // Add up any brains installed as resident on any sites + if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == GetTeamOfPlayer(metaPlayer) && (*sItr)->GetResidentBrain(m_Players[metaPlayer].GetInGamePlayer())) + brainCount += 1; + } + } -int MetaMan::ClearSceneData() -{ - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - if ((*sItr)->ClearData() < 0) - return -1; - } - return 0; -} + return brainCount; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldCountOfTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total gold funds of all the players of a specific team combined. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MetaMan object. + int MetaMan::GetGoldCountOfTeam(int team) const { + if (team <= Activity::NoTeam || team >= m_TeamCount) + return 0; -void MetaMan::Destroy() -{ - delete m_pMetaGUI; + float goldTotal = 0; + // Go through all players and add up the funds of all who belong to this team + for (int metaPlayer = Players::PlayerOne; metaPlayer < m_Players.size(); ++metaPlayer) { + if (m_Players[metaPlayer].GetTeam() == team) + goldTotal += m_Players[metaPlayer].GetFunds(); + } - for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) - delete (*sItr); - for (std::vector::iterator aItr = m_RoundOffensives.begin(); aItr != m_RoundOffensives.end(); ++aItr) - delete (*aItr); + return goldTotal; + } - Clear(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSceneCountOfTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total number of bases that any specific team owns. + + int MetaMan::GetSceneCountOfTeam(int team) const { + if (team <= Activity::NoTeam || team >= m_TeamCount) + return 0; + + // Go through all scenes and add up all the ones owned by this team + int sceneCount = 0; + for (std::vector::const_iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { + // Add up any brains installed as resident on any sites + if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == team) + ++sceneCount; + } + return sceneCount; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlayerTurn -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows which player's turn is now or coming up. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalBrainCountOfTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total number of brains that a team has, including ones that + // are resident down on sites. -int MetaMan::GetPlayerTurn() const -{ - // Player 1's turn is coming up on this round - if (g_MetaMan.m_GameState <= PLAYER1TURN) - return Players::PlayerOne; - // we're past the player turns on this round, so player 1 is up next again - else if ((g_MetaMan.m_GameState - PLAYER1TURN) > (m_Players.size() - 1)) - return Players::PlayerOne; + int MetaMan::GetTotalBrainCountOfTeam(int team, bool countPoolsOnly) const { + if (team <= Activity::NoTeam || team >= m_TeamCount) + return 0; - // Return whos player's turn it is - return g_MetaMan.m_GameState - PLAYER1TURN; -} + // Go through all players and add up the brains of the ones who are on this team + int brainCount = 0; + for (int metaPlayer = Players::PlayerOne; metaPlayer < m_Players.size(); ++metaPlayer) { + if (m_Players[metaPlayer].GetTeam() == team) + brainCount += GetTotalBrainCountOfPlayer(metaPlayer, countPoolsOnly); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMetaPlayerOfInGamePlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the MetaPlayer playing a specific in-game player, if any. + return brainCount; + } -MetaPlayer * MetaMan::GetMetaPlayerOfInGamePlayer(int inGamePlayer) -{ - for (std::vector::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if ((*itr).GetInGamePlayer() == inGamePlayer) - return &(*itr); - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnlyTeamWithAnyBrainPoolLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates which team, if any, is the only one left with brains in its + // pool. + + int MetaMan::OnlyTeamWithAnyBrainPoolLeft() { + // See if only one team remains with any brains + int brainTeamCount = 0; + int brainTeam = Activity::NoTeam; + for (int t = Activity::TeamOne; t < m_TeamCount; ++t) { + // Only count brains in pools; not resident ones also + if (GetTotalBrainCountOfTeam(t, true) > 0) { + brainTeamCount++; + brainTeam = t; + } + } - // Didn't find any metaplayer that is using that in-game player - return 0; -} + // If exactly one team with brains, return that + if (brainTeamCount == 1) + return brainTeam; + // None OR more than two teams are left with brains! + return Activity::NoTeam; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextSceneOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next Scene in play that is owned by a specific player. - -const Scene * MetaMan::GetNextSceneOfPlayer(int player, const Scene *pStartScene) const -{ - if (m_Scenes.empty()) - return 0; - - const Scene *pFoundScene = 0; - - // If no valid start scene specified, just start search immediately - bool foundStart = !(pStartScene && pStartScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(player)); - - // Search for the next scene owned by the currently animated player - int scenesSearched = 0; - for (std::vector::const_iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Don't search beyond what has been revealed already - if (scenesSearched >= std::floor(m_RevealedScenes)) - break; - - // Find the place where to start the actual search for the next owned Scene from - if (!foundStart) - { - // Ok, found the start, now begin actual search - if (*sItr == pStartScene) - foundStart = true; - } - // Now find the actual next owned Scene of this player - else if ((*sItr)->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(player)) - { - pFoundScene = *sItr; - break; - } - ++scenesSearched; - } - - return pFoundScene; -} + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OneOrNoneTeamsLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether there is less than two teams left in this game with + // a brain in its ranks at all. + + bool MetaMan::OneOrNoneTeamsLeft() + { + // See if only one team remains with any brains + int brainTeamCount = 0; + int brainTeam = Activity::NoTeam; + for (int t = Activity::TeamOne; t < m_TeamCount; ++t) + { + // Any brains left on this team? If so, they're a potential winner + if (GetTotalBrainCountOfTeam(t) > 0) + { + brainTeamCount++; + brainTeam = t; + } + } + + // If less than two teams left with any brains, they get indicated + // Also, if NO teams with brain are left, that is indicated with NoTeam + if (brainTeamCount <= 1) + return true; + + return false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalBrainCountOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total number of brains that a player has, including ones of -// his that are resident down on sites. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WhichTeamLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates which single team is left, if any. + // Arguments: None. + + int MetaMan::WhichTeamLeft() + { + int whichTeam = Activity::NoTeam; + + // See if only one team remains with any brains + int brainTeamCount = 0; + int brainTeam = Activity::NoTeam; + for (int t = Activity::TeamOne; t < m_TeamCount; ++t) + { + if (GetTotalBrainCountOfTeam(t) > 0) + { + brainTeamCount++; + brainTeam = t; + } + } + + // If exactly one team with brains, return that + if (brainTeamCount == 1) + return brainTeam; + + // No team is left with brains! + return Activity::NoTeam; + } + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: NoBrainsLeftInAnyPool + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether there are no brains left in any active player's pool + // at all. This does NOT count deployed brain in bases. + + bool MetaMan::NoBrainsLeftInAnyPool() { + // Go through all players and check each for any brains in any pool + for (std::vector::iterator mpItr = m_Players.begin(); mpItr != m_Players.end(); ++mpItr) { + if ((*mpItr).GetBrainPoolCount() > 0) + return false; + } + return true; + } -int MetaMan::GetTotalBrainCountOfPlayer(int metaPlayer, bool countPoolsOnly) const -{ - if (metaPlayer <= Players::NoPlayer || metaPlayer >= Players::MaxPlayerCount) - return 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WhichTeamIsLeading + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates which single team has the most owned bases, and if there's a + // tie between two teams, total owned gold funds is used as a tiebreaker. + + int MetaMan::WhichTeamIsLeading() { + int leaderTeam = Activity::NoTeam; + bool tiedTeams[Activity::MaxTeamCount]; + for (int t = Activity::TeamOne; t < m_TeamCount; ++t) + tiedTeams[t] = false; + + int baseCount = 0; + // If we have a tie between two teams + bool baseCountTie = false; + // This is the record so far; negative so the first team with 0 won't detect as tied + int highestBaseCount = -1; + for (int team = Activity::TeamOne; team < m_TeamCount; ++team) { + baseCount = GetSceneCountOfTeam(team); + // We have a tie! + if (baseCount == highestBaseCount) { + // No leader - there's a tie + leaderTeam = Activity::NoTeam; + tiedTeams[team] = true; + baseCountTie = true; + } + // In the lead; clear all other tie flags + if (baseCount > highestBaseCount) { + // Leader! + leaderTeam = team; + highestBaseCount = baseCount; + // No more tie + for (int t = Activity::TeamOne; t < m_TeamCount; ++t) + tiedTeams[t] = false; + // This team is now tied with itself (ie not tied) + tiedTeams[team] = true; + // There's no tie as of now + baseCountTie = false; + } + } - // Count the pool first - int brainCount = m_Players[metaPlayer].GetBrainPoolCount(); - // Plus any that are out travelling between sites - brainCount += m_Players[metaPlayer].GetBrainsInTransit(); + // If we have a tie in base count; then break the tie by looking at total gold funds of all tied teams + if (baseCountTie) { + float highestGold = 0; + // Go through all tied teams + for (int team = Activity::TeamOne; team < m_TeamCount; ++team) { + // One of the teams tied in bases + if (tiedTeams[team]) { + if (GetGoldCountOfTeam(team) >= highestGold) { + // New leader! + highestGold = GetGoldCountOfTeam(team); + leaderTeam = team; + } + } + } + } - if (!countPoolsOnly) - { - for (std::vector::const_iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) - { - // Add up any brains installed as resident on any sites - if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == GetTeamOfPlayer(metaPlayer) && (*sItr)->GetResidentBrain(m_Players[metaPlayer].GetInGamePlayer())) - brainCount += 1; - } - } + // We have a winner! + return leaderTeam; + } - return brainCount; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSceneIncomeOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total income from all scenes owned by a specific player. + float MetaMan::GetSceneIncomeOfPlayer(int metaPlayer) const { + float totalIncome = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldCountOfTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total gold funds of all the players of a specific team combined. + for (std::vector::const_iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Add up all the generated income for this player this round + if ((*sItr)->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer)) + totalIncome += (*sItr)->GetRoundIncome(); + } + return totalIncome; + } -int MetaMan::GetGoldCountOfTeam(int team) const -{ - if (team <= Activity::NoTeam || team >= m_TeamCount) - return 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBudgetedRatioOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the ratio of funds already allocated to budgets of this player. - float goldTotal = 0; - // Go through all players and add up the funds of all who belong to this team - for (int metaPlayer = Players::PlayerOne; metaPlayer < m_Players.size(); ++metaPlayer) - { - if (m_Players[metaPlayer].GetTeam() == team) - goldTotal += m_Players[metaPlayer].GetFunds(); - } + float MetaMan::GetBudgetedRatioOfPlayer(int metaPlayer, const Scene* pException, bool includeOffensive, bool includeDefensive) const { + float totalAllocated = 0; - return goldTotal; -} + // Counting defensive allocations + if (includeDefensive) { + for (std::vector::const_iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Add up all the allocated funds so far this round, first of bases we're building + if ((*sItr)->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer) && *sItr != pException) + totalAllocated += (*sItr)->GetBuildBudget(m_Players[metaPlayer].GetInGamePlayer()); + } + } + // Also the money allocated for offensive action + if (includeOffensive && !m_Players[metaPlayer].GetOffensiveTargetName().empty() && (!pException || (pException && pException->GetPresetName() != m_Players[metaPlayer].GetOffensiveTargetName()))) + totalAllocated += m_Players[metaPlayer].GetOffensiveBudget(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSceneCountOfTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total number of bases that any specific team owns. + return totalAllocated / m_Players[metaPlayer].GetFunds(); + } -int MetaMan::GetSceneCountOfTeam(int team) const -{ - if (team <= Activity::NoTeam || team >= m_TeamCount) - return 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSuspend + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Suspends or unsuspends the game so exclusive GUIs and menus can be + // shown + + void MetaMan::SetSuspend(bool suspend) { + if (suspend && !m_Suspended) { + m_Suspended = true; + m_GameSaved = false; + } else if (!suspend && m_Suspended) { + m_Suspended = false; + m_GameSaved = false; + } + } - // Go through all scenes and add up all the ones owned by this team - int sceneCount = 0; - for (std::vector::const_iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) - { - // Add up any brains installed as resident on any sites - if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == team) - ++sceneCount; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WhichTeamOwnsAllSites + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks whether one team has ownership of all revealed sites. + + int MetaMan::WhichTeamOwnsAllSites() { + int owner = Activity::NoTeam; + for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { + if ((*sItr)->IsRevealed()) { + // A site with no owner means that not all sites have been taken duh + if ((*sItr)->GetTeamOwnership() == Activity::NoTeam) { + owner = Activity::NoTeam; + break; + } + + // So the site is owned by someone, and that someone is the only encountered owner yet + if (owner == Activity::NoTeam || (*sItr)->GetTeamOwnership() == owner) + owner = (*sItr)->GetTeamOwnership(); + // We found two diff teams owning sites, so noone owns em all + else { + owner = Activity::NoTeam; + break; + } + } + } + return owner; + } - return sceneCount; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsGameOver + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks for game over condition + bool MetaMan::IsGameOver() { + // This is the old condition of all sites being conquered + // if (m_RevealedScenes >= m_Scenes.size() && WhichTeamOwnsAllSites() != Activity::NoTeam) + // return true; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalBrainCountOfTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total number of brains that a team has, including ones that -// are resident down on sites. + // GAME IS OVER: + // IF no players have any brains left in their respective pool, OR only one team does AND they are the leader in sites owned + int onlyTeamLeft = OnlyTeamWithAnyBrainPoolLeft(); + if (NoBrainsLeftInAnyPool() || (onlyTeamLeft != Activity::NoTeam && WhichTeamIsLeading() == onlyTeamLeft)) + return true; -int MetaMan::GetTotalBrainCountOfTeam(int team, bool countPoolsOnly) const -{ - if (team <= Activity::NoTeam || team >= m_TeamCount) - return 0; + return false; + } - // Go through all players and add up the brains of the ones who are on this team - int brainCount = 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TotalScenePresets + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Yields a set of ALL eligible Scene presets for a new game. + + int MetaMan::TotalScenePresets(std::list* pScenes) { + int totalCount = 0; + // Get the list of ALL read-in Scene presets + std::list allScenePresets; + g_PresetMan.GetAllOfType(allScenePresets, "Scene"); + Scene* pScenePreset = 0; + + // Temporary list of planet locations already being used + std::list usedLocations; + bool locationOK = true; + + if (pScenes) + pScenes->clear(); + + // Go through the preset list and count/copy over all eligible ones + for (std::list::iterator sItr = allScenePresets.begin(); sItr != allScenePresets.end(); ++sItr) { + pScenePreset = dynamic_cast(*sItr); + // Filter out editor or special scenes, or ones that don't have locations defined. + if (pScenePreset && !pScenePreset->GetLocation().IsZero() && pScenePreset->IsMetagamePlayable() && pScenePreset->GetMetasceneParent() == "") { + // Make sure this exact site location on the planet isn't occupied already + locationOK = true; + for (std::list::iterator vItr = usedLocations.begin(); vItr != usedLocations.end(); ++vItr) { + if (pScenePreset->GetLocation() == *vItr) { + locationOK = false; + break; + } + } + + if (locationOK) { + // Add this unique location to the list of locations that are now occupied + usedLocations.push_back(pScenePreset->GetLocation()); + // Add to list if there is a list + if (pScenes) + pScenes->push_back(pScenePreset); + // Count the eligible scene + totalCount++; + } + } + } - for (int metaPlayer = Players::PlayerOne; metaPlayer < m_Players.size(); ++metaPlayer) - { - if (m_Players[metaPlayer].GetTeam() == team) - brainCount += GetTotalBrainCountOfPlayer(metaPlayer, countPoolsOnly); - } + return totalCount; + } - return brainCount; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SelectScenePresets + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Yields a set of randomly selected Scene presets for a new game. + + int MetaMan::SelectScenePresets(int gameSize, std::list* pSelected) { + // Get the list of ALL eligible read-in Scene presets + std::list scenePresets; + TotalScenePresets(&scenePresets); + + // If we need to actually fill the list, do so + if (pSelected) { + // Go through the list and randomly knock out as many presets as necessary to reach the number we need for this game + int randomIndex; + int currentIndex; + while (scenePresets.size() > gameSize) { + // Randomly select one of the scenes and remove it + currentIndex = 0; + randomIndex = RandomNum(0, scenePresets.size() - 1); + for (std::list::iterator pItr = scenePresets.begin(); pItr != scenePresets.end(); ++pItr) { + if (currentIndex == randomIndex) { + scenePresets.erase(pItr); + break; + } + currentIndex++; + } + } + // Cast and copy (not deep!) to fill the provided list + pSelected->clear(); + for (Scene* scenePointer: scenePresets) { + pSelected->push_back(scenePointer); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnlyTeamWithAnyBrainPoolLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates which team, if any, is the only one left with brains in its -// pool. - -int MetaMan::OnlyTeamWithAnyBrainPoolLeft() -{ - // See if only one team remains with any brains - int brainTeamCount = 0; - int brainTeam = Activity::NoTeam; - for (int t = Activity::TeamOne; t < m_TeamCount; ++t) - { - // Only count brains in pools; not resident ones also - if (GetTotalBrainCountOfTeam(t, true) > 0) - { - brainTeamCount++; - brainTeam = t; - } - } - - // If exactly one team with brains, return that - if (brainTeamCount == 1) - return brainTeam; - - // None OR more than two teams are left with brains! - return Activity::NoTeam; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OneOrNoneTeamsLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether there is less than two teams left in this game with -// a brain in its ranks at all. - -bool MetaMan::OneOrNoneTeamsLeft() -{ - // See if only one team remains with any brains - int brainTeamCount = 0; - int brainTeam = Activity::NoTeam; - for (int t = Activity::TeamOne; t < m_TeamCount; ++t) - { - // Any brains left on this team? If so, they're a potential winner - if (GetTotalBrainCountOfTeam(t) > 0) - { - brainTeamCount++; - brainTeam = t; - } - } - - // If less than two teams left with any brains, they get indicated - // Also, if NO teams with brain are left, that is indicated with NoTeam - if (brainTeamCount <= 1) - return true; - - return false; -} + return gameSize; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearActivities + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears out all the lined-up activities for the current round. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WhichTeamLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates which single team is left, if any. -// Arguments: None. - -int MetaMan::WhichTeamLeft() -{ - int whichTeam = Activity::NoTeam; - - // See if only one team remains with any brains - int brainTeamCount = 0; - int brainTeam = Activity::NoTeam; - for (int t = Activity::TeamOne; t < m_TeamCount; ++t) - { - if (GetTotalBrainCountOfTeam(t) > 0) - { - brainTeamCount++; - brainTeam = t; - } - } - - // If exactly one team with brains, return that - if (brainTeamCount == 1) - return brainTeam; - - // No team is left with brains! - return Activity::NoTeam; -} -*/ + void MetaMan::ClearActivities() { + for (std::vector::iterator aItr = m_RoundOffensives.begin(); aItr != m_RoundOffensives.end(); ++aItr) + delete (*aItr); + m_RoundOffensives.clear(); + m_CurrentOffensive = 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: NoBrainsLeftInAnyPool -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether there are no brains left in any active player's pool -// at all. This does NOT count deployed brain in bases. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AIPlayerTurn + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does all the things an AI player needs to do during his turn. -bool MetaMan::NoBrainsLeftInAnyPool() -{ - // Go through all players and check each for any brains in any pool - for (std::vector::iterator mpItr = m_Players.begin(); mpItr != m_Players.end(); ++mpItr) - { - if ((*mpItr).GetBrainPoolCount() > 0) - return false; - } - return true; -} + void MetaMan::AIPlayerTurn(int metaPlayer) { + if (metaPlayer < 0 || metaPlayer >= m_Players.size()) + return; + MetaPlayer* pThisPlayer = &(m_Players[metaPlayer]); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WhichTeamIsLeading -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates which single team has the most owned bases, and if there's a -// tie between two teams, total owned gold funds is used as a tiebreaker. - -int MetaMan::WhichTeamIsLeading() -{ - int leaderTeam = Activity::NoTeam; - bool tiedTeams[Activity::MaxTeamCount]; - for (int t = Activity::TeamOne; t < m_TeamCount; ++t) - tiedTeams[t] = false; - - int baseCount = 0; - // If we have a tie between two teams - bool baseCountTie = false; - // This is the record so far; negative so the first team with 0 won't detect as tied - int highestBaseCount = -1; - for (int team = Activity::TeamOne; team < m_TeamCount; ++team) - { - baseCount = GetSceneCountOfTeam(team); - // We have a tie! - if (baseCount == highestBaseCount) - { - // No leader - there's a tie - leaderTeam = Activity::NoTeam; - tiedTeams[team] = true; - baseCountTie = true; - } - // In the lead; clear all other tie flags - if (baseCount > highestBaseCount) - { - // Leader! - leaderTeam = team; - highestBaseCount = baseCount; - // No more tie - for (int t = Activity::TeamOne; t < m_TeamCount; ++t) - tiedTeams[t] = false; - // This team is now tied with itself (ie not tied) - tiedTeams[team] = true; - // There's no tie as of now - baseCountTie = false; - } - } - - // If we have a tie in base count; then break the tie by looking at total gold funds of all tied teams - if (baseCountTie) - { - float highestGold = 0; - // Go through all tied teams - for (int team = Activity::TeamOne; team < m_TeamCount; ++team) - { - // One of the teams tied in bases - if (tiedTeams[team]) - { - if (GetGoldCountOfTeam(team) >= highestGold) - { - // New leader! - highestGold = GetGoldCountOfTeam(team); - leaderTeam = team; - } - } - } - } - - // We have a winner! - return leaderTeam; -} + // If this player has no brains at all left, then do nothing + if (GetTotalBrainCountOfPlayer(metaPlayer) <= 0) { + pThisPlayer->SetGameOverRound(m_CurrentRound); + pThisPlayer->SetOffensiveBudget(0); + return; + } + // Tally up all the scenes according to who owns them + int sceneCount = 0; + int revealedScenes = std::floor(m_RevealedScenes); + std::vector ownedScenes; + std::vector enemyScenes; + std::vector unclaimedScenes; + + float sceneMark = 0; + Scene* pBestAttackCandidateScene = 0; + + for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { + float currentMark = 0; + + // We are only concerned with Scenes that are currently revealed and in play + ++sceneCount; + if (sceneCount > revealedScenes) + break; + + // Scene is owned by this guy's team + if ((*sItr)->GetTeamOwnership() == pThisPlayer->GetTeam()) + ownedScenes.push_back(*sItr); + // Enemy-owned scene + else if ((*sItr)->GetTeamOwnership() != Activity::NoTeam) { + enemyScenes.push_back(*sItr); + // Scenes with heavy investment owned by a team with lots of funds are less likely to attack + currentMark = -((*sItr)->GetTotalInvestment() + GetGoldCountOfTeam((*sItr)->GetTeamOwnership())); + } + // Unoccupied scene + else { + unclaimedScenes.push_back(*sItr); + // Unclaimed scenes are guaranteed to attack + currentMark = 1000; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSceneIncomeOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total income from all scenes owned by a specific player. + // Set new attack candidate if we have some better options to attack + if (currentMark != 0 && currentMark > sceneMark) { + sceneMark = currentMark; + pBestAttackCandidateScene = (*sItr); + } + } -float MetaMan::GetSceneIncomeOfPlayer(int metaPlayer) const -{ - float totalIncome = 0; + // Decide how much of current budget to spend on offense vs defense, and also unassigned countering budget for when we are attacked at a base + float counterRatio = 0.15; + float offenseRatio = pThisPlayer->GetAggressiveness() * 0.8; + float attackGoldThreshold = 250 + ownedScenes.size() * 250; + + if (!unclaimedScenes.empty()) + attackGoldThreshold = 250; + + // Special case: no brains left in pool to attack with + if (pThisPlayer->GetBrainPoolCount() <= 0) { + // Save for someone attacking back + counterRatio = 0.5; + // Nothing to attack with + offenseRatio = 0; + pThisPlayer->SetOffensiveTargetName(""); + } + // Special case: no owned bases + else if (ownedScenes.empty()) { + // Don't save anything for counter or defenses if we don't have any bases! + counterRatio = 0; + // Also don't hold back anything for defense if we don't have any bases to spend on + offenseRatio = 1.0; + // Always attack now matter how low the funds are, there's no way to get them anyway + attackGoldThreshold = -1; + } - for (std::vector::const_iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Add up all the generated income for this player this round - if ((*sItr)->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer)) - totalIncome += (*sItr)->GetRoundIncome(); - } - return totalIncome; -} + // What remains is for defense + float defenseRatio = 1.0 - offenseRatio - counterRatio; + + // Set the attack budget, if there's anything to attack, and anything to attack with + if ((!enemyScenes.empty() || !unclaimedScenes.empty()) && offenseRatio > 0 && pThisPlayer->GetFunds() * offenseRatio >= attackGoldThreshold) { + pThisPlayer->SetOffensiveBudget(pThisPlayer->GetFunds() * offenseRatio); + // Use two methods to select which scene to attack, first one is based on the previously obtained scene mark and the second is mostly random + if (RandomNum() < 0.6F && pBestAttackCandidateScene) { + pThisPlayer->SetOffensiveTargetName(pBestAttackCandidateScene->GetPresetName()); + } else { + // And the target scene, randomly selected for now from all unfriendly targets + int unfriendlySceneCount = enemyScenes.size() + unclaimedScenes.size(); + int targetIndex = RandomNum(0, unfriendlySceneCount - 1); + // Give it a strong preference for unclaimed scenes! They make more strategic sense than to attack a hardened target + if (!unclaimedScenes.empty() && targetIndex >= unclaimedScenes.size()) + targetIndex = RandomNum() < 0.75F ? RandomNum(0, unclaimedScenes.size() - 1) : targetIndex; + // From index to actual Scene and selection + Scene* selectedTarget = targetIndex < unclaimedScenes.size() ? unclaimedScenes[targetIndex] : enemyScenes[targetIndex - unclaimedScenes.size()]; + if (selectedTarget) + pThisPlayer->SetOffensiveTargetName(selectedTarget->GetPresetName()); + } + } + // All on defense instead after all + else { + pThisPlayer->SetOffensiveBudget(0); + defenseRatio = 1.0; + } + // Spread out the defensive budgets on the owned sites + float totalDefenseBudget = pThisPlayer->GetFunds() * defenseRatio; + int player = pThisPlayer->GetInGamePlayer(); + for (std::vector::iterator sItr = ownedScenes.begin(); sItr != ownedScenes.end(); ++sItr) { + // Evenly, for now.. later, might want to prioritize sites with established bases, or opposite? + (*sItr)->SetBuildBudget(player, totalDefenseBudget / ownedScenes.size()); + + // Save the build budget ratio that was selected as well, so it can be re-used next round? + // (AI doesn't really need to use this since they re-do their allocation algo above each round) + if (pThisPlayer->GetFunds() > 0) + (*sItr)->SetBuildBudgetRatio(player, (*sItr)->GetBuildBudget(player) / pThisPlayer->GetFunds()); + else + (*sItr)->SetBuildBudgetRatio(player, 0); + + // Move building pieces from the Scene's AI blueprint queue to the actual blueprints, but only approximately as much as can afford, so the entire AI pre-built base plan isn't revealed + (*sItr)->ApplyAIPlan(player); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBudgetedRatioOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the ratio of funds already allocated to budgets of this player. + // TODO: Pay for and schedule to scan a random unfriendly site to keep things fair + } -float MetaMan::GetBudgetedRatioOfPlayer(int metaPlayer, const Scene *pException, bool includeOffensive, bool includeDefensive) const -{ - float totalAllocated = 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this. Supposed to be done every frame before drawing. - // Counting defensive allocations - if (includeDefensive) - { - for (std::vector::const_iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Add up all the allocated funds so far this round, first of bases we're building - if ((*sItr)->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer) && *sItr != pException) - totalAllocated += (*sItr)->GetBuildBudget(m_Players[metaPlayer].GetInGamePlayer()); - } - } + void MetaMan::Update() { + m_pMetaGUI->Update(); - // Also the money allocated for offensive action - if (includeOffensive && !m_Players[metaPlayer].GetOffensiveTargetName().empty() && (!pException || (pException && pException->GetPresetName() != m_Players[metaPlayer].GetOffensiveTargetName()))) - totalAllocated += m_Players[metaPlayer].GetOffensiveBudget(); + //////////////////////////////////////////// + // METAGAME STATE MACHINE UPDATER - return totalAllocated / m_Players[metaPlayer].GetFunds(); -} + // Game is temporarily suspended, don't do anything + if (m_Suspended) { + } + // Game not started; the GUI will show the new game dialog + else if (m_GameState == NOGAME) { + // Show suspended so the new game dialog will show up + m_Suspended = true; + + // State end + if (m_pMetaGUI->ContinuePhase()) { + m_GameState = GAMEINTRO; + m_StateChanged = true; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSuspend -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Suspends or unsuspends the game so exclusive GUIs and menus can be -// shown - -void MetaMan::SetSuspend(bool suspend) -{ - if (suspend && !m_Suspended) - { - m_Suspended = true; - m_GameSaved = false; - } - else if (!suspend && m_Suspended) - { - m_Suspended = false; - m_GameSaved = false; - } -} + // INTRO + // Show some nice graphical intro of the campaign + else if (m_GameState == GAMEINTRO) { + // State init + if (m_StateChanged) { + m_PhaseTimer.Reset(); + m_StateChanged = false; + } + // State body -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WhichTeamOwnsAllSites -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether one team has ownership of all revealed sites. - -int MetaMan::WhichTeamOwnsAllSites() -{ - int owner = Activity::NoTeam; - for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) - { - if ((*sItr)->IsRevealed()) - { - // A site with no owner means that not all sites have been taken duh - if ((*sItr)->GetTeamOwnership() == Activity::NoTeam) - { - owner = Activity::NoTeam; - break; - } - - // So the site is owned by someone, and that someone is the only encountered owner yet - if (owner == Activity::NoTeam || (*sItr)->GetTeamOwnership() == owner) - owner = (*sItr)->GetTeamOwnership(); - // We found two diff teams owning sites, so noone owns em all - else - { - owner = Activity::NoTeam; - break; - } - } - } - return owner; -} + // State end + if (1) // m_pMetaGUI->ContinuePhase()) + { + m_GameState = NEWROUND; + m_StateChanged = true; + } + } + // NEW ROUND + // Show a nice banner for the new round and its number + else if (m_GameState == NEWROUND) { + // State init + if (m_StateChanged) { + // New day; clear out old activities + ClearActivities(); + m_PhaseTimer.Reset(); + m_StateChanged = false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsGameOver -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks for game over condition + // State body -bool MetaMan::IsGameOver() -{ -// This is the old condition of all sites being conquered -// if (m_RevealedScenes >= m_Scenes.size() && WhichTeamOwnsAllSites() != Activity::NoTeam) -// return true; + // State end + if (m_pMetaGUI->ContinuePhase()) { + m_GameState = REVEALSCENES; + m_StateChanged = true; + } + } - // GAME IS OVER: - // IF no players have any brains left in their respective pool, OR only one team does AND they are the leader in sites owned - int onlyTeamLeft = OnlyTeamWithAnyBrainPoolLeft(); - if (NoBrainsLeftInAnyPool() || (onlyTeamLeft != Activity::NoTeam && WhichTeamIsLeading() == onlyTeamLeft)) - return true; + // REVEAL SCENES + // If not all the sites are revealed yet, reveal one or two (depending on how many players playing?) + else if (m_GameState == REVEALSCENES) { + // State init + if (m_StateChanged) { + m_PhaseTimer.Reset(); + m_StateChanged = false; + } - return false; -} + // State body + // State end + if (m_pMetaGUI->ContinuePhase()) { + m_GameState = COUNTINCOME; + m_StateChanged = true; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TotalScenePresets -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Yields a set of ALL eligible Scene presets for a new game. - -int MetaMan::TotalScenePresets(std::list *pScenes) -{ - int totalCount = 0; - // Get the list of ALL read-in Scene presets - std::list allScenePresets; - g_PresetMan.GetAllOfType(allScenePresets, "Scene"); - Scene *pScenePreset = 0; - - // Temporary list of planet locations already being used - std::list usedLocations; - bool locationOK = true; - - if (pScenes) - pScenes->clear(); - - // Go through the preset list and count/copy over all eligible ones - for (std::list::iterator sItr = allScenePresets.begin(); sItr != allScenePresets.end(); ++sItr) - { - pScenePreset = dynamic_cast(*sItr); - // Filter out editor or special scenes, or ones that don't have locations defined. - if (pScenePreset && !pScenePreset->GetLocation().IsZero() && pScenePreset->IsMetagamePlayable() && pScenePreset->GetMetasceneParent() == "") - { - // Make sure this exact site location on the planet isn't occupied already - locationOK = true; - for (std::list::iterator vItr = usedLocations.begin(); vItr != usedLocations.end(); ++vItr) - { - if (pScenePreset->GetLocation() == *vItr) - { - locationOK = false; - break; - } - } - - if (locationOK) - { - // Add this unique location to the list of locations that are now occupied - usedLocations.push_back(pScenePreset->GetLocation()); - // Add to list if there is a list - if (pScenes) - pScenes->push_back(pScenePreset); - // Count the eligible scene - totalCount++; - } - } - } - - return totalCount; -} + // COUNT INCOME + // Count all the income from all owned bases, add to respective players' funds: + // Show this with lines from the bases adding to the floating player counters, showing income ratios, and leave the lines there to show base ownership and value during turns + else if (m_GameState == COUNTINCOME) { + // State init + if (m_StateChanged) { + m_PhaseTimer.Reset(); + m_StateChanged = false; + } + // State body -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SelectScenePresets -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Yields a set of randomly selected Scene presets for a new game. - -int MetaMan::SelectScenePresets(int gameSize, std::list *pSelected) -{ - // Get the list of ALL eligible read-in Scene presets - std::list scenePresets; - TotalScenePresets(&scenePresets); - - // If we need to actually fill the list, do so - if (pSelected) - { - // Go through the list and randomly knock out as many presets as necessary to reach the number we need for this game - int randomIndex; - int currentIndex; - while (scenePresets.size() > gameSize) - { - // Randomly select one of the scenes and remove it - currentIndex = 0; - randomIndex = RandomNum(0, scenePresets.size() - 1); - for (std::list::iterator pItr = scenePresets.begin(); pItr != scenePresets.end(); ++pItr) - { - if (currentIndex == randomIndex) - { - scenePresets.erase(pItr); - break; - } - currentIndex++; - } - } - - // Cast and copy (not deep!) to fill the provided list - pSelected->clear(); - for (Scene *scenePointer : scenePresets) { - pSelected->push_back(scenePointer); + // State end + if (m_pMetaGUI->ContinuePhase()) { + m_GameState = PLAYER1TURN; + m_StateChanged = true; + } } - } - return gameSize; -} + // PLAYER TURNS + // Player-controlled player turn sequence: + // Show planet and current known sites, allow inspection of each + // Allow to zoom in on each site owned by this player: + // Build blueprints (allocating defense budget) in Scene Editor Activity until player done + // Allow clicking on sites unvisited by anyone + // Allocate expedition budget to this site with a slider + // Allow clicking on sites already claimed by other player + // Allocate attack budget to this site with a slider + // [Question: allow more than one expedition/offense action per turn?? - A: no] + // Wait til player hits [End Turn] button + // AI-controlled player turn sequence: + // Determine offense/defense ratio of funds based on AI temperament/aggressiveness setting + // Select expansion site(s??), allocate the offense funds if we decide to go with more than one expansion/round + // Build as much as can afford on owned sites, based on the pre-defined base templates of the Scenes + else if (m_GameState >= PLAYER1TURN && m_GameState <= PLAYER4TURN) { + // State init + if (m_StateChanged) { + m_PhaseTimer.Reset(); + m_StateChanged = false; + } + int metaPlayer = m_GameState - PLAYER1TURN; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearActivities -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out all the lined-up activities for the current round. + // If an AI player, do the AI player's logic now and go to next player immediately afterward + if (!m_Players[metaPlayer].IsHuman()) + AIPlayerTurn(metaPlayer); -void MetaMan::ClearActivities() -{ - for (std::vector::iterator aItr = m_RoundOffensives.begin(); aItr != m_RoundOffensives.end(); ++aItr) - delete (*aItr); - m_RoundOffensives.clear(); - m_CurrentOffensive = 0; -} + // State end - skip A.I. metaplayer turns in the GUI; also skip human player who have been knocked out of the game in previous rounds + if (m_pMetaGUI->ContinuePhase() || !m_Players[metaPlayer].IsHuman() || m_Players[metaPlayer].IsGameOverByRound(m_CurrentRound)) { + // If this player is now done for, mark him as such so he'll be completely skipped in future rounds + if (GetTotalBrainCountOfPlayer(metaPlayer) <= 0 && !m_Players[metaPlayer].IsGameOverByRound(m_CurrentRound)) + m_Players[metaPlayer].SetGameOverRound(m_CurrentRound); + // Find the next player which is not out of the game yet + do { + // Next player, if any left + m_GameState++; + metaPlayer = m_GameState - PLAYER1TURN; + } while (m_GameState <= PLAYER4TURN && metaPlayer < m_Players.size() && m_Players[metaPlayer].IsGameOverByRound(m_CurrentRound)); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AIPlayerTurn -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does all the things an AI player needs to do during his turn. - -void MetaMan::AIPlayerTurn(int metaPlayer) -{ - if (metaPlayer < 0 || metaPlayer >= m_Players.size()) - return; - - MetaPlayer *pThisPlayer = &(m_Players[metaPlayer]); - - // If this player has no brains at all left, then do nothing - if (GetTotalBrainCountOfPlayer(metaPlayer) <= 0) - { - pThisPlayer->SetGameOverRound(m_CurrentRound); - pThisPlayer->SetOffensiveBudget(0); - return; - } - - // Tally up all the scenes according to who owns them - int sceneCount = 0; - int revealedScenes = std::floor(m_RevealedScenes); - std::vector ownedScenes; - std::vector enemyScenes; - std::vector unclaimedScenes; - - float sceneMark = 0; - Scene * pBestAttackCandidateScene = 0; - - for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) - { - float currentMark = 0; - - // We are only concerned with Scenes that are currently revealed and in play - ++sceneCount; - if (sceneCount > revealedScenes) - break; - - // Scene is owned by this guy's team - if ((*sItr)->GetTeamOwnership() == pThisPlayer->GetTeam()) - ownedScenes.push_back(*sItr); - // Enemy-owned scene - else if ((*sItr)->GetTeamOwnership() != Activity::NoTeam) - { - enemyScenes.push_back(*sItr); - // Scenes with heavy investment owned by a team with lots of funds are less likely to attack - currentMark = -((*sItr)->GetTotalInvestment() + GetGoldCountOfTeam((*sItr)->GetTeamOwnership())); - } - // Unoccupied scene - else - { - unclaimedScenes.push_back(*sItr); - // Unclaimed scenes are guaranteed to attack - currentMark = 1000; + // If not, jump to building bases + if (m_GameState > PLAYER4TURN || metaPlayer >= m_Players.size()) + m_GameState = BUILDBASES; + + m_StateChanged = true; + } } - // Set new attack candidate if we have some better options to attack - if (currentMark != 0 && currentMark > sceneMark) - { - sceneMark = currentMark; - pBestAttackCandidateScene = (*sItr); + // BUILD BASES + // Apply all blueprint pieces to all claimed/base sites (or just save them in list and apply at next load of Scene?) + // In sequence, graphically show all defense investments of each player with lines from the floating funds meters to the bases spent on + else if (m_GameState == BUILDBASES) { + // State init + if (m_StateChanged) { + m_PhaseTimer.Reset(); + m_StateChanged = false; + } + + // State body + + // State end + if (m_pMetaGUI->ContinuePhase()) { + m_GameState = RUNACTIVITIES; + m_StateChanged = true; + } } - } - - // Decide how much of current budget to spend on offense vs defense, and also unassigned countering budget for when we are attacked at a base - float counterRatio = 0.15; - float offenseRatio = pThisPlayer->GetAggressiveness() * 0.8; - float attackGoldThreshold = 250 + ownedScenes.size() * 250; - - if (!unclaimedScenes.empty()) - attackGoldThreshold = 250; - - // Special case: no brains left in pool to attack with - if (pThisPlayer->GetBrainPoolCount() <= 0) - { - // Save for someone attacking back - counterRatio = 0.5; - // Nothing to attack with - offenseRatio = 0; - pThisPlayer->SetOffensiveTargetName(""); - } - // Special case: no owned bases - else if (ownedScenes.empty()) - { - // Don't save anything for counter or defenses if we don't have any bases! - counterRatio = 0; - // Also don't hold back anything for defense if we don't have any bases to spend on - offenseRatio = 1.0; - // Always attack now matter how low the funds are, there's no way to get them anyway - attackGoldThreshold = -1; - } - - // What remains is for defense - float defenseRatio = 1.0 - offenseRatio - counterRatio; - - // Set the attack budget, if there's anything to attack, and anything to attack with - if ((!enemyScenes.empty() || !unclaimedScenes.empty()) && offenseRatio > 0 && pThisPlayer->GetFunds() * offenseRatio >= attackGoldThreshold) - { - pThisPlayer->SetOffensiveBudget(pThisPlayer->GetFunds() * offenseRatio); - // Use two methods to select which scene to attack, first one is based on the previously obtained scene mark and the second is mostly random - if (RandomNum() < 0.6F && pBestAttackCandidateScene) - { - pThisPlayer->SetOffensiveTargetName(pBestAttackCandidateScene->GetPresetName()); + + // RUN ACTIVITIES + // Generate and go through list of all Activities caused by player offensive moves during their turns: + // Init the Activity with the corresponding Scene and player setups as dictated by the round's situation + // LOAD the Scene from disk, from the current metagame folder where all terrain damage etc is preserved + // Let the Activity play out til the end + // Show some stats of the outcome of the Activity + // Have the outcome reflected in the Metagame: Funds won/lost, site ownership change, etc + // SAVE the Scene to disk, preserving the Terrain + else if (m_GameState == RUNACTIVITIES) { + // State init + if (m_StateChanged) { + m_PhaseTimer.Reset(); + m_StateChanged = false; + } + + // State body + + // State end, either as signaled by the GUI, or by the fact that we are out of things to run + if (m_pMetaGUI->ContinuePhase() || m_CurrentOffensive >= m_RoundOffensives.size()) { + m_GameState = ENDROUND; + m_StateChanged = true; + } } - else - { - // And the target scene, randomly selected for now from all unfriendly targets - int unfriendlySceneCount = enemyScenes.size() + unclaimedScenes.size(); - int targetIndex = RandomNum(0, unfriendlySceneCount - 1); - // Give it a strong preference for unclaimed scenes! They make more strategic sense than to attack a hardened target - if (!unclaimedScenes.empty() && targetIndex >= unclaimedScenes.size()) - targetIndex = RandomNum() < 0.75F ? RandomNum(0, unclaimedScenes.size() - 1) : targetIndex; - // From index to actual Scene and selection - Scene *selectedTarget = targetIndex < unclaimedScenes.size() ? unclaimedScenes[targetIndex] : enemyScenes[targetIndex - unclaimedScenes.size()]; - if (selectedTarget) - pThisPlayer->SetOffensiveTargetName(selectedTarget->GetPresetName()); + + // END ROUND + // If all sites of this metagame have been revealed, check if any player owns all the of them now - if so, GAME OVER + else if (m_GameState == ENDROUND) { + // State init + if (m_StateChanged) { + m_PhaseTimer.Reset(); + m_StateChanged = false; + + // Check for GAME OVER condition + if (IsGameOver()) { + m_GameState = GAMEOVER; + m_StateChanged = true; + } + } + + // State body + + // Game isn't over yet, so start the next round when user is done looking at stats + if (m_pMetaGUI->ContinuePhase()) { + m_CurrentRound++; + m_GameState = NEWROUND; + m_StateChanged = true; + } } - } - // All on defense instead after all - else - { - pThisPlayer->SetOffensiveBudget(0); - defenseRatio = 1.0; - } - - // Spread out the defensive budgets on the owned sites - float totalDefenseBudget = pThisPlayer->GetFunds() * defenseRatio; - int player = pThisPlayer->GetInGamePlayer(); - for (std::vector::iterator sItr = ownedScenes.begin(); sItr != ownedScenes.end(); ++sItr) - { - // Evenly, for now.. later, might want to prioritize sites with established bases, or opposite? - (*sItr)->SetBuildBudget(player, totalDefenseBudget / ownedScenes.size()); - - // Save the build budget ratio that was selected as well, so it can be re-used next round? - // (AI doesn't really need to use this since they re-do their allocation algo above each round) - if (pThisPlayer->GetFunds() > 0) - (*sItr)->SetBuildBudgetRatio(player, (*sItr)->GetBuildBudget(player) / pThisPlayer->GetFunds()); - else - (*sItr)->SetBuildBudgetRatio(player, 0); - - // Move building pieces from the Scene's AI blueprint queue to the actual blueprints, but only approximately as much as can afford, so the entire AI pre-built base plan isn't revealed - (*sItr)->ApplyAIPlan(player); - } - -// TODO: Pay for and schedule to scan a random unfriendly site to keep things fair - -} + // GAME OVER + // Show some game over graphical thing and perhaps stats + else if (m_GameState == GAMEOVER) { + // State init + if (m_StateChanged) { + m_PhaseTimer.Reset(); + m_StateChanged = false; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this. Supposed to be done every frame before drawing. - -void MetaMan::Update() -{ - m_pMetaGUI->Update(); - - //////////////////////////////////////////// - // METAGAME STATE MACHINE UPDATER - - // Game is temporarily suspended, don't do anything - if (m_Suspended) - { - - } - // Game not started; the GUI will show the new game dialog - else if (m_GameState == NOGAME) - { - // Show suspended so the new game dialog will show up - m_Suspended = true; - - // State end - if (m_pMetaGUI->ContinuePhase()) - { - m_GameState = GAMEINTRO; - m_StateChanged = true; - } - } - - // INTRO - // Show some nice graphical intro of the campaign - else if (m_GameState == GAMEINTRO) - { - // State init - if (m_StateChanged) - { - m_PhaseTimer.Reset(); - m_StateChanged = false; - } - - // State body - - // State end - if (1)//m_pMetaGUI->ContinuePhase()) - { - m_GameState = NEWROUND; - m_StateChanged = true; - } - } - - // NEW ROUND - // Show a nice banner for the new round and its number - else if (m_GameState == NEWROUND) - { - // State init - if (m_StateChanged) - { - // New day; clear out old activities - ClearActivities(); - m_PhaseTimer.Reset(); - m_StateChanged = false; - } - - // State body - - - // State end - if (m_pMetaGUI->ContinuePhase()) - { - m_GameState = REVEALSCENES; - m_StateChanged = true; - } - } - - // REVEAL SCENES - // If not all the sites are revealed yet, reveal one or two (depending on how many players playing?) - else if (m_GameState == REVEALSCENES) - { - // State init - if (m_StateChanged) - { - m_PhaseTimer.Reset(); - m_StateChanged = false; - } - - // State body - - // State end - if (m_pMetaGUI->ContinuePhase()) - { - m_GameState = COUNTINCOME; - m_StateChanged = true; - } - } - - // COUNT INCOME - // Count all the income from all owned bases, add to respective players' funds: - // Show this with lines from the bases adding to the floating player counters, showing income ratios, and leave the lines there to show base ownership and value during turns - else if (m_GameState == COUNTINCOME) - { - // State init - if (m_StateChanged) - { - m_PhaseTimer.Reset(); - m_StateChanged = false; - } - - // State body - - - // State end - if (m_pMetaGUI->ContinuePhase()) - { - m_GameState = PLAYER1TURN; - m_StateChanged = true; - } - } - - // PLAYER TURNS - // Player-controlled player turn sequence: - // Show planet and current known sites, allow inspection of each - // Allow to zoom in on each site owned by this player: - // Build blueprints (allocating defense budget) in Scene Editor Activity until player done - // Allow clicking on sites unvisited by anyone - // Allocate expedition budget to this site with a slider - // Allow clicking on sites already claimed by other player - // Allocate attack budget to this site with a slider - // [Question: allow more than one expedition/offense action per turn?? - A: no] - // Wait til player hits [End Turn] button - // AI-controlled player turn sequence: - // Determine offense/defense ratio of funds based on AI temperament/aggressiveness setting - // Select expansion site(s??), allocate the offense funds if we decide to go with more than one expansion/round - // Build as much as can afford on owned sites, based on the pre-defined base templates of the Scenes - else if (m_GameState >= PLAYER1TURN && m_GameState <= PLAYER4TURN) - { - // State init - if (m_StateChanged) - { - m_PhaseTimer.Reset(); - m_StateChanged = false; - } - - int metaPlayer = m_GameState - PLAYER1TURN; - - // If an AI player, do the AI player's logic now and go to next player immediately afterward - if (!m_Players[metaPlayer].IsHuman()) - AIPlayerTurn(metaPlayer); - - // State end - skip A.I. metaplayer turns in the GUI; also skip human player who have been knocked out of the game in previous rounds - if (m_pMetaGUI->ContinuePhase() || !m_Players[metaPlayer].IsHuman() || m_Players[metaPlayer].IsGameOverByRound(m_CurrentRound)) - { - // If this player is now done for, mark him as such so he'll be completely skipped in future rounds - if (GetTotalBrainCountOfPlayer(metaPlayer) <= 0 && !m_Players[metaPlayer].IsGameOverByRound(m_CurrentRound)) - m_Players[metaPlayer].SetGameOverRound(m_CurrentRound); - - // Find the next player which is not out of the game yet - do - { - // Next player, if any left - m_GameState++; - metaPlayer = m_GameState - PLAYER1TURN; - } - while (m_GameState <= PLAYER4TURN && metaPlayer < m_Players.size() && m_Players[metaPlayer].IsGameOverByRound(m_CurrentRound)); - - // If not, jump to building bases - if (m_GameState > PLAYER4TURN || metaPlayer >= m_Players.size()) - m_GameState = BUILDBASES; - - m_StateChanged = true; - } - } - - // BUILD BASES - // Apply all blueprint pieces to all claimed/base sites (or just save them in list and apply at next load of Scene?) - // In sequence, graphically show all defense investments of each player with lines from the floating funds meters to the bases spent on - else if (m_GameState == BUILDBASES) - { - // State init - if (m_StateChanged) - { - m_PhaseTimer.Reset(); - m_StateChanged = false; - } - - // State body - - // State end - if (m_pMetaGUI->ContinuePhase()) - { - m_GameState = RUNACTIVITIES; - m_StateChanged = true; - } - } - - // RUN ACTIVITIES - // Generate and go through list of all Activities caused by player offensive moves during their turns: - // Init the Activity with the corresponding Scene and player setups as dictated by the round's situation - // LOAD the Scene from disk, from the current metagame folder where all terrain damage etc is preserved - // Let the Activity play out til the end - // Show some stats of the outcome of the Activity - // Have the outcome reflected in the Metagame: Funds won/lost, site ownership change, etc - // SAVE the Scene to disk, preserving the Terrain - else if (m_GameState == RUNACTIVITIES) - { - // State init - if (m_StateChanged) - { - m_PhaseTimer.Reset(); - m_StateChanged = false; - } - - // State body - - // State end, either as signaled by the GUI, or by the fact that we are out of things to run - if (m_pMetaGUI->ContinuePhase() || m_CurrentOffensive >= m_RoundOffensives.size()) - { - m_GameState = ENDROUND; - m_StateChanged = true; - } - } - - // END ROUND - // If all sites of this metagame have been revealed, check if any player owns all the of them now - if so, GAME OVER - else if (m_GameState == ENDROUND) - { - // State init - if (m_StateChanged) - { - m_PhaseTimer.Reset(); - m_StateChanged = false; - - // Check for GAME OVER condition - if (IsGameOver()) - { - m_GameState = GAMEOVER; - m_StateChanged = true; - } - } - - // State body - - // Game isn't over yet, so start the next round when user is done looking at stats - if (m_pMetaGUI->ContinuePhase()) - { - m_CurrentRound++; - m_GameState = NEWROUND; - m_StateChanged = true; - } - } - - // GAME OVER - // Show some game over graphical thing and perhaps stats - else if (m_GameState == GAMEOVER) - { - // State init - if (m_StateChanged) - { - m_PhaseTimer.Reset(); - m_StateChanged = false; - } - - // State body -/* finito! - // State end - if (m_pMetaGUI->ContinuePhase()) - { - // Next player - m_GameState = GAMEOVER; - m_StateChanged = true; - } -*/ - } - - // Whoops, state is f'd up, restart the last round - else - { - RTEAbort("Metagame State is out of bounds!?"); - m_GameState = REVEALSCENES; - } -} + // State body + /* finito! + // State end + if (m_pMetaGUI->ContinuePhase()) + { + // Next player + m_GameState = GAMEOVER; + m_StateChanged = true; + } + */ + } + // Whoops, state is f'd up, restart the last round + else { + RTEAbort("Metagame State is out of bounds!?"); + m_GameState = REVEALSCENES; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MetaMan's current graphical representation to a -// BITMAP of choice. This includes all game-related graphics. - -void MetaMan::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) -{ -/* - GUIFont *pLargeFont = g_FrameMan.GetLargeFont(); - GUIFont *pSmallFont = g_FrameMan.GetSmallFont(); - AllegroBitmap pBitmapInt(pTargetBitmap); - - // Iterate through all players - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - ; - } -*/ - - m_pMetaGUI->Draw(pTargetBitmap); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this MetaMan's current graphical representation to a + // BITMAP of choice. This includes all game-related graphics. + + void MetaMan::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + /* + GUIFont *pLargeFont = g_FrameMan.GetLargeFont(); + GUIFont *pSmallFont = g_FrameMan.GetSmallFont(); + AllegroBitmap pBitmapInt(pTargetBitmap); + + // Iterate through all players + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) + { + ; + } + */ + + m_pMetaGUI->Draw(pTargetBitmap); + } } // namespace RTE \ No newline at end of file diff --git a/Source/Managers/MetaMan.h b/Source/Managers/MetaMan.h index e974d2a2ec..cca5d38b85 100644 --- a/Source/Managers/MetaMan.h +++ b/Source/Managers/MetaMan.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -18,634 +17,578 @@ #include "Singleton.h" #define g_MetaMan MetaMan::Instance() -//#include "Serializable.h" +// #include "Serializable.h" #include "SceneObject.h" #include "Controller.h" -//#include "FrameMan.h" -//#include "SceneMan.h" +// #include "FrameMan.h" +// #include "SceneMan.h" #include "ActivityMan.h" #include "MetaPlayer.h" #include "GAScripted.h" #include "Icon.h" #include "GUIBanner.h" -namespace RTE -{ +namespace RTE { #define DEFAULTGAMENAME "NewGame" #define AUTOSAVENAME "AutoSave" #define METASAVEPATH System::GetUserdataDirectory() + c_UserConquestSavesModuleName + "/" #define METASAVEMODULENAME c_UserConquestSavesModuleName -class MetagameGUI; -class MetaSave; -class Scene; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: MetaMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The singleton manager of the Metagame of Cortex Command, ie the -// games played out in the campaign screen. -// Parent(s): Singleton, serializable -// Class history: 10/10/2009 MetaMan created. - -class MetaMan : public Singleton, public Serializable { - friend struct ManagerLuaBindings; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - -friend class MetagameGUI; -friend class MetaSave; - - SerializableClassNameGetter; - SerializableOverrideMethods; - - enum MetagameState - { - NOGAME = -1, - GAMEINTRO = 0, - NEWROUND, - REVEALSCENES, - COUNTINCOME, - PLAYER1TURN, - PLAYER2TURN, - PLAYER3TURN, - PLAYER4TURN, - BUILDBASES, - RUNACTIVITIES, - ENDROUND, - GAMEOVER - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: MetaMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a MetaMan object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - MetaMan() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~MetaMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a MetaMan object before deletion -// from system memory. -// Arguments: None. - - ~MetaMan() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MetaMan object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Initialize(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: NewGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wipes any current and sets up a new game based on a size parameter. -// Arguments: The size of the new Metagame, which will affect how -// many Scenes/Sites will ultimately be used. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int NewGame(int gameSize = 3); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EndGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wipes any current metagame and sets things back to as if program start. -// Arguments: None. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int EndGame(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Load -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Load a Metagame from disk out of the special Metagame.rte data module -// Arguments: The MetaSave object to load from - Ownership Is Not Transferred! -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int Load(const MetaSave *pSave); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveSceneData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the bitmap data of all Scenes of this Metagame that are currently -// loaded. -// Arguments: The filepath base to the where to save the Bitmap data. This means -// everything up to and including the unique name of the game. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int SaveSceneData(std::string pathBase); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadSceneData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads the bitmap data of all Scenes of this Metagame that have once -// been saved to files. -// Arguments: None. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int LoadSceneData(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearSceneData -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the bitmap data of all Scenes of this Metagame that have once -// been saved to files. -// Arguments: None. -// Return value: An error return value signaling success or any particular failure. -// Anything below 0 is an error signal. - - int ClearSceneData(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire MetaMan, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MetaMan object. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetGameName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the name of the currently played Metagame. It's what's used when -// saving to disk. -// Arguments: The Metagame's name. -// Return value: None. - - void SetGameName(std::string newName) { m_GameName = newName; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGameName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the name of the currently played Metagame. It's what's used when -// saving to disk. -// Arguments: None. -// Return value: The name of the current metagame. - - std::string GetGameName() const { return m_GameName; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the GUI controller of this Metagame. -// Arguments: None. -// Return value: The GUI controller of the metagame. - - MetagameGUI *GetGUI() { return m_pMetaGUI; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlayerTurn -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows which player's turn is now or coming up. -// Arguments: None. -// Return value: The player who is currently doing his turn, or coming up next in an -// intermediate phase. - - int GetPlayerTurn() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlayerCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets current number of MetaPlayers -// Arguments: None -// Return value: The number of meta players in the current game. - - int GetPlayerCount() const { return m_Players.size(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTeamOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the designated team of a specific player -// Arguments: Which player. -// Return value: The team of that player. - - int GetTeamOfPlayer(int metaPlayer) const { return metaPlayer >= Players::PlayerOne && metaPlayer < m_Players.size() ? m_Players[metaPlayer].GetTeam() : Activity::NoTeam; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the specified MetaPlayer -// Arguments: Which player. -// Return value: The requested MetaPlayer - - MetaPlayer * GetPlayer(int metaPlayer) { return (metaPlayer >= Players::PlayerOne && metaPlayer < m_Players.size()) ? &(m_Players[metaPlayer]) : 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMetaPlayerOfInGamePlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the MetaPlayer playing a specific in-game player, if any. -// Arguments: Which in-game player to translate into a metaplayer. -// Return value: The requested MetaPlayer, if any is playing that in-game player. If not -// 0 is returned. - - MetaPlayer * GetMetaPlayerOfInGamePlayer(int inGamePlayer); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTeamIcon -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the flag Icon of a specific team -// Arguments: The team to get the Team icon of. -// Return value: A reference to the Icon. - - Icon & GetTeamIcon(int team) { return m_TeamIcons[team]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextSceneOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next Scene in play that is owned by a specific player. -// Arguments: The player to get the next owned Scene of. -// The Scene to start searching from in the current roster of Scenes, OWNERSHIP IS NOT TRANSFERRED! -// Return value: A pointer to the next Scene found in the sequence. OWNERSHIP IS NOT TRANSFERRED! - - const Scene * GetNextSceneOfPlayer(int metaPlayer, const Scene *pScene = 0) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalBrainCountOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total number of brains that a player has, including ones of -// his that are resident down on sites. -// Arguments: The metagame player to get the total brain count from. -// Whether to only count the brains in the pools, or to also include all -// resident brains as well. -// Return value: The total number of brains that belong to the metagame player. - - int GetTotalBrainCountOfPlayer(int metaPlayer, bool countPoolsOnly = false) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGoldCountOfTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total gold funds of all the players of a specific team combined. -// Arguments: The metagame team to get the total gold funds of. -// Return value: The total amount of ounces of gold this team has. - - int GetGoldCountOfTeam(int team) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSceneCountOfTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total number of bases that any specific team owns. -// Arguments: The team to get the scene/site ownership count of. -// Return value: The count of scenes owned by this team. - - int GetSceneCountOfTeam(int team) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalBrainCountOfTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total number of brains that a team has, including ones that -// are resident down on sites. -// Arguments: The metagame team to get the total brain count from. -// Whether to only count the brains in the pools, or to also include all -// resident brains as well. -// Return value: The total number of brains that belong to the metagame team. - - int GetTotalBrainCountOfTeam(int team, bool countPoolsOnly = false) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OnlyTeamWithAnyBrainPoolLeft -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates which team, if any, is the only one left with brains in its -// pool. -// Arguments: None. -// Return value: Which team, if any, is the sole remaining with any brains left in its -// players' brain pools. - - int OnlyTeamWithAnyBrainPoolLeft(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: NoBrainsLeftInAnyPool -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether there are no brains left in any active player's pool -// at all. This does NOT count deployed brain in bases. -// Arguments: None. -// Return value: Whether there are no brains left in any player's brain pool. - - bool NoBrainsLeftInAnyPool(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WhichTeamIsLeading -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates which single team has the most owned bases, and if there's a -// tie between two teams, total owned gold funds is used as a tiebreaker. -// Arguments: None. -// Return value: Which team is currently in the lead. - - int WhichTeamIsLeading(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSceneIncomeOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total income from all scenes owned by a specific metaPlayer. -// Arguments: The metagame player to get the total scene income from. -// Return value: The amount of income, in oz, the player made this round from its scenes. - - float GetSceneIncomeOfPlayer(int metaPlayer) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBudgetedRatioOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the ratio of funds already allocated to budgets of this player. -// Arguments: The metagame player to get the budget ratio of. -// A scene to exclude from the tally, if any. -// Whether to count the money allocated for offensive action. -// Whether to count the money allocated for defensive actions. -// Return value: The amount, in ratio, that this player already has allocated. - - float GetBudgetedRatioOfPlayer(int metaPlayer, const Scene *pException = 0, bool includeOffensive = true, bool includeDefensive = true) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRemainingFundsOfPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the count of funds still unbudgeted and available of a player. -// Arguments: The metagame player to get the unallocated funds of. -// A scene to exclude from the tally, if any. -// Whether to count the money allocated for offensive action as remaining. -// Whether to count the money allocated for defensive action as remaining. -// Return value: The amount, in oz, that this player unallocated and unused this turn. - - float GetRemainingFundsOfPlayer(int metaPlayer, const Scene *pException = 0, bool deductOffensive = false, bool deductDefensive = false) const { return m_Players[metaPlayer].GetFunds() - m_Players[metaPlayer].GetFunds() * GetBudgetedRatioOfPlayer(metaPlayer, pException, !deductOffensive, !deductDefensive); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GameInProgress -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether a game is currently in progress -// Arguments: None. -// Return value: Whether a game is going or not. - - bool GameInProgress() { return m_GameState >= GAMEINTRO && m_GameState <= ENDROUND; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsSuspended -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether game is suspended or not. -// Arguments: None. -// Return value: Whether suspended or not. - - bool IsSuspended() { return m_Suspended; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSuspend -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Suspends or unsuspends the game so exclusive GUIs and menus can be -// shown. -// Arguments: Whether to suspend or not. -// Return value: None. - - void SetSuspend(bool suspend); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsActivePlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks wheter a certain player index is valid for the current game -// Arguments: None. -// Return value: Whether the player index passed in is active for the current game. - - bool IsActivePlayer(int metaPlayer) { return metaPlayer >= Players::PlayerOne && metaPlayer < m_Players.size(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsActiveTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks wheter a certain team index is valid for the current game -// Arguments: None. -// Return value: Whether the team index passed in is active for the current game. - - bool IsActiveTeam(int team) { return team >= Activity::TeamOne && team < m_TeamCount; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WhichTeamOwnsAllSites -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether one team has ownership of all revealed sites. -// Arguments: None. -// Return value: Which team has all sites, if any. If not NoTeam is returned. - - int WhichTeamOwnsAllSites(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsGameOver -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks for game over condition -// Arguments: None. -// Return value: Whether the game over conditions have been met - - bool IsGameOver(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GameIsSaved -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether the game has been saved and no data loss will result -// if program is quit right now. -// Arguments: None. -// Return value: Whether the game is saved. - - bool GameIsSaved() { return m_GameSaved || m_GameState <= NOGAME || m_GameState >= GAMEOVER; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TotalScenePresets -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Yields a set of ALL eligible Scene presets for a new game. -// Arguments: The list to fill with all the eligible presets. If no list is passed -// it will be ignored. Presets returned in list are NOT OWNED there. -// Return value: The count of total number preset scenes eligible for gameplay. - - int TotalScenePresets(std::list *pScenes = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SelectScenePresets -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Yields a set of randomly selected Scene presets for a new game. -// Arguments: The size of the set. -// The list to fill with the selected presets, depending on currently -// set player numbers and loaded eligible scenes. If no list is passed -// it will be ignored. Presets returned in list are NOT OWNED there. -// Return value: The count of selected preset scenes. - - int SelectScenePresets(int gameSize, std::list *pSelected = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearActivities -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out all the lined-up activities for the current round. -// Arguments: None. -// Return value: None. - - void ClearActivities(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AIPlayerTurn -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does all the things an AI metaPlayer needs to do during his turn. -// Arguments: Which AI metaPlayer we're going to process. -// Return value: None. - - void AIPlayerTurn(int metaPlayer); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this and the current Metagame. Supposed to be -// done every frame before drawing. -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MetaMan's current graphical representation to a BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // GUI controller, owned - MetagameGUI *m_pMetaGUI; - - // Current Metagame state - int m_GameState; - // Whether the state just changed - bool m_StateChanged; - // Whether the game is currently suspended (e.g. in the menu) - bool m_Suspended; - // Whether game has been saved since the last suspension of it - bool m_GameSaved; - - // The save name of the currently played metagame - std::string m_GameName; - // The players of the metagame - std::vector m_Players; - // The number of Team:s in play this game - int m_TeamCount; - // The flag icons of all teams - Icon m_TeamIcons[Activity::MaxTeamCount]; - // The current round the game is on, starting with count on 0 - int m_CurrentRound; - // All Scenes of the current game, OWNED by this. Stored sequentially in order of revealing - std::vector m_Scenes; - // How many of the scenes have been revealed so far - the whole number. It's a float to increase it slower than once a round - float m_RevealedScenes; - // How many scenes to reveal each round.. can be a fractional that adds up over several days - float m_RevealRate; - // Any extra reveals to make next reveal phase - float m_RevealExtra; - - // The Activities generated by the current round's offensive maneuvers - std::vector m_RoundOffensives; - // The current offensive action that we're about to play next - int m_CurrentOffensive; - // Game difficulty - int m_Difficulty; - // Teams AI Skill - int m_TeamAISkill[Activity::MaxTeamCount]; - - // Timer for measuring how long each phase has gone for - Timer m_PhaseTimer; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MetaMan, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - MetaMan(const MetaMan &reference) = delete; - MetaMan & operator=(const MetaMan &rhs) = delete; - -}; + class MetagameGUI; + class MetaSave; + class Scene; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: MetaMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The singleton manager of the Metagame of Cortex Command, ie the + // games played out in the campaign screen. + // Parent(s): Singleton, serializable + // Class history: 10/10/2009 MetaMan created. + + class MetaMan : public Singleton, public Serializable { + friend struct ManagerLuaBindings; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + friend class MetagameGUI; + friend class MetaSave; + + SerializableClassNameGetter; + SerializableOverrideMethods; + + enum MetagameState { + NOGAME = -1, + GAMEINTRO = 0, + NEWROUND, + REVEALSCENES, + COUNTINCOME, + PLAYER1TURN, + PLAYER2TURN, + PLAYER3TURN, + PLAYER4TURN, + BUILDBASES, + RUNACTIVITIES, + ENDROUND, + GAMEOVER + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: MetaMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a MetaMan object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + MetaMan() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~MetaMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a MetaMan object before deletion + // from system memory. + // Arguments: None. + + ~MetaMan() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MetaMan object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Initialize(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: NewGame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Wipes any current and sets up a new game based on a size parameter. + // Arguments: The size of the new Metagame, which will affect how + // many Scenes/Sites will ultimately be used. + // Return value: An error return value signaling success or any particular failure. + // Anything below 0 is an error signal. + + int NewGame(int gameSize = 3); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EndGame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Wipes any current metagame and sets things back to as if program start. + // Arguments: None. + // Return value: An error return value signaling success or any particular failure. + // Anything below 0 is an error signal. + + int EndGame(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Load + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Load a Metagame from disk out of the special Metagame.rte data module + // Arguments: The MetaSave object to load from - Ownership Is Not Transferred! + // Return value: An error return value signaling success or any particular failure. + // Anything below 0 is an error signal. + + int Load(const MetaSave* pSave); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SaveSceneData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the bitmap data of all Scenes of this Metagame that are currently + // loaded. + // Arguments: The filepath base to the where to save the Bitmap data. This means + // everything up to and including the unique name of the game. + // Return value: An error return value signaling success or any particular failure. + // Anything below 0 is an error signal. + + int SaveSceneData(std::string pathBase); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadSceneData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads the bitmap data of all Scenes of this Metagame that have once + // been saved to files. + // Arguments: None. + // Return value: An error return value signaling success or any particular failure. + // Anything below 0 is an error signal. + + int LoadSceneData(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearSceneData + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the bitmap data of all Scenes of this Metagame that have once + // been saved to files. + // Arguments: None. + // Return value: An error return value signaling success or any particular failure. + // Anything below 0 is an error signal. + + int ClearSceneData(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire MetaMan, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the MetaMan object. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetGameName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the name of the currently played Metagame. It's what's used when + // saving to disk. + // Arguments: The Metagame's name. + // Return value: None. + + void SetGameName(std::string newName) { m_GameName = newName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGameName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the name of the currently played Metagame. It's what's used when + // saving to disk. + // Arguments: None. + // Return value: The name of the current metagame. + + std::string GetGameName() const { return m_GameName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the GUI controller of this Metagame. + // Arguments: None. + // Return value: The GUI controller of the metagame. + + MetagameGUI* GetGUI() { return m_pMetaGUI; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlayerTurn + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows which player's turn is now or coming up. + // Arguments: None. + // Return value: The player who is currently doing his turn, or coming up next in an + // intermediate phase. + + int GetPlayerTurn() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlayerCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets current number of MetaPlayers + // Arguments: None + // Return value: The number of meta players in the current game. + + int GetPlayerCount() const { return m_Players.size(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTeamOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the designated team of a specific player + // Arguments: Which player. + // Return value: The team of that player. + + int GetTeamOfPlayer(int metaPlayer) const { return metaPlayer >= Players::PlayerOne && metaPlayer < m_Players.size() ? m_Players[metaPlayer].GetTeam() : Activity::NoTeam; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the specified MetaPlayer + // Arguments: Which player. + // Return value: The requested MetaPlayer + + MetaPlayer* GetPlayer(int metaPlayer) { return (metaPlayer >= Players::PlayerOne && metaPlayer < m_Players.size()) ? &(m_Players[metaPlayer]) : 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMetaPlayerOfInGamePlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the MetaPlayer playing a specific in-game player, if any. + // Arguments: Which in-game player to translate into a metaplayer. + // Return value: The requested MetaPlayer, if any is playing that in-game player. If not + // 0 is returned. + + MetaPlayer* GetMetaPlayerOfInGamePlayer(int inGamePlayer); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTeamIcon + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the flag Icon of a specific team + // Arguments: The team to get the Team icon of. + // Return value: A reference to the Icon. + + Icon& GetTeamIcon(int team) { return m_TeamIcons[team]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNextSceneOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the next Scene in play that is owned by a specific player. + // Arguments: The player to get the next owned Scene of. + // The Scene to start searching from in the current roster of Scenes, OWNERSHIP IS NOT TRANSFERRED! + // Return value: A pointer to the next Scene found in the sequence. OWNERSHIP IS NOT TRANSFERRED! + + const Scene* GetNextSceneOfPlayer(int metaPlayer, const Scene* pScene = 0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalBrainCountOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total number of brains that a player has, including ones of + // his that are resident down on sites. + // Arguments: The metagame player to get the total brain count from. + // Whether to only count the brains in the pools, or to also include all + // resident brains as well. + // Return value: The total number of brains that belong to the metagame player. + + int GetTotalBrainCountOfPlayer(int metaPlayer, bool countPoolsOnly = false) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGoldCountOfTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total gold funds of all the players of a specific team combined. + // Arguments: The metagame team to get the total gold funds of. + // Return value: The total amount of ounces of gold this team has. + + int GetGoldCountOfTeam(int team) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSceneCountOfTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total number of bases that any specific team owns. + // Arguments: The team to get the scene/site ownership count of. + // Return value: The count of scenes owned by this team. + + int GetSceneCountOfTeam(int team) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalBrainCountOfTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total number of brains that a team has, including ones that + // are resident down on sites. + // Arguments: The metagame team to get the total brain count from. + // Whether to only count the brains in the pools, or to also include all + // resident brains as well. + // Return value: The total number of brains that belong to the metagame team. + + int GetTotalBrainCountOfTeam(int team, bool countPoolsOnly = false) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OnlyTeamWithAnyBrainPoolLeft + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates which team, if any, is the only one left with brains in its + // pool. + // Arguments: None. + // Return value: Which team, if any, is the sole remaining with any brains left in its + // players' brain pools. + + int OnlyTeamWithAnyBrainPoolLeft(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: NoBrainsLeftInAnyPool + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether there are no brains left in any active player's pool + // at all. This does NOT count deployed brain in bases. + // Arguments: None. + // Return value: Whether there are no brains left in any player's brain pool. + + bool NoBrainsLeftInAnyPool(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WhichTeamIsLeading + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates which single team has the most owned bases, and if there's a + // tie between two teams, total owned gold funds is used as a tiebreaker. + // Arguments: None. + // Return value: Which team is currently in the lead. + + int WhichTeamIsLeading(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSceneIncomeOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total income from all scenes owned by a specific metaPlayer. + // Arguments: The metagame player to get the total scene income from. + // Return value: The amount of income, in oz, the player made this round from its scenes. + + float GetSceneIncomeOfPlayer(int metaPlayer) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetBudgetedRatioOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the ratio of funds already allocated to budgets of this player. + // Arguments: The metagame player to get the budget ratio of. + // A scene to exclude from the tally, if any. + // Whether to count the money allocated for offensive action. + // Whether to count the money allocated for defensive actions. + // Return value: The amount, in ratio, that this player already has allocated. + + float GetBudgetedRatioOfPlayer(int metaPlayer, const Scene* pException = 0, bool includeOffensive = true, bool includeDefensive = true) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRemainingFundsOfPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the count of funds still unbudgeted and available of a player. + // Arguments: The metagame player to get the unallocated funds of. + // A scene to exclude from the tally, if any. + // Whether to count the money allocated for offensive action as remaining. + // Whether to count the money allocated for defensive action as remaining. + // Return value: The amount, in oz, that this player unallocated and unused this turn. + + float GetRemainingFundsOfPlayer(int metaPlayer, const Scene* pException = 0, bool deductOffensive = false, bool deductDefensive = false) const { return m_Players[metaPlayer].GetFunds() - m_Players[metaPlayer].GetFunds() * GetBudgetedRatioOfPlayer(metaPlayer, pException, !deductOffensive, !deductDefensive); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GameInProgress + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether a game is currently in progress + // Arguments: None. + // Return value: Whether a game is going or not. + + bool GameInProgress() { return m_GameState >= GAMEINTRO && m_GameState <= ENDROUND; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsSuspended + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether game is suspended or not. + // Arguments: None. + // Return value: Whether suspended or not. + + bool IsSuspended() { return m_Suspended; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSuspend + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Suspends or unsuspends the game so exclusive GUIs and menus can be + // shown. + // Arguments: Whether to suspend or not. + // Return value: None. + + void SetSuspend(bool suspend); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsActivePlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks wheter a certain player index is valid for the current game + // Arguments: None. + // Return value: Whether the player index passed in is active for the current game. + + bool IsActivePlayer(int metaPlayer) { return metaPlayer >= Players::PlayerOne && metaPlayer < m_Players.size(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsActiveTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks wheter a certain team index is valid for the current game + // Arguments: None. + // Return value: Whether the team index passed in is active for the current game. + + bool IsActiveTeam(int team) { return team >= Activity::TeamOne && team < m_TeamCount; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WhichTeamOwnsAllSites + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks whether one team has ownership of all revealed sites. + // Arguments: None. + // Return value: Which team has all sites, if any. If not NoTeam is returned. + + int WhichTeamOwnsAllSites(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsGameOver + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks for game over condition + // Arguments: None. + // Return value: Whether the game over conditions have been met + + bool IsGameOver(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GameIsSaved + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether the game has been saved and no data loss will result + // if program is quit right now. + // Arguments: None. + // Return value: Whether the game is saved. + + bool GameIsSaved() { return m_GameSaved || m_GameState <= NOGAME || m_GameState >= GAMEOVER; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TotalScenePresets + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Yields a set of ALL eligible Scene presets for a new game. + // Arguments: The list to fill with all the eligible presets. If no list is passed + // it will be ignored. Presets returned in list are NOT OWNED there. + // Return value: The count of total number preset scenes eligible for gameplay. + + int TotalScenePresets(std::list* pScenes = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SelectScenePresets + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Yields a set of randomly selected Scene presets for a new game. + // Arguments: The size of the set. + // The list to fill with the selected presets, depending on currently + // set player numbers and loaded eligible scenes. If no list is passed + // it will be ignored. Presets returned in list are NOT OWNED there. + // Return value: The count of selected preset scenes. + + int SelectScenePresets(int gameSize, std::list* pSelected = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearActivities + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears out all the lined-up activities for the current round. + // Arguments: None. + // Return value: None. + + void ClearActivities(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AIPlayerTurn + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Does all the things an AI metaPlayer needs to do during his turn. + // Arguments: Which AI metaPlayer we're going to process. + // Return value: None. + + void AIPlayerTurn(int metaPlayer); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this and the current Metagame. Supposed to be + // done every frame before drawing. + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this MetaMan's current graphical representation to a BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // GUI controller, owned + MetagameGUI* m_pMetaGUI; + + // Current Metagame state + int m_GameState; + // Whether the state just changed + bool m_StateChanged; + // Whether the game is currently suspended (e.g. in the menu) + bool m_Suspended; + // Whether game has been saved since the last suspension of it + bool m_GameSaved; + + // The save name of the currently played metagame + std::string m_GameName; + // The players of the metagame + std::vector m_Players; + // The number of Team:s in play this game + int m_TeamCount; + // The flag icons of all teams + Icon m_TeamIcons[Activity::MaxTeamCount]; + // The current round the game is on, starting with count on 0 + int m_CurrentRound; + // All Scenes of the current game, OWNED by this. Stored sequentially in order of revealing + std::vector m_Scenes; + // How many of the scenes have been revealed so far - the whole number. It's a float to increase it slower than once a round + float m_RevealedScenes; + // How many scenes to reveal each round.. can be a fractional that adds up over several days + float m_RevealRate; + // Any extra reveals to make next reveal phase + float m_RevealExtra; + + // The Activities generated by the current round's offensive maneuvers + std::vector m_RoundOffensives; + // The current offensive action that we're about to play next + int m_CurrentOffensive; + // Game difficulty + int m_Difficulty; + // Teams AI Skill + int m_TeamAISkill[Activity::MaxTeamCount]; + + // Timer for measuring how long each phase has gone for + Timer m_PhaseTimer; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MetaMan, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + MetaMan(const MetaMan& reference) = delete; + MetaMan& operator=(const MetaMan& rhs) = delete; + }; } // namespace RTE #endif // File \ No newline at end of file diff --git a/Source/Managers/MovableMan.cpp b/Source/Managers/MovableMan.cpp index ec29af0c4e..6172abddb2 100644 --- a/Source/Managers/MovableMan.cpp +++ b/Source/Managers/MovableMan.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -41,2147 +40,2002 @@ namespace RTE { -AlarmEvent::AlarmEvent(const Vector &pos, int team, float range) -{ - m_ScenePos = pos; - m_Team = (Activity::Teams)team; - m_Range = range * g_FrameMan.GetPlayerScreenWidth() * 0.51F; -} + AlarmEvent::AlarmEvent(const Vector& pos, int team, float range) { + m_ScenePos = pos; + m_Team = (Activity::Teams)team; + m_Range = range * g_FrameMan.GetPlayerScreenWidth() * 0.51F; + } -const std::string MovableMan::c_ClassName = "MovableMan"; + const std::string MovableMan::c_ClassName = "MovableMan"; + + // Comparison functor for sorting movable objects by their X position using STL's sort + struct MOXPosComparison { + bool operator()(MovableObject* pRhs, MovableObject* pLhs) { return pRhs->GetPos().m_X < pLhs->GetPos().m_X; } + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MovableMan, effectively + // resetting the members of this abstraction level only. + + void MovableMan::Clear() { + m_Actors.clear(); + m_ContiguousActorIDs.clear(); + m_Items.clear(); + m_Particles.clear(); + m_AddedActors.clear(); + m_AddedItems.clear(); + m_AddedParticles.clear(); + m_ValidActors.clear(); + m_ValidItems.clear(); + m_ValidParticles.clear(); + m_ActorRoster[Activity::TeamOne].clear(); + m_ActorRoster[Activity::TeamTwo].clear(); + m_ActorRoster[Activity::TeamThree].clear(); + m_ActorRoster[Activity::TeamFour].clear(); + m_SortTeamRoster[Activity::TeamOne] = false; + m_SortTeamRoster[Activity::TeamTwo] = false; + m_SortTeamRoster[Activity::TeamThree] = false; + m_SortTeamRoster[Activity::TeamFour] = false; + m_AddedAlarmEvents.clear(); + m_AlarmEvents.clear(); + m_MOIDIndex.clear(); + m_SplashRatio = 0.75; + m_MaxDroppedItems = 100; + m_SettlingEnabled = true; + m_MOSubtractionEnabled = true; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MovableMan object ready for use. -// Comparison functor for sorting movable objects by their X position using STL's sort -struct MOXPosComparison { - bool operator()(MovableObject *pRhs, MovableObject *pLhs) { return pRhs->GetPos().m_X < pLhs->GetPos().m_X; } -}; + int MovableMan::Initialize() { + // TODO: Increase this number, or maybe only for certain classes? + Entity::ClassInfo::FillAllPools(); + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MovableMan, effectively -// resetting the members of this abstraction level only. + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: ReadProperty + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a property value from a reader stream. If the name isn't + // recognized by this class, then ReadProperty of the parent class + // is called. If the property isn't recognized by any of the base classes, + // false is returned, and the reader's position is untouched. -void MovableMan::Clear() -{ - m_Actors.clear(); - m_ContiguousActorIDs.clear(); - m_Items.clear(); - m_Particles.clear(); - m_AddedActors.clear(); - m_AddedItems.clear(); - m_AddedParticles.clear(); - m_ValidActors.clear(); - m_ValidItems.clear(); - m_ValidParticles.clear(); - m_ActorRoster[Activity::TeamOne].clear(); - m_ActorRoster[Activity::TeamTwo].clear(); - m_ActorRoster[Activity::TeamThree].clear(); - m_ActorRoster[Activity::TeamFour].clear(); - m_SortTeamRoster[Activity::TeamOne] = false; - m_SortTeamRoster[Activity::TeamTwo] = false; - m_SortTeamRoster[Activity::TeamThree] = false; - m_SortTeamRoster[Activity::TeamFour] = false; - m_AddedAlarmEvents.clear(); - m_AlarmEvents.clear(); - m_MOIDIndex.clear(); - m_SplashRatio = 0.75; - m_MaxDroppedItems = 100; - m_SettlingEnabled = true; - m_MOSubtractionEnabled = true; -} + int MovableMan::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Serializable::ReadProperty(propName, reader)); + MatchProperty("AddEffect", { g_PresetMan.GetEntityPreset(reader); }); + MatchProperty("AddAmmo", { g_PresetMan.GetEntityPreset(reader); }); + MatchProperty("AddDevice", { g_PresetMan.GetEntityPreset(reader); }); + MatchProperty("AddActor", { g_PresetMan.GetEntityPreset(reader); }); + MatchProperty("SplashRatio", { reader >> m_SplashRatio; }); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MovableMan object ready for use. + EndPropertyList; + } -int MovableMan::Initialize() -{ - // TODO: Increase this number, or maybe only for certain classes? - Entity::ClassInfo::FillAllPools(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this MovableMan with a Writer for + // later recreation with Create(Reader &reader); - return 0; -} + int MovableMan::Save(Writer& writer) const { + Serializable::Save(writer); + writer << m_Actors.size(); + for (std::deque::const_iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) + writer << **itr; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. + writer << m_Particles.size(); + for (std::deque::const_iterator itr2 = m_Particles.begin(); itr2 != m_Particles.end(); ++itr2) + writer << **itr2; -int MovableMan::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Serializable::ReadProperty(propName, reader)); - - MatchProperty("AddEffect", { g_PresetMan.GetEntityPreset(reader); }); - MatchProperty("AddAmmo", { g_PresetMan.GetEntityPreset(reader); }); - MatchProperty("AddDevice", { g_PresetMan.GetEntityPreset(reader); }); - MatchProperty("AddActor", { g_PresetMan.GetEntityPreset(reader); }); - MatchProperty("SplashRatio", { reader >> m_SplashRatio; }); - - EndPropertyList; -} + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the MovableMan object. -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this MovableMan with a Writer for -// later recreation with Create(Reader &reader); + void MovableMan::Destroy() { + for (std::deque::iterator it1 = m_Actors.begin(); it1 != m_Actors.end(); ++it1) + delete (*it1); + for (std::deque::iterator it2 = m_Items.begin(); it2 != m_Items.end(); ++it2) + delete (*it2); + for (std::deque::iterator it3 = m_Particles.begin(); it3 != m_Particles.end(); ++it3) + delete (*it3); -int MovableMan::Save(Writer &writer) const -{ - Serializable::Save(writer); + Clear(); + } - writer << m_Actors.size(); - for (std::deque::const_iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) - writer << **itr; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMOFromID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a MO from its MOID. Note that MOID's are only valid during the + // same frame as they were assigned to the MOs! - writer << m_Particles.size(); - for (std::deque::const_iterator itr2 = m_Particles.begin(); itr2 != m_Particles.end(); ++itr2) - writer << **itr2; + MovableObject* MovableMan::GetMOFromID(MOID whichID) { + if (whichID != g_NoMOID && whichID != 0 && whichID < m_MOIDIndex.size()) { + return m_MOIDIndex[whichID]; + } + return nullptr; + } - return 0; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + MOID MovableMan::GetMOIDPixel(int pixelX, int pixelY, const std::vector& moidList) { + // Note - We loop through the MOs in reverse to make sure that the topmost (last drawn) MO that overlaps the specified coordinates is the one returned. + for (auto itr = moidList.rbegin(), itrEnd = moidList.rend(); itr < itrEnd; ++itr) { + MOID moid = *itr; + const MovableObject* mo = GetMOFromID(moid); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MovableMan object. + RTEAssert(mo, "Null MO found in MOID list!"); + if (mo == nullptr) { + continue; + } -void MovableMan::Destroy() -{ - for (std::deque::iterator it1 = m_Actors.begin(); it1 != m_Actors.end(); ++it1) - delete (*it1); - for (std::deque::iterator it2 = m_Items.begin(); it2 != m_Items.end(); ++it2) - delete (*it2); - for (std::deque::iterator it3 = m_Particles.begin(); it3 != m_Particles.end(); ++it3) - delete (*it3); - - Clear(); -} + if (mo->GetScale() == 0.0f) { + return g_NoMOID; + } else if (mo->HitTestAtPixel(pixelX, pixelY)) { + return moid; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMOFromID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a MO from its MOID. Note that MOID's are only valid during the -// same frame as they were assigned to the MOs! + return g_NoMOID; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RegisterObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Registers an object in a global Map collection so it could be found later with FindObjectByUniqueId + // Arguments: MO to register. + // Return value: None. + + void MovableMan::RegisterObject(MovableObject* mo) { + if (!mo) { + return; + } -MovableObject * MovableMan::GetMOFromID(MOID whichID) { - if (whichID != g_NoMOID && whichID != 0 && whichID < m_MOIDIndex.size()) { - return m_MOIDIndex[whichID]; + std::lock_guard guard(m_ObjectRegisteredMutex); + m_KnownObjects[mo->GetUniqueID()] = mo; } - return nullptr; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UnregisterObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes an object from the global lookup collection + // Arguments: MO to remove. + // Return value: None. -MOID MovableMan::GetMOIDPixel(int pixelX, int pixelY, const std::vector &moidList) { - // Note - We loop through the MOs in reverse to make sure that the topmost (last drawn) MO that overlaps the specified coordinates is the one returned. - for (auto itr = moidList.rbegin(), itrEnd = moidList.rend(); itr < itrEnd; ++itr) { - MOID moid = *itr; - const MovableObject *mo = GetMOFromID(moid); + void MovableMan::UnregisterObject(MovableObject* mo) { + if (!mo) { + return; + } - RTEAssert(mo, "Null MO found in MOID list!"); - if (mo == nullptr) { - continue; - } + std::lock_guard guard(m_ObjectRegisteredMutex); + m_KnownObjects.erase(mo->GetUniqueID()); + } - if (mo->GetScale() == 0.0f) { - return g_NoMOID; - } else if (mo->HitTestAtPixel(pixelX, pixelY)) { - return moid; - } - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return g_NoMOID; -} + const std::vector* MovableMan::GetMOsInBox(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const { + std::vector* vectorForLua = new std::vector(); + *vectorForLua = std::move(g_SceneMan.GetMOIDGrid().GetMOsInBox(box, ignoreTeam, getsHitByMOsOnly)); + return vectorForLua; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RegisterObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Registers an object in a global Map collection so it could be found later with FindObjectByUniqueId -// Arguments: MO to register. -// Return value: None. + const std::vector* MovableMan::GetMOsInRadius(const Vector& centre, float radius, int ignoreTeam, bool getsHitByMOsOnly) const { + std::vector* vectorForLua = new std::vector(); + *vectorForLua = std::move(g_SceneMan.GetMOIDGrid().GetMOsInRadius(centre, radius, ignoreTeam, getsHitByMOsOnly)); + return vectorForLua; + } -void MovableMan::RegisterObject(MovableObject * mo) -{ - if (!mo) { - return; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PurgeAllMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears out all MovableObject:s out of this. Effectively empties the world + // of anything moving, without resetting all of this' settings. - std::lock_guard guard(m_ObjectRegisteredMutex); - m_KnownObjects[mo->GetUniqueID()] = mo; -} + void MovableMan::PurgeAllMOs() { + for (std::deque::iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) { + (*itr)->DestroyScriptState(); + } + for (std::deque::iterator itr = m_Items.begin(); itr != m_Items.end(); ++itr) { + (*itr)->DestroyScriptState(); + } + for (std::deque::iterator itr = m_Particles.begin(); itr != m_Particles.end(); ++itr) { + (*itr)->DestroyScriptState(); + } + for (std::deque::iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) { + delete (*itr); + } + for (std::deque::iterator itr = m_Items.begin(); itr != m_Items.end(); ++itr) { + delete (*itr); + } + for (std::deque::iterator itr = m_Particles.begin(); itr != m_Particles.end(); ++itr) { + delete (*itr); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UnregisterObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes an object from the global lookup collection -// Arguments: MO to remove. -// Return value: None. + m_Actors.clear(); + m_Items.clear(); + m_Particles.clear(); + m_AddedActors.clear(); + m_AddedItems.clear(); + m_AddedParticles.clear(); + m_ValidActors.clear(); + m_ValidItems.clear(); + m_ValidParticles.clear(); + m_ActorRoster[Activity::TeamOne].clear(); + m_ActorRoster[Activity::TeamTwo].clear(); + m_ActorRoster[Activity::TeamThree].clear(); + m_ActorRoster[Activity::TeamFour].clear(); + m_SortTeamRoster[Activity::TeamOne] = false; + m_SortTeamRoster[Activity::TeamTwo] = false; + m_SortTeamRoster[Activity::TeamThree] = false; + m_SortTeamRoster[Activity::TeamFour] = false; + m_AddedAlarmEvents.clear(); + m_AlarmEvents.clear(); + m_MOIDIndex.clear(); + // We want to keep known objects around, 'cause these can exist even when not in the simulation (they're here from creation till deletion, regardless of whether they are in sim) + // m_KnownObjects.clear(); + } -void MovableMan::UnregisterObject(MovableObject * mo) -{ - if (!mo) { - return; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNextActorInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the first Actor in the internal Actor list that is + // of a specifc group, alternatively the first one AFTER a specific actor! + + Actor* MovableMan::GetNextActorInGroup(std::string group, Actor* pAfterThis) { + if (group.empty()) + return 0; + + // Begin at the beginning + std::deque::const_iterator aIt = m_Actors.begin(); + + // Search for the actor to start search from, if specified + if (pAfterThis) { + // Make the iterator point to the specified starting point actor + for (; aIt != m_Actors.end() && !((*aIt)->IsInGroup(group) && *aIt == pAfterThis); ++aIt) + ; + + // If we couldn't find the one to search for, + // then just start at the beginning again and get the first actor at the next step + if (aIt == m_Actors.end()) + aIt = m_Actors.begin(); + // Go one more step so we're not pointing at the one we're not supposed to get + else + ++aIt; + } - std::lock_guard guard(m_ObjectRegisteredMutex); - m_KnownObjects.erase(mo->GetUniqueID()); -} + // Now search for the first actor of the team from the search point (beginning or otherwise) + for (; aIt != m_Actors.end() && !(*aIt)->IsInGroup(group); ++aIt) + ; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // If nothing found between a specified actor and the end, + // then restart and see if there's anything between beginning and that specified actor + if (pAfterThis && aIt == m_Actors.end()) { + for (aIt = m_Actors.begin(); aIt != m_Actors.end() && !(*aIt)->IsInGroup(group); ++aIt) + ; -const std::vector * MovableMan::GetMOsInBox(const Box &box, int ignoreTeam, bool getsHitByMOsOnly) const { - std::vector *vectorForLua = new std::vector(); - *vectorForLua = std::move(g_SceneMan.GetMOIDGrid().GetMOsInBox(box, ignoreTeam, getsHitByMOsOnly)); - return vectorForLua; -} + // Still nothing?? Should at least get the specified actor and return it! - EDIT No becuase it just may not be there! + // RTEAssert(aIt != m_Actors.end(), "Search for something after specified actor, and didn't even find the specified actor!?"); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Still nothing, so return nothing + if (aIt == m_Actors.end()) + return 0; -const std::vector * MovableMan::GetMOsInRadius(const Vector ¢re, float radius, int ignoreTeam, bool getsHitByMOsOnly) const { - std::vector *vectorForLua = new std::vector(); - *vectorForLua = std::move(g_SceneMan.GetMOIDGrid().GetMOsInRadius(centre, radius, ignoreTeam, getsHitByMOsOnly)); - return vectorForLua; -} + if ((*aIt)->IsInGroup(group)) + return *aIt; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PurgeAllMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out all MovableObject:s out of this. Effectively empties the world -// of anything moving, without resetting all of this' settings. + return 0; + } -void MovableMan::PurgeAllMOs() -{ - for (std::deque::iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) { - (*itr)->DestroyScriptState(); - } - for (std::deque::iterator itr = m_Items.begin(); itr != m_Items.end(); ++itr) { - (*itr)->DestroyScriptState(); - } - for (std::deque::iterator itr = m_Particles.begin(); itr != m_Particles.end(); ++itr) { - (*itr)->DestroyScriptState(); - } - - for (std::deque::iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) { - delete (*itr); - } - for (std::deque::iterator itr = m_Items.begin(); itr != m_Items.end(); ++itr) { - delete (*itr); - } - for (std::deque::iterator itr = m_Particles.begin(); itr != m_Particles.end(); ++itr) { - delete (*itr); - } - - m_Actors.clear(); - m_Items.clear(); - m_Particles.clear(); - m_AddedActors.clear(); - m_AddedItems.clear(); - m_AddedParticles.clear(); - m_ValidActors.clear(); - m_ValidItems.clear(); - m_ValidParticles.clear(); - m_ActorRoster[Activity::TeamOne].clear(); - m_ActorRoster[Activity::TeamTwo].clear(); - m_ActorRoster[Activity::TeamThree].clear(); - m_ActorRoster[Activity::TeamFour].clear(); - m_SortTeamRoster[Activity::TeamOne] = false; - m_SortTeamRoster[Activity::TeamTwo] = false; - m_SortTeamRoster[Activity::TeamThree] = false; - m_SortTeamRoster[Activity::TeamFour] = false; - m_AddedAlarmEvents.clear(); - m_AlarmEvents.clear(); - m_MOIDIndex.clear(); - // We want to keep known objects around, 'cause these can exist even when not in the simulation (they're here from creation till deletion, regardless of whether they are in sim) - //m_KnownObjects.clear(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPrevActorInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the last Actor in the internal Actor list that is + // of a specifc group, alternatively the last one BEFORE a specific actor! + + Actor* MovableMan::GetPrevActorInGroup(std::string group, Actor* pBeforeThis) { + if (group.empty()) + return 0; + + // Begin at the reverse beginning + std::deque::reverse_iterator aIt = m_Actors.rbegin(); + + // Search for the actor to start search from, if specified + if (pBeforeThis) { + // Make the iterator point to the specified starting point actor + for (; aIt != m_Actors.rend() && !((*aIt)->IsInGroup(group) && *aIt == pBeforeThis); ++aIt) + ; + + // If we couldn't find the one to search for, + // then just start at the beginning again and get the first actor at the next step + if (aIt == m_Actors.rend()) + aIt = m_Actors.rbegin(); + // Go one more step so we're not pointing at the one we're not supposed to get + else + ++aIt; + } + // Now search for the first actor of the team from the search point (beginning or otherwise) + for (; aIt != m_Actors.rend() && !(*aIt)->IsInGroup(group); ++aIt) + ; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextActorInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the first Actor in the internal Actor list that is -// of a specifc group, alternatively the first one AFTER a specific actor! + // If nothing found between a specified actor and the end, + // then restart and see if there's anything between beginning and that specified actor + if (pBeforeThis && aIt == m_Actors.rend()) { + for (aIt = m_Actors.rbegin(); aIt != m_Actors.rend() && !(*aIt)->IsInGroup(group); ++aIt) + ; -Actor * MovableMan::GetNextActorInGroup(std::string group, Actor *pAfterThis) -{ - if (group.empty()) - return 0; - - // Begin at the beginning - std::deque::const_iterator aIt = m_Actors.begin(); - - // Search for the actor to start search from, if specified - if (pAfterThis) - { - // Make the iterator point to the specified starting point actor - for (; aIt != m_Actors.end() && !((*aIt)->IsInGroup(group) && *aIt == pAfterThis); ++aIt) - ; - - // If we couldn't find the one to search for, - // then just start at the beginning again and get the first actor at the next step - if (aIt == m_Actors.end()) - aIt = m_Actors.begin(); - // Go one more step so we're not pointing at the one we're not supposed to get - else - ++aIt; - } - - // Now search for the first actor of the team from the search point (beginning or otherwise) - for (; aIt != m_Actors.end() && !(*aIt)->IsInGroup(group); ++aIt) - ; - - // If nothing found between a specified actor and the end, - // then restart and see if there's anything between beginning and that specified actor - if (pAfterThis && aIt == m_Actors.end()) - { - for (aIt = m_Actors.begin(); aIt != m_Actors.end() && !(*aIt)->IsInGroup(group); ++aIt) - ; - - // Still nothing?? Should at least get the specified actor and return it! - EDIT No becuase it just may not be there! -// RTEAssert(aIt != m_Actors.end(), "Search for something after specified actor, and didn't even find the specified actor!?"); - } - - // Still nothing, so return nothing - if (aIt == m_Actors.end()) - return 0; - - if ((*aIt)->IsInGroup(group)) - return *aIt; - - return 0; -} + // Still nothing?? Should at least get the specified actor and return it! - EDIT No becuase it just may not be there! + // RTEAssert(aIt != m_Actors.rend(), "Search for something after specified actor, and didn't even find the specified actor!?"); + } + // Still nothing, so return nothing + if (aIt == m_Actors.rend()) + return 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPrevActorInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the last Actor in the internal Actor list that is -// of a specifc group, alternatively the last one BEFORE a specific actor! + if ((*aIt)->IsInGroup(group)) + return *aIt; -Actor * MovableMan::GetPrevActorInGroup(std::string group, Actor *pBeforeThis) -{ - if (group.empty()) - return 0; - - // Begin at the reverse beginning - std::deque::reverse_iterator aIt = m_Actors.rbegin(); - - // Search for the actor to start search from, if specified - if (pBeforeThis) - { - // Make the iterator point to the specified starting point actor - for (; aIt != m_Actors.rend() && !((*aIt)->IsInGroup(group) && *aIt == pBeforeThis); ++aIt) - ; - - // If we couldn't find the one to search for, - // then just start at the beginning again and get the first actor at the next step - if (aIt == m_Actors.rend()) - aIt = m_Actors.rbegin(); - // Go one more step so we're not pointing at the one we're not supposed to get - else - ++aIt; - } - - // Now search for the first actor of the team from the search point (beginning or otherwise) - for (; aIt != m_Actors.rend() && !(*aIt)->IsInGroup(group); ++aIt) - ; - - // If nothing found between a specified actor and the end, - // then restart and see if there's anything between beginning and that specified actor - if (pBeforeThis && aIt == m_Actors.rend()) - { - for (aIt = m_Actors.rbegin(); aIt != m_Actors.rend() && !(*aIt)->IsInGroup(group); ++aIt) - ; - - // Still nothing?? Should at least get the specified actor and return it! - EDIT No becuase it just may not be there! -// RTEAssert(aIt != m_Actors.rend(), "Search for something after specified actor, and didn't even find the specified actor!?"); - } - - // Still nothing, so return nothing - if (aIt == m_Actors.rend()) - return 0; - - if ((*aIt)->IsInGroup(group)) - return *aIt; - - return 0; -} + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNextTeamActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the first Actor in the internal Actor list that is + // of a specifc team, alternatively the first one AFTER a specific actor! + + Actor* MovableMan::GetNextTeamActor(int team, Actor* pAfterThis) { + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount || m_ActorRoster[team].empty()) + return 0; + /* + // Begin at the beginning + std::deque::const_iterator aIt = m_Actors.begin(); + + // Search for the actor to start search from, if specified + if (pAfterThis) + { + // Make the iterator point to the specified starting point actor + for (; aIt != m_Actors.end() && !((*aIt)->GetTeam() == team && *aIt == pAfterThis); ++aIt) + ; + + // If we couldn't find the one to search for, + // then just start at the beginning again and get the first actor at the next step + if (aIt == m_Actors.end()) + aIt = m_Actors.begin(); + // Go one more step so we're not pointing at the one we're not supposed to get + else + ++aIt; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextTeamActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the first Actor in the internal Actor list that is -// of a specifc team, alternatively the first one AFTER a specific actor! + // Now search for the first actor of the team from the search point (beginning or otherwise) + for (; aIt != m_Actors.end() && (*aIt)->GetTeam() != team; ++aIt) + ; -Actor * MovableMan::GetNextTeamActor(int team, Actor *pAfterThis) -{ - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount || m_ActorRoster[team].empty()) - return 0; -/* - // Begin at the beginning - std::deque::const_iterator aIt = m_Actors.begin(); - - // Search for the actor to start search from, if specified - if (pAfterThis) - { - // Make the iterator point to the specified starting point actor - for (; aIt != m_Actors.end() && !((*aIt)->GetTeam() == team && *aIt == pAfterThis); ++aIt) - ; - - // If we couldn't find the one to search for, - // then just start at the beginning again and get the first actor at the next step - if (aIt == m_Actors.end()) - aIt = m_Actors.begin(); - // Go one more step so we're not pointing at the one we're not supposed to get - else - ++aIt; - } - - // Now search for the first actor of the team from the search point (beginning or otherwise) - for (; aIt != m_Actors.end() && (*aIt)->GetTeam() != team; ++aIt) - ; - - // If nothing found between a specified actor and the end, - // then restart and see if there's anything between beginning and that specified actor - if (pAfterThis && aIt == m_Actors.end()) - { - for (aIt = m_Actors.begin(); aIt != m_Actors.end() && (*aIt)->GetTeam() != team; ++aIt) - ; - - // Still nothing?? Should at least get the specified actor and return it! - EDIT No becuase it just may not be there! -// RTEAssert(aIt != m_Actors.end(), "Search for something after specified actor, and didn't even find the specified actor!?"); - } - - // Still nothing, so return nothing - if (aIt == m_Actors.end()) - return 0; - - if ((*aIt)->GetTeam() == team) - return *aIt; - - return 0; -*/ - // First sort the roster - m_ActorRoster[team].sort(MOXPosComparison()); - - // Begin at the beginning - std::list::const_iterator aIt = m_ActorRoster[team].begin(); - - // Search for the actor to start search from, if specified - if (pAfterThis) - { - // Make the iterator point to the specified starting point actor - for (; aIt != m_ActorRoster[team].end() && *aIt != pAfterThis; ++aIt) - ; - - // If we couldn't find the one to search for, then just return the first one - if (aIt == m_ActorRoster[team].end()) - aIt = m_ActorRoster[team].begin(); - // Go one more step so we're not pointing at the one we're not supposed to get - else - { - ++aIt; - // If that was the last one, then return the first in the list - if (aIt == m_ActorRoster[team].end()) - aIt = m_ActorRoster[team].begin(); - } - } - - RTEAssert((*aIt)->GetTeam() == team, "Actor of wrong team found in the wrong roster!"); - return *aIt; -} + // If nothing found between a specified actor and the end, + // then restart and see if there's anything between beginning and that specified actor + if (pAfterThis && aIt == m_Actors.end()) + { + for (aIt = m_Actors.begin(); aIt != m_Actors.end() && (*aIt)->GetTeam() != team; ++aIt) + ; + // Still nothing?? Should at least get the specified actor and return it! - EDIT No becuase it just may not be there! + // RTEAssert(aIt != m_Actors.end(), "Search for something after specified actor, and didn't even find the specified actor!?"); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPrevTeamActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the last Actor in the internal Actor list that is -// of a specifc team, alternatively the last one BEFORE a specific actor! + // Still nothing, so return nothing + if (aIt == m_Actors.end()) + return 0; + + if ((*aIt)->GetTeam() == team) + return *aIt; + + return 0; + */ + // First sort the roster + m_ActorRoster[team].sort(MOXPosComparison()); + + // Begin at the beginning + std::list::const_iterator aIt = m_ActorRoster[team].begin(); + + // Search for the actor to start search from, if specified + if (pAfterThis) { + // Make the iterator point to the specified starting point actor + for (; aIt != m_ActorRoster[team].end() && *aIt != pAfterThis; ++aIt) + ; + + // If we couldn't find the one to search for, then just return the first one + if (aIt == m_ActorRoster[team].end()) + aIt = m_ActorRoster[team].begin(); + // Go one more step so we're not pointing at the one we're not supposed to get + else { + ++aIt; + // If that was the last one, then return the first in the list + if (aIt == m_ActorRoster[team].end()) + aIt = m_ActorRoster[team].begin(); + } + } -Actor * MovableMan::GetPrevTeamActor(int team, Actor *pBeforeThis) -{ - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount || m_Actors.empty() || m_ActorRoster[team].empty()) - return 0; -/* Obsolete, now uses team rosters which are sorted - // Begin at the reverse beginning - std::deque::const_reverse_iterator aIt = m_Actors.rbegin(); - - // Search for the actor to start search from, if specified - if (pBeforeThis) - { - // Make the iterator point to the specified starting point actor - for (; aIt != m_Actors.rend() && !((*aIt)->GetTeam() == team && *aIt == pBeforeThis); ++aIt) - ; - - // If we couldn't find the one to search for, - // then just start at the beginning again and get the first actor at the next step - if (aIt == m_Actors.rend()) - aIt = m_Actors.rbegin(); - // Go one more step so we're not pointing at the one we're not supposed to get - else - ++aIt; - } - - // Now search for the first actor of the team from the search point (beginning or otherwise) - for (; aIt != m_Actors.rend() && (*aIt)->GetTeam() != team; ++aIt) - ; - - // If nothing found between a specified actor and the end, - // then restart and see if there's anything between beginning and that specified actor - if (pBeforeThis && aIt == m_Actors.rend()) - { - for (aIt = m_Actors.rbegin(); aIt != m_Actors.rend() && (*aIt)->GetTeam() != team; ++aIt) - ; - - // Still nothing?? Should at least get the specified actor and return it! - EDIT No becuase it just may not be there! -// RTEAssert(aIt != m_Actors.rend(), "Search for something after specified actor, and didn't even find the specified actor!?"); - } - - // Still nothing, so return nothing - if (aIt == m_Actors.rend()) - return 0; - - if ((*aIt)->GetTeam() == team) - return *aIt; - - return 0; -*/ - // First sort the roster - m_ActorRoster[team].sort(MOXPosComparison()); - - // Begin at the reverse beginning of roster - std::list::reverse_iterator aIt = m_ActorRoster[team].rbegin(); - - // Search for the actor to start search from, if specified - if (pBeforeThis) - { - // Make the iterator point to the specified starting point actor - for (; aIt != m_ActorRoster[team].rend() && *aIt != pBeforeThis; ++aIt) - ; - - // If we couldn't find the one to search for, then just return the one at the end - if (aIt == m_ActorRoster[team].rend()) - aIt = m_ActorRoster[team].rbegin(); - // Go one more step so we're not pointing at the one we're not supposed to get - else - { - ++aIt; - // If that was the first one, then return the last in the list - if (aIt == m_ActorRoster[team].rend()) - aIt = m_ActorRoster[team].rbegin(); - } - } - - RTEAssert((*aIt)->GetTeam() == team, "Actor of wrong team found in the wrong roster!"); - return *aIt; -} + RTEAssert((*aIt)->GetTeam() == team, "Actor of wrong team found in the wrong roster!"); + return *aIt; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPrevTeamActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the last Actor in the internal Actor list that is + // of a specifc team, alternatively the last one BEFORE a specific actor! + + Actor* MovableMan::GetPrevTeamActor(int team, Actor* pBeforeThis) { + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount || m_Actors.empty() || m_ActorRoster[team].empty()) + return 0; + /* Obsolete, now uses team rosters which are sorted + // Begin at the reverse beginning + std::deque::const_reverse_iterator aIt = m_Actors.rbegin(); + + // Search for the actor to start search from, if specified + if (pBeforeThis) + { + // Make the iterator point to the specified starting point actor + for (; aIt != m_Actors.rend() && !((*aIt)->GetTeam() == team && *aIt == pBeforeThis); ++aIt) + ; + + // If we couldn't find the one to search for, + // then just start at the beginning again and get the first actor at the next step + if (aIt == m_Actors.rend()) + aIt = m_Actors.rbegin(); + // Go one more step so we're not pointing at the one we're not supposed to get + else + ++aIt; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetClosestTeamActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to an Actor in the internal Actor list that is of a -// specifc team and closest to a specific scene point. + // Now search for the first actor of the team from the search point (beginning or otherwise) + for (; aIt != m_Actors.rend() && (*aIt)->GetTeam() != team; ++aIt) + ; -Actor * MovableMan::GetClosestTeamActor(int team, int player, const Vector &scenePoint, int maxRadius, Vector &getDistance, bool onlyPlayerControllableActors, const Actor *excludeThis) -{ - if (team < Activity::NoTeam || team >= Activity::MaxTeamCount || m_Actors.empty() || m_ActorRoster[team].empty()) - return 0; - - Activity *pActivity = g_ActivityMan.GetActivity(); - - float sqrShortestDistance = static_cast(maxRadius * maxRadius); - Actor *pClosestActor = 0; - - // If we're looking for a noteam actor, then go through the entire actor list instead - if (team == Activity::NoTeam) - { - for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) - { - if ((*aIt) == excludeThis || (*aIt)->GetTeam() != Activity::NoTeam || (onlyPlayerControllableActors && !(*aIt)->IsPlayerControllable())) { - continue; - } - - // Check if even within search radius - float sqrDistance = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()).GetSqrMagnitude(); - if (sqrDistance < sqrShortestDistance) - { - sqrShortestDistance = sqrDistance; - pClosestActor = *aIt; - } - } - } - // A specific team, so use the rosters instead - else - { - for (std::list::iterator aIt = m_ActorRoster[team].begin(); aIt != m_ActorRoster[team].end(); ++aIt) - { - if ((*aIt) == excludeThis || (onlyPlayerControllableActors && !(*aIt)->IsPlayerControllable()) || (player != NoPlayer && ((*aIt)->GetController()->IsPlayerControlled(player) || (pActivity && pActivity->IsOtherPlayerBrain(*aIt, player))))) { + // If nothing found between a specified actor and the end, + // then restart and see if there's anything between beginning and that specified actor + if (pBeforeThis && aIt == m_Actors.rend()) + { + for (aIt = m_Actors.rbegin(); aIt != m_Actors.rend() && (*aIt)->GetTeam() != team; ++aIt) + ; + + // Still nothing?? Should at least get the specified actor and return it! - EDIT No becuase it just may not be there! + // RTEAssert(aIt != m_Actors.rend(), "Search for something after specified actor, and didn't even find the specified actor!?"); + } + + // Still nothing, so return nothing + if (aIt == m_Actors.rend()) + return 0; + + if ((*aIt)->GetTeam() == team) + return *aIt; + + return 0; + */ + // First sort the roster + m_ActorRoster[team].sort(MOXPosComparison()); + + // Begin at the reverse beginning of roster + std::list::reverse_iterator aIt = m_ActorRoster[team].rbegin(); + + // Search for the actor to start search from, if specified + if (pBeforeThis) { + // Make the iterator point to the specified starting point actor + for (; aIt != m_ActorRoster[team].rend() && *aIt != pBeforeThis; ++aIt) + ; + + // If we couldn't find the one to search for, then just return the one at the end + if (aIt == m_ActorRoster[team].rend()) + aIt = m_ActorRoster[team].rbegin(); + // Go one more step so we're not pointing at the one we're not supposed to get + else { + ++aIt; + // If that was the first one, then return the last in the list + if (aIt == m_ActorRoster[team].rend()) + aIt = m_ActorRoster[team].rbegin(); + } + } + + RTEAssert((*aIt)->GetTeam() == team, "Actor of wrong team found in the wrong roster!"); + return *aIt; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetClosestTeamActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to an Actor in the internal Actor list that is of a + // specifc team and closest to a specific scene point. + + Actor* MovableMan::GetClosestTeamActor(int team, int player, const Vector& scenePoint, int maxRadius, Vector& getDistance, bool onlyPlayerControllableActors, const Actor* excludeThis) { + if (team < Activity::NoTeam || team >= Activity::MaxTeamCount || m_Actors.empty() || m_ActorRoster[team].empty()) + return 0; + + Activity* pActivity = g_ActivityMan.GetActivity(); + + float sqrShortestDistance = static_cast(maxRadius * maxRadius); + Actor* pClosestActor = 0; + + // If we're looking for a noteam actor, then go through the entire actor list instead + if (team == Activity::NoTeam) { + for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) { + if ((*aIt) == excludeThis || (*aIt)->GetTeam() != Activity::NoTeam || (onlyPlayerControllableActors && !(*aIt)->IsPlayerControllable())) { + continue; + } + + // Check if even within search radius + float sqrDistance = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()).GetSqrMagnitude(); + if (sqrDistance < sqrShortestDistance) { + sqrShortestDistance = sqrDistance; + pClosestActor = *aIt; + } + } + } + // A specific team, so use the rosters instead + else { + for (std::list::iterator aIt = m_ActorRoster[team].begin(); aIt != m_ActorRoster[team].end(); ++aIt) { + if ((*aIt) == excludeThis || (onlyPlayerControllableActors && !(*aIt)->IsPlayerControllable()) || (player != NoPlayer && ((*aIt)->GetController()->IsPlayerControlled(player) || (pActivity && pActivity->IsOtherPlayerBrain(*aIt, player))))) { + continue; + } + + Vector distanceVec = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()); + + // Check if even within search radius + float sqrDistance = distanceVec.GetSqrMagnitude(); + if (sqrDistance < sqrShortestDistance) { + sqrShortestDistance = sqrDistance; + pClosestActor = *aIt; + getDistance.SetXY(distanceVec.GetX(), distanceVec.GetY()); + } + } + } + + return pClosestActor; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetClosestEnemyActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to an Actor in the internal Actor list that is is not of + // the specified team and closest to a specific scene point. + + Actor* MovableMan::GetClosestEnemyActor(int team, const Vector& scenePoint, int maxRadius, Vector& getDistance) { + if (team < Activity::NoTeam || team >= Activity::MaxTeamCount || m_Actors.empty()) + return 0; + + Activity* pActivity = g_ActivityMan.GetActivity(); + + float sqrShortestDistance = static_cast(maxRadius * maxRadius); + Actor* pClosestActor = 0; + + for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) { + if ((*aIt)->GetTeam() == team) continue; + + Vector distanceVec = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()); + + // Check if even within search radius + float sqrDistance = distanceVec.GetSqrMagnitude(); + if (sqrDistance < sqrShortestDistance) { + sqrShortestDistance = sqrDistance; + pClosestActor = *aIt; + getDistance.SetXY(distanceVec.GetX(), distanceVec.GetY()); } + } + + return pClosestActor; + } - Vector distanceVec = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetClosestActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to an Actor in the internal Actor list that is closest + // to a specific scene point. - // Check if even within search radius - float sqrDistance = distanceVec.GetSqrMagnitude(); - if (sqrDistance < sqrShortestDistance) - { - sqrShortestDistance = sqrDistance; - pClosestActor = *aIt; + Actor* MovableMan::GetClosestActor(const Vector& scenePoint, int maxRadius, Vector& getDistance, const Actor* pExcludeThis) { + if (m_Actors.empty()) + return 0; + + Activity* pActivity = g_ActivityMan.GetActivity(); + + float sqrShortestDistance = static_cast(maxRadius * maxRadius); + Actor* pClosestActor = 0; + + for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) { + if ((*aIt) == pExcludeThis) + continue; + + Vector distanceVec = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()); + + // Check if even within search radius + float sqrDistance = distanceVec.GetSqrMagnitude(); + if (sqrDistance < sqrShortestDistance) { + sqrShortestDistance = sqrDistance; + pClosestActor = *aIt; getDistance.SetXY(distanceVec.GetX(), distanceVec.GetY()); - } - } - } + } + } - return pClosestActor; -} + return pClosestActor; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetClosestBrainActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the brain actor of a specific team that is closest to + // a scene point. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetClosestEnemyActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to an Actor in the internal Actor list that is is not of -// the specified team and closest to a specific scene point. + Actor* MovableMan::GetClosestBrainActor(int team, const Vector& scenePoint) const { + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount || m_ActorRoster[team].empty()) + return 0; -Actor * MovableMan::GetClosestEnemyActor(int team, const Vector &scenePoint, int maxRadius, Vector &getDistance) -{ - if (team < Activity::NoTeam || team >= Activity::MaxTeamCount || m_Actors.empty()) - return 0; - - Activity *pActivity = g_ActivityMan.GetActivity(); - - float sqrShortestDistance = static_cast(maxRadius * maxRadius); - Actor *pClosestActor = 0; - - for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) - { - if ((*aIt)->GetTeam() == team) - continue; - - Vector distanceVec = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()); - - // Check if even within search radius - float sqrDistance = distanceVec.GetSqrMagnitude(); - if (sqrDistance < sqrShortestDistance) - { - sqrShortestDistance = sqrDistance; - pClosestActor = *aIt; - getDistance.SetXY(distanceVec.GetX(), distanceVec.GetY()); - } - } - - return pClosestActor; -} + float sqrShortestDistance = std::numeric_limits::infinity(); + sqrShortestDistance *= sqrShortestDistance; + Actor* pClosestBrain = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetClosestActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to an Actor in the internal Actor list that is closest -// to a specific scene point. + for (std::list::const_iterator aIt = m_ActorRoster[team].begin(); aIt != m_ActorRoster[team].end(); ++aIt) { + if (!(*aIt)->HasObjectInGroup("Brains")) + continue; -Actor * MovableMan::GetClosestActor(const Vector &scenePoint, int maxRadius, Vector &getDistance, const Actor *pExcludeThis) -{ - if (m_Actors.empty()) - return 0; + // Check if closer than best so far + float sqrDistance = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()).GetSqrMagnitude(); + if (sqrDistance < sqrShortestDistance) { + sqrShortestDistance = sqrDistance; + pClosestBrain = *aIt; + } + } - Activity *pActivity = g_ActivityMan.GetActivity(); + return pClosestBrain; + } - float sqrShortestDistance = static_cast(maxRadius * maxRadius); - Actor *pClosestActor = 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetClosestOtherBrainActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the brain actor NOT of a specific team that is closest + // to a scene point. + + Actor* MovableMan::GetClosestOtherBrainActor(int notOfTeam, const Vector& scenePoint) const { + if (notOfTeam < Activity::TeamOne || notOfTeam >= Activity::MaxTeamCount || m_Actors.empty()) + return 0; + + float sqrShortestDistance = std::numeric_limits::infinity(); + sqrShortestDistance *= sqrShortestDistance; + + Actor* pClosestBrain = 0; + Actor* pContenderBrain = 0; + + for (int t = Activity::TeamOne; t < g_ActivityMan.GetActivity()->GetTeamCount(); ++t) { + if (t != notOfTeam) { + pContenderBrain = GetClosestBrainActor(t, scenePoint); + float sqrDistance = (pContenderBrain->GetPos() - scenePoint).GetSqrMagnitude(); + if (sqrDistance < sqrShortestDistance) { + sqrShortestDistance = sqrDistance; + pClosestBrain = pContenderBrain; + } + } + } + return pClosestBrain; + } - for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) - { - if ((*aIt) == pExcludeThis) - continue; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetUnassignedBrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the brain actor of a specific team. + // Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. - Vector distanceVec = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()); + Actor* MovableMan::GetUnassignedBrain(int team) const { + if (/*m_Actors.empty() || */ m_ActorRoster[team].empty()) + return 0; - // Check if even within search radius - float sqrDistance = distanceVec.GetSqrMagnitude(); - if (sqrDistance < sqrShortestDistance) - { - sqrShortestDistance = sqrDistance; - pClosestActor = *aIt; - getDistance.SetXY(distanceVec.GetX(), distanceVec.GetY()); - } - } + for (std::list::const_iterator aIt = m_ActorRoster[team].begin(); aIt != m_ActorRoster[team].end(); ++aIt) { + if ((*aIt)->HasObjectInGroup("Brains") && !g_ActivityMan.GetActivity()->IsAssignedBrain(*aIt)) + return *aIt; + } - return pClosestActor; -} + // Also need to look through all the actors added this frame, one might be a brain. + int actorTeam = Activity::NoTeam; + for (std::deque::const_iterator aaIt = m_AddedActors.begin(); aaIt != m_AddedActors.end(); ++aaIt) { + int actorTeam = (*aaIt)->GetTeam(); + // Accept no-team brains too - ACTUALLY, DON'T + if ((actorTeam == team /* || actorTeam == Activity::NoTeam*/) && (*aaIt)->HasObjectInGroup("Brains") && !g_ActivityMan.GetActivity()->IsAssignedBrain(*aaIt)) + return *aaIt; + } + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetClosestBrainActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the brain actor of a specific team that is closest to -// a scene point. + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool MovableMan::AddMO(MovableObject* movableObjectToAdd) { + if (!movableObjectToAdd) { + return false; + } + + if (Actor* actorToAdd = dynamic_cast(movableObjectToAdd)) { + AddActor(actorToAdd); + return true; + } else if (HeldDevice* heldDeviceToAdd = dynamic_cast(movableObjectToAdd)) { + AddItem(heldDeviceToAdd); + return true; + } + AddParticle(movableObjectToAdd); + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MovableMan::AddActor(Actor* actorToAdd) { + if (actorToAdd) { + actorToAdd->SetAsAddedToMovableMan(); + actorToAdd->CorrectAttachableAndWoundPositionsAndRotations(); + + if (actorToAdd->IsTooFast()) { + actorToAdd->SetToDelete(true); + } else { + if (!dynamic_cast(actorToAdd)) { + actorToAdd->MoveOutOfTerrain(g_MaterialGrass); + } + if (actorToAdd->IsStatus(Actor::INACTIVE)) { + actorToAdd->SetStatus(Actor::STABLE); + } + actorToAdd->NotResting(); + actorToAdd->NewFrame(); + actorToAdd->SetAge(g_TimerMan.GetDeltaTimeMS() * -1.0f); + } + + { + std::lock_guard lock(m_AddedActorsMutex); + m_AddedActors.push_back(actorToAdd); + m_ValidActors.insert(actorToAdd); + + // This will call SetTeam and subsequently force the team as active. + AddActorToTeamRoster(actorToAdd); + } + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MovableMan::AddItem(HeldDevice* itemToAdd) { + if (itemToAdd) { + g_ActivityMan.GetActivity()->ForceSetTeamAsActive(itemToAdd->GetTeam()); + itemToAdd->SetAsAddedToMovableMan(); + itemToAdd->CorrectAttachableAndWoundPositionsAndRotations(); + + if (itemToAdd->IsTooFast()) { + itemToAdd->SetToDelete(true); + } else { + if (!itemToAdd->IsSetToDelete()) { + itemToAdd->MoveOutOfTerrain(g_MaterialGrass); + } + itemToAdd->NotResting(); + itemToAdd->NewFrame(); + itemToAdd->SetAge(g_TimerMan.GetDeltaTimeMS() * -1.0f); + } + + std::lock_guard lock(m_AddedItemsMutex); + m_AddedItems.push_back(itemToAdd); + m_ValidItems.insert(itemToAdd); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MovableMan::AddParticle(MovableObject* particleToAdd) { + if (particleToAdd) { + g_ActivityMan.GetActivity()->ForceSetTeamAsActive(particleToAdd->GetTeam()); + particleToAdd->SetAsAddedToMovableMan(); + if (MOSRotating* particleToAddAsMOSRotating = dynamic_cast(particleToAdd)) { + particleToAddAsMOSRotating->CorrectAttachableAndWoundPositionsAndRotations(); + } + + if (particleToAdd->IsTooFast()) { + particleToAdd->SetToDelete(true); + } else { + // TODO consider moving particles out of grass. It's old code that was removed because it's slow to do this for every particle. + particleToAdd->NotResting(); + particleToAdd->NewFrame(); + particleToAdd->SetAge(g_TimerMan.GetDeltaTimeMS() * -1.0f); + } + if (particleToAdd->IsDevice()) { + std::lock_guard lock(m_AddedItemsMutex); + m_AddedItems.push_back(particleToAdd); + m_ValidItems.insert(particleToAdd); + } else { + std::lock_guard lock(m_AddedParticlesMutex); + m_AddedParticles.push_back(particleToAdd); + m_ValidParticles.insert(particleToAdd); + } + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes an Actor from the internal list of MO:s. After the Actor is + // removed, ownership is effectively released and transferred to whatever + // client called this method. + + Actor* MovableMan::RemoveActor(MovableObject* pActorToRem) { + Actor* removed = nullptr; + + if (pActorToRem) { + for (std::deque::iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) { + if (*itr == pActorToRem) { + std::lock_guard lock(m_ActorsMutex); + removed = *itr; + m_ValidActors.erase(*itr); + m_Actors.erase(itr); + break; + } + } + // Try the newly added actors if we couldn't find it in the regular deque + if (!removed) { + for (std::deque::iterator itr = m_AddedActors.begin(); itr != m_AddedActors.end(); ++itr) { + if (*itr == pActorToRem) { + std::lock_guard lock(m_AddedActorsMutex); + removed = *itr; + m_ValidActors.erase(*itr); + m_AddedActors.erase(itr); + break; + } + } + } + RemoveActorFromTeamRoster(dynamic_cast(pActorToRem)); + pActorToRem->SetAsAddedToMovableMan(false); + } + return removed; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a pickup-able MovableObject item from the internal list of + // MO:s. After the item is removed, ownership is effectively released and + // transferred to whatever client called this method. + + MovableObject* MovableMan::RemoveItem(MovableObject* pItemToRem) { + MovableObject* removed = nullptr; + + if (pItemToRem) { + for (std::deque::iterator itr = m_Items.begin(); itr != m_Items.end(); ++itr) { + if (*itr == pItemToRem) { + std::lock_guard lock(m_ItemsMutex); + removed = *itr; + m_ValidItems.erase(*itr); + m_Items.erase(itr); + break; + } + } + // Try the newly added items if we couldn't find it in the regular deque + if (!removed) { + for (std::deque::iterator itr = m_AddedItems.begin(); itr != m_AddedItems.end(); ++itr) { + if (*itr == pItemToRem) { + std::lock_guard lock(m_AddedItemsMutex); + removed = *itr; + m_ValidItems.erase(*itr); + m_AddedItems.erase(itr); + break; + } + } + } + pItemToRem->SetAsAddedToMovableMan(false); + } + return removed; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveParticle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a MovableObject from the internal list of MO:s. After the + // MO is removed, ownership is effectively released and transferred to + // whatever client called this method. + + MovableObject* MovableMan::RemoveParticle(MovableObject* pMOToRem) { + MovableObject* removed = nullptr; + + if (pMOToRem) { + for (std::deque::iterator itr = m_Particles.begin(); itr != m_Particles.end(); ++itr) { + if (*itr == pMOToRem) { + std::lock_guard lock(m_ParticlesMutex); + removed = *itr; + m_ValidParticles.erase(*itr); + m_Particles.erase(itr); + break; + } + } + // Try the newly added particles if we couldn't find it in the regular deque + if (!removed) { + for (std::deque::iterator itr = m_AddedParticles.begin(); itr != m_AddedParticles.end(); ++itr) { + if (*itr == pMOToRem) { + std::lock_guard lock(m_AddedParticlesMutex); + removed = *itr; + m_ValidParticles.erase(*itr); + m_AddedParticles.erase(itr); + break; + } + } + } + pMOToRem->SetAsAddedToMovableMan(false); + } + return removed; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddActorToTeamRoster + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds actor to internal team roster + // Arguments: Pointer to actor + // Return value: None. + + void MovableMan::AddActorToTeamRoster(Actor* pActorToAdd) { + if (!pActorToAdd) { + return; + } + + // Add to the team roster and then sort it too + int team = pActorToAdd->GetTeam(); + // Also re-set the TEam so that the Team Icons get set up properly + pActorToAdd->SetTeam(team); + // Only add to a roster if it's on a team AND is controllable (eg doors are not) + if (team >= Activity::TeamOne && team < Activity::MaxTeamCount && pActorToAdd->IsControllable()) { + std::lock_guard lock(m_ActorRosterMutex); + m_ActorRoster[pActorToAdd->GetTeam()].push_back(pActorToAdd); + m_ActorRoster[pActorToAdd->GetTeam()].sort(MOXPosComparison()); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveActorToTeamRoster + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes actor from internal team roster + // Arguments: Pointer to actor + // Return value: None. + + void MovableMan::RemoveActorFromTeamRoster(Actor* pActorToRem) { + if (!pActorToRem) { + return; + } + + int team = pActorToRem->GetTeam(); + + // Remove from roster as well + if (team >= Activity::TeamOne && team < Activity::MaxTeamCount) { + std::lock_guard lock(m_ActorRosterMutex); + m_ActorRoster[team].remove(pActorToRem); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeActorTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes actor team and updates team rosters. + + void MovableMan::ChangeActorTeam(Actor* pActor, int team) { + if (!pActor) { + return; + } + + if (pActor->IsPlayerControlled()) { + g_ActivityMan.GetActivity()->LoseControlOfActor(pActor->GetController()->GetPlayer()); + } + + RemoveActorFromTeamRoster(pActor); + pActor->SetTeam(team); + AddActorToTeamRoster(pActor); + + // Because doors affect the team-based pathfinders, we need to tell them there's been a change. + // This is hackily done by erasing the door material, updating the pathfinders, then redrawing it and updating them again so they properly account for the door's new team. + if (ADoor* actorAsADoor = dynamic_cast(pActor); actorAsADoor && actorAsADoor->GetDoorMaterialDrawn()) { + actorAsADoor->TempEraseOrRedrawDoorMaterial(true); + g_SceneMan.GetTerrain()->AddUpdatedMaterialArea(actorAsADoor->GetBoundingBox()); + g_SceneMan.GetScene()->UpdatePathFinding(); + actorAsADoor->TempEraseOrRedrawDoorMaterial(false); + g_SceneMan.GetTerrain()->AddUpdatedMaterialArea(actorAsADoor->GetBoundingBox()); + g_SceneMan.GetScene()->UpdatePathFinding(); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ValidateMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through and checks that all MOID's have valid MO pointers + // associated with them. This shuold only be used for testing, as it will + // crash the app if validation fails. + + bool MovableMan::ValidateMOIDs() { +#ifdef DEBUG_BUILD + for (const MovableObject* mo: m_MOIDIndex) { + RTEAssert(mo, "Null MO found!"); + } +#endif + return true; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ValidMO + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MovableObject pointer points to an + // MO that's currently active in the simulation, and kept by this MovableMan. + + bool MovableMan::ValidMO(const MovableObject* pMOToCheck) { + bool exists = m_ValidActors.find(pMOToCheck) != m_ValidActors.end() || + m_ValidItems.find(pMOToCheck) != m_ValidItems.end() || + m_ValidParticles.find(pMOToCheck) != m_ValidParticles.end(); + + return pMOToCheck && exists; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MovableObject is an active Actor kept + // by this MovableMan or not. + + bool MovableMan::IsActor(const MovableObject* pMOToCheck) { + return pMOToCheck && m_ValidActors.find(pMOToCheck) != m_ValidActors.end(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MovableObject is an active Item kept + // by this MovableMan or not. + + bool MovableMan::IsDevice(const MovableObject* pMOToCheck) { + return pMOToCheck && m_ValidItems.find(pMOToCheck) != m_ValidItems.end(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsParticle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MovableObject is an active Item kept + // by this MovableMan or not. + + bool MovableMan::IsParticle(const MovableObject* pMOToCheck) { + return pMOToCheck && m_ValidParticles.find(pMOToCheck) != m_ValidParticles.end(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsOfActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MOID is that of an MO which either is + // or is parented to an active Actor by this MovableMan, or not. + + bool MovableMan::IsOfActor(MOID checkMOID) { + if (checkMOID == g_NoMOID) + return false; + + bool found = false; + MovableObject* pMO = GetMOFromID(checkMOID); + + if (pMO) { + MOID rootMOID = pMO->GetRootID(); + if (checkMOID != g_NoMOID) { + for (std::deque::iterator itr = m_Actors.begin(); !found && itr != m_Actors.end(); ++itr) { + if ((*itr)->GetID() == checkMOID || (*itr)->GetID() == rootMOID) { + found = true; + break; + } + } + // Check actors just added this frame + if (!found) { + for (std::deque::iterator itr = m_AddedActors.begin(); !found && itr != m_AddedActors.end(); ++itr) { + if ((*itr)->GetID() == checkMOID || (*itr)->GetID() == rootMOID) { + found = true; + break; + } + } + } + } + } + return found; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + int MovableMan::GetContiguousActorID(const Actor* actor) const { + auto itr = m_ContiguousActorIDs.find(actor); + if (itr == m_ContiguousActorIDs.end()) { + return -1; + } + + return itr->second; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRootMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Produces the root MOID of the MOID of a potential child MO to another MO. + + MOID MovableMan::GetRootMOID(MOID checkMOID) { + MovableObject* pMO = GetMOFromID(checkMOID); + if (pMO) + return pMO->GetRootID(); + + return g_NoMOID; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveMO + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a MovableObject from the any and all internal lists of MO:s. + // After the MO is removed, ownership is effectively released and + // transferred to whatever client called this method. + + bool MovableMan::RemoveMO(MovableObject* pMOToRem) { + if (pMOToRem) { + if (RemoveActor(pMOToRem)) + return true; + if (RemoveItem(pMOToRem)) + return true; + if (RemoveParticle(pMOToRem)) + return true; + } + + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MovableMan::KillAllTeamActors(int teamToKill) const { + int killCount = 0; + + for (std::deque actorList: {m_Actors, m_AddedActors}) { + for (Actor* actor: actorList) { + if (actor->GetTeam() == teamToKill) { + const AHuman* actorAsHuman = dynamic_cast(actor); + if (actorAsHuman && actorAsHuman->GetHead()) { + actorAsHuman->GetHead()->GibThis(); + } else { + actor->GibThis(); + } + killCount++; + } + } + } + + return killCount; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MovableMan::KillAllEnemyActors(int teamNotToKill) const { + int killCount = 0; + + for (std::deque actorList: {m_Actors, m_AddedActors}) { + for (Actor* actor: actorList) { + if (actor->GetTeam() != teamNotToKill) { + const AHuman* actorAsHuman = dynamic_cast(actor); + if (actorAsHuman && actorAsHuman->GetHead()) { + actorAsHuman->GetHead()->GibThis(); + } else { + actor->GibThis(); + } + killCount++; + } + } + } + + return killCount; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MovableMan::GetAllActors(bool transferOwnership, std::list& actorList, int onlyTeam, bool noBrains) { + int addedCount = 0; + + // Add all regular Actors + for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) { + Actor* actor = *aIt; + // Only grab ones of a specific team; delete all others + if ((onlyTeam == Activity::NoTeam || actor->GetTeam() == onlyTeam) && (!noBrains || !actor->HasObjectInGroup("Brains"))) { + actorList.push_back(actor); + addedCount++; + } else if (transferOwnership) { + delete actor; + } + } + + // Add all Actors added this frame + for (std::deque::iterator aIt = m_AddedActors.begin(); aIt != m_AddedActors.end(); ++aIt) { + Actor* actor = *aIt; + // Only grab ones of a specific team; delete all others + if ((onlyTeam == Activity::NoTeam || actor->GetTeam() == onlyTeam) && (!noBrains || !actor->HasObjectInGroup("Brains"))) { + actorList.push_back(actor); + addedCount++; + } else if (transferOwnership) { + delete actor; + } + } + + if (transferOwnership) { + // Clear the internal Actor lists; we transferred the ownership of them + m_Actors.clear(); + m_AddedActors.clear(); + m_ValidActors.clear(); + + // Also clear the actor rosters + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + m_ActorRoster[team].clear(); + } + } + + return addedCount; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MovableMan::GetAllItems(bool transferOwnership, std::list& itemList) { + int addedCount = 0; + + // Add all regular Items + for (std::deque::iterator iIt = m_Items.begin(); iIt != m_Items.end(); ++iIt) { + itemList.push_back((*iIt)); + addedCount++; + } + + // Add all Items added this frame + for (std::deque::iterator iIt = m_AddedItems.begin(); iIt != m_AddedItems.end(); ++iIt) { + itemList.push_back((*iIt)); + addedCount++; + } + + if (transferOwnership) { + // Clear the internal Item list; we transferred the ownership of them + m_Items.clear(); + m_AddedItems.clear(); + m_ValidItems.clear(); + } + + return addedCount; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MovableMan::GetAllParticles(bool transferOwnership, std::list& particleList) { + int addedCount = 0; + + // Add all regular particles + for (std::deque::iterator iIt = m_Particles.begin(); iIt != m_Particles.end(); ++iIt) { + particleList.push_back((*iIt)); + addedCount++; + } + + // Add all particles added this frame + for (std::deque::iterator iIt = m_AddedParticles.begin(); iIt != m_AddedParticles.end(); ++iIt) { + particleList.push_back((*iIt)); + addedCount++; + } + + if (transferOwnership) { + // Clear the internal Particle list; we transferred the ownership of them + m_Particles.clear(); + m_AddedParticles.clear(); + m_ValidParticles.clear(); + } + + return addedCount; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int MovableMan::GetTeamMOIDCount(int team) const { + if (team > Activity::NoTeam && team < Activity::MaxTeamCount) + return m_TeamMOIDCount[team]; + else + return 0; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void MovableMan::OpenAllDoors(bool open, int team) const { + for (std::deque actorDeque: {m_Actors, m_AddedActors}) { + for (Actor* actor: actorDeque) { + if (ADoor* actorAsADoor = dynamic_cast(actor); actorAsADoor && (team == Activity::NoTeam || actorAsADoor->GetTeam() == team)) { + if (actorAsADoor->GetDoorState() != (open ? ADoor::DoorState::OPEN : ADoor::DoorState::CLOSED)) { + actorAsADoor->Update(); + actorAsADoor->SetClosedByDefault(!open); + } + actorAsADoor->ResetSensorTimer(); + if (open) { + actorAsADoor->OpenDoor(); + } else { + actorAsADoor->CloseDoor(); + } + } + } + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // TODO: Completely tear out and delete this. + // It shouldn't belong to MovableMan, instead it probably ought to be on the pathfinder. On that note, pathfinders shouldn't be part of the scene! + // AIMan? PathingMan? Something like that. Ideally, we completely tear out this hack, and allow for doors in a completely different way. + void MovableMan::OverrideMaterialDoors(bool eraseDoorMaterial, int team) const { + for (std::deque actorDeque: {m_Actors, m_AddedActors}) { + for (Actor* actor: actorDeque) { + if (ADoor* actorAsDoor = dynamic_cast(actor); actorAsDoor && (team == Activity::NoTeam || actorAsDoor->GetTeam() == team)) { + actorAsDoor->TempEraseOrRedrawDoorMaterial(eraseDoorMaterial); + } + } + } + } -Actor * MovableMan::GetClosestBrainActor(int team, const Vector &scenePoint) const -{ - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount || m_ActorRoster[team].empty()) - return 0; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float sqrShortestDistance = std::numeric_limits::infinity(); - sqrShortestDistance *= sqrShortestDistance; + void MovableMan::RegisterAlarmEvent(const AlarmEvent& newEvent) { + std::lock_guard lock(m_AddedAlarmEventsMutex); + m_AddedAlarmEvents.push_back(newEvent); + } - Actor *pClosestBrain = 0; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RedrawOverlappingMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces all objects potnetially overlapping a specific MO to re-draw + // this MOID representations onto the MOID bitmap. - for (std::list::const_iterator aIt = m_ActorRoster[team].begin(); aIt != m_ActorRoster[team].end(); ++aIt) - { - if (!(*aIt)->HasObjectInGroup("Brains")) - continue; + void MovableMan::RedrawOverlappingMOIDs(MovableObject* pOverlapsThis) { + for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) { + (*aIt)->DrawMOIDIfOverlapping(pOverlapsThis); + } - // Check if closer than best so far - float sqrDistance = g_SceneMan.ShortestDistance((*aIt)->GetPos(), scenePoint, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()).GetSqrMagnitude(); - if (sqrDistance < sqrShortestDistance) - { - sqrShortestDistance = sqrDistance; - pClosestBrain = *aIt; - } - } + for (std::deque::iterator iIt = m_Items.begin(); iIt != m_Items.end(); ++iIt) { + (*iIt)->DrawMOIDIfOverlapping(pOverlapsThis); + } - return pClosestBrain; -} + for (std::deque::iterator parIt = m_Particles.begin(); parIt != m_Particles.end(); ++parIt) { + (*parIt)->DrawMOIDIfOverlapping(pOverlapsThis); + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetClosestOtherBrainActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the brain actor NOT of a specific team that is closest -// to a scene point. + void callLuaFunctionOnMORecursive(MovableObject* mo, const std::string& functionName, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { + if (MOSRotating* mosr = dynamic_cast(mo)) { + for (auto attachablrItr = mosr->GetAttachableList().begin(); attachablrItr != mosr->GetAttachableList().end();) { + Attachable* attachable = *attachablrItr; + ++attachablrItr; -Actor * MovableMan::GetClosestOtherBrainActor(int notOfTeam, const Vector &scenePoint) const -{ - if (notOfTeam < Activity::TeamOne || notOfTeam >= Activity::MaxTeamCount || m_Actors.empty()) - return 0; - - float sqrShortestDistance = std::numeric_limits::infinity(); - sqrShortestDistance *= sqrShortestDistance; - - Actor *pClosestBrain = 0; - Actor *pContenderBrain = 0; - - for (int t = Activity::TeamOne; t < g_ActivityMan.GetActivity()->GetTeamCount(); ++t) - { - if (t != notOfTeam) - { - pContenderBrain = GetClosestBrainActor(t, scenePoint); - float sqrDistance = (pContenderBrain->GetPos() - scenePoint).GetSqrMagnitude(); - if (sqrDistance < sqrShortestDistance) - { - sqrShortestDistance = sqrDistance; - pClosestBrain = pContenderBrain; - } - } - } - return pClosestBrain; -} + attachable->RunScriptedFunctionInAppropriateScripts(functionName, false, false, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + callLuaFunctionOnMORecursive(attachable, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + } + for (auto woundItr = mosr->GetWoundList().begin(); woundItr != mosr->GetWoundList().end();) { + AEmitter* wound = *woundItr; + ++woundItr; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetUnassignedBrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the brain actor of a specific team. -// Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. + wound->RunScriptedFunctionInAppropriateScripts(functionName, false, false, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + callLuaFunctionOnMORecursive(wound, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + } + } -Actor * MovableMan::GetUnassignedBrain(int team) const -{ - if (/*m_Actors.empty() || */m_ActorRoster[team].empty()) - return 0; - - for (std::list::const_iterator aIt = m_ActorRoster[team].begin(); aIt != m_ActorRoster[team].end(); ++aIt) - { - if ((*aIt)->HasObjectInGroup("Brains") && !g_ActivityMan.GetActivity()->IsAssignedBrain(*aIt)) - return *aIt; - } - - // Also need to look through all the actors added this frame, one might be a brain. - int actorTeam = Activity::NoTeam; - for (std::deque::const_iterator aaIt = m_AddedActors.begin(); aaIt != m_AddedActors.end(); ++aaIt) - { - int actorTeam = (*aaIt)->GetTeam(); - // Accept no-team brains too - ACTUALLY, DON'T - if ((actorTeam == team/* || actorTeam == Activity::NoTeam*/) && (*aaIt)->HasObjectInGroup("Brains") && !g_ActivityMan.GetActivity()->IsAssignedBrain(*aaIt)) - return *aaIt; - } - - return 0; -} + mo->RunScriptedFunctionInAppropriateScripts(functionName, false, false, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + }; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool MovableMan::AddMO(MovableObject *movableObjectToAdd) { - if (!movableObjectToAdd) { - return false; - } + void MovableMan::RunLuaFunctionOnAllMOs(const std::string& functionName, bool includeAdded, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { + if (includeAdded) { + for (Actor* actor: m_AddedActors) { + callLuaFunctionOnMORecursive(actor, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + } - if (Actor *actorToAdd = dynamic_cast(movableObjectToAdd)) { - AddActor(actorToAdd); - return true; - } else if (HeldDevice *heldDeviceToAdd = dynamic_cast(movableObjectToAdd)) { - AddItem(heldDeviceToAdd); - return true; - } - AddParticle(movableObjectToAdd); + for (MovableObject* item: m_AddedItems) { + callLuaFunctionOnMORecursive(item, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + } - return true; -} + for (MovableObject* particle: m_AddedParticles) { + callLuaFunctionOnMORecursive(particle, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + for (Actor* actor: m_Actors) { + callLuaFunctionOnMORecursive(actor, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + } -void MovableMan::AddActor(Actor *actorToAdd) { - if (actorToAdd) { - actorToAdd->SetAsAddedToMovableMan(); - actorToAdd->CorrectAttachableAndWoundPositionsAndRotations(); + for (MovableObject* item: m_Items) { + callLuaFunctionOnMORecursive(item, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + } - if (actorToAdd->IsTooFast()) { - actorToAdd->SetToDelete(true); - } else { - if (!dynamic_cast(actorToAdd)) { actorToAdd->MoveOutOfTerrain(g_MaterialGrass); } - if (actorToAdd->IsStatus(Actor::INACTIVE)) { actorToAdd->SetStatus(Actor::STABLE); } - actorToAdd->NotResting(); - actorToAdd->NewFrame(); - actorToAdd->SetAge(g_TimerMan.GetDeltaTimeMS() * -1.0f); - } - - { - std::lock_guard lock(m_AddedActorsMutex); - m_AddedActors.push_back(actorToAdd); - m_ValidActors.insert(actorToAdd); - - // This will call SetTeam and subsequently force the team as active. - AddActorToTeamRoster(actorToAdd); - } - } -} + for (MovableObject* particle: m_Particles) { + callLuaFunctionOnMORecursive(particle, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MovableMan::AddItem(HeldDevice *itemToAdd) { - if (itemToAdd) { - g_ActivityMan.GetActivity()->ForceSetTeamAsActive(itemToAdd->GetTeam()); - itemToAdd->SetAsAddedToMovableMan(); - itemToAdd->CorrectAttachableAndWoundPositionsAndRotations(); - - if (itemToAdd->IsTooFast()) { - itemToAdd->SetToDelete(true); - } else { - if (!itemToAdd->IsSetToDelete()) { itemToAdd->MoveOutOfTerrain(g_MaterialGrass); } - itemToAdd->NotResting(); - itemToAdd->NewFrame(); - itemToAdd->SetAge(g_TimerMan.GetDeltaTimeMS() * -1.0f); - } - - std::lock_guard lock(m_AddedItemsMutex); - m_AddedItems.push_back(itemToAdd); - m_ValidItems.insert(itemToAdd); - } -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void reloadLuaFunctionsOnMORecursive(MovableObject* mo) { + if (MOSRotating* mosr = dynamic_cast(mo)) { + for (auto attachablrItr = mosr->GetAttachableList().begin(); attachablrItr != mosr->GetAttachableList().end();) { + Attachable* attachable = *attachablrItr; + ++attachablrItr; -void MovableMan::AddParticle(MovableObject *particleToAdd){ - if (particleToAdd) { - g_ActivityMan.GetActivity()->ForceSetTeamAsActive(particleToAdd->GetTeam()); - particleToAdd->SetAsAddedToMovableMan(); - if (MOSRotating *particleToAddAsMOSRotating = dynamic_cast(particleToAdd)) { particleToAddAsMOSRotating->CorrectAttachableAndWoundPositionsAndRotations(); } + attachable->ReloadScripts(); + reloadLuaFunctionsOnMORecursive(attachable); + } - if (particleToAdd->IsTooFast()) { - particleToAdd->SetToDelete(true); - } else { - //TODO consider moving particles out of grass. It's old code that was removed because it's slow to do this for every particle. - particleToAdd->NotResting(); - particleToAdd->NewFrame(); - particleToAdd->SetAge(g_TimerMan.GetDeltaTimeMS() * -1.0f); - } - if (particleToAdd->IsDevice()) { - std::lock_guard lock(m_AddedItemsMutex); - m_AddedItems.push_back(particleToAdd); - m_ValidItems.insert(particleToAdd); - } else { - std::lock_guard lock(m_AddedParticlesMutex); - m_AddedParticles.push_back(particleToAdd); - m_ValidParticles.insert(particleToAdd); - } - } -} + for (auto woundItr = mosr->GetWoundList().begin(); woundItr != mosr->GetWoundList().end();) { + AEmitter* wound = *woundItr; + ++woundItr; + wound->ReloadScripts(); + reloadLuaFunctionsOnMORecursive(wound); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes an Actor from the internal list of MO:s. After the Actor is -// removed, ownership is effectively released and transferred to whatever -// client called this method. + mo->ReloadScripts(); + }; -Actor * MovableMan::RemoveActor(MovableObject *pActorToRem) -{ - Actor *removed = nullptr; - - if (pActorToRem) - { - for (std::deque::iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) - { - if (*itr == pActorToRem) - { - std::lock_guard lock(m_ActorsMutex); - removed = *itr; - m_ValidActors.erase(*itr); - m_Actors.erase(itr); - break; - } - } - // Try the newly added actors if we couldn't find it in the regular deque - if (!removed) - { - for (std::deque::iterator itr = m_AddedActors.begin(); itr != m_AddedActors.end(); ++itr) - { - if (*itr == pActorToRem) - { - std::lock_guard lock(m_AddedActorsMutex); - removed = *itr; - m_ValidActors.erase(*itr); - m_AddedActors.erase(itr); - break; - } - } - } - RemoveActorFromTeamRoster(dynamic_cast(pActorToRem)); - pActorToRem->SetAsAddedToMovableMan(false); - } - return removed; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void MovableMan::ReloadLuaScripts() { + for (Actor* actor: m_AddedActors) { + reloadLuaFunctionsOnMORecursive(actor); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a pickup-able MovableObject item from the internal list of -// MO:s. After the item is removed, ownership is effectively released and -// transferred to whatever client called this method. + for (MovableObject* item: m_AddedItems) { + reloadLuaFunctionsOnMORecursive(item); + } -MovableObject * MovableMan::RemoveItem(MovableObject *pItemToRem) -{ - MovableObject *removed = nullptr; - - if (pItemToRem) - { - for (std::deque::iterator itr = m_Items.begin(); itr != m_Items.end(); ++itr) - { - if (*itr == pItemToRem) - { - std::lock_guard lock(m_ItemsMutex); - removed = *itr; - m_ValidItems.erase(*itr); - m_Items.erase(itr); - break; - } - } - // Try the newly added items if we couldn't find it in the regular deque - if (!removed) - { - for (std::deque::iterator itr = m_AddedItems.begin(); itr != m_AddedItems.end(); ++itr) - { - if (*itr == pItemToRem) - { - std::lock_guard lock(m_AddedItemsMutex); - removed = *itr; - m_ValidItems.erase(*itr); - m_AddedItems.erase(itr); - break; - } - } - } - pItemToRem->SetAsAddedToMovableMan(false); - } - return removed; -} + for (MovableObject* particle: m_AddedParticles) { + reloadLuaFunctionsOnMORecursive(particle); + } + for (Actor* actor: m_Actors) { + reloadLuaFunctionsOnMORecursive(actor); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveParticle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a MovableObject from the internal list of MO:s. After the -// MO is removed, ownership is effectively released and transferred to -// whatever client called this method. + for (MovableObject* item: m_Items) { + reloadLuaFunctionsOnMORecursive(item); + } -MovableObject * MovableMan::RemoveParticle(MovableObject *pMOToRem) -{ - MovableObject *removed = nullptr; - - if (pMOToRem) - { - for (std::deque::iterator itr = m_Particles.begin(); itr != m_Particles.end(); ++itr) - { - if (*itr == pMOToRem) - { - std::lock_guard lock(m_ParticlesMutex); - removed = *itr; - m_ValidParticles.erase(*itr); - m_Particles.erase(itr); - break; - } - } - // Try the newly added particles if we couldn't find it in the regular deque - if (!removed) - { - for (std::deque::iterator itr = m_AddedParticles.begin(); itr != m_AddedParticles.end(); ++itr) - { - if (*itr == pMOToRem) - { - std::lock_guard lock(m_AddedParticlesMutex); - removed = *itr; - m_ValidParticles.erase(*itr); - m_AddedParticles.erase(itr); - break; - } - } - } - pMOToRem->SetAsAddedToMovableMan(false); - } - return removed; -} + for (MovableObject* particle: m_Particles) { + reloadLuaFunctionsOnMORecursive(particle); + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this MovableMan. Supposed to be done every frame. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddActorToTeamRoster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds actor to internal team roster -// Arguments: Pointer to actor -// Return value: None. + void MovableMan::Update() { + ZoneScoped; -void MovableMan::AddActorToTeamRoster(Actor * pActorToAdd) -{ - if (!pActorToAdd) { - return; - } - - // Add to the team roster and then sort it too - int team = pActorToAdd->GetTeam(); - // Also re-set the TEam so that the Team Icons get set up properly - pActorToAdd->SetTeam(team); - // Only add to a roster if it's on a team AND is controllable (eg doors are not) - if (team >= Activity::TeamOne && team < Activity::MaxTeamCount && pActorToAdd->IsControllable()) - { - std::lock_guard lock(m_ActorRosterMutex); - m_ActorRoster[pActorToAdd->GetTeam()].push_back(pActorToAdd); - m_ActorRoster[pActorToAdd->GetTeam()].sort(MOXPosComparison()); - } -} + // Don't update if paused + if (g_ActivityMan.GetActivity() && g_ActivityMan.ActivityPaused()) { + return; + } + m_SimUpdateFrameNumber++; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveActorToTeamRoster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes actor from internal team roster -// Arguments: Pointer to actor -// Return value: None. + // ---TEMP --- + // These are here for multithreaded AI, but will be unnecessary when multithreaded-sim-and-render is in! + // Clear the MO color layer only if this is a drawn update + if (g_TimerMan.DrawnSimUpdate()) { + g_SceneMan.ClearMOColorLayer(); + } -void MovableMan::RemoveActorFromTeamRoster(Actor * pActorToRem) -{ - if (!pActorToRem) { - return; - } + // If this is the first sim update since a drawn one, then clear the post effects + if (g_TimerMan.SimUpdatesSinceDrawn() == 0) { + g_PostProcessMan.ClearScenePostEffects(); + } + // ---TEMP--- + + // Reset the draw HUD roster line settings + m_SortTeamRoster[Activity::TeamOne] = false; + m_SortTeamRoster[Activity::TeamTwo] = false; + m_SortTeamRoster[Activity::TeamThree] = false; + m_SortTeamRoster[Activity::TeamFour] = false; + + // Move all last frame's alarm events into the proper buffer, and clear out the new one to fill up with this frame's + m_AlarmEvents.clear(); + for (std::vector::iterator aeItr = m_AddedAlarmEvents.begin(); aeItr != m_AddedAlarmEvents.end(); ++aeItr) { + m_AlarmEvents.push_back(*aeItr); + } + m_AddedAlarmEvents.clear(); - int team = pActorToRem->GetTeam(); + // Travel MOs + Travel(); - // Remove from roster as well - if (team >= Activity::TeamOne && team < Activity::MaxTeamCount) { - std::lock_guard lock(m_ActorRosterMutex); - m_ActorRoster[team].remove(pActorToRem); - } -} + // If our debug settings switch is forcing all pathing requests to immediately complete, make sure they're done here + if (g_SettingsMan.GetForceImmediatePathingRequestCompletion() && g_SceneMan.GetScene()) { + g_SceneMan.GetScene()->BlockUntilAllPathingRequestsComplete(); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeActorTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes actor team and updates team rosters. + // Prior to controller/AI update, execute lua callbacks + g_LuaMan.ExecuteLuaScriptCallbacks(); -void MovableMan::ChangeActorTeam(Actor * pActor, int team) -{ - if (!pActor) { - return; - } - - if (pActor->IsPlayerControlled()) { g_ActivityMan.GetActivity()->LoseControlOfActor(pActor->GetController()->GetPlayer()); } - - RemoveActorFromTeamRoster(pActor); - pActor->SetTeam(team); - AddActorToTeamRoster(pActor); - - // Because doors affect the team-based pathfinders, we need to tell them there's been a change. - // This is hackily done by erasing the door material, updating the pathfinders, then redrawing it and updating them again so they properly account for the door's new team. - if (ADoor *actorAsADoor = dynamic_cast(pActor); actorAsADoor && actorAsADoor->GetDoorMaterialDrawn()) { - actorAsADoor->TempEraseOrRedrawDoorMaterial(true); - g_SceneMan.GetTerrain()->AddUpdatedMaterialArea(actorAsADoor->GetBoundingBox()); - g_SceneMan.GetScene()->UpdatePathFinding(); - actorAsADoor->TempEraseOrRedrawDoorMaterial(false); - g_SceneMan.GetTerrain()->AddUpdatedMaterialArea(actorAsADoor->GetBoundingBox()); - g_SceneMan.GetScene()->UpdatePathFinding(); - } -} + // Updates everything needed prior to AI/user input being processed + // Fugly hack to keep backwards compat with scripts that rely on weird frame-delay-ordering behaviours + // TODO, cleanup the pre-controller update and post-controller updates to have some consistent logic of what goes where + PreControllerUpdate(); + // Updates AI/user input + UpdateControllers(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ValidateMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through and checks that all MOID's have valid MO pointers -// associated with them. This shuold only be used for testing, as it will -// crash the app if validation fails. + // Will use some common iterators + std::deque::iterator aIt; + std::deque::iterator amidIt; + std::deque::iterator iIt; + std::deque::iterator imidIt; + std::deque::iterator parIt; + std::deque::iterator midIt; -bool MovableMan::ValidateMOIDs() { -#ifdef DEBUG_BUILD - for (const MovableObject *mo : m_MOIDIndex) { - RTEAssert(mo, "Null MO found!"); - } -#endif - return true; -} + // Update all multithreaded scripts for all objects + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ScriptsUpdate); + { + ZoneScopedN("Multithreaded Scripts Update"); + const std::string threadedUpdate = "ThreadedUpdate"; // avoid string reconstruction -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ValidMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MovableObject pointer points to an -// MO that's currently active in the simulation, and kept by this MovableMan. + LuaStatesArray& luaStates = g_LuaMan.GetThreadedScriptStates(); + g_ThreadMan.GetPriorityThreadPool().parallelize_loop(luaStates.size(), + [&](int start, int end) { + RTEAssert(start + 1 == end, "Threaded script state being updated across multiple threads!"); + LuaStateWrapper& luaState = luaStates[start]; + g_LuaMan.SetThreadLuaStateOverride(&luaState); -bool MovableMan::ValidMO(const MovableObject *pMOToCheck) { - bool exists = m_ValidActors.find(pMOToCheck) != m_ValidActors.end() || - m_ValidItems.find(pMOToCheck) != m_ValidItems.end() || - m_ValidParticles.find(pMOToCheck) != m_ValidParticles.end(); + for (MovableObject* mo: luaState.GetRegisteredMOs()) { + mo->RunScriptedFunctionInAppropriateScripts(threadedUpdate, false, false, {}, {}, {}); + } - return pMOToCheck && exists; -} + g_LuaMan.SetThreadLuaStateOverride(nullptr); + }) + .wait(); + } + { + ZoneScopedN("Multithreaded Scripts SyncedUpdate"); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MovableObject is an active Actor kept -// by this MovableMan or not. + const std::string syncedUpdate = "SyncedUpdate"; // avoid string reconstruction -bool MovableMan::IsActor(const MovableObject *pMOToCheck) -{ - return pMOToCheck && m_ValidActors.find(pMOToCheck) != m_ValidActors.end(); -} + for (LuaStateWrapper& luaState: g_LuaMan.GetThreadedScriptStates()) { + g_LuaMan.SetThreadLuaStateOverride(&luaState); + for (MovableObject* mo: luaState.GetRegisteredMOs()) { + if (mo->HasRequestedSyncedUpdate()) { + mo->RunScriptedFunctionInAppropriateScripts(syncedUpdate, false, false, {}, {}, {}); + mo->ResetRequestedSyncedUpdateFlag(); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MovableObject is an active Item kept -// by this MovableMan or not. + g_LuaMan.SetThreadLuaStateOverride(nullptr); + } + } + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ScriptsUpdate); -bool MovableMan::IsDevice(const MovableObject *pMOToCheck) -{ - return pMOToCheck && m_ValidItems.find(pMOToCheck) != m_ValidItems.end(); -} + { + {ZoneScopedN("Actors Update"); + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActorsUpdate); + for (Actor* actor: m_Actors) { + actor->Update(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsParticle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MovableObject is an active Item kept -// by this MovableMan or not. + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ScriptsUpdate); + actor->UpdateScripts(); + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ScriptsUpdate); -bool MovableMan::IsParticle(const MovableObject *pMOToCheck) -{ - return pMOToCheck && m_ValidParticles.find(pMOToCheck) != m_ValidParticles.end(); -} + actor->ApplyImpulses(); + } + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActorsUpdate); + } + { + ZoneScopedN("Items Update"); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsOfActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MOID is that of an MO which either is -// or is parented to an active Actor by this MovableMan, or not. + int count = 0; + int itemLimit = m_Items.size() - m_MaxDroppedItems; + for (iIt = m_Items.begin(); iIt != m_Items.end(); ++iIt, ++count) { + (*iIt)->Update(); -bool MovableMan::IsOfActor(MOID checkMOID) -{ - if (checkMOID == g_NoMOID) - return false; - - bool found = false; - MovableObject *pMO = GetMOFromID(checkMOID); - - if (pMO) - { - MOID rootMOID = pMO->GetRootID(); - if (checkMOID != g_NoMOID) - { - for (std::deque::iterator itr = m_Actors.begin(); !found && itr != m_Actors.end(); ++itr) - { - if ((*itr)->GetID() == checkMOID || (*itr)->GetID() == rootMOID) - { - found = true; - break; - } - } - // Check actors just added this frame - if (!found) - { - for (std::deque::iterator itr = m_AddedActors.begin(); !found && itr != m_AddedActors.end(); ++itr) - { - if ((*itr)->GetID() == checkMOID || (*itr)->GetID() == rootMOID) - { - found = true; - break; - } - } - } - } - } - return found; -} + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ScriptsUpdate); + (*iIt)->UpdateScripts(); + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ScriptsUpdate); -////////////////////////////////////////////////////////////////////////////////////////// + (*iIt)->ApplyImpulses(); + if (count <= itemLimit) { + (*iIt)->SetToSettle(true); + } + } + } -int MovableMan::GetContiguousActorID(const Actor *actor) const { - auto itr = m_ContiguousActorIDs.find(actor); - if (itr == m_ContiguousActorIDs.end()) { - return -1; - } + { + ZoneScopedN("Particles Update"); - return itr->second; -} + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ParticlesUpdate); + for (MovableObject* particle: m_Particles) { + particle->Update(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRootMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Produces the root MOID of the MOID of a potential child MO to another MO. + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ScriptsUpdate); + particle->UpdateScripts(); + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ScriptsUpdate); -MOID MovableMan::GetRootMOID(MOID checkMOID) -{ - MovableObject *pMO = GetMOFromID(checkMOID); - if (pMO) - return pMO->GetRootID(); + particle->ApplyImpulses(); + particle->RestDetection(); + // Copy particles that are at rest to the terrain and mark them for deletion. + if (particle->IsAtRest()) { + particle->SetToSettle(true); + } + } + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ParticlesUpdate); + } - return g_NoMOID; -} + { + ZoneScopedN("Post Update"); + for (Actor* actor: m_Actors) { + actor->PostUpdate(); + } + for (MovableObject* item: m_Items) { + item->PostUpdate(); + } + for (MovableObject* particle: m_Particles) { + particle->PostUpdate(); + } + } +} // namespace RTE -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a MovableObject from the any and all internal lists of MO:s. -// After the MO is removed, ownership is effectively released and -// transferred to whatever client called this method. +////////////////////////////////////////////////////////////////////// +// TRANSFER ALL MOs ADDED THIS FRAME +// All Actors, Items, and Particles added this frame now are officially added -bool MovableMan::RemoveMO(MovableObject *pMOToRem) { - if (pMOToRem) - { - if (RemoveActor(pMOToRem)) - return true; - if (RemoveItem(pMOToRem)) - return true; - if (RemoveParticle(pMOToRem)) - return true; - } - - return false; -} + ZoneScopedN("MO Transfer and Deletion"); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int MovableMan::KillAllTeamActors(int teamToKill) const { - int killCount = 0; - - for (std::deque actorList : { m_Actors, m_AddedActors }) { - for (Actor *actor : actorList) { - if (actor->GetTeam() == teamToKill) { - const AHuman *actorAsHuman = dynamic_cast(actor); - if (actorAsHuman && actorAsHuman->GetHead()) { - actorAsHuman->GetHead()->GibThis(); - } else { - actor->GibThis(); - } - killCount++; - } - } - } - - return killCount; -} + { + // Actors + for (aIt = m_AddedActors.begin(); aIt != m_AddedActors.end(); ++aIt) { + // Delete instead if it's marked for it + if (!(*aIt)->IsSetToDelete()) + m_Actors.push_back(*aIt); + else { + m_ValidActors.erase(*aIt); + + // Also remove actor from the roster + if ((*aIt)->GetTeam() >= 0) { + // m_ActorRoster[(*aIt)->GetTeam()].remove(*aIt); + RemoveActorFromTeamRoster(*aIt); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int MovableMan::KillAllEnemyActors(int teamNotToKill) const { - int killCount = 0; - - for (std::deque actorList : { m_Actors, m_AddedActors }) { - for (Actor *actor : actorList) { - if (actor->GetTeam() != teamNotToKill) { - const AHuman *actorAsHuman = dynamic_cast(actor); - if (actorAsHuman && actorAsHuman->GetHead()) { - actorAsHuman->GetHead()->GibThis(); - } else { - actor->GibThis(); - } - killCount++; - } - } - } - - return killCount; -} + (*aIt)->DestroyScriptState(); + delete (*aIt); + } + } + m_AddedActors.clear(); + + // Items + for (iIt = m_AddedItems.begin(); iIt != m_AddedItems.end(); ++iIt) { + // Delete instead if it's marked for it + if (!(*iIt)->IsSetToDelete()) { + m_Items.push_back(*iIt); + } else { + m_ValidItems.erase(*iIt); + (*iIt)->DestroyScriptState(); + delete (*iIt); + } + } + m_AddedItems.clear(); + + // Particles + for (parIt = m_AddedParticles.begin(); parIt != m_AddedParticles.end(); ++parIt) { + // Delete instead if it's marked for it + if (!(*parIt)->IsSetToDelete()) { + m_Particles.push_back(*parIt); + } else { + m_ValidParticles.erase(*parIt); + (*parIt)->DestroyScriptState(); + delete (*parIt); + } + } + m_AddedParticles.clear(); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// + // Copy (Settle) Pass -int MovableMan::GetAllActors(bool transferOwnership, std::list &actorList, int onlyTeam, bool noBrains) -{ - int addedCount = 0; - - // Add all regular Actors - for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) - { - Actor *actor = *aIt; - // Only grab ones of a specific team; delete all others - if ((onlyTeam == Activity::NoTeam || actor->GetTeam() == onlyTeam) && (!noBrains || !actor->HasObjectInGroup("Brains"))) - { - actorList.push_back(actor); - addedCount++; - } - else if (transferOwnership) - { - delete actor; - } - } - - // Add all Actors added this frame - for (std::deque::iterator aIt = m_AddedActors.begin(); aIt != m_AddedActors.end(); ++aIt) - { - Actor *actor = *aIt; - // Only grab ones of a specific team; delete all others - if ((onlyTeam == Activity::NoTeam || actor->GetTeam() == onlyTeam) && (!noBrains || !actor->HasObjectInGroup("Brains"))) - { - actorList.push_back(actor); - addedCount++; - } - else if (transferOwnership) - { - delete actor; - } - } - - if (transferOwnership) - { - // Clear the internal Actor lists; we transferred the ownership of them - m_Actors.clear(); - m_AddedActors.clear(); - m_ValidActors.clear(); - - // Also clear the actor rosters - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - m_ActorRoster[team].clear(); - } - } - - return addedCount; -} + { + // DEATH ////////////////////////////////////////////////////////// + // Transfer dead actors from Actor list to particle list + aIt = partition(m_Actors.begin(), m_Actors.end(), std::not_fn(std::mem_fn(&Actor::IsDead))); + amidIt = aIt; + + // Move dead Actor to particles list + if (amidIt != m_Actors.end() /* && m_Actors.size() > 1*/) { + while (aIt != m_Actors.end()) { + // Report the death of the actor to the game + g_ActivityMan.GetActivity()->ReportDeath((*aIt)->GetTeam()); + + // Add to the particles list + m_Particles.push_back(*aIt); + m_ValidParticles.insert(*aIt); + // Remove from the team roster + + if ((*aIt)->GetTeam() >= 0) { + // m_ActorRoster[(*aIt)->GetTeam()].remove(*aIt); + RemoveActorFromTeamRoster(*aIt); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + m_ValidActors.erase(*aIt); + aIt++; + } + // Try to set the existing iterator to a safer value, erase can crash in debug mode otherwise? + aIt = m_Actors.begin(); + m_Actors.erase(amidIt, m_Actors.end()); + } -int MovableMan::GetAllItems(bool transferOwnership, std::list &itemList) -{ - int addedCount = 0; - - // Add all regular Items - for (std::deque::iterator iIt = m_Items.begin(); iIt != m_Items.end(); ++iIt) - { - itemList.push_back((*iIt)); - addedCount++; - } - - // Add all Items added this frame - for (std::deque::iterator iIt = m_AddedItems.begin(); iIt != m_AddedItems.end(); ++iIt) - { - itemList.push_back((*iIt)); - addedCount++; - } - - if (transferOwnership) - { - // Clear the internal Item list; we transferred the ownership of them - m_Items.clear(); - m_AddedItems.clear(); - m_ValidItems.clear(); - } - - return addedCount; -} + // ITEM SETTLE ////////////////////////////////////////////////////////// + // Transfer excess items to particle list - use stable partition, item orde is important + iIt = stable_partition(m_Items.begin(), m_Items.end(), std::not_fn(std::mem_fn(&MovableObject::ToSettle))); + imidIt = iIt; + + // Move force-settled items to particles list + if (imidIt != m_Items.end() /* && m_Items.size() > 1*/) { + while (iIt != m_Items.end()) { + (*iIt)->SetToSettle(false); + // Disable TDExplosive's immunity to settling + if ((*iIt)->GetRestThreshold() < 0) { + (*iIt)->SetRestThreshold(500); + } + m_ValidItems.erase(*iIt); + m_Particles.push_back(*iIt); + iIt++; + } + m_Items.erase(imidIt, m_Items.end()); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // DELETE ////////////////////////////////////////////////////////// + // Only delete after all travels & updates are done + // Actors + aIt = partition(m_Actors.begin(), m_Actors.end(), std::not_fn(std::mem_fn(&MovableObject::ToDelete))); + amidIt = aIt; -int MovableMan::GetAllParticles(bool transferOwnership, std::list &particleList) -{ - int addedCount = 0; - - // Add all regular particles - for (std::deque::iterator iIt = m_Particles.begin(); iIt != m_Particles.end(); ++iIt) - { - particleList.push_back((*iIt)); - addedCount++; - } - - // Add all particles added this frame - for (std::deque::iterator iIt = m_AddedParticles.begin(); iIt != m_AddedParticles.end(); ++iIt) - { - particleList.push_back((*iIt)); - addedCount++; - } - - if (transferOwnership) - { - // Clear the internal Particle list; we transferred the ownership of them - m_Particles.clear(); - m_AddedParticles.clear(); - m_ValidParticles.clear(); - } - - return addedCount; -} + while (aIt != m_Actors.end()) { + // Set brain to 0 to avoid crashes due to brain deletion + Activity* pActivity = g_ActivityMan.GetActivity(); + if (pActivity) { + if (pActivity->IsAssignedBrain(*aIt)) + pActivity->SetPlayerBrain(0, pActivity->IsBrainOfWhichPlayer(*aIt)); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + pActivity->ReportDeath((*aIt)->GetTeam()); + } -int MovableMan::GetTeamMOIDCount(int team) const -{ - if (team > Activity::NoTeam && team < Activity::MaxTeamCount) - return m_TeamMOIDCount[team]; - else - return 0; -} + // Remove from team rosters + if ((*aIt)->GetTeam() >= Activity::TeamOne && (*aIt)->GetTeam() < Activity::MaxTeamCount) + // m_ActorRoster[(*aIt)->GetTeam()].remove(*aIt); + RemoveActorFromTeamRoster(*aIt); + + // Delete + m_ValidActors.erase(*aIt); + (*aIt)->DestroyScriptState(); + delete (*aIt); + aIt++; + } + // Try to set the existing iterator to a safer value, erase can crash in debug mode otherwise? + aIt = m_Actors.begin(); + m_Actors.erase(amidIt, m_Actors.end()); + + // Items + iIt = stable_partition(m_Items.begin(), m_Items.end(), std::not_fn(std::mem_fn(&MovableObject::ToDelete))); + imidIt = iIt; + + while (iIt != m_Items.end()) { + m_ValidItems.erase(*iIt); + (*iIt)->DestroyScriptState(); + delete (*iIt); + iIt++; + } + m_Items.erase(imidIt, m_Items.end()); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Particles + parIt = partition(m_Particles.begin(), m_Particles.end(), std::not_fn(std::mem_fn(&MovableObject::ToDelete))); + midIt = parIt; -void MovableMan::OpenAllDoors(bool open, int team) const { - for (std::deque actorDeque : { m_Actors, m_AddedActors }) { - for (Actor *actor : actorDeque) { - if (ADoor *actorAsADoor = dynamic_cast(actor); actorAsADoor && (team == Activity::NoTeam || actorAsADoor->GetTeam() == team)) { - if (actorAsADoor->GetDoorState() != (open ? ADoor::DoorState::OPEN : ADoor::DoorState::CLOSED)) { - actorAsADoor->Update(); - actorAsADoor->SetClosedByDefault(!open); - } - actorAsADoor->ResetSensorTimer(); - if (open) { - actorAsADoor->OpenDoor(); - } else { - actorAsADoor->CloseDoor(); - } - } + while (parIt != m_Particles.end()) { + m_ValidParticles.erase(*parIt); + (*parIt)->DestroyScriptState(); + delete (*parIt); + parIt++; } + m_Particles.erase(midIt, m_Particles.end()); } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// TODO: Completely tear out and delete this. -// It shouldn't belong to MovableMan, instead it probably ought to be on the pathfinder. On that note, pathfinders shouldn't be part of the scene! -// AIMan? PathingMan? Something like that. Ideally, we completely tear out this hack, and allow for doors in a completely different way. -void MovableMan::OverrideMaterialDoors(bool eraseDoorMaterial, int team) const { - for (std::deque actorDeque : { m_Actors, m_AddedActors }) { - for (Actor *actor : actorDeque) { - if (ADoor *actorAsDoor = dynamic_cast(actor); actorAsDoor && (team == Activity::NoTeam || actorAsDoor->GetTeam() == team)) { - actorAsDoor->TempEraseOrRedrawDoorMaterial(eraseDoorMaterial); + // SETTLE PARTICLES ////////////////////////////////////////////////// + // Only settle after all updates and deletions are done + if (m_SettlingEnabled) { + parIt = partition(m_Particles.begin(), m_Particles.end(), std::not_fn(std::mem_fn(&MovableObject::ToSettle))); + midIt = parIt; + + while (parIt != m_Particles.end()) { + Vector parPos((*parIt)->GetPos()); + Material const* terrMat = g_SceneMan.GetMaterialFromID(g_SceneMan.GetTerrain()->GetMaterialPixel(parPos.GetFloorIntX(), parPos.GetFloorIntY())); + int piling = (*parIt)->GetMaterial()->GetPiling(); + if (piling > 0) { + for (int s = 0; s < piling && (terrMat->GetIndex() == (*parIt)->GetMaterial()->GetIndex() || terrMat->GetIndex() == (*parIt)->GetMaterial()->GetSettleMaterial()); ++s) { + if ((piling - s) % 2 == 0) { + parPos.m_Y -= 1.0F; + } else { + parPos.m_X += (RandomNum() >= 0.5F ? 1.0F : -1.0F); + } + terrMat = g_SceneMan.GetMaterialFromID(g_SceneMan.GetTerrain()->GetMaterialPixel(parPos.GetFloorIntX(), parPos.GetFloorIntY())); + } + (*parIt)->SetPos(parPos.GetFloored()); + } + if ((*parIt)->GetDrawPriority() >= terrMat->GetPriority()) { + (*parIt)->DrawToTerrain(g_SceneMan.GetTerrain()); } + m_ValidParticles.erase(*parIt); + (*parIt)->DestroyScriptState(); + delete (*parIt); + parIt++; } + m_Particles.erase(midIt, m_Particles.end()); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// We've finished stuff that can interact with lua script, so it's the ideal time to start a gc run +g_LuaMan.StartAsyncGarbageCollection(); -void MovableMan::RegisterAlarmEvent(const AlarmEvent &newEvent) -{ - std::lock_guard lock(m_AddedAlarmEventsMutex); - m_AddedAlarmEvents.push_back(newEvent); -} +//////////////////////////////////////////////////////////////////////// +// Draw the MO matter and IDs to their layers for next frame +m_DrawMOIDsTask = g_ThreadMan.GetPriorityThreadPool().submit([this]() { + UpdateDrawMOIDs(g_SceneMan.GetMOIDBitmap()); +}); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RedrawOverlappingMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces all objects potnetially overlapping a specific MO to re-draw -// this MOID representations onto the MOID bitmap. +//////////////////////////////////////////////////////////////////// +// Draw the MO colors ONLY if this is a drawn update! -void MovableMan::RedrawOverlappingMOIDs(MovableObject *pOverlapsThis) +if (g_TimerMan.DrawnSimUpdate()) + Draw(g_SceneMan.GetMOColorBitmap()); + +// Sort team rosters if necessary { - for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) - { - (*aIt)->DrawMOIDIfOverlapping(pOverlapsThis); - } - - for (std::deque::iterator iIt = m_Items.begin(); iIt != m_Items.end(); ++iIt) - { - (*iIt)->DrawMOIDIfOverlapping(pOverlapsThis); - } - - for (std::deque::iterator parIt = m_Particles.begin(); parIt != m_Particles.end(); ++parIt) - { - (*parIt)->DrawMOIDIfOverlapping(pOverlapsThis); - } + if (m_SortTeamRoster[Activity::TeamOne]) + m_ActorRoster[Activity::TeamOne].sort(MOXPosComparison()); + if (m_SortTeamRoster[Activity::TeamTwo]) + m_ActorRoster[Activity::TeamTwo].sort(MOXPosComparison()); + if (m_SortTeamRoster[Activity::TeamThree]) + m_ActorRoster[Activity::TeamThree].sort(MOXPosComparison()); + if (m_SortTeamRoster[Activity::TeamFour]) + m_ActorRoster[Activity::TeamFour].sort(MOXPosComparison()); } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void callLuaFunctionOnMORecursive(MovableObject* mo, const std::string& functionName, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { - if (MOSRotating* mosr = dynamic_cast(mo)) { - for (auto attachablrItr = mosr->GetAttachableList().begin(); attachablrItr != mosr->GetAttachableList().end(); ) { - Attachable* attachable = *attachablrItr; - ++attachablrItr; - - attachable->RunScriptedFunctionInAppropriateScripts(functionName, false, false, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - callLuaFunctionOnMORecursive(attachable, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - } - - for (auto woundItr = mosr->GetWoundList().begin(); woundItr != mosr->GetWoundList().end(); ) { - AEmitter* wound = *woundItr; - ++woundItr; - - wound->RunScriptedFunctionInAppropriateScripts(functionName, false, false, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - callLuaFunctionOnMORecursive(wound, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - } - } - - mo->RunScriptedFunctionInAppropriateScripts(functionName, false, false, functionEntityArguments, functionLiteralArguments, functionObjectArguments); -}; - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MovableMan::RunLuaFunctionOnAllMOs(const std::string &functionName, bool includeAdded, const std::vector &functionEntityArguments, const std::vector &functionLiteralArguments, const std::vector &functionObjectArguments) { - if (includeAdded) { - for (Actor* actor : m_AddedActors) { - callLuaFunctionOnMORecursive(actor, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - } - - for (MovableObject *item : m_AddedItems) { - callLuaFunctionOnMORecursive(item, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - } - - for (MovableObject* particle : m_AddedParticles) { - callLuaFunctionOnMORecursive(particle, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - } - } - - for (Actor *actor : m_Actors) { - callLuaFunctionOnMORecursive(actor, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - } - - for (MovableObject *item : m_Items) { - callLuaFunctionOnMORecursive(item, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - } - - for (MovableObject* particle : m_Particles) { - callLuaFunctionOnMORecursive(particle, functionName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void reloadLuaFunctionsOnMORecursive(MovableObject* mo) { - if (MOSRotating* mosr = dynamic_cast(mo)) { - for (auto attachablrItr = mosr->GetAttachableList().begin(); attachablrItr != mosr->GetAttachableList().end(); ) { - Attachable* attachable = *attachablrItr; - ++attachablrItr; - - attachable->ReloadScripts(); - reloadLuaFunctionsOnMORecursive(attachable); - } - - for (auto woundItr = mosr->GetWoundList().begin(); woundItr != mosr->GetWoundList().end(); ) { - AEmitter* wound = *woundItr; - ++woundItr; - - wound->ReloadScripts(); - reloadLuaFunctionsOnMORecursive(wound); - } - } - - mo->ReloadScripts(); -}; - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void MovableMan::ReloadLuaScripts() { - for (Actor* actor : m_AddedActors) { - reloadLuaFunctionsOnMORecursive(actor); - } +////////////////////////////////////////////////////////////////////////////////////////// - for (MovableObject* item : m_AddedItems) { - reloadLuaFunctionsOnMORecursive(item); - } +void MovableMan::Travel() { + ZoneScoped; - for (MovableObject* particle : m_AddedParticles) { - reloadLuaFunctionsOnMORecursive(particle); - } + if (m_DrawMOIDsTask.valid()) { + m_DrawMOIDsTask.wait(); + } - for (Actor* actor : m_Actors) { - reloadLuaFunctionsOnMORecursive(actor); - } + // Travel Actors + { + ZoneScopedN("Actors Travel"); + + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActorsTravel); + for (auto aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) { + if (!((*aIt)->IsUpdated())) { + (*aIt)->ApplyForces(); + (*aIt)->PreTravel(); + (*aIt)->Travel(); + (*aIt)->PostTravel(); + } + (*aIt)->NewFrame(); + } + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActorsTravel); + } - for (MovableObject* item : m_Items) { - reloadLuaFunctionsOnMORecursive(item); - } + // Travel items + { + ZoneScopedN("Items Travel"); + + for (auto iIt = m_Items.begin(); iIt != m_Items.end(); ++iIt) { + if (!((*iIt)->IsUpdated())) { + (*iIt)->ApplyForces(); + (*iIt)->PreTravel(); + (*iIt)->Travel(); + (*iIt)->PostTravel(); + } + (*iIt)->NewFrame(); + } + } - for (MovableObject* particle : m_Particles) { - reloadLuaFunctionsOnMORecursive(particle); - } + // Travel particles + { + ZoneScopedN("Particles Travel"); + + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ParticlesTravel); + for (auto parIt = m_Particles.begin(); parIt != m_Particles.end(); ++parIt) { + if (!((*parIt)->IsUpdated())) { + (*parIt)->ApplyForces(); + (*parIt)->PreTravel(); + (*parIt)->Travel(); + (*parIt)->PostTravel(); + } + (*parIt)->NewFrame(); + } + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ParticlesTravel); + } } ////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this MovableMan. Supposed to be done every frame. -void MovableMan::Update() -{ - ZoneScoped; - - // Don't update if paused - if (g_ActivityMan.GetActivity() && g_ActivityMan.ActivityPaused()) { - return; - } - - m_SimUpdateFrameNumber++; - - // ---TEMP --- - // These are here for multithreaded AI, but will be unnecessary when multithreaded-sim-and-render is in! - // Clear the MO color layer only if this is a drawn update - if (g_TimerMan.DrawnSimUpdate()) { - g_SceneMan.ClearMOColorLayer(); - } - - // If this is the first sim update since a drawn one, then clear the post effects - if (g_TimerMan.SimUpdatesSinceDrawn() == 0) { - g_PostProcessMan.ClearScenePostEffects(); - } - // ---TEMP--- - - // Reset the draw HUD roster line settings - m_SortTeamRoster[Activity::TeamOne] = false; - m_SortTeamRoster[Activity::TeamTwo] = false; - m_SortTeamRoster[Activity::TeamThree] = false; - m_SortTeamRoster[Activity::TeamFour] = false; - - // Move all last frame's alarm events into the proper buffer, and clear out the new one to fill up with this frame's - m_AlarmEvents.clear(); - for (std::vector::iterator aeItr = m_AddedAlarmEvents.begin(); aeItr != m_AddedAlarmEvents.end(); ++aeItr) { - m_AlarmEvents.push_back(*aeItr); - } - m_AddedAlarmEvents.clear(); - - // Travel MOs - Travel(); - - // If our debug settings switch is forcing all pathing requests to immediately complete, make sure they're done here - if (g_SettingsMan.GetForceImmediatePathingRequestCompletion() && g_SceneMan.GetScene()) { - g_SceneMan.GetScene()->BlockUntilAllPathingRequestsComplete(); - } - - // Prior to controller/AI update, execute lua callbacks - g_LuaMan.ExecuteLuaScriptCallbacks(); - - // Updates everything needed prior to AI/user input being processed - // Fugly hack to keep backwards compat with scripts that rely on weird frame-delay-ordering behaviours - // TODO, cleanup the pre-controller update and post-controller updates to have some consistent logic of what goes where - PreControllerUpdate(); - - // Updates AI/user input - UpdateControllers(); - - // Will use some common iterators - std::deque::iterator aIt; - std::deque::iterator amidIt; - std::deque::iterator iIt; - std::deque::iterator imidIt; - std::deque::iterator parIt; - std::deque::iterator midIt; - - // Update all multithreaded scripts for all objects - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ScriptsUpdate); - { - ZoneScopedN("Multithreaded Scripts Update"); - - const std::string threadedUpdate = "ThreadedUpdate"; // avoid string reconstruction - - LuaStatesArray& luaStates = g_LuaMan.GetThreadedScriptStates(); - g_ThreadMan.GetPriorityThreadPool().parallelize_loop(luaStates.size(), - [&](int start, int end) { - RTEAssert(start + 1 == end, "Threaded script state being updated across multiple threads!"); - LuaStateWrapper& luaState = luaStates[start]; - g_LuaMan.SetThreadLuaStateOverride(&luaState); - - for (MovableObject *mo : luaState.GetRegisteredMOs()) { - mo->RunScriptedFunctionInAppropriateScripts(threadedUpdate, false, false, {}, {}, {}); - } - - g_LuaMan.SetThreadLuaStateOverride(nullptr); - }).wait(); - } - - { - ZoneScopedN("Multithreaded Scripts SyncedUpdate"); - - const std::string syncedUpdate = "SyncedUpdate"; // avoid string reconstruction - - for (LuaStateWrapper& luaState : g_LuaMan.GetThreadedScriptStates()) { - g_LuaMan.SetThreadLuaStateOverride(&luaState); - - for (MovableObject* mo : luaState.GetRegisteredMOs()) { - if (mo->HasRequestedSyncedUpdate()) { - mo->RunScriptedFunctionInAppropriateScripts(syncedUpdate, false, false, {}, {}, {}); - mo->ResetRequestedSyncedUpdateFlag(); - } - } - - g_LuaMan.SetThreadLuaStateOverride(nullptr); - } - } - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ScriptsUpdate); - - { - { - ZoneScopedN("Actors Update"); - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActorsUpdate); - for (Actor* actor : m_Actors) { - actor->Update(); - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ScriptsUpdate); - actor->UpdateScripts(); - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ScriptsUpdate); - - actor->ApplyImpulses(); - } - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActorsUpdate); - } - - { - ZoneScopedN("Items Update"); - - int count = 0; - int itemLimit = m_Items.size() - m_MaxDroppedItems; - for (iIt = m_Items.begin(); iIt != m_Items.end(); ++iIt, ++count) { - (*iIt)->Update(); - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ScriptsUpdate); - (*iIt)->UpdateScripts(); - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ScriptsUpdate); - - (*iIt)->ApplyImpulses(); - if (count <= itemLimit) { - (*iIt)->SetToSettle(true); - } - } - } - - { - ZoneScopedN("Particles Update"); - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ParticlesUpdate); - for (MovableObject* particle : m_Particles) { - particle->Update(); - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ScriptsUpdate); - particle->UpdateScripts(); - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ScriptsUpdate); - - particle->ApplyImpulses(); - particle->RestDetection(); - // Copy particles that are at rest to the terrain and mark them for deletion. - if (particle->IsAtRest()) { - particle->SetToSettle(true); - } - } - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ParticlesUpdate); - } - - { - ZoneScopedN("Post Update"); - - for (Actor* actor : m_Actors) { - actor->PostUpdate(); - } - for (MovableObject* item : m_Items) { - item->PostUpdate(); - } - for (MovableObject* particle : m_Particles) { - particle->PostUpdate(); - } - } - } - - ////////////////////////////////////////////////////////////////////// - // TRANSFER ALL MOs ADDED THIS FRAME - // All Actors, Items, and Particles added this frame now are officially added - - { - ZoneScopedN("MO Transfer and Deletion"); - - { - // Actors - for (aIt = m_AddedActors.begin(); aIt != m_AddedActors.end(); ++aIt) - { - // Delete instead if it's marked for it - if (!(*aIt)->IsSetToDelete()) - m_Actors.push_back(*aIt); - else - { - m_ValidActors.erase(*aIt); - - // Also remove actor from the roster - if ((*aIt)->GetTeam() >= 0) { - //m_ActorRoster[(*aIt)->GetTeam()].remove(*aIt); - RemoveActorFromTeamRoster(*aIt); - } - - (*aIt)->DestroyScriptState(); - delete (*aIt); - } - } - m_AddedActors.clear(); - - // Items - for (iIt = m_AddedItems.begin(); iIt != m_AddedItems.end(); ++iIt) - { - // Delete instead if it's marked for it - if (!(*iIt)->IsSetToDelete()) { - m_Items.push_back(*iIt); - } else { - m_ValidItems.erase(*iIt); - (*iIt)->DestroyScriptState(); - delete (*iIt); - } - } - m_AddedItems.clear(); - - // Particles - for (parIt = m_AddedParticles.begin(); parIt != m_AddedParticles.end(); ++parIt) - { - // Delete instead if it's marked for it - if (!(*parIt)->IsSetToDelete()) { - m_Particles.push_back(*parIt); - } else { - m_ValidParticles.erase(*parIt); - (*parIt)->DestroyScriptState(); - delete (*parIt); - } - } - m_AddedParticles.clear(); - } - - //////////////////////////////////////////////////////////////////////////// - // Copy (Settle) Pass - - { - // DEATH ////////////////////////////////////////////////////////// - // Transfer dead actors from Actor list to particle list - aIt = partition(m_Actors.begin(), m_Actors.end(), std::not_fn(std::mem_fn(&Actor::IsDead))); - amidIt = aIt; - - // Move dead Actor to particles list - if (amidIt != m_Actors.end()/* && m_Actors.size() > 1*/) - { - while (aIt != m_Actors.end()) - { - // Report the death of the actor to the game - g_ActivityMan.GetActivity()->ReportDeath((*aIt)->GetTeam()); - - // Add to the particles list - m_Particles.push_back(*aIt); - m_ValidParticles.insert(*aIt); - // Remove from the team roster - - if ((*aIt)->GetTeam() >= 0) { - //m_ActorRoster[(*aIt)->GetTeam()].remove(*aIt); - RemoveActorFromTeamRoster(*aIt); - } - - m_ValidActors.erase(*aIt); - aIt++; - } - // Try to set the existing iterator to a safer value, erase can crash in debug mode otherwise? - aIt = m_Actors.begin(); - m_Actors.erase(amidIt, m_Actors.end()); - } - - // ITEM SETTLE ////////////////////////////////////////////////////////// - // Transfer excess items to particle list - use stable partition, item orde is important - iIt = stable_partition(m_Items.begin(), m_Items.end(), std::not_fn(std::mem_fn(&MovableObject::ToSettle))); - imidIt = iIt; - - // Move force-settled items to particles list - if (imidIt != m_Items.end()/* && m_Items.size() > 1*/) - { - while (iIt != m_Items.end()) - { - (*iIt)->SetToSettle(false); - // Disable TDExplosive's immunity to settling - if ((*iIt)->GetRestThreshold() < 0) { - (*iIt)->SetRestThreshold(500); - } - m_ValidItems.erase(*iIt); - m_Particles.push_back(*iIt); - iIt++; - } - m_Items.erase(imidIt, m_Items.end()); - } - - // DELETE ////////////////////////////////////////////////////////// - // Only delete after all travels & updates are done - // Actors - aIt = partition(m_Actors.begin(), m_Actors.end(), std::not_fn(std::mem_fn(&MovableObject::ToDelete))); - amidIt = aIt; - - while (aIt != m_Actors.end()) - { - // Set brain to 0 to avoid crashes due to brain deletion - Activity * pActivity = g_ActivityMan.GetActivity(); - if (pActivity) - { - if (pActivity->IsAssignedBrain(*aIt)) - pActivity->SetPlayerBrain(0, pActivity->IsBrainOfWhichPlayer(*aIt)); - - pActivity->ReportDeath((*aIt)->GetTeam()); - } - - // Remove from team rosters - if ((*aIt)->GetTeam() >= Activity::TeamOne && (*aIt)->GetTeam() < Activity::MaxTeamCount) - //m_ActorRoster[(*aIt)->GetTeam()].remove(*aIt); - RemoveActorFromTeamRoster(*aIt); - - // Delete - m_ValidActors.erase(*aIt); - (*aIt)->DestroyScriptState(); - delete (*aIt); - aIt++; - } - // Try to set the existing iterator to a safer value, erase can crash in debug mode otherwise? - aIt = m_Actors.begin(); - m_Actors.erase(amidIt, m_Actors.end()); - - // Items - iIt = stable_partition(m_Items.begin(), m_Items.end(), std::not_fn(std::mem_fn(&MovableObject::ToDelete))); - imidIt = iIt; - - while (iIt != m_Items.end()) { - m_ValidItems.erase(*iIt); - (*iIt)->DestroyScriptState(); - delete (*iIt); - iIt++; - } - m_Items.erase(imidIt, m_Items.end()); - - // Particles - parIt = partition(m_Particles.begin(), m_Particles.end(), std::not_fn(std::mem_fn(&MovableObject::ToDelete))); - midIt = parIt; - - while (parIt != m_Particles.end()) { - m_ValidParticles.erase(*parIt); - (*parIt)->DestroyScriptState(); - delete (*parIt); - parIt++; - } - m_Particles.erase(midIt, m_Particles.end()); - } - - // SETTLE PARTICLES ////////////////////////////////////////////////// - // Only settle after all updates and deletions are done - if (m_SettlingEnabled) { - parIt = partition(m_Particles.begin(), m_Particles.end(), std::not_fn(std::mem_fn(&MovableObject::ToSettle))); - midIt = parIt; - - while (parIt != m_Particles.end()) { - Vector parPos((*parIt)->GetPos()); - Material const * terrMat = g_SceneMan.GetMaterialFromID(g_SceneMan.GetTerrain()->GetMaterialPixel(parPos.GetFloorIntX(), parPos.GetFloorIntY())); - int piling = (*parIt)->GetMaterial()->GetPiling(); - if (piling > 0) { - for (int s = 0; s < piling && (terrMat->GetIndex() == (*parIt)->GetMaterial()->GetIndex() || terrMat->GetIndex() == (*parIt)->GetMaterial()->GetSettleMaterial()); ++s) { - if ((piling - s) % 2 == 0) { - parPos.m_Y -= 1.0F; - } else { - parPos.m_X += (RandomNum() >= 0.5F ? 1.0F : -1.0F); - } - terrMat = g_SceneMan.GetMaterialFromID(g_SceneMan.GetTerrain()->GetMaterialPixel(parPos.GetFloorIntX(), parPos.GetFloorIntY())); - } - (*parIt)->SetPos(parPos.GetFloored()); - } - if ((*parIt)->GetDrawPriority() >= terrMat->GetPriority()) { (*parIt)->DrawToTerrain(g_SceneMan.GetTerrain()); } - m_ValidParticles.erase(*parIt); - (*parIt)->DestroyScriptState(); - delete (*parIt); - parIt++; - } - m_Particles.erase(midIt, m_Particles.end()); - } - } - - // We've finished stuff that can interact with lua script, so it's the ideal time to start a gc run - g_LuaMan.StartAsyncGarbageCollection(); - - //////////////////////////////////////////////////////////////////////// - // Draw the MO matter and IDs to their layers for next frame - m_DrawMOIDsTask = g_ThreadMan.GetPriorityThreadPool().submit([this]() { - UpdateDrawMOIDs(g_SceneMan.GetMOIDBitmap()); - }); - - - //////////////////////////////////////////////////////////////////// - // Draw the MO colors ONLY if this is a drawn update! - - if (g_TimerMan.DrawnSimUpdate()) - Draw(g_SceneMan.GetMOColorBitmap()); - - // Sort team rosters if necessary - { - if (m_SortTeamRoster[Activity::TeamOne]) - m_ActorRoster[Activity::TeamOne].sort(MOXPosComparison()); - if (m_SortTeamRoster[Activity::TeamTwo]) - m_ActorRoster[Activity::TeamTwo].sort(MOXPosComparison()); - if (m_SortTeamRoster[Activity::TeamThree]) - m_ActorRoster[Activity::TeamThree].sort(MOXPosComparison()); - if (m_SortTeamRoster[Activity::TeamFour]) - m_ActorRoster[Activity::TeamFour].sort(MOXPosComparison()); - } -} +void MovableMan::UpdateControllers() { + ZoneScoped; -////////////////////////////////////////////////////////////////////////////////////////// + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActorsAI); + { + for (Actor* actor: m_Actors) { + actor->GetController()->Update(); + } -void MovableMan::Travel() -{ - ZoneScoped; - - if (m_DrawMOIDsTask.valid()) { - m_DrawMOIDsTask.wait(); - } - - // Travel Actors - { - ZoneScopedN("Actors Travel"); - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActorsTravel); - for (auto aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) - { - if (!((*aIt)->IsUpdated())) - { - (*aIt)->ApplyForces(); - (*aIt)->PreTravel(); - (*aIt)->Travel(); - (*aIt)->PostTravel(); - } - (*aIt)->NewFrame(); - } - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActorsTravel); - } - - // Travel items - { - ZoneScopedN("Items Travel"); - - for (auto iIt = m_Items.begin(); iIt != m_Items.end(); ++iIt) - { - if (!((*iIt)->IsUpdated())) - { - (*iIt)->ApplyForces(); - (*iIt)->PreTravel(); - (*iIt)->Travel(); - (*iIt)->PostTravel(); - } - (*iIt)->NewFrame(); - } - } - - // Travel particles - { - ZoneScopedN("Particles Travel"); - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ParticlesTravel); - for (auto parIt = m_Particles.begin(); parIt != m_Particles.end(); ++parIt) - { - if (!((*parIt)->IsUpdated())) - { - (*parIt)->ApplyForces(); - (*parIt)->PreTravel(); - (*parIt)->Travel(); - (*parIt)->PostTravel(); - } - (*parIt)->NewFrame(); - } - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ParticlesTravel); - } + LuaStatesArray& luaStates = g_LuaMan.GetThreadedScriptStates(); + g_ThreadMan.GetPriorityThreadPool().parallelize_loop(luaStates.size(), + [&](int start, int end) { + RTEAssert(start + 1 == end, "Threaded script state being updated across multiple threads!"); + LuaStateWrapper& luaState = luaStates[start]; + g_LuaMan.SetThreadLuaStateOverride(&luaState); + for (Actor* actor: m_Actors) { + if (actor->GetLuaState() == &luaState && actor->GetController()->ShouldUpdateAIThisFrame()) { + actor->RunScriptedFunctionInAppropriateScripts("ThreadedUpdateAI", false, true, {}, {}, {}); + } + } + g_LuaMan.SetThreadLuaStateOverride(nullptr); + }) + .wait(); + + for (Actor* actor: m_Actors) { + if (actor->GetController()->ShouldUpdateAIThisFrame()) { + actor->RunScriptedFunctionInAppropriateScripts("UpdateAI", false, true, {}, {}, {}); + } + } + } + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActorsAI); } ////////////////////////////////////////////////////////////////////////////////////////// -void MovableMan::UpdateControllers() -{ - ZoneScoped; - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActorsAI); - { - for (Actor* actor : m_Actors) { - actor->GetController()->Update(); - } - - LuaStatesArray& luaStates = g_LuaMan.GetThreadedScriptStates(); - g_ThreadMan.GetPriorityThreadPool().parallelize_loop(luaStates.size(), - [&](int start, int end) { - RTEAssert(start + 1 == end, "Threaded script state being updated across multiple threads!"); - LuaStateWrapper& luaState = luaStates[start]; - g_LuaMan.SetThreadLuaStateOverride(&luaState); - for (Actor *actor : m_Actors) { - if (actor->GetLuaState() == &luaState && actor->GetController()->ShouldUpdateAIThisFrame()) { - actor->RunScriptedFunctionInAppropriateScripts("ThreadedUpdateAI", false, true, {}, {}, {}); - } - } - g_LuaMan.SetThreadLuaStateOverride(nullptr); - }).wait(); - - for (Actor* actor : m_Actors) { - if (actor->GetController()->ShouldUpdateAIThisFrame()) { - actor->RunScriptedFunctionInAppropriateScripts("UpdateAI", false, true, {}, {}, {}); - } - } - } - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActorsAI); -} +void MovableMan::PreControllerUpdate() { + ZoneScoped; -////////////////////////////////////////////////////////////////////////////////////////// + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActorsUpdate); + for (Actor* actor: m_Actors) { + actor->PreControllerUpdate(); + } + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActorsUpdate); -void MovableMan::PreControllerUpdate() -{ - ZoneScoped; - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActorsUpdate); - for (Actor *actor : m_Actors) { - actor->PreControllerUpdate(); - } - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActorsUpdate); - - for (MovableObject* item : m_Items) { - item->PreControllerUpdate(); - } - - g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ParticlesUpdate); - for (MovableObject* particle : m_Particles) { - particle->PreControllerUpdate(); - } - g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ParticlesUpdate); + for (MovableObject* item: m_Items) { + item->PreControllerUpdate(); + } + + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ParticlesUpdate); + for (MovableObject* particle: m_Particles) { + particle->PreControllerUpdate(); + } + g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ParticlesUpdate); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -2190,14 +2044,13 @@ void MovableMan::PreControllerUpdate() // Description: Draws this MovableMan's all MO's current material representations to a // BITMAP of choice. -void MovableMan::DrawMatter(BITMAP *pTargetBitmap, Vector &targetPos) -{ - // Draw objects to accumulation bitmap - for (std::deque::iterator aIt = --m_Actors.end(); aIt != --m_Actors.begin(); --aIt) - (*aIt)->Draw(pTargetBitmap, targetPos, g_DrawMaterial); +void MovableMan::DrawMatter(BITMAP* pTargetBitmap, Vector& targetPos) { + // Draw objects to accumulation bitmap + for (std::deque::iterator aIt = --m_Actors.end(); aIt != --m_Actors.begin(); --aIt) + (*aIt)->Draw(pTargetBitmap, targetPos, g_DrawMaterial); - for (std::deque::iterator parIt = --m_Particles.end(); parIt != --m_Particles.begin(); --parIt) - (*parIt)->Draw(pTargetBitmap, targetPos, g_DrawMaterial); + for (std::deque::iterator parIt = --m_Particles.end(); parIt != --m_Particles.begin(); --parIt) + (*parIt)->Draw(pTargetBitmap, targetPos, g_DrawMaterial); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -2207,29 +2060,24 @@ void MovableMan::DrawMatter(BITMAP *pTargetBitmap, Vector &targetPos) // Arguments: None. // Return value: None. -void MovableMan::VerifyMOIDIndex() -{ +void MovableMan::VerifyMOIDIndex() { int count = 0; - for (std::vector::iterator aIt = m_MOIDIndex.begin(); aIt != m_MOIDIndex.end(); ++aIt) - { - if (*aIt) - { + for (std::vector::iterator aIt = m_MOIDIndex.begin(); aIt != m_MOIDIndex.end(); ++aIt) { + if (*aIt) { RTEAssert((*aIt)->GetID() == g_NoMOID || (*aIt)->GetID() == count, "MOIDIndex broken!"); RTEAssert((*aIt)->GetRootID() == g_NoMOID || ((*aIt)->GetRootID() >= 0 && (*aIt)->GetRootID() < g_MovableMan.GetMOIDCount()), "MOIDIndex broken!"); } count++; - if (count == g_NoMOID) count++; + if (count == g_NoMOID) + count++; } - - for (std::deque::iterator itr = m_Items.begin(); itr != m_Items.end(); ++itr) - { + for (std::deque::iterator itr = m_Items.begin(); itr != m_Items.end(); ++itr) { RTEAssert((*itr)->GetID() == g_NoMOID || (*itr)->GetID() < GetMOIDCount(), "MOIDIndex broken!"); RTEAssert((*itr)->GetRootID() == g_NoMOID || ((*itr)->GetRootID() >= 0 && (*itr)->GetRootID() < g_MovableMan.GetMOIDCount()), "MOIDIndex broken!"); } // Try the items just added this frame - for (std::deque::iterator itr = m_AddedItems.begin(); itr != m_AddedItems.end(); ++itr) - { + for (std::deque::iterator itr = m_AddedItems.begin(); itr != m_AddedItems.end(); ++itr) { RTEAssert((*itr)->GetID() == g_NoMOID || (*itr)->GetID() < GetMOIDCount(), "MOIDIndex broken!"); RTEAssert((*itr)->GetRootID() == g_NoMOID || ((*itr)->GetRootID() >= 0 && (*itr)->GetRootID() < g_MovableMan.GetMOIDCount()), "MOIDIndex broken!"); } @@ -2241,71 +2089,69 @@ void MovableMan::VerifyMOIDIndex() // Description: Updates the MOIDs of all current MOs and draws their ID's to a BITMAP // of choice. -void MovableMan::UpdateDrawMOIDs(BITMAP *pTargetBitmap) -{ - ZoneScoped; +void MovableMan::UpdateDrawMOIDs(BITMAP* pTargetBitmap) { + ZoneScoped; - /////////////////////////////////////////////////// - // Clear the MOID layer before starting to delete stuff which may be in the MOIDIndex - g_SceneMan.ClearAllMOIDDrawings(); + /////////////////////////////////////////////////// + // Clear the MOID layer before starting to delete stuff which may be in the MOIDIndex + g_SceneMan.ClearAllMOIDDrawings(); - // Clear the index each frame and do it over because MO's get added and deleted between each frame. - m_MOIDIndex.clear(); - m_ContiguousActorIDs.clear(); + // Clear the index each frame and do it over because MO's get added and deleted between each frame. + m_MOIDIndex.clear(); + m_ContiguousActorIDs.clear(); - // Add a null and start counter at 1 because MOID == 0 means no MO. - // - Update: This isnt' true anymore, but still keep 0 free just to be safe - m_MOIDIndex.push_back(0); + // Add a null and start counter at 1 because MOID == 0 means no MO. + // - Update: This isnt' true anymore, but still keep 0 free just to be safe + m_MOIDIndex.push_back(0); - MOID currentMOID = 1; + MOID currentMOID = 1; - int actorID = 0; - for (Actor *actor : m_Actors) { - m_ContiguousActorIDs[actor] = actorID++; + int actorID = 0; + for (Actor* actor: m_Actors) { + m_ContiguousActorIDs[actor] = actorID++; if (!actor->IsSetToDelete()) { - actor->UpdateMOID(m_MOIDIndex); - actor->Draw(pTargetBitmap, Vector(), g_DrawMOID, true); - currentMOID = m_MOIDIndex.size(); - } else { - actor->SetAsNoID(); - } - } - - for (MovableObject *item : m_Items) { - if (!item->IsSetToDelete()) { - item->UpdateMOID(m_MOIDIndex); - item->Draw(pTargetBitmap, Vector(), g_DrawMOID, true); - currentMOID = m_MOIDIndex.size(); - } else { - item->SetAsNoID(); - } - } - - for (MovableObject *particle : m_Particles) { - if (!particle->IsSetToDelete()) { - particle->UpdateMOID(m_MOIDIndex); - particle->Draw(pTargetBitmap, Vector(), g_DrawMOID, true); - currentMOID = m_MOIDIndex.size(); - } else { - particle->SetAsNoID(); - } - } - - // COUNT MOID USAGE PER TEAM ////////////////////////////////////////////////// - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; team++) { - m_TeamMOIDCount[team] = 0; - } - - for (auto itr = m_MOIDIndex.begin(); itr != m_MOIDIndex.end(); ++itr) { - if (*itr) { - int team = (*itr)->GetTeam(); - if (team > Activity::NoTeam && team < Activity::MaxTeamCount) { - m_TeamMOIDCount[team]++; - } - } - } -} + actor->UpdateMOID(m_MOIDIndex); + actor->Draw(pTargetBitmap, Vector(), g_DrawMOID, true); + currentMOID = m_MOIDIndex.size(); + } else { + actor->SetAsNoID(); + } + } + + for (MovableObject* item: m_Items) { + if (!item->IsSetToDelete()) { + item->UpdateMOID(m_MOIDIndex); + item->Draw(pTargetBitmap, Vector(), g_DrawMOID, true); + currentMOID = m_MOIDIndex.size(); + } else { + item->SetAsNoID(); + } + } + + for (MovableObject* particle: m_Particles) { + if (!particle->IsSetToDelete()) { + particle->UpdateMOID(m_MOIDIndex); + particle->Draw(pTargetBitmap, Vector(), g_DrawMOID, true); + currentMOID = m_MOIDIndex.size(); + } else { + particle->SetAsNoID(); + } + } + + // COUNT MOID USAGE PER TEAM ////////////////////////////////////////////////// + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; team++) { + m_TeamMOIDCount[team] = 0; + } + for (auto itr = m_MOIDIndex.begin(); itr != m_MOIDIndex.end(); ++itr) { + if (*itr) { + int team = (*itr)->GetTeam(); + if (team > Activity::NoTeam && team < Activity::MaxTeamCount) { + m_TeamMOIDCount[team]++; + } + } + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: Draw @@ -2313,54 +2159,51 @@ void MovableMan::UpdateDrawMOIDs(BITMAP *pTargetBitmap) // Description: Draws this MovableMan's current graphical representation to a // BITMAP of choice. -void MovableMan::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) -{ - ZoneScoped; +void MovableMan::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { + ZoneScoped; - // Draw objects to accumulation bitmap, in reverse order so actors appear on top. + // Draw objects to accumulation bitmap, in reverse order so actors appear on top. - { - ZoneScopedN("Particles Draw"); + { + ZoneScopedN("Particles Draw"); - for (std::deque::iterator parIt = m_Particles.begin(); parIt != m_Particles.end(); ++parIt) { - (*parIt)->Draw(pTargetBitmap, targetPos); - } - } + for (std::deque::iterator parIt = m_Particles.begin(); parIt != m_Particles.end(); ++parIt) { + (*parIt)->Draw(pTargetBitmap, targetPos); + } + } - { - ZoneScopedN("Items Draw"); + { + ZoneScopedN("Items Draw"); - for (std::deque::reverse_iterator itmIt = m_Items.rbegin(); itmIt != m_Items.rend(); ++itmIt) { - (*itmIt)->Draw(pTargetBitmap, targetPos); - } - } + for (std::deque::reverse_iterator itmIt = m_Items.rbegin(); itmIt != m_Items.rend(); ++itmIt) { + (*itmIt)->Draw(pTargetBitmap, targetPos); + } + } - { - ZoneScopedN("Actors Draw"); + { + ZoneScopedN("Actors Draw"); - for (std::deque::reverse_iterator aIt = m_Actors.rbegin(); aIt != m_Actors.rend(); ++aIt) { - (*aIt)->Draw(pTargetBitmap, targetPos); - } - } + for (std::deque::reverse_iterator aIt = m_Actors.rbegin(); aIt != m_Actors.rend(); ++aIt) { + (*aIt)->Draw(pTargetBitmap, targetPos); + } + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: DrawHUD ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws this MovableMan's current graphical representation to a // BITMAP of choice. -void MovableMan::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int which, bool playerControlled) -{ - ZoneScoped; +void MovableMan::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int which, bool playerControlled) { + ZoneScoped; - // Draw HUD elements - for (std::deque::reverse_iterator itmIt = m_Items.rbegin(); itmIt != m_Items.rend(); ++itmIt) - (*itmIt)->DrawHUD(pTargetBitmap, targetPos, which); + // Draw HUD elements + for (std::deque::reverse_iterator itmIt = m_Items.rbegin(); itmIt != m_Items.rend(); ++itmIt) + (*itmIt)->DrawHUD(pTargetBitmap, targetPos, which); - for (std::deque::reverse_iterator aIt = m_Actors.rbegin(); aIt != m_Actors.rend(); ++aIt) - (*aIt)->DrawHUD(pTargetBitmap, targetPos, which); + for (std::deque::reverse_iterator aIt = m_Actors.rbegin(); aIt != m_Actors.rend(); ++aIt) + (*aIt)->DrawHUD(pTargetBitmap, targetPos, which); } } // namespace RTE diff --git a/Source/Managers/MovableMan.h b/Source/Managers/MovableMan.h index 39a0bfcb20..d8d049869b 100644 --- a/Source/Managers/MovableMan.h +++ b/Source/Managers/MovableMan.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,1052 +19,997 @@ #define g_MovableMan MovableMan::Instance() -namespace RTE -{ - -class MovableObject; -class Actor; -class HeldDevice; -class MOPixel; -class MOSprite; -class AHuman; -class SceneLayer; -class SceneObject; -class Box; -class LuabindObjectWrapper; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Struct: AlarmEvent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A struct to keep all data about a an alarming event for the AI Actors. -// Parent(s): None. -// Class history: 10/3/2008 AlarmEvent created. - -struct AlarmEvent { - AlarmEvent() { m_ScenePos.Reset(); m_Team = Activity::NoTeam; m_Range = 1.0F; } - // TODO: Stop relying on screen width for this shit! - AlarmEvent(const Vector &pos, int team = Activity::NoTeam, float range = 1.0F); - - // Absolute position in the scene where this occurred - Vector m_ScenePos; - // The team of whatever object that caused this event - Activity::Teams m_Team; - // The range multiplier, that this alarming event can be heard - float m_Range; -}; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: MovableMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The singleton manager of all movable objects in the RTE. -// Parent(s): Singleton, Serializable. -// Class history: 12/25/2001 MovableMan created. - -class MovableMan : public Singleton, public Serializable { - friend class SettingsMan; - friend struct ManagerLuaBindings; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - SerializableClassNameGetter; - SerializableOverrideMethods; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: MovableMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a MovableMan object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - MovableMan() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~MovableMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a MovableMan object before deletion -// from system memory. -// Arguments: None. - - ~MovableMan() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MovableMan object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Initialize(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire MovableMan, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MovableMan object. -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMOFromID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a MO from its MOID. Note that MOID's are only valid during the -// same frame as they were assigned to the MOs! -// Arguments: The MOID to get the matching MO from. -// Return value: A pointer to the requested MovableObject instance. 0 if no MO with that -// MOID was found. 0 if 0 was passed in as MOID (no MOID). Ownership is -// *NOT* transferred!! - - MovableObject * GetMOFromID(MOID whichID); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMOIDCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of MOID's currently in use this frame. -// Arguments: None. -// Return value: The count of MOIDs in use this frame. - - int GetMOIDCount() { return m_MOIDIndex.size(); } - - /// - /// Gets a MOID from pixel coordinates in the Scene. - /// - /// The X coordinate of the Scene pixel to get the MOID of. - /// The Y coordinate of the Scene pixel to get the MOID of. - /// The collection of MOIDs to check the against the specified coordinates. - /// The topmost MOID currently at the specified pixel coordinates. - MOID GetMOIDPixel(int pixelX, int pixelY, const std::vector &moidList); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTeamMOIDCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns MO count for specified team -// Arguments: Team to count MO's -// Return value: MO's count owned by this team - - int GetTeamMOIDCount(int team) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PurgeAllMOs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears out all MovableObject:s out of this. Effectively empties the world -// of anything moving, without resetting all of this' settings. -// Arguments: None. -// Return value: None. - - void PurgeAllMOs(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextActorInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the first Actor in the internal Actor list that is -// of a specifc group, alternatively the first one AFTER a specific actor! -// Arguments: Which group to try to get an Actor for. -// A pointer to an Actor to use as starting point in the forward search. -// Ownership NOT xferred! -// Return value: An Actor pointer to the requested team's first Actor encountered -// in the list. 0 if there are no Actors of that team. - - Actor * GetNextActorInGroup(std::string group, Actor *pAfterThis = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPrevActorInGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the last Actor in the internal Actor list that is -// of a specifc group, alternatively the last one BEFORE a specific actor! -// Arguments: Which group to try to get an Actor for. -// A pointer to an Actor to use as starting point in the backward search. -// Ownership NOT xferred! -// Return value: An Actor pointer to the requested team's last Actor encountered -// in the list. 0 if there are no Actors of that team. - - Actor * GetPrevActorInGroup(std::string group, Actor *pBeforeThis = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTeamRoster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the list of all actors on one team, ordered by their X positions. -// Arguments: Which team to try to get the roster for. -// Return value: A pointer to the list of all the actors on the specified team, sorted -// ascending by their X posistions. Ownership of the list or contained -// actors is NOT transferred! - - std::list * GetTeamRoster(int team = 0) { return &(m_ActorRoster[team]); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextTeamActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the first Actor in the internal Actor list that is -// of a specifc team, alternatively the first one AFTER a specific actor! -// Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. -// A pointer to an Actor to use as starting point in the forward search. -// Ownership NOT xferred! -// Return value: An Actor pointer to the requested team's first Actor encountered -// in the list. 0 if there are no Actors of that team. - - Actor * GetNextTeamActor(int team = 0, Actor *pAfterThis = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPrevTeamActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the last Actor in the internal Actor list that is -// of a specifc team, alternatively the last one BEFORE a specific actor! -// Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. -// A pointer to an Actor to use as starting point in the backward search. -// Ownership NOT xferred! -// Return value: An Actor pointer to the requested team's last Actor encountered -// in the list. 0 if there are no Actors of that team. - - Actor * GetPrevTeamActor(int team = 0, Actor *pBeforeThis = 0); - - /// - /// Get a pointer to an Actor in the internal Actor list that is of a specifc team and closest to a specific scene point. - /// - /// Which team to try to get an Actor for. 0 means first team, 1 means 2nd. - /// The player to get the Actor for. This affects which brain can be marked. - /// The Scene point to search for the closest to. - /// The maximum radius around that scene point to search. - /// A Vector to be filled out with the distance of the returned closest to the search point. Will be unaltered if no object was found within radius. - /// An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! - /// An Actor pointer to the requested team's Actor closest to the Scene point, but not outside the max radius. If no Actor other than the excluded one was found within the radius of the point, nullptr is returned. - Actor * GetClosestTeamActor(int team, int player, const Vector &scenePoint, int maxRadius, Vector &getDistance, const Actor *excludeThis = nullptr) { return GetClosestTeamActor(team, player, scenePoint, maxRadius, getDistance, false, excludeThis); } - - /// - /// Get a pointer to an Actor in the internal Actor list that is of a specifc team and closest to a specific scene point. - /// - /// Which team to try to get an Actor for. 0 means first team, 1 means 2nd. - /// The player to get the Actor for. This affects which brain can be marked. - /// The Scene point to search for the closest to. - /// The maximum radius around that scene point to search. - /// A Vector to be filled out with the distance of the returned closest to the search point. Will be unaltered if no object was found within radius. - /// Whether to only get Actors that are flagged as player controllable. - /// An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! - /// An Actor pointer to the requested team's Actor closest to the Scene point, but not outside the max radius. If no Actor other than the excluded one was found within the radius of the point, nullptr is returned. - Actor * GetClosestTeamActor(int team, int player, const Vector &scenePoint, int maxRadius, Vector &getDistance, bool onlyPlayerControllableActors, const Actor *excludeThis = nullptr); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetClosestEnemyActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to an Actor in the internal Actor list that is is not of -// the specified team and closest to a specific scene point. -// Arguments: Which team to try to get an enemy Actor for. NoTeam means all teams. -// The Scene point to search for the closest to. -// The maximum radius around that scene point to search. -// A Vector to be filled out with the distance of the returned closest to -// the search point. Will be unaltered if no object was found within radius. -// Return value: An Actor pointer to the enemy closest to the Scene -// point, but not outside the max radius. If no Actor -// was found within the radius of the point, 0 is returned. - - Actor * GetClosestEnemyActor(int team, const Vector &scenePoint, int maxRadius, Vector &getDistance); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFirstTeamActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to first best Actor in the internal Actor list that is -// of a specifc team. -// Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. -// The player to get the Actor for. This affects which brain can be marked. -// Return value: An Actor pointer to the first one of the requested team. If no Actor -// is in that team, 0 is returned. - - Actor * GetFirstTeamActor(int team, int player) { Vector temp; return GetClosestTeamActor(team, player, Vector(), 10000000, temp); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetClosestActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to an Actor in the internal Actor list that is closest -// to a specific scene point. -// Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. -// The Scene point to search for the closest to. -// The maximum radius around that scene point to search. -// A Vector to be filled out with the distance of the returned closest to -// the search point. Will be unaltered if no object was found within radius. -// An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! -// Return value: An Actor pointer to the requested Actor closest to the Scene -// point, but not outside the max radius. If no Actor other than the -// excluded one was found within the radius of the point, 0 is returned. - - Actor * GetClosestActor(const Vector &scenePoint, int maxRadius, Vector &getDistance, const Actor *pExcludeThis = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetClosestBrainActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the brain actor of a specific team that is closest to -// a scene point. OWNERSHIP IS NOT TRANSFERRED! -// Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. -// The point in the scene where to look for the closest opposite team brain. -// Return value: An Actor pointer to the requested team's brain closest to the point. -// 0 if there are no brains of that team. OWNERSHIP IS NOT TRANSFERRED! - - Actor * GetClosestBrainActor(int team, const Vector &scenePoint) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFirstBrainActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the brain actor of a specific team that is closest to -// a scene point. OWNERSHIP IS NOT TRANSFERRED! -// Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. -// The point in the scene where to look for the closest opposite team brain. -// Return value: An Actor pointer to the requested team's brain closest to the point. -// 0 if there are no brains of that team. OWNERSHIP IS NOT TRANSFERRED! - - Actor * GetFirstBrainActor(int team) const { return GetClosestBrainActor(team, Vector()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetClosestOtherBrainActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the brain actor NOT of a specific team that is closest -// to a scene point. OWNERSHIP IS NOT TRANSFERRED! -// Arguments: Which team to NOT get the brain for. 0 means first team, 1 means 2nd. -// The point where to look for the closest brain not of this team. -// Return value: An Actor pointer to the requested brain closest to the point. -// 0 if there are no brains not on that team. OWNERSHIP IS NOT TRANSFERRED! - - Actor * GetClosestOtherBrainActor(int notOfTeam, const Vector &scenePoint) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFirstOtherBrainActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the brain actor NOT of a specific team. OWNERSHIP IS NOT TRANSFERRED! -// Arguments: Which team to NOT get the brain for. 0 means first team, 1 means 2nd. -// Return value: An Actor pointer to the requested brain of that team. -// 0 if there are no brains not on that team. OWNERSHIP IS NOT TRANSFERRED! - - Actor * GetFirstOtherBrainActor(int notOfTeam) const { return GetClosestOtherBrainActor(notOfTeam, Vector()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetUnassignedBrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Get a pointer to the first brain actor of a specific team which hasn't -// been assigned to a player yet. -// Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. -// Return value: An Actor pointer to the requested team's first brain encountered -// in the list that hasn't been assigned to a player. 0 if there are no -// unassigned brains of that team. - - Actor * GetUnassignedBrain(int team = 0) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActorCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of actors currently held. -// Arguments: None. -// Return value: The number of actors. - - long GetActorCount() const { return m_Actors.size(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetParticleCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of particles (MOPixel:s) currently held. -// Arguments: None. -// Return value: The number of particles. - - long GetParticleCount() const { return m_Particles.size(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSplashRatio -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the global setting for how much splash MOPixels should be created -// an MO penetrates the terrain deeply. -// Arguments: None. -// Return value: A float with the global splash amount setting, form 1.0 to 0.0. - - float GetSplashRatio() const { return m_SplashRatio; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMaxDroppedItems -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the max number of dropped items that will be reached before the -// first dropped with be copied to the terrain. -// Arguments: An int spefifying the limit. -// Return value: None. - - void SetMaxDroppedItems(int newLimit) { m_MaxDroppedItems = newLimit; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaxDroppedItems -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the max number of dropped items that will be reached before the -// first dropped with be copied to the terrain. -// Arguments: None. -// Return value: An int spefifying the limit. - - int GetMaxDroppedItems() const { return m_MaxDroppedItems; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SortTeamRoster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this to draw HUD lines for a specific team's roster this frame. -// Arguments: Which team to have lines drawn of. -// Return value: None. - - void SortTeamRoster(int team) { m_SortTeamRoster[team] = true; } - - /// - /// Adds a MovableObject to this, after it is determined what it is and the best way to add it is. E.g. if it's an Actor, it will be added as such. Ownership IS transferred! - /// - /// A pointer to the MovableObject to add. Ownership IS transferred! - /// Whether the MovableObject was successfully added or not. Note that Ownership IS transferred either way, but the MovableObject will be deleted if this is not successful. - bool AddMO(MovableObject *movableObjectToAdd); - - /// - /// Adds an Actor to the internal list of Actors. Destruction and deletion will be taken care of automatically. Ownership IS transferred! - /// - /// A pointer to the Actor to add. Ownership IS transferred! - void AddActor(Actor *actorToAdd); - - /// - /// Adds a pickup-able item to the internal list of items. Destruction and deletion will be taken care of automatically. Ownership IS transferred! - /// - /// A pointer to the item to add. Ownership IS transferred! - void AddItem(HeldDevice *itemToAdd); - - /// - /// Adds a MovableObject to the internal list of particles. Destruction and deletion will be taken care of automatically. Ownership IS transferred! - /// - /// A pointer to the MovableObject to add. Ownership is transferred! - void AddParticle(MovableObject *particleToAdd); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes an Actor from the internal list of MO:s. After the Actor is -// removed, ownership is effectively released and transferred to whatever -// client called this method. -// Arguments: A pointer to the MovableObject to remove. -// Return value: Whether the object was found in the particle list, and consequently -// removed. If the particle entry wasn't found, false is returned. - - Actor * RemoveActor(MovableObject *pActorToRem); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a pickup-able MovableObject item from the internal list of -// MO:s. After the item is removed, ownership is effectively released and -// transferred to whatever client called this method. -// Arguments: A pointer to the MovableObject to remove. -// Return value: Whether the object was found in the particle list, and consequently -// removed. If the particle entry wasn't found, false is returned. - - MovableObject * RemoveItem(MovableObject *pItemToRem); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveParticle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a MovableObject from the internal list of MO:s. After the -// MO is removed, ownership is effectively released and transferred to -// whatever client called this method. -// Arguments: A pointer to the MovableObject to remove. -// Return value: Whether the object was found in the particle list, and consequently -// removed. If the particle entry wasn't found, false is returned. - - MovableObject * RemoveParticle(MovableObject *pMOToRem); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeActorTeam -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes actor team and updates team rosters. -// Arguments: Pointer to actor, new team value -// Return value: None. - - void ChangeActorTeam(Actor * pActor, int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddActorToTeamRoster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds actor to internal team roster -// Arguments: Pointer to actor -// Return value: None. - - void AddActorToTeamRoster(Actor * pActorToAdd); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveActorToTeamRoster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes actor from internal team roster -// Arguments: Pointer to actor -// Return value: None. - - void RemoveActorFromTeamRoster(Actor * pActorToRem); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ValidateMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Goes through and checks that all MOID's have valid MO pointers -// associated with them. This shuold only be used for testing, as it will -// crash the app if validation fails. -// Arguments: None. -// Return value: All MOIDs valid. - - bool ValidateMOIDs(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ValidMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MovableObject pointer points to an -// MO that's currently active in the simulation, and kept by this -// MovableMan. Internal optimization is made so that the same MO can -// efficiently be checked many times during the same frame. -// Arguments: A pointer to the MovableObject to check for being actively kept by -// this MovableMan. -// Return value: Whether the MO instance was found in the active list or not. - - bool ValidMO(const MovableObject *pMOToCheck); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MovableObject is an active Actor kept -// by this MovableMan or not. -// Arguments: A pointer to the MovableObject to check for Actorness. -// Return value: Whether the object was found in the Actor list or not. - - bool IsActor(const MovableObject *pMOToCheck); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MovableObject is an active Item kept -// by this MovableMan or not. -// Arguments: A pointer to the MovableObject to check for Itemness. -// Return value: Whether the object was found in the Item list or not. - - bool IsDevice(const MovableObject *pMOToCheck); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsParticle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MovableObject is an active Item kept -// by this MovableMan or not. -// Arguments: A pointer to the MovableObject to check for Itemness. -// Return value: Whether the object was found in the Particle list or not. - - bool IsParticle(const MovableObject *pMOToCheck); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsOfActor -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the passed in MOID is that of an MO which either is -// or is parented to an active Actor by this MovableMan, or not. -// Arguments: An MOID to check for Actorness. -// Return value: Whether the object was found or owned by an MO in the Actor list or not. - - bool IsOfActor(MOID checkMOID); - - /// - /// Gives a unique, contiguous id per-actor. This is regenerated every frame. - /// - /// The actor to get a contiguous id for. - /// A contiguous id for the actor. Returns -1 if the actor doesn't exist in MovableMan. - /// This function is used for AI throttling. - int GetContiguousActorID(const Actor *actor) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRootMOID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Produces the root MOID of the MOID of a potential child MO to another MO. -// Arguments: An MOID to get the root MOID of. -// Return value: The MOID of the root MO of the MO the passed-in MOID represents. This -// will be the same as the MOID passed in if the MO is a root itself. It will -// be equal to g_NoMOID if the MOID isn't allocated to an MO. - - MOID GetRootMOID(MOID checkMOID); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a MovableObject from the any and all internal lists of MO:s. -// After the MO is removed, ownership is effectively released and -// transferred to whatever client called this method. -// Arguments: A pointer to the MovableObject to remove. -// Return value: Whether the object was found in MovableMan's custody, and consequently -// removed. If the MO entry wasn't found, false is returned. - - bool RemoveMO(MovableObject *pMOToRem); - - /// - /// Kills and destroys all Actors of a specific Team. - /// - /// The team to annihilate. If NoTeam is passed in, then NO Actors die. - /// How many Actors were killed. - int KillAllTeamActors(int teamToKill) const; - - /// - /// Kills and destroys all enemy Actors of a specific Team. - /// - /// The team to NOT annihilate. If NoTeam is passed in, then ALL Actors die. - /// How many Actors were killed. - int KillAllEnemyActors(int teamNotToKill = Activity::NoTeam) const; - - /// - /// Adds all Actors in MovableMan to the given list. - /// - /// Whether or not ownership of the Actors should be transferred from MovableMan to the list. - /// The list to be filled with Actors. - /// The team to get Actors of. If NoTeam, then all teams will be used. - /// Whether or not to get brain Actors. - /// The number of Actors added to the list. - int GetAllActors(bool transferOwnership, std::list &actorList, int onlyTeam = -1, bool noBrains = false); - - /// - /// Whether or not ownershp of the items shoudl be transferred from MovableMan to the list. - /// The list to be filled with items. - /// The number of items added to the list. - int GetAllItems(bool transferOwnership, std::list &itemList); - - /// - /// Adds all particles in MovableMan to the given list. - /// - /// Whether or not ownership of the particles should be transferred from MovableMan to the list. - /// The list to be filled with particles. - /// The number of particles added to the list. - int GetAllParticles(bool transferOwnership, std::list &particleList); - - /// - /// Opens all doors and keeps them open until this is called again with false. - /// - /// Whether to open all doors (true), or close all doors (false). - /// Which team to open doors for. NoTeam means all teams. - void OpenAllDoors(bool open = true, int team = Activity::NoTeam) const; - - /// - /// Temporarily erases or redraws any material door representations of a specific team. - /// Used to make pathfinding work better, allowing Actors to navigate through firendly bases despite the door material layer. - /// - /// Whether to erase door material, thereby overriding it, or redraw it and undo the override. - /// Which team to do this for, NoTeam means all teams. - void OverrideMaterialDoors(bool eraseDoorMaterial, int team = Activity::NoTeam) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RegisterAlarmEvent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Registers an AlarmEvent to notify things around that somehting alarming -// like a gunshot or explosion just happened. -// Arguments: The AlarmEvent to register. -// Return value: None. - - void RegisterAlarmEvent(const AlarmEvent &newEvent); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAlarmEvents -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the list of AlarmEvent:s from last frame's update. -// Arguments: None. -// Return value: The const list of AlarmEvent:s. - - const std::vector & GetAlarmEvents() const { return m_AlarmEvents; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsParticleSettlingEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whetehr particles are set to get copied to the terrain upon -// settling -// Arguments: None. -// Return value: Whether enabled or not. - - bool IsParticleSettlingEnabled() { return m_SettlingEnabled; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnableParticleSettling -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether particles will get copied into the terrain upon them -// settling down. -// Arguments: Whether to enable or not. -// Return value: None. - - void EnableParticleSettling(bool enable = true) { m_SettlingEnabled = enable; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsMOSubtractionEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether MO's sihouettes can get subtracted from the terrain at all. -// Arguments: None. -// Return value: Whether enabled or not. - - bool IsMOSubtractionEnabled() { return m_MOSubtractionEnabled; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RedrawOverlappingMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces all objects potnetially overlapping a specific MO to re-draw -// this MOID representations onto the MOID bitmap. -// Arguments: A pointer to the MO to check for overlaps against. Ownerhip is NOT -// transferred. -// Return value: None. - - void RedrawOverlappingMOIDs(MovableObject *pOverlapsThis); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this MovableMan. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawMatter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MovableMan's all MO's current material representations to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void DrawMatter(BITMAP *pTargetBitmap, Vector& targetPos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateDrawMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the MOIDs of all current MOs and draws their ID's to a BITMAP -// of choice. If there are more than 255 MO's to draw, some will not be. -// Arguments: A pointer to a BITMAP to draw on. -// Return value: None. - - void UpdateDrawMOIDs(BITMAP *pTargetBitmap); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MovableMan's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the HUDs of all MovableObject:s of this MovableMan to a BITMAP -// of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Which player's screen is being drawn. Tis affects which actor's HUDs -// get drawn. -// Return value: None. - - void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int which = 0, bool playerControlled = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: VerifyMOIDIndex -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Verifieis whether all elements of MOID index has correct ID. Should be used in Debug mode only. -// Arguments: None. -// Return value: None. - - void VerifyMOIDIndex(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RegisterObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Registers an object in a global Map collection so it could be found later with FindObjectByUniqueId -// Arguments: MO to register. -// Return value: None. - - void RegisterObject(MovableObject * mo); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UnregisterObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes an object from the global lookup collection -// Arguments: MO to remove. -// Return value: None. - - void UnregisterObject(MovableObject * mo); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FindObjectByUniqueId -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Uses a global lookup map to find an object by it's unique id. -// Arguments: Unique Id to look for. -// Return value: Object found or 0 if not found any. - - MovableObject * FindObjectByUniqueID(long int id) { if (m_KnownObjects.count(id) > 0) return m_KnownObjects[id]; else return 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetKnownObjectsCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the size of the object registry collection -// Arguments: None. -// Return value: Size of the objects registry. - - unsigned int GetKnownObjectsCount() { return m_KnownObjects.size(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSimUpdateFrameNumber -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the current sim update frame number -// Arguments: None. -// Return value: Current sim update frame number. - - unsigned int GetSimUpdateFrameNumber() const { return m_SimUpdateFrameNumber; } - - /// - /// Gets pointers to the MOs that are within the given Box, and whose team is not ignored. - /// - /// The Box to get MOs within. - /// The team to ignore. - /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. - /// Pointers to the MOs that are within the given Box, and whose team is not ignored. - const std::vector *GetMOsInBox(const Box &box, int ignoreTeam, bool getsHitByMOsOnly) const; - - /// - /// Gets pointers to the MOs that are within the given Box, and whose team is not ignored. - /// - /// The Box to get MOs within. - /// The team to ignore. - /// Pointers to the MOs that are within the given Box, and whose team is not ignored. - const std::vector *GetMOsInBox(const Box &box, int ignoreTeam) const { return GetMOsInBox(box, ignoreTeam, false); } - - /// - /// Gets pointers to the MOs that are within the given Box. - /// - /// The Box to get MOs within. - /// Pointers to the MOs that are within the given Box. - const std::vector * GetMOsInBox(const Box &box) const { return GetMOsInBox(box, Activity::NoTeam); } - - /// - /// Gets pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. - /// - /// The position to check for MOs in. - /// The radius to check for MOs within. - /// The team to ignore. - /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. - /// Pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. - const std::vector *GetMOsInRadius(const Vector ¢re, float radius, int ignoreTeam, bool getsHitByMOsOnly) const; - - /// - /// Gets pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. - /// - /// The position to check for MOs in. - /// The radius to check for MOs within. - /// The team to ignore. - /// Pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. - const std::vector *GetMOsInRadius(const Vector ¢re, float radius, int ignoreTeam) const { return GetMOsInRadius(centre, radius, ignoreTeam, false); } - - /// - /// Gets pointers to the MOs that are within the specified radius of the given centre position. - /// - /// The position to check for MOs in. - /// The radius to check for MOs within. - /// Pointers to the MOs that are within the specified radius of the given centre position. - const std::vector * GetMOsInRadius(const Vector ¢re, float radius) const { return GetMOsInRadius(centre, radius, Activity::NoTeam); } - - /// - /// Runs a lua function on all MOs in the simulation, including owned child MOs. - /// - void RunLuaFunctionOnAllMOs(const std::string& functionName, bool includeAdded, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); - - /// - /// Clears all cached lua functions on all MOs, including owned child MOs. - /// - void ReloadLuaScripts(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // All actors in the scene - std::deque m_Actors; - // A map to give a unique contiguous identifier per-actor. This is re-created per frame. - std::unordered_map m_ContiguousActorIDs; - // List of items that are pickup-able by actors - std::deque m_Items; - // List of free, dead particles flying around - std::deque m_Particles; - // These are the actors/items/particles which were added during a frame. - // They are moved to the containers above at the end of the frame. - std::deque m_AddedActors; - std::deque m_AddedItems; - std::deque m_AddedParticles; - - // Currently active MOs in the simulation. This is required because the code is awful and ownership isn't transported to/from lua in any sensible way. - // It's entirely possible that stuff is deleted in the game but a reference to it is kept in Lua. Which is awful. Obviously. - // Or perhaps even more concerningly, stuff can be deleted, re-allocated over the same space, and then readded to movableman. Which even this solution does nothing to fix. - // Anyways, until we fix up ownership semantics... this is the best we can do. - std::unordered_set m_ValidActors; - std::unordered_set m_ValidItems; - std::unordered_set m_ValidParticles; - - // Mutexes to ensure MOs aren't being removed from separate threads at the same time - std::mutex m_ActorsMutex; - std::mutex m_ItemsMutex; - std::mutex m_ParticlesMutex; - - // Mutexes to ensure MOs aren't being added from separate threads at the same time - std::mutex m_AddedActorsMutex; - std::mutex m_AddedItemsMutex; - std::mutex m_AddedParticlesMutex; - - // Mutex to ensure objects aren't registered/deregistered from separate threads at the same time - std::mutex m_ObjectRegisteredMutex; - - // Mutex to ensure actors don't change team roster from seperate threads at the same time - std::mutex m_ActorRosterMutex; - - // Async to draw MOIDs while rendering - std::future m_DrawMOIDsTask; - - // Roster of each team's actors, sorted by their X positions in the scene. Actors not owned here - std::list m_ActorRoster[Activity::MaxTeamCount]; - // Whether to draw HUD lines between the actors of a specific team - bool m_SortTeamRoster[Activity::MaxTeamCount]; - // Every team's MO footprint - int m_TeamMOIDCount[Activity::MaxTeamCount]; - - // The alarm events on the scene where something alarming happened, for use with AI firings awareness os they react to shots fired etc. - // This is the last frame's events, is the one for Actors to poll for events, should be cleaned out and refilled each frame. - std::vector m_AlarmEvents; - // The alarm events on the scene where something alarming happened, for use with AI firings awareness os they react to shots fired etc. - // This is the current frame's events, will be filled up during MovableMan Updates, should be transferred to Last Frame at end of update. - std::vector m_AddedAlarmEvents; - - // Mutexes to ensure alarm events aren't being added from separate threads at the same time - std::mutex m_AddedAlarmEventsMutex; - - // The list created each frame to register all the current MO's - std::vector m_MOIDIndex; - - // The ration of terrain pixels to be converted into MOPixel:s upon - // deep impact of MO. - float m_SplashRatio; - // The maximum number of loose items allowed. - int m_MaxDroppedItems; - - // Whether settling of particles is enabled or not - bool m_SettlingEnabled; - // Whtehr MO's vcanng et subtracted form the terrain at all - bool m_MOSubtractionEnabled; - - unsigned int m_SimUpdateFrameNumber; - - // Global map which stores all objects so they could be foud by their unique ID - std::map m_KnownObjects; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MovableMan, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - /// - /// Travels all of our MOs, updating their location/velocity/physical characteristics. - /// - void Travel(); - - /// - /// Updates the controllers of all the actors we own. - /// This is needed for a tricky reason - we want the controller from the activity to override the normal controller state - /// So we need to update the controller state prior to activity, so the changes from activity are layered on top. - /// - void UpdateControllers(); - - /// - /// Updates all things that need to be done before we update the controllers. - /// This is needed because of a very awkward and ugly old code path where controllers were updated in the middle of update, and various mods relied of this behaviour for actions that were therefore delayed by a frame - /// Ideally we wouldn't need this, but this is all very fragile code and I'd prefer to avoid breaking things. - /// - void PreControllerUpdate(); - - // Disallow the use of some implicit methods. - MovableMan(const MovableMan &reference) = delete; - MovableMan & operator=(const MovableMan &rhs) = delete; - -}; +namespace RTE { + + class MovableObject; + class Actor; + class HeldDevice; + class MOPixel; + class MOSprite; + class AHuman; + class SceneLayer; + class SceneObject; + class Box; + class LuabindObjectWrapper; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Struct: AlarmEvent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A struct to keep all data about a an alarming event for the AI Actors. + // Parent(s): None. + // Class history: 10/3/2008 AlarmEvent created. + + struct AlarmEvent { + AlarmEvent() { + m_ScenePos.Reset(); + m_Team = Activity::NoTeam; + m_Range = 1.0F; + } + // TODO: Stop relying on screen width for this shit! + AlarmEvent(const Vector& pos, int team = Activity::NoTeam, float range = 1.0F); + + // Absolute position in the scene where this occurred + Vector m_ScenePos; + // The team of whatever object that caused this event + Activity::Teams m_Team; + // The range multiplier, that this alarming event can be heard + float m_Range; + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: MovableMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The singleton manager of all movable objects in the RTE. + // Parent(s): Singleton, Serializable. + // Class history: 12/25/2001 MovableMan created. + + class MovableMan : public Singleton, public Serializable { + friend class SettingsMan; + friend struct ManagerLuaBindings; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + SerializableClassNameGetter; + SerializableOverrideMethods; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: MovableMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a MovableMan object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + MovableMan() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~MovableMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a MovableMan object before deletion + // from system memory. + // Arguments: None. + + ~MovableMan() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MovableMan object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Initialize(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire MovableMan, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the MovableMan object. + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMOFromID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a MO from its MOID. Note that MOID's are only valid during the + // same frame as they were assigned to the MOs! + // Arguments: The MOID to get the matching MO from. + // Return value: A pointer to the requested MovableObject instance. 0 if no MO with that + // MOID was found. 0 if 0 was passed in as MOID (no MOID). Ownership is + // *NOT* transferred!! + + MovableObject* GetMOFromID(MOID whichID); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMOIDCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of MOID's currently in use this frame. + // Arguments: None. + // Return value: The count of MOIDs in use this frame. + + int GetMOIDCount() { return m_MOIDIndex.size(); } + + /// + /// Gets a MOID from pixel coordinates in the Scene. + /// + /// The X coordinate of the Scene pixel to get the MOID of. + /// The Y coordinate of the Scene pixel to get the MOID of. + /// The collection of MOIDs to check the against the specified coordinates. + /// The topmost MOID currently at the specified pixel coordinates. + MOID GetMOIDPixel(int pixelX, int pixelY, const std::vector& moidList); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTeamMOIDCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns MO count for specified team + // Arguments: Team to count MO's + // Return value: MO's count owned by this team + + int GetTeamMOIDCount(int team) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PurgeAllMOs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears out all MovableObject:s out of this. Effectively empties the world + // of anything moving, without resetting all of this' settings. + // Arguments: None. + // Return value: None. + + void PurgeAllMOs(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNextActorInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the first Actor in the internal Actor list that is + // of a specifc group, alternatively the first one AFTER a specific actor! + // Arguments: Which group to try to get an Actor for. + // A pointer to an Actor to use as starting point in the forward search. + // Ownership NOT xferred! + // Return value: An Actor pointer to the requested team's first Actor encountered + // in the list. 0 if there are no Actors of that team. + + Actor* GetNextActorInGroup(std::string group, Actor* pAfterThis = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPrevActorInGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the last Actor in the internal Actor list that is + // of a specifc group, alternatively the last one BEFORE a specific actor! + // Arguments: Which group to try to get an Actor for. + // A pointer to an Actor to use as starting point in the backward search. + // Ownership NOT xferred! + // Return value: An Actor pointer to the requested team's last Actor encountered + // in the list. 0 if there are no Actors of that team. + + Actor* GetPrevActorInGroup(std::string group, Actor* pBeforeThis = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTeamRoster + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the list of all actors on one team, ordered by their X positions. + // Arguments: Which team to try to get the roster for. + // Return value: A pointer to the list of all the actors on the specified team, sorted + // ascending by their X posistions. Ownership of the list or contained + // actors is NOT transferred! + + std::list* GetTeamRoster(int team = 0) { return &(m_ActorRoster[team]); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNextTeamActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the first Actor in the internal Actor list that is + // of a specifc team, alternatively the first one AFTER a specific actor! + // Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + // A pointer to an Actor to use as starting point in the forward search. + // Ownership NOT xferred! + // Return value: An Actor pointer to the requested team's first Actor encountered + // in the list. 0 if there are no Actors of that team. + + Actor* GetNextTeamActor(int team = 0, Actor* pAfterThis = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPrevTeamActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the last Actor in the internal Actor list that is + // of a specifc team, alternatively the last one BEFORE a specific actor! + // Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + // A pointer to an Actor to use as starting point in the backward search. + // Ownership NOT xferred! + // Return value: An Actor pointer to the requested team's last Actor encountered + // in the list. 0 if there are no Actors of that team. + + Actor* GetPrevTeamActor(int team = 0, Actor* pBeforeThis = 0); + + /// + /// Get a pointer to an Actor in the internal Actor list that is of a specifc team and closest to a specific scene point. + /// + /// Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + /// The player to get the Actor for. This affects which brain can be marked. + /// The Scene point to search for the closest to. + /// The maximum radius around that scene point to search. + /// A Vector to be filled out with the distance of the returned closest to the search point. Will be unaltered if no object was found within radius. + /// An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! + /// An Actor pointer to the requested team's Actor closest to the Scene point, but not outside the max radius. If no Actor other than the excluded one was found within the radius of the point, nullptr is returned. + Actor* GetClosestTeamActor(int team, int player, const Vector& scenePoint, int maxRadius, Vector& getDistance, const Actor* excludeThis = nullptr) { return GetClosestTeamActor(team, player, scenePoint, maxRadius, getDistance, false, excludeThis); } + + /// + /// Get a pointer to an Actor in the internal Actor list that is of a specifc team and closest to a specific scene point. + /// + /// Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + /// The player to get the Actor for. This affects which brain can be marked. + /// The Scene point to search for the closest to. + /// The maximum radius around that scene point to search. + /// A Vector to be filled out with the distance of the returned closest to the search point. Will be unaltered if no object was found within radius. + /// Whether to only get Actors that are flagged as player controllable. + /// An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! + /// An Actor pointer to the requested team's Actor closest to the Scene point, but not outside the max radius. If no Actor other than the excluded one was found within the radius of the point, nullptr is returned. + Actor* GetClosestTeamActor(int team, int player, const Vector& scenePoint, int maxRadius, Vector& getDistance, bool onlyPlayerControllableActors, const Actor* excludeThis = nullptr); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetClosestEnemyActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to an Actor in the internal Actor list that is is not of + // the specified team and closest to a specific scene point. + // Arguments: Which team to try to get an enemy Actor for. NoTeam means all teams. + // The Scene point to search for the closest to. + // The maximum radius around that scene point to search. + // A Vector to be filled out with the distance of the returned closest to + // the search point. Will be unaltered if no object was found within radius. + // Return value: An Actor pointer to the enemy closest to the Scene + // point, but not outside the max radius. If no Actor + // was found within the radius of the point, 0 is returned. + + Actor* GetClosestEnemyActor(int team, const Vector& scenePoint, int maxRadius, Vector& getDistance); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFirstTeamActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to first best Actor in the internal Actor list that is + // of a specifc team. + // Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + // The player to get the Actor for. This affects which brain can be marked. + // Return value: An Actor pointer to the first one of the requested team. If no Actor + // is in that team, 0 is returned. + + Actor* GetFirstTeamActor(int team, int player) { + Vector temp; + return GetClosestTeamActor(team, player, Vector(), 10000000, temp); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetClosestActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to an Actor in the internal Actor list that is closest + // to a specific scene point. + // Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + // The Scene point to search for the closest to. + // The maximum radius around that scene point to search. + // A Vector to be filled out with the distance of the returned closest to + // the search point. Will be unaltered if no object was found within radius. + // An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! + // Return value: An Actor pointer to the requested Actor closest to the Scene + // point, but not outside the max radius. If no Actor other than the + // excluded one was found within the radius of the point, 0 is returned. + + Actor* GetClosestActor(const Vector& scenePoint, int maxRadius, Vector& getDistance, const Actor* pExcludeThis = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetClosestBrainActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the brain actor of a specific team that is closest to + // a scene point. OWNERSHIP IS NOT TRANSFERRED! + // Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. + // The point in the scene where to look for the closest opposite team brain. + // Return value: An Actor pointer to the requested team's brain closest to the point. + // 0 if there are no brains of that team. OWNERSHIP IS NOT TRANSFERRED! + + Actor* GetClosestBrainActor(int team, const Vector& scenePoint) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFirstBrainActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the brain actor of a specific team that is closest to + // a scene point. OWNERSHIP IS NOT TRANSFERRED! + // Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. + // The point in the scene where to look for the closest opposite team brain. + // Return value: An Actor pointer to the requested team's brain closest to the point. + // 0 if there are no brains of that team. OWNERSHIP IS NOT TRANSFERRED! + + Actor* GetFirstBrainActor(int team) const { return GetClosestBrainActor(team, Vector()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetClosestOtherBrainActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the brain actor NOT of a specific team that is closest + // to a scene point. OWNERSHIP IS NOT TRANSFERRED! + // Arguments: Which team to NOT get the brain for. 0 means first team, 1 means 2nd. + // The point where to look for the closest brain not of this team. + // Return value: An Actor pointer to the requested brain closest to the point. + // 0 if there are no brains not on that team. OWNERSHIP IS NOT TRANSFERRED! + + Actor* GetClosestOtherBrainActor(int notOfTeam, const Vector& scenePoint) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetFirstOtherBrainActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the brain actor NOT of a specific team. OWNERSHIP IS NOT TRANSFERRED! + // Arguments: Which team to NOT get the brain for. 0 means first team, 1 means 2nd. + // Return value: An Actor pointer to the requested brain of that team. + // 0 if there are no brains not on that team. OWNERSHIP IS NOT TRANSFERRED! + + Actor* GetFirstOtherBrainActor(int notOfTeam) const { return GetClosestOtherBrainActor(notOfTeam, Vector()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetUnassignedBrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Get a pointer to the first brain actor of a specific team which hasn't + // been assigned to a player yet. + // Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. + // Return value: An Actor pointer to the requested team's first brain encountered + // in the list that hasn't been assigned to a player. 0 if there are no + // unassigned brains of that team. + + Actor* GetUnassignedBrain(int team = 0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActorCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of actors currently held. + // Arguments: None. + // Return value: The number of actors. + + long GetActorCount() const { return m_Actors.size(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetParticleCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the number of particles (MOPixel:s) currently held. + // Arguments: None. + // Return value: The number of particles. + + long GetParticleCount() const { return m_Particles.size(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSplashRatio + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the global setting for how much splash MOPixels should be created + // an MO penetrates the terrain deeply. + // Arguments: None. + // Return value: A float with the global splash amount setting, form 1.0 to 0.0. + + float GetSplashRatio() const { return m_SplashRatio; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMaxDroppedItems + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the max number of dropped items that will be reached before the + // first dropped with be copied to the terrain. + // Arguments: An int spefifying the limit. + // Return value: None. + + void SetMaxDroppedItems(int newLimit) { m_MaxDroppedItems = newLimit; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMaxDroppedItems + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the max number of dropped items that will be reached before the + // first dropped with be copied to the terrain. + // Arguments: None. + // Return value: An int spefifying the limit. + + int GetMaxDroppedItems() const { return m_MaxDroppedItems; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SortTeamRoster + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets this to draw HUD lines for a specific team's roster this frame. + // Arguments: Which team to have lines drawn of. + // Return value: None. + + void SortTeamRoster(int team) { m_SortTeamRoster[team] = true; } + + /// + /// Adds a MovableObject to this, after it is determined what it is and the best way to add it is. E.g. if it's an Actor, it will be added as such. Ownership IS transferred! + /// + /// A pointer to the MovableObject to add. Ownership IS transferred! + /// Whether the MovableObject was successfully added or not. Note that Ownership IS transferred either way, but the MovableObject will be deleted if this is not successful. + bool AddMO(MovableObject* movableObjectToAdd); + + /// + /// Adds an Actor to the internal list of Actors. Destruction and deletion will be taken care of automatically. Ownership IS transferred! + /// + /// A pointer to the Actor to add. Ownership IS transferred! + void AddActor(Actor* actorToAdd); + + /// + /// Adds a pickup-able item to the internal list of items. Destruction and deletion will be taken care of automatically. Ownership IS transferred! + /// + /// A pointer to the item to add. Ownership IS transferred! + void AddItem(HeldDevice* itemToAdd); + + /// + /// Adds a MovableObject to the internal list of particles. Destruction and deletion will be taken care of automatically. Ownership IS transferred! + /// + /// A pointer to the MovableObject to add. Ownership is transferred! + void AddParticle(MovableObject* particleToAdd); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes an Actor from the internal list of MO:s. After the Actor is + // removed, ownership is effectively released and transferred to whatever + // client called this method. + // Arguments: A pointer to the MovableObject to remove. + // Return value: Whether the object was found in the particle list, and consequently + // removed. If the particle entry wasn't found, false is returned. + + Actor* RemoveActor(MovableObject* pActorToRem); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a pickup-able MovableObject item from the internal list of + // MO:s. After the item is removed, ownership is effectively released and + // transferred to whatever client called this method. + // Arguments: A pointer to the MovableObject to remove. + // Return value: Whether the object was found in the particle list, and consequently + // removed. If the particle entry wasn't found, false is returned. + + MovableObject* RemoveItem(MovableObject* pItemToRem); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveParticle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a MovableObject from the internal list of MO:s. After the + // MO is removed, ownership is effectively released and transferred to + // whatever client called this method. + // Arguments: A pointer to the MovableObject to remove. + // Return value: Whether the object was found in the particle list, and consequently + // removed. If the particle entry wasn't found, false is returned. + + MovableObject* RemoveParticle(MovableObject* pMOToRem); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeActorTeam + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes actor team and updates team rosters. + // Arguments: Pointer to actor, new team value + // Return value: None. + + void ChangeActorTeam(Actor* pActor, int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddActorToTeamRoster + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds actor to internal team roster + // Arguments: Pointer to actor + // Return value: None. + + void AddActorToTeamRoster(Actor* pActorToAdd); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveActorToTeamRoster + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes actor from internal team roster + // Arguments: Pointer to actor + // Return value: None. + + void RemoveActorFromTeamRoster(Actor* pActorToRem); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ValidateMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Goes through and checks that all MOID's have valid MO pointers + // associated with them. This shuold only be used for testing, as it will + // crash the app if validation fails. + // Arguments: None. + // Return value: All MOIDs valid. + + bool ValidateMOIDs(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ValidMO + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MovableObject pointer points to an + // MO that's currently active in the simulation, and kept by this + // MovableMan. Internal optimization is made so that the same MO can + // efficiently be checked many times during the same frame. + // Arguments: A pointer to the MovableObject to check for being actively kept by + // this MovableMan. + // Return value: Whether the MO instance was found in the active list or not. + + bool ValidMO(const MovableObject* pMOToCheck); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MovableObject is an active Actor kept + // by this MovableMan or not. + // Arguments: A pointer to the MovableObject to check for Actorness. + // Return value: Whether the object was found in the Actor list or not. + + bool IsActor(const MovableObject* pMOToCheck); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsDevice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MovableObject is an active Item kept + // by this MovableMan or not. + // Arguments: A pointer to the MovableObject to check for Itemness. + // Return value: Whether the object was found in the Item list or not. + + bool IsDevice(const MovableObject* pMOToCheck); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsParticle + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MovableObject is an active Item kept + // by this MovableMan or not. + // Arguments: A pointer to the MovableObject to check for Itemness. + // Return value: Whether the object was found in the Particle list or not. + + bool IsParticle(const MovableObject* pMOToCheck); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsOfActor + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the passed in MOID is that of an MO which either is + // or is parented to an active Actor by this MovableMan, or not. + // Arguments: An MOID to check for Actorness. + // Return value: Whether the object was found or owned by an MO in the Actor list or not. + + bool IsOfActor(MOID checkMOID); + + /// + /// Gives a unique, contiguous id per-actor. This is regenerated every frame. + /// + /// The actor to get a contiguous id for. + /// A contiguous id for the actor. Returns -1 if the actor doesn't exist in MovableMan. + /// This function is used for AI throttling. + int GetContiguousActorID(const Actor* actor) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRootMOID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Produces the root MOID of the MOID of a potential child MO to another MO. + // Arguments: An MOID to get the root MOID of. + // Return value: The MOID of the root MO of the MO the passed-in MOID represents. This + // will be the same as the MOID passed in if the MO is a root itself. It will + // be equal to g_NoMOID if the MOID isn't allocated to an MO. + + MOID GetRootMOID(MOID checkMOID); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveMO + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a MovableObject from the any and all internal lists of MO:s. + // After the MO is removed, ownership is effectively released and + // transferred to whatever client called this method. + // Arguments: A pointer to the MovableObject to remove. + // Return value: Whether the object was found in MovableMan's custody, and consequently + // removed. If the MO entry wasn't found, false is returned. + + bool RemoveMO(MovableObject* pMOToRem); + + /// + /// Kills and destroys all Actors of a specific Team. + /// + /// The team to annihilate. If NoTeam is passed in, then NO Actors die. + /// How many Actors were killed. + int KillAllTeamActors(int teamToKill) const; + + /// + /// Kills and destroys all enemy Actors of a specific Team. + /// + /// The team to NOT annihilate. If NoTeam is passed in, then ALL Actors die. + /// How many Actors were killed. + int KillAllEnemyActors(int teamNotToKill = Activity::NoTeam) const; + + /// + /// Adds all Actors in MovableMan to the given list. + /// + /// Whether or not ownership of the Actors should be transferred from MovableMan to the list. + /// The list to be filled with Actors. + /// The team to get Actors of. If NoTeam, then all teams will be used. + /// Whether or not to get brain Actors. + /// The number of Actors added to the list. + int GetAllActors(bool transferOwnership, std::list& actorList, int onlyTeam = -1, bool noBrains = false); + + /// + /// Whether or not ownershp of the items shoudl be transferred from MovableMan to the list. + /// The list to be filled with items. + /// The number of items added to the list. + int GetAllItems(bool transferOwnership, std::list& itemList); + + /// + /// Adds all particles in MovableMan to the given list. + /// + /// Whether or not ownership of the particles should be transferred from MovableMan to the list. + /// The list to be filled with particles. + /// The number of particles added to the list. + int GetAllParticles(bool transferOwnership, std::list& particleList); + + /// + /// Opens all doors and keeps them open until this is called again with false. + /// + /// Whether to open all doors (true), or close all doors (false). + /// Which team to open doors for. NoTeam means all teams. + void OpenAllDoors(bool open = true, int team = Activity::NoTeam) const; + + /// + /// Temporarily erases or redraws any material door representations of a specific team. + /// Used to make pathfinding work better, allowing Actors to navigate through firendly bases despite the door material layer. + /// + /// Whether to erase door material, thereby overriding it, or redraw it and undo the override. + /// Which team to do this for, NoTeam means all teams. + void OverrideMaterialDoors(bool eraseDoorMaterial, int team = Activity::NoTeam) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RegisterAlarmEvent + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Registers an AlarmEvent to notify things around that somehting alarming + // like a gunshot or explosion just happened. + // Arguments: The AlarmEvent to register. + // Return value: None. + + void RegisterAlarmEvent(const AlarmEvent& newEvent); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAlarmEvents + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the list of AlarmEvent:s from last frame's update. + // Arguments: None. + // Return value: The const list of AlarmEvent:s. + + const std::vector& GetAlarmEvents() const { return m_AlarmEvents; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsParticleSettlingEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whetehr particles are set to get copied to the terrain upon + // settling + // Arguments: None. + // Return value: Whether enabled or not. + + bool IsParticleSettlingEnabled() { return m_SettlingEnabled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnableParticleSettling + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether particles will get copied into the terrain upon them + // settling down. + // Arguments: Whether to enable or not. + // Return value: None. + + void EnableParticleSettling(bool enable = true) { m_SettlingEnabled = enable; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsMOSubtractionEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether MO's sihouettes can get subtracted from the terrain at all. + // Arguments: None. + // Return value: Whether enabled or not. + + bool IsMOSubtractionEnabled() { return m_MOSubtractionEnabled; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RedrawOverlappingMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces all objects potnetially overlapping a specific MO to re-draw + // this MOID representations onto the MOID bitmap. + // Arguments: A pointer to the MO to check for overlaps against. Ownerhip is NOT + // transferred. + // Return value: None. + + void RedrawOverlappingMOIDs(MovableObject* pOverlapsThis); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this MovableMan. Supposed to be done every frame. + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawMatter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this MovableMan's all MO's current material representations to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void DrawMatter(BITMAP* pTargetBitmap, Vector& targetPos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateDrawMOIDs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the MOIDs of all current MOs and draws their ID's to a BITMAP + // of choice. If there are more than 255 MO's to draw, some will not be. + // Arguments: A pointer to a BITMAP to draw on. + // Return value: None. + + void UpdateDrawMOIDs(BITMAP* pTargetBitmap); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this MovableMan's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawHUD + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the HUDs of all MovableObject:s of this MovableMan to a BITMAP + // of choice. + // Arguments: A pointer to a BITMAP to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Which player's screen is being drawn. Tis affects which actor's HUDs + // get drawn. + // Return value: None. + + void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0, bool playerControlled = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: VerifyMOIDIndex + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Verifieis whether all elements of MOID index has correct ID. Should be used in Debug mode only. + // Arguments: None. + // Return value: None. + + void VerifyMOIDIndex(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RegisterObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Registers an object in a global Map collection so it could be found later with FindObjectByUniqueId + // Arguments: MO to register. + // Return value: None. + + void RegisterObject(MovableObject* mo); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UnregisterObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes an object from the global lookup collection + // Arguments: MO to remove. + // Return value: None. + + void UnregisterObject(MovableObject* mo); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FindObjectByUniqueId + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Uses a global lookup map to find an object by it's unique id. + // Arguments: Unique Id to look for. + // Return value: Object found or 0 if not found any. + + MovableObject* FindObjectByUniqueID(long int id) { + if (m_KnownObjects.count(id) > 0) + return m_KnownObjects[id]; + else + return 0; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetKnownObjectsCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the size of the object registry collection + // Arguments: None. + // Return value: Size of the objects registry. + + unsigned int GetKnownObjectsCount() { return m_KnownObjects.size(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSimUpdateFrameNumber + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the current sim update frame number + // Arguments: None. + // Return value: Current sim update frame number. + + unsigned int GetSimUpdateFrameNumber() const { return m_SimUpdateFrameNumber; } + + /// + /// Gets pointers to the MOs that are within the given Box, and whose team is not ignored. + /// + /// The Box to get MOs within. + /// The team to ignore. + /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. + /// Pointers to the MOs that are within the given Box, and whose team is not ignored. + const std::vector* GetMOsInBox(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const; + + /// + /// Gets pointers to the MOs that are within the given Box, and whose team is not ignored. + /// + /// The Box to get MOs within. + /// The team to ignore. + /// Pointers to the MOs that are within the given Box, and whose team is not ignored. + const std::vector* GetMOsInBox(const Box& box, int ignoreTeam) const { return GetMOsInBox(box, ignoreTeam, false); } + + /// + /// Gets pointers to the MOs that are within the given Box. + /// + /// The Box to get MOs within. + /// Pointers to the MOs that are within the given Box. + const std::vector* GetMOsInBox(const Box& box) const { return GetMOsInBox(box, Activity::NoTeam); } + + /// + /// Gets pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. + /// + /// The position to check for MOs in. + /// The radius to check for MOs within. + /// The team to ignore. + /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. + /// Pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. + const std::vector* GetMOsInRadius(const Vector& centre, float radius, int ignoreTeam, bool getsHitByMOsOnly) const; + + /// + /// Gets pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. + /// + /// The position to check for MOs in. + /// The radius to check for MOs within. + /// The team to ignore. + /// Pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. + const std::vector* GetMOsInRadius(const Vector& centre, float radius, int ignoreTeam) const { return GetMOsInRadius(centre, radius, ignoreTeam, false); } + + /// + /// Gets pointers to the MOs that are within the specified radius of the given centre position. + /// + /// The position to check for MOs in. + /// The radius to check for MOs within. + /// Pointers to the MOs that are within the specified radius of the given centre position. + const std::vector* GetMOsInRadius(const Vector& centre, float radius) const { return GetMOsInRadius(centre, radius, Activity::NoTeam); } + + /// + /// Runs a lua function on all MOs in the simulation, including owned child MOs. + /// + void RunLuaFunctionOnAllMOs(const std::string& functionName, bool includeAdded, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); + + /// + /// Clears all cached lua functions on all MOs, including owned child MOs. + /// + void ReloadLuaScripts(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // All actors in the scene + std::deque m_Actors; + // A map to give a unique contiguous identifier per-actor. This is re-created per frame. + std::unordered_map m_ContiguousActorIDs; + // List of items that are pickup-able by actors + std::deque m_Items; + // List of free, dead particles flying around + std::deque m_Particles; + // These are the actors/items/particles which were added during a frame. + // They are moved to the containers above at the end of the frame. + std::deque m_AddedActors; + std::deque m_AddedItems; + std::deque m_AddedParticles; + + // Currently active MOs in the simulation. This is required because the code is awful and ownership isn't transported to/from lua in any sensible way. + // It's entirely possible that stuff is deleted in the game but a reference to it is kept in Lua. Which is awful. Obviously. + // Or perhaps even more concerningly, stuff can be deleted, re-allocated over the same space, and then readded to movableman. Which even this solution does nothing to fix. + // Anyways, until we fix up ownership semantics... this is the best we can do. + std::unordered_set m_ValidActors; + std::unordered_set m_ValidItems; + std::unordered_set m_ValidParticles; + + // Mutexes to ensure MOs aren't being removed from separate threads at the same time + std::mutex m_ActorsMutex; + std::mutex m_ItemsMutex; + std::mutex m_ParticlesMutex; + + // Mutexes to ensure MOs aren't being added from separate threads at the same time + std::mutex m_AddedActorsMutex; + std::mutex m_AddedItemsMutex; + std::mutex m_AddedParticlesMutex; + + // Mutex to ensure objects aren't registered/deregistered from separate threads at the same time + std::mutex m_ObjectRegisteredMutex; + + // Mutex to ensure actors don't change team roster from seperate threads at the same time + std::mutex m_ActorRosterMutex; + + // Async to draw MOIDs while rendering + std::future m_DrawMOIDsTask; + + // Roster of each team's actors, sorted by their X positions in the scene. Actors not owned here + std::list m_ActorRoster[Activity::MaxTeamCount]; + // Whether to draw HUD lines between the actors of a specific team + bool m_SortTeamRoster[Activity::MaxTeamCount]; + // Every team's MO footprint + int m_TeamMOIDCount[Activity::MaxTeamCount]; + + // The alarm events on the scene where something alarming happened, for use with AI firings awareness os they react to shots fired etc. + // This is the last frame's events, is the one for Actors to poll for events, should be cleaned out and refilled each frame. + std::vector m_AlarmEvents; + // The alarm events on the scene where something alarming happened, for use with AI firings awareness os they react to shots fired etc. + // This is the current frame's events, will be filled up during MovableMan Updates, should be transferred to Last Frame at end of update. + std::vector m_AddedAlarmEvents; + + // Mutexes to ensure alarm events aren't being added from separate threads at the same time + std::mutex m_AddedAlarmEventsMutex; + + // The list created each frame to register all the current MO's + std::vector m_MOIDIndex; + + // The ration of terrain pixels to be converted into MOPixel:s upon + // deep impact of MO. + float m_SplashRatio; + // The maximum number of loose items allowed. + int m_MaxDroppedItems; + + // Whether settling of particles is enabled or not + bool m_SettlingEnabled; + // Whtehr MO's vcanng et subtracted form the terrain at all + bool m_MOSubtractionEnabled; + + unsigned int m_SimUpdateFrameNumber; + + // Global map which stores all objects so they could be foud by their unique ID + std::map m_KnownObjects; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MovableMan, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + /// + /// Travels all of our MOs, updating their location/velocity/physical characteristics. + /// + void Travel(); + + /// + /// Updates the controllers of all the actors we own. + /// This is needed for a tricky reason - we want the controller from the activity to override the normal controller state + /// So we need to update the controller state prior to activity, so the changes from activity are layered on top. + /// + void UpdateControllers(); + + /// + /// Updates all things that need to be done before we update the controllers. + /// This is needed because of a very awkward and ugly old code path where controllers were updated in the middle of update, and various mods relied of this behaviour for actions that were therefore delayed by a frame + /// Ideally we wouldn't need this, but this is all very fragile code and I'd prefer to avoid breaking things. + /// + void PreControllerUpdate(); + + // Disallow the use of some implicit methods. + MovableMan(const MovableMan& reference) = delete; + MovableMan& operator=(const MovableMan& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Managers/NetworkClient.cpp b/Source/Managers/NetworkClient.cpp index 662ede6044..23a5c2378f 100644 --- a/Source/Managers/NetworkClient.cpp +++ b/Source/Managers/NetworkClient.cpp @@ -16,7 +16,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::Clear() { m_LastInputSentTime = 0; @@ -53,14 +53,14 @@ namespace RTE { m_MouseButtonReleasedState[i] = -1; } // Stop all sounds received from server - for (const auto &[channelIndex, soundContainer] : m_ServerSounds) { + for (const auto& [channelIndex, soundContainer]: m_ServerSounds) { soundContainer->Stop(); delete soundContainer; } m_ServerSounds.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int NetworkClient::Initialize() { // Record the first client that connects to us so we can pass it to the ping function @@ -70,7 +70,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::Connect(std::string serverName, unsigned short serverPort, std::string playerName) { g_ConsoleMan.PrintString("CLIENT: Connecting to " + serverName); @@ -85,7 +85,7 @@ namespace RTE { g_ConsoleMan.PrintString((connectionAttempt == RakNet::CONNECTION_ATTEMPT_STARTED) ? "CLIENT: Connect request sent" : "CLIENT: Unable to connect"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::ConnectNAT(RakNet::SystemAddress address) { g_ConsoleMan.PrintString("CLIENT: Connecting to server through NAT"); @@ -97,10 +97,12 @@ namespace RTE { g_ConsoleMan.PrintString((connectionAttempt == RakNet::CONNECTION_ATTEMPT_STARTED) ? "CLIENT: Connect request sent" : "CLIENT: Unable to connect"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::Disconnect() { - if (m_IsConnected || m_IsRegistered) { SendDisconnectMsg(); } + if (m_IsConnected || m_IsRegistered) { + SendDisconnectMsg(); + } m_IsRegistered = false; m_IsConnected = false; @@ -110,7 +112,7 @@ namespace RTE { g_ConsoleMan.PrintString("CLIENT: Disconnect"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::PerformNATPunchThrough(std::string serviceServerName, unsigned short serviceServerPort, std::string playerName, std::string serverName, std::string serverPassword) { m_UseNATPunchThroughService = true; @@ -134,14 +136,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - RakNet::SystemAddress NetworkClient::ConnectBlocking(RakNet::RakPeerInterface *rakPeer, const char *address, unsigned short port) { + RakNet::SystemAddress NetworkClient::ConnectBlocking(RakNet::RakPeerInterface* rakPeer, const char* address, unsigned short port) { if (rakPeer->Connect(address, port, nullptr, 0) != RakNet::CONNECTION_ATTEMPT_STARTED) { return RakNet::UNASSIGNED_SYSTEM_ADDRESS; } - RakNet::Packet *packet; + RakNet::Packet* packet; while (true) { for (packet = rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet = rakPeer->Receive()) { if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) { @@ -155,9 +157,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned char NetworkClient::GetPacketIdentifier(RakNet::Packet *packet) const { + unsigned char NetworkClient::GetPacketIdentifier(RakNet::Packet* packet) const { if (packet == nullptr) { return 255; } @@ -169,7 +171,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::SendRegisterMsg() { MsgRegister msg = {}; @@ -177,47 +179,47 @@ namespace RTE { msg.ResolutionX = g_WindowMan.GetResX(); msg.ResolutionY = g_WindowMan.GetResY(); strncpy(msg.Name, m_PlayerName.c_str(), c_PlayerNameCharLimit); - m_Client->Send((const char *)&msg, sizeof(msg), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); + m_Client->Send((const char*)&msg, sizeof(msg), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); g_ConsoleMan.PrintString("CLIENT: Registration Sent"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::ReceiveAcceptedMsg() { g_ConsoleMan.PrintString("CLIENT: Registration accepted."); m_IsRegistered = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::SendDisconnectMsg() { MsgRegister msg = {}; msg.Id = ID_CLT_DISCONNECT; - m_Client->Send((const char *)&msg, sizeof(msg), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); + m_Client->Send((const char*)&msg, sizeof(msg), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); g_ConsoleMan.PrintString("CLIENT: Disconnection Sent"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::SendServerGUIDRequest(RakNet::SystemAddress address, std::string serverName, std::string serverPassword) { MsgGetServerRequest msg = {}; msg.Id = ID_NAT_SERVER_GET_SERVER_GUID; strncpy(msg.ServerName, serverName.c_str(), 62); strncpy(msg.ServerPassword, serverPassword.c_str(), 62); - m_Client->Send((const char *)&msg, sizeof(RTE::MsgGetServerRequest), IMMEDIATE_PRIORITY, RELIABLE, 0, address, false); + m_Client->Send((const char*)&msg, sizeof(RTE::MsgGetServerRequest), IMMEDIATE_PRIORITY, RELIABLE, 0, address, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveServerGUIDAnswer(RakNet::Packet *packet) { - const MsgGetServerAnswer *msg = (MsgGetServerAnswer *)packet->data; + void NetworkClient::ReceiveServerGUIDAnswer(RakNet::Packet* packet) { + const MsgGetServerAnswer* msg = (MsgGetServerAnswer*)packet->data; m_ServerGUID.FromString(msg->ServerGuid); m_NATPunchthroughClient.OpenNAT(m_ServerGUID, m_NATServiceServerID); g_ConsoleMan.PrintString("CLIENT: Open NAT to server"); g_ConsoleMan.PrintString(m_ServerGUID.ToString()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::SendInputMsg() { MsgInput msg = {}; @@ -253,24 +255,28 @@ namespace RTE { // Store element states as bit flags for (int i = 0; i < INPUT_COUNT; i++) { - if (g_UInputMan.ElementHeld(0, i)) { msg.InputElementState = msg.InputElementState | bitMask; } + if (g_UInputMan.ElementHeld(0, i)) { + msg.InputElementState = msg.InputElementState | bitMask; + } bitMask <<= 1; } g_UInputMan.ClearNetworkAccumulatedStates(); - m_Client->Send((const char *)&msg, sizeof(msg), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); + m_Client->Send((const char*)&msg, sizeof(msg), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveFrameSetupMsg(RakNet::Packet *packet) { - const MsgFrameSetup *frameData = (MsgFrameSetup *)packet->data; + void NetworkClient::ReceiveFrameSetupMsg(RakNet::Packet* packet) { + const MsgFrameSetup* frameData = (MsgFrameSetup*)packet->data; if (frameData->FrameNumber >= c_FramesToRemember) { return; } - if (!g_SettingsMan.UseExperimentalMultiplayerSpeedBoosts()) { DrawFrame(frameData->FrameNumber, frameData->Interlaced, !frameData->DeltaCompressed); } + if (!g_SettingsMan.UseExperimentalMultiplayerSpeedBoosts()) { + DrawFrame(frameData->FrameNumber, frameData->Interlaced, !frameData->DeltaCompressed); + } m_PostEffects[m_CurrentFrameNum].clear(); m_CurrentFrameNum = frameData->FrameNumber; @@ -289,14 +295,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveFrameLineMsg(RakNet::Packet *packet) { - const MsgFrameLine *frameData = (MsgFrameLine *)packet->data; + void NetworkClient::ReceiveFrameLineMsg(RakNet::Packet* packet) { + const MsgFrameLine* frameData = (MsgFrameLine*)packet->data; int lineNumber = frameData->LineNumber; m_CurrentSceneLayerReceived = -1; - BITMAP *bmp = nullptr; + BITMAP* bmp = nullptr; if (frameData->Layer == 0) { bmp = g_FrameMan.GetNetworkBackBufferIntermediate8Ready(0); @@ -320,21 +326,21 @@ namespace RTE { #ifdef _WIN32 memcpy_s(bmp->line[lineNumber], bmp->w, packet->data + sizeof(MsgFrameLine), pixels); #else - //Fallback to non safe memcpy + // Fallback to non safe memcpy memcpy(bmp->line[lineNumber], packet->data + sizeof(MsgFrameLine), pixels); #endif } else { - LZ4_decompress_safe((char *)(packet->data + sizeof(MsgFrameLine)), (char *)(bmp->line[lineNumber]), frameData->DataSize, bmp->w); + LZ4_decompress_safe((char*)(packet->data + sizeof(MsgFrameLine)), (char*)(bmp->line[lineNumber]), frameData->DataSize, bmp->w); } } } release_bitmap(bmp); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveFrameBoxMsg(RakNet::Packet *packet) { - const MsgFrameBox *frameData = (MsgFrameBox *)packet->data; + void NetworkClient::ReceiveFrameBoxMsg(RakNet::Packet* packet) { + const MsgFrameBox* frameData = (MsgFrameBox*)packet->data; if (m_CurrentBoxWidth == 0 || m_CurrentBoxHeight == 0) { return; @@ -344,7 +350,7 @@ namespace RTE { int bpy = frameData->BoxY * m_CurrentBoxHeight; m_CurrentSceneLayerReceived = -1; - BITMAP *bmp = nullptr; + BITMAP* bmp = nullptr; bool isDelta = frameData->Id == ID_SRV_FRAME_BOX_MO_DELTA || frameData->Id == ID_SRV_FRAME_BOX_UI_DELTA; if (frameData->Id == ID_SRV_FRAME_BOX_MO || frameData->Id == ID_SRV_FRAME_BOX_MO_DELTA) { @@ -359,13 +365,19 @@ namespace RTE { int maxHeight = m_CurrentBoxHeight; // If box with default size is out of bounds, then it was truncated by the screen edge - if (bpx + maxWidth >= bmp->w) { maxWidth = bmp->w - bpx; } - if (bpy + maxHeight >= bmp->h) { maxHeight = bmp->h - bpy; } + if (bpx + maxWidth >= bmp->w) { + maxWidth = bmp->w - bpx; + } + if (bpy + maxHeight >= bmp->h) { + maxHeight = bmp->h - bpy; + } int size = frameData->DataSize; int uncompressedSize = maxWidth * maxHeight; - if (m_CurrentFrameInterlaced) { uncompressedSize = maxWidth * (maxHeight / 2); } + if (m_CurrentFrameInterlaced) { + uncompressedSize = maxWidth * (maxHeight / 2); + } float compressionRatio = static_cast(size) / static_cast(uncompressedSize); @@ -385,20 +397,21 @@ namespace RTE { memcpy(m_PixelLineBuffer, packet->data + sizeof(MsgFrameBox), size); #endif } else { - LZ4_decompress_safe((char *)(packet->data + sizeof(MsgFrameBox)), (char *)(m_PixelLineBuffer), size, uncompressedSize); + LZ4_decompress_safe((char*)(packet->data + sizeof(MsgFrameBox)), (char*)(m_PixelLineBuffer), size, uncompressedSize); } - int lineStart = 0; int lineStep = 1; if (m_CurrentFrameInterlaced) { lineStep = 2; - if (m_CurrentFrameNum % 2 != 0) { lineStart = 1; } + if (m_CurrentFrameNum % 2 != 0) { + lineStart = 1; + } } if (isDelta) { - const unsigned char *lineAddr = m_PixelLineBuffer; + const unsigned char* lineAddr = m_PixelLineBuffer; for (int y = lineStart; y < maxHeight; y += lineStep) { for (int x = 0; x < maxWidth; x++) { *(bmp->line[bpy + y] + bpx + x) += lineAddr[x]; @@ -407,7 +420,7 @@ namespace RTE { } } else { // Copy box to bitmap line by line - const unsigned char *lineAddr = m_PixelLineBuffer; + const unsigned char* lineAddr = m_PixelLineBuffer; for (int y = lineStart; y < maxHeight; y += lineStep) { #ifdef _WIN32 memcpy_s(bmp->line[bpy + y] + bpx, maxWidth, lineAddr, maxWidth); @@ -433,19 +446,19 @@ namespace RTE { release_bitmap(bmp); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::SendSceneAcceptedMsg() { MsgRegister msg = {}; msg.Id = ID_CLT_SCENE_ACCEPTED; - m_Client->Send((const char *)&msg, sizeof(msg), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); + m_Client->Send((const char*)&msg, sizeof(msg), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); g_ConsoleMan.PrintString("CLIENT: Scene ACK Sent"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveSceneMsg(RakNet::Packet *packet) { - const MsgSceneLine *frameData = (MsgSceneLine *)packet->data; + void NetworkClient::ReceiveSceneMsg(RakNet::Packet* packet) { + const MsgSceneLine* frameData = (MsgSceneLine*)packet->data; if (frameData->SceneId != m_SceneID) { return; } @@ -453,7 +466,7 @@ namespace RTE { int linex = frameData->X; int liney = frameData->Y; - const BITMAP *bmp = nullptr; + const BITMAP* bmp = nullptr; if (frameData->Layer == 0) { bmp = m_SceneBackgroundBitmap; @@ -477,31 +490,35 @@ namespace RTE { memcpy(bmp->line[liney] + linex, packet->data + sizeof(MsgSceneLine), pixels); #endif } else { - LZ4_decompress_safe((char *)(packet->data + sizeof(MsgSceneLine)), (char *)(bmp->line[liney] + linex), frameData->DataSize, width); + LZ4_decompress_safe((char*)(packet->data + sizeof(MsgSceneLine)), (char*)(bmp->line[liney] + linex), frameData->DataSize, width); } } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::ReceiveSceneEndMsg() { g_ConsoleMan.PrintString("CLIENT: Scene received."); SendSceneAcceptedMsg(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveSceneSetupMsg(RakNet::Packet *packet) { + void NetworkClient::ReceiveSceneSetupMsg(RakNet::Packet* packet) { clear_to_color(g_FrameMan.GetNetworkBackBufferIntermediateGUI8Ready(0), g_MaskColor); clear_to_color(g_FrameMan.GetNetworkBackBufferGUI8Ready(0), g_MaskColor); - const MsgSceneSetup *frameData = (MsgSceneSetup *)packet->data; + const MsgSceneSetup* frameData = (MsgSceneSetup*)packet->data; m_SceneID = frameData->SceneId; - if (m_SceneBackgroundBitmap) { destroy_bitmap(m_SceneBackgroundBitmap); } - if (m_SceneForegroundBitmap) { destroy_bitmap(m_SceneForegroundBitmap); } + if (m_SceneBackgroundBitmap) { + destroy_bitmap(m_SceneBackgroundBitmap); + } + if (m_SceneForegroundBitmap) { + destroy_bitmap(m_SceneForegroundBitmap); + } m_SceneBackgroundBitmap = create_bitmap_ex(8, frameData->Width, frameData->Height); m_SceneForegroundBitmap = create_bitmap_ex(8, frameData->Width, frameData->Height); @@ -551,8 +568,8 @@ namespace RTE { } } // Reset network framebuffers - BITMAP *net_bmp = g_FrameMan.GetNetworkBackBufferIntermediate8Ready(0); - BITMAP *net_gui_bmp = g_FrameMan.GetNetworkBackBufferIntermediateGUI8Ready(0); + BITMAP* net_bmp = g_FrameMan.GetNetworkBackBufferIntermediate8Ready(0); + BITMAP* net_gui_bmp = g_FrameMan.GetNetworkBackBufferIntermediateGUI8Ready(0); clear_to_color(net_bmp, ColorKeys::g_MaskColor); clear_to_color(net_gui_bmp, ColorKeys::g_MaskColor); @@ -561,25 +578,25 @@ namespace RTE { g_ConsoleMan.PrintString("CLIENT: Scene setup accepted"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::SendSceneSetupAcceptedMsg() { MsgRegister msg; msg.Id = ID_CLT_SCENE_SETUP_ACCEPTED; - m_Client->Send((const char *)&msg, sizeof(msg), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); + m_Client->Send((const char*)&msg, sizeof(msg), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); g_ConsoleMan.PrintString("CLIENT: Scene setup ACK Sent"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveTerrainChangeMsg(RakNet::Packet *packet) { - const MsgTerrainChange *frameData = (MsgTerrainChange *)packet->data; + void NetworkClient::ReceiveTerrainChangeMsg(RakNet::Packet* packet) { + const MsgTerrainChange* frameData = (MsgTerrainChange*)packet->data; if (frameData->SceneId != m_SceneID) { return; } if (frameData->W == 1 && frameData->H == 1) { - BITMAP *bmp = 0; + BITMAP* bmp = 0; bmp = frameData->Back ? m_SceneBackgroundBitmap : m_SceneForegroundBitmap; putpixel(bmp, frameData->X, frameData->Y, frameData->Color); } else { @@ -592,14 +609,14 @@ namespace RTE { memcpy(m_PixelLineBuffer, packet->data + sizeof(MsgTerrainChange), size); #endif } else { - LZ4_decompress_safe((char *)(packet->data + sizeof(MsgTerrainChange)), (char *)m_PixelLineBuffer, frameData->DataSize, size); + LZ4_decompress_safe((char*)(packet->data + sizeof(MsgTerrainChange)), (char*)m_PixelLineBuffer, frameData->DataSize, size); } // Copy bitmap data to scene bitmap - const BITMAP *bmp = 0; + const BITMAP* bmp = 0; bmp = (frameData->Back) ? m_SceneBackgroundBitmap : m_SceneForegroundBitmap; - const unsigned char *src = m_PixelLineBuffer; + const unsigned char* src = m_PixelLineBuffer; for (int y = 0; y < frameData->H && frameData->Y + y < bmp->h; y++) { memcpy(bmp->line[frameData->Y + y] + frameData->X, src, frameData->W); src += frameData->W; @@ -607,42 +624,44 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceivePostEffectsMsg(RakNet::Packet *packet) { - MsgPostEffects *msg = (MsgPostEffects *)packet->data; - const PostEffectNetworkData *effDataPtr = (PostEffectNetworkData *)((char *)msg + sizeof(MsgPostEffects)); + void NetworkClient::ReceivePostEffectsMsg(RakNet::Packet* packet) { + MsgPostEffects* msg = (MsgPostEffects*)packet->data; + const PostEffectNetworkData* effDataPtr = (PostEffectNetworkData*)((char*)msg + sizeof(MsgPostEffects)); for (int i = 0; i < msg->PostEffectsCount; i++) { - BITMAP *bmp = nullptr; + BITMAP* bmp = nullptr; std::string bitmapPath = ContentFile::GetPathFromHash(effDataPtr->BitmapHash); if (!bitmapPath.empty()) { ContentFile fl(bitmapPath.c_str()); bmp = fl.GetAsBitmap(); } - if (bmp) { m_PostEffects[msg->FrameNumber].push_back(PostEffect(Vector(effDataPtr->X, effDataPtr->Y), bmp, 0, effDataPtr->Strength, effDataPtr->Angle)); } + if (bmp) { + m_PostEffects[msg->FrameNumber].push_back(PostEffect(Vector(effDataPtr->X, effDataPtr->Y), bmp, 0, effDataPtr->Strength, effDataPtr->Angle)); + } effDataPtr++; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveSoundEventsMsg(RakNet::Packet *packet) { - MsgSoundEvents *msg = (MsgSoundEvents *)packet->data; - const AudioMan::NetworkSoundData *soundDataPointer = (AudioMan::NetworkSoundData *)((char *)msg + sizeof(MsgSoundEvents)); - std::unordered_multimap alreadyHandledSoundContainers; + void NetworkClient::ReceiveSoundEventsMsg(RakNet::Packet* packet) { + MsgSoundEvents* msg = (MsgSoundEvents*)packet->data; + const AudioMan::NetworkSoundData* soundDataPointer = (AudioMan::NetworkSoundData*)((char*)msg + sizeof(MsgSoundEvents)); + std::unordered_multimap alreadyHandledSoundContainers; for (int msgIndex = 0; msgIndex < msg->SoundEventsCount; msgIndex++) { if (soundDataPointer->State == AudioMan::SOUND_SET_GLOBAL_PITCH) { g_AudioMan.SetGlobalPitch(soundDataPointer->Pitch); } else { int serverSoundChannelIndex = soundDataPointer->Channel; - std::unordered_map::iterator serverSoundEntryForChannel = m_ServerSounds.find(serverSoundChannelIndex); + std::unordered_map::iterator serverSoundEntryForChannel = m_ServerSounds.find(serverSoundChannelIndex); if (soundDataPointer->State == AudioMan::SOUND_PLAY || serverSoundEntryForChannel != m_ServerSounds.end()) { - SoundContainer *soundContainerToHandle = (serverSoundEntryForChannel == m_ServerSounds.end()) ? nullptr : m_ServerSounds.at(serverSoundChannelIndex); + SoundContainer* soundContainerToHandle = (serverSoundEntryForChannel == m_ServerSounds.end()) ? nullptr : m_ServerSounds.at(serverSoundChannelIndex); auto alreadyHandledSoundStates = alreadyHandledSoundContainers.equal_range(soundContainerToHandle); - bool alreadyHandled = soundDataPointer->State != AudioMan::SOUND_PLAY && std::any_of(alreadyHandledSoundStates.first, alreadyHandledSoundStates.second, [&soundDataPointer](const std::pair &alreadyHandledSoundStateEntry) { return static_cast(alreadyHandledSoundStateEntry.second) == soundDataPointer->State; }); + bool alreadyHandled = soundDataPointer->State != AudioMan::SOUND_PLAY && std::any_of(alreadyHandledSoundStates.first, alreadyHandledSoundStates.second, [&soundDataPointer](const std::pair& alreadyHandledSoundStateEntry) { return static_cast(alreadyHandledSoundStateEntry.second) == soundDataPointer->State; }); if (!alreadyHandled) { switch (soundDataPointer->State) { case AudioMan::SOUND_PLAY: @@ -695,18 +714,20 @@ namespace RTE { } alreadyHandledSoundContainers.insert({soundContainerToHandle, soundDataPointer->State}); } - if (soundDataPointer->State == AudioMan::SOUND_PLAY) { m_ServerSounds.insert({ serverSoundChannelIndex, soundContainerToHandle }); } + if (soundDataPointer->State == AudioMan::SOUND_PLAY) { + m_ServerSounds.insert({serverSoundChannelIndex, soundContainerToHandle}); + } } } soundDataPointer++; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveMusicEventsMsg(RakNet::Packet *packet) { - MsgMusicEvents *msg = (MsgMusicEvents *)packet->data; - const AudioMan::NetworkMusicData *musicDataPointer = (AudioMan::NetworkMusicData *)((char *)msg + sizeof(MsgMusicEvents)); + void NetworkClient::ReceiveMusicEventsMsg(RakNet::Packet* packet) { + MsgMusicEvents* msg = (MsgMusicEvents*)packet->data; + const AudioMan::NetworkMusicData* musicDataPointer = (AudioMan::NetworkMusicData*)((char*)msg + sizeof(MsgMusicEvents)); for (int i = 0; i < msg->MusicEventsCount; i++) { switch (musicDataPointer->State) { @@ -720,7 +741,9 @@ namespace RTE { std::snprintf(buf, sizeof(buf), "MUSIC %s %d", path, musicDataPointer->LoopsOrSilence); g_AudioMan.PlayMusic(path, musicDataPointer->LoopsOrSilence); - if (musicDataPointer->Position > 0) { g_AudioMan.SetMusicPosition(musicDataPointer->Position); } + if (musicDataPointer->Position > 0) { + g_AudioMan.SetMusicPosition(musicDataPointer->Position); + } break; case AudioMan::MUSIC_STOP: g_AudioMan.StopMusic(); @@ -739,12 +762,12 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::DrawBackgrounds(BITMAP *targetBitmap) { + void NetworkClient::DrawBackgrounds(BITMAP* targetBitmap) { for (int i = m_ActiveBackgroundLayers - 1; i >= 0; i--) { if (m_BackgroundBitmaps[i] != 0) { - //masked_blit(m_BackgroundBitmaps[i], targetBitmap, 0, 0, 0, 0, m_BackgroundBitmaps[i]->w, m_BackgroundBitmaps[i]->h); + // masked_blit(m_BackgroundBitmaps[i], targetBitmap, 0, 0, 0, 0, m_BackgroundBitmaps[i]->w, m_BackgroundBitmaps[i]->h); Vector scrollOverride(0, 0); bool scrollOverridden = false; @@ -819,7 +842,7 @@ namespace RTE { set_clip_rect(targetBitmap, targetBox.GetCorner().m_X, targetBox.GetCorner().m_Y, targetBox.GetCorner().m_X + targetBox.GetWidth() - 1, targetBox.GetCorner().m_Y + targetBox.GetHeight() - 1); // Choose the correct blitting function based on transparency setting - void(*pfBlit)(BITMAP *source, BITMAP *dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height) = m_BackgroundLayers[frame][i].DrawTrans ? &masked_blit : &blit; + void (*pfBlit)(BITMAP* source, BITMAP* dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height) = m_BackgroundLayers[frame][i].DrawTrans ? &masked_blit : &blit; // See if this SceneLayer is wider AND higher than the target bitmap; then use simple wrapping logic - otherwise need to tile if (m_BackgroundBitmaps[i]->w >= targetBitmap->w && m_BackgroundBitmaps[i]->h >= targetBitmap->h) { @@ -921,20 +944,20 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::DrawPostEffects(int frame) { g_PostProcessMan.SetNetworkPostEffectsList(0, m_PostEffects[frame]); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::DrawFrame(int frameNumber, bool useInterlacing, bool clearFramebuffer) { - BITMAP *src_bmp = g_FrameMan.GetNetworkBackBufferIntermediate8Ready(0); - BITMAP *dst_bmp = g_FrameMan.GetNetworkBackBuffer8Ready(0); + BITMAP* src_bmp = g_FrameMan.GetNetworkBackBufferIntermediate8Ready(0); + BITMAP* dst_bmp = g_FrameMan.GetNetworkBackBuffer8Ready(0); - BITMAP *src_gui_bmp = g_FrameMan.GetNetworkBackBufferIntermediateGUI8Ready(0); - BITMAP *dst_gui_bmp = g_FrameMan.GetNetworkBackBufferGUI8Ready(0); + BITMAP* src_gui_bmp = g_FrameMan.GetNetworkBackBufferIntermediateGUI8Ready(0); + BITMAP* dst_gui_bmp = g_FrameMan.GetNetworkBackBufferGUI8Ready(0); // Have to clear to color to fallback if there's no skybox on client clear_to_color(dst_bmp, g_BlackColor); @@ -962,7 +985,7 @@ namespace RTE { masked_blit(m_SceneBackgroundBitmap, dst_bmp, 0, sourceY, newDestX, destY, width, src_bmp->h); } - //Draw received bitmap + // Draw received bitmap masked_blit(src_bmp, dst_bmp, 0, 0, 0, 0, src_bmp->w, src_bmp->h); masked_blit(src_gui_bmp, dst_gui_bmp, 0, 0, 0, 0, src_bmp->w, src_bmp->h); masked_blit(m_SceneForegroundBitmap, dst_bmp, sourceX, sourceY, destX, destY, src_bmp->w, src_bmp->h); @@ -993,10 +1016,14 @@ namespace RTE { int boxedWidth = src_bmp->w / m_CurrentBoxWidth; int boxedHeight = src_bmp->h / m_CurrentBoxHeight; - if (src_bmp->w % m_CurrentBoxWidth != 0) { boxedWidth = boxedWidth + 1; } + if (src_bmp->w % m_CurrentBoxWidth != 0) { + boxedWidth = boxedWidth + 1; + } int lineStart = 0; - if (clearEven) { lineStart = 1; } + if (clearEven) { + lineStart = 1; + } for (int by = 0; by <= boxedHeight; by++) { for (int bx = 0; bx <= boxedWidth; bx++) { @@ -1008,10 +1035,14 @@ namespace RTE { } int maxWidth = m_CurrentBoxWidth; - if (bpx + m_CurrentBoxWidth >= src_bmp->w) { maxWidth = src_bmp->w - bpx; } + if (bpx + m_CurrentBoxWidth >= src_bmp->w) { + maxWidth = src_bmp->w - bpx; + } int maxHeight = m_CurrentBoxHeight; - if (bpy + m_CurrentBoxHeight >= src_bmp->h) { maxHeight = src_bmp->h - bpy; } + if (bpy + m_CurrentBoxHeight >= src_bmp->h) { + maxHeight = src_bmp->h - bpy; + } } } } else { @@ -1021,14 +1052,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::Update() { HandleNetworkPackets(); // Draw level loading animation if (m_CurrentSceneLayerReceived != -1) { - BITMAP * bmp = nullptr; + BITMAP* bmp = nullptr; if (m_CurrentSceneLayerReceived == -1) { bmp = m_SceneBackgroundBitmap; @@ -1038,7 +1069,7 @@ namespace RTE { bmp = m_SceneForegroundBitmap; } - BITMAP *dst_bmp = g_FrameMan.GetNetworkBackBuffer8Ready(0); + BITMAP* dst_bmp = g_FrameMan.GetNetworkBackBuffer8Ready(0); clear_to_color(dst_bmp, g_BlackColor); @@ -1048,7 +1079,9 @@ namespace RTE { int x = 0; int y = g_WindowMan.GetResY() / 2 - h / 2; - if (h >= g_WindowMan.GetResY()) { y = 0; } + if (h >= g_WindowMan.GetResY()) { + y = 0; + } // Recalculate everything for tall maps if (static_cast(bmp->h) / static_cast(bmp->w) > 1) { @@ -1058,23 +1091,39 @@ namespace RTE { x = g_WindowMan.GetResX() / 2 - w / 2; y = 0; - if (w >= g_WindowMan.GetResX()) { x = 0; } + if (w >= g_WindowMan.GetResX()) { + x = 0; + } } // Draw previous layer - if (m_CurrentSceneLayerReceived == 1) { masked_stretch_blit(m_SceneBackgroundBitmap, dst_bmp, 0, 0, bmp->w, bmp->h, x, y, w, h); } + if (m_CurrentSceneLayerReceived == 1) { + masked_stretch_blit(m_SceneBackgroundBitmap, dst_bmp, 0, 0, bmp->w, bmp->h, x, y, w, h); + } masked_stretch_blit(bmp, dst_bmp, 0, 0, bmp->w, bmp->h, x, y, w, h); } // Detect short mouse events like presses and releases. Holds are detected during input send - if (m_MouseButtonPressedState[MOUSE_LEFT] < 1) { m_MouseButtonPressedState[MOUSE_LEFT] = g_UInputMan.MouseButtonPressed(MOUSE_LEFT, -1) ? 1 : 0; } - if (m_MouseButtonPressedState[MOUSE_RIGHT] < 1) { m_MouseButtonPressedState[MOUSE_RIGHT] = g_UInputMan.MouseButtonPressed(MOUSE_RIGHT, -1) ? 1 : 0; } - if (m_MouseButtonPressedState[MOUSE_MIDDLE] < 1) { m_MouseButtonPressedState[MOUSE_MIDDLE] = g_UInputMan.MouseButtonPressed(MOUSE_MIDDLE, -1) ? 1 : 0; } + if (m_MouseButtonPressedState[MOUSE_LEFT] < 1) { + m_MouseButtonPressedState[MOUSE_LEFT] = g_UInputMan.MouseButtonPressed(MOUSE_LEFT, -1) ? 1 : 0; + } + if (m_MouseButtonPressedState[MOUSE_RIGHT] < 1) { + m_MouseButtonPressedState[MOUSE_RIGHT] = g_UInputMan.MouseButtonPressed(MOUSE_RIGHT, -1) ? 1 : 0; + } + if (m_MouseButtonPressedState[MOUSE_MIDDLE] < 1) { + m_MouseButtonPressedState[MOUSE_MIDDLE] = g_UInputMan.MouseButtonPressed(MOUSE_MIDDLE, -1) ? 1 : 0; + } - if (m_MouseButtonReleasedState[MOUSE_LEFT] < 1) { m_MouseButtonReleasedState[MOUSE_LEFT] = g_UInputMan.MouseButtonReleased(MOUSE_LEFT, -1) ? 1 : 0; } - if (m_MouseButtonReleasedState[MOUSE_RIGHT] < 1) { m_MouseButtonReleasedState[MOUSE_RIGHT] = g_UInputMan.MouseButtonReleased(MOUSE_RIGHT, -1) ? 1 : 0; } - if (m_MouseButtonReleasedState[MOUSE_MIDDLE] < 1) { m_MouseButtonReleasedState[MOUSE_MIDDLE] = g_UInputMan.MouseButtonReleased(MOUSE_MIDDLE, -1) ? 1 : 0; } + if (m_MouseButtonReleasedState[MOUSE_LEFT] < 1) { + m_MouseButtonReleasedState[MOUSE_LEFT] = g_UInputMan.MouseButtonReleased(MOUSE_LEFT, -1) ? 1 : 0; + } + if (m_MouseButtonReleasedState[MOUSE_RIGHT] < 1) { + m_MouseButtonReleasedState[MOUSE_RIGHT] = g_UInputMan.MouseButtonReleased(MOUSE_RIGHT, -1) ? 1 : 0; + } + if (m_MouseButtonReleasedState[MOUSE_MIDDLE] < 1) { + m_MouseButtonReleasedState[MOUSE_MIDDLE] = g_UInputMan.MouseButtonReleased(MOUSE_MIDDLE, -1) ? 1 : 0; + } if (g_UInputMan.MouseWheelMoved() != 0) { m_MouseWheelMoved = g_UInputMan.MouseWheelMoved(); @@ -1089,12 +1138,16 @@ namespace RTE { #endif long long currentTicks = g_TimerMan.GetRealTickCount(); - if (currentTicks - m_LastInputSentTime < 0) { m_LastInputSentTime = currentTicks; } + if (currentTicks - m_LastInputSentTime < 0) { + m_LastInputSentTime = currentTicks; + } if (static_cast((currentTicks - m_LastInputSentTime)) / static_cast(g_TimerMan.GetTicksPerSecond()) > 1.0 / inputSend) { m_LastInputSentTime = g_TimerMan.GetRealTickCount(); if (IsConnectedAndRegistered()) { - if (g_SettingsMan.UseExperimentalMultiplayerSpeedBoosts()) { DrawFrame(m_CurrentFrameNum, m_CurrentFrameInterlaced, !m_CurrentFrameDeltaCompressed); } + if (g_SettingsMan.UseExperimentalMultiplayerSpeedBoosts()) { + DrawFrame(m_CurrentFrameNum, m_CurrentFrameInterlaced, !m_CurrentFrameDeltaCompressed); + } SendInputMsg(); } } @@ -1104,12 +1157,12 @@ namespace RTE { #endif } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkClient::HandleNetworkPackets() { std::string msg; - for (RakNet::Packet *packet = m_Client->Receive(); packet; m_Client->DeallocatePacket(packet), packet = m_Client->Receive()) { + for (RakNet::Packet* packet = m_Client->Receive(); packet; m_Client->DeallocatePacket(packet), packet = m_Client->Receive()) { // We got a packet, get the identifier with our handy function unsigned char packetIdentifier = GetPacketIdentifier(packet); @@ -1263,4 +1316,4 @@ namespace RTE { } } } -} +} // namespace RTE diff --git a/Source/Managers/NetworkClient.h b/Source/Managers/NetworkClient.h index febe121c01..91504c3f58 100644 --- a/Source/Managers/NetworkClient.h +++ b/Source/Managers/NetworkClient.h @@ -30,12 +30,14 @@ namespace RTE { friend class SettingsMan; public: - #pragma region Creation /// /// Constructor method used to instantiate a NetworkClient object in system memory. Create() should be called before using the object. /// - NetworkClient() { Clear(); Initialize(); } + NetworkClient() { + Clear(); + Initialize(); + } /// /// Makes the NetworkClient object ready for use. @@ -85,7 +87,7 @@ namespace RTE { /// Get the coordinates of the center of the current frame. /// /// A vector containing the X/Y coordinates of the frame target. - const Vector & GetFrameTarget() const { return m_TargetPos[m_CurrentFrameNum]; } + const Vector& GetFrameTarget() const { return m_TargetPos[m_CurrentFrameNum]; } #pragma endregion @@ -133,16 +135,15 @@ namespace RTE { /// /// /// - RakNet::SystemAddress ConnectBlocking(RakNet::RakPeerInterface *rakPeer, const char *address, unsigned short port); + RakNet::SystemAddress ConnectBlocking(RakNet::RakPeerInterface* rakPeer, const char* address, unsigned short port); #pragma endregion protected: - static constexpr unsigned short c_PlayerNameCharLimit = 15; //!< Maximum length of the player name. std::string m_PlayerName; //!< The player name the will be used by the client in network games. - RakNet::RakPeerInterface *m_Client; //!< The client RakPeerInterface. + RakNet::RakPeerInterface* m_Client; //!< The client RakPeerInterface. RakNet::SystemAddress m_ClientID; //!< The client's identifier. RakNet::SystemAddress m_ServerID; //!< The server's identifier. @@ -176,15 +177,15 @@ namespace RTE { Vector m_TargetPos[c_FramesToRemember]; //!< std::list m_PostEffects[c_FramesToRemember]; //!< List of post-effects received from server. - std::unordered_map m_ServerSounds; //!< Unordered map of SoundContainers received from server. OWNED!!! + std::unordered_map m_ServerSounds; //!< Unordered map of SoundContainers received from server. OWNED!!! unsigned char m_SceneID; //!< int m_CurrentSceneLayerReceived; //!< - BITMAP *m_SceneBackgroundBitmap; //!< - BITMAP *m_SceneForegroundBitmap; //!< + BITMAP* m_SceneBackgroundBitmap; //!< + BITMAP* m_SceneForegroundBitmap; //!< - BITMAP *m_BackgroundBitmaps[c_MaxLayersStoredForNetwork]; //!< + BITMAP* m_BackgroundBitmaps[c_MaxLayersStoredForNetwork]; //!< LightweightSceneLayer m_BackgroundLayers[c_FramesToRemember][c_MaxLayersStoredForNetwork]; //!< int m_ActiveBackgroundLayers; //!< bool m_SceneWrapsX; //!< @@ -196,7 +197,6 @@ namespace RTE { int m_MouseWheelMoved; //!< Whether the mouse wheel was moved this Update. Used to make mouse wheel detection better. private: - #pragma region Update Breakdown /// /// @@ -210,7 +210,7 @@ namespace RTE { /// /// /// - unsigned char GetPacketIdentifier(RakNet::Packet *packet) const; + unsigned char GetPacketIdentifier(RakNet::Packet* packet) const; /// /// @@ -239,7 +239,7 @@ namespace RTE { /// /// /// - void ReceiveServerGUIDAnswer(RakNet::Packet *packet); + void ReceiveServerGUIDAnswer(RakNet::Packet* packet); /// /// @@ -250,19 +250,19 @@ namespace RTE { /// /// /// - void ReceiveFrameSetupMsg(RakNet::Packet *packet); + void ReceiveFrameSetupMsg(RakNet::Packet* packet); /// /// /// /// - void ReceiveFrameLineMsg(RakNet::Packet *packet); + void ReceiveFrameLineMsg(RakNet::Packet* packet); /// /// /// /// - void ReceiveFrameBoxMsg(RakNet::Packet *packet); + void ReceiveFrameBoxMsg(RakNet::Packet* packet); /// /// @@ -273,7 +273,7 @@ namespace RTE { /// /// /// - void ReceiveSceneMsg(RakNet::Packet *packet); + void ReceiveSceneMsg(RakNet::Packet* packet); /// /// @@ -284,7 +284,7 @@ namespace RTE { /// /// /// - void ReceiveSceneSetupMsg(RakNet::Packet *packet); + void ReceiveSceneSetupMsg(RakNet::Packet* packet); /// /// @@ -295,25 +295,25 @@ namespace RTE { /// Receive and handle a packet of terrain change data. /// /// The packet to handle. - void ReceiveTerrainChangeMsg(RakNet::Packet *packet); + void ReceiveTerrainChangeMsg(RakNet::Packet* packet); /// /// Receive and handle a packet of post-effect data. /// /// The packet to handle. - void ReceivePostEffectsMsg(RakNet::Packet *packet); + void ReceivePostEffectsMsg(RakNet::Packet* packet); /// /// Receive and handle a packet of sound event data. /// /// The packet to handle. - void ReceiveSoundEventsMsg(RakNet::Packet *packet); + void ReceiveSoundEventsMsg(RakNet::Packet* packet); /// /// Receive and handle a packet of music event data. /// /// The packet to handle. - void ReceiveMusicEventsMsg(RakNet::Packet *packet); + void ReceiveMusicEventsMsg(RakNet::Packet* packet); #pragma endregion #pragma region Drawing @@ -321,7 +321,7 @@ namespace RTE { /// /// /// - void DrawBackgrounds(BITMAP *targetBitmap); + void DrawBackgrounds(BITMAP* targetBitmap); /// /// @@ -350,8 +350,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - NetworkClient(const NetworkClient &reference) = delete; - NetworkClient & operator=(const NetworkClient &rhs) = delete; + NetworkClient(const NetworkClient& reference) = delete; + NetworkClient& operator=(const NetworkClient& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Managers/NetworkServer.cpp b/Source/Managers/NetworkServer.cpp index 8370f51a1e..c004e44263 100644 --- a/Source/Managers/NetworkServer.cpp +++ b/Source/Managers/NetworkServer.cpp @@ -25,9 +25,9 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::BackgroundSendThreadFunction(NetworkServer *server, short player) { + void NetworkServer::BackgroundSendThreadFunction(NetworkServer* server, short player) { const int sleepTime = 1000000 / server->m_EncodingFps; while (server->IsServerModeEnabled() && server->IsPlayerConnected(player)) { if (server->NeedToSendSceneSetupData(player) && server->IsSceneAvailable(player)) { @@ -44,10 +44,9 @@ namespace RTE { server->UpdateStats(player); } server->SetThreadExitReason(player, NetworkServer::THREAD_FINISH); - } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::Clear() { m_SleepWhenIdle = false; @@ -148,7 +147,7 @@ namespace RTE { m_LastPackedReceived = nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int NetworkServer::Initialize() { m_IsInServerMode = false; @@ -157,7 +156,7 @@ namespace RTE { m_LastPackedReceived = std::make_unique(); - for (std::unique_ptr &pingTimer : m_PingTimer) { + for (std::unique_ptr& pingTimer: m_PingTimer) { pingTimer = std::make_unique(); } @@ -174,7 +173,9 @@ namespace RTE { if (m_BoxHeight % 2 != 0) { g_ConsoleMan.PrintString("SERVER: Box height must be divisible by 2! Box height will be adjusted!"); m_BoxHeight -= 1; - if (m_BoxHeight == 0) { m_BoxHeight = 2; } + if (m_BoxHeight == 0) { + m_BoxHeight = 2; + } } for (int i = 0; i < c_MaxClients; i++) { @@ -190,10 +191,10 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::Destroy() { - //Send a signal that server is going to shutdown + // Send a signal that server is going to shutdown m_IsInServerMode = false; // Wait for thread to shut down RakSleep(250); @@ -204,34 +205,42 @@ namespace RTE { for (short i = 0; i < c_MaxClients; i++) { DestroyBackBuffer(i); - if (m_LZ4CompressionState[i]) { free(m_LZ4CompressionState[i]); } + if (m_LZ4CompressionState[i]) { + free(m_LZ4CompressionState[i]); + } m_LZ4CompressionState[i] = 0; - if (m_LZ4FastCompressionState[i]) { free(m_LZ4FastCompressionState[i]); } + if (m_LZ4FastCompressionState[i]) { + free(m_LZ4FastCompressionState[i]); + } m_LZ4FastCompressionState[i] = 0; } Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool NetworkServer::ReadyForSimulation() { short playersReady = 0; short playersTotal = 0; for (short player = 0; player < c_MaxClients; player++) { - if (IsPlayerConnected(player)) { playersTotal++; } - if (SendFrameData(player)) { playersReady++; } + if (IsPlayerConnected(player)) { + playersTotal++; + } + if (SendFrameData(player)) { + playersReady++; + } } return playersReady >= playersTotal; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SetServerPort(const std::string &newPort) { + void NetworkServer::SetServerPort(const std::string& newPort) { bool useDefault = false; - for (const char &stringChar : newPort) { + for (const char& stringChar: newPort) { if (!std::isdigit(stringChar)) { g_ConsoleMan.PrintString("ERROR: Invalid port passed into \"-server\" argument, using default (8000) instead!"); useDefault = true; @@ -241,7 +250,7 @@ namespace RTE { m_ServerPort = useDefault ? "8000" : newPort; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::Start() { RakNet::SocketDescriptor socketDescriptors[1]; @@ -271,7 +280,9 @@ namespace RTE { serverName = g_SettingsMan.GetNATServiceAddress().substr(0, portPos); std::string portStr = g_SettingsMan.GetNATServiceAddress().substr(portPos + 1, g_SettingsMan.GetNATServiceAddress().length() - 2); port = atoi(portStr.c_str()); - if (port == 0) { port = 61111; } + if (port == 0) { + port = 61111; + } } else { serverName = g_SettingsMan.GetNATServiceAddress(); port = 61111; @@ -288,7 +299,7 @@ namespace RTE { m_Server->SetUnreliableTimeout(50); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::LockScene(bool isLocked) { for (int i = 0; i < c_MaxClients; i++) { @@ -300,7 +311,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::ResetScene() { m_SceneID++; @@ -312,7 +323,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::RegisterTerrainChange(NetworkTerrainChange terrainChange) { if (m_IsInServerMode) { @@ -326,9 +337,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned char NetworkServer::GetPacketIdentifier(RakNet::Packet *packet) const { + unsigned char NetworkServer::GetPacketIdentifier(RakNet::Packet* packet) const { if (packet == 0) { return 255; } @@ -340,9 +351,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveNewIncomingConnection(RakNet::Packet *packet) { + void NetworkServer::ReceiveNewIncomingConnection(RakNet::Packet* packet) { std::string msg; RakNet::SystemAddress clientID; char buf[256]; @@ -357,11 +368,11 @@ namespace RTE { RakNet::SystemAddress internalId = RakNet::UNASSIGNED_SYSTEM_ADDRESS; - //g_ConsoleMan.PrintString("SERVER: Remote internal IDs:\n"); + // g_ConsoleMan.PrintString("SERVER: Remote internal IDs:\n"); for (int index = 0; index < MAXIMUM_NUMBER_OF_INTERNAL_IDS; index++) { internalId = m_Server->GetInternalID(packet->systemAddress, index); if (internalId != RakNet::UNASSIGNED_SYSTEM_ADDRESS) { - //g_ConsoleMan.PrintString(internalId.ToString(true)); + // g_ConsoleMan.PrintString(internalId.ToString(true)); } } @@ -383,20 +394,22 @@ namespace RTE { break; } } - if (!connected) { g_ConsoleMan.PrintString("SERVER: Could not accept connection"); } + if (!connected) { + g_ConsoleMan.PrintString("SERVER: Could not accept connection"); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendAcceptedMsg(short player) { MsgAccepted msg; msg.Id = ID_SRV_ACCEPTED; - m_Server->Send((const char *)&msg, sizeof(MsgAccepted), HIGH_PRIORITY, RELIABLE_SEQUENCED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)&msg, sizeof(MsgAccepted), HIGH_PRIORITY, RELIABLE_SEQUENCED, 0, m_ClientConnections[player].ClientId, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveDisconnection(RakNet::Packet *packet) { + void NetworkServer::ReceiveDisconnection(RakNet::Packet* packet) { std::string msg = "ID_CONNECTION_LOST from"; msg += packet->systemAddress.ToString(true); g_ConsoleMan.PrintString(msg); @@ -407,7 +420,7 @@ namespace RTE { m_ClientConnections[index].ClientId = RakNet::UNASSIGNED_SYSTEM_ADDRESS; m_ClientConnections[index].InternalId = RakNet::UNASSIGNED_SYSTEM_ADDRESS; - //delete m_ClientConnections[index].SendThread; + // delete m_ClientConnections[index].SendThread; m_ClientConnections[index].SendThread = 0; m_SendSceneSetupData[index] = true; @@ -417,11 +430,11 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveRegisterMsg(RakNet::Packet *packet) { + void NetworkServer::ReceiveRegisterMsg(RakNet::Packet* packet) { std::string msg; - const MsgRegister *msgReg = (MsgRegister *)packet->data; + const MsgRegister* msgReg = (MsgRegister*)packet->data; char buf[32]; msg = "SERVER: CLIENT REGISTRATION: RES "; @@ -448,8 +461,8 @@ namespace RTE { m_SendFrameData[index] = false; // Get backbuffer bitmap for this player - BITMAP *frameManBmp = g_FrameMan.GetNetworkBackBuffer8Ready(index); - BITMAP *frameManGUIBmp = g_FrameMan.GetNetworkBackBufferGUI8Ready(index); + BITMAP* frameManBmp = g_FrameMan.GetNetworkBackBuffer8Ready(index); + BITMAP* frameManGUIBmp = g_FrameMan.GetNetworkBackBufferGUI8Ready(index); if (!m_BackBuffer8[index]) { CreateBackBuffer(index, frameManBmp->w, frameManBmp->h); @@ -465,7 +478,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendNATServerRegistrationMsg(RakNet::SystemAddress address) { MsgRegisterServer msg = {}; @@ -478,18 +491,20 @@ namespace RTE { int payloadSize = sizeof(MsgSceneSetup); - m_Server->Send((const char *)&msg, payloadSize, IMMEDIATE_PRIORITY, RELIABLE, 0, address, false); + m_Server->Send((const char*)&msg, payloadSize, IMMEDIATE_PRIORITY, RELIABLE, 0, address, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveInputMsg(RakNet::Packet *packet) { - const MsgInput *m = (MsgInput *)packet->data; + void NetworkServer::ReceiveInputMsg(RakNet::Packet* packet) { + const MsgInput* m = (MsgInput*)packet->data; int player = -1; for (int index = 0; index < c_MaxClients; index++) { - if (m_ClientConnections[index].ClientId == packet->systemAddress) { player = index; } + if (m_ClientConnections[index].ClientId == packet->systemAddress) { + player = index; + } } if (player >= 0 && player < c_MaxClients) { @@ -515,27 +530,43 @@ namespace RTE { if (!m_InputMessages[player].empty()) { MsgInput lastmsg = m_InputMessages[player].back(); - if (msg.MouseX != lastmsg.MouseX) { skip = false; } - if (msg.MouseY != lastmsg.MouseY) { skip = false; } + if (msg.MouseX != lastmsg.MouseX) { + skip = false; + } + if (msg.MouseY != lastmsg.MouseY) { + skip = false; + } for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) { - if (msg.MouseButtonState[i] != lastmsg.MouseButtonState[i]) { skip = false; } + if (msg.MouseButtonState[i] != lastmsg.MouseButtonState[i]) { + skip = false; + } + } + if (msg.ResetActivityVote != lastmsg.ResetActivityVote) { + skip = false; + } + if (msg.RestartActivityVote != lastmsg.RestartActivityVote) { + skip = false; } - if (msg.ResetActivityVote != lastmsg.ResetActivityVote) { skip = false; } - if (msg.RestartActivityVote != lastmsg.RestartActivityVote) { skip = false; } - if (msg.MouseWheelMoved != lastmsg.MouseWheelMoved) { skip = false; } + if (msg.MouseWheelMoved != lastmsg.MouseWheelMoved) { + skip = false; + } - if (msg.InputElementState != lastmsg.InputElementState) { skip = false; } + if (msg.InputElementState != lastmsg.InputElementState) { + skip = false; + } } else { skip = false; } - if (!skip) { m_InputMessages[player].push_back(msg); } + if (!skip) { + m_InputMessages[player].push_back(msg); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::ProcessInputMsg(short player, MsgInput msg) { if (player >= 0 && player < c_MaxClients) { @@ -573,11 +604,11 @@ namespace RTE { // We need to replace mouse input obtained from the allegro with mouse input obtained from network clients GUIInput::SetNetworkMouseMovement(player, msg.MouseX, msg.MouseY); } else { - //g_ConsoleMan.PrintString("SERVER: Input for unknown client. Ignored."); + // g_ConsoleMan.PrintString("SERVER: Input for unknown client. Ignored."); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::ClearInputMessages(short player) { if (player >= 0 && player < c_MaxClients) { @@ -585,7 +616,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendSoundData(short player) { std::list events; @@ -595,13 +626,13 @@ namespace RTE { return; } - MsgSoundEvents *msg = (MsgSoundEvents *)m_CompressedLineBuffer[player]; - AudioMan::NetworkSoundData *soundDataPointer = (AudioMan::NetworkSoundData *)((char *)msg + sizeof(MsgSoundEvents)); + MsgSoundEvents* msg = (MsgSoundEvents*)m_CompressedLineBuffer[player]; + AudioMan::NetworkSoundData* soundDataPointer = (AudioMan::NetworkSoundData*)((char*)msg + sizeof(MsgSoundEvents)); msg->Id = ID_SRV_SOUND_EVENTS; msg->FrameNumber = m_FrameNumbers[player]; msg->SoundEventsCount = 0; - for (const AudioMan::NetworkSoundData &soundEvent : events) { + for (const AudioMan::NetworkSoundData& soundEvent: events) { if (sizeof(MsgSoundEvents) + (msg->SoundEventsCount * sizeof(AudioMan::NetworkSoundData)) <= c_MaxPixelLineBufferSize) { soundDataPointer->State = soundEvent.State; soundDataPointer->SoundFileHash = soundEvent.SoundFileHash; @@ -621,20 +652,20 @@ namespace RTE { soundDataPointer++; } else { int payloadSize = sizeof(MsgSoundEvents) + (msg->SoundEventsCount * sizeof(AudioMan::NetworkSoundData)); - m_Server->Send((const char *)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); m_SoundDataSentCurrent[player][STAT_CURRENT] += payloadSize; m_SoundDataSentTotal[player] += payloadSize; m_DataSentTotal[player] += payloadSize; - soundDataPointer = (AudioMan::NetworkSoundData *)((char *)msg + sizeof(MsgSoundEvents)); + soundDataPointer = (AudioMan::NetworkSoundData*)((char*)msg + sizeof(MsgSoundEvents)); msg->SoundEventsCount = 0; } } if (msg->SoundEventsCount > 0) { int payloadSize = sizeof(MsgSoundEvents) + (msg->SoundEventsCount * sizeof(AudioMan::NetworkSoundData)); - m_Server->Send((const char *)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); m_SoundDataSentCurrent[player][STAT_CURRENT] += payloadSize; m_SoundDataSentTotal[player] += payloadSize; @@ -642,7 +673,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendMusicData(short player) { std::list events; @@ -652,14 +683,14 @@ namespace RTE { return; } - MsgMusicEvents *msg = (MsgMusicEvents *)m_CompressedLineBuffer[player]; - AudioMan::NetworkMusicData *musicDataPointer = (AudioMan::NetworkMusicData *)((char *)msg + sizeof(MsgMusicEvents)); + MsgMusicEvents* msg = (MsgMusicEvents*)m_CompressedLineBuffer[player]; + AudioMan::NetworkMusicData* musicDataPointer = (AudioMan::NetworkMusicData*)((char*)msg + sizeof(MsgMusicEvents)); msg->Id = ID_SRV_MUSIC_EVENTS; msg->FrameNumber = m_FrameNumbers[player]; msg->MusicEventsCount = 0; - for (const AudioMan::NetworkMusicData &musicEvent : events) { + for (const AudioMan::NetworkMusicData& musicEvent: events) { musicDataPointer->State = musicEvent.State; musicDataPointer->LoopsOrSilence = musicEvent.LoopsOrSilence; musicDataPointer->Pitch = musicEvent.Pitch; @@ -671,21 +702,20 @@ namespace RTE { if (msg->MusicEventsCount >= 4) { int payloadSize = sizeof(MsgMusicEvents) + sizeof(AudioMan::NetworkMusicData) * msg->MusicEventsCount; - m_Server->Send((const char *)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); msg->MusicEventsCount = 0; - musicDataPointer = (AudioMan::NetworkMusicData *)((char *)msg + sizeof(MsgMusicEvents)); + musicDataPointer = (AudioMan::NetworkMusicData*)((char*)msg + sizeof(MsgMusicEvents)); m_SoundDataSentCurrent[player][STAT_CURRENT] += payloadSize; m_SoundDataSentTotal[player] += payloadSize; m_DataSentTotal[player] += payloadSize; - } } if (msg->MusicEventsCount > 0) { int payloadSize = sizeof(MsgMusicEvents) + sizeof(AudioMan::NetworkMusicData) * msg->MusicEventsCount; - m_Server->Send((const char *)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); m_SoundDataSentCurrent[player][STAT_CURRENT] += payloadSize; m_SoundDataSentTotal[player] += payloadSize; @@ -694,7 +724,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendSceneSetupData(short player) { MsgSceneSetup msgSceneSetup = {}; @@ -704,12 +734,12 @@ namespace RTE { msgSceneSetup.Height = static_cast(g_SceneMan.GetSceneHeight()); msgSceneSetup.SceneWrapsX = g_SceneMan.SceneWrapsX(); - Scene *scene = g_SceneMan.GetScene(); + Scene* scene = g_SceneMan.GetScene(); - std::list sceneLayers = scene->GetBackLayers(); + std::list sceneLayers = scene->GetBackLayers(); short index = 0; - for (SLBackground * &layer : sceneLayers) { + for (SLBackground*& layer: sceneLayers) { // Recalculate layers internal values for this player layer->InitScrollRatios(true, player); msgSceneSetup.BackgroundLayers[index].BitmapHash = layer->m_BitmapFile.GetHash(); @@ -743,7 +773,7 @@ namespace RTE { int payloadSize = sizeof(MsgSceneSetup); - m_Server->Send((const char *)&msgSceneSetup, payloadSize, HIGH_PRIORITY, RELIABLE_SEQUENCED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)&msgSceneSetup, payloadSize, HIGH_PRIORITY, RELIABLE_SEQUENCED, 0, m_ClientConnections[player].ClientId, false); m_DataSentCurrent[player][STAT_CURRENT] += payloadSize; m_DataSentTotal[player] += payloadSize; @@ -761,17 +791,21 @@ namespace RTE { // While we're on the same thread with freshly connected player, send current music being played if (g_AudioMan.IsMusicPlaying()) { std::string currentMusic = g_AudioMan.GetMusicPath(); - if (!currentMusic.empty()) { g_AudioMan.RegisterMusicEvent(player, AudioMan::MUSIC_PLAY, currentMusic.c_str(), -1, g_AudioMan.GetMusicPosition(), 1.0F); } + if (!currentMusic.empty()) { + g_AudioMan.RegisterMusicEvent(player, AudioMan::MUSIC_PLAY, currentMusic.c_str(), -1, g_AudioMan.GetMusicPosition(), 1.0F); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveSceneSetupDataAccepted(RakNet::Packet *packet) { + void NetworkServer::ReceiveSceneSetupDataAccepted(RakNet::Packet* packet) { short player = -1; for (short index = 0; index < c_MaxClients; index++) { - if (m_ClientConnections[index].ClientId == packet->systemAddress) { player = index; } + if (m_ClientConnections[index].ClientId == packet->systemAddress) { + player = index; + } } if (player > -1) { @@ -781,13 +815,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendSceneData(short player) { // Check for congestion RakNet::RakNetStatistics rns; - MsgSceneLine *sceneData = (MsgSceneLine *)m_CompressedLineBuffer[player]; + MsgSceneLine* sceneData = (MsgSceneLine*)m_CompressedLineBuffer[player]; // Save message ID sceneData->Id = ID_SRV_SCENE; @@ -796,8 +830,8 @@ namespace RTE { int lineY = 0; int lineWidth = 1280; - Scene *scene = g_SceneMan.GetScene(); - SLTerrain *terrain = 0; + Scene* scene = g_SceneMan.GetScene(); + SLTerrain* terrain = 0; if (scene) { terrain = scene->GetTerrain(); @@ -812,7 +846,7 @@ namespace RTE { m_SceneLock[player].lock(); for (int layer = 0; layer < 2; layer++) { - BITMAP *bmp = 0; + BITMAP* bmp = 0; if (layer == 0) { bmp = terrain->GetBGColorBitmap(); } else if (layer == 1) { @@ -821,9 +855,11 @@ namespace RTE { lock_bitmap(bmp); - for (lineX = 0; ; lineX += lineWidth) { + for (lineX = 0;; lineX += lineWidth) { int width = lineWidth; - if (lineX + width >= g_SceneMan.GetSceneWidth()) { width = g_SceneMan.GetSceneWidth() - lineX; } + if (lineX + width >= g_SceneMan.GetSceneWidth()) { + width = g_SceneMan.GetSceneWidth() - lineX; + } for (lineY = 0; lineY < g_SceneMan.GetSceneHeight(); lineY++) { // Save scene fragment data @@ -836,9 +872,9 @@ namespace RTE { // Compression section int result = 0; - //bool lineIsEmpty = false; + // bool lineIsEmpty = false; - result = LZ4_compress_HC_extStateHC(m_LZ4CompressionState[player], (char *)bmp->line[lineY] + lineX, (char *)(m_CompressedLineBuffer[player] + sizeof(MsgSceneLine)), width, width, LZ4HC_CLEVEL_MAX); + result = LZ4_compress_HC_extStateHC(m_LZ4CompressionState[player], (char*)bmp->line[lineY] + lineX, (char*)(m_CompressedLineBuffer[player] + sizeof(MsgSceneLine)), width, width, LZ4HC_CLEVEL_MAX); // Compression failed or ineffective, send as is if (result == 0 || result == width) { @@ -853,7 +889,7 @@ namespace RTE { int payloadSize = sceneData->DataSize + sizeof(MsgSceneLine); - m_Server->Send((const char *)sceneData, payloadSize, HIGH_PRIORITY, RELIABLE, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)sceneData, payloadSize, HIGH_PRIORITY, RELIABLE, 0, m_ClientConnections[player].ClientId, false); m_DataSentCurrent[player][STAT_CURRENT] += payloadSize; m_DataSentTotal[player] += payloadSize; @@ -896,7 +932,7 @@ namespace RTE { SendSceneEndMsg(player); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::ClearTerrainChangeQueue(short player) { m_Mutex[player].lock(); @@ -909,7 +945,7 @@ namespace RTE { m_Mutex[player].unlock(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool NetworkServer::NeedToProcessTerrainChanges(short player) { bool result; @@ -921,7 +957,7 @@ namespace RTE { return result; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::ProcessTerrainChanges(short player) { m_Mutex[player].lock(); @@ -970,7 +1006,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendTerrainChangeMsg(short player, NetworkTerrainChange terrainChange) { if (terrainChange.w == 1 && terrainChange.h == 1) { @@ -988,7 +1024,7 @@ namespace RTE { int payloadSize = sizeof(MsgTerrainChange); - m_Server->Send((const char *)&msg, payloadSize, MEDIUM_PRIORITY, RELIABLE, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)&msg, payloadSize, MEDIUM_PRIORITY, RELIABLE, 0, m_ClientConnections[player].ClientId, false); m_DataSentCurrent[player][STAT_CURRENT] += payloadSize; m_DataSentTotal[player] += payloadSize; @@ -999,7 +1035,7 @@ namespace RTE { m_DataUncompressedCurrent[player][STAT_CURRENT] += payloadSize; m_DataUncompressedTotal[player] += payloadSize; } else { - MsgTerrainChange *msg = (MsgTerrainChange *)m_CompressedLineBuffer[player]; + MsgTerrainChange* msg = (MsgTerrainChange*)m_CompressedLineBuffer[player]; msg->Id = ID_SRV_TERRAIN; msg->X = terrainChange.x; msg->Y = terrainChange.y; @@ -1012,13 +1048,13 @@ namespace RTE { msg->Color = terrainChange.color; msg->Back = terrainChange.back; - Scene * scene = g_SceneMan.GetScene(); - SLTerrain * terrain = scene->GetTerrain(); + Scene* scene = g_SceneMan.GetScene(); + SLTerrain* terrain = scene->GetTerrain(); - const BITMAP *bmp = 0; + const BITMAP* bmp = 0; bmp = msg->Back ? terrain->GetBGColorBitmap() : terrain->GetFGColorBitmap(); - unsigned char *dest = (unsigned char *)(m_PixelLineBuffer[player]); + unsigned char* dest = (unsigned char*)(m_PixelLineBuffer[player]); // Copy bitmap data for (int y = 0; y < msg->H && msg->Y + y < bmp->h; y++) { @@ -1028,7 +1064,7 @@ namespace RTE { int result = 0; - result = LZ4_compress_HC_extStateHC(m_LZ4CompressionState[player], (char *)m_PixelLineBuffer[player], (char *)(m_CompressedLineBuffer[player] + sizeof(MsgTerrainChange)), size, size, LZ4HC_CLEVEL_OPT_MIN); + result = LZ4_compress_HC_extStateHC(m_LZ4CompressionState[player], (char*)m_PixelLineBuffer[player], (char*)(m_CompressedLineBuffer[player] + sizeof(MsgTerrainChange)), size, size, LZ4HC_CLEVEL_OPT_MIN); // Compression failed or ineffective, send as is if (result == 0 || result == size) { @@ -1043,7 +1079,7 @@ namespace RTE { int payloadSize = sizeof(MsgTerrainChange) + msg->DataSize; - m_Server->Send((const char *)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE, 0, m_ClientConnections[player].ClientId, false); m_DataSentCurrent[player][STAT_CURRENT] += payloadSize; m_DataSentTotal[player] += payloadSize; @@ -1056,23 +1092,25 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveSceneAcceptedMsg(RakNet::Packet *packet) { + void NetworkServer::ReceiveSceneAcceptedMsg(RakNet::Packet* packet) { for (short player = 0; player < c_MaxClients; player++) { - if (m_ClientConnections[player].ClientId == packet->systemAddress) { m_SendFrameData[player] = true; } + if (m_ClientConnections[player].ClientId == packet->systemAddress) { + m_SendFrameData[player] = true; + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendSceneEndMsg(short player) { MsgSceneEnd msg = {}; msg.Id = ID_SRV_SCENE_END; - m_Server->Send((const char *)&msg, sizeof(MsgSceneSetup), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)&msg, sizeof(MsgSceneSetup), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::CreateBackBuffer(short player, int w, int h) { m_BackBuffer8[player] = create_bitmap_ex(8, w, h); @@ -1090,7 +1128,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::ClearBackBuffer(int player, int w, int h) { clear_to_color(m_BackBuffer8[player], ColorKeys::g_MaskColor); @@ -1105,7 +1143,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::DestroyBackBuffer(short player) { if (m_BackBuffer8) { @@ -1127,7 +1165,7 @@ namespace RTE { m_PixelLineBuffersGUIPrev[player] = nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendFrameSetupMsg(short player) { MsgFrameSetup msgFrameSetup; @@ -1147,7 +1185,7 @@ namespace RTE { int payloadSize = sizeof(MsgFrameSetup); - m_Server->Send((const char *)&msgFrameSetup, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)&msgFrameSetup, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); m_DataSentCurrent[player][STAT_CURRENT] += payloadSize; m_DataSentTotal[player] += payloadSize; @@ -1161,7 +1199,7 @@ namespace RTE { m_SendSceneData[player] = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::SendPostEffectData(short player) { std::list effects; @@ -1171,14 +1209,14 @@ namespace RTE { return; } - MsgPostEffects *msg = (MsgPostEffects *)m_CompressedLineBuffer[player]; - PostEffectNetworkData *effDataPtr = (PostEffectNetworkData *)((char *)msg + sizeof(MsgPostEffects)); + MsgPostEffects* msg = (MsgPostEffects*)m_CompressedLineBuffer[player]; + PostEffectNetworkData* effDataPtr = (PostEffectNetworkData*)((char*)msg + sizeof(MsgPostEffects)); msg->Id = ID_SRV_POST_EFFECTS; msg->FrameNumber = m_FrameNumbers[player]; msg->PostEffectsCount = 0; - for (const PostEffect postEffectEvent : effects) { + for (const PostEffect postEffectEvent: effects) { effDataPtr->X = postEffectEvent.m_Pos.GetX(); effDataPtr->Y = postEffectEvent.m_Pos.GetY(); effDataPtr->BitmapHash = postEffectEvent.m_BitmapHash; @@ -1190,9 +1228,9 @@ namespace RTE { if (msg->PostEffectsCount >= 75) { int payloadSize = sizeof(MsgPostEffects) + sizeof(PostEffectNetworkData) * msg->PostEffectsCount; - m_Server->Send((const char *)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); msg->PostEffectsCount = 0; - effDataPtr = (PostEffectNetworkData *)((char *)msg + sizeof(MsgPostEffects)); + effDataPtr = (PostEffectNetworkData*)((char*)msg + sizeof(MsgPostEffects)); m_PostEffectDataSentCurrent[player][STAT_CURRENT] += payloadSize; m_PostEffectDataSentTotal[player] += payloadSize; @@ -1202,13 +1240,13 @@ namespace RTE { } if (msg->PostEffectsCount > 0) { - //int header = sizeof(MsgPostEffects); - //int data = sizeof(PostEffectNetworkData); - //int total = header + data * msg->PostEffectsCount; - //int sz = sizeof(size_t); + // int header = sizeof(MsgPostEffects); + // int data = sizeof(PostEffectNetworkData); + // int total = header + data * msg->PostEffectsCount; + // int sz = sizeof(size_t); int payloadSize = sizeof(MsgPostEffects) + sizeof(PostEffectNetworkData) * msg->PostEffectsCount; - m_Server->Send((const char *)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)msg, payloadSize, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); m_PostEffectDataSentCurrent[player][STAT_CURRENT] += payloadSize; m_PostEffectDataSentTotal[player] += payloadSize; @@ -1217,7 +1255,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int NetworkServer::SendFrame(short player) { long long currentTicks = g_TimerMan.GetRealTickCount(); @@ -1229,7 +1267,9 @@ namespace RTE { double secsSinceLastFrame = static_cast(currentTicks - m_LastFrameSentTime[player]) / static_cast(g_TimerMan.GetTicksPerSecond()); // Fix for an overflow which may happen if server lags for a few seconds when loading activities - if (secsSinceLastFrame < 0) { secsSinceLastFrame = secsPerFrame; } + if (secsSinceLastFrame < 0) { + secsSinceLastFrame = secsPerFrame; + } m_MsecPerFrame[player] = static_cast(secsSinceLastFrame * 1000.0); @@ -1241,8 +1281,8 @@ namespace RTE { SetThreadExitReason(player, NetworkServer::NORMAL); // Get backbuffer bitmap for this player - BITMAP *frameManBmp = g_FrameMan.GetNetworkBackBuffer8Ready(player); - BITMAP *frameManGUIBmp = g_FrameMan.GetNetworkBackBufferGUI8Ready(player); + BITMAP* frameManBmp = g_FrameMan.GetNetworkBackBuffer8Ready(player); + BITMAP* frameManGUIBmp = g_FrameMan.GetNetworkBackBufferGUI8Ready(player); if (!m_BackBuffer8[player]) { CreateBackBuffer(player, frameManBmp->w, frameManBmp->h); @@ -1251,12 +1291,14 @@ namespace RTE { if (m_BackBuffer8[player]->w != frameManBmp->w || m_BackBuffer8[player]->h != frameManBmp->h) { DestroyBackBuffer(player); CreateBackBuffer(player, frameManBmp->w, frameManBmp->h); - //g_ConsoleMan.PrintString("SERVER: Backbuffer recreated"); + // g_ConsoleMan.PrintString("SERVER: Backbuffer recreated"); } } m_FrameNumbers[player]++; - if (m_FrameNumbers[player] >= c_FramesToRemember) { m_FrameNumbers[player] = 0; } + if (m_FrameNumbers[player] >= c_FramesToRemember) { + m_FrameNumbers[player] = 0; + } // Save a copy of buffer to avoid tearing when the original is updated by frame man blit(frameManBmp, m_BackBuffer8[player], 0, 0, 0, 0, frameManBmp->w, frameManBmp->h); @@ -1276,17 +1318,19 @@ namespace RTE { m_SendEven[player] = !m_SendEven[player]; if (m_TransmitAsBoxes) { - MsgFrameBox *frameData = (MsgFrameBox *)m_CompressedLineBuffer[player]; + MsgFrameBox* frameData = (MsgFrameBox*)m_CompressedLineBuffer[player]; int boxedWidth = m_BackBuffer8[player]->w / m_BoxWidth; int boxedHeight = m_BackBuffer8[player]->h / m_BoxHeight; int boxMaxSize = m_BoxWidth * m_BoxHeight; - if (m_BackBuffer8[player]->w % m_BoxWidth != 0) { boxedWidth += 1; } + if (m_BackBuffer8[player]->w % m_BoxWidth != 0) { + boxedWidth += 1; + } for (unsigned char layer = 0; layer < 2; layer++) { - const BITMAP *backBuffer = nullptr; - unsigned char *prevLineBuffers = 0; + const BITMAP* backBuffer = nullptr; + unsigned char* prevLineBuffers = 0; if (layer == 0) { backBuffer = m_BackBuffer8[player]; @@ -1309,10 +1353,14 @@ namespace RTE { frameData->BoxY = by; int maxWidth = m_BoxWidth; - if (bpx + m_BoxWidth >= m_BackBuffer8[player]->w) { maxWidth = m_BackBuffer8[player]->w - bpx; } + if (bpx + m_BoxWidth >= m_BackBuffer8[player]->w) { + maxWidth = m_BackBuffer8[player]->w - bpx; + } int maxHeight = m_BoxHeight; - if (bpy + m_BoxHeight >= m_BackBuffer8[player]->h) { maxHeight = m_BackBuffer8[player]->h - bpy; } + if (bpy + m_BoxHeight >= m_BackBuffer8[player]->h) { + maxHeight = m_BackBuffer8[player]->h - bpy; + } int lineStart = 0; int lineStep = 1; @@ -1320,7 +1368,9 @@ namespace RTE { if (m_UseInterlacing) { lineStep = 2; - if (m_SendEven[player]) { lineStart = 1; } + if (m_SendEven[player]) { + lineStart = 1; + } maxHeight /= 2; } int thisBoxSize = maxWidth * maxHeight; @@ -1329,7 +1379,7 @@ namespace RTE { bool boxIsDelta = false; bool sendEmptyBox = false; - unsigned char *dest = (unsigned char *)(m_PixelLineBuffer[player]); + unsigned char* dest = (unsigned char*)(m_PixelLineBuffer[player]); // Copy block line by line to linear buffer for (int line = lineStart; line < lineCount; line += lineStep) { @@ -1348,21 +1398,29 @@ namespace RTE { // Previous line to delta against int interlacedOffset = 0; - if (m_UseInterlacing) { interlacedOffset = m_SendEven[player] ? thisBoxSize : 0; } + if (m_UseInterlacing) { + interlacedOffset = m_SendEven[player] ? thisBoxSize : 0; + } - unsigned char *prevLineBufferStart = prevLineBuffers + by * boxedWidth * boxMaxSize + bx * boxMaxSize; - unsigned char *prevLineBufferWithOffset = prevLineBufferStart + interlacedOffset; + unsigned char* prevLineBufferStart = prevLineBuffers + by * boxedWidth * boxMaxSize + bx * boxMaxSize; + unsigned char* prevLineBufferWithOffset = prevLineBufferStart + interlacedOffset; // Currently processed box line buffer and delta storage - unsigned char *currLineBuffer = (unsigned char*)(m_PixelLineBufferDelta[player]); + unsigned char* currLineBuffer = (unsigned char*)(m_PixelLineBufferDelta[player]); // Calculate delta and decide whether we use it for (int i = 0; i < thisBoxSize; i++) { - if (currLineBuffer[i] > 0) { bytesNeededPlain++; } - if (prevLineBufferWithOffset[i] > 0) { bytesNeededPrev++; } + if (currLineBuffer[i] > 0) { + bytesNeededPlain++; + } + if (prevLineBufferWithOffset[i] > 0) { + bytesNeededPrev++; + } currLineBuffer[i] = currLineBuffer[i] - prevLineBufferWithOffset[i]; - if (currLineBuffer[i] > 0) { bytesNeededDelta++; } + if (currLineBuffer[i] > 0) { + bytesNeededDelta++; + } } // Store current line for delta check in the next frame @@ -1384,11 +1442,13 @@ namespace RTE { } } else { // Previous non empty block is now empty, clear it - if (bytesNeededPrev > 0 && bytesNeededPlain == 0) { sendEmptyBox = true; } + if (bytesNeededPrev > 0 && bytesNeededPlain == 0) { + sendEmptyBox = true; + } } } else { // Check if block is empty by evaluating by 64-bit ints - unsigned long *pixelInt = (unsigned long *)m_PixelLineBuffer[player]; + unsigned long* pixelInt = (unsigned long*)m_PixelLineBuffer[player]; int counter = 0; for (counter = 0; counter < thisBoxSize; counter += sizeof(unsigned long)) { @@ -1403,7 +1463,7 @@ namespace RTE { pixelInt--; counter -= sizeof(unsigned long); - const unsigned char *pixelChr = (unsigned char*)pixelInt; + const unsigned char* pixelChr = (unsigned char*)pixelInt; for (; counter < thisBoxSize; counter++) { if (*pixelChr > 0) { boxIsEmpty = false; @@ -1424,14 +1484,16 @@ namespace RTE { if (boxIsEmpty) { frameData->DataSize = 0; - if (!sendEmptyBox) { m_EmptyBlocks[player]++; } + if (!sendEmptyBox) { + m_EmptyBlocks[player]++; + } } else { int result = 0; if (m_UseHighCompression) { - result = LZ4_compress_HC_extStateHC(m_LZ4CompressionState[player], (char *)m_PixelLineBuffer[player], (char *)(m_CompressedLineBuffer[player] + sizeof(MsgFrameBox)), thisBoxSize, thisBoxSize, compressionMethod); + result = LZ4_compress_HC_extStateHC(m_LZ4CompressionState[player], (char*)m_PixelLineBuffer[player], (char*)(m_CompressedLineBuffer[player] + sizeof(MsgFrameBox)), thisBoxSize, thisBoxSize, compressionMethod); } else if (m_UseFastCompression) { - result = LZ4_compress_fast_extState(m_LZ4FastCompressionState[player], (char *)m_PixelLineBuffer[player], (char *)(m_CompressedLineBuffer[player] + sizeof(MsgFrameBox)), thisBoxSize, thisBoxSize, accelerationFactor); + result = LZ4_compress_fast_extState(m_LZ4FastCompressionState[player], (char*)m_PixelLineBuffer[player], (char*)(m_CompressedLineBuffer[player] + sizeof(MsgFrameBox)), thisBoxSize, thisBoxSize, accelerationFactor); } // Compression failed or ineffective, send as is @@ -1451,7 +1513,7 @@ namespace RTE { int payloadSize = frameData->DataSize + sizeof(MsgFrameBox); if (!boxIsEmpty || sendEmptyBox) { - m_Server->Send((const char *)frameData, payloadSize, MEDIUM_PRIORITY, UNRELIABLE_SEQUENCED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)frameData, payloadSize, MEDIUM_PRIORITY, UNRELIABLE_SEQUENCED, 0, m_ClientConnections[player].ClientId, false); } else { payloadSize = 0; } @@ -1476,7 +1538,7 @@ namespace RTE { } } } else { - MsgFrameLine *frameData = (MsgFrameLine *)m_CompressedLineBuffer[player]; + MsgFrameLine* frameData = (MsgFrameLine*)m_CompressedLineBuffer[player]; frameData->FrameNumber = m_FrameNumbers[player]; // Save message ID @@ -1493,7 +1555,7 @@ namespace RTE { for (int m_CurrentFrameLine = startLine; m_CurrentFrameLine < m_BackBuffer8[player]->h; m_CurrentFrameLine += step) { for (int layer = 0; layer < 2; layer++) { - const BITMAP *backBuffer = 0; + const BITMAP* backBuffer = 0; if (layer == 0) { backBuffer = m_BackBuffer8[player]; @@ -1515,7 +1577,7 @@ namespace RTE { // Check if line is empty { - unsigned long *pixelInt = (unsigned long *)backBuffer->line[m_CurrentFrameLine]; + unsigned long* pixelInt = (unsigned long*)backBuffer->line[m_CurrentFrameLine]; int counter = 0; for (counter = 0; counter < backBuffer->w; counter += sizeof(unsigned long)) { if (*pixelInt > 0) { @@ -1528,7 +1590,7 @@ namespace RTE { pixelInt--; counter -= sizeof(unsigned long); - const unsigned char *pixelChr = (unsigned char *)pixelInt; + const unsigned char* pixelChr = (unsigned char*)pixelInt; for (; counter < backBuffer->w; counter++) { if (*pixelChr > 0) { lineIsEmpty = false; @@ -1541,9 +1603,9 @@ namespace RTE { if (!lineIsEmpty) { if (m_UseHighCompression) { - result = LZ4_compress_HC_extStateHC(m_LZ4CompressionState[player], (char *)backBuffer->line[m_CurrentFrameLine], (char *)(m_CompressedLineBuffer[player] + sizeof(MsgFrameLine)), backBuffer->w, backBuffer->w, compressionMethod); + result = LZ4_compress_HC_extStateHC(m_LZ4CompressionState[player], (char*)backBuffer->line[m_CurrentFrameLine], (char*)(m_CompressedLineBuffer[player] + sizeof(MsgFrameLine)), backBuffer->w, backBuffer->w, compressionMethod); } else if (m_UseFastCompression) { - result = LZ4_compress_fast_extState(m_LZ4FastCompressionState[player], (char *)backBuffer->line[m_CurrentFrameLine], (char *)(m_CompressedLineBuffer[player] + sizeof(MsgFrameLine)), backBuffer->w, backBuffer->w, accelerationFactor); + result = LZ4_compress_fast_extState(m_LZ4FastCompressionState[player], (char*)backBuffer->line[m_CurrentFrameLine], (char*)(m_CompressedLineBuffer[player] + sizeof(MsgFrameLine)), backBuffer->w, backBuffer->w, accelerationFactor); } // Compression failed or ineffective, send as is @@ -1565,7 +1627,7 @@ namespace RTE { int payloadSize = frameData->DataSize + sizeof(MsgFrameLine); - m_Server->Send((const char *)frameData, payloadSize, MEDIUM_PRIORITY, UNRELIABLE_SEQUENCED, 0, m_ClientConnections[player].ClientId, false); + m_Server->Send((const char*)frameData, payloadSize, MEDIUM_PRIORITY, UNRELIABLE_SEQUENCED, 0, m_ClientConnections[player].ClientId, false); m_DataSentCurrent[player][STAT_CURRENT] += payloadSize; m_DataSentTotal[player] += payloadSize; @@ -1589,7 +1651,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::UpdateStats(short player) { long long currentTicks = g_TimerMan.GetRealTickCount(); @@ -1628,12 +1690,12 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::DrawStatisticsData() { int midX = g_WindowMan.GetResX() / 2; - BITMAP *bmp = g_FrameMan.GetBackBuffer8(); + BITMAP* bmp = g_FrameMan.GetBackBuffer8(); AllegroBitmap guiBMP(bmp); clear_to_color(bmp, g_BlackColor); @@ -1652,7 +1714,7 @@ namespace RTE { } if (g_ActivityMan.IsInActivity()) { - const GameActivity *gameActivity = dynamic_cast(g_ActivityMan.GetActivity()); + const GameActivity* gameActivity = dynamic_cast(g_ActivityMan.GetActivity()); if (gameActivity) { std::snprintf(buf, sizeof(buf), "Activity: %s Players: %d", gameActivity->GetPresetName().c_str(), gameActivity->GetPlayerCount()); g_FrameMan.GetLargeFont()->DrawAligned(&guiBMP, midX, 50, buf, GUIFont::Centre); @@ -1687,7 +1749,6 @@ namespace RTE { m_FullBlocks[c_MaxClients] = 0; m_EmptyBlocks[c_MaxClients] = 0; - for (short i = 0; i < MAX_STAT_RECORDS; i++) { // Update sum if (i < c_MaxClients) { @@ -1729,47 +1790,46 @@ namespace RTE { // Jesus christ std::snprintf(buf, sizeof(buf), - "%s\nPing %u\n" - "Cmp Mbit : % .1f\n" - "Unc Mbit : % .1f\n" - "R : % .2f\n" - "Full Blck %lu (%.1f Kb)\n" - "Empty Blck %lu (%.1f Kb)\n" - "Frame Kb : %lu\n" - "Glow Kb : %lu\n" - "Sound Kb : %lu\n" - "Scene Kb : %lu\n" - "Frames sent : %uK\n" - "Frame skipped : %uK\n" - "Blocks full : %uK\n" - "Blocks empty : %uK\n" - "Blk Ratio : % .2f\n" - "Frames ms : % d\n" - "Send ms % d\n" - "Total Data %lu MB", - - (i == c_MaxClients) ? "- TOTALS - " : playerName.c_str(), - (i < c_MaxClients) ? m_Ping[i] : 0, - static_cast(m_DataSentCurrent[i][STAT_SHOWN]) / 125000, - static_cast(m_DataUncompressedCurrent[i][STAT_SHOWN]) / 125000, - compressionRatio, - m_FullBlocksSentCurrent[i][STAT_SHOWN], - static_cast(m_FullBlocksDataSentCurrent[i][STAT_SHOWN]) / (125), - m_EmptyBlocksSentCurrent[i][STAT_SHOWN], - static_cast(m_EmptyBlocksDataSentCurrent[i][STAT_SHOWN]) / (125), - m_FrameDataSentCurrent[i][STAT_SHOWN] / 125, - m_PostEffectDataSentCurrent[i][STAT_SHOWN] / 125, - m_SoundDataSentCurrent[i][STAT_SHOWN] / 125, - m_TerrainDataSentCurrent[i][STAT_SHOWN] / 125, - m_FramesSent[i] / 1000, - m_FramesSkipped[i], - m_FullBlocks[i] / 1000, - m_EmptyBlocks[i] / 1000, - emptyRatio, - (i < c_MaxClients) ? m_MsecPerFrame[i] : 0, - (i < c_MaxClients) ? m_MsecPerSendCall[i] : 0, - m_DataSentTotal[i] / (1024 * 1024) - ); + "%s\nPing %u\n" + "Cmp Mbit : % .1f\n" + "Unc Mbit : % .1f\n" + "R : % .2f\n" + "Full Blck %lu (%.1f Kb)\n" + "Empty Blck %lu (%.1f Kb)\n" + "Frame Kb : %lu\n" + "Glow Kb : %lu\n" + "Sound Kb : %lu\n" + "Scene Kb : %lu\n" + "Frames sent : %uK\n" + "Frame skipped : %uK\n" + "Blocks full : %uK\n" + "Blocks empty : %uK\n" + "Blk Ratio : % .2f\n" + "Frames ms : % d\n" + "Send ms % d\n" + "Total Data %lu MB", + + (i == c_MaxClients) ? "- TOTALS - " : playerName.c_str(), + (i < c_MaxClients) ? m_Ping[i] : 0, + static_cast(m_DataSentCurrent[i][STAT_SHOWN]) / 125000, + static_cast(m_DataUncompressedCurrent[i][STAT_SHOWN]) / 125000, + compressionRatio, + m_FullBlocksSentCurrent[i][STAT_SHOWN], + static_cast(m_FullBlocksDataSentCurrent[i][STAT_SHOWN]) / (125), + m_EmptyBlocksSentCurrent[i][STAT_SHOWN], + static_cast(m_EmptyBlocksDataSentCurrent[i][STAT_SHOWN]) / (125), + m_FrameDataSentCurrent[i][STAT_SHOWN] / 125, + m_PostEffectDataSentCurrent[i][STAT_SHOWN] / 125, + m_SoundDataSentCurrent[i][STAT_SHOWN] / 125, + m_TerrainDataSentCurrent[i][STAT_SHOWN] / 125, + m_FramesSent[i] / 1000, + m_FramesSkipped[i], + m_FullBlocks[i] / 1000, + m_EmptyBlocks[i] / 1000, + emptyRatio, + (i < c_MaxClients) ? m_MsecPerFrame[i] : 0, + (i < c_MaxClients) ? m_MsecPerSendCall[i] : 0, + m_DataSentTotal[i] / (1024 * 1024)); g_FrameMan.GetLargeFont()->DrawAligned(&guiBMP, 10 + i * g_WindowMan.GetResX() / 5, 75, buf, GUIFont::Left); @@ -1781,14 +1841,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - RakNet::SystemAddress NetworkServer::ConnectBlocking(RakNet::RakPeerInterface *rakPeer, const char *address, unsigned short port) { + RakNet::SystemAddress NetworkServer::ConnectBlocking(RakNet::RakPeerInterface* rakPeer, const char* address, unsigned short port) { if (rakPeer->Connect(address, port, 0, 0) != RakNet::CONNECTION_ATTEMPT_STARTED) { return RakNet::UNASSIGNED_SYSTEM_ADDRESS; } - RakNet::Packet *packet; + RakNet::Packet* packet; while (true) { for (packet = rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet = rakPeer->Receive()) { if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) { @@ -1803,7 +1863,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::Update(bool processInput) { HandleNetworkPackets(); @@ -1811,7 +1871,7 @@ namespace RTE { if (processInput) { for (short player = 0; player < c_MaxClients; player++) { if (!m_InputMessages[player].empty()) { - for (const MsgInput &msg: m_InputMessages[player]) { + for (const MsgInput& msg: m_InputMessages[player]) { ProcessInputMsg(player, msg); } m_InputMessages[player].clear(); @@ -1846,7 +1906,9 @@ namespace RTE { displayMsg = "Voting to end activity: " + std::to_string(endActivityVotes) + " of " + std::to_string(votesNeeded); } if (restartVotes > 0) { - if (!displayMsg.empty()) { displayMsg += "\n"; } + if (!displayMsg.empty()) { + displayMsg += "\n"; + } displayMsg += "Voting to restart activity: " + std::to_string(restartVotes) + " of " + std::to_string(votesNeeded); } @@ -1866,11 +1928,15 @@ namespace RTE { g_ActivityMan.EndActivity(); g_ActivityMan.SetRestartActivity(); g_ActivityMan.SetInActivity(false); - for (short player = 0; player < c_MaxClients; player++) { ClearInputMessages(player); } + for (short player = 0; player < c_MaxClients; player++) { + ClearInputMessages(player); + } } else if (restartVotes >= votesNeeded) { m_LatestRestartTime = currentTicks; g_ActivityMan.RestartActivity(); - for (short player = 0; player < c_MaxClients; player++) { ClearInputMessages(player); } + for (short player = 0; player < c_MaxClients; player++) { + ClearInputMessages(player); + } } } } @@ -1895,14 +1961,16 @@ namespace RTE { break; } - if (playersConnected == 0) { RakSleep(250); } + if (playersConnected == 0) { + RakSleep(250); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void NetworkServer::HandleNetworkPackets() { - for (RakNet::Packet *packet = m_Server->Receive(); packet; m_Server->DeallocatePacket(packet), packet = m_Server->Receive()) { + for (RakNet::Packet* packet = m_Server->Receive(); packet; m_Server->DeallocatePacket(packet), packet = m_Server->Receive()) { m_LastPackedReceived->Reset(); // We got a packet, get the identifier with our handy function @@ -1972,4 +2040,4 @@ namespace RTE { } } } -} +} // namespace RTE diff --git a/Source/Managers/NetworkServer.h b/Source/Managers/NetworkServer.h index d5bf687cb4..8a14d3111f 100644 --- a/Source/Managers/NetworkServer.h +++ b/Source/Managers/NetworkServer.h @@ -28,7 +28,6 @@ namespace RTE { friend class SettingsMan; public: - /// /// /// @@ -66,7 +65,10 @@ namespace RTE { /// /// Constructor method used to instantiate a NetworkServer object in system memory. This will call Create() so it shouldn't be called after. /// - NetworkServer() { Clear(); Initialize(); } + NetworkServer() { + Clear(); + Initialize(); + } /// /// Makes the NetworkServer object ready for use. @@ -110,7 +112,7 @@ namespace RTE { /// /// The player to check for. /// A string with the network player's name. - std::string & GetPlayerName(short player) { return m_ClientConnections[player].PlayerName; } + std::string& GetPlayerName(short player) { return m_ClientConnections[player].PlayerName; } /// /// Gets whether the specified player is connected to the server or not. @@ -123,7 +125,7 @@ namespace RTE { /// Sets the port this server will be using. /// /// The new port to set. - void SetServerPort(const std::string &newPort); + void SetServerPort(const std::string& newPort); /// /// Sets whether interlacing is used to reduce bandwidth usage or not. @@ -191,7 +193,6 @@ namespace RTE { #pragma endregion protected: - /// /// /// @@ -201,7 +202,7 @@ namespace RTE { RakNet::SystemAddress InternalId; //!< int ResX; //!< int ResY; //!< - std::thread *SendThread; //!< + std::thread* SendThread; //!< std::string PlayerName; //!< }; @@ -215,7 +216,7 @@ namespace RTE { long m_MSecsSinceLastUpdate[c_MaxClients]; //!< long m_MSecsToSleep[c_MaxClients]; //!< - RakNet::RakPeerInterface *m_Server; //!< + RakNet::RakPeerInterface* m_Server; //!< std::string m_ServerPort; //!< @@ -226,11 +227,11 @@ namespace RTE { RakNet::SystemAddress m_NATServiceServerID; //!< bool m_NatServerConnected; //!< - BITMAP *m_BackBuffer8[c_MaxClients]; //!< Buffers to store client screens before compression. - BITMAP *m_BackBufferGUI8[c_MaxClients]; //!< Buffers to store client GUI screens before compression. + BITMAP* m_BackBuffer8[c_MaxClients]; //!< Buffers to store client screens before compression. + BITMAP* m_BackBufferGUI8[c_MaxClients]; //!< Buffers to store client GUI screens before compression. - void *m_LZ4CompressionState[c_MaxClients]; //!< - void *m_LZ4FastCompressionState[c_MaxClients]; //!< + void* m_LZ4CompressionState[c_MaxClients]; //!< + void* m_LZ4FastCompressionState[c_MaxClients]; //!< int m_MouseState1[c_MaxClients]; //!< int m_MouseState2[c_MaxClients]; //!< @@ -268,8 +269,8 @@ namespace RTE { unsigned char m_PixelLineBufferDelta[c_MaxClients][c_MaxPixelLineBufferSize]; //!< Buffer to store currently transferred pixel data line. unsigned char m_CompressedLineBuffer[c_MaxClients][c_MaxPixelLineBufferSize]; //!< Buffer to store compressed pixel data line. - unsigned char *m_PixelLineBuffersPrev[c_MaxClients]; //!< - unsigned char *m_PixelLineBuffersGUIPrev[c_MaxClients]; //!< + unsigned char* m_PixelLineBuffersPrev[c_MaxClients]; //!< + unsigned char* m_PixelLineBuffersGUIPrev[c_MaxClients]; //!< std::queue m_PendingTerrainChanges[c_MaxClients]; //!< std::queue m_CurrentTerrainChanges[c_MaxClients]; //!< @@ -329,27 +330,26 @@ namespace RTE { unsigned long m_FrameDataSentCurrent[MAX_STAT_RECORDS][2]; //!< unsigned long m_FrameDataSentTotal[MAX_STAT_RECORDS]; //!< - unsigned long m_PostEffectDataSentCurrent[MAX_STAT_RECORDS][2]; //!< - unsigned long m_PostEffectDataSentTotal[MAX_STAT_RECORDS]; //!< + unsigned long m_PostEffectDataSentCurrent[MAX_STAT_RECORDS][2]; //!< + unsigned long m_PostEffectDataSentTotal[MAX_STAT_RECORDS]; //!< - unsigned long m_SoundDataSentCurrent[MAX_STAT_RECORDS][2]; //!< - unsigned long m_SoundDataSentTotal[MAX_STAT_RECORDS]; //!< + unsigned long m_SoundDataSentCurrent[MAX_STAT_RECORDS][2]; //!< + unsigned long m_SoundDataSentTotal[MAX_STAT_RECORDS]; //!< - unsigned long m_TerrainDataSentCurrent[MAX_STAT_RECORDS][2]; //!< - unsigned long m_TerrainDataSentTotal[MAX_STAT_RECORDS]; //!< + unsigned long m_TerrainDataSentCurrent[MAX_STAT_RECORDS][2]; //!< + unsigned long m_TerrainDataSentTotal[MAX_STAT_RECORDS]; //!< unsigned long m_OtherDataSentCurrent[MAX_STAT_RECORDS][2]; //!< unsigned long m_OtherDataSentTotal[MAX_STAT_RECORDS]; //!< private: - #pragma region Thread Handling /// /// /// /// /// - static void BackgroundSendThreadFunction(NetworkServer *server, short player); + static void BackgroundSendThreadFunction(NetworkServer* server, short player); /// /// @@ -365,13 +365,13 @@ namespace RTE { /// /// /// - unsigned char GetPacketIdentifier(RakNet::Packet *packet) const; + unsigned char GetPacketIdentifier(RakNet::Packet* packet) const; /// /// /// /// - void ReceiveNewIncomingConnection(RakNet::Packet *packet); + void ReceiveNewIncomingConnection(RakNet::Packet* packet); /// /// @@ -383,13 +383,13 @@ namespace RTE { /// /// /// - void ReceiveDisconnection(RakNet::Packet *packet); + void ReceiveDisconnection(RakNet::Packet* packet); /// /// /// /// - void ReceiveRegisterMsg(RakNet::Packet *packet); + void ReceiveRegisterMsg(RakNet::Packet* packet); /// /// @@ -401,7 +401,7 @@ namespace RTE { /// /// /// - void ReceiveInputMsg(RakNet::Packet *packet); + void ReceiveInputMsg(RakNet::Packet* packet); /// /// @@ -454,7 +454,7 @@ namespace RTE { /// /// /// - void ReceiveSceneSetupDataAccepted(RakNet::Packet *packet); + void ReceiveSceneSetupDataAccepted(RakNet::Packet* packet); /// /// @@ -499,7 +499,7 @@ namespace RTE { /// /// /// - void ReceiveSceneAcceptedMsg(RakNet::Packet *packet); + void ReceiveSceneAcceptedMsg(RakNet::Packet* packet); /// /// @@ -591,7 +591,7 @@ namespace RTE { /// /// /// - RakNet::SystemAddress ConnectBlocking(RakNet::RakPeerInterface *rakPeer, const char *address, unsigned short port); + RakNet::SystemAddress ConnectBlocking(RakNet::RakPeerInterface* rakPeer, const char* address, unsigned short port); /// /// Clears all the member variables of this NetworkServer, effectively resetting the members of this abstraction level only. @@ -599,8 +599,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - NetworkServer(const NetworkServer &reference) = delete; - NetworkServer & operator=(const NetworkServer &rhs) = delete; + NetworkServer(const NetworkServer& reference) = delete; + NetworkServer& operator=(const NetworkServer& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Managers/PerformanceMan.cpp b/Source/Managers/PerformanceMan.cpp index 42bde776fd..9d559d7a9a 100644 --- a/Source/Managers/PerformanceMan.cpp +++ b/Source/Managers/PerformanceMan.cpp @@ -8,12 +8,12 @@ namespace RTE { - const std::array PerformanceMan::m_PerfCounterNames = { "Total", "Act AI", "Act Travel", "Act Update", "Prt Travel", "Prt Update", "Activity", "Scripts"}; + const std::array PerformanceMan::m_PerfCounterNames = {"Total", "Act AI", "Act Travel", "Act Update", "Prt Travel", "Prt Update", "Activity", "Scripts"}; thread_local std::array s_PerfMeasureStart; //!< Current measurement start time in microseconds. thread_local std::array s_PerfMeasureStop; //!< Current measurement stop time in microseconds. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PerformanceMan::Clear() { m_ShowPerfStats = false; @@ -31,7 +31,7 @@ namespace RTE { m_CurrentPing = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PerformanceMan::Initialize() { m_SimUpdateTimer = std::make_unique(); @@ -44,24 +44,26 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PerformanceMan::StartPerformanceMeasurement(PerformanceCounters counter) { s_PerfMeasureStart[counter] = g_TimerMan.GetAbsoluteTime(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PerformanceMan::StopPerformanceMeasurement(PerformanceCounters counter) { s_PerfMeasureStop[counter] = g_TimerMan.GetAbsoluteTime(); AddPerformanceSample(counter, s_PerfMeasureStop[counter] - s_PerfMeasureStart[counter]); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PerformanceMan::NewPerformanceSample() { m_Sample++; - if (m_Sample >= c_MaxSamples) { m_Sample = 0; } + if (m_Sample >= c_MaxSamples) { + m_Sample = 0; + } for (int counter = 0; counter < PerformanceCounters::PerfCounterCount; ++counter) { m_PerfData[counter][m_Sample] = 0; @@ -69,7 +71,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PerformanceMan::CalculateSamplePercentages() { for (int counter = 0; counter < PerformanceCounters::PerfCounterCount; ++counter) { @@ -78,7 +80,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// uint64_t PerformanceMan::GetPerformanceCounterAverage(PerformanceCounters counter) const { uint64_t totalPerformanceMeasurement = 0; @@ -86,26 +88,28 @@ namespace RTE { for (int i = 0; i < c_Average; ++i) { totalPerformanceMeasurement += m_PerfData[counter][sample]; sample--; - if (sample < 0) { sample = c_MaxSamples - 1; } + if (sample < 0) { + sample = c_MaxSamples - 1; + } } return totalPerformanceMeasurement / c_Average; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::CalculateTimeAverage(std::deque &timeMeasurements, float &avgResult, float newTimeMeasurement) const { + void PerformanceMan::CalculateTimeAverage(std::deque& timeMeasurements, float& avgResult, float newTimeMeasurement) const { timeMeasurements.emplace_back(newTimeMeasurement); while (timeMeasurements.size() > c_MSPAverageSampleSize) { timeMeasurements.pop_front(); } avgResult = 0; - for (const float &timeMeasurement : timeMeasurements) { + for (const float& timeMeasurement: timeMeasurements) { avgResult += timeMeasurement; } avgResult /= static_cast(timeMeasurements.size()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PerformanceMan::UpdateMSPF(long long measuredUpdateTime, long long measuredDrawTime) { CalculateTimeAverage(m_MSPUs, m_MSPUAverage, static_cast(measuredUpdateTime / 1000)); @@ -113,13 +117,13 @@ namespace RTE { CalculateTimeAverage(m_MSPFs, m_MSPFAverage, static_cast((measuredUpdateTime + measuredDrawTime) / 1000)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::Draw(BITMAP *bitmapToDrawTo) { + void PerformanceMan::Draw(BITMAP* bitmapToDrawTo) { if (m_ShowPerfStats) { AllegroBitmap drawBitmap(bitmapToDrawTo); - GUIFont *guiFont = g_FrameMan.GetLargeFont(true); + GUIFont* guiFont = g_FrameMan.GetLargeFont(true); char str[128]; float fps = 1.0F / (m_MSPFAverage / 1000.0F); @@ -158,8 +162,7 @@ namespace RTE { std::snprintf(str, sizeof(str), "Lua scripts taking the most time to call Update() this frame:"); guiFont->DrawAligned(&drawBitmap, c_StatsOffsetX, c_StatsHeight + 100, str, GUIFont::Left); - for (int i = 0; i < std::min((size_t)3, m_SortedScriptTimings.size()); i++) - { + for (int i = 0; i < std::min((size_t)3, m_SortedScriptTimings.size()); i++) { std::pair scriptTiming = m_SortedScriptTimings.at(i); std::snprintf(str, sizeof(str), "%.1fms total with %i calls in %s", scriptTiming.second.m_Time / 1000.0, scriptTiming.second.m_CallCount, scriptTiming.first.c_str()); @@ -173,12 +176,12 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::DrawPeformanceGraphs(AllegroBitmap &bitmapToDrawTo) { + void PerformanceMan::DrawPeformanceGraphs(AllegroBitmap& bitmapToDrawTo) { CalculateSamplePercentages(); - GUIFont *guiFont = g_FrameMan.GetLargeFont(true); + GUIFont* guiFont = g_FrameMan.GetLargeFont(true); char str[128]; for (int pc = 0; pc < PerformanceCounters::PerfCounterCount; ++pc) { @@ -210,9 +213,13 @@ namespace RTE { bitmapToDrawTo.SetPixel(c_StatsOffsetX - 1 + c_MaxSamples - i, graphStart + c_GraphHeight - dotHeight, makecol32(234, 21, 7)); // Palette index 13. - if (peak < m_PerfData[pc][sample]) { peak = static_cast(m_PerfData[pc][sample]); } + if (peak < m_PerfData[pc][sample]) { + peak = static_cast(m_PerfData[pc][sample]); + } - if (sample == 0) { sample = c_MaxSamples; } + if (sample == 0) { + sample = c_MaxSamples; + } sample--; } @@ -221,16 +228,16 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PerformanceMan::DrawCurrentPing() const { AllegroBitmap allegroBitmap(g_FrameMan.GetBackBuffer8()); g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, g_FrameMan.GetBackBuffer8()->w - 25, g_FrameMan.GetBackBuffer8()->h - 14, "PING: " + std::to_string(m_CurrentPing), GUIFont::Right); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::UpdateSortedScriptTimings(const std::unordered_map &scriptTimings) { + void PerformanceMan::UpdateSortedScriptTimings(const std::unordered_map& scriptTimings) { std::vector> sortedScriptTimings; for (auto it = scriptTimings.begin(); it != scriptTimings.end(); it++) { sortedScriptTimings.push_back(*it); @@ -240,4 +247,4 @@ namespace RTE { g_PerformanceMan.m_SortedScriptTimings = sortedScriptTimings; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Managers/PerformanceMan.h b/Source/Managers/PerformanceMan.h index 5e6d009af3..448cb27132 100644 --- a/Source/Managers/PerformanceMan.h +++ b/Source/Managers/PerformanceMan.h @@ -19,7 +19,6 @@ namespace RTE { friend class SettingsMan; public: - /// /// Enumeration of all available performance counters. /// @@ -134,7 +133,12 @@ namespace RTE { /// /// Clears current performance timings. /// - void ResetPerformanceTimings() { m_MSPSUs.clear(); m_MSPFs.clear(); m_MSPUs.clear(); m_MSPDs.clear(); } + void ResetPerformanceTimings() { + m_MSPSUs.clear(); + m_MSPFs.clear(); + m_MSPUs.clear(); + m_MSPDs.clear(); + } /// /// Resets the sim update timer. @@ -151,13 +155,16 @@ namespace RTE { /// /// Updates the individual sim update time measurements and recalculates the average. Supposed to be done every sim update. /// - void UpdateMSPSU() { CalculateTimeAverage(m_MSPSUs, m_MSPSUAverage, static_cast(m_SimUpdateTimer->GetElapsedRealTimeMS())); m_SimUpdateTimer->Reset(); } + void UpdateMSPSU() { + CalculateTimeAverage(m_MSPSUs, m_MSPSUAverage, static_cast(m_SimUpdateTimer->GetElapsedRealTimeMS())); + m_SimUpdateTimer->Reset(); + } /// /// Draws the performance stats to the screen. /// /// The BITMAP to draw the performance stats to. - void Draw(BITMAP *bitmapToDrawTo); + void Draw(BITMAP* bitmapToDrawTo); /// /// Draws the current ping value to the screen. @@ -168,10 +175,9 @@ namespace RTE { /// /// Updates m_SortedScriptTimings so PerformanceMan::Draw() can list how long scripts took. /// - void UpdateSortedScriptTimings(const std::unordered_map &scriptTimings); + void UpdateSortedScriptTimings(const std::unordered_map& scriptTimings); protected: - static constexpr int c_MSPAverageSampleSize = 10; //!< How many samples to use to calculate average milliseconds-per-something value. static constexpr int c_MaxSamples = 120; //!< How many performance samples to store, directly affects graph size. static constexpr int c_Average = 10; //!< How many samples to use to calculate average value displayed on screen. @@ -209,7 +215,6 @@ namespace RTE { std::vector> m_SortedScriptTimings; //!< Sorted vector storing how long scripts took to execute. private: - #pragma region Performance Counter Handling /// /// Adds provided value to current sample of specified performance counter. @@ -237,12 +242,12 @@ namespace RTE { /// The deque of time measurements to store the new measurement in and to recalculate the average with. /// The variable the recalculated average should be stored in. /// The new time measurement to store. - void CalculateTimeAverage(std::deque &timeMeasurements, float &avgResult, float newTimeMeasurement) const; + void CalculateTimeAverage(std::deque& timeMeasurements, float& avgResult, float newTimeMeasurement) const; /// /// Draws the performance graphs to the screen. This will be called by Draw() if advanced performance stats are enabled. /// - void DrawPeformanceGraphs(AllegroBitmap &bitmapToDrawTo); + void DrawPeformanceGraphs(AllegroBitmap& bitmapToDrawTo); /// /// Clears all the member variables of this PerformanceMan, effectively resetting the members of this abstraction level only. @@ -250,8 +255,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - PerformanceMan(const PerformanceMan &reference) = delete; - PerformanceMan & operator=(const PerformanceMan &rhs) = delete; + PerformanceMan(const PerformanceMan& reference) = delete; + PerformanceMan& operator=(const PerformanceMan& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Managers/PostProcessMan.cpp b/Source/Managers/PostProcessMan.cpp index 89ca5aefb6..67b2a2865e 100644 --- a/Source/Managers/PostProcessMan.cpp +++ b/Source/Managers/PostProcessMan.cpp @@ -16,7 +16,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostProcessMan::Clear() { m_PostScreenEffects.clear(); @@ -41,7 +41,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int PostProcessMan::Initialize() { InitializeGLPointers(); @@ -72,7 +72,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostProcessMan::InitializeGLPointers() { glGenTextures(1, &m_BackBuffer8); @@ -89,19 +89,19 @@ namespace RTE { glBufferData(GL_ARRAY_BUFFER, sizeof(c_Quad), c_Quad.data(), GL_STATIC_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr); glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float))); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); glEnableVertexAttribArray(1); glBindVertexArray(0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostProcessMan::DestroyGLPointers() { glDeleteTextures(1, &m_BackBuffer8); glDeleteTextures(1, &m_BackBuffer32); glDeleteTextures(1, &m_Palette8Texture); glDeleteFramebuffers(1, &m_BlitFramebuffer); - for (auto &bitmapTexture : m_BitmapTextures) { + for (auto& bitmapTexture: m_BitmapTextures) { glDeleteTextures(1, &bitmapTexture->m_Texture); } glDeleteFramebuffers(1, &m_PostProcessFramebuffer); @@ -110,7 +110,7 @@ namespace RTE { glDeleteBuffers(1, &m_VertexBuffer); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostProcessMan::CreateGLBackBuffers() { glBindTexture(GL_TEXTURE_2D, m_BackBuffer8); @@ -131,7 +131,7 @@ namespace RTE { m_ProjectionMatrix = glm::ortho(0.0F, static_cast(g_FrameMan.GetBackBuffer8()->w), 0.0F, static_cast(g_FrameMan.GetBackBuffer8()->h), -1.0F, 1.0F); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostProcessMan::UpdatePalette() { glBindTexture(GL_TEXTURE_1D, m_Palette8Texture); @@ -148,12 +148,12 @@ namespace RTE { glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::LazyInitBitmap(BITMAP *bitmap) { + void PostProcessMan::LazyInitBitmap(BITMAP* bitmap) { m_BitmapTextures.emplace_back(new GLBitmapInfo); glGenTextures(1, &m_BitmapTextures.back()->m_Texture); - bitmap->extra = reinterpret_cast(m_BitmapTextures.back().get()); + bitmap->extra = reinterpret_cast(m_BitmapTextures.back().get()); glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap_color_depth(bitmap) == 8 ? 1 : 4); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, reinterpret_cast(bitmap->extra)->m_Texture); @@ -163,10 +163,10 @@ namespace RTE { glGenerateMipmap(GL_TEXTURE_2D); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostProcessMan::Destroy() { - for (std::pair tempBitmapEntry : m_TempEffectBitmaps) { + for (std::pair tempBitmapEntry: m_TempEffectBitmaps) { destroy_bitmap(tempBitmapEntry.second); } DestroyGLPointers(); @@ -175,9 +175,9 @@ namespace RTE { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::AdjustEffectsPosToPlayerScreen(int playerScreen, BITMAP *targetBitmap, const Vector &targetBitmapOffset, std::list &screenRelativeEffectsList, std::list &screenRelativeGlowBoxesList) { + void PostProcessMan::AdjustEffectsPosToPlayerScreen(int playerScreen, BITMAP* targetBitmap, const Vector& targetBitmapOffset, std::list& screenRelativeEffectsList, std::list& screenRelativeGlowBoxesList) { int screenOcclusionOffsetX = g_CameraMan.GetScreenOcclusion(playerScreen).GetFloorIntX(); int screenOcclusionOffsetY = g_CameraMan.GetScreenOcclusion(playerScreen).GetFloorIntY(); int occludedOffsetX = targetBitmap->w + screenOcclusionOffsetX; @@ -189,34 +189,34 @@ namespace RTE { } // Adjust for the player screen's position on the final buffer - for (const PostEffect &postEffect : screenRelativeEffectsList) { + for (const PostEffect& postEffect: screenRelativeEffectsList) { // Make sure we won't be adding any effects to a part of the screen that is occluded by menus and such if (postEffect.m_Pos.GetFloorIntX() > screenOcclusionOffsetX && postEffect.m_Pos.GetFloorIntY() > screenOcclusionOffsetY && postEffect.m_Pos.GetFloorIntX() < occludedOffsetX && postEffect.m_Pos.GetFloorIntY() < occludedOffsetY) { m_PostScreenEffects.emplace_back(postEffect.m_Pos + targetBitmapOffset, postEffect.m_Bitmap, postEffect.m_BitmapHash, postEffect.m_Strength, postEffect.m_Angle); } } // Adjust glow areas for the player screen's position on the final buffer - for (const Box &glowBox : screenRelativeGlowBoxesList) { + for (const Box& glowBox: screenRelativeGlowBoxesList) { m_PostScreenGlowBoxes.push_back(glowBox); // Adjust each added glow area for the player screen's position on the final buffer m_PostScreenGlowBoxes.back().m_Corner += targetBitmapOffset; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::RegisterPostEffect(const Vector &effectPos, BITMAP *effect, size_t hash, int strength, float angle) { + void PostProcessMan::RegisterPostEffect(const Vector& effectPos, BITMAP* effect, size_t hash, int strength, float angle) { // These effects get applied when there's a drawn frame that followed one or more sim updates. // They are not only registered on drawn sim updates; flashes and stuff could be missed otherwise if they occur on undrawn sim updates. - + if (effect && g_TimerMan.SimUpdatesSinceDrawn() >= 0) { m_PostSceneEffects.push_back(PostEffect(effectPos, effect, hash, strength, angle)); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PostProcessMan::GetPostScreenEffectsWrapped(const Vector &boxPos, int boxWidth, int boxHeight, std::list &effectsList, int team) { + bool PostProcessMan::GetPostScreenEffectsWrapped(const Vector& boxPos, int boxWidth, int boxHeight, std::list& effectsList, int team) { bool found = false; // Do the first unwrapped rect @@ -248,12 +248,12 @@ namespace RTE { return found; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP *PostProcessMan::GetTempEffectBitmap(BITMAP *bitmap) const { + BITMAP* PostProcessMan::GetTempEffectBitmap(BITMAP* bitmap) const { // Get the largest dimension of the bitmap and convert it to a multiple of 16, i.e. 16, 32, etc int bitmapSizeNeeded = static_cast(std::ceil(static_cast(std::max(bitmap->w, bitmap->h)) / 16.0F)) * 16; - std::unordered_map::const_iterator correspondingBitmapSizeEntry = m_TempEffectBitmaps.find(bitmapSizeNeeded); + std::unordered_map::const_iterator correspondingBitmapSizeEntry = m_TempEffectBitmaps.find(bitmapSizeNeeded); // If we didn't find a match then the bitmap size is greater than 512 but that's the biggest we've got, so return it if (correspondingBitmapSizeEntry == m_TempEffectBitmaps.end()) { @@ -263,33 +263,33 @@ namespace RTE { return correspondingBitmapSizeEntry->second; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::RegisterGlowDotEffect(const Vector &effectPos, DotGlowColor color, int strength) { + void PostProcessMan::RegisterGlowDotEffect(const Vector& effectPos, DotGlowColor color, int strength) { // These effects only apply only once per drawn sim update, and only on the first frame drawn after one or more sim updates if (color != NoDot && g_TimerMan.DrawnSimUpdate() && g_TimerMan.SimUpdatesSinceDrawn() >= 0) { RegisterPostEffect(effectPos, GetDotGlowEffect(color), GetDotGlowEffectHash(color), strength); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PostProcessMan::GetGlowAreasWrapped(const Vector &boxPos, int boxWidth, int boxHeight, std::list &areaList) const { + bool PostProcessMan::GetGlowAreasWrapped(const Vector& boxPos, int boxWidth, int boxHeight, std::list& areaList) const { bool foundAny = false; Vector intRectPosRelativeToBox; // Account for wrapping in any registered glow IntRects, as well as on the box we're testing against std::list wrappedGlowRects; - for (const IntRect &glowArea : m_GlowAreas) { + for (const IntRect& glowArea: m_GlowAreas) { g_SceneMan.WrapRect(glowArea, wrappedGlowRects); } std::list wrappedTestRects; g_SceneMan.WrapRect(IntRect(boxPos.GetFloorIntX(), boxPos.GetFloorIntY(), boxPos.GetFloorIntX() + boxWidth, boxPos.GetFloorIntY() + boxHeight), wrappedTestRects); // Check for intersections. If any are found, cut down the intersecting IntRect to the bounds of the IntRect we're testing against, then make and store a Box out of it - for (IntRect &wrappedTestRect : wrappedTestRects) { - for (const IntRect &wrappedGlowRect : wrappedGlowRects) { + for (IntRect& wrappedTestRect: wrappedTestRects) { + for (const IntRect& wrappedGlowRect: wrappedGlowRects) { if (wrappedTestRect.Intersects(wrappedGlowRect)) { IntRect cutRect(wrappedGlowRect); cutRect.IntersectionCut(wrappedTestRect); @@ -302,38 +302,40 @@ namespace RTE { return foundAny; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::GetNetworkPostEffectsList(int whichScreen, std::list &outputList) { + void PostProcessMan::GetNetworkPostEffectsList(int whichScreen, std::list& outputList) { ScreenRelativeEffectsMutex.at(whichScreen).lock(); outputList.clear(); - for (const PostEffect &postEffect : m_ScreenRelativeEffects.at(whichScreen)) { + for (const PostEffect& postEffect: m_ScreenRelativeEffects.at(whichScreen)) { outputList.push_back(PostEffect(postEffect.m_Pos, postEffect.m_Bitmap, postEffect.m_BitmapHash, postEffect.m_Strength, postEffect.m_Angle)); } ScreenRelativeEffectsMutex.at(whichScreen).unlock(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::SetNetworkPostEffectsList(int whichScreen, std::list &inputList) { + void PostProcessMan::SetNetworkPostEffectsList(int whichScreen, std::list& inputList) { ScreenRelativeEffectsMutex.at(whichScreen).lock(); m_ScreenRelativeEffects.at(whichScreen).clear(); - for (const PostEffect &postEffect : inputList) { + for (const PostEffect& postEffect: inputList) { m_ScreenRelativeEffects.at(whichScreen).push_back(PostEffect(postEffect.m_Pos, postEffect.m_Bitmap, postEffect.m_BitmapHash, postEffect.m_Strength, postEffect.m_Angle)); } ScreenRelativeEffectsMutex.at(whichScreen).unlock(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PostProcessMan::GetPostScreenEffects(Vector boxPos, int boxWidth, int boxHeight, std::list &effectsList, int team) { + bool PostProcessMan::GetPostScreenEffects(Vector boxPos, int boxWidth, int boxHeight, std::list& effectsList, int team) { bool found = false; bool unseen = false; Vector postEffectPosRelativeToBox; if (g_SceneMan.GetScene()) { - for (PostEffect &scenePostEffect : m_PostSceneEffects) { - if (team != Activity::NoTeam) { unseen = g_SceneMan.IsUnseen(scenePostEffect.m_Pos.GetFloorIntX(), scenePostEffect.m_Pos.GetFloorIntY(), team); } + for (PostEffect& scenePostEffect: m_PostSceneEffects) { + if (team != Activity::NoTeam) { + unseen = g_SceneMan.IsUnseen(scenePostEffect.m_Pos.GetFloorIntX(), scenePostEffect.m_Pos.GetFloorIntY(), team); + } if (WithinBox(scenePostEffect.m_Pos, boxPos, static_cast(boxWidth), static_cast(boxHeight)) && !unseen) { found = true; @@ -345,14 +347,14 @@ namespace RTE { return found; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PostProcessMan::GetPostScreenEffects(int left, int top, int right, int bottom, std::list &effectsList, int team) { + bool PostProcessMan::GetPostScreenEffects(int left, int top, int right, int bottom, std::list& effectsList, int team) { bool found = false; bool unseen = false; Vector postEffectPosRelativeToBox; - for (PostEffect &scenePostEffect : m_PostSceneEffects) { + for (PostEffect& scenePostEffect: m_PostSceneEffects) { if (team != Activity::NoTeam) { unseen = g_SceneMan.IsUnseen(scenePostEffect.m_Pos.GetFloorIntX(), scenePostEffect.m_Pos.GetFloorIntY(), team); } @@ -366,9 +368,9 @@ namespace RTE { return found; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP * PostProcessMan::GetDotGlowEffect(DotGlowColor whichColor) const { + BITMAP* PostProcessMan::GetDotGlowEffect(DotGlowColor whichColor) const { switch (whichColor) { case NoDot: return nullptr; @@ -384,7 +386,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// size_t PostProcessMan::GetDotGlowEffectHash(DotGlowColor whichColor) const { switch (whichColor) { @@ -402,7 +404,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostProcessMan::PostProcess() { UpdatePalette(); @@ -447,7 +449,7 @@ namespace RTE { m_PostScreenEffects.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostProcessMan::DrawDotGlowEffects() { int startX = 0; @@ -455,7 +457,7 @@ namespace RTE { int endX = 0; int endY = 0; int testpixel = 0; - if(!m_YellowGlow->extra) { + if (!m_YellowGlow->extra) { LazyInitBitmap(m_YellowGlow); } @@ -464,7 +466,7 @@ namespace RTE { GL_CHECK(glBindTexture(GL_TEXTURE_2D, reinterpret_cast(m_YellowGlow->extra)->m_Texture)); // Randomly sample the entire backbuffer, looking for pixels to put a glow on. - for (const Box &glowBox : m_PostScreenGlowBoxes) { + for (const Box& glowBox: m_PostScreenGlowBoxes) { startX = glowBox.m_Corner.GetFloorIntX(); startY = glowBox.m_Corner.GetFloorIntY(); endX = startX + static_cast(glowBox.m_Width); @@ -472,7 +474,7 @@ namespace RTE { // Sanity check a little at least if (startX < 0 || startX >= g_FrameMan.GetBackBuffer8()->w || startY < 0 || startY >= g_FrameMan.GetBackBuffer8()->h || - endX < 0 || endX >= g_FrameMan.GetBackBuffer8()->w || endY < 0 || endY >= g_FrameMan.GetBackBuffer8()->h) { + endX < 0 || endX >= g_FrameMan.GetBackBuffer8()->w || endY < 0 || endY >= g_FrameMan.GetBackBuffer8()->h) { continue; } @@ -499,11 +501,11 @@ namespace RTE { // RED /* if (testpixel == 13) { - draw_trans_sprite(m_BackBuffer32, m_RedGlow, x - 2, y - 2); + draw_trans_sprite(m_BackBuffer32, m_RedGlow, x - 2, y - 2); } // BLUE if (testpixel == 166) { - draw_trans_sprite(g_FrameMan.GetBackBuffer32(), m_BlueGlow, x - 2, y - 2); + draw_trans_sprite(g_FrameMan.GetBackBuffer32(), m_BlueGlow, x - 2, y - 2); } */ } @@ -511,10 +513,10 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostProcessMan::DrawPostScreenEffects() { - BITMAP *effectBitmap = nullptr; + BITMAP* effectBitmap = nullptr; float effectPosX = 0; float effectPosY = 0; float effectStrength = 0; @@ -525,7 +527,7 @@ namespace RTE { m_PostProcessShader->SetInt(m_PostProcessShader->GetTextureUniform(), 0); m_PostProcessShader->SetMatrix4f(m_PostProcessShader->GetProjectionUniform(), m_ProjectionMatrix); - for (const PostEffect &postEffect : m_PostScreenEffects) { + for (const PostEffect& postEffect: m_PostScreenEffects) { if (postEffect.m_Bitmap) { if (!postEffect.m_Bitmap->extra) { LazyInitBitmap(postEffect.m_Bitmap); diff --git a/Source/Managers/PostProcessMan.h b/Source/Managers/PostProcessMan.h index 7fa4a8e896..c0fdf65d11 100644 --- a/Source/Managers/PostProcessMan.h +++ b/Source/Managers/PostProcessMan.h @@ -22,7 +22,7 @@ namespace RTE { /// Structure for storing a post-process screen effect to be applied at the last stage of 32bpp rendering. /// struct PostEffect { - BITMAP *m_Bitmap = nullptr; //!< The bitmap to blend, not owned. + BITMAP* m_Bitmap = nullptr; //!< The bitmap to blend, not owned. size_t m_BitmapHash = 0; //!< Hash used to transmit glow events over the network. float m_Angle = 0.0F; // Post effect angle in radians. int m_Strength = 128; //!< Scalar float for how hard to blend it in, 0 - 255. @@ -31,7 +31,8 @@ namespace RTE { /// /// Constructor method used to instantiate a PostEffect object in system memory. /// - PostEffect(const Vector &pos, BITMAP *bitmap, size_t bitmapHash, int strength, float angle) : m_Bitmap(bitmap), m_BitmapHash(bitmapHash), m_Angle(angle), m_Strength(strength), m_Pos(pos) {} + PostEffect(const Vector& pos, BITMAP* bitmap, size_t bitmapHash, int strength, float angle) : + m_Bitmap(bitmap), m_BitmapHash(bitmapHash), m_Angle(angle), m_Strength(strength), m_Pos(pos) {} }; /// @@ -40,7 +41,6 @@ namespace RTE { class PostProcessMan : public Singleton { public: - #pragma region Creation /// /// Constructor method used to instantiate a PostProcessMan object in system memory. Create() should be called before using the object. @@ -73,12 +73,18 @@ namespace RTE { /// /// Clears the list of registered post-processing screen effects and glow boxes. /// - void ClearScreenPostEffects() { m_PostScreenEffects.clear(); m_PostScreenGlowBoxes.clear(); } + void ClearScreenPostEffects() { + m_PostScreenEffects.clear(); + m_PostScreenGlowBoxes.clear(); + } /// /// Clears the list of registered post-processing scene effects and glow areas. /// - void ClearScenePostEffects() { m_PostSceneEffects.clear(); m_GlowAreas.clear(); } + void ClearScenePostEffects() { + m_PostSceneEffects.clear(); + m_GlowAreas.clear(); + } #pragma endregion #pragma region Concrete Methods @@ -95,7 +101,7 @@ namespace RTE { /// The position of the specified player's draw screen on the backbuffer. /// List of the specified player's accumulated post effects for this frame. /// List of the specified player's accumulated glow boxes for this frame. - void AdjustEffectsPosToPlayerScreen(int playerScreen, BITMAP *targetBitmap, const Vector &targetBitmapOffset, std::list &screenRelativeEffectsList, std::list &screenRelativeGlowBoxesList); + void AdjustEffectsPosToPlayerScreen(int playerScreen, BITMAP* targetBitmap, const Vector& targetBitmapOffset, std::list& screenRelativeEffectsList, std::list& screenRelativeGlowBoxesList); #pragma endregion #pragma region Post Effect Handling @@ -107,7 +113,7 @@ namespace RTE { /// Hash value of the effect for transmitting over the network. /// The intensity level this effect should have when blended in post. 0 - 255. /// The angle this effect should be rotated at in radians. - void RegisterPostEffect(const Vector &effectPos, BITMAP *effect, size_t hash, int strength = 255, float angle = 0); + void RegisterPostEffect(const Vector& effectPos, BITMAP* effect, size_t hash, int strength = 255, float angle = 0); /// /// Gets all screen effects that are located within a box in the scene. @@ -119,14 +125,14 @@ namespace RTE { /// The list to add the screen effects that fall within the box to. The coordinates of the effects returned here will be relative to the boxPos passed in above. /// The team whose unseen layer should obscure the screen effects here. /// Whether any active post effects were found in that box. - bool GetPostScreenEffectsWrapped(const Vector &boxPos, int boxWidth, int boxHeight, std::list &effectsList, int team = -1); + bool GetPostScreenEffectsWrapped(const Vector& boxPos, int boxWidth, int boxHeight, std::list& effectsList, int team = -1); /// /// Gets a temporary bitmap of specified size to rotate post effects in. /// /// Size of bitmap to get. /// Pointer to the temporary bitmap. - BITMAP * GetTempEffectBitmap(BITMAP *bitmap) const; + BITMAP* GetTempEffectBitmap(BITMAP* bitmap) const; #pragma endregion #pragma region Post Pixel Glow Handling @@ -135,14 +141,18 @@ namespace RTE { /// Registers a specific IntRect to be post-processed and have special pixel colors lit up by glow effects in it. /// /// The IntRect to have special color pixels glow in, in scene coordinates. - void RegisterGlowArea(const IntRect &glowArea) { if (g_TimerMan.DrawnSimUpdate() && g_TimerMan.SimUpdatesSinceDrawn() >= 0) { m_GlowAreas.push_back(glowArea); } } + void RegisterGlowArea(const IntRect& glowArea) { + if (g_TimerMan.DrawnSimUpdate() && g_TimerMan.SimUpdatesSinceDrawn() >= 0) { + m_GlowAreas.push_back(glowArea); + } + } /// /// Creates an IntRect and registers it to be post-processed and have special pixel colors lit up by glow effects in it. /// /// The center of the IntRect. /// The radius around it to add as an area. - void RegisterGlowArea(const Vector ¢er, float radius) { + void RegisterGlowArea(const Vector& center, float radius) { RegisterGlowArea(IntRect(static_cast(center.m_X - radius), static_cast(center.m_Y - radius), static_cast(center.m_X + radius), static_cast(center.m_Y + radius))); } @@ -152,7 +162,7 @@ namespace RTE { /// The absolute scene coordinates of the center of the effect. /// Which glow dot color to register, see the DotGlowColor enumerator. /// The intensity level this effect should have when blended in post. 0 - 255. - void RegisterGlowDotEffect(const Vector &effectPos, DotGlowColor color, int strength = 255); + void RegisterGlowDotEffect(const Vector& effectPos, DotGlowColor color, int strength = 255); /// /// Gets all glow areas that affect anything within a box in the scene. @@ -163,7 +173,7 @@ namespace RTE { /// The height of the box. /// The list to add the glow Boxes that intersect to. The coordinates of the Boxes returned here will be relative to the boxPos passed in above. /// Whether any active post effects were found in that box. - bool GetGlowAreasWrapped(const Vector &boxPos, int boxWidth, int boxHeight, std::list &areaList) const; + bool GetGlowAreasWrapped(const Vector& boxPos, int boxWidth, int boxHeight, std::list& areaList) const; #pragma endregion #pragma region Network Post Effect Handling @@ -172,14 +182,14 @@ namespace RTE { /// /// Which player screen to get list for. /// Reference to the list of post effects to copy into. - void GetNetworkPostEffectsList(int whichScreen, std::list &outputList); + void GetNetworkPostEffectsList(int whichScreen, std::list& outputList); /// /// Copies the player's screen relative post effects from the referenced list to the list of this PostProcessMan. Used for receiving post effect data over the network. /// /// Which player screen to set list for. /// Reference to the list of post effects to copy from. - void SetNetworkPostEffectsList(int whichScreen, std::list &inputList); + void SetNetworkPostEffectsList(int whichScreen, std::list& inputList); #pragma endregion /// @@ -189,7 +199,6 @@ namespace RTE { GLuint GetPostProcessColorBuffer() { return m_BackBuffer32; } protected: - std::list m_PostScreenEffects; //!< List of effects to apply at the end of each frame. This list gets cleared out and re-filled each frame. std::list m_PostSceneEffects; //!< All post-processing effects registered for this draw frame in the scene. @@ -199,15 +208,15 @@ namespace RTE { std::array, c_MaxScreenCount> m_ScreenRelativeEffects; //!< List of screen relative effects for each player in online multiplayer. std::array ScreenRelativeEffectsMutex; //!< Mutex for the ScreenRelativeEffects list when accessed by multiple threads in online multiplayer. - BITMAP *m_YellowGlow; //!< Bitmap for the yellow dot glow effect. - BITMAP *m_RedGlow; //!< Bitmap for the red dot glow effect. - BITMAP *m_BlueGlow; //!< Bitmap for the blue dot glow effect. + BITMAP* m_YellowGlow; //!< Bitmap for the yellow dot glow effect. + BITMAP* m_RedGlow; //!< Bitmap for the red dot glow effect. + BITMAP* m_BlueGlow; //!< Bitmap for the blue dot glow effect. size_t m_YellowGlowHash; //!< Hash value for the yellow dot glow effect bitmap. size_t m_RedGlowHash; //!< Hash value for the red dot glow effect bitmap. size_t m_BlueGlowHash; //!< Hash value for the blue dot glow effect bitmap. - std::unordered_map m_TempEffectBitmaps; //!< Stores temporary bitmaps to rotate post effects in for quick access. + std::unordered_map m_TempEffectBitmaps; //!< Stores temporary bitmaps to rotate post effects in for quick access. private: GLuint m_BackBuffer8; //!< Backbuffer texture for incoming indexed drawings. @@ -233,7 +242,7 @@ namespace RTE { /// The list to add the screen effects that fall within the box to. The coordinates of the effects returned here will be relative to the boxPos passed in above. /// The team whose unseen area should block the glows. /// Whether any active post effects were found in that box. - bool GetPostScreenEffects(Vector boxPos, int boxWidth, int boxHeight, std::list &effectsList, int team = -1); + bool GetPostScreenEffects(Vector boxPos, int boxWidth, int boxHeight, std::list& effectsList, int team = -1); /// /// Gets all screen effects that are located within a box in the scene. Their coordinates will be returned relative to the upper left corner of the box passed in here. @@ -245,7 +254,7 @@ namespace RTE { /// The list to add the screen effects that fall within the box to. The coordinates of the effects returned here will be relative to the boxPos passed in above. /// The team whose unseen area should block the glows. /// Whether any active post effects were found in that box. - bool GetPostScreenEffects(int left, int top, int right, int bottom, std::list &effectsList, int team = -1); + bool GetPostScreenEffects(int left, int top, int right, int bottom, std::list& effectsList, int team = -1); #pragma endregion #pragma region Post Pixel Glow Handling @@ -254,7 +263,7 @@ namespace RTE { /// /// Which of the dot glow colors to get, see the DotGlowColor enumerator. /// The requested glow dot BITMAP. - BITMAP * GetDotGlowEffect(DotGlowColor whichColor) const; + BITMAP* GetDotGlowEffect(DotGlowColor whichColor) const; /// /// Gets the hash value of a specific standard dot glow effect for making pixels glow. @@ -285,7 +294,7 @@ namespace RTE { /// Initializes all the GL pointers used by this PostProcessMan. /// void InitializeGLPointers(); - + /// /// Destroys all the GL pointers used by this PostProcessMan. /// @@ -300,11 +309,11 @@ namespace RTE { /// Creates and upload a new GL texture. The texture pointer is stored in the BITMAP->extra field. /// /// The bitmap to create a texture for. - void LazyInitBitmap(BITMAP *bitmap); + void LazyInitBitmap(BITMAP* bitmap); // Disallow the use of some implicit methods. - PostProcessMan(const PostProcessMan &reference) = delete; - PostProcessMan & operator=(const PostProcessMan &rhs) = delete; + PostProcessMan(const PostProcessMan& reference) = delete; + PostProcessMan& operator=(const PostProcessMan& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Managers/PresetMan.cpp b/Source/Managers/PresetMan.cpp index e788bb2ae2..fdecb4bf99 100644 --- a/Source/Managers/PresetMan.cpp +++ b/Source/Managers/PresetMan.cpp @@ -8,8 +8,7 @@ // http://www.datarealms.com // Suppress compiler warning about unrecognized escape sequence on line 183 -#pragma warning( disable : 4129 ) - +#pragma warning(disable : 4129) ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -19,11 +18,11 @@ #include "SceneObject.h" #include "Loadout.h" #include "ACraft.h" -//#include "AHuman.h" -//#include "MOPixel.h" -//#include "SLTerrain.h" -//#include "AtomGroup.h" -//#include "Atom.h" +// #include "AHuman.h" +// #include "MOPixel.h" +// #include "SLTerrain.h" +// #include "AtomGroup.h" +// #include "Atom.h" #include "ConsoleMan.h" #include "LoadingScreen.h" @@ -31,1150 +30,1058 @@ namespace RTE { - const std::array PresetMan::c_OfficialModules = { "Base.rte", "Coalition.rte", "Imperatus.rte", "Techion.rte", "Dummy.rte", "Ronin.rte", "Browncoats.rte", "Uzira.rte", "MuIlaak.rte", "Missions.rte" }; - const std::array, 3> PresetMan::c_UserdataModules = {{ - {c_UserScenesModuleName, "User Scenes"}, - {c_UserConquestSavesModuleName, "Conquest Saves"}, - {c_UserScriptedSavesModuleName, "Scripted Activity Saves" } - }}; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this PresetMan, effectively -// resetting the members of this abstraction level only. - -void PresetMan::Clear() -{ - m_pDataModules.clear(); - m_DataModuleIDs.clear(); - m_OfficialModuleCount = 0; - m_TotalGroupRegister.clear(); - m_LastReloadedEntityPresetInfo.fill(""); - m_ReloadEntityPresetCalledThisUpdate = false; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this PresetMan with a Writer for -// later recreation with Create(Reader &reader); - -int PresetMan::Save(Writer &writer) const -{ - writer << m_Actors.size(); - for (list::const_iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) - writer << **itr; + const std::array PresetMan::c_OfficialModules = {"Base.rte", "Coalition.rte", "Imperatus.rte", "Techion.rte", "Dummy.rte", "Ronin.rte", "Browncoats.rte", "Uzira.rte", "MuIlaak.rte", "Missions.rte"}; + const std::array, 3> PresetMan::c_UserdataModules = {{{c_UserScenesModuleName, "User Scenes"}, + {c_UserConquestSavesModuleName, "Conquest Saves"}, + {c_UserScriptedSavesModuleName, "Scripted Activity Saves"}}}; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this PresetMan, effectively + // resetting the members of this abstraction level only. + + void PresetMan::Clear() { + m_pDataModules.clear(); + m_DataModuleIDs.clear(); + m_OfficialModuleCount = 0; + m_TotalGroupRegister.clear(); + m_LastReloadedEntityPresetInfo.fill(""); + m_ReloadEntityPresetCalledThisUpdate = false; + } - writer << m_Particles.size(); - for (list::const_iterator itr2 = m_Particles.begin(); itr2 != m_Particles.end(); ++itr2) - writer << **itr2; + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Save + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the complete state of this PresetMan with a Writer for + // later recreation with Create(Reader &reader); - return 0; -} -*/ + int PresetMan::Save(Writer &writer) const + { + writer << m_Actors.size(); + for (list::const_iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) + writer << **itr; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the PresetMan entity. + writer << m_Particles.size(); + for (list::const_iterator itr2 = m_Particles.begin(); itr2 != m_Particles.end(); ++itr2) + writer << **itr2; -void PresetMan::Destroy() -{ - for (std::vector::iterator dmItr = m_pDataModules.begin(); dmItr != m_pDataModules.end(); ++dmItr) - { - delete (*dmItr); - } + return 0; + } + */ - Clear(); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the PresetMan entity. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void PresetMan::Destroy() { + for (std::vector::iterator dmItr = m_pDataModules.begin(); dmItr != m_pDataModules.end(); ++dmItr) { + delete (*dmItr); + } -bool PresetMan::LoadDataModule(const std::string &moduleName, bool official, bool userdata, const ProgressCallback &progressCallback) { - if (moduleName.empty()) { - return false; + Clear(); } - // Make a lowercase-version of the module name so it makes it easier to compare to and find case-agnostically. - std::string lowercaseName = moduleName; - std::transform(lowercaseName.begin(), lowercaseName.end(), lowercaseName.begin(), ::tolower); - // Make sure we don't add the same module twice. - for (const DataModule *dataModule : m_pDataModules) { - if (dataModule->GetFileName() == moduleName) { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool PresetMan::LoadDataModule(const std::string& moduleName, bool official, bool userdata, const ProgressCallback& progressCallback) { + if (moduleName.empty()) { return false; } - } + // Make a lowercase-version of the module name so it makes it easier to compare to and find case-agnostically. + std::string lowercaseName = moduleName; + std::transform(lowercaseName.begin(), lowercaseName.end(), lowercaseName.begin(), ::tolower); - // Only instantiate it here, because it needs to be in the lists of this before being created. - DataModule *newModule = new DataModule(); + // Make sure we don't add the same module twice. + for (const DataModule* dataModule: m_pDataModules) { + if (dataModule->GetFileName() == moduleName) { + return false; + } + } - // Official modules are stacked in the beginning of the vector. - if (official && !userdata) { - // Halt if an official module is being loaded after any non-official ones! - //RTEAssert(m_pDataModules.size() == m_OfficialModuleCount, "Trying to load an official module after a non-official one has been loaded!"); + // Only instantiate it here, because it needs to be in the lists of this before being created. + DataModule* newModule = new DataModule(); - // Find where the official modules end in the vector. - std::vector::iterator moduleItr = m_pDataModules.begin(); - size_t newModuleID = 0; - for (; newModuleID < m_OfficialModuleCount; ++newModuleID) { - moduleItr++; + // Official modules are stacked in the beginning of the vector. + if (official && !userdata) { + // Halt if an official module is being loaded after any non-official ones! + // RTEAssert(m_pDataModules.size() == m_OfficialModuleCount, "Trying to load an official module after a non-official one has been loaded!"); + + // Find where the official modules end in the vector. + std::vector::iterator moduleItr = m_pDataModules.begin(); + size_t newModuleID = 0; + for (; newModuleID < m_OfficialModuleCount; ++newModuleID) { + moduleItr++; + } + // Insert into after the last official one. + m_pDataModules.emplace(moduleItr, newModule); + m_DataModuleIDs.try_emplace(lowercaseName, newModuleID); + m_OfficialModuleCount++; + } else { + if (userdata) { + newModule->SetAsUserdata(); + } + m_pDataModules.emplace_back(newModule); + m_DataModuleIDs.try_emplace(lowercaseName, m_pDataModules.size() - 1); } - // Insert into after the last official one. - m_pDataModules.emplace(moduleItr, newModule); - m_DataModuleIDs.try_emplace(lowercaseName, newModuleID); - m_OfficialModuleCount++; - } else { - if (userdata) { newModule->SetAsUserdata(); } - m_pDataModules.emplace_back(newModule); - m_DataModuleIDs.try_emplace(lowercaseName, m_pDataModules.size() - 1); - } - if (newModule->Create(moduleName, progressCallback) < 0) { - RTEAbort("Failed to find the " + moduleName + " Data Module!"); - return false; + if (newModule->Create(moduleName, progressCallback) < 0) { + RTEAbort("Failed to find the " + moduleName + " Data Module!"); + return false; + } + newModule = nullptr; + return true; } - newModule = nullptr; - return true; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool PresetMan::LoadAllDataModules() { - auto moduleLoadTimerStart = std::chrono::steady_clock::now(); + bool PresetMan::LoadAllDataModules() { + auto moduleLoadTimerStart = std::chrono::steady_clock::now(); - // Destroy any possible loaded modules - Destroy(); + // Destroy any possible loaded modules + Destroy(); - FindAndExtractZippedModules(); + FindAndExtractZippedModules(); - // Load all the official modules first! - for (const std::string &officialModule : c_OfficialModules) { - if (!LoadDataModule(officialModule, true, false, LoadingScreen::LoadingSplashProgressReport)) { - return false; + // Load all the official modules first! + for (const std::string& officialModule: c_OfficialModules) { + if (!LoadDataModule(officialModule, true, false, LoadingScreen::LoadingSplashProgressReport)) { + return false; + } } - } - // If a single module is specified, skip loading all other unofficial modules and load specified module only. - if (!m_SingleModuleToLoad.empty() && !IsModuleOfficial(m_SingleModuleToLoad)) { - if (!LoadDataModule(m_SingleModuleToLoad, false, false, LoadingScreen::LoadingSplashProgressReport)) { - g_ConsoleMan.PrintString("ERROR: Failed to load DataModule \"" + m_SingleModuleToLoad + "\"! Only official modules were loaded!"); - return false; - } - } else { - std::vector modDirectoryFolders; - const std::string modDirectory = System::GetWorkingDirectory() + System::GetModDirectory(); - std::copy_if(std::filesystem::directory_iterator(modDirectory), std::filesystem::directory_iterator(), std::back_inserter(modDirectoryFolders), - [](auto dirEntry){ return std::filesystem::is_directory(dirEntry); } - ); - std::sort(modDirectoryFolders.begin(), modDirectoryFolders.end()); - - for (const std::filesystem::directory_entry &directoryEntry : modDirectoryFolders) { - std::string directoryEntryPath = directoryEntry.path().generic_string(); - if (std::regex_match(directoryEntryPath, std::regex(".*\.rte"))) { - std::string moduleName = directoryEntryPath.substr(directoryEntryPath.find_last_of('/') + 1, std::string::npos); - if (!g_SettingsMan.IsModDisabled(moduleName) && !IsModuleOfficial(moduleName) && !IsModuleUserdata(moduleName)) { - int moduleID = GetModuleID(moduleName); - // NOTE: LoadDataModule can return false (especially since it may try to load already loaded modules, which is okay) and shouldn't cause stop, so we can ignore its return value here. - if (moduleID < 0 || moduleID >= GetOfficialModuleCount()) { LoadDataModule(moduleName, false, false, LoadingScreen::LoadingSplashProgressReport); } + // If a single module is specified, skip loading all other unofficial modules and load specified module only. + if (!m_SingleModuleToLoad.empty() && !IsModuleOfficial(m_SingleModuleToLoad)) { + if (!LoadDataModule(m_SingleModuleToLoad, false, false, LoadingScreen::LoadingSplashProgressReport)) { + g_ConsoleMan.PrintString("ERROR: Failed to load DataModule \"" + m_SingleModuleToLoad + "\"! Only official modules were loaded!"); + return false; + } + } else { + std::vector modDirectoryFolders; + const std::string modDirectory = System::GetWorkingDirectory() + System::GetModDirectory(); + std::copy_if(std::filesystem::directory_iterator(modDirectory), std::filesystem::directory_iterator(), std::back_inserter(modDirectoryFolders), + [](auto dirEntry) { return std::filesystem::is_directory(dirEntry); }); + std::sort(modDirectoryFolders.begin(), modDirectoryFolders.end()); + + for (const std::filesystem::directory_entry& directoryEntry: modDirectoryFolders) { + std::string directoryEntryPath = directoryEntry.path().generic_string(); + if (std::regex_match(directoryEntryPath, std::regex(".*\.rte"))) { + std::string moduleName = directoryEntryPath.substr(directoryEntryPath.find_last_of('/') + 1, std::string::npos); + if (!g_SettingsMan.IsModDisabled(moduleName) && !IsModuleOfficial(moduleName) && !IsModuleUserdata(moduleName)) { + int moduleID = GetModuleID(moduleName); + // NOTE: LoadDataModule can return false (especially since it may try to load already loaded modules, which is okay) and shouldn't cause stop, so we can ignore its return value here. + if (moduleID < 0 || moduleID >= GetOfficialModuleCount()) { + LoadDataModule(moduleName, false, false, LoadingScreen::LoadingSplashProgressReport); + } + } } } - } - // Load userdata modules AFTER all other techs etc are loaded; might be referring to stuff in user mods. - for (const auto &[userdataModuleName, userdataModuleFriendlyName] : c_UserdataModules) { - if (!std::filesystem::exists(System::GetWorkingDirectory() + System::GetUserdataDirectory() + userdataModuleName)) { - bool scanContentsAndIgnoreMissing = userdataModuleName == c_UserScenesModuleName; - DataModule::CreateOnDiskAsUserdata(userdataModuleName, userdataModuleFriendlyName, scanContentsAndIgnoreMissing, scanContentsAndIgnoreMissing); - } - if (!LoadDataModule(userdataModuleName, false, true, LoadingScreen::LoadingSplashProgressReport)) { - return false; + // Load userdata modules AFTER all other techs etc are loaded; might be referring to stuff in user mods. + for (const auto& [userdataModuleName, userdataModuleFriendlyName]: c_UserdataModules) { + if (!std::filesystem::exists(System::GetWorkingDirectory() + System::GetUserdataDirectory() + userdataModuleName)) { + bool scanContentsAndIgnoreMissing = userdataModuleName == c_UserScenesModuleName; + DataModule::CreateOnDiskAsUserdata(userdataModuleName, userdataModuleFriendlyName, scanContentsAndIgnoreMissing, scanContentsAndIgnoreMissing); + } + if (!LoadDataModule(userdataModuleName, false, true, LoadingScreen::LoadingSplashProgressReport)) { + return false; + } } } + + if (g_SettingsMan.IsMeasuringModuleLoadTime()) { + std::chrono::milliseconds moduleLoadElapsedTime = std::chrono::duration_cast(std::chrono::steady_clock::now() - moduleLoadTimerStart); + g_ConsoleMan.PrintString("Module load duration is: " + std::to_string(moduleLoadElapsedTime.count()) + "ms"); + } + return true; } - if (g_SettingsMan.IsMeasuringModuleLoadTime()) { - std::chrono::milliseconds moduleLoadElapsedTime = std::chrono::duration_cast(std::chrono::steady_clock::now() - moduleLoadTimerStart); - g_ConsoleMan.PrintString("Module load duration is: " + std::to_string(moduleLoadElapsedTime.count()) + "ms"); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDataModule + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a specific loaded DataModule + + const DataModule* PresetMan::GetDataModule(int whichModule) { + RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); + return m_pDataModules[whichModule]; } - return true; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDataModuleName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a name specific loaded DataModule -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDataModule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific loaded DataModule + const std::string PresetMan::GetDataModuleName(int whichModule) { + RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); + return m_pDataModules[whichModule]->GetFileName(); + } -const DataModule * PresetMan::GetDataModule(int whichModule) -{ - RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); - return m_pDataModules[whichModule]; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetModuleID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the ID of a loaded DataModule. + + int PresetMan::GetModuleID(std::string moduleName) { + // Lower-case search name so we can match up against the already-lowercase names in m_DataModuleIDs + std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), ::tolower); + + // First pass + std::map::iterator itr = m_DataModuleIDs.find(moduleName); + if (itr != m_DataModuleIDs.end()) + return (*itr).second; + + // Try with or without the .rte on the end before giving up + int dotPos = moduleName.find_last_of('.'); + // Wasnt, so try adding it + if (dotPos == std::string::npos) + moduleName = moduleName + System::GetModulePackageExtension(); + // There was ".rte", so try to shave it off the name + else + moduleName = moduleName.substr(0, dotPos); + + // Try to find the module again! + itr = m_DataModuleIDs.find(moduleName); + if (itr != m_DataModuleIDs.end()) + return (*itr).second; + + /* No need to do this second pass now; we immediately do the case-agnostic search + // Add .rte and try to find the module in case-agnostic fashion + moduleName += ".rte"; + std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), ::tolower); + + itr = m_DataModuleIDs.find(moduleName); + if (itr != m_DataModuleIDs.end()) + return (*itr).second; + + // Try with or without the .rte on the end before giving up + dotPos = moduleName.find_last_of('.'); + // Wasnt, so try adding it + if (dotPos == string::npos) + moduleName = moduleName + System::GetModulePackageExtension(); + // There was ".rte", so try to shave it off the name + else + moduleName = moduleName.substr(0, dotPos); + + // Try to find the module again! + itr = m_DataModuleIDs.find(moduleName); + if (itr != m_DataModuleIDs.end()) + return (*itr).second; + */ + return -1; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDataModuleName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a name specific loaded DataModule + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -const std::string PresetMan::GetDataModuleName(int whichModule) -{ - RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); - return m_pDataModules[whichModule]->GetFileName(); -} + std::string PresetMan::GetModuleNameFromPath(const std::string& dataPath) const { + if (dataPath.empty()) { + return ""; + } + size_t slashPos = dataPath.find_first_of("/\\"); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetModuleID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the ID of a loaded DataModule. - -int PresetMan::GetModuleID(std::string moduleName) -{ - // Lower-case search name so we can match up against the already-lowercase names in m_DataModuleIDs - std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), ::tolower); - - // First pass - std::map::iterator itr = m_DataModuleIDs.find(moduleName); - if (itr != m_DataModuleIDs.end()) - return (*itr).second; - - // Try with or without the .rte on the end before giving up - int dotPos = moduleName.find_last_of('.'); - // Wasnt, so try adding it - if (dotPos == std::string::npos) - moduleName = moduleName + System::GetModulePackageExtension(); - // There was ".rte", so try to shave it off the name - else - moduleName = moduleName.substr(0, dotPos); - - // Try to find the module again! - itr = m_DataModuleIDs.find(moduleName); - if (itr != m_DataModuleIDs.end()) - return (*itr).second; - -/* No need to do this second pass now; we immediately do the case-agnostic search - // Add .rte and try to find the module in case-agnostic fashion - moduleName += ".rte"; - std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), ::tolower); - - itr = m_DataModuleIDs.find(moduleName); - if (itr != m_DataModuleIDs.end()) - return (*itr).second; - - // Try with or without the .rte on the end before giving up - dotPos = moduleName.find_last_of('.'); - // Wasnt, so try adding it - if (dotPos == string::npos) - moduleName = moduleName + System::GetModulePackageExtension(); - // There was ".rte", so try to shave it off the name - else - moduleName = moduleName.substr(0, dotPos); - - // Try to find the module again! - itr = m_DataModuleIDs.find(moduleName); - if (itr != m_DataModuleIDs.end()) - return (*itr).second; -*/ - return -1; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -std::string PresetMan::GetModuleNameFromPath(const std::string &dataPath) const { - if (dataPath.empty()) { - return ""; - } - size_t slashPos = dataPath.find_first_of("/\\"); + // Include trailing slash in the substring range in case we need to match against the Data/Mods/Userdata directory. + std::string moduleName = (slashPos != std::string::npos) ? dataPath.substr(0, slashPos + 1) : dataPath; - // Include trailing slash in the substring range in case we need to match against the Data/Mods/Userdata directory. - std::string moduleName = (slashPos != std::string::npos) ? dataPath.substr(0, slashPos + 1) : dataPath; + // Check if path starts with Data/ or the Mods/Userdata dir names and remove that part to get to the actual module name. + if (moduleName == System::GetDataDirectory() || moduleName == System::GetModDirectory() || moduleName == System::GetUserdataDirectory()) { + std::string shortenPath = dataPath.substr(slashPos + 1); + slashPos = shortenPath.find_first_of("/\\"); + moduleName = shortenPath.substr(0, slashPos + 1); + } - // Check if path starts with Data/ or the Mods/Userdata dir names and remove that part to get to the actual module name. - if (moduleName == System::GetDataDirectory() || moduleName == System::GetModDirectory() || moduleName == System::GetUserdataDirectory()) { - std::string shortenPath = dataPath.substr(slashPos + 1); - slashPos = shortenPath.find_first_of("/\\"); - moduleName = shortenPath.substr(0, slashPos + 1); + if (!moduleName.empty() && moduleName.back() == '/') { + moduleName.pop_back(); + } + return moduleName; } - if (!moduleName.empty() && moduleName.back() == '/') { - moduleName.pop_back(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int PresetMan::GetModuleIDFromPath(const std::string& dataPath) { + if (dataPath.empty()) { + return -1; + } + return GetModuleID(GetModuleNameFromPath(dataPath)); } - return moduleName; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int PresetMan::GetModuleIDFromPath(const std::string &dataPath) { - if (dataPath.empty()) { - return -1; + bool PresetMan::IsModuleOfficial(const std::string& moduleName) const { + return std::find(c_OfficialModules.begin(), c_OfficialModules.end(), moduleName) != c_OfficialModules.end(); } - return GetModuleID(GetModuleNameFromPath(dataPath)); -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool PresetMan::IsModuleOfficial(const std::string &moduleName) const { - return std::find(c_OfficialModules.begin(), c_OfficialModules.end(), moduleName) != c_OfficialModules.end(); -} + bool PresetMan::IsModuleUserdata(const std::string& moduleName) const { + auto userdataModuleItr = std::find_if(c_UserdataModules.begin(), c_UserdataModules.end(), + [&moduleName](const auto& userdataModulesEntry) { + return userdataModulesEntry.first == moduleName; + }); + return userdataModuleItr != c_UserdataModules.end(); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool PresetMan::IsModuleUserdata(const std::string &moduleName) const { - auto userdataModuleItr = std::find_if(c_UserdataModules.begin(), c_UserdataModules.end(), - [&moduleName](const auto &userdataModulesEntry) { - return userdataModulesEntry.first == moduleName; - } - ); - return userdataModuleItr != c_UserdataModules.end(); -} + std::string PresetMan::GetFullModulePath(const std::string& modulePath) const { + // Note: Mods may use mixed path separators, which aren't supported on non Windows systems. + // Since Windows supports both forward and backslash separators it's safe to replace all backslashes with forward slashes. + std::string modulePathGeneric = std::filesystem::path(modulePath).generic_string(); + std::replace(modulePathGeneric.begin(), modulePathGeneric.end(), '\\', '/'); + + const std::string pathTopDir = modulePathGeneric.substr(0, modulePathGeneric.find_first_of("/") + 1); + const std::string moduleName = GetModuleNameFromPath(modulePathGeneric); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + std::string moduleTopDir = System::GetModDirectory(); -std::string PresetMan::GetFullModulePath(const std::string &modulePath) const { - // Note: Mods may use mixed path separators, which aren't supported on non Windows systems. - // Since Windows supports both forward and backslash separators it's safe to replace all backslashes with forward slashes. - std::string modulePathGeneric = std::filesystem::path(modulePath).generic_string(); - std::replace(modulePathGeneric.begin(), modulePathGeneric.end(), '\\', '/'); + if (IsModuleOfficial(moduleName)) { + moduleTopDir = System::GetDataDirectory(); + } else if (IsModuleUserdata(moduleName)) { + moduleTopDir = System::GetUserdataDirectory(); + } + return (pathTopDir == moduleTopDir) ? modulePathGeneric : moduleTopDir + modulePathGeneric; + } - const std::string pathTopDir = modulePathGeneric.substr(0, modulePathGeneric.find_first_of("/") + 1); - const std::string moduleName = GetModuleNameFromPath(modulePathGeneric); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddEntityPreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an Entity instance's pointer and name associations to the + // internal list of already read in Entity:s. Ownership is NOT transferred! + // If there already is an instance defined, nothing happens. If there + // is not, a clone is made of the passed-in Entity and added to the library. - std::string moduleTopDir = System::GetModDirectory(); + bool PresetMan::AddEntityPreset(Entity* pEntToAdd, int whichModule, bool overwriteSame, std::string readFromFile) { + RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); - if (IsModuleOfficial(moduleName)) { - moduleTopDir = System::GetDataDirectory(); - } else if (IsModuleUserdata(moduleName)) { - moduleTopDir = System::GetUserdataDirectory(); + return m_pDataModules[whichModule]->AddEntityPreset(pEntToAdd, overwriteSame, readFromFile); } - return (pathTopDir == moduleTopDir) ? modulePathGeneric : moduleTopDir + modulePathGeneric; -} -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddEntityPreset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an Entity instance's pointer and name associations to the -// internal list of already read in Entity:s. Ownership is NOT transferred! -// If there already is an instance defined, nothing happens. If there -// is not, a clone is made of the passed-in Entity and added to the library. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEntityPreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a previously read in (defined) Entity, by type and instance name. -bool PresetMan::AddEntityPreset(Entity *pEntToAdd, int whichModule, bool overwriteSame, std::string readFromFile) -{ - RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); + const Entity* PresetMan::GetEntityPreset(std::string type, std::string preset, int whichModule) { + RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); - return m_pDataModules[whichModule]->AddEntityPreset(pEntToAdd, overwriteSame, readFromFile); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEntityPreset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a previously read in (defined) Entity, by type and instance name. - -const Entity * PresetMan::GetEntityPreset(std::string type, std::string preset, int whichModule) -{ - RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); - - const Entity *pRetEntity = 0; - - // Preset name might have "[ModuleName]/" preceding it, detect it here and select proper module! - int slashPos = preset.find_first_of('/'); - if (slashPos != std::string::npos) - { - // Get the module ID and cut off the module specifier in the string - whichModule = GetModuleID(preset.substr(0, slashPos)); - preset = preset.substr(slashPos + 1); - } - - // All modules - if (whichModule < 0) - { - // Search all modules - for (int i = 0; i < m_pDataModules.size() && !pRetEntity; ++i) - pRetEntity = m_pDataModules[i]->GetEntityPreset(type, preset); - } - // Specific module - else - { - // Try to get it from the asked for module - pRetEntity = m_pDataModules[whichModule]->GetEntityPreset(type, preset); - - // If couldn't find it in there, then try all the official modules! - if (!pRetEntity) - { - RTEAssert(m_OfficialModuleCount <= m_pDataModules.size(), "More official modules than modules loaded?!"); - for (int i = 0; i < m_OfficialModuleCount && !pRetEntity; ++i) - { - pRetEntity = m_pDataModules[i]->GetEntityPreset(type, preset); - } - } - } - - return pRetEntity; -} + const Entity* pRetEntity = 0; + // Preset name might have "[ModuleName]/" preceding it, detect it here and select proper module! + int slashPos = preset.find_first_of('/'); + if (slashPos != std::string::npos) { + // Get the module ID and cut off the module specifier in the string + whichModule = GetModuleID(preset.substr(0, slashPos)); + preset = preset.substr(slashPos + 1); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEntityPreset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads an instance of an Entity that will be used as preset -// to later on be used to generate more instances of the same state. -// Will check if there was already one of the same class and instance -// name read in previously, and will return that one if found, or -// add the newly read in one to this PresetMan's list if not found to -// exist there previously. Ownership is NOT transferred! - -const Entity * PresetMan::GetEntityPreset(Reader &reader) -{ - // The reader is aware of which DataModule it is reading within - int whichModule = reader.GetReadModuleID(); - RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Reader has an out of bounds module number!"); - - std::string ClassName; - const Entity::ClassInfo *pClass = 0; - Entity *pNewInstance = 0; - const Entity *pReturnPreset = 0; - // Load class name and then preset instance - reader >> ClassName; - pClass = Entity::ClassInfo::GetClass(ClassName); - - if (pClass && pClass->IsConcrete()) - { - // Instantiate - pNewInstance = pClass->NewInstance(); - - // Get this before reading entity, since if it's the last one in its datafile, the stream will show the parent file instead - std::string entityFilePath = reader.GetCurrentFilePath(); - - // Try to read in the preset instance's data from the reader - if (pNewInstance && pNewInstance->Create(reader, false) < 0) - { - // Abort loading if we can't create entity and it's not in a module that allows ignoring missing items. - if (!g_PresetMan.GetDataModule(whichModule)->GetIgnoreMissingItems()) - RTEAbort("Reading of a preset instance \"" + pNewInstance->GetPresetName() + "\" of class " + pNewInstance->GetClassName() + " failed in file " + reader.GetCurrentFilePath() + ", shortly before line #" + reader.GetCurrentFileLine()); + // All modules + if (whichModule < 0) { + // Search all modules + for (int i = 0; i < m_pDataModules.size() && !pRetEntity; ++i) + pRetEntity = m_pDataModules[i]->GetEntityPreset(type, preset); } - else if (pNewInstance) - { - // Try to add the instance to the collection - m_pDataModules[whichModule]->AddEntityPreset(pNewInstance, reader.GetPresetOverwriting(), entityFilePath); - - // Regardless of whether there was a collision or not, use whatever now exists in the instance map of that class and name - pReturnPreset = m_pDataModules[whichModule]->GetEntityPreset(pNewInstance->GetClassName(), pNewInstance->GetPresetName()); - // If the instance wasn't found in the specific DataModule, try to find it in all the official ones instead - if (!pReturnPreset) - { + // Specific module + else { + // Try to get it from the asked for module + pRetEntity = m_pDataModules[whichModule]->GetEntityPreset(type, preset); + + // If couldn't find it in there, then try all the official modules! + if (!pRetEntity) { RTEAssert(m_OfficialModuleCount <= m_pDataModules.size(), "More official modules than modules loaded?!"); - for (int i = 0; i < m_OfficialModuleCount && !pReturnPreset; ++i) - pReturnPreset = m_pDataModules[i]->GetEntityPreset(pNewInstance->GetClassName(), pNewInstance->GetPresetName()); + for (int i = 0; i < m_OfficialModuleCount && !pRetEntity; ++i) { + pRetEntity = m_pDataModules[i]->GetEntityPreset(type, preset); + } } } - // Get rid of the read-in instance as its copy is now either added to the map, or discarded as there already was somehting in there of the same name. - delete pNewInstance; pNewInstance = 0; - } - else - pReturnPreset = 0; - return pReturnPreset; -} + return pRetEntity; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEntityPreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads an instance of an Entity that will be used as preset + // to later on be used to generate more instances of the same state. + // Will check if there was already one of the same class and instance + // name read in previously, and will return that one if found, or + // add the newly read in one to this PresetMan's list if not found to + // exist there previously. Ownership is NOT transferred! + + const Entity* PresetMan::GetEntityPreset(Reader& reader) { + // The reader is aware of which DataModule it is reading within + int whichModule = reader.GetReadModuleID(); + RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Reader has an out of bounds module number!"); + + std::string ClassName; + const Entity::ClassInfo* pClass = 0; + Entity* pNewInstance = 0; + const Entity* pReturnPreset = 0; + // Load class name and then preset instance + reader >> ClassName; + pClass = Entity::ClassInfo::GetClass(ClassName); + + if (pClass && pClass->IsConcrete()) { + // Instantiate + pNewInstance = pClass->NewInstance(); + + // Get this before reading entity, since if it's the last one in its datafile, the stream will show the parent file instead + std::string entityFilePath = reader.GetCurrentFilePath(); + + // Try to read in the preset instance's data from the reader + if (pNewInstance && pNewInstance->Create(reader, false) < 0) { + // Abort loading if we can't create entity and it's not in a module that allows ignoring missing items. + if (!g_PresetMan.GetDataModule(whichModule)->GetIgnoreMissingItems()) + RTEAbort("Reading of a preset instance \"" + pNewInstance->GetPresetName() + "\" of class " + pNewInstance->GetClassName() + " failed in file " + reader.GetCurrentFilePath() + ", shortly before line #" + reader.GetCurrentFileLine()); + } else if (pNewInstance) { + // Try to add the instance to the collection + m_pDataModules[whichModule]->AddEntityPreset(pNewInstance, reader.GetPresetOverwriting(), entityFilePath); + + // Regardless of whether there was a collision or not, use whatever now exists in the instance map of that class and name + pReturnPreset = m_pDataModules[whichModule]->GetEntityPreset(pNewInstance->GetClassName(), pNewInstance->GetPresetName()); + // If the instance wasn't found in the specific DataModule, try to find it in all the official ones instead + if (!pReturnPreset) { + RTEAssert(m_OfficialModuleCount <= m_pDataModules.size(), "More official modules than modules loaded?!"); + for (int i = 0; i < m_OfficialModuleCount && !pReturnPreset; ++i) + pReturnPreset = m_pDataModules[i]->GetEntityPreset(pNewInstance->GetClassName(), pNewInstance->GetPresetName()); + } + } + // Get rid of the read-in instance as its copy is now either added to the map, or discarded as there already was somehting in there of the same name. + delete pNewInstance; + pNewInstance = 0; + } else + pReturnPreset = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReadReflectedPreset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads an preset of an Entity and tries to add it to the list of -// read-in presets. Regardless of whether there is a name collision, -// the read-in preset will be returned, ownership TRANSFERRED! - -Entity * PresetMan::ReadReflectedPreset(Reader &reader) -{ - // The reader is aware of which DataModule it's reading within - int whichModule = reader.GetReadModuleID(); - RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Reader has an out of bounds module number!"); - - std::string ClassName; - const Entity::ClassInfo *pClass = 0; - Entity *pNewInstance = 0; - // Load class name and then preset instance - reader >> ClassName; - pClass = Entity::ClassInfo::GetClass(ClassName); - - if (pClass && pClass->IsConcrete()) - { - // Instantiate - pNewInstance = pClass->NewInstance(); - - // Get this before reading entity, since if it's the last one in its datafile, the stream will show the parent file instead - std::string entityFilePath = reader.GetCurrentFilePath(); - - // Try to read in the preset instance's data from the reader - if (pNewInstance && pNewInstance->Create(reader, false) < 0) - { - if (!g_PresetMan.GetDataModule(whichModule)->GetIgnoreMissingItems()) - RTEAbort("Reading of a preset instance \"" + pNewInstance->GetPresetName() + "\" of class " + pNewInstance->GetClassName() + " failed in file " + reader.GetCurrentFilePath() + ", shortly before line #" + reader.GetCurrentFileLine()); - } - else - { - // Try to add the instance to the collection. - // Note that we'll return this instance regardless of whether the adding was succesful or not - m_pDataModules[whichModule]->AddEntityPreset(pNewInstance, reader.GetPresetOverwriting(), entityFilePath); - return pNewInstance; + return pReturnPreset; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReadReflectedPreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads an preset of an Entity and tries to add it to the list of + // read-in presets. Regardless of whether there is a name collision, + // the read-in preset will be returned, ownership TRANSFERRED! + + Entity* PresetMan::ReadReflectedPreset(Reader& reader) { + // The reader is aware of which DataModule it's reading within + int whichModule = reader.GetReadModuleID(); + RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Reader has an out of bounds module number!"); + + std::string ClassName; + const Entity::ClassInfo* pClass = 0; + Entity* pNewInstance = 0; + // Load class name and then preset instance + reader >> ClassName; + pClass = Entity::ClassInfo::GetClass(ClassName); + + if (pClass && pClass->IsConcrete()) { + // Instantiate + pNewInstance = pClass->NewInstance(); + + // Get this before reading entity, since if it's the last one in its datafile, the stream will show the parent file instead + std::string entityFilePath = reader.GetCurrentFilePath(); + + // Try to read in the preset instance's data from the reader + if (pNewInstance && pNewInstance->Create(reader, false) < 0) { + if (!g_PresetMan.GetDataModule(whichModule)->GetIgnoreMissingItems()) + RTEAbort("Reading of a preset instance \"" + pNewInstance->GetPresetName() + "\" of class " + pNewInstance->GetClassName() + " failed in file " + reader.GetCurrentFilePath() + ", shortly before line #" + reader.GetCurrentFileLine()); + } else { + // Try to add the instance to the collection. + // Note that we'll return this instance regardless of whether the adding was succesful or not + m_pDataModules[whichModule]->AddEntityPreset(pNewInstance, reader.GetPresetOverwriting(), entityFilePath); + return pNewInstance; + } } - } - return 0; -} + return 0; + } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAllOfType + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds to a list all previously read in (defined) Entitys, by type. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAllOfType -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds to a list all previously read in (defined) Entitys, by type. + bool PresetMan::GetAllOfType(std::list& entityList, std::string type, int whichModule) { + if (type.empty()) + return false; -bool PresetMan::GetAllOfType(std::list &entityList, std::string type, int whichModule) -{ - if (type.empty()) - return false; + bool foundAny = false; - bool foundAny = false; + // All modules + if (whichModule < 0) { + // Send the list to each module + for (int i = 0; i < m_pDataModules.size(); ++i) + foundAny = m_pDataModules[i]->GetAllOfType(entityList, type) || foundAny; + } + // Specific module + else { + RTEAssert(whichModule < (int)m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID!"); + foundAny = m_pDataModules[whichModule]->GetAllOfType(entityList, type); + } - // All modules - if (whichModule < 0) - { - // Send the list to each module - for (int i = 0; i < m_pDataModules.size(); ++i) - foundAny = m_pDataModules[i]->GetAllOfType(entityList, type) || foundAny; - } - // Specific module - else - { - RTEAssert(whichModule < (int)m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID!"); - foundAny = m_pDataModules[whichModule]->GetAllOfType(entityList, type); - } + return foundAny; + } - return foundAny; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAllOfTypeInModuleSpace + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds to a list all previously read in (defined) Entitys which are + // of a specific type, and only exist in a specific module space. + bool PresetMan::GetAllOfTypeInModuleSpace(std::list& entityList, std::string type, int whichModuleSpace) { + if (type.empty()) + return false; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAllOfTypeInModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds to a list all previously read in (defined) Entitys which are -// of a specific type, and only exist in a specific module space. - -bool PresetMan::GetAllOfTypeInModuleSpace(std::list &entityList, std::string type, int whichModuleSpace) -{ - if (type.empty()) - return false; - - bool foundAny = false; - - // All modules - if (whichModuleSpace < 0) - foundAny = GetAllOfType(entityList, type, whichModuleSpace); - // Specific module space - else - { - // Get all entitys of the specific type in the official modules loaded before the specified one - for (int module = 0; module < m_OfficialModuleCount && module < whichModuleSpace; ++module) - foundAny = GetAllOfType(entityList, type, module) || foundAny; - - // Now get the groups of the specified module (official or not) - foundAny = GetAllOfType(entityList, type, whichModuleSpace) || foundAny; - } - - return foundAny; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool PresetMan::GetAllOfGroups(std::list &entityList, const std::vector &groups, const std::string &type, int whichModule) { - RTEAssert(!groups.empty(), "Looking for empty groups in PresetMan::GetAllOfGroups!"); - bool foundAny = false; - - if (whichModule < 0) { - for (DataModule *dataModule : m_pDataModules) { - foundAny = dataModule->GetAllOfGroups(entityList, groups, type) || foundAny; + bool foundAny = false; + + // All modules + if (whichModuleSpace < 0) + foundAny = GetAllOfType(entityList, type, whichModuleSpace); + // Specific module space + else { + // Get all entitys of the specific type in the official modules loaded before the specified one + for (int module = 0; module < m_OfficialModuleCount && module < whichModuleSpace; ++module) + foundAny = GetAllOfType(entityList, type, module) || foundAny; + + // Now get the groups of the specified module (official or not) + foundAny = GetAllOfType(entityList, type, whichModuleSpace) || foundAny; } - } else { - RTEAssert(whichModule < (int)m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID in PresetMan::GetAllOfGroups!"); - foundAny = m_pDataModules[whichModule]->GetAllOfGroups(entityList, groups, type); + + return foundAny; } - return foundAny; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool PresetMan::GetAllNotOfGroups(std::list &entityList, const std::vector &groups, const std::string &type, int whichModule) { - if (groups.empty()) { - RTEAbort("Looking for empty groups in PresetMan::GetAllNotOfGroups!"); - } else if (std::find(groups.begin(), groups.end(), "All") != groups.end()) { - RTEAbort("Trying to exclude all groups while looking for presets in PresetMan::GetAllNotOfGroups!"); + bool PresetMan::GetAllOfGroups(std::list& entityList, const std::vector& groups, const std::string& type, int whichModule) { + RTEAssert(!groups.empty(), "Looking for empty groups in PresetMan::GetAllOfGroups!"); + bool foundAny = false; + + if (whichModule < 0) { + for (DataModule* dataModule: m_pDataModules) { + foundAny = dataModule->GetAllOfGroups(entityList, groups, type) || foundAny; + } + } else { + RTEAssert(whichModule < (int)m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID in PresetMan::GetAllOfGroups!"); + foundAny = m_pDataModules[whichModule]->GetAllOfGroups(entityList, groups, type); + } + return foundAny; } - bool foundAny = false; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (whichModule < 0) { - for (DataModule *dataModule : m_pDataModules) { - foundAny = dataModule->GetAllNotOfGroups(entityList, groups, type) || foundAny; + bool PresetMan::GetAllNotOfGroups(std::list& entityList, const std::vector& groups, const std::string& type, int whichModule) { + if (groups.empty()) { + RTEAbort("Looking for empty groups in PresetMan::GetAllNotOfGroups!"); + } else if (std::find(groups.begin(), groups.end(), "All") != groups.end()) { + RTEAbort("Trying to exclude all groups while looking for presets in PresetMan::GetAllNotOfGroups!"); } - } else { - RTEAssert(whichModule < (int)m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID in PresetMan::GetAllNotOfGroups!"); - foundAny = m_pDataModules[whichModule]->GetAllNotOfGroups(entityList, groups, type); - } - return foundAny; -} + bool foundAny = false; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRandomOfGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a previously read in (defined) Entity which is randomly -// selected from a specific group. - -Entity * PresetMan::GetRandomOfGroup(std::string group, std::string type, int whichModule) -{ - RTEAssert(!group.empty(), "Looking for empty group!"); - - bool foundAny = false; - // The total list we'll select a random one from - std::list entityList; - - // All modules - if (whichModule < 0) - { - // Get from all modules - for (int i = 0; i < m_pDataModules.size(); ++i) - // Send the list to each module, let them add - foundAny = m_pDataModules[i]->GetAllOfGroups(entityList, { group }, type) || foundAny; - } - // Specific one - else - { - RTEAssert(whichModule < m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID!"); - foundAny = m_pDataModules[whichModule]->GetAllOfGroups(entityList, { group }, type); - } - - // Didn't find any of that group in those module(s) - if (!foundAny) - return 0; - - // Pick one and return it - int current = 0; - int selection = RandomNum(0, entityList.size() - 1); - for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) - { - if (current == selection) - return (*itr); - current++; - } - - RTEAssert(0, "Tried selecting randomly but didn't?"); - return 0; -} + if (whichModule < 0) { + for (DataModule* dataModule: m_pDataModules) { + foundAny = dataModule->GetAllNotOfGroups(entityList, groups, type) || foundAny; + } + } else { + RTEAssert(whichModule < (int)m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID in PresetMan::GetAllNotOfGroups!"); + foundAny = m_pDataModules[whichModule]->GetAllNotOfGroups(entityList, groups, type); + } + return foundAny; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRandomOfGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a previously read in (defined) Entity which is randomly + // selected from a specific group. + + Entity* PresetMan::GetRandomOfGroup(std::string group, std::string type, int whichModule) { + RTEAssert(!group.empty(), "Looking for empty group!"); + + bool foundAny = false; + // The total list we'll select a random one from + std::list entityList; + + // All modules + if (whichModule < 0) { + // Get from all modules + for (int i = 0; i < m_pDataModules.size(); ++i) + // Send the list to each module, let them add + foundAny = m_pDataModules[i]->GetAllOfGroups(entityList, {group}, type) || foundAny; + } + // Specific one + else { + RTEAssert(whichModule < m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID!"); + foundAny = m_pDataModules[whichModule]->GetAllOfGroups(entityList, {group}, type); + } + // Didn't find any of that group in those module(s) + if (!foundAny) + return 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRandomOfGroupFromTech -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a previously read in (defined) Entity which is randomly -// selected from a specific group. - -Entity * PresetMan::GetRandomBuyableOfGroupFromTech(std::string group, std::string type, int whichModule) -{ - RTEAssert(!group.empty(), "Looking for empty group!"); - - bool foundAny = false; - // The total list we'll select a random one from - std::list entityList; - std::list tempList; - - // All modules - if (whichModule < 0) { - for (DataModule *dataModule : m_pDataModules) { - if (dataModule->IsFaction()) { foundAny = dataModule->GetAllOfGroups(tempList, { group }, type) || foundAny; } + // Pick one and return it + int current = 0; + int selection = RandomNum(0, entityList.size() - 1); + for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) { + if (current == selection) + return (*itr); + current++; } - } else { - RTEAssert(whichModule < m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID!"); - foundAny = m_pDataModules[whichModule]->GetAllOfGroups(tempList, { group }, type); + + RTEAssert(0, "Tried selecting randomly but didn't?"); + return 0; } - //Filter found entities, we need only buyables - if (foundAny) - { - //Do not filter anything if we're looking for brains - if (group == "Brains") - { - foundAny = false; - for (std::list::iterator oItr = tempList.begin(); oItr != tempList.end(); ++oItr) - { - entityList.push_back(*oItr); - foundAny = true; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRandomOfGroupFromTech + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a previously read in (defined) Entity which is randomly + // selected from a specific group. + + Entity* PresetMan::GetRandomBuyableOfGroupFromTech(std::string group, std::string type, int whichModule) { + RTEAssert(!group.empty(), "Looking for empty group!"); + + bool foundAny = false; + // The total list we'll select a random one from + std::list entityList; + std::list tempList; + + // All modules + if (whichModule < 0) { + for (DataModule* dataModule: m_pDataModules) { + if (dataModule->IsFaction()) { + foundAny = dataModule->GetAllOfGroups(tempList, {group}, type) || foundAny; + } } + } else { + RTEAssert(whichModule < m_pDataModules.size(), "Trying to get from an out of bounds DataModule ID!"); + foundAny = m_pDataModules[whichModule]->GetAllOfGroups(tempList, {group}, type); } - else - { - foundAny = false; - for (std::list::iterator oItr = tempList.begin(); oItr != tempList.end(); ++oItr) - { - SceneObject * pSObject = dynamic_cast(*oItr); - // Buyable and not brain? - if (pSObject && pSObject->IsBuyable() && !pSObject->IsBuyableInObjectPickerOnly() && !pSObject->IsInGroup("Brains")) - { + + // Filter found entities, we need only buyables + if (foundAny) { + // Do not filter anything if we're looking for brains + if (group == "Brains") { + foundAny = false; + for (std::list::iterator oItr = tempList.begin(); oItr != tempList.end(); ++oItr) { entityList.push_back(*oItr); foundAny = true; } + } else { + foundAny = false; + for (std::list::iterator oItr = tempList.begin(); oItr != tempList.end(); ++oItr) { + SceneObject* pSObject = dynamic_cast(*oItr); + // Buyable and not brain? + if (pSObject && pSObject->IsBuyable() && !pSObject->IsBuyableInObjectPickerOnly() && !pSObject->IsInGroup("Brains")) { + entityList.push_back(*oItr); + foundAny = true; + } + } } } - } - // Didn't find any of that group in those module(s) - if (!foundAny) - return 0; + // Didn't find any of that group in those module(s) + if (!foundAny) + return 0; - // Pick one and return it - int current = 0; - int selection = RandomNum(0, entityList.size() - 1); + // Pick one and return it + int current = 0; + int selection = RandomNum(0, entityList.size() - 1); - int totalWeight = 0; - for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) - totalWeight += (*itr)->GetRandomWeight(); + int totalWeight = 0; + for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) + totalWeight += (*itr)->GetRandomWeight(); - // Use random weights if looking in specific modules - if (whichModule >= 0) - { - if (totalWeight == 0) - return 0; + // Use random weights if looking in specific modules + if (whichModule >= 0) { + if (totalWeight == 0) + return 0; - selection = RandomNum(0, totalWeight - 1); - - for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) - { - bool found = false; - int bucketCounter = 0; - - if ((*itr)->GetRandomWeight() > 0) - { - while (bucketCounter < (*itr)->GetRandomWeight()) - { - if (current == selection) - { - found = true; - break; - } + selection = RandomNum(0, totalWeight - 1); + + for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) { + bool found = false; + int bucketCounter = 0; + + if ((*itr)->GetRandomWeight() > 0) { + while (bucketCounter < (*itr)->GetRandomWeight()) { + if (current == selection) { + found = true; + break; + } - current++; - bucketCounter++; + current++; + bucketCounter++; + } } + + if (found) + return (*itr); } + } else { + for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) { + if (current == selection) + return (*itr); - if (found) - return (*itr); + current++; + } } + + RTEAssert(0, "Tried selecting randomly but didn't?"); + return 0; } - else - { - for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) - { - if (current == selection) - return (*itr); - current++; + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAllOfGroupInModuleSpace + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds to a list all previously read in (defined) Entitys which are + // associated with a specific group, and only exist in a specific module + // space. + + bool PresetMan::GetAllOfGroupInModuleSpace(std::list& entityList, std::string group, std::string type, int whichModuleSpace) { + RTEAssert(!group.empty(), "Looking for empty group!"); + + bool foundAny = false; + + // All modules + if (whichModuleSpace < 0) + foundAny = GetAllOfGroup(entityList, group, type, whichModuleSpace); + // Specific module space + else { + // Get all entitys of the specific group the official modules loaded before the specified one + for (int module = 0; module < m_OfficialModuleCount && module < whichModuleSpace; ++module) + foundAny = GetAllOfGroup(entityList, group, type, module) || foundAny; + + // Now get the groups of the specified module (official or not) + foundAny = GetAllOfGroup(entityList, group, type, whichModuleSpace) || foundAny; } - } - RTEAssert(0, "Tried selecting randomly but didn't?"); - return 0; -} + return foundAny; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAllOfGroupInModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds to a list all previously read in (defined) Entitys which are -// associated with a specific group, and only exist in a specific module -// space. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRandomOfGroupInModuleSpace + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a previously read in (defined) Entity which is associated with + // a specific group, randomly selected and only exist in a specific module + // space. + + Entity* PresetMan::GetRandomOfGroupInModuleSpace(std::string group, std::string type, int whichModuleSpace) { + RTEAssert(!group.empty(), "Looking for empty group!"); + + bool foundAny = false; + // The total list we'll select a random one from + std::list entityList; + + // All modules + if (whichModuleSpace < 0) + foundAny = GetAllOfGroup(entityList, group, type, whichModuleSpace); + // Specific module space + else { + // Get all entitys of the specific group the official modules loaded before the specified one + for (int module = 0; module < m_OfficialModuleCount && module < whichModuleSpace; ++module) + foundAny = GetAllOfGroup(entityList, group, type, module) || foundAny; + + // Now get the groups of the specified module (official or not) + foundAny = GetAllOfGroup(entityList, group, type, whichModuleSpace) || foundAny; + } -bool PresetMan::GetAllOfGroupInModuleSpace(std::list &entityList, std::string group, std::string type, int whichModuleSpace) -{ - RTEAssert(!group.empty(), "Looking for empty group!"); + // Didn't find any of that group in those module(s) + if (!foundAny) + return 0; - bool foundAny = false; + // Pick one and return it + int current = 0; + int selection = RandomNum(0, entityList.size() - 1); + for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) { + if (current == selection) + return (*itr); + current++; + } - // All modules - if (whichModuleSpace < 0) - foundAny = GetAllOfGroup(entityList, group, type, whichModuleSpace); - // Specific module space - else - { - // Get all entitys of the specific group the official modules loaded before the specified one - for (int module = 0; module < m_OfficialModuleCount && module < whichModuleSpace; ++module) - foundAny = GetAllOfGroup(entityList, group, type, module) || foundAny; + RTEAssert(0, "Tried selecting randomly but didn't?"); + return 0; + } - // Now get the groups of the specified module (official or not) - foundAny = GetAllOfGroup(entityList, group, type, whichModuleSpace) || foundAny; - } + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEntityDataLocation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the data file path of a previously read in (defined) Entity. - return foundAny; -} + std::string PresetMan::GetEntityDataLocation(std::string type, std::string preset, int whichModule) { + RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); + std::string pRetPath = ""; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRandomOfGroupInModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a previously read in (defined) Entity which is associated with -// a specific group, randomly selected and only exist in a specific module -// space. - -Entity * PresetMan::GetRandomOfGroupInModuleSpace(std::string group, std::string type, int whichModuleSpace) -{ - RTEAssert(!group.empty(), "Looking for empty group!"); - - bool foundAny = false; - // The total list we'll select a random one from - std::list entityList; - - // All modules - if (whichModuleSpace < 0) - foundAny = GetAllOfGroup(entityList, group, type, whichModuleSpace); - // Specific module space - else - { - // Get all entitys of the specific group the official modules loaded before the specified one - for (int module = 0; module < m_OfficialModuleCount && module < whichModuleSpace; ++module) - foundAny = GetAllOfGroup(entityList, group, type, module) || foundAny; - - // Now get the groups of the specified module (official or not) - foundAny = GetAllOfGroup(entityList, group, type, whichModuleSpace) || foundAny; - } - - // Didn't find any of that group in those module(s) - if (!foundAny) - return 0; - - // Pick one and return it - int current = 0; - int selection = RandomNum(0, entityList.size() - 1); - for (std::list::iterator itr = entityList.begin(); itr != entityList.end(); ++itr) - { - if (current == selection) - return (*itr); - current++; - } - - RTEAssert(0, "Tried selecting randomly but didn't?"); - return 0; -} + // All modules + if (whichModule < 0) { + // Search all modules + for (int i = 0; i < m_pDataModules.size() && pRetPath.empty(); ++i) + pRetPath = m_pDataModules[i]->GetEntityDataLocation(type, preset); + } + // Specific module + else { + // Try to get it from the asked for module + pRetPath = m_pDataModules[whichModule]->GetEntityDataLocation(type, preset); + // If couldn't find it in there, then try all the official modules! + if (pRetPath.empty()) { + RTEAssert(m_OfficialModuleCount <= m_pDataModules.size(), "More official modules than modules loaded?!"); + for (int i = 0; i < m_OfficialModuleCount && pRetPath.empty(); ++i) { + pRetPath = m_pDataModules[i]->GetEntityDataLocation(type, preset); + } + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEntityDataLocation -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the data file path of a previously read in (defined) Entity. - -std::string PresetMan::GetEntityDataLocation(std::string type, std::string preset, int whichModule) -{ - RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); - - std::string pRetPath = ""; - - // All modules - if (whichModule < 0) - { - // Search all modules - for (int i = 0; i < m_pDataModules.size() && pRetPath.empty(); ++i) - pRetPath = m_pDataModules[i]->GetEntityDataLocation(type, preset); - } - // Specific module - else - { - // Try to get it from the asked for module - pRetPath = m_pDataModules[whichModule]->GetEntityDataLocation(type, preset); - - // If couldn't find it in there, then try all the official modules! - if (pRetPath.empty()) - { - RTEAssert(m_OfficialModuleCount <= m_pDataModules.size(), "More official modules than modules loaded?!"); - for (int i = 0; i < m_OfficialModuleCount && pRetPath.empty(); ++i) - { - pRetPath = m_pDataModules[i]->GetEntityDataLocation(type, preset); - } - } - } - - return pRetPath; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void PresetMan::ReloadAllScripts() const { - g_LuaMan.ClearUserModuleCache(); - for (const DataModule *dataModule : m_pDataModules) { - dataModule->ReloadAllScripts(); + return pRetPath; } - g_MovableMan.ReloadLuaScripts(); - g_ConsoleMan.PrintString("SYSTEM: Scripts reloaded!"); -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool PresetMan::ReloadEntityPreset(const std::string &presetName, const std::string &className, const std::string &moduleName, bool storeReloadedPresetDataForQuickReloading) { - if (className.empty() || presetName.empty()) { - g_ConsoleMan.PrintString("ERROR: Trying to reload Entity preset without specifying preset name or type!"); - return false; + void PresetMan::ReloadAllScripts() const { + g_LuaMan.ClearUserModuleCache(); + for (const DataModule* dataModule: m_pDataModules) { + dataModule->ReloadAllScripts(); + } + g_MovableMan.ReloadLuaScripts(); + g_ConsoleMan.PrintString("SYSTEM: Scripts reloaded!"); } - int moduleId = -1; - if (moduleName != "") { - moduleId = GetModuleID(moduleName); - if (moduleId < 0) { - g_ConsoleMan.PrintString("ERROR: Failed to find data module with name \"" + moduleName + "\" while attempting to reload an Entity preset with name \"" + presetName + "\" of type \"" + className + "\"!"); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool PresetMan::ReloadEntityPreset(const std::string& presetName, const std::string& className, const std::string& moduleName, bool storeReloadedPresetDataForQuickReloading) { + if (className.empty() || presetName.empty()) { + g_ConsoleMan.PrintString("ERROR: Trying to reload Entity preset without specifying preset name or type!"); return false; } - } - std::string actualDataModuleOfPreset = moduleName; - - std::string presetDataLocation = GetEntityDataLocation(className, presetName, moduleId); - if (presetDataLocation.empty()) { - g_ConsoleMan.PrintString("ERROR: Failed to locate data of Entity preset with name \"" + presetName + "\" of type \"" + className + "\" in \"" + moduleName + "\" or any official module! The preset might not exist!"); - return false; - } - // GetEntityDataLocation will attempt to locate the preset in the official modules if it fails to locate it in the specified module. Warn and correct the result string. - if (std::string presetDataLocationModuleName = presetDataLocation.substr(0, presetDataLocation.find_first_of("/\\")); presetDataLocationModuleName != actualDataModuleOfPreset) { - actualDataModuleOfPreset = presetDataLocationModuleName; + int moduleId = -1; if (moduleName != "") { - g_ConsoleMan.PrintString("WARNING: Failed to locate data of Entity preset with name \"" + presetName + "\" of type \"" + className + "\" in \"" + moduleName + "\"! Entity preset data matching the name and type was found in \"" + actualDataModuleOfPreset + "\"!"); + moduleId = GetModuleID(moduleName); + if (moduleId < 0) { + g_ConsoleMan.PrintString("ERROR: Failed to find data module with name \"" + moduleName + "\" while attempting to reload an Entity preset with name \"" + presetName + "\" of type \"" + className + "\"!"); + return false; + } } - } + std::string actualDataModuleOfPreset = moduleName; - m_ReloadEntityPresetCalledThisUpdate = true; + std::string presetDataLocation = GetEntityDataLocation(className, presetName, moduleId); + if (presetDataLocation.empty()) { + g_ConsoleMan.PrintString("ERROR: Failed to locate data of Entity preset with name \"" + presetName + "\" of type \"" + className + "\" in \"" + moduleName + "\" or any official module! The preset might not exist!"); + return false; + } - Reader reader(presetDataLocation.c_str(), true); - while (reader.NextProperty()) { - reader.ReadPropName(); - g_PresetMan.GetEntityPreset(reader); - } - g_ConsoleMan.PrintString("SYSTEM: Entity preset with name \"" + presetName + "\" of type \"" + className + "\" defined in \"" + actualDataModuleOfPreset + "\" was successfully reloaded"); + // GetEntityDataLocation will attempt to locate the preset in the official modules if it fails to locate it in the specified module. Warn and correct the result string. + if (std::string presetDataLocationModuleName = presetDataLocation.substr(0, presetDataLocation.find_first_of("/\\")); presetDataLocationModuleName != actualDataModuleOfPreset) { + actualDataModuleOfPreset = presetDataLocationModuleName; + if (moduleName != "") { + g_ConsoleMan.PrintString("WARNING: Failed to locate data of Entity preset with name \"" + presetName + "\" of type \"" + className + "\" in \"" + moduleName + "\"! Entity preset data matching the name and type was found in \"" + actualDataModuleOfPreset + "\"!"); + } + } - if (storeReloadedPresetDataForQuickReloading) { - m_LastReloadedEntityPresetInfo[0] = presetName; - m_LastReloadedEntityPresetInfo[1] = className; - m_LastReloadedEntityPresetInfo[2] = moduleName == "" ? actualDataModuleOfPreset : moduleName; // If there was a module name, store it as-is so that if there's a data location warning, it persists on every quick reload. + m_ReloadEntityPresetCalledThisUpdate = true; + + Reader reader(presetDataLocation.c_str(), true); + while (reader.NextProperty()) { + reader.ReadPropName(); + g_PresetMan.GetEntityPreset(reader); + } + g_ConsoleMan.PrintString("SYSTEM: Entity preset with name \"" + presetName + "\" of type \"" + className + "\" defined in \"" + actualDataModuleOfPreset + "\" was successfully reloaded"); + + if (storeReloadedPresetDataForQuickReloading) { + m_LastReloadedEntityPresetInfo[0] = presetName; + m_LastReloadedEntityPresetInfo[1] = className; + m_LastReloadedEntityPresetInfo[2] = moduleName == "" ? actualDataModuleOfPreset : moduleName; // If there was a module name, store it as-is so that if there's a data location warning, it persists on every quick reload. + } + return true; } - return true; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool PresetMan::QuickReloadEntityPreset() { - for (const std::string &entityPresetInfoEntry : m_LastReloadedEntityPresetInfo) { - if (entityPresetInfoEntry.empty()) { - g_ConsoleMan.PrintString("ERROR: Trying to quick reload Entity preset when there is nothing set to reload!"); - return false; + bool PresetMan::QuickReloadEntityPreset() { + for (const std::string& entityPresetInfoEntry: m_LastReloadedEntityPresetInfo) { + if (entityPresetInfoEntry.empty()) { + g_ConsoleMan.PrintString("ERROR: Trying to quick reload Entity preset when there is nothing set to reload!"); + return false; + } } + return ReloadEntityPreset(m_LastReloadedEntityPresetInfo[0], m_LastReloadedEntityPresetInfo[1], m_LastReloadedEntityPresetInfo[2]); } - return ReloadEntityPreset(m_LastReloadedEntityPresetInfo[0], m_LastReloadedEntityPresetInfo[1], m_LastReloadedEntityPresetInfo[2]); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddMaterialMapping + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a Material mapping local to a DataModule. This is used for when + // multiple DataModule:s are loading conflicting Material:s, and need to + // resolve the conflicts by mapping their materials to ID's different than + // those specified in the data files. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddMaterialMapping -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a Material mapping local to a DataModule. This is used for when -// multiple DataModule:s are loading conflicting Material:s, and need to -// resolve the conflicts by mapping their materials to ID's different than -// those specified in the data files. + bool PresetMan::AddMaterialMapping(int fromID, int toID, int whichModule) { + RTEAssert(whichModule >= m_OfficialModuleCount && whichModule < m_pDataModules.size(), "Tried to make a material mapping in an offical or out-of-bounds DataModule!"); -bool PresetMan::AddMaterialMapping(int fromID, int toID, int whichModule) -{ - RTEAssert(whichModule >= m_OfficialModuleCount && whichModule < m_pDataModules.size(), "Tried to make a material mapping in an offical or out-of-bounds DataModule!"); + return m_pDataModules[whichModule]->AddMaterialMapping(fromID, toID); + } - return m_pDataModules[whichModule]->AddMaterialMapping(fromID, toID); -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RegisterGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Registers the existence of an Entity group, and in which module. + void PresetMan::RegisterGroup(std::string newGroup, int whichModule) { + RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RegisterGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Registers the existence of an Entity group, and in which module. + // Register in the handy total list + m_TotalGroupRegister.push_back(newGroup); + m_TotalGroupRegister.sort(); + m_TotalGroupRegister.unique(); -void PresetMan::RegisterGroup(std::string newGroup, int whichModule) -{ - RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); + // Register in the specified module too + m_pDataModules[whichModule]->RegisterGroup(newGroup); + } - // Register in the handy total list - m_TotalGroupRegister.push_back(newGroup); - m_TotalGroupRegister.sort(); - m_TotalGroupRegister.unique(); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGroups + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the list of all groups registered in a specific module. - // Register in the specified module too - m_pDataModules[whichModule]->RegisterGroup(newGroup); -} + bool PresetMan::GetGroups(std::list& groupList, int whichModule, std::string withType) const { + RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); + bool foundAny = false; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGroups -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the list of all groups registered in a specific module. - -bool PresetMan::GetGroups(std::list &groupList, int whichModule, std::string withType) const -{ - RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); - - bool foundAny = false; - - // Asked for ALL groups ever registered - if (whichModule < 0) - { - // Get all applicable groups - if (withType == "All" || withType.empty()) - { - for (std::list::const_iterator gItr = m_TotalGroupRegister.begin(); gItr != m_TotalGroupRegister.end(); ++gItr) - groupList.push_back(*gItr); - - foundAny = !m_TotalGroupRegister.empty(); - } - // Filter out groups without any entitys of the specified type - else - { - for (int module = 0; module < (int)m_pDataModules.size(); ++module) - foundAny = m_pDataModules[module]->GetGroupsWithType(groupList, withType) || foundAny; - } - } - // Asked for specific DataModule's groups - else if (!m_pDataModules[whichModule]->GetGroupRegister()->empty()) - { - // Get ALL groups of that module - if (withType == "All" || withType.empty()) - { - const std::list *pGroupList = m_pDataModules[whichModule]->GetGroupRegister(); - for (std::list::const_iterator gItr = pGroupList->begin(); gItr != pGroupList->end(); ++gItr) - groupList.push_back(*gItr); - - foundAny = !pGroupList->empty(); - } - // Get only modules that contain an entity of valid type - else - foundAny = m_pDataModules[whichModule]->GetGroupsWithType(groupList, withType) || foundAny; - } - - return foundAny; -} + // Asked for ALL groups ever registered + if (whichModule < 0) { + // Get all applicable groups + if (withType == "All" || withType.empty()) { + for (std::list::const_iterator gItr = m_TotalGroupRegister.begin(); gItr != m_TotalGroupRegister.end(); ++gItr) + groupList.push_back(*gItr); + + foundAny = !m_TotalGroupRegister.empty(); + } + // Filter out groups without any entitys of the specified type + else { + for (int module = 0; module < (int)m_pDataModules.size(); ++module) + foundAny = m_pDataModules[module]->GetGroupsWithType(groupList, withType) || foundAny; + } + } + // Asked for specific DataModule's groups + else if (!m_pDataModules[whichModule]->GetGroupRegister()->empty()) { + // Get ALL groups of that module + if (withType == "All" || withType.empty()) { + const std::list* pGroupList = m_pDataModules[whichModule]->GetGroupRegister(); + for (std::list::const_iterator gItr = pGroupList->begin(); gItr != pGroupList->end(); ++gItr) + groupList.push_back(*gItr); + + foundAny = !pGroupList->empty(); + } + // Get only modules that contain an entity of valid type + else + foundAny = m_pDataModules[whichModule]->GetGroupsWithType(groupList, withType) || foundAny; + } + return foundAny; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetModuleSpaceGroups -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Fills out a list with all groups registered in all official, PLUS a -// a specific non-official module as well. - -bool PresetMan::GetModuleSpaceGroups(std::list &groupList, int whichModule, std::string withType) const -{ - RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); - - bool foundAny = false; - - // If all, then just copy the total register - if (whichModule < 0) - { - // Just get all groups ever registered - if (withType == "All" || withType.empty()) - { - for (std::list::const_iterator gItr = m_TotalGroupRegister.begin(); gItr != m_TotalGroupRegister.end(); ++gItr) - groupList.push_back(*gItr); - - foundAny = !m_TotalGroupRegister.empty(); - } - // Get type filtered groups from ALL data modules - else - { - for (int module = 0; module < (int)m_pDataModules.size(); ++module) - foundAny = GetGroups(groupList, module, withType) || foundAny; - } - } - // Getting modulespace of specific module - else - { - // Get all groups of the official modules that are loaded before the specified one - for (int module = 0; module < m_OfficialModuleCount && module < whichModule; ++module) - foundAny = GetGroups(groupList, module, withType) || foundAny; - - // Now get the groups of the specified module (official or not) - foundAny = GetGroups(groupList, whichModule, withType) || foundAny; - - // Make sure there are no dupe groups in the list - groupList.sort(); - groupList.unique(); - } - - return foundAny; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetModuleSpaceGroups + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Fills out a list with all groups registered in all official, PLUS a + // a specific non-official module as well. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLoadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns actor defined in the specified loadout. + bool PresetMan::GetModuleSpaceGroups(std::list& groupList, int whichModule, std::string withType) const { + RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); -Actor * PresetMan::GetLoadout(std::string loadoutName, std::string module, bool spawnDropShip) -{ - return GetLoadout(loadoutName, GetModuleID(module), spawnDropShip); -} + bool foundAny = false; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLoadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns actor defined in the specified loadout. + // If all, then just copy the total register + if (whichModule < 0) { + // Just get all groups ever registered + if (withType == "All" || withType.empty()) { + for (std::list::const_iterator gItr = m_TotalGroupRegister.begin(); gItr != m_TotalGroupRegister.end(); ++gItr) + groupList.push_back(*gItr); -Actor * PresetMan::GetLoadout(std::string loadoutName, int moduleNumber, bool spawnDropShip) -{ - if (spawnDropShip) - { - // Find the Loadout that this Deployment is referring to - const Loadout *pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", loadoutName, moduleNumber)); - if (pLoadout) - { - const ACraft * pCraftPreset = pLoadout->GetDeliveryCraft(); - if (pCraftPreset) - { - ACraft * pCraft = dynamic_cast(pCraftPreset->Clone()); - if (pCraft) - { - float tally = 0; - // Create and pass along the first Actor and his inventory defined in the Loadout - Actor * pActor = pLoadout->CreateFirstActor(moduleNumber, 1, 1, tally); - // Set the position and team etc for the Actor we are prepping to spawn - if (pActor) - pCraft->AddInventoryItem(pActor); - } - return pCraft; + foundAny = !m_TotalGroupRegister.empty(); + } + // Get type filtered groups from ALL data modules + else { + for (int module = 0; module < (int)m_pDataModules.size(); ++module) + foundAny = GetGroups(groupList, module, withType) || foundAny; } } - } - else - { - // Find the Loadout that this Deployment is referring to - const Loadout *pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", loadoutName, moduleNumber)); - if (pLoadout) - { - float tally = 0; - // Create and pass along the first Actor and his inventory defined in the Loadout - Actor * pReturnActor = pLoadout->CreateFirstActor(moduleNumber, 1, 1, tally); - // Set the position and team etc for the Actor we are prepping to spawn - return pReturnActor; + // Getting modulespace of specific module + else { + // Get all groups of the official modules that are loaded before the specified one + for (int module = 0; module < m_OfficialModuleCount && module < whichModule; ++module) + foundAny = GetGroups(groupList, module, withType) || foundAny; + + // Now get the groups of the specified module (official or not) + foundAny = GetGroups(groupList, whichModule, withType) || foundAny; + + // Make sure there are no dupe groups in the list + groupList.sort(); + groupList.unique(); } + + return foundAny; } - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLoadout + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns actor defined in the specified loadout. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Actor* PresetMan::GetLoadout(std::string loadoutName, std::string module, bool spawnDropShip) { + return GetLoadout(loadoutName, GetModuleID(module), spawnDropShip); + } -void PresetMan::FindAndExtractZippedModules() const { - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + System::GetModDirectory())) { - std::string zippedModulePath = std::filesystem::path(directoryEntry).generic_string(); - if (zippedModulePath.ends_with(System::GetZippedModulePackageExtension())) { - LoadingScreen::LoadingSplashProgressReport("Extracting Data Module from: " + directoryEntry.path().filename().generic_string(), true); - LoadingScreen::LoadingSplashProgressReport(System::ExtractZippedDataModule(zippedModulePath), true); + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLoadout + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns actor defined in the specified loadout. + + Actor* PresetMan::GetLoadout(std::string loadoutName, int moduleNumber, bool spawnDropShip) { + if (spawnDropShip) { + // Find the Loadout that this Deployment is referring to + const Loadout* pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", loadoutName, moduleNumber)); + if (pLoadout) { + const ACraft* pCraftPreset = pLoadout->GetDeliveryCraft(); + if (pCraftPreset) { + ACraft* pCraft = dynamic_cast(pCraftPreset->Clone()); + if (pCraft) { + float tally = 0; + // Create and pass along the first Actor and his inventory defined in the Loadout + Actor* pActor = pLoadout->CreateFirstActor(moduleNumber, 1, 1, tally); + // Set the position and team etc for the Actor we are prepping to spawn + if (pActor) + pCraft->AddInventoryItem(pActor); + } + return pCraft; + } + } + } else { + // Find the Loadout that this Deployment is referring to + const Loadout* pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", loadoutName, moduleNumber)); + if (pLoadout) { + float tally = 0; + // Create and pass along the first Actor and his inventory defined in the Loadout + Actor* pReturnActor = pLoadout->CreateFirstActor(moduleNumber, 1, 1, tally); + // Set the position and team etc for the Actor we are prepping to spawn + return pReturnActor; + } + } + + return 0; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void PresetMan::FindAndExtractZippedModules() const { + for (const std::filesystem::directory_entry& directoryEntry: std::filesystem::directory_iterator(System::GetWorkingDirectory() + System::GetModDirectory())) { + std::string zippedModulePath = std::filesystem::path(directoryEntry).generic_string(); + if (zippedModulePath.ends_with(System::GetZippedModulePackageExtension())) { + LoadingScreen::LoadingSplashProgressReport("Extracting Data Module from: " + directoryEntry.path().filename().generic_string(), true); + LoadingScreen::LoadingSplashProgressReport(System::ExtractZippedDataModule(zippedModulePath), true); + } } } -} } // namespace RTE diff --git a/Source/Managers/PresetMan.h b/Source/Managers/PresetMan.h index b06811cd09..42706c3067 100644 --- a/Source/Managers/PresetMan.h +++ b/Source/Managers/PresetMan.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -19,584 +18,549 @@ #define g_PresetMan PresetMan::Instance() -namespace RTE -{ - -class Actor; -class DataModule; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: PresetMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The singleton manager of all presets of Entity:s in the RTE. -// The presets serve as a respository of Entity instances with specific -// and unique and initial runtime data. -// Parent(s): Singleton, Serializable. -// Class history: 12/25/2001 PresetMan created. -// Class history: 05/30/2008 Changed name to PresetMan. - -class PresetMan : public Singleton { - friend struct ManagerLuaBindings; - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: PresetMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a PresetMan entity in system -// memory. Create() should be called before using the entity. -// Arguments: None. - - PresetMan() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~PresetMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a PresetMan entity before deletion -// from system memory. -// Arguments: None. - - ~PresetMan() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the PresetMan entity ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Initialize() { return 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire PresetMan, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the PresetMan entity. -// Arguments: None. -// Return value: None. - - void Destroy(); - - - /// - /// Reads an entire DataModule and adds it to this. NOTE that official modules can't be loaded after any non-official ones! - /// - /// The module name to read, e.g. "Base.rte". - /// Whether this module is 'official' or third party. If official, it has to not have any name conflicts with any other official module. - /// Whether this module is a userdata module. If true, will be treated as an unofficial module. - /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. - /// Whether the DataModule was read and added correctly. - bool LoadDataModule(const std::string &moduleName, bool official, bool userdata = false, const ProgressCallback &progressCallback = nullptr); - - /// - /// Reads an entire DataModule and adds it to this. NOTE that official modules can't be loaded after any non-official ones! - /// - /// The module name to read, e.g. "Base.rte". - /// Whether the DataModule was read and added correctly. - bool LoadDataModule(const std::string &moduleName) { return LoadDataModule(moduleName, false); } - - /// - /// Loads all the official data modules individually with LoadDataModule, then proceeds to look for any non-official modules and loads them as well. - /// - /// - bool LoadAllDataModules(); - - /// - /// Sets the single module to be loaded after the official modules. This will be the ONLY non-official module to be loaded. - /// - /// Name of the module to load. - void SetSingleModuleToLoad(std::string moduleName) { m_SingleModuleToLoad = moduleName; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDataModule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific loaded DataModule -// Arguments: The ID of the module to get. -// Return value: The requested DataModule. Ownership is NOT transferred! - - const DataModule * GetDataModule(int whichModule = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDataModuleName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a name specific loaded DataModule -// Arguments: The ID of the module to get. -// Return value: The requested DataModule name. - - const std::string GetDataModuleName(int whichModule = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetModuleID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the ID of a loaded DataModule. -// Arguments: The name of the DataModule to get the ID from, including the ".rte" -// Return value: The requested ID. If no module of the name was found, -1 will be returned. - - int GetModuleID(std::string moduleName); - - /// - /// Gets the Name of a loaded DataModule, from a full data file path. - /// - /// The full path to a data file inside the data module id you want to get. - /// The requested Name. If no module of the name was found, "" will be returned. - std::string GetModuleNameFromPath(const std::string &dataPath) const; - - /// - /// Gets the ID of a loaded DataModule from a full data file path. - /// - /// The full path to a data file inside the data module ID you want to get. - /// The requested ID. If no module of the name was found, -1 will be returned. - int GetModuleIDFromPath(const std::string &dataPath); - - /// - /// Returns whether or not the module is vanilla. - /// - /// The name of the module to check, in the form "[moduleName].rte" - /// True if the module is an official data module, otherwise false. - bool IsModuleOfficial(const std::string &moduleName) const; - - /// - /// Returns whether or not the module is vanilla. - /// - /// The name of the module to check, in the form "[moduleName].rte" - /// True if the module is a listed user data module, otherwise false. - bool IsModuleUserdata(const std::string &moduleName) const; - - /// - /// Returns the Full path to the module including Data/, Userdata/ or Mods/. - /// - /// The Path to be completed. - /// The complete path to the file, including Data/, Userdata/ or Mods/ based on whether or not it's part of an official module or userdata. - std::string GetFullModulePath(const std::string &modulePath) const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalModuleCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total number of modules loaded so far, official or not. -// Arguments: None. -// Return value: The number of modules loaded so far, both official and non. - - int GetTotalModuleCount() { return m_pDataModules.size(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOfficialModuleCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total number of OFFICIAL modules loaded so far. -// Arguments: None. -// Return value: The number of official modules loaded so far. - - int GetOfficialModuleCount() { return m_OfficialModuleCount; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddEntityPreset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an Entity instance's pointer and name associations to the -// internal list of already read in Entity:s. Ownership is NOT transferred! -// If there already is an instance defined, nothing happens. If there -// is not, a clone is made of the passed-in Entity and added to the library. -// Arguments: A pointer to the Entity derived instance to add. It should be created -// from a Reader. Ownership is NOT transferred! -// Which module to add the entity to. -// Whether to overwrite if an instance of the EXACT same TYPE and name -// was found. If one of the same name but not the exact type, false -// is returned regardless and nothing will have been added. -// The file this instance was read from, or where it should be written. -// If "Same" is passed as the file path read from, an overwritten instance -// will keep the old one's file location entry. -// Return value: Whether or not a copy of the passed-in instance was successfully inserted -// into the module. False will be returned if there already was an instance -// of that class and instance name inserted previously, unless overwritten. - - bool AddEntityPreset(Entity *pEntToAdd, int whichModule = 0, bool overwriteSame = false, std::string readFromFile = "Same"); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEntityPreset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a previously read in (defined) Entity, by type and instance name. -// Arguments: The type name of the derived Entity. Ownership is NOT transferred! -// The instance name of the derived Entity instance. -// Which module to try to get the entity from. If it's not found there, -// the official modules will be searched also. -1 means search ALL modules! -// Return value: A pointer to the requested Entity instance. 0 if no Entity with that -// derived type or instance name was found. Ownership is NOT transferred! - - const Entity * GetEntityPreset(std::string type, std::string preset, int whichModule = -1); - // Helper for passing in string module name instead of ID - const Entity * GetEntityPreset(std::string type, std::string preset, std::string module) { return GetEntityPreset(type, preset, GetModuleID(module)); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEntityPreset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads an instance of an Entity that will be used as preset -// to later on be used to generate more instances of the same state. -// Will check if there was already one of the same class and instance -// name read in previously, and will return that one if found, or -// add the newly read in one to this PresetMan's list if not found to -// exist there previously. Ownership is NOT transferred! -// Arguments: The Reader which is about to read in an instance reference. It'll make -// this look in the same module as it's reading from. -// Return value: A const pointer to the Entity instance read in. 0 if there was an -// error, or the instance name was 'None'. Ownership is NOT transferred! - - const Entity * GetEntityPreset(Reader &reader); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReadReflectedPreset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a preset of an Entity and tries to add it to the list of -// read-in instances. Regardless of whether there is a name collision, -// the read-in preset will be returned, ownership TRANSFERRED! -// Arguments: The Reader which is about to read in a preset. -// Return value: A pointer to the Entity preset read in. 0 if there was an -// error, or the instance name was 'None'. Ownership IS transferred! - - Entity * ReadReflectedPreset(Reader &reader); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAllOfType -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds to a list all previously read in (defined) Entitys, by type. -// Arguments: Reference to a list which will get all matching Entity:s added to it. -// Ownership of the list or the Entitys placed in it are NOT transferred! -// The type name of the Entitys you want. -// Whether to only get those of one specific DataModule (0-n), or all (-1). -// Return value: Whether any Entity:s were found and added to the list. - - bool GetAllOfType(std::list &entityList, std::string type, int whichModule = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAllOfTypeInModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds to a list all previously read in (defined) Entitys which are -// of a specific type, and only exist in a specific module space. -// Arguments: Reference to a list which will get all matching Entity:s added to it. -// Ownership of the list or the Entitys placed in it are NOT transferred! -// The type name of the Entitys you want. -// Which module to get the instances for, in addition to all groups in -// official modules loaded earlier than the one specified here. -1 means -// get ALL groups ever reg'd. -// Return value: Whether any Entity:s were found and added to the list. - - bool GetAllOfTypeInModuleSpace(std::list &entityList, std::string type, int whichModuleSpace); - - - /// - /// Adds to a list all previously read in (defined) Entities which are associated with a specific group. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The group to look for. "All" will look in all. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether to only get those of one specific DataModule (0-n), or all (-1). - /// Whether any Entities were found and added to the list. - bool GetAllOfGroup(std::list &entityList, const std::string &group, const std::string &type = "All", int whichModule = -1) { return GetAllOfGroups(entityList, { group }, type, whichModule); } - - /// - /// Adds to a list all previously read in (defined) Entities which are associated with several specific groups. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The groups to look for. "All" will look in all. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether to only get those of one specific DataModule (0-n), or all (-1). - /// Whether any Entities were found and added to the list. - bool GetAllOfGroups(std::list &entityList, const std::vector &groups, const std::string &type = "All", int whichModule = -1); - - /// - /// Adds to a list all previously read in (defined) Entities which are not associated with a specific group. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The group to exclude. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether to only get those of one specific DataModule (0-n), or all (-1). - /// Whether any Entities were found and added to the list. - bool GetAllNotOfGroup(std::list &entityList, const std::string &group, const std::string &type = "All", int whichModule = -1) { return GetAllNotOfGroups(entityList, { group }, type, whichModule); } - - /// - /// Adds to a list all previously read in (defined) Entities which are not associated with several specific groups. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The groups to exclude. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether to only get those of one specific DataModule (0-n), or all (-1). - /// Whether any Entities were found and added to the list. - bool GetAllNotOfGroups(std::list &entityList, const std::vector &groups, const std::string &type = "All", int whichModule = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRandomOfGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a previously read in (defined) Entity which is randomly -// selected from a specific group. -// Arguments: The group to randomly select an Entity from. "All" will look in all. -// The name of the least common denominator type of the Entitys you want. -// "All" will look at all types. -// Whether to only get those of one specific DataModule (0-n), or all (-1). -// Return value: The Entity preset that was randomly selected. Ownership is NOT transferred! - - Entity * GetRandomOfGroup(std::string group, std::string type = "All", int whichModule = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRandomOfGroupFromTech -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a previously read in (defined) Entity which is randomly -// selected from a specific group only if it belongs to some tech. -// Arguments: The group to randomly select an Entity from. "All" will look in all. -// The name of the least common denominator type of the Entitys you want. -// "All" will look at all types. -// Whether to only get those of one specific DataModule (0-n), or all (-1) -// or all modules uncluding non-tech ones. -// Return value: The Entity preset that was randomly selected. Ownership is NOT transferred! - - Entity * GetRandomBuyableOfGroupFromTech(std::string group, std::string type = "All", int whichModule = -1); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAllOfGroupInModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds to a list all previously read in (defined) Entitys which are -// associated with a specific group, and only exist in a specific module -// space. -// Arguments: Reference to a list which will get all matching Entity:s added to it. -// Ownership of the list or the Entitys placed in it are NOT transferred! -// The group to look for. "All" will look in all. -// The name of the least common denominator type of the Entitys you want. -// "All" will look at all types. -// Which module to get the instances for, in addition to all groups in -// official modules loaded earlier than the one specified here. -1 means -// get ALL groups ever reg'd. -// Return value: Whether any Entity:s were found and added to the list. - - bool GetAllOfGroupInModuleSpace(std::list &entityList, std::string group, std::string type, int whichModuleSpace); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRandomOfGroupInModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a previously read in (defined) Entity which is associated with -// a specific group, randomly selected and only exist in a specific module -// space. -// Arguments: Ownership of the list or the Entitys placed in it are NOT transferred! -// The group to randomly select from. "All" will look in all. -// The name of the least common denominator type of the Entity:s you want. -// "All" will look at all types. -// Which module to get the instances for, in addition to all groups in -// official modules loaded earlier than the one specified here. -1 means -// get ALL groups ever reg'd. -// Return value: The randomly select preset, if any was found with thse search params. -// Ownership is NOT transferred! - - Entity * GetRandomOfGroupInModuleSpace(std::string group, std::string type, int whichModuleSpace); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEntityDataLocation -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the data file path of a previously read in (defined) Entity. -// Arguments: The type name of the derived Entity. Ownership is NOT transferred! -// The preset name of the derived Entity preset. -// Which module to try to get the entity from. If it's not found there, -// the official modules will be searched also. -// Return value: The file path of the data file that the specified Entity was read from. -// If no Entity of that description was found, "" is returned. - - std::string GetEntityDataLocation(std::string type, std::string preset, int whichModule); - - - /// - /// Reloads all scripted Entity Presets with the latest version of their respective script files. - /// - void ReloadAllScripts() const; - - /// - /// Reloads an Entity preset and all related presets with the latest version of their respective files. - /// Stores the passed in Entity preset info for later re-use in PresetMan::QuickReloadEntityPreset. - /// - /// The name of the preset to reload. - /// The type of the preset to reload, to avoid any ambiguity. - /// The DataModule the preset to reload is defined in. - /// Whether or not to store the reloaded entity preset data for quick reloading. - /// Whether reloading the preset was successful. - bool ReloadEntityPreset(const std::string &presetName, const std::string &className, const std::string &dataModule, bool storeReloadedPresetDataForQuickReloading = true); - - /// - /// Reloads the previously reloaded Entity preset and all related presets with the latest version of their respective files. - /// - /// Whether reloading the preset was successful. - bool QuickReloadEntityPreset(); - - /// - /// Gets whether or not ReloadEntityPreset was called this update. - /// - /// Whether or not ReloadEntityPreset was called this update. - bool GetReloadEntityPresetCalledThisUpdate() const { return m_ReloadEntityPresetCalledThisUpdate; } - - /// - /// Resets whether or not ReloadEntityPreset was called this update. - /// - void ClearReloadEntityPresetCalledThisUpdate() { m_ReloadEntityPresetCalledThisUpdate = false; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddMaterialMapping -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a Material mapping local to a DataModule. This is used for when -// multiple DataModule:s are loading conflicting Material:s, and need to -// resolve the conflicts by mapping their materials to ID's different than -// those specified in the data files. -// Arguments: The material ID to map from. -// The material ID to map to. -// The ID of the DataModule we are making the mapping for. This should be -// a non-official module as mapping shouldn't be needed in the official -// modules. -// Return value: Whether this created a new mapping which didn't override a previous -// material mapping. - - bool AddMaterialMapping(int fromID, int toID, int whichModule); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RegisterGroup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Registers the existence of an Entity group, and in which module. -// Arguments: The group to register. -// The ID of the module in which at least one entity of this group can be -// found. -// global register. -// Return value: None. - - void RegisterGroup(std::string newGroup, int whichModule); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGroups -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Fills out a list with all groups registered in a specific module. -// Arguments: The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! -// Which module to get the groups for. -1 means get ALL groups ever reg'd. -// Pass a type name here and only groups with entitys of that type will be -// be included. "All" means don't consider what types are in the groups. -// Return value: Whether any groups were found and thus added to the list. - - bool GetGroups(std::list &groupList, int whichModule = -1, std::string withType = "All") const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetModuleSpaceGroups -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Fills out a list with all groups registered in all official modules, -// PLUS a specific non-official module as well. -// Arguments: The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! -// Which module to get the groups for, in addition to all groups in -// official modules loaded earlier than the one specified here. -1 means -// get ALL groups ever reg'd. -// Pass a type name here and only groups with entitys of that type will be -// be included. "All" means don't consider what types are in the groups. -// Return value: Whether any groups were found and thus added to the list. - - bool GetModuleSpaceGroups(std::list &groupList, int whichModule, std::string withType = "All") const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLoadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns actor defined in the specified loadout. -// Arguments: Loadout preset name, module name, whether or not spawn delivery craft defined for that loadout -// Return value: Created actor if matching loadout was found or 0. OWNERSHIP IS TRANSFERED. - - Actor * GetLoadout(std::string loadoutName, std::string module, bool spawnDropShip); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLoadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates and returns actor defined in the specified loadout. -// Arguments: Loadout preset name, module id, whether or not spawn delivery craft defined for that loadout -// Return value: Created actor if matching loadout was found or 0. OWNERSHIP IS TRANSFERED. - - Actor * GetLoadout(std::string loadoutName, int moduleNumber, bool spawnDropShip); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Owned and loaded DataModule:s - std::vector m_pDataModules; - - // Names of all DataModule:s mapped to indices into the m_pDataModules vector. - // The names are all lowercase name so we can more easily find them in case-agnostic fashion - std::map m_DataModuleIDs; - - // How many modules are 'official' and shipped with the game, and guaranteed to not have name conflicts among them - // All official modules are in the beginning of the m_TypeMap, so this count shows how many into that vector they represent - int m_OfficialModuleCount; - - std::string m_SingleModuleToLoad; //!< Name of the single module to load after the official modules. - - // List of all Entity groups ever registered, all uniques - // This is just a handy total of all the groups registered in all the individual DataModule:s - std::list m_TotalGroupRegister; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - static const std::array c_OfficialModules; // Array storing the names of all the official modules. - static const std::array, 3> c_UserdataModules; // Array storing the names of all the userdata modules. - - std::array m_LastReloadedEntityPresetInfo; //!< Array storing the last reloaded Entity preset info (ClassName, PresetName and DataModule). Used for quick reloading via key combination. - bool m_ReloadEntityPresetCalledThisUpdate; //!< A flag for whether or not ReloadEntityPreset was called this update. - - /// - /// Iterates through the working directory to find any files matching the zipped module package extension (.rte.zip) and proceeds to extract them. - /// - void FindAndExtractZippedModules() const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this PresetMan, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - PresetMan(const PresetMan &reference) = delete; - PresetMan & operator=(const PresetMan &rhs) = delete; - -}; +namespace RTE { + + class Actor; + class DataModule; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: PresetMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The singleton manager of all presets of Entity:s in the RTE. + // The presets serve as a respository of Entity instances with specific + // and unique and initial runtime data. + // Parent(s): Singleton, Serializable. + // Class history: 12/25/2001 PresetMan created. + // Class history: 05/30/2008 Changed name to PresetMan. + + class PresetMan : public Singleton { + friend struct ManagerLuaBindings; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: PresetMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a PresetMan entity in system + // memory. Create() should be called before using the entity. + // Arguments: None. + + PresetMan() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~PresetMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a PresetMan entity before deletion + // from system memory. + // Arguments: None. + + ~PresetMan() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the PresetMan entity ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Initialize() { return 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire PresetMan, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the PresetMan entity. + // Arguments: None. + // Return value: None. + + void Destroy(); + + /// + /// Reads an entire DataModule and adds it to this. NOTE that official modules can't be loaded after any non-official ones! + /// + /// The module name to read, e.g. "Base.rte". + /// Whether this module is 'official' or third party. If official, it has to not have any name conflicts with any other official module. + /// Whether this module is a userdata module. If true, will be treated as an unofficial module. + /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. + /// Whether the DataModule was read and added correctly. + bool LoadDataModule(const std::string& moduleName, bool official, bool userdata = false, const ProgressCallback& progressCallback = nullptr); + + /// + /// Reads an entire DataModule and adds it to this. NOTE that official modules can't be loaded after any non-official ones! + /// + /// The module name to read, e.g. "Base.rte". + /// Whether the DataModule was read and added correctly. + bool LoadDataModule(const std::string& moduleName) { return LoadDataModule(moduleName, false); } + + /// + /// Loads all the official data modules individually with LoadDataModule, then proceeds to look for any non-official modules and loads them as well. + /// + /// + bool LoadAllDataModules(); + + /// + /// Sets the single module to be loaded after the official modules. This will be the ONLY non-official module to be loaded. + /// + /// Name of the module to load. + void SetSingleModuleToLoad(std::string moduleName) { m_SingleModuleToLoad = moduleName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDataModule + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a specific loaded DataModule + // Arguments: The ID of the module to get. + // Return value: The requested DataModule. Ownership is NOT transferred! + + const DataModule* GetDataModule(int whichModule = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDataModuleName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a name specific loaded DataModule + // Arguments: The ID of the module to get. + // Return value: The requested DataModule name. + + const std::string GetDataModuleName(int whichModule = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetModuleID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the ID of a loaded DataModule. + // Arguments: The name of the DataModule to get the ID from, including the ".rte" + // Return value: The requested ID. If no module of the name was found, -1 will be returned. + + int GetModuleID(std::string moduleName); + + /// + /// Gets the Name of a loaded DataModule, from a full data file path. + /// + /// The full path to a data file inside the data module id you want to get. + /// The requested Name. If no module of the name was found, "" will be returned. + std::string GetModuleNameFromPath(const std::string& dataPath) const; + + /// + /// Gets the ID of a loaded DataModule from a full data file path. + /// + /// The full path to a data file inside the data module ID you want to get. + /// The requested ID. If no module of the name was found, -1 will be returned. + int GetModuleIDFromPath(const std::string& dataPath); + + /// + /// Returns whether or not the module is vanilla. + /// + /// The name of the module to check, in the form "[moduleName].rte" + /// True if the module is an official data module, otherwise false. + bool IsModuleOfficial(const std::string& moduleName) const; + + /// + /// Returns whether or not the module is vanilla. + /// + /// The name of the module to check, in the form "[moduleName].rte" + /// True if the module is a listed user data module, otherwise false. + bool IsModuleUserdata(const std::string& moduleName) const; + + /// + /// Returns the Full path to the module including Data/, Userdata/ or Mods/. + /// + /// The Path to be completed. + /// The complete path to the file, including Data/, Userdata/ or Mods/ based on whether or not it's part of an official module or userdata. + std::string GetFullModulePath(const std::string& modulePath) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalModuleCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total number of modules loaded so far, official or not. + // Arguments: None. + // Return value: The number of modules loaded so far, both official and non. + + int GetTotalModuleCount() { return m_pDataModules.size(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOfficialModuleCount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total number of OFFICIAL modules loaded so far. + // Arguments: None. + // Return value: The number of official modules loaded so far. + + int GetOfficialModuleCount() { return m_OfficialModuleCount; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddEntityPreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an Entity instance's pointer and name associations to the + // internal list of already read in Entity:s. Ownership is NOT transferred! + // If there already is an instance defined, nothing happens. If there + // is not, a clone is made of the passed-in Entity and added to the library. + // Arguments: A pointer to the Entity derived instance to add. It should be created + // from a Reader. Ownership is NOT transferred! + // Which module to add the entity to. + // Whether to overwrite if an instance of the EXACT same TYPE and name + // was found. If one of the same name but not the exact type, false + // is returned regardless and nothing will have been added. + // The file this instance was read from, or where it should be written. + // If "Same" is passed as the file path read from, an overwritten instance + // will keep the old one's file location entry. + // Return value: Whether or not a copy of the passed-in instance was successfully inserted + // into the module. False will be returned if there already was an instance + // of that class and instance name inserted previously, unless overwritten. + + bool AddEntityPreset(Entity* pEntToAdd, int whichModule = 0, bool overwriteSame = false, std::string readFromFile = "Same"); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEntityPreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a previously read in (defined) Entity, by type and instance name. + // Arguments: The type name of the derived Entity. Ownership is NOT transferred! + // The instance name of the derived Entity instance. + // Which module to try to get the entity from. If it's not found there, + // the official modules will be searched also. -1 means search ALL modules! + // Return value: A pointer to the requested Entity instance. 0 if no Entity with that + // derived type or instance name was found. Ownership is NOT transferred! + + const Entity* GetEntityPreset(std::string type, std::string preset, int whichModule = -1); + // Helper for passing in string module name instead of ID + const Entity* GetEntityPreset(std::string type, std::string preset, std::string module) { return GetEntityPreset(type, preset, GetModuleID(module)); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEntityPreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads an instance of an Entity that will be used as preset + // to later on be used to generate more instances of the same state. + // Will check if there was already one of the same class and instance + // name read in previously, and will return that one if found, or + // add the newly read in one to this PresetMan's list if not found to + // exist there previously. Ownership is NOT transferred! + // Arguments: The Reader which is about to read in an instance reference. It'll make + // this look in the same module as it's reading from. + // Return value: A const pointer to the Entity instance read in. 0 if there was an + // error, or the instance name was 'None'. Ownership is NOT transferred! + + const Entity* GetEntityPreset(Reader& reader); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ReadReflectedPreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reads a preset of an Entity and tries to add it to the list of + // read-in instances. Regardless of whether there is a name collision, + // the read-in preset will be returned, ownership TRANSFERRED! + // Arguments: The Reader which is about to read in a preset. + // Return value: A pointer to the Entity preset read in. 0 if there was an + // error, or the instance name was 'None'. Ownership IS transferred! + + Entity* ReadReflectedPreset(Reader& reader); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAllOfType + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds to a list all previously read in (defined) Entitys, by type. + // Arguments: Reference to a list which will get all matching Entity:s added to it. + // Ownership of the list or the Entitys placed in it are NOT transferred! + // The type name of the Entitys you want. + // Whether to only get those of one specific DataModule (0-n), or all (-1). + // Return value: Whether any Entity:s were found and added to the list. + + bool GetAllOfType(std::list& entityList, std::string type, int whichModule = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAllOfTypeInModuleSpace + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds to a list all previously read in (defined) Entitys which are + // of a specific type, and only exist in a specific module space. + // Arguments: Reference to a list which will get all matching Entity:s added to it. + // Ownership of the list or the Entitys placed in it are NOT transferred! + // The type name of the Entitys you want. + // Which module to get the instances for, in addition to all groups in + // official modules loaded earlier than the one specified here. -1 means + // get ALL groups ever reg'd. + // Return value: Whether any Entity:s were found and added to the list. + + bool GetAllOfTypeInModuleSpace(std::list& entityList, std::string type, int whichModuleSpace); + + /// + /// Adds to a list all previously read in (defined) Entities which are associated with a specific group. + /// + /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// The group to look for. "All" will look in all. + /// The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// Whether to only get those of one specific DataModule (0-n), or all (-1). + /// Whether any Entities were found and added to the list. + bool GetAllOfGroup(std::list& entityList, const std::string& group, const std::string& type = "All", int whichModule = -1) { return GetAllOfGroups(entityList, {group}, type, whichModule); } + + /// + /// Adds to a list all previously read in (defined) Entities which are associated with several specific groups. + /// + /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// The groups to look for. "All" will look in all. + /// The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// Whether to only get those of one specific DataModule (0-n), or all (-1). + /// Whether any Entities were found and added to the list. + bool GetAllOfGroups(std::list& entityList, const std::vector& groups, const std::string& type = "All", int whichModule = -1); + + /// + /// Adds to a list all previously read in (defined) Entities which are not associated with a specific group. + /// + /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// The group to exclude. + /// The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// Whether to only get those of one specific DataModule (0-n), or all (-1). + /// Whether any Entities were found and added to the list. + bool GetAllNotOfGroup(std::list& entityList, const std::string& group, const std::string& type = "All", int whichModule = -1) { return GetAllNotOfGroups(entityList, {group}, type, whichModule); } + + /// + /// Adds to a list all previously read in (defined) Entities which are not associated with several specific groups. + /// + /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// The groups to exclude. + /// The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// Whether to only get those of one specific DataModule (0-n), or all (-1). + /// Whether any Entities were found and added to the list. + bool GetAllNotOfGroups(std::list& entityList, const std::vector& groups, const std::string& type = "All", int whichModule = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRandomOfGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a previously read in (defined) Entity which is randomly + // selected from a specific group. + // Arguments: The group to randomly select an Entity from. "All" will look in all. + // The name of the least common denominator type of the Entitys you want. + // "All" will look at all types. + // Whether to only get those of one specific DataModule (0-n), or all (-1). + // Return value: The Entity preset that was randomly selected. Ownership is NOT transferred! + + Entity* GetRandomOfGroup(std::string group, std::string type = "All", int whichModule = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRandomOfGroupFromTech + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a previously read in (defined) Entity which is randomly + // selected from a specific group only if it belongs to some tech. + // Arguments: The group to randomly select an Entity from. "All" will look in all. + // The name of the least common denominator type of the Entitys you want. + // "All" will look at all types. + // Whether to only get those of one specific DataModule (0-n), or all (-1) + // or all modules uncluding non-tech ones. + // Return value: The Entity preset that was randomly selected. Ownership is NOT transferred! + + Entity* GetRandomBuyableOfGroupFromTech(std::string group, std::string type = "All", int whichModule = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetAllOfGroupInModuleSpace + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds to a list all previously read in (defined) Entitys which are + // associated with a specific group, and only exist in a specific module + // space. + // Arguments: Reference to a list which will get all matching Entity:s added to it. + // Ownership of the list or the Entitys placed in it are NOT transferred! + // The group to look for. "All" will look in all. + // The name of the least common denominator type of the Entitys you want. + // "All" will look at all types. + // Which module to get the instances for, in addition to all groups in + // official modules loaded earlier than the one specified here. -1 means + // get ALL groups ever reg'd. + // Return value: Whether any Entity:s were found and added to the list. + + bool GetAllOfGroupInModuleSpace(std::list& entityList, std::string group, std::string type, int whichModuleSpace); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRandomOfGroupInModuleSpace + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a previously read in (defined) Entity which is associated with + // a specific group, randomly selected and only exist in a specific module + // space. + // Arguments: Ownership of the list or the Entitys placed in it are NOT transferred! + // The group to randomly select from. "All" will look in all. + // The name of the least common denominator type of the Entity:s you want. + // "All" will look at all types. + // Which module to get the instances for, in addition to all groups in + // official modules loaded earlier than the one specified here. -1 means + // get ALL groups ever reg'd. + // Return value: The randomly select preset, if any was found with thse search params. + // Ownership is NOT transferred! + + Entity* GetRandomOfGroupInModuleSpace(std::string group, std::string type, int whichModuleSpace); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEntityDataLocation + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the data file path of a previously read in (defined) Entity. + // Arguments: The type name of the derived Entity. Ownership is NOT transferred! + // The preset name of the derived Entity preset. + // Which module to try to get the entity from. If it's not found there, + // the official modules will be searched also. + // Return value: The file path of the data file that the specified Entity was read from. + // If no Entity of that description was found, "" is returned. + + std::string GetEntityDataLocation(std::string type, std::string preset, int whichModule); + + /// + /// Reloads all scripted Entity Presets with the latest version of their respective script files. + /// + void ReloadAllScripts() const; + + /// + /// Reloads an Entity preset and all related presets with the latest version of their respective files. + /// Stores the passed in Entity preset info for later re-use in PresetMan::QuickReloadEntityPreset. + /// + /// The name of the preset to reload. + /// The type of the preset to reload, to avoid any ambiguity. + /// The DataModule the preset to reload is defined in. + /// Whether or not to store the reloaded entity preset data for quick reloading. + /// Whether reloading the preset was successful. + bool ReloadEntityPreset(const std::string& presetName, const std::string& className, const std::string& dataModule, bool storeReloadedPresetDataForQuickReloading = true); + + /// + /// Reloads the previously reloaded Entity preset and all related presets with the latest version of their respective files. + /// + /// Whether reloading the preset was successful. + bool QuickReloadEntityPreset(); + + /// + /// Gets whether or not ReloadEntityPreset was called this update. + /// + /// Whether or not ReloadEntityPreset was called this update. + bool GetReloadEntityPresetCalledThisUpdate() const { return m_ReloadEntityPresetCalledThisUpdate; } + + /// + /// Resets whether or not ReloadEntityPreset was called this update. + /// + void ClearReloadEntityPresetCalledThisUpdate() { m_ReloadEntityPresetCalledThisUpdate = false; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddMaterialMapping + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a Material mapping local to a DataModule. This is used for when + // multiple DataModule:s are loading conflicting Material:s, and need to + // resolve the conflicts by mapping their materials to ID's different than + // those specified in the data files. + // Arguments: The material ID to map from. + // The material ID to map to. + // The ID of the DataModule we are making the mapping for. This should be + // a non-official module as mapping shouldn't be needed in the official + // modules. + // Return value: Whether this created a new mapping which didn't override a previous + // material mapping. + + bool AddMaterialMapping(int fromID, int toID, int whichModule); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RegisterGroup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Registers the existence of an Entity group, and in which module. + // Arguments: The group to register. + // The ID of the module in which at least one entity of this group can be + // found. + // global register. + // Return value: None. + + void RegisterGroup(std::string newGroup, int whichModule); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGroups + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Fills out a list with all groups registered in a specific module. + // Arguments: The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! + // Which module to get the groups for. -1 means get ALL groups ever reg'd. + // Pass a type name here and only groups with entitys of that type will be + // be included. "All" means don't consider what types are in the groups. + // Return value: Whether any groups were found and thus added to the list. + + bool GetGroups(std::list& groupList, int whichModule = -1, std::string withType = "All") const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetModuleSpaceGroups + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Fills out a list with all groups registered in all official modules, + // PLUS a specific non-official module as well. + // Arguments: The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! + // Which module to get the groups for, in addition to all groups in + // official modules loaded earlier than the one specified here. -1 means + // get ALL groups ever reg'd. + // Pass a type name here and only groups with entitys of that type will be + // be included. "All" means don't consider what types are in the groups. + // Return value: Whether any groups were found and thus added to the list. + + bool GetModuleSpaceGroups(std::list& groupList, int whichModule, std::string withType = "All") const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLoadout + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns actor defined in the specified loadout. + // Arguments: Loadout preset name, module name, whether or not spawn delivery craft defined for that loadout + // Return value: Created actor if matching loadout was found or 0. OWNERSHIP IS TRANSFERED. + + Actor* GetLoadout(std::string loadoutName, std::string module, bool spawnDropShip); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLoadout + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates and returns actor defined in the specified loadout. + // Arguments: Loadout preset name, module id, whether or not spawn delivery craft defined for that loadout + // Return value: Created actor if matching loadout was found or 0. OWNERSHIP IS TRANSFERED. + + Actor* GetLoadout(std::string loadoutName, int moduleNumber, bool spawnDropShip); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + // Owned and loaded DataModule:s + std::vector m_pDataModules; + + // Names of all DataModule:s mapped to indices into the m_pDataModules vector. + // The names are all lowercase name so we can more easily find them in case-agnostic fashion + std::map m_DataModuleIDs; + + // How many modules are 'official' and shipped with the game, and guaranteed to not have name conflicts among them + // All official modules are in the beginning of the m_TypeMap, so this count shows how many into that vector they represent + int m_OfficialModuleCount; + + std::string m_SingleModuleToLoad; //!< Name of the single module to load after the official modules. + + // List of all Entity groups ever registered, all uniques + // This is just a handy total of all the groups registered in all the individual DataModule:s + std::list m_TotalGroupRegister; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + static const std::array c_OfficialModules; // Array storing the names of all the official modules. + static const std::array, 3> c_UserdataModules; // Array storing the names of all the userdata modules. + + std::array m_LastReloadedEntityPresetInfo; //!< Array storing the last reloaded Entity preset info (ClassName, PresetName and DataModule). Used for quick reloading via key combination. + bool m_ReloadEntityPresetCalledThisUpdate; //!< A flag for whether or not ReloadEntityPreset was called this update. + + /// + /// Iterates through the working directory to find any files matching the zipped module package extension (.rte.zip) and proceeds to extract them. + /// + void FindAndExtractZippedModules() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this PresetMan, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + PresetMan(const PresetMan& reference) = delete; + PresetMan& operator=(const PresetMan& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Managers/PrimitiveMan.cpp b/Source/Managers/PrimitiveMan.cpp index 6fe5b3f69b..5df7c5491f 100644 --- a/Source/Managers/PrimitiveMan.cpp +++ b/Source/Managers/PrimitiveMan.cpp @@ -9,55 +9,55 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::unique_ptr PrimitiveMan::MakeUniqueOfAppropriateTypeFromPrimitiveRawPtr(GraphicalPrimitive *primitive) { + std::unique_ptr PrimitiveMan::MakeUniqueOfAppropriateTypeFromPrimitiveRawPtr(GraphicalPrimitive* primitive) { switch (primitive->GetPrimitiveType()) { case GraphicalPrimitive::PrimitiveType::Line: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::Arc: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::Spline: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::Box: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::BoxFill: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::RoundedBox: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::RoundedBoxFill: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::Circle: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::CircleFill: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::Ellipse: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::EllipseFill: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::Triangle: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::TriangleFill: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::Text: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); case GraphicalPrimitive::PrimitiveType::Bitmap: - return std::unique_ptr(static_cast(primitive)); + return std::unique_ptr(static_cast(primitive)); default: return nullptr; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PrimitiveMan::SchedulePrimitive(std::unique_ptr&& primitive) { std::lock_guard lock(m_Mutex); m_ScheduledPrimitives.emplace_back(std::move(primitive)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::SchedulePrimitivesForBlendedDrawing(DrawBlendMode blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const std::vector &primitives) { + void PrimitiveMan::SchedulePrimitivesForBlendedDrawing(DrawBlendMode blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const std::vector& primitives) { if (blendMode < DrawBlendMode::NoBlend || blendMode >= DrawBlendMode::BlendModeCount) { g_ConsoleMan.PrintString("ERROR: Encountered invalid blending mode when attempting to draw primitives! Drawing will be skipped! See the DrawBlendMode enumeration for valid modes."); return; @@ -67,16 +67,16 @@ namespace RTE { blendAmountB = std::clamp(blendAmountB, static_cast(BlendAmountLimits::MinBlend), static_cast(BlendAmountLimits::MaxBlend)); blendAmountA = std::clamp(blendAmountA, static_cast(BlendAmountLimits::MinBlend), static_cast(BlendAmountLimits::MaxBlend)); - for (GraphicalPrimitive *primitive : primitives) { + for (GraphicalPrimitive* primitive: primitives) { primitive->m_BlendMode = blendMode; - primitive->m_ColorChannelBlendAmounts = { blendAmountR, blendAmountG, blendAmountB, blendAmountA }; + primitive->m_ColorChannelBlendAmounts = {blendAmountR, blendAmountG, blendAmountB, blendAmountA}; SchedulePrimitive(MakeUniqueOfAppropriateTypeFromPrimitiveRawPtr(primitive)); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawLinePrimitive(int player, const Vector &startPos, const Vector &endPos, unsigned char color, int thickness) { + void PrimitiveMan::DrawLinePrimitive(int player, const Vector& startPos, const Vector& endPos, unsigned char color, int thickness) { if (thickness > 1) { Vector dirVector = g_SceneMan.ShortestDistance(startPos, endPos, g_SceneMan.SceneWrapsX()).SetMagnitude(static_cast(thickness - 1) / 2.0F).Perpendicularize(); Vector pointA = startPos + dirVector; @@ -91,165 +91,165 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawArcPrimitive(const Vector ¢erPos, float startAngle, float endAngle, int radius, unsigned char color) { + void PrimitiveMan::DrawArcPrimitive(const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, startAngle, endAngle, radius, 1, color)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawArcPrimitive(const Vector ¢erPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness) { + void PrimitiveMan::DrawArcPrimitive(const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness) { SchedulePrimitive(std::make_unique(-1, centerPos, startAngle, endAngle, radius, thickness, color)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawArcPrimitive(int player, const Vector ¢erPos, float startAngle, float endAngle, int radius, unsigned char color) { + void PrimitiveMan::DrawArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, startAngle, endAngle, radius, 1, color)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawArcPrimitive(int player, const Vector ¢erPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness) { + void PrimitiveMan::DrawArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness) { SchedulePrimitive(std::make_unique(player, centerPos, startAngle, endAngle, radius, thickness, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawSplinePrimitive(const Vector &startPos, const Vector &guideA, const Vector &guideB, const Vector &endPos, unsigned char color) { + void PrimitiveMan::DrawSplinePrimitive(const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color) { SchedulePrimitive(std::make_unique(-1, startPos, guideA, guideB, endPos, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawSplinePrimitive(int player, const Vector &startPos, const Vector &guideA, const Vector &guideB, const Vector &endPos, unsigned char color) { + void PrimitiveMan::DrawSplinePrimitive(int player, const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color) { SchedulePrimitive(std::make_unique(player, startPos, guideA, guideB, endPos, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBoxPrimitive(const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color) { + void PrimitiveMan::DrawBoxPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { SchedulePrimitive(std::make_unique(-1, topLeftPos, bottomRightPos, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBoxPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color) { + void PrimitiveMan::DrawBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { SchedulePrimitive(std::make_unique(player, topLeftPos, bottomRightPos, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBoxFillPrimitive(const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color) { + void PrimitiveMan::DrawBoxFillPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { SchedulePrimitive(std::make_unique(-1, topLeftPos, bottomRightPos, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBoxFillPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color) { + void PrimitiveMan::DrawBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { SchedulePrimitive(std::make_unique(player, topLeftPos, bottomRightPos, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawRoundedBoxPrimitive(const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color) { + void PrimitiveMan::DrawRoundedBoxPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, topLeftPos, bottomRightPos, cornerRadius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawRoundedBoxPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color) { + void PrimitiveMan::DrawRoundedBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) { SchedulePrimitive(std::make_unique(player, topLeftPos, bottomRightPos, cornerRadius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawRoundedBoxFillPrimitive(const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color) { + void PrimitiveMan::DrawRoundedBoxFillPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, topLeftPos, bottomRightPos, cornerRadius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawRoundedBoxFillPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color) { + void PrimitiveMan::DrawRoundedBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) { SchedulePrimitive(std::make_unique(player, topLeftPos, bottomRightPos, cornerRadius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawCirclePrimitive(const Vector ¢erPos, int radius, unsigned char color) { + void PrimitiveMan::DrawCirclePrimitive(const Vector& centerPos, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, radius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawCirclePrimitive(int player, const Vector ¢erPos, int radius, unsigned char color) { + void PrimitiveMan::DrawCirclePrimitive(int player, const Vector& centerPos, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, radius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawCircleFillPrimitive(const Vector ¢erPos, int radius, unsigned char color) { + void PrimitiveMan::DrawCircleFillPrimitive(const Vector& centerPos, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, radius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawCircleFillPrimitive(int player, const Vector ¢erPos, int radius, unsigned char color) { + void PrimitiveMan::DrawCircleFillPrimitive(int player, const Vector& centerPos, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, radius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawEllipsePrimitive(const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color) { + void PrimitiveMan::DrawEllipsePrimitive(const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, horizRadius, vertRadius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawEllipsePrimitive(int player, const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color) { + void PrimitiveMan::DrawEllipsePrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, horizRadius, vertRadius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawEllipseFillPrimitive(const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color) { + void PrimitiveMan::DrawEllipseFillPrimitive(const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, horizRadius, vertRadius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawEllipseFillPrimitive(int player, const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color) { + void PrimitiveMan::DrawEllipseFillPrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, horizRadius, vertRadius, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTrianglePrimitive(const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color) { + void PrimitiveMan::DrawTrianglePrimitive(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) { SchedulePrimitive(std::make_unique(-1, pointA, pointB, pointC, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTrianglePrimitive(int player, const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color) { + void PrimitiveMan::DrawTrianglePrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) { SchedulePrimitive(std::make_unique(player, pointA, pointB, pointC, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTriangleFillPrimitive(const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color) { + void PrimitiveMan::DrawTriangleFillPrimitive(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) { SchedulePrimitive(std::make_unique(-1, pointA, pointB, pointC, color)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTriangleFillPrimitive(int player, const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color) { + void PrimitiveMan::DrawTriangleFillPrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) { SchedulePrimitive(std::make_unique(player, pointA, pointB, pointC, color)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawPolygonOrPolygonFillPrimitive(int player, const Vector &startPos, unsigned char color, const std::vector &vertices, bool filled) { + void PrimitiveMan::DrawPolygonOrPolygonFillPrimitive(int player, const Vector& startPos, unsigned char color, const std::vector& vertices, bool filled) { if (vertices.size() < 2) { g_ConsoleMan.PrintString("ERROR: Polygon primitive should have at least 2 vertices! Drawing will be skipped!"); return; @@ -261,53 +261,53 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTextPrimitive(const Vector &start, const std::string &text, bool isSmall, int alignment) { + void PrimitiveMan::DrawTextPrimitive(const Vector& start, const std::string& text, bool isSmall, int alignment) { SchedulePrimitive(std::make_unique(-1, start, text, isSmall, alignment, 0)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTextPrimitive(const Vector &start, const std::string &text, bool isSmall, int alignment, float rotAngle) { + void PrimitiveMan::DrawTextPrimitive(const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle) { SchedulePrimitive(std::make_unique(-1, start, text, isSmall, alignment, rotAngle)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTextPrimitive(int player, const Vector &start, const std::string &text, bool isSmall, int alignment) { + void PrimitiveMan::DrawTextPrimitive(int player, const Vector& start, const std::string& text, bool isSmall, int alignment) { SchedulePrimitive(std::make_unique(player, start, text, isSmall, alignment, 0)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTextPrimitive(int player, const Vector &start, const std::string &text, bool isSmall, int alignment, float rotAngle) { + void PrimitiveMan::DrawTextPrimitive(int player, const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle) { SchedulePrimitive(std::make_unique(player, start, text, isSmall, alignment, rotAngle)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBitmapPrimitive(int player, const Vector ¢erPos, const MOSprite *moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped) { + void PrimitiveMan::DrawBitmapPrimitive(int player, const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped) { SchedulePrimitive(std::make_unique(player, centerPos, moSprite, rotAngle, frame, hFlipped, vFlipped)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBitmapPrimitive(int player, const Vector ¢erPos, const std::string &filePath, float rotAngle, bool hFlipped, bool vFlipped) { + void PrimitiveMan::DrawBitmapPrimitive(int player, const Vector& centerPos, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped) { SchedulePrimitive(std::make_unique(player, centerPos, filePath, rotAngle, hFlipped, vFlipped)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawIconPrimitive(int player, const Vector ¢erPos, Entity *entity) { - if (const MOSprite *moSprite = dynamic_cast(entity)) { - SchedulePrimitive(std::make_unique(player, centerPos, moSprite->GetGraphicalIcon(), 0, false, false)); + void PrimitiveMan::DrawIconPrimitive(int player, const Vector& centerPos, Entity* entity) { + if (const MOSprite* moSprite = dynamic_cast(entity)) { + SchedulePrimitive(std::make_unique(player, centerPos, moSprite->GetGraphicalIcon(), 0, false, false)); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawPrimitives(int player, BITMAP *targetBitmap, const Vector &targetPos) const { + void PrimitiveMan::DrawPrimitives(int player, BITMAP* targetBitmap, const Vector& targetPos) const { ZoneScoped; if (m_ScheduledPrimitives.empty()) { @@ -316,12 +316,12 @@ namespace RTE { int lastDrawMode = DRAW_MODE_SOLID; DrawBlendMode lastBlendMode = DrawBlendMode::NoBlend; - std::array lastBlendAmounts = { BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend }; + std::array lastBlendAmounts = {BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend}; - for (const std::unique_ptr &primitive : m_ScheduledPrimitives) { + for (const std::unique_ptr& primitive: m_ScheduledPrimitives) { if (int playerToDrawFor = primitive->m_Player; playerToDrawFor == player || playerToDrawFor == -1) { if (DrawBlendMode blendMode = primitive->m_BlendMode; blendMode > DrawBlendMode::NoBlend) { - if (const std::array &blendAmounts = primitive->m_ColorChannelBlendAmounts; blendMode != lastBlendMode || blendAmounts != lastBlendAmounts) { + if (const std::array& blendAmounts = primitive->m_ColorChannelBlendAmounts; blendMode != lastBlendMode || blendAmounts != lastBlendAmounts) { g_FrameMan.SetColorTable(blendMode, blendAmounts); lastBlendMode = blendMode; lastBlendAmounts = blendAmounts; @@ -343,4 +343,4 @@ namespace RTE { } drawing_mode(DRAW_MODE_SOLID, nullptr, 0, 0); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Managers/PrimitiveMan.h b/Source/Managers/PrimitiveMan.h index 16a92e8a30..a265d91df5 100644 --- a/Source/Managers/PrimitiveMan.h +++ b/Source/Managers/PrimitiveMan.h @@ -16,7 +16,6 @@ namespace RTE { class PrimitiveMan : public Singleton { public: - #pragma region Creation /// /// Constructor method used to instantiate a PrimitiveMan object in system memory. @@ -38,7 +37,7 @@ namespace RTE { /// Player to draw for. /// Bitmap to draw on. /// Position to draw. - void DrawPrimitives(int player, BITMAP *targetBitmap, const Vector &targetPos) const; + void DrawPrimitives(int player, BITMAP* targetBitmap, const Vector& targetPos) const; #pragma endregion #pragma region Primitive Draw Scheduling @@ -51,7 +50,7 @@ namespace RTE { /// The blending amount for the Blue channel. 0-100. /// The blending amount for the Alpha channel. 0-100. /// A vector of primitives to schedule drawing for. - void SchedulePrimitivesForBlendedDrawing(DrawBlendMode blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const std::vector &primitives); + void SchedulePrimitivesForBlendedDrawing(DrawBlendMode blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const std::vector& primitives); /// /// Schedule to draw a line primitive. @@ -59,7 +58,7 @@ namespace RTE { /// Start position of primitive in scene coordinates. /// End position of primitive in scene coordinates. /// Color to draw primitive with. - void DrawLinePrimitive(const Vector &startPos, const Vector &endPos, unsigned char color) { DrawLinePrimitive(-1, startPos, endPos, color, 1); } + void DrawLinePrimitive(const Vector& startPos, const Vector& endPos, unsigned char color) { DrawLinePrimitive(-1, startPos, endPos, color, 1); } /// /// Schedule to draw a line primitive with the option to change thickness. @@ -68,7 +67,7 @@ namespace RTE { /// End position of primitive in scene coordinates. /// Color to draw primitive with. /// Thickness of the line in pixels. - void DrawLinePrimitive(const Vector &startPos, const Vector &endPos, unsigned char color, int thickness) { DrawLinePrimitive(-1, startPos, endPos, color, thickness); } + void DrawLinePrimitive(const Vector& startPos, const Vector& endPos, unsigned char color, int thickness) { DrawLinePrimitive(-1, startPos, endPos, color, thickness); } /// /// Schedule to draw a line primitive visible only to a specified player. @@ -77,7 +76,7 @@ namespace RTE { /// Start position of primitive in scene coordinates. /// End position of primitive in scene coordinates. /// Color to draw primitive with. - void DrawLinePrimitive(int player, const Vector &startPos, const Vector &endPos, unsigned char color) { DrawLinePrimitive(player, startPos, endPos, color, 1); } + void DrawLinePrimitive(int player, const Vector& startPos, const Vector& endPos, unsigned char color) { DrawLinePrimitive(player, startPos, endPos, color, 1); } /// /// Schedule to draw a line primitive visible only to a specified player with the option to change thickness. @@ -87,7 +86,7 @@ namespace RTE { /// End position of primitive in scene coordinates. /// Color to draw primitive with. /// Thickness of the line in pixels. - void DrawLinePrimitive(int player, const Vector &startPos, const Vector &endPos, unsigned char color, int thickness); + void DrawLinePrimitive(int player, const Vector& startPos, const Vector& endPos, unsigned char color, int thickness); /// /// Schedule to draw an arc primitive. @@ -97,7 +96,7 @@ namespace RTE { /// The angle at which the arc drawing ends. /// Radius of the arc primitive. /// Color to draw primitive with. - void DrawArcPrimitive(const Vector ¢erPos, float startAngle, float endAngle, int radius, unsigned char color); + void DrawArcPrimitive(const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color); /// /// Schedule to draw an arc primitive with the option to change thickness. @@ -108,7 +107,7 @@ namespace RTE { /// Radius of the arc primitive. /// Color to draw primitive with. /// Thickness of the arc in pixels. - void DrawArcPrimitive(const Vector ¢erPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness); + void DrawArcPrimitive(const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness); /// /// Schedule to draw an arc primitive visible only to a specified player. @@ -119,7 +118,7 @@ namespace RTE { /// The angle at which the arc drawing ends. /// Radius of the arc primitive. /// Color to draw primitive with. - void DrawArcPrimitive(int player, const Vector ¢erPos, float startAngle, float endAngle, int radius, unsigned char color); + void DrawArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color); /// /// Schedule to draw an arc primitive visible only to a specified player with the option to change thickness. @@ -131,7 +130,7 @@ namespace RTE { /// Radius of the arc primitive. /// Color to draw primitive with. /// Thickness of the arc in pixels. - void DrawArcPrimitive(int player, const Vector ¢erPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness); + void DrawArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness); /// /// Schedule to draw a Bezier spline primitive. @@ -141,7 +140,7 @@ namespace RTE { /// The second guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. /// End position of primitive in scene coordinates. /// Color to draw primitive with. - void DrawSplinePrimitive(const Vector &startPos, const Vector &guideA, const Vector &guideB, const Vector &endPos, unsigned char color); + void DrawSplinePrimitive(const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color); /// /// Schedule to draw a Bezier spline primitive visible only to a specified player. @@ -152,7 +151,7 @@ namespace RTE { /// The second guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. /// End position of primitive in scene coordinates. /// Color to draw primitive with. - void DrawSplinePrimitive(int player, const Vector &startPos, const Vector &guideA, const Vector &guideB, const Vector &endPos, unsigned char color); + void DrawSplinePrimitive(int player, const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color); /// /// Schedule to draw a box primitive. @@ -160,7 +159,7 @@ namespace RTE { /// Start position of primitive in scene coordinates. Top left corner. /// End position of primitive in scene coordinates. Bottom right corner. /// Color to draw primitive with. - void DrawBoxPrimitive(const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color); + void DrawBoxPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color); /// /// Schedule to draw a box primitive visible only to a specified player. @@ -169,7 +168,7 @@ namespace RTE { /// Start position of primitive in scene coordinates. Top left corner. /// End position of primitive in scene coordinates. Bottom right corner. /// Color to draw primitive with. - void DrawBoxPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color); + void DrawBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color); /// /// Schedule to draw a filled box primitive. @@ -177,7 +176,7 @@ namespace RTE { /// Start position of primitive in scene coordinates. Top left corner. /// End position of primitive in scene coordinates. Bottom right corner. /// Color to draw primitive with. - void DrawBoxFillPrimitive(const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color); + void DrawBoxFillPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color); /// /// Schedule to draw a filled box primitive visible only to a specified player. @@ -186,7 +185,7 @@ namespace RTE { /// Start position of primitive in scene coordinates. Top left corner. /// End position of primitive in scene coordinates. Bottom right corner. /// Color to draw primitive with. - void DrawBoxFillPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color); + void DrawBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color); /// /// Schedule to draw a rounded box primitive. @@ -195,7 +194,7 @@ namespace RTE { /// End position of primitive in scene coordinates. Bottom right corner. /// The radius of the corners of the box. Smaller radius equals sharper corners. /// Color to draw primitive with. - void DrawRoundedBoxPrimitive(const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color); + void DrawRoundedBoxPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color); /// /// Schedule to draw a rounded box primitive visible only to a specified player. @@ -205,7 +204,7 @@ namespace RTE { /// End position of primitive in scene coordinates. Bottom right corner. /// The radius of the corners of the box. Smaller radius equals sharper corners. /// Color to draw primitive with. - void DrawRoundedBoxPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color); + void DrawRoundedBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color); /// /// Schedule to draw a filled rounded box primitive. @@ -214,7 +213,7 @@ namespace RTE { /// End position of primitive in scene coordinates. Bottom right corner. /// The radius of the corners of the box. Smaller radius equals sharper corners. /// Color to draw primitive with. - void DrawRoundedBoxFillPrimitive(const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color); + void DrawRoundedBoxFillPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color); /// /// Schedule to draw a filled rounded box primitive visible only to a specified player. @@ -224,7 +223,7 @@ namespace RTE { /// End position of primitive in scene coordinates. Bottom right corner. /// The radius of the corners of the box. Smaller radius equals sharper corners. /// Color to draw primitive with. - void DrawRoundedBoxFillPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color); + void DrawRoundedBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color); /// /// Schedule to draw a circle primitive. @@ -232,7 +231,7 @@ namespace RTE { /// Position of primitive's center in scene coordinates. /// Radius of circle primitive. /// Color to draw primitive with. - void DrawCirclePrimitive(const Vector ¢erPos, int radius, unsigned char color); + void DrawCirclePrimitive(const Vector& centerPos, int radius, unsigned char color); /// /// Schedule to draw a circle primitive visible only to a specified player. @@ -241,7 +240,7 @@ namespace RTE { /// Position of primitive's center in scene coordinates. /// Radius of circle primitive. /// Color to draw primitive with. - void DrawCirclePrimitive(int player, const Vector ¢erPos, int radius, unsigned char color); + void DrawCirclePrimitive(int player, const Vector& centerPos, int radius, unsigned char color); /// /// Schedule to draw a filled circle primitive. @@ -249,7 +248,7 @@ namespace RTE { /// Position of primitive's center in scene coordinates. /// Radius of circle primitive. /// Color to fill primitive with. - void DrawCircleFillPrimitive(const Vector ¢erPos, int radius, unsigned char color); + void DrawCircleFillPrimitive(const Vector& centerPos, int radius, unsigned char color); /// /// Schedule to draw a filled circle primitive visible only to a specified player. @@ -258,7 +257,7 @@ namespace RTE { /// Position of primitive's center in scene coordinates. /// Radius of circle primitive. /// Color to fill primitive with. - void DrawCircleFillPrimitive(int player, const Vector ¢erPos, int radius, unsigned char color); + void DrawCircleFillPrimitive(int player, const Vector& centerPos, int radius, unsigned char color); /// /// Schedule to draw an ellipse primitive. @@ -267,7 +266,7 @@ namespace RTE { /// Horizontal radius of the ellipse primitive. /// Vertical radius of the ellipse primitive. /// Color to draw primitive with. - void DrawEllipsePrimitive(const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color); + void DrawEllipsePrimitive(const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color); /// /// Schedule to draw an ellipse primitive visible only to a specified player. @@ -277,7 +276,7 @@ namespace RTE { /// Horizontal radius of the ellipse primitive. /// Vertical radius of the ellipse primitive. /// Color to draw primitive with. - void DrawEllipsePrimitive(int player, const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color); + void DrawEllipsePrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color); /// /// Schedule to draw a filled ellipse primitive. @@ -286,7 +285,7 @@ namespace RTE { /// Horizontal radius of the ellipse primitive. /// Vertical radius of the ellipse primitive. /// Color to fill primitive with. - void DrawEllipseFillPrimitive(const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color); + void DrawEllipseFillPrimitive(const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color); /// /// Schedule to draw a filled ellipse primitive visible only to a specified player. @@ -296,7 +295,7 @@ namespace RTE { /// Horizontal radius of the ellipse primitive. /// Vertical radius of the ellipse primitive. /// Color to fill primitive with. - void DrawEllipseFillPrimitive(int player, const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color); + void DrawEllipseFillPrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color); /// /// Schedule to draw a triangle primitive. @@ -305,7 +304,7 @@ namespace RTE { /// Position of the second point of the triangle in scene coordinates. /// Position of the third point of the triangle in scene coordinates. /// Color to fill primitive with. - void DrawTrianglePrimitive(const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color); + void DrawTrianglePrimitive(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color); /// /// Schedule to draw a triangle primitive visible only to a specified player. @@ -315,7 +314,7 @@ namespace RTE { /// Position of the second point of the triangle in scene coordinates. /// Position of the third point of the triangle in scene coordinates. /// Color to fill primitive with. - void DrawTrianglePrimitive(int player, const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color); + void DrawTrianglePrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color); /// /// Schedule to draw a filled triangle primitive. @@ -324,7 +323,7 @@ namespace RTE { /// Position of the second point of the triangle in scene coordinates. /// Position of the third point of the triangle in scene coordinates. /// Color to fill primitive with. - void DrawTriangleFillPrimitive(const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color); + void DrawTriangleFillPrimitive(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color); /// /// Schedule to draw a filled triangle primitive visible only to a specified player. @@ -334,7 +333,7 @@ namespace RTE { /// Position of the second point of the triangle in scene coordinates. /// Position of the third point of the triangle in scene coordinates. /// Color to fill primitive with. - void DrawTriangleFillPrimitive(int player, const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color); + void DrawTriangleFillPrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color); /// /// Schedule to draw a polygon primitive visible only to a specified player. @@ -344,7 +343,7 @@ namespace RTE { /// Color to draw primitive with. /// A vector containing the positions of the vertices of the polygon, relative to the center position. /// Whether a PolygonFillPrimitive should be scheduled instead of PolygonPrimitive. - void DrawPolygonOrPolygonFillPrimitive(int player, const Vector &startPos, unsigned char color, const std::vector &vertices, bool filled); + void DrawPolygonOrPolygonFillPrimitive(int player, const Vector& startPos, unsigned char color, const std::vector& vertices, bool filled); /// /// Schedule to draw a text primitive. @@ -353,7 +352,7 @@ namespace RTE { /// Text string to draw. /// Use small or large font. True for small font. /// Alignment of text. - void DrawTextPrimitive(const Vector &start, const std::string &text, bool isSmall, int alignment); + void DrawTextPrimitive(const Vector& start, const std::string& text, bool isSmall, int alignment); /// /// Schedule to draw a text primitive. @@ -363,7 +362,7 @@ namespace RTE { /// Use small or large font. True for small font. /// Alignment of text. /// Angle to rotate text in radians. - void DrawTextPrimitive(const Vector &start, const std::string &text, bool isSmall, int alignment, float rotAngle); + void DrawTextPrimitive(const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle); /// /// Schedule to draw a text primitive visible only to a specified player. @@ -373,7 +372,7 @@ namespace RTE { /// Text string to draw. /// Use small or large font. True for small font. /// Alignment of text. - void DrawTextPrimitive(int player, const Vector &start, const std::string &text, bool isSmall, int alignment); + void DrawTextPrimitive(int player, const Vector& start, const std::string& text, bool isSmall, int alignment); /// /// Schedule to draw a text primitive visible only to a specified player. @@ -384,7 +383,7 @@ namespace RTE { /// Use small or large font. True for small font. /// Alignment of text. /// Angle to rotate text in radians. - void DrawTextPrimitive(int player, const Vector &start, const std::string &text, bool isSmall, int alignment, float rotAngle); + void DrawTextPrimitive(int player, const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle); /// /// Schedule to draw a bitmap primitive. @@ -393,7 +392,7 @@ namespace RTE { /// A MOSprite to draw BITMAP from. /// Rotation angle in radians. /// Frame to draw. - void DrawBitmapPrimitive(const Vector ¢erPos, const MOSprite *moSprite, float rotAngle, int frame) { DrawBitmapPrimitive(-1, centerPos, moSprite, rotAngle, frame, false, false); } + void DrawBitmapPrimitive(const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame) { DrawBitmapPrimitive(-1, centerPos, moSprite, rotAngle, frame, false, false); } /// /// Schedule to draw a bitmap primitive with the option to flip the primitive horizontally and vertically. @@ -404,7 +403,7 @@ namespace RTE { /// Frame to draw. /// Whether to flip the sprite horizontally. /// Whether to flip the sprite vertically. - void DrawBitmapPrimitive(const Vector ¢erPos, const MOSprite *moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped) { DrawBitmapPrimitive(-1, centerPos, moSprite, rotAngle, frame, hFlipped, vFlipped); } + void DrawBitmapPrimitive(const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped) { DrawBitmapPrimitive(-1, centerPos, moSprite, rotAngle, frame, hFlipped, vFlipped); } /// /// Schedule to draw a bitmap primitive visible only to a specified player. @@ -414,7 +413,7 @@ namespace RTE { /// A MOSprite to draw BITMAP from. /// Rotation angle in radians. /// Frame to draw. - void DrawBitmapPrimitive(int player, const Vector ¢erPos, const MOSprite *moSprite, float rotAngle, int frame) { DrawBitmapPrimitive(player, centerPos, moSprite, rotAngle, frame, false, false); } + void DrawBitmapPrimitive(int player, const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame) { DrawBitmapPrimitive(player, centerPos, moSprite, rotAngle, frame, false, false); } /// /// Schedule to draw a bitmap primitive visible only to a specified player with the option to flip the primitive horizontally or vertically. @@ -426,7 +425,7 @@ namespace RTE { /// Frame to draw. /// Whether to flip the sprite horizontally. /// Whether to flip the sprite vertically. - void DrawBitmapPrimitive(int player, const Vector ¢erPos, const MOSprite *moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped); + void DrawBitmapPrimitive(int player, const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped); /// /// Schedule to draw a bitmap primitive. @@ -434,7 +433,7 @@ namespace RTE { /// Position of primitive's center in scene coordinates. /// Path to the bitmap to draw. /// Rotation angle in radians. - void DrawBitmapPrimitive(const Vector ¢erPos, const std::string &filePath, float rotAngle) { DrawBitmapPrimitive(-1, centerPos, filePath, rotAngle, false, false); } + void DrawBitmapPrimitive(const Vector& centerPos, const std::string& filePath, float rotAngle) { DrawBitmapPrimitive(-1, centerPos, filePath, rotAngle, false, false); } /// /// Schedule to draw a bitmap primitive with the option to flip the primitive horizontally and vertically. @@ -444,7 +443,7 @@ namespace RTE { /// Rotation angle in radians. /// Whether to flip the sprite horizontally. /// Whether to flip the sprite vertically. - void DrawBitmapPrimitive(const Vector ¢erPos, const std::string &filePath, float rotAngle, bool hFlipped, bool vFlipped) { DrawBitmapPrimitive(-1, centerPos, filePath, rotAngle, hFlipped, vFlipped); } + void DrawBitmapPrimitive(const Vector& centerPos, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped) { DrawBitmapPrimitive(-1, centerPos, filePath, rotAngle, hFlipped, vFlipped); } /// /// Schedule to draw a bitmap primitive visible only to a specified player. @@ -453,7 +452,7 @@ namespace RTE { /// Position of primitive's center in scene coordinates. /// Path to the bitmap to draw. /// Rotation angle in radians. - void DrawBitmapPrimitive(int player, const Vector ¢erPos, const std::string &filePath, float rotAngle) { DrawBitmapPrimitive(player, centerPos, filePath, rotAngle, false, false); } + void DrawBitmapPrimitive(int player, const Vector& centerPos, const std::string& filePath, float rotAngle) { DrawBitmapPrimitive(player, centerPos, filePath, rotAngle, false, false); } /// /// Schedule to draw a bitmap primitive visible only to a specified player with the option to flip the primitive horizontally or vertically. @@ -464,14 +463,14 @@ namespace RTE { /// Rotation angle in radians. /// Whether to flip the sprite horizontally. /// Whether to flip the sprite vertically. - void DrawBitmapPrimitive(int player, const Vector ¢erPos, const std::string &filePath, float rotAngle, bool hFlipped, bool vFlipped); + void DrawBitmapPrimitive(int player, const Vector& centerPos, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped); /// /// Schedule to draw the GUI icon of an object. /// /// Position of primitive's center in scene coordinates. /// An entity to draw sprite from. - void DrawIconPrimitive(const Vector ¢erPos, Entity *entity) { DrawIconPrimitive(-1, centerPos, entity); } + void DrawIconPrimitive(const Vector& centerPos, Entity* entity) { DrawIconPrimitive(-1, centerPos, entity); } /// /// Schedule to draw the GUI icon of an object, visible only to a specified player. @@ -479,11 +478,10 @@ namespace RTE { /// Player screen to draw primitive on. /// Position of primitive's center in scene coordinates. /// An entity to draw sprite from. - void DrawIconPrimitive(int player, const Vector ¢erPos, Entity *entity); + void DrawIconPrimitive(int player, const Vector& centerPos, Entity* entity); #pragma endregion protected: - std::mutex m_Mutex; //!< Mutex so that mutiple threads (i.e multithreaded scripts) can safely queue up draws std::deque> m_ScheduledPrimitives; //!< List of graphical primitives scheduled to draw this frame, cleared every frame during FrameMan::Draw(). @@ -494,7 +492,7 @@ namespace RTE { /// /// Raw pointer to the GraphicalPrimitive object to make unique. /// A unique_ptr of the appropriate derived GraphicalPrimitive type. Ownership is transferred! - std::unique_ptr MakeUniqueOfAppropriateTypeFromPrimitiveRawPtr(GraphicalPrimitive *primitive); + std::unique_ptr MakeUniqueOfAppropriateTypeFromPrimitiveRawPtr(GraphicalPrimitive* primitive); /// /// Safely schedules a primite to draw in a thread-safe manner. @@ -503,8 +501,8 @@ namespace RTE { void SchedulePrimitive(std::unique_ptr&& primitive); // Disallow the use of some implicit methods. - PrimitiveMan(const PrimitiveMan &reference) = delete; - PrimitiveMan & operator=(const PrimitiveMan &rhs) = delete; + PrimitiveMan(const PrimitiveMan& reference) = delete; + PrimitiveMan& operator=(const PrimitiveMan& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Managers/SceneMan.cpp b/Source/Managers/SceneMan.cpp index 438bec0411..1af9595598 100644 --- a/Source/Managers/SceneMan.cpp +++ b/Source/Managers/SceneMan.cpp @@ -7,7 +7,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files #include "NetworkServer.h" @@ -38,2899 +37,2680 @@ #include "tracy/Tracy.hpp" -namespace RTE -{ +namespace RTE { #define CLEANAIRINTERVAL 200000 -const std::string SceneMan::c_ClassName = "SceneMan"; -std::vector> SceneMan::m_IntermediateSettlingBitmaps; + const std::string SceneMan::c_ClassName = "SceneMan"; + std::vector> SceneMan::m_IntermediateSettlingBitmaps; -// Stored as a thread-local instead of in the class, because multithreaded Lua scripts will interfere otherwise -thread_local Vector s_LastRayHitPos; + // Stored as a thread-local instead of in the class, because multithreaded Lua scripts will interfere otherwise + thread_local Vector s_LastRayHitPos; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void SceneMan::Clear() -{ - m_DefaultSceneName = "Tutorial Bunker"; - m_pSceneToLoad = nullptr; - m_PlaceObjects = true; - m_PlaceUnits = true; - m_pCurrentScene = nullptr; - m_pMOColorLayer = nullptr; - m_pMOIDLayer = nullptr; - m_pDebugLayer = nullptr; + void SceneMan::Clear() { + m_DefaultSceneName = "Tutorial Bunker"; + m_pSceneToLoad = nullptr; + m_PlaceObjects = true; + m_PlaceUnits = true; + m_pCurrentScene = nullptr; + m_pMOColorLayer = nullptr; + m_pMOIDLayer = nullptr; + m_pDebugLayer = nullptr; - m_LayerDrawMode = g_LayerNormal; + m_LayerDrawMode = g_LayerNormal; - m_MatNameMap.clear(); - m_apMatPalette.fill(nullptr); - m_MaterialCount = 0; + m_MatNameMap.clear(); + m_apMatPalette.fill(nullptr); + m_MaterialCount = 0; - m_MaterialCopiesVector.clear(); + m_MaterialCopiesVector.clear(); - m_pUnseenRevealSound = nullptr; - m_DrawRayCastVisualizations = false; - m_DrawPixelCheckVisualizations = false; - m_LastUpdatedScreen = 0; - m_SecondStructPass = false; -// m_CalcTimer.Reset(); - m_CleanTimer.Reset(); + m_pUnseenRevealSound = nullptr; + m_DrawRayCastVisualizations = false; + m_DrawPixelCheckVisualizations = false; + m_LastUpdatedScreen = 0; + m_SecondStructPass = false; + // m_CalcTimer.Reset(); + m_CleanTimer.Reset(); - if (m_pOrphanSearchBitmap) - destroy_bitmap(m_pOrphanSearchBitmap); - m_pOrphanSearchBitmap = create_bitmap_ex(8, MAXORPHANRADIUS , MAXORPHANRADIUS); - - m_ScrapCompactingHeight = 25; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void SceneMan::Initialize() const { - // Can't create these earlier in the static declaration because allegro_init needs to be called before create_bitmap - m_IntermediateSettlingBitmaps = { - { 16, create_bitmap_ex(8, 16, 16) }, - { 32, create_bitmap_ex(8, 32, 32) }, - { 48, create_bitmap_ex(8, 48, 48) }, - { 64, create_bitmap_ex(8, 64, 64) }, - { 96, create_bitmap_ex(8, 96, 96) }, - { 128, create_bitmap_ex(8, 128, 128) }, - { 192, create_bitmap_ex(8, 192, 192) }, - { 256, create_bitmap_ex(8, 256, 256) }, - { 384, create_bitmap_ex(8, 384, 384) }, - { 512, create_bitmap_ex(8, 512, 512) } - }; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::Create(std::string readerFile) -{ - Reader *reader = new Reader(); - if (reader->Create(readerFile.c_str())) - g_ConsoleMan.PrintString("ERROR: Could not find Scene definition file!"); - - Serializable::Create(*reader); - delete reader; - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Material * SceneMan::AddMaterialCopy(Material *mat) -{ - Material * matCopy = dynamic_cast(mat->Clone()); - if (matCopy) - m_MaterialCopiesVector.push_back(matCopy); - - return matCopy; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::LoadScene(Scene *pNewScene, bool placeObjects, bool placeUnits) { - if (!pNewScene) { - return -1; + if (m_pOrphanSearchBitmap) + destroy_bitmap(m_pOrphanSearchBitmap); + m_pOrphanSearchBitmap = create_bitmap_ex(8, MAXORPHANRADIUS, MAXORPHANRADIUS); + + m_ScrapCompactingHeight = 25; } - g_MovableMan.PurgeAllMOs(); - g_PostProcessMan.ClearScenePostEffects(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::Initialize() const { + // Can't create these earlier in the static declaration because allegro_init needs to be called before create_bitmap + m_IntermediateSettlingBitmaps = { + {16, create_bitmap_ex(8, 16, 16)}, + {32, create_bitmap_ex(8, 32, 32)}, + {48, create_bitmap_ex(8, 48, 48)}, + {64, create_bitmap_ex(8, 64, 64)}, + {96, create_bitmap_ex(8, 96, 96)}, + {128, create_bitmap_ex(8, 128, 128)}, + {192, create_bitmap_ex(8, 192, 192)}, + {256, create_bitmap_ex(8, 256, 256)}, + {384, create_bitmap_ex(8, 384, 384)}, + {512, create_bitmap_ex(8, 512, 512)}}; + } - if (m_pCurrentScene) { - delete m_pCurrentScene; - m_pCurrentScene = nullptr; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::Create(std::string readerFile) { + Reader* reader = new Reader(); + if (reader->Create(readerFile.c_str())) + g_ConsoleMan.PrintString("ERROR: Could not find Scene definition file!"); + + Serializable::Create(*reader); + delete reader; + + return 0; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Material* SceneMan::AddMaterialCopy(Material* mat) { + Material* matCopy = dynamic_cast(mat->Clone()); + if (matCopy) + m_MaterialCopiesVector.push_back(matCopy); + + return matCopy; } - g_NetworkServer.LockScene(true); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::LoadScene(Scene* pNewScene, bool placeObjects, bool placeUnits) { + if (!pNewScene) { + return -1; + } + + g_MovableMan.PurgeAllMOs(); + g_PostProcessMan.ClearScenePostEffects(); + + if (m_pCurrentScene) { + delete m_pCurrentScene; + m_pCurrentScene = nullptr; + } + + g_NetworkServer.LockScene(true); + + m_pCurrentScene = pNewScene; + if (m_pCurrentScene->LoadData(placeObjects, true, placeUnits) < 0) { + g_ConsoleMan.PrintString("ERROR: Loading scene \'" + m_pCurrentScene->GetPresetName() + "\' failed! Has it been properly defined?"); + g_NetworkServer.LockScene(false); + return -1; + } + + // Report successful load to the console + g_ConsoleMan.PrintString("SYSTEM: Scene \"" + m_pCurrentScene->GetPresetName() + "\" was loaded"); + + // Set the proper scales of the unseen obscuring SceneLayers + SceneLayer* pUnseenLayer = 0; + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + if (!g_ActivityMan.GetActivity()->TeamActive(team)) + continue; + SceneLayer* pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); + if (pUnseenLayer && pUnseenLayer->GetBitmap()) { + // Calculate how many times smaller the unseen map is compared to the entire terrain's dimensions, and set it as the scale factor on the Unseen layer + pUnseenLayer->SetScaleFactor(Vector((float)m_pCurrentScene->GetTerrain()->GetBitmap()->w / (float)pUnseenLayer->GetBitmap()->w, (float)m_pCurrentScene->GetTerrain()->GetBitmap()->h / (float)pUnseenLayer->GetBitmap()->h)); + } + } + + // Get the unseen reveal sound + if (!m_pUnseenRevealSound) + m_pUnseenRevealSound = dynamic_cast(g_PresetMan.GetEntityPreset("SoundContainer", "Unseen Reveal Blip")->Clone()); + + // m_pCurrentScene->GetTerrain()->CleanAir(); + + // Re-create the MoveableObject:s color SceneLayer + delete m_pMOColorLayer; + BITMAP* pBitmap = create_bitmap_ex(8, GetSceneWidth(), GetSceneHeight()); + clear_to_color(pBitmap, g_MaskColor); + m_pMOColorLayer = new SceneLayerTracked(); + m_pMOColorLayer->Create(pBitmap, true, Vector(), m_pCurrentScene->WrapsX(), m_pCurrentScene->WrapsY(), Vector(1.0, 1.0)); + pBitmap = 0; + + // Re-create the MoveableObject:s ID SceneLayer + delete m_pMOIDLayer; + pBitmap = create_bitmap_ex(c_MOIDLayerBitDepth, GetSceneWidth(), GetSceneHeight()); + clear_to_color(pBitmap, g_NoMOID); + m_pMOIDLayer = new SceneLayerTracked(); + m_pMOIDLayer->Create(pBitmap, false, Vector(), m_pCurrentScene->WrapsX(), m_pCurrentScene->WrapsY(), Vector(1.0, 1.0)); + pBitmap = 0; + + const int cellSize = 20; + m_MOIDsGrid = SpatialPartitionGrid(GetSceneWidth(), GetSceneHeight(), cellSize); + + // Create the Debug SceneLayer + if (m_DrawRayCastVisualizations || m_DrawPixelCheckVisualizations) { + delete m_pDebugLayer; + pBitmap = create_bitmap_ex(8, GetSceneWidth(), GetSceneHeight()); + clear_to_color(pBitmap, g_MaskColor); + m_pDebugLayer = new SceneLayer(); + m_pDebugLayer->Create(pBitmap, true, Vector(), m_pCurrentScene->WrapsX(), m_pCurrentScene->WrapsY(), Vector(1.0, 1.0)); + pBitmap = nullptr; + } + + // Finally draw the ID:s of the MO:s to the MOID layers for the first time + g_MovableMan.UpdateDrawMOIDs(m_pMOIDLayer->GetBitmap()); - m_pCurrentScene = pNewScene; - if (m_pCurrentScene->LoadData(placeObjects, true, placeUnits) < 0) - { - g_ConsoleMan.PrintString("ERROR: Loading scene \'" + m_pCurrentScene->GetPresetName() + "\' failed! Has it been properly defined?"); g_NetworkServer.LockScene(false); - return -1; - } - - // Report successful load to the console - g_ConsoleMan.PrintString("SYSTEM: Scene \"" + m_pCurrentScene->GetPresetName() + "\" was loaded"); - - // Set the proper scales of the unseen obscuring SceneLayers - SceneLayer *pUnseenLayer = 0; - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - if (!g_ActivityMan.GetActivity()->TeamActive(team)) - continue; - SceneLayer *pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); - if (pUnseenLayer && pUnseenLayer->GetBitmap()) - { - // Calculate how many times smaller the unseen map is compared to the entire terrain's dimensions, and set it as the scale factor on the Unseen layer - pUnseenLayer->SetScaleFactor(Vector((float)m_pCurrentScene->GetTerrain()->GetBitmap()->w / (float)pUnseenLayer->GetBitmap()->w, (float)m_pCurrentScene->GetTerrain()->GetBitmap()->h / (float)pUnseenLayer->GetBitmap()->h)); - } - } - - // Get the unseen reveal sound - if (!m_pUnseenRevealSound) - m_pUnseenRevealSound = dynamic_cast(g_PresetMan.GetEntityPreset("SoundContainer", "Unseen Reveal Blip")->Clone()); - -// m_pCurrentScene->GetTerrain()->CleanAir(); - - // Re-create the MoveableObject:s color SceneLayer - delete m_pMOColorLayer; - BITMAP *pBitmap = create_bitmap_ex(8, GetSceneWidth(), GetSceneHeight()); - clear_to_color(pBitmap, g_MaskColor); - m_pMOColorLayer = new SceneLayerTracked(); - m_pMOColorLayer->Create(pBitmap, true, Vector(), m_pCurrentScene->WrapsX(), m_pCurrentScene->WrapsY(), Vector(1.0, 1.0)); - pBitmap = 0; - - // Re-create the MoveableObject:s ID SceneLayer - delete m_pMOIDLayer; - pBitmap = create_bitmap_ex(c_MOIDLayerBitDepth, GetSceneWidth(), GetSceneHeight()); - clear_to_color(pBitmap, g_NoMOID); - m_pMOIDLayer = new SceneLayerTracked(); - m_pMOIDLayer->Create(pBitmap, false, Vector(), m_pCurrentScene->WrapsX(), m_pCurrentScene->WrapsY(), Vector(1.0, 1.0)); - pBitmap = 0; - - const int cellSize = 20; - m_MOIDsGrid = SpatialPartitionGrid(GetSceneWidth(), GetSceneHeight(), cellSize); - - // Create the Debug SceneLayer - if (m_DrawRayCastVisualizations || m_DrawPixelCheckVisualizations) { - delete m_pDebugLayer; - pBitmap = create_bitmap_ex(8, GetSceneWidth(), GetSceneHeight()); - clear_to_color(pBitmap, g_MaskColor); - m_pDebugLayer = new SceneLayer(); - m_pDebugLayer->Create(pBitmap, true, Vector(), m_pCurrentScene->WrapsX(), m_pCurrentScene->WrapsY(), Vector(1.0, 1.0)); - pBitmap = nullptr; - } - - // Finally draw the ID:s of the MO:s to the MOID layers for the first time - g_MovableMan.UpdateDrawMOIDs(m_pMOIDLayer->GetBitmap()); - - g_NetworkServer.LockScene(false); - g_NetworkServer.ResetScene(); - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::SetSceneToLoad(std::string sceneName, bool placeObjects, bool placeUnits) -{ - // Use the name passed in to load the preset requested - const Scene *pSceneRef = dynamic_cast(g_PresetMan.GetEntityPreset("Scene", sceneName)); - - if (!pSceneRef) - { - g_ConsoleMan.PrintString("ERROR: Finding Scene preset \'" + sceneName + "\' failed! Has it been properly defined?"); - return -1; - } - - // Store the scene reference to load later - SetSceneToLoad(pSceneRef, placeObjects, placeUnits); - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::LoadScene() -{ - // In case we have no set Scene reference to load from, do something graceful about it - if (!m_pSceneToLoad) - { - // Try to use the Scene the current Activity is associated with - if (g_ActivityMan.GetActivity()) - SetSceneToLoad(g_ActivityMan.GetActivity()->GetSceneName()); - - // If that failed, then resort to the default scene name - if (SetSceneToLoad(m_DefaultSceneName) < 0) - { - g_ConsoleMan.PrintString("ERROR: Couldn't start because no Scene has been specified to load!"); - return -1; - } - } - - return LoadScene(dynamic_cast(m_pSceneToLoad->Clone()), m_PlaceObjects, m_PlaceUnits); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::LoadScene(std::string sceneName, bool placeObjects, bool placeUnits) -{ - // First retrieve and set up the preset reference - int error = SetSceneToLoad(sceneName, placeObjects, placeUnits); - if (error < 0) - return error; - // Now actually load and start it - error = LoadScene(); - return error; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::ReadProperty(const std::string_view &propName, Reader &reader) -{ - StartPropertyList(return Serializable::ReadProperty(propName, reader)); - - MatchProperty("AddMaterial", - { - // Get this before reading Object, since if it's the last one in its datafile, the stream will show the parent file instead - std::string objectFilePath = reader.GetCurrentFilePath(); - - // Don't use the << operator, because it adds the material to the PresetMan before we get a chance to set the proper ID! - Material *pNewMat = new Material; - ((Serializable *)(pNewMat))->Create(reader); - - // If the initially requested material slot is available, then put it there - // But if it's not available, then check if any subsequent one is, looping around the palette if necessary - for (int tryId = pNewMat->GetIndex(); tryId < c_PaletteEntriesNumber; ++tryId) - { - // We found an empty slot in the Material palette! - if (m_apMatPalette.at(tryId) == nullptr) - { - // If the final ID isn't the same as the one originally requested by the data file, then make the mapping so - // subsequent ID references to this within the same data module can be translated to the actual ID of this material - if (tryId != pNewMat->GetIndex()) - g_PresetMan.AddMaterialMapping(pNewMat->GetIndex(), tryId, reader.GetReadModuleID()); - - // Assign the final ID to the material and register it in the palette - pNewMat->SetIndex(tryId); - - // Ensure out-of-bounds material is unbreakable - if (tryId == MaterialColorKeys::g_MaterialOutOfBounds) { - RTEAssert(pNewMat->GetIntegrity() == std::numeric_limits::max(), "Material with index " + std::to_string(MaterialColorKeys::g_MaterialOutOfBounds) + " (i.e out-of-bounds material) has a finite integrity!\n This should be infinity (-1)."); - } - - m_apMatPalette.at(tryId) = pNewMat; - m_MatNameMap.insert(std::pair(std::string(pNewMat->GetPresetName()), pNewMat->GetIndex())); - // Now add the instance, when ID has been registered! - g_PresetMan.AddEntityPreset(pNewMat, reader.GetReadModuleID(), reader.GetPresetOverwriting(), objectFilePath); - ++m_MaterialCount; - break; - } - // We reached the end of the Material palette without finding any empty slots.. loop around to the start - else if (tryId >= c_PaletteEntriesNumber - 1) - tryId = 0; - // If we've looped around without finding anything, break and throw error - else if (tryId == pNewMat->GetIndex() - 1) - { -// TODO: find the closest matching mateiral and map to it? - RTEAbort("Tried to load material \"" + pNewMat->GetPresetName() + "\" but the material palette (256 max) is full! Try consolidating or removing some redundant materials, or removing some entire data modules."); - break; - } - } - }); - - EndPropertyList; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::Save(Writer &writer) const { - g_ConsoleMan.PrintString("ERROR: Tried to save SceneMan, screen does not make sense"); - - Serializable::Save(writer); - - for (int i = 0; i < m_MaterialCount; ++i) { - writer.NewPropertyWithValue("AddMaterial", *(m_apMatPalette.at(i))); - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void SceneMan::Destroy() -{ - for (int i = 0; i < c_PaletteEntriesNumber; ++i) - delete m_apMatPalette[i]; - - delete m_pCurrentScene; - delete m_pDebugLayer; - delete m_pMOIDLayer; - delete m_pMOColorLayer; - delete m_pUnseenRevealSound; - - destroy_bitmap(m_pOrphanSearchBitmap); - m_pOrphanSearchBitmap = 0; - - for (const auto &[bitmapSize, bitmapPtr] : m_IntermediateSettlingBitmaps) { - destroy_bitmap(bitmapPtr); - } - - Clear(); -} + g_NetworkServer.ResetScene(); + + return 0; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::SetSceneToLoad(std::string sceneName, bool placeObjects, bool placeUnits) { + // Use the name passed in to load the preset requested + const Scene* pSceneRef = dynamic_cast(g_PresetMan.GetEntityPreset("Scene", sceneName)); + + if (!pSceneRef) { + g_ConsoleMan.PrintString("ERROR: Finding Scene preset \'" + sceneName + "\' failed! Has it been properly defined?"); + return -1; + } + + // Store the scene reference to load later + SetSceneToLoad(pSceneRef, placeObjects, placeUnits); + + return 0; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::LoadScene() { + // In case we have no set Scene reference to load from, do something graceful about it + if (!m_pSceneToLoad) { + // Try to use the Scene the current Activity is associated with + if (g_ActivityMan.GetActivity()) + SetSceneToLoad(g_ActivityMan.GetActivity()->GetSceneName()); + + // If that failed, then resort to the default scene name + if (SetSceneToLoad(m_DefaultSceneName) < 0) { + g_ConsoleMan.PrintString("ERROR: Couldn't start because no Scene has been specified to load!"); + return -1; + } + } + + return LoadScene(dynamic_cast(m_pSceneToLoad->Clone()), m_PlaceObjects, m_PlaceUnits); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::LoadScene(std::string sceneName, bool placeObjects, bool placeUnits) { + // First retrieve and set up the preset reference + int error = SetSceneToLoad(sceneName, placeObjects, placeUnits); + if (error < 0) + return error; + // Now actually load and start it + error = LoadScene(); + return error; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::ReadProperty(const std::string_view& propName, Reader& reader) { + StartPropertyList(return Serializable::ReadProperty(propName, reader)); + + MatchProperty("AddMaterial", + { + // Get this before reading Object, since if it's the last one in its datafile, the stream will show the parent file instead + std::string objectFilePath = reader.GetCurrentFilePath(); + + // Don't use the << operator, because it adds the material to the PresetMan before we get a chance to set the proper ID! + Material* pNewMat = new Material; + ((Serializable*)(pNewMat))->Create(reader); + + // If the initially requested material slot is available, then put it there + // But if it's not available, then check if any subsequent one is, looping around the palette if necessary + for (int tryId = pNewMat->GetIndex(); tryId < c_PaletteEntriesNumber; ++tryId) { + // We found an empty slot in the Material palette! + if (m_apMatPalette.at(tryId) == nullptr) { + // If the final ID isn't the same as the one originally requested by the data file, then make the mapping so + // subsequent ID references to this within the same data module can be translated to the actual ID of this material + if (tryId != pNewMat->GetIndex()) + g_PresetMan.AddMaterialMapping(pNewMat->GetIndex(), tryId, reader.GetReadModuleID()); + + // Assign the final ID to the material and register it in the palette + pNewMat->SetIndex(tryId); + + // Ensure out-of-bounds material is unbreakable + if (tryId == MaterialColorKeys::g_MaterialOutOfBounds) { + RTEAssert(pNewMat->GetIntegrity() == std::numeric_limits::max(), "Material with index " + std::to_string(MaterialColorKeys::g_MaterialOutOfBounds) + " (i.e out-of-bounds material) has a finite integrity!\n This should be infinity (-1)."); + } + + m_apMatPalette.at(tryId) = pNewMat; + m_MatNameMap.insert(std::pair(std::string(pNewMat->GetPresetName()), pNewMat->GetIndex())); + // Now add the instance, when ID has been registered! + g_PresetMan.AddEntityPreset(pNewMat, reader.GetReadModuleID(), reader.GetPresetOverwriting(), objectFilePath); + ++m_MaterialCount; + break; + } + // We reached the end of the Material palette without finding any empty slots.. loop around to the start + else if (tryId >= c_PaletteEntriesNumber - 1) + tryId = 0; + // If we've looped around without finding anything, break and throw error + else if (tryId == pNewMat->GetIndex() - 1) { + // TODO: find the closest matching mateiral and map to it? + RTEAbort("Tried to load material \"" + pNewMat->GetPresetName() + "\" but the material palette (256 max) is full! Try consolidating or removing some redundant materials, or removing some entire data modules."); + break; + } + } + }); + + EndPropertyList; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::Save(Writer& writer) const { + g_ConsoleMan.PrintString("ERROR: Tried to save SceneMan, screen does not make sense"); + + Serializable::Save(writer); + + for (int i = 0; i < m_MaterialCount; ++i) { + writer.NewPropertyWithValue("AddMaterial", *(m_apMatPalette.at(i))); + } + + return 0; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SceneMan::Destroy() { + for (int i = 0; i < c_PaletteEntriesNumber; ++i) + delete m_apMatPalette[i]; -Vector SceneMan::GetSceneDim() const -{ - if (m_pCurrentScene) { - RTEAssert(m_pCurrentScene->GetTerrain() && m_pCurrentScene->GetTerrain()->GetBitmap(), "Trying to get terrain info before there is a scene or terrain!"); - return m_pCurrentScene->GetDimensions(); - } - return Vector(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::GetSceneWidth() const -{ - if (g_NetworkClient.IsConnectedAndRegistered()) { - return g_NetworkClient.GetSceneWidth(); - } - - if (m_pCurrentScene) - return m_pCurrentScene->GetWidth(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::GetSceneHeight() const -{ -// RTEAssert(m_pCurrentScene, "Trying to get terrain info before there is a scene or terrain!"); - if (m_pCurrentScene) - return m_pCurrentScene->GetHeight(); - return 0; -} + delete m_pCurrentScene; + delete m_pDebugLayer; + delete m_pMOIDLayer; + delete m_pMOColorLayer; + delete m_pUnseenRevealSound; + + destroy_bitmap(m_pOrphanSearchBitmap); + m_pOrphanSearchBitmap = 0; + + for (const auto& [bitmapSize, bitmapPtr]: m_IntermediateSettlingBitmaps) { + destroy_bitmap(bitmapPtr); + } + + Clear(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::SceneWrapsX() const -{ - if (g_NetworkClient.IsConnectedAndRegistered()) { - return g_NetworkClient.SceneWrapsX(); + Vector SceneMan::GetSceneDim() const { + if (m_pCurrentScene) { + RTEAssert(m_pCurrentScene->GetTerrain() && m_pCurrentScene->GetTerrain()->GetBitmap(), "Trying to get terrain info before there is a scene or terrain!"); + return m_pCurrentScene->GetDimensions(); + } + return Vector(); } - if (m_pCurrentScene) - return m_pCurrentScene->WrapsX(); - return false; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::GetSceneWidth() const { + if (g_NetworkClient.IsConnectedAndRegistered()) { + return g_NetworkClient.GetSceneWidth(); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (m_pCurrentScene) + return m_pCurrentScene->GetWidth(); + return 0; + } -bool SceneMan::SceneWrapsY() const -{ - if (m_pCurrentScene) - return m_pCurrentScene->WrapsY(); - return false; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int SceneMan::GetSceneHeight() const { + // RTEAssert(m_pCurrentScene, "Trying to get terrain info before there is a scene or terrain!"); + if (m_pCurrentScene) + return m_pCurrentScene->GetHeight(); + return 0; + } -Directions SceneMan::GetSceneOrbitDirection() const { - if (m_pCurrentScene) { - SLTerrain *terrain = m_pCurrentScene->GetTerrain(); - if (terrain) { - return terrain->GetOrbitDirection(); - } - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return Directions::Up; -} + bool SceneMan::SceneWrapsX() const { + if (g_NetworkClient.IsConnectedAndRegistered()) { + return g_NetworkClient.SceneWrapsX(); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (m_pCurrentScene) + return m_pCurrentScene->WrapsX(); + return false; + } -SLTerrain * SceneMan::GetTerrain() -{ -// RTEAssert(m_pCurrentScene, "Trying to get terrain matter before there is a scene or terrain!"); - if (m_pCurrentScene) { - return m_pCurrentScene->GetTerrain(); - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return nullptr; -} + bool SceneMan::SceneWrapsY() const { + if (m_pCurrentScene) + return m_pCurrentScene->WrapsY(); + return false; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -BITMAP * SceneMan::GetMOColorBitmap() const { - return m_pMOColorLayer->GetBitmap(); -} + Directions SceneMan::GetSceneOrbitDirection() const { + if (m_pCurrentScene) { + SLTerrain* terrain = m_pCurrentScene->GetTerrain(); + if (terrain) { + return terrain->GetOrbitDirection(); + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + return Directions::Up; + } -BITMAP *SceneMan::GetDebugBitmap() const { - RTEAssert(m_pDebugLayer, "Tried to get debug bitmap but debug layer doesn't exist. Note that the debug layer is only created under certain circumstances."); - return m_pDebugLayer->GetBitmap(); -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + SLTerrain* SceneMan::GetTerrain() { + // RTEAssert(m_pCurrentScene, "Trying to get terrain matter before there is a scene or terrain!"); + if (m_pCurrentScene) { + return m_pCurrentScene->GetTerrain(); + } -BITMAP * SceneMan::GetMOIDBitmap() const { - return m_pMOIDLayer->GetBitmap(); -} + return nullptr; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// TEMP! -bool SceneMan::MOIDClearCheck() -{ - BITMAP *pMOIDMap = m_pMOIDLayer->GetBitmap(); - int badMOID = g_NoMOID; - for (int y = 0; y < pMOIDMap->h; ++y) - { - for (int x = 0; x < pMOIDMap->w; ++x) - { - if ((badMOID = _getpixel(pMOIDMap, x, y)) != g_NoMOID) - { - g_FrameMan.SaveBitmapToPNG(pMOIDMap, "MOIDCheck"); - g_FrameMan.SaveBitmapToPNG(m_pMOColorLayer->GetBitmap(), "MOIDCheck"); - RTEAbort("Bad MOID of MO detected: " + g_MovableMan.GetMOFromID(badMOID)->GetPresetName()); - return false; - } - } - } - return true; -} + BITMAP* SceneMan::GetMOColorBitmap() const { + return m_pMOColorLayer->GetBitmap(); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -unsigned char SceneMan::GetTerrMatter(int pixelX, int pixelY) -{ - RTEAssert(m_pCurrentScene, "Trying to get terrain matter before there is a scene or terrain!"); + BITMAP* SceneMan::GetDebugBitmap() const { + RTEAssert(m_pDebugLayer, "Tried to get debug bitmap but debug layer doesn't exist. Note that the debug layer is only created under certain circumstances."); + return m_pDebugLayer->GetBitmap(); + } - WrapPosition(pixelX, pixelY); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (m_pDebugLayer && m_DrawPixelCheckVisualizations) { m_pDebugLayer->SetPixel(pixelX, pixelY, 5); } + BITMAP* SceneMan::GetMOIDBitmap() const { + return m_pMOIDLayer->GetBitmap(); + } - BITMAP *pTMatBitmap = m_pCurrentScene->GetTerrain()->GetMaterialBitmap(); - if (pTMatBitmap == nullptr) { - return g_MaterialAir; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // TEMP! + bool SceneMan::MOIDClearCheck() { + BITMAP* pMOIDMap = m_pMOIDLayer->GetBitmap(); + int badMOID = g_NoMOID; + for (int y = 0; y < pMOIDMap->h; ++y) { + for (int x = 0; x < pMOIDMap->w; ++x) { + if ((badMOID = _getpixel(pMOIDMap, x, y)) != g_NoMOID) { + g_FrameMan.SaveBitmapToPNG(pMOIDMap, "MOIDCheck"); + g_FrameMan.SaveBitmapToPNG(m_pMOColorLayer->GetBitmap(), "MOIDCheck"); + RTEAbort("Bad MOID of MO detected: " + g_MovableMan.GetMOFromID(badMOID)->GetPresetName()); + return false; + } + } + } + return true; } - // If it's still below or to the sides out of bounds after - // what is supposed to be wrapped, shit is out of bounds. - if (pixelX < 0 || pixelX >= pTMatBitmap->w || pixelY >= pTMatBitmap->h) - return g_MaterialAir; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // If above terrain bitmap, return air material. - if (pixelY < 0) - return g_MaterialAir; + unsigned char SceneMan::GetTerrMatter(int pixelX, int pixelY) { + RTEAssert(m_pCurrentScene, "Trying to get terrain matter before there is a scene or terrain!"); - return getpixel(pTMatBitmap, pixelX, pixelY); -} + WrapPosition(pixelX, pixelY); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (m_pDebugLayer && m_DrawPixelCheckVisualizations) { + m_pDebugLayer->SetPixel(pixelX, pixelY, 5); + } + + BITMAP* pTMatBitmap = m_pCurrentScene->GetTerrain()->GetMaterialBitmap(); + if (pTMatBitmap == nullptr) { + return g_MaterialAir; + } + + // If it's still below or to the sides out of bounds after + // what is supposed to be wrapped, shit is out of bounds. + if (pixelX < 0 || pixelX >= pTMatBitmap->w || pixelY >= pTMatBitmap->h) + return g_MaterialAir; + + // If above terrain bitmap, return air material. + if (pixelY < 0) + return g_MaterialAir; + + return getpixel(pTMatBitmap, pixelX, pixelY); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -MOID SceneMan::GetMOIDPixel(int pixelX, int pixelY, int ignoreTeam) -{ - WrapPosition(pixelX, pixelY); + MOID SceneMan::GetMOIDPixel(int pixelX, int pixelY, int ignoreTeam) { + WrapPosition(pixelX, pixelY); - if (m_pDebugLayer && m_DrawPixelCheckVisualizations) { m_pDebugLayer->SetPixel(pixelX, pixelY, 5); } + if (m_pDebugLayer && m_DrawPixelCheckVisualizations) { + m_pDebugLayer->SetPixel(pixelX, pixelY, 5); + } - if (pixelX < 0 || pixelX >= m_pMOIDLayer->GetBitmap()->w || pixelY < 0 || pixelY >= m_pMOIDLayer->GetBitmap()->h) { - return g_NoMOID; - } + if (pixelX < 0 || pixelX >= m_pMOIDLayer->GetBitmap()->w || pixelY < 0 || pixelY >= m_pMOIDLayer->GetBitmap()->h) { + return g_NoMOID; + } #ifdef DRAW_MOID_LAYER - MOID moid = getpixel(m_pMOIDLayer->GetBitmap(), pixelX, pixelY); + MOID moid = getpixel(m_pMOIDLayer->GetBitmap(), pixelX, pixelY); #else - const std::vector &moidList = m_MOIDsGrid.GetMOIDsAtPosition(pixelX, pixelY, ignoreTeam, true); - MOID moid = g_MovableMan.GetMOIDPixel(pixelX, pixelY, moidList); + const std::vector& moidList = m_MOIDsGrid.GetMOIDsAtPosition(pixelX, pixelY, ignoreTeam, true); + MOID moid = g_MovableMan.GetMOIDPixel(pixelX, pixelY, moidList); #endif - if (g_SettingsMan.SimplifiedCollisionDetection()) { - if (moid != ColorKeys::g_NoMOID && moid != ColorKeys::g_MOIDMaskColor) { - const MOSprite *mo = dynamic_cast(g_MovableMan.GetMOFromID(moid)); - return (mo && !mo->GetTraveling()) ? moid : ColorKeys::g_NoMOID; - } else { - return ColorKeys::g_NoMOID; - } - } - - return moid; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Material const * SceneMan::GetMaterial(const std::string &matName) -{ - std::map::iterator itr = m_MatNameMap.find(matName); - if (itr == m_MatNameMap.end()) - { - g_ConsoleMan.PrintString("ERROR: Material of name: " + matName + " not found!"); - return 0; - } - else - return m_apMatPalette.at((*itr).second); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Vector SceneMan::GetGlobalAcc() const -{ - RTEAssert(m_pCurrentScene, "Trying to get terrain matter before there is a scene or terrain!"); - return m_pCurrentScene->GetGlobalAcc(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void SceneMan::LockScene() -{ -// RTEAssert(!m_pCurrentScene->IsLocked(), "Hey, locking already locked scene!"); - if (m_pCurrentScene && !m_pCurrentScene->IsLocked()) - { - m_pCurrentScene->Lock(); - m_pMOColorLayer->LockBitmaps(); - m_pMOIDLayer->LockBitmaps(); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void SceneMan::UnlockScene() -{ -// RTEAssert(m_pCurrentScene->IsLocked(), "Hey, unlocking already unlocked scene!"); - if (m_pCurrentScene && m_pCurrentScene->IsLocked()) - { - m_pCurrentScene->Unlock(); - m_pMOColorLayer->UnlockBitmaps(); - m_pMOIDLayer->UnlockBitmaps(); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::SceneIsLocked() const -{ - RTEAssert(m_pCurrentScene, "Trying to check if scene is locked before there is a scene or terrain!"); - return m_pCurrentScene->IsLocked(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void SceneMan::RegisterDrawing(const BITMAP *bitmap, int moid, int left, int top, int right, int bottom) { - if (m_pMOColorLayer && m_pMOColorLayer->GetBitmap() == bitmap) { - m_pMOColorLayer->RegisterDrawing(left, top, right, bottom); - } else if (m_pMOIDLayer && m_pMOIDLayer->GetBitmap() == bitmap) { + if (g_SettingsMan.SimplifiedCollisionDetection()) { + if (moid != ColorKeys::g_NoMOID && moid != ColorKeys::g_MOIDMaskColor) { + const MOSprite* mo = dynamic_cast(g_MovableMan.GetMOFromID(moid)); + return (mo && !mo->GetTraveling()) ? moid : ColorKeys::g_NoMOID; + } else { + return ColorKeys::g_NoMOID; + } + } + + return moid; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Material const* SceneMan::GetMaterial(const std::string& matName) { + std::map::iterator itr = m_MatNameMap.find(matName); + if (itr == m_MatNameMap.end()) { + g_ConsoleMan.PrintString("ERROR: Material of name: " + matName + " not found!"); + return 0; + } else + return m_apMatPalette.at((*itr).second); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Vector SceneMan::GetGlobalAcc() const { + RTEAssert(m_pCurrentScene, "Trying to get terrain matter before there is a scene or terrain!"); + return m_pCurrentScene->GetGlobalAcc(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::LockScene() { + // RTEAssert(!m_pCurrentScene->IsLocked(), "Hey, locking already locked scene!"); + if (m_pCurrentScene && !m_pCurrentScene->IsLocked()) { + m_pCurrentScene->Lock(); + m_pMOColorLayer->LockBitmaps(); + m_pMOIDLayer->LockBitmaps(); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::UnlockScene() { + // RTEAssert(m_pCurrentScene->IsLocked(), "Hey, unlocking already unlocked scene!"); + if (m_pCurrentScene && m_pCurrentScene->IsLocked()) { + m_pCurrentScene->Unlock(); + m_pMOColorLayer->UnlockBitmaps(); + m_pMOIDLayer->UnlockBitmaps(); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::SceneIsLocked() const { + RTEAssert(m_pCurrentScene, "Trying to check if scene is locked before there is a scene or terrain!"); + return m_pCurrentScene->IsLocked(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::RegisterDrawing(const BITMAP* bitmap, int moid, int left, int top, int right, int bottom) { + if (m_pMOColorLayer && m_pMOColorLayer->GetBitmap() == bitmap) { + m_pMOColorLayer->RegisterDrawing(left, top, right, bottom); + } else if (m_pMOIDLayer && m_pMOIDLayer->GetBitmap() == bitmap) { #ifdef DRAW_MOID_LAYER - m_pMOIDLayer->RegisterDrawing(left, top, right, bottom); + m_pMOIDLayer->RegisterDrawing(left, top, right, bottom); #else - const MovableObject *mo = g_MovableMan.GetMOFromID(moid); - if (mo) { - IntRect rect(left, top, right, bottom); - m_MOIDsGrid.Add(rect, *mo); - } + const MovableObject* mo = g_MovableMan.GetMOFromID(moid); + if (mo) { + IntRect rect(left, top, right, bottom); + m_MOIDsGrid.Add(rect, *mo); + } #endif - } -} + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void SceneMan::RegisterDrawing(const BITMAP *bitmap, int moid, const Vector ¢er, float radius) { - if (radius != 0.0F) { - RegisterDrawing(bitmap, moid, static_cast(std::floor(center.m_X - radius)), static_cast(std::floor(center.m_Y - radius)), static_cast(std::floor(center.m_X + radius)), static_cast(std::floor(center.m_Y + radius))); + void SceneMan::RegisterDrawing(const BITMAP* bitmap, int moid, const Vector& center, float radius) { + if (radius != 0.0F) { + RegisterDrawing(bitmap, moid, static_cast(std::floor(center.m_X - radius)), static_cast(std::floor(center.m_Y - radius)), static_cast(std::floor(center.m_X + radius)), static_cast(std::floor(center.m_Y + radius))); + } } -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void SceneMan::ClearAllMOIDDrawings() { + void SceneMan::ClearAllMOIDDrawings() { #ifdef DRAW_MOID_LAYER - m_pMOIDLayer->ClearBitmap(g_NoMOID); + m_pMOIDLayer->ClearBitmap(g_NoMOID); #else - m_MOIDsGrid.Reset(); + m_MOIDsGrid.Reset(); #endif -} + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool SceneMan::WillPenetrate(const int posX, - const int posY, - const Vector &impulse) -{ - RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); + bool SceneMan::WillPenetrate(const int posX, + const int posY, + const Vector& impulse) { + RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); - if (!m_pCurrentScene->GetTerrain()->IsWithinBounds(posX, posY)) - return false; + if (!m_pCurrentScene->GetTerrain()->IsWithinBounds(posX, posY)) + return false; - unsigned char materialID = getpixel(m_pCurrentScene->GetTerrain()->GetMaterialBitmap(), posX, posY); - float integrity = GetMaterialFromID(materialID)->GetIntegrity(); - return impulse.MagnitudeIsGreaterThan(integrity); -} + unsigned char materialID = getpixel(m_pCurrentScene->GetTerrain()->GetMaterialBitmap(), posX, posY); + float integrity = GetMaterialFromID(materialID)->GetIntegrity(); + return impulse.MagnitudeIsGreaterThan(integrity); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int SceneMan::RemoveOrphans(int posX, int posY, int radius, int maxArea, bool remove) -{ - if (radius > MAXORPHANRADIUS) - radius = MAXORPHANRADIUS; + int SceneMan::RemoveOrphans(int posX, int posY, int radius, int maxArea, bool remove) { + if (radius > MAXORPHANRADIUS) + radius = MAXORPHANRADIUS; - clear_to_color(m_pOrphanSearchBitmap, g_MaterialAir); - int area = RemoveOrphans(posX, posY, posX, posY, 0, radius, maxArea, false); - if (remove && area <= maxArea) - { clear_to_color(m_pOrphanSearchBitmap, g_MaterialAir); - RemoveOrphans(posX, posY, posX, posY, 0, radius, maxArea, true); + int area = RemoveOrphans(posX, posY, posX, posY, 0, radius, maxArea, false); + if (remove && area <= maxArea) { + clear_to_color(m_pOrphanSearchBitmap, g_MaterialAir); + RemoveOrphans(posX, posY, posX, posY, 0, radius, maxArea, true); + } + + return area; } - return area; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int SceneMan::RemoveOrphans(int posX, int posY, + int centerPosX, int centerPosY, + int accumulatedArea, int radius, int maxArea, bool remove) { + int area = 0; + int bmpX = 0; + int bmpY = 0; -int SceneMan::RemoveOrphans(int posX, int posY, - int centerPosX, int centerPosY, - int accumulatedArea, int radius, int maxArea, bool remove) -{ - int area = 0; - int bmpX = 0; - int bmpY = 0; + BITMAP* mat = m_pCurrentScene->GetTerrain()->GetMaterialBitmap(); - BITMAP * mat = m_pCurrentScene->GetTerrain()->GetMaterialBitmap(); + if (posX < 0 || posY < 0 || posX >= mat->w || posY >= mat->h) + return 0; - if (posX < 0 || posY < 0 || posX >= mat->w || posY >= mat->h) return 0; + unsigned char materialID = _getpixel(mat, posX, posY); + if (materialID == g_MaterialAir && (posX != centerPosX || posY != centerPosY)) + return 0; + else { + bmpX = posX - (centerPosX - radius / 2); + bmpY = posY - (centerPosY - radius / 2); - unsigned char materialID = _getpixel(mat, posX, posY); - if (materialID == g_MaterialAir && (posX != centerPosX || posY != centerPosY)) - return 0; - else - { - bmpX = posX - (centerPosX - radius / 2); - bmpY = posY - (centerPosY - radius / 2); - - // We reached the border of orphan-searching area and - // there are still material pixels there -> the area is not an orphaned teran piece, abort search - if (bmpX <= 0 || bmpY <= 0 || bmpX >= radius - 1 || bmpY >= radius - 1) - return MAXORPHANRADIUS * MAXORPHANRADIUS + 1; - else - // Check if pixel was already checked - { - if (_getpixel(m_pOrphanSearchBitmap, bmpX, bmpY) != g_MaterialAir) - return 0; - } - } - - _putpixel(m_pOrphanSearchBitmap, bmpX, bmpY, materialID); - area++; - - // We're clear to remove the pixel - if (remove) - { - Material const * sceneMat = GetMaterialFromID(materialID); - Material const * spawnMat; - spawnMat = sceneMat->GetSpawnMaterial() ? GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; - float sprayScale = 0.1; - Color spawnColor; - if (spawnMat->UsesOwnColor()) - spawnColor = spawnMat->GetColor(); - else - spawnColor.SetRGBWithIndex(m_pCurrentScene->GetTerrain()->GetFGColorPixel(posX, posY)); - - // No point generating a key-colored MOPixel - if (spawnColor.GetIndex() != g_MaskColor) - { - // TEST COLOR - // spawnColor = 5; - - // Get the new pixel from the pre-allocated pool, should be faster than dynamic allocation - // Density is used as the mass for the new MOPixel - float tempMax = 2.0F * sprayScale; - float tempMin = tempMax / 2.0F; - MOPixel *pixelMO = new MOPixel(spawnColor, - spawnMat->GetPixelDensity(), - Vector(posX, posY), - Vector(-RandomNum(tempMin, tempMax), - -RandomNum(tempMin, tempMax)), - new Atom(Vector(), spawnMat->GetIndex(), 0, spawnColor, 2), - 0); - - pixelMO->SetToHitMOs(spawnMat->GetIndex() == c_GoldMaterialID); - pixelMO->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(pixelMO); - pixelMO = 0; - } - m_pCurrentScene->GetTerrain()->SetFGColorPixel(posX, posY, g_MaskColor); - RegisterTerrainChange(posX, posY, 1, 1, g_MaskColor, false); - m_pCurrentScene->GetTerrain()->SetMaterialPixel(posX, posY, g_MaterialAir); - } - - int xoff[8] = { -1, 0, 1, -1, 1, -1, 0, 1}; - int yoff[8] = { -1, -1, -1, 0, 0, 1, 1, 1}; - - for (int c = 0; c < 8; c++) - { - area += RemoveOrphans(posX + xoff[c], posY + yoff[c], centerPosX, centerPosY, area, radius, maxArea, remove); - if (accumulatedArea + area > maxArea) - break; - } - - return area; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void SceneMan::RegisterTerrainChange(int x, int y, int w, int h, unsigned char color, bool back) -{ - if (!g_NetworkServer.IsServerModeEnabled()) - return; - - // Crop if it's out of scene as both the client and server will not tolerate out of bitmap coords while packing/unpacking - if (y < 0) - y = 0; - - if (y + h >= GetSceneHeight()) - h = GetSceneHeight() - y - 1; - - if (y >= GetSceneHeight() || h <= 0) - return; - - if (w == 1) - { - if (x >= GetSceneWidth()) - { - if (!SceneWrapsX()) - return; - x = x - GetSceneWidth(); - } - if (x < 0) - { - if (!SceneWrapsX()) - return; - x = GetSceneWidth() + x; - } - } - else - { - // Divide region if crossing the seam - if (x + w >= GetSceneWidth() || x < 0) - { - // Crossing right part of the scene - if (x + w >= GetSceneWidth()) + // We reached the border of orphan-searching area and + // there are still material pixels there -> the area is not an orphaned teran piece, abort search + if (bmpX <= 0 || bmpY <= 0 || bmpX >= radius - 1 || bmpY >= radius - 1) + return MAXORPHANRADIUS * MAXORPHANRADIUS + 1; + else + // Check if pixel was already checked { - // Left part, on the scene - NetworkServer::NetworkTerrainChange tc1; - tc1.x = x; - tc1.y = y; - tc1.w = GetSceneWidth() - x; - tc1.h = h; - tc1.back = back; - tc1.color = color; - g_NetworkServer.RegisterTerrainChange(tc1); - - // Discard out of scene part if scene is not wrapped - if (!SceneWrapsX()) - return; + if (_getpixel(m_pOrphanSearchBitmap, bmpX, bmpY) != g_MaterialAir) + return 0; + } + } - // Right part, out of scene - NetworkServer::NetworkTerrainChange tc2; - tc2.x = 0; - tc2.y = y; - tc2.w = w - (GetSceneWidth() - x); - tc2.h = h; - tc2.back = back; - tc2.color = color; - - g_NetworkServer.RegisterTerrainChange(tc2); - return; + _putpixel(m_pOrphanSearchBitmap, bmpX, bmpY, materialID); + area++; + + // We're clear to remove the pixel + if (remove) { + Material const* sceneMat = GetMaterialFromID(materialID); + Material const* spawnMat; + spawnMat = sceneMat->GetSpawnMaterial() ? GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; + float sprayScale = 0.1; + Color spawnColor; + if (spawnMat->UsesOwnColor()) + spawnColor = spawnMat->GetColor(); + else + spawnColor.SetRGBWithIndex(m_pCurrentScene->GetTerrain()->GetFGColorPixel(posX, posY)); + + // No point generating a key-colored MOPixel + if (spawnColor.GetIndex() != g_MaskColor) { + // TEST COLOR + // spawnColor = 5; + + // Get the new pixel from the pre-allocated pool, should be faster than dynamic allocation + // Density is used as the mass for the new MOPixel + float tempMax = 2.0F * sprayScale; + float tempMin = tempMax / 2.0F; + MOPixel* pixelMO = new MOPixel(spawnColor, + spawnMat->GetPixelDensity(), + Vector(posX, posY), + Vector(-RandomNum(tempMin, tempMax), + -RandomNum(tempMin, tempMax)), + new Atom(Vector(), spawnMat->GetIndex(), 0, spawnColor, 2), + 0); + + pixelMO->SetToHitMOs(spawnMat->GetIndex() == c_GoldMaterialID); + pixelMO->SetToGetHitByMOs(false); + g_MovableMan.AddParticle(pixelMO); + pixelMO = 0; } + m_pCurrentScene->GetTerrain()->SetFGColorPixel(posX, posY, g_MaskColor); + RegisterTerrainChange(posX, posY, 1, 1, g_MaskColor, false); + m_pCurrentScene->GetTerrain()->SetMaterialPixel(posX, posY, g_MaterialAir); + } - if (x < 0) - { - // Right part, on the scene - NetworkServer::NetworkTerrainChange tc2; - tc2.x = 0; - tc2.y = y; - tc2.w = w + x; - tc2.h = h; - tc2.back = back; - tc2.color = color; - g_NetworkServer.RegisterTerrainChange(tc2); - - // Discard out of scene part if scene is not wrapped + int xoff[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; + int yoff[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; + + for (int c = 0; c < 8; c++) { + area += RemoveOrphans(posX + xoff[c], posY + yoff[c], centerPosX, centerPosY, area, radius, maxArea, remove); + if (accumulatedArea + area > maxArea) + break; + } + + return area; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::RegisterTerrainChange(int x, int y, int w, int h, unsigned char color, bool back) { + if (!g_NetworkServer.IsServerModeEnabled()) + return; + + // Crop if it's out of scene as both the client and server will not tolerate out of bitmap coords while packing/unpacking + if (y < 0) + y = 0; + + if (y + h >= GetSceneHeight()) + h = GetSceneHeight() - y - 1; + + if (y >= GetSceneHeight() || h <= 0) + return; + + if (w == 1) { + if (x >= GetSceneWidth()) { + if (!SceneWrapsX()) + return; + x = x - GetSceneWidth(); + } + if (x < 0) { if (!SceneWrapsX()) return; + x = GetSceneWidth() + x; + } + } else { + // Divide region if crossing the seam + if (x + w >= GetSceneWidth() || x < 0) { + // Crossing right part of the scene + if (x + w >= GetSceneWidth()) { + // Left part, on the scene + NetworkServer::NetworkTerrainChange tc1; + tc1.x = x; + tc1.y = y; + tc1.w = GetSceneWidth() - x; + tc1.h = h; + tc1.back = back; + tc1.color = color; + g_NetworkServer.RegisterTerrainChange(tc1); + + // Discard out of scene part if scene is not wrapped + if (!SceneWrapsX()) + return; + + // Right part, out of scene + NetworkServer::NetworkTerrainChange tc2; + tc2.x = 0; + tc2.y = y; + tc2.w = w - (GetSceneWidth() - x); + tc2.h = h; + tc2.back = back; + tc2.color = color; + + g_NetworkServer.RegisterTerrainChange(tc2); + return; + } - // Left part, out of the scene - NetworkServer::NetworkTerrainChange tc1; - tc1.x = GetSceneWidth() + x; - tc1.y = y; - tc1.w = -x; - tc1.h = h; - tc1.back = back; - tc1.color = color; - g_NetworkServer.RegisterTerrainChange(tc1); - return; + if (x < 0) { + // Right part, on the scene + NetworkServer::NetworkTerrainChange tc2; + tc2.x = 0; + tc2.y = y; + tc2.w = w + x; + tc2.h = h; + tc2.back = back; + tc2.color = color; + g_NetworkServer.RegisterTerrainChange(tc2); + + // Discard out of scene part if scene is not wrapped + if (!SceneWrapsX()) + return; + + // Left part, out of the scene + NetworkServer::NetworkTerrainChange tc1; + tc1.x = GetSceneWidth() + x; + tc1.y = y; + tc1.w = -x; + tc1.h = h; + tc1.back = back; + tc1.color = color; + g_NetworkServer.RegisterTerrainChange(tc1); + return; + } } } - } - NetworkServer::NetworkTerrainChange tc; - tc.x = x; - tc.y = y; - tc.w = w; - tc.h = h; - tc.back = back; - tc.color = color; - g_NetworkServer.RegisterTerrainChange(tc); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::TryPenetrate(int posX, - int posY, - const Vector &impulse, - const Vector &velocity, - float &retardation, - const float airRatio, - const int numPenetrations, - const int removeOrphansRadius, - const int removeOrphansMaxArea, - const float removeOrphansRate) -{ - RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); - - if (!m_pCurrentScene->GetTerrain()->IsWithinBounds(posX, posY)) - return false; - - WrapPosition(posX, posY); - unsigned char materialID = _getpixel(m_pCurrentScene->GetTerrain()->GetMaterialBitmap(), posX, posY); - if (materialID == g_MaterialAir) - { -// RTEAbort("Why are we penetrating air??"); - return true; - } - Material const * sceneMat = GetMaterialFromID(materialID); - Material const * spawnMat; - - float sprayScale = 0.1F; - float sqrImpMag = impulse.GetSqrMagnitude(); - - // Test if impulse force is enough to penetrate - if (sqrImpMag >= (sceneMat->GetIntegrity() * sceneMat->GetIntegrity())) - { - if (numPenetrations <= 3) - { - spawnMat = sceneMat->GetSpawnMaterial() ? GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; - Color spawnColor; - if (spawnMat->UsesOwnColor()) - spawnColor = spawnMat->GetColor(); - else - spawnColor.SetRGBWithIndex(m_pCurrentScene->GetTerrain()->GetFGColorPixel(posX, posY)); - - // No point generating a key-colored MOPixel - if (spawnColor.GetIndex() != g_MaskColor) - { - // Get the new pixel from the pre-allocated pool, should be faster than dynamic allocation - // Density is used as the mass for the new MOPixel -/* MOPixel *pixelMO = dynamic_cast(MOPixel::InstanceFromPool()); - pixelMO->Create(spawnColor, - spawnMat.pixelDensity, - Vector(posX, posY), - Vector(-RandomNum((velocity.m_X * sprayScale) / 2 , velocity.m_X * sprayScale), - -RandomNum((velocity.m_Y * sprayScale) / 2 , velocity.m_Y * sprayScale)), -// -(impulse * (sprayScale * RandomNum() / spawnMat.density)), - new Atom(Vector(), spawnMat, 0, spawnColor, 2), - 0); -*/ - float tempMaxX = velocity.m_X * sprayScale; - float tempMinX = tempMaxX / 2.0F; - float tempMaxY = velocity.m_Y * sprayScale; - float tempMinY = tempMaxY / 2.0F; - MOPixel *pixelMO = new MOPixel(spawnColor, - spawnMat->GetPixelDensity(), - Vector(posX, posY), - Vector(-RandomNum(tempMinX, tempMaxX), - -RandomNum(tempMinY, tempMaxY)), -// -(impulse * (sprayScale * RandomNum() / spawnMat.density)), - new Atom(Vector(), spawnMat->GetIndex(), 0, spawnColor, 2), - 0); - -// TODO: Make material IDs more robust!") - pixelMO->SetToHitMOs(spawnMat->GetIndex() == c_GoldMaterialID); - pixelMO->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(pixelMO); - pixelMO = 0; - } - m_pCurrentScene->GetTerrain()->SetFGColorPixel(posX, posY, g_MaskColor); - RegisterTerrainChange(posX, posY, 1, 1, g_MaskColor, false); + NetworkServer::NetworkTerrainChange tc; + tc.x = x; + tc.y = y; + tc.w = w; + tc.h = h; + tc.back = back; + tc.color = color; + g_NetworkServer.RegisterTerrainChange(tc); + } - m_pCurrentScene->GetTerrain()->SetMaterialPixel(posX, posY, g_MaterialAir); - } -// TODO: Improve / tweak randomized pushing away of terrain") - else if (RandomNum() <= airRatio) - { - m_pCurrentScene->GetTerrain()->SetFGColorPixel(posX, posY, g_MaskColor); - RegisterTerrainChange(posX, posY, 1, 1, g_MaskColor, false); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::TryPenetrate(int posX, + int posY, + const Vector& impulse, + const Vector& velocity, + float& retardation, + const float airRatio, + const int numPenetrations, + const int removeOrphansRadius, + const int removeOrphansMaxArea, + const float removeOrphansRate) { + RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); + + if (!m_pCurrentScene->GetTerrain()->IsWithinBounds(posX, posY)) + return false; + + WrapPosition(posX, posY); + unsigned char materialID = _getpixel(m_pCurrentScene->GetTerrain()->GetMaterialBitmap(), posX, posY); + if (materialID == g_MaterialAir) { + // RTEAbort("Why are we penetrating air??"); + return true; + } + Material const* sceneMat = GetMaterialFromID(materialID); + Material const* spawnMat; + + float sprayScale = 0.1F; + float sqrImpMag = impulse.GetSqrMagnitude(); + + // Test if impulse force is enough to penetrate + if (sqrImpMag >= (sceneMat->GetIntegrity() * sceneMat->GetIntegrity())) { + if (numPenetrations <= 3) { + spawnMat = sceneMat->GetSpawnMaterial() ? GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; + Color spawnColor; + if (spawnMat->UsesOwnColor()) + spawnColor = spawnMat->GetColor(); + else + spawnColor.SetRGBWithIndex(m_pCurrentScene->GetTerrain()->GetFGColorPixel(posX, posY)); + + // No point generating a key-colored MOPixel + if (spawnColor.GetIndex() != g_MaskColor) { + // Get the new pixel from the pre-allocated pool, should be faster than dynamic allocation + // Density is used as the mass for the new MOPixel + /* MOPixel *pixelMO = dynamic_cast(MOPixel::InstanceFromPool()); + pixelMO->Create(spawnColor, + spawnMat.pixelDensity, + Vector(posX, posY), + Vector(-RandomNum((velocity.m_X * sprayScale) / 2 , velocity.m_X * sprayScale), + -RandomNum((velocity.m_Y * sprayScale) / 2 , velocity.m_Y * sprayScale)), + // -(impulse * (sprayScale * RandomNum() / spawnMat.density)), + new Atom(Vector(), spawnMat, 0, spawnColor, 2), + 0); + */ + float tempMaxX = velocity.m_X * sprayScale; + float tempMinX = tempMaxX / 2.0F; + float tempMaxY = velocity.m_Y * sprayScale; + float tempMinY = tempMaxY / 2.0F; + MOPixel* pixelMO = new MOPixel(spawnColor, + spawnMat->GetPixelDensity(), + Vector(posX, posY), + Vector(-RandomNum(tempMinX, tempMaxX), + -RandomNum(tempMinY, tempMaxY)), + // -(impulse * (sprayScale * RandomNum() / spawnMat.density)), + new Atom(Vector(), spawnMat->GetIndex(), 0, spawnColor, 2), + 0); + + // TODO: Make material IDs more robust!") + pixelMO->SetToHitMOs(spawnMat->GetIndex() == c_GoldMaterialID); + pixelMO->SetToGetHitByMOs(false); + g_MovableMan.AddParticle(pixelMO); + pixelMO = 0; + } + m_pCurrentScene->GetTerrain()->SetFGColorPixel(posX, posY, g_MaskColor); + RegisterTerrainChange(posX, posY, 1, 1, g_MaskColor, false); - m_pCurrentScene->GetTerrain()->SetMaterialPixel(posX, posY, g_MaterialAir); - } - - // Save the impulse force effects of the penetrating particle. -// retardation = -sceneMat.density; - retardation = -(sceneMat->GetIntegrity() / std::sqrt(sqrImpMag)); - - // If this is a scrap pixel, or there is no background pixel 'supporting' the knocked-loose pixel, make the column above also turn into particles. - if (m_ScrapCompactingHeight > 0 && (sceneMat->IsScrap() || _getpixel(m_pCurrentScene->GetTerrain()->GetBGColorBitmap(), posX, posY) == g_MaskColor)) { - // Get quicker direct access to bitmaps - BITMAP *pFGColor = m_pCurrentScene->GetTerrain()->GetFGColorBitmap(); - BITMAP *pBGColor = m_pCurrentScene->GetTerrain()->GetBGColorBitmap(); - BITMAP *pMaterial = m_pCurrentScene->GetTerrain()->GetMaterialBitmap(); - - int testMaterialID = g_MaterialAir; - MOPixel *pixelMO = 0; - Color spawnColor; - float sprayMag = std::sqrt(velocity.GetMagnitude() * sprayScale); - Vector sprayVel; - - for (int testY = posY - 1; testY > posY - m_ScrapCompactingHeight && testY >= 0; --testY) { - if ((testMaterialID = _getpixel(pMaterial, posX, testY)) != g_MaterialAir) { - sceneMat = GetMaterialFromID(testMaterialID); - - if (sceneMat->IsScrap() || _getpixel(pBGColor, posX, testY) == g_MaskColor) { - if (RandomNum() < 0.7F) { - spawnMat = sceneMat->GetSpawnMaterial() ? GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; - if (spawnMat->UsesOwnColor()) { - spawnColor = spawnMat->GetColor(); - } else { - spawnColor.SetRGBWithIndex(m_pCurrentScene->GetTerrain()->GetFGColorPixel(posX, testY)); - } - if (spawnColor.GetIndex() != g_MaskColor) { - // Send terrain pixels flying at a diminishing rate the higher the column goes. - sprayVel.SetXY(0, -sprayMag * (1.0F - (static_cast(posY - testY) / static_cast(m_ScrapCompactingHeight)))); - sprayVel.RadRotate(RandomNum(-c_HalfPI, c_HalfPI)); + m_pCurrentScene->GetTerrain()->SetMaterialPixel(posX, posY, g_MaterialAir); + } + // TODO: Improve / tweak randomized pushing away of terrain") + else if (RandomNum() <= airRatio) { + m_pCurrentScene->GetTerrain()->SetFGColorPixel(posX, posY, g_MaskColor); + RegisterTerrainChange(posX, posY, 1, 1, g_MaskColor, false); - pixelMO = new MOPixel(spawnColor, spawnMat->GetPixelDensity(), Vector(posX, testY), sprayVel, new Atom(Vector(), spawnMat->GetIndex(), 0, spawnColor, 2), 0); + m_pCurrentScene->GetTerrain()->SetMaterialPixel(posX, posY, g_MaterialAir); + } - pixelMO->SetToHitMOs(spawnMat->GetIndex() == c_GoldMaterialID); - pixelMO->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(pixelMO); - pixelMO = 0; + // Save the impulse force effects of the penetrating particle. + // retardation = -sceneMat.density; + retardation = -(sceneMat->GetIntegrity() / std::sqrt(sqrImpMag)); + + // If this is a scrap pixel, or there is no background pixel 'supporting' the knocked-loose pixel, make the column above also turn into particles. + if (m_ScrapCompactingHeight > 0 && (sceneMat->IsScrap() || _getpixel(m_pCurrentScene->GetTerrain()->GetBGColorBitmap(), posX, posY) == g_MaskColor)) { + // Get quicker direct access to bitmaps + BITMAP* pFGColor = m_pCurrentScene->GetTerrain()->GetFGColorBitmap(); + BITMAP* pBGColor = m_pCurrentScene->GetTerrain()->GetBGColorBitmap(); + BITMAP* pMaterial = m_pCurrentScene->GetTerrain()->GetMaterialBitmap(); + + int testMaterialID = g_MaterialAir; + MOPixel* pixelMO = 0; + Color spawnColor; + float sprayMag = std::sqrt(velocity.GetMagnitude() * sprayScale); + Vector sprayVel; + + for (int testY = posY - 1; testY > posY - m_ScrapCompactingHeight && testY >= 0; --testY) { + if ((testMaterialID = _getpixel(pMaterial, posX, testY)) != g_MaterialAir) { + sceneMat = GetMaterialFromID(testMaterialID); + + if (sceneMat->IsScrap() || _getpixel(pBGColor, posX, testY) == g_MaskColor) { + if (RandomNum() < 0.7F) { + spawnMat = sceneMat->GetSpawnMaterial() ? GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; + if (spawnMat->UsesOwnColor()) { + spawnColor = spawnMat->GetColor(); + } else { + spawnColor.SetRGBWithIndex(m_pCurrentScene->GetTerrain()->GetFGColorPixel(posX, testY)); + } + if (spawnColor.GetIndex() != g_MaskColor) { + // Send terrain pixels flying at a diminishing rate the higher the column goes. + sprayVel.SetXY(0, -sprayMag * (1.0F - (static_cast(posY - testY) / static_cast(m_ScrapCompactingHeight)))); + sprayVel.RadRotate(RandomNum(-c_HalfPI, c_HalfPI)); + + pixelMO = new MOPixel(spawnColor, spawnMat->GetPixelDensity(), Vector(posX, testY), sprayVel, new Atom(Vector(), spawnMat->GetIndex(), 0, spawnColor, 2), 0); + + pixelMO->SetToHitMOs(spawnMat->GetIndex() == c_GoldMaterialID); + pixelMO->SetToGetHitByMOs(false); + g_MovableMan.AddParticle(pixelMO); + pixelMO = 0; + } + RemoveOrphans(posX + testY % 2 ? -1 : 1, testY, removeOrphansRadius + 5, removeOrphansMaxArea + 10, true); } - RemoveOrphans(posX + testY % 2 ? -1 : 1, testY, removeOrphansRadius + 5, removeOrphansMaxArea + 10, true); - } - RegisterTerrainChange(posX, testY, 1, 1, g_MaskColor, false); - _putpixel(pFGColor, posX, testY, g_MaskColor); - _putpixel(pMaterial, posX, testY, g_MaterialAir); - } else { - break; + RegisterTerrainChange(posX, testY, 1, 1, g_MaskColor, false); + _putpixel(pFGColor, posX, testY, g_MaskColor); + _putpixel(pMaterial, posX, testY, g_MaterialAir); + } else { + break; + } } - } - } - } + } + } + + // Remove orphaned regions if told to by parent MO who travelled an atom which tries to penetrate terrain + if (removeOrphansRadius && removeOrphansMaxArea && removeOrphansRate > 0 && RandomNum() < removeOrphansRate) { + RemoveOrphans(posX, posY, removeOrphansRadius, removeOrphansMaxArea, true); + /*PALETTE palette; + get_palette(palette); + save_bmp("Orphan.bmp", m_pOrphanSearchBitmap, palette);*/ + } - // Remove orphaned regions if told to by parent MO who travelled an atom which tries to penetrate terrain - if (removeOrphansRadius && removeOrphansMaxArea && removeOrphansRate > 0 && RandomNum() < removeOrphansRate) - { - RemoveOrphans(posX, posY, removeOrphansRadius, removeOrphansMaxArea, true); - /*PALETTE palette; - get_palette(palette); - save_bmp("Orphan.bmp", m_pOrphanSearchBitmap, palette);*/ + return true; } + return false; + } - return true; - } - return false; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + MovableObject* SceneMan::DislodgePixel(int posX, int posY) { + int materialID = getpixel(m_pCurrentScene->GetTerrain()->GetMaterialBitmap(), posX, posY); + if (materialID <= MaterialColorKeys::g_MaterialAir) { + return nullptr; + } + const Material* sceneMat = GetMaterialFromID(static_cast(materialID)); + const Material* spawnMat = sceneMat->GetSpawnMaterial() ? GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; -MovableObject * SceneMan::DislodgePixel(int posX, int posY) { - int materialID = getpixel(m_pCurrentScene->GetTerrain()->GetMaterialBitmap(), posX, posY); - if (materialID <= MaterialColorKeys::g_MaterialAir) { - return nullptr; - } - const Material *sceneMat = GetMaterialFromID(static_cast(materialID)); - const Material *spawnMat = sceneMat->GetSpawnMaterial() ? GetMaterialFromID(sceneMat->GetSpawnMaterial()) : sceneMat; + Color spawnColor; + if (spawnMat->UsesOwnColor()) { + spawnColor = spawnMat->GetColor(); + } else { + spawnColor.SetRGBWithIndex(m_pCurrentScene->GetTerrain()->GetFGColorPixel(posX, posY)); + } + // No point generating a key-colored MOPixel. + if (spawnColor.GetIndex() == ColorKeys::g_MaskColor) { + return nullptr; + } + Atom* pixelAtom = new Atom(Vector(), spawnMat->GetIndex(), nullptr, spawnColor, 2); + MOPixel* pixelMO = new MOPixel(spawnColor, spawnMat->GetPixelDensity(), Vector(static_cast(posX), static_cast(posY)), Vector(), pixelAtom, 0); + pixelMO->SetToHitMOs(spawnMat->GetIndex() == c_GoldMaterialID); + g_MovableMan.AddParticle(pixelMO); - Color spawnColor; - if (spawnMat->UsesOwnColor()) { - spawnColor = spawnMat->GetColor(); - } else { - spawnColor.SetRGBWithIndex(m_pCurrentScene->GetTerrain()->GetFGColorPixel(posX, posY)); - } - // No point generating a key-colored MOPixel. - if (spawnColor.GetIndex() == ColorKeys::g_MaskColor) { - return nullptr; + m_pCurrentScene->GetTerrain()->SetFGColorPixel(posX, posY, ColorKeys::g_MaskColor); + RegisterTerrainChange(posX, posY, 1, 1, ColorKeys::g_MaskColor, false); + m_pCurrentScene->GetTerrain()->SetMaterialPixel(posX, posY, MaterialColorKeys::g_MaterialAir); + + return pixelMO; } - Atom *pixelAtom = new Atom(Vector(), spawnMat->GetIndex(), nullptr, spawnColor, 2); - MOPixel *pixelMO = new MOPixel(spawnColor, spawnMat->GetPixelDensity(), Vector(static_cast(posX), static_cast(posY)), Vector(), pixelAtom, 0); - pixelMO->SetToHitMOs(spawnMat->GetIndex() == c_GoldMaterialID); - g_MovableMan.AddParticle(pixelMO); - m_pCurrentScene->GetTerrain()->SetFGColorPixel(posX, posY, ColorKeys::g_MaskColor); - RegisterTerrainChange(posX, posY, 1, 1, ColorKeys::g_MaskColor, false); - m_pCurrentScene->GetTerrain()->SetMaterialPixel(posX, posY, MaterialColorKeys::g_MaterialAir); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return pixelMO; -} + void SceneMan::MakeAllUnseen(Vector pixelSize, const int team) { + RTEAssert(m_pCurrentScene, "Messing with scene before the scene exists!"); + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) + return; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + m_pCurrentScene->FillUnseenLayer(pixelSize, team); + } -void SceneMan::MakeAllUnseen(Vector pixelSize, const int team) -{ - RTEAssert(m_pCurrentScene, "Messing with scene before the scene exists!"); - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) - return; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - m_pCurrentScene->FillUnseenLayer(pixelSize, team); -} + bool SceneMan::LoadUnseenLayer(std::string bitmapPath, int team) { + ContentFile bitmapFile(bitmapPath.c_str()); + SceneLayer* pUnseenLayer = new SceneLayer(); + if (pUnseenLayer->Create(bitmapFile.GetAsBitmap(COLORCONV_NONE, false), true, Vector(), m_pCurrentScene->WrapsX(), m_pCurrentScene->WrapsY(), Vector(1.0, 1.0)) < 0) { + g_ConsoleMan.PrintString("ERROR: Loading background layer " + pUnseenLayer->GetPresetName() + "\'s data failed!"); + return false; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Pass in ownership here + m_pCurrentScene->SetUnseenLayer(pUnseenLayer, team); + return true; + } -bool SceneMan::LoadUnseenLayer(std::string bitmapPath, int team) -{ - ContentFile bitmapFile(bitmapPath.c_str()); - SceneLayer *pUnseenLayer = new SceneLayer(); - if (pUnseenLayer->Create(bitmapFile.GetAsBitmap(COLORCONV_NONE, false), true, Vector(), m_pCurrentScene->WrapsX(), m_pCurrentScene->WrapsY(), Vector(1.0, 1.0)) < 0) - { - g_ConsoleMan.PrintString("ERROR: Loading background layer " + pUnseenLayer->GetPresetName() + "\'s data failed!"); - return false; - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Pass in ownership here - m_pCurrentScene->SetUnseenLayer(pUnseenLayer, team); - return true; -} + bool SceneMan::AnythingUnseen(const int team) { + RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when checking if anything is unseen!"); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + return m_pCurrentScene->GetUnseenLayer(team) != 0; + // TODO: Actually check all pixels on the map too? + } -bool SceneMan::AnythingUnseen(const int team) -{ - RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when checking if anything is unseen!"); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return m_pCurrentScene->GetUnseenLayer(team) != 0; -// TODO: Actually check all pixels on the map too? -} + Vector SceneMan::GetUnseenResolution(const int team) const { + RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when getting unseen resolution!"); + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) + return Vector(1, 1); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + SceneLayer* pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); + if (pUnseenLayer) + return pUnseenLayer->GetScaleFactor(); -Vector SceneMan::GetUnseenResolution(const int team) const -{ - RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when getting unseen resolution!"); - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) return Vector(1, 1); + } - SceneLayer *pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); - if (pUnseenLayer) - return pUnseenLayer->GetScaleFactor(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return Vector(1, 1); -} + bool SceneMan::IsUnseen(const int posX, const int posY, const int team) { + RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when checking if a position is unseen!"); + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) + return false; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + SceneLayer* pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); + if (pUnseenLayer) { + // Translate to the scaled unseen layer's coordinates + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; + return getpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY) != g_MaskColor; + } -bool SceneMan::IsUnseen(const int posX, const int posY, const int team) -{ - RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when checking if a position is unseen!"); - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) return false; + } - SceneLayer *pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); - if (pUnseenLayer) - { - // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleFactor(); - int scaledX = posX / scale.m_X; - int scaledY = posY / scale.m_Y; - return getpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY) != g_MaskColor; - } - - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::RevealUnseen(const int posX, const int posY, const int team) -{ - RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when revealing an unseen position!"); - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) - return false; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::RevealUnseen(const int posX, const int posY, const int team) { + RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when revealing an unseen position!"); + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) + return false; + + SceneLayer* pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); + if (pUnseenLayer) { + // Translate to the scaled unseen layer's coordinates + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; + + // Make sure we're actually revealing an unseen pixel that is ON the bitmap! + int pixel = getpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY); + if (pixel != g_MaskColor && pixel != -1) { + // Add the pixel to the list of now seen pixels so it can be visually flashed + m_pCurrentScene->GetSeenPixels(team).push_back(Vector(scaledX, scaledY)); + // Clear to key color that pixel on the map so it won't be detected as unseen again + putpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY, g_MaskColor); + // Play the reveal sound, if there's not too many already revealed this frame + if (g_SettingsMan.BlipOnRevealUnseen() && m_pUnseenRevealSound && m_pCurrentScene->GetSeenPixels(team).size() < 5) + m_pUnseenRevealSound->Play(Vector(posX, posY)); + // Show that we actually cleared an unseen pixel + return true; + } + } - SceneLayer *pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); - if (pUnseenLayer) - { - // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleFactor(); - int scaledX = posX / scale.m_X; - int scaledY = posY / scale.m_Y; - - // Make sure we're actually revealing an unseen pixel that is ON the bitmap! - int pixel = getpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY); - if (pixel != g_MaskColor && pixel != -1) - { - // Add the pixel to the list of now seen pixels so it can be visually flashed - m_pCurrentScene->GetSeenPixels(team).push_back(Vector(scaledX, scaledY)); - // Clear to key color that pixel on the map so it won't be detected as unseen again - putpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY, g_MaskColor); - // Play the reveal sound, if there's not too many already revealed this frame - if (g_SettingsMan.BlipOnRevealUnseen() && m_pUnseenRevealSound && m_pCurrentScene->GetSeenPixels(team).size() < 5) - m_pUnseenRevealSound->Play(Vector(posX, posY)); - // Show that we actually cleared an unseen pixel - return true; - } - } - - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::RestoreUnseen(const int posX, const int posY, const int team) -{ - RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when making a position unseen!"); - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) return false; - - SceneLayer *pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); - if (pUnseenLayer) - { - // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleFactor(); - int scaledX = posX / scale.m_X; - int scaledY = posY / scale.m_Y; - - // Make sure we're actually revealing an unseen pixel that is ON the bitmap! - int pixel = getpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY); - if (pixel != g_BlackColor && pixel != -1) - { - // Add the pixel to the list of now seen pixels so it can be visually flashed - m_pCurrentScene->GetSeenPixels(team).push_back(Vector(scaledX, scaledY)); - // Clear to key color that pixel on the map so it won't be detected as unseen again - putpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY, g_BlackColor); - // Play the reveal sound, if there's not too many already revealed this frame - //if (g_SettingsMan.BlipOnRevealUnseen() && m_pUnseenRevealSound && m_pCurrentScene->GetSeenPixels(team).size() < 5) - // m_pUnseenRevealSound->Play(g_SceneMan.TargetDistanceScalar(Vector(posX, posY))); - // Show that we actually cleared an unseen pixel - return true; - } - } - - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void SceneMan::RevealUnseenBox(const int posX, const int posY, const int width, const int height, const int team) -{ - RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when revealing an unseen area!"); - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) - return; - - SceneLayer *pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); - if (pUnseenLayer) - { - // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleFactor(); - int scaledX = posX / scale.m_X; - int scaledY = posY / scale.m_Y; - int scaledW = width / scale.m_X; - int scaledH = height / scale.m_Y; - - // Fill the box - rectfill(pUnseenLayer->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_MaskColor); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void SceneMan::RestoreUnseenBox(const int posX, const int posY, const int width, const int height, const int team) -{ - RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when making an area unseen!"); - if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) - return; - - SceneLayer *pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); - if (pUnseenLayer) - { - // Translate to the scaled unseen layer's coordinates - Vector scale = pUnseenLayer->GetScaleFactor(); - int scaledX = posX / scale.m_X; - int scaledY = posY / scale.m_Y; - int scaledW = width / scale.m_X; - int scaledH = height / scale.m_Y; - - // Fill the box - rectfill(pUnseenLayer->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_BlackColor); - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -//TODO Every raycast should use some shared line drawing method (or maybe something more efficient if it exists, that needs looking into) instead of having a ton of duplicated code. -bool SceneMan::CastUnseenRay(int team, const Vector &start, const Vector &ray, Vector &endPos, int strengthLimit, int skip, bool reveal) -{ - if (!m_pCurrentScene->GetUnseenLayer(team)) - return false; - - int hitCount = 0, error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - bool affectedAny = false; - unsigned char materialID; - Material const * foundMaterial; - int totalStrength = 0; - // Save the projected end of the ray pos - endPos = start + ray; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - - if (delta[X] == 0 && delta[Y] == 0) - return false; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } - else { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - // Scene wrapping - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - // Reveal if we can, save the result - if (reveal) - affectedAny = RevealUnseen(intPos[X], intPos[Y], team) || affectedAny; - else - affectedAny = RestoreUnseen(intPos[X], intPos[Y], team) || affectedAny; - - // Check the strength of the terrain to see if we can penetrate further - materialID = GetTerrMatter(intPos[X], intPos[Y]); - // Get the material object - foundMaterial = GetMaterialFromID(materialID); - // Add the encountered material's strength to the tally - totalStrength += foundMaterial->GetIntegrity(); - // See if we have hit the limits of our ray's strength - if (totalStrength >= strengthLimit) - { - // Save the position of the end of the ray where blocked - endPos.SetXY(intPos[X], intPos[Y]); - break; - } - // Reset skip counter - skipped = 0; - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } - } - - return affectedAny; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::CastSeeRay(int team, const Vector &start, const Vector &ray, Vector &endPos, int strengthLimit, int skip) -{ - return CastUnseenRay(team, start, ray, endPos, strengthLimit, skip, true); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::CastUnseeRay(int team, const Vector &start, const Vector &ray, Vector &endPos, int strengthLimit, int skip) -{ - return CastUnseenRay(team, start, ray, endPos, strengthLimit, skip, false); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::CastMaterialRay(const Vector &start, const Vector &ray, unsigned char material, Vector &result, int skip, bool wrap) -{ - - int hitCount = 0, error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - bool foundPixel = false; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - - if (delta[X] == 0 && delta[Y] == 0) - return false; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } - else { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - // Scene wrapping, if necessary - if (wrap) - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - - // See if we found the looked-for pixel of the correct material - if (GetTerrMatter(intPos[X], intPos[Y]) == material) - { - // Save result and report success - foundPixel = true; - result.SetXY(intPos[X], intPos[Y]); - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - break; - } - - skipped = 0; - - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } - } - - return foundPixel; -} - - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float SceneMan::CastMaterialRay(const Vector &start, const Vector &ray, unsigned char material, int skip) -{ - Vector result; - if (CastMaterialRay(start, ray, material, result, skip)) - { - // Calculate the length between the start and the found material pixel coords - result -= start; - return result.GetMagnitude(); - } - - // Signal that we didn't hit anything - return -1; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::CastNotMaterialRay(const Vector &start, const Vector &ray, unsigned char material, Vector &result, int skip, bool checkMOs) -{ - int hitCount = 0, error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - bool foundPixel = false; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - - if (delta[X] == 0 && delta[Y] == 0) - return false; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } - else { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - // Scene wrapping, if necessary - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - - // See if we found the looked-for pixel of the correct material, - // Or an MO is blocking the way - if (GetTerrMatter(intPos[X], intPos[Y]) != material || - (checkMOs && g_SceneMan.GetMOIDPixel(intPos[X], intPos[Y], Activity::NoTeam) != g_NoMOID)) - { - // Save result and report success - foundPixel = true; - result.SetXY(intPos[X], intPos[Y]); - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - break; - } - - skipped = 0; - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } - } - - return foundPixel; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float SceneMan::CastNotMaterialRay(const Vector &start, const Vector &ray, unsigned char material, int skip, bool checkMOs) -{ - Vector result; - if (CastNotMaterialRay(start, ray, material, result, skip, checkMOs)) - { - // Calculate the length between the start and the found material pixel coords - result -= start; - return result.GetMagnitude(); - } - - // Signal that we didn't hit anything - return -1; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float SceneMan::CastStrengthSumRay(const Vector &start, const Vector &end, int skip, unsigned char ignoreMaterial) -{ - Vector ray = g_SceneMan.ShortestDistance(start, end); - float strengthSum = 0; - - int error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - bool foundPixel = false; - unsigned char materialID; - Material foundMaterial; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - - if (delta[X] == 0 && delta[Y] == 0) - return false; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } - else - { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - // Scene wrapping, if necessary - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - - // Sum all strengths - materialID = GetTerrMatter(intPos[X], intPos[Y]); - if (materialID != g_MaterialAir && materialID != ignoreMaterial) { - strengthSum += GetMaterialFromID(materialID)->GetIntegrity(); - } - - skipped = 0; - - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } - } - - return strengthSum; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float SceneMan::CastMaxStrengthRay(const Vector &start, const Vector &end, int skip, unsigned char ignoreMaterial) { - return CastMaxStrengthRayMaterial(start, end, skip, ignoreMaterial)->GetIntegrity(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -const Material * SceneMan::CastMaxStrengthRayMaterial(const Vector &start, const Vector &end, int skip, unsigned char ignoreMaterial) { - Vector ray = g_SceneMan.ShortestDistance(start, end); - const Material *strongestMaterial = GetMaterialFromID(MaterialColorKeys::g_MaterialAir); - - int error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - bool foundPixel = false; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - - if (delta[X] == 0 && delta[Y] == 0) { - return strongestMaterial; } - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } - else - { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - // Scene wrapping, if necessary - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - - // Sum all strengths - unsigned char materialID = GetTerrMatter(intPos[X], intPos[Y]); - if (materialID != g_MaterialAir && materialID != ignoreMaterial) { - const Material *foundMaterial = GetMaterialFromID(materialID); - if (foundMaterial->GetIntegrity() > strongestMaterial->GetIntegrity()) { - strongestMaterial = foundMaterial; - } - } - - skipped = 0; - - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } - } - - return strongestMaterial; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::CastStrengthRay(const Vector &start, const Vector &ray, float strength, Vector &result, int skip, unsigned char ignoreMaterial, bool wrap) -{ - int hitCount = 0, error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - bool foundPixel = false; - unsigned char materialID; - Material const * foundMaterial; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - - if (delta[X] == 0 && delta[Y] == 0) - return false; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } - else { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - // Scene wrapping, if necessary - if (wrap) - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - - materialID = GetTerrMatter(intPos[X], intPos[Y]); - // Ignore the ignore material - if (materialID != ignoreMaterial) - { - // Get the material object - foundMaterial = GetMaterialFromID(materialID); - - // See if we found a pixel of equal or more strength than the threshold - if (foundMaterial->GetIntegrity() >= strength) - { - // Save result and report success - foundPixel = true; - result.SetXY(intPos[X], intPos[Y]); - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - break; - } - } - skipped = 0; - - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } - } - - // If no pixel of sufficient strength was found, set the result to the final tried position - if (!foundPixel) - result.SetXY(intPos[X], intPos[Y]); - - return foundPixel; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::CastWeaknessRay(const Vector &start, const Vector &ray, float strength, Vector &result, int skip, bool wrap) -{ - int hitCount = 0, error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - bool foundPixel = false; - unsigned char materialID; - Material const *foundMaterial; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - - if (delta[X] == 0 && delta[Y] == 0) - return false; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } - else { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - // Scene wrapping, if necessary - if (wrap) - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - - materialID = GetTerrMatter(intPos[X], intPos[Y]); - foundMaterial = GetMaterialFromID(materialID); - - // See if we found a pixel of equal or less strength than the threshold - if (foundMaterial->GetIntegrity() <= strength) - { - // Save result and report success - foundPixel = true; - result.SetXY(intPos[X], intPos[Y]); - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - break; - } - - skipped = 0; - - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } - } - - // If no pixel of sufficient strength was found, set the result to the final tried position - if (!foundPixel) - result.SetXY(intPos[X], intPos[Y]); - - return foundPixel; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -MOID SceneMan::CastMORay(const Vector &start, const Vector &ray, MOID ignoreMOID, int ignoreTeam, unsigned char ignoreMaterial, bool ignoreAllTerrain, int skip) -{ - int hitCount = 0, error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - MOID hitMOID = g_NoMOID; - unsigned char hitTerrain = 0; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - - if (delta[X] == 0 && delta[Y] == 0) - return g_NoMOID; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } - else { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - - // Scene wrapping, if necessary - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - - // Detect MOIDs - hitMOID = GetMOIDPixel(intPos[X], intPos[Y], ignoreTeam); - if (hitMOID != g_NoMOID && hitMOID != ignoreMOID && g_MovableMan.GetRootMOID(hitMOID) != ignoreMOID) - { -#ifdef DRAW_MOID_LAYER // Unnecessary with non-drawn MOIDs - they'll be culled out at the spatial partition level. - // Check if we're supposed to ignore the team of what we hit - if (ignoreTeam != Activity::NoTeam) - { - const MovableObject *pHitMO = g_MovableMan.GetMOFromID(hitMOID); - pHitMO = pHitMO ? pHitMO->GetRootParent() : 0; - // Yup, we are supposed to ignore this! - if (pHitMO && pHitMO->IgnoresTeamHits() && pHitMO->GetTeam() == ignoreTeam) - { - ; - } - else - { - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - return hitMOID; - } - } - // Legit hit - else -#endif - { - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - return hitMOID; - } - } - - // Detect terrain hits - if (!ignoreAllTerrain) - { - hitTerrain = g_SceneMan.GetTerrMatter(intPos[X], intPos[Y]); - if (hitTerrain != g_MaterialAir && hitTerrain != ignoreMaterial) - { - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - return g_NoMOID; - } - } - - skipped = 0; - - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } - } - - // Didn't hit anything but air - return g_NoMOID; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::CastFindMORay(const Vector &start, const Vector &ray, MOID targetMOID, Vector &resultPos, unsigned char ignoreMaterial, bool ignoreAllTerrain, int skip) -{ - int hitCount = 0, error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - MOID hitMOID = g_NoMOID; - unsigned char hitTerrain = 0; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - - if (delta[X] == 0 && delta[Y] == 0) - return g_NoMOID; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) - { - increment[X] = -1; - delta[X] = -delta[X]; - } - else - increment[X] = 1; - - if (delta[Y] < 0) - { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } - else - increment[Y] = 1; - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] << 1; - delta2[Y] = delta[Y] << 1; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } - else { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - // Scene wrapping, if necessary - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - - // Detect MOIDs - hitMOID = GetMOIDPixel(intPos[X], intPos[Y], Activity::NoTeam); - if (hitMOID == targetMOID || g_MovableMan.GetRootMOID(hitMOID) == targetMOID) - { - // Found target MOID, so save result and report success - resultPos.SetXY(intPos[X], intPos[Y]); - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - return true; - } - - // Detect terrain hits - if (!ignoreAllTerrain) - { - hitTerrain = g_SceneMan.GetTerrMatter(intPos[X], intPos[Y]); - if (hitTerrain != g_MaterialAir && hitTerrain != ignoreMaterial) - { - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - return false; - } - } - - skipped = 0; - - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } - } - - // Didn't hit the target - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float SceneMan::CastObstacleRay(const Vector &start, const Vector &ray, Vector &obstaclePos, Vector &freePos, MOID ignoreMOID, int ignoreTeam, unsigned char ignoreMaterial, int skip) -{ - int hitCount = 0, error, dom, sub, domSteps, skipped = skip; - int intPos[2], delta[2], delta2[2], increment[2]; - bool hitObstacle = false; - - intPos[X] = std::floor(start.m_X); - intPos[Y] = std::floor(start.m_Y); - delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; - delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - // The fraction of a pixel that we start from, to be added to the integer result positions for accuracy - Vector startFraction(start.m_X - intPos[X], start.m_Y - intPos[Y]); - - if (delta[X] == 0 && delta[Y] == 0) { - return -1.0f; - } - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm preparation - - if (delta[X] < 0) { - increment[X] = -1; - delta[X] = -delta[X]; - } else { - increment[X] = 1; - } - - if (delta[Y] < 0) { - increment[Y] = -1; - delta[Y] = -delta[Y]; - } else { - increment[Y] = 1; - } - - // Scale by 2, for better accuracy of the error at the first pixel - delta2[X] = delta[X] * 2; - delta2[Y] = delta[Y] * 2; - - // If X is dominant, Y is submissive, and vice versa. - if (delta[X] > delta[Y]) { - dom = X; - sub = Y; - } else { - dom = Y; - sub = X; - } - - error = delta2[sub] - delta[dom]; - - ///////////////////////////////////////////////////// - // Bresenham's line drawing algorithm execution - - for (domSteps = 0; domSteps < delta[dom]; ++domSteps) - { - intPos[dom] += increment[dom]; - if (error >= 0) - { - intPos[sub] += increment[sub]; - error -= delta2[dom]; - } - error += delta2[sub]; - - // Only check pixel if we're not due to skip any, or if this is the last pixel - if (++skipped > skip || domSteps + 1 == delta[dom]) - { - // Scene wrapping, if necessary - g_SceneMan.WrapPosition(intPos[X], intPos[Y]); - - unsigned char checkMat = GetTerrMatter(intPos[X], intPos[Y]); - MOID checkMOID = GetMOIDPixel(intPos[X], intPos[Y], ignoreTeam); - - // Translate any found MOID into the root MOID of that hit MO - if (checkMOID != g_NoMOID) - { - MovableObject *pHitMO = g_MovableMan.GetMOFromID(checkMOID); - if (pHitMO) - { - checkMOID = pHitMO->GetRootID(); -#ifdef DRAW_MOID_LAYER // Unnecessary with non-drawn MOIDs - they'll be culled out at the spatial partition level. - // Check if we're supposed to ignore the team of what we hit - if (ignoreTeam != Activity::NoTeam) - { - pHitMO = pHitMO->GetRootParent(); - // We are indeed supposed to ignore this object because of its ignoring of its specific team - if (pHitMO && pHitMO->IgnoresTeamHits() && pHitMO->GetTeam() == ignoreTeam) { - checkMOID = g_NoMOID; - } - } -#endif - } - } - - // See if we found the looked-for pixel of the correct material, - // Or an MO is blocking the way - if ((checkMat != g_MaterialAir && checkMat != ignoreMaterial) || (checkMOID != g_NoMOID && checkMOID != ignoreMOID)) { - hitObstacle = true; - obstaclePos.SetXY(intPos[X], intPos[Y]); - // Save last ray pos - s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); - break; - } else { - freePos.SetXY(intPos[X], intPos[Y]); - } - - skipped = 0; - - if (m_pDebugLayer && m_DrawRayCastVisualizations) { m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); } - } else { - freePos.SetXY(intPos[X], intPos[Y]); - } - } - - // Add the pixel fraction to the free position if there were any free pixels - if (domSteps != 0) { - freePos += startFraction; - } - - if (hitObstacle) - { - // Add the pixel fraction to the obstacle position, to acoid losing precision - obstaclePos += startFraction; - if (domSteps == 0) { - // If there was an obstacle on the start position, return 0 as the distance to obstacle - return 0.0F; - } else { - // Calculate the length between the start and the found material pixel coords - return g_SceneMan.ShortestDistance(obstaclePos, start).GetMagnitude(); - } - } - - // Didn't hit anything but air - return -1.0F; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -const Vector& SceneMan::GetLastRayHitPos() -{ - // The absolute end position of the last ray cast - return s_LastRayHitPos; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float SceneMan::FindAltitude(const Vector &from, int max, int accuracy, bool fromSceneOrbitDirection) -{ -// TODO: Also make this avoid doors - Vector temp(from); - ForceBounds(temp); - - Directions orbitDirection = Directions::Up; - if (fromSceneOrbitDirection && m_pCurrentScene) { - orbitDirection = m_pCurrentScene->GetTerrain()->GetOrbitDirection(); - } - - float yDir = max > 0 ? max : g_SceneMan.GetSceneHeight(); - yDir *= orbitDirection == Directions::Up ? 1.0 : -1.0f; - Vector direction = Vector(0, yDir); - - float result = g_SceneMan.CastNotMaterialRay(temp, direction, g_MaterialAir, accuracy); - // If we didn't find anything but air, then report max height - if (result < 0) { - result = max > 0 ? max : g_SceneMan.GetSceneHeight(); - } - - return orbitDirection == Directions::Up ? result : g_SceneMan.GetSceneHeight() - result; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::OverAltitude(const Vector &point, int threshold, int accuracy) -{ - Vector temp(point); - ForceBounds(temp); - return g_SceneMan.CastNotMaterialRay(temp, Vector(0, threshold), g_MaterialAir, accuracy) < 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Vector SceneMan::MovePointToGround(const Vector &from, int maxAltitude, int accuracy) -{ - // Todo, instead of a nograv area maybe best to tag certain areas as NoGrav. As otherwise it's tricky to keep track of when things are removed - if (m_pCurrentScene) { - Scene::Area* noGravArea = m_pCurrentScene->GetOptionalArea("NoGravityArea"); - if (noGravArea && noGravArea->IsInside(from)) { - return from; - } - } - - Vector temp(from); - ForceBounds(temp); - - float altitude = FindAltitude(temp, g_SceneMan.GetSceneHeight(), accuracy); - - // If there's no ground beneath us, do nothing - if (altitude == g_SceneMan.GetSceneHeight()) { - return temp; - } - - // Only move down if we're above the maxAltitude over the ground - Vector groundPoint(temp.m_X, temp.m_Y + (altitude > maxAltitude ? altitude - maxAltitude : 0)); - return groundPoint; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::IsWithinBounds(const int pixelX, const int pixelY, const int margin) -{ - if (m_pCurrentScene) - return m_pCurrentScene->GetTerrain()->IsWithinBounds(pixelX, pixelY, margin); - - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::ForceBounds(int &posX, int &posY) -{ - RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); - return m_pCurrentScene->GetTerrain()->ForceBounds(posX, posY); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::ForceBounds(Vector &pos) -{ - RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); - - int posX = std::floor(pos.m_X); - int posY = std::floor(pos.m_Y); - - bool wrapped = m_pCurrentScene->GetTerrain()->ForceBounds(posX, posY); - - pos.m_X = posX + (pos.m_X - std::floor(pos.m_X)); - pos.m_Y = posY + (pos.m_Y - std::floor(pos.m_Y)); - - return wrapped; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::WrapPosition(int &posX, int &posY) -{ - RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); - return m_pCurrentScene->GetTerrain()->WrapPosition(posX, posY); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::WrapPosition(Vector &pos) -{ - RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); - - int posX = std::floor(pos.m_X); - int posY = std::floor(pos.m_Y); - - bool wrapped = m_pCurrentScene->GetTerrain()->WrapPosition(posX, posY); - - pos.m_X = posX + (pos.m_X - std::floor(pos.m_X)); - pos.m_Y = posY + (pos.m_Y - std::floor(pos.m_Y)); - - return wrapped; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Vector SceneMan::SnapPosition(const Vector &pos, bool snap) -{ - Vector snappedPos = pos; - - if (snap) - { - snappedPos.m_X = std::floor((pos.m_X / SCENESNAPSIZE) + 0.5) * SCENESNAPSIZE; - snappedPos.m_Y = std::floor((pos.m_Y / SCENESNAPSIZE) + 0.5) * SCENESNAPSIZE; - } - - return snappedPos; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -Vector SceneMan::ShortestDistance(Vector pos1, Vector pos2, bool checkBounds) -{ - if (!m_pCurrentScene) - return Vector(); - - if (checkBounds) - { - WrapPosition(pos1); - WrapPosition(pos2); - } - - Vector distance = pos2 - pos1; - float sceneWidth = m_pCurrentScene->GetWidth(); - float sceneHeight = m_pCurrentScene->GetHeight(); - - if (m_pCurrentScene->GetTerrain()->WrapsX()) - { - if (distance.m_X > 0) - { - if (distance.m_X > (sceneWidth / 2)) - distance.m_X -= sceneWidth; - } - else - { - if (abs(distance.m_X) > (sceneWidth / 2)) - distance.m_X += sceneWidth; - } - } - - if (m_pCurrentScene->GetTerrain()->WrapsY()) - { - if (distance.m_Y > 0) - { - if (distance.m_Y > (sceneHeight / 2)) - distance.m_Y -= sceneHeight; - } - else - { - if (abs(distance.m_Y) > (sceneHeight / 2)) - distance.m_Y += sceneHeight; - } - } - - return distance; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float SceneMan::ShortestDistanceX(float val1, float val2, bool checkBounds, int direction) -{ - if (!m_pCurrentScene) - return 0; - - if (checkBounds) - { - int x1 = val1; - int x2 = val2; - int crap = 0; - WrapPosition(x1, crap); - WrapPosition(x2, crap); - val1 = x1; - val2 = x2; - } - - float distance = val2 - val1; - float sceneWidth = m_pCurrentScene->GetWidth(); - - if (m_pCurrentScene->GetTerrain()->WrapsX()) - { - if (distance > 0) - { - if (distance > (sceneWidth / 2)) - distance -= sceneWidth; - } - else - { - if (abs(distance) > (sceneWidth / 2)) - distance += sceneWidth; - } - - // Apply direction constraint if wrapped - if (direction > 0 && distance < 0) - distance += sceneWidth; - else if (direction < 0 && distance > 0) - distance -= sceneWidth; - } - - return distance; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -float SceneMan::ShortestDistanceY(float val1, float val2, bool checkBounds, int direction) -{ - if (!m_pCurrentScene) - return 0; - - if (checkBounds) - { - int y1 = val1; - int y2 = val2; - int crap = 0; - WrapPosition(crap, y1); - WrapPosition(crap, y2); - val1 = y1; - val2 = y2; - } - - float distance = val2 - val1; - float sceneHeight = m_pCurrentScene->GetHeight(); - - if (m_pCurrentScene->GetTerrain()->WrapsY()) - { - if (distance > 0) - { - if (distance > (sceneHeight / 2)) - distance -= sceneHeight; - } - else - { - if (abs(distance) > (sceneHeight / 2)) - distance += sceneHeight; - } - - // Apply direction constraint if wrapped - if (direction > 0 && distance < 0) - distance += sceneHeight; - else if (direction < 0 && distance > 0) - distance -= sceneHeight; - } - - return distance; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::ObscuredPoint(int x, int y, int team) -{ - bool obscured = m_pCurrentScene->GetTerrain()->GetPixel(x, y) != g_MaterialAir || GetMOIDPixel(x, y, Activity::NoTeam) != g_NoMOID; - - if (team != Activity::NoTeam) - obscured = obscured || IsUnseen(x, y, team); - - return obscured; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::WrapRect(const IntRect &wrapRect, std::list &outputList) -{ - // Always add at least one copy of the unwrapped rect - int addedTimes = 1; - outputList.push_back(wrapRect); - - // Only bother with wrap checking if the scene actually wraps around in X - if (SceneWrapsX()) - { - int sceneWidth = GetSceneWidth(); - - if (wrapRect.m_Left < 0) - { - outputList.push_back(wrapRect); - outputList.back().m_Left += sceneWidth; - outputList.back().m_Right += sceneWidth; - addedTimes++; - } - if (wrapRect.m_Right >= sceneWidth) - { - outputList.push_back(wrapRect); - outputList.back().m_Left -= sceneWidth; - outputList.back().m_Right -= sceneWidth; - addedTimes++; - } - } - - // Only bother with wrap checking if the scene actually wraps around in Y - if (SceneWrapsY()) - { - int sceneHeight = GetSceneHeight(); - - if (wrapRect.m_Top < 0) - { - outputList.push_back(wrapRect); - outputList.back().m_Top += sceneHeight; - outputList.back().m_Bottom += sceneHeight; - addedTimes++; - } - if (wrapRect.m_Bottom >= sceneHeight) - { - outputList.push_back(wrapRect); - outputList.back().m_Top -= sceneHeight; - outputList.back().m_Bottom -= sceneHeight; - addedTimes++; - } - } - - return addedTimes; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int SceneMan::WrapBox(const Box &wrapBox, std::list &outputList) -{ - // Unflip the input box, or checking will be tedious - Box flipBox(wrapBox); - flipBox.Unflip(); - - // Always add at least one copy of the unwrapped rect - int addedTimes = 1; - outputList.push_back(flipBox); - - // Only bother with wrap checking if the scene actually wraps around in X - if (SceneWrapsX()) - { - int sceneWidth = GetSceneWidth(); - - if (flipBox.m_Corner.m_X < 0) - { - outputList.push_back(flipBox); - outputList.back().m_Corner.m_X += sceneWidth; - addedTimes++; - } - if (flipBox.m_Corner.m_X + flipBox.m_Width >= sceneWidth) - { - outputList.push_back(flipBox); - outputList.back().m_Corner.m_X -= sceneWidth; - addedTimes++; - } - } - - // Only bother with wrap checking if the scene actually wraps around in Y - if (SceneWrapsY()) - { - int sceneHeight = GetSceneHeight(); - - if (flipBox.m_Corner.m_Y < 0) - { - outputList.push_back(flipBox); - outputList.back().m_Corner.m_Y += sceneHeight; - addedTimes++; - } - if (flipBox.m_Corner.m_Y + flipBox.m_Height >= sceneHeight) - { - outputList.push_back(flipBox); - outputList.back().m_Corner.m_Y -= sceneHeight; - addedTimes++; - } - } - - return addedTimes; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool SceneMan::AddSceneObject(SceneObject *sceneObject) { - bool result = false; - if (sceneObject) { - if (MovableObject *sceneObjectAsMovableObject = dynamic_cast(sceneObject)) { - return g_MovableMan.AddMO(sceneObjectAsMovableObject); - } else if (TerrainObject *sceneObjectAsTerrainObject = dynamic_cast(sceneObject)) { - result = m_pCurrentScene && sceneObjectAsTerrainObject->PlaceOnTerrain(m_pCurrentScene->GetTerrain()); - if (result) { - Box airBox(sceneObjectAsTerrainObject->GetPos() + sceneObjectAsTerrainObject->GetBitmapOffset(), static_cast(sceneObjectAsTerrainObject->GetBitmapWidth()), static_cast(sceneObjectAsTerrainObject->GetBitmapHeight())); - m_pCurrentScene->GetTerrain()->CleanAirBox(airBox, GetScene()->WrapsX(), GetScene()->WrapsY()); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::RestoreUnseen(const int posX, const int posY, const int team) { + RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when making a position unseen!"); + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) + return false; + + SceneLayer* pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); + if (pUnseenLayer) { + // Translate to the scaled unseen layer's coordinates + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; + + // Make sure we're actually revealing an unseen pixel that is ON the bitmap! + int pixel = getpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY); + if (pixel != g_BlackColor && pixel != -1) { + // Add the pixel to the list of now seen pixels so it can be visually flashed + m_pCurrentScene->GetSeenPixels(team).push_back(Vector(scaledX, scaledY)); + // Clear to key color that pixel on the map so it won't be detected as unseen again + putpixel(pUnseenLayer->GetBitmap(), scaledX, scaledY, g_BlackColor); + // Play the reveal sound, if there's not too many already revealed this frame + // if (g_SettingsMan.BlipOnRevealUnseen() && m_pUnseenRevealSound && m_pCurrentScene->GetSeenPixels(team).size() < 5) + // m_pUnseenRevealSound->Play(g_SceneMan.TargetDistanceScalar(Vector(posX, posY))); + // Show that we actually cleared an unseen pixel + return true; } } + + return false; } - delete sceneObject; - return result; -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::RevealUnseenBox(const int posX, const int posY, const int width, const int height, const int team) { + RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when revealing an unseen area!"); + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) + return; -void SceneMan::Update(int screenId) { - ZoneScoped; - - if (!m_pCurrentScene) { - return; + SceneLayer* pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); + if (pUnseenLayer) { + // Translate to the scaled unseen layer's coordinates + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; + int scaledW = width / scale.m_X; + int scaledH = height / scale.m_Y; + + // Fill the box + rectfill(pUnseenLayer->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_MaskColor); + } } - m_LastUpdatedScreen = screenId; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Vector &offset = g_CameraMan.GetOffset(screenId); - m_pMOColorLayer->SetOffset(offset); - m_pMOIDLayer->SetOffset(offset); - if (m_pDebugLayer) { - m_pDebugLayer->SetOffset(offset); - } + void SceneMan::RestoreUnseenBox(const int posX, const int posY, const int width, const int height, const int team) { + RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when making an area unseen!"); + if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) + return; - SLTerrain *terrain = m_pCurrentScene->GetTerrain(); - terrain->SetOffset(offset); - terrain->Update(); + SceneLayer* pUnseenLayer = m_pCurrentScene->GetUnseenLayer(team); + if (pUnseenLayer) { + // Translate to the scaled unseen layer's coordinates + Vector scale = pUnseenLayer->GetScaleFactor(); + int scaledX = posX / scale.m_X; + int scaledY = posY / scale.m_Y; + int scaledW = width / scale.m_X; + int scaledH = height / scale.m_Y; - // Background layers may scroll in fractions of the real offset and need special care to avoid jumping after having traversed wrapped edges, so they need the total offset without taking wrapping into account. - const Vector &unwrappedOffset = g_CameraMan.GetUnwrappedOffset(screenId); - for (SLBackground *backgroundLayer : m_pCurrentScene->GetBackLayers()) { - backgroundLayer->SetOffset(unwrappedOffset); - backgroundLayer->Update(); + // Fill the box + rectfill(pUnseenLayer->GetBitmap(), scaledX, scaledY, scaledX + scaledW, scaledY + scaledH, g_BlackColor); + } } - // Update the unseen obstruction layer for this team's screen view, if there is one. - const int teamId = g_CameraMan.GetScreenTeam(screenId); - if (SceneLayer *unseenLayer = (teamId != Activity::NoTeam) ? m_pCurrentScene->GetUnseenLayer(teamId) : nullptr) { - unseenLayer->SetOffset(offset); - } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // TODO Every raycast should use some shared line drawing method (or maybe something more efficient if it exists, that needs looking into) instead of having a ton of duplicated code. + bool SceneMan::CastUnseenRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip, bool reveal) { + if (!m_pCurrentScene->GetUnseenLayer(team)) + return false; + + int hitCount = 0, error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + bool affectedAny = false; + unsigned char materialID; + Material const* foundMaterial; + int totalStrength = 0; + // Save the projected end of the ray pos + endPos = start + ray; + + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; + + if (delta[X] == 0 && delta[Y] == 0) + return false; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } + + error = delta2[sub] - delta[dom]; - if (m_CleanTimer.GetElapsedSimTimeMS() > CLEANAIRINTERVAL) { - terrain->CleanAir(); - m_CleanTimer.Reset(); - } -} + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + // Scene wrapping + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + // Reveal if we can, save the result + if (reveal) + affectedAny = RevealUnseen(intPos[X], intPos[Y], team) || affectedAny; + else + affectedAny = RestoreUnseen(intPos[X], intPos[Y], team) || affectedAny; + + // Check the strength of the terrain to see if we can penetrate further + materialID = GetTerrMatter(intPos[X], intPos[Y]); + // Get the material object + foundMaterial = GetMaterialFromID(materialID); + // Add the encountered material's strength to the tally + totalStrength += foundMaterial->GetIntegrity(); + // See if we have hit the limits of our ray's strength + if (totalStrength >= strengthLimit) { + // Save the position of the end of the ray where blocked + endPos.SetXY(intPos[X], intPos[Y]); + break; + } + // Reset skip counter + skipped = 0; + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } + } -void SceneMan::Draw(BITMAP *targetBitmap, BITMAP *targetGUIBitmap, const Vector &targetPos, bool skipBackgroundLayers, bool skipTerrain) { - ZoneScoped; - - if (!m_pCurrentScene) { - return; + return affectedAny; } - SLTerrain *terrain = m_pCurrentScene->GetTerrain(); - // Set up the target box to draw to on the target bitmap, if it is larger than the scene in either dimension. - Box targetBox(Vector(), static_cast(targetBitmap->w), static_cast(targetBitmap->h)); - if (!terrain->WrapsX() && targetBitmap->w > GetSceneWidth()) { - targetBox.SetCorner(Vector(static_cast((targetBitmap->w - GetSceneWidth()) / 2), targetBox.GetCorner().GetY())); - targetBox.SetWidth(static_cast(GetSceneWidth())); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::CastSeeRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip) { + return CastUnseenRay(team, start, ray, endPos, strengthLimit, skip, true); } - if (!terrain->WrapsY() && targetBitmap->h > GetSceneHeight()) { - targetBox.SetCorner(Vector(targetBox.GetCorner().GetX(), static_cast((targetBitmap->h - GetSceneHeight()) / 2))); - targetBox.SetHeight(static_cast(GetSceneHeight())); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::CastUnseeRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip) { + return CastUnseenRay(team, start, ray, endPos, strengthLimit, skip, false); } - switch (m_LayerDrawMode) { - case LayerDrawMode::g_LayerTerrainMatter: - terrain->SetLayerToDraw(SLTerrain::LayerType::MaterialLayer); - terrain->Draw(targetBitmap, targetBox); - break; -#ifdef DRAW_MOID_LAYER - case LayerDrawMode::g_LayerMOID: - m_pMOIDLayer->Draw(targetBitmap, targetBox); - break; -#endif - default: - if (!skipBackgroundLayers) { - for (std::list::reverse_iterator backgroundLayer = m_pCurrentScene->GetBackLayers().rbegin(); backgroundLayer != m_pCurrentScene->GetBackLayers().rend(); ++backgroundLayer) { - (*backgroundLayer)->Draw(targetBitmap, targetBox); - } - } - if (!skipTerrain) { - terrain->SetLayerToDraw(SLTerrain::LayerType::BackgroundLayer); - terrain->Draw(targetBitmap, targetBox); - } - m_pMOColorLayer->Draw(targetBitmap, targetBox); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (!skipTerrain) { - terrain->SetLayerToDraw(SLTerrain::LayerType::ForegroundLayer); - terrain->Draw(targetBitmap, targetBox); - } - if (!g_FrameMan.IsInMultiplayerMode()) { - int teamId = g_CameraMan.GetScreenTeam(m_LastUpdatedScreen); - if (SceneLayer *unseenLayer = (teamId != Activity::NoTeam) ? m_pCurrentScene->GetUnseenLayer(teamId) : nullptr) { - unseenLayer->Draw(targetBitmap, targetBox); - } - } + bool SceneMan::CastMaterialRay(const Vector& start, const Vector& ray, unsigned char material, Vector& result, int skip, bool wrap) { - bool shouldDrawHUD = !g_FrameMan.IsHudDisabled(m_LastUpdatedScreen); - if (shouldDrawHUD) { - g_MovableMan.DrawHUD(targetGUIBitmap, targetPos, m_LastUpdatedScreen); - } + int hitCount = 0, error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + bool foundPixel = false; - g_PrimitiveMan.DrawPrimitives(m_LastUpdatedScreen, targetGUIBitmap, targetPos); + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; - if (shouldDrawHUD) { - g_ActivityMan.GetActivity()->DrawGUI(targetGUIBitmap, targetPos, m_LastUpdatedScreen); - } + if (delta[X] == 0 && delta[Y] == 0) + return false; -#ifdef DRAW_NOGRAV_BOXES - if (Scene::Area* noGravArea = m_pCurrentScene->GetArea("NoGravityArea")) { - const std::vector& boxList = noGravArea->GetBoxes(); - g_FrameMan.SetTransTableFromPreset(TransparencyPreset::MoreTrans); - drawing_mode(DRAW_MODE_TRANS, 0, 0, 0); - - std::list wrappedBoxes; - for (std::vector::const_iterator bItr = boxList.begin(); bItr != boxList.end(); ++bItr) - { - wrappedBoxes.clear(); - g_SceneMan.WrapBox(*bItr, wrappedBoxes); - - for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - Vector adjCorner = (*wItr).GetCorner() - targetPos; - rectfill(targetBitmap, adjCorner.m_X, adjCorner.m_Y, adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y + (*wItr).GetHeight(), g_RedColor); - } - } - } -#endif + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation - if (m_pDebugLayer) { - m_pDebugLayer->Draw(targetBitmap, targetBox); - } + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; - break; - } -} + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + error = delta2[sub] - delta[dom]; -void SceneMan::ClearMOColorLayer() -{ - m_pMOColorLayer->ClearBitmap(g_MaskColor); - if (m_pDebugLayer) { m_pDebugLayer->ClearBitmap(g_MaskColor); } -} + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + // Scene wrapping, if necessary + if (wrap) + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + + // See if we found the looked-for pixel of the correct material + if (GetTerrMatter(intPos[X], intPos[Y]) == material) { + // Save result and report success + foundPixel = true; + result.SetXY(intPos[X], intPos[Y]); + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + break; + } -void SceneMan::ClearSeenPixels() -{ - if (!m_pCurrentScene) - return; + skipped = 0; - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - m_pCurrentScene->ClearSeenPixels(team); -} + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + return foundPixel; + } -void SceneMan::ClearCurrentScene() { - m_pCurrentScene = nullptr; -} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float SceneMan::CastMaterialRay(const Vector& start, const Vector& ray, unsigned char material, int skip) { + Vector result; + if (CastMaterialRay(start, ray, material, result, skip)) { + // Calculate the length between the start and the found material pixel coords + result -= start; + return result.GetMagnitude(); + } -BITMAP * SceneMan::GetIntermediateBitmapForSettlingIntoTerrain(int moDiameter) const { - int bitmapSizeNeeded = static_cast(std::ceil(static_cast(moDiameter) / 16.0F)) * 16; - for (const auto &[bitmapSize, bitmapPtr] : m_IntermediateSettlingBitmaps) { - if (std::min(bitmapSize, bitmapSizeNeeded) >= bitmapSizeNeeded) { - return bitmapPtr; + // Signal that we didn't hit anything + return -1; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::CastNotMaterialRay(const Vector& start, const Vector& ray, unsigned char material, Vector& result, int skip, bool checkMOs) { + int hitCount = 0, error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + bool foundPixel = false; + + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; + + if (delta[X] == 0 && delta[Y] == 0) + return false; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } + + error = delta2[sub] - delta[dom]; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution + + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + // Scene wrapping, if necessary + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + + // See if we found the looked-for pixel of the correct material, + // Or an MO is blocking the way + if (GetTerrMatter(intPos[X], intPos[Y]) != material || + (checkMOs && g_SceneMan.GetMOIDPixel(intPos[X], intPos[Y], Activity::NoTeam) != g_NoMOID)) { + // Save result and report success + foundPixel = true; + result.SetXY(intPos[X], intPos[Y]); + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + break; + } + + skipped = 0; + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } + } + + return foundPixel; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float SceneMan::CastNotMaterialRay(const Vector& start, const Vector& ray, unsigned char material, int skip, bool checkMOs) { + Vector result; + if (CastNotMaterialRay(start, ray, material, result, skip, checkMOs)) { + // Calculate the length between the start and the found material pixel coords + result -= start; + return result.GetMagnitude(); + } + + // Signal that we didn't hit anything + return -1; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float SceneMan::CastStrengthSumRay(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial) { + Vector ray = g_SceneMan.ShortestDistance(start, end); + float strengthSum = 0; + + int error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + bool foundPixel = false; + unsigned char materialID; + Material foundMaterial; + + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; + + if (delta[X] == 0 && delta[Y] == 0) + return false; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } + + error = delta2[sub] - delta[dom]; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution + + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + // Scene wrapping, if necessary + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + + // Sum all strengths + materialID = GetTerrMatter(intPos[X], intPos[Y]); + if (materialID != g_MaterialAir && materialID != ignoreMaterial) { + strengthSum += GetMaterialFromID(materialID)->GetIntegrity(); + } + + skipped = 0; + + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } + } + + return strengthSum; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float SceneMan::CastMaxStrengthRay(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial) { + return CastMaxStrengthRayMaterial(start, end, skip, ignoreMaterial)->GetIntegrity(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + const Material* SceneMan::CastMaxStrengthRayMaterial(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial) { + Vector ray = g_SceneMan.ShortestDistance(start, end); + const Material* strongestMaterial = GetMaterialFromID(MaterialColorKeys::g_MaterialAir); + + int error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + bool foundPixel = false; + + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; + + if (delta[X] == 0 && delta[Y] == 0) { + return strongestMaterial; + } + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } + + error = delta2[sub] - delta[dom]; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution + + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + // Scene wrapping, if necessary + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + + // Sum all strengths + unsigned char materialID = GetTerrMatter(intPos[X], intPos[Y]); + if (materialID != g_MaterialAir && materialID != ignoreMaterial) { + const Material* foundMaterial = GetMaterialFromID(materialID); + if (foundMaterial->GetIntegrity() > strongestMaterial->GetIntegrity()) { + strongestMaterial = foundMaterial; + } + } + + skipped = 0; + + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } + } + + return strongestMaterial; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::CastStrengthRay(const Vector& start, const Vector& ray, float strength, Vector& result, int skip, unsigned char ignoreMaterial, bool wrap) { + int hitCount = 0, error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + bool foundPixel = false; + unsigned char materialID; + Material const* foundMaterial; + + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; + + if (delta[X] == 0 && delta[Y] == 0) + return false; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } + + error = delta2[sub] - delta[dom]; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution + + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + // Scene wrapping, if necessary + if (wrap) + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + + materialID = GetTerrMatter(intPos[X], intPos[Y]); + // Ignore the ignore material + if (materialID != ignoreMaterial) { + // Get the material object + foundMaterial = GetMaterialFromID(materialID); + + // See if we found a pixel of equal or more strength than the threshold + if (foundMaterial->GetIntegrity() >= strength) { + // Save result and report success + foundPixel = true; + result.SetXY(intPos[X], intPos[Y]); + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + break; + } + } + skipped = 0; + + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } + } + + // If no pixel of sufficient strength was found, set the result to the final tried position + if (!foundPixel) + result.SetXY(intPos[X], intPos[Y]); + + return foundPixel; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::CastWeaknessRay(const Vector& start, const Vector& ray, float strength, Vector& result, int skip, bool wrap) { + int hitCount = 0, error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + bool foundPixel = false; + unsigned char materialID; + Material const* foundMaterial; + + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; + + if (delta[X] == 0 && delta[Y] == 0) + return false; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } + + error = delta2[sub] - delta[dom]; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution + + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + // Scene wrapping, if necessary + if (wrap) + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + + materialID = GetTerrMatter(intPos[X], intPos[Y]); + foundMaterial = GetMaterialFromID(materialID); + + // See if we found a pixel of equal or less strength than the threshold + if (foundMaterial->GetIntegrity() <= strength) { + // Save result and report success + foundPixel = true; + result.SetXY(intPos[X], intPos[Y]); + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + break; + } + + skipped = 0; + + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } + } + + // If no pixel of sufficient strength was found, set the result to the final tried position + if (!foundPixel) + result.SetXY(intPos[X], intPos[Y]); + + return foundPixel; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MOID SceneMan::CastMORay(const Vector& start, const Vector& ray, MOID ignoreMOID, int ignoreTeam, unsigned char ignoreMaterial, bool ignoreAllTerrain, int skip) { + int hitCount = 0, error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + MOID hitMOID = g_NoMOID; + unsigned char hitTerrain = 0; + + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; + + if (delta[X] == 0 && delta[Y] == 0) + return g_NoMOID; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } + + error = delta2[sub] - delta[dom]; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution + + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + + // Scene wrapping, if necessary + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + + // Detect MOIDs + hitMOID = GetMOIDPixel(intPos[X], intPos[Y], ignoreTeam); + if (hitMOID != g_NoMOID && hitMOID != ignoreMOID && g_MovableMan.GetRootMOID(hitMOID) != ignoreMOID) { +#ifdef DRAW_MOID_LAYER // Unnecessary with non-drawn MOIDs - they'll be culled out at the spatial partition level. + // Check if we're supposed to ignore the team of what we hit + if (ignoreTeam != Activity::NoTeam) { + const MovableObject* pHitMO = g_MovableMan.GetMOFromID(hitMOID); + pHitMO = pHitMO ? pHitMO->GetRootParent() : 0; + // Yup, we are supposed to ignore this! + if (pHitMO && pHitMO->IgnoresTeamHits() && pHitMO->GetTeam() == ignoreTeam) { + ; + } else { + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + return hitMOID; + } + } + // Legit hit + else +#endif + { + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + return hitMOID; + } + } + + // Detect terrain hits + if (!ignoreAllTerrain) { + hitTerrain = g_SceneMan.GetTerrMatter(intPos[X], intPos[Y]); + if (hitTerrain != g_MaterialAir && hitTerrain != ignoreMaterial) { + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + return g_NoMOID; + } + } + + skipped = 0; + + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } + } + + // Didn't hit anything but air + return g_NoMOID; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::CastFindMORay(const Vector& start, const Vector& ray, MOID targetMOID, Vector& resultPos, unsigned char ignoreMaterial, bool ignoreAllTerrain, int skip) { + int hitCount = 0, error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + MOID hitMOID = g_NoMOID; + unsigned char hitTerrain = 0; + + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; + + if (delta[X] == 0 && delta[Y] == 0) + return g_NoMOID; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else + increment[X] = 1; + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else + increment[Y] = 1; + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] << 1; + delta2[Y] = delta[Y] << 1; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } + + error = delta2[sub] - delta[dom]; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution + + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + // Scene wrapping, if necessary + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + + // Detect MOIDs + hitMOID = GetMOIDPixel(intPos[X], intPos[Y], Activity::NoTeam); + if (hitMOID == targetMOID || g_MovableMan.GetRootMOID(hitMOID) == targetMOID) { + // Found target MOID, so save result and report success + resultPos.SetXY(intPos[X], intPos[Y]); + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + return true; + } + + // Detect terrain hits + if (!ignoreAllTerrain) { + hitTerrain = g_SceneMan.GetTerrMatter(intPos[X], intPos[Y]); + if (hitTerrain != g_MaterialAir && hitTerrain != ignoreMaterial) { + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + return false; + } + } + + skipped = 0; + + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } + } + + // Didn't hit the target + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float SceneMan::CastObstacleRay(const Vector& start, const Vector& ray, Vector& obstaclePos, Vector& freePos, MOID ignoreMOID, int ignoreTeam, unsigned char ignoreMaterial, int skip) { + int hitCount = 0, error, dom, sub, domSteps, skipped = skip; + int intPos[2], delta[2], delta2[2], increment[2]; + bool hitObstacle = false; + + intPos[X] = std::floor(start.m_X); + intPos[Y] = std::floor(start.m_Y); + delta[X] = std::floor(start.m_X + ray.m_X) - intPos[X]; + delta[Y] = std::floor(start.m_Y + ray.m_Y) - intPos[Y]; + // The fraction of a pixel that we start from, to be added to the integer result positions for accuracy + Vector startFraction(start.m_X - intPos[X], start.m_Y - intPos[Y]); + + if (delta[X] == 0 && delta[Y] == 0) { + return -1.0f; + } + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm preparation + + if (delta[X] < 0) { + increment[X] = -1; + delta[X] = -delta[X]; + } else { + increment[X] = 1; + } + + if (delta[Y] < 0) { + increment[Y] = -1; + delta[Y] = -delta[Y]; + } else { + increment[Y] = 1; + } + + // Scale by 2, for better accuracy of the error at the first pixel + delta2[X] = delta[X] * 2; + delta2[Y] = delta[Y] * 2; + + // If X is dominant, Y is submissive, and vice versa. + if (delta[X] > delta[Y]) { + dom = X; + sub = Y; + } else { + dom = Y; + sub = X; + } + + error = delta2[sub] - delta[dom]; + + ///////////////////////////////////////////////////// + // Bresenham's line drawing algorithm execution + + for (domSteps = 0; domSteps < delta[dom]; ++domSteps) { + intPos[dom] += increment[dom]; + if (error >= 0) { + intPos[sub] += increment[sub]; + error -= delta2[dom]; + } + error += delta2[sub]; + + // Only check pixel if we're not due to skip any, or if this is the last pixel + if (++skipped > skip || domSteps + 1 == delta[dom]) { + // Scene wrapping, if necessary + g_SceneMan.WrapPosition(intPos[X], intPos[Y]); + + unsigned char checkMat = GetTerrMatter(intPos[X], intPos[Y]); + MOID checkMOID = GetMOIDPixel(intPos[X], intPos[Y], ignoreTeam); + + // Translate any found MOID into the root MOID of that hit MO + if (checkMOID != g_NoMOID) { + MovableObject* pHitMO = g_MovableMan.GetMOFromID(checkMOID); + if (pHitMO) { + checkMOID = pHitMO->GetRootID(); +#ifdef DRAW_MOID_LAYER // Unnecessary with non-drawn MOIDs - they'll be culled out at the spatial partition level. + // Check if we're supposed to ignore the team of what we hit + if (ignoreTeam != Activity::NoTeam) { + pHitMO = pHitMO->GetRootParent(); + // We are indeed supposed to ignore this object because of its ignoring of its specific team + if (pHitMO && pHitMO->IgnoresTeamHits() && pHitMO->GetTeam() == ignoreTeam) { + checkMOID = g_NoMOID; + } + } +#endif + } + } + + // See if we found the looked-for pixel of the correct material, + // Or an MO is blocking the way + if ((checkMat != g_MaterialAir && checkMat != ignoreMaterial) || (checkMOID != g_NoMOID && checkMOID != ignoreMOID)) { + hitObstacle = true; + obstaclePos.SetXY(intPos[X], intPos[Y]); + // Save last ray pos + s_LastRayHitPos.SetXY(intPos[X], intPos[Y]); + break; + } else { + freePos.SetXY(intPos[X], intPos[Y]); + } + + skipped = 0; + + if (m_pDebugLayer && m_DrawRayCastVisualizations) { + m_pDebugLayer->SetPixel(intPos[X], intPos[Y], 13); + } + } else { + freePos.SetXY(intPos[X], intPos[Y]); + } + } + + // Add the pixel fraction to the free position if there were any free pixels + if (domSteps != 0) { + freePos += startFraction; + } + + if (hitObstacle) { + // Add the pixel fraction to the obstacle position, to acoid losing precision + obstaclePos += startFraction; + if (domSteps == 0) { + // If there was an obstacle on the start position, return 0 as the distance to obstacle + return 0.0F; + } else { + // Calculate the length between the start and the found material pixel coords + return g_SceneMan.ShortestDistance(obstaclePos, start).GetMagnitude(); + } + } + + // Didn't hit anything but air + return -1.0F; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + const Vector& SceneMan::GetLastRayHitPos() { + // The absolute end position of the last ray cast + return s_LastRayHitPos; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float SceneMan::FindAltitude(const Vector& from, int max, int accuracy, bool fromSceneOrbitDirection) { + // TODO: Also make this avoid doors + Vector temp(from); + ForceBounds(temp); + + Directions orbitDirection = Directions::Up; + if (fromSceneOrbitDirection && m_pCurrentScene) { + orbitDirection = m_pCurrentScene->GetTerrain()->GetOrbitDirection(); + } + + float yDir = max > 0 ? max : g_SceneMan.GetSceneHeight(); + yDir *= orbitDirection == Directions::Up ? 1.0 : -1.0f; + Vector direction = Vector(0, yDir); + + float result = g_SceneMan.CastNotMaterialRay(temp, direction, g_MaterialAir, accuracy); + // If we didn't find anything but air, then report max height + if (result < 0) { + result = max > 0 ? max : g_SceneMan.GetSceneHeight(); + } + + return orbitDirection == Directions::Up ? result : g_SceneMan.GetSceneHeight() - result; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::OverAltitude(const Vector& point, int threshold, int accuracy) { + Vector temp(point); + ForceBounds(temp); + return g_SceneMan.CastNotMaterialRay(temp, Vector(0, threshold), g_MaterialAir, accuracy) < 0; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Vector SceneMan::MovePointToGround(const Vector& from, int maxAltitude, int accuracy) { + // Todo, instead of a nograv area maybe best to tag certain areas as NoGrav. As otherwise it's tricky to keep track of when things are removed + if (m_pCurrentScene) { + Scene::Area* noGravArea = m_pCurrentScene->GetOptionalArea("NoGravityArea"); + if (noGravArea && noGravArea->IsInside(from)) { + return from; + } + } + + Vector temp(from); + ForceBounds(temp); + + float altitude = FindAltitude(temp, g_SceneMan.GetSceneHeight(), accuracy); + + // If there's no ground beneath us, do nothing + if (altitude == g_SceneMan.GetSceneHeight()) { + return temp; + } + + // Only move down if we're above the maxAltitude over the ground + Vector groundPoint(temp.m_X, temp.m_Y + (altitude > maxAltitude ? altitude - maxAltitude : 0)); + return groundPoint; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::IsWithinBounds(const int pixelX, const int pixelY, const int margin) { + if (m_pCurrentScene) + return m_pCurrentScene->GetTerrain()->IsWithinBounds(pixelX, pixelY, margin); + + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::ForceBounds(int& posX, int& posY) { + RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); + return m_pCurrentScene->GetTerrain()->ForceBounds(posX, posY); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::ForceBounds(Vector& pos) { + RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); + + int posX = std::floor(pos.m_X); + int posY = std::floor(pos.m_Y); + + bool wrapped = m_pCurrentScene->GetTerrain()->ForceBounds(posX, posY); + + pos.m_X = posX + (pos.m_X - std::floor(pos.m_X)); + pos.m_Y = posY + (pos.m_Y - std::floor(pos.m_Y)); + + return wrapped; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::WrapPosition(int& posX, int& posY) { + RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); + return m_pCurrentScene->GetTerrain()->WrapPosition(posX, posY); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::WrapPosition(Vector& pos) { + RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); + + int posX = std::floor(pos.m_X); + int posY = std::floor(pos.m_Y); + + bool wrapped = m_pCurrentScene->GetTerrain()->WrapPosition(posX, posY); + + pos.m_X = posX + (pos.m_X - std::floor(pos.m_X)); + pos.m_Y = posY + (pos.m_Y - std::floor(pos.m_Y)); + + return wrapped; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Vector SceneMan::SnapPosition(const Vector& pos, bool snap) { + Vector snappedPos = pos; + + if (snap) { + snappedPos.m_X = std::floor((pos.m_X / SCENESNAPSIZE) + 0.5) * SCENESNAPSIZE; + snappedPos.m_Y = std::floor((pos.m_Y / SCENESNAPSIZE) + 0.5) * SCENESNAPSIZE; + } + + return snappedPos; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Vector SceneMan::ShortestDistance(Vector pos1, Vector pos2, bool checkBounds) { + if (!m_pCurrentScene) + return Vector(); + + if (checkBounds) { + WrapPosition(pos1); + WrapPosition(pos2); + } + + Vector distance = pos2 - pos1; + float sceneWidth = m_pCurrentScene->GetWidth(); + float sceneHeight = m_pCurrentScene->GetHeight(); + + if (m_pCurrentScene->GetTerrain()->WrapsX()) { + if (distance.m_X > 0) { + if (distance.m_X > (sceneWidth / 2)) + distance.m_X -= sceneWidth; + } else { + if (abs(distance.m_X) > (sceneWidth / 2)) + distance.m_X += sceneWidth; + } + } + + if (m_pCurrentScene->GetTerrain()->WrapsY()) { + if (distance.m_Y > 0) { + if (distance.m_Y > (sceneHeight / 2)) + distance.m_Y -= sceneHeight; + } else { + if (abs(distance.m_Y) > (sceneHeight / 2)) + distance.m_Y += sceneHeight; + } + } + + return distance; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float SceneMan::ShortestDistanceX(float val1, float val2, bool checkBounds, int direction) { + if (!m_pCurrentScene) + return 0; + + if (checkBounds) { + int x1 = val1; + int x2 = val2; + int crap = 0; + WrapPosition(x1, crap); + WrapPosition(x2, crap); + val1 = x1; + val2 = x2; + } + + float distance = val2 - val1; + float sceneWidth = m_pCurrentScene->GetWidth(); + + if (m_pCurrentScene->GetTerrain()->WrapsX()) { + if (distance > 0) { + if (distance > (sceneWidth / 2)) + distance -= sceneWidth; + } else { + if (abs(distance) > (sceneWidth / 2)) + distance += sceneWidth; + } + + // Apply direction constraint if wrapped + if (direction > 0 && distance < 0) + distance += sceneWidth; + else if (direction < 0 && distance > 0) + distance -= sceneWidth; + } + + return distance; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float SceneMan::ShortestDistanceY(float val1, float val2, bool checkBounds, int direction) { + if (!m_pCurrentScene) + return 0; + + if (checkBounds) { + int y1 = val1; + int y2 = val2; + int crap = 0; + WrapPosition(crap, y1); + WrapPosition(crap, y2); + val1 = y1; + val2 = y2; + } + + float distance = val2 - val1; + float sceneHeight = m_pCurrentScene->GetHeight(); + + if (m_pCurrentScene->GetTerrain()->WrapsY()) { + if (distance > 0) { + if (distance > (sceneHeight / 2)) + distance -= sceneHeight; + } else { + if (abs(distance) > (sceneHeight / 2)) + distance += sceneHeight; + } + + // Apply direction constraint if wrapped + if (direction > 0 && distance < 0) + distance += sceneHeight; + else if (direction < 0 && distance > 0) + distance -= sceneHeight; + } + + return distance; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::ObscuredPoint(int x, int y, int team) { + bool obscured = m_pCurrentScene->GetTerrain()->GetPixel(x, y) != g_MaterialAir || GetMOIDPixel(x, y, Activity::NoTeam) != g_NoMOID; + + if (team != Activity::NoTeam) + obscured = obscured || IsUnseen(x, y, team); + + return obscured; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::WrapRect(const IntRect& wrapRect, std::list& outputList) { + // Always add at least one copy of the unwrapped rect + int addedTimes = 1; + outputList.push_back(wrapRect); + + // Only bother with wrap checking if the scene actually wraps around in X + if (SceneWrapsX()) { + int sceneWidth = GetSceneWidth(); + + if (wrapRect.m_Left < 0) { + outputList.push_back(wrapRect); + outputList.back().m_Left += sceneWidth; + outputList.back().m_Right += sceneWidth; + addedTimes++; + } + if (wrapRect.m_Right >= sceneWidth) { + outputList.push_back(wrapRect); + outputList.back().m_Left -= sceneWidth; + outputList.back().m_Right -= sceneWidth; + addedTimes++; + } + } + + // Only bother with wrap checking if the scene actually wraps around in Y + if (SceneWrapsY()) { + int sceneHeight = GetSceneHeight(); + + if (wrapRect.m_Top < 0) { + outputList.push_back(wrapRect); + outputList.back().m_Top += sceneHeight; + outputList.back().m_Bottom += sceneHeight; + addedTimes++; + } + if (wrapRect.m_Bottom >= sceneHeight) { + outputList.push_back(wrapRect); + outputList.back().m_Top -= sceneHeight; + outputList.back().m_Bottom -= sceneHeight; + addedTimes++; + } + } + + return addedTimes; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int SceneMan::WrapBox(const Box& wrapBox, std::list& outputList) { + // Unflip the input box, or checking will be tedious + Box flipBox(wrapBox); + flipBox.Unflip(); + + // Always add at least one copy of the unwrapped rect + int addedTimes = 1; + outputList.push_back(flipBox); + + // Only bother with wrap checking if the scene actually wraps around in X + if (SceneWrapsX()) { + int sceneWidth = GetSceneWidth(); + + if (flipBox.m_Corner.m_X < 0) { + outputList.push_back(flipBox); + outputList.back().m_Corner.m_X += sceneWidth; + addedTimes++; + } + if (flipBox.m_Corner.m_X + flipBox.m_Width >= sceneWidth) { + outputList.push_back(flipBox); + outputList.back().m_Corner.m_X -= sceneWidth; + addedTimes++; + } + } + + // Only bother with wrap checking if the scene actually wraps around in Y + if (SceneWrapsY()) { + int sceneHeight = GetSceneHeight(); + + if (flipBox.m_Corner.m_Y < 0) { + outputList.push_back(flipBox); + outputList.back().m_Corner.m_Y += sceneHeight; + addedTimes++; + } + if (flipBox.m_Corner.m_Y + flipBox.m_Height >= sceneHeight) { + outputList.push_back(flipBox); + outputList.back().m_Corner.m_Y -= sceneHeight; + addedTimes++; + } + } + + return addedTimes; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool SceneMan::AddSceneObject(SceneObject* sceneObject) { + bool result = false; + if (sceneObject) { + if (MovableObject* sceneObjectAsMovableObject = dynamic_cast(sceneObject)) { + return g_MovableMan.AddMO(sceneObjectAsMovableObject); + } else if (TerrainObject* sceneObjectAsTerrainObject = dynamic_cast(sceneObject)) { + result = m_pCurrentScene && sceneObjectAsTerrainObject->PlaceOnTerrain(m_pCurrentScene->GetTerrain()); + if (result) { + Box airBox(sceneObjectAsTerrainObject->GetPos() + sceneObjectAsTerrainObject->GetBitmapOffset(), static_cast(sceneObjectAsTerrainObject->GetBitmapWidth()), static_cast(sceneObjectAsTerrainObject->GetBitmapHeight())); + m_pCurrentScene->GetTerrain()->CleanAirBox(airBox, GetScene()->WrapsX(), GetScene()->WrapsY()); + } + } + } + delete sceneObject; + return result; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::Update(int screenId) { + ZoneScoped; + + if (!m_pCurrentScene) { + return; + } + + m_LastUpdatedScreen = screenId; + + const Vector& offset = g_CameraMan.GetOffset(screenId); + m_pMOColorLayer->SetOffset(offset); + m_pMOIDLayer->SetOffset(offset); + if (m_pDebugLayer) { + m_pDebugLayer->SetOffset(offset); + } + + SLTerrain* terrain = m_pCurrentScene->GetTerrain(); + terrain->SetOffset(offset); + terrain->Update(); + + // Background layers may scroll in fractions of the real offset and need special care to avoid jumping after having traversed wrapped edges, so they need the total offset without taking wrapping into account. + const Vector& unwrappedOffset = g_CameraMan.GetUnwrappedOffset(screenId); + for (SLBackground* backgroundLayer: m_pCurrentScene->GetBackLayers()) { + backgroundLayer->SetOffset(unwrappedOffset); + backgroundLayer->Update(); + } + + // Update the unseen obstruction layer for this team's screen view, if there is one. + const int teamId = g_CameraMan.GetScreenTeam(screenId); + if (SceneLayer* unseenLayer = (teamId != Activity::NoTeam) ? m_pCurrentScene->GetUnseenLayer(teamId) : nullptr) { + unseenLayer->SetOffset(offset); + } + + if (m_CleanTimer.GetElapsedSimTimeMS() > CLEANAIRINTERVAL) { + terrain->CleanAir(); + m_CleanTimer.Reset(); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::Draw(BITMAP* targetBitmap, BITMAP* targetGUIBitmap, const Vector& targetPos, bool skipBackgroundLayers, bool skipTerrain) { + ZoneScoped; + + if (!m_pCurrentScene) { + return; + } + SLTerrain* terrain = m_pCurrentScene->GetTerrain(); + // Set up the target box to draw to on the target bitmap, if it is larger than the scene in either dimension. + Box targetBox(Vector(), static_cast(targetBitmap->w), static_cast(targetBitmap->h)); + + if (!terrain->WrapsX() && targetBitmap->w > GetSceneWidth()) { + targetBox.SetCorner(Vector(static_cast((targetBitmap->w - GetSceneWidth()) / 2), targetBox.GetCorner().GetY())); + targetBox.SetWidth(static_cast(GetSceneWidth())); + } + if (!terrain->WrapsY() && targetBitmap->h > GetSceneHeight()) { + targetBox.SetCorner(Vector(targetBox.GetCorner().GetX(), static_cast((targetBitmap->h - GetSceneHeight()) / 2))); + targetBox.SetHeight(static_cast(GetSceneHeight())); + } + + switch (m_LayerDrawMode) { + case LayerDrawMode::g_LayerTerrainMatter: + terrain->SetLayerToDraw(SLTerrain::LayerType::MaterialLayer); + terrain->Draw(targetBitmap, targetBox); + break; +#ifdef DRAW_MOID_LAYER + case LayerDrawMode::g_LayerMOID: + m_pMOIDLayer->Draw(targetBitmap, targetBox); + break; +#endif + default: + if (!skipBackgroundLayers) { + for (std::list::reverse_iterator backgroundLayer = m_pCurrentScene->GetBackLayers().rbegin(); backgroundLayer != m_pCurrentScene->GetBackLayers().rend(); ++backgroundLayer) { + (*backgroundLayer)->Draw(targetBitmap, targetBox); + } + } + if (!skipTerrain) { + terrain->SetLayerToDraw(SLTerrain::LayerType::BackgroundLayer); + terrain->Draw(targetBitmap, targetBox); + } + m_pMOColorLayer->Draw(targetBitmap, targetBox); + + if (!skipTerrain) { + terrain->SetLayerToDraw(SLTerrain::LayerType::ForegroundLayer); + terrain->Draw(targetBitmap, targetBox); + } + if (!g_FrameMan.IsInMultiplayerMode()) { + int teamId = g_CameraMan.GetScreenTeam(m_LastUpdatedScreen); + if (SceneLayer* unseenLayer = (teamId != Activity::NoTeam) ? m_pCurrentScene->GetUnseenLayer(teamId) : nullptr) { + unseenLayer->Draw(targetBitmap, targetBox); + } + } + + bool shouldDrawHUD = !g_FrameMan.IsHudDisabled(m_LastUpdatedScreen); + if (shouldDrawHUD) { + g_MovableMan.DrawHUD(targetGUIBitmap, targetPos, m_LastUpdatedScreen); + } + + g_PrimitiveMan.DrawPrimitives(m_LastUpdatedScreen, targetGUIBitmap, targetPos); + + if (shouldDrawHUD) { + g_ActivityMan.GetActivity()->DrawGUI(targetGUIBitmap, targetPos, m_LastUpdatedScreen); + } + +#ifdef DRAW_NOGRAV_BOXES + if (Scene::Area* noGravArea = m_pCurrentScene->GetArea("NoGravityArea")) { + const std::vector& boxList = noGravArea->GetBoxes(); + g_FrameMan.SetTransTableFromPreset(TransparencyPreset::MoreTrans); + drawing_mode(DRAW_MODE_TRANS, 0, 0, 0); + + std::list wrappedBoxes; + for (std::vector::const_iterator bItr = boxList.begin(); bItr != boxList.end(); ++bItr) { + wrappedBoxes.clear(); + g_SceneMan.WrapBox(*bItr, wrappedBoxes); + + for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + Vector adjCorner = (*wItr).GetCorner() - targetPos; + rectfill(targetBitmap, adjCorner.m_X, adjCorner.m_Y, adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y + (*wItr).GetHeight(), g_RedColor); + } + } + } +#endif + + if (m_pDebugLayer) { + m_pDebugLayer->Draw(targetBitmap, targetBox); + } + + break; + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::ClearMOColorLayer() { + m_pMOColorLayer->ClearBitmap(g_MaskColor); + if (m_pDebugLayer) { + m_pDebugLayer->ClearBitmap(g_MaskColor); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::ClearSeenPixels() { + if (!m_pCurrentScene) + return; + + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) + m_pCurrentScene->ClearSeenPixels(team); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SceneMan::ClearCurrentScene() { + m_pCurrentScene = nullptr; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + BITMAP* SceneMan::GetIntermediateBitmapForSettlingIntoTerrain(int moDiameter) const { + int bitmapSizeNeeded = static_cast(std::ceil(static_cast(moDiameter) / 16.0F)) * 16; + for (const auto& [bitmapSize, bitmapPtr]: m_IntermediateSettlingBitmaps) { + if (std::min(bitmapSize, bitmapSizeNeeded) >= bitmapSizeNeeded) { + return bitmapPtr; + } } + return m_IntermediateSettlingBitmaps.back().second; } - return m_IntermediateSettlingBitmaps.back().second; -} } // namespace RTE \ No newline at end of file diff --git a/Source/Managers/SceneMan.h b/Source/Managers/SceneMan.h index 7873c254de..e4a781aee1 100644 --- a/Source/Managers/SceneMan.h +++ b/Source/Managers/SceneMan.h @@ -10,7 +10,6 @@ // data@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -24,1496 +23,1405 @@ #define g_SceneMan SceneMan::Instance() -namespace RTE -{ - -class Scene; -class SceneLayer; -class SceneLayerTracked; -class SLTerrain; -class SceneObject; -class TerrainObject; -class MovableObject; -class Material; -class SoundContainer; -struct PostEffect; - -// Different modes to draw the SceneLayers in -enum LayerDrawMode -{ - g_LayerNormal = 0, - g_LayerTerrainMatter, +namespace RTE { + + class Scene; + class SceneLayer; + class SceneLayerTracked; + class SLTerrain; + class SceneObject; + class TerrainObject; + class MovableObject; + class Material; + class SoundContainer; + struct PostEffect; + + // Different modes to draw the SceneLayers in + enum LayerDrawMode { + g_LayerNormal = 0, + g_LayerTerrainMatter, #ifdef DRAW_MOID_LAYER - g_LayerMOID + g_LayerMOID #endif -}; + }; #define SCENEGRIDSIZE 24 #define SCENESNAPSIZE 12 #define MAXORPHANRADIUS 11 - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: SceneMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The singleton manager of all terrain and backgrounds in the RTE. -// Parent(s): Singleton, Serializable. -// Class history: 12/25/2001 SceneMan created. - -class SceneMan : public Singleton, public Serializable { - friend class SettingsMan; - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - SerializableClassNameGetter; - SerializableOverrideMethods; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: SceneMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a SceneMan object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - SceneMan() { m_pOrphanSearchBitmap = 0; Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~SceneMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a SceneMan object before deletion -// from system memory. -// Arguments: None. - - ~SceneMan() { Destroy(); } - - - /// - /// Makes the SceneMan object ready for use. - /// - void Initialize() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneMan object ready for use. -// Arguments: A string with the filepath to a Reader file from screen this SceneMan's -// data should be created. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(std::string readerFile); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetDefaultSceneName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the instance name of the default Scene to be loaded if nothing -// else is available. -// Arguments: The default scene instance name. -// Return value: None. - - void SetDefaultSceneName(std::string defaultSceneName) { m_DefaultSceneName = defaultSceneName; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDefaultSceneName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the name of the default A to be loaded if nothing -// else is available. -// Arguments: None. -// Return value: The default Scene instance name. - - std::string GetDefaultSceneName() const { return m_DefaultSceneName; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually loads a new Scene into memory. has to be done before using -// this object. -// Arguments: The instance of the Scene, ownership IS transferred! -// Whether the scene should actually apply all its SceneObject:s placed -// in its definition. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int LoadScene(Scene *pNewScene, bool placeObjects = true, bool placeUnits = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSceneToLoad -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Stores a Scene reference to be loaded later into the SceneMan. -// Arguments: The instance reference of the Scene, ownership IS NOT (!!) transferred! -// Whether the scene should actually apply all its SceneObject:s placed -// in its definition. -// Return value: None. - - void SetSceneToLoad(const Scene *pLoadScene, bool placeObjects = true, bool placeUnits = true) { m_pSceneToLoad = pLoadScene; m_PlaceObjects = placeObjects; m_PlaceUnits = placeUnits; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetSceneToLoad -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a scene to load later, by preset name. -// Arguments: The name of the Scene preset instance to load. -// Whether the scene should actually apply all its SceneObject:s placed -// in its definition. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int SetSceneToLoad(std::string sceneName, bool placeObjects = true, bool placeUnits = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSceneToLoad -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the stored Scene reference to be loaded later into the SceneMan. -// Arguments: None. -// Return value: The instance reference of the Scene, ownership IS NOT (!!) transferred! - - const Scene * GetSceneToLoad() { return m_pSceneToLoad; } - - /// - /// Gets whether objects are placed when the Scene is initially started. Used for saving/loading games. - /// - /// Whether objects are placed when the Scene is initially started. - bool GetPlaceObjectsOnLoad() const { return m_PlaceObjects; } - - /// - /// Gets whether units are placed when the Scene is initially started. Used for saving/loading games. - /// - /// Whether units are placed when the Scene is initially started. - bool GetPlaceUnitsOnLoad() const { return m_PlaceUnits; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Actually loads the Scene set to be loaded in SetSceneToLoad. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int LoadScene(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads a Scene right now, by preset name. -// Arguments: The name of the Scene preset instance to load. -// Whether the scene should actually apply all its SceneObject:s placed -// in its definition. -// Whether the scene should actually deploy all units placed in its definition. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int LoadScene(std::string sceneName, bool placeObjects = true, bool placeUnits = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads a Scene right now, by preset name. -// Arguments: The name of the Scene preset instance to load. -// Whether the scene should actually apply all its SceneObject:s placed -// in its definition. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int LoadScene(std::string sceneName, bool placeObjects = true) { return LoadScene(sceneName, placeObjects, true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire SceneMan, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneMan object. -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the currently loaded scene, if any. -// Arguments: None. -// Return value: The scene, ownership IS NOT TRANSFERRED! - - Scene * GetScene() const { return m_pCurrentScene; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSceneDim -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total dimensions (width and height) of the scene, in pixels. -// Arguments: None. -// Return value: A Vector describing the scene dimensions. - - Vector GetSceneDim() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSceneWidth -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total width of the scene, in pixels. -// Arguments: None. -// Return value: An int describing the scene width. - - int GetSceneWidth() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSceneHeight -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total height of the scene, in pixels. -// Arguments: None. -// Return value: An int describing the scene width. - - int GetSceneHeight() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaterialPalette -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets access to the whole material palette array of 256 entries. -// Arguments: None. -// Return value: A const reference to the material palette array. - - const std::array & GetMaterialPalette() const { return m_apMatPalette; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific material by name. Ownership is NOT transferred! -// Arguments: The string name of the Material to get. -// Return value: A pointer to the requested material, or 0 if no material with that -// name was found. - - Material const * GetMaterial(const std::string &matName); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaterialFromID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific material from the material palette. Ownership is NOT -// transferred! -// Arguments: The unsigned char index specifying screen material to get (0-255). -// Return value: A reference to the requested material. OWNERSHIP IS NOT TRANSFERRED! - - Material const * GetMaterialFromID(unsigned char screen) { return screen >= 0 && screen < c_PaletteEntriesNumber && m_apMatPalette[screen] ? m_apMatPalette[screen] : m_apMatPalette[g_MaterialAir]; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SceneWrapsX -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the scene wraps its scrolling around the X axis. -// Arguments: None. -// Return value: Whether the scene wraps around the X axis or not. - - bool SceneWrapsX() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SceneWrapsY -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the scene wraps its scrolling around the Y axis. -// Arguments: None. -// Return value: Whether the scene wraps around the Y axis or not. - - bool SceneWrapsY() const; - - /// - /// Gets the orbit direction for the current scene. - /// - /// The orbit direction for the current scene. - Directions GetSceneOrbitDirection() const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTerrain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the SLTerrain, or 0 if no scene is loaded. -// Arguments: None. -// Return value: A pointer to the SLTerrain. Ownership is NOT transferred! - - SLTerrain * GetTerrain(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMOColorBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the bitmap of the intermediary collection SceneLayer that all -// MovableObject:s draw themselves onto before it itself gets drawn onto -// the screen back buffer. -// Arguments: None. -// Return value: A BITMAP pointer to the MO bitmap. Ownership is NOT transferred! - - BITMAP * GetMOColorBitmap() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDebugBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the bitmap of the SceneLayer that debug graphics is drawn onto. -// Will only return valid BITMAP if building with DEBUG_BUILD. -// Arguments: None. -// Return value: A BITMAP pointer to the debug bitmap. Ownership is NOT transferred! - - BITMAP * GetDebugBitmap() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMOIDBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the bitmap of the SceneLayer that all MovableObject:s draw thir -// current (for the frame only!) MOID's onto. -// Arguments: None. -// Return value: A BITMAP pointer to the MO bitmap. Ownership is NOT transferred! - - BITMAP * GetMOIDBitmap() const; - -// TEMP! -////////////////////////////////////////////////////////////////////////////////////////// -// Method: MOIDClearCheck -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes sure the MOID bitmap layer is completely of NoMOID color. -// If found to be not, dumps MOID layer and the FG actor color layer for -// debugging. -// Arguments: None. -// Return value: Was it clear? - - bool MOIDClearCheck(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLayerDrawMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current drawing mode of the SceneMan. -// Arguments: None. -// Return value: The current layer draw mode, see the LayerDrawMode enumeration for the -// different possible mode settings. - - int GetLayerDrawMode() const { return m_LayerDrawMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTerrMatter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets a specific pixel from the total material representation of -// this Scene. LockScene() must be called before using this method. -// Arguments: The X and Y coordinates of screen material pixel to get. -// Return value: An unsigned char specifying the requested pixel's material index. - - unsigned char GetTerrMatter(int pixelX, int pixelY); - - - /// - /// Gets a MOID from pixel coordinates in the Scene. LockScene() must be called before using this method. - /// - /// The X coordinate of the Scene pixel to test. - /// The Y coordinate of the Scene pixel to test. - /// The team to ignore. - /// The MOID currently at the specified pixel coordinates. - MOID GetMOIDPixel(int pixelX, int pixelY, int ignoreTeam); - - /// - /// Gets a MOID from pixel coordinates in the Scene. LockScene() must be called before using this method. - /// - /// The X coordinate of the Scene pixel to test. - /// The Y coordinate of the Scene pixel to test. - /// The MOID currently at the specified pixel coordinates. - MOID GetMOIDPixel(int pixelX, int pixelY) { return GetMOIDPixel(pixelX, pixelY, Activity::NoTeam); } - - /// - /// Gets this Scene's MOID SpatialPartitionGrid. - /// - /// This Scene's MOID SpatialPartitionGrid. - const SpatialPartitionGrid & GetMOIDGrid() const { return m_MOIDsGrid; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGlobalAcc -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the global acceleration (in m/s^2) that is applied to all movable -// objects' velocities during every frame. Typically models gravity. -// Arguments: None. -// Return value: A Vector describing the global acceleration. - - Vector GetGlobalAcc() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOzPerKg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how many Ounces there are in a metric Kilogram -// Arguments: None. -// Return value: A float describing the Oz/Kg ratio. - - float GetOzPerKg() const { return 35.27396; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetKgPerOz -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets how many metric Kilograms there are in an Ounce. -// Arguments: None. -// Return value: A float describing the Kg/Oz ratio. - - float GetKgPerOz() const { return 0.02834952; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetLayerDrawMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the drawing mode of the SceneMan, to easily view what's going on -// in the different internal SceneLayer:s. -// Arguments: The layer mode to draw in, see the LayerDrawMode enumeration for the -// different possible settings. -// Return value: None. - - void SetLayerDrawMode(int mode) { m_LayerDrawMode = mode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LockScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Locks all dynamic internal scene bitmaps so that manipulaitons of the -// scene's color and matter representations can take place. -// Doing it in a separate method like this is more efficient because -// many bitmap manipulaitons can be performed between a lock and unlock. -// UnlockScene() should always be called after accesses are completed. -// Arguments: None. -// Return value: None. - - void LockScene(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UnlockScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Unlocks the scene's bitmaps and prevents access to display memory. -// Doing it in a separate method like this is more efficient because -// many bitmap accesses can be performed between a lock and an unlock. -// UnlockScene() should only be called after LockScene(). -// Arguments: None. -// Return value: None. - - void UnlockScene(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SceneIsLocked -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the entire scene is currently locked or not. -// Arguments: None. -// Return value: Whether the entire scene is currently locked or not. - - bool SceneIsLocked() const; - - /// - /// Registers an area to be drawn upon, so it can be tracked and cleared later. - /// - /// The bitmap being drawn upon. - /// The MOID, if we're drawing MOIDs. - /// The left boundary of the draw area. - /// The top boundary of the drawn area. - /// The right boundary of the draw area. - /// The bottom boundary of the draw area. - void RegisterDrawing(const BITMAP *bitmap, int moid, int left, int top, int right, int bottom); - - /// - /// Registers an area of to be drawn upon, so it can be tracked and cleared later. - /// - /// The bitmap being drawn upon. - /// The MOID, if we're drawing MOIDs. - /// The centre position of the drawn area. - /// The radius of the drawn area. - void RegisterDrawing(const BITMAP *bitmap, int moid, const Vector ¢er, float radius); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearAllMOIDDrawings -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all registered drawn areas of the MOID layer to the g_NoMOID -// color and clears the registrations too. Should be done each sim update. -// Arguments: None. -// Return value: None. - - void ClearAllMOIDDrawings(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WillPenetrate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Test whether a pixel of the scene would be knocked loose and -// turned into a MO by another particle of a certain material going at a -// certain velocity. Scene needs to be locked to do this! -// Arguments: The X and Y coords of the scene pixel that is collided with. -// The velocity of the incoming particle. -// The mass of the incoming particle. -// Return value: A bool indicating wether the scene pixel would be knocked loose or -// not. If the pixel location specified happens to be of the air -// material (0) false will be returned here. - - bool WillPenetrate(const int posX, - const int posY, - const Vector &velocity, - const float mass) { return WillPenetrate(posX, posY, velocity * mass); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WillPenetrate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Test whether a pixel of the scene would be knocked loose and -// turned into a MO by a certian impulse force. Scene needs to be locked -// to do this! -// Arguments: The X and Y coords of the scene pixel that is collided with. -// The impulse force vector, in Kg * m/s. -// Return value: A bool indicating wether the scene pixel would be knocked loose or -// not. If the pixel location specified happens to be of the air -// material (0) false will be returned here. - - bool WillPenetrate(const int posX, - const int posY, - const Vector &impulse); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TryPenetrate -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculate whether a pixel of the scene would be knocked loose and -// turned into a MO by another particle of a certain material going at a -// certain velocity. If so, the incoming particle will knock loose the -// specified pixel in the scene and momentarily take its place. -// Scene needs to be locked to do this! -// Arguments: The X and Y coord of the scene pixel that is to be collided with. -// The impulse force exerted on the terrain pixel. If this magnitude -// exceeds the strength threshold of the material of the terrain pixel -// hit, the terrain pixel will be knocked loose an turned into an MO. -// The velocity of the the point hitting the terrain here. -// A float reference screen will be set to the factor with screen to -// multiply the collision velocity to get the resulting retardation -// (negative acceleration) that occurs when a penetration happens. -// The normalized probability ratio between 0.0 and 1.0 that determines -// the chance of a penetration to remove a pixel from the scene and -// thus replace it with and air pixel. 1.0 = always, 0.0 = never. -// How many consecutive penetrations in a row immediately before this try. -// The size of the area to look for orphaned terrain elements. -// Max area or orphaned area to remove. -// Orphan area removal trigger rate. -// Return value: A bool indicating wether the scene pixel was knocked loose or not. -// If the pixel location specified happens to be of the air material (0) -// false will be returned here. - - bool TryPenetrate(int posX, - int posY, - const Vector &impulse, - const Vector &velocity, - float &retardation, - const float airRatio, - const int numPenetrations = 0, - const int removeOrphansRadius = 0, - const int removeOrphansMaxArea = 0, - const float removeOrphansRate = 0.0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveOrphans -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the area of an orphaned region at specified coordinates. -// Arguments: Coordinates to check for region, whether the orphaned region should be converted into MOPixels and region removed. -// Area of orphaned object calculated during recursve function call to check if we're out of limits -// Size of the are to look for orphaned objects -// Max area of orphaned object to remove -// Whether to actually remove orphaned pixels or not -// Whether to clear internal terrain tracking bitmap or not -// Return value: The area of orphaned region at posX,posY - - int RemoveOrphans(int posX, int posY, int radius, int maxArea, bool remove = false); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveOrphans -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the area of an orphaned region at specified coordinates. -// Arguments: Coordinates to check for region, whether the orphaned region should be converted into MOPixels and region removed. -// Coordinates of initial terrain penetration to check, which serves as a center of orphaned object detection. -// Area of orphaned object calculated during recursve function call to check if we're out of limits -// Size of the are to look for orphaned objects -// Max area of orphaned object to remove -// Whether to actually remove orphaned pixels or not -// Whether to clear internal terrain tracking bitmap or not -// Return value: The area of orphaned region at posX,posY - - int RemoveOrphans(int posX, - int posY, - int centerPosX, - int centerPosY, - int accumulatedArea, - int radius, - int maxArea, - bool remove = false); - - /// - /// Removes a pixel from the terrain and adds it to MovableMan. - /// - /// The X coordinate of the terrain pixel. - /// The Y coordinate of the terrain pixel. - /// The newly dislodged pixel, if one was found. - MovableObject * DislodgePixel(int posX, int posY); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: MakeAllUnseen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets one team's view of the scene to be unseen, using a generated map -// of a specific resolution chunkiness. -// Arguments: The dimensions of the pixels that should make up the unseen layer. -// The team we're talking about. -// Return value: None. - - void MakeAllUnseen(Vector pixelSize, const int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: MakeAllSeen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets one team's view of the scene to be all seen. -// Arguments: The team we're talking about. -// Return value: None. - - void MakeAllSeen(const int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadUnseenLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads a bitmap from file and use it as the unseen layer for a team. -// Arguments: The path to the bitmap to use as the unseen layer. -// Which team we're talking about. -// Return value: Whether the loading was successful or not. - - bool LoadUnseenLayer(std::string bitmapPath, const int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AnythingUnseen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a team has anything still unseen on the scene. -// Arguments: The team we're talking about. -// Return value: A bool indicating whether that team has anyhting yet unseen. - - bool AnythingUnseen(const int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetUnseenResolution -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows what the resolution factor of the unseen map to the entire Scene -// is, in both axes. -// Arguments: The team we're talking about. -// Return value: A vector witht he factors in each element representing the factors. - - Vector GetUnseenResolution(const int team) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsUnseen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether a pixel is in an unseen area on of a specific team. -// Arguments: The X and Y coords of the scene pixel that is to be checked. -// The team we're talking about. -// Return value: A bool indicating whether that point is yet unseen. - - bool IsUnseen(const int posX, const int posY, const int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RevealUnseen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reveals a pixel on the unseen map for a specific team, if there is any. -// Arguments: The X and Y coord of the scene pixel that is to be revealed. -// The team to reveal for. -// Return value: A bool indicating whether there was an unseen pixel revealed there. - - bool RevealUnseen(const int posX, const int posY, const int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RestoreUnseen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Hides a pixel on the unseen map for a specific team, if there is any. -// Arguments: The X and Y coord of the scene pixel that is to be revealed. -// The team to hide for. -// Return value: A bool indicating whether there was a seen pixel hidden there. - - bool RestoreUnseen(const int posX, const int posY, const int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RevealUnseenBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reveals a box on the unseen map for a specific team, if there is any. -// Arguments: The X and Y coords of the upper left corner of the box to be revealed. -// The width and height of the box to be revealed, in scene units (pixels) -// The team to reveal for. -// Return value: None. - - void RevealUnseenBox(const int posX, const int posY, const int width, const int height, const int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RestoreUnseenBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Restores a box on the unseen map for a specific team, if there is any. -// Arguments: The X and Y coords of the upper left corner of the box to be revealed. -// The width and height of the box to be restored, in scene units (pixels) -// The team to restore for. -// Return value: None. - - void RestoreUnseenBox(const int posX, const int posY, const int width, const int height, const int team); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastUnseenRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and reveals or hides pixels on the unseen layer of a team -// as long as the accumulated material strengths traced through the terrain -// don't exceed a specific value. -// Arguments: The team to see for. -// The starting position. -// The vector to trace along. -// A Vector that will be set to the position of where the sight ray was -// terminated. If it reached the end, it will be set to the end of the ray. -// The material strength limit where -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Whether the ray should reveal or restore unseen layer -// Return value: Whether any unseen pixels were revealed as a result of this seeing. - - bool CastUnseenRay(int team, const Vector &start, const Vector &ray, Vector &endPos, int strengthLimit, int skip, bool reveal); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastSeeRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and reveals pixels on the unseen layer of a team -// as long as the accumulated material strengths traced through the terrain -// don't exceed a specific value. -// Arguments: The team to see for. -// The starting position. -// The vector to trace along. -// A Vector that will be set to the position of where the sight ray was -// terminated. If it reached the end, it will be set to the end of the ray. -// The material strength limit where -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Return value: Whether any unseen pixels were revealed as a result of this seeing. - - bool CastSeeRay(int team, const Vector &start, const Vector &ray, Vector &endPos, int strengthLimit, int skip = 0); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastUnseeRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and hides pixels on the unseen layer of a team -// as long as the accumulated material strengths traced through the terrain -// don't exceed a specific value. -// Arguments: The team to see for. -// The starting position. -// The vector to trace along. -// A Vector that will be set to the position of where the sight ray was -// terminated. If it reached the end, it will be set to the end of the ray. -// The material strength limit where -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Return value: Whether any unseen pixels were revealed as a result of this seeing. - - bool CastUnseeRay(int team, const Vector &start, const Vector &ray, Vector &endPos, int strengthLimit, int skip = 0); - - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastMaterialRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and gets the location of the first encountered -// pixel of a specific material in the terrain. -// Arguments: The starting position. -// The vector to trace along. -// The material ID to look for. -// A reference to the vector screen will be filled out with the absolute -// location of the found terrain pixel of the above material. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Whetehr the ray should wrap around the scene if it crosses a seam. -// Return value: Whether the material was found along the ray. If not, the fourth -// parameter will not have been altered (and may still not be 0!) - - bool CastMaterialRay(const Vector &start, const Vector &ray, unsigned char material, Vector &result, int skip = 0, bool wrap = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastMaterialRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and returns how far along that ray there is an -// encounter with a pixel of a specific material in the terrain. -// Arguments: The starting position. -// The vector to trace along. -// The material ID to look for. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Return value: How far along, in pixel units, the ray the material pixel was encountered. -// If no pixel of the right material was found, < 0 is returned. - - float CastMaterialRay(const Vector &start, const Vector &ray, unsigned char material, int skip = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastNotMaterialRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and gets the location of the first encountered -// pixel that is NOT of a specific material in the scene's terrain. -// Arguments: The starting position. -// The vector to trace along. -// The material ID to find something OTHER than. -// A reference to the vector screen will be filled out with the absolute -// location of the found terrain pixel of the above material. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Whether to check for MO layer collisions as well, not just terrain. -// Return value: Whether the a pixel other than the material was found along the ray. -// If not, the fourth parameter will not have been altered (and may still not be 0!) - - bool CastNotMaterialRay(const Vector &start, const Vector &ray, unsigned char material, Vector &result, int skip = 0, bool checkMOs = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastNotMaterialRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and returns how far along that ray there is an -// encounter with a pixel of OTHER than a specific material in the terrain. -// Arguments: The starting position. -// The vector to trace along. -// The material ID to find something OTHER than. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Whether to check for MO layer collisions as well, not just terrain. -// Return value: How far along, in pixel units, the ray the pixel of any other material -// was encountered. If no pixel of the right material was found, < 0 is returned. - - float CastNotMaterialRay(const Vector &start, const Vector &ray, unsigned char material, int skip = 0, bool checkMOs = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastStrengthSumRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and returns how the sum of all encountered pixels' -// material strength values. This will take wrapping into account. -// Arguments: The starting position. -// The ending position. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// A material ID to ignore, IN ADDITION to Air. -// Return value: The sum of all encountered pixels' material strength vales. So if it was -// all Air, then 0 is returned (Air's strength value is 0). - - float CastStrengthSumRay(const Vector &start, const Vector &end, int skip = 0, unsigned char ignoreMaterial = g_MaterialAir); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastMaxStrengthRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and returns the strongest of all encountered pixels' -// material strength values. -// This will take wrapping into account. -// Arguments: The starting position. -// The ending position. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// A material ID to ignore, IN ADDITION to Air. This defaults to doors, for legacy script purposes -// Return value: The max of all encountered pixels' material strength vales. So if it was -// all Air, then 0 is returned (Air's strength value is 0). - - // We use two accessors instead of default parameters, for lua compat - float CastMaxStrengthRay(const Vector &start, const Vector &end, int skip, unsigned char ignoreMaterial); - float CastMaxStrengthRay(const Vector &start, const Vector &end, int skip) { return CastMaxStrengthRay(start, end, skip, g_MaterialDoor); }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastMaxStrengthRayMaterial -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and returns the strongest of all encountered pixels' materials -// This will take wrapping into account. -// Arguments: The starting position. -// The ending position. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// A material ID to ignore, IN ADDITION to Air. -// Return value: The strongest material encountered - const Material * CastMaxStrengthRayMaterial(const Vector &start, const Vector &end, int skip, unsigned char ignoreMaterial); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastStrengthRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and shows where along that ray there is an -// encounter with a pixel of a material with strength more than or equal -// to a specific value. -// Arguments: The starting position. -// The vector to trace along. -// The strength value of screen any found to be equal or more than will -// terminate the ray. -// A reference to the vector screen will be filled out with the absolute -// location of the found terrain pixel of less than or equal to above strength. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// A material ID to ignore, IN ADDITION to Air. -// Whetehr the ray should wrap around the scene if it crosses a seam. -// Return value: Whether a material of equal or more strength was found along the ray. -// If not, the fourth parameter have been set to last position of the ray. - - bool CastStrengthRay(const Vector &start, const Vector &ray, float strength, Vector &result, int skip = 0, unsigned char ignoreMaterial = g_MaterialAir, bool wrap = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastWeaknessRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and shows where along that ray there is an -// encounter with a pixel of a material with strength less than or equal -// to a specific value. -// Arguments: The starting position. -// The vector to trace along. -// The strength value of screen any found to be equal or less than will -// terminate the ray. -// A reference to the vector screen will be filled out with the absolute -// location of the found terrain pixel of less than or equal to above strength. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Whetehr the ray should wrap around the scene if it crosses a seam. -// Return value: Whether a material of equal or less strength was found along the ray. -// If not, the fourth parameter have been set to last position of the ray. - - bool CastWeaknessRay(const Vector &start, const Vector &ray, float strength, Vector &result, int skip = 0, bool wrap = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastMORay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and returns MOID of the first non-ignored -// non-NoMOID MO encountered. If a non-air terrain pixel is encountered -// first, g_NoMOID will be returned. -// Arguments: The starting position. -// The vector to trace along. -// An MOID to ignore. Any child MO's of this MOID will also be ignored. -// To enable ignoring of all MOIDs associated with an object of a specific -// team which also has team ignoring enabled itself. -// A specific material ID to ignore hits with. -// Whether to ignore all terrain hits or not. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Return value: The MOID of the hit non-ignored MO, or g_NoMOID if terrain or no MO was hit. - - MOID CastMORay(const Vector &start, const Vector &ray, MOID ignoreMOID = g_NoMOID, int ignoreTeam = Activity::NoTeam, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false, int skip = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastFindMORay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and shows where a specific MOID has been found. -// Arguments: The starting position. -// The vector to trace along. -// An MOID to find. Any child MO's of this MOID will also be found. ------------ ??? -// A reference to the vector screen will be filled out with the absolute -// location of the found MO pixel of the above MOID. -// A specific material ID to ignore hits with. -// Whether to ignore all terrain hits or not. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Return value: Whether the target MOID was found along the ray or not. - - bool CastFindMORay(const Vector &start, const Vector &ray, MOID targetMOID, Vector &resultPos, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false, int skip = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CastObstacleRay -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Traces along a vector and returns the length of how far the trace went -// without hitting any non-ignored terrain material or MOID at all. -// Arguments: The starting position. -// The vector to trace along. -// A reference to the vector screen will be filled out with the absolute -// location of the first obstacle, or the end of the ray if none was hit. -// A reference to the vector screen will be filled out with the absolute -// location of the last free position before hitting an obstacle, or the -// end of the ray if none was hit. This is only altered if thre are any -// free pixels encountered. -// An MOID to ignore. Any child MO's of this MOID will also be ignored. -// To enable ignoring of all MOIDs associated with an object of a specific -// team which also has team ignoring enabled itself. -// A specific material ID to ignore hits with. -// For every pixel checked along the line, how many to skip between them -// for optimization reasons. 0 = every pixel is checked. -// Return value: How far along, in pixel units, the ray the pixel of any obstacle was -// encountered. If no pixel of the right material was found, < 0 is returned. -// If an obstacle on the starting position was encountered, 0 is returned. - - float CastObstacleRay(const Vector &start, const Vector &ray, Vector &obstaclePos, Vector &freePos, MOID ignoreMOID = g_NoMOID, int ignoreTeam = Activity::NoTeam, unsigned char ignoreMaterial = 0, int skip = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLastRayHitPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the abosulte pos of where the last cast ray hit somehting. -// Arguments: None. -// Return value: A vector with the absolute pos of where the last ray cast hit somehting. - - const Vector& GetLastRayHitPos(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FindAltitude -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the altitide of a certain point above the terrain, measured -// in pixels. -// Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. -// The accuracy within screen measurement is acceptable. Higher number -// here means less calculation. -// Return value: The altitude over the terrain, in pixels. - - float FindAltitude(const Vector &from, int max, int accuracy, bool fromSceneOrbitDirection); - float FindAltitude(const Vector &from, int max, int accuracy) { return FindAltitude(from, max, accuracy, false); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: OverAltitude -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the altitide of a certain point above the terrain, measured -// in pixels, and then tells if that point is over a certain value. -// Arguments: The altitude threshold you want to check for. -// The accuracy within screen measurement is acceptable. Higher number -// here means less costly. -// Return value: Whether the point is over the threshold altitude or not. - - bool OverAltitude(const Vector &point, int threshold, int accuracy = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: MovePointToGround -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes an arbitrary point in the air and calculates it to be straight -// down at a certain maximum distance from the ground. -// Arguments: The point to start from. Should be in the air, or the same point will -// be returned (null operation) -// The max altitude in px you want the point to be above the ground. -// The accuracy within screen measurement is acceptable. Higher number -// here means less calculation. -// Return value: The new point screen is no higher than accuracy + max altitude over -// the terrain. - - Vector MovePointToGround(const Vector &from, int maxAltitude = 0, int accuracy = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsWithinBounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether the integer coordinates passed in are within the -// bounds of the current Scene, considering its wrapping. -// Arguments: Int coordinates. -// A margin -// Return value: Whether within bounds or not, considering wrapping. - - bool IsWithinBounds(const int pixelX, const int pixelY, const int margin = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ForceBounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wraps or bounds a position coordinate if it is off bounds of the -// Scene, depending on the wrap settings of this Scene. -// Arguments: The X and Y coordinates of the position to wrap, if needed. -// Return value: Whether wrapping was performed or not. (Does not report on bounding) - - bool ForceBounds(int &posX, int &posY); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ForceBounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Wraps or bounds a position coordinate if it is off bounds of the -// Scene, depending on the wrap settings of this Scene. -// Arguments: The vector coordinates of the position to wrap, if needed. -// Return value: Whether wrapping was performed or not. (Does not report on bounding) - - bool ForceBounds(Vector &pos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapPosition -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Only wraps a position coordinate if it is off bounds of the Scene -// and wrapping in the corresponding axes are turned on. -// Arguments: The X and Y coordinates of the position to wrap, if needed. -// Return value: Whether wrapping was performed or not. - - bool WrapPosition(int &posX, int &posY); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapPosition -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Only wraps a position coordinate if it is off bounds of the Scene -// and wrapping in the corresponding axes are turned on. -// Arguments: The vector coordinates of the position to wrap, if needed. -// Return value: Whether wrapping was performed or not. - - bool WrapPosition(Vector &pos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SnapPosition -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a position snapped to the current scene grid. -// Arguments: The vector coordinates of the position to snap. -// Whether to actually snap or not. This is useful for cleaner toggle code. -// Return value: The new snapped position. - - Vector SnapPosition(const Vector &pos, bool snap = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ShortestDistance -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the shortest distance between two points in scene -// coordinates, taking into account all wrapping and out of bounds of the -// two points. -// Arguments: The two Vector coordinates of the two positions to find the shortest -// distance between. -// Whether to check if the passed in points are outside the scene, and to -// wrap them if they are. -// Return value: The resulting vector screen shows the shortest distance, spanning over -// wrapping borders etc. Basically the ideal pos2 - pos1. - - Vector ShortestDistance(Vector pos1, Vector pos2, bool checkBounds = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ShortestDistanceX -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the shortest distance between two x values in scene -// coordinates, taking into account all wrapping and out of bounds of the -// two values. -// Arguments: The X coordinates of the two values to find the shortest distance between. -// Whether to check if the passed in points are outside the scene, and to -// wrap them if they are. -// Whether to constrain the distance to only be in a certain direction: -// 0 means no constraint, < 0 means only look in the negative dir, etc. -// If the scene doesn't wrap in the constraint's direction, the constraint -// will be ignored. -// Return value: The resulting X value screen shows the shortest distance, spanning over -// wrapping borders etc. Basically the ideal val2 - val1. - - float ShortestDistanceX(float val1, float val2, bool checkBounds = false, int direction = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ShortestDistanceY -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the shortest distance between two Y values in scene -// coordinates, taking into account all wrapping and out of bounds of the -// two values. -// Arguments: The Y coordinates of the two values to find the shortest distance between. -// Whether to check if the passed in points are outside the scene, and to -// wrap them if they are. -// Whether to constrain the distance to only be in a certain direction: -// 0 means no constraint, < 0 means only look in the negative dir, etc. -// If the scene doesn't wrap in the constraint's direction, the constraint -// will be ignored. -// Return value: The resulting Y value screen shows the shortest distance, spanning over -// wrapping borders etc. Basically the ideal val2 - val1. - - float ShortestDistanceY(float val1, float val2, bool checkBounds = false, int direction = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ObscuredPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a point on the scene is obscured by MOID or Terrain -// non-air material. -// Arguments: The point on the scene to check. -// Wheter to also check for unseen areas of a specific team. -1 means -// don't check this. -// Return value: Whether that point is obscured/obstructed or not. - - bool ObscuredPoint(Vector &point, int team = -1) { return ObscuredPoint(point.GetFloorIntX(), point.GetFloorIntY()); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ObscuredPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a point on the scene is obscured by MOID or Terrain -// non-air material. -// Arguments: The point on the scene to check. -// Wheter to also check for unseen areas of a specific team. -1 means -// don't check this. -// Return value: Whether that point is obscured/obstructed or not. - - bool ObscuredPoint(int x, int y, int team = -1); - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SceneRectsIntersect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether two IntRect:s in the scene intersect, while taking -// wrapping into account. -// Arguments: The point on the scene to check. -// Return value: Whether that point is obscured/obstructed or not. - - bool SceneRectsIntersect(int x, int y); -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapRect -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes a rect and adds all possible wrapped appearances of that rect -// to a passed-in list. IF if a passed in rect straddles the seam of a -// wrapped scene axis, it will be added twice to the output list. If it -// doesn't straddle any seam, it will be only added once. -// Arguments: The IntRect to check for wrapping of and add to the output list below. -// A reference to a list of IntRect:s that will only be added to, never -// cleared. -// Return value: How many additions of the passed in rect was added to the list. 1 if -// no wrapping need was detected, up to 4 possible (if straddling both seams) - - int WrapRect(const IntRect &wrapRect, std::list &outputList); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: WrapBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes a Box and adds all possible scenewrapped appearances of that Box -// to a passed-in list. IF if a passed in rect straddles the seam of a -// wrapped scene axis, it will be added twice to the output list. If it -// doesn't straddle any seam, it will be only added once. -// Arguments: The IntRect to check for wrapping of and add to the output list below. -// A reference to a list of IntRect:s that will only be added to, never -// cleared. -// Return value: How many additions of the passed in Box was added to the list. 1 if -// no wrapping need was detected, up to 4 possible (if straddling both seams) - - int WrapBox(const Box &wrapBox, std::list &outputList); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddSceneObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Takes any scene object and adds it to the scene in the appropriate way. -// If it's a TerrainObject, then it gets applied to the terrain, if it's -// an MO, it gets added to the correct type group in MovableMan. -// Arguments: The SceneObject to add. Ownership IS transferred! -// Return value: Whether the SceneObject was successfully added or not. Either way, -// ownership was transferred. If no success, the object was deleted. - - bool AddSceneObject(SceneObject *pObject); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this SceneMan. Supposed to be done every frame -// before drawing. -// Arguments: Which screen to update for. -// Return value: None. - - void Update(int screenId = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this SceneMan's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on, appropriately sized for the split -// screen segment. -// The offset into the scene where the target bitmap's upper left corner -// is located. -// Return value: None. - - void Draw(BITMAP *targetBitmap, BITMAP *targetGUIBitmap, const Vector &targetPos = Vector(), bool skipBackgroundLayers = false, bool skipTerrain = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearMOColorLayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the color MO layer. Should be done every frame. -// Arguments: None. -// Return value: None. - - void ClearMOColorLayer(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearSeenPixels -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the list of pixels on the unseen map that have been revealed. -// Arguments: None. -// Return value: None. - - void ClearSeenPixels(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddMaterialCopy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a copy of passed material and stores it into internal vector -// to make sure there's only one material owner -// Arguments: Material to add. -// Return value: Pointer to stored material. - - Material * AddMaterialCopy(Material *mat); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RegisterTerrainChange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Registers terrain change event for the network server to be then sent to clients. -// Arguments: x,y - scene coordinates of change, w,h - size of the changed region, -// color - changed color for one-pixel events, -// back - if true, then background bitmap was changed if false then foreground. -// Return value: None. - - void RegisterTerrainChange(int x, int y, int w, int h, unsigned char color, bool back); - - - /// - /// Gets an intermediate bitmap that is used for drawing a settled MovableObject into the terrain. - /// - /// The diameter of the MovableObject to calculate the required bitmap size. - /// Pointer to the temp BITMAP of the appropriate size. Ownership is NOT transferred! - BITMAP * GetIntermediateBitmapForSettlingIntoTerrain(int moDiameter) const; - - /// - /// Sets the current scene pointer to null - /// - void ClearCurrentScene(); - - /// - /// Gets the maximum height of a column of scrap terrain to collapse, when the bottom pixel is knocked loose. - /// - /// The compacting height of scrap terrain. - int GetScrapCompactingHeight() const { return m_ScrapCompactingHeight; } - - /// - /// Sets the maximum height of a column of scrap terrain to collapse, when the bottom pixel is knocked loose. - /// - /// The new compacting height, in pixels. - void SetScrapCompactingHeight(int newHeight) { m_ScrapCompactingHeight = newHeight; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - - protected: - - static std::vector> m_IntermediateSettlingBitmaps; //!< Intermediate bitmaps of different sizes that are used to draw settled MovableObjects into the terrain. - - // Default Scene name to load if nothing else is specified - std::string m_DefaultSceneName; - // Scene reference to load from, if any. NOT OWNED - a clone of this actually gets loaded - const Scene *m_pSceneToLoad; - // Whether to place objects later when loading a clone of the above scene - bool m_PlaceObjects; - // Whether to place units and deployments when loading scence - bool m_PlaceUnits; - - // Current scene being used - Scene *m_pCurrentScene; - // Color MO layer - SceneLayerTracked *m_pMOColorLayer; - // MovableObject ID layer - SceneLayerTracked *m_pMOIDLayer; - // A spatial partitioning grid of MOIDs, used to optimize collision and distance queries - SpatialPartitionGrid m_MOIDsGrid; - - // Debug layer for seeing cast rays etc - SceneLayer *m_pDebugLayer; - - // The mode we're drawing layers in to the screen - int m_LayerDrawMode; - - // Material palette stuff - std::map m_MatNameMap; - // This gets filled with holes, not contigous from 0 onward, but whatever the ini specifies. The Material objects are owned here - std::array m_apMatPalette; - // The total number of added materials so far - int m_MaterialCount; - - // Non original materials added by inheritance - std::vector m_MaterialCopiesVector; - - // Sound of an unseen pixel on an unseen layer being revealed. - SoundContainer *m_pUnseenRevealSound; - - bool m_DrawRayCastVisualizations; //!< Whether to visibly draw RayCasts to the Scene debug Bitmap. - bool m_DrawPixelCheckVisualizations; //!< Whether to visibly draw pixel checks (GetTerrMatter and GetMOIDPixel) to the Scene debug Bitmap. - - // The last screen everything has been updated to - int m_LastUpdatedScreen; - // Whether we're in second pass of the structural computations. - // Second pass is where structurally unsound areas of the Terrain are turned into - // MovableObject:s. - bool m_SecondStructPass; - - // The Timer that keeps track of how much time there is left for - // structural calculations each frame. - Timer m_CalcTimer; - - // The Timer to measure time between cleanings of the color layer of the Terrain. - Timer m_CleanTimer; - // Bitmap to look for orphaned regions - BITMAP * m_pOrphanSearchBitmap; - - int m_ScrapCompactingHeight; //!< The maximum height of a column of scrap terrain to collapse, when the bottom pixel is knocked loose. - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SceneMan, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - // Disallow the use of some implicit methods. - SceneMan(const SceneMan &reference) = delete; - SceneMan & operator=(const SceneMan &rhs) = delete; - -}; + // Class: SceneMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The singleton manager of all terrain and backgrounds in the RTE. + // Parent(s): Singleton, Serializable. + // Class history: 12/25/2001 SceneMan created. + + class SceneMan : public Singleton, public Serializable { + friend class SettingsMan; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + SerializableClassNameGetter; + SerializableOverrideMethods; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: SceneMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a SceneMan object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + SceneMan() { + m_pOrphanSearchBitmap = 0; + Clear(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~SceneMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a SceneMan object before deletion + // from system memory. + // Arguments: None. + + ~SceneMan() { Destroy(); } + + /// + /// Makes the SceneMan object ready for use. + /// + void Initialize() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the SceneMan object ready for use. + // Arguments: A string with the filepath to a Reader file from screen this SceneMan's + // data should be created. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(std::string readerFile); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetDefaultSceneName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the instance name of the default Scene to be loaded if nothing + // else is available. + // Arguments: The default scene instance name. + // Return value: None. + + void SetDefaultSceneName(std::string defaultSceneName) { m_DefaultSceneName = defaultSceneName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDefaultSceneName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the name of the default A to be loaded if nothing + // else is available. + // Arguments: None. + // Return value: The default Scene instance name. + + std::string GetDefaultSceneName() const { return m_DefaultSceneName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Actually loads a new Scene into memory. has to be done before using + // this object. + // Arguments: The instance of the Scene, ownership IS transferred! + // Whether the scene should actually apply all its SceneObject:s placed + // in its definition. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int LoadScene(Scene* pNewScene, bool placeObjects = true, bool placeUnits = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSceneToLoad + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Stores a Scene reference to be loaded later into the SceneMan. + // Arguments: The instance reference of the Scene, ownership IS NOT (!!) transferred! + // Whether the scene should actually apply all its SceneObject:s placed + // in its definition. + // Return value: None. + + void SetSceneToLoad(const Scene* pLoadScene, bool placeObjects = true, bool placeUnits = true) { + m_pSceneToLoad = pLoadScene; + m_PlaceObjects = placeObjects; + m_PlaceUnits = placeUnits; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetSceneToLoad + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets a scene to load later, by preset name. + // Arguments: The name of the Scene preset instance to load. + // Whether the scene should actually apply all its SceneObject:s placed + // in its definition. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int SetSceneToLoad(std::string sceneName, bool placeObjects = true, bool placeUnits = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSceneToLoad + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the stored Scene reference to be loaded later into the SceneMan. + // Arguments: None. + // Return value: The instance reference of the Scene, ownership IS NOT (!!) transferred! + + const Scene* GetSceneToLoad() { return m_pSceneToLoad; } + + /// + /// Gets whether objects are placed when the Scene is initially started. Used for saving/loading games. + /// + /// Whether objects are placed when the Scene is initially started. + bool GetPlaceObjectsOnLoad() const { return m_PlaceObjects; } + + /// + /// Gets whether units are placed when the Scene is initially started. Used for saving/loading games. + /// + /// Whether units are placed when the Scene is initially started. + bool GetPlaceUnitsOnLoad() const { return m_PlaceUnits; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Actually loads the Scene set to be loaded in SetSceneToLoad. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int LoadScene(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads a Scene right now, by preset name. + // Arguments: The name of the Scene preset instance to load. + // Whether the scene should actually apply all its SceneObject:s placed + // in its definition. + // Whether the scene should actually deploy all units placed in its definition. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int LoadScene(std::string sceneName, bool placeObjects = true, bool placeUnits = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads a Scene right now, by preset name. + // Arguments: The name of the Scene preset instance to load. + // Whether the scene should actually apply all its SceneObject:s placed + // in its definition. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int LoadScene(std::string sceneName, bool placeObjects = true) { return LoadScene(sceneName, placeObjects, true); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire SceneMan, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneMan object. + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the currently loaded scene, if any. + // Arguments: None. + // Return value: The scene, ownership IS NOT TRANSFERRED! + + Scene* GetScene() const { return m_pCurrentScene; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSceneDim + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total dimensions (width and height) of the scene, in pixels. + // Arguments: None. + // Return value: A Vector describing the scene dimensions. + + Vector GetSceneDim() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSceneWidth + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total width of the scene, in pixels. + // Arguments: None. + // Return value: An int describing the scene width. + + int GetSceneWidth() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetSceneHeight + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total height of the scene, in pixels. + // Arguments: None. + // Return value: An int describing the scene width. + + int GetSceneHeight() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMaterialPalette + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets access to the whole material palette array of 256 entries. + // Arguments: None. + // Return value: A const reference to the material palette array. + + const std::array& GetMaterialPalette() const { return m_apMatPalette; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMaterial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a specific material by name. Ownership is NOT transferred! + // Arguments: The string name of the Material to get. + // Return value: A pointer to the requested material, or 0 if no material with that + // name was found. + + Material const* GetMaterial(const std::string& matName); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMaterialFromID + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a specific material from the material palette. Ownership is NOT + // transferred! + // Arguments: The unsigned char index specifying screen material to get (0-255). + // Return value: A reference to the requested material. OWNERSHIP IS NOT TRANSFERRED! + + Material const* GetMaterialFromID(unsigned char screen) { return screen >= 0 && screen < c_PaletteEntriesNumber && m_apMatPalette[screen] ? m_apMatPalette[screen] : m_apMatPalette[g_MaterialAir]; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SceneWrapsX + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the scene wraps its scrolling around the X axis. + // Arguments: None. + // Return value: Whether the scene wraps around the X axis or not. + + bool SceneWrapsX() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SceneWrapsY + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the scene wraps its scrolling around the Y axis. + // Arguments: None. + // Return value: Whether the scene wraps around the Y axis or not. + + bool SceneWrapsY() const; + + /// + /// Gets the orbit direction for the current scene. + /// + /// The orbit direction for the current scene. + Directions GetSceneOrbitDirection() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTerrain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the SLTerrain, or 0 if no scene is loaded. + // Arguments: None. + // Return value: A pointer to the SLTerrain. Ownership is NOT transferred! + + SLTerrain* GetTerrain(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMOColorBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the bitmap of the intermediary collection SceneLayer that all + // MovableObject:s draw themselves onto before it itself gets drawn onto + // the screen back buffer. + // Arguments: None. + // Return value: A BITMAP pointer to the MO bitmap. Ownership is NOT transferred! + + BITMAP* GetMOColorBitmap() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDebugBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the bitmap of the SceneLayer that debug graphics is drawn onto. + // Will only return valid BITMAP if building with DEBUG_BUILD. + // Arguments: None. + // Return value: A BITMAP pointer to the debug bitmap. Ownership is NOT transferred! + + BITMAP* GetDebugBitmap() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMOIDBitmap + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the bitmap of the SceneLayer that all MovableObject:s draw thir + // current (for the frame only!) MOID's onto. + // Arguments: None. + // Return value: A BITMAP pointer to the MO bitmap. Ownership is NOT transferred! + + BITMAP* GetMOIDBitmap() const; + + // TEMP! + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: MOIDClearCheck + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes sure the MOID bitmap layer is completely of NoMOID color. + // If found to be not, dumps MOID layer and the FG actor color layer for + // debugging. + // Arguments: None. + // Return value: Was it clear? + + bool MOIDClearCheck(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLayerDrawMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current drawing mode of the SceneMan. + // Arguments: None. + // Return value: The current layer draw mode, see the LayerDrawMode enumeration for the + // different possible mode settings. + + int GetLayerDrawMode() const { return m_LayerDrawMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTerrMatter + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets a specific pixel from the total material representation of + // this Scene. LockScene() must be called before using this method. + // Arguments: The X and Y coordinates of screen material pixel to get. + // Return value: An unsigned char specifying the requested pixel's material index. + + unsigned char GetTerrMatter(int pixelX, int pixelY); + + /// + /// Gets a MOID from pixel coordinates in the Scene. LockScene() must be called before using this method. + /// + /// The X coordinate of the Scene pixel to test. + /// The Y coordinate of the Scene pixel to test. + /// The team to ignore. + /// The MOID currently at the specified pixel coordinates. + MOID GetMOIDPixel(int pixelX, int pixelY, int ignoreTeam); + + /// + /// Gets a MOID from pixel coordinates in the Scene. LockScene() must be called before using this method. + /// + /// The X coordinate of the Scene pixel to test. + /// The Y coordinate of the Scene pixel to test. + /// The MOID currently at the specified pixel coordinates. + MOID GetMOIDPixel(int pixelX, int pixelY) { return GetMOIDPixel(pixelX, pixelY, Activity::NoTeam); } + + /// + /// Gets this Scene's MOID SpatialPartitionGrid. + /// + /// This Scene's MOID SpatialPartitionGrid. + const SpatialPartitionGrid& GetMOIDGrid() const { return m_MOIDsGrid; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGlobalAcc + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the global acceleration (in m/s^2) that is applied to all movable + // objects' velocities during every frame. Typically models gravity. + // Arguments: None. + // Return value: A Vector describing the global acceleration. + + Vector GetGlobalAcc() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOzPerKg + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how many Ounces there are in a metric Kilogram + // Arguments: None. + // Return value: A float describing the Oz/Kg ratio. + + float GetOzPerKg() const { return 35.27396; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetKgPerOz + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets how many metric Kilograms there are in an Ounce. + // Arguments: None. + // Return value: A float describing the Kg/Oz ratio. + + float GetKgPerOz() const { return 0.02834952; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetLayerDrawMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the drawing mode of the SceneMan, to easily view what's going on + // in the different internal SceneLayer:s. + // Arguments: The layer mode to draw in, see the LayerDrawMode enumeration for the + // different possible settings. + // Return value: None. + + void SetLayerDrawMode(int mode) { m_LayerDrawMode = mode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LockScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Locks all dynamic internal scene bitmaps so that manipulaitons of the + // scene's color and matter representations can take place. + // Doing it in a separate method like this is more efficient because + // many bitmap manipulaitons can be performed between a lock and unlock. + // UnlockScene() should always be called after accesses are completed. + // Arguments: None. + // Return value: None. + + void LockScene(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UnlockScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Unlocks the scene's bitmaps and prevents access to display memory. + // Doing it in a separate method like this is more efficient because + // many bitmap accesses can be performed between a lock and an unlock. + // UnlockScene() should only be called after LockScene(). + // Arguments: None. + // Return value: None. + + void UnlockScene(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SceneIsLocked + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Indicates whether the entire scene is currently locked or not. + // Arguments: None. + // Return value: Whether the entire scene is currently locked or not. + + bool SceneIsLocked() const; + + /// + /// Registers an area to be drawn upon, so it can be tracked and cleared later. + /// + /// The bitmap being drawn upon. + /// The MOID, if we're drawing MOIDs. + /// The left boundary of the draw area. + /// The top boundary of the drawn area. + /// The right boundary of the draw area. + /// The bottom boundary of the draw area. + void RegisterDrawing(const BITMAP* bitmap, int moid, int left, int top, int right, int bottom); + + /// + /// Registers an area of to be drawn upon, so it can be tracked and cleared later. + /// + /// The bitmap being drawn upon. + /// The MOID, if we're drawing MOIDs. + /// The centre position of the drawn area. + /// The radius of the drawn area. + void RegisterDrawing(const BITMAP* bitmap, int moid, const Vector& center, float radius); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearAllMOIDDrawings + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all registered drawn areas of the MOID layer to the g_NoMOID + // color and clears the registrations too. Should be done each sim update. + // Arguments: None. + // Return value: None. + + void ClearAllMOIDDrawings(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WillPenetrate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Test whether a pixel of the scene would be knocked loose and + // turned into a MO by another particle of a certain material going at a + // certain velocity. Scene needs to be locked to do this! + // Arguments: The X and Y coords of the scene pixel that is collided with. + // The velocity of the incoming particle. + // The mass of the incoming particle. + // Return value: A bool indicating wether the scene pixel would be knocked loose or + // not. If the pixel location specified happens to be of the air + // material (0) false will be returned here. + + bool WillPenetrate(const int posX, + const int posY, + const Vector& velocity, + const float mass) { return WillPenetrate(posX, posY, velocity * mass); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WillPenetrate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Test whether a pixel of the scene would be knocked loose and + // turned into a MO by a certian impulse force. Scene needs to be locked + // to do this! + // Arguments: The X and Y coords of the scene pixel that is collided with. + // The impulse force vector, in Kg * m/s. + // Return value: A bool indicating wether the scene pixel would be knocked loose or + // not. If the pixel location specified happens to be of the air + // material (0) false will be returned here. + + bool WillPenetrate(const int posX, + const int posY, + const Vector& impulse); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TryPenetrate + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculate whether a pixel of the scene would be knocked loose and + // turned into a MO by another particle of a certain material going at a + // certain velocity. If so, the incoming particle will knock loose the + // specified pixel in the scene and momentarily take its place. + // Scene needs to be locked to do this! + // Arguments: The X and Y coord of the scene pixel that is to be collided with. + // The impulse force exerted on the terrain pixel. If this magnitude + // exceeds the strength threshold of the material of the terrain pixel + // hit, the terrain pixel will be knocked loose an turned into an MO. + // The velocity of the the point hitting the terrain here. + // A float reference screen will be set to the factor with screen to + // multiply the collision velocity to get the resulting retardation + // (negative acceleration) that occurs when a penetration happens. + // The normalized probability ratio between 0.0 and 1.0 that determines + // the chance of a penetration to remove a pixel from the scene and + // thus replace it with and air pixel. 1.0 = always, 0.0 = never. + // How many consecutive penetrations in a row immediately before this try. + // The size of the area to look for orphaned terrain elements. + // Max area or orphaned area to remove. + // Orphan area removal trigger rate. + // Return value: A bool indicating wether the scene pixel was knocked loose or not. + // If the pixel location specified happens to be of the air material (0) + // false will be returned here. + + bool TryPenetrate(int posX, + int posY, + const Vector& impulse, + const Vector& velocity, + float& retardation, + const float airRatio, + const int numPenetrations = 0, + const int removeOrphansRadius = 0, + const int removeOrphansMaxArea = 0, + const float removeOrphansRate = 0.0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveOrphans + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the area of an orphaned region at specified coordinates. + // Arguments: Coordinates to check for region, whether the orphaned region should be converted into MOPixels and region removed. + // Area of orphaned object calculated during recursve function call to check if we're out of limits + // Size of the are to look for orphaned objects + // Max area of orphaned object to remove + // Whether to actually remove orphaned pixels or not + // Whether to clear internal terrain tracking bitmap or not + // Return value: The area of orphaned region at posX,posY + + int RemoveOrphans(int posX, int posY, int radius, int maxArea, bool remove = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveOrphans + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the area of an orphaned region at specified coordinates. + // Arguments: Coordinates to check for region, whether the orphaned region should be converted into MOPixels and region removed. + // Coordinates of initial terrain penetration to check, which serves as a center of orphaned object detection. + // Area of orphaned object calculated during recursve function call to check if we're out of limits + // Size of the are to look for orphaned objects + // Max area of orphaned object to remove + // Whether to actually remove orphaned pixels or not + // Whether to clear internal terrain tracking bitmap or not + // Return value: The area of orphaned region at posX,posY + + int RemoveOrphans(int posX, + int posY, + int centerPosX, + int centerPosY, + int accumulatedArea, + int radius, + int maxArea, + bool remove = false); + + /// + /// Removes a pixel from the terrain and adds it to MovableMan. + /// + /// The X coordinate of the terrain pixel. + /// The Y coordinate of the terrain pixel. + /// The newly dislodged pixel, if one was found. + MovableObject* DislodgePixel(int posX, int posY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: MakeAllUnseen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets one team's view of the scene to be unseen, using a generated map + // of a specific resolution chunkiness. + // Arguments: The dimensions of the pixels that should make up the unseen layer. + // The team we're talking about. + // Return value: None. + + void MakeAllUnseen(Vector pixelSize, const int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: MakeAllSeen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets one team's view of the scene to be all seen. + // Arguments: The team we're talking about. + // Return value: None. + + void MakeAllSeen(const int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadUnseenLayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads a bitmap from file and use it as the unseen layer for a team. + // Arguments: The path to the bitmap to use as the unseen layer. + // Which team we're talking about. + // Return value: Whether the loading was successful or not. + + bool LoadUnseenLayer(std::string bitmapPath, const int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AnythingUnseen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a team has anything still unseen on the scene. + // Arguments: The team we're talking about. + // Return value: A bool indicating whether that team has anyhting yet unseen. + + bool AnythingUnseen(const int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetUnseenResolution + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows what the resolution factor of the unseen map to the entire Scene + // is, in both axes. + // Arguments: The team we're talking about. + // Return value: A vector witht he factors in each element representing the factors. + + Vector GetUnseenResolution(const int team) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsUnseen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks whether a pixel is in an unseen area on of a specific team. + // Arguments: The X and Y coords of the scene pixel that is to be checked. + // The team we're talking about. + // Return value: A bool indicating whether that point is yet unseen. + + bool IsUnseen(const int posX, const int posY, const int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RevealUnseen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reveals a pixel on the unseen map for a specific team, if there is any. + // Arguments: The X and Y coord of the scene pixel that is to be revealed. + // The team to reveal for. + // Return value: A bool indicating whether there was an unseen pixel revealed there. + + bool RevealUnseen(const int posX, const int posY, const int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RestoreUnseen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Hides a pixel on the unseen map for a specific team, if there is any. + // Arguments: The X and Y coord of the scene pixel that is to be revealed. + // The team to hide for. + // Return value: A bool indicating whether there was a seen pixel hidden there. + + bool RestoreUnseen(const int posX, const int posY, const int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RevealUnseenBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reveals a box on the unseen map for a specific team, if there is any. + // Arguments: The X and Y coords of the upper left corner of the box to be revealed. + // The width and height of the box to be revealed, in scene units (pixels) + // The team to reveal for. + // Return value: None. + + void RevealUnseenBox(const int posX, const int posY, const int width, const int height, const int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RestoreUnseenBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Restores a box on the unseen map for a specific team, if there is any. + // Arguments: The X and Y coords of the upper left corner of the box to be revealed. + // The width and height of the box to be restored, in scene units (pixels) + // The team to restore for. + // Return value: None. + + void RestoreUnseenBox(const int posX, const int posY, const int width, const int height, const int team); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastUnseenRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and reveals or hides pixels on the unseen layer of a team + // as long as the accumulated material strengths traced through the terrain + // don't exceed a specific value. + // Arguments: The team to see for. + // The starting position. + // The vector to trace along. + // A Vector that will be set to the position of where the sight ray was + // terminated. If it reached the end, it will be set to the end of the ray. + // The material strength limit where + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Whether the ray should reveal or restore unseen layer + // Return value: Whether any unseen pixels were revealed as a result of this seeing. + + bool CastUnseenRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip, bool reveal); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastSeeRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and reveals pixels on the unseen layer of a team + // as long as the accumulated material strengths traced through the terrain + // don't exceed a specific value. + // Arguments: The team to see for. + // The starting position. + // The vector to trace along. + // A Vector that will be set to the position of where the sight ray was + // terminated. If it reached the end, it will be set to the end of the ray. + // The material strength limit where + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Return value: Whether any unseen pixels were revealed as a result of this seeing. + + bool CastSeeRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastUnseeRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and hides pixels on the unseen layer of a team + // as long as the accumulated material strengths traced through the terrain + // don't exceed a specific value. + // Arguments: The team to see for. + // The starting position. + // The vector to trace along. + // A Vector that will be set to the position of where the sight ray was + // terminated. If it reached the end, it will be set to the end of the ray. + // The material strength limit where + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Return value: Whether any unseen pixels were revealed as a result of this seeing. + + bool CastUnseeRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastMaterialRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and gets the location of the first encountered + // pixel of a specific material in the terrain. + // Arguments: The starting position. + // The vector to trace along. + // The material ID to look for. + // A reference to the vector screen will be filled out with the absolute + // location of the found terrain pixel of the above material. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Whetehr the ray should wrap around the scene if it crosses a seam. + // Return value: Whether the material was found along the ray. If not, the fourth + // parameter will not have been altered (and may still not be 0!) + + bool CastMaterialRay(const Vector& start, const Vector& ray, unsigned char material, Vector& result, int skip = 0, bool wrap = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastMaterialRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and returns how far along that ray there is an + // encounter with a pixel of a specific material in the terrain. + // Arguments: The starting position. + // The vector to trace along. + // The material ID to look for. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Return value: How far along, in pixel units, the ray the material pixel was encountered. + // If no pixel of the right material was found, < 0 is returned. + + float CastMaterialRay(const Vector& start, const Vector& ray, unsigned char material, int skip = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastNotMaterialRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and gets the location of the first encountered + // pixel that is NOT of a specific material in the scene's terrain. + // Arguments: The starting position. + // The vector to trace along. + // The material ID to find something OTHER than. + // A reference to the vector screen will be filled out with the absolute + // location of the found terrain pixel of the above material. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Whether to check for MO layer collisions as well, not just terrain. + // Return value: Whether the a pixel other than the material was found along the ray. + // If not, the fourth parameter will not have been altered (and may still not be 0!) + + bool CastNotMaterialRay(const Vector& start, const Vector& ray, unsigned char material, Vector& result, int skip = 0, bool checkMOs = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastNotMaterialRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and returns how far along that ray there is an + // encounter with a pixel of OTHER than a specific material in the terrain. + // Arguments: The starting position. + // The vector to trace along. + // The material ID to find something OTHER than. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Whether to check for MO layer collisions as well, not just terrain. + // Return value: How far along, in pixel units, the ray the pixel of any other material + // was encountered. If no pixel of the right material was found, < 0 is returned. + + float CastNotMaterialRay(const Vector& start, const Vector& ray, unsigned char material, int skip = 0, bool checkMOs = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastStrengthSumRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and returns how the sum of all encountered pixels' + // material strength values. This will take wrapping into account. + // Arguments: The starting position. + // The ending position. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // A material ID to ignore, IN ADDITION to Air. + // Return value: The sum of all encountered pixels' material strength vales. So if it was + // all Air, then 0 is returned (Air's strength value is 0). + + float CastStrengthSumRay(const Vector& start, const Vector& end, int skip = 0, unsigned char ignoreMaterial = g_MaterialAir); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastMaxStrengthRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and returns the strongest of all encountered pixels' + // material strength values. + // This will take wrapping into account. + // Arguments: The starting position. + // The ending position. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // A material ID to ignore, IN ADDITION to Air. This defaults to doors, for legacy script purposes + // Return value: The max of all encountered pixels' material strength vales. So if it was + // all Air, then 0 is returned (Air's strength value is 0). + + // We use two accessors instead of default parameters, for lua compat + float CastMaxStrengthRay(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial); + float CastMaxStrengthRay(const Vector& start, const Vector& end, int skip) { return CastMaxStrengthRay(start, end, skip, g_MaterialDoor); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastMaxStrengthRayMaterial + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and returns the strongest of all encountered pixels' materials + // This will take wrapping into account. + // Arguments: The starting position. + // The ending position. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // A material ID to ignore, IN ADDITION to Air. + // Return value: The strongest material encountered + const Material* CastMaxStrengthRayMaterial(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastStrengthRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and shows where along that ray there is an + // encounter with a pixel of a material with strength more than or equal + // to a specific value. + // Arguments: The starting position. + // The vector to trace along. + // The strength value of screen any found to be equal or more than will + // terminate the ray. + // A reference to the vector screen will be filled out with the absolute + // location of the found terrain pixel of less than or equal to above strength. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // A material ID to ignore, IN ADDITION to Air. + // Whetehr the ray should wrap around the scene if it crosses a seam. + // Return value: Whether a material of equal or more strength was found along the ray. + // If not, the fourth parameter have been set to last position of the ray. + + bool CastStrengthRay(const Vector& start, const Vector& ray, float strength, Vector& result, int skip = 0, unsigned char ignoreMaterial = g_MaterialAir, bool wrap = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastWeaknessRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and shows where along that ray there is an + // encounter with a pixel of a material with strength less than or equal + // to a specific value. + // Arguments: The starting position. + // The vector to trace along. + // The strength value of screen any found to be equal or less than will + // terminate the ray. + // A reference to the vector screen will be filled out with the absolute + // location of the found terrain pixel of less than or equal to above strength. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Whetehr the ray should wrap around the scene if it crosses a seam. + // Return value: Whether a material of equal or less strength was found along the ray. + // If not, the fourth parameter have been set to last position of the ray. + + bool CastWeaknessRay(const Vector& start, const Vector& ray, float strength, Vector& result, int skip = 0, bool wrap = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastMORay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and returns MOID of the first non-ignored + // non-NoMOID MO encountered. If a non-air terrain pixel is encountered + // first, g_NoMOID will be returned. + // Arguments: The starting position. + // The vector to trace along. + // An MOID to ignore. Any child MO's of this MOID will also be ignored. + // To enable ignoring of all MOIDs associated with an object of a specific + // team which also has team ignoring enabled itself. + // A specific material ID to ignore hits with. + // Whether to ignore all terrain hits or not. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Return value: The MOID of the hit non-ignored MO, or g_NoMOID if terrain or no MO was hit. + + MOID CastMORay(const Vector& start, const Vector& ray, MOID ignoreMOID = g_NoMOID, int ignoreTeam = Activity::NoTeam, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false, int skip = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastFindMORay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and shows where a specific MOID has been found. + // Arguments: The starting position. + // The vector to trace along. + // An MOID to find. Any child MO's of this MOID will also be found. ------------ ??? + // A reference to the vector screen will be filled out with the absolute + // location of the found MO pixel of the above MOID. + // A specific material ID to ignore hits with. + // Whether to ignore all terrain hits or not. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Return value: Whether the target MOID was found along the ray or not. + + bool CastFindMORay(const Vector& start, const Vector& ray, MOID targetMOID, Vector& resultPos, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false, int skip = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CastObstacleRay + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Traces along a vector and returns the length of how far the trace went + // without hitting any non-ignored terrain material or MOID at all. + // Arguments: The starting position. + // The vector to trace along. + // A reference to the vector screen will be filled out with the absolute + // location of the first obstacle, or the end of the ray if none was hit. + // A reference to the vector screen will be filled out with the absolute + // location of the last free position before hitting an obstacle, or the + // end of the ray if none was hit. This is only altered if thre are any + // free pixels encountered. + // An MOID to ignore. Any child MO's of this MOID will also be ignored. + // To enable ignoring of all MOIDs associated with an object of a specific + // team which also has team ignoring enabled itself. + // A specific material ID to ignore hits with. + // For every pixel checked along the line, how many to skip between them + // for optimization reasons. 0 = every pixel is checked. + // Return value: How far along, in pixel units, the ray the pixel of any obstacle was + // encountered. If no pixel of the right material was found, < 0 is returned. + // If an obstacle on the starting position was encountered, 0 is returned. + + float CastObstacleRay(const Vector& start, const Vector& ray, Vector& obstaclePos, Vector& freePos, MOID ignoreMOID = g_NoMOID, int ignoreTeam = Activity::NoTeam, unsigned char ignoreMaterial = 0, int skip = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLastRayHitPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the abosulte pos of where the last cast ray hit somehting. + // Arguments: None. + // Return value: A vector with the absolute pos of where the last ray cast hit somehting. + + const Vector& GetLastRayHitPos(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FindAltitude + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the altitide of a certain point above the terrain, measured + // in pixels. + // Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. + // The accuracy within screen measurement is acceptable. Higher number + // here means less calculation. + // Return value: The altitude over the terrain, in pixels. + + float FindAltitude(const Vector& from, int max, int accuracy, bool fromSceneOrbitDirection); + float FindAltitude(const Vector& from, int max, int accuracy) { return FindAltitude(from, max, accuracy, false); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: OverAltitude + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the altitide of a certain point above the terrain, measured + // in pixels, and then tells if that point is over a certain value. + // Arguments: The altitude threshold you want to check for. + // The accuracy within screen measurement is acceptable. Higher number + // here means less costly. + // Return value: Whether the point is over the threshold altitude or not. + + bool OverAltitude(const Vector& point, int threshold, int accuracy = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: MovePointToGround + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes an arbitrary point in the air and calculates it to be straight + // down at a certain maximum distance from the ground. + // Arguments: The point to start from. Should be in the air, or the same point will + // be returned (null operation) + // The max altitude in px you want the point to be above the ground. + // The accuracy within screen measurement is acceptable. Higher number + // here means less calculation. + // Return value: The new point screen is no higher than accuracy + max altitude over + // the terrain. + + Vector MovePointToGround(const Vector& from, int maxAltitude = 0, int accuracy = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsWithinBounds + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether the integer coordinates passed in are within the + // bounds of the current Scene, considering its wrapping. + // Arguments: Int coordinates. + // A margin + // Return value: Whether within bounds or not, considering wrapping. + + bool IsWithinBounds(const int pixelX, const int pixelY, const int margin = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ForceBounds + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Wraps or bounds a position coordinate if it is off bounds of the + // Scene, depending on the wrap settings of this Scene. + // Arguments: The X and Y coordinates of the position to wrap, if needed. + // Return value: Whether wrapping was performed or not. (Does not report on bounding) + + bool ForceBounds(int& posX, int& posY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ForceBounds + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Wraps or bounds a position coordinate if it is off bounds of the + // Scene, depending on the wrap settings of this Scene. + // Arguments: The vector coordinates of the position to wrap, if needed. + // Return value: Whether wrapping was performed or not. (Does not report on bounding) + + bool ForceBounds(Vector& pos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WrapPosition + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Only wraps a position coordinate if it is off bounds of the Scene + // and wrapping in the corresponding axes are turned on. + // Arguments: The X and Y coordinates of the position to wrap, if needed. + // Return value: Whether wrapping was performed or not. + + bool WrapPosition(int& posX, int& posY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WrapPosition + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Only wraps a position coordinate if it is off bounds of the Scene + // and wrapping in the corresponding axes are turned on. + // Arguments: The vector coordinates of the position to wrap, if needed. + // Return value: Whether wrapping was performed or not. + + bool WrapPosition(Vector& pos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SnapPosition + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a position snapped to the current scene grid. + // Arguments: The vector coordinates of the position to snap. + // Whether to actually snap or not. This is useful for cleaner toggle code. + // Return value: The new snapped position. + + Vector SnapPosition(const Vector& pos, bool snap = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ShortestDistance + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the shortest distance between two points in scene + // coordinates, taking into account all wrapping and out of bounds of the + // two points. + // Arguments: The two Vector coordinates of the two positions to find the shortest + // distance between. + // Whether to check if the passed in points are outside the scene, and to + // wrap them if they are. + // Return value: The resulting vector screen shows the shortest distance, spanning over + // wrapping borders etc. Basically the ideal pos2 - pos1. + + Vector ShortestDistance(Vector pos1, Vector pos2, bool checkBounds = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ShortestDistanceX + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the shortest distance between two x values in scene + // coordinates, taking into account all wrapping and out of bounds of the + // two values. + // Arguments: The X coordinates of the two values to find the shortest distance between. + // Whether to check if the passed in points are outside the scene, and to + // wrap them if they are. + // Whether to constrain the distance to only be in a certain direction: + // 0 means no constraint, < 0 means only look in the negative dir, etc. + // If the scene doesn't wrap in the constraint's direction, the constraint + // will be ignored. + // Return value: The resulting X value screen shows the shortest distance, spanning over + // wrapping borders etc. Basically the ideal val2 - val1. + + float ShortestDistanceX(float val1, float val2, bool checkBounds = false, int direction = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ShortestDistanceY + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Calculates the shortest distance between two Y values in scene + // coordinates, taking into account all wrapping and out of bounds of the + // two values. + // Arguments: The Y coordinates of the two values to find the shortest distance between. + // Whether to check if the passed in points are outside the scene, and to + // wrap them if they are. + // Whether to constrain the distance to only be in a certain direction: + // 0 means no constraint, < 0 means only look in the negative dir, etc. + // If the scene doesn't wrap in the constraint's direction, the constraint + // will be ignored. + // Return value: The resulting Y value screen shows the shortest distance, spanning over + // wrapping borders etc. Basically the ideal val2 - val1. + + float ShortestDistanceY(float val1, float val2, bool checkBounds = false, int direction = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ObscuredPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a point on the scene is obscured by MOID or Terrain + // non-air material. + // Arguments: The point on the scene to check. + // Wheter to also check for unseen areas of a specific team. -1 means + // don't check this. + // Return value: Whether that point is obscured/obstructed or not. + + bool ObscuredPoint(Vector& point, int team = -1) { return ObscuredPoint(point.GetFloorIntX(), point.GetFloorIntY()); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ObscuredPoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a point on the scene is obscured by MOID or Terrain + // non-air material. + // Arguments: The point on the scene to check. + // Wheter to also check for unseen areas of a specific team. -1 means + // don't check this. + // Return value: Whether that point is obscured/obstructed or not. + + bool ObscuredPoint(int x, int y, int team = -1); + + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SceneRectsIntersect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether two IntRect:s in the scene intersect, while taking + // wrapping into account. + // Arguments: The point on the scene to check. + // Return value: Whether that point is obscured/obstructed or not. + + bool SceneRectsIntersect(int x, int y); + */ + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WrapRect + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes a rect and adds all possible wrapped appearances of that rect + // to a passed-in list. IF if a passed in rect straddles the seam of a + // wrapped scene axis, it will be added twice to the output list. If it + // doesn't straddle any seam, it will be only added once. + // Arguments: The IntRect to check for wrapping of and add to the output list below. + // A reference to a list of IntRect:s that will only be added to, never + // cleared. + // Return value: How many additions of the passed in rect was added to the list. 1 if + // no wrapping need was detected, up to 4 possible (if straddling both seams) + + int WrapRect(const IntRect& wrapRect, std::list& outputList); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: WrapBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes a Box and adds all possible scenewrapped appearances of that Box + // to a passed-in list. IF if a passed in rect straddles the seam of a + // wrapped scene axis, it will be added twice to the output list. If it + // doesn't straddle any seam, it will be only added once. + // Arguments: The IntRect to check for wrapping of and add to the output list below. + // A reference to a list of IntRect:s that will only be added to, never + // cleared. + // Return value: How many additions of the passed in Box was added to the list. 1 if + // no wrapping need was detected, up to 4 possible (if straddling both seams) + + int WrapBox(const Box& wrapBox, std::list& outputList); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddSceneObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Takes any scene object and adds it to the scene in the appropriate way. + // If it's a TerrainObject, then it gets applied to the terrain, if it's + // an MO, it gets added to the correct type group in MovableMan. + // Arguments: The SceneObject to add. Ownership IS transferred! + // Return value: Whether the SceneObject was successfully added or not. Either way, + // ownership was transferred. If no success, the object was deleted. + + bool AddSceneObject(SceneObject* pObject); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this SceneMan. Supposed to be done every frame + // before drawing. + // Arguments: Which screen to update for. + // Return value: None. + + void Update(int screenId = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this SceneMan's current graphical representation to a + // BITMAP of choice. + // Arguments: A pointer to a BITMAP to draw on, appropriately sized for the split + // screen segment. + // The offset into the scene where the target bitmap's upper left corner + // is located. + // Return value: None. + + void Draw(BITMAP* targetBitmap, BITMAP* targetGUIBitmap, const Vector& targetPos = Vector(), bool skipBackgroundLayers = false, bool skipTerrain = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearMOColorLayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the color MO layer. Should be done every frame. + // Arguments: None. + // Return value: None. + + void ClearMOColorLayer(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearSeenPixels + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the list of pixels on the unseen map that have been revealed. + // Arguments: None. + // Return value: None. + + void ClearSeenPixels(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddMaterialCopy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Creates a copy of passed material and stores it into internal vector + // to make sure there's only one material owner + // Arguments: Material to add. + // Return value: Pointer to stored material. + + Material* AddMaterialCopy(Material* mat); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RegisterTerrainChange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Registers terrain change event for the network server to be then sent to clients. + // Arguments: x,y - scene coordinates of change, w,h - size of the changed region, + // color - changed color for one-pixel events, + // back - if true, then background bitmap was changed if false then foreground. + // Return value: None. + + void RegisterTerrainChange(int x, int y, int w, int h, unsigned char color, bool back); + + /// + /// Gets an intermediate bitmap that is used for drawing a settled MovableObject into the terrain. + /// + /// The diameter of the MovableObject to calculate the required bitmap size. + /// Pointer to the temp BITMAP of the appropriate size. Ownership is NOT transferred! + BITMAP* GetIntermediateBitmapForSettlingIntoTerrain(int moDiameter) const; + + /// + /// Sets the current scene pointer to null + /// + void ClearCurrentScene(); + + /// + /// Gets the maximum height of a column of scrap terrain to collapse, when the bottom pixel is knocked loose. + /// + /// The compacting height of scrap terrain. + int GetScrapCompactingHeight() const { return m_ScrapCompactingHeight; } + + /// + /// Sets the maximum height of a column of scrap terrain to collapse, when the bottom pixel is knocked loose. + /// + /// The new compacting height, in pixels. + void SetScrapCompactingHeight(int newHeight) { m_ScrapCompactingHeight = newHeight; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + static std::vector> m_IntermediateSettlingBitmaps; //!< Intermediate bitmaps of different sizes that are used to draw settled MovableObjects into the terrain. + + // Default Scene name to load if nothing else is specified + std::string m_DefaultSceneName; + // Scene reference to load from, if any. NOT OWNED - a clone of this actually gets loaded + const Scene* m_pSceneToLoad; + // Whether to place objects later when loading a clone of the above scene + bool m_PlaceObjects; + // Whether to place units and deployments when loading scence + bool m_PlaceUnits; + + // Current scene being used + Scene* m_pCurrentScene; + // Color MO layer + SceneLayerTracked* m_pMOColorLayer; + // MovableObject ID layer + SceneLayerTracked* m_pMOIDLayer; + // A spatial partitioning grid of MOIDs, used to optimize collision and distance queries + SpatialPartitionGrid m_MOIDsGrid; + + // Debug layer for seeing cast rays etc + SceneLayer* m_pDebugLayer; + + // The mode we're drawing layers in to the screen + int m_LayerDrawMode; + + // Material palette stuff + std::map m_MatNameMap; + // This gets filled with holes, not contigous from 0 onward, but whatever the ini specifies. The Material objects are owned here + std::array m_apMatPalette; + // The total number of added materials so far + int m_MaterialCount; + + // Non original materials added by inheritance + std::vector m_MaterialCopiesVector; + + // Sound of an unseen pixel on an unseen layer being revealed. + SoundContainer* m_pUnseenRevealSound; + + bool m_DrawRayCastVisualizations; //!< Whether to visibly draw RayCasts to the Scene debug Bitmap. + bool m_DrawPixelCheckVisualizations; //!< Whether to visibly draw pixel checks (GetTerrMatter and GetMOIDPixel) to the Scene debug Bitmap. + + // The last screen everything has been updated to + int m_LastUpdatedScreen; + // Whether we're in second pass of the structural computations. + // Second pass is where structurally unsound areas of the Terrain are turned into + // MovableObject:s. + bool m_SecondStructPass; + + // The Timer that keeps track of how much time there is left for + // structural calculations each frame. + Timer m_CalcTimer; + + // The Timer to measure time between cleanings of the color layer of the Terrain. + Timer m_CleanTimer; + // Bitmap to look for orphaned regions + BITMAP* m_pOrphanSearchBitmap; + + int m_ScrapCompactingHeight; //!< The maximum height of a column of scrap terrain to collapse, when the bottom pixel is knocked loose. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this SceneMan, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + SceneMan(const SceneMan& reference) = delete; + SceneMan& operator=(const SceneMan& rhs) = delete; + }; } // namespace RTE diff --git a/Source/Managers/SettingsMan.cpp b/Source/Managers/SettingsMan.cpp index 6afd9221b4..f356bfec5f 100644 --- a/Source/Managers/SettingsMan.cpp +++ b/Source/Managers/SettingsMan.cpp @@ -15,7 +15,7 @@ namespace RTE { const std::string SettingsMan::c_ClassName = "SettingsMan"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsMan::Clear() { m_SettingsPath = System::GetUserdataDirectory() + "Settings.ini"; @@ -52,7 +52,7 @@ namespace RTE { m_DisableFactionBuyMenuThemeCursors = false; m_PathFinderGridNodeSize = c_PPM; m_AIUpdateInterval = 2; - + m_NumberOfLuaStatesOverride = -1; m_ForceImmediatePathingRequestCompletion = false; @@ -72,10 +72,12 @@ namespace RTE { m_EnabledGlobalScripts.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int SettingsMan::Initialize() { - if (const char *settingsTempPath = std::getenv("CCCP_SETTINGSPATH")) { m_SettingsPath = std::string(settingsTempPath); } + if (const char* settingsTempPath = std::getenv("CCCP_SETTINGSPATH")) { + m_SettingsPath = std::string(settingsTempPath); + } Reader settingsReader(m_SettingsPath, false, nullptr, true, true); @@ -103,18 +105,18 @@ namespace RTE { return failureCode; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsMan::UpdateSettingsFile() const { Writer settingsWriter(m_SettingsPath); g_SettingsMan.Save(settingsWriter); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SettingsMan::ReadProperty(const std::string_view &propName, Reader &reader) { + int SettingsMan::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("PaletteFile", { reader >> g_FrameMan.m_PaletteFile; }); MatchProperty("ResolutionX", { reader >> g_WindowMan.m_ResX; }); MatchProperty("ResolutionY", { reader >> g_WindowMan.m_ResY; }); @@ -132,14 +134,13 @@ namespace RTE { MatchProperty("SoundPanningEffectStrength", { reader >> g_AudioMan.m_SoundPanningEffectStrength; - ////////////////////////////////////////////////// - //TODO These need to be removed when our soundscape is sorted out. They're only here temporarily to allow for easier tweaking by pawnis. + ////////////////////////////////////////////////// + // TODO These need to be removed when our soundscape is sorted out. They're only here temporarily to allow for easier tweaking by pawnis. }); MatchProperty("ListenerZOffset", { reader >> g_AudioMan.m_ListenerZOffset; }); MatchProperty("MinimumDistanceForPanning", { reader >> g_AudioMan.m_MinimumDistanceForPanning; - ////////////////////////////////////////////////// - + ////////////////////////////////////////////////// }); MatchProperty("ShowForeignItems", { reader >> m_ShowForeignItems; }); MatchProperty("FlashOnBrainDamage", { reader >> m_FlashOnBrainDamage; }); @@ -230,13 +231,13 @@ namespace RTE { } } }); - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SettingsMan::Save(Writer &writer) const { + int SettingsMan::Save(Writer& writer) const { Serializable::Save(writer); writer.NewDivider(false); @@ -264,7 +265,7 @@ namespace RTE { writer.NewPropertyWithValue("SoundPanningEffectStrength", g_AudioMan.m_SoundPanningEffectStrength); ////////////////////////////////////////////////// - //TODO These need to be removed when our soundscape is sorted out. They're only here temporarily to allow for easier tweaking. + // TODO These need to be removed when our soundscape is sorted out. They're only here temporarily to allow for easier tweaking. writer.NewPropertyWithValue("ListenerZOffset", g_AudioMan.m_ListenerZOffset); writer.NewPropertyWithValue("MinimumDistanceForPanning", g_AudioMan.m_MinimumDistanceForPanning); ////////////////////////////////////////////////// @@ -325,13 +326,13 @@ namespace RTE { writer.NewPropertyWithValue("EnableParticleSettling", g_MovableMan.m_SettlingEnabled); writer.NewPropertyWithValue("EnableMOSubtraction", g_MovableMan.m_MOSubtractionEnabled); writer.NewPropertyWithValue("DeltaTime", g_TimerMan.GetDeltaTimeSecs()); - + // No experimental settings right now :) - //writer.NewLine(false, 2); - //writer.NewDivider(false); - //writer.NewLineString("// Engine Settings - EXPERIMENTAL", false); - //writer.NewLineString("// These settings are experimental! They may break mods, crash the game, corrupt saves or worse. Use at your own risk.", false); - //writer.NewLine(false); + // writer.NewLine(false, 2); + // writer.NewDivider(false); + // writer.NewLineString("// Engine Settings - EXPERIMENTAL", false); + // writer.NewLineString("// These settings are experimental! They may break mods, crash the game, corrupt saves or worse. Use at your own risk.", false); + // writer.NewLine(false); writer.NewLine(false, 2); writer.NewDivider(false); @@ -401,7 +402,7 @@ namespace RTE { writer.NewDivider(false); writer.NewLineString("// Enabled Bunker Assembly Groups", false); writer.NewLine(false); - for (const std::string &visibleAssembly : m_VisibleAssemblyGroupsList) { + for (const std::string& visibleAssembly: m_VisibleAssemblyGroupsList) { writer.NewPropertyWithValue("VisibleAssemblyGroup", visibleAssembly); } } @@ -411,8 +412,10 @@ namespace RTE { writer.NewDivider(false); writer.NewLineString("// Disabled Mods", false); writer.NewLine(false); - for (const auto &[modPath, modDisabled] : m_DisabledMods) { - if (modDisabled) { writer.NewPropertyWithValue("DisableMod", modPath); } + for (const auto& [modPath, modDisabled]: m_DisabledMods) { + if (modDisabled) { + writer.NewPropertyWithValue("DisableMod", modPath); + } } } @@ -421,8 +424,10 @@ namespace RTE { writer.NewDivider(false); writer.NewLineString("// Enabled Global Scripts", false); writer.NewLine(false); - for (const auto &[scriptPresetName, scriptEnabled] : m_EnabledGlobalScripts) { - if (scriptEnabled) { writer.NewPropertyWithValue("EnableGlobalScript", scriptPresetName); } + for (const auto& [scriptPresetName, scriptEnabled]: m_EnabledGlobalScripts) { + if (scriptEnabled) { + writer.NewPropertyWithValue("EnableGlobalScript", scriptPresetName); + } } } @@ -449,4 +454,4 @@ namespace RTE { return 0; } -} +} // namespace RTE diff --git a/Source/Managers/SettingsMan.h b/Source/Managers/SettingsMan.h index 5e30ca27bb..240b3ca679 100644 --- a/Source/Managers/SettingsMan.h +++ b/Source/Managers/SettingsMan.h @@ -14,7 +14,6 @@ namespace RTE { class SettingsMan : public Singleton, public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -299,7 +298,7 @@ namespace RTE { /// Sets the player name that will be used in network multiplayer matches. /// /// String with the new player name to use. - void SetPlayerNetworkName(const std::string &newName) { m_PlayerNetworkName = newName.empty() ? "Dummy" : newName; } + void SetPlayerNetworkName(const std::string& newName) { m_PlayerNetworkName = newName.empty() ? "Dummy" : newName; } /// /// Gets the LAN server address to connect to. @@ -311,43 +310,43 @@ namespace RTE { /// Sets the LAN server address to connect to. /// /// New LAN server address to connect to. - void SetNetworkServerAddress(const std::string &newAddress) { m_NetworkServerAddress = newAddress.empty() ? "127.0.0.1:8000" : newAddress; } + void SetNetworkServerAddress(const std::string& newAddress) { m_NetworkServerAddress = newAddress.empty() ? "127.0.0.1:8000" : newAddress; } /// /// Gets the NAT punch-through server address. /// /// The current NAT punch-through server address to connect to. - std::string & GetNATServiceAddress() { return m_NATServiceAddress; } + std::string& GetNATServiceAddress() { return m_NATServiceAddress; } /// /// Sets the NAT punch-through server address. /// /// New NAT punch-through server address to connect to. - void SetNATServiceAddress(const std::string &newAddress) { m_NATServiceAddress = newAddress.empty() ? "127.0.0.1:61111" : newAddress; } + void SetNATServiceAddress(const std::string& newAddress) { m_NATServiceAddress = newAddress.empty() ? "127.0.0.1:61111" : newAddress; } /// /// Gets the server name used when connecting via NAT punch-through service. /// /// Name of the NAT punch-through server. - std::string & GetNATServerName() { return m_NATServerName; } + std::string& GetNATServerName() { return m_NATServerName; } /// /// Sets the server name to use when connecting via NAT punch-through service. /// /// New NAT punch-through server name. - void SetNATServerName(const std::string &newName) { m_NATServerName = newName.empty() ? "DefaultServerName" : newName; } + void SetNATServerName(const std::string& newName) { m_NATServerName = newName.empty() ? "DefaultServerName" : newName; } /// /// Gets the server password to use when connecting via NAT punch-through service. /// /// The server password to use when connecting via NAT punch-through service. - std::string & GetNATServerPassword() { return m_NATServerPassword; } + std::string& GetNATServerPassword() { return m_NATServerPassword; } /// /// Sets the server password to use when connecting via NAT punch-through service. /// /// New password to use when connecting via NAT punch-through service. - void SetNATServerPassword(const std::string &newValue) { m_NATServerPassword = newValue.empty() ? "DefaultServerPassword" : newValue; } + void SetNATServerPassword(const std::string& newValue) { m_NATServerPassword = newValue.empty() ? "DefaultServerPassword" : newValue; } /// /// Gets whether or not experimental multiplayer speedboosts should be used. @@ -387,27 +386,27 @@ namespace RTE { /// Gets the map of mods which are disabled. /// /// Map of mods which are disabled. - std::unordered_map & GetDisabledModsMap() { return m_DisabledMods; } + std::unordered_map& GetDisabledModsMap() { return m_DisabledMods; } /// /// Gets whether the specified mod is disabled in the settings. /// /// Mod to check. /// Whether the mod is disabled via settings. - bool IsModDisabled(const std::string &modModule) const { return (m_DisabledMods.find(modModule) != m_DisabledMods.end()) ? m_DisabledMods.at(modModule) : false; } + bool IsModDisabled(const std::string& modModule) const { return (m_DisabledMods.find(modModule) != m_DisabledMods.end()) ? m_DisabledMods.at(modModule) : false; } /// /// Gets the map of global scripts which are enabled. /// /// Map of global scripts which are enabled. - std::unordered_map & GetEnabledGlobalScriptMap() { return m_EnabledGlobalScripts; } + std::unordered_map& GetEnabledGlobalScriptMap() { return m_EnabledGlobalScripts; } /// /// Gets whether the specified global script is enabled in the settings. /// /// Global script to check. /// Whether the global script is enabled via settings. - bool IsGlobalScriptEnabled(const std::string &scriptName) const { return (m_EnabledGlobalScripts.find(scriptName) != m_EnabledGlobalScripts.end()) ? m_EnabledGlobalScripts.at(scriptName) : false; } + bool IsGlobalScriptEnabled(const std::string& scriptName) const { return (m_EnabledGlobalScripts.find(scriptName) != m_EnabledGlobalScripts.end()) ? m_EnabledGlobalScripts.at(scriptName) : false; } #pragma endregion #pragma region Misc Settings @@ -527,7 +526,6 @@ namespace RTE { #pragma endregion protected: - bool m_SettingsNeedOverwrite; //!< Whether the settings file was generated with minimal defaults and needs to be overwritten to be fully populated. bool m_ShowForeignItems; //!< Do not show foreign items in buy menu. @@ -581,7 +579,6 @@ namespace RTE { std::unordered_map m_EnabledGlobalScripts; //!< Map of the global script names we enabled. private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. std::string m_SettingsPath; //!< String containing the Path to the Settings.ini file. @@ -592,8 +589,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - SettingsMan(const SettingsMan &reference) = delete; - SettingsMan & operator=(const SettingsMan &rhs) = delete; + SettingsMan(const SettingsMan& reference) = delete; + SettingsMan& operator=(const SettingsMan& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Managers/ThreadMan.cpp b/Source/Managers/ThreadMan.cpp index 5b20b69880..bb20f8da3c 100644 --- a/Source/Managers/ThreadMan.cpp +++ b/Source/Managers/ThreadMan.cpp @@ -3,54 +3,46 @@ ////////////////////////////////////////////////////////////////////////////////////////// // Description: Source file for the ThreadMan class. // Project: Retro Terrain Engine -// Author(s): -// -// - +// Author(s): +// +// ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files - #include "ThreadMan.h" using namespace std; -namespace RTE -{ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ThreadMan, effectively -// resetting the members of this abstraction level only. - -void ThreadMan::Clear() -{ - m_PriorityThreadPool.reset(); - m_BackgroundThreadPool.reset(std::thread::hardware_concurrency() / 2); -} +namespace RTE { + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ThreadMan, effectively + // resetting the members of this abstraction level only. -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ThreadMan object ready for use. + void ThreadMan::Clear() { + m_PriorityThreadPool.reset(); + m_BackgroundThreadPool.reset(std::thread::hardware_concurrency() / 2); + } -int ThreadMan::Create() -{ - return 0; -} + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ThreadMan object ready for use. + int ThreadMan::Create() { + return 0; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the ThreadMan object. + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the ThreadMan object. -void ThreadMan::Destroy() -{ - Clear(); -} + void ThreadMan::Destroy() { + Clear(); + } } // namespace RTE \ No newline at end of file diff --git a/Source/Managers/ThreadMan.h b/Source/Managers/ThreadMan.h index 7cf623a66d..69f2be9b07 100644 --- a/Source/Managers/ThreadMan.h +++ b/Source/Managers/ThreadMan.h @@ -6,10 +6,9 @@ ////////////////////////////////////////////////////////////////////////////////////////// // Description: Header file for the ThreadMan class. // Project: Retro Terrain Engine -// Author(s): -// -// - +// Author(s): +// +// ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -19,121 +18,109 @@ #include "BS_thread_pool.hpp" -namespace RTE -{ - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: ThreadMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: The centralized singleton manager of all threads. -// Parent(s): Singleton -// Class history: 03/29/2014 ThreadMan created. - - -class ThreadMan: - public Singleton -{ - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: ThreadMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a ThreadMan object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - ThreadMan() { Clear(); Create(); } - - /// - /// Makes the TimerMan object ready for use. - /// - void Initialize() { }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~ThreadMan -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a ThreadMan object before deletion -// from system memory. -// Arguments: None. - - virtual ~ThreadMan() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the ThreadMan object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - virtual int Create(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire ThreadMan, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - virtual void Reset() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the ThreadMan object. -// Arguments: None. -// Return value: None. - - void Destroy(); - - - BS::thread_pool& GetPriorityThreadPool() { return m_PriorityThreadPool; } - - BS::thread_pool& GetBackgroundThreadPool() { return m_BackgroundThreadPool; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this ThreadMan, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - // Disallow the use of some implicit methods. - ThreadMan(const ThreadMan &reference); - ThreadMan & operator=(const ThreadMan &rhs); - - // For tasks that we want to be performed ASAP, i.e needs to be complete this frame at some point - BS::thread_pool m_PriorityThreadPool; - - // For background tasks that we can just let happen whenever over multiple frames - BS::thread_pool m_BackgroundThreadPool; -}; +namespace RTE { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: ThreadMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: The centralized singleton manager of all threads. + // Parent(s): Singleton + // Class history: 03/29/2014 ThreadMan created. + + class ThreadMan : + public Singleton { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: ThreadMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a ThreadMan object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + ThreadMan() { + Clear(); + Create(); + } + + /// + /// Makes the TimerMan object ready for use. + /// + void Initialize(){}; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~ThreadMan + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a ThreadMan object before deletion + // from system memory. + // Arguments: None. + + virtual ~ThreadMan() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the ThreadMan object ready for use. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + virtual int Create(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Virtual method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire ThreadMan, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + virtual void Reset() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the ThreadMan object. + // Arguments: None. + // Return value: None. + + void Destroy(); + + BS::thread_pool& GetPriorityThreadPool() { return m_PriorityThreadPool; } + + BS::thread_pool& GetBackgroundThreadPool() { return m_BackgroundThreadPool; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this ThreadMan, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + ThreadMan(const ThreadMan& reference); + ThreadMan& operator=(const ThreadMan& rhs); + + // For tasks that we want to be performed ASAP, i.e needs to be complete this frame at some point + BS::thread_pool m_PriorityThreadPool; + + // For background tasks that we can just let happen whenever over multiple frames + BS::thread_pool m_BackgroundThreadPool; + }; } // namespace RTE diff --git a/Source/Managers/TimerMan.cpp b/Source/Managers/TimerMan.cpp index 44d06b3098..49b3315572 100644 --- a/Source/Managers/TimerMan.cpp +++ b/Source/Managers/TimerMan.cpp @@ -12,7 +12,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TimerMan::Clear() { m_StartTime = std::chrono::steady_clock::now(); @@ -31,34 +31,36 @@ namespace RTE { m_SimPaused = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TimerMan::Initialize() { // Get the frequency of ticks/s for this machine m_TicksPerSecond = 1000000; ResetTime(); - if (m_DeltaTimeS <= 0) { SetDeltaTimeSecs(c_DefaultDeltaTimeS); } + if (m_DeltaTimeS <= 0) { + SetDeltaTimeSecs(c_DefaultDeltaTimeS); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// long long TimerMan::GetAbsoluteTime() const { return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float TimerMan::GetRealToSimCap() const { return c_RealToSimCap; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float TimerMan::GetAIDeltaTimeSecs() const { return m_DeltaTimeS * static_cast(g_SettingsMan.GetAIUpdateInterval()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TimerMan::ResetTime() { m_StartTime = std::chrono::steady_clock::now(); @@ -72,7 +74,7 @@ namespace RTE { m_TimeScale = 1.0F; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TimerMan::UpdateSim() { if (TimeForSimUpdate()) { @@ -90,7 +92,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TimerMan::Update() { long long prevTime = m_RealTimeTicks; @@ -115,9 +117,9 @@ namespace RTE { // Reset the counter since the last drawn update. Set it negative since we're counting full pure sim updates and this will be incremented to 0 on next SimUpdate. if (m_DrawnSimUpdate) { - m_SimUpdatesSinceDrawn = -1; + m_SimUpdatesSinceDrawn = -1; } m_SimSpeed = std::min(maxPossibleSimSpeed, GetTimeScale()); } -} +} // namespace RTE diff --git a/Source/Managers/TimerMan.h b/Source/Managers/TimerMan.h index d02c897ae4..b3826bf090 100644 --- a/Source/Managers/TimerMan.h +++ b/Source/Managers/TimerMan.h @@ -14,12 +14,14 @@ namespace RTE { class TimerMan : public Singleton { public: - #pragma region Creation /// /// Constructor method used to instantiate a TimerMan object in system memory. Initialize() should be called before using this object. /// - TimerMan() { Clear(); Initialize(); }; + TimerMan() { + Clear(); + Initialize(); + }; /// /// Makes the TimerMan object ready for use. @@ -46,7 +48,11 @@ namespace RTE { /// This also clears the accumulator, to avoid the case where the sim may update while paused when behind schedule. /// /// Whether the sim should be paused or not. - void PauseSim(bool pause = false) { m_SimPaused = pause; if(pause) m_SimAccumulator = 0.0F; } + void PauseSim(bool pause = false) { + m_SimPaused = pause; + if (pause) + m_SimAccumulator = 0.0F; + } /// /// Tells whether there is enough sim time accumulated to do at least one physics update. @@ -131,7 +137,10 @@ namespace RTE { /// Sets the number of ticks that a simulation update delta time should take. /// /// The new delta time in ticks. - void SetDeltaTimeTicks(int newDelta) { m_DeltaTime = newDelta; m_DeltaTimeS = static_cast(m_DeltaTime) / static_cast(m_TicksPerSecond); } + void SetDeltaTimeTicks(int newDelta) { + m_DeltaTime = newDelta; + m_DeltaTimeS = static_cast(m_DeltaTime) / static_cast(m_TicksPerSecond); + } /// /// Gets the current fixed delta time of the simulation updates, in ms. @@ -161,7 +170,10 @@ namespace RTE { /// Sets the number of seconds that a simulation update delta time should take. /// /// The new delta time in seconds. - void SetDeltaTimeSecs(float newDelta) { m_DeltaTimeS = newDelta; m_DeltaTime = static_cast(m_DeltaTimeS * static_cast(m_TicksPerSecond)); } + void SetDeltaTimeSecs(float newDelta) { + m_DeltaTimeS = newDelta; + m_DeltaTime = static_cast(m_DeltaTimeS * static_cast(m_TicksPerSecond)); + } #pragma endregion #pragma region Concrete Methods @@ -190,7 +202,6 @@ namespace RTE { #pragma endregion protected: - std::chrono::steady_clock::time_point m_StartTime; //!< The point in real time when the simulation (re)started. long long m_TicksPerSecond; //!< The frequency of ticks each second, ie the resolution of the timer. long long m_RealTimeTicks; //!< The number of actual microseconds counted so far. @@ -211,15 +222,14 @@ namespace RTE { bool m_SimPaused; //!< Simulation paused; no real time ticks will go to the sim accumulator. private: - /// /// Clears all the member variables of this TimerMan, effectively resetting the members of this abstraction level only. /// void Clear(); // Disallow the use of some implicit methods. - TimerMan(const TimerMan &reference) = delete; - TimerMan & operator=(const TimerMan &rhs) = delete; + TimerMan(const TimerMan& reference) = delete; + TimerMan& operator=(const TimerMan& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Managers/UInputMan.cpp b/Source/Managers/UInputMan.cpp index b1b57d2f98..3671072ca9 100644 --- a/Source/Managers/UInputMan.cpp +++ b/Source/Managers/UInputMan.cpp @@ -24,11 +24,12 @@ namespace RTE { std::vector UInputMan::s_PrevJoystickStates(Players::MaxPlayerCount); std::vector UInputMan::s_ChangedJoystickStates(Players::MaxPlayerCount); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::Clear() { m_SkipHandlingSpecialInput = false; - m_TextInput.clear();; + m_TextInput.clear(); + ; m_NumJoysticks = 0; m_OverrideInput = false; m_AbsoluteMousePos.Reset(); @@ -37,7 +38,7 @@ namespace RTE { m_MouseSensitivity = 0.6F; m_MouseWheelChange = 0; m_TrapMousePos = false; - m_PlayerScreenMouseBounds = { 0, 0, 0, 0 }; + m_PlayerScreenMouseBounds = {0, 0, 0, 0}; m_MouseTrapRadius = 350; m_LastDeviceWhichControlledGUICursor = InputDevice::DEVICE_KEYB_ONLY; m_DisableKeyboard = false; @@ -53,7 +54,7 @@ namespace RTE { std::fill(s_PrevMouseButtonStates.begin(), s_PrevMouseButtonStates.end(), false); std::fill(s_ChangedMouseButtonStates.begin(), s_ChangedMouseButtonStates.end(), false); - for (Gamepad &gamepad: s_PrevJoystickStates) { + for (Gamepad& gamepad: s_PrevJoystickStates) { if (gamepad.m_JoystickID != -1) { SDL_GameControllerClose(SDL_GameControllerFromInstanceID(gamepad.m_JoystickID)); gamepad.m_JoystickID = -1; @@ -86,18 +87,18 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int UInputMan::Initialize() { int numKeys; - const Uint8 *keyboardState = SDL_GetKeyboardState(&numKeys); + const Uint8* keyboardState = SDL_GetKeyboardState(&numKeys); std::copy(keyboardState, keyboardState + numKeys, s_PrevKeyStates.begin()); int controllerIndex = 0; for (size_t index = 0; index < std::min(SDL_NumJoysticks(), static_cast(Players::MaxPlayerCount)); ++index) { if (SDL_IsGameController(index)) { - SDL_GameController *controller = SDL_GameControllerOpen(index); + SDL_GameController* controller = SDL_GameControllerOpen(index); if (!controller) { g_ConsoleMan.PrintString("ERROR: Failed to connect gamepad " + std::to_string(index) + " " + std::string(SDL_GetError())); continue; @@ -109,7 +110,7 @@ namespace RTE { controllerIndex++; m_NumJoysticks++; } else { - SDL_Joystick *joy = SDL_JoystickOpen(index); + SDL_Joystick* joy = SDL_JoystickOpen(index); if (!joy) { g_ConsoleMan.PrintString("ERROR: Failed to connect joystick."); continue; @@ -123,34 +124,33 @@ namespace RTE { } m_PlayerScreenMouseBounds = { - 0, - 0, - static_cast(g_FrameMan.GetPlayerFrameBufferWidth(Players::NoPlayer) * g_WindowMan.GetResMultiplier()), - static_cast(g_FrameMan.GetPlayerFrameBufferHeight(Players::NoPlayer) * g_WindowMan.GetResMultiplier()) - }; + 0, + 0, + static_cast(g_FrameMan.GetPlayerFrameBufferWidth(Players::NoPlayer) * g_WindowMan.GetResMultiplier()), + static_cast(g_FrameMan.GetPlayerFrameBufferHeight(Players::NoPlayer) * g_WindowMan.GetResMultiplier())}; return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::LoadDeviceIcons() { - m_DeviceIcons[InputDevice::DEVICE_KEYB_ONLY] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Keyboard")); - m_DeviceIcons[InputDevice::DEVICE_MOUSE_KEYB] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Mouse")); + m_DeviceIcons[InputDevice::DEVICE_KEYB_ONLY] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Keyboard")); + m_DeviceIcons[InputDevice::DEVICE_MOUSE_KEYB] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Mouse")); for (int gamepad = InputDevice::DEVICE_GAMEPAD_1; gamepad < InputDevice::DEVICE_COUNT; gamepad++) { - m_DeviceIcons[gamepad] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad " + std::to_string(gamepad - 1))); + m_DeviceIcons[gamepad] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Gamepad " + std::to_string(gamepad - 1))); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector UInputMan::AnalogMoveValues(int whichPlayer) { Vector moveValues(0, 0); InputDevice device = m_ControlScheme.at(whichPlayer).GetDevice(); if (device >= InputDevice::DEVICE_GAMEPAD_1) { int whichJoy = GetJoystickIndex(device); - const std::array *inputElements = m_ControlScheme.at(whichPlayer).GetInputMappings(); + const std::array* inputElements = m_ControlScheme.at(whichPlayer).GetInputMappings(); // Assume axes are stretched out over up-down, and left-right. if (inputElements->at(InputElements::INPUT_L_LEFT).JoyDirMapped()) { @@ -163,12 +163,14 @@ namespace RTE { return moveValues; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector UInputMan::AnalogAimValues(int whichPlayer) { InputDevice device = m_ControlScheme.at(whichPlayer).GetDevice(); - if (IsInMultiplayerMode()) { device = InputDevice::DEVICE_MOUSE_KEYB; } + if (IsInMultiplayerMode()) { + device = InputDevice::DEVICE_MOUSE_KEYB; + } Vector aimValues(0, 0); if (device == InputDevice::DEVICE_MOUSE_KEYB) { @@ -176,7 +178,7 @@ namespace RTE { } if (device >= InputDevice::DEVICE_GAMEPAD_1) { int whichJoy = GetJoystickIndex(device); - const std::array *inputElements = m_ControlScheme.at(whichPlayer).GetInputMappings(); + const std::array* inputElements = m_ControlScheme.at(whichPlayer).GetInputMappings(); // Assume axes are stretched out over up-down, and left-right if (inputElements->at(InputElements::INPUT_R_LEFT).JoyDirMapped()) { @@ -189,7 +191,7 @@ namespace RTE { return aimValues; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector UInputMan::GetMenuDirectional() { Vector allInput(0, 0); @@ -237,27 +239,35 @@ namespace RTE { return allInput; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::AnyKeyOrJoyInput() const { bool input = AnyKeyPress(); - if (!input) { input = AnyJoyInput(); } + if (!input) { + input = AnyJoyInput(); + } return input; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::AnyPress() const { bool pressed = false; - if (!pressed) { pressed = AnyKeyPress(); } - if (!pressed) { pressed = AnyMouseButtonPress(); } - if (!pressed) { pressed = AnyJoyPress(); } + if (!pressed) { + pressed = AnyKeyPress(); + } + if (!pressed) { + pressed = AnyMouseButtonPress(); + } + if (!pressed) { + pressed = AnyJoyPress(); + } return pressed; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::AnyStartPress(bool includeSpacebar) { if (KeyPressed(SDLK_ESCAPE) || (includeSpacebar && KeyPressed(SDLK_SPACE))) { @@ -271,7 +281,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::AnyBackPress() { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { @@ -282,7 +292,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::AnyKeyPress() const { for (size_t testKey = SDL_SCANCODE_A; testKey < SDL_NUM_SCANCODES; ++testKey) { @@ -293,7 +303,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int UInputMan::MouseUsedByPlayer() const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { @@ -304,7 +314,7 @@ namespace RTE { return Players::NoPlayer; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::DisableMouseMoving(bool disable) { if (disable) { @@ -317,7 +327,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector UInputMan::GetMouseMovement(int whichPlayer) const { if (IsInMultiplayerMode() && whichPlayer >= Players::PlayerOne && whichPlayer < Players::MaxPlayerCount) { @@ -329,7 +339,7 @@ namespace RTE { return Vector(0, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::SetMouseValueMagnitude(float magCap, int whichPlayer) { if (IsInMultiplayerMode() && whichPlayer >= Players::PlayerOne && whichPlayer < Players::MaxPlayerCount) { @@ -338,7 +348,7 @@ namespace RTE { m_AnalogMouseData.SetMagnitude(m_MouseTrapRadius * magCap); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::SetMouseValueAngle(float angle, int whichPlayer) { if (IsInMultiplayerMode() && whichPlayer >= Players::PlayerOne && whichPlayer < Players::MaxPlayerCount) { @@ -347,16 +357,16 @@ namespace RTE { m_AnalogMouseData.SetAbsRadAngle(angle); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::SetMousePos(const Vector &newPos, int whichPlayer) const { + void UInputMan::SetMousePos(const Vector& newPos, int whichPlayer) const { // Only mess with the mouse if the original mouse position is not above the screen and may be grabbing the title bar of the game window if (!m_DisableMouseMoving && !m_TrapMousePos && (whichPlayer == Players::NoPlayer || m_ControlScheme.at(whichPlayer).GetDevice() == InputDevice::DEVICE_MOUSE_KEYB)) { SDL_WarpMouseInWindow(g_WindowMan.GetWindow(), newPos.GetFloorIntX(), newPos.GetFloorIntY()); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::AnyMouseButtonPress() const { for (int button = MouseButtons::MOUSE_LEFT; button < MouseButtons::MAX_MOUSE_BUTTONS; ++button) { @@ -367,7 +377,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::TrapMousePos(bool trap, int whichPlayer) { if (!IsInMultiplayerMode() && (whichPlayer == Players::NoPlayer || m_ControlScheme.at(whichPlayer).GetDevice() == InputDevice::DEVICE_MOUSE_KEYB)) { @@ -377,7 +387,7 @@ namespace RTE { m_TrapMousePosPerPlayer[whichPlayer] = trap; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::ForceMouseWithinBox(int x, int y, int width, int height, int whichPlayer) const { // Only mess with the mouse if the original mouse position is not above the screen and may be grabbing the title bar of the game window. @@ -400,11 +410,10 @@ namespace RTE { } } else { SDL_Rect newMouseBounds = { - std::clamp(m_PlayerScreenMouseBounds.x + x, m_PlayerScreenMouseBounds.x, rightMostPos), - std::clamp(m_PlayerScreenMouseBounds.y + y, m_PlayerScreenMouseBounds.y, bottomMostPos), - std::clamp(width, 0, rightMostPos - x), - std::clamp(height, 0, bottomMostPos - y) - }; + std::clamp(m_PlayerScreenMouseBounds.x + x, m_PlayerScreenMouseBounds.x, rightMostPos), + std::clamp(m_PlayerScreenMouseBounds.y + y, m_PlayerScreenMouseBounds.y, bottomMostPos), + std::clamp(width, 0, rightMostPos - x), + std::clamp(height, 0, bottomMostPos - y)}; if (newMouseBounds.x >= rightMostPos || newMouseBounds.y >= bottomMostPos) { g_ConsoleMan.PrintString("ERROR: Trying to force mouse wihin a box that is outside the player screen bounds!"); @@ -415,7 +424,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::ForceMouseWithinPlayerScreen(bool force, int whichPlayer) { float resMultiplier = g_WindowMan.GetResMultiplier(); @@ -426,20 +435,20 @@ namespace RTE { switch (g_ActivityMan.GetActivity()->ScreenOfPlayer(whichPlayer)) { case 0: - m_PlayerScreenMouseBounds = { 0, 0, screenWidth, screenHeight }; + m_PlayerScreenMouseBounds = {0, 0, screenWidth, screenHeight}; break; case 1: if (g_FrameMan.GetVSplit()) { - m_PlayerScreenMouseBounds = { screenWidth, 0, screenWidth, screenHeight }; + m_PlayerScreenMouseBounds = {screenWidth, 0, screenWidth, screenHeight}; } else { - m_PlayerScreenMouseBounds = { 0, screenHeight, screenWidth, screenHeight }; + m_PlayerScreenMouseBounds = {0, screenHeight, screenWidth, screenHeight}; } break; case 2: - m_PlayerScreenMouseBounds = { 0, screenHeight, screenWidth, screenHeight }; + m_PlayerScreenMouseBounds = {0, screenHeight, screenWidth, screenHeight}; break; case 3: - m_PlayerScreenMouseBounds = { screenWidth, screenHeight, screenWidth, screenHeight }; + m_PlayerScreenMouseBounds = {screenWidth, screenHeight, screenWidth, screenHeight}; break; default: force = false; @@ -456,12 +465,12 @@ namespace RTE { } } else { // Set the mouse bounds to the whole window so ForceMouseWithinBox is not stuck being relative to some player screen, because it can still bind the mouse even if this doesn't. - m_PlayerScreenMouseBounds = { 0, 0, static_cast(g_WindowMan.GetResX() * resMultiplier), static_cast(g_WindowMan.GetResY() * resMultiplier) }; + m_PlayerScreenMouseBounds = {0, 0, static_cast(g_WindowMan.GetResX() * resMultiplier), static_cast(g_WindowMan.GetResY() * resMultiplier)}; SDL_SetWindowMouseRect(g_WindowMan.GetWindow(), nullptr); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int UInputMan::GetJoystickAxisCount(int whichJoy) const { if (whichJoy >= 0 && whichJoy < s_PrevJoystickStates.size()) { @@ -470,7 +479,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int UInputMan::WhichJoyButtonHeld(int whichJoy) const { if (whichJoy >= 0 && whichJoy < s_PrevJoystickStates.size()) { @@ -484,7 +493,7 @@ namespace RTE { return JoyButtons::JOY_NONE; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int UInputMan::WhichJoyButtonPressed(int whichJoy) const { if (whichJoy >= 0 && whichJoy < s_PrevJoystickStates.size()) { @@ -497,7 +506,7 @@ namespace RTE { return JoyButtons::JOY_NONE; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float UInputMan::AnalogAxisValue(int whichJoy, int whichAxis) const { if (whichJoy < s_PrevJoystickStates.size() && whichAxis < s_PrevJoystickStates[whichJoy].m_Axis.size()) { @@ -506,14 +515,14 @@ namespace RTE { return analogValue; } } - return 0; + return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::AnyJoyInput(bool checkForPresses) const { int gamepadIndex = 0; - for (const Gamepad &gamepad : s_PrevJoystickStates) { + for (const Gamepad& gamepad: s_PrevJoystickStates) { for (int button = 0; button < gamepad.m_Buttons.size(); ++button) { if (!checkForPresses) { if (gamepad.m_Buttons[button]) { @@ -537,7 +546,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::AnyJoyButtonPress(int whichJoy) const { for (int button = 0; button < s_PrevJoystickStates[whichJoy].m_Buttons.size(); ++button) { @@ -548,7 +557,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Vector UInputMan::GetNetworkAccumulatedRawMouseMovement(int player) { Vector accumulatedMovement = m_NetworkAccumulatedRawMouseMovement[player]; @@ -556,7 +565,7 @@ namespace RTE { return accumulatedMovement; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::ClearNetworkAccumulatedStates() { for (int inputState = InputState::Pressed; inputState < InputState::InputStateCount; inputState++) { @@ -566,7 +575,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::GetInputElementState(int whichPlayer, int whichElement, InputState whichState) { if (IsInMultiplayerMode() && whichPlayer >= Players::PlayerOne && whichPlayer < Players::MaxPlayerCount) { @@ -574,17 +583,21 @@ namespace RTE { } bool elementState = false; InputDevice device = m_ControlScheme.at(whichPlayer).GetDevice(); - const InputMapping *element = &(m_ControlScheme.at(whichPlayer).GetInputMappings()->at(whichElement)); + const InputMapping* element = &(m_ControlScheme.at(whichPlayer).GetInputMappings()->at(whichElement)); if (!elementState && device == InputDevice::DEVICE_KEYB_ONLY || (device == InputDevice::DEVICE_MOUSE_KEYB && !(whichElement == InputElements::INPUT_AIM_UP || whichElement == InputElements::INPUT_AIM_DOWN))) { elementState = GetKeyboardButtonState(static_cast(element->GetKey()), whichState); } - if (!elementState && device == InputDevice::DEVICE_MOUSE_KEYB && m_TrapMousePos) { elementState = GetMouseButtonState(whichPlayer, element->GetMouseButton(), whichState); } + if (!elementState && device == InputDevice::DEVICE_MOUSE_KEYB && m_TrapMousePos) { + elementState = GetMouseButtonState(whichPlayer, element->GetMouseButton(), whichState); + } if (!elementState && device >= InputDevice::DEVICE_GAMEPAD_1) { int whichJoy = GetJoystickIndex(device); elementState = GetJoystickButtonState(whichJoy, element->GetJoyButton(), whichState); - if (!elementState && element->JoyDirMapped()) { elementState = GetJoystickDirectionState(whichJoy, element->GetAxis(), element->GetDirection(), whichState); } + if (!elementState && element->JoyDirMapped()) { + elementState = GetJoystickDirectionState(whichJoy, element->GetAxis(), element->GetDirection(), whichState); + } } return elementState; } @@ -605,7 +618,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::GetMenuButtonState(int whichButton, InputState whichState) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { @@ -625,7 +638,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::GetKeyboardButtonState(SDL_Scancode scancodeToTest, InputState whichState) const { if (m_DisableKeyboard && (scancodeToTest >= SDL_SCANCODE_0 && scancodeToTest < SDL_SCANCODE_ESCAPE)) { @@ -644,7 +657,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::GetMouseButtonState(int whichPlayer, int whichButton, InputState whichState) const { if (whichButton < MouseButtons::MOUSE_LEFT || whichButton >= MouseButtons::MAX_MOUSE_BUTTONS) { @@ -668,7 +681,7 @@ namespace RTE { } bool UInputMan::GetNetworkMouseButtonState(int whichPlayer, int whichButton, InputState whichState) const { - + if (whichPlayer == Players::NoPlayer || whichPlayer >= Players::MaxPlayerCount) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (GetNetworkMouseButtonState(player, whichButton, whichState)) { @@ -691,7 +704,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::GetJoystickButtonState(int whichJoy, int whichButton, InputState whichState) const { if (whichJoy < 0 || whichJoy >= s_PrevJoystickStates.size() || whichButton < 0 || whichButton >= s_PrevJoystickStates[whichJoy].m_Buttons.size()) { @@ -715,7 +728,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool UInputMan::GetJoystickDirectionState(int whichJoy, int whichAxis, int whichDir, InputState whichState) const { if (whichJoy < 0 || whichJoy >= s_PrevJoystickStates.size() || whichAxis < 0 || whichAxis >= s_PrevJoystickStates[whichJoy].m_DigitalAxis.size()) { @@ -752,20 +765,20 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::QueueInputEvent(const SDL_Event &inputEvent) { + void UInputMan::QueueInputEvent(const SDL_Event& inputEvent) { m_EventQueue.emplace_back(inputEvent); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int UInputMan::Update() { m_LastDeviceWhichControlledGUICursor = InputDevice::DEVICE_KEYB_ONLY; std::fill(s_ChangedKeyStates.begin(), s_ChangedKeyStates.end(), false); std::fill(s_ChangedMouseButtonStates.begin(), s_ChangedMouseButtonStates.end(), false); - for (Gamepad &gamepad : s_ChangedJoystickStates) { + for (Gamepad& gamepad: s_ChangedJoystickStates) { std::fill(gamepad.m_Buttons.begin(), gamepad.m_Buttons.end(), false); std::fill(gamepad.m_Axis.begin(), gamepad.m_Axis.end(), 0); std::fill(gamepad.m_DigitalAxis.begin(), gamepad.m_DigitalAxis.end(), 0); @@ -841,7 +854,7 @@ namespace RTE { case SDL_CONTROLLERBUTTONUP: case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: - if (std::vector::iterator device = std::find(s_PrevJoystickStates.begin(), s_PrevJoystickStates.end(), (inputEvent.type == SDL_CONTROLLERBUTTONDOWN || inputEvent.type == SDL_CONTROLLERBUTTONUP) ? inputEvent.cbutton.which : inputEvent.jbutton.which); device != s_PrevJoystickStates.end()) { + if (std::vector::iterator device = std::find(s_PrevJoystickStates.begin(), s_PrevJoystickStates.end(), (inputEvent.type == SDL_CONTROLLERBUTTONDOWN || inputEvent.type == SDL_CONTROLLERBUTTONUP) ? inputEvent.cbutton.which : inputEvent.jbutton.which); device != s_PrevJoystickStates.end()) { int button = -1; int state = -1; if (SDL_IsGameController(device->m_DeviceIndex)) { @@ -881,7 +894,6 @@ namespace RTE { default: break; } - } m_EventQueue.clear(); m_RawMouseMovement *= m_MouseSensitivity; @@ -900,7 +912,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::HandleSpecialInput() { // If we launched into editor directly, skip the logic and quit quickly. @@ -910,7 +922,7 @@ namespace RTE { } if (g_ActivityMan.IsInActivity()) { - const GameActivity *gameActivity = dynamic_cast(g_ActivityMan.GetActivity()); + const GameActivity* gameActivity = dynamic_cast(g_ActivityMan.GetActivity()); // Don't allow pausing and returning to main menu when running in server mode to not disrupt the simulation for the clients if (!g_NetworkServer.IsServerModeEnabled() && AnyStartPress(false) && (!gameActivity || !gameActivity->IsBuyGUIVisible(-1))) { g_ActivityMan.PauseActivity(true, FlagShiftState()); @@ -933,13 +945,13 @@ namespace RTE { // Ctrl+S to save continuous ScreenDumps if (KeyHeld(SDLK_s)) { g_FrameMan.SaveScreenToPNG("ScreenDump"); - // Ctrl+W to save a WorldDump + // Ctrl+W to save a WorldDump } else if (KeyPressed(SDLK_w)) { g_FrameMan.SaveWorldToPNG("WorldDump"); - // Ctrl+M to cycle draw modes + // Ctrl+M to cycle draw modes } else if (KeyPressed(SDLK_m)) { g_SceneMan.SetLayerDrawMode((g_SceneMan.GetLayerDrawMode() + 1) % 3); - // Ctrl+P to toggle performance stats + // Ctrl+P to toggle performance stats } else if (KeyPressed(SDLK_p)) { g_PerformanceMan.ShowPerformanceStats(!g_PerformanceMan.IsShowingPerformanceStats()); } else if (KeyPressed(SDLK_F2)) { @@ -956,10 +968,10 @@ namespace RTE { } else if (!FlagCtrlState() && FlagAltState()) { if (KeyPressed(SDLK_F2)) { ContentFile::ReloadAllBitmaps(); - // Alt+Enter to switch resolution multiplier + // Alt+Enter to switch resolution multiplier } else if (KeyPressed(SDLK_RETURN)) { g_WindowMan.ToggleFullscreen(); - // Alt+W to save ScenePreviewDump (miniature WorldDump) + // Alt+W to save ScenePreviewDump (miniature WorldDump) } else if (KeyPressed(SDLK_w)) { g_FrameMan.SaveWorldPreviewToPNG("ScenePreviewDump"); } else if (g_PerformanceMan.IsShowingPerformanceStats()) { @@ -986,7 +998,7 @@ namespace RTE { g_ActivityMan.LoadAndLaunchGame("QuickSave"); } else if (KeyPressed(SDLK_F10)) { g_ConsoleMan.ClearLog(); - // F12 to save a single ScreenDump - Note that F12 triggers a breakpoint when the VS debugger is attached, regardless of config - this is by design. Thanks Microsoft. + // F12 to save a single ScreenDump - Note that F12 triggers a breakpoint when the VS debugger is attached, regardless of config - this is by design. Thanks Microsoft. } else if (KeyPressed(SDLK_F12)) { g_FrameMan.SaveScreenToPNG("ScreenDump"); } @@ -1011,7 +1023,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::UpdateMouseInput() { // Detect and store mouse movement input, translated to analog stick emulation @@ -1037,9 +1049,7 @@ namespace RTE { } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::UpdateJoystickAxis(std::vector::iterator device, int axis, int value) { if (device != s_PrevJoystickStates.end()) { @@ -1067,7 +1077,7 @@ namespace RTE { bool isAxisMapped = false; if (joystickPlayer != Players::NoPlayer && deadZone > 0.0F) { Vector aimValues; - const std::array *inputElements = m_ControlScheme[joystickPlayer].GetInputMappings(); + const std::array* inputElements = m_ControlScheme[joystickPlayer].GetInputMappings(); std::array elementsToCheck = {InputElements::INPUT_L_LEFT, InputElements::INPUT_L_UP, InputElements::INPUT_R_LEFT, InputElements::INPUT_R_UP}; for (size_t i = 0; i < elementsToCheck.size(); i += 2) { @@ -1109,7 +1119,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::UpdateJoystickDigitalAxis() { for (size_t i = 0; i < s_PrevJoystickStates.size(); ++i) { @@ -1130,16 +1140,16 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::HandleGamepadHotPlug(int deviceIndex) { - SDL_Joystick *controller = nullptr; + SDL_Joystick* controller = nullptr; int controllerIndex = 0; for (controllerIndex = 0; controllerIndex < s_PrevJoystickStates.size(); ++controllerIndex) { if (s_PrevJoystickStates[controllerIndex].m_DeviceIndex == deviceIndex || s_PrevJoystickStates[controllerIndex].m_DeviceIndex == -1) { if (SDL_IsGameController(deviceIndex)) { - SDL_GameController *gameController = SDL_GameControllerOpen(deviceIndex); + SDL_GameController* gameController = SDL_GameControllerOpen(deviceIndex); if (!gameController) { std::string connectString = s_PrevJoystickStates[controllerIndex].m_DeviceIndex == deviceIndex ? "reconnect" : "connect"; g_ConsoleMan.PrintString("ERROR: Failed to " + connectString + " Gamepad " + std::to_string(controllerIndex + 1)); @@ -1182,8 +1192,7 @@ namespace RTE { } } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::UpdateNetworkMouseMovement() { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { if (!m_NetworkAccumulatedRawMouseMovement[player].IsZero()) { @@ -1194,24 +1203,23 @@ namespace RTE { } m_NetworkAccumulatedRawMouseMovement[player].Reset(); - // Reset mouse wheel state to stop over-wheeling m_NetworkMouseWheelState[player] = 0; } } void UInputMan::ClearNetworkChangedState() { - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - for (int element = InputElements::INPUT_L_UP; element < InputElements::INPUT_COUNT; element++) { - m_NetworkServerChangedInputElementState[player][element] = false; - } - for (int mouseButton = MouseButtons::MOUSE_LEFT; mouseButton < MouseButtons::MAX_MOUSE_BUTTONS; mouseButton++) { - m_NetworkServerChangedMouseButtonState[player][mouseButton] = false; - } - m_NetworkMouseWheelState[player] = 0; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + for (int element = InputElements::INPUT_L_UP; element < InputElements::INPUT_COUNT; element++) { + m_NetworkServerChangedInputElementState[player][element] = false; + } + for (int mouseButton = MouseButtons::MOUSE_LEFT; mouseButton < MouseButtons::MAX_MOUSE_BUTTONS; mouseButton++) { + m_NetworkServerChangedMouseButtonState[player][mouseButton] = false; } + m_NetworkMouseWheelState[player] = 0; + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::SetNetworkInputElementState(int player, int element, bool newState) { if (element >= InputElements::INPUT_L_UP && element < InputElements::INPUT_COUNT && player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_NetworkServerChangedInputElementState[player][element] = (newState != m_NetworkServerPreviousInputElementState[player][element]); @@ -1219,7 +1227,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::SetNetworkMouseButtonState(int player, int whichButton, InputState whichState, bool newState) { if (whichButton >= MouseButtons::MOUSE_LEFT && whichButton < MouseButtons::MAX_MOUSE_BUTTONS && player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_NetworkServerChangedMouseButtonState[player][whichButton] = (newState != m_NetworkServerPreviousMouseButtonState[player][whichButton]); @@ -1227,7 +1235,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::StoreInputEventsForNextUpdate() { // Store pressed and released events to be picked by NetworkClient during its update. These will be cleared after update so we don't care about false but we store the result regardless. for (int inputState = InputState::Pressed; inputState < InputState::InputStateCount; inputState++) { @@ -1237,7 +1245,4 @@ namespace RTE { } } - - - -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Managers/UInputMan.h b/Source/Managers/UInputMan.h index 65d2950ad2..c8c3d39075 100644 --- a/Source/Managers/UInputMan.h +++ b/Source/Managers/UInputMan.h @@ -11,7 +11,7 @@ #define g_UInputMan UInputMan::Instance() extern "C" { - struct SDL_Rect; +struct SDL_Rect; } namespace RTE { @@ -25,11 +25,14 @@ namespace RTE { friend class SettingsMan; public: - /// /// Enumeration for the mouse cursor actions in menus. /// - enum MenuCursorButtons { MENU_PRIMARY, MENU_SECONDARY, MENU_EITHER }; + enum MenuCursorButtons { + MENU_PRIMARY, + MENU_SECONDARY, + MENU_EITHER + }; #pragma region Creation /// @@ -66,7 +69,7 @@ namespace RTE { /// Adds an (input) SDL_Event to the Event queue for processing on Update. /// /// The SDL input event to queue. - void QueueInputEvent(const SDL_Event &inputEvent); + void QueueInputEvent(const SDL_Event& inputEvent); /// /// Updates the state of this UInputMan. Supposed to be done every frame. @@ -94,21 +97,21 @@ namespace RTE { /// /// Which player to get the scheme for. /// A pointer to the requested player's control scheme. Ownership is NOT transferred! - InputScheme * GetControlScheme(int whichPlayer) { return IsInMultiplayerMode() ? &m_ControlScheme[Players::PlayerOne] : &m_ControlScheme.at(whichPlayer); } + InputScheme* GetControlScheme(int whichPlayer) { return IsInMultiplayerMode() ? &m_ControlScheme[Players::PlayerOne] : &m_ControlScheme.at(whichPlayer); } /// /// Get the current device Icon of a specific player's scheme. /// /// Which player to get the scheme device icon of. /// A const pointer to the requested player's control scheme icon. Ownership is NOT transferred! - const Icon * GetSchemeIcon(int whichPlayer) const { return (whichPlayer < Players::PlayerOne || whichPlayer >= Players::MaxPlayerCount) ? nullptr : m_DeviceIcons[m_ControlScheme.at(whichPlayer).GetDevice()]; } + const Icon* GetSchemeIcon(int whichPlayer) const { return (whichPlayer < Players::PlayerOne || whichPlayer >= Players::MaxPlayerCount) ? nullptr : m_DeviceIcons[m_ControlScheme.at(whichPlayer).GetDevice()]; } /// /// Get the current device Icon of a specific device. /// /// Which device to get the icon of. /// A const pointer to the requested device's control scheme icon. Ownership is NOT transferred! - const Icon * GetDeviceIcon(int whichDevice) const { return (whichDevice < InputDevice::DEVICE_KEYB_ONLY || whichDevice > InputDevice::DEVICE_GAMEPAD_4) ? nullptr : m_DeviceIcons[whichDevice]; } + const Icon* GetDeviceIcon(int whichDevice) const { return (whichDevice < InputDevice::DEVICE_KEYB_ONLY || whichDevice > InputDevice::DEVICE_GAMEPAD_4) ? nullptr : m_DeviceIcons[whichDevice]; } #pragma endregion #pragma region General Input Handling @@ -246,7 +249,7 @@ namespace RTE { /// /// A keycode to test. See SDL_KeyCode enumeration. /// Whether the key is held or not. - bool KeyHeld(SDL_Keycode keycodeToTest) const { return KeyHeld(SDL_GetScancodeFromKey(keycodeToTest));} + bool KeyHeld(SDL_Keycode keycodeToTest) const { return KeyHeld(SDL_GetScancodeFromKey(keycodeToTest)); } /// /// Gets whether a key was pressed between the last update and the one previous to it, by scancode. @@ -270,7 +273,7 @@ namespace RTE { bool KeyReleased(SDL_Scancode scancodeToTest) const { return GetKeyboardButtonState(scancodeToTest, InputState::Released); } /// - ///Gets whether a key was released between the last update and the one previous to it, by keycode. + /// Gets whether a key was released between the last update and the one previous to it, by keycode. /// /// A keycode to test. See SDL_KeyCode enumeration. /// Whether the key is released or not. @@ -287,7 +290,10 @@ namespace RTE { /// /// The std::string to fill. /// Whether there is text input. - bool GetTextInput(std::string &text) const { text = m_TextInput; return !m_TextInput.empty(); } + bool GetTextInput(std::string& text) const { + text = m_TextInput; + return !m_TextInput.empty(); + } /// /// Returns whether text input events are available. @@ -298,7 +304,7 @@ namespace RTE { /// Returns the current text input. /// /// The current text input. - const std::string& GetTextInput() const {return m_TextInput; } + const std::string& GetTextInput() const { return m_TextInput; } #pragma endregion #pragma region Mouse Handling @@ -325,7 +331,7 @@ namespace RTE { /// Set the absolute mouse position (e.g. for player input mouse movement). Does not move the system cursor. /// /// The new mouse position. - void SetAbsoluteMousePosition(const Vector &pos) { m_AbsoluteMousePos = pos; } + void SetAbsoluteMousePosition(const Vector& pos) { m_AbsoluteMousePos = pos; } /// /// Gets the relative movement of the mouse since last update. Only returns true if the selected player is actually using the mouse. @@ -353,7 +359,7 @@ namespace RTE { /// /// Where to place the mouse. /// Which player is trying to control the mouse. Only the player with actual control over the mouse will be affected. -1 means do it regardless of player. - void SetMousePos(const Vector &newPos, int whichPlayer = -1) const; + void SetMousePos(const Vector& newPos, int whichPlayer = -1) const; /// /// Gets mouse sensitivity while in Activity. @@ -581,7 +587,7 @@ namespace RTE { /// /// The player to set for. /// The new position of the mouse. - void SetNetworkMouseMovement(int player, const Vector &input) { m_NetworkAccumulatedRawMouseMovement[player] += input; } + void SetNetworkMouseMovement(int player, const Vector& input) { m_NetworkAccumulatedRawMouseMovement[player] += input; } /// /// Sets whether an input element is held by a player during network multiplayer. @@ -620,7 +626,11 @@ namespace RTE { /// /// The player to set for. /// The new state of the mouse wheel. - void SetNetworkMouseWheelState(int player, int state) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_NetworkMouseWheelState[player] += state; } } + void SetNetworkMouseWheelState(int player, int state) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { + m_NetworkMouseWheelState[player] += state; + } + } /// /// Gets whether the specified input element is pressed during network multiplayer. @@ -643,11 +653,15 @@ namespace RTE { #pragma endregion private: - /// /// Enumeration for the different states an input element or button can be in. /// - enum InputState { Held, Pressed, Released, InputStateCount }; + enum InputState { + Held, + Pressed, + Released, + InputStateCount + }; static std::array s_PrevKeyStates; //!< Key states as they were the previous update. static std::array s_ChangedKeyStates; //!< Key states that have changed. @@ -670,7 +684,7 @@ namespace RTE { bool m_OverrideInput; //!< If true then this instance operates in multiplayer mode and the input is overridden by network input. std::array m_ControlScheme; //!< Which control scheme is being used by each player. - const Icon *m_DeviceIcons[InputDevice::DEVICE_COUNT]; //!< The Icons representing all different devices. + const Icon* m_DeviceIcons[InputDevice::DEVICE_COUNT]; //!< The Icons representing all different devices. Vector m_AbsoluteMousePos; //!< The absolute mouse position in screen coordinates. Vector m_RawMouseMovement; //!< The raw absolute movement of the mouse between the last two Updates. @@ -738,7 +752,7 @@ namespace RTE { /// Which menu button to check for. See MenuButtons enumeration. /// Which state to check for. See InputState enumeration. /// Whether the menu button is in the specified state or not. - bool GetMenuButtonState(int whichButton, InputState whichState) ; + bool GetMenuButtonState(int whichButton, InputState whichState); /// /// Gets whether a keyboard key is in the specified state. @@ -854,8 +868,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - UInputMan(const UInputMan &reference) = delete; - UInputMan & operator=(const UInputMan &rhs) = delete; + UInputMan(const UInputMan& reference) = delete; + UInputMan& operator=(const UInputMan& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Managers/WindowMan.cpp b/Source/Managers/WindowMan.cpp index 20691eca99..8ce090df17 100644 --- a/Source/Managers/WindowMan.cpp +++ b/Source/Managers/WindowMan.cpp @@ -24,13 +24,13 @@ namespace RTE { - void SDLWindowDeleter::operator()(SDL_Window *window) const { SDL_DestroyWindow(window); } - void SDLRendererDeleter::operator()(SDL_Renderer *renderer) const { SDL_DestroyRenderer(renderer); } - void SDLTextureDeleter::operator()(SDL_Texture *texture) const { SDL_DestroyTexture(texture); } + void SDLWindowDeleter::operator()(SDL_Window* window) const { SDL_DestroyWindow(window); } + void SDLRendererDeleter::operator()(SDL_Renderer* renderer) const { SDL_DestroyRenderer(renderer); } + void SDLTextureDeleter::operator()(SDL_Texture* texture) const { SDL_DestroyTexture(texture); } void SDLContextDeleter::operator()(SDL_GLContext context) const { SDL_GL_DeleteContext(context); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::Clear() { m_EventQueue.clear(); @@ -67,7 +67,7 @@ namespace RTE { m_UseMultiDisplays = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::ClearMultiDisplayData() { m_MultiDisplayTextureOffsets.clear(); @@ -75,13 +75,13 @@ namespace RTE { m_MultiDisplayWindows.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// WindowMan::WindowMan() { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// WindowMan::~WindowMan() = default; @@ -93,7 +93,7 @@ namespace RTE { glDeleteFramebuffers(1, &m_ScreenBufferFBO); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::Initialize() { m_NumDisplays = SDL_GetNumVideoDisplays(); @@ -129,7 +129,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::CreatePrimaryWindow() { std::string windowTitle = "Cortex Command Community Project"; @@ -180,7 +180,7 @@ namespace RTE { } #ifdef __linux__ - SDL_Surface *iconSurface = IMG_ReadXPMFromArray(ccicon); + SDL_Surface* iconSurface = IMG_ReadXPMFromArray(ccicon); if (iconSurface) { SDL_SetWindowIcon(m_PrimaryWindow.get(), iconSurface); SDL_FreeSurface(iconSurface); @@ -213,7 +213,7 @@ namespace RTE { glBufferData(GL_ARRAY_BUFFER, sizeof(c_Quad), c_Quad.data(), GL_STATIC_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr); glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float))); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); glEnableVertexAttribArray(1); glBindVertexArray(0); glGenTextures(1, &m_BackBuffer32Texture); @@ -222,7 +222,7 @@ namespace RTE { TracyGpuContext; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::CreateBackBufferTexture() { glBindTexture(GL_TEXTURE_2D, m_BackBuffer32Texture); @@ -235,7 +235,7 @@ namespace RTE { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int WindowMan::GetWindowResX() { int w, h; @@ -263,7 +263,7 @@ namespace RTE { SDL_GL_SetSwapInterval(sdlEnableVSync); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::UpdatePrimaryDisplayInfo() { m_PrimaryWindowDisplayIndex = SDL_GetWindowDisplayIndex(m_PrimaryWindow.get()); @@ -276,14 +276,14 @@ namespace RTE { } SDL_Rect WindowMan::GetUsableBoundsWithDecorations(int display) { - if(m_Fullscreen) { + if (m_Fullscreen) { SDL_Rect displayBounds; SDL_GetDisplayBounds(display, &displayBounds); return displayBounds; } SDL_Rect displayBounds; SDL_GetDisplayUsableBounds(display, &displayBounds); - + int top, left, bottom, right; SDL_GetWindowBordersSize(m_PrimaryWindow.get(), &top, &left, &bottom, &right); displayBounds.x += left; @@ -298,15 +298,14 @@ namespace RTE { if (resMultiplier == 1) { return (resX == displayBounds.w) && (resY == displayBounds.h); } else { - return glm::epsilonEqual(resX * resMultiplier, displayBounds.w, resMultiplier) - && glm::epsilonEqual(resY * resMultiplier, displayBounds.h, resMultiplier); + return glm::epsilonEqual(resX * resMultiplier, displayBounds.w, resMultiplier) && glm::epsilonEqual(resY * resMultiplier, displayBounds.h, resMultiplier); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::MapDisplays(bool updatePrimaryDisplayInfo) { - auto setSingleDisplayMode = [this](const std::string &errorMsg = "") { + auto setSingleDisplayMode = [this](const std::string& errorMsg = "") { m_MaxResX = m_PrimaryWindowDisplayWidth; m_MaxResY = m_PrimaryWindowDisplayHeight; m_MaxResMultiplier = std::min(m_MaxResX / static_cast(c_MinResX), m_MaxResY / static_cast(c_MinResY)); @@ -361,11 +360,11 @@ namespace RTE { } std::stable_sort(m_ValidDisplayIndicesAndBoundsForMultiDisplayFullscreen.begin(), m_ValidDisplayIndicesAndBoundsForMultiDisplayFullscreen.end(), - [](auto left, auto right) { - return left.second.x < right.second.x; - }); + [](auto left, auto right) { + return left.second.x < right.second.x; + }); - for (const auto &[displayIndex, displayBounds] : m_ValidDisplayIndicesAndBoundsForMultiDisplayFullscreen) { + for (const auto& [displayIndex, displayBounds]: m_ValidDisplayIndicesAndBoundsForMultiDisplayFullscreen) { // Translate display offsets to backbuffer offsets, where the top left corner is (0,0) to figure out if the display arrangement is unreasonable garbage, i.e not top or bottom edge aligned. // If any of the translated offsets ends up negative, or over-positive for the Y offset, disallow going into multi-display fullscreen // because we'll just end up with an access violation when trying to read from the backbuffer pixel array during rendering. @@ -378,7 +377,7 @@ namespace RTE { } } - for (const auto &[displayIndex, displayBounds] : m_ValidDisplayIndicesAndBoundsForMultiDisplayFullscreen) { + for (const auto& [displayIndex, displayBounds]: m_ValidDisplayIndicesAndBoundsForMultiDisplayFullscreen) { #if SDL_VERSION_ATLEAST(2, 24, 0) m_DisplayArrangmentLeftMostDisplayIndex = SDL_GetRectDisplayIndex(&displayBounds); if (m_DisplayArrangmentLeftMostDisplayIndex >= 0) { @@ -404,17 +403,16 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::ValidateResolution(int &resX, int &resY, float &resMultiplier) const { + void WindowMan::ValidateResolution(int& resX, int& resY, float& resMultiplier) const { if (resX < c_MinResX || resY < c_MinResY) { resX = c_MinResX; resY = c_MinResY; resMultiplier = 1.0f; RTEError::ShowMessageBox("Resolution too low, overriding to fit!"); g_SettingsMan.SetSettingsNeedOverwrite(); - } - else if (resMultiplier > m_MaxResMultiplier) { + } else if (resMultiplier > m_MaxResMultiplier) { resMultiplier = 1.0f; RTEError::ShowMessageBox("Resolution multiplier too high, overriding to fit!"); g_SettingsMan.SetSettingsNeedOverwrite(); @@ -440,7 +438,7 @@ namespace RTE { m_PrimaryWindowViewport = std::make_unique(offsetX, windowH - offsetY - height, width, height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::AttemptToRevertToPreviousResolution(bool revertToDefaults) { auto setDefaultResSettings = [this]() { @@ -474,7 +472,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::ChangeResolution(int newResX, int newResY, float newResMultiplier, bool fullscreen, bool displaysAlreadyMapped) { @@ -540,7 +538,7 @@ namespace RTE { g_ConsoleMan.PrintString("SYSTEM: " + std::string(!recoveredToPreviousSettings ? "Switched to different resolution." : "Failed to switch to different resolution. Reverted to previous settings.")); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::ToggleFullscreen() { bool fullscreen = !m_Fullscreen; @@ -557,7 +555,7 @@ namespace RTE { ChangeResolution(m_ResX, m_ResY, m_ResMultiplier, fullscreen, true); } - if(!fullscreen) { + if (!fullscreen) { SDL_SetWindowFullscreen(m_PrimaryWindow.get(), 0); SDL_SetWindowMinimumSize(m_PrimaryWindow.get(), c_MinResX, c_MinResY); } else { @@ -571,7 +569,7 @@ namespace RTE { SetViewportLetterboxed(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool WindowMan::ChangeResolutionToMultiDisplayFullscreen(float resMultiplier) { if (!m_CanMultiDisplayFullscreen) { @@ -589,7 +587,7 @@ namespace RTE { bool errorSettingFullscreen = false; - for (const auto &[displayIndex, displayBounds] : m_ValidDisplayIndicesAndBoundsForMultiDisplayFullscreen) { + for (const auto& [displayIndex, displayBounds]: m_ValidDisplayIndicesAndBoundsForMultiDisplayFullscreen) { int displayOffsetX = displayBounds.x; int displayOffsetY = displayBounds.y; int displayWidth = displayBounds.w; @@ -613,11 +611,11 @@ namespace RTE { glm::mat4 textureOffset = glm::translate(glm::mat4(1), {m_DisplayArrangementLeftMostOffset, m_DisplayArrangementTopMostOffset, 0.0f}); textureOffset = glm::scale(textureOffset, {m_MaxResX * 0.5f, m_MaxResY * 0.5f, 1.0f}); - textureOffset = glm::translate(textureOffset, {1.0f, 1.0f, 0.0f}); //Shift the quad so we're scaling from top left instead of center. + textureOffset = glm::translate(textureOffset, {1.0f, 1.0f, 0.0f}); // Shift the quad so we're scaling from top left instead of center. m_MultiDisplayTextureOffsets.emplace_back(textureOffset); glm::mat4 projection = glm::ortho(static_cast(textureOffsetX), static_cast(textureOffsetX + displayWidth), static_cast(textureOffsetY), static_cast(textureOffsetY + displayHeight), -1.0f, 1.0f); - m_MultiDisplayProjections.emplace_back( projection); + m_MultiDisplayProjections.emplace_back(projection); } if (errorSettingFullscreen) { @@ -630,14 +628,14 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::DisplaySwitchIn(SDL_Window *windowThatShouldTakeInputFocus) const { + void WindowMan::DisplaySwitchIn(SDL_Window* windowThatShouldTakeInputFocus) const { g_UInputMan.DisableMouseMoving(false); g_UInputMan.DisableKeys(false); if (!m_MultiDisplayWindows.empty()) { - for (const auto &window : m_MultiDisplayWindows) { + for (const auto& window: m_MultiDisplayWindows) { SDL_RaiseWindow(window.get()); } SDL_RaiseWindow(windowThatShouldTakeInputFocus); @@ -649,7 +647,7 @@ namespace RTE { SDL_ShowCursor(SDL_DISABLE); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::DisplaySwitchOut() const { g_UInputMan.DisableMouseMoving(true); @@ -660,16 +658,16 @@ namespace RTE { SDL_SetCursor(nullptr); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::QueueWindowEvent(const SDL_Event &windowEvent) { + void WindowMan::QueueWindowEvent(const SDL_Event& windowEvent) { if (g_UInputMan.IsInMultiplayerMode()) { return; } m_EventQueue.emplace_back(windowEvent); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::Update() { // Some bullshit we have to deal with to correctly focus windows in multi-display fullscreen so mouse binding/unbinding works correctly. Not relevant for single window. @@ -693,7 +691,7 @@ namespace RTE { switch (windowEvent.window.event) { case SDL_WINDOWEVENT_ENTER: if (SDL_GetWindowID(SDL_GetMouseFocus()) > 0 && m_AnyWindowHasFocus && FullyCoversAllDisplays()) { - for (const auto &window : m_MultiDisplayWindows) { + for (const auto& window: m_MultiDisplayWindows) { SDL_RaiseWindow(window.get()); } SDL_RaiseWindow(SDL_GetWindowFromID(windowID)); @@ -724,7 +722,7 @@ namespace RTE { m_EventQueue.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::ClearRenderer() { glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -737,7 +735,7 @@ namespace RTE { m_DrawPostProcessBuffer = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WindowMan::UploadFrame() { TracyGpuZone("Upload Frame"); diff --git a/Source/Managers/WindowMan.h b/Source/Managers/WindowMan.h index 2928137022..da7ce6c9bc 100644 --- a/Source/Managers/WindowMan.h +++ b/Source/Managers/WindowMan.h @@ -7,11 +7,11 @@ #define g_WindowMan WindowMan::Instance() extern "C" { - struct SDL_Window; - struct SDL_Renderer; - struct SDL_Texture; - struct SDL_Rect; - union SDL_Event; +struct SDL_Window; +struct SDL_Renderer; +struct SDL_Texture; +struct SDL_Rect; +union SDL_Event; } namespace RTE { @@ -19,19 +19,19 @@ namespace RTE { class Shader; struct SDLWindowDeleter { - void operator() (SDL_Window *window) const; + void operator()(SDL_Window* window) const; }; struct SDLRendererDeleter { - void operator()(SDL_Renderer *renderer) const; + void operator()(SDL_Renderer* renderer) const; }; struct SDLTextureDeleter { - void operator()(SDL_Texture *texture) const; + void operator()(SDL_Texture* texture) const; }; struct SDLContextDeleter { - void operator()(void *context) const; + void operator()(void* context) const; }; /// @@ -41,7 +41,6 @@ namespace RTE { friend class SettingsMan; public: - #pragma region Creation /// /// Constructor method used to instantiate a WindowMan object in system memory. Initialize() should be called before using the object. @@ -71,7 +70,7 @@ namespace RTE { /// Gets a pointer to the primary game window. OWNERSHIP IS NOT TRANSFERRED! /// /// Pointer to the primary game window. - SDL_Window * GetWindow() const { return m_PrimaryWindow.get(); } + SDL_Window* GetWindow() const { return m_PrimaryWindow.get(); } /// /// Gets whether any of the game windows is currently in focus. @@ -109,7 +108,6 @@ namespace RTE { /// The horizontal resolution the game window is currently sized at, in pixels. int GetWindowResX(); - /// /// Gets the vertical resolution the game window is currently sized at, in pixels. /// @@ -227,7 +225,7 @@ namespace RTE { /// Adds an SDL_Event to the Event queue for processing on Update. /// /// The SDL window event to queue. - void QueueWindowEvent(const SDL_Event &windowEvent); + void QueueWindowEvent(const SDL_Event& windowEvent); /// /// Updates the state of this WindowMan. @@ -251,7 +249,6 @@ namespace RTE { #pragma endregion private: - std::vector m_EventQueue; //!< List of incoming window events. bool m_FocusEventsDispatchedByMovingBetweenWindows; //!< Whether queued events were dispatched due to raising windows when moving between windows in multi-display fullscreen in the previous update. @@ -351,7 +348,7 @@ namespace RTE { /// Game window width to check. /// Game window height to check. /// Game window resolution multiplier to check. - void ValidateResolution(int &resX, int &resY, float &resMultiplier) const; + void ValidateResolution(int& resX, int& resY, float& resMultiplier) const; /// /// Attempts to revert to the previous resolution settings if the new ones failed for whatever reason. Will recursively attempt to revert to defaults if previous settings fail as well. @@ -379,7 +376,7 @@ namespace RTE { /// Handles focus gain when switching back to the game window. /// /// The window that should take focus of input after all the windows are raised. This is only relevant in multi-display fullscreen. - void DisplaySwitchIn(SDL_Window *windowThatShouldTakeInputFocus) const; + void DisplaySwitchIn(SDL_Window* windowThatShouldTakeInputFocus) const; /// /// Handles focus loss when switching away from the game window. @@ -394,8 +391,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - WindowMan(const WindowMan &reference) = delete; - WindowMan & operator=(const WindowMan &rhs) = delete; + WindowMan(const WindowMan& reference) = delete; + WindowMan& operator=(const WindowMan& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/AreaEditorGUI.cpp b/Source/Menus/AreaEditorGUI.cpp index 960f6d8fb9..dfc5acd792 100644 --- a/Source/Menus/AreaEditorGUI.cpp +++ b/Source/Menus/AreaEditorGUI.cpp @@ -7,7 +7,6 @@ // dtabar@datArealms.com // http://www.datArealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -37,95 +36,89 @@ using namespace RTE; // Description: Clears all the member variables of this AreaEditorGUI, effectively // resetting the members of this abstraction level only. -void AreaEditorGUI::Clear() -{ - m_pController = 0; - m_FullFeatured = false; - m_EditMade = false; - m_EditorGUIMode = PICKINGAREA; - m_PreviousMode = PREADDMOVEBOX; - m_BlinkTimer.Reset(); - m_BlinkMode = NOBLINK; - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); +void AreaEditorGUI::Clear() { + m_pController = 0; + m_FullFeatured = false; + m_EditMade = false; + m_EditorGUIMode = PICKINGAREA; + m_PreviousMode = PREADDMOVEBOX; + m_BlinkTimer.Reset(); + m_BlinkMode = NOBLINK; + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); m_PieMenu = nullptr; - m_pPicker = 0; - m_GridSnapping = true; - m_CursorPos.Reset(); - m_CursorOffset.Reset(); - m_CursorInAir = true; - m_pCurrentArea = 0; - m_EditedBox.Reset(); - m_pBoxToBlink = 0; + m_pPicker = 0; + m_GridSnapping = true; + m_CursorPos.Reset(); + m_CursorOffset.Reset(); + m_CursorInAir = true; + m_pCurrentArea = 0; + m_EditedBox.Reset(); + m_pBoxToBlink = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the AreaEditorGUI Area ready for use. -int AreaEditorGUI::Create(Controller *pController, bool fullFeatured, int whichModuleSpace) -{ - RTEAssert(pController, "No controller sent to AreaEditorGUI on creation!"); - m_pController = pController; +int AreaEditorGUI::Create(Controller* pController, bool fullFeatured, int whichModuleSpace) { + RTEAssert(pController, "No controller sent to AreaEditorGUI on creation!"); + m_pController = pController; - m_FullFeatured = fullFeatured; + m_FullFeatured = fullFeatured; - if (m_PieMenu) { m_PieMenu = nullptr; } + if (m_PieMenu) { + m_PieMenu = nullptr; + } std::string pieMenuName = m_FullFeatured ? "Area Editor Full Pie Menu" : "Area Editor Minimal Pie Menu"; - m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", pieMenuName)->Clone())); + m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", pieMenuName)->Clone())); m_PieMenu->SetMenuController(pController); - // Allocate and (re)create the Editor GUIs - if (!m_pPicker) - m_pPicker = new AreaPickerGUI(); - else - m_pPicker->Destroy(); - m_pPicker->Create(pController); + // Allocate and (re)create the Editor GUIs + if (!m_pPicker) + m_pPicker = new AreaPickerGUI(); + else + m_pPicker->Destroy(); + m_pPicker->Create(pController); - // Cursor init - m_CursorPos = g_SceneMan.GetSceneDim() / 2; + // Cursor init + m_CursorPos = g_SceneMan.GetSceneDim() / 2; - // Set initial focus, category list, and label settings - m_EditorGUIMode = PICKINGAREA; - m_pCurrentArea = 0; + // Set initial focus, category list, and label settings + m_EditorGUIMode = PICKINGAREA; + m_pCurrentArea = 0; - // Reset repeat timers - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); + // Reset repeat timers + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); - return 0; + return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the AreaEditorGUI Area. -void AreaEditorGUI::Destroy() -{ - delete m_pPicker; +void AreaEditorGUI::Destroy() { + delete m_pPicker; - Clear(); + Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetController ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets the controller used by this. The ownership of the controller is // NOT transferred! -void AreaEditorGUI::SetController(Controller *pController) -{ - m_pController = pController; +void AreaEditorGUI::SetController(Controller* pController) { + m_pController = pController; m_PieMenu->SetMenuController(pController); - m_pPicker->SetController(pController); + m_pPicker->SetController(pController); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetPosOnScreen ////////////////////////////////////////////////////////////////////////////////////////// @@ -133,12 +126,10 @@ void AreaEditorGUI::SetController(Controller *pController) // left corner, then 0, 0. This will affect the way the mouse is positioned // etc. -void AreaEditorGUI::SetPosOnScreen(int newPosX, int newPosY) -{ - m_pPicker->SetPosOnScreen(newPosX, newPosY); +void AreaEditorGUI::SetPosOnScreen(int newPosX, int newPosY) { + m_pPicker->SetPosOnScreen(newPosX, newPosY); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetActivatedPieSlice ////////////////////////////////////////////////////////////////////////////////////////// @@ -148,147 +139,139 @@ PieSlice::SliceType AreaEditorGUI::GetActivatedPieSlice() const { return m_PieMenu->GetPieCommand(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetCurrentArea ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets the currently selected Area of this Editor. Ownership IS NOT // transferred! -void AreaEditorGUI::SetCurrentArea(Scene::Area *pArea) -{ - if (!pArea) - return; - - m_pCurrentArea = pArea; - // Display the name of the newly seclected Area in the center of the screen - g_FrameMan.ClearScreenText(g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - g_FrameMan.SetScreenText(m_pCurrentArea->GetName(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer()), 0, 1500, true); - // Make the yellow outline edited box vanish - m_EditedBox.Reset(); - - // Jump the cursor pos to a reasonable center of the newly selected Area's coverage area - if (!m_pCurrentArea->m_BoxList.empty()) - { - // Average center of the all the boxes, weighted by their respective areas - m_CursorPos = m_pCurrentArea->GetCenterPoint(); - } +void AreaEditorGUI::SetCurrentArea(Scene::Area* pArea) { + if (!pArea) + return; + + m_pCurrentArea = pArea; + // Display the name of the newly seclected Area in the center of the screen + g_FrameMan.ClearScreenText(g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + g_FrameMan.SetScreenText(m_pCurrentArea->GetName(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer()), 0, 1500, true); + // Make the yellow outline edited box vanish + m_EditedBox.Reset(); + + // Jump the cursor pos to a reasonable center of the newly selected Area's coverage area + if (!m_pCurrentArea->m_BoxList.empty()) { + // Average center of the all the boxes, weighted by their respective areas + m_CursorPos = m_pCurrentArea->GetCenterPoint(); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePickerList ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the list that the GUI's Area picker has, from the current // scene state. -void AreaEditorGUI::UpdatePickerList(std::string selectAreaName) -{ - m_pPicker->UpdateAreasList(selectAreaName); +void AreaEditorGUI::UpdatePickerList(std::string selectAreaName) { + m_pPicker->UpdateAreasList(selectAreaName); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Update ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the state of this Menu each frame -void AreaEditorGUI::Update() -{ - // Update the user controller -// m_pController->Update(); - - if (!g_SceneMan.GetScene()) - return; - - // If no Area is selected yet, and there are Areas in the current scene, then select the first one automatically - if (!m_pCurrentArea && !g_SceneMan.GetScene()->m_AreaList.empty()) - m_pCurrentArea = &(g_SceneMan.GetScene()->m_AreaList.front()); - - m_EditMade = false; - m_pBoxToBlink = 0; - - //////////////////////////////////////////// - // Blinking logic -/* - if (m_BlinkMode == OBJECTBLINK) - { - m_pCostLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); - } - else if (m_BlinkMode == NOCRAFT) - { - bool blink = m_BlinkTimer.AlternateSim(250); - m_pCraftLabel->SetVisible(blink); - m_pCraftBox->SetVisible(blink); - } - - // Time out the blinker - if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastSimMS(1500)) - { - m_pCostLabel->SetVisible(true); - m_pCraftLabel->SetVisible(true); - m_pCraftBox->SetVisible(true); - m_BlinkMode = NOBLINK; - } -*/ - ///////////////////////////////////////////// - // Repeating input logic - - bool pressLeft = m_pController->IsState(PRESS_LEFT); - bool pressRight = m_pController->IsState(PRESS_RIGHT); - bool pressUp = m_pController->IsState(PRESS_UP); - bool pressDown = m_pController->IsState(PRESS_DOWN); - - // If no direciton is held down, then cancel the repeating - if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) - { - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - } - - // Check if any direction has been held for the starting amount of time to get into repeat mode - if (m_RepeatStartTimer.IsPastRealMS(200)) - { - // Check for the repeat interval - if (m_RepeatTimer.IsPastRealMS(30)) - { - if (m_pController->IsState(MOVE_RIGHT)) - pressRight = true; - else if (m_pController->IsState(MOVE_LEFT)) - pressLeft = true; - - if (m_pController->IsState(MOVE_UP)) - pressUp = true; - else if (m_pController->IsState(MOVE_DOWN)) - pressDown = true; - - m_RepeatTimer.Reset(); - } - } - - /////////////////////////////////////////////// - // Analog cursor input - - Vector analogInput; - if (m_pController->GetAnalogMove().MagnitudeIsGreaterThan(0.1F)) - analogInput = m_pController->GetAnalogMove(); -// else if (m_pController->GetAnalogAim().MagnitudeIsGreaterThan(0.1F)) -// analogInput = m_pController->GetAnalogAim(); - - ///////////////////////////////////////////// - // PIE MENU - - m_PieMenu->Update(); - - // Show the pie menu only when the secondary button is held down - if (m_pController->IsState(PRESS_SECONDARY) && m_EditorGUIMode != INACTIVE && m_EditorGUIMode != PICKINGAREA) { +void AreaEditorGUI::Update() { + // Update the user controller + // m_pController->Update(); + + if (!g_SceneMan.GetScene()) + return; + + // If no Area is selected yet, and there are Areas in the current scene, then select the first one automatically + if (!m_pCurrentArea && !g_SceneMan.GetScene()->m_AreaList.empty()) + m_pCurrentArea = &(g_SceneMan.GetScene()->m_AreaList.front()); + + m_EditMade = false; + m_pBoxToBlink = 0; + + //////////////////////////////////////////// + // Blinking logic + /* + if (m_BlinkMode == OBJECTBLINK) + { + m_pCostLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); + } + else if (m_BlinkMode == NOCRAFT) + { + bool blink = m_BlinkTimer.AlternateSim(250); + m_pCraftLabel->SetVisible(blink); + m_pCraftBox->SetVisible(blink); + } + + // Time out the blinker + if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastSimMS(1500)) + { + m_pCostLabel->SetVisible(true); + m_pCraftLabel->SetVisible(true); + m_pCraftBox->SetVisible(true); + m_BlinkMode = NOBLINK; + } + */ + ///////////////////////////////////////////// + // Repeating input logic + + bool pressLeft = m_pController->IsState(PRESS_LEFT); + bool pressRight = m_pController->IsState(PRESS_RIGHT); + bool pressUp = m_pController->IsState(PRESS_UP); + bool pressDown = m_pController->IsState(PRESS_DOWN); + + // If no direciton is held down, then cancel the repeating + if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) { + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + } + + // Check if any direction has been held for the starting amount of time to get into repeat mode + if (m_RepeatStartTimer.IsPastRealMS(200)) { + // Check for the repeat interval + if (m_RepeatTimer.IsPastRealMS(30)) { + if (m_pController->IsState(MOVE_RIGHT)) + pressRight = true; + else if (m_pController->IsState(MOVE_LEFT)) + pressLeft = true; + + if (m_pController->IsState(MOVE_UP)) + pressUp = true; + else if (m_pController->IsState(MOVE_DOWN)) + pressDown = true; + + m_RepeatTimer.Reset(); + } + } + + /////////////////////////////////////////////// + // Analog cursor input + + Vector analogInput; + if (m_pController->GetAnalogMove().MagnitudeIsGreaterThan(0.1F)) + analogInput = m_pController->GetAnalogMove(); + // else if (m_pController->GetAnalogAim().MagnitudeIsGreaterThan(0.1F)) + // analogInput = m_pController->GetAnalogAim(); + + ///////////////////////////////////////////// + // PIE MENU + + m_PieMenu->Update(); + + // Show the pie menu only when the secondary button is held down + if (m_pController->IsState(PRESS_SECONDARY) && m_EditorGUIMode != INACTIVE && m_EditorGUIMode != PICKINGAREA) { m_PieMenu->SetPos(m_GridSnapping ? g_SceneMan.SnapPosition(m_CursorPos) : m_CursorPos); m_PieMenu->SetEnabled(true); - } + } - if (!m_pController->IsState(PIE_MENU_ACTIVE) || m_EditorGUIMode == INACTIVE || m_EditorGUIMode == PICKINGAREA) { m_PieMenu->SetEnabled(false); } + if (!m_pController->IsState(PIE_MENU_ACTIVE) || m_EditorGUIMode == INACTIVE || m_EditorGUIMode == PICKINGAREA) { + m_PieMenu->SetEnabled(false); + } - if (m_PieMenu->GetPieCommand() != PieSlice::SliceType::NoType) { + if (m_PieMenu->GetPieCommand() != PieSlice::SliceType::NoType) { if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorPick) { m_EditorGUIMode = PICKINGAREA; } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorMove) { @@ -298,373 +281,332 @@ void AreaEditorGUI::Update() } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorDone) { m_EditorGUIMode = DONEEDITING; } - } - - ////////////////////////////////////////// - // Picker logic - - // Enable or disable the picker - m_pPicker->SetEnabled(m_EditorGUIMode == PICKINGAREA); - - // Update the picker GUI - m_pPicker->Update(); - - if (m_EditorGUIMode == PICKINGAREA && m_pPicker->AreaPicked()) - { - // Assign the pointer of the picked Area to be the currently selected one. - if (m_pPicker->AreaPicked()) - { - SetCurrentArea(m_pPicker->AreaPicked()); -// TODO: Make the view center on the newly picked Area, somehow using average of all its Box:es centers? - - // If done picking, revert to adding/movind Box mode for the newly selected Area - if (m_pPicker->DonePicking()) - { - m_EditorGUIMode = PREADDMOVEBOX; - } - } - } - - if (!m_pPicker->IsVisible()) - g_CameraMan.SetScreenOcclusion(Vector(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - if (m_EditorGUIMode != PICKINGAREA) - { - // Mousewheel is used as shortcut for getting next and prev items in the picker's Area list - if (m_pController->IsState(SCROLL_UP)) - { - // Assign the non-owned pointer of the next picked Area to be the currently selected one. - Scene::Area *pNewArea = m_pPicker->GetPrevArea(); - if (pNewArea) - SetCurrentArea(pNewArea); - } - else if (m_pController->IsState(SCROLL_DOWN)) - { - // Assign the non-owned pointer of the next picked Area to be the currently selected one. - Scene::Area *pNewArea = m_pPicker->GetNextArea(); - if (pNewArea) - SetCurrentArea(pNewArea); - } - } - - // Make sure we have a picked area if there are any areas at all! - if (!m_pCurrentArea && !g_SceneMan.GetScene()->m_AreaList.empty()) - m_pCurrentArea = &(g_SceneMan.GetScene()->m_AreaList.front()); - // If there are no Area:s, AreaEditor should detect it and force user to create a new one with a dialog -// else -// m_EditorGUIMode = PREADDMOVEBOX; - - ///////////////////////////////////// - // ADDING or MOVING BOX MODE - - if (m_pCurrentArea && m_EditorGUIMode == PREADDMOVEBOX && !m_PieMenu->IsEnabled()) - { - g_FrameMan.SetScreenText("Click and drag to ADD a new box to the Area - Drag existing ones to MOVE them", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - // Trap the mouse cursor - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - - // Move the cursor according to analog or mouse input - if (!analogInput.IsZero()) - { - m_CursorPos += analogInput * 8; - // Re-enable snapping only when the cursor is moved again - m_GridSnapping = true; - } - else if (!m_pController->GetMouseMovement().IsZero()) - { - m_CursorPos += m_pController->GetMouseMovement(); - // Re-enable snapping only when the cursor is moved again - m_GridSnapping = true; - } - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= SCENESNAPSIZE; - if (pressRight) - m_CursorPos.m_X += SCENESNAPSIZE; - if (pressDown) - m_CursorPos.m_Y += SCENESNAPSIZE; - if (pressLeft) - m_CursorPos.m_X -= SCENESNAPSIZE; - // Re-enable snapping only when the cursor is moved again - if (pressUp || pressRight || pressDown || pressLeft) - m_GridSnapping = true; - } - - // Detect whether the cursor is in the air, or if it's overlapping some terrain - Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); - m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; - - Box *pBox = 0; - - // Start the timer when the button is first pressed, and when the picker has deactivated - if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) - { - m_BlinkTimer.Reset(); - - // See if the start of the drag is inside an existing Box, and set the next mode (MOVE) accordingly - if (pBox = m_pCurrentArea->GetBoxInside(m_CursorPos)) - { - // Save a copy of the dragged box - m_EditedBox = *pBox; - // Set the offset between the Box being dragged and the cursor so the box doesn't jump - m_CursorOffset = m_CursorPos - m_EditedBox.GetCorner(); - // Remove the dragged box from the area, we are now in control of it - m_pCurrentArea->RemoveBoxInside(m_CursorPos); - // Switch the mode - m_EditorGUIMode = MOVINGBOX; - m_PreviousMode = PREADDMOVEBOX; - } - // Or outside all, and set the next mode to be NEW instead to start a new box drag to add to the current Area - else - { - // Place the start corner of the new box - m_EditedBox.SetCorner(m_CursorPos); - // Cursor offset is 0 becuase we're placing the corner with current cursor pos - m_CursorOffset.Reset(); - // Switch mode - m_EditorGUIMode = ADDINGBOX; - m_PreviousMode = PREADDMOVEBOX; - } - - g_GUISound.PlacementBlip()->Play(); - } - // Just hovering over things, show what would be moved if we started dragging - else - { - pBox = m_pCurrentArea->GetBoxInside(m_CursorPos); - if (pBox) - m_EditedBox = *pBox; - else - m_EditedBox.Reset(); - } - } - - ///////////////////////////////////////////////////////////// - // POINTING AT/DRAGGING MODES WITHOUT SNAPPING - - else if (m_pCurrentArea && (m_EditorGUIMode == MOVINGBOX || m_EditorGUIMode == ADDINGBOX || m_EditorGUIMode == DELETINGBOX) && !m_PieMenu->IsEnabled()) - { - // Trap the mouse cursor - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - - // Move the cursor according to analog or mouse input - if (!analogInput.IsZero()) - m_CursorPos += analogInput * 4; - else if (!m_pController->GetMouseMovement().IsZero()) - m_CursorPos += m_pController->GetMouseMovement() / 2; - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= 1; - if (pressRight) - m_CursorPos.m_X += 1; - if (pressDown) - m_CursorPos.m_Y += 1; - if (pressLeft) - m_CursorPos.m_X -= 1; - } - - ///////////////////////////////// - // MOVING BOX MODE - - if (m_EditorGUIMode == MOVINGBOX) - { - g_FrameMan.SetScreenText("Keep dragging the box to MOVE it", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - // While primary is kept held down, keep dragging the Box - if (m_pController->IsState(PRIMARY_ACTION)) - { -// TODO: really wrap? - Vector wrappedCorner = m_CursorPos - m_CursorOffset; - g_SceneMan.WrapPosition(wrappedCorner); - m_EditedBox.SetCorner(wrappedCorner); - } - // When released, we are done moving that box and go back to prev mode - else if (m_pController->IsState(RELEASE_PRIMARY)) - { - // Add it back to the Area - m_EditedBox.Unflip(); - m_pCurrentArea->AddBox(m_EditedBox); - // Make the yellow outline edited box vanish - m_EditedBox.Reset(); - m_EditMade = true; - m_EditorGUIMode = PREADDMOVEBOX; - m_PreviousMode = MOVINGBOX; - g_GUISound.PlacementThud()->Play(); - } - } - - ///////////////////////////////// - // ADDING BOX MODE - - if (m_EditorGUIMode == ADDINGBOX) - { - g_FrameMan.SetScreenText("Keep dragging the new box out - release and it is ADDED to the current Area", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - // While primary is kept held down, keep dragging out the otehr corner of the Box being defined - if (m_pController->IsState(PRIMARY_ACTION)) - { - Vector dimensions = g_SceneMan.ShortestDistance(m_EditedBox.GetCorner(), m_CursorPos).GetFloored(); - m_EditedBox.SetWidth(dimensions.m_X); - m_EditedBox.SetHeight(dimensions.m_Y); - } - // When released, we are done with that box and go back to prev mode - else if (m_pController->IsState(RELEASE_PRIMARY)) - { - m_EditedBox.Unflip(); - m_pCurrentArea->AddBox(m_EditedBox); - // Make the yellow outline edited box vanish - m_EditedBox.Reset(); - m_EditMade = true; - m_EditorGUIMode = PREADDMOVEBOX; - m_PreviousMode = ADDINGBOX; - g_GUISound.PlacementThud()->Play(); - } - } - - //////////////////////////// - // REMOVING BOX MODE - - else if (m_EditorGUIMode == DELETINGBOX) - { - g_FrameMan.SetScreenText("Click and hold to select a Box - release to DELETE it", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - m_EditedBox.Reset(); - - // When primary is held down, pick Box and show which one will be nuked if released - if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) - { - Box *pBoxToDelete = m_pCurrentArea->GetBoxInside(m_CursorPos); - // Indicate which box we're talking aobut to delete - if (pBoxToDelete) - m_EditedBox = *pBoxToDelete; - } - else if (m_pController->IsState(RELEASE_PRIMARY)) - { - Box removed = m_pCurrentArea->RemoveBoxInside(m_CursorPos); - // If we didnt' remove any box, play error sound - if (removed.IsEmpty()) - g_GUISound.UserErrorSound()->Play(); - else - m_EditMade = true; - } - } - } - else if (m_EditorGUIMode == DONEEDITING) - { -// if (!m_FullFeatured) -// g_FrameMan.SetScreenText("DONE editing, wait for all other players to finish too...", ScreenOfPlayer(m_pController->GetPlayer())); - } - - // Remove cursor offset if not applicable anymore - if (m_EditorGUIMode == PREADDMOVEBOX) - m_CursorOffset.Reset(); - - // Keep the cursor position within the world - bool cursorWrapped = g_SceneMan.ForceBounds(m_CursorPos); -// TODO: make setscrolltarget with 'sloppy' target - // Scroll to the cursor's scene position - g_CameraMan.SetScrollTarget(m_CursorPos, 0.3, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); -} + } + + ////////////////////////////////////////// + // Picker logic + + // Enable or disable the picker + m_pPicker->SetEnabled(m_EditorGUIMode == PICKINGAREA); + + // Update the picker GUI + m_pPicker->Update(); + + if (m_EditorGUIMode == PICKINGAREA && m_pPicker->AreaPicked()) { + // Assign the pointer of the picked Area to be the currently selected one. + if (m_pPicker->AreaPicked()) { + SetCurrentArea(m_pPicker->AreaPicked()); + // TODO: Make the view center on the newly picked Area, somehow using average of all its Box:es centers? + + // If done picking, revert to adding/movind Box mode for the newly selected Area + if (m_pPicker->DonePicking()) { + m_EditorGUIMode = PREADDMOVEBOX; + } + } + } + + if (!m_pPicker->IsVisible()) + g_CameraMan.SetScreenOcclusion(Vector(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + if (m_EditorGUIMode != PICKINGAREA) { + // Mousewheel is used as shortcut for getting next and prev items in the picker's Area list + if (m_pController->IsState(SCROLL_UP)) { + // Assign the non-owned pointer of the next picked Area to be the currently selected one. + Scene::Area* pNewArea = m_pPicker->GetPrevArea(); + if (pNewArea) + SetCurrentArea(pNewArea); + } else if (m_pController->IsState(SCROLL_DOWN)) { + // Assign the non-owned pointer of the next picked Area to be the currently selected one. + Scene::Area* pNewArea = m_pPicker->GetNextArea(); + if (pNewArea) + SetCurrentArea(pNewArea); + } + } + + // Make sure we have a picked area if there are any areas at all! + if (!m_pCurrentArea && !g_SceneMan.GetScene()->m_AreaList.empty()) + m_pCurrentArea = &(g_SceneMan.GetScene()->m_AreaList.front()); + // If there are no Area:s, AreaEditor should detect it and force user to create a new one with a dialog + // else + // m_EditorGUIMode = PREADDMOVEBOX; + + ///////////////////////////////////// + // ADDING or MOVING BOX MODE + + if (m_pCurrentArea && m_EditorGUIMode == PREADDMOVEBOX && !m_PieMenu->IsEnabled()) { + g_FrameMan.SetScreenText("Click and drag to ADD a new box to the Area - Drag existing ones to MOVE them", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + // Trap the mouse cursor + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + + // Move the cursor according to analog or mouse input + if (!analogInput.IsZero()) { + m_CursorPos += analogInput * 8; + // Re-enable snapping only when the cursor is moved again + m_GridSnapping = true; + } else if (!m_pController->GetMouseMovement().IsZero()) { + m_CursorPos += m_pController->GetMouseMovement(); + // Re-enable snapping only when the cursor is moved again + m_GridSnapping = true; + } + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= SCENESNAPSIZE; + if (pressRight) + m_CursorPos.m_X += SCENESNAPSIZE; + if (pressDown) + m_CursorPos.m_Y += SCENESNAPSIZE; + if (pressLeft) + m_CursorPos.m_X -= SCENESNAPSIZE; + // Re-enable snapping only when the cursor is moved again + if (pressUp || pressRight || pressDown || pressLeft) + m_GridSnapping = true; + } + // Detect whether the cursor is in the air, or if it's overlapping some terrain + Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); + m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; + + Box* pBox = 0; + + // Start the timer when the button is first pressed, and when the picker has deactivated + if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) { + m_BlinkTimer.Reset(); + + // See if the start of the drag is inside an existing Box, and set the next mode (MOVE) accordingly + if (pBox = m_pCurrentArea->GetBoxInside(m_CursorPos)) { + // Save a copy of the dragged box + m_EditedBox = *pBox; + // Set the offset between the Box being dragged and the cursor so the box doesn't jump + m_CursorOffset = m_CursorPos - m_EditedBox.GetCorner(); + // Remove the dragged box from the area, we are now in control of it + m_pCurrentArea->RemoveBoxInside(m_CursorPos); + // Switch the mode + m_EditorGUIMode = MOVINGBOX; + m_PreviousMode = PREADDMOVEBOX; + } + // Or outside all, and set the next mode to be NEW instead to start a new box drag to add to the current Area + else { + // Place the start corner of the new box + m_EditedBox.SetCorner(m_CursorPos); + // Cursor offset is 0 becuase we're placing the corner with current cursor pos + m_CursorOffset.Reset(); + // Switch mode + m_EditorGUIMode = ADDINGBOX; + m_PreviousMode = PREADDMOVEBOX; + } + + g_GUISound.PlacementBlip()->Play(); + } + // Just hovering over things, show what would be moved if we started dragging + else { + pBox = m_pCurrentArea->GetBoxInside(m_CursorPos); + if (pBox) + m_EditedBox = *pBox; + else + m_EditedBox.Reset(); + } + } + + ///////////////////////////////////////////////////////////// + // POINTING AT/DRAGGING MODES WITHOUT SNAPPING + + else if (m_pCurrentArea && (m_EditorGUIMode == MOVINGBOX || m_EditorGUIMode == ADDINGBOX || m_EditorGUIMode == DELETINGBOX) && !m_PieMenu->IsEnabled()) { + // Trap the mouse cursor + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + + // Move the cursor according to analog or mouse input + if (!analogInput.IsZero()) + m_CursorPos += analogInput * 4; + else if (!m_pController->GetMouseMovement().IsZero()) + m_CursorPos += m_pController->GetMouseMovement() / 2; + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= 1; + if (pressRight) + m_CursorPos.m_X += 1; + if (pressDown) + m_CursorPos.m_Y += 1; + if (pressLeft) + m_CursorPos.m_X -= 1; + } + + ///////////////////////////////// + // MOVING BOX MODE + + if (m_EditorGUIMode == MOVINGBOX) { + g_FrameMan.SetScreenText("Keep dragging the box to MOVE it", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + // While primary is kept held down, keep dragging the Box + if (m_pController->IsState(PRIMARY_ACTION)) { + // TODO: really wrap? + Vector wrappedCorner = m_CursorPos - m_CursorOffset; + g_SceneMan.WrapPosition(wrappedCorner); + m_EditedBox.SetCorner(wrappedCorner); + } + // When released, we are done moving that box and go back to prev mode + else if (m_pController->IsState(RELEASE_PRIMARY)) { + // Add it back to the Area + m_EditedBox.Unflip(); + m_pCurrentArea->AddBox(m_EditedBox); + // Make the yellow outline edited box vanish + m_EditedBox.Reset(); + m_EditMade = true; + m_EditorGUIMode = PREADDMOVEBOX; + m_PreviousMode = MOVINGBOX; + g_GUISound.PlacementThud()->Play(); + } + } + + ///////////////////////////////// + // ADDING BOX MODE + + if (m_EditorGUIMode == ADDINGBOX) { + g_FrameMan.SetScreenText("Keep dragging the new box out - release and it is ADDED to the current Area", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + // While primary is kept held down, keep dragging out the otehr corner of the Box being defined + if (m_pController->IsState(PRIMARY_ACTION)) { + Vector dimensions = g_SceneMan.ShortestDistance(m_EditedBox.GetCorner(), m_CursorPos).GetFloored(); + m_EditedBox.SetWidth(dimensions.m_X); + m_EditedBox.SetHeight(dimensions.m_Y); + } + // When released, we are done with that box and go back to prev mode + else if (m_pController->IsState(RELEASE_PRIMARY)) { + m_EditedBox.Unflip(); + m_pCurrentArea->AddBox(m_EditedBox); + // Make the yellow outline edited box vanish + m_EditedBox.Reset(); + m_EditMade = true; + m_EditorGUIMode = PREADDMOVEBOX; + m_PreviousMode = ADDINGBOX; + g_GUISound.PlacementThud()->Play(); + } + } + + //////////////////////////// + // REMOVING BOX MODE + + else if (m_EditorGUIMode == DELETINGBOX) { + g_FrameMan.SetScreenText("Click and hold to select a Box - release to DELETE it", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + m_EditedBox.Reset(); + + // When primary is held down, pick Box and show which one will be nuked if released + if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) { + Box* pBoxToDelete = m_pCurrentArea->GetBoxInside(m_CursorPos); + // Indicate which box we're talking aobut to delete + if (pBoxToDelete) + m_EditedBox = *pBoxToDelete; + } else if (m_pController->IsState(RELEASE_PRIMARY)) { + Box removed = m_pCurrentArea->RemoveBoxInside(m_CursorPos); + // If we didnt' remove any box, play error sound + if (removed.IsEmpty()) + g_GUISound.UserErrorSound()->Play(); + else + m_EditMade = true; + } + } + } else if (m_EditorGUIMode == DONEEDITING) { + // if (!m_FullFeatured) + // g_FrameMan.SetScreenText("DONE editing, wait for all other players to finish too...", ScreenOfPlayer(m_pController->GetPlayer())); + } + + // Remove cursor offset if not applicable anymore + if (m_EditorGUIMode == PREADDMOVEBOX) + m_CursorOffset.Reset(); + + // Keep the cursor position within the world + bool cursorWrapped = g_SceneMan.ForceBounds(m_CursorPos); + // TODO: make setscrolltarget with 'sloppy' target + // Scroll to the cursor's scene position + g_CameraMan.SetScrollTarget(m_CursorPos, 0.3, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); +} ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws the menu -void AreaEditorGUI::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) const -{ - // Done or can't, so don't draw the UI - if (!m_pCurrentArea || m_EditorGUIMode == DONEEDITING) - return; - - // List to capture scene-wrapped boxes - std::list wrappedBoxes; - - // First draw all the objects placed in the scene by the Scene Editor - const std::list *pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); - if (m_FullFeatured) - { - // Draw all already placed Objects, and the currently held one in the order it is about to be placed in the scene - int i = 0; - for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr, ++i) - { - (*itr)->Draw(pTargetBitmap, targetPos); - // Draw basic HUD if an actor - Actor *pActor = dynamic_cast(*itr); - if (pActor) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - } - - // Draw the Box:es defined for the currently selected Area - Vector adjCorner; - const std::vector *pBoxList = &(m_pCurrentArea->m_BoxList); - if (m_FullFeatured) - { - // Set the drawin mode to be transparent and use the -// g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - g_FrameMan.SetTransTableFromPreset(TransparencyPreset::MoreTrans); - drawing_mode(DRAW_MODE_TRANS, 0, 0, 0); - - // Draw all already placed Box:es, and the currently edited one - for (std::vector::const_iterator bItr = pBoxList->begin(); bItr != pBoxList->end(); ++bItr) - { - // Handle wrapped boxes properly - wrappedBoxes.clear(); - g_SceneMan.WrapBox(*bItr, wrappedBoxes); - - // Iterate through the wrapped boxes - will only be one if there's no wrapping - for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - // Draw the rectangle of each Box, adjusted for the offet of the target bitmap in the scene - adjCorner = (*wItr).GetCorner() - targetPos; - rectfill(pTargetBitmap, adjCorner.m_X, adjCorner.m_Y, adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y + (*wItr).GetHeight(), g_RedColor); - } - } - } - - // Draw the currently edited box outline in glowing yellow - drawing_mode(DRAW_MODE_SOLID, 0, 0, 0); - if (!m_EditedBox.IsEmpty()) - { - // Handle wrapped boxes properly - wrappedBoxes.clear(); - g_SceneMan.WrapBox(m_EditedBox, wrappedBoxes); - - // Iterate through the wrapped boxes - will only be one if there's no wrapping - for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) - { - adjCorner = (*wItr).GetCorner() - targetPos; - // Special 'X' drawing when deleting - if (m_EditorGUIMode == DELETINGBOX) - { - line(pTargetBitmap, adjCorner.m_X, adjCorner.m_Y, adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y + (*wItr).GetHeight(), g_YellowGlowColor); - line(pTargetBitmap, adjCorner.m_X, adjCorner.m_Y + (*wItr).GetHeight(), adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y, g_YellowGlowColor); - } - else - rect(pTargetBitmap, adjCorner.m_X, adjCorner.m_Y, adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y + (*wItr).GetHeight(), g_YellowGlowColor); - } - } - - // Set drawing mode back to solid - drawing_mode(DRAW_MODE_SOLID, 0, 0, 0); - - // Draw picking Area crosshairs - Vector center = m_CursorPos - targetPos; - putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); - - m_pPicker->Draw(pTargetBitmap); - - // Draw the pie menu +void AreaEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { + // Done or can't, so don't draw the UI + if (!m_pCurrentArea || m_EditorGUIMode == DONEEDITING) + return; + + // List to capture scene-wrapped boxes + std::list wrappedBoxes; + + // First draw all the objects placed in the scene by the Scene Editor + const std::list* pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); + if (m_FullFeatured) { + // Draw all already placed Objects, and the currently held one in the order it is about to be placed in the scene + int i = 0; + for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr, ++i) { + (*itr)->Draw(pTargetBitmap, targetPos); + // Draw basic HUD if an actor + Actor* pActor = dynamic_cast(*itr); + if (pActor) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + } + + // Draw the Box:es defined for the currently selected Area + Vector adjCorner; + const std::vector* pBoxList = &(m_pCurrentArea->m_BoxList); + if (m_FullFeatured) { + // Set the drawin mode to be transparent and use the + // g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + g_FrameMan.SetTransTableFromPreset(TransparencyPreset::MoreTrans); + drawing_mode(DRAW_MODE_TRANS, 0, 0, 0); + + // Draw all already placed Box:es, and the currently edited one + for (std::vector::const_iterator bItr = pBoxList->begin(); bItr != pBoxList->end(); ++bItr) { + // Handle wrapped boxes properly + wrappedBoxes.clear(); + g_SceneMan.WrapBox(*bItr, wrappedBoxes); + + // Iterate through the wrapped boxes - will only be one if there's no wrapping + for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + // Draw the rectangle of each Box, adjusted for the offet of the target bitmap in the scene + adjCorner = (*wItr).GetCorner() - targetPos; + rectfill(pTargetBitmap, adjCorner.m_X, adjCorner.m_Y, adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y + (*wItr).GetHeight(), g_RedColor); + } + } + } + + // Draw the currently edited box outline in glowing yellow + drawing_mode(DRAW_MODE_SOLID, 0, 0, 0); + if (!m_EditedBox.IsEmpty()) { + // Handle wrapped boxes properly + wrappedBoxes.clear(); + g_SceneMan.WrapBox(m_EditedBox, wrappedBoxes); + + // Iterate through the wrapped boxes - will only be one if there's no wrapping + for (std::list::iterator wItr = wrappedBoxes.begin(); wItr != wrappedBoxes.end(); ++wItr) { + adjCorner = (*wItr).GetCorner() - targetPos; + // Special 'X' drawing when deleting + if (m_EditorGUIMode == DELETINGBOX) { + line(pTargetBitmap, adjCorner.m_X, adjCorner.m_Y, adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y + (*wItr).GetHeight(), g_YellowGlowColor); + line(pTargetBitmap, adjCorner.m_X, adjCorner.m_Y + (*wItr).GetHeight(), adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y, g_YellowGlowColor); + } else + rect(pTargetBitmap, adjCorner.m_X, adjCorner.m_Y, adjCorner.m_X + (*wItr).GetWidth(), adjCorner.m_Y + (*wItr).GetHeight(), g_YellowGlowColor); + } + } + + // Set drawing mode back to solid + drawing_mode(DRAW_MODE_SOLID, 0, 0, 0); + + // Draw picking Area crosshairs + Vector center = m_CursorPos - targetPos; + putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); + + m_pPicker->Draw(pTargetBitmap); + + // Draw the pie menu m_PieMenu->Draw(pTargetBitmap, targetPos); } \ No newline at end of file diff --git a/Source/Menus/AreaEditorGUI.h b/Source/Menus/AreaEditorGUI.h index 93113ac03c..ff4d673e7a 100644 --- a/Source/Menus/AreaEditorGUI.h +++ b/Source/Menus/AreaEditorGUI.h @@ -10,11 +10,10 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files -//#include "FrameMan.h" +// #include "FrameMan.h" #include "Scene.h" #include "Timer.h" #include "Vector.h" @@ -24,301 +23,271 @@ struct BITMAP; - -namespace RTE -{ - -class AreaPickerGUI; -class PieMenu; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: AreaEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A full menu system that represents the scene editing GUI for Cortex Command -// Parent(s): None. -// Class history: 7/08/2007 AreaEditorGUI Created. - -class AreaEditorGUI { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - // Different modes of this editor - enum EditorGUIMode - { - INACTIVE = 0, - PICKINGAREA, - PREADDMOVEBOX, - ADDINGBOX, - MOVINGBOX, - DELETINGBOX, - DONEEDITING, - EDITORGUIMODECOUNT - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: AreaEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a AreaEditorGUI Area in system -// memory. Create() should be called before using the Area. -// Arguments: None. - - AreaEditorGUI() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~AreaEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a AreaEditorGUI Area before deletion -// from system memory. -// Arguments: None. - - ~AreaEditorGUI() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AreaEditorGUI Area ready for use. -// Arguments: A poitner to a Controller which will control this Menu. Ownership is -// NOT TRANSFERRED! -// Whether the editor should have all the features enabled, like load/save -// and undo capabilities. -// Which module space that this eidtor will be able to pick Areas from. -// -1 means all modules. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(Controller *pController, bool fullFeatured = false, int whichModuleSpace = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire AreaEditorGUI, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AreaEditorGUI Area. -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! -// Arguments: The new controller for this menu. Ownership is NOT transferred -// Return value: None. - - void SetController(Controller *pController); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. -// Arguments: The new screen position of this entire GUI. - - void SetPosOnScreen(int newPosX, int newPosY); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCursorPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the absolute scene coordinates of the cursor of this Editor. -// Arguments: The new cursor position in absolute scene units. -// Return value: None. - - void SetCursorPos(const Vector &newCursorPos) { m_CursorPos = newCursorPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActivatedPieSlice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets any Pie menu slice command activated last update. -// Arguments: None. -// Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. - - PieSlice::SliceType GetActivatedPieSlice() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCurrentArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the currently selected Area of this Editor. Ownership IS NOT -// transferred! -// Arguments: The new area for this to work with, if any. OWNERSHIP IS NOT TRANSFERRED! -// Return value: None. - - void SetCurrentArea(Scene::Area *pArea); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the currently held Area in the cursor of this Editor. Ownership -// IS NOT transferred! -// Arguments: None. -// Return value: The currently held Area, if any. OWNERSHIP IS NOT TRANSFERRED! - - Scene::Area * GetCurrentArea() { return m_pCurrentArea; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EditMade -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether an edit on the scene was made in the last Update. -// Arguments: None. -// Return value: Whether any edit was made. - - bool EditMade() const { return m_EditMade; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePickerList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the list that the GUI's Area picker has, from the current -// scene state. -// Arguments: The name of the Area to leave selected after the list is updated. -// Return value: None. - - void UpdatePickerList(std::string selectAreaName = ""); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the editor -// Arguments: The bitmap to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - - enum BlinkMode - { - NOBLINK = 0, - OBJECTBLINKON, - OBJECTBLINKOFF, - BLINKMODECOUNT - }; - - // Controller which conrols this menu. Not owned - Controller *m_pController; - // Full featured or the in-game version - bool m_FullFeatured; - // Whether an editor was made to the Scene in the last Update - bool m_EditMade; - // The current mode of the whole GUI. See EditorGUIMode enum. - EditorGUIMode m_EditorGUIMode; - // The previous mode of the whole GUI, to go back to when the current mode is done in some cases - EditorGUIMode m_PreviousMode; - // Notification blink timer - Timer m_BlinkTimer; - // What we're blinking - int m_BlinkMode; - // Measures the time to when to start repeating inputs when they're held down - Timer m_RepeatStartTimer; - // Measures the interval between input repeats - Timer m_RepeatTimer; - - std::unique_ptr m_PieMenu; //!< The PieMenu for this AreaEditorGUI. - // The Area picker - AreaPickerGUI *m_pPicker; - // Grid snapping enabled - bool m_GridSnapping; - // Current cursor position, in absolute scene coordinates - Vector m_CursorPos; - // The offset from the currently dragged Box's corner position to the cursor, if any - Vector m_CursorOffset; - // Cursor position in free air, or over something - bool m_CursorInAir; - // Currently selected Area. NOT OWNED BY THIS - Scene::Area *m_pCurrentArea; - // Whether to draw the currently held Area - bool m_DrawCurrentArea; - // The Box currently being added/moved - Box m_EditedBox; - // Currently placed scene Area to make blink when drawing it. NOT OWNED. - const Box *m_pBoxToBlink; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AreaEditorGUI, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - AreaEditorGUI(const AreaEditorGUI &reference) = delete; - AreaEditorGUI & operator=(const AreaEditorGUI &rhs) = delete; - -}; +namespace RTE { + + class AreaPickerGUI; + class PieMenu; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: AreaEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A full menu system that represents the scene editing GUI for Cortex Command + // Parent(s): None. + // Class history: 7/08/2007 AreaEditorGUI Created. + + class AreaEditorGUI { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Different modes of this editor + enum EditorGUIMode { + INACTIVE = 0, + PICKINGAREA, + PREADDMOVEBOX, + ADDINGBOX, + MOVINGBOX, + DELETINGBOX, + DONEEDITING, + EDITORGUIMODECOUNT + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: AreaEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a AreaEditorGUI Area in system + // memory. Create() should be called before using the Area. + // Arguments: None. + + AreaEditorGUI() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~AreaEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a AreaEditorGUI Area before deletion + // from system memory. + // Arguments: None. + + ~AreaEditorGUI() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the AreaEditorGUI Area ready for use. + // Arguments: A poitner to a Controller which will control this Menu. Ownership is + // NOT TRANSFERRED! + // Whether the editor should have all the features enabled, like load/save + // and undo capabilities. + // Which module space that this eidtor will be able to pick Areas from. + // -1 means all modules. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(Controller* pController, bool fullFeatured = false, int whichModuleSpace = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire AreaEditorGUI, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the AreaEditorGUI Area. + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetController + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the controller used by this. The ownership of the controller is + // NOT transferred! + // Arguments: The new controller for this menu. Ownership is NOT transferred + // Return value: None. + + void SetController(Controller* pController); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPosOnScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets where on the screen that this GUI is being drawn to. If upper + // left corner, then 0, 0. This will affect the way the mouse is positioned + // etc. + // Arguments: The new screen position of this entire GUI. + + void SetPosOnScreen(int newPosX, int newPosY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCursorPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the absolute scene coordinates of the cursor of this Editor. + // Arguments: The new cursor position in absolute scene units. + // Return value: None. + + void SetCursorPos(const Vector& newCursorPos) { m_CursorPos = newCursorPos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActivatedPieSlice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets any Pie menu slice command activated last update. + // Arguments: None. + // Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. + + PieSlice::SliceType GetActivatedPieSlice() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCurrentArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the currently selected Area of this Editor. Ownership IS NOT + // transferred! + // Arguments: The new area for this to work with, if any. OWNERSHIP IS NOT TRANSFERRED! + // Return value: None. + + void SetCurrentArea(Scene::Area* pArea); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the currently held Area in the cursor of this Editor. Ownership + // IS NOT transferred! + // Arguments: None. + // Return value: The currently held Area, if any. OWNERSHIP IS NOT TRANSFERRED! + + Scene::Area* GetCurrentArea() { return m_pCurrentArea; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EditMade + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether an edit on the scene was made in the last Update. + // Arguments: None. + // Return value: Whether any edit was made. + + bool EditMade() const { return m_EditMade; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePickerList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the list that the GUI's Area picker has, from the current + // scene state. + // Arguments: The name of the Area to leave selected after the list is updated. + // Return value: None. + + void UpdatePickerList(std::string selectAreaName = ""); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this Menu each frame + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the editor + // Arguments: The bitmap to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + enum BlinkMode { + NOBLINK = 0, + OBJECTBLINKON, + OBJECTBLINKOFF, + BLINKMODECOUNT + }; + + // Controller which conrols this menu. Not owned + Controller* m_pController; + // Full featured or the in-game version + bool m_FullFeatured; + // Whether an editor was made to the Scene in the last Update + bool m_EditMade; + // The current mode of the whole GUI. See EditorGUIMode enum. + EditorGUIMode m_EditorGUIMode; + // The previous mode of the whole GUI, to go back to when the current mode is done in some cases + EditorGUIMode m_PreviousMode; + // Notification blink timer + Timer m_BlinkTimer; + // What we're blinking + int m_BlinkMode; + // Measures the time to when to start repeating inputs when they're held down + Timer m_RepeatStartTimer; + // Measures the interval between input repeats + Timer m_RepeatTimer; + + std::unique_ptr m_PieMenu; //!< The PieMenu for this AreaEditorGUI. + // The Area picker + AreaPickerGUI* m_pPicker; + // Grid snapping enabled + bool m_GridSnapping; + // Current cursor position, in absolute scene coordinates + Vector m_CursorPos; + // The offset from the currently dragged Box's corner position to the cursor, if any + Vector m_CursorOffset; + // Cursor position in free air, or over something + bool m_CursorInAir; + // Currently selected Area. NOT OWNED BY THIS + Scene::Area* m_pCurrentArea; + // Whether to draw the currently held Area + bool m_DrawCurrentArea; + // The Box currently being added/moved + Box m_EditedBox; + // Currently placed scene Area to make blink when drawing it. NOT OWNED. + const Box* m_pBoxToBlink; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this AreaEditorGUI, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + AreaEditorGUI(const AreaEditorGUI& reference) = delete; + AreaEditorGUI& operator=(const AreaEditorGUI& rhs) = delete; + }; } // namespace RTE -#endif // File \ No newline at end of file +#endif // File \ No newline at end of file diff --git a/Source/Menus/AreaPickerGUI.cpp b/Source/Menus/AreaPickerGUI.cpp index 7ac5ff3305..fdd843bb33 100644 --- a/Source/Menus/AreaPickerGUI.cpp +++ b/Source/Menus/AreaPickerGUI.cpp @@ -7,7 +7,6 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -37,7 +36,7 @@ using namespace RTE; -BITMAP *RTE::AreaPickerGUI::s_pCursor = 0; +BITMAP* RTE::AreaPickerGUI::s_pCursor = 0; ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear @@ -45,160 +44,145 @@ BITMAP *RTE::AreaPickerGUI::s_pCursor = 0; // Description: Clears all the member variables of this AreaPickerGUI, effectively // resetting the members of this abstraction level only. -void AreaPickerGUI::Clear() -{ - m_pController = 0; - m_pGUIScreen = 0; - m_pGUIInput = 0; - m_pGUIController = 0; - m_PickerEnabled = DISABLED; - m_MenuSpeed = 0.3; - m_ShowType.clear(); - m_SelectedGroupIndex = 0; - m_SelectedAreaIndex = 0; - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - m_pParentBox = 0; - m_pAreasList = 0; - m_pDeleteAreaButton = 0; - m_pPickedArea = 0; - m_CursorPos.Reset(); +void AreaPickerGUI::Clear() { + m_pController = 0; + m_pGUIScreen = 0; + m_pGUIInput = 0; + m_pGUIController = 0; + m_PickerEnabled = DISABLED; + m_MenuSpeed = 0.3; + m_ShowType.clear(); + m_SelectedGroupIndex = 0; + m_SelectedAreaIndex = 0; + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + m_pParentBox = 0; + m_pAreasList = 0; + m_pDeleteAreaButton = 0; + m_pPickedArea = 0; + m_CursorPos.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the AreaPickerGUI area ready for use. -int AreaPickerGUI::Create(Controller *pController, std::string onlyOfType) -{ - RTEAssert(pController, "No controller sent to AreaPickerGUI on creation!"); - m_pController = pController; - - if (!m_pGUIScreen) - m_pGUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer8()); - if (!m_pGUIInput) - m_pGUIInput = new GUIInputWrapper(pController->GetPlayer()); - if (!m_pGUIController) - m_pGUIController = new GUIControlManager(); +int AreaPickerGUI::Create(Controller* pController, std::string onlyOfType) { + RTEAssert(pController, "No controller sent to AreaPickerGUI on creation!"); + m_pController = pController; + + if (!m_pGUIScreen) + m_pGUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer8()); + if (!m_pGUIInput) + m_pGUIInput = new GUIInputWrapper(pController->GetPlayer()); + if (!m_pGUIController) + m_pGUIController = new GUIControlManager(); if (!m_pGUIController->Create(m_pGUIScreen, m_pGUIInput, "Base.rte/GUIs/Skins", "DefaultSkin.ini")) { RTEAbort("Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/DefaultSkin.ini"); } - m_pGUIController->Load("Base.rte/GUIs/AreaPickerGUI.ini"); - m_pGUIController->EnableMouse(pController->IsMouseControlled()); - - if (!s_pCursor) - { - ContentFile cursorFile("Base.rte/GUIs/Skins/Cursor.png"); - s_pCursor = cursorFile.GetAsBitmap(); - } - - // Stretch the invisible root box to fill the screen - dynamic_cast(m_pGUIController->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); - - // Make sure we have convenient points to teh containing GUI colleciton boxes that we will manipulate the positions of - if (!m_pParentBox) - { - m_pParentBox = dynamic_cast(m_pGUIController->GetControl("PickerGUIBox")); - - // Set the background image of the parent collection box -// ContentFile backgroundFile("Base.rte/GUIs/BuyMenuBackground.png"); -// m_pParentBox->SetDrawImage(new AllegroBitmap(backgroundFile.GetAsBitmap())); -// m_pParentBox->SetDrawBackground(true); -// m_pParentBox->SetDrawType(GUICollectionBox::Image); - m_pParentBox->SetDrawType(GUICollectionBox::Color); - } - m_pParentBox->SetPositionAbs(g_FrameMan.GetPlayerScreenWidth(), 0); - m_pParentBox->SetEnabled(false); - m_pParentBox->SetVisible(false); - - m_pAreasList = dynamic_cast(m_pGUIController->GetControl("AreasLB")); - m_pDeleteAreaButton = dynamic_cast(m_pGUIController->GetControl("DeleteAreaButton")); - - // If we're not split screen horizontally, then stretch out the layout for all the relevant controls - if (!g_FrameMan.GetHSplit()) - { - int stretchAmount = g_WindowMan.GetResY() / 2; - m_pParentBox->SetSize(m_pParentBox->GetWidth(), m_pParentBox->GetHeight() + stretchAmount); - m_pAreasList->SetSize(m_pAreasList->GetWidth(), m_pAreasList->GetHeight() + stretchAmount); - m_pDeleteAreaButton->SetPositionAbs(m_pDeleteAreaButton->GetXPos(), m_pDeleteAreaButton->GetYPos() + stretchAmount); - } - - m_pAreasList->SetAlternateDrawMode(false); - m_pAreasList->SetMultiSelect(false); - - // Populate the Areas list with the current Scene's Area:s - UpdateAreasList(); - - // Reset repeat timers - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - - return 0; -} + m_pGUIController->Load("Base.rte/GUIs/AreaPickerGUI.ini"); + m_pGUIController->EnableMouse(pController->IsMouseControlled()); + + if (!s_pCursor) { + ContentFile cursorFile("Base.rte/GUIs/Skins/Cursor.png"); + s_pCursor = cursorFile.GetAsBitmap(); + } + + // Stretch the invisible root box to fill the screen + dynamic_cast(m_pGUIController->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + + // Make sure we have convenient points to teh containing GUI colleciton boxes that we will manipulate the positions of + if (!m_pParentBox) { + m_pParentBox = dynamic_cast(m_pGUIController->GetControl("PickerGUIBox")); + // Set the background image of the parent collection box + // ContentFile backgroundFile("Base.rte/GUIs/BuyMenuBackground.png"); + // m_pParentBox->SetDrawImage(new AllegroBitmap(backgroundFile.GetAsBitmap())); + // m_pParentBox->SetDrawBackground(true); + // m_pParentBox->SetDrawType(GUICollectionBox::Image); + m_pParentBox->SetDrawType(GUICollectionBox::Color); + } + m_pParentBox->SetPositionAbs(g_FrameMan.GetPlayerScreenWidth(), 0); + m_pParentBox->SetEnabled(false); + m_pParentBox->SetVisible(false); + + m_pAreasList = dynamic_cast(m_pGUIController->GetControl("AreasLB")); + m_pDeleteAreaButton = dynamic_cast(m_pGUIController->GetControl("DeleteAreaButton")); + + // If we're not split screen horizontally, then stretch out the layout for all the relevant controls + if (!g_FrameMan.GetHSplit()) { + int stretchAmount = g_WindowMan.GetResY() / 2; + m_pParentBox->SetSize(m_pParentBox->GetWidth(), m_pParentBox->GetHeight() + stretchAmount); + m_pAreasList->SetSize(m_pAreasList->GetWidth(), m_pAreasList->GetHeight() + stretchAmount); + m_pDeleteAreaButton->SetPositionAbs(m_pDeleteAreaButton->GetXPos(), m_pDeleteAreaButton->GetYPos() + stretchAmount); + } + + m_pAreasList->SetAlternateDrawMode(false); + m_pAreasList->SetMultiSelect(false); + + // Populate the Areas list with the current Scene's Area:s + UpdateAreasList(); + + // Reset repeat timers + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + + return 0; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the AreaPickerGUI area. -void AreaPickerGUI::Destroy() -{ - delete m_pGUIController; - delete m_pGUIInput; - delete m_pGUIScreen; +void AreaPickerGUI::Destroy() { + delete m_pGUIController; + delete m_pGUIInput; + delete m_pGUIScreen; - Clear(); + Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetEnabled ////////////////////////////////////////////////////////////////////////////////////////// // Description: Enables or disables the menu. This will animate it in and out of view. -void AreaPickerGUI::SetEnabled(bool enable) -{ - if (enable && m_PickerEnabled != ENABLED && m_PickerEnabled != ENABLING) - { - // If we're not split screen horizontally, then stretch out the layout for all the relevant controls - int stretchAmount = g_FrameMan.GetPlayerScreenHeight() - m_pParentBox->GetHeight(); - if (stretchAmount != 0) - { - m_pParentBox->SetSize(m_pParentBox->GetWidth(), m_pParentBox->GetHeight() + stretchAmount); - m_pAreasList->SetSize(m_pAreasList->GetWidth(), m_pAreasList->GetHeight() + stretchAmount); - } - - m_PickerEnabled = ENABLING; - // Reset repeat timers - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - // Set the mouse cursor free - g_UInputMan.TrapMousePos(false, m_pController->GetPlayer()); - // Move the mouse cursor to the middle of the player's screen - int mouseOffX, mouseOffY; - m_pGUIInput->GetMouseOffset(mouseOffX, mouseOffY); - Vector mousePos(-mouseOffX + (g_FrameMan.GetPlayerScreenWidth() / 2), -mouseOffY + (g_FrameMan.GetPlayerScreenHeight() / 2)); - g_UInputMan.SetMousePos(mousePos, m_pController->GetPlayer()); - g_GUISound.EnterMenuSound()->Play(); - - // Repopulate with the current Scene's list of Area:s - UpdateAreasList(); - } - else if (!enable && m_PickerEnabled != DISABLED && m_PickerEnabled != DISABLING) - { - m_PickerEnabled = DISABLING; - // Trap the mouse cursor again - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - // Only play switching away sound -// if (!m_pPickedArea) - g_GUISound.ExitMenuSound()->Play(); - } +void AreaPickerGUI::SetEnabled(bool enable) { + if (enable && m_PickerEnabled != ENABLED && m_PickerEnabled != ENABLING) { + // If we're not split screen horizontally, then stretch out the layout for all the relevant controls + int stretchAmount = g_FrameMan.GetPlayerScreenHeight() - m_pParentBox->GetHeight(); + if (stretchAmount != 0) { + m_pParentBox->SetSize(m_pParentBox->GetWidth(), m_pParentBox->GetHeight() + stretchAmount); + m_pAreasList->SetSize(m_pAreasList->GetWidth(), m_pAreasList->GetHeight() + stretchAmount); + } + + m_PickerEnabled = ENABLING; + // Reset repeat timers + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + // Set the mouse cursor free + g_UInputMan.TrapMousePos(false, m_pController->GetPlayer()); + // Move the mouse cursor to the middle of the player's screen + int mouseOffX, mouseOffY; + m_pGUIInput->GetMouseOffset(mouseOffX, mouseOffY); + Vector mousePos(-mouseOffX + (g_FrameMan.GetPlayerScreenWidth() / 2), -mouseOffY + (g_FrameMan.GetPlayerScreenHeight() / 2)); + g_UInputMan.SetMousePos(mousePos, m_pController->GetPlayer()); + g_GUISound.EnterMenuSound()->Play(); + + // Repopulate with the current Scene's list of Area:s + UpdateAreasList(); + } else if (!enable && m_PickerEnabled != DISABLED && m_PickerEnabled != DISABLING) { + m_PickerEnabled = DISABLING; + // Trap the mouse cursor again + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + // Only play switching away sound + // if (!m_pPickedArea) + g_GUISound.ExitMenuSound()->Play(); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetPosOnScreen ////////////////////////////////////////////////////////////////////////////////////////// @@ -206,368 +190,317 @@ void AreaPickerGUI::SetEnabled(bool enable) // left corner, then 0, 0. This will affect the way the mouse is positioned // etc. -void AreaPickerGUI::SetPosOnScreen(int newPosX, int newPosY) -{ - m_pGUIController->SetPosOnScreen(newPosX, newPosY); +void AreaPickerGUI::SetPosOnScreen(int newPosX, int newPosY) { + m_pGUIController->SetPosOnScreen(newPosX, newPosY); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetNextArea ////////////////////////////////////////////////////////////////////////////////////////// // Description: Gets the next area in the areas list, even if the picker is disabled. -Scene::Area * AreaPickerGUI::GetNextArea() -{ - m_SelectedAreaIndex++; - // Loop around - if (m_SelectedAreaIndex >= m_pAreasList->GetItemList()->size()) - m_SelectedAreaIndex = 0; - - m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); - // Report the newly selected item as being 'picked', but don't close the picker - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - if (pItem) - { - g_GUISound.SelectionChangeSound()->Play(); - return g_SceneMan.GetScene()->GetArea(pItem->m_Name); - } - return 0; +Scene::Area* AreaPickerGUI::GetNextArea() { + m_SelectedAreaIndex++; + // Loop around + if (m_SelectedAreaIndex >= m_pAreasList->GetItemList()->size()) + m_SelectedAreaIndex = 0; + + m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); + // Report the newly selected item as being 'picked', but don't close the picker + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + if (pItem) { + g_GUISound.SelectionChangeSound()->Play(); + return g_SceneMan.GetScene()->GetArea(pItem->m_Name); + } + return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetPrevArea ////////////////////////////////////////////////////////////////////////////////////////// // Description: Gets the prev area in the areas list, even if the picker is disabled. -Scene::Area * AreaPickerGUI::GetPrevArea() -{ - m_SelectedAreaIndex--; - // Loop around - if (m_SelectedAreaIndex < 0) - m_SelectedAreaIndex = m_pAreasList->GetItemList()->size() - 1; - - m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); - // Report the newly selected item as being 'picked', but don't close the picker - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - if (pItem) - { - g_GUISound.SelectionChangeSound()->Play(); - return g_SceneMan.GetScene()->GetArea(pItem->m_Name); - } - return 0; +Scene::Area* AreaPickerGUI::GetPrevArea() { + m_SelectedAreaIndex--; + // Loop around + if (m_SelectedAreaIndex < 0) + m_SelectedAreaIndex = m_pAreasList->GetItemList()->size() - 1; + + m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); + // Report the newly selected item as being 'picked', but don't close the picker + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + if (pItem) { + g_GUISound.SelectionChangeSound()->Play(); + return g_SceneMan.GetScene()->GetArea(pItem->m_Name); + } + return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateAreasList ////////////////////////////////////////////////////////////////////////////////////////// // Description: Adds all areas of a specific type already defined in PresetMan // to the current Areas list -void AreaPickerGUI::UpdateAreasList(std::string selectAreaName) -{ - m_pAreasList->ClearList(); - - if (g_SceneMan.GetScene() && !g_SceneMan.GetScene()->m_AreaList.empty()) - { - Scene *pScene = g_SceneMan.GetScene(); - int indexToSelect = 0; - // Add all the current Scene's Area:s to the list! - for (std::list::iterator itr = pScene->m_AreaList.begin(); itr != pScene->m_AreaList.end(); ++itr) - { - m_pAreasList->AddItem((*itr).GetName()); - // If an Area's name matches the one we're supposed to leave selected after update, then save teh index - if ((*itr).GetName() == selectAreaName) - m_SelectedAreaIndex = indexToSelect; - indexToSelect++; - } - - m_pAreasList->ScrollToTop(); - // No actually select the Area with the matching name/index - m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); - // Set the picked area to be the one now selected at the top - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - if (pItem) - m_pPickedArea = pScene->GetArea(pItem->m_Name); - } +void AreaPickerGUI::UpdateAreasList(std::string selectAreaName) { + m_pAreasList->ClearList(); + + if (g_SceneMan.GetScene() && !g_SceneMan.GetScene()->m_AreaList.empty()) { + Scene* pScene = g_SceneMan.GetScene(); + int indexToSelect = 0; + // Add all the current Scene's Area:s to the list! + for (std::list::iterator itr = pScene->m_AreaList.begin(); itr != pScene->m_AreaList.end(); ++itr) { + m_pAreasList->AddItem((*itr).GetName()); + // If an Area's name matches the one we're supposed to leave selected after update, then save teh index + if ((*itr).GetName() == selectAreaName) + m_SelectedAreaIndex = indexToSelect; + indexToSelect++; + } + + m_pAreasList->ScrollToTop(); + // No actually select the Area with the matching name/index + m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); + // Set the picked area to be the one now selected at the top + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + if (pItem) + m_pPickedArea = pScene->GetArea(pItem->m_Name); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Update ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the state of this Menu each frame -void AreaPickerGUI::Update() -{ - // Enable mouse input if the controller allows it - m_pGUIController->EnableMouse(m_pController->IsMouseControlled()); - - // Reset the picked selector - m_pPickedArea = 0; - - //////////////////////////////////////////////////////////////////////// - // Animate the menu into and out of view if enabled or disabled - - if (m_PickerEnabled == ENABLING) - { - m_pParentBox->SetEnabled(true); - m_pParentBox->SetVisible(true); - - Vector position, occlusion; - - float enabledPos = g_FrameMan.GetPlayerScreenWidth() - m_pParentBox->GetWidth(); - - float toGo = std::floor((enabledPos - (float)m_pParentBox->GetXPos()) * m_MenuSpeed); - position.m_X = m_pParentBox->GetXPos() + toGo; - occlusion.m_X = m_pParentBox->GetXPos() - g_FrameMan.GetPlayerScreenWidth(); - - m_pParentBox->SetPositionAbs(position.m_X, position.m_Y); - g_CameraMan.SetScreenOcclusion(occlusion, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - if (m_pParentBox->GetXPos() <= enabledPos) - m_PickerEnabled = ENABLED; - } - // Animate the menu out of view - else if (m_PickerEnabled == DISABLING) - { - float disabledPos = g_FrameMan.GetPlayerScreenWidth(); - - float toGo = std::ceil((disabledPos - (float)m_pParentBox->GetXPos()) * m_MenuSpeed); - m_pParentBox->SetPositionAbs(m_pParentBox->GetXPos() + toGo, 0); - g_CameraMan.SetScreenOcclusion(Vector(m_pParentBox->GetXPos() - g_FrameMan.GetPlayerScreenWidth(), 0), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - if (m_pParentBox->GetXPos() >= g_FrameMan.GetPlayerScreenWidth()) - { - m_pParentBox->SetEnabled(false); - m_pParentBox->SetVisible(false); - m_PickerEnabled = DISABLED; - } - } - else if (m_PickerEnabled == ENABLED) - { - m_pParentBox->SetEnabled(true); - m_pParentBox->SetVisible(true); - } - else if (m_PickerEnabled == DISABLED) - { - m_pParentBox->SetEnabled(false); - m_pParentBox->SetVisible(false); - } - - // Quit now if we aren't enabled - if (m_PickerEnabled != ENABLED && m_PickerEnabled != ENABLING) - return; - - // Update the user controller -// m_pController->Update(); - - - ///////////////////////////////////////////////////// - // Mouse cursor logic - - int mouseX, mouseY; - m_pGUIInput->GetMousePosition(&mouseX, &mouseY); - m_CursorPos.SetXY(mouseX, mouseY); - - ///////////////////////////////////////////// - // Repeating input logic - - bool pressLeft = m_pController->IsState(PRESS_LEFT); - bool pressRight = m_pController->IsState(PRESS_RIGHT); - bool pressUp = m_pController->IsState(PRESS_UP) || m_pController->IsState(SCROLL_UP); - bool pressDown = m_pController->IsState(PRESS_DOWN) || m_pController->IsState(SCROLL_DOWN); - - // If no direciton is held down, then cancel the repeating - if (!(/*m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || */m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) - { - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - } - - // Check if any direction has been held for the starting amount of time to get into repeat mode - if (m_RepeatStartTimer.IsPastRealMS(200)) - { - // Check for the repeat interval - if (m_RepeatTimer.IsPastRealMS(75)) - { -/* L-R Not needed for picker - if (m_pController->IsState(MOVE_RIGHT)) - pressRight = true; - else if (m_pController->IsState(MOVE_LEFT)) - pressLeft = true; -*/ - if (m_pController->IsState(MOVE_UP)) - pressUp = true; - else if (m_pController->IsState(MOVE_DOWN)) - pressDown = true; - - m_RepeatTimer.Reset(); - } - } - - - ///////////////////////////////////////// - // AREAS LIST - - int listSize = m_pAreasList->GetItemList()->size(); - if (pressDown) - { - m_SelectedAreaIndex++; - // Loop around - if (m_SelectedAreaIndex >= listSize) - m_SelectedAreaIndex = 0; - - m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); - // Report the newly selected item as being 'picked', but don't close the picker - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - if (pItem) - m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name); - g_GUISound.SelectionChangeSound()->Play(); - } - else if (pressUp) - { - m_SelectedAreaIndex--; - // Loop around - if (m_SelectedAreaIndex < 0) - m_SelectedAreaIndex = listSize - 1; - - m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); - // Report the newly selected item as being 'picked', but don't close the picker - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - if (pItem) - m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name); - - g_GUISound.SelectionChangeSound()->Play(); - } - - // Fire button picks the area and deactivates the picker GUI - if (m_PickerEnabled == ENABLED && m_pController->IsState(PRESS_FACEBUTTON)) - { - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - if (pItem) - { - // User has made final selection, so close the Picker - if (m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name)) - { - g_GUISound.AreaPickedSound()->Play(); - SetEnabled(false); - } - } - } - - // Right click, or pie menu press close the menu - if (m_pController->IsState(PRESS_SECONDARY)) - { - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - if (pItem) - { - // User has made final selection, so close the Picker - if (m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name)) - { - g_GUISound.AreaPickedSound()->Play(); - SetEnabled(false); - } - } - } - - ////////////////////////////////////////// +void AreaPickerGUI::Update() { + // Enable mouse input if the controller allows it + m_pGUIController->EnableMouse(m_pController->IsMouseControlled()); + + // Reset the picked selector + m_pPickedArea = 0; + + //////////////////////////////////////////////////////////////////////// + // Animate the menu into and out of view if enabled or disabled + + if (m_PickerEnabled == ENABLING) { + m_pParentBox->SetEnabled(true); + m_pParentBox->SetVisible(true); + + Vector position, occlusion; + + float enabledPos = g_FrameMan.GetPlayerScreenWidth() - m_pParentBox->GetWidth(); + + float toGo = std::floor((enabledPos - (float)m_pParentBox->GetXPos()) * m_MenuSpeed); + position.m_X = m_pParentBox->GetXPos() + toGo; + occlusion.m_X = m_pParentBox->GetXPos() - g_FrameMan.GetPlayerScreenWidth(); + + m_pParentBox->SetPositionAbs(position.m_X, position.m_Y); + g_CameraMan.SetScreenOcclusion(occlusion, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + if (m_pParentBox->GetXPos() <= enabledPos) + m_PickerEnabled = ENABLED; + } + // Animate the menu out of view + else if (m_PickerEnabled == DISABLING) { + float disabledPos = g_FrameMan.GetPlayerScreenWidth(); + + float toGo = std::ceil((disabledPos - (float)m_pParentBox->GetXPos()) * m_MenuSpeed); + m_pParentBox->SetPositionAbs(m_pParentBox->GetXPos() + toGo, 0); + g_CameraMan.SetScreenOcclusion(Vector(m_pParentBox->GetXPos() - g_FrameMan.GetPlayerScreenWidth(), 0), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + if (m_pParentBox->GetXPos() >= g_FrameMan.GetPlayerScreenWidth()) { + m_pParentBox->SetEnabled(false); + m_pParentBox->SetVisible(false); + m_PickerEnabled = DISABLED; + } + } else if (m_PickerEnabled == ENABLED) { + m_pParentBox->SetEnabled(true); + m_pParentBox->SetVisible(true); + } else if (m_PickerEnabled == DISABLED) { + m_pParentBox->SetEnabled(false); + m_pParentBox->SetVisible(false); + } + + // Quit now if we aren't enabled + if (m_PickerEnabled != ENABLED && m_PickerEnabled != ENABLING) + return; + + // Update the user controller + // m_pController->Update(); + + ///////////////////////////////////////////////////// + // Mouse cursor logic + + int mouseX, mouseY; + m_pGUIInput->GetMousePosition(&mouseX, &mouseY); + m_CursorPos.SetXY(mouseX, mouseY); + + ///////////////////////////////////////////// + // Repeating input logic + + bool pressLeft = m_pController->IsState(PRESS_LEFT); + bool pressRight = m_pController->IsState(PRESS_RIGHT); + bool pressUp = m_pController->IsState(PRESS_UP) || m_pController->IsState(SCROLL_UP); + bool pressDown = m_pController->IsState(PRESS_DOWN) || m_pController->IsState(SCROLL_DOWN); + + // If no direciton is held down, then cancel the repeating + if (!(/*m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || */ m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) { + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + } + + // Check if any direction has been held for the starting amount of time to get into repeat mode + if (m_RepeatStartTimer.IsPastRealMS(200)) { + // Check for the repeat interval + if (m_RepeatTimer.IsPastRealMS(75)) { + /* L-R Not needed for picker + if (m_pController->IsState(MOVE_RIGHT)) + pressRight = true; + else if (m_pController->IsState(MOVE_LEFT)) + pressLeft = true; + */ + if (m_pController->IsState(MOVE_UP)) + pressUp = true; + else if (m_pController->IsState(MOVE_DOWN)) + pressDown = true; + + m_RepeatTimer.Reset(); + } + } + + ///////////////////////////////////////// + // AREAS LIST + + int listSize = m_pAreasList->GetItemList()->size(); + if (pressDown) { + m_SelectedAreaIndex++; + // Loop around + if (m_SelectedAreaIndex >= listSize) + m_SelectedAreaIndex = 0; + + m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); + // Report the newly selected item as being 'picked', but don't close the picker + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + if (pItem) + m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name); + g_GUISound.SelectionChangeSound()->Play(); + } else if (pressUp) { + m_SelectedAreaIndex--; + // Loop around + if (m_SelectedAreaIndex < 0) + m_SelectedAreaIndex = listSize - 1; + + m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); + // Report the newly selected item as being 'picked', but don't close the picker + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + if (pItem) + m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name); + + g_GUISound.SelectionChangeSound()->Play(); + } + + // Fire button picks the area and deactivates the picker GUI + if (m_PickerEnabled == ENABLED && m_pController->IsState(PRESS_FACEBUTTON)) { + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + if (pItem) { + // User has made final selection, so close the Picker + if (m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name)) { + g_GUISound.AreaPickedSound()->Play(); + SetEnabled(false); + } + } + } + + // Right click, or pie menu press close the menu + if (m_pController->IsState(PRESS_SECONDARY)) { + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + if (pItem) { + // User has made final selection, so close the Picker + if (m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name)) { + g_GUISound.AreaPickedSound()->Play(); + SetEnabled(false); + } + } + } + + ////////////////////////////////////////// // Update the ControlManager m_pGUIController->Update(); - - //////////////////////////////////////////////////////// - // Handle events for mouse input on the controls + //////////////////////////////////////////////////////// + // Handle events for mouse input on the controls GUIEvent anEvent; - while(m_pGUIController->GetEvent(&anEvent)) - { - // If we're not supposed to have mouse control, then ignore these messages - if (!m_pController->IsMouseControlled()) - break; - - if (anEvent.GetType() == GUIEvent::Command) - { - // Delete area button - if(anEvent.GetControl() == m_pDeleteAreaButton) - { - UpdateAreasList(); - m_pDeleteAreaButton->SetFocus(); - - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - // Only delete if there's more than one Area left - if (pItem && g_SceneMan.GetScene()->m_AreaList.size() > 1) - { - // Pick the next area, then remove the one specified - m_pPickedArea = GetNextArea(); - g_SceneMan.GetScene()->RemoveArea(pItem->m_Name); - // Update the list so it shows the one removed - UpdateAreasList(m_pPickedArea->GetName()); - g_GUISound.AreaPickedSound()->Play(); - } - else - g_GUISound.UserErrorSound()->Play(); + while (m_pGUIController->GetEvent(&anEvent)) { + // If we're not supposed to have mouse control, then ignore these messages + if (!m_pController->IsMouseControlled()) + break; + + if (anEvent.GetType() == GUIEvent::Command) { + // Delete area button + if (anEvent.GetControl() == m_pDeleteAreaButton) { + UpdateAreasList(); + m_pDeleteAreaButton->SetFocus(); + + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + // Only delete if there's more than one Area left + if (pItem && g_SceneMan.GetScene()->m_AreaList.size() > 1) { + // Pick the next area, then remove the one specified + m_pPickedArea = GetNextArea(); + g_SceneMan.GetScene()->RemoveArea(pItem->m_Name); + // Update the list so it shows the one removed + UpdateAreasList(m_pPickedArea->GetName()); + g_GUISound.AreaPickedSound()->Play(); + } else + g_GUISound.UserErrorSound()->Play(); + } + } else if (anEvent.GetType() == GUIEvent::Notification) { + /////////////////////////////////////////////// + // Clicks on the Areas List + + if (anEvent.GetControl() == m_pAreasList) { + if (anEvent.GetMsg() == GUIListBox::MouseDown && (anEvent.GetData() & GUIListBox::MOUSE_LEFT)) { + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + if (pItem) { + m_SelectedAreaIndex = m_pAreasList->GetSelectedIndex(); + // User has made final selection, so close the Picker + if (m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name)) { + g_GUISound.AreaPickedSound()->Play(); + SetEnabled(false); + } + } + // Undo the click deselection if nothing was selected + else + m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); + } } - } - else if (anEvent.GetType() == GUIEvent::Notification) - { - /////////////////////////////////////////////// - // Clicks on the Areas List - - if (anEvent.GetControl() == m_pAreasList) - { - if(anEvent.GetMsg() == GUIListBox::MouseDown && (anEvent.GetData() & GUIListBox::MOUSE_LEFT)) - { - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - if (pItem) - { - m_SelectedAreaIndex = m_pAreasList->GetSelectedIndex(); - // User has made final selection, so close the Picker - if (m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name)) - { - g_GUISound.AreaPickedSound()->Play(); - SetEnabled(false); - } - } - // Undo the click deselection if nothing was selected - else - m_pAreasList->SetSelectedIndex(m_SelectedAreaIndex); + } + + // If clicked outside the picker, then close the picker GUI + if (anEvent.GetMsg() == GUIListBox::Click && m_PickerEnabled == ENABLED && m_CursorPos.m_X < m_pParentBox->GetXPos()) { + GUIListPanel::Item* pItem = m_pAreasList->GetSelected(); + if (pItem) { + // User has made final selection, so close the Picker + if (m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name)) { + g_GUISound.AreaPickedSound()->Play(); + SetEnabled(false); } - } - } - - // If clicked outside the picker, then close the picker GUI - if (anEvent.GetMsg() == GUIListBox::Click && m_PickerEnabled == ENABLED && m_CursorPos.m_X < m_pParentBox->GetXPos()) - { - GUIListPanel::Item *pItem = m_pAreasList->GetSelected(); - if (pItem) - { - // User has made final selection, so close the Picker - if (m_pPickedArea = g_SceneMan.GetScene()->GetArea(pItem->m_Name)) - { - g_GUISound.AreaPickedSound()->Play(); - SetEnabled(false); - } - } - } - } + } + } + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws the menu -void AreaPickerGUI::Draw(BITMAP *drawBitmap) const -{ - AllegroScreen drawScreen(drawBitmap); - m_pGUIController->Draw(&drawScreen); +void AreaPickerGUI::Draw(BITMAP* drawBitmap) const { + AllegroScreen drawScreen(drawBitmap); + m_pGUIController->Draw(&drawScreen); - // Draw the cursor on top of everything - if (IsEnabled() && m_pController->IsMouseControlled()) - draw_sprite(drawBitmap, s_pCursor, m_CursorPos.GetFloorIntX(), m_CursorPos.GetFloorIntY()); + // Draw the cursor on top of everything + if (IsEnabled() && m_pController->IsMouseControlled()) + draw_sprite(drawBitmap, s_pCursor, m_CursorPos.GetFloorIntX(), m_CursorPos.GetFloorIntY()); } \ No newline at end of file diff --git a/Source/Menus/AreaPickerGUI.h b/Source/Menus/AreaPickerGUI.h index 98bdb462ad..a3a2fbae00 100644 --- a/Source/Menus/AreaPickerGUI.h +++ b/Source/Menus/AreaPickerGUI.h @@ -10,304 +10,276 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files -//#include "FrameMan.h" +// #include "FrameMan.h" #include "Timer.h" #include "Controller.h" #include "Scene.h" struct BITMAP; -namespace RTE -{ - -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUILabel; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: AreaPickerGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A GUI for picking isntance areas in Cortex Command -// Parent(s): None. -// Class history: 7/16/2007 AreaPickerGUI Created. - -class AreaPickerGUI { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: AreaPickerGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a AreaPickerGUI area in system -// memory. Create() should be called before using the area. -// Arguments: None. - - AreaPickerGUI() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~AreaPickerGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a AreaPickerGUI area before deletion -// from system memory. -// Arguments: None. - - ~AreaPickerGUI() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AreaPickerGUI area ready for use. -// Arguments: A poitner to a Controller which will control this Menu. Ownership is -// NOT TRANSFERRED! -// Which lowest common denominator type to be showing. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(Controller *pController, std::string onlyOfType = "All"); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire AreaPickerGUI, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AreaPickerGUI area. -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! -// Arguments: The new controller for this menu. Ownership is NOT transferred -// Return value: None. - - void SetController(Controller *pController) { m_pController = pController; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Enables or disables the menu. This will animate it in and out of view. -// Arguments: Whether to enable or disable the menu. -// Return value: None. - - void SetEnabled(bool enable = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the menu is enabled or not. -// Arguments: None. -// Return value: None. - - bool IsEnabled() const { return m_PickerEnabled == ENABLED || m_PickerEnabled == ENABLING; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the menu is at all visible or not. -// Arguments: None. -// Return value: None. - - bool IsVisible() const { return m_PickerEnabled != DISABLED; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. -// Arguments: The new screen position of this entire GUI. - - void SetPosOnScreen(int newPosX, int newPosY); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AreaPicked -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether and which Area has been picked by the player. This -// may happen even though the player isn't done with the picker. (ie -// a different area is picked each time the user selects something else -// in the areas list). -// Arguments: None. -// Return value: Whether an area has been picked bt the player. 0 if not. Ownership -// is NOT transferred! - - Scene::Area * AreaPicked() { return m_pPickedArea; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DonePicking -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the user has finished using the picker, and the final -// picked Area is returned. -// Arguments: None. -// Return value: Whether an area has been positively and finally picked bt the player. -// 0 if not. Ownership is NOT transferred! - - Scene::Area * DonePicking() { return !IsEnabled() && m_pPickedArea ? m_pPickedArea : 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next area in the areas list, even if the picker is disabled. -// Arguments: None. -// Return value: The next area in the picker list, looping around if necessary. -// 0 if no area can be selected. OWNERSHIP IS NOT TRANSFERRED! - - Scene::Area * GetNextArea(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPrevArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the prev area in the areas list, even if the picker is disabled. -// Arguments: None. -// Return value: The prev area in the picker list, looping around if necessary. -// 0 if no area can be selected. OWNERSHIP IS NOT TRANSFERRED! - - Scene::Area * GetPrevArea(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateAreasList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds all areas of the currently selected group to the Areas list. -// Arguments: The name of the Area to leave selected after the list is updated. -// Return value: None. - - void UpdateAreasList(std::string selectAreaName = ""); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu -// Arguments: The bitmap to draw on. -// Return value: None. - - void Draw(BITMAP *drawBitmap) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - enum PickerEnabled - { - ENABLING = 0, - ENABLED, - DISABLING, - DISABLED - }; - - // Controller which conrols this menu. Not owned - Controller *m_pController; - // GUI Screen for use by the in-game GUI - GUIScreen *m_pGUIScreen; - // Input controller - GUIInput *m_pGUIInput; - // The control manager which holds all the controls - GUIControlManager *m_pGUIController; - // Visibility state of the area picker - int m_PickerEnabled; - // Speed at which the menus appear and disappear - float m_MenuSpeed; - // Only show areas of this type. "" or "All" will show areas of all types - std::string m_ShowType; - // Which Group in the groups list box we have selected - int m_SelectedGroupIndex; - // Which Oroup in the Areas list box we have selected - int m_SelectedAreaIndex; - // Measures the time to when to start repeating inputs when they're held down - Timer m_RepeatStartTimer; - // Measures the interval between input repeats - Timer m_RepeatTimer; - - // Collection box of the puicker GUI - GUICollectionBox *m_pParentBox; - // The Listbox which lists all the groups - GUIListBox *m_pGroupsList; - // The Listbox which lists all the areas in the currently selected group - GUIListBox *m_pAreasList; - // The Button for deleting the currently selected Area - GUIButton *m_pDeleteAreaButton; - // Currently picked area. This is 0 until the user actually picks somehting, not just has the cursor over it - // Not owned by this. - Scene::Area *m_pPickedArea; - // The cursor image shared by all pickers - static BITMAP *s_pCursor; - // Screen position of the cursor - Vector m_CursorPos; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AreaPickerGUI, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - AreaPickerGUI(const AreaPickerGUI &reference) = delete; - AreaPickerGUI & operator=(const AreaPickerGUI &rhs) = delete; - -}; +namespace RTE { + + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUILabel; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: AreaPickerGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A GUI for picking isntance areas in Cortex Command + // Parent(s): None. + // Class history: 7/16/2007 AreaPickerGUI Created. + + class AreaPickerGUI { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: AreaPickerGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a AreaPickerGUI area in system + // memory. Create() should be called before using the area. + // Arguments: None. + + AreaPickerGUI() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~AreaPickerGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a AreaPickerGUI area before deletion + // from system memory. + // Arguments: None. + + ~AreaPickerGUI() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the AreaPickerGUI area ready for use. + // Arguments: A poitner to a Controller which will control this Menu. Ownership is + // NOT TRANSFERRED! + // Which lowest common denominator type to be showing. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(Controller* pController, std::string onlyOfType = "All"); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire AreaPickerGUI, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the AreaPickerGUI area. + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetController + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the controller used by this. The ownership of the controller is + // NOT transferred! + // Arguments: The new controller for this menu. Ownership is NOT transferred + // Return value: None. + + void SetController(Controller* pController) { m_pController = pController; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Enables or disables the menu. This will animate it in and out of view. + // Arguments: Whether to enable or disable the menu. + // Return value: None. + + void SetEnabled(bool enable = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the menu is enabled or not. + // Arguments: None. + // Return value: None. + + bool IsEnabled() const { return m_PickerEnabled == ENABLED || m_PickerEnabled == ENABLING; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the menu is at all visible or not. + // Arguments: None. + // Return value: None. + + bool IsVisible() const { return m_PickerEnabled != DISABLED; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPosOnScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets where on the screen that this GUI is being drawn to. If upper + // left corner, then 0, 0. This will affect the way the mouse is positioned + // etc. + // Arguments: The new screen position of this entire GUI. + + void SetPosOnScreen(int newPosX, int newPosY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AreaPicked + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether and which Area has been picked by the player. This + // may happen even though the player isn't done with the picker. (ie + // a different area is picked each time the user selects something else + // in the areas list). + // Arguments: None. + // Return value: Whether an area has been picked bt the player. 0 if not. Ownership + // is NOT transferred! + + Scene::Area* AreaPicked() { return m_pPickedArea; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DonePicking + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the user has finished using the picker, and the final + // picked Area is returned. + // Arguments: None. + // Return value: Whether an area has been positively and finally picked bt the player. + // 0 if not. Ownership is NOT transferred! + + Scene::Area* DonePicking() { return !IsEnabled() && m_pPickedArea ? m_pPickedArea : 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetNextArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the next area in the areas list, even if the picker is disabled. + // Arguments: None. + // Return value: The next area in the picker list, looping around if necessary. + // 0 if no area can be selected. OWNERSHIP IS NOT TRANSFERRED! + + Scene::Area* GetNextArea(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPrevArea + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the prev area in the areas list, even if the picker is disabled. + // Arguments: None. + // Return value: The prev area in the picker list, looping around if necessary. + // 0 if no area can be selected. OWNERSHIP IS NOT TRANSFERRED! + + Scene::Area* GetPrevArea(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateAreasList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds all areas of the currently selected group to the Areas list. + // Arguments: The name of the Area to leave selected after the list is updated. + // Return value: None. + + void UpdateAreasList(std::string selectAreaName = ""); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this Menu each frame + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the menu + // Arguments: The bitmap to draw on. + // Return value: None. + + void Draw(BITMAP* drawBitmap) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + enum PickerEnabled { + ENABLING = 0, + ENABLED, + DISABLING, + DISABLED + }; + + // Controller which conrols this menu. Not owned + Controller* m_pController; + // GUI Screen for use by the in-game GUI + GUIScreen* m_pGUIScreen; + // Input controller + GUIInput* m_pGUIInput; + // The control manager which holds all the controls + GUIControlManager* m_pGUIController; + // Visibility state of the area picker + int m_PickerEnabled; + // Speed at which the menus appear and disappear + float m_MenuSpeed; + // Only show areas of this type. "" or "All" will show areas of all types + std::string m_ShowType; + // Which Group in the groups list box we have selected + int m_SelectedGroupIndex; + // Which Oroup in the Areas list box we have selected + int m_SelectedAreaIndex; + // Measures the time to when to start repeating inputs when they're held down + Timer m_RepeatStartTimer; + // Measures the interval between input repeats + Timer m_RepeatTimer; + + // Collection box of the puicker GUI + GUICollectionBox* m_pParentBox; + // The Listbox which lists all the groups + GUIListBox* m_pGroupsList; + // The Listbox which lists all the areas in the currently selected group + GUIListBox* m_pAreasList; + // The Button for deleting the currently selected Area + GUIButton* m_pDeleteAreaButton; + // Currently picked area. This is 0 until the user actually picks somehting, not just has the cursor over it + // Not owned by this. + Scene::Area* m_pPickedArea; + // The cursor image shared by all pickers + static BITMAP* s_pCursor; + // Screen position of the cursor + Vector m_CursorPos; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this AreaPickerGUI, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + AreaPickerGUI(const AreaPickerGUI& reference) = delete; + AreaPickerGUI& operator=(const AreaPickerGUI& rhs) = delete; + }; } // namespace RTE -#endif // File \ No newline at end of file +#endif // File \ No newline at end of file diff --git a/Source/Menus/AssemblyEditorGUI.cpp b/Source/Menus/AssemblyEditorGUI.cpp index 16dc10b3dc..45dc73ba92 100644 --- a/Source/Menus/AssemblyEditorGUI.cpp +++ b/Source/Menus/AssemblyEditorGUI.cpp @@ -7,7 +7,6 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -42,8 +41,8 @@ using namespace RTE; #define BLUEPRINTREVEALRATE 150 #define BLUEPRINTREVEALPAUSE 1500 -BITMAP *AssemblyEditorGUI::s_pValidPathDot = 0; -BITMAP *AssemblyEditorGUI::s_pInvalidPathDot = 0; +BITMAP* AssemblyEditorGUI::s_pValidPathDot = 0; +BITMAP* AssemblyEditorGUI::s_pInvalidPathDot = 0; ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear @@ -51,140 +50,133 @@ BITMAP *AssemblyEditorGUI::s_pInvalidPathDot = 0; // Description: Clears all the member variables of this AssemblyEditorGUI, effectively // resetting the members of this abstraction level only. -void AssemblyEditorGUI::Clear() -{ - m_pController = 0; - m_FeatureSet = ONLOADEDIT; - m_EditMade = false; - m_EditorGUIMode = PICKINGOBJECT; - m_PreviousMode = ADDINGOBJECT; - m_ModeChanged = true; - m_BlinkTimer.Reset(); - m_BlinkMode = NOBLINK; - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - m_RevealTimer.Reset(); - m_RevealIndex = 0; +void AssemblyEditorGUI::Clear() { + m_pController = 0; + m_FeatureSet = ONLOADEDIT; + m_EditMade = false; + m_EditorGUIMode = PICKINGOBJECT; + m_PreviousMode = ADDINGOBJECT; + m_ModeChanged = true; + m_BlinkTimer.Reset(); + m_BlinkMode = NOBLINK; + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + m_RevealTimer.Reset(); + m_RevealIndex = 0; m_PieMenu = nullptr; - m_pPicker = 0; - m_NativeTechModule = 0; - m_ForeignCostMult = 4.0; - m_GridSnapping = true; - m_CursorPos.Reset(); - m_CursorOffset.Reset(); - m_CursorInAir = true; - m_FacingLeft = false; - m_PlaceTeam = Activity::TeamOne; - m_pCurrentObject = 0; - m_ObjectListOrder = -1; - m_DrawCurrentObject = true; - m_pObjectToBlink = 0; - m_BrainSkyPath.clear(); - m_BrainSkyPathCost = 0; + m_pPicker = 0; + m_NativeTechModule = 0; + m_ForeignCostMult = 4.0; + m_GridSnapping = true; + m_CursorPos.Reset(); + m_CursorOffset.Reset(); + m_CursorInAir = true; + m_FacingLeft = false; + m_PlaceTeam = Activity::TeamOne; + m_pCurrentObject = 0; + m_ObjectListOrder = -1; + m_DrawCurrentObject = true; + m_pObjectToBlink = 0; + m_BrainSkyPath.clear(); + m_BrainSkyPathCost = 0; m_RequireClearPathToOrbit = true; m_pCurrentScheme = 0; m_CurrentAssemblyName.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the AssemblyEditorGUI object ready for use. -int AssemblyEditorGUI::Create(Controller *pController, FeatureSets featureSet, int whichModuleSpace, int nativeTechModule, float foreignCostMult) -{ - RTEAssert(pController, "No controller sent to AssemblyEditorGUI on creation!"); - m_pController = pController; +int AssemblyEditorGUI::Create(Controller* pController, FeatureSets featureSet, int whichModuleSpace, int nativeTechModule, float foreignCostMult) { + RTEAssert(pController, "No controller sent to AssemblyEditorGUI on creation!"); + m_pController = pController; - m_FeatureSet = featureSet; + m_FeatureSet = featureSet; - if (m_PieMenu) { m_PieMenu = nullptr; } - m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", "Assembly Editor Pie Menu")->Clone())); + if (m_PieMenu) { + m_PieMenu = nullptr; + } + m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", "Assembly Editor Pie Menu")->Clone())); m_PieMenu->SetMenuController(pController); - // Update the brain path - UpdateBrainPath(); - - // Allocate and (re)create the Editor GUIs - if (!m_pPicker) - m_pPicker = new ObjectPickerGUI(); - else - m_pPicker->Reset(); - m_pPicker->Create(pController, whichModuleSpace); - - m_NativeTechModule = nativeTechModule; - m_ForeignCostMult = foreignCostMult; - // Also apply these to the picker - m_pPicker->SetNativeTechModule(m_NativeTechModule); - m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); - - // Cursor init - m_CursorPos = g_SceneMan.GetSceneDim() / 2; - - // Set initial focus, category list, and label settings - m_EditorGUIMode = PICKINGOBJECT; - m_ModeChanged = true; - m_pCurrentObject = 0; - - // Reset repeat timers - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - m_RevealTimer.Reset(); - m_RevealTimer.SetRealTimeLimitMS(100); - - //Check if we need to check for a clear path to orbit + // Update the brain path + UpdateBrainPath(); + + // Allocate and (re)create the Editor GUIs + if (!m_pPicker) + m_pPicker = new ObjectPickerGUI(); + else + m_pPicker->Reset(); + m_pPicker->Create(pController, whichModuleSpace); + + m_NativeTechModule = nativeTechModule; + m_ForeignCostMult = foreignCostMult; + // Also apply these to the picker + m_pPicker->SetNativeTechModule(m_NativeTechModule); + m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); + + // Cursor init + m_CursorPos = g_SceneMan.GetSceneDim() / 2; + + // Set initial focus, category list, and label settings + m_EditorGUIMode = PICKINGOBJECT; + m_ModeChanged = true; + m_pCurrentObject = 0; + + // Reset repeat timers + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + m_RevealTimer.Reset(); + m_RevealTimer.SetRealTimeLimitMS(100); + + // Check if we need to check for a clear path to orbit m_RequireClearPathToOrbit = true; - GameActivity * gameActivity = dynamic_cast(g_ActivityMan.GetActivity()); + GameActivity* gameActivity = dynamic_cast(g_ActivityMan.GetActivity()); if (gameActivity) m_RequireClearPathToOrbit = gameActivity->GetRequireClearPathToOrbit(); // Always disable clear path requirement in scene editor - SceneEditor * editorActivity = dynamic_cast(g_ActivityMan.GetActivity()); + SceneEditor* editorActivity = dynamic_cast(g_ActivityMan.GetActivity()); if (editorActivity) m_RequireClearPathToOrbit = false; - // Only load the static dot bitmaps once - if (!s_pValidPathDot) - { - ContentFile dotFile("Base.rte/GUIs/Indicators/PathDotValid.png"); - s_pValidPathDot = dotFile.GetAsBitmap(); - dotFile.SetDataPath("Base.rte/GUIs/Indicators/PathDotInvalid.png"); - s_pInvalidPathDot = dotFile.GetAsBitmap(); - } + // Only load the static dot bitmaps once + if (!s_pValidPathDot) { + ContentFile dotFile("Base.rte/GUIs/Indicators/PathDotValid.png"); + s_pValidPathDot = dotFile.GetAsBitmap(); + dotFile.SetDataPath("Base.rte/GUIs/Indicators/PathDotInvalid.png"); + s_pInvalidPathDot = dotFile.GetAsBitmap(); + } - return 0; + return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the AssemblyEditorGUI object. -void AssemblyEditorGUI::Destroy() -{ - delete m_pPicker; - delete m_pCurrentObject; +void AssemblyEditorGUI::Destroy() { + delete m_pPicker; + delete m_pCurrentObject; delete m_pCurrentScheme; - Clear(); + Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetController ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets the controller used by this. The ownership of the controller is // NOT transferred! -void AssemblyEditorGUI::SetController(Controller *pController) -{ - m_pController = pController; +void AssemblyEditorGUI::SetController(Controller* pController) { + m_pController = pController; m_PieMenu->SetMenuController(pController); - m_pPicker->SetController(pController); + m_pPicker->SetController(pController); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetPosOnScreen ////////////////////////////////////////////////////////////////////////////////////////// @@ -192,100 +184,84 @@ void AssemblyEditorGUI::SetController(Controller *pController) // left corner, then 0, 0. This will affect the way the mouse is positioned // etc. -void AssemblyEditorGUI::SetPosOnScreen(int newPosX, int newPosY) -{ - m_pPicker->SetPosOnScreen(newPosX, newPosY); +void AssemblyEditorGUI::SetPosOnScreen(int newPosX, int newPosY) { + m_pPicker->SetPosOnScreen(newPosX, newPosY); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetCurrentObject ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets the new Object to be held at the cursor of this Editor. Ownership // IS transferred! -bool AssemblyEditorGUI::SetCurrentObject(SceneObject *pNewObject) -{ - if (m_pCurrentObject == pNewObject) - return true; +bool AssemblyEditorGUI::SetCurrentObject(SceneObject* pNewObject) { + if (m_pCurrentObject == pNewObject) + return true; - // Replace the current object with the new one - if (MovableObject *asMo = dynamic_cast(m_pCurrentObject)) { - asMo->DestroyScriptState(); - } - delete m_pCurrentObject; - m_pCurrentObject = pNewObject; - - if (!m_pCurrentObject) - return false; - - m_pCurrentObject->SetTeam(m_FeatureSet == ONLOADEDIT ? m_PlaceTeam : m_pController->GetTeam()); - m_pCurrentObject->SetPlacedByPlayer(m_pController->GetPlayer()); - - // Disable any controller, if an actor - if (Actor *pActor = dynamic_cast(m_pCurrentObject)) - { - pActor->GetController()->SetDisabled(true); - pActor->SetStatus(Actor::INACTIVE); - } + // Replace the current object with the new one + if (MovableObject* asMo = dynamic_cast(m_pCurrentObject)) { + asMo->DestroyScriptState(); + } + delete m_pCurrentObject; + m_pCurrentObject = pNewObject; + if (!m_pCurrentObject) + return false; + m_pCurrentObject->SetTeam(m_FeatureSet == ONLOADEDIT ? m_PlaceTeam : m_pController->GetTeam()); + m_pCurrentObject->SetPlacedByPlayer(m_pController->GetPlayer()); + // Disable any controller, if an actor + if (Actor* pActor = dynamic_cast(m_pCurrentObject)) { + pActor->GetController()->SetDisabled(true); + pActor->SetStatus(Actor::INACTIVE); + } - return true; + return true; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetActivatedPieSlice ////////////////////////////////////////////////////////////////////////////////////////// // Description: Gets any Pie menu slice command activated last update. PieSlice::SliceType AssemblyEditorGUI::GetActivatedPieSlice() const { - return m_PieMenu->GetPieCommand(); + return m_PieMenu->GetPieCommand(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetModuleSpace ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets which DataModule space to be picking objects from. If -1, then // let the player pick from all loaded modules. -void AssemblyEditorGUI::SetModuleSpace(int moduleSpaceID) -{ - m_pPicker->SetModuleSpace(moduleSpaceID); +void AssemblyEditorGUI::SetModuleSpace(int moduleSpaceID) { + m_pPicker->SetModuleSpace(moduleSpaceID); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetNativeTechModule ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets which DataModule ID should be treated as the native tech of the // user of this menu. -void AssemblyEditorGUI::SetNativeTechModule(int whichModule) -{ - if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) - { - m_NativeTechModule = whichModule; - m_pPicker->SetNativeTechModule(m_NativeTechModule); - } +void AssemblyEditorGUI::SetNativeTechModule(int whichModule) { + if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) { + m_NativeTechModule = whichModule; + m_pPicker->SetNativeTechModule(m_NativeTechModule); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetForeignCostMultiplier ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets the multiplier of the cost of any foreign Tech items. -void AssemblyEditorGUI::SetForeignCostMultiplier(float newMultiplier) -{ - m_ForeignCostMult = newMultiplier; - m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); +void AssemblyEditorGUI::SetForeignCostMultiplier(float newMultiplier) { + m_ForeignCostMult = newMultiplier; + m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: TestBrainResidence ////////////////////////////////////////////////////////////////////////////////////////// @@ -295,109 +271,104 @@ void AssemblyEditorGUI::SetForeignCostMultiplier(float newMultiplier) // current resident brain if the current placement is no bueno. It also // removes the faulty brain from residence in the scene! -bool AssemblyEditorGUI::TestBrainResidence(bool noBrainIsOK) -{ +bool AssemblyEditorGUI::TestBrainResidence(bool noBrainIsOK) { // Brain is fine, leave it be - return false; + return false; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Update ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the state of this Menu each frame -void AssemblyEditorGUI::Update() -{ - // Update the user controller -// m_pController->Update(); +void AssemblyEditorGUI::Update() { + // Update the user controller + // m_pController->Update(); - std::string selectedAssembly = "\nSelected scheme: "; + std::string selectedAssembly = "\nSelected scheme: "; - if (m_pCurrentScheme) - { - std::list assemblies; + if (m_pCurrentScheme) { + std::list assemblies; g_PresetMan.GetAllOfGroup(assemblies, m_pCurrentScheme->GetPresetName(), "BunkerAssembly"); std::stringstream assemblyName; - assemblyName << m_pCurrentScheme->GetPresetName() << " [ " << assemblies.size() << " ]"; + assemblyName << m_pCurrentScheme->GetPresetName() << " [ " << assemblies.size() << " ]"; selectedAssembly += assemblyName.str(); } selectedAssembly += "\nCurrent assembly: " + m_CurrentAssemblyName; - m_EditMade = false; - m_pObjectToBlink = 0; - // Which set of placed objects in the scene we're editing - int editedSet = Scene::PLACEONLOAD; - - ///////////////////////////////////////////// - // Repeating input logic - - bool pressLeft = m_pController->IsState(PRESS_LEFT); - bool pressRight = m_pController->IsState(PRESS_RIGHT); - bool pressUp = m_pController->IsState(PRESS_UP); - bool pressDown = m_pController->IsState(PRESS_DOWN); - - // If no direciton is held down, then cancel the repeating - if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) - { - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - } - - // Check if any direction has been held for the starting amount of time to get into repeat mode - if (m_RepeatStartTimer.IsPastRealMS(200)) - { - // Check for the repeat interval - if (m_RepeatTimer.IsPastRealMS(30)) - { - if (m_pController->IsState(MOVE_RIGHT)) - pressRight = true; - else if (m_pController->IsState(MOVE_LEFT)) - pressLeft = true; - - if (m_pController->IsState(MOVE_UP)) - pressUp = true; - else if (m_pController->IsState(MOVE_DOWN)) - pressDown = true; - - m_RepeatTimer.Reset(); - } - } - - /////////////////////////////////////////////// - // Analog cursor input - - Vector analogInput; - if (m_pController->GetAnalogMove().MagnitudeIsGreaterThan(0.1F)) - analogInput = m_pController->GetAnalogMove(); -// else if (m_pController->GetAnalogAim().MagnitudeIsGreaterThan(0.1F)) -// analogInput = m_pController->GetAnalogAim(); - - ///////////////////////////////////////////// - // PIE MENU + m_EditMade = false; + m_pObjectToBlink = 0; + // Which set of placed objects in the scene we're editing + int editedSet = Scene::PLACEONLOAD; + + ///////////////////////////////////////////// + // Repeating input logic + + bool pressLeft = m_pController->IsState(PRESS_LEFT); + bool pressRight = m_pController->IsState(PRESS_RIGHT); + bool pressUp = m_pController->IsState(PRESS_UP); + bool pressDown = m_pController->IsState(PRESS_DOWN); + + // If no direciton is held down, then cancel the repeating + if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) { + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + } + + // Check if any direction has been held for the starting amount of time to get into repeat mode + if (m_RepeatStartTimer.IsPastRealMS(200)) { + // Check for the repeat interval + if (m_RepeatTimer.IsPastRealMS(30)) { + if (m_pController->IsState(MOVE_RIGHT)) + pressRight = true; + else if (m_pController->IsState(MOVE_LEFT)) + pressLeft = true; + + if (m_pController->IsState(MOVE_UP)) + pressUp = true; + else if (m_pController->IsState(MOVE_DOWN)) + pressDown = true; + + m_RepeatTimer.Reset(); + } + } + + /////////////////////////////////////////////// + // Analog cursor input + + Vector analogInput; + if (m_pController->GetAnalogMove().MagnitudeIsGreaterThan(0.1F)) + analogInput = m_pController->GetAnalogMove(); + // else if (m_pController->GetAnalogAim().MagnitudeIsGreaterThan(0.1F)) + // analogInput = m_pController->GetAnalogAim(); + + ///////////////////////////////////////////// + // PIE MENU m_PieMenu->Update(); - // Show the pie menu only when the secondary button is held down - if (m_pController->IsState(PRESS_SECONDARY) && m_EditorGUIMode != INACTIVE && m_EditorGUIMode != PICKINGOBJECT) { + // Show the pie menu only when the secondary button is held down + if (m_pController->IsState(PRESS_SECONDARY) && m_EditorGUIMode != INACTIVE && m_EditorGUIMode != PICKINGOBJECT) { m_PieMenu->SetPos(m_GridSnapping ? g_SceneMan.SnapPosition(m_CursorPos) : m_CursorPos); m_PieMenu->SetEnabled(true); - PieSlice *saveSlice = m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorSave); + PieSlice* saveSlice = m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorSave); if (saveSlice) { saveSlice->SetEnabled(m_pCurrentScheme != nullptr); saveSlice->SetDescription(m_pCurrentScheme != nullptr ? "Save Assembly" : "Can't Save Assembly, Scheme Not Selected!"); } - } + } - if (!m_pController->IsState(PIE_MENU_ACTIVE) || m_EditorGUIMode == INACTIVE || m_EditorGUIMode == PICKINGOBJECT) { m_PieMenu->SetEnabled(false); } + if (!m_pController->IsState(PIE_MENU_ACTIVE) || m_EditorGUIMode == INACTIVE || m_EditorGUIMode == PICKINGOBJECT) { + m_PieMenu->SetEnabled(false); + } - /////////////////////////////////////// - // Handle pie menu selections + /////////////////////////////////////// + // Handle pie menu selections - if (m_PieMenu->GetPieCommand() != PieSlice::SliceType::NoType) { + if (m_PieMenu->GetPieCommand() != PieSlice::SliceType::NoType) { if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorPick) { m_EditorGUIMode = PICKINGOBJECT; } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorMove) { @@ -408,268 +379,233 @@ void AssemblyEditorGUI::Update() m_EditorGUIMode = DONEEDITING; } - UpdateBrainPath(); - m_ModeChanged = true; - } - - ////////////////////////////////////////// - // Picker logic - - // Enable or disable the picker - m_pPicker->SetEnabled(m_EditorGUIMode == PICKINGOBJECT); - - // Update the picker GUI - m_pPicker->Update(); - - if (m_EditorGUIMode == PICKINGOBJECT && m_pPicker->ObjectPicked()) - { - // Assign a copy of the picked object to be the currently held one. - if (SetCurrentObject(dynamic_cast(m_pPicker->ObjectPicked()->Clone()))) - { - // Set the team - if (m_FeatureSet != ONLOADEDIT) - m_pCurrentObject->SetTeam(m_pController->GetTeam()); - // Set the list order to be at the end so new objects are added there - m_ObjectListOrder = -1; - // Update the object - m_pCurrentObject->FullUpdate(); - // Update the path to the brain, or clear it if there's none - UpdateBrainPath(); - - // If done picking, revert to moving object mode - if (m_pPicker->DonePicking()) - { - m_EditorGUIMode = ADDINGOBJECT; - UpdateBrainPath(); - m_ModeChanged = true; - } - } - } - - if (!m_pPicker->IsVisible()) - g_CameraMan.SetScreenOcclusion(Vector(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - else - g_FrameMan.SetScreenText("Pick what you want to place next" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - ///////////////////////////////////// - // ADDING OBJECT MODE - - if (m_EditorGUIMode == ADDINGOBJECT && !m_PieMenu->IsEnabled()) - { - if (m_ModeChanged) - { - m_ModeChanged = false; - } - g_FrameMan.SetScreenText("Click to ADD a new object - Drag for precision" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - - m_DrawCurrentObject = true; - - // Trap the mouse cursor - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - - // Move the cursor according to analog or mouse input - if (!analogInput.IsZero()) - { - m_CursorPos += analogInput * 8; - // Re-enable snapping only when the cursor is moved again - m_GridSnapping = true; - } - else if (!m_pController->GetMouseMovement().IsZero()) - { - m_CursorPos += m_pController->GetMouseMovement(); - // Re-enable snapping only when the cursor is moved again - m_GridSnapping = true; - } - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= SCENESNAPSIZE; - if (pressRight) - m_CursorPos.m_X += SCENESNAPSIZE; - if (pressDown) - m_CursorPos.m_Y += SCENESNAPSIZE; - if (pressLeft) - m_CursorPos.m_X -= SCENESNAPSIZE; - // Re-enable snapping only when the cursor is moved again - if (pressUp || pressRight || pressDown || pressLeft) - m_GridSnapping = true; - } - - // Detect whether the cursor is in the air, or if it's overlapping some terrain - Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); - m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; - - // Mousewheel is used as shortcut for getting next and prev items in teh picker's object list - if (m_pController->IsState(SCROLL_UP) || m_pController->IsState(ControlState::ACTOR_NEXT)) - { - // Assign a copy of the next picked object to be the currently held one. - const SceneObject *pNewObject = m_pPicker->GetPrevObject(); - if (pNewObject) - { - // Set and update the cursor object - if (SetCurrentObject(dynamic_cast(pNewObject->Clone()))) - m_pCurrentObject->FullUpdate(); - } - } - else if (m_pController->IsState(SCROLL_DOWN) || m_pController->IsState(ControlState::ACTOR_PREV)) - { - // Assign a copy of the next picked object to be the currently held one. - const SceneObject *pNewObject = m_pPicker->GetNextObject(); - if (pNewObject) - { - // Set and update the object - if (SetCurrentObject(dynamic_cast(pNewObject->Clone()))) - m_pCurrentObject->FullUpdate(); - } - } - - // Start the timer when the button is first pressed, and when the picker has deactivated - if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) - { - m_BlinkTimer.Reset(); - m_EditorGUIMode = PLACINGOBJECT; - m_PreviousMode = ADDINGOBJECT; - m_ModeChanged = true; - g_GUISound.PlacementBlip()->Play(); - } - - // Apply the team to the current actor, if applicable - if (m_pCurrentObject && m_DrawCurrentObject) - { - // Set the team of SceneObject based on what's been selected - // Only if full featured mode, otherwise it's based on the controller when placed - if (m_FeatureSet == ONLOADEDIT) - m_pCurrentObject->SetTeam(m_PlaceTeam); - } - } - - ///////////////////////////////////////////////////////////// - // PLACING MODE - - if (m_EditorGUIMode == PLACINGOBJECT) - { - if (m_ModeChanged) - { - m_ModeChanged = false; - } - - if (m_PreviousMode == MOVINGOBJECT) - g_FrameMan.SetScreenText("Click and drag on a placed object to MOVE it - Click quickly to DETACH" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - else - g_FrameMan.SetScreenText("Release to ADD the new object - Tap other button to cancel" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - m_DrawCurrentObject = true; - - // Freeze when first pressing down and grid snapping is still engaged - if (!(m_pController->IsState(PRIMARY_ACTION) && m_GridSnapping)) - { - if (!analogInput.IsZero()) - { - m_CursorPos += analogInput; - m_FacingLeft = analogInput.m_X < 0 || (m_FacingLeft && analogInput.m_X == 0); - } - // Try the mouse - else if (!m_pController->GetMouseMovement().IsZero()) - { - m_CursorPos += m_pController->GetMouseMovement(); - m_FacingLeft = m_pController->GetMouseMovement().m_X < 0 || (m_FacingLeft && m_pController->GetMouseMovement().m_X == 0); - } - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= 1; - if (pressRight) - { - m_CursorPos.m_X += 1; - m_FacingLeft = false; - } - if (pressDown) - m_CursorPos.m_Y += 1; - if (pressLeft) - { - m_CursorPos.m_X -= 1; - m_FacingLeft = true; - } - } - - // Detect whether the cursor is in the air, or if it's overlapping some terrain - Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); - m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; - // Also check that it isn't over unseen areas, can't place there - m_CursorInAir = m_CursorInAir && !g_SceneMan.IsUnseen(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY(), m_pController->GetTeam()); - } - - // Constrain the cursor to only be within specific scene areas -// TODO: THIS!!! - - // Disable snapping after a small interval of holding down the button, to avoid unintentional nudges when just placing on the grid - if (m_pController->IsState(PRIMARY_ACTION) && m_BlinkTimer.IsPastRealMS(333) && m_GridSnapping) - { - m_GridSnapping = false; - m_CursorPos = g_SceneMan.SnapPosition(m_CursorPos); - } - - // Cancel placing if secondary button is pressed - if (m_pController->IsState(PRESS_SECONDARY) || m_pController->IsState(PIE_MENU_ACTIVE)) - { - m_EditorGUIMode = m_PreviousMode; - m_ModeChanged = true; - } - // If previous mode was moving, tear the gib loose if the button is released to soo - else if (m_PreviousMode == MOVINGOBJECT && m_pController->IsState(RELEASE_PRIMARY) && !m_BlinkTimer.IsPastRealMS(150)) - { - m_EditorGUIMode = ADDINGOBJECT; - m_ModeChanged = true; - } - // Only place if the picker and pie menus are completely out of view, to avoid immediate placing after picking - else if (m_pCurrentObject && m_pController->IsState(RELEASE_PRIMARY) && !m_pPicker->IsVisible()) - { - m_pCurrentObject->FullUpdate(); - //If true we need to place object in the end, if false, then it was already given to an actor + UpdateBrainPath(); + m_ModeChanged = true; + } + + ////////////////////////////////////////// + // Picker logic + + // Enable or disable the picker + m_pPicker->SetEnabled(m_EditorGUIMode == PICKINGOBJECT); + + // Update the picker GUI + m_pPicker->Update(); + + if (m_EditorGUIMode == PICKINGOBJECT && m_pPicker->ObjectPicked()) { + // Assign a copy of the picked object to be the currently held one. + if (SetCurrentObject(dynamic_cast(m_pPicker->ObjectPicked()->Clone()))) { + // Set the team + if (m_FeatureSet != ONLOADEDIT) + m_pCurrentObject->SetTeam(m_pController->GetTeam()); + // Set the list order to be at the end so new objects are added there + m_ObjectListOrder = -1; + // Update the object + m_pCurrentObject->FullUpdate(); + // Update the path to the brain, or clear it if there's none + UpdateBrainPath(); + + // If done picking, revert to moving object mode + if (m_pPicker->DonePicking()) { + m_EditorGUIMode = ADDINGOBJECT; + UpdateBrainPath(); + m_ModeChanged = true; + } + } + } + + if (!m_pPicker->IsVisible()) + g_CameraMan.SetScreenOcclusion(Vector(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + else + g_FrameMan.SetScreenText("Pick what you want to place next" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + ///////////////////////////////////// + // ADDING OBJECT MODE + + if (m_EditorGUIMode == ADDINGOBJECT && !m_PieMenu->IsEnabled()) { + if (m_ModeChanged) { + m_ModeChanged = false; + } + g_FrameMan.SetScreenText("Click to ADD a new object - Drag for precision" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + m_DrawCurrentObject = true; + + // Trap the mouse cursor + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + + // Move the cursor according to analog or mouse input + if (!analogInput.IsZero()) { + m_CursorPos += analogInput * 8; + // Re-enable snapping only when the cursor is moved again + m_GridSnapping = true; + } else if (!m_pController->GetMouseMovement().IsZero()) { + m_CursorPos += m_pController->GetMouseMovement(); + // Re-enable snapping only when the cursor is moved again + m_GridSnapping = true; + } + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= SCENESNAPSIZE; + if (pressRight) + m_CursorPos.m_X += SCENESNAPSIZE; + if (pressDown) + m_CursorPos.m_Y += SCENESNAPSIZE; + if (pressLeft) + m_CursorPos.m_X -= SCENESNAPSIZE; + // Re-enable snapping only when the cursor is moved again + if (pressUp || pressRight || pressDown || pressLeft) + m_GridSnapping = true; + } + + // Detect whether the cursor is in the air, or if it's overlapping some terrain + Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); + m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; + + // Mousewheel is used as shortcut for getting next and prev items in teh picker's object list + if (m_pController->IsState(SCROLL_UP) || m_pController->IsState(ControlState::ACTOR_NEXT)) { + // Assign a copy of the next picked object to be the currently held one. + const SceneObject* pNewObject = m_pPicker->GetPrevObject(); + if (pNewObject) { + // Set and update the cursor object + if (SetCurrentObject(dynamic_cast(pNewObject->Clone()))) + m_pCurrentObject->FullUpdate(); + } + } else if (m_pController->IsState(SCROLL_DOWN) || m_pController->IsState(ControlState::ACTOR_PREV)) { + // Assign a copy of the next picked object to be the currently held one. + const SceneObject* pNewObject = m_pPicker->GetNextObject(); + if (pNewObject) { + // Set and update the object + if (SetCurrentObject(dynamic_cast(pNewObject->Clone()))) + m_pCurrentObject->FullUpdate(); + } + } + + // Start the timer when the button is first pressed, and when the picker has deactivated + if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) { + m_BlinkTimer.Reset(); + m_EditorGUIMode = PLACINGOBJECT; + m_PreviousMode = ADDINGOBJECT; + m_ModeChanged = true; + g_GUISound.PlacementBlip()->Play(); + } + + // Apply the team to the current actor, if applicable + if (m_pCurrentObject && m_DrawCurrentObject) { + // Set the team of SceneObject based on what's been selected + // Only if full featured mode, otherwise it's based on the controller when placed + if (m_FeatureSet == ONLOADEDIT) + m_pCurrentObject->SetTeam(m_PlaceTeam); + } + } + + ///////////////////////////////////////////////////////////// + // PLACING MODE + + if (m_EditorGUIMode == PLACINGOBJECT) { + if (m_ModeChanged) { + m_ModeChanged = false; + } + + if (m_PreviousMode == MOVINGOBJECT) + g_FrameMan.SetScreenText("Click and drag on a placed object to MOVE it - Click quickly to DETACH" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + else + g_FrameMan.SetScreenText("Release to ADD the new object - Tap other button to cancel" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + m_DrawCurrentObject = true; + + // Freeze when first pressing down and grid snapping is still engaged + if (!(m_pController->IsState(PRIMARY_ACTION) && m_GridSnapping)) { + if (!analogInput.IsZero()) { + m_CursorPos += analogInput; + m_FacingLeft = analogInput.m_X < 0 || (m_FacingLeft && analogInput.m_X == 0); + } + // Try the mouse + else if (!m_pController->GetMouseMovement().IsZero()) { + m_CursorPos += m_pController->GetMouseMovement(); + m_FacingLeft = m_pController->GetMouseMovement().m_X < 0 || (m_FacingLeft && m_pController->GetMouseMovement().m_X == 0); + } + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= 1; + if (pressRight) { + m_CursorPos.m_X += 1; + m_FacingLeft = false; + } + if (pressDown) + m_CursorPos.m_Y += 1; + if (pressLeft) { + m_CursorPos.m_X -= 1; + m_FacingLeft = true; + } + } + + // Detect whether the cursor is in the air, or if it's overlapping some terrain + Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); + m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; + // Also check that it isn't over unseen areas, can't place there + m_CursorInAir = m_CursorInAir && !g_SceneMan.IsUnseen(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY(), m_pController->GetTeam()); + } + + // Constrain the cursor to only be within specific scene areas + // TODO: THIS!!! + + // Disable snapping after a small interval of holding down the button, to avoid unintentional nudges when just placing on the grid + if (m_pController->IsState(PRIMARY_ACTION) && m_BlinkTimer.IsPastRealMS(333) && m_GridSnapping) { + m_GridSnapping = false; + m_CursorPos = g_SceneMan.SnapPosition(m_CursorPos); + } + + // Cancel placing if secondary button is pressed + if (m_pController->IsState(PRESS_SECONDARY) || m_pController->IsState(PIE_MENU_ACTIVE)) { + m_EditorGUIMode = m_PreviousMode; + m_ModeChanged = true; + } + // If previous mode was moving, tear the gib loose if the button is released to soo + else if (m_PreviousMode == MOVINGOBJECT && m_pController->IsState(RELEASE_PRIMARY) && !m_BlinkTimer.IsPastRealMS(150)) { + m_EditorGUIMode = ADDINGOBJECT; + m_ModeChanged = true; + } + // Only place if the picker and pie menus are completely out of view, to avoid immediate placing after picking + else if (m_pCurrentObject && m_pController->IsState(RELEASE_PRIMARY) && !m_pPicker->IsVisible()) { + m_pCurrentObject->FullUpdate(); + // If true we need to place object in the end, if false, then it was already given to an actor bool toPlace = true; - //If we're placing an item then give that item to actor instead of dropping it nearby - HeldDevice *pHeldDevice = dynamic_cast(m_pCurrentObject); - if (pHeldDevice) - { - if (dynamic_cast(pHeldDevice) || dynamic_cast(pHeldDevice)) - { + // If we're placing an item then give that item to actor instead of dropping it nearby + HeldDevice* pHeldDevice = dynamic_cast(m_pCurrentObject); + if (pHeldDevice) { + if (dynamic_cast(pHeldDevice) || dynamic_cast(pHeldDevice)) { int objectListPosition = -1; - //Find out if we have AHuman under the cursor - const SceneObject *pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &objectListPosition); - const Actor *pAHuman = 0; + // Find out if we have AHuman under the cursor + const SceneObject* pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &objectListPosition); + const Actor* pAHuman = 0; // Looks like we got nothing, search for actors in range then if (!pPickedSceneObject) - pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedActorInRange(editedSet, m_CursorPos, 20, &objectListPosition); + pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedActorInRange(editedSet, m_CursorPos, 20, &objectListPosition); - if (pPickedSceneObject) - { - pAHuman = dynamic_cast(pPickedSceneObject); + if (pPickedSceneObject) { + pAHuman = dynamic_cast(pPickedSceneObject); - if (!pAHuman) - { + if (!pAHuman) { // Maybe we clicked the underlying bunker module, search for actor in range - pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedActorInRange(editedSet, m_CursorPos, 20, &objectListPosition); + pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedActorInRange(editedSet, m_CursorPos, 20, &objectListPosition); if (pPickedSceneObject) - pAHuman = dynamic_cast(pPickedSceneObject); + pAHuman = dynamic_cast(pPickedSceneObject); } - if (pAHuman) - { - //Create a new AHuman instead of old one, give him an item, and delete old AHuman - SceneObject * pNewObject = dynamic_cast(pPickedSceneObject->Clone()); + if (pAHuman) { + // Create a new AHuman instead of old one, give him an item, and delete old AHuman + SceneObject* pNewObject = dynamic_cast(pPickedSceneObject->Clone()); g_SceneMan.GetScene()->RemovePlacedObject(editedSet, objectListPosition); - AHuman *pAHuman = dynamic_cast(pNewObject); - if (pAHuman) - { - pAHuman->AddInventoryItem(dynamic_cast(m_pCurrentObject->Clone())); + AHuman* pAHuman = dynamic_cast(pNewObject); + if (pAHuman) { + pAHuman->AddInventoryItem(dynamic_cast(m_pCurrentObject->Clone())); pAHuman->FlashWhite(150); } @@ -679,19 +615,14 @@ void AssemblyEditorGUI::Update() g_GUISound.PlacementThud()->Play(); toPlace = false; } - } - else - { - //No human? Look for brains robots then - SceneObject *pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); - if (pBrain) - { - if (g_SceneMan.ShortestDistance(pBrain->GetPos(), m_CursorPos,true).MagnitudeIsLessThan(20.0F)) - { - AHuman * pBrainAHuman = dynamic_cast(pBrain); - if (pBrainAHuman) - { - pBrainAHuman->AddInventoryItem(dynamic_cast(m_pCurrentObject->Clone())); + } else { + // No human? Look for brains robots then + SceneObject* pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); + if (pBrain) { + if (g_SceneMan.ShortestDistance(pBrain->GetPos(), m_CursorPos, true).MagnitudeIsLessThan(20.0F)) { + AHuman* pBrainAHuman = dynamic_cast(pBrain); + if (pBrainAHuman) { + pBrainAHuman->AddInventoryItem(dynamic_cast(m_pCurrentObject->Clone())); pBrainAHuman->FlashWhite(150); m_EditMade = true; g_GUISound.PlacementThud()->Play(); @@ -703,37 +634,32 @@ void AssemblyEditorGUI::Update() } } - BunkerAssemblyScheme *pBAS = dynamic_cast(m_pCurrentObject); - if (pBAS) - { - //Clear current scheme and assign new one + BunkerAssemblyScheme* pBAS = dynamic_cast(m_pCurrentObject); + if (pBAS) { + // Clear current scheme and assign new one delete m_pCurrentScheme; - m_pCurrentScheme = dynamic_cast(m_pCurrentObject->Clone()); + m_pCurrentScheme = dynamic_cast(m_pCurrentObject->Clone()); m_CurrentAssemblyName = pBAS->GetPresetName(); // Look through available assemblies and set the name appropriately int number = 1; - std::list assemblies; + std::list assemblies; g_PresetMan.GetAllOfGroup(assemblies, pBAS->GetPresetName(), "BunkerAssembly", -1); - for (int i = 1; i < 256; i++) - { + for (int i = 1; i < 256; i++) { number = i; char currentName[256]; std::snprintf(currentName, sizeof(currentName), "%s - %d", m_CurrentAssemblyName.c_str(), 1); - for (std::list::iterator itr = assemblies.begin(); itr != assemblies.end(); itr++) - { + for (std::list::iterator itr = assemblies.begin(); itr != assemblies.end(); itr++) { std::snprintf(currentName, sizeof(currentName), "%s - %d", m_CurrentAssemblyName.c_str(), number); - if ((*itr)->GetPresetName() == currentName) - { + if ((*itr)->GetPresetName() == currentName) { number = 0; break; } } - if (number > 0) - { + if (number > 0) { m_CurrentAssemblyName = currentName; break; } @@ -746,32 +672,28 @@ void AssemblyEditorGUI::Update() // When we're trying to place bunker assembly it replaces current Scheme with it's parent Scheme // and places all included objects - BunkerAssembly *pBA = dynamic_cast(m_pCurrentObject); - if (pBA) - { - //Clear current scheme and assign new one + BunkerAssembly* pBA = dynamic_cast(m_pCurrentObject); + if (pBA) { + // Clear current scheme and assign new one delete m_pCurrentScheme; - const Entity *pPreset = g_PresetMan.GetEntityPreset("BunkerAssemblyScheme", pBA->GetParentAssemblySchemeName(), -1); - if (pPreset) - { - m_pCurrentScheme = dynamic_cast(pPreset->Clone()); + const Entity* pPreset = g_PresetMan.GetEntityPreset("BunkerAssemblyScheme", pBA->GetParentAssemblySchemeName(), -1); + if (pPreset) { + m_pCurrentScheme = dynamic_cast(pPreset->Clone()); m_pCurrentScheme->SetPos(m_pCurrentObject->GetPos()); } - //Place objects inlcuded in bunker assembly - const std::list *objects = pBA->GetPlacedObjects(); - - for (std::list::const_iterator oItr = objects->begin(); oItr != objects->end(); ++oItr) - { - SceneObject *pSO = dynamic_cast((*oItr)->Clone()); + // Place objects inlcuded in bunker assembly + const std::list* objects = pBA->GetPlacedObjects(); + + for (std::list::const_iterator oItr = objects->begin(); oItr != objects->end(); ++oItr) { + SceneObject* pSO = dynamic_cast((*oItr)->Clone()); // Convert relative coordinates to scene coordintaes Vector pos = pBA->GetPos() + pSO->GetPos() + pBA->GetBitmapOffset(); - //Wrap over seam - if (g_SceneMan.GetScene()->WrapsX()) - { + // Wrap over seam + if (g_SceneMan.GetScene()->WrapsX()) { if (pos.m_X < 0) pos.m_X += g_SceneMan.GetScene()->GetWidth(); @@ -779,8 +701,7 @@ void AssemblyEditorGUI::Update() pos.m_X -= g_SceneMan.GetScene()->GetWidth(); } - if (g_SceneMan.GetScene()->WrapsY()) - { + if (g_SceneMan.GetScene()->WrapsY()) { if (pos.m_Y < 0) pos.m_Y += g_SceneMan.GetScene()->GetHeight(); @@ -800,275 +721,243 @@ void AssemblyEditorGUI::Update() toPlace = false; } - //Finally place an object if still necessary - if (toPlace) - { - g_SceneMan.GetScene()->AddPlacedObject(editedSet, dynamic_cast(m_pCurrentObject->Clone()), m_ObjectListOrder); + // Finally place an object if still necessary + if (toPlace) { + g_SceneMan.GetScene()->AddPlacedObject(editedSet, dynamic_cast(m_pCurrentObject->Clone()), m_ObjectListOrder); // Increment the list order so we place over last placed item if (m_ObjectListOrder >= 0) m_ObjectListOrder++; g_GUISound.PlacementThud()->Play(); m_EditMade = true; - } -// TEMP REMOVE WEHN YOU CLEAN UP THE ABOVE HARDCODED BRAIN PLACEMENT - if (m_EditorGUIMode != PICKINGOBJECT) -// TEMP REMOVE ABOVE - // Go back to previous mode - m_EditorGUIMode = m_PreviousMode; - m_ModeChanged = true; - } - - // Set the facing of AHumans based on right/left cursor movements -// TODO: Improve - Actor *pActor = dynamic_cast(m_pCurrentObject); - if (pActor && dynamic_cast(pActor) || dynamic_cast(pActor)) - pActor->SetHFlipped(m_FacingLeft); - - Deployment *pDeployment = dynamic_cast(m_pCurrentObject); + } + // TEMP REMOVE WEHN YOU CLEAN UP THE ABOVE HARDCODED BRAIN PLACEMENT + if (m_EditorGUIMode != PICKINGOBJECT) + // TEMP REMOVE ABOVE + // Go back to previous mode + m_EditorGUIMode = m_PreviousMode; + m_ModeChanged = true; + } + + // Set the facing of AHumans based on right/left cursor movements + // TODO: Improve + Actor* pActor = dynamic_cast(m_pCurrentObject); + if (pActor && dynamic_cast(pActor) || dynamic_cast(pActor)) + pActor->SetHFlipped(m_FacingLeft); + + Deployment* pDeployment = dynamic_cast(m_pCurrentObject); if (pDeployment) pDeployment->SetHFlipped(m_FacingLeft); - } - - ///////////////////////////////////////////////////////////// - // POINTING AT MODES - - else if ((m_EditorGUIMode == MOVINGOBJECT || m_EditorGUIMode == DELETINGOBJECT) && !m_PieMenu->IsEnabled()) - { - m_DrawCurrentObject = false; - - // Trap the mouse cursor - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - - // Move the cursor according to analog or mouse input - if (!analogInput.IsZero()) - m_CursorPos += analogInput * 4; - else if (!m_pController->GetMouseMovement().IsZero()) - m_CursorPos += m_pController->GetMouseMovement() / 2; - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= 1; - if (pressRight) - m_CursorPos.m_X += 1; - if (pressDown) - m_CursorPos.m_Y += 1; - if (pressLeft) - m_CursorPos.m_X -= 1; - } - - ///////////////////////////////// - // MOVING OBJECT MODE - - if (m_EditorGUIMode == MOVINGOBJECT) - { - if (m_ModeChanged) - { - - m_ModeChanged = false; - } - g_FrameMan.SetScreenText("Click and drag on a placed object to MOVE it - Click quickly to DETACH" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - // Pick an object under the cursor and start moving it - if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) - { - const SceneObject *pPicked = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder); - if (pPicked) - { - // Grab the position and a copy of the the object itself before killing it from the scene - SetCurrentObject(dynamic_cast(pPicked->Clone())); - m_CursorOffset = m_CursorPos - m_pCurrentObject->GetPos(); - g_SceneMan.GetScene()->RemovePlacedObject(editedSet, m_ObjectListOrder); - m_EditMade = true; - - // Go to placing mode to move it around - m_EditorGUIMode = PLACINGOBJECT; - m_PreviousMode = MOVINGOBJECT; - m_ModeChanged = true; - m_BlinkTimer.Reset(); - g_GUISound.PlacementBlip()->Play(); - g_GUISound.PlacementGravel()->Play(); - } - else - g_GUISound.UserErrorSound()->Play(); - } - } - - //////////////////////////// - // REMOVING OBJECT MODE - - else if (m_EditorGUIMode == DELETINGOBJECT) - { - if (m_ModeChanged) - { - - m_ModeChanged = false; - } - g_FrameMan.SetScreenText("Click and hold to select an object - release to DELETE it" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - // When primary is held down, pick object and show which one will be nuked if released - if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) - { - m_pObjectToBlink = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos); - } - else if (m_pController->IsState(RELEASE_PRIMARY)) - { - if (g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder)) - { - // Nuke it! - g_SceneMan.GetScene()->RemovePlacedObject(editedSet, m_ObjectListOrder); - m_EditMade = true; -// TODO: Add awesome destruction sound here - } - else - g_GUISound.UserErrorSound()->Play(); - } - } - } - else if (m_EditorGUIMode == DONEEDITING) - { - // Check first that the brain is in a good spot if finishing up a base edit - if (m_FeatureSet != ONLOADEDIT) - TestBrainResidence(); -// if (m_FeatureSet != ONLOADEDIT) -// g_FrameMan.SetScreenText("DONE editing, wait for all other players to finish too...", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - } - - // Remove cursor offset if not applicable anymore - if (m_EditorGUIMode != PLACINGOBJECT) - m_CursorOffset.Reset(); - - // Keep the cursor position within the world - g_SceneMan.ForceBounds(m_CursorPos); -// TODO: make setscrolltarget with 'sloppy' target - // Scroll to the cursor's scene position - g_CameraMan.SetScrollTarget(m_CursorPos, 0.3, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - // Apply the cursor position to the currently held object - if (m_pCurrentObject && m_DrawCurrentObject) - { - m_pCurrentObject->SetPos(g_SceneMan.SnapPosition(m_CursorPos - m_CursorOffset, m_GridSnapping)); - // If an actor, set it to be inactive so it doesn't scan around and reveal unseen areas - if (Actor *pCurrentActor = dynamic_cast(m_pCurrentObject)) - { - pCurrentActor->SetStatus(Actor::INACTIVE); - pCurrentActor->GetController()->SetDisabled(true); - } - m_pCurrentObject->FullUpdate(); - } -} + } + + ///////////////////////////////////////////////////////////// + // POINTING AT MODES + + else if ((m_EditorGUIMode == MOVINGOBJECT || m_EditorGUIMode == DELETINGOBJECT) && !m_PieMenu->IsEnabled()) { + m_DrawCurrentObject = false; + + // Trap the mouse cursor + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + + // Move the cursor according to analog or mouse input + if (!analogInput.IsZero()) + m_CursorPos += analogInput * 4; + else if (!m_pController->GetMouseMovement().IsZero()) + m_CursorPos += m_pController->GetMouseMovement() / 2; + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= 1; + if (pressRight) + m_CursorPos.m_X += 1; + if (pressDown) + m_CursorPos.m_Y += 1; + if (pressLeft) + m_CursorPos.m_X -= 1; + } + + ///////////////////////////////// + // MOVING OBJECT MODE + + if (m_EditorGUIMode == MOVINGOBJECT) { + if (m_ModeChanged) { + + m_ModeChanged = false; + } + g_FrameMan.SetScreenText("Click and drag on a placed object to MOVE it - Click quickly to DETACH" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + // Pick an object under the cursor and start moving it + if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) { + const SceneObject* pPicked = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder); + if (pPicked) { + // Grab the position and a copy of the the object itself before killing it from the scene + SetCurrentObject(dynamic_cast(pPicked->Clone())); + m_CursorOffset = m_CursorPos - m_pCurrentObject->GetPos(); + g_SceneMan.GetScene()->RemovePlacedObject(editedSet, m_ObjectListOrder); + m_EditMade = true; + + // Go to placing mode to move it around + m_EditorGUIMode = PLACINGOBJECT; + m_PreviousMode = MOVINGOBJECT; + m_ModeChanged = true; + m_BlinkTimer.Reset(); + g_GUISound.PlacementBlip()->Play(); + g_GUISound.PlacementGravel()->Play(); + } else + g_GUISound.UserErrorSound()->Play(); + } + } + + //////////////////////////// + // REMOVING OBJECT MODE + + else if (m_EditorGUIMode == DELETINGOBJECT) { + if (m_ModeChanged) { + + m_ModeChanged = false; + } + g_FrameMan.SetScreenText("Click and hold to select an object - release to DELETE it" + selectedAssembly, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + // When primary is held down, pick object and show which one will be nuked if released + if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) { + m_pObjectToBlink = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos); + } else if (m_pController->IsState(RELEASE_PRIMARY)) { + if (g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder)) { + // Nuke it! + g_SceneMan.GetScene()->RemovePlacedObject(editedSet, m_ObjectListOrder); + m_EditMade = true; + // TODO: Add awesome destruction sound here + } else + g_GUISound.UserErrorSound()->Play(); + } + } + } else if (m_EditorGUIMode == DONEEDITING) { + // Check first that the brain is in a good spot if finishing up a base edit + if (m_FeatureSet != ONLOADEDIT) + TestBrainResidence(); + // if (m_FeatureSet != ONLOADEDIT) + // g_FrameMan.SetScreenText("DONE editing, wait for all other players to finish too...", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + } + // Remove cursor offset if not applicable anymore + if (m_EditorGUIMode != PLACINGOBJECT) + m_CursorOffset.Reset(); + + // Keep the cursor position within the world + g_SceneMan.ForceBounds(m_CursorPos); + // TODO: make setscrolltarget with 'sloppy' target + // Scroll to the cursor's scene position + g_CameraMan.SetScrollTarget(m_CursorPos, 0.3, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + // Apply the cursor position to the currently held object + if (m_pCurrentObject && m_DrawCurrentObject) { + m_pCurrentObject->SetPos(g_SceneMan.SnapPosition(m_CursorPos - m_CursorOffset, m_GridSnapping)); + // If an actor, set it to be inactive so it doesn't scan around and reveal unseen areas + if (Actor* pCurrentActor = dynamic_cast(m_pCurrentObject)) { + pCurrentActor->SetStatus(Actor::INACTIVE); + pCurrentActor->GetController()->SetDisabled(true); + } + m_pCurrentObject->FullUpdate(); + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws the menu -void AssemblyEditorGUI::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) const -{ - // Done, so don't draw the UI - if (m_EditorGUIMode == DONEEDITING) - return; - - // The get a list of the currently edited set of placed objects in the Scene - const std::list *pSceneObjectList = 0; - if (m_FeatureSet == ONLOADEDIT) - pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); - - // Draw the set of currently edited objects already placed in the Scene - if (pSceneObjectList) - { - // Draw all already placed Objects, and the currently held one in the order it is about to be placed in the scene - int i = 0; - Actor *pActor = 0; -// HeldDevice *pDevice = 0; - for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr, ++i) - { - // Draw the currently held object into the order of the list if it is to be placed inside - if (m_pCurrentObject && m_DrawCurrentObject && i == m_ObjectListOrder) - { - g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - m_pCurrentObject->Draw(pTargetBitmap, targetPos, g_DrawTrans); - pActor = dynamic_cast(m_pCurrentObject); - if (pActor) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - - // Is the placed object an actor? - pActor = dynamic_cast(*itr); -// pItem = dynamic_cast(*itr); - - // Blink trans if we are supposed to blink this one - if ((*itr) == m_pObjectToBlink) - { - g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - (*itr)->Draw(pTargetBitmap, targetPos, g_DrawTrans); - } - // Drawing of already placed objects that aren't highlighted or anything - else - { +void AssemblyEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { + // Done, so don't draw the UI + if (m_EditorGUIMode == DONEEDITING) + return; + + // The get a list of the currently edited set of placed objects in the Scene + const std::list* pSceneObjectList = 0; + if (m_FeatureSet == ONLOADEDIT) + pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); + + // Draw the set of currently edited objects already placed in the Scene + if (pSceneObjectList) { + // Draw all already placed Objects, and the currently held one in the order it is about to be placed in the scene + int i = 0; + Actor* pActor = 0; + // HeldDevice *pDevice = 0; + for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr, ++i) { + // Draw the currently held object into the order of the list if it is to be placed inside + if (m_pCurrentObject && m_DrawCurrentObject && i == m_ObjectListOrder) { + g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + m_pCurrentObject->Draw(pTargetBitmap, targetPos, g_DrawTrans); + pActor = dynamic_cast(m_pCurrentObject); + if (pActor) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + + // Is the placed object an actor? + pActor = dynamic_cast(*itr); + // pItem = dynamic_cast(*itr); + + // Blink trans if we are supposed to blink this one + if ((*itr) == m_pObjectToBlink) { + g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + (*itr)->Draw(pTargetBitmap, targetPos, g_DrawTrans); + } + // Drawing of already placed objects that aren't highlighted or anything + else { (*itr)->Draw(pTargetBitmap, targetPos); - } - - // Draw basic HUD if an actor - don't do this for blueprints.. it is confusing - if (pActor) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - } - - // Draw picking object crosshairs and not the selected object - if (!m_DrawCurrentObject) - { - Vector center = m_CursorPos - targetPos; - putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); - } - // If the held object will be placed at the end of the list, draw it last to the scene, transperent blinking - else if (m_pCurrentObject && (m_ObjectListOrder < 0 || (pSceneObjectList && m_ObjectListOrder == pSceneObjectList->size()))) - { - g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - m_pCurrentObject->Draw(pTargetBitmap, targetPos, g_DrawTrans); - Actor *pActor = dynamic_cast(m_pCurrentObject); - if (pActor) - pActor->DrawHUD(pTargetBitmap, targetPos); - } + } + + // Draw basic HUD if an actor - don't do this for blueprints.. it is confusing + if (pActor) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + } + + // Draw picking object crosshairs and not the selected object + if (!m_DrawCurrentObject) { + Vector center = m_CursorPos - targetPos; + putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); + } + // If the held object will be placed at the end of the list, draw it last to the scene, transperent blinking + else if (m_pCurrentObject && (m_ObjectListOrder < 0 || (pSceneObjectList && m_ObjectListOrder == pSceneObjectList->size()))) { + g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + m_pCurrentObject->Draw(pTargetBitmap, targetPos, g_DrawTrans); + Actor* pActor = dynamic_cast(m_pCurrentObject); + if (pActor) + pActor->DrawHUD(pTargetBitmap, targetPos); + } if (m_pCurrentScheme) m_pCurrentScheme->Draw(pTargetBitmap, targetPos); - m_pPicker->Draw(pTargetBitmap); + m_pPicker->Draw(pTargetBitmap); - // Draw the pie menu + // Draw the pie menu m_PieMenu->Draw(pTargetBitmap, targetPos); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateBrainPath ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the brain path to the current resident brain, if any. If // there's none, the path is cleared. -bool AssemblyEditorGUI::UpdateBrainPath() -{ - // First see if we have a brain in hand - if (m_pCurrentObject && m_pCurrentObject->IsInGroup("Brains")) - { - m_BrainSkyPathCost = g_SceneMan.GetScene()->CalculatePath(m_CursorPos, Vector(m_CursorPos.m_X, 0), m_BrainSkyPath, c_PathFindingDefaultDigStrength, static_cast(m_pController->GetTeam())); - return true; - } - - // If not, then do we have a resident? - SceneObject *pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); - if (pBrain) - m_BrainSkyPathCost = g_SceneMan.GetScene()->CalculatePath(pBrain->GetPos(), Vector(pBrain->GetPos().m_X, 0), m_BrainSkyPath, c_PathFindingDefaultDigStrength, static_cast(m_pController->GetTeam())); - else - { - m_BrainSkyPath.clear(); - m_BrainSkyPathCost = 0; - return false; - } - return true; +bool AssemblyEditorGUI::UpdateBrainPath() { + // First see if we have a brain in hand + if (m_pCurrentObject && m_pCurrentObject->IsInGroup("Brains")) { + m_BrainSkyPathCost = g_SceneMan.GetScene()->CalculatePath(m_CursorPos, Vector(m_CursorPos.m_X, 0), m_BrainSkyPath, c_PathFindingDefaultDigStrength, static_cast(m_pController->GetTeam())); + return true; + } + + // If not, then do we have a resident? + SceneObject* pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); + if (pBrain) + m_BrainSkyPathCost = g_SceneMan.GetScene()->CalculatePath(pBrain->GetPos(), Vector(pBrain->GetPos().m_X, 0), m_BrainSkyPath, c_PathFindingDefaultDigStrength, static_cast(m_pController->GetTeam())); + else { + m_BrainSkyPath.clear(); + m_BrainSkyPathCost = 0; + return false; + } + return true; } \ No newline at end of file diff --git a/Source/Menus/AssemblyEditorGUI.h b/Source/Menus/AssemblyEditorGUI.h index 3631bc3314..1d1ae2160d 100644 --- a/Source/Menus/AssemblyEditorGUI.h +++ b/Source/Menus/AssemblyEditorGUI.h @@ -10,11 +10,10 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files -//#include "FrameMan.h" +// #include "FrameMan.h" #include "Timer.h" #include "Vector.h" #include "BunkerAssemblyScheme.h" @@ -24,429 +23,389 @@ struct BITMAP; - -namespace RTE -{ - -class SceneObject; -class ObjectPickerGUI; -class PieMenu; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: AssemblyEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A full menu system that represents the scene editing GUI for Cortex Command -// Parent(s): None. -// Class history: 7/08/2007 AssemblyEditorGUI Created. - -class AssemblyEditorGUI { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - enum FeatureSets - { - ONLOADEDIT = 0 - }; - - // Different modes of this editor - enum EditorGUIMode - { - INACTIVE = 0, - PICKINGOBJECT, - ADDINGOBJECT, - PLACINGOBJECT, - MOVINGOBJECT, - DELETINGOBJECT, - DONEEDITING, - EDITORGUIMODECOUNT - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: AssemblyEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a AssemblyEditorGUI object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - AssemblyEditorGUI() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~AssemblyEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a AssemblyEditorGUI object before deletion -// from system memory. -// Arguments: None. - - ~AssemblyEditorGUI() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AssemblyEditorGUI object ready for use. -// Arguments: A poitner to a Controller which will control this Menu. Ownership is -// NOT TRANSFERRED! -// Whether the editor should have all the features enabled, like load/save -// and undo capabilities. -// Which module space that this eidtor will be able to pick objects from. -// -1 means all modules. -// Which Tech module that will be presented as the native one to the player. -// The multiplier of all foreign techs' costs. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(Controller *pController, FeatureSets featureSet = ONLOADEDIT, int whichModuleSpace = -1, int nativeTechModule = 0, float foreignCostMult = 1.0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire AssemblyEditorGUI, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AssemblyEditorGUI object. -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! -// Arguments: The new controller for this menu. Ownership is NOT transferred -// Return value: None. - - void SetController(Controller *pController); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. -// Arguments: The new screen position of this entire GUI. - - void SetPosOnScreen(int newPosX, int newPosY); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCursorPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the absolute scene coordinates of the cursor of this Editor. -// Arguments: The new cursor position in absolute scene units. -// Return value: None. - - void SetCursorPos(const Vector &newCursorPos) { m_CursorPos = newCursorPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCurrentObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the new Object to be held at the cursor of this Editor. Ownership -// IS transferred! -// Arguments: The new Object to be held by the cursor. Ownership IS transferred! -// Return value: Whether the cursor holds a valid object after setting. - - bool SetCurrentObject(SceneObject *pNewObject); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActivatedPieSlice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets any Pie menu slice command activated last update. -// Arguments: None. -// Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. - - PieSlice::SliceType GetActivatedPieSlice() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the currently held Object in the cursor of this Editor. Ownership -// IS NOT transferred! -// Arguments: None. -// Return value: The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! - - const SceneObject * GetCurrentObject() const { return m_pCurrentObject; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule space to be picking objects from. If -1, then -// let the player pick from all loaded modules. -// Arguments: The ID of the module to let the player pick objects from. All official -// modules' objects will alwayws be presented, in addition to the one -// passed in here. -// Return value: None. - - void SetModuleSpace(int moduleSpaceID = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetNativeTechModule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule ID should be treated as the native tech of the -// user of this menu. -// Arguments: The module ID to set as the native one. 0 means everything is native. -// Return value: None. - - void SetNativeTechModule(int whichModule); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetForeignCostMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the multiplier of the cost of any foreign Tech items. -// Arguments: The scalar multiplier of the costs of foreign Tech items. -// Return value: None. - - void SetForeignCostMultiplier(float newMultiplier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EditMade -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether an edit on the scene was made in the last Update. -// Arguments: None. -// Return value: Whether any edit was made. - - bool EditMade() const { return m_EditMade; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TestBrainResidence -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether the resident brain is currently placed into a valid -// location in this scene, based on whether there is a clear path to the -// sky above it. This forces the editor into place brain mode with the -// current resident brain if the current placement is no bueno. It also -// removes the faulty brain from residence in the scene! -// Arguments: Whether it's OK if we dont' have a brain right now - ie don't force -// into isntallation mode if no brain was found. -// Return value: Whether a resident brain was found, AND found in a valid location! - - bool TestBrainResidence(bool noBrainIsOK = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the editor -// Arguments: The bitmap to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentAssemblyScheme -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a pointer to currently selected assembly scheme -// Arguments: None. -// Return value: Pointer to current assembly scheme. - - BunkerAssemblyScheme * GetCurrentAssemblyScheme() { return m_pCurrentScheme; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentAssemblyName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the name of currently edited assembly -// Arguments: None. -// Return value: Name of currently edited assembly. - - std::string GetCurrentAssemblyName() { return m_CurrentAssemblyName; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCurrentAssemblyScheme -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets new name of currently edited assembly -// Arguments: New name for assembly. -// Return value: None. - - void SetCurrentAssemblyName(std::string newName) { m_CurrentAssemblyName = newName; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePieMenu -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the PieMenu config based ont eh current editor state. -// Arguments: None. -// Return value: None. - - void UpdatePieMenu(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateBrainPath -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the brain path to the current brain in cursor or resident -// in the scene, if any. If there's none, the path is cleared. -// Arguments: None. -// Return value: Whether a resident brain was found in the scene. - - bool UpdateBrainPath(); - - - enum BlinkMode - { - NOBLINK = 0, - OBJECTBLINKON, - OBJECTBLINKOFF, - BLINKMODECOUNT - }; - - // Controller which conrols this menu. Not owned - Controller *m_pController; - // Full featured or the in-game version, or the base building mode - int m_FeatureSet; - // Whether an edit was made to the Scene in the last Update - bool m_EditMade; - // The current mode of the whole GUI. See EditorGUIMode enum. - EditorGUIMode m_EditorGUIMode; - // The previous mode of the whole GUI, to go back to when the current mode is done in some cases - EditorGUIMode m_PreviousMode; - // Whether the editor mode has changed - bool m_ModeChanged; - // Notification blink timer - Timer m_BlinkTimer; - // What we're blinking - int m_BlinkMode; - // Measures the time to when to start repeating inputs when they're held down - Timer m_RepeatStartTimer; - // Measures the interval between input repeats - Timer m_RepeatTimer; - // Measures the interval between graphically revealing objects - Timer m_RevealTimer; - // The index which keeps track of the point in the build queue that blueprint objects go from being ghosted to revealed - int m_RevealIndex; - // Whether we need a clear path to orbit to place brain - bool m_RequireClearPathToOrbit; - - std::unique_ptr m_PieMenu; //!< The PieMenu for this AssemblyEditorGUI. - // The object picker - ObjectPickerGUI *m_pPicker; - // The ID of the DataModule that contains the native Tech of the Player using this menu - int m_NativeTechModule; - // The multiplier of costs of any foreign tech items - float m_ForeignCostMult; - // Grid snapping enabled - bool m_GridSnapping; - // Current cursor position, in absolute scene coordinates - Vector m_CursorPos; - // The offset from the current object's position to the cursor, if any - Vector m_CursorOffset; - // Cursor position in free air, or over something - bool m_CursorInAir; - // SceneObject facing left or not when placing - bool m_FacingLeft; - // The team of the placed SceneObject:s - int m_PlaceTeam; - // Currently held object. This is what is attached to the cursor and will be placed when the fire button is pressed - // OWNED by this. - SceneObject *m_pCurrentObject; - // Where in the scene's list order the next object should be placed. If -1, then place at the end of the list. - int m_ObjectListOrder; - // Whether to draw the currently held object - bool m_DrawCurrentObject; - // Currently placed scene object to make blink when drawing it. NOT OWNED. - const SceneObject *m_pObjectToBlink; - // Path found between brain pos and the sky to make sure fair brain placement - std::list m_BrainSkyPath; - // The cost of the path from the current position of the brain to the sky - float m_BrainSkyPathCost; - // Valid brain path line dots - static BITMAP *s_pValidPathDot; - // Invalid brain path line dots - static BITMAP *s_pInvalidPathDot; - - //Edited scheme type - BunkerAssemblyScheme *m_pCurrentScheme; - //Edited assembly name - std::string m_CurrentAssemblyName; - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AssemblyEditorGUI, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - AssemblyEditorGUI(const AssemblyEditorGUI &reference) = delete; - AssemblyEditorGUI & operator=(const AssemblyEditorGUI &rhs) = delete; - -}; +namespace RTE { + + class SceneObject; + class ObjectPickerGUI; + class PieMenu; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: AssemblyEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A full menu system that represents the scene editing GUI for Cortex Command + // Parent(s): None. + // Class history: 7/08/2007 AssemblyEditorGUI Created. + + class AssemblyEditorGUI { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + enum FeatureSets { + ONLOADEDIT = 0 + }; + + // Different modes of this editor + enum EditorGUIMode { + INACTIVE = 0, + PICKINGOBJECT, + ADDINGOBJECT, + PLACINGOBJECT, + MOVINGOBJECT, + DELETINGOBJECT, + DONEEDITING, + EDITORGUIMODECOUNT + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: AssemblyEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a AssemblyEditorGUI object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + AssemblyEditorGUI() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~AssemblyEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a AssemblyEditorGUI object before deletion + // from system memory. + // Arguments: None. + + ~AssemblyEditorGUI() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the AssemblyEditorGUI object ready for use. + // Arguments: A poitner to a Controller which will control this Menu. Ownership is + // NOT TRANSFERRED! + // Whether the editor should have all the features enabled, like load/save + // and undo capabilities. + // Which module space that this eidtor will be able to pick objects from. + // -1 means all modules. + // Which Tech module that will be presented as the native one to the player. + // The multiplier of all foreign techs' costs. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(Controller* pController, FeatureSets featureSet = ONLOADEDIT, int whichModuleSpace = -1, int nativeTechModule = 0, float foreignCostMult = 1.0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire AssemblyEditorGUI, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the AssemblyEditorGUI object. + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetController + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the controller used by this. The ownership of the controller is + // NOT transferred! + // Arguments: The new controller for this menu. Ownership is NOT transferred + // Return value: None. + + void SetController(Controller* pController); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPosOnScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets where on the screen that this GUI is being drawn to. If upper + // left corner, then 0, 0. This will affect the way the mouse is positioned + // etc. + // Arguments: The new screen position of this entire GUI. + + void SetPosOnScreen(int newPosX, int newPosY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCursorPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the absolute scene coordinates of the cursor of this Editor. + // Arguments: The new cursor position in absolute scene units. + // Return value: None. + + void SetCursorPos(const Vector& newCursorPos) { m_CursorPos = newCursorPos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCurrentObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the new Object to be held at the cursor of this Editor. Ownership + // IS transferred! + // Arguments: The new Object to be held by the cursor. Ownership IS transferred! + // Return value: Whether the cursor holds a valid object after setting. + + bool SetCurrentObject(SceneObject* pNewObject); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActivatedPieSlice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets any Pie menu slice command activated last update. + // Arguments: None. + // Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. + + PieSlice::SliceType GetActivatedPieSlice() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the currently held Object in the cursor of this Editor. Ownership + // IS NOT transferred! + // Arguments: None. + // Return value: The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! + + const SceneObject* GetCurrentObject() const { return m_pCurrentObject; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetModuleSpace + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which DataModule space to be picking objects from. If -1, then + // let the player pick from all loaded modules. + // Arguments: The ID of the module to let the player pick objects from. All official + // modules' objects will alwayws be presented, in addition to the one + // passed in here. + // Return value: None. + + void SetModuleSpace(int moduleSpaceID = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetNativeTechModule + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which DataModule ID should be treated as the native tech of the + // user of this menu. + // Arguments: The module ID to set as the native one. 0 means everything is native. + // Return value: None. + + void SetNativeTechModule(int whichModule); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetForeignCostMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the multiplier of the cost of any foreign Tech items. + // Arguments: The scalar multiplier of the costs of foreign Tech items. + // Return value: None. + + void SetForeignCostMultiplier(float newMultiplier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EditMade + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether an edit on the scene was made in the last Update. + // Arguments: None. + // Return value: Whether any edit was made. + + bool EditMade() const { return m_EditMade; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TestBrainResidence + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks whether the resident brain is currently placed into a valid + // location in this scene, based on whether there is a clear path to the + // sky above it. This forces the editor into place brain mode with the + // current resident brain if the current placement is no bueno. It also + // removes the faulty brain from residence in the scene! + // Arguments: Whether it's OK if we dont' have a brain right now - ie don't force + // into isntallation mode if no brain was found. + // Return value: Whether a resident brain was found, AND found in a valid location! + + bool TestBrainResidence(bool noBrainIsOK = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this Menu each frame + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the editor + // Arguments: The bitmap to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentAssemblyScheme + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a pointer to currently selected assembly scheme + // Arguments: None. + // Return value: Pointer to current assembly scheme. + + BunkerAssemblyScheme* GetCurrentAssemblyScheme() { return m_pCurrentScheme; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentAssemblyName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the name of currently edited assembly + // Arguments: None. + // Return value: Name of currently edited assembly. + + std::string GetCurrentAssemblyName() { return m_CurrentAssemblyName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCurrentAssemblyScheme + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets new name of currently edited assembly + // Arguments: New name for assembly. + // Return value: None. + + void SetCurrentAssemblyName(std::string newName) { m_CurrentAssemblyName = newName; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePieMenu + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the PieMenu config based ont eh current editor state. + // Arguments: None. + // Return value: None. + + void UpdatePieMenu(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateBrainPath + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the brain path to the current brain in cursor or resident + // in the scene, if any. If there's none, the path is cleared. + // Arguments: None. + // Return value: Whether a resident brain was found in the scene. + + bool UpdateBrainPath(); + + enum BlinkMode { + NOBLINK = 0, + OBJECTBLINKON, + OBJECTBLINKOFF, + BLINKMODECOUNT + }; + + // Controller which conrols this menu. Not owned + Controller* m_pController; + // Full featured or the in-game version, or the base building mode + int m_FeatureSet; + // Whether an edit was made to the Scene in the last Update + bool m_EditMade; + // The current mode of the whole GUI. See EditorGUIMode enum. + EditorGUIMode m_EditorGUIMode; + // The previous mode of the whole GUI, to go back to when the current mode is done in some cases + EditorGUIMode m_PreviousMode; + // Whether the editor mode has changed + bool m_ModeChanged; + // Notification blink timer + Timer m_BlinkTimer; + // What we're blinking + int m_BlinkMode; + // Measures the time to when to start repeating inputs when they're held down + Timer m_RepeatStartTimer; + // Measures the interval between input repeats + Timer m_RepeatTimer; + // Measures the interval between graphically revealing objects + Timer m_RevealTimer; + // The index which keeps track of the point in the build queue that blueprint objects go from being ghosted to revealed + int m_RevealIndex; + // Whether we need a clear path to orbit to place brain + bool m_RequireClearPathToOrbit; + + std::unique_ptr m_PieMenu; //!< The PieMenu for this AssemblyEditorGUI. + // The object picker + ObjectPickerGUI* m_pPicker; + // The ID of the DataModule that contains the native Tech of the Player using this menu + int m_NativeTechModule; + // The multiplier of costs of any foreign tech items + float m_ForeignCostMult; + // Grid snapping enabled + bool m_GridSnapping; + // Current cursor position, in absolute scene coordinates + Vector m_CursorPos; + // The offset from the current object's position to the cursor, if any + Vector m_CursorOffset; + // Cursor position in free air, or over something + bool m_CursorInAir; + // SceneObject facing left or not when placing + bool m_FacingLeft; + // The team of the placed SceneObject:s + int m_PlaceTeam; + // Currently held object. This is what is attached to the cursor and will be placed when the fire button is pressed + // OWNED by this. + SceneObject* m_pCurrentObject; + // Where in the scene's list order the next object should be placed. If -1, then place at the end of the list. + int m_ObjectListOrder; + // Whether to draw the currently held object + bool m_DrawCurrentObject; + // Currently placed scene object to make blink when drawing it. NOT OWNED. + const SceneObject* m_pObjectToBlink; + // Path found between brain pos and the sky to make sure fair brain placement + std::list m_BrainSkyPath; + // The cost of the path from the current position of the brain to the sky + float m_BrainSkyPathCost; + // Valid brain path line dots + static BITMAP* s_pValidPathDot; + // Invalid brain path line dots + static BITMAP* s_pInvalidPathDot; + + // Edited scheme type + BunkerAssemblyScheme* m_pCurrentScheme; + // Edited assembly name + std::string m_CurrentAssemblyName; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this AssemblyEditorGUI, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + AssemblyEditorGUI(const AssemblyEditorGUI& reference) = delete; + AssemblyEditorGUI& operator=(const AssemblyEditorGUI& rhs) = delete; + }; } // namespace RTE -#endif // File \ No newline at end of file +#endif // File \ No newline at end of file diff --git a/Source/Menus/BuyMenuGUI.cpp b/Source/Menus/BuyMenuGUI.cpp index 2e09912021..9a8f6a9251 100644 --- a/Source/Menus/BuyMenuGUI.cpp +++ b/Source/Menus/BuyMenuGUI.cpp @@ -7,7 +7,6 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -46,7 +45,7 @@ using namespace RTE; -BITMAP *RTE::BuyMenuGUI::s_pCursor = 0; +BITMAP* RTE::BuyMenuGUI::s_pCursor = 0; const std::string BuyMenuGUI::c_DefaultBannerImagePath = "Base.rte/GUIs/BuyMenu/BuyMenuBanner.png"; const std::string BuyMenuGUI::c_DefaultLogoImagePath = "Base.rte/GUIs/BuyMenu/BuyMenuLogo.png"; @@ -57,46 +56,44 @@ const std::string BuyMenuGUI::c_DefaultLogoImagePath = "Base.rte/GUIs/BuyMenu/Bu // Description: Clears all the member variables of this BuyMenuGUI, effectively // resetting the members of this abstraction level only. -void BuyMenuGUI::Clear() -{ - m_pController = 0; - m_pGUIScreen = 0; - m_pGUIInput = 0; - m_pGUIController = 0; - m_MenuEnabled = DISABLED; - m_MenuFocus = OK; - m_FocusChange = false; - m_MenuCategory = CRAFT; - m_MenuSpeed = 8.0; - m_ListItemIndex = 0; - m_DraggedItemIndex = -1; - m_IsDragging = false; - m_LastHoveredMouseIndex = 0; - m_BlinkTimer.Reset(); - m_BlinkMode = NOBLINK; - m_MenuTimer.Reset(); - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - m_pParentBox = 0; - m_pPopupBox = 0; - m_pPopupText = 0; +void BuyMenuGUI::Clear() { + m_pController = 0; + m_pGUIScreen = 0; + m_pGUIInput = 0; + m_pGUIController = 0; + m_MenuEnabled = DISABLED; + m_MenuFocus = OK; + m_FocusChange = false; + m_MenuCategory = CRAFT; + m_MenuSpeed = 8.0; + m_ListItemIndex = 0; + m_DraggedItemIndex = -1; + m_IsDragging = false; + m_LastHoveredMouseIndex = 0; + m_BlinkTimer.Reset(); + m_BlinkMode = NOBLINK; + m_MenuTimer.Reset(); + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + m_pParentBox = 0; + m_pPopupBox = 0; + m_pPopupText = 0; m_Banner = nullptr; - m_Logo = nullptr; - for (int i = 0; i < CATEGORYCOUNT; ++i) - { - m_pCategoryTabs[i] = 0; - m_CategoryItemIndex[i] = 0; - } - m_MetaPlayer = Players::NoPlayer; - m_NativeTechModule = 0; - m_ForeignCostMult = 4.0; - int moduleCount = g_PresetMan.GetTotalModuleCount(); - m_aExpandedModules = new bool[moduleCount]; - for (int i = 0; i < moduleCount; ++i) - m_aExpandedModules[i] = i == 0 ? true : false; - m_pShopList = 0; - m_pCartList = 0; - m_pCraftBox = 0; + m_Logo = nullptr; + for (int i = 0; i < CATEGORYCOUNT; ++i) { + m_pCategoryTabs[i] = 0; + m_CategoryItemIndex[i] = 0; + } + m_MetaPlayer = Players::NoPlayer; + m_NativeTechModule = 0; + m_ForeignCostMult = 4.0; + int moduleCount = g_PresetMan.GetTotalModuleCount(); + m_aExpandedModules = new bool[moduleCount]; + for (int i = 0; i < moduleCount; ++i) + m_aExpandedModules[i] = i == 0 ? true : false; + m_pShopList = 0; + m_pCartList = 0; + m_pCraftBox = 0; m_pCraftCollectionBox = 0; m_pCraftNameLabel = 0; @@ -106,15 +103,15 @@ void BuyMenuGUI::Clear() m_pCraftMassCaptionLabel = 0; m_pCraftMassLabel = 0; - m_pSelectedCraft = 0; + m_pSelectedCraft = 0; m_DeliveryWidth = 0; - m_pCostLabel = 0; - m_pBuyButton = 0; + m_pCostLabel = 0; + m_pBuyButton = 0; m_ClearOrderButton = nullptr; - m_pSaveButton = 0; - m_pClearButton = 0; - m_Loadouts.clear(); - m_PurchaseMade = false; + m_pSaveButton = 0; + m_pClearButton = 0; + m_Loadouts.clear(); + m_PurchaseMade = false; m_EnforceMaxPassengersConstraint = true; m_EnforceMaxMassConstraint = true; @@ -124,120 +121,112 @@ void BuyMenuGUI::Clear() m_AlwaysAllowedItems.clear(); m_OwnedItems.clear(); - m_SelectingEquipment = false; - m_LastVisitedEquipmentTab = GUNS; - m_LastVisitedMainTab = BODIES; - m_LastEquipmentScrollPosition = -1; - m_LastMainScrollPosition = -1; - m_FirstMainTab = CRAFT; - m_LastMainTab = SETS; - m_FirstEquipmentTab = TOOLS; - m_LastEquipmentTab = SHIELDS; + m_SelectingEquipment = false; + m_LastVisitedEquipmentTab = GUNS; + m_LastVisitedMainTab = BODIES; + m_LastEquipmentScrollPosition = -1; + m_LastMainScrollPosition = -1; + m_FirstMainTab = CRAFT; + m_LastMainTab = SETS; + m_FirstEquipmentTab = TOOLS; + m_LastEquipmentTab = SHIELDS; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the BuyMenuGUI object ready for use. -int BuyMenuGUI::Create(Controller *pController) -{ - RTEAssert(pController, "No controller sent to BuyMenyGUI on creation!"); - m_pController = pController; - - if (!m_pGUIScreen) - m_pGUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer8()); - if (!m_pGUIInput) - m_pGUIInput = new GUIInputWrapper(pController->GetPlayer()); - if (!m_pGUIController) - m_pGUIController = new GUIControlManager(); +int BuyMenuGUI::Create(Controller* pController) { + RTEAssert(pController, "No controller sent to BuyMenyGUI on creation!"); + m_pController = pController; + + if (!m_pGUIScreen) + m_pGUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer8()); + if (!m_pGUIInput) + m_pGUIInput = new GUIInputWrapper(pController->GetPlayer()); + if (!m_pGUIController) + m_pGUIController = new GUIControlManager(); if (!m_pGUIController->Create(m_pGUIScreen, m_pGUIInput, "Base.rte/GUIs/Skins", "DefaultSkin.ini")) { RTEAbort("Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/DefaultSkin.ini"); } - m_pGUIController->Load("Base.rte/GUIs/BuyMenuGUI.ini"); - m_pGUIController->EnableMouse(pController->IsMouseControlled()); + m_pGUIController->Load("Base.rte/GUIs/BuyMenuGUI.ini"); + m_pGUIController->EnableMouse(pController->IsMouseControlled()); - if (!s_pCursor) - { - ContentFile cursorFile("Base.rte/GUIs/Skins/Cursor.png"); - s_pCursor = cursorFile.GetAsBitmap(); - } - - // Stretch the invisible root box to fill the screen - if (g_FrameMan.IsInMultiplayerMode()) - { - dynamic_cast(m_pGUIController->GetControl("base"))->SetSize(g_FrameMan.GetPlayerFrameBufferWidth(pController->GetPlayer()), g_FrameMan.GetPlayerFrameBufferHeight(pController->GetPlayer())); + if (!s_pCursor) { + ContentFile cursorFile("Base.rte/GUIs/Skins/Cursor.png"); + s_pCursor = cursorFile.GetAsBitmap(); } - else - { - dynamic_cast(m_pGUIController->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + + // Stretch the invisible root box to fill the screen + if (g_FrameMan.IsInMultiplayerMode()) { + dynamic_cast(m_pGUIController->GetControl("base"))->SetSize(g_FrameMan.GetPlayerFrameBufferWidth(pController->GetPlayer()), g_FrameMan.GetPlayerFrameBufferHeight(pController->GetPlayer())); + } else { + dynamic_cast(m_pGUIController->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); } - // Make sure we have convenient points to teh containing GUI colleciton boxes that we will manipulate the positions of + // Make sure we have convenient points to teh containing GUI colleciton boxes that we will manipulate the positions of if (!m_pParentBox) { - m_pParentBox = dynamic_cast(m_pGUIController->GetControl("BuyGUIBox")); + m_pParentBox = dynamic_cast(m_pGUIController->GetControl("BuyGUIBox")); m_pParentBox->SetDrawBackground(true); m_pParentBox->SetDrawType(GUICollectionBox::Color); - m_Banner = dynamic_cast(m_pGUIController->GetControl("CatalogHeader")); + m_Banner = dynamic_cast(m_pGUIController->GetControl("CatalogHeader")); SetBannerImage(c_DefaultBannerImagePath); - m_Logo = dynamic_cast(m_pGUIController->GetControl("CatalogLogo")); + m_Logo = dynamic_cast(m_pGUIController->GetControl("CatalogLogo")); SetLogoImage(c_DefaultLogoImagePath); } - m_pParentBox->SetPositionAbs(-m_pParentBox->GetWidth(), 0); - m_pParentBox->SetEnabled(false); - m_pParentBox->SetVisible(false); - - if (!m_pPopupBox) - { - m_pPopupBox = dynamic_cast(m_pGUIController->GetControl("BuyGUIPopup")); - m_pPopupText = dynamic_cast(m_pGUIController->GetControl("PopupText")); - - m_pPopupBox->SetDrawType(GUICollectionBox::Panel); - m_pPopupBox->SetDrawBackground(true); - // Never enable the popup, because it steals focus and cuases other windows to think teh cursor left them - m_pPopupBox->SetEnabled(false); - m_pPopupBox->SetVisible(false); - // Set the font - m_pPopupText->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); - } + m_pParentBox->SetPositionAbs(-m_pParentBox->GetWidth(), 0); + m_pParentBox->SetEnabled(false); + m_pParentBox->SetVisible(false); + + if (!m_pPopupBox) { + m_pPopupBox = dynamic_cast(m_pGUIController->GetControl("BuyGUIPopup")); + m_pPopupText = dynamic_cast(m_pGUIController->GetControl("PopupText")); + + m_pPopupBox->SetDrawType(GUICollectionBox::Panel); + m_pPopupBox->SetDrawBackground(true); + // Never enable the popup, because it steals focus and cuases other windows to think teh cursor left them + m_pPopupBox->SetEnabled(false); + m_pPopupBox->SetVisible(false); + // Set the font + m_pPopupText->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); + } - m_pCategoryTabs[CRAFT] = dynamic_cast(m_pGUIController->GetControl("CraftTab")); - m_pCategoryTabs[BODIES] = dynamic_cast(m_pGUIController->GetControl("BodiesTab")); - m_pCategoryTabs[MECHA] = dynamic_cast(m_pGUIController->GetControl("MechaTab")); - m_pCategoryTabs[TOOLS] = dynamic_cast(m_pGUIController->GetControl("ToolsTab")); - m_pCategoryTabs[GUNS] = dynamic_cast(m_pGUIController->GetControl("GunsTab")); - m_pCategoryTabs[BOMBS] = dynamic_cast(m_pGUIController->GetControl("BombsTab")); - m_pCategoryTabs[SHIELDS] = dynamic_cast(m_pGUIController->GetControl("ShieldsTab")); - m_pCategoryTabs[SETS] = dynamic_cast(m_pGUIController->GetControl("SetsTab")); - RefreshTabDisabledStates(); - - m_pShopList = dynamic_cast(m_pGUIController->GetControl("CatalogLB")); - m_pCartList = dynamic_cast(m_pGUIController->GetControl("OrderLB")); - m_pCraftLabel = dynamic_cast(m_pGUIController->GetControl("CraftLabel")); - m_pCraftBox = dynamic_cast(m_pGUIController->GetControl("CraftTB")); - - m_pCraftCollectionBox = dynamic_cast(m_pGUIController->GetControl("CraftCollection")); - m_pCraftNameLabel = dynamic_cast(m_pGUIController->GetControl("CraftNameLabel")); - m_pCraftPriceLabel = dynamic_cast(m_pGUIController->GetControl("CraftPriceLabel")); - m_pCraftPassengersCaptionLabel = dynamic_cast(m_pGUIController->GetControl("CraftPassengersCaptionLabel")); - m_pCraftPassengersLabel = dynamic_cast(m_pGUIController->GetControl("CraftPassengersLabel")); - m_pCraftMassCaptionLabel = dynamic_cast(m_pGUIController->GetControl("CraftMassCaptionLabel")); - m_pCraftMassLabel = dynamic_cast(m_pGUIController->GetControl("CraftMassLabel")); - - m_pCostLabel = dynamic_cast(m_pGUIController->GetControl("TotalLabel")); - m_pBuyButton = dynamic_cast(m_pGUIController->GetControl("BuyButton")); - m_ClearOrderButton = dynamic_cast(m_pGUIController->GetControl("OrderClearButton")); - m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveButton")); - m_pClearButton = dynamic_cast(m_pGUIController->GetControl("ClearButton")); - m_pSaveButton->SetVisible(false); - m_pClearButton->SetVisible(false); + m_pCategoryTabs[CRAFT] = dynamic_cast(m_pGUIController->GetControl("CraftTab")); + m_pCategoryTabs[BODIES] = dynamic_cast(m_pGUIController->GetControl("BodiesTab")); + m_pCategoryTabs[MECHA] = dynamic_cast(m_pGUIController->GetControl("MechaTab")); + m_pCategoryTabs[TOOLS] = dynamic_cast(m_pGUIController->GetControl("ToolsTab")); + m_pCategoryTabs[GUNS] = dynamic_cast(m_pGUIController->GetControl("GunsTab")); + m_pCategoryTabs[BOMBS] = dynamic_cast(m_pGUIController->GetControl("BombsTab")); + m_pCategoryTabs[SHIELDS] = dynamic_cast(m_pGUIController->GetControl("ShieldsTab")); + m_pCategoryTabs[SETS] = dynamic_cast(m_pGUIController->GetControl("SetsTab")); + RefreshTabDisabledStates(); + + m_pShopList = dynamic_cast(m_pGUIController->GetControl("CatalogLB")); + m_pCartList = dynamic_cast(m_pGUIController->GetControl("OrderLB")); + m_pCraftLabel = dynamic_cast(m_pGUIController->GetControl("CraftLabel")); + m_pCraftBox = dynamic_cast(m_pGUIController->GetControl("CraftTB")); + + m_pCraftCollectionBox = dynamic_cast(m_pGUIController->GetControl("CraftCollection")); + m_pCraftNameLabel = dynamic_cast(m_pGUIController->GetControl("CraftNameLabel")); + m_pCraftPriceLabel = dynamic_cast(m_pGUIController->GetControl("CraftPriceLabel")); + m_pCraftPassengersCaptionLabel = dynamic_cast(m_pGUIController->GetControl("CraftPassengersCaptionLabel")); + m_pCraftPassengersLabel = dynamic_cast(m_pGUIController->GetControl("CraftPassengersLabel")); + m_pCraftMassCaptionLabel = dynamic_cast(m_pGUIController->GetControl("CraftMassCaptionLabel")); + m_pCraftMassLabel = dynamic_cast(m_pGUIController->GetControl("CraftMassLabel")); + + m_pCostLabel = dynamic_cast(m_pGUIController->GetControl("TotalLabel")); + m_pBuyButton = dynamic_cast(m_pGUIController->GetControl("BuyButton")); + m_ClearOrderButton = dynamic_cast(m_pGUIController->GetControl("OrderClearButton")); + m_pSaveButton = dynamic_cast(m_pGUIController->GetControl("SaveButton")); + m_pClearButton = dynamic_cast(m_pGUIController->GetControl("ClearButton")); + m_pSaveButton->SetVisible(false); + m_pClearButton->SetVisible(false); // Stretch buy menu if in multiplayer mode - if (g_FrameMan.IsInMultiplayerMode()) - { + if (g_FrameMan.IsInMultiplayerMode()) { int stretchAmount = g_FrameMan.GetPlayerFrameBufferHeight(pController->GetPlayer()) / 2; m_pParentBox->SetSize(m_pParentBox->GetWidth(), m_pParentBox->GetHeight() + stretchAmount); @@ -250,14 +239,11 @@ int BuyMenuGUI::Create(Controller *pController) m_pCostLabel->SetPositionAbs(m_pCostLabel->GetXPos(), m_pCostLabel->GetYPos() + stretchAmount); m_pBuyButton->SetPositionAbs(m_pBuyButton->GetXPos(), m_pBuyButton->GetYPos() + stretchAmount); - } - else - { + } else { // If we're not split screen horizontally, then stretch out the layout for all the relevant controls int stretchAmount = g_WindowMan.GetResY() / 2; - if (!g_FrameMan.GetHSplit()) - { + if (!g_FrameMan.GetHSplit()) { m_pParentBox->SetSize(m_pParentBox->GetWidth(), m_pParentBox->GetHeight() + stretchAmount); m_pShopList->SetSize(m_pShopList->GetWidth(), m_pShopList->GetHeight() + stretchAmount); m_pCartList->SetSize(m_pCartList->GetWidth(), m_pCartList->GetHeight() + stretchAmount); @@ -271,59 +257,57 @@ int BuyMenuGUI::Create(Controller *pController) } } - m_pShopList->SetAlternateDrawMode(true); - m_pCartList->SetAlternateDrawMode(true); - m_pShopList->SetMultiSelect(false); - m_pCartList->SetMultiSelect(false); -// Do this manually with the MoseMoved notifications -// m_pShopList->SetHotTracking(true); -// m_pCartList->SetHotTracking(true); - m_pCraftBox->SetLocked(true); - m_pShopList->EnableScrollbars(false, true); + m_pShopList->SetAlternateDrawMode(true); + m_pCartList->SetAlternateDrawMode(true); + m_pShopList->SetMultiSelect(false); + m_pCartList->SetMultiSelect(false); + // Do this manually with the MoseMoved notifications + // m_pShopList->SetHotTracking(true); + // m_pCartList->SetHotTracking(true); + m_pCraftBox->SetLocked(true); + m_pShopList->EnableScrollbars(false, true); m_pShopList->SetScrollBarThickness(13); - m_pCartList->EnableScrollbars(false, true); + m_pCartList->EnableScrollbars(false, true); m_pCartList->SetScrollBarThickness(13); - // Load the loadouts initially.. this might be done again later as well by Activity scripts after they set metaplayer etc - LoadAllLoadoutsFromFile(); + // Load the loadouts initially.. this might be done again later as well by Activity scripts after they set metaplayer etc + LoadAllLoadoutsFromFile(); - // Set initial focus, category list, and label settings - m_MenuFocus = OK; - m_FocusChange = true; - m_MenuCategory = CRAFT; - CategoryChange(); - UpdateTotalCostLabel(m_pController->GetTeam()); + // Set initial focus, category list, and label settings + m_MenuFocus = OK; + m_FocusChange = true; + m_MenuCategory = CRAFT; + CategoryChange(); + UpdateTotalCostLabel(m_pController->GetTeam()); - UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); + UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); - // Reset repeat timers - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); + // Reset repeat timers + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); - return 0; + return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the BuyMenuGUI object. -void BuyMenuGUI::Destroy() -{ - delete m_pGUIController; - delete m_pGUIInput; - delete m_pGUIScreen; +void BuyMenuGUI::Destroy() { + delete m_pGUIController; + delete m_pGUIInput; + delete m_pGUIScreen; - delete [] m_aExpandedModules; + delete[] m_aExpandedModules; - Clear(); + Clear(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void BuyMenuGUI::SetBannerImage(const std::string &imagePath) { +void BuyMenuGUI::SetBannerImage(const std::string& imagePath) { ContentFile bannerFile((imagePath.empty() ? c_DefaultBannerImagePath : imagePath).c_str()); m_Banner->SetDrawImage(new AllegroBitmap(bannerFile.GetAsBitmap())); m_Banner->SetDrawType(GUICollectionBox::Image); @@ -331,7 +315,7 @@ void BuyMenuGUI::SetBannerImage(const std::string &imagePath) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void BuyMenuGUI::SetLogoImage(const std::string &imagePath) { +void BuyMenuGUI::SetLogoImage(const std::string& imagePath) { ContentFile logoFile((imagePath.empty() ? c_DefaultLogoImagePath : imagePath).c_str()); m_Logo->SetDrawImage(new AllegroBitmap(logoFile.GetAsBitmap())); m_Logo->SetDrawType(GUICollectionBox::Image); @@ -339,17 +323,16 @@ void BuyMenuGUI::SetLogoImage(const std::string &imagePath) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void BuyMenuGUI::ClearCartList() -{ +void BuyMenuGUI::ClearCartList() { m_pCartList->ClearList(); - m_ListItemIndex = 0; + m_ListItemIndex = 0; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void BuyMenuGUI::AddCartItem(const std::string &name, const std::string &rightText, GUIBitmap *pBitmap, const Entity *pEntity, const int extraIndex) { - m_pCartList->AddItem(name, rightText, pBitmap, pEntity, extraIndex); - UpdateItemNestingLevels(); +void BuyMenuGUI::AddCartItem(const std::string& name, const std::string& rightText, GUIBitmap* pBitmap, const Entity* pEntity, const int extraIndex) { + m_pCartList->AddItem(name, rightText, pBitmap, pEntity, extraIndex); + UpdateItemNestingLevels(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -359,147 +342,138 @@ void BuyMenuGUI::DuplicateCartItem(const int itemIndex) { return; } - std::vector addedItems; + std::vector addedItems; - auto addDuplicateItemAtEnd = [&](const GUIListPanel::Item *itemToCopy) { - GUIBitmap *pItemBitmap = new AllegroBitmap(dynamic_cast(itemToCopy->m_pBitmap)->GetBitmap()); - m_pCartList->AddItem(itemToCopy->m_Name, itemToCopy->m_RightText, pItemBitmap, itemToCopy->m_pEntity, itemToCopy->m_ExtraIndex); - return m_pCartList->GetItem(m_pCartList->GetItemList()->size() - 1); - }; + auto addDuplicateItemAtEnd = [&](const GUIListPanel::Item* itemToCopy) { + GUIBitmap* pItemBitmap = new AllegroBitmap(dynamic_cast(itemToCopy->m_pBitmap)->GetBitmap()); + m_pCartList->AddItem(itemToCopy->m_Name, itemToCopy->m_RightText, pItemBitmap, itemToCopy->m_pEntity, itemToCopy->m_ExtraIndex); + return m_pCartList->GetItem(m_pCartList->GetItemList()->size() - 1); + }; - bool copyingActorWithInventory = m_pCartList->GetItem(itemIndex)->m_pEntity->GetClassName() == "AHuman"; + bool copyingActorWithInventory = m_pCartList->GetItem(itemIndex)->m_pEntity->GetClassName() == "AHuman"; - int currentIndex = itemIndex; - do { - GUIListPanel::Item *newItem = addDuplicateItemAtEnd(*(m_pCartList->GetItemList()->begin() + currentIndex)); - newItem->m_ID = currentIndex; - addedItems.push_back(newItem); + int currentIndex = itemIndex; + do { + GUIListPanel::Item* newItem = addDuplicateItemAtEnd(*(m_pCartList->GetItemList()->begin() + currentIndex)); + newItem->m_ID = currentIndex; + addedItems.push_back(newItem); - currentIndex++; - } while (copyingActorWithInventory && - currentIndex < m_pCartList->GetItemList()->size() - addedItems.size() && - dynamic_cast(m_pCartList->GetItem(currentIndex)->m_pEntity)); + currentIndex++; + } while (copyingActorWithInventory && + currentIndex < m_pCartList->GetItemList()->size() - addedItems.size() && + dynamic_cast(m_pCartList->GetItem(currentIndex)->m_pEntity)); - // Fix up the IDs of the items we're about to shift. - for (auto itr = m_pCartList->GetItemList()->begin() + itemIndex, itr_end = m_pCartList->GetItemList()->end() - addedItems.size(); itr < itr_end; ++itr) { - (*itr)->m_ID += addedItems.size(); - } + // Fix up the IDs of the items we're about to shift. + for (auto itr = m_pCartList->GetItemList()->begin() + itemIndex, itr_end = m_pCartList->GetItemList()->end() - addedItems.size(); itr < itr_end; ++itr) { + (*itr)->m_ID += addedItems.size(); + } - // Now shift all items up to make space. - std::copy(m_pCartList->GetItemList()->begin() + itemIndex, m_pCartList->GetItemList()->end() - addedItems.size(), m_pCartList->GetItemList()->begin() + itemIndex + addedItems.size()); + // Now shift all items up to make space. + std::copy(m_pCartList->GetItemList()->begin() + itemIndex, m_pCartList->GetItemList()->end() - addedItems.size(), m_pCartList->GetItemList()->begin() + itemIndex + addedItems.size()); - // And copy our new items into place. - std::copy(addedItems.begin(), addedItems.end(), m_pCartList->GetItemList()->begin() + itemIndex); + // And copy our new items into place. + std::copy(addedItems.begin(), addedItems.end(), m_pCartList->GetItemList()->begin() + itemIndex); - // Reselect the item, so our selection doesn't move. - m_pCartList->SetSelectedIndex(itemIndex); + // Reselect the item, so our selection doesn't move. + m_pCartList->SetSelectedIndex(itemIndex); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool BuyMenuGUI::LoadAllLoadoutsFromFile() -{ - // First clear out all loadouts - m_Loadouts.clear(); - // Try to load the player's loadout settings from file, if there is one - char loadoutPath[256]; +bool BuyMenuGUI::LoadAllLoadoutsFromFile() { + // First clear out all loadouts + m_Loadouts.clear(); + // Try to load the player's loadout settings from file, if there is one + char loadoutPath[256]; - // A metagame player - if (m_MetaPlayer != Players::NoPlayer) - { - // Start loading any additional stuff from the custom user file - std::snprintf(loadoutPath, sizeof(loadoutPath), "%s%s - LoadoutsMP%d.ini", (System::GetUserdataDirectory() + c_UserConquestSavesModuleName + "/").c_str(), g_MetaMan.GetGameName().c_str(), m_MetaPlayer + 1); - - if (!System::PathExistsCaseSensitive(loadoutPath)) - { - // If the file doesn't exist, then we're not loading it, are we? - loadoutPath[0] = 0; - } - } - // Not a metagame player, just a regular scenario player - else - { - std::snprintf(loadoutPath, sizeof(loadoutPath), "%sLoadoutsP%d.ini", System::GetUserdataDirectory().c_str(), m_pController->GetPlayer() + 1); + // A metagame player + if (m_MetaPlayer != Players::NoPlayer) { + // Start loading any additional stuff from the custom user file + std::snprintf(loadoutPath, sizeof(loadoutPath), "%s%s - LoadoutsMP%d.ini", (System::GetUserdataDirectory() + c_UserConquestSavesModuleName + "/").c_str(), g_MetaMan.GetGameName().c_str(), m_MetaPlayer + 1); + if (!System::PathExistsCaseSensitive(loadoutPath)) { + // If the file doesn't exist, then we're not loading it, are we? + loadoutPath[0] = 0; + } + } + // Not a metagame player, just a regular scenario player + else { + std::snprintf(loadoutPath, sizeof(loadoutPath), "%sLoadoutsP%d.ini", System::GetUserdataDirectory().c_str(), m_pController->GetPlayer() + 1); } - // Open the file - Reader loadoutFile(loadoutPath, false, nullptr, true, true); - - // Read any and all loadout presets from file - while (loadoutFile.ReaderOK() && loadoutFile.NextProperty()) - { - Loadout newLoad; - loadoutFile >> newLoad; - // If we successfully found everything this loadout requires, add it to the preset menu -// Why be picky? - if (!newLoad.GetCargoList()->empty())//newLoad.IsComplete()) - m_Loadouts.push_back(newLoad); - } + // Open the file + Reader loadoutFile(loadoutPath, false, nullptr, true, true); + + // Read any and all loadout presets from file + while (loadoutFile.ReaderOK() && loadoutFile.NextProperty()) { + Loadout newLoad; + loadoutFile >> newLoad; + // If we successfully found everything this loadout requires, add it to the preset menu + // Why be picky? + if (!newLoad.GetCargoList()->empty()) // newLoad.IsComplete()) + m_Loadouts.push_back(newLoad); + } - if (m_NativeTechModule > 0) - { + if (m_NativeTechModule > 0) { // Then try to get the different standard Loadouts for this' player's native tech module if it's not -All- - const Loadout *pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Default", m_NativeTechModule)); + const Loadout* pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Default", m_NativeTechModule)); // Add it to the Loadout list - it will be copied inside so no worry about passing in a preset instance if (pDefaultLoadoutPreset) m_Loadouts.push_back(*pDefaultLoadoutPreset); // Attempt to do it for all the other standard loadout types as well - if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Light", m_NativeTechModule))) + if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Light", m_NativeTechModule))) m_Loadouts.push_back(*pDefaultLoadoutPreset); - if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Heavy", m_NativeTechModule))) + if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Heavy", m_NativeTechModule))) m_Loadouts.push_back(*pDefaultLoadoutPreset); - if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry CQB", m_NativeTechModule))) + if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry CQB", m_NativeTechModule))) m_Loadouts.push_back(*pDefaultLoadoutPreset); - if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Grenadier", m_NativeTechModule))) + if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Grenadier", m_NativeTechModule))) m_Loadouts.push_back(*pDefaultLoadoutPreset); - if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Sniper", m_NativeTechModule))) + if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Sniper", m_NativeTechModule))) m_Loadouts.push_back(*pDefaultLoadoutPreset); - if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Engineer", m_NativeTechModule))) + if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Engineer", m_NativeTechModule))) m_Loadouts.push_back(*pDefaultLoadoutPreset); - if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Mecha", m_NativeTechModule))) + if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Mecha", m_NativeTechModule))) m_Loadouts.push_back(*pDefaultLoadoutPreset); - if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Turret", m_NativeTechModule))) + if (pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Turret", m_NativeTechModule))) m_Loadouts.push_back(*pDefaultLoadoutPreset); } - // If no file was found, try to load a Tech module-specified loadout defaults! - if (m_Loadouts.empty()) - { - // Try to get the default Loadout for this' player's native tech module - const Loadout *pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Default", m_NativeTechModule)); - // Add it to the Loadout list - it will be copied inside so no worry about passing in a preset instance - if (pDefaultLoadoutPreset) - m_Loadouts.push_back(*pDefaultLoadoutPreset); - } -/* This is dangerous, crash prone and unneccessary - // If there were no loadouts to load, or no file present, or default Presets defined, set up a single failsafe default one - if (m_Loadouts.empty()) - { - Loadout defaultLoadout; - // Default craft - defaultLoadout.SetDeliveryCraft(dynamic_cast(g_PresetMan.GetEntityPreset("ACRocket", "Rocket MK1"))); - // Default passenger - defaultLoadout.AddToCargoList(dynamic_cast(g_PresetMan.GetEntityPreset("AHuman", "Robot 1"))); - // Default primary weapon - defaultLoadout.AddToCargoList(dynamic_cast(g_PresetMan.GetEntityPreset("HDFirearm", "SMG"))); - // Default tool - defaultLoadout.AddToCargoList(dynamic_cast(g_PresetMan.GetEntityPreset("HDFirearm", "Medium Digger"))); - // Add to list - if (defaultLoadout.IsComplete()) - m_Loadouts.push_back(defaultLoadout); - } -*/ - // Load the first loadout preset into the cart by default - DeployLoadout(0); - - // Refresh all views so we see the new sets if we're in the preset category - CategoryChange(); - - return true; + // If no file was found, try to load a Tech module-specified loadout defaults! + if (m_Loadouts.empty()) { + // Try to get the default Loadout for this' player's native tech module + const Loadout* pDefaultLoadoutPreset = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Default", m_NativeTechModule)); + // Add it to the Loadout list - it will be copied inside so no worry about passing in a preset instance + if (pDefaultLoadoutPreset) + m_Loadouts.push_back(*pDefaultLoadoutPreset); + } + /* This is dangerous, crash prone and unneccessary + // If there were no loadouts to load, or no file present, or default Presets defined, set up a single failsafe default one + if (m_Loadouts.empty()) + { + Loadout defaultLoadout; + // Default craft + defaultLoadout.SetDeliveryCraft(dynamic_cast(g_PresetMan.GetEntityPreset("ACRocket", "Rocket MK1"))); + // Default passenger + defaultLoadout.AddToCargoList(dynamic_cast(g_PresetMan.GetEntityPreset("AHuman", "Robot 1"))); + // Default primary weapon + defaultLoadout.AddToCargoList(dynamic_cast(g_PresetMan.GetEntityPreset("HDFirearm", "SMG"))); + // Default tool + defaultLoadout.AddToCargoList(dynamic_cast(g_PresetMan.GetEntityPreset("HDFirearm", "Medium Digger"))); + // Add to list + if (defaultLoadout.IsComplete()) + m_Loadouts.push_back(defaultLoadout); + } + */ + // Load the first loadout preset into the cart by default + DeployLoadout(0); + + // Refresh all views so we see the new sets if we're in the preset category + CategoryChange(); + + return true; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: SaveAllLoadoutsToFile ////////////////////////////////////////////////////////////////////////////////////////// @@ -507,55 +481,49 @@ bool BuyMenuGUI::LoadAllLoadoutsFromFile() // any named presets which will be loaded from the standard preset // loadouts first anyway. -bool BuyMenuGUI::SaveAllLoadoutsToFile() -{ - // Nothing to save - if (m_Loadouts.empty()) - return true; - - char loadoutPath[256]; - // A metagame player - if (m_MetaPlayer != Players::NoPlayer) - { - // If a new metagame, then just save over the metagame autosave instead of to the new game save - // Since the players of a new game are likely to have different techs and therefore different default loadouts - // So we should start fresh with new loadouts loaded from tech defaults for each player - if (g_MetaMan.GetGameName() == DEFAULTGAMENAME) - std::snprintf(loadoutPath, sizeof(loadoutPath), "%s%s - LoadoutsMP%d.ini", (System::GetUserdataDirectory() + c_UserConquestSavesModuleName + "/").c_str(), AUTOSAVENAME, m_MetaPlayer + 1); - else - std::snprintf(loadoutPath, sizeof(loadoutPath), "%s%s - LoadoutsMP%d.ini", (System::GetUserdataDirectory() + c_UserConquestSavesModuleName + "/").c_str(), g_MetaMan.GetGameName().c_str(), m_MetaPlayer + 1); - } - else - std::snprintf(loadoutPath, sizeof(loadoutPath), "%sLoadoutsP%d.ini", System::GetUserdataDirectory().c_str(), m_pController->GetPlayer() + 1); +bool BuyMenuGUI::SaveAllLoadoutsToFile() { + // Nothing to save + if (m_Loadouts.empty()) + return true; + + char loadoutPath[256]; + // A metagame player + if (m_MetaPlayer != Players::NoPlayer) { + // If a new metagame, then just save over the metagame autosave instead of to the new game save + // Since the players of a new game are likely to have different techs and therefore different default loadouts + // So we should start fresh with new loadouts loaded from tech defaults for each player + if (g_MetaMan.GetGameName() == DEFAULTGAMENAME) + std::snprintf(loadoutPath, sizeof(loadoutPath), "%s%s - LoadoutsMP%d.ini", (System::GetUserdataDirectory() + c_UserConquestSavesModuleName + "/").c_str(), AUTOSAVENAME, m_MetaPlayer + 1); + else + std::snprintf(loadoutPath, sizeof(loadoutPath), "%s%s - LoadoutsMP%d.ini", (System::GetUserdataDirectory() + c_UserConquestSavesModuleName + "/").c_str(), g_MetaMan.GetGameName().c_str(), m_MetaPlayer + 1); + } else + std::snprintf(loadoutPath, sizeof(loadoutPath), "%sLoadoutsP%d.ini", System::GetUserdataDirectory().c_str(), m_pController->GetPlayer() + 1); - // Open the file - Writer loadoutFile(loadoutPath, false); + // Open the file + Writer loadoutFile(loadoutPath, false); // Write out all the loadouts that are user made. - for (const Loadout &loadoutEntry : m_Loadouts) { + for (const Loadout& loadoutEntry: m_Loadouts) { // Don't write out preset references, they'll be read first from PresetMan on load anyway - if (loadoutEntry.GetPresetName() == "None") { loadoutFile.NewPropertyWithValue("AddLoadout", loadoutEntry); } + if (loadoutEntry.GetPresetName() == "None") { + loadoutFile.NewPropertyWithValue("AddLoadout", loadoutEntry); + } } - return true; + return true; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetEnabled ////////////////////////////////////////////////////////////////////////////////////////// // Description: Enables or disables the menu. This will animate it in and out of view. -void BuyMenuGUI::SetEnabled(bool enable) -{ - if (enable && m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) - { - if (g_FrameMan.IsInMultiplayerMode()) - { +void BuyMenuGUI::SetEnabled(bool enable) { + if (enable && m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) { + if (g_FrameMan.IsInMultiplayerMode()) { // If we're not split screen horizontally, then stretch out the layout for all the relevant controls int stretchAmount = g_FrameMan.GetPlayerFrameBufferHeight(m_pController->GetPlayer()) - m_pParentBox->GetHeight(); - if (stretchAmount != 0) - { + if (stretchAmount != 0) { m_pParentBox->SetSize(m_pParentBox->GetWidth(), m_pParentBox->GetHeight() + stretchAmount); m_pShopList->SetSize(m_pShopList->GetWidth(), m_pShopList->GetHeight() + stretchAmount); m_pCartList->SetSize(m_pCartList->GetWidth(), m_pCartList->GetHeight() + stretchAmount); @@ -567,13 +535,10 @@ void BuyMenuGUI::SetEnabled(bool enable) m_pCostLabel->SetPositionAbs(m_pCostLabel->GetXPos(), m_pCostLabel->GetYPos() + stretchAmount); m_pBuyButton->SetPositionAbs(m_pBuyButton->GetXPos(), m_pBuyButton->GetYPos() + stretchAmount); } - } - else - { + } else { // If we're not split screen horizontally, then stretch out the layout for all the relevant controls int stretchAmount = g_FrameMan.GetPlayerScreenHeight() - m_pParentBox->GetHeight(); - if (stretchAmount != 0) - { + if (stretchAmount != 0) { m_pParentBox->SetSize(m_pParentBox->GetWidth(), m_pParentBox->GetHeight() + stretchAmount); m_pShopList->SetSize(m_pShopList->GetWidth(), m_pShopList->GetHeight() + stretchAmount); m_pCartList->SetSize(m_pCartList->GetWidth(), m_pCartList->GetHeight() + stretchAmount); @@ -587,39 +552,38 @@ void BuyMenuGUI::SetEnabled(bool enable) } } - m_MenuEnabled = ENABLING; - // Reset repeat timers - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - // Set the mouse cursor free - g_UInputMan.TrapMousePos(false, m_pController->GetPlayer()); - // Move the mouse cursor to the middle of the player's screen - int mouseOffX, mouseOffY; - m_pGUIInput->GetMouseOffset(mouseOffX, mouseOffY); - Vector mousePos(-mouseOffX + (g_FrameMan.GetPlayerFrameBufferWidth(m_pController->GetPlayer()) / 2), -mouseOffY + (g_FrameMan.GetPlayerFrameBufferHeight(m_pController->GetPlayer()) / 2)); - g_UInputMan.SetMousePos(mousePos, m_pController->GetPlayer()); - - // Default focus to the menu button - m_MenuFocus = OK; - m_FocusChange = true; - UpdateTotalCostLabel(m_pController->GetTeam()); - - UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); - - g_GUISound.EnterMenuSound()->Play(m_pController->GetPlayer()); - } else if (!enable && m_MenuEnabled != DISABLED && m_MenuEnabled != DISABLING) { - EnableEquipmentSelection(false); - m_MenuEnabled = DISABLING; - // Trap the mouse cursor again - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - // Only play switching away sound -// if (!m_PurchaseMade) - g_GUISound.ExitMenuSound()->Play(m_pController->GetPlayer()); - } + m_MenuEnabled = ENABLING; + // Reset repeat timers + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + // Set the mouse cursor free + g_UInputMan.TrapMousePos(false, m_pController->GetPlayer()); + // Move the mouse cursor to the middle of the player's screen + int mouseOffX, mouseOffY; + m_pGUIInput->GetMouseOffset(mouseOffX, mouseOffY); + Vector mousePos(-mouseOffX + (g_FrameMan.GetPlayerFrameBufferWidth(m_pController->GetPlayer()) / 2), -mouseOffY + (g_FrameMan.GetPlayerFrameBufferHeight(m_pController->GetPlayer()) / 2)); + g_UInputMan.SetMousePos(mousePos, m_pController->GetPlayer()); + + // Default focus to the menu button + m_MenuFocus = OK; + m_FocusChange = true; + UpdateTotalCostLabel(m_pController->GetTeam()); + + UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); + + g_GUISound.EnterMenuSound()->Play(m_pController->GetPlayer()); + } else if (!enable && m_MenuEnabled != DISABLED && m_MenuEnabled != DISABLING) { + EnableEquipmentSelection(false); + m_MenuEnabled = DISABLING; + // Trap the mouse cursor again + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + // Only play switching away sound + // if (!m_PurchaseMade) + g_GUISound.ExitMenuSound()->Play(m_pController->GetPlayer()); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetPosOnScreen ////////////////////////////////////////////////////////////////////////////////////////// @@ -627,25 +591,21 @@ void BuyMenuGUI::SetEnabled(bool enable) // left corner, then 0, 0. This will affect the way the mouse is positioned // etc. -void BuyMenuGUI::SetPosOnScreen(int newPosX, int newPosY) -{ - m_pGUIController->SetPosOnScreen(newPosX, newPosY); +void BuyMenuGUI::SetPosOnScreen(int newPosX, int newPosY) { + m_pGUIController->SetPosOnScreen(newPosX, newPosY); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetMetaPlayer ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets which MetaPlayer uses this menu, if any. -void BuyMenuGUI::SetMetaPlayer(int metaPlayer) -{ - if (metaPlayer >= Players::PlayerOne && metaPlayer < g_MetaMan.GetPlayerCount()) - { - m_MetaPlayer = metaPlayer; - SetNativeTechModule(g_MetaMan.GetPlayer(m_MetaPlayer)->GetNativeTechModule()); - SetForeignCostMultiplier(g_MetaMan.GetPlayer(m_MetaPlayer)->GetForeignCostMultiplier()); - } +void BuyMenuGUI::SetMetaPlayer(int metaPlayer) { + if (metaPlayer >= Players::PlayerOne && metaPlayer < g_MetaMan.GetPlayerCount()) { + m_MetaPlayer = metaPlayer; + SetNativeTechModule(g_MetaMan.GetPlayer(m_MetaPlayer)->GetNativeTechModule()); + SetForeignCostMultiplier(g_MetaMan.GetPlayer(m_MetaPlayer)->GetForeignCostMultiplier()); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -657,8 +617,8 @@ void BuyMenuGUI::SetNativeTechModule(int whichModule) { DeployLoadout(0); if (!g_SettingsMan.FactionBuyMenuThemesDisabled() && m_NativeTechModule > 0) { - if (const DataModule *techModule = g_PresetMan.GetDataModule(whichModule); techModule->IsFaction()) { - const DataModule::BuyMenuTheme &techBuyMenuTheme = techModule->GetFactionBuyMenuTheme(); + if (const DataModule* techModule = g_PresetMan.GetDataModule(whichModule); techModule->IsFaction()) { + const DataModule::BuyMenuTheme& techBuyMenuTheme = techModule->GetFactionBuyMenuTheme(); if (!techBuyMenuTheme.SkinFilePath.empty()) { // Not specifying the skin file directory allows us to load image files from the whole working directory in the skin file instead of just the specified directory. @@ -669,7 +629,9 @@ void BuyMenuGUI::SetNativeTechModule(int whichModule) { m_pGUIController->GetSkin()->GetValue("DescriptionBoxText", "Font", &themeDescriptionBoxTextFont); m_pPopupText->SetFont(m_pGUIController->GetSkin()->GetFont(themeDescriptionBoxTextFont)); } - if (techBuyMenuTheme.BackgroundColorIndex >= 0) { m_pParentBox->SetDrawColor(std::clamp(techBuyMenuTheme.BackgroundColorIndex, 0, 255)); } + if (techBuyMenuTheme.BackgroundColorIndex >= 0) { + m_pParentBox->SetDrawColor(std::clamp(techBuyMenuTheme.BackgroundColorIndex, 0, 255)); + } SetBannerImage(techBuyMenuTheme.BannerImagePath); SetLogoImage(techBuyMenuTheme.LogoImagePath); } @@ -683,130 +645,110 @@ void BuyMenuGUI::SetNativeTechModule(int whichModule) { // Description: Sets whether a data module shown in the item menu should be expanded // or not. -void BuyMenuGUI::SetModuleExpanded(int whichModule, bool expanded) -{ - int moduleCount = g_PresetMan.GetTotalModuleCount(); - if (whichModule > 0 && whichModule < moduleCount) - { - m_aExpandedModules[whichModule] = expanded; - // Refresh the item view with the newly expanded module items - CategoryChange(false); - } - // If base module (0), or out of range module, then affect all - else - { - for (int m = 0; m < moduleCount; ++m) - m_aExpandedModules[m] = expanded; - } +void BuyMenuGUI::SetModuleExpanded(int whichModule, bool expanded) { + int moduleCount = g_PresetMan.GetTotalModuleCount(); + if (whichModule > 0 && whichModule < moduleCount) { + m_aExpandedModules[whichModule] = expanded; + // Refresh the item view with the newly expanded module items + CategoryChange(false); + } + // If base module (0), or out of range module, then affect all + else { + for (int m = 0; m < moduleCount; ++m) + m_aExpandedModules[m] = expanded; + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetOrderList ////////////////////////////////////////////////////////////////////////////////////////// // Description: Return the list of things currently in the purchase order list box. -bool BuyMenuGUI::GetOrderList(std::list &listToFill) const -{ - if (m_pCartList->GetItemList()->empty()) - return false; +bool BuyMenuGUI::GetOrderList(std::list& listToFill) const { + if (m_pCartList->GetItemList()->empty()) + return false; - const SceneObject *pSObject = 0; - for (std::vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr) - { - if (pSObject = dynamic_cast((*itr)->m_pEntity)) - listToFill.push_back(pSObject); - } + const SceneObject* pSObject = 0; + for (std::vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr) { + if (pSObject = dynamic_cast((*itr)->m_pEntity)) + listToFill.push_back(pSObject); + } - return true; + return true; } -bool BuyMenuGUI::CommitPurchase(std::string presetName) -{ - if (m_OwnedItems.size() > 0) - { - if (m_OwnedItems.find(presetName) != m_OwnedItems.end() && m_OwnedItems[presetName] > 0) - { +bool BuyMenuGUI::CommitPurchase(std::string presetName) { + if (m_OwnedItems.size() > 0) { + if (m_OwnedItems.find(presetName) != m_OwnedItems.end() && m_OwnedItems[presetName] > 0) { m_OwnedItems[presetName] -= 1; return true; - } - else + } else return false; } return false; } -float BuyMenuGUI::GetTotalCost(bool includeDelivery) const -{ +float BuyMenuGUI::GetTotalCost(bool includeDelivery) const { float totalCost = 0; - if (m_OwnedItems.size() > 0) - { - std::map orderedItems; + if (m_OwnedItems.size() > 0) { + std::map orderedItems; - for (std::vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr) - { + for (std::vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr) { bool needsToBePaid = true; - std::string presetName = (*itr)->m_pEntity->GetModuleAndPresetName(); + std::string presetName = (*itr)->m_pEntity->GetModuleAndPresetName(); if (orderedItems.find(presetName) != orderedItems.end()) orderedItems[presetName] = 1; else orderedItems[presetName] += 1; - auto itrFound = m_OwnedItems.find(presetName); + auto itrFound = m_OwnedItems.find(presetName); if (itrFound != m_OwnedItems.end() && itrFound->second >= orderedItems[presetName]) needsToBePaid = false; - if (needsToBePaid) - { - totalCost += dynamic_cast((*itr)->m_pEntity)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); + if (needsToBePaid) { + totalCost += dynamic_cast((*itr)->m_pEntity)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); } } - if (m_pSelectedCraft && includeDelivery) - { + if (m_pSelectedCraft && includeDelivery) { bool needsToBePaid = true; - std::string presetName = m_pSelectedCraft->GetModuleAndPresetName(); + std::string presetName = m_pSelectedCraft->GetModuleAndPresetName(); if (orderedItems.find(presetName) != orderedItems.end()) orderedItems[presetName] = 1; else orderedItems[presetName] += 1; - auto itrFound = m_OwnedItems.find(presetName); + auto itrFound = m_OwnedItems.find(presetName); if (itrFound != m_OwnedItems.end() && itrFound->second >= orderedItems[presetName]) needsToBePaid = false; - if (needsToBePaid) - { - totalCost += dynamic_cast(m_pSelectedCraft)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); + if (needsToBePaid) { + totalCost += dynamic_cast(m_pSelectedCraft)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); } } - } - else - { - for (std::vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr) - totalCost += dynamic_cast((*itr)->m_pEntity)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); + } else { + for (std::vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr) + totalCost += dynamic_cast((*itr)->m_pEntity)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); // Add the delivery craft's cost - if (m_pSelectedCraft && includeDelivery) - { - totalCost += dynamic_cast(m_pSelectedCraft)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); + if (m_pSelectedCraft && includeDelivery) { + totalCost += dynamic_cast(m_pSelectedCraft)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); } } - return totalCost; + return totalCost; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float BuyMenuGUI::GetTotalOrderMass() const { float totalMass = 0.0F; - for (const GUIListPanel::Item *cartItem : *m_pCartList->GetItemList()) { - const MovableObject *itemAsMO = dynamic_cast(cartItem->m_pEntity); + for (const GUIListPanel::Item* cartItem: *m_pCartList->GetItemList()) { + const MovableObject* itemAsMO = dynamic_cast(cartItem->m_pEntity); if (itemAsMO) { totalMass += itemAsMO->GetMass(); } else { @@ -817,7 +759,6 @@ float BuyMenuGUI::GetTotalOrderMass() const { return totalMass; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float BuyMenuGUI::GetCraftMass() { @@ -825,12 +766,11 @@ float BuyMenuGUI::GetCraftMass() { // Add the delivery craft's mass if (m_pSelectedCraft) - totalMass += dynamic_cast(m_pSelectedCraft)->GetMass(); + totalMass += dynamic_cast(m_pSelectedCraft)->GetMass(); return totalMass; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetTotalOrderPassengers ////////////////////////////////////////////////////////////////////////////////////////// @@ -840,13 +780,11 @@ float BuyMenuGUI::GetCraftMass() { int BuyMenuGUI::GetTotalOrderPassengers() const { int passengers = 0; - for (std::vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr) - { - const Actor* passenger = dynamic_cast((*itr)->m_pEntity); - if (passenger) - { - passengers += passenger->GetPassengerSlots(); - } + for (std::vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr) { + const Actor* passenger = dynamic_cast((*itr)->m_pEntity); + if (passenger) { + passengers += passenger->GetPassengerSlots(); + } } return passengers; @@ -855,36 +793,36 @@ int BuyMenuGUI::GetTotalOrderPassengers() const { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void BuyMenuGUI::EnableEquipmentSelection(bool enabled) { - if (enabled != m_SelectingEquipment && g_SettingsMan.SmartBuyMenuNavigationEnabled()) { - m_SelectingEquipment = enabled; - RefreshTabDisabledStates(); - - if (m_SelectingEquipment) { - m_LastVisitedMainTab = static_cast(m_MenuCategory); - m_LastMainScrollPosition = m_pShopList->GetScrollVerticalValue(); - m_MenuCategory = m_LastVisitedEquipmentTab; - } else { - m_LastVisitedEquipmentTab = static_cast(m_MenuCategory); - m_LastEquipmentScrollPosition = m_pShopList->GetScrollVerticalValue(); - m_MenuCategory = m_LastVisitedMainTab; - } + if (enabled != m_SelectingEquipment && g_SettingsMan.SmartBuyMenuNavigationEnabled()) { + m_SelectingEquipment = enabled; + RefreshTabDisabledStates(); + + if (m_SelectingEquipment) { + m_LastVisitedMainTab = static_cast(m_MenuCategory); + m_LastMainScrollPosition = m_pShopList->GetScrollVerticalValue(); + m_MenuCategory = m_LastVisitedEquipmentTab; + } else { + m_LastVisitedEquipmentTab = static_cast(m_MenuCategory); + m_LastEquipmentScrollPosition = m_pShopList->GetScrollVerticalValue(); + m_MenuCategory = m_LastVisitedMainTab; + } - CategoryChange(); - m_pShopList->ScrollTo(m_SelectingEquipment ? m_LastEquipmentScrollPosition : m_LastMainScrollPosition); + CategoryChange(); + m_pShopList->ScrollTo(m_SelectingEquipment ? m_LastEquipmentScrollPosition : m_LastMainScrollPosition); - m_MenuFocus = ITEMS; - m_FocusChange = 1; - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } + m_MenuFocus = ITEMS; + m_FocusChange = 1; + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void BuyMenuGUI::UpdateItemNestingLevels() { - const int ownedDeviceOffsetX = 8; + const int ownedDeviceOffsetX = 8; - int nextHeldDeviceBelongsToAHuman = false; - for (GUIListPanel::Item *cartItem : (*m_pCartList->GetItemList())) { + int nextHeldDeviceBelongsToAHuman = false; + for (GUIListPanel::Item* cartItem: (*m_pCartList->GetItemList())) { if (dynamic_cast(cartItem->m_pEntity)) { nextHeldDeviceBelongsToAHuman = true; } else if (!dynamic_cast(cartItem->m_pEntity)) { @@ -894,7 +832,7 @@ void BuyMenuGUI::UpdateItemNestingLevels() { } } - m_pCartList->BuildBitmap(false, true); + m_pCartList->BuildBitmap(false, true); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -918,488 +856,445 @@ void BuyMenuGUI::RefreshTabDisabledStates() { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the state of this Menu each frame -void BuyMenuGUI::Update() -{ - // Enable mouse input if the controller allows it - m_pGUIController->EnableMouse(m_pController->IsMouseControlled()); +void BuyMenuGUI::Update() { + // Enable mouse input if the controller allows it + m_pGUIController->EnableMouse(m_pController->IsMouseControlled()); - // Reset the purchasing indicator - m_PurchaseMade = false; + // Reset the purchasing indicator + m_PurchaseMade = false; - // Popup box is hidden by default - m_pPopupBox->SetVisible(false); + // Popup box is hidden by default + m_pPopupBox->SetVisible(false); - //////////////////////////////////////////////////////////////////////// - // Animate the menu into and out of view if enabled or disabled + //////////////////////////////////////////////////////////////////////// + // Animate the menu into and out of view if enabled or disabled - if (m_MenuEnabled == ENABLING) - { - m_pParentBox->SetEnabled(true); - m_pParentBox->SetVisible(true); - - Vector position, occlusion; - - float toGo = -std::floor((float)m_pParentBox->GetXPos()); - float goProgress = m_MenuSpeed * m_MenuTimer.GetElapsedRealTimeS(); - if (goProgress > 1.0) - goProgress = 1.0; - position.m_X = m_pParentBox->GetXPos() + std::ceil(toGo * goProgress); - occlusion.m_X = m_pParentBox->GetWidth() + m_pParentBox->GetXPos(); - - // If not split screened, then make the menu scroll in diagonally instead of straight from the side - // Tie it to the (X) horizontal position - // EDIT: nah, just make the menu larger, but do change the occlusion, looks better - if (!g_FrameMan.GetHSplit()) - { -// position.m_Y = -m_pParentBox->GetHeight() * fabs(position.m_X / m_pParentBox->GetWidth()); -// occlusion.m_Y = m_pParentBox->GetHeight() + m_pParentBox->GetYPos(); - occlusion.m_Y = m_pParentBox->GetHeight() / 2; - } + if (m_MenuEnabled == ENABLING) { + m_pParentBox->SetEnabled(true); + m_pParentBox->SetVisible(true); + + Vector position, occlusion; - m_pParentBox->SetPositionAbs(position.m_X, position.m_Y); - g_CameraMan.SetScreenOcclusion(occlusion, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + float toGo = -std::floor((float)m_pParentBox->GetXPos()); + float goProgress = m_MenuSpeed * m_MenuTimer.GetElapsedRealTimeS(); + if (goProgress > 1.0) + goProgress = 1.0; + position.m_X = m_pParentBox->GetXPos() + std::ceil(toGo * goProgress); + occlusion.m_X = m_pParentBox->GetWidth() + m_pParentBox->GetXPos(); + + // If not split screened, then make the menu scroll in diagonally instead of straight from the side + // Tie it to the (X) horizontal position + // EDIT: nah, just make the menu larger, but do change the occlusion, looks better + if (!g_FrameMan.GetHSplit()) { + // position.m_Y = -m_pParentBox->GetHeight() * fabs(position.m_X / m_pParentBox->GetWidth()); + // occlusion.m_Y = m_pParentBox->GetHeight() + m_pParentBox->GetYPos(); + occlusion.m_Y = m_pParentBox->GetHeight() / 2; + } - if (m_pParentBox->GetXPos() >= 0) - { + m_pParentBox->SetPositionAbs(position.m_X, position.m_Y); + g_CameraMan.SetScreenOcclusion(occlusion, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + if (m_pParentBox->GetXPos() >= 0) { m_MenuEnabled = ENABLED; CategoryChange(); } - } - // Animate the menu out of view - else if (m_MenuEnabled == DISABLING) - { - float toGo = -std::ceil(((float)m_pParentBox->GetWidth() + (float)m_pParentBox->GetXPos())); - float goProgress = m_MenuSpeed * m_MenuTimer.GetElapsedRealTimeS(); - if (goProgress > 1.0) - goProgress = 1.0; - m_pParentBox->SetPositionAbs(m_pParentBox->GetXPos() + std::floor(toGo * goProgress), 0); - g_CameraMan.SetScreenOcclusion(Vector(m_pParentBox->GetWidth() + m_pParentBox->GetXPos(), 0), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - m_pPopupBox->SetVisible(false); - - if (m_pParentBox->GetXPos() <= -m_pParentBox->GetWidth()) - { - m_pParentBox->SetEnabled(false); - m_pParentBox->SetVisible(false); - m_MenuEnabled = DISABLED; - } - } - else if (m_MenuEnabled == ENABLED) - { - m_pParentBox->SetEnabled(true); - m_pParentBox->SetVisible(true); - - } - else if (m_MenuEnabled == DISABLED) - { - m_pParentBox->SetEnabled(false); - m_pParentBox->SetVisible(false); - m_pPopupBox->SetVisible(false); - } + } + // Animate the menu out of view + else if (m_MenuEnabled == DISABLING) { + float toGo = -std::ceil(((float)m_pParentBox->GetWidth() + (float)m_pParentBox->GetXPos())); + float goProgress = m_MenuSpeed * m_MenuTimer.GetElapsedRealTimeS(); + if (goProgress > 1.0) + goProgress = 1.0; + m_pParentBox->SetPositionAbs(m_pParentBox->GetXPos() + std::floor(toGo * goProgress), 0); + g_CameraMan.SetScreenOcclusion(Vector(m_pParentBox->GetWidth() + m_pParentBox->GetXPos(), 0), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + m_pPopupBox->SetVisible(false); + + if (m_pParentBox->GetXPos() <= -m_pParentBox->GetWidth()) { + m_pParentBox->SetEnabled(false); + m_pParentBox->SetVisible(false); + m_MenuEnabled = DISABLED; + } + } else if (m_MenuEnabled == ENABLED) { + m_pParentBox->SetEnabled(true); + m_pParentBox->SetVisible(true); + + } else if (m_MenuEnabled == DISABLED) { + m_pParentBox->SetEnabled(false); + m_pParentBox->SetVisible(false); + m_pPopupBox->SetVisible(false); + } - // Reset the menu animation timer so we can measure how long it takes till next frame - m_MenuTimer.Reset(); + // Reset the menu animation timer so we can measure how long it takes till next frame + m_MenuTimer.Reset(); - // Quit now if we aren't enabled - if (m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) - return; + // Quit now if we aren't enabled + if (m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) + return; - // Update the user controller -// m_pController->Update(); + // Update the user controller + // m_pController->Update(); - // Gotto update this all the time because other players may have bought stuff and changed the funds available to the team - UpdateTotalCostLabel(m_pController->GetTeam()); + // Gotto update this all the time because other players may have bought stuff and changed the funds available to the team + UpdateTotalCostLabel(m_pController->GetTeam()); - UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); + UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); - ///////////////////////////////////////////////////// - // Mouse cursor logic + ///////////////////////////////////////////////////// + // Mouse cursor logic int mousePosX; int mousePosY; - m_pGUIInput->GetMousePosition(&mousePosX, &mousePosY); + m_pGUIInput->GetMousePosition(&mousePosX, &mousePosY); - //////////////////////////////////////////// - // Notification blinking logic + //////////////////////////////////////////// + // Notification blinking logic - if (m_BlinkMode == NOFUNDS) - { - m_pCostLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); - } - else if (m_BlinkMode == NOCRAFT) - { - bool blink = m_BlinkTimer.AlternateSim(250); - m_pCraftLabel->SetVisible(blink); - m_pCraftBox->SetVisible(blink); + if (m_BlinkMode == NOFUNDS) { + m_pCostLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); + } else if (m_BlinkMode == NOCRAFT) { + bool blink = m_BlinkTimer.AlternateSim(250); + m_pCraftLabel->SetVisible(blink); + m_pCraftBox->SetVisible(blink); m_pCraftCollectionBox->SetVisible(blink); - } - else if (m_BlinkMode == MAXMASS) - { + } else if (m_BlinkMode == MAXMASS) { m_pCraftMassLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); m_pCraftMassCaptionLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); - } - else if (m_BlinkMode == MAXPASSENGERS) - { + } else if (m_BlinkMode == MAXPASSENGERS) { m_pCraftPassengersLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); m_pCraftPassengersCaptionLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); } - // Time out the blinker - if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastSimMS(1500)) - { - m_pCostLabel->SetVisible(true); - m_pCraftLabel->SetVisible(true); - m_pCraftBox->SetVisible(true); + // Time out the blinker + if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastSimMS(1500)) { + m_pCostLabel->SetVisible(true); + m_pCraftLabel->SetVisible(true); + m_pCraftBox->SetVisible(true); m_pCraftCollectionBox->SetVisible(true); m_pCraftMassCaptionLabel->SetVisible(true); m_pCraftMassLabel->SetVisible(true); m_pCraftPassengersCaptionLabel->SetVisible(true); m_pCraftPassengersLabel->SetVisible(true); m_BlinkMode = NOBLINK; - } + } - ///////////////////////////////////////////// - // Repeating input logic + ///////////////////////////////////////////// + // Repeating input logic - bool pressLeft = m_pController->IsState(PRESS_LEFT); - bool pressRight = m_pController->IsState(PRESS_RIGHT); - bool pressUp = m_pController->IsState(PRESS_UP); - bool pressDown = m_pController->IsState(PRESS_DOWN); - bool pressScrollUp = m_pController->IsState(SCROLL_UP); - bool pressScrollDown = m_pController->IsState(SCROLL_DOWN); + bool pressLeft = m_pController->IsState(PRESS_LEFT); + bool pressRight = m_pController->IsState(PRESS_RIGHT); + bool pressUp = m_pController->IsState(PRESS_UP); + bool pressDown = m_pController->IsState(PRESS_DOWN); + bool pressScrollUp = m_pController->IsState(SCROLL_UP); + bool pressScrollDown = m_pController->IsState(SCROLL_DOWN); - bool pressNextActor = m_pController->IsState(ACTOR_NEXT); - bool pressPrevActor = m_pController->IsState(ACTOR_PREV); + bool pressNextActor = m_pController->IsState(ACTOR_NEXT); + bool pressPrevActor = m_pController->IsState(ACTOR_PREV); - // If no direciton is held down, then cancel the repeating - if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN) || m_pController->IsState(ACTOR_NEXT_PREP) || m_pController->IsState(ACTOR_PREV_PREP))) - { - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - } - - // Check if any direction has been held for the starting amount of time to get into repeat mode - if (m_RepeatStartTimer.IsPastRealMS(200)) { - // Check for the repeat interval - if (m_RepeatTimer.IsPastRealMS(75)) { - if (m_pController->IsState(MOVE_RIGHT)) { - pressRight = true; - } else if (m_pController->IsState(MOVE_LEFT)) { - pressLeft = true; - } else if (m_pController->IsState(MOVE_UP)) { - pressUp = true; - } else if (m_pController->IsState(MOVE_DOWN)) { - pressDown = true; - } else if (m_pController->IsState(ACTOR_NEXT_PREP)) { - pressNextActor = true; - } else if (m_pController->IsState(ACTOR_PREV_PREP)) { - pressPrevActor = true; - } - m_RepeatTimer.Reset(); - } - } - - ///////////////////////////////////////////// - // Change focus as the user directs + // If no direciton is held down, then cancel the repeating + if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN) || m_pController->IsState(ACTOR_NEXT_PREP) || m_pController->IsState(ACTOR_PREV_PREP))) { + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + } - if (pressRight) - { - m_MenuFocus++; - m_FocusChange = 1; + // Check if any direction has been held for the starting amount of time to get into repeat mode + if (m_RepeatStartTimer.IsPastRealMS(200)) { + // Check for the repeat interval + if (m_RepeatTimer.IsPastRealMS(75)) { + if (m_pController->IsState(MOVE_RIGHT)) { + pressRight = true; + } else if (m_pController->IsState(MOVE_LEFT)) { + pressLeft = true; + } else if (m_pController->IsState(MOVE_UP)) { + pressUp = true; + } else if (m_pController->IsState(MOVE_DOWN)) { + pressDown = true; + } else if (m_pController->IsState(ACTOR_NEXT_PREP)) { + pressNextActor = true; + } else if (m_pController->IsState(ACTOR_PREV_PREP)) { + pressPrevActor = true; + } + m_RepeatTimer.Reset(); + } + } - // Went too far. Don't allow focusing the clear order button by pressing right, will most definitely lead to accidental clearing and rage. - if (m_MenuFocus >= MenuFocus::CLEARORDER) - { - m_MenuFocus = MenuFocus::CLEARORDER - 1; - m_FocusChange = 0; - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - // Skip categories if we're going sideways from the sets buttons - if (m_MenuFocus == CATEGORIES) - { - m_MenuFocus++; - m_FocusChange++; - } - // Skip giving focus to the items list if it's empty - if (m_MenuFocus == ITEMS && m_pShopList->GetItemList()->empty()) - { - m_MenuFocus++; - m_FocusChange++; - } - // Skip giving focus to the order list if it's empty - if (m_MenuFocus == ORDER && m_pCartList->GetItemList()->empty()) - { - m_MenuFocus++; - m_FocusChange++; - } - } - else if (pressLeft) - { - m_MenuFocus--; - m_FocusChange = -1; + ///////////////////////////////////////////// + // Change focus as the user directs - // Went too far - if (m_MenuFocus < 0) - { - m_MenuFocus = 0; - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - // Skip giving focus to the order or item list if they're empty - if (m_MenuFocus == ORDER && m_pCartList->GetItemList()->empty()) - { - m_MenuFocus--; - m_FocusChange--; - } - // Skip giving focus to the items list if it's empty - if (m_MenuFocus == ITEMS && m_pShopList->GetItemList()->empty()) - { - m_MenuFocus--; - m_FocusChange--; - } - } - // Play focus change sound, if applicable - if (m_FocusChange && m_MenuEnabled != ENABLING) - g_GUISound.FocusChangeSound()->Play(m_pController->GetPlayer()); - /* Blah, should control whatever is currently focused - // Mouse wheel only controls the categories, so switch to it and make the category go up or down - if (m_pController->IsState(SCROLL_UP) || m_pController->IsState(SCROLL_DOWN)) - { - m_FocusChange = CATEGORIES - m_MenuFocus; - m_MenuFocus = CATEGORIES; - } - */ + if (pressRight) { + m_MenuFocus++; + m_FocusChange = 1; - ///////////////////////////////////////// - // Change Category directly + // Went too far. Don't allow focusing the clear order button by pressing right, will most definitely lead to accidental clearing and rage. + if (m_MenuFocus >= MenuFocus::CLEARORDER) { + m_MenuFocus = MenuFocus::CLEARORDER - 1; + m_FocusChange = 0; + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + // Skip categories if we're going sideways from the sets buttons + if (m_MenuFocus == CATEGORIES) { + m_MenuFocus++; + m_FocusChange++; + } + // Skip giving focus to the items list if it's empty + if (m_MenuFocus == ITEMS && m_pShopList->GetItemList()->empty()) { + m_MenuFocus++; + m_FocusChange++; + } + // Skip giving focus to the order list if it's empty + if (m_MenuFocus == ORDER && m_pCartList->GetItemList()->empty()) { + m_MenuFocus++; + m_FocusChange++; + } + } else if (pressLeft) { + m_MenuFocus--; + m_FocusChange = -1; + + // Went too far + if (m_MenuFocus < 0) { + m_MenuFocus = 0; + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + // Skip giving focus to the order or item list if they're empty + if (m_MenuFocus == ORDER && m_pCartList->GetItemList()->empty()) { + m_MenuFocus--; + m_FocusChange--; + } + // Skip giving focus to the items list if it's empty + if (m_MenuFocus == ITEMS && m_pShopList->GetItemList()->empty()) { + m_MenuFocus--; + m_FocusChange--; + } + } + // Play focus change sound, if applicable + if (m_FocusChange && m_MenuEnabled != ENABLING) + g_GUISound.FocusChangeSound()->Play(m_pController->GetPlayer()); + /* Blah, should control whatever is currently focused + // Mouse wheel only controls the categories, so switch to it and make the category go up or down + if (m_pController->IsState(SCROLL_UP) || m_pController->IsState(SCROLL_DOWN)) + { + m_FocusChange = CATEGORIES - m_MenuFocus; + m_MenuFocus = CATEGORIES; + } + */ - if (pressNextActor || pressPrevActor) { - bool smartBuyMenuNavigationEnabled = g_SettingsMan.SmartBuyMenuNavigationEnabled(); - if (pressNextActor) { - m_MenuCategory++; - if (!smartBuyMenuNavigationEnabled) { - m_MenuCategory = m_MenuCategory >= MenuCategory::CATEGORYCOUNT ? 0 : m_MenuCategory; - } else if (m_SelectingEquipment && m_MenuCategory > m_LastEquipmentTab) { - m_MenuCategory = m_FirstEquipmentTab; - } else if (!m_SelectingEquipment) { - if (m_MenuCategory > m_LastMainTab) { - m_MenuCategory = m_FirstMainTab; - } else if (m_MenuCategory == m_FirstEquipmentTab) { - m_MenuCategory = m_LastMainTab; - } - } - } else if (pressPrevActor) { - m_MenuCategory--; - if (!smartBuyMenuNavigationEnabled) { - m_MenuCategory = m_MenuCategory < 0 ? MenuCategory::CATEGORYCOUNT - 1 : m_MenuCategory; - } else if (m_SelectingEquipment && m_MenuCategory < m_FirstEquipmentTab) { - m_MenuCategory = m_LastEquipmentTab; - } else if (!m_SelectingEquipment) { - if (m_MenuCategory < m_FirstMainTab) { - m_MenuCategory = m_LastMainTab; - } else if (m_MenuCategory == m_LastEquipmentTab) { - m_MenuCategory = m_FirstEquipmentTab - 1; - } - } - } + ///////////////////////////////////////// + // Change Category directly + + if (pressNextActor || pressPrevActor) { + bool smartBuyMenuNavigationEnabled = g_SettingsMan.SmartBuyMenuNavigationEnabled(); + if (pressNextActor) { + m_MenuCategory++; + if (!smartBuyMenuNavigationEnabled) { + m_MenuCategory = m_MenuCategory >= MenuCategory::CATEGORYCOUNT ? 0 : m_MenuCategory; + } else if (m_SelectingEquipment && m_MenuCategory > m_LastEquipmentTab) { + m_MenuCategory = m_FirstEquipmentTab; + } else if (!m_SelectingEquipment) { + if (m_MenuCategory > m_LastMainTab) { + m_MenuCategory = m_FirstMainTab; + } else if (m_MenuCategory == m_FirstEquipmentTab) { + m_MenuCategory = m_LastMainTab; + } + } + } else if (pressPrevActor) { + m_MenuCategory--; + if (!smartBuyMenuNavigationEnabled) { + m_MenuCategory = m_MenuCategory < 0 ? MenuCategory::CATEGORYCOUNT - 1 : m_MenuCategory; + } else if (m_SelectingEquipment && m_MenuCategory < m_FirstEquipmentTab) { + m_MenuCategory = m_LastEquipmentTab; + } else if (!m_SelectingEquipment) { + if (m_MenuCategory < m_FirstMainTab) { + m_MenuCategory = m_LastMainTab; + } else if (m_MenuCategory == m_LastEquipmentTab) { + m_MenuCategory = m_FirstEquipmentTab - 1; + } + } + } - CategoryChange(); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + CategoryChange(); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - m_MenuFocus = ITEMS; - m_FocusChange = 1; - } + m_MenuFocus = ITEMS; + m_FocusChange = 1; + } - ///////////////////////////////////////// - // Scroll buy menu + ///////////////////////////////////////// + // Scroll buy menu - if (pressScrollUp) { - m_pShopList->ScrollUp(); - } else if (pressScrollDown) { - m_pShopList->ScrollDown(); - } + if (pressScrollUp) { + m_pShopList->ScrollUp(); + } else if (pressScrollDown) { + m_pShopList->ScrollDown(); + } - //Special handling to allow user to click on disabled tab buttons with mouse. - RefreshTabDisabledStates(); - for (int category = 0; category < CATEGORYCOUNT; ++category) { - if (!m_pCategoryTabs[category]->IsEnabled()) { - if (m_MenuCategory == category) { m_pCategoryTabs[m_MenuCategory]->SetEnabled(true); } - if (m_pCategoryTabs[category]->PointInside(mousePosX + 3, mousePosY)) { - if (m_pController->IsState(PRESS_PRIMARY)) { - m_MenuCategory = category; - m_pCategoryTabs[m_MenuCategory]->SetFocus(); - m_pCategoryTabs[m_MenuCategory]->SetEnabled(true); - CategoryChange(); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } else if (!m_pCategoryTabs[category]->IsEnabled() && !m_pCategoryTabs[category]->HasFocus()) { - m_pCategoryTabs[category]->SetFocus(); - } - } - } - } + // Special handling to allow user to click on disabled tab buttons with mouse. + RefreshTabDisabledStates(); + for (int category = 0; category < CATEGORYCOUNT; ++category) { + if (!m_pCategoryTabs[category]->IsEnabled()) { + if (m_MenuCategory == category) { + m_pCategoryTabs[m_MenuCategory]->SetEnabled(true); + } + if (m_pCategoryTabs[category]->PointInside(mousePosX + 3, mousePosY)) { + if (m_pController->IsState(PRESS_PRIMARY)) { + m_MenuCategory = category; + m_pCategoryTabs[m_MenuCategory]->SetFocus(); + m_pCategoryTabs[m_MenuCategory]->SetEnabled(true); + CategoryChange(); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } else if (!m_pCategoryTabs[category]->IsEnabled() && !m_pCategoryTabs[category]->HasFocus()) { + m_pCategoryTabs[category]->SetFocus(); + } + } + } + } - ///////////////////////////////////////// - // SETS BUTTONS focus + ///////////////////////////////////////// + // SETS BUTTONS focus - if (m_MenuFocus == SETBUTTONS) - { - if (m_FocusChange) - { - // Set the correct special Sets category so the sets buttons show up - m_MenuCategory = SETS; - CategoryChange(); - m_pSaveButton->SetFocus(); - m_FocusChange = 0; - } + if (m_MenuFocus == SETBUTTONS) { + if (m_FocusChange) { + // Set the correct special Sets category so the sets buttons show up + m_MenuCategory = SETS; + CategoryChange(); + m_pSaveButton->SetFocus(); + m_FocusChange = 0; + } - if (m_pController->IsState(PRESS_FACEBUTTON)) - { - if (m_pSaveButton->HasFocus()) - SaveCurrentLoadout(); - else if (m_pClearButton->HasFocus() && m_Loadouts.size() != 0) - { - m_Loadouts.pop_back(); - // Update the list of loadout presets so the removal shows up - CategoryChange(); - // Set focus back on the save button (CatChange changed it) - m_pClearButton->SetFocus(); - } - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); - } + if (m_pController->IsState(PRESS_FACEBUTTON)) { + if (m_pSaveButton->HasFocus()) + SaveCurrentLoadout(); + else if (m_pClearButton->HasFocus() && m_Loadouts.size() != 0) { + m_Loadouts.pop_back(); + // Update the list of loadout presets so the removal shows up + CategoryChange(); + // Set focus back on the save button (CatChange changed it) + m_pClearButton->SetFocus(); + } + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + } - // Switch back focus to the category list if the player presses up while on the save button - if (pressUp) - { - if (m_pSaveButton->HasFocus()) - { - m_MenuFocus = CATEGORIES; - m_FocusChange = 1; - } - else if (m_pClearButton->HasFocus()) - { - m_pSaveButton->SetFocus(); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - } - else if (pressDown) - { - if (m_pSaveButton->HasFocus()) - { - m_pClearButton->SetFocus(); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - else if (m_pClearButton->HasFocus()) - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - } + // Switch back focus to the category list if the player presses up while on the save button + if (pressUp) { + if (m_pSaveButton->HasFocus()) { + m_MenuFocus = CATEGORIES; + m_FocusChange = 1; + } else if (m_pClearButton->HasFocus()) { + m_pSaveButton->SetFocus(); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } + } else if (pressDown) { + if (m_pSaveButton->HasFocus()) { + m_pClearButton->SetFocus(); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } else if (m_pClearButton->HasFocus()) + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + } - ///////////////////////////////////////// - // CATEGORIES focus + ///////////////////////////////////////// + // CATEGORIES focus - else if (m_MenuFocus == CATEGORIES) { - if (m_FocusChange) { - m_pCategoryTabs[m_MenuCategory]->SetFocus(); - m_FocusChange = 0; - } + else if (m_MenuFocus == CATEGORIES) { + if (m_FocusChange) { + m_pCategoryTabs[m_MenuCategory]->SetFocus(); + m_FocusChange = 0; + } - if (pressDown) { - m_MenuCategory++; - if (m_MenuCategory >= CATEGORYCOUNT) { - m_MenuCategory = CATEGORYCOUNT - 1; - m_MenuFocus = SETBUTTONS; - m_FocusChange = -1; - } else { - CategoryChange(); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - } else if (pressUp) { - m_MenuCategory--; - if (m_MenuCategory < 0) { - m_MenuCategory = 0; - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } else { - CategoryChange(); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - } - } + if (pressDown) { + m_MenuCategory++; + if (m_MenuCategory >= CATEGORYCOUNT) { + m_MenuCategory = CATEGORYCOUNT - 1; + m_MenuFocus = SETBUTTONS; + m_FocusChange = -1; + } else { + CategoryChange(); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } + } else if (pressUp) { + m_MenuCategory--; + if (m_MenuCategory < 0) { + m_MenuCategory = 0; + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } else { + CategoryChange(); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } + } + } - ///////////////////////////////////////// - // ITEMS LIST focus + ///////////////////////////////////////// + // ITEMS LIST focus + + else if (m_MenuFocus == ITEMS) { + if (m_FocusChange) { + m_pShopList->SetFocus(); + // Select the top one in the item list if none is already selected + if (!m_pShopList->GetItemList()->empty() && m_pShopList->GetSelectedIndex() < 0) + m_pShopList->SetSelectedIndex(m_ListItemIndex = 0); + // Synch our index with the one already sleected in the list + else { + m_ListItemIndex = m_pShopList->GetSelectedIndex(); + m_pShopList->ScrollToSelected(); + } - else if (m_MenuFocus == ITEMS) - { - if (m_FocusChange) - { - m_pShopList->SetFocus(); - // Select the top one in the item list if none is already selected - if (!m_pShopList->GetItemList()->empty() && m_pShopList->GetSelectedIndex() < 0) - m_pShopList->SetSelectedIndex(m_ListItemIndex = 0); - // Synch our index with the one already sleected in the list - else - { - m_ListItemIndex = m_pShopList->GetSelectedIndex(); - m_pShopList->ScrollToSelected(); - } + m_FocusChange = 0; + } - m_FocusChange = 0; - } + int listSize = m_pShopList->GetItemList()->size(); + if (pressDown) { + m_ListItemIndex++; + // Loop around + if (m_ListItemIndex >= listSize) + m_ListItemIndex = 0; + + // Update the selected shop item index + m_CategoryItemIndex[m_MenuCategory] = m_ListItemIndex; + m_pShopList->SetSelectedIndex(m_ListItemIndex); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } else if (pressUp) { + m_ListItemIndex--; + // Loop around + if (m_ListItemIndex < 0) + m_ListItemIndex = listSize - 1; + + // Update the selected shop item index + m_CategoryItemIndex[m_MenuCategory] = m_ListItemIndex; + m_pShopList->SetSelectedIndex(m_ListItemIndex); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } - int listSize = m_pShopList->GetItemList()->size(); - if (pressDown) - { - m_ListItemIndex++; - // Loop around - if (m_ListItemIndex >= listSize) - m_ListItemIndex = 0; - - // Update the selected shop item index - m_CategoryItemIndex[m_MenuCategory] = m_ListItemIndex; - m_pShopList->SetSelectedIndex(m_ListItemIndex); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - else if (pressUp) - { - m_ListItemIndex--; - // Loop around - if (m_ListItemIndex < 0) - m_ListItemIndex = listSize - 1; - - // Update the selected shop item index - m_CategoryItemIndex[m_MenuCategory] = m_ListItemIndex; - m_pShopList->SetSelectedIndex(m_ListItemIndex); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } + // Get handle to the currently selected item, if any + GUIListPanel::Item* pItem = m_pShopList->GetItem(m_ListItemIndex); + std::string description = ""; - // Get handle to the currently selected item, if any - GUIListPanel::Item *pItem = m_pShopList->GetItem(m_ListItemIndex); - std::string description = ""; - - if (pItem && pItem->m_pEntity) { - description = ((pItem->m_pEntity->GetDescription().empty()) ? "-No Information Found-": pItem->m_pEntity->GetDescription()) + "\n"; - const Entity *currentItem = pItem->m_pEntity; - const ACraft *itemAsCraft = dynamic_cast(currentItem); - if (itemAsCraft) { - int craftMaxPassengers = itemAsCraft->GetMaxPassengers(); - float craftMaxMass = itemAsCraft->GetMaxInventoryMass(); - if (craftMaxMass == 0) { - description += "\nNO CARGO SPACE!"; - } else if (craftMaxMass > 0) { - description += "\nMax Mass: " + RoundFloatToPrecision(craftMaxMass, craftMaxMass < 50.0F ? 1 : 0, 3) + " kg"; - } - if (craftMaxPassengers >= 0 && craftMaxMass != 0) { description += (craftMaxPassengers == 0) ? "\nNO PASSENGER SPACE!" : "\nMax Passengers: " + std::to_string(craftMaxPassengers); } - } else { + if (pItem && pItem->m_pEntity) { + description = ((pItem->m_pEntity->GetDescription().empty()) ? "-No Information Found-" : pItem->m_pEntity->GetDescription()) + "\n"; + const Entity* currentItem = pItem->m_pEntity; + const ACraft* itemAsCraft = dynamic_cast(currentItem); + if (itemAsCraft) { + int craftMaxPassengers = itemAsCraft->GetMaxPassengers(); + float craftMaxMass = itemAsCraft->GetMaxInventoryMass(); + if (craftMaxMass == 0) { + description += "\nNO CARGO SPACE!"; + } else if (craftMaxMass > 0) { + description += "\nMax Mass: " + RoundFloatToPrecision(craftMaxMass, craftMaxMass < 50.0F ? 1 : 0, 3) + " kg"; + } + if (craftMaxPassengers >= 0 && craftMaxMass != 0) { + description += (craftMaxPassengers == 0) ? "\nNO PASSENGER SPACE!" : "\nMax Passengers: " + std::to_string(craftMaxPassengers); + } + } else { // Items in the BuyMenu always have any remainder rounded up in their masses. - const Actor *itemAsActor = dynamic_cast(currentItem); - if (itemAsActor) { + const Actor* itemAsActor = dynamic_cast(currentItem); + if (itemAsActor) { description += "\nMass: " + (itemAsActor->GetMass() < 0.1F ? "<0.1 kg" : RoundFloatToPrecision(itemAsActor->GetMass(), itemAsActor->GetMass() < 50.0F ? 1 : 0, 3) + " kg"); - int passengerSlotsTaken = itemAsActor->GetPassengerSlots(); - if (passengerSlotsTaken > 1) { - description += "\nPassenger Slots: " + std::to_string(passengerSlotsTaken); - } - } else { - const MovableObject *itemAsMO = dynamic_cast(currentItem); - if (itemAsMO) { - const MOSRotating *itemAsMOSRotating = dynamic_cast(currentItem); + int passengerSlotsTaken = itemAsActor->GetPassengerSlots(); + if (passengerSlotsTaken > 1) { + description += "\nPassenger Slots: " + std::to_string(passengerSlotsTaken); + } + } else { + const MovableObject* itemAsMO = dynamic_cast(currentItem); + if (itemAsMO) { + const MOSRotating* itemAsMOSRotating = dynamic_cast(currentItem); float extraMass = 0; if (itemAsMOSRotating) { if (itemAsMOSRotating->NumberValueExists("Grenade Count")) { @@ -1412,36 +1307,36 @@ void BuyMenuGUI::Update() extraMass = itemAsMOSRotating->GetNumberValue("Belt Mass"); } } - description += "\nMass: " + (itemAsMO->GetMass() + extraMass < 0.1F ? "<0.1 kg" : RoundFloatToPrecision(itemAsMO->GetMass() + extraMass, itemAsMO->GetMass() + extraMass < 50.0F ? 1 : 0, 3) + " kg"); - } - } - } - } else if (pItem && pItem->m_ExtraIndex >= 0) { - const DataModule *pModule = g_PresetMan.GetDataModule(pItem->m_ExtraIndex); - if (pModule && !pModule->GetDescription().empty()) { - description = pModule->GetDescription(); - } - } + description += "\nMass: " + (itemAsMO->GetMass() + extraMass < 0.1F ? "<0.1 kg" : RoundFloatToPrecision(itemAsMO->GetMass() + extraMass, itemAsMO->GetMass() + extraMass < 50.0F ? 1 : 0, 3) + " kg"); + } + } + } + } else if (pItem && pItem->m_ExtraIndex >= 0) { + const DataModule* pModule = g_PresetMan.GetDataModule(pItem->m_ExtraIndex); + if (pModule && !pModule->GetDescription().empty()) { + description = pModule->GetDescription(); + } + } - // Show popup info box next to selected item if it has a description or tooltip. - if (!description.empty()) { - // Show the popup box with the hovered item's description - m_pPopupBox->SetVisible(true); - // Need to add an offset to make it look better and not have the cursor obscure text - m_pPopupBox->SetPositionAbs(m_pShopList->GetXPos() - 6 + m_pShopList->GetWidth(), m_pShopList->GetYPos() + m_pShopList->GetStackHeight(pItem) - m_pShopList->GetScrollVerticalValue()); - // Make sure the popup box doesn't drop out of sight - if (m_pPopupBox->GetYPos() + m_pPopupBox->GetHeight() > m_pParentBox->GetHeight()) - m_pPopupBox->SetPositionAbs(m_pPopupBox->GetXPos(), m_pParentBox->GetHeight() - m_pPopupBox->GetHeight()); - m_pPopupText->SetHAlignment(GUIFont::Left); - m_pPopupText->SetText(description); - // Resize the box height to fit the text - int newHeight = m_pPopupText->ResizeHeightToFit(); - m_pPopupBox->Resize(m_pPopupBox->GetWidth(), newHeight + 10); - } + // Show popup info box next to selected item if it has a description or tooltip. + if (!description.empty()) { + // Show the popup box with the hovered item's description + m_pPopupBox->SetVisible(true); + // Need to add an offset to make it look better and not have the cursor obscure text + m_pPopupBox->SetPositionAbs(m_pShopList->GetXPos() - 6 + m_pShopList->GetWidth(), m_pShopList->GetYPos() + m_pShopList->GetStackHeight(pItem) - m_pShopList->GetScrollVerticalValue()); + // Make sure the popup box doesn't drop out of sight + if (m_pPopupBox->GetYPos() + m_pPopupBox->GetHeight() > m_pParentBox->GetHeight()) + m_pPopupBox->SetPositionAbs(m_pPopupBox->GetXPos(), m_pParentBox->GetHeight() - m_pPopupBox->GetHeight()); + m_pPopupText->SetHAlignment(GUIFont::Left); + m_pPopupText->SetText(description); + // Resize the box height to fit the text + int newHeight = m_pPopupText->ResizeHeightToFit(); + m_pPopupBox->Resize(m_pPopupBox->GetWidth(), newHeight + 10); + } - // User selected to add an item to cart list! - if (m_pController->IsState(PRESS_FACEBUTTON)) { - // User pressed on a module group item; toggle its expansion! + // User selected to add an item to cart list! + if (m_pController->IsState(PRESS_FACEBUTTON)) { + // User pressed on a module group item; toggle its expansion! if (pItem && pItem->m_ExtraIndex >= 0) { // Make appropriate sound if (!m_aExpandedModules[pItem->m_ExtraIndex]) { @@ -1455,159 +1350,154 @@ void BuyMenuGUI::Update() // Re-populate the item list with the new module expansion configuation CategoryChange(false); } - // User pressed on a loadout set, so load it into the menu - else if (pItem && m_MenuCategory == SETS) { - // Beep if there's an error - if (!DeployLoadout(m_ListItemIndex)) - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - // User mashed button on a regular shop item, add it to cargo, or select craft - else if (pItem && pItem->m_pEntity) - { - // Select the craft - if (m_MenuCategory == CRAFT) - { - if (m_pSelectedCraft = dynamic_cast(pItem->m_pEntity)) - { - m_pCraftBox->SetText(pItem->m_Name); - m_pCraftBox->SetRightText(pItem->m_RightText); + // User pressed on a loadout set, so load it into the menu + else if (pItem && m_MenuCategory == SETS) { + // Beep if there's an error + if (!DeployLoadout(m_ListItemIndex)) + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + // User mashed button on a regular shop item, add it to cargo, or select craft + else if (pItem && pItem->m_pEntity) { + // Select the craft + if (m_MenuCategory == CRAFT) { + if (m_pSelectedCraft = dynamic_cast(pItem->m_pEntity)) { + m_pCraftBox->SetText(pItem->m_Name); + m_pCraftBox->SetRightText(pItem->m_RightText); m_pCraftNameLabel->SetText(pItem->m_Name); m_pCraftPriceLabel->SetText(pItem->m_RightText); - UpdateTotalPassengersLabel(dynamic_cast(pItem->m_pEntity), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(pItem->m_pEntity), m_pCraftMassLabel); + UpdateTotalPassengersLabel(dynamic_cast(pItem->m_pEntity), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(pItem->m_pEntity), m_pCraftMassLabel); } - } - // Regular ship inventory - else - { - // Gotto make a copy of the bitmap to pass it to the next list - GUIBitmap *pItemBitmap = new AllegroBitmap(dynamic_cast(pItem->m_pBitmap)->GetBitmap()); - AddCartItem(pItem->m_Name, pItem->m_RightText, pItemBitmap, pItem->m_pEntity); - // If I just selected an AHuman, enable equipment selection mode - if (m_MenuCategory == BODIES && pItem->m_pEntity->GetClassName() == "AHuman") - { - EnableEquipmentSelection(true); - } - } - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); - } - - UpdateTotalCostLabel(m_pController->GetTeam()); - - UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); - } - } + } + // Regular ship inventory + else { + // Gotto make a copy of the bitmap to pass it to the next list + GUIBitmap* pItemBitmap = new AllegroBitmap(dynamic_cast(pItem->m_pBitmap)->GetBitmap()); + AddCartItem(pItem->m_Name, pItem->m_RightText, pItemBitmap, pItem->m_pEntity); + // If I just selected an AHuman, enable equipment selection mode + if (m_MenuCategory == BODIES && pItem->m_pEntity->GetClassName() == "AHuman") { + EnableEquipmentSelection(true); + } + } + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + } - ///////////////////////////////////////// - // CART/ORDER LIST focus - - else if (m_MenuFocus == ORDER) { - // Changed to the list, so select the top one in the item list - if (m_FocusChange) { - m_pCartList->SetFocus(); - if (!m_pCartList->GetItemList()->empty() && m_pCartList->GetSelectedIndex() < 0) { - m_pCartList->SetSelectedIndex(m_ListItemIndex = 0); - // Synch our index with the one already selected in the list - } else { - m_ListItemIndex = m_pCartList->GetSelectedIndex(); - m_pCartList->ScrollToSelected(); - } - - m_FocusChange = 0; - } + UpdateTotalCostLabel(m_pController->GetTeam()); + + UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); + } + } + + ///////////////////////////////////////// + // CART/ORDER LIST focus + + else if (m_MenuFocus == ORDER) { + // Changed to the list, so select the top one in the item list + if (m_FocusChange) { + m_pCartList->SetFocus(); + if (!m_pCartList->GetItemList()->empty() && m_pCartList->GetSelectedIndex() < 0) { + m_pCartList->SetSelectedIndex(m_ListItemIndex = 0); + // Synch our index with the one already selected in the list + } else { + m_ListItemIndex = m_pCartList->GetSelectedIndex(); + m_pCartList->ScrollToSelected(); + } + + m_FocusChange = 0; + } - bool itemsChanged = false; - - int listSize = m_pCartList->GetItemList()->size(); - if (m_DraggedItemIndex != -1) { - if (pressDown && m_DraggedItemIndex < listSize - 1) { - m_IsDragging = true; - itemsChanged = true; - std::swap((*m_pCartList->GetItemList())[m_DraggedItemIndex], (*m_pCartList->GetItemList())[m_DraggedItemIndex + 1]); - std::swap((*m_pCartList->GetItemList())[m_DraggedItemIndex + 1]->m_ID, (*m_pCartList->GetItemList())[m_DraggedItemIndex]->m_ID); - m_ListItemIndex = ++m_DraggedItemIndex; - m_pCartList->SetSelectedIndex(m_ListItemIndex); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } else if (pressUp && m_DraggedItemIndex > 0) { - m_IsDragging = true; - itemsChanged = true; - std::swap((*m_pCartList->GetItemList())[m_DraggedItemIndex], (*m_pCartList->GetItemList())[m_DraggedItemIndex - 1]); - std::swap((*m_pCartList->GetItemList())[m_DraggedItemIndex - 1]->m_ID, (*m_pCartList->GetItemList())[m_DraggedItemIndex]->m_ID); - m_ListItemIndex = --m_DraggedItemIndex; - m_pCartList->SetSelectedIndex(m_ListItemIndex); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - } else { - if (pressDown) { - m_ListItemIndex++; - if (m_ListItemIndex >= listSize) { - m_ListItemIndex = listSize - 1; - // If at the end of the list and the player presses down, then switch focus to the BUY button. - m_FocusChange = 1; - m_MenuFocus = OK; - } else { - // Only do list change logic if we actually did change. - m_pCartList->SetSelectedIndex(m_ListItemIndex); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - } else if (pressUp) { - m_ListItemIndex--; - if (m_ListItemIndex < 0) { - m_ListItemIndex = 0; + bool itemsChanged = false; + + int listSize = m_pCartList->GetItemList()->size(); + if (m_DraggedItemIndex != -1) { + if (pressDown && m_DraggedItemIndex < listSize - 1) { + m_IsDragging = true; + itemsChanged = true; + std::swap((*m_pCartList->GetItemList())[m_DraggedItemIndex], (*m_pCartList->GetItemList())[m_DraggedItemIndex + 1]); + std::swap((*m_pCartList->GetItemList())[m_DraggedItemIndex + 1]->m_ID, (*m_pCartList->GetItemList())[m_DraggedItemIndex]->m_ID); + m_ListItemIndex = ++m_DraggedItemIndex; + m_pCartList->SetSelectedIndex(m_ListItemIndex); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } else if (pressUp && m_DraggedItemIndex > 0) { + m_IsDragging = true; + itemsChanged = true; + std::swap((*m_pCartList->GetItemList())[m_DraggedItemIndex], (*m_pCartList->GetItemList())[m_DraggedItemIndex - 1]); + std::swap((*m_pCartList->GetItemList())[m_DraggedItemIndex - 1]->m_ID, (*m_pCartList->GetItemList())[m_DraggedItemIndex]->m_ID); + m_ListItemIndex = --m_DraggedItemIndex; + m_pCartList->SetSelectedIndex(m_ListItemIndex); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } + } else { + if (pressDown) { + m_ListItemIndex++; + if (m_ListItemIndex >= listSize) { + m_ListItemIndex = listSize - 1; + // If at the end of the list and the player presses down, then switch focus to the BUY button. + m_FocusChange = 1; + m_MenuFocus = OK; + } else { + // Only do list change logic if we actually did change. + m_pCartList->SetSelectedIndex(m_ListItemIndex); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } + } else if (pressUp) { + m_ListItemIndex--; + if (m_ListItemIndex < 0) { + m_ListItemIndex = 0; // If at the top of the list and the player presses up, then switch focus to the CLEAR button. m_FocusChange = 1; m_MenuFocus = MenuFocus::CLEARORDER; - } else { - // Only do list change logic if we actually did change. - m_pCartList->SetSelectedIndex(m_ListItemIndex); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - } - } + } else { + // Only do list change logic if we actually did change. + m_pCartList->SetSelectedIndex(m_ListItemIndex); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } + } + } - // Get handle to the currently selected item, if any - GUIListPanel::Item *pItem = m_pCartList->GetItem(m_ListItemIndex); - std::string description = ""; + // Get handle to the currently selected item, if any + GUIListPanel::Item* pItem = m_pCartList->GetItem(m_ListItemIndex); + std::string description = ""; - if (pItem && pItem->m_pEntity) { + if (pItem && pItem->m_pEntity) { description = ((pItem->m_pEntity->GetDescription().empty()) ? "-No Information Found-" : pItem->m_pEntity->GetDescription()) + "\n"; - const Entity *currentItem = pItem->m_pEntity; - const Actor *itemAsActor = dynamic_cast(currentItem); - if (itemAsActor) { + const Entity* currentItem = pItem->m_pEntity; + const Actor* itemAsActor = dynamic_cast(currentItem); + if (itemAsActor) { description += "\nMass: " + (itemAsActor->GetMass() < 0.1F ? "<0.1 kg" : RoundFloatToPrecision(itemAsActor->GetMass(), itemAsActor->GetMass() < 50.0F ? 1 : 0, 3) + " kg"); - int passengerSlotsTaken = itemAsActor->GetPassengerSlots(); - if (passengerSlotsTaken > 1) { - description += "\nPassenger Slots: " + std::to_string(passengerSlotsTaken); - } - } else { - const MovableObject *itemAsMO = dynamic_cast(currentItem); - if (itemAsMO) { + int passengerSlotsTaken = itemAsActor->GetPassengerSlots(); + if (passengerSlotsTaken > 1) { + description += "\nPassenger Slots: " + std::to_string(passengerSlotsTaken); + } + } else { + const MovableObject* itemAsMO = dynamic_cast(currentItem); + if (itemAsMO) { description += "\nMass: " + (itemAsMO->GetMass() < 0.1F ? "<0.1 kg" : RoundFloatToPrecision(itemAsMO->GetMass(), itemAsMO->GetMass() < 50.0F ? 1 : 0, 3) + " kg"); - } - } - } + } + } + } - if (!description.empty()) { - // Show the popup box with the hovered item's description - m_pPopupBox->SetVisible(true); - // Need to add an offset to make it look better and not have the cursor obscure text - m_pPopupBox->SetPositionAbs(m_pCartList->GetXPos() - m_pPopupBox->GetWidth() + 4, m_pCartList->GetYPos() + m_pCartList->GetStackHeight(pItem) - m_pCartList->GetScrollVerticalValue()); - // Make sure the popup box doesn't drop out of sight - if (m_pPopupBox->GetYPos() + m_pPopupBox->GetHeight() > m_pParentBox->GetHeight()) - m_pPopupBox->SetPositionAbs(m_pPopupBox->GetXPos(), m_pParentBox->GetHeight() - m_pPopupBox->GetHeight()); - m_pPopupText->SetHAlignment(GUIFont::Right); - m_pPopupText->SetText(description); - // Resize the box height to fit the text - int newHeight = m_pPopupText->ResizeHeightToFit(); - m_pPopupBox->Resize(m_pPopupBox->GetWidth(), newHeight + 10); - } + if (!description.empty()) { + // Show the popup box with the hovered item's description + m_pPopupBox->SetVisible(true); + // Need to add an offset to make it look better and not have the cursor obscure text + m_pPopupBox->SetPositionAbs(m_pCartList->GetXPos() - m_pPopupBox->GetWidth() + 4, m_pCartList->GetYPos() + m_pCartList->GetStackHeight(pItem) - m_pCartList->GetScrollVerticalValue()); + // Make sure the popup box doesn't drop out of sight + if (m_pPopupBox->GetYPos() + m_pPopupBox->GetHeight() > m_pParentBox->GetHeight()) + m_pPopupBox->SetPositionAbs(m_pPopupBox->GetXPos(), m_pParentBox->GetHeight() - m_pPopupBox->GetHeight()); + m_pPopupText->SetHAlignment(GUIFont::Right); + m_pPopupText->SetText(description); + // Resize the box height to fit the text + int newHeight = m_pPopupText->ResizeHeightToFit(); + m_pPopupBox->Resize(m_pPopupBox->GetWidth(), newHeight + 10); + } - // Fire button removes items from the order list, including equipment on AHumans + // Fire button removes items from the order list, including equipment on AHumans bool isKeyboardControlled = !m_pController->IsMouseControlled() && !m_pController->IsGamepadControlled(); - if (isKeyboardControlled ? (m_pController->IsState(PRESS_FACEBUTTON) && !m_pController->IsState(AIM_SHARP)) : (m_pController->IsState(RELEASE_FACEBUTTON) && !m_IsDragging)) { + if (isKeyboardControlled ? (m_pController->IsState(PRESS_FACEBUTTON) && !m_pController->IsState(AIM_SHARP)) : (m_pController->IsState(RELEASE_FACEBUTTON) && !m_IsDragging)) { if (g_UInputMan.FlagShiftState()) { ClearCartList(); pItem = nullptr; @@ -1616,12 +1506,13 @@ void BuyMenuGUI::Update() if (pItem && pItem->m_pEntity && pItem->m_pEntity->GetClassName() == "AHuman" && g_SettingsMan.SmartBuyMenuNavigationEnabled()) { int lastItemToDelete = m_pCartList->GetItemList()->size() - 1; for (int i = m_ListItemIndex + 1; i != m_pCartList->GetItemList()->size(); i++) { - GUIListPanel::Item *cartItem = m_pCartList->GetItem(i); - if (dynamic_cast(cartItem->m_pEntity)) { + GUIListPanel::Item* cartItem = m_pCartList->GetItem(i); + if (dynamic_cast(cartItem->m_pEntity)) { lastItemToDelete = i - 1; break; } - } for (int i = lastItemToDelete; i > m_ListItemIndex; i--) { + } + for (int i = lastItemToDelete; i > m_ListItemIndex; i--) { m_pCartList->DeleteItem(i); } } @@ -1637,422 +1528,381 @@ void BuyMenuGUI::Update() m_MenuFocus = ORDER; } } - } else if (m_pController->IsState(WEAPON_PICKUP)) { - itemsChanged = true; - DuplicateCartItem(m_ListItemIndex); - } + } else if (m_pController->IsState(WEAPON_PICKUP)) { + itemsChanged = true; + DuplicateCartItem(m_ListItemIndex); + } - if (isKeyboardControlled ? m_pController->IsState(AIM_SHARP) : m_pController->IsState(PRESS_FACEBUTTON)) { - m_DraggedItemIndex = m_pCartList->GetSelectedIndex(); - } else if (m_pController->IsState(RELEASE_FACEBUTTON)) { - m_DraggedItemIndex = -1; - m_IsDragging = false; - } + if (isKeyboardControlled ? m_pController->IsState(AIM_SHARP) : m_pController->IsState(PRESS_FACEBUTTON)) { + m_DraggedItemIndex = m_pCartList->GetSelectedIndex(); + } else if (m_pController->IsState(RELEASE_FACEBUTTON)) { + m_DraggedItemIndex = -1; + m_IsDragging = false; + } - if (itemsChanged) { - UpdateTotalCostLabel(m_pController->GetTeam()); - UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); + if (itemsChanged) { + UpdateTotalCostLabel(m_pController->GetTeam()); + UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); - UpdateItemNestingLevels(); + UpdateItemNestingLevels(); - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); - } - } + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + } + } ///////////////////////////////////////// // ORDER CLEAR BUTTON focus - else if (m_MenuFocus == MenuFocus::CLEARORDER) { - if (m_FocusChange) { - m_ClearOrderButton->SetFocus(); - m_FocusChange = 0; - } - if (m_pController->IsState(ControlState::PRESS_FACEBUTTON)) { - ClearCartList(); - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); - } - // Switch back focus to the order list if the player presses down - if (pressDown) { - m_MenuFocus = MenuFocus::ORDER; - m_FocusChange = -1; - } else if (pressUp) { - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - } - - ///////////////////////////////////////// - // OK BUTTON focus + else if (m_MenuFocus == MenuFocus::CLEARORDER) { + if (m_FocusChange) { + m_ClearOrderButton->SetFocus(); + m_FocusChange = 0; + } + if (m_pController->IsState(ControlState::PRESS_FACEBUTTON)) { + ClearCartList(); + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + } + // Switch back focus to the order list if the player presses down + if (pressDown) { + m_MenuFocus = MenuFocus::ORDER; + m_FocusChange = -1; + } else if (pressUp) { + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + } - else if (m_MenuFocus == OK) - { - if (m_FocusChange) - { - m_pBuyButton->SetFocus(); - m_FocusChange = 0; - } + ///////////////////////////////////////// + // OK BUTTON focus - if (m_pController->IsState(PRESS_FACEBUTTON)) - { - // Attempt to do the purchase - TryPurchase(); - } + else if (m_MenuFocus == OK) { + if (m_FocusChange) { + m_pBuyButton->SetFocus(); + m_FocusChange = 0; + } - // Switch back focus to the order list if the player presses up - if (pressUp) - { - m_MenuFocus = ORDER; - m_FocusChange = -1; - } - else if (pressDown) - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } + if (m_pController->IsState(PRESS_FACEBUTTON)) { + // Attempt to do the purchase + TryPurchase(); + } - if (m_MenuEnabled == ENABLED) { - if (m_pController->IsState(WEAPON_RELOAD)) { - TryPurchase(); - } else if (m_pController->IsMouseControlled() && (m_pController->IsState(PRESS_PRIMARY) || m_pController->IsState(PRESS_SECONDARY)) && mousePosX > m_pParentBox->GetWidth()) { - if (m_pController->IsState(PRESS_PRIMARY)) { - TryPurchase(); - } else { - SetEnabled(false); - } - } else if (m_pController->IsState(PRESS_SECONDARY) || g_UInputMan.AnyStartPress(false)) { - if (m_SelectingEquipment) { - EnableEquipmentSelection(false); - } else { - SetEnabled(false); - } - } - } + // Switch back focus to the order list if the player presses up + if (pressUp) { + m_MenuFocus = ORDER; + m_FocusChange = -1; + } else if (pressDown) + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + + if (m_MenuEnabled == ENABLED) { + if (m_pController->IsState(WEAPON_RELOAD)) { + TryPurchase(); + } else if (m_pController->IsMouseControlled() && (m_pController->IsState(PRESS_PRIMARY) || m_pController->IsState(PRESS_SECONDARY)) && mousePosX > m_pParentBox->GetWidth()) { + if (m_pController->IsState(PRESS_PRIMARY)) { + TryPurchase(); + } else { + SetEnabled(false); + } + } else if (m_pController->IsState(PRESS_SECONDARY) || g_UInputMan.AnyStartPress(false)) { + if (m_SelectingEquipment) { + EnableEquipmentSelection(false); + } else { + SetEnabled(false); + } + } + } - ////////////////////////////////////////// + ////////////////////////////////////////// // Update the ControlManager m_pGUIController->Update(); - - /////////////////////////////////////// - // Handle events + /////////////////////////////////////// + // Handle events GUIEvent anEvent; - while(m_pGUIController->GetEvent(&anEvent)) - { - if (anEvent.GetType() == GUIEvent::Command) - { - // SAVE button clicks - if(anEvent.GetControl() == m_pSaveButton) - { - m_pSaveButton->SetFocus(); - SaveCurrentLoadout(); - m_MenuFocus = SETBUTTONS; -// m_FocusChange = -1; - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + while (m_pGUIController->GetEvent(&anEvent)) { + if (anEvent.GetType() == GUIEvent::Command) { + // SAVE button clicks + if (anEvent.GetControl() == m_pSaveButton) { + m_pSaveButton->SetFocus(); + SaveCurrentLoadout(); + m_MenuFocus = SETBUTTONS; + // m_FocusChange = -1; + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); } - // CLEAR button clicks - if(anEvent.GetControl() == m_pClearButton) - { - m_pClearButton->SetFocus(); + // CLEAR button clicks + if (anEvent.GetControl() == m_pClearButton) { + m_pClearButton->SetFocus(); if (!m_Loadouts.empty()) m_Loadouts.pop_back(); - // Update the list of loadout presets so the removal shows up - CategoryChange(); - // Save new loadout config to file - SaveAllLoadoutsToFile(); - // Set focus back on the clear button (CatChange changed it) - m_pClearButton->SetFocus(); - m_MenuFocus = SETBUTTONS; -// m_FocusChange = -1; - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + // Update the list of loadout presets so the removal shows up + CategoryChange(); + // Save new loadout config to file + SaveAllLoadoutsToFile(); + // Set focus back on the clear button (CatChange changed it) + m_pClearButton->SetFocus(); + m_MenuFocus = SETBUTTONS; + // m_FocusChange = -1; + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); } - // BUY button clicks - if(anEvent.GetControl() == m_pBuyButton) - { - m_pBuyButton->SetFocus(); - TryPurchase(); -// g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + // BUY button clicks + if (anEvent.GetControl() == m_pBuyButton) { + m_pBuyButton->SetFocus(); + TryPurchase(); + // g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); } if (anEvent.GetControl() == m_ClearOrderButton) { ClearCartList(); g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); } - } - else if (anEvent.GetType() == GUIEvent::Notification) - { - ////////////////////////////////////////// + } else if (anEvent.GetType() == GUIEvent::Notification) { + ////////////////////////////////////////// // Clicks on any of the category tabs - for (int cat = 0; cat < CATEGORYCOUNT; ++cat) - { - if(anEvent.GetControl() == m_pCategoryTabs[cat]) - { - // Mouse hovering over - if(anEvent.GetMsg() == GUITab::Hovered) - { - // Just give focus to the categories column - m_MenuFocus = CATEGORIES; - m_pCategoryTabs[cat]->SetFocus(); - } - // Regular click - if(anEvent.GetMsg() == GUITab::Pushed) - { - m_MenuFocus = CATEGORIES; - m_MenuCategory = cat; - m_pCategoryTabs[m_MenuCategory]->SetFocus(); - CategoryChange(); - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - } - } - - ////////////////////////////////////////// + for (int cat = 0; cat < CATEGORYCOUNT; ++cat) { + if (anEvent.GetControl() == m_pCategoryTabs[cat]) { + // Mouse hovering over + if (anEvent.GetMsg() == GUITab::Hovered) { + // Just give focus to the categories column + m_MenuFocus = CATEGORIES; + m_pCategoryTabs[cat]->SetFocus(); + } + // Regular click + if (anEvent.GetMsg() == GUITab::Pushed) { + m_MenuFocus = CATEGORIES; + m_MenuCategory = cat; + m_pCategoryTabs[m_MenuCategory]->SetFocus(); + CategoryChange(); + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } + } + } + + ////////////////////////////////////////// // Events on the Shop List - if(anEvent.GetControl() == m_pShopList) - { - if (anEvent.GetMsg() == GUIListBox::MouseDown && (anEvent.GetData() & GUIListBox::MOUSE_LEFT)) - { - m_pShopList->SetFocus(); - m_MenuFocus = ITEMS; - - GUIListPanel::Item *pItem = m_pShopList->GetItem(mousePosX, mousePosY); - - // If a module group list item, toggle its expansion and update the list - if (pItem && pItem->m_ExtraIndex >= 0) - { - // Make appropriate sound - if (!m_aExpandedModules[pItem->m_ExtraIndex]) - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); - // Different, maybe? - else - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); - // Toggle the expansion of the module group item's items below - m_aExpandedModules[pItem->m_ExtraIndex] = !m_aExpandedModules[pItem->m_ExtraIndex]; - // Re-populate the item list with the new module expansion configuation - CategoryChange(false); - } - // Special case: user clicked on a loadout set, so load it into the menu - else if (pItem && m_MenuCategory == SETS) - { - // Beep if there's an error - if (!DeployLoadout(m_ListItemIndex)) - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - // Normal: only add an item if there's an entity attached to the list item - else if (pItem && pItem->m_pEntity) - { - m_CategoryItemIndex[m_MenuCategory] = m_ListItemIndex = m_pShopList->GetSelectedIndex(); - m_pShopList->ScrollToSelected(); - - // Select the craft - if (m_MenuCategory == CRAFT) - { - if (m_pSelectedCraft = dynamic_cast(pItem->m_pEntity)) - { - m_pCraftBox->SetText(pItem->m_Name); - m_pCraftBox->SetRightText(pItem->m_RightText); + if (anEvent.GetControl() == m_pShopList) { + if (anEvent.GetMsg() == GUIListBox::MouseDown && (anEvent.GetData() & GUIListBox::MOUSE_LEFT)) { + m_pShopList->SetFocus(); + m_MenuFocus = ITEMS; + + GUIListPanel::Item* pItem = m_pShopList->GetItem(mousePosX, mousePosY); + + // If a module group list item, toggle its expansion and update the list + if (pItem && pItem->m_ExtraIndex >= 0) { + // Make appropriate sound + if (!m_aExpandedModules[pItem->m_ExtraIndex]) + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + // Different, maybe? + else + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + // Toggle the expansion of the module group item's items below + m_aExpandedModules[pItem->m_ExtraIndex] = !m_aExpandedModules[pItem->m_ExtraIndex]; + // Re-populate the item list with the new module expansion configuation + CategoryChange(false); + } + // Special case: user clicked on a loadout set, so load it into the menu + else if (pItem && m_MenuCategory == SETS) { + // Beep if there's an error + if (!DeployLoadout(m_ListItemIndex)) + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + // Normal: only add an item if there's an entity attached to the list item + else if (pItem && pItem->m_pEntity) { + m_CategoryItemIndex[m_MenuCategory] = m_ListItemIndex = m_pShopList->GetSelectedIndex(); + m_pShopList->ScrollToSelected(); + + // Select the craft + if (m_MenuCategory == CRAFT) { + if (m_pSelectedCraft = dynamic_cast(pItem->m_pEntity)) { + m_pCraftBox->SetText(pItem->m_Name); + m_pCraftBox->SetRightText(pItem->m_RightText); m_pCraftNameLabel->SetText(pItem->m_Name); m_pCraftPriceLabel->SetText(pItem->m_RightText); - UpdateTotalPassengersLabel(dynamic_cast(pItem->m_pEntity), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(pItem->m_pEntity), m_pCraftMassLabel); + UpdateTotalPassengersLabel(dynamic_cast(pItem->m_pEntity), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(pItem->m_pEntity), m_pCraftMassLabel); } - } - // Regular ship inventory - else - { - // Gotto make a copy of the bitmap to pass it to the next list - GUIBitmap *pItemBitmap = new AllegroBitmap(dynamic_cast(pItem->m_pBitmap)->GetBitmap()); - - if (m_OwnedItems.size() > 0 || m_OnlyShowOwnedItems) - { - if (GetOwnedItemsAmount(pItem->m_pEntity->GetModuleAndPresetName()) > 0) - { + } + // Regular ship inventory + else { + // Gotto make a copy of the bitmap to pass it to the next list + GUIBitmap* pItemBitmap = new AllegroBitmap(dynamic_cast(pItem->m_pBitmap)->GetBitmap()); + + if (m_OwnedItems.size() > 0 || m_OnlyShowOwnedItems) { + if (GetOwnedItemsAmount(pItem->m_pEntity->GetModuleAndPresetName()) > 0) { AddCartItem(pItem->m_Name, "1 pc", pItemBitmap, pItem->m_pEntity); - } - else - { - if (m_OnlyShowOwnedItems) - { + } else { + if (m_OnlyShowOwnedItems) { if (IsAlwaysAllowedItem(pItem->m_Name)) AddCartItem(pItem->m_Name, pItem->m_RightText, pItemBitmap, pItem->m_pEntity); - } - else - { + } else { AddCartItem(pItem->m_Name, pItem->m_RightText, pItemBitmap, pItem->m_pEntity); } } - } - else - { + } else { AddCartItem(pItem->m_Name, pItem->m_RightText, pItemBitmap, pItem->m_pEntity); } - // If I just selected an AHuman, enable equipment selection mode - if (m_MenuCategory == BODIES && pItem->m_pEntity->GetClassName() == "AHuman") - { - EnableEquipmentSelection(true); - } - } - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); - } - // Undo the click deselection if nothing was selected -// else -// m_pShopList->SetSelectedIndex(m_SelectedObjectIndex); - - UpdateTotalCostLabel(m_pController->GetTeam()); - - UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); + // If I just selected an AHuman, enable equipment selection mode + if (m_MenuCategory == BODIES && pItem->m_pEntity->GetClassName() == "AHuman") { + EnableEquipmentSelection(true); + } + } + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + } + // Undo the click deselection if nothing was selected + // else + // m_pShopList->SetSelectedIndex(m_SelectedObjectIndex); + + UpdateTotalCostLabel(m_pController->GetTeam()); + + UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); } - // Mouse moved over the panel, show the popup with item description - else if(anEvent.GetMsg() == GUIListBox::MouseMove) - { - // Mouse is moving within the list, so make it focus on the list - m_pShopList->SetFocus(); - m_MenuFocus = ITEMS; - - // See if it's hovering over any item - GUIListPanel::Item *pItem = m_pShopList->GetItem(mousePosX, mousePosY); - if (pItem) - { - // Don't let mouse movement change the index if it's still hovering inside the same item. - // This is to avoid erratic selection curosr if using both mouse and keyboard to work the menu - if (m_LastHoveredMouseIndex != pItem->m_ID) - { - m_LastHoveredMouseIndex = pItem->m_ID; - - // Play select sound if new index - if (m_ListItemIndex != pItem->m_ID) - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - // Update the seleciton in both the GUI control and our menu - m_pShopList->SetSelectedIndex(m_CategoryItemIndex[m_MenuCategory] = m_ListItemIndex = pItem->m_ID); - } - } - } - } - - /////////////////////////////////////////////// - // Events on the Cart List - - else if (anEvent.GetControl() == m_pCartList) - { - if (anEvent.GetMsg() == GUIListBox::MouseUp && !m_IsDragging) { - GUIListPanel::Item *pItem = m_pCartList->GetSelected(); - if (pItem) - { - if (anEvent.GetData() & GUIListBox::MOUSE_LEFT) { - //TODO in future it would be nice to add the concept of a modifier key to Controller, so we can do this for gamepad inputs as well. + // Mouse moved over the panel, show the popup with item description + else if (anEvent.GetMsg() == GUIListBox::MouseMove) { + // Mouse is moving within the list, so make it focus on the list + m_pShopList->SetFocus(); + m_MenuFocus = ITEMS; + + // See if it's hovering over any item + GUIListPanel::Item* pItem = m_pShopList->GetItem(mousePosX, mousePosY); + if (pItem) { + // Don't let mouse movement change the index if it's still hovering inside the same item. + // This is to avoid erratic selection curosr if using both mouse and keyboard to work the menu + if (m_LastHoveredMouseIndex != pItem->m_ID) { + m_LastHoveredMouseIndex = pItem->m_ID; + + // Play select sound if new index + if (m_ListItemIndex != pItem->m_ID) + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + // Update the seleciton in both the GUI control and our menu + m_pShopList->SetSelectedIndex(m_CategoryItemIndex[m_MenuCategory] = m_ListItemIndex = pItem->m_ID); + } + } + } + } + + /////////////////////////////////////////////// + // Events on the Cart List + + else if (anEvent.GetControl() == m_pCartList) { + if (anEvent.GetMsg() == GUIListBox::MouseUp && !m_IsDragging) { + GUIListPanel::Item* pItem = m_pCartList->GetSelected(); + if (pItem) { + if (anEvent.GetData() & GUIListBox::MOUSE_LEFT) { + // TODO in future it would be nice to add the concept of a modifier key to Controller, so we can do this for gamepad inputs as well. if (g_UInputMan.FlagShiftState()) { - ClearCartList(); - pItem = nullptr; - } - - if (pItem && pItem->m_pEntity && pItem->m_pEntity->GetClassName() == "AHuman" && g_SettingsMan.SmartBuyMenuNavigationEnabled()) { - int lastItemToDelete = m_pCartList->GetItemList()->size() - 1; - for (int i = m_ListItemIndex + 1; i != m_pCartList->GetItemList()->size(); i++) { - GUIListPanel::Item *cartItem = m_pCartList->GetItem(i); - if (!cartItem || dynamic_cast(cartItem->m_pEntity)) { - lastItemToDelete = i - 1; - break; - } - } for (int i = lastItemToDelete; i > m_ListItemIndex; i--) { - m_pCartList->DeleteItem(i); - } - } - m_pCartList->DeleteItem(m_ListItemIndex); - // If we're not at the bottom, then select the item in the same place as the one just deleted - if (m_pCartList->GetItemList()->size() > m_ListItemIndex) { - m_pCartList->SetSelectedIndex(m_ListItemIndex); - // If we're not at the top, then move selection up one - } else if (m_ListItemIndex > 0) { - m_pCartList->SetSelectedIndex(--m_ListItemIndex); - // Shift focus back to the item list - } else { - m_MenuFocus = ORDER; - } - } else if (anEvent.GetData() & GUIListBox::MOUSE_MIDDLE) { - DuplicateCartItem(m_ListItemIndex); - } - - UpdateTotalCostLabel(m_pController->GetTeam()); - UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); - UpdateItemNestingLevels(); - - g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); - } + ClearCartList(); + pItem = nullptr; + } + + if (pItem && pItem->m_pEntity && pItem->m_pEntity->GetClassName() == "AHuman" && g_SettingsMan.SmartBuyMenuNavigationEnabled()) { + int lastItemToDelete = m_pCartList->GetItemList()->size() - 1; + for (int i = m_ListItemIndex + 1; i != m_pCartList->GetItemList()->size(); i++) { + GUIListPanel::Item* cartItem = m_pCartList->GetItem(i); + if (!cartItem || dynamic_cast(cartItem->m_pEntity)) { + lastItemToDelete = i - 1; + break; + } + } + for (int i = lastItemToDelete; i > m_ListItemIndex; i--) { + m_pCartList->DeleteItem(i); + } + } + m_pCartList->DeleteItem(m_ListItemIndex); + // If we're not at the bottom, then select the item in the same place as the one just deleted + if (m_pCartList->GetItemList()->size() > m_ListItemIndex) { + m_pCartList->SetSelectedIndex(m_ListItemIndex); + // If we're not at the top, then move selection up one + } else if (m_ListItemIndex > 0) { + m_pCartList->SetSelectedIndex(--m_ListItemIndex); + // Shift focus back to the item list + } else { + m_MenuFocus = ORDER; + } + } else if (anEvent.GetData() & GUIListBox::MOUSE_MIDDLE) { + DuplicateCartItem(m_ListItemIndex); + } + + UpdateTotalCostLabel(m_pController->GetTeam()); + UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); + UpdateItemNestingLevels(); + + g_GUISound.ItemChangeSound()->Play(m_pController->GetPlayer()); + } } - // Mouse moved over the panel, show the popup with item description - else if(anEvent.GetMsg() == GUIListBox::MouseMove) - { - // Mouse is moving within the list, so make it focus on the list - m_pCartList->SetFocus(); - m_MenuFocus = ORDER; - - // See if it's hovering over any item - GUIListPanel::Item *pItem = m_pCartList->GetItem(mousePosX, mousePosY); - if (pItem) - { - // Don't let mouse movement change the index if it's still hovering inside the same item. - // This is to avoid erratic selection curosr if using both mouse and keyboard to work the menu - if (m_LastHoveredMouseIndex != pItem->m_ID) { - if (m_DraggedItemIndex != -1 && m_DraggedItemIndex != pItem->m_ID) { - m_IsDragging = true; - int start = std::min(m_DraggedItemIndex, pItem->m_ID); - int end = std::max(m_DraggedItemIndex, pItem->m_ID); - int direction = pItem->m_ID > m_DraggedItemIndex ? 1 : -1; - for (int i = start; i < end; i++) { - int oldIndex = m_DraggedItemIndex; - if (oldIndex + direction < 0 || oldIndex + direction >= m_pCartList->GetItemList()->size()) { - break; - } - - m_DraggedItemIndex = oldIndex + direction; - std::swap((*m_pCartList->GetItemList())[oldIndex], (*m_pCartList->GetItemList())[oldIndex + direction]); - std::swap((*m_pCartList->GetItemList())[oldIndex + direction]->m_ID, (*m_pCartList->GetItemList())[oldIndex]->m_ID); - } - UpdateItemNestingLevels(); - } - - m_LastHoveredMouseIndex = pItem->m_ID; - // Play select sound if new index - if (m_ListItemIndex != pItem->m_ID) { - g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); - } - - // Update the selection in both the GUI control and our menu - m_pCartList->SetSelectedIndex(m_ListItemIndex = pItem->m_ID); - } - } - } - - if (anEvent.GetMsg() == GUIListBox::MouseDown) { - m_pCartList->SetFocus(); - m_MenuFocus = ORDER; - m_ListItemIndex = m_pCartList->GetSelectedIndex(); - m_pCartList->ScrollToSelected(); - if (anEvent.GetData() & GUIListBox::MOUSE_LEFT) { - m_DraggedItemIndex = m_pCartList->GetSelectedIndex(); - } - } - } - - // We do this down here, outside the m_pCartList control, because if we have a mouse-up event even outside the cart, we should stop dragging. We also check UInputMan in case the mouse is released entirely outside of the buy menu. - if ((anEvent.GetMsg() == GUIListBox::MouseUp && (anEvent.GetData() & GUIListBox::MOUSE_LEFT)) || g_UInputMan.MouseButtonReleased(MouseButtons::MOUSE_LEFT, m_pController->GetPlayer())) { - m_DraggedItemIndex = -1; - m_IsDragging = false; - } - } - } + // Mouse moved over the panel, show the popup with item description + else if (anEvent.GetMsg() == GUIListBox::MouseMove) { + // Mouse is moving within the list, so make it focus on the list + m_pCartList->SetFocus(); + m_MenuFocus = ORDER; + + // See if it's hovering over any item + GUIListPanel::Item* pItem = m_pCartList->GetItem(mousePosX, mousePosY); + if (pItem) { + // Don't let mouse movement change the index if it's still hovering inside the same item. + // This is to avoid erratic selection curosr if using both mouse and keyboard to work the menu + if (m_LastHoveredMouseIndex != pItem->m_ID) { + if (m_DraggedItemIndex != -1 && m_DraggedItemIndex != pItem->m_ID) { + m_IsDragging = true; + int start = std::min(m_DraggedItemIndex, pItem->m_ID); + int end = std::max(m_DraggedItemIndex, pItem->m_ID); + int direction = pItem->m_ID > m_DraggedItemIndex ? 1 : -1; + for (int i = start; i < end; i++) { + int oldIndex = m_DraggedItemIndex; + if (oldIndex + direction < 0 || oldIndex + direction >= m_pCartList->GetItemList()->size()) { + break; + } + + m_DraggedItemIndex = oldIndex + direction; + std::swap((*m_pCartList->GetItemList())[oldIndex], (*m_pCartList->GetItemList())[oldIndex + direction]); + std::swap((*m_pCartList->GetItemList())[oldIndex + direction]->m_ID, (*m_pCartList->GetItemList())[oldIndex]->m_ID); + } + UpdateItemNestingLevels(); + } + + m_LastHoveredMouseIndex = pItem->m_ID; + // Play select sound if new index + if (m_ListItemIndex != pItem->m_ID) { + g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer()); + } + + // Update the selection in both the GUI control and our menu + m_pCartList->SetSelectedIndex(m_ListItemIndex = pItem->m_ID); + } + } + } + + if (anEvent.GetMsg() == GUIListBox::MouseDown) { + m_pCartList->SetFocus(); + m_MenuFocus = ORDER; + m_ListItemIndex = m_pCartList->GetSelectedIndex(); + m_pCartList->ScrollToSelected(); + if (anEvent.GetData() & GUIListBox::MOUSE_LEFT) { + m_DraggedItemIndex = m_pCartList->GetSelectedIndex(); + } + } + } + + // We do this down here, outside the m_pCartList control, because if we have a mouse-up event even outside the cart, we should stop dragging. We also check UInputMan in case the mouse is released entirely outside of the buy menu. + if ((anEvent.GetMsg() == GUIListBox::MouseUp && (anEvent.GetData() & GUIListBox::MOUSE_LEFT)) || g_UInputMan.MouseButtonReleased(MouseButtons::MOUSE_LEFT, m_pController->GetPlayer())) { + m_DraggedItemIndex = -1; + m_IsDragging = false; + } + } + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void BuyMenuGUI::Draw(BITMAP *drawBitmap) const { +void BuyMenuGUI::Draw(BITMAP* drawBitmap) const { AllegroScreen drawScreen(drawBitmap); m_pGUIController->Draw(&drawScreen); if (IsEnabled() && m_pController->IsMouseControlled()) { @@ -2119,38 +1969,34 @@ void BuyMenuGUI::FocusChange() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes sure all things that to happen when category is changed, happens. -void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs) -{ - // Re-set the GUI manager's focus on the tabs if we're supposed to - // We don't want to do that if we're just refreshing the same category, like in the case of of expanding a module group item - if (focusOnCategoryTabs) - { - m_pCategoryTabs[m_MenuCategory]->SetFocus(); - m_pCategoryTabs[m_MenuCategory]->SetCheck(true); - } - m_pShopList->ClearList(); - - // Hide/show the logo and special sets category buttons, and add all current presets to the list, and we're done. - if (m_MenuCategory == SETS) - { - m_Logo->SetVisible(false); - m_pSaveButton->SetVisible(true); - m_pClearButton->SetVisible(true); - // Add and done! - AddPresetsToItemList(); - return; - } - // Hide the sets buttons otherwise - else - { - m_Logo->SetVisible(true); - m_pSaveButton->SetVisible(false); - m_pClearButton->SetVisible(false); - } +void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs) { + // Re-set the GUI manager's focus on the tabs if we're supposed to + // We don't want to do that if we're just refreshing the same category, like in the case of of expanding a module group item + if (focusOnCategoryTabs) { + m_pCategoryTabs[m_MenuCategory]->SetFocus(); + m_pCategoryTabs[m_MenuCategory]->SetCheck(true); + } + m_pShopList->ClearList(); + + // Hide/show the logo and special sets category buttons, and add all current presets to the list, and we're done. + if (m_MenuCategory == SETS) { + m_Logo->SetVisible(false); + m_pSaveButton->SetVisible(true); + m_pClearButton->SetVisible(true); + // Add and done! + AddPresetsToItemList(); + return; + } + // Hide the sets buttons otherwise + else { + m_Logo->SetVisible(true); + m_pSaveButton->SetVisible(false); + m_pClearButton->SetVisible(false); + } - // The vector of lists which will be filled with catalog objects, grouped by which data module they were read from - std::vector> catalogList; - std::vector mechaCategoryGroups = { "Actors - Mecha", "Actors - Turrets" }; + // The vector of lists which will be filled with catalog objects, grouped by which data module they were read from + std::vector> catalogList; + std::vector mechaCategoryGroups = {"Actors - Mecha", "Actors - Turrets"}; if (m_MenuCategory == CRAFT) { AddObjectsToItemList(catalogList, "ACRocket"); @@ -2162,142 +2008,123 @@ void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs) AddObjectsToItemList(catalogList, "AHuman", mechaCategoryGroups, false); AddObjectsToItemList(catalogList, "ACrab", mechaCategoryGroups, false); } else if (m_MenuCategory == TOOLS) { - AddObjectsToItemList(catalogList, "HeldDevice", { "Tools" }); + AddObjectsToItemList(catalogList, "HeldDevice", {"Tools"}); } else if (m_MenuCategory == GUNS) { - AddObjectsToItemList(catalogList, "HDFirearm", { "Weapons" }); + AddObjectsToItemList(catalogList, "HDFirearm", {"Weapons"}); } else if (m_MenuCategory == BOMBS) { - AddObjectsToItemList(catalogList, "ThrownDevice", { "Bombs" }); + AddObjectsToItemList(catalogList, "ThrownDevice", {"Bombs"}); } else if (m_MenuCategory == SHIELDS) { - AddObjectsToItemList(catalogList, "HeldDevice", { "Shields" }); + AddObjectsToItemList(catalogList, "HeldDevice", {"Shields"}); } - SceneObject *pSObject = 0; - const DataModule *pModule = 0; - GUIBitmap *pItemBitmap = 0; - std::list tempList; - for (int moduleID = 0; moduleID < catalogList.size(); ++moduleID) - { - // Don't add an empty module grouping - if (!catalogList[moduleID].empty()) - { - tempList.clear(); - - // Move all valid/desired entities from the module list to the intermediate list - for (std::list::iterator oItr = catalogList[moduleID].begin(); oItr != catalogList[moduleID].end(); ++oItr) - { - pSObject = dynamic_cast(*oItr); - // Only add buyable and non-brain items, unless they are explicitly set to be available. + SceneObject* pSObject = 0; + const DataModule* pModule = 0; + GUIBitmap* pItemBitmap = 0; + std::list tempList; + for (int moduleID = 0; moduleID < catalogList.size(); ++moduleID) { + // Don't add an empty module grouping + if (!catalogList[moduleID].empty()) { + tempList.clear(); + + // Move all valid/desired entities from the module list to the intermediate list + for (std::list::iterator oItr = catalogList[moduleID].begin(); oItr != catalogList[moduleID].end(); ++oItr) { + pSObject = dynamic_cast(*oItr); + // Only add buyable and non-brain items, unless they are explicitly set to be available. if ((pSObject && pSObject->IsBuyable() && !pSObject->IsBuyableInObjectPickerOnly() && !pSObject->IsBuyableInScriptOnly() && !pSObject->IsInGroup("Brains")) || GetOwnedItemsAmount((pSObject)->GetModuleAndPresetName()) > 0 || m_AlwaysAllowedItems.find((pSObject)->GetModuleAndPresetName()) != m_AlwaysAllowedItems.end()) { tempList.push_back(pSObject); } - } - - // Don't add anyhting to the real buy item list if the current module didn't yield any valid items - if (!tempList.empty()) - { - // Add the DataModule separator in the shop list, with appropriate name and perhaps icon? Don't add for first base module - if (moduleID != 0 && (pModule = g_PresetMan.GetDataModule(moduleID))) - { - pItemBitmap = pModule->GetIcon() ? new AllegroBitmap(pModule->GetIcon()) : 0; - // Passing in ownership of the bitmap, making uppercase the name - std::string name = pModule->GetFriendlyName(); - transform(name.begin(), name.end(), name.begin(), ::toupper); - m_pShopList->AddItem(name, m_aExpandedModules[moduleID] ? "-" : "+", pItemBitmap, 0, moduleID); - } - - // If the module is expanded, add all the items within it below - if (moduleID == 0 || m_aExpandedModules[moduleID]) - { - // Transfer from the temp intermediate list to the real gui list - for (std::list::iterator tItr = tempList.begin(); tItr != tempList.end(); ++tItr) - { - // Get a good icon and wrap it, while not passing ownership into the AllegroBitmap - pItemBitmap = new AllegroBitmap((*tItr)->GetGraphicalIcon()); - // Passing in ownership of the bitmap, but not of the pSpriteObj - if (m_OwnedItems.size() > 0 || m_OnlyShowOwnedItems) - { - if (GetOwnedItemsAmount((*tItr)->GetModuleAndPresetName()) > 0) - { - std::string amount = std::to_string(GetOwnedItemsAmount((*tItr)->GetModuleAndPresetName())) + " pcs"; - m_pShopList->AddItem((*tItr)->GetPresetName(), amount , pItemBitmap, *tItr); - } - else - { + } + + // Don't add anyhting to the real buy item list if the current module didn't yield any valid items + if (!tempList.empty()) { + // Add the DataModule separator in the shop list, with appropriate name and perhaps icon? Don't add for first base module + if (moduleID != 0 && (pModule = g_PresetMan.GetDataModule(moduleID))) { + pItemBitmap = pModule->GetIcon() ? new AllegroBitmap(pModule->GetIcon()) : 0; + // Passing in ownership of the bitmap, making uppercase the name + std::string name = pModule->GetFriendlyName(); + transform(name.begin(), name.end(), name.begin(), ::toupper); + m_pShopList->AddItem(name, m_aExpandedModules[moduleID] ? "-" : "+", pItemBitmap, 0, moduleID); + } + + // If the module is expanded, add all the items within it below + if (moduleID == 0 || m_aExpandedModules[moduleID]) { + // Transfer from the temp intermediate list to the real gui list + for (std::list::iterator tItr = tempList.begin(); tItr != tempList.end(); ++tItr) { + // Get a good icon and wrap it, while not passing ownership into the AllegroBitmap + pItemBitmap = new AllegroBitmap((*tItr)->GetGraphicalIcon()); + // Passing in ownership of the bitmap, but not of the pSpriteObj + if (m_OwnedItems.size() > 0 || m_OnlyShowOwnedItems) { + if (GetOwnedItemsAmount((*tItr)->GetModuleAndPresetName()) > 0) { + std::string amount = std::to_string(GetOwnedItemsAmount((*tItr)->GetModuleAndPresetName())) + " pcs"; + m_pShopList->AddItem((*tItr)->GetPresetName(), amount, pItemBitmap, *tItr); + } else { if (!m_OnlyShowOwnedItems) m_pShopList->AddItem((*tItr)->GetPresetName(), (*tItr)->GetGoldValueString(m_NativeTechModule, m_ForeignCostMult), pItemBitmap, *tItr); - else - { + else { if (m_AlwaysAllowedItems.find((*tItr)->GetModuleAndPresetName()) != m_AlwaysAllowedItems.end()) m_pShopList->AddItem((*tItr)->GetPresetName(), (*tItr)->GetGoldValueString(m_NativeTechModule, m_ForeignCostMult), pItemBitmap, *tItr); } } - } - else - { + } else { m_pShopList->AddItem((*tItr)->GetPresetName(), (*tItr)->GetGoldValueString(m_NativeTechModule, m_ForeignCostMult), pItemBitmap, *tItr); } - } - } - } - } - } + } + } + } + } + } - // Set the last saved index for this category so the menu scrolls down to it - m_pShopList->SetSelectedIndex(m_CategoryItemIndex[m_MenuCategory]); - m_ListItemIndex = m_CategoryItemIndex[m_MenuCategory]; - if (focusOnCategoryTabs) - m_pShopList->ScrollToSelected(); + // Set the last saved index for this category so the menu scrolls down to it + m_pShopList->SetSelectedIndex(m_CategoryItemIndex[m_MenuCategory]); + m_ListItemIndex = m_CategoryItemIndex[m_MenuCategory]; + if (focusOnCategoryTabs) + m_pShopList->ScrollToSelected(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: SaveCurrentLoadout ////////////////////////////////////////////////////////////////////////////////////////// // Description: Saves the current loadout into a Set. -void BuyMenuGUI::SaveCurrentLoadout() -{ - Loadout newSet; +void BuyMenuGUI::SaveCurrentLoadout() { + Loadout newSet; - // Abort if there's no cargo to save into the preset - if (!GetOrderList(*(newSet.GetCargoList()))) - return; + // Abort if there's no cargo to save into the preset + if (!GetOrderList(*(newSet.GetCargoList()))) + return; - // Add the ship - newSet.SetDeliveryCraft(dynamic_cast(GetDeliveryCraftPreset())); + // Add the ship + newSet.SetDeliveryCraft(dynamic_cast(GetDeliveryCraftPreset())); - // Add it to the loadouts - m_Loadouts.push_back(newSet); + // Add it to the loadouts + m_Loadouts.push_back(newSet); - // Save the new list of loadouts to file - SaveAllLoadoutsToFile(); + // Save the new list of loadouts to file + SaveAllLoadoutsToFile(); - // Update the list of loadout presets so the new one shows up - CategoryChange(); + // Update the list of loadout presets so the new one shows up + CategoryChange(); - // Set focus back on the save button (CatChange changed it) - m_pSaveButton->SetFocus(); + // Set focus back on the save button (CatChange changed it) + m_pSaveButton->SetFocus(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: DeployLoadout ////////////////////////////////////////////////////////////////////////////////////////// // Description: Loads the loadout set into the cart, replacing whatever's there now. -bool BuyMenuGUI::DeployLoadout(int index) -{ - if (index < 0 || index >= m_Loadouts.size()) - return false; +bool BuyMenuGUI::DeployLoadout(int index) { + if (index < 0 || index >= m_Loadouts.size()) + return false; - // Clear the cart, we're going to refill it with the selected loadout - m_pCartList->ClearList(); + // Clear the cart, we're going to refill it with the selected loadout + m_pCartList->ClearList(); // Check if the craft is available - const ACraft * pCraft = m_Loadouts[index].GetDeliveryCraft(); + const ACraft* pCraft = m_Loadouts[index].GetDeliveryCraft(); - if (pCraft) - { + if (pCraft) { bool craftAvailable = true; if (IsAllowedItem(pCraft->GetModuleAndPresetName())) @@ -2306,8 +2133,7 @@ bool BuyMenuGUI::DeployLoadout(int index) if (IsProhibitedItem(pCraft->GetModuleAndPresetName())) craftAvailable = false; - if (m_OnlyShowOwnedItems && craftAvailable) - { + if (m_OnlyShowOwnedItems && craftAvailable) { if (GetOwnedItemsAmount(pCraft->GetModuleAndPresetName()) > 0) craftAvailable = true; else @@ -2321,14 +2147,13 @@ bool BuyMenuGUI::DeployLoadout(int index) return false; } - // Get and add all the stuff in the selected loadout - std::list *pCargo = m_Loadouts[index].GetCargoList(); - AllegroBitmap *pItemBitmap = 0; - for (std::list::iterator cItr = pCargo->begin(); cItr != pCargo->end(); ++cItr) - { - // Get a good icon and wrap it, while not passing ownership into the AllegroBitmap - pItemBitmap = new AllegroBitmap(const_cast(*cItr)->GetGraphicalIcon()); - // Take into account whether these are native or not, and multiply the cost accordingly + // Get and add all the stuff in the selected loadout + std::list* pCargo = m_Loadouts[index].GetCargoList(); + AllegroBitmap* pItemBitmap = 0; + for (std::list::iterator cItr = pCargo->begin(); cItr != pCargo->end(); ++cItr) { + // Get a good icon and wrap it, while not passing ownership into the AllegroBitmap + pItemBitmap = new AllegroBitmap(const_cast(*cItr)->GetGraphicalIcon()); + // Take into account whether these are native or not, and multiply the cost accordingly bool canAdd = true; if (IsAllowedItem((*cItr)->GetModuleAndPresetName())) @@ -2337,15 +2162,12 @@ bool BuyMenuGUI::DeployLoadout(int index) if (IsProhibitedItem((*cItr)->GetModuleAndPresetName())) canAdd = false; - if (m_OnlyShowOwnedItems && canAdd) - { - if (GetOwnedItemsAmount((*cItr)->GetModuleAndPresetName()) > 0) - { + if (m_OnlyShowOwnedItems && canAdd) { + if (GetOwnedItemsAmount((*cItr)->GetModuleAndPresetName()) > 0) { canAdd = false; // Add manually with pcs counter AddCartItem((*cItr)->GetPresetName(), "1 pc", pItemBitmap, *cItr); - } - else + } else canAdd = false; } @@ -2354,30 +2176,29 @@ bool BuyMenuGUI::DeployLoadout(int index) if (canAdd) AddCartItem((*cItr)->GetPresetName(), (*cItr)->GetGoldValueString(m_NativeTechModule, m_ForeignCostMult), pItemBitmap, *cItr); - } - // Now set the craft to what the loadout specifies, if anything - if (m_Loadouts[index].GetDeliveryCraft()) - { - m_pSelectedCraft = m_Loadouts[index].GetDeliveryCraft(); - // Take into account whether these are native or not, and multiply the cost accordingly - m_pCraftBox->SetText(m_pSelectedCraft->GetPresetName()); - m_pCraftBox->SetRightText(m_pSelectedCraft->GetGoldValueString(m_NativeTechModule, m_ForeignCostMult)); + } + // Now set the craft to what the loadout specifies, if anything + if (m_Loadouts[index].GetDeliveryCraft()) { + m_pSelectedCraft = m_Loadouts[index].GetDeliveryCraft(); + // Take into account whether these are native or not, and multiply the cost accordingly + m_pCraftBox->SetText(m_pSelectedCraft->GetPresetName()); + m_pCraftBox->SetRightText(m_pSelectedCraft->GetGoldValueString(m_NativeTechModule, m_ForeignCostMult)); m_pCraftNameLabel->SetText(m_pSelectedCraft->GetPresetName()); m_pCraftPriceLabel->SetText(m_pSelectedCraft->GetGoldValueString(m_NativeTechModule, m_ForeignCostMult)); - UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); - UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); - } + UpdateTotalPassengersLabel(dynamic_cast(m_pSelectedCraft), m_pCraftPassengersLabel); + UpdateTotalMassLabel(dynamic_cast(m_pSelectedCraft), m_pCraftMassLabel); + } - // Update labels with the new config's values - UpdateTotalCostLabel(); + // Update labels with the new config's values + UpdateTotalCostLabel(); - return true; + return true; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void BuyMenuGUI::AddObjectsToItemList(std::vector> &moduleList, const std::string &type, const std::vector &groups, bool excludeGroups) { +void BuyMenuGUI::AddObjectsToItemList(std::vector>& moduleList, const std::string& type, const std::vector& groups, bool excludeGroups) { while (moduleList.size() < g_PresetMan.GetTotalModuleCount()) { moduleList.emplace_back(); } @@ -2396,14 +2217,11 @@ void BuyMenuGUI::AddObjectsToItemList(std::vector> &moduleLi } // Remove itwms which are not allowed to buy - if (m_AllowedItems.size() > 0) - { - for (int moduleID = 0; moduleID < moduleList.size(); ++moduleID) - { - std::list toRemove; - - for (std::list::iterator itr = moduleList[moduleID].begin(); itr != moduleList[moduleID].end(); ++itr) - { + if (m_AllowedItems.size() > 0) { + for (int moduleID = 0; moduleID < moduleList.size(); ++moduleID) { + std::list toRemove; + + for (std::list::iterator itr = moduleList[moduleID].begin(); itr != moduleList[moduleID].end(); ++itr) { bool allowed = false; if (m_AllowedItems.find((*itr)->GetModuleAndPresetName()) != m_AllowedItems.end()) @@ -2417,20 +2235,17 @@ void BuyMenuGUI::AddObjectsToItemList(std::vector> &moduleLi } // Remove items from the list - for (std::list::iterator itr = toRemove.begin(); itr != toRemove.end(); ++itr) + for (std::list::iterator itr = toRemove.begin(); itr != toRemove.end(); ++itr) moduleList[moduleID].remove((*itr)); } } // Remove items which are prohibited - if (m_ProhibitedItems.size() > 0) - { - for (int moduleID = 0; moduleID < moduleList.size(); ++moduleID) - { - std::list toRemove; - - for (std::list::iterator itr = moduleList[moduleID].begin(); itr != moduleList[moduleID].end(); ++itr) - { + if (m_ProhibitedItems.size() > 0) { + for (int moduleID = 0; moduleID < moduleList.size(); ++moduleID) { + std::list toRemove; + + for (std::list::iterator itr = moduleList[moduleID].begin(); itr != moduleList[moduleID].end(); ++itr) { bool allowed = true; if (m_ProhibitedItems.find((*itr)->GetModuleAndPresetName()) != m_ProhibitedItems.end()) @@ -2444,24 +2259,20 @@ void BuyMenuGUI::AddObjectsToItemList(std::vector> &moduleLi } // Remove items from the list - for (std::list::iterator itr = toRemove.begin(); itr != toRemove.end(); ++itr) + for (std::list::iterator itr = toRemove.begin(); itr != toRemove.end(); ++itr) moduleList[moduleID].remove((*itr)); } } // Remove items which are not in stock - if (m_OnlyShowOwnedItems && m_OwnedItems.size() > 0) - { - for (int moduleID = 0; moduleID < moduleList.size(); ++moduleID) - { - std::list toRemove; - - for (std::list::iterator itr = moduleList[moduleID].begin(); itr != moduleList[moduleID].end(); ++itr) - { + if (m_OnlyShowOwnedItems && m_OwnedItems.size() > 0) { + for (int moduleID = 0; moduleID < moduleList.size(); ++moduleID) { + std::list toRemove; + + for (std::list::iterator itr = moduleList[moduleID].begin(); itr != moduleList[moduleID].end(); ++itr) { bool allowed = false; - for (std::map::iterator itrA = m_OwnedItems.begin(); itrA != m_OwnedItems.end(); ++itrA) - { + for (std::map::iterator itrA = m_OwnedItems.begin(); itrA != m_OwnedItems.end(); ++itrA) { if ((*itr)->GetModuleAndPresetName() == (*itrA).first && (*itrA).second > 0) allowed = true; } @@ -2474,71 +2285,65 @@ void BuyMenuGUI::AddObjectsToItemList(std::vector> &moduleLi } // Remove items from the list - for (std::list::iterator itr = toRemove.begin(); itr != toRemove.end(); ++itr) + for (std::list::iterator itr = toRemove.begin(); itr != toRemove.end(); ++itr) moduleList[moduleID].remove((*itr)); } } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: AddPresetsToItemList ////////////////////////////////////////////////////////////////////////////////////////// // Description: Adds all loadout presets' representations to the item GUI list. -void BuyMenuGUI::AddPresetsToItemList() -{ - GUIBitmap *pItemBitmap = 0; - std::string loadoutLabel; - float loadoutCost; - const Actor *pPassenger = 0; - char costString[256]; - - // Go through all the presets, making intelligible list items from then for the GUI item list - for (std::vector::iterator lItr = m_Loadouts.begin(); lItr != m_Loadouts.end(); ++lItr) - { - loadoutLabel.clear(); - loadoutCost = 0; - pItemBitmap = 0; - pPassenger = 0; +void BuyMenuGUI::AddPresetsToItemList() { + GUIBitmap* pItemBitmap = 0; + std::string loadoutLabel; + float loadoutCost; + const Actor* pPassenger = 0; + char costString[256]; + + // Go through all the presets, making intelligible list items from then for the GUI item list + for (std::vector::iterator lItr = m_Loadouts.begin(); lItr != m_Loadouts.end(); ++lItr) { + loadoutLabel.clear(); + loadoutCost = 0; + pItemBitmap = 0; + pPassenger = 0; // Add preset name at the begining to differentiate loadouts from user-defined presets if ((*lItr).GetPresetName() != "None") loadoutLabel = (*lItr).GetPresetName() + ":\n"; - // Go through the cargo setup of each loadout and encode a meaningful label for the list item - for (std::list::iterator cItr = (*lItr).GetCargoList()->begin(); cItr != (*lItr).GetCargoList()->end(); ++cItr) - { - // If not the first one, add a comma separator to the label - if (cItr != (*lItr).GetCargoList()->begin()) - loadoutLabel += ", "; - // Append the name of the current cargo thing to the label - loadoutLabel += (*cItr)->GetPresetName(); - // Adjust price for foreignness of the items to this player - loadoutCost += (*cItr)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); - if (!pPassenger) - pPassenger = dynamic_cast(*cItr); - } + // Go through the cargo setup of each loadout and encode a meaningful label for the list item + for (std::list::iterator cItr = (*lItr).GetCargoList()->begin(); cItr != (*lItr).GetCargoList()->end(); ++cItr) { + // If not the first one, add a comma separator to the label + if (cItr != (*lItr).GetCargoList()->begin()) + loadoutLabel += ", "; + // Append the name of the current cargo thing to the label + loadoutLabel += (*cItr)->GetPresetName(); + // Adjust price for foreignness of the items to this player + loadoutCost += (*cItr)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); + if (!pPassenger) + pPassenger = dynamic_cast(*cItr); + } - // Add the ship's cost, if there is one defined - if ((*lItr).GetDeliveryCraft()) - { - loadoutLabel += " via " + (*lItr).GetDeliveryCraft()->GetPresetName(); - // Adjust price for foreignness of the ship to this player - loadoutCost += (*lItr).GetDeliveryCraft()->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); - } + // Add the ship's cost, if there is one defined + if ((*lItr).GetDeliveryCraft()) { + loadoutLabel += " via " + (*lItr).GetDeliveryCraft()->GetPresetName(); + // Adjust price for foreignness of the ship to this player + loadoutCost += (*lItr).GetDeliveryCraft()->GetGoldValue(m_NativeTechModule, m_ForeignCostMult); + } - // Make the cost label - std::snprintf(costString, sizeof(costString), "%.0f", loadoutCost); - // Get a good icon and wrap it, while not passing ownership into the AllegroBitmap - // We're trying to pick the icon of the first passenger, or the first item if there's no passengers in the loadout - pItemBitmap = new AllegroBitmap(pPassenger ? const_cast(pPassenger)->GetGraphicalIcon() : const_cast((*lItr).GetCargoList()->front())->GetGraphicalIcon()); - // Passing in ownership of the bitmap, but not of the pSpriteObj - m_pShopList->AddItem(loadoutLabel, costString, pItemBitmap, 0); - } + // Make the cost label + std::snprintf(costString, sizeof(costString), "%.0f", loadoutCost); + // Get a good icon and wrap it, while not passing ownership into the AllegroBitmap + // We're trying to pick the icon of the first passenger, or the first item if there's no passengers in the loadout + pItemBitmap = new AllegroBitmap(pPassenger ? const_cast(pPassenger)->GetGraphicalIcon() : const_cast((*lItr).GetCargoList()->front())->GetGraphicalIcon()); + // Passing in ownership of the bitmap, but not of the pSpriteObj + m_pShopList->AddItem(loadoutLabel, costString, pItemBitmap, 0); + } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void BuyMenuGUI::UpdateTotalCostLabel(int whichTeam) { @@ -2546,7 +2351,6 @@ void BuyMenuGUI::UpdateTotalCostLabel(int whichTeam) { m_pCostLabel->SetText(display); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void BuyMenuGUI::UpdateTotalMassLabel(const ACraft* pCraft, GUILabel* pLabel) const { @@ -2567,7 +2371,6 @@ void BuyMenuGUI::UpdateTotalMassLabel(const ACraft* pCraft, GUILabel* pLabel) co pLabel->SetText(display); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void BuyMenuGUI::UpdateTotalPassengersLabel(const ACraft* pCraft, GUILabel* pLabel) const { @@ -2588,35 +2391,34 @@ void BuyMenuGUI::UpdateTotalPassengersLabel(const ACraft* pCraft, GUILabel* pLab pLabel->SetText(display); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void BuyMenuGUI::TryPurchase() { int player = m_pController->GetPlayer(); - // Switch to the Craft category to give the user a hint - if (!m_pSelectedCraft) - { - m_MenuCategory = CRAFT; - CategoryChange(); - m_FocusChange = -2; - m_MenuFocus = ITEMS; - g_GUISound.UserErrorSound()->Play(player); - // Set the notification blinker - m_BlinkMode = NOCRAFT; - m_BlinkTimer.Reset(); + // Switch to the Craft category to give the user a hint + if (!m_pSelectedCraft) { + m_MenuCategory = CRAFT; + CategoryChange(); + m_FocusChange = -2; + m_MenuFocus = ITEMS; + g_GUISound.UserErrorSound()->Play(player); + // Set the notification blinker + m_BlinkMode = NOCRAFT; + m_BlinkTimer.Reset(); return; - } - // Can't afford it :( - else if (GetTotalOrderCost() > g_ActivityMan.GetActivity()->GetTeamFunds(m_pController->GetTeam())) - { - g_GUISound.UserErrorSound()->Play(player); - // Set the notification blinker - m_BlinkMode = NOFUNDS; - m_BlinkTimer.Reset(); + } + // Can't afford it :( + else if (GetTotalOrderCost() > g_ActivityMan.GetActivity()->GetTeamFunds(m_pController->GetTeam())) { + g_GUISound.UserErrorSound()->Play(player); + // Set the notification blinker + m_BlinkMode = NOFUNDS; + m_BlinkTimer.Reset(); return; } else { - if (m_SelectingEquipment) { EnableEquipmentSelection(false); } - const ACraft * pCraft = dynamic_cast(m_pSelectedCraft); + if (m_SelectingEquipment) { + EnableEquipmentSelection(false); + } + const ACraft* pCraft = dynamic_cast(m_pSelectedCraft); if (pCraft) { // Enforce max mass if (m_EnforceMaxMassConstraint && pCraft->GetMaxInventoryMass() >= 0 && GetTotalOrderMass() > pCraft->GetMaxInventoryMass()) { @@ -2628,8 +2430,7 @@ void BuyMenuGUI::TryPurchase() { } // Enforce max passengers - if (pCraft->GetMaxPassengers() >= 0 && GetTotalOrderPassengers() > pCraft->GetMaxPassengers() && m_EnforceMaxPassengersConstraint) - { + if (pCraft->GetMaxPassengers() >= 0 && GetTotalOrderPassengers() > pCraft->GetMaxPassengers() && m_EnforceMaxPassengersConstraint) { g_GUISound.UserErrorSound()->Play(player); // Set the notification blinker m_BlinkMode = MAXPASSENGERS; @@ -2642,9 +2443,8 @@ void BuyMenuGUI::TryPurchase() { // Only allow purchase if there is a delivery craft and enough funds if (m_pSelectedCraft && std::floor(GetTotalOrderCost()) <= std::floor(g_ActivityMan.GetActivity()->GetTeamFunds(m_pController->GetTeam()))) { m_PurchaseMade = true; - m_DeliveryWidth = static_cast(m_pSelectedCraft)->GetSpriteWidth(); + m_DeliveryWidth = static_cast(m_pSelectedCraft)->GetSpriteWidth(); g_GUISound.PurchaseMadeSound()->Play(player); } } - diff --git a/Source/Menus/BuyMenuGUI.h b/Source/Menus/BuyMenuGUI.h index 399cf96788..4c7b1344c2 100644 --- a/Source/Menus/BuyMenuGUI.h +++ b/Source/Menus/BuyMenuGUI.h @@ -10,926 +10,868 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files -//#include "FrameMan.h" +// #include "FrameMan.h" #include "Timer.h" #include "Controller.h" #include "Loadout.h" struct BITMAP; - -namespace RTE -{ - -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUITab; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUIBitmap; -class GUILabel; -class SceneObject; -class MovableObject; -class ACraft; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: BuyMenuGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A full menu system that represents a purchasing GUI for Cortex Command -// Parent(s): None. -// Class history: 8/22/2006 BuyMenuGUI Created. - -class BuyMenuGUI { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: BuyMenuGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a BuyMenuGUI object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - BuyMenuGUI() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~BuyMenuGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a BuyMenuGUI object before deletion -// from system memory. -// Arguments: None. - - ~BuyMenuGUI() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BuyMenuGUI object ready for use. -// Arguments: A poitner to a Controller which will control this Menu. Ownership is -// NOT TRANSFERRED! -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(Controller *pController); - - -////////////////////////////////////////////////////////////////////////////////////////// -// method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire BuyMenuGUI, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the BuyMenuGUI object. -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadAllLoadoutsFromFile -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads or re-loads all the loadout presets from the appropriate files -// on disk. This will first clear out all current loadout presets! -// Arguments: None. -// Return value: Success or not. - - bool LoadAllLoadoutsFromFile(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveAllLoadoutsToFile -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves all the loadouts to appropriate file on disk. Does NOT save -// any named presets which will be loaded from the standard preset -// loadouts first anyway. -// Arguments: None. -// Return value: Success or not. - - bool SaveAllLoadoutsToFile(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! -// Arguments: The new controller for this menu. Ownership is NOT transferred -// Return value: None. - - void SetController(Controller *pController) { m_pController = pController; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Enables or disables the menu. This will animate it in and out of view. -// Arguments: Whether to enable or disable the menu. -// Return value: None. - - void SetEnabled(bool enable = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the menu is enabled or not. -// Arguments: None. -// Return value: None. - - bool IsEnabled() const { return m_MenuEnabled == ENABLED || m_MenuEnabled == ENABLING; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the menu is at all visible or not. -// Arguments: None. -// Return value: None. - - bool IsVisible() const { return m_MenuEnabled == ENABLED || m_MenuEnabled == ENABLING || m_MenuEnabled == DISABLING; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. -// Arguments: The new screen position of this entire GUI. - - void SetPosOnScreen(int newPosX, int newPosY); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMetaPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which MetaPlayer uses this menu, if any. -// Arguments: The index of the MetaPlayer that uses this menu. -// Return value: None. - - void SetMetaPlayer(int metaPlayer); - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMetaPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets which MetaPlayer uses this menu, if any. -// Arguments: None. -// Return value: Metaplayer who owns this buy menu - - int GetMetaPlayer() const { return m_MetaPlayer; } - - - /// - /// Sets which DataModule ID should be treated as the native tech of the user of this menu. - /// This will also apply the DataModule's faction BuyMenu theme, if applicable. - /// - /// The module ID to set as the native one. 0 means everything is native. - void SetNativeTechModule(int whichModule); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetForeignCostMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the multiplier of the cost of any foreign Tech items. -// Arguments: The scalar multiplier of the costs of foreign Tech items. -// Return value: None. - - void SetForeignCostMultiplier(float newMultiplier) { m_ForeignCostMult = newMultiplier; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetModuleExpanded -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether a data module shown in the item menu should be expanded -// or not. -// Arguments: The module ID to set as expanded. -// Whether should be expanded or not. -// Return value: None. - - void SetModuleExpanded(int whichModule, bool expanded = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PurchaseMade -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether a purchase was made during the last Update. -// Arguments: None. -// Return value: Wheter the BUY button was pressed or not during the last update. - - bool PurchaseMade() const { return m_PurchaseMade; } - - - /// - /// Gets the width of the current delivery craft. - /// - /// The width of the delivery craft, in pixels. - int GetDeliveryWidth() const { return m_DeliveryWidth; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOrderList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return the list of things currently in the purchase order list box. -// Arguments: A reference to a an empty list to fill with the Object:s ordered. -// Ownership of the Object:s is NOT TRANSFERRED! -// Return value: Whetehr any items were put in the list at all. false if there are no -// items in the order listbox. - - bool GetOrderList(std::list &listToFill) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLoadoutPresets -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return the list of loadouts currently saved as presets. -// Arguments: None. -// Return value: A reference to the list of loadout presets. - - std::vector & GetLoadoutPresets() { return m_Loadouts; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveCurrentLoadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the current loadout into a Set. -// Arguments: None. -// Return value: None. - - void SaveCurrentLoadout(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetDeliveryCraftPreset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return the intended delivery vehicle instance from the order. -// Arguments: None. -// Return value: The poiner to the specified delivery craft instance. Note that this is -// just PresetMan's const pointer, so ownership is NOT transferred! - - const SceneObject * GetDeliveryCraftPreset() { return m_pSelectedCraft; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalCost -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return the total cost of everything listed in the order box. -// Arguments: Whether or not to include delivery cost. -// Return value: The total cost in ounces of gold. - - float GetTotalCost(bool includeDelivery = true) const; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalOrderCost -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return the total cost of everything listed in the order box, including delivery costs. -// Arguments: None. -// Return value: The total cost in ounces of gold. - - float GetTotalOrderCost() const { return GetTotalCost(true); }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalCartCost -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return the total cost of everything listed in the order box, excluding delivery costs. -// Arguments: None. -// Return value: The total cost in ounces of gold. - - float GetTotalCartCost() const { return GetTotalCost(false); } - - - /// - /// Return the total mass of all items listed in the order box. - /// - /// The total mass (in kg) of the BuyMenu's cart. - float GetTotalOrderMass() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCraftMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return mass of craft used in the order box. -// Arguments: None. -// Return value: The total mass in kg. - - float GetCraftMass(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalOrderPassengers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return teh total number of passengers in the order box. -// Arguments: None. -// Return value: The total number of passengers. - - int GetTotalOrderPassengers() const; - - /// - /// Enable or disable the equipment selection mode for this BuyMenuGUI. - /// - /// Whether or not equipment selection mode should be enabled. - void EnableEquipmentSelection(bool enabled); - - /// - /// Updates the nesting level for every item in the cart. - /// - void UpdateItemNestingLevels(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu -// Arguments: The bitmap to draw on. -// Return value: None. - - void Draw(BITMAP *drawBitmap) const; - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnforceMaxPassengersConstraint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether passenger count constraints are enforced by this buy menu. -// Arguments: None. -// Return value: True if passenger constraints are enforced by this menu, false otherwise - - bool EnforceMaxPassengersConstraint() const { return m_EnforceMaxPassengersConstraint; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEnforceMaxPassengersConstraint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether passenger count constraints are enforced by this buy menu. -// Arguments: True to enforce passenger constraints by this menu, false otherwise -// Return value: None. - - void SetEnforceMaxPassengersConstraint(bool enforce) { m_EnforceMaxPassengersConstraint = enforce; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnforceMaxMassConstraint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether mass constraints are enforced by this buy menu. -// Arguments: True if mass constraints are enforced by this menu, false otherwise -// Return value: None. - - bool EnforceMaxMassConstraint() const { return m_EnforceMaxMassConstraint; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEnforceMaxMassConstraint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether mass constraints are enforced by this buy menu. -// Arguments: True to enforce mass constraints by this menu, false otherwise -// Return value: None. - - void SetEnforceMaxMassConstraint(bool enforce) { m_EnforceMaxMassConstraint = enforce; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddAllowedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an item to the list of allowed items. -// If the list is not empty then everything not in the list is removed from the buy menu -// Items will be removed from the buy menu when it's called, category changed or after a ForceRefresh(). -// Arguments: Full preset name to add. -// Return value: None. - - void AddAllowedItem(std::string presetName) { m_AllowedItems[presetName] = true; }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAllowedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes an item from the list of allowed items. -// Arguments: Full preset name to remove. -// Return value: None. - - void RemoveAllowedItem(std::string presetName) { m_AllowedItems.erase(presetName); }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearAllowedItems -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the list of allowed items -// Arguments: None. -// Return value: None. - - void ClearAllowedItems() { m_AllowedItems.clear(); }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsAllowedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns true if the item is in allowed list -// Arguments: Full preset name. -// Return value: None. - - bool IsAllowedItem(std::string presetName) { return m_AllowedItems.find(presetName) != m_AllowedItems.end(); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddAlwaysAllowedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an item to the list of always allowed items. This list overrides all previous constraints. -// Arguments: Full preset name to add. -// Return value: None. - - void AddAlwaysAllowedItem(std::string presetName) { m_AlwaysAllowedItems[presetName] = true; }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAlwaysAllowedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes an item from the list of always allowed items. -// Arguments: Full preset name to remove. -// Return value: None. - - void RemoveAlwaysAllowedItem(std::string presetName) { m_AlwaysAllowedItems.erase(presetName); }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearAlwaysAllowedItems -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the list of allowed items -// Arguments: None. -// Return value: None. - - void ClearAlwaysAllowedItems() { m_AlwaysAllowedItems.clear(); }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsAlwaysAllowedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns true if the item is in always allowed list -// Arguments: Full preset name. -// Return value: None. - - bool IsAlwaysAllowedItem(std::string presetName) { return m_AlwaysAllowedItems.find(presetName) != m_AlwaysAllowedItems.end(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddProhibitedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds an item prohibited to buy from the buy menu. -// The item will be removed from the buy menu when it's called, category changed or after a ForceRefresh(). -// Arguments: Full preset name to add. -// Return value: None. - - void AddProhibitedItem(std::string presetName) { m_ProhibitedItems[presetName] = true; }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveProhibitedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes item from the list of prohibited items -// Arguments: Full preset name to remove. -// Return value: None. - - void RemoveProhibitedItem(std::string presetName) { m_ProhibitedItems.erase(presetName); }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearProhibitedItems -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears the list of prohibited items -// Arguments: None. -// Return value: None. - - void ClearProhibitedItems() { m_ProhibitedItems.clear(); }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsProhibitedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns true if the item is in prohibited list -// Arguments: Full preset name. -// Return value: None. - - bool IsProhibitedItem(std::string presetName) { return m_ProhibitedItems.find(presetName) != m_ProhibitedItems.end(); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ForceRefresh -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Forces a refresh update of the list of buy menu items -// Arguments: None. -// Return value: None. - - void ForceRefresh() { CategoryChange(); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ClearCartList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clear the cart out of items selected for purchase -// Arguments: None. -// Return value: None. - - void ClearCartList(); - - /// - /// Adds an item to the cart. - /// - /// The name shown for the item. - /// The text that is shown right-aligned on the item (typically the cost of the item). - /// The sprite image rendered for the item. This takes ownership! - /// The entity that this item refers to and will create when bought. - /// Extra index for special indexing or reference that the item is associated with. Menu-specific. - void AddCartItem(const std::string &name, const std::string &rightText = "", GUIBitmap *pBitmap = nullptr, const Entity *pEntity = 0, const int extraIndex = -1); - - /// - /// Duplicates an item in the cart. - /// - /// The index of the item to duplicate. - void DuplicateCartItem(const int itemIndex); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadDefaultLoadoutToCart -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads the default loadout to the cart -// Arguments: None. -// Return value: None. - - void LoadDefaultLoadoutToCart() { DeployLoadout(0); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOnlyShowOwnedItems -////////////////////////////////////////////////////////////////////////////////////////// -// Description: If set to true only owned items will be shown in buy menu. Overriden by AlwaysAllowed list. -// Arguments: Value. -// Return value: None. - - void SetOnlyShowOwnedItems(bool value) { m_OnlyShowOwnedItems = value; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOnlyShowOwnedItems -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns whether only owned items will be shown in buy menu. Overriden by AlwaysAllowed list. -// Arguments: None. -// Return value: Whether only owned items will be shown in buy menu. - - bool GetOnlyShowOwnedItems() const { return m_OnlyShowOwnedItems; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOwnedItemsAmount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the amount of specified items to be owned in this buy menu -// Arguments: Full preset name of item to own. Amount of owned items. -// Return value: None. - - void SetOwnedItemsAmount(std::string presetName, int amount) { m_OwnedItems[presetName] = amount; }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOwnedItemsAmount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the amount of specified items owned in this buy menu -// Arguments: Full preset name of item. -// Return value: Amount of owned items. - - int GetOwnedItemsAmount(std::string presetName) { if (m_OwnedItems.find(presetName) != m_OwnedItems.end()) return m_OwnedItems[presetName]; else return 0; }; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CommitPurchase -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Deducts 1 piece of owned item and return true if purchase can be made or false if the item is out of stock. -// Arguments: Full preset name of item. -// Return value: Whether the purchase can be conducted or the item is out of stock. - - bool CommitPurchase(std::string presetName); +namespace RTE { + + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUITab; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUIBitmap; + class GUILabel; + class SceneObject; + class MovableObject; + class ACraft; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: BuyMenuGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A full menu system that represents a purchasing GUI for Cortex Command + // Parent(s): None. + // Class history: 8/22/2006 BuyMenuGUI Created. + + class BuyMenuGUI { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: BuyMenuGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a BuyMenuGUI object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + BuyMenuGUI() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~BuyMenuGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a BuyMenuGUI object before deletion + // from system memory. + // Arguments: None. + + ~BuyMenuGUI() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the BuyMenuGUI object ready for use. + // Arguments: A poitner to a Controller which will control this Menu. Ownership is + // NOT TRANSFERRED! + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(Controller* pController); + + ////////////////////////////////////////////////////////////////////////////////////////// + // method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire BuyMenuGUI, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the BuyMenuGUI object. + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadAllLoadoutsFromFile + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads or re-loads all the loadout presets from the appropriate files + // on disk. This will first clear out all current loadout presets! + // Arguments: None. + // Return value: Success or not. + + bool LoadAllLoadoutsFromFile(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SaveAllLoadoutsToFile + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves all the loadouts to appropriate file on disk. Does NOT save + // any named presets which will be loaded from the standard preset + // loadouts first anyway. + // Arguments: None. + // Return value: Success or not. + + bool SaveAllLoadoutsToFile(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetController + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the controller used by this. The ownership of the controller is + // NOT transferred! + // Arguments: The new controller for this menu. Ownership is NOT transferred + // Return value: None. + + void SetController(Controller* pController) { m_pController = pController; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Enables or disables the menu. This will animate it in and out of view. + // Arguments: Whether to enable or disable the menu. + // Return value: None. + + void SetEnabled(bool enable = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the menu is enabled or not. + // Arguments: None. + // Return value: None. + + bool IsEnabled() const { return m_MenuEnabled == ENABLED || m_MenuEnabled == ENABLING; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the menu is at all visible or not. + // Arguments: None. + // Return value: None. + + bool IsVisible() const { return m_MenuEnabled == ENABLED || m_MenuEnabled == ENABLING || m_MenuEnabled == DISABLING; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPosOnScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets where on the screen that this GUI is being drawn to. If upper + // left corner, then 0, 0. This will affect the way the mouse is positioned + // etc. + // Arguments: The new screen position of this entire GUI. + + void SetPosOnScreen(int newPosX, int newPosY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetMetaPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which MetaPlayer uses this menu, if any. + // Arguments: The index of the MetaPlayer that uses this menu. + // Return value: None. + + void SetMetaPlayer(int metaPlayer); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetMetaPlayer + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets which MetaPlayer uses this menu, if any. + // Arguments: None. + // Return value: Metaplayer who owns this buy menu + + int GetMetaPlayer() const { return m_MetaPlayer; } + + /// + /// Sets which DataModule ID should be treated as the native tech of the user of this menu. + /// This will also apply the DataModule's faction BuyMenu theme, if applicable. + /// + /// The module ID to set as the native one. 0 means everything is native. + void SetNativeTechModule(int whichModule); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetForeignCostMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the multiplier of the cost of any foreign Tech items. + // Arguments: The scalar multiplier of the costs of foreign Tech items. + // Return value: None. + + void SetForeignCostMultiplier(float newMultiplier) { m_ForeignCostMult = newMultiplier; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetModuleExpanded + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether a data module shown in the item menu should be expanded + // or not. + // Arguments: The module ID to set as expanded. + // Whether should be expanded or not. + // Return value: None. + + void SetModuleExpanded(int whichModule, bool expanded = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PurchaseMade + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether a purchase was made during the last Update. + // Arguments: None. + // Return value: Wheter the BUY button was pressed or not during the last update. + + bool PurchaseMade() const { return m_PurchaseMade; } + + /// + /// Gets the width of the current delivery craft. + /// + /// The width of the delivery craft, in pixels. + int GetDeliveryWidth() const { return m_DeliveryWidth; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOrderList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Return the list of things currently in the purchase order list box. + // Arguments: A reference to a an empty list to fill with the Object:s ordered. + // Ownership of the Object:s is NOT TRANSFERRED! + // Return value: Whetehr any items were put in the list at all. false if there are no + // items in the order listbox. + + bool GetOrderList(std::list& listToFill) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetLoadoutPresets + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Return the list of loadouts currently saved as presets. + // Arguments: None. + // Return value: A reference to the list of loadout presets. + + std::vector& GetLoadoutPresets() { return m_Loadouts; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SaveCurrentLoadout + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Saves the current loadout into a Set. + // Arguments: None. + // Return value: None. + + void SaveCurrentLoadout(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetDeliveryCraftPreset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Return the intended delivery vehicle instance from the order. + // Arguments: None. + // Return value: The poiner to the specified delivery craft instance. Note that this is + // just PresetMan's const pointer, so ownership is NOT transferred! + + const SceneObject* GetDeliveryCraftPreset() { return m_pSelectedCraft; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalCost + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Return the total cost of everything listed in the order box. + // Arguments: Whether or not to include delivery cost. + // Return value: The total cost in ounces of gold. + + float GetTotalCost(bool includeDelivery = true) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalOrderCost + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Return the total cost of everything listed in the order box, including delivery costs. + // Arguments: None. + // Return value: The total cost in ounces of gold. + + float GetTotalOrderCost() const { return GetTotalCost(true); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalCartCost + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Return the total cost of everything listed in the order box, excluding delivery costs. + // Arguments: None. + // Return value: The total cost in ounces of gold. + + float GetTotalCartCost() const { return GetTotalCost(false); } + + /// + /// Return the total mass of all items listed in the order box. + /// + /// The total mass (in kg) of the BuyMenu's cart. + float GetTotalOrderMass() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCraftMass + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Return mass of craft used in the order box. + // Arguments: None. + // Return value: The total mass in kg. + + float GetCraftMass(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetTotalOrderPassengers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Return teh total number of passengers in the order box. + // Arguments: None. + // Return value: The total number of passengers. + + int GetTotalOrderPassengers() const; + + /// + /// Enable or disable the equipment selection mode for this BuyMenuGUI. + /// + /// Whether or not equipment selection mode should be enabled. + void EnableEquipmentSelection(bool enabled); + + /// + /// Updates the nesting level for every item in the cart. + /// + void UpdateItemNestingLevels(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this Menu each frame + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the menu + // Arguments: The bitmap to draw on. + // Return value: None. + + void Draw(BITMAP* drawBitmap) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnforceMaxPassengersConstraint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether passenger count constraints are enforced by this buy menu. + // Arguments: None. + // Return value: True if passenger constraints are enforced by this menu, false otherwise + + bool EnforceMaxPassengersConstraint() const { return m_EnforceMaxPassengersConstraint; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEnforceMaxPassengersConstraint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether passenger count constraints are enforced by this buy menu. + // Arguments: True to enforce passenger constraints by this menu, false otherwise + // Return value: None. + + void SetEnforceMaxPassengersConstraint(bool enforce) { m_EnforceMaxPassengersConstraint = enforce; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EnforceMaxMassConstraint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether mass constraints are enforced by this buy menu. + // Arguments: True if mass constraints are enforced by this menu, false otherwise + // Return value: None. + + bool EnforceMaxMassConstraint() const { return m_EnforceMaxMassConstraint; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEnforceMaxMassConstraint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets whether mass constraints are enforced by this buy menu. + // Arguments: True to enforce mass constraints by this menu, false otherwise + // Return value: None. + + void SetEnforceMaxMassConstraint(bool enforce) { m_EnforceMaxMassConstraint = enforce; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddAllowedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an item to the list of allowed items. + // If the list is not empty then everything not in the list is removed from the buy menu + // Items will be removed from the buy menu when it's called, category changed or after a ForceRefresh(). + // Arguments: Full preset name to add. + // Return value: None. + + void AddAllowedItem(std::string presetName) { m_AllowedItems[presetName] = true; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveAllowedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes an item from the list of allowed items. + // Arguments: Full preset name to remove. + // Return value: None. + + void RemoveAllowedItem(std::string presetName) { m_AllowedItems.erase(presetName); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearAllowedItems + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the list of allowed items + // Arguments: None. + // Return value: None. + + void ClearAllowedItems() { m_AllowedItems.clear(); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsAllowedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns true if the item is in allowed list + // Arguments: Full preset name. + // Return value: None. + + bool IsAllowedItem(std::string presetName) { return m_AllowedItems.find(presetName) != m_AllowedItems.end(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddAlwaysAllowedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an item to the list of always allowed items. This list overrides all previous constraints. + // Arguments: Full preset name to add. + // Return value: None. + + void AddAlwaysAllowedItem(std::string presetName) { m_AlwaysAllowedItems[presetName] = true; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveAlwaysAllowedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes an item from the list of always allowed items. + // Arguments: Full preset name to remove. + // Return value: None. + + void RemoveAlwaysAllowedItem(std::string presetName) { m_AlwaysAllowedItems.erase(presetName); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearAlwaysAllowedItems + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the list of allowed items + // Arguments: None. + // Return value: None. + + void ClearAlwaysAllowedItems() { m_AlwaysAllowedItems.clear(); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsAlwaysAllowedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns true if the item is in always allowed list + // Arguments: Full preset name. + // Return value: None. + + bool IsAlwaysAllowedItem(std::string presetName) { return m_AlwaysAllowedItems.find(presetName) != m_AlwaysAllowedItems.end(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddProhibitedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds an item prohibited to buy from the buy menu. + // The item will be removed from the buy menu when it's called, category changed or after a ForceRefresh(). + // Arguments: Full preset name to add. + // Return value: None. + + void AddProhibitedItem(std::string presetName) { m_ProhibitedItems[presetName] = true; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveProhibitedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes item from the list of prohibited items + // Arguments: Full preset name to remove. + // Return value: None. + + void RemoveProhibitedItem(std::string presetName) { m_ProhibitedItems.erase(presetName); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearProhibitedItems + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears the list of prohibited items + // Arguments: None. + // Return value: None. + + void ClearProhibitedItems() { m_ProhibitedItems.clear(); }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsProhibitedItem + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns true if the item is in prohibited list + // Arguments: Full preset name. + // Return value: None. + + bool IsProhibitedItem(std::string presetName) { return m_ProhibitedItems.find(presetName) != m_ProhibitedItems.end(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ForceRefresh + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Forces a refresh update of the list of buy menu items + // Arguments: None. + // Return value: None. + + void ForceRefresh() { CategoryChange(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ClearCartList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clear the cart out of items selected for purchase + // Arguments: None. + // Return value: None. + + void ClearCartList(); + + /// + /// Adds an item to the cart. + /// + /// The name shown for the item. + /// The text that is shown right-aligned on the item (typically the cost of the item). + /// The sprite image rendered for the item. This takes ownership! + /// The entity that this item refers to and will create when bought. + /// Extra index for special indexing or reference that the item is associated with. Menu-specific. + void AddCartItem(const std::string& name, const std::string& rightText = "", GUIBitmap* pBitmap = nullptr, const Entity* pEntity = 0, const int extraIndex = -1); + + /// + /// Duplicates an item in the cart. + /// + /// The index of the item to duplicate. + void DuplicateCartItem(const int itemIndex); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadDefaultLoadoutToCart + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads the default loadout to the cart + // Arguments: None. + // Return value: None. + + void LoadDefaultLoadoutToCart() { DeployLoadout(0); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOnlyShowOwnedItems + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: If set to true only owned items will be shown in buy menu. Overriden by AlwaysAllowed list. + // Arguments: Value. + // Return value: None. + + void SetOnlyShowOwnedItems(bool value) { m_OnlyShowOwnedItems = value; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOnlyShowOwnedItems + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns whether only owned items will be shown in buy menu. Overriden by AlwaysAllowed list. + // Arguments: None. + // Return value: Whether only owned items will be shown in buy menu. + + bool GetOnlyShowOwnedItems() const { return m_OnlyShowOwnedItems; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetOwnedItemsAmount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the amount of specified items to be owned in this buy menu + // Arguments: Full preset name of item to own. Amount of owned items. + // Return value: None. + + void SetOwnedItemsAmount(std::string presetName, int amount) { m_OwnedItems[presetName] = amount; }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetOwnedItemsAmount + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the amount of specified items owned in this buy menu + // Arguments: Full preset name of item. + // Return value: Amount of owned items. + + int GetOwnedItemsAmount(std::string presetName) { + if (m_OwnedItems.find(presetName) != m_OwnedItems.end()) + return m_OwnedItems[presetName]; + else + return 0; + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CommitPurchase + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Deducts 1 piece of owned item and return true if purchase can be made or false if the item is out of stock. + // Arguments: Full preset name of item. + // Return value: Whether the purchase can be conducted or the item is out of stock. + + bool CommitPurchase(std::string presetName); #pragma region Faction Theme Handling - /// - /// Changes the banner image to the one specified. If none is specified, resets it to the default banner image. - /// - /// Path to image to set as banner. - void SetBannerImage(const std::string &imagePath); - - /// - /// Changes the logo image to the one specified. If none is specified, resets it to the default logo image. - /// - /// Path to image to set as logo. - void SetLogoImage(const std::string &imagePath); + /// + /// Changes the banner image to the one specified. If none is specified, resets it to the default banner image. + /// + /// Path to image to set as banner. + void SetBannerImage(const std::string& imagePath); + + /// + /// Changes the logo image to the one specified. If none is specified, resets it to the default logo image. + /// + /// Path to image to set as logo. + void SetLogoImage(const std::string& imagePath); #pragma endregion -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CategoryChange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes sure all things that to happen when category is changed, happens. -// Arguments: Wheter to change focus to the category tabs or not. -// Return value: None. - - void CategoryChange(bool focusOnCategoryTabs = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DeployLoadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads the loadout set into the cart, replacing whatever's there now. -// Arguments: The index of the loadout to load. -// Return value: Whether it was loaded successfully or not. - - bool DeployLoadout(int index); - - - /// - /// Adds all objects of a specific type already defined in PresetMan to the current shop/item list. They will be grouped into the different data modules they were read from. - /// - /// Reference to the data module vector of entity lists to add the items to. - /// The name of the class to add all objects of. "" or "All" looks for all. - /// The name of the groups to add all objects of. An empty vector or "All" looks for all. - /// Whether the specified groups should be excluded, meaning all objects NOT associated with the groups will be added. - void AddObjectsToItemList(std::vector> &moduleList, const std::string &type = "", const std::vector &groups = {}, bool excludeGroups = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddPresetsToItemList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds all loadout presets' representations to the item GUI list. -// Arguments: None. -// Return value: None. - - void AddPresetsToItemList(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateTotalCostLabel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the text of the total cost label to reflect the total cost of -// all the items in teh order box. -// Arguments: The team to display the total funds of. -// Return value: None. - - void UpdateTotalCostLabel(int whichTeam = 0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateTotalMassLabel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the text of the specified label to reflect the total mass of -// all the items in the order box. -// Arguments: Craft to read MaxMass from. Label to update. -// Return value: None. - - void UpdateTotalMassLabel(const ACraft * pCraft, GUILabel * pLabel) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateTotalPassengersLabel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the text of the specified label to reflect the total passenger count of -// all the items in teh order box. -// Arguments: Craft to read MaxPassengers from. Label to update. -// Return value: None. - - void UpdateTotalPassengersLabel(const ACraft * pCraft, GUILabel * pLabel) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TryPurchase -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attempts to make a purchase with everything already set up. -// Arguments: None. -// Return value: None. - - void TryPurchase(); - - - enum MenuEnabled - { - ENABLING = 0, - ENABLED, - DISABLING, - DISABLED - }; - - enum MenuFocus - { - SETBUTTONS = 0, - CATEGORIES, - ITEMS, - ORDER, - OK, - CLEARORDER, - FOCUSCOUNT - }; - - enum MenuCategory - { - CRAFT = 0, - BODIES, - MECHA, - TOOLS, - GUNS, - BOMBS, - SHIELDS, - SETS, - CATEGORYCOUNT - }; - - enum BlinkMode - { - NOBLINK = 0, - NOFUNDS, - NOCRAFT, - MAXPASSENGERS, - MAXMASS, - BLINKMODECOUNT - }; - - // Controls this Menu. Not owned - Controller *m_pController; - // GUI Screen for use by the in-game GUI - GUIScreen *m_pGUIScreen; - // Input controller - GUIInput *m_pGUIInput; - // The control manager which holds all the controls - GUIControlManager *m_pGUIController; - // Visibility state of each menu - int m_MenuEnabled; - // Focus state - int m_MenuFocus; - // Focus change direction - 0 is non,e negative is back, positive forward - int m_FocusChange; - // Category selection state - int m_MenuCategory; - // Speed at which the menus appear and disappear - float m_MenuSpeed; - // Which item in the currently focused list box we have selected - int m_ListItemIndex; - // Which item we're dragging - int m_DraggedItemIndex; - // Whether we're currently dragging - bool m_IsDragging; - // Which object was last hovered over by the mouse, to avoid repeatedly selecting hte same item over and over when mose only moves a pixel - int m_LastHoveredMouseIndex; - // Which item in each of the categories was last selected, so the scrolling doesn't have to be redone each time user flips back and forth - int m_CategoryItemIndex[CATEGORYCOUNT]; - // Which metaplayer, if any, is using this menu - int m_MetaPlayer; - // The ID of the DataModule that contains the native Tech of the Player using this menu - int m_NativeTechModule; - // The multiplier of costs of any foreign tech items - float m_ForeignCostMult; - // Arry of bools showing which modules that have been expanded in the item list - bool *m_aExpandedModules; - // Notification blink timer - Timer m_BlinkTimer; - // What we're blinking - int m_BlinkMode; - // Measures real time to determine how fast the menu should animate when opening/closing to appear real time to the player - Timer m_MenuTimer; - // Measures the time to when to start repeating inputs when they're held down - Timer m_RepeatStartTimer; - // Measures the interval between input repeats - Timer m_RepeatTimer; - - bool m_SelectingEquipment; //!< Whether or not the menu is in equipment mode. - MenuCategory m_LastVisitedEquipmentTab; //!< The last tab visited while in equipment mode. - MenuCategory m_LastVisitedMainTab; //!< The last tab visited while not in equipment mode. - int m_LastEquipmentScrollPosition; //!< The last scroll position while in equipment mode. - int m_LastMainScrollPosition; //!< The last scroll position while not in equipment mode. - MenuCategory m_FirstMainTab; //!< The first enabled tab when not in equipment mode. - MenuCategory m_LastMainTab; //!< The last enabled tab when not in equipment mode. - MenuCategory m_FirstEquipmentTab; //!< The first enabled tab when in equipment mode. - MenuCategory m_LastEquipmentTab; //!< The last enabled tab when in equipment mode. - - // Collection box of the buy GUIs - GUICollectionBox *m_pParentBox; - // Collection box of the buy popups that contain information about items - GUICollectionBox *m_pPopupBox; - // Label displaying the item popup description - GUILabel *m_pPopupText; - // Top banner - GUICollectionBox *m_Banner; - // Logo label that disappears when the sets category is selected - GUICollectionBox *m_Logo; - // All the radio buttons for the different shop categories - GUITab *m_pCategoryTabs[CATEGORYCOUNT]; - // The Listbox which lists all the shop's items in the currently selected category - GUIListBox *m_pShopList; - // The Listbox which lists all the items currently in the shopping cart or order - GUIListBox *m_pCartList; - // The single-line textbox which shows the selected delivery craft - GUITextBox *m_pCraftBox; - - // Panel with craft parameters - GUICollectionBox *m_pCraftCollectionBox; - - // Selected craft name - GUILabel *m_pCraftNameLabel; - // Selected craft price - GUILabel *m_pCraftPriceLabel; - // Selected craft passenger caption - GUILabel *m_pCraftPassengersCaptionLabel; - // Selected craft passenger count - GUILabel *m_pCraftPassengersLabel; - // Selected craft total mass caption - GUILabel *m_pCraftMassCaptionLabel; - // Selected craft total mass - GUILabel *m_pCraftMassLabel; - - // Label displaying "Delivered On:" - GUILabel *m_pCraftLabel; - // The selected craft instance for delivery - const SceneObject *m_pSelectedCraft; - // Label displaying the total cost - GUILabel *m_pCostLabel; - // The purchasing button - GUIButton *m_pBuyButton; - - GUIButton *m_ClearOrderButton; //!< Button for clearing the cart. - // The save set button - GUIButton *m_pSaveButton; - // The clear set button - GUIButton *m_pClearButton; - // Sets of user-defined loadouts that can be selected quickly. - std::vector m_Loadouts; - // Purchase has been made - bool m_PurchaseMade; - int m_DeliveryWidth; //!< The width of the currently selected delivery craft, which will determine the width of the LZ marker. - // The cursor image shared by all buy menus - static BITMAP *s_pCursor; - - // If true UI won't afford to order a craft with more passengers than allowed by craft - bool m_EnforceMaxPassengersConstraint; - // If true UI won't afford to order a craft with more mass than allowed by craft - bool m_EnforceMaxMassConstraint; - - // Only show items that owned - bool m_OnlyShowOwnedItems; - // If not empty then only shows items present in this list - std::map m_AllowedItems; - // If not empty then items from this list are always shown int he buy menu no matter what other constraints there are - std::map m_AlwaysAllowedItems; - // If not empty then removes items from ths list the buy menu - std::map m_ProhibitedItems; - // A map of owned items, for which the gold will not be deducted when bought - std::map m_OwnedItems; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - static const std::string c_DefaultBannerImagePath; //!< Path to the default banner image. - static const std::string c_DefaultLogoImagePath; //!< Path to the default logo image. - - /// - /// Refresh tab disabled states, so tabs get properly enabled/disabled based on whether or not equipment selection mode is enabled. - /// - void RefreshTabDisabledStates(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this BuyMenuGUI, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - BuyMenuGUI(const BuyMenuGUI &reference) = delete; - BuyMenuGUI & operator=(const BuyMenuGUI &rhs) = delete; - -}; + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CategoryChange + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes sure all things that to happen when category is changed, happens. + // Arguments: Wheter to change focus to the category tabs or not. + // Return value: None. + + void CategoryChange(bool focusOnCategoryTabs = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DeployLoadout + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Loads the loadout set into the cart, replacing whatever's there now. + // Arguments: The index of the loadout to load. + // Return value: Whether it was loaded successfully or not. + + bool DeployLoadout(int index); + + /// + /// Adds all objects of a specific type already defined in PresetMan to the current shop/item list. They will be grouped into the different data modules they were read from. + /// + /// Reference to the data module vector of entity lists to add the items to. + /// The name of the class to add all objects of. "" or "All" looks for all. + /// The name of the groups to add all objects of. An empty vector or "All" looks for all. + /// Whether the specified groups should be excluded, meaning all objects NOT associated with the groups will be added. + void AddObjectsToItemList(std::vector>& moduleList, const std::string& type = "", const std::vector& groups = {}, bool excludeGroups = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddPresetsToItemList + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds all loadout presets' representations to the item GUI list. + // Arguments: None. + // Return value: None. + + void AddPresetsToItemList(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateTotalCostLabel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the text of the total cost label to reflect the total cost of + // all the items in teh order box. + // Arguments: The team to display the total funds of. + // Return value: None. + + void UpdateTotalCostLabel(int whichTeam = 0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateTotalMassLabel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the text of the specified label to reflect the total mass of + // all the items in the order box. + // Arguments: Craft to read MaxMass from. Label to update. + // Return value: None. + + void UpdateTotalMassLabel(const ACraft* pCraft, GUILabel* pLabel) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateTotalPassengersLabel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the text of the specified label to reflect the total passenger count of + // all the items in teh order box. + // Arguments: Craft to read MaxPassengers from. Label to update. + // Return value: None. + + void UpdateTotalPassengersLabel(const ACraft* pCraft, GUILabel* pLabel) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TryPurchase + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Attempts to make a purchase with everything already set up. + // Arguments: None. + // Return value: None. + + void TryPurchase(); + + enum MenuEnabled { + ENABLING = 0, + ENABLED, + DISABLING, + DISABLED + }; + + enum MenuFocus { + SETBUTTONS = 0, + CATEGORIES, + ITEMS, + ORDER, + OK, + CLEARORDER, + FOCUSCOUNT + }; + + enum MenuCategory { + CRAFT = 0, + BODIES, + MECHA, + TOOLS, + GUNS, + BOMBS, + SHIELDS, + SETS, + CATEGORYCOUNT + }; + + enum BlinkMode { + NOBLINK = 0, + NOFUNDS, + NOCRAFT, + MAXPASSENGERS, + MAXMASS, + BLINKMODECOUNT + }; + + // Controls this Menu. Not owned + Controller* m_pController; + // GUI Screen for use by the in-game GUI + GUIScreen* m_pGUIScreen; + // Input controller + GUIInput* m_pGUIInput; + // The control manager which holds all the controls + GUIControlManager* m_pGUIController; + // Visibility state of each menu + int m_MenuEnabled; + // Focus state + int m_MenuFocus; + // Focus change direction - 0 is non,e negative is back, positive forward + int m_FocusChange; + // Category selection state + int m_MenuCategory; + // Speed at which the menus appear and disappear + float m_MenuSpeed; + // Which item in the currently focused list box we have selected + int m_ListItemIndex; + // Which item we're dragging + int m_DraggedItemIndex; + // Whether we're currently dragging + bool m_IsDragging; + // Which object was last hovered over by the mouse, to avoid repeatedly selecting hte same item over and over when mose only moves a pixel + int m_LastHoveredMouseIndex; + // Which item in each of the categories was last selected, so the scrolling doesn't have to be redone each time user flips back and forth + int m_CategoryItemIndex[CATEGORYCOUNT]; + // Which metaplayer, if any, is using this menu + int m_MetaPlayer; + // The ID of the DataModule that contains the native Tech of the Player using this menu + int m_NativeTechModule; + // The multiplier of costs of any foreign tech items + float m_ForeignCostMult; + // Arry of bools showing which modules that have been expanded in the item list + bool* m_aExpandedModules; + // Notification blink timer + Timer m_BlinkTimer; + // What we're blinking + int m_BlinkMode; + // Measures real time to determine how fast the menu should animate when opening/closing to appear real time to the player + Timer m_MenuTimer; + // Measures the time to when to start repeating inputs when they're held down + Timer m_RepeatStartTimer; + // Measures the interval between input repeats + Timer m_RepeatTimer; + + bool m_SelectingEquipment; //!< Whether or not the menu is in equipment mode. + MenuCategory m_LastVisitedEquipmentTab; //!< The last tab visited while in equipment mode. + MenuCategory m_LastVisitedMainTab; //!< The last tab visited while not in equipment mode. + int m_LastEquipmentScrollPosition; //!< The last scroll position while in equipment mode. + int m_LastMainScrollPosition; //!< The last scroll position while not in equipment mode. + MenuCategory m_FirstMainTab; //!< The first enabled tab when not in equipment mode. + MenuCategory m_LastMainTab; //!< The last enabled tab when not in equipment mode. + MenuCategory m_FirstEquipmentTab; //!< The first enabled tab when in equipment mode. + MenuCategory m_LastEquipmentTab; //!< The last enabled tab when in equipment mode. + + // Collection box of the buy GUIs + GUICollectionBox* m_pParentBox; + // Collection box of the buy popups that contain information about items + GUICollectionBox* m_pPopupBox; + // Label displaying the item popup description + GUILabel* m_pPopupText; + // Top banner + GUICollectionBox* m_Banner; + // Logo label that disappears when the sets category is selected + GUICollectionBox* m_Logo; + // All the radio buttons for the different shop categories + GUITab* m_pCategoryTabs[CATEGORYCOUNT]; + // The Listbox which lists all the shop's items in the currently selected category + GUIListBox* m_pShopList; + // The Listbox which lists all the items currently in the shopping cart or order + GUIListBox* m_pCartList; + // The single-line textbox which shows the selected delivery craft + GUITextBox* m_pCraftBox; + + // Panel with craft parameters + GUICollectionBox* m_pCraftCollectionBox; + + // Selected craft name + GUILabel* m_pCraftNameLabel; + // Selected craft price + GUILabel* m_pCraftPriceLabel; + // Selected craft passenger caption + GUILabel* m_pCraftPassengersCaptionLabel; + // Selected craft passenger count + GUILabel* m_pCraftPassengersLabel; + // Selected craft total mass caption + GUILabel* m_pCraftMassCaptionLabel; + // Selected craft total mass + GUILabel* m_pCraftMassLabel; + + // Label displaying "Delivered On:" + GUILabel* m_pCraftLabel; + // The selected craft instance for delivery + const SceneObject* m_pSelectedCraft; + // Label displaying the total cost + GUILabel* m_pCostLabel; + // The purchasing button + GUIButton* m_pBuyButton; + + GUIButton* m_ClearOrderButton; //!< Button for clearing the cart. + // The save set button + GUIButton* m_pSaveButton; + // The clear set button + GUIButton* m_pClearButton; + // Sets of user-defined loadouts that can be selected quickly. + std::vector m_Loadouts; + // Purchase has been made + bool m_PurchaseMade; + int m_DeliveryWidth; //!< The width of the currently selected delivery craft, which will determine the width of the LZ marker. + // The cursor image shared by all buy menus + static BITMAP* s_pCursor; + + // If true UI won't afford to order a craft with more passengers than allowed by craft + bool m_EnforceMaxPassengersConstraint; + // If true UI won't afford to order a craft with more mass than allowed by craft + bool m_EnforceMaxMassConstraint; + + // Only show items that owned + bool m_OnlyShowOwnedItems; + // If not empty then only shows items present in this list + std::map m_AllowedItems; + // If not empty then items from this list are always shown int he buy menu no matter what other constraints there are + std::map m_AlwaysAllowedItems; + // If not empty then removes items from ths list the buy menu + std::map m_ProhibitedItems; + // A map of owned items, for which the gold will not be deducted when bought + std::map m_OwnedItems; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + static const std::string c_DefaultBannerImagePath; //!< Path to the default banner image. + static const std::string c_DefaultLogoImagePath; //!< Path to the default logo image. + + /// + /// Refresh tab disabled states, so tabs get properly enabled/disabled based on whether or not equipment selection mode is enabled. + /// + void RefreshTabDisabledStates(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this BuyMenuGUI, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + BuyMenuGUI(const BuyMenuGUI& reference) = delete; + BuyMenuGUI& operator=(const BuyMenuGUI& rhs) = delete; + }; } // namespace RTE -#endif // File +#endif // File diff --git a/Source/Menus/GibEditorGUI.cpp b/Source/Menus/GibEditorGUI.cpp index a690bb7c95..18ac0abc9a 100644 --- a/Source/Menus/GibEditorGUI.cpp +++ b/Source/Menus/GibEditorGUI.cpp @@ -7,7 +7,6 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -39,108 +38,102 @@ using namespace RTE; // Description: Clears all the member variables of this GibEditorGUI, effectively // resetting the members of this abstraction level only. -void GibEditorGUI::Clear() -{ - m_pController = 0; - m_EditMade = false; - m_EditorGUIMode = PICKINGGIB; - m_PreviousMode = ADDINGGIB; - m_pObjectToLoad = 0; - m_BlinkTimer.Reset(); - m_BlinkMode = NOBLINK; - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); +void GibEditorGUI::Clear() { + m_pController = 0; + m_EditMade = false; + m_EditorGUIMode = PICKINGGIB; + m_PreviousMode = ADDINGGIB; + m_pObjectToLoad = 0; + m_BlinkTimer.Reset(); + m_BlinkMode = NOBLINK; + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); m_PieMenu = nullptr; - m_pPicker = 0; - m_GridSnapping = false; - m_pZoomSource = 0; - m_ZoomFactor = 1; - m_CursorPos.Reset(); - m_CursorOffset.Reset(); - m_CursorInAir = true; - m_FacingLeft = false; - m_PlacedGibs.clear(); - m_pCurrentGib = 0; - m_GibListOrder = -1; - m_DrawCurrentGib = true; - m_pObjectToBlink = 0; + m_pPicker = 0; + m_GridSnapping = false; + m_pZoomSource = 0; + m_ZoomFactor = 1; + m_CursorPos.Reset(); + m_CursorOffset.Reset(); + m_CursorInAir = true; + m_FacingLeft = false; + m_PlacedGibs.clear(); + m_pCurrentGib = 0; + m_GibListOrder = -1; + m_DrawCurrentGib = true; + m_pObjectToBlink = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the GibEditorGUI object ready for use. -int GibEditorGUI::Create(Controller *pController, int whichModuleSpace) -{ - RTEAssert(pController, "No controller sent to GibEditorGUI on creation!"); - m_pController = pController; +int GibEditorGUI::Create(Controller* pController, int whichModuleSpace) { + RTEAssert(pController, "No controller sent to GibEditorGUI on creation!"); + m_pController = pController; - if (m_PieMenu) { m_PieMenu = nullptr; } - m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", "Gib Editor Pie Menu")->Clone())); + if (m_PieMenu) { + m_PieMenu = nullptr; + } + m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", "Gib Editor Pie Menu")->Clone())); m_PieMenu->SetMenuController(pController); - // Allocate and (re)create the Editor GUIs - if (!m_pPicker) - m_pPicker = new ObjectPickerGUI(); - else - m_pPicker->Reset(); - // Only show MovableObject:s as valid gibs to be placed - m_pPicker->Create(pController, whichModuleSpace, "MovableObject"); + // Allocate and (re)create the Editor GUIs + if (!m_pPicker) + m_pPicker = new ObjectPickerGUI(); + else + m_pPicker->Reset(); + // Only show MovableObject:s as valid gibs to be placed + m_pPicker->Create(pController, whichModuleSpace, "MovableObject"); - // Intermediate zooming bitmap - m_pZoomSource = create_bitmap_ex(8, 64, 64); + // Intermediate zooming bitmap + m_pZoomSource = create_bitmap_ex(8, 64, 64); - // Cursor init - m_CursorPos = g_SceneMan.GetSceneDim() / 2; + // Cursor init + m_CursorPos = g_SceneMan.GetSceneDim() / 2; - // Set initial focus, category list, and label settings - m_EditorGUIMode = PICKINGGIB; - m_pCurrentGib = 0; + // Set initial focus, category list, and label settings + m_EditorGUIMode = PICKINGGIB; + m_pCurrentGib = 0; - // Reset repeat timers - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); + // Reset repeat timers + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); - return 0; + return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the GibEditorGUI object. -void GibEditorGUI::Destroy() -{ - delete m_pPicker; +void GibEditorGUI::Destroy() { + delete m_pPicker; - destroy_bitmap(m_pZoomSource); + destroy_bitmap(m_pZoomSource); - for (std::list::iterator gItr = m_PlacedGibs.begin(); gItr != m_PlacedGibs.end(); ++gItr) - delete (*gItr); + for (std::list::iterator gItr = m_PlacedGibs.begin(); gItr != m_PlacedGibs.end(); ++gItr) + delete (*gItr); - delete m_pCurrentGib; + delete m_pCurrentGib; - Clear(); + Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetController ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets the controller used by this. The ownership of the controller is // NOT transferred! -void GibEditorGUI::SetController(Controller *pController) -{ - m_pController = pController; +void GibEditorGUI::SetController(Controller* pController) { + m_pController = pController; m_PieMenu->SetMenuController(pController); - m_pPicker->SetController(pController); + m_pPicker->SetController(pController); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetPosOnScreen ////////////////////////////////////////////////////////////////////////////////////////// @@ -148,146 +141,145 @@ void GibEditorGUI::SetController(Controller *pController) // left corner, then 0, 0. This will affect the way the mouse is positioned // etc. -void GibEditorGUI::SetPosOnScreen(int newPosX, int newPosY) -{ - m_pPicker->SetPosOnScreen(newPosX, newPosY); +void GibEditorGUI::SetPosOnScreen(int newPosX, int newPosY) { + m_pPicker->SetPosOnScreen(newPosX, newPosY); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetActivatedPieSlice ////////////////////////////////////////////////////////////////////////////////////////// // Description: Gets any Pie menu slice command activated last update. PieSlice::SliceType GibEditorGUI::GetActivatedPieSlice() const { - return m_PieMenu->GetPieCommand(); + return m_PieMenu->GetPieCommand(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetModuleSpace ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets which DataModule space to be picking objects from. If -1, then // let the player pick from all loaded modules. -void GibEditorGUI::SetModuleSpace(int moduleSpaceID) -{ - m_pPicker->SetModuleSpace(moduleSpaceID); +void GibEditorGUI::SetModuleSpace(int moduleSpaceID) { + m_pPicker->SetModuleSpace(moduleSpaceID); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Update ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the state of this Menu each frame -void GibEditorGUI::Update() -{ - // Update the user controller -// m_pController->Update(); - - m_pObjectToLoad = 0; - m_EditMade = false; - m_pObjectToBlink = 0; - - //////////////////////////////////////////// - // Blinking logic -/* - if (m_BlinkMode == OBJECTBLINK) - { - m_pCostLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); - } - else if (m_BlinkMode == NOCRAFT) - { - bool blink = m_BlinkTimer.AlternateSim(250); - m_pCraftLabel->SetVisible(blink); - m_pCraftBox->SetVisible(blink); - } - - // Time out the blinker - if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastSimMS(1500)) - { - m_pCostLabel->SetVisible(true); - m_pCraftLabel->SetVisible(true); - m_pCraftBox->SetVisible(true); - m_BlinkMode = NOBLINK; - } -*/ - ///////////////////////////////////////////// - // Repeating input logic - - bool pressLeft = m_pController->IsState(PRESS_LEFT); - bool pressRight = m_pController->IsState(PRESS_RIGHT); - bool pressUp = m_pController->IsState(PRESS_UP); - bool pressDown = m_pController->IsState(PRESS_DOWN); - - // If no direction is held down, then cancel the repeating - if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) - { - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - } - - // Check if any direction has been held for the starting amount of time to get into repeat mode - if (m_RepeatStartTimer.IsPastRealMS(200)) - { - // Check for the repeat interval - if (m_RepeatTimer.IsPastRealMS(50)) - { - if (m_pController->IsState(MOVE_RIGHT)) - pressRight = true; - else if (m_pController->IsState(MOVE_LEFT)) - pressLeft = true; - - if (m_pController->IsState(MOVE_UP)) - pressUp = true; - else if (m_pController->IsState(MOVE_DOWN)) - pressDown = true; - - m_RepeatTimer.Reset(); - } - } - - /////////////////////////////////////////////// - // Analog cursor input - - Vector analogInput; - if (m_pController->GetAnalogMove().MagnitudeIsGreaterThan(0.1F)) - analogInput = m_pController->GetAnalogMove(); -// else if (m_pController->GetAnalogAim().MagnitudeIsGreaterThan(0.1F)) -// analogInput = m_pController->GetAnalogAim(); - - ///////////////////////////////////////////// - // PIE MENU +void GibEditorGUI::Update() { + // Update the user controller + // m_pController->Update(); + + m_pObjectToLoad = 0; + m_EditMade = false; + m_pObjectToBlink = 0; + + //////////////////////////////////////////// + // Blinking logic + /* + if (m_BlinkMode == OBJECTBLINK) + { + m_pCostLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); + } + else if (m_BlinkMode == NOCRAFT) + { + bool blink = m_BlinkTimer.AlternateSim(250); + m_pCraftLabel->SetVisible(blink); + m_pCraftBox->SetVisible(blink); + } + + // Time out the blinker + if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastSimMS(1500)) + { + m_pCostLabel->SetVisible(true); + m_pCraftLabel->SetVisible(true); + m_pCraftBox->SetVisible(true); + m_BlinkMode = NOBLINK; + } + */ + ///////////////////////////////////////////// + // Repeating input logic + + bool pressLeft = m_pController->IsState(PRESS_LEFT); + bool pressRight = m_pController->IsState(PRESS_RIGHT); + bool pressUp = m_pController->IsState(PRESS_UP); + bool pressDown = m_pController->IsState(PRESS_DOWN); + + // If no direction is held down, then cancel the repeating + if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) { + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + } + + // Check if any direction has been held for the starting amount of time to get into repeat mode + if (m_RepeatStartTimer.IsPastRealMS(200)) { + // Check for the repeat interval + if (m_RepeatTimer.IsPastRealMS(50)) { + if (m_pController->IsState(MOVE_RIGHT)) + pressRight = true; + else if (m_pController->IsState(MOVE_LEFT)) + pressLeft = true; + + if (m_pController->IsState(MOVE_UP)) + pressUp = true; + else if (m_pController->IsState(MOVE_DOWN)) + pressDown = true; + + m_RepeatTimer.Reset(); + } + } + + /////////////////////////////////////////////// + // Analog cursor input + + Vector analogInput; + if (m_pController->GetAnalogMove().MagnitudeIsGreaterThan(0.1F)) + analogInput = m_pController->GetAnalogMove(); + // else if (m_pController->GetAnalogAim().MagnitudeIsGreaterThan(0.1F)) + // analogInput = m_pController->GetAnalogAim(); + + ///////////////////////////////////////////// + // PIE MENU m_PieMenu->Update(); - if (PieSlice *zoomInSlice = m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorZoomIn)) { zoomInSlice->SetEnabled(m_ZoomFactor < MAXZOOMFACTOR); } - if (PieSlice *zoomOutSlice = m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorZoomOut)) { zoomOutSlice->SetEnabled(m_ZoomFactor > MINZOOMFACTOR); } + if (PieSlice* zoomInSlice = m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorZoomIn)) { + zoomInSlice->SetEnabled(m_ZoomFactor < MAXZOOMFACTOR); + } + if (PieSlice* zoomOutSlice = m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorZoomOut)) { + zoomOutSlice->SetEnabled(m_ZoomFactor > MINZOOMFACTOR); + } - // Show the pie menu only when the secondary button is held down - if (m_pController->IsState(PRESS_SECONDARY) && m_EditorGUIMode != INACTIVE && m_EditorGUIMode != PICKINGGIB) { + // Show the pie menu only when the secondary button is held down + if (m_pController->IsState(PRESS_SECONDARY) && m_EditorGUIMode != INACTIVE && m_EditorGUIMode != PICKINGGIB) { m_PieMenu->SetPos(m_GridSnapping ? g_SceneMan.SnapPosition(m_CursorPos) : m_CursorPos); m_PieMenu->SetEnabled(true); - std::array infrontAndBehindPieSlices = { m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorInFront), m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorBehind) }; - for (PieSlice *pieSlice : infrontAndBehindPieSlices) { - if (pieSlice) { pieSlice->SetEnabled(m_EditorGUIMode == ADDINGGIB); } + std::array infrontAndBehindPieSlices = {m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorInFront), m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorBehind)}; + for (PieSlice* pieSlice: infrontAndBehindPieSlices) { + if (pieSlice) { + pieSlice->SetEnabled(m_EditorGUIMode == ADDINGGIB); + } } - } + } - if (!m_pController->IsState(PIE_MENU_ACTIVE) || m_EditorGUIMode == INACTIVE || m_EditorGUIMode == PICKINGGIB) { m_PieMenu->SetEnabled(false); } + if (!m_pController->IsState(PIE_MENU_ACTIVE) || m_EditorGUIMode == INACTIVE || m_EditorGUIMode == PICKINGGIB) { + m_PieMenu->SetEnabled(false); + } - /////////////////////////////////////// - // Handle pie menu selections + /////////////////////////////////////// + // Handle pie menu selections - if (m_PieMenu->GetPieCommand() != PieSlice::SliceType::NoType) { + if (m_PieMenu->GetPieCommand() != PieSlice::SliceType::NoType) { if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorPick) { m_EditorGUIMode = PICKINGGIB; } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorLoad) { - // Set up the picker to pick an MOSRotating to load - m_EditorGUIMode = PICKOBJECTTOLOAD; - m_pPicker->ShowOnlyType("MOSRotating"); + // Set up the picker to pick an MOSRotating to load + m_EditorGUIMode = PICKOBJECTTOLOAD; + m_pPicker->ShowOnlyType("MOSRotating"); } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorMove) { m_EditorGUIMode = MOVINGGIB; } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorRemove) { @@ -299,581 +291,510 @@ void GibEditorGUI::Update() } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorZoomOut && m_ZoomFactor > MINZOOMFACTOR) { m_ZoomFactor--; } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorInFront) { - m_PreviousMode = m_EditorGUIMode; - m_EditorGUIMode = PLACEINFRONT; - } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorBehind) { - m_PreviousMode = m_EditorGUIMode; - m_EditorGUIMode = PLACEBEHIND; - } - } - - ////////////////////////////////////////// - // Picker logic - - // Enable or disable the picker - m_pPicker->SetEnabled(m_EditorGUIMode == PICKOBJECTTOLOAD || m_EditorGUIMode == PICKINGGIB); - - // Update the picker GUI - m_pPicker->Update(); - - // Picking something to load into the editor - if (m_EditorGUIMode == PICKOBJECTTOLOAD) - { - g_FrameMan.ClearScreenText(); - g_FrameMan.SetScreenText("Select an object to LOAD into the gib editor ->", 0, 333); - - // Picked something! - if (m_pPicker->ObjectPicked() && !m_pPicker->IsEnabled()) - { - m_pObjectToLoad = dynamic_cast(m_pPicker->ObjectPicked()); - // Set picker back to showing all valid gib types - if (m_pObjectToLoad) - { - m_pPicker->ShowOnlyType("MovableObject"); - g_FrameMan.ClearScreenText(); - } - } - } - // Picking an object to place as a gib in currently edited object - else if (m_EditorGUIMode == PICKINGGIB) - { - g_FrameMan.SetScreenText("Select a new Gib object to add onto the edited object ->"); - - if (m_pPicker->ObjectPicked()) - { - // Assign a copy of the picked object to be the currently held one. - delete m_pCurrentGib; - if (m_pCurrentGib = dynamic_cast(m_pPicker->ObjectPicked()->Clone())) - { - // Disable any controller, if an actor - Actor *pActor = dynamic_cast(m_pCurrentGib); - if (pActor) - pActor->GetController()->SetDisabled(true); - // Set the list order to be at the end so new objects are added there - m_GibListOrder = -1; - // Update the object - m_pCurrentGib->Update(); - // If done picking, revert to moving object mode - if (m_pPicker->DonePicking()) - { - m_EditorGUIMode = ADDINGGIB; - } - } - } - } - - if (!m_pPicker->IsVisible()) - g_CameraMan.SetScreenOcclusion(Vector(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - ///////////////////////////////////// - // ADDING GIB MODE - - if (m_EditorGUIMode == ADDINGGIB && !m_PieMenu->IsEnabled()) - { - g_FrameMan.SetScreenText("Click to ADD a new gib to the edited object - Drag to place with precision", 0); - - m_DrawCurrentGib = true; - - // Trap the mouse cursor - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - - // Move the cursor according to analog or mouse input - if (!analogInput.IsZero()) - { - m_CursorPos += analogInput * 8; - // Re-enable snapping only when the cursor is moved again -// m_GridSnapping = true; - } - else if (!m_pController->GetMouseMovement().IsZero()) - { - m_CursorPos += m_pController->GetMouseMovement(); - // Re-enable snapping only when the cursor is moved again -// m_GridSnapping = true; - } - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= SCENESNAPSIZE; - if (pressRight) - m_CursorPos.m_X += SCENESNAPSIZE; - if (pressDown) - m_CursorPos.m_Y += SCENESNAPSIZE; - if (pressLeft) - m_CursorPos.m_X -= SCENESNAPSIZE; - // Re-enable snapping only when the cursor is moved again -// if (pressUp || pressRight || pressDown || pressLeft) -// m_GridSnapping = true; - } - - // Detect whether the cursor is in the air, or if it's overlapping some terrain - Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); - m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; - - // Mousewheel is used as shortcut for getting next and prev items in teh picker's object list - if (m_pController->IsState(SCROLL_UP) || m_pController->IsState(ControlState::ACTOR_NEXT)) - { - // Assign a copy of the next picked object to be the currently held one. - const SceneObject *pNewObject = m_pPicker->GetPrevObject(); - if (pNewObject) - { - delete m_pCurrentGib; - m_pCurrentGib = dynamic_cast(pNewObject->Clone()); - // Disable any controller, if an actor - Actor *pActor = dynamic_cast(m_pCurrentGib); - if (pActor) - pActor->GetController()->SetDisabled(true); - // Update the object - m_pCurrentGib->Update(); - } - } - else if (m_pController->IsState(SCROLL_DOWN) || m_pController->IsState(ControlState::ACTOR_PREV)) - { - // Assign a copy of the next picked object to be the currently held one. - const SceneObject *pNewObject = m_pPicker->GetNextObject(); - if (pNewObject) - { - delete m_pCurrentGib; - m_pCurrentGib = dynamic_cast(pNewObject->Clone()); - // Disable any controller, if an actor - Actor *pActor = dynamic_cast(m_pCurrentGib); - if (pActor) - pActor->GetController()->SetDisabled(true); - // Update the object - m_pCurrentGib->Update(); - } - } - - // Start the timer when the button is first pressed, and when the picker has deactivated - if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) - { - m_BlinkTimer.Reset(); - m_EditorGUIMode = PLACINGGIB; - m_PreviousMode = ADDINGGIB; - g_GUISound.PlacementBlip()->Play(); - } - } - - ///////////////////////////////////////////////////////////// - // PLACING MODE - - else if (m_EditorGUIMode == PLACINGGIB) - { - if (m_PreviousMode == MOVINGGIB) - g_FrameMan.SetScreenText("Click and drag on a placed gib to MOVE it - Click quickly to DETACH", 0); - else - g_FrameMan.SetScreenText("Click to ADD a new gib to the edited object - Drag to place with precision", 0); - - m_DrawCurrentGib = true; - - // Freeze when first pressing down and grid snapping is still engaged - if (!(m_pController->IsState(PRIMARY_ACTION) && m_GridSnapping)) - { - if (!analogInput.IsZero()) - { - m_CursorPos += analogInput; - m_FacingLeft = analogInput.m_X < 0 || (m_FacingLeft && analogInput.m_X == 0); - } - // Try the mouse - else if (!m_pController->GetMouseMovement().IsZero()) - { - m_CursorPos += m_pController->GetMouseMovement(); - m_FacingLeft = m_pController->GetMouseMovement().m_X < 0 || (m_FacingLeft && m_pController->GetMouseMovement().m_X == 0); - } - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= 1; - if (pressRight) - { - m_CursorPos.m_X += 1; - m_FacingLeft = false; - } - if (pressDown) - m_CursorPos.m_Y += 1; - if (pressLeft) - { - m_CursorPos.m_X -= 1; - m_FacingLeft = true; - } - } - - // Detect whether the cursor is in the air, or if it's overlapping some terrain - Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); - m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; - } - - // Disable snapping after a small interval of holding down the button, to avoid unintentional nudges when just placing on the grid - if (m_pController->IsState(PRIMARY_ACTION) && m_BlinkTimer.IsPastRealMS(333) && m_GridSnapping) - { - m_GridSnapping = false; - m_CursorPos = g_SceneMan.SnapPosition(m_CursorPos); - } - - if (m_pController->IsState(RELEASE_PRIMARY)) - - // Cancel placing if secondary button is pressed - if (m_pController->IsState(PRESS_SECONDARY) || m_pController->IsState(PIE_MENU_ACTIVE)) - { - m_EditorGUIMode = m_PreviousMode; - } - // If previous mode was moving, tear the gib loose if the button is released to soo - else if (m_PreviousMode == MOVINGGIB && m_pController->IsState(RELEASE_PRIMARY) && !m_BlinkTimer.IsPastRealMS(150)) - { - m_EditorGUIMode = ADDINGGIB; - } - // Only place if the picker and pie menus are completely out of view, to avoid immediate placing after picking - else if (m_pCurrentGib && m_pController->IsState(RELEASE_PRIMARY) && !m_pPicker->IsVisible()) - { - m_pCurrentGib->Update(); - - // Add to the placed objects list - AddPlacedObject(dynamic_cast(m_pCurrentGib->Clone()), m_GibListOrder); - // Increment the list order so we place over last placed item - if (m_GibListOrder >= 0) - m_GibListOrder++; - g_GUISound.PlacementThud()->Play(); -// g_GUISound.PlacementGravel()->Play(); - m_EditMade = true; - -// TEMP REMOVE WEHN YOU CLEAN UP THE ABOVE HARDCODED BRAIN PLACEMENT - if (m_EditorGUIMode != PICKINGGIB) -// TEMP REMOVE ABOVE - // Go back to previous mode - m_EditorGUIMode = m_PreviousMode; - } - - // Set the facing of AHumans based on right/left cursor movements - AHuman *pAHuman = dynamic_cast(m_pCurrentGib); - if (pAHuman) - pAHuman->SetHFlipped(m_FacingLeft); - } - - ///////////////////////////////////////////////////////////// - // POINTING AT MODES - - else if ((m_EditorGUIMode == MOVINGGIB || m_EditorGUIMode == DELETINGGIB || m_EditorGUIMode == PLACEINFRONT || m_EditorGUIMode == PLACEBEHIND) && !m_PieMenu->IsEnabled()) - { - m_DrawCurrentGib = false; - - // Trap the mouse cursor - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - - // Move the cursor according to analog or mouse input - if (!analogInput.IsZero()) - m_CursorPos += analogInput * 4; - else if (!m_pController->GetMouseMovement().IsZero()) - m_CursorPos += m_pController->GetMouseMovement() / 2; - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= 1; - if (pressRight) - m_CursorPos.m_X += 1; - if (pressDown) - m_CursorPos.m_Y += 1; - if (pressLeft) - m_CursorPos.m_X -= 1; - } - - ///////////////////////////////// - // MOVING GIB MODE - - if (m_EditorGUIMode == MOVINGGIB) - { - g_FrameMan.SetScreenText("Click and drag on a placed gib to MOVE it - Click quickly to DETACH", 0); - - // Pick an object under the cursor and start moving it - if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) - { - const MovableObject *pPicked = PickPlacedObject(m_CursorPos, &m_GibListOrder); - if (pPicked) - { - // Grab the position and a copy of the the object itself before killing it from the scene - m_pCurrentGib = dynamic_cast(pPicked->Clone()); - m_CursorOffset = m_CursorPos - m_pCurrentGib->GetPos(); - RemovePlacedObject(m_GibListOrder); - m_EditMade = true; - - // Go to placing mode to move it around - m_EditorGUIMode = PLACINGGIB; - m_PreviousMode = MOVINGGIB; - m_BlinkTimer.Reset(); - g_GUISound.PlacementBlip()->Play(); - g_GUISound.PlacementGravel()->Play(); - } - else - g_GUISound.UserErrorSound()->Play(); - } - } - - //////////////////////////// - // REMOVING GIB MODE - - else if (m_EditorGUIMode == DELETINGGIB) - { - g_FrameMan.SetScreenText("Click and hold to select an object - release to DELETE it", 0); - - // When primary is held down, pick object and show which one will be nuked if released - if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) - { - m_pObjectToBlink = PickPlacedObject(m_CursorPos); - } - else if (m_pController->IsState(RELEASE_PRIMARY)) - { - if (PickPlacedObject(m_CursorPos, &m_GibListOrder)) - { - // Nuke it! - RemovePlacedObject(m_GibListOrder); - m_EditMade = true; -// TODO: Add awesome destruction sound here - } - else - g_GUISound.UserErrorSound()->Play(); - } - } - - ///////////////////////////////////// - // PLACE IN FRONT AND BEHIND OF MODES - - else if (m_EditorGUIMode == PLACEINFRONT || m_EditorGUIMode == PLACEBEHIND) - { - if (m_EditorGUIMode == PLACEINFRONT) - g_FrameMan.SetScreenText("Click an object to place the next one IN FRONT of it", 0); - else if (m_EditorGUIMode == PLACEBEHIND) - g_FrameMan.SetScreenText("Click an object to place the next one BEHIND it", 0); - - // When primary is held down, pick object and show which one will be nuked if released - if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) - { - m_pObjectToBlink = PickPlacedObject(m_CursorPos); - } - else if (m_pController->IsState(RELEASE_PRIMARY)) - { - if (PickPlacedObject(m_CursorPos, &m_GibListOrder)) - { - // Adjust the next list order to be in front if applicable (it's automatically behind if same order index) - if (m_EditorGUIMode == PLACEINFRONT) - m_GibListOrder++; - - // Go back to previous mode - m_EditorGUIMode = m_PreviousMode; - } - else - g_GUISound.UserErrorSound()->Play(); - } - } - } - - // Remove cursor offset if not applicable anymore - if (m_EditorGUIMode != PLACINGGIB) - m_CursorOffset.Reset(); - - // Keep the cursor position within the world - g_SceneMan.ForceBounds(m_CursorPos); -// TODO: make setscrolltarget with 'sloppy' target - // Scroll to the cursor's scene position - g_CameraMan.SetScrollTarget(m_CursorPos, 0.3, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - // Apply the cursor position to the currently held object - if (m_pCurrentGib && m_DrawCurrentGib) - { + m_PreviousMode = m_EditorGUIMode; + m_EditorGUIMode = PLACEINFRONT; + } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorBehind) { + m_PreviousMode = m_EditorGUIMode; + m_EditorGUIMode = PLACEBEHIND; + } + } + + ////////////////////////////////////////// + // Picker logic + + // Enable or disable the picker + m_pPicker->SetEnabled(m_EditorGUIMode == PICKOBJECTTOLOAD || m_EditorGUIMode == PICKINGGIB); + + // Update the picker GUI + m_pPicker->Update(); + + // Picking something to load into the editor + if (m_EditorGUIMode == PICKOBJECTTOLOAD) { + g_FrameMan.ClearScreenText(); + g_FrameMan.SetScreenText("Select an object to LOAD into the gib editor ->", 0, 333); + + // Picked something! + if (m_pPicker->ObjectPicked() && !m_pPicker->IsEnabled()) { + m_pObjectToLoad = dynamic_cast(m_pPicker->ObjectPicked()); + // Set picker back to showing all valid gib types + if (m_pObjectToLoad) { + m_pPicker->ShowOnlyType("MovableObject"); + g_FrameMan.ClearScreenText(); + } + } + } + // Picking an object to place as a gib in currently edited object + else if (m_EditorGUIMode == PICKINGGIB) { + g_FrameMan.SetScreenText("Select a new Gib object to add onto the edited object ->"); + + if (m_pPicker->ObjectPicked()) { + // Assign a copy of the picked object to be the currently held one. + delete m_pCurrentGib; + if (m_pCurrentGib = dynamic_cast(m_pPicker->ObjectPicked()->Clone())) { + // Disable any controller, if an actor + Actor* pActor = dynamic_cast(m_pCurrentGib); + if (pActor) + pActor->GetController()->SetDisabled(true); + // Set the list order to be at the end so new objects are added there + m_GibListOrder = -1; + // Update the object + m_pCurrentGib->Update(); + // If done picking, revert to moving object mode + if (m_pPicker->DonePicking()) { + m_EditorGUIMode = ADDINGGIB; + } + } + } + } + + if (!m_pPicker->IsVisible()) + g_CameraMan.SetScreenOcclusion(Vector(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + ///////////////////////////////////// + // ADDING GIB MODE + + if (m_EditorGUIMode == ADDINGGIB && !m_PieMenu->IsEnabled()) { + g_FrameMan.SetScreenText("Click to ADD a new gib to the edited object - Drag to place with precision", 0); + + m_DrawCurrentGib = true; + + // Trap the mouse cursor + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + + // Move the cursor according to analog or mouse input + if (!analogInput.IsZero()) { + m_CursorPos += analogInput * 8; + // Re-enable snapping only when the cursor is moved again + // m_GridSnapping = true; + } else if (!m_pController->GetMouseMovement().IsZero()) { + m_CursorPos += m_pController->GetMouseMovement(); + // Re-enable snapping only when the cursor is moved again + // m_GridSnapping = true; + } + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= SCENESNAPSIZE; + if (pressRight) + m_CursorPos.m_X += SCENESNAPSIZE; + if (pressDown) + m_CursorPos.m_Y += SCENESNAPSIZE; + if (pressLeft) + m_CursorPos.m_X -= SCENESNAPSIZE; + // Re-enable snapping only when the cursor is moved again + // if (pressUp || pressRight || pressDown || pressLeft) + // m_GridSnapping = true; + } + + // Detect whether the cursor is in the air, or if it's overlapping some terrain + Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); + m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; + + // Mousewheel is used as shortcut for getting next and prev items in teh picker's object list + if (m_pController->IsState(SCROLL_UP) || m_pController->IsState(ControlState::ACTOR_NEXT)) { + // Assign a copy of the next picked object to be the currently held one. + const SceneObject* pNewObject = m_pPicker->GetPrevObject(); + if (pNewObject) { + delete m_pCurrentGib; + m_pCurrentGib = dynamic_cast(pNewObject->Clone()); + // Disable any controller, if an actor + Actor* pActor = dynamic_cast(m_pCurrentGib); + if (pActor) + pActor->GetController()->SetDisabled(true); + // Update the object + m_pCurrentGib->Update(); + } + } else if (m_pController->IsState(SCROLL_DOWN) || m_pController->IsState(ControlState::ACTOR_PREV)) { + // Assign a copy of the next picked object to be the currently held one. + const SceneObject* pNewObject = m_pPicker->GetNextObject(); + if (pNewObject) { + delete m_pCurrentGib; + m_pCurrentGib = dynamic_cast(pNewObject->Clone()); + // Disable any controller, if an actor + Actor* pActor = dynamic_cast(m_pCurrentGib); + if (pActor) + pActor->GetController()->SetDisabled(true); + // Update the object + m_pCurrentGib->Update(); + } + } + + // Start the timer when the button is first pressed, and when the picker has deactivated + if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) { + m_BlinkTimer.Reset(); + m_EditorGUIMode = PLACINGGIB; + m_PreviousMode = ADDINGGIB; + g_GUISound.PlacementBlip()->Play(); + } + } + + ///////////////////////////////////////////////////////////// + // PLACING MODE + + else if (m_EditorGUIMode == PLACINGGIB) { + if (m_PreviousMode == MOVINGGIB) + g_FrameMan.SetScreenText("Click and drag on a placed gib to MOVE it - Click quickly to DETACH", 0); + else + g_FrameMan.SetScreenText("Click to ADD a new gib to the edited object - Drag to place with precision", 0); + + m_DrawCurrentGib = true; + + // Freeze when first pressing down and grid snapping is still engaged + if (!(m_pController->IsState(PRIMARY_ACTION) && m_GridSnapping)) { + if (!analogInput.IsZero()) { + m_CursorPos += analogInput; + m_FacingLeft = analogInput.m_X < 0 || (m_FacingLeft && analogInput.m_X == 0); + } + // Try the mouse + else if (!m_pController->GetMouseMovement().IsZero()) { + m_CursorPos += m_pController->GetMouseMovement(); + m_FacingLeft = m_pController->GetMouseMovement().m_X < 0 || (m_FacingLeft && m_pController->GetMouseMovement().m_X == 0); + } + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= 1; + if (pressRight) { + m_CursorPos.m_X += 1; + m_FacingLeft = false; + } + if (pressDown) + m_CursorPos.m_Y += 1; + if (pressLeft) { + m_CursorPos.m_X -= 1; + m_FacingLeft = true; + } + } + + // Detect whether the cursor is in the air, or if it's overlapping some terrain + Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); + m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; + } + + // Disable snapping after a small interval of holding down the button, to avoid unintentional nudges when just placing on the grid + if (m_pController->IsState(PRIMARY_ACTION) && m_BlinkTimer.IsPastRealMS(333) && m_GridSnapping) { + m_GridSnapping = false; + m_CursorPos = g_SceneMan.SnapPosition(m_CursorPos); + } + + if (m_pController->IsState(RELEASE_PRIMARY)) + + // Cancel placing if secondary button is pressed + if (m_pController->IsState(PRESS_SECONDARY) || m_pController->IsState(PIE_MENU_ACTIVE)) { + m_EditorGUIMode = m_PreviousMode; + } + // If previous mode was moving, tear the gib loose if the button is released to soo + else if (m_PreviousMode == MOVINGGIB && m_pController->IsState(RELEASE_PRIMARY) && !m_BlinkTimer.IsPastRealMS(150)) { + m_EditorGUIMode = ADDINGGIB; + } + // Only place if the picker and pie menus are completely out of view, to avoid immediate placing after picking + else if (m_pCurrentGib && m_pController->IsState(RELEASE_PRIMARY) && !m_pPicker->IsVisible()) { + m_pCurrentGib->Update(); + + // Add to the placed objects list + AddPlacedObject(dynamic_cast(m_pCurrentGib->Clone()), m_GibListOrder); + // Increment the list order so we place over last placed item + if (m_GibListOrder >= 0) + m_GibListOrder++; + g_GUISound.PlacementThud()->Play(); + // g_GUISound.PlacementGravel()->Play(); + m_EditMade = true; + + // TEMP REMOVE WEHN YOU CLEAN UP THE ABOVE HARDCODED BRAIN PLACEMENT + if (m_EditorGUIMode != PICKINGGIB) + // TEMP REMOVE ABOVE + // Go back to previous mode + m_EditorGUIMode = m_PreviousMode; + } + + // Set the facing of AHumans based on right/left cursor movements + AHuman* pAHuman = dynamic_cast(m_pCurrentGib); + if (pAHuman) + pAHuman->SetHFlipped(m_FacingLeft); + } + + ///////////////////////////////////////////////////////////// + // POINTING AT MODES + + else if ((m_EditorGUIMode == MOVINGGIB || m_EditorGUIMode == DELETINGGIB || m_EditorGUIMode == PLACEINFRONT || m_EditorGUIMode == PLACEBEHIND) && !m_PieMenu->IsEnabled()) { + m_DrawCurrentGib = false; + + // Trap the mouse cursor + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + + // Move the cursor according to analog or mouse input + if (!analogInput.IsZero()) + m_CursorPos += analogInput * 4; + else if (!m_pController->GetMouseMovement().IsZero()) + m_CursorPos += m_pController->GetMouseMovement() / 2; + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= 1; + if (pressRight) + m_CursorPos.m_X += 1; + if (pressDown) + m_CursorPos.m_Y += 1; + if (pressLeft) + m_CursorPos.m_X -= 1; + } + + ///////////////////////////////// + // MOVING GIB MODE + + if (m_EditorGUIMode == MOVINGGIB) { + g_FrameMan.SetScreenText("Click and drag on a placed gib to MOVE it - Click quickly to DETACH", 0); + + // Pick an object under the cursor and start moving it + if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) { + const MovableObject* pPicked = PickPlacedObject(m_CursorPos, &m_GibListOrder); + if (pPicked) { + // Grab the position and a copy of the the object itself before killing it from the scene + m_pCurrentGib = dynamic_cast(pPicked->Clone()); + m_CursorOffset = m_CursorPos - m_pCurrentGib->GetPos(); + RemovePlacedObject(m_GibListOrder); + m_EditMade = true; + + // Go to placing mode to move it around + m_EditorGUIMode = PLACINGGIB; + m_PreviousMode = MOVINGGIB; + m_BlinkTimer.Reset(); + g_GUISound.PlacementBlip()->Play(); + g_GUISound.PlacementGravel()->Play(); + } else + g_GUISound.UserErrorSound()->Play(); + } + } + + //////////////////////////// + // REMOVING GIB MODE + + else if (m_EditorGUIMode == DELETINGGIB) { + g_FrameMan.SetScreenText("Click and hold to select an object - release to DELETE it", 0); + + // When primary is held down, pick object and show which one will be nuked if released + if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) { + m_pObjectToBlink = PickPlacedObject(m_CursorPos); + } else if (m_pController->IsState(RELEASE_PRIMARY)) { + if (PickPlacedObject(m_CursorPos, &m_GibListOrder)) { + // Nuke it! + RemovePlacedObject(m_GibListOrder); + m_EditMade = true; + // TODO: Add awesome destruction sound here + } else + g_GUISound.UserErrorSound()->Play(); + } + } + + ///////////////////////////////////// + // PLACE IN FRONT AND BEHIND OF MODES + + else if (m_EditorGUIMode == PLACEINFRONT || m_EditorGUIMode == PLACEBEHIND) { + if (m_EditorGUIMode == PLACEINFRONT) + g_FrameMan.SetScreenText("Click an object to place the next one IN FRONT of it", 0); + else if (m_EditorGUIMode == PLACEBEHIND) + g_FrameMan.SetScreenText("Click an object to place the next one BEHIND it", 0); + + // When primary is held down, pick object and show which one will be nuked if released + if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) { + m_pObjectToBlink = PickPlacedObject(m_CursorPos); + } else if (m_pController->IsState(RELEASE_PRIMARY)) { + if (PickPlacedObject(m_CursorPos, &m_GibListOrder)) { + // Adjust the next list order to be in front if applicable (it's automatically behind if same order index) + if (m_EditorGUIMode == PLACEINFRONT) + m_GibListOrder++; + + // Go back to previous mode + m_EditorGUIMode = m_PreviousMode; + } else + g_GUISound.UserErrorSound()->Play(); + } + } + } + + // Remove cursor offset if not applicable anymore + if (m_EditorGUIMode != PLACINGGIB) + m_CursorOffset.Reset(); + + // Keep the cursor position within the world + g_SceneMan.ForceBounds(m_CursorPos); + // TODO: make setscrolltarget with 'sloppy' target + // Scroll to the cursor's scene position + g_CameraMan.SetScrollTarget(m_CursorPos, 0.3, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + // Apply the cursor position to the currently held object + if (m_pCurrentGib && m_DrawCurrentGib) { Vector gibPos = g_SceneMan.SnapPosition(m_CursorPos - m_CursorOffset, m_GridSnapping); gibPos.SetX(static_cast(gibPos.m_X)); gibPos.SetY(static_cast(gibPos.m_Y)); - m_pCurrentGib->SetPos(gibPos); - m_pCurrentGib->Update(); - } + m_pCurrentGib->SetPos(gibPos); + m_pCurrentGib->Update(); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws the menu -void GibEditorGUI::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) const -{ - // Done, so don't draw the UI - if (m_EditorGUIMode == DONEEDITING) - return; - - // Draw all already placed Objects, and the currently held one in the order it is about to be placed in the scene - int i = 0; - for (std::list::const_iterator itr = m_PlacedGibs.begin(); itr != m_PlacedGibs.end(); ++itr, ++i) - { - // Draw the currently held object into the order of the list if it is to be placed inside - if (m_pCurrentGib && m_DrawCurrentGib && i == m_GibListOrder) - { - g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGGIB ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - m_pCurrentGib->Draw(pTargetBitmap, targetPos, g_DrawTrans); - Actor *pActor = dynamic_cast(m_pCurrentGib); - if (pActor) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - - // Blink trans if we are supposed to blink this one - if ((*itr) == m_pObjectToBlink) - { - g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - (*itr)->Draw(pTargetBitmap, targetPos, g_DrawTrans); - } - else - (*itr)->Draw(pTargetBitmap, targetPos); - - // Draw basic HUD if an actor - Actor *pActor = dynamic_cast(*itr); - if (pActor) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - - // Draw picking object crosshairs and not the selected object - if (!m_DrawCurrentGib) - { - Vector center = m_CursorPos - targetPos; - putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); - } - // If the held object will be placed at the end of the list, draw it last to the scene, transperent blinking - else if (m_pCurrentGib && (m_GibListOrder < 0 || m_GibListOrder == m_PlacedGibs.size())) - { - g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGGIB ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - m_pCurrentGib->Draw(pTargetBitmap, targetPos, g_DrawTrans); - Actor *pActor = dynamic_cast(m_pCurrentGib); - if (pActor) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - - // Draw the zoom window, if active - if (m_ZoomFactor > 1) - { - Vector sourceCenter = m_CursorPos - targetPos; - - // Make sure the source is within the target bitmap - int halfWidth = m_pZoomSource->w / 2; - if (sourceCenter.m_X - halfWidth < 0) - sourceCenter.m_X = halfWidth; - else if (sourceCenter.m_X + halfWidth >= pTargetBitmap->w) - sourceCenter.m_X = pTargetBitmap->w - halfWidth; - int halfHeight = m_pZoomSource->w / 2; - if (sourceCenter.m_Y - halfHeight < 0) - sourceCenter.m_Y = halfHeight; - else if (sourceCenter.m_Y + halfHeight >= pTargetBitmap->h) - sourceCenter.m_Y = pTargetBitmap->h - halfHeight; - - // Copy to the intermediate source bitmap - blit(pTargetBitmap, m_pZoomSource, sourceCenter.m_X - halfWidth, sourceCenter.m_Y - halfHeight, 0, 0, m_pZoomSource->w, m_pZoomSource->h); - - - Vector zoomedCenter = m_CursorPos - targetPos; - - // Make sure the zoomed view is within the target bitmap - halfWidth = (m_pZoomSource->w / 2) * m_ZoomFactor; - if (zoomedCenter.m_X - halfWidth < 0) - zoomedCenter.m_X = halfWidth; - else if (zoomedCenter.m_X + halfWidth >= pTargetBitmap->w) - zoomedCenter.m_X = pTargetBitmap->w - halfWidth; - halfHeight = (m_pZoomSource->w / 2) * m_ZoomFactor; - if (zoomedCenter.m_Y - halfHeight < 0) - zoomedCenter.m_Y = halfHeight; - else if (zoomedCenter.m_Y + halfHeight >= pTargetBitmap->h) - zoomedCenter.m_Y = pTargetBitmap->h - halfHeight; - - // Then draw right back but stretched to the target - stretch_blit(m_pZoomSource, pTargetBitmap, 0, 0, m_pZoomSource->w, m_pZoomSource->h, zoomedCenter.m_X - halfWidth, zoomedCenter.m_Y - halfHeight, m_pZoomSource->w * m_ZoomFactor, m_pZoomSource->h * m_ZoomFactor); - rect(pTargetBitmap, zoomedCenter.m_X - halfWidth, zoomedCenter.m_Y - halfHeight, zoomedCenter.m_X + halfWidth - 1, zoomedCenter.m_Y + halfHeight - 1, g_YellowGlowColor); - } - - m_pPicker->Draw(pTargetBitmap); - - // Draw the pie menu +void GibEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { + // Done, so don't draw the UI + if (m_EditorGUIMode == DONEEDITING) + return; + + // Draw all already placed Objects, and the currently held one in the order it is about to be placed in the scene + int i = 0; + for (std::list::const_iterator itr = m_PlacedGibs.begin(); itr != m_PlacedGibs.end(); ++itr, ++i) { + // Draw the currently held object into the order of the list if it is to be placed inside + if (m_pCurrentGib && m_DrawCurrentGib && i == m_GibListOrder) { + g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGGIB ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + m_pCurrentGib->Draw(pTargetBitmap, targetPos, g_DrawTrans); + Actor* pActor = dynamic_cast(m_pCurrentGib); + if (pActor) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + + // Blink trans if we are supposed to blink this one + if ((*itr) == m_pObjectToBlink) { + g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + (*itr)->Draw(pTargetBitmap, targetPos, g_DrawTrans); + } else + (*itr)->Draw(pTargetBitmap, targetPos); + + // Draw basic HUD if an actor + Actor* pActor = dynamic_cast(*itr); + if (pActor) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + + // Draw picking object crosshairs and not the selected object + if (!m_DrawCurrentGib) { + Vector center = m_CursorPos - targetPos; + putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); + } + // If the held object will be placed at the end of the list, draw it last to the scene, transperent blinking + else if (m_pCurrentGib && (m_GibListOrder < 0 || m_GibListOrder == m_PlacedGibs.size())) { + g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGGIB ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + m_pCurrentGib->Draw(pTargetBitmap, targetPos, g_DrawTrans); + Actor* pActor = dynamic_cast(m_pCurrentGib); + if (pActor) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + + // Draw the zoom window, if active + if (m_ZoomFactor > 1) { + Vector sourceCenter = m_CursorPos - targetPos; + + // Make sure the source is within the target bitmap + int halfWidth = m_pZoomSource->w / 2; + if (sourceCenter.m_X - halfWidth < 0) + sourceCenter.m_X = halfWidth; + else if (sourceCenter.m_X + halfWidth >= pTargetBitmap->w) + sourceCenter.m_X = pTargetBitmap->w - halfWidth; + int halfHeight = m_pZoomSource->w / 2; + if (sourceCenter.m_Y - halfHeight < 0) + sourceCenter.m_Y = halfHeight; + else if (sourceCenter.m_Y + halfHeight >= pTargetBitmap->h) + sourceCenter.m_Y = pTargetBitmap->h - halfHeight; + + // Copy to the intermediate source bitmap + blit(pTargetBitmap, m_pZoomSource, sourceCenter.m_X - halfWidth, sourceCenter.m_Y - halfHeight, 0, 0, m_pZoomSource->w, m_pZoomSource->h); + + Vector zoomedCenter = m_CursorPos - targetPos; + + // Make sure the zoomed view is within the target bitmap + halfWidth = (m_pZoomSource->w / 2) * m_ZoomFactor; + if (zoomedCenter.m_X - halfWidth < 0) + zoomedCenter.m_X = halfWidth; + else if (zoomedCenter.m_X + halfWidth >= pTargetBitmap->w) + zoomedCenter.m_X = pTargetBitmap->w - halfWidth; + halfHeight = (m_pZoomSource->w / 2) * m_ZoomFactor; + if (zoomedCenter.m_Y - halfHeight < 0) + zoomedCenter.m_Y = halfHeight; + else if (zoomedCenter.m_Y + halfHeight >= pTargetBitmap->h) + zoomedCenter.m_Y = pTargetBitmap->h - halfHeight; + + // Then draw right back but stretched to the target + stretch_blit(m_pZoomSource, pTargetBitmap, 0, 0, m_pZoomSource->w, m_pZoomSource->h, zoomedCenter.m_X - halfWidth, zoomedCenter.m_Y - halfHeight, m_pZoomSource->w * m_ZoomFactor, m_pZoomSource->h * m_ZoomFactor); + rect(pTargetBitmap, zoomedCenter.m_X - halfWidth, zoomedCenter.m_Y - halfHeight, zoomedCenter.m_X + halfWidth - 1, zoomedCenter.m_Y + halfHeight - 1, g_YellowGlowColor); + } + + m_pPicker->Draw(pTargetBitmap); + + // Draw the pie menu m_PieMenu->Draw(pTargetBitmap, targetPos); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: AddPlacedObject ////////////////////////////////////////////////////////////////////////////////////////// // Description: Adds a MovableObject to be placed in this scene. Ownership IS transferred! -void GibEditorGUI::AddPlacedObject(MovableObject *pObjectToAdd, int listOrder) -{ - if (!pObjectToAdd) - return; - - if (listOrder < 0 || listOrder >= m_PlacedGibs.size()) - m_PlacedGibs.push_back(pObjectToAdd); - else - { - // Find the spot - std::list::iterator itr = m_PlacedGibs.begin(); - for (int i = 0; i != listOrder && itr != m_PlacedGibs.end(); ++i, ++itr) - ; - - // Put 'er in - m_PlacedGibs.insert(itr, pObjectToAdd); - } +void GibEditorGUI::AddPlacedObject(MovableObject* pObjectToAdd, int listOrder) { + if (!pObjectToAdd) + return; + + if (listOrder < 0 || listOrder >= m_PlacedGibs.size()) + m_PlacedGibs.push_back(pObjectToAdd); + else { + // Find the spot + std::list::iterator itr = m_PlacedGibs.begin(); + for (int i = 0; i != listOrder && itr != m_PlacedGibs.end(); ++i, ++itr) + ; + + // Put 'er in + m_PlacedGibs.insert(itr, pObjectToAdd); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: RemovePlacedObject ////////////////////////////////////////////////////////////////////////////////////////// // Description: Removes a MovableObject placed in this scene. -void GibEditorGUI::RemovePlacedObject(int whichToRemove) -{ - if (m_PlacedGibs.empty()) - return; - - if (whichToRemove < 0 || whichToRemove >= m_PlacedGibs.size()) - { - delete m_PlacedGibs.back(); - m_PlacedGibs.pop_back(); - } - else - { - // Find the spot - std::list::iterator itr = m_PlacedGibs.begin(); - for (int i = 0; i != whichToRemove && itr != m_PlacedGibs.end(); ++i, ++itr) - ; - - delete (*itr); - m_PlacedGibs.erase(itr); - } +void GibEditorGUI::RemovePlacedObject(int whichToRemove) { + if (m_PlacedGibs.empty()) + return; + + if (whichToRemove < 0 || whichToRemove >= m_PlacedGibs.size()) { + delete m_PlacedGibs.back(); + m_PlacedGibs.pop_back(); + } else { + // Find the spot + std::list::iterator itr = m_PlacedGibs.begin(); + for (int i = 0; i != whichToRemove && itr != m_PlacedGibs.end(); ++i, ++itr) + ; + + delete (*itr); + m_PlacedGibs.erase(itr); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: PickPlacedObject ////////////////////////////////////////////////////////////////////////////////////////// // Description: Returns the last placed object that graphically overlaps an absolute // point in the scene. -const MovableObject * GibEditorGUI::PickPlacedObject(Vector &scenePoint, int *pListOrderPlace) const -{ - // REVERSE! - int i = m_PlacedGibs.size() - 1; - for (std::list::const_reverse_iterator itr = m_PlacedGibs.rbegin(); itr != m_PlacedGibs.rend(); ++itr, --i) - { - if ((*itr)->IsOnScenePoint(scenePoint)) - { - if (pListOrderPlace) - *pListOrderPlace = i; - return *itr; - } - } - - if (pListOrderPlace) - *pListOrderPlace = -1; - return 0; -} +const MovableObject* GibEditorGUI::PickPlacedObject(Vector& scenePoint, int* pListOrderPlace) const { + // REVERSE! + int i = m_PlacedGibs.size() - 1; + for (std::list::const_reverse_iterator itr = m_PlacedGibs.rbegin(); itr != m_PlacedGibs.rend(); ++itr, --i) { + if ((*itr)->IsOnScenePoint(scenePoint)) { + if (pListOrderPlace) + *pListOrderPlace = i; + return *itr; + } + } + if (pListOrderPlace) + *pListOrderPlace = -1; + return 0; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePlacedObjects @@ -881,10 +802,8 @@ const MovableObject * GibEditorGUI::PickPlacedObject(Vector &scenePoint, int *pL // Description: Updated the objects in the placed scene objects list of this. This is // mostly for the editor to represent the items correctly. -void GibEditorGUI::UpdatePlacedObjects() -{ - for (std::list::iterator itr = m_PlacedGibs.begin(); itr != m_PlacedGibs.end(); ++itr) - { - (*itr)->Update(); - } +void GibEditorGUI::UpdatePlacedObjects() { + for (std::list::iterator itr = m_PlacedGibs.begin(); itr != m_PlacedGibs.end(); ++itr) { + (*itr)->Update(); + } } \ No newline at end of file diff --git a/Source/Menus/GibEditorGUI.h b/Source/Menus/GibEditorGUI.h index fd15e09e5f..3d25286313 100644 --- a/Source/Menus/GibEditorGUI.h +++ b/Source/Menus/GibEditorGUI.h @@ -10,11 +10,10 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files -//#include "FrameMan.h" +// #include "FrameMan.h" #include "Timer.h" #include "Vector.h" #include "Controller.h" @@ -22,387 +21,351 @@ struct BITMAP; - -namespace RTE -{ - -class MovableObject; -class MOSRotating; -class ObjectPickerGUI; -class PieMenu; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: GibEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A placement part of the gib editor which manages the pie menu and picker. -// Parent(s): None. -// Class history: 9/16/2007 GibEditorGUI Created. - -class GibEditorGUI { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - // Different modes of this editor - enum EditorGUIMode - { - INACTIVE = 0, - PICKOBJECTTOLOAD, - PICKINGGIB, - ADDINGGIB, - PLACINGGIB, - MOVINGGIB, - DELETINGGIB, - PLACEINFRONT, - PLACEBEHIND, - DONEEDITING, - EDITORGUIMODECOUNT - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: GibEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a GibEditorGUI object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - GibEditorGUI() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~GibEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a GibEditorGUI object before deletion -// from system memory. -// Arguments: None. - - ~GibEditorGUI() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GibEditorGUI object ready for use. -// Arguments: A poitner to a Controller which will control this Menu. Ownership is -// NOT TRANSFERRED! -// Which module space that this eidtor will be able to pick objects from. -// -1 means all modules. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(Controller *pController, int whichModuleSpace = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire GibEditorGUI, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GibEditorGUI object. -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! -// Arguments: The new controller for this menu. Ownership is NOT transferred -// Return value: None. - - void SetController(Controller *pController); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. -// Arguments: The new screen position of this entire GUI. - - void SetPosOnScreen(int newPosX, int newPosY); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCursorPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the absolute scene coordinates of the cursor of this Editor. -// Arguments: The new cursor position in absolute scene units. -// Return value: None. - - void SetCursorPos(const Vector &newCursorPos) { m_CursorPos = newCursorPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCurrentGib -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the new Object to be held at the gib cursor of this Editor. Ownership -// IS transferred! -// Arguments: The new Object to be held by the cursor. Ownership IS transferred! -// Return value: None. - - void SetCurrentGib(MovableObject *pNewGibObject) { m_pCurrentGib = pNewGibObject; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActivatedPieSlice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets any Pie menu slice command activated last update. -// Arguments: None. -// Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. - - PieSlice::SliceType GetActivatedPieSlice() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlacedGibs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the list of already placed gibs of the currently edited object. -// Ownership of neither list not objects IS NOT transferred! -// Arguments: None. -// Return value: The current list of placed gibs. OWNERSHIP IS NOT TRANSFERRED! - - std::list * GetPlacedGibs() { return &m_PlacedGibs; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentGib -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the currently held Object in the cursor of this Editor. Ownership -// IS NOT transferred! -// Arguments: None. -// Return value: The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! - - const MovableObject * GetCurrentGib() { return m_pCurrentGib; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetObjectToLoad -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns a refernece instance of an object if it has been picked to be -// loaded into the editor. Ownership is NOT transferred. -// Arguments: None. -// Return value: Reference instance of the picked object to be loaded. 0 if nothing. OWNERSHIP IS NOT TRANSFERRED! -// was picked since last update. - - const MOSRotating * GetObjectToLoad() { return m_pObjectToLoad; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule space to be picking objects from. If -1, then -// let the player pick from all loaded modules. -// Arguments: The ID of the module to let the player pick objects from. All official -// modules' objects will alwayws be presented, in addition to the one -// passed in here. -// Return value: None. - - void SetModuleSpace(int moduleSpaceID = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EditMade -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether an edit on the scene was made in the last Update. -// Arguments: None. -// Return value: Whether any edit was made. - - bool EditMade() const { return m_EditMade; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the editor -// Arguments: The bitmap to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a MovableObject to be placed in the editor. Ownership IS transferred! -// Arguments: The MovableOjbect instace to add, OIT! -// Where in the list the object should be inserted. -1 means at the end -// of the list. -// Return value: None. - - void AddPlacedObject(MovableObject *pObjectToAdd, int listOrder = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemovePlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a MovableObject placed in this editor. -// Arguments: The list order number of the object to remove. If -1, the last one is removed. -// Return value: None. - - void RemovePlacedObject(int whichToRemove = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PickPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the last placed object that graphically overlaps an absolute -// point in the scene. Note that the placed object don't really exist in -// the scene, but in the editor. Their own Pos's are used. -// Arguments: The point in absolute scene coordinates that will be used to pick the -// last placed MovableObject which overlaps it. -// An int which will be filled out with the order place of any found object -// in the list. if nothing is found, it will get a value of -1. -// Return value: The last hit MovableObject, if any. Ownership is NOT transferred! - - const MovableObject * PickPlacedObject(Vector &scenePoint, int *pListOrderPlace = 0) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlacedObjects -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updated the objects in the placed scene objects list of this. This is -// mostly for the editor to represent the items correctly. -// Arguments: None. -// Return value: None. - - void UpdatePlacedObjects(); - - - enum BlinkMode - { - NOBLINK = 0, - OBJECTBLINKON, - OBJECTBLINKOFF, - BLINKMODECOUNT - }; - - // Controller which conrols this menu. Not owned - Controller *m_pController; - // Whether an editor was made to the Scene in the last Update - bool m_EditMade; - // The current mode of the whole GUI. See EditorGUIMode enum. - EditorGUIMode m_EditorGUIMode; - // The previous mode of the whole GUI, to go back to when the current mode is done in some cases - EditorGUIMode m_PreviousMode; - // The ref instance picked to be loaded into the editor from the picker. 0 if none has been yet. Not owned. - const MOSRotating *m_pObjectToLoad; - // Notification blink timer - Timer m_BlinkTimer; - // What we're blinking - int m_BlinkMode; - // Measures the time to when to start repeating inputs when they're held down - Timer m_RepeatStartTimer; - // Measures the interval between input repeats - Timer m_RepeatTimer; - - std::unique_ptr m_PieMenu; //!< The PieMenu for this GibEditorGUI. - // The object picker - ObjectPickerGUI *m_pPicker; - // Grid snapping enabled - bool m_GridSnapping; - // The zooming bitmaps owned by this; source gets the area from the screen, destination is the zoomed in view - BITMAP *m_pZoomSource; - // The zoom factor of the magnifying window. 0 means no zoom window - int m_ZoomFactor; - // Current cursor position, in absolute scene coordinates - Vector m_CursorPos; - // The offset from the current object's position to the cursor, if any - Vector m_CursorOffset; - // Cursor position in free air, or over something - bool m_CursorInAir; - // Gib facing left or not when placing - bool m_FacingLeft; - // List of all the gibs currently placed on the edited object. - // They are proxies of the actual gib list of the edited object, and are OWNED by editor. - std::list m_PlacedGibs; - // Currently held object to place as a gib. This is what is attached to the cursor and will be placed when the fire button is pressed - // OWNED by this. - MovableObject *m_pCurrentGib; - // Where in the scene's list order the next object should be placed. If -1, then place at the end of the list. - int m_GibListOrder; - // Whether to draw the currently held object - bool m_DrawCurrentGib; - // Currently placed scene object to make blink when drawing it. NOT OWNED. - const MovableObject *m_pObjectToBlink; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this GibEditorGUI, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - GibEditorGUI(const GibEditorGUI &reference) = delete; - GibEditorGUI & operator=(const GibEditorGUI &rhs) = delete; - -}; +namespace RTE { + + class MovableObject; + class MOSRotating; + class ObjectPickerGUI; + class PieMenu; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: GibEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A placement part of the gib editor which manages the pie menu and picker. + // Parent(s): None. + // Class history: 9/16/2007 GibEditorGUI Created. + + class GibEditorGUI { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + // Different modes of this editor + enum EditorGUIMode { + INACTIVE = 0, + PICKOBJECTTOLOAD, + PICKINGGIB, + ADDINGGIB, + PLACINGGIB, + MOVINGGIB, + DELETINGGIB, + PLACEINFRONT, + PLACEBEHIND, + DONEEDITING, + EDITORGUIMODECOUNT + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: GibEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a GibEditorGUI object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + GibEditorGUI() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~GibEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a GibEditorGUI object before deletion + // from system memory. + // Arguments: None. + + ~GibEditorGUI() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the GibEditorGUI object ready for use. + // Arguments: A poitner to a Controller which will control this Menu. Ownership is + // NOT TRANSFERRED! + // Which module space that this eidtor will be able to pick objects from. + // -1 means all modules. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(Controller* pController, int whichModuleSpace = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire GibEditorGUI, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the GibEditorGUI object. + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetController + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the controller used by this. The ownership of the controller is + // NOT transferred! + // Arguments: The new controller for this menu. Ownership is NOT transferred + // Return value: None. + + void SetController(Controller* pController); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPosOnScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets where on the screen that this GUI is being drawn to. If upper + // left corner, then 0, 0. This will affect the way the mouse is positioned + // etc. + // Arguments: The new screen position of this entire GUI. + + void SetPosOnScreen(int newPosX, int newPosY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCursorPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the absolute scene coordinates of the cursor of this Editor. + // Arguments: The new cursor position in absolute scene units. + // Return value: None. + + void SetCursorPos(const Vector& newCursorPos) { m_CursorPos = newCursorPos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCurrentGib + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the new Object to be held at the gib cursor of this Editor. Ownership + // IS transferred! + // Arguments: The new Object to be held by the cursor. Ownership IS transferred! + // Return value: None. + + void SetCurrentGib(MovableObject* pNewGibObject) { m_pCurrentGib = pNewGibObject; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActivatedPieSlice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets any Pie menu slice command activated last update. + // Arguments: None. + // Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. + + PieSlice::SliceType GetActivatedPieSlice() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlacedGibs + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the list of already placed gibs of the currently edited object. + // Ownership of neither list not objects IS NOT transferred! + // Arguments: None. + // Return value: The current list of placed gibs. OWNERSHIP IS NOT TRANSFERRED! + + std::list* GetPlacedGibs() { return &m_PlacedGibs; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentGib + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the currently held Object in the cursor of this Editor. Ownership + // IS NOT transferred! + // Arguments: None. + // Return value: The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! + + const MovableObject* GetCurrentGib() { return m_pCurrentGib; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetObjectToLoad + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns a refernece instance of an object if it has been picked to be + // loaded into the editor. Ownership is NOT transferred. + // Arguments: None. + // Return value: Reference instance of the picked object to be loaded. 0 if nothing. OWNERSHIP IS NOT TRANSFERRED! + // was picked since last update. + + const MOSRotating* GetObjectToLoad() { return m_pObjectToLoad; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetModuleSpace + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which DataModule space to be picking objects from. If -1, then + // let the player pick from all loaded modules. + // Arguments: The ID of the module to let the player pick objects from. All official + // modules' objects will alwayws be presented, in addition to the one + // passed in here. + // Return value: None. + + void SetModuleSpace(int moduleSpaceID = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EditMade + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether an edit on the scene was made in the last Update. + // Arguments: None. + // Return value: Whether any edit was made. + + bool EditMade() const { return m_EditMade; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this Menu each frame + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the editor + // Arguments: The bitmap to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AddPlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Adds a MovableObject to be placed in the editor. Ownership IS transferred! + // Arguments: The MovableOjbect instace to add, OIT! + // Where in the list the object should be inserted. -1 means at the end + // of the list. + // Return value: None. + + void AddPlacedObject(MovableObject* pObjectToAdd, int listOrder = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemovePlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a MovableObject placed in this editor. + // Arguments: The list order number of the object to remove. If -1, the last one is removed. + // Return value: None. + + void RemovePlacedObject(int whichToRemove = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PickPlacedObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Returns the last placed object that graphically overlaps an absolute + // point in the scene. Note that the placed object don't really exist in + // the scene, but in the editor. Their own Pos's are used. + // Arguments: The point in absolute scene coordinates that will be used to pick the + // last placed MovableObject which overlaps it. + // An int which will be filled out with the order place of any found object + // in the list. if nothing is found, it will get a value of -1. + // Return value: The last hit MovableObject, if any. Ownership is NOT transferred! + + const MovableObject* PickPlacedObject(Vector& scenePoint, int* pListOrderPlace = 0) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePlacedObjects + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updated the objects in the placed scene objects list of this. This is + // mostly for the editor to represent the items correctly. + // Arguments: None. + // Return value: None. + + void UpdatePlacedObjects(); + + enum BlinkMode { + NOBLINK = 0, + OBJECTBLINKON, + OBJECTBLINKOFF, + BLINKMODECOUNT + }; + + // Controller which conrols this menu. Not owned + Controller* m_pController; + // Whether an editor was made to the Scene in the last Update + bool m_EditMade; + // The current mode of the whole GUI. See EditorGUIMode enum. + EditorGUIMode m_EditorGUIMode; + // The previous mode of the whole GUI, to go back to when the current mode is done in some cases + EditorGUIMode m_PreviousMode; + // The ref instance picked to be loaded into the editor from the picker. 0 if none has been yet. Not owned. + const MOSRotating* m_pObjectToLoad; + // Notification blink timer + Timer m_BlinkTimer; + // What we're blinking + int m_BlinkMode; + // Measures the time to when to start repeating inputs when they're held down + Timer m_RepeatStartTimer; + // Measures the interval between input repeats + Timer m_RepeatTimer; + + std::unique_ptr m_PieMenu; //!< The PieMenu for this GibEditorGUI. + // The object picker + ObjectPickerGUI* m_pPicker; + // Grid snapping enabled + bool m_GridSnapping; + // The zooming bitmaps owned by this; source gets the area from the screen, destination is the zoomed in view + BITMAP* m_pZoomSource; + // The zoom factor of the magnifying window. 0 means no zoom window + int m_ZoomFactor; + // Current cursor position, in absolute scene coordinates + Vector m_CursorPos; + // The offset from the current object's position to the cursor, if any + Vector m_CursorOffset; + // Cursor position in free air, or over something + bool m_CursorInAir; + // Gib facing left or not when placing + bool m_FacingLeft; + // List of all the gibs currently placed on the edited object. + // They are proxies of the actual gib list of the edited object, and are OWNED by editor. + std::list m_PlacedGibs; + // Currently held object to place as a gib. This is what is attached to the cursor and will be placed when the fire button is pressed + // OWNED by this. + MovableObject* m_pCurrentGib; + // Where in the scene's list order the next object should be placed. If -1, then place at the end of the list. + int m_GibListOrder; + // Whether to draw the currently held object + bool m_DrawCurrentGib; + // Currently placed scene object to make blink when drawing it. NOT OWNED. + const MovableObject* m_pObjectToBlink; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this GibEditorGUI, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + GibEditorGUI(const GibEditorGUI& reference) = delete; + GibEditorGUI& operator=(const GibEditorGUI& rhs) = delete; + }; } // namespace RTE -#endif // File \ No newline at end of file +#endif // File \ No newline at end of file diff --git a/Source/Menus/InventoryMenuGUI.cpp b/Source/Menus/InventoryMenuGUI.cpp index db53148ea3..4876550034 100644 --- a/Source/Menus/InventoryMenuGUI.cpp +++ b/Source/Menus/InventoryMenuGUI.cpp @@ -28,14 +28,14 @@ namespace RTE { const Vector InventoryMenuGUI::c_CarouselBoxSizeStep = (c_CarouselBoxMaxSize - c_CarouselBoxMinSize) / (c_ItemsPerRow / 2); const int InventoryMenuGUI::c_CarouselBoxCornerRadius = ((c_CarouselBoxMaxSize.GetFloorIntY() - c_CarouselBoxMinSize.GetFloorIntY()) / 2) - 1; - BITMAP *InventoryMenuGUI::s_CursorBitmap = nullptr; + BITMAP* InventoryMenuGUI::s_CursorBitmap = nullptr; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::CarouselItemBox::GetIconsAndMass(std::vector &itemIcons, float &totalItemMass, const std::vector> *equippedItems) const { + void InventoryMenuGUI::CarouselItemBox::GetIconsAndMass(std::vector& itemIcons, float& totalItemMass, const std::vector>* equippedItems) const { if (IsForEquippedItems) { itemIcons.reserve(equippedItems->size()); - for (const auto &[equippedItem, offhandEquippedItem] : *equippedItems) { + for (const auto& [equippedItem, offhandEquippedItem]: *equippedItems) { if (equippedItem && equippedItem->GetUniqueID() != 0) { itemIcons.emplace_back(equippedItem->GetGraphicalIcon()); totalItemMass += equippedItem->GetMass(); @@ -51,7 +51,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::Clear() { m_SmallFont = nullptr; @@ -78,7 +78,7 @@ namespace RTE { m_CarouselAnimationDirection = CarouselAnimationDirection::None; m_CarouselAnimationTimer.Reset(); m_CarouselAnimationTimer.SetRealTimeLimitMS(200); - for (std::unique_ptr &carouselItemBox : m_CarouselItemBoxes) { + for (std::unique_ptr& carouselItemBox: m_CarouselItemBoxes) { carouselItemBox = nullptr; } m_CarouselExitingItemBox.reset(); @@ -123,14 +123,18 @@ namespace RTE { m_GUIInventoryItemsScrollbar = nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InventoryMenuGUI::Create(Controller *activityPlayerController, Actor *inventoryActor, MenuMode menuMode) { + int InventoryMenuGUI::Create(Controller* activityPlayerController, Actor* inventoryActor, MenuMode menuMode) { RTEAssert(activityPlayerController, "No controller sent to InventoryMenuGUI on creation!"); RTEAssert(c_ItemsPerRow % 2 == 1, "Don't you dare use an even number of items per inventory row, you filthy animal!"); - if (!m_SmallFont) { m_SmallFont = g_FrameMan.GetSmallFont(); } - if (!m_LargeFont) { m_LargeFont = g_FrameMan.GetLargeFont(); } + if (!m_SmallFont) { + m_SmallFont = g_FrameMan.GetSmallFont(); + } + if (!m_LargeFont) { + m_LargeFont = g_FrameMan.GetLargeFont(); + } m_MenuController = activityPlayerController; SetInventoryActor(inventoryActor); @@ -144,7 +148,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::Destroy() { destroy_bitmap(m_CarouselBitmap.release()); @@ -153,10 +157,10 @@ namespace RTE { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int InventoryMenuGUI::SetupCarouselMode() { - for (std::unique_ptr &carouselItemBox : m_CarouselItemBoxes) { + for (std::unique_ptr& carouselItemBox: m_CarouselItemBoxes) { carouselItemBox = std::make_unique(); carouselItemBox->IsForEquippedItems = false; } @@ -188,17 +192,23 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int InventoryMenuGUI::SetupFullOrTransferMode() { - if (!m_GUIControlManager) { m_GUIControlManager = std::make_unique(); } - if (!m_GUIScreen) { m_GUIScreen = std::make_unique(g_FrameMan.GetBackBuffer8()); } - if (!m_GUIInput) { m_GUIInput = std::make_unique(m_MenuController->GetPlayer()); } + if (!m_GUIControlManager) { + m_GUIControlManager = std::make_unique(); + } + if (!m_GUIScreen) { + m_GUIScreen = std::make_unique(g_FrameMan.GetBackBuffer8()); + } + if (!m_GUIInput) { + m_GUIInput = std::make_unique(m_MenuController->GetPlayer()); + } RTEAssert(m_GUIControlManager->Create(m_GUIScreen.get(), m_GUIInput.get(), "Base.rte/GUIs/Skins", "InventoryMenuSkin.ini"), "Failed to create InventoryMenuGUI GUIControlManager and load it from Base.rte/GUIs/Skins/Menus/InventoryMenuSkin.ini"); - //TODO When this is split into 2 classes, full mode should use the fonts from its gui control manager while transfer mode, will need to get its fonts from FrameMan. May be good for the ingame menu base class to have these font pointers, even if some subclasses set em up in different ways. - //if (!m_SmallFont) { m_SmallFont = m_GUIControlManager->GetSkin()->GetFont("FontSmall.png"); } - //if (!m_LargeFont) { m_LargeFont = m_GUIControlManager->GetSkin()->GetFont("FontLarge.png"); } + // TODO When this is split into 2 classes, full mode should use the fonts from its gui control manager while transfer mode, will need to get its fonts from FrameMan. May be good for the ingame menu base class to have these font pointers, even if some subclasses set em up in different ways. + // if (!m_SmallFont) { m_SmallFont = m_GUIControlManager->GetSkin()->GetFont("FontSmall.png"); } + // if (!m_LargeFont) { m_LargeFont = m_GUIControlManager->GetSkin()->GetFont("FontLarge.png"); } m_GUIControlManager->Load("Base.rte/GUIs/InventoryMenuGUI.ini"); m_GUIControlManager->EnableMouse(m_MenuController->IsMouseControlled()); @@ -208,40 +218,40 @@ namespace RTE { } if (g_FrameMan.IsInMultiplayerMode()) { - dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_FrameMan.GetPlayerFrameBufferWidth(m_MenuController->GetPlayer()), g_FrameMan.GetPlayerFrameBufferHeight(m_MenuController->GetPlayer())); + dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_FrameMan.GetPlayerFrameBufferWidth(m_MenuController->GetPlayer()), g_FrameMan.GetPlayerFrameBufferHeight(m_MenuController->GetPlayer())); } else { - dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); } - m_GUITopLevelBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBox_InventoryMenuGUI")); + m_GUITopLevelBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBox_InventoryMenuGUI")); m_GUITopLevelBox->SetPositionAbs(g_FrameMan.GetPlayerFrameBufferWidth(m_MenuController->GetPlayer()), 0); m_GUITopLevelBoxFullSize.SetXY(static_cast(m_GUITopLevelBox->GetWidth()), static_cast(m_GUITopLevelBox->GetHeight())); - m_GUIInformationText = dynamic_cast(m_GUIControlManager->GetControl("Label_InformationText")); - m_GUIInformationToggleButton = dynamic_cast(m_GUIControlManager->GetControl("Button_InformationToggle")); + m_GUIInformationText = dynamic_cast(m_GUIControlManager->GetControl("Label_InformationText")); + m_GUIInformationToggleButton = dynamic_cast(m_GUIControlManager->GetControl("Button_InformationToggle")); m_GUIInformationToggleButton->SetText(""); - m_GUIInformationToggleButtonIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Information")); + m_GUIInformationToggleButtonIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Information")); - m_GUIEquippedItemsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBox_EquippedItems")); - m_GUISwapSetButton = dynamic_cast(m_GUIControlManager->GetControl("Button_SwapSet")); + m_GUIEquippedItemsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBox_EquippedItems")); + m_GUISwapSetButton = dynamic_cast(m_GUIControlManager->GetControl("Button_SwapSet")); m_GUISwapSetButton->SetEnabled(false); m_GUISwapSetButton->SetVisible(false); - m_GUIEquippedItemButton = dynamic_cast(m_GUIControlManager->GetControl("Button_EquippedItem")); + m_GUIEquippedItemButton = dynamic_cast(m_GUIControlManager->GetControl("Button_EquippedItem")); m_GUIEquippedItemButton->SetHorizontalOverflowScroll(true); - m_GUIOffhandEquippedItemButton = dynamic_cast(m_GUIControlManager->GetControl("Button_OffhandEquippedItem")); + m_GUIOffhandEquippedItemButton = dynamic_cast(m_GUIControlManager->GetControl("Button_OffhandEquippedItem")); m_GUIOffhandEquippedItemButton->SetHorizontalOverflowScroll(true); - m_GUIReloadButton = dynamic_cast(m_GUIControlManager->GetControl("Button_Reload")); + m_GUIReloadButton = dynamic_cast(m_GUIControlManager->GetControl("Button_Reload")); m_GUIReloadButton->SetText(""); - m_GUIReloadButtonIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Refresh")); - m_GUIDropButton = dynamic_cast(m_GUIControlManager->GetControl("Button_Drop")); + m_GUIReloadButtonIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Refresh")); + m_GUIDropButton = dynamic_cast(m_GUIControlManager->GetControl("Button_Drop")); m_GUIDropButton->SetText(""); - m_GUIDropButtonIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Drop")); + m_GUIDropButtonIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Drop")); - m_GUIInventoryItemsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBox_InventoryItems")); - m_GUIInventoryItemsScrollbar = dynamic_cast(m_GUIControlManager->GetControl("Scrollbar_InventoryItems")); + m_GUIInventoryItemsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBox_InventoryItems")); + m_GUIInventoryItemsScrollbar = dynamic_cast(m_GUIControlManager->GetControl("Scrollbar_InventoryItems")); m_GUIInventoryItemsScrollbar->SetValue(0); m_GUIInventoryItemsScrollbar->SetMinimum(0); - GUIButton *inventoryItemButtonTemplate = dynamic_cast(m_GUIControlManager->GetControl("Button_InventoryItemTemplate")); + GUIButton* inventoryItemButtonTemplate = dynamic_cast(m_GUIControlManager->GetControl("Button_InventoryItemTemplate")); inventoryItemButtonTemplate->SetEnabled(false); inventoryItemButtonTemplate->SetVisible(false); @@ -253,11 +263,11 @@ namespace RTE { std::string emptyButtonText = "> <"; inventoryItemButtonProperties.AddVariable("Text", emptyButtonText); inventoryItemButtonProperties.AddVariable("HorizontalOverflowScroll", true); - std::pair itemButtonPair; + std::pair itemButtonPair; for (int i = 0; i < c_FullViewPageItemLimit; i++) { inventoryItemButtonProperties.AddVariable("Name", std::to_string(i)); - itemButtonPair = {nullptr, dynamic_cast(m_GUIControlManager->AddControl(&inventoryItemButtonProperties))}; + itemButtonPair = {nullptr, dynamic_cast(m_GUIControlManager->AddControl(&inventoryItemButtonProperties))}; itemButtonPair.second->SetPositionRel(inventoryItemButtonTemplate->GetRelXPos() + ((i % c_ItemsPerRow) * inventoryItemButtonTemplate->GetWidth()), inventoryItemButtonTemplate->GetRelYPos() + ((i / c_ItemsPerRow) * inventoryItemButtonTemplate->GetHeight())); m_GUIInventoryItemButtons.emplace_back(itemButtonPair); } @@ -265,18 +275,20 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::SetInventoryActor(Actor *newInventoryActor) { + void InventoryMenuGUI::SetInventoryActor(Actor* newInventoryActor) { m_InventoryActor = newInventoryActor; if (m_InventoryActor) { - m_InventoryActorIsHuman = dynamic_cast(m_InventoryActor); + m_InventoryActorIsHuman = dynamic_cast(m_InventoryActor); - if (g_SceneMan.ShortestDistance(m_CenterPos, m_InventoryActor->GetCPUPos(), g_SceneMan.SceneWrapsX()).GetMagnitude() > 2.0F) { m_CenterPos = m_InventoryActor->GetCPUPos(); } + if (g_SceneMan.ShortestDistance(m_CenterPos, m_InventoryActor->GetCPUPos(), g_SceneMan.SceneWrapsX()).GetMagnitude() > 2.0F) { + m_CenterPos = m_InventoryActor->GetCPUPos(); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::SetEnabled(bool enable) { if (!m_MenuController || !m_InventoryActor) { @@ -293,17 +305,19 @@ namespace RTE { m_NonMousePreviousEquippedItemsBoxButton = nullptr; m_NonMousePreviousInventoryItemsBoxButton = nullptr; m_NonMousePreviousReloadOrDropButton = nullptr; - if (m_NonMouseHighlightedButton) { m_NonMouseHighlightedButton->OnMouseLeave(0, 0, 0, 0); } - for (const auto &[inventoryItem, inventoryItemButton] : m_GUIInventoryItemButtons) { + if (m_NonMouseHighlightedButton) { + m_NonMouseHighlightedButton->OnMouseLeave(0, 0, 0, 0); + } + for (const auto& [inventoryItem, inventoryItemButton]: m_GUIInventoryItemButtons) { inventoryItemButton->OnMouseLeave(0, 0, 0, 0); } - SoundContainer *soundToPlay = enable ? g_GUISound.EnterMenuSound() : g_GUISound.ExitMenuSound(); + SoundContainer* soundToPlay = enable ? g_GUISound.EnterMenuSound() : g_GUISound.ExitMenuSound(); soundToPlay->Play(); } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool InventoryMenuGUI::EnableIfNotEmpty() { bool shouldEnable = !m_InventoryActorEquippedItems.empty() || (m_InventoryActor && !m_InventoryActor->IsInventoryEmpty()); @@ -311,7 +325,7 @@ namespace RTE { return shouldEnable; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::ClearSelectedItem() { if (m_GUISelectedItem) { @@ -320,12 +334,16 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::SetSelectedItem(GUIButton *selectedItemButton, MovableObject *selectedItemObject, int inventoryIndex, int equippedItemIndex, bool isBeingDragged) { - if (!selectedItemButton || !selectedItemObject || (inventoryIndex < 0 && equippedItemIndex < 0)) { ClearSelectedItem(); } + void InventoryMenuGUI::SetSelectedItem(GUIButton* selectedItemButton, MovableObject* selectedItemObject, int inventoryIndex, int equippedItemIndex, bool isBeingDragged) { + if (!selectedItemButton || !selectedItemObject || (inventoryIndex < 0 && equippedItemIndex < 0)) { + ClearSelectedItem(); + } - if (!m_GUISelectedItem) { m_GUISelectedItem = std::make_unique(); } + if (!m_GUISelectedItem) { + m_GUISelectedItem = std::make_unique(); + } m_GUISelectedItem->Button = selectedItemButton; m_GUISelectedItem->Object = selectedItemObject; m_GUISelectedItem->InventoryIndex = inventoryIndex; @@ -334,7 +352,7 @@ namespace RTE { m_GUISelectedItem->DragHoldCount = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::Update() { if (IsEnabled() && (!m_MenuController || !m_InventoryActor || !g_MovableMan.ValidMO(m_InventoryActor))) { @@ -343,15 +361,19 @@ namespace RTE { } if (IsEnablingOrDisabling() && m_EnableDisableAnimationTimer.IsPastRealTimeLimit()) { m_EnabledState = (m_EnabledState == EnabledState::Enabling) ? EnabledState::Enabled : EnabledState::Disabled; - //Note gui size setting is handled here rather than in Full Mode updating so it can be made to only happen once when things are fully enabled, instead of setting over-and-over and potentially triggering extra redrawing. - if (m_EnabledState == EnabledState::Enabled && m_MenuMode != MenuMode::Carousel) { m_GUITopLevelBox->SetSize(m_GUITopLevelBoxFullSize.GetFloorIntX(), m_GUITopLevelBoxFullSize.GetFloorIntY()); } + // Note gui size setting is handled here rather than in Full Mode updating so it can be made to only happen once when things are fully enabled, instead of setting over-and-over and potentially triggering extra redrawing. + if (m_EnabledState == EnabledState::Enabled && m_MenuMode != MenuMode::Carousel) { + m_GUITopLevelBox->SetSize(m_GUITopLevelBoxFullSize.GetFloorIntX(), m_GUITopLevelBoxFullSize.GetFloorIntY()); + } } if (m_InventoryActor && g_MovableMan.ValidMO(m_InventoryActor)) { - if (const AHuman *inventoryActorAsAHuman = (m_InventoryActorIsHuman ? dynamic_cast(m_InventoryActor) : nullptr)) { + if (const AHuman* inventoryActorAsAHuman = (m_InventoryActorIsHuman ? dynamic_cast(m_InventoryActor) : nullptr)) { m_InventoryActorEquippedItems.clear(); m_InventoryActorEquippedItems.reserve(1); - if (inventoryActorAsAHuman->GetEquippedItem() || inventoryActorAsAHuman->GetEquippedBGItem()) { m_InventoryActorEquippedItems.push_back({inventoryActorAsAHuman->GetEquippedItem(), inventoryActorAsAHuman->GetEquippedBGItem()}); } + if (inventoryActorAsAHuman->GetEquippedItem() || inventoryActorAsAHuman->GetEquippedBGItem()) { + m_InventoryActorEquippedItems.push_back({inventoryActorAsAHuman->GetEquippedItem(), inventoryActorAsAHuman->GetEquippedBGItem()}); + } } else if (!m_InventoryActorEquippedItems.empty()) { m_InventoryActorEquippedItems.clear(); } @@ -380,9 +402,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::Draw(BITMAP *targetBitmap, const Vector &targetPos) const { + void InventoryMenuGUI::Draw(BITMAP* targetBitmap, const Vector& targetPos) const { Vector drawPos = m_CenterPos - targetPos; switch (m_MenuMode) { @@ -407,28 +429,30 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::UpdateCarouselMode() { - if (!CarouselModeReadyForUse()) { SetupCarouselMode(); } + if (!CarouselModeReadyForUse()) { + SetupCarouselMode(); + } - for (const std::unique_ptr &carouselItemBox : m_CarouselItemBoxes) { + for (const std::unique_ptr& carouselItemBox: m_CarouselItemBoxes) { carouselItemBox->Item = nullptr; } - const std::deque *inventory = m_InventoryActor->GetInventory(); + const std::deque* inventory = m_InventoryActor->GetInventory(); if (inventory && !inventory->empty()) { int leftSideItemCount = std::min(static_cast(std::floor(static_cast(inventory->size()) / 2)), c_ItemsPerRow / 2); int rightSideItemCount = std::min(static_cast(std::ceil(static_cast(inventory->size()) / 2)), c_ItemsPerRow / 2 + (m_InventoryActorEquippedItems.empty() ? 1 : 0)); int carouselIndex = 0; - std::vector temporaryLeftSideItemsForProperOrdering; + std::vector temporaryLeftSideItemsForProperOrdering; temporaryLeftSideItemsForProperOrdering.reserve(leftSideItemCount); if (leftSideItemCount > 0) { - std::for_each(inventory->crbegin(), inventory->crbegin() + leftSideItemCount, [&temporaryLeftSideItemsForProperOrdering](MovableObject *carouselItem) { + std::for_each(inventory->crbegin(), inventory->crbegin() + leftSideItemCount, [&temporaryLeftSideItemsForProperOrdering](MovableObject* carouselItem) { temporaryLeftSideItemsForProperOrdering.emplace_back(carouselItem); }); } - std::for_each(temporaryLeftSideItemsForProperOrdering.crbegin(), temporaryLeftSideItemsForProperOrdering.crend(), [this, &carouselIndex, &leftSideItemCount](MovableObject *carouselItem) { + std::for_each(temporaryLeftSideItemsForProperOrdering.crbegin(), temporaryLeftSideItemsForProperOrdering.crend(), [this, &carouselIndex, &leftSideItemCount](MovableObject* carouselItem) { m_CarouselItemBoxes.at(carouselIndex + (c_ItemsPerRow / 2) - leftSideItemCount)->Item = carouselItem; m_CarouselItemBoxes.at(carouselIndex)->IsForEquippedItems = false; carouselIndex++; @@ -439,7 +463,7 @@ namespace RTE { carouselIndex++; } if (rightSideItemCount > 0) { - std::for_each(inventory->cbegin(), inventory->cbegin() + rightSideItemCount, [this, &carouselIndex](MovableObject *carouselItem) { + std::for_each(inventory->cbegin(), inventory->cbegin() + rightSideItemCount, [this, &carouselIndex](MovableObject* carouselItem) { m_CarouselItemBoxes.at(carouselIndex)->Item = carouselItem; m_CarouselItemBoxes.at(carouselIndex)->IsForEquippedItems = false; carouselIndex++; @@ -464,7 +488,7 @@ namespace RTE { UpdateCarouselItemBoxSizesAndPositions(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::UpdateCarouselItemBoxSizesAndPositions() { float halfMassFontHeight = static_cast(m_SmallFont->GetFontHeight() / 2); @@ -476,7 +500,7 @@ namespace RTE { /// /// Lambda to do all the repetitive boilerplate of setting up a carousel item box. /// - auto SetupCarouselItemBox = [this, &halfMassFontHeight, &carouselIndex, &carouselAnimationProgress, ¤tBoxHorizontalOffset](const std::unique_ptr &carouselItemBox, bool leftSideRoundedAndBordered, bool rightSideRoundedAndBordered) { + auto SetupCarouselItemBox = [this, &halfMassFontHeight, &carouselIndex, &carouselAnimationProgress, ¤tBoxHorizontalOffset](const std::unique_ptr& carouselItemBox, bool leftSideRoundedAndBordered, bool rightSideRoundedAndBordered) { carouselItemBox->CurrentSize = carouselItemBox->FullSize; if (carouselIndex == -1) { carouselItemBox->CurrentSize += c_CarouselBoxSizeStep * (1.0F - carouselAnimationProgress); @@ -500,7 +524,7 @@ namespace RTE { SetupCarouselItemBox(m_CarouselExitingItemBox, true, false); } - for (const std::unique_ptr &carouselItemBox : m_CarouselItemBoxes) { + for (const std::unique_ptr& carouselItemBox: m_CarouselItemBoxes) { if (carouselIndex == 0 && m_CarouselAnimationDirection == CarouselAnimationDirection::Right && directionalAnimationProgress == 0.0F) { carouselIndex++; continue; @@ -522,15 +546,19 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::UpdateFullMode() { - if (!FullOrTransferModeReadyForUse()) { SetupFullOrTransferMode(); } + if (!FullOrTransferModeReadyForUse()) { + SetupFullOrTransferMode(); + } if (!m_GUIShowEmptyRows) { int numberOfRowsToShow = static_cast(std::ceil(static_cast(std::min(c_FullViewPageItemLimit, m_InventoryActor->GetInventorySize())) / static_cast(c_ItemsPerRow))); int expectedInventoryHeight = m_GUIInventoryItemButtons[0].second->GetHeight() * numberOfRowsToShow; - if (numberOfRowsToShow * c_ItemsPerRow < c_FullViewPageItemLimit) { expectedInventoryHeight -= 1; } + if (numberOfRowsToShow * c_ItemsPerRow < c_FullViewPageItemLimit) { + expectedInventoryHeight -= 1; + } if (m_GUIInventoryItemsBox->GetHeight() != expectedInventoryHeight) { int inventoryItemsBoxPreviousHeight = m_GUIInventoryItemsBox->GetHeight(); m_GUIInventoryItemsBox->SetSize(m_GUIInventoryItemsBox->GetWidth(), expectedInventoryHeight); @@ -541,7 +569,7 @@ namespace RTE { UpdateFullModeEquippedItemButtons(); - const std::deque *inventory = m_InventoryActor->GetInventory(); + const std::deque* inventory = m_InventoryActor->GetInventory(); UpdateFullModeScrollbar(inventory); @@ -560,8 +588,10 @@ namespace RTE { if (m_GUISelectedItem) { m_GUISelectedItem->Button->OnGainFocus(); - if (m_GUISelectedItem->DragWasHeldForLongEnough()) { m_GUIInformationToggleButton->SetEnabled(false); } - if (const HDFirearm *selectedItemAsFirearm = dynamic_cast(m_GUISelectedItem->Object)) { + if (m_GUISelectedItem->DragWasHeldForLongEnough()) { + m_GUIInformationToggleButton->SetEnabled(false); + } + if (const HDFirearm* selectedItemAsFirearm = dynamic_cast(m_GUISelectedItem->Object)) { m_GUIReloadButton->SetEnabled(!selectedItemAsFirearm->IsFull()); } else { m_GUIReloadButton->SetEnabled(false); @@ -569,13 +599,13 @@ namespace RTE { m_GUIDropButton->SetEnabled(true); } else { m_GUIReloadButton->SetEnabled(false); - for (const auto[equippedItem, offhandEquippedItem] : m_InventoryActorEquippedItems) { - const HDFirearm *equippedItemAsFirearm = dynamic_cast(equippedItem); + for (const auto [equippedItem, offhandEquippedItem]: m_InventoryActorEquippedItems) { + const HDFirearm* equippedItemAsFirearm = dynamic_cast(equippedItem); if (equippedItemAsFirearm && !equippedItemAsFirearm->IsFull()) { m_GUIReloadButton->SetEnabled(true); break; } - const HDFirearm *offhandEquippedItemAsFirearm = dynamic_cast(offhandEquippedItem); + const HDFirearm* offhandEquippedItemAsFirearm = dynamic_cast(offhandEquippedItem); if (offhandEquippedItemAsFirearm && !offhandEquippedItemAsFirearm->IsFull()) { m_GUIReloadButton->SetEnabled(true); break; @@ -589,11 +619,11 @@ namespace RTE { UpdateFullModeNonItemButtonIconsAndHighlightWidths(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::UpdateFullModeEquippedItemButtons() { - const MovableObject *equippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first : nullptr; - const MovableObject *offhandEquippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).second : nullptr; + const MovableObject* equippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first : nullptr; + const MovableObject* offhandEquippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).second : nullptr; m_GUIEquippedItemButton->SetEnabled(equippedItem); if (m_GUISelectedItem && m_GUISelectedItem->Button == m_GUIEquippedItemButton && m_GUISelectedItem->DragWasHeldForLongEnough()) { @@ -615,7 +645,7 @@ namespace RTE { m_GUIOffhandEquippedItemButton->SetIconAndText(nullptr, "> <"); } - bool showOffhandButton = offhandEquippedItem || (dynamic_cast(equippedItem) && dynamic_cast(equippedItem)->IsOneHanded()); + bool showOffhandButton = offhandEquippedItem || (dynamic_cast(equippedItem) && dynamic_cast(equippedItem)->IsOneHanded()); if (showOffhandButton && !m_GUIOffhandEquippedItemButton->GetVisible()) { m_GUIEquippedItemButton->Resize(m_GUIEquippedItemButton->GetWidth() / 2, m_GUIEquippedItemButton->GetHeight()); m_GUIOffhandEquippedItemButton->SetVisible(true); @@ -625,9 +655,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateFullModeScrollbar(const std::deque *inventory) { + void InventoryMenuGUI::UpdateFullModeScrollbar(const std::deque* inventory) { if (inventory->size() > c_FullViewPageItemLimit) { m_GUIInventoryItemsScrollbar->SetMaximum(static_cast(std::ceil(static_cast(inventory->size() - c_FullViewPageItemLimit) / static_cast(c_ItemsPerRow))) + 1); if (!m_GUIInventoryItemsScrollbar->GetVisible()) { @@ -654,13 +684,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateFullModeInventoryItemButtons(const std::deque *inventory) { + void InventoryMenuGUI::UpdateFullModeInventoryItemButtons(const std::deque* inventory) { int startIndex = m_GUIInventoryItemsScrollbar->GetValue() * c_ItemsPerRow; int lastPopulatedIndex = static_cast(inventory->size() - 1); - GUIButton *itemButton; - MovableObject *inventoryItem; + GUIButton* itemButton; + MovableObject* inventoryItem; for (int i = startIndex; (i - startIndex) < c_FullViewPageItemLimit; i++) { itemButton = m_GUIInventoryItemButtons.at(i - startIndex).second; if (i <= lastPopulatedIndex) { @@ -685,13 +715,15 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateFullModeInformationText(const std::deque *inventory) { + void InventoryMenuGUI::UpdateFullModeInformationText(const std::deque* inventory) { if (!m_GUIShowInformationText && m_GUIInformationText->GetVisible()) { m_GUIInformationText->SetVisible(false); } else if (m_GUIShowInformationText) { - if (!m_GUIInformationText->GetVisible()) { m_GUIInformationText->SetVisible(true); } + if (!m_GUIInformationText->GetVisible()) { + m_GUIInformationText->SetVisible(true); + } std::string informationText; if (m_GUISelectedItem) { @@ -708,7 +740,7 @@ namespace RTE { } else { if (m_GUIDisplayOnly) { informationText = ">> DISPLAY ONLY <<"; - } else if (m_InventoryActorEquippedItems.empty() && inventory->empty()) { + } else if (m_InventoryActorEquippedItems.empty() && inventory->empty()) { informationText = "No items to display."; } else { informationText = m_MenuController->IsMouseControlled() ? "Click an item to interact with it. You can also click and hold to drag items." : "Press an item to interact with it."; @@ -718,16 +750,15 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::UpdateFullModeNonItemButtonIconsAndHighlightWidths() { - std::vector> buttonsToCheckIconsFor = { - { m_GUIInformationToggleButton, m_GUIInformationToggleButtonIcon }, - { m_GUIReloadButton, m_GUIReloadButtonIcon }, - { m_GUIDropButton, m_GUIDropButtonIcon } - }; + std::vector> buttonsToCheckIconsFor = { + {m_GUIInformationToggleButton, m_GUIInformationToggleButtonIcon}, + {m_GUIReloadButton, m_GUIReloadButtonIcon}, + {m_GUIDropButton, m_GUIDropButtonIcon}}; - for (const auto &[button, icon] : buttonsToCheckIconsFor) { + for (const auto& [button, icon]: buttonsToCheckIconsFor) { if (icon) { if (button->IsEnabled()) { button->SetIcon((button->HasFocus() || button->IsMousedOver() || button->IsPushed()) ? icon->GetBitmaps8()[1] : icon->GetBitmaps8()[0]); @@ -746,13 +777,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::UpdateTransferMode() { - //TODO Make Transfer mode + // TODO Make Transfer mode } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::HandleInput() { if (m_MenuController->IsState(ControlState::PRESS_SECONDARY)) { @@ -760,7 +791,7 @@ namespace RTE { return; } - //TODO Maybe add support for other click types, e.g. if player right clicks on an item when they have one selected, we can call some custom lua function, so you can do custom stuff like combining items or whatever + // TODO Maybe add support for other click types, e.g. if player right clicks on an item when they have one selected, we can call some custom lua function, so you can do custom stuff like combining items or whatever if (m_MenuController->IsMouseControlled()) { if (HandleMouseInput()) { return; @@ -769,10 +800,12 @@ namespace RTE { HandleNonMouseInput(); } - if (m_GUISelectedItem && m_MenuController->IsState(ControlState::WEAPON_DROP)) { DropSelectedItem(); } + if (m_GUISelectedItem && m_MenuController->IsState(ControlState::WEAPON_DROP)) { + DropSelectedItem(); + } GUIEvent guiEvent; - const GUIControl *guiControl; + const GUIControl* guiControl; while (m_GUIControlManager->GetEvent(&guiEvent)) { guiControl = guiEvent.GetControl(); if (guiEvent.GetType() == GUIEvent::Notification && guiEvent.GetMsg() == GUIButton::Focused) { @@ -787,7 +820,7 @@ namespace RTE { m_GUIInformationToggleButton->OnLoseFocus(); } else if (guiControl == m_GUIEquippedItemButton) { int equippedItemButtonIndexToUse = 0; - if (const AHuman *inventoryActorAsAHuman = (m_InventoryActorIsHuman ? dynamic_cast(m_InventoryActor) : nullptr); inventoryActorAsAHuman && !inventoryActorAsAHuman->GetFGArm() && !inventoryActorAsAHuman->GetEquippedBGItem()) { + if (const AHuman* inventoryActorAsAHuman = (m_InventoryActorIsHuman ? dynamic_cast(m_InventoryActor) : nullptr); inventoryActorAsAHuman && !inventoryActorAsAHuman->GetFGArm() && !inventoryActorAsAHuman->GetEquippedBGItem()) { equippedItemButtonIndexToUse = 1; } HandleItemButtonPressOrHold(m_GUIEquippedItemButton, m_InventoryActorEquippedItems.empty() ? nullptr : m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first, equippedItemButtonIndexToUse, buttonHeld); @@ -799,15 +832,17 @@ namespace RTE { } else if (!buttonHeld && guiControl == m_GUIDropButton) { DropSelectedItem(); } else { - for (const auto &[inventoryObject, inventoryItemButton] : m_GUIInventoryItemButtons) { - if (guiControl == inventoryItemButton) { HandleItemButtonPressOrHold(inventoryItemButton, inventoryObject, -1, buttonHeld); } + for (const auto& [inventoryObject, inventoryItemButton]: m_GUIInventoryItemButtons) { + if (guiControl == inventoryItemButton) { + HandleItemButtonPressOrHold(inventoryItemButton, inventoryObject, -1, buttonHeld); + } } } } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool InventoryMenuGUI::HandleMouseInput() { int mouseX; @@ -818,21 +853,35 @@ namespace RTE { if (m_GUIInventoryItemsScrollbar->GetVisible()) { int mouseWheelChange = m_MenuController->IsState(ControlState::SCROLL_UP) ? -1 : (m_MenuController->IsState(ControlState::SCROLL_DOWN) ? 1 : 0); - if (mouseWheelChange != 0) { m_GUIInventoryItemsScrollbar->SetValue(std::clamp(m_GUIInventoryItemsScrollbar->GetValue() + mouseWheelChange, m_GUIInventoryItemsScrollbar->GetMinimum(), m_GUIInventoryItemsScrollbar->GetMaximum())); } + if (mouseWheelChange != 0) { + m_GUIInventoryItemsScrollbar->SetValue(std::clamp(m_GUIInventoryItemsScrollbar->GetValue() + mouseWheelChange, m_GUIInventoryItemsScrollbar->GetMinimum(), m_GUIInventoryItemsScrollbar->GetMaximum())); + } } if (m_GUISelectedItem && m_GUISelectedItem->IsBeingDragged) { if (!m_GUISelectedItem->DragWasHeldForLongEnough()) { m_GUISelectedItem->DragHoldCount++; - if (m_GUISelectedItem->DragWasHeldForLongEnough()) { g_GUISound.ItemChangeSound()->Play(m_MenuController->GetPlayer()); } + if (m_GUISelectedItem->DragWasHeldForLongEnough()) { + g_GUISound.ItemChangeSound()->Play(m_MenuController->GetPlayer()); + } } - if (m_GUIEquippedItemButton->IsPushed() && !m_GUIEquippedItemButton->PointInside(mouseX, mouseY)) { m_GUIEquippedItemButton->SetPushed(false); } - if (m_GUIOffhandEquippedItemButton->IsPushed() && !m_GUIOffhandEquippedItemButton->PointInside(mouseX, mouseY)) { m_GUIOffhandEquippedItemButton->SetPushed(false); } - if (m_GUIReloadButton->IsPushed() && !m_GUIReloadButton->PointInside(mouseX, mouseY)) { m_GUIReloadButton->SetPushed(false); } - if (m_GUIDropButton->IsPushed() && !m_GUIDropButton->PointInside(mouseX, mouseY)) { m_GUIDropButton->SetPushed(false); } - for (const auto &[unused, inventoryItemButton] : m_GUIInventoryItemButtons) { - if (inventoryItemButton->IsPushed() && !inventoryItemButton->PointInside(mouseX, mouseY)) { inventoryItemButton->SetPushed(false); } + if (m_GUIEquippedItemButton->IsPushed() && !m_GUIEquippedItemButton->PointInside(mouseX, mouseY)) { + m_GUIEquippedItemButton->SetPushed(false); + } + if (m_GUIOffhandEquippedItemButton->IsPushed() && !m_GUIOffhandEquippedItemButton->PointInside(mouseX, mouseY)) { + m_GUIOffhandEquippedItemButton->SetPushed(false); + } + if (m_GUIReloadButton->IsPushed() && !m_GUIReloadButton->PointInside(mouseX, mouseY)) { + m_GUIReloadButton->SetPushed(false); + } + if (m_GUIDropButton->IsPushed() && !m_GUIDropButton->PointInside(mouseX, mouseY)) { + m_GUIDropButton->SetPushed(false); + } + for (const auto& [unused, inventoryItemButton]: m_GUIInventoryItemButtons) { + if (inventoryItemButton->IsPushed() && !inventoryItemButton->PointInside(mouseX, mouseY)) { + inventoryItemButton->SetPushed(false); + } } int mouseEvents[3]; @@ -856,7 +905,7 @@ namespace RTE { g_GUISound.SelectionChangeSound()->Play(m_MenuController->GetPlayer()); } else if (mouseReleased) { int equippedItemButtonIndexToUse = 0; - if (const AHuman *inventoryActorAsAHuman = (m_InventoryActorIsHuman ? dynamic_cast(m_InventoryActor) : nullptr); inventoryActorAsAHuman && !inventoryActorAsAHuman->GetFGArm() && !inventoryActorAsAHuman->GetEquippedBGItem()) { + if (const AHuman* inventoryActorAsAHuman = (m_InventoryActorIsHuman ? dynamic_cast(m_InventoryActor) : nullptr); inventoryActorAsAHuman && !inventoryActorAsAHuman->GetFGArm() && !inventoryActorAsAHuman->GetEquippedBGItem()) { equippedItemButtonIndexToUse = 1; } HandleItemButtonPressOrHold(m_GUIEquippedItemButton, m_InventoryActorEquippedItems.empty() ? nullptr : m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first, equippedItemButtonIndexToUse); @@ -893,7 +942,7 @@ namespace RTE { m_GUIDropButton->SetPushed(false); } } else { - for (const auto &[inventoryObject, inventoryItemButton] : m_GUIInventoryItemButtons) { + for (const auto& [inventoryObject, inventoryItemButton]: m_GUIInventoryItemButtons) { if (inventoryItemButton->PointInside(mouseX, mouseY)) { if (mouseHeld && !inventoryItemButton->IsPushed()) { inventoryItemButton->SetPushed(true); @@ -919,11 +968,15 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::HandleNonMouseInput() { - if (!m_NonMouseHighlightedButton || !m_NonMouseHighlightedButton->GetVisible() || (!m_GUIShowEmptyRows && m_NonMouseHighlightedButton->GetParent() == m_GUIInventoryItemsBox && m_InventoryActor->IsInventoryEmpty())) { m_NonMouseHighlightedButton = m_GUIEquippedItemButton; } - if (!m_NonMouseHighlightedButton->IsMousedOver()) { m_NonMouseHighlightedButton->OnMouseEnter(0, 0, 0, 0); } + if (!m_NonMouseHighlightedButton || !m_NonMouseHighlightedButton->GetVisible() || (!m_GUIShowEmptyRows && m_NonMouseHighlightedButton->GetParent() == m_GUIInventoryItemsBox && m_InventoryActor->IsInventoryEmpty())) { + m_NonMouseHighlightedButton = m_GUIEquippedItemButton; + } + if (!m_NonMouseHighlightedButton->IsMousedOver()) { + m_NonMouseHighlightedButton->OnMouseEnter(0, 0, 0, 0); + } if (m_MenuController->IsState(ControlState::PRESS_PRIMARY)) { if (m_NonMouseHighlightedButton->IsEnabled()) { @@ -936,7 +989,7 @@ namespace RTE { } } - GUIButton *nextButtonToHighlight = nullptr; + GUIButton* nextButtonToHighlight = nullptr; Directions pressedDirection = GetNonMouseButtonControllerMovement(); switch (pressedDirection) { case Directions::Up: @@ -969,7 +1022,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Directions InventoryMenuGUI::GetNonMouseButtonControllerMovement() { bool pressUp = m_MenuController->IsState(ControlState::PRESS_UP) || m_MenuController->IsState(ControlState::SCROLL_UP); @@ -1004,10 +1057,10 @@ namespace RTE { return Directions::None; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIButton * InventoryMenuGUI::HandleNonMouseUpInput() { - GUIButton *nextButtonToHighlight = nullptr; + GUIButton* InventoryMenuGUI::HandleNonMouseUpInput() { + GUIButton* nextButtonToHighlight = nullptr; if (m_NonMouseHighlightedButton == m_GUIDropButton) { nextButtonToHighlight = m_GUIReloadButton; @@ -1039,10 +1092,10 @@ namespace RTE { return nextButtonToHighlight; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIButton * InventoryMenuGUI::HandleNonMouseDownInput() { - GUIButton *nextButtonToHighlight = nullptr; + GUIButton* InventoryMenuGUI::HandleNonMouseDownInput() { + GUIButton* nextButtonToHighlight = nullptr; if (!m_InventoryActor->IsInventoryEmpty() && (m_NonMouseHighlightedButton == m_GUISwapSetButton || m_NonMouseHighlightedButton == m_GUIEquippedItemButton || m_NonMouseHighlightedButton == m_GUIOffhandEquippedItemButton || m_NonMouseHighlightedButton == m_GUIDropButton || m_NonMouseHighlightedButton == m_GUIInformationToggleButton)) { nextButtonToHighlight = m_NonMousePreviousInventoryItemsBoxButton; @@ -1078,10 +1131,10 @@ namespace RTE { return nextButtonToHighlight; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIButton * InventoryMenuGUI::HandleNonMouseLeftInput() { - GUIButton *nextButtonToHighlight = nullptr; + GUIButton* InventoryMenuGUI::HandleNonMouseLeftInput() { + GUIButton* nextButtonToHighlight = nullptr; if (m_NonMouseHighlightedButton->GetParent() == m_GUIInventoryItemsBox) { try { @@ -1104,15 +1157,17 @@ namespace RTE { } else if (m_NonMouseHighlightedButton == m_GUIInformationToggleButton) { nextButtonToHighlight = m_NonMousePreviousReloadOrDropButton ? m_NonMousePreviousReloadOrDropButton : m_GUIReloadButton; } - if (nextButtonToHighlight) { m_NonMousePreviousInventoryItemsBoxButton = nullptr; } + if (nextButtonToHighlight) { + m_NonMousePreviousInventoryItemsBoxButton = nullptr; + } } return nextButtonToHighlight; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIButton * InventoryMenuGUI::HandleNonMouseRightInput() { - GUIButton *nextButtonToHighlight = nullptr; + GUIButton* InventoryMenuGUI::HandleNonMouseRightInput() { + GUIButton* nextButtonToHighlight = nullptr; if (m_NonMouseHighlightedButton->GetParent() == m_GUIInventoryItemsBox) { try { @@ -1122,7 +1177,9 @@ namespace RTE { nextButtonToHighlight = m_GUIInventoryItemButtons.at(highlightedButtonIndex - c_ItemsPerRow + 1).second; } else { int numberOfVisibleButtons = m_GUIShowEmptyRows ? c_FullViewPageItemLimit : c_ItemsPerRow * static_cast(std::ceil(static_cast(std::min(c_FullViewPageItemLimit, m_InventoryActor->GetInventorySize())) / static_cast(c_ItemsPerRow))); - if (highlightedButtonIndex + 1 < numberOfVisibleButtons) { nextButtonToHighlight = m_GUIInventoryItemButtons.at(highlightedButtonIndex + 1).second; } + if (highlightedButtonIndex + 1 < numberOfVisibleButtons) { + nextButtonToHighlight = m_GUIInventoryItemButtons.at(highlightedButtonIndex + 1).second; + } m_NonMousePreviousEquippedItemsBoxButton = nullptr; } } catch (std::invalid_argument) { @@ -1140,18 +1197,20 @@ namespace RTE { } else if (m_NonMouseHighlightedButton == m_GUIReloadButton || m_NonMouseHighlightedButton == m_GUIDropButton) { nextButtonToHighlight = m_GUIInformationToggleButton; } - if (nextButtonToHighlight) { m_NonMousePreviousInventoryItemsBoxButton = nullptr; } + if (nextButtonToHighlight) { + m_NonMousePreviousInventoryItemsBoxButton = nullptr; + } } return nextButtonToHighlight; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::HandleItemButtonPressOrHold(GUIButton *pressedButton, MovableObject *buttonObject, int buttonEquippedItemIndex, bool buttonHeld) { + void InventoryMenuGUI::HandleItemButtonPressOrHold(GUIButton* pressedButton, MovableObject* buttonObject, int buttonEquippedItemIndex, bool buttonHeld) { if (buttonHeld && m_GUISelectedItem) { return; } - if ((buttonEquippedItemIndex == 1 && m_GUISelectedItem && !m_GUISelectedItem->Object->HasObjectInGroup("Shields") && !dynamic_cast(m_GUISelectedItem->Object)->IsDualWieldable()) || (m_GUISelectedItem && m_GUISelectedItem->EquippedItemIndex == 1 && buttonObject && !buttonObject->HasObjectInGroup("Shields") && !dynamic_cast(buttonObject)->IsDualWieldable())) { + if ((buttonEquippedItemIndex == 1 && m_GUISelectedItem && !m_GUISelectedItem->Object->HasObjectInGroup("Shields") && !dynamic_cast(m_GUISelectedItem->Object)->IsDualWieldable()) || (m_GUISelectedItem && m_GUISelectedItem->EquippedItemIndex == 1 && buttonObject && !buttonObject->HasObjectInGroup("Shields") && !dynamic_cast(buttonObject)->IsDualWieldable())) { g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); return; } @@ -1166,7 +1225,9 @@ namespace RTE { } if (m_GUISelectedItem == nullptr) { - if (!buttonHeld) { g_GUISound.ItemChangeSound()->Play(m_MenuController->GetPlayer()); } + if (!buttonHeld) { + g_GUISound.ItemChangeSound()->Play(m_MenuController->GetPlayer()); + } if (buttonEquippedItemIndex > -1) { SetSelectedItem(pressedButton, buttonObject, -1, pressedButtonItemIndex, buttonHeld); } else { @@ -1178,17 +1239,17 @@ namespace RTE { } else { if (m_GUISelectedItem->EquippedItemIndex > -1) { if (buttonEquippedItemIndex > -1) { - Arm *selectedItemArm = dynamic_cast(m_GUISelectedItem->Object->GetParent()); - Arm *buttonObjectArm = selectedItemArm && buttonObject ? dynamic_cast(buttonObject->GetParent()) : nullptr; + Arm* selectedItemArm = dynamic_cast(m_GUISelectedItem->Object->GetParent()); + Arm* buttonObjectArm = selectedItemArm && buttonObject ? dynamic_cast(buttonObject->GetParent()) : nullptr; if (!buttonObject) { - const AHuman *inventoryActorAsAHuman = dynamic_cast(m_InventoryActor); + const AHuman* inventoryActorAsAHuman = dynamic_cast(m_InventoryActor); buttonObjectArm = buttonEquippedItemIndex == 0 ? inventoryActorAsAHuman->GetFGArm() : inventoryActorAsAHuman->GetBGArm(); } - if (selectedItemArm && buttonObjectArm && dynamic_cast(buttonObject) && dynamic_cast(m_GUISelectedItem->Object)) { + if (selectedItemArm && buttonObjectArm && dynamic_cast(buttonObject) && dynamic_cast(m_GUISelectedItem->Object)) { selectedItemArm->RemoveAttachable(selectedItemArm->GetHeldDevice()); buttonObjectArm->RemoveAttachable(buttonObjectArm->GetHeldDevice()); - selectedItemArm->SetHeldDevice(dynamic_cast(buttonObject)); - buttonObjectArm->SetHeldDevice(dynamic_cast(m_GUISelectedItem->Object)); + selectedItemArm->SetHeldDevice(dynamic_cast(buttonObject)); + buttonObjectArm->SetHeldDevice(dynamic_cast(m_GUISelectedItem->Object)); m_InventoryActor->GetDeviceSwitchSound()->Play(m_MenuController->GetPlayer()); } else { g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); @@ -1213,7 +1274,7 @@ namespace RTE { pressedButton->OnLoseFocus(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool InventoryMenuGUI::SwapEquippedItemAndInventoryItem(int equippedItemIndex, int inventoryItemIndex) { if (!m_InventoryActorIsHuman) { @@ -1221,11 +1282,11 @@ namespace RTE { return false; } - AHuman *inventoryActorAsAHuman = dynamic_cast(m_InventoryActor); - MovableObject *equippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() && !m_InventoryActorEquippedItems.empty() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first : nullptr; - MovableObject *offhandEquippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() && !m_InventoryActorEquippedItems.empty() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).second : nullptr; + AHuman* inventoryActorAsAHuman = dynamic_cast(m_InventoryActor); + MovableObject* equippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() && !m_InventoryActorEquippedItems.empty() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first : nullptr; + MovableObject* offhandEquippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() && !m_InventoryActorEquippedItems.empty() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).second : nullptr; - const HeldDevice *inventoryItemToSwapIn = inventoryItemIndex < m_InventoryActor->GetInventorySize() ? dynamic_cast(m_InventoryActor->GetInventory()->at(inventoryItemIndex)) : nullptr; + const HeldDevice* inventoryItemToSwapIn = inventoryItemIndex < m_InventoryActor->GetInventorySize() ? dynamic_cast(m_InventoryActor->GetInventory()->at(inventoryItemIndex)) : nullptr; if (!inventoryItemToSwapIn && inventoryItemIndex < m_InventoryActor->GetInventorySize()) { g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); return false; @@ -1233,15 +1294,15 @@ namespace RTE { bool inventoryItemCanGoInOffhand = !inventoryItemToSwapIn || inventoryItemToSwapIn->IsDualWieldable() || inventoryItemToSwapIn->HasObjectInGroup("Shields"); equippedItemIndex = !inventoryItemCanGoInOffhand || !inventoryActorAsAHuman->GetBGArm() ? 0 : equippedItemIndex; - MovableObject *equippedItemToSwapOut = equippedItemIndex == 0 ? equippedItem : offhandEquippedItem; + MovableObject* equippedItemToSwapOut = equippedItemIndex == 0 ? equippedItem : offhandEquippedItem; if (equippedItemIndex == 0 && !inventoryActorAsAHuman->GetFGArm()) { g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); return false; } - Arm *equippedItemArm = equippedItemIndex == 0 ? inventoryActorAsAHuman->GetFGArm() : inventoryActorAsAHuman->GetBGArm(); - equippedItemArm->SetHeldDevice(dynamic_cast(m_InventoryActor->SetInventoryItemAtIndex(equippedItemArm->RemoveAttachable(equippedItemArm->GetHeldDevice()), inventoryItemIndex))); + Arm* equippedItemArm = equippedItemIndex == 0 ? inventoryActorAsAHuman->GetFGArm() : inventoryActorAsAHuman->GetBGArm(); + equippedItemArm->SetHeldDevice(dynamic_cast(m_InventoryActor->SetInventoryItemAtIndex(equippedItemArm->RemoveAttachable(equippedItemArm->GetHeldDevice()), inventoryItemIndex))); equippedItemArm->SetHandPos(m_InventoryActor->GetPos() + m_InventoryActor->GetHolsterOffset().GetXFlipped(m_InventoryActor->IsHFlipped())); if (!inventoryItemCanGoInOffhand && offhandEquippedItem) { m_InventoryActor->AddInventoryItem(inventoryActorAsAHuman->GetBGArm()->RemoveAttachable(inventoryActorAsAHuman->GetBGArm()->GetHeldDevice())); @@ -1251,16 +1312,16 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::ReloadSelectedItem() { //for a in MovableMan.Actors do print(ToAHuman(a).EquippedBGItem:SetOneHanded(true)) end + void InventoryMenuGUI::ReloadSelectedItem() { // for a in MovableMan.Actors do print(ToAHuman(a).EquippedBGItem:SetOneHanded(true)) end if (!m_InventoryActorIsHuman) { return; } - AHuman *inventoryActorAsAHuman = dynamic_cast(m_InventoryActor); + AHuman* inventoryActorAsAHuman = dynamic_cast(m_InventoryActor); if (m_GUISelectedItem == nullptr) { inventoryActorAsAHuman->ReloadFirearms(); - } else if (const HDFirearm *selectedItemObjectAsFirearm = dynamic_cast(m_GUISelectedItem->Object)) { + } else if (const HDFirearm* selectedItemObjectAsFirearm = dynamic_cast(m_GUISelectedItem->Object)) { bool selectedItemIsEquipped = m_GUISelectedItem->EquippedItemIndex > -1; if (!selectedItemIsEquipped) { int equippedItemIndexToUse = inventoryActorAsAHuman->GetFGArm() ? 0 : 1; @@ -1276,10 +1337,10 @@ namespace RTE { m_GUIReloadButton->OnLoseFocus(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DropSelectedItem(const Vector *dropDirection) { - auto LaunchInventoryItem = [this, &dropDirection](MovableObject *itemToLaunch) { + void InventoryMenuGUI::DropSelectedItem(const Vector* dropDirection) { + auto LaunchInventoryItem = [this, &dropDirection](MovableObject* itemToLaunch) { Vector itemPosition = m_InventoryActor->GetPos(); Vector throwForce(0.75F + (0.25F * RandomNum()), 0); if (dropDirection && dropDirection->MagnitudeIsGreaterThan(0.5F)) { @@ -1301,7 +1362,7 @@ namespace RTE { }; if (m_GUISelectedItem->EquippedItemIndex > -1) { - Attachable *itemToLaunch = dynamic_cast(m_GUISelectedItem->Object->GetParent())->RemoveAttachable(dynamic_cast(m_GUISelectedItem->Object->GetParent())->GetHeldDevice()); + Attachable* itemToLaunch = dynamic_cast(m_GUISelectedItem->Object->GetParent())->RemoveAttachable(dynamic_cast(m_GUISelectedItem->Object->GetParent())->GetHeldDevice()); if (itemToLaunch) { LaunchInventoryItem(itemToLaunch); } @@ -1313,15 +1374,15 @@ namespace RTE { m_GUIDropButton->OnLoseFocus(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DrawCarouselMode(BITMAP *targetBitmap, const Vector &drawPos) const { + void InventoryMenuGUI::DrawCarouselMode(BITMAP* targetBitmap, const Vector& drawPos) const { clear_to_color(m_CarouselBitmap.get(), g_MaskColor); clear_to_color(m_CarouselBGBitmap.get(), g_MaskColor); AllegroBitmap carouselAllegroBitmap(m_CarouselBitmap.get()); float enableDisableProgress = static_cast(m_EnableDisableAnimationTimer.RealTimeLimitProgress()); - for (const std::unique_ptr &carouselItemBox : m_CarouselItemBoxes) { + for (const std::unique_ptr& carouselItemBox: m_CarouselItemBoxes) { if ((carouselItemBox->Item && carouselItemBox->Item->GetUniqueID() != 0) || (carouselItemBox->IsForEquippedItems && !m_InventoryActorEquippedItems.empty())) { DrawCarouselItemBoxBackground(*carouselItemBox); DrawCarouselItemBoxForeground(*carouselItemBox, &carouselAllegroBitmap); @@ -1352,23 +1413,25 @@ namespace RTE { bool hasDrawnAtLeastOnce = false; std::list wrappedRectangles; g_SceneMan.WrapRect(IntRect(drawPos.GetFloorIntX(), drawPos.GetFloorIntY(), drawPos.GetFloorIntX() + m_CarouselBitmap->w, drawPos.GetFloorIntY() + m_CarouselBitmap->h), wrappedRectangles); - for (const IntRect &wrappedRectangle : wrappedRectangles) { + for (const IntRect& wrappedRectangle: wrappedRectangles) { if (m_CarouselBackgroundTransparent && !g_FrameMan.IsInMultiplayerMode()) { g_FrameMan.SetTransTableFromPreset(TransparencyPreset::MoreTrans); draw_trans_sprite(targetBitmap, m_CarouselBGBitmap.get(), wrappedRectangle.m_Left - m_CarouselBGBitmap->w / 2, wrappedRectangle.m_Top - m_CarouselBGBitmap->h / 2); draw_sprite(targetBitmap, m_CarouselBitmap.get(), wrappedRectangle.m_Left - m_CarouselBitmap->w / 2, wrappedRectangle.m_Top - m_CarouselBitmap->h / 2); } else { - if (!hasDrawnAtLeastOnce) { draw_sprite(m_CarouselBGBitmap.get(), m_CarouselBitmap.get(), 0, 0); } + if (!hasDrawnAtLeastOnce) { + draw_sprite(m_CarouselBGBitmap.get(), m_CarouselBitmap.get(), 0, 0); + } draw_sprite(targetBitmap, m_CarouselBGBitmap.get(), wrappedRectangle.m_Left - m_CarouselBGBitmap->w / 2, wrappedRectangle.m_Top - m_CarouselBGBitmap->h / 2); } hasDrawnAtLeastOnce = true; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DrawCarouselItemBoxBackground(const CarouselItemBox &itemBoxToDraw) const { - auto DrawBox = [](BITMAP *targetBitmap, const Vector &boxTopLeftCorner, const Vector &boxBottomRightCorner, int color, bool roundedLeftSide, bool roundedRightSide) { + void InventoryMenuGUI::DrawCarouselItemBoxBackground(const CarouselItemBox& itemBoxToDraw) const { + auto DrawBox = [](BITMAP* targetBitmap, const Vector& boxTopLeftCorner, const Vector& boxBottomRightCorner, int color, bool roundedLeftSide, bool roundedRightSide) { if (roundedLeftSide) { circlefill(targetBitmap, boxTopLeftCorner.GetFloorIntX() + c_CarouselBoxCornerRadius, boxTopLeftCorner.GetFloorIntY() + c_CarouselBoxCornerRadius, c_CarouselBoxCornerRadius, color); circlefill(targetBitmap, boxTopLeftCorner.GetFloorIntX() + c_CarouselBoxCornerRadius, boxBottomRightCorner.GetFloorIntY() - c_CarouselBoxCornerRadius, c_CarouselBoxCornerRadius, color); @@ -1389,28 +1452,32 @@ namespace RTE { DrawBox(m_CarouselBGBitmap.get(), itemBoxToDraw.Pos + (itemBoxToDraw.RoundedAndBorderedSides.first ? m_CarouselBackgroundBoxBorderSize : Vector(0, m_CarouselBackgroundBoxBorderSize.GetY())), itemBoxToDraw.Pos + itemBoxToDraw.CurrentSize - spriteZeroIndexSizeOffset - (itemBoxToDraw.RoundedAndBorderedSides.second ? m_CarouselBackgroundBoxBorderSize : Vector(0, m_CarouselBackgroundBoxBorderSize.GetY())), m_CarouselBackgroundBoxColor, itemBoxToDraw.RoundedAndBorderedSides.first, itemBoxToDraw.RoundedAndBorderedSides.second); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DrawCarouselItemBoxForeground(const CarouselItemBox &itemBoxToDraw, AllegroBitmap *carouselAllegroBitmap) const { - std::vector itemIcons; + void InventoryMenuGUI::DrawCarouselItemBoxForeground(const CarouselItemBox& itemBoxToDraw, AllegroBitmap* carouselAllegroBitmap) const { + std::vector itemIcons; float totalItemMass = 0; itemBoxToDraw.GetIconsAndMass(itemIcons, totalItemMass, &m_InventoryActorEquippedItems); Vector spriteZeroIndexSizeOffset(1, 1); Vector multiItemDrawOffset = Vector(c_MultipleItemInBoxOffset * static_cast(itemIcons.size() - 1), -c_MultipleItemInBoxOffset * static_cast(itemIcons.size() - 1)); Vector iconMaxSize = itemBoxToDraw.CurrentSize - Vector(c_MinimumItemPadding * 2, c_MinimumItemPadding * 2) - spriteZeroIndexSizeOffset; - if (itemBoxToDraw.RoundedAndBorderedSides.first) { iconMaxSize.SetX(iconMaxSize.GetX() - m_CarouselBackgroundBoxBorderSize.GetX()); } - if (itemBoxToDraw.RoundedAndBorderedSides.second) { iconMaxSize.SetX(iconMaxSize.GetX() - m_CarouselBackgroundBoxBorderSize.GetX()); } - std::for_each(itemIcons.crbegin(), itemIcons.crend(), [this, &itemBoxToDraw, &multiItemDrawOffset, &iconMaxSize](BITMAP *iconToDraw) { + if (itemBoxToDraw.RoundedAndBorderedSides.first) { + iconMaxSize.SetX(iconMaxSize.GetX() - m_CarouselBackgroundBoxBorderSize.GetX()); + } + if (itemBoxToDraw.RoundedAndBorderedSides.second) { + iconMaxSize.SetX(iconMaxSize.GetX() - m_CarouselBackgroundBoxBorderSize.GetX()); + } + std::for_each(itemIcons.crbegin(), itemIcons.crend(), [this, &itemBoxToDraw, &multiItemDrawOffset, &iconMaxSize](BITMAP* iconToDraw) { if (iconToDraw) { float stretchRatio = std::max(static_cast(iconToDraw->w - 1 + (multiItemDrawOffset.GetFloorIntX() / 2)) / iconMaxSize.GetX(), static_cast(iconToDraw->h - 1 + (multiItemDrawOffset.GetFloorIntY() / 2)) / iconMaxSize.GetY()); if (stretchRatio > 1.0F) { float stretchedWidth = static_cast(iconToDraw->w) / stretchRatio; float stretchedHeight = static_cast(iconToDraw->h) / stretchRatio; stretch_sprite(m_CarouselBitmap.get(), iconToDraw, - itemBoxToDraw.IconCenterPosition.GetFloorIntX() - static_cast(itemBoxToDraw.RoundedAndBorderedSides.first ? std::floor(stretchedWidth / 2.0F) : std::ceil(stretchedWidth / 2.0F)) + multiItemDrawOffset.GetFloorIntX() + (itemBoxToDraw.RoundedAndBorderedSides.first ? m_CarouselBackgroundBoxBorderSize.GetFloorIntX() / 2 : 0) - (itemBoxToDraw.RoundedAndBorderedSides.second ? m_CarouselBackgroundBoxBorderSize.GetFloorIntX() / 2 : 0), - itemBoxToDraw.IconCenterPosition.GetFloorIntY() - static_cast(stretchedHeight / 2.0F) + multiItemDrawOffset.GetFloorIntY(), - static_cast(itemBoxToDraw.RoundedAndBorderedSides.first ? std::ceil(stretchedWidth) : std::floor(stretchedWidth)), static_cast(stretchedHeight)); + itemBoxToDraw.IconCenterPosition.GetFloorIntX() - static_cast(itemBoxToDraw.RoundedAndBorderedSides.first ? std::floor(stretchedWidth / 2.0F) : std::ceil(stretchedWidth / 2.0F)) + multiItemDrawOffset.GetFloorIntX() + (itemBoxToDraw.RoundedAndBorderedSides.first ? m_CarouselBackgroundBoxBorderSize.GetFloorIntX() / 2 : 0) - (itemBoxToDraw.RoundedAndBorderedSides.second ? m_CarouselBackgroundBoxBorderSize.GetFloorIntX() / 2 : 0), + itemBoxToDraw.IconCenterPosition.GetFloorIntY() - static_cast(stretchedHeight / 2.0F) + multiItemDrawOffset.GetFloorIntY(), + static_cast(itemBoxToDraw.RoundedAndBorderedSides.first ? std::ceil(stretchedWidth) : std::floor(stretchedWidth)), static_cast(stretchedHeight)); } else { draw_sprite(m_CarouselBitmap.get(), iconToDraw, itemBoxToDraw.IconCenterPosition.GetFloorIntX() - (iconToDraw->w / 2) + multiItemDrawOffset.GetFloorIntX(), itemBoxToDraw.IconCenterPosition.GetFloorIntY() - (iconToDraw->h / 2) + multiItemDrawOffset.GetFloorIntY()); } @@ -1422,14 +1489,16 @@ namespace RTE { m_SmallFont->DrawAligned(carouselAllegroBitmap, itemBoxToDraw.IconCenterPosition.GetFloorIntX(), itemBoxToDraw.IconCenterPosition.GetFloorIntY() - ((itemBoxToDraw.CurrentSize.GetFloorIntY() + m_SmallFont->GetFontHeight()) / 2) + 1, massString.c_str(), GUIFont::Centre); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DrawFullMode(BITMAP *targetBitmap, const Vector &drawPos) const { + void InventoryMenuGUI::DrawFullMode(BITMAP* targetBitmap, const Vector& drawPos) const { m_GUITopLevelBox->SetPositionAbs(drawPos.GetFloorIntX(), drawPos.GetFloorIntY()); if (IsEnablingOrDisabling()) { float enableDisableProgress = static_cast(m_EnableDisableAnimationTimer.RealTimeLimitProgress()); - if (m_EnabledState == EnabledState::Disabling) { enableDisableProgress = 1.0F - enableDisableProgress; } + if (m_EnabledState == EnabledState::Disabling) { + enableDisableProgress = 1.0F - enableDisableProgress; + } m_GUITopLevelBox->SetSize(static_cast(m_GUITopLevelBoxFullSize.GetX() * enableDisableProgress), static_cast(m_GUITopLevelBoxFullSize.GetY() * enableDisableProgress)); m_GUITopLevelBox->SetPositionAbs(m_GUITopLevelBox->GetXPos() + ((m_GUITopLevelBoxFullSize.GetFloorIntX() - m_GUITopLevelBox->GetWidth()) / 2), m_GUITopLevelBox->GetYPos() + ((m_GUITopLevelBoxFullSize.GetFloorIntY() - m_GUITopLevelBox->GetHeight()) / 2)); } @@ -1438,11 +1507,11 @@ namespace RTE { m_GUIControlManager->Draw(&guiScreen); if (IsEnabled() && !m_GUIDisplayOnly && m_MenuController->IsMouseControlled()) { if (m_GUISelectedItem && m_GUISelectedItem->DragWasHeldForLongEnough()) { - BITMAP *selectedObjectIcon = m_GUISelectedItem->Object->GetGraphicalIcon(); + BITMAP* selectedObjectIcon = m_GUISelectedItem->Object->GetGraphicalIcon(); draw_sprite(targetBitmap, selectedObjectIcon, m_GUICursorPos.GetFloorIntX() - (selectedObjectIcon->w / 2), m_GUICursorPos.GetFloorIntY() - (selectedObjectIcon->h / 2)); } else { draw_sprite(targetBitmap, s_CursorBitmap, m_GUICursorPos.GetFloorIntX(), m_GUICursorPos.GetFloorIntY()); } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/InventoryMenuGUI.h b/Source/Menus/InventoryMenuGUI.h index af1801960e..30c6783a81 100644 --- a/Source/Menus/InventoryMenuGUI.h +++ b/Source/Menus/InventoryMenuGUI.h @@ -28,11 +28,14 @@ namespace RTE { class InventoryMenuGUI { public: - /// /// Enumeration for the modes an InventoryMenuGUI can have. /// - enum class MenuMode { Carousel, Full, Transfer }; + enum class MenuMode { + Carousel, + Full, + Transfer + }; #pragma region Creation /// @@ -47,7 +50,7 @@ namespace RTE { /// The Actor whose inventory this GUI will display. Ownership is NOT transferred! /// The mode this menu should use when it's enabled. Defaults to Carousel. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(Controller *activityPlayerController, Actor *inventoryActor = nullptr, MenuMode menuMode = MenuMode::Carousel); + int Create(Controller* activityPlayerController, Actor* inventoryActor = nullptr, MenuMode menuMode = MenuMode::Carousel); #pragma endregion #pragma region Destruction @@ -67,19 +70,19 @@ namespace RTE { /// Sets the controller used by this. The ownership of the controller is NOT transferred! /// /// The new controller for this menu. Ownership is NOT transferred! - void SetController(Controller *controller) { m_MenuController = controller; } + void SetController(Controller* controller) { m_MenuController = controller; } /// /// Gets the Actor whose inventory this GUI will display. The ownership of the Actor is NOT transferred! /// /// The Actor whose inventory this GUI will display. Ownership is NOT transferred! - const Actor * GetInventoryActor() const { return m_InventoryActor; } + const Actor* GetInventoryActor() const { return m_InventoryActor; } /// /// Sets the Actor whose inventory this GUI will display. The ownership of the Actor is NOT transferred! /// /// The new Actor whose inventory this GUI will display. Ownership is NOT transferred! - void SetInventoryActor(Actor *newInventoryActor); + void SetInventoryActor(Actor* newInventoryActor); /// /// Gets the MenuMode this InventoryMenuGUI is currently in. @@ -145,7 +148,7 @@ namespace RTE { /// The index of this selected item in the displayed inventory. -1 means the item is not in the inventory. /// The index of this selected item in the vector of equipped items. -1 means the item is not in the inventory. /// Whether or not the selected item is being dragged. - void SetSelectedItem(GUIButton *selectedItemButton, MovableObject *selectedItemObject, int inventoryIndex, int equippedItemIndex, bool isBeingDragged); + void SetSelectedItem(GUIButton* selectedItemButton, MovableObject* selectedItemObject, int inventoryIndex, int equippedItemIndex, bool isBeingDragged); #pragma endregion #pragma region Updating @@ -159,16 +162,15 @@ namespace RTE { /// /// A pointer to a BITMAP to draw on. Generally a screen BITMAP. /// The absolute position of the target bitmap's upper left corner in the scene. - void Draw(BITMAP *targetBitmap, const Vector &targetPos = Vector()) const; + void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector()) const; #pragma endregion private: - /// /// A struct containing all information required to drawn and animate a carousel item box in Carousel MenuMode. /// struct CarouselItemBox { - MovableObject *Item; //!< A pointer to the item being displayed in the CarouselItemBox. + MovableObject* Item; //!< A pointer to the item being displayed in the CarouselItemBox. bool IsForEquippedItems; //!< Whether or not this CarouselItemBox is for displaying equipped items. Vector FullSize; //!< The full size for this CarouselItemBox when it's not animating. Vector CurrentSize; //!< The current size of this CarouselItemBox. @@ -183,15 +185,15 @@ namespace RTE { /// A vector of Bitmaps to be filled in with the icon(s) for this CarouselItemBox's item(s). /// A float to be filled in with the total mass of this CarouselItemBox's item(s). /// A pointer to the vector of sets of equipped items to be used if the CarouselItemBox IsForEquippedItem. - void GetIconsAndMass(std::vector &itemIcons, float &totalItemMass, const std::vector> *equippedItems) const; + void GetIconsAndMass(std::vector& itemIcons, float& totalItemMass, const std::vector>* equippedItems) const; }; /// /// A struct containing all information required to describe a selected item in Full/Transfer MenuMode. /// struct GUISelectedItem { - GUIButton *Button; //!< A pointer to the button for this GUISelectedItem. - MovableObject *Object; //!< A pointer to the MovableObject for this GUISelectedItem. Should always match up with what the Button is displaying. + GUIButton* Button; //!< A pointer to the button for this GUISelectedItem. + MovableObject* Object; //!< A pointer to the MovableObject for this GUISelectedItem. Should always match up with what the Button is displaying. int InventoryIndex; //!< The index in the InventoryItemsBox that this GUISelectedItem is for. Either this or the EquippedItemIndex must have a value. int EquippedItemIndex; //!< The index in the EquippedItemIndex that this GUISelectedItem is for. Either this or the InventoryIndex must have a value. bool IsBeingDragged; //!< Whether or not the GUISelectedItem is currently being dragged. @@ -207,12 +209,21 @@ namespace RTE { /// /// Enumeration for enabled states when enabling/disabling the InventoryMenuGUI. /// - enum class EnabledState { Enabling, Enabled, Disabling, Disabled }; + enum class EnabledState { + Enabling, + Enabled, + Disabling, + Disabled + }; /// /// Enumeration for which direction an inventory is being swapped in. /// - enum class CarouselAnimationDirection { Left = -1, None, Right}; + enum class CarouselAnimationDirection { + Left = -1, + None, + Right + }; static constexpr int c_ItemsPerRow = 5; //!< The number of items per row of the inventory display. MUST be an odd number. Used in all MenuModes. static constexpr int c_MinimumItemPadding = 1; //!< The padding between item icons and their containing boxes. Items will have at least this much padding on all sides. Used in all MenuModes. @@ -227,13 +238,13 @@ namespace RTE { static constexpr int c_FullViewPageItemLimit = c_ItemsPerRow * 3; //!< The default number of rows in the full inventory display. Used in Full/Transfer MenuModes. static constexpr int c_FullMenuVerticalOffset = 50; //!< How high above its target the full GUI will be. Used in Full/Transfer MenuModes. - static BITMAP *s_CursorBitmap; //!< The cursor image shared by all GUIs. + static BITMAP* s_CursorBitmap; //!< The cursor image shared by all GUIs. - GUIFont *m_SmallFont; //!< A pointer to the small font from FrameMan. Not owned here. - GUIFont *m_LargeFont; //!< A pointer to the large font from FrameMan. Not owned here. + GUIFont* m_SmallFont; //!< A pointer to the small font from FrameMan. Not owned here. + GUIFont* m_LargeFont; //!< A pointer to the large font from FrameMan. Not owned here. - Controller *m_MenuController; //!< The Controller which controls this menu. - Actor *m_InventoryActor; //!< The Actor whose inventory this GUI will display. + Controller* m_MenuController; //!< The Controller which controls this menu. + Actor* m_InventoryActor; //!< The Actor whose inventory this GUI will display. MenuMode m_MenuMode; //!< The mode this menu is in. See MenuMode enum for more details. Vector m_CenterPos; //!< The center position of this menu in the scene. @@ -241,7 +252,7 @@ namespace RTE { Timer m_EnableDisableAnimationTimer; //!< Timer for progressing enabling/disabling animations. bool m_InventoryActorIsHuman; //!< Whether the Actor whose inventory this GUI will display is an AHuman. - std::vector> m_InventoryActorEquippedItems; //!< A vector of pairs of pointers to the equipped item and equipped offhand item of the Actor whose inventory this GUI will display, if applicable. Pointers are NOT owned. + std::vector> m_InventoryActorEquippedItems; //!< A vector of pairs of pointers to the equipped item and equipped offhand item of the Actor whose inventory this GUI will display, if applicable. Pointers are NOT owned. bool m_CarouselDrawEmptyBoxes; //!< Whether or not the carousel should draw empty item boxes. Used in Carousel MenuMode. bool m_CarouselBackgroundTransparent; //!< Whether or not the carousel's background should be drawn transparently. Used in Carousel MenuMode. @@ -265,18 +276,18 @@ namespace RTE { Timer m_GUIRepeatStartTimer; //!< Measures the time to when to start repeating inputs when they're held down. Used in Full/Transfer MenuModes. Timer m_GUIRepeatTimer; //!< Measures the interval between input repeats. Used in Full/Transfer MenuModes. - GUIButton *m_NonMouseHighlightedButton; //!< A pointer to the GUIButton currently highlighted by the keyboard or gamepad. - //TODO These previous buttons should be cleaned up when InventoryMenuGUI is refactored. Requirements were unclear so this is more complicated than it needs to be, these 3 can be consolidated into 2 or 1 variable, and the code handling them can be simplified a bit. - GUIButton *m_NonMousePreviousEquippedItemsBoxButton; //!< A pointer to the last GUIButton in the EquippedItemsBox that was highlighted by the keyboard or gamepad. - GUIButton *m_NonMousePreviousInventoryItemsBoxButton; //!< A pointer to the last GUIButton in the InventoryItemsBox that was highlighted by the keyboard or gamepad. - GUIButton *m_NonMousePreviousReloadOrDropButton; //!< A pointer to whichever of the reload or drop GUIButtons was last highlighted by the keyboard or gamepad. + GUIButton* m_NonMouseHighlightedButton; //!< A pointer to the GUIButton currently highlighted by the keyboard or gamepad. + // TODO These previous buttons should be cleaned up when InventoryMenuGUI is refactored. Requirements were unclear so this is more complicated than it needs to be, these 3 can be consolidated into 2 or 1 variable, and the code handling them can be simplified a bit. + GUIButton* m_NonMousePreviousEquippedItemsBoxButton; //!< A pointer to the last GUIButton in the EquippedItemsBox that was highlighted by the keyboard or gamepad. + GUIButton* m_NonMousePreviousInventoryItemsBoxButton; //!< A pointer to the last GUIButton in the InventoryItemsBox that was highlighted by the keyboard or gamepad. + GUIButton* m_NonMousePreviousReloadOrDropButton; //!< A pointer to whichever of the reload or drop GUIButtons was last highlighted by the keyboard or gamepad. Vector m_GUITopLevelBoxFullSize; //!< A Vector holding the full size of the top level box for enabling/disabling animations. bool m_GUIShowInformationText; //!< Whether or information text explaining how to use the menu should be showing. - const Icon *m_GUIInformationToggleButtonIcon; //!< A pointer to the PresetMan pie icon for information, used here for the information toggle button. Not Owned here. - const Icon *m_GUIReloadButtonIcon; //!< A pointer to the PresetMan pie icon for reloading, used here for the reload button. Not Owned here. - const Icon *m_GUIDropButtonIcon; //!< A pointer to the PresetMan pie icon for dropping items, used here for the drop button. Not Owned here. - std::vector> m_GUIInventoryItemButtons; //!< A vector of pairs of MovableObject pointers and GUIButton pointers, connecting inventory GUIButtons to their corresponding MovableObjects. + const Icon* m_GUIInformationToggleButtonIcon; //!< A pointer to the PresetMan pie icon for information, used here for the information toggle button. Not Owned here. + const Icon* m_GUIReloadButtonIcon; //!< A pointer to the PresetMan pie icon for reloading, used here for the reload button. Not Owned here. + const Icon* m_GUIDropButtonIcon; //!< A pointer to the PresetMan pie icon for dropping items, used here for the drop button. Not Owned here. + std::vector> m_GUIInventoryItemButtons; //!< A vector of pairs of MovableObject pointers and GUIButton pointers, connecting inventory GUIButtons to their corresponding MovableObjects. /// /// GUI elements that make up the full mode InventoryMenuGUI. @@ -284,17 +295,17 @@ namespace RTE { std::unique_ptr m_GUIControlManager; std::unique_ptr m_GUIScreen; std::unique_ptr m_GUIInput; - GUICollectionBox *m_GUITopLevelBox; - GUILabel *m_GUIInformationText; - GUIButton *m_GUIInformationToggleButton; - GUICollectionBox *m_GUIEquippedItemsBox; - GUIButton *m_GUISwapSetButton; - GUIButton *m_GUIEquippedItemButton; - GUIButton *m_GUIOffhandEquippedItemButton; - GUIButton *m_GUIReloadButton; - GUIButton *m_GUIDropButton; - GUICollectionBox *m_GUIInventoryItemsBox; - GUIScrollbar *m_GUIInventoryItemsScrollbar; + GUICollectionBox* m_GUITopLevelBox; + GUILabel* m_GUIInformationText; + GUIButton* m_GUIInformationToggleButton; + GUICollectionBox* m_GUIEquippedItemsBox; + GUIButton* m_GUISwapSetButton; + GUIButton* m_GUIEquippedItemButton; + GUIButton* m_GUIOffhandEquippedItemButton; + GUIButton* m_GUIReloadButton; + GUIButton* m_GUIDropButton; + GUICollectionBox* m_GUIInventoryItemsBox; + GUIScrollbar* m_GUIInventoryItemsScrollbar; #pragma region Create Breakdown /// @@ -347,19 +358,19 @@ namespace RTE { /// Handles hiding and showing the scrollbar, and calculating everything related to it in Full MenuMode. /// /// A pointer to the inventory this InventoryMenuGUI is displaying. - void UpdateFullModeScrollbar(const std::deque *inventory); + void UpdateFullModeScrollbar(const std::deque* inventory); /// /// Handles content and enabled status for inventory item buttons in Full MenuMode. Does not deal with button input. /// /// A pointer to the inventory this InventoryMenuGUI is displaying. - void UpdateFullModeInventoryItemButtons(const std::deque *inventory); + void UpdateFullModeInventoryItemButtons(const std::deque* inventory); /// /// Handles everything for displaying information text in Full MenuMode. /// /// A pointer to the inventory this InventoryMenuGUI is displaying. - void UpdateFullModeInformationText(const std::deque *inventory); + void UpdateFullModeInformationText(const std::deque* inventory); /// /// Handles updating icons and widths to support higlighting, for non-item buttons in Full MenuMode. @@ -399,25 +410,25 @@ namespace RTE { /// Breakdown of HandleNonMouseInput for handling pressing/holding up. /// /// The next button to highlight, based on input handling. - GUIButton * HandleNonMouseUpInput(); + GUIButton* HandleNonMouseUpInput(); /// /// Breakdown of HandleNonMouseInput for handling pressing/holding down. /// /// The next button to highlight, based on input handling. - GUIButton * HandleNonMouseDownInput(); + GUIButton* HandleNonMouseDownInput(); /// /// Breakdown of HandleNonMouseInput for handling pressing/holding left. /// /// The next button to highlight, based on input handling. - GUIButton * HandleNonMouseLeftInput(); + GUIButton* HandleNonMouseLeftInput(); /// /// Breakdown of HandleNonMouseInput for handling pressing/holding right. /// /// The next button to highlight, based on input handling. - GUIButton * HandleNonMouseRightInput(); + GUIButton* HandleNonMouseRightInput(); /// /// Handles item button press command events from the GUIControls of this InventoryMenuGUI by selecting/de-selecting the corresponding item. @@ -426,15 +437,15 @@ namespace RTE { /// A pointer to the MovableObject the GUIButton represents. /// The index of this button in the vector of equipped items if applicable. The default value of -1 means it's not an equipped item. /// Whether or not the button was held. - void HandleItemButtonPressOrHold(GUIButton *pressedButton, MovableObject *buttonObject, int buttonEquippedItemIndex = -1, bool buttonHeld = false); + void HandleItemButtonPressOrHold(GUIButton* pressedButton, MovableObject* buttonObject, int buttonEquippedItemIndex = -1, bool buttonHeld = false); #pragma endregion #pragma region GUI Button Actions - //TODO implement swap mode + // TODO implement swap mode /// /// Handles set swap button press command events from the GUIControls of this InventoryMenuGUI. /// - //void SwapEquippedItemSet() {} + // void SwapEquippedItemSet() {} /// /// Swaps the equipped item at the given equipped item index with one in the inventory Actor's inventory at the given inventory item index. @@ -454,7 +465,7 @@ namespace RTE { /// Drops the selected item, rotating its drop velocity in the direction of the passed in dropDirection Vector. /// /// A pointer to a Vector containing the direction the selected item should be dropped in. Nullptr means standard dropping will be used. - void DropSelectedItem(const Vector *dropDirection = nullptr); + void DropSelectedItem(const Vector* dropDirection = nullptr); #pragma endregion #pragma region Draw Breakdown @@ -463,27 +474,27 @@ namespace RTE { /// /// A pointer to a BITMAP to draw on. Generally a screen BITMAP. /// The position at which to draw the carousel. - void DrawCarouselMode(BITMAP *targetBitmap, const Vector &drawPos) const; + void DrawCarouselMode(BITMAP* targetBitmap, const Vector& drawPos) const; /// /// Draws the InventoryMenuGUI when it's in Full MenuMode. /// /// A pointer to a BITMAP to draw on. Generally a screen BITMAP. /// The position at which to draw the GUI. - void DrawFullMode(BITMAP *targetBitmap, const Vector &drawPos) const; + void DrawFullMode(BITMAP* targetBitmap, const Vector& drawPos) const; /// /// Draws the specified CarouselItemBox's background to the carousel background Bitmap. /// /// The CarouselItemBox to draw. - void DrawCarouselItemBoxBackground(const CarouselItemBox &itemBoxToDraw) const; + void DrawCarouselItemBoxBackground(const CarouselItemBox& itemBoxToDraw) const; /// /// Draws the specified CarouselItemBox's item(s) and mass text to the carousel Bitmap. /// /// The CarouselItemBox to draw. /// An AllegroBitmap of the bitmap the CarouselItemBox should draw its foreground to. Used for drawing mass strings, and predefined to avoid needless creation. - void DrawCarouselItemBoxForeground(const CarouselItemBox &itemBoxToDraw, AllegroBitmap *carouselAllegroBitmap) const; + void DrawCarouselItemBoxForeground(const CarouselItemBox& itemBoxToDraw, AllegroBitmap* carouselAllegroBitmap) const; #pragma endregion /// @@ -492,8 +503,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - InventoryMenuGUI(const InventoryMenuGUI &reference) = delete; - InventoryMenuGUI & operator=(const InventoryMenuGUI &rhs) = delete; + InventoryMenuGUI(const InventoryMenuGUI& reference) = delete; + InventoryMenuGUI& operator=(const InventoryMenuGUI& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Menus/LoadingScreen.cpp b/Source/Menus/LoadingScreen.cpp index 0e6fa6e22b..06a6efe3f9 100644 --- a/Source/Menus/LoadingScreen.cpp +++ b/Source/Menus/LoadingScreen.cpp @@ -14,7 +14,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LoadingScreen::Clear() { m_LoadingLogWriter = nullptr; @@ -24,9 +24,9 @@ namespace RTE { m_ProgressListboxPosY = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::Create(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool progressReportDisabled) { + void LoadingScreen::Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool progressReportDisabled) { GUIControlManager loadingScreenManager; RTEAssert(loadingScreenManager.Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "LoadingScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/LoadingScreenSkin.ini"); loadingScreenManager.Load("Base.rte/GUIs/LoadingGUI.ini"); @@ -59,14 +59,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LoadingScreen::CreateLoadingSplash(int xOffset) { if (m_LoadingSplashBitmap) { destroy_bitmap(m_LoadingSplashBitmap); m_LoadingSplashBitmap = nullptr; } - const BITMAP *backbuffer = g_FrameMan.GetBackBuffer32(); + const BITMAP* backbuffer = g_FrameMan.GetBackBuffer32(); m_LoadingSplashBitmap = create_bitmap_ex(FrameMan::c_BPP, backbuffer->w, backbuffer->h); clear_bitmap(m_LoadingSplashBitmap); @@ -79,14 +79,16 @@ namespace RTE { loadingSplash.Draw(m_LoadingSplashBitmap, loadingSplashTargetBox); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::CreateProgressReportListbox(GUIControlManager *parentControlManager) { - dynamic_cast(parentControlManager->GetControl("root"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); - GUIListBox *listBox = dynamic_cast(parentControlManager->GetControl("ProgressBox")); + void LoadingScreen::CreateProgressReportListbox(GUIControlManager* parentControlManager) { + dynamic_cast(parentControlManager->GetControl("root"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + GUIListBox* listBox = dynamic_cast(parentControlManager->GetControl("ProgressBox")); // Make the box a bit bigger if there's room in higher, HD resolutions. - if (g_WindowMan.GetResX() >= c_DefaultResX) { listBox->Resize((g_WindowMan.GetResX() / 3) - 12, listBox->GetHeight()); } + if (g_WindowMan.GetResX() >= c_DefaultResX) { + listBox->Resize((g_WindowMan.GetResX() / 3) - 12, listBox->GetHeight()); + } listBox->SetPositionRel(g_WindowMan.GetResX() - listBox->GetWidth() - 12, (g_WindowMan.GetResY() / 2) - (listBox->GetHeight() / 2)); listBox->ClearList(); @@ -102,7 +104,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LoadingScreen::Destroy() { destroy_bitmap(m_LoadingSplashBitmap); @@ -112,15 +114,21 @@ namespace RTE { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::LoadingSplashProgressReport(const std::string &reportString, bool newItem) { - if (System::IsLoggingToCLI()) { System::PrintLoadingToCLI(reportString, newItem); } + void LoadingScreen::LoadingSplashProgressReport(const std::string& reportString, bool newItem) { + if (System::IsLoggingToCLI()) { + System::PrintLoadingToCLI(reportString, newItem); + } if (newItem) { // Write out the last line to the log file before starting a new one and scroll the bitmap upwards. - if (g_LoadingScreen.m_LoadingLogWriter) { g_LoadingScreen.m_LoadingLogWriter->NewLineString(reportString, false); } - if (g_LoadingScreen.m_ProgressListboxBitmap) { blit(g_LoadingScreen.m_ProgressListboxBitmap, g_LoadingScreen.m_ProgressListboxBitmap, 2, 12, 2, 2, g_LoadingScreen.m_ProgressListboxBitmap->w - 3, g_LoadingScreen.m_ProgressListboxBitmap->h - 12); } + if (g_LoadingScreen.m_LoadingLogWriter) { + g_LoadingScreen.m_LoadingLogWriter->NewLineString(reportString, false); + } + if (g_LoadingScreen.m_ProgressListboxBitmap) { + blit(g_LoadingScreen.m_ProgressListboxBitmap, g_LoadingScreen.m_ProgressListboxBitmap, 2, 12, 2, 2, g_LoadingScreen.m_ProgressListboxBitmap->w - 3, g_LoadingScreen.m_ProgressListboxBitmap->h - 12); + } } if (g_LoadingScreen.m_ProgressListboxBitmap) { @@ -141,9 +149,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void LoadingScreen::DrawLoadingSplash() { draw_sprite(g_FrameMan.GetBackBuffer32(), m_LoadingSplashBitmap, 0, 0); } -} +} // namespace RTE diff --git a/Source/Menus/LoadingScreen.h b/Source/Menus/LoadingScreen.h index 2486610f84..1400f97bc4 100644 --- a/Source/Menus/LoadingScreen.h +++ b/Source/Menus/LoadingScreen.h @@ -18,7 +18,6 @@ namespace RTE { class LoadingScreen : public Singleton { public: - #pragma region Creation /// /// Constructor method used to instantiate a LoadingScreen object in system memory. @@ -31,7 +30,7 @@ namespace RTE { /// Pointer to a GUIScreen interface that will be used by this LoadingScreen's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this LoadingScreen's GUIControlManager. Ownership is NOT transferred! /// Whether the loading screen progress report is disabled meaning GUI elements and adjustments relevant to it can be skipped. - void Create(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool progressReportDisabled); + void Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool progressReportDisabled); /// /// Creates the loading splash screen and draws the composed frame to the LoadingSplashBitmap. @@ -44,7 +43,7 @@ namespace RTE { /// As it turned out, a massive amount of time is spent updating the GUI control and flipping the frame buffers. /// /// Pointer to the parent GUIControlManager which owns all the GUIControls of this LoadingScreen. Ownership is NOT transferred! - void CreateProgressReportListbox(GUIControlManager *parentControlManager); + void CreateProgressReportListbox(GUIControlManager* parentControlManager); #pragma endregion #pragma region Destruction @@ -60,7 +59,7 @@ namespace RTE { /// /// The string to print in the report and log. /// Whether to start a new line in the log writer and to scroll the bitmap. - static void LoadingSplashProgressReport(const std::string &reportString, bool newItem = false); + static void LoadingSplashProgressReport(const std::string& reportString, bool newItem = false); /// /// Draws the loading splash to the screen. @@ -69,11 +68,10 @@ namespace RTE { #pragma endregion private: - std::unique_ptr m_LoadingLogWriter; //!< The Writer that generates the loading log. - BITMAP *m_LoadingSplashBitmap; //!< BITMAP that is used for drawing the splash screen. - BITMAP *m_ProgressListboxBitmap; //!< BITMAP that the progress report will be drawn into. + BITMAP* m_LoadingSplashBitmap; //!< BITMAP that is used for drawing the splash screen. + BITMAP* m_ProgressListboxBitmap; //!< BITMAP that the progress report will be drawn into. int m_ProgressListboxPosX; //!< Position of the progress report box on X axis. int m_ProgressListboxPosY; //!< Position of the progress report box on Y axis. @@ -83,8 +81,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - LoadingScreen(const LoadingScreen &reference) = delete; - LoadingScreen &operator=(const LoadingScreen &rhs) = delete; + LoadingScreen(const LoadingScreen& reference) = delete; + LoadingScreen& operator=(const LoadingScreen& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/MainMenuGUI.cpp b/Source/Menus/MainMenuGUI.cpp index 11c69ccbef..7582440fe8 100644 --- a/Source/Menus/MainMenuGUI.cpp +++ b/Source/Menus/MainMenuGUI.cpp @@ -20,7 +20,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::Clear() { m_RootBoxMaxWidth = 0; @@ -54,9 +54,9 @@ namespace RTE { m_MainScreenPrevHoveredButtonIndex = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::Create(AllegroScreen *guiScreen, GUIInputWrapper *guiInput) { + void MainMenuGUI::Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { m_MainMenuScreenGUIControlManager = std::make_unique(); RTEAssert(m_MainMenuScreenGUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuScreenSkin.ini"); m_MainMenuScreenGUIControlManager->Load("Base.rte/GUIs/MainMenuGUI.ini"); @@ -67,10 +67,10 @@ namespace RTE { m_RootBoxMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); - GUICollectionBox *mainScreenRootBox = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("root")); + GUICollectionBox* mainScreenRootBox = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("root")); mainScreenRootBox->Resize(m_RootBoxMaxWidth, mainScreenRootBox->GetHeight()); - GUICollectionBox *subMenuScreenRootBox = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("root")); + GUICollectionBox* subMenuScreenRootBox = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("root")); subMenuScreenRootBox->Resize(m_RootBoxMaxWidth, g_WindowMan.GetResY()); m_MainMenuButtons[MenuButton::BackToMainButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonBackToMain")); @@ -90,22 +90,22 @@ namespace RTE { SetActiveMenuScreen(g_WindowMan.ResolutionChanged() ? MenuScreen::SettingsScreen : MenuScreen::MainScreen, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::CreateMainScreen() { - m_MainMenuScreens[MenuScreen::MainScreen] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("MainScreen")); + m_MainMenuScreens[MenuScreen::MainScreen] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("MainScreen")); m_MainMenuScreens[MenuScreen::MainScreen]->CenterInParent(true, false); - m_MainMenuButtons[MenuButton::MetaGameButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToMetaGame")); - m_MainMenuButtons[MenuButton::ScenarioButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToSkirmish")); - m_MainMenuButtons[MenuButton::MultiplayerButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToMultiplayer")); + m_MainMenuButtons[MenuButton::MetaGameButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToMetaGame")); + m_MainMenuButtons[MenuButton::ScenarioButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToSkirmish")); + m_MainMenuButtons[MenuButton::MultiplayerButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToMultiplayer")); m_MainMenuButtons[MenuButton::SaveOrLoadGameButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonSaveOrLoadGame")); - m_MainMenuButtons[MenuButton::SettingsButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToOptions")); - m_MainMenuButtons[MenuButton::ModManagerButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToModManager")); - m_MainMenuButtons[MenuButton::EditorsButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToEditor")); - m_MainMenuButtons[MenuButton::CreditsButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToCreds")); - m_MainMenuButtons[MenuButton::QuitButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonQuit")); - m_MainMenuButtons[MenuButton::ResumeButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonResume")); + m_MainMenuButtons[MenuButton::SettingsButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToOptions")); + m_MainMenuButtons[MenuButton::ModManagerButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToModManager")); + m_MainMenuButtons[MenuButton::EditorsButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToEditor")); + m_MainMenuButtons[MenuButton::CreditsButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonMainToCreds")); + m_MainMenuButtons[MenuButton::QuitButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonQuit")); + m_MainMenuButtons[MenuButton::ResumeButton] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("ButtonResume")); for (int mainScreenButton = MenuButton::MetaGameButton; mainScreenButton <= MenuButton::ResumeButton; ++mainScreenButton) { m_MainMenuButtons.at(mainScreenButton)->CenterInParent(true, false); @@ -118,49 +118,49 @@ namespace RTE { m_MainMenuButtons.at(mainScreenButton)->SetText(m_MainScreenButtonUnhoveredText.at(mainScreenButton)); } - m_VersionLabel = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("VersionLabel")); + m_VersionLabel = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("VersionLabel")); m_VersionLabel->SetText("Community Project\nv" + c_GameVersion.str()); m_VersionLabel->SetPositionAbs(10, g_WindowMan.GetResY() - m_VersionLabel->GetTextHeight() - 5); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::CreateMetaGameNoticeScreen() { - m_MainMenuScreens[MenuScreen::MetaGameNoticeScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("MetaScreen")); + m_MainMenuScreens[MenuScreen::MetaGameNoticeScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("MetaScreen")); m_MainMenuScreens[MenuScreen::MetaGameNoticeScreen]->CenterInParent(true, false); - m_MainMenuButtons[MenuButton::PlayTutorialButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonTutorial")); - m_MainMenuButtons[MenuButton::MetaGameContinueButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonContinue")); + m_MainMenuButtons[MenuButton::PlayTutorialButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonTutorial")); + m_MainMenuButtons[MenuButton::MetaGameContinueButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonContinue")); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::CreateEditorsScreen() { - m_MainMenuScreens[MenuScreen::EditorScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("EditorScreen")); + m_MainMenuScreens[MenuScreen::EditorScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("EditorScreen")); m_MainMenuScreens[MenuScreen::EditorScreen]->CenterInParent(true, false); - m_MainMenuButtons[MenuButton::SceneEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonSceneEditor")); - m_MainMenuButtons[MenuButton::AreaEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonAreaEditor")); - m_MainMenuButtons[MenuButton::AssemblyEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonAssemblyEditor")); - m_MainMenuButtons[MenuButton::GibEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonGibPlacement")); - m_MainMenuButtons[MenuButton::ActorEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonActorEditor")); + m_MainMenuButtons[MenuButton::SceneEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonSceneEditor")); + m_MainMenuButtons[MenuButton::AreaEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonAreaEditor")); + m_MainMenuButtons[MenuButton::AssemblyEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonAssemblyEditor")); + m_MainMenuButtons[MenuButton::GibEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonGibPlacement")); + m_MainMenuButtons[MenuButton::ActorEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonActorEditor")); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::CreateCreditsScreen() { - m_MainMenuScreens[MenuScreen::CreditsScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("CreditsScreen")); + m_MainMenuScreens[MenuScreen::CreditsScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("CreditsScreen")); m_MainMenuScreens[MenuScreen::CreditsScreen]->Resize(m_MainMenuScreens[MenuScreen::CreditsScreen]->GetWidth(), g_WindowMan.GetResY()); m_MainMenuScreens[MenuScreen::CreditsScreen]->CenterInParent(true, false); - m_CreditsScrollPanel = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("CreditsPanel")); + m_CreditsScrollPanel = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("CreditsPanel")); m_CreditsScrollPanel->Resize(m_CreditsScrollPanel->GetWidth(), g_WindowMan.GetResY() - m_CreditsScrollPanel->GetYPos() - 50); - m_CreditsTextLabel = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("CreditsLabel")); + m_CreditsTextLabel = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("CreditsLabel")); // TODO: Get Unicode going! // Hack here to change the special characters over 128 in the ANSI ASCII table to match our font files - for (char &stringChar : s_CreditsText) { + for (char& stringChar: s_CreditsText) { if (stringChar == -60) { stringChar = static_cast(142); //'Ä' } else if (stringChar == -42) { @@ -173,26 +173,28 @@ namespace RTE { m_CreditsTextLabel->ResizeHeightToFit(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::CreateQuitScreen() { - m_MainMenuScreens[MenuScreen::QuitScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("QuitConfirmBox")); + m_MainMenuScreens[MenuScreen::QuitScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("QuitConfirmBox")); m_MainMenuScreens[MenuScreen::QuitScreen]->CenterInParent(true, false); - m_MainMenuButtons[MenuButton::QuitConfirmButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("QuitConfirmButton")); - m_MainMenuButtons[MenuButton::QuitCancelButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("QuitCancelButton")); + m_MainMenuButtons[MenuButton::QuitConfirmButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("QuitConfirmButton")); + m_MainMenuButtons[MenuButton::QuitCancelButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("QuitCancelButton")); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::HideAllScreens() { - for (GUICollectionBox *menuScreen : m_MainMenuScreens) { - if (menuScreen) { menuScreen->SetVisible(false); } + for (GUICollectionBox* menuScreen: m_MainMenuScreens) { + if (menuScreen) { + menuScreen->SetVisible(false); + } } m_MenuScreenChange = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::SetActiveMenuScreen(MenuScreen screenToShow, bool playButtonPressSound) { if (screenToShow != m_ActiveMenuScreen) { @@ -205,13 +207,13 @@ namespace RTE { m_SaveLoadMenu->Refresh(); } - if (playButtonPressSound) { - g_GUISound.ButtonPressSound()->Play(); + if (playButtonPressSound) { + g_GUISound.ButtonPressSound()->Play(); } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::ShowMainScreen() { m_VersionLabel->SetVisible(true); @@ -225,7 +227,7 @@ namespace RTE { m_MenuScreenChange = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::ShowMetaGameNoticeScreen() { m_MainMenuScreens[MenuScreen::MetaGameNoticeScreen]->SetVisible(true); @@ -234,14 +236,13 @@ namespace RTE { m_MainMenuButtons[MenuButton::BackToMainButton]->SetVisible(true); m_MainMenuButtons[MenuButton::BackToMainButton]->SetPositionAbs((m_RootBoxMaxWidth - m_MainMenuButtons[MenuButton::BackToMainButton]->GetWidth()) / 2, m_MainMenuButtons[MenuButton::MetaGameContinueButton]->GetYPos() + 25); - GUILabel *metaNoticeLabel = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("MetaLabel")); + GUILabel* metaNoticeLabel = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("MetaLabel")); std::string metaNotice = { - "- A T T E N T I O N -\n\n" - "Please note that Conquest mode is very INCOMPLETE and flawed, and the Cortex Command Community Project team will be remaking it from the ground-up in future. " - "For now though, it's fully playable, and you can absolutely enjoy playing it with the A.I. and/or up to three friends. Alternatively, check out Void Wanderers on cccp.mod.io!\n\n" - "Also, if you have not yet played Cortex Command, we recommend you first try the tutorial:" - }; + "- A T T E N T I O N -\n\n" + "Please note that Conquest mode is very INCOMPLETE and flawed, and the Cortex Command Community Project team will be remaking it from the ground-up in future. " + "For now though, it's fully playable, and you can absolutely enjoy playing it with the A.I. and/or up to three friends. Alternatively, check out Void Wanderers on cccp.mod.io!\n\n" + "Also, if you have not yet played Cortex Command, we recommend you first try the tutorial:"}; metaNoticeLabel->SetText(metaNotice); metaNoticeLabel->SetVisible(true); @@ -251,7 +252,7 @@ namespace RTE { m_MenuScreenChange = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::ShowEditorsScreen() { m_MainMenuScreens[MenuScreen::EditorScreen]->SetVisible(true); @@ -263,7 +264,7 @@ namespace RTE { m_MenuScreenChange = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::ShowCreditsScreen() { m_MainMenuScreens[MenuScreen::CreditsScreen]->SetVisible(true); @@ -280,7 +281,7 @@ namespace RTE { m_MenuScreenChange = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::ShowQuitScreenOrQuit() { if (m_ActiveMenuScreen != MenuScreen::QuitScreen && g_ActivityMan.GetActivity() && (g_ActivityMan.GetActivity()->GetActivityState() == Activity::Running || g_ActivityMan.GetActivity()->GetActivityState() == Activity::Editing)) { @@ -292,7 +293,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::ShowAndBlinkResumeButton() { if (!m_MainMenuButtons[MenuButton::ResumeButton]->GetVisible()) { @@ -310,7 +311,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool MainMenuGUI::RollCredits() { int scrollDuration = m_CreditsTextLabel->GetHeight() * 50; @@ -324,7 +325,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// MainMenuGUI::MainMenuUpdateResult MainMenuGUI::Update() { m_UpdateResult = MainMenuUpdateResult::NoEvent; @@ -337,12 +338,16 @@ namespace RTE { switch (m_ActiveMenuScreen) { case MenuScreen::MainScreen: - if (m_MenuScreenChange) { ShowMainScreen(); } + if (m_MenuScreenChange) { + ShowMainScreen(); + } backToMainMenu = HandleInputEvents(); ShowAndBlinkResumeButton(); break; case MenuScreen::MetaGameNoticeScreen: - if (m_MenuScreenChange) { ShowMetaGameNoticeScreen(); } + if (m_MenuScreenChange) { + ShowMetaGameNoticeScreen(); + } backToMainMenu = HandleInputEvents(); break; case MenuScreen::SaveOrLoadGameScreen: @@ -356,11 +361,15 @@ namespace RTE { backToMainMenu = m_ModManagerMenu->HandleInputEvents(); break; case MenuScreen::EditorScreen: - if (m_MenuScreenChange) { ShowEditorsScreen(); } + if (m_MenuScreenChange) { + ShowEditorsScreen(); + } backToMainMenu = HandleInputEvents(); break; case MenuScreen::CreditsScreen: - if (m_MenuScreenChange) { ShowCreditsScreen(); } + if (m_MenuScreenChange) { + ShowCreditsScreen(); + } backToMainMenu = RollCredits() ? true : HandleInputEvents(); break; case MenuScreen::QuitScreen: @@ -372,17 +381,21 @@ namespace RTE { } HandleBackNavigation(backToMainMenu); - if (m_UpdateResult == MainMenuUpdateResult::ActivityStarted || m_UpdateResult == MainMenuUpdateResult::ActivityResumed) { m_MainMenuButtons[MenuButton::ResumeButton]->SetVisible(false); } + if (m_UpdateResult == MainMenuUpdateResult::ActivityStarted || m_UpdateResult == MainMenuUpdateResult::ActivityResumed) { + m_MainMenuButtons[MenuButton::ResumeButton]->SetVisible(false); + } return m_UpdateResult; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::HandleBackNavigation(bool backButtonPressed) { if ((!m_ActiveDialogBox || m_ActiveDialogBox == m_MainMenuScreens[MenuScreen::QuitScreen]) && (backButtonPressed || g_UInputMan.KeyPressed(SDLK_ESCAPE))) { if (m_ActiveMenuScreen != MenuScreen::MainScreen) { if (m_ActiveMenuScreen == MenuScreen::SettingsScreen || m_ActiveMenuScreen == MenuScreen::ModManagerScreen) { - if (m_ActiveMenuScreen == MenuScreen::SettingsScreen) { m_SettingsMenu->RefreshActiveSettingsMenuScreen(); } + if (m_ActiveMenuScreen == MenuScreen::SettingsScreen) { + m_SettingsMenu->RefreshActiveSettingsMenuScreen(); + } g_SettingsMan.UpdateSettingsFile(); } else if (m_ActiveMenuScreen == MenuScreen::CreditsScreen) { m_UpdateResult = MainMenuUpdateResult::BackToMainFromCredits; @@ -398,14 +411,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool MainMenuGUI::HandleInputEvents() { if (m_ActiveMenuScreen == MenuScreen::MainScreen) { int mouseX = 0; int mouseY = 0; m_ActiveGUIControlManager->GetManager()->GetInputController()->GetMousePosition(&mouseX, &mouseY); - UpdateMainScreenHoveredButton(dynamic_cast(m_MainMenuScreenGUIControlManager->GetControlUnderPoint(mouseX, mouseY, m_MainMenuScreens[MenuScreen::MainScreen], 1))); + UpdateMainScreenHoveredButton(dynamic_cast(m_MainMenuScreenGUIControlManager->GetControlUnderPoint(mouseX, mouseY, m_MainMenuScreens[MenuScreen::MainScreen], 1))); } m_ActiveGUIControlManager->Update(); @@ -431,14 +444,16 @@ namespace RTE { default: break; } - } else if (guiEvent.GetType() == GUIEvent::Notification && (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl()))) { g_GUISound.SelectionChangeSound()->Play(); } + } else if (guiEvent.GetType() == GUIEvent::Notification && (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl()))) { + g_GUISound.SelectionChangeSound()->Play(); + } } return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HandleMainScreenInputEvents(const GUIControl *guiEventControl) { + void MainMenuGUI::HandleMainScreenInputEvents(const GUIControl* guiEventControl) { if (guiEventControl == m_MainMenuButtons[MenuButton::MetaGameButton]) { if (!m_MetaGameNoticeShown) { SetActiveMenuScreen(MenuScreen::MetaGameNoticeScreen); @@ -471,9 +486,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HandleMetaGameNoticeScreenInputEvents(const GUIControl *guiEventControl) { + void MainMenuGUI::HandleMetaGameNoticeScreenInputEvents(const GUIControl* guiEventControl) { if (guiEventControl == m_MainMenuButtons[MenuButton::PlayTutorialButton]) { m_UpdateResult = MainMenuUpdateResult::ActivityStarted; SetActiveMenuScreen(MenuScreen::MainScreen); @@ -483,9 +498,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HandleEditorsScreenInputEvents(const GUIControl *guiEventControl) { + void MainMenuGUI::HandleEditorsScreenInputEvents(const GUIControl* guiEventControl) { std::string editorToStart; if (guiEventControl == m_MainMenuButtons[MenuButton::SceneEditorButton]) { editorToStart = "SceneEditor"; @@ -506,9 +521,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HandleQuitScreenInputEvents(const GUIControl *guiEventControl) { + void MainMenuGUI::HandleQuitScreenInputEvents(const GUIControl* guiEventControl) { if (guiEventControl == m_MainMenuButtons[MenuButton::QuitConfirmButton]) { m_UpdateResult = MainMenuUpdateResult::Quit; g_GUISound.ButtonPressSound()->Play(); @@ -517,16 +532,20 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::UpdateMainScreenHoveredButton(const GUIButton *hoveredButton) { + void MainMenuGUI::UpdateMainScreenHoveredButton(const GUIButton* hoveredButton) { int hoveredButtonIndex = -1; if (hoveredButton) { hoveredButtonIndex = std::distance(m_MainMenuButtons.begin(), std::find(m_MainMenuButtons.begin(), m_MainMenuButtons.end(), hoveredButton)); - if (hoveredButton != m_MainScreenHoveredButton) { m_MainMenuButtons.at(hoveredButtonIndex)->SetText(m_MainScreenButtonHoveredText.at(hoveredButtonIndex)); } + if (hoveredButton != m_MainScreenHoveredButton) { + m_MainMenuButtons.at(hoveredButtonIndex)->SetText(m_MainScreenButtonHoveredText.at(hoveredButtonIndex)); + } m_MainScreenHoveredButton = m_MainMenuButtons.at(hoveredButtonIndex); } - if (!hoveredButton || hoveredButtonIndex != m_MainScreenPrevHoveredButtonIndex) { m_MainMenuButtons.at(m_MainScreenPrevHoveredButtonIndex)->SetText(m_MainScreenButtonUnhoveredText.at(m_MainScreenPrevHoveredButtonIndex)); } + if (!hoveredButton || hoveredButtonIndex != m_MainScreenPrevHoveredButtonIndex) { + m_MainMenuButtons.at(m_MainScreenPrevHoveredButtonIndex)->SetText(m_MainScreenButtonUnhoveredText.at(m_MainScreenPrevHoveredButtonIndex)); + } if (hoveredButtonIndex >= 0) { m_MainScreenPrevHoveredButtonIndex = hoveredButtonIndex; @@ -535,7 +554,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MainMenuGUI::Draw() { // Early return to avoid single frame flicker when title screen goes into transition from the meta notice screen to meta config screen. @@ -564,4 +583,4 @@ namespace RTE { } m_ActiveGUIControlManager->DrawMouse(); } -} +} // namespace RTE diff --git a/Source/Menus/MainMenuGUI.h b/Source/Menus/MainMenuGUI.h index 18e0363219..f7aa5f4d6e 100644 --- a/Source/Menus/MainMenuGUI.h +++ b/Source/Menus/MainMenuGUI.h @@ -23,7 +23,6 @@ namespace RTE { class MainMenuGUI { public: - /// /// Enumeration for the results of the MainMenuGUI input and event update. /// @@ -44,14 +43,17 @@ namespace RTE { /// /// Pointer to a GUIScreen interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! - MainMenuGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput) { Clear(); Create(guiScreen, guiInput); } + MainMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { + Clear(); + Create(guiScreen, guiInput); + } /// /// Makes the MainMenuGUI object ready for use. /// /// Pointer to a GUIScreen interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! - void Create(AllegroScreen *guiScreen, GUIInputWrapper *guiInput); + void Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput); #pragma endregion #pragma region Concrete Methods @@ -68,7 +70,6 @@ namespace RTE { #pragma endregion private: - /// /// Enumeration for the different sub-menu screens of the main menu. /// @@ -115,8 +116,8 @@ namespace RTE { std::unique_ptr m_MainMenuScreenGUIControlManager; //!< The GUIControlManager which owns all the GUIControls of the MainMenuGUI main screen. Alternative to changing skins at runtime which is expensive, since the main screen now has a unique skin. std::unique_ptr m_SubMenuScreenGUIControlManager; //!< The GUIControlManager which owns all the GUIControls of the MainMenuGUI sub-menus. - GUIControlManager *m_ActiveGUIControlManager; //!< The GUIControlManager that is currently being updated and drawn to the screen. - GUICollectionBox *m_ActiveDialogBox; // The currently active GUICollectionBox in any of the main or sub-menu screens that acts as a dialog box and requires drawing an overlay. + GUIControlManager* m_ActiveGUIControlManager; //!< The GUIControlManager that is currently being updated and drawn to the screen. + GUICollectionBox* m_ActiveDialogBox; // The currently active GUICollectionBox in any of the main or sub-menu screens that acts as a dialog box and requires drawing an overlay. MenuScreen m_ActiveMenuScreen; //!< The currently active menu screen that is being updated and drawn to the screen. See MenuScreen enumeration. MainMenuUpdateResult m_UpdateResult; //!< The result of the MainMenuGUI update. See MainMenuUpdateResult enumeration. @@ -134,17 +135,17 @@ namespace RTE { // Right now the way this works is the font graphic has different character visuals for uppercase and lowercase and the visual change happens by applying the appropriate case string when hovering/unhovering. std::array m_MainScreenButtonHoveredText; //!< Array containing uppercase strings of the main screen buttons text that are used to display the larger font when a button is hovered over. std::array m_MainScreenButtonUnhoveredText; //!< Array containing lowercase strings of the main menu screen buttons text that are used to display the smaller font when a button is not hovered over. - GUIButton *m_MainScreenHoveredButton; //!< The currently hovered main screen button. + GUIButton* m_MainScreenHoveredButton; //!< The currently hovered main screen button. int m_MainScreenPrevHoveredButtonIndex; //!< The index of the previously hovered main screen button in the main menu button array. /// /// GUI elements that compose the main menu screen. /// - GUILabel *m_VersionLabel; - GUILabel *m_CreditsTextLabel; - GUICollectionBox *m_CreditsScrollPanel; - std::array m_MainMenuScreens; - std::array m_MainMenuButtons; + GUILabel* m_VersionLabel; + GUILabel* m_CreditsTextLabel; + GUICollectionBox* m_CreditsScrollPanel; + std::array m_MainMenuScreens; + std::array m_MainMenuButtons; #pragma region Create Breakdown /// @@ -240,31 +241,31 @@ namespace RTE { /// Handles the player interaction with the main screen GUI elements. /// /// Pointer to the GUI element that the player interacted with. - void HandleMainScreenInputEvents(const GUIControl *guiEventControl); + void HandleMainScreenInputEvents(const GUIControl* guiEventControl); /// /// Handles the player interaction with the MetaGame notice screen GUI elements. /// /// Pointer to the GUI element that the player interacted with. - void HandleMetaGameNoticeScreenInputEvents(const GUIControl *guiEventControl); + void HandleMetaGameNoticeScreenInputEvents(const GUIControl* guiEventControl); /// /// Handles the player interaction with the editor selection screen GUI elements. /// /// Pointer to the GUI element that the player interacted with. - void HandleEditorsScreenInputEvents(const GUIControl *guiEventControl); + void HandleEditorsScreenInputEvents(const GUIControl* guiEventControl); /// /// Handles the player interaction with the quit screen GUI elements. /// /// Pointer to the GUI element that the player interacted with. - void HandleQuitScreenInputEvents(const GUIControl *guiEventControl); + void HandleQuitScreenInputEvents(const GUIControl* guiEventControl); /// /// Updates the currently hovered main screen button text to give the hovered visual and updates the previously hovered button to remove the hovered visual. /// /// Pointer to the currently hovered main screen button, if any. Acquired by GUIControlManager::GetControlUnderPoint. - void UpdateMainScreenHoveredButton(const GUIButton *hoveredButton); + void UpdateMainScreenHoveredButton(const GUIButton* hoveredButton); #pragma endregion /// @@ -273,8 +274,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - MainMenuGUI(const MainMenuGUI &reference) = delete; - MainMenuGUI & operator=(const MainMenuGUI &rhs) = delete; + MainMenuGUI(const MainMenuGUI& reference) = delete; + MainMenuGUI& operator=(const MainMenuGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/MetagameGUI.cpp b/Source/Menus/MetagameGUI.cpp index 8074c06f0c..a62c667d28 100644 --- a/Source/Menus/MetagameGUI.cpp +++ b/Source/Menus/MetagameGUI.cpp @@ -7,7 +7,6 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -61,92 +60,77 @@ using namespace RTE; #define STARTGOLDMIN 2000 #define STARTGOLDMAX 8000 #define BRAINPOOLMAX 20 -#define BRAINGOLDVALUE 4000//1250 +#define BRAINGOLDVALUE 4000 // 1250 #define BATTLEPAD 10 #define BRAINOVERLAP 2 - const std::string MetagameGUI::c_ClassName = "MetagameGUI"; - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws a neat animation over a site to show it changing team ownership. -void MetagameGUI::SiteTarget::Draw(BITMAP *drawBitmap) const -{ - if (!drawBitmap) - return; - - // Draw the appropriate growing geometric figure around the location, growing - if (m_Style == SiteTarget::CROSSHAIRSSHRINK) - { - float radius = LERP(0.0, 1.0, 200, 10, m_AnimProgress); - float lineLen = LERP(0.0, 1.0, 60, 10, m_AnimProgress); - float rotation = 0;//LERP(0.0, 1.0, -c_EighthPI, 0.0, m_AnimProgress); - Vector inner; - Vector outer; - - // Draw all the crosshair lines, being rotated - for (int i = 0; i < 4; ++i) - { - inner.SetXY(radius, 0); - outer.SetXY(radius + lineLen, 0); - inner.RadRotate(rotation + (c_HalfPI * i)); - outer.RadRotate(rotation + (c_HalfPI * i)); - DrawGlowLine(drawBitmap, m_CenterPos + inner, m_CenterPos + outer, m_Color); - } - } - else if (m_Style == SiteTarget::CROSSHAIRSGROW) - { - float radius = LERP(0.0, 1.0, 10, 200, m_AnimProgress); - float lineLen = LERP(0.0, 1.0, 10, 60, m_AnimProgress); - float rotation = 0;//LERP(0.0, 1.0, -c_EighthPI, 0.0, m_AnimProgress); - Vector inner; - Vector outer; - - // Draw all the crosshair lines, being rotated - for (int i = 0; i < 4; ++i) - { - inner.SetXY(radius, 0); - outer.SetXY(radius + lineLen, 0); - inner.RadRotate(rotation + (c_HalfPI * i)); - outer.RadRotate(rotation + (c_HalfPI * i)); - DrawGlowLine(drawBitmap, m_CenterPos + inner, m_CenterPos + outer, m_Color); - } - } - else if (m_Style == SiteTarget::CIRCLESHRINK) - { - float radius = LERP(0.0, 1.0, 24, 6, m_AnimProgress); - int blendAmount = LERP(0.0, 1.0, 0, 255, m_AnimProgress);// + 15 * NormalRand(); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - circle(drawBitmap, m_CenterPos.m_X, m_CenterPos.m_Y, radius, m_Color); - } - else if (m_Style == SiteTarget::CIRCLEGROW) - { - float radius = LERP(0.0, 1.0, 6, 24, m_AnimProgress); - int blendAmount = LERP(0.0, 1.0, 255, 0, m_AnimProgress);// + 15 * NormalRand(); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - circle(drawBitmap, m_CenterPos.m_X, m_CenterPos.m_Y, radius, m_Color); - } - else if (m_Style == SiteTarget::SQUARESHRINK) - { - float radius = LERP(0.0, 1.0, 24, 6, m_AnimProgress); - int blendAmount = LERP(0.0, 1.0, 0, 255, m_AnimProgress);// + 15 * NormalRand(); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - rect(drawBitmap, m_CenterPos.m_X - radius, m_CenterPos.m_Y - radius, m_CenterPos.m_X + radius, m_CenterPos.m_Y + radius, m_Color); - } - // Default - else// if (m_Style == SiteTarget::SQUAREGROW) - { - float radius = LERP(0.0, 1.0, 6, 24, m_AnimProgress); - int blendAmount = LERP(0.0, 1.0, 255, 0, m_AnimProgress);// + 15 * NormalRand(); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - rect(drawBitmap, m_CenterPos.m_X - radius, m_CenterPos.m_Y - radius, m_CenterPos.m_X + radius, m_CenterPos.m_Y + radius, m_Color); - } -} +void MetagameGUI::SiteTarget::Draw(BITMAP* drawBitmap) const { + if (!drawBitmap) + return; + // Draw the appropriate growing geometric figure around the location, growing + if (m_Style == SiteTarget::CROSSHAIRSSHRINK) { + float radius = LERP(0.0, 1.0, 200, 10, m_AnimProgress); + float lineLen = LERP(0.0, 1.0, 60, 10, m_AnimProgress); + float rotation = 0; // LERP(0.0, 1.0, -c_EighthPI, 0.0, m_AnimProgress); + Vector inner; + Vector outer; + + // Draw all the crosshair lines, being rotated + for (int i = 0; i < 4; ++i) { + inner.SetXY(radius, 0); + outer.SetXY(radius + lineLen, 0); + inner.RadRotate(rotation + (c_HalfPI * i)); + outer.RadRotate(rotation + (c_HalfPI * i)); + DrawGlowLine(drawBitmap, m_CenterPos + inner, m_CenterPos + outer, m_Color); + } + } else if (m_Style == SiteTarget::CROSSHAIRSGROW) { + float radius = LERP(0.0, 1.0, 10, 200, m_AnimProgress); + float lineLen = LERP(0.0, 1.0, 10, 60, m_AnimProgress); + float rotation = 0; // LERP(0.0, 1.0, -c_EighthPI, 0.0, m_AnimProgress); + Vector inner; + Vector outer; + + // Draw all the crosshair lines, being rotated + for (int i = 0; i < 4; ++i) { + inner.SetXY(radius, 0); + outer.SetXY(radius + lineLen, 0); + inner.RadRotate(rotation + (c_HalfPI * i)); + outer.RadRotate(rotation + (c_HalfPI * i)); + DrawGlowLine(drawBitmap, m_CenterPos + inner, m_CenterPos + outer, m_Color); + } + } else if (m_Style == SiteTarget::CIRCLESHRINK) { + float radius = LERP(0.0, 1.0, 24, 6, m_AnimProgress); + int blendAmount = LERP(0.0, 1.0, 0, 255, m_AnimProgress); // + 15 * NormalRand(); + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + circle(drawBitmap, m_CenterPos.m_X, m_CenterPos.m_Y, radius, m_Color); + } else if (m_Style == SiteTarget::CIRCLEGROW) { + float radius = LERP(0.0, 1.0, 6, 24, m_AnimProgress); + int blendAmount = LERP(0.0, 1.0, 255, 0, m_AnimProgress); // + 15 * NormalRand(); + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + circle(drawBitmap, m_CenterPos.m_X, m_CenterPos.m_Y, radius, m_Color); + } else if (m_Style == SiteTarget::SQUARESHRINK) { + float radius = LERP(0.0, 1.0, 24, 6, m_AnimProgress); + int blendAmount = LERP(0.0, 1.0, 0, 255, m_AnimProgress); // + 15 * NormalRand(); + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + rect(drawBitmap, m_CenterPos.m_X - radius, m_CenterPos.m_Y - radius, m_CenterPos.m_X + radius, m_CenterPos.m_Y + radius, m_Color); + } + // Default + else // if (m_Style == SiteTarget::SQUAREGROW) + { + float radius = LERP(0.0, 1.0, 6, 24, m_AnimProgress); + int blendAmount = LERP(0.0, 1.0, 255, 0, m_AnimProgress); // + 15 * NormalRand(); + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + rect(drawBitmap, m_CenterPos.m_X - radius, m_CenterPos.m_Y - radius, m_CenterPos.m_X + radius, m_CenterPos.m_Y + radius, m_Color); + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear @@ -154,182 +138,178 @@ void MetagameGUI::SiteTarget::Draw(BITMAP *drawBitmap) const // Description: Clears all the member variables of this MetagameGUI, effectively // resetting the members of this abstraction level only. -void MetagameGUI::Clear() -{ +void MetagameGUI::Clear() { m_RootBoxMaxWidth = 0; - m_pController = 0; - m_pGUIScreen = 0; - m_pGUIInput = 0; - m_pGUIController = 0; - m_MenuEnabled = ENABLED; - m_MenuScreen = NEWDIALOG; - m_ScreenChange = true; - m_SceneFocus = 0; - m_FocusChange = 0; - m_MenuSpeed = 0.3; - m_BlinkTimer.Reset(); - m_BlinkMode = NOBLINK; - - m_pBannerRedTop = 0; - m_pBannerRedBottom = 0; - m_pBannerYellowTop = 0; - m_pBannerYellowBottom = 0; - - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - m_AnimTimer3.Reset(); - - m_AnimMode = 0; - m_AnimModeChange = false; - m_AnimModeDuration = 2000; - m_AnimMetaPlayer = Players::NoPlayer; - m_AnimDefenseTeam = Activity::NoTeam; - m_AnimActivityChange = false; - Scene *m_pAnimScene = 0; - m_AnimRatio = 0; - m_AnimProgress = 0; - m_AnimTotalFunds = 0; - m_AnimFundsMax = 0; - m_AnimFundsMin = 0; - m_AnimBuildCount = 0; - m_AnimIncomeLine = 0; - m_AnimIncomeLineChange = false; - m_AnimActionLine = 0; - m_AnimActionLineChange = false; - m_AnimSegment = 0; - m_AnimCountStart = 0; - m_AnimCountCurrent = 0; - m_AnimCountEnd = 0; - m_LineConnected = false; - - m_IncomeSiteLines.clear(); - m_ActivePlayerIncomeLines = -1; - m_ActionMeterDrawOverride = false; - m_NewSiteIndicators.clear(); - m_SiteSwitchIndicators.clear(); - - m_PlanetCenter.Reset(); - m_PlanetRadius = 240.0f; - - m_pGameMessageLabel = 0; - - m_pToolTipBox = 0; - m_pToolTipText = 0; - m_ToolTipTimer.Reset(); - m_pHoveredControl = 0; - - for (int iscreen = 0; iscreen < SCREENCOUNT; ++iscreen) - m_apScreenBox[iscreen] = 0; - for (int button = 0; button < METABUTTONCOUNT; ++button) - m_apMetaButton[button] = 0; - - for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) - { - m_apPlayerBox[metaPlayer] = 0; - m_apPlayerTeamBox[metaPlayer] = 0; - m_apPlayerTeamActionBox[metaPlayer] = 0; - m_apPlayerBarLabel[metaPlayer] = 0; - m_apPlayerBrainTravelLabel[metaPlayer] = 0; - m_aBattleFunds[metaPlayer] = 0; - m_aBattleAttacker[metaPlayer] = false; - m_aAnimDestroyed[metaPlayer] = false; - m_aBrainIconPos[metaPlayer].Reset(); - m_aQuadTakenBy[metaPlayer] = Players::NoPlayer; - m_apBrainPoolLabel[metaPlayer] = 0; - m_apFundsChangeLabel[metaPlayer] = 0; - m_apBrainChangeLabel[metaPlayer] = 0; - m_apFundsChangeTimer[metaPlayer].Reset(); - m_apBrainsChangeTimer[metaPlayer].Reset(); - - m_apPlayerControlButton[metaPlayer] = 0; - m_apPlayerTeamSelect[metaPlayer] = 0; - m_apPlayerTechSelect[metaPlayer] = 0; - m_apPlayerHandicap[metaPlayer] = 0; - m_apPlayerNameBox[metaPlayer] = 0; + m_pController = 0; + m_pGUIScreen = 0; + m_pGUIInput = 0; + m_pGUIController = 0; + m_MenuEnabled = ENABLED; + m_MenuScreen = NEWDIALOG; + m_ScreenChange = true; + m_SceneFocus = 0; + m_FocusChange = 0; + m_MenuSpeed = 0.3; + m_BlinkTimer.Reset(); + m_BlinkMode = NOBLINK; + + m_pBannerRedTop = 0; + m_pBannerRedBottom = 0; + m_pBannerYellowTop = 0; + m_pBannerYellowBottom = 0; + + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + m_AnimTimer3.Reset(); + + m_AnimMode = 0; + m_AnimModeChange = false; + m_AnimModeDuration = 2000; + m_AnimMetaPlayer = Players::NoPlayer; + m_AnimDefenseTeam = Activity::NoTeam; + m_AnimActivityChange = false; + Scene* m_pAnimScene = 0; + m_AnimRatio = 0; + m_AnimProgress = 0; + m_AnimTotalFunds = 0; + m_AnimFundsMax = 0; + m_AnimFundsMin = 0; + m_AnimBuildCount = 0; + m_AnimIncomeLine = 0; + m_AnimIncomeLineChange = false; + m_AnimActionLine = 0; + m_AnimActionLineChange = false; + m_AnimSegment = 0; + m_AnimCountStart = 0; + m_AnimCountCurrent = 0; + m_AnimCountEnd = 0; + m_LineConnected = false; + + m_IncomeSiteLines.clear(); + m_ActivePlayerIncomeLines = -1; + m_ActionMeterDrawOverride = false; + m_NewSiteIndicators.clear(); + m_SiteSwitchIndicators.clear(); + + m_PlanetCenter.Reset(); + m_PlanetRadius = 240.0f; + + m_pGameMessageLabel = 0; + + m_pToolTipBox = 0; + m_pToolTipText = 0; + m_ToolTipTimer.Reset(); + m_pHoveredControl = 0; + + for (int iscreen = 0; iscreen < SCREENCOUNT; ++iscreen) + m_apScreenBox[iscreen] = 0; + for (int button = 0; button < METABUTTONCOUNT; ++button) + m_apMetaButton[button] = 0; + + for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) { + m_apPlayerBox[metaPlayer] = 0; + m_apPlayerTeamBox[metaPlayer] = 0; + m_apPlayerTeamActionBox[metaPlayer] = 0; + m_apPlayerBarLabel[metaPlayer] = 0; + m_apPlayerBrainTravelLabel[metaPlayer] = 0; + m_aBattleFunds[metaPlayer] = 0; + m_aBattleAttacker[metaPlayer] = false; + m_aAnimDestroyed[metaPlayer] = false; + m_aBrainIconPos[metaPlayer].Reset(); + m_aQuadTakenBy[metaPlayer] = Players::NoPlayer; + m_apBrainPoolLabel[metaPlayer] = 0; + m_apFundsChangeLabel[metaPlayer] = 0; + m_apBrainChangeLabel[metaPlayer] = 0; + m_apFundsChangeTimer[metaPlayer].Reset(); + m_apBrainsChangeTimer[metaPlayer].Reset(); + + m_apPlayerControlButton[metaPlayer] = 0; + m_apPlayerTeamSelect[metaPlayer] = 0; + m_apPlayerTechSelect[metaPlayer] = 0; + m_apPlayerHandicap[metaPlayer] = 0; + m_apPlayerNameBox[metaPlayer] = 0; m_apPlayerAISkillSlider[metaPlayer] = 0; m_apPlayerAISkillLabel[metaPlayer] = 0; - m_aStationIncomeLineIndices[metaPlayer] = -1; - m_aBrainSaleIncomeLineIndices[metaPlayer] = -1; - m_ActionSiteLines[metaPlayer].clear(); - } - - m_PrevMousePos.Reset(); - - m_pConfirmationBox = 0; - m_pConfirmationLabel = 0; - m_pConfirmationButton = 0; - - m_pPhaseBox = 0; - m_pPhaseLabel = 0; - m_PreTurn = false; - m_BattleToResume = false; - m_PostBattleReview = false; - m_BattleCausedOwnershipChange = false; - m_PreBattleTeamOwnership = Activity::NoTeam; - - m_pScenePlanetLabel = 0; - m_pSceneInfoPopup = 0; - m_pSceneCloseButton = 0; - m_pSceneNameLabel = 0; - m_pSceneOwnerTeam = 0; - m_pSceneResidentsLabel = 0; - m_pSceneInfoLabel = 0; - m_pSceneBudgetLabel = 0; - m_pSceneBudgetSlider = 0; - m_pSceneBudgetBar = 0; - m_pAutoDesignCheckbox = 0; - m_pScanInfoLabel = 0; - - m_pDraggedBox = 0; - m_EngageDrag = false; - m_pHoveredScene = 0; - m_pSelectedScene = 0; - m_pPlayingScene = 0; - - m_pSizeLabel = 0; - m_pSizeSlider = 0; - m_pDifficultyLabel = 0; - m_pDifficultySlider = 0; - m_pGoldLabel = 0; - m_pGoldSlider = 0; - m_pLengthLabel = 0; - m_pLengthSlider = 0; - m_pErrorLabel = 0; - - m_NewSaveBox = 0; - m_pSavesToOverwriteCombo = 0; - m_pSavesToLoadCombo = 0; - m_pSaveInfoLabel = 0; - m_pLoadInfoLabel = 0; - m_pSelectedGameToLoad = 0; - - m_ContinuePhase = false; - m_ActivityRestarted = false; - m_ActivityResumed = false; - m_StartFunds = 1600; - m_CPUPlayer = -1; - m_StartDifficulty = Activity::MediumDifficulty; - m_BackToMain = false; - m_Quit = false; + m_aStationIncomeLineIndices[metaPlayer] = -1; + m_aBrainSaleIncomeLineIndices[metaPlayer] = -1; + m_ActionSiteLines[metaPlayer].clear(); + } + + m_PrevMousePos.Reset(); + + m_pConfirmationBox = 0; + m_pConfirmationLabel = 0; + m_pConfirmationButton = 0; + + m_pPhaseBox = 0; + m_pPhaseLabel = 0; + m_PreTurn = false; + m_BattleToResume = false; + m_PostBattleReview = false; + m_BattleCausedOwnershipChange = false; + m_PreBattleTeamOwnership = Activity::NoTeam; + + m_pScenePlanetLabel = 0; + m_pSceneInfoPopup = 0; + m_pSceneCloseButton = 0; + m_pSceneNameLabel = 0; + m_pSceneOwnerTeam = 0; + m_pSceneResidentsLabel = 0; + m_pSceneInfoLabel = 0; + m_pSceneBudgetLabel = 0; + m_pSceneBudgetSlider = 0; + m_pSceneBudgetBar = 0; + m_pAutoDesignCheckbox = 0; + m_pScanInfoLabel = 0; + + m_pDraggedBox = 0; + m_EngageDrag = false; + m_pHoveredScene = 0; + m_pSelectedScene = 0; + m_pPlayingScene = 0; + + m_pSizeLabel = 0; + m_pSizeSlider = 0; + m_pDifficultyLabel = 0; + m_pDifficultySlider = 0; + m_pGoldLabel = 0; + m_pGoldSlider = 0; + m_pLengthLabel = 0; + m_pLengthSlider = 0; + m_pErrorLabel = 0; + + m_NewSaveBox = 0; + m_pSavesToOverwriteCombo = 0; + m_pSavesToLoadCombo = 0; + m_pSaveInfoLabel = 0; + m_pLoadInfoLabel = 0; + m_pSelectedGameToLoad = 0; + + m_ContinuePhase = false; + m_ActivityRestarted = false; + m_ActivityResumed = false; + m_StartFunds = 1600; + m_CPUPlayer = -1; + m_StartDifficulty = Activity::MediumDifficulty; + m_BackToMain = false; + m_Quit = false; m_StationPosOnOrbit.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetToStartNewGame ////////////////////////////////////////////////////////////////////////////////////////// // Description: Resets internal state of GUI to show 'Start new campaign' screen // Arguments: None. // Return value: None. -void MetagameGUI::SetToStartNewGame() -{ +void MetagameGUI::SetToStartNewGame() { g_MetaMan.SetSuspend(true); HideAllScreens(); UpdatePlayerSetup(); - //UpdatePlayerBars(); + // UpdatePlayerBars(); SwitchToScreen(MetagameGUI::NEWDIALOG); } @@ -338,278 +318,271 @@ void MetagameGUI::SetToStartNewGame() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the MetagameGUI object ready for use. -int MetagameGUI::Create() -{ - for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) - { - m_ActionSiteLines[metaPlayer].clear(); - // The relevant onces will be enabled again in update - m_apPlayerBox[metaPlayer]->SetVisible(false); - // Clear out the team flag Icons; they will be re-set on next update - m_apPlayerTeamBox[metaPlayer]->SetDrawImage(0); - m_apPlayerTeamActionBox[metaPlayer]->SetDrawImage(0); - m_apBrainPoolLabel[metaPlayer]->SetVisible(false); - } - - return 0; -} +int MetagameGUI::Create() { + for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) { + m_ActionSiteLines[metaPlayer].clear(); + // The relevant onces will be enabled again in update + m_apPlayerBox[metaPlayer]->SetVisible(false); + // Clear out the team flag Icons; they will be re-set on next update + m_apPlayerTeamBox[metaPlayer]->SetDrawImage(0); + m_apPlayerTeamActionBox[metaPlayer]->SetDrawImage(0); + m_apBrainPoolLabel[metaPlayer]->SetVisible(false); + } + return 0; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the MetagameGUI object ready for use. -int MetagameGUI::Create(Controller *pController) -{ - RTEAssert(pController, "No controller sent to MetagameGUI on creation!"); - m_pController = pController; +int MetagameGUI::Create(Controller* pController) { + RTEAssert(pController, "No controller sent to MetagameGUI on creation!"); + m_pController = pController; - char str[256]; + char str[256]; - if (!m_pGUIScreen) - m_pGUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer32()); - if (!m_pGUIInput) - m_pGUIInput = new GUIInputWrapper(-1, true); - if (!m_pGUIController) - m_pGUIController = new GUIControlManager(); + if (!m_pGUIScreen) + m_pGUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer32()); + if (!m_pGUIInput) + m_pGUIInput = new GUIInputWrapper(-1, true); + if (!m_pGUIController) + m_pGUIController = new GUIControlManager(); if (!m_pGUIController->Create(m_pGUIScreen, m_pGUIInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini")) { RTEAbort("Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); } - m_pGUIController->Load("Base.rte/GUIs/MetagameGUI.ini"); + m_pGUIController->Load("Base.rte/GUIs/MetagameGUI.ini"); m_RootBoxMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); - // Make sure we have convenient points to the containing GUI colleciton boxes that we will manipulate the positions of - GUICollectionBox *pRootBox = m_apScreenBox[ROOTBOX] = dynamic_cast(m_pGUIController->GetControl("root")); - // Make the root box fill the screen -// pRootBox->SetPositionAbs((g_WindowMan.GetResX() - pRootBox->GetWidth()) / 2, 0);// (g_WindowMan.GetResY() - pRootBox->GetHeight()) / 2); - pRootBox->SetDrawBackground(false); - pRootBox->Resize(m_RootBoxMaxWidth, g_WindowMan.GetResY()); - - m_pBannerRedTop = new GUIBanner(); - m_pBannerRedBottom = new GUIBanner(); - m_pBannerYellowTop = new GUIBanner(); - m_pBannerYellowBottom = new GUIBanner(); - m_pBannerRedTop->Create("Base.rte/GUIs/Fonts/BannerFontRedReg.png", "Base.rte/GUIs/Fonts/BannerFontRedBlur.png", 32); - m_pBannerRedBottom->Create("Base.rte/GUIs/Fonts/BannerFontRedReg.png", "Base.rte/GUIs/Fonts/BannerFontRedBlur.png", 32); - m_pBannerYellowTop->Create("Base.rte/GUIs/Fonts/BannerFontYellowReg.png", "Base.rte/GUIs/Fonts/BannerFontYellowBlur.png", 32); - m_pBannerYellowBottom->Create("Base.rte/GUIs/Fonts/BannerFontYellowReg.png", "Base.rte/GUIs/Fonts/BannerFontYellowBlur.png", 32); - - // General game message label - m_pGameMessageLabel = dynamic_cast(m_pGUIController->GetControl("GameMessageLabel")); - m_pGameMessageLabel->CenterInParent(true, false); - m_pGameMessageLabel->SetPositionAbs(m_pGameMessageLabel->GetXPos(), 0); - m_pGameMessageLabel->SetVisible(false); - - // ToolTip box - m_pToolTipBox = dynamic_cast(m_pGUIController->GetControl("ToolTipBox")); - m_pToolTipText = dynamic_cast(m_pGUIController->GetControl("ToolTipLabel")); - m_pToolTipBox->SetDrawType(GUICollectionBox::Panel); - m_pToolTipBox->SetDrawBackground(true); - // Never enable the popup, because it steals focus and cuases other windows to think teh cursor left them - m_pToolTipBox->SetEnabled(false); - m_pToolTipBox->SetVisible(false); - // Set the font - m_pToolTipText->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); - - // Make sure we have convenient points to the containing GUI colleciton boxes that we will manipulate the positions of - m_apScreenBox[NEWDIALOG] = dynamic_cast(m_pGUIController->GetControl("NewGameDialog")); - m_apScreenBox[LOADDIALOG] = dynamic_cast(m_pGUIController->GetControl("LoadDialog")); - m_apScreenBox[SAVEDIALOG] = dynamic_cast(m_pGUIController->GetControl("SaveDialog")); - m_apScreenBox[MENUDIALOG] = dynamic_cast(m_pGUIController->GetControl("GameMenu")); - m_apScreenBox[STATSDIALOG] = dynamic_cast(m_pGUIController->GetControl("GameStatsBox")); - m_apScreenBox[SCENEINFOBOX] = dynamic_cast(m_pGUIController->GetControl("SceneInfoBox")); - - // Center most of the main boxes in the root box - m_apScreenBox[NEWDIALOG]->CenterInParent(true, true); - m_apScreenBox[LOADDIALOG]->CenterInParent(true, true); - m_apScreenBox[SAVEDIALOG]->CenterInParent(true, true); - m_apScreenBox[MENUDIALOG]->CenterInParent(true, true); - m_apScreenBox[STATSDIALOG]->CenterInParent(true, true); - - // Buttons which will be altered - m_apMetaButton[CONFIRM] = dynamic_cast(m_pGUIController->GetControl("ConfirmButton")); - m_apMetaButton[P1CONTROL] = dynamic_cast(m_pGUIController->GetControl("P1ControlButton")); - m_apMetaButton[P2CONTROL] = dynamic_cast(m_pGUIController->GetControl("P2ControlButton")); - m_apMetaButton[P3CONTROL] = dynamic_cast(m_pGUIController->GetControl("P3ControlButton")); - m_apMetaButton[P4CONTROL] = dynamic_cast(m_pGUIController->GetControl("P4ControlButton")); - m_apMetaButton[STARTNEW] = dynamic_cast(m_pGUIController->GetControl("StartButton")); - m_apMetaButton[LOADNOW] = dynamic_cast(m_pGUIController->GetControl("LoadButton")); - m_apMetaButton[SAVENOW] = dynamic_cast(m_pGUIController->GetControl("SaveButton")); - m_apMetaButton[CONTINUE] = dynamic_cast(m_pGUIController->GetControl("ContinueButton")); - m_apMetaButton[SCENEACTION] = dynamic_cast(m_pGUIController->GetControl("SceneActionButton")); - m_apMetaButton[DESIGNBASE] = dynamic_cast(m_pGUIController->GetControl("DesignBaseButton")); - m_apMetaButton[SCANNOW] = dynamic_cast(m_pGUIController->GetControl("ScanNowButton")); - m_apMetaButton[SCANLATER] = dynamic_cast(m_pGUIController->GetControl("ScanLaterButton")); - - // Confirmation box - m_pConfirmationBox = dynamic_cast(m_pGUIController->GetControl("ConfirmDialog")); - m_pConfirmationLabel = dynamic_cast(m_pGUIController->GetControl("ConfirmLabel")); - m_pConfirmationButton = dynamic_cast(m_pGUIController->GetControl("ConfirmButton")); - m_pConfirmationBox->CenterInParent(true, true); - m_pConfirmationBox->SetVisible(false); - - // Floating player bars - m_apPlayerBox[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1Bar")); - m_apPlayerBox[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2Bar")); - m_apPlayerBox[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3Bar")); - m_apPlayerBox[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4Bar")); - m_apPlayerTeamBox[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1Team")); - m_apPlayerTeamBox[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2Team")); - m_apPlayerTeamBox[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3Team")); - m_apPlayerTeamBox[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4Team")); - m_apPlayerBarLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1FundsLabel")); - m_apPlayerBarLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2FundsLabel")); - m_apPlayerBarLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3FundsLabel")); - m_apPlayerBarLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4FundsLabel")); - m_apBrainPoolLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1BrainLabel")); - m_apBrainPoolLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2BrainLabel")); - m_apBrainPoolLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3BrainLabel")); - m_apBrainPoolLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4BrainLabel")); - m_apFundsChangeLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1FundsChangeLabel")); - m_apFundsChangeLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2FundsChangeLabel")); - m_apFundsChangeLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3FundsChangeLabel")); - m_apFundsChangeLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4FundsChangeLabel")); - m_apBrainChangeLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1BrainChangeLabel")); - m_apBrainChangeLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2BrainChangeLabel")); - m_apBrainChangeLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3BrainChangeLabel")); - m_apBrainChangeLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4BrainChangeLabel")); - - // Battle site display - m_apPlayerTeamActionBox[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1TeamAction")); - m_apPlayerTeamActionBox[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2TeamAction")); - m_apPlayerTeamActionBox[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3TeamAction")); - m_apPlayerTeamActionBox[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4TeamAction")); - m_apPlayerBrainTravelLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1BrainTravelLabel")); - m_apPlayerBrainTravelLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2BrainTravelLabel")); - m_apPlayerBrainTravelLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3BrainTravelLabel")); - m_apPlayerBrainTravelLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4BrainTravelLabel")); - - for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) - { - m_apBrainPoolLabel[metaPlayer]->SetVisible(false); - m_apBrainPoolLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); - // Tuck these away so they don't interfere with dragging etc - m_apFundsChangeLabel[metaPlayer]->SetVisible(false); - m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); - m_apBrainChangeLabel[metaPlayer]->SetVisible(false); - m_apBrainChangeLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); - - // Hide the battle display initially - m_apPlayerTeamActionBox[metaPlayer]->SetVisible(false); - m_apPlayerTeamActionBox[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); - m_apPlayerBrainTravelLabel[metaPlayer]->SetVisible(false); - m_apPlayerBrainTravelLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); - } - - // Phase info box - m_pPhaseBox = dynamic_cast(m_pGUIController->GetControl("PhaseBox")); - m_pPhaseLabel = dynamic_cast(m_pGUIController->GetControl("PhaseLabel")); - // Just get the menu button temporarily so we can set the custom menu icon - if (GUIButton *pMenuButton = dynamic_cast(m_pGUIController->GetControl("OpenMenuButton"))) - { - std::snprintf(str, sizeof(str), "%c", -22); - pMenuButton->SetText(std::string(str)); - } - - // Planet mouseover scene label - m_pScenePlanetLabel = dynamic_cast(m_pGUIController->GetControl("ScenePlanetLabel")); - m_pScenePlanetLabel->SetVisible(false); - - // Scene Info Box - m_pSceneInfoPopup = dynamic_cast(m_pGUIController->GetControl("SceneInfoBox")); - m_pSceneCloseButton = dynamic_cast(m_pGUIController->GetControl("SceneCloseButton")); - m_pSceneNameLabel = dynamic_cast(m_pGUIController->GetControl("SceneNameLabel")); - m_pSceneOwnerTeam = dynamic_cast(m_pGUIController->GetControl("SceneOwnerTeam")); - m_pSceneResidentsLabel = dynamic_cast(m_pGUIController->GetControl("SceneResidentsLabel")); - m_pSceneInfoLabel = dynamic_cast(m_pGUIController->GetControl("SceneInfoLabel")); - m_pSceneInfoLabel->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); - m_pSceneInfoPopup->SetVisible(false); - m_pSceneBudgetLabel = dynamic_cast(m_pGUIController->GetControl("SceneBudgetLabel")); - m_pSceneBudgetSlider = dynamic_cast(m_pGUIController->GetControl("SceneBudgetSlider")); - m_pSceneBudgetBar = dynamic_cast(m_pGUIController->GetControl("SceneBudgetBar")); - m_pSceneBudgetBar->SetDrawColor(makecol(55, 5, 10)); - m_pAutoDesignCheckbox = dynamic_cast(m_pGUIController->GetControl("AutoDesignCheckbox")); - m_pScanInfoLabel = dynamic_cast(m_pGUIController->GetControl("ScanInfoLabel")); - - // Set initial focus, category list, and label settings - m_ScreenChange = true; - m_FocusChange = 1; -// CategoryChange(); - - // New Game Dialog - m_pSizeLabel = dynamic_cast(m_pGUIController->GetControl("SizeLabel")); - m_pSizeSlider = dynamic_cast(m_pGUIController->GetControl("SizeSlider")); - m_pDifficultyLabel = dynamic_cast(m_pGUIController->GetControl("DifficultyLabel")); - m_pDifficultySlider = dynamic_cast(m_pGUIController->GetControl("DifficultySlider")); - m_pGoldLabel = dynamic_cast(m_pGUIController->GetControl("GoldLabel")); - m_pGoldSlider = dynamic_cast(m_pGUIController->GetControl("GoldSlider")); - m_pLengthLabel = dynamic_cast(m_pGUIController->GetControl("LengthLabel")); - m_pLengthSlider = dynamic_cast(m_pGUIController->GetControl("LengthSlider")); - m_pErrorLabel = dynamic_cast(m_pGUIController->GetControl("StartErrorLabel")); - m_apPlayerControlButton[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1ControlButton")); - m_apPlayerControlButton[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2ControlButton")); - m_apPlayerControlButton[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3ControlButton")); - m_apPlayerControlButton[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4ControlButton")); - m_apPlayerTeamSelect[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1TeamCombo")); - m_apPlayerTeamSelect[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2TeamCombo")); - m_apPlayerTeamSelect[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3TeamCombo")); - m_apPlayerTeamSelect[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4TeamCombo")); - m_apPlayerTechSelect[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1TechCombo")); - m_apPlayerTechSelect[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2TechCombo")); - m_apPlayerTechSelect[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3TechCombo")); - m_apPlayerTechSelect[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4TechCombo")); - m_apPlayerHandicap[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1HCCombo")); - m_apPlayerHandicap[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2HCCombo")); - m_apPlayerHandicap[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3HCCombo")); - m_apPlayerHandicap[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4HCCombo")); - m_apPlayerNameBox[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1NameText")); - m_apPlayerNameBox[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2NameText")); - m_apPlayerNameBox[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3NameText")); - m_apPlayerNameBox[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4NameText")); - m_apPlayerAISkillSlider[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1AISkillSlider")); - m_apPlayerAISkillSlider[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2AISkillSlider")); - m_apPlayerAISkillSlider[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3AISkillSlider")); - m_apPlayerAISkillSlider[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4AISkillSlider")); - m_apPlayerAISkillLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1AISkillLabel")); - m_apPlayerAISkillLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2AISkillLabel")); - m_apPlayerAISkillLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3AISkillLabel")); - m_apPlayerAISkillLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4AISkillLabel")); + // Make sure we have convenient points to the containing GUI colleciton boxes that we will manipulate the positions of + GUICollectionBox* pRootBox = m_apScreenBox[ROOTBOX] = dynamic_cast(m_pGUIController->GetControl("root")); + // Make the root box fill the screen + // pRootBox->SetPositionAbs((g_WindowMan.GetResX() - pRootBox->GetWidth()) / 2, 0);// (g_WindowMan.GetResY() - pRootBox->GetHeight()) / 2); + pRootBox->SetDrawBackground(false); + pRootBox->Resize(m_RootBoxMaxWidth, g_WindowMan.GetResY()); + + m_pBannerRedTop = new GUIBanner(); + m_pBannerRedBottom = new GUIBanner(); + m_pBannerYellowTop = new GUIBanner(); + m_pBannerYellowBottom = new GUIBanner(); + m_pBannerRedTop->Create("Base.rte/GUIs/Fonts/BannerFontRedReg.png", "Base.rte/GUIs/Fonts/BannerFontRedBlur.png", 32); + m_pBannerRedBottom->Create("Base.rte/GUIs/Fonts/BannerFontRedReg.png", "Base.rte/GUIs/Fonts/BannerFontRedBlur.png", 32); + m_pBannerYellowTop->Create("Base.rte/GUIs/Fonts/BannerFontYellowReg.png", "Base.rte/GUIs/Fonts/BannerFontYellowBlur.png", 32); + m_pBannerYellowBottom->Create("Base.rte/GUIs/Fonts/BannerFontYellowReg.png", "Base.rte/GUIs/Fonts/BannerFontYellowBlur.png", 32); + + // General game message label + m_pGameMessageLabel = dynamic_cast(m_pGUIController->GetControl("GameMessageLabel")); + m_pGameMessageLabel->CenterInParent(true, false); + m_pGameMessageLabel->SetPositionAbs(m_pGameMessageLabel->GetXPos(), 0); + m_pGameMessageLabel->SetVisible(false); + + // ToolTip box + m_pToolTipBox = dynamic_cast(m_pGUIController->GetControl("ToolTipBox")); + m_pToolTipText = dynamic_cast(m_pGUIController->GetControl("ToolTipLabel")); + m_pToolTipBox->SetDrawType(GUICollectionBox::Panel); + m_pToolTipBox->SetDrawBackground(true); + // Never enable the popup, because it steals focus and cuases other windows to think teh cursor left them + m_pToolTipBox->SetEnabled(false); + m_pToolTipBox->SetVisible(false); + // Set the font + m_pToolTipText->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); + + // Make sure we have convenient points to the containing GUI colleciton boxes that we will manipulate the positions of + m_apScreenBox[NEWDIALOG] = dynamic_cast(m_pGUIController->GetControl("NewGameDialog")); + m_apScreenBox[LOADDIALOG] = dynamic_cast(m_pGUIController->GetControl("LoadDialog")); + m_apScreenBox[SAVEDIALOG] = dynamic_cast(m_pGUIController->GetControl("SaveDialog")); + m_apScreenBox[MENUDIALOG] = dynamic_cast(m_pGUIController->GetControl("GameMenu")); + m_apScreenBox[STATSDIALOG] = dynamic_cast(m_pGUIController->GetControl("GameStatsBox")); + m_apScreenBox[SCENEINFOBOX] = dynamic_cast(m_pGUIController->GetControl("SceneInfoBox")); + + // Center most of the main boxes in the root box + m_apScreenBox[NEWDIALOG]->CenterInParent(true, true); + m_apScreenBox[LOADDIALOG]->CenterInParent(true, true); + m_apScreenBox[SAVEDIALOG]->CenterInParent(true, true); + m_apScreenBox[MENUDIALOG]->CenterInParent(true, true); + m_apScreenBox[STATSDIALOG]->CenterInParent(true, true); + + // Buttons which will be altered + m_apMetaButton[CONFIRM] = dynamic_cast(m_pGUIController->GetControl("ConfirmButton")); + m_apMetaButton[P1CONTROL] = dynamic_cast(m_pGUIController->GetControl("P1ControlButton")); + m_apMetaButton[P2CONTROL] = dynamic_cast(m_pGUIController->GetControl("P2ControlButton")); + m_apMetaButton[P3CONTROL] = dynamic_cast(m_pGUIController->GetControl("P3ControlButton")); + m_apMetaButton[P4CONTROL] = dynamic_cast(m_pGUIController->GetControl("P4ControlButton")); + m_apMetaButton[STARTNEW] = dynamic_cast(m_pGUIController->GetControl("StartButton")); + m_apMetaButton[LOADNOW] = dynamic_cast(m_pGUIController->GetControl("LoadButton")); + m_apMetaButton[SAVENOW] = dynamic_cast(m_pGUIController->GetControl("SaveButton")); + m_apMetaButton[CONTINUE] = dynamic_cast(m_pGUIController->GetControl("ContinueButton")); + m_apMetaButton[SCENEACTION] = dynamic_cast(m_pGUIController->GetControl("SceneActionButton")); + m_apMetaButton[DESIGNBASE] = dynamic_cast(m_pGUIController->GetControl("DesignBaseButton")); + m_apMetaButton[SCANNOW] = dynamic_cast(m_pGUIController->GetControl("ScanNowButton")); + m_apMetaButton[SCANLATER] = dynamic_cast(m_pGUIController->GetControl("ScanLaterButton")); + + // Confirmation box + m_pConfirmationBox = dynamic_cast(m_pGUIController->GetControl("ConfirmDialog")); + m_pConfirmationLabel = dynamic_cast(m_pGUIController->GetControl("ConfirmLabel")); + m_pConfirmationButton = dynamic_cast(m_pGUIController->GetControl("ConfirmButton")); + m_pConfirmationBox->CenterInParent(true, true); + m_pConfirmationBox->SetVisible(false); + + // Floating player bars + m_apPlayerBox[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1Bar")); + m_apPlayerBox[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2Bar")); + m_apPlayerBox[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3Bar")); + m_apPlayerBox[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4Bar")); + m_apPlayerTeamBox[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1Team")); + m_apPlayerTeamBox[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2Team")); + m_apPlayerTeamBox[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3Team")); + m_apPlayerTeamBox[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4Team")); + m_apPlayerBarLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1FundsLabel")); + m_apPlayerBarLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2FundsLabel")); + m_apPlayerBarLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3FundsLabel")); + m_apPlayerBarLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4FundsLabel")); + m_apBrainPoolLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1BrainLabel")); + m_apBrainPoolLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2BrainLabel")); + m_apBrainPoolLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3BrainLabel")); + m_apBrainPoolLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4BrainLabel")); + m_apFundsChangeLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1FundsChangeLabel")); + m_apFundsChangeLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2FundsChangeLabel")); + m_apFundsChangeLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3FundsChangeLabel")); + m_apFundsChangeLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4FundsChangeLabel")); + m_apBrainChangeLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1BrainChangeLabel")); + m_apBrainChangeLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2BrainChangeLabel")); + m_apBrainChangeLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3BrainChangeLabel")); + m_apBrainChangeLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4BrainChangeLabel")); + + // Battle site display + m_apPlayerTeamActionBox[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1TeamAction")); + m_apPlayerTeamActionBox[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2TeamAction")); + m_apPlayerTeamActionBox[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3TeamAction")); + m_apPlayerTeamActionBox[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4TeamAction")); + m_apPlayerBrainTravelLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1BrainTravelLabel")); + m_apPlayerBrainTravelLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2BrainTravelLabel")); + m_apPlayerBrainTravelLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3BrainTravelLabel")); + m_apPlayerBrainTravelLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4BrainTravelLabel")); + + for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) { + m_apBrainPoolLabel[metaPlayer]->SetVisible(false); + m_apBrainPoolLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); + // Tuck these away so they don't interfere with dragging etc + m_apFundsChangeLabel[metaPlayer]->SetVisible(false); + m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); + m_apBrainChangeLabel[metaPlayer]->SetVisible(false); + m_apBrainChangeLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); + + // Hide the battle display initially + m_apPlayerTeamActionBox[metaPlayer]->SetVisible(false); + m_apPlayerTeamActionBox[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); + m_apPlayerBrainTravelLabel[metaPlayer]->SetVisible(false); + m_apPlayerBrainTravelLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); + } + + // Phase info box + m_pPhaseBox = dynamic_cast(m_pGUIController->GetControl("PhaseBox")); + m_pPhaseLabel = dynamic_cast(m_pGUIController->GetControl("PhaseLabel")); + // Just get the menu button temporarily so we can set the custom menu icon + if (GUIButton* pMenuButton = dynamic_cast(m_pGUIController->GetControl("OpenMenuButton"))) { + std::snprintf(str, sizeof(str), "%c", -22); + pMenuButton->SetText(std::string(str)); + } + + // Planet mouseover scene label + m_pScenePlanetLabel = dynamic_cast(m_pGUIController->GetControl("ScenePlanetLabel")); + m_pScenePlanetLabel->SetVisible(false); + + // Scene Info Box + m_pSceneInfoPopup = dynamic_cast(m_pGUIController->GetControl("SceneInfoBox")); + m_pSceneCloseButton = dynamic_cast(m_pGUIController->GetControl("SceneCloseButton")); + m_pSceneNameLabel = dynamic_cast(m_pGUIController->GetControl("SceneNameLabel")); + m_pSceneOwnerTeam = dynamic_cast(m_pGUIController->GetControl("SceneOwnerTeam")); + m_pSceneResidentsLabel = dynamic_cast(m_pGUIController->GetControl("SceneResidentsLabel")); + m_pSceneInfoLabel = dynamic_cast(m_pGUIController->GetControl("SceneInfoLabel")); + m_pSceneInfoLabel->SetFont(m_pGUIController->GetSkin()->GetFont("FontSmall.png")); + m_pSceneInfoPopup->SetVisible(false); + m_pSceneBudgetLabel = dynamic_cast(m_pGUIController->GetControl("SceneBudgetLabel")); + m_pSceneBudgetSlider = dynamic_cast(m_pGUIController->GetControl("SceneBudgetSlider")); + m_pSceneBudgetBar = dynamic_cast(m_pGUIController->GetControl("SceneBudgetBar")); + m_pSceneBudgetBar->SetDrawColor(makecol(55, 5, 10)); + m_pAutoDesignCheckbox = dynamic_cast(m_pGUIController->GetControl("AutoDesignCheckbox")); + m_pScanInfoLabel = dynamic_cast(m_pGUIController->GetControl("ScanInfoLabel")); + + // Set initial focus, category list, and label settings + m_ScreenChange = true; + m_FocusChange = 1; + // CategoryChange(); + + // New Game Dialog + m_pSizeLabel = dynamic_cast(m_pGUIController->GetControl("SizeLabel")); + m_pSizeSlider = dynamic_cast(m_pGUIController->GetControl("SizeSlider")); + m_pDifficultyLabel = dynamic_cast(m_pGUIController->GetControl("DifficultyLabel")); + m_pDifficultySlider = dynamic_cast(m_pGUIController->GetControl("DifficultySlider")); + m_pGoldLabel = dynamic_cast(m_pGUIController->GetControl("GoldLabel")); + m_pGoldSlider = dynamic_cast(m_pGUIController->GetControl("GoldSlider")); + m_pLengthLabel = dynamic_cast(m_pGUIController->GetControl("LengthLabel")); + m_pLengthSlider = dynamic_cast(m_pGUIController->GetControl("LengthSlider")); + m_pErrorLabel = dynamic_cast(m_pGUIController->GetControl("StartErrorLabel")); + m_apPlayerControlButton[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1ControlButton")); + m_apPlayerControlButton[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2ControlButton")); + m_apPlayerControlButton[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3ControlButton")); + m_apPlayerControlButton[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4ControlButton")); + m_apPlayerTeamSelect[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1TeamCombo")); + m_apPlayerTeamSelect[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2TeamCombo")); + m_apPlayerTeamSelect[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3TeamCombo")); + m_apPlayerTeamSelect[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4TeamCombo")); + m_apPlayerTechSelect[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1TechCombo")); + m_apPlayerTechSelect[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2TechCombo")); + m_apPlayerTechSelect[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3TechCombo")); + m_apPlayerTechSelect[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4TechCombo")); + m_apPlayerHandicap[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1HCCombo")); + m_apPlayerHandicap[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2HCCombo")); + m_apPlayerHandicap[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3HCCombo")); + m_apPlayerHandicap[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4HCCombo")); + m_apPlayerNameBox[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1NameText")); + m_apPlayerNameBox[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2NameText")); + m_apPlayerNameBox[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3NameText")); + m_apPlayerNameBox[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4NameText")); + m_apPlayerAISkillSlider[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1AISkillSlider")); + m_apPlayerAISkillSlider[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2AISkillSlider")); + m_apPlayerAISkillSlider[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3AISkillSlider")); + m_apPlayerAISkillSlider[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4AISkillSlider")); + m_apPlayerAISkillLabel[Players::PlayerOne] = dynamic_cast(m_pGUIController->GetControl("P1AISkillLabel")); + m_apPlayerAISkillLabel[Players::PlayerTwo] = dynamic_cast(m_pGUIController->GetControl("P2AISkillLabel")); + m_apPlayerAISkillLabel[Players::PlayerThree] = dynamic_cast(m_pGUIController->GetControl("P3AISkillLabel")); + m_apPlayerAISkillLabel[Players::PlayerFour] = dynamic_cast(m_pGUIController->GetControl("P4AISkillLabel")); m_apPlayerControlButton[Players::PlayerOne]->SetText("Human"); -// m_apPlayerControlButton[Players::PlayerTwo]->SetText("Human"); - m_apPlayerControlButton[Players::PlayerTwo]->SetText("A.I."); - m_apPlayerNameBox[Players::PlayerOne]->SetText("Player 1"); - m_apPlayerNameBox[Players::PlayerTwo]->SetText("Player 2"); - m_apPlayerNameBox[Players::PlayerThree]->SetText("Player 3"); - m_apPlayerNameBox[Players::PlayerFour]->SetText("Player 4"); - - // Add the handicap options to the dropdowns - // Prepare the brain icon - std::snprintf(str, sizeof(str), "%c", -48); - for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) - { - m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " +5", "", 0, 0, -1); - m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " +3", "", 0, 0, -1); - m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " +1", "", 0, 0, -1); - m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " +-", "", 0, 0, -1); - m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " -1", "", 0, 0, -1); - m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " -3", "", 0, 0, -1); - m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " -5", "", 0, 0, -1); - - // Preselect the 0 handicap - m_apPlayerHandicap[metaPlayer]->SetSelectedIndex(3); - // Make the lists be scrolled to the top when they are initially dropped -// m_apPlayerHandicap[metaPlayer]->GetListPanel()->ScrollToTop(); - } - - // Save and Load Dialogs - m_NewSaveBox = dynamic_cast(m_pGUIController->GetControl("NewSaveText")); - m_pSavesToOverwriteCombo = dynamic_cast(m_pGUIController->GetControl("OverwriteList")); - m_pSavesToLoadCombo = dynamic_cast(m_pGUIController->GetControl("LoadList")); - m_pSaveInfoLabel = dynamic_cast(m_pGUIController->GetControl("SavedGameStats")); - m_pLoadInfoLabel = dynamic_cast(m_pGUIController->GetControl("LoadStats")); + // m_apPlayerControlButton[Players::PlayerTwo]->SetText("Human"); + m_apPlayerControlButton[Players::PlayerTwo]->SetText("A.I."); + m_apPlayerNameBox[Players::PlayerOne]->SetText("Player 1"); + m_apPlayerNameBox[Players::PlayerTwo]->SetText("Player 2"); + m_apPlayerNameBox[Players::PlayerThree]->SetText("Player 3"); + m_apPlayerNameBox[Players::PlayerFour]->SetText("Player 4"); + + // Add the handicap options to the dropdowns + // Prepare the brain icon + std::snprintf(str, sizeof(str), "%c", -48); + for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) { + m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " +5", "", 0, 0, -1); + m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " +3", "", 0, 0, -1); + m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " +1", "", 0, 0, -1); + m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " +-", "", 0, 0, -1); + m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " -1", "", 0, 0, -1); + m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " -3", "", 0, 0, -1); + m_apPlayerHandicap[metaPlayer]->GetListPanel()->AddItem(std::string(str) + " -5", "", 0, 0, -1); + + // Preselect the 0 handicap + m_apPlayerHandicap[metaPlayer]->SetSelectedIndex(3); + // Make the lists be scrolled to the top when they are initially dropped + // m_apPlayerHandicap[metaPlayer]->GetListPanel()->ScrollToTop(); + } + + // Save and Load Dialogs + m_NewSaveBox = dynamic_cast(m_pGUIController->GetControl("NewSaveText")); + m_pSavesToOverwriteCombo = dynamic_cast(m_pGUIController->GetControl("OverwriteList")); + m_pSavesToLoadCombo = dynamic_cast(m_pGUIController->GetControl("LoadList")); + m_pSaveInfoLabel = dynamic_cast(m_pGUIController->GetControl("SavedGameStats")); + m_pLoadInfoLabel = dynamic_cast(m_pGUIController->GetControl("LoadStats")); for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { m_apPlayerTeamSelect[player]->ClearList(); @@ -620,14 +593,14 @@ int MetagameGUI::Create(Controller *pController) m_apPlayerTeamSelect[player]->SetDropDownStyle(GUIComboBox::DropDownList); } - // Special height for the last one so it doesn't fly out of the dialog box - m_apPlayerTeamSelect[Players::PlayerFour]->SetDropHeight(40); - m_apPlayerTechSelect[Players::PlayerFour]->SetDropHeight(40); - m_apPlayerHandicap[Players::PlayerFour]->SetDropHeight(40); + // Special height for the last one so it doesn't fly out of the dialog box + m_apPlayerTeamSelect[Players::PlayerFour]->SetDropHeight(40); + m_apPlayerTechSelect[Players::PlayerFour]->SetDropHeight(40); + m_apPlayerHandicap[Players::PlayerFour]->SetDropHeight(40); - // Put the new game dialog in order - UpdatePlayerSetup(); - UpdateGameSizeLabels(); + // Put the new game dialog in order + UpdatePlayerSetup(); + UpdateGameSizeLabels(); UpdateAISkillSliders(Players::PlayerOne); UpdateAISkillSliders(Players::PlayerTwo); @@ -636,21 +609,19 @@ int MetagameGUI::Create(Controller *pController) MoveLocationsIntoTheScreen(); - // Hide all screens, the appropriate screen will reappear on next update - HideAllScreens(); - return 0; + // Hide all screens, the appropriate screen will reappear on next update + HideAllScreens(); + return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: MoveLocationsIntoTheScreen ////////////////////////////////////////////////////////////////////////////////////////// // Description: Moves any locations closer to the center of the planet if they were left out -void MetagameGUI::MoveLocationsIntoTheScreen() -{ - //Clear offsets - for (std::vector::iterator pItr = g_MetaMan.m_Scenes.begin(); pItr != g_MetaMan.m_Scenes.end(); ++pItr) - (*pItr)->SetLocationOffset(Vector(0,0)); +void MetagameGUI::MoveLocationsIntoTheScreen() { + // Clear offsets + for (std::vector::iterator pItr = g_MetaMan.m_Scenes.begin(); pItr != g_MetaMan.m_Scenes.end(); ++pItr) + (*pItr)->SetLocationOffset(Vector(0, 0)); // We need to calculate planet center manually because m_PlanetCenter reflects coords of moving planet // which is outside the screen when this is called first time @@ -659,14 +630,12 @@ void MetagameGUI::MoveLocationsIntoTheScreen() if (!m_PlanetCenter.IsZero()) planetCenter = m_PlanetCenter; - //Move out-of-screen scenes closer to the middle of the planet if we have planet info - for (std::vector::iterator pItr = g_MetaMan.m_Scenes.begin(); pItr != g_MetaMan.m_Scenes.end(); ++pItr) - { + // Move out-of-screen scenes closer to the middle of the planet if we have planet info + for (std::vector::iterator pItr = g_MetaMan.m_Scenes.begin(); pItr != g_MetaMan.m_Scenes.end(); ++pItr) { float y = planetCenter.GetY() + (*pItr)->GetLocation().GetY(); // Do not touch scenes outside the planet, they might be hidden intentionally - if (abs((*pItr)->GetLocation().GetY()) < m_PlanetRadius + 100 && abs((*pItr)->GetLocation().GetX()) < m_PlanetRadius + 100) - { + if (abs((*pItr)->GetLocation().GetY()) < m_PlanetRadius + 100 && abs((*pItr)->GetLocation().GetX()) < m_PlanetRadius + 100) { if (y < 10) (*pItr)->SetLocationOffset(Vector(0, -y + 14)); @@ -676,24 +645,19 @@ void MetagameGUI::MoveLocationsIntoTheScreen() } // Add offsets to reveal overlapping scenes if any - for (std::vector::iterator pItr = g_MetaMan.m_Scenes.begin(); pItr != g_MetaMan.m_Scenes.end(); ++pItr) - { + for (std::vector::iterator pItr = g_MetaMan.m_Scenes.begin(); pItr != g_MetaMan.m_Scenes.end(); ++pItr) { bool isOverlapped = false; - do - { + do { isOverlapped = false; // Find overlapping scene dot - for (std::vector::iterator pItr2 = g_MetaMan.m_Scenes.begin(); pItr2 != g_MetaMan.m_Scenes.end(); ++pItr2) - { - if ((*pItr) != (*pItr2)) - { + for (std::vector::iterator pItr2 = g_MetaMan.m_Scenes.begin(); pItr2 != g_MetaMan.m_Scenes.end(); ++pItr2) { + if ((*pItr) != (*pItr2)) { Vector pos1 = (*pItr)->GetLocation() + (*pItr)->GetLocationOffset(); Vector pos2 = (*pItr2)->GetLocation() + (*pItr2)->GetLocationOffset(); - if ((pos1 - pos2).MagnitudeIsLessThan(8.0F)) - { + if ((pos1 - pos2).MagnitudeIsLessThan(8.0F)) { isOverlapped = true; break; } @@ -709,12 +673,10 @@ void MetagameGUI::MoveLocationsIntoTheScreen() if (isOverlapped) (*pItr)->SetLocationOffset((*pItr)->GetLocationOffset() + offsetIncrement); - } - while (isOverlapped); + } while (isOverlapped); } } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: ReadProperty ////////////////////////////////////////////////////////////////////////////////////////// @@ -723,49 +685,47 @@ void MetagameGUI::MoveLocationsIntoTheScreen() // is called. If the property isn't recognized by any of the base classes, // false is returned, and the Reader's position is untouched. -int MetagameGUI::ReadProperty(const std::string_view &propName, Reader &reader) -{ - Vector tempPos; - - StartPropertyList(return Serializable::ReadProperty(propName, reader)); - - MatchProperty("P1BoxPos", - { - reader >> tempPos; - m_apPlayerBox[Players::PlayerOne]->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); - }); - MatchProperty("P2BoxPos", - { - reader >> tempPos; - m_apPlayerBox[Players::PlayerTwo]->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); - }); - MatchProperty("P3BoxPos", - { - reader >> tempPos; - m_apPlayerBox[Players::PlayerThree]->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); - }); - MatchProperty("P4BoxPos", - { - reader >> tempPos; - m_apPlayerBox[Players::PlayerFour]->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); - }); - MatchProperty("PhaseBoxPos", - { - reader >> tempPos; - m_pPhaseBox->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); - }); - - EndPropertyList; +int MetagameGUI::ReadProperty(const std::string_view& propName, Reader& reader) { + Vector tempPos; + + StartPropertyList(return Serializable::ReadProperty(propName, reader)); + + MatchProperty("P1BoxPos", + { + reader >> tempPos; + m_apPlayerBox[Players::PlayerOne]->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); + }); + MatchProperty("P2BoxPos", + { + reader >> tempPos; + m_apPlayerBox[Players::PlayerTwo]->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); + }); + MatchProperty("P3BoxPos", + { + reader >> tempPos; + m_apPlayerBox[Players::PlayerThree]->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); + }); + MatchProperty("P4BoxPos", + { + reader >> tempPos; + m_apPlayerBox[Players::PlayerFour]->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); + }); + MatchProperty("PhaseBoxPos", + { + reader >> tempPos; + m_pPhaseBox->SetPositionAbs(tempPos.GetFloorIntX(), tempPos.GetFloorIntY()); + }); + + EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Save ////////////////////////////////////////////////////////////////////////////////////////// // Description: Saves the complete state of this MetagameGUI to an output stream for // later recreation with Create(Reader &reader); -int MetagameGUI::Save(Writer &writer) const { +int MetagameGUI::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("P1BoxPos", Vector(m_apPlayerBox[Players::PlayerOne]->GetXPos(), m_apPlayerBox[Players::PlayerOne]->GetYPos())); @@ -778,55 +738,46 @@ int MetagameGUI::Save(Writer &writer) const { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the MetagameGUI object. -void MetagameGUI::Destroy() -{ - delete m_pGUIController; - delete m_pGUIInput; - delete m_pGUIScreen; +void MetagameGUI::Destroy() { + delete m_pGUIController; + delete m_pGUIInput; + delete m_pGUIScreen; - delete m_pBannerRedTop; - delete m_pBannerRedBottom; - delete m_pBannerYellowTop; - delete m_pBannerYellowBottom; + delete m_pBannerRedTop; + delete m_pBannerRedBottom; + delete m_pBannerYellowTop; + delete m_pBannerYellowBottom; - Clear(); + Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetGUIControlManager ////////////////////////////////////////////////////////////////////////////////////////// // Description: Gets the GUIControlManager owned and used by this. -GUIControlManager * MetagameGUI::GetGUIControlManager() -{ - return m_pGUIController; +GUIControlManager* MetagameGUI::GetGUIControlManager() { + return m_pGUIController; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetEnabled ////////////////////////////////////////////////////////////////////////////////////////// // Description: Enables or disables the menu. This will animate it in and out of view. -void MetagameGUI::SetEnabled(bool enable) -{ - if (enable && m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) - { - m_MenuEnabled = ENABLING; - g_GUISound.EnterMenuSound()->Play(); - } - else if (!enable && m_MenuEnabled != DISABLED && m_MenuEnabled != DISABLING) - { - m_MenuEnabled = DISABLING; - g_GUISound.ExitMenuSound()->Play(); - } +void MetagameGUI::SetEnabled(bool enable) { + if (enable && m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) { + m_MenuEnabled = ENABLING; + g_GUISound.EnterMenuSound()->Play(); + } else if (!enable && m_MenuEnabled != DISABLED && m_MenuEnabled != DISABLING) { + m_MenuEnabled = DISABLING; + g_GUISound.ExitMenuSound()->Play(); + } // Populate the tech comboboxes with the available tech modules for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { @@ -834,7 +785,7 @@ void MetagameGUI::SetEnabled(bool enable) m_apPlayerTechSelect[team]->SetSelectedIndex(0); } for (int moduleID = 0; moduleID < g_PresetMan.GetTotalModuleCount(); ++moduleID) { - if (const DataModule *dataModule = g_PresetMan.GetDataModule(moduleID)) { + if (const DataModule* dataModule = g_PresetMan.GetDataModule(moduleID)) { if (dataModule->IsFaction()) { for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { m_apPlayerTechSelect[team]->GetListPanel()->AddItem(dataModule->GetFriendlyName(), "", nullptr, nullptr, moduleID); @@ -844,214 +795,196 @@ void MetagameGUI::SetEnabled(bool enable) } } - std::list flagList; + std::list flagList; g_PresetMan.GetAllOfGroup(flagList, "Flags", "Icon"); for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - for (std::list::iterator itr = flagList.begin(); itr != flagList.end(); ++itr) { - if (const Icon *pIcon = dynamic_cast(*itr)) { m_apPlayerTeamSelect[player]->AddItem("", "", new AllegroBitmap(pIcon->GetBitmaps32()[0]), pIcon); } + for (std::list::iterator itr = flagList.begin(); itr != flagList.end(); ++itr) { + if (const Icon* pIcon = dynamic_cast(*itr)) { + m_apPlayerTeamSelect[player]->AddItem("", "", new AllegroBitmap(pIcon->GetBitmaps32()[0]), pIcon); + } } } - if (m_apPlayerTeamSelect[Players::PlayerOne]->GetSelectedIndex() < 0) { m_apPlayerTeamSelect[Players::PlayerOne]->SetSelectedIndex(0); } + if (m_apPlayerTeamSelect[Players::PlayerOne]->GetSelectedIndex() < 0) { + m_apPlayerTeamSelect[Players::PlayerOne]->SetSelectedIndex(0); + } - m_ScreenChange = true; + m_ScreenChange = true; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SelectScene ////////////////////////////////////////////////////////////////////////////////////////// // Description: Tries to select a specifically named scene on the metagame field. -void MetagameGUI::SelectScene(Scene *pScene) -{ - m_pSelectedScene = pScene; - - // Update the budget slider - if (m_pSelectedScene) - { - // If during a player's round phase, show and set the budget slider to reflect the newly selected scene's setting - if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) - { - int metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; - - // If owned by this player's team, make the budget slider represent the currently set setting of this Scene - if (m_pSelectedScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer)) - { - m_pSceneBudgetSlider->SetValue(std::floor((m_pSelectedScene->GetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()) / g_MetaMan.m_Players[metaPlayer].GetFunds()) * 100)); - } - // Owned by enemy player, so show the attack budget set up for this scene - else if (g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())) - { - if (m_pSelectedScene->GetPresetName() == g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName()) - m_pSceneBudgetSlider->SetValue(std::floor((g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget() / g_MetaMan.m_Players[metaPlayer].GetFunds()) * 100)); - // Not the current target, so set slider to 0. It will set the new budget as - else - m_pSceneBudgetSlider->SetValue(0); - } - // Unowned site, so set up expedition budget (same so far) - else - { - if (m_pSelectedScene->GetPresetName() == g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName()) - m_pSceneBudgetSlider->SetValue(std::floor((g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget() / g_MetaMan.m_Players[metaPlayer].GetFunds()) * 100)); - // Not the current target, so set slider to 0. It will set the new budget as - else - m_pSceneBudgetSlider->SetValue(0); - } - } - - UpdateScenesBox(true); - } -} +void MetagameGUI::SelectScene(Scene* pScene) { + m_pSelectedScene = pScene; + + // Update the budget slider + if (m_pSelectedScene) { + // If during a player's round phase, show and set the budget slider to reflect the newly selected scene's setting + if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) { + int metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; + + // If owned by this player's team, make the budget slider represent the currently set setting of this Scene + if (m_pSelectedScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer)) { + m_pSceneBudgetSlider->SetValue(std::floor((m_pSelectedScene->GetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()) / g_MetaMan.m_Players[metaPlayer].GetFunds()) * 100)); + } + // Owned by enemy player, so show the attack budget set up for this scene + else if (g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())) { + if (m_pSelectedScene->GetPresetName() == g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName()) + m_pSceneBudgetSlider->SetValue(std::floor((g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget() / g_MetaMan.m_Players[metaPlayer].GetFunds()) * 100)); + // Not the current target, so set slider to 0. It will set the new budget as + else + m_pSceneBudgetSlider->SetValue(0); + } + // Unowned site, so set up expedition budget (same so far) + else { + if (m_pSelectedScene->GetPresetName() == g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName()) + m_pSceneBudgetSlider->SetValue(std::floor((g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget() / g_MetaMan.m_Players[metaPlayer].GetFunds()) * 100)); + // Not the current target, so set slider to 0. It will set the new budget as + else + m_pSceneBudgetSlider->SetValue(0); + } + } + UpdateScenesBox(true); + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: SelectScene ////////////////////////////////////////////////////////////////////////////////////////// // Description: Tries to select a specifically named scene on the metagame field. -bool MetagameGUI::SelectScene(std::string sceneName) -{ - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Only allow selection if the Scene is revealed yet! - if ((*sItr)->GetPresetName() == sceneName && (*sItr)->IsRevealed()) - { - SelectScene(*sItr); - return true; - } - } - - return false; -} +bool MetagameGUI::SelectScene(std::string sceneName) { + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Only allow selection if the Scene is revealed yet! + if ((*sItr)->GetPresetName() == sceneName && (*sItr)->IsRevealed()) { + SelectScene(*sItr); + return true; + } + } + return false; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: SwitchToScreen ////////////////////////////////////////////////////////////////////////////////////////// // Description: Switches to showing a specific menu screen/mode. -void MetagameGUI::SwitchToScreen(int newScreen) -{ - RTEAssert(newScreen >= ROOTBOX && newScreen < SCREENCOUNT, "Tried to switch to an out of bounds screen!"); +void MetagameGUI::SwitchToScreen(int newScreen) { + RTEAssert(newScreen >= ROOTBOX && newScreen < SCREENCOUNT, "Tried to switch to an out of bounds screen!"); - // Hide all previously shown screens - HideAllScreens(); + // Hide all previously shown screens + HideAllScreens(); - m_MenuScreen = newScreen; - m_ScreenChange = true; + m_MenuScreen = newScreen; + m_ScreenChange = true; - // Show the selected screen! - m_apScreenBox[m_MenuScreen]->SetVisible(true); + // Show the selected screen! + m_apScreenBox[m_MenuScreen]->SetVisible(true); - // Suspend game depending on which screen it is - g_MetaMan.SetSuspend(m_MenuScreen != ROOTBOX && m_MenuScreen != STATSDIALOG && m_MenuScreen != SCENEINFOBOX); + // Suspend game depending on which screen it is + g_MetaMan.SetSuspend(m_MenuScreen != ROOTBOX && m_MenuScreen != STATSDIALOG && m_MenuScreen != SCENEINFOBOX); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetRoundName ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes a round number into a nice friendly text string. "ONE" for 1 etc -std::string MetagameGUI::GetRoundName(int roundNumber) -{ - if (roundNumber < 12) - { - if (roundNumber == 0) - return "ONE"; - else if (roundNumber == 1) - return "TWO"; - else if (roundNumber == 2) - return "THREE"; - else if (roundNumber == 3) - return "FOUR"; - else if (roundNumber == 4) - return "FIVE"; - else if (roundNumber == 5) - return "SIX"; - else if (roundNumber == 6) - return "SEVEN"; - else if (roundNumber == 7) - return "EIGHT"; - else if (roundNumber == 8) - return "NINE"; - else if (roundNumber == 9) - return "TEN"; - else if (roundNumber == 10) - return "ELEVEN"; - else if (roundNumber == 11) - return "TWELVE"; - } - char numStr[8]; - std::snprintf(numStr, sizeof(numStr), "%d", roundNumber + 1); - return std::string(numStr); +std::string MetagameGUI::GetRoundName(int roundNumber) { + if (roundNumber < 12) { + if (roundNumber == 0) + return "ONE"; + else if (roundNumber == 1) + return "TWO"; + else if (roundNumber == 2) + return "THREE"; + else if (roundNumber == 3) + return "FOUR"; + else if (roundNumber == 4) + return "FIVE"; + else if (roundNumber == 5) + return "SIX"; + else if (roundNumber == 6) + return "SEVEN"; + else if (roundNumber == 7) + return "EIGHT"; + else if (roundNumber == 8) + return "NINE"; + else if (roundNumber == 9) + return "TEN"; + else if (roundNumber == 10) + return "ELEVEN"; + else if (roundNumber == 11) + return "TWELVE"; + } + char numStr[8]; + std::snprintf(numStr, sizeof(numStr), "%d", roundNumber + 1); + return std::string(numStr); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: StartNewGame ////////////////////////////////////////////////////////////////////////////////////////// // Description: Attempts to start a new Metagame using the settings set in the // New Game dialog box. -bool MetagameGUI::StartNewGame() -{ - // Prepare the UI for the game intro/start - UpdatePlayerSetup(); - HideAllScreens(); - m_IncomeSiteLines.clear(); - m_NewSiteIndicators.clear(); - m_SiteSwitchIndicators.clear(); - - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - g_MetaMan.m_TeamIcons[team].Reset(); - - // Create the MetaPlayer:s based on the settings in the dialog box - char str[256]; - g_MetaMan.m_Players.clear(); - g_MetaMan.m_TeamCount = 0; +bool MetagameGUI::StartNewGame() { + // Prepare the UI for the game intro/start + UpdatePlayerSetup(); + HideAllScreens(); + m_IncomeSiteLines.clear(); + m_NewSiteIndicators.clear(); + m_SiteSwitchIndicators.clear(); + + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) + g_MetaMan.m_TeamIcons[team].Reset(); + + // Create the MetaPlayer:s based on the settings in the dialog box + char str[256]; + g_MetaMan.m_Players.clear(); + g_MetaMan.m_TeamCount = 0; g_MetaMan.m_Difficulty = m_pDifficultySlider->GetValue(); m_StartDifficulty = m_pDifficultySlider->GetValue(); - const Icon *pTeamIcon = 0; + const Icon* pTeamIcon = 0; - //Clear metaman's AI skill to defaults + // Clear metaman's AI skill to defaults for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; team++) g_MetaMan.m_TeamAISkill[team] = Activity::DefaultSkill; - // Starting gold amount is common to all - int startGold = STARTGOLDMIN + ((STARTGOLDMAX - STARTGOLDMIN) * (float)m_pGoldSlider->GetValue() / 100.0); - - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - m_ActionSiteLines[player].clear(); - // clear the flag icons on the floating player bars; they will be set on next update in UpdatePlayerBars() - m_apPlayerTeamBox[player]->SetDrawType(GUICollectionBox::Image); - m_apPlayerTeamBox[player]->SetDrawImage(0); - // Same for the players' team flags that appear around the battle sites - m_apPlayerTeamActionBox[player]->SetDrawType(GUICollectionBox::Image); - m_apPlayerTeamActionBox[player]->SetDrawImage(0); - - // Found an active player - if (m_apPlayerControlButton[player]->GetText() != "None") - { - // Disallow empty player name strings - if (m_apPlayerNameBox[player]->GetText() == "") - { - std::snprintf(str, sizeof(str), "Player %d", player); - m_apPlayerNameBox[player]->SetText(str); - } - - // Set up the new player and add it - MetaPlayer newPlayer; - newPlayer.SetName(m_apPlayerNameBox[player]->GetText()); - // Set the in-game control mapping of this metagame player - newPlayer.m_InGamePlayer = player; - // Whether this is a human or AI player - newPlayer.SetHuman(m_apPlayerControlButton[player]->GetText() == "Human"); + // Starting gold amount is common to all + int startGold = STARTGOLDMIN + ((STARTGOLDMAX - STARTGOLDMIN) * (float)m_pGoldSlider->GetValue() / 100.0); + + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + m_ActionSiteLines[player].clear(); + // clear the flag icons on the floating player bars; they will be set on next update in UpdatePlayerBars() + m_apPlayerTeamBox[player]->SetDrawType(GUICollectionBox::Image); + m_apPlayerTeamBox[player]->SetDrawImage(0); + // Same for the players' team flags that appear around the battle sites + m_apPlayerTeamActionBox[player]->SetDrawType(GUICollectionBox::Image); + m_apPlayerTeamActionBox[player]->SetDrawImage(0); + + // Found an active player + if (m_apPlayerControlButton[player]->GetText() != "None") { + // Disallow empty player name strings + if (m_apPlayerNameBox[player]->GetText() == "") { + std::snprintf(str, sizeof(str), "Player %d", player); + m_apPlayerNameBox[player]->SetText(str); + } + + // Set up the new player and add it + MetaPlayer newPlayer; + newPlayer.SetName(m_apPlayerNameBox[player]->GetText()); + // Set the in-game control mapping of this metagame player + newPlayer.m_InGamePlayer = player; + // Whether this is a human or AI player + newPlayer.SetHuman(m_apPlayerControlButton[player]->GetText() == "Human"); // Set native cost multypliiers according to difficulty - if (!newPlayer.IsHuman()) - { + if (!newPlayer.IsHuman()) { if (g_MetaMan.m_Difficulty < Activity::CakeDifficulty) newPlayer.SetNativeCostMultiplier(1.2); else if (g_MetaMan.m_Difficulty < Activity::EasyDifficulty) @@ -1066,42 +999,39 @@ bool MetagameGUI::StartNewGame() newPlayer.SetNativeCostMultiplier(0.40); } -// TODO: Add the control scheme icons to the newgame dialog for clarity - - // Get the chosen team icon - if (m_apPlayerTeamSelect[player]->GetSelectedItem()) - pTeamIcon = dynamic_cast(m_apPlayerTeamSelect[player]->GetSelectedItem()->m_pEntity); - // Just get the first one if nothing is selected - else if (m_apPlayerTeamSelect[player]->GetCount() > 0) - pTeamIcon = dynamic_cast(m_apPlayerTeamSelect[player]->GetItem(0)->m_pEntity); - - // See if the player is designated to a new team or one that has already been created - bool newTeam = true; - for (int team = Activity::TeamOne; team < g_MetaMan.m_TeamCount; ++team) - { - // Join existing team! - if (pTeamIcon->GetPresetName() == g_MetaMan.m_TeamIcons[team].GetPresetName()) - { - newPlayer.SetTeam(team); - newTeam = false; - break; - } - } - - // If we didn't find that the team we were designated already exists, then create it - if (newTeam) - { - // Set the team of the new player - newPlayer.SetTeam(g_MetaMan.m_TeamCount); + // TODO: Add the control scheme icons to the newgame dialog for clarity + + // Get the chosen team icon + if (m_apPlayerTeamSelect[player]->GetSelectedItem()) + pTeamIcon = dynamic_cast(m_apPlayerTeamSelect[player]->GetSelectedItem()->m_pEntity); + // Just get the first one if nothing is selected + else if (m_apPlayerTeamSelect[player]->GetCount() > 0) + pTeamIcon = dynamic_cast(m_apPlayerTeamSelect[player]->GetItem(0)->m_pEntity); + + // See if the player is designated to a new team or one that has already been created + bool newTeam = true; + for (int team = Activity::TeamOne; team < g_MetaMan.m_TeamCount; ++team) { + // Join existing team! + if (pTeamIcon->GetPresetName() == g_MetaMan.m_TeamIcons[team].GetPresetName()) { + newPlayer.SetTeam(team); + newTeam = false; + break; + } + } + + // If we didn't find that the team we were designated already exists, then create it + if (newTeam) { + // Set the team of the new player + newPlayer.SetTeam(g_MetaMan.m_TeamCount); // Set AI Skill level g_MetaMan.m_TeamAISkill[g_MetaMan.m_TeamCount] = m_apPlayerAISkillSlider[player]->GetValue(); - // Set the new team icon - g_MetaMan.m_TeamIcons[g_MetaMan.m_TeamCount] = *pTeamIcon; - // Increase the team count - g_MetaMan.m_TeamCount++; - } + // Set the new team icon + g_MetaMan.m_TeamIcons[g_MetaMan.m_TeamCount] = *pTeamIcon; + // Increase the team count + g_MetaMan.m_TeamCount++; + } - if (const GUIListPanel::Item *selectedTech = m_apPlayerTechSelect[player]->GetSelectedItem()) { + if (const GUIListPanel::Item* selectedTech = m_apPlayerTechSelect[player]->GetSelectedItem()) { // If the "random" selection, choose one from the list of loaded techs. if (m_apPlayerTechSelect[player]->GetSelectedIndex() <= 0) { int randomSelection = 0; @@ -1112,79 +1042,81 @@ bool MetagameGUI::StartNewGame() randomSelection = RandomNum(1, m_apPlayerTechSelect[player]->GetListPanel()->GetItemList()->size() - 1); ok = true; for (int p = 0; p < player; p++) { - if (randomSelection == m_apPlayerTechSelect[p]->GetSelectedIndex()) { ok = false; } + if (randomSelection == m_apPlayerTechSelect[p]->GetSelectedIndex()) { + ok = false; + } } } selectedTech = m_apPlayerTechSelect[player]->GetItem(randomSelection); } - if (selectedTech) { newPlayer.m_NativeTechModule = selectedTech->m_ExtraIndex; } - } - - // Set the starting brains for this player - // Start with the baseline setting - newPlayer.m_BrainPool = m_pLengthSlider->GetValue(); - // Baseline can never be 0 - newPlayer.m_BrainPool = MAX(newPlayer.m_BrainPool, 1); - // Apply the handicap! - if (m_apPlayerHandicap[player]->GetSelectedIndex() == 0) - newPlayer.m_BrainPool += 5; - else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 1) - newPlayer.m_BrainPool += 3; - else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 2) - newPlayer.m_BrainPool += 1; - else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 4) - newPlayer.m_BrainPool -= 1; - else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 5) - newPlayer.m_BrainPool -= 3; - else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 6) - newPlayer.m_BrainPool -= 5; - // Give at least ONE brain! - newPlayer.m_BrainPool = MAX(newPlayer.m_BrainPool, 1); - - // Starting gold amount; common to all - newPlayer.m_Funds = startGold; - - g_MetaMan.m_Players.push_back(newPlayer); - - m_apPlayerTeamSelect[player]->SetVisible(false); - m_apPlayerTechSelect[player]->SetVisible(false); - m_apPlayerNameBox[player]->SetVisible(false); + if (selectedTech) { + newPlayer.m_NativeTechModule = selectedTech->m_ExtraIndex; + } + } + + // Set the starting brains for this player + // Start with the baseline setting + newPlayer.m_BrainPool = m_pLengthSlider->GetValue(); + // Baseline can never be 0 + newPlayer.m_BrainPool = MAX(newPlayer.m_BrainPool, 1); + // Apply the handicap! + if (m_apPlayerHandicap[player]->GetSelectedIndex() == 0) + newPlayer.m_BrainPool += 5; + else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 1) + newPlayer.m_BrainPool += 3; + else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 2) + newPlayer.m_BrainPool += 1; + else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 4) + newPlayer.m_BrainPool -= 1; + else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 5) + newPlayer.m_BrainPool -= 3; + else if (m_apPlayerHandicap[player]->GetSelectedIndex() == 6) + newPlayer.m_BrainPool -= 5; + // Give at least ONE brain! + newPlayer.m_BrainPool = MAX(newPlayer.m_BrainPool, 1); + + // Starting gold amount; common to all + newPlayer.m_Funds = startGold; + + g_MetaMan.m_Players.push_back(newPlayer); + + m_apPlayerTeamSelect[player]->SetVisible(false); + m_apPlayerTechSelect[player]->SetVisible(false); + m_apPlayerNameBox[player]->SetVisible(false); m_apPlayerAISkillSlider[player]->SetVisible(false); m_apPlayerAISkillLabel[player]->SetVisible(false); - m_aBattleFunds[player] = 0; - m_aBattleAttacker[player] = false; - m_aAnimDestroyed[player] = false; - m_aBrainIconPos[player].Reset(); - continue; - } - } - - // Arrange all the floating UI elements neatly, depending on number of playing players - m_apPlayerBox[Players::PlayerOne]->SetPositionRel(20, 30); - m_apPlayerBox[Players::PlayerTwo]->SetPositionRel(20, m_apScreenBox[ROOTBOX]->GetHeight() - m_apPlayerBox[Players::PlayerTwo]->GetHeight() - 30); - m_apPlayerBox[Players::PlayerThree]->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_apPlayerBox[Players::PlayerThree]->GetWidth() - 20, m_apScreenBox[ROOTBOX]->GetHeight() - m_apPlayerBox[Players::PlayerThree]->GetHeight() - 30); - m_apPlayerBox[Players::PlayerFour]->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_apPlayerBox[Players::PlayerFour]->GetWidth() - 20, 30); - UpdatePlayerBars(); - - // Place Scene Info popup in convenient position - if (g_MetaMan.m_Players.size() <= 3) - m_pSceneInfoPopup->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_pSceneInfoPopup->GetWidth() - 16, 16); - else - m_pSceneInfoPopup->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_pSceneInfoPopup->GetWidth() - 16, 110); - - // If two or fewer players, place phase box in lower right corner of screen, otherwise center bottom - if (g_MetaMan.m_Players.size() <= 2) - m_pPhaseBox->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_pPhaseBox->GetWidth() - 10, m_apScreenBox[ROOTBOX]->GetHeight() - m_pPhaseBox->GetHeight() - 40); - else - m_pPhaseBox->SetPositionRel((m_apScreenBox[ROOTBOX]->GetWidth() / 2) - (m_pPhaseBox->GetWidth() / 2), m_apScreenBox[ROOTBOX]->GetHeight() - m_pPhaseBox->GetHeight() - 10); - - - // Start game of specified size! - g_MetaMan.NewGame(m_pSizeSlider->GetValue()); - - return true; -} + m_aBattleFunds[player] = 0; + m_aBattleAttacker[player] = false; + m_aAnimDestroyed[player] = false; + m_aBrainIconPos[player].Reset(); + continue; + } + } + // Arrange all the floating UI elements neatly, depending on number of playing players + m_apPlayerBox[Players::PlayerOne]->SetPositionRel(20, 30); + m_apPlayerBox[Players::PlayerTwo]->SetPositionRel(20, m_apScreenBox[ROOTBOX]->GetHeight() - m_apPlayerBox[Players::PlayerTwo]->GetHeight() - 30); + m_apPlayerBox[Players::PlayerThree]->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_apPlayerBox[Players::PlayerThree]->GetWidth() - 20, m_apScreenBox[ROOTBOX]->GetHeight() - m_apPlayerBox[Players::PlayerThree]->GetHeight() - 30); + m_apPlayerBox[Players::PlayerFour]->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_apPlayerBox[Players::PlayerFour]->GetWidth() - 20, 30); + UpdatePlayerBars(); + + // Place Scene Info popup in convenient position + if (g_MetaMan.m_Players.size() <= 3) + m_pSceneInfoPopup->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_pSceneInfoPopup->GetWidth() - 16, 16); + else + m_pSceneInfoPopup->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_pSceneInfoPopup->GetWidth() - 16, 110); + + // If two or fewer players, place phase box in lower right corner of screen, otherwise center bottom + if (g_MetaMan.m_Players.size() <= 2) + m_pPhaseBox->SetPositionRel(m_apScreenBox[ROOTBOX]->GetWidth() - m_pPhaseBox->GetWidth() - 10, m_apScreenBox[ROOTBOX]->GetHeight() - m_pPhaseBox->GetHeight() - 40); + else + m_pPhaseBox->SetPositionRel((m_apScreenBox[ROOTBOX]->GetWidth() / 2) - (m_pPhaseBox->GetWidth() / 2), m_apScreenBox[ROOTBOX]->GetHeight() - m_pPhaseBox->GetHeight() - 10); + + // Start game of specified size! + g_MetaMan.NewGame(m_pSizeSlider->GetValue()); + + return true; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: LoadGame @@ -1192,114 +1124,107 @@ bool MetagameGUI::StartNewGame() // Description: Attempts to load a Metagame from disk using the settings set in the // Load Game dialog box. -bool MetagameGUI::LoadGame() -{ - // Get the MetaSave to load from the previously temporarily saved combobox selection - if (m_pSelectedGameToLoad) - { - const MetaSave *saveToLoad = dynamic_cast(m_pSelectedGameToLoad); - if (saveToLoad) - { - if (g_MetaMan.Load(saveToLoad) < 0) - { - g_ConsoleMan.PrintString("ERROR: Failed to load Metagame '" + saveToLoad->GetPresetName() + "' from " + saveToLoad->GetSavePath()); - return false; - } +bool MetagameGUI::LoadGame() { + // Get the MetaSave to load from the previously temporarily saved combobox selection + if (m_pSelectedGameToLoad) { + const MetaSave* saveToLoad = dynamic_cast(m_pSelectedGameToLoad); + if (saveToLoad) { + if (g_MetaMan.Load(saveToLoad) < 0) { + g_ConsoleMan.PrintString("ERROR: Failed to load Metagame '" + saveToLoad->GetPresetName() + "' from " + saveToLoad->GetSavePath()); + return false; + } // Reconstruct income site lines without changing funds UpdateIncomeCounting(true); - // Reconstruct the player action lines - they are important! - for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - UpdatePlayerActionLines(metaPlayer); + // Reconstruct the player action lines - they are important! + for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) + UpdatePlayerActionLines(metaPlayer); - // Re-init some other GUI elements - UpdatePlayerBars(); - UpdateScenesBox(true); + // Re-init some other GUI elements + UpdatePlayerBars(); + UpdateScenesBox(true); - // Make sure GUI boxes are on the screen; save game might have been made on wonky resolution - for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - KeepBoxOnScreen(m_apPlayerBox[metaPlayer], 30); - KeepBoxOnScreen(m_pPhaseBox, 30); - KeepBoxOnScreen(m_pSceneInfoPopup, 30); + // Make sure GUI boxes are on the screen; save game might have been made on wonky resolution + for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) + KeepBoxOnScreen(m_apPlayerBox[metaPlayer], 30); + KeepBoxOnScreen(m_pPhaseBox, 30); + KeepBoxOnScreen(m_pSceneInfoPopup, 30); - // Reset some special state vars - m_PostBattleReview = false; - m_BattleCausedOwnershipChange = false; + // Reset some special state vars + m_PostBattleReview = false; + m_BattleCausedOwnershipChange = false; - //Move locations back to screen if they are not visible + // Move locations back to screen if they are not visible MoveLocationsIntoTheScreen(); - g_ConsoleMan.PrintString("Successfully loaded Metagame '" + saveToLoad->GetPresetName() + "' from " + saveToLoad->GetSavePath()); - m_ContinuePhase = false; - return true; - } - } + g_ConsoleMan.PrintString("Successfully loaded Metagame '" + saveToLoad->GetPresetName() + "' from " + saveToLoad->GetSavePath()); + m_ContinuePhase = false; + return true; + } + } - g_ConsoleMan.PrintString("ERROR: Failed to load the Metagame"); - return false; + g_ConsoleMan.PrintString("ERROR: Failed to load the Metagame"); + return false; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SaveGame ////////////////////////////////////////////////////////////////////////////////////////// // Description: Attempts to save a Metagame to disk using the settings set in the // Save Game dialog box. -bool MetagameGUI::SaveGame(std::string saveName, std::string savePath, bool resaveSceneData) -{ - const std::string fullSavePath = g_PresetMan.GetFullModulePath(savePath); - // If specified, first load all bitmap data of all Scenes in the current Metagame that have once saved em, so we can re-save them to the new files - if (resaveSceneData) - g_MetaMan.LoadSceneData(); - - // Set the GameName of the MetaGame to be the save name - g_MetaMan.m_GameName = saveName; - - // Save any loaded scene data FIRST, so that all the paths of ContentFiles get updated to the actual save location first, - // which may have been changed due to the saveName being different than before. - g_MetaMan.SaveSceneData(METASAVEPATH + saveName); - - // Whichever new or existing, create a writer with the path - Writer metaWriter(fullSavePath.c_str()); - // Now that all the updated data files have been written to disk and their paths updated, send the MetaMan state for actual writing to an ini - if (g_MetaMan.Save(metaWriter) < 0) - return false; - - // Clear out the scene data again so we're not keeping it in memory unnecessarily - if (resaveSceneData) - g_MetaMan.ClearSceneData(); - - // After successful save, update the corresponding preset to reflect the newly saved game - // Create a new MetaSave preset that will hold the runtime info of this new save (so it shows up as something we can overwrite later this same runtime) - MetaSave newSave; - // This will automatically set all internal members to represent what MetaMan's current state is - newSave.Create(fullSavePath); - newSave.SetPresetName(saveName); - - // Now add or update the actual Preset - g_PresetMan.AddEntityPreset(&newSave, g_PresetMan.GetModuleID(METASAVEMODULENAME), true, std::string(METASAVEPATH) + "Index.ini"); - - // Now write out the index file of all MetaSaves so the new save is found on next runtime - Writer indexWriter((std::string(METASAVEPATH) + "Index.ini").c_str()); - indexWriter.ObjectStart("DataModule"); - indexWriter.NewPropertyWithValue("ModuleName", "Metagame Saves"); - // Get the current list of all MetaSave Preset:s, including the new one we just saved - std::list saveList; - g_PresetMan.GetAllOfType(saveList, "MetaSave"); - // Go through the list and add their names to the combo box - for (const Entity *saveListEntry : saveList) { - indexWriter.NewPropertyWithValue("AddMetaSave", saveListEntry); - } - indexWriter.ObjectEnd(); - - // Report to console - g_ConsoleMan.PrintString("Successfully saved Metagame '" + saveName + "' to " + savePath); - - return true; -} +bool MetagameGUI::SaveGame(std::string saveName, std::string savePath, bool resaveSceneData) { + const std::string fullSavePath = g_PresetMan.GetFullModulePath(savePath); + // If specified, first load all bitmap data of all Scenes in the current Metagame that have once saved em, so we can re-save them to the new files + if (resaveSceneData) + g_MetaMan.LoadSceneData(); + + // Set the GameName of the MetaGame to be the save name + g_MetaMan.m_GameName = saveName; + + // Save any loaded scene data FIRST, so that all the paths of ContentFiles get updated to the actual save location first, + // which may have been changed due to the saveName being different than before. + g_MetaMan.SaveSceneData(METASAVEPATH + saveName); + + // Whichever new or existing, create a writer with the path + Writer metaWriter(fullSavePath.c_str()); + // Now that all the updated data files have been written to disk and their paths updated, send the MetaMan state for actual writing to an ini + if (g_MetaMan.Save(metaWriter) < 0) + return false; + + // Clear out the scene data again so we're not keeping it in memory unnecessarily + if (resaveSceneData) + g_MetaMan.ClearSceneData(); + + // After successful save, update the corresponding preset to reflect the newly saved game + // Create a new MetaSave preset that will hold the runtime info of this new save (so it shows up as something we can overwrite later this same runtime) + MetaSave newSave; + // This will automatically set all internal members to represent what MetaMan's current state is + newSave.Create(fullSavePath); + newSave.SetPresetName(saveName); + + // Now add or update the actual Preset + g_PresetMan.AddEntityPreset(&newSave, g_PresetMan.GetModuleID(METASAVEMODULENAME), true, std::string(METASAVEPATH) + "Index.ini"); + + // Now write out the index file of all MetaSaves so the new save is found on next runtime + Writer indexWriter((std::string(METASAVEPATH) + "Index.ini").c_str()); + indexWriter.ObjectStart("DataModule"); + indexWriter.NewPropertyWithValue("ModuleName", "Metagame Saves"); + // Get the current list of all MetaSave Preset:s, including the new one we just saved + std::list saveList; + g_PresetMan.GetAllOfType(saveList, "MetaSave"); + // Go through the list and add their names to the combo box + for (const Entity* saveListEntry: saveList) { + indexWriter.NewPropertyWithValue("AddMetaSave", saveListEntry); + } + indexWriter.ObjectEnd(); + // Report to console + g_ConsoleMan.PrintString("Successfully saved Metagame '" + saveName + "' to " + savePath); + + return true; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: SaveGameFromDialog @@ -1307,38 +1232,34 @@ bool MetagameGUI::SaveGame(std::string saveName, std::string savePath, bool resa // Description: Attempts to save a Metagame to disk using the settings set in the // Save Game dialog box. -bool MetagameGUI::SaveGameFromDialog() -{ - std::string saveName; - std::string savePath; - - // Determine whether we have a new save file or one chosen from the list to overwrite - // User wrote in a new save file/folder to use, so go ahead and create it - if (!m_NewSaveBox->GetText().empty()) - { - saveName = m_NewSaveBox->GetText(); - savePath = METASAVEPATH + m_NewSaveBox->GetText() + ".ini"; - } - // A game was selected to be overwritten, so extract its path and save to it - else if (m_pSavesToOverwriteCombo->GetSelectedItem()) - { - saveName = m_pSavesToOverwriteCombo->GetSelectedItem()->m_Name; - const MetaSave *pSave = dynamic_cast(m_pSavesToOverwriteCombo->GetSelectedItem()->m_pEntity); - if (pSave) - savePath = pSave->GetSavePath(); - // Fall back on just doing a new save with that name - else - savePath = METASAVEPATH + m_NewSaveBox->GetText() + ".ini"; - } - // Coulnd't find a valid name selected by the player?? (GUI problem) - else - { - g_ConsoleMan.PrintString("ERROR: Could not save Metagame, because no valid save name was specified?"); - return false; - } - - // First load all bitmap data of all Scenes in the current Metagame that have once saved em, so we can re-save them to the new files - return SaveGame(saveName, savePath, true); +bool MetagameGUI::SaveGameFromDialog() { + std::string saveName; + std::string savePath; + + // Determine whether we have a new save file or one chosen from the list to overwrite + // User wrote in a new save file/folder to use, so go ahead and create it + if (!m_NewSaveBox->GetText().empty()) { + saveName = m_NewSaveBox->GetText(); + savePath = METASAVEPATH + m_NewSaveBox->GetText() + ".ini"; + } + // A game was selected to be overwritten, so extract its path and save to it + else if (m_pSavesToOverwriteCombo->GetSelectedItem()) { + saveName = m_pSavesToOverwriteCombo->GetSelectedItem()->m_Name; + const MetaSave* pSave = dynamic_cast(m_pSavesToOverwriteCombo->GetSelectedItem()->m_pEntity); + if (pSave) + savePath = pSave->GetSavePath(); + // Fall back on just doing a new save with that name + else + savePath = METASAVEPATH + m_NewSaveBox->GetText() + ".ini"; + } + // Coulnd't find a valid name selected by the player?? (GUI problem) + else { + g_ConsoleMan.PrintString("ERROR: Could not save Metagame, because no valid save name was specified?"); + return false; + } + + // First load all bitmap data of all Scenes in the current Metagame that have once saved em, so we can re-save them to the new files + return SaveGame(saveName, savePath, true); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1346,107 +1267,98 @@ bool MetagameGUI::SaveGameFromDialog() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the state of this Menu each frame -void MetagameGUI::Update() -{ - // Update the input controller - m_pController->Update(); - - // ToolTip box is hidden by default - m_pToolTipBox->SetVisible(false); - - // Handle recovering from a completed activity - if (m_ActivityRestarted || m_ActivityResumed) - CompletedActivity(); - - // Reset the specific triggers - m_ContinuePhase = false; - m_ActivityRestarted = false; - m_ActivityResumed = false; - m_StartDifficulty = 0; - m_BackToMain = false; - m_Quit = false; - - // Don't update the menu if the console is open - if (g_ConsoleMan.IsEnabled()) - return; - - // Quit now if we aren't enabled - if (m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) - return; - - //////////////////////////////////////////// - // Do all input handling! - - int mouseX, mouseY; - m_pGUIInput->GetMousePosition(&mouseX, &mouseY); - Vector mousePos(mouseX, mouseY); - - UpdateInput(); - -/* - //////////////////////////////////////////// - // Notification blinking logic - - if (m_BlinkMode == NOFUNDS) - { - m_pCostLabel->SetVisible((m_BlinkTimer.GetElapsedRealTimeMS() % 500) > 250); - } - else if (m_BlinkMode == NOCRAFT) - { - bool blink = (m_BlinkTimer.GetElapsedRealTimeMS() % 500) > 250; - m_pCraftLabel->SetVisible(blink); - m_pCraftBox->SetVisible(blink); - } - - // Time out the blinker - if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastRealMS(1500)) - { - m_pCostLabel->SetVisible(true); - m_pCraftLabel->SetVisible(true); - m_pCraftBox->SetVisible(true); - m_BlinkMode = NOBLINK; - } -*/ - ////////////////////////////////////////////////////////////// - // Update screens - - if (m_MenuScreen == NEWDIALOG) - { - if (m_ScreenChange) - { - m_ScreenChange = false; - } - } - else if (m_MenuScreen == LOADDIALOG) - { - if (m_ScreenChange) - { - // Clear out the loadable save list control so we can repopulate it - m_pSavesToLoadCombo->ClearList(); - // Get the list of all read in MetaSave:s - std::list saveList; - g_PresetMan.GetAllOfType(saveList, "MetaSave"); - // Go through the list and add their names to the combo box - int i = 0; - int autoSaveIndex = -1; - MetaSave *pAutoSave = 0; - for (std::list::iterator itr = saveList.begin(); itr != saveList.end(); ++itr) - { - m_pSavesToLoadCombo->AddItem((*itr)->GetPresetName(), "", 0, *itr); - // Take note of the autosave, if it is in here - if ((*itr)->GetPresetName() == AUTOSAVENAME) - { - autoSaveIndex = i; - pAutoSave = dynamic_cast(*itr); - } - ++i; - } - // Select the autosave index if we found it - if (autoSaveIndex >= 0 && pAutoSave) - { - m_pSavesToLoadCombo->SetSelectedIndex(autoSaveIndex); - // Update the game stats info box with the info of the autosave - char info[512]; +void MetagameGUI::Update() { + // Update the input controller + m_pController->Update(); + + // ToolTip box is hidden by default + m_pToolTipBox->SetVisible(false); + + // Handle recovering from a completed activity + if (m_ActivityRestarted || m_ActivityResumed) + CompletedActivity(); + + // Reset the specific triggers + m_ContinuePhase = false; + m_ActivityRestarted = false; + m_ActivityResumed = false; + m_StartDifficulty = 0; + m_BackToMain = false; + m_Quit = false; + + // Don't update the menu if the console is open + if (g_ConsoleMan.IsEnabled()) + return; + + // Quit now if we aren't enabled + if (m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) + return; + + //////////////////////////////////////////// + // Do all input handling! + + int mouseX, mouseY; + m_pGUIInput->GetMousePosition(&mouseX, &mouseY); + Vector mousePos(mouseX, mouseY); + + UpdateInput(); + + /* + //////////////////////////////////////////// + // Notification blinking logic + + if (m_BlinkMode == NOFUNDS) + { + m_pCostLabel->SetVisible((m_BlinkTimer.GetElapsedRealTimeMS() % 500) > 250); + } + else if (m_BlinkMode == NOCRAFT) + { + bool blink = (m_BlinkTimer.GetElapsedRealTimeMS() % 500) > 250; + m_pCraftLabel->SetVisible(blink); + m_pCraftBox->SetVisible(blink); + } + + // Time out the blinker + if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastRealMS(1500)) + { + m_pCostLabel->SetVisible(true); + m_pCraftLabel->SetVisible(true); + m_pCraftBox->SetVisible(true); + m_BlinkMode = NOBLINK; + } + */ + ////////////////////////////////////////////////////////////// + // Update screens + + if (m_MenuScreen == NEWDIALOG) { + if (m_ScreenChange) { + m_ScreenChange = false; + } + } else if (m_MenuScreen == LOADDIALOG) { + if (m_ScreenChange) { + // Clear out the loadable save list control so we can repopulate it + m_pSavesToLoadCombo->ClearList(); + // Get the list of all read in MetaSave:s + std::list saveList; + g_PresetMan.GetAllOfType(saveList, "MetaSave"); + // Go through the list and add their names to the combo box + int i = 0; + int autoSaveIndex = -1; + MetaSave* pAutoSave = 0; + for (std::list::iterator itr = saveList.begin(); itr != saveList.end(); ++itr) { + m_pSavesToLoadCombo->AddItem((*itr)->GetPresetName(), "", 0, *itr); + // Take note of the autosave, if it is in here + if ((*itr)->GetPresetName() == AUTOSAVENAME) { + autoSaveIndex = i; + pAutoSave = dynamic_cast(*itr); + } + ++i; + } + // Select the autosave index if we found it + if (autoSaveIndex >= 0 && pAutoSave) { + m_pSavesToLoadCombo->SetSelectedIndex(autoSaveIndex); + // Update the game stats info box with the info of the autosave + char info[512]; std::string difficultyString; if (pAutoSave->GetDifficulty() < Activity::CakeDifficulty) @@ -1463,614 +1375,533 @@ void MetagameGUI::Update() difficultyString = "Difficulty: Nuts!"; std::snprintf(info, sizeof(info), "Game Size: %d sites\nTotal Players: %d\nDay: %d\n%s", pAutoSave->GetSiteCount(), pAutoSave->GetPlayerCount(), pAutoSave->GetRoundCount() + 1, difficultyString.c_str()); - m_pLoadInfoLabel->SetText(info); - // Show the Load button since we have one locked in - m_apMetaButton[LOADNOW]->SetVisible(true); - } - // Couldn't find the autosave, so leave the combobox empty and let the player select something - else - { - // Clear out the load game info box - m_pLoadInfoLabel->SetText(""); - // Hide the load button until we have a game to load selected - m_apMetaButton[LOADNOW]->SetVisible(false); - } - m_ScreenChange = false; - } - } - else if (m_MenuScreen == SAVEDIALOG) - { - if (m_ScreenChange) - { - // Clear out the new save text box - m_NewSaveBox->SetText(""); - // Clear out the overwrite save list control so we can repopulate it - m_pSavesToOverwriteCombo->ClearList(); - // Get the list of all read in MetaSave:s - std::list saveList; - g_PresetMan.GetAllOfType(saveList, "MetaSave"); - // Go through the list and add their names to the combo box - for (std::list::iterator itr = saveList.begin(); itr != saveList.end(); ++itr) - m_pSavesToOverwriteCombo->AddItem((*itr)->GetPresetName(), "", 0, *itr); - // Select the first one - don't, let the player select one if they want to overwrite -// m_pSavesToOverwriteCombo->SetSelectedIndex(0); - // Clear out the save game info box - m_pSaveInfoLabel->SetText(""); - // Hide the save button until we either have a new name or a game to overwrite selected - m_apMetaButton[SAVENOW]->SetVisible(false); - m_apMetaButton[SAVENOW]->SetText("Save"); - m_ScreenChange = false; - } - - // Something written in the new save box, so disable overwriting - if (!m_NewSaveBox->GetText().empty()) - { - m_pGUIController->GetControl("SaveOrLabel")->SetVisible(false); - m_pGUIController->GetControl("SaveAsLabel")->SetVisible(false); - m_pSavesToOverwriteCombo->SetVisible(false); - m_pSaveInfoLabel->SetVisible(false); - // Show the save button since we can now save - m_apMetaButton[SAVENOW]->SetVisible(true); - } - // Overwriting is an option - else - { - m_pGUIController->GetControl("SaveOrLabel")->SetVisible(true); - m_pGUIController->GetControl("SaveAsLabel")->SetVisible(true); - m_pSavesToOverwriteCombo->SetVisible(true); - // Hide the save button if nothing is selected in the overwrite combo box - m_apMetaButton[SAVENOW]->SetVisible(m_pSavesToOverwriteCombo->GetSelectedItem() && m_pSavesToOverwriteCombo->GetSelectedItem()->m_pEntity); - - // If the button is visible and we're overwriting something, make the player press the Save button twice to confirm overwriting a game - if (m_apMetaButton[SAVENOW]->GetVisible() && m_apMetaButton[SAVENOW]->GetText() != "Save") - { - m_apMetaButton[SAVENOW]->SetText(m_BlinkTimer.AlternateReal(333) ? "CONFIRM?" : ""); - m_pGUIController->GetControl("SaveAsLabel")->SetVisible(m_BlinkTimer.AlternateReal(333)); - } - } - } - - - ///////////////////////////////////////////////////////////// - // Update based on the MetaGame's state - - // Always show the phase box if we're in-game - if (g_MetaMan.m_GameState != MetaMan::NOGAME && !g_MetaMan.IsSuspended()) - { - m_pPhaseBox->SetVisible(true); - // Never let a game go if we have 0 players -// RTEAssert(g_MetaMan.m_Players.size() > 0, "Game in progress without any players!"); - } - - // Deselect scenes and player bars on state change - if (g_MetaMan.m_StateChanged) - { - m_pSelectedScene = 0; - m_ActivePlayerIncomeLines = -1; - // Go through all lines and make them fully visible - for (std::vector::iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) - (*slItr).m_OnlyFirstSegments = (*slItr).m_OnlyLastSegments = -1; - for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) - { - for (std::vector::iterator slItr = m_ActionSiteLines[metaPlayer].begin(); slItr != m_ActionSiteLines[metaPlayer].end(); ++slItr) - (*slItr).m_OnlyFirstSegments = (*slItr).m_OnlyLastSegments = -1; - } - } - - // Game is suspended - if (g_MetaMan.IsSuspended()) - { - // Do nothing, game is suspended while in the game menu - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - m_AnimTimer3.Reset(); - - // Show the current menu if we're not showing some confirmation box and we're not already showing the screen we're supposed to be - if (!m_pConfirmationBox->GetVisible() && !m_apScreenBox[m_MenuScreen]->GetVisible()) - SwitchToScreen(m_MenuScreen); - } - // No game yet, show the new game menu - else if (g_MetaMan.m_GameState == MetaMan::NOGAME) - { - m_pPhaseLabel->SetText("Game Not Started"); - if (m_ScreenChange) - { - SwitchToScreen(NEWDIALOG); - UpdatePlayerSetup(); - m_ScreenChange = false; - } - } - else if (g_MetaMan.m_GameState == MetaMan::GAMEINTRO) - { - m_pPhaseLabel->SetText("Game Intro"); - m_apMetaButton[CONTINUE]->SetText("Skip Intro"); - } - else if (g_MetaMan.m_GameState == MetaMan::NEWROUND) - { - if (g_MetaMan.m_StateChanged) - { - m_pBannerYellowTop->ShowText("DAY", GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 2500, 200); - m_pBannerYellowBottom->ShowText(GetRoundName(g_MetaMan.m_CurrentRound), GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 2500, 300); - } - m_pPhaseLabel->SetText("New Day"); - // Blink the start button to draw attention to it - m_apMetaButton[CONTINUE]->SetText(m_BlinkTimer.AlternateReal(333) ? "> Start <" : "Start"); - } - else if (g_MetaMan.m_GameState == MetaMan::REVEALSCENES) - { - if (g_MetaMan.m_StateChanged) - { - m_pBannerYellowTop->HideText(2500, 200); - m_pBannerYellowBottom->HideText(2500, 300); -// m_pBannerRedTop->ShowText("Sites Found", GUIBanner::FLYBYLEFTWARD, 1000, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.1, 3500, 200); - } - - m_pPhaseLabel->SetText("New Sites Found"); - m_apMetaButton[CONTINUE]->SetText("Skip"); - UpdateSiteRevealing(); - } - else if (g_MetaMan.m_GameState == MetaMan::COUNTINCOME) - { - if (g_MetaMan.m_StateChanged) - { -// m_pBannerRedBottom->ShowText("Incomes", GUIBanner::FLYBYLEFTWARD, 1000, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.2, 3500, 200); - } - m_pPhaseLabel->SetText("Counting Incomes"); - m_apMetaButton[CONTINUE]->SetText("Skip"); - UpdateIncomeCounting(); - } - else if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) - { - // Who's turn is it, anyway? - int metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; - - // Set up the pre-player-turn intermediate status IF this is a human player still in the game - if (g_MetaMan.m_Players[metaPlayer].IsHuman() && !g_MetaMan.m_Players[metaPlayer].IsGameOverByRound(g_MetaMan.m_CurrentRound)) - { - if (g_MetaMan.m_StateChanged) - { - // If this human player has no brains left anywhere, there's really nothing he can do, and it's game over for him - // Communicate it to the loser - if (g_MetaMan.GetTotalBrainCountOfPlayer(metaPlayer) <= 0) - { - m_apMetaButton[CONTINUE]->SetText("Continue"); - m_pPhaseLabel->SetText(g_MetaMan.m_Players[metaPlayer].GetName() + "'s Turn"); - m_pBannerRedTop->ShowText("Game Over", GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); - m_pBannerRedBottom->ShowText("for " + g_MetaMan.m_Players[metaPlayer].GetName() + "!", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); - - // Show a lil descriptive message as to why the game ended - m_pGameMessageLabel->SetVisible(true); - m_pGameMessageLabel->SetPositionAbs(m_pGameMessageLabel->GetXPos(), (m_apScreenBox[ROOTBOX]->GetHeight() / 2) + 110 - 16); - m_pGameMessageLabel->SetText(g_MetaMan.m_Players[metaPlayer].GetName() + "'s brains are all gone, so he/she can do nothing more. Good effort, though!"); - - // Just skip this guy's turn completely - m_PreTurn = false; - } - // Normal player turn start - else - { - m_apMetaButton[CONTINUE]->SetText("Start Turn"); - m_pPhaseLabel->SetText(g_MetaMan.m_Players[metaPlayer].GetName() + "'s Turn"); - m_pBannerRedTop->ShowText(g_MetaMan.m_Players[metaPlayer].GetName() + "'s", GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); - m_pBannerRedBottom->ShowText("Turn", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); - m_pGameMessageLabel->SetVisible(false); - m_PreTurn = true; - } - } - - // Do some more init and then cleanup later - UpdateHumanPlayerTurn(metaPlayer); - } - // Non-human player - else - { - m_pGameMessageLabel->SetVisible(false); - } - - // Before or after the turn prep start - if (m_PreTurn) - { - // Blink the start button to draw attention to it - m_apMetaButton[CONTINUE]->SetText(m_BlinkTimer.AlternateReal(333) ? "> Start Turn <" : "Start Turn"); - } - else - { - // Dead player's turn, just waiting for the continue button to be pressed - if (g_MetaMan.GetTotalBrainCountOfPlayer(metaPlayer) <= 0) - { - m_apMetaButton[CONTINUE]->SetText(m_BlinkTimer.AlternateReal(333) ? "> Continue <" : "Continue"); - UpdatePlayerActionLines(metaPlayer); - } - // Normal turn - else - { - m_apMetaButton[CONTINUE]->SetText("End Turn"); - m_pBannerRedTop->HideText(3500, 0); - m_pBannerRedBottom->HideText(3500, 0); - UpdatePlayerActionLines(metaPlayer); - } - } - } - else if (g_MetaMan.m_GameState == MetaMan::BUILDBASES) - { - // Hide any banners that might be up - if (g_MetaMan.m_StateChanged) - { - m_pBannerYellowTop->HideText(2500, 200); - m_pBannerYellowBottom->HideText(2500, 300); - m_pBannerRedTop->HideText(3500, 0); - m_pBannerRedBottom->HideText(3500, 0); - } - m_apMetaButton[CONTINUE]->SetText("Skip"); - UpdateBaseBuilding(); - } - else if (g_MetaMan.m_GameState == MetaMan::RUNACTIVITIES) - { - // Hide any banners that might be up - if (g_MetaMan.m_StateChanged) - { - m_pBannerYellowTop->HideText(2500, 200); - m_pBannerYellowBottom->HideText(2500, 300); - m_pBannerRedTop->HideText(3500, 0); - m_pBannerRedBottom->HideText(3500, 0); - } - m_apMetaButton[CONTINUE]->SetText("Start!"); - UpdateOffensives(); - } - else if (g_MetaMan.m_GameState == MetaMan::ENDROUND) - { - m_pPhaseLabel->SetText("End of Day"); - m_apMetaButton[CONTINUE]->SetText("Continue"); - } - else if (g_MetaMan.m_GameState == MetaMan::GAMEOVER) - { - if (g_MetaMan.m_StateChanged) - { - m_pPhaseLabel->SetText("Game Over"); - m_apMetaButton[CONTINUE]->SetText("Good Game!"); - // Which team has the most bases? - int winnerTeam = g_MetaMan.WhichTeamIsLeading(); - - // Noone left?? - if (winnerTeam == Activity::NoTeam) - { - m_pBannerRedTop->ShowText("EVERYONE", GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); - m_pBannerYellowBottom->ShowText("-DIED-", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); - } - else - { - - // Find out who was on this winning team so we can name them by name - std::string winnerNames = ""; - bool plural = false; - for (std::vector::iterator pItr = g_MetaMan.m_Players.begin(); pItr != g_MetaMan.m_Players.end(); ++pItr) - { - // WINRAR - if ((*pItr).GetTeam() == winnerTeam) - { - // There's now more than one name in there - if (!winnerNames.empty()) - plural = true; - - winnerNames = winnerNames + (winnerNames.empty() ? "" : " and ") + (*pItr).GetName(); - } - } - m_pBannerRedTop->ShowText(winnerNames, GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); - m_pBannerYellowBottom->ShowText(plural ? "WIN!" : "WINS!", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); -// char winStr[256]; -// std::snprintf(winStr, sizeof(winStr), "Team %d", winner + 1); -// m_pBannerRedTop->ShowText(winStr, GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); -// m_pBannerYellowBottom->ShowText("WINS!", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); - } - } - - // Show that the game is over because noone has any brains left to do anyhting with - for (int metaPlayer = 0; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - m_apBrainPoolLabel[metaPlayer]->SetVisible(m_AnimTimer2.AlternateReal(333)); - - // Show a lil descriptive message as to why the game ended - m_pGameMessageLabel->SetVisible(true); - m_pGameMessageLabel->SetPositionAbs(m_pGameMessageLabel->GetXPos(), (m_apScreenBox[ROOTBOX]->GetHeight() / 2) + 110 - 16); - if (g_MetaMan.NoBrainsLeftInAnyPool()) - m_pGameMessageLabel->SetText("All players' brains have been deployed, and so the team with the most\nowned sites (and if tied, the most gold) won the mining contract for this planet!"); - else - m_pGameMessageLabel->SetText("The team that is in the lead (in owned sites - or gold, if tied)\nand has brains left to deploy has won the mining contract for this planet!"); - } - - // Update site change animations independent of the phase/mode.. they can happen here and there - UpdateSiteChangeAnim(); - - // SCENE SELECTION LOGIC - if (!g_MetaMan.IsSuspended() && !m_PreTurn) - { - if (m_ScreenChange) - { -// m_apScreenBox[SCENESCREEN]->SetVisible(true); -// m_apMetaButton[BACKTOMAIN]->SetVisible(true); - m_ScreenChange = false; - } - - Vector screenLocation; - - // Make any player box which is floated over with cursor the one to show its sitelines - if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) - { - GUICollectionBox *pBox = dynamic_cast(m_pGUIController->GetControlUnderPoint(mouseX, mouseY, m_apScreenBox[ROOTBOX], 1)); - bool found = false; - for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) - { - // Find hovered box - if (pBox == m_apPlayerBox[metaPlayer]) - { - m_ActivePlayerIncomeLines = metaPlayer; - found = true; - } - } - // Not hovering over any player box, so don't show any lines - if (!found) - m_ActivePlayerIncomeLines = Players::NoPlayer; - } - - // Validate mouse position as being over the planet area for hover operations! - if (!m_pDraggedBox && (mousePos - m_PlanetCenter).MagnitudeIsLessThan(m_PlanetRadius)) - { - // If unlocked, detect any Scene close to the mouse and highlight it - bool foundAnyHover = false; - bool foundNewHover = false; - std::vector::iterator sItr; - std::vector::iterator newCandidateItr = g_MetaMan.m_Scenes.end(); - - float sqrShortestDist = std::numeric_limits::infinity(); - for (sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Only mess with Scenes we can see - if (!(*sItr)->IsRevealed()) - continue; - - screenLocation = m_PlanetCenter + (*sItr)->GetLocation() + (*sItr)->GetLocationOffset(); + m_pLoadInfoLabel->SetText(info); + // Show the Load button since we have one locked in + m_apMetaButton[LOADNOW]->SetVisible(true); + } + // Couldn't find the autosave, so leave the combobox empty and let the player select something + else { + // Clear out the load game info box + m_pLoadInfoLabel->SetText(""); + // Hide the load button until we have a game to load selected + m_apMetaButton[LOADNOW]->SetVisible(false); + } + m_ScreenChange = false; + } + } else if (m_MenuScreen == SAVEDIALOG) { + if (m_ScreenChange) { + // Clear out the new save text box + m_NewSaveBox->SetText(""); + // Clear out the overwrite save list control so we can repopulate it + m_pSavesToOverwriteCombo->ClearList(); + // Get the list of all read in MetaSave:s + std::list saveList; + g_PresetMan.GetAllOfType(saveList, "MetaSave"); + // Go through the list and add their names to the combo box + for (std::list::iterator itr = saveList.begin(); itr != saveList.end(); ++itr) + m_pSavesToOverwriteCombo->AddItem((*itr)->GetPresetName(), "", 0, *itr); + // Select the first one - don't, let the player select one if they want to overwrite + // m_pSavesToOverwriteCombo->SetSelectedIndex(0); + // Clear out the save game info box + m_pSaveInfoLabel->SetText(""); + // Hide the save button until we either have a new name or a game to overwrite selected + m_apMetaButton[SAVENOW]->SetVisible(false); + m_apMetaButton[SAVENOW]->SetText("Save"); + m_ScreenChange = false; + } + + // Something written in the new save box, so disable overwriting + if (!m_NewSaveBox->GetText().empty()) { + m_pGUIController->GetControl("SaveOrLabel")->SetVisible(false); + m_pGUIController->GetControl("SaveAsLabel")->SetVisible(false); + m_pSavesToOverwriteCombo->SetVisible(false); + m_pSaveInfoLabel->SetVisible(false); + // Show the save button since we can now save + m_apMetaButton[SAVENOW]->SetVisible(true); + } + // Overwriting is an option + else { + m_pGUIController->GetControl("SaveOrLabel")->SetVisible(true); + m_pGUIController->GetControl("SaveAsLabel")->SetVisible(true); + m_pSavesToOverwriteCombo->SetVisible(true); + // Hide the save button if nothing is selected in the overwrite combo box + m_apMetaButton[SAVENOW]->SetVisible(m_pSavesToOverwriteCombo->GetSelectedItem() && m_pSavesToOverwriteCombo->GetSelectedItem()->m_pEntity); + + // If the button is visible and we're overwriting something, make the player press the Save button twice to confirm overwriting a game + if (m_apMetaButton[SAVENOW]->GetVisible() && m_apMetaButton[SAVENOW]->GetText() != "Save") { + m_apMetaButton[SAVENOW]->SetText(m_BlinkTimer.AlternateReal(333) ? "CONFIRM?" : ""); + m_pGUIController->GetControl("SaveAsLabel")->SetVisible(m_BlinkTimer.AlternateReal(333)); + } + } + } + + ///////////////////////////////////////////////////////////// + // Update based on the MetaGame's state + + // Always show the phase box if we're in-game + if (g_MetaMan.m_GameState != MetaMan::NOGAME && !g_MetaMan.IsSuspended()) { + m_pPhaseBox->SetVisible(true); + // Never let a game go if we have 0 players + // RTEAssert(g_MetaMan.m_Players.size() > 0, "Game in progress without any players!"); + } + + // Deselect scenes and player bars on state change + if (g_MetaMan.m_StateChanged) { + m_pSelectedScene = 0; + m_ActivePlayerIncomeLines = -1; + // Go through all lines and make them fully visible + for (std::vector::iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) + (*slItr).m_OnlyFirstSegments = (*slItr).m_OnlyLastSegments = -1; + for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) { + for (std::vector::iterator slItr = m_ActionSiteLines[metaPlayer].begin(); slItr != m_ActionSiteLines[metaPlayer].end(); ++slItr) + (*slItr).m_OnlyFirstSegments = (*slItr).m_OnlyLastSegments = -1; + } + } + + // Game is suspended + if (g_MetaMan.IsSuspended()) { + // Do nothing, game is suspended while in the game menu + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + m_AnimTimer3.Reset(); + + // Show the current menu if we're not showing some confirmation box and we're not already showing the screen we're supposed to be + if (!m_pConfirmationBox->GetVisible() && !m_apScreenBox[m_MenuScreen]->GetVisible()) + SwitchToScreen(m_MenuScreen); + } + // No game yet, show the new game menu + else if (g_MetaMan.m_GameState == MetaMan::NOGAME) { + m_pPhaseLabel->SetText("Game Not Started"); + if (m_ScreenChange) { + SwitchToScreen(NEWDIALOG); + UpdatePlayerSetup(); + m_ScreenChange = false; + } + } else if (g_MetaMan.m_GameState == MetaMan::GAMEINTRO) { + m_pPhaseLabel->SetText("Game Intro"); + m_apMetaButton[CONTINUE]->SetText("Skip Intro"); + } else if (g_MetaMan.m_GameState == MetaMan::NEWROUND) { + if (g_MetaMan.m_StateChanged) { + m_pBannerYellowTop->ShowText("DAY", GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 2500, 200); + m_pBannerYellowBottom->ShowText(GetRoundName(g_MetaMan.m_CurrentRound), GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 2500, 300); + } + m_pPhaseLabel->SetText("New Day"); + // Blink the start button to draw attention to it + m_apMetaButton[CONTINUE]->SetText(m_BlinkTimer.AlternateReal(333) ? "> Start <" : "Start"); + } else if (g_MetaMan.m_GameState == MetaMan::REVEALSCENES) { + if (g_MetaMan.m_StateChanged) { + m_pBannerYellowTop->HideText(2500, 200); + m_pBannerYellowBottom->HideText(2500, 300); + // m_pBannerRedTop->ShowText("Sites Found", GUIBanner::FLYBYLEFTWARD, 1000, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.1, 3500, 200); + } + + m_pPhaseLabel->SetText("New Sites Found"); + m_apMetaButton[CONTINUE]->SetText("Skip"); + UpdateSiteRevealing(); + } else if (g_MetaMan.m_GameState == MetaMan::COUNTINCOME) { + if (g_MetaMan.m_StateChanged) { + // m_pBannerRedBottom->ShowText("Incomes", GUIBanner::FLYBYLEFTWARD, 1000, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.2, 3500, 200); + } + m_pPhaseLabel->SetText("Counting Incomes"); + m_apMetaButton[CONTINUE]->SetText("Skip"); + UpdateIncomeCounting(); + } else if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) { + // Who's turn is it, anyway? + int metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; + + // Set up the pre-player-turn intermediate status IF this is a human player still in the game + if (g_MetaMan.m_Players[metaPlayer].IsHuman() && !g_MetaMan.m_Players[metaPlayer].IsGameOverByRound(g_MetaMan.m_CurrentRound)) { + if (g_MetaMan.m_StateChanged) { + // If this human player has no brains left anywhere, there's really nothing he can do, and it's game over for him + // Communicate it to the loser + if (g_MetaMan.GetTotalBrainCountOfPlayer(metaPlayer) <= 0) { + m_apMetaButton[CONTINUE]->SetText("Continue"); + m_pPhaseLabel->SetText(g_MetaMan.m_Players[metaPlayer].GetName() + "'s Turn"); + m_pBannerRedTop->ShowText("Game Over", GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); + m_pBannerRedBottom->ShowText("for " + g_MetaMan.m_Players[metaPlayer].GetName() + "!", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); + + // Show a lil descriptive message as to why the game ended + m_pGameMessageLabel->SetVisible(true); + m_pGameMessageLabel->SetPositionAbs(m_pGameMessageLabel->GetXPos(), (m_apScreenBox[ROOTBOX]->GetHeight() / 2) + 110 - 16); + m_pGameMessageLabel->SetText(g_MetaMan.m_Players[metaPlayer].GetName() + "'s brains are all gone, so he/she can do nothing more. Good effort, though!"); + + // Just skip this guy's turn completely + m_PreTurn = false; + } + // Normal player turn start + else { + m_apMetaButton[CONTINUE]->SetText("Start Turn"); + m_pPhaseLabel->SetText(g_MetaMan.m_Players[metaPlayer].GetName() + "'s Turn"); + m_pBannerRedTop->ShowText(g_MetaMan.m_Players[metaPlayer].GetName() + "'s", GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); + m_pBannerRedBottom->ShowText("Turn", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); + m_pGameMessageLabel->SetVisible(false); + m_PreTurn = true; + } + } + + // Do some more init and then cleanup later + UpdateHumanPlayerTurn(metaPlayer); + } + // Non-human player + else { + m_pGameMessageLabel->SetVisible(false); + } + + // Before or after the turn prep start + if (m_PreTurn) { + // Blink the start button to draw attention to it + m_apMetaButton[CONTINUE]->SetText(m_BlinkTimer.AlternateReal(333) ? "> Start Turn <" : "Start Turn"); + } else { + // Dead player's turn, just waiting for the continue button to be pressed + if (g_MetaMan.GetTotalBrainCountOfPlayer(metaPlayer) <= 0) { + m_apMetaButton[CONTINUE]->SetText(m_BlinkTimer.AlternateReal(333) ? "> Continue <" : "Continue"); + UpdatePlayerActionLines(metaPlayer); + } + // Normal turn + else { + m_apMetaButton[CONTINUE]->SetText("End Turn"); + m_pBannerRedTop->HideText(3500, 0); + m_pBannerRedBottom->HideText(3500, 0); + UpdatePlayerActionLines(metaPlayer); + } + } + } else if (g_MetaMan.m_GameState == MetaMan::BUILDBASES) { + // Hide any banners that might be up + if (g_MetaMan.m_StateChanged) { + m_pBannerYellowTop->HideText(2500, 200); + m_pBannerYellowBottom->HideText(2500, 300); + m_pBannerRedTop->HideText(3500, 0); + m_pBannerRedBottom->HideText(3500, 0); + } + m_apMetaButton[CONTINUE]->SetText("Skip"); + UpdateBaseBuilding(); + } else if (g_MetaMan.m_GameState == MetaMan::RUNACTIVITIES) { + // Hide any banners that might be up + if (g_MetaMan.m_StateChanged) { + m_pBannerYellowTop->HideText(2500, 200); + m_pBannerYellowBottom->HideText(2500, 300); + m_pBannerRedTop->HideText(3500, 0); + m_pBannerRedBottom->HideText(3500, 0); + } + m_apMetaButton[CONTINUE]->SetText("Start!"); + UpdateOffensives(); + } else if (g_MetaMan.m_GameState == MetaMan::ENDROUND) { + m_pPhaseLabel->SetText("End of Day"); + m_apMetaButton[CONTINUE]->SetText("Continue"); + } else if (g_MetaMan.m_GameState == MetaMan::GAMEOVER) { + if (g_MetaMan.m_StateChanged) { + m_pPhaseLabel->SetText("Game Over"); + m_apMetaButton[CONTINUE]->SetText("Good Game!"); + // Which team has the most bases? + int winnerTeam = g_MetaMan.WhichTeamIsLeading(); + + // Noone left?? + if (winnerTeam == Activity::NoTeam) { + m_pBannerRedTop->ShowText("EVERYONE", GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); + m_pBannerYellowBottom->ShowText("-DIED-", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); + } else { + + // Find out who was on this winning team so we can name them by name + std::string winnerNames = ""; + bool plural = false; + for (std::vector::iterator pItr = g_MetaMan.m_Players.begin(); pItr != g_MetaMan.m_Players.end(); ++pItr) { + // WINRAR + if ((*pItr).GetTeam() == winnerTeam) { + // There's now more than one name in there + if (!winnerNames.empty()) + plural = true; + + winnerNames = winnerNames + (winnerNames.empty() ? "" : " and ") + (*pItr).GetName(); + } + } + m_pBannerRedTop->ShowText(winnerNames, GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); + m_pBannerYellowBottom->ShowText(plural ? "WIN!" : "WINS!", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); + // char winStr[256]; + // std::snprintf(winStr, sizeof(winStr), "Team %d", winner + 1); + // m_pBannerRedTop->ShowText(winStr, GUIBanner::FLYBYLEFTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.4, 3500, 0); + // m_pBannerYellowBottom->ShowText("WINS!", GUIBanner::FLYBYRIGHTWARD, -1, Vector(m_RootBoxMaxWidth, g_WindowMan.GetResY()), 0.6, 3500, 0); + } + } + + // Show that the game is over because noone has any brains left to do anyhting with + for (int metaPlayer = 0; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) + m_apBrainPoolLabel[metaPlayer]->SetVisible(m_AnimTimer2.AlternateReal(333)); + + // Show a lil descriptive message as to why the game ended + m_pGameMessageLabel->SetVisible(true); + m_pGameMessageLabel->SetPositionAbs(m_pGameMessageLabel->GetXPos(), (m_apScreenBox[ROOTBOX]->GetHeight() / 2) + 110 - 16); + if (g_MetaMan.NoBrainsLeftInAnyPool()) + m_pGameMessageLabel->SetText("All players' brains have been deployed, and so the team with the most\nowned sites (and if tied, the most gold) won the mining contract for this planet!"); + else + m_pGameMessageLabel->SetText("The team that is in the lead (in owned sites - or gold, if tied)\nand has brains left to deploy has won the mining contract for this planet!"); + } + + // Update site change animations independent of the phase/mode.. they can happen here and there + UpdateSiteChangeAnim(); + + // SCENE SELECTION LOGIC + if (!g_MetaMan.IsSuspended() && !m_PreTurn) { + if (m_ScreenChange) { + // m_apScreenBox[SCENESCREEN]->SetVisible(true); + // m_apMetaButton[BACKTOMAIN]->SetVisible(true); + m_ScreenChange = false; + } + + Vector screenLocation; + + // Make any player box which is floated over with cursor the one to show its sitelines + if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) { + GUICollectionBox* pBox = dynamic_cast(m_pGUIController->GetControlUnderPoint(mouseX, mouseY, m_apScreenBox[ROOTBOX], 1)); + bool found = false; + for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) { + // Find hovered box + if (pBox == m_apPlayerBox[metaPlayer]) { + m_ActivePlayerIncomeLines = metaPlayer; + found = true; + } + } + // Not hovering over any player box, so don't show any lines + if (!found) + m_ActivePlayerIncomeLines = Players::NoPlayer; + } + + // Validate mouse position as being over the planet area for hover operations! + if (!m_pDraggedBox && (mousePos - m_PlanetCenter).MagnitudeIsLessThan(m_PlanetRadius)) { + // If unlocked, detect any Scene close to the mouse and highlight it + bool foundAnyHover = false; + bool foundNewHover = false; + std::vector::iterator sItr; + std::vector::iterator newCandidateItr = g_MetaMan.m_Scenes.end(); + + float sqrShortestDist = std::numeric_limits::infinity(); + for (sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Only mess with Scenes we can see + if (!(*sItr)->IsRevealed()) + continue; + + screenLocation = m_PlanetCenter + (*sItr)->GetLocation() + (*sItr)->GetLocationOffset(); if (m_pSceneInfoPopup->GetVisible() && m_pSceneInfoPopup->PointInside(screenLocation.GetRoundIntX(), screenLocation.GetRoundIntY())) { continue; } - float sqrDistance = (screenLocation - mousePos).GetSqrMagnitude(); - - // The first new scene the mouse's position is close to when unlocked, make selected - if (sqrDistance < (16.0F * 16.0F) && sqrDistance < sqrShortestDist) - { - // This is now the shortest - sqrShortestDist = sqrDistance; - foundAnyHover = true; - // See if the scene hovered is different from the previously hovered one, and if so, set it to the new candidate to switch hovering to -// Actually, don't because it will cause alternating each frame if two hover zones overlap! -// if (*sItr != m_pHoveredScene) - newCandidateItr = sItr; - } - } - - // Set new hovered scene to be the one now closest to the cursor, if there is any and if it is different the a currently hovered one - if (newCandidateItr != g_MetaMan.m_Scenes.end() && (*newCandidateItr) != m_pHoveredScene) - { - m_pHoveredScene = (*newCandidateItr); - foundNewHover = true; - g_GUISound.SelectionChangeSound()->Play(); - } - - // If we didn't find anything to hover over, then remove the hover status - if (!foundAnyHover) - m_pHoveredScene = 0; - - // Set up the hover label to appear over any hovered scene location - if (m_pHoveredScene) - UpdateSiteNameLabel(true, m_pHoveredScene->GetPresetName(), m_pHoveredScene->GetLocation() + m_pHoveredScene->GetLocationOffset()); - else if (g_MetaMan.m_GameState != MetaMan::COUNTINCOME && g_MetaMan.m_GameState != MetaMan::BUILDBASES && g_MetaMan.m_GameState != MetaMan::RUNACTIVITIES) - UpdateSiteNameLabel(false); - - // If clicked, whatever is hovered becomes selected - if (g_UInputMan.MenuButtonPressed(UInputMan::MENU_EITHER)) - { - if (m_pHoveredScene) - { - SelectScene(m_pHoveredScene); - g_GUISound.ItemChangeSound()->Play(); - } -/* Can't do this, doesn't take into account clicks on floating UI boxes - // Not hovering over anything on click, so deselect whatever was selected - else if (m_pSelectedScene) - { - m_pSelectedScene = 0; - g_GUISound.FocusChangeSound()->Play(); - } -*/ - } - } - } - - // Update the scene info/action box - UpdateScenesBox(); - - // Update the site lines, if neccessary - for (int metaPlayer = 0; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - { - // The tradestar is a moving target + float sqrDistance = (screenLocation - mousePos).GetSqrMagnitude(); + + // The first new scene the mouse's position is close to when unlocked, make selected + if (sqrDistance < (16.0F * 16.0F) && sqrDistance < sqrShortestDist) { + // This is now the shortest + sqrShortestDist = sqrDistance; + foundAnyHover = true; + // See if the scene hovered is different from the previously hovered one, and if so, set it to the new candidate to switch hovering to + // Actually, don't because it will cause alternating each frame if two hover zones overlap! + // if (*sItr != m_pHoveredScene) + newCandidateItr = sItr; + } + } + + // Set new hovered scene to be the one now closest to the cursor, if there is any and if it is different the a currently hovered one + if (newCandidateItr != g_MetaMan.m_Scenes.end() && (*newCandidateItr) != m_pHoveredScene) { + m_pHoveredScene = (*newCandidateItr); + foundNewHover = true; + g_GUISound.SelectionChangeSound()->Play(); + } + + // If we didn't find anything to hover over, then remove the hover status + if (!foundAnyHover) + m_pHoveredScene = 0; + + // Set up the hover label to appear over any hovered scene location + if (m_pHoveredScene) + UpdateSiteNameLabel(true, m_pHoveredScene->GetPresetName(), m_pHoveredScene->GetLocation() + m_pHoveredScene->GetLocationOffset()); + else if (g_MetaMan.m_GameState != MetaMan::COUNTINCOME && g_MetaMan.m_GameState != MetaMan::BUILDBASES && g_MetaMan.m_GameState != MetaMan::RUNACTIVITIES) + UpdateSiteNameLabel(false); + + // If clicked, whatever is hovered becomes selected + if (g_UInputMan.MenuButtonPressed(UInputMan::MENU_EITHER)) { + if (m_pHoveredScene) { + SelectScene(m_pHoveredScene); + g_GUISound.ItemChangeSound()->Play(); + } + /* Can't do this, doesn't take into account clicks on floating UI boxes + // Not hovering over anything on click, so deselect whatever was selected + else if (m_pSelectedScene) + { + m_pSelectedScene = 0; + g_GUISound.FocusChangeSound()->Play(); + } + */ + } + } + } + + // Update the scene info/action box + UpdateScenesBox(); + + // Update the site lines, if neccessary + for (int metaPlayer = 0; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) { + // The tradestar is a moving target if (m_aStationIncomeLineIndices[metaPlayer] >= 0 && !m_IncomeSiteLines.empty()) m_IncomeSiteLines[m_aStationIncomeLineIndices[metaPlayer]].m_PlanetPoint = m_StationPosOnOrbit; - // The brain pool counters might also be if player moves the player bar - if (m_aBrainSaleIncomeLineIndices[metaPlayer] >= 0 && !m_IncomeSiteLines.empty()) - { - m_IncomeSiteLines[m_aBrainSaleIncomeLineIndices[metaPlayer]].m_CircleSize = 1.0; - m_IncomeSiteLines[m_aBrainSaleIncomeLineIndices[metaPlayer]].m_PlanetPoint.SetXY(m_apBrainPoolLabel[metaPlayer]->GetXPos() + (m_apBrainPoolLabel[metaPlayer]->GetHAlignment() == GUIFont::Left ? 5 : 16), m_apBrainPoolLabel[metaPlayer]->GetYPos() + 9); - // Compensate for the fact that we're tracking a screen UI element and not a planetary site - m_IncomeSiteLines[m_aBrainSaleIncomeLineIndices[metaPlayer]].m_PlanetPoint -= m_PlanetCenter; - } - } - - // Update the GUI Banners - m_pBannerRedTop->Update(); - m_pBannerRedBottom->Update(); - m_pBannerYellowTop->Update(); - m_pBannerYellowBottom->Update(); - - // Update the floating Player bars - UpdatePlayerBars(); - - // Save mouse pos for next frame so we can do dragging - if (m_EngageDrag) - m_PrevMousePos = mousePos; -} + // The brain pool counters might also be if player moves the player bar + if (m_aBrainSaleIncomeLineIndices[metaPlayer] >= 0 && !m_IncomeSiteLines.empty()) { + m_IncomeSiteLines[m_aBrainSaleIncomeLineIndices[metaPlayer]].m_CircleSize = 1.0; + m_IncomeSiteLines[m_aBrainSaleIncomeLineIndices[metaPlayer]].m_PlanetPoint.SetXY(m_apBrainPoolLabel[metaPlayer]->GetXPos() + (m_apBrainPoolLabel[metaPlayer]->GetHAlignment() == GUIFont::Left ? 5 : 16), m_apBrainPoolLabel[metaPlayer]->GetYPos() + 9); + // Compensate for the fact that we're tracking a screen UI element and not a planetary site + m_IncomeSiteLines[m_aBrainSaleIncomeLineIndices[metaPlayer]].m_PlanetPoint -= m_PlanetCenter; + } + } + // Update the GUI Banners + m_pBannerRedTop->Update(); + m_pBannerRedBottom->Update(); + m_pBannerYellowTop->Update(); + m_pBannerYellowBottom->Update(); + + // Update the floating Player bars + UpdatePlayerBars(); + + // Save mouse pos for next frame so we can do dragging + if (m_EngageDrag) + m_PrevMousePos = mousePos; +} ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws the menu -void MetagameGUI::Draw(BITMAP *drawBitmap) -{ - // Don't draw site lines and dots if we're in the menus - if (!g_MetaMan.IsSuspended()) - { - // Transparency effect on the scene dots and lines - drawing_mode(DRAW_MODE_TRANS, 0, 0, 0); - // Screen blend the dots and lines, with some flickering in its intensity +void MetagameGUI::Draw(BITMAP* drawBitmap) { + // Don't draw site lines and dots if we're in the menus + if (!g_MetaMan.IsSuspended()) { + // Transparency effect on the scene dots and lines + drawing_mode(DRAW_MODE_TRANS, 0, 0, 0); + // Screen blend the dots and lines, with some flickering in its intensity int blendAmount = 130 + RandomNum(-45, 45); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - - // Draw the scene location dots - Vector screenLocation; - BITMAP *pIcon = 0; - for (std::vector::const_iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Only draw Scenes that are revealed yet - if (!(*sItr)->IsRevealed()) - continue; - - screenLocation = m_PlanetCenter + (*sItr)->GetLocation() + (*sItr)->GetLocationOffset(); - // If currently being shown as being fought over; make more dramatic - bool battleSite = m_PostBattleReview && (*sItr) == m_pAnimScene; - - // Find out what team we should show here.. it is not always the current team ownership; but might be a previous one temporarily displayed for dramatic effect - int team = battleSite ? m_PreBattleTeamOwnership : (*sItr)->GetTeamOwnership(); - - // Make sure team is within bounds to show an icon - pIcon = g_MetaMan.IsActiveTeam(team) ? g_MetaMan.GetTeamIcon(team).GetBitmaps32()[0] : 0; - if (pIcon) - masked_blit(pIcon, drawBitmap, 0, 0, screenLocation.m_X - (pIcon->w / 2), screenLocation.m_Y - (pIcon->h / 2), pIcon->w, pIcon->h); - // Ownership not known, so place nondescript dot instead - else - { - // Make it flicker more if it's currently being fought over + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + + // Draw the scene location dots + Vector screenLocation; + BITMAP* pIcon = 0; + for (std::vector::const_iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Only draw Scenes that are revealed yet + if (!(*sItr)->IsRevealed()) + continue; + + screenLocation = m_PlanetCenter + (*sItr)->GetLocation() + (*sItr)->GetLocationOffset(); + // If currently being shown as being fought over; make more dramatic + bool battleSite = m_PostBattleReview && (*sItr) == m_pAnimScene; + + // Find out what team we should show here.. it is not always the current team ownership; but might be a previous one temporarily displayed for dramatic effect + int team = battleSite ? m_PreBattleTeamOwnership : (*sItr)->GetTeamOwnership(); + + // Make sure team is within bounds to show an icon + pIcon = g_MetaMan.IsActiveTeam(team) ? g_MetaMan.GetTeamIcon(team).GetBitmaps32()[0] : 0; + if (pIcon) + masked_blit(pIcon, drawBitmap, 0, 0, screenLocation.m_X - (pIcon->w / 2), screenLocation.m_Y - (pIcon->h / 2), pIcon->w, pIcon->h); + // Ownership not known, so place nondescript dot instead + else { + // Make it flicker more if it's currently being fought over blendAmount = 95 + (battleSite ? RandomNum(-25, 25) : RandomNum(-15, 15)); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - circlefill(drawBitmap, screenLocation.m_X, screenLocation.m_Y, 4, c_GUIColorYellow); - circlefill(drawBitmap, screenLocation.m_X, screenLocation.m_Y, 2, c_GUIColorYellow); + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + circlefill(drawBitmap, screenLocation.m_X, screenLocation.m_Y, 4, c_GUIColorYellow); + circlefill(drawBitmap, screenLocation.m_X, screenLocation.m_Y, 2, c_GUIColorYellow); blendAmount = 210 + RandomNum(-45, 45); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - circlefill(drawBitmap, screenLocation.m_X, screenLocation.m_Y, 1, c_GUIColorYellow); - } - } - - // Draw the lines etc pointing at the selected Scene from the Scene Info box - if (m_pSelectedScene && m_pSceneInfoPopup->GetVisible() && !m_PreTurn) - { - Vector sceneInfoBoxPos(m_pSceneInfoPopup->GetXPos() + (m_pSceneInfoPopup->GetWidth() / 2), m_pSceneInfoPopup->GetYPos() + (m_pSceneInfoPopup->GetHeight() / 2)); - DrawScreenLineToSitePoint(drawBitmap, sceneInfoBoxPos, m_pSelectedScene->GetLocation() + m_pSelectedScene->GetLocationOffset(), c_GUIColorWhite, -1, -1, (m_pSceneInfoPopup->GetHeight() / 2) + CHAMFERSIZE + 6, 1.0, g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())); - } - - // Draw all the player income site lines we are supposed to - // for (vector::const_iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) - for (int slI = 0; slI < m_IncomeSiteLines.size(); ++slI) - { - // Don't draw these during state change frames, to avoid blinking things inappropriately - if (m_ContinuePhase || g_MetaMan.m_StateChanged) - continue; - // Only draw the lines of the active player, when we're not in RUNACTIVITIES state - if (g_MetaMan.m_GameState != MetaMan::RUNACTIVITIES && m_IncomeSiteLines[slI].m_Player != m_ActivePlayerIncomeLines) - continue; - // If this is the line currently being animated, report back if the line connected with the current parameters - if (slI == m_AnimIncomeLine) - m_LineConnected = DrawPlayerLineToSitePoint(drawBitmap, m_IncomeSiteLines[slI]) || m_LineConnected; - // Just draw the regular unanimated line - else - DrawPlayerLineToSitePoint(drawBitmap, m_IncomeSiteLines[slI]); - } - - // Action lines - // If during a player's round phase, and not showing any income lines - if (!m_PreTurn && !g_MetaMan.m_StateChanged && g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::BUILDBASES && m_ActivePlayerIncomeLines == Players::NoPlayer) - { - int metaPlayer = 0; - // Show the lines of the relevant player during turns - if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) - metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; - // If we're showing base building or attacks, then go with the currently animated player - else if (g_MetaMan.m_GameState == MetaMan::BUILDBASES && m_AnimMetaPlayer < g_MetaMan.m_Players.size()) - metaPlayer = m_AnimMetaPlayer; - - for (int slI = 0; slI < m_ActionSiteLines[metaPlayer].size(); ++slI) - { - // Only draw the lines of the active player -// if (m_ActionSiteLines[metaPlayer][slI].m_Player != m_ActivePlayerIncomeLines) -// continue; - // If this is the line currently being animated, report back if the line connected with the current parameters - if (slI == m_AnimActionLine) - m_LineConnected = DrawPlayerLineToSitePoint(drawBitmap, m_ActionSiteLines[metaPlayer][slI], m_ActionMeterDrawOverride) || m_LineConnected; - // Just draw the regular unanimated line - else - DrawPlayerLineToSitePoint(drawBitmap, m_ActionSiteLines[metaPlayer][slI], m_ActionMeterDrawOverride); - } - } - - // Draw the new site location crosshairs when revealing them - if (g_MetaMan.m_GameState == MetaMan::REVEALSCENES && !g_MetaMan.m_StateChanged) - { - for (std::vector::iterator stItr = m_NewSiteIndicators.begin(); stItr != m_NewSiteIndicators.end(); ++stItr) - (*stItr).Draw(drawBitmap); - } - - // Draw any site ownership change animations -// if (g_MetaMan.m_GameState == MetaMan::REVEALSCENES && !g_MetaMan.m_StateChanged) - { - for (std::vector::iterator stItr = m_SiteSwitchIndicators.begin(); stItr != m_SiteSwitchIndicators.end(); ++stItr) - (*stItr).Draw(drawBitmap); - } - - // Draw the attack activity crosshairs over the site being violated - if (g_MetaMan.m_GameState == MetaMan::RUNACTIVITIES && !g_MetaMan.m_StateChanged) - { - // Draw the target crosshairs on the attacked site - m_SiteAttackTarget.Draw(drawBitmap); - - // Draw the attack lines that are currently being revealed by possibly multiple attackers - for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) - { - for (int slI = 0; slI < m_ActionSiteLines[metaPlayer].size(); ++slI) - { - DrawPlayerLineToSitePoint(drawBitmap, m_ActionSiteLines[metaPlayer][slI], m_ActionMeterDrawOverride); - } - } - } - } - - // Back to solid drawing - drawing_mode(DRAW_MODE_SOLID, 0, 0, 0); - - // Draw the GUI elements first - AllegroScreen drawScreen(drawBitmap); - m_pGUIController->Draw(&drawScreen); - m_pGUIController->DrawMouse(); - - // Draw the banners on top of all GUI - if (!g_MetaMan.IsSuspended()) - { - m_pBannerRedTop->Draw(drawBitmap); - m_pBannerRedBottom->Draw(drawBitmap); - m_pBannerYellowTop->Draw(drawBitmap); - m_pBannerYellowBottom->Draw(drawBitmap); - } -} + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + circlefill(drawBitmap, screenLocation.m_X, screenLocation.m_Y, 1, c_GUIColorYellow); + } + } + + // Draw the lines etc pointing at the selected Scene from the Scene Info box + if (m_pSelectedScene && m_pSceneInfoPopup->GetVisible() && !m_PreTurn) { + Vector sceneInfoBoxPos(m_pSceneInfoPopup->GetXPos() + (m_pSceneInfoPopup->GetWidth() / 2), m_pSceneInfoPopup->GetYPos() + (m_pSceneInfoPopup->GetHeight() / 2)); + DrawScreenLineToSitePoint(drawBitmap, sceneInfoBoxPos, m_pSelectedScene->GetLocation() + m_pSelectedScene->GetLocationOffset(), c_GUIColorWhite, -1, -1, (m_pSceneInfoPopup->GetHeight() / 2) + CHAMFERSIZE + 6, 1.0, g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())); + } + + // Draw all the player income site lines we are supposed to + // for (vector::const_iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) + for (int slI = 0; slI < m_IncomeSiteLines.size(); ++slI) { + // Don't draw these during state change frames, to avoid blinking things inappropriately + if (m_ContinuePhase || g_MetaMan.m_StateChanged) + continue; + // Only draw the lines of the active player, when we're not in RUNACTIVITIES state + if (g_MetaMan.m_GameState != MetaMan::RUNACTIVITIES && m_IncomeSiteLines[slI].m_Player != m_ActivePlayerIncomeLines) + continue; + // If this is the line currently being animated, report back if the line connected with the current parameters + if (slI == m_AnimIncomeLine) + m_LineConnected = DrawPlayerLineToSitePoint(drawBitmap, m_IncomeSiteLines[slI]) || m_LineConnected; + // Just draw the regular unanimated line + else + DrawPlayerLineToSitePoint(drawBitmap, m_IncomeSiteLines[slI]); + } + + // Action lines + // If during a player's round phase, and not showing any income lines + if (!m_PreTurn && !g_MetaMan.m_StateChanged && g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::BUILDBASES && m_ActivePlayerIncomeLines == Players::NoPlayer) { + int metaPlayer = 0; + // Show the lines of the relevant player during turns + if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) + metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; + // If we're showing base building or attacks, then go with the currently animated player + else if (g_MetaMan.m_GameState == MetaMan::BUILDBASES && m_AnimMetaPlayer < g_MetaMan.m_Players.size()) + metaPlayer = m_AnimMetaPlayer; + + for (int slI = 0; slI < m_ActionSiteLines[metaPlayer].size(); ++slI) { + // Only draw the lines of the active player + // if (m_ActionSiteLines[metaPlayer][slI].m_Player != m_ActivePlayerIncomeLines) + // continue; + // If this is the line currently being animated, report back if the line connected with the current parameters + if (slI == m_AnimActionLine) + m_LineConnected = DrawPlayerLineToSitePoint(drawBitmap, m_ActionSiteLines[metaPlayer][slI], m_ActionMeterDrawOverride) || m_LineConnected; + // Just draw the regular unanimated line + else + DrawPlayerLineToSitePoint(drawBitmap, m_ActionSiteLines[metaPlayer][slI], m_ActionMeterDrawOverride); + } + } + // Draw the new site location crosshairs when revealing them + if (g_MetaMan.m_GameState == MetaMan::REVEALSCENES && !g_MetaMan.m_StateChanged) { + for (std::vector::iterator stItr = m_NewSiteIndicators.begin(); stItr != m_NewSiteIndicators.end(); ++stItr) + (*stItr).Draw(drawBitmap); + } + + // Draw any site ownership change animations + // if (g_MetaMan.m_GameState == MetaMan::REVEALSCENES && !g_MetaMan.m_StateChanged) + { + for (std::vector::iterator stItr = m_SiteSwitchIndicators.begin(); stItr != m_SiteSwitchIndicators.end(); ++stItr) + (*stItr).Draw(drawBitmap); + } + + // Draw the attack activity crosshairs over the site being violated + if (g_MetaMan.m_GameState == MetaMan::RUNACTIVITIES && !g_MetaMan.m_StateChanged) { + // Draw the target crosshairs on the attacked site + m_SiteAttackTarget.Draw(drawBitmap); + + // Draw the attack lines that are currently being revealed by possibly multiple attackers + for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) { + for (int slI = 0; slI < m_ActionSiteLines[metaPlayer].size(); ++slI) { + DrawPlayerLineToSitePoint(drawBitmap, m_ActionSiteLines[metaPlayer][slI], m_ActionMeterDrawOverride); + } + } + } + } + + // Back to solid drawing + drawing_mode(DRAW_MODE_SOLID, 0, 0, 0); + + // Draw the GUI elements first + AllegroScreen drawScreen(drawBitmap); + m_pGUIController->Draw(&drawScreen); + m_pGUIController->DrawMouse(); + + // Draw the banners on top of all GUI + if (!g_MetaMan.IsSuspended()) { + m_pBannerRedTop->Draw(drawBitmap); + m_pBannerRedBottom->Draw(drawBitmap); + m_pBannerYellowTop->Draw(drawBitmap); + m_pBannerYellowBottom->Draw(drawBitmap); + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateInput @@ -2079,9 +1910,8 @@ void MetagameGUI::Draw(BITMAP *drawBitmap) // Arguments: None. // Return value: None. -void MetagameGUI::UpdateInput() -{ - // If esc pressed, show campaign dialog if applicable +void MetagameGUI::UpdateInput() { + // If esc pressed, show campaign dialog if applicable if (g_UInputMan.KeyPressed(SDLK_ESCAPE)) { if (m_MenuScreen == MENUDIALOG) { g_MetaMan.SetSuspend(false); @@ -2094,81 +1924,80 @@ void MetagameGUI::UpdateInput() return; } - /////////////////////////////////////////////////////////// - // Mouse handling - - // Get mouse position - int mouseX, mouseY; - m_pGUIInput->GetMousePosition(&mouseX, &mouseY); - Vector mousePos(mouseX, mouseY); - - // If not currently dragging a box, see if we should start - bool menuButtonHeld = g_UInputMan.MenuButtonHeld(UInputMan::MENU_EITHER); - if (!m_pDraggedBox && menuButtonHeld && !m_EngageDrag) - { - GUICollectionBox *pBox = dynamic_cast(m_pGUIController->GetControlUnderPoint(mouseX, mouseY, m_apScreenBox[ROOTBOX], 1)); - - // Player bars - for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) - { - if (pBox == m_apPlayerBox[metaPlayer]) - m_pDraggedBox = pBox; - } - - // Scene info box? - if (pBox == m_pSceneInfoPopup) - m_pDraggedBox = pBox; - - // Phase box? - if (pBox == m_pPhaseBox) - m_pDraggedBox = pBox; - - // Save the mouse pos at the start of the drag so we can measure if we should engage - if (m_pDraggedBox) - m_PrevMousePos = mousePos; - } - // Release if button isn't being held - else if (!menuButtonHeld) - { - m_pDraggedBox = 0; - m_EngageDrag = false; - } - - // Figure out dragging of the player bars, if one is being dragged - if (m_pDraggedBox && !m_EngageDrag) - { - // Only start drag if we're over a threshold, to prevent small unintentional nudges - if ((mousePos - m_PrevMousePos).GetLargest() > 4) - m_EngageDrag = true; - } - - // Actually drag if we now are engaged - if (m_pDraggedBox && m_EngageDrag) - { - m_pDraggedBox->MoveRelative(mousePos.m_X - m_PrevMousePos.m_X, mousePos.m_Y - m_PrevMousePos.m_Y); - // Ensure the drag didn't shove it off-screen - KeepBoxOnScreen(m_pDraggedBox); - } - - ////////////////////////////////////////// - // Update the ControlManager - - m_pGUIController->Update(); - - ////////////////////////////////////////// - // Update the ToolTip popup - - // Show the ToolTip popup, if we are hovering over anything that has one to show + /////////////////////////////////////////////////////////// + // Mouse handling + + // Get mouse position + int mouseX, mouseY; + m_pGUIInput->GetMousePosition(&mouseX, &mouseY); + Vector mousePos(mouseX, mouseY); + + // If not currently dragging a box, see if we should start + bool menuButtonHeld = g_UInputMan.MenuButtonHeld(UInputMan::MENU_EITHER); + if (!m_pDraggedBox && menuButtonHeld && !m_EngageDrag) { + GUICollectionBox* pBox = dynamic_cast(m_pGUIController->GetControlUnderPoint(mouseX, mouseY, m_apScreenBox[ROOTBOX], 1)); + + // Player bars + for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) { + if (pBox == m_apPlayerBox[metaPlayer]) + m_pDraggedBox = pBox; + } + + // Scene info box? + if (pBox == m_pSceneInfoPopup) + m_pDraggedBox = pBox; + + // Phase box? + if (pBox == m_pPhaseBox) + m_pDraggedBox = pBox; + + // Save the mouse pos at the start of the drag so we can measure if we should engage + if (m_pDraggedBox) + m_PrevMousePos = mousePos; + } + // Release if button isn't being held + else if (!menuButtonHeld) { + m_pDraggedBox = 0; + m_EngageDrag = false; + } + + // Figure out dragging of the player bars, if one is being dragged + if (m_pDraggedBox && !m_EngageDrag) { + // Only start drag if we're over a threshold, to prevent small unintentional nudges + if ((mousePos - m_PrevMousePos).GetLargest() > 4) + m_EngageDrag = true; + } + + // Actually drag if we now are engaged + if (m_pDraggedBox && m_EngageDrag) { + m_pDraggedBox->MoveRelative(mousePos.m_X - m_PrevMousePos.m_X, mousePos.m_Y - m_PrevMousePos.m_Y); + // Ensure the drag didn't shove it off-screen + KeepBoxOnScreen(m_pDraggedBox); + } + + ////////////////////////////////////////// + // Update the ControlManager + + m_pGUIController->Update(); + + ////////////////////////////////////////// + // Update the ToolTip popup + + // Show the ToolTip popup, if we are hovering over anything that has one to show std::string toolTip = ""; - GUIControl *pCurrentHover = m_pGUIController->GetControlUnderPoint(mouseX, mouseY); + GUIControl* pCurrentHover = m_pGUIController->GetControlUnderPoint(mouseX, mouseY); if (pCurrentHover) { toolTip = pCurrentHover->GetToolTip(); // The control's tooltip can end up being "True" for reasons unknown to man, so just clear it. - if (toolTip == "None" || toolTip == "True") { toolTip.clear(); } + if (toolTip == "None" || toolTip == "True") { + toolTip.clear(); + } } if (g_SettingsMan.ShowToolTips() && pCurrentHover && !toolTip.empty()) { // Restart timer if there's a new thing we're hovering over - if (pCurrentHover != m_pHoveredControl) { m_ToolTipTimer.Reset(); } + if (pCurrentHover != m_pHoveredControl) { + m_ToolTipTimer.Reset(); + } // If we've been hovering over the same thing for enough time, then show the tooltip if (m_ToolTipTimer.IsPastRealMS(500)) { @@ -2193,496 +2022,442 @@ void MetagameGUI::UpdateInput() m_pHoveredControl = nullptr; } - - /////////////////////////////////////// - // Handle events + /////////////////////////////////////// + // Handle events // Phase advance button pressed while in an unpaused game; set the signal flag at the end of input handling - bool phaseButtonPressed = !g_MetaMan.IsSuspended() && g_UInputMan.AnyStartPress(); + bool phaseButtonPressed = !g_MetaMan.IsSuspended() && g_UInputMan.AnyStartPress(); GUIEvent anEvent; - while(m_pGUIController->GetEvent(&anEvent)) - { + while (m_pGUIController->GetEvent(&anEvent)) { const std::string eventControlName = anEvent.GetControl()->GetName(); - // Commands - if (anEvent.GetType() == GUIEvent::Command) - { + // Commands + if (anEvent.GetType() == GUIEvent::Command) { // Open game menu button pressed // Most big dialog cancel buttons lead back to the game menu too if (eventControlName == "OpenMenuButton" || - eventControlName == "SaveCancelButton" || - eventControlName == "LoadCancelButton" || - (eventControlName == "NewCancelButton" && g_MetaMan.GameInProgress()) || - eventControlName == "ConfirmCancelButton") - { - g_MetaMan.SetSuspend(true); - SwitchToScreen(MENUDIALOG); - g_GUISound.BackButtonPressSound()->Play(); - } + eventControlName == "SaveCancelButton" || + eventControlName == "LoadCancelButton" || + (eventControlName == "NewCancelButton" && g_MetaMan.GameInProgress()) || + eventControlName == "ConfirmCancelButton") { + g_MetaMan.SetSuspend(true); + SwitchToScreen(MENUDIALOG); + g_GUISound.BackButtonPressSound()->Play(); + } // Return to main menu button pressed - else if (eventControlName == "MainMenuButton" || eventControlName == "NewCancelButton") - { - //Return Metagame dialog to new game state - // weegee SwitchToScreen(NEWDIALOG); - // Hide all screens, the appropriate screen will reappear on next update - HideAllScreens(); - if (g_MetaMan.m_GameState == MetaMan::GAMEOVER && g_ActivityMan.GetActivity()) { g_PostProcessMan.ClearScreenPostEffects(); } - // Signal that we want to go back to main menu - m_BackToMain = true; - g_GUISound.BackButtonPressSound()->Play(); - - } + else if (eventControlName == "MainMenuButton" || eventControlName == "NewCancelButton") { + // Return Metagame dialog to new game state + // weegee SwitchToScreen(NEWDIALOG); + // Hide all screens, the appropriate screen will reappear on next update + HideAllScreens(); + if (g_MetaMan.m_GameState == MetaMan::GAMEOVER && g_ActivityMan.GetActivity()) { + g_PostProcessMan.ClearScreenPostEffects(); + } + // Signal that we want to go back to main menu + m_BackToMain = true; + g_GUISound.BackButtonPressSound()->Play(); + + } // Open save menu button pressed - else if (eventControlName == "MenuSaveButton") - { - g_MetaMan.SetSuspend(true); - SwitchToScreen(SAVEDIALOG); - g_GUISound.ButtonPressSound()->Play(); - } + else if (eventControlName == "MenuSaveButton") { + g_MetaMan.SetSuspend(true); + SwitchToScreen(SAVEDIALOG); + g_GUISound.ButtonPressSound()->Play(); + } // Open load menu button pressed else if (eventControlName == "MenuLoadButton" || - eventControlName == "NewLoadButton") - { - g_MetaMan.SetSuspend(true); - SwitchToScreen(LOADDIALOG); - g_GUISound.ButtonPressSound()->Play(); - } + eventControlName == "NewLoadButton") { + g_MetaMan.SetSuspend(true); + SwitchToScreen(LOADDIALOG); + g_GUISound.ButtonPressSound()->Play(); + } // New Game menu button pressed - else if (eventControlName == "MenuNewButton") - { - g_MetaMan.SetSuspend(true); - SwitchToScreen(NEWDIALOG); - UpdatePlayerSetup(); - g_GUISound.ButtonPressSound()->Play(); - } + else if (eventControlName == "MenuNewButton") { + g_MetaMan.SetSuspend(true); + SwitchToScreen(NEWDIALOG); + UpdatePlayerSetup(); + g_GUISound.ButtonPressSound()->Play(); + } // Quit Program button pressed - else if (eventControlName == "MenuQuitButton") - { - HideAllScreens(); - g_MetaMan.SetSuspend(true); - m_pConfirmationLabel->SetText("Sure you want to quit to OS?\nAny unsaved progress\nwill be lost!"); - m_pConfirmationButton->SetText("Quit"); - m_pConfirmationBox->SetVisible(true); - g_GUISound.ButtonPressSound()->Play(); - } + else if (eventControlName == "MenuQuitButton") { + HideAllScreens(); + g_MetaMan.SetSuspend(true); + m_pConfirmationLabel->SetText("Sure you want to quit to OS?\nAny unsaved progress\nwill be lost!"); + m_pConfirmationButton->SetText("Quit"); + m_pConfirmationBox->SetVisible(true); + g_GUISound.ButtonPressSound()->Play(); + } // Resume Game menu button pressed - else if (eventControlName == "MenuResumeButton") - { - g_MetaMan.SetSuspend(false); - // If game over, then go to new game dialog on resume - if (g_MetaMan.m_GameState == MetaMan::GAMEOVER) - { - SwitchToScreen(NEWDIALOG); - UpdatePlayerSetup(); - } - else - SwitchToScreen(ROOTBOX); - g_GUISound.ButtonPressSound()->Play(); - } + else if (eventControlName == "MenuResumeButton") { + g_MetaMan.SetSuspend(false); + // If game over, then go to new game dialog on resume + if (g_MetaMan.m_GameState == MetaMan::GAMEOVER) { + SwitchToScreen(NEWDIALOG); + UpdatePlayerSetup(); + } else + SwitchToScreen(ROOTBOX); + g_GUISound.ButtonPressSound()->Play(); + } // Confirm button pressed - if (anEvent.GetControl() == m_apMetaButton[CONFIRM]) - { - // Confirm Quit Program button + if (anEvent.GetControl() == m_apMetaButton[CONFIRM]) { + // Confirm Quit Program button if (m_pConfirmationButton->GetText() == "Quit") System::SetQuit(); - // Do the appropriate thing depending on which screen we're confirming - if (m_MenuScreen == NEWDIALOG) - { -// if (m_apMetaButton[CONFIRM]->GetText() == "Load") -// ; -// else if (m_apMetaButton[CONFIRM]->GetText() == "Start New") - StartNewGame(); - } - else if (m_MenuScreen == LOADDIALOG) - { - LoadGame(); - // Switch directly into the game after a load is done - g_MetaMan.SetSuspend(false); - SwitchToScreen(ROOTBOX); - } - else if (m_MenuScreen == SAVEDIALOG) - { - SaveGameFromDialog(); - // Go back to the metagame menu after a save is done - SwitchToScreen(MENUDIALOG); - } - - g_MetaMan.SetSuspend(false); - SwitchToScreen(ROOTBOX); - - g_GUISound.ButtonPressSound()->Play(); - } - - // NEW GAME SETUP DIALOG - // Player Control Setup toggle buttons - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (anEvent.GetControl() == m_apPlayerControlButton[player]) - { - // Cycle through the different modes - if (m_apPlayerControlButton[player]->GetText() == "None") - m_apPlayerControlButton[player]->SetText("A.I."); - else if (m_apPlayerControlButton[player]->GetText() == "A.I.") - m_apPlayerControlButton[player]->SetText("Human"); - else - m_apPlayerControlButton[player]->SetText("None"); - // Show changes in affected player' gizmos - UpdatePlayerSetup(); - - g_GUISound.ButtonPressSound()->Play(); - } - } + // Do the appropriate thing depending on which screen we're confirming + if (m_MenuScreen == NEWDIALOG) { + // if (m_apMetaButton[CONFIRM]->GetText() == "Load") + // ; + // else if (m_apMetaButton[CONFIRM]->GetText() == "Start New") + StartNewGame(); + } else if (m_MenuScreen == LOADDIALOG) { + LoadGame(); + // Switch directly into the game after a load is done + g_MetaMan.SetSuspend(false); + SwitchToScreen(ROOTBOX); + } else if (m_MenuScreen == SAVEDIALOG) { + SaveGameFromDialog(); + // Go back to the metagame menu after a save is done + SwitchToScreen(MENUDIALOG); + } - // Start New Game menu button pressed - if (eventControlName == "StartButton") - { - // Current game needs saved or there will be data loss, so show confirmation box - if (!g_MetaMan.GameIsSaved()) - { - HideAllScreens(); - m_pConfirmationLabel->SetText("There is a game going!\nAny unsaved progress\nin it will be lost!"); - m_pConfirmationButton->SetText("Start New"); - m_pConfirmationBox->SetVisible(true); - } - else - StartNewGame(); - - g_GUISound.ButtonPressSound()->Play(); - } + g_MetaMan.SetSuspend(false); + SwitchToScreen(ROOTBOX); - // Save game button pressed - if (eventControlName == "SaveButton") - { - // Overwrite confirmation click has been done already - if (!m_NewSaveBox->GetText().empty() || m_apMetaButton[SAVENOW]->GetText() != "Save") - { - // Make sure there is somehting to save - if (!g_MetaMan.GameIsSaved()) - { -// TODO: WARN the player that they will have to re-play an offensive battle from start if they save a metagame -/* if () - { - HideAllScreens(); - m_pConfirmationLabel->SetText("There is an on-planet battle\ncurrently underway!\nThe progress in it will NOT be saved here;\nyou will have to re-play it\nif you load this game later!"); - m_pConfirmationButton->SetText("Save Anyway"); - m_pConfirmationBox->SetVisible(true); - } - else - { -*/ - SaveGameFromDialog(); - // Switch back to the menu after a save is done - SwitchToScreen(MENUDIALOG); -// } - } - g_GUISound.ButtonPressSound()->Play(); - } - else - { - m_apMetaButton[SAVENOW]->SetText("CONFIRM?"); - g_GUISound.ItemChangeSound()->Play(); - } - } + g_GUISound.ButtonPressSound()->Play(); + } + + // NEW GAME SETUP DIALOG + // Player Control Setup toggle buttons + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (anEvent.GetControl() == m_apPlayerControlButton[player]) { + // Cycle through the different modes + if (m_apPlayerControlButton[player]->GetText() == "None") + m_apPlayerControlButton[player]->SetText("A.I."); + else if (m_apPlayerControlButton[player]->GetText() == "A.I.") + m_apPlayerControlButton[player]->SetText("Human"); + else + m_apPlayerControlButton[player]->SetText("None"); + // Show changes in affected player' gizmos + UpdatePlayerSetup(); + + g_GUISound.ButtonPressSound()->Play(); + } + } + + // Start New Game menu button pressed + if (eventControlName == "StartButton") { + // Current game needs saved or there will be data loss, so show confirmation box + if (!g_MetaMan.GameIsSaved()) { + HideAllScreens(); + m_pConfirmationLabel->SetText("There is a game going!\nAny unsaved progress\nin it will be lost!"); + m_pConfirmationButton->SetText("Start New"); + m_pConfirmationBox->SetVisible(true); + } else + StartNewGame(); + + g_GUISound.ButtonPressSound()->Play(); + } + + // Save game button pressed + if (eventControlName == "SaveButton") { + // Overwrite confirmation click has been done already + if (!m_NewSaveBox->GetText().empty() || m_apMetaButton[SAVENOW]->GetText() != "Save") { + // Make sure there is somehting to save + if (!g_MetaMan.GameIsSaved()) { + // TODO: WARN the player that they will have to re-play an offensive battle from start if they save a metagame + /* if () + { + HideAllScreens(); + m_pConfirmationLabel->SetText("There is an on-planet battle\ncurrently underway!\nThe progress in it will NOT be saved here;\nyou will have to re-play it\nif you load this game later!"); + m_pConfirmationButton->SetText("Save Anyway"); + m_pConfirmationBox->SetVisible(true); + } + else + { + */ + SaveGameFromDialog(); + // Switch back to the menu after a save is done + SwitchToScreen(MENUDIALOG); + // } + } + g_GUISound.ButtonPressSound()->Play(); + } else { + m_apMetaButton[SAVENOW]->SetText("CONFIRM?"); + g_GUISound.ItemChangeSound()->Play(); + } + } // Load game button pressed - if (eventControlName == "LoadButton" && m_pSavesToLoadCombo->GetSelectedItem()) - { - // Save this Entity selection because the ComboBox gets cleared out when the conf dlg appears, so we can't get to the selection later when we acutally decide to load the damn thing - m_pSelectedGameToLoad = m_pSavesToLoadCombo->GetSelectedItem()->m_pEntity; - - // Current game needs saved or there will be data loss, so show conf box - if (!g_MetaMan.GameIsSaved()) - { - HideAllScreens(); - m_pConfirmationLabel->SetText("There is a game going!\nAny unsaved progress\nin it will be lost!"); - m_pConfirmationButton->SetText("Load"); - m_pConfirmationBox->SetVisible(true); - } - else - { - LoadGame(); - // Switch directly into the game after a load is done - g_MetaMan.SetSuspend(false); - SwitchToScreen(ROOTBOX); - } - - g_GUISound.ButtonPressSound()->Play(); - } + if (eventControlName == "LoadButton" && m_pSavesToLoadCombo->GetSelectedItem()) { + // Save this Entity selection because the ComboBox gets cleared out when the conf dlg appears, so we can't get to the selection later when we acutally decide to load the damn thing + m_pSelectedGameToLoad = m_pSavesToLoadCombo->GetSelectedItem()->m_pEntity; + + // Current game needs saved or there will be data loss, so show conf box + if (!g_MetaMan.GameIsSaved()) { + HideAllScreens(); + m_pConfirmationLabel->SetText("There is a game going!\nAny unsaved progress\nin it will be lost!"); + m_pConfirmationButton->SetText("Load"); + m_pConfirmationBox->SetVisible(true); + } else { + LoadGame(); + // Switch directly into the game after a load is done + g_MetaMan.SetSuspend(false); + SwitchToScreen(ROOTBOX); + } + + g_GUISound.ButtonPressSound()->Play(); + } // Graphical 'Continue Phase' button pressed; set the signal flag and handle at the end of UpdateInput if (anEvent.GetControl() == m_apMetaButton[CONTINUE]) - phaseButtonPressed = true; + phaseButtonPressed = true; // Scene Action button pressed; this only means to scan at this point - if (anEvent.GetControl() == m_apMetaButton[SCENEACTION] && m_pSelectedScene) - { - // Set up site scan of it (for a price) - int metaPlayer = g_MetaMan.GetPlayerTurn(); - int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); - // Check if we have enough money for this! - if (g_MetaMan.GetRemainingFundsOfPlayer(metaPlayer, 0, false, false) < SCANCOST) - { - m_apMetaButton[SCENEACTION]->SetText("NOT ENOUGH FUNDS!"); - g_GUISound.UserErrorSound()->Play(); - } - // Ask whether the player wants to scan now or later, with two new buttons - else - { - m_apMetaButton[SCENEACTION]->SetVisible(false); - m_apMetaButton[DESIGNBASE]->SetVisible(false); - m_apMetaButton[SCANNOW]->SetVisible(true); - m_apMetaButton[SCANLATER]->SetVisible(true); - m_pScanInfoLabel->SetVisible(true); - m_pScanInfoLabel->SetText("or"); - - g_GUISound.ButtonPressSound()->Play(); - } - } + if (anEvent.GetControl() == m_apMetaButton[SCENEACTION] && m_pSelectedScene) { + // Set up site scan of it (for a price) + int metaPlayer = g_MetaMan.GetPlayerTurn(); + int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); + // Check if we have enough money for this! + if (g_MetaMan.GetRemainingFundsOfPlayer(metaPlayer, 0, false, false) < SCANCOST) { + m_apMetaButton[SCENEACTION]->SetText("NOT ENOUGH FUNDS!"); + g_GUISound.UserErrorSound()->Play(); + } + // Ask whether the player wants to scan now or later, with two new buttons + else { + m_apMetaButton[SCENEACTION]->SetVisible(false); + m_apMetaButton[DESIGNBASE]->SetVisible(false); + m_apMetaButton[SCANNOW]->SetVisible(true); + m_apMetaButton[SCANLATER]->SetVisible(true); + m_pScanInfoLabel->SetVisible(true); + m_pScanInfoLabel->SetText("or"); + + g_GUISound.ButtonPressSound()->Play(); + } + } // manual Base Design button pressed; start an activity to let player design the blueprints to be built there - if (anEvent.GetControl() == m_apMetaButton[DESIGNBASE] && m_pSelectedScene) - { - // If owned by this player, then set up base building controls - if (m_pSelectedScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(g_MetaMan.GetPlayerTurn())) - { - // Set up and start the editor activity for making changes to the base blueprint for this round - BaseEditor *pNewEditor = new BaseEditor; - pNewEditor->Create(); - char str[64]; - std::snprintf(str, sizeof(str), "R%dEdit", g_MetaMan.m_CurrentRound + 1); - pNewEditor->SetPresetName(g_MetaMan.GetGameName() + str); - - // Gotto deact all players since by default there is one in slot 1 - pNewEditor->ClearPlayers(); - // Editing player - int metaPlayer = g_MetaMan.GetPlayerTurn(); - int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); -// TODO: add all other players of same team, for coop editing?? Whata bout resident brains of lazy teammates who hacen't placed yet -// NO, this is silly. Just make the objects placed by this player marked as placed by him - pNewEditor->AddPlayer(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), true, team, 0, &(g_MetaMan.GetTeamIcon(team))); - - // Send in the Scene to load in later when the screen has faded out etc - m_pPlayingScene = m_pSelectedScene; - g_SceneMan.SetSceneToLoad(m_pPlayingScene); - g_ActivityMan.SetStartActivity(pNewEditor); - m_ActivityRestarted = true; - - g_GUISound.ButtonPressSound()->Play(); - } - } + if (anEvent.GetControl() == m_apMetaButton[DESIGNBASE] && m_pSelectedScene) { + // If owned by this player, then set up base building controls + if (m_pSelectedScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(g_MetaMan.GetPlayerTurn())) { + // Set up and start the editor activity for making changes to the base blueprint for this round + BaseEditor* pNewEditor = new BaseEditor; + pNewEditor->Create(); + char str[64]; + std::snprintf(str, sizeof(str), "R%dEdit", g_MetaMan.m_CurrentRound + 1); + pNewEditor->SetPresetName(g_MetaMan.GetGameName() + str); + + // Gotto deact all players since by default there is one in slot 1 + pNewEditor->ClearPlayers(); + // Editing player + int metaPlayer = g_MetaMan.GetPlayerTurn(); + int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); + // TODO: add all other players of same team, for coop editing?? Whata bout resident brains of lazy teammates who hacen't placed yet + // NO, this is silly. Just make the objects placed by this player marked as placed by him + pNewEditor->AddPlayer(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), true, team, 0, &(g_MetaMan.GetTeamIcon(team))); + + // Send in the Scene to load in later when the screen has faded out etc + m_pPlayingScene = m_pSelectedScene; + g_SceneMan.SetSceneToLoad(m_pPlayingScene); + g_ActivityMan.SetStartActivity(pNewEditor); + m_ActivityRestarted = true; + + g_GUISound.ButtonPressSound()->Play(); + } + } // Scan Now button pressed; time to start the scanning process immediately - if (anEvent.GetControl() == m_apMetaButton[SCANNOW] && m_pSelectedScene) - { - int metaPlayer = g_MetaMan.GetPlayerTurn(); - int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); - // Actually change the player's funds - g_MetaMan.m_Players[metaPlayer].m_Funds -= SCANCOST; - // Set up and start the scripted activity for scanning the site for this' team - GAScripted *pScanActivity = new GAScripted; - pScanActivity->Create("Base.rte/Activities/SiteScan.lua", "SiteScan"); - char str[64]; - std::snprintf(str, sizeof(str), "R%dScan", g_MetaMan.m_CurrentRound + 1); - pScanActivity->SetPresetName(g_MetaMan.GetGameName() + str); - - // Gotto deact all players since by default there is one in slot 1 - pScanActivity->ClearPlayers(); - // Add the player who ordered this snooping around - pScanActivity->AddPlayer(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), true, team, SCANCOST, &(g_MetaMan.GetTeamIcon(team))); - // Add the players who own this place, if any - their actors and brains are in the scene and should be shown - if (g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())) - { - // Go through all players and add the ones of the defending team, based on who has resident brains here - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Got to remember to translate from metagame player index into the in-game player index and to flag them as not a human so they dont' get their own screens -// if (g_MetaMan.m_Players[mp].GetTeam() == m_pSelectedScene->GetTeamOwnership()) - if (m_pSelectedScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - pScanActivity->AddPlayer(g_MetaMan.m_Players[mp].GetInGamePlayer(), false, g_MetaMan.m_Players[mp].GetTeam(), 0, &(g_MetaMan.GetTeamIcon(g_MetaMan.m_Players[mp].GetTeam()))); - } - } - // Send in the Scene to load in later when the screen has faded out etc - m_pPlayingScene = m_pSelectedScene; - g_SceneMan.SetSceneToLoad(m_pPlayingScene); - g_ActivityMan.SetStartActivity(pScanActivity); - m_ActivityRestarted = true; - - g_GUISound.ButtonPressSound()->Play(); - } + if (anEvent.GetControl() == m_apMetaButton[SCANNOW] && m_pSelectedScene) { + int metaPlayer = g_MetaMan.GetPlayerTurn(); + int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); + // Actually change the player's funds + g_MetaMan.m_Players[metaPlayer].m_Funds -= SCANCOST; + // Set up and start the scripted activity for scanning the site for this' team + GAScripted* pScanActivity = new GAScripted; + pScanActivity->Create("Base.rte/Activities/SiteScan.lua", "SiteScan"); + char str[64]; + std::snprintf(str, sizeof(str), "R%dScan", g_MetaMan.m_CurrentRound + 1); + pScanActivity->SetPresetName(g_MetaMan.GetGameName() + str); + + // Gotto deact all players since by default there is one in slot 1 + pScanActivity->ClearPlayers(); + // Add the player who ordered this snooping around + pScanActivity->AddPlayer(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), true, team, SCANCOST, &(g_MetaMan.GetTeamIcon(team))); + // Add the players who own this place, if any - their actors and brains are in the scene and should be shown + if (g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())) { + // Go through all players and add the ones of the defending team, based on who has resident brains here + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Got to remember to translate from metagame player index into the in-game player index and to flag them as not a human so they dont' get their own screens + // if (g_MetaMan.m_Players[mp].GetTeam() == m_pSelectedScene->GetTeamOwnership()) + if (m_pSelectedScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) + pScanActivity->AddPlayer(g_MetaMan.m_Players[mp].GetInGamePlayer(), false, g_MetaMan.m_Players[mp].GetTeam(), 0, &(g_MetaMan.GetTeamIcon(g_MetaMan.m_Players[mp].GetTeam()))); + } + } + // Send in the Scene to load in later when the screen has faded out etc + m_pPlayingScene = m_pSelectedScene; + g_SceneMan.SetSceneToLoad(m_pPlayingScene); + g_ActivityMan.SetStartActivity(pScanActivity); + m_ActivityRestarted = true; + + g_GUISound.ButtonPressSound()->Play(); + } // Scan Later button pressed; mark the Scene for scanning by this metaplayer's team - if (anEvent.GetControl() == m_apMetaButton[SCANLATER] && m_pSelectedScene) - { - int metaPlayer = g_MetaMan.GetPlayerTurn(); - int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); - // Double-check to make sure we do have the funds to do this AND that the scan hasn't already been paid for - if (g_MetaMan.m_Players[metaPlayer].m_Funds < SCANCOST && !m_pSelectedScene->IsScanScheduled(team)) - g_GUISound.UserErrorSound()->Play(); - else - { - // Show the cost change the funds meter - FundsChangeIndication(metaPlayer, -SCANCOST, Vector(m_apPlayerBarLabel[metaPlayer]->GetXPos() + m_apPlayerBarLabel[metaPlayer]->GetWidth(), m_apPlayerBarLabel[metaPlayer]->GetYPos()), 2000); - // Actually change the player's funds - g_MetaMan.m_Players[metaPlayer].m_Funds -= SCANCOST; - - // Mark the selected Scene to be scanned next time it is loaded by a player of this' team - m_pSelectedScene->SetScheduledScan(team, true); - - // Update the scenes box to show that we have scheduled this scan - UpdateScenesBox(true); - - // Update the budget slider to reflect the scan cost being deducted from the funds - if (g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName() == m_pSelectedScene->GetPresetName()) - m_pSceneBudgetSlider->SetValue(std::floor((g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget() / g_MetaMan.m_Players[metaPlayer].GetFunds()) * 100)); - - // Play an appropriate sound to indicate that the scan is bought and scheduled - g_GUISound.ItemChangeSound()->Play(); - } - } + if (anEvent.GetControl() == m_apMetaButton[SCANLATER] && m_pSelectedScene) { + int metaPlayer = g_MetaMan.GetPlayerTurn(); + int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); + // Double-check to make sure we do have the funds to do this AND that the scan hasn't already been paid for + if (g_MetaMan.m_Players[metaPlayer].m_Funds < SCANCOST && !m_pSelectedScene->IsScanScheduled(team)) + g_GUISound.UserErrorSound()->Play(); + else { + // Show the cost change the funds meter + FundsChangeIndication(metaPlayer, -SCANCOST, Vector(m_apPlayerBarLabel[metaPlayer]->GetXPos() + m_apPlayerBarLabel[metaPlayer]->GetWidth(), m_apPlayerBarLabel[metaPlayer]->GetYPos()), 2000); + // Actually change the player's funds + g_MetaMan.m_Players[metaPlayer].m_Funds -= SCANCOST; + + // Mark the selected Scene to be scanned next time it is loaded by a player of this' team + m_pSelectedScene->SetScheduledScan(team, true); + + // Update the scenes box to show that we have scheduled this scan + UpdateScenesBox(true); + + // Update the budget slider to reflect the scan cost being deducted from the funds + if (g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName() == m_pSelectedScene->GetPresetName()) + m_pSceneBudgetSlider->SetValue(std::floor((g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget() / g_MetaMan.m_Players[metaPlayer].GetFunds()) * 100)); + + // Play an appropriate sound to indicate that the scan is bought and scheduled + g_GUISound.ItemChangeSound()->Play(); + } + } // Scene info box close button pressed - if (anEvent.GetControl() == m_pSceneCloseButton) - { - m_pSelectedScene = 0; - g_GUISound.ButtonPressSound()->Play(); - } - } + if (anEvent.GetControl() == m_pSceneCloseButton) { + m_pSelectedScene = 0; + g_GUISound.ButtonPressSound()->Play(); + } + } // Notifications - else if (anEvent.GetType() == GUIEvent::Notification) - { - // Button focus notification that we can play a sound to - if (dynamic_cast(anEvent.GetControl())) - { - if (anEvent.GetMsg() == GUIButton::Focused) - g_GUISound.SelectionChangeSound()->Play(); - // Also stop dragging any panels if we're over any button - m_pDraggedBox = 0; - m_EngageDrag = true; - } + else if (anEvent.GetType() == GUIEvent::Notification) { + // Button focus notification that we can play a sound to + if (dynamic_cast(anEvent.GetControl())) { + if (anEvent.GetMsg() == GUIButton::Focused) + g_GUISound.SelectionChangeSound()->Play(); + // Also stop dragging any panels if we're over any button + m_pDraggedBox = 0; + m_EngageDrag = true; + } // Site budget slider changed - if(anEvent.GetControl() == m_pSceneBudgetSlider) - { - // Update the appropriate budget var - if (m_pSelectedScene && !g_MetaMan.IsSuspended()) - { - // If during a player's round phase, figure out which player - if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) - { - int metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; - - // Update the scenes box - UpdateScenesBox(true); - - // If owned by this player, then set update base building budget for this Scene - float budget = ((float)m_pSceneBudgetSlider->GetValue() / 100.0f) * g_MetaMan.m_Players[metaPlayer].GetFunds(); - if (m_pSelectedScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer)) - { - m_pSelectedScene->SetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), budget); - } - // Site owned by enemy player, update the attack budget - else if (g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())) - { - g_MetaMan.m_Players[metaPlayer].SetOffensiveBudget(budget); - g_MetaMan.m_Players[metaPlayer].SetOffensiveTargetName(m_pSelectedScene->GetPresetName()); - } - // Unowned site, update the expedition budget - else - { - g_MetaMan.m_Players[metaPlayer].SetOffensiveBudget(budget); - g_MetaMan.m_Players[metaPlayer].SetOffensiveTargetName(m_pSelectedScene->GetPresetName()); - } - } - } - - // Also stop dragging any panels if we're over any button - m_pDraggedBox = 0; - m_EngageDrag = true; - } - - // Checkbox for changing the auto design setting of a scene changed; update the actual scene to reflect this - if (anEvent.GetControl() == m_pAutoDesignCheckbox && m_pSelectedScene) - { - if (anEvent.GetMsg() == GUICheckbox::Changed) - m_pSelectedScene->SetAutoDesigned(m_pAutoDesignCheckbox->GetCheck()); - } + if (anEvent.GetControl() == m_pSceneBudgetSlider) { + // Update the appropriate budget var + if (m_pSelectedScene && !g_MetaMan.IsSuspended()) { + // If during a player's round phase, figure out which player + if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) { + int metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; + + // Update the scenes box + UpdateScenesBox(true); + + // If owned by this player, then set update base building budget for this Scene + float budget = ((float)m_pSceneBudgetSlider->GetValue() / 100.0f) * g_MetaMan.m_Players[metaPlayer].GetFunds(); + if (m_pSelectedScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer)) { + m_pSelectedScene->SetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), budget); + } + // Site owned by enemy player, update the attack budget + else if (g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())) { + g_MetaMan.m_Players[metaPlayer].SetOffensiveBudget(budget); + g_MetaMan.m_Players[metaPlayer].SetOffensiveTargetName(m_pSelectedScene->GetPresetName()); + } + // Unowned site, update the expedition budget + else { + g_MetaMan.m_Players[metaPlayer].SetOffensiveBudget(budget); + g_MetaMan.m_Players[metaPlayer].SetOffensiveTargetName(m_pSelectedScene->GetPresetName()); + } + } + } + + // Also stop dragging any panels if we're over any button + m_pDraggedBox = 0; + m_EngageDrag = true; + } + + // Checkbox for changing the auto design setting of a scene changed; update the actual scene to reflect this + if (anEvent.GetControl() == m_pAutoDesignCheckbox && m_pSelectedScene) { + if (anEvent.GetMsg() == GUICheckbox::Changed) + m_pSelectedScene->SetAutoDesigned(m_pAutoDesignCheckbox->GetCheck()); + } // Game Size slider changed - if(anEvent.GetControl() == m_pSizeSlider || anEvent.GetControl() == m_pGoldSlider || anEvent.GetControl() == m_pLengthSlider || anEvent.GetControl() == m_pDifficultySlider) - UpdateGameSizeLabels(); - - if(anEvent.GetControl() == m_apPlayerAISkillSlider[Players::PlayerOne]) - UpdateAISkillSliders(Players::PlayerOne); - if(anEvent.GetControl() == m_apPlayerAISkillSlider[Players::PlayerTwo]) - UpdateAISkillSliders(Players::PlayerTwo); - if(anEvent.GetControl() == m_apPlayerAISkillSlider[Players::PlayerThree]) - UpdateAISkillSliders(Players::PlayerThree); - if(anEvent.GetControl() == m_apPlayerAISkillSlider[Players::PlayerFour]) - UpdateAISkillSliders(Players::PlayerFour); - -/* Dun work so well, only if hovering naked box - // If hovering over any player bar, show the site lines going out from it - for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) - { - if (anEvent.GetControl() == m_apPlayerBox[metaPlayer]) - m_ActivePlayerIncomeLines = metaPlayer; - } -*/ - // Player flag selection changed - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (anEvent.GetControl() == m_apPlayerTeamSelect[player] && anEvent.GetMsg() == GUIComboBox::Closed) - { -/* Not relevant anymore, more than one Player can be of the same Team - // If some flag has been selected, make sure all other lists don't show the currently selected icon - if (m_apPlayerTeamSelect[player]->GetSelectedItem() && m_apPlayerTeamSelect[player]->GetSelectedItem()->m_pEntity) - { - for (int otherPlayer = Players::PlayerOne; otherPlayer < Players::MaxPlayerCount; ++otherPlayer) - { - if (player != otherPlayer && m_apPlayerTeamSelect[otherPlayer]->GetSelectedItem() && m_apPlayerTeamSelect[otherPlayer]->GetSelectedItem()->m_pEntity) - { - // They're the same, so swap them so they'll all stay unique - if (m_apPlayerTeamSelect[player]->GetSelectedItem()->m_pEntity->GetPresetName() == m_apPlayerTeamSelect[otherPlayer]->GetSelectedItem()->m_pEntity->GetPresetName()) - { - // Gotto use the old selection previous to the list opening - int swapIndex = m_apPlayerTeamSelect[player]->GetOldSelectionIndex(); - m_apPlayerTeamSelect[player]->SetSelectedIndex(m_apPlayerTeamSelect[otherPlayer]->GetSelectedIndex()); - m_apPlayerTeamSelect[otherPlayer]->SetSelectedIndex(swapIndex); - } - } - } - } -*/ + if (anEvent.GetControl() == m_pSizeSlider || anEvent.GetControl() == m_pGoldSlider || anEvent.GetControl() == m_pLengthSlider || anEvent.GetControl() == m_pDifficultySlider) + UpdateGameSizeLabels(); + + if (anEvent.GetControl() == m_apPlayerAISkillSlider[Players::PlayerOne]) + UpdateAISkillSliders(Players::PlayerOne); + if (anEvent.GetControl() == m_apPlayerAISkillSlider[Players::PlayerTwo]) + UpdateAISkillSliders(Players::PlayerTwo); + if (anEvent.GetControl() == m_apPlayerAISkillSlider[Players::PlayerThree]) + UpdateAISkillSliders(Players::PlayerThree); + if (anEvent.GetControl() == m_apPlayerAISkillSlider[Players::PlayerFour]) + UpdateAISkillSliders(Players::PlayerFour); + + /* Dun work so well, only if hovering naked box + // If hovering over any player bar, show the site lines going out from it + for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) + { + if (anEvent.GetControl() == m_apPlayerBox[metaPlayer]) + m_ActivePlayerIncomeLines = metaPlayer; + } + */ + // Player flag selection changed + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (anEvent.GetControl() == m_apPlayerTeamSelect[player] && anEvent.GetMsg() == GUIComboBox::Closed) { + /* Not relevant anymore, more than one Player can be of the same Team + // If some flag has been selected, make sure all other lists don't show the currently selected icon + if (m_apPlayerTeamSelect[player]->GetSelectedItem() && m_apPlayerTeamSelect[player]->GetSelectedItem()->m_pEntity) + { + for (int otherPlayer = Players::PlayerOne; otherPlayer < Players::MaxPlayerCount; ++otherPlayer) + { + if (player != otherPlayer && m_apPlayerTeamSelect[otherPlayer]->GetSelectedItem() && m_apPlayerTeamSelect[otherPlayer]->GetSelectedItem()->m_pEntity) + { + // They're the same, so swap them so they'll all stay unique + if (m_apPlayerTeamSelect[player]->GetSelectedItem()->m_pEntity->GetPresetName() == m_apPlayerTeamSelect[otherPlayer]->GetSelectedItem()->m_pEntity->GetPresetName()) + { + // Gotto use the old selection previous to the list opening + int swapIndex = m_apPlayerTeamSelect[player]->GetOldSelectionIndex(); + m_apPlayerTeamSelect[player]->SetSelectedIndex(m_apPlayerTeamSelect[otherPlayer]->GetSelectedIndex()); + m_apPlayerTeamSelect[otherPlayer]->SetSelectedIndex(swapIndex); + } + } + } + } + */ // Update sliders UpdateAISkillSliders(player); - UpdatePlayerSetup(); -// g_GUISound.ButtonPressSound()->Play(); - } - } - - // Load game combo box selection changed - if (anEvent.GetControl() == m_pSavesToLoadCombo && anEvent.GetMsg() == GUIComboBox::Closed) - { - // Get a handle to the actual game save Entity so we can extract its info - const MetaSave *pGame = 0; - GUIListPanel::Item *pItem = m_pSavesToLoadCombo->GetSelectedItem(); - if (pItem) - pGame = dynamic_cast(pItem->m_pEntity); - if (pGame) - { - // Update the game stats info box with the info of the selected game - char info[512]; + UpdatePlayerSetup(); + // g_GUISound.ButtonPressSound()->Play(); + } + } + + // Load game combo box selection changed + if (anEvent.GetControl() == m_pSavesToLoadCombo && anEvent.GetMsg() == GUIComboBox::Closed) { + // Get a handle to the actual game save Entity so we can extract its info + const MetaSave* pGame = 0; + GUIListPanel::Item* pItem = m_pSavesToLoadCombo->GetSelectedItem(); + if (pItem) + pGame = dynamic_cast(pItem->m_pEntity); + if (pGame) { + // Update the game stats info box with the info of the selected game + char info[512]; std::string difficultyString; if (pGame->GetDifficulty() < Activity::CakeDifficulty) @@ -2698,30 +2473,28 @@ void MetagameGUI::UpdateInput() else difficultyString = "Difficulty: Nuts!"; - std::snprintf(info, sizeof(info), "Game Size: %d sites\nTotal Players: %d\nDay: %d\n%s", pGame->GetSiteCount(), pGame->GetPlayerCount(), pGame->GetRoundCount() + 1, difficultyString.c_str()); - m_pLoadInfoLabel->SetText(info); - // Show the Load button since we have one locked in - m_apMetaButton[LOADNOW]->SetVisible(true); - } - // Hide the Load button if we don't have a valid game selected - else - m_apMetaButton[LOADNOW]->SetVisible(false); - // Play a ding - g_GUISound.ItemChangeSound()->Play(); - } - - // Save overwrite combo box selection changed - if (anEvent.GetControl() == m_pSavesToOverwriteCombo && anEvent.GetMsg() == GUIComboBox::Closed) - { - // Get a handle to the actual game save Entity so we can extract its info - const MetaSave *pGame = 0; - GUIListPanel::Item *pItem = m_pSavesToOverwriteCombo->GetSelectedItem(); - if (pItem) - pGame = dynamic_cast(pItem->m_pEntity); - if (pGame) - { - // Update the game stats info box with the info of the selected game - char info[512]; + std::snprintf(info, sizeof(info), "Game Size: %d sites\nTotal Players: %d\nDay: %d\n%s", pGame->GetSiteCount(), pGame->GetPlayerCount(), pGame->GetRoundCount() + 1, difficultyString.c_str()); + m_pLoadInfoLabel->SetText(info); + // Show the Load button since we have one locked in + m_apMetaButton[LOADNOW]->SetVisible(true); + } + // Hide the Load button if we don't have a valid game selected + else + m_apMetaButton[LOADNOW]->SetVisible(false); + // Play a ding + g_GUISound.ItemChangeSound()->Play(); + } + + // Save overwrite combo box selection changed + if (anEvent.GetControl() == m_pSavesToOverwriteCombo && anEvent.GetMsg() == GUIComboBox::Closed) { + // Get a handle to the actual game save Entity so we can extract its info + const MetaSave* pGame = 0; + GUIListPanel::Item* pItem = m_pSavesToOverwriteCombo->GetSelectedItem(); + if (pItem) + pGame = dynamic_cast(pItem->m_pEntity); + if (pGame) { + // Update the game stats info box with the info of the selected game + char info[512]; std::string difficultyString; if (pGame->GetDifficulty() < Activity::CakeDifficulty) @@ -2737,118 +2510,107 @@ void MetagameGUI::UpdateInput() else difficultyString = "Difficulty: Nuts!"; - std::snprintf(info, sizeof(info), "Game Size: %d sites\nTotal Players: %d\nDay: %d\n%s", pGame->GetSiteCount(), pGame->GetPlayerCount(), pGame->GetRoundCount() + 1, difficultyString.c_str()); - m_pSaveInfoLabel->SetText(info); - m_pSaveInfoLabel->SetVisible(true); - // Show the Save button since we have one to overwrite locked in - m_apMetaButton[SAVENOW]->SetVisible(true); - m_apMetaButton[SAVENOW]->SetText("Save"); - } - // Hide the Save button if we don't have a valid game selected, AND no new save name to use - else if (m_NewSaveBox->GetText().empty()) - { - m_pSaveInfoLabel->SetVisible(false); - m_apMetaButton[SAVENOW]->SetVisible(false); - } - - // Play a ding - g_GUISound.ItemChangeSound()->Play(); - } - } - } + std::snprintf(info, sizeof(info), "Game Size: %d sites\nTotal Players: %d\nDay: %d\n%s", pGame->GetSiteCount(), pGame->GetPlayerCount(), pGame->GetRoundCount() + 1, difficultyString.c_str()); + m_pSaveInfoLabel->SetText(info); + m_pSaveInfoLabel->SetVisible(true); + // Show the Save button since we have one to overwrite locked in + m_apMetaButton[SAVENOW]->SetVisible(true); + m_apMetaButton[SAVENOW]->SetText("Save"); + } + // Hide the Save button if we don't have a valid game selected, AND no new save name to use + else if (m_NewSaveBox->GetText().empty()) { + m_pSaveInfoLabel->SetVisible(false); + m_apMetaButton[SAVENOW]->SetVisible(false); + } + + // Play a ding + g_GUISound.ItemChangeSound()->Play(); + } + } + } // Phase advance button pressed; set the signal flag at the end of input handling - if (phaseButtonPressed) - { - // If we're in a pre-player-turn state, then continue out of it first - if (m_PreTurn && g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) - m_PreTurn = false; - // If we/re going through offensive ativities, then that's what the continue button starts - else if (m_PreTurn && g_MetaMan.m_GameState == MetaMan::RUNACTIVITIES) - m_PreTurn = false; - else if (g_MetaMan.m_GameState == MetaMan::GAMEOVER) - SwitchToScreen(MENUDIALOG); - else - m_ContinuePhase = true; - g_GUISound.ButtonPressSound()->Play(); - } + if (phaseButtonPressed) { + // If we're in a pre-player-turn state, then continue out of it first + if (m_PreTurn && g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) + m_PreTurn = false; + // If we/re going through offensive ativities, then that's what the continue button starts + else if (m_PreTurn && g_MetaMan.m_GameState == MetaMan::RUNACTIVITIES) + m_PreTurn = false; + else if (g_MetaMan.m_GameState == MetaMan::GAMEOVER) + SwitchToScreen(MENUDIALOG); + else + m_ContinuePhase = true; + g_GUISound.ButtonPressSound()->Play(); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: HideAllScreens ////////////////////////////////////////////////////////////////////////////////////////// // Description: Hides all menu screens, so one can easily be unhidden and shown only. -void MetagameGUI::HideAllScreens() -{ - for (int iscreen = 0; iscreen < SCREENCOUNT; ++iscreen) - { - if (m_apScreenBox[iscreen] && iscreen != ROOTBOX) - m_apScreenBox[iscreen]->SetVisible(false); - } - - // Also hide the player bars - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - m_apPlayerBox[player]->SetVisible(false); - m_apBrainPoolLabel[player]->SetVisible(false); - m_apFundsChangeLabel[player]->SetVisible(false); - m_apBrainChangeLabel[player]->SetVisible(false); - m_apPlayerTeamActionBox[player]->SetVisible(false); - m_apPlayerBrainTravelLabel[player]->SetVisible(false); - } - - // Message label too - m_pGameMessageLabel->SetVisible(false); - - // And the phase, scene info, and confirmation boxes - m_pPhaseBox->SetVisible(false); - m_pSceneInfoPopup->SetVisible(false); - m_pConfirmationBox->SetVisible(false); - UpdateSiteNameLabel(false); - - // And the big text banners - m_pBannerRedTop->HideText(); - m_pBannerRedBottom->HideText(); - m_pBannerYellowTop->HideText(); - m_pBannerYellowBottom->HideText(); - - m_ScreenChange = true; -} +void MetagameGUI::HideAllScreens() { + for (int iscreen = 0; iscreen < SCREENCOUNT; ++iscreen) { + if (m_apScreenBox[iscreen] && iscreen != ROOTBOX) + m_apScreenBox[iscreen]->SetVisible(false); + } + + // Also hide the player bars + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + m_apPlayerBox[player]->SetVisible(false); + m_apBrainPoolLabel[player]->SetVisible(false); + m_apFundsChangeLabel[player]->SetVisible(false); + m_apBrainChangeLabel[player]->SetVisible(false); + m_apPlayerTeamActionBox[player]->SetVisible(false); + m_apPlayerBrainTravelLabel[player]->SetVisible(false); + } + + // Message label too + m_pGameMessageLabel->SetVisible(false); + + // And the phase, scene info, and confirmation boxes + m_pPhaseBox->SetVisible(false); + m_pSceneInfoPopup->SetVisible(false); + m_pConfirmationBox->SetVisible(false); + UpdateSiteNameLabel(false); + // And the big text banners + m_pBannerRedTop->HideText(); + m_pBannerRedBottom->HideText(); + m_pBannerYellowTop->HideText(); + m_pBannerYellowBottom->HideText(); + + m_ScreenChange = true; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: KeepBoxOnScreen ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes sure a specific box doesn't end up moved completely off-screen. -void MetagameGUI::KeepBoxOnScreen(GUICollectionBox *pBox, int margin) -{ - if (margin < 0) - { - // Use the dimensions of the box itself to prevent it from at all going outside the screen - if (pBox->GetXPos() < 0) - pBox->SetPositionAbs(0, pBox->GetYPos()); - if (pBox->GetXPos() > m_apScreenBox[ROOTBOX]->GetWidth() - pBox->GetWidth()) - pBox->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth() - pBox->GetWidth(), pBox->GetYPos()); - if (pBox->GetYPos() < 0) - pBox->SetPositionAbs(pBox->GetXPos(), 0); - if (pBox->GetYPos() > m_apScreenBox[ROOTBOX]->GetHeight() - pBox->GetHeight()) - pBox->SetPositionAbs(pBox->GetXPos(), m_apScreenBox[ROOTBOX]->GetHeight() - pBox->GetHeight()); - } - else - { - // Make sure the box doesn't go entirely outside of the screen - if (pBox->GetXPos() < (-pBox->GetWidth() + margin)) - pBox->SetPositionAbs(-pBox->GetWidth() + margin, pBox->GetYPos()); - if (pBox->GetXPos() > m_apScreenBox[ROOTBOX]->GetWidth() - margin) - pBox->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth() - margin, pBox->GetYPos()); - if (pBox->GetYPos() < (-pBox->GetHeight() + margin)) - pBox->SetPositionAbs(pBox->GetXPos(), -pBox->GetHeight() + margin); - if (pBox->GetYPos() > m_apScreenBox[ROOTBOX]->GetHeight() - margin) - pBox->SetPositionAbs(pBox->GetXPos(), m_apScreenBox[ROOTBOX]->GetHeight() - margin); - } +void MetagameGUI::KeepBoxOnScreen(GUICollectionBox* pBox, int margin) { + if (margin < 0) { + // Use the dimensions of the box itself to prevent it from at all going outside the screen + if (pBox->GetXPos() < 0) + pBox->SetPositionAbs(0, pBox->GetYPos()); + if (pBox->GetXPos() > m_apScreenBox[ROOTBOX]->GetWidth() - pBox->GetWidth()) + pBox->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth() - pBox->GetWidth(), pBox->GetYPos()); + if (pBox->GetYPos() < 0) + pBox->SetPositionAbs(pBox->GetXPos(), 0); + if (pBox->GetYPos() > m_apScreenBox[ROOTBOX]->GetHeight() - pBox->GetHeight()) + pBox->SetPositionAbs(pBox->GetXPos(), m_apScreenBox[ROOTBOX]->GetHeight() - pBox->GetHeight()); + } else { + // Make sure the box doesn't go entirely outside of the screen + if (pBox->GetXPos() < (-pBox->GetWidth() + margin)) + pBox->SetPositionAbs(-pBox->GetWidth() + margin, pBox->GetYPos()); + if (pBox->GetXPos() > m_apScreenBox[ROOTBOX]->GetWidth() - margin) + pBox->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth() - margin, pBox->GetYPos()); + if (pBox->GetYPos() < (-pBox->GetHeight() + margin)) + pBox->SetPositionAbs(pBox->GetXPos(), -pBox->GetHeight() + margin); + if (pBox->GetYPos() > m_apScreenBox[ROOTBOX]->GetHeight() - margin) + pBox->SetPositionAbs(pBox->GetXPos(), m_apScreenBox[ROOTBOX]->GetHeight() - margin); + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -2857,148 +2619,137 @@ void MetagameGUI::KeepBoxOnScreen(GUICollectionBox *pBox, int margin) // Description: Handles what happens after an Activity within the Metagame was // run and completed fully. -void MetagameGUI::CompletedActivity() -{ - Activity *pDoneActivity = dynamic_cast(g_ActivityMan.GetActivity()); - GAScripted *pDoneScriptedActivity = dynamic_cast(g_ActivityMan.GetActivity()); - Scene *pAlteredScene = g_SceneMan.GetScene(); - bool autoResolved = false; - int winningTeam = Activity::NoTeam; - - // Retain any changes done to the Scene just played to the one that is kept by the MetaMan session - if (pDoneActivity && pAlteredScene && m_pPlayingScene) - { - // There was an error with the activity and it bailed.. try to recover - if (pDoneActivity->GetActivityState() == Activity::HasError) - { - m_AnimTimer2.Reset(); - m_apMetaButton[CONTINUE]->SetText("Start!"); - m_PreTurn = true; - } - // If a MetaFight activity isn't over yet, that means we're just pausing and will resume - else if (pDoneScriptedActivity && pDoneScriptedActivity->GetLuaClassName() == "MetaFight" && !pDoneActivity->IsOver()) - { - m_AnimTimer2.Reset(); - m_apMetaButton[CONTINUE]->SetText("Resume"); - m_BattleToResume = true; - m_PreTurn = true; - } - // All types of activites should be saving their scenes to disk - objects may have placed/stamped onto their layers - else - { - // Coming out of a base design session - if (pDoneActivity->GetClassName() == "BaseEditor") - { - // ? - } - // Site scan session - else if (pDoneScriptedActivity && pDoneScriptedActivity->GetLuaClassName() == "SiteScan") - { - // ? - } - // Offensive action session needs some things done after a battle is concluded - else if (pDoneScriptedActivity && pDoneScriptedActivity->GetLuaClassName() == "MetaFight") - { - if (pDoneActivity->IsOver()) - { - // If this ended for whatever reason without a winning team, then resolve the rest of the fight automatically - if (pDoneScriptedActivity->GetWinnerTeam() == Activity::NoTeam)//pDoneActivity->HumanBrainCount() == 0) - { - // Resolve the rest of the fight between the AI guys and display the outcome - m_BattleCausedOwnershipChange = AutoResolveOffensive(pDoneScriptedActivity, pAlteredScene, true); - - autoResolved = true; - winningTeam = pDoneScriptedActivity->GetWinnerTeam(); - } - // If it ended and is truly over, see if it resulted in a team ownership change - else - m_BattleCausedOwnershipChange = m_pPlayingScene->GetTeamOwnership() != pAlteredScene->GetTeamOwnership(); - - // Copy the completed activity back on the one in the queue that it started as, so we can grab parameters for the post battle review - g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->Destroy(); - g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->Create(*pDoneScriptedActivity); - // Scrub the module ID so the migration goes well.. this is a bit hacky, but ok in this special case - g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->SetModuleID(-1); - // Remind the Offensive that it is a unique snowflake and should save itself as such - g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->MigrateToModule(g_PresetMan.GetModuleID(METASAVEMODULENAME)); - - // We are now reviewing the battle after it has concluded; UpdateOffensives will show all that stuff - m_PreTurn = true; - m_PostBattleReview = true; - // Pause for a little to allow players to reorient themselves visually on the new screen before animaitons start happening - ChangeAnimMode(SHOWPOSTBATTLEPAUSE); - } - } - - // Transfer back the brain to the Scene as a resident AND collect the remaining funds left over - // UNLESS we're editing the base, which handles the brains specially by not actually placing them into the scene, but always keeping them as residents in the scene - // Also BaseEditor doesn't use funds at all, so there's nothing to collect - if (pDoneActivity->GetClassName() != "BaseEditor") - { - // Don't mess with this if activity was automatically resolved - then the resident brains and funds adjustments are already taken care of - if (!autoResolved) - pAlteredScene->RetrieveResidentBrains(*pDoneActivity); -/* Later in FinalizeOffensive - // Deduct the original funds contribution of each player - less any unused funds of the team, taking original player contribution ratios into account - for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - { -// ANIMATE THIS NICELY INSTEAD - // Deduct the player's original contribution to team funds - g_MetaMan.m_Players[metaPlayer].m_Funds -= pDoneActivity->GetPlayerFundsContribution(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()); - // Add back whatever his share of whatever his team has left in the fight at its end - g_MetaMan.m_Players[metaPlayer].m_Funds += pDoneActivity->GetPlayerFundsShare(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()); - // IF This guy was ATTACKING this turn, adjust his attack budget to match what just happened in the battle - // so in case he is defending in a upcoming battle this turn, his avaialbe funds will be accurately calculated - if (g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName() == m_pAnimScene->GetPresetName()) - { - g_MetaMan.m_Players[metaPlayer].SetOffensiveBudget(g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsShare(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer())); -// g_MetaMan.m_Players[metaPlayer].SetOffensiveTargetName(""); - } - // Update the ratios of the meter now that the funds have changed - UpdatePlayerLineRatios(m_ActionSiteLines[metaPlayer], metaPlayer, false, g_MetaMan.m_Players[metaPlayer].m_Funds); - } -*/ - } - // Suck up all the remaining Actors and Items left in the world and put them into the list to place next load - // However, don't suck up actors of any non-winning team, and don't save the brains if we autoresolved, because that took care of placing the resident brains already - pAlteredScene->RetrieveSceneObjects(true, winningTeam, autoResolved); - // Save out the altered scene before clearing out its data from memory - pAlteredScene->SaveData(METASAVEPATH + std::string(AUTOSAVENAME) + " - " + pAlteredScene->GetPresetName(), false); - // Clear the bitmap data etc of the altered scene, we don't need to copy that over - pAlteredScene->ClearData(); +void MetagameGUI::CompletedActivity() { + Activity* pDoneActivity = dynamic_cast(g_ActivityMan.GetActivity()); + GAScripted* pDoneScriptedActivity = dynamic_cast(g_ActivityMan.GetActivity()); + Scene* pAlteredScene = g_SceneMan.GetScene(); + bool autoResolved = false; + int winningTeam = Activity::NoTeam; + + // Retain any changes done to the Scene just played to the one that is kept by the MetaMan session + if (pDoneActivity && pAlteredScene && m_pPlayingScene) { + // There was an error with the activity and it bailed.. try to recover + if (pDoneActivity->GetActivityState() == Activity::HasError) { + m_AnimTimer2.Reset(); + m_apMetaButton[CONTINUE]->SetText("Start!"); + m_PreTurn = true; + } + // If a MetaFight activity isn't over yet, that means we're just pausing and will resume + else if (pDoneScriptedActivity && pDoneScriptedActivity->GetLuaClassName() == "MetaFight" && !pDoneActivity->IsOver()) { + m_AnimTimer2.Reset(); + m_apMetaButton[CONTINUE]->SetText("Resume"); + m_BattleToResume = true; + m_PreTurn = true; + } + // All types of activites should be saving their scenes to disk - objects may have placed/stamped onto their layers + else { + // Coming out of a base design session + if (pDoneActivity->GetClassName() == "BaseEditor") { + // ? + } + // Site scan session + else if (pDoneScriptedActivity && pDoneScriptedActivity->GetLuaClassName() == "SiteScan") { + // ? + } + // Offensive action session needs some things done after a battle is concluded + else if (pDoneScriptedActivity && pDoneScriptedActivity->GetLuaClassName() == "MetaFight") { + if (pDoneActivity->IsOver()) { + // If this ended for whatever reason without a winning team, then resolve the rest of the fight automatically + if (pDoneScriptedActivity->GetWinnerTeam() == Activity::NoTeam) // pDoneActivity->HumanBrainCount() == 0) + { + // Resolve the rest of the fight between the AI guys and display the outcome + m_BattleCausedOwnershipChange = AutoResolveOffensive(pDoneScriptedActivity, pAlteredScene, true); + + autoResolved = true; + winningTeam = pDoneScriptedActivity->GetWinnerTeam(); + } + // If it ended and is truly over, see if it resulted in a team ownership change + else + m_BattleCausedOwnershipChange = m_pPlayingScene->GetTeamOwnership() != pAlteredScene->GetTeamOwnership(); + + // Copy the completed activity back on the one in the queue that it started as, so we can grab parameters for the post battle review + g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->Destroy(); + g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->Create(*pDoneScriptedActivity); + // Scrub the module ID so the migration goes well.. this is a bit hacky, but ok in this special case + g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->SetModuleID(-1); + // Remind the Offensive that it is a unique snowflake and should save itself as such + g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->MigrateToModule(g_PresetMan.GetModuleID(METASAVEMODULENAME)); + + // We are now reviewing the battle after it has concluded; UpdateOffensives will show all that stuff + m_PreTurn = true; + m_PostBattleReview = true; + // Pause for a little to allow players to reorient themselves visually on the new screen before animaitons start happening + ChangeAnimMode(SHOWPOSTBATTLEPAUSE); + } + } + + // Transfer back the brain to the Scene as a resident AND collect the remaining funds left over + // UNLESS we're editing the base, which handles the brains specially by not actually placing them into the scene, but always keeping them as residents in the scene + // Also BaseEditor doesn't use funds at all, so there's nothing to collect + if (pDoneActivity->GetClassName() != "BaseEditor") { + // Don't mess with this if activity was automatically resolved - then the resident brains and funds adjustments are already taken care of + if (!autoResolved) + pAlteredScene->RetrieveResidentBrains(*pDoneActivity); + /* Later in FinalizeOffensive + // Deduct the original funds contribution of each player - less any unused funds of the team, taking original player contribution ratios into account + for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) + { + // ANIMATE THIS NICELY INSTEAD + // Deduct the player's original contribution to team funds + g_MetaMan.m_Players[metaPlayer].m_Funds -= pDoneActivity->GetPlayerFundsContribution(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()); + // Add back whatever his share of whatever his team has left in the fight at its end + g_MetaMan.m_Players[metaPlayer].m_Funds += pDoneActivity->GetPlayerFundsShare(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()); + // IF This guy was ATTACKING this turn, adjust his attack budget to match what just happened in the battle + // so in case he is defending in a upcoming battle this turn, his avaialbe funds will be accurately calculated + if (g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName() == m_pAnimScene->GetPresetName()) + { + g_MetaMan.m_Players[metaPlayer].SetOffensiveBudget(g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsShare(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer())); + // g_MetaMan.m_Players[metaPlayer].SetOffensiveTargetName(""); + } + // Update the ratios of the meter now that the funds have changed + UpdatePlayerLineRatios(m_ActionSiteLines[metaPlayer], metaPlayer, false, g_MetaMan.m_Players[metaPlayer].m_Funds); + } + */ + } + // Suck up all the remaining Actors and Items left in the world and put them into the list to place next load + // However, don't suck up actors of any non-winning team, and don't save the brains if we autoresolved, because that took care of placing the resident brains already + pAlteredScene->RetrieveSceneObjects(true, winningTeam, autoResolved); + // Save out the altered scene before clearing out its data from memory + pAlteredScene->SaveData(METASAVEPATH + std::string(AUTOSAVENAME) + " - " + pAlteredScene->GetPresetName(), false); + // Clear the bitmap data etc of the altered scene, we don't need to copy that over + pAlteredScene->ClearData(); // Clear waypoints on resident brains, otherwise when they're cloned they try to update their move paths, which requires a material bitmap (which we destroyed when we cleared data above). for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - if (Actor *residentBrain = dynamic_cast(pAlteredScene->GetResidentBrain(player))) { + if (Actor* residentBrain = dynamic_cast(pAlteredScene->GetResidentBrain(player))) { residentBrain->ClearAIWaypoints(); } } - // Deep copy over all the edits made to the newly played Scene - m_pPlayingScene->Destroy(); - m_pPlayingScene->Create(*pAlteredScene); - // Null the current scene, which is pointed to by pAlteredScene. - g_SceneMan.ClearCurrentScene(); - // Scrub the module ID so the migration goes well.. this is a bit hacky, but ok in this special case - m_pPlayingScene->SetModuleID(-1); - m_pPlayingScene->GetTerrain()->SetModuleID(-1); - // Remind the Scene that it is a unique snowflake and should save itself as such - m_pPlayingScene->MigrateToModule(g_PresetMan.GetModuleID(METASAVEMODULENAME)); - // We're not playing this anymore - m_pPlayingScene = 0; - // Auto save the entire MetaMan state too -// Do this later in FinalizeOffensive, since the funds change until then -// SaveGame(AUTOSAVENAME, METASAVEPATH + string(AUTOSAVENAME) + ".ini", false); - - // Update the Scene info box since the scene might have changed - UpdateScenesBox(true); - - // Play some nice ambient music - g_AudioMan.PlayMusic("Base.rte/Music/Hubnester/ccmenu.ogg", -1, 0.4); - } - } + // Deep copy over all the edits made to the newly played Scene + m_pPlayingScene->Destroy(); + m_pPlayingScene->Create(*pAlteredScene); + // Null the current scene, which is pointed to by pAlteredScene. + g_SceneMan.ClearCurrentScene(); + // Scrub the module ID so the migration goes well.. this is a bit hacky, but ok in this special case + m_pPlayingScene->SetModuleID(-1); + m_pPlayingScene->GetTerrain()->SetModuleID(-1); + // Remind the Scene that it is a unique snowflake and should save itself as such + m_pPlayingScene->MigrateToModule(g_PresetMan.GetModuleID(METASAVEMODULENAME)); + // We're not playing this anymore + m_pPlayingScene = 0; + // Auto save the entire MetaMan state too + // Do this later in FinalizeOffensive, since the funds change until then + // SaveGame(AUTOSAVENAME, METASAVEPATH + string(AUTOSAVENAME) + ".ini", false); + + // Update the Scene info box since the scene might have changed + UpdateScenesBox(true); + + // Play some nice ambient music + g_AudioMan.PlayMusic("Base.rte/Music/Hubnester/ccmenu.ogg", -1, 0.4); + } + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: AutoResolveOffensive ////////////////////////////////////////////////////////////////////////////////////////// @@ -3006,439 +2757,391 @@ void MetagameGUI::CompletedActivity() // and going through an Activity. Will randomly determine who won and // what the consequences are. -bool MetagameGUI::AutoResolveOffensive(GAScripted *pOffensive, Scene *pScene, bool brainCheck) -{ - bool changedOwnership = false; - const Loadout *pLoadout = 0; - Actor *pBrain = 0; - float cost = 0; -/* - // Find the scene being attacked in this offensive Activity - Scene *pScene = 0; - for (vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - if ((*sItr)->IsRevealed() && (*sItr)->GetPresetName() == pOffensive->GetSceneName()) - pScene = (*sItr); - } -*/ - RTEAssert(pScene, "Couldn't find the Site that has been selected as auto resolution of an attack!"); - - // Check all players for active brains, and deactivate them if they don't have them - if (brainCheck) - { - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - // Deactivate any players which had brains but don't anymore - ie they died and are not in the game anymore - if (pOffensive->PlayerActive(player) && pOffensive->PlayerHadBrain(player) && !pOffensive->GetPlayerBrain(player)) - pOffensive->DeactivatePlayer(player); - } - } - // If we're not making this check, we should assume all active players had a brain at some point - // This is effectively a simluation for pure offensive activities where no human player was ever involved - else - { - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - // Simulate that all active players had a brain at some point - if (pOffensive->PlayerActive(player)) - pOffensive->SetPlayerHadBrain(player, true); - } - } - - // SINGLE player going to an unowned site.. it will be always be taken over, at some base cost of a brain landing - if (pOffensive->GetPlayerCount() == 1) - { - // Find out which single player is visiting this place, and grant him victory - int activePlayer = Players::NoPlayer; - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (pOffensive->PlayerActive(player)) - { - activePlayer = player; - break; - } - } - // Add a resident brain to this scene for the AI player, based on the metaplayer's Loadout corresponding to its tech - MetaPlayer *pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(activePlayer); - if (pMetaPlayer) - { - // NOTE: Brain pool resource counter gets adjusted down in FinalizeOffense - - // Find the player's tech's brain, and instantiate it - if (pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Brain", pMetaPlayer->GetNativeTechModule()))) - pBrain = pLoadout->CreateFirstActor(pMetaPlayer->GetNativeTechModule(), pMetaPlayer->GetForeignCostMultiplier(), pMetaPlayer->GetNativeCostMultiplier(), cost); - // Pass the instance and ownership thereof to the scene as a resident - if (pBrain) - { - // Set a pos outside the Scene so it'll be gracefully placed later when Scene's Terrain is actually loaded - pBrain->SetPos(Vector(-1, -1)); +bool MetagameGUI::AutoResolveOffensive(GAScripted* pOffensive, Scene* pScene, bool brainCheck) { + bool changedOwnership = false; + const Loadout* pLoadout = 0; + Actor* pBrain = 0; + float cost = 0; + /* + // Find the scene being attacked in this offensive Activity + Scene *pScene = 0; + for (vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) + { + if ((*sItr)->IsRevealed() && (*sItr)->GetPresetName() == pOffensive->GetSceneName()) + pScene = (*sItr); + } + */ + RTEAssert(pScene, "Couldn't find the Site that has been selected as auto resolution of an attack!"); + + // Check all players for active brains, and deactivate them if they don't have them + if (brainCheck) { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + // Deactivate any players which had brains but don't anymore - ie they died and are not in the game anymore + if (pOffensive->PlayerActive(player) && pOffensive->PlayerHadBrain(player) && !pOffensive->GetPlayerBrain(player)) + pOffensive->DeactivatePlayer(player); + } + } + // If we're not making this check, we should assume all active players had a brain at some point + // This is effectively a simluation for pure offensive activities where no human player was ever involved + else { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + // Simulate that all active players had a brain at some point + if (pOffensive->PlayerActive(player)) + pOffensive->SetPlayerHadBrain(player, true); + } + } + + // SINGLE player going to an unowned site.. it will be always be taken over, at some base cost of a brain landing + if (pOffensive->GetPlayerCount() == 1) { + // Find out which single player is visiting this place, and grant him victory + int activePlayer = Players::NoPlayer; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (pOffensive->PlayerActive(player)) { + activePlayer = player; + break; + } + } + // Add a resident brain to this scene for the AI player, based on the metaplayer's Loadout corresponding to its tech + MetaPlayer* pMetaPlayer = g_MetaMan.GetMetaPlayerOfInGamePlayer(activePlayer); + if (pMetaPlayer) { + // NOTE: Brain pool resource counter gets adjusted down in FinalizeOffense + + // Find the player's tech's brain, and instantiate it + if (pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Brain", pMetaPlayer->GetNativeTechModule()))) + pBrain = pLoadout->CreateFirstActor(pMetaPlayer->GetNativeTechModule(), pMetaPlayer->GetForeignCostMultiplier(), pMetaPlayer->GetNativeCostMultiplier(), cost); + // Pass the instance and ownership thereof to the scene as a resident + if (pBrain) { + // Set a pos outside the Scene so it'll be gracefully placed later when Scene's Terrain is actually loaded + pBrain->SetPos(Vector(-1, -1)); pBrain->SetTeam(pMetaPlayer->GetTeam()); - pScene->SetResidentBrain(activePlayer, pBrain); - // Set his team to own this place now - pScene->SetTeamOwnership(pOffensive->GetTeamOfPlayer(activePlayer)); - // Also declare winner of the activity - pOffensive->SetWinnerTeam(pOffensive->GetTeamOfPlayer(activePlayer)); - // Just mess with the funds; the metaplayers' funds will be affected afterward, according to their shares etc -// pOffensive->SetTeamFunds(pOffensive->GetTeamFunds(pOffensive->GetTeamOfPlayer(activePlayer)) * RandomNum(), pOffensive->GetTeamOfPlayer(activePlayer)); - // For now, just deduct the price of the brain - pOffensive->ChangeTeamFunds(-cost, pOffensive->GetTeamOfPlayer(activePlayer)); - // Signal that ownership of the site has changed - changedOwnership = true; - } - } - } - // MULTIPLE players are battling it out.. randomize the outcome - else - { - // First see if NO TEAM will get this - could be all brains die - if (RandomNum() < 0.05F) - { - // See if we should signal change of ownership - if (pScene->GetTeamOwnership() != Activity::NoTeam) - changedOwnership = true; - - // Eliminate all ownership of this place + pScene->SetResidentBrain(activePlayer, pBrain); + // Set his team to own this place now + pScene->SetTeamOwnership(pOffensive->GetTeamOfPlayer(activePlayer)); + // Also declare winner of the activity + pOffensive->SetWinnerTeam(pOffensive->GetTeamOfPlayer(activePlayer)); + // Just mess with the funds; the metaplayers' funds will be affected afterward, according to their shares etc + // pOffensive->SetTeamFunds(pOffensive->GetTeamFunds(pOffensive->GetTeamOfPlayer(activePlayer)) * RandomNum(), pOffensive->GetTeamOfPlayer(activePlayer)); + // For now, just deduct the price of the brain + pOffensive->ChangeTeamFunds(-cost, pOffensive->GetTeamOfPlayer(activePlayer)); + // Signal that ownership of the site has changed + changedOwnership = true; + } + } + } + // MULTIPLE players are battling it out.. randomize the outcome + else { + // First see if NO TEAM will get this - could be all brains die + if (RandomNum() < 0.05F) { + // See if we should signal change of ownership + if (pScene->GetTeamOwnership() != Activity::NoTeam) + changedOwnership = true; + + // Eliminate all ownership of this place pScene->RemoveAllPlacedActors(Activity::NoTeam); - pScene->SetTeamOwnership(Activity::NoTeam); - - // Each player's brain gets wiped out - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (pOffensive->PlayerActive(player)) - pScene->SetResidentBrain(player, 0); - } - } - // Okay, someone got this place - now figure out who - else - { - MetaPlayer * aMetaPlayers[Players::MaxPlayerCount]; - // The normalized scalar chances of each team to win - float aTeamChance[Activity::MaxTeamCount]; + pScene->SetTeamOwnership(Activity::NoTeam); + + // Each player's brain gets wiped out + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (pOffensive->PlayerActive(player)) + pScene->SetResidentBrain(player, 0); + } + } + // Okay, someone got this place - now figure out who + else { + MetaPlayer* aMetaPlayers[Players::MaxPlayerCount]; + // The normalized scalar chances of each team to win + float aTeamChance[Activity::MaxTeamCount]; for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) aTeamChance[team] = 0; - // The total number of 'chance' points that all teams have in aggregate - float totalPoints = 0; - - // Add the points representing the defense investment for the defenders - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - // Invested defenses counts for half the points.. some may have been destroyed, defense is harder etc - if (pOffensive->TeamActive(team) && pScene->GetTeamOwnership() == team) - aTeamChance[team] += pScene->GetTotalInvestment() / 2; - } - - // Now add the funds contributed by each player to each team's chance pool - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (pOffensive->PlayerActive(player)) - { - aMetaPlayers[player] = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); - // Add the funds contributed to this activity's attack or defense - if (aMetaPlayers[player]) - aTeamChance[aMetaPlayers[player]->GetTeam()] += pOffensive->GetPlayerFundsContribution(player); - } - } - - // Now tally up the total chance points - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - if (pOffensive->TeamActive(team)) - totalPoints += aTeamChance[team]; - } - // Normalize all the teams' chances - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - if (pOffensive->TeamActive(team)) - aTeamChance[team] = aTeamChance[team] / totalPoints; - } - // The deciding normalized scalar number - float decision = RandomNum(); - // Keeps track of the thresholds - float teamChanceTally = 0; - int winnerTeam = Activity::NoTeam; - // See who actually won, based on the respective chances - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - if (pOffensive->TeamActive(team)) - { - // Move the threshold forward - teamChanceTally += aTeamChance[team]; - // See if this team was chosen as winner - if (decision < teamChanceTally) - { - // Declare winner - winnerTeam = team; - // See if we should signal change of ownership - if (pScene->GetTeamOwnership() != winnerTeam) - changedOwnership = true; - // We have picked our winner and are done + // The total number of 'chance' points that all teams have in aggregate + float totalPoints = 0; + + // Add the points representing the defense investment for the defenders + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + // Invested defenses counts for half the points.. some may have been destroyed, defense is harder etc + if (pOffensive->TeamActive(team) && pScene->GetTeamOwnership() == team) + aTeamChance[team] += pScene->GetTotalInvestment() / 2; + } + + // Now add the funds contributed by each player to each team's chance pool + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (pOffensive->PlayerActive(player)) { + aMetaPlayers[player] = g_MetaMan.GetMetaPlayerOfInGamePlayer(player); + // Add the funds contributed to this activity's attack or defense + if (aMetaPlayers[player]) + aTeamChance[aMetaPlayers[player]->GetTeam()] += pOffensive->GetPlayerFundsContribution(player); + } + } + + // Now tally up the total chance points + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + if (pOffensive->TeamActive(team)) + totalPoints += aTeamChance[team]; + } + // Normalize all the teams' chances + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + if (pOffensive->TeamActive(team)) + aTeamChance[team] = aTeamChance[team] / totalPoints; + } + // The deciding normalized scalar number + float decision = RandomNum(); + // Keeps track of the thresholds + float teamChanceTally = 0; + int winnerTeam = Activity::NoTeam; + // See who actually won, based on the respective chances + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + if (pOffensive->TeamActive(team)) { + // Move the threshold forward + teamChanceTally += aTeamChance[team]; + // See if this team was chosen as winner + if (decision < teamChanceTally) { + // Declare winner + winnerTeam = team; + // See if we should signal change of ownership + if (pScene->GetTeamOwnership() != winnerTeam) + changedOwnership = true; + // We have picked our winner and are done // NOTE: Actual scene owners will be set below, along with removal of actors // and door replacements. - break; - } - } - } - - // Now change brain residencies if the ownership actually changed - if (changedOwnership) - { - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - pLoadout = 0; - pBrain = 0; - if (pOffensive->PlayerActive(player) && aMetaPlayers[player]) - { - // Winning player, so place a brain of his as a resident - if (aMetaPlayers[player]->GetTeam() == winnerTeam) - { - // NOTE: Brain pool resource counter gets adjusted down in FinalizeOffense - - // Find the player's tech's brain, and instantiate it - if (pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Brain", aMetaPlayers[player]->GetNativeTechModule()))) - pBrain = pLoadout->CreateFirstActor(aMetaPlayers[player]->GetNativeTechModule(), aMetaPlayers[player]->GetForeignCostMultiplier(), aMetaPlayers[player]->GetNativeCostMultiplier(), cost); - // Pass the instance and ownership thereof to the scene as a resident - if (pBrain) - { - // Set a pos outside the Scene so it'll be gracefully placed later when Scene's Terrain is actually loaded - pBrain->SetPos(Vector(-1, -1)); - pScene->SetResidentBrain(player, pBrain); - // Deduct the cost of the brain from the player's funds -// This is covered by the global cost adjustments below -// aMetaPlayers[player]->ChangeFunds(-cost); - } - // Change the ownership of all placed doors to the winning team - pScene->SetOwnerOfAllDoors(winnerTeam, player); - } - // Losing player, clear out his resident brain, if any - else - pScene->SetResidentBrain(player, 0); - } - // Nonexistent player, so clear out the resident brain just to be safe - else - pScene->SetResidentBrain(player, 0); - } - // Remove all remaining actors placed in blueprints etc by teams other than the winning one - pScene->RemoveAllPlacedActors(winnerTeam); - - // Actually change ownership and declare winner team - pScene->SetTeamOwnership(winnerTeam); - pOffensive->SetWinnerTeam(winnerTeam); - } - - // Figure out who of the losers actually evacuated instead of dying - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - // LOSER.. but evacuated successfully? - if (pOffensive->PlayerActive(player) && pOffensive->PlayerHadBrain(player) && aMetaPlayers[player] && aMetaPlayers[player]->GetTeam() != winnerTeam) - pOffensive->SetBrainEvacuated(player, RandomNum() < 0.25F); - } - } - - // Now make the party cost for all the teams - for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) - { - // Only mess with active teams - if (pOffensive->TeamActive(team)) - { - // Just mess with the funds; the metaplayers' funds will be affected afterward, according to their shares - // Never let team funds dip below 0 - pOffensive->SetTeamFunds(MAX(0, pOffensive->GetTeamFunds(team) * RandomNum()), team); - } - } - } - - return changedOwnership; -} + break; + } + } + } + + // Now change brain residencies if the ownership actually changed + if (changedOwnership) { + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + pLoadout = 0; + pBrain = 0; + if (pOffensive->PlayerActive(player) && aMetaPlayers[player]) { + // Winning player, so place a brain of his as a resident + if (aMetaPlayers[player]->GetTeam() == winnerTeam) { + // NOTE: Brain pool resource counter gets adjusted down in FinalizeOffense + + // Find the player's tech's brain, and instantiate it + if (pLoadout = dynamic_cast(g_PresetMan.GetEntityPreset("Loadout", "Infantry Brain", aMetaPlayers[player]->GetNativeTechModule()))) + pBrain = pLoadout->CreateFirstActor(aMetaPlayers[player]->GetNativeTechModule(), aMetaPlayers[player]->GetForeignCostMultiplier(), aMetaPlayers[player]->GetNativeCostMultiplier(), cost); + // Pass the instance and ownership thereof to the scene as a resident + if (pBrain) { + // Set a pos outside the Scene so it'll be gracefully placed later when Scene's Terrain is actually loaded + pBrain->SetPos(Vector(-1, -1)); + pScene->SetResidentBrain(player, pBrain); + // Deduct the cost of the brain from the player's funds + // This is covered by the global cost adjustments below + // aMetaPlayers[player]->ChangeFunds(-cost); + } + // Change the ownership of all placed doors to the winning team + pScene->SetOwnerOfAllDoors(winnerTeam, player); + } + // Losing player, clear out his resident brain, if any + else + pScene->SetResidentBrain(player, 0); + } + // Nonexistent player, so clear out the resident brain just to be safe + else + pScene->SetResidentBrain(player, 0); + } + // Remove all remaining actors placed in blueprints etc by teams other than the winning one + pScene->RemoveAllPlacedActors(winnerTeam); + + // Actually change ownership and declare winner team + pScene->SetTeamOwnership(winnerTeam); + pOffensive->SetWinnerTeam(winnerTeam); + } + + // Figure out who of the losers actually evacuated instead of dying + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + // LOSER.. but evacuated successfully? + if (pOffensive->PlayerActive(player) && pOffensive->PlayerHadBrain(player) && aMetaPlayers[player] && aMetaPlayers[player]->GetTeam() != winnerTeam) + pOffensive->SetBrainEvacuated(player, RandomNum() < 0.25F); + } + } + + // Now make the party cost for all the teams + for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { + // Only mess with active teams + if (pOffensive->TeamActive(team)) { + // Just mess with the funds; the metaplayers' funds will be affected afterward, according to their shares + // Never let team funds dip below 0 + pOffensive->SetTeamFunds(MAX(0, pOffensive->GetTeamFunds(team) * RandomNum()), team); + } + } + } + return changedOwnership; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateSiteRevealing ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the New Site Revealing animation -void MetagameGUI::UpdateSiteRevealing() -{ - // First set up all the targets we'll be animating into view - if (g_MetaMan.m_StateChanged) - { - // Save the number of sites we have reveelaed up til now so we can compare - m_AnimCountStart = m_AnimCountCurrent = (int)std::floor(g_MetaMan.m_RevealedScenes); - // Progress the number of site we have revealed with the set rate + the extra - g_MetaMan.m_RevealedScenes += g_MetaMan.m_RevealRate + g_MetaMan.m_RevealExtra; - // Reset the extra to 0 now after we've applied it - g_MetaMan.m_RevealExtra = 0; - // Don't reveal more than there are scenes! - if ((int)std::floor(g_MetaMan.m_RevealedScenes) >= g_MetaMan.m_Scenes.size()) - g_MetaMan.m_RevealedScenes = g_MetaMan.m_Scenes.size(); - // Figure out how many new sites we gots this round - int delta = (int)std::floor(g_MetaMan.m_RevealedScenes) - m_AnimCountStart; - // No new sites this round, so just continue onto next phase! - if (delta < 1) - { - m_ContinuePhase = true; - return; - } - // Where we need to go with the animation - m_AnimCountEnd = m_AnimCountStart + delta; - RTEAssert(m_AnimCountEnd <= g_MetaMan.m_Scenes.size(), "Trying to reveal more scenes than there are!"); - - // Clear and add target crosshairs pointing out all the new scenes - m_NewSiteIndicators.clear(); - m_AnimTimer1.Reset(); - // We want to start right away - m_AnimTimer1.SetElapsedRealTimeMS(1000); - m_AnimTimer2.Reset(); - } - - // Show a new set of crosshairs - if (m_AnimTimer1.GetElapsedRealTimeMS() > 600 && m_AnimCountCurrent < m_AnimCountEnd) - { - // Make the scene draw and selectable in the metagame view - g_MetaMan.m_Scenes[m_AnimCountCurrent]->SetRevealed(true); - // Create and set up its crosshairs -// TODO: TEMP change this back - m_NewSiteIndicators.push_back(SiteTarget(m_PlanetCenter + g_MetaMan.m_Scenes[m_AnimCountCurrent]->GetLocation() + g_MetaMan.m_Scenes[m_AnimCountCurrent]->GetLocationOffset(), 0, SiteTarget::CROSSHAIRSSHRINK, c_GUIColorYellow, m_AnimTimer2.GetElapsedRealTimeMS())); - m_SiteSwitchIndicators.push_back(SiteTarget(m_PlanetCenter + g_MetaMan.m_Scenes[m_AnimCountCurrent]->GetLocation() + g_MetaMan.m_Scenes[m_AnimCountCurrent]->GetLocationOffset(), 0, SiteTarget::CIRCLEGROW, c_GUIColorYellow, m_AnimTimer2.GetElapsedRealTimeMS())); - // Increment the current scene index - m_AnimCountCurrent++; - // Reset the timer so we will get the next reveal in the set time - m_AnimTimer1.Reset(); - } - - // Animate the existing crosshairs - double shrinkTime = 0; - double shrinkInterval = 600; - for (int i = 0 ; i < (m_AnimCountCurrent - m_AnimCountStart); ++i) - { - // How long have we been animating this one? - shrinkTime = m_AnimTimer2.GetElapsedRealTimeMS() - m_NewSiteIndicators[i].m_StartTime; - // If it's still in the shrink interval, keep doing it - if (shrinkTime < shrinkInterval) - m_NewSiteIndicators[i].m_AnimProgress = EaseOut(0.0, 1.0, shrinkTime / shrinkInterval); - // If it's after, then just keep pulsating the target - else - m_NewSiteIndicators[i].m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)shrinkTime % (int)shrinkInterval) / shrinkInterval); - } - - // Set the phase label grammatically correct - m_pPhaseLabel->SetText((m_AnimCountEnd - m_AnimCountStart) > 1 ? "Gold Sites Found!" : "Gold Site Found!"); - - // If we are done showing things, then make the continue button blink - if (m_NewSiteIndicators.size() == m_AnimCountEnd - m_AnimCountStart) - { - m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Continue <" : "Continue"); - } - - // Clean up if we're going onto next phase - if (m_ContinuePhase) - { - m_NewSiteIndicators.clear(); - - // Reveal all sites that are supposed to ahve been revelaed if player skipped the animation early - for (m_AnimCountCurrent = m_AnimCountStart; m_AnimCountCurrent < m_AnimCountEnd; ++m_AnimCountCurrent) - if (m_AnimCountCurrent < g_MetaMan.m_Scenes.size()) - g_MetaMan.m_Scenes[m_AnimCountCurrent]->SetRevealed(true); - } -} +void MetagameGUI::UpdateSiteRevealing() { + // First set up all the targets we'll be animating into view + if (g_MetaMan.m_StateChanged) { + // Save the number of sites we have reveelaed up til now so we can compare + m_AnimCountStart = m_AnimCountCurrent = (int)std::floor(g_MetaMan.m_RevealedScenes); + // Progress the number of site we have revealed with the set rate + the extra + g_MetaMan.m_RevealedScenes += g_MetaMan.m_RevealRate + g_MetaMan.m_RevealExtra; + // Reset the extra to 0 now after we've applied it + g_MetaMan.m_RevealExtra = 0; + // Don't reveal more than there are scenes! + if ((int)std::floor(g_MetaMan.m_RevealedScenes) >= g_MetaMan.m_Scenes.size()) + g_MetaMan.m_RevealedScenes = g_MetaMan.m_Scenes.size(); + // Figure out how many new sites we gots this round + int delta = (int)std::floor(g_MetaMan.m_RevealedScenes) - m_AnimCountStart; + // No new sites this round, so just continue onto next phase! + if (delta < 1) { + m_ContinuePhase = true; + return; + } + // Where we need to go with the animation + m_AnimCountEnd = m_AnimCountStart + delta; + RTEAssert(m_AnimCountEnd <= g_MetaMan.m_Scenes.size(), "Trying to reveal more scenes than there are!"); + + // Clear and add target crosshairs pointing out all the new scenes + m_NewSiteIndicators.clear(); + m_AnimTimer1.Reset(); + // We want to start right away + m_AnimTimer1.SetElapsedRealTimeMS(1000); + m_AnimTimer2.Reset(); + } + + // Show a new set of crosshairs + if (m_AnimTimer1.GetElapsedRealTimeMS() > 600 && m_AnimCountCurrent < m_AnimCountEnd) { + // Make the scene draw and selectable in the metagame view + g_MetaMan.m_Scenes[m_AnimCountCurrent]->SetRevealed(true); + // Create and set up its crosshairs + // TODO: TEMP change this back + m_NewSiteIndicators.push_back(SiteTarget(m_PlanetCenter + g_MetaMan.m_Scenes[m_AnimCountCurrent]->GetLocation() + g_MetaMan.m_Scenes[m_AnimCountCurrent]->GetLocationOffset(), 0, SiteTarget::CROSSHAIRSSHRINK, c_GUIColorYellow, m_AnimTimer2.GetElapsedRealTimeMS())); + m_SiteSwitchIndicators.push_back(SiteTarget(m_PlanetCenter + g_MetaMan.m_Scenes[m_AnimCountCurrent]->GetLocation() + g_MetaMan.m_Scenes[m_AnimCountCurrent]->GetLocationOffset(), 0, SiteTarget::CIRCLEGROW, c_GUIColorYellow, m_AnimTimer2.GetElapsedRealTimeMS())); + // Increment the current scene index + m_AnimCountCurrent++; + // Reset the timer so we will get the next reveal in the set time + m_AnimTimer1.Reset(); + } + + // Animate the existing crosshairs + double shrinkTime = 0; + double shrinkInterval = 600; + for (int i = 0; i < (m_AnimCountCurrent - m_AnimCountStart); ++i) { + // How long have we been animating this one? + shrinkTime = m_AnimTimer2.GetElapsedRealTimeMS() - m_NewSiteIndicators[i].m_StartTime; + // If it's still in the shrink interval, keep doing it + if (shrinkTime < shrinkInterval) + m_NewSiteIndicators[i].m_AnimProgress = EaseOut(0.0, 1.0, shrinkTime / shrinkInterval); + // If it's after, then just keep pulsating the target + else + m_NewSiteIndicators[i].m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)shrinkTime % (int)shrinkInterval) / shrinkInterval); + } + + // Set the phase label grammatically correct + m_pPhaseLabel->SetText((m_AnimCountEnd - m_AnimCountStart) > 1 ? "Gold Sites Found!" : "Gold Site Found!"); + + // If we are done showing things, then make the continue button blink + if (m_NewSiteIndicators.size() == m_AnimCountEnd - m_AnimCountStart) { + m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Continue <" : "Continue"); + } + + // Clean up if we're going onto next phase + if (m_ContinuePhase) { + m_NewSiteIndicators.clear(); + // Reveal all sites that are supposed to ahve been revelaed if player skipped the animation early + for (m_AnimCountCurrent = m_AnimCountStart; m_AnimCountCurrent < m_AnimCountEnd; ++m_AnimCountCurrent) + if (m_AnimCountCurrent < g_MetaMan.m_Scenes.size()) + g_MetaMan.m_Scenes[m_AnimCountCurrent]->SetRevealed(true); + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateSiteChangeAnim ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates team ownership change animations, if any. -void MetagameGUI::UpdateSiteChangeAnim() -{ - // Continue animating the existing markers - double animInterval = 600; - double animTime = 0; - for (std::vector::iterator itr = m_SiteSwitchIndicators.begin(); itr != m_SiteSwitchIndicators.end(); ++itr) - { - // How long have we been animating this one? - animTime = (*itr).m_AnimTimer.GetElapsedRealTimeMS(); - // If it's still in the shrink interval, keep doing it - if (animTime < animInterval) - (*itr).m_AnimProgress = EaseOut(0.0, 1.0, animTime / animInterval); -// // If it's after, then just keep pulsating the target -// else -// (*itr).m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)animTime % (int)animInterval) / animInterval); - } - - // Remove the markers that are done animating - for (std::vector::iterator itr = m_SiteSwitchIndicators.begin(); itr != m_SiteSwitchIndicators.end();) - { - if ((*itr).m_AnimTimer.GetElapsedRealTimeMS() > animInterval)//(*itr).m_AnimProgress >= 1.0) - { - m_SiteSwitchIndicators.erase(itr); - // Start over so we don't have trouble with invalidated iterators - itr = m_SiteSwitchIndicators.begin(); - } - else - itr++; - } -} +void MetagameGUI::UpdateSiteChangeAnim() { + // Continue animating the existing markers + double animInterval = 600; + double animTime = 0; + for (std::vector::iterator itr = m_SiteSwitchIndicators.begin(); itr != m_SiteSwitchIndicators.end(); ++itr) { + // How long have we been animating this one? + animTime = (*itr).m_AnimTimer.GetElapsedRealTimeMS(); + // If it's still in the shrink interval, keep doing it + if (animTime < animInterval) + (*itr).m_AnimProgress = EaseOut(0.0, 1.0, animTime / animInterval); + // // If it's after, then just keep pulsating the target + // else + // (*itr).m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)animTime % (int)animInterval) / animInterval); + } + // Remove the markers that are done animating + for (std::vector::iterator itr = m_SiteSwitchIndicators.begin(); itr != m_SiteSwitchIndicators.end();) { + if ((*itr).m_AnimTimer.GetElapsedRealTimeMS() > animInterval) //(*itr).m_AnimProgress >= 1.0) + { + m_SiteSwitchIndicators.erase(itr); + // Start over so we don't have trouble with invalidated iterators + itr = m_SiteSwitchIndicators.begin(); + } else + itr++; + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateIncomeCounting ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the Count Income animation -void MetagameGUI::UpdateIncomeCounting(bool initOverride) -{ - // First set up all the site lines we should be animating into view - if (g_MetaMan.m_StateChanged || initOverride) - { - // Make note of all funds from previous round - for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) - (*mpItr).m_PhaseStartFunds = (*mpItr).m_Funds; - - // If this is the very first round and no Scenes are owned yet, then we should just skip this - // Don't return out of this yet thuogh because there's some finalization going on in the end of this func - if (g_MetaMan.m_CurrentRound == 0) - m_ContinuePhase = true; - - // Init all the SiteLine:s and make them hidden; we will reveal each one sequentially with an animation - m_IncomeSiteLines.clear(); - for (int metaPlayer = 0; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - m_aStationIncomeLineIndices[metaPlayer] = -1; - m_AnimMetaPlayer = m_ActivePlayerIncomeLines = Players::PlayerOne; - m_pAnimScene = 0; - float totalRent, totalIncome, totalEndFunds; - int channelHeight; - // Loop through the players - do - { - // If this player is DONE FOR, skip income counting completely - if (g_MetaMan.GetTotalBrainCountOfPlayer(m_AnimMetaPlayer) <= 0 || - g_MetaMan.m_Players[m_AnimMetaPlayer].IsGameOverByRound(g_MetaMan.m_CurrentRound)) - { - m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer] = -1; - UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer, false); - continue; - } - - // Only charge rent if there's any brains left in pool -// Make this rent variable somehow?? - totalRent = initOverride ? 0 : g_MetaMan.m_Players[m_AnimMetaPlayer].GetBrainPoolCount() > 0 ? TRADESTARRENT : 0; - totalIncome = initOverride ? 0 : g_MetaMan.GetSceneIncomeOfPlayer(m_AnimMetaPlayer); - totalEndFunds = g_MetaMan.m_Players[m_AnimMetaPlayer].m_PhaseStartFunds + totalIncome - totalRent; - channelHeight = 60; - - // TRADESTAR BANK ACCOUNT AND RENT - // Add line to show existing funds stored in space station, and deduct rent from - m_IncomeSiteLines.push_back(SiteLine(m_AnimMetaPlayer, 0, 1.0, m_StationPosOnOrbit, "TradeStar Midas", 0, c_GUIColorYellow, -1, 0, channelHeight, 2.0f)); - m_IncomeSiteLines.back().m_FundsAmount = g_MetaMan.m_Players[m_AnimMetaPlayer].m_PhaseStartFunds; - m_IncomeSiteLines.back().m_FundsTarget = m_IncomeSiteLines.back().m_FundsAmount - totalRent; - // Save the index so we can update the line later - m_aStationIncomeLineIndices[m_AnimMetaPlayer] = m_IncomeSiteLines.size() - 1; - - // SCENE INCOME - // Loop through the scenes owned by that player, setting up the site line for each +void MetagameGUI::UpdateIncomeCounting(bool initOverride) { + // First set up all the site lines we should be animating into view + if (g_MetaMan.m_StateChanged || initOverride) { + // Make note of all funds from previous round + for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) + (*mpItr).m_PhaseStartFunds = (*mpItr).m_Funds; + + // If this is the very first round and no Scenes are owned yet, then we should just skip this + // Don't return out of this yet thuogh because there's some finalization going on in the end of this func + if (g_MetaMan.m_CurrentRound == 0) + m_ContinuePhase = true; + + // Init all the SiteLine:s and make them hidden; we will reveal each one sequentially with an animation + m_IncomeSiteLines.clear(); + for (int metaPlayer = 0; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) + m_aStationIncomeLineIndices[metaPlayer] = -1; + m_AnimMetaPlayer = m_ActivePlayerIncomeLines = Players::PlayerOne; + m_pAnimScene = 0; + float totalRent, totalIncome, totalEndFunds; + int channelHeight; + // Loop through the players + do { + // If this player is DONE FOR, skip income counting completely + if (g_MetaMan.GetTotalBrainCountOfPlayer(m_AnimMetaPlayer) <= 0 || + g_MetaMan.m_Players[m_AnimMetaPlayer].IsGameOverByRound(g_MetaMan.m_CurrentRound)) { + m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer] = -1; + UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer, false); + continue; + } + + // Only charge rent if there's any brains left in pool + // Make this rent variable somehow?? + totalRent = initOverride ? 0 : g_MetaMan.m_Players[m_AnimMetaPlayer].GetBrainPoolCount() > 0 ? TRADESTARRENT + : 0; + totalIncome = initOverride ? 0 : g_MetaMan.GetSceneIncomeOfPlayer(m_AnimMetaPlayer); + totalEndFunds = g_MetaMan.m_Players[m_AnimMetaPlayer].m_PhaseStartFunds + totalIncome - totalRent; + channelHeight = 60; + + // TRADESTAR BANK ACCOUNT AND RENT + // Add line to show existing funds stored in space station, and deduct rent from + m_IncomeSiteLines.push_back(SiteLine(m_AnimMetaPlayer, 0, 1.0, m_StationPosOnOrbit, "TradeStar Midas", 0, c_GUIColorYellow, -1, 0, channelHeight, 2.0f)); + m_IncomeSiteLines.back().m_FundsAmount = g_MetaMan.m_Players[m_AnimMetaPlayer].m_PhaseStartFunds; + m_IncomeSiteLines.back().m_FundsTarget = m_IncomeSiteLines.back().m_FundsAmount - totalRent; + // Save the index so we can update the line later + m_aStationIncomeLineIndices[m_AnimMetaPlayer] = m_IncomeSiteLines.size() - 1; + + // SCENE INCOME + // Loop through the scenes owned by that player, setting up the site line for each if (!initOverride) { - while (m_pAnimScene = g_MetaMan.GetNextSceneOfPlayer(m_AnimMetaPlayer, m_pAnimScene)) - { + while (m_pAnimScene = g_MetaMan.GetNextSceneOfPlayer(m_AnimMetaPlayer, m_pAnimScene)) { m_IncomeSiteLines.push_back(SiteLine(m_AnimMetaPlayer, 1.0, 0, m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), m_pAnimScene->GetPresetName(), m_pAnimScene, c_GUIColorYellow, -1, 0, channelHeight, 1.0f, g_MetaMan.IsActiveTeam(m_pAnimScene->GetTeamOwnership()))); // Star them at 0, make them go to the round income for this base m_IncomeSiteLines.back().m_FundsAmount = 0; @@ -3447,764 +3150,659 @@ void MetagameGUI::UpdateIncomeCounting(bool initOverride) } } - // BRAIN LIQUIDATION if funds will end up under 0 - if (totalEndFunds <= 0 || g_MetaMan.GetTotalBrainCountOfPlayer(m_AnimMetaPlayer) <= 0) - { - m_IncomeSiteLines.push_back(SiteLine(m_AnimMetaPlayer, 1.0, 0, Vector(m_apBrainPoolLabel[m_AnimMetaPlayer]->GetXPos() + (m_apBrainPoolLabel[m_AnimMetaPlayer]->GetHAlignment() == GUIFont::Left ? 5 : 16), m_apBrainPoolLabel[m_AnimMetaPlayer]->GetYPos() + 9) - m_PlanetCenter, "Brain Liquidation", 0, c_GUIColorYellow, -1, 0, channelHeight, 2.0f, false)); - // Start at 0 and grow to how much the brain is worth - m_IncomeSiteLines.back().m_FundsAmount = 0; - m_IncomeSiteLines.back().m_FundsTarget = BRAINGOLDVALUE; - m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer] = m_IncomeSiteLines.size() - 1; - } - else - m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer] = -1; - - // This will set up all meter ratios properly, based on the actual funds numbers -// TODO, really?: "actually not a good idea here, the values above are good init" - UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer, false); - } - while (++m_AnimMetaPlayer < g_MetaMan.m_Players.size()); - - // Start animating these lines appearing, one after another - m_AnimIncomeLine = 0; - m_AnimIncomeLineChange = true; - m_AnimTimer1.Reset(); - ChangeAnimMode(PAUSEANIM); - } - - // Animate sitelines into view, one by one - if (!initOverride && !m_IncomeSiteLines.empty() && m_AnimIncomeLine < m_IncomeSiteLines.size()) - { - // Did the players change? If so, pause -// if (m_AnimMetaPlayer != m_IncomeSiteLines[m_AnimIncomeLine].m_Player && !m_AnimTimer1.IsPastRealMS(1000)) -// ChangeAnimMode(PAUSEANIM); - // If a new line, choose which animation is appropriate - if (m_AnimIncomeLineChange) - { - // Which player is this line of? - m_AnimMetaPlayer = m_ActivePlayerIncomeLines = m_IncomeSiteLines[m_AnimIncomeLine].m_Player; - // Station line, blink its meter and grow outward to the station - if (m_AnimIncomeLine == m_aStationIncomeLineIndices[m_AnimMetaPlayer]) - ChangeAnimMode(BLINKMETER); - // Regular site line, start with shrinking circle around the site and draw the line backward toward the bar - else - ChangeAnimMode(SHRINKCIRCLE); - - m_AnimIncomeLineChange = false; - } - - if (m_AnimMode == BLINKCIRCLE) - { -/* - // Show the meter - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = 1; - // Start blinking - if (!m_AnimTimer1.IsPastRealMS(600)) - { - m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(m_AnimTimer1.AlternateReal(150)); - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = m_AnimTimer1.AlternateReal(150) ? 0 : 1; - } - else - { - // Leave the label showing - m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(true); - // Start connecting the meter to the circle! - ChangeAnimMode(LINECONNECTFW); - } -*/ - } - else if (m_AnimMode == SHRINKCIRCLE) - { - if (NewAnimMode()) - { - m_AnimTimer1.Reset(); - // Start with the circle showing - m_AnimSegment = 1; - m_AnimFundsMax = 0; - m_LineConnected = false; - } - // Show the circle - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = m_AnimSegment; - // Show the site name over the site loc - UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint); - // Shrink by certain rate - if (!m_AnimTimer1.IsPastRealMS(350)) - { - // If line to space station, special case animation - if (m_AnimIncomeLine == m_aStationIncomeLineIndices[m_AnimMetaPlayer]) - m_IncomeSiteLines[m_AnimIncomeLine].m_CircleSize = EaseOut(20.0, 2.0, EaseOut(0, 1.0, m_AnimTimer1.GetElapsedRealTimeMS() / 350)); - else - m_IncomeSiteLines[m_AnimIncomeLine].m_CircleSize = EaseOut(7.0, 1.0, EaseOut(0, 1.0, m_AnimTimer1.GetElapsedRealTimeMS() / 350)); - - m_AnimTimer2.Reset(); - } - // Finished shrinking circle to the target size, now pause and blink it for a lil bit - else if (!m_AnimTimer2.IsPastRealMS(600)) - { - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = m_AnimTimer2.AlternateReal(150) ? 0 : 1; - // Also finish the circle in the destination size - m_IncomeSiteLines[m_AnimIncomeLine].m_CircleSize = m_AnimIncomeLine == m_aStationIncomeLineIndices[m_AnimMetaPlayer] ? 2.0 : 1.0; - } - else - { - // Start connecting the circle to the player bar! - ChangeAnimMode(LINECONNECTBW); - m_LineConnected = false; - } - } - else if (m_AnimMode == LINECONNECTFW) - { - if (NewAnimMode()) - { - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = 0; - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; - m_LineConnected = false; - // Hide the site name over the site loc at first - UpdateSiteNameLabel(false); - } - - // If line not yet connected, keep revealing segments - if (!m_LineConnected) - { - if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) - { - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = ++m_AnimSegment; - m_AnimTimer1.Reset(); - } - } -/* - // Oh! Line now completed, pause for a while before continuing to next line - else if (!m_AnimTimer1.IsPastRealMS(1500)) - { - // Show the site name over the site loc while we're waiting - UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint, 1.3); - } -*/ - // Done waiting, continue to next line - else - { - // If this is also the last line of a player, then do the retract animation -// if ((m_AnimIncomeLine + 1) >= m_IncomeSiteLines.size() || m_IncomeSiteLines[m_AnimIncomeLine + 1].m_Player != m_AnimMetaPlayer) -// ChangeAnimMode(RETRACTLINES); - // Just another line on the same player, so start animating it immediately -// else - { - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = -1; -/* - // Hide the site label again - UpdateSiteNameLabel(false); - m_AnimIncomeLine++; - m_AnimIncomeLineChange = true; -*/ - // Going to show rent being deducted from the tradestar - ChangeAnimMode(SHRINKMETER); - } - } - } - else if (m_AnimMode == LINECONNECTBW) - { - if (NewAnimMode()) - { - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = -1; - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = 0; - m_LineConnected = false; - } - // Show the site name over the site loc - UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint); - // Need to set up the ratio animation before we draw the line so it appears as the meter is 0 - if (m_AnimFundsMax == 0) - { - m_AnimFundsMax = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount; - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = 0; - } - - // If line not yet connected, keep revealing segments - if (!m_LineConnected) - { - if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) - { - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = ++m_AnimSegment; - m_AnimTimer1.Reset(); - } - } - // Oh! Line now completed, start growing the meter - else - ChangeAnimMode(GROWMETER); - } - else if (m_AnimMode == BLINKMETER) - { - if (NewAnimMode()) - { - m_AnimTimer1.Reset(); - // Show the meter - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = 1; - } - // Start blinking - if (!m_AnimTimer1.IsPastRealMS(1500)) - { - m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(m_AnimTimer1.AlternateReal(150)); - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = m_AnimTimer1.AlternateReal(150) ? 1 : 0; - } - else - { - // Leave the label showing - m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(true); - // Start connecting the meter to the circle! - ChangeAnimMode(LINECONNECTFW); - m_AnimSegment = 1; - } - } - // Grow each meter from 0 to its target ratio - else if (m_AnimMode == GROWMETER) - { - if (NewAnimMode()) - { - // Longer if we're liquidating brain.. it's important - m_AnimModeDuration = m_AnimIncomeLine == m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer] ? 4000 : 2000; - m_AnimTimer1.Reset(); - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; - // Show the change - FundsChangeIndication(m_AnimMetaPlayer, m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount, Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth(), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos()), m_AnimModeDuration); - - // Show the brain being sucked away if this is a brain liquidation income event - if (m_AnimIncomeLine == m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer]) - { - // This is the text label showing the brain going away - BrainsChangeIndication(m_AnimMetaPlayer, -1, Vector(m_apBrainPoolLabel[m_AnimMetaPlayer]->GetXPos(), m_apBrainPoolLabel[m_AnimMetaPlayer]->GetYPos()), m_apBrainPoolLabel[m_AnimMetaPlayer]->GetHAlignment(), m_AnimModeDuration); - // This is the display adjustment to the actual counter; the final actual change at the end of the animation - g_MetaMan.m_Players[m_AnimMetaPlayer].ChangeBrainsInTransit(1); - } - } - // Show the site name over the site loc - UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint); - // Grow for a certain amount of time - if (!m_AnimTimer1.IsPastRealMS(m_AnimModeDuration)) - { - // Make this animation correlate in duration with the funds change label that rises - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = EaseOut(0, m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget, m_AnimTimer1.GetElapsedRealTimeMS() / m_AnimModeDuration); - g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, m_AnimMetaPlayer); - UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer); - m_AnimTimer2.Reset(); - } - // Finished growing the meter to the target size - else - { - UpdateSiteNameLabel(false); - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget; - g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, m_AnimMetaPlayer); - UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer); - - // Check if there's more lines to draw, and if so, if the next one is of a different player - // OR if there's no lines left at all, just retract the last player's lines we just finished - // Then pause to retract all the lines of the just finished player - if ((m_AnimIncomeLine + 1) >= m_IncomeSiteLines.size() || m_IncomeSiteLines[m_AnimIncomeLine + 1].m_Player != m_AnimMetaPlayer) - { - // Wait for a little bit when we've displayed all sites of a player - if (m_AnimTimer2.IsPastRealMS(500)) - ChangeAnimMode(RETRACTLINES); - } - // Just another line on the same player, so start animating it immediately - else - { - m_AnimIncomeLine++; - m_AnimIncomeLineChange = true; - ChangeAnimMode(SHRINKCIRCLE); - } - } - } - // Shrink the station line to show it costing rent - else if (m_AnimMode == SHRINKMETER) - { - if (NewAnimMode()) - { - m_AnimModeDuration = 2000; - m_AnimTimer1.Reset(); - m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; - // Show the change, if any - if (fabs(m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount) > 0) - { - // Show why we are paying money - PlayerTextIndication(m_AnimMetaPlayer, "TradeStar brain storage rent", Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + (m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth() / 2), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos() + (m_apPlayerBarLabel[m_AnimMetaPlayer]->GetHeight() / 2)), m_AnimModeDuration); - FundsChangeIndication(m_AnimMetaPlayer, m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount, Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth(), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos()), m_AnimModeDuration); - } - // Indicate why we're not paying anything - else - PlayerTextIndication(m_AnimMetaPlayer, "No brains; no rent!", Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + (m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth() / 2), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos() + (m_apPlayerBarLabel[m_AnimMetaPlayer]->GetHeight() / 2)), m_AnimModeDuration); - -/* This is done above on init now - // Only charge rent if we've still got brains at the tradestar - if (g_MetaMan.m_Players[m_AnimMetaPlayer].GetBrainPoolCount() > 0) - { - // Establish the after-rent target - m_AnimFundsMin = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount - TRADESTARRENT; - // Show the change in rent - FundsChangeIndication(m_AnimMetaPlayer, -TRADESTARRENT, Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth(), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos()), 2000); - } - // No change in funds - else - m_AnimFundsMin = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount; -*/ - } - // Show the site name over the site loc -// UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint, 1.2); - // This'll always be the tradestar, so hardcode some descriptive text - UpdateSiteNameLabel(true, "TradeStar Midas", m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint, 1.25); - // Shrink for a certain amount of time - if (!m_AnimTimer1.IsPastRealMS(m_AnimModeDuration)) - { - // Make this animation correlate in duration with the funds change label that rises - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = EaseOut(g_MetaMan.m_Players[m_AnimMetaPlayer].m_PhaseStartFunds, m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget, m_AnimTimer1.GetElapsedRealTimeMS() / m_AnimModeDuration); - UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer); - g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, m_AnimMetaPlayer); - m_AnimTimer2.Reset(); - } - // Finished shrinking the meter to the target size - else - { - UpdateSiteNameLabel(false); - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget; - g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, m_AnimMetaPlayer); - UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer); - - // Check if there's more lines to draw, and if so, if the next one is of a different player - // OR if there's no lines left at all, just retract the last player's lines we just finished - // Then pause to retract all the lines of the just finished player - if ((m_AnimIncomeLine + 1) >= m_IncomeSiteLines.size() || m_IncomeSiteLines[m_AnimIncomeLine + 1].m_Player != m_AnimMetaPlayer) - { - // Wait for a little bit when we've displayed all sites of a player - if (m_AnimTimer2.IsPastRealMS(500)) - ChangeAnimMode(RETRACTLINES); - } - // Just another line on the same player, so start animating it immediately - else - { - m_AnimIncomeLine++; - m_AnimIncomeLineChange = true; - ChangeAnimMode(SHRINKCIRCLE); - } - } - } - // Retract all the lines for a specific player from the sites to the bar - else if (m_AnimMode == RETRACTLINES) - { - if (NewAnimMode()) - { - m_AnimTimer1.Reset(); - // A few extra will give some pause - m_AnimSegment = 7; - // Stop showing the site label - UpdateSiteNameLabel(false); - } - - if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) - { - m_AnimSegment--; - m_AnimTimer1.Reset(); - // Go through all lines and animate away the ones belonging to this player - for (std::vector::iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) - { - if ((*slItr).m_Player == m_AnimMetaPlayer) - { - (*slItr).m_OnlyFirstSegments = m_AnimSegment; - (*slItr).m_OnlyLastSegments = -1; - } - } - } - if (m_AnimSegment == 0) - { - // Have another player's lines to animate, so start doing that - m_AnimIncomeLine++; - m_AnimIncomeLineChange = true; - ChangeAnimMode(SHRINKCIRCLE); - } - } - } - - // If no more lines, DONE, continue phase to next - if (m_AnimIncomeLine >= m_IncomeSiteLines.size() && !initOverride) - m_ContinuePhase = true; - - // Phase ending, make sure everything is set up to continue - if (m_ContinuePhase || initOverride) - { - // Set all lines' fund amounts to their targets and reset their segment animations so we can see them - int lineIndex = 0; - for (std::vector::iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) - { - (*slItr).m_FundsAmount = (*slItr).m_FundsTarget; - (*slItr).m_OnlyFirstSegments = -1; - (*slItr).m_OnlyLastSegments = -1; - - // Also, if there are any brain liquidation lines, then adjust the brain counter for that poor player - if ((*slItr).m_Player >= Players::PlayerOne && (*slItr).m_Player < Players::MaxPlayerCount && - m_aBrainSaleIncomeLineIndices[(*slItr).m_Player] == lineIndex) - { - // Remove the display adjustment and APPLY the actual change to brains when one is liquidated - g_MetaMan.m_Players[(*slItr).m_Player].SetBrainsInTransit(0); - g_MetaMan.m_Players[(*slItr).m_Player].ChangeBrainPoolCount(-1); - } - // Keep this synched with the iterator - lineIndex++; - } - // Hide all lines for rendering.. appropriate lines will show up when user mouseovers bars in turn phases - m_ActivePlayerIncomeLines = -1; - - // Make sure all fund labels and line ratios are good - for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - { - UpdatePlayerLineRatios(m_IncomeSiteLines, metaPlayer, false); - m_apPlayerBarLabel[metaPlayer]->SetVisible(true); - // Set all funds to the final values, if not a gameover guy - if (g_MetaMan.GetTotalBrainCountOfPlayer(metaPlayer) > 0) - g_MetaMan.m_Players[metaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, metaPlayer, false); - - if (g_SettingsMan.EndlessMetaGameMode()) - { + // BRAIN LIQUIDATION if funds will end up under 0 + if (totalEndFunds <= 0 || g_MetaMan.GetTotalBrainCountOfPlayer(m_AnimMetaPlayer) <= 0) { + m_IncomeSiteLines.push_back(SiteLine(m_AnimMetaPlayer, 1.0, 0, Vector(m_apBrainPoolLabel[m_AnimMetaPlayer]->GetXPos() + (m_apBrainPoolLabel[m_AnimMetaPlayer]->GetHAlignment() == GUIFont::Left ? 5 : 16), m_apBrainPoolLabel[m_AnimMetaPlayer]->GetYPos() + 9) - m_PlanetCenter, "Brain Liquidation", 0, c_GUIColorYellow, -1, 0, channelHeight, 2.0f, false)); + // Start at 0 and grow to how much the brain is worth + m_IncomeSiteLines.back().m_FundsAmount = 0; + m_IncomeSiteLines.back().m_FundsTarget = BRAINGOLDVALUE; + m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer] = m_IncomeSiteLines.size() - 1; + } else + m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer] = -1; + + // This will set up all meter ratios properly, based on the actual funds numbers + // TODO, really?: "actually not a good idea here, the values above are good init" + UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer, false); + } while (++m_AnimMetaPlayer < g_MetaMan.m_Players.size()); + + // Start animating these lines appearing, one after another + m_AnimIncomeLine = 0; + m_AnimIncomeLineChange = true; + m_AnimTimer1.Reset(); + ChangeAnimMode(PAUSEANIM); + } + + // Animate sitelines into view, one by one + if (!initOverride && !m_IncomeSiteLines.empty() && m_AnimIncomeLine < m_IncomeSiteLines.size()) { + // Did the players change? If so, pause + // if (m_AnimMetaPlayer != m_IncomeSiteLines[m_AnimIncomeLine].m_Player && !m_AnimTimer1.IsPastRealMS(1000)) + // ChangeAnimMode(PAUSEANIM); + // If a new line, choose which animation is appropriate + if (m_AnimIncomeLineChange) { + // Which player is this line of? + m_AnimMetaPlayer = m_ActivePlayerIncomeLines = m_IncomeSiteLines[m_AnimIncomeLine].m_Player; + // Station line, blink its meter and grow outward to the station + if (m_AnimIncomeLine == m_aStationIncomeLineIndices[m_AnimMetaPlayer]) + ChangeAnimMode(BLINKMETER); + // Regular site line, start with shrinking circle around the site and draw the line backward toward the bar + else + ChangeAnimMode(SHRINKCIRCLE); + + m_AnimIncomeLineChange = false; + } + + if (m_AnimMode == BLINKCIRCLE) { + /* + // Show the meter + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = 1; + // Start blinking + if (!m_AnimTimer1.IsPastRealMS(600)) + { + m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(m_AnimTimer1.AlternateReal(150)); + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = m_AnimTimer1.AlternateReal(150) ? 0 : 1; + } + else + { + // Leave the label showing + m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(true); + // Start connecting the meter to the circle! + ChangeAnimMode(LINECONNECTFW); + } + */ + } else if (m_AnimMode == SHRINKCIRCLE) { + if (NewAnimMode()) { + m_AnimTimer1.Reset(); + // Start with the circle showing + m_AnimSegment = 1; + m_AnimFundsMax = 0; + m_LineConnected = false; + } + // Show the circle + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = m_AnimSegment; + // Show the site name over the site loc + UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint); + // Shrink by certain rate + if (!m_AnimTimer1.IsPastRealMS(350)) { + // If line to space station, special case animation + if (m_AnimIncomeLine == m_aStationIncomeLineIndices[m_AnimMetaPlayer]) + m_IncomeSiteLines[m_AnimIncomeLine].m_CircleSize = EaseOut(20.0, 2.0, EaseOut(0, 1.0, m_AnimTimer1.GetElapsedRealTimeMS() / 350)); + else + m_IncomeSiteLines[m_AnimIncomeLine].m_CircleSize = EaseOut(7.0, 1.0, EaseOut(0, 1.0, m_AnimTimer1.GetElapsedRealTimeMS() / 350)); + + m_AnimTimer2.Reset(); + } + // Finished shrinking circle to the target size, now pause and blink it for a lil bit + else if (!m_AnimTimer2.IsPastRealMS(600)) { + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = m_AnimTimer2.AlternateReal(150) ? 0 : 1; + // Also finish the circle in the destination size + m_IncomeSiteLines[m_AnimIncomeLine].m_CircleSize = m_AnimIncomeLine == m_aStationIncomeLineIndices[m_AnimMetaPlayer] ? 2.0 : 1.0; + } else { + // Start connecting the circle to the player bar! + ChangeAnimMode(LINECONNECTBW); + m_LineConnected = false; + } + } else if (m_AnimMode == LINECONNECTFW) { + if (NewAnimMode()) { + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = 0; + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; + m_LineConnected = false; + // Hide the site name over the site loc at first + UpdateSiteNameLabel(false); + } + + // If line not yet connected, keep revealing segments + if (!m_LineConnected) { + if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) { + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = ++m_AnimSegment; + m_AnimTimer1.Reset(); + } + } + /* + // Oh! Line now completed, pause for a while before continuing to next line + else if (!m_AnimTimer1.IsPastRealMS(1500)) + { + // Show the site name over the site loc while we're waiting + UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint, 1.3); + } + */ + // Done waiting, continue to next line + else { + // If this is also the last line of a player, then do the retract animation + // if ((m_AnimIncomeLine + 1) >= m_IncomeSiteLines.size() || m_IncomeSiteLines[m_AnimIncomeLine + 1].m_Player != m_AnimMetaPlayer) + // ChangeAnimMode(RETRACTLINES); + // Just another line on the same player, so start animating it immediately + // else + { + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = -1; + /* + // Hide the site label again + UpdateSiteNameLabel(false); + m_AnimIncomeLine++; + m_AnimIncomeLineChange = true; + */ + // Going to show rent being deducted from the tradestar + ChangeAnimMode(SHRINKMETER); + } + } + } else if (m_AnimMode == LINECONNECTBW) { + if (NewAnimMode()) { + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = -1; + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = 0; + m_LineConnected = false; + } + // Show the site name over the site loc + UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint); + // Need to set up the ratio animation before we draw the line so it appears as the meter is 0 + if (m_AnimFundsMax == 0) { + m_AnimFundsMax = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount; + m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = 0; + } + + // If line not yet connected, keep revealing segments + if (!m_LineConnected) { + if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) { + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = ++m_AnimSegment; + m_AnimTimer1.Reset(); + } + } + // Oh! Line now completed, start growing the meter + else + ChangeAnimMode(GROWMETER); + } else if (m_AnimMode == BLINKMETER) { + if (NewAnimMode()) { + m_AnimTimer1.Reset(); + // Show the meter + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = 1; + } + // Start blinking + if (!m_AnimTimer1.IsPastRealMS(1500)) { + m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(m_AnimTimer1.AlternateReal(150)); + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyFirstSegments = m_AnimTimer1.AlternateReal(150) ? 1 : 0; + } else { + // Leave the label showing + m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(true); + // Start connecting the meter to the circle! + ChangeAnimMode(LINECONNECTFW); + m_AnimSegment = 1; + } + } + // Grow each meter from 0 to its target ratio + else if (m_AnimMode == GROWMETER) { + if (NewAnimMode()) { + // Longer if we're liquidating brain.. it's important + m_AnimModeDuration = m_AnimIncomeLine == m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer] ? 4000 : 2000; + m_AnimTimer1.Reset(); + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; + // Show the change + FundsChangeIndication(m_AnimMetaPlayer, m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount, Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth(), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos()), m_AnimModeDuration); + + // Show the brain being sucked away if this is a brain liquidation income event + if (m_AnimIncomeLine == m_aBrainSaleIncomeLineIndices[m_AnimMetaPlayer]) { + // This is the text label showing the brain going away + BrainsChangeIndication(m_AnimMetaPlayer, -1, Vector(m_apBrainPoolLabel[m_AnimMetaPlayer]->GetXPos(), m_apBrainPoolLabel[m_AnimMetaPlayer]->GetYPos()), m_apBrainPoolLabel[m_AnimMetaPlayer]->GetHAlignment(), m_AnimModeDuration); + // This is the display adjustment to the actual counter; the final actual change at the end of the animation + g_MetaMan.m_Players[m_AnimMetaPlayer].ChangeBrainsInTransit(1); + } + } + // Show the site name over the site loc + UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint); + // Grow for a certain amount of time + if (!m_AnimTimer1.IsPastRealMS(m_AnimModeDuration)) { + // Make this animation correlate in duration with the funds change label that rises + m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = EaseOut(0, m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget, m_AnimTimer1.GetElapsedRealTimeMS() / m_AnimModeDuration); + g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, m_AnimMetaPlayer); + UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer); + m_AnimTimer2.Reset(); + } + // Finished growing the meter to the target size + else { + UpdateSiteNameLabel(false); + m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget; + g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, m_AnimMetaPlayer); + UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer); + + // Check if there's more lines to draw, and if so, if the next one is of a different player + // OR if there's no lines left at all, just retract the last player's lines we just finished + // Then pause to retract all the lines of the just finished player + if ((m_AnimIncomeLine + 1) >= m_IncomeSiteLines.size() || m_IncomeSiteLines[m_AnimIncomeLine + 1].m_Player != m_AnimMetaPlayer) { + // Wait for a little bit when we've displayed all sites of a player + if (m_AnimTimer2.IsPastRealMS(500)) + ChangeAnimMode(RETRACTLINES); + } + // Just another line on the same player, so start animating it immediately + else { + m_AnimIncomeLine++; + m_AnimIncomeLineChange = true; + ChangeAnimMode(SHRINKCIRCLE); + } + } + } + // Shrink the station line to show it costing rent + else if (m_AnimMode == SHRINKMETER) { + if (NewAnimMode()) { + m_AnimModeDuration = 2000; + m_AnimTimer1.Reset(); + m_IncomeSiteLines[m_AnimIncomeLine].m_OnlyLastSegments = -1; + // Show the change, if any + if (fabs(m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount) > 0) { + // Show why we are paying money + PlayerTextIndication(m_AnimMetaPlayer, "TradeStar brain storage rent", Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + (m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth() / 2), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos() + (m_apPlayerBarLabel[m_AnimMetaPlayer]->GetHeight() / 2)), m_AnimModeDuration); + FundsChangeIndication(m_AnimMetaPlayer, m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget - m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount, Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth(), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos()), m_AnimModeDuration); + } + // Indicate why we're not paying anything + else + PlayerTextIndication(m_AnimMetaPlayer, "No brains; no rent!", Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + (m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth() / 2), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos() + (m_apPlayerBarLabel[m_AnimMetaPlayer]->GetHeight() / 2)), m_AnimModeDuration); + + /* This is done above on init now + // Only charge rent if we've still got brains at the tradestar + if (g_MetaMan.m_Players[m_AnimMetaPlayer].GetBrainPoolCount() > 0) + { + // Establish the after-rent target + m_AnimFundsMin = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount - TRADESTARRENT; + // Show the change in rent + FundsChangeIndication(m_AnimMetaPlayer, -TRADESTARRENT, Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth(), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos()), 2000); + } + // No change in funds + else + m_AnimFundsMin = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount; + */ + } + // Show the site name over the site loc + // UpdateSiteNameLabel(true, m_IncomeSiteLines[m_AnimIncomeLine].m_SiteName, m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint, 1.2); + // This'll always be the tradestar, so hardcode some descriptive text + UpdateSiteNameLabel(true, "TradeStar Midas", m_IncomeSiteLines[m_AnimIncomeLine].m_PlanetPoint, 1.25); + // Shrink for a certain amount of time + if (!m_AnimTimer1.IsPastRealMS(m_AnimModeDuration)) { + // Make this animation correlate in duration with the funds change label that rises + m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = EaseOut(g_MetaMan.m_Players[m_AnimMetaPlayer].m_PhaseStartFunds, m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget, m_AnimTimer1.GetElapsedRealTimeMS() / m_AnimModeDuration); + UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer); + g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, m_AnimMetaPlayer); + m_AnimTimer2.Reset(); + } + // Finished shrinking the meter to the target size + else { + UpdateSiteNameLabel(false); + m_IncomeSiteLines[m_AnimIncomeLine].m_FundsAmount = m_IncomeSiteLines[m_AnimIncomeLine].m_FundsTarget; + g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, m_AnimMetaPlayer); + UpdatePlayerLineRatios(m_IncomeSiteLines, m_AnimMetaPlayer); + + // Check if there's more lines to draw, and if so, if the next one is of a different player + // OR if there's no lines left at all, just retract the last player's lines we just finished + // Then pause to retract all the lines of the just finished player + if ((m_AnimIncomeLine + 1) >= m_IncomeSiteLines.size() || m_IncomeSiteLines[m_AnimIncomeLine + 1].m_Player != m_AnimMetaPlayer) { + // Wait for a little bit when we've displayed all sites of a player + if (m_AnimTimer2.IsPastRealMS(500)) + ChangeAnimMode(RETRACTLINES); + } + // Just another line on the same player, so start animating it immediately + else { + m_AnimIncomeLine++; + m_AnimIncomeLineChange = true; + ChangeAnimMode(SHRINKCIRCLE); + } + } + } + // Retract all the lines for a specific player from the sites to the bar + else if (m_AnimMode == RETRACTLINES) { + if (NewAnimMode()) { + m_AnimTimer1.Reset(); + // A few extra will give some pause + m_AnimSegment = 7; + // Stop showing the site label + UpdateSiteNameLabel(false); + } + + if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) { + m_AnimSegment--; + m_AnimTimer1.Reset(); + // Go through all lines and animate away the ones belonging to this player + for (std::vector::iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) { + if ((*slItr).m_Player == m_AnimMetaPlayer) { + (*slItr).m_OnlyFirstSegments = m_AnimSegment; + (*slItr).m_OnlyLastSegments = -1; + } + } + } + if (m_AnimSegment == 0) { + // Have another player's lines to animate, so start doing that + m_AnimIncomeLine++; + m_AnimIncomeLineChange = true; + ChangeAnimMode(SHRINKCIRCLE); + } + } + } + + // If no more lines, DONE, continue phase to next + if (m_AnimIncomeLine >= m_IncomeSiteLines.size() && !initOverride) + m_ContinuePhase = true; + + // Phase ending, make sure everything is set up to continue + if (m_ContinuePhase || initOverride) { + // Set all lines' fund amounts to their targets and reset their segment animations so we can see them + int lineIndex = 0; + for (std::vector::iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) { + (*slItr).m_FundsAmount = (*slItr).m_FundsTarget; + (*slItr).m_OnlyFirstSegments = -1; + (*slItr).m_OnlyLastSegments = -1; + + // Also, if there are any brain liquidation lines, then adjust the brain counter for that poor player + if ((*slItr).m_Player >= Players::PlayerOne && (*slItr).m_Player < Players::MaxPlayerCount && + m_aBrainSaleIncomeLineIndices[(*slItr).m_Player] == lineIndex) { + // Remove the display adjustment and APPLY the actual change to brains when one is liquidated + g_MetaMan.m_Players[(*slItr).m_Player].SetBrainsInTransit(0); + g_MetaMan.m_Players[(*slItr).m_Player].ChangeBrainPoolCount(-1); + } + // Keep this synched with the iterator + lineIndex++; + } + // Hide all lines for rendering.. appropriate lines will show up when user mouseovers bars in turn phases + m_ActivePlayerIncomeLines = -1; + + // Make sure all fund labels and line ratios are good + for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) { + UpdatePlayerLineRatios(m_IncomeSiteLines, metaPlayer, false); + m_apPlayerBarLabel[metaPlayer]->SetVisible(true); + // Set all funds to the final values, if not a gameover guy + if (g_MetaMan.GetTotalBrainCountOfPlayer(metaPlayer) > 0) + g_MetaMan.m_Players[metaPlayer].m_Funds = GetPlayerLineFunds(m_IncomeSiteLines, metaPlayer, false); + + if (g_SettingsMan.EndlessMetaGameMode()) { g_MetaMan.m_Players[metaPlayer].ChangeBrainPoolCount(20 - g_MetaMan.m_Players[metaPlayer].GetBrainPoolCount()); g_MetaMan.m_Players[metaPlayer].ChangeFunds(10000 - g_MetaMan.m_Players[metaPlayer].GetFunds()); } - } - } + } + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateHumanPlayerTurn ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates a human player's turn -void MetagameGUI::UpdateHumanPlayerTurn(int metaPlayer) -{ - // In-game player - IMPORTANT to pass this to the Scenes, and not the metaplayer - int player = g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(); - - // First do setup - if (g_MetaMan.m_StateChanged) - { - // Reset the target so we don't put the player into autopilot (and also he might not have brains to deploy!) - g_MetaMan.m_Players[metaPlayer].SetOffensiveBudget(0); - g_MetaMan.m_Players[metaPlayer].SetOffensiveTargetName(""); - // Re-set up all build budgets in oz to match what the player spent on this site last round, proportionally - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Only mess with Scenes we can see and that are owned by this player, and he spent something on last turn - if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == g_MetaMan.m_Players[metaPlayer].GetTeam() && (*sItr)->GetBuildBudgetRatio(player) > 0) - (*sItr)->SetBuildBudget(player, g_MetaMan.m_Players[metaPlayer].GetFunds() * (*sItr)->GetBuildBudgetRatio(player)); - } - } - - // Phase ENDING, make sure everything is set up to continue - if (m_ContinuePhase) - { - // Save all the base building budget ratios sowe can re-set the gold values next turn, for player convenience - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Only mess with Scenes we can see and that are owned by this player, and he spent something on last turn - if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == g_MetaMan.m_Players[metaPlayer].GetTeam() && g_MetaMan.m_Players[metaPlayer].GetFunds() > 0) - (*sItr)->SetBuildBudgetRatio(player, (*sItr)->GetBuildBudget(player) / g_MetaMan.m_Players[metaPlayer].GetFunds()); - else - (*sItr)->SetBuildBudgetRatio(player, 0); - } - - // Hide the game message label - m_pGameMessageLabel->SetVisible(false); - } -} +void MetagameGUI::UpdateHumanPlayerTurn(int metaPlayer) { + // In-game player - IMPORTANT to pass this to the Scenes, and not the metaplayer + int player = g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(); + + // First do setup + if (g_MetaMan.m_StateChanged) { + // Reset the target so we don't put the player into autopilot (and also he might not have brains to deploy!) + g_MetaMan.m_Players[metaPlayer].SetOffensiveBudget(0); + g_MetaMan.m_Players[metaPlayer].SetOffensiveTargetName(""); + // Re-set up all build budgets in oz to match what the player spent on this site last round, proportionally + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Only mess with Scenes we can see and that are owned by this player, and he spent something on last turn + if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == g_MetaMan.m_Players[metaPlayer].GetTeam() && (*sItr)->GetBuildBudgetRatio(player) > 0) + (*sItr)->SetBuildBudget(player, g_MetaMan.m_Players[metaPlayer].GetFunds() * (*sItr)->GetBuildBudgetRatio(player)); + } + } + // Phase ENDING, make sure everything is set up to continue + if (m_ContinuePhase) { + // Save all the base building budget ratios sowe can re-set the gold values next turn, for player convenience + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Only mess with Scenes we can see and that are owned by this player, and he spent something on last turn + if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == g_MetaMan.m_Players[metaPlayer].GetTeam() && g_MetaMan.m_Players[metaPlayer].GetFunds() > 0) + (*sItr)->SetBuildBudgetRatio(player, (*sItr)->GetBuildBudget(player) / g_MetaMan.m_Players[metaPlayer].GetFunds()); + else + (*sItr)->SetBuildBudgetRatio(player, 0); + } + + // Hide the game message label + m_pGameMessageLabel->SetVisible(false); + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateBaseBuilding ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the Base Building animation -void MetagameGUI::UpdateBaseBuilding() -{ - m_pPhaseLabel->SetText("Building Bases"); - - // First do setup - if (g_MetaMan.m_StateChanged) - { - // Make sure all fund labels and line ratios are good - for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - { - // Save the fund levels FROM THE START so we can calculate the after state if players skip the animation - g_MetaMan.m_Players[metaPlayer].m_PhaseStartFunds = g_MetaMan.m_Players[metaPlayer].m_Funds; - - // Update the player action lines for all players one last time; this will catch the AI ones as well and reflect their actions - UpdatePlayerActionLines(metaPlayer); - - // Hide all lines so we only see their meters - for (std::vector::iterator slItr = m_ActionSiteLines[metaPlayer].begin(); slItr != m_ActionSiteLines[metaPlayer].end(); ++slItr) - { - (*slItr).m_OnlyFirstSegments = 1; - (*slItr).m_OnlyLastSegments = -1; - } - UpdatePlayerLineRatios(m_ActionSiteLines[metaPlayer], metaPlayer, false, g_MetaMan.m_Players[metaPlayer].m_Funds); - - // Also, for human players, auto design their blueprints if they have chosen to do so - if (g_MetaMan.m_Players[metaPlayer].IsHuman()) - { - // Go through all scenes and check if they're owned by this, and if so, whether their defenses should be automatically designed by canned AI plan - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Move building pieces from the Scene's AI plan queue to the actual blueprints, but only approximately as much as can afford, so the entire AI pre-built base plan isn't revealed - if ((*sItr)->GetTeamOwnership() == g_MetaMan.m_Players[metaPlayer].GetTeam() && (*sItr)->GetAutoDesigned()) - (*sItr)->ApplyAIPlan(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()); - } - } - } - - // Start animating these defensive lines appearing, one after another - m_AnimMetaPlayer = Players::PlayerOne; - m_AnimActionLine = 0; - m_AnimActionLineChange = true; - m_ActionMeterDrawOverride = false; - m_AnimTimer1.Reset(); - ChangeAnimMode(PAUSEANIM); - } - - // If a new line, set it up - if (m_AnimActionLineChange) - { - // New Player also? If so, set up his lines - if (m_AnimActionLine == 0) - { - // Hide all but the meters of all lines - for (std::vector::iterator slItr = m_ActionSiteLines[m_AnimMetaPlayer].begin(); slItr != m_ActionSiteLines[m_AnimMetaPlayer].end(); ++slItr) - { - (*slItr).m_OnlyFirstSegments = 1; - (*slItr).m_OnlyLastSegments = -1; - } - } - - // Did the players change? If so, pause -// if (m_AnimMetaPlayer != m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_Player && !m_AnimTimer1.IsPastRealMS(1000)) -// ChangeAnimMode(PAUSEANIM); - - // Find the next green defense line of this player - while (m_AnimActionLine < m_ActionSiteLines[m_AnimMetaPlayer].size() && m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_Color != c_GUIColorGreen) - m_AnimActionLine++; - - // Regular site line, start with bar bracket and line appearing toward the site -// Blinking is too tedious to watch -// ChangeAnimMode(BLINKMETER); - ChangeAnimMode(LINECONNECTFW); - - m_AnimActionLineChange = false; - } - - // Animate defense spending sitelines into view, and then count them away one by one -// for (vector::iterator slItr = m_ActionSiteLines[m_AnimMetaPlayer].begin(); slItr != m_ActionSiteLines[m_AnimMetaPlayer].end(); ++slItr) - if (!m_ActionSiteLines[m_AnimMetaPlayer].empty() && m_AnimActionLine < m_ActionSiteLines[m_AnimMetaPlayer].size()) - { - if (m_AnimMode == BLINKMETER) - { - if (NewAnimMode()) - { - m_AnimTimer1.Reset(); - // Show the meter - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = -1; - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = 1; - } - // Start blinking - if (!m_AnimTimer1.IsPastRealMS(1500)) - { -// m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(m_AnimTimer1.AlternateReal(150)); - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = m_AnimTimer1.AlternateReal(150) ? 1 : 0; - } - else - { - // Leave the label showing - m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(true); - // Start connecting the meter to the circle! - ChangeAnimMode(LINECONNECTFW); - m_AnimSegment = 1; - } - } - else if (m_AnimMode == LINECONNECTFW) - { - if (NewAnimMode()) - { - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = 1; - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = -1; - m_LineConnected = false; - // Hide the site name over the site loc at first - UpdateSiteNameLabel(false); - m_AnimSegment = 1; - m_AnimTimer1.Reset(); - } - - // If line not yet connected, keep revealing segments - if (!m_LineConnected) - { - if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) - { - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments++; - m_AnimTimer1.Reset(); - } - } - // Oh! Line now completed, pause for a while before continuing to shrinking the meter away - else if (!m_AnimTimer1.IsPastRealMS(500)) - { - // Show the site name over the site loc while we're waiting - UpdateSiteNameLabel(true, m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_SiteName, m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_PlanetPoint); - } - // Done waiting, proceed to shrink the meter - else - { - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = -1; - // Start shrinking the meter, showing that the money is being spent on the base - ChangeAnimMode(SHRINKMETER); - } - } - // Shrink each meter from its current value to 0 - else if (m_AnimMode == SHRINKMETER) - { - if (NewAnimMode()) - { - m_AnimTimer1.Reset(); - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = -1; - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = -1; - // Save the total funds so we can make the proportional animation right - m_AnimTotalFunds = g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds; - // Get a handy pointer to the scene we're talking about - m_pAnimScene = m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_pScene; - RTEAssert(m_pAnimScene, "Couldn't find the scene that we're building the base on!"); - // Using the line target as the going-from point - m_AnimFundsMax = m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount; - // The calculated budget use is the target value we're shrinking to - m_AnimFundsMin = m_AnimFundsMax - m_pAnimScene->CalcBuildBudgetUse(g_MetaMan.m_Players[m_AnimMetaPlayer].GetInGamePlayer(), &m_AnimBuildCount); - // Show the negative change of funds, if any - if ((m_AnimFundsMin - m_AnimFundsMax) < 0) - FundsChangeIndication(m_AnimMetaPlayer, m_AnimFundsMin - m_AnimFundsMax, Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth(), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos()), 2000); - } - // Show the site name over the site loc - UpdateSiteNameLabel(true, m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_SiteName, m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_PlanetPoint); - // Shrink by certain rate to the target value - if (m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount > m_AnimFundsMin) - { - // Make this animation match in duration with the funds change label that is falling - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount = EaseOut(m_AnimFundsMax, m_AnimFundsMin, m_AnimTimer1.GetElapsedRealTimeMS() / 2000);//(m_AnimFundsMax * 2)); - // Adjust the funds to show how much we subtracted this frame - g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = m_AnimTotalFunds - (m_AnimFundsMax - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount); - UpdatePlayerLineRatios(m_ActionSiteLines[m_AnimMetaPlayer], m_AnimMetaPlayer, false, g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds); - m_AnimTimer2.Reset(); - } - // Finished shrinking the meter to the target size, now start disconnecting it - else - { - UpdateSiteNameLabel(false); - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount = m_AnimFundsMin; - UpdatePlayerLineRatios(m_ActionSiteLines[m_AnimMetaPlayer], m_AnimMetaPlayer, false, g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds); - - ChangeAnimMode(LINEDISCONNECTFW); - } - } - else if (m_AnimMode == LINEDISCONNECTFW) - { - if (NewAnimMode()) - { - // Describe what happened over the site while the lines are disconnecting - if (m_AnimBuildCount == 0) - PlayerTextIndication(m_AnimMetaPlayer, m_AnimFundsMax < 100 ? "Can't afford any!" : "Nothing to build!", m_PlanetCenter + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_PlanetPoint, 1500); - else - { - char str[64]; - std::snprintf(str, sizeof(str), m_AnimBuildCount == 1 ? "Built %d item" : "Built %d items", m_AnimBuildCount); - PlayerTextIndication(m_AnimMetaPlayer, str, m_PlanetCenter + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_PlanetPoint, 2500); - } - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = -1; - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = 6; - m_LineConnected = false; - } - - // If line not yet gone, keep removing segments - if (m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments > 0) - { - if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) - { - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments--; - m_AnimTimer1.Reset(); - } - } - // Oh! site line completely gone, remove it and continue to next line - else - { -// No need to do this; the budgets get set to 0 so they will diasappear next update of the bars -// DON'T DO THIS, we need it later to make sure everyhting got built! -// RemoveSiteLine(m_ActionSiteLines[m_AnimMetaPlayer], m_AnimActionLine); - // Dont' need to increment since the vector just shrunk due to removal - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = 0; - m_AnimActionLine++; - m_AnimActionLineChange = true; - ChangeAnimMode(BLINKMETER); - } - } - } - - // If no more lines, DONE, continue to next player, and if no more players, continue to next phase of the round - if (m_AnimActionLine >= m_ActionSiteLines[m_AnimMetaPlayer].size()) - { - m_AnimActionLineChange = true; - m_AnimActionLine = 0; - - if (++m_AnimMetaPlayer >= g_MetaMan.m_Players.size()) - m_ContinuePhase = true; - } - - // Phase ENDING, make sure everything is set up to continue - if (m_ContinuePhase) - { - m_AnimMetaPlayer = g_MetaMan.m_Players.size() - 1; - - // Make sure all fund labels and line ratios are good - Scene *pScene = 0; - for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - { - // Reset the funds to the full value before we started messing with animating them - g_MetaMan.m_Players[metaPlayer].m_Funds = g_MetaMan.m_Players[metaPlayer].m_PhaseStartFunds; - - // Go through the sitelines and make sure all the things that need to be done are done before moving onto next phase - for (std::vector::iterator slItr = m_ActionSiteLines[metaPlayer].begin(); slItr != m_ActionSiteLines[metaPlayer].end(); ++slItr) - { - // APPLY all the defense budget allocations to actually building, but not before subtracting their values from the pre funds - if ((*slItr).m_Color == c_GUIColorGreen) - { - if ((*slItr).m_pScene) - { - // Get a non-const pointer to the scene - pScene = const_cast((*slItr).m_pScene); - // Acutally do the spending on these places that have this player's brain and has funds budgeted for them - // THIS IS WHAT GETS STUFF BUILT - // NOTE THE CAREFUL MAPPING BETWEEN METAPLAYERS AND IN-GAME PLAYERS - int player = g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(); - if (pScene->GetResidentBrain(player) && pScene->GetBuildBudget(player) > 0) - g_MetaMan.m_Players[metaPlayer].m_Funds -= pScene->ApplyBuildBudget(player); - } - } - // Reset all non-defensive lines' segment animations so we can see them - else - { - (*slItr).m_OnlyFirstSegments = -1; - (*slItr).m_OnlyLastSegments = -1; - } - } - - // Make all scene build budgets set to 0; they will be re-set to their previous turns' budget ratios on the next player turns - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - // Only mess with Scenes we can see and that are owned by this player's team - if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == g_MetaMan.m_Players[metaPlayer].GetTeam()) - (*sItr)->SetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), 0); - } - // Refresh the aciton lines one last time before moving on, which will CLEAN OUT all defensive action lines! - UpdatePlayerActionLines(metaPlayer); - } - } -} +void MetagameGUI::UpdateBaseBuilding() { + m_pPhaseLabel->SetText("Building Bases"); + + // First do setup + if (g_MetaMan.m_StateChanged) { + // Make sure all fund labels and line ratios are good + for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) { + // Save the fund levels FROM THE START so we can calculate the after state if players skip the animation + g_MetaMan.m_Players[metaPlayer].m_PhaseStartFunds = g_MetaMan.m_Players[metaPlayer].m_Funds; + + // Update the player action lines for all players one last time; this will catch the AI ones as well and reflect their actions + UpdatePlayerActionLines(metaPlayer); + + // Hide all lines so we only see their meters + for (std::vector::iterator slItr = m_ActionSiteLines[metaPlayer].begin(); slItr != m_ActionSiteLines[metaPlayer].end(); ++slItr) { + (*slItr).m_OnlyFirstSegments = 1; + (*slItr).m_OnlyLastSegments = -1; + } + UpdatePlayerLineRatios(m_ActionSiteLines[metaPlayer], metaPlayer, false, g_MetaMan.m_Players[metaPlayer].m_Funds); + + // Also, for human players, auto design their blueprints if they have chosen to do so + if (g_MetaMan.m_Players[metaPlayer].IsHuman()) { + // Go through all scenes and check if they're owned by this, and if so, whether their defenses should be automatically designed by canned AI plan + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Move building pieces from the Scene's AI plan queue to the actual blueprints, but only approximately as much as can afford, so the entire AI pre-built base plan isn't revealed + if ((*sItr)->GetTeamOwnership() == g_MetaMan.m_Players[metaPlayer].GetTeam() && (*sItr)->GetAutoDesigned()) + (*sItr)->ApplyAIPlan(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()); + } + } + } + + // Start animating these defensive lines appearing, one after another + m_AnimMetaPlayer = Players::PlayerOne; + m_AnimActionLine = 0; + m_AnimActionLineChange = true; + m_ActionMeterDrawOverride = false; + m_AnimTimer1.Reset(); + ChangeAnimMode(PAUSEANIM); + } + + // If a new line, set it up + if (m_AnimActionLineChange) { + // New Player also? If so, set up his lines + if (m_AnimActionLine == 0) { + // Hide all but the meters of all lines + for (std::vector::iterator slItr = m_ActionSiteLines[m_AnimMetaPlayer].begin(); slItr != m_ActionSiteLines[m_AnimMetaPlayer].end(); ++slItr) { + (*slItr).m_OnlyFirstSegments = 1; + (*slItr).m_OnlyLastSegments = -1; + } + } + // Did the players change? If so, pause + // if (m_AnimMetaPlayer != m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_Player && !m_AnimTimer1.IsPastRealMS(1000)) + // ChangeAnimMode(PAUSEANIM); + + // Find the next green defense line of this player + while (m_AnimActionLine < m_ActionSiteLines[m_AnimMetaPlayer].size() && m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_Color != c_GUIColorGreen) + m_AnimActionLine++; + + // Regular site line, start with bar bracket and line appearing toward the site + // Blinking is too tedious to watch + // ChangeAnimMode(BLINKMETER); + ChangeAnimMode(LINECONNECTFW); + + m_AnimActionLineChange = false; + } + + // Animate defense spending sitelines into view, and then count them away one by one + // for (vector::iterator slItr = m_ActionSiteLines[m_AnimMetaPlayer].begin(); slItr != m_ActionSiteLines[m_AnimMetaPlayer].end(); ++slItr) + if (!m_ActionSiteLines[m_AnimMetaPlayer].empty() && m_AnimActionLine < m_ActionSiteLines[m_AnimMetaPlayer].size()) { + if (m_AnimMode == BLINKMETER) { + if (NewAnimMode()) { + m_AnimTimer1.Reset(); + // Show the meter + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = -1; + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = 1; + } + // Start blinking + if (!m_AnimTimer1.IsPastRealMS(1500)) { + // m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(m_AnimTimer1.AlternateReal(150)); + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = m_AnimTimer1.AlternateReal(150) ? 1 : 0; + } else { + // Leave the label showing + m_apPlayerBarLabel[m_AnimMetaPlayer]->SetVisible(true); + // Start connecting the meter to the circle! + ChangeAnimMode(LINECONNECTFW); + m_AnimSegment = 1; + } + } else if (m_AnimMode == LINECONNECTFW) { + if (NewAnimMode()) { + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = 1; + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = -1; + m_LineConnected = false; + // Hide the site name over the site loc at first + UpdateSiteNameLabel(false); + m_AnimSegment = 1; + m_AnimTimer1.Reset(); + } + + // If line not yet connected, keep revealing segments + if (!m_LineConnected) { + if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) { + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments++; + m_AnimTimer1.Reset(); + } + } + // Oh! Line now completed, pause for a while before continuing to shrinking the meter away + else if (!m_AnimTimer1.IsPastRealMS(500)) { + // Show the site name over the site loc while we're waiting + UpdateSiteNameLabel(true, m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_SiteName, m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_PlanetPoint); + } + // Done waiting, proceed to shrink the meter + else { + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = -1; + // Start shrinking the meter, showing that the money is being spent on the base + ChangeAnimMode(SHRINKMETER); + } + } + // Shrink each meter from its current value to 0 + else if (m_AnimMode == SHRINKMETER) { + if (NewAnimMode()) { + m_AnimTimer1.Reset(); + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = -1; + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = -1; + // Save the total funds so we can make the proportional animation right + m_AnimTotalFunds = g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds; + // Get a handy pointer to the scene we're talking about + m_pAnimScene = m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_pScene; + RTEAssert(m_pAnimScene, "Couldn't find the scene that we're building the base on!"); + // Using the line target as the going-from point + m_AnimFundsMax = m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount; + // The calculated budget use is the target value we're shrinking to + m_AnimFundsMin = m_AnimFundsMax - m_pAnimScene->CalcBuildBudgetUse(g_MetaMan.m_Players[m_AnimMetaPlayer].GetInGamePlayer(), &m_AnimBuildCount); + // Show the negative change of funds, if any + if ((m_AnimFundsMin - m_AnimFundsMax) < 0) + FundsChangeIndication(m_AnimMetaPlayer, m_AnimFundsMin - m_AnimFundsMax, Vector(m_apPlayerBarLabel[m_AnimMetaPlayer]->GetXPos() + m_apPlayerBarLabel[m_AnimMetaPlayer]->GetWidth(), m_apPlayerBarLabel[m_AnimMetaPlayer]->GetYPos()), 2000); + } + // Show the site name over the site loc + UpdateSiteNameLabel(true, m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_SiteName, m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_PlanetPoint); + // Shrink by certain rate to the target value + if (m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount > m_AnimFundsMin) { + // Make this animation match in duration with the funds change label that is falling + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount = EaseOut(m_AnimFundsMax, m_AnimFundsMin, m_AnimTimer1.GetElapsedRealTimeMS() / 2000); //(m_AnimFundsMax * 2)); + // Adjust the funds to show how much we subtracted this frame + g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds = m_AnimTotalFunds - (m_AnimFundsMax - m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount); + UpdatePlayerLineRatios(m_ActionSiteLines[m_AnimMetaPlayer], m_AnimMetaPlayer, false, g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds); + m_AnimTimer2.Reset(); + } + // Finished shrinking the meter to the target size, now start disconnecting it + else { + UpdateSiteNameLabel(false); + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_FundsAmount = m_AnimFundsMin; + UpdatePlayerLineRatios(m_ActionSiteLines[m_AnimMetaPlayer], m_AnimMetaPlayer, false, g_MetaMan.m_Players[m_AnimMetaPlayer].m_Funds); + + ChangeAnimMode(LINEDISCONNECTFW); + } + } else if (m_AnimMode == LINEDISCONNECTFW) { + if (NewAnimMode()) { + // Describe what happened over the site while the lines are disconnecting + if (m_AnimBuildCount == 0) + PlayerTextIndication(m_AnimMetaPlayer, m_AnimFundsMax < 100 ? "Can't afford any!" : "Nothing to build!", m_PlanetCenter + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_PlanetPoint, 1500); + else { + char str[64]; + std::snprintf(str, sizeof(str), m_AnimBuildCount == 1 ? "Built %d item" : "Built %d items", m_AnimBuildCount); + PlayerTextIndication(m_AnimMetaPlayer, str, m_PlanetCenter + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_PlanetPoint, 2500); + } + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyFirstSegments = -1; + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = 6; + m_LineConnected = false; + } + + // If line not yet gone, keep removing segments + if (m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments > 0) { + if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) { + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments--; + m_AnimTimer1.Reset(); + } + } + // Oh! site line completely gone, remove it and continue to next line + else { + // No need to do this; the budgets get set to 0 so they will diasappear next update of the bars + // DON'T DO THIS, we need it later to make sure everyhting got built! + // RemoveSiteLine(m_ActionSiteLines[m_AnimMetaPlayer], m_AnimActionLine); + // Dont' need to increment since the vector just shrunk due to removal + m_ActionSiteLines[m_AnimMetaPlayer][m_AnimActionLine].m_OnlyLastSegments = 0; + m_AnimActionLine++; + m_AnimActionLineChange = true; + ChangeAnimMode(BLINKMETER); + } + } + } + + // If no more lines, DONE, continue to next player, and if no more players, continue to next phase of the round + if (m_AnimActionLine >= m_ActionSiteLines[m_AnimMetaPlayer].size()) { + m_AnimActionLineChange = true; + m_AnimActionLine = 0; + + if (++m_AnimMetaPlayer >= g_MetaMan.m_Players.size()) + m_ContinuePhase = true; + } + + // Phase ENDING, make sure everything is set up to continue + if (m_ContinuePhase) { + m_AnimMetaPlayer = g_MetaMan.m_Players.size() - 1; + + // Make sure all fund labels and line ratios are good + Scene* pScene = 0; + for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) { + // Reset the funds to the full value before we started messing with animating them + g_MetaMan.m_Players[metaPlayer].m_Funds = g_MetaMan.m_Players[metaPlayer].m_PhaseStartFunds; + + // Go through the sitelines and make sure all the things that need to be done are done before moving onto next phase + for (std::vector::iterator slItr = m_ActionSiteLines[metaPlayer].begin(); slItr != m_ActionSiteLines[metaPlayer].end(); ++slItr) { + // APPLY all the defense budget allocations to actually building, but not before subtracting their values from the pre funds + if ((*slItr).m_Color == c_GUIColorGreen) { + if ((*slItr).m_pScene) { + // Get a non-const pointer to the scene + pScene = const_cast((*slItr).m_pScene); + // Acutally do the spending on these places that have this player's brain and has funds budgeted for them + // THIS IS WHAT GETS STUFF BUILT + // NOTE THE CAREFUL MAPPING BETWEEN METAPLAYERS AND IN-GAME PLAYERS + int player = g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(); + if (pScene->GetResidentBrain(player) && pScene->GetBuildBudget(player) > 0) + g_MetaMan.m_Players[metaPlayer].m_Funds -= pScene->ApplyBuildBudget(player); + } + } + // Reset all non-defensive lines' segment animations so we can see them + else { + (*slItr).m_OnlyFirstSegments = -1; + (*slItr).m_OnlyLastSegments = -1; + } + } + + // Make all scene build budgets set to 0; they will be re-set to their previous turns' budget ratios on the next player turns + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + // Only mess with Scenes we can see and that are owned by this player's team + if ((*sItr)->IsRevealed() && (*sItr)->GetTeamOwnership() == g_MetaMan.m_Players[metaPlayer].GetTeam()) + (*sItr)->SetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), 0); + } + // Refresh the aciton lines one last time before moving on, which will CLEAN OUT all defensive action lines! + UpdatePlayerActionLines(metaPlayer); + } + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetupOffensives @@ -4212,974 +3810,858 @@ void MetagameGUI::UpdateBaseBuilding() // Description: Sets up the Activities that represent all the offensive actions of // the teams this round. -void MetagameGUI::SetupOffensives() -{ - // Clear out the old ones, if any - g_MetaMan.ClearActivities(); - - // Go through all players' offensive actions and create the unique activities - std::string targetName; - float offensiveBudget; - bool playerDone = false; - int team = Activity::NoTeam; - int offensiveCount = 0; - for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) - { - playerDone = false; - team = g_MetaMan.m_Players[metaPlayer].GetTeam(); - // If we have a selected offensive target, find its scene in the metagame - targetName = g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName(); - offensiveBudget = g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget(); - if (!targetName.empty() && offensiveBudget > 0) - { - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); !playerDone && sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - if ((*sItr)->IsRevealed() && (*sItr)->GetPresetName() == targetName) - { - // Now check that we haven't already created an offensive action at this site - for (std::vector::iterator aItr = g_MetaMan.m_RoundOffensives.begin(); aItr != g_MetaMan.m_RoundOffensives.end(); ++aItr) - { - if ((*aItr)->GetSceneName() == targetName) - { - // Ok, activity at this site already exists, so add this player to the attacking forces - (*aItr)->AddPlayer(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), g_MetaMan.m_Players[metaPlayer].IsHuman(), team, offensiveBudget, &(g_MetaMan.GetTeamIcon(team))); - // Move onto next player - playerDone = true; - } - } - - // Create new offensive Activity - if (!playerDone) - { - // Set up the MetaFight activity - GAScripted *pOffensive = new GAScripted; - pOffensive->Create("Base.rte/Activities/MetaFight.lua", "MetaFight"); - char str[64]; - std::snprintf(str, sizeof(str), "R%dA%d", g_MetaMan.m_CurrentRound + 1, offensiveCount); - pOffensive->SetPresetName(g_MetaMan.GetGameName() + str); - // Associate the name of the scene with where this thing is supposed to take place - pOffensive->SetSceneName(targetName); - // Gotto deact all players since by default there is one in slot 1 - pOffensive->ClearPlayers(); +void MetagameGUI::SetupOffensives() { + // Clear out the old ones, if any + g_MetaMan.ClearActivities(); + + // Go through all players' offensive actions and create the unique activities + std::string targetName; + float offensiveBudget; + bool playerDone = false; + int team = Activity::NoTeam; + int offensiveCount = 0; + for (int metaPlayer = Players::PlayerOne; metaPlayer < g_MetaMan.m_Players.size(); ++metaPlayer) { + playerDone = false; + team = g_MetaMan.m_Players[metaPlayer].GetTeam(); + // If we have a selected offensive target, find its scene in the metagame + targetName = g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName(); + offensiveBudget = g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget(); + if (!targetName.empty() && offensiveBudget > 0) { + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); !playerDone && sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + if ((*sItr)->IsRevealed() && (*sItr)->GetPresetName() == targetName) { + // Now check that we haven't already created an offensive action at this site + for (std::vector::iterator aItr = g_MetaMan.m_RoundOffensives.begin(); aItr != g_MetaMan.m_RoundOffensives.end(); ++aItr) { + if ((*aItr)->GetSceneName() == targetName) { + // Ok, activity at this site already exists, so add this player to the attacking forces + (*aItr)->AddPlayer(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), g_MetaMan.m_Players[metaPlayer].IsHuman(), team, offensiveBudget, &(g_MetaMan.GetTeamIcon(team))); + // Move onto next player + playerDone = true; + } + } + + // Create new offensive Activity + if (!playerDone) { + // Set up the MetaFight activity + GAScripted* pOffensive = new GAScripted; + pOffensive->Create("Base.rte/Activities/MetaFight.lua", "MetaFight"); + char str[64]; + std::snprintf(str, sizeof(str), "R%dA%d", g_MetaMan.m_CurrentRound + 1, offensiveCount); + pOffensive->SetPresetName(g_MetaMan.GetGameName() + str); + // Associate the name of the scene with where this thing is supposed to take place + pOffensive->SetSceneName(targetName); + // Gotto deact all players since by default there is one in slot 1 + pOffensive->ClearPlayers(); // Set difficulty pOffensive->SetDifficulty(g_MetaMan.m_Difficulty); // Set AI skill levels for (int t = Activity::TeamOne; t < Activity::MaxTeamCount; t++) pOffensive->SetTeamAISkill(t, g_MetaMan.m_TeamAISkill[t]); - // Attacker - pOffensive->AddPlayer(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), g_MetaMan.m_Players[metaPlayer].IsHuman(), team, offensiveBudget, &(g_MetaMan.GetTeamIcon(team))); - - // Unless exploring an unclaimed spot, there's going to be defenders - if ((*sItr)->GetTeamOwnership() != Activity::NoTeam) - { - // Go through all players and add the ones of the defending team, based on who has resident brains here - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Got to remember to translate from metagame player index into the in-game player index -// TODO: Remove this requirement to have a brain resident to play? error-prone and not so fun for co-op player on sme team if they can't all play -// if (g_MetaMan.m_Players[mp].GetTeam() == (*sItr)->GetTeamOwnership()) - if ((*sItr)->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - pOffensive->AddPlayer(g_MetaMan.m_Players[mp].GetInGamePlayer(), g_MetaMan.m_Players[mp].IsHuman(), g_MetaMan.m_Players[mp].GetTeam(), g_MetaMan.GetRemainingFundsOfPlayer(mp, *sItr, false, false), &(g_MetaMan.GetTeamIcon(g_MetaMan.m_Players[mp].GetTeam()))); - } - } - } - - // Add the new Activity to the queue of offensive events that should happen this round! - g_MetaMan.m_RoundOffensives.push_back(pOffensive); - // Done with this guy's offensive action - playerDone = true; - // Count this new offensive Activity - ++offensiveCount; - } - } - } - } - } -/* - // Check all created Offensive Activites that none only contain AI MetaPlayer:s, because they have to be handled differently and not actually played - for (vector::iterator aItr = g_MetaMan.m_RoundOffensives.begin(); aItr != g_MetaMan.m_RoundOffensives.end(); ++aItr) - { - // No hooomins?? - if ((*aItr)->GetHumanCount() < 1) - { - - } - } -*/ -} + // Attacker + pOffensive->AddPlayer(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(), g_MetaMan.m_Players[metaPlayer].IsHuman(), team, offensiveBudget, &(g_MetaMan.GetTeamIcon(team))); + + // Unless exploring an unclaimed spot, there's going to be defenders + if ((*sItr)->GetTeamOwnership() != Activity::NoTeam) { + // Go through all players and add the ones of the defending team, based on who has resident brains here + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Got to remember to translate from metagame player index into the in-game player index + // TODO: Remove this requirement to have a brain resident to play? error-prone and not so fun for co-op player on sme team if they can't all play + // if (g_MetaMan.m_Players[mp].GetTeam() == (*sItr)->GetTeamOwnership()) + if ((*sItr)->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + pOffensive->AddPlayer(g_MetaMan.m_Players[mp].GetInGamePlayer(), g_MetaMan.m_Players[mp].IsHuman(), g_MetaMan.m_Players[mp].GetTeam(), g_MetaMan.GetRemainingFundsOfPlayer(mp, *sItr, false, false), &(g_MetaMan.GetTeamIcon(g_MetaMan.m_Players[mp].GetTeam()))); + } + } + } + + // Add the new Activity to the queue of offensive events that should happen this round! + g_MetaMan.m_RoundOffensives.push_back(pOffensive); + // Done with this guy's offensive action + playerDone = true; + // Count this new offensive Activity + ++offensiveCount; + } + } + } + } + } + /* + // Check all created Offensive Activites that none only contain AI MetaPlayer:s, because they have to be handled differently and not actually played + for (vector::iterator aItr = g_MetaMan.m_RoundOffensives.begin(); aItr != g_MetaMan.m_RoundOffensives.end(); ++aItr) + { + // No hooomins?? + if ((*aItr)->GetHumanCount() < 1) + { + + } + } + */ +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Method: UpdateOffensives +////////////////////////////////////////////////////////////////////////////////////////// +// Description: Updates the offensive actions animation + +void MetagameGUI::UpdateOffensives() { + char str[256]; + + /////////////////////////////////// + // First do setup of all the activities we're going to play in order + if (g_MetaMan.m_StateChanged) { + // Generate all offensive activites, based on the players' offensive moves + // But ONLY do this if it doesn't appear we are continuing a loaded game that has already completed battles!! + if (g_MetaMan.m_CurrentOffensive == 0) + SetupOffensives(); + + // Hide all income lines so they don't flash once on phase start + for (std::vector::iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) + (*slItr).m_OnlyFirstSegments = (*slItr).m_OnlyLastSegments = 0; + + // Set the battle info display to the start of its animation. + UpdatePreBattleAttackers(0); + UpdatePreBattleDefenders(0); + + // Start processing the first activity + m_AnimActivityChange = true; + // Turn off the post battle review flag + m_PostBattleReview = false; + // Don't clear this! We may have loaded a game with a battle completed so we don't want to start on 0 again + // g_MetaMan.m_CurrentOffensive = 0; + } + + // No Offensives this round? then skip all this business + if (g_MetaMan.m_RoundOffensives.empty() || g_MetaMan.m_CurrentOffensive < 0 || g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size()) { + m_ContinuePhase = true; + // TODO: Make sure that quitting out here is ok + return; + } + + // If we're still reviewing a previous battle and the player decided to skip to the next battle right away then do so and move directly to the next offensive + if (m_PostBattleReview && !m_PreTurn) { + // We're done with this activity + m_AnimActivityChange = true; + // Turn off the post battle review flag too + m_PostBattleReview = false; + // This finishes off the current battle we're reviewing, and moves onto the next + if (!FinalizeOffensive()) + return; + } + + //////////////////////////////////////////////////// + // New Offensive, so set it up + if (m_AnimActivityChange) { + // Show which mission we're on of all the offensive activities in queue + std::snprintf(str, sizeof(str), "Battle %d of %d", (g_MetaMan.m_CurrentOffensive + 1), (int)(g_MetaMan.m_RoundOffensives.size())); + m_pPhaseLabel->SetText(str); + + // Find the scene being attacked in this offensive Activity + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + if ((*sItr)->IsRevealed() && (*sItr)->GetPresetName() == g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetSceneName()) + m_pAnimScene = (*sItr); + } + RTEAssert(m_pAnimScene, "Couldn't find the Site that has been selected as attacked!"); + + // It's owned by a team, so set up and show its defenders + if (m_pAnimScene->GetTeamOwnership() != Activity::NoTeam) + m_AnimDefenseTeam = m_pAnimScene->GetTeamOwnership(); + + // Set up all the offensive and defensive lines for each player involved in this site's battle + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // If this player is involved in this fight, show his lines etc + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // Save the fund levels FROM THE START of each battle so we can calculate the after state if players skip the animation + g_MetaMan.m_Players[mp].m_PhaseStartFunds = g_MetaMan.m_Players[mp].m_Funds; + + // Rebuild all the action site lines for the player + float meterStart = UpdatePlayerActionLines(mp); + // Reset battle funds and other animation indicators + m_aBattleFunds[mp] = 0; + m_aBattleAttacker[mp] = false; + m_aAnimDestroyed[mp] = false; + m_aBrainIconPos[mp].Reset(); + + // What is this player attacking? + std::string targetName = g_MetaMan.m_Players[mp].GetOffensiveTargetName(); + float offensiveBudget = g_MetaMan.m_Players[mp].GetOffensiveBudget(); + // If attacking anything, see if it's the current site that's getting hit + if (!targetName.empty() && offensiveBudget > 0) { + // Set the displayed battle funds for this player if it's attacking this site + if (m_pAnimScene->GetPresetName() == targetName) { + m_aBattleFunds[mp] = offensiveBudget; + m_aBattleAttacker[mp] = true; + } + } + + // If this player owns and has a resident brain at this site, add his defending action line of unallocated funds, + // and also update the offensive activity itself to match teh defensive funds to what he has left after multiple completed offensives this turn + if (m_pAnimScene->GetTeamOwnership() == g_MetaMan.m_Players[mp].GetTeam() && m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // Find out how much gold this player has left after all allocations and previous battles this turn + float remainingFunds = g_MetaMan.GetRemainingFundsOfPlayer(mp); + // UPDATE the contributed funds of the defending player to whatever gold he has left after all (if any) previous battles this turn! + bool updated = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->UpdatePlayerFundsContribution(g_MetaMan.m_Players[mp].GetInGamePlayer(), remainingFunds); + // Add the unused build budget FOR THIS SCENE to the battle funds display - doesn't do anyhting now because build budgets are set to 0 in UpdateBaseBuilding + m_aBattleFunds[mp] += m_pAnimScene->GetBuildBudget(g_MetaMan.m_Players[mp].GetInGamePlayer()); + // Add the unallocated funds meter, even if there's 0 left.. it shows why there's 0, and also the meter can grow after battle if the defending player digs up gold + if (remainingFunds >= 0) { + // Tell the player why he has no defensive money... uh too early + // if (remainingFunds <= 0) + // PlayerTextIndication(mp, "No unallocated funds!", Vector(m_apPlayerBox[mp]->GetXPos(), m_apPlayerBox[mp]->GetYPos()), 1500); + + m_ActionSiteLines[mp].push_back(SiteLine(mp, meterStart, remainingFunds / g_MetaMan.m_Players[mp].m_Funds, m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), m_pAnimScene->GetPresetName(), m_pAnimScene, c_GUIColorYellow, -1, -1, 60, 1.0f, g_MetaMan.IsActiveTeam(m_pAnimScene->GetTeamOwnership()))); + // This will actually affect the line meter + m_ActionSiteLines[mp].back().m_FundsAmount = remainingFunds; + // Add to the battle funds display too + m_aBattleFunds[mp] += remainingFunds; + // Move the meter start position forward as necessary + meterStart += m_ActionSiteLines[mp].back().m_MeterAmount; + } + } + + // Hide all action lines except for their bars + for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) { + (*slItr).m_OnlyFirstSegments = 1; + (*slItr).m_OnlyLastSegments = -1; + } + + // Update the ratios now that we've messed with them + UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); + } + // If player not involved in the fight then hide all lines of this player + { + for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) { + (*slItr).m_OnlyFirstSegments = 0; + (*slItr).m_OnlyLastSegments = -1; + } + } + } + + // Start out the brain travel animaitons hidden and at 0 + ResetBattleInfo(); + UpdatePreBattleAttackers(0); + UpdatePreBattleDefenders(0); + // Make the meters draw all the time + // This might not be such a great thing after all to show all the bars.. not needed anymore since we're not connecting to defensive budgets in offensive phases anymore + // m_ActionMeterDrawOverride = true; + + // Show that we are waiting for the players to press the Continue button so we can start the Activity we're setting up here + m_PreTurn = true; + m_BattleToResume = false; + // New battle, reset the post battle flags + m_PostBattleReview = false; + m_BattleCausedOwnershipChange = false; + m_PreBattleTeamOwnership = Activity::NoTeam; + + ChangeAnimMode(TARGETZEROING); + m_AnimActivityChange = false; + } + + ///////////////////////////////////////////// + // Make a menacing target crosshairs zero in on the site we're playing this activity on + if (m_AnimMode == TARGETZEROING) { + if (NewAnimMode()) { + m_SiteAttackTarget.m_CenterPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); + m_SiteAttackTarget.m_AnimProgress = 0; + m_SiteAttackTarget.m_Color = c_GUIColorRed; + m_AnimModeDuration = 600; + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + } + m_SiteAttackTarget.m_AnimProgress = EaseOut(0.0, 1.0, m_AnimTimer1.GetElapsedRealTimeMS() / m_AnimModeDuration); + + // BAM - show the name of the site and start showing who's attacking it + if (m_SiteAttackTarget.m_AnimProgress == 1.0) { + // // After a while, go on to show the attacker action site lines + // if (m_AnimTimer1.IsPastRealMS(1200)) + // { + ChangeAnimMode(LINECONNECTFW); + } + } + + ///////////////////////////////////////////// + // Show who's attacking this target; connect the attack budget lines (while still zeroed) and animate the brains going there from their respective pools + if (m_AnimMode == LINECONNECTFW) { + if (NewAnimMode()) { + // Make sure all offensive action lines are set up for this phase + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Find all players that are active during this battle + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // If this player is attacking, indicate that we've got a brain in transit.. this just changes the display, not the actual brain pool count yet + if (m_pAnimScene->GetPresetName() == g_MetaMan.m_Players[mp].GetOffensiveTargetName()) + g_MetaMan.m_Players[mp].ChangeBrainsInTransit(1); + + // Find their offensive funds site lines + for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) { + // Offensive lines are red; set them up to have their meters visible + if ((*slItr).m_Color == c_GUIColorRed) { + (*slItr).m_OnlyFirstSegments = 1; + (*slItr).m_OnlyLastSegments = -1; + } + } + } + } + + m_AnimSegment = 0; + m_AnimModeDuration = 2000; + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + } + + // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site + UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); + + // Keep revealing segments simultaneously from all attackers until they are all revealed + if (m_AnimSegment < 15) { + if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) { + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only care if this player is attacking this site + if (m_pAnimScene->GetPresetName() == g_MetaMan.m_Players[mp].GetOffensiveTargetName() && g_MetaMan.m_Players[mp].GetOffensiveBudget() > 0) + // if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) + { + // Find their offensive funds site lines + for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) { + // Offensive lines are red; grow all of em + if ((*slItr).m_Color == c_GUIColorRed) + (*slItr).m_OnlyFirstSegments++; + } + } + } + + m_AnimSegment++; + m_AnimTimer1.Reset(); + } + } + + // Animate the crosshairs subtly + m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); + + // Animate the brain label travel animations + UpdatePreBattleAttackers(EaseInOut(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); + UpdatePreBattleDefenders(0); + + // Just wait until the players hit the continue button.. + if (m_BattleToResume) + m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Resume <" : "Resume"); + else + m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Start! <" : "Start!"); + + // Move onto growing the attack budget meters + if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) + ChangeAnimMode(SHOWDEFENDERS); + } + + ///////////////////////////////////////////// + // Show the defending brains for this battle + if (m_AnimMode == SHOWDEFENDERS) { + if (NewAnimMode()) { + // Make sure all defensive and unallocated budget action lines are set up for this phase + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + } + + m_AnimModeDuration = 500; + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + } + + // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site + UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); + + // Animate the crosshairs subtly + m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); + + // Keep the brain label travel animations in one spot and their labels updated + UpdatePreBattleAttackers(1.0); + UpdatePreBattleDefenders(EaseOut(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); + + // Just wait until the players hit the continue button.. + if (m_BattleToResume) + m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Resume <" : "Resume"); + else + m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Start! <" : "Start!"); + + // Move onto growing the attack budget meters + if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) + ChangeAnimMode(LINECONNECTBW); + } + + ///////////////////////////////////////////// + // Show the defending budget lines of the players who are defending this site + if (m_AnimMode == LINECONNECTBW) { + if (NewAnimMode()) { + // Make sure all defensive and unallocated budget action lines are set up for this phase + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only care of this player is involved in this particular battle + // Only care about defending players of this site + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer()) && + m_pAnimScene->GetTeamOwnership() == g_MetaMan.m_Players[mp].GetTeam() && + m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // Find their defensive funds site lines + for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) { + // Offensive lines are green; unallocated funds are white - attach all of them + if ((*slItr).m_Color != c_GUIColorRed) { + (*slItr).m_OnlyFirstSegments = -1; + (*slItr).m_OnlyLastSegments = 0; + } + } + } + } + + m_AnimSegment = 0; + m_AnimModeDuration = 1000; + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + } + + // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site + UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); + + // Keep revealing segments simultaneously from all attackers until they are all revealed + if (m_AnimSegment < 15) { + if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) { + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only care of this player is involved in this particular battle + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // Find their defensive-related funds site lines + for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) { + // Offensive lines are red; grow the defensive ones relevant to this scene + if ((*slItr).m_Color != c_GUIColorRed && m_pAnimScene->GetPresetName() == (*slItr).m_SiteName && + (m_pAnimScene->GetTeamOwnership() == g_MetaMan.m_Players[mp].GetTeam() && m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer()))) + (*slItr).m_OnlyLastSegments++; + } + } + } + m_AnimSegment++; + m_AnimTimer1.Reset(); + } + } + + // Animate the crosshairs subtly + m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); + + // Keep the brain label travel animations in one spot and their labels updated + UpdatePreBattleAttackers(1.0); + UpdatePreBattleDefenders(1.0); + + // Just wait until the players hit the continue button.. + if (m_BattleToResume) + m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Resume <" : "Resume"); + else + m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Start! <" : "Start!"); + } + + ////////////////////////////////// + // POST BATTLE REVIEW + + ///////////////////////////////////////////// + // A Short pause for after players come out of a run simulation battle, + // so they can orient themselves visually before battle review starts happening + if (m_AnimMode == SHOWPOSTBATTLEPAUSE) { + if (NewAnimMode()) { + // The length of the pause + m_AnimModeDuration = 2000; + + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + } + + // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site + UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); + // Animate the crosshairs subtly + m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); + + UpdatePostBattleRetreaters(0); + UpdatePostBattleResidents(0); + + // Give the players the option to skip the post battle review and go to next battle, or phase if there aren't any more battles + m_apMetaButton[CONTINUE]->SetText("Next"); + + // Move onto having the victorious brains go back inside the site and take it over + if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) + ChangeAnimMode(SHOWPOSTBATTLEBALANCE); + } + + ///////////////////////////////////////////// + // Show how much is being allocated to attacking this place + if (m_AnimMode == SHOWPOSTBATTLEBALANCE) { + if (NewAnimMode()) { + m_AnimModeDuration = 2000; + + // Make sure all offensive action-related lines are set up for this battle review animation + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only the players of this battle + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // Find their site lines that are connected to the site + for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) { + // Only lines that are connected are relevant + if ((*slItr).m_OnlyFirstSegments > 1 || (*slItr).m_OnlyLastSegments > 1) { + // Re-set the funds amount we're going from - actually, not necessary + (*slItr).m_FundsAmount = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsContribution(g_MetaMan.m_Players[mp].GetInGamePlayer()); + // Set the target we're going toward + (*slItr).m_FundsTarget = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsShare(g_MetaMan.m_Players[mp].GetInGamePlayer()); + // Start the battle money display off at the right point too + m_aBattleFunds[mp] = (*slItr).m_FundsAmount; + // Display the change amount over/under the player bar + FundsChangeIndication(mp, (*slItr).m_FundsTarget - (*slItr).m_FundsAmount, Vector(m_apPlayerBarLabel[mp]->GetXPos() + m_apPlayerBarLabel[mp]->GetWidth(), m_apPlayerBarLabel[mp]->GetYPos()), m_AnimModeDuration); + + // Update the ratios.. shouldn't do anyhitng, really + UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); + } + } + } + } + + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + } + + // Find the players who are involved in this battle + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only the players of this battle + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // The brains who DID NOT MAKE IT - Show them blowing up at some random interval into the animation + if (!m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer()) && + !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // If not yet blown up, then see if we should yet + if (!m_aAnimDestroyed[mp] && m_AnimTimer2.GetElapsedRealTimeMS() > (m_AnimModeDuration * 0.5F) && RandomNum() < 0.05F) { + // Add circle explosion effect to where the brain icon used to be + m_SiteSwitchIndicators.push_back(SiteTarget(m_aBrainIconPos[mp], 0, SiteTarget::CIRCLEGROW, c_GUIColorRed)); + // Switch the brain icon drawing off + m_aAnimDestroyed[mp] = true; + } + } + + // Find their site lines that are connected to the site + for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) { + // Only lines that are connected are relevant + if ((*slItr).m_OnlyFirstSegments > 1 || (*slItr).m_OnlyLastSegments > 1) { + float startFunds = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsContribution(g_MetaMan.m_Players[mp].GetInGamePlayer()); + + // Grow/shrink all the meters, showing how the funds changed during battle + if (m_AnimTimer1.GetElapsedRealTimeMS() < m_AnimModeDuration) { + // Make this animation correlate in duration with the funds change label that rises + (*slItr).m_FundsAmount = EaseOut(startFunds, (*slItr).m_FundsTarget, m_AnimTimer1.GetElapsedRealTimeMS() / m_AnimModeDuration); + // Update the battle funds display; it will be shown in the brian action label + m_aBattleFunds[mp] = (*slItr).m_FundsAmount; + // Adjust the funds to show how much we subtracted this frame - this will be reset in the end anyway, it's just for show during this battle review animation + g_MetaMan.m_Players[mp].m_Funds = g_MetaMan.m_Players[mp].m_PhaseStartFunds - (startFunds - (*slItr).m_FundsAmount); + UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); + } + // Finished growing the meter to the target size + else { + m_aBattleFunds[mp] = (*slItr).m_FundsAmount = (*slItr).m_FundsTarget; + g_MetaMan.m_Players[mp].m_Funds = g_MetaMan.m_Players[mp].m_PhaseStartFunds - (startFunds - (*slItr).m_FundsAmount); + UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); + } + } + } + } + } + + // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site + UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); + // Animate the crosshairs subtly + m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); + + // Animate the brain label travel animations + UpdatePostBattleRetreaters(0); + UpdatePostBattleResidents(0); + + UpdatePlayerBars(); + // Give the players the option to skip the post battle review and go to next battle, or phase if there aren't any more battles + m_apMetaButton[CONTINUE]->SetText("Next"); + + // Move onto showing what happened to each brain + if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) { + // Blow up any remaining brains who are doomed + // Find the players who are involved in this battle + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only the players of this battle who didn't evacuate + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer()) && + !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // The brains who DID NOT MAKE IT - Show them blowing up + if (!m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // If not yet blown up, then we should be + if (!m_aAnimDestroyed[mp]) { + // Add circle explosion effect to where the brain icon used to be + m_SiteSwitchIndicators.push_back(SiteTarget(m_aBrainIconPos[mp], 0, SiteTarget::CIRCLEGROW, c_GUIColorRed)); + // Switch the brain icon drawing off + m_aAnimDestroyed[mp] = true; + } + } + } + } + ChangeAnimMode(SHOWPOSTBATTLEBRAINS); + } + } + + ///////////////////////////////////////////// + // Show what happened to the brains after a battle + if (m_AnimMode == SHOWPOSTBATTLEBRAINS) { + if (NewAnimMode()) { + // Make sure all defensive and unallocated budget action lines are set up for this phase + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + } + + // The duration of this depends on whethere there are any evacuees to travel back + m_AnimModeDuration = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->AnyBrainWasEvacuated() ? 2000 : 500; + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + } + + // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site + UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); + // Animate the crosshairs subtly + m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); + + // The retreating brain label travel animations get updated to go back to their pools + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->AnyBrainWasEvacuated()) + UpdatePostBattleRetreaters(EaseInOut(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); + else + UpdatePostBattleRetreaters(1.0); + UpdatePostBattleResidents(0); + + // Give the players the option to skip the post battle review and go to next battle, or phase if there aren't any more battles + m_apMetaButton[CONTINUE]->SetText("Next"); + + // Move onto having the victorious brains go back inside the site and take it over + if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) { + // Change the display to show the evacuees transferring back to their brain pools + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->AnyBrainWasEvacuated()) { + // Find the players who are evacuated anything this battle + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only the players of this battle who evac'd their brain + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // Both an Attacker aborting an attack, and a Defender abandoning his site has the same effect here on the brian pool display + g_MetaMan.m_Players[mp].ChangeBrainsInTransit(-1); + } + } + } + + ChangeAnimMode(SHOWNEWRESIDENTS); + } + } + + ///////////////////////////////////////////// + // Show the victorious brains going back into the site and taking it over + if (m_AnimMode == SHOWNEWRESIDENTS) { + if (NewAnimMode()) { + // Make sure all defensive and unallocated budget action lines are set up for this phase + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + } + + m_AnimModeDuration = m_BattleCausedOwnershipChange ? 500 : 750; + m_AnimTimer1.Reset(); + m_AnimTimer2.Reset(); + } + + // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site + UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); + // Animate the crosshairs subtly + m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); + + // The brains going back into their lair are animated + UpdatePostBattleRetreaters(1.0); + // Tweak the animaiton slightly based on whether the site switched hands or not + if (m_BattleCausedOwnershipChange) + UpdatePostBattleResidents(EaseIn(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); + else + UpdatePostBattleResidents(EaseOut(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); + + // Find the players who are involved in this battle + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only the players of this battle + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // The guys who died - remove their icons completely + if (!m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) + m_apPlayerBrainTravelLabel[mp]->SetVisible(false); + } + } + + // Give the players the option to skip the post battle review and go to next battle, or phase if there aren't any more battles + m_apMetaButton[CONTINUE]->SetText("Next"); + // Move onto the next offensive in line, if any! + if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) { + // Quit if there's no more offensives after the one we just finalized + if (!FinalizeOffensive()) + return; + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateOffensives -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the offensive actions animation + ///////////////////////////////////////////// + // If the player have chosen to start the attack activity by hitting the continue button, then do eeeet + if (!m_PreTurn) { + // Make sure this is set back + m_apMetaButton[CONTINUE]->SetText("Start!"); + UpdateSiteNameLabel(false); + + // Max out the offensive lines in case the player started the battle before the lines were fully animated as connected + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only care of this player is involved in this particular battle + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) { + // Only show lines that are going to the site we're battling on + if ((*slItr).m_SiteName == m_pAnimScene->GetPresetName()) { + // Find their offensive funds site lines and set the offensive budget back to where it is supposed to end up + if ((*slItr).m_Color == c_GUIColorRed) + (*slItr).m_OnlyFirstSegments = 15; + // Defensive site + else if (m_pAnimScene->GetTeamOwnership() == g_MetaMan.m_Players[mp].GetTeam() && m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) + (*slItr).m_OnlyLastSegments = 15; + } + // Hide all other lines + else { + (*slItr).m_OnlyFirstSegments = 0; + (*slItr).m_OnlyLastSegments = 0; + } + } + } + } -void MetagameGUI::UpdateOffensives() -{ - char str[256]; - - /////////////////////////////////// - // First do setup of all the activities we're going to play in order - if (g_MetaMan.m_StateChanged) - { - // Generate all offensive activites, based on the players' offensive moves - // But ONLY do this if it doesn't appear we are continuing a loaded game that has already completed battles!! - if (g_MetaMan.m_CurrentOffensive == 0) - SetupOffensives(); - - // Hide all income lines so they don't flash once on phase start - for (std::vector::iterator slItr = m_IncomeSiteLines.begin(); slItr != m_IncomeSiteLines.end(); ++slItr) - (*slItr).m_OnlyFirstSegments = (*slItr).m_OnlyLastSegments = 0; - - // Set the battle info display to the start of its animation. - UpdatePreBattleAttackers(0); - UpdatePreBattleDefenders(0); - - // Start processing the first activity - m_AnimActivityChange = true; - // Turn off the post battle review flag - m_PostBattleReview = false; -// Don't clear this! We may have loaded a game with a battle completed so we don't want to start on 0 again -// g_MetaMan.m_CurrentOffensive = 0; - } - - // No Offensives this round? then skip all this business - if (g_MetaMan.m_RoundOffensives.empty() || g_MetaMan.m_CurrentOffensive < 0 || g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size()) - { - m_ContinuePhase = true; -// TODO: Make sure that quitting out here is ok - return; - } - - // If we're still reviewing a previous battle and the player decided to skip to the next battle right away then do so and move directly to the next offensive - if (m_PostBattleReview && !m_PreTurn) - { - // We're done with this activity - m_AnimActivityChange = true; - // Turn off the post battle review flag too - m_PostBattleReview = false; - // This finishes off the current battle we're reviewing, and moves onto the next - if (!FinalizeOffensive()) - return; - } - - //////////////////////////////////////////////////// - // New Offensive, so set it up - if (m_AnimActivityChange) - { - // Show which mission we're on of all the offensive activities in queue - std::snprintf(str, sizeof(str), "Battle %d of %d", (g_MetaMan.m_CurrentOffensive + 1), (int)(g_MetaMan.m_RoundOffensives.size())); - m_pPhaseLabel->SetText(str); - - // Find the scene being attacked in this offensive Activity - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - if ((*sItr)->IsRevealed() && (*sItr)->GetPresetName() == g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetSceneName()) - m_pAnimScene = (*sItr); - } - RTEAssert(m_pAnimScene, "Couldn't find the Site that has been selected as attacked!"); - - // It's owned by a team, so set up and show its defenders - if (m_pAnimScene->GetTeamOwnership() != Activity::NoTeam) - m_AnimDefenseTeam = m_pAnimScene->GetTeamOwnership(); - - // Set up all the offensive and defensive lines for each player involved in this site's battle - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // If this player is involved in this fight, show his lines etc - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // Save the fund levels FROM THE START of each battle so we can calculate the after state if players skip the animation - g_MetaMan.m_Players[mp].m_PhaseStartFunds = g_MetaMan.m_Players[mp].m_Funds; - - // Rebuild all the action site lines for the player - float meterStart = UpdatePlayerActionLines(mp); - // Reset battle funds and other animation indicators - m_aBattleFunds[mp] = 0; - m_aBattleAttacker[mp] = false; - m_aAnimDestroyed[mp] = false; - m_aBrainIconPos[mp].Reset(); - - // What is this player attacking? - std::string targetName = g_MetaMan.m_Players[mp].GetOffensiveTargetName(); - float offensiveBudget = g_MetaMan.m_Players[mp].GetOffensiveBudget(); - // If attacking anything, see if it's the current site that's getting hit - if (!targetName.empty() && offensiveBudget > 0) - { - // Set the displayed battle funds for this player if it's attacking this site - if (m_pAnimScene->GetPresetName() == targetName) - { - m_aBattleFunds[mp] = offensiveBudget; - m_aBattleAttacker[mp] = true; - } - } - - // If this player owns and has a resident brain at this site, add his defending action line of unallocated funds, - // and also update the offensive activity itself to match teh defensive funds to what he has left after multiple completed offensives this turn - if (m_pAnimScene->GetTeamOwnership() == g_MetaMan.m_Players[mp].GetTeam() && m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // Find out how much gold this player has left after all allocations and previous battles this turn - float remainingFunds = g_MetaMan.GetRemainingFundsOfPlayer(mp); - // UPDATE the contributed funds of the defending player to whatever gold he has left after all (if any) previous battles this turn! - bool updated = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->UpdatePlayerFundsContribution(g_MetaMan.m_Players[mp].GetInGamePlayer(), remainingFunds); - // Add the unused build budget FOR THIS SCENE to the battle funds display - doesn't do anyhting now because build budgets are set to 0 in UpdateBaseBuilding - m_aBattleFunds[mp] += m_pAnimScene->GetBuildBudget(g_MetaMan.m_Players[mp].GetInGamePlayer()); - // Add the unallocated funds meter, even if there's 0 left.. it shows why there's 0, and also the meter can grow after battle if the defending player digs up gold - if (remainingFunds >= 0) - { - // Tell the player why he has no defensive money... uh too early -// if (remainingFunds <= 0) -// PlayerTextIndication(mp, "No unallocated funds!", Vector(m_apPlayerBox[mp]->GetXPos(), m_apPlayerBox[mp]->GetYPos()), 1500); - - m_ActionSiteLines[mp].push_back(SiteLine(mp, meterStart, remainingFunds / g_MetaMan.m_Players[mp].m_Funds, m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), m_pAnimScene->GetPresetName(), m_pAnimScene, c_GUIColorYellow, -1, -1, 60, 1.0f, g_MetaMan.IsActiveTeam(m_pAnimScene->GetTeamOwnership()))); - // This will actually affect the line meter - m_ActionSiteLines[mp].back().m_FundsAmount = remainingFunds; - // Add to the battle funds display too - m_aBattleFunds[mp] += remainingFunds; - // Move the meter start position forward as necessary - meterStart += m_ActionSiteLines[mp].back().m_MeterAmount; - } - } - - // Hide all action lines except for their bars - for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) - { - (*slItr).m_OnlyFirstSegments = 1; - (*slItr).m_OnlyLastSegments = -1; - } - - // Update the ratios now that we've messed with them - UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); - } - // If player not involved in the fight then hide all lines of this player - { - for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) - { - (*slItr).m_OnlyFirstSegments = 0; - (*slItr).m_OnlyLastSegments = -1; - } - } - } - - // Start out the brain travel animaitons hidden and at 0 - ResetBattleInfo(); - UpdatePreBattleAttackers(0); - UpdatePreBattleDefenders(0); - // Make the meters draw all the time -// This might not be such a great thing after all to show all the bars.. not needed anymore since we're not connecting to defensive budgets in offensive phases anymore -// m_ActionMeterDrawOverride = true; - - // Show that we are waiting for the players to press the Continue button so we can start the Activity we're setting up here - m_PreTurn = true; - m_BattleToResume = false; - // New battle, reset the post battle flags - m_PostBattleReview = false; - m_BattleCausedOwnershipChange = false; - m_PreBattleTeamOwnership = Activity::NoTeam; - - ChangeAnimMode(TARGETZEROING); - m_AnimActivityChange = false; - } - - ///////////////////////////////////////////// - // Make a menacing target crosshairs zero in on the site we're playing this activity on - if (m_AnimMode == TARGETZEROING) - { - if (NewAnimMode()) - { - m_SiteAttackTarget.m_CenterPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); - m_SiteAttackTarget.m_AnimProgress = 0; - m_SiteAttackTarget.m_Color = c_GUIColorRed; - m_AnimModeDuration = 600; - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - } - m_SiteAttackTarget.m_AnimProgress = EaseOut(0.0, 1.0, m_AnimTimer1.GetElapsedRealTimeMS() / m_AnimModeDuration); - - // BAM - show the name of the site and start showing who's attacking it - if (m_SiteAttackTarget.m_AnimProgress == 1.0) - { -// // After a while, go on to show the attacker action site lines -// if (m_AnimTimer1.IsPastRealMS(1200)) -// { - ChangeAnimMode(LINECONNECTFW); - } - } - - ///////////////////////////////////////////// - // Show who's attacking this target; connect the attack budget lines (while still zeroed) and animate the brains going there from their respective pools - if (m_AnimMode == LINECONNECTFW) - { - if (NewAnimMode()) - { - // Make sure all offensive action lines are set up for this phase - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Find all players that are active during this battle - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // If this player is attacking, indicate that we've got a brain in transit.. this just changes the display, not the actual brain pool count yet - if (m_pAnimScene->GetPresetName() == g_MetaMan.m_Players[mp].GetOffensiveTargetName()) - g_MetaMan.m_Players[mp].ChangeBrainsInTransit(1); - - // Find their offensive funds site lines - for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) - { - // Offensive lines are red; set them up to have their meters visible - if ((*slItr).m_Color == c_GUIColorRed) - { - (*slItr).m_OnlyFirstSegments = 1; - (*slItr).m_OnlyLastSegments = -1; - } - } - } - } - - m_AnimSegment = 0; - m_AnimModeDuration = 2000; - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - } - - // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site - UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); - - // Keep revealing segments simultaneously from all attackers until they are all revealed - if (m_AnimSegment < 15) - { - if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) - { - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only care if this player is attacking this site - if (m_pAnimScene->GetPresetName() == g_MetaMan.m_Players[mp].GetOffensiveTargetName() && g_MetaMan.m_Players[mp].GetOffensiveBudget() > 0) -// if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // Find their offensive funds site lines - for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) - { - // Offensive lines are red; grow all of em - if ((*slItr).m_Color == c_GUIColorRed) - (*slItr).m_OnlyFirstSegments++; - } - } - } - - m_AnimSegment++; - m_AnimTimer1.Reset(); - } - } - - // Animate the crosshairs subtly - m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); - - // Animate the brain label travel animations - UpdatePreBattleAttackers(EaseInOut(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); - UpdatePreBattleDefenders(0); - - // Just wait until the players hit the continue button.. - if (m_BattleToResume) - m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Resume <" : "Resume"); - else - m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Start! <" : "Start!"); - - // Move onto growing the attack budget meters - if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) - ChangeAnimMode(SHOWDEFENDERS); - } - - ///////////////////////////////////////////// - // Show the defending brains for this battle - if (m_AnimMode == SHOWDEFENDERS) - { - if (NewAnimMode()) - { - // Make sure all defensive and unallocated budget action lines are set up for this phase - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - - } - - m_AnimModeDuration = 500; - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - } - - // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site - UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); - - // Animate the crosshairs subtly - m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); - - // Keep the brain label travel animations in one spot and their labels updated - UpdatePreBattleAttackers(1.0); - UpdatePreBattleDefenders(EaseOut(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); - - // Just wait until the players hit the continue button.. - if (m_BattleToResume) - m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Resume <" : "Resume"); - else - m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Start! <" : "Start!"); - - // Move onto growing the attack budget meters - if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) - ChangeAnimMode(LINECONNECTBW); - } - - ///////////////////////////////////////////// - // Show the defending budget lines of the players who are defending this site - if (m_AnimMode == LINECONNECTBW) - { - if (NewAnimMode()) - { - // Make sure all defensive and unallocated budget action lines are set up for this phase - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only care of this player is involved in this particular battle - // Only care about defending players of this site - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())&& - m_pAnimScene->GetTeamOwnership() == g_MetaMan.m_Players[mp].GetTeam() && - m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // Find their defensive funds site lines - for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) - { - // Offensive lines are green; unallocated funds are white - attach all of them - if ((*slItr).m_Color != c_GUIColorRed) - { - (*slItr).m_OnlyFirstSegments = -1; - (*slItr).m_OnlyLastSegments = 0; - } - } - } - } - - m_AnimSegment = 0; - m_AnimModeDuration = 1000; - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - } - - // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site - UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); - - // Keep revealing segments simultaneously from all attackers until they are all revealed - if (m_AnimSegment < 15) - { - if (m_AnimTimer1.GetElapsedRealTimeMS() > 150) - { - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only care of this player is involved in this particular battle - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // Find their defensive-related funds site lines - for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) - { - // Offensive lines are red; grow the defensive ones relevant to this scene - if ((*slItr).m_Color != c_GUIColorRed && m_pAnimScene->GetPresetName() == (*slItr).m_SiteName && - (m_pAnimScene->GetTeamOwnership() == g_MetaMan.m_Players[mp].GetTeam() && m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer()))) - (*slItr).m_OnlyLastSegments++; - } - } - } - m_AnimSegment++; - m_AnimTimer1.Reset(); - } - } - - // Animate the crosshairs subtly - m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); - - // Keep the brain label travel animations in one spot and their labels updated - UpdatePreBattleAttackers(1.0); - UpdatePreBattleDefenders(1.0); - - // Just wait until the players hit the continue button.. - if (m_BattleToResume) - m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Resume <" : "Resume"); - else - m_apMetaButton[CONTINUE]->SetText(m_AnimTimer2.AlternateReal(333) ? "> Start! <" : "Start!"); - } - - ////////////////////////////////// - // POST BATTLE REVIEW - - ///////////////////////////////////////////// - // A Short pause for after players come out of a run simulation battle, - // so they can orient themselves visually before battle review starts happening - if (m_AnimMode == SHOWPOSTBATTLEPAUSE) - { - if (NewAnimMode()) - { - // The length of the pause - m_AnimModeDuration = 2000; - - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - } - - // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site - UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); - // Animate the crosshairs subtly - m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); - - UpdatePostBattleRetreaters(0); - UpdatePostBattleResidents(0); - - // Give the players the option to skip the post battle review and go to next battle, or phase if there aren't any more battles - m_apMetaButton[CONTINUE]->SetText("Next"); - - // Move onto having the victorious brains go back inside the site and take it over - if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) - ChangeAnimMode(SHOWPOSTBATTLEBALANCE); - } - - ///////////////////////////////////////////// - // Show how much is being allocated to attacking this place - if (m_AnimMode == SHOWPOSTBATTLEBALANCE) - { - if (NewAnimMode()) - { - m_AnimModeDuration = 2000; - - // Make sure all offensive action-related lines are set up for this battle review animation - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only the players of this battle - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // Find their site lines that are connected to the site - for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) - { - // Only lines that are connected are relevant - if ((*slItr).m_OnlyFirstSegments > 1 || (*slItr).m_OnlyLastSegments > 1) - { - // Re-set the funds amount we're going from - actually, not necessary - (*slItr).m_FundsAmount = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsContribution(g_MetaMan.m_Players[mp].GetInGamePlayer()); - // Set the target we're going toward - (*slItr).m_FundsTarget = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsShare(g_MetaMan.m_Players[mp].GetInGamePlayer()); - // Start the battle money display off at the right point too - m_aBattleFunds[mp] = (*slItr).m_FundsAmount; - // Display the change amount over/under the player bar - FundsChangeIndication(mp, (*slItr).m_FundsTarget - (*slItr).m_FundsAmount, Vector(m_apPlayerBarLabel[mp]->GetXPos() + m_apPlayerBarLabel[mp]->GetWidth(), m_apPlayerBarLabel[mp]->GetYPos()), m_AnimModeDuration); - - // Update the ratios.. shouldn't do anyhitng, really - UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); - } - } - } - } - - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - } - - // Find the players who are involved in this battle - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only the players of this battle - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // The brains who DID NOT MAKE IT - Show them blowing up at some random interval into the animation - if (!m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer()) && - !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // If not yet blown up, then see if we should yet - if (!m_aAnimDestroyed[mp] && m_AnimTimer2.GetElapsedRealTimeMS() > (m_AnimModeDuration * 0.5F) && RandomNum() < 0.05F) - { - // Add circle explosion effect to where the brain icon used to be - m_SiteSwitchIndicators.push_back(SiteTarget(m_aBrainIconPos[mp], 0, SiteTarget::CIRCLEGROW, c_GUIColorRed)); - // Switch the brain icon drawing off - m_aAnimDestroyed[mp] = true; - } - } - - // Find their site lines that are connected to the site - for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) - { - // Only lines that are connected are relevant - if ((*slItr).m_OnlyFirstSegments > 1 || (*slItr).m_OnlyLastSegments > 1) - { - float startFunds = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsContribution(g_MetaMan.m_Players[mp].GetInGamePlayer()); - - // Grow/shrink all the meters, showing how the funds changed during battle - if (m_AnimTimer1.GetElapsedRealTimeMS() < m_AnimModeDuration) - { - // Make this animation correlate in duration with the funds change label that rises - (*slItr).m_FundsAmount = EaseOut(startFunds, (*slItr).m_FundsTarget, m_AnimTimer1.GetElapsedRealTimeMS() / m_AnimModeDuration); - // Update the battle funds display; it will be shown in the brian action label - m_aBattleFunds[mp] = (*slItr).m_FundsAmount; - // Adjust the funds to show how much we subtracted this frame - this will be reset in the end anyway, it's just for show during this battle review animation - g_MetaMan.m_Players[mp].m_Funds = g_MetaMan.m_Players[mp].m_PhaseStartFunds - (startFunds - (*slItr).m_FundsAmount); - UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); - } - // Finished growing the meter to the target size - else - { - m_aBattleFunds[mp] = (*slItr).m_FundsAmount = (*slItr).m_FundsTarget; - g_MetaMan.m_Players[mp].m_Funds = g_MetaMan.m_Players[mp].m_PhaseStartFunds - (startFunds - (*slItr).m_FundsAmount); - UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); - } - } - } - } - } - - // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site - UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); - // Animate the crosshairs subtly - m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); - - // Animate the brain label travel animations - UpdatePostBattleRetreaters(0); - UpdatePostBattleResidents(0); - - UpdatePlayerBars(); - // Give the players the option to skip the post battle review and go to next battle, or phase if there aren't any more battles - m_apMetaButton[CONTINUE]->SetText("Next"); - - // Move onto showing what happened to each brain - if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) - { - // Blow up any remaining brains who are doomed - // Find the players who are involved in this battle - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only the players of this battle who didn't evacuate - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer()) && - !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // The brains who DID NOT MAKE IT - Show them blowing up - if (!m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // If not yet blown up, then we should be - if (!m_aAnimDestroyed[mp]) - { - // Add circle explosion effect to where the brain icon used to be - m_SiteSwitchIndicators.push_back(SiteTarget(m_aBrainIconPos[mp], 0, SiteTarget::CIRCLEGROW, c_GUIColorRed)); - // Switch the brain icon drawing off - m_aAnimDestroyed[mp] = true; - } - } - } - } - ChangeAnimMode(SHOWPOSTBATTLEBRAINS); - } - } - - ///////////////////////////////////////////// - // Show what happened to the brains after a battle - if (m_AnimMode == SHOWPOSTBATTLEBRAINS) - { - if (NewAnimMode()) - { - // Make sure all defensive and unallocated budget action lines are set up for this phase - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - - } - - // The duration of this depends on whethere there are any evacuees to travel back - m_AnimModeDuration = g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->AnyBrainWasEvacuated() ? 2000 : 500; - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - } - - // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site - UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); - // Animate the crosshairs subtly - m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); - - // The retreating brain label travel animations get updated to go back to their pools - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->AnyBrainWasEvacuated()) - UpdatePostBattleRetreaters(EaseInOut(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); - else - UpdatePostBattleRetreaters(1.0); - UpdatePostBattleResidents(0); - - // Give the players the option to skip the post battle review and go to next battle, or phase if there aren't any more battles - m_apMetaButton[CONTINUE]->SetText("Next"); - - // Move onto having the victorious brains go back inside the site and take it over - if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) - { - // Change the display to show the evacuees transferring back to their brain pools - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->AnyBrainWasEvacuated()) - { - // Find the players who are evacuated anything this battle - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only the players of this battle who evac'd their brain - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // Both an Attacker aborting an attack, and a Defender abandoning his site has the same effect here on the brian pool display - g_MetaMan.m_Players[mp].ChangeBrainsInTransit(-1); - } - } - } - - ChangeAnimMode(SHOWNEWRESIDENTS); - } - } - - ///////////////////////////////////////////// - // Show the victorious brains going back into the site and taking it over - if (m_AnimMode == SHOWNEWRESIDENTS) - { - if (NewAnimMode()) - { - // Make sure all defensive and unallocated budget action lines are set up for this phase - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - - } - - m_AnimModeDuration = m_BattleCausedOwnershipChange ? 500 : 750; - m_AnimTimer1.Reset(); - m_AnimTimer2.Reset(); - } - - // Keep the name on the attack site, keeping the name high so the contestants' info can fit in the quadrants around the site - UpdateSiteNameLabel(true, m_pAnimScene->GetPresetName(), m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 1.8); - // Animate the crosshairs subtly - m_SiteAttackTarget.m_AnimProgress = 0.975 + 0.025 * cos(c_TwoPI * (float)((int)m_AnimTimer2.GetElapsedRealTimeMS() % 666) / 666.0f); - - // The brains going back into their lair are animated - UpdatePostBattleRetreaters(1.0); - // Tweak the animaiton slightly based on whether the site switched hands or not - if (m_BattleCausedOwnershipChange) - UpdatePostBattleResidents(EaseIn(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); - else - UpdatePostBattleResidents(EaseOut(0, 1.0, MIN(1.0, m_AnimTimer2.GetElapsedRealTimeMS() / m_AnimModeDuration))); - - // Find the players who are involved in this battle - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only the players of this battle - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // The guys who died - remove their icons completely - if (!m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - m_apPlayerBrainTravelLabel[mp]->SetVisible(false); - } - } - - // Give the players the option to skip the post battle review and go to next battle, or phase if there aren't any more battles - m_apMetaButton[CONTINUE]->SetText("Next"); - - // Move onto the next offensive in line, if any! - if (m_AnimTimer2.GetElapsedRealTimeMS() > m_AnimModeDuration) - { - // Quit if there's no more offensives after the one we just finalized - if (!FinalizeOffensive()) - return; - } - } - - - ///////////////////////////////////////////// - // If the player have chosen to start the attack activity by hitting the continue button, then do eeeet - if (!m_PreTurn) - { - // Make sure this is set back - m_apMetaButton[CONTINUE]->SetText("Start!"); - UpdateSiteNameLabel(false); - - // Max out the offensive lines in case the player started the battle before the lines were fully animated as connected - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only care of this player is involved in this particular battle - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - for (std::vector::iterator slItr = m_ActionSiteLines[mp].begin(); slItr != m_ActionSiteLines[mp].end(); ++slItr) - { - // Only show lines that are going to the site we're battling on - if ((*slItr).m_SiteName == m_pAnimScene->GetPresetName()) - { - // Find their offensive funds site lines and set the offensive budget back to where it is supposed to end up - if ((*slItr).m_Color == c_GUIColorRed) - (*slItr).m_OnlyFirstSegments = 15; - // Defensive site - else if (m_pAnimScene->GetTeamOwnership() == g_MetaMan.m_Players[mp].GetTeam() && m_pAnimScene->GetResidentBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - (*slItr).m_OnlyLastSegments = 15; - } - // Hide all other lines - else - { - (*slItr).m_OnlyFirstSegments = 0; - (*slItr).m_OnlyLastSegments = 0; - } - } - } - } - - // Resuming ongoing battle - if (m_BattleToResume) - { - m_BattleToResume = false; - m_ActivityResumed = true; - g_GUISound.ExitMenuSound()->Play(); - } - // Starting the next battle - else - { - // Clear the battle info of the last one -// No need here; it is done in FinalizeOffensive, and it actually screw up the positions of the losing brain labels, which should stay put until after rewview is over -// ResetBattleInfo(); - - if (g_MetaMan.m_CurrentOffensive < g_MetaMan.m_RoundOffensives.size()) - { - // Temporarly save the previous ownership state of the scene, so it can be held and displayed until the dramatic change is shown in battle review - m_PreBattleTeamOwnership = m_pAnimScene->GetTeamOwnership(); - - // No human players in this one, or only one team going here? Then skip the whole sim and just show the result of the site - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetHumanCount() < 1 || g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetTeamCount() == 1) - { - // AUTOMATIC BATTLE RESOLUTION - // If the automatic resolution caused a site change, show it clearly with animated crosshairs - m_BattleCausedOwnershipChange = AutoResolveOffensive(g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive], const_cast(m_pAnimScene)); - - // Move onto showing what happened in the battle before we move on to next battle - m_PreTurn = true; - m_PostBattleReview = true; - ChangeAnimMode(SHOWPOSTBATTLEBALANCE); - } - // START SIM with activity that has at least one human player involved and more than one team total - else - { - m_pPlayingScene = const_cast(m_pAnimScene); - g_SceneMan.SetSceneToLoad(m_pPlayingScene); - g_ActivityMan.SetStartActivity(dynamic_cast(g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->Clone())); - m_ActivityRestarted = true; - } - } - // Whoops, we have no further activities left to start, so just continue to next round phase (shouldn't happen; this is handled in CompletedActivity) - else - { - // Turn off drawing the aciton meters draw all the time - m_ActionMeterDrawOverride = false; - m_ContinuePhase = true; - } - } - } + // Resuming ongoing battle + if (m_BattleToResume) { + m_BattleToResume = false; + m_ActivityResumed = true; + g_GUISound.ExitMenuSound()->Play(); + } + // Starting the next battle + else { + // Clear the battle info of the last one + // No need here; it is done in FinalizeOffensive, and it actually screw up the positions of the losing brain labels, which should stay put until after rewview is over + // ResetBattleInfo(); + + if (g_MetaMan.m_CurrentOffensive < g_MetaMan.m_RoundOffensives.size()) { + // Temporarly save the previous ownership state of the scene, so it can be held and displayed until the dramatic change is shown in battle review + m_PreBattleTeamOwnership = m_pAnimScene->GetTeamOwnership(); + + // No human players in this one, or only one team going here? Then skip the whole sim and just show the result of the site + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetHumanCount() < 1 || g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetTeamCount() == 1) { + // AUTOMATIC BATTLE RESOLUTION + // If the automatic resolution caused a site change, show it clearly with animated crosshairs + m_BattleCausedOwnershipChange = AutoResolveOffensive(g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive], const_cast(m_pAnimScene)); + + // Move onto showing what happened in the battle before we move on to next battle + m_PreTurn = true; + m_PostBattleReview = true; + ChangeAnimMode(SHOWPOSTBATTLEBALANCE); + } + // START SIM with activity that has at least one human player involved and more than one team total + else { + m_pPlayingScene = const_cast(m_pAnimScene); + g_SceneMan.SetSceneToLoad(m_pPlayingScene); + g_ActivityMan.SetStartActivity(dynamic_cast(g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->Clone())); + m_ActivityRestarted = true; + } + } + // Whoops, we have no further activities left to start, so just continue to next round phase (shouldn't happen; this is handled in CompletedActivity) + else { + // Turn off drawing the aciton meters draw all the time + m_ActionMeterDrawOverride = false; + m_ContinuePhase = true; + } + } + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: FinalizeOffensive ////////////////////////////////////////////////////////////////////////////////////////// // Description: Finishes one battle in the UpdateOffensives and moves onto the next. -bool MetagameGUI::FinalizeOffensive() -{ - // No Offensives this round? then skip all this business - if (g_MetaMan.m_RoundOffensives.empty() || g_MetaMan.m_CurrentOffensive < 0 || g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || !m_pAnimScene) - { - m_ContinuePhase = true; - return false; - } - - // Deduct the original funds contribution of each player - less any unused funds of the team, taking original player contribution ratios into account - for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) - { - // Only the players who were battling this offensive - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) - { - // Re-set the funds level to where it was at the start of this offensive (NOT at the start of the phase, actually) - g_MetaMan.m_Players[mp].m_Funds = g_MetaMan.m_Players[mp].m_PhaseStartFunds; - // Deduct the original contribution to team funds - g_MetaMan.m_Players[mp].m_Funds -= g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsContribution(g_MetaMan.m_Players[mp].GetInGamePlayer()); - // Add back whatever his share of whatever his team has left in the fight at its end - g_MetaMan.m_Players[mp].m_Funds += g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsShare(g_MetaMan.m_Players[mp].GetInGamePlayer()); - // IF This guy was ATTACKING this turn, adjust his attack budget to match what just happened in the battle - // so in case he is defending in a upcoming battle this turn, his avaialbe funds will be accurately calculated - if (g_MetaMan.m_Players[mp].GetOffensiveTargetName() == m_pAnimScene->GetPresetName()) - { - g_MetaMan.m_Players[mp].SetOffensiveBudget(g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsShare(g_MetaMan.m_Players[mp].GetInGamePlayer())); -// g_MetaMan.m_Players[mp].SetOffensiveTargetName(""); - - // The only condition where this attacking player does NOT get a brain deducted from pool, is when he aborts the attack with an evacuation and the brain goes back to pool! - if (!g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) - g_MetaMan.m_Players[mp].ChangeBrainPoolCount(-1); - } - // Defending player - else - { - // If this player evacuated his brain, WHILE DEFENDING, then add it back to his pool - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) - g_MetaMan.m_Players[mp].ChangeBrainPoolCount(1); - } - // Update the ratios of the meter now that the funds have changed - UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); - // Clear out the brain transit display either way; nothing should be out flying now anyway - g_MetaMan.m_Players[mp].SetBrainsInTransit(0); - } - } - - // If the battle caused ownership change, then show it with a cool indication - if (m_BattleCausedOwnershipChange) - m_SiteSwitchIndicators.push_back(SiteTarget(m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 0, m_pAnimScene->GetTeamOwnership() != Activity::NoTeam ? SiteTarget::SQUAREGROW : SiteTarget::CIRCLEGROW, c_GUIColorRed, m_AnimTimer2.GetElapsedRealTimeMS())); - - // Clear the battle info of the last one - ResetBattleInfo(); - // Clear the site name label - UpdateSiteNameLabel(false); - // Done reviewing the battle - m_PostBattleReview = false; - // We're done with this activity - m_AnimActivityChange = true; - - // Update the count to point to the next (now current) offensive - g_MetaMan.m_CurrentOffensive++; - - // AUTO-SAVE THE GAME AFTER EACH PLAYED BATTLE - SaveGame(AUTOSAVENAME, METASAVEPATH + std::string(AUTOSAVENAME) + ".ini", false); - - // If we're out of offensives, then move onto the next phase - if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size()) - { - m_ContinuePhase = true; - // Quit out of here, there's no more battles to fight! - return false; - } - // Indicate that there's at least one more battle lined up now - return true; -} +bool MetagameGUI::FinalizeOffensive() { + // No Offensives this round? then skip all this business + if (g_MetaMan.m_RoundOffensives.empty() || g_MetaMan.m_CurrentOffensive < 0 || g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || !m_pAnimScene) { + m_ContinuePhase = true; + return false; + } + + // Deduct the original funds contribution of each player - less any unused funds of the team, taking original player contribution ratios into account + for (int mp = Players::PlayerOne; mp < g_MetaMan.m_Players.size(); ++mp) { + // Only the players who were battling this offensive + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain(g_MetaMan.m_Players[mp].GetInGamePlayer())) { + // Re-set the funds level to where it was at the start of this offensive (NOT at the start of the phase, actually) + g_MetaMan.m_Players[mp].m_Funds = g_MetaMan.m_Players[mp].m_PhaseStartFunds; + // Deduct the original contribution to team funds + g_MetaMan.m_Players[mp].m_Funds -= g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsContribution(g_MetaMan.m_Players[mp].GetInGamePlayer()); + // Add back whatever his share of whatever his team has left in the fight at its end + g_MetaMan.m_Players[mp].m_Funds += g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsShare(g_MetaMan.m_Players[mp].GetInGamePlayer()); + // IF This guy was ATTACKING this turn, adjust his attack budget to match what just happened in the battle + // so in case he is defending in a upcoming battle this turn, his avaialbe funds will be accurately calculated + if (g_MetaMan.m_Players[mp].GetOffensiveTargetName() == m_pAnimScene->GetPresetName()) { + g_MetaMan.m_Players[mp].SetOffensiveBudget(g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->GetPlayerFundsShare(g_MetaMan.m_Players[mp].GetInGamePlayer())); + // g_MetaMan.m_Players[mp].SetOffensiveTargetName(""); + + // The only condition where this attacking player does NOT get a brain deducted from pool, is when he aborts the attack with an evacuation and the brain goes back to pool! + if (!g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) + g_MetaMan.m_Players[mp].ChangeBrainPoolCount(-1); + } + // Defending player + else { + // If this player evacuated his brain, WHILE DEFENDING, then add it back to his pool + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated(g_MetaMan.m_Players[mp].GetInGamePlayer())) + g_MetaMan.m_Players[mp].ChangeBrainPoolCount(1); + } + // Update the ratios of the meter now that the funds have changed + UpdatePlayerLineRatios(m_ActionSiteLines[mp], mp, false, g_MetaMan.m_Players[mp].m_Funds); + // Clear out the brain transit display either way; nothing should be out flying now anyway + g_MetaMan.m_Players[mp].SetBrainsInTransit(0); + } + } + // If the battle caused ownership change, then show it with a cool indication + if (m_BattleCausedOwnershipChange) + m_SiteSwitchIndicators.push_back(SiteTarget(m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(), 0, m_pAnimScene->GetTeamOwnership() != Activity::NoTeam ? SiteTarget::SQUAREGROW : SiteTarget::CIRCLEGROW, c_GUIColorRed, m_AnimTimer2.GetElapsedRealTimeMS())); + + // Clear the battle info of the last one + ResetBattleInfo(); + // Clear the site name label + UpdateSiteNameLabel(false); + // Done reviewing the battle + m_PostBattleReview = false; + // We're done with this activity + m_AnimActivityChange = true; + + // Update the count to point to the next (now current) offensive + g_MetaMan.m_CurrentOffensive++; + + // AUTO-SAVE THE GAME AFTER EACH PLAYED BATTLE + SaveGame(AUTOSAVENAME, METASAVEPATH + std::string(AUTOSAVENAME) + ".ini", false); + + // If we're out of offensives, then move onto the next phase + if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size()) { + m_ContinuePhase = true; + // Quit out of here, there's no more battles to fight! + return false; + } + // Indicate that there's at least one more battle lined up now + return true; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: ResetBattleInfo ////////////////////////////////////////////////////////////////////////////////////////// // Description: Hides and resets all battle info labels and panels -void MetagameGUI::ResetBattleInfo() -{ - int mp = 0; - for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) - { - // Init and hide everything initially - if (!m_apPlayerTeamActionBox[mp]->GetDrawImage()) - { - // Set the flag icons on the floating player bars - m_apPlayerTeamActionBox[mp]->SetDrawType(GUICollectionBox::Image); - m_apPlayerTeamActionBox[mp]->SetDrawImage(new AllegroBitmap(g_MetaMan.m_TeamIcons[(*mpItr).GetTeam()].GetBitmaps32()[0])); - } - // Hide everything initially - m_apPlayerTeamActionBox[mp]->SetVisible(false); - m_apPlayerTeamActionBox[mp]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); - m_apPlayerBrainTravelLabel[mp]->SetVisible(false); - m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); - - ++mp; - } - - // Done reviewing the battle too - m_PostBattleReview = false; -} +void MetagameGUI::ResetBattleInfo() { + int mp = 0; + for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) { + // Init and hide everything initially + if (!m_apPlayerTeamActionBox[mp]->GetDrawImage()) { + // Set the flag icons on the floating player bars + m_apPlayerTeamActionBox[mp]->SetDrawType(GUICollectionBox::Image); + m_apPlayerTeamActionBox[mp]->SetDrawImage(new AllegroBitmap(g_MetaMan.m_TeamIcons[(*mpItr).GetTeam()].GetBitmaps32()[0])); + } + // Hide everything initially + m_apPlayerTeamActionBox[mp]->SetVisible(false); + m_apPlayerTeamActionBox[mp]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); + m_apPlayerBrainTravelLabel[mp]->SetVisible(false); + m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); + ++mp; + } + + // Done reviewing the battle too + m_PostBattleReview = false; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateBattleQuads @@ -5187,196 +4669,179 @@ void MetagameGUI::ResetBattleInfo() // Description: Updates which player get placed in which quad around a fought-over // site. -void MetagameGUI::UpdateBattleQuads(Vector targetPos) -{ - // Start with a clean slate - for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) - m_aQuadTakenBy[q] = Players::NoPlayer; - - // Go through all players, assigning the quads depending on how the player bars are positioned in relation to each other - int mp = 0; - for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) - { - // Select which quadrant makes most sense for this player, based on his floating bar's position relative to the site - int initialQuad = 0; - if (m_apPlayerBox[mp]->GetXPos() < targetPos.m_X) - initialQuad = m_apPlayerBox[mp]->GetYPos() < targetPos.m_Y ? 0 : 1; - else - initialQuad = m_apPlayerBox[mp]->GetYPos() < targetPos.m_Y ? 3 : 2; - - // If the initial selection is taken, just find the next available one - int quadIndex = initialQuad; - while (m_aQuadTakenBy[quadIndex] != Players::NoPlayer) - { - quadIndex++; - // Loop around - if (quadIndex >= 4) - quadIndex = 0; - // If we've looped completely around, somehting's wrong! There should always be at least one quad available - if (quadIndex == initialQuad) - break; - } - // Mark this spot as taken by this player - m_aQuadTakenBy[quadIndex] = mp; - // Advance the metaplayer index to sync with the iterator - mp++; - } +void MetagameGUI::UpdateBattleQuads(Vector targetPos) { + // Start with a clean slate + for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) + m_aQuadTakenBy[q] = Players::NoPlayer; + + // Go through all players, assigning the quads depending on how the player bars are positioned in relation to each other + int mp = 0; + for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) { + // Select which quadrant makes most sense for this player, based on his floating bar's position relative to the site + int initialQuad = 0; + if (m_apPlayerBox[mp]->GetXPos() < targetPos.m_X) + initialQuad = m_apPlayerBox[mp]->GetYPos() < targetPos.m_Y ? 0 : 1; + else + initialQuad = m_apPlayerBox[mp]->GetYPos() < targetPos.m_Y ? 3 : 2; + + // If the initial selection is taken, just find the next available one + int quadIndex = initialQuad; + while (m_aQuadTakenBy[quadIndex] != Players::NoPlayer) { + quadIndex++; + // Loop around + if (quadIndex >= 4) + quadIndex = 0; + // If we've looped completely around, somehting's wrong! There should always be at least one quad available + if (quadIndex == initialQuad) + break; + } + // Mark this spot as taken by this player + m_aQuadTakenBy[quadIndex] = mp; + // Advance the metaplayer index to sync with the iterator + mp++; + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePreBattleAttackers ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the animation and display of the info for the current // offensive battle being next in line for this round. -void MetagameGUI::UpdatePreBattleAttackers(float progress) -{ - // Sanity check that we have any offensive battle activity and scene to display around - if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || - !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive] || - !m_pAnimScene) - return; - - int mp = 0; - char str[256]; - int quadIndex = Players::NoPlayer; - // The screen coordinate position of the current battle site - Vector siteScreenPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); - // Where the brain label starts from - Vector brainLabelStartPos; - // Where it ends up, aligned properly next to the site, in the proper quadrant - Vector brainLabelSitePos; - // The distance the brain label should travel between the two - Vector brainLabelTravel; - // The actual total distance it will travel, when going along each cardinal axis at a time - float brainTravelLength; - // The animation progress point at which travel switches from one axis to the other - float travelSwitchRatio; - // The actual current position of the brain label at the current specified animaiton progress point - Vector brainTravelPos; - - // Update the quad assignments for all players - UpdateBattleQuads(siteScreenPos); - - // Go through all attacking players for this activity - for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) - { - // Player active and ATTACKING in current battle, so display his team flag and place it according to the animation progress - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive((*mpItr).GetInGamePlayer()) && - !m_pAnimScene->GetResidentBrain((*mpItr).GetInGamePlayer())) - { - // Show the active players' team flag icons around the site if the brains have arrived - m_apPlayerTeamActionBox[mp]->SetVisible(progress >= 1.0); - // Show the traveling brain if we're not at 0 - m_apPlayerBrainTravelLabel[mp]->SetVisible(progress > 0); - - // Figure out which quad this player is assigned to - for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) - { - if (m_aQuadTakenBy[q] == mp) - { - quadIndex = q; - break; - } - } - - // Write the brain label, with info if applicable for the current progress of animation - if (progress < 1.0) - std::snprintf(str, sizeof(str), "%c", -48); - // When at site destination, take into account the side the brain icon needs to be on - else - { - if (quadIndex <= 1) - std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -48); - else - std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); - } - m_apPlayerBrainTravelLabel[mp]->SetText(str); - m_apPlayerBrainTravelLabel[mp]->SetToolTip("The specific brain that is being sent in to attack this place, and the funds he has been budgeted to do so with."); - - // Figure out start and ending positions for the brain label's travels and the team flag badge - // Upper left quadrant - if (quadIndex == 0) - { - // Team flag position - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label start position, taking into account its text alignment - brainLabelStartPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() - (m_apPlayerBrainTravelLabel[mp]->GetWidth() / 2) - 30, m_apBrainPoolLabel[mp]->GetYPos() - 5); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - } - // Lower left quadrant - else if (quadIndex == 1) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label start position, taking into account its text alignment - brainLabelStartPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() - (m_apPlayerBrainTravelLabel[mp]->GetWidth() / 2) - 30, m_apBrainPoolLabel[mp]->GetYPos() - 5); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); - } - // Lower right quadrant - else if (quadIndex == 2) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label start position, taking into account its text alignment - brainLabelStartPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() + 8, m_apBrainPoolLabel[mp]->GetYPos() - 5); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); - } - // Upper right quadrant - else if (quadIndex == 3) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label start position, taking into account its text alignment - brainLabelStartPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() + 8, m_apBrainPoolLabel[mp]->GetYPos() - 5); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - } - - // Now place the travelling brain label at the appropriate spot based on the animation progress - brainLabelTravel = brainLabelSitePos - brainLabelStartPos; - // The raw distance the label needs to travel when going down one axis and then the other - brainTravelLength = fabs(brainLabelTravel.m_X) + fabs(brainLabelTravel.m_Y); - // Figure out where along the progression that brain travel switches from one axis to the other - travelSwitchRatio = fabs(brainLabelTravel.m_Y) / brainTravelLength; - - // If animation progress is less than where the switch happens, handle that - if (progress <= travelSwitchRatio) - { - // No progress in X - brainTravelPos.m_X = brainLabelStartPos.m_X; - // Some progress in Y - brainTravelPos.m_Y = brainLabelStartPos.m_Y + (brainLabelTravel.m_Y * (progress / travelSwitchRatio)); - } - // Progress is past the switch - else if (progress < 1.0) - { - // Some progress in X - brainTravelPos.m_X = brainLabelStartPos.m_X + (brainLabelTravel.m_X * ((progress - travelSwitchRatio) / (1.0 - travelSwitchRatio))); - // FULL progress in Y - brainTravelPos.m_Y = brainLabelSitePos.m_Y; - } - // Reached destination - else - brainTravelPos = brainLabelSitePos; - - // Now actually move the label - m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainTravelPos.m_X, brainTravelPos.m_Y); - } - // Advance the metaplayer index to sync with the iterator - mp++; - } -} +void MetagameGUI::UpdatePreBattleAttackers(float progress) { + // Sanity check that we have any offensive battle activity and scene to display around + if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || + !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive] || + !m_pAnimScene) + return; + + int mp = 0; + char str[256]; + int quadIndex = Players::NoPlayer; + // The screen coordinate position of the current battle site + Vector siteScreenPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); + // Where the brain label starts from + Vector brainLabelStartPos; + // Where it ends up, aligned properly next to the site, in the proper quadrant + Vector brainLabelSitePos; + // The distance the brain label should travel between the two + Vector brainLabelTravel; + // The actual total distance it will travel, when going along each cardinal axis at a time + float brainTravelLength; + // The animation progress point at which travel switches from one axis to the other + float travelSwitchRatio; + // The actual current position of the brain label at the current specified animaiton progress point + Vector brainTravelPos; + + // Update the quad assignments for all players + UpdateBattleQuads(siteScreenPos); + + // Go through all attacking players for this activity + for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) { + // Player active and ATTACKING in current battle, so display his team flag and place it according to the animation progress + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive((*mpItr).GetInGamePlayer()) && + !m_pAnimScene->GetResidentBrain((*mpItr).GetInGamePlayer())) { + // Show the active players' team flag icons around the site if the brains have arrived + m_apPlayerTeamActionBox[mp]->SetVisible(progress >= 1.0); + // Show the traveling brain if we're not at 0 + m_apPlayerBrainTravelLabel[mp]->SetVisible(progress > 0); + + // Figure out which quad this player is assigned to + for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) { + if (m_aQuadTakenBy[q] == mp) { + quadIndex = q; + break; + } + } + + // Write the brain label, with info if applicable for the current progress of animation + if (progress < 1.0) + std::snprintf(str, sizeof(str), "%c", -48); + // When at site destination, take into account the side the brain icon needs to be on + else { + if (quadIndex <= 1) + std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -48); + else + std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); + } + m_apPlayerBrainTravelLabel[mp]->SetText(str); + m_apPlayerBrainTravelLabel[mp]->SetToolTip("The specific brain that is being sent in to attack this place, and the funds he has been budgeted to do so with."); + + // Figure out start and ending positions for the brain label's travels and the team flag badge + // Upper left quadrant + if (quadIndex == 0) { + // Team flag position + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label start position, taking into account its text alignment + brainLabelStartPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() - (m_apPlayerBrainTravelLabel[mp]->GetWidth() / 2) - 30, m_apBrainPoolLabel[mp]->GetYPos() - 5); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + } + // Lower left quadrant + else if (quadIndex == 1) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label start position, taking into account its text alignment + brainLabelStartPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() - (m_apPlayerBrainTravelLabel[mp]->GetWidth() / 2) - 30, m_apBrainPoolLabel[mp]->GetYPos() - 5); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); + } + // Lower right quadrant + else if (quadIndex == 2) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label start position, taking into account its text alignment + brainLabelStartPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() + 8, m_apBrainPoolLabel[mp]->GetYPos() - 5); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); + } + // Upper right quadrant + else if (quadIndex == 3) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label start position, taking into account its text alignment + brainLabelStartPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() + 8, m_apBrainPoolLabel[mp]->GetYPos() - 5); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + } + // Now place the travelling brain label at the appropriate spot based on the animation progress + brainLabelTravel = brainLabelSitePos - brainLabelStartPos; + // The raw distance the label needs to travel when going down one axis and then the other + brainTravelLength = fabs(brainLabelTravel.m_X) + fabs(brainLabelTravel.m_Y); + // Figure out where along the progression that brain travel switches from one axis to the other + travelSwitchRatio = fabs(brainLabelTravel.m_Y) / brainTravelLength; + + // If animation progress is less than where the switch happens, handle that + if (progress <= travelSwitchRatio) { + // No progress in X + brainTravelPos.m_X = brainLabelStartPos.m_X; + // Some progress in Y + brainTravelPos.m_Y = brainLabelStartPos.m_Y + (brainLabelTravel.m_Y * (progress / travelSwitchRatio)); + } + // Progress is past the switch + else if (progress < 1.0) { + // Some progress in X + brainTravelPos.m_X = brainLabelStartPos.m_X + (brainLabelTravel.m_X * ((progress - travelSwitchRatio) / (1.0 - travelSwitchRatio))); + // FULL progress in Y + brainTravelPos.m_Y = brainLabelSitePos.m_Y; + } + // Reached destination + else + brainTravelPos = brainLabelSitePos; + + // Now actually move the label + m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainTravelPos.m_X, brainTravelPos.m_Y); + } + // Advance the metaplayer index to sync with the iterator + mp++; + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePreBattleDefenders @@ -5384,139 +4849,128 @@ void MetagameGUI::UpdatePreBattleAttackers(float progress) // Description: Updates the animation and display of the info for the current // offensive battle's defenders being next in line for this round. -void MetagameGUI::UpdatePreBattleDefenders(float progress) -{ - // Sanity check that we have any offensive battle activity and scene to display around - if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || - !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive] || - !m_pAnimScene) - return; - - int mp = 0; - char str[256]; - int quadIndex = Players::NoPlayer; - // The screen coordinate position of the current battle site - Vector siteScreenPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); - // Where the brain label starts from - Vector brainLabelStartPos; - // Where it ends up, aligned properly next to the site, in the proper quadrant - Vector brainLabelSitePos; - // The distance the brain label should travel between the two - Vector brainLabelTravel; - // The actual total distance it will travel, when going along each cardinal axis at a time - //float brainTravelLength; - // The animation progress point at which travel switches from one axis to the other - //float travelSwitchRatio; - // The actual current position of the brain label at the current specified animaiton progress point - Vector brainTravelPos; - - // Update the quad assignments for all players - UpdateBattleQuads(siteScreenPos); - - // Go through all players for this activity - for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) - { - // Player active and DEFENDING in current battle, so display his team flag and place it according to the animation progress - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive((*mpItr).GetInGamePlayer()) && - m_pAnimScene->GetResidentBrain((*mpItr).GetInGamePlayer())) - { - // Show the active players' team flag icons around the site if the brains have arrived - m_apPlayerTeamActionBox[mp]->SetVisible(progress >= 1.0); - // Show the traveling brain if we're not at 0 - m_apPlayerBrainTravelLabel[mp]->SetVisible(progress > 0); - - // Figure out which quad this player is assigned to - for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) - { - if (m_aQuadTakenBy[q] == mp) - { - quadIndex = q; - break; - } - } - - // Write the brain label, with info if applicable for the current progress of animation - if (progress < 1.0) - std::snprintf(str, sizeof(str), "%c", -48); - // When at site destination, take into account the side the brain icon needs to be on - else - { - if (quadIndex <= 1) - std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -48); - else - std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); - } - m_apPlayerBrainTravelLabel[mp]->SetText(str); - m_apPlayerBrainTravelLabel[mp]->SetToolTip("The resident brain that is defending this site from attack, and the unallocated funds of its player that he gets to use (beyond the defense investments already made here)."); - - // Figure out start and ending positions for the brain label's travels and the team flag badge - // Upper left quadrant - if (quadIndex == 0) - { - // Team flag position - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label start position, taking into account its text alignment - brainLabelStartPos.SetXY(siteScreenPos.m_X - m_apPlayerBrainTravelLabel[mp]->GetWidth() + 7, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - } - // Lower left quadrant - else if (quadIndex == 1) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label start position, taking into account its text alignment - brainLabelStartPos.SetXY(siteScreenPos.m_X - m_apPlayerBrainTravelLabel[mp]->GetWidth() + 7, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); - } - // Lower right quadrant - else if (quadIndex == 2) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label start position, taking into account its text alignment - brainLabelStartPos.SetXY(siteScreenPos.m_X - 5, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); - } - // Upper right quadrant - else if (quadIndex == 3) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label start position, taking into account its text alignment - brainLabelStartPos.SetXY(siteScreenPos.m_X - 5, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - } - - // Now place the travelling brain label at the appropriate spot based on the animation progress - brainLabelTravel = brainLabelSitePos - brainLabelStartPos; - - // How far have we come - if (progress <= 0) - brainTravelPos = brainLabelStartPos; - // Somewhere in between - else if (progress < 1.0) - brainTravelPos = brainLabelStartPos + brainLabelTravel * progress; - // Reached destination - else - brainTravelPos = brainLabelSitePos; - - // Now actually move the label - m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainTravelPos.m_X, brainTravelPos.m_Y); - } - // Advance the metaplayer index to sync with the iterator - mp++; - } -} +void MetagameGUI::UpdatePreBattleDefenders(float progress) { + // Sanity check that we have any offensive battle activity and scene to display around + if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || + !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive] || + !m_pAnimScene) + return; + + int mp = 0; + char str[256]; + int quadIndex = Players::NoPlayer; + // The screen coordinate position of the current battle site + Vector siteScreenPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); + // Where the brain label starts from + Vector brainLabelStartPos; + // Where it ends up, aligned properly next to the site, in the proper quadrant + Vector brainLabelSitePos; + // The distance the brain label should travel between the two + Vector brainLabelTravel; + // The actual total distance it will travel, when going along each cardinal axis at a time + // float brainTravelLength; + // The animation progress point at which travel switches from one axis to the other + // float travelSwitchRatio; + // The actual current position of the brain label at the current specified animaiton progress point + Vector brainTravelPos; + + // Update the quad assignments for all players + UpdateBattleQuads(siteScreenPos); + + // Go through all players for this activity + for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) { + // Player active and DEFENDING in current battle, so display his team flag and place it according to the animation progress + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerActive((*mpItr).GetInGamePlayer()) && + m_pAnimScene->GetResidentBrain((*mpItr).GetInGamePlayer())) { + // Show the active players' team flag icons around the site if the brains have arrived + m_apPlayerTeamActionBox[mp]->SetVisible(progress >= 1.0); + // Show the traveling brain if we're not at 0 + m_apPlayerBrainTravelLabel[mp]->SetVisible(progress > 0); + + // Figure out which quad this player is assigned to + for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) { + if (m_aQuadTakenBy[q] == mp) { + quadIndex = q; + break; + } + } + + // Write the brain label, with info if applicable for the current progress of animation + if (progress < 1.0) + std::snprintf(str, sizeof(str), "%c", -48); + // When at site destination, take into account the side the brain icon needs to be on + else { + if (quadIndex <= 1) + std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -48); + else + std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); + } + m_apPlayerBrainTravelLabel[mp]->SetText(str); + m_apPlayerBrainTravelLabel[mp]->SetToolTip("The resident brain that is defending this site from attack, and the unallocated funds of its player that he gets to use (beyond the defense investments already made here)."); + + // Figure out start and ending positions for the brain label's travels and the team flag badge + // Upper left quadrant + if (quadIndex == 0) { + // Team flag position + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label start position, taking into account its text alignment + brainLabelStartPos.SetXY(siteScreenPos.m_X - m_apPlayerBrainTravelLabel[mp]->GetWidth() + 7, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + } + // Lower left quadrant + else if (quadIndex == 1) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label start position, taking into account its text alignment + brainLabelStartPos.SetXY(siteScreenPos.m_X - m_apPlayerBrainTravelLabel[mp]->GetWidth() + 7, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); + } + // Lower right quadrant + else if (quadIndex == 2) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label start position, taking into account its text alignment + brainLabelStartPos.SetXY(siteScreenPos.m_X - 5, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); + } + // Upper right quadrant + else if (quadIndex == 3) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label start position, taking into account its text alignment + brainLabelStartPos.SetXY(siteScreenPos.m_X - 5, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + } + // Now place the travelling brain label at the appropriate spot based on the animation progress + brainLabelTravel = brainLabelSitePos - brainLabelStartPos; + + // How far have we come + if (progress <= 0) + brainTravelPos = brainLabelStartPos; + // Somewhere in between + else if (progress < 1.0) + brainTravelPos = brainLabelStartPos + brainLabelTravel * progress; + // Reached destination + else + brainTravelPos = brainLabelSitePos; + + // Now actually move the label + m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainTravelPos.m_X, brainTravelPos.m_Y); + } + // Advance the metaplayer index to sync with the iterator + mp++; + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePostBattleRetreaters @@ -5524,154 +4978,141 @@ void MetagameGUI::UpdatePreBattleDefenders(float progress) // Description: Updates the animation and display of the info for the current // offensive battle's retreating brains going back to their pools -void MetagameGUI::UpdatePostBattleRetreaters(float progress) -{ - // Sanity check that we have any offensive battle activity and scene to display around - if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || - !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive] || - !m_pAnimScene) - return; - - int mp = 0; - char str[256]; - int quadIndex = Players::NoPlayer; - // The screen coordinate position of the current battle site - Vector siteScreenPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); - // Where it ends up, aligned properly next to the site, in the proper quadrant - Vector brainLabelSitePos; - // Where the brain label ends at, back at the pool - Vector brainLabelEndPos; - // The distance the brain label should travel between the two - Vector brainLabelTravel; - // The actual total distance it will travel, when going along each cardinal axis at a time - float brainTravelLength; - // The animation progress point at which travel switches from one axis to the other - float travelSwitchRatio; - // The actual current position of the brain label at the current specified animaiton progress point - Vector brainTravelPos; - - // Update the quad assignments for all players - UpdateBattleQuads(siteScreenPos); - - // Go through all players for this activity - for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) - { - // Player active and EVACUATION/RETREATING in current battle, so display his team flag and place it according to the animation progress - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain((*mpItr).GetInGamePlayer()) && - g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated((*mpItr).GetInGamePlayer())) - { - // Show the active players' team flag icons around the site if the brains have arrived - m_apPlayerTeamActionBox[mp]->SetVisible(progress <= 0); - // Show the traveling brain if we're not at destination yet - m_apPlayerBrainTravelLabel[mp]->SetVisible(progress < 1.0); - - // Figure out which quad this player is assigned to - for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) - { - if (m_aQuadTakenBy[q] == mp) - { - quadIndex = q; - break; - } - } - - // Write the brain label, with info if applicable for the current progress of animation - if (progress > 0) - std::snprintf(str, sizeof(str), "%c", -48); - // When at site destination, take into account the side the brain icon needs to be on - else - { - if (quadIndex <= 1) - std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -48); - else - std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); - } - m_apPlayerBrainTravelLabel[mp]->SetText(str); - m_apPlayerBrainTravelLabel[mp]->SetToolTip("The specific brain that is being sent in to attack this place, and the funds he has been budgeted to do so with."); - - // Figure out start and ending positions for the brain label's travels and the team flag badge - // Upper left quadrant - if (quadIndex == 0) - { - // Team flag position - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - // Brain label start position, taking into account its text alignment - brainLabelEndPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() - (m_apPlayerBrainTravelLabel[mp]->GetWidth() / 2) - 30, m_apBrainPoolLabel[mp]->GetYPos() - 5); - } - // Lower left quadrant - else if (quadIndex == 1) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); - // Brain label start position, taking into account its text alignment - brainLabelEndPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() - (m_apPlayerBrainTravelLabel[mp]->GetWidth() / 2) - 30, m_apBrainPoolLabel[mp]->GetYPos() - 5); - } - // Lower right quadrant - else if (quadIndex == 2) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); - // Brain label start position, taking into account its text alignment - brainLabelEndPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() + 8, m_apBrainPoolLabel[mp]->GetYPos() - 5); - } - // Upper right quadrant - else if (quadIndex == 3) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - // Brain label start position, taking into account its text alignment - brainLabelEndPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() + 8, m_apBrainPoolLabel[mp]->GetYPos() - 5); - } - - // Now place the travelling brain label at the appropriate spot based on the animation progress - brainLabelTravel = brainLabelEndPos - brainLabelSitePos; - // The raw distance the label needs to travel when going down one axis and then the other - brainTravelLength = fabs(brainLabelTravel.m_X) + fabs(brainLabelTravel.m_Y); - // Figure out where along the progression that brain travel switches from one axis to the other - travelSwitchRatio = fabs(brainLabelTravel.m_X) / brainTravelLength; - - // If animation progress is less than where the switch happens, handle that - if (progress <= travelSwitchRatio) - { - - // Some progress in X - brainTravelPos.m_X = brainLabelSitePos.m_X + (brainLabelTravel.m_X * (progress / travelSwitchRatio)); - // No progress in Y - brainTravelPos.m_Y = brainLabelSitePos.m_Y; - } - // Progress is past the switch - else if (progress < 1.0) - { - // FULL progress in X - brainTravelPos.m_X = brainLabelEndPos.m_X; - // Some progress in Y - brainTravelPos.m_Y = brainLabelSitePos.m_Y + (brainLabelTravel.m_Y * ((progress - travelSwitchRatio) / (1.0 - travelSwitchRatio))); - } - // Reached destination - else - brainTravelPos = brainLabelEndPos; - - // Now actually move the label - m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainTravelPos.m_X, brainTravelPos.m_Y); - } - // Advance the metaplayer index to sync with the iterator - mp++; - } -} +void MetagameGUI::UpdatePostBattleRetreaters(float progress) { + // Sanity check that we have any offensive battle activity and scene to display around + if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || + !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive] || + !m_pAnimScene) + return; + + int mp = 0; + char str[256]; + int quadIndex = Players::NoPlayer; + // The screen coordinate position of the current battle site + Vector siteScreenPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); + // Where it ends up, aligned properly next to the site, in the proper quadrant + Vector brainLabelSitePos; + // Where the brain label ends at, back at the pool + Vector brainLabelEndPos; + // The distance the brain label should travel between the two + Vector brainLabelTravel; + // The actual total distance it will travel, when going along each cardinal axis at a time + float brainTravelLength; + // The animation progress point at which travel switches from one axis to the other + float travelSwitchRatio; + // The actual current position of the brain label at the current specified animaiton progress point + Vector brainTravelPos; + + // Update the quad assignments for all players + UpdateBattleQuads(siteScreenPos); + + // Go through all players for this activity + for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) { + // Player active and EVACUATION/RETREATING in current battle, so display his team flag and place it according to the animation progress + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain((*mpItr).GetInGamePlayer()) && + g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated((*mpItr).GetInGamePlayer())) { + // Show the active players' team flag icons around the site if the brains have arrived + m_apPlayerTeamActionBox[mp]->SetVisible(progress <= 0); + // Show the traveling brain if we're not at destination yet + m_apPlayerBrainTravelLabel[mp]->SetVisible(progress < 1.0); + + // Figure out which quad this player is assigned to + for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) { + if (m_aQuadTakenBy[q] == mp) { + quadIndex = q; + break; + } + } + + // Write the brain label, with info if applicable for the current progress of animation + if (progress > 0) + std::snprintf(str, sizeof(str), "%c", -48); + // When at site destination, take into account the side the brain icon needs to be on + else { + if (quadIndex <= 1) + std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -48); + else + std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); + } + m_apPlayerBrainTravelLabel[mp]->SetText(str); + m_apPlayerBrainTravelLabel[mp]->SetToolTip("The specific brain that is being sent in to attack this place, and the funds he has been budgeted to do so with."); + + // Figure out start and ending positions for the brain label's travels and the team flag badge + // Upper left quadrant + if (quadIndex == 0) { + // Team flag position + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + // Brain label start position, taking into account its text alignment + brainLabelEndPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() - (m_apPlayerBrainTravelLabel[mp]->GetWidth() / 2) - 30, m_apBrainPoolLabel[mp]->GetYPos() - 5); + } + // Lower left quadrant + else if (quadIndex == 1) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); + // Brain label start position, taking into account its text alignment + brainLabelEndPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() - (m_apPlayerBrainTravelLabel[mp]->GetWidth() / 2) - 30, m_apBrainPoolLabel[mp]->GetYPos() - 5); + } + // Lower right quadrant + else if (quadIndex == 2) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); + // Brain label start position, taking into account its text alignment + brainLabelEndPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() + 8, m_apBrainPoolLabel[mp]->GetYPos() - 5); + } + // Upper right quadrant + else if (quadIndex == 3) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + // Brain label start position, taking into account its text alignment + brainLabelEndPos.SetXY(m_apBrainPoolLabel[mp]->GetXPos() + 8, m_apBrainPoolLabel[mp]->GetYPos() - 5); + } + + // Now place the travelling brain label at the appropriate spot based on the animation progress + brainLabelTravel = brainLabelEndPos - brainLabelSitePos; + // The raw distance the label needs to travel when going down one axis and then the other + brainTravelLength = fabs(brainLabelTravel.m_X) + fabs(brainLabelTravel.m_Y); + // Figure out where along the progression that brain travel switches from one axis to the other + travelSwitchRatio = fabs(brainLabelTravel.m_X) / brainTravelLength; + // If animation progress is less than where the switch happens, handle that + if (progress <= travelSwitchRatio) { + + // Some progress in X + brainTravelPos.m_X = brainLabelSitePos.m_X + (brainLabelTravel.m_X * (progress / travelSwitchRatio)); + // No progress in Y + brainTravelPos.m_Y = brainLabelSitePos.m_Y; + } + // Progress is past the switch + else if (progress < 1.0) { + // FULL progress in X + brainTravelPos.m_X = brainLabelEndPos.m_X; + // Some progress in Y + brainTravelPos.m_Y = brainLabelSitePos.m_Y + (brainLabelTravel.m_Y * ((progress - travelSwitchRatio) / (1.0 - travelSwitchRatio))); + } + // Reached destination + else + brainTravelPos = brainLabelEndPos; + + // Now actually move the label + m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainTravelPos.m_X, brainTravelPos.m_Y); + } + // Advance the metaplayer index to sync with the iterator + mp++; + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePostBattleResidents @@ -5679,232 +5120,210 @@ void MetagameGUI::UpdatePostBattleRetreaters(float progress) // Description: Updates the animation and display of the info for the current done // offensive battle's winning brains going back into the site. -void MetagameGUI::UpdatePostBattleResidents(float progress) -{ - // Sanity check that we have any offensive battle activity and scene to display around - if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || - !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive] || - !m_pAnimScene) - return; - - int mp = 0; - char str[256]; - int quadIndex = Players::NoPlayer; - // The screen coordinate position of the current battle site - Vector siteScreenPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); - // Where the brain label ends at - Vector brainLabelEndPos; - // Where it ends up, aligned properly next to the site, in the proper quadrant - Vector brainLabelSitePos; - // The distance the brain label should travel between the two - Vector brainLabelTravel; - // The actual total distance it will travel, when going along each cardinal axis at a time - //float brainTravelLength; - // The animation progress point at which travel switches from one axis to the other - //float travelSwitchRatio; - // The actual current position of the brain label at the current specified animaiton progress point - Vector brainTravelPos; - - // Update the quad assignments for all players - UpdateBattleQuads(siteScreenPos); - - // Go through all players for this activity - for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) - { - // Player active in the past battle, so display his team flag and place it according to the animation progress - // Also player who didn't evacuate - they are handled by UpdatePostBattleRetreaters - if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain((*mpItr).GetInGamePlayer()) && - !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated((*mpItr).GetInGamePlayer())) - { - // Figure out which quad this player is assigned to - for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) - { - if (m_aQuadTakenBy[q] == mp) - { - quadIndex = q; - break; - } - } - - // The brains who DID NOT MAKE IT - DEAD and did not evac - if (!m_pAnimScene->GetResidentBrain((*mpItr).GetInGamePlayer())) - { - // Hide the losers after the residents start moving in - m_apPlayerTeamActionBox[mp]->SetVisible(progress <= 0); - m_apPlayerBrainTravelLabel[mp]->SetVisible(progress <= 0); - - if (progress <= 0) - { - // Death mask - if (progress > 0) - std::snprintf(str, sizeof(str), "%c", -26); - // Brain with line blinking over it and the funds still showing - else - { - if (quadIndex <= 1) - { - if (m_aAnimDestroyed[mp]) - std::snprintf(str, sizeof(str), "%c %.0f oz ", -58, m_aBattleFunds[mp]); - else - std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -26); -// std::snprintf(str, sizeof(str), "%c %.0f oz %c", -58, m_aBattleFunds[mp], m_AnimTimer2.AlternateReal(200) ? -39 : -26); - } - else - { - if (m_aAnimDestroyed[mp]) - std::snprintf(str, sizeof(str), " %c %.0f oz", -58, m_aBattleFunds[mp]); - else - std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", m_aAnimDestroyed[mp] ? ' ' : -26, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); -// std::snprintf(str, sizeof(str), "%c %c %.0f oz", m_AnimTimer2.AlternateReal(200) ? -39 : -26, -58, m_aBattleFunds[mp]); - } - } - m_apPlayerBrainTravelLabel[mp]->SetText(str); - m_apPlayerBrainTravelLabel[mp]->SetToolTip("The specific brain that is being sent in to attack this place, and the funds he has been budgeted to do so with."); - } - - // Figure out the position of the dead brain label - // Upper left quadrant - if (quadIndex == 0) - { - // Team flag position - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label start position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - // Where the brain icon is now exactly, so we can accurately pin the blow up animation on it - m_aBrainIconPos[mp] = brainLabelSitePos + Vector(m_apPlayerBrainTravelLabel[mp]->GetWidth() - 6, m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2); - } - // Lower left quadrant - else if (quadIndex == 1) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label start position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); - // Where the brain icon is now exactly, so we can accurately pin the blow up animation on it - m_aBrainIconPos[mp] = brainLabelSitePos + Vector(m_apPlayerBrainTravelLabel[mp]->GetWidth() - 6, m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2); - } - // Lower right quadrant - else if (quadIndex == 2) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label start position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); - // Where the brain icon is now exactly, so we can accurately pin the blow up animation on it - m_aBrainIconPos[mp] = brainLabelSitePos + Vector(6, m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2); - } - // Upper right quadrant - else if (quadIndex == 3) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label start position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - // Where the brain icon is now exactly, so we can accurately pin the blow up animation on it - m_aBrainIconPos[mp] = brainLabelSitePos + Vector(6, m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2); - } - - // Now actually place the label - m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainLabelSitePos.m_X, brainLabelSitePos.m_Y); - } - // The SURVIVORS who aren't evacuating either - ie they won and are staying - else - { - // Show the active players' team flag icons around the site if the brains have arrived - m_apPlayerTeamActionBox[mp]->SetVisible(progress <= 0); - // Show the traveling brain if we're not at 1.0 - m_apPlayerBrainTravelLabel[mp]->SetVisible(progress < 1.0); - - // Write the brain label, with info if applicable for the current progress of animation - if (progress > 0) - std::snprintf(str, sizeof(str), "%c", -48); - // When at site start position, take into account the side the brain icon needs to be on - else - { - if (quadIndex <= 1) - std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -48); -// std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], -47, -48); - else - std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); -// std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, -47, -58, m_aBattleFunds[mp]); - } - m_apPlayerBrainTravelLabel[mp]->SetText(str); - m_apPlayerBrainTravelLabel[mp]->SetToolTip("The new resident brain that has won this site and is settling in here now."); - - // Figure out start and ending positions for the brain label's travels and the team flag badge - // Upper left quadrant - if (quadIndex == 0) - { - // Team flag position - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - // Brain label start position, taking into account its text alignment - brainLabelEndPos.SetXY(siteScreenPos.m_X - m_apPlayerBrainTravelLabel[mp]->GetWidth() + 7, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); - } - // Lower left quadrant - else if (quadIndex == 1) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); - // Brain label start position, taking into account its text alignment - brainLabelEndPos.SetXY(siteScreenPos.m_X - m_apPlayerBrainTravelLabel[mp]->GetWidth() + 7, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); - } - // Lower right quadrant - else if (quadIndex == 2) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); - // Brain label start position, taking into account its text alignment - brainLabelEndPos.SetXY(siteScreenPos.m_X - 5, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); - } - // Upper right quadrant - else if (quadIndex == 3) - { - m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); - // Brain label text alignment, depending on the quadrant - m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); - // Brain label end/site position, taking into account its text alignment - brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); - // Brain label start position, taking into account its text alignment - brainLabelEndPos.SetXY(siteScreenPos.m_X - 5, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); - } - - // Now place the travelling brain label at the appropriate spot based on the animation progress - brainLabelTravel = brainLabelEndPos - brainLabelSitePos; - - // How far have we come - if (progress <= 0) - brainTravelPos = brainLabelSitePos; - // Somewhere in between - else if (progress < 1.0) - brainTravelPos = brainLabelSitePos + brainLabelTravel * progress; - // Reached destination - else - brainTravelPos = brainLabelEndPos; - - // Now actually move the label - m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainTravelPos.m_X, brainTravelPos.m_Y); - } - } - // Advance the metaplayer index to sync with the iterator - mp++; - } -} +void MetagameGUI::UpdatePostBattleResidents(float progress) { + // Sanity check that we have any offensive battle activity and scene to display around + if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || + !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive] || + !m_pAnimScene) + return; + + int mp = 0; + char str[256]; + int quadIndex = Players::NoPlayer; + // The screen coordinate position of the current battle site + Vector siteScreenPos = m_PlanetCenter + m_pAnimScene->GetLocation() + m_pAnimScene->GetLocationOffset(); + // Where the brain label ends at + Vector brainLabelEndPos; + // Where it ends up, aligned properly next to the site, in the proper quadrant + Vector brainLabelSitePos; + // The distance the brain label should travel between the two + Vector brainLabelTravel; + // The actual total distance it will travel, when going along each cardinal axis at a time + // float brainTravelLength; + // The animation progress point at which travel switches from one axis to the other + // float travelSwitchRatio; + // The actual current position of the brain label at the current specified animaiton progress point + Vector brainTravelPos; + + // Update the quad assignments for all players + UpdateBattleQuads(siteScreenPos); + + // Go through all players for this activity + for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) { + // Player active in the past battle, so display his team flag and place it according to the animation progress + // Also player who didn't evacuate - they are handled by UpdatePostBattleRetreaters + if (g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->PlayerHadBrain((*mpItr).GetInGamePlayer()) && + !g_MetaMan.m_RoundOffensives[g_MetaMan.m_CurrentOffensive]->BrainWasEvacuated((*mpItr).GetInGamePlayer())) { + // Figure out which quad this player is assigned to + for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) { + if (m_aQuadTakenBy[q] == mp) { + quadIndex = q; + break; + } + } + + // The brains who DID NOT MAKE IT - DEAD and did not evac + if (!m_pAnimScene->GetResidentBrain((*mpItr).GetInGamePlayer())) { + // Hide the losers after the residents start moving in + m_apPlayerTeamActionBox[mp]->SetVisible(progress <= 0); + m_apPlayerBrainTravelLabel[mp]->SetVisible(progress <= 0); + + if (progress <= 0) { + // Death mask + if (progress > 0) + std::snprintf(str, sizeof(str), "%c", -26); + // Brain with line blinking over it and the funds still showing + else { + if (quadIndex <= 1) { + if (m_aAnimDestroyed[mp]) + std::snprintf(str, sizeof(str), "%c %.0f oz ", -58, m_aBattleFunds[mp]); + else + std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -26); + // std::snprintf(str, sizeof(str), "%c %.0f oz %c", -58, m_aBattleFunds[mp], m_AnimTimer2.AlternateReal(200) ? -39 : -26); + } else { + if (m_aAnimDestroyed[mp]) + std::snprintf(str, sizeof(str), " %c %.0f oz", -58, m_aBattleFunds[mp]); + else + std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", m_aAnimDestroyed[mp] ? ' ' : -26, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); + // std::snprintf(str, sizeof(str), "%c %c %.0f oz", m_AnimTimer2.AlternateReal(200) ? -39 : -26, -58, m_aBattleFunds[mp]); + } + } + m_apPlayerBrainTravelLabel[mp]->SetText(str); + m_apPlayerBrainTravelLabel[mp]->SetToolTip("The specific brain that is being sent in to attack this place, and the funds he has been budgeted to do so with."); + } + + // Figure out the position of the dead brain label + // Upper left quadrant + if (quadIndex == 0) { + // Team flag position + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label start position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + // Where the brain icon is now exactly, so we can accurately pin the blow up animation on it + m_aBrainIconPos[mp] = brainLabelSitePos + Vector(m_apPlayerBrainTravelLabel[mp]->GetWidth() - 6, m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2); + } + // Lower left quadrant + else if (quadIndex == 1) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label start position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); + // Where the brain icon is now exactly, so we can accurately pin the blow up animation on it + m_aBrainIconPos[mp] = brainLabelSitePos + Vector(m_apPlayerBrainTravelLabel[mp]->GetWidth() - 6, m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2); + } + // Lower right quadrant + else if (quadIndex == 2) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label start position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); + // Where the brain icon is now exactly, so we can accurately pin the blow up animation on it + m_aBrainIconPos[mp] = brainLabelSitePos + Vector(6, m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2); + } + // Upper right quadrant + else if (quadIndex == 3) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label start position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + // Where the brain icon is now exactly, so we can accurately pin the blow up animation on it + m_aBrainIconPos[mp] = brainLabelSitePos + Vector(6, m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2); + } + + // Now actually place the label + m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainLabelSitePos.m_X, brainLabelSitePos.m_Y); + } + // The SURVIVORS who aren't evacuating either - ie they won and are staying + else { + // Show the active players' team flag icons around the site if the brains have arrived + m_apPlayerTeamActionBox[mp]->SetVisible(progress <= 0); + // Show the traveling brain if we're not at 1.0 + m_apPlayerBrainTravelLabel[mp]->SetVisible(progress < 1.0); + + // Write the brain label, with info if applicable for the current progress of animation + if (progress > 0) + std::snprintf(str, sizeof(str), "%c", -48); + // When at site start position, take into account the side the brain icon needs to be on + else { + if (quadIndex <= 1) + std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], m_aBattleAttacker[mp] ? -46 : -47, -48); + // std::snprintf(str, sizeof(str), "%c %.0f oz %c%c", -58, m_aBattleFunds[mp], -47, -48); + else + std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, m_aBattleAttacker[mp] ? -46 : -47, -58, m_aBattleFunds[mp]); + // std::snprintf(str, sizeof(str), "%c%c %c %.0f oz", -48, -47, -58, m_aBattleFunds[mp]); + } + m_apPlayerBrainTravelLabel[mp]->SetText(str); + m_apPlayerBrainTravelLabel[mp]->SetToolTip("The new resident brain that has won this site and is settling in here now."); + + // Figure out start and ending positions for the brain label's travels and the team flag badge + // Upper left quadrant + if (quadIndex == 0) { + // Team flag position + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + // Brain label start position, taking into account its text alignment + brainLabelEndPos.SetXY(siteScreenPos.m_X - m_apPlayerBrainTravelLabel[mp]->GetWidth() + 7, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); + } + // Lower left quadrant + else if (quadIndex == 1) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X - m_apPlayerTeamActionBox[mp]->GetWidth() - BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Right); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(-BATTLEPAD - (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) - BRAINOVERLAP - m_apPlayerBrainTravelLabel[mp]->GetWidth(), 0); + // Brain label start position, taking into account its text alignment + brainLabelEndPos.SetXY(siteScreenPos.m_X - m_apPlayerBrainTravelLabel[mp]->GetWidth() + 7, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); + } + // Lower right quadrant + else if (quadIndex == 2) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y + BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, 0); + // Brain label start position, taking into account its text alignment + brainLabelEndPos.SetXY(siteScreenPos.m_X - 5, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); + } + // Upper right quadrant + else if (quadIndex == 3) { + m_apPlayerTeamActionBox[mp]->SetPositionAbs(siteScreenPos.m_X + BATTLEPAD, siteScreenPos.m_Y - m_apPlayerTeamActionBox[mp]->GetHeight() - BATTLEPAD); + // Brain label text alignment, depending on the quadrant + m_apPlayerBrainTravelLabel[mp]->SetHAlignment(GUIFont::Left); + // Brain label end/site position, taking into account its text alignment + brainLabelSitePos = siteScreenPos + Vector(BATTLEPAD + (m_apPlayerTeamActionBox[mp]->GetWidth() / 2) + BRAINOVERLAP, -m_apPlayerBrainTravelLabel[mp]->GetHeight()); + // Brain label start position, taking into account its text alignment + brainLabelEndPos.SetXY(siteScreenPos.m_X - 5, siteScreenPos.m_Y - (m_apPlayerBrainTravelLabel[mp]->GetHeight() / 2) + 2); + } + + // Now place the travelling brain label at the appropriate spot based on the animation progress + brainLabelTravel = brainLabelEndPos - brainLabelSitePos; + // How far have we come + if (progress <= 0) + brainTravelPos = brainLabelSitePos; + // Somewhere in between + else if (progress < 1.0) + brainTravelPos = brainLabelSitePos + brainLabelTravel * progress; + // Reached destination + else + brainTravelPos = brainLabelEndPos; + + // Now actually move the label + m_apPlayerBrainTravelLabel[mp]->SetPositionAbs(brainTravelPos.m_X, brainTravelPos.m_Y); + } + } + // Advance the metaplayer index to sync with the iterator + mp++; + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePlayerActionLines @@ -5912,259 +5331,232 @@ void MetagameGUI::UpdatePostBattleResidents(float progress) // Description: Updates the action lines as per what the player has chosen to do // during the current turn so far. -float MetagameGUI::UpdatePlayerActionLines(int metaPlayer)//, bool addUnallocated) +float MetagameGUI::UpdatePlayerActionLines(int metaPlayer) //, bool addUnallocated) { - // Make sure we're in a player turn phase - if (metaPlayer < Players::PlayerOne || metaPlayer >= g_MetaMan.m_Players.size()) - return 0; - - // First clean out all Action sitelines for this player - m_ActionSiteLines[metaPlayer].clear(); - - // Get the total funds we have to work with - float totalFunds = g_MetaMan.m_Players[metaPlayer].GetFunds(); - - // Loop through the scenes owned by that player, setting up the building budget site line for each - const Scene *pScene = 0; - int channelHeight = 60; - float meterStart = 0; - while (pScene = g_MetaMan.GetNextSceneOfPlayer(metaPlayer, pScene)) - { - // Add line for scenes which are owned and whose build budgets have been set to something - if (pScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer) && std::floor(pScene->GetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer())) > 0) - { - m_ActionSiteLines[metaPlayer].push_back(SiteLine(metaPlayer, meterStart, pScene->GetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()) / totalFunds, pScene->GetLocation() + pScene->GetLocationOffset(), pScene->GetPresetName(), pScene, c_GUIColorGreen, -1, -1, channelHeight, 1.0f, g_MetaMan.IsActiveTeam(pScene->GetTeamOwnership()))); - m_ActionSiteLines[metaPlayer].back().m_FundsAmount = pScene->GetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()); - meterStart += m_ActionSiteLines[metaPlayer].back().m_MeterAmount; - channelHeight += 10; - } - } - - // If we have a selected offensive target, find it and create its line as well - std::string targetName = g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName(); - float offensiveBudget = g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget(); - if (!targetName.empty() && offensiveBudget > 0) - { - for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) - { - if ((*sItr)->IsRevealed() && (*sItr)->GetPresetName() == targetName) - { - m_ActionSiteLines[metaPlayer].push_back(SiteLine(metaPlayer, meterStart, offensiveBudget / totalFunds, (*sItr)->GetLocation() + (*sItr)->GetLocationOffset(), (*sItr)->GetPresetName(), (*sItr), c_GUIColorRed, -1, -1, channelHeight, 1.0f, g_MetaMan.IsActiveTeam((*sItr)->GetTeamOwnership()))); - m_ActionSiteLines[metaPlayer].back().m_FundsAmount = offensiveBudget; - meterStart += m_ActionSiteLines[metaPlayer].back().m_MeterAmount; - } - } - } - - return meterStart; -} + // Make sure we're in a player turn phase + if (metaPlayer < Players::PlayerOne || metaPlayer >= g_MetaMan.m_Players.size()) + return 0; + + // First clean out all Action sitelines for this player + m_ActionSiteLines[metaPlayer].clear(); + + // Get the total funds we have to work with + float totalFunds = g_MetaMan.m_Players[metaPlayer].GetFunds(); + + // Loop through the scenes owned by that player, setting up the building budget site line for each + const Scene* pScene = 0; + int channelHeight = 60; + float meterStart = 0; + while (pScene = g_MetaMan.GetNextSceneOfPlayer(metaPlayer, pScene)) { + // Add line for scenes which are owned and whose build budgets have been set to something + if (pScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer) && std::floor(pScene->GetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer())) > 0) { + m_ActionSiteLines[metaPlayer].push_back(SiteLine(metaPlayer, meterStart, pScene->GetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()) / totalFunds, pScene->GetLocation() + pScene->GetLocationOffset(), pScene->GetPresetName(), pScene, c_GUIColorGreen, -1, -1, channelHeight, 1.0f, g_MetaMan.IsActiveTeam(pScene->GetTeamOwnership()))); + m_ActionSiteLines[metaPlayer].back().m_FundsAmount = pScene->GetBuildBudget(g_MetaMan.m_Players[metaPlayer].GetInGamePlayer()); + meterStart += m_ActionSiteLines[metaPlayer].back().m_MeterAmount; + channelHeight += 10; + } + } + + // If we have a selected offensive target, find it and create its line as well + std::string targetName = g_MetaMan.m_Players[metaPlayer].GetOffensiveTargetName(); + float offensiveBudget = g_MetaMan.m_Players[metaPlayer].GetOffensiveBudget(); + if (!targetName.empty() && offensiveBudget > 0) { + for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { + if ((*sItr)->IsRevealed() && (*sItr)->GetPresetName() == targetName) { + m_ActionSiteLines[metaPlayer].push_back(SiteLine(metaPlayer, meterStart, offensiveBudget / totalFunds, (*sItr)->GetLocation() + (*sItr)->GetLocationOffset(), (*sItr)->GetPresetName(), (*sItr), c_GUIColorRed, -1, -1, channelHeight, 1.0f, g_MetaMan.IsActiveTeam((*sItr)->GetTeamOwnership()))); + m_ActionSiteLines[metaPlayer].back().m_FundsAmount = offensiveBudget; + meterStart += m_ActionSiteLines[metaPlayer].back().m_MeterAmount; + } + } + } + return meterStart; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateScenesBox ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the contents of the scene selection box. -void MetagameGUI::UpdateScenesBox(bool sceneChanged) -{ - // Always show the info box if something is selected - if (m_pSelectedScene && !g_MetaMan.IsSuspended()) - { - char str[256]; - // Set the close button to have that fancy X - std::snprintf(str, sizeof(str), "%c", -36); - m_pSceneCloseButton->SetText(std::string(str)); - - // Set the currently selected scene's texts - m_pSceneInfoPopup->SetVisible(true); - m_pSceneNameLabel->SetText(m_pSelectedScene->GetPresetName()); - - // Show which team owns this place, and how many resident brains there are here of that team - if (m_pSelectedScene->GetTeamOwnership() > Activity::NoTeam && m_pSelectedScene->GetTeamOwnership() < Activity::MaxTeamCount) - { - // Set the team flag icon - m_pSceneOwnerTeam->SetVisible(true); - m_pSceneOwnerTeam->SetDrawType(GUICollectionBox::Image); - m_pSceneOwnerTeam->SetDrawImage(new AllegroBitmap(g_MetaMan.m_TeamIcons[m_pSelectedScene->GetTeamOwnership()].GetBitmaps32()[0])); - // Show how many resident brains there are hanging out here - std::snprintf(str, sizeof(str), "%c", -26); - std::string brainString = ""; - int brainCount = m_pSelectedScene->GetResidentBrainCount(); - for (int i = 0; i < brainCount; ++i) - brainString += str; - m_pSceneResidentsLabel->SetText(brainString); - m_pSceneResidentsLabel->SetVisible(true); - } - // Noone owns this place, so hide this ownership stuff - else - { - m_pSceneOwnerTeam->SetVisible(false); - m_pSceneResidentsLabel->SetVisible(false); - m_pSceneResidentsLabel->SetText(""); - } - - // Write the description, and add the total defense investment in this place so far as a lil stat +void MetagameGUI::UpdateScenesBox(bool sceneChanged) { + // Always show the info box if something is selected + if (m_pSelectedScene && !g_MetaMan.IsSuspended()) { + char str[256]; + // Set the close button to have that fancy X + std::snprintf(str, sizeof(str), "%c", -36); + m_pSceneCloseButton->SetText(std::string(str)); + + // Set the currently selected scene's texts + m_pSceneInfoPopup->SetVisible(true); + m_pSceneNameLabel->SetText(m_pSelectedScene->GetPresetName()); + + // Show which team owns this place, and how many resident brains there are here of that team + if (m_pSelectedScene->GetTeamOwnership() > Activity::NoTeam && m_pSelectedScene->GetTeamOwnership() < Activity::MaxTeamCount) { + // Set the team flag icon + m_pSceneOwnerTeam->SetVisible(true); + m_pSceneOwnerTeam->SetDrawType(GUICollectionBox::Image); + m_pSceneOwnerTeam->SetDrawImage(new AllegroBitmap(g_MetaMan.m_TeamIcons[m_pSelectedScene->GetTeamOwnership()].GetBitmaps32()[0])); + // Show how many resident brains there are hanging out here + std::snprintf(str, sizeof(str), "%c", -26); + std::string brainString = ""; + int brainCount = m_pSelectedScene->GetResidentBrainCount(); + for (int i = 0; i < brainCount; ++i) + brainString += str; + m_pSceneResidentsLabel->SetText(brainString); + m_pSceneResidentsLabel->SetVisible(true); + } + // Noone owns this place, so hide this ownership stuff + else { + m_pSceneOwnerTeam->SetVisible(false); + m_pSceneResidentsLabel->SetVisible(false); + m_pSceneResidentsLabel->SetText(""); + } + + // Write the description, and add the total defense investment in this place so far as a lil stat Vector sceneSizeMeters = m_pSelectedScene->GetDimensions() / c_PPM; std::string sceneDimensions = "Site Dimensions: " + std::to_string(sceneSizeMeters.GetFloorIntX()) + " x " + std::to_string(sceneSizeMeters.GetFloorIntY()) + " meters"; std::string sceneInvestments = "Total base investments here: " + std::to_string(static_cast(m_pSelectedScene->GetTotalInvestment())) + "oz"; - m_pSceneInfoLabel->SetText(m_pSelectedScene->GetDescription() + "\n\n" + sceneDimensions + "\n\n" + sceneInvestments); - // Adjust the height of the text box and container so it fits the text to display - int newHeight = m_pSceneInfoLabel->ResizeHeightToFit(); - m_pSceneInfoPopup->Resize(m_pSceneInfoPopup->GetWidth(), newHeight + 96); - - // If during a player's round phase, show the budget slider and edit button - if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) - { - int metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; - int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); - - // Resize the collection box to fit the extra controls - m_pSceneInfoPopup->Resize(m_pSceneInfoPopup->GetWidth(), newHeight + 96); - m_pSceneBudgetLabel->SetVisible(true); - m_pSceneBudgetSlider->SetVisible(true); - m_pSceneBudgetBar->SetVisible(true); - - // Set up the slider limit bar - bool sceneOwnedByPlayer = m_pSelectedScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer); - int blockedWidth = std::floor((m_pSceneBudgetSlider->GetWidth() - 4) * g_MetaMan.GetBudgetedRatioOfPlayer(metaPlayer, m_pSelectedScene, sceneOwnedByPlayer)); - - if (blockedWidth > 0) - { - m_pSceneBudgetBar->SetVisible(true); - m_pSceneBudgetBar->SetPositionAbs(m_pSceneBudgetSlider->GetXPos() - 2 + m_pSceneBudgetSlider->GetWidth() - blockedWidth, m_pSceneBudgetSlider->GetYPos()); - m_pSceneBudgetBar->SetSize(blockedWidth, m_pSceneBudgetSlider->GetHeight()); - } - else - m_pSceneBudgetBar->SetVisible(false); - - // Make sure the slider can't go over the max allowed, as blocked by already budgeted funds - int maxVal = 100 - g_MetaMan.GetBudgetedRatioOfPlayer(metaPlayer, m_pSelectedScene, sceneOwnedByPlayer) * 100; - if (m_pSceneBudgetSlider->GetValue() > maxVal) - { -// TODO: Play some sound, blink the bar etc too - m_pSceneBudgetSlider->SetValue(maxVal); - } - - // If owned by this player, then set up base building controls - if (sceneOwnedByPlayer) - { - // Set the budget label as per the slider - int budget = floorf(((float)m_pSceneBudgetSlider->GetValue() / 100.0f) * g_MetaMan.m_Players[metaPlayer].GetFunds()); - std::snprintf(str, sizeof(str), "Build Budget: %d oz", budget); - m_pSceneBudgetLabel->SetText(str); - m_apMetaButton[SCANNOW]->SetVisible(false); - m_apMetaButton[SCANLATER]->SetVisible(false); - m_pScanInfoLabel->SetVisible(false); - m_apMetaButton[SCENEACTION]->SetVisible(false); - m_apMetaButton[DESIGNBASE]->SetVisible(true); - m_apMetaButton[DESIGNBASE]->SetText("Design Base"); - m_pSceneBudgetLabel->SetToolTip("Sets how much of your total funds will be budgeted toward building base defense blueprints on this site."); - m_pSceneBudgetSlider->SetToolTip("Sets how much of your total funds will be budgeted toward building base defense blueprints on this site."); - m_pAutoDesignCheckbox->SetVisible(true); - m_pAutoDesignCheckbox->SetCheck(m_pSelectedScene->GetAutoDesigned()); - } - // Not owned by this player's team, so set up the scanning controls - else - { - // Only update this if the scene has changed in any way - otherwise UI changes done elsewhere might be overridden - if (sceneChanged) - { - // Set the budget label as per the slider - int budget = std::floor(((float)m_pSceneBudgetSlider->GetValue() / 100.0f) * g_MetaMan.m_Players[metaPlayer].GetFunds()); - // Set the appropriate action message, depending on whether this is enemy owned, or merely unexplored - if (g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())) - { - std::snprintf(str, sizeof(str), "Attack Budget: %d oz", budget); - m_pSceneBudgetLabel->SetToolTip("Sets how much of your total funds will be budgeted toward exploring this site. Any gold that isn't used in the attack will return to your account afterward, but will also be tied up and can't be used for defense if someone else attacks any of your bases during the same turn. You can only attack one site per turn!"); - m_pSceneBudgetSlider->SetToolTip("Sets how much of your total funds will be budgeted toward exploring this site. Any gold that isn't used in the attack will return to your account afterward, but will also be tied up and can't be used for defense if someone else attacks any of your bases during the same turn. You can only attack one site per turn!"); - } - else - { - std::snprintf(str, sizeof(str), "Expedition Budget: %d oz", budget); - m_pSceneBudgetLabel->SetToolTip("Sets how much of your total funds will be budgeted toward attacking this site. Any gold that isn't used in the attack will return to your account afterward, but will also be tied up and can't be used for defense if someone else attacks any of your bases during the same turn. You can only explore one site per turn!"); - m_pSceneBudgetSlider->SetToolTip("Sets how much of your total funds will be budgeted toward attacking this site. Any gold that isn't used in the attack will return to your account afterward, but will also be tied up and can't be used for defense if someone else attacks any of your bases during the same turn. You can only explore one site per turn!"); - } - m_pSceneBudgetLabel->SetText(str); - - // A Scan is already scheduled for this scene - if (m_pSelectedScene->IsScanScheduled(team)) - { - // Hide buttons and show feedback message that scan has already been scheduled - m_apMetaButton[SCENEACTION]->SetVisible(false); - m_apMetaButton[DESIGNBASE]->SetVisible(false); - m_apMetaButton[SCANNOW]->SetVisible(false); - m_apMetaButton[SCANLATER]->SetVisible(false); - m_pScanInfoLabel->SetVisible(true); - m_pScanInfoLabel->SetText("- Orbital scan scheduled -"); - } - // Check to make sure we're even able to afford a new scan at all - else if (g_MetaMan.GetRemainingFundsOfPlayer(metaPlayer, 0, false, false) < SCANCOST) - { - // Hide buttons and show feedback message that scan has already been scheduled - m_apMetaButton[SCENEACTION]->SetVisible(false); - m_apMetaButton[DESIGNBASE]->SetVisible(false); - m_apMetaButton[SCANNOW]->SetVisible(false); - m_apMetaButton[SCANLATER]->SetVisible(false); - m_pScanInfoLabel->SetVisible(true); - std::snprintf(str, sizeof(str), "%d", SCANCOST); - m_pScanInfoLabel->SetText("Need " + std::string(str) + " oz left to Scan!"); - } - // Site can be scheduled to be scanned - else - { - // Show the scan button and hide the label - m_apMetaButton[SCANNOW]->SetVisible(false); - m_apMetaButton[SCANLATER]->SetVisible(false); - m_pScanInfoLabel->SetVisible(false); - m_apMetaButton[DESIGNBASE]->SetVisible(false); - m_apMetaButton[SCENEACTION]->SetVisible(true); - std::snprintf(str, sizeof(str), "%d", SCANCOST); - m_apMetaButton[SCENEACTION]->SetText("Scan Site (" + std::string(str) + " oz)"); - m_apMetaButton[SCENEACTION]->SetToolTip("Performs an orbital scan of this site, which will show everything that is on the surface, but will not be able to penetrate far into the ground."); - } - // Hide the auto check box - m_pAutoDesignCheckbox->SetVisible(false); - } - - // If the player doesn't have any brains left to deploy, then disable these attack controls instead - if (g_MetaMan.m_Players[metaPlayer].GetBrainPoolCount() <= 0) - { - m_pSceneBudgetLabel->SetText("- NO BRAINS TO DEPLOY -"); - m_pSceneBudgetLabel->SetToolTip("Since you do not have any more brains in your brain pool available for deployment, you can't attack this or any other site. Defend the sites you do own and hope you'll keep enough of them to win the game!"); - m_pSceneBudgetBar->SetToolTip("Since you do not have any more brains in your brain pool available for deployment, you can't attack this or any other site. Defend the sites you do own and hope you'll keep enough of them to win the game!"); - // Make the blockage bar be all over the place - m_pSceneBudgetBar->SetVisible(true); - m_pSceneBudgetBar->SetPositionAbs(m_pSceneBudgetSlider->GetXPos() - 2, m_pSceneBudgetSlider->GetYPos()); - m_pSceneBudgetBar->SetSize(std::floor((m_pSceneBudgetSlider->GetWidth() + 4)), m_pSceneBudgetSlider->GetHeight()); - m_pSceneBudgetSlider->SetVisible(false); - } - } - } - // Just showing scene info - else - { - // Resize the collection box to not show the player-relevant controls - m_pSceneInfoPopup->Resize(m_pSceneInfoPopup->GetWidth(), newHeight + 34); - m_pSceneBudgetLabel->SetVisible(false); - m_pSceneBudgetSlider->SetVisible(false); - m_pSceneBudgetBar->SetVisible(false); - m_apMetaButton[SCENEACTION]->SetVisible(false); - m_apMetaButton[DESIGNBASE]->SetVisible(false); - m_apMetaButton[SCANNOW]->SetVisible(false); - m_apMetaButton[SCANLATER]->SetVisible(false); - m_pAutoDesignCheckbox->SetVisible(false); - m_pScanInfoLabel->SetVisible(false); - } - - // Make sure the box doesn't go entirely outside of the screen - KeepBoxOnScreen(m_pSceneInfoPopup); - } - else - { - m_pSceneInfoPopup->SetVisible(false); - m_pSceneNameLabel->SetText(""); - m_pSceneInfoLabel->SetText(""); - } + m_pSceneInfoLabel->SetText(m_pSelectedScene->GetDescription() + "\n\n" + sceneDimensions + "\n\n" + sceneInvestments); + // Adjust the height of the text box and container so it fits the text to display + int newHeight = m_pSceneInfoLabel->ResizeHeightToFit(); + m_pSceneInfoPopup->Resize(m_pSceneInfoPopup->GetWidth(), newHeight + 96); + + // If during a player's round phase, show the budget slider and edit button + if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) { + int metaPlayer = g_MetaMan.m_GameState - MetaMan::PLAYER1TURN; + int team = g_MetaMan.m_Players[metaPlayer].GetTeam(); + + // Resize the collection box to fit the extra controls + m_pSceneInfoPopup->Resize(m_pSceneInfoPopup->GetWidth(), newHeight + 96); + m_pSceneBudgetLabel->SetVisible(true); + m_pSceneBudgetSlider->SetVisible(true); + m_pSceneBudgetBar->SetVisible(true); + + // Set up the slider limit bar + bool sceneOwnedByPlayer = m_pSelectedScene->GetTeamOwnership() == g_MetaMan.GetTeamOfPlayer(metaPlayer); + int blockedWidth = std::floor((m_pSceneBudgetSlider->GetWidth() - 4) * g_MetaMan.GetBudgetedRatioOfPlayer(metaPlayer, m_pSelectedScene, sceneOwnedByPlayer)); + + if (blockedWidth > 0) { + m_pSceneBudgetBar->SetVisible(true); + m_pSceneBudgetBar->SetPositionAbs(m_pSceneBudgetSlider->GetXPos() - 2 + m_pSceneBudgetSlider->GetWidth() - blockedWidth, m_pSceneBudgetSlider->GetYPos()); + m_pSceneBudgetBar->SetSize(blockedWidth, m_pSceneBudgetSlider->GetHeight()); + } else + m_pSceneBudgetBar->SetVisible(false); + + // Make sure the slider can't go over the max allowed, as blocked by already budgeted funds + int maxVal = 100 - g_MetaMan.GetBudgetedRatioOfPlayer(metaPlayer, m_pSelectedScene, sceneOwnedByPlayer) * 100; + if (m_pSceneBudgetSlider->GetValue() > maxVal) { + // TODO: Play some sound, blink the bar etc too + m_pSceneBudgetSlider->SetValue(maxVal); + } + + // If owned by this player, then set up base building controls + if (sceneOwnedByPlayer) { + // Set the budget label as per the slider + int budget = floorf(((float)m_pSceneBudgetSlider->GetValue() / 100.0f) * g_MetaMan.m_Players[metaPlayer].GetFunds()); + std::snprintf(str, sizeof(str), "Build Budget: %d oz", budget); + m_pSceneBudgetLabel->SetText(str); + m_apMetaButton[SCANNOW]->SetVisible(false); + m_apMetaButton[SCANLATER]->SetVisible(false); + m_pScanInfoLabel->SetVisible(false); + m_apMetaButton[SCENEACTION]->SetVisible(false); + m_apMetaButton[DESIGNBASE]->SetVisible(true); + m_apMetaButton[DESIGNBASE]->SetText("Design Base"); + m_pSceneBudgetLabel->SetToolTip("Sets how much of your total funds will be budgeted toward building base defense blueprints on this site."); + m_pSceneBudgetSlider->SetToolTip("Sets how much of your total funds will be budgeted toward building base defense blueprints on this site."); + m_pAutoDesignCheckbox->SetVisible(true); + m_pAutoDesignCheckbox->SetCheck(m_pSelectedScene->GetAutoDesigned()); + } + // Not owned by this player's team, so set up the scanning controls + else { + // Only update this if the scene has changed in any way - otherwise UI changes done elsewhere might be overridden + if (sceneChanged) { + // Set the budget label as per the slider + int budget = std::floor(((float)m_pSceneBudgetSlider->GetValue() / 100.0f) * g_MetaMan.m_Players[metaPlayer].GetFunds()); + // Set the appropriate action message, depending on whether this is enemy owned, or merely unexplored + if (g_MetaMan.IsActiveTeam(m_pSelectedScene->GetTeamOwnership())) { + std::snprintf(str, sizeof(str), "Attack Budget: %d oz", budget); + m_pSceneBudgetLabel->SetToolTip("Sets how much of your total funds will be budgeted toward exploring this site. Any gold that isn't used in the attack will return to your account afterward, but will also be tied up and can't be used for defense if someone else attacks any of your bases during the same turn. You can only attack one site per turn!"); + m_pSceneBudgetSlider->SetToolTip("Sets how much of your total funds will be budgeted toward exploring this site. Any gold that isn't used in the attack will return to your account afterward, but will also be tied up and can't be used for defense if someone else attacks any of your bases during the same turn. You can only attack one site per turn!"); + } else { + std::snprintf(str, sizeof(str), "Expedition Budget: %d oz", budget); + m_pSceneBudgetLabel->SetToolTip("Sets how much of your total funds will be budgeted toward attacking this site. Any gold that isn't used in the attack will return to your account afterward, but will also be tied up and can't be used for defense if someone else attacks any of your bases during the same turn. You can only explore one site per turn!"); + m_pSceneBudgetSlider->SetToolTip("Sets how much of your total funds will be budgeted toward attacking this site. Any gold that isn't used in the attack will return to your account afterward, but will also be tied up and can't be used for defense if someone else attacks any of your bases during the same turn. You can only explore one site per turn!"); + } + m_pSceneBudgetLabel->SetText(str); + + // A Scan is already scheduled for this scene + if (m_pSelectedScene->IsScanScheduled(team)) { + // Hide buttons and show feedback message that scan has already been scheduled + m_apMetaButton[SCENEACTION]->SetVisible(false); + m_apMetaButton[DESIGNBASE]->SetVisible(false); + m_apMetaButton[SCANNOW]->SetVisible(false); + m_apMetaButton[SCANLATER]->SetVisible(false); + m_pScanInfoLabel->SetVisible(true); + m_pScanInfoLabel->SetText("- Orbital scan scheduled -"); + } + // Check to make sure we're even able to afford a new scan at all + else if (g_MetaMan.GetRemainingFundsOfPlayer(metaPlayer, 0, false, false) < SCANCOST) { + // Hide buttons and show feedback message that scan has already been scheduled + m_apMetaButton[SCENEACTION]->SetVisible(false); + m_apMetaButton[DESIGNBASE]->SetVisible(false); + m_apMetaButton[SCANNOW]->SetVisible(false); + m_apMetaButton[SCANLATER]->SetVisible(false); + m_pScanInfoLabel->SetVisible(true); + std::snprintf(str, sizeof(str), "%d", SCANCOST); + m_pScanInfoLabel->SetText("Need " + std::string(str) + " oz left to Scan!"); + } + // Site can be scheduled to be scanned + else { + // Show the scan button and hide the label + m_apMetaButton[SCANNOW]->SetVisible(false); + m_apMetaButton[SCANLATER]->SetVisible(false); + m_pScanInfoLabel->SetVisible(false); + m_apMetaButton[DESIGNBASE]->SetVisible(false); + m_apMetaButton[SCENEACTION]->SetVisible(true); + std::snprintf(str, sizeof(str), "%d", SCANCOST); + m_apMetaButton[SCENEACTION]->SetText("Scan Site (" + std::string(str) + " oz)"); + m_apMetaButton[SCENEACTION]->SetToolTip("Performs an orbital scan of this site, which will show everything that is on the surface, but will not be able to penetrate far into the ground."); + } + // Hide the auto check box + m_pAutoDesignCheckbox->SetVisible(false); + } + + // If the player doesn't have any brains left to deploy, then disable these attack controls instead + if (g_MetaMan.m_Players[metaPlayer].GetBrainPoolCount() <= 0) { + m_pSceneBudgetLabel->SetText("- NO BRAINS TO DEPLOY -"); + m_pSceneBudgetLabel->SetToolTip("Since you do not have any more brains in your brain pool available for deployment, you can't attack this or any other site. Defend the sites you do own and hope you'll keep enough of them to win the game!"); + m_pSceneBudgetBar->SetToolTip("Since you do not have any more brains in your brain pool available for deployment, you can't attack this or any other site. Defend the sites you do own and hope you'll keep enough of them to win the game!"); + // Make the blockage bar be all over the place + m_pSceneBudgetBar->SetVisible(true); + m_pSceneBudgetBar->SetPositionAbs(m_pSceneBudgetSlider->GetXPos() - 2, m_pSceneBudgetSlider->GetYPos()); + m_pSceneBudgetBar->SetSize(std::floor((m_pSceneBudgetSlider->GetWidth() + 4)), m_pSceneBudgetSlider->GetHeight()); + m_pSceneBudgetSlider->SetVisible(false); + } + } + } + // Just showing scene info + else { + // Resize the collection box to not show the player-relevant controls + m_pSceneInfoPopup->Resize(m_pSceneInfoPopup->GetWidth(), newHeight + 34); + m_pSceneBudgetLabel->SetVisible(false); + m_pSceneBudgetSlider->SetVisible(false); + m_pSceneBudgetBar->SetVisible(false); + m_apMetaButton[SCENEACTION]->SetVisible(false); + m_apMetaButton[DESIGNBASE]->SetVisible(false); + m_apMetaButton[SCANNOW]->SetVisible(false); + m_apMetaButton[SCANLATER]->SetVisible(false); + m_pAutoDesignCheckbox->SetVisible(false); + m_pScanInfoLabel->SetVisible(false); + } + + // Make sure the box doesn't go entirely outside of the screen + KeepBoxOnScreen(m_pSceneInfoPopup); + } else { + m_pSceneInfoPopup->SetVisible(false); + m_pSceneNameLabel->SetText(""); + m_pSceneInfoLabel->SetText(""); + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -6172,16 +5564,12 @@ void MetagameGUI::UpdateScenesBox(bool sceneChanged) ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates AI skill sliders and labels for all players. -void MetagameGUI::UpdateAISkillSliders(int player) -{ - if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) - { +void MetagameGUI::UpdateAISkillSliders(int player) { + if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_apPlayerAISkillLabel[player]->SetText(Activity::GetAISkillString(m_apPlayerAISkillSlider[player]->GetValue())); - for (int otherPlayer = Players::PlayerOne; otherPlayer < Players::MaxPlayerCount; otherPlayer++) - { - if (otherPlayer != player && m_apPlayerTeamSelect[player]->GetSelectedIndex() == m_apPlayerTeamSelect[otherPlayer]->GetSelectedIndex()) - { + for (int otherPlayer = Players::PlayerOne; otherPlayer < Players::MaxPlayerCount; otherPlayer++) { + if (otherPlayer != player && m_apPlayerTeamSelect[player]->GetSelectedIndex() == m_apPlayerTeamSelect[otherPlayer]->GetSelectedIndex()) { m_apPlayerAISkillSlider[otherPlayer]->SetValue(m_apPlayerAISkillSlider[player]->GetValue()); m_apPlayerAISkillLabel[otherPlayer]->SetText(Activity::GetAISkillString(m_apPlayerAISkillSlider[otherPlayer]->GetValue())); } @@ -6189,23 +5577,21 @@ void MetagameGUI::UpdateAISkillSliders(int player) } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateGameSizeLabels ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the game size label of the new game dialog -void MetagameGUI::UpdateGameSizeLabels() -{ - // How many players do we have set to go - int playerCount = 0; - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - if (m_apPlayerControlButton[player]->GetText() != "None") - ++playerCount; +void MetagameGUI::UpdateGameSizeLabels() { + // How many players do we have set to go + int playerCount = 0; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) + if (m_apPlayerControlButton[player]->GetText() != "None") + ++playerCount; // How many scenes the game should end up with, according to the specified game size. // Note that it will never be all or none of all the available scenes! -// TODO: Hook these constants up to settings!! + // TODO: Hook these constants up to settings!! // How many scenes are there total const int totalCount = g_MetaMan.TotalScenePresets(); int minCount = std::max((playerCount * 3 / 2), 3); @@ -6215,433 +5601,388 @@ void MetagameGUI::UpdateGameSizeLabels() m_pSizeSlider->SetMaximum(std::max(totalCount * 7 / 10, minCount)); m_pSizeSlider->SetValueResolution(1); - char str[256]; - std::snprintf(str, sizeof(str), "Game Size: %d/%d sites", m_pSizeSlider->GetValue(), totalCount); - m_pSizeLabel->SetText(str); - - // How much starting gold does the slider yield - int startGold = STARTGOLDMIN + ((STARTGOLDMAX - STARTGOLDMIN) * (float)m_pGoldSlider->GetValue() / 100.0); - std::snprintf(str, sizeof(str), "Starting Gold: %c %d oz", -58, startGold); - m_pGoldLabel->SetText(str); - - // Set the length label also according to the game length slider - int brainCount = m_pLengthSlider->GetValue(); - brainCount = MAX(brainCount, 1); - std::snprintf(str, sizeof(str), "Game Length: %c%c%d starting brains", -48, -36, brainCount); - m_pLengthLabel->SetText(str); - - if (m_pDifficultySlider->GetValue() < Activity::CakeDifficulty) - m_pDifficultyLabel->SetText("Difficulty: Cake"); - else if (m_pDifficultySlider->GetValue() < Activity::EasyDifficulty) - m_pDifficultyLabel->SetText("Difficulty: Easy"); - else if (m_pDifficultySlider->GetValue() < Activity::MediumDifficulty) - m_pDifficultyLabel->SetText("Difficulty: Medium"); - else if (m_pDifficultySlider->GetValue() < Activity::HardDifficulty) - m_pDifficultyLabel->SetText("Difficulty: Hard"); - else if (m_pDifficultySlider->GetValue() < Activity::NutsDifficulty) - m_pDifficultyLabel->SetText("Difficulty: Nuts"); - else - m_pDifficultyLabel->SetText("Difficulty: Nuts!"); + char str[256]; + std::snprintf(str, sizeof(str), "Game Size: %d/%d sites", m_pSizeSlider->GetValue(), totalCount); + m_pSizeLabel->SetText(str); + + // How much starting gold does the slider yield + int startGold = STARTGOLDMIN + ((STARTGOLDMAX - STARTGOLDMIN) * (float)m_pGoldSlider->GetValue() / 100.0); + std::snprintf(str, sizeof(str), "Starting Gold: %c %d oz", -58, startGold); + m_pGoldLabel->SetText(str); + + // Set the length label also according to the game length slider + int brainCount = m_pLengthSlider->GetValue(); + brainCount = MAX(brainCount, 1); + std::snprintf(str, sizeof(str), "Game Length: %c%c%d starting brains", -48, -36, brainCount); + m_pLengthLabel->SetText(str); + + if (m_pDifficultySlider->GetValue() < Activity::CakeDifficulty) + m_pDifficultyLabel->SetText("Difficulty: Cake"); + else if (m_pDifficultySlider->GetValue() < Activity::EasyDifficulty) + m_pDifficultyLabel->SetText("Difficulty: Easy"); + else if (m_pDifficultySlider->GetValue() < Activity::MediumDifficulty) + m_pDifficultyLabel->SetText("Difficulty: Medium"); + else if (m_pDifficultySlider->GetValue() < Activity::HardDifficulty) + m_pDifficultyLabel->SetText("Difficulty: Hard"); + else if (m_pDifficultySlider->GetValue() < Activity::NutsDifficulty) + m_pDifficultyLabel->SetText("Difficulty: Nuts"); + else + m_pDifficultyLabel->SetText("Difficulty: Nuts!"); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePlayerSetup ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the player setup controls of the new game dialog -void MetagameGUI::UpdatePlayerSetup() -{ - int humanPlayers = 0; - int totalPlayers = 0; - const Icon *pTeamIcon = 0; - std::list teamList; - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) - { - if (m_apPlayerControlButton[player]->GetText() == "None") - { - m_apPlayerTeamSelect[player]->SetVisible(false); - m_apPlayerTechSelect[player]->SetVisible(false); - m_apPlayerHandicap[player]->SetVisible(false); - m_apPlayerNameBox[player]->SetVisible(false); +void MetagameGUI::UpdatePlayerSetup() { + int humanPlayers = 0; + int totalPlayers = 0; + const Icon* pTeamIcon = 0; + std::list teamList; + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + if (m_apPlayerControlButton[player]->GetText() == "None") { + m_apPlayerTeamSelect[player]->SetVisible(false); + m_apPlayerTechSelect[player]->SetVisible(false); + m_apPlayerHandicap[player]->SetVisible(false); + m_apPlayerNameBox[player]->SetVisible(false); m_apPlayerAISkillSlider[player]->SetVisible(false); m_apPlayerAISkillLabel[player]->SetVisible(false); - continue; - } - else - { - totalPlayers++; - if (m_apPlayerControlButton[player]->GetText() == "Human") - humanPlayers++; - m_apPlayerTeamSelect[player]->SetVisible(true); - m_apPlayerTechSelect[player]->SetVisible(true); - m_apPlayerHandicap[player]->SetVisible(true); - m_apPlayerNameBox[player]->SetVisible(true); + continue; + } else { + totalPlayers++; + if (m_apPlayerControlButton[player]->GetText() == "Human") + humanPlayers++; + m_apPlayerTeamSelect[player]->SetVisible(true); + m_apPlayerTechSelect[player]->SetVisible(true); + m_apPlayerHandicap[player]->SetVisible(true); + m_apPlayerNameBox[player]->SetVisible(true); m_apPlayerAISkillSlider[player]->SetVisible(true); m_apPlayerAISkillLabel[player]->SetVisible(true); - } - - // Count teams - if (m_apPlayerTeamSelect[player]->GetVisible()) - { - // Get the chosen team icon - if (m_apPlayerTeamSelect[player]->GetSelectedItem()) - pTeamIcon = dynamic_cast(m_apPlayerTeamSelect[player]->GetSelectedItem()->m_pEntity); - // Just get the first one if nothing is selected - else if (m_apPlayerTeamSelect[player]->GetCount() > 0) - pTeamIcon = dynamic_cast(m_apPlayerTeamSelect[player]->GetItem(0)->m_pEntity); - - if (pTeamIcon) - { - // See if the player is designated to a new team or one that has already been found - bool newTeam = true; - for (std::list::iterator itr = teamList.begin(); itr != teamList.end(); ++itr) - { - // Found existing team! - if (pTeamIcon->GetPresetName() == (*itr)->GetPresetName()) - { - newTeam = false; - break; - } - } - - // If we didn't find that the team we were designated already exists, then count it - if (newTeam) - teamList.push_back(pTeamIcon); - } - } - } - - // Hide/show the start game button depending on whether there's a valid player config right now - m_apMetaButton[STARTNEW]->SetVisible(humanPlayers >= 1 && totalPlayers >= 2 && teamList.size() >= 2); - - // Show a helpful error message if the requirements for starting a game aren't met - if (humanPlayers < 1) - m_pErrorLabel->SetText("Need 1 Human"); - else if (totalPlayers < 2) - m_pErrorLabel->SetText("Need 2 Players"); - else if (teamList.size() < 2) - m_pErrorLabel->SetText("Need 2 Teams"); - else - m_pErrorLabel->SetText(""); - - // Update the game size label since the number of players may have changed - UpdateGameSizeLabels(); -} + } + + // Count teams + if (m_apPlayerTeamSelect[player]->GetVisible()) { + // Get the chosen team icon + if (m_apPlayerTeamSelect[player]->GetSelectedItem()) + pTeamIcon = dynamic_cast(m_apPlayerTeamSelect[player]->GetSelectedItem()->m_pEntity); + // Just get the first one if nothing is selected + else if (m_apPlayerTeamSelect[player]->GetCount() > 0) + pTeamIcon = dynamic_cast(m_apPlayerTeamSelect[player]->GetItem(0)->m_pEntity); + + if (pTeamIcon) { + // See if the player is designated to a new team or one that has already been found + bool newTeam = true; + for (std::list::iterator itr = teamList.begin(); itr != teamList.end(); ++itr) { + // Found existing team! + if (pTeamIcon->GetPresetName() == (*itr)->GetPresetName()) { + newTeam = false; + break; + } + } + + // If we didn't find that the team we were designated already exists, then count it + if (newTeam) + teamList.push_back(pTeamIcon); + } + } + } + // Hide/show the start game button depending on whether there's a valid player config right now + m_apMetaButton[STARTNEW]->SetVisible(humanPlayers >= 1 && totalPlayers >= 2 && teamList.size() >= 2); + + // Show a helpful error message if the requirements for starting a game aren't met + if (humanPlayers < 1) + m_pErrorLabel->SetText("Need 1 Human"); + else if (totalPlayers < 2) + m_pErrorLabel->SetText("Need 2 Players"); + else if (teamList.size() < 2) + m_pErrorLabel->SetText("Need 2 Teams"); + else + m_pErrorLabel->SetText(""); + + // Update the game size label since the number of players may have changed + UpdateGameSizeLabels(); +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePlayerBars ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the floating player bars with current funds, flag, etc. -void MetagameGUI::UpdatePlayerBars() -{ - if (g_MetaMan.m_GameState >= MetaMan::NOGAME && g_MetaMan.m_GameState <= MetaMan::ENDROUND && !g_MetaMan.IsSuspended()) - { - int metaPlayer = 0; - char str[256]; - for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) - { -// m_apPlayerBox[metaPlayer]; -// m_apPlayerTeamBox[metaPlayer]; - - // Show only the active players this game - m_apPlayerBox[metaPlayer]->SetVisible(true); - - // Make sure their team flag icons are set up correctly - if (!m_apPlayerTeamBox[metaPlayer]->GetDrawImage()) - { - // Set the flag icons on the floating player bars - m_apPlayerTeamBox[metaPlayer]->SetDrawType(GUICollectionBox::Image); - m_apPlayerTeamBox[metaPlayer]->SetDrawImage(new AllegroBitmap(g_MetaMan.m_TeamIcons[(*mpItr).GetTeam()].GetBitmaps32()[0])); - } - - // Show funds of player if income lines are showing, or we are counting income/expenses somehow - if ((!m_PreTurn && metaPlayer == (g_MetaMan.m_GameState - MetaMan::PLAYER1TURN) && m_pSelectedScene) || - metaPlayer == m_ActivePlayerIncomeLines || g_MetaMan.m_GameState == MetaMan::COUNTINCOME || g_MetaMan.m_GameState == MetaMan::BUILDBASES || g_MetaMan.m_GameState == MetaMan::RUNACTIVITIES || g_MetaMan.m_GameState == MetaMan::ENDROUND) - { - std::snprintf(str, sizeof(str), "%c %.0f oz", -58, (*mpItr).m_Funds); -// std::snprintf(str, sizeof(str), "%cx%d %c %.0f oz", -48, (*mpItr).GetBrainPoolCount(), -58, (*mpItr).m_Funds); - m_apPlayerBarLabel[metaPlayer]->SetText(str); - m_apPlayerBarLabel[metaPlayer]->SetHAlignment(GUIFont::Right); - m_apPlayerBarLabel[metaPlayer]->SetToolTip("This player's total funds"); - } - // Show player name instead - else - { - // If the player is out of the game, show a skull before the name - if (g_MetaMan.GetTotalBrainCountOfPlayer(metaPlayer) <= 0) - std::snprintf(str, sizeof(str), "%c ", -39); - else - str[0] = 0; - - m_apPlayerBarLabel[metaPlayer]->SetText(std::string(str) + (*mpItr).GetName()); - m_apPlayerBarLabel[metaPlayer]->SetHAlignment(GUIFont::Left); - m_apPlayerBarLabel[metaPlayer]->SetToolTip(""); - } - - // Update the brain pool count display - m_apBrainPoolLabel[metaPlayer]->SetVisible(true); - // Put the brain label on the right or left of the player box based on which half of the screen it's on - if (m_apPlayerBox[metaPlayer]->GetXPos() > ((m_apScreenBox[ROOTBOX]->GetWidth() / 2) - (m_apPlayerBox[metaPlayer]->GetWidth() / 2))) - { - m_apBrainPoolLabel[metaPlayer]->SetPositionAbs(m_apPlayerBox[metaPlayer]->GetXPos() - m_apBrainPoolLabel[metaPlayer]->GetWidth() - 5, m_apPlayerBox[metaPlayer]->GetYPos()); - m_apBrainPoolLabel[metaPlayer]->SetHAlignment(GUIFont::Right); - } - else - { - m_apBrainPoolLabel[metaPlayer]->SetPositionAbs(m_apPlayerBox[metaPlayer]->GetXPos() + m_apPlayerBox[metaPlayer]->GetWidth() + 5, m_apPlayerBox[metaPlayer]->GetYPos()); - m_apBrainPoolLabel[metaPlayer]->SetHAlignment(GUIFont::Left); - } - // [Brain Icon] [X] Number - // The number to display is adjusted with whether any brains are out and about in the gui animations - int brainDisplayCount = (*mpItr).GetBrainPoolCount() - (*mpItr).GetBrainsInTransit(); - std::snprintf(str, sizeof(str), "%c%c%d", brainDisplayCount > 0 ? -48 : -25, -36, brainDisplayCount); - m_apBrainPoolLabel[metaPlayer]->SetText(str); - - // Animate any funds change indicator labels, make them float upward - if (!m_apFundsChangeTimer[metaPlayer].IsPastRealTimeLimit()) - { - // Animate downward if value is negative, upward if positive - int animDir = m_apFundsChangeLabel[metaPlayer]->GetText()[2] == '-' ? 1 : -1; - int heightChange = EaseOut(0, 25, m_apFundsChangeTimer[metaPlayer].RealTimeLimitProgress()); - // Use the height of the label to keep track of the animation progress over several frames - m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(m_apFundsChangeLabel[metaPlayer]->GetXPos(), m_apFundsChangeLabel[metaPlayer]->GetYPos() + animDir * (heightChange - m_apFundsChangeLabel[metaPlayer]->GetHeight() + 16)); - m_apFundsChangeLabel[metaPlayer]->Resize(m_apFundsChangeLabel[metaPlayer]->GetWidth(), 16 + heightChange); - m_apFundsChangeLabel[metaPlayer]->SetVisible(true); - } - // Done animating and showing the change label, make it invisible and disappear - else - { - m_apFundsChangeLabel[metaPlayer]->SetVisible(false); - m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); - } - - // Animate any brain change indicator labels, make them float upward - if (!m_apBrainsChangeTimer[metaPlayer].IsPastRealTimeLimit()) - { - // Animate downward if value is negative, upward if positive - int animDir = m_apBrainChangeLabel[metaPlayer]->GetText()[1] == '-' ? 1 : -1; - int heightChange = EaseOut(0, 25, m_apBrainsChangeTimer[metaPlayer].RealTimeLimitProgress()); - // Use the height of the label to keep track of the animation progress over several frames - m_apBrainChangeLabel[metaPlayer]->SetPositionAbs(m_apBrainChangeLabel[metaPlayer]->GetXPos(), m_apBrainChangeLabel[metaPlayer]->GetYPos() + animDir * (heightChange - m_apBrainChangeLabel[metaPlayer]->GetHeight() + 16)); - m_apBrainChangeLabel[metaPlayer]->Resize(m_apBrainChangeLabel[metaPlayer]->GetWidth(), 16 + heightChange); - m_apBrainChangeLabel[metaPlayer]->SetVisible(true); - } - // Done animating and showing the change label, make it invisible and disappear - else - { - m_apBrainChangeLabel[metaPlayer]->SetVisible(false); - m_apBrainChangeLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); - } - -/* This is now obsolete; we show the name inside the bar - // Update the player name labels above each floating bar, IF we're not drawing lines - if (metaPlayer != m_ActivePlayerIncomeLines) - { - m_apFundsChangeLabel[metaPlayer]->SetText((*mpItr).GetName()); - m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(m_apPlayerBox[metaPlayer]->GetXPos() + 5, m_apPlayerBox[metaPlayer]->GetYPos() - 15); - m_apFundsChangeLabel[metaPlayer]->SetVisible(true); - } - // Don't show the name label if we're drawing lines out of this player bar - else - { - - if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) - { - if () - m_apFundsChangeLabel[metaPlayer]->SetVisible(false); - } - else - } -*/ - metaPlayer++; - } - - // Always hide any remaining player labels - while (metaPlayer < Players::MaxPlayerCount) - { - m_apFundsChangeLabel[metaPlayer]->SetVisible(false); - m_apBrainChangeLabel[metaPlayer]->SetVisible(false); - metaPlayer++; - } - } -} +void MetagameGUI::UpdatePlayerBars() { + if (g_MetaMan.m_GameState >= MetaMan::NOGAME && g_MetaMan.m_GameState <= MetaMan::ENDROUND && !g_MetaMan.IsSuspended()) { + int metaPlayer = 0; + char str[256]; + for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) { + // m_apPlayerBox[metaPlayer]; + // m_apPlayerTeamBox[metaPlayer]; + + // Show only the active players this game + m_apPlayerBox[metaPlayer]->SetVisible(true); + + // Make sure their team flag icons are set up correctly + if (!m_apPlayerTeamBox[metaPlayer]->GetDrawImage()) { + // Set the flag icons on the floating player bars + m_apPlayerTeamBox[metaPlayer]->SetDrawType(GUICollectionBox::Image); + m_apPlayerTeamBox[metaPlayer]->SetDrawImage(new AllegroBitmap(g_MetaMan.m_TeamIcons[(*mpItr).GetTeam()].GetBitmaps32()[0])); + } + + // Show funds of player if income lines are showing, or we are counting income/expenses somehow + if ((!m_PreTurn && metaPlayer == (g_MetaMan.m_GameState - MetaMan::PLAYER1TURN) && m_pSelectedScene) || + metaPlayer == m_ActivePlayerIncomeLines || g_MetaMan.m_GameState == MetaMan::COUNTINCOME || g_MetaMan.m_GameState == MetaMan::BUILDBASES || g_MetaMan.m_GameState == MetaMan::RUNACTIVITIES || g_MetaMan.m_GameState == MetaMan::ENDROUND) { + std::snprintf(str, sizeof(str), "%c %.0f oz", -58, (*mpItr).m_Funds); + // std::snprintf(str, sizeof(str), "%cx%d %c %.0f oz", -48, (*mpItr).GetBrainPoolCount(), -58, (*mpItr).m_Funds); + m_apPlayerBarLabel[metaPlayer]->SetText(str); + m_apPlayerBarLabel[metaPlayer]->SetHAlignment(GUIFont::Right); + m_apPlayerBarLabel[metaPlayer]->SetToolTip("This player's total funds"); + } + // Show player name instead + else { + // If the player is out of the game, show a skull before the name + if (g_MetaMan.GetTotalBrainCountOfPlayer(metaPlayer) <= 0) + std::snprintf(str, sizeof(str), "%c ", -39); + else + str[0] = 0; + + m_apPlayerBarLabel[metaPlayer]->SetText(std::string(str) + (*mpItr).GetName()); + m_apPlayerBarLabel[metaPlayer]->SetHAlignment(GUIFont::Left); + m_apPlayerBarLabel[metaPlayer]->SetToolTip(""); + } + + // Update the brain pool count display + m_apBrainPoolLabel[metaPlayer]->SetVisible(true); + // Put the brain label on the right or left of the player box based on which half of the screen it's on + if (m_apPlayerBox[metaPlayer]->GetXPos() > ((m_apScreenBox[ROOTBOX]->GetWidth() / 2) - (m_apPlayerBox[metaPlayer]->GetWidth() / 2))) { + m_apBrainPoolLabel[metaPlayer]->SetPositionAbs(m_apPlayerBox[metaPlayer]->GetXPos() - m_apBrainPoolLabel[metaPlayer]->GetWidth() - 5, m_apPlayerBox[metaPlayer]->GetYPos()); + m_apBrainPoolLabel[metaPlayer]->SetHAlignment(GUIFont::Right); + } else { + m_apBrainPoolLabel[metaPlayer]->SetPositionAbs(m_apPlayerBox[metaPlayer]->GetXPos() + m_apPlayerBox[metaPlayer]->GetWidth() + 5, m_apPlayerBox[metaPlayer]->GetYPos()); + m_apBrainPoolLabel[metaPlayer]->SetHAlignment(GUIFont::Left); + } + // [Brain Icon] [X] Number + // The number to display is adjusted with whether any brains are out and about in the gui animations + int brainDisplayCount = (*mpItr).GetBrainPoolCount() - (*mpItr).GetBrainsInTransit(); + std::snprintf(str, sizeof(str), "%c%c%d", brainDisplayCount > 0 ? -48 : -25, -36, brainDisplayCount); + m_apBrainPoolLabel[metaPlayer]->SetText(str); + + // Animate any funds change indicator labels, make them float upward + if (!m_apFundsChangeTimer[metaPlayer].IsPastRealTimeLimit()) { + // Animate downward if value is negative, upward if positive + int animDir = m_apFundsChangeLabel[metaPlayer]->GetText()[2] == '-' ? 1 : -1; + int heightChange = EaseOut(0, 25, m_apFundsChangeTimer[metaPlayer].RealTimeLimitProgress()); + // Use the height of the label to keep track of the animation progress over several frames + m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(m_apFundsChangeLabel[metaPlayer]->GetXPos(), m_apFundsChangeLabel[metaPlayer]->GetYPos() + animDir * (heightChange - m_apFundsChangeLabel[metaPlayer]->GetHeight() + 16)); + m_apFundsChangeLabel[metaPlayer]->Resize(m_apFundsChangeLabel[metaPlayer]->GetWidth(), 16 + heightChange); + m_apFundsChangeLabel[metaPlayer]->SetVisible(true); + } + // Done animating and showing the change label, make it invisible and disappear + else { + m_apFundsChangeLabel[metaPlayer]->SetVisible(false); + m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); + } + + // Animate any brain change indicator labels, make them float upward + if (!m_apBrainsChangeTimer[metaPlayer].IsPastRealTimeLimit()) { + // Animate downward if value is negative, upward if positive + int animDir = m_apBrainChangeLabel[metaPlayer]->GetText()[1] == '-' ? 1 : -1; + int heightChange = EaseOut(0, 25, m_apBrainsChangeTimer[metaPlayer].RealTimeLimitProgress()); + // Use the height of the label to keep track of the animation progress over several frames + m_apBrainChangeLabel[metaPlayer]->SetPositionAbs(m_apBrainChangeLabel[metaPlayer]->GetXPos(), m_apBrainChangeLabel[metaPlayer]->GetYPos() + animDir * (heightChange - m_apBrainChangeLabel[metaPlayer]->GetHeight() + 16)); + m_apBrainChangeLabel[metaPlayer]->Resize(m_apBrainChangeLabel[metaPlayer]->GetWidth(), 16 + heightChange); + m_apBrainChangeLabel[metaPlayer]->SetVisible(true); + } + // Done animating and showing the change label, make it invisible and disappear + else { + m_apBrainChangeLabel[metaPlayer]->SetVisible(false); + m_apBrainChangeLabel[metaPlayer]->SetPositionAbs(m_apScreenBox[ROOTBOX]->GetWidth(), 0); + } + + /* This is now obsolete; we show the name inside the bar + // Update the player name labels above each floating bar, IF we're not drawing lines + if (metaPlayer != m_ActivePlayerIncomeLines) + { + m_apFundsChangeLabel[metaPlayer]->SetText((*mpItr).GetName()); + m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(m_apPlayerBox[metaPlayer]->GetXPos() + 5, m_apPlayerBox[metaPlayer]->GetYPos() - 15); + m_apFundsChangeLabel[metaPlayer]->SetVisible(true); + } + // Don't show the name label if we're drawing lines out of this player bar + else + { + + if (g_MetaMan.m_GameState >= MetaMan::PLAYER1TURN && g_MetaMan.m_GameState <= MetaMan::PLAYER4TURN) + { + if () + m_apFundsChangeLabel[metaPlayer]->SetVisible(false); + } + else + } + */ + metaPlayer++; + } + // Always hide any remaining player labels + while (metaPlayer < Players::MaxPlayerCount) { + m_apFundsChangeLabel[metaPlayer]->SetVisible(false); + m_apBrainChangeLabel[metaPlayer]->SetVisible(false); + metaPlayer++; + } + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateSiteHoverLabel ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the floating label over a planet site. -void MetagameGUI::UpdateSiteNameLabel(bool visible, std::string text, const Vector &location, float height) -{ - // Set up the hover label to appear over any hovered scene location - m_pScenePlanetLabel->SetVisible(visible); - if (visible) - { - m_pScenePlanetLabel->SetText(text); - m_pScenePlanetLabel->SetPositionAbs(m_PlanetCenter.m_X + location.m_X - (m_pScenePlanetLabel->GetWidth() / 2), - m_PlanetCenter.m_Y + location.m_Y - (m_pScenePlanetLabel->GetHeight() * 1.5 * height)); - - // Clamp it to within the screen.. only Y applies to the label though - int pad = 6; -/* - if (m_pScenePlanetLabel->GetXPos() < pad) - m_pScenePlanetLabel->SetPositionAbs(pad, m_pScenePlanetLabel->GetYPos()); - else if (m_pScenePlanetLabel->GetXPos() + m_pScenePlanetLabel->GetWidth() + pad >= m_RootBoxMaxWidth) - m_pScenePlanetLabel->SetPositionAbs(m_RootBoxMaxWidth - m_pScenePlanetLabel->GetWidth() - pad, m_pScenePlanetLabel->GetYPos()); -*/ - if (m_pScenePlanetLabel->GetYPos() < pad) - m_pScenePlanetLabel->SetPositionAbs(m_pScenePlanetLabel->GetXPos(), pad); - else if (m_pScenePlanetLabel->GetYPos() + m_pScenePlanetLabel->GetHeight() + pad >= g_WindowMan.GetResY()) - m_pScenePlanetLabel->SetPositionAbs(m_pScenePlanetLabel->GetXPos(), g_WindowMan.GetResY() - m_pScenePlanetLabel->GetHeight() - pad); - } +void MetagameGUI::UpdateSiteNameLabel(bool visible, std::string text, const Vector& location, float height) { + // Set up the hover label to appear over any hovered scene location + m_pScenePlanetLabel->SetVisible(visible); + if (visible) { + m_pScenePlanetLabel->SetText(text); + m_pScenePlanetLabel->SetPositionAbs(m_PlanetCenter.m_X + location.m_X - (m_pScenePlanetLabel->GetWidth() / 2), + m_PlanetCenter.m_Y + location.m_Y - (m_pScenePlanetLabel->GetHeight() * 1.5 * height)); + + // Clamp it to within the screen.. only Y applies to the label though + int pad = 6; + /* + if (m_pScenePlanetLabel->GetXPos() < pad) + m_pScenePlanetLabel->SetPositionAbs(pad, m_pScenePlanetLabel->GetYPos()); + else if (m_pScenePlanetLabel->GetXPos() + m_pScenePlanetLabel->GetWidth() + pad >= m_RootBoxMaxWidth) + m_pScenePlanetLabel->SetPositionAbs(m_RootBoxMaxWidth - m_pScenePlanetLabel->GetWidth() - pad, m_pScenePlanetLabel->GetYPos()); + */ + if (m_pScenePlanetLabel->GetYPos() < pad) + m_pScenePlanetLabel->SetPositionAbs(m_pScenePlanetLabel->GetXPos(), pad); + else if (m_pScenePlanetLabel->GetYPos() + m_pScenePlanetLabel->GetHeight() + pad >= g_WindowMan.GetResY()) + m_pScenePlanetLabel->SetPositionAbs(m_pScenePlanetLabel->GetXPos(), g_WindowMan.GetResY() - m_pScenePlanetLabel->GetHeight() - pad); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: PlayerTextIndication ////////////////////////////////////////////////////////////////////////////////////////// // Description: Starts an animation of a label showing a text string over a player bar -void MetagameGUI::PlayerTextIndication(int metaPlayer, std::string text, const Vector &screenPos, double animLengthMS) -{ - m_apFundsChangeLabel[metaPlayer]->SetText(text); - m_apFundsChangeLabel[metaPlayer]->SetHAlignment(GUIFont::Centre); - m_apFundsChangeLabel[metaPlayer]->SetVAlignment(GUIFont::Middle); - m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(screenPos.m_X - (m_apFundsChangeLabel[metaPlayer]->GetWidth() / 2), screenPos.m_Y - (m_apFundsChangeLabel[metaPlayer]->GetHeight() / 2)); - // The height is how the things get animated - m_apFundsChangeLabel[metaPlayer]->Resize(m_apFundsChangeLabel[metaPlayer]->GetWidth(), 16); - m_apFundsChangeLabel[metaPlayer]->SetVisible(true); - - // Start off the timer for the animation, which is updated in UpdatePlayerBars() - m_apFundsChangeTimer[metaPlayer].Reset(); - m_apFundsChangeTimer[metaPlayer].SetRealTimeLimitMS(animLengthMS); +void MetagameGUI::PlayerTextIndication(int metaPlayer, std::string text, const Vector& screenPos, double animLengthMS) { + m_apFundsChangeLabel[metaPlayer]->SetText(text); + m_apFundsChangeLabel[metaPlayer]->SetHAlignment(GUIFont::Centre); + m_apFundsChangeLabel[metaPlayer]->SetVAlignment(GUIFont::Middle); + m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(screenPos.m_X - (m_apFundsChangeLabel[metaPlayer]->GetWidth() / 2), screenPos.m_Y - (m_apFundsChangeLabel[metaPlayer]->GetHeight() / 2)); + // The height is how the things get animated + m_apFundsChangeLabel[metaPlayer]->Resize(m_apFundsChangeLabel[metaPlayer]->GetWidth(), 16); + m_apFundsChangeLabel[metaPlayer]->SetVisible(true); + + // Start off the timer for the animation, which is updated in UpdatePlayerBars() + m_apFundsChangeTimer[metaPlayer].Reset(); + m_apFundsChangeTimer[metaPlayer].SetRealTimeLimitMS(animLengthMS); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: FundsChangeIndication ////////////////////////////////////////////////////////////////////////////////////////// // Description: Starts an animation of a label showing funds changing for a player -void MetagameGUI::FundsChangeIndication(int metaPlayer, float change, const Vector &screenPos, double animLengthMS) -{ - char str[256]; - std::snprintf(str, sizeof(str), change >= 1.0 ? "%c +%.0f oz" : (change <= -1.0 ? "%c %.0f oz" : "%c %.0f oz"), -58, change); - m_apFundsChangeLabel[metaPlayer]->SetText(str); - m_apFundsChangeLabel[metaPlayer]->SetHAlignment(GUIFont::Right); - m_apFundsChangeLabel[metaPlayer]->SetVAlignment(GUIFont::Top); - m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(screenPos.m_X - m_apFundsChangeLabel[metaPlayer]->GetWidth(), screenPos.m_Y); - // The height is how the things get animated - m_apFundsChangeLabel[metaPlayer]->Resize(m_apFundsChangeLabel[metaPlayer]->GetWidth(), 16); - m_apFundsChangeLabel[metaPlayer]->SetVisible(true); - - // Start off the timer for the animation, which is updated in UpdatePlayerBars() - m_apFundsChangeTimer[metaPlayer].Reset(); - m_apFundsChangeTimer[metaPlayer].SetRealTimeLimitMS(animLengthMS); +void MetagameGUI::FundsChangeIndication(int metaPlayer, float change, const Vector& screenPos, double animLengthMS) { + char str[256]; + std::snprintf(str, sizeof(str), change >= 1.0 ? "%c +%.0f oz" : (change <= -1.0 ? "%c %.0f oz" : "%c %.0f oz"), -58, change); + m_apFundsChangeLabel[metaPlayer]->SetText(str); + m_apFundsChangeLabel[metaPlayer]->SetHAlignment(GUIFont::Right); + m_apFundsChangeLabel[metaPlayer]->SetVAlignment(GUIFont::Top); + m_apFundsChangeLabel[metaPlayer]->SetPositionAbs(screenPos.m_X - m_apFundsChangeLabel[metaPlayer]->GetWidth(), screenPos.m_Y); + // The height is how the things get animated + m_apFundsChangeLabel[metaPlayer]->Resize(m_apFundsChangeLabel[metaPlayer]->GetWidth(), 16); + m_apFundsChangeLabel[metaPlayer]->SetVisible(true); + + // Start off the timer for the animation, which is updated in UpdatePlayerBars() + m_apFundsChangeTimer[metaPlayer].Reset(); + m_apFundsChangeTimer[metaPlayer].SetRealTimeLimitMS(animLengthMS); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: BrainsChangeIndication ////////////////////////////////////////////////////////////////////////////////////////// // Description: Starts an animation of a label showing brains changing for a metaPlayer -void MetagameGUI::BrainsChangeIndication(int metaPlayer, int change, const Vector &screenPos, int fontAlignment, double animLengthMS) -{ - char str[256]; - // [Brain Icon] [X] Number - // The number to display is adjusted with whether any brains are out and about in the gui animations - std::snprintf(str, sizeof(str), change >= 0 ? "%c+%d" : "%c%d", -48, change); - m_apBrainChangeLabel[metaPlayer]->SetText(str); - - m_apBrainChangeLabel[metaPlayer]->SetHAlignment(fontAlignment); - m_apBrainChangeLabel[metaPlayer]->SetVAlignment(GUIFont::Top); - m_apBrainChangeLabel[metaPlayer]->SetPositionAbs(screenPos.m_X, screenPos.m_Y); - // The height is how the things get animated - m_apBrainChangeLabel[metaPlayer]->Resize(m_apBrainPoolLabel[metaPlayer]->GetWidth(), 16); - m_apBrainChangeLabel[metaPlayer]->SetVisible(true); - - // Start off the timer for the animation, which is updated in UpdatePlayerBars() - m_apBrainsChangeTimer[metaPlayer].Reset(); - m_apBrainsChangeTimer[metaPlayer].SetRealTimeLimitMS(animLengthMS); +void MetagameGUI::BrainsChangeIndication(int metaPlayer, int change, const Vector& screenPos, int fontAlignment, double animLengthMS) { + char str[256]; + // [Brain Icon] [X] Number + // The number to display is adjusted with whether any brains are out and about in the gui animations + std::snprintf(str, sizeof(str), change >= 0 ? "%c+%d" : "%c%d", -48, change); + m_apBrainChangeLabel[metaPlayer]->SetText(str); + + m_apBrainChangeLabel[metaPlayer]->SetHAlignment(fontAlignment); + m_apBrainChangeLabel[metaPlayer]->SetVAlignment(GUIFont::Top); + m_apBrainChangeLabel[metaPlayer]->SetPositionAbs(screenPos.m_X, screenPos.m_Y); + // The height is how the things get animated + m_apBrainChangeLabel[metaPlayer]->Resize(m_apBrainPoolLabel[metaPlayer]->GetWidth(), 16); + m_apBrainChangeLabel[metaPlayer]->SetVisible(true); + + // Start off the timer for the animation, which is updated in UpdatePlayerBars() + m_apBrainsChangeTimer[metaPlayer].Reset(); + m_apBrainsChangeTimer[metaPlayer].SetRealTimeLimitMS(animLengthMS); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: RemoveSiteLine ////////////////////////////////////////////////////////////////////////////////////////// // Description: Removes a specific index siteline out of a vector. -bool MetagameGUI::RemoveSiteLine(std::vector &lineList, int removeIndex) -{ - if (lineList.empty()) - return false; - - int index = 0; - bool removed = false; - for (std::vector::iterator slItr = lineList.begin(); slItr != lineList.end(); ++slItr, ++index) - { - if (index == removeIndex) - { - lineList.erase(slItr); - removed = true; - } - } - return removed; -} +bool MetagameGUI::RemoveSiteLine(std::vector& lineList, int removeIndex) { + if (lineList.empty()) + return false; + int index = 0; + bool removed = false; + for (std::vector::iterator slItr = lineList.begin(); slItr != lineList.end(); ++slItr, ++index) { + if (index == removeIndex) { + lineList.erase(slItr); + removed = true; + } + } + return removed; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetPlayerLineFunds ////////////////////////////////////////////////////////////////////////////////////////// // Description: Gets the total funds of all visible lines of a specific player. -float MetagameGUI::GetPlayerLineFunds(std::vector &lineList, int metaPlayer, bool onlyVisible) -{ - if (metaPlayer < Players::PlayerOne || metaPlayer >= g_MetaMan.m_Players.size()) - return 0; +float MetagameGUI::GetPlayerLineFunds(std::vector& lineList, int metaPlayer, bool onlyVisible) { + if (metaPlayer < Players::PlayerOne || metaPlayer >= g_MetaMan.m_Players.size()) + return 0; - // Figure out the total visible meter funds of this player - float totalFunds = 0; - for (std::vector::iterator slItr = lineList.begin(); slItr != lineList.end(); ++slItr) - if ((*slItr).m_Player == metaPlayer && (!onlyVisible || IsSiteLineVisible(*slItr))) - totalFunds += (*slItr).m_FundsAmount; + // Figure out the total visible meter funds of this player + float totalFunds = 0; + for (std::vector::iterator slItr = lineList.begin(); slItr != lineList.end(); ++slItr) + if ((*slItr).m_Player == metaPlayer && (!onlyVisible || IsSiteLineVisible(*slItr))) + totalFunds += (*slItr).m_FundsAmount; - return totalFunds; + return totalFunds; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdatePlayerLineRatios ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the site line meter ratios of a player based on their fund // amounts and visibilty. -void MetagameGUI::UpdatePlayerLineRatios(std::vector &lineList, int metaPlayer, bool onlyVisible, float total) -{ - if (metaPlayer < Players::PlayerOne || metaPlayer >= g_MetaMan.m_Players.size()) - return; - - // Figure out the total visible meter funds of this player, unless a total already specifically specified - float totalFunds = total > 0 ? total : GetPlayerLineFunds(lineList, metaPlayer, onlyVisible); - - // Now go through the visible ones and set their meter ratios appropriately - float meterStart = 0; - for (std::vector::iterator slItr = lineList.begin(); slItr != lineList.end(); ++slItr) - { - if ((*slItr).m_Player == metaPlayer && (!onlyVisible || IsSiteLineVisible(*slItr))) - { - (*slItr).m_StartMeterAt = meterStart; - (*slItr).m_MeterAmount = (*slItr).m_FundsAmount / totalFunds; - meterStart += (*slItr).m_MeterAmount; - } - } -} +void MetagameGUI::UpdatePlayerLineRatios(std::vector& lineList, int metaPlayer, bool onlyVisible, float total) { + if (metaPlayer < Players::PlayerOne || metaPlayer >= g_MetaMan.m_Players.size()) + return; + // Figure out the total visible meter funds of this player, unless a total already specifically specified + float totalFunds = total > 0 ? total : GetPlayerLineFunds(lineList, metaPlayer, onlyVisible); + + // Now go through the visible ones and set their meter ratios appropriately + float meterStart = 0; + for (std::vector::iterator slItr = lineList.begin(); slItr != lineList.end(); ++slItr) { + if ((*slItr).m_Player == metaPlayer && (!onlyVisible || IsSiteLineVisible(*slItr))) { + (*slItr).m_StartMeterAt = meterStart; + (*slItr).m_MeterAmount = (*slItr).m_FundsAmount / totalFunds; + meterStart += (*slItr).m_MeterAmount; + } + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: DrawGlowLine @@ -6649,162 +5990,151 @@ void MetagameGUI::UpdatePlayerLineRatios(std::vector &lineList, int me // Description: Draws a fancy thick flickering line to point out scene points on the // planet. -void MetagameGUI::DrawGlowLine(BITMAP *drawBitmap, const Vector &start, const Vector &end, int color) -{ +void MetagameGUI::DrawGlowLine(BITMAP* drawBitmap, const Vector& start, const Vector& end, int color) { int blendAmount = 210 + RandomNum(-15, 15); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - line(drawBitmap, start.m_X, start.m_Y, end.m_X, end.m_Y, color); -/* Looks like ass - // Draw the thickener lines thicker in the appropriate directions - if (fabs(end.m_X - start.m_X) > fabs(end.m_Y - start.m_Y)) - { - line(drawBitmap, start.m_X, start.m_Y + 1, end.m_X, end.m_Y + 1, color); - line(drawBitmap, start.m_X, start.m_Y - 1, end.m_X, end.m_Y - 1, color); - } - else - { - line(drawBitmap, start.m_X + 1, start.m_Y, end.m_X + 1, end.m_Y, color); - line(drawBitmap, start.m_X - 1, start.m_Y, end.m_X - 1, end.m_Y, color); - } -*/ + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + line(drawBitmap, start.m_X, start.m_Y, end.m_X, end.m_Y, color); + /* Looks like ass + // Draw the thickener lines thicker in the appropriate directions + if (fabs(end.m_X - start.m_X) > fabs(end.m_Y - start.m_Y)) + { + line(drawBitmap, start.m_X, start.m_Y + 1, end.m_X, end.m_Y + 1, color); + line(drawBitmap, start.m_X, start.m_Y - 1, end.m_X, end.m_Y - 1, color); + } + else + { + line(drawBitmap, start.m_X + 1, start.m_Y, end.m_X + 1, end.m_Y, color); + line(drawBitmap, start.m_X - 1, start.m_Y, end.m_X - 1, end.m_Y, color); + } + */ blendAmount = 45 + RandomNum(-25, 25); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - line(drawBitmap, start.m_X + 1, start.m_Y, end.m_X + 1, end.m_Y, color); - line(drawBitmap, start.m_X - 1, start.m_Y, end.m_X - 1, end.m_Y, color); - line(drawBitmap, start.m_X, start.m_Y + 1, end.m_X, end.m_Y + 1, color); - line(drawBitmap, start.m_X, start.m_Y - 1, end.m_X, end.m_Y - 1, color); + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + line(drawBitmap, start.m_X + 1, start.m_Y, end.m_X + 1, end.m_Y, color); + line(drawBitmap, start.m_X - 1, start.m_Y, end.m_X - 1, end.m_Y, color); + line(drawBitmap, start.m_X, start.m_Y + 1, end.m_X, end.m_Y + 1, color); + line(drawBitmap, start.m_X, start.m_Y - 1, end.m_X, end.m_Y - 1, color); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: DrawScreenLineToSitePoint ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws a fancy thick flickering lines to point out scene points on the // planet, FROM an arbitrary screen point. -bool MetagameGUI::DrawScreenLineToSitePoint(BITMAP *drawBitmap, - const Vector &screenPoint, - const Vector &planetPoint, +bool MetagameGUI::DrawScreenLineToSitePoint(BITMAP* drawBitmap, + const Vector& screenPoint, + const Vector& planetPoint, int color, int onlyFirstSegments, int onlyLastSegments, int channelHeight, float circleSize, - bool squareSite) const -{ - // No part of the line is visible with these params, so just quit - if (onlyFirstSegments == 0 || onlyLastSegments == 0) - return false; - // Detect disabling of the segment controls - if (onlyFirstSegments < 0) - onlyFirstSegments = 100; - if (onlyLastSegments < 0) - onlyLastSegments = 100; - - int totalSegments = 0; - int drawnFirstSegments = 0; - int lastSegmentsToDraw = 0; - int circleRadius = squareSite ? std::floor(6 * circleSize) : std::floor(8 * circleSize); - int chamferSize = CHAMFERSIZE; - Vector chamferPoint1; - Vector chamferPoint2; - Vector sitePos = m_PlanetCenter + planetPoint; - bool siteIsAbove = sitePos.m_Y < screenPoint.m_Y; - float yDirMult = siteIsAbove ? -1.0 : 1.0; - bool twoBends = fabs(sitePos.m_Y - screenPoint.m_Y) < (channelHeight - circleRadius); - bool noBends = (fabs(sitePos.m_X - screenPoint.m_X) < circleRadius);// && ((m_apPlayerBox[player]->GetWidth() * meterAmount * 0.5) >= fabs(sitePos.m_X - screenPoint.m_X)); - Vector firstBend(screenPoint.m_X, twoBends ? (screenPoint.m_Y + channelHeight * yDirMult) : sitePos.m_Y); - Vector secondBend(sitePos.m_X, firstBend.m_Y); - bool siteIsLeft = sitePos.m_X < screenPoint.m_X; - float xDirMult = siteIsLeft ? -1.0 : 1.0; - - // No bends, meaning the mid of the meter goes straight up/down into the site circle - if (noBends) - { - // How many possible segments there are total for this type of line: to site + circle - totalSegments = lastSegmentsToDraw = 1 + 1; - // Draw the line to the site - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, screenPoint + Vector(sitePos.m_X - screenPoint.m_X, 0), sitePos + Vector(0, (circleRadius + 1) * -yDirMult), color); - } - // Extra lines depending on whether there needs to be two bends due to the site being in the 'channel', ie next to the floating player bar - else if (twoBends) - { - // Cap the chamfer size on the second bend appropriately - chamferSize = MIN((firstBend - secondBend).GetMagnitude() - 15, chamferSize); - chamferSize = MIN((secondBend - sitePos).GetMagnitude() - circleRadius * 3, chamferSize); - // Snap the chamfer to not exist below a minimum size - chamferSize = (chamferSize < 15) ? 0 : chamferSize; - // No inverted chamfer - chamferSize = MAX(0, chamferSize); - chamferPoint1.SetXY(secondBend.m_X + chamferSize * -xDirMult, secondBend.m_Y); - chamferPoint2.SetXY(secondBend.m_X, secondBend.m_Y + chamferSize * -yDirMult); - // How many of the last segments to draw: to first bend + to second bend chamfer + chamfer + to site + circle - totalSegments = lastSegmentsToDraw = 1 + 1 + (int)(chamferSize > 0) + 1 + 1; - // Line to the first bend - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, screenPoint, firstBend, color); - // Line to the second bend, incl the chamfer - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, firstBend, chamferPoint1, color); - if (chamferSize > 0 && !(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, chamferPoint1, chamferPoint2, color); - // Line to the site - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, chamferPoint2, sitePos + Vector(0, (circleRadius + 1) * yDirMult), color); - } - // Just one bend - else - { - // Cap the chamfer size on the first bend appropriately - chamferSize = MIN((screenPoint - firstBend).GetMagnitude() - 15, chamferSize); - chamferSize = MIN((firstBend - sitePos).GetMagnitude() - circleRadius * 3, chamferSize); - // Snap the chamfer to not exist below a minimum size - chamferSize = (chamferSize < 15) ? 0 : chamferSize; - // No inverted chamfer - chamferSize = MAX(0, chamferSize); - chamferPoint1.SetXY(screenPoint.m_X, firstBend.m_Y + chamferSize * -yDirMult); - chamferPoint2.SetXY(firstBend.m_X + chamferSize * xDirMult, sitePos.m_Y); - // How many of the last segments to draw: to first bend chamfer + chamfer + to site + circle - totalSegments = lastSegmentsToDraw = 1 + (int)(chamferSize > 0) + 1 + 1; - // Draw line to the first bend, incl the chamfer - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, screenPoint, chamferPoint1, color); - if (chamferSize > 0 && !(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, chamferPoint1, chamferPoint2, color); - // Draw line to the site - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, chamferPoint2, sitePos + Vector((circleRadius + 1) * -xDirMult, 0), color); - } - - // Draw a circle around the site target - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - { + bool squareSite) const { + // No part of the line is visible with these params, so just quit + if (onlyFirstSegments == 0 || onlyLastSegments == 0) + return false; + // Detect disabling of the segment controls + if (onlyFirstSegments < 0) + onlyFirstSegments = 100; + if (onlyLastSegments < 0) + onlyLastSegments = 100; + + int totalSegments = 0; + int drawnFirstSegments = 0; + int lastSegmentsToDraw = 0; + int circleRadius = squareSite ? std::floor(6 * circleSize) : std::floor(8 * circleSize); + int chamferSize = CHAMFERSIZE; + Vector chamferPoint1; + Vector chamferPoint2; + Vector sitePos = m_PlanetCenter + planetPoint; + bool siteIsAbove = sitePos.m_Y < screenPoint.m_Y; + float yDirMult = siteIsAbove ? -1.0 : 1.0; + bool twoBends = fabs(sitePos.m_Y - screenPoint.m_Y) < (channelHeight - circleRadius); + bool noBends = (fabs(sitePos.m_X - screenPoint.m_X) < circleRadius); // && ((m_apPlayerBox[player]->GetWidth() * meterAmount * 0.5) >= fabs(sitePos.m_X - screenPoint.m_X)); + Vector firstBend(screenPoint.m_X, twoBends ? (screenPoint.m_Y + channelHeight * yDirMult) : sitePos.m_Y); + Vector secondBend(sitePos.m_X, firstBend.m_Y); + bool siteIsLeft = sitePos.m_X < screenPoint.m_X; + float xDirMult = siteIsLeft ? -1.0 : 1.0; + + // No bends, meaning the mid of the meter goes straight up/down into the site circle + if (noBends) { + // How many possible segments there are total for this type of line: to site + circle + totalSegments = lastSegmentsToDraw = 1 + 1; + // Draw the line to the site + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, screenPoint + Vector(sitePos.m_X - screenPoint.m_X, 0), sitePos + Vector(0, (circleRadius + 1) * -yDirMult), color); + } + // Extra lines depending on whether there needs to be two bends due to the site being in the 'channel', ie next to the floating player bar + else if (twoBends) { + // Cap the chamfer size on the second bend appropriately + chamferSize = MIN((firstBend - secondBend).GetMagnitude() - 15, chamferSize); + chamferSize = MIN((secondBend - sitePos).GetMagnitude() - circleRadius * 3, chamferSize); + // Snap the chamfer to not exist below a minimum size + chamferSize = (chamferSize < 15) ? 0 : chamferSize; + // No inverted chamfer + chamferSize = MAX(0, chamferSize); + chamferPoint1.SetXY(secondBend.m_X + chamferSize * -xDirMult, secondBend.m_Y); + chamferPoint2.SetXY(secondBend.m_X, secondBend.m_Y + chamferSize * -yDirMult); + // How many of the last segments to draw: to first bend + to second bend chamfer + chamfer + to site + circle + totalSegments = lastSegmentsToDraw = 1 + 1 + (int)(chamferSize > 0) + 1 + 1; + // Line to the first bend + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, screenPoint, firstBend, color); + // Line to the second bend, incl the chamfer + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, firstBend, chamferPoint1, color); + if (chamferSize > 0 && !(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, chamferPoint1, chamferPoint2, color); + // Line to the site + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, chamferPoint2, sitePos + Vector(0, (circleRadius + 1) * yDirMult), color); + } + // Just one bend + else { + // Cap the chamfer size on the first bend appropriately + chamferSize = MIN((screenPoint - firstBend).GetMagnitude() - 15, chamferSize); + chamferSize = MIN((firstBend - sitePos).GetMagnitude() - circleRadius * 3, chamferSize); + // Snap the chamfer to not exist below a minimum size + chamferSize = (chamferSize < 15) ? 0 : chamferSize; + // No inverted chamfer + chamferSize = MAX(0, chamferSize); + chamferPoint1.SetXY(screenPoint.m_X, firstBend.m_Y + chamferSize * -yDirMult); + chamferPoint2.SetXY(firstBend.m_X + chamferSize * xDirMult, sitePos.m_Y); + // How many of the last segments to draw: to first bend chamfer + chamfer + to site + circle + totalSegments = lastSegmentsToDraw = 1 + (int)(chamferSize > 0) + 1 + 1; + // Draw line to the first bend, incl the chamfer + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, screenPoint, chamferPoint1, color); + if (chamferSize > 0 && !(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, chamferPoint1, chamferPoint2, color); + // Draw line to the site + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, chamferPoint2, sitePos + Vector((circleRadius + 1) * -xDirMult, 0), color); + } + + // Draw a circle around the site target + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) { int blendAmount = 225 + RandomNum(-20, 20); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - - // If specified, draw a squareSite instead (with chamfered corners) - if (squareSite) - { - hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_X + circleRadius, color); - hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1 - 1, sitePos.m_X + circleRadius, color); - hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y + circleRadius, sitePos.m_X + circleRadius, color); - hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y + circleRadius + 1, sitePos.m_X + circleRadius, color); - vline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_Y + circleRadius, color); - vline(drawBitmap, sitePos.m_X - circleRadius - 1 - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_Y + circleRadius, color); - vline(drawBitmap, sitePos.m_X + circleRadius, sitePos.m_Y + circleRadius, sitePos.m_Y - circleRadius - 1, color); - vline(drawBitmap, sitePos.m_X + circleRadius + 1, sitePos.m_Y + circleRadius, sitePos.m_Y - circleRadius - 1, color); - } - else - { - circle(drawBitmap, sitePos.m_X, sitePos.m_Y, circleRadius, color); - circle(drawBitmap, sitePos.m_X, sitePos.m_Y, circleRadius - 1, color); - } - } - - return totalSegments <= onlyFirstSegments && totalSegments <= onlyLastSegments; -} + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + + // If specified, draw a squareSite instead (with chamfered corners) + if (squareSite) { + hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_X + circleRadius, color); + hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1 - 1, sitePos.m_X + circleRadius, color); + hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y + circleRadius, sitePos.m_X + circleRadius, color); + hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y + circleRadius + 1, sitePos.m_X + circleRadius, color); + vline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_Y + circleRadius, color); + vline(drawBitmap, sitePos.m_X - circleRadius - 1 - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_Y + circleRadius, color); + vline(drawBitmap, sitePos.m_X + circleRadius, sitePos.m_Y + circleRadius, sitePos.m_Y - circleRadius - 1, color); + vline(drawBitmap, sitePos.m_X + circleRadius + 1, sitePos.m_Y + circleRadius, sitePos.m_Y - circleRadius - 1, color); + } else { + circle(drawBitmap, sitePos.m_X, sitePos.m_Y, circleRadius, color); + circle(drawBitmap, sitePos.m_X, sitePos.m_Y, circleRadius - 1, color); + } + } + return totalSegments <= onlyFirstSegments && totalSegments <= onlyLastSegments; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: DrawPlayerLineToSitePoint @@ -6812,159 +6142,148 @@ bool MetagameGUI::DrawScreenLineToSitePoint(BITMAP *drawBitmap, // Description: Draws a fancy thick flickering lines to point out scene points on the // planet, FROM a floating player bar, showing a certain ratio. -bool MetagameGUI::DrawPlayerLineToSitePoint(BITMAP *drawBitmap, - int metaPlayer, - float startMeterAt, - float meterAmount, - const Vector &planetPoint, - int color, - int onlyFirstSegments, - int onlyLastSegments, - int channelHeight, - float circleSize, - bool squareSite, - bool drawMeterOverride) const -{ - RTEAssert(metaPlayer >= Players::PlayerOne && metaPlayer < Players::MaxPlayerCount, "Player out of bounds"); - // No part of the line is visible with these params, so just quit - if ((onlyFirstSegments == 0 || onlyLastSegments == 0) && !drawMeterOverride) - return false; - startMeterAt = MAX(0, startMeterAt); - startMeterAt = MIN(1.0, startMeterAt); - if ((startMeterAt + meterAmount) > 1.0) - meterAmount = 1.0 - startMeterAt; - // Detect disabling of the segment controls - if (onlyFirstSegments < 0) - onlyFirstSegments = 100; - if (onlyLastSegments < 0) - onlyLastSegments = 100; - - int totalSegments = 0; - int drawnFirstSegments = 0; - int lastSegmentsToDraw = 0; - int meterHeight = 5; - int circleRadius = squareSite ? std::floor(6 * circleSize) : std::floor(8 * circleSize); - int chamferSize = CHAMFERSIZE; - Vector chamferPoint1; - Vector chamferPoint2; - int boxMidY = m_apPlayerBox[metaPlayer]->GetYPos() + (m_apPlayerBox[metaPlayer]->GetHeight() / 2); - Vector sitePos = m_PlanetCenter + planetPoint; - bool siteIsAbove = sitePos.m_Y < boxMidY; - float yDirMult = siteIsAbove ? -1.0 : 1.0; - bool twoBends = fabs(sitePos.m_Y - boxMidY) < (channelHeight - circleRadius); - Vector startMeter(m_apPlayerBox[metaPlayer]->GetXPos() + (m_apPlayerBox[metaPlayer]->GetWidth() - 1) * startMeterAt + 1, m_apPlayerBox[metaPlayer]->GetYPos() + (siteIsAbove ? 0 : m_apPlayerBox[metaPlayer]->GetHeight())); - Vector endMeter(startMeter.m_X + MAX(0, (m_apPlayerBox[metaPlayer]->GetWidth() - 1) * meterAmount - 2), startMeter.m_Y); - Vector midMeter(startMeter.m_X + MAX(0, (m_apPlayerBox[metaPlayer]->GetWidth() - 1) * meterAmount * 0.5 - 1), startMeter.m_Y + meterHeight * yDirMult); - bool noBends = (fabs(sitePos.m_X - midMeter.m_X) < circleRadius) && ((m_apPlayerBox[metaPlayer]->GetWidth() * meterAmount * 0.5) >= fabs(sitePos.m_X - midMeter.m_X)); - Vector firstBend(midMeter.m_X, twoBends ? (boxMidY + channelHeight * yDirMult) : sitePos.m_Y); - Vector secondBend(sitePos.m_X, firstBend.m_Y); - bool siteIsLeft = sitePos.m_X < midMeter.m_X; - float xDirMult = siteIsLeft ? -1.0 : 1.0; - - // No bends, meaning the mid of the meter goes straight up/down into the site circle - if (noBends) - { - // How many possible segments there are total for this type of line: meter + to site + circle - totalSegments = lastSegmentsToDraw = 1 + 1 + 1; - // Draw the meter - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments) || drawMeterOverride) - { - DrawGlowLine(drawBitmap, startMeter, startMeter + Vector(0, meterHeight * yDirMult), color); - DrawGlowLine(drawBitmap, endMeter, endMeter + Vector(0, meterHeight * yDirMult), color); - DrawGlowLine(drawBitmap, startMeter + Vector(0, meterHeight * yDirMult), endMeter + Vector(0, meterHeight * yDirMult), color); - } - // Draw the line to the site - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, midMeter + Vector(sitePos.m_X - midMeter.m_X, 0), sitePos + Vector(0, (circleRadius + 1) * -yDirMult), color); - } - // Extra lines depending on whether there needs to be two bends due to the site being in the 'channel', ie next to teh floating metaPlayer bar - else if (twoBends) - { - // Cap the chamfer size on the second bend appropriately - chamferSize = MIN((firstBend - secondBend).GetMagnitude() - meterHeight * 3, chamferSize); - chamferSize = MIN((secondBend - sitePos).GetMagnitude() - circleRadius * 3, chamferSize); - // Snap the chamfer to not exist below a minimum size - chamferSize = (chamferSize < (meterHeight * 3)) ? 0 : chamferSize; - // No inverted chamfer - chamferSize = MAX(0, chamferSize); - chamferPoint1.SetXY(secondBend.m_X + chamferSize * -xDirMult, secondBend.m_Y); - chamferPoint2.SetXY(secondBend.m_X, secondBend.m_Y + chamferSize * -yDirMult); - // How many of the last segments to draw: meter + to first bend + to second bend chamfer + chamfer + to site + circle - totalSegments = lastSegmentsToDraw = 1 + 1 + 1 + (int)(chamferSize > 0) + 1 + 1; - // Draw the meter - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments) || drawMeterOverride) - { - DrawGlowLine(drawBitmap, startMeter, startMeter + Vector(0, meterHeight * yDirMult), color); - DrawGlowLine(drawBitmap, endMeter, endMeter + Vector(0, meterHeight * yDirMult), color); - DrawGlowLine(drawBitmap, startMeter + Vector(0, meterHeight * yDirMult), endMeter + Vector(0, meterHeight * yDirMult), color); - } - // Line to the first bend - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, midMeter, firstBend, color); - // Line to the second bend, incl the chamfer - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, firstBend, chamferPoint1, color); - if (chamferSize > 0 && !(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, chamferPoint1, chamferPoint2, color); - // Line to the site - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, chamferPoint2, sitePos + Vector(0, (circleRadius + 1) * yDirMult), color); - } - // Just one bend - else - { - // Cap the chamfer size on the first bend appropriately - chamferSize = MIN((midMeter - firstBend).GetMagnitude() - meterHeight * 3, chamferSize); - chamferSize = MIN((firstBend - sitePos).GetMagnitude() - circleRadius * 3, chamferSize); - // Snap the chamfer to not exist below a minimum size - chamferSize = (chamferSize < (meterHeight * 3)) ? 0 : chamferSize; - // No inverted chamfer - chamferSize = MAX(0, chamferSize); - chamferPoint1.SetXY(midMeter.m_X, firstBend.m_Y + chamferSize * -yDirMult); - chamferPoint2.SetXY(firstBend.m_X + chamferSize * xDirMult, sitePos.m_Y); - // How many of the last segments to draw: meter + to first bend chamfer + chamfer + to site + circle - totalSegments = lastSegmentsToDraw = 1 + 1 + (int)(chamferSize > 0) + 1 + 1; - // Draw the meter - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments) || drawMeterOverride) - { - DrawGlowLine(drawBitmap, startMeter, startMeter + Vector(0, meterHeight * yDirMult), color); - DrawGlowLine(drawBitmap, endMeter, endMeter + Vector(0, meterHeight * yDirMult), color); - DrawGlowLine(drawBitmap, startMeter + Vector(0, meterHeight * yDirMult), endMeter + Vector(0, meterHeight * yDirMult), color); - } - // Draw line to the first bend, incl the chamfer - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, midMeter, chamferPoint1, color); - if (chamferSize > 0 && !(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, chamferPoint1, chamferPoint2, color); - // Draw line to the site - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - DrawGlowLine(drawBitmap, chamferPoint2, sitePos + Vector((circleRadius + 1) * -xDirMult, 0), color); - } - - // Draw a circle around the site target - if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) - { - int blendAmount = 225 + RandomNum(-20, 20); - set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); - - // If specified, draw a squareSite instead (with chamfered corners) - if (squareSite) - { - hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_X + circleRadius, color); - hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1 - 1, sitePos.m_X + circleRadius, color); - hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y + circleRadius, sitePos.m_X + circleRadius, color); - hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y + circleRadius + 1, sitePos.m_X + circleRadius, color); - vline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_Y + circleRadius, color); - vline(drawBitmap, sitePos.m_X - circleRadius - 1 - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_Y + circleRadius, color); - vline(drawBitmap, sitePos.m_X + circleRadius, sitePos.m_Y + circleRadius, sitePos.m_Y - circleRadius - 1, color); - vline(drawBitmap, sitePos.m_X + circleRadius + 1, sitePos.m_Y + circleRadius, sitePos.m_Y - circleRadius - 1, color); - } - else - { - circle(drawBitmap, sitePos.m_X, sitePos.m_Y, circleRadius, color); - circle(drawBitmap, sitePos.m_X, sitePos.m_Y, circleRadius - 1, color); - } - } - - return totalSegments <= onlyFirstSegments && totalSegments <= onlyLastSegments; +bool MetagameGUI::DrawPlayerLineToSitePoint(BITMAP* drawBitmap, + int metaPlayer, + float startMeterAt, + float meterAmount, + const Vector& planetPoint, + int color, + int onlyFirstSegments, + int onlyLastSegments, + int channelHeight, + float circleSize, + bool squareSite, + bool drawMeterOverride) const { + RTEAssert(metaPlayer >= Players::PlayerOne && metaPlayer < Players::MaxPlayerCount, "Player out of bounds"); + // No part of the line is visible with these params, so just quit + if ((onlyFirstSegments == 0 || onlyLastSegments == 0) && !drawMeterOverride) + return false; + startMeterAt = MAX(0, startMeterAt); + startMeterAt = MIN(1.0, startMeterAt); + if ((startMeterAt + meterAmount) > 1.0) + meterAmount = 1.0 - startMeterAt; + // Detect disabling of the segment controls + if (onlyFirstSegments < 0) + onlyFirstSegments = 100; + if (onlyLastSegments < 0) + onlyLastSegments = 100; + + int totalSegments = 0; + int drawnFirstSegments = 0; + int lastSegmentsToDraw = 0; + int meterHeight = 5; + int circleRadius = squareSite ? std::floor(6 * circleSize) : std::floor(8 * circleSize); + int chamferSize = CHAMFERSIZE; + Vector chamferPoint1; + Vector chamferPoint2; + int boxMidY = m_apPlayerBox[metaPlayer]->GetYPos() + (m_apPlayerBox[metaPlayer]->GetHeight() / 2); + Vector sitePos = m_PlanetCenter + planetPoint; + bool siteIsAbove = sitePos.m_Y < boxMidY; + float yDirMult = siteIsAbove ? -1.0 : 1.0; + bool twoBends = fabs(sitePos.m_Y - boxMidY) < (channelHeight - circleRadius); + Vector startMeter(m_apPlayerBox[metaPlayer]->GetXPos() + (m_apPlayerBox[metaPlayer]->GetWidth() - 1) * startMeterAt + 1, m_apPlayerBox[metaPlayer]->GetYPos() + (siteIsAbove ? 0 : m_apPlayerBox[metaPlayer]->GetHeight())); + Vector endMeter(startMeter.m_X + MAX(0, (m_apPlayerBox[metaPlayer]->GetWidth() - 1) * meterAmount - 2), startMeter.m_Y); + Vector midMeter(startMeter.m_X + MAX(0, (m_apPlayerBox[metaPlayer]->GetWidth() - 1) * meterAmount * 0.5 - 1), startMeter.m_Y + meterHeight * yDirMult); + bool noBends = (fabs(sitePos.m_X - midMeter.m_X) < circleRadius) && ((m_apPlayerBox[metaPlayer]->GetWidth() * meterAmount * 0.5) >= fabs(sitePos.m_X - midMeter.m_X)); + Vector firstBend(midMeter.m_X, twoBends ? (boxMidY + channelHeight * yDirMult) : sitePos.m_Y); + Vector secondBend(sitePos.m_X, firstBend.m_Y); + bool siteIsLeft = sitePos.m_X < midMeter.m_X; + float xDirMult = siteIsLeft ? -1.0 : 1.0; + + // No bends, meaning the mid of the meter goes straight up/down into the site circle + if (noBends) { + // How many possible segments there are total for this type of line: meter + to site + circle + totalSegments = lastSegmentsToDraw = 1 + 1 + 1; + // Draw the meter + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments) || drawMeterOverride) { + DrawGlowLine(drawBitmap, startMeter, startMeter + Vector(0, meterHeight * yDirMult), color); + DrawGlowLine(drawBitmap, endMeter, endMeter + Vector(0, meterHeight * yDirMult), color); + DrawGlowLine(drawBitmap, startMeter + Vector(0, meterHeight * yDirMult), endMeter + Vector(0, meterHeight * yDirMult), color); + } + // Draw the line to the site + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, midMeter + Vector(sitePos.m_X - midMeter.m_X, 0), sitePos + Vector(0, (circleRadius + 1) * -yDirMult), color); + } + // Extra lines depending on whether there needs to be two bends due to the site being in the 'channel', ie next to teh floating metaPlayer bar + else if (twoBends) { + // Cap the chamfer size on the second bend appropriately + chamferSize = MIN((firstBend - secondBend).GetMagnitude() - meterHeight * 3, chamferSize); + chamferSize = MIN((secondBend - sitePos).GetMagnitude() - circleRadius * 3, chamferSize); + // Snap the chamfer to not exist below a minimum size + chamferSize = (chamferSize < (meterHeight * 3)) ? 0 : chamferSize; + // No inverted chamfer + chamferSize = MAX(0, chamferSize); + chamferPoint1.SetXY(secondBend.m_X + chamferSize * -xDirMult, secondBend.m_Y); + chamferPoint2.SetXY(secondBend.m_X, secondBend.m_Y + chamferSize * -yDirMult); + // How many of the last segments to draw: meter + to first bend + to second bend chamfer + chamfer + to site + circle + totalSegments = lastSegmentsToDraw = 1 + 1 + 1 + (int)(chamferSize > 0) + 1 + 1; + // Draw the meter + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments) || drawMeterOverride) { + DrawGlowLine(drawBitmap, startMeter, startMeter + Vector(0, meterHeight * yDirMult), color); + DrawGlowLine(drawBitmap, endMeter, endMeter + Vector(0, meterHeight * yDirMult), color); + DrawGlowLine(drawBitmap, startMeter + Vector(0, meterHeight * yDirMult), endMeter + Vector(0, meterHeight * yDirMult), color); + } + // Line to the first bend + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, midMeter, firstBend, color); + // Line to the second bend, incl the chamfer + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, firstBend, chamferPoint1, color); + if (chamferSize > 0 && !(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, chamferPoint1, chamferPoint2, color); + // Line to the site + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, chamferPoint2, sitePos + Vector(0, (circleRadius + 1) * yDirMult), color); + } + // Just one bend + else { + // Cap the chamfer size on the first bend appropriately + chamferSize = MIN((midMeter - firstBend).GetMagnitude() - meterHeight * 3, chamferSize); + chamferSize = MIN((firstBend - sitePos).GetMagnitude() - circleRadius * 3, chamferSize); + // Snap the chamfer to not exist below a minimum size + chamferSize = (chamferSize < (meterHeight * 3)) ? 0 : chamferSize; + // No inverted chamfer + chamferSize = MAX(0, chamferSize); + chamferPoint1.SetXY(midMeter.m_X, firstBend.m_Y + chamferSize * -yDirMult); + chamferPoint2.SetXY(firstBend.m_X + chamferSize * xDirMult, sitePos.m_Y); + // How many of the last segments to draw: meter + to first bend chamfer + chamfer + to site + circle + totalSegments = lastSegmentsToDraw = 1 + 1 + (int)(chamferSize > 0) + 1 + 1; + // Draw the meter + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments) || drawMeterOverride) { + DrawGlowLine(drawBitmap, startMeter, startMeter + Vector(0, meterHeight * yDirMult), color); + DrawGlowLine(drawBitmap, endMeter, endMeter + Vector(0, meterHeight * yDirMult), color); + DrawGlowLine(drawBitmap, startMeter + Vector(0, meterHeight * yDirMult), endMeter + Vector(0, meterHeight * yDirMult), color); + } + // Draw line to the first bend, incl the chamfer + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, midMeter, chamferPoint1, color); + if (chamferSize > 0 && !(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, chamferPoint1, chamferPoint2, color); + // Draw line to the site + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) + DrawGlowLine(drawBitmap, chamferPoint2, sitePos + Vector((circleRadius + 1) * -xDirMult, 0), color); + } + + // Draw a circle around the site target + if (!(drawnFirstSegments++ >= onlyFirstSegments || lastSegmentsToDraw-- > onlyLastSegments)) { + int blendAmount = 225 + RandomNum(-20, 20); + set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); + + // If specified, draw a squareSite instead (with chamfered corners) + if (squareSite) { + hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_X + circleRadius, color); + hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1 - 1, sitePos.m_X + circleRadius, color); + hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y + circleRadius, sitePos.m_X + circleRadius, color); + hline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y + circleRadius + 1, sitePos.m_X + circleRadius, color); + vline(drawBitmap, sitePos.m_X - circleRadius - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_Y + circleRadius, color); + vline(drawBitmap, sitePos.m_X - circleRadius - 1 - 1, sitePos.m_Y - circleRadius - 1, sitePos.m_Y + circleRadius, color); + vline(drawBitmap, sitePos.m_X + circleRadius, sitePos.m_Y + circleRadius, sitePos.m_Y - circleRadius - 1, color); + vline(drawBitmap, sitePos.m_X + circleRadius + 1, sitePos.m_Y + circleRadius, sitePos.m_Y - circleRadius - 1, color); + } else { + circle(drawBitmap, sitePos.m_X, sitePos.m_Y, circleRadius, color); + circle(drawBitmap, sitePos.m_X, sitePos.m_Y, circleRadius - 1, color); + } + } + + return totalSegments <= onlyFirstSegments && totalSegments <= onlyLastSegments; } diff --git a/Source/Menus/MetagameGUI.h b/Source/Menus/MetagameGUI.h index 45f0a52dc4..3972678476 100644 --- a/Source/Menus/MetagameGUI.h +++ b/Source/Menus/MetagameGUI.h @@ -10,7 +10,6 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -20,1265 +19,1190 @@ struct BITMAP; - -namespace RTE -{ - -class GUIControl; -class GUIScreen; -class GUIInput; -class GUIControlManager; -class GUICollectionBox; -class GUIComboBox; -class GUICheckbox; -class GUITab; -class GUIListBox; -class GUITextBox; -class GUIButton; -class GUILabel; -class GUISlider; -class Entity; -class Scene; -class Activity; -class GAScripted; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: MetagameGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A full menu system that represents the metagame GUI for Cortex Command -// Parent(s): Serializable. -// Class history: 8/22/2008 MetagameGUI Created. - -class MetagameGUI : public Serializable { - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - - SerializableClassNameGetter - SerializableOverrideMethods - -public: - - enum MenuScreens - { - ROOTBOX = 0, - NEWDIALOG, - LOADDIALOG, - SAVEDIALOG, - MENUDIALOG, - STATSDIALOG, - SCENEINFOBOX, - SCREENCOUNT - }; - - // For storing lines to be drawn upon draw time - struct SiteLine - { - int m_Player; - float m_StartMeterAt; - float m_MeterAmount; - float m_FundsAmount; - float m_FundsTarget; - Vector m_PlanetPoint; - std::string m_SiteName; - // NOT owned here - const Scene *m_pScene; - int m_Color; - int m_OnlyFirstSegments; - int m_OnlyLastSegments; - int m_ChannelHeight; - float m_CircleSize; - bool m_Square; - - SiteLine(int player, - float startMeterAt, - float meterAmount, - const Vector &planetPoint, - std::string siteName, - // Ownership NOT passed in - const Scene *pScene, - int color, - int onlyFirstSegments = -1, - int onlyLastSegments = -1, - int channelHeight = 60, - float circleSize = 1.0f, - bool squareSite = false) - { - m_Player = player; - m_StartMeterAt = startMeterAt; - m_MeterAmount = meterAmount; - m_PlanetPoint = planetPoint; - m_SiteName = siteName; - m_pScene = pScene; - m_Color = color; - m_OnlyFirstSegments = onlyFirstSegments; - m_OnlyLastSegments = onlyLastSegments; - m_ChannelHeight = channelHeight; - m_CircleSize = circleSize; - m_Square = squareSite; - } - }; - - // For storing info about target crosshairs over sites - struct SiteTarget - { - enum SiteTargetStyle - { - CROSSHAIRSSHRINK = 0, - CROSSHAIRSGROW, - CIRCLESHRINK, - CIRCLEGROW, - SQUARESHRINK, - SQUAREGROW, - STYLECOUNT - }; - - Vector m_CenterPos; - float m_AnimProgress; - int m_Style; - int m_Color; - double m_StartTime; - Timer m_AnimTimer; - - SiteTarget() { m_CenterPos.Reset(); m_AnimProgress = 0; m_Style = 0; m_Color = 0; m_StartTime = 0; m_AnimTimer.Reset(); } - - SiteTarget(const Vector ¢erPos, - float animProgress, - int style, - int color, - double startTime = 0) - { - m_CenterPos = centerPos; - m_AnimProgress = animProgress; - m_Style = style; - m_Color = color; - m_StartTime = startTime; - m_AnimTimer.Reset(); - } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this SiteTarget onto a bitmap of choice. - // Arguments: The bitmap to draw to. - // Return value: None. - - void Draw(BITMAP *drawBitmap) const; - - - }; - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: MetagameGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a MetagameGUI object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - MetagameGUI() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~MetagameGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a MetagameGUI object before deletion -// from system memory. -// Arguments: None. - - ~MetagameGUI() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MetagameGUI object ready for use. -// Arguments: A poitner to a Controller which will control this Menu. Ownership is -// NOT TRANSFERRED! -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(Controller *pController); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire MetagameGUI, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MetagameGUI object. -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGUIControlManager -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the GUIControlManager owned and used by this. -// Arguments: None. -// Return value: The GUIControlManager. Ownership is not transferred! - - GUIControlManager * GetGUIControlManager(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Enables or disables the menu. This will animate it in and out of view. -// Arguments: Whether to enable or disable the menu. -// Return value: None. - - void SetEnabled(bool enable = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the menu is enabled or not. -// Arguments: None. -// Return value: None. - - bool IsEnabled() { return m_MenuEnabled == ENABLED || m_MenuEnabled == ENABLING; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwitchToScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches to showing a specific menu screen/mode. -// Arguments: The MenuScreen to switch to. -// Return value: None. - - void SwitchToScreen(int newScreen); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRoundName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes a round number into a nice friendly text string. "ONE" for 1 etc -// Arguments: The number of the round to convert to a string. -// Return value: The friendly text string for that round. - - std::string GetRoundName(int roundNumber); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPlanetInfo -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where the planet is on the scren and its other data so the menu -// can overlay properly on it. -// Arguments: The absolute screen coordinates of the planet's center. -// The radius, in screen pixel units, of the planet. -// Return value: None. - - void SetPlanetInfo(const Vector ¢er, float radius) { m_PlanetCenter = center; m_PlanetRadius = radius; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SelectScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets a specific scene as the currently selected one. OWNERSHIP IS NOT TRANSFERRED! -// Arguments: The Scene to set as selected. Ownership is NOT transferred. -// Return value: None. - - void SelectScene(Scene *pScene); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SelectScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tries to select a specifically named scene on the metagame field. -// Arguments: The name of the Scene to try to find and select. -// Return value: Whether mission was found and selected. - - bool SelectScene(std::string sceneName); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ContinuePhase -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the player has decided to continue to next phase of the -// round of the current game. -// Arguments: None. -// Return value: Whether the player just decided to continue this frame - - bool ContinuePhase() { return m_ContinuePhase; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ActivityRestarted -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the player has decided to restart an activity this frame. -// All parameters for the new game has been fed into ActivityMan already. -// Arguments: None. -// Return value: Whether the activity should be restarted. - - bool ActivityRestarted() { return m_ActivityRestarted; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ActivityResumed -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the player has decided to resume the current activity. -// Arguments: None. -// Return value: Whether the activity should be resumed. - - bool ActivityResumed() { return m_ActivityResumed; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BackToMain -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the player has decided to go back to the main menu. -// Arguments: None. -// Return value: Whether we should go back to main menu. - - bool BackToMain() { return m_BackToMain; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: QuitProgram -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reports whether the player has decided to quit the program. -// Arguments: None. -// Return value: Whether the program has been commanded to shit down by the user. - - bool QuitProgram() { return m_Quit; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StartNewGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attempts to start a new Metagame using the settings set in the -// New Game dialog box. -// Arguments: None. -// Return value: Whether the game was able to be set up with the current settings. - - bool StartNewGame(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attempts to load a Metagame from disk using the settings set in the -// Load Game dialog box. -// Arguments: None. -// Return value: Whether the game was able to be loaded with the current settings. - - bool LoadGame(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Just saves out the MetaGame and all its Scene data as-is to a specific -// location. -// Arguments: The name of the save game to create or overwrite here. -// The full path of the ini that we want to save the Metagame state to. -// Whether to load all the scene data that is on disk first so it will -// be re-saved to the new location here. -// Return value: Whether the game was able to be saved there. - - bool SaveGame(std::string saveName, std::string savePath, bool resaveSceneData = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveGameFromDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attempts to save a Metagame to disk using the settings set in the -// Save Game dialog box. -// Arguments: None. -// Return value: Whether the game was able to be saved with the current settings. - - bool SaveGameFromDialog(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu -// Arguments: The bitmap to draw on. -// Return value: None. - - void Draw(BITMAP *drawBitmap); - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetToStartNewGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets internal state of GUI to show 'Start new campaign' screen -// Arguments: None. -// Return value: None. - void SetToStartNewGame(); - - /// - /// Sets where the station is located on the planet orbit. - /// - /// The position of the station on the planet orbit. - void SetStationOrbitPos(const Vector &newStationPos) { m_StationPosOnOrbit = newStationPos; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MetaMan object ready for use -> this is acutally a light -// and not complete version of the one that takes a controller. -// It is only for init after reading stuff from file as a Serializable. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateInput -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the user input processing. -// Arguments: None. -// Return value: None. - - void UpdateInput(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HideAllScreens -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Hides all menu screens, so one can easily be unhidden and shown only. -// Arguments: None. -// Return value: None. - - void HideAllScreens(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: KeepBoxOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes sure a specific box doesn't end up moved completely off-screen. -// Arguments: The GUICollectionBox to adjust, if necessary. -// The amount of margin to allow the box to stay within. If negative, -// the width/height of the box itself are used. -// Return value: None. - - void KeepBoxOnScreen(GUICollectionBox *pBox, int margin = 10); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ChangeAnimMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Changes the animation mode -// Arguments: None. -// Return value: None. - - void ChangeAnimMode(int newMode) { m_AnimMode = newMode; m_AnimModeChange = true; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: NewAnimMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks for and switches off the new animation mode flag -// Arguments: None. -// Return value: None. - - bool NewAnimMode() { bool changed = m_AnimModeChange; m_AnimModeChange = false; return changed; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CompletedActivity -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Handles what happens after an Activity within the Metagame was -// run and completed fully. -// Arguments: None. -// Return value: None. - - void CompletedActivity(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AutoResolveOffensive -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Automatically resolves an offensive fight without actually launching -// and going through an Activity. Will randomly determine who won and -// what the consequences are. -// Arguments: The Offsenive Activity to resolve and manipulate accordingly. OWNERSHIP IS NOT TRANSFERRED! -// The Scene this Offensive is supposed to take place on. OWNERSHIP IS NOT TRANSFERRED! -// Whether to check the validity of all players based on whether they -// have brains remaining alive. If false, all active players will be -// instead be flagged as having had brains at some point. -// Return value: Whether the ownership of the relevant Scene changed due to this. - - bool AutoResolveOffensive(GAScripted *pOffensive, Scene *pScene, bool brainCheck = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateSiteRevealing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New Site Revealing animation -// Arguments: None. -// Return value: None. - - void UpdateSiteRevealing(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateSiteChangeAnim -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates team ownership change animations, if any. -// Arguments: None. -// Return value: None. - - void UpdateSiteChangeAnim(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateIncomeCounting -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Count Income animation -// Arguments: Whether to just set up the lines and funds as if we had a new round. Also skips changing funds to avoid an income/cost duplication glitch when saving a game at the start of a round. -// Return value: None. - - void UpdateIncomeCounting(bool initOverride = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateHumanPlayerTurn -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates a human player's turn. -// Arguments: Which metaplayer' turn it is -// Return value: None. - - void UpdateHumanPlayerTurn(int metaPlayer); - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateBaseBuilding -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Base Building animation -// Arguments: None. -// Return value: None. - - void UpdateBaseBuilding(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetupOffensives -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets up the Activities that represent all the offensive actions of -// the teams this round. -// Arguments: None. -// Return value: None. - - void SetupOffensives(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateOffensives -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the offensive actions animation -// Arguments: None. -// Return value: None. - - void UpdateOffensives(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FinalizeOffensive -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Finishes one battle in the UpdateOffensives and moves onto the next. -// Arguments: None. -// Return value: If there are any more battles after the one that was just finalized. - - bool FinalizeOffensive(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResetBattleInfo -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Hides and resets all battle info labels and panels -// Arguments: None. -// Return value: None. - - void ResetBattleInfo(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateBattleQuads -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates which player get placed in which quad around a fought-over -// site. -// Arguments: The absolutel screen position of the target site. -// Return value: None. - - void UpdateBattleQuads(Vector targetPos); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePreBattleAttackers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the animation and display of the info for the current -// offensive battle's attackers being next in line for this round. -// Arguments: The normalized scalar which will set the desired progress of the -// total animation. 0 means nothing is shown, because it is at the start -// of the animation where brain icons start moving around. -// Return value: None. - - void UpdatePreBattleAttackers(float progress); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePreBattleDefenders -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the animation and display of the info for the current -// offensive battle's defenders being next in line for this round. -// Arguments: The normalized scalar which will set the desired progress of the -// total animation. 0 means nothing is shown, because it is at the start -// of the animation where brain icons start moving around. -// Return value: None. - - void UpdatePreBattleDefenders(float progress); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePostBattleRetreaters -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the animation and display of the info for the current -// offensive battle's retreating brains going back to their pools -// Arguments: The normalized scalar which will set the desired progress of the -// total animation. 0 means nothing has happened, because it is at the -// start of the animation where brain icons start moving around. -// Return value: None. - - void UpdatePostBattleRetreaters(float progress); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePostBattleResidents -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the animation and display of the info for the current done -// offensive battle's winning brains going back into the site. -// Arguments: The normalized scalar which will set the desired progress of the -// total animation. 0 means nothing has happened, because it is at the -// start of the animation where brain icons start moving around. -// Return value: None. - - void UpdatePostBattleResidents(float progress); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlayerActionLines -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the action lines as per what the player has chosen to do -// during the current turn so far. -// Arguments: The metaplayer we want to update the lines for. -// Also add a line for the unallocated funds the player hasn't used for -// anyhting else yet. - NOPE, NOT IMPL YET -// Return value: The meter start that remains after all the lines are added. - - float UpdatePlayerActionLines(int player);//, bool addUnallocated = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateScenesBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the contents of the scene selection box. -// Arguments: Whether the selected has changed and should refresh the box completely. -// Return value: None. - - void UpdateScenesBox(bool sceneChanged = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateGameSizeLabels -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the game size labels of the new game dialog -// Arguments: None. -// Return value: None. - - void UpdateGameSizeLabels(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateAISkillSliders -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates AI skill sliders and labels for all players. -// Arguments: Which player's slider was changed. -// Return value: None. - - void UpdateAISkillSliders(int player); - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlayerSetup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the player setup controls of the new game dialog -// Arguments: None. -// Return value: None. - - void UpdatePlayerSetup(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlayerBars -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the floating player bars with current funds, flag, etc. -// Arguments: None. -// Return value: None. - - void UpdatePlayerBars(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateSiteHoverLabel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the floating label over a planet site. -// Arguments: Label is visible. -// Text to show above the location. -// The location in planetary coords. -// How high above the location to show the text, adjustment from a good default. -// Return value: None. - - void UpdateSiteNameLabel(bool visible, std::string text = "", const Vector &location = Vector(), float height = 1.0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PlayerTextIndication -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Starts an animation of a label showing a text string over a player bar -// Arguments: Which player the indication is relevant to -// The string to display. -// Where, in screen coords the change should be indicated. The CENTER of -// the floating label will line up with this pos. -// How long, in MS, that the animation should linger -// Return value: None. - - void PlayerTextIndication(int player, std::string text, const Vector &screenPos, double animLengthMS); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FundsChangeIndication -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Starts an animation of a label showing funds changing for a player -// Arguments: Which player the change is relevant to -// The change in funds to display. -// Where, in screen coords the change should be indicated. The RIGHTMOST -// UPPER CORNER of the floating label will line up with this pos. -// How long, in MS, that the animation should linger -// Return value: None. - - void FundsChangeIndication(int player, float change, const Vector &screenPos, double animLengthMS); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BrainsChangeIndication -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Starts an animation of a label showing brains changing for a player -// Arguments: Which player the change is relevant to -// The change in brains to display. -// Where, in screen coords the change should be indicated. The LEFTMOST -// UPPER CORNER of the floating label will line up with this pos. -// How long, in MS, that the animation should linger -// The horizontal font alignment of the change. -// Return value: None. - - void BrainsChangeIndication(int player, int change, const Vector &screenPos, int fontAlignment, double animLengthMS); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsSiteLineVisible -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tells whether a SiteLine can be considered visible. -// Arguments: The SiteLine to check. -// Return value: Whether visible. - - bool IsSiteLineVisible(SiteLine &sl) { return sl.m_OnlyFirstSegments != 0 && sl.m_OnlyLastSegments != 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveSiteLine -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specific index siteline out of a vector. -// Arguments: The vector of SiteLine:s to remove from. -// The index of the siteline to remove -// Return value: Whether the line was removed or not. - - bool RemoveSiteLine(std::vector &lineList, int removeIndex); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlayerLineFunds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total funds of all visible lines of a specific player. -// Arguments: A vector with SiteLine:s which may contain other players' lines too. -// Which player's lines to check for. -// Only count the funds of visible lines. -// Return value: The total funds, in oz. - - float GetPlayerLineFunds(std::vector &lineList, int player, bool onlyVisible = true); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlayerLineRatios -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the site line meter ratios of a player based on their fund -// amounts and visibilty. -// Arguments: A vector with SiteLine:s which may contain other players' lines too. -// Which player's lines to update. -// Whetehr to only care about visible lines. -// The total funds to be calculating the ratios against. If negative, -// the total line amounts is what will be used. -// Return value: None. - - void UpdatePlayerLineRatios(std::vector &lineList, int player, bool onlyVisible = true, float total = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGlowLine -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a fancy thick flickering line to point out scene points on the -// planet. -// Arguments: The bitmap to draw to. -// The start and end Vector:s for the line, in absolute screen coordinates. -// The color to draw the line in. Use makecol(r, g, b) to create the color -// Return value: None. - - static void DrawGlowLine(BITMAP *drawBitmap, const Vector &start, const Vector &end, int color); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawScreenLineToSitePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a fancy thick flickering lines to point out scene points on the -// planet, FROM an arbitrary screen point. -// Arguments: The bitmap to draw to. -// The point on the screen to point from, in screen coordinates. -// The point on the planet to point at, in planet coordinates. -// The color of the line. -// How many of the segments from the start (the start of the line) to draw. -// How many of the segments from the end (site circle) to draw. -1 is all. -// The height of the 'channel' above and below that the lines will go around -// the player bar. -// What size factor from 'normal' should the circle's diameter be drawn. -// Return value: Whether all segments of the line were drawn with the segment params. - - bool DrawScreenLineToSitePoint(BITMAP *drawBitmap, - const Vector &screenPoint, - const Vector &planetPoint, - int color, - int onlyFirstSegments = -1, - int onlyLastSegments = -1, - int channelHeight = 80, - float circleSize = 1.0, - bool squareSite = false) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawPlayerLineToSitePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a fancy thick flickering lines to point out scene points on the -// planet, FROM a floating player bar, showing a certain ratio. -// Arguments: The bitmap to draw to. -// The player whose floating bar we draw from. -// The start percentage of the meter to indicate, from 0 to 1.0 -// The actual percentage of the meter to indicate, from 0 to 1.0 -// The point on the planet to point at, in planet coordinates. -// The color of the line. -// How many of the segments from the start (the player floater) to draw. -// How many of the segments from the end (site circle) to draw. -1 is all. -// The height of the 'channel' above and below that the lines will go around -// the player bar. -// What size factor from 'normal' should the circle's diameter be drawn. -// Whether the circle should instead be a squareSite! -// Whether to draw the meter (FirstSegment == 1) no matter what -// Return value: Whether all segments of the line were drawn with the segment params. - - bool DrawPlayerLineToSitePoint(BITMAP *drawBitmap, - int player, - float startMeterAt, - float meterAmount, - const Vector &planetPoint, - int color, - int onlyFirstSegments = -1, - int onlyLastSegments = -1, - int channelHeight = 60, - float circleSize = 1.0, - bool squareSite = false, - bool drawMeterOverride = false) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawPlayerLineToSitePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a fancy thick flickering lines to point out scene points on the -// planet, FROM a floating player bar, showing a certain ratio. -// Arguments: The bitmap to draw to. -// The SiteLine struct with all the parameters this needs. -// Whether to draw the meter (FirstSegment == 1) no matter what -// Return value: Whether all segments of the line were drawn with the segment params. - - bool DrawPlayerLineToSitePoint(BITMAP *drawBitmap, const SiteLine &sl, bool drawMeterOverride = false) const { return DrawPlayerLineToSitePoint(drawBitmap, sl.m_Player, sl.m_StartMeterAt, sl.m_MeterAmount, sl.m_PlanetPoint.GetFloored(), sl.m_Color, sl.m_OnlyFirstSegments, sl.m_OnlyLastSegments, sl.m_ChannelHeight, sl.m_CircleSize, sl.m_Square, drawMeterOverride); } - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: MoveLocationsIntoTheScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Moves any locations closer to the ceonter of the planet if they were left out -// of the screen due to low display resolution. -// Arguments: None. -// Return value: None. - - void MoveLocationsIntoTheScreen(); - - - enum MenuEnabled - { - ENABLING = 0, - ENABLED, - DISABLING, - DISABLED - }; - - enum MetaButtons - { - CONFIRM = 0, - P1CONTROL, - P2CONTROL, - P3CONTROL, - P4CONTROL, - STARTNEW, - LOADNOW, - SAVENOW, - CONTINUE, - SCENEACTION, - DESIGNBASE, - SCANNOW, - SCANLATER, - METABUTTONCOUNT - }; - - enum BlinkMode - { - NOBLINK = 0, - NOFUNDS, - NOCRAFT, - BLINKMODECOUNT - }; - - enum LineAnimMode - { - PAUSEANIM = 0, - TARGETZEROING, - BLINKCIRCLE, - SHRINKCIRCLE, - LINECONNECTFW, - LINECONNECTBW, - LINEDISCONNECTFW, - BLINKMETER, - GROWMETER, - SHRINKMETER, - RETRACTLINES, - SHOWDEFENDERS, - SHOWPOSTBATTLEPAUSE, - SHOWPOSTBATTLEBALANCE, - SHOWPOSTBATTLEBRAINS, - SHOWNEWRESIDENTS, - ANIMMODECOUNT - }; - - int m_RootBoxMaxWidth; //!< The maximum width the root CollectionBox that holds all this menu's GUI elements. This is to constrain this menu to the primary window's display (left-most) while in multi-display fullscreen, otherwise positioning can get stupid. - - // Controller which controls this menu. Not owned - Controller *m_pController; - // GUI Screen for use by the in-game GUI - GUIScreen *m_pGUIScreen; - // Input controller - GUIInput *m_pGUIInput; - // The control manager which holds all the controls - GUIControlManager *m_pGUIController; - // Visibility state of the menu - int m_MenuEnabled; - // Screen selection state - int m_MenuScreen; - // Change in menu screens detected - bool m_ScreenChange; - // Focus state on selecting scenes - int m_SceneFocus; - // Focus change direction - 0 is none, negative is back, positive forward - int m_FocusChange; - // Speed at which the menus appear and disappear - float m_MenuSpeed; - // Notification blink timer - Timer m_BlinkTimer; - // What we're blinking - int m_BlinkMode; - - // GUI Banners - GUIBanner *m_pBannerRedTop; - GUIBanner *m_pBannerRedBottom; - GUIBanner *m_pBannerYellowTop; - GUIBanner *m_pBannerYellowBottom; - - // General-purpose animation timers - Timer m_AnimTimer1; - Timer m_AnimTimer2; - Timer m_AnimTimer3; - - // Currently animated things - int m_AnimMode; - bool m_AnimModeChange; - float m_AnimModeDuration; - int m_AnimMetaPlayer; - int m_AnimDefenseTeam; - bool m_AnimActivityChange; - const Scene *m_pAnimScene; - float m_AnimRatio; - float m_AnimProgress; - float m_AnimTotalFunds; - float m_AnimFundsMax; - float m_AnimFundsMin; - int m_AnimBuildCount; - int m_AnimIncomeLine; - bool m_AnimIncomeLineChange; - int m_AnimActionLine; - bool m_AnimActionLineChange; - int m_AnimSegment; - int m_AnimCountStart; - int m_AnimCountCurrent; - int m_AnimCountEnd; - // Whether the line we're animating managed to connect with the current params - bool m_LineConnected; - // The income-related lines to keep drawing each frame - std::vector m_IncomeSiteLines; - // Indices to the player sitelines that point at the moving station - int m_aStationIncomeLineIndices[Players::MaxPlayerCount]; - // Indices to the player sitelines that point at the their own brain pool counters - int m_aBrainSaleIncomeLineIndices[Players::MaxPlayerCount]; - // Which player are currently showing their player lines. -1 means none - int m_ActivePlayerIncomeLines; - // The action-related lines to keep drawing each frame - std::vector m_ActionSiteLines[Players::MaxPlayerCount]; - // Override to alwasys draw the player action meters, no matter what - bool m_ActionMeterDrawOverride; - // The attack target crosshair info - SiteTarget m_SiteAttackTarget; - // The crosshairs showing new sites - std::vector m_NewSiteIndicators; - // The indicators of sites that just changed ownership - std::vector m_SiteSwitchIndicators; - - // The absolute screen position of the planet center - Vector m_PlanetCenter; - // The screen radius of the planet - float m_PlanetRadius; - - // General game message label - GUILabel *m_pGameMessageLabel; - - // Tooltip box - GUICollectionBox *m_pToolTipBox; - // Label displaying the ToolTip info - GUILabel *m_pToolTipText; - // Timer for detemining when it's time to actually show the tt - Timer m_ToolTipTimer; - // The control that the cursor has hovered over - GUIControl *m_pHoveredControl; - - // Collection boxes of the main screens of the GUI - GUICollectionBox *m_apScreenBox[SCREENCOUNT]; - // The metagame menu buttons - GUIButton *m_apMetaButton[METABUTTONCOUNT]; - - // The confirmation box and its controls - GUICollectionBox *m_pConfirmationBox; - GUILabel *m_pConfirmationLabel; - GUIButton *m_pConfirmationButton; - - // The player floating bars - GUICollectionBox *m_apPlayerBox[Players::MaxPlayerCount]; - // The player flag icon in the floating bars - GUICollectionBox *m_apPlayerTeamBox[Players::MaxPlayerCount]; - // Funds label in the floating bars - GUILabel *m_apPlayerBarLabel[Players::MaxPlayerCount]; - // Brain Pool label next to the floating bars - GUILabel *m_apBrainPoolLabel[Players::MaxPlayerCount]; - // The animated label that shows a message to the player over his bar -// GUILabel *m_apPlayerMessageLabel[Players::MaxPlayerCount]; - // The animated label that shows a change in the funds of a player, animating up or downward - GUILabel *m_apFundsChangeLabel[Players::MaxPlayerCount]; - // The animated label that shows a change in the brain pool of a player, animating up or downward - GUILabel *m_apBrainChangeLabel[Players::MaxPlayerCount]; - - // Timer for animating the message labels going northward -// Timer m_apPlayerMessageTimer[Players::MaxPlayerCount]; - // Timer for animating the change labels going northward - Timer m_apFundsChangeTimer[Players::MaxPlayerCount]; - // Timer for animating the change labels going northward - Timer m_apBrainsChangeTimer[Players::MaxPlayerCount]; - // Previous pos of mouse to calculate dragging - Vector m_PrevMousePos; - - // Battle site display - // The player flag icon that surrrounds the battle sites - GUICollectionBox *m_apPlayerTeamActionBox[Players::MaxPlayerCount]; - // Traveling brain label that ends up around battle sites, showing info - GUILabel *m_apPlayerBrainTravelLabel[Players::MaxPlayerCount]; - // How much funds have been allocated to each player's battle chest for the next battle - float m_aBattleFunds[Players::MaxPlayerCount]; - // Which of the players are currently attacking a place - just used for icons - bool m_aBattleAttacker[Players::MaxPlayerCount]; - // Which of the battling brains are yet graphically destroyed - bool m_aAnimDestroyed[Players::MaxPlayerCount]; - // Where the center of the brain icon is on the traveling brain label - Vector m_aBrainIconPos[Players::MaxPlayerCount]; - // Which quadrant positions of the battle matrix that have been taken by which metaplayer. NOPLAYER means no player (duh) - int m_aQuadTakenBy[4]; - - // Game Phase Box and info - GUICollectionBox *m_pPhaseBox; - GUILabel *m_pPhaseLabel; - // Pre-player-turn hold so player's privacy is protected - bool m_PreTurn; - // Have an incompleted offensive battle to resume - bool m_BattleToResume; - // Still showing the aftermath of a battle before moving onto the next - bool m_PostBattleReview; - // Whether the last battle caused a change in team - bool m_BattleCausedOwnershipChange; - // The previous ownership status of a battled scene. - // It's used to for a period during battle review still show the old ownership until the new one is dramaticlaly revealed - int m_PreBattleTeamOwnership; - - // Hover name label over Scene:s - GUILabel *m_pScenePlanetLabel; - - // Scene info popup mouseover box and controls - GUICollectionBox *m_pSceneInfoPopup; - GUIButton *m_pSceneCloseButton; - GUILabel *m_pSceneNameLabel; - GUICollectionBox *m_pSceneOwnerTeam; - GUILabel *m_pSceneResidentsLabel; - GUILabel *m_pSceneInfoLabel; - GUILabel *m_pSceneBudgetLabel; - GUISlider *m_pSceneBudgetSlider; - GUICollectionBox *m_pSceneBudgetBar; - GUICheckbox *m_pAutoDesignCheckbox; - GUILabel *m_pScanInfoLabel; - - // Currently dragged GUI box - GUICollectionBox *m_pDraggedBox; - // New potential drag is starting - bool m_EngageDrag; - // The scene currently hovered, NOT OWNED - Scene *m_pHoveredScene; - // The scene currently selected, NOT OWNED - Scene *m_pSelectedScene; - // The scene currently being played, NOT OWNED - Scene *m_pPlayingScene; - - // NEW GAME DIALOG - // Game size label and slider - GUILabel *m_pSizeLabel; - GUISlider *m_pSizeSlider; - GUILabel *m_pDifficultyLabel; - GUISlider *m_pDifficultySlider; - GUILabel *m_pGoldLabel; - GUISlider *m_pGoldSlider; - GUILabel *m_pLengthLabel; - GUISlider *m_pLengthSlider; - GUILabel *m_pErrorLabel; - GUIButton *m_apPlayerControlButton[Players::MaxPlayerCount]; - GUIComboBox *m_apPlayerTeamSelect[Players::MaxPlayerCount]; - GUIComboBox *m_apPlayerTechSelect[Players::MaxPlayerCount]; - GUIComboBox *m_apPlayerHandicap[Players::MaxPlayerCount]; - GUITextBox *m_apPlayerNameBox[Players::MaxPlayerCount]; - GUISlider *m_apPlayerAISkillSlider[Players::MaxPlayerCount]; - GUILabel *m_apPlayerAISkillLabel[Players::MaxPlayerCount]; - - // SAVING/LOADING GAME DIALOGS - GUITextBox *m_NewSaveBox; - // The combobox which lists all the games that can be saved over - GUIComboBox *m_pSavesToOverwriteCombo; - GUIComboBox *m_pSavesToLoadCombo; - // Info boxes showing some basic data on a save game - GUILabel *m_pSaveInfoLabel; - GUILabel *m_pLoadInfoLabel; - // Hack to keep the MetaSave Entity that has been selected for load even though confirmation dlg pups up and clears the selection combo - const Entity *m_pSelectedGameToLoad; - - // Whether player decided to continue to the next phase of the game - bool m_ContinuePhase; - // Whether the game was restarted this frame or not - bool m_ActivityRestarted; - // Whether the game was resumed this frame or not - bool m_ActivityResumed; - // How much money both players start with in the new game - int m_StartFunds; - // Which player is CPU managed, if any (-1) - int m_CPUPlayer; - // Difficulty setting - int m_StartDifficulty; - // Whether user has chosen to go back to the main menu - bool m_BackToMain; - // Player selected to quit the program - bool m_Quit; - - Vector m_StationPosOnOrbit; //!< The position of the station on the planet orbit. - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MetagameGUI, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - MetagameGUI(const MetagameGUI &reference) = delete; - MetagameGUI & operator=(const MetagameGUI &rhs) = delete; - -}; +namespace RTE { + + class GUIControl; + class GUIScreen; + class GUIInput; + class GUIControlManager; + class GUICollectionBox; + class GUIComboBox; + class GUICheckbox; + class GUITab; + class GUIListBox; + class GUITextBox; + class GUIButton; + class GUILabel; + class GUISlider; + class Entity; + class Scene; + class Activity; + class GAScripted; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: MetagameGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A full menu system that represents the metagame GUI for Cortex Command + // Parent(s): Serializable. + // Class history: 8/22/2008 MetagameGUI Created. + + class MetagameGUI : public Serializable { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + SerializableClassNameGetter + SerializableOverrideMethods + + public : + + enum MenuScreens { + ROOTBOX = 0, + NEWDIALOG, + LOADDIALOG, + SAVEDIALOG, + MENUDIALOG, + STATSDIALOG, + SCENEINFOBOX, + SCREENCOUNT + }; + + // For storing lines to be drawn upon draw time + struct SiteLine { + int m_Player; + float m_StartMeterAt; + float m_MeterAmount; + float m_FundsAmount; + float m_FundsTarget; + Vector m_PlanetPoint; + std::string m_SiteName; + // NOT owned here + const Scene* m_pScene; + int m_Color; + int m_OnlyFirstSegments; + int m_OnlyLastSegments; + int m_ChannelHeight; + float m_CircleSize; + bool m_Square; + + SiteLine(int player, + float startMeterAt, + float meterAmount, + const Vector& planetPoint, + std::string siteName, + // Ownership NOT passed in + const Scene* pScene, + int color, + int onlyFirstSegments = -1, + int onlyLastSegments = -1, + int channelHeight = 60, + float circleSize = 1.0f, + bool squareSite = false) { + m_Player = player; + m_StartMeterAt = startMeterAt; + m_MeterAmount = meterAmount; + m_PlanetPoint = planetPoint; + m_SiteName = siteName; + m_pScene = pScene; + m_Color = color; + m_OnlyFirstSegments = onlyFirstSegments; + m_OnlyLastSegments = onlyLastSegments; + m_ChannelHeight = channelHeight; + m_CircleSize = circleSize; + m_Square = squareSite; + } + }; + + // For storing info about target crosshairs over sites + struct SiteTarget { + enum SiteTargetStyle { + CROSSHAIRSSHRINK = 0, + CROSSHAIRSGROW, + CIRCLESHRINK, + CIRCLEGROW, + SQUARESHRINK, + SQUAREGROW, + STYLECOUNT + }; + + Vector m_CenterPos; + float m_AnimProgress; + int m_Style; + int m_Color; + double m_StartTime; + Timer m_AnimTimer; + + SiteTarget() { + m_CenterPos.Reset(); + m_AnimProgress = 0; + m_Style = 0; + m_Color = 0; + m_StartTime = 0; + m_AnimTimer.Reset(); + } + + SiteTarget(const Vector& centerPos, + float animProgress, + int style, + int color, + double startTime = 0) { + m_CenterPos = centerPos; + m_AnimProgress = animProgress; + m_Style = style; + m_Color = color; + m_StartTime = startTime; + m_AnimTimer.Reset(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws this SiteTarget onto a bitmap of choice. + // Arguments: The bitmap to draw to. + // Return value: None. + + void Draw(BITMAP* drawBitmap) const; + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: MetagameGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a MetagameGUI object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + MetagameGUI() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~MetagameGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a MetagameGUI object before deletion + // from system memory. + // Arguments: None. + + ~MetagameGUI() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MetagameGUI object ready for use. + // Arguments: A poitner to a Controller which will control this Menu. Ownership is + // NOT TRANSFERRED! + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(Controller* pController); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire MetagameGUI, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() override { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the MetagameGUI object. + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetGUIControlManager + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the GUIControlManager owned and used by this. + // Arguments: None. + // Return value: The GUIControlManager. Ownership is not transferred! + + GUIControlManager* GetGUIControlManager(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Enables or disables the menu. This will animate it in and out of view. + // Arguments: Whether to enable or disable the menu. + // Return value: None. + + void SetEnabled(bool enable = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsEnabled + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the menu is enabled or not. + // Arguments: None. + // Return value: None. + + bool IsEnabled() { return m_MenuEnabled == ENABLED || m_MenuEnabled == ENABLING; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SwitchToScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Switches to showing a specific menu screen/mode. + // Arguments: The MenuScreen to switch to. + // Return value: None. + + void SwitchToScreen(int newScreen); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetRoundName + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes a round number into a nice friendly text string. "ONE" for 1 etc + // Arguments: The number of the round to convert to a string. + // Return value: The friendly text string for that round. + + std::string GetRoundName(int roundNumber); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPlanetInfo + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets where the planet is on the scren and its other data so the menu + // can overlay properly on it. + // Arguments: The absolute screen coordinates of the planet's center. + // The radius, in screen pixel units, of the planet. + // Return value: None. + + void SetPlanetInfo(const Vector& center, float radius) { + m_PlanetCenter = center; + m_PlanetRadius = radius; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SelectScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets a specific scene as the currently selected one. OWNERSHIP IS NOT TRANSFERRED! + // Arguments: The Scene to set as selected. Ownership is NOT transferred. + // Return value: None. + + void SelectScene(Scene* pScene); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SelectScene + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tries to select a specifically named scene on the metagame field. + // Arguments: The name of the Scene to try to find and select. + // Return value: Whether mission was found and selected. + + bool SelectScene(std::string sceneName); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ContinuePhase + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the player has decided to continue to next phase of the + // round of the current game. + // Arguments: None. + // Return value: Whether the player just decided to continue this frame + + bool ContinuePhase() { return m_ContinuePhase; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ActivityRestarted + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the player has decided to restart an activity this frame. + // All parameters for the new game has been fed into ActivityMan already. + // Arguments: None. + // Return value: Whether the activity should be restarted. + + bool ActivityRestarted() { return m_ActivityRestarted; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ActivityResumed + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the player has decided to resume the current activity. + // Arguments: None. + // Return value: Whether the activity should be resumed. + + bool ActivityResumed() { return m_ActivityResumed; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BackToMain + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the player has decided to go back to the main menu. + // Arguments: None. + // Return value: Whether we should go back to main menu. + + bool BackToMain() { return m_BackToMain; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: QuitProgram + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Reports whether the player has decided to quit the program. + // Arguments: None. + // Return value: Whether the program has been commanded to shit down by the user. + + bool QuitProgram() { return m_Quit; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: StartNewGame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Attempts to start a new Metagame using the settings set in the + // New Game dialog box. + // Arguments: None. + // Return value: Whether the game was able to be set up with the current settings. + + bool StartNewGame(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: LoadGame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Attempts to load a Metagame from disk using the settings set in the + // Load Game dialog box. + // Arguments: None. + // Return value: Whether the game was able to be loaded with the current settings. + + bool LoadGame(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SaveGame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Just saves out the MetaGame and all its Scene data as-is to a specific + // location. + // Arguments: The name of the save game to create or overwrite here. + // The full path of the ini that we want to save the Metagame state to. + // Whether to load all the scene data that is on disk first so it will + // be re-saved to the new location here. + // Return value: Whether the game was able to be saved there. + + bool SaveGame(std::string saveName, std::string savePath, bool resaveSceneData = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SaveGameFromDialog + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Attempts to save a Metagame to disk using the settings set in the + // Save Game dialog box. + // Arguments: None. + // Return value: Whether the game was able to be saved with the current settings. + + bool SaveGameFromDialog(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this Menu each frame + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the menu + // Arguments: The bitmap to draw on. + // Return value: None. + + void Draw(BITMAP* drawBitmap); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetToStartNewGame + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets internal state of GUI to show 'Start new campaign' screen + // Arguments: None. + // Return value: None. + void SetToStartNewGame(); + + /// + /// Sets where the station is located on the planet orbit. + /// + /// The position of the station on the planet orbit. + void SetStationOrbitPos(const Vector& newStationPos) { m_StationPosOnOrbit = newStationPos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the MetaMan object ready for use -> this is acutally a light + // and not complete version of the one that takes a controller. + // It is only for init after reading stuff from file as a Serializable. + // Arguments: None. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create() override; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateInput + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the user input processing. + // Arguments: None. + // Return value: None. + + void UpdateInput(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: HideAllScreens + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Hides all menu screens, so one can easily be unhidden and shown only. + // Arguments: None. + // Return value: None. + + void HideAllScreens(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: KeepBoxOnScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes sure a specific box doesn't end up moved completely off-screen. + // Arguments: The GUICollectionBox to adjust, if necessary. + // The amount of margin to allow the box to stay within. If negative, + // the width/height of the box itself are used. + // Return value: None. + + void KeepBoxOnScreen(GUICollectionBox* pBox, int margin = 10); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ChangeAnimMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Changes the animation mode + // Arguments: None. + // Return value: None. + + void ChangeAnimMode(int newMode) { + m_AnimMode = newMode; + m_AnimModeChange = true; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: NewAnimMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks for and switches off the new animation mode flag + // Arguments: None. + // Return value: None. + + bool NewAnimMode() { + bool changed = m_AnimModeChange; + m_AnimModeChange = false; + return changed; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: CompletedActivity + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Handles what happens after an Activity within the Metagame was + // run and completed fully. + // Arguments: None. + // Return value: None. + + void CompletedActivity(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: AutoResolveOffensive + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Automatically resolves an offensive fight without actually launching + // and going through an Activity. Will randomly determine who won and + // what the consequences are. + // Arguments: The Offsenive Activity to resolve and manipulate accordingly. OWNERSHIP IS NOT TRANSFERRED! + // The Scene this Offensive is supposed to take place on. OWNERSHIP IS NOT TRANSFERRED! + // Whether to check the validity of all players based on whether they + // have brains remaining alive. If false, all active players will be + // instead be flagged as having had brains at some point. + // Return value: Whether the ownership of the relevant Scene changed due to this. + + bool AutoResolveOffensive(GAScripted* pOffensive, Scene* pScene, bool brainCheck = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateSiteRevealing + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the New Site Revealing animation + // Arguments: None. + // Return value: None. + + void UpdateSiteRevealing(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateSiteChangeAnim + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates team ownership change animations, if any. + // Arguments: None. + // Return value: None. + + void UpdateSiteChangeAnim(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateIncomeCounting + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Count Income animation + // Arguments: Whether to just set up the lines and funds as if we had a new round. Also skips changing funds to avoid an income/cost duplication glitch when saving a game at the start of a round. + // Return value: None. + + void UpdateIncomeCounting(bool initOverride = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateHumanPlayerTurn + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates a human player's turn. + // Arguments: Which metaplayer' turn it is + // Return value: None. + + void UpdateHumanPlayerTurn(int metaPlayer); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateBaseBuilding + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the Base Building animation + // Arguments: None. + // Return value: None. + + void UpdateBaseBuilding(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetupOffensives + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets up the Activities that represent all the offensive actions of + // the teams this round. + // Arguments: None. + // Return value: None. + + void SetupOffensives(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateOffensives + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the offensive actions animation + // Arguments: None. + // Return value: None. + + void UpdateOffensives(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FinalizeOffensive + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Finishes one battle in the UpdateOffensives and moves onto the next. + // Arguments: None. + // Return value: If there are any more battles after the one that was just finalized. + + bool FinalizeOffensive(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: ResetBattleInfo + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Hides and resets all battle info labels and panels + // Arguments: None. + // Return value: None. + + void ResetBattleInfo(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateBattleQuads + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates which player get placed in which quad around a fought-over + // site. + // Arguments: The absolutel screen position of the target site. + // Return value: None. + + void UpdateBattleQuads(Vector targetPos); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePreBattleAttackers + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the animation and display of the info for the current + // offensive battle's attackers being next in line for this round. + // Arguments: The normalized scalar which will set the desired progress of the + // total animation. 0 means nothing is shown, because it is at the start + // of the animation where brain icons start moving around. + // Return value: None. + + void UpdatePreBattleAttackers(float progress); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePreBattleDefenders + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the animation and display of the info for the current + // offensive battle's defenders being next in line for this round. + // Arguments: The normalized scalar which will set the desired progress of the + // total animation. 0 means nothing is shown, because it is at the start + // of the animation where brain icons start moving around. + // Return value: None. + + void UpdatePreBattleDefenders(float progress); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePostBattleRetreaters + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the animation and display of the info for the current + // offensive battle's retreating brains going back to their pools + // Arguments: The normalized scalar which will set the desired progress of the + // total animation. 0 means nothing has happened, because it is at the + // start of the animation where brain icons start moving around. + // Return value: None. + + void UpdatePostBattleRetreaters(float progress); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePostBattleResidents + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the animation and display of the info for the current done + // offensive battle's winning brains going back into the site. + // Arguments: The normalized scalar which will set the desired progress of the + // total animation. 0 means nothing has happened, because it is at the + // start of the animation where brain icons start moving around. + // Return value: None. + + void UpdatePostBattleResidents(float progress); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePlayerActionLines + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the action lines as per what the player has chosen to do + // during the current turn so far. + // Arguments: The metaplayer we want to update the lines for. + // Also add a line for the unallocated funds the player hasn't used for + // anyhting else yet. - NOPE, NOT IMPL YET + // Return value: The meter start that remains after all the lines are added. + + float UpdatePlayerActionLines(int player); //, bool addUnallocated = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateScenesBox + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the contents of the scene selection box. + // Arguments: Whether the selected has changed and should refresh the box completely. + // Return value: None. + + void UpdateScenesBox(bool sceneChanged = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateGameSizeLabels + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the game size labels of the new game dialog + // Arguments: None. + // Return value: None. + + void UpdateGameSizeLabels(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateAISkillSliders + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates AI skill sliders and labels for all players. + // Arguments: Which player's slider was changed. + // Return value: None. + + void UpdateAISkillSliders(int player); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePlayerSetup + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the player setup controls of the new game dialog + // Arguments: None. + // Return value: None. + + void UpdatePlayerSetup(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePlayerBars + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the floating player bars with current funds, flag, etc. + // Arguments: None. + // Return value: None. + + void UpdatePlayerBars(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdateSiteHoverLabel + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the floating label over a planet site. + // Arguments: Label is visible. + // Text to show above the location. + // The location in planetary coords. + // How high above the location to show the text, adjustment from a good default. + // Return value: None. + + void UpdateSiteNameLabel(bool visible, std::string text = "", const Vector& location = Vector(), float height = 1.0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: PlayerTextIndication + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Starts an animation of a label showing a text string over a player bar + // Arguments: Which player the indication is relevant to + // The string to display. + // Where, in screen coords the change should be indicated. The CENTER of + // the floating label will line up with this pos. + // How long, in MS, that the animation should linger + // Return value: None. + + void PlayerTextIndication(int player, std::string text, const Vector& screenPos, double animLengthMS); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: FundsChangeIndication + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Starts an animation of a label showing funds changing for a player + // Arguments: Which player the change is relevant to + // The change in funds to display. + // Where, in screen coords the change should be indicated. The RIGHTMOST + // UPPER CORNER of the floating label will line up with this pos. + // How long, in MS, that the animation should linger + // Return value: None. + + void FundsChangeIndication(int player, float change, const Vector& screenPos, double animLengthMS); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: BrainsChangeIndication + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Starts an animation of a label showing brains changing for a player + // Arguments: Which player the change is relevant to + // The change in brains to display. + // Where, in screen coords the change should be indicated. The LEFTMOST + // UPPER CORNER of the floating label will line up with this pos. + // How long, in MS, that the animation should linger + // The horizontal font alignment of the change. + // Return value: None. + + void BrainsChangeIndication(int player, int change, const Vector& screenPos, int fontAlignment, double animLengthMS); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: IsSiteLineVisible + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Tells whether a SiteLine can be considered visible. + // Arguments: The SiteLine to check. + // Return value: Whether visible. + + bool IsSiteLineVisible(SiteLine& sl) { return sl.m_OnlyFirstSegments != 0 && sl.m_OnlyLastSegments != 0; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: RemoveSiteLine + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Removes a specific index siteline out of a vector. + // Arguments: The vector of SiteLine:s to remove from. + // The index of the siteline to remove + // Return value: Whether the line was removed or not. + + bool RemoveSiteLine(std::vector& lineList, int removeIndex); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetPlayerLineFunds + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the total funds of all visible lines of a specific player. + // Arguments: A vector with SiteLine:s which may contain other players' lines too. + // Which player's lines to check for. + // Only count the funds of visible lines. + // Return value: The total funds, in oz. + + float GetPlayerLineFunds(std::vector& lineList, int player, bool onlyVisible = true); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: UpdatePlayerLineRatios + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the site line meter ratios of a player based on their fund + // amounts and visibilty. + // Arguments: A vector with SiteLine:s which may contain other players' lines too. + // Which player's lines to update. + // Whetehr to only care about visible lines. + // The total funds to be calculating the ratios against. If negative, + // the total line amounts is what will be used. + // Return value: None. + + void UpdatePlayerLineRatios(std::vector& lineList, int player, bool onlyVisible = true, float total = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawGlowLine + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws a fancy thick flickering line to point out scene points on the + // planet. + // Arguments: The bitmap to draw to. + // The start and end Vector:s for the line, in absolute screen coordinates. + // The color to draw the line in. Use makecol(r, g, b) to create the color + // Return value: None. + + static void DrawGlowLine(BITMAP* drawBitmap, const Vector& start, const Vector& end, int color); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawScreenLineToSitePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws a fancy thick flickering lines to point out scene points on the + // planet, FROM an arbitrary screen point. + // Arguments: The bitmap to draw to. + // The point on the screen to point from, in screen coordinates. + // The point on the planet to point at, in planet coordinates. + // The color of the line. + // How many of the segments from the start (the start of the line) to draw. + // How many of the segments from the end (site circle) to draw. -1 is all. + // The height of the 'channel' above and below that the lines will go around + // the player bar. + // What size factor from 'normal' should the circle's diameter be drawn. + // Return value: Whether all segments of the line were drawn with the segment params. + + bool DrawScreenLineToSitePoint(BITMAP* drawBitmap, + const Vector& screenPoint, + const Vector& planetPoint, + int color, + int onlyFirstSegments = -1, + int onlyLastSegments = -1, + int channelHeight = 80, + float circleSize = 1.0, + bool squareSite = false) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawPlayerLineToSitePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws a fancy thick flickering lines to point out scene points on the + // planet, FROM a floating player bar, showing a certain ratio. + // Arguments: The bitmap to draw to. + // The player whose floating bar we draw from. + // The start percentage of the meter to indicate, from 0 to 1.0 + // The actual percentage of the meter to indicate, from 0 to 1.0 + // The point on the planet to point at, in planet coordinates. + // The color of the line. + // How many of the segments from the start (the player floater) to draw. + // How many of the segments from the end (site circle) to draw. -1 is all. + // The height of the 'channel' above and below that the lines will go around + // the player bar. + // What size factor from 'normal' should the circle's diameter be drawn. + // Whether the circle should instead be a squareSite! + // Whether to draw the meter (FirstSegment == 1) no matter what + // Return value: Whether all segments of the line were drawn with the segment params. + + bool DrawPlayerLineToSitePoint(BITMAP* drawBitmap, + int player, + float startMeterAt, + float meterAmount, + const Vector& planetPoint, + int color, + int onlyFirstSegments = -1, + int onlyLastSegments = -1, + int channelHeight = 60, + float circleSize = 1.0, + bool squareSite = false, + bool drawMeterOverride = false) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: DrawPlayerLineToSitePoint + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws a fancy thick flickering lines to point out scene points on the + // planet, FROM a floating player bar, showing a certain ratio. + // Arguments: The bitmap to draw to. + // The SiteLine struct with all the parameters this needs. + // Whether to draw the meter (FirstSegment == 1) no matter what + // Return value: Whether all segments of the line were drawn with the segment params. + + bool DrawPlayerLineToSitePoint(BITMAP* drawBitmap, const SiteLine& sl, bool drawMeterOverride = false) const { return DrawPlayerLineToSitePoint(drawBitmap, sl.m_Player, sl.m_StartMeterAt, sl.m_MeterAmount, sl.m_PlanetPoint.GetFloored(), sl.m_Color, sl.m_OnlyFirstSegments, sl.m_OnlyLastSegments, sl.m_ChannelHeight, sl.m_CircleSize, sl.m_Square, drawMeterOverride); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: MoveLocationsIntoTheScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Moves any locations closer to the ceonter of the planet if they were left out + // of the screen due to low display resolution. + // Arguments: None. + // Return value: None. + + void MoveLocationsIntoTheScreen(); + + enum MenuEnabled { + ENABLING = 0, + ENABLED, + DISABLING, + DISABLED + }; + + enum MetaButtons { + CONFIRM = 0, + P1CONTROL, + P2CONTROL, + P3CONTROL, + P4CONTROL, + STARTNEW, + LOADNOW, + SAVENOW, + CONTINUE, + SCENEACTION, + DESIGNBASE, + SCANNOW, + SCANLATER, + METABUTTONCOUNT + }; + + enum BlinkMode { + NOBLINK = 0, + NOFUNDS, + NOCRAFT, + BLINKMODECOUNT + }; + + enum LineAnimMode { + PAUSEANIM = 0, + TARGETZEROING, + BLINKCIRCLE, + SHRINKCIRCLE, + LINECONNECTFW, + LINECONNECTBW, + LINEDISCONNECTFW, + BLINKMETER, + GROWMETER, + SHRINKMETER, + RETRACTLINES, + SHOWDEFENDERS, + SHOWPOSTBATTLEPAUSE, + SHOWPOSTBATTLEBALANCE, + SHOWPOSTBATTLEBRAINS, + SHOWNEWRESIDENTS, + ANIMMODECOUNT + }; + + int m_RootBoxMaxWidth; //!< The maximum width the root CollectionBox that holds all this menu's GUI elements. This is to constrain this menu to the primary window's display (left-most) while in multi-display fullscreen, otherwise positioning can get stupid. + + // Controller which controls this menu. Not owned + Controller* m_pController; + // GUI Screen for use by the in-game GUI + GUIScreen* m_pGUIScreen; + // Input controller + GUIInput* m_pGUIInput; + // The control manager which holds all the controls + GUIControlManager* m_pGUIController; + // Visibility state of the menu + int m_MenuEnabled; + // Screen selection state + int m_MenuScreen; + // Change in menu screens detected + bool m_ScreenChange; + // Focus state on selecting scenes + int m_SceneFocus; + // Focus change direction - 0 is none, negative is back, positive forward + int m_FocusChange; + // Speed at which the menus appear and disappear + float m_MenuSpeed; + // Notification blink timer + Timer m_BlinkTimer; + // What we're blinking + int m_BlinkMode; + + // GUI Banners + GUIBanner* m_pBannerRedTop; + GUIBanner* m_pBannerRedBottom; + GUIBanner* m_pBannerYellowTop; + GUIBanner* m_pBannerYellowBottom; + + // General-purpose animation timers + Timer m_AnimTimer1; + Timer m_AnimTimer2; + Timer m_AnimTimer3; + + // Currently animated things + int m_AnimMode; + bool m_AnimModeChange; + float m_AnimModeDuration; + int m_AnimMetaPlayer; + int m_AnimDefenseTeam; + bool m_AnimActivityChange; + const Scene* m_pAnimScene; + float m_AnimRatio; + float m_AnimProgress; + float m_AnimTotalFunds; + float m_AnimFundsMax; + float m_AnimFundsMin; + int m_AnimBuildCount; + int m_AnimIncomeLine; + bool m_AnimIncomeLineChange; + int m_AnimActionLine; + bool m_AnimActionLineChange; + int m_AnimSegment; + int m_AnimCountStart; + int m_AnimCountCurrent; + int m_AnimCountEnd; + // Whether the line we're animating managed to connect with the current params + bool m_LineConnected; + // The income-related lines to keep drawing each frame + std::vector m_IncomeSiteLines; + // Indices to the player sitelines that point at the moving station + int m_aStationIncomeLineIndices[Players::MaxPlayerCount]; + // Indices to the player sitelines that point at the their own brain pool counters + int m_aBrainSaleIncomeLineIndices[Players::MaxPlayerCount]; + // Which player are currently showing their player lines. -1 means none + int m_ActivePlayerIncomeLines; + // The action-related lines to keep drawing each frame + std::vector m_ActionSiteLines[Players::MaxPlayerCount]; + // Override to alwasys draw the player action meters, no matter what + bool m_ActionMeterDrawOverride; + // The attack target crosshair info + SiteTarget m_SiteAttackTarget; + // The crosshairs showing new sites + std::vector m_NewSiteIndicators; + // The indicators of sites that just changed ownership + std::vector m_SiteSwitchIndicators; + + // The absolute screen position of the planet center + Vector m_PlanetCenter; + // The screen radius of the planet + float m_PlanetRadius; + + // General game message label + GUILabel* m_pGameMessageLabel; + + // Tooltip box + GUICollectionBox* m_pToolTipBox; + // Label displaying the ToolTip info + GUILabel* m_pToolTipText; + // Timer for detemining when it's time to actually show the tt + Timer m_ToolTipTimer; + // The control that the cursor has hovered over + GUIControl* m_pHoveredControl; + + // Collection boxes of the main screens of the GUI + GUICollectionBox* m_apScreenBox[SCREENCOUNT]; + // The metagame menu buttons + GUIButton* m_apMetaButton[METABUTTONCOUNT]; + + // The confirmation box and its controls + GUICollectionBox* m_pConfirmationBox; + GUILabel* m_pConfirmationLabel; + GUIButton* m_pConfirmationButton; + + // The player floating bars + GUICollectionBox* m_apPlayerBox[Players::MaxPlayerCount]; + // The player flag icon in the floating bars + GUICollectionBox* m_apPlayerTeamBox[Players::MaxPlayerCount]; + // Funds label in the floating bars + GUILabel* m_apPlayerBarLabel[Players::MaxPlayerCount]; + // Brain Pool label next to the floating bars + GUILabel* m_apBrainPoolLabel[Players::MaxPlayerCount]; + // The animated label that shows a message to the player over his bar + // GUILabel *m_apPlayerMessageLabel[Players::MaxPlayerCount]; + // The animated label that shows a change in the funds of a player, animating up or downward + GUILabel* m_apFundsChangeLabel[Players::MaxPlayerCount]; + // The animated label that shows a change in the brain pool of a player, animating up or downward + GUILabel* m_apBrainChangeLabel[Players::MaxPlayerCount]; + + // Timer for animating the message labels going northward + // Timer m_apPlayerMessageTimer[Players::MaxPlayerCount]; + // Timer for animating the change labels going northward + Timer m_apFundsChangeTimer[Players::MaxPlayerCount]; + // Timer for animating the change labels going northward + Timer m_apBrainsChangeTimer[Players::MaxPlayerCount]; + // Previous pos of mouse to calculate dragging + Vector m_PrevMousePos; + + // Battle site display + // The player flag icon that surrrounds the battle sites + GUICollectionBox* m_apPlayerTeamActionBox[Players::MaxPlayerCount]; + // Traveling brain label that ends up around battle sites, showing info + GUILabel* m_apPlayerBrainTravelLabel[Players::MaxPlayerCount]; + // How much funds have been allocated to each player's battle chest for the next battle + float m_aBattleFunds[Players::MaxPlayerCount]; + // Which of the players are currently attacking a place - just used for icons + bool m_aBattleAttacker[Players::MaxPlayerCount]; + // Which of the battling brains are yet graphically destroyed + bool m_aAnimDestroyed[Players::MaxPlayerCount]; + // Where the center of the brain icon is on the traveling brain label + Vector m_aBrainIconPos[Players::MaxPlayerCount]; + // Which quadrant positions of the battle matrix that have been taken by which metaplayer. NOPLAYER means no player (duh) + int m_aQuadTakenBy[4]; + + // Game Phase Box and info + GUICollectionBox* m_pPhaseBox; + GUILabel* m_pPhaseLabel; + // Pre-player-turn hold so player's privacy is protected + bool m_PreTurn; + // Have an incompleted offensive battle to resume + bool m_BattleToResume; + // Still showing the aftermath of a battle before moving onto the next + bool m_PostBattleReview; + // Whether the last battle caused a change in team + bool m_BattleCausedOwnershipChange; + // The previous ownership status of a battled scene. + // It's used to for a period during battle review still show the old ownership until the new one is dramaticlaly revealed + int m_PreBattleTeamOwnership; + + // Hover name label over Scene:s + GUILabel* m_pScenePlanetLabel; + + // Scene info popup mouseover box and controls + GUICollectionBox* m_pSceneInfoPopup; + GUIButton* m_pSceneCloseButton; + GUILabel* m_pSceneNameLabel; + GUICollectionBox* m_pSceneOwnerTeam; + GUILabel* m_pSceneResidentsLabel; + GUILabel* m_pSceneInfoLabel; + GUILabel* m_pSceneBudgetLabel; + GUISlider* m_pSceneBudgetSlider; + GUICollectionBox* m_pSceneBudgetBar; + GUICheckbox* m_pAutoDesignCheckbox; + GUILabel* m_pScanInfoLabel; + + // Currently dragged GUI box + GUICollectionBox* m_pDraggedBox; + // New potential drag is starting + bool m_EngageDrag; + // The scene currently hovered, NOT OWNED + Scene* m_pHoveredScene; + // The scene currently selected, NOT OWNED + Scene* m_pSelectedScene; + // The scene currently being played, NOT OWNED + Scene* m_pPlayingScene; + + // NEW GAME DIALOG + // Game size label and slider + GUILabel* m_pSizeLabel; + GUISlider* m_pSizeSlider; + GUILabel* m_pDifficultyLabel; + GUISlider* m_pDifficultySlider; + GUILabel* m_pGoldLabel; + GUISlider* m_pGoldSlider; + GUILabel* m_pLengthLabel; + GUISlider* m_pLengthSlider; + GUILabel* m_pErrorLabel; + GUIButton* m_apPlayerControlButton[Players::MaxPlayerCount]; + GUIComboBox* m_apPlayerTeamSelect[Players::MaxPlayerCount]; + GUIComboBox* m_apPlayerTechSelect[Players::MaxPlayerCount]; + GUIComboBox* m_apPlayerHandicap[Players::MaxPlayerCount]; + GUITextBox* m_apPlayerNameBox[Players::MaxPlayerCount]; + GUISlider* m_apPlayerAISkillSlider[Players::MaxPlayerCount]; + GUILabel* m_apPlayerAISkillLabel[Players::MaxPlayerCount]; + + // SAVING/LOADING GAME DIALOGS + GUITextBox* m_NewSaveBox; + // The combobox which lists all the games that can be saved over + GUIComboBox* m_pSavesToOverwriteCombo; + GUIComboBox* m_pSavesToLoadCombo; + // Info boxes showing some basic data on a save game + GUILabel* m_pSaveInfoLabel; + GUILabel* m_pLoadInfoLabel; + // Hack to keep the MetaSave Entity that has been selected for load even though confirmation dlg pups up and clears the selection combo + const Entity* m_pSelectedGameToLoad; + + // Whether player decided to continue to the next phase of the game + bool m_ContinuePhase; + // Whether the game was restarted this frame or not + bool m_ActivityRestarted; + // Whether the game was resumed this frame or not + bool m_ActivityResumed; + // How much money both players start with in the new game + int m_StartFunds; + // Which player is CPU managed, if any (-1) + int m_CPUPlayer; + // Difficulty setting + int m_StartDifficulty; + // Whether user has chosen to go back to the main menu + bool m_BackToMain; + // Player selected to quit the program + bool m_Quit; + + Vector m_StationPosOnOrbit; //!< The position of the station on the planet orbit. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this MetagameGUI, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + MetagameGUI(const MetagameGUI& reference) = delete; + MetagameGUI& operator=(const MetagameGUI& rhs) = delete; + }; } // namespace RTE -#endif // File \ No newline at end of file +#endif // File \ No newline at end of file diff --git a/Source/Menus/ModManagerGUI.cpp b/Source/Menus/ModManagerGUI.cpp index 14d9480d42..b29edc0fd7 100644 --- a/Source/Menus/ModManagerGUI.cpp +++ b/Source/Menus/ModManagerGUI.cpp @@ -16,23 +16,23 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ModManagerGUI::ModManagerGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool createForPauseMenu) { + ModManagerGUI::ModManagerGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); m_GUIControlManager->Load("Base.rte/GUIs/ModManagerGUI.ini"); int rootBoxMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); - GUICollectionBox *rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); + GUICollectionBox* rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); rootBox->Resize(rootBoxMaxWidth, g_WindowMan.GetResY()); - GUICollectionBox *modManagerMenuBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxModManager")); + GUICollectionBox* modManagerMenuBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxModManager")); modManagerMenuBox->CenterInParent(true, true); modManagerMenuBox->SetPositionAbs(modManagerMenuBox->GetXPos(), (rootBox->GetHeight() < 540) ? modManagerMenuBox->GetYPos() - 15 : 140); - m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMainMenu")); + m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMainMenu")); if (createForPauseMenu) { m_BackToMainButton->SetSize(120, 20); @@ -40,46 +40,46 @@ namespace RTE { } m_BackToMainButton->SetPositionAbs((rootBox->GetWidth() - m_BackToMainButton->GetWidth()) / 2, modManagerMenuBox->GetYPos() + modManagerMenuBox->GetHeight() + 10); - m_ModsListBox = dynamic_cast(m_GUIControlManager->GetControl("ListBoxMods")); + m_ModsListBox = dynamic_cast(m_GUIControlManager->GetControl("ListBoxMods")); m_ModsListBox->SetMouseScrolling(true); m_ModsListBox->SetScrollBarThickness(15); m_ModsListBox->SetScrollBarPadding(2); - m_ScriptsListBox = dynamic_cast(m_GUIControlManager->GetControl("ListBoxScripts")); + m_ScriptsListBox = dynamic_cast(m_GUIControlManager->GetControl("ListBoxScripts")); m_ScriptsListBox->SetMouseScrolling(true); m_ScriptsListBox->SetScrollBarThickness(15); m_ScriptsListBox->SetScrollBarPadding(2); - m_ToggleModButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonToggleMod")); - m_ToggleScriptButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonToggleScript")); - m_ModOrScriptDescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelDescription")); + m_ToggleModButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonToggleMod")); + m_ToggleScriptButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonToggleScript")); + m_ModOrScriptDescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelDescription")); m_ModsListFetched = false; m_ScriptsListFetched = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ModManagerGUI::PopulateKnownModsList() { for (int i = 0; i < g_PresetMan.GetTotalModuleCount(); ++i) { if (i >= g_PresetMan.GetOfficialModuleCount() && i < g_PresetMan.GetTotalModuleCount()) { - if (const DataModule *dataModule = g_PresetMan.GetDataModule(i); dataModule && !dataModule->IsUserdata()) { - ModRecord modRecord = { dataModule->GetFileName(), dataModule->GetFriendlyName(), dataModule->GetDescription(), g_SettingsMan.IsModDisabled(dataModule->GetFileName()) }; + if (const DataModule* dataModule = g_PresetMan.GetDataModule(i); dataModule && !dataModule->IsUserdata()) { + ModRecord modRecord = {dataModule->GetFileName(), dataModule->GetFriendlyName(), dataModule->GetDescription(), g_SettingsMan.IsModDisabled(dataModule->GetFileName())}; m_KnownMods.emplace_back(modRecord); } } } // Add missing data from disabled mods settings - for (const auto &[modPath, modDisabled] : g_SettingsMan.GetDisabledModsMap()) { + for (const auto& [modPath, modDisabled]: g_SettingsMan.GetDisabledModsMap()) { bool found = false; - for (const ModRecord &knowModListEntry : m_KnownMods) { + for (const ModRecord& knowModListEntry: m_KnownMods) { if (modPath == knowModListEntry.ModulePath) { found = true; break; } } if (!found) { - ModRecord disabledModRecord = { modPath, "N/A, Module not loaded", "N/A, Module not loaded", modDisabled }; + ModRecord disabledModRecord = {modPath, "N/A, Module not loaded", "N/A, Module not loaded", modDisabled}; m_KnownMods.emplace_back(disabledModRecord); } } @@ -92,15 +92,15 @@ namespace RTE { m_ModsListFetched = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ModManagerGUI::PopulateKnownScriptsList() { - std::list globalScriptList; + std::list globalScriptList; g_PresetMan.GetAllOfType(globalScriptList, "GlobalScript"); - for (Entity *globalScriptListEntry : globalScriptList) { - if (const GlobalScript *globalScript = dynamic_cast(globalScriptListEntry)) { - ScriptRecord scriptRecord = { globalScript->GetModuleAndPresetName(), globalScript->GetDescription(), g_SettingsMan.IsGlobalScriptEnabled(scriptRecord.PresetName) }; + for (Entity* globalScriptListEntry: globalScriptList) { + if (const GlobalScript* globalScript = dynamic_cast(globalScriptListEntry)) { + ScriptRecord scriptRecord = {globalScript->GetModuleAndPresetName(), globalScript->GetDescription(), g_SettingsMan.IsGlobalScriptEnabled(scriptRecord.PresetName)}; m_KnownScripts.emplace_back(scriptRecord); } } @@ -113,14 +113,14 @@ namespace RTE { m_ScriptsListFetched = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ModManagerGUI::ToggleMod() { int index = m_ModsListBox->GetSelectedIndex(); if (index > -1) { - std::unordered_map &disabledModsList = g_SettingsMan.GetDisabledModsMap(); - GUIListPanel::Item *selectedItem = m_ModsListBox->GetSelected(); - ModRecord &modRecord = m_KnownMods.at(selectedItem->m_ExtraIndex); + std::unordered_map& disabledModsList = g_SettingsMan.GetDisabledModsMap(); + GUIListPanel::Item* selectedItem = m_ModsListBox->GetSelected(); + ModRecord& modRecord = m_KnownMods.at(selectedItem->m_ExtraIndex); modRecord.Disabled = !modRecord.Disabled; if (modRecord.Disabled) { @@ -141,14 +141,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ModManagerGUI::ToggleScript() { int index = m_ScriptsListBox->GetSelectedIndex(); if (index > -1) { - std::unordered_map &enabledScriptList = g_SettingsMan.GetEnabledGlobalScriptMap(); - GUIListPanel::Item *selectedItem = m_ScriptsListBox->GetSelected(); - ScriptRecord &scriptRecord = m_KnownScripts.at(selectedItem->m_ExtraIndex); + std::unordered_map& enabledScriptList = g_SettingsMan.GetEnabledGlobalScriptMap(); + GUIListPanel::Item* selectedItem = m_ScriptsListBox->GetSelected(); + ScriptRecord& scriptRecord = m_KnownScripts.at(selectedItem->m_ExtraIndex); scriptRecord.Enabled = !scriptRecord.Enabled; if (scriptRecord.Enabled) { @@ -169,7 +169,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ModManagerGUI::HandleInputEvents() { if (!ListsFetched()) { @@ -189,14 +189,16 @@ namespace RTE { ToggleScript(); } } else if (guiEvent.GetType() == GUIEvent::Notification) { - if (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl())) { g_GUISound.SelectionChangeSound()->Play(); } + if (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl())) { + g_GUISound.SelectionChangeSound()->Play(); + } if (guiEvent.GetControl() == m_ModsListBox && (guiEvent.GetMsg() == GUIListBox::Select && m_ModsListBox->GetSelectedIndex() > -1)) { - const ModRecord &modRecord = m_KnownMods.at(m_ModsListBox->GetSelected()->m_ExtraIndex); + const ModRecord& modRecord = m_KnownMods.at(m_ModsListBox->GetSelected()->m_ExtraIndex); m_ModOrScriptDescriptionLabel->SetText(modRecord.Description); m_ToggleModButton->SetText(modRecord.Disabled ? "Enable Mod" : "Disable Mod"); } else if (guiEvent.GetControl() == m_ScriptsListBox && (guiEvent.GetMsg() == GUIListBox::Select && m_ScriptsListBox->GetSelectedIndex() > -1)) { - const ScriptRecord &scriptRecord = m_KnownScripts.at(m_ScriptsListBox->GetSelected()->m_ExtraIndex); + const ScriptRecord& scriptRecord = m_KnownScripts.at(m_ScriptsListBox->GetSelected()->m_ExtraIndex); m_ModOrScriptDescriptionLabel->SetText(scriptRecord.Description); m_ToggleScriptButton->SetText(scriptRecord.Enabled ? "Disable Script" : "Enable Script"); } @@ -205,9 +207,9 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ModManagerGUI::Draw() const { m_GUIControlManager->Draw(); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/ModManagerGUI.h b/Source/Menus/ModManagerGUI.h index b9ed1686f9..ec62385bc7 100644 --- a/Source/Menus/ModManagerGUI.h +++ b/Source/Menus/ModManagerGUI.h @@ -16,7 +16,6 @@ namespace RTE { class ModManagerGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a ModManagerGUI object in system memory and make it ready for use. @@ -24,7 +23,7 @@ namespace RTE { /// Pointer to a GUIScreen interface that will be used by this ModManagerGUI's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this ModManagerGUI's GUIControlManager. Ownership is NOT transferred! /// Whether this SettingsGUI is part of ModManagerGUI and should have a slightly different layout. - ModManagerGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool createForPauseMenu = false); + ModManagerGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu = false); #pragma endregion #pragma region Concrete Methods @@ -41,7 +40,6 @@ namespace RTE { #pragma endregion private: - /// /// Struct containing information about a valid mod DataModule. /// @@ -62,7 +60,7 @@ namespace RTE { /// /// ModRecord to compare with. /// Bool with result of the alphabetical comparison. - bool operator<(const ModRecord &rhs) const { return ModulePath < rhs.ModulePath; } + bool operator<(const ModRecord& rhs) const { return ModulePath < rhs.ModulePath; } }; /// @@ -84,7 +82,7 @@ namespace RTE { /// /// ScriptRecord to compare with. /// Bool with result of the alphabetical comparison. - bool operator<(const ScriptRecord &rhs) const { return PresetName < rhs.PresetName; } + bool operator<(const ScriptRecord& rhs) const { return PresetName < rhs.PresetName; } }; std::unique_ptr m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of the ModManagerGUI. @@ -98,12 +96,12 @@ namespace RTE { /// /// GUI elements that compose the Mod Manager menu screen. /// - GUIButton *m_BackToMainButton; - GUIButton *m_ToggleModButton; - GUIButton *m_ToggleScriptButton; - GUIListBox *m_ModsListBox; - GUIListBox *m_ScriptsListBox; - GUILabel *m_ModOrScriptDescriptionLabel; + GUIButton* m_BackToMainButton; + GUIButton* m_ToggleModButton; + GUIButton* m_ToggleScriptButton; + GUIListBox* m_ModsListBox; + GUIListBox* m_ScriptsListBox; + GUILabel* m_ModOrScriptDescriptionLabel; #pragma region Mod and Script Handling /// @@ -134,8 +132,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - ModManagerGUI(const ModManagerGUI &reference) = delete; - ModManagerGUI & operator=(const ModManagerGUI &rhs) = delete; + ModManagerGUI(const ModManagerGUI& reference) = delete; + ModManagerGUI& operator=(const ModManagerGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/MultiplayerGameGUI.cpp b/Source/Menus/MultiplayerGameGUI.cpp index 7d179476c7..733b237de5 100644 --- a/Source/Menus/MultiplayerGameGUI.cpp +++ b/Source/Menus/MultiplayerGameGUI.cpp @@ -7,7 +7,6 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -15,7 +14,6 @@ #include "FrameMan.h" - using namespace std; using namespace RTE; @@ -25,37 +23,32 @@ using namespace RTE; // Description: Clears all the member variables of this MultiplayerGameGUI, effectively // resetting the members of this abstraction level only. -void MultiplayerGameGUI::Clear() -{ +void MultiplayerGameGUI::Clear() { m_pController = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the MultiplayerGameGUI object ready for use. -int MultiplayerGameGUI::Create(Controller *pController) -{ +int MultiplayerGameGUI::Create(Controller* pController) { AAssert(pController, "No controller sent to MultiplayerGameGUI on creation!"); m_pController = pController; // Allocate and (re)create the Editor GUIs - //m_pPieMenu = new PieMenuGUI(); - //m_pPieMenu->Create(pController); + // m_pPieMenu = new PieMenuGUI(); + // m_pPieMenu->Create(pController); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the MultiplayerGameGUI object. -void MultiplayerGameGUI::Destroy() -{ +void MultiplayerGameGUI::Destroy() { Clear(); } @@ -64,18 +57,13 @@ void MultiplayerGameGUI::Destroy() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the state of this Menu each frame -void MultiplayerGameGUI::Update() -{ +void MultiplayerGameGUI::Update() { } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws the menu -void MultiplayerGameGUI::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) const -{ +void MultiplayerGameGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { } - - diff --git a/Source/Menus/MultiplayerGameGUI.h b/Source/Menus/MultiplayerGameGUI.h index 4ea16e6889..cf73b715ae 100644 --- a/Source/Menus/MultiplayerGameGUI.h +++ b/Source/Menus/MultiplayerGameGUI.h @@ -4,24 +4,21 @@ ////////////////////////////////////////////////////////////////////////////////////////// // File: MultiplayerGameGUI.h ////////////////////////////////////////////////////////////////////////////////////////// -// Description: +// Description: // Project: GUI Library -// Author(s): - +// Author(s): ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files -//#include "FrameMan.h" +// #include "FrameMan.h" #include "Timer.h" #include "Vector.h" #include "Controller.h" struct BITMAP; - -namespace RTE -{ +namespace RTE { class SceneObject; class ObjectPickerGUI; class PieMenuGUI; @@ -29,9 +26,9 @@ namespace RTE ////////////////////////////////////////////////////////////////////////////////////////// // Class: MultiplayerGameGUI ////////////////////////////////////////////////////////////////////////////////////////// - // Description: + // Description: // Parent(s): None. - // Class history: + // Class history: class MultiplayerGameGUI { @@ -40,13 +37,11 @@ namespace RTE public: // Different modes of this editor - enum GUIMode - { + enum GUIMode { INACTIVE = 0, ACTIVE = 1 }; - ////////////////////////////////////////////////////////////////////////////////////////// // Constructor: MultiplayerGameGUI ////////////////////////////////////////////////////////////////////////////////////////// @@ -56,7 +51,6 @@ namespace RTE MultiplayerGameGUI() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Destructor: ~MultiplayerGameGUI ////////////////////////////////////////////////////////////////////////////////////////// @@ -66,7 +60,6 @@ namespace RTE ~MultiplayerGameGUI() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// @@ -82,8 +75,7 @@ namespace RTE // Return value: An error return value signaling sucess or any particular failure. // Anything below 0 is an error signal. - int Create(Controller *pController); - + int Create(Controller* pController); ////////////////////////////////////////////////////////////////////////////////////////// // Method: Reset @@ -93,8 +85,7 @@ namespace RTE // Arguments: None. // Return value: None. - void Reset() { Clear(); } - + void Reset() { Clear(); } ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy @@ -105,7 +96,6 @@ namespace RTE void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Update ////////////////////////////////////////////////////////////////////////////////////////// @@ -113,7 +103,7 @@ namespace RTE // Arguments: None. // Return value: None. - void Update(); + void Update(); ////////////////////////////////////////////////////////////////////////////////////////// // Method: Draw @@ -123,21 +113,19 @@ namespace RTE // The absolute position of the target bitmap's upper left corner in the scene. // Return value: None. - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) const; + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; ////////////////////////////////////////////////////////////////////////////////////////// // Protected member variable and method declarations protected: - // Controller which conrols this menu. Not owned - Controller *m_pController; + Controller* m_pController; ////////////////////////////////////////////////////////////////////////////////////////// // Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear ////////////////////////////////////////////////////////////////////////////////////////// @@ -148,13 +136,11 @@ namespace RTE void Clear(); - // Disallow the use of some implicit methods. - MultiplayerGameGUI(const MultiplayerGameGUI &reference); - MultiplayerGameGUI & operator=(const MultiplayerGameGUI &rhs); - + MultiplayerGameGUI(const MultiplayerGameGUI& reference); + MultiplayerGameGUI& operator=(const MultiplayerGameGUI& rhs); }; } // namespace RTE -#endif // File +#endif // File diff --git a/Source/Menus/ObjectPickerGUI.cpp b/Source/Menus/ObjectPickerGUI.cpp index 3db530a6c7..07d017db46 100644 --- a/Source/Menus/ObjectPickerGUI.cpp +++ b/Source/Menus/ObjectPickerGUI.cpp @@ -25,9 +25,9 @@ namespace RTE { - BITMAP *ObjectPickerGUI::s_Cursor = nullptr; + BITMAP* ObjectPickerGUI::s_Cursor = nullptr; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::Clear() { m_GUIScreen = nullptr; @@ -58,15 +58,21 @@ namespace RTE { m_ExpandedModules[0] = true; // Base.rte is always expanded } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ObjectPickerGUI::Create(Controller *controller, int whichModuleSpace, const std::string_view &onlyOfType) { + int ObjectPickerGUI::Create(Controller* controller, int whichModuleSpace, const std::string_view& onlyOfType) { RTEAssert(controller, "No controller sent to ObjectPickerGUI on creation!"); m_Controller = controller; - if (!m_GUIScreen) { m_GUIScreen = std::make_unique(g_FrameMan.GetBackBuffer8()); } - if (!m_GUIInput) { m_GUIInput = std::make_unique(controller->GetPlayer()); } - if (!m_GUIControlManager) { m_GUIControlManager = std::make_unique(); } + if (!m_GUIScreen) { + m_GUIScreen = std::make_unique(g_FrameMan.GetBackBuffer8()); + } + if (!m_GUIInput) { + m_GUIInput = std::make_unique(controller->GetPlayer()); + } + if (!m_GUIControlManager) { + m_GUIControlManager = std::make_unique(); + } RTEAssert(m_GUIControlManager->Create(m_GUIScreen.get(), m_GUIInput.get(), "Base.rte/GUIs/Skins", "DefaultSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/DefaultSkin.ini"); m_GUIControlManager->Load("Base.rte/GUIs/ObjectPickerGUI.ini"); @@ -78,21 +84,23 @@ namespace RTE { } if (g_FrameMan.IsInMultiplayerMode()) { - dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_FrameMan.GetPlayerFrameBufferWidth(controller->GetPlayer()), g_FrameMan.GetPlayerFrameBufferHeight(controller->GetPlayer())); + dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_FrameMan.GetPlayerFrameBufferWidth(controller->GetPlayer()), g_FrameMan.GetPlayerFrameBufferHeight(controller->GetPlayer())); } else { - dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); + dynamic_cast(m_GUIControlManager->GetControl("base"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); } - if (!m_ParentBox) { m_ParentBox = dynamic_cast(m_GUIControlManager->GetControl("PickerGUIBox")); } + if (!m_ParentBox) { + m_ParentBox = dynamic_cast(m_GUIControlManager->GetControl("PickerGUIBox")); + } m_ParentBox->SetPositionAbs(g_FrameMan.GetPlayerFrameBufferWidth(m_Controller->GetPlayer()), 0); m_ParentBox->SetEnabled(false); m_ParentBox->SetVisible(false); - m_GroupsList = dynamic_cast(m_GUIControlManager->GetControl("GroupsLB")); + m_GroupsList = dynamic_cast(m_GUIControlManager->GetControl("GroupsLB")); m_GroupsList->EnableScrollbars(false, true); m_GroupsList->SetScrollBarThickness(13); m_GroupsList->SetAlternateDrawMode(false); m_GroupsList->SetMultiSelect(false); - m_ObjectsList = dynamic_cast(m_GUIControlManager->GetControl("ObjectsLB")); + m_ObjectsList = dynamic_cast(m_GUIControlManager->GetControl("ObjectsLB")); m_ObjectsList->EnableScrollbars(false, true); m_ObjectsList->SetScrollBarThickness(13); m_ObjectsList->SetAlternateDrawMode(true); @@ -106,8 +114,8 @@ namespace RTE { } if (!m_PopupBox) { - m_PopupBox = dynamic_cast(m_GUIControlManager->GetControl("BuyGUIPopup")); - m_PopupText = dynamic_cast(m_GUIControlManager->GetControl("PopupText")); + m_PopupBox = dynamic_cast(m_GUIControlManager->GetControl("BuyGUIPopup")); + m_PopupText = dynamic_cast(m_GUIControlManager->GetControl("PopupText")); m_PopupText->SetFont(m_GUIControlManager->GetSkin()->GetFont("FontSmall.png")); // Never enable the popup box because it steals focus and causes other windows to think the cursor left them @@ -121,7 +129,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::SetEnabled(bool enable) { if (enable && m_PickerState != PickerState::Enabled && m_PickerState != PickerState::Enabling) { @@ -143,7 +151,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::SetNativeTechModule(int whichModule) { if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) { @@ -152,8 +160,8 @@ namespace RTE { SetObjectsListModuleGroupExpanded(m_NativeTechModuleID); if (!g_SettingsMan.FactionBuyMenuThemesDisabled()) { - if (const DataModule *techModule = g_PresetMan.GetDataModule(whichModule); techModule->IsFaction()) { - const DataModule::BuyMenuTheme &techBuyMenuTheme = techModule->GetFactionBuyMenuTheme(); + if (const DataModule* techModule = g_PresetMan.GetDataModule(whichModule); techModule->IsFaction()) { + const DataModule::BuyMenuTheme& techBuyMenuTheme = techModule->GetFactionBuyMenuTheme(); if (!techBuyMenuTheme.SkinFilePath.empty()) { // Not specifying the skin file directory allows us to load image files from the whole working directory in the skin file instead of just the specified directory. @@ -164,14 +172,16 @@ namespace RTE { m_GUIControlManager->GetSkin()->GetValue("DescriptionBoxText", "Font", &themeDescriptionBoxTextFont); m_PopupText->SetFont(m_GUIControlManager->GetSkin()->GetFont(themeDescriptionBoxTextFont)); } - if (techBuyMenuTheme.BackgroundColorIndex >= 0) { m_ParentBox->SetDrawColor(std::clamp(techBuyMenuTheme.BackgroundColorIndex, 0, 255)); } + if (techBuyMenuTheme.BackgroundColorIndex >= 0) { + m_ParentBox->SetDrawColor(std::clamp(techBuyMenuTheme.BackgroundColorIndex, 0, 255)); + } } } } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ObjectPickerGUI::SetListFocus(PickerFocus listToFocusOn) { if (listToFocusOn == m_PickerFocus) { @@ -191,10 +201,10 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ObjectPickerGUI::SelectGroupByName(const std::string_view &groupName) { - for (const GUIListPanel::Item *groupListItem : *m_GroupsList->GetItemList()) { + bool ObjectPickerGUI::SelectGroupByName(const std::string_view& groupName) { + for (const GUIListPanel::Item* groupListItem: *m_GroupsList->GetItemList()) { if (groupListItem->m_Name == groupName) { SelectGroupByIndex(groupListItem->m_ID); SetListFocus(PickerFocus::ObjectList); @@ -204,7 +214,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::SelectGroupByIndex(int groupIndex, bool updateObjectsList) { m_SelectedGroupIndex = (groupIndex < 0) ? m_ShownGroupIndex : groupIndex; @@ -219,42 +229,50 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::SelectNextOrPrevGroup(bool selectPrev) { int groupIndex = m_SelectedGroupIndex; if (selectPrev) { groupIndex--; - if (groupIndex < 0) { groupIndex = m_GroupsList->GetItemList()->size() - 1; } + if (groupIndex < 0) { + groupIndex = m_GroupsList->GetItemList()->size() - 1; + } } else { groupIndex++; - if (groupIndex >= m_GroupsList->GetItemList()->size()) { groupIndex = 0; } + if (groupIndex >= m_GroupsList->GetItemList()->size()) { + groupIndex = 0; + } } SelectGroupByIndex(groupIndex); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::UpdateGroupsList() { m_GroupsList->ClearList(); - bool showAssemblySchemes = dynamic_cast(g_ActivityMan.GetActivity()); + bool showAssemblySchemes = dynamic_cast(g_ActivityMan.GetActivity()); std::list groupList; g_PresetMan.GetModuleSpaceGroups(groupList, m_ModuleSpaceID, m_ShowType); - for (const std::string &groupListEntry : groupList) { - std::list objectList; + for (const std::string& groupListEntry: groupList) { + std::list objectList; g_PresetMan.GetAllOfGroupInModuleSpace(objectList, groupListEntry, m_ShowType, m_ModuleSpaceID); bool onlyAssembliesInGroup = true; bool onlyAssemblySchemesInGroup = true; bool hasObjectsToShow = false; - for (Entity *objectListEntry : objectList) { - if (!dynamic_cast(objectListEntry)) { onlyAssembliesInGroup = false; } - if (!dynamic_cast(objectListEntry)) { onlyAssemblySchemesInGroup = false; } + for (Entity* objectListEntry: objectList) { + if (!dynamic_cast(objectListEntry)) { + onlyAssembliesInGroup = false; + } + if (!dynamic_cast(objectListEntry)) { + onlyAssemblySchemesInGroup = false; + } - const SceneObject *sceneObject = dynamic_cast(objectListEntry); + const SceneObject* sceneObject = dynamic_cast(objectListEntry); if (sceneObject && sceneObject->IsBuyable() && !sceneObject->IsBuyableInBuyMenuOnly() && !sceneObject->IsBuyableInScriptOnly()) { hasObjectsToShow = true; break; @@ -262,17 +280,19 @@ namespace RTE { } // If this group is in the SettingsMan list of always visible assembly groups, then force the onlyAssembliesInGroup flag off so this group is always shown if (onlyAssembliesInGroup) { - for (const std::string &assemblyGroup : g_SettingsMan.GetVisibleAssemblyGroupsList()) { + for (const std::string& assemblyGroup: g_SettingsMan.GetVisibleAssemblyGroupsList()) { if (groupListEntry == assemblyGroup) { onlyAssembliesInGroup = false; break; } } } - if (!objectList.empty() && hasObjectsToShow && (!onlyAssembliesInGroup || groupListEntry == "Assemblies") && (!onlyAssemblySchemesInGroup || showAssemblySchemes)) { m_GroupsList->AddItem(groupListEntry); } + if (!objectList.empty() && hasObjectsToShow && (!onlyAssembliesInGroup || groupListEntry == "Assemblies") && (!onlyAssemblySchemesInGroup || showAssemblySchemes)) { + m_GroupsList->AddItem(groupListEntry); + } } - if (const GUIListPanel::Item *listItem = m_GroupsList->GetItem(0)) { + if (const GUIListPanel::Item* listItem = m_GroupsList->GetItem(0)) { m_GroupsList->ScrollToTop(); m_SelectedGroupIndex = 0; m_GroupsList->SetSelectedIndex(m_SelectedGroupIndex); @@ -280,48 +300,54 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const SceneObject * ObjectPickerGUI::GetSelectedObject() { - if (const GUIListPanel::Item *selectedItem = m_ObjectsList->GetSelected()) { - return dynamic_cast(selectedItem->m_pEntity); + const SceneObject* ObjectPickerGUI::GetSelectedObject() { + if (const GUIListPanel::Item* selectedItem = m_ObjectsList->GetSelected()) { + return dynamic_cast(selectedItem->m_pEntity); } return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::SelectObjectByIndex(int objectIndex, bool playSelectionSound) { m_SelectedObjectIndex = objectIndex; m_ObjectsList->SetSelectedIndex(m_SelectedObjectIndex); - if (playSelectionSound) { g_GUISound.SelectionChangeSound()->Play(m_Controller->GetPlayer()); } + if (playSelectionSound) { + g_GUISound.SelectionChangeSound()->Play(m_Controller->GetPlayer()); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::SelectNextOrPrevObject(bool getPrev) { int objectIndex = m_SelectedObjectIndex; if (getPrev) { objectIndex--; - if (objectIndex < 0) { objectIndex = m_ObjectsList->GetItemList()->size() - 1; } + if (objectIndex < 0) { + objectIndex = m_ObjectsList->GetItemList()->size() - 1; + } } else { objectIndex++; - if (objectIndex >= m_ObjectsList->GetItemList()->size()) { objectIndex = 0; } + if (objectIndex >= m_ObjectsList->GetItemList()->size()) { + objectIndex = 0; + } } SelectObjectByIndex(objectIndex); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::AddObjectsListModuleGroup(int moduleID) { - const DataModule *dataModule = g_PresetMan.GetDataModule(moduleID); + const DataModule* dataModule = g_PresetMan.GetDataModule(moduleID); std::string moduleName = dataModule->GetFriendlyName(); std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), ::toupper); - GUIBitmap *dataModuleIcon = dataModule->GetIcon() ? new AllegroBitmap(dataModule->GetIcon()) : nullptr; + GUIBitmap* dataModuleIcon = dataModule->GetIcon() ? new AllegroBitmap(dataModule->GetIcon()) : nullptr; m_ObjectsList->AddItem(moduleName, m_ExpandedModules.at(moduleID) ? "-" : "+", dataModuleIcon, nullptr, moduleID); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::SetObjectsListModuleGroupExpanded(int moduleID, bool expanded) { if (moduleID > 0 && moduleID < m_ExpandedModules.size()) { @@ -332,7 +358,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::ToggleObjectsListModuleGroupExpansion(int moduleID) { if (moduleID > 0 && moduleID < m_ExpandedModules.size()) { @@ -342,22 +368,26 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::ShowDescriptionPopupBox() { std::string description = ""; - GUIListPanel::Item *objectListItem = m_ObjectsList->GetSelected(); + GUIListPanel::Item* objectListItem = m_ObjectsList->GetSelected(); if (objectListItem && objectListItem->m_pEntity && !objectListItem->m_pEntity->GetDescription().empty()) { description = objectListItem->m_pEntity->GetDescription(); } else if (objectListItem && objectListItem->m_ExtraIndex >= 0) { - const DataModule *dataModule = g_PresetMan.GetDataModule(objectListItem->m_ExtraIndex); - if (dataModule && !dataModule->GetDescription().empty()) { description = dataModule->GetDescription(); } + const DataModule* dataModule = g_PresetMan.GetDataModule(objectListItem->m_ExtraIndex); + if (dataModule && !dataModule->GetDescription().empty()) { + description = dataModule->GetDescription(); + } } if (!description.empty()) { m_PopupBox->SetEnabled(false); m_PopupBox->SetVisible(true); m_PopupBox->SetPositionAbs(m_ObjectsList->GetXPos() - m_PopupBox->GetWidth() + 4, m_ObjectsList->GetYPos() + m_ObjectsList->GetStackHeight(objectListItem) - m_ObjectsList->GetScrollVerticalValue()); - if (m_PopupBox->GetYPos() + m_PopupBox->GetHeight() > m_ParentBox->GetHeight()) { m_PopupBox->SetPositionAbs(m_PopupBox->GetXPos(), m_ParentBox->GetHeight() - m_PopupBox->GetHeight()); } + if (m_PopupBox->GetYPos() + m_PopupBox->GetHeight() > m_ParentBox->GetHeight()) { + m_PopupBox->SetPositionAbs(m_PopupBox->GetXPos(), m_ParentBox->GetHeight() - m_PopupBox->GetHeight()); + } m_PopupText->SetHAlignment(GUIFont::Left); m_PopupText->SetText(description); @@ -365,13 +395,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::UpdateObjectsList(bool selectTop) { m_ObjectsList->ClearList(); - std::vector> moduleList(g_PresetMan.GetTotalModuleCount(), std::list()); + std::vector> moduleList(g_PresetMan.GetTotalModuleCount(), std::list()); - if (const GUIListPanel::Item *groupListItem = m_GroupsList->GetSelected()) { + if (const GUIListPanel::Item* groupListItem = m_GroupsList->GetSelected()) { if (m_ModuleSpaceID < 0) { if (g_SettingsMan.ShowForeignItems() || m_NativeTechModuleID <= 0) { for (int moduleID = 0; moduleID < moduleList.size(); ++moduleID) { @@ -379,7 +409,9 @@ namespace RTE { } } else { for (int moduleID = 0; moduleID < moduleList.size(); ++moduleID) { - if (moduleID == 0 || moduleID == m_NativeTechModuleID || g_PresetMan.GetDataModule(moduleID)->IsMerchant()) { g_PresetMan.GetAllOfGroup(moduleList.at(moduleID), groupListItem->m_Name, m_ShowType, moduleID); } + if (moduleID == 0 || moduleID == m_NativeTechModuleID || g_PresetMan.GetDataModule(moduleID)->IsMerchant()) { + g_PresetMan.GetAllOfGroup(moduleList.at(moduleID), groupListItem->m_Name, m_ShowType, moduleID); + } } } } else { @@ -394,16 +426,20 @@ namespace RTE { if (moduleList.at(moduleID).empty()) { continue; } - std::list objectList; - for (Entity *moduleListEntryEntity : moduleList.at(moduleID)) { - SceneObject *sceneObject = dynamic_cast(moduleListEntryEntity); - if (sceneObject && sceneObject->IsBuyable() && !sceneObject->IsBuyableInBuyMenuOnly() && !sceneObject->IsBuyableInScriptOnly()) { objectList.emplace_back(sceneObject); } + std::list objectList; + for (Entity* moduleListEntryEntity: moduleList.at(moduleID)) { + SceneObject* sceneObject = dynamic_cast(moduleListEntryEntity); + if (sceneObject && sceneObject->IsBuyable() && !sceneObject->IsBuyableInBuyMenuOnly() && !sceneObject->IsBuyableInScriptOnly()) { + objectList.emplace_back(sceneObject); + } } if (!objectList.empty()) { - if (moduleID != 0) { AddObjectsListModuleGroup(moduleID); } + if (moduleID != 0) { + AddObjectsListModuleGroup(moduleID); + } if (moduleID == 0 || m_ExpandedModules.at(moduleID)) { - for (SceneObject *objectListEntry : objectList) { - GUIBitmap *objectIcon = new AllegroBitmap(objectListEntry->GetGraphicalIcon()); + for (SceneObject* objectListEntry: objectList) { + GUIBitmap* objectIcon = new AllegroBitmap(objectListEntry->GetGraphicalIcon()); m_ObjectsList->AddItem(objectListEntry->GetPresetName(), objectListEntry->GetGoldValueString(m_NativeTechModuleID, m_ForeignCostMult), objectIcon, objectListEntry); } } @@ -416,23 +452,29 @@ namespace RTE { SelectObjectByIndex(m_SelectedObjectIndex, false); m_ObjectsList->ScrollToSelected(); } - if (const GUIListPanel::Item *selectedItem = m_ObjectsList->GetSelected()) { m_PickedObject = dynamic_cast(selectedItem->m_pEntity); } + if (const GUIListPanel::Item* selectedItem = m_ObjectsList->GetSelected()) { + m_PickedObject = dynamic_cast(selectedItem->m_pEntity); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::Update() { m_PopupBox->SetVisible(false); - if (m_PickerState != PickerState::Enabled && m_PickerState != PickerState::Disabled) { AnimateOpenClose(); } - if (m_PickerState == PickerState::Enabled || m_PickerState == PickerState::Enabling) { m_GUIControlManager->Update(true); } + if (m_PickerState != PickerState::Enabled && m_PickerState != PickerState::Disabled) { + AnimateOpenClose(); + } + if (m_PickerState == PickerState::Enabled || m_PickerState == PickerState::Enabling) { + m_GUIControlManager->Update(true); + } if (m_PickerState == PickerState::Enabled) { m_GUIControlManager->EnableMouse(m_Controller->IsMouseControlled()); m_PickedObject = nullptr; if (HandleInput()) { - if (const GUIListPanel::Item *selectedItem = m_ObjectsList->GetSelected()) { - m_PickedObject = dynamic_cast(selectedItem->m_pEntity); + if (const GUIListPanel::Item* selectedItem = m_ObjectsList->GetSelected()) { + m_PickedObject = dynamic_cast(selectedItem->m_pEntity); if (m_PickedObject) { g_GUISound.ObjectPickedSound()->Play(m_Controller->GetPlayer()); SetEnabled(false); @@ -440,15 +482,19 @@ namespace RTE { } return; } - if (m_PickerFocus == PickerFocus::ObjectList) { ShowDescriptionPopupBox(); } + if (m_PickerFocus == PickerFocus::ObjectList) { + ShowDescriptionPopupBox(); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ObjectPickerGUI::HandleInput() { bool objectPickedOrPickerClosed = false; - if (m_Controller->IsMouseControlled()) { objectPickedOrPickerClosed = HandleMouseEvents(); } + if (m_Controller->IsMouseControlled()) { + objectPickedOrPickerClosed = HandleMouseEvents(); + } if (!objectPickedOrPickerClosed && !m_Controller->IsState(ControlState::PRESS_SECONDARY)) { bool pressUp = m_Controller->IsState(ControlState::PRESS_UP); @@ -493,7 +539,7 @@ namespace RTE { } else if (pressScrollDown) { m_ObjectsList->ScrollDown(); } else if (m_Controller->IsState(ControlState::PRESS_FACEBUTTON)) { - if (const GUIListPanel::Item *objectListItem = m_ObjectsList->GetSelected()) { + if (const GUIListPanel::Item* objectListItem = m_ObjectsList->GetSelected()) { if (objectListItem->m_ExtraIndex >= 0) { ToggleObjectsListModuleGroupExpansion(objectListItem->m_ExtraIndex); } else { @@ -510,7 +556,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ObjectPickerGUI::HandleMouseEvents() { int mousePosX; @@ -524,8 +570,10 @@ namespace RTE { if (guiEvent.GetMsg() == GUIListBox::MouseDown && (guiEvent.GetData() & GUIListBox::MOUSE_LEFT)) { SelectGroupByIndex(m_GroupsList->GetSelectedIndex()); } else if (guiEvent.GetMsg() == GUIListBox::MouseMove) { - const GUIListPanel::Item *groupListItem = m_GroupsList->GetItem(mousePosX, mousePosY); - if (groupListItem && m_SelectedGroupIndex != groupListItem->m_ID) { SelectGroupByIndex(groupListItem->m_ID, false); } + const GUIListPanel::Item* groupListItem = m_GroupsList->GetItem(mousePosX, mousePosY); + if (groupListItem && m_SelectedGroupIndex != groupListItem->m_ID) { + SelectGroupByIndex(groupListItem->m_ID, false); + } } else if (guiEvent.GetMsg() == GUIListBox::MouseEnter) { SetListFocus(PickerFocus::GroupList); } else if (guiEvent.GetMsg() == GUIListBox::MouseLeave && m_SelectedGroupIndex != m_ShownGroupIndex) { @@ -534,7 +582,7 @@ namespace RTE { } else if (guiEvent.GetControl() == m_ObjectsList) { if (guiEvent.GetMsg() == GUIListBox::MouseDown && (guiEvent.GetData() & GUIListBox::MOUSE_LEFT)) { m_ObjectsList->ScrollToSelected(); - if (const GUIListPanel::Item *objectListItem = m_ObjectsList->GetSelected()) { + if (const GUIListPanel::Item* objectListItem = m_ObjectsList->GetSelected()) { if (objectListItem->m_ExtraIndex >= 0) { ToggleObjectsListModuleGroupExpansion(objectListItem->m_ExtraIndex); } else if (objectListItem->m_pEntity) { @@ -543,8 +591,10 @@ namespace RTE { } } } else if (guiEvent.GetMsg() == GUIListBox::MouseMove) { - const GUIListPanel::Item *objectListItem = m_ObjectsList->GetItem(mousePosX, mousePosY); - if (objectListItem && m_SelectedObjectIndex != objectListItem->m_ID) { SelectObjectByIndex(objectListItem->m_ID); } + const GUIListPanel::Item* objectListItem = m_ObjectsList->GetItem(mousePosX, mousePosY); + if (objectListItem && m_SelectedObjectIndex != objectListItem->m_ID) { + SelectObjectByIndex(objectListItem->m_ID); + } } else if (guiEvent.GetMsg() == GUIListBox::MouseEnter) { SetListFocus(PickerFocus::ObjectList); } @@ -558,7 +608,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ObjectPickerGUI::AnimateOpenClose() { if (m_PickerState == PickerState::Enabling) { @@ -591,9 +641,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::Draw(BITMAP *drawBitmap) const { + void ObjectPickerGUI::Draw(BITMAP* drawBitmap) const { AllegroScreen drawScreen(drawBitmap); m_GUIControlManager->Draw(&drawScreen); if (IsEnabled() && m_Controller->IsMouseControlled()) { @@ -607,4 +657,4 @@ namespace RTE { } } } -} +} // namespace RTE diff --git a/Source/Menus/ObjectPickerGUI.h b/Source/Menus/ObjectPickerGUI.h index 876963e533..d78853450d 100644 --- a/Source/Menus/ObjectPickerGUI.h +++ b/Source/Menus/ObjectPickerGUI.h @@ -22,7 +22,6 @@ namespace RTE { class ObjectPickerGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a ObjectPickerGUI object in system memory. Create() should be called before using the object. @@ -36,7 +35,7 @@ namespace RTE { /// Which DataModule space to be picking from. -1 means pick from all objects loaded in all DataModules. /// Which lowest common denominator type to be showing. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(Controller *controller, int whichModuleSpace = -1, const std::string_view &onlyOfType = "All"); + int Create(Controller* controller, int whichModuleSpace = -1, const std::string_view& onlyOfType = "All"); #pragma endregion #pragma region Destruction @@ -69,7 +68,7 @@ namespace RTE { /// Sets the controller used by this. The ownership of the controller is NOT transferred! /// /// The new controller for this menu. Ownership is NOT transferred! - void SetController(Controller *controller) { m_Controller = controller; } + void SetController(Controller* controller) { m_Controller = controller; } /// /// Sets where on the screen that this GUI is being drawn to. If upper left corner, then 0, 0. This will affect the way the mouse is positioned etc. @@ -82,13 +81,21 @@ namespace RTE { /// Sets which DataModule space to be picking objects from. If -1, then let the player pick from all loaded modules. /// /// The ID of the module to let the player pick objects from. All official module objects will always be presented, in addition to the one passed in here. - void SetModuleSpace(int newModuleSpaceID = -1) { if (newModuleSpaceID != m_ModuleSpaceID) { m_ModuleSpaceID = newModuleSpaceID; UpdateGroupsList(); } } + void SetModuleSpace(int newModuleSpaceID = -1) { + if (newModuleSpaceID != m_ModuleSpaceID) { + m_ModuleSpaceID = newModuleSpaceID; + UpdateGroupsList(); + } + } /// /// Sets which DataModule space to be picking objects from. If -1, then let the player pick from all loaded modules. /// /// The ID of the module to let the player pick objects from. All official module objects will always be presented, in addition to the one passed in here. - void ShowOnlyType(const std::string_view &showType = "All") { m_ShowType = showType; UpdateGroupsList(); } + void ShowOnlyType(const std::string_view& showType = "All") { + m_ShowType = showType; + UpdateGroupsList(); + } /// /// Sets which DataModule ID should be treated as the native tech of the user of this menu. @@ -108,7 +115,7 @@ namespace RTE { /// /// The name of the group to select in the picker. /// Whether the group was found and switched to successfully. - bool SelectGroupByName(const std::string_view &groupName); + bool SelectGroupByName(const std::string_view& groupName); #pragma endregion #pragma region Object Picking Handling @@ -116,25 +123,33 @@ namespace RTE { /// Gets the next object in the objects list, even if the picker is disabled. /// /// The next object in the picker list, looping around if necessary. If the next object is an invalid SceneObject (e.g. a module subgroup) then this will recurse until a valid object is found. - const SceneObject * GetNextObject() { SelectNextOrPrevObject(false); const SceneObject *object = GetSelectedObject(); return object ? object : GetNextObject(); } + const SceneObject* GetNextObject() { + SelectNextOrPrevObject(false); + const SceneObject* object = GetSelectedObject(); + return object ? object : GetNextObject(); + } /// /// Gets the previous object in the objects list, even if the picker is disabled. /// /// The previous object in the picker list, looping around if necessary. If the previous object is an invalid SceneObject (e.g. a module subgroup) then this will recurse until a valid object is found. - const SceneObject * GetPrevObject() { SelectNextOrPrevObject(true); const SceneObject *object = GetSelectedObject(); return object ? object : GetPrevObject(); } + const SceneObject* GetPrevObject() { + SelectNextOrPrevObject(true); + const SceneObject* object = GetSelectedObject(); + return object ? object : GetPrevObject(); + } /// /// Reports whether and which object has been picked by the player. There may be an object picked even when the player is not done with the picker, as scrolling through objects (but not mousing over them) picks them. /// /// A pointer to the object picked by the player, or nullptr if none was picked. Ownership is NOT transferred! - const SceneObject * ObjectPicked() const { return m_PickedObject; } + const SceneObject* ObjectPicked() const { return m_PickedObject; } /// /// Reports whether the player has finished using the picker, and the final picked object is returned. /// /// The object the player picked before they closed the picker, or nullptr if none was picked. Ownership is NOT transferred!< / returns> - const SceneObject * DonePicking() const { return (!IsEnabled() && m_PickedObject) ? m_PickedObject : nullptr; } + const SceneObject* DonePicking() const { return (!IsEnabled() && m_PickedObject) ? m_PickedObject : nullptr; } #pragma endregion #pragma region Concrete Methods @@ -147,33 +162,40 @@ namespace RTE { /// Draws the ObjectPickerGUI to the specified BITMAP. /// /// The BITMAP to draw on. - void Draw(BITMAP *drawBitmap) const; + void Draw(BITMAP* drawBitmap) const; #pragma endregion private: - /// /// Enumeration for ObjectPicker states when enabling/disabling the ObjectPicker. /// - enum class PickerState { Enabling, Enabled, Disabling, Disabled }; + enum class PickerState { + Enabling, + Enabled, + Disabling, + Disabled + }; /// /// Enumeration for the ObjectPicker columns ListBox focus states. /// - enum class PickerFocus { GroupList, ObjectList }; + enum class PickerFocus { + GroupList, + ObjectList + }; - static BITMAP *s_Cursor; //!< The cursor image shared by all pickers. + static BITMAP* s_Cursor; //!< The cursor image shared by all pickers. std::unique_ptr m_GUIScreen; //!< The GUIScreen interface that will be used by this ObjectPickerGUI's GUIControlManager. std::unique_ptr m_GUIInput; //!< The GUIInput interface that will be used by this ObjectPickerGUI's GUIControlManager. std::unique_ptr m_GUIControlManager; //!< The control manager which holds all the controls. - GUICollectionBox *m_ParentBox; //!< Collection box of the picker GUI. - GUICollectionBox *m_PopupBox; //!< Collection box of the buy popups that contain information about items. - GUILabel *m_PopupText; //!< Label displaying the item popup description. - GUIListBox *m_GroupsList; //!< The ListBox which lists all the groups. - GUIListBox *m_ObjectsList; //!< The ListBox which lists all the objects in the currently selected group. + GUICollectionBox* m_ParentBox; //!< Collection box of the picker GUI. + GUICollectionBox* m_PopupBox; //!< Collection box of the buy popups that contain information about items. + GUILabel* m_PopupText; //!< Label displaying the item popup description. + GUIListBox* m_GroupsList; //!< The ListBox which lists all the groups. + GUIListBox* m_ObjectsList; //!< The ListBox which lists all the objects in the currently selected group. - Controller *m_Controller; //!< Controller which controls this menu. Not owned. + Controller* m_Controller; //!< Controller which controls this menu. Not owned. PickerState m_PickerState; //!< Visibility state of the object picker. PickerFocus m_PickerFocus; //!< The currently focused list in the Picker. @@ -187,7 +209,7 @@ namespace RTE { int m_ShownGroupIndex; //!< Which group in the groups list is currently showing it's objects list. int m_SelectedGroupIndex; //!< Which group in the groups list box we have selected. int m_SelectedObjectIndex; //!< Which object in the objects list box we have selected. - const SceneObject *m_PickedObject; //!< Currently picked object. This may be a valid object even if the player is not done with the picker, as scrolling through objects (but not mousing over them) picks them. Not owned by this. + const SceneObject* m_PickedObject; //!< Currently picked object. This may be a valid object even if the player is not done with the picker, as scrolling through objects (but not mousing over them) picks them. Not owned by this. Timer m_RepeatStartTimer; //!< Measures the time to when to start repeating inputs when they're held down. Timer m_RepeatTimer; //!< Measures the interval between input repeats. @@ -228,7 +250,7 @@ namespace RTE { /// Gets the SceneObject from the currently selected index in the objects list. Ownership is NOT transferred! /// /// The SceneObject of the currently selected index in the objects list. Nullptr if no valid object is selected (eg. a module subgroup). - const SceneObject * GetSelectedObject(); + const SceneObject* GetSelectedObject(); /// /// Selects the specified object index in the objects list. @@ -299,8 +321,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - ObjectPickerGUI(const ObjectPickerGUI &reference) = delete; - ObjectPickerGUI & operator=(const ObjectPickerGUI &rhs) = delete; + ObjectPickerGUI(const ObjectPickerGUI& reference) = delete; + ObjectPickerGUI& operator=(const ObjectPickerGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/PauseMenuGUI.cpp b/Source/Menus/PauseMenuGUI.cpp index d80298547b..0bba284d62 100644 --- a/Source/Menus/PauseMenuGUI.cpp +++ b/Source/Menus/PauseMenuGUI.cpp @@ -21,7 +21,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PauseMenuGUI::Clear() { m_GUIControlManager = nullptr; @@ -49,25 +49,25 @@ namespace RTE { m_PauseMenuButtons.fill(nullptr); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::Create(AllegroScreen *guiScreen, GUIInputWrapper *guiInput) { + void PauseMenuGUI::Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuScreenSkin.ini"); m_GUIControlManager->Load("Base.rte/GUIs/PauseMenuGUI.ini"); int rootBoxMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); - dynamic_cast(m_GUIControlManager->GetControl("root"))->Resize(rootBoxMaxWidth, g_WindowMan.GetResY()); + dynamic_cast(m_GUIControlManager->GetControl("root"))->Resize(rootBoxMaxWidth, g_WindowMan.GetResY()); - m_PauseMenuBox = dynamic_cast(m_GUIControlManager->GetControl("PauseScreen")); + m_PauseMenuBox = dynamic_cast(m_GUIControlManager->GetControl("PauseScreen")); m_PauseMenuBox->CenterInParent(true, true); - m_PauseMenuButtons[PauseMenuButton::BackToMainButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMain")); - m_PauseMenuButtons[PauseMenuButton::SaveOrLoadGameButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonSaveOrLoadGame")); - m_PauseMenuButtons[PauseMenuButton::SettingsButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonSettings")); - m_PauseMenuButtons[PauseMenuButton::ModManagerButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonModManager")); - m_PauseMenuButtons[PauseMenuButton::ResumeButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonResume")); + m_PauseMenuButtons[PauseMenuButton::BackToMainButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMain")); + m_PauseMenuButtons[PauseMenuButton::SaveOrLoadGameButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonSaveOrLoadGame")); + m_PauseMenuButtons[PauseMenuButton::SettingsButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonSettings")); + m_PauseMenuButtons[PauseMenuButton::ModManagerButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonModManager")); + m_PauseMenuButtons[PauseMenuButton::ResumeButton] = dynamic_cast(m_GUIControlManager->GetControl("ButtonResume")); for (size_t pauseMenuButton = 0; pauseMenuButton < m_PauseMenuButtons.size(); ++pauseMenuButton) { std::string buttonText = m_PauseMenuButtons[pauseMenuButton]->GetText(); @@ -84,7 +84,7 @@ namespace RTE { if (m_BackdropBitmap) { destroy_bitmap(m_BackdropBitmap); } - const BITMAP *backbuffer = g_FrameMan.GetBackBuffer32(); + const BITMAP* backbuffer = g_FrameMan.GetBackBuffer32(); m_BackdropBitmap = create_bitmap_ex(FrameMan::c_BPP, backbuffer->w, backbuffer->h); unsigned int halfTransBlack = makeacol32(0, 0, 0, 96); clear_to_color(m_BackdropBitmap, halfTransBlack); @@ -94,9 +94,9 @@ namespace RTE { m_ModManagerMenu = std::make_unique(guiScreen, guiInput, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::SetBackButtonTargetName(const std::string &menuName) { + void PauseMenuGUI::SetBackButtonTargetName(const std::string& menuName) { std::string newButtonText = "Back to " + menuName + " Menu"; std::transform(newButtonText.begin(), newButtonText.end(), newButtonText.begin(), ::toupper); @@ -111,17 +111,17 @@ namespace RTE { m_PauseMenuButtons[PauseMenuButton::BackToMainButton]->CenterInParent(true, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PauseMenuGUI::EnableOrDisablePauseMenuFeatures() { bool disableModManager = true; - if (const Activity *activity = g_ActivityMan.GetActivity(); activity) { + if (const Activity* activity = g_ActivityMan.GetActivity(); activity) { disableModManager = activity->GetClassName() != "GAScripted"; } if (m_ModManagerButtonDisabled != disableModManager) { - GUIButton *modManagerButton = m_PauseMenuButtons[PauseMenuButton::ModManagerButton]; + GUIButton* modManagerButton = m_PauseMenuButtons[PauseMenuButton::ModManagerButton]; modManagerButton->SetEnabled(!disableModManager); modManagerButton->SetVisible(!disableModManager); @@ -135,7 +135,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PauseMenuGUI::SetActiveMenuScreen(PauseMenuScreen screenToShow, bool playButtonPressSound) { if (screenToShow != m_ActiveMenuScreen) { @@ -149,7 +149,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// PauseMenuGUI::PauseMenuUpdateResult PauseMenuGUI::Update() { m_UpdateResult = PauseMenuUpdateResult::NoEvent; @@ -183,7 +183,7 @@ namespace RTE { return m_UpdateResult; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PauseMenuGUI::HandleBackNavigation(bool backButtonPressed) { if (!m_ActiveDialogBox && (backButtonPressed || g_UInputMan.KeyPressed(SDLK_ESCAPE))) { @@ -205,14 +205,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool PauseMenuGUI::HandleInputEvents() { if (m_ActiveMenuScreen == PauseMenuScreen::MainScreen) { int mousePosX; int mousePosY; m_GUIControlManager->GetManager()->GetInputController()->GetMousePosition(&mousePosX, &mousePosY); - UpdateHoveredButton(dynamic_cast(m_GUIControlManager->GetControlUnderPoint(mousePosX, mousePosY, m_PauseMenuBox, 1))); + UpdateHoveredButton(dynamic_cast(m_GUIControlManager->GetControlUnderPoint(mousePosX, mousePosY, m_PauseMenuBox, 1))); } m_GUIControlManager->Update(); @@ -232,16 +232,16 @@ namespace RTE { m_UpdateResult = PauseMenuUpdateResult::BackToMain; } } - if (guiEvent.GetType() == GUIEvent::Notification && (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl()))) { + if (guiEvent.GetType() == GUIEvent::Notification && (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl()))) { g_GUISound.SelectionChangeSound()->Play(); } } return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::UpdateHoveredButton(const GUIButton *hoveredButton) { + void PauseMenuGUI::UpdateHoveredButton(const GUIButton* hoveredButton) { int hoveredButtonIndex = -1; if (hoveredButton) { hoveredButtonIndex = std::distance(m_PauseMenuButtons.begin(), std::find(m_PauseMenuButtons.begin(), m_PauseMenuButtons.end(), hoveredButton)); @@ -261,7 +261,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PauseMenuGUI::BlinkResumeButton() { if (m_HoveredButton && m_HoveredButton == m_PauseMenuButtons[PauseMenuButton::ResumeButton]) { @@ -271,7 +271,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PauseMenuGUI::Draw() { g_WindowMan.DrawPostProcessBuffer(); @@ -298,4 +298,4 @@ namespace RTE { } m_GUIControlManager->DrawMouse(); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/PauseMenuGUI.h b/Source/Menus/PauseMenuGUI.h index f1278b7233..a4708b94d3 100644 --- a/Source/Menus/PauseMenuGUI.h +++ b/Source/Menus/PauseMenuGUI.h @@ -22,7 +22,6 @@ namespace RTE { class PauseMenuGUI { public: - /// /// Enumeration for the results of the PauseMenuGUI input and event update. /// @@ -38,14 +37,17 @@ namespace RTE { /// /// Pointer to a GUIScreen interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! - PauseMenuGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput) { Clear(); Create(guiScreen, guiInput); } + PauseMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { + Clear(); + Create(guiScreen, guiInput); + } /// /// Makes the PauseMenuGUI object ready for use. /// /// Pointer to a GUIScreen interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! - void Create(AllegroScreen *guiScreen, GUIInputWrapper *guiInput); + void Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput); #pragma endregion #pragma region Setters @@ -53,7 +55,7 @@ namespace RTE { /// Sets the "Back to Main Menu" button text to the menu we will be going back to. /// /// The target menu name, e.g. "Conquest" will result in "Back to Conquest Menu". - void SetBackButtonTargetName(const std::string &menuName); + void SetBackButtonTargetName(const std::string& menuName); #pragma endregion #pragma region Concrete Methods @@ -75,7 +77,6 @@ namespace RTE { #pragma endregion private: - /// /// Enumeration for the different sub-menu screens of the pause menu. /// @@ -100,9 +101,9 @@ namespace RTE { }; std::unique_ptr m_GUIControlManager; //!< The GUIControlManager which owns all the GUIControls of the PauseMenuGUI. - GUICollectionBox *m_ActiveDialogBox; // The currently active GUICollectionBox in any of the pause menu screens that acts as a dialog box and requires drawing an overlay. + GUICollectionBox* m_ActiveDialogBox; // The currently active GUICollectionBox in any of the pause menu screens that acts as a dialog box and requires drawing an overlay. - BITMAP *m_BackdropBitmap; ///!< Bitmap to store half transparent black overlay. + BITMAP* m_BackdropBitmap; ///!< Bitmap to store half transparent black overlay. PauseMenuScreen m_ActiveMenuScreen; //!< The currently active pause menu screen that is being updated and drawn to the screen. See PauseMenuScreen enumeration. PauseMenuUpdateResult m_UpdateResult; //!< The result of the PauseMenuGUI update. See PauseMenuUpdateResult enumeration. @@ -117,7 +118,7 @@ namespace RTE { // Right now the way this works is the font graphic has different character visuals for uppercase and lowercase and the visual change happens by applying the appropriate case string when hovering/unhovering. std::array m_ButtonHoveredText; //!< Array containing uppercase strings of the pause menu buttons text that are used to display the larger font when a button is hovered over. std::array m_ButtonUnhoveredText; //!< Array containing lowercase strings of the pause menu buttons text that are used to display the smaller font when a button is not hovered over. - GUIButton *m_HoveredButton; //!< The currently hovered pause menu button. + GUIButton* m_HoveredButton; //!< The currently hovered pause menu button. int m_PrevHoveredButtonIndex; //!< The index of the previously hovered pause menu button in the main menu button array. bool m_SavingButtonsDisabled; //!< Whether the save and load buttons are disabled and hidden. @@ -126,8 +127,8 @@ namespace RTE { /// /// GUI elements that compose the pause menu screen. /// - GUICollectionBox *m_PauseMenuBox; - std::array m_PauseMenuButtons; + GUICollectionBox* m_PauseMenuBox; + std::array m_PauseMenuButtons; #pragma region Menu Screen Handling /// @@ -155,7 +156,7 @@ namespace RTE { /// Updates the currently hovered button text to give the hovered visual and updates the previously hovered button to remove the hovered visual. /// /// Pointer to the currently hovered button, if any. Acquired by GUIControlManager::GetControlUnderPoint. - void UpdateHoveredButton(const GUIButton *hoveredButton); + void UpdateHoveredButton(const GUIButton* hoveredButton); /// /// Animates (blinking) the resume game button. @@ -169,8 +170,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - PauseMenuGUI(const PauseMenuGUI &reference) = delete; - PauseMenuGUI & operator=(const PauseMenuGUI &rhs) = delete; + PauseMenuGUI(const PauseMenuGUI& reference) = delete; + PauseMenuGUI& operator=(const PauseMenuGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/SaveLoadMenuGUI.cpp b/Source/Menus/SaveLoadMenuGUI.cpp index 421198fd0e..f33229c5a2 100644 --- a/Source/Menus/SaveLoadMenuGUI.cpp +++ b/Source/Menus/SaveLoadMenuGUI.cpp @@ -21,19 +21,19 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SaveLoadMenuGUI::SaveLoadMenuGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool createForPauseMenu) { + SaveLoadMenuGUI::SaveLoadMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); m_GUIControlManager->Load("Base.rte/GUIs/SaveLoadMenuGUI.ini"); int rootBoxMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); - GUICollectionBox *rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); + GUICollectionBox* rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); rootBox->Resize(rootBoxMaxWidth, g_WindowMan.GetResY()); - m_SaveGameMenuBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSaveGameMenu")); + m_SaveGameMenuBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSaveGameMenu")); m_SaveGameMenuBox->CenterInParent(true, true); m_SaveGameMenuBox->SetPositionAbs(m_SaveGameMenuBox->GetXPos(), (rootBox->GetHeight() < 540) ? m_SaveGameMenuBox->GetYPos() - 15 : 140); @@ -41,9 +41,9 @@ namespace RTE { m_OrderByComboBox->AddItem("Name"); m_OrderByComboBox->AddItem("Date"); m_OrderByComboBox->AddItem("Activity"); - m_OrderByComboBox->SetSelectedIndex(1); //order by Date by default + m_OrderByComboBox->SetSelectedIndex(1); // order by Date by default - m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMainMenu")); + m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMainMenu")); if (createForPauseMenu) { m_BackToMainButton->SetSize(120, 20); @@ -51,39 +51,39 @@ namespace RTE { } m_BackToMainButton->SetPositionAbs((rootBox->GetWidth() - m_BackToMainButton->GetWidth()) / 2, m_SaveGameMenuBox->GetYPos() + m_SaveGameMenuBox->GetHeight() + 10); - m_SaveGamesListBox = dynamic_cast(m_GUIControlManager->GetControl("ListBoxSaveGames")); + m_SaveGamesListBox = dynamic_cast(m_GUIControlManager->GetControl("ListBoxSaveGames")); m_SaveGamesListBox->SetFont(m_GUIControlManager->GetSkin()->GetFont("FontConsoleMonospace.png")); m_SaveGamesListBox->SetMouseScrolling(true); m_SaveGamesListBox->SetScrollBarThickness(15); m_SaveGamesListBox->SetScrollBarPadding(2); - m_SaveGameName = dynamic_cast(m_GUIControlManager->GetControl("SaveGameName")); - m_LoadButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonLoad")); - m_CreateButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCreate")); - m_OverwriteButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonOverwrite")); - m_DeleteButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonDelete")); - m_ActivityCannotBeSavedLabel = dynamic_cast(m_GUIControlManager->GetControl("ActivityCannotBeSavedWarning")); + m_SaveGameName = dynamic_cast(m_GUIControlManager->GetControl("SaveGameName")); + m_LoadButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonLoad")); + m_CreateButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCreate")); + m_OverwriteButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonOverwrite")); + m_DeleteButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonDelete")); + m_ActivityCannotBeSavedLabel = dynamic_cast(m_GUIControlManager->GetControl("ActivityCannotBeSavedWarning")); - m_ConfirmationBox = dynamic_cast(m_GUIControlManager->GetControl("ConfirmDialog")); + m_ConfirmationBox = dynamic_cast(m_GUIControlManager->GetControl("ConfirmDialog")); m_ConfirmationBox->CenterInParent(true, true); - m_ConfirmationLabel = dynamic_cast(m_GUIControlManager->GetControl("ConfirmLabel")); - m_ConfirmationButton = dynamic_cast(m_GUIControlManager->GetControl("ConfirmButton")); - m_CancelButton = dynamic_cast(m_GUIControlManager->GetControl("CancelButton")); + m_ConfirmationLabel = dynamic_cast(m_GUIControlManager->GetControl("ConfirmLabel")); + m_ConfirmationButton = dynamic_cast(m_GUIControlManager->GetControl("ConfirmButton")); + m_CancelButton = dynamic_cast(m_GUIControlManager->GetControl("CancelButton")); SwitchToConfirmDialogMode(ConfirmDialogMode::None); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SaveLoadMenuGUI::PopulateSaveGamesList() { m_SaveGames.clear(); m_SaveGameName->SetText(""); - + m_GUIControlManager->GetManager()->SetFocus(nullptr); std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/"; - for (const auto &entry : std::filesystem::directory_iterator(saveFilePath)) { + for (const auto& entry: std::filesystem::directory_iterator(saveFilePath)) { if (entry.path().extension() == ".ccsave" && entry.path().filename() != "Index.ini") { SaveRecord record; record.SavePath = entry.path(); @@ -93,42 +93,41 @@ namespace RTE { } std::for_each(std::execution::par_unseq, - m_SaveGames.begin(), m_SaveGames.end(), - [](SaveRecord &record) { - Reader reader(record.SavePath.string(), true, nullptr, true); - - bool readActivity = false; - bool readSceneName = false; - - GAScripted activity; - - std::string originalScenePresetName; - while (reader.NextProperty()) { - std::string propName = reader.ReadPropName(); - if (propName == "Activity") { - reader >> activity; - readActivity = true; - } else if (propName == "OriginalScenePresetName") { - reader >> originalScenePresetName; - readSceneName = true; - } - - if (readActivity && readSceneName) { - break; - } - } - - record.Activity = activity.GetPresetName(); - record.Scene = originalScenePresetName; - }); + m_SaveGames.begin(), m_SaveGames.end(), + [](SaveRecord& record) { + Reader reader(record.SavePath.string(), true, nullptr, true); + + bool readActivity = false; + bool readSceneName = false; + + GAScripted activity; + + std::string originalScenePresetName; + while (reader.NextProperty()) { + std::string propName = reader.ReadPropName(); + if (propName == "Activity") { + reader >> activity; + readActivity = true; + } else if (propName == "OriginalScenePresetName") { + reader >> originalScenePresetName; + readSceneName = true; + } + + if (readActivity && readSceneName) { + break; + } + } + + record.Activity = activity.GetPresetName(); + record.Scene = originalScenePresetName; + }); UpdateSaveGamesGUIList(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::UpdateSaveGamesGUIList() - { + void SaveLoadMenuGUI::UpdateSaveGamesGUIList() { const std::string& currentOrder = m_OrderByComboBox->GetSelectedItem()->m_Name; if (currentOrder == "Name") { std::stable_sort(m_SaveGames.begin(), m_SaveGames.end(), [](const SaveRecord& lhs, const SaveRecord& rhs) { return lhs.SavePath.stem().string() < rhs.SavePath.stem().string(); }); @@ -140,7 +139,7 @@ namespace RTE { m_SaveGamesListBox->ClearList(); for (int i = 0; i < m_SaveGames.size(); i++) { - const SaveRecord &save = m_SaveGames[i]; + const SaveRecord& save = m_SaveGames[i]; std::stringstream saveNameText; saveNameText << std::left << std::setfill(' ') << std::setw(32) << save.SavePath.stem().string(); @@ -149,16 +148,16 @@ namespace RTE { #if defined(_MSC_VER) || __GNUC__ > 12 const auto saveFsTime = std::chrono::clock_cast(save.SaveDate); const auto saveTime = std::chrono::system_clock::to_time_t(saveFsTime); -#else +#else // TODO - kill this monstrosity when we move to GCC13 auto saveFsTime = std::chrono::system_clock::time_point(save.SaveDate.time_since_epoch()); - #ifdef _WIN32 +#ifdef _WIN32 // Windows epoch time are the number of seconds since... 1601-01-01 00:00:00. Seriously. saveFsTime -= std::chrono::seconds(11644473600LL); - #elif defined(__GLIBCXX__) +#elif defined(__GLIBCXX__) // libstdc++ file_clock epoch is 2174-01-01 00:00:00 for reasons saveFsTime += std::chrono::seconds(6437664000LL); - #endif +#endif const auto saveTime = std::chrono::system_clock::to_time_t(saveFsTime); #endif const auto saveTimeLocal = std::localtime(&saveTime); @@ -172,7 +171,7 @@ namespace RTE { m_SaveGamesListBox->ScrollToTop(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SaveLoadMenuGUI::LoadSave() { bool success = g_ActivityMan.LoadAndLaunchGame(m_SaveGameName->GetText()); @@ -186,7 +185,7 @@ namespace RTE { return success; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SaveLoadMenuGUI::CreateSave() { bool success = g_ActivityMan.SaveCurrentGame(m_SaveGameName->GetText()); @@ -199,7 +198,7 @@ namespace RTE { PopulateSaveGamesList(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SaveLoadMenuGUI::DeleteSave() { std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + m_SaveGameName->GetText() + ".ccsave"; @@ -210,7 +209,7 @@ namespace RTE { PopulateSaveGamesList(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SaveLoadMenuGUI::UpdateButtonEnabledStates() { bool allowSave = g_ActivityMan.GetActivity() && g_ActivityMan.GetActivity()->GetAllowsUserSaving() && m_SaveGameName->GetText() != ""; @@ -243,7 +242,7 @@ namespace RTE { m_ActivityCannotBeSavedLabel->SetVisible(g_ActivityMan.GetActivity() && !g_ActivityMan.GetActivity()->GetAllowsUserSaving()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SaveLoadMenuGUI::SwitchToConfirmDialogMode(ConfirmDialogMode mode) { m_ConfirmDialogMode = mode; @@ -254,18 +253,18 @@ namespace RTE { m_ConfirmationBox->SetVisible(dialogOpen); switch (m_ConfirmDialogMode) { - case ConfirmDialogMode::ConfirmOverwrite: - m_ConfirmationLabel->SetText("Are you sure you want to overwrite this savegame?"); - break; - case ConfirmDialogMode::ConfirmDelete: - m_ConfirmationLabel->SetText("Are you sure you want to delete this savegame?"); - break; + case ConfirmDialogMode::ConfirmOverwrite: + m_ConfirmationLabel->SetText("Are you sure you want to overwrite this savegame?"); + break; + case ConfirmDialogMode::ConfirmDelete: + m_ConfirmationLabel->SetText("Are you sure you want to delete this savegame?"); + break; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SaveLoadMenuGUI::HandleInputEvents(PauseMenuGUI *pauseMenu) { + bool SaveLoadMenuGUI::HandleInputEvents(PauseMenuGUI* pauseMenu) { m_GUIControlManager->Update(); GUIEvent guiEvent; @@ -286,24 +285,24 @@ namespace RTE { SwitchToConfirmDialogMode(ConfirmDialogMode::ConfirmDelete); } else if (guiEvent.GetControl() == m_ConfirmationButton) { switch (m_ConfirmDialogMode) { - case ConfirmDialogMode::ConfirmOverwrite: - CreateSave(); - break; - case ConfirmDialogMode::ConfirmDelete: - DeleteSave(); - break; + case ConfirmDialogMode::ConfirmOverwrite: + CreateSave(); + break; + case ConfirmDialogMode::ConfirmDelete: + DeleteSave(); + break; } SwitchToConfirmDialogMode(ConfirmDialogMode::None); } else if (guiEvent.GetControl() == m_CancelButton) { SwitchToConfirmDialogMode(ConfirmDialogMode::None); } } else if (guiEvent.GetType() == GUIEvent::Notification) { - if (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl())) { - g_GUISound.SelectionChangeSound()->Play(); + if (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl())) { + g_GUISound.SelectionChangeSound()->Play(); } if (guiEvent.GetControl() == m_SaveGamesListBox && (guiEvent.GetMsg() == GUIListBox::Select && m_SaveGamesListBox->GetSelectedIndex() > -1)) { - const SaveRecord &record = m_SaveGames[m_SaveGamesListBox->GetSelected()->m_ExtraIndex]; + const SaveRecord& record = m_SaveGames[m_SaveGamesListBox->GetSelected()->m_ExtraIndex]; m_SaveGameName->SetText(record.SavePath.stem().string()); } @@ -318,16 +317,16 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SaveLoadMenuGUI::Refresh() { PopulateSaveGamesList(); UpdateButtonEnabledStates(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SaveLoadMenuGUI::Draw() const { m_GUIControlManager->Draw(); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/SaveLoadMenuGUI.h b/Source/Menus/SaveLoadMenuGUI.h index 3b95328dea..73caabca16 100644 --- a/Source/Menus/SaveLoadMenuGUI.h +++ b/Source/Menus/SaveLoadMenuGUI.h @@ -23,7 +23,6 @@ namespace RTE { class SaveLoadMenuGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a SaveLoadMenuGUI object in system memory and make it ready for use. @@ -31,7 +30,7 @@ namespace RTE { /// Pointer to a GUIScreen interface that will be used by this SaveLoadMenuGUI's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this SaveLoadMenuGUI's GUIControlManager. Ownership is NOT transferred! /// Whether this SettingsGUI is part of SaveLoadMenuGUI and should have a slightly different layout. - SaveLoadMenuGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool createForPauseMenu = false); + SaveLoadMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu = false); #pragma endregion #pragma region Concrete Methods @@ -40,7 +39,7 @@ namespace RTE { /// /// Pointer to the pause menu, if we're being called from the pause menu. Ownership is NOT transferred! /// Whether the player requested to return to the main menu. - bool HandleInputEvents(PauseMenuGUI *pauseMenu = nullptr); + bool HandleInputEvents(PauseMenuGUI* pauseMenu = nullptr); /// /// Causes a refresh of the save files. @@ -79,23 +78,23 @@ namespace RTE { /// /// GUI elements that compose the Mod Manager menu screen. /// - GUICollectionBox *m_SaveGameMenuBox; - GUIButton *m_BackToMainButton; - GUITextBox *m_SaveGameName; - GUIButton *m_LoadButton; - GUIButton *m_CreateButton; - GUIButton *m_OverwriteButton; - GUIButton *m_DeleteButton; - GUIListBox *m_SaveGamesListBox; - GUILabel *m_ActivityCannotBeSavedLabel; - GUIComboBox *m_OrderByComboBox; + GUICollectionBox* m_SaveGameMenuBox; + GUIButton* m_BackToMainButton; + GUITextBox* m_SaveGameName; + GUIButton* m_LoadButton; + GUIButton* m_CreateButton; + GUIButton* m_OverwriteButton; + GUIButton* m_DeleteButton; + GUIListBox* m_SaveGamesListBox; + GUILabel* m_ActivityCannotBeSavedLabel; + GUIComboBox* m_OrderByComboBox; // The confirmation box and its controls ConfirmDialogMode m_ConfirmDialogMode; - GUICollectionBox *m_ConfirmationBox; - GUILabel *m_ConfirmationLabel; + GUICollectionBox* m_ConfirmationBox; + GUILabel* m_ConfirmationLabel; GUIButton* m_ConfirmationButton; - GUIButton *m_CancelButton; + GUIButton* m_CancelButton; #pragma region Savegame Handling /// @@ -142,8 +141,8 @@ namespace RTE { void SwitchToConfirmDialogMode(ConfirmDialogMode mode); // Disallow the use of some implicit methods. - SaveLoadMenuGUI(const SaveLoadMenuGUI &reference) = delete; - SaveLoadMenuGUI & operator=(const SaveLoadMenuGUI &rhs) = delete; + SaveLoadMenuGUI(const SaveLoadMenuGUI& reference) = delete; + SaveLoadMenuGUI& operator=(const SaveLoadMenuGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/ScenarioActivityConfigGUI.cpp b/Source/Menus/ScenarioActivityConfigGUI.cpp index f73b40c94a..272fda0dd8 100644 --- a/Source/Menus/ScenarioActivityConfigGUI.cpp +++ b/Source/Menus/ScenarioActivityConfigGUI.cpp @@ -19,56 +19,57 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ScenarioActivityConfigGUI::ScenarioActivityConfigGUI(GUIControlManager *parentControlManager) : m_GUIControlManager(parentControlManager) { - m_ActivityConfigBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxActivityConfig")); + ScenarioActivityConfigGUI::ScenarioActivityConfigGUI(GUIControlManager* parentControlManager) : + m_GUIControlManager(parentControlManager) { + m_ActivityConfigBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxActivityConfig")); m_ActivityConfigBox->CenterInParent(true, true); m_ActivityConfigBox->SetVisible(false); - m_CancelConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCancelConfig")); + m_CancelConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCancelConfig")); - m_ActivityDifficultyLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelActivityDifficultyValue")); - m_ActivityDifficultySlider = dynamic_cast(m_GUIControlManager->GetControl("SliderActivityDifficulty")); - m_StartingGoldLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelStartingGoldValue")); - m_StartingGoldSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderStartingGold")); + m_ActivityDifficultyLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelActivityDifficultyValue")); + m_ActivityDifficultySlider = dynamic_cast(m_GUIControlManager->GetControl("SliderActivityDifficulty")); + m_StartingGoldLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelStartingGoldValue")); + m_StartingGoldSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderStartingGold")); - m_RequireClearPathToOrbitCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxRequireClearPathToOrbit")); - m_FogOfWarCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxFogOfWar")); - m_DeployUnitsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxDeployUnits")); + m_RequireClearPathToOrbitCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxRequireClearPathToOrbit")); + m_FogOfWarCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxFogOfWar")); + m_DeployUnitsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxDeployUnits")); - m_PlayersAndTeamsConfigBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPlayersAndTeamsConfig")); - m_TeamIconBoxes[TeamRows::DisabledTeam] = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxDisabledTeamIcon")); - m_TeamNameLabels[TeamRows::DisabledTeam] = dynamic_cast(m_GUIControlManager->GetControl("LabelDisabledTeam")); + m_PlayersAndTeamsConfigBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPlayersAndTeamsConfig")); + m_TeamIconBoxes[TeamRows::DisabledTeam] = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxDisabledTeamIcon")); + m_TeamNameLabels[TeamRows::DisabledTeam] = dynamic_cast(m_GUIControlManager->GetControl("LabelDisabledTeam")); - GUILabel *teamTechLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelTeamTech")); + GUILabel* teamTechLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelTeamTech")); for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { std::string teamNumber = std::to_string(team + 1); - m_TeamIconBoxes[team] = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxTeam" + teamNumber + "Icon")); - m_TeamNameLabels[team] = dynamic_cast(m_GUIControlManager->GetControl("LabelTeam" + teamNumber)); + m_TeamIconBoxes[team] = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxTeam" + teamNumber + "Icon")); + m_TeamNameLabels[team] = dynamic_cast(m_GUIControlManager->GetControl("LabelTeam" + teamNumber)); - m_TeamTechComboBoxes[team] = dynamic_cast(m_GUIControlManager->GetControl("ComboBoxTeam" + teamNumber + "Tech")); + m_TeamTechComboBoxes[team] = dynamic_cast(m_GUIControlManager->GetControl("ComboBoxTeam" + teamNumber + "Tech")); m_TeamTechComboBoxes[team]->Move(teamTechLabel->GetXPos(), teamTechLabel->GetYPos() + teamTechLabel->GetHeight() + 5 + (25 * (team + 1))); m_TeamTechComboBoxes[team]->SetVisible(false); - m_TeamAISkillSliders[team] = dynamic_cast(m_GUIControlManager->GetControl("SliderTeam" + teamNumber + "AISkill")); + m_TeamAISkillSliders[team] = dynamic_cast(m_GUIControlManager->GetControl("SliderTeam" + teamNumber + "AISkill")); m_TeamAISkillSliders[team]->SetValue(Activity::AISkillSetting::DefaultSkill); - m_TeamAISkillLabels[team] = dynamic_cast(m_GUIControlManager->GetControl("LabelTeam" + teamNumber + "AISkill")); + m_TeamAISkillLabels[team] = dynamic_cast(m_GUIControlManager->GetControl("LabelTeam" + teamNumber + "AISkill")); m_TeamAISkillLabels[team]->SetText(Activity::GetAISkillString(m_TeamAISkillSliders[team]->GetValue())); } for (int player = Players::PlayerOne; player < PlayerColumns::PlayerColumnCount; ++player) { for (int team = Activity::Teams::TeamOne; team < TeamRows::TeamRowCount; ++team) { - m_PlayerBoxes[player][team] = dynamic_cast(m_GUIControlManager->GetControl("P" + std::to_string(player + 1) + "T" + std::to_string(team + 1) + "Box")); + m_PlayerBoxes[player][team] = dynamic_cast(m_GUIControlManager->GetControl("P" + std::to_string(player + 1) + "T" + std::to_string(team + 1) + "Box")); } } - m_CPULockLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelCPUTeamLock")); - m_StartErrorLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelStartError")); - m_StartGameButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonStartGame")); + m_CPULockLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelCPUTeamLock")); + m_StartErrorLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelStartError")); + m_StartGameButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonStartGame")); m_TechListFetched = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioActivityConfigGUI::PopulateTechComboBoxes() { for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { @@ -76,7 +77,7 @@ namespace RTE { m_TeamTechComboBoxes[team]->GetListPanel()->AddItem("-Random-", "", nullptr, nullptr, -1); } for (int moduleID = 0; moduleID < g_PresetMan.GetTotalModuleCount(); ++moduleID) { - if (const DataModule *dataModule = g_PresetMan.GetDataModule(moduleID)) { + if (const DataModule* dataModule = g_PresetMan.GetDataModule(moduleID)) { if (dataModule->IsFaction()) { for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { m_TeamTechComboBoxes[team]->GetListPanel()->AddItem(dataModule->GetFriendlyName(), "", nullptr, nullptr, moduleID); @@ -89,21 +90,21 @@ namespace RTE { m_TechListFetched = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ScenarioActivityConfigGUI::IsEnabled() const { return m_ActivityConfigBox->GetEnabled() && m_ActivityConfigBox->GetVisible(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioActivityConfigGUI::SetEnabled(bool enable, const Activity *selectedActivity, Scene *selectedScene) { + void ScenarioActivityConfigGUI::SetEnabled(bool enable, const Activity* selectedActivity, Scene* selectedScene) { m_ActivityConfigBox->SetEnabled(enable); m_ActivityConfigBox->SetVisible(enable); bool selectingPreviousActivityWithManuallyAdjustedGold = m_StartingGoldAdjustedManually && m_PreviouslySelectedActivity == selectedActivity; if (enable) { - m_SelectedActivity = dynamic_cast(selectedActivity); + m_SelectedActivity = dynamic_cast(selectedActivity); m_SelectedScene = selectedScene; RTEAssert(m_SelectedActivity && m_SelectedScene, "Trying to start a scenario game without an Activity or a Scene!"); } else { @@ -116,7 +117,9 @@ namespace RTE { m_TeamTechComboBoxes[team]->SetVisible(enable); } if (enable && m_SelectedActivity && m_SelectedScene) { - if (!m_TechListFetched) { PopulateTechComboBoxes(); } + if (!m_TechListFetched) { + PopulateTechComboBoxes(); + } int startingGoldOverride = selectingPreviousActivityWithManuallyAdjustedGold ? m_StartingGoldSlider->GetValue() : -1; ResetActivityConfigBox(); @@ -129,7 +132,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioActivityConfigGUI::ResetActivityConfigBox() { m_ActivityDifficultyLabel->SetText(" " + Activity::GetDifficultyString(m_ActivityDifficultySlider->GetValue())); @@ -156,7 +159,9 @@ namespace RTE { m_PlayerBoxes[player][team]->SetDrawColor(c_GUIColorBlue); } if (player < Players::MaxPlayerCount) { - if (const Icon *playerDeviceIcon = g_UInputMan.GetSchemeIcon(player)) { m_PlayerBoxes[player][TeamRows::DisabledTeam]->SetDrawImage(new AllegroBitmap(playerDeviceIcon->GetBitmaps32()[0])); } + if (const Icon* playerDeviceIcon = g_UInputMan.GetSchemeIcon(player)) { + m_PlayerBoxes[player][TeamRows::DisabledTeam]->SetDrawImage(new AllegroBitmap(playerDeviceIcon->GetBitmaps32()[0])); + } m_PlayerBoxes[player][TeamRows::DisabledTeam]->SetDrawType(GUICollectionBox::Image); } else { int cpuInitialTeam = TeamRows::DisabledTeam; @@ -169,33 +174,41 @@ namespace RTE { m_CPULockLabel->SetVisible(false); } m_PlayerBoxes.at(player).at(cpuInitialTeam)->SetDrawType(GUICollectionBox::Image); - if (const Icon *cpuIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU"))) { m_PlayerBoxes.at(PlayerColumns::PlayerCPU).at(cpuInitialTeam)->SetDrawImage(new AllegroBitmap(cpuIcon->GetBitmaps32()[0])); } + if (const Icon* cpuIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU"))) { + m_PlayerBoxes.at(PlayerColumns::PlayerCPU).at(cpuInitialTeam)->SetDrawImage(new AllegroBitmap(cpuIcon->GetBitmaps32()[0])); + } } } for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { - const Icon *teamIcon = nullptr; + const Icon* teamIcon = nullptr; if (m_SelectedActivity->TeamActive(team)) { teamIcon = m_SelectedActivity->GetTeamIcon(team); - if (!teamIcon) { teamIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Team " + std::to_string(team + 1) + " Default")); } + if (!teamIcon) { + teamIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Team " + std::to_string(team + 1) + " Default")); + } m_TeamNameLabels.at(team)->SetText(m_SelectedActivity->GetTeamName(team)); } else { - teamIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Locked Team")); + teamIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Locked Team")); m_TeamNameLabels.at(team)->SetText("Unavailable"); } - if (teamIcon) { m_TeamIconBoxes.at(team)->SetDrawImage(new AllegroBitmap(teamIcon->GetBitmaps32()[0])); } + if (teamIcon) { + m_TeamIconBoxes.at(team)->SetDrawImage(new AllegroBitmap(teamIcon->GetBitmaps32()[0])); + } m_TeamTechComboBoxes.at(team)->SetVisible(m_SelectedActivity->TeamActive(team)); m_TeamAISkillSliders.at(team)->SetVisible(m_SelectedActivity->TeamActive(team)); m_TeamAISkillLabels.at(team)->SetVisible(m_SelectedActivity->TeamActive(team)); } - if (const Icon *disabledTeamIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Disabled Team"))) { m_TeamIconBoxes.at(TeamRows::DisabledTeam)->SetDrawImage(new AllegroBitmap(disabledTeamIcon->GetBitmaps32()[0])); } + if (const Icon* disabledTeamIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Disabled Team"))) { + m_TeamIconBoxes.at(TeamRows::DisabledTeam)->SetDrawImage(new AllegroBitmap(disabledTeamIcon->GetBitmaps32()[0])); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioActivityConfigGUI::StartGame() { - GameActivity *gameActivity = dynamic_cast(m_SelectedActivity->Clone()); + GameActivity* gameActivity = dynamic_cast(m_SelectedActivity->Clone()); gameActivity->SetDifficulty(m_ActivityDifficultySlider->GetValue()); gameActivity->SetStartingGold((m_StartingGoldSlider->GetValue() == m_StartingGoldSlider->GetMaximum()) ? 1000000000 : static_cast(std::floor(m_StartingGoldSlider->GetValue()))); @@ -219,7 +232,7 @@ namespace RTE { } for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { - if (const GUIListPanel::Item *techItem = m_TeamTechComboBoxes.at(team)->GetSelectedItem()) { + if (const GUIListPanel::Item* techItem = m_TeamTechComboBoxes.at(team)->GetSelectedItem()) { if (techItem->m_ExtraIndex == -2) { gameActivity->SetTeamTech(team, "-All-"); } else if (techItem->m_ExtraIndex == -1) { @@ -234,7 +247,7 @@ namespace RTE { g_ActivityMan.SetStartActivity(gameActivity); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ScenarioActivityConfigGUI::Update(int mouseX, int mouseY) { UpdatePlayerTeamSetupCell(mouseX, mouseY); @@ -254,7 +267,9 @@ namespace RTE { } } } - if (foundPlayer) { teamsWithPlayers++; } + if (foundPlayer) { + teamsWithPlayers++; + } } } @@ -273,12 +288,14 @@ namespace RTE { m_StartErrorLabel->SetVisible(!errorMessage.empty()); m_StartGameButton->SetVisible(errorMessage.empty()); - if (m_StartGameButton->GetVisible()) { m_GUIControlManager->GetManager()->SetFocus(m_StartGameButtonBlinkTimer.AlternateReal(500) ? m_StartGameButton : nullptr); } + if (m_StartGameButton->GetVisible()) { + m_GUIControlManager->GetManager()->SetFocus(m_StartGameButtonBlinkTimer.AlternateReal(500) ? m_StartGameButton : nullptr); + } return HandleInputEvents(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioActivityConfigGUI::UpdateStartingGoldSliderAndLabel() { if (!m_StartingGoldAdjustedManually) { @@ -310,10 +327,10 @@ namespace RTE { m_StartingGoldLabel->SetText(goldString); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioActivityConfigGUI::UpdatePlayerTeamSetupCell(int mouseX, int mouseY) { - if (const GUICollectionBox *hoveredCell = dynamic_cast(m_GUIControlManager->GetControlUnderPoint(mouseX, mouseY, m_PlayersAndTeamsConfigBox, 1))) { + if (const GUICollectionBox* hoveredCell = dynamic_cast(m_GUIControlManager->GetControlUnderPoint(mouseX, mouseY, m_PlayersAndTeamsConfigBox, 1))) { int hoveredPlayer = PlayerColumns::PlayerColumnCount; int hoveredTeam = TeamRows::TeamRowCount; for (int player = Players::PlayerOne; player < PlayerColumns::PlayerColumnCount; ++player) { @@ -337,12 +354,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioActivityConfigGUI::HandleClickOnPlayerTeamSetupCell(int clickedPlayer, int clickedTeam) { m_PlayerBoxes.at(clickedPlayer).at(clickedTeam)->SetDrawType(GUICollectionBox::Image); - const Icon *playerIcon = (clickedPlayer != PlayerColumns::PlayerCPU) ? g_UInputMan.GetSchemeIcon(clickedPlayer) : dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); - if (playerIcon) { m_PlayerBoxes.at(clickedPlayer).at(clickedTeam)->SetDrawImage(new AllegroBitmap(playerIcon->GetBitmaps32()[0])); } + const Icon* playerIcon = (clickedPlayer != PlayerColumns::PlayerCPU) ? g_UInputMan.GetSchemeIcon(clickedPlayer) : dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); + if (playerIcon) { + m_PlayerBoxes.at(clickedPlayer).at(clickedTeam)->SetDrawImage(new AllegroBitmap(playerIcon->GetBitmaps32()[0])); + } if (clickedPlayer == PlayerColumns::PlayerCPU) { if (clickedTeam == TeamRows::DisabledTeam) { @@ -371,7 +390,9 @@ namespace RTE { m_PlayerBoxes.at(humanPlayer).at(clickedTeam)->SetDrawColor(c_GUIColorBlue); m_PlayerBoxes.at(humanPlayer).at(TeamRows::DisabledTeam)->SetDrawType(GUICollectionBox::Image); playerIcon = g_UInputMan.GetSchemeIcon(humanPlayer); - if (playerIcon) { m_PlayerBoxes.at(humanPlayer).at(TeamRows::DisabledTeam)->SetDrawImage(new AllegroBitmap(playerIcon->GetBitmaps32()[0])); } + if (playerIcon) { + m_PlayerBoxes.at(humanPlayer).at(TeamRows::DisabledTeam)->SetDrawImage(new AllegroBitmap(playerIcon->GetBitmaps32()[0])); + } } } } else if (clickedPlayer != PlayerColumns::PlayerCPU && clickedTeam != TeamRows::DisabledTeam && m_PlayerBoxes.at(PlayerColumns::PlayerCPU).at(clickedTeam)->GetDrawType() == GUICollectionBox::Image) { @@ -380,18 +401,22 @@ namespace RTE { int cpuTeamCount = 0; for (int teamToCount = Activity::Teams::TeamOne; teamToCount < TeamRows::DisabledTeam; ++teamToCount) { - if (m_PlayerBoxes.at(PlayerColumns::PlayerCPU).at(teamToCount)->GetDrawType() == GUICollectionBox::Image) { cpuTeamCount++; } + if (m_PlayerBoxes.at(PlayerColumns::PlayerCPU).at(teamToCount)->GetDrawType() == GUICollectionBox::Image) { + cpuTeamCount++; + } } if (cpuTeamCount == 0) { m_PlayerBoxes.at(PlayerColumns::PlayerCPU).at(TeamRows::DisabledTeam)->SetDrawType(GUICollectionBox::Image); - playerIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); - if (playerIcon) { m_PlayerBoxes.at(PlayerColumns::PlayerCPU).at(TeamRows::DisabledTeam)->SetDrawImage(new AllegroBitmap(playerIcon->GetBitmaps32()[0])); } + playerIcon = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); + if (playerIcon) { + m_PlayerBoxes.at(PlayerColumns::PlayerCPU).at(TeamRows::DisabledTeam)->SetDrawImage(new AllegroBitmap(playerIcon->GetBitmaps32()[0])); + } } } g_GUISound.FocusChangeSound()->Play(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool ScenarioActivityConfigGUI::HandleInputEvents() { GUIEvent guiEvent; @@ -409,13 +434,19 @@ namespace RTE { } else if (guiEvent.GetType() == GUIEvent::Notification) { if (guiEvent.GetControl() == m_ActivityDifficultySlider) { m_ActivityDifficultyLabel->SetText(" " + Activity::GetDifficultyString(m_ActivityDifficultySlider->GetValue())); - if (!m_StartingGoldAdjustedManually) { UpdateStartingGoldSliderAndLabel(); } + if (!m_StartingGoldAdjustedManually) { + UpdateStartingGoldSliderAndLabel(); + } } else if (guiEvent.GetControl() == m_StartingGoldSlider) { - if (guiEvent.GetMsg() == GUISlider::Clicked) { m_StartingGoldAdjustedManually = true; } + if (guiEvent.GetMsg() == GUISlider::Clicked) { + m_StartingGoldAdjustedManually = true; + } UpdateStartingGoldSliderAndLabel(); } else if (guiEvent.GetMsg() == GUISlider::Changed) { for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; team++) { - if (guiEvent.GetControl() == m_TeamAISkillSliders.at(team)) { m_TeamAISkillLabels.at(team)->SetText(Activity::GetAISkillString(m_TeamAISkillSliders.at(team)->GetValue())); } + if (guiEvent.GetControl() == m_TeamAISkillSliders.at(team)) { + m_TeamAISkillLabels.at(team)->SetText(Activity::GetAISkillString(m_TeamAISkillSliders.at(team)->GetValue())); + } } } } @@ -423,7 +454,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioActivityConfigGUI::Draw() { m_GUIControlManager->Draw(); @@ -451,7 +482,9 @@ namespace RTE { for (int team = Activity::Teams::MaxTeamCount - 1; team >= Activity::Teams::TeamOne; --team) { if (m_TeamTechComboBoxes.at(team)->GetVisible()) { m_TeamTechComboBoxes.at(team)->Draw(m_GUIControlManager->GetScreen()); - if (m_TeamTechComboBoxes.at(team)->IsDropped()) { m_TeamTechComboBoxes.at(team)->GetListPanel()->Draw(m_GUIControlManager->GetScreen()); } + if (m_TeamTechComboBoxes.at(team)->IsDropped()) { + m_TeamTechComboBoxes.at(team)->GetListPanel()->Draw(m_GUIControlManager->GetScreen()); + } } if (m_TeamAISkillSliders.at(team)->GetVisible()) { m_TeamAISkillSliders.at(team)->Draw(m_GUIControlManager->GetScreen()); @@ -459,4 +492,4 @@ namespace RTE { } } } -} +} // namespace RTE diff --git a/Source/Menus/ScenarioActivityConfigGUI.h b/Source/Menus/ScenarioActivityConfigGUI.h index c9a4f3ef6f..733b98781a 100644 --- a/Source/Menus/ScenarioActivityConfigGUI.h +++ b/Source/Menus/ScenarioActivityConfigGUI.h @@ -22,13 +22,12 @@ namespace RTE { class ScenarioActivityConfigGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a ScenarioActivityConfigGUI object in system memory and make it ready for use. /// /// Pointer to the parent GUIControlManager which owns all the GUIControls of this ScenarioActivityConfigGUI. Ownership is NOT transferred! - explicit ScenarioActivityConfigGUI(GUIControlManager *parentControlManager); + explicit ScenarioActivityConfigGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters @@ -44,7 +43,7 @@ namespace RTE { /// Show and enable or hide and disable the ScenarioActivityConfigGUI. /// Pointer to the Activity this ScenarioActivityConfigGUI will be configuring for. /// Pointer to the Scene the passed in Activity will be using. - void SetEnabled(bool enable, const Activity *selectedActivity = nullptr, Scene *selectedScene = nullptr); + void SetEnabled(bool enable, const Activity* selectedActivity = nullptr, Scene* selectedScene = nullptr); #pragma endregion #pragma region Concrete Methods @@ -63,22 +62,27 @@ namespace RTE { #pragma endregion private: - /// /// Enumeration for all the player columns in the player setup box. "Extends" the Players enumeration by adding an entry for the CPU player. /// - enum PlayerColumns { PlayerCPU = Players::MaxPlayerCount, PlayerColumnCount }; + enum PlayerColumns { + PlayerCPU = Players::MaxPlayerCount, + PlayerColumnCount + }; /// /// Enumeration for all the team rows in the player setup box. "Extends" the Teams enumeration by adding an entry for unused (disabled) Team. /// - enum TeamRows { DisabledTeam = Activity::Teams::MaxTeamCount, TeamRowCount }; + enum TeamRows { + DisabledTeam = Activity::Teams::MaxTeamCount, + TeamRowCount + }; - GUIControlManager *m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. + GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. - const GameActivity *m_SelectedActivity; //!< The Activity this ScenarioActivityConfigGUI is configuring. - const GameActivity *m_PreviouslySelectedActivity; //!< The Activity this ScenarioActivityConfigGUI was configuring last, before it got was disabled. - Scene *m_SelectedScene; //!< The Scene the selected Activity will be using. + const GameActivity* m_SelectedActivity; //!< The Activity this ScenarioActivityConfigGUI is configuring. + const GameActivity* m_PreviouslySelectedActivity; //!< The Activity this ScenarioActivityConfigGUI was configuring last, before it got was disabled. + Scene* m_SelectedScene; //!< The Scene the selected Activity will be using. int m_LockedCPUTeam = Activity::Teams::NoTeam; //!< Which team the CPU is locked to, if any. bool m_StartingGoldAdjustedManually; //!< Whether the player adjusted the starting gold, meaning it should stop automatically adjusting to the difficulty setting default starting gold where applicable. @@ -90,25 +94,25 @@ namespace RTE { /// /// GUI elements that compose the Activity setup box. /// - GUICollectionBox *m_ActivityConfigBox; - GUILabel *m_StartErrorLabel; - GUIButton *m_StartGameButton; - GUIButton *m_CancelConfigButton; - GUILabel *m_ActivityDifficultyLabel; - GUISlider *m_ActivityDifficultySlider; - GUILabel *m_StartingGoldLabel; - GUISlider *m_StartingGoldSlider; - GUICheckbox *m_RequireClearPathToOrbitCheckbox; - GUICheckbox *m_FogOfWarCheckbox; - GUICheckbox *m_DeployUnitsCheckbox; - GUILabel *m_CPULockLabel; - GUICollectionBox *m_PlayersAndTeamsConfigBox; - std::array m_TeamIconBoxes; - std::array m_TeamNameLabels; - std::array, PlayerColumns::PlayerColumnCount> m_PlayerBoxes; - std::array m_TeamTechComboBoxes; - std::array m_TeamAISkillLabels; - std::array m_TeamAISkillSliders; + GUICollectionBox* m_ActivityConfigBox; + GUILabel* m_StartErrorLabel; + GUIButton* m_StartGameButton; + GUIButton* m_CancelConfigButton; + GUILabel* m_ActivityDifficultyLabel; + GUISlider* m_ActivityDifficultySlider; + GUILabel* m_StartingGoldLabel; + GUISlider* m_StartingGoldSlider; + GUICheckbox* m_RequireClearPathToOrbitCheckbox; + GUICheckbox* m_FogOfWarCheckbox; + GUICheckbox* m_DeployUnitsCheckbox; + GUILabel* m_CPULockLabel; + GUICollectionBox* m_PlayersAndTeamsConfigBox; + std::array m_TeamIconBoxes; + std::array m_TeamNameLabels; + std::array, PlayerColumns::PlayerColumnCount> m_PlayerBoxes; + std::array m_TeamTechComboBoxes; + std::array m_TeamAISkillLabels; + std::array m_TeamAISkillSliders; #pragma region Activity Configuration Screen Handling /// @@ -154,8 +158,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - ScenarioActivityConfigGUI(const ScenarioActivityConfigGUI &reference) = delete; - ScenarioActivityConfigGUI & operator=(const ScenarioActivityConfigGUI &rhs) = delete; + ScenarioActivityConfigGUI(const ScenarioActivityConfigGUI& reference) = delete; + ScenarioActivityConfigGUI& operator=(const ScenarioActivityConfigGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/ScenarioGUI.cpp b/Source/Menus/ScenarioGUI.cpp index 0f3fc89ddd..f86bce4526 100644 --- a/Source/Menus/ScenarioGUI.cpp +++ b/Source/Menus/ScenarioGUI.cpp @@ -23,7 +23,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::Clear() { m_RootBoxMaxWidth = 0; @@ -51,25 +51,25 @@ namespace RTE { m_DrawDefaultScenePreview = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::Create(AllegroScreen *guiScreen, GUIInputWrapper *guiInput) { + void ScenarioGUI::Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); m_GUIControlManager->Load("Base.rte/GUIs/ScenarioGUI.ini"); m_RootBoxMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); - m_RootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); + m_RootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); m_RootBox->Resize(m_RootBoxMaxWidth, g_WindowMan.GetResY()); - m_ActivityConfigBoxRootBox = dynamic_cast(m_GUIControlManager->GetControl("ConfigRoot")); + m_ActivityConfigBoxRootBox = dynamic_cast(m_GUIControlManager->GetControl("ConfigRoot")); m_ActivityConfigBoxRootBox->Resize(m_RootBox->GetWidth(), m_RootBox->GetHeight()); - m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("BackToMainButton")); + m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("BackToMainButton")); m_BackToMainButton->SetPositionRel(m_RootBox->GetWidth() - m_BackToMainButton->GetWidth() - 16, m_RootBox->GetHeight() - m_BackToMainButton->GetHeight() - 22); - m_ResumeButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonResume")); + m_ResumeButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonResume")); m_ResumeButton->SetPositionRel(m_RootBox->GetWidth() - m_ResumeButton->GetWidth() - 16, m_RootBox->GetHeight() - m_ResumeButton->GetHeight() - 47); - m_SitePointNameLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneNameOnPlanet")); + m_SitePointNameLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneNameOnPlanet")); CreateActivityInfoBox(); CreateSceneInfoBox(); @@ -77,28 +77,28 @@ namespace RTE { m_ActivityConfigBox = std::make_unique(m_GUIControlManager.get()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::CreateActivityInfoBox() { - m_ActivityInfoBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxActivityInfo")); + m_ActivityInfoBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxActivityInfo")); m_ActivityInfoBox->SetPositionRel(16, 16); - m_ActivitySelectComboBox = dynamic_cast(m_GUIControlManager->GetControl("ComboBoxActivitySelect")); + m_ActivitySelectComboBox = dynamic_cast(m_GUIControlManager->GetControl("ComboBoxActivitySelect")); m_ActivitySelectComboBox->Move(m_ActivityInfoBox->GetXPos() + 8, m_ActivityInfoBox->GetYPos() + 25); - m_ActivityDescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelActivityDescription")); + m_ActivityDescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelActivityDescription")); m_ActivityDescriptionLabel->SetFont(m_GUIControlManager->GetSkin()->GetFont("FontSmall.png")); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::CreateSceneInfoBox() { - m_SceneInfoBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSceneInfo")); + m_SceneInfoBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSceneInfo")); m_SceneInfoBox->SetPositionRel(m_RootBox->GetWidth() - m_SceneInfoBox->GetWidth() - 16, 16); - m_SceneBoxCloseButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCloseSceneBox")); - m_SceneNameLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneName")); - m_SceneDescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneDescription")); + m_SceneBoxCloseButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCloseSceneBox")); + m_SceneNameLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneName")); + m_SceneDescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneDescription")); m_SceneDescriptionLabel->SetFont(m_GUIControlManager->GetSkin()->GetFont("FontSmall.png")); - m_ScenePreviewImageBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxScenePreview")); - m_StartActivityConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonStartActivityConfig")); + m_ScenePreviewImageBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxScenePreview")); + m_StartActivityConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonStartActivityConfig")); m_DefaultScenePreview.Create(ContentFile("Base.rte/GUIs/DefaultPreview.png"), 5); m_DefaultScenePreview.SetSpriteAnimMode(SpriteAnimMode::ALWAYSLOOP); @@ -109,18 +109,20 @@ namespace RTE { m_ScenePreviewBitmap->Create(c_ScenePreviewWidth, c_ScenePreviewHeight, 32); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::SetEnabled(const Vector ¢er, float radius) { + void ScenarioGUI::SetEnabled(const Vector& center, float radius) { bool centerChanged = (center != m_PlanetCenter); m_PlanetCenter = center; m_PlanetRadius = radius; - if (centerChanged) { CalculateLinesToSitePoint(); } + if (centerChanged) { + CalculateLinesToSitePoint(); + } FetchActivitiesAndScenesLists(); // Only show the resume button if the current Activity is a GameActivity. Editor or Multiplayer Activities are resumed from the main menu, so the resume button shouldn't show for them. - const GameActivity *currentActivity = dynamic_cast(g_ActivityMan.GetActivity()); + const GameActivity* currentActivity = dynamic_cast(g_ActivityMan.GetActivity()); m_ResumeButton->SetVisible(currentActivity && (currentActivity->GetActivityState() == Activity::Running || currentActivity->GetActivityState() == Activity::Editing)); m_ActivityInfoBox->SetVisible(true); @@ -128,23 +130,25 @@ namespace RTE { m_ScenePreviewAnimTimer.Reset(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::SetDraggedBox(int mouseX, int mouseY) { - GUICollectionBox *hoveredBox = dynamic_cast(m_GUIControlManager->GetControlUnderPoint(mouseX, mouseY, m_RootBox, 1)); - const GUIControl *hoveredControl = m_GUIControlManager->GetControlUnderPoint(mouseX, mouseY, hoveredBox, 1); - bool nonDragControl = (dynamic_cast(hoveredControl) || dynamic_cast(hoveredControl)); - if (hoveredBox && !nonDragControl && !m_DraggedBox && !m_ActivitySelectComboBox->IsDropped()) { m_DraggedBox = hoveredBox; } + GUICollectionBox* hoveredBox = dynamic_cast(m_GUIControlManager->GetControlUnderPoint(mouseX, mouseY, m_RootBox, 1)); + const GUIControl* hoveredControl = m_GUIControlManager->GetControlUnderPoint(mouseX, mouseY, hoveredBox, 1); + bool nonDragControl = (dynamic_cast(hoveredControl) || dynamic_cast(hoveredControl)); + if (hoveredBox && !nonDragControl && !m_DraggedBox && !m_ActivitySelectComboBox->IsDropped()) { + m_DraggedBox = hoveredBox; + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::SetSelectedActivity(const Activity *newSelectedActivity) { + void ScenarioGUI::SetSelectedActivity(const Activity* newSelectedActivity) { m_SelectedActivity = newSelectedActivity; m_ActivityScenes = nullptr; if (m_SelectedActivity) { - for (auto &[activity, sceneList] : m_ScenarioActivities) { + for (auto& [activity, sceneList]: m_ScenarioActivities) { if (activity == m_SelectedActivity) { m_ActivityScenes = &sceneList; break; @@ -164,14 +168,14 @@ namespace RTE { m_ActivityInfoBox->Resize(m_ActivityInfoBox->GetWidth(), m_ActivityDescriptionLabel->ResizeHeightToFit() + 60); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::SetSelectedScene(Scene *newSelectedScene) { + void ScenarioGUI::SetSelectedScene(Scene* newSelectedScene) { m_SelectedScene = newSelectedScene; if (m_SelectedScene) { m_SceneInfoBox->SetVisible(true); - if (BITMAP *preview = m_SelectedScene->GetPreviewBitmap()) { + if (BITMAP* preview = m_SelectedScene->GetPreviewBitmap()) { clear_to_color(m_ScenePreviewBitmap->GetBitmap(), ColorKeys::g_MaskColor); draw_sprite(m_ScenePreviewBitmap->GetBitmap(), preview, 0, 0); m_ScenePreviewImageBox->SetDrawImage(new AllegroBitmap(m_ScenePreviewBitmap->GetBitmap())); @@ -198,7 +202,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::DragBox(int mouseX, int mouseY) { if (m_DraggedBox) { @@ -215,33 +219,37 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::FetchActivitiesAndScenesLists() { int prevSelectedActivityIndex = m_ActivitySelectComboBox->GetSelectedIndex(); - Scene *prevSelectedScene = m_SelectedScene; + Scene* prevSelectedScene = m_SelectedScene; m_ActivitySelectComboBox->ClearList(); m_ScenarioActivities.clear(); m_ActivityScenes = nullptr; - std::list presetList; + std::list presetList; g_PresetMan.GetAllOfType(presetList, "Scene"); - std::vector filteredScenes; - for (Entity *presetEntity : presetList) { - Scene *presetScene = dynamic_cast(presetEntity); - if (presetScene && !presetScene->GetLocation().IsZero() && !presetScene->IsMetagameInternal() && !presetScene->IsSavedGameInternal() && (presetScene->GetMetasceneParent().empty() || g_SettingsMan.ShowMetascenes())) { filteredScenes.emplace_back(presetScene); } + std::vector filteredScenes; + for (Entity* presetEntity: presetList) { + Scene* presetScene = dynamic_cast(presetEntity); + if (presetScene && !presetScene->GetLocation().IsZero() && !presetScene->IsMetagameInternal() && !presetScene->IsSavedGameInternal() && (presetScene->GetMetasceneParent().empty() || g_SettingsMan.ShowMetascenes())) { + filteredScenes.emplace_back(presetScene); + } } AdjustSitePointOffsetsOnPlanet(filteredScenes); presetList.clear(); g_PresetMan.GetAllOfType(presetList, "Activity"); int index = 0; - for (Entity *presetEntity : presetList) { - if (GameActivity *presetActivity = dynamic_cast(presetEntity)) { - std::pair> activityAndCompatibleScenes(presetActivity, std::vector()); - for (Scene *filteredScene : filteredScenes) { - if (presetActivity->SceneIsCompatible(filteredScene)) { activityAndCompatibleScenes.second.emplace_back(filteredScene); } + for (Entity* presetEntity: presetList) { + if (GameActivity* presetActivity = dynamic_cast(presetEntity)) { + std::pair> activityAndCompatibleScenes(presetActivity, std::vector()); + for (Scene* filteredScene: filteredScenes) { + if (presetActivity->SceneIsCompatible(filteredScene)) { + activityAndCompatibleScenes.second.emplace_back(filteredScene); + } } m_ScenarioActivities.insert(activityAndCompatibleScenes); // Add to the activity selection ComboBox and attach the activity pointer, not passing in ownership. @@ -256,15 +264,17 @@ namespace RTE { } if (prevSelectedActivityIndex >= 0) { m_ActivitySelectComboBox->SetSelectedIndex(prevSelectedActivityIndex); - SetSelectedActivity(dynamic_cast(m_ActivitySelectComboBox->GetSelectedItem()->m_pEntity)); - if (prevSelectedScene) { SetSelectedScene(prevSelectedScene); } + SetSelectedActivity(dynamic_cast(m_ActivitySelectComboBox->GetSelectedItem()->m_pEntity)); + if (prevSelectedScene) { + SetSelectedScene(prevSelectedScene); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::AdjustSitePointOffsetsOnPlanet(const std::vector &sceneList) const { - for (Scene *sceneListEntry : sceneList) { + void ScenarioGUI::AdjustSitePointOffsetsOnPlanet(const std::vector& sceneList) const { + for (Scene* sceneListEntry: sceneList) { int sceneYPos = (m_PlanetCenter + sceneListEntry->GetLocation() + sceneListEntry->GetLocationOffset()).GetFloorIntY(); if (std::abs(sceneListEntry->GetLocation().GetY()) < m_PlanetRadius + 100 && std::abs(sceneListEntry->GetLocation().GetX()) < m_PlanetRadius + 100) { if (sceneYPos < 10) { @@ -281,8 +291,8 @@ namespace RTE { bool foundOverlap = true; while (foundOverlap) { foundOverlap = false; - for (Scene *sceneListEntry1 : sceneList) { - for (const Scene *sceneListEntry2 : sceneList) { + for (Scene* sceneListEntry1: sceneList) { + for (const Scene* sceneListEntry2: sceneList) { if (sceneListEntry1 != sceneListEntry2) { Vector pos1 = sceneListEntry1->GetLocation() + sceneListEntry1->GetLocationOffset(); Vector pos2 = sceneListEntry2->GetLocation() + sceneListEntry2->GetLocationOffset(); @@ -317,7 +327,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::CalculateLinesToSitePoint() { m_LineToSitePoints.clear(); @@ -359,10 +369,14 @@ namespace RTE { chamferSize = std::min(std::abs(sceneBoxEdge.GetFloorIntX() - sitePos.GetFloorIntX()) - minSiteDistance, std::abs(sceneBoxEdge.GetFloorIntY() - sitePos.GetFloorIntY()) - minStraightLength); chamferSize = std::min(chamferSize, maxChamferSize); - if (chamferSize < minChamferSize) { chamferSize = 0; } + if (chamferSize < minChamferSize) { + chamferSize = 0; + } m_LineToSitePoints.emplace_back(Vector(bendPoint.GetX(), bendPoint.GetY() + static_cast(chamferSize) * -yDirMult)); - if (chamferSize > 0) { m_LineToSitePoints.emplace_back(Vector(bendPoint.GetX() + static_cast(chamferSize) * xDirMult, bendPoint.GetY())); } + if (chamferSize > 0) { + m_LineToSitePoints.emplace_back(Vector(bendPoint.GetX() + static_cast(chamferSize) * xDirMult, bendPoint.GetY())); + } m_LineToSitePoints.emplace_back(sitePos + Vector((static_cast(circleRadius + 1)) * -xDirMult, 0)); } else { // Two bends. extraLength ensures that there will be straight lines coming out of the site and the box, and that they are nearly as short as possible. @@ -373,16 +387,20 @@ namespace RTE { chamferSize = std::min(std::abs(sceneBoxEdge.GetFloorIntX() - sitePos.GetFloorIntX()) - minSiteDistance, std::abs(secondBend.GetFloorIntY() - sitePos.GetFloorIntY()) - minSiteDistance); chamferSize = std::min(chamferSize, maxChamferSize); - if (chamferSize < minChamferSize) { chamferSize = 0; } + if (chamferSize < minChamferSize) { + chamferSize = 0; + } m_LineToSitePoints.emplace_back(firstBend); m_LineToSitePoints.emplace_back(Vector(secondBend.GetX() + static_cast(chamferSize) * -xDirMult, secondBend.GetY())); - if (chamferSize > 0) { m_LineToSitePoints.emplace_back(Vector(secondBend.GetX(), secondBend.GetY() + static_cast(chamferSize) * -yDirMult)); } + if (chamferSize > 0) { + m_LineToSitePoints.emplace_back(Vector(secondBend.GetX(), secondBend.GetY() + static_cast(chamferSize) * -yDirMult)); + } m_LineToSitePoints.emplace_back(sitePos + Vector(0, (static_cast(circleRadius + 1)) * yDirMult)); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ScenarioGUI::ScenarioMenuUpdateResult ScenarioGUI::Update() { m_UpdateResult = ScenarioMenuUpdateResult::NoEvent; @@ -421,26 +439,30 @@ namespace RTE { } // Only show the resume button if the current Activity is a GameActivity. Editor or Multiplayer Activities are resumed from the main menu, so the resume button shouldn't show for them. - const GameActivity *currentActivity = dynamic_cast(g_ActivityMan.GetActivity()); + const GameActivity* currentActivity = dynamic_cast(g_ActivityMan.GetActivity()); m_ResumeButton->SetVisible(currentActivity && (currentActivity->GetActivityState() == Activity::Running || currentActivity->GetActivityState() == Activity::Editing)); - if (m_ResumeButton->GetVisible()) { m_GUIControlManager->GetManager()->SetFocus((m_BlinkTimer.AlternateReal(500)) ? m_ResumeButton : nullptr); } + if (m_ResumeButton->GetVisible()) { + m_GUIControlManager->GetManager()->SetFocus((m_BlinkTimer.AlternateReal(500)) ? m_ResumeButton : nullptr); + } } else { m_RootBox->SetVisible(false); m_ActivityConfigBoxRootBox->SetVisible(true); - if (m_ActivityConfigBox->Update(mousePosX, mousePosY)) { m_UpdateResult = ScenarioMenuUpdateResult::ActivityStarted; } + if (m_ActivityConfigBox->Update(mousePosX, mousePosY)) { + m_UpdateResult = ScenarioMenuUpdateResult::ActivityStarted; + } } return m_UpdateResult; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::UpdateHoveredSitePointLabel(int mouseX, int mouseY) { bool foundAnyHover = false; if (m_ActivityScenes && !m_DraggedBox && !m_ActivityInfoBox->PointInside(mouseX, mouseY) && !m_SceneInfoBox->PointInside(mouseX, mouseY)) { - Scene *candidateScene = nullptr; + Scene* candidateScene = nullptr; float sqrShortestDistance = 10.0F * 10.0F; - for (Scene *activityScene : *m_ActivityScenes) { + for (Scene* activityScene: *m_ActivityScenes) { float sqrDistance = (m_PlanetCenter + activityScene->GetLocation() + activityScene->GetLocationOffset() - Vector(static_cast(mouseX), static_cast(mouseY))).GetSqrMagnitude(); if (sqrDistance < sqrShortestDistance) { sqrShortestDistance = sqrDistance; @@ -467,7 +489,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::HandleInputEvents(int mouseX, int mouseY) { GUIEvent guiEvent; @@ -487,11 +509,13 @@ namespace RTE { m_ActivityConfigBox->SetEnabled(true, m_SelectedActivity, m_SelectedScene); } } else if (guiEvent.GetType() == GUIEvent::Notification) { - if (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl())) { g_GUISound.SelectionChangeSound()->Play(); } + if (guiEvent.GetMsg() == GUIButton::Focused && dynamic_cast(guiEvent.GetControl())) { + g_GUISound.SelectionChangeSound()->Play(); + } if (guiEvent.GetMsg() == GUIComboBox::Closed && guiEvent.GetControl() == m_ActivitySelectComboBox) { g_GUISound.ItemChangeSound()->Play(); - SetSelectedActivity((m_ActivitySelectComboBox->GetSelectedItem()) ? dynamic_cast(m_ActivitySelectComboBox->GetSelectedItem()->m_pEntity) : nullptr); + SetSelectedActivity((m_ActivitySelectComboBox->GetSelectedItem()) ? dynamic_cast(m_ActivitySelectComboBox->GetSelectedItem()->m_pEntity) : nullptr); } } } @@ -505,11 +529,13 @@ namespace RTE { } else if (g_UInputMan.MenuButtonReleased(UInputMan::MenuCursorButtons::MENU_PRIMARY)) { m_DraggedBox = nullptr; } - if (g_UInputMan.MenuButtonHeld(UInputMan::MenuCursorButtons::MENU_PRIMARY)) { DragBox(mouseX, mouseY); } + if (g_UInputMan.MenuButtonHeld(UInputMan::MenuCursorButtons::MENU_PRIMARY)) { + DragBox(mouseX, mouseY); + } m_PrevMousePos.SetXY(static_cast(mouseX), static_cast(mouseY)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ScenarioGUI::Draw() const { // Early return to avoid single frame flicker before title screen goes into fadeout. @@ -520,22 +546,26 @@ namespace RTE { if (m_ActivityScenes) { drawing_mode(DRAW_MODE_TRANS, nullptr, 0, 0); DrawSitePoints(g_FrameMan.GetBackBuffer32()); - if (m_SelectedScene && m_SceneInfoBox->GetVisible()) { DrawLinesToSitePoint(g_FrameMan.GetBackBuffer32()); } + if (m_SelectedScene && m_SceneInfoBox->GetVisible()) { + DrawLinesToSitePoint(g_FrameMan.GetBackBuffer32()); + } drawing_mode(DRAW_MODE_SOLID, nullptr, 0, 0); } m_GUIControlManager->Draw(); - if (m_DrawDefaultScenePreview && m_SceneInfoBox->GetVisible()) { m_DefaultScenePreview.Draw(g_FrameMan.GetBackBuffer32()); } + if (m_DrawDefaultScenePreview && m_SceneInfoBox->GetVisible()) { + m_DefaultScenePreview.Draw(g_FrameMan.GetBackBuffer32()); + } } else { m_ActivityConfigBox->Draw(); } m_GUIControlManager->DrawMouse(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::DrawSitePoints(BITMAP *drawBitmap) const { + void ScenarioGUI::DrawSitePoints(BITMAP* drawBitmap) const { int blendAmount = 0; - for (const Scene *scenePointer : *m_ActivityScenes) { + for (const Scene* scenePointer: *m_ActivityScenes) { int drawColor = 0; if (scenePointer->GetModuleID() == g_PresetMan.GetModuleID("Base.rte")) { drawColor = c_GUIColorYellow; @@ -559,9 +589,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::DrawLinesToSitePoint(BITMAP *drawBitmap) const { + void ScenarioGUI::DrawLinesToSitePoint(BITMAP* drawBitmap) const { int blendAmount = 0; int drawColor = c_GUIColorWhite; @@ -597,4 +627,4 @@ namespace RTE { set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); circle(drawBitmap, sitePosX, sitePosY, circleRadius - 1, drawColor); } -} +} // namespace RTE diff --git a/Source/Menus/ScenarioGUI.h b/Source/Menus/ScenarioGUI.h index 19d6a3d629..cf4af8798d 100644 --- a/Source/Menus/ScenarioGUI.h +++ b/Source/Menus/ScenarioGUI.h @@ -23,7 +23,6 @@ namespace RTE { class ScenarioGUI { public: - /// /// Enumeration for the results of the ScenarioGUI input and event update. /// @@ -40,14 +39,17 @@ namespace RTE { /// /// Pointer to a GUIScreen interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! - ScenarioGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput) { Clear(); Create(guiScreen, guiInput); } + ScenarioGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { + Clear(); + Create(guiScreen, guiInput); + } /// /// Makes the ScenarioGUI object ready for use. /// /// Pointer to a GUIScreen interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! - void Create(AllegroScreen *guiScreen, GUIInputWrapper *guiInput); + void Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput); #pragma endregion #pragma region Setters @@ -56,7 +58,7 @@ namespace RTE { /// /// The absolute screen coordinates of the planet's center. /// The radius, in screen pixel units, of the planet. - void SetEnabled(const Vector ¢er, float radius); + void SetEnabled(const Vector& center, float radius); #pragma endregion #pragma region Concrete Methods @@ -73,24 +75,23 @@ namespace RTE { #pragma endregion private: - int m_RootBoxMaxWidth; //!< The maximum width the root CollectionBox that holds all this menu's GUI elements. This is to constrain this menu to the primary window's display (left-most) while in multi-display fullscreen, otherwise positioning can get stupid. std::unique_ptr m_GUIControlManager; //!< The GUIControlManager which owns all the GUIControls of the ScenarioGUI. ScenarioMenuUpdateResult m_UpdateResult; //!< The result of the ScenarioGUI update. See ScenarioMenuUpdateResult enumeration. - std::map> m_ScenarioActivities; //!< The map of Activities and the Scenes compatible with each, neither of which are owned here. - const Activity *m_SelectedActivity; //!< The currently selected Activity. Not owned. + std::map> m_ScenarioActivities; //!< The map of Activities and the Scenes compatible with each, neither of which are owned here. + const Activity* m_SelectedActivity; //!< The currently selected Activity. Not owned. - std::vector *m_ActivityScenes; //!< Pointer to the current set of Scenes being displayed. Not owned, and neither are the Scenes. - Scene *m_SelectedScene; //!< The scene preset currently selected. Not owned. - Scene *m_HoveredScene; //!< The scene preset currently hovered. Not owned. + std::vector* m_ActivityScenes; //!< Pointer to the current set of Scenes being displayed. Not owned, and neither are the Scenes. + Scene* m_SelectedScene; //!< The scene preset currently selected. Not owned. + Scene* m_HoveredScene; //!< The scene preset currently hovered. Not owned. Vector m_PlanetCenter; //!< The absolute screen position of the planet center. float m_PlanetRadius; //!< The screen radius of the planet. std::vector m_LineToSitePoints; //!< Collection of points that form lines from a screen point to the selected site point. - GUICollectionBox *m_DraggedBox; //!< Currently dragged GUI box. + GUICollectionBox* m_DraggedBox; //!< Currently dragged GUI box. Vector m_PrevMousePos; //!< Previous position of the mouse to calculate dragging. Timer m_BlinkTimer; //!< Timer for blinking the resume and config start buttons. @@ -104,21 +105,21 @@ namespace RTE { /// /// GUI elements that compose the scenario menu screen. /// - GUICollectionBox *m_RootBox; - GUICollectionBox *m_ActivityConfigBoxRootBox; - GUIButton *m_BackToMainButton; - GUIButton *m_ResumeButton; - GUICollectionBox *m_ActivityInfoBox; - GUIComboBox *m_ActivitySelectComboBox; - GUILabel *m_ActivityDescriptionLabel; - GUICollectionBox *m_SceneInfoBox; - GUIButton *m_SceneBoxCloseButton; - GUILabel *m_SceneNameLabel; - GUILabel *m_SceneDescriptionLabel; - GUICollectionBox *m_ScenePreviewImageBox; + GUICollectionBox* m_RootBox; + GUICollectionBox* m_ActivityConfigBoxRootBox; + GUIButton* m_BackToMainButton; + GUIButton* m_ResumeButton; + GUICollectionBox* m_ActivityInfoBox; + GUIComboBox* m_ActivitySelectComboBox; + GUILabel* m_ActivityDescriptionLabel; + GUICollectionBox* m_SceneInfoBox; + GUIButton* m_SceneBoxCloseButton; + GUILabel* m_SceneNameLabel; + GUILabel* m_SceneDescriptionLabel; + GUICollectionBox* m_ScenePreviewImageBox; std::unique_ptr m_ScenePreviewBitmap; - GUIButton *m_StartActivityConfigButton; - GUILabel *m_SitePointNameLabel; + GUIButton* m_StartActivityConfigButton; + GUILabel* m_SitePointNameLabel; #pragma region Create Breakdown /// @@ -144,13 +145,13 @@ namespace RTE { /// Sets the selected Activity, refreshes the compatible Scenes on the planet and updates the Activity info box appropriately. /// /// The new selected Activity. - void SetSelectedActivity(const Activity *newSelectedActivity); + void SetSelectedActivity(const Activity* newSelectedActivity); /// /// Sets the currently selected Scene and updates the Scene info box appropriately. /// /// The new selected Scene. - void SetSelectedScene(Scene *newSelectedScene); + void SetSelectedScene(Scene* newSelectedScene); /// /// Moves the CollectionBox that is selected as being dragged, if any. @@ -168,7 +169,7 @@ namespace RTE { /// Adjusts the positions of the site points on the planet if they don't fit the screen or overlap. /// /// Vector of Scenes to adjust positions for. - void AdjustSitePointOffsetsOnPlanet(const std::vector &sceneList) const; + void AdjustSitePointOffsetsOnPlanet(const std::vector& sceneList) const; /// /// Calculates how to draw lines from the Scene info box to the selected site point on the planet. @@ -197,13 +198,13 @@ namespace RTE { /// Draws the site points on top of the planet. /// /// The bitmap to draw on. - void DrawSitePoints(BITMAP *drawBitmap) const; + void DrawSitePoints(BITMAP* drawBitmap) const; /// /// Draws fancy thick flickering lines from the Scene info box to the selected scene point on the planet. /// /// The bitmap to draw to. - void DrawLinesToSitePoint(BITMAP *drawBitmap) const; + void DrawLinesToSitePoint(BITMAP* drawBitmap) const; #pragma endregion /// @@ -212,8 +213,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - ScenarioGUI(const ScenarioGUI &reference) = delete; - ScenarioGUI & operator=(const ScenarioGUI &rhs) = delete; + ScenarioGUI(const ScenarioGUI& reference) = delete; + ScenarioGUI& operator=(const ScenarioGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/SceneEditorGUI.cpp b/Source/Menus/SceneEditorGUI.cpp index c63a8ba4f2..24ca42c700 100644 --- a/Source/Menus/SceneEditorGUI.cpp +++ b/Source/Menus/SceneEditorGUI.cpp @@ -7,7 +7,6 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files @@ -43,8 +42,8 @@ using namespace RTE; #define BLUEPRINTREVEALRATE 150 #define BLUEPRINTREVEALPAUSE 1500 -BITMAP *SceneEditorGUI::s_pValidPathDot = 0; -BITMAP *SceneEditorGUI::s_pInvalidPathDot = 0; +BITMAP* SceneEditorGUI::s_pValidPathDot = 0; +BITMAP* SceneEditorGUI::s_pInvalidPathDot = 0; ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear @@ -52,141 +51,136 @@ BITMAP *SceneEditorGUI::s_pInvalidPathDot = 0; // Description: Clears all the member variables of this SceneEditorGUI, effectively // resetting the members of this abstraction level only. -void SceneEditorGUI::Clear() -{ - m_pController = 0; - m_FeatureSet = INGAMEEDIT; - m_EditMade = false; - m_EditorGUIMode = PICKINGOBJECT; - m_PreviousMode = ADDINGOBJECT; - m_ModeChanged = true; - m_BlinkTimer.Reset(); - m_BlinkMode = NOBLINK; - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - m_RevealTimer.Reset(); - m_RevealIndex = 0; +void SceneEditorGUI::Clear() { + m_pController = 0; + m_FeatureSet = INGAMEEDIT; + m_EditMade = false; + m_EditorGUIMode = PICKINGOBJECT; + m_PreviousMode = ADDINGOBJECT; + m_ModeChanged = true; + m_BlinkTimer.Reset(); + m_BlinkMode = NOBLINK; + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + m_RevealTimer.Reset(); + m_RevealIndex = 0; m_PieMenu = nullptr; - m_pPicker = 0; - m_NativeTechModule = 0; - m_ForeignCostMult = 4.0; - m_GridSnapping = true; - m_CursorPos.Reset(); - m_CursorOffset.Reset(); - m_CursorInAir = true; - m_FacingLeft = false; - m_PlaceTeam = Activity::TeamOne; - m_pCurrentObject = 0; - m_ObjectListOrder = -1; - m_DrawCurrentObject = true; - m_pObjectToBlink = 0; - m_BrainSkyPath.clear(); - m_BrainSkyPathCost = 0; + m_pPicker = 0; + m_NativeTechModule = 0; + m_ForeignCostMult = 4.0; + m_GridSnapping = true; + m_CursorPos.Reset(); + m_CursorOffset.Reset(); + m_CursorInAir = true; + m_FacingLeft = false; + m_PlaceTeam = Activity::TeamOne; + m_pCurrentObject = 0; + m_ObjectListOrder = -1; + m_DrawCurrentObject = true; + m_pObjectToBlink = 0; + m_BrainSkyPath.clear(); + m_BrainSkyPathCost = 0; m_RequireClearPathToOrbit = false; - m_PathRequest.reset(); + m_PathRequest.reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Makes the SceneEditorGUI object ready for use. -int SceneEditorGUI::Create(Controller *pController, FeatureSets featureSet, int whichModuleSpace, int nativeTechModule, float foreignCostMult) -{ - RTEAssert(pController, "No controller sent to SceneEditorGUI on creation!"); - m_pController = pController; +int SceneEditorGUI::Create(Controller* pController, FeatureSets featureSet, int whichModuleSpace, int nativeTechModule, float foreignCostMult) { + RTEAssert(pController, "No controller sent to SceneEditorGUI on creation!"); + m_pController = pController; SetFeatureSet(featureSet); - // Update the brain path - UpdateBrainPath(); - - // Allocate and (re)create the Editor GUIs - if (!m_pPicker) - m_pPicker = new ObjectPickerGUI(); - else - m_pPicker->Reset(); - m_pPicker->Create(pController, whichModuleSpace); - - m_NativeTechModule = nativeTechModule; - m_ForeignCostMult = foreignCostMult; - // Also apply these to the picker - m_pPicker->SetNativeTechModule(m_NativeTechModule); - m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); - - // Cursor init - m_CursorPos = g_SceneMan.GetSceneDim() / 2; - - // Set initial focus, category std::list, and label settings - m_EditorGUIMode = PICKINGOBJECT; - m_ModeChanged = true; - m_pCurrentObject = 0; - - // Reset repeat timers - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - m_RevealTimer.Reset(); - m_RevealTimer.SetRealTimeLimitMS(100); - - //Check if we need to check for a clear path to orbit + // Update the brain path + UpdateBrainPath(); + + // Allocate and (re)create the Editor GUIs + if (!m_pPicker) + m_pPicker = new ObjectPickerGUI(); + else + m_pPicker->Reset(); + m_pPicker->Create(pController, whichModuleSpace); + + m_NativeTechModule = nativeTechModule; + m_ForeignCostMult = foreignCostMult; + // Also apply these to the picker + m_pPicker->SetNativeTechModule(m_NativeTechModule); + m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); + + // Cursor init + m_CursorPos = g_SceneMan.GetSceneDim() / 2; + + // Set initial focus, category std::list, and label settings + m_EditorGUIMode = PICKINGOBJECT; + m_ModeChanged = true; + m_pCurrentObject = 0; + + // Reset repeat timers + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); + m_RevealTimer.Reset(); + m_RevealTimer.SetRealTimeLimitMS(100); + + // Check if we need to check for a clear path to orbit m_RequireClearPathToOrbit = false; - GameActivity * gameActivity = dynamic_cast(g_ActivityMan.GetActivity()); - if (gameActivity) { - m_RequireClearPathToOrbit = gameActivity->GetRequireClearPathToOrbit(); - } + GameActivity* gameActivity = dynamic_cast(g_ActivityMan.GetActivity()); + if (gameActivity) { + m_RequireClearPathToOrbit = gameActivity->GetRequireClearPathToOrbit(); + } // Always disable clear path requirement in scene editor - SceneEditor * editorActivity = dynamic_cast(g_ActivityMan.GetActivity()); - if (editorActivity) { - m_RequireClearPathToOrbit = false; - } - - // Only load the static dot bitmaps once - if (!s_pValidPathDot) { - ContentFile dotFile("Base.rte/GUIs/Indicators/PathDotValid.png"); - s_pValidPathDot = dotFile.GetAsBitmap(); - dotFile.SetDataPath("Base.rte/GUIs/Indicators/PathDotInvalid.png"); - s_pInvalidPathDot = dotFile.GetAsBitmap(); - } - - return 0; -} + SceneEditor* editorActivity = dynamic_cast(g_ActivityMan.GetActivity()); + if (editorActivity) { + m_RequireClearPathToOrbit = false; + } + + // Only load the static dot bitmaps once + if (!s_pValidPathDot) { + ContentFile dotFile("Base.rte/GUIs/Indicators/PathDotValid.png"); + s_pValidPathDot = dotFile.GetAsBitmap(); + dotFile.SetDataPath("Base.rte/GUIs/Indicators/PathDotInvalid.png"); + s_pInvalidPathDot = dotFile.GetAsBitmap(); + } + return 0; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the SceneEditorGUI object. -void SceneEditorGUI::Destroy() -{ - delete m_pPicker; +void SceneEditorGUI::Destroy() { + delete m_pPicker; - delete m_pCurrentObject; + delete m_pCurrentObject; - Clear(); + Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetController ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets the controller used by this. The ownership of the controller is // NOT transferred! -void SceneEditorGUI::SetController(Controller *pController) -{ - m_pController = pController; +void SceneEditorGUI::SetController(Controller* pController) { + m_pController = pController; m_PieMenu->SetMenuController(pController); - m_pPicker->SetController(pController); + m_pPicker->SetController(pController); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SceneEditorGUI::SetFeatureSet(SceneEditorGUI::FeatureSets newFeatureSet) { m_FeatureSet = newFeatureSet; - if (m_PieMenu) { m_PieMenu = nullptr; } + if (m_PieMenu) { + m_PieMenu = nullptr; + } std::string pieMenuName; switch (m_FeatureSet) { case FeatureSets::ONLOADEDIT: @@ -204,14 +198,13 @@ void SceneEditorGUI::SetFeatureSet(SceneEditorGUI::FeatureSets newFeatureSet) { default: RTEAbort("Unhandled SceneEditorGUI FeatureSet when setting up PieMenu."); } - m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", pieMenuName)->Clone())); - //m_PieMenu->Create(); + m_PieMenu = std::unique_ptr(dynamic_cast(g_PresetMan.GetEntityPreset("PieMenu", pieMenuName)->Clone())); + // m_PieMenu->Create(); m_PieMenu->SetMenuController(m_pController); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetPosOnScreen ////////////////////////////////////////////////////////////////////////////////////////// @@ -219,95 +212,81 @@ void SceneEditorGUI::SetFeatureSet(SceneEditorGUI::FeatureSets newFeatureSet) { // left corner, then 0, 0. This will affect the way the mouse is positioned // etc. -void SceneEditorGUI::SetPosOnScreen(int newPosX, int newPosY) -{ - m_pPicker->SetPosOnScreen(newPosX, newPosY); +void SceneEditorGUI::SetPosOnScreen(int newPosX, int newPosY) { + m_pPicker->SetPosOnScreen(newPosX, newPosY); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetCurrentObject ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets the new Object to be held at the cursor of this Editor. Ownership // IS transferred! -bool SceneEditorGUI::SetCurrentObject(SceneObject *pNewObject) -{ - if (m_pCurrentObject == pNewObject) - return true; +bool SceneEditorGUI::SetCurrentObject(SceneObject* pNewObject) { + if (m_pCurrentObject == pNewObject) + return true; - // Replace the current object with the new one - delete m_pCurrentObject; - m_pCurrentObject = pNewObject; + // Replace the current object with the new one + delete m_pCurrentObject; + m_pCurrentObject = pNewObject; - if (!m_pCurrentObject) - return false; - - m_pCurrentObject->SetTeam(m_FeatureSet == ONLOADEDIT ? m_PlaceTeam : m_pController->GetTeam()); - m_pCurrentObject->SetPlacedByPlayer(m_pController->GetPlayer()); + if (!m_pCurrentObject) + return false; - // Disable any controller, if an actor - if (Actor *pActor = dynamic_cast(m_pCurrentObject)) - { - pActor->GetController()->SetDisabled(true); - pActor->SetStatus(Actor::INACTIVE); - } + m_pCurrentObject->SetTeam(m_FeatureSet == ONLOADEDIT ? m_PlaceTeam : m_pController->GetTeam()); + m_pCurrentObject->SetPlacedByPlayer(m_pController->GetPlayer()); + // Disable any controller, if an actor + if (Actor* pActor = dynamic_cast(m_pCurrentObject)) { + pActor->GetController()->SetDisabled(true); + pActor->SetStatus(Actor::INACTIVE); + } - return true; + return true; } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetActivatedPieSlice ////////////////////////////////////////////////////////////////////////////////////////// // Description: Gets any Pie menu slice command activated last update. PieSlice::SliceType SceneEditorGUI::GetActivatedPieSlice() const { - return m_PieMenu->GetPieCommand(); + return m_PieMenu->GetPieCommand(); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetModuleSpace ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets which DataModule space to be picking objects from. If -1, then // let the player pick from all loaded modules. -void SceneEditorGUI::SetModuleSpace(int moduleSpaceID) -{ - m_pPicker->SetModuleSpace(moduleSpaceID); +void SceneEditorGUI::SetModuleSpace(int moduleSpaceID) { + m_pPicker->SetModuleSpace(moduleSpaceID); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetNativeTechModule ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets which DataModule ID should be treated as the native tech of the // user of this menu. -void SceneEditorGUI::SetNativeTechModule(int whichModule) -{ - if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) - { - m_NativeTechModule = whichModule; - m_pPicker->SetNativeTechModule(m_NativeTechModule); - } +void SceneEditorGUI::SetNativeTechModule(int whichModule) { + if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) { + m_NativeTechModule = whichModule; + m_pPicker->SetNativeTechModule(m_NativeTechModule); + } } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetForeignCostMultiplier ////////////////////////////////////////////////////////////////////////////////////////// // Description: Sets the multiplier of the cost of any foreign Tech items. -void SceneEditorGUI::SetForeignCostMultiplier(float newMultiplier) -{ - m_ForeignCostMult = newMultiplier; - m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); +void SceneEditorGUI::SetForeignCostMultiplier(float newMultiplier) { + m_ForeignCostMult = newMultiplier; + m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); } - ////////////////////////////////////////////////////////////////////////////////////////// // Method: TestBrainResidence ////////////////////////////////////////////////////////////////////////////////////////// @@ -317,177 +296,166 @@ void SceneEditorGUI::SetForeignCostMultiplier(float newMultiplier) // current resident brain if the current placement is no bueno. It also // removes the faulty brain from residence in the scene! -bool SceneEditorGUI::TestBrainResidence(bool noBrainIsOK) -{ - // Do we have a resident at all? - SceneObject *pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); - - // If not, can we find an unassigned brain and make it the resident? - if (!pBrain) - { - pBrain = g_MovableMan.GetUnassignedBrain(g_ActivityMan.GetActivity()->GetTeamOfPlayer(m_pController->GetPlayer())); - // Found one, so make it the resident brain (passing ownership) and remove it from the sim - if (pBrain) - { - g_SceneMan.GetScene()->SetResidentBrain(m_pController->GetPlayer(), pBrain); - g_MovableMan.RemoveMO(dynamic_cast(pBrain)); - } - } - - // We do we have a resident brain now, so let's check that it's in a legit spot - if (pBrain) - { - // Got to update the pathfinding graphs so the latest terrain is used for the below tests - g_SceneMan.GetScene()->UpdatePathFinding(); +bool SceneEditorGUI::TestBrainResidence(bool noBrainIsOK) { + // Do we have a resident at all? + SceneObject* pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); + + // If not, can we find an unassigned brain and make it the resident? + if (!pBrain) { + pBrain = g_MovableMan.GetUnassignedBrain(g_ActivityMan.GetActivity()->GetTeamOfPlayer(m_pController->GetPlayer())); + // Found one, so make it the resident brain (passing ownership) and remove it from the sim + if (pBrain) { + g_SceneMan.GetScene()->SetResidentBrain(m_pController->GetPlayer(), pBrain); + g_MovableMan.RemoveMO(dynamic_cast(pBrain)); + } + } + + // We do we have a resident brain now, so let's check that it's in a legit spot + if (pBrain) { + // Got to update the pathfinding graphs so the latest terrain is used for the below tests + g_SceneMan.GetScene()->UpdatePathFinding(); UpdateBrainSkyPathAndCost(pBrain->GetPos()); - } - else - { - // No brain found, so we better place one - if (!noBrainIsOK) - { - m_EditorGUIMode = INSTALLINGBRAIN; - m_ModeChanged = true; - UpdateBrainPath(); - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - return false; - } - - // Block on our path request completing - while (m_PathRequest && !m_PathRequest->complete) {}; + } else { + // No brain found, so we better place one + if (!noBrainIsOK) { + m_EditorGUIMode = INSTALLINGBRAIN; + m_ModeChanged = true; + UpdateBrainPath(); + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + return false; + } + + // Block on our path request completing + while (m_PathRequest && !m_PathRequest->complete) {}; // Nope! Not valid spot for this brain we found, need to force user to re-place it - if (m_BrainSkyPathCost > MAXBRAINPATHCOST && m_RequireClearPathToOrbit) - { - // Jump to where the bad brain is - m_CursorPos = pBrain->GetPos(); - // Put the resident clone as the current object we need to place - SetCurrentObject(dynamic_cast(pBrain->Clone())); - // Get rid of the resident - it can't be helped there - g_SceneMan.GetScene()->SetResidentBrain(m_pController->GetPlayer(), 0); - // Switch to brain placement mode - m_EditorGUIMode = INSTALLINGBRAIN; - m_ModeChanged = true; - UpdateBrainPath(); - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - return false; - } - - // Brain is fine, leave it be - return true; -} + if (m_BrainSkyPathCost > MAXBRAINPATHCOST && m_RequireClearPathToOrbit) { + // Jump to where the bad brain is + m_CursorPos = pBrain->GetPos(); + // Put the resident clone as the current object we need to place + SetCurrentObject(dynamic_cast(pBrain->Clone())); + // Get rid of the resident - it can't be helped there + g_SceneMan.GetScene()->SetResidentBrain(m_pController->GetPlayer(), 0); + // Switch to brain placement mode + m_EditorGUIMode = INSTALLINGBRAIN; + m_ModeChanged = true; + UpdateBrainPath(); + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + return false; + } + // Brain is fine, leave it be + return true; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: Update ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates the state of this Menu each frame -void SceneEditorGUI::Update() -{ - // Update the user controller -// m_pController->Update(); - - m_EditMade = false; - m_pObjectToBlink = 0; - // Which set of placed objects in the scene we're editing - int editedSet = m_FeatureSet == ONLOADEDIT ? Scene::PLACEONLOAD : (m_FeatureSet == AIPLANEDIT ? Scene::AIPLAN : Scene::BLUEPRINT); - - //////////////////////////////////////////// - // Blinking logic -/* - if (m_BlinkMode == OBJECTBLINK) - { - m_pCostLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); - } - else if (m_BlinkMode == NOCRAFT) - { - bool blink = m_BlinkTimer.AlternateSim(250); - m_pCraftLabel->SetVisible(blink); - m_pCraftBox->SetVisible(blink); - } - - // Time out the blinker - if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastSimMS(1500)) - { - m_pCostLabel->SetVisible(true); - m_pCraftLabel->SetVisible(true); - m_pCraftBox->SetVisible(true); - m_BlinkMode = NOBLINK; - } -*/ +void SceneEditorGUI::Update() { + // Update the user controller + // m_pController->Update(); + + m_EditMade = false; + m_pObjectToBlink = 0; + // Which set of placed objects in the scene we're editing + int editedSet = m_FeatureSet == ONLOADEDIT ? Scene::PLACEONLOAD : (m_FeatureSet == AIPLANEDIT ? Scene::AIPLAN : Scene::BLUEPRINT); + + //////////////////////////////////////////// + // Blinking logic + /* + if (m_BlinkMode == OBJECTBLINK) + { + m_pCostLabel->SetVisible(m_BlinkTimer.AlternateSim(250)); + } + else if (m_BlinkMode == NOCRAFT) + { + bool blink = m_BlinkTimer.AlternateSim(250); + m_pCraftLabel->SetVisible(blink); + m_pCraftBox->SetVisible(blink); + } + + // Time out the blinker + if (m_BlinkMode != NOBLINK && m_BlinkTimer.IsPastSimMS(1500)) + { + m_pCostLabel->SetVisible(true); + m_pCraftLabel->SetVisible(true); + m_pCraftBox->SetVisible(true); + m_BlinkMode = NOBLINK; + } + */ if (m_pCurrentObject && m_EditorGUIMode != PICKINGOBJECT && g_PresetMan.GetReloadEntityPresetCalledThisUpdate()) { - m_pCurrentObject = dynamic_cast(g_PresetMan.GetEntityPreset(m_pCurrentObject->GetClassName(), m_pCurrentObject->GetPresetName(), m_pCurrentObject->GetModuleName())->Clone()); + m_pCurrentObject = dynamic_cast(g_PresetMan.GetEntityPreset(m_pCurrentObject->GetClassName(), m_pCurrentObject->GetPresetName(), m_pCurrentObject->GetModuleName())->Clone()); + } + + ///////////////////////////////////////////// + // Repeating input logic + + bool pressLeft = m_pController->IsState(PRESS_LEFT); + bool pressRight = m_pController->IsState(PRESS_RIGHT); + bool pressUp = m_pController->IsState(PRESS_UP); + bool pressDown = m_pController->IsState(PRESS_DOWN); + + // If no direciton is held down, then cancel the repeating + if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) { + m_RepeatStartTimer.Reset(); + m_RepeatTimer.Reset(); } - ///////////////////////////////////////////// - // Repeating input logic - - bool pressLeft = m_pController->IsState(PRESS_LEFT); - bool pressRight = m_pController->IsState(PRESS_RIGHT); - bool pressUp = m_pController->IsState(PRESS_UP); - bool pressDown = m_pController->IsState(PRESS_DOWN); - - // If no direciton is held down, then cancel the repeating - if (!(m_pController->IsState(MOVE_RIGHT) || m_pController->IsState(MOVE_LEFT) || m_pController->IsState(MOVE_UP) || m_pController->IsState(MOVE_DOWN))) - { - m_RepeatStartTimer.Reset(); - m_RepeatTimer.Reset(); - } - - // Check if any direction has been held for the starting amount of time to get into repeat mode - if (m_RepeatStartTimer.IsPastRealMS(200)) - { - // Check for the repeat interval - if (m_RepeatTimer.IsPastRealMS(30)) - { - if (m_pController->IsState(MOVE_RIGHT)) - pressRight = true; - else if (m_pController->IsState(MOVE_LEFT)) - pressLeft = true; - - if (m_pController->IsState(MOVE_UP)) - pressUp = true; - else if (m_pController->IsState(MOVE_DOWN)) - pressDown = true; - - m_RepeatTimer.Reset(); - } - } - - /////////////////////////////////////////////// - // Analog cursor input - - Vector analogInput; - if (m_pController->GetAnalogMove().MagnitudeIsGreaterThan(0.1F)) - analogInput = m_pController->GetAnalogMove(); -// else if (m_pController->GetAnalogAim().MagnitudeIsGreaterThan(0.1F)) -// analogInput = m_pController->GetAnalogAim(); - - ///////////////////////////////////////////// - // PIE MENU + // Check if any direction has been held for the starting amount of time to get into repeat mode + if (m_RepeatStartTimer.IsPastRealMS(200)) { + // Check for the repeat interval + if (m_RepeatTimer.IsPastRealMS(30)) { + if (m_pController->IsState(MOVE_RIGHT)) + pressRight = true; + else if (m_pController->IsState(MOVE_LEFT)) + pressLeft = true; + + if (m_pController->IsState(MOVE_UP)) + pressUp = true; + else if (m_pController->IsState(MOVE_DOWN)) + pressDown = true; + + m_RepeatTimer.Reset(); + } + } + + /////////////////////////////////////////////// + // Analog cursor input + + Vector analogInput; + if (m_pController->GetAnalogMove().MagnitudeIsGreaterThan(0.1F)) + analogInput = m_pController->GetAnalogMove(); + // else if (m_pController->GetAnalogAim().MagnitudeIsGreaterThan(0.1F)) + // analogInput = m_pController->GetAnalogAim(); + + ///////////////////////////////////////////// + // PIE MENU m_PieMenu->Update(); - // Show the pie menu only when the secondary button is held down - if (m_pController->IsState(PIE_MENU_ACTIVE) && m_EditorGUIMode != INACTIVE && m_EditorGUIMode != PICKINGOBJECT) { - m_PieMenu->SetEnabled(true); - m_PieMenu->SetPos(m_GridSnapping ? g_SceneMan.SnapPosition(m_CursorPos) : m_CursorPos); + // Show the pie menu only when the secondary button is held down + if (m_pController->IsState(PIE_MENU_ACTIVE) && m_EditorGUIMode != INACTIVE && m_EditorGUIMode != PICKINGOBJECT) { + m_PieMenu->SetEnabled(true); + m_PieMenu->SetPos(m_GridSnapping ? g_SceneMan.SnapPosition(m_CursorPos) : m_CursorPos); - std::array infrontAndBehindPieSlices = { m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorInFront), m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorBehind) }; - for (PieSlice* pieSlice : infrontAndBehindPieSlices) { - if (pieSlice) { pieSlice->SetEnabled(m_EditorGUIMode == ADDINGOBJECT); } - } - } else { - m_PieMenu->SetEnabled(false); - } + std::array infrontAndBehindPieSlices = {m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorInFront), m_PieMenu->GetFirstPieSliceByType(PieSlice::SliceType::EditorBehind)}; + for (PieSlice* pieSlice: infrontAndBehindPieSlices) { + if (pieSlice) { + pieSlice->SetEnabled(m_EditorGUIMode == ADDINGOBJECT); + } + } + } else { + m_PieMenu->SetEnabled(false); + } - /////////////////////////////////////// - // Handle pie menu selections + /////////////////////////////////////// + // Handle pie menu selections - if (m_PieMenu->GetPieCommand() != PieSlice::SliceType::NoType) { + if (m_PieMenu->GetPieCommand() != PieSlice::SliceType::NoType) { if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorPick) { m_EditorGUIMode = PICKINGOBJECT; } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorMove) { @@ -499,11 +467,11 @@ void SceneEditorGUI::Update() } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorDone) { m_EditorGUIMode = DONEEDITING; } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorInFront) { - m_PreviousMode = m_EditorGUIMode; - m_EditorGUIMode = PLACEINFRONT; - } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorBehind) { - m_PreviousMode = m_EditorGUIMode; - m_EditorGUIMode = PLACEBEHIND; + m_PreviousMode = m_EditorGUIMode; + m_EditorGUIMode = PLACEINFRONT; + } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorBehind) { + m_PreviousMode = m_EditorGUIMode; + m_EditorGUIMode = PLACEBEHIND; } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorTeam1) { m_PlaceTeam = Activity::TeamOne; } else if (m_PieMenu->GetPieCommand() == PieSlice::SliceType::EditorTeam2) { @@ -517,397 +485,353 @@ void SceneEditorGUI::Update() SetFeatureSet(m_FeatureSet == FeatureSets::ONLOADEDIT ? FeatureSets::AIPLANEDIT : FeatureSets::ONLOADEDIT); } - UpdateBrainPath(); - m_ModeChanged = true; - } - - ////////////////////////////////////////// - // Picker logic - - // Enable or disable the picker - m_pPicker->SetEnabled(m_EditorGUIMode == PICKINGOBJECT); - - // Update the picker GUI - m_pPicker->Update(); - - if (m_EditorGUIMode == PICKINGOBJECT && m_pPicker->ObjectPicked()) - { - // Assign a copy of the picked object to be the currently held one. - if (SetCurrentObject(dynamic_cast(m_pPicker->ObjectPicked()->Clone()))) - { - // Set the team - if (m_FeatureSet != ONLOADEDIT) - m_pCurrentObject->SetTeam(m_pController->GetTeam()); - // Set the std::list order to be at the end so new objects are added there - m_ObjectListOrder = -1; - // Update the object - m_pCurrentObject->FullUpdate(); - // Update the path to the brain, or clear it if there's none - UpdateBrainPath(); - - // If done picking, revert to moving object mode - if (m_pPicker->DonePicking()) - { - // If picked a Brain Actor, then enter install brain mode. Allow to use deployments in brain mode only while editing in-game - if (m_pCurrentObject->IsInGroup("Brains") && (m_FeatureSet == INGAMEEDIT || !dynamic_cast(m_pCurrentObject))) + UpdateBrainPath(); + m_ModeChanged = true; + } + + ////////////////////////////////////////// + // Picker logic + + // Enable or disable the picker + m_pPicker->SetEnabled(m_EditorGUIMode == PICKINGOBJECT); + + // Update the picker GUI + m_pPicker->Update(); + + if (m_EditorGUIMode == PICKINGOBJECT && m_pPicker->ObjectPicked()) { + // Assign a copy of the picked object to be the currently held one. + if (SetCurrentObject(dynamic_cast(m_pPicker->ObjectPicked()->Clone()))) { + // Set the team + if (m_FeatureSet != ONLOADEDIT) + m_pCurrentObject->SetTeam(m_pController->GetTeam()); + // Set the std::list order to be at the end so new objects are added there + m_ObjectListOrder = -1; + // Update the object + m_pCurrentObject->FullUpdate(); + // Update the path to the brain, or clear it if there's none + UpdateBrainPath(); + + // If done picking, revert to moving object mode + if (m_pPicker->DonePicking()) { + // If picked a Brain Actor, then enter install brain mode. Allow to use deployments in brain mode only while editing in-game + if (m_pCurrentObject->IsInGroup("Brains") && (m_FeatureSet == INGAMEEDIT || !dynamic_cast(m_pCurrentObject))) m_EditorGUIMode = INSTALLINGBRAIN; - else - m_EditorGUIMode = ADDINGOBJECT; - - UpdateBrainPath(); - m_ModeChanged = true; - } - } - } - - if (!m_pPicker->IsVisible()) - g_CameraMan.SetScreenOcclusion(Vector(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - else - g_FrameMan.SetScreenText("Pick what you want to place next", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - ///////////////////////////////////// - // ADDING OBJECT MODE - - if (m_EditorGUIMode == ADDINGOBJECT && !m_PieMenu->IsEnabled()) - { - if (m_ModeChanged) - { - - m_ModeChanged = false; - } - g_FrameMan.SetScreenText("Click to ADD a new object - Drag for precision", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - - m_DrawCurrentObject = true; - - // Trap the mouse cursor - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - - // Move the cursor according to analog or mouse input - if (!analogInput.IsZero()) - { - m_CursorPos += analogInput * 8; - // Re-enable snapping only when the cursor is moved again - m_GridSnapping = true; - } - else if (!m_pController->GetMouseMovement().IsZero()) - { - m_CursorPos += m_pController->GetMouseMovement(); - // Re-enable snapping only when the cursor is moved again - m_GridSnapping = true; - } - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= SCENESNAPSIZE; - if (pressRight) - m_CursorPos.m_X += SCENESNAPSIZE; - if (pressDown) - m_CursorPos.m_Y += SCENESNAPSIZE; - if (pressLeft) - m_CursorPos.m_X -= SCENESNAPSIZE; - // Re-enable snapping only when the cursor is moved again - if (pressUp || pressRight || pressDown || pressLeft) - m_GridSnapping = true; - } - - // Detect whether the cursor is in the air, or if it's overlapping some terrain - Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); - m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; - - // Mousewheel is used as shortcut for getting next and prev items in teh picker's object std::list - if (m_pController->IsState(SCROLL_UP) || m_pController->IsState(ControlState::ACTOR_NEXT)) - { - // Assign a copy of the next picked object to be the currently held one. - const SceneObject *pNewObject = m_pPicker->GetPrevObject(); - if (pNewObject) - { - // Set and update the cursor object - if (SetCurrentObject(dynamic_cast(pNewObject->Clone()))) - m_pCurrentObject->FullUpdate(); - } - } - else if (m_pController->IsState(SCROLL_DOWN) || m_pController->IsState(ControlState::ACTOR_PREV)) - { - // Assign a copy of the next picked object to be the currently held one. - const SceneObject *pNewObject = m_pPicker->GetNextObject(); - if (pNewObject) - { - // Set and update the object - if (SetCurrentObject(dynamic_cast(pNewObject->Clone()))) - m_pCurrentObject->FullUpdate(); - } - } - - // Start the timer when the button is first pressed, and when the picker has deactivated - if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) - { - m_BlinkTimer.Reset(); - m_EditorGUIMode = PLACINGOBJECT; - m_PreviousMode = ADDINGOBJECT; - m_ModeChanged = true; - g_GUISound.PlacementBlip()->Play(m_pController->GetPlayer()); - } - - // Apply the team to the current actor, if applicable - if (m_pCurrentObject && m_DrawCurrentObject) - { - // Set the team of SceneObject based on what's been selected - // Only if full featured mode, otherwise it's based on the controller when placed - if (m_FeatureSet == ONLOADEDIT) - m_pCurrentObject->SetTeam(m_PlaceTeam); - } - } - - ///////////////////////////////////// - // INSTALLING BRAIN MODE - - if (m_EditorGUIMode == INSTALLINGBRAIN && !m_PieMenu->IsEnabled()) - { - if (m_ModeChanged) - { - // Check if we already have a resident brain to replace/re-place - SceneObject *pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); - if (pBrain) - { - // Fly over to where the brain was previously found - SetCursorPos(pBrain->GetPos()); - // If the brain in hand is not appropriate replacement, then just use the resident brain as a copy to re-place it - if (!m_pCurrentObject->IsInGroup("Brains")) - { - // Make sure the player's resident brain is the one being held in cursor - SetCurrentObject(dynamic_cast(pBrain->Clone())); - // Clear the brain of the scene; it will be reinstated when we place it again -// NOPE, keep it here in case placing the brain goes awry - it won't be drawn anyway -// g_SceneMan.GetScene()->SetResidentBrain(m_pController->GetPlayer(), 0); - } - } - // Ok, so no resident brain - do we have one in hand already?? - else if (m_pCurrentObject && m_pCurrentObject->IsInGroup("Brains")) - { - // Continue with placing it as per usual - ; - } - // Pick a brain to install if no one existed already in scene or in hand - else - { - m_pPicker->SelectGroupByName("Brains"); - m_EditorGUIMode = PICKINGOBJECT; - m_ModeChanged = true; - UpdateBrainPath(); - } - - m_ModeChanged = false; - } - g_FrameMan.SetScreenText("Click to INSTALL your governor brain with a clear path to orbit - Drag for precision", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - m_DrawCurrentObject = true; - - // Trap the mouse cursor - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - - // Move the cursor according to analog or mouse input - if (!analogInput.IsZero()) - { - m_CursorPos += analogInput * 8; - // Re-enable snapping only when the cursor is moved again - m_GridSnapping = true; - } - else if (!m_pController->GetMouseMovement().IsZero()) - { - m_CursorPos += m_pController->GetMouseMovement(); - // Re-enable snapping only when the cursor is moved again - m_GridSnapping = true; - } - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= SCENESNAPSIZE; - if (pressRight) - m_CursorPos.m_X += SCENESNAPSIZE; - if (pressDown) - m_CursorPos.m_Y += SCENESNAPSIZE; - if (pressLeft) - m_CursorPos.m_X -= SCENESNAPSIZE; - // Re-enable snapping only when the cursor is moved again - if (pressUp || pressRight || pressDown || pressLeft) - m_GridSnapping = true; - } - - // Detect whether the cursor is in the air, or if it's overlapping some terrain - Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); - m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; - - // Check brain position validity with pathfinding and show a path to the sky + else + m_EditorGUIMode = ADDINGOBJECT; + + UpdateBrainPath(); + m_ModeChanged = true; + } + } + } + + if (!m_pPicker->IsVisible()) + g_CameraMan.SetScreenOcclusion(Vector(), g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + else + g_FrameMan.SetScreenText("Pick what you want to place next", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + ///////////////////////////////////// + // ADDING OBJECT MODE + + if (m_EditorGUIMode == ADDINGOBJECT && !m_PieMenu->IsEnabled()) { + if (m_ModeChanged) { + + m_ModeChanged = false; + } + g_FrameMan.SetScreenText("Click to ADD a new object - Drag for precision", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + m_DrawCurrentObject = true; + + // Trap the mouse cursor + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + + // Move the cursor according to analog or mouse input + if (!analogInput.IsZero()) { + m_CursorPos += analogInput * 8; + // Re-enable snapping only when the cursor is moved again + m_GridSnapping = true; + } else if (!m_pController->GetMouseMovement().IsZero()) { + m_CursorPos += m_pController->GetMouseMovement(); + // Re-enable snapping only when the cursor is moved again + m_GridSnapping = true; + } + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= SCENESNAPSIZE; + if (pressRight) + m_CursorPos.m_X += SCENESNAPSIZE; + if (pressDown) + m_CursorPos.m_Y += SCENESNAPSIZE; + if (pressLeft) + m_CursorPos.m_X -= SCENESNAPSIZE; + // Re-enable snapping only when the cursor is moved again + if (pressUp || pressRight || pressDown || pressLeft) + m_GridSnapping = true; + } + + // Detect whether the cursor is in the air, or if it's overlapping some terrain + Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); + m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; + + // Mousewheel is used as shortcut for getting next and prev items in teh picker's object std::list + if (m_pController->IsState(SCROLL_UP) || m_pController->IsState(ControlState::ACTOR_NEXT)) { + // Assign a copy of the next picked object to be the currently held one. + const SceneObject* pNewObject = m_pPicker->GetPrevObject(); + if (pNewObject) { + // Set and update the cursor object + if (SetCurrentObject(dynamic_cast(pNewObject->Clone()))) + m_pCurrentObject->FullUpdate(); + } + } else if (m_pController->IsState(SCROLL_DOWN) || m_pController->IsState(ControlState::ACTOR_PREV)) { + // Assign a copy of the next picked object to be the currently held one. + const SceneObject* pNewObject = m_pPicker->GetNextObject(); + if (pNewObject) { + // Set and update the object + if (SetCurrentObject(dynamic_cast(pNewObject->Clone()))) + m_pCurrentObject->FullUpdate(); + } + } + + // Start the timer when the button is first pressed, and when the picker has deactivated + if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) { + m_BlinkTimer.Reset(); + m_EditorGUIMode = PLACINGOBJECT; + m_PreviousMode = ADDINGOBJECT; + m_ModeChanged = true; + g_GUISound.PlacementBlip()->Play(m_pController->GetPlayer()); + } + + // Apply the team to the current actor, if applicable + if (m_pCurrentObject && m_DrawCurrentObject) { + // Set the team of SceneObject based on what's been selected + // Only if full featured mode, otherwise it's based on the controller when placed + if (m_FeatureSet == ONLOADEDIT) + m_pCurrentObject->SetTeam(m_PlaceTeam); + } + } + + ///////////////////////////////////// + // INSTALLING BRAIN MODE + + if (m_EditorGUIMode == INSTALLINGBRAIN && !m_PieMenu->IsEnabled()) { + if (m_ModeChanged) { + // Check if we already have a resident brain to replace/re-place + SceneObject* pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); + if (pBrain) { + // Fly over to where the brain was previously found + SetCursorPos(pBrain->GetPos()); + // If the brain in hand is not appropriate replacement, then just use the resident brain as a copy to re-place it + if (!m_pCurrentObject->IsInGroup("Brains")) { + // Make sure the player's resident brain is the one being held in cursor + SetCurrentObject(dynamic_cast(pBrain->Clone())); + // Clear the brain of the scene; it will be reinstated when we place it again + // NOPE, keep it here in case placing the brain goes awry - it won't be drawn anyway + // g_SceneMan.GetScene()->SetResidentBrain(m_pController->GetPlayer(), 0); + } + } + // Ok, so no resident brain - do we have one in hand already?? + else if (m_pCurrentObject && m_pCurrentObject->IsInGroup("Brains")) { + // Continue with placing it as per usual + ; + } + // Pick a brain to install if no one existed already in scene or in hand + else { + m_pPicker->SelectGroupByName("Brains"); + m_EditorGUIMode = PICKINGOBJECT; + m_ModeChanged = true; + UpdateBrainPath(); + } + + m_ModeChanged = false; + } + g_FrameMan.SetScreenText("Click to INSTALL your governor brain with a clear path to orbit - Drag for precision", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + m_DrawCurrentObject = true; + + // Trap the mouse cursor + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + + // Move the cursor according to analog or mouse input + if (!analogInput.IsZero()) { + m_CursorPos += analogInput * 8; + // Re-enable snapping only when the cursor is moved again + m_GridSnapping = true; + } else if (!m_pController->GetMouseMovement().IsZero()) { + m_CursorPos += m_pController->GetMouseMovement(); + // Re-enable snapping only when the cursor is moved again + m_GridSnapping = true; + } + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= SCENESNAPSIZE; + if (pressRight) + m_CursorPos.m_X += SCENESNAPSIZE; + if (pressDown) + m_CursorPos.m_Y += SCENESNAPSIZE; + if (pressLeft) + m_CursorPos.m_X -= SCENESNAPSIZE; + // Re-enable snapping only when the cursor is moved again + if (pressUp || pressRight || pressDown || pressLeft) + m_GridSnapping = true; + } + + // Detect whether the cursor is in the air, or if it's overlapping some terrain + Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); + m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; + + // Check brain position validity with pathfinding and show a path to the sky UpdateBrainSkyPathAndCost(m_CursorPos); -/* - // Process the new path we now have, if any - if (!m_BrainSkyPath.empty()) - { - // Smash all airborne waypoints down to just above the ground, except for when it makes the path intersect terrain or it is the final destination - std::list::iterator finalItr = m_BrainSkyPath.end(); - finalItr--; - Vector smashedPoint; - Vector previousPoint = *(m_BrainSkyPath.begin()); - std::list::iterator nextItr = m_BrainSkyPath.begin(); - for (std::list::iterator lItr = m_BrainSkyPath.begin(); lItr != finalItr; ++lItr) - { - nextItr++; - smashedPoint = g_SceneMan.MovePointToGround((*lItr), 20, 10); - Vector notUsed; - - // Only smash if the new location doesn't cause the path to intersect hard terrain ahead or behind of it - // Try three times to halve the height to see if that won't intersect - for (int i = 0; i < 3; i++) - { - if (!g_SceneMan.CastStrengthRay(previousPoint, smashedPoint - previousPoint, 5, notUsed, 3, g_MaterialDoor) && - nextItr != m_BrainSkyPath.end() && !g_SceneMan.CastStrengthRay(smashedPoint, (*nextItr) - smashedPoint, 5, notUsed, 3, g_MaterialDoor)) - { - (*lItr) = smashedPoint; - break; - } - else - smashedPoint.m_Y -= ((smashedPoint.m_Y - (*lItr).m_Y) / 2); - } - - previousPoint = (*lItr); - } - } -*/ - // Start the timer when the button is first pressed, and when the picker has deactivated - if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) - { - m_BlinkTimer.Reset(); - m_EditorGUIMode = PLACINGOBJECT; - m_PreviousMode = INSTALLINGBRAIN; - m_ModeChanged = true; - g_GUISound.PlacementBlip()->Play(m_pController->GetPlayer()); - } - - // Apply the team to the current actor, if applicable - if (m_pCurrentObject && m_DrawCurrentObject) - { - // Set the team of SceneObject based on what's been selected - // Only if full featured mode, otherwise it's based on the controller when placed - if (m_FeatureSet == ONLOADEDIT) - m_pCurrentObject->SetTeam(m_PlaceTeam); - } - } - - ///////////////////////////////////////////////////////////// - // PLACING MODE - - else if (m_EditorGUIMode == PLACINGOBJECT) - { - if (m_ModeChanged) - { - - m_ModeChanged = false; - } - - if (m_PreviousMode == MOVINGOBJECT) - g_FrameMan.SetScreenText("Click and drag on a placed object to MOVE it - Click quickly to DETACH", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - else if (m_PreviousMode == INSTALLINGBRAIN) - g_FrameMan.SetScreenText("Release to INSTALL the governor brain - Tap other button to cancel", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - else - g_FrameMan.SetScreenText("Release to ADD the new object - Tap other button to cancel", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - // Check brain position validity with pathfinding and show a path to the sky - if (m_PreviousMode == INSTALLINGBRAIN) { UpdateBrainSkyPathAndCost(m_CursorPos); } - - m_DrawCurrentObject = true; - - // Freeze when first pressing down and grid snapping is still engaged - if (!(m_pController->IsState(PRIMARY_ACTION) && m_GridSnapping)) - { - if (!analogInput.IsZero()) - { - m_CursorPos += analogInput; - m_FacingLeft = analogInput.m_X < 0 || (m_FacingLeft && analogInput.m_X == 0); - } - // Try the mouse - else if (!m_pController->GetMouseMovement().IsZero()) - { - m_CursorPos += m_pController->GetMouseMovement(); - m_FacingLeft = m_pController->GetMouseMovement().m_X < 0 || (m_FacingLeft && m_pController->GetMouseMovement().m_X == 0); - } - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= 1; - if (pressRight) - { - m_CursorPos.m_X += 1; - m_FacingLeft = false; - } - if (pressDown) - m_CursorPos.m_Y += 1; - if (pressLeft) - { - m_CursorPos.m_X -= 1; - m_FacingLeft = true; - } - } - - // Detect whether the cursor is in the air, or if it's overlapping some terrain - Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); - m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; - // Also check that it isn't over unseen areas, can't place there - m_CursorInAir = m_CursorInAir && !g_SceneMan.IsUnseen(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY(), m_pController->GetTeam()); - } - - // Constrain the cursor to only be within specific scene areas -// TODO: THIS!!! - - // Disable snapping after a small interval of holding down the button, to avoid unintentional nudges when just placing on the grid - if (m_pController->IsState(PRIMARY_ACTION) && m_BlinkTimer.IsPastRealMS(333) && m_GridSnapping) - { - m_GridSnapping = false; - m_CursorPos = g_SceneMan.SnapPosition(m_CursorPos); - } - - // Cancel placing if secondary button is pressed - if (m_pController->IsState(PRESS_SECONDARY) || m_pController->IsState(PIE_MENU_ACTIVE)) - { - m_EditorGUIMode = m_PreviousMode; - m_ModeChanged = true; - } - // If previous mode was moving, tear the gib loose if the button is released to soo - else if (m_PreviousMode == MOVINGOBJECT && m_pController->IsState(RELEASE_PRIMARY) && !m_BlinkTimer.IsPastRealMS(150)) - { - m_EditorGUIMode = ADDINGOBJECT; - m_ModeChanged = true; - } - // Only place if the picker and pie menus are completely out of view, to avoid immediate placing after picking - else if (m_pCurrentObject && m_pController->IsState(RELEASE_PRIMARY) && !m_pPicker->IsVisible()) - { - m_pCurrentObject->FullUpdate(); - - // Placing governor brain, which actually just puts it back into the resident brain roster - if (m_PreviousMode == INSTALLINGBRAIN) - { - // Force our path request to complete so we know whether we can place or not - while (m_PathRequest && !m_PathRequest->complete) {}; - - // Only place if the brain has a clear path to the sky! - if (m_BrainSkyPathCost <= MAXBRAINPATHCOST || !m_RequireClearPathToOrbit) - { + /* + // Process the new path we now have, if any + if (!m_BrainSkyPath.empty()) + { + // Smash all airborne waypoints down to just above the ground, except for when it makes the path intersect terrain or it is the final destination + std::list::iterator finalItr = m_BrainSkyPath.end(); + finalItr--; + Vector smashedPoint; + Vector previousPoint = *(m_BrainSkyPath.begin()); + std::list::iterator nextItr = m_BrainSkyPath.begin(); + for (std::list::iterator lItr = m_BrainSkyPath.begin(); lItr != finalItr; ++lItr) + { + nextItr++; + smashedPoint = g_SceneMan.MovePointToGround((*lItr), 20, 10); + Vector notUsed; + + // Only smash if the new location doesn't cause the path to intersect hard terrain ahead or behind of it + // Try three times to halve the height to see if that won't intersect + for (int i = 0; i < 3; i++) + { + if (!g_SceneMan.CastStrengthRay(previousPoint, smashedPoint - previousPoint, 5, notUsed, 3, g_MaterialDoor) && + nextItr != m_BrainSkyPath.end() && !g_SceneMan.CastStrengthRay(smashedPoint, (*nextItr) - smashedPoint, 5, notUsed, 3, g_MaterialDoor)) + { + (*lItr) = smashedPoint; + break; + } + else + smashedPoint.m_Y -= ((smashedPoint.m_Y - (*lItr).m_Y) / 2); + } + + previousPoint = (*lItr); + } + } + */ + // Start the timer when the button is first pressed, and when the picker has deactivated + if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) { + m_BlinkTimer.Reset(); + m_EditorGUIMode = PLACINGOBJECT; + m_PreviousMode = INSTALLINGBRAIN; + m_ModeChanged = true; + g_GUISound.PlacementBlip()->Play(m_pController->GetPlayer()); + } + + // Apply the team to the current actor, if applicable + if (m_pCurrentObject && m_DrawCurrentObject) { + // Set the team of SceneObject based on what's been selected + // Only if full featured mode, otherwise it's based on the controller when placed + if (m_FeatureSet == ONLOADEDIT) + m_pCurrentObject->SetTeam(m_PlaceTeam); + } + } + + ///////////////////////////////////////////////////////////// + // PLACING MODE + + else if (m_EditorGUIMode == PLACINGOBJECT) { + if (m_ModeChanged) { + + m_ModeChanged = false; + } + + if (m_PreviousMode == MOVINGOBJECT) + g_FrameMan.SetScreenText("Click and drag on a placed object to MOVE it - Click quickly to DETACH", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + else if (m_PreviousMode == INSTALLINGBRAIN) + g_FrameMan.SetScreenText("Release to INSTALL the governor brain - Tap other button to cancel", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + else + g_FrameMan.SetScreenText("Release to ADD the new object - Tap other button to cancel", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + // Check brain position validity with pathfinding and show a path to the sky + if (m_PreviousMode == INSTALLINGBRAIN) { + UpdateBrainSkyPathAndCost(m_CursorPos); + } + + m_DrawCurrentObject = true; + + // Freeze when first pressing down and grid snapping is still engaged + if (!(m_pController->IsState(PRIMARY_ACTION) && m_GridSnapping)) { + if (!analogInput.IsZero()) { + m_CursorPos += analogInput; + m_FacingLeft = analogInput.m_X < 0 || (m_FacingLeft && analogInput.m_X == 0); + } + // Try the mouse + else if (!m_pController->GetMouseMovement().IsZero()) { + m_CursorPos += m_pController->GetMouseMovement(); + m_FacingLeft = m_pController->GetMouseMovement().m_X < 0 || (m_FacingLeft && m_pController->GetMouseMovement().m_X == 0); + } + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= 1; + if (pressRight) { + m_CursorPos.m_X += 1; + m_FacingLeft = false; + } + if (pressDown) + m_CursorPos.m_Y += 1; + if (pressLeft) { + m_CursorPos.m_X -= 1; + m_FacingLeft = true; + } + } + + // Detect whether the cursor is in the air, or if it's overlapping some terrain + Vector snappedPos = g_SceneMan.SnapPosition(m_CursorPos, m_GridSnapping); + m_CursorInAir = g_SceneMan.GetTerrMatter(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY()) == g_MaterialAir; + // Also check that it isn't over unseen areas, can't place there + m_CursorInAir = m_CursorInAir && !g_SceneMan.IsUnseen(snappedPos.GetFloorIntX(), snappedPos.GetFloorIntY(), m_pController->GetTeam()); + } + + // Constrain the cursor to only be within specific scene areas + // TODO: THIS!!! + + // Disable snapping after a small interval of holding down the button, to avoid unintentional nudges when just placing on the grid + if (m_pController->IsState(PRIMARY_ACTION) && m_BlinkTimer.IsPastRealMS(333) && m_GridSnapping) { + m_GridSnapping = false; + m_CursorPos = g_SceneMan.SnapPosition(m_CursorPos); + } + + // Cancel placing if secondary button is pressed + if (m_pController->IsState(PRESS_SECONDARY) || m_pController->IsState(PIE_MENU_ACTIVE)) { + m_EditorGUIMode = m_PreviousMode; + m_ModeChanged = true; + } + // If previous mode was moving, tear the gib loose if the button is released to soo + else if (m_PreviousMode == MOVINGOBJECT && m_pController->IsState(RELEASE_PRIMARY) && !m_BlinkTimer.IsPastRealMS(150)) { + m_EditorGUIMode = ADDINGOBJECT; + m_ModeChanged = true; + } + // Only place if the picker and pie menus are completely out of view, to avoid immediate placing after picking + else if (m_pCurrentObject && m_pController->IsState(RELEASE_PRIMARY) && !m_pPicker->IsVisible()) { + m_pCurrentObject->FullUpdate(); + + // Placing governor brain, which actually just puts it back into the resident brain roster + if (m_PreviousMode == INSTALLINGBRAIN) { + // Force our path request to complete so we know whether we can place or not + while (m_PathRequest && !m_PathRequest->complete) {}; + + // Only place if the brain has a clear path to the sky! + if (m_BrainSkyPathCost <= MAXBRAINPATHCOST || !m_RequireClearPathToOrbit) { bool placeBrain = true; // Brain deployment's are translated into the appropriate loadout and put in place in the scene // but only while editing ingame - Deployment *pDep = dynamic_cast(m_pCurrentObject); - if (pDep) - { - if (m_FeatureSet == INGAMEEDIT) - { + Deployment* pDep = dynamic_cast(m_pCurrentObject); + if (pDep) { + if (m_FeatureSet == INGAMEEDIT) { float cost; - Actor *pActor = pDep->CreateDeployedActor(pDep->GetPlacedByPlayer(), cost); - if (pActor && pActor->IsInGroup("Brains")) - { + Actor* pActor = pDep->CreateDeployedActor(pDep->GetPlacedByPlayer(), cost); + if (pActor && pActor->IsInGroup("Brains")) { delete m_pCurrentObject; m_pCurrentObject = pActor; } else { @@ -918,12 +842,11 @@ void SceneEditorGUI::Update() } } - if (placeBrain) - { + if (placeBrain) { // Place and let go (passing ownership) of the new governor brain g_SceneMan.GetScene()->SetResidentBrain(m_pController->GetPlayer(), m_pCurrentObject); - // NO! This deletes the brain we just passed ownership of! just let go, man - // SetCurrentObject(0); + // NO! This deletes the brain we just passed ownership of! just let go, man + // SetCurrentObject(0); m_pCurrentObject = 0; // Nothing in cursor now, so force to pick somehting else to place right away m_EditorGUIMode = PICKINGOBJECT; @@ -937,62 +860,54 @@ void SceneEditorGUI::Update() UpdateBrainPath(); g_GUISound.PlacementThud()->Play(m_pController->GetPlayer()); } - } - // If no clear path to the sky, just reject the placment and keep the brain in hand - else - { - g_FrameMan.ClearScreenText(g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - g_FrameMan.SetScreenText("Your brain can only be placed with a clear access path to orbit!", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer()), 333, 3500); - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - } - // Non-brain thing is being placed - else - { - // If we're not editing in-game, then just add to the placed objects std::list - if (m_FeatureSet != INGAMEEDIT) - { - //If true we need to place object in the end, if false, then it was already given to an actor + } + // If no clear path to the sky, just reject the placment and keep the brain in hand + else { + g_FrameMan.ClearScreenText(g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + g_FrameMan.SetScreenText("Your brain can only be placed with a clear access path to orbit!", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer()), 333, 3500); + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + } + // Non-brain thing is being placed + else { + // If we're not editing in-game, then just add to the placed objects std::list + if (m_FeatureSet != INGAMEEDIT) { + // If true we need to place object in the end, if false, then it was already given to an actor bool toPlace = true; - //If we're placing an item then give that item to actor instead of dropping it nearby - HeldDevice *pHeldDevice = dynamic_cast(m_pCurrentObject); - if (pHeldDevice) - if (dynamic_cast(pHeldDevice) || dynamic_cast(pHeldDevice)) - { + // If we're placing an item then give that item to actor instead of dropping it nearby + HeldDevice* pHeldDevice = dynamic_cast(m_pCurrentObject); + if (pHeldDevice) + if (dynamic_cast(pHeldDevice) || dynamic_cast(pHeldDevice)) { int objectListPosition = -1; - //Find out if we have AHuman under the cursor - const SceneObject *pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &objectListPosition); - const Actor *pAHuman = 0; + // Find out if we have AHuman under the cursor + const SceneObject* pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &objectListPosition); + const Actor* pAHuman = 0; // Looks like we got nothing, search for actors in range then if (!pPickedSceneObject) - pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedActorInRange(editedSet, m_CursorPos, 20, &objectListPosition); + pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedActorInRange(editedSet, m_CursorPos, 20, &objectListPosition); - if (pPickedSceneObject) - { - pAHuman = dynamic_cast(pPickedSceneObject); + if (pPickedSceneObject) { + pAHuman = dynamic_cast(pPickedSceneObject); - if (!pAHuman) - { + if (!pAHuman) { // Maybe we clicked the underlying bunker module, search for actor in range - pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedActorInRange(editedSet, m_CursorPos, 20, &objectListPosition); + pPickedSceneObject = g_SceneMan.GetScene()->PickPlacedActorInRange(editedSet, m_CursorPos, 20, &objectListPosition); if (pPickedSceneObject) - pAHuman = dynamic_cast(pPickedSceneObject); + pAHuman = dynamic_cast(pPickedSceneObject); } - if (pAHuman) - { - //Create a new AHuman instead of old one, give him an item, and delete old AHuman - SceneObject * pNewObject = dynamic_cast(pPickedSceneObject->Clone()); + if (pAHuman) { + // Create a new AHuman instead of old one, give him an item, and delete old AHuman + SceneObject* pNewObject = dynamic_cast(pPickedSceneObject->Clone()); g_SceneMan.GetScene()->RemovePlacedObject(editedSet, objectListPosition); - AHuman *pAHuman = dynamic_cast(pNewObject); - if (pAHuman) - { - pAHuman->AddInventoryItem(dynamic_cast(m_pCurrentObject->Clone())); + AHuman* pAHuman = dynamic_cast(pNewObject); + if (pAHuman) { + pAHuman->AddInventoryItem(dynamic_cast(m_pCurrentObject->Clone())); pAHuman->FlashWhite(150); } @@ -1002,19 +917,14 @@ void SceneEditorGUI::Update() g_GUISound.PlacementThud()->Play(m_pController->GetPlayer()); toPlace = false; } - } - else - { - //No human? Look for brains robots then - SceneObject *pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); - if (pBrain) - { - if (g_SceneMan.ShortestDistance(pBrain->GetPos(), m_CursorPos,true).MagnitudeIsLessThan(20.0F)) - { - AHuman * pBrainAHuman = dynamic_cast(pBrain); - if (pBrainAHuman) - { - pBrainAHuman->AddInventoryItem(dynamic_cast(m_pCurrentObject->Clone())); + } else { + // No human? Look for brains robots then + SceneObject* pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); + if (pBrain) { + if (g_SceneMan.ShortestDistance(pBrain->GetPos(), m_CursorPos, true).MagnitudeIsLessThan(20.0F)) { + AHuman* pBrainAHuman = dynamic_cast(pBrain); + if (pBrainAHuman) { + pBrainAHuman->AddInventoryItem(dynamic_cast(m_pCurrentObject->Clone())); pBrainAHuman->FlashWhite(150); m_EditMade = true; g_GUISound.PlacementThud()->Play(m_pController->GetPlayer()); @@ -1025,88 +935,80 @@ void SceneEditorGUI::Update() } } - if (toPlace) - { - g_SceneMan.GetScene()->AddPlacedObject(editedSet, dynamic_cast(m_pCurrentObject->Clone()), m_ObjectListOrder); + if (toPlace) { + g_SceneMan.GetScene()->AddPlacedObject(editedSet, dynamic_cast(m_pCurrentObject->Clone()), m_ObjectListOrder); // Increment the std::list order so we place over last placed item if (m_ObjectListOrder >= 0) m_ObjectListOrder++; g_GUISound.PlacementThud()->Play(m_pController->GetPlayer()); m_EditMade = true; } - } - // If in-game editing, then place into the sim - else - { - // Check if team can afford the placed object and if so, deduct the cost - if (g_ActivityMan.GetActivity()->GetTeamFunds(m_pController->GetTeam()) < m_pCurrentObject->GetTotalValue(m_NativeTechModule, m_ForeignCostMult)) - { - g_FrameMan.ClearScreenText(g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - g_FrameMan.SetScreenText("You can't afford to place that!", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer()), 333, 1500); - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - else - { -// TODO: Experimental! clean up this messiness - SceneObject *pPlacedClone = dynamic_cast(m_pCurrentObject->Clone()); - pPlacedClone->SetTeam(m_pController->GetTeam()); - - TerrainObject *pTO = dynamic_cast(pPlacedClone); - if (pTO) - { - // Deduct the cost from team funds - g_ActivityMan.GetActivity()->ChangeTeamFunds(-m_pCurrentObject->GetTotalValue(m_NativeTechModule, m_ForeignCostMult), m_pController->GetTeam()); + } + // If in-game editing, then place into the sim + else { + // Check if team can afford the placed object and if so, deduct the cost + if (g_ActivityMan.GetActivity()->GetTeamFunds(m_pController->GetTeam()) < m_pCurrentObject->GetTotalValue(m_NativeTechModule, m_ForeignCostMult)) { + g_FrameMan.ClearScreenText(g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + g_FrameMan.SetScreenText("You can't afford to place that!", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer()), 333, 1500); + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } else { + // TODO: Experimental! clean up this messiness + SceneObject* pPlacedClone = dynamic_cast(m_pCurrentObject->Clone()); + pPlacedClone->SetTeam(m_pController->GetTeam()); + + TerrainObject* pTO = dynamic_cast(pPlacedClone); + if (pTO) { + // Deduct the cost from team funds + g_ActivityMan.GetActivity()->ChangeTeamFunds(-m_pCurrentObject->GetTotalValue(m_NativeTechModule, m_ForeignCostMult), m_pController->GetTeam()); pTO->PlaceOnTerrain(g_SceneMan.GetTerrain()); g_SceneMan.GetTerrain()->CleanAir(); Vector terrainObjectPos = pTO->GetPos() + pTO->GetBitmapOffset(); - if (pTO->HasBGColorBitmap()) { g_SceneMan.RegisterTerrainChange(terrainObjectPos.GetFloorIntX(), terrainObjectPos.GetFloorIntY(), pTO->GetBitmapWidth(), pTO->GetBitmapHeight(), ColorKeys::g_MaskColor, true); } - if (pTO->HasFGColorBitmap()) { g_SceneMan.RegisterTerrainChange(terrainObjectPos.GetFloorIntX(), terrainObjectPos.GetFloorIntY(), pTO->GetBitmapWidth(), pTO->GetBitmapHeight(), ColorKeys::g_MaskColor, false); } - -// TODO: Make IsBrain function to see if one was placed - if (pTO->GetPresetName() == "Brain Vault") - { - // Register the brain as this player's - // g_ActivityMan.GetActivity()->SetPlayerBrain(pBrain, m_pController->GetPlayer()); - m_EditorGUIMode = PICKINGOBJECT; - m_ModeChanged = true; - } - - delete pPlacedClone; - pPlacedClone = 0; - g_GUISound.PlacementThud()->Play(m_pController->GetPlayer()); - g_GUISound.PlacementGravel()->Play(m_pController->GetPlayer()); - m_EditMade = true; - } - // Only place if the cursor is clear of terrain obstructions - else if (m_CursorInAir) - { + if (pTO->HasBGColorBitmap()) { + g_SceneMan.RegisterTerrainChange(terrainObjectPos.GetFloorIntX(), terrainObjectPos.GetFloorIntY(), pTO->GetBitmapWidth(), pTO->GetBitmapHeight(), ColorKeys::g_MaskColor, true); + } + if (pTO->HasFGColorBitmap()) { + g_SceneMan.RegisterTerrainChange(terrainObjectPos.GetFloorIntX(), terrainObjectPos.GetFloorIntY(), pTO->GetBitmapWidth(), pTO->GetBitmapHeight(), ColorKeys::g_MaskColor, false); + } + + // TODO: Make IsBrain function to see if one was placed + if (pTO->GetPresetName() == "Brain Vault") { + // Register the brain as this player's + // g_ActivityMan.GetActivity()->SetPlayerBrain(pBrain, m_pController->GetPlayer()); + m_EditorGUIMode = PICKINGOBJECT; + m_ModeChanged = true; + } + + delete pPlacedClone; + pPlacedClone = 0; + g_GUISound.PlacementThud()->Play(m_pController->GetPlayer()); + g_GUISound.PlacementGravel()->Play(m_pController->GetPlayer()); + m_EditMade = true; + } + // Only place if the cursor is clear of terrain obstructions + else if (m_CursorInAir) { float value = m_pCurrentObject->GetTotalValue(m_NativeTechModule, m_ForeignCostMult); // Deployment:s are translated into the appropriate loadout and put in place in the scene - Deployment *pDep = dynamic_cast(pPlacedClone); - if (pDep) - { + Deployment* pDep = dynamic_cast(pPlacedClone); + if (pDep) { // Ownership IS transferred here; pass it along into the MovableMan float cost = 0; - Actor *pActor = pDep->CreateDeployedActor(pDep->GetPlacedByPlayer(), cost); - if (pActor) - { + Actor* pActor = pDep->CreateDeployedActor(pDep->GetPlacedByPlayer(), cost); + if (pActor) { value = cost; g_MovableMan.AddActor(pActor); } // Just a simple Device in the Deployment? - else - { + else { value = cost; // Get the Item/Device and add to scene, passing ownership - SceneObject *pObject = pDep->CreateDeployedObject(pDep->GetPlacedByPlayer(), cost); - MovableObject *pMO = dynamic_cast(pObject); + SceneObject* pObject = pDep->CreateDeployedObject(pDep->GetPlacedByPlayer(), cost); + MovableObject* pMO = dynamic_cast(pObject); if (pMO) g_MovableMan.AddMO(pMO); - else - { + else { delete pObject; pObject = 0; } @@ -1118,534 +1020,474 @@ void SceneEditorGUI::Update() m_EditMade = true; } - // Deduct the cost from team funds - g_ActivityMan.GetActivity()->ChangeTeamFunds(-value, m_pController->GetTeam()); - - Actor *pActor = dynamic_cast(pPlacedClone); - HeldDevice *pDevice = 0; - if (pActor) - { - g_MovableMan.AddActor(pActor); - m_EditMade = true; - } - else if (pDevice = dynamic_cast(pPlacedClone)) - { + // Deduct the cost from team funds + g_ActivityMan.GetActivity()->ChangeTeamFunds(-value, m_pController->GetTeam()); + + Actor* pActor = dynamic_cast(pPlacedClone); + HeldDevice* pDevice = 0; + if (pActor) { + g_MovableMan.AddActor(pActor); + m_EditMade = true; + } else if (pDevice = dynamic_cast(pPlacedClone)) { // If we have a friendly actor or brain nearby then give him an item instead of placing it bool toPlace = true; Vector distanceToActor; - Actor *pNearestActor = g_MovableMan.GetClosestTeamActor(m_pController->GetTeam(), m_pController->GetPlayer(), pPlacedClone->GetPos(), 20, distanceToActor); + Actor* pNearestActor = g_MovableMan.GetClosestTeamActor(m_pController->GetTeam(), m_pController->GetPlayer(), pPlacedClone->GetPos(), 20, distanceToActor); // If we could not find an ordinary actor, then look for brain actor - if (!pNearestActor) - { + if (!pNearestActor) { // Find a brain and check if it's close enough - SceneObject *pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); - if (pBrain) - { - if (g_SceneMan.ShortestDistance(pBrain->GetPos(), pPlacedClone->GetPos(),true).MagnitudeIsLessThan(20.0F)) - { - AHuman * pBrainAHuman = dynamic_cast(pBrain); - if (pBrainAHuman) - { + SceneObject* pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); + if (pBrain) { + if (g_SceneMan.ShortestDistance(pBrain->GetPos(), pPlacedClone->GetPos(), true).MagnitudeIsLessThan(20.0F)) { + AHuman* pBrainAHuman = dynamic_cast(pBrain); + if (pBrainAHuman) { pNearestActor = pBrainAHuman; } } } } - //If we have any AHuman actor then give it an item - if (pNearestActor) - { - AHuman * pNearestAHuman = dynamic_cast(pNearestActor); - if (pNearestAHuman) - { + // If we have any AHuman actor then give it an item + if (pNearestActor) { + AHuman* pNearestAHuman = dynamic_cast(pNearestActor); + if (pNearestAHuman) { pNearestAHuman->AddInventoryItem(pDevice); - //TODO: Resident brain remains white when flashed, don't know how to avoid that. Better than nothing though + // TODO: Resident brain remains white when flashed, don't know how to avoid that. Better than nothing though pNearestAHuman->FlashWhite(150); toPlace = false; m_EditMade = true; } } - if (toPlace) - { - g_MovableMan.AddItem(pDevice); - m_EditMade = true; + if (toPlace) { + g_MovableMan.AddItem(pDevice); + m_EditMade = true; } - } - // Something else - else - { - MovableObject *pObj = dynamic_cast(pPlacedClone); - if (pObj) - g_MovableMan.AddParticle(pObj); - m_EditMade = true; - } - } - // Something was wrong with placement - else - { - delete pPlacedClone; - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - } - } - } -// TEMP REMOVE WEHN YOU CLEAN UP THE ABOVE HARDCODED BRAIN PLACEMENT - if (m_EditorGUIMode != PICKINGOBJECT) -// TEMP REMOVE ABOVE - // Go back to previous mode - m_EditorGUIMode = m_PreviousMode; - m_ModeChanged = true; - } - - // Set the facing of AHumans based on right/left cursor movements -// TODO: Improve - Actor *pActor = dynamic_cast(m_pCurrentObject); - if (pActor && dynamic_cast(pActor) || dynamic_cast(pActor)) - pActor->SetHFlipped(m_FacingLeft); - - Deployment *pDeployment = dynamic_cast(m_pCurrentObject); + } + // Something else + else { + MovableObject* pObj = dynamic_cast(pPlacedClone); + if (pObj) + g_MovableMan.AddParticle(pObj); + m_EditMade = true; + } + } + // Something was wrong with placement + else { + delete pPlacedClone; + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + } + } + } + // TEMP REMOVE WEHN YOU CLEAN UP THE ABOVE HARDCODED BRAIN PLACEMENT + if (m_EditorGUIMode != PICKINGOBJECT) + // TEMP REMOVE ABOVE + // Go back to previous mode + m_EditorGUIMode = m_PreviousMode; + m_ModeChanged = true; + } + + // Set the facing of AHumans based on right/left cursor movements + // TODO: Improve + Actor* pActor = dynamic_cast(m_pCurrentObject); + if (pActor && dynamic_cast(pActor) || dynamic_cast(pActor)) + pActor->SetHFlipped(m_FacingLeft); + + Deployment* pDeployment = dynamic_cast(m_pCurrentObject); if (pDeployment) pDeployment->SetHFlipped(m_FacingLeft); - } - - ///////////////////////////////////////////////////////////// - // POINTING AT MODES - - else if ((m_EditorGUIMode == MOVINGOBJECT || m_EditorGUIMode == DELETINGOBJECT || m_EditorGUIMode == PLACEINFRONT || m_EditorGUIMode == PLACEBEHIND) && !m_PieMenu->IsEnabled()) - { - m_DrawCurrentObject = false; - - // Trap the mouse cursor - g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); - - // Move the cursor according to analog or mouse input - if (!analogInput.IsZero()) - m_CursorPos += analogInput * 4; - else if (!m_pController->GetMouseMovement().IsZero()) - m_CursorPos += m_pController->GetMouseMovement() / 2; - // Digital input? - else - { - if (pressUp) - m_CursorPos.m_Y -= 1; - if (pressRight) - m_CursorPos.m_X += 1; - if (pressDown) - m_CursorPos.m_Y += 1; - if (pressLeft) - m_CursorPos.m_X -= 1; - } - - ///////////////////////////////// - // MOVING OBJECT MODE - - if (m_EditorGUIMode == MOVINGOBJECT) - { - if (m_ModeChanged) - { - - m_ModeChanged = false; - } - g_FrameMan.SetScreenText("Click and drag on a placed object to MOVE it - Click quickly to DETACH", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - // Pick an object under the cursor and start moving it - if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) - { - const SceneObject *pPicked = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder); - if (pPicked) - { - // Grab the position and a copy of the the object itself before killing it from the scene - SetCurrentObject(dynamic_cast(pPicked->Clone())); - m_CursorOffset = m_CursorPos - m_pCurrentObject->GetPos(); - g_SceneMan.GetScene()->RemovePlacedObject(editedSet, m_ObjectListOrder); - m_EditMade = true; - - // Go to placing mode to move it around - m_EditorGUIMode = PLACINGOBJECT; - m_PreviousMode = MOVINGOBJECT; - m_ModeChanged = true; - m_BlinkTimer.Reset(); - g_GUISound.PlacementBlip()->Play(m_pController->GetPlayer()); - g_GUISound.PlacementGravel()->Play(m_pController->GetPlayer()); - } - else - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - } - - //////////////////////////// - // REMOVING OBJECT MODE - - else if (m_EditorGUIMode == DELETINGOBJECT) - { - if (m_ModeChanged) - { - - m_ModeChanged = false; - } - g_FrameMan.SetScreenText("Click and hold to select an object - release to DELETE it", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - // When primary is held down, pick object and show which one will be nuked if released - if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) - { - m_pObjectToBlink = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos); - } - else if (m_pController->IsState(RELEASE_PRIMARY)) - { - if (g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder)) - { - // Nuke it! - g_SceneMan.GetScene()->RemovePlacedObject(editedSet, m_ObjectListOrder); - m_EditMade = true; -// TODO: Add awesome destruction sound here - } - else - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - } - - ///////////////////////////////////// - // PLACE IN FRONT AND BEHIND OF MODES - - else if (m_EditorGUIMode == PLACEINFRONT || m_EditorGUIMode == PLACEBEHIND) - { - if (m_ModeChanged) - { - - m_ModeChanged = false; - } - if (m_EditorGUIMode == PLACEINFRONT) - g_FrameMan.SetScreenText(m_FeatureSet == ONLOADEDIT ? "Click an object to place the next one IN FRONT of it" : "Click an object to place the next one AFTER it in the build order", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - else if (m_EditorGUIMode == PLACEBEHIND) - g_FrameMan.SetScreenText(m_FeatureSet == ONLOADEDIT ? "Click an object to place the next one BEHIND it" : "Click an object to insert the next one BEFORE it in the build order", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - - // When primary is held down, pick object and show which one will be nuked if released - if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) - { - m_pObjectToBlink = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos); - } - else if (m_pController->IsState(RELEASE_PRIMARY)) - { - if (g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder)) - { - // Adjust the next std::list order to be in front if applicable (it's automatically behind if same order index) - if (m_EditorGUIMode == PLACEINFRONT) - m_ObjectListOrder++; - - // Go back to previous mode - m_EditorGUIMode = m_PreviousMode; - m_ModeChanged = true; - } - else - g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); - } - } - } - else if (m_EditorGUIMode == DONEEDITING) - { - // Check first that the brain is in a good spot if finishing up a base edit - if (m_FeatureSet != ONLOADEDIT) - TestBrainResidence(); -// if (m_FeatureSet != ONLOADEDIT) -// g_FrameMan.SetScreenText("DONE editing, wait for all other players to finish too...", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - } - - // Remove cursor offset if not applicable anymore - if (m_EditorGUIMode != PLACINGOBJECT) - m_CursorOffset.Reset(); - - // Keep the cursor position within the world - g_SceneMan.ForceBounds(m_CursorPos); -// TODO: make setscrolltarget with 'sloppy' target - // Scroll to the cursor's scene position - g_CameraMan.SetScrollTarget(m_CursorPos, 0.3, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); - // Apply the cursor position to the currently held object - if (m_pCurrentObject && m_DrawCurrentObject) - { - m_pCurrentObject->SetPos(g_SceneMan.SnapPosition(m_CursorPos - m_CursorOffset, m_GridSnapping)); - // If an actor, set it to be inactive so it doesn't scan around and reveal unseen areas - if (Actor *pCurrentActor = dynamic_cast(m_pCurrentObject)) - { - pCurrentActor->SetStatus(Actor::INACTIVE); - pCurrentActor->GetController()->SetDisabled(true); - } - m_pCurrentObject->FullUpdate(); - } - - // Animate the reveal index so it is clear which order blueprint things are placed/built - if (m_RevealTimer.IsPastRealTimeLimit()) - { - // Make the animation stop at full built and only reset after the pause - if (m_RevealTimer.GetRealTimeLimitMS() > (BLUEPRINTREVEALRATE * 2)) - m_RevealIndex = 0; - else - m_RevealIndex++; - - // Loop - if (m_RevealIndex >= g_SceneMan.GetScene()->GetPlacedObjects(m_FeatureSet == BLUEPRINTEDIT ? Scene::BLUEPRINT : Scene::AIPLAN)->size()) - // Set a long time when reset so the animation won't look so spazzy if there's only a few objects yet - m_RevealTimer.SetRealTimeLimitMS(BLUEPRINTREVEALPAUSE); - else - m_RevealTimer.SetRealTimeLimitMS(BLUEPRINTREVEALRATE); - - m_RevealTimer.Reset(); - } -} + } + ///////////////////////////////////////////////////////////// + // POINTING AT MODES + + else if ((m_EditorGUIMode == MOVINGOBJECT || m_EditorGUIMode == DELETINGOBJECT || m_EditorGUIMode == PLACEINFRONT || m_EditorGUIMode == PLACEBEHIND) && !m_PieMenu->IsEnabled()) { + m_DrawCurrentObject = false; + + // Trap the mouse cursor + g_UInputMan.TrapMousePos(true, m_pController->GetPlayer()); + + // Move the cursor according to analog or mouse input + if (!analogInput.IsZero()) + m_CursorPos += analogInput * 4; + else if (!m_pController->GetMouseMovement().IsZero()) + m_CursorPos += m_pController->GetMouseMovement() / 2; + // Digital input? + else { + if (pressUp) + m_CursorPos.m_Y -= 1; + if (pressRight) + m_CursorPos.m_X += 1; + if (pressDown) + m_CursorPos.m_Y += 1; + if (pressLeft) + m_CursorPos.m_X -= 1; + } + + ///////////////////////////////// + // MOVING OBJECT MODE + + if (m_EditorGUIMode == MOVINGOBJECT) { + if (m_ModeChanged) { + + m_ModeChanged = false; + } + g_FrameMan.SetScreenText("Click and drag on a placed object to MOVE it - Click quickly to DETACH", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + // Pick an object under the cursor and start moving it + if (m_pController->IsState(PRESS_PRIMARY) && !m_pPicker->IsVisible()) { + const SceneObject* pPicked = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder); + if (pPicked) { + // Grab the position and a copy of the the object itself before killing it from the scene + SetCurrentObject(dynamic_cast(pPicked->Clone())); + m_CursorOffset = m_CursorPos - m_pCurrentObject->GetPos(); + g_SceneMan.GetScene()->RemovePlacedObject(editedSet, m_ObjectListOrder); + m_EditMade = true; + + // Go to placing mode to move it around + m_EditorGUIMode = PLACINGOBJECT; + m_PreviousMode = MOVINGOBJECT; + m_ModeChanged = true; + m_BlinkTimer.Reset(); + g_GUISound.PlacementBlip()->Play(m_pController->GetPlayer()); + g_GUISound.PlacementGravel()->Play(m_pController->GetPlayer()); + } else + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + } + + //////////////////////////// + // REMOVING OBJECT MODE + + else if (m_EditorGUIMode == DELETINGOBJECT) { + if (m_ModeChanged) { + + m_ModeChanged = false; + } + g_FrameMan.SetScreenText("Click and hold to select an object - release to DELETE it", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + // When primary is held down, pick object and show which one will be nuked if released + if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) { + m_pObjectToBlink = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos); + } else if (m_pController->IsState(RELEASE_PRIMARY)) { + if (g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder)) { + // Nuke it! + g_SceneMan.GetScene()->RemovePlacedObject(editedSet, m_ObjectListOrder); + m_EditMade = true; + // TODO: Add awesome destruction sound here + } else + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + } + + ///////////////////////////////////// + // PLACE IN FRONT AND BEHIND OF MODES + + else if (m_EditorGUIMode == PLACEINFRONT || m_EditorGUIMode == PLACEBEHIND) { + if (m_ModeChanged) { + + m_ModeChanged = false; + } + if (m_EditorGUIMode == PLACEINFRONT) + g_FrameMan.SetScreenText(m_FeatureSet == ONLOADEDIT ? "Click an object to place the next one IN FRONT of it" : "Click an object to place the next one AFTER it in the build order", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + else if (m_EditorGUIMode == PLACEBEHIND) + g_FrameMan.SetScreenText(m_FeatureSet == ONLOADEDIT ? "Click an object to place the next one BEHIND it" : "Click an object to insert the next one BEFORE it in the build order", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + + // When primary is held down, pick object and show which one will be nuked if released + if (m_pController->IsState(PRIMARY_ACTION) && !m_pPicker->IsVisible()) { + m_pObjectToBlink = g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos); + } else if (m_pController->IsState(RELEASE_PRIMARY)) { + if (g_SceneMan.GetScene()->PickPlacedObject(editedSet, m_CursorPos, &m_ObjectListOrder)) { + // Adjust the next std::list order to be in front if applicable (it's automatically behind if same order index) + if (m_EditorGUIMode == PLACEINFRONT) + m_ObjectListOrder++; + + // Go back to previous mode + m_EditorGUIMode = m_PreviousMode; + m_ModeChanged = true; + } else + g_GUISound.UserErrorSound()->Play(m_pController->GetPlayer()); + } + } + } else if (m_EditorGUIMode == DONEEDITING) { + // Check first that the brain is in a good spot if finishing up a base edit + if (m_FeatureSet != ONLOADEDIT) + TestBrainResidence(); + // if (m_FeatureSet != ONLOADEDIT) + // g_FrameMan.SetScreenText("DONE editing, wait for all other players to finish too...", g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + } + + // Remove cursor offset if not applicable anymore + if (m_EditorGUIMode != PLACINGOBJECT) + m_CursorOffset.Reset(); + + // Keep the cursor position within the world + g_SceneMan.ForceBounds(m_CursorPos); + // TODO: make setscrolltarget with 'sloppy' target + // Scroll to the cursor's scene position + g_CameraMan.SetScrollTarget(m_CursorPos, 0.3, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); + // Apply the cursor position to the currently held object + if (m_pCurrentObject && m_DrawCurrentObject) { + m_pCurrentObject->SetPos(g_SceneMan.SnapPosition(m_CursorPos - m_CursorOffset, m_GridSnapping)); + // If an actor, set it to be inactive so it doesn't scan around and reveal unseen areas + if (Actor* pCurrentActor = dynamic_cast(m_pCurrentObject)) { + pCurrentActor->SetStatus(Actor::INACTIVE); + pCurrentActor->GetController()->SetDisabled(true); + } + m_pCurrentObject->FullUpdate(); + } + + // Animate the reveal index so it is clear which order blueprint things are placed/built + if (m_RevealTimer.IsPastRealTimeLimit()) { + // Make the animation stop at full built and only reset after the pause + if (m_RevealTimer.GetRealTimeLimitMS() > (BLUEPRINTREVEALRATE * 2)) + m_RevealIndex = 0; + else + m_RevealIndex++; + + // Loop + if (m_RevealIndex >= g_SceneMan.GetScene()->GetPlacedObjects(m_FeatureSet == BLUEPRINTEDIT ? Scene::BLUEPRINT : Scene::AIPLAN)->size()) + // Set a long time when reset so the animation won't look so spazzy if there's only a few objects yet + m_RevealTimer.SetRealTimeLimitMS(BLUEPRINTREVEALPAUSE); + else + m_RevealTimer.SetRealTimeLimitMS(BLUEPRINTREVEALRATE); + + m_RevealTimer.Reset(); + } +} ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws the menu -void SceneEditorGUI::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) const -{ - // Done, so don't draw the UI - if (m_EditorGUIMode == DONEEDITING) - return; - - // The get a std::list of the currently edited set of placed objects in the Scene - const std::list *pSceneObjectList = 0; - if (m_FeatureSet == ONLOADEDIT) - pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); - else if (m_FeatureSet == BLUEPRINTEDIT) - { - pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::BLUEPRINT); - // Draw the 'original' set of placed scene objects as solid before the blueprints - const std::list *pOriginalsList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); - for (std::list::const_iterator itr = pOriginalsList->begin(); itr != pOriginalsList->end(); ++itr) - { - (*itr)->Draw(pTargetBitmap, targetPos); - // Draw basic HUD if an actor - Actor *pActor = dynamic_cast(*itr); -// if (pActor) -// pActor->DrawHUD(pTargetBitmap, targetPos); - } - } - else if (m_FeatureSet == AIPLANEDIT) - { - pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::AIPLAN); - // Draw the 'original' set of placed scene objects as solid before the planned base - const std::list *pOriginalsList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); - for (std::list::const_iterator itr = pOriginalsList->begin(); itr != pOriginalsList->end(); ++itr) - { - (*itr)->Draw(pTargetBitmap, targetPos); - // Draw basic HUD if an actor - Actor *pActor = dynamic_cast(*itr); -// if (pActor) -// pActor->DrawHUD(pTargetBitmap, targetPos); - } - } - - // Draw the set of currently edited objects already placed in the Scene - if (pSceneObjectList) - { - // Draw all already placed Objects, and the currently held one in the order it is about to be placed in the scene - int i = 0; - Actor *pActor = 0; -// HeldDevice *pDevice = 0; - for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr, ++i) - { - // Draw the currently held object into the order of the std::list if it is to be placed inside - if (m_pCurrentObject && m_DrawCurrentObject && i == m_ObjectListOrder) - { - g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - m_pCurrentObject->Draw(pTargetBitmap, targetPos, g_DrawTrans); - pActor = dynamic_cast(m_pCurrentObject); - if (pActor) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - - // Is the placed object an actor? - pActor = dynamic_cast(*itr); -// pItem = dynamic_cast(*itr); - - // Blink trans if we are supposed to blink this one - if ((*itr) == m_pObjectToBlink) - { - g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - (*itr)->Draw(pTargetBitmap, targetPos, g_DrawTrans); - } - // Drawing of already placed objects that aren't highlighted or anything - else - { - // In Base edit mode, draw them alternatingly transparent and solid to show that they're just designs - if (m_FeatureSet == BLUEPRINTEDIT || m_FeatureSet == AIPLANEDIT) - { -// if (((int)m_BlinkTimer.GetElapsedRealTimeMS() % 2000) > 1750) -// if (m_BlinkTimer.AlternateReal(1000)) - // Animate the ghosted into appearing solid in the build order to make the order clear - if (i >= m_RevealIndex) - { - g_FrameMan.SetTransTableFromPreset(pActor ? TransparencyPreset::MoreTrans : TransparencyPreset::HalfTrans); - (*itr)->Draw(pTargetBitmap, targetPos, g_DrawTrans); - } - // Show as non-transparent half the time to still give benefits of WYSIWYG - else - (*itr)->Draw(pTargetBitmap, targetPos); - } - // In full scene edit mode, we want to give a WYSIWYG view - else - { - (*itr)->Draw(pTargetBitmap, targetPos); - - //Draw team marks for doors, deployments and assemblies - Deployment *pDeployment = dynamic_cast(*itr); +void SceneEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { + // Done, so don't draw the UI + if (m_EditorGUIMode == DONEEDITING) + return; + + // The get a std::list of the currently edited set of placed objects in the Scene + const std::list* pSceneObjectList = 0; + if (m_FeatureSet == ONLOADEDIT) + pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); + else if (m_FeatureSet == BLUEPRINTEDIT) { + pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::BLUEPRINT); + // Draw the 'original' set of placed scene objects as solid before the blueprints + const std::list* pOriginalsList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); + for (std::list::const_iterator itr = pOriginalsList->begin(); itr != pOriginalsList->end(); ++itr) { + (*itr)->Draw(pTargetBitmap, targetPos); + // Draw basic HUD if an actor + Actor* pActor = dynamic_cast(*itr); + // if (pActor) + // pActor->DrawHUD(pTargetBitmap, targetPos); + } + } else if (m_FeatureSet == AIPLANEDIT) { + pSceneObjectList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::AIPLAN); + // Draw the 'original' set of placed scene objects as solid before the planned base + const std::list* pOriginalsList = g_SceneMan.GetScene()->GetPlacedObjects(Scene::PLACEONLOAD); + for (std::list::const_iterator itr = pOriginalsList->begin(); itr != pOriginalsList->end(); ++itr) { + (*itr)->Draw(pTargetBitmap, targetPos); + // Draw basic HUD if an actor + Actor* pActor = dynamic_cast(*itr); + // if (pActor) + // pActor->DrawHUD(pTargetBitmap, targetPos); + } + } + + // Draw the set of currently edited objects already placed in the Scene + if (pSceneObjectList) { + // Draw all already placed Objects, and the currently held one in the order it is about to be placed in the scene + int i = 0; + Actor* pActor = 0; + // HeldDevice *pDevice = 0; + for (std::list::const_iterator itr = pSceneObjectList->begin(); itr != pSceneObjectList->end(); ++itr, ++i) { + // Draw the currently held object into the order of the std::list if it is to be placed inside + if (m_pCurrentObject && m_DrawCurrentObject && i == m_ObjectListOrder) { + g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + m_pCurrentObject->Draw(pTargetBitmap, targetPos, g_DrawTrans); + pActor = dynamic_cast(m_pCurrentObject); + if (pActor) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + + // Is the placed object an actor? + pActor = dynamic_cast(*itr); + // pItem = dynamic_cast(*itr); + + // Blink trans if we are supposed to blink this one + if ((*itr) == m_pObjectToBlink) { + g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + (*itr)->Draw(pTargetBitmap, targetPos, g_DrawTrans); + } + // Drawing of already placed objects that aren't highlighted or anything + else { + // In Base edit mode, draw them alternatingly transparent and solid to show that they're just designs + if (m_FeatureSet == BLUEPRINTEDIT || m_FeatureSet == AIPLANEDIT) { + // if (((int)m_BlinkTimer.GetElapsedRealTimeMS() % 2000) > 1750) + // if (m_BlinkTimer.AlternateReal(1000)) + // Animate the ghosted into appearing solid in the build order to make the order clear + if (i >= m_RevealIndex) { + g_FrameMan.SetTransTableFromPreset(pActor ? TransparencyPreset::MoreTrans : TransparencyPreset::HalfTrans); + (*itr)->Draw(pTargetBitmap, targetPos, g_DrawTrans); + } + // Show as non-transparent half the time to still give benefits of WYSIWYG + else + (*itr)->Draw(pTargetBitmap, targetPos); + } + // In full scene edit mode, we want to give a WYSIWYG view + else { + (*itr)->Draw(pTargetBitmap, targetPos); + + // Draw team marks for doors, deployments and assemblies + Deployment* pDeployment = dynamic_cast(*itr); if (pDeployment) (*itr)->DrawTeamMark(pTargetBitmap, targetPos); // Works for both doors and bunker assemblies - TerrainObject *pTObject = dynamic_cast(*itr); + TerrainObject* pTObject = dynamic_cast(*itr); if (pTObject && !pTObject->GetChildObjects().empty()) (*itr)->DrawTeamMark(pTargetBitmap, targetPos); - BunkerAssemblyScheme *pBA = dynamic_cast(*itr); + BunkerAssemblyScheme* pBA = dynamic_cast(*itr); if (pBA) (*itr)->DrawTeamMark(pTargetBitmap, targetPos); - } - } - - // Draw basic HUD if an actor - don't do this for blueprints.. it is confusing - if (pActor && m_FeatureSet != BLUEPRINTEDIT && m_FeatureSet != AIPLANEDIT) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - } - - // Draw the path between brain and sky, backwards so the dot spacing can be even and they don't crawl as the guy approaches - // Always draw the path so the player can see what he would be blocking off when building - if (m_RequireClearPathToOrbit && !m_BrainSkyPath.empty()) - { - int skipPhase = 0; - std::list::const_reverse_iterator lLast = m_BrainSkyPath.rbegin(); - std::list::const_reverse_iterator lItr = m_BrainSkyPath.rbegin(); - for (; lItr != m_BrainSkyPath.rend(); ++lItr) - { - // Draw these backwards so the skip phase works - skipPhase = g_FrameMan.DrawDotLine(pTargetBitmap, (*lLast) - targetPos, (*lItr) - targetPos, m_BrainSkyPathCost <= MAXBRAINPATHCOST ? s_pValidPathDot : s_pInvalidPathDot, 16, skipPhase, true); - lLast = lItr; - } - } - - // Draw the currently placed brain, if any, UNLESS it's being re-placed atm - if ((m_pCurrentObject && !m_pCurrentObject->IsInGroup("Brains")) && m_EditorGUIMode != INSTALLINGBRAIN && !(m_EditorGUIMode == PLACINGOBJECT && m_PreviousMode == INSTALLINGBRAIN)) - { - SceneObject *pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); - if (pBrain) - { - pBrain->Draw(pTargetBitmap, targetPos); - // Draw basic HUD if an actor - Actor *pActor = dynamic_cast(pBrain); - if (pActor) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - } - - // Draw picking object crosshairs and not the selected object - if (!m_DrawCurrentObject) - { - Vector center = m_CursorPos - targetPos; - putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); - hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); - vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); - } - // If the held object will be placed at the end of the std::list, draw it last to the scene, transperent blinking - else if (m_pCurrentObject && (m_ObjectListOrder < 0 || (pSceneObjectList && m_ObjectListOrder == pSceneObjectList->size()))) - { - g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); - m_pCurrentObject->Draw(pTargetBitmap, targetPos, g_DrawTrans); - Actor *pActor = dynamic_cast(m_pCurrentObject); - if (pActor && m_FeatureSet != BLUEPRINTEDIT && m_FeatureSet != AIPLANEDIT) - pActor->DrawHUD(pTargetBitmap, targetPos); - } - - m_pPicker->Draw(pTargetBitmap); - - // Draw the pie menu + } + + // Draw basic HUD if an actor - don't do this for blueprints.. it is confusing + if (pActor && m_FeatureSet != BLUEPRINTEDIT && m_FeatureSet != AIPLANEDIT) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + } + + // Draw the path between brain and sky, backwards so the dot spacing can be even and they don't crawl as the guy approaches + // Always draw the path so the player can see what he would be blocking off when building + if (m_RequireClearPathToOrbit && !m_BrainSkyPath.empty()) { + int skipPhase = 0; + std::list::const_reverse_iterator lLast = m_BrainSkyPath.rbegin(); + std::list::const_reverse_iterator lItr = m_BrainSkyPath.rbegin(); + for (; lItr != m_BrainSkyPath.rend(); ++lItr) { + // Draw these backwards so the skip phase works + skipPhase = g_FrameMan.DrawDotLine(pTargetBitmap, (*lLast) - targetPos, (*lItr) - targetPos, m_BrainSkyPathCost <= MAXBRAINPATHCOST ? s_pValidPathDot : s_pInvalidPathDot, 16, skipPhase, true); + lLast = lItr; + } + } + + // Draw the currently placed brain, if any, UNLESS it's being re-placed atm + if ((m_pCurrentObject && !m_pCurrentObject->IsInGroup("Brains")) && m_EditorGUIMode != INSTALLINGBRAIN && !(m_EditorGUIMode == PLACINGOBJECT && m_PreviousMode == INSTALLINGBRAIN)) { + SceneObject* pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); + if (pBrain) { + pBrain->Draw(pTargetBitmap, targetPos); + // Draw basic HUD if an actor + Actor* pActor = dynamic_cast(pBrain); + if (pActor) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + } + + // Draw picking object crosshairs and not the selected object + if (!m_DrawCurrentObject) { + Vector center = m_CursorPos - targetPos; + putpixel(pTargetBitmap, center.m_X, center.m_Y, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X - 5, center.m_Y, center.m_X - 2, g_YellowGlowColor); + hline(pTargetBitmap, center.m_X + 5, center.m_Y, center.m_X + 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y - 5, center.m_Y - 2, g_YellowGlowColor); + vline(pTargetBitmap, center.m_X, center.m_Y + 5, center.m_Y + 2, g_YellowGlowColor); + } + // If the held object will be placed at the end of the std::list, draw it last to the scene, transperent blinking + else if (m_pCurrentObject && (m_ObjectListOrder < 0 || (pSceneObjectList && m_ObjectListOrder == pSceneObjectList->size()))) { + g_FrameMan.SetTransTableFromPreset(m_BlinkTimer.AlternateReal(333) || m_EditorGUIMode == PLACINGOBJECT ? TransparencyPreset::LessTrans : TransparencyPreset::HalfTrans); + m_pCurrentObject->Draw(pTargetBitmap, targetPos, g_DrawTrans); + Actor* pActor = dynamic_cast(m_pCurrentObject); + if (pActor && m_FeatureSet != BLUEPRINTEDIT && m_FeatureSet != AIPLANEDIT) + pActor->DrawHUD(pTargetBitmap, targetPos); + } + + m_pPicker->Draw(pTargetBitmap); + + // Draw the pie menu m_PieMenu->Draw(pTargetBitmap, targetPos); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SceneEditorGUI::UpdateBrainSkyPathAndCost(Vector brainPos) { - if (!m_RequireClearPathToOrbit) { - m_PathRequest.reset(); - return; - } - - if (m_PathRequest) { - if (!m_PathRequest->complete) { - // Wait for the request to complete - return; - } - - if (g_SceneMan.GetScene()->PositionsAreTheSamePathNode(const_cast(m_PathRequest->targetPos), brainPos)) { - // No need to recalculate - return; - } - } - - Vector orbitPos; - - int staticPos; - int checkPos; - int lengthToCheck; - - // If the ceiling directly above is blocked, search the surroundings for gaps. - Directions orbitDirection = g_SceneMan.GetTerrain()->GetOrbitDirection(); - switch (orbitDirection) { - default: - case Directions::Up: - checkPos = brainPos.GetFloorIntX(); - staticPos = 0; - lengthToCheck = g_SceneMan.GetSceneWidth(); - break; - case Directions::Down: - checkPos = brainPos.GetFloorIntX(); - staticPos = g_SceneMan.GetSceneHeight(); - lengthToCheck = g_SceneMan.GetSceneWidth(); - break; - case Directions::Left: - checkPos = brainPos.GetFloorIntY(); - staticPos = 0; - lengthToCheck = g_SceneMan.GetSceneHeight(); - break; - case Directions::Right: - checkPos = brainPos.GetFloorIntY(); - staticPos = g_SceneMan.GetSceneWidth(); - lengthToCheck = g_SceneMan.GetSceneHeight(); - break; - } - - auto getVector = [&](int pos) { - switch (orbitDirection) { - default: - case Directions::Up: - return Vector(pos, 0); - case Directions::Down: - return Vector(pos, g_SceneMan.GetSceneHeight()); - case Directions::Left: - return Vector(0, pos); - case Directions::Right: - return Vector(g_SceneMan.GetSceneWidth(), pos); - } - }; - - int offset = 0; + if (!m_RequireClearPathToOrbit) { + m_PathRequest.reset(); + return; + } + + if (m_PathRequest) { + if (!m_PathRequest->complete) { + // Wait for the request to complete + return; + } + + if (g_SceneMan.GetScene()->PositionsAreTheSamePathNode(const_cast(m_PathRequest->targetPos), brainPos)) { + // No need to recalculate + return; + } + } + + Vector orbitPos; + + int staticPos; + int checkPos; + int lengthToCheck; + + // If the ceiling directly above is blocked, search the surroundings for gaps. + Directions orbitDirection = g_SceneMan.GetTerrain()->GetOrbitDirection(); + switch (orbitDirection) { + default: + case Directions::Up: + checkPos = brainPos.GetFloorIntX(); + staticPos = 0; + lengthToCheck = g_SceneMan.GetSceneWidth(); + break; + case Directions::Down: + checkPos = brainPos.GetFloorIntX(); + staticPos = g_SceneMan.GetSceneHeight(); + lengthToCheck = g_SceneMan.GetSceneWidth(); + break; + case Directions::Left: + checkPos = brainPos.GetFloorIntY(); + staticPos = 0; + lengthToCheck = g_SceneMan.GetSceneHeight(); + break; + case Directions::Right: + checkPos = brainPos.GetFloorIntY(); + staticPos = g_SceneMan.GetSceneWidth(); + lengthToCheck = g_SceneMan.GetSceneHeight(); + break; + } + + auto getVector = [&](int pos) { + switch (orbitDirection) { + default: + case Directions::Up: + return Vector(pos, 0); + case Directions::Down: + return Vector(pos, g_SceneMan.GetSceneHeight()); + case Directions::Left: + return Vector(0, pos); + case Directions::Right: + return Vector(g_SceneMan.GetSceneWidth(), pos); + } + }; + + int offset = 0; int spacing = 20; for (int i = 0; i < lengthToCheck / spacing; i++) { - int offsetCheckPos = checkPos + offset; - orbitPos = getVector(offsetCheckPos); - if (!g_SceneMan.IsWithinBounds(orbitPos.GetFloorIntX(), orbitPos.GetFloorIntY())) { - offset *= -1; - offsetCheckPos = checkPos + offset; - orbitPos = getVector(offsetCheckPos); - } + int offsetCheckPos = checkPos + offset; + orbitPos = getVector(offsetCheckPos); + if (!g_SceneMan.IsWithinBounds(orbitPos.GetFloorIntX(), orbitPos.GetFloorIntY())) { + offset *= -1; + offsetCheckPos = checkPos + offset; + orbitPos = getVector(offsetCheckPos); + } if (g_SceneMan.GetTerrMatter(orbitPos.GetFloorIntX(), orbitPos.GetFloorIntY()) == g_MaterialAir) { break; @@ -1655,11 +1497,11 @@ void SceneEditorGUI::UpdateBrainSkyPathAndCost(Vector brainPos) { } Activity::Teams team = static_cast(g_ActivityMan.GetActivity()->GetTeamOfPlayer(m_pController->GetPlayer())); - m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(orbitPos, brainPos, c_PathFindingDefaultDigStrength, team, - [&](std::shared_ptr pathRequest) { - m_BrainSkyPath = const_cast&>(pathRequest->path); - m_BrainSkyPathCost = pathRequest->totalCost; - }); + m_PathRequest = g_SceneMan.GetScene()->CalculatePathAsync(orbitPos, brainPos, c_PathFindingDefaultDigStrength, team, + [&](std::shared_ptr pathRequest) { + m_BrainSkyPath = const_cast&>(pathRequest->path); + m_BrainSkyPathCost = pathRequest->totalCost; + }); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1667,7 +1509,7 @@ void SceneEditorGUI::UpdateBrainSkyPathAndCost(Vector brainPos) { bool SceneEditorGUI::UpdateBrainPath() { if (m_pCurrentObject && m_pCurrentObject->IsInGroup("Brains")) { UpdateBrainSkyPathAndCost(m_CursorPos); - } else if (SceneObject *residentBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer())) { + } else if (SceneObject* residentBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer())) { UpdateBrainSkyPathAndCost(residentBrain->GetPos()); } else { m_BrainSkyPath.clear(); diff --git a/Source/Menus/SceneEditorGUI.h b/Source/Menus/SceneEditorGUI.h index fff12a00a7..8d2d998e7a 100644 --- a/Source/Menus/SceneEditorGUI.h +++ b/Source/Menus/SceneEditorGUI.h @@ -10,11 +10,10 @@ // dtabar@datarealms.com // http://www.datarealms.com - ////////////////////////////////////////////////////////////////////////////////////////// // Inclusions of header files -//#include "FrameMan.h" +// #include "FrameMan.h" #include "Timer.h" #include "Vector.h" #include "Controller.h" @@ -23,398 +22,364 @@ struct BITMAP; - -namespace RTE -{ - -class SceneObject; -class ObjectPickerGUI; -class PieMenu; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: SceneEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A full menu system that represents the scene editing GUI for Cortex Command -// Parent(s): None. -// Class history: 7/08/2007 SceneEditorGUI Created. - -class SceneEditorGUI { - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - enum FeatureSets - { - ONLOADEDIT = 0, - BLUEPRINTEDIT, - AIPLANEDIT, - INGAMEEDIT - }; - - // Different modes of this editor - enum EditorGUIMode - { - INACTIVE = 0, - PICKINGOBJECT, - ADDINGOBJECT, - INSTALLINGBRAIN, - PLACINGOBJECT, - MOVINGOBJECT, - DELETINGOBJECT, - PLACEINFRONT, - PLACEBEHIND, - DONEEDITING, - EDITORGUIMODECOUNT - }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: SceneEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a SceneEditorGUI object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - SceneEditorGUI() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~SceneEditorGUI -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a SceneEditorGUI object before deletion -// from system memory. -// Arguments: None. - - ~SceneEditorGUI() { Destroy(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneEditorGUI object ready for use. -// Arguments: A poitner to a Controller which will control this Menu. Ownership is -// NOT TRANSFERRED! -// Whether the editor should have all the features enabled, like load/save -// and undo capabilities. -// Which module space that this eidtor will be able to pick objects from. -// -1 means all modules. -// Which Tech module that will be presented as the native one to the player. -// The multiplier of all foreign techs' costs. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(Controller *pController, FeatureSets featureSet = INGAMEEDIT, int whichModuleSpace = -1, int nativeTechModule = 0, float foreignCostMult = 1.0); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire SceneEditorGUI, including its inherited members, to -// their default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneEditorGUI object. -// Arguments: None. -// Return value: None. - - void Destroy(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! -// Arguments: The new controller for this menu. Ownership is NOT transferred -// Return value: None. - - void SetController(Controller *pController); - - /// - /// Sets the FeatureSet for this SceneEditorGUI, and sets up the PieMenu accordingly. - /// - /// The new FeatureSet for this SceneEditorGUI. - void SetFeatureSet(SceneEditorGUI::FeatureSets newFeatureSet); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. -// Arguments: The new screen position of this entire GUI. - - void SetPosOnScreen(int newPosX, int newPosY); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCursorPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the absolute scene coordinates of the cursor of this Editor. -// Arguments: The new cursor position in absolute scene units. -// Return value: None. - - void SetCursorPos(const Vector &newCursorPos) { m_CursorPos = newCursorPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCurrentObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the new Object to be held at the cursor of this Editor. Ownership -// IS transferred! -// Arguments: The new Object to be held by the cursor. Ownership IS transferred! -// Return value: Whether the cursor holds a valid object after setting. - - bool SetCurrentObject(SceneObject *pNewObject); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActivatedPieSlice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets any Pie menu slice command activated last update. -// Arguments: None. -// Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. - - PieSlice::SliceType GetActivatedPieSlice() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetCurrentObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the currently held Object in the cursor of this Editor. Ownership -// IS NOT transferred! -// Arguments: None. -// Return value: The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! - - const SceneObject * GetCurrentObject() const { return m_pCurrentObject; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current mode of this editor. -// Arguments: The new mode to set to, see the EditorGUIMode enum. -// Return value: None. - - void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEditorMode -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the current mode of this editor. -// Arguments: None. -// Return value: The current mode this is set to; see the EditorGUIMode enum. - - EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule space to be picking objects from. If -1, then -// let the player pick from all loaded modules. -// Arguments: The ID of the module to let the player pick objects from. All official -// modules' objects will alwayws be presented, in addition to the one -// passed in here. -// Return value: None. - - void SetModuleSpace(int moduleSpaceID = -1); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetNativeTechModule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule ID should be treated as the native tech of the -// user of this menu. -// Arguments: The module ID to set as the native one. 0 means everything is native. -// Return value: None. - - void SetNativeTechModule(int whichModule); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetForeignCostMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the multiplier of the cost of any foreign Tech items. -// Arguments: The scalar multiplier of the costs of foreign Tech items. -// Return value: None. - - void SetForeignCostMultiplier(float newMultiplier); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EditMade -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Shows whether an edit on the scene was made in the last Update. -// Arguments: None. -// Return value: Whether any edit was made. - - bool EditMade() const { return m_EditMade; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TestBrainResidence -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether the resident brain is currently placed into a valid -// location in this scene, based on whether there is a clear path to the -// sky above it. This forces the editor into place brain mode with the -// current resident brain if the current placement is no bueno. It also -// removes the faulty brain from residence in the scene! -// Arguments: Whether it's OK if we dont' have a brain right now - ie don't force -// into isntallation mode if no brain was found. -// Return value: Whether a resident brain was found, AND found in a valid location! - - bool TestBrainResidence(bool noBrainIsOK = false); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame -// Arguments: None. -// Return value: None. - - void Update(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the editor -// Arguments: The bitmap to draw on. -// The absolute position of the target bitmap's upper left corner in the scene. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector()) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - /// - /// Updates the path to the current brain in the cursor or resident in the scene, if any. If there's none, the path is cleared. - /// - /// Whether a brain was found in the cursor or the scene. - bool UpdateBrainPath(); - - /// - /// Updates the path from the designated position to orbit, and its cost. - /// - /// The designated position of the brain. - void UpdateBrainSkyPathAndCost(Vector brainPos); - - - enum BlinkMode - { - NOBLINK = 0, - OBJECTBLINKON, - OBJECTBLINKOFF, - BLINKMODECOUNT - }; - - // Controller which conrols this menu. Not owned - Controller *m_pController; - // Full featured or the in-game version, or the base building mode - int m_FeatureSet; - // Whether an edit was made to the Scene in the last Update - bool m_EditMade; - // The current mode of the whole GUI. See EditorGUIMode enum. - EditorGUIMode m_EditorGUIMode; - // The previous mode of the whole GUI, to go back to when the current mode is done in some cases - EditorGUIMode m_PreviousMode; - // Whether the editor mode has changed - bool m_ModeChanged; - // Notification blink timer - Timer m_BlinkTimer; - // What we're blinking - int m_BlinkMode; - // Measures the time to when to start repeating inputs when they're held down - Timer m_RepeatStartTimer; - // Measures the interval between input repeats - Timer m_RepeatTimer; - // Measures the interval between graphically revealing objects - Timer m_RevealTimer; - // The index which keeps track of the point in the build queue that blueprint objects go from being ghosted to revealed - int m_RevealIndex; - // Whether we need a clear path to orbit to place brain - bool m_RequireClearPathToOrbit; - - std::unique_ptr m_PieMenu; //!< The PieMenu for this SceneEditorGUI. - // The object picker - ObjectPickerGUI *m_pPicker; - // The ID of the DataModule that contains the native Tech of the Player using this menu - int m_NativeTechModule; - // The multiplier of costs of any foreign tech items - float m_ForeignCostMult; - // Grid snapping enabled - bool m_GridSnapping; - // Current cursor position, in absolute scene coordinates - Vector m_CursorPos; - // The offset from the current object's position to the cursor, if any - Vector m_CursorOffset; - // Cursor position in free air, or over something - bool m_CursorInAir; - // SceneObject facing left or not when placing - bool m_FacingLeft; - // The team of the placed SceneObject:s - int m_PlaceTeam; - // Currently held object. This is what is attached to the cursor and will be placed when the fire button is pressed - // OWNED by this. - SceneObject *m_pCurrentObject; - // Where in the scene's list order the next object should be placed. If -1, then place at the end of the list. - int m_ObjectListOrder; - // Whether to draw the currently held object - bool m_DrawCurrentObject; - // Currently placed scene object to make blink when drawing it. NOT OWNED. - const SceneObject *m_pObjectToBlink; - // Path found between brain pos and the sky to make sure fair brain placement - std::list m_BrainSkyPath; - // The cost of the path from the current position of the brain to the sky - float m_BrainSkyPathCost; - // Valid brain path line dots - static BITMAP *s_pValidPathDot; - // Invalid brain path line dots - static BITMAP *s_pInvalidPathDot; - // The current pathfinding request - std::shared_ptr m_PathRequest; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SceneEditorGUI, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - SceneEditorGUI(const SceneEditorGUI &reference) = delete; - SceneEditorGUI & operator=(const SceneEditorGUI &rhs) = delete; - -}; +namespace RTE { + + class SceneObject; + class ObjectPickerGUI; + class PieMenu; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Class: SceneEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: A full menu system that represents the scene editing GUI for Cortex Command + // Parent(s): None. + // Class history: 7/08/2007 SceneEditorGUI Created. + + class SceneEditorGUI { + + ////////////////////////////////////////////////////////////////////////////////////////// + // Public member variable, method and friend function declarations + + public: + enum FeatureSets { + ONLOADEDIT = 0, + BLUEPRINTEDIT, + AIPLANEDIT, + INGAMEEDIT + }; + + // Different modes of this editor + enum EditorGUIMode { + INACTIVE = 0, + PICKINGOBJECT, + ADDINGOBJECT, + INSTALLINGBRAIN, + PLACINGOBJECT, + MOVINGOBJECT, + DELETINGOBJECT, + PLACEINFRONT, + PLACEBEHIND, + DONEEDITING, + EDITORGUIMODECOUNT + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Constructor: SceneEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Constructor method used to instantiate a SceneEditorGUI object in system + // memory. Create() should be called before using the object. + // Arguments: None. + + SceneEditorGUI() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Destructor: ~SceneEditorGUI + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destructor method used to clean up a SceneEditorGUI object before deletion + // from system memory. + // Arguments: None. + + ~SceneEditorGUI() { Destroy(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Create + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Makes the SceneEditorGUI object ready for use. + // Arguments: A poitner to a Controller which will control this Menu. Ownership is + // NOT TRANSFERRED! + // Whether the editor should have all the features enabled, like load/save + // and undo capabilities. + // Which module space that this eidtor will be able to pick objects from. + // -1 means all modules. + // Which Tech module that will be presented as the native one to the player. + // The multiplier of all foreign techs' costs. + // Return value: An error return value signaling sucess or any particular failure. + // Anything below 0 is an error signal. + + int Create(Controller* pController, FeatureSets featureSet = INGAMEEDIT, int whichModuleSpace = -1, int nativeTechModule = 0, float foreignCostMult = 1.0); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Reset + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Resets the entire SceneEditorGUI, including its inherited members, to + // their default settings or values. + // Arguments: None. + // Return value: None. + + void Reset() { Clear(); } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Destroy + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Destroys and resets (through Clear()) the SceneEditorGUI object. + // Arguments: None. + // Return value: None. + + void Destroy(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetController + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the controller used by this. The ownership of the controller is + // NOT transferred! + // Arguments: The new controller for this menu. Ownership is NOT transferred + // Return value: None. + + void SetController(Controller* pController); + + /// + /// Sets the FeatureSet for this SceneEditorGUI, and sets up the PieMenu accordingly. + /// + /// The new FeatureSet for this SceneEditorGUI. + void SetFeatureSet(SceneEditorGUI::FeatureSets newFeatureSet); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetPosOnScreen + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets where on the screen that this GUI is being drawn to. If upper + // left corner, then 0, 0. This will affect the way the mouse is positioned + // etc. + // Arguments: The new screen position of this entire GUI. + + void SetPosOnScreen(int newPosX, int newPosY); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCursorPos + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the absolute scene coordinates of the cursor of this Editor. + // Arguments: The new cursor position in absolute scene units. + // Return value: None. + + void SetCursorPos(const Vector& newCursorPos) { m_CursorPos = newCursorPos; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetCurrentObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the new Object to be held at the cursor of this Editor. Ownership + // IS transferred! + // Arguments: The new Object to be held by the cursor. Ownership IS transferred! + // Return value: Whether the cursor holds a valid object after setting. + + bool SetCurrentObject(SceneObject* pNewObject); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetActivatedPieSlice + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets any Pie menu slice command activated last update. + // Arguments: None. + // Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. + + PieSlice::SliceType GetActivatedPieSlice() const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetCurrentObject + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the currently held Object in the cursor of this Editor. Ownership + // IS NOT transferred! + // Arguments: None. + // Return value: The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! + + const SceneObject* GetCurrentObject() const { return m_pCurrentObject; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the current mode of this editor. + // Arguments: The new mode to set to, see the EditorGUIMode enum. + // Return value: None. + + void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: GetEditorMode + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Gets the current mode of this editor. + // Arguments: None. + // Return value: The current mode this is set to; see the EditorGUIMode enum. + + EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetModuleSpace + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which DataModule space to be picking objects from. If -1, then + // let the player pick from all loaded modules. + // Arguments: The ID of the module to let the player pick objects from. All official + // modules' objects will alwayws be presented, in addition to the one + // passed in here. + // Return value: None. + + void SetModuleSpace(int moduleSpaceID = -1); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetNativeTechModule + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets which DataModule ID should be treated as the native tech of the + // user of this menu. + // Arguments: The module ID to set as the native one. 0 means everything is native. + // Return value: None. + + void SetNativeTechModule(int whichModule); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: SetForeignCostMultiplier + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Sets the multiplier of the cost of any foreign Tech items. + // Arguments: The scalar multiplier of the costs of foreign Tech items. + // Return value: None. + + void SetForeignCostMultiplier(float newMultiplier); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: EditMade + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Shows whether an edit on the scene was made in the last Update. + // Arguments: None. + // Return value: Whether any edit was made. + + bool EditMade() const { return m_EditMade; } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: TestBrainResidence + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Checks whether the resident brain is currently placed into a valid + // location in this scene, based on whether there is a clear path to the + // sky above it. This forces the editor into place brain mode with the + // current resident brain if the current placement is no bueno. It also + // removes the faulty brain from residence in the scene! + // Arguments: Whether it's OK if we dont' have a brain right now - ie don't force + // into isntallation mode if no brain was found. + // Return value: Whether a resident brain was found, AND found in a valid location! + + bool TestBrainResidence(bool noBrainIsOK = false); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Update + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Updates the state of this Menu each frame + // Arguments: None. + // Return value: None. + + void Update(); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Draw + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Draws the editor + // Arguments: The bitmap to draw on. + // The absolute position of the target bitmap's upper left corner in the scene. + // Return value: None. + + void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Protected member variable and method declarations + + protected: + /// + /// Updates the path to the current brain in the cursor or resident in the scene, if any. If there's none, the path is cleared. + /// + /// Whether a brain was found in the cursor or the scene. + bool UpdateBrainPath(); + + /// + /// Updates the path from the designated position to orbit, and its cost. + /// + /// The designated position of the brain. + void UpdateBrainSkyPathAndCost(Vector brainPos); + + enum BlinkMode { + NOBLINK = 0, + OBJECTBLINKON, + OBJECTBLINKOFF, + BLINKMODECOUNT + }; + + // Controller which conrols this menu. Not owned + Controller* m_pController; + // Full featured or the in-game version, or the base building mode + int m_FeatureSet; + // Whether an edit was made to the Scene in the last Update + bool m_EditMade; + // The current mode of the whole GUI. See EditorGUIMode enum. + EditorGUIMode m_EditorGUIMode; + // The previous mode of the whole GUI, to go back to when the current mode is done in some cases + EditorGUIMode m_PreviousMode; + // Whether the editor mode has changed + bool m_ModeChanged; + // Notification blink timer + Timer m_BlinkTimer; + // What we're blinking + int m_BlinkMode; + // Measures the time to when to start repeating inputs when they're held down + Timer m_RepeatStartTimer; + // Measures the interval between input repeats + Timer m_RepeatTimer; + // Measures the interval between graphically revealing objects + Timer m_RevealTimer; + // The index which keeps track of the point in the build queue that blueprint objects go from being ghosted to revealed + int m_RevealIndex; + // Whether we need a clear path to orbit to place brain + bool m_RequireClearPathToOrbit; + + std::unique_ptr m_PieMenu; //!< The PieMenu for this SceneEditorGUI. + // The object picker + ObjectPickerGUI* m_pPicker; + // The ID of the DataModule that contains the native Tech of the Player using this menu + int m_NativeTechModule; + // The multiplier of costs of any foreign tech items + float m_ForeignCostMult; + // Grid snapping enabled + bool m_GridSnapping; + // Current cursor position, in absolute scene coordinates + Vector m_CursorPos; + // The offset from the current object's position to the cursor, if any + Vector m_CursorOffset; + // Cursor position in free air, or over something + bool m_CursorInAir; + // SceneObject facing left or not when placing + bool m_FacingLeft; + // The team of the placed SceneObject:s + int m_PlaceTeam; + // Currently held object. This is what is attached to the cursor and will be placed when the fire button is pressed + // OWNED by this. + SceneObject* m_pCurrentObject; + // Where in the scene's list order the next object should be placed. If -1, then place at the end of the list. + int m_ObjectListOrder; + // Whether to draw the currently held object + bool m_DrawCurrentObject; + // Currently placed scene object to make blink when drawing it. NOT OWNED. + const SceneObject* m_pObjectToBlink; + // Path found between brain pos and the sky to make sure fair brain placement + std::list m_BrainSkyPath; + // The cost of the path from the current position of the brain to the sky + float m_BrainSkyPathCost; + // Valid brain path line dots + static BITMAP* s_pValidPathDot; + // Invalid brain path line dots + static BITMAP* s_pInvalidPathDot; + // The current pathfinding request + std::shared_ptr m_PathRequest; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Private member variable and method declarations + + private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Method: Clear + ////////////////////////////////////////////////////////////////////////////////////////// + // Description: Clears all the member variables of this SceneEditorGUI, effectively + // resetting the members of this abstraction level only. + // Arguments: None. + // Return value: None. + + void Clear(); + + // Disallow the use of some implicit methods. + SceneEditorGUI(const SceneEditorGUI& reference) = delete; + SceneEditorGUI& operator=(const SceneEditorGUI& rhs) = delete; + }; } // namespace RTE -#endif // File \ No newline at end of file +#endif // File \ No newline at end of file diff --git a/Source/Menus/SettingsAudioGUI.cpp b/Source/Menus/SettingsAudioGUI.cpp index 176dd4dd89..7d0fc940b9 100644 --- a/Source/Menus/SettingsAudioGUI.cpp +++ b/Source/Menus/SettingsAudioGUI.cpp @@ -9,24 +9,25 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsAudioGUI::SettingsAudioGUI(GUIControlManager *parentControlManager) : m_GUIControlManager(parentControlManager) { - m_AudioSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxAudioSettings")); + SettingsAudioGUI::SettingsAudioGUI(GUIControlManager* parentControlManager) : + m_GUIControlManager(parentControlManager) { + m_AudioSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxAudioSettings")); - m_MasterVolumeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelMasterVolume")); - m_MasterVolumeSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderMasterVolume")); - m_MasterMuteCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxMuteMaster")); + m_MasterVolumeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelMasterVolume")); + m_MasterVolumeSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderMasterVolume")); + m_MasterMuteCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxMuteMaster")); m_MasterMuteCheckbox->SetCheck(g_AudioMan.GetMasterMuted()); - m_MusicVolumeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelMusicVolume")); - m_MusicVolumeSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderMusicVolume")); - m_MusicMuteCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxMuteMusic")); + m_MusicVolumeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelMusicVolume")); + m_MusicVolumeSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderMusicVolume")); + m_MusicMuteCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxMuteMusic")); m_MusicMuteCheckbox->SetCheck(g_AudioMan.GetMusicMuted()); - m_SoundVolumeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSoundVolume")); - m_SoundVolumeSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderSoundVolume")); - m_SoundMuteCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxMuteSound")); + m_SoundVolumeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSoundVolume")); + m_SoundVolumeSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderSoundVolume")); + m_SoundMuteCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxMuteSound")); m_SoundMuteCheckbox->SetCheck(g_AudioMan.GetSoundsMuted()); UpdateMasterVolumeControls(); @@ -34,14 +35,14 @@ namespace RTE { UpdateSoundVolumeControls(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsAudioGUI::SetEnabled(bool enable) const { m_AudioSettingsBox->SetVisible(enable); m_AudioSettingsBox->SetEnabled(enable); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsAudioGUI::UpdateMasterVolumeControls() { int masterVolume = static_cast(std::round(g_AudioMan.GetMasterVolume() * 100)); @@ -49,7 +50,7 @@ namespace RTE { m_MasterVolumeSlider->SetValue(masterVolume); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsAudioGUI::UpdateMusicVolumeControls() { int musicVolume = static_cast(std::round(g_AudioMan.GetMusicVolume() * 100)); @@ -57,7 +58,7 @@ namespace RTE { m_MusicVolumeSlider->SetValue(musicVolume); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsAudioGUI::UpdateSoundVolumeControls() { int soundVolume = static_cast(std::round(g_AudioMan.GetSoundsVolume() * 100)); @@ -65,13 +66,15 @@ namespace RTE { m_SoundVolumeSlider->SetValue(soundVolume); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsAudioGUI::HandleInputEvents(GUIEvent &guiEvent) { + void SettingsAudioGUI::HandleInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Notification) { if (guiEvent.GetControl() == m_MasterVolumeSlider) { float newMasterVolume = static_cast(m_MasterVolumeSlider->GetValue()) / 100.0F; - if (newMasterVolume != g_AudioMan.GetSoundsVolume() && !g_GUISound.TestSound()->IsBeingPlayed()) { g_GUISound.TestSound()->Play(); } + if (newMasterVolume != g_AudioMan.GetSoundsVolume() && !g_GUISound.TestSound()->IsBeingPlayed()) { + g_GUISound.TestSound()->Play(); + } g_AudioMan.SetMasterVolume(newMasterVolume); UpdateMasterVolumeControls(); } else if (guiEvent.GetControl() == m_MusicVolumeSlider) { @@ -79,7 +82,9 @@ namespace RTE { UpdateMusicVolumeControls(); } else if (guiEvent.GetControl() == m_SoundVolumeSlider) { float newSoundVolume = static_cast(m_SoundVolumeSlider->GetValue()) / 100.0F; - if (newSoundVolume != g_AudioMan.GetSoundsVolume() && !g_GUISound.TestSound()->IsBeingPlayed()) { g_GUISound.TestSound()->Play(); } + if (newSoundVolume != g_AudioMan.GetSoundsVolume() && !g_GUISound.TestSound()->IsBeingPlayed()) { + g_GUISound.TestSound()->Play(); + } g_AudioMan.SetSoundsVolume(newSoundVolume); UpdateSoundVolumeControls(); } else if (guiEvent.GetControl() == m_MasterMuteCheckbox) { @@ -91,4 +96,4 @@ namespace RTE { } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/SettingsAudioGUI.h b/Source/Menus/SettingsAudioGUI.h index 0be6275db9..ae3edf1614 100644 --- a/Source/Menus/SettingsAudioGUI.h +++ b/Source/Menus/SettingsAudioGUI.h @@ -16,13 +16,12 @@ namespace RTE { class SettingsAudioGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a SettingsAudioGUI object in system memory and make it ready for use. /// /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsAudioGUI. Ownership is NOT transferred! - explicit SettingsAudioGUI(GUIControlManager *parentControlManager); + explicit SettingsAudioGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Concrete Methods @@ -36,26 +35,25 @@ namespace RTE { /// Handles the player interaction with the AudioVideoGUI GUI elements. /// /// The GUIEvent containing information about the player interaction with an element. - void HandleInputEvents(GUIEvent &guiEvent); + void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: - - GUIControlManager *m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. + GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. /// /// GUI elements that compose the audio settings menu screen. /// - GUICollectionBox *m_AudioSettingsBox; - GUILabel *m_MasterVolumeLabel; - GUISlider *m_MasterVolumeSlider; - GUICheckbox *m_MasterMuteCheckbox; - GUILabel *m_MusicVolumeLabel; - GUISlider *m_MusicVolumeSlider; - GUICheckbox *m_MusicMuteCheckbox; - GUILabel *m_SoundVolumeLabel; - GUISlider *m_SoundVolumeSlider; - GUICheckbox *m_SoundMuteCheckbox; + GUICollectionBox* m_AudioSettingsBox; + GUILabel* m_MasterVolumeLabel; + GUISlider* m_MasterVolumeSlider; + GUICheckbox* m_MasterMuteCheckbox; + GUILabel* m_MusicVolumeLabel; + GUISlider* m_MusicVolumeSlider; + GUICheckbox* m_MusicMuteCheckbox; + GUILabel* m_SoundVolumeLabel; + GUISlider* m_SoundVolumeSlider; + GUICheckbox* m_SoundMuteCheckbox; #pragma region Audio Settings Handling /// @@ -75,8 +73,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - SettingsAudioGUI(const SettingsAudioGUI &reference) = delete; - SettingsAudioGUI & operator=(const SettingsAudioGUI &rhs) = delete; + SettingsAudioGUI(const SettingsAudioGUI& reference) = delete; + SettingsAudioGUI& operator=(const SettingsAudioGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/SettingsGUI.cpp b/Source/Menus/SettingsGUI.cpp index 5cc5040e61..2d2b6cb5b1 100644 --- a/Source/Menus/SettingsGUI.cpp +++ b/Source/Menus/SettingsGUI.cpp @@ -10,30 +10,32 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsGUI::SettingsGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool createForPauseMenu) { + SettingsGUI::SettingsGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); m_GUIControlManager->Load(createForPauseMenu ? "Base.rte/GUIs/SettingsPauseGUI.ini" : "Base.rte/GUIs/SettingsGUI.ini"); int rootBoxMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); - GUICollectionBox *rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); + GUICollectionBox* rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); rootBox->Resize(rootBoxMaxWidth, g_WindowMan.GetResY()); - m_SettingsTabberBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSettingsBase")); + m_SettingsTabberBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSettingsBase")); m_SettingsTabberBox->SetPositionAbs((rootBox->GetWidth() - m_SettingsTabberBox->GetWidth()) / 2, 140); - if (rootBox->GetHeight() < 540) { m_SettingsTabberBox->CenterInParent(true, true); } + if (rootBox->GetHeight() < 540) { + m_SettingsTabberBox->CenterInParent(true, true); + } - m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMainMenu")); + m_BackToMainButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonBackToMainMenu")); m_BackToMainButton->SetPositionAbs((rootBox->GetWidth() - m_BackToMainButton->GetWidth()) / 2, m_SettingsTabberBox->GetYPos() + m_SettingsTabberBox->GetHeight() + 10); - m_SettingsMenuTabs[SettingsMenuScreen::VideoSettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabVideoSettings")); - m_SettingsMenuTabs[SettingsMenuScreen::AudioSettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabAudioSettings")); - m_SettingsMenuTabs[SettingsMenuScreen::InputSettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabInputSettings")); - m_SettingsMenuTabs[SettingsMenuScreen::GameplaySettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabGameplaySettings")); - m_SettingsMenuTabs[SettingsMenuScreen::MiscSettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabMiscSettings")); + m_SettingsMenuTabs[SettingsMenuScreen::VideoSettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabVideoSettings")); + m_SettingsMenuTabs[SettingsMenuScreen::AudioSettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabAudioSettings")); + m_SettingsMenuTabs[SettingsMenuScreen::InputSettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabInputSettings")); + m_SettingsMenuTabs[SettingsMenuScreen::GameplaySettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabGameplaySettings")); + m_SettingsMenuTabs[SettingsMenuScreen::MiscSettingsMenu] = dynamic_cast(m_GUIControlManager->GetControl("TabMiscSettings")); m_VideoSettingsMenu = std::make_unique(m_GUIControlManager.get()); m_AudioSettingsMenu = std::make_unique(m_GUIControlManager.get()); @@ -52,10 +54,10 @@ namespace RTE { m_SettingsMenuTabs[m_ActiveSettingsMenuScreen]->SetCheck(true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICollectionBox * SettingsGUI::GetActiveDialogBox() const { - GUICollectionBox *activeDialogBox = nullptr; + GUICollectionBox* SettingsGUI::GetActiveDialogBox() const { + GUICollectionBox* activeDialogBox = nullptr; switch (m_ActiveSettingsMenuScreen) { case SettingsMenuScreen::VideoSettingsMenu: activeDialogBox = m_VideoSettingsMenu->GetActiveDialogBox(); @@ -71,7 +73,7 @@ namespace RTE { return activeDialogBox; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGUI::CloseActiveDialogBox() const { switch (m_ActiveSettingsMenuScreen) { @@ -88,16 +90,16 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGUI::DisableSettingsMenuNavigation(bool disable) const { m_BackToMainButton->SetEnabled(!disable); - for (GUITab *settingsTabberTab : m_SettingsMenuTabs) { + for (GUITab* settingsTabberTab: m_SettingsMenuTabs) { settingsTabberTab->SetEnabled(!disable); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGUI::SetActiveSettingsMenuScreen(SettingsMenuScreen activeMenu, bool playButtonPressSound) { m_VideoSettingsMenu->SetEnabled(false); @@ -130,10 +132,12 @@ namespace RTE { // Remove focus so the tab hovered graphic is removed after being pressed, otherwise it remains stuck on the active tab. m_GUIControlManager->GetManager()->SetFocus(nullptr); - if (playButtonPressSound) { g_GUISound.BackButtonPressSound()->Play(); } + if (playButtonPressSound) { + g_GUISound.BackButtonPressSound()->Play(); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsGUI::HandleInputEvents() { m_GUIControlManager->Update(); @@ -146,7 +150,9 @@ namespace RTE { return true; } } else if (guiEvent.GetType() == GUIEvent::Notification) { - if ((guiEvent.GetMsg() == GUIButton::Focused) && dynamic_cast(guiEvent.GetControl()) || (guiEvent.GetMsg() == GUITab::Hovered && dynamic_cast(guiEvent.GetControl()))) { g_GUISound.SelectionChangeSound()->Play(); } + if ((guiEvent.GetMsg() == GUIButton::Focused) && dynamic_cast(guiEvent.GetControl()) || (guiEvent.GetMsg() == GUITab::Hovered && dynamic_cast(guiEvent.GetControl()))) { + g_GUISound.SelectionChangeSound()->Play(); + } if (guiEvent.GetMsg() == GUITab::UnPushed) { if (guiEvent.GetControl() == m_SettingsMenuTabs[SettingsMenuScreen::VideoSettingsMenu]) { @@ -194,10 +200,10 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGUI::Draw() const { m_GUIControlManager->Draw(); m_GUIControlManager->DrawMouse(); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/SettingsGUI.h b/Source/Menus/SettingsGUI.h index 5c5d1e04ea..b025955716 100644 --- a/Source/Menus/SettingsGUI.h +++ b/Source/Menus/SettingsGUI.h @@ -21,7 +21,6 @@ namespace RTE { class SettingsGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a SettingsGUI object in system memory and make it ready for use. @@ -29,7 +28,7 @@ namespace RTE { /// Pointer to a GUIScreen interface that will be used by this SettingsGUI's GUIControlManager. Ownership is NOT transferred! /// Pointer to a GUIInput interface that will be used by this SettingsGUI's GUIControlManager. Ownership is NOT transferred! /// Whether this SettingsGUI is part of PauseMenuGUI and should have a slightly different layout. - SettingsGUI(AllegroScreen *guiScreen, GUIInputWrapper *guiInput, bool createForPauseMenu = false); + SettingsGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu = false); #pragma endregion #pragma region Getters @@ -37,7 +36,7 @@ namespace RTE { /// Gets the currently active GUICollectionBox of this SettingsGUI or any of its sub-menus that acts as a dialog box and requires disabling navigation and drawing an overlay. /// /// Pointer to the GUICollectionBox that is the currently active dialog box. Ownership is NOT transferred! - GUICollectionBox * GetActiveDialogBox() const; + GUICollectionBox* GetActiveDialogBox() const; #pragma endregion #pragma region Concrete Methods @@ -64,7 +63,6 @@ namespace RTE { #pragma endregion private: - /// /// Enumeration for the different sub-menu screens of the settings menu. /// @@ -90,9 +88,9 @@ namespace RTE { /// /// GUI elements that compose the settings menu screen. /// - GUICollectionBox *m_SettingsTabberBox; - GUIButton *m_BackToMainButton; - std::array m_SettingsMenuTabs; + GUICollectionBox* m_SettingsTabberBox; + GUIButton* m_BackToMainButton; + std::array m_SettingsMenuTabs; #pragma region Settings Menu Handling /// @@ -109,8 +107,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - SettingsGUI(const SettingsGUI &reference) = delete; - SettingsGUI & operator=(const SettingsGUI &rhs) = delete; + SettingsGUI(const SettingsGUI& reference) = delete; + SettingsGUI& operator=(const SettingsGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/SettingsGameplayGUI.cpp b/Source/Menus/SettingsGameplayGUI.cpp index e11a0ee94e..0a7fccfffb 100644 --- a/Source/Menus/SettingsGameplayGUI.cpp +++ b/Source/Menus/SettingsGameplayGUI.cpp @@ -12,43 +12,44 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsGameplayGUI::SettingsGameplayGUI(GUIControlManager *parentControlManager) : m_GUIControlManager(parentControlManager) { - m_GameplaySettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxGameplaySettings")); + SettingsGameplayGUI::SettingsGameplayGUI(GUIControlManager* parentControlManager) : + m_GUIControlManager(parentControlManager) { + m_GameplaySettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxGameplaySettings")); - m_FlashOnBrainDamageCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxFlashOnBrainDamage")); + m_FlashOnBrainDamageCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxFlashOnBrainDamage")); m_FlashOnBrainDamageCheckbox->SetCheck(g_SettingsMan.FlashOnBrainDamage()); - m_BlipOnRevealUnseenCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxBlipOnRevealUnseen")); + m_BlipOnRevealUnseenCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxBlipOnRevealUnseen")); m_BlipOnRevealUnseenCheckbox->SetCheck(g_SettingsMan.BlipOnRevealUnseen()); - m_ShowForeignItemsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxShowForeignItems")); + m_ShowForeignItemsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxShowForeignItems")); m_ShowForeignItemsCheckbox->SetCheck(g_SettingsMan.ShowForeignItems()); - m_EnableCrabBombsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxCrabBombs")); + m_EnableCrabBombsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxCrabBombs")); m_EnableCrabBombsCheckbox->SetCheck(g_SettingsMan.CrabBombsEnabled()); - m_EndlessMetaGameCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxEndlessMetaGame")); + m_EndlessMetaGameCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxEndlessMetaGame")); m_EndlessMetaGameCheckbox->SetCheck(g_SettingsMan.EndlessMetaGameMode()); - m_ShowEnemyHUDCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxEnemyHUD")); + m_ShowEnemyHUDCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxEnemyHUD")); m_ShowEnemyHUDCheckbox->SetCheck(g_SettingsMan.ShowEnemyHUD()); - m_EnableSmartBuyMenuNavigationCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxSmartBuyMenuNavigation")); + m_EnableSmartBuyMenuNavigationCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxSmartBuyMenuNavigation")); m_EnableSmartBuyMenuNavigationCheckbox->SetCheck(g_SettingsMan.SmartBuyMenuNavigationEnabled()); - m_MaxUnheldItemsTextbox = dynamic_cast(m_GUIControlManager->GetControl("TextboxMaxUnheldItems")); + m_MaxUnheldItemsTextbox = dynamic_cast(m_GUIControlManager->GetControl("TextboxMaxUnheldItems")); m_MaxUnheldItemsTextbox->SetText(std::to_string(g_MovableMan.GetMaxDroppedItems())); m_MaxUnheldItemsTextbox->SetNumericOnly(true); m_MaxUnheldItemsTextbox->SetMaxTextLength(2); - m_CrabBombThresholdTextbox = dynamic_cast(m_GUIControlManager->GetControl("TextboxCrabBombThreshold")); + m_CrabBombThresholdTextbox = dynamic_cast(m_GUIControlManager->GetControl("TextboxCrabBombThreshold")); m_CrabBombThresholdTextbox->SetText(std::to_string(g_SettingsMan.GetCrabBombThreshold())); m_CrabBombThresholdTextbox->SetNumericOnly(true); m_CrabBombThresholdTextbox->SetMaxTextLength(3); - m_UnheldItemsHUDDisplayRangeSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderUnheldItemsHUDRange")); + m_UnheldItemsHUDDisplayRangeSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderUnheldItemsHUDRange")); int unheldItemsHUDDisplayRangeValue = static_cast(g_SettingsMan.GetUnheldItemsHUDDisplayRange()); if (unheldItemsHUDDisplayRangeValue == 0) { m_UnheldItemsHUDDisplayRangeSlider->SetValue(m_UnheldItemsHUDDisplayRangeSlider->GetMinimum()); @@ -57,13 +58,13 @@ namespace RTE { } else { m_UnheldItemsHUDDisplayRangeSlider->SetValue(static_cast(g_SettingsMan.GetUnheldItemsHUDDisplayRange() / c_PPM)); } - m_UnheldItemsHUDDisplayRangeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelUnheldItemsHUDRangeValue")); + m_UnheldItemsHUDDisplayRangeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelUnheldItemsHUDRangeValue")); UpdateUnheldItemsHUDDisplayRange(); - m_AlwaysDisplayUnheldItemsInStrategicModeCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxAlwaysShowUnheldItemsInStrategicMode")); + m_AlwaysDisplayUnheldItemsInStrategicModeCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxAlwaysShowUnheldItemsInStrategicMode")); m_AlwaysDisplayUnheldItemsInStrategicModeCheckbox->SetCheck(g_SettingsMan.AlwaysDisplayUnheldItemsInStrategicMode()); - m_ScreenShakeStrengthSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderScreenShakeStrength")); + m_ScreenShakeStrengthSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderScreenShakeStrength")); int screenShakeStrengthValue = static_cast(g_CameraMan.GetScreenShakeStrength() * 100.0F); if (screenShakeStrengthValue == 0) { m_ScreenShakeStrengthSlider->SetValue(m_ScreenShakeStrengthSlider->GetMinimum()); @@ -72,11 +73,11 @@ namespace RTE { } else { m_ScreenShakeStrengthSlider->SetValue(static_cast(g_CameraMan.GetScreenShakeStrength() * 100.0F)); } - m_ScreenShakeStrengthLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelScreenShakeStrengthValue")); + m_ScreenShakeStrengthLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelScreenShakeStrengthValue")); UpdateScreenShakeStrength(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGameplayGUI::SetEnabled(bool enable) { m_GameplaySettingsBox->SetVisible(enable); @@ -88,24 +89,30 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGameplayGUI::UpdateMaxUnheldItemsTextbox() { - if (m_MaxUnheldItemsTextbox->GetText().empty()) { m_MaxUnheldItemsTextbox->SetText(std::to_string(g_MovableMan.GetMaxDroppedItems())); } + if (m_MaxUnheldItemsTextbox->GetText().empty()) { + m_MaxUnheldItemsTextbox->SetText(std::to_string(g_MovableMan.GetMaxDroppedItems())); + } g_MovableMan.SetMaxDroppedItems(std::stoi(m_MaxUnheldItemsTextbox->GetText())); m_GameplaySettingsBox->SetFocus(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGameplayGUI::UpdateCrabBombThresholdTextbox() { - if (m_CrabBombThresholdTextbox->GetText().empty()) { m_CrabBombThresholdTextbox->SetText(std::to_string(g_SettingsMan.GetCrabBombThreshold())); } - if (std::stoi(m_CrabBombThresholdTextbox->GetText()) == 0) { m_CrabBombThresholdTextbox->SetText("1"); } + if (m_CrabBombThresholdTextbox->GetText().empty()) { + m_CrabBombThresholdTextbox->SetText(std::to_string(g_SettingsMan.GetCrabBombThreshold())); + } + if (std::stoi(m_CrabBombThresholdTextbox->GetText()) == 0) { + m_CrabBombThresholdTextbox->SetText("1"); + } g_SettingsMan.SetCrabBombThreshold(std::stoi(m_CrabBombThresholdTextbox->GetText())); m_GameplaySettingsBox->SetFocus(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGameplayGUI::UpdateUnheldItemsHUDDisplayRange() { int newValue = m_UnheldItemsHUDDisplayRangeSlider->GetValue(); @@ -121,7 +128,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGameplayGUI::UpdateScreenShakeStrength() { int newValue = m_ScreenShakeStrengthSlider->GetValue(); @@ -129,9 +136,9 @@ namespace RTE { g_CameraMan.SetScreenShakeStrength(static_cast(newValue) / 100.0F); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGameplayGUI::HandleInputEvents(GUIEvent &guiEvent) { + void SettingsGameplayGUI::HandleInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Notification) { if (guiEvent.GetControl() == m_FlashOnBrainDamageCheckbox) { g_SettingsMan.SetFlashOnBrainDamage(m_FlashOnBrainDamageCheckbox->GetCheck()); @@ -157,11 +164,11 @@ namespace RTE { UpdateScreenShakeStrength(); } else if (guiEvent.GetControl() == m_AlwaysDisplayUnheldItemsInStrategicModeCheckbox) { g_SettingsMan.SetAlwaysDisplayUnheldItemsInStrategicMode(m_AlwaysDisplayUnheldItemsInStrategicModeCheckbox->GetCheck()); - // Update both textboxes when clicking the main CollectionBox, otherwise clicking off focused textboxes does not remove their focus or update the setting values and they will still capture keyboard input. + // Update both textboxes when clicking the main CollectionBox, otherwise clicking off focused textboxes does not remove their focus or update the setting values and they will still capture keyboard input. } else if (guiEvent.GetControl() == m_GameplaySettingsBox && guiEvent.GetMsg() == GUICollectionBox::Clicked && !m_GameplaySettingsBox->HasFocus()) { UpdateMaxUnheldItemsTextbox(); UpdateCrabBombThresholdTextbox(); } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/SettingsGameplayGUI.h b/Source/Menus/SettingsGameplayGUI.h index 446bef4cad..79030ea3f2 100644 --- a/Source/Menus/SettingsGameplayGUI.h +++ b/Source/Menus/SettingsGameplayGUI.h @@ -17,13 +17,12 @@ namespace RTE { class SettingsGameplayGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a SettingsGameplayGUI object in system memory and make it ready for use. /// /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsGameplayGUI. Ownership is NOT transferred! - explicit SettingsGameplayGUI(GUIControlManager *parentControlManager); + explicit SettingsGameplayGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Concrete Methods @@ -37,34 +36,33 @@ namespace RTE { /// Handles the player interaction with the SettingsInputGUI GUI elements. /// /// The GUIEvent containing information about the player interaction with an element. - void HandleInputEvents(GUIEvent &guiEvent); + void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: - - GUIControlManager *m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. + GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. /// /// GUI elements that compose the gameplay settings menu screen. /// - GUICollectionBox *m_GameplaySettingsBox; - GUICheckbox *m_FlashOnBrainDamageCheckbox; - GUICheckbox *m_BlipOnRevealUnseenCheckbox; - GUICheckbox *m_ShowForeignItemsCheckbox; - GUICheckbox *m_EnableCrabBombsCheckbox; - GUICheckbox *m_EndlessMetaGameCheckbox; - GUICheckbox *m_ShowEnemyHUDCheckbox; - GUICheckbox *m_EnableSmartBuyMenuNavigationCheckbox; - GUITextBox *m_MaxUnheldItemsTextbox; - GUITextBox *m_CrabBombThresholdTextbox; + GUICollectionBox* m_GameplaySettingsBox; + GUICheckbox* m_FlashOnBrainDamageCheckbox; + GUICheckbox* m_BlipOnRevealUnseenCheckbox; + GUICheckbox* m_ShowForeignItemsCheckbox; + GUICheckbox* m_EnableCrabBombsCheckbox; + GUICheckbox* m_EndlessMetaGameCheckbox; + GUICheckbox* m_ShowEnemyHUDCheckbox; + GUICheckbox* m_EnableSmartBuyMenuNavigationCheckbox; + GUITextBox* m_MaxUnheldItemsTextbox; + GUITextBox* m_CrabBombThresholdTextbox; - GUISlider *m_UnheldItemsHUDDisplayRangeSlider; - GUILabel *m_UnheldItemsHUDDisplayRangeLabel; + GUISlider* m_UnheldItemsHUDDisplayRangeSlider; + GUILabel* m_UnheldItemsHUDDisplayRangeLabel; - GUICheckbox *m_AlwaysDisplayUnheldItemsInStrategicModeCheckbox; + GUICheckbox* m_AlwaysDisplayUnheldItemsInStrategicModeCheckbox; - GUISlider *m_ScreenShakeStrengthSlider; - GUILabel *m_ScreenShakeStrengthLabel; + GUISlider* m_ScreenShakeStrengthSlider; + GUILabel* m_ScreenShakeStrengthLabel; #pragma region Gameplay Settings Handling /// @@ -89,8 +87,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - SettingsGameplayGUI(const SettingsGameplayGUI &reference) = delete; - SettingsGameplayGUI & operator=(const SettingsGameplayGUI &rhs) = delete; + SettingsGameplayGUI(const SettingsGameplayGUI& reference) = delete; + SettingsGameplayGUI& operator=(const SettingsGameplayGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/SettingsInputGUI.cpp b/Source/Menus/SettingsInputGUI.cpp index bf5606b30b..dab27d5b24 100644 --- a/Source/Menus/SettingsInputGUI.cpp +++ b/Source/Menus/SettingsInputGUI.cpp @@ -10,27 +10,28 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsInputGUI::SettingsInputGUI(GUIControlManager *parentControlManager) : m_GUIControlManager(parentControlManager) { - m_InputSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxInputSettings")); + SettingsInputGUI::SettingsInputGUI(GUIControlManager* parentControlManager) : + m_GUIControlManager(parentControlManager) { + m_InputSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxInputSettings")); for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { std::string playerNum = std::to_string(player + 1); - m_PlayerInputSettingsBoxes[player].SelectedDeviceLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelP" + playerNum + "SelectedDevice")); + m_PlayerInputSettingsBoxes[player].SelectedDeviceLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelP" + playerNum + "SelectedDevice")); - m_PlayerInputSettingsBoxes[player].NextDeviceButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonP" + playerNum + "NextDevice")); - m_PlayerInputSettingsBoxes[player].PrevDeviceButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonP" + playerNum + "PrevDevice")); - m_PlayerInputSettingsBoxes[player].ConfigureControlsButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonP" + playerNum + "Config")); - m_PlayerInputSettingsBoxes[player].ResetControlsButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonP" + playerNum + "Clear")); + m_PlayerInputSettingsBoxes[player].NextDeviceButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonP" + playerNum + "NextDevice")); + m_PlayerInputSettingsBoxes[player].PrevDeviceButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonP" + playerNum + "PrevDevice")); + m_PlayerInputSettingsBoxes[player].ConfigureControlsButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonP" + playerNum + "Config")); + m_PlayerInputSettingsBoxes[player].ResetControlsButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonP" + playerNum + "Clear")); - m_PlayerInputSettingsBoxes[player].SensitivityLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelP" + playerNum + "Sensitivity")); - m_PlayerInputSettingsBoxes[player].SensitivitySlider = dynamic_cast(m_GUIControlManager->GetControl("SliderP" + playerNum + "Sensitivity")); + m_PlayerInputSettingsBoxes[player].SensitivityLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelP" + playerNum + "Sensitivity")); + m_PlayerInputSettingsBoxes[player].SensitivitySlider = dynamic_cast(m_GUIControlManager->GetControl("SliderP" + playerNum + "Sensitivity")); - m_PlayerInputSettingsBoxes[player].DeadZoneControlsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxP" + playerNum + "DeadzoneControls")); - m_PlayerInputSettingsBoxes[player].CircleDeadZoneRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioP" + playerNum + "DeadzoneCircle")); - m_PlayerInputSettingsBoxes[player].SquareDeadZoneRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioP" + playerNum + "DeadzoneSquare")); + m_PlayerInputSettingsBoxes[player].DeadZoneControlsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxP" + playerNum + "DeadzoneControls")); + m_PlayerInputSettingsBoxes[player].CircleDeadZoneRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioP" + playerNum + "DeadzoneCircle")); + m_PlayerInputSettingsBoxes[player].SquareDeadZoneRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioP" + playerNum + "DeadzoneSquare")); } for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { UpdatePlayerSelectedDeviceLabel(player); @@ -39,26 +40,30 @@ namespace RTE { m_InputMappingConfigMenu = std::make_unique(parentControlManager); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputGUI::SetEnabled(bool enable) const { m_InputSettingsBox->SetVisible(enable); m_InputSettingsBox->SetEnabled(enable); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputGUI::ResetPlayerInputSettings(int player) { if (m_PlayerInputSettingsBoxes.at(player).ResetControlsButton->GetText() == "Reset") { // Only one player's reset button can be pending confirmation at a time, so cancel any other pending confirmations. for (int otherPlayer = Players::PlayerOne; otherPlayer < Players::MaxPlayerCount; ++otherPlayer) { - if (otherPlayer != player) { m_PlayerInputSettingsBoxes.at(otherPlayer).ResetControlsButton->SetText("Reset"); } + if (otherPlayer != player) { + m_PlayerInputSettingsBoxes.at(otherPlayer).ResetControlsButton->SetText("Reset"); + } } m_PlayerInputSettingsBoxes.at(player).ResetControlsButton->SetText("CONFIRM?"); } else { - InputScheme *playerControlScheme = g_UInputMan.GetControlScheme(player); + InputScheme* playerControlScheme = g_UInputMan.GetControlScheme(player); playerControlScheme->ResetToPlayerDefaults(static_cast(player)); - if (playerControlScheme->GetDevice() == InputDevice::DEVICE_MOUSE_KEYB) { g_UInputMan.SetMouseSensitivity(0.6F); } + if (playerControlScheme->GetDevice() == InputDevice::DEVICE_MOUSE_KEYB) { + g_UInputMan.SetMouseSensitivity(0.6F); + } UpdatePlayerSelectedDeviceLabel(player); ShowOrHidePlayerInputDeviceSensitivityControls(player); @@ -69,24 +74,28 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputGUI::SetPlayerNextOrPrevInputDevice(int player, bool nextDevice) { int currentDevice = static_cast(g_UInputMan.GetControlScheme(player)->GetDevice()); if (nextDevice) { currentDevice++; - if (currentDevice >= InputDevice::DEVICE_COUNT) { currentDevice = InputDevice::DEVICE_KEYB_ONLY; } + if (currentDevice >= InputDevice::DEVICE_COUNT) { + currentDevice = InputDevice::DEVICE_KEYB_ONLY; + } } else { currentDevice--; - if (currentDevice < InputDevice::DEVICE_KEYB_ONLY) { currentDevice = InputDevice::DEVICE_GAMEPAD_4; } + if (currentDevice < InputDevice::DEVICE_KEYB_ONLY) { + currentDevice = InputDevice::DEVICE_GAMEPAD_4; + } } g_UInputMan.GetControlScheme(player)->SetDevice(static_cast(currentDevice)); UpdatePlayerSelectedDeviceLabel(player); ShowOrHidePlayerInputDeviceSensitivityControls(player); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputGUI::UpdatePlayerSelectedDeviceLabel(int player) { std::string deviceLabel; @@ -116,7 +125,7 @@ namespace RTE { m_PlayerInputSettingsBoxes.at(player).SelectedDeviceLabel->SetText(deviceLabel); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputGUI::ShowOrHidePlayerInputDeviceSensitivityControls(int player) { m_PlayerInputSettingsBoxes.at(player).SensitivityLabel->SetVisible(false); @@ -149,7 +158,7 @@ namespace RTE { UpdatePlayerInputSensitivityControlValues(player); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputGUI::UpdatePlayerInputSensitivityControlValues(int player) { switch (g_UInputMan.GetControlScheme(player)->GetDevice()) { @@ -183,9 +192,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputGUI::HandleInputEvents(GUIEvent &guiEvent) { + void SettingsInputGUI::HandleInputEvents(GUIEvent& guiEvent) { if (m_InputMappingConfigMenu->IsEnabled()) { m_InputMappingConfigMenu->HandleInputEvents(guiEvent); return; @@ -221,4 +230,4 @@ namespace RTE { } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/SettingsInputGUI.h b/Source/Menus/SettingsInputGUI.h index 79df3264fa..227db550c0 100644 --- a/Source/Menus/SettingsInputGUI.h +++ b/Source/Menus/SettingsInputGUI.h @@ -22,13 +22,12 @@ namespace RTE { class SettingsInputGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a SettingsInputGUI object in system memory and make it ready for use. /// /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsInputGUI. Ownership is NOT transferred! - explicit SettingsInputGUI(GUIControlManager *parentControlManager); + explicit SettingsInputGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters @@ -42,7 +41,7 @@ namespace RTE { /// Gets the currently active GUICollectionBox that acts as a dialog box and requires disabling navigation and drawing an overlay. /// /// Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! - GUICollectionBox * GetActiveDialogBox() const { return m_InputMappingConfigMenu->GetActiveDialogBox(); } + GUICollectionBox* GetActiveDialogBox() const { return m_InputMappingConfigMenu->GetActiveDialogBox(); } /// /// Closes the currently active GUICollectionBox that acts as a dialog box by hiding it. If the active dialog box is a sub-menu, disables it. @@ -79,35 +78,34 @@ namespace RTE { /// Handles the player interaction with the SettingsInputGUI GUI elements. /// /// The GUIEvent containing information about the player interaction with an element. - void HandleInputEvents(GUIEvent &guiEvent); + void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: - /// /// Struct containing GUI elements that compose the input settings box of a player. /// struct PlayerInputSettingsBox { - GUILabel *SelectedDeviceLabel; - GUIButton *NextDeviceButton; - GUIButton *PrevDeviceButton; - GUIButton *ConfigureControlsButton; - GUIButton *ResetControlsButton; - GUILabel *SensitivityLabel; - GUISlider *SensitivitySlider; - GUICollectionBox *DeadZoneControlsBox; - GUIRadioButton *CircleDeadZoneRadioButton; - GUIRadioButton *SquareDeadZoneRadioButton; + GUILabel* SelectedDeviceLabel; + GUIButton* NextDeviceButton; + GUIButton* PrevDeviceButton; + GUIButton* ConfigureControlsButton; + GUIButton* ResetControlsButton; + GUILabel* SensitivityLabel; + GUISlider* SensitivitySlider; + GUICollectionBox* DeadZoneControlsBox; + GUIRadioButton* CircleDeadZoneRadioButton; + GUIRadioButton* SquareDeadZoneRadioButton; }; - GUIControlManager *m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. + GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. std::unique_ptr m_InputMappingConfigMenu; //!< The input mapping configuration sub-menu. /// /// GUI elements that compose the input settings menu screen. /// - GUICollectionBox *m_InputSettingsBox; + GUICollectionBox* m_InputSettingsBox; std::array m_PlayerInputSettingsBoxes; #pragma region Input Settings Handling @@ -144,8 +142,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - SettingsInputGUI(const SettingsInputGUI &reference) = delete; - SettingsInputGUI & operator=(const SettingsInputGUI &rhs) = delete; + SettingsInputGUI(const SettingsInputGUI& reference) = delete; + SettingsInputGUI& operator=(const SettingsInputGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/SettingsInputMappingGUI.cpp b/Source/Menus/SettingsInputMappingGUI.cpp index 420d648685..c345a3f38a 100644 --- a/Source/Menus/SettingsInputMappingGUI.cpp +++ b/Source/Menus/SettingsInputMappingGUI.cpp @@ -10,36 +10,37 @@ namespace RTE { - std::array SettingsInputMappingGUI::m_InputElementsUsedByMouse = { InputElements::INPUT_FIRE, InputElements::INPUT_PIEMENU_ANALOG, InputElements::INPUT_AIM, InputElements::INPUT_AIM_UP, InputElements::INPUT_AIM_DOWN, InputElements::INPUT_AIM_LEFT, InputElements::INPUT_AIM_RIGHT }; + std::array SettingsInputMappingGUI::m_InputElementsUsedByMouse = {InputElements::INPUT_FIRE, InputElements::INPUT_PIEMENU_ANALOG, InputElements::INPUT_AIM, InputElements::INPUT_AIM_UP, InputElements::INPUT_AIM_DOWN, InputElements::INPUT_AIM_LEFT, InputElements::INPUT_AIM_RIGHT}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsInputMappingGUI::SettingsInputMappingGUI(GUIControlManager *parentControlManager) : m_GUIControlManager(parentControlManager) { - m_InputMappingSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPlayerInputMapping")); + SettingsInputMappingGUI::SettingsInputMappingGUI(GUIControlManager* parentControlManager) : + m_GUIControlManager(parentControlManager) { + m_InputMappingSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPlayerInputMapping")); m_InputMappingSettingsBox->SetVisible(false); - m_InputMappingSettingsLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelPlayerInputMappingTitle")); - m_CloseMappingBoxButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCloseMappingBox")); - m_RunConfigWizardButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonRunConfigWizard")); + m_InputMappingSettingsLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelPlayerInputMappingTitle")); + m_CloseMappingBoxButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCloseMappingBox")); + m_RunConfigWizardButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonRunConfigWizard")); - m_InputMapScrollingBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxScrollingMappingBox")); - m_InputMapScrollingBoxScrollbar = dynamic_cast(m_GUIControlManager->GetControl("ScrollbarScrollingMappingBox")); + m_InputMapScrollingBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxScrollingMappingBox")); + m_InputMapScrollingBoxScrollbar = dynamic_cast(m_GUIControlManager->GetControl("ScrollbarScrollingMappingBox")); m_InputMapScrollingBoxScrollbar->SetMaximum(m_InputMapScrollingBox->GetHeight()); m_InputMapScrollingBoxScrollbar->SetPageSize(m_InputMapScrollingBoxScrollbar->GetMaximum() / 2); m_LastInputMapScrollingBoxScrollbarValue = m_InputMapScrollingBoxScrollbar->GetValue(); for (int i = 0; i < InputElements::INPUT_COUNT; ++i) { - m_InputMapLabel[i] = dynamic_cast(m_GUIControlManager->GetControl("LabelInputName" + std::to_string(i + 1))); + m_InputMapLabel[i] = dynamic_cast(m_GUIControlManager->GetControl("LabelInputName" + std::to_string(i + 1))); m_InputMapLabel[i]->SetText(c_InputElementNames[i]); - m_InputMapButton[i] = dynamic_cast(m_GUIControlManager->GetControl("ButtonInputKey" + std::to_string(i + 1))); + m_InputMapButton[i] = dynamic_cast(m_GUIControlManager->GetControl("ButtonInputKey" + std::to_string(i + 1))); } - m_InputMappingCaptureBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxInputCapture")); + m_InputMappingCaptureBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxInputCapture")); m_InputMappingCaptureBox->SetVisible(false); - GUICollectionBox *settingsRootBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSettingsBase")); + GUICollectionBox* settingsRootBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSettingsBase")); m_InputMappingCaptureBox->SetPositionAbs(settingsRootBox->GetXPos() + ((settingsRootBox->GetWidth() - m_InputMappingCaptureBox->GetWidth()) / 2), settingsRootBox->GetYPos() + ((settingsRootBox->GetHeight() - m_InputMappingCaptureBox->GetHeight()) / 2)); - m_InputElementCapturingInputNameLabel = dynamic_cast(m_GUIControlManager->GetControl("ButtonLabelInputMappingName")); + m_InputElementCapturingInputNameLabel = dynamic_cast(m_GUIControlManager->GetControl("ButtonLabelInputMappingName")); m_InputConfigWizardMenu = std::make_unique(parentControlManager); @@ -49,13 +50,13 @@ namespace RTE { m_InputElementCapturingInput = InputElements::INPUT_COUNT; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsInputMappingGUI::IsEnabled() const { return m_InputMappingSettingsBox->GetVisible() && m_InputMappingSettingsBox->GetEnabled(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingGUI::SetEnabled(bool enable, int player) { m_InputMappingSettingsBox->SetVisible(enable); @@ -75,9 +76,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICollectionBox * SettingsInputMappingGUI::GetActiveDialogBox() const { + GUICollectionBox* SettingsInputMappingGUI::GetActiveDialogBox() const { if (m_InputConfigWizardMenu->IsEnabled()) { return m_InputConfigWizardMenu->GetActiveDialogBox(); } else if (m_InputMappingCaptureBox->GetEnabled() && m_InputMappingCaptureBox->GetVisible()) { @@ -86,7 +87,7 @@ namespace RTE { return (m_InputMappingSettingsBox->GetEnabled() && m_InputMappingSettingsBox->GetVisible()) ? m_InputMappingSettingsBox : nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingGUI::CloseActiveDialogBox() { if (m_InputConfigWizardMenu->IsEnabled()) { @@ -98,13 +99,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsInputMappingGUI::IsConfiguringManually() const { return m_ConfiguringManually && m_InputMappingCaptureBox->GetVisible() && m_InputMappingCaptureBox->GetEnabled(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingGUI::ShowInputMappingCaptureBox(InputElements inputElement) { m_InputMappingSettingsBox->SetEnabled(false); @@ -117,7 +118,7 @@ namespace RTE { g_UInputMan.SetSkipHandlingSpecialInput(true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingGUI::HideInputMappingCaptureBox() { m_InputMappingSettingsBox->SetEnabled(true); @@ -128,13 +129,15 @@ namespace RTE { g_UInputMan.SetSkipHandlingSpecialInput(false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingGUI::UpdateMappingButtonLabels() { - const std::array *inputMappings = m_ConfiguringPlayerInputScheme->GetInputMappings(); + const std::array* inputMappings = m_ConfiguringPlayerInputScheme->GetInputMappings(); for (int i = 0; i < InputElements::INPUT_COUNT; ++i) { std::string inputDescription = inputMappings->at(i).GetPresetDescription(); - if (inputDescription.empty()) { inputDescription = m_ConfiguringPlayerInputScheme->GetMappingName(i); } + if (inputDescription.empty()) { + inputDescription = m_ConfiguringPlayerInputScheme->GetMappingName(i); + } m_InputMapButton[i]->SetText(!inputDescription.empty() ? "[" + inputDescription + "]" : "[Undefined]"); } // Adjust the scrolling box scroll range to hide mappings that are only relevant to gamepads. @@ -142,7 +145,7 @@ namespace RTE { m_InputMapScrollingBoxScrollbar->SetPageSize(m_InputMapScrollingBoxScrollbar->GetMaximum() / 2); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingGUI::UpdateScrollingInputBoxScrollPosition() { int scrollbarValue = m_InputMapScrollingBoxScrollbar->GetValue(); @@ -150,11 +153,13 @@ namespace RTE { m_LastInputMapScrollingBoxScrollbarValue = scrollbarValue; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingGUI::HandleInputEvents(GUIEvent &guiEvent) { + void SettingsInputMappingGUI::HandleInputEvents(GUIEvent& guiEvent) { if (m_InputConfigWizardMenu->IsEnabled()) { - if (m_InputConfigWizardMenu->HandleInputEvents(guiEvent)) { UpdateMappingButtonLabels(); } + if (m_InputConfigWizardMenu->HandleInputEvents(guiEvent)) { + UpdateMappingButtonLabels(); + } return; } if (guiEvent.GetType() == GUIEvent::Command) { @@ -186,7 +191,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingGUI::HandleManualConfigSequence() { bool inputCaptured = false; @@ -209,4 +214,4 @@ namespace RTE { HideInputMappingCaptureBox(); } } -} +} // namespace RTE diff --git a/Source/Menus/SettingsInputMappingGUI.h b/Source/Menus/SettingsInputMappingGUI.h index 712b4c17ac..b28a261bfb 100644 --- a/Source/Menus/SettingsInputMappingGUI.h +++ b/Source/Menus/SettingsInputMappingGUI.h @@ -19,13 +19,12 @@ namespace RTE { class SettingsInputMappingGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a SettingsInputMappingGUI object in system memory and make it ready for use. /// /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsInputMappingGUI. Ownership is NOT transferred! - explicit SettingsInputMappingGUI(GUIControlManager *parentControlManager); + explicit SettingsInputMappingGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters @@ -46,7 +45,7 @@ namespace RTE { /// Gets the currently active GUICollectionBox that acts as a dialog box and requires disabling navigation and drawing an overlay. /// /// Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! - GUICollectionBox * GetActiveDialogBox() const; + GUICollectionBox* GetActiveDialogBox() const; /// /// Closes the currently active GUICollectionBox that acts as a dialog box by hiding it. If the active dialog box is a sub-menu, disables it. @@ -63,7 +62,7 @@ namespace RTE { /// Gets the SettingsInputMappingWizardGUI of this SettingsInputMappingGUI. /// /// Pointer to the SettingsInputMappingWizardGUI of this SettingsInputMappingGUI. Ownership is NOT transferred! - SettingsInputMappingWizardGUI * GetInputConfigWizardMenu() { return m_InputConfigWizardMenu.get(); } + SettingsInputMappingWizardGUI* GetInputConfigWizardMenu() { return m_InputConfigWizardMenu.get(); } #pragma endregion #pragma region Concrete Methods @@ -71,7 +70,7 @@ namespace RTE { /// Handles the player interaction with the SettingsInputMappingGUI GUI elements. /// /// The GUIEvent containing information about the player interaction with an element. - void HandleInputEvents(GUIEvent &guiEvent); + void HandleInputEvents(GUIEvent& guiEvent); /// /// Handles capturing input and updating the manual input configuration sequence. @@ -80,13 +79,12 @@ namespace RTE { #pragma endregion private: - static std::array m_InputElementsUsedByMouse; //!< Array containing InputElements that are hard mapped to mouse controls when using mouse + keyboard. - GUIControlManager *m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. + GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. Players m_ConfiguringPlayer; //!< The player this SettingsInputMappingGUI is configuring input mapping for. - InputScheme *m_ConfiguringPlayerInputScheme; //!< The InputScheme of the configuring player. + InputScheme* m_ConfiguringPlayerInputScheme; //!< The InputScheme of the configuring player. bool m_ConfiguringManually; //!< Indicates that the SettingsInputMappingGUI needs to capture input because the player is configuring manually. InputElements m_InputElementCapturingInput; //!< The InputElement in the configuring player's InputScheme that is currently being configured and is capturing input. @@ -98,16 +96,16 @@ namespace RTE { /// /// GUI elements that compose the input mapping settings menu screen. /// - GUICollectionBox *m_InputMappingSettingsBox; - GUILabel *m_InputMappingSettingsLabel; - GUIButton *m_CloseMappingBoxButton; - GUIButton *m_RunConfigWizardButton; - GUICollectionBox *m_InputMapScrollingBox; - GUIScrollbar *m_InputMapScrollingBoxScrollbar; - GUICollectionBox *m_InputMappingCaptureBox; - GUIButton *m_InputElementCapturingInputNameLabel; - std::array m_InputMapLabel; - std::array m_InputMapButton; + GUICollectionBox* m_InputMappingSettingsBox; + GUILabel* m_InputMappingSettingsLabel; + GUIButton* m_CloseMappingBoxButton; + GUIButton* m_RunConfigWizardButton; + GUICollectionBox* m_InputMapScrollingBox; + GUIScrollbar* m_InputMapScrollingBoxScrollbar; + GUICollectionBox* m_InputMappingCaptureBox; + GUIButton* m_InputElementCapturingInputNameLabel; + std::array m_InputMapLabel; + std::array m_InputMapButton; #pragma region Input Mapping Settings Handling /// @@ -133,8 +131,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - SettingsInputMappingGUI(const SettingsInputMappingGUI &reference) = delete; - SettingsInputMappingGUI & operator=(const SettingsInputMappingGUI &rhs) = delete; + SettingsInputMappingGUI(const SettingsInputMappingGUI& reference) = delete; + SettingsInputMappingGUI& operator=(const SettingsInputMappingGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/SettingsInputMappingWizardGUI.cpp b/Source/Menus/SettingsInputMappingWizardGUI.cpp index 75fa50c76b..c7d1762d1b 100644 --- a/Source/Menus/SettingsInputMappingWizardGUI.cpp +++ b/Source/Menus/SettingsInputMappingWizardGUI.cpp @@ -9,7 +9,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::Clear() { m_ConfiguringPlayer = Players::NoPlayer; @@ -35,13 +35,14 @@ namespace RTE { m_DualAnalogXBDiagramBitmaps.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsInputMappingWizardGUI::SettingsInputMappingWizardGUI(GUIControlManager *parentControlManager) : m_GUIControlManager(parentControlManager) { + SettingsInputMappingWizardGUI::SettingsInputMappingWizardGUI(GUIControlManager* parentControlManager) : + m_GUIControlManager(parentControlManager) { Clear(); - m_InputWizardScreenBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxInputMappingWizard")); - m_InputWizardTitleLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelPlayerInputMappingWizardTitle")); + m_InputWizardScreenBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxInputMappingWizard")); + m_InputWizardTitleLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelPlayerInputMappingWizardTitle")); int dpadDiagramBitampCount = 13; ContentFile("Base.rte/GUIs/Controllers/D-Pad.png").GetAsAnimation(m_DPadDiagramBitmaps, dpadDiagramBitampCount, COLORCONV_8_TO_32); @@ -54,56 +55,56 @@ namespace RTE { CreatePresetSelectionScreen(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::CreateManualConfigScreen() { - m_WizardManualConfigScreen.ManualConfigBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxWizardManualConfig")); - - m_WizardManualConfigScreen.ConfigDeviceTypeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelConfigDeviceType")); - m_WizardManualConfigScreen.ConfigStepDescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelConfigInputDescription")); - m_WizardManualConfigScreen.ConfigStepRecommendedKeyLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelRecommendedKeyInput")); - m_WizardManualConfigScreen.GamepadConfigRecommendedBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxGamepadRecommendedInput")); - m_WizardManualConfigScreen.GamepadConfigStepRecommendedInputLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelRecommendedGamepadInput")); - m_WizardManualConfigScreen.GamepadConfigRecommendedDiagramBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxRecommendedGamepadInputDiagram")); - - m_WizardManualConfigScreen.ConfigStepLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelConfigStep")); - m_WizardManualConfigScreen.PrevConfigStepButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigPrevStep")); - m_WizardManualConfigScreen.NextConfigStepButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigNextStep")); - m_WizardManualConfigScreen.ResetConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigRestart")); - m_WizardManualConfigScreen.DiscardOrApplyConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonDiscardOrApply")); + m_WizardManualConfigScreen.ManualConfigBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxWizardManualConfig")); + + m_WizardManualConfigScreen.ConfigDeviceTypeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelConfigDeviceType")); + m_WizardManualConfigScreen.ConfigStepDescriptionLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelConfigInputDescription")); + m_WizardManualConfigScreen.ConfigStepRecommendedKeyLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelRecommendedKeyInput")); + m_WizardManualConfigScreen.GamepadConfigRecommendedBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxGamepadRecommendedInput")); + m_WizardManualConfigScreen.GamepadConfigStepRecommendedInputLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelRecommendedGamepadInput")); + m_WizardManualConfigScreen.GamepadConfigRecommendedDiagramBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxRecommendedGamepadInputDiagram")); + + m_WizardManualConfigScreen.ConfigStepLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelConfigStep")); + m_WizardManualConfigScreen.PrevConfigStepButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigPrevStep")); + m_WizardManualConfigScreen.NextConfigStepButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigNextStep")); + m_WizardManualConfigScreen.ResetConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigRestart")); + m_WizardManualConfigScreen.DiscardOrApplyConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonDiscardOrApply")); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::CreatePresetSelectionScreen() { - m_WizardPresetSelectScreen.PresetSelectBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxWizardPresets")); - m_WizardPresetSelectScreen.CloseWizardButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCloseWizardBox")); + m_WizardPresetSelectScreen.PresetSelectBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxWizardPresets")); + m_WizardPresetSelectScreen.CloseWizardButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCloseWizardBox")); - m_WizardPresetSelectScreen.PresetSelectSNESButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonPresetDPadSNES")); - m_WizardPresetSelectScreen.PresetSelectDS4Button = dynamic_cast(m_GUIControlManager->GetControl("ButtonPresetAnalogDS4")); - m_WizardPresetSelectScreen.PresetSelectXB360Button = dynamic_cast(m_GUIControlManager->GetControl("ButtonPresetAnalogXB360")); - m_WizardPresetSelectScreen.StartConfigDPadTypeButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigDPadType")); - m_WizardPresetSelectScreen.StartConfigAnalogDSTypeButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigAnalogTypeDS")); - m_WizardPresetSelectScreen.StartConfigAnalogXBTypeButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigAnalogTypeXB")); + m_WizardPresetSelectScreen.PresetSelectSNESButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonPresetDPadSNES")); + m_WizardPresetSelectScreen.PresetSelectDS4Button = dynamic_cast(m_GUIControlManager->GetControl("ButtonPresetAnalogDS4")); + m_WizardPresetSelectScreen.PresetSelectXB360Button = dynamic_cast(m_GUIControlManager->GetControl("ButtonPresetAnalogXB360")); + m_WizardPresetSelectScreen.StartConfigDPadTypeButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigDPadType")); + m_WizardPresetSelectScreen.StartConfigAnalogDSTypeButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigAnalogTypeDS")); + m_WizardPresetSelectScreen.StartConfigAnalogXBTypeButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfigAnalogTypeXB")); // Ownership of the AllegroBitmaps is passed to the GUIControlManager. - dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxConfigDPadTypeDiagram"))->SetDrawImage(new AllegroBitmap(m_DPadDiagramBitmaps[0])); - dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPresetDPadSNESDiagram"))->SetDrawImage(new AllegroBitmap(m_DPadDiagramBitmaps[0])); - dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxConfigAnalogTypeDSDiagram"))->SetDrawImage(new AllegroBitmap(m_DualAnalogDSDiagramBitmaps[0])); - dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPresetAnalogDS4Diagram"))->SetDrawImage(new AllegroBitmap(m_DualAnalogDSDiagramBitmaps[0])); - dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxConfigAnalogTypeXBDiagram"))->SetDrawImage(new AllegroBitmap(m_DualAnalogXBDiagramBitmaps[0])); - dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPresetAnalogXB360Diagram"))->SetDrawImage(new AllegroBitmap(m_DualAnalogXBDiagramBitmaps[0])); + dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxConfigDPadTypeDiagram"))->SetDrawImage(new AllegroBitmap(m_DPadDiagramBitmaps[0])); + dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPresetDPadSNESDiagram"))->SetDrawImage(new AllegroBitmap(m_DPadDiagramBitmaps[0])); + dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxConfigAnalogTypeDSDiagram"))->SetDrawImage(new AllegroBitmap(m_DualAnalogDSDiagramBitmaps[0])); + dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPresetAnalogDS4Diagram"))->SetDrawImage(new AllegroBitmap(m_DualAnalogDSDiagramBitmaps[0])); + dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxConfigAnalogTypeXBDiagram"))->SetDrawImage(new AllegroBitmap(m_DualAnalogXBDiagramBitmaps[0])); + dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPresetAnalogXB360Diagram"))->SetDrawImage(new AllegroBitmap(m_DualAnalogXBDiagramBitmaps[0])); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsInputMappingWizardGUI::IsEnabled() const { return m_InputWizardScreenBox->GetVisible() && m_InputWizardScreenBox->GetEnabled(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::SetEnabled(bool enable, int player, InputScheme *playerScheme) { + void SettingsInputMappingWizardGUI::SetEnabled(bool enable, int player, InputScheme* playerScheme) { m_InputWizardScreenBox->SetVisible(enable); m_InputWizardScreenBox->SetEnabled(enable); @@ -133,19 +134,19 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICollectionBox * SettingsInputMappingWizardGUI::GetActiveDialogBox() const { + GUICollectionBox* SettingsInputMappingWizardGUI::GetActiveDialogBox() const { return (m_InputWizardScreenBox->GetEnabled() && m_InputWizardScreenBox->GetVisible()) ? m_InputWizardScreenBox : nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsInputMappingWizardGUI::IsConfiguringManually() const { return m_ConfiguringManually && m_WizardManualConfigScreen.ManualConfigBox->GetVisible() && m_WizardManualConfigScreen.ManualConfigBox->GetEnabled(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::ShowManualConfigScreen() { m_WizardPresetSelectScreen.PresetSelectBox->SetVisible(false); @@ -191,7 +192,7 @@ namespace RTE { m_ConfiguringManually = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::ShowPresetSelectionScreen() { m_WizardManualConfigScreen.ManualConfigBox->SetVisible(false); @@ -201,7 +202,7 @@ namespace RTE { m_WizardPresetSelectScreen.PresetSelectBox->SetEnabled(true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::ResetManualConfigSequence() { m_ConfigFinished = false; @@ -211,7 +212,7 @@ namespace RTE { m_NewInputScheme.SetDevice(m_ConfiguringDevice); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::ApplyGamepadInputPreset(GamepadType gamepadType) { switch (gamepadType) { @@ -232,7 +233,7 @@ namespace RTE { m_NewInputSchemeApplied = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::ApplyManuallyConfiguredScheme() { m_ConfiguringPlayerScheme->SetDevice(m_NewInputScheme.GetDevice()); @@ -254,9 +255,9 @@ namespace RTE { m_ConfiguringManually = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingWizardGUI::HandleInputEvents(GUIEvent &guiEvent) { + bool SettingsInputMappingWizardGUI::HandleInputEvents(GUIEvent& guiEvent) { if (m_WizardManualConfigScreen.ManualConfigBox->GetVisible()) { HandleManualConfigScreenInputEvents(guiEvent); } else if (m_WizardPresetSelectScreen.PresetSelectBox->GetVisible()) { @@ -269,9 +270,9 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::HandleManualConfigScreenInputEvents(GUIEvent &guiEvent) { + void SettingsInputMappingWizardGUI::HandleManualConfigScreenInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Command) { if (guiEvent.GetControl() == m_WizardManualConfigScreen.PrevConfigStepButton) { g_GUISound.ButtonPressSound()->Play(); @@ -303,9 +304,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::HandlePresetSelectScreenInputEvents(GUIEvent &guiEvent) { + void SettingsInputMappingWizardGUI::HandlePresetSelectScreenInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Command) { if (guiEvent.GetControl() == m_WizardPresetSelectScreen.CloseWizardButton) { g_GUISound.ButtonPressSound()->Play(); @@ -332,10 +333,12 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::HandleManualConfigSequence() { - if (m_ConfigStepChange) { HandleManualConfigStepChange(); } + if (m_ConfigStepChange) { + HandleManualConfigStepChange(); + } if (m_ConfigFinished) { m_GUIControlManager->GetManager()->SetFocus(m_BlinkTimer.AlternateReal(500) ? m_WizardManualConfigScreen.DiscardOrApplyConfigButton : nullptr); @@ -364,14 +367,16 @@ namespace RTE { break; } if (inputCaptured) { - if (!m_ConfigFinished) { m_ConfigStep++; } + if (!m_ConfigFinished) { + m_ConfigStep++; + } g_GUISound.ExitMenuSound()->Play(); m_ConfigStepChange = true; } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsInputMappingWizardGUI::HandleManualConfigStepChange() { int configuringDeviceSteps = 0; @@ -408,13 +413,15 @@ namespace RTE { m_WizardManualConfigScreen.ConfigStepDescriptionLabel->SetVisible(false); m_WizardManualConfigScreen.ConfigStepRecommendedKeyLabel->SetVisible(false); m_WizardManualConfigScreen.GamepadConfigRecommendedBox->SetVisible(false); - if (m_ConfigStep < configuringDeviceSteps - 1) { m_ConfigFinished = false; } + if (m_ConfigStep < configuringDeviceSteps - 1) { + m_ConfigFinished = false; + } } m_WizardManualConfigScreen.PrevConfigStepButton->SetVisible(m_ConfigStep != 0); m_WizardManualConfigScreen.NextConfigStepButton->SetVisible(m_ConfigStep < configuringDeviceSteps - 1); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsInputMappingWizardGUI::UpdateKeyboardConfigSequence() { switch (m_ConfigStep) { @@ -586,7 +593,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsInputMappingWizardGUI::UpdateMouseAndKeyboardConfigSequence() { switch (m_ConfigStep) { @@ -710,7 +717,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsInputMappingWizardGUI::UpdateGamepadDPadConfigSequence() { switch (m_ConfigStep) { @@ -844,7 +851,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsInputMappingWizardGUI::UpdateGamepadAnalogConfigSequence() { switch (m_ConfigStep) { @@ -1059,4 +1066,4 @@ namespace RTE { } return false; } -} +} // namespace RTE diff --git a/Source/Menus/SettingsInputMappingWizardGUI.h b/Source/Menus/SettingsInputMappingWizardGUI.h index c8cf0b5316..896138999f 100644 --- a/Source/Menus/SettingsInputMappingWizardGUI.h +++ b/Source/Menus/SettingsInputMappingWizardGUI.h @@ -21,13 +21,12 @@ namespace RTE { class SettingsInputMappingWizardGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a SettingsInputMappingWizardGUI object in system memory and make it ready for use. /// /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsInputMappingWizardGUI. Ownership is NOT transferred! - explicit SettingsInputMappingWizardGUI(GUIControlManager *parentControlManager); + explicit SettingsInputMappingWizardGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters @@ -42,13 +41,13 @@ namespace RTE { /// /// Show and enable or hide and disable the SettingsInputMappingWizardGUI. /// The player this SettingsInputMappingWizardGUI is mapping inputs for. - void SetEnabled(bool enable = true, int player = 0, InputScheme *playerScheme = nullptr); + void SetEnabled(bool enable = true, int player = 0, InputScheme* playerScheme = nullptr); /// /// Gets the currently active GUICollectionBox that acts as a dialog box and requires disabling navigation and drawing an overlay. /// /// Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! - GUICollectionBox * GetActiveDialogBox() const; + GUICollectionBox* GetActiveDialogBox() const; /// /// Gets whether this SettingsInputMappingWizardGUI needs to capture input for manual configuration. @@ -63,7 +62,7 @@ namespace RTE { /// /// The GUIEvent containing information about the player interaction with an element. /// Whether this SettingsInputMappingGUI changed the input scheme of the configuring player. - bool HandleInputEvents(GUIEvent &guiEvent); + bool HandleInputEvents(GUIEvent& guiEvent); /// /// Handles updating and progressing the manual input configuration sequence. @@ -72,42 +71,45 @@ namespace RTE { #pragma endregion private: - /// /// Enumeration for the different types of gamepads that can be configured. /// - enum GamepadType { DPad, AnalogDualShock, AnalogXbox }; + enum GamepadType { + DPad, + AnalogDualShock, + AnalogXbox + }; /// /// Struct containing GUI elements that compose the input mapping wizard manual configuration menu screen. /// struct WizardManualConfigScreen { - GUICollectionBox *ManualConfigBox; - GUILabel *ConfigDeviceTypeLabel; - GUILabel *ConfigStepDescriptionLabel; - GUILabel *ConfigStepRecommendedKeyLabel; - GUICollectionBox *GamepadConfigRecommendedBox; - GUILabel *GamepadConfigStepRecommendedInputLabel; - GUICollectionBox *GamepadConfigRecommendedDiagramBox; - GUILabel *ConfigStepLabel; - GUIButton *PrevConfigStepButton; - GUIButton *NextConfigStepButton; - GUIButton *ResetConfigButton; - GUIButton *DiscardOrApplyConfigButton; + GUICollectionBox* ManualConfigBox; + GUILabel* ConfigDeviceTypeLabel; + GUILabel* ConfigStepDescriptionLabel; + GUILabel* ConfigStepRecommendedKeyLabel; + GUICollectionBox* GamepadConfigRecommendedBox; + GUILabel* GamepadConfigStepRecommendedInputLabel; + GUICollectionBox* GamepadConfigRecommendedDiagramBox; + GUILabel* ConfigStepLabel; + GUIButton* PrevConfigStepButton; + GUIButton* NextConfigStepButton; + GUIButton* ResetConfigButton; + GUIButton* DiscardOrApplyConfigButton; }; /// /// Struct containing GUI elements that compose the input mapping wizard preset selection menu screen. /// struct WizardPresetSelectScreen { - GUICollectionBox *PresetSelectBox; - GUIButton *CloseWizardButton; - GUIButton *PresetSelectSNESButton; - GUIButton *PresetSelectDS4Button; - GUIButton *PresetSelectXB360Button; - GUIButton *StartConfigDPadTypeButton; - GUIButton *StartConfigAnalogDSTypeButton; - GUIButton *StartConfigAnalogXBTypeButton; + GUICollectionBox* PresetSelectBox; + GUIButton* CloseWizardButton; + GUIButton* PresetSelectSNESButton; + GUIButton* PresetSelectDS4Button; + GUIButton* PresetSelectXB360Button; + GUIButton* StartConfigDPadTypeButton; + GUIButton* StartConfigAnalogDSTypeButton; + GUIButton* StartConfigAnalogXBTypeButton; }; static constexpr int m_KeyboardConfigSteps = 16; //!< The step count for keyboard only manual configuration. @@ -115,10 +117,10 @@ namespace RTE { static constexpr int m_DPadConfigSteps = 12; //!< The step count for DPad type gamepad manual configuration. static constexpr int m_DualAnalogConfigSteps = 20; //!< The step count for DualAnalog type gamepad manual configuration. - GUIControlManager *m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. + GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. Players m_ConfiguringPlayer; //!< The player this SettingsInputMappingWizardGUI is configuring input mapping for. - InputScheme *m_ConfiguringPlayerScheme; //!< The InputScheme of the configuring player. + InputScheme* m_ConfiguringPlayerScheme; //!< The InputScheme of the configuring player. InputDevice m_ConfiguringDevice; //!< Which type of device we are currently configuring. bool m_ConfiguringDeviceIsGamepad; //!< Whether the device being configured is a gamepad of any type. @@ -135,9 +137,9 @@ namespace RTE { Timer m_BlinkTimer; //!< Timer for blinking the "Apply Changes" button and animating the recommended input diagram when configuring gamepads. - std::vector m_DPadDiagramBitmaps; //!< Vector containing all the D-Pad type gamepad recommended input diagram bitmaps. - std::vector m_DualAnalogDSDiagramBitmaps; //!< Vector containing all the DualShock type gamepad recommended input diagram bitmaps. - std::vector m_DualAnalogXBDiagramBitmaps; //!< Vector containing all the Xbox type gamepad recommended input diagram bitmaps. + std::vector m_DPadDiagramBitmaps; //!< Vector containing all the D-Pad type gamepad recommended input diagram bitmaps. + std::vector m_DualAnalogDSDiagramBitmaps; //!< Vector containing all the DualShock type gamepad recommended input diagram bitmaps. + std::vector m_DualAnalogXBDiagramBitmaps; //!< Vector containing all the Xbox type gamepad recommended input diagram bitmaps. WizardManualConfigScreen m_WizardManualConfigScreen; //!< The manual input configuration menu screen. WizardPresetSelectScreen m_WizardPresetSelectScreen; //!< The preset selection menu screen. @@ -145,8 +147,8 @@ namespace RTE { /// /// GUI elements that compose the input mapping wizard menu screen. /// - GUICollectionBox *m_InputWizardScreenBox; - GUILabel *m_InputWizardTitleLabel; + GUICollectionBox* m_InputWizardScreenBox; + GUILabel* m_InputWizardTitleLabel; #pragma region Create Breakdown /// @@ -192,13 +194,13 @@ namespace RTE { /// Handles the player interaction with the SettingsInputMappingWizardGUI's WizardManualConfigScreen GUI elements. /// /// The GUIEvent containing information about the player interaction with an element. - void HandleManualConfigScreenInputEvents(GUIEvent &guiEvent); + void HandleManualConfigScreenInputEvents(GUIEvent& guiEvent); /// /// Handles the player interaction with the SettingsInputMappingWizardGUI's WizardPresetSelectScreen GUI elements. /// /// The GUIEvent containing information about the player interaction with an element. - void HandlePresetSelectScreenInputEvents(GUIEvent &guiEvent); + void HandlePresetSelectScreenInputEvents(GUIEvent& guiEvent); #pragma endregion #pragma region Input Configuration Sequence Handling Breakdown @@ -238,8 +240,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - SettingsInputMappingWizardGUI(const SettingsInputMappingWizardGUI &reference) = delete; - SettingsInputMappingWizardGUI & operator=(const SettingsInputMappingWizardGUI &rhs) = delete; + SettingsInputMappingWizardGUI(const SettingsInputMappingWizardGUI& reference) = delete; + SettingsInputMappingWizardGUI& operator=(const SettingsInputMappingWizardGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/SettingsMiscGUI.cpp b/Source/Menus/SettingsMiscGUI.cpp index 8b3e43cc31..520f494e9a 100644 --- a/Source/Menus/SettingsMiscGUI.cpp +++ b/Source/Menus/SettingsMiscGUI.cpp @@ -11,50 +11,51 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsMiscGUI::SettingsMiscGUI(GUIControlManager *parentControlManager) : m_GUIControlManager(parentControlManager) { - m_MiscSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxMiscSettings")); + SettingsMiscGUI::SettingsMiscGUI(GUIControlManager* parentControlManager) : + m_GUIControlManager(parentControlManager) { + m_MiscSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxMiscSettings")); - m_SkipIntroCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxSkipIntro")); + m_SkipIntroCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxSkipIntro")); m_SkipIntroCheckbox->SetCheck(g_SettingsMan.SkipIntro()); - m_ShowToolTipsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxShowToolTips")); + m_ShowToolTipsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxShowToolTips")); m_ShowToolTipsCheckbox->SetCheck(g_SettingsMan.ShowToolTips()); - m_ShowLoadingScreenProgressReportCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxShowLoadingScreenProgressReport")); + m_ShowLoadingScreenProgressReportCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxShowLoadingScreenProgressReport")); m_ShowLoadingScreenProgressReportCheckbox->SetCheck(!g_SettingsMan.GetLoadingScreenProgressReportDisabled()); - m_ShowAdvancedPerfStatsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxShowAdvancedPerfStats")); + m_ShowAdvancedPerfStatsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxShowAdvancedPerfStats")); m_ShowAdvancedPerfStatsCheckbox->SetCheck(g_PerformanceMan.AdvancedPerformanceStatsEnabled()); - m_MeasureLoadTimeCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxMeasureLoadingTime")); + m_MeasureLoadTimeCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxMeasureLoadingTime")); m_MeasureLoadTimeCheckbox->SetCheck(g_SettingsMan.IsMeasuringModuleLoadTime()); - m_UseMonospaceConsoleFontCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxUseMonospaceConsoleFont")); + m_UseMonospaceConsoleFontCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxUseMonospaceConsoleFont")); m_UseMonospaceConsoleFontCheckbox->SetCheck(g_ConsoleMan.GetConsoleUseMonospaceFont()); - m_DisableFactionBuyMenuThemesCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxDisableFactionBuyMenuThemes")); + m_DisableFactionBuyMenuThemesCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxDisableFactionBuyMenuThemes")); m_DisableFactionBuyMenuThemesCheckbox->SetCheck(g_SettingsMan.FactionBuyMenuThemesDisabled()); - m_DisableFactionBuyMenuThemeCursorsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxDisableFactionBuyMenuThemeCursors")); + m_DisableFactionBuyMenuThemeCursorsCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxDisableFactionBuyMenuThemeCursors")); m_DisableFactionBuyMenuThemeCursorsCheckbox->SetCheck(g_SettingsMan.FactionBuyMenuThemeCursorsDisabled()); - m_SceneBackgroundAutoScaleLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneBackgroundAutoScaleSetting")); + m_SceneBackgroundAutoScaleLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneBackgroundAutoScaleSetting")); UpdateSceneBackgroundAutoScaleLabel(); - m_SceneBackgroundAutoScaleSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderSceneBackgroundAutoScale")); + m_SceneBackgroundAutoScaleSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderSceneBackgroundAutoScale")); m_SceneBackgroundAutoScaleSlider->SetValue(g_SettingsMan.GetSceneBackgroundAutoScaleMode()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsMiscGUI::SetEnabled(bool enable) const { m_MiscSettingsBox->SetVisible(enable); m_MiscSettingsBox->SetEnabled(enable); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsMiscGUI::UpdateSceneBackgroundAutoScaleLabel() { switch (g_SettingsMan.GetSceneBackgroundAutoScaleMode()) { @@ -70,9 +71,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsMiscGUI::HandleInputEvents(GUIEvent &guiEvent) { + void SettingsMiscGUI::HandleInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Notification) { if (guiEvent.GetControl() == m_SkipIntroCheckbox) { g_SettingsMan.SetSkipIntro(m_SkipIntroCheckbox->GetCheck()); @@ -96,4 +97,4 @@ namespace RTE { } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/SettingsMiscGUI.h b/Source/Menus/SettingsMiscGUI.h index f396db5a85..be88b35ba5 100644 --- a/Source/Menus/SettingsMiscGUI.h +++ b/Source/Menus/SettingsMiscGUI.h @@ -16,13 +16,12 @@ namespace RTE { class SettingsMiscGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a SettingsMiscGUI object in system memory and make it ready for use. /// /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsMiscGUI. Ownership is NOT transferred! - explicit SettingsMiscGUI(GUIControlManager *parentControlManager); + explicit SettingsMiscGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Concrete Methods @@ -36,27 +35,26 @@ namespace RTE { /// Handles the player interaction with the SettingsMiscGUI GUI elements. /// /// The GUIEvent containing information about the player interaction with an element. - void HandleInputEvents(GUIEvent &guiEvent); + void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: - - GUIControlManager *m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. + GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. /// /// GUI elements that compose the misc settings menu screen. /// - GUICollectionBox *m_MiscSettingsBox; - GUICheckbox *m_SkipIntroCheckbox; - GUICheckbox *m_ShowToolTipsCheckbox; - GUICheckbox *m_ShowLoadingScreenProgressReportCheckbox; - GUICheckbox *m_ShowAdvancedPerfStatsCheckbox; - GUICheckbox *m_MeasureLoadTimeCheckbox; - GUICheckbox *m_UseMonospaceConsoleFontCheckbox; - GUICheckbox *m_DisableFactionBuyMenuThemesCheckbox; - GUICheckbox *m_DisableFactionBuyMenuThemeCursorsCheckbox; - GUILabel *m_SceneBackgroundAutoScaleLabel; - GUISlider *m_SceneBackgroundAutoScaleSlider; + GUICollectionBox* m_MiscSettingsBox; + GUICheckbox* m_SkipIntroCheckbox; + GUICheckbox* m_ShowToolTipsCheckbox; + GUICheckbox* m_ShowLoadingScreenProgressReportCheckbox; + GUICheckbox* m_ShowAdvancedPerfStatsCheckbox; + GUICheckbox* m_MeasureLoadTimeCheckbox; + GUICheckbox* m_UseMonospaceConsoleFontCheckbox; + GUICheckbox* m_DisableFactionBuyMenuThemesCheckbox; + GUICheckbox* m_DisableFactionBuyMenuThemeCursorsCheckbox; + GUILabel* m_SceneBackgroundAutoScaleLabel; + GUISlider* m_SceneBackgroundAutoScaleSlider; #pragma region Misc Settings Handling /// @@ -66,8 +64,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - SettingsMiscGUI(const SettingsMiscGUI &reference) = delete; - SettingsMiscGUI & operator=(const SettingsMiscGUI &rhs) = delete; + SettingsMiscGUI(const SettingsMiscGUI& reference) = delete; + SettingsMiscGUI& operator=(const SettingsMiscGUI& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/Menus/SettingsVideoGUI.cpp b/Source/Menus/SettingsVideoGUI.cpp index 6b9391c8a7..24e123f9c0 100644 --- a/Source/Menus/SettingsVideoGUI.cpp +++ b/Source/Menus/SettingsVideoGUI.cpp @@ -26,48 +26,49 @@ namespace RTE { #if __cpp_lib_format >= 201907L return std::format("{}x{} ({:.1g}x Fullscreen scale)", Width, Height, Scale); #else - return std::to_string(Width) + "x" + std::to_string(Height); + return std::to_string(Width) + "x" + std::to_string(Height); #endif } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsVideoGUI::SettingsVideoGUI(GUIControlManager *parentControlManager) : m_GUIControlManager(parentControlManager) { + SettingsVideoGUI::SettingsVideoGUI(GUIControlManager* parentControlManager) : + m_GUIControlManager(parentControlManager) { m_NewResX = g_WindowMan.GetResX(); m_NewResY = g_WindowMan.GetResY(); m_NewResMultiplier = g_WindowMan.GetResMultiplier(); m_NewFullscreen = g_WindowMan.IsFullscreen(); - m_VideoSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxVideoSettings")); + m_VideoSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxVideoSettings")); - m_ResolutionQuickToggleButtons[ResolutionQuickChangeType::Windowed] = dynamic_cast(m_GUIControlManager->GetControl("ButtonQuickWindowed")); - m_ResolutionQuickToggleButtons[ResolutionQuickChangeType::Fullscreen] = dynamic_cast(m_GUIControlManager->GetControl("ButtonQuickBorderless")); + m_ResolutionQuickToggleButtons[ResolutionQuickChangeType::Windowed] = dynamic_cast(m_GUIControlManager->GetControl("ButtonQuickWindowed")); + m_ResolutionQuickToggleButtons[ResolutionQuickChangeType::Fullscreen] = dynamic_cast(m_GUIControlManager->GetControl("ButtonQuickBorderless")); - m_TwoPlayerSplitscreenHSplitRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioSplitscreenHoriz")); - m_TwoPlayerSplitscreenVSplitRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioSplitscreenVert")); + m_TwoPlayerSplitscreenHSplitRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioSplitscreenHoriz")); + m_TwoPlayerSplitscreenVSplitRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioSplitscreenVert")); m_TwoPlayerSplitscreenVSplitRadioButton->SetCheck(g_FrameMan.GetTwoPlayerVSplit()); - m_EnableVSyncCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxEnableVSync")); + m_EnableVSyncCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxEnableVSync")); m_EnableVSyncCheckbox->SetCheck(g_WindowMan.GetVSyncEnabled()); - m_FullscreenCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxFullscreen")); + m_FullscreenCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxFullscreen")); m_FullscreenCheckbox->SetCheck(m_NewFullscreen); - m_UseMultiDisplaysCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxUseMultiDisplays")); + m_UseMultiDisplaysCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxUseMultiDisplays")); m_UseMultiDisplaysCheckbox->SetCheck(g_WindowMan.GetUseMultiDisplays()); m_UseMultiDisplaysCheckbox->SetVisible(m_UseMultiDisplaysCheckbox->GetVisible() && SDL_GetNumVideoDisplays() > 1); - m_PresetResolutionRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioPresetResolution")); - m_CustomResolutionRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioCustomResolution")); + m_PresetResolutionRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioPresetResolution")); + m_CustomResolutionRadioButton = dynamic_cast(m_GUIControlManager->GetControl("RadioCustomResolution")); - m_ResolutionChangeDialogBox = dynamic_cast(m_GUIControlManager->GetControl("ResolutionChangeDialog")); + m_ResolutionChangeDialogBox = dynamic_cast(m_GUIControlManager->GetControl("ResolutionChangeDialog")); m_ResolutionChangeDialogBox->SetVisible(false); - const GUICollectionBox *settingsRootBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSettingsBase")); + const GUICollectionBox* settingsRootBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSettingsBase")); m_ResolutionChangeDialogBox->SetPositionAbs(settingsRootBox->GetXPos() + ((settingsRootBox->GetWidth() - m_ResolutionChangeDialogBox->GetWidth()) / 2), settingsRootBox->GetYPos() + ((settingsRootBox->GetHeight() - m_ResolutionChangeDialogBox->GetHeight()) / 2)); - m_ResolutionChangeConfirmButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfirmResolutionChange")); - m_ResolutionChangeCancelButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCancelResolutionChange")); + m_ResolutionChangeConfirmButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonConfirmResolutionChange")); + m_ResolutionChangeCancelButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCancelResolutionChange")); CreatePresetResolutionBox(); CreateCustomResolutionBox(); @@ -82,37 +83,37 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::CreatePresetResolutionBox() { - m_PresetResolutionBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionPresetResolution")); - m_PresetResolutionComboBox = dynamic_cast(m_GUIControlManager->GetControl("ComboPresetResolution")); - m_PresetResolutionApplyButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonApplyPresetResolution")); - m_PresetResolutionMessageLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelPresetResolutonValidation")); + m_PresetResolutionBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionPresetResolution")); + m_PresetResolutionComboBox = dynamic_cast(m_GUIControlManager->GetControl("ComboPresetResolution")); + m_PresetResolutionApplyButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonApplyPresetResolution")); + m_PresetResolutionMessageLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelPresetResolutonValidation")); m_PresetResolutionMessageLabel->SetVisible(false); PopulateResolutionsComboBox(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::CreateCustomResolutionBox() { - m_CustomResolutionBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionCustomResolution")); + m_CustomResolutionBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionCustomResolution")); m_CustomResolutionBox->SetVisible(false); - m_CustomResolutionWidthTextBox = dynamic_cast(m_GUIControlManager->GetControl("TextboxCustomWidth")); + m_CustomResolutionWidthTextBox = dynamic_cast(m_GUIControlManager->GetControl("TextboxCustomWidth")); m_CustomResolutionWidthTextBox->SetNumericOnly(true); m_CustomResolutionWidthTextBox->SetMaxNumericValue(g_WindowMan.GetMaxResX()); m_CustomResolutionWidthTextBox->SetMaxTextLength(4); m_CustomResolutionWidthTextBox->SetText(std::to_string(static_cast(g_WindowMan.GetResX()))); - m_CustomResolutionHeightTextBox = dynamic_cast(m_GUIControlManager->GetControl("TextboxCustomHeight")); + m_CustomResolutionHeightTextBox = dynamic_cast(m_GUIControlManager->GetControl("TextboxCustomHeight")); m_CustomResolutionHeightTextBox->SetNumericOnly(true); m_CustomResolutionHeightTextBox->SetMaxNumericValue(g_WindowMan.GetMaxResY()); m_CustomResolutionHeightTextBox->SetMaxTextLength(4); m_CustomResolutionHeightTextBox->SetText(std::to_string(static_cast(g_WindowMan.GetResY()))); - m_CustomResolutionMultiplierComboBox = dynamic_cast(m_GUIControlManager->GetControl("ComboboxResolutionMultiplier")); + m_CustomResolutionMultiplierComboBox = dynamic_cast(m_GUIControlManager->GetControl("ComboboxResolutionMultiplier")); PopulateResMultplierComboBox(); #if __cpp_lib_format >= 201907L m_CustomResolutionMultiplierComboBox->SetText(std::format("{:.3g}x", m_NewResMultiplier)); @@ -120,12 +121,12 @@ namespace RTE { m_CustomResolutionMultiplierComboBox->SetText(std::to_string(m_NewResMultiplier)); #endif - m_CustomResolutionApplyButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonApplyCustomResolution")); - m_CustomResolutionMessageLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelCustomResolutionValidation")); + m_CustomResolutionApplyButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonApplyCustomResolution")); + m_CustomResolutionMessageLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelCustomResolutionValidation")); m_CustomResolutionMessageLabel->SetVisible(false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::SetEnabled(bool enable) const { m_VideoSettingsBox->SetVisible(enable); @@ -146,13 +147,13 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICollectionBox * SettingsVideoGUI::GetActiveDialogBox() const { + GUICollectionBox* SettingsVideoGUI::GetActiveDialogBox() const { return (m_ResolutionChangeDialogBox->GetEnabled() && m_ResolutionChangeDialogBox->GetVisible()) ? m_ResolutionChangeDialogBox : nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::CloseActiveDialogBox() const { if (m_ResolutionChangeDialogBox->GetEnabled() && m_ResolutionChangeDialogBox->GetVisible()) { @@ -161,7 +162,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool SettingsVideoGUI::IsSupportedResolution(int width, int height) const { if ((width >= c_MinResX && height >= c_MinResY) && (width <= g_WindowMan.GetMaxResX() && height <= g_WindowMan.GetMaxResY())) { @@ -170,7 +171,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::PopulateResolutionsComboBox() { m_PresetResolutions.clear(); @@ -195,7 +196,7 @@ namespace RTE { float defaultScale = std::min(std::round(g_WindowMan.GetMaxResX() / static_cast(c_DefaultResX)), std::round(g_WindowMan.GetMaxResY() / static_cast(c_DefaultResY))); for (int i = 0; i < m_PresetResolutions.size(); ++i) { - const PresetResolutionRecord &resRecord = m_PresetResolutions[i]; + const PresetResolutionRecord& resRecord = m_PresetResolutions[i]; m_PresetResolutionComboBox->AddItem(resRecord.GetDisplayString()); if (m_PresetResolutionComboBox->GetSelectedIndex() < 0 && (glm::epsilonEqual(resRecord.Scale, defaultScale, 0.5f))) { m_PresetResolutionComboBox->SetSelectedIndex(i); @@ -220,7 +221,7 @@ namespace RTE { m_CustomResolutionMultiplierComboBox->SetSelectedIndex(0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::UpdateCustomResolutionLimits() { g_WindowMan.MapDisplays(); @@ -238,7 +239,7 @@ namespace RTE { m_CustomResolutionHeightTextBox->SetText(std::to_string(newMaxResY)); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::ApplyNewResolution(bool displaysWereMapped) { bool needWarning = (g_WindowMan.GetResX() != m_NewResX) && (g_WindowMan.GetResY() != m_NewResY); @@ -257,7 +258,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::ApplyQuickChangeResolution(ResolutionQuickChangeType resolutionChangeType) { g_WindowMan.MapDisplays(); @@ -297,7 +298,7 @@ namespace RTE { ApplyNewResolution(true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::ApplyPresetResolution() { int presetResListEntryID = m_PresetResolutionComboBox->GetSelectedIndex(); @@ -317,7 +318,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::ApplyCustomResolution() { m_CustomResolutionMessageLabel->SetVisible(false); @@ -349,7 +350,6 @@ namespace RTE { m_CustomResolutionHeightTextBox->SetText(std::to_string(m_NewResY)); } - bool invalidResolution = false; if (m_NewResX < c_MinResX || m_NewResY < c_MinResY) { m_CustomResolutionMessageLabel->SetText("Resolution width or height lower than the minimum (" + std::to_string(c_MinResX) + "x" + std::to_string(c_MinResY) + ") is not supported."); @@ -366,9 +366,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::HandleInputEvents(GUIEvent &guiEvent) { + void SettingsVideoGUI::HandleInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Command) { if (guiEvent.GetMsg() == GUIButton::Pushed) { if (guiEvent.GetControl() == m_ResolutionQuickToggleButtons[ResolutionQuickChangeType::Windowed]) { diff --git a/Source/Menus/SettingsVideoGUI.h b/Source/Menus/SettingsVideoGUI.h index fe2967d2b9..599ebd55d8 100644 --- a/Source/Menus/SettingsVideoGUI.h +++ b/Source/Menus/SettingsVideoGUI.h @@ -19,13 +19,12 @@ namespace RTE { class SettingsVideoGUI { public: - #pragma region Creation /// /// Constructor method used to instantiate a SettingsVideoGUI object in system memory and make it ready for use. /// /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsVideoGUI. Ownership is NOT transferred! - explicit SettingsVideoGUI(GUIControlManager *parentControlManager); + explicit SettingsVideoGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters @@ -39,7 +38,7 @@ namespace RTE { /// Gets the currently active GUICollectionBox that acts as a dialog box and requires disabling navigation and drawing an overlay. /// /// Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! - GUICollectionBox * GetActiveDialogBox() const; + GUICollectionBox* GetActiveDialogBox() const; /// /// Closes the currently active GUICollectionBox that acts as a dialog box by hiding it. If the active dialog box is a sub-menu, disables it. @@ -52,11 +51,10 @@ namespace RTE { /// Handles the player interaction with the SettingsVideoGUI GUI elements. /// /// The GUIEvent containing information about the player interaction with an element. - void HandleInputEvents(GUIEvent &guiEvent); + void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: - /// /// Enumeration for the different types of quick resolution change options. /// @@ -81,7 +79,8 @@ namespace RTE { /// Resolution width. /// Resolution height. /// Whether resolution is upscaled. - PresetResolutionRecord(int width, int height, float scale) : Width(width), Height(height), Scale(scale) {} + PresetResolutionRecord(int width, int height, float scale) : + Width(width), Height(height), Scale(scale) {} /// /// Makes UI displayable string with resolution info. @@ -94,7 +93,7 @@ namespace RTE { /// /// The PresetResolutionRecord to compare with. /// Bool with the result of the comparison. - bool operator<(const PresetResolutionRecord &rhs) const { + bool operator<(const PresetResolutionRecord& rhs) const { if (Width == rhs.Width && Height == rhs.Height) { return Scale > rhs.Scale; } else if (Width == rhs.Width) { @@ -104,40 +103,40 @@ namespace RTE { } }; - GUIControlManager *m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. + GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. std::vector m_PresetResolutions; //!< Contains PresetResolutionRecords for all the supported preset resolutions. int m_NewResX; //!< The new resolution width to use when changing resolution. int m_NewResY; //!< The new resolution height to use when changing resolution. float m_NewResMultiplier; //!< How much the new resolution should be upscaled when changing resolution. - bool m_NewFullscreen; //!< Whether the game will be windowed or fullscreen. + bool m_NewFullscreen; //!< Whether the game will be windowed or fullscreen. /// /// GUI elements that compose the video settings menu screen. /// - GUICollectionBox *m_VideoSettingsBox; - GUIRadioButton *m_TwoPlayerSplitscreenHSplitRadioButton; - GUIRadioButton *m_TwoPlayerSplitscreenVSplitRadioButton; - GUICheckbox *m_EnableVSyncCheckbox; - GUICheckbox *m_FullscreenCheckbox; - GUICheckbox *m_UseMultiDisplaysCheckbox; - GUIRadioButton *m_PresetResolutionRadioButton; - GUIRadioButton *m_CustomResolutionRadioButton; - GUICollectionBox *m_PresetResolutionBox; - GUIComboBox *m_PresetResolutionComboBox; - GUIButton *m_PresetResolutionApplyButton; - GUILabel *m_PresetResolutionMessageLabel; - GUICollectionBox *m_CustomResolutionBox; - GUITextBox *m_CustomResolutionWidthTextBox; - GUITextBox *m_CustomResolutionHeightTextBox; - GUIComboBox *m_CustomResolutionMultiplierComboBox; - GUILabel *m_CustomResolutionMessageLabel; - GUIButton *m_CustomResolutionApplyButton; - GUICollectionBox *m_ResolutionChangeDialogBox; - GUIButton *m_ResolutionChangeConfirmButton; - GUIButton *m_ResolutionChangeCancelButton; - std::array m_ResolutionQuickToggleButtons; + GUICollectionBox* m_VideoSettingsBox; + GUIRadioButton* m_TwoPlayerSplitscreenHSplitRadioButton; + GUIRadioButton* m_TwoPlayerSplitscreenVSplitRadioButton; + GUICheckbox* m_EnableVSyncCheckbox; + GUICheckbox* m_FullscreenCheckbox; + GUICheckbox* m_UseMultiDisplaysCheckbox; + GUIRadioButton* m_PresetResolutionRadioButton; + GUIRadioButton* m_CustomResolutionRadioButton; + GUICollectionBox* m_PresetResolutionBox; + GUIComboBox* m_PresetResolutionComboBox; + GUIButton* m_PresetResolutionApplyButton; + GUILabel* m_PresetResolutionMessageLabel; + GUICollectionBox* m_CustomResolutionBox; + GUITextBox* m_CustomResolutionWidthTextBox; + GUITextBox* m_CustomResolutionHeightTextBox; + GUIComboBox* m_CustomResolutionMultiplierComboBox; + GUILabel* m_CustomResolutionMessageLabel; + GUIButton* m_CustomResolutionApplyButton; + GUICollectionBox* m_ResolutionChangeDialogBox; + GUIButton* m_ResolutionChangeConfirmButton; + GUIButton* m_ResolutionChangeCancelButton; + std::array m_ResolutionQuickToggleButtons; #pragma region Create Breakdown /// @@ -199,8 +198,8 @@ namespace RTE { #pragma endregion // Disallow the use of some implicit methods. - SettingsVideoGUI(const SettingsVideoGUI &reference) = delete; - SettingsVideoGUI & operator=(const SettingsVideoGUI &rhs) = delete; + SettingsVideoGUI(const SettingsVideoGUI& reference) = delete; + SettingsVideoGUI& operator=(const SettingsVideoGUI& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/Menus/TitleScreen.cpp b/Source/Menus/TitleScreen.cpp index c9145b5036..645da4f5de 100644 --- a/Source/Menus/TitleScreen.cpp +++ b/Source/Menus/TitleScreen.cpp @@ -12,7 +12,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::Clear() { m_FadeAmount = 0; @@ -68,9 +68,9 @@ namespace RTE { m_IntroSlides.fill(nullptr); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::Create(AllegroScreen *guiScreen) { + void TitleScreen::Create(AllegroScreen* guiScreen) { m_TitleScreenMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); CreateTitleElements(); @@ -94,7 +94,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::CreateTitleElements() { m_DataRealmsLogo = ContentFile("Base.rte/GUIs/Title/Intro/DRLogo5x.png").GetAsBitmap(); @@ -115,13 +115,13 @@ namespace RTE { m_Nebula.SetScrollRatio(Vector(-1.0F, 1.0F / 3.0F)); int starSmallBitmapCount = 4; - std::vector starSmallBitmaps = ContentFile("Base.rte/GUIs/Title/Stars/StarSmall.png").GetAsAnimation(starSmallBitmapCount); + std::vector starSmallBitmaps = ContentFile("Base.rte/GUIs/Title/Stars/StarSmall.png").GetAsAnimation(starSmallBitmapCount); int starLargeBitmapCount = 1; - std::vector starLargeBitmaps = ContentFile("Base.rte/GUIs/Title/Stars/StarLarge.png").GetAsAnimation(starLargeBitmapCount); + std::vector starLargeBitmaps = ContentFile("Base.rte/GUIs/Title/Stars/StarLarge.png").GetAsAnimation(starLargeBitmapCount); int starHugeBitmapCount = 2; - std::vector starHugeBitmaps = ContentFile("Base.rte/GUIs/Title/Stars/StarHuge.png").GetAsAnimation(starHugeBitmapCount); + std::vector starHugeBitmaps = ContentFile("Base.rte/GUIs/Title/Stars/StarHuge.png").GetAsAnimation(starHugeBitmapCount); int starCount = (g_WindowMan.GetResX() * m_Nebula.GetBitmap()->h) / 1000; for (int i = 0; i < starCount; ++i) { @@ -145,7 +145,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::CreateIntroSequenceSlides() { std::string highRes = (g_WindowMan.GetResY() >= 680) ? "HD" : ""; @@ -154,15 +154,19 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::Update() { - if (m_SectionSwitch) { m_SectionTimer.Reset(); } + if (m_SectionSwitch) { + m_SectionTimer.Reset(); + } m_SectionElapsedTime = static_cast(m_SectionTimer.GetElapsedRealTimeS()); m_SectionProgress = std::min((m_SectionDuration > 0) ? m_SectionElapsedTime / m_SectionDuration : 0, 0.9999F); // Checking for 0.999 instead of 0.9999 or 1.0 here otherwise there is a hiccup between ending and starting a new orbit cycle. - if (m_StationOrbitProgress >= 0.999F) { m_StationOrbitTimer.Reset(); } + if (m_StationOrbitProgress >= 0.999F) { + m_StationOrbitTimer.Reset(); + } m_StationOrbitProgress = std::clamp(static_cast(m_StationOrbitTimer.GetElapsedRealTimeS()) / 60.0F, 0.0F, 0.9999F); m_StationOrbitRotation = LERP(0, 1.0F, c_PI, -c_PI, m_StationOrbitProgress); @@ -181,7 +185,9 @@ namespace RTE { m_ScrollOffset.SetY(LERP(0, 1.0F, m_IntroScrollStartOffsetY, m_GameLogoAppearScrollOffsetY, introScrollProgress)); UpdateIntroSlideshowSequence(g_UInputMan.AnyStartPress()); } else if (m_IntroSequenceState >= IntroSequence::GameLogoAppear && m_IntroSequenceState <= IntroSequence::MainMenuAppear) { - if (m_IntroSequenceState < IntroSequence::PreMainMenu) { m_ScrollOffset.SetY(EaseOut(m_GameLogoAppearScrollOffsetY, m_PreMainMenuScrollOffsetY, introScrollProgress)); } + if (m_IntroSequenceState < IntroSequence::PreMainMenu) { + m_ScrollOffset.SetY(EaseOut(m_GameLogoAppearScrollOffsetY, m_PreMainMenuScrollOffsetY, introScrollProgress)); + } UpdateIntroPreMainMenuSequence(); } if (m_SectionElapsedTime >= m_SectionDuration) { @@ -194,7 +200,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::UpdateIntroLogoSequence(bool skipSection) { if (skipSection && m_IntroSequenceState != IntroSequence::FmodLogoFadeOut) { @@ -218,21 +224,31 @@ namespace RTE { m_FadeAmount = static_cast(LERP(0, 1.0F, 255.0F, 0, m_SectionProgress)); break; case IntroSequence::DataRealmsLogoDisplay: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(2.0F); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(2.0F); + } break; case IntroSequence::DataRealmsLogoFadeOut: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(0.25F); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(0.25F); + } m_FadeAmount = static_cast(LERP(0, 1.0F, 0, 255.0F, m_SectionProgress)); break; case IntroSequence::FmodLogoFadeIn: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(0.25F); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(0.25F); + } m_FadeAmount = static_cast(LERP(0, 1.0F, 255.0F, 0, m_SectionProgress)); break; case IntroSequence::FmodLogoDisplay: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(2.0F); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(2.0F); + } break; case IntroSequence::FmodLogoFadeOut: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(0.5F); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(0.5F); + } m_FadeAmount = static_cast(LERP(0, 1.0F, 0, 255.0F, m_SectionProgress)); break; default: @@ -240,7 +256,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::UpdateIntroSlideshowSequence(bool skipSlideshow) { if (skipSlideshow && (m_IntroSequenceState > IntroSequence::SlideshowFadeIn && m_IntroSequenceState != IntroSequence::MainMenuAppear)) { @@ -267,7 +283,9 @@ namespace RTE { m_FadeAmount = static_cast(LERP(0, 1.0F, 255.0F, 0, m_SectionProgress)); break; case IntroSequence::PreSlideshowPause: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(3.2F - static_cast(m_IntroSongTimer.GetElapsedRealTimeS())); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(3.2F - static_cast(m_IntroSongTimer.GetElapsedRealTimeS())); + } break; case IntroSequence::ShowSlide1: if (m_SectionSwitch) { @@ -275,7 +293,9 @@ namespace RTE { m_SlideFadeInDuration = 2.0F; m_SlideFadeOutDuration = 0.5F; } - if (m_SectionElapsedTime > 1.25F) { m_SlideshowSlideText = "At the end of humanity's darkest century..."; } + if (m_SectionElapsedTime > 1.25F) { + m_SlideshowSlideText = "At the end of humanity's darkest century..."; + } break; case IntroSequence::ShowSlide2: if (m_SectionSwitch) { @@ -283,7 +303,9 @@ namespace RTE { m_SlideFadeInDuration = 0.5F; m_SlideFadeOutDuration = 2.5F; } - if (m_SectionElapsedTime < m_SectionDuration - 1.75F) { m_SlideshowSlideText = "...a curious symbiosis between man and machine emerged."; } + if (m_SectionElapsedTime < m_SectionDuration - 1.75F) { + m_SlideshowSlideText = "...a curious symbiosis between man and machine emerged."; + } break; case IntroSequence::ShowSlide3: if (m_SectionSwitch) { @@ -354,7 +376,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::UpdateIntroPreMainMenuSequence() { switch (m_IntroSequenceState) { @@ -373,7 +395,9 @@ namespace RTE { SetSectionDurationAndResetSwitch(92.4F - static_cast(m_IntroSongTimer.GetElapsedRealTimeS())); clear_to_color(g_FrameMan.GetOverlayBitmap32(), 0); } - if (m_SectionProgress > 0.5F) { m_GameLogo.SetPos(Vector(static_cast(m_TitleScreenMaxWidth / 2), EaseIn((static_cast(g_WindowMan.GetResY() / 2)) - 20, 120, (m_SectionProgress - 0.5F) / 0.5F))); } + if (m_SectionProgress > 0.5F) { + m_GameLogo.SetPos(Vector(static_cast(m_TitleScreenMaxWidth / 2), EaseIn((static_cast(g_WindowMan.GetResY() / 2)) - 20, 120, (m_SectionProgress - 0.5F) / 0.5F))); + } break; case IntroSequence::PreMainMenu: if (m_SectionSwitch) { @@ -399,7 +423,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::UpdateTitleTransitions() { static const float endDelay = 0.2F * g_SettingsMan.GetMenuTransitionDurationMultiplier(); @@ -428,7 +452,9 @@ namespace RTE { } m_ScrollOffset.SetY(EaseOut(0, m_PlanetViewScrollOffsetY, m_SectionProgress)); m_GameLogo.SetPos(Vector(static_cast(m_TitleScreenMaxWidth / 2), EaseOut(m_GameLogoMainMenuOffsetY, m_GameLogoPlanetViewOffsetY, m_SectionProgress))); - if (m_SectionElapsedTime >= m_SectionDuration) { SetTitleTransitionState((m_TitleTransitionState == TitleTransition::MainMenuToScenario) ? TitleTransition::ScenarioMenu : TitleTransition::MetaGameMenu); } + if (m_SectionElapsedTime >= m_SectionDuration) { + SetTitleTransitionState((m_TitleTransitionState == TitleTransition::MainMenuToScenario) ? TitleTransition::ScenarioMenu : TitleTransition::MetaGameMenu); + } break; case TitleTransition::PlanetToMainMenu: if (m_SectionSwitch) { @@ -437,18 +463,26 @@ namespace RTE { } m_ScrollOffset.SetY(EaseOut(m_PlanetViewScrollOffsetY, 0, m_SectionProgress)); m_GameLogo.SetPos(Vector(static_cast(m_TitleScreenMaxWidth / 2), EaseOut(m_GameLogoPlanetViewOffsetY, m_GameLogoMainMenuOffsetY, m_SectionProgress))); - if (m_SectionElapsedTime >= m_SectionDuration) { SetTitleTransitionState(TitleTransition::MainMenu); } + if (m_SectionElapsedTime >= m_SectionDuration) { + SetTitleTransitionState(TitleTransition::MainMenu); + } break; case TitleTransition::MainMenuToCredits: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(1.0F * g_SettingsMan.GetMenuTransitionDurationMultiplier()); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(1.0F * g_SettingsMan.GetMenuTransitionDurationMultiplier()); + } m_ScrollOffset.SetY(EaseOut(0, m_PlanetViewScrollOffsetY, m_SectionProgress)); m_FadeAmount = static_cast(EaseOut(0, 128.0F, m_SectionProgress)); break; case TitleTransition::CreditsToMainMenu: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(1.0F * g_SettingsMan.GetMenuTransitionDurationMultiplier()); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(1.0F * g_SettingsMan.GetMenuTransitionDurationMultiplier()); + } m_ScrollOffset.SetY(EaseOut(m_PlanetViewScrollOffsetY, 0, m_SectionProgress)); m_FadeAmount = static_cast(EaseOut(128.0F, 0, m_SectionProgress)); - if (m_SectionElapsedTime >= m_SectionDuration) { SetTitleTransitionState(TitleTransition::MainMenu); } + if (m_SectionElapsedTime >= m_SectionDuration) { + SetTitleTransitionState(TitleTransition::MainMenu); + } break; case TitleTransition::ScenarioFadeIn: case TitleTransition::MetaGameFadeIn: @@ -461,13 +495,19 @@ namespace RTE { } g_AudioMan.SetTempMusicVolume(EaseOut(0, 1.0F, m_SectionProgress)); m_FadeAmount = static_cast(LERP(0, 1.0F, 255.0F, 0, m_SectionProgress)); - if (m_SectionElapsedTime >= m_SectionDuration) { SetTitleTransitionState((m_TitleTransitionState == TitleTransition::ScenarioFadeIn) ? TitleTransition::ScenarioMenu : TitleTransition::MetaGameMenu); } + if (m_SectionElapsedTime >= m_SectionDuration) { + SetTitleTransitionState((m_TitleTransitionState == TitleTransition::ScenarioFadeIn) ? TitleTransition::ScenarioMenu : TitleTransition::MetaGameMenu); + } break; case TitleTransition::FadeOut: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(0.75F * g_SettingsMan.GetMenuTransitionDurationMultiplier()); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(0.75F * g_SettingsMan.GetMenuTransitionDurationMultiplier()); + } g_AudioMan.SetTempMusicVolume(EaseIn(1.0F, 0, m_SectionProgress)); m_FadeAmount = static_cast(EaseIn(0, 255, m_SectionProgress)); - if (m_SectionElapsedTime >= (m_SectionDuration + endDelay)) { SetTitleTransitionState(TitleTransition::TransitionEnd); } + if (m_SectionElapsedTime >= (m_SectionDuration + endDelay)) { + SetTitleTransitionState(TitleTransition::TransitionEnd); + } break; case TitleTransition::ScrollingFadeIn: if (m_SectionSwitch) { @@ -479,16 +519,22 @@ namespace RTE { m_ScrollOffset.SetY(EaseOut(250, 0, m_SectionProgress)); m_GameLogo.SetPos(Vector(static_cast(m_TitleScreenMaxWidth / 2), EaseOut(m_GameLogoPlanetViewOffsetY, m_GameLogoMainMenuOffsetY, m_SectionProgress))); m_FadeAmount = static_cast(EaseOut(255, 0, m_SectionProgress)); - if (m_SectionElapsedTime >= m_SectionDuration) { SetTitleTransitionState(TitleTransition::MainMenu); } + if (m_SectionElapsedTime >= m_SectionDuration) { + SetTitleTransitionState(TitleTransition::MainMenu); + } break; case TitleTransition::ScrollingFadeOut: case TitleTransition::ScrollingFadeOutQuit: - if (m_SectionSwitch) { SetSectionDurationAndResetSwitch(0.75F * g_SettingsMan.GetMenuTransitionDurationMultiplier()); } + if (m_SectionSwitch) { + SetSectionDurationAndResetSwitch(0.75F * g_SettingsMan.GetMenuTransitionDurationMultiplier()); + } g_AudioMan.SetTempMusicVolume(EaseIn(1.0F, 0, m_SectionProgress)); m_ScrollOffset.SetY(EaseIn(0, 250, m_SectionProgress)); m_GameLogo.SetPos(Vector(static_cast(m_TitleScreenMaxWidth / 2), EaseIn(m_GameLogoMainMenuOffsetY, m_GameLogoPlanetViewOffsetY, m_SectionProgress))); m_FadeAmount = static_cast(EaseIn(0, 255, m_SectionProgress)); - if (m_SectionElapsedTime >= (m_SectionDuration + endDelay)) { SetTitleTransitionState((m_TitleTransitionState == TitleTransition::ScrollingFadeOutQuit) ? TitleTransition::TransitionEndQuit : TitleTransition::TransitionEnd); } + if (m_SectionElapsedTime >= (m_SectionDuration + endDelay)) { + SetTitleTransitionState((m_TitleTransitionState == TitleTransition::ScrollingFadeOutQuit) ? TitleTransition::TransitionEndQuit : TitleTransition::TransitionEnd); + } break; case TitleTransition::TransitionEnd: if (m_SectionSwitch) { @@ -501,12 +547,16 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::Draw() { if (!m_FinishedPlayingIntro) { - if (m_IntroSequenceState >= IntroSequence::SlideshowFadeIn) { DrawTitleScreenScene(); } - if (m_IntroSequenceState >= IntroSequence::GameLogoAppear) { DrawGameLogo(); } + if (m_IntroSequenceState >= IntroSequence::SlideshowFadeIn) { + DrawTitleScreenScene(); + } + if (m_IntroSequenceState >= IntroSequence::GameLogoAppear) { + DrawGameLogo(); + } if (m_IntroSequenceState >= IntroSequence::DataRealmsLogoFadeIn && m_IntroSequenceState <= IntroSequence::DataRealmsLogoFadeOut) { draw_sprite(g_FrameMan.GetBackBuffer32(), m_DataRealmsLogo, (m_TitleScreenMaxWidth - m_DataRealmsLogo->w) / 2, (g_WindowMan.GetResY() - m_DataRealmsLogo->h) / 2); @@ -531,28 +581,32 @@ namespace RTE { // In credits have to draw the overlay before the game logo otherwise drawing the game logo again on top of an existing one causes the glow effect to look wonky. if (m_TitleTransitionState == TitleTransition::MainMenuToCredits || m_TitleTransitionState == TitleTransition::CreditsToMainMenu) { - if (m_FadeAmount > 0) { DrawOverlayEffectBitmap(); } + if (m_FadeAmount > 0) { + DrawOverlayEffectBitmap(); + } DrawGameLogo(); return; } DrawGameLogo(); } - if (m_FadeAmount > 0) { DrawOverlayEffectBitmap(); } + if (m_FadeAmount > 0) { + DrawOverlayEffectBitmap(); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::DrawTitleScreenScene() { // This only needs to be done once, but bitmaps can be reloaded which effectively undoes this, so just do it all the time to not deal with flags and checks. set_write_alpha_blender(); draw_trans_sprite(m_Planet.GetSpriteFrame(0), ContentFile("Base.rte/GUIs/Title/PlanetAlpha.png").GetAsBitmap(), 0, 0); draw_trans_sprite(m_Moon.GetSpriteFrame(0), ContentFile("Base.rte/GUIs/Title/MoonAlpha.png").GetAsBitmap(), 0, 0); - + Box nebulaTargetBox; m_Nebula.SetOffset(Vector(static_cast((m_TitleScreenMaxWidth - m_Nebula.GetBitmap()->w) / 2), m_ScrollOffset.GetY())); m_Nebula.Draw(g_FrameMan.GetBackBuffer32(), nebulaTargetBox, true); - for (const Star &star : m_BackdropStars) { + for (const Star& star: m_BackdropStars) { int intensity = star.Intensity + RandomNum(0, (star.Size == Star::StarSize::StarSmall) ? 35 : 70); set_screen_blender(intensity, intensity, intensity, intensity); int starPosY = static_cast(star.Position.GetY() - (m_ScrollOffset.GetY() * (m_Nebula.GetScrollRatio().GetY() * ((star.Size == Star::StarSize::StarSmall) ? 0.8F : 1.0F)))); @@ -572,7 +626,7 @@ namespace RTE { m_Station.Draw(g_FrameMan.GetBackBuffer32()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::DrawGameLogo() { m_GameLogo.Draw(g_FrameMan.GetBackBuffer32()); @@ -582,7 +636,7 @@ namespace RTE { m_GameLogoGlow.Draw(g_FrameMan.GetBackBuffer32(), Vector(), DrawMode::g_DrawTrans); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::DrawSlideshowSlide() { int slide = static_cast(m_IntroSequenceState) - static_cast(IntroSequence::ShowSlide1); @@ -614,10 +668,10 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::DrawOverlayEffectBitmap() const { set_trans_blender(m_FadeAmount, m_FadeAmount, m_FadeAmount, m_FadeAmount); draw_trans_sprite(g_FrameMan.GetBackBuffer32(), g_FrameMan.GetOverlayBitmap32(), 0, 0); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/Menus/TitleScreen.h b/Source/Menus/TitleScreen.h index caca20ca76..94f5f5fd5e 100644 --- a/Source/Menus/TitleScreen.h +++ b/Source/Menus/TitleScreen.h @@ -18,7 +18,6 @@ namespace RTE { class TitleScreen { public: - /// /// Enumeration for the different transition (scrolling) states of the title screen. /// @@ -48,13 +47,16 @@ namespace RTE { /// Constructor method used to instantiate a TitleScreen object in system memory and make it ready for use. /// /// Pointer to a GUIScreen interface that will be used to create this TitleScreen's GUIFont. Ownership is NOT transferred! - explicit TitleScreen(AllegroScreen *guiScreen) { Clear(); Create(guiScreen); } + explicit TitleScreen(AllegroScreen* guiScreen) { + Clear(); + Create(guiScreen); + } /// /// Makes the TitleScreen object ready for use. /// /// Pointer to a GUIScreen interface that will be used to create this TitleScreen's GUIFont. Ownership is NOT transferred! - void Create(AllegroScreen *guiScreen); + void Create(AllegroScreen* guiScreen); #pragma endregion #pragma region Getters and Setters @@ -68,13 +70,22 @@ namespace RTE { /// Sets the target title transition state and, if different from the current, sets the section switch to trigger the transition. /// /// The target title transition state. - void SetTitleTransitionState(TitleTransition newTransitionState) { if (newTransitionState != m_TitleTransitionState) { m_TitleTransitionState = newTransitionState; m_SectionSwitch = true; } } + void SetTitleTransitionState(TitleTransition newTransitionState) { + if (newTransitionState != m_TitleTransitionState) { + m_TitleTransitionState = newTransitionState; + m_SectionSwitch = true; + } + } /// /// Sets the title transition to a pending state, stores the orbit timer elapsed time and resets the fade screen blend value. /// This is used to correctly restart transition states after breaking out of the game loop back to the menu loop. /// - void SetTitlePendingTransition() { m_TitleTransitionState = TitleTransition::TransitionPending; m_StationOrbitTimerElapsedTime = static_cast(m_StationOrbitTimer.GetElapsedRealTimeS()); m_FadeAmount = 0; } + void SetTitlePendingTransition() { + m_TitleTransitionState = TitleTransition::TransitionPending; + m_StationOrbitTimerElapsedTime = static_cast(m_StationOrbitTimer.GetElapsedRealTimeS()); + m_FadeAmount = 0; + } /// /// Gets the position of the planet on the title screen scene. @@ -108,7 +119,6 @@ namespace RTE { #pragma endregion private: - /// /// Enumeration for the different states of the intro sequence. /// @@ -143,10 +153,14 @@ namespace RTE { /// /// Enumeration for the different Star sizes. /// - enum class StarSize { StarSmall, StarLarge, StarHuge }; + enum class StarSize { + StarSmall, + StarLarge, + StarHuge + }; StarSize Size; //!< The size of the Star. Used for the appropriate Bitmap selection and Intensity randomization when drawing. - BITMAP *Bitmap; //!< The bitmap to draw, not owned by this. Not Owned. + BITMAP* Bitmap; //!< The bitmap to draw, not owned by this. Not Owned. int Intensity; //!< Intensity value on a scale from 0 to 255. Vector Position; //!< The position of the Star on the title screen scene backdrop. }; @@ -196,11 +210,11 @@ namespace RTE { float m_SlideFadeOutDuration; //!< How many seconds the duration of a slideshow slide fade out is supposed to elapse. std::unique_ptr m_IntroTextFont; //!< The GUIFont used for drawing text during the logo splash screens and slideshow. std::string m_SlideshowSlideText; //!< String containing the slide text during each section of the slideshow. - BITMAP *m_DataRealmsLogo; //!< The DataRealms logo bitmap used in the logo splash screen. Not Owned. - BITMAP *m_FmodLogo; //!< The Fmod logo bitmap used in the logo splash screen. Not Owned. + BITMAP* m_DataRealmsLogo; //!< The DataRealms logo bitmap used in the logo splash screen. Not Owned. + BITMAP* m_FmodLogo; //!< The Fmod logo bitmap used in the logo splash screen. Not Owned. MOSParticle m_PreGameLogoText; //!< The pre-game logo text that appears at the end of the slideshow. MOSParticle m_PreGameLogoTextGlow; //!< The pre-game logo text glow. - std::array m_IntroSlides; //!< Array that contains all the slideshow slide bitmaps. Not Owned. + std::array m_IntroSlides; //!< Array that contains all the slideshow slide bitmaps. Not Owned. #pragma region Create Breakdown /// @@ -219,7 +233,10 @@ namespace RTE { /// Sets the duration of a new section and resets the switch. /// /// The duration of the new section, in seconds. - void SetSectionDurationAndResetSwitch(float newDuration) { m_SectionDuration = newDuration; m_SectionSwitch = false; } + void SetSectionDurationAndResetSwitch(float newDuration) { + m_SectionDuration = newDuration; + m_SectionSwitch = false; + } /// /// Updates the title screen transition states and scrolls the title screen scene accordingly. @@ -272,8 +289,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - TitleScreen(const TitleScreen &reference) = delete; - TitleScreen & operator=(const TitleScreen &rhs) = delete; + TitleScreen(const TitleScreen& reference) = delete; + TitleScreen& operator=(const TitleScreen& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/AllegroTools.cpp b/Source/System/AllegroTools.cpp index 1a429df5d1..756cc77bb6 100644 --- a/Source/System/AllegroTools.cpp +++ b/Source/System/AllegroTools.cpp @@ -12,7 +12,9 @@ namespace RTE { n = geta32(x); - if (n){ n++; } + if (n) { + n++; + } res = ((x & 0xFF00FF) - (y & 0xFF00FF)) * n / 256 + y; y &= 0xFF00; diff --git a/Source/System/AllegroTools.h b/Source/System/AllegroTools.h index a714c44cd9..07e037301e 100644 --- a/Source/System/AllegroTools.h +++ b/Source/System/AllegroTools.h @@ -6,18 +6,18 @@ /// Note: Prefer fixing in allegro itself over adding hacks here. /// namespace RTE { - #pragma region True Alpha Blending +#pragma region True Alpha Blending - /// - /// Workaround for allegro's missing true alpha blender, use instead of set_alpha_blender when alpha values are desired or necessary after draw: - /// ``` set_blender_mode_ex(_blender_black, _blender_black, _blender_black, TrueAlphaBlender, _blender_black, _blender_black, _blender_black, 0, 0, 0, 0);``` - /// - unsigned long TrueAlphaBlender(unsigned long x, unsigned long y, unsigned long n); - /// - /// Sets the 32bit allegro blender mode to TrueAlphaBlender - /// - void SetTrueAlphaBlender(); - #pragma endregion -} + /// + /// Workaround for allegro's missing true alpha blender, use instead of set_alpha_blender when alpha values are desired or necessary after draw: + /// ``` set_blender_mode_ex(_blender_black, _blender_black, _blender_black, TrueAlphaBlender, _blender_black, _blender_black, _blender_black, 0, 0, 0, 0);``` + /// + unsigned long TrueAlphaBlender(unsigned long x, unsigned long y, unsigned long n); + /// + /// Sets the 32bit allegro blender mode to TrueAlphaBlender + /// + void SetTrueAlphaBlender(); +#pragma endregion +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Atom.cpp b/Source/System/Atom.cpp index 0d567f8169..0c09a68e20 100644 --- a/Source/System/Atom.cpp +++ b/Source/System/Atom.cpp @@ -14,14 +14,14 @@ namespace RTE { const std::string Atom::c_ClassName = "Atom"; std::mutex Atom::s_MemoryPoolMutex; - std::vector Atom::s_AllocatedPool; + std::vector Atom::s_AllocatedPool; int Atom::s_PoolAllocBlockCount = 200; int Atom::s_InstancesInUse = 0; // This forms a circle around the Atom's offset center, to check for mask color pixels in order to determine the normal at the Atom's position. - const int Atom::s_NormalChecks[c_NormalCheckCount][2] = { {0, -3}, {1, -3}, {2, -2}, {3, -1}, {3, 0}, {3, 1}, {2, 2}, {1, 3}, {0, 3}, {-1, 3}, {-2, 2}, {-3, 1}, {-3, 0}, {-3, -1}, {-2, -2}, {-1, -3} }; + const int Atom::s_NormalChecks[c_NormalCheckCount][2] = {{0, -3}, {1, -3}, {2, -2}, {3, -1}, {3, 0}, {3, 1}, {2, 2}, {1, 3}, {0, 3}, {-1, 3}, {-2, 2}, {-3, 1}, {-3, 0}, {-3, -1}, {-2, -2}, {-1, -3}}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Atom::Clear() { m_Offset.Reset(); @@ -58,13 +58,13 @@ namespace RTE { // While an AtomGroup is travelling, the OnCollideWithTerrain Lua function can run, which will in turn force Create to run if it hasn't already. // If this Create function adds to an AtomGroup (e.g. adds an Attachable to it), there will be problems. // Setting these values in Clear doesn't help if Atoms are removed at this point, but helps if Atoms are added, since these values mean the added Atoms won't try to step forwards. - //m_Dom = 0; - //m_Delta[m_Dom] = 0; + // m_Dom = 0; + // m_Delta[m_Dom] = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::Create(const Vector &offset, Material const *material, MovableObject *owner, Color trailColor, int trailLength) { + int Atom::Create(const Vector& offset, Material const* material, MovableObject* owner, Color trailColor, int trailLength) { m_Offset = m_OriginalOffset = offset; // Use the offset as normal for now m_Normal = m_Offset; @@ -77,9 +77,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::Create(const Atom &reference) { + int Atom::Create(const Atom& reference) { m_Offset = reference.m_Offset; m_OriginalOffset = reference.m_OriginalOffset; m_Normal = reference.m_Normal; @@ -96,11 +96,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::ReadProperty(const std::string_view &propName, Reader &reader) { + int Atom::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("Offset", { reader >> m_Offset; }); MatchProperty("OriginalOffset", { reader >> m_OriginalOffset; }); MatchProperty("Material", { @@ -108,19 +108,20 @@ namespace RTE { mat.Reset(); reader >> mat; m_Material = g_SceneMan.AddMaterialCopy(&mat); - if (!m_Material) { RTEAbort("Failed to store material \"" + mat.GetPresetName() + "\". Aborting!"); } + if (!m_Material) { + RTEAbort("Failed to store material \"" + mat.GetPresetName() + "\". Aborting!"); + } }); MatchProperty("TrailColor", { reader >> m_TrailColor; }); MatchProperty("TrailLength", { reader >> m_TrailLength; }); MatchProperty("TrailLengthVariation", { reader >> m_TrailLengthVariation; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::Save(Writer &writer) const { + int Atom::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("Offset", m_Offset); @@ -133,16 +134,18 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void * Atom::GetPoolMemory() { + void* Atom::GetPoolMemory() { std::lock_guard guard(s_MemoryPoolMutex); // If the pool is empty, then fill it up again with as many instances as we are set to - if (s_AllocatedPool.empty()) { FillPool((s_PoolAllocBlockCount > 0) ? s_PoolAllocBlockCount : 10); } + if (s_AllocatedPool.empty()) { + FillPool((s_PoolAllocBlockCount > 0) ? s_PoolAllocBlockCount : 10); + } // Get the instance in the top of the pool and pop it off - void *foundMemory = s_AllocatedPool.back(); + void* foundMemory = s_AllocatedPool.back(); s_AllocatedPool.pop_back(); RTEAssert(foundMemory, "Could not find an available instance in the pool, even after increasing its size!"); @@ -153,11 +156,13 @@ namespace RTE { return foundMemory; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Atom::FillPool(int fillAmount) { // Default to the set block allocation size if fillAmount is 0 - if (fillAmount <= 0) { fillAmount = s_PoolAllocBlockCount; } + if (fillAmount <= 0) { + fillAmount = s_PoolAllocBlockCount; + } // If concrete class, fill up the pool with pre-allocated memory blocks the size of the type if (fillAmount > 0) { @@ -168,9 +173,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::ReturnPoolMemory(void *returnedMemory) { + int Atom::ReturnPoolMemory(void* returnedMemory) { if (!returnedMemory) { return false; } @@ -184,9 +189,9 @@ namespace RTE { return s_InstancesInUse; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Atom::CalculateNormal(BITMAP *sprite, Vector spriteCenter) { + bool Atom::CalculateNormal(BITMAP* sprite, Vector spriteCenter) { RTEAssert(sprite, "Trying to set up Atom normal without passing in bitmap"); // Can't set up a normal on an atom that doesn't have an offset from its parent's center @@ -217,22 +222,22 @@ namespace RTE { // Check whether the normal vector makes sense at all. It can't point against the offset, for example if (m_Normal.Dot(m_Offset) < 0) { - // Abort and revert to offset-based normal - m_Normal = m_Offset; - m_Normal.Normalize(); - return false; + // Abort and revert to offset-based normal + m_Normal = m_Offset; + m_Normal.Normalize(); + return false; } */ return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Atom::IsIgnoringMOID(MOID whichMOID) { if (whichMOID == m_IgnoreMOID) { return true; } - const MovableObject *hitMO = g_MovableMan.GetMOFromID(whichMOID); + const MovableObject* hitMO = g_MovableMan.GetMOFromID(whichMOID); hitMO = hitMO ? hitMO->GetRootParent() : 0; // First check if we are ignoring the team of the MO we hit, or if it's an AtomGroup and we're ignoring all those @@ -240,7 +245,7 @@ namespace RTE { if (m_OwnerMO->IgnoresTeamHits() && hitMO->IgnoresTeamHits() && m_OwnerMO->GetTeam() == hitMO->GetTeam()) { return true; } - if ((m_OwnerMO->IgnoresAtomGroupHits() && dynamic_cast(hitMO)) || (hitMO->IgnoresAtomGroupHits() && dynamic_cast(m_OwnerMO))) { + if ((m_OwnerMO->IgnoresAtomGroupHits() && dynamic_cast(hitMO)) || (hitMO->IgnoresAtomGroupHits() && dynamic_cast(m_OwnerMO))) { return true; } if ((m_OwnerMO->GetIgnoresActorHits() && dynamic_cast(hitMO)) || (hitMO->GetIgnoresActorHits() && dynamic_cast(m_OwnerMO))) { @@ -249,7 +254,7 @@ namespace RTE { } // Now check for explicit ignore bool ignored = false; - for (const MOID &moid : m_IgnoreMOIDs) { + for (const MOID& moid: m_IgnoreMOIDs) { if (moid == whichMOID) { ignored = true; break; @@ -257,7 +262,7 @@ namespace RTE { } // Check in AtomGroup-owned list if it's assigned to this atom if (!ignored && m_IgnoreMOIDsByGroup) { - for (const MOID &moid : *m_IgnoreMOIDsByGroup) { + for (const MOID& moid: *m_IgnoreMOIDsByGroup) { if (moid == whichMOID) { ignored = true; break; @@ -267,7 +272,7 @@ namespace RTE { return ignored; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Atom::MOHitResponse() { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -306,7 +311,9 @@ namespace RTE { } m_LastHit.BitmapNormal.Normalize(); - if (!m_Normal.IsZero()) { m_LastHit.BitmapNormal = -m_OwnerMO->RotateOffset(m_Normal); } + if (!m_Normal.IsZero()) { + m_LastHit.BitmapNormal = -m_OwnerMO->RotateOffset(m_Normal); + } // Cancel collision response for this if it appears the collision is happening in the 'wrong' direction, meaning away from the center. // This happens when things are sunk into each other, and thus getting 'hooked' on each other @@ -324,8 +331,12 @@ namespace RTE { #endif // Get the roots for both bodies - if (m_LastHit.Body[HITOR]) { m_LastHit.RootBody[HITOR] = m_LastHit.Body[HITOR]->GetRootParent(); } - if (m_LastHit.Body[HITEE]) { m_LastHit.RootBody[HITEE] = m_LastHit.Body[HITEE]->GetRootParent(); } + if (m_LastHit.Body[HITOR]) { + m_LastHit.RootBody[HITOR] = m_LastHit.Body[HITOR]->GetRootParent(); + } + if (m_LastHit.Body[HITEE]) { + m_LastHit.RootBody[HITEE] = m_LastHit.Body[HITEE]->GetRootParent(); + } validHit = validHit && m_LastHit.Body[HITEE]->CollideAtPoint(m_LastHit); @@ -335,9 +346,9 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - HitData & Atom::TerrHitResponse() { + HitData& Atom::TerrHitResponse() { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); if (m_TerrainMatHit) { @@ -345,9 +356,9 @@ namespace RTE { MID domMaterialID = g_MaterialAir; MID subMaterialID = g_MaterialAir; m_LastHit.HitMaterial[HITOR] = m_Material; - Material const *hitMaterial = m_LastHit.HitMaterial[HITEE] = g_SceneMan.GetMaterialFromID(hitMaterialID); - Material const *domMaterial = g_SceneMan.GetMaterialFromID(g_MaterialAir); - Material const *subMaterial = g_SceneMan.GetMaterialFromID(g_MaterialAir); + Material const* hitMaterial = m_LastHit.HitMaterial[HITEE] = g_SceneMan.GetMaterialFromID(hitMaterialID); + Material const* domMaterial = g_SceneMan.GetMaterialFromID(g_MaterialAir); + Material const* subMaterial = g_SceneMan.GetMaterialFromID(g_MaterialAir); bool hit[2]; hit[X] = hit[Y] = false; m_LastHit.BitmapNormal.Reset(); @@ -411,7 +422,7 @@ namespace RTE { return m_LastHit; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Atom::SetupPos(Vector startPos) { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -429,7 +440,9 @@ namespace RTE { if ((m_TerrainMatHit = g_SceneMan.GetTerrMatter(m_IntPos[X], m_IntPos[Y])) != g_MaterialAir) { m_OwnerMO->SetHitWhatTerrMaterial(m_TerrainMatHit); - if (m_OwnerMO->IntersectionWarning()) { m_TerrainHitsDisabled = true; } + if (m_OwnerMO->IntersectionWarning()) { + m_TerrainHitsDisabled = true; + } } else { m_TerrainHitsDisabled = false; } @@ -437,7 +450,7 @@ namespace RTE { return m_MOIDHit != g_NoMOID || m_TerrainMatHit != g_MaterialAir; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Atom::SetupSeg(Vector startPos, Vector trajectory, float stepRatio) { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -492,7 +505,7 @@ namespace RTE { return m_Delta[m_Dom] - m_DomSteps; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Atom::StepForward(int numSteps) { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -507,7 +520,9 @@ namespace RTE { if (m_DomSteps < m_Delta[m_Dom]) { ++m_DomSteps; - if (m_SubStepped) { ++m_SubSteps; } + if (m_SubStepped) { + ++m_SubSteps; + } m_SubStepped = false; m_IntPos[m_Dom] += m_Increment[m_Dom]; @@ -516,13 +531,13 @@ namespace RTE { m_SubStepped = true; m_Error -= m_Delta2[m_Dom]; } - //if (m_ChangedDir){ - m_Error += m_Delta2[m_Sub]; + // if (m_ChangedDir){ + m_Error += m_Delta2[m_Sub]; //} else { - //m_Error = m_PrevError; + // m_Error = m_PrevError; //} - // Scene wrapping, if necessary + // Scene wrapping, if necessary g_SceneMan.WrapPosition(m_IntPos[X], m_IntPos[Y]); // Detect terrain hits, if not disabled. @@ -544,7 +559,9 @@ namespace RTE { // Detect hits with non-ignored MO's, if enabled. if (m_OwnerMO->m_HitsMOs) { m_MOIDHit = g_SceneMan.GetMOIDPixel(m_IntPos[X], m_IntPos[Y], m_OwnerMO->GetTeam()); - if (IsIgnoringMOID(m_MOIDHit)) { m_MOIDHit = g_NoMOID; } + if (IsIgnoringMOID(m_MOIDHit)) { + m_MOIDHit = g_NoMOID; + } if (m_MOIDHit != g_NoMOID) { if (!m_MOHitsDisabled) { @@ -564,7 +581,7 @@ namespace RTE { if (m_OwnerMO) { abortString += "\nRoot owner is " + m_OwnerMO->GetPresetName() + "."; if (m_SubgroupID != 0) { - const MovableObject *realOwner = g_MovableMan.FindObjectByUniqueID(m_SubgroupID); + const MovableObject* realOwner = g_MovableMan.FindObjectByUniqueID(m_SubgroupID); abortString += " Owner is " + realOwner->GetPresetName() + "."; } } @@ -576,7 +593,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Atom::StepBack() { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -601,7 +618,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Atom::Travel(float travelTime, bool autoTravel, bool scenePreLocked) { ZoneScoped; @@ -610,14 +627,14 @@ namespace RTE { RTEAbort("Traveling an Atom without a parent MO!"); return travelTime; } - Vector &position = m_OwnerMO->m_Pos; - Vector &velocity = m_OwnerMO->m_Vel; + Vector& position = m_OwnerMO->m_Pos; + Vector& velocity = m_OwnerMO->m_Vel; float mass = m_OwnerMO->GetMass(); float sharpness = m_OwnerMO->GetSharpness(); - bool &didWrap = m_OwnerMO->m_DidWrap; + bool& didWrap = m_OwnerMO->m_DidWrap; m_LastHit.Reset(); - BITMAP *trailBitmap = 0; + BITMAP* trailBitmap = 0; int hitCount = 0; int error = 0; @@ -639,15 +656,15 @@ namespace RTE { bool hit[2]; bool sinkHit; bool subStepped; - //bool endOfTraj = false; + // bool endOfTraj = false; - const Material *hitMaterial = 0; //g_SceneMan.GetMaterialFromID(g_MaterialAir); + const Material* hitMaterial = 0; // g_SceneMan.GetMaterialFromID(g_MaterialAir); unsigned char hitMaterialID = 0; - const Material *domMaterial = 0; //g_SceneMan.GetMaterialFromID(g_MaterialAir); + const Material* domMaterial = 0; // g_SceneMan.GetMaterialFromID(g_MaterialAir); unsigned char domMaterialID = 0; - const Material *subMaterial = 0; //g_SceneMan.GetMaterialFromID(g_MaterialAir); + const Material* subMaterial = 0; // g_SceneMan.GetMaterialFromID(g_MaterialAir); unsigned char subMaterialID = 0; Vector segTraj; @@ -667,7 +684,9 @@ namespace RTE { position += m_Offset; // Lock all bitmaps involved outside the loop. - if (!scenePreLocked) { g_SceneMan.LockScene(); } + if (!scenePreLocked) { + g_SceneMan.LockScene(); + } // Loop for all the different straight segments (between bounces etc) that have to be traveled during the timeLeft. do { @@ -677,7 +696,7 @@ namespace RTE { // Get trail bitmap and put first pixel. if (m_TrailLength) { trailBitmap = g_SceneMan.GetMOColorBitmap(); - trailPoints.push_back({ intPos[X], intPos[Y] }); + trailPoints.push_back({intPos[X], intPos[Y]}); } // Compute and scale the actual on-screen travel trajectory for this segment, based on the velocity, the travel time and the pixels-per-meter constant. segTraj = velocity * timeLeft * c_PPM; @@ -686,14 +705,14 @@ namespace RTE { delta[Y] = std::floor(position.m_Y + segTraj.m_Y) - intPos[Y]; RTEAssert(std::abs(delta[X]) < 2500 && std::abs(delta[Y] < 2500), "Extremely long difference trajectory found during Atom::Travel. Owner is " + m_OwnerMO->GetPresetName() + ", with Vel (" + std::to_string(velocity.GetX()) + ", " + std::to_string(velocity.GetY()) + ")."); - //segProgress = 0.0F; - //delta2[X] = 0; - //delta2[Y] = 0; - //increment[X] = 0; - //increment[Y] = 0; + // segProgress = 0.0F; + // delta2[X] = 0; + // delta2[Y] = 0; + // increment[X] = 0; + // increment[Y] = 0; hit[X] = false; hit[Y] = false; - //domSteps = 0; + // domSteps = 0; subSteps = 0; subStepped = false; sinkHit = false; @@ -703,9 +722,9 @@ namespace RTE { break; } - //HitMaterial->Reset(); - //domMaterial->Reset(); - //subMaterial->Reset(); + // HitMaterial->Reset(); + // domMaterial->Reset(); + // subMaterial->Reset(); // Bresenham's line drawing algorithm preparation if (delta[X] < 0) { @@ -742,18 +761,20 @@ namespace RTE { ++hitCount; hit[X] = hit[Y] = true; if (g_SceneMan.TryPenetrate(intPos[X], intPos[Y], velocity * mass * sharpness, velocity, retardation, 0.5F, m_NumPenetrations, removeOrphansRadius, removeOrphansMaxArea, removeOrphansRate)) { - //segProgress = 0.0F; + // segProgress = 0.0F; velocity += velocity * retardation; continue; } else { - //segProgress = 1.0F; + // segProgress = 1.0F; velocity.SetXY(0, 0); timeLeft = 0.0F; break; } } - if (subStepped) { ++subSteps; } + if (subStepped) { + ++subSteps; + } subStepped = false; intPos[dom] += increment[dom]; @@ -780,7 +801,9 @@ namespace RTE { // Back up so the Atom is not inside the MO. intPos[dom] -= increment[dom]; - if (subStepped) { intPos[sub] -= increment[sub]; } + if (subStepped) { + intPos[sub] -= increment[sub]; + } m_LastHit.Reset(); m_LastHit.TotalMass[HITOR] = mass; @@ -788,12 +811,14 @@ namespace RTE { m_LastHit.MomInertia[HITOR] = 1.0F; m_LastHit.ImpulseFactor[HITOR] = 1.0F; m_LastHit.ImpulseFactor[HITEE] = 1.0F; - //m_LastHit.HitPoint = Vector(hitPos[X], hitPos[Y]); + // m_LastHit.HitPoint = Vector(hitPos[X], hitPos[Y]); m_LastHit.HitVel[HITOR] = velocity; - MovableObject *MO = g_MovableMan.GetMOFromID(m_MOIDHit); + MovableObject* MO = g_MovableMan.GetMOFromID(m_MOIDHit); - if (MO) { MO->SetHitWhatParticleUniqueID(m_OwnerMO->GetUniqueID()); } + if (MO) { + MO->SetHitWhatParticleUniqueID(m_OwnerMO->GetUniqueID()); + } m_LastHit.Body[HITOR] = m_OwnerMO; m_LastHit.Body[HITEE] = MO; @@ -805,13 +830,13 @@ namespace RTE { // Don't do this normal approximation based on object centers, it causes particles to 'slide into' sprite objects when they should be resting on them. // Orthogonal normals only, as the pixel boundaries themselves! See further down for the setting of this. - //m_LastHit.BitmapNormal = m_LastHit.Body[HITOR]->GetPos() - m_LastHit.Body[HITEE]->GetPos(); - //m_LastHit.BitmapNormal.Normalize(); + // m_LastHit.BitmapNormal = m_LastHit.Body[HITOR]->GetPos() - m_LastHit.Body[HITEE]->GetPos(); + // m_LastHit.BitmapNormal.Normalize(); // Gold special collection case! // TODO: Make material IDs more robust!") if (m_Material->GetIndex() == c_GoldMaterialID && g_MovableMan.IsOfActor(m_MOIDHit)) { - if (Actor *actor = dynamic_cast(g_MovableMan.GetMOFromID(m_LastHit.Body[HITEE]->GetRootID())); actor && !actor->IsDead()) { + if (Actor* actor = dynamic_cast(g_MovableMan.GetMOFromID(m_LastHit.Body[HITEE]->GetRootID())); actor && !actor->IsDead()) { actor->AddGold(m_OwnerMO->GetMass() * g_SceneMan.GetOzPerKg() * removeOrphansRadius ? 1.25F : 1.0F); m_OwnerMO->SetToDelete(true); // This is to break out of the do-while and the function properly. @@ -869,7 +894,9 @@ namespace RTE { // If there was no MO collision detected, then check for terrain hits. else if ((hitMaterialID = g_SceneMan.GetTerrMatter(intPos[X], intPos[Y])) && !m_OwnerMO->m_IgnoreTerrain) { - if (hitMaterialID != g_MaterialAir) { m_OwnerMO->SetHitWhatTerrMaterial(hitMaterialID); } + if (hitMaterialID != g_MaterialAir) { + m_OwnerMO->SetHitWhatTerrMaterial(hitMaterialID); + } hitMaterial = g_SceneMan.GetMaterialFromID(hitMaterialID); hitPos[X] = intPos[X]; @@ -877,7 +904,9 @@ namespace RTE { ++hitCount; #ifdef DEBUG_BUILD - if (m_TrailLength) { putpixel(trailBitmap, intPos[X], intPos[Y], 199); } + if (m_TrailLength) { + putpixel(trailBitmap, intPos[X], intPos[Y], 199); + } #endif // Try penetration of the terrain. if (hitMaterial->GetIndex() != g_MaterialOutOfBounds && g_SceneMan.TryPenetrate(intPos[X], intPos[Y], velocity * mass * sharpness, velocity, retardation, 0.65F, m_NumPenetrations, removeOrphansRadius, removeOrphansMaxArea, removeOrphansRate)) { @@ -896,14 +925,16 @@ namespace RTE { // Back up so the Atom is not inside the terrain. intPos[dom] -= increment[dom]; - if (subStepped) { intPos[sub] -= increment[sub]; } + if (subStepped) { + intPos[sub] -= increment[sub]; + } // Undo scene wrapping, if necessary g_SceneMan.WrapPosition(intPos[X], intPos[Y]); // Check if particle is sticky and should adhere to where it collided if (!m_OwnerMO->IsMissionCritical() && velocity.MagnitudeIsGreaterThan(1.0F)) { - MOPixel *ownerMOAsPixel = dynamic_cast(m_OwnerMO); + MOPixel* ownerMOAsPixel = dynamic_cast(m_OwnerMO); if (RandomNum() < std::max(m_Material->GetStickiness(), ownerMOAsPixel ? ownerMOAsPixel->GetStaininess() : 0.0f)) { // Weighted random select between stickiness or staininess const float randomChoice = RandomNum(0.0f, m_Material->GetStickiness() + (ownerMOAsPixel ? ownerMOAsPixel->GetStaininess() : 0.0f)); @@ -913,7 +944,7 @@ namespace RTE { m_OwnerMO->SetToDelete(true); m_LastHit.Terminate[HITOR] = hit[dom] = hit[sub] = true; break; - } else if (MOPixel *ownerMOAsPixel = dynamic_cast(m_OwnerMO); ownerMOAsPixel && randomChoice <= m_Material->GetStickiness() + ownerMOAsPixel->GetStaininess()) { + } else if (MOPixel* ownerMOAsPixel = dynamic_cast(m_OwnerMO); ownerMOAsPixel && randomChoice <= m_Material->GetStickiness() + ownerMOAsPixel->GetStaininess()) { Vector stickPos(intPos[X], intPos[Y]); stickPos += velocity * (c_PPM * g_TimerMan.GetDeltaTimeSecs()) * RandomNum(); int terrainMaterialID = g_SceneMan.GetTerrain()->GetMaterialPixel(stickPos.GetFloorIntX(), stickPos.GetFloorIntY()); @@ -953,7 +984,7 @@ namespace RTE { // If hit right on the corner of a pixel, bounce straight back with no friction. if (!hit[dom] && !hit[sub]) { hit[dom] = true; - hitAccel[dom] = -velocity[dom] - velocity[dom] * m_Material->GetRestitution() * hitMaterial->GetRestitution(); + hitAccel[dom] = -velocity[dom] - velocity[dom] * m_Material->GetRestitution() * hitMaterial->GetRestitution(); hit[sub] = true; hitAccel[sub] = -velocity[sub] - velocity[sub] * m_Material->GetRestitution() * hitMaterial->GetRestitution(); } else if (hit[dom] && !hit[sub]) { @@ -964,7 +995,7 @@ namespace RTE { } } } else if (m_TrailLength) { - trailPoints.push_back({ intPos[X], intPos[Y] }); + trailPoints.push_back({intPos[X], intPos[Y]}); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -974,14 +1005,14 @@ namespace RTE { if ((hit[X] || hit[Y]) && !m_LastHit.Terminate[HITOR]) { // Calculate the progress made on this segment before hitting something. // We count the hitting step made if it resulted in a terrain sink, because the Atoms weren't stepped back out of intersection. - //segProgress = static_cast(domSteps + sinkHit) / static_cast(delta[dom]); + // segProgress = static_cast(domSteps + sinkHit) / static_cast(delta[dom]); segProgress = (static_cast(domSteps + static_cast(sinkHit)) < delta[dom]) ? (static_cast(domSteps + static_cast(sinkHit)) / std::fabs(static_cast(segTraj[dom]))) : 1.0F; // Now calculate the total time left to travel, according to the progress made. timeLeft -= timeLeft * segProgress; // Move position forward to the hit position. - //position += segTraj * segProgress; + // position += segTraj * segProgress; // Only move the dom forward by int domSteps, so we don't cross into a pixel too far position[dom] += (domSteps + static_cast(sinkHit)) * increment[dom]; @@ -1002,7 +1033,7 @@ namespace RTE { } } while ((hit[X] || hit[Y]) && /* !segTraj.GetFloored().IsZero() && */ hitCount < 100 && !m_LastHit.Terminate[HITOR]); - //RTEAssert(hitCount < 100, "Atom travel resulted in more than 100 segments!!"); + // RTEAssert(hitCount < 100, "Atom travel resulted in more than 100 segments!!"); // Draw the trail if (g_TimerMan.DrawnSimUpdate() && m_TrailLength && trailPoints.size() > 0) { @@ -1023,14 +1054,18 @@ namespace RTE { } // Unlock all bitmaps involved. - //if (m_TrailLength) { trailBitmap->UnLock(); } - if (!scenePreLocked) { g_SceneMan.UnlockScene(); } + // if (m_TrailLength) { trailBitmap->UnLock(); } + if (!scenePreLocked) { + g_SceneMan.UnlockScene(); + } // Extract Atom offset. position -= m_Offset; // Travel along the remaining segTraj. - if (!(hit[X] || hit[Y]) && autoTravel) { position += segTraj; } + if (!(hit[X] || hit[Y]) && autoTravel) { + position += segTraj; + } didWrap = g_SceneMan.WrapPosition(position) || didWrap; @@ -1039,7 +1074,7 @@ namespace RTE { return hitCount; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void HitData::Clear() { HitPoint.Reset(); @@ -1063,9 +1098,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - HitData & HitData::operator=(const HitData &rhs) { + HitData& HitData::operator=(const HitData& rhs) { if (this == &rhs) { return *this; } @@ -1092,4 +1127,4 @@ namespace RTE { } return *this; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/Atom.h b/Source/System/Atom.h index 66bb976fe5..7c826aad1f 100644 --- a/Source/System/Atom.h +++ b/Source/System/Atom.h @@ -10,7 +10,10 @@ namespace RTE { class SLTerrain; class MovableObject; - enum { HITOR = 0, HITEE = 1 }; + enum { + HITOR = 0, + HITEE = 1 + }; #pragma region HitData /// @@ -18,12 +21,12 @@ namespace RTE { /// struct HitData { - MovableObject *Body[2]; //!< Pointers to the two hitting bodies. If hitee is 0, that means collision with terrain. The HitData struct doesn't own these. - MovableObject *RootBody[2]; //!< Pointers to root parents of the two hitting bodies. If hitee is 0, that means collision with terrain. The HitData struct doesn't own these. + MovableObject* Body[2]; //!< Pointers to the two hitting bodies. If hitee is 0, that means collision with terrain. The HitData struct doesn't own these. + MovableObject* RootBody[2]; //!< Pointers to root parents of the two hitting bodies. If hitee is 0, that means collision with terrain. The HitData struct doesn't own these. Vector BitmapNormal; //!< The approximated normal vector of the bitmap that the hitee is presenting to the hittor at the collision point. The inverse of this is the one representing the hittor bitmap. - const Material *HitMaterial[2]; //!< The material of the respective bodies at the hit point. + const Material* HitMaterial[2]; //!< The material of the respective bodies at the hit point. float TotalMass[2]; //!< Total mass of each body. float MomInertia[2]; //!< Moment of inertia. If 0, assume to be a point mass. @@ -56,7 +59,7 @@ namespace RTE { /// /// A HitData reference. /// A reference to the changed HitData. - HitData & operator=(const HitData &rhs); + HitData& operator=(const HitData& rhs); /// /// Clears all the member variables of this HitData, effectively resetting the members of this abstraction level only. @@ -71,7 +74,6 @@ namespace RTE { class Atom : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -85,7 +87,12 @@ namespace RTE { /// Copy constructor method used to instantiate an Atom object identical to an already existing one. /// /// An Atom object which is passed in by reference. - Atom(const Atom &reference) { if (this != &reference) { Clear(); Create(reference); } } + Atom(const Atom& reference) { + if (this != &reference) { + Clear(); + Create(reference); + } + } /// /// Convenience constructor to both instantiate an Atom in memory and Create it at the same time. @@ -95,7 +102,10 @@ namespace RTE { /// The owner MovableObject of this Atom. Ownership is NOT transferred! /// The trail color. /// The trail length. If 0, no trail will be drawn. - Atom(const Vector &offset, Material const *material, MovableObject *owner, Color trailColor = Color(), int trailLength = 0) { Clear(); Create(offset, material, owner, trailColor, trailLength); } + Atom(const Vector& offset, Material const* material, MovableObject* owner, Color trailColor = Color(), int trailLength = 0) { + Clear(); + Create(offset, material, owner, trailColor, trailLength); + } /// /// Convenience constructor to both instantiate an Atom in memory and Create it at the same time. @@ -105,14 +115,17 @@ namespace RTE { /// The owner MovableObject of this Atom. Ownership is NOT transferred! /// The trail color. /// The trail length. If 0, no trail will be drawn. - Atom(const Vector &offset, unsigned char materialID, MovableObject *owner, Color trailColor = Color(), int trailLength = 0) { Clear(); Create(offset, g_SceneMan.GetMaterialFromID(materialID), owner, trailColor, trailLength); } + Atom(const Vector& offset, unsigned char materialID, MovableObject* owner, Color trailColor = Color(), int trailLength = 0) { + Clear(); + Create(offset, g_SceneMan.GetMaterialFromID(materialID), owner, trailColor, trailLength); + } /// /// Creates an Atom to be identical to another, by deep copy. /// /// A reference to the Atom to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Atom &reference); + int Create(const Atom& reference); /// /// Makes the Atom object ready for use. @@ -123,7 +136,7 @@ namespace RTE { /// The trail color. /// The trail length. If 0, no trail will be drawn. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Vector &offset, Material const *material, MovableObject *owner, Color trailColor = Color(), int trailLength = 0); + int Create(const Vector& offset, Material const* material, MovableObject* owner, Color trailColor = Color(), int trailLength = 0); #pragma endregion #pragma region Destruction @@ -143,7 +156,7 @@ namespace RTE { /// Grabs from the pre-allocated pool, an available chunk of memory the exact size of an Atom. OWNERSHIP IS TRANSFERRED! /// /// A pointer to the pre-allocated pool memory. OWNERSHIP IS TRANSFERRED! - static void * GetPoolMemory(); + static void* GetPoolMemory(); /// /// Adds a certain number of newly allocated instances to this' pool. @@ -156,7 +169,7 @@ namespace RTE { /// /// The raw chunk of memory that is being returned. Needs to be the same size as an Atom. OWNERSHIP IS TRANSFERRED! /// The count of outstanding memory chunks after this was returned. - static int ReturnPoolMemory(void *returnedMemory); + static int ReturnPoolMemory(void* returnedMemory); #pragma endregion #pragma region Getters and Setters @@ -164,13 +177,13 @@ namespace RTE { /// Gets the current owner MovableObject of this AtomGroup. /// /// A const pointer to the owner. - const MovableObject * GetOwner() const { return m_OwnerMO; } + const MovableObject* GetOwner() const { return m_OwnerMO; } /// /// Sets the current owner MovableObject of this AtomGroup. /// /// A pointer to the new owner. Ownership is NOT transferred! - void SetOwner(MovableObject *newOwner) { m_OwnerMO = newOwner; } + void SetOwner(MovableObject* newOwner) { m_OwnerMO = newOwner; } /// /// Gets the group ID of this Atom. @@ -188,13 +201,13 @@ namespace RTE { /// Gets the material of this Atom. /// /// The material of this Atom. - Material const * GetMaterial() const { return m_Material ? m_Material : g_SceneMan.GetMaterialFromID(g_MaterialAir); } + Material const* GetMaterial() const { return m_Material ? m_Material : g_SceneMan.GetMaterialFromID(g_MaterialAir); } /// /// Sets the material of this Atom. /// /// The new material of this Atom. - void SetMaterial(const Material *newMat) { m_Material = newMat; } + void SetMaterial(const Material* newMat) { m_Material = newMat; } /// /// Gets the Color of this Atom's trail. @@ -236,25 +249,25 @@ namespace RTE { /// Gets the offset vector that was first set for this Atom. The GetOffset may have additional offsets baked into it if this is part of an group. /// /// The original offset Vector. - const Vector & GetOriginalOffset() const { return m_OriginalOffset; } + const Vector& GetOriginalOffset() const { return m_OriginalOffset; } /// /// Gets the offset vector. /// /// The current offset Vector. - const Vector & GetOffset() const { return m_Offset; } + const Vector& GetOffset() const { return m_Offset; } /// /// Sets a new offset vector for the collision calculations. /// /// A const reference to a Vector that will be used as offset. - void SetOffset(const Vector &newOffset) { m_Offset = newOffset; } + void SetOffset(const Vector& newOffset) { m_Offset = newOffset; } /// /// Gets the surface normal of this vector, if it has been successfully calculated. If not, it'll be a 0 vector. /// /// The current normalized surface normal Vector of this. - const Vector & GetNormal() const { return m_Normal; } + const Vector& GetNormal() const { return m_Normal; } #pragma endregion #pragma region Concrete Methods @@ -265,7 +278,7 @@ namespace RTE { /// The bitmap to check against. Ownership IS NOT transferred! /// Where on the bitmap the center of the object is. This atom's offset will be applied automatically before checking for its normal. /// Whether normal was successfully derived from the bitmap. If not, then a provisional one is derived from the offset. - bool CalculateNormal(BITMAP *sprite, Vector spriteCenter); + bool CalculateNormal(BITMAP* sprite, Vector spriteCenter); #pragma endregion #pragma region Collision @@ -273,13 +286,13 @@ namespace RTE { /// Gets the stored data struct on the last collision experienced by this Atom. /// /// The Vector describing the velocity of this Atom at the last time it hit something. - HitData & GetHitData() { return m_LastHit; } + HitData& GetHitData() { return m_LastHit; } /// /// Sets the HitData struct this Atom uses to represent the last hit it experienced. /// /// A reference to a HitData struct that will be copied to the Atom's. - void SetHitData(const HitData &newHitData) { m_LastHit = newHitData; } + void SetHitData(const HitData& newHitData) { m_LastHit = newHitData; } /// /// Checks whether this Atom is set to ignore collisions with the terrain. @@ -310,7 +323,7 @@ namespace RTE { /// AtomGroup may set this shared list of ignored MOIDs to avoid setting and removing ignored MOIDs for every atom one by one. The list is maintained only by AtomGroup, Atom never owns it. /// /// New MOIDs list to ignore. - void SetIgnoreMOIDsByGroup(std::vector const * ignoreMOIDsByGroup) { m_IgnoreMOIDsByGroup = ignoreMOIDsByGroup; }; + void SetIgnoreMOIDsByGroup(std::vector const* ignoreMOIDsByGroup) { m_IgnoreMOIDsByGroup = ignoreMOIDsByGroup; }; /// /// Clear the list of MOIDs that this Atom is set to ignore collisions with during its next travel sequence. @@ -352,7 +365,7 @@ namespace RTE { /// /// Collision data should already be set by SetHitData(). /// The resulting HitData of this Atom with all the information about the collision filled out. - HitData & TerrHitResponse(); + HitData& TerrHitResponse(); #pragma endregion #pragma region Travel @@ -447,28 +460,33 @@ namespace RTE { /// /// /// - static void *operator new (size_t size) { return Atom::GetPoolMemory(); } + static void* operator new(size_t size) { return Atom::GetPoolMemory(); } /// /// Delete operator overload for the pool deallocation of Atoms. /// /// - static void operator delete (void *instance) { Atom::ReturnPoolMemory(instance); } + static void operator delete(void* instance) { Atom::ReturnPoolMemory(instance); } /// /// An assignment operator for setting one Atom equal to another. /// /// An Atom reference. /// A reference to the changed Atom. - Atom & operator=(const Atom &rhs) { if (this != &rhs) { Destroy(); Create(rhs); } return *this; } + Atom& operator=(const Atom& rhs) { + if (this != &rhs) { + Destroy(); + Create(rhs); + } + return *this; + } #pragma endregion protected: - static constexpr int c_NormalCheckCount = 16; //!< Array size for offsets to form circle in s_NormalChecks. static std::mutex s_MemoryPoolMutex; - static std::vector s_AllocatedPool; //!< Pool of pre-allocated Atoms. + static std::vector s_AllocatedPool; //!< Pool of pre-allocated Atoms. static int s_PoolAllocBlockCount; //!< The number of instances to fill up the pool of Atoms with each time it runs dry. static int s_InstancesInUse; //!< The number of allocated instances passed out from the pool. static const int s_NormalChecks[c_NormalCheckCount][2]; //!< This forms a circle around the Atom's offset center, to check for key color pixels in order to determine the normal at the Atom's position. @@ -476,7 +494,7 @@ namespace RTE { Vector m_Offset; //!< The offset of this Atom for collision calculations. Vector m_OriginalOffset; //!< This offset is before altering the m_Offset for use in composite groups. Vector m_Normal; //!< The current normalized surface normal Vector of this Atom. - Material const * m_Material; //!< The material this Atom is made of. + Material const* m_Material; //!< The material this Atom is made of. int m_SubgroupID; //!< Identifying ID for adding and removing atoms from AtomGroups. bool m_StepWasTaken; //!< Whether the last call to StepForward actually resulted in a step or not. @@ -491,10 +509,10 @@ namespace RTE { bool m_MOHitsDisabled; //!< Temporary disabling of MO collisions for this. bool m_TerrainHitsDisabled; //!< Temporary disabling of terrain collisions for this. Will be re-enabled once out of terrain again. - MovableObject *m_OwnerMO; //!< The owner of this Atom. The owner is obviously not owned by this Atom. + MovableObject* m_OwnerMO; //!< The owner of this Atom. The owner is obviously not owned by this Atom. MOID m_IgnoreMOID; //!< Special ignored MOID. std::vector m_IgnoreMOIDs; //!< ignore hits with MOs of these IDs. - std::vector const * m_IgnoreMOIDsByGroup; //!< Also ignore hits with MOs of these IDs. This one may be set externally by atom group. + std::vector const* m_IgnoreMOIDsByGroup; //!< Also ignore hits with MOs of these IDs. This one may be set externally by atom group. HitData m_LastHit; //!< Data containing information on the last collision experienced by this Atom. MOID m_MOIDHit; //!< The MO, if any, this Atom hit on the last step. @@ -528,7 +546,6 @@ namespace RTE { bool m_SubStepped; private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. /// @@ -536,5 +553,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Box.cpp b/Source/System/Box.cpp index ca340a3456..a49aee9940 100644 --- a/Source/System/Box.cpp +++ b/Source/System/Box.cpp @@ -4,9 +4,9 @@ namespace RTE { const std::string Box::c_ClassName = "Box"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool IntRect::IntersectionCut(const IntRect &rhs) { + bool IntRect::IntersectionCut(const IntRect& rhs) { if (Intersects(rhs)) { m_Left = std::max(m_Left, rhs.m_Left); m_Right = std::min(m_Right, rhs.m_Right); @@ -17,9 +17,9 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::Create(const Vector &corner1, const Vector &corner2) { + int Box::Create(const Vector& corner1, const Vector& corner2) { m_Corner = corner1; m_Width = corner2.m_X - corner1.m_X; m_Height = corner2.m_Y - corner1.m_Y; @@ -27,7 +27,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Box::Create(float x1, float y1, float x2, float y2) { m_Corner.SetXY(x1, y1); @@ -37,9 +37,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::Create(const Vector &corner, float width, float height) { + int Box::Create(const Vector& corner, float width, float height) { m_Corner = corner; m_Width = width; m_Height = height; @@ -47,9 +47,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::Create(const Box &reference) { + int Box::Create(const Box& reference) { m_Corner = reference.m_Corner; m_Width = reference.m_Width; m_Height = reference.m_Height; @@ -57,22 +57,21 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::ReadProperty(const std::string_view &propName, Reader &reader) { + int Box::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("Corner", { reader >> m_Corner; }); MatchProperty("Width", { reader >> m_Width; }); MatchProperty("Height", { reader >> m_Height; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::Save(Writer &writer) const { + int Box::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("Corner", m_Corner); @@ -82,7 +81,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Box::Unflip() { if (m_Width < 0) { @@ -95,30 +94,30 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Box::IsWithinBox(const Vector &point) const { + bool Box::IsWithinBox(const Vector& point) const { return !IsEmpty() && (((m_Width > 0 && point.m_X >= m_Corner.m_X && point.m_X < (m_Corner.m_X + m_Width)) || - (m_Width < 0 && point.m_X < m_Corner.m_X && point.m_X >= (m_Corner.m_X + m_Width))) && - (m_Height > 0 && point.m_Y >= m_Corner.m_Y && point.m_Y < (m_Corner.m_Y + m_Height)) || - (m_Height < 0 && point.m_Y < m_Corner.m_Y && point.m_Y <= (m_Corner.m_Y + m_Height))); + (m_Width < 0 && point.m_X < m_Corner.m_X && point.m_X >= (m_Corner.m_X + m_Width))) && + (m_Height > 0 && point.m_Y >= m_Corner.m_Y && point.m_Y < (m_Corner.m_Y + m_Height)) || + (m_Height < 0 && point.m_Y < m_Corner.m_Y && point.m_Y <= (m_Corner.m_Y + m_Height))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Box::IsWithinBoxX(float pointX) const { return !IsEmpty() && ((m_Width > 0 && pointX >= m_Corner.m_X && pointX < (m_Corner.m_X + m_Width)) || - (m_Width < 0 && pointX < m_Corner.m_X && pointX >= (m_Corner.m_X + m_Width))); + (m_Width < 0 && pointX < m_Corner.m_X && pointX >= (m_Corner.m_X + m_Width))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Box::IsWithinBoxY(float pointY) const { return !IsEmpty() && ((m_Height > 0 && pointY >= m_Corner.m_Y && pointY < (m_Corner.m_Y + m_Height)) || - (m_Height < 0 && pointY < m_Corner.m_Y && pointY <= (m_Corner.m_Y + m_Height))); + (m_Height < 0 && pointY < m_Corner.m_Y && pointY <= (m_Corner.m_Y + m_Height))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Box::GetWithinBoxX(float pointX) const { if (m_Width > 0) { @@ -129,7 +128,7 @@ namespace RTE { return m_Corner.m_X; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Box::GetWithinBoxY(float pointY) const { if (m_Height > 0) { @@ -140,9 +139,9 @@ namespace RTE { return m_Corner.m_Y; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Box::IntersectsBox(const Box &rhs) { + bool Box::IntersectsBox(const Box& rhs) { if (IsEmpty() || rhs.IsEmpty()) { return false; } @@ -151,6 +150,6 @@ namespace RTE { box1.Unflip(); box2.Unflip(); return (box1.m_Corner.m_X < box2.m_Corner.m_X + box2.m_Width) && (box1.m_Corner.m_X + box1.m_Width > box2.m_Corner.m_X) && - (box1.m_Corner.m_Y < box2.m_Corner.m_Y + box2.m_Height) && (box1.m_Corner.m_Y + box1.m_Height > box2.m_Corner.m_Y); + (box1.m_Corner.m_Y < box2.m_Corner.m_Y + box2.m_Height) && (box1.m_Corner.m_Y + box1.m_Height > box2.m_Corner.m_Y); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/Box.h b/Source/System/Box.h index fe6495a84d..ffb1094de8 100644 --- a/Source/System/Box.h +++ b/Source/System/Box.h @@ -23,14 +23,15 @@ namespace RTE { /// Y position of the IntRect top left corner. /// X position of the IntRect bottom right corner. /// Y position of the IntRect bottom right corner. - IntRect(int left, int top, int right, int bottom) : m_Left(left), m_Top(top), m_Right(right), m_Bottom(bottom) {} + IntRect(int left, int top, int right, int bottom) : + m_Left(left), m_Top(top), m_Right(right), m_Bottom(bottom) {} /// /// Checks whether this IntRect is intersecting another one. /// /// The other IntRect to check for intersection with. /// Whether this IntRect is intersecting another one. - bool Intersects(const IntRect &rhs) const { return m_Left < rhs.m_Right && m_Right > rhs.m_Left && m_Top < rhs.m_Bottom && m_Bottom > rhs.m_Top; } + bool Intersects(const IntRect& rhs) const { return m_Left < rhs.m_Right && m_Right > rhs.m_Left && m_Top < rhs.m_Bottom && m_Bottom > rhs.m_Top; } /// /// If this and the passed in IntRect intersect, this will be modified to represent the boolean AND of the two. @@ -38,7 +39,7 @@ namespace RTE { /// /// THe other IntRect to cut against. /// Whether an intersection was detected and this was cut down to the AND of the two IntRects. - bool IntersectionCut(const IntRect &rhs); + bool IntersectionCut(const IntRect& rhs); }; /// @@ -47,7 +48,6 @@ namespace RTE { class Box : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -66,7 +66,7 @@ namespace RTE { /// /// Vector position of the upper left corner of this box. /// Vector position of the lower right corner of this box. - Box(const Vector &corner1, const Vector &corner2) { Create(corner1, corner2); } + Box(const Vector& corner1, const Vector& corner2) { Create(corner1, corner2); } /// /// Constructor method used to instantiate a Box object from four float values defining the initial corners of this Box. @@ -84,13 +84,13 @@ namespace RTE { /// Vector position of the upper left corner of this box. /// Width of this box. /// Height of this box. - Box(const Vector &corner, float width, float height) { Create(corner, width, height); } + Box(const Vector& corner, float width, float height) { Create(corner, width, height); } /// /// Copy constructor method used to instantiate a Box object identical to an already existing one. /// /// A Box object which is passed in by reference. - Box(const Box &reference) { Create(reference); } + Box(const Box& reference) { Create(reference); } /// /// Makes the Box object ready for use. @@ -98,7 +98,7 @@ namespace RTE { /// Vector position of the upper left corner of this box. /// Vector position of the lower right corner of this box. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Vector &corner1, const Vector &corner2); + int Create(const Vector& corner1, const Vector& corner2); /// /// Makes the Box object ready for use. @@ -117,14 +117,14 @@ namespace RTE { /// Width of this box. /// Height of this box. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Vector &corner, float width, float height); + int Create(const Vector& corner, float width, float height); /// /// Creates a Box to be identical to another, by deep copy. /// /// A reference to the Box to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Box &reference); + int Create(const Box& reference); #pragma endregion #pragma region Destruction @@ -158,7 +158,7 @@ namespace RTE { /// Sets the primary corner of this box. /// /// A Vector with the new primary corner. - void SetCorner(const Vector &newCorner) { m_Corner = newCorner; } + void SetCorner(const Vector& newCorner) { m_Corner = newCorner; } /// /// Gets the center point of this Box' area, in absolute Scene coordinates. @@ -170,7 +170,7 @@ namespace RTE { /// Sets the primary corner of this box, by specifying where the center ought to be. /// /// A Vector with the new center point. - void SetCenter(const Vector &newCenter) { m_Corner.SetXY(newCenter.m_X - (m_Width / 2), newCenter.m_Y - (m_Height / 2)); } + void SetCenter(const Vector& newCenter) { m_Corner.SetXY(newCenter.m_X - (m_Width / 2), newCenter.m_Y - (m_Height / 2)); } /// /// Gets the width of this box. Note that this can be negative if the box hasn't been righted with Unflip(). @@ -215,14 +215,14 @@ namespace RTE { /// /// The other Box to check for intersection with. /// Intersecting the other box or not. - bool IntersectsBox(const Box &rhs); + bool IntersectsBox(const Box& rhs); /// /// Tells whether a point is within the Box or not, taking potential flipping into account. /// /// The Vector describing the point to test for within box bounds. /// Inside the box or not. False if the box IsEmpty() - bool IsWithinBox(const Vector &point) const; + bool IsWithinBox(const Vector& point) const; /// /// Tells whether an X coordinate is within the Box's X-range or not, taking potential flipping into account. @@ -243,7 +243,7 @@ namespace RTE { /// /// The Vector describing the point to constrain inside the box. /// The resulting point inside the box. - Vector GetWithinBox(const Vector &point) const { return Vector(GetWithinBoxX(point.m_X), GetWithinBoxY(point.m_Y)); } + Vector GetWithinBox(const Vector& point) const { return Vector(GetWithinBoxX(point.m_X), GetWithinBoxY(point.m_Y)); } /// /// Returns an X value constrained inside the Box and returns it. @@ -266,7 +266,12 @@ namespace RTE { /// /// A Box reference. /// A reference to the changed Box. - Box & operator=(const Box &rhs) { if (*this != rhs) { Create(rhs); } return *this; } + Box& operator=(const Box& rhs) { + if (*this != rhs) { + Create(rhs); + } + return *this; + } /// /// An equality operator for testing if any two Boxes are equal. @@ -274,7 +279,7 @@ namespace RTE { /// A Box reference as the left hand side operand. /// A Box reference as the right hand side operand. /// A boolean indicating whether the two operands are equal or not. - friend bool operator==(const Box &lhs, const Box &rhs) { return lhs.m_Corner == rhs.m_Corner && lhs.m_Width == rhs.m_Width && lhs.m_Height == rhs.m_Height; } + friend bool operator==(const Box& lhs, const Box& rhs) { return lhs.m_Corner == rhs.m_Corner && lhs.m_Width == rhs.m_Width && lhs.m_Height == rhs.m_Height; } /// /// An inequality operator for testing if any two Boxes are unequal. @@ -282,17 +287,19 @@ namespace RTE { /// A Box reference as the left hand side operand. /// A Box reference as the right hand side operand. /// A boolean indicating whether the two operands are unequal or not. - friend bool operator!=(const Box &lhs, const Box &rhs) { return lhs.m_Corner != rhs.m_Corner || lhs.m_Width != rhs.m_Width || lhs.m_Height != rhs.m_Height; } + friend bool operator!=(const Box& lhs, const Box& rhs) { return lhs.m_Corner != rhs.m_Corner || lhs.m_Width != rhs.m_Width || lhs.m_Height != rhs.m_Height; } #pragma endregion private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. /// /// Clears all the member variables of this Box, effectively resetting the members of this abstraction level only. /// - void Clear() { m_Corner.Reset(); m_Width = m_Height = 0; } + void Clear() { + m_Corner.Reset(); + m_Width = m_Height = 0; + } }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Color.cpp b/Source/System/Color.cpp index e9f763f70d..414be41984 100644 --- a/Source/System/Color.cpp +++ b/Source/System/Color.cpp @@ -5,7 +5,7 @@ namespace RTE { const std::string Color::c_ClassName = "Color"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Color::Create() { if (Serializable::Create()) { @@ -15,7 +15,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Color::Create(int inputR, int inputG, int inputB) { SetR(inputR); @@ -25,11 +25,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Color::ReadProperty(const std::string_view &propName, Reader &reader) { + int Color::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("Index", { SetRGBWithIndex(std::stoi(reader.ReadPropValue())); }); MatchProperty("R", { SetR(std::stoi(reader.ReadPropValue())); }); MatchProperty("G", { SetG(std::stoi(reader.ReadPropValue())); }); @@ -38,9 +38,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Color::Save(Writer &writer) const { + int Color::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("R", m_R); @@ -50,7 +50,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Color::SetRGBWithIndex(int index) { m_Index = std::clamp(index, 0, 255); @@ -64,9 +64,9 @@ namespace RTE { m_B = rgbColor.b * 4; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Color::RecalculateIndex() { return m_Index = makecol8(m_R, m_G, m_B); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/Color.h b/Source/System/Color.h index 2a1692fa64..cf8cb63676 100644 --- a/Source/System/Color.h +++ b/Source/System/Color.h @@ -11,7 +11,6 @@ namespace RTE { class Color : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -27,19 +26,28 @@ namespace RTE { /// Initial Red value of this color. /// Initial Green value of this color. /// Initial Blue value of this color. - Color(int R, int G, int B) { Clear(); Create(R, G, B); } + Color(int R, int G, int B) { + Clear(); + Create(R, G, B); + } /// /// Constructor method used to instantiate a Color object from an entry in the current color palette. /// /// Palette index entry to create this color from. - Color(int index) { Clear(); SetRGBWithIndex(index); } + Color(int index) { + Clear(); + SetRGBWithIndex(index); + } /// /// Copy constructor method used to instantiate a Color object identical to an already existing one. /// /// A Color object which is passed in by reference. - Color(const Color &reference) { Clear(); Create(reference.m_R, reference.m_G, reference.m_B); } + Color(const Color& reference) { + Clear(); + Create(reference.m_R, reference.m_G, reference.m_B); + } /// /// Makes the Color object ready for use. @@ -87,7 +95,10 @@ namespace RTE { /// Sets the red value of this Color. /// /// An integer value that the R value will be set to, between 0 and 255. - void SetR(int newR) { m_R = std::clamp(newR, 0, 255); m_Index = 0; } + void SetR(int newR) { + m_R = std::clamp(newR, 0, 255); + m_Index = 0; + } /// /// Gets the green value of this Color. @@ -99,7 +110,10 @@ namespace RTE { /// Sets the green value of this Color. /// /// An integer value that the green value will be set to, between 0 and 255. - void SetG(int newG) { m_G = std::clamp(newG, 0, 255); m_Index = 0; } + void SetG(int newG) { + m_G = std::clamp(newG, 0, 255); + m_Index = 0; + } /// /// Gets the blue value of this Color. @@ -111,7 +125,10 @@ namespace RTE { /// Sets the blue value of this Color. /// /// An integer value that the blue value will be set to, between 0 and 255. - void SetB(int newB) { m_B = std::clamp(newB, 0, 255); m_Index = 0; } + void SetB(int newB) { + m_B = std::clamp(newB, 0, 255); + m_Index = 0; + } /// /// Sets all three RGB values of this Color. @@ -119,7 +136,12 @@ namespace RTE { /// Integer value that the Red value will be set to, between 0 and 255. /// Integer value that the Green value will be set to, between 0 and 255. /// Integer value that the Blue value will be set to, between 0 and 255. - void SetRGB(int newR, int newG, int newB) { SetR(newR); SetG(newG); SetB(newB); m_Index = 0; } + void SetRGB(int newR, int newG, int newB) { + SetR(newR); + SetG(newG); + SetB(newB); + m_Index = 0; + } #pragma endregion #pragma region Concrete Methods @@ -131,14 +153,12 @@ namespace RTE { #pragma endregion protected: - int m_R; //!< Red value of this color. int m_G; //!< Green value of this color. int m_B; //!< Blue value of this color. int m_Index; //!< The closest matching index in the current color palette. If 0, this needs to be recalculated and updated. private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. /// @@ -146,5 +166,5 @@ namespace RTE { /// void Clear() { m_R = m_G = m_B = m_Index = 0; } }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Constants.h b/Source/System/Constants.h index a2416f4c67..cc91d106a5 100644 --- a/Source/System/Constants.h +++ b/Source/System/Constants.h @@ -30,7 +30,7 @@ namespace RTE { #pragma endregion #pragma region AI Constants - static constexpr float c_PathFindingDefaultDigStrength = 35.0F; //!< A default pathfinder penetration value that'll allow pathing through corpses, debris, and such stuff. + static constexpr float c_PathFindingDefaultDigStrength = 35.0F; //!< A default pathfinder penetration value that'll allow pathing through corpses, debris, and such stuff. #pragma endregion #pragma region Graphics Constants @@ -53,9 +53,9 @@ namespace RTE { enum ColorKeys { g_InvalidColor = -1, g_MaskColor = 0, //!< Mask color for all 8bpp bitmaps (palette index 0 (255,0,255)). This color is fully transparent. - //g_MOIDMaskColor = 0, //!< Mask color for 8bpp MOID layer bitmaps (palette index 0 (255,0,255)). This color is fully transparent. + // g_MOIDMaskColor = 0, //!< Mask color for 8bpp MOID layer bitmaps (palette index 0 (255,0,255)). This color is fully transparent. g_MOIDMaskColor = 0xF81F, //!< Mask color for 16bpp MOID layer bitmaps (255,0,255). This color is fully transparent. - //g_MOIDMaskColor = 0xFF00FF, //!< Mask color for 32bpp MOID layer bitmaps (255,0,255). This color is fully transparent. + // g_MOIDMaskColor = 0xFF00FF, //!< Mask color for 32bpp MOID layer bitmaps (255,0,255). This color is fully transparent. g_BlackColor = 245, g_WhiteColor = 254, g_RedColor = 13, @@ -75,7 +75,12 @@ namespace RTE { g_MaterialDoor = 181 }; - enum DotGlowColor { NoDot, YellowDot, RedDot, BlueDot }; + enum DotGlowColor { + NoDot, + YellowDot, + RedDot, + BlueDot + }; enum DrawBlendMode { NoBlend, @@ -93,9 +98,16 @@ namespace RTE { BlendModeCount }; - enum BlendAmountLimits { MinBlend = 0, MaxBlend = 100 }; + enum BlendAmountLimits { + MinBlend = 0, + MaxBlend = 100 + }; - enum TransparencyPreset { LessTrans = 25, HalfTrans = 50, MoreTrans = 75 }; + enum TransparencyPreset { + LessTrans = 25, + HalfTrans = 50, + MoreTrans = 75 + }; enum SpriteAnimMode { NOANIM, @@ -110,25 +122,25 @@ namespace RTE { SpriteAnimModeCount }; - // GUI colors - #define c_GUIColorWhite makecol(255, 255, 255) - #define c_GUIColorYellow makecol(255, 255, 128) - #define c_GUIColorRed makecol(255, 100, 100) - #define c_GUIColorGreen makecol(128, 255, 128) - #define c_GUIColorCyan makecol(127, 255, 255) - #define c_GUIColorLightBlue makecol(109, 117, 170) - #define c_GUIColorBlue makecol(59, 65, 83) - #define c_GUIColorDarkBlue makecol(12, 20, 39) - #define c_GUIColorGray makecol(232, 232, 248) - - #define c_PlayerSlotColorDefault makecol(161, 109, 20) - #define c_PlayerSlotColorHovered makecol(203, 130, 56) - #define c_PlayerSlotColorDisabled makecol(104, 67, 15) - static constexpr std::array c_Quad { - 1.0f, 1.0f, 1.0f, 0.0f, - 1.0f, -1.0f, 1.0f, 1.0f, - -1.0f, 1.0f, 0.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 1.0f}; +// GUI colors +#define c_GUIColorWhite makecol(255, 255, 255) +#define c_GUIColorYellow makecol(255, 255, 128) +#define c_GUIColorRed makecol(255, 100, 100) +#define c_GUIColorGreen makecol(128, 255, 128) +#define c_GUIColorCyan makecol(127, 255, 255) +#define c_GUIColorLightBlue makecol(109, 117, 170) +#define c_GUIColorBlue makecol(59, 65, 83) +#define c_GUIColorDarkBlue makecol(12, 20, 39) +#define c_GUIColorGray makecol(232, 232, 248) + +#define c_PlayerSlotColorDefault makecol(161, 109, 20) +#define c_PlayerSlotColorHovered makecol(203, 130, 56) +#define c_PlayerSlotColorDisabled makecol(104, 67, 15) + static constexpr std::array c_Quad{ + 1.0f, 1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 1.0f}; #pragma endregion #pragma region Math Constants @@ -142,7 +154,7 @@ namespace RTE { #pragma endregion #pragma region Audio Constants - static constexpr std::array c_SupportedAudioFormats = { ".wav", ".ogg", ".flac" }; + static constexpr std::array c_SupportedAudioFormats = {".wav", ".ogg", ".flac"}; static constexpr unsigned short c_MaxSoftwareChannels = 128; static constexpr unsigned short c_MaxVirtualChannels = 1024; @@ -217,33 +229,33 @@ namespace RTE { }; static const std::array c_InputElementNames = { - "Move Up", // INPUT_L_UP - "Move Down", // INPUT_L_DOWN - "Move Left", // INPUT_L_LEFT - "Move Right", // INPUT_L_RIGHT - "Aim Up", // INPUT_AIM_UP - "Aim Down", // INPUT_AIM_DOWN - "Aim Left", // INPUT_AIM_LEFT - "Aim Right", // INPUT_AIM_RIGHT - "Fire/Activate", // INPUT_FIRE - "Sharp Aim", // INPUT_AIM - "Pie Menu Analog", // INPUT_PIEMENU_ANALOG - "Pie Menu Digital", // INPUT_PIEMENU_DIGITAL - "Jump", // INPUT_JUMP - "Crouch", // INPUT_CROUCH - "Next Body", // INPUT_NEXT - "Prev. Body", // INPUT_PREV - "Next Device", // INPUT_WEAPON_CHANGE_NEXT - "Prev. Device", // INPUT_WEAPON_CHANGE_PREV - "Pick Up Device", // INPUT_WEAPON_PICKUP - "Drop Device", // INPUT_WEAPON_DROP - "Reload Weapon", // INPUT_WEAPON_RELOAD - "Start", // INPUT_START - "Back", // INPUT_BACK - "Analog Aim Up", // INPUT_R_UP - "Analog Aim Down", // INPUT_R_DOWN - "Analog Aim Left", // INPUT_R_LEFT - "Analog Aim Right" // INPUT_R_RIGHT + "Move Up", // INPUT_L_UP + "Move Down", // INPUT_L_DOWN + "Move Left", // INPUT_L_LEFT + "Move Right", // INPUT_L_RIGHT + "Aim Up", // INPUT_AIM_UP + "Aim Down", // INPUT_AIM_DOWN + "Aim Left", // INPUT_AIM_LEFT + "Aim Right", // INPUT_AIM_RIGHT + "Fire/Activate", // INPUT_FIRE + "Sharp Aim", // INPUT_AIM + "Pie Menu Analog", // INPUT_PIEMENU_ANALOG + "Pie Menu Digital", // INPUT_PIEMENU_DIGITAL + "Jump", // INPUT_JUMP + "Crouch", // INPUT_CROUCH + "Next Body", // INPUT_NEXT + "Prev. Body", // INPUT_PREV + "Next Device", // INPUT_WEAPON_CHANGE_NEXT + "Prev. Device", // INPUT_WEAPON_CHANGE_PREV + "Pick Up Device", // INPUT_WEAPON_PICKUP + "Drop Device", // INPUT_WEAPON_DROP + "Reload Weapon", // INPUT_WEAPON_RELOAD + "Start", // INPUT_START + "Back", // INPUT_BACK + "Analog Aim Up", // INPUT_R_UP + "Analog Aim Down", // INPUT_R_DOWN + "Analog Aim Left", // INPUT_R_LEFT + "Analog Aim Right" // INPUT_R_RIGHT }; /// @@ -289,14 +301,20 @@ namespace RTE { /// /// Enumeration for joystick direction types. /// - enum JoyDirections { JOYDIR_ONE = 0, JOYDIR_TWO }; + enum JoyDirections { + JOYDIR_ONE = 0, + JOYDIR_TWO + }; /// /// Enumeration for joystick dead zone types. /// Square deadzone cuts-off any input from every axis separately. For example if x-axis has less than 20% input and y-axis has more, x-axis input is ignored. /// Circle uses a round zone to capture stick position on both axis then cut-off if this position is inside the round dead zone. /// - enum DeadZoneType { CIRCLE = 0, SQUARE = 1 }; + enum DeadZoneType { + CIRCLE = 0, + SQUARE = 1 + }; #pragma endregion #pragma region Global Enumerations @@ -315,38 +333,43 @@ namespace RTE { /// /// Enumeration and supporting maps for cardinal directions, as well as None and Any. /// - enum Directions { None = -1, Up, Down, Left, Right, Any }; + enum Directions { + None = -1, + Up, + Down, + Left, + Right, + Any + }; static const std::unordered_map c_DirectionNameToDirectionsMap = { - {"None", Directions::None}, - {"Up", Directions::Up}, - {"Down", Directions::Down}, - {"Left", Directions::Left}, - {"Right", Directions::Right}, - {"Any", Directions::Any} - }; + {"None", Directions::None}, + {"Up", Directions::Up}, + {"Down", Directions::Down}, + {"Left", Directions::Left}, + {"Right", Directions::Right}, + {"Any", Directions::Any}}; static const std::unordered_map c_DirectionsToRadiansMap = { - {Directions::Up, c_HalfPI}, - {Directions::Down, c_OneAndAHalfPI}, - {Directions::Left, c_PI}, - {Directions::Right, 0.0F} - }; + {Directions::Up, c_HalfPI}, + {Directions::Down, c_OneAndAHalfPI}, + {Directions::Left, c_PI}, + {Directions::Right, 0.0F}}; #pragma endregion #pragma region Un-Definitions - // Allegro defines these via define in astdint.h and Boost with stdlib go crazy so we need to undefine them manually. - #undef int8_t - #undef uint8_t - #undef int16_t - #undef uint16_t - #undef int32_t - #undef uint32_t - #undef intptr_t - #undef uintptr_t - #undef LONG_LONG - #undef int64_t - #undef uint64_t +// Allegro defines these via define in astdint.h and Boost with stdlib go crazy so we need to undefine them manually. +#undef int8_t +#undef uint8_t +#undef int16_t +#undef uint16_t +#undef int32_t +#undef uint32_t +#undef intptr_t +#undef uintptr_t +#undef LONG_LONG +#undef int64_t +#undef uint64_t #pragma endregion -} +} // namespace RTE #endif diff --git a/Source/System/ContentFile.cpp b/Source/System/ContentFile.cpp index f4552e20e4..acdda8ef3b 100644 --- a/Source/System/ContentFile.cpp +++ b/Source/System/ContentFile.cpp @@ -13,11 +13,11 @@ namespace RTE { const std::string ContentFile::c_ClassName = "ContentFile"; - std::array, ContentFile::BitDepths::BitDepthCount> ContentFile::s_LoadedBitmaps; - std::unordered_map ContentFile::s_LoadedSamples; + std::array, ContentFile::BitDepths::BitDepthCount> ContentFile::s_LoadedBitmaps; + std::unordered_map ContentFile::s_LoadedSamples; std::unordered_map ContentFile::s_PathHashes; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ContentFile::Clear() { m_DataPath.clear(); @@ -31,17 +31,17 @@ namespace RTE { m_ImageFileInfo.fill(-1); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::Create(const char *filePath) { + int ContentFile::Create(const char* filePath) { SetDataPath(filePath); return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::Create(const ContentFile &reference) { + int ContentFile::Create(const ContentFile& reference) { m_DataPath = reference.m_DataPath; m_DataPathExtension = reference.m_DataPathExtension; m_DataPathWithoutExtension = reference.m_DataPathWithoutExtension; @@ -50,46 +50,47 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ContentFile::FreeAllLoaded() { for (int depth = BitDepths::Eight; depth < BitDepths::BitDepthCount; ++depth) { - for (const auto &[bitmapPath, bitmapPtr] : s_LoadedBitmaps[depth]) { + for (const auto& [bitmapPath, bitmapPtr]: s_LoadedBitmaps[depth]) { destroy_bitmap(bitmapPtr); } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::ReadProperty(const std::string_view &propName, Reader &reader) { + int ContentFile::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchForwards("FilePath") MatchProperty("Path", { SetDataPath(reader.ReadPropValue()); }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::Save(Writer &writer) const { + int ContentFile::Save(Writer& writer) const { Serializable::Save(writer); - if (!m_DataPath.empty()) { writer.NewPropertyWithValue("FilePath", m_DataPath); } + if (!m_DataPath.empty()) { + writer.NewPropertyWithValue("FilePath", m_DataPath); + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int ContentFile::GetDataModuleID() const { return (m_DataModuleID < 0) ? g_PresetMan.GetModuleIDFromPath(m_DataPath) : m_DataModuleID; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::SetDataPath(const std::string &newDataPath) { + void ContentFile::SetDataPath(const std::string& newDataPath) { m_DataPath = g_PresetMan.GetFullModulePath(newDataPath); m_DataPathExtension = std::filesystem::path(m_DataPath).extension().string(); @@ -102,31 +103,31 @@ namespace RTE { m_DataModuleID = g_PresetMan.GetModuleIDFromPath(m_DataPath); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// size_t ContentFile::GetHash() const { return Hash(m_DataPath); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::SetFormattedReaderPosition(const std::string &newPosition) { + void ContentFile::SetFormattedReaderPosition(const std::string& newPosition) { m_FormattedReaderPosition = newPosition; m_DataPathAndReaderPosition = m_DataPath + "\n" + newPosition; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int ContentFile::GetImageFileInfo(ImageFileInfoType infoTypeToGet) { bool fetchFileInfo = false; - for (const int &fileInfoEntry : m_ImageFileInfo) { + for (const int& fileInfoEntry: m_ImageFileInfo) { if (fileInfoEntry == -1) { fetchFileInfo = true; break; } } if (fetchFileInfo) { - FILE *imageFile = fopen(m_DataPath.c_str(), "rb"); + FILE* imageFile = fopen(m_DataPath.c_str(), "rb"); RTEAssert(imageFile, "Failed to open file prior to reading info of image file with following path and name:\n\n" + m_DataPath + "\n\nThe file may not exist or be corrupt."); if (m_DataPathExtension == ".png") { @@ -141,9 +142,9 @@ namespace RTE { return m_ImageFileInfo[infoTypeToGet]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::ReadAndStorePNGFileInfo(FILE *imageFile) { + void ContentFile::ReadAndStorePNGFileInfo(FILE* imageFile) { std::array fileSignature = {}; // Read the first 8 bytes to then verify they match the PNG file signature which is { 137, 80, 78, 71, 13, 10, 26, 10 } or { '\211', 'P', 'N', 'G', '\r', '\n', '\032', '\n' }. @@ -167,10 +168,10 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::ReadAndStoreBMPFileInfo(FILE *imageFile) { - std::array bmpSignature = { 0x42, 0x4D }; // { 'B', 'M' }. + void ContentFile::ReadAndStoreBMPFileInfo(FILE* imageFile) { + std::array bmpSignature = {0x42, 0x4D}; // { 'B', 'M' }. std::array fileSignature = {}; // Read the first 2 bytes to then verify they match the BMP file signature. @@ -202,24 +203,24 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ContentFile::ReloadAllBitmaps() { - for (const std::unordered_map &bitmapCache : s_LoadedBitmaps) { - for (const auto &[filePath, oldBitmap] : bitmapCache) { + for (const std::unordered_map& bitmapCache: s_LoadedBitmaps) { + for (const auto& [filePath, oldBitmap]: bitmapCache) { ReloadBitmap(filePath); } } g_ConsoleMan.PrintString("SYSTEM: Sprites reloaded"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP * ContentFile::GetAsBitmap(int conversionMode, bool storeBitmap, const std::string &dataPathToSpecificFrame) { + BITMAP* ContentFile::GetAsBitmap(int conversionMode, bool storeBitmap, const std::string& dataPathToSpecificFrame) { if (m_DataPath.empty()) { return nullptr; } - BITMAP *returnBitmap = nullptr; + BITMAP* returnBitmap = nullptr; const int bitDepth = conversionMode == COLORCONV_8_TO_32 ? BitDepths::ThirtyTwo : BitDepths::Eight; std::string dataPathToLoad = dataPathToSpecificFrame.empty() ? m_DataPath : dataPathToSpecificFrame; @@ -228,7 +229,7 @@ namespace RTE { } // Check if the file has already been read and loaded from the disk and, if so, use that data. - std::unordered_map::iterator foundBitmap = s_LoadedBitmaps[bitDepth].find(dataPathToLoad); + std::unordered_map::iterator foundBitmap = s_LoadedBitmaps[bitDepth].find(dataPathToLoad); if (storeBitmap && foundBitmap != s_LoadedBitmaps[bitDepth].end()) { returnBitmap = (*foundBitmap).second; } else { @@ -247,14 +248,16 @@ namespace RTE { returnBitmap = LoadAndReleaseBitmap(conversionMode, dataPathToLoad); // NOTE: This takes ownership of the bitmap file // Insert the bitmap into the map, PASSING OVER OWNERSHIP OF THE LOADED DATAFILE - if (storeBitmap) { s_LoadedBitmaps[bitDepth].try_emplace(dataPathToLoad, returnBitmap); } + if (storeBitmap) { + s_LoadedBitmaps[bitDepth].try_emplace(dataPathToLoad, returnBitmap); + } } return returnBitmap; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::GetAsAnimation(std::vector &vectorToFill, int frameCount, int conversionMode) { + void ContentFile::GetAsAnimation(std::vector& vectorToFill, int frameCount, int conversionMode) { if (m_DataPath.empty() || frameCount < 1) { return; } @@ -282,15 +285,15 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP * ContentFile::LoadAndReleaseBitmap(int conversionMode, const std::string &dataPathToSpecificFrame) { + BITMAP* ContentFile::LoadAndReleaseBitmap(int conversionMode, const std::string& dataPathToSpecificFrame) { if (m_DataPath.empty()) { return nullptr; } const std::string dataPathToLoad = dataPathToSpecificFrame.empty() ? m_DataPath : dataPathToSpecificFrame; - BITMAP *returnBitmap = nullptr; + BITMAP* returnBitmap = nullptr; PALETTE currentPalette; get_palette(currentPalette); @@ -303,19 +306,19 @@ namespace RTE { return returnBitmap; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD::Sound * ContentFile::GetAsSound(bool abortGameForInvalidSound, bool asyncLoading) { + FMOD::Sound* ContentFile::GetAsSound(bool abortGameForInvalidSound, bool asyncLoading) { if (m_DataPath.empty() || !g_AudioMan.IsAudioEnabled()) { return nullptr; } - FMOD::Sound *returnSample = nullptr; + FMOD::Sound* returnSample = nullptr; - std::unordered_map::iterator foundSound = s_LoadedSamples.find(m_DataPath); + std::unordered_map::iterator foundSound = s_LoadedSamples.find(m_DataPath); if (foundSound != s_LoadedSamples.end()) { returnSample = (*foundSound).second; } else { - returnSample = LoadAndReleaseSound(abortGameForInvalidSound, asyncLoading); //NOTE: This takes ownership of the sample file + returnSample = LoadAndReleaseSound(abortGameForInvalidSound, asyncLoading); // NOTE: This takes ownership of the sample file // Insert the Sound object into the map, PASSING OVER OWNERSHIP OF THE LOADED FILE s_LoadedSamples.try_emplace(m_DataPath, returnSample); @@ -323,15 +326,15 @@ namespace RTE { return returnSample; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD::Sound * ContentFile::LoadAndReleaseSound(bool abortGameForInvalidSound, bool asyncLoading) { + FMOD::Sound* ContentFile::LoadAndReleaseSound(bool abortGameForInvalidSound, bool asyncLoading) { if (m_DataPath.empty() || !g_AudioMan.IsAudioEnabled()) { return nullptr; } if (!System::PathExistsCaseSensitive(m_DataPath)) { bool foundAltExtension = false; - for (const std::string &altFileExtension : c_SupportedAudioFormats) { + for (const std::string& altFileExtension: c_SupportedAudioFormats) { const std::string altDataPathToLoad = m_DataPathWithoutExtension + altFileExtension; if (System::PathExistsCaseSensitive(altDataPathToLoad)) { g_ConsoleMan.AddLoadWarningLogExtensionMismatchEntry(m_DataPath, m_FormattedReaderPosition, altFileExtension); @@ -353,7 +356,7 @@ namespace RTE { g_ConsoleMan.PrintString("ERROR: " + errorMessage + m_DataPath); return nullptr; } - FMOD::Sound *returnSample = nullptr; + FMOD::Sound* returnSample = nullptr; FMOD_MODE fmodFlags = FMOD_CREATESAMPLE | FMOD_3D | (asyncLoading ? FMOD_NONBLOCKING : FMOD_DEFAULT); FMOD_RESULT result = g_AudioMan.GetAudioSystem()->createSound(m_DataPath.c_str(), fmodFlags, nullptr, &returnSample); @@ -367,9 +370,9 @@ namespace RTE { return returnSample; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::ReloadBitmap(const std::string &filePath, int conversionMode) { + void ContentFile::ReloadBitmap(const std::string& filePath, int conversionMode) { const int bitDepth = (conversionMode == COLORCONV_8_TO_32) ? BitDepths::ThirtyTwo : BitDepths::Eight; auto bmpItr = s_LoadedBitmaps[bitDepth].find(filePath); @@ -381,8 +384,8 @@ namespace RTE { get_palette(currentPalette); set_color_conversion((conversionMode == COLORCONV_NONE) ? COLORCONV_MOST : conversionMode); - BITMAP *loadedBitmap = (*bmpItr).second; - BITMAP *newBitmap = load_bitmap(filePath.c_str(), currentPalette); + BITMAP* loadedBitmap = (*bmpItr).second; + BITMAP* newBitmap = load_bitmap(filePath.c_str(), currentPalette); AddAlphaChannel(newBitmap); BITMAP swap; @@ -393,7 +396,7 @@ namespace RTE { destroy_bitmap(newBitmap); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ContentFile::AddAlphaChannel(BITMAP* bitmap) { if (!bitmap) { @@ -406,9 +409,9 @@ namespace RTE { for (int x = 0; x < bitmap->w; ++x) { unsigned long color = _getpixel32(bitmap, x, y); if (color != MASK_COLOR_32) { - _putpixel32(bitmap, x, y, color|(0xFF << 24)); + _putpixel32(bitmap, x, y, color | (0xFF << 24)); } } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/ContentFile.h b/Source/System/ContentFile.h index a0503ea479..89331690de 100644 --- a/Source/System/ContentFile.h +++ b/Source/System/ContentFile.h @@ -3,7 +3,9 @@ #include "Serializable.h" -namespace FMOD { class Sound; } +namespace FMOD { + class Sound; +} struct BITMAP; namespace RTE { @@ -14,7 +16,6 @@ namespace RTE { class ContentFile : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -28,27 +29,33 @@ namespace RTE { /// Constructor method used to instantiate a ContentFile object in system memory, and also do a Create() in the same line. /// /// A string defining the path to where the content file itself is located. - explicit ContentFile(const char *filePath) { Clear(); Create(filePath); } + explicit ContentFile(const char* filePath) { + Clear(); + Create(filePath); + } /// /// Constructor method used to instantiate a ContentFile object in system memory from a hash value of the file path, and also do a Create() in the same line. /// /// A hash value containing the path to where the content file itself is located. - explicit ContentFile(size_t hash) { Clear(); Create(GetPathFromHash(hash).c_str()); } + explicit ContentFile(size_t hash) { + Clear(); + Create(GetPathFromHash(hash).c_str()); + } /// /// Makes the ContentFile object ready for use. /// /// A string defining the path to where the content file itself is located. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const char *filePath); + int Create(const char* filePath); /// /// Creates a ContentFile to be identical to another, by deep copy. /// /// A reference to the ContentFile to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const ContentFile &reference); + int Create(const ContentFile& reference); #pragma endregion #pragma region Destruction @@ -79,13 +86,13 @@ namespace RTE { /// Gets the file path of the content file represented by this ContentFile object. /// /// A string with the file name path. - const std::string & GetDataPath() const { return m_DataPath; } + const std::string& GetDataPath() const { return m_DataPath; } /// /// Sets the file path of the content file represented by this ContentFile object. /// /// A string with the new file name path. - void SetDataPath(const std::string &newDataPath); + void SetDataPath(const std::string& newDataPath); /// /// Creates a hash value out of a path to a ContentFile. @@ -106,13 +113,13 @@ namespace RTE { /// Gets the file and line that are currently being read. Formatted to be used for logging warnings and errors. /// /// A string containing the currently read file path and the line being read. - const std::string & GetFormattedReaderPosition() const { return m_FormattedReaderPosition; } + const std::string& GetFormattedReaderPosition() const { return m_FormattedReaderPosition; } /// /// Sets the file and line that are currently being read. Formatted to be used for logging warnings and errors. /// /// A string containing the currently read file path and the line being read. - void SetFormattedReaderPosition(const std::string &newPosition) override; + void SetFormattedReaderPosition(const std::string& newPosition) override; #pragma endregion #pragma region Image Info Getters @@ -154,7 +161,7 @@ namespace RTE { /// Whether to store the BITMAP in the relevant static map after loading it or not. If this is false, ownership of the BITMAP IS transferred! /// Path to a specific frame when loading an animation to avoid overwriting the original preset DataPath when loading each frame. /// Pointer to the BITMAP loaded from disk. - BITMAP * GetAsBitmap(int conversionMode = 0, bool storeBitmap = true, const std::string &dataPathToSpecificFrame = ""); + BITMAP* GetAsBitmap(int conversionMode = 0, bool storeBitmap = true, const std::string& dataPathToSpecificFrame = ""); /// /// Fills an existing vector of Allegro BITMAPs representing each frame in the animation with the data represented by this ContentFile object. @@ -163,7 +170,7 @@ namespace RTE { /// The existing vector of Allegro BITMAPs to fill. /// The number of frames to attempt to load, more than 1 frame will mean 00# is appended to DataPath to handle naming conventions. /// The Allegro color conversion mode to use when loading this bitmap. - void GetAsAnimation(std::vector &vectorToFill, int frameCount = 1, int conversionMode = 0); + void GetAsAnimation(std::vector& vectorToFill, int frameCount = 1, int conversionMode = 0); /// /// Gets the data represented by this ContentFile object as a vector of Allegro BITMAPs, each representing a frame in the animation. @@ -172,7 +179,11 @@ namespace RTE { /// The number of frames to attempt to load, more than 1 frame will mean 00# is appended to DataPath to handle naming conventions. /// The Allegro color conversion mode to use when loading this bitmap. /// A vector of BITMAP pointers loaded from the disk. - std::vector GetAsAnimation(int frameCount = 1, int conversionMode = 0) { std::vector returnBitmaps; GetAsAnimation(returnBitmaps, frameCount, conversionMode); return returnBitmaps; } + std::vector GetAsAnimation(int frameCount = 1, int conversionMode = 0) { + std::vector returnBitmaps; + GetAsAnimation(returnBitmaps, frameCount, conversionMode); + return returnBitmaps; + } /// /// Gets the data represented by this ContentFile object as an FMOD FSOUND_SAMPLE, loading it into the static maps if it's not already loaded. Ownership of the FSOUND_SAMPLE is NOT transferred! @@ -180,26 +191,34 @@ namespace RTE { /// Whether to abort the game if the sound couldn't be added, or just show a console error. Default true. /// Whether to enable FMOD asynchronous loading or not. Should be disabled for loading audio files with Lua AddSound. /// Pointer to the FSOUND_SAMPLE loaded from disk. - FMOD::Sound * GetAsSound(bool abortGameForInvalidSound = true, bool asyncLoading = true); + FMOD::Sound* GetAsSound(bool abortGameForInvalidSound = true, bool asyncLoading = true); #pragma endregion private: - /// /// Enumeration for loading BITMAPs by bit depth. NOTE: This can't be lower down because s_LoadedBitmaps relies on this definition. /// - enum BitDepths { Eight = 0, ThirtyTwo, BitDepthCount }; + enum BitDepths { + Eight = 0, + ThirtyTwo, + BitDepthCount + }; /// /// Enumeration for the image file information types that can be stored. /// - enum ImageFileInfoType { ImageBitDepth, ImageWidth, ImageHeight, ImageInfoTypeCount }; + enum ImageFileInfoType { + ImageBitDepth, + ImageWidth, + ImageHeight, + ImageInfoTypeCount + }; static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. static std::unordered_map s_PathHashes; //!< Static map containing the hash values of paths of all loaded data files. - static std::array, BitDepths::BitDepthCount> s_LoadedBitmaps; //!< Static map containing all the already loaded BITMAPs and their paths for each bit depth. - static std::unordered_map s_LoadedSamples; //!< Static map containing all the already loaded FSOUND_SAMPLEs and their paths. + static std::array, BitDepths::BitDepthCount> s_LoadedBitmaps; //!< Static map containing all the already loaded BITMAPs and their paths for each bit depth. + static std::unordered_map s_LoadedSamples; //!< Static map containing all the already loaded FSOUND_SAMPLEs and their paths. std::string m_DataPath; //!< The path to this ContentFile's data file. In the case of an animation, this filename/name will be appended with 000, 001, 002 etc. std::string m_DataPathExtension; //!< The extension of the data file of this ContentFile's path. @@ -225,13 +244,13 @@ namespace RTE { /// Reads a PNG file from disk and stores the relevant information without actually loading the whole file into memory. /// /// Pointer to the file stream to read. - void ReadAndStorePNGFileInfo(FILE *imageFile); + void ReadAndStorePNGFileInfo(FILE* imageFile); /// /// Reads a BMP file from disk and stores the relevant information without actually loading the whole file into memory. /// /// Pointer to the file stream to read. - void ReadAndStoreBMPFileInfo(FILE *imageFile); + void ReadAndStoreBMPFileInfo(FILE* imageFile); #pragma endregion #pragma region Data Handling @@ -242,7 +261,7 @@ namespace RTE { /// The Allegro color conversion mode to use when loading this bitmap. Only applies the first time a bitmap is loaded from the disk. /// Path to a specific frame when loading an animation to avoid overwriting the original preset DataPath when loading each frame. /// Pointer to the BITMAP loaded from disk. - BITMAP * LoadAndReleaseBitmap(int conversionMode = 0, const std::string &dataPathToSpecificFrame = ""); + BITMAP* LoadAndReleaseBitmap(int conversionMode = 0, const std::string& dataPathToSpecificFrame = ""); /// /// Loads and transfers the data represented by this ContentFile object as an FMOD FSOUND_SAMPLE. Ownership of the FSOUND_SAMPLE is NOT transferred! @@ -250,14 +269,14 @@ namespace RTE { /// Whether to abort the game if the sound couldn't be added, or just show a console error. Default true. /// Whether to enable FMOD asynchronous loading or not. Should be disabled for loading audio files with Lua AddSound. /// Pointer to the FSOUND_SAMPLE loaded from disk. - FMOD::Sound * LoadAndReleaseSound(bool abortGameForInvalidSound = true, bool asyncLoading = true); + FMOD::Sound* LoadAndReleaseSound(bool abortGameForInvalidSound = true, bool asyncLoading = true); /// /// Reloads a specific BITMAP in the cache from disk, allowing any changes to be reflected at runtime. /// /// The filepath to the bitmap we want to reload. /// The Allegro color conversion mode to use when reloading this bitmap. - static void ReloadBitmap(const std::string &filePath, int conversionMode = 0); + static void ReloadBitmap(const std::string& filePath, int conversionMode = 0); /// /// Set alpha value of non mask color pixels to 255 for 32-bit bitmaps. (WARN: would override existing alpha values!) @@ -270,5 +289,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif diff --git a/Source/System/Controller.cpp b/Source/System/Controller.cpp index fad7b95f3d..fad67c4503 100644 --- a/Source/System/Controller.cpp +++ b/Source/System/Controller.cpp @@ -7,7 +7,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Controller::Clear() { m_ControlStates.fill(false); @@ -28,25 +28,27 @@ namespace RTE { m_WeaponDropIgnore = false; m_WeaponReloadIgnore = false; m_MouseMovement.Reset(); - m_AnalogCursorAngleLimits = { {0, 0}, false }; + m_AnalogCursorAngleLimits = {{0, 0}, false}; m_ReleaseTimer.Reset(); m_JoyAccelTimer.Reset(); m_KeyAccelTimer.Reset(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Controller::Create(InputMode mode, Actor *controlledActor) { + int Controller::Create(InputMode mode, Actor* controlledActor) { m_InputMode = mode; m_ControlledActor = controlledActor; - if (m_ControlledActor) { m_Team = m_ControlledActor->GetTeam(); } + if (m_ControlledActor) { + m_Team = m_ControlledActor->GetTeam(); + } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Controller::Create(const Controller &reference) { + int Controller::Create(const Controller& reference) { for (int i = 0; i < ControlState::CONTROLSTATECOUNT; ++i) { m_ControlStates[i] = reference.m_ControlStates[i]; } @@ -70,9 +72,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Controller::RelativeCursorMovement(Vector &cursorPos, float moveScale) const { + bool Controller::RelativeCursorMovement(Vector& cursorPos, float moveScale) const { bool altered = false; // Try the mouse first for analog input @@ -80,14 +82,14 @@ namespace RTE { cursorPos += GetMouseMovement() * moveScale; altered = true; - // See if there's other analog input, only if the mouse isn't active (or the cursor will float if mouse is used!) + // See if there's other analog input, only if the mouse isn't active (or the cursor will float if mouse is used!) } else if (GetAnalogCursor().GetLargest() > 0.1F && !IsMouseControlled()) { // See how much to accelerate the joystick input based on how long the stick has been pushed around float acceleration = static_cast(0.5 + std::min(m_JoyAccelTimer.GetElapsedRealTimeS(), 0.5) * 6); cursorPos += GetAnalogCursor() * 10 * moveScale * acceleration; altered = true; - // Digital movement + // Digital movement } else { // See how much to accelerate the keyboard input based on how long any key has been pressed float acceleration = static_cast(0.25 + std::min(m_KeyAccelTimer.GetElapsedRealTimeS(), 0.75) * 6); @@ -112,25 +114,25 @@ namespace RTE { return altered; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Controller::GetDigitalAimSpeed() const { return m_Player != Players::NoPlayer ? g_UInputMan.GetControlScheme(m_Player)->GetDigitalAimSpeed() : 1.0F; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Controller::IsMouseControlled() const { return m_Player != Players::NoPlayer && g_UInputMan.GetControlScheme(m_Player)->GetDevice() == InputDevice::DEVICE_MOUSE_KEYB; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Controller::IsKeyboardOnlyControlled() const { return m_Player != Players::NoPlayer && g_UInputMan.GetControlScheme(m_Player)->GetDevice() == InputDevice::DEVICE_KEYB_ONLY; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Controller::IsGamepadControlled() const { bool isGamepadControlled = false; @@ -143,27 +145,29 @@ namespace RTE { return isGamepadControlled; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Controller::GetTeam() const { return m_ControlledActor ? m_ControlledActor->GetTeam() : m_Team; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Controller::SetTeam(short team) { - if (m_ControlledActor) { m_ControlledActor->SetTeam(team); } + if (m_ControlledActor) { + m_ControlledActor->SetTeam(team); + } m_Team = team; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Controller::Update() { if (IsDisabled()) { return; } - if (m_ControlledActor) { + if (m_ControlledActor) { m_Team = m_ControlledActor->GetTeam(); if (m_ControlledActor->GetHealth() == 0.0f || m_ControlledActor->GetStatus() == Actor::DYING || m_ControlledActor->GetStatus() == Actor::DEAD) { @@ -187,7 +191,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Controller::ResetCommandState() { // Reset all command states. @@ -198,7 +202,7 @@ namespace RTE { m_MouseMovement.Reset(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Controller::GetInputFromPlayer() { ResetCommandState(); @@ -210,10 +214,9 @@ namespace RTE { UpdatePlayerInput(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Controller::ShouldUpdateAIThisFrame() const - { + bool Controller::ShouldUpdateAIThisFrame() const { if (IsDisabled() || m_InputMode != InputMode::CIM_AI) { return false; } @@ -228,9 +231,9 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Controller & Controller::operator=(const Controller &rhs) { + Controller& Controller::operator=(const Controller& rhs) { if (this == &rhs) { return *this; } @@ -239,15 +242,14 @@ namespace RTE { return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::Override(const Controller &otherController) - { + void Controller::Override(const Controller& otherController) { RTEAssert(otherController.m_ControlledActor == m_ControlledActor, "Overriding a controller with a mismatched controlled actor!"); *this = otherController; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Controller::UpdatePlayerInput() { UpdatePlayerPieMenuInput(); @@ -255,17 +257,21 @@ namespace RTE { // Only actually switch when the change button(s) are released // BRAIN ACTOR if ((g_UInputMan.ElementHeld(m_Player, InputElements::INPUT_NEXT) && g_UInputMan.ElementPressed(m_Player, InputElements::INPUT_PREV)) || - (g_UInputMan.ElementPressed(m_Player, InputElements::INPUT_NEXT) && g_UInputMan.ElementHeld(m_Player, InputElements::INPUT_PREV))) { + (g_UInputMan.ElementPressed(m_Player, InputElements::INPUT_NEXT) && g_UInputMan.ElementHeld(m_Player, InputElements::INPUT_PREV))) { m_ControlStates[ControlState::ACTOR_BRAIN] = true; // Ignore the next releases of next and previous buttons so that the brain isn't switched away form immediate after using the brain shortcut m_NextIgnore = m_PrevIgnore = true; - // NEXT ACTOR + // NEXT ACTOR } else if (g_UInputMan.ElementReleased(m_Player, InputElements::INPUT_NEXT)) { - if (!m_NextIgnore) { m_ControlStates[ControlState::ACTOR_NEXT] = true; } + if (!m_NextIgnore) { + m_ControlStates[ControlState::ACTOR_NEXT] = true; + } m_NextIgnore = false; - // PREV ACTOR + // PREV ACTOR } else if (g_UInputMan.ElementReleased(m_Player, InputElements::INPUT_PREV)) { - if (!m_PrevIgnore) { m_ControlStates[ControlState::ACTOR_PREV] = true; } + if (!m_PrevIgnore) { + m_ControlStates[ControlState::ACTOR_PREV] = true; + } m_PrevIgnore = false; } else if (g_UInputMan.ElementReleased(m_Player, InputElements::INPUT_WEAPON_CHANGE_NEXT)) { m_WeaponChangeNextIgnore = false; @@ -298,9 +304,9 @@ namespace RTE { UpdatePlayerAnalogInput(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::UpdatePlayerPieMenuInput() { + void Controller::UpdatePlayerPieMenuInput() { // Holding of the switch buttons disables aiming later if (g_UInputMan.ElementHeld(m_Player, InputElements::INPUT_NEXT)) { m_ControlStates[ControlState::ACTOR_NEXT_PREP] = true; @@ -308,7 +314,7 @@ namespace RTE { } else if (g_UInputMan.ElementHeld(m_Player, InputElements::INPUT_PREV)) { m_ControlStates[ControlState::ACTOR_PREV_PREP] = true; m_ReleaseTimer.Reset(); - // No actions can be performed while switching actors, and short time thereafter + // No actions can be performed while switching actors, and short time thereafter } else if (m_ReleaseTimer.IsPastRealMS(m_ReleaseDelay)) { m_ControlStates[ControlState::WEAPON_FIRE] = g_UInputMan.ElementHeld(m_Player, InputElements::INPUT_FIRE); m_ControlStates[ControlState::AIM_SHARP] = g_UInputMan.ElementHeld(m_Player, InputElements::INPUT_AIM); @@ -384,7 +390,7 @@ namespace RTE { m_ControlStates[ControlState::WEAPON_FIRE] = false; m_ControlStates[ControlState::AIM_UP] = false; m_ControlStates[ControlState::AIM_DOWN] = false; - + if (m_ControlStates[ControlState::PIE_MENU_ACTIVE_DIGITAL]) { m_ControlStates[ControlState::MOVE_RIGHT] = false; m_ControlStates[ControlState::MOVE_LEFT] = false; @@ -397,7 +403,7 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Controller::UpdatePlayerAnalogInput() { // ANALOG joystick values @@ -409,8 +415,8 @@ namespace RTE { // Only change aim and move if not holding actor switch buttons - don't want to mess up AI's aim if (!m_ControlStates[ControlState::ACTOR_PREV_PREP] && !m_ControlStates[ControlState::ACTOR_NEXT_PREP] && m_ReleaseTimer.IsPastRealMS(m_ReleaseDelay)) { m_AnalogMove = move; - } - + } + if (!pieMenuActive || m_ControlStates[ControlState::PIE_MENU_ACTIVE_DIGITAL]) { m_AnalogAim = aim; } else { @@ -421,18 +427,18 @@ namespace RTE { } // If the joystick-controlled analog cursor is less than at the edge of input range, don't accelerate - if (GetAnalogCursor().MagnitudeIsLessThan(0.85F)) { - m_JoyAccelTimer.Reset(); + if (GetAnalogCursor().MagnitudeIsLessThan(0.85F)) { + m_JoyAccelTimer.Reset(); } - + // If the keyboard inputs for cursor movements is initially pressed, reset the acceleration timer if (IsState(ControlState::ACTOR_NEXT) || IsState(ControlState::ACTOR_PREV) || (IsState(ControlState::PRESS_LEFT) || IsState(ControlState::PRESS_RIGHT) || IsState(ControlState::PRESS_UP) || IsState(ControlState::PRESS_DOWN))) { m_KeyAccelTimer.Reset(); } // Translate analog aim input into sharp aim control state - if (m_AnalogAim.MagnitudeIsGreaterThan(0.1F) && !pieMenuActive) { - m_ControlStates[ControlState::AIM_SHARP] = true; + if (m_AnalogAim.MagnitudeIsGreaterThan(0.1F) && !pieMenuActive) { + m_ControlStates[ControlState::AIM_SHARP] = true; } // Disable sharp aim while moving - this also helps with keyboard vs mouse fighting when moving and aiming in opposite directions @@ -462,4 +468,4 @@ namespace RTE { m_ControlStates[ControlState::RELEASE_SECONDARY] = g_UInputMan.MouseButtonReleased(activeSecondary, m_Player); } } -} +} // namespace RTE diff --git a/Source/System/Controller.h b/Source/System/Controller.h index 18c510b9ea..b1ce22b4f4 100644 --- a/Source/System/Controller.h +++ b/Source/System/Controller.h @@ -71,7 +71,6 @@ namespace RTE { class Controller { public: - Vector m_AnalogMove; //!< Analog values for movement. Vector m_AnalogAim; //!< Analog values for aiming. Vector m_AnalogCursor; //!< Analog values for Pie Menu operation. @@ -96,20 +95,30 @@ namespace RTE { /// /// The controller input mode, like AI, player etc. /// The Actor this is supposed to control. Ownership is NOT transferred! - Controller(InputMode mode, Actor *controlledActor) { Clear(); Create(mode, controlledActor); } + Controller(InputMode mode, Actor* controlledActor) { + Clear(); + Create(mode, controlledActor); + } /// /// Constructor method used to instantiate a Controller object in system memory. Create() should be called before using the object. /// /// The controller input mode, like AI, player etc. /// Which human player is controlling this. - Controller(InputMode mode, int player = Players::PlayerOne) { Clear(); Create(mode, player); } + Controller(InputMode mode, int player = Players::PlayerOne) { + Clear(); + Create(mode, player); + } /// /// Copy constructor method used to instantiate a Controller object identical to an already existing one. /// /// A Controller object which is passed in by reference. - Controller(const Controller &reference) { if (this != &reference) { Create(reference); } } + Controller(const Controller& reference) { + if (this != &reference) { + Create(reference); + } + } /// /// Makes the Controller object ready for use. @@ -117,7 +126,7 @@ namespace RTE { /// The controller input mode, like AI, player etc. /// The Actor this is supposed to control. Ownership is NOT transferred! /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(InputMode mode, Actor *controlledActor); + int Create(InputMode mode, Actor* controlledActor); /// /// Makes the Controller object ready for use. @@ -125,14 +134,18 @@ namespace RTE { /// The controller input mode, like AI, player etc. /// Which player is controlling this. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(InputMode mode, int player) { m_InputMode = mode; m_Player = player; return 0; } + int Create(InputMode mode, int player) { + m_InputMode = mode; + m_Player = player; + return 0; + } /// /// Creates a Controller to be identical to another, by deep copy. /// /// A reference to the Controller to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Controller &reference); + int Create(const Controller& reference); #pragma endregion #pragma region Destruction @@ -160,7 +173,13 @@ namespace RTE { /// Sets whether this is a disabled controller that doesn't give any new output. /// /// Disabled or not. - void SetDisabled(bool disabled = true) { if (m_Disabled != disabled) { m_ReleaseTimer.Reset(); ResetCommandState(); } m_Disabled = disabled; } + void SetDisabled(bool disabled = true) { + if (m_Disabled != disabled) { + m_ReleaseTimer.Reset(); + ResetCommandState(); + } + m_Disabled = disabled; + } /// /// Shows whether the current controller is in a specific state. @@ -174,7 +193,10 @@ namespace RTE { /// /// Value of the state being set. - void SetState(ControlState controlState, bool setting = true) { RTEAssert(controlState >= 0 && controlState < ControlState::CONTROLSTATECOUNT, "Control state out of whack"); m_ControlStates[controlState] = setting; }; + void SetState(ControlState controlState, bool setting = true) { + RTEAssert(controlState >= 0 && controlState < ControlState::CONTROLSTATECOUNT, "Control state out of whack"); + m_ControlStates[controlState] = setting; + }; /// /// Gets the current mode of input for this Controller. @@ -186,7 +208,12 @@ namespace RTE { /// Sets the mode of input for this Controller. /// /// The new InputMode for this controller to use. - void SetInputMode(InputMode newMode) { if (m_InputMode != newMode) { m_ReleaseTimer.Reset(); } m_InputMode = newMode; } + void SetInputMode(InputMode newMode) { + if (m_InputMode != newMode) { + m_ReleaseTimer.Reset(); + } + m_InputMode = newMode; + } /// /// Gets the analog movement input data. @@ -198,7 +225,7 @@ namespace RTE { /// Sets the analog movement vector state of this. /// /// The new analog movement vector. - void SetAnalogMove(const Vector &newMove) { m_AnalogMove = newMove; } + void SetAnalogMove(const Vector& newMove) { m_AnalogMove = newMove; } /// /// Gets the analog aiming input data. @@ -210,7 +237,7 @@ namespace RTE { /// Sets the analog aiming vector state of this. /// /// The new analog aiming vector. - void SetAnalogAim(const Vector &newAim) { m_AnalogAim = newAim; } + void SetAnalogAim(const Vector& newAim) { m_AnalogAim = newAim; } /// /// Gets the analog menu input data. @@ -222,14 +249,14 @@ namespace RTE { /// Sets the analog cursor to the specified position. /// /// The position the analog cursor should be set to. - void SetAnalogCursor(const Vector &newAnalogCursor) { m_AnalogCursor = newAnalogCursor; } + void SetAnalogCursor(const Vector& newAnalogCursor) { m_AnalogCursor = newAnalogCursor; } /// /// Sets the analog cursor angle limits for the given player (does nothing for player -1). The limit end is always CCW from the limit start. /// /// The starting angle limit for the analog cursor. /// The ending angle limit for the analog cursor. - void SetAnalogCursorAngleLimits(float angleLimitStart, float angleLimitEnd) { m_AnalogCursorAngleLimits = { {angleLimitStart, angleLimitEnd}, true }; } + void SetAnalogCursorAngleLimits(float angleLimitStart, float angleLimitEnd) { m_AnalogCursorAngleLimits = {{angleLimitStart, angleLimitEnd}, true}; } /// /// Clears the analog cursor aim limits for the given player (does nothing for player -1). @@ -242,7 +269,7 @@ namespace RTE { /// The vector to alter. /// The scale of the input. 1.0 is 'normal'. /// Whether the vector was altered or not. - bool RelativeCursorMovement(Vector &cursorPos, float moveScale = 1.0F) const; + bool RelativeCursorMovement(Vector& cursorPos, float moveScale = 1.0F) const; /// /// Indicates whether this is listening to mouse input at all. @@ -266,7 +293,7 @@ namespace RTE { /// Gets the relative movement of the mouse since last update. /// /// The relative mouse movements, in both axes. - const Vector & GetMouseMovement() const { return m_MouseMovement; } + const Vector& GetMouseMovement() const { return m_MouseMovement; } /// /// Get the digital aim speed multiplier of the scheme associated with this Controller. @@ -290,7 +317,12 @@ namespace RTE { /// Sets which player's input this is listening to, and will enable player input mode. /// /// The player number. - void SetPlayer(int player) { m_Player = player; if (m_Player >= Players::PlayerOne) { m_InputMode = InputMode::CIM_PLAYER; } } + void SetPlayer(int player) { + m_Player = player; + if (m_Player >= Players::PlayerOne) { + m_InputMode = InputMode::CIM_PLAYER; + } + } /// /// Gets the Team number using this controller. @@ -308,13 +340,13 @@ namespace RTE { /// Gets which Actor is being controlled by this. 0 if none. /// /// A pointer to the Actor which is being controlled by this. Ownership is NOT transferred! - Actor * GetControlledActor() const { return m_ControlledActor; } + Actor* GetControlledActor() const { return m_ControlledActor; } /// /// Sets which Actor is supposed to be controlled by this. /// /// A pointer to a an Actor which is being controlled by this. Ownership is NOT transferred! - void SetControlledActor(Actor *controlledActor = nullptr) { m_ControlledActor = controlledActor; } + void SetControlledActor(Actor* controlledActor = nullptr) { m_ControlledActor = controlledActor; } /// /// Returns whether the AI should be updated this frame. @@ -336,7 +368,7 @@ namespace RTE { /// /// A Controller reference. /// A reference to the changed Controller. - Controller & operator=(const Controller &rhs); + Controller& operator=(const Controller& rhs); #pragma endregion #pragma region Misc @@ -349,7 +381,6 @@ namespace RTE { #pragma endregion protected: - static constexpr int m_ReleaseDelay = 250; //!< The delay between releasing a menu button and activating the regular controls, to avoid accidental input. std::array m_ControlStates; //!< Control states. @@ -357,7 +388,7 @@ namespace RTE { InputMode m_InputMode; //!< The current controller input mode, like AI, player etc. - Actor *m_ControlledActor; //!< The actor controlled by this. + Actor* m_ControlledActor; //!< The actor controlled by this. /// /// The last player this controlled. This is necessary so we still have some control after controlled's death. @@ -390,7 +421,6 @@ namespace RTE { std::pair, bool> m_AnalogCursorAngleLimits; //!< Analog aim value limits, as well as whether or not the limit is actually enabled. private: - #pragma region Update Breakdown /// /// Updates the player's inputs portion of this Controller. For breaking down Update into more comprehensible chunks. @@ -424,5 +454,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/DataModule.cpp b/Source/System/DataModule.cpp index 1f8b5e5ca6..1102d391fb 100644 --- a/Source/System/DataModule.cpp +++ b/Source/System/DataModule.cpp @@ -10,7 +10,7 @@ namespace RTE { const std::string DataModule::c_ClassName = "DataModule"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void DataModule::Clear() { m_IsUserdata = false; @@ -35,22 +35,26 @@ namespace RTE { m_IsMerchant = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::Create(const std::string &moduleName, const ProgressCallback &progressCallback) { + int DataModule::Create(const std::string& moduleName, const ProgressCallback& progressCallback) { m_FileName = std::filesystem::path(moduleName).generic_string(); m_ModuleID = g_PresetMan.GetModuleID(moduleName); m_CrabToHumanSpawnRatio = 0; // Report that we're starting to read a new DataModule - if (progressCallback) { progressCallback(m_FileName + " " + static_cast(-43) + " loading:", true); } + if (progressCallback) { + progressCallback(m_FileName + " " + static_cast(-43) + " loading:", true); + } Reader reader; std::string indexPath = g_PresetMan.GetFullModulePath(m_FileName + "/Index.ini"); std::string mergedIndexPath = g_PresetMan.GetFullModulePath(m_FileName + "/MergedIndex.ini"); // NOTE: This looks for the MergedIndex.ini generated by the index merger tool. The tool is mostly superseded by disabling loading visuals, but still provides some benefit. - if (std::filesystem::exists(mergedIndexPath)) { indexPath = mergedIndexPath; } + if (std::filesystem::exists(mergedIndexPath)) { + indexPath = mergedIndexPath; + } // If the module is a mod, read only its `index.ini` to validate its SupportedGameVersion. if (m_ModuleID >= g_PresetMan.GetOfficialModuleCount() && !m_IsUserdata && ReadModuleProperties(moduleName, progressCallback) >= 0) { @@ -61,18 +65,22 @@ namespace RTE { int result = Serializable::Create(reader); // Print an empty line to separate the end of a module from the beginning of the next one in the loading progress log. - if (progressCallback) { progressCallback(" ", true); } + if (progressCallback) { + progressCallback(" ", true); + } - if (m_ScanFolderContents) { result = FindAndRead(progressCallback); } + if (m_ScanFolderContents) { + result = FindAndRead(progressCallback); + } return result; } return -1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::CreateOnDiskAsUserdata(const std::string &moduleName, const std::string_view &friendlyName, bool ignoreMissingItems, bool scanFolderContents) { + bool DataModule::CreateOnDiskAsUserdata(const std::string& moduleName, const std::string_view& friendlyName, bool ignoreMissingItems, bool scanFolderContents) { std::string moduleNameWithPackageExtension = System::GetUserdataDirectory() + moduleName + (moduleName.ends_with(System::GetModulePackageExtension()) ? "" : System::GetModulePackageExtension()); if (Writer writer(moduleNameWithPackageExtension + "/Index.ini", false, true); writer.WriterOK()) { DataModule newModule; @@ -88,25 +96,27 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void DataModule::Destroy() { - for (const PresetEntry &preset : m_PresetList){ + for (const PresetEntry& preset: m_PresetList) { delete preset.m_EntityPreset; } delete m_SupportedGameVersion; Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::ReadModuleProperties(const std::string &moduleName, const ProgressCallback &progressCallback) { + int DataModule::ReadModuleProperties(const std::string& moduleName, const ProgressCallback& progressCallback) { m_FileName = moduleName; m_ModuleID = g_PresetMan.GetModuleID(moduleName); m_CrabToHumanSpawnRatio = 0; // Report that we're starting to read a new DataModule - if (progressCallback) { progressCallback(m_FileName + " " + static_cast(-43) + " reading properties:", true); } + if (progressCallback) { + progressCallback(m_FileName + " " + static_cast(-43) + " reading properties:", true); + } Reader reader; std::string indexPath(m_FileName + "/Index.ini"); @@ -118,12 +128,12 @@ namespace RTE { return -1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::ReadProperty(const std::string_view &propName, Reader &reader) { + int DataModule::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(if (!g_PresetMan.GetEntityPreset(reader)) { reader.ReportError("Could not understand Preset type!"); }) - - MatchProperty("ModuleName", { reader >> m_FriendlyName; }); + + MatchProperty("ModuleName", { reader >> m_FriendlyName; }); MatchProperty("Author", { reader >> m_Author; }); MatchProperty("Description", { std::string descriptionValue = reader.ReadPropValue(); @@ -141,11 +151,15 @@ namespace RTE { }); MatchProperty("IsFaction", { reader >> m_IsFaction; - if (m_IsMerchant) { m_IsFaction = false; } + if (m_IsMerchant) { + m_IsFaction = false; + } }); MatchProperty("IsMerchant", { reader >> m_IsMerchant; - if (m_IsMerchant) { m_IsFaction = false; } + if (m_IsMerchant) { + m_IsFaction = false; + } }); MatchProperty("SupportedGameVersion", { std::string versionText; @@ -154,7 +168,7 @@ namespace RTE { if (!m_SupportedGameVersion) { try { m_SupportedGameVersion = new version::Semver200_version(versionText); - } catch (version::Parse_error &) { + } catch (version::Parse_error&) { reader.ReportError("Couldn't parse the supported game version from the value provided: \"" + versionText + "\"!\nThe supported game version must be a valid semantic version number.\n"); } } @@ -202,9 +216,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::Save(Writer &writer) const { + int DataModule::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("ModuleName", m_FriendlyName); @@ -228,16 +242,16 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string DataModule::GetEntityDataLocation(const std::string &exactType, const std::string &instance) { - const Entity *foundEntity = GetEntityPreset(exactType, instance); + std::string DataModule::GetEntityDataLocation(const std::string& exactType, const std::string& instance) { + const Entity* foundEntity = GetEntityPreset(exactType, instance); if (foundEntity == nullptr) { return ""; } // Search for entity in instanceList - for (const PresetEntry &presetListEntry : m_PresetList) { + for (const PresetEntry& presetListEntry: m_PresetList) { if (presetListEntry.m_EntityPreset == foundEntity) { return presetListEntry.m_FileReadFrom; } @@ -247,15 +261,15 @@ namespace RTE { return ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Entity * DataModule::GetEntityPreset(const std::string &exactType, const std::string &instance) { + const Entity* DataModule::GetEntityPreset(const std::string& exactType, const std::string& instance) { if (exactType.empty() || instance == "None" || instance.empty()) { return nullptr; } if (auto classItr = m_TypeMap.find(exactType); classItr != m_TypeMap.end()) { // Find an instance of that EXACT type and name; derived types are not matched - for (const auto &[instanceName, entity] : classItr->second) { + for (const auto& [instanceName, entity]: classItr->second) { if (instanceName == instance && entity->GetClassName() == exactType) { return entity; } @@ -264,21 +278,21 @@ namespace RTE { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::AddEntityPreset(Entity *entityToAdd, bool overwriteSame, const std::string &readFromFile) { + bool DataModule::AddEntityPreset(Entity* entityToAdd, bool overwriteSame, const std::string& readFromFile) { // Fail if the entity is unnamed or it's not the original preset. - //TODO If we're overwriting, we may not want to fail if it's not the original preset, this needs to be investigated + // TODO If we're overwriting, we may not want to fail if it's not the original preset, this needs to be investigated if (entityToAdd->GetPresetName() == "None" || entityToAdd->GetPresetName().empty() || !entityToAdd->IsOriginalPreset()) { return false; } bool entityAdded = false; - if (Entity *existingEntity = GetEntityIfExactType(entityToAdd->GetClassName(), entityToAdd->GetPresetName())) { + if (Entity* existingEntity = GetEntityIfExactType(entityToAdd->GetClassName(), entityToAdd->GetPresetName())) { // If we're commanded to overwrite any collisions, then do so by cloning over the existing instance in the list // This way we're not invalidating any instance references that would have been taken out and held by clients if (overwriteSame) { - entityToAdd->SetModuleID(m_ModuleID); //TODO this is probably overwritten by Entity::Create(other), making it useless. Double-check this and remove this line if certain + entityToAdd->SetModuleID(m_ModuleID); // TODO this is probably overwritten by Entity::Create(other), making it useless. Double-check this and remove this line if certain entityToAdd->Clone(existingEntity); // Make sure the existing one is still marked as the Original Preset existingEntity->m_IsOriginalPreset = true; @@ -300,7 +314,7 @@ namespace RTE { } } else { entityToAdd->SetModuleID(m_ModuleID); - Entity *entityClone = entityToAdd->Clone(); + Entity* entityClone = entityToAdd->Clone(); // Mark the one we are about to add to the list as the Original now - this is now the actual Original Preset instance entityClone->m_IsOriginalPreset = true; @@ -316,25 +330,25 @@ namespace RTE { return entityAdded; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::GetGroupsWithType(std::list &groupList, const std::string &withType) { + bool DataModule::GetGroupsWithType(std::list& groupList, const std::string& withType) { bool foundAny = false; if (withType == "All" || withType.empty()) { - for (const std::string &groupRegisterEntry : m_GroupRegister) { + for (const std::string& groupRegisterEntry: m_GroupRegister) { groupList.push_back(groupRegisterEntry); // TODO: it seems weird that foundAny isn't set to true here, given that the list gets filled. // But I suppose no actual finding is done. Investigate this and see where it's called, maybe this should be changed } } else { if (auto classItr = m_TypeMap.find(withType); classItr != m_TypeMap.end()) { - const std::unordered_set *groupListPtr = nullptr; + const std::unordered_set* groupListPtr = nullptr; // Go through all the entities of that type, adding the groups they belong to - for (const auto &[instanceName, entity] : classItr->second) { + for (const auto& [instanceName, entity]: classItr->second) { groupListPtr = entity->GetGroups(); - for (const std::string &groupListEntry : *groupListPtr) { + for (const std::string& groupListEntry: *groupListPtr) { groupList.push_back(groupListEntry); // Get the grouped entities, without transferring ownership foundAny = true; } @@ -348,10 +362,9 @@ namespace RTE { return foundAny; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::GetAllOfOrNotOfGroups(std::list &entityList, const std::string &type, const std::vector &groups, bool excludeGroups) { + bool DataModule::GetAllOfOrNotOfGroups(std::list& entityList, const std::string& type, const std::vector& groups, bool excludeGroups) { if (groups.empty()) { return false; } @@ -361,10 +374,10 @@ namespace RTE { if (auto classItr = m_TypeMap.find((type.empty() || type == "All") ? "Entity" : type); classItr != m_TypeMap.end()) { RTEAssert(!classItr->second.empty(), "DataModule has class entry without instances in its map!?"); - for (const auto &[instanceName, entity] : classItr->second) { + for (const auto& [instanceName, entity]: classItr->second) { if (excludeGroups) { bool excludeEntity = false; - for (const std::string &group : groups) { + for (const std::string& group: groups) { if (entity->IsInGroup(group)) { excludeEntity = true; break; @@ -375,7 +388,7 @@ namespace RTE { foundAny = true; } } else { - for (const std::string &group : groups) { + for (const std::string& group: groups) { if (entity->IsInGroup(group)) { entityList.emplace_back(entity); foundAny = true; @@ -388,17 +401,17 @@ namespace RTE { return foundAny; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::GetAllOfType(std::list &entityList, const std::string &type) { + bool DataModule::GetAllOfType(std::list& entityList, const std::string& type) { if (type.empty()) { return false; } - if (auto classItr = m_TypeMap.find(type); classItr != m_TypeMap.end()) { + if (auto classItr = m_TypeMap.find(type); classItr != m_TypeMap.end()) { RTEAssert(!classItr->second.empty(), "DataModule has class entry without instances in its map!?"); - for (const auto &[instanceName, entity] : classItr->second) { + for (const auto& [instanceName, entity]: classItr->second) { entityList.push_back(entity); // Get the entities, without transferring ownership } return true; @@ -406,7 +419,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool DataModule::AddMaterialMapping(unsigned char fromID, unsigned char toID) { RTEAssert(fromID > 0 && fromID < c_PaletteEntriesNumber && toID > 0 && toID < c_PaletteEntriesNumber, "Tried to make an out-of-bounds Material mapping"); @@ -417,59 +430,61 @@ namespace RTE { return clear; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int DataModule::LoadScripts() const { if (m_ScriptPath.empty()) { return 0; } - + g_LuaMan.GetMasterScriptState().RunScriptFile(m_ScriptPath); - for (LuaStateWrapper &luaState : g_LuaMan.GetThreadedScriptStates()) { + for (LuaStateWrapper& luaState: g_LuaMan.GetThreadedScriptStates()) { luaState.RunScriptFile(m_ScriptPath); } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void DataModule::ReloadAllScripts() const { - for (const PresetEntry &presetListEntry : m_PresetList) { + for (const PresetEntry& presetListEntry: m_PresetList) { presetListEntry.m_EntityPreset->ReloadScripts(); } LoadScripts(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::FindAndRead(const ProgressCallback &progressCallback) { + int DataModule::FindAndRead(const ProgressCallback& progressCallback) { int result = 0; const std::string directoryToScan = g_PresetMan.GetFullModulePath(m_FileName); - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + directoryToScan)) { + for (const std::filesystem::directory_entry& directoryEntry: std::filesystem::directory_iterator(System::GetWorkingDirectory() + directoryToScan)) { if (directoryEntry.path().extension() == ".ini" && directoryEntry.path().filename() != "Index.ini") { Reader iniReader; if (iniReader.Create(directoryToScan + "/" + directoryEntry.path().filename().generic_string(), false, progressCallback) >= 0) { result = Serializable::CreateSerializable(iniReader, false, true, true); - if (progressCallback) { progressCallback(" ", true); } + if (progressCallback) { + progressCallback(" ", true); + } } } } return result; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: This method is almost identical to GetEntityPreset, except it doesn't return a const Entity *. // Investigate if the latter needs to return const (based on what's using it) and if not, get rid of this and replace its uses. At the very least, consider renaming this // See https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/87 - Entity * DataModule::GetEntityIfExactType(const std::string &exactType, const std::string &presetName) { + Entity* DataModule::GetEntityIfExactType(const std::string& exactType, const std::string& presetName) { if (exactType.empty() || presetName == "None" || presetName.empty()) { return nullptr; } if (auto classItr = m_TypeMap.find(exactType); classItr != m_TypeMap.end()) { // Find an instance of that EXACT type and name; derived types are not matched - for (const auto &[instanceName, entity] : classItr->second) { + for (const auto& [instanceName, entity]: classItr->second) { if (instanceName == presetName && entity->GetClassName() == exactType) { return entity; } @@ -478,29 +493,29 @@ namespace RTE { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::AddToTypeMap(Entity *entityToAdd) { + bool DataModule::AddToTypeMap(Entity* entityToAdd) { if (!entityToAdd || entityToAdd->GetPresetName() == "None" || entityToAdd->GetPresetName().empty()) { return false; } // Walk up the class hierarchy till we reach the top, adding an entry of the passed in entity into each typelist as we go along - for (const Entity::ClassInfo *pClass = &(entityToAdd->GetClass()); pClass != nullptr; pClass = pClass->GetParent()) { + for (const Entity::ClassInfo* pClass = &(entityToAdd->GetClass()); pClass != nullptr; pClass = pClass->GetParent()) { auto classItr = m_TypeMap.find(pClass->GetName()); // No instances of this entity have been added yet so add a class category for it if (classItr == m_TypeMap.end()) { - classItr = (m_TypeMap.insert(std::pair>>(pClass->GetName(), std::list>()))).first; + classItr = (m_TypeMap.insert(std::pair>>(pClass->GetName(), std::list>()))).first; } // NOTE We're adding the entity to the class category list but not transferring ownership. Also, we're not checking for collisions as they're assumed to have been checked for already - (*classItr).second.push_back(std::pair(entityToAdd->GetPresetName(), entityToAdd)); + (*classItr).second.push_back(std::pair(entityToAdd->GetPresetName(), entityToAdd)); } return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void DataModule::CheckSupportedGameVersion() const { if (*m_SupportedGameVersion == c_GameVersion) { @@ -526,4 +541,4 @@ namespace RTE { RTEAssert(majorVersionMatch && minorVersionInRange, m_FileName + " was developed for Cortex Command v" + m_SupportedGameVersion->str() + ", so this version of Cortex Command (v" + c_GameVersion.str() + ") may not support it.\n" + contactAuthor); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/DataModule.h b/Source/System/DataModule.h index 79a4a99bd1..67f47c4674 100644 --- a/Source/System/DataModule.h +++ b/Source/System/DataModule.h @@ -4,7 +4,7 @@ #include "ContentFile.h" #include "Constants.h" -//struct DATAFILE; // DataFile loading not implemented. +// struct DATAFILE; // DataFile loading not implemented. struct BITMAP; namespace version { @@ -22,7 +22,6 @@ namespace RTE { friend struct SystemLuaBindings; public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -49,7 +48,10 @@ namespace RTE { /// /// A string defining the path to where the content file itself is located, either within the package file, or directly on the disk. /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. - DataModule(const std::string &moduleName, const ProgressCallback &progressCallback = nullptr) { Clear(); Create(moduleName, progressCallback); } + DataModule(const std::string& moduleName, const ProgressCallback& progressCallback = nullptr) { + Clear(); + Create(moduleName, progressCallback); + } /// /// Makes the DataModule object ready for use. This needs to be called after PresetMan is created. @@ -58,7 +60,7 @@ namespace RTE { /// A string defining the name of this DataModule, e.g. "MyModule.rte". /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const std::string &moduleName, const ProgressCallback &progressCallback = nullptr); + int Create(const std::string& moduleName, const ProgressCallback& progressCallback = nullptr); /// /// Creates a new DataModule directory with "Index.ini" on disk to be used for userdata. Does NOT instantiate the newly created DataModule. @@ -68,7 +70,7 @@ namespace RTE { /// Whether module loader should scan for any .ini's inside module folder instead of loading files defined in IncludeFile only. /// Whether module loader should ignore missing items in this module. /// Whether the DataModule was successfully created on disk. - static bool CreateOnDiskAsUserdata(const std::string &moduleName, const std::string_view &friendlyName, bool scanFolderContents = false, bool ignoreMissingItems = false); + static bool CreateOnDiskAsUserdata(const std::string& moduleName, const std::string_view& friendlyName, bool scanFolderContents = false, bool ignoreMissingItems = false); #pragma endregion #pragma region Destruction @@ -95,7 +97,7 @@ namespace RTE { /// A string defining the name of this DataModule, e.g. "MyModule.rte". /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int ReadModuleProperties(const std::string &moduleName, const ProgressCallback &progressCallback = nullptr); + int ReadModuleProperties(const std::string& moduleName, const ProgressCallback& progressCallback = nullptr); /// /// Returns true if loader should ignore missing items in this module. @@ -120,25 +122,25 @@ namespace RTE { /// Gets the file name of this DataModule, e.g. "MyMod.rte". /// /// A string with the data module file name. - const std::string & GetFileName() const { return m_FileName; } + const std::string& GetFileName() const { return m_FileName; } /// /// Gets the friendly name of this DataModule, e.g. "My Great Mod". /// /// A string with the data module's friendly name. - const std::string & GetFriendlyName() const { return m_FriendlyName; } + const std::string& GetFriendlyName() const { return m_FriendlyName; } /// /// Gets the author name of this DataModule, e.g. "Data Realms, LLC". /// /// A string with the author's name. - const std::string & GetAuthor() const { return m_Author; } + const std::string& GetAuthor() const { return m_Author; } /// /// Gets the description of this DataModule's contents. /// /// A string with the description. - const std::string & GetDescription() const { return m_Description; } + const std::string& GetDescription() const { return m_Description; } /// /// Gets whether this DataModule is considered a faction. @@ -162,7 +164,7 @@ namespace RTE { /// Gets the BITMAP that visually represents this DataModule, for use in menus. /// /// BITMAP pointer that might have the icon. 0 is very possible. - BITMAP * GetIcon() const { return m_Icon; } + BITMAP* GetIcon() const { return m_Icon; } /// /// Returns crab-to-human spawn ration for this tech. @@ -174,7 +176,7 @@ namespace RTE { /// Gets the faction BuyMenu theme data of this DataModule. /// /// The faction BuyMenu theme information of this DataModule - const BuyMenuTheme & GetFactionBuyMenuTheme() const { return m_BuyMenuTheme; } + const BuyMenuTheme& GetFactionBuyMenuTheme() const { return m_BuyMenuTheme; } #pragma endregion #pragma region Entity Mapping @@ -184,7 +186,7 @@ namespace RTE { /// The type name of the derived Entity. Ownership is NOT transferred! /// The instance name of the derived Entity instance. /// The file path of the data file that the specified Entity was read from. If no Entity of that description was found, "" is returned. - std::string GetEntityDataLocation(const std::string &exactType, const std::string &instance); + std::string GetEntityDataLocation(const std::string& exactType, const std::string& instance); /// /// Gets a previously read in (defined) Entity, by exact type and instance name. Ownership is NOT transferred! @@ -192,7 +194,7 @@ namespace RTE { /// The exact type name of the derived Entity instance to get. /// The instance name of the derived Entity instance. /// A pointer to the requested Entity instance. 0 if no Entity with that derived type or instance name was found. Ownership is NOT transferred! - const Entity * GetEntityPreset(const std::string &exactType, const std::string &instance); + const Entity* GetEntityPreset(const std::string& exactType, const std::string& instance); /// /// Adds an Entity instance's pointer and name associations to the internal list of already read in Entities. Ownership is NOT transferred! @@ -211,19 +213,23 @@ namespace RTE { /// Whether or not a copy of the passed-in instance was successfully inserted into the module. /// False will be returned if there already was an instance of that class and instance name inserted previously, unless overwritten. /// - bool AddEntityPreset(Entity *entityToAdd, bool overwriteSame = false, const std::string &readFromFile = "Same"); + bool AddEntityPreset(Entity* entityToAdd, bool overwriteSame = false, const std::string& readFromFile = "Same"); /// /// Gets the list of all registered Entity groups of this. /// /// The list of all groups. Ownership is not transferred. - const std::list * GetGroupRegister() const { return &m_GroupRegister; } + const std::list* GetGroupRegister() const { return &m_GroupRegister; } /// /// Registers the existence of an Entity group in this module. /// /// The group to register. - void RegisterGroup(const std::string &newGroup) { m_GroupRegister.push_back(newGroup); m_GroupRegister.sort(); m_GroupRegister.unique(); } + void RegisterGroup(const std::string& newGroup) { + m_GroupRegister.push_back(newGroup); + m_GroupRegister.sort(); + m_GroupRegister.unique(); + } /// /// Fills out a list with all groups registered with this that contain any objects of a specific type and it derivatives. @@ -231,7 +237,7 @@ namespace RTE { /// The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! /// The name of the type to only get groups of. /// Whether any groups with the specified type were found. - bool GetGroupsWithType(std::list &groupList, const std::string &withType); + bool GetGroupsWithType(std::list& groupList, const std::string& withType); /// /// Adds to a list all previously read in (defined) Entities which are associated with several specific groups. @@ -240,7 +246,7 @@ namespace RTE { /// A list of groups to look for. /// The name of the least common denominator type of the Entities you want. "All" will look at all types. /// Whether any Entities were found and added to the list. - bool GetAllOfGroups(std::list &entityList, const std::vector &groups, const std::string &type) { return GetAllOfOrNotOfGroups(entityList, type, groups, false); } + bool GetAllOfGroups(std::list& entityList, const std::vector& groups, const std::string& type) { return GetAllOfOrNotOfGroups(entityList, type, groups, false); } /// /// Adds to a list all previously read in (defined) Entities which are not associated with several specific groups. @@ -249,7 +255,7 @@ namespace RTE { /// A list of groups to exclude. /// The name of the least common denominator type of the Entities you want. "All" will look at all types. /// Whether any Entities were found and added to the list. - bool GetAllNotOfGroups(std::list &entityList, const std::vector &groups, const std::string &type) { return GetAllOfOrNotOfGroups(entityList, type, groups, true); } + bool GetAllNotOfGroups(std::list& entityList, const std::vector& groups, const std::string& type) { return GetAllOfOrNotOfGroups(entityList, type, groups, true); } /// /// Adds to a list all previously read in (defined) Entities, by inexact type. @@ -257,7 +263,7 @@ namespace RTE { /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! /// The name of the least common denominator type of the Entities you want. "All" will look at all types. /// Whether any Entities were found and added to the list. - bool GetAllOfType(std::list &objectList, const std::string &type); + bool GetAllOfType(std::list& objectList, const std::string& type); #pragma endregion #pragma region Material Mapping @@ -273,7 +279,7 @@ namespace RTE { /// Gets the entire Material mapping array local to this DataModule. /// /// A const reference to the entire local mapping array, 256 unsigned chars. Ownership is NOT transferred! - const std::array & GetAllMaterialMappings() const { return m_MaterialMappings; } + const std::array& GetAllMaterialMappings() const { return m_MaterialMappings; } /// /// Adds a Material mapping local to a DataModule. @@ -299,7 +305,6 @@ namespace RTE { #pragma endregion protected: - /// /// Holds and owns the actual object instance pointer, and the location of the data file it was read from, as well as where in that file. /// @@ -307,9 +312,10 @@ namespace RTE { /// /// Constructor method used to instantiate a PresetEntry object in system memory. /// - PresetEntry(Entity *preset, const std::string &file) : m_EntityPreset(preset), m_FileReadFrom(file) {} + PresetEntry(Entity* preset, const std::string& file) : + m_EntityPreset(preset), m_FileReadFrom(file) {} - Entity *m_EntityPreset; //!< Owned by this. + Entity* m_EntityPreset; //!< Owned by this. std::string m_FileReadFrom; //!< Where the instance was read from. }; @@ -324,18 +330,18 @@ namespace RTE { std::string m_ScriptPath; //!< Path to script to execute when this module is loaded. bool m_IsFaction; //!< Whether this data module is considered a faction. bool m_IsMerchant; //!< Whether this data module is considered a merchant. - version::Semver200_version *m_SupportedGameVersion; //!< Game version this DataModule supports. Needs to satisfy Caret Version Range for this DataModule to be allowed. Base DataModules don't need this. + version::Semver200_version* m_SupportedGameVersion; //!< Game version this DataModule supports. Needs to satisfy Caret Version Range for this DataModule to be allowed. Base DataModules don't need this. int m_Version; //!< Version number, starting with 1. int m_ModuleID; //!< ID number assigned to this upon loading, for internal use only, don't reflect in ini's. ContentFile m_IconFile; //!< File to the icon/symbol bitmap. - BITMAP *m_Icon; //!< Bitmap with the icon loaded from above file. + BITMAP* m_Icon; //!< Bitmap with the icon loaded from above file. BuyMenuTheme m_BuyMenuTheme; //!< Faction BuyMenu theme data. float m_CrabToHumanSpawnRatio; //!< Crab-to-human Spawn ratio to replace value from Constants.lua. - std::list m_EntityList; //!< A list of loaded entities solely for the purpose of enumeration presets from Lua. + std::list m_EntityList; //!< A list of loaded entities solely for the purpose of enumeration presets from Lua. std::list m_GroupRegister; //!< List of all Entity groups ever registered in this, all uniques. std::array m_MaterialMappings; //!< Material mappings local to this DataModule. @@ -352,10 +358,9 @@ namespace RTE { /// There can be multiple entries of the same instance name in any of the type sub-maps, but only ONE whose exact class is that of the type-list! /// The Entity instances are NOT owned by this map. /// - std::unordered_map>> m_TypeMap; + std::unordered_map>> m_TypeMap; private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. #pragma region INI Handling @@ -369,7 +374,7 @@ namespace RTE { /// /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int FindAndRead(const ProgressCallback &progressCallback = nullptr); + int FindAndRead(const ProgressCallback& progressCallback = nullptr); #pragma endregion #pragma region Entity Mapping @@ -381,7 +386,7 @@ namespace RTE { /// The groups to look for. /// Whether Entities belonging to the specified group or groups should be excluded. /// Whether any Entities were found and added to the list. - bool GetAllOfOrNotOfGroups(std::list &entityList, const std::string &type, const std::vector &groups, bool excludeGroups); + bool GetAllOfOrNotOfGroups(std::list& entityList, const std::string& type, const std::vector& groups, bool excludeGroups); /// /// Checks if the type map has an instance added of a specific name and exact type. @@ -390,7 +395,7 @@ namespace RTE { /// The exact type name to look for. /// The exact PresetName to look for. /// The found Entity Preset of the exact type and name, if found. - Entity * GetEntityIfExactType(const std::string &exactType, const std::string &presetName); + Entity* GetEntityIfExactType(const std::string& exactType, const std::string& presetName); /// /// Adds a newly added preset instance to the type map, where it will end up in every type-list of every class it derived from as well. @@ -400,7 +405,7 @@ namespace RTE { /// /// The new object instance to add. OWNERSHIP IS NOT TRANSFERRED! /// Whether the Entity was added successfully or not. - bool AddToTypeMap(Entity *entityToAdd); + bool AddToTypeMap(Entity* entityToAdd); #pragma endregion /// @@ -409,8 +414,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - DataModule(const DataModule &reference) = delete; - DataModule & operator=(const DataModule &rhs) = delete; + DataModule(const DataModule& reference) = delete; + DataModule& operator=(const DataModule& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Entity.cpp b/Source/System/Entity.cpp index be6982a4b8..3a7fc9a5b6 100644 --- a/Source/System/Entity.cpp +++ b/Source/System/Entity.cpp @@ -7,9 +7,9 @@ namespace RTE { Entity::ClassInfo Entity::m_sClass("Entity"); - Entity::ClassInfo * Entity::ClassInfo::s_ClassHead = 0; + Entity::ClassInfo* Entity::ClassInfo::s_ClassHead = 0; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Entity::Clear() { m_PresetName = "None"; @@ -20,36 +20,35 @@ namespace RTE { m_RandomWeight = 100; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Entity::Create() { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::Create(const Entity &reference) { + int Entity::Create(const Entity& reference) { m_PresetName = reference.m_PresetName; // Note how m_IsOriginalPreset is NOT assigned, automatically indicating that the copy is not an original Preset! m_DefinedInModule = reference.m_DefinedInModule; m_PresetDescription = reference.m_PresetDescription; - for (const std::string &group : reference.m_Groups) { + for (const std::string& group: reference.m_Groups) { m_Groups.emplace(group); } m_RandomWeight = reference.m_RandomWeight; return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::ReadProperty(const std::string_view &propName, Reader &reader) { + int Entity::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList( - // Search for a property name match failed! - // TODO: write this out to some log file - return Serializable::ReadProperty(propName, reader); - ); - + // Search for a property name match failed! + // TODO: write this out to some log file + return Serializable::ReadProperty(propName, reader);); + MatchProperty("CopyOf", { std::string refName = reader.ReadPropValue(); if (refName != "None") { @@ -66,7 +65,9 @@ namespace RTE { SetPresetName(reader.ReadPropValue()); // Preset name might have "[ModuleName]/" preceding it, detect it here and select proper module! int slashPos = m_PresetName.find_first_of('/'); - if (slashPos != std::string::npos) { m_PresetName = m_PresetName.substr(slashPos + 1); } + if (slashPos != std::string::npos) { + m_PresetName = m_PresetName.substr(slashPos + 1); + } // Mark this so that the derived class knows it should be added to the PresetMan when it's done reading all properties. m_IsOriginalPreset = true; // Indicate where this was read from @@ -101,9 +102,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::Save(Writer &writer) const { + int Entity::Save(Writer& writer) const { Serializable::Save(writer); // Is an original preset definition @@ -113,7 +114,7 @@ namespace RTE { if (!m_PresetDescription.empty()) { writer.NewPropertyWithValue("Description", m_PresetDescription); } - // Only write out a copy reference if there is one + // Only write out a copy reference if there is one } else if (!m_PresetName.empty() && m_PresetName != "None") { writer.NewPropertyWithValue("CopyOf", GetModuleAndPresetName()); } @@ -121,15 +122,15 @@ namespace RTE { // TODO: Make proper save system that knows not to save redundant data! /* for (auto itr = m_Groups.begin(); itr != m_Groups.end(); ++itr) { - writer.NewPropertyWithValue("AddToGroup", *itr); + writer.NewPropertyWithValue("AddToGroup", *itr); } */ return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::SavePresetCopy(Writer &writer) const { + int Entity::SavePresetCopy(Writer& writer) const { // Can only save out copies with this if (m_IsOriginalPreset) { RTEAbort("Tried to save out a pure Preset Copy Reference from an original Preset!"); @@ -142,19 +143,19 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Entity * Entity::GetPreset() const { - return g_PresetMan.GetEntityPreset(GetClassName(), GetPresetName(), m_DefinedInModule); - } + const Entity* Entity::GetPreset() const { + return g_PresetMan.GetEntityPreset(GetClassName(), GetPresetName(), m_DefinedInModule); + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string Entity::GetModuleAndPresetName() const { if (m_DefinedInModule < 0) { return GetPresetName(); } - const DataModule *dataModule = g_PresetMan.GetDataModule(m_DefinedInModule); + const DataModule* dataModule = g_PresetMan.GetDataModule(m_DefinedInModule); if (!dataModule) { return GetPresetName(); @@ -162,18 +163,18 @@ namespace RTE { return dataModule->GetFileName() + "/" + GetPresetName(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string Entity::GetModuleName() const { if (m_DefinedInModule >= 0) { - if (const DataModule *dataModule = g_PresetMan.GetDataModule(m_DefinedInModule)) { + if (const DataModule* dataModule = g_PresetMan.GetDataModule(m_DefinedInModule)) { return dataModule->GetFileName(); } } return ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Entity::MigrateToModule(int whichModule) { if (m_DefinedInModule == whichModule) { @@ -184,9 +185,9 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader & operator>>(Reader &reader, Entity &operand) { + Reader& operator>>(Reader& reader, Entity& operand) { // Get this before reading Entity, since if it's the last one in its datafile, the stream will show the parent file instead std::string objectFilePath = reader.GetCurrentFilePath(); // Read the Entity from the file and try to add it to PresetMan @@ -196,9 +197,9 @@ namespace RTE { return reader; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader & operator>>(Reader &reader, Entity *operand) { + Reader& operator>>(Reader& reader, Entity* operand) { if (operand) { // Get this before reading Entity, since if it's the last one in its datafile, the stream will show the parent file instead std::string objectFilePath = reader.GetCurrentFilePath(); @@ -211,38 +212,38 @@ namespace RTE { return reader; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Entity::ClassInfo::ClassInfo(const std::string &name, ClassInfo *parentInfo, MemoryAllocate allocFunc, MemoryDeallocate deallocFunc, Entity * (*newFunc)(), int allocBlockCount) : - m_Name(name), - m_ParentInfo(parentInfo), - m_Allocate(allocFunc), - m_Deallocate(deallocFunc), - m_NewInstance(newFunc), - m_NextClass(s_ClassHead) { - s_ClassHead = this; + Entity::ClassInfo::ClassInfo(const std::string& name, ClassInfo* parentInfo, MemoryAllocate allocFunc, MemoryDeallocate deallocFunc, Entity* (*newFunc)(), int allocBlockCount) : + m_Name(name), + m_ParentInfo(parentInfo), + m_Allocate(allocFunc), + m_Deallocate(deallocFunc), + m_NewInstance(newFunc), + m_NextClass(s_ClassHead) { + s_ClassHead = this; - m_AllocatedPool.clear(); - m_PoolAllocBlockCount = (allocBlockCount > 0) ? allocBlockCount : 10; - } + m_AllocatedPool.clear(); + m_PoolAllocBlockCount = (allocBlockCount > 0) ? allocBlockCount : 10; + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::list Entity::ClassInfo::GetClassNames() { std::list retList; - for (const ClassInfo *itr = s_ClassHead; itr != 0; itr = itr->m_NextClass) { + for (const ClassInfo* itr = s_ClassHead; itr != 0; itr = itr->m_NextClass) { retList.push_back(itr->GetName()); } return retList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Entity::ClassInfo * Entity::ClassInfo::GetClass(const std::string &name) { + const Entity::ClassInfo* Entity::ClassInfo::GetClass(const std::string& name) { if (name.empty() || name == "None") { return 0; } - for (const ClassInfo *itr = s_ClassHead; itr != 0; itr = itr->m_NextClass) { + for (const ClassInfo* itr = s_ClassHead; itr != 0; itr = itr->m_NextClass) { if (itr->GetName() == name) { return itr; } @@ -250,19 +251,23 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Entity::ClassInfo::FillAllPools(int fillAmount) { - for (ClassInfo *itr = s_ClassHead; itr != 0; itr = itr->m_NextClass) { - if (itr->IsConcrete()) { itr->FillPool(fillAmount); } + for (ClassInfo* itr = s_ClassHead; itr != 0; itr = itr->m_NextClass) { + if (itr->IsConcrete()) { + itr->FillPool(fillAmount); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Entity::ClassInfo::FillPool(int fillAmount) { // Default to the set block allocation size if fillAmount is 0 - if (fillAmount <= 0) { fillAmount = m_PoolAllocBlockCount; } + if (fillAmount <= 0) { + fillAmount = m_PoolAllocBlockCount; + } // If concrete class, fill up the pool with pre-allocated memory blocks the size of the type if (m_Allocate && fillAmount > 0) { @@ -272,9 +277,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Entity::ClassInfo::IsClassOrChildClassOf(const ClassInfo *classInfoToCheck) const { + bool Entity::ClassInfo::IsClassOrChildClassOf(const ClassInfo* classInfoToCheck) const { if (GetName() == classInfoToCheck->GetName()) { return true; } else if (m_ParentInfo) { @@ -283,18 +288,20 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void * Entity::ClassInfo::GetPoolMemory() { + void* Entity::ClassInfo::GetPoolMemory() { std::lock_guard guard(m_Mutex); RTEAssert(IsConcrete(), "Trying to get pool memory of an abstract Entity class!"); // If the pool is empty, then fill it up again with as many instances as we are set to - if (m_AllocatedPool.empty()) { FillPool((m_PoolAllocBlockCount > 0) ? m_PoolAllocBlockCount : 10); } + if (m_AllocatedPool.empty()) { + FillPool((m_PoolAllocBlockCount > 0) ? m_PoolAllocBlockCount : 10); + } // Get the instance in the top of the pool and pop it off - void *foundMemory = m_AllocatedPool.back(); + void* foundMemory = m_AllocatedPool.back(); m_AllocatedPool.pop_back(); RTEAssert(foundMemory, "Could not find an available instance in the pool, even after increasing its size!"); @@ -305,9 +312,9 @@ namespace RTE { return foundMemory; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::ClassInfo::ReturnPoolMemory(void *returnedMemory) { + int Entity::ClassInfo::ReturnPoolMemory(void* returnedMemory) { if (!returnedMemory) { return 0; } @@ -320,11 +327,13 @@ namespace RTE { return m_InstancesInUse; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Entity::ClassInfo::DumpPoolMemoryInfo(const Writer &fileWriter) { - for (const ClassInfo *itr = s_ClassHead; itr != nullptr; itr = itr->m_NextClass) { - if (itr->IsConcrete()) { fileWriter.NewLineString(itr->GetName() + ": " + std::to_string(itr->m_InstancesInUse), false); } + void Entity::ClassInfo::DumpPoolMemoryInfo(const Writer& fileWriter) { + for (const ClassInfo* itr = s_ClassHead; itr != nullptr; itr = itr->m_NextClass) { + if (itr->IsConcrete()) { + fileWriter.NewLineString(itr->GetName() + ": " + std::to_string(itr->m_InstancesInUse), false); + } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/Entity.h b/Source/System/Entity.h index 258b813462..438d74c4f7 100644 --- a/Source/System/Entity.h +++ b/Source/System/Entity.h @@ -10,42 +10,44 @@ namespace RTE { typedef std::function MemoryDeallocate; //!< Convenient name definition for the memory deallocation callback function. #pragma region Global Macro Definitions - #define AbstractClassInfo(TYPE, PARENT) \ - Entity::ClassInfo TYPE::m_sClass(#TYPE, &PARENT::m_sClass); - - #define ConcreteClassInfo(TYPE, PARENT, BLOCKCOUNT) \ - Entity::ClassInfo TYPE::m_sClass(#TYPE, &PARENT::m_sClass, TYPE::Allocate, TYPE::Deallocate, TYPE::NewInstance, BLOCKCOUNT); - - #define ConcreteSubClassInfo(TYPE, SUPER, PARENT, BLOCKCOUNT) \ - Entity::ClassInfo SUPER::TYPE::m_sClass(#TYPE, &PARENT::m_sClass, SUPER::TYPE::Allocate, SUPER::TYPE::Deallocate, SUPER::TYPE::NewInstance, BLOCKCOUNT); - - /// - /// Convenience macro to cut down on duplicate ClassInfo methods in classes that extend Entity. - /// - #define ClassInfoGetters \ - const Entity::ClassInfo & GetClass() const override { return m_sClass; } \ - const std::string & GetClassName() const override { return m_sClass.GetName(); } - - /// - /// Static method used in conjunction with ClassInfo to allocate an Entity. - /// This function is passed into the constructor of this Entity's static ClassInfo's constructor, so that it can instantiate MovableObjects. - /// - /// A pointer to the newly dynamically allocated Entity. Ownership is transferred as well. - #define EntityAllocation(TYPE) \ - static void * operator new (size_t size) { return TYPE::m_sClass.GetPoolMemory(); } \ - static void operator delete (void *instance) { TYPE::m_sClass.ReturnPoolMemory(instance); } \ - static void * operator new (size_t size, void *p) throw() { return p; } \ - static void operator delete (void *, void *) throw() { } \ - static void * Allocate() { return malloc(sizeof(TYPE)); } \ - static void Deallocate(void *instance) { free(instance); } \ - static Entity * NewInstance() { return new TYPE; } \ - Entity * Clone(Entity *cloneTo = nullptr) const override { \ - TYPE *ent = cloneTo ? dynamic_cast(cloneTo) : new TYPE(); \ - RTEAssert(ent, "Tried to clone to an incompatible instance!"); \ - if (cloneTo) { ent->Destroy(); } \ - ent->Create(*this); \ - return ent; \ - } +#define AbstractClassInfo(TYPE, PARENT) \ + Entity::ClassInfo TYPE::m_sClass(#TYPE, &PARENT::m_sClass); + +#define ConcreteClassInfo(TYPE, PARENT, BLOCKCOUNT) \ + Entity::ClassInfo TYPE::m_sClass(#TYPE, &PARENT::m_sClass, TYPE::Allocate, TYPE::Deallocate, TYPE::NewInstance, BLOCKCOUNT); + +#define ConcreteSubClassInfo(TYPE, SUPER, PARENT, BLOCKCOUNT) \ + Entity::ClassInfo SUPER::TYPE::m_sClass(#TYPE, &PARENT::m_sClass, SUPER::TYPE::Allocate, SUPER::TYPE::Deallocate, SUPER::TYPE::NewInstance, BLOCKCOUNT); + +/// +/// Convenience macro to cut down on duplicate ClassInfo methods in classes that extend Entity. +/// +#define ClassInfoGetters \ + const Entity::ClassInfo& GetClass() const override { return m_sClass; } \ + const std::string& GetClassName() const override { return m_sClass.GetName(); } + +/// +/// Static method used in conjunction with ClassInfo to allocate an Entity. +/// This function is passed into the constructor of this Entity's static ClassInfo's constructor, so that it can instantiate MovableObjects. +/// +/// A pointer to the newly dynamically allocated Entity. Ownership is transferred as well. +#define EntityAllocation(TYPE) \ + static void* operator new(size_t size) { return TYPE::m_sClass.GetPoolMemory(); } \ + static void operator delete(void* instance) { TYPE::m_sClass.ReturnPoolMemory(instance); } \ + static void* operator new(size_t size, void* p) throw() { return p; } \ + static void operator delete(void*, void*) throw() {} \ + static void* Allocate() { return malloc(sizeof(TYPE)); } \ + static void Deallocate(void* instance) { free(instance); } \ + static Entity* NewInstance() { return new TYPE; } \ + Entity* Clone(Entity* cloneTo = nullptr) const override { \ + TYPE* ent = cloneTo ? dynamic_cast(cloneTo) : new TYPE(); \ + RTEAssert(ent, "Tried to clone to an incompatible instance!"); \ + if (cloneTo) { \ + ent->Destroy(); \ + } \ + ent->Create(*this); \ + return ent; \ + } #pragma endregion /// @@ -69,7 +71,6 @@ namespace RTE { friend class DataModule; public: - SerializableOverrideMethods; #pragma region ClassInfo @@ -80,7 +81,6 @@ namespace RTE { friend class Entity; public: - #pragma region Creation /// /// Constructor method used to instantiate a ClassInfo Entity. @@ -91,7 +91,7 @@ namespace RTE { /// Function pointer to the raw deallocation function of memory. If the represented Entity subclass isn't concrete, pass in 0. /// Function pointer to the new instance factory. If the represented Entity subclass isn't concrete, pass in 0. /// The number of new instances to fill the pre-allocated pool with when it runs out. - ClassInfo(const std::string &name, ClassInfo *parentInfo = 0, MemoryAllocate allocFunc = 0, MemoryDeallocate deallocFunc = 0, Entity * (*newFunc)() = 0, int allocBlockCount = 10); + ClassInfo(const std::string& name, ClassInfo* parentInfo = 0, MemoryAllocate allocFunc = 0, MemoryDeallocate deallocFunc = 0, Entity* (*newFunc)() = 0, int allocBlockCount = 10); #pragma endregion #pragma region Getters @@ -99,7 +99,7 @@ namespace RTE { /// Gets the name of this ClassInfo. /// /// A string with the friendly-formatted name of this ClassInfo. - const std::string & GetName() const { return m_Name; } + const std::string& GetName() const { return m_Name; } /// /// Gets the names of all ClassInfos in existence. @@ -112,41 +112,41 @@ namespace RTE { /// /// The friendly name of the desired ClassInfo. /// A pointer to the requested ClassInfo, or 0 if none that matched the name was found. Ownership is NOT transferred! - static const ClassInfo * GetClass(const std::string &name); + static const ClassInfo* GetClass(const std::string& name); /// /// Gets the ClassInfo which describes the parent of this. /// /// A pointer to the parent ClassInfo. 0 if this is a root class. - const ClassInfo * GetParent() const { return m_ParentInfo; } + const ClassInfo* GetParent() const { return m_ParentInfo; } /// /// Gets whether or not this ClassInfo is the same as, or a parent of the ClassInfo corresponding to the given class name. /// /// The name of the class to check for. /// Whether or not this ClassInfo is the same as, or a parent of corresponding ClassInfo for the given class. - bool IsClassOrParentClassOf(const std::string &classNameToCheck) const { return GetClass(classNameToCheck)->IsClassOrChildClassOf(this); } + bool IsClassOrParentClassOf(const std::string& classNameToCheck) const { return GetClass(classNameToCheck)->IsClassOrChildClassOf(this); } /// /// Gets whether or not this ClassInfo is the same as, or a parent of the given ClassInfo. /// /// The name of the class to check for. /// Whether or not this ClassInfo is the same as, or a parent of the given ClassInfo. - bool IsClassOrParentClassOf(const ClassInfo *classInfoToCheck) const { return classInfoToCheck->IsClassOrChildClassOf(this); } + bool IsClassOrParentClassOf(const ClassInfo* classInfoToCheck) const { return classInfoToCheck->IsClassOrChildClassOf(this); } /// /// Gets whether or not this ClassInfo is the same as, or a child of the ClassInfo corresponding to the given class name. /// /// The name of the class to check for. /// Whether or not this ClassInfo is the same as, or a child of corresponding ClassInfo for the given class. - bool IsClassOrChildClassOf(const std::string &classNameToCheck) const { return IsClassOrChildClassOf(GetClass(classNameToCheck)); } + bool IsClassOrChildClassOf(const std::string& classNameToCheck) const { return IsClassOrChildClassOf(GetClass(classNameToCheck)); } /// /// Gets whether or not this ClassInfo is the same as, or a child of the given ClassInfo. /// /// The name of the class to check for. /// Whether or not this ClassInfo is the same as, or a child of the given ClassInfo. - bool IsClassOrChildClassOf(const ClassInfo *classInfoToCheck) const; + bool IsClassOrChildClassOf(const ClassInfo* classInfoToCheck) const; #pragma endregion #pragma region Memory Management @@ -154,20 +154,20 @@ namespace RTE { /// Grabs from the pre-allocated pool, an available chunk of memory the exact size of the Entity this ClassInfo represents. OWNERSHIP IS TRANSFERRED! /// /// A pointer to the pre-allocated pool memory. OWNERSHIP IS TRANSFERRED! - void * GetPoolMemory(); + void* GetPoolMemory(); /// /// Returns a raw chunk of memory back to the pre-allocated available pool. /// /// The raw chunk of memory that is being returned. Needs to be the same size as the type this ClassInfo describes. OWNERSHIP IS TRANSFERRED! /// The count of outstanding memory chunks after this was returned. - int ReturnPoolMemory(void *returnedMemory); + int ReturnPoolMemory(void* returnedMemory); /// /// Writes a bunch of useful debug info about the memory pools to a file. /// /// The writer to write info to. - static void DumpPoolMemoryInfo(const Writer &fileWriter); + static void DumpPoolMemoryInfo(const Writer& fileWriter); /// /// Adds a certain number of newly allocated instances to this' pool. @@ -193,32 +193,31 @@ namespace RTE { /// Dynamically allocates an instance of the Entity subclass that this ClassInfo represents. If the Entity isn't concrete, 0 will be returned. /// /// A pointer to the dynamically allocated Entity. Ownership is transferred. If the represented Entity subclass isn't concrete, 0 will be returned. - virtual Entity * NewInstance() const { return IsConcrete() ? m_NewInstance() : 0; } + virtual Entity* NewInstance() const { return IsConcrete() ? m_NewInstance() : 0; } #pragma endregion protected: - - static ClassInfo *s_ClassHead; //!< Head of unordered linked list of ClassInfos in existence. + static ClassInfo* s_ClassHead; //!< Head of unordered linked list of ClassInfos in existence. const std::string m_Name; //!< A string with the friendly - formatted name of this ClassInfo. - const ClassInfo *m_ParentInfo; //!< A pointer to the parent ClassInfo. + const ClassInfo* m_ParentInfo; //!< A pointer to the parent ClassInfo. MemoryAllocate m_Allocate; //!< Raw memory allocation for the size of the type this ClassInfo describes. MemoryDeallocate m_Deallocate; //!< Raw memory deallocation for the size of the type this ClassInfo describes. // TODO: figure out why this doesn't want to work when defined as std::function. - Entity *(*m_NewInstance)(); //!< Returns an actual new instance of the type that this describes. + Entity* (*m_NewInstance)(); //!< Returns an actual new instance of the type that this describes. - ClassInfo *m_NextClass; //!< Next ClassInfo after this one on aforementioned unordered linked list. + ClassInfo* m_NextClass; //!< Next ClassInfo after this one on aforementioned unordered linked list. - std::vector m_AllocatedPool; //!< Pool of pre-allocated objects of the type described by this ClassInfo. + std::vector m_AllocatedPool; //!< Pool of pre-allocated objects of the type described by this ClassInfo. int m_PoolAllocBlockCount; //!< The number of instances to fill up the pool of this type with each time it runs dry. int m_InstancesInUse; //!< The number of allocated instances passed out from the pool. std::mutex m_Mutex; //!< Mutex to ensure multiple things aren't grabbing/deallocating memory at the same time // Forbidding copying - ClassInfo(const ClassInfo &reference) = delete; - ClassInfo & operator=(const ClassInfo &rhs) = delete; + ClassInfo(const ClassInfo& reference) = delete; + ClassInfo& operator=(const ClassInfo& rhs) = delete; }; #pragma endregion @@ -239,7 +238,7 @@ namespace RTE { /// /// A reference to the Entity to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - virtual int Create(const Entity &reference); + virtual int Create(const Entity& reference); /// /// Makes the Serializable ready for use. @@ -248,14 +247,17 @@ namespace RTE { /// Whether there is a class name in the stream to check against to make sure the correct type is being read from the stream. /// Whether to do any additional initialization of the object after reading in all the properties from the Reader. This is done by calling Create(). /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(Reader &reader, bool checkType = true, bool doCreate = true) override { return Serializable::Create(reader, checkType, doCreate); } + int Create(Reader& reader, bool checkType = true, bool doCreate = true) override { return Serializable::Create(reader, checkType, doCreate); } /// /// Uses a passed-in instance, or creates a new one, and makes it identical to this. /// /// A pointer to an instance to make identical to this. If 0 is passed in, a new instance is made inside here, and ownership of it IS returned! /// An Entity pointer to the newly cloned-to instance. Ownership IS transferred! - virtual Entity * Clone(Entity *cloneTo = nullptr) const { RTEAbort("Attempt to clone an abstract or unclonable type!"); return nullptr; } + virtual Entity* Clone(Entity* cloneTo = nullptr) const { + RTEAbort("Attempt to clone an abstract or unclonable type!"); + return nullptr; + } #pragma endregion #pragma region Destruction @@ -283,7 +285,7 @@ namespace RTE { /// /// A Writer that the Entity will save itself to. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int SavePresetCopy(Writer &writer) const; + int SavePresetCopy(Writer& writer) const; #pragma endregion #pragma region Getters and Setters @@ -303,13 +305,13 @@ namespace RTE { /// Gets this Entity's data preset. /// /// This Entity's data preset. - const Entity * GetPreset() const; + const Entity* GetPreset() const; /// /// Gets the name of this Entity's data Preset. /// /// A string reference with the instance name of this Entity. - const std::string & GetPresetName() const { return m_PresetName; } + const std::string& GetPresetName() const { return m_PresetName; } /// /// Sets the name of this Entity's data Preset. @@ -318,19 +320,22 @@ namespace RTE { /// Whether this method was called from Lua, in which case this change is cosmetic only and shouldn't affect scripts. // TODO: Replace the calledFromLua flag with some DisplayName property // TODO: Figure out how to handle if same name was set, still make it wasgivenname = true? - virtual void SetPresetName(const std::string &newName, bool calledFromLua = false) { /*if (m_PresetName != newName) { m_IsOriginalPreset = true; }*/ m_IsOriginalPreset = calledFromLua ? m_IsOriginalPreset : true; m_PresetName = newName; } + virtual void SetPresetName(const std::string& newName, bool calledFromLua = false) { /*if (m_PresetName != newName) { m_IsOriginalPreset = true; }*/ + m_IsOriginalPreset = calledFromLua ? m_IsOriginalPreset : true; + m_PresetName = newName; + } /// /// Gets the plain text description of this Entity's data Preset. /// /// A string reference with the plain text description name of this Preset. - const std::string & GetDescription() const { return m_PresetDescription; } + const std::string& GetDescription() const { return m_PresetDescription; } /// /// Sets the plain text description of this Entity's data Preset. Shouldn't be more than a couple of sentences. /// /// A string reference with the preset description. - void SetDescription(const std::string &newDesc) { m_PresetDescription = newDesc; } + void SetDescription(const std::string& newDesc) { m_PresetDescription = newDesc; } /// /// Gets the name of this Entity's data Preset, preceded by the name of the Data Module it was defined in, separated with a '/'. @@ -361,13 +366,13 @@ namespace RTE { /// Gets the file and line that are currently being read. Formatted to be used for logging warnings and errors. /// /// A string containing the currently read file path and the line being read. - const std::string & GetFormattedReaderPosition() const { return m_FormattedReaderPosition; } + const std::string& GetFormattedReaderPosition() const { return m_FormattedReaderPosition; } /// /// Sets the file and line that are currently being read. Formatted to be used for logging warnings and errors. /// /// A string containing the currently read file path and the line being read. - void SetFormattedReaderPosition(const std::string &newPosition) override { m_FormattedReaderPosition = newPosition; } + void SetFormattedReaderPosition(const std::string& newPosition) override { m_FormattedReaderPosition = newPosition; } #pragma endregion #pragma region Virtual Override Methods @@ -384,26 +389,26 @@ namespace RTE { /// Gets the set of groups this is member of. /// /// A pointer to a list of strings which describes the groups this is added to. Ownership is NOT transferred! - const std::unordered_set * GetGroups() const { return &m_Groups; } + const std::unordered_set* GetGroups() const { return &m_Groups; } /// /// Gets whether this is part of a specific group or not. /// /// A string which describes the group to check for. /// Whether this Entity is in the specified group or not. - bool IsInGroup(const std::string &whichGroup) const { return whichGroup == "None" ? false : (whichGroup == "All" || whichGroup == "Any" || m_Groups.contains(whichGroup)); } + bool IsInGroup(const std::string& whichGroup) const { return whichGroup == "None" ? false : (whichGroup == "All" || whichGroup == "Any" || m_Groups.contains(whichGroup)); } /// /// Adds this Entity to a new grouping. /// /// A string which describes the group to add this to. Duplicates will be ignored. - void AddToGroup(const std::string &newGroup) { m_Groups.emplace(newGroup); } + void AddToGroup(const std::string& newGroup) { m_Groups.emplace(newGroup); } /// /// Removes this Entity from the specified grouping. /// /// A string which describes the group to remove this from. - void RemoveFromGroup(const std::string &groupToRemoveFrom) { m_Groups.erase(groupToRemoveFrom); } + void RemoveFromGroup(const std::string& groupToRemoveFrom) { m_Groups.erase(groupToRemoveFrom); } /// /// Returns random weight used in PresetMan::GetRandomBuyableOfGroupFromTech. @@ -428,7 +433,10 @@ namespace RTE { /// An ostream reference as the left hand side operand. /// A Entity reference as the right hand side operand. /// An ostream reference for further use in an expression. - friend std::ostream & operator<<(std::ostream &stream, const Entity &operand) { stream << operand.GetPresetName() << ", " << operand.GetClassName(); return stream; } + friend std::ostream& operator<<(std::ostream& stream, const Entity& operand) { + stream << operand.GetPresetName() << ", " << operand.GetClassName(); + return stream; + } /// /// A Reader extraction operator for filling an Entity from a Reader. @@ -436,7 +444,7 @@ namespace RTE { /// A Reader reference as the left hand side operand. /// An Entity reference as the right hand side operand. /// A Reader reference for further use in an expression. - friend Reader & operator>>(Reader &reader, Entity &operand); + friend Reader& operator>>(Reader& reader, Entity& operand); /// /// A Reader extraction operator for filling an Entity from a Reader. @@ -444,7 +452,7 @@ namespace RTE { /// A Reader reference as the left hand side operand. /// An Entity pointer as the right hand side operand. /// A Reader reference for further use in an expression. - friend Reader & operator>>(Reader &reader, Entity *operand); + friend Reader& operator>>(Reader& reader, Entity* operand); #pragma endregion #pragma region Class Info @@ -452,17 +460,16 @@ namespace RTE { /// Gets the ClassInfo instance of this Entity. /// /// A reference to the ClassInfo of this' class. - virtual const Entity::ClassInfo & GetClass() const { return m_sClass; } + virtual const Entity::ClassInfo& GetClass() const { return m_sClass; } /// /// Gets the class name of this Entity. /// /// A string with the friendly-formatted type name of this Entity. - virtual const std::string & GetClassName() const { return m_sClass.GetName(); } + virtual const std::string& GetClassName() const { return m_sClass.GetName(); } #pragma endregion protected: - static Entity::ClassInfo m_sClass; //!< Type description of this Entity. std::string m_PresetName; //!< The name of the Preset data this was cloned from, if any. @@ -477,15 +484,14 @@ namespace RTE { int m_RandomWeight; //!< Random weight used when picking item using PresetMan::GetRandomBuyableOfGroupFromTech. From 0 to 100. 0 means item won't be ever picked. // Forbidding copying - Entity(const Entity &reference) {} - Entity & operator=(const Entity &rhs) { return *this; } + Entity(const Entity& reference) {} + Entity& operator=(const Entity& rhs) { return *this; } private: - /// /// Clears all the member variables of this Entity, effectively resetting the members of this abstraction level only. /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/GLCheck.cpp b/Source/System/GLCheck.cpp index 17f7e7166d..a715eee0cd 100644 --- a/Source/System/GLCheck.cpp +++ b/Source/System/GLCheck.cpp @@ -1,6 +1,6 @@ #include "GLCheck.h" #include "glad/gl.h" -void CheckOpenGLError(const char *stmt, const char *fname, int line) { +void CheckOpenGLError(const char* stmt, const char* fname, int line) { GLenum err = glGetError(); if (err != GL_NO_ERROR) { printf("OpenGL error %08x, at %s:%i - for %s\n", err, fname, line, stmt); diff --git a/Source/System/GLCheck.h b/Source/System/GLCheck.h index ca3bf92393..4e2038c7d0 100644 --- a/Source/System/GLCheck.h +++ b/Source/System/GLCheck.h @@ -5,7 +5,7 @@ /// Debug function to print GL errors to the console from: /// https : // stackoverflow.com/questions/11256470/define-a-macro-to-facilitate-opengl-command-debugging /// -void CheckOpenGLError(const char *stmt, const char *fname, int line); +void CheckOpenGLError(const char* stmt, const char* fname, int line); ///< summary> /// Debug macro to be used for all GL calls. diff --git a/Source/System/GameVersion.h b/Source/System/GameVersion.h index 4ca1327c13..b3f1c81795 100644 --- a/Source/System/GameVersion.h +++ b/Source/System/GameVersion.h @@ -6,8 +6,8 @@ namespace RTE { #pragma region Game Version - static constexpr const char *c_VersionString = "5.1.0"; + static constexpr const char* c_VersionString = "5.1.0"; static const version::Semver200_version c_GameVersion = version::Semver200_version(c_VersionString); #pragma endregion -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Gamepad.h b/Source/System/Gamepad.h index 2b4a7336cf..c9e09020e0 100644 --- a/Source/System/Gamepad.h +++ b/Source/System/Gamepad.h @@ -29,7 +29,8 @@ namespace RTE { /// The joystick ID for event handling. /// Number of analog axis. /// Number of buttons. - Gamepad(int deviceIndex, SDL_JoystickID id, int numAxis, int numButtons) : m_DeviceIndex(deviceIndex), m_JoystickID(id), m_Axis(numAxis), m_DigitalAxis(numAxis), m_Buttons(numButtons) {} + Gamepad(int deviceIndex, SDL_JoystickID id, int numAxis, int numButtons) : + m_DeviceIndex(deviceIndex), m_JoystickID(id), m_Axis(numAxis), m_DigitalAxis(numAxis), m_Buttons(numButtons) {} #pragma endregion #pragma region Operator Overloads @@ -45,14 +46,14 @@ namespace RTE { /// /// A Gamepad reference as the right hand side operand. /// A boolean indicating whether the two operands are equal or not. - bool operator==(const Gamepad &rhs) const { return m_JoystickID == rhs.m_JoystickID; } + bool operator==(const Gamepad& rhs) const { return m_JoystickID == rhs.m_JoystickID; } /// /// Comparison operator for sorting Gamepads by ID. /// /// A Gamepad reference as the right hand side operand. /// A boolean indicating the comparison result. - bool operator<(const Gamepad &rhs) const { return m_JoystickID < rhs.m_JoystickID; } + bool operator<(const Gamepad& rhs) const { return m_JoystickID < rhs.m_JoystickID; } }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/GenericSavedData.cpp b/Source/System/GenericSavedData.cpp index e9b22abff1..05bd06b63e 100644 --- a/Source/System/GenericSavedData.cpp +++ b/Source/System/GenericSavedData.cpp @@ -9,11 +9,11 @@ namespace RTE { const std::string GenericSavedData::GenericSavedStrings::c_ClassName = "GenericSavedStrings"; const std::string GenericSavedData::GenericSavedNumbers::c_ClassName = "GenericSavedNumbers"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::ReadProperty(const std::string_view &propName, Reader &reader) { + int GenericSavedData::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("StringValues", { reader >> m_SavedStrings; }); MatchProperty("EncodedStringValues", { reader >> m_SavedEncodedStrings; }); MatchProperty("NumberValues", { reader >> m_SavedNumbers; }); @@ -21,9 +21,9 @@ namespace RTE { EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::Save(Writer &writer) const { + int GenericSavedData::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("EncodedStringValues", m_SavedEncodedStrings); @@ -33,9 +33,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GenericSavedData::SaveString(const std::string &key, const std::string &value) { + void GenericSavedData::SaveString(const std::string& key, const std::string& value) { if (value.length() == 0) { m_SavedEncodedStrings.m_Data[key] = value; m_SavedStrings.m_Data[key] = value; @@ -67,74 +67,78 @@ namespace RTE { m_SavedEncodedStrings.m_Data[key] = ""; m_SavedStrings.m_Data[key] = value; } - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::string &GenericSavedData::LoadString(const std::string &key) { - const std::string *loadString = &m_SavedStrings.m_Data[key]; + const std::string& GenericSavedData::LoadString(const std::string& key) { + const std::string* loadString = &m_SavedStrings.m_Data[key]; if (*loadString == "") { loadString = &m_SavedEncodedStrings.m_Data[key]; } return *loadString; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedEncodedStrings::ReadProperty(const std::string_view &propName, Reader &reader) { + int GenericSavedData::GenericSavedEncodedStrings::ReadProperty(const std::string_view& propName, Reader& reader) { std::string value = reader.ReadPropValue(); m_Data[std::string(propName)] = base64_decode(value); // until we get P0919R2. return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedEncodedStrings::Save(Writer &writer) const { + int GenericSavedData::GenericSavedEncodedStrings::Save(Writer& writer) const { Serializable::Save(writer); - for (const auto &[propName, value] : m_Data) { + for (const auto& [propName, value]: m_Data) { // Need to encode as URL, so it avoids = character - if (!value.empty()) { writer.NewPropertyWithValue(propName, base64_encode(value, true)); } + if (!value.empty()) { + writer.NewPropertyWithValue(propName, base64_encode(value, true)); + } } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedStrings::ReadProperty(const std::string_view &propName, Reader &reader) { + int GenericSavedData::GenericSavedStrings::ReadProperty(const std::string_view& propName, Reader& reader) { m_Data[std::string(propName)] = reader.ReadPropValue(); // until we get P0919R2. return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedStrings::Save(Writer &writer) const { + int GenericSavedData::GenericSavedStrings::Save(Writer& writer) const { Serializable::Save(writer); - for (const auto &[propName, value] : m_Data) { - if (!value.empty()) { writer.NewPropertyWithValue(propName, value); } + for (const auto& [propName, value]: m_Data) { + if (!value.empty()) { + writer.NewPropertyWithValue(propName, value); + } } return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedNumbers::ReadProperty(const std::string_view &propName, Reader &reader) { + int GenericSavedData::GenericSavedNumbers::ReadProperty(const std::string_view& propName, Reader& reader) { float value; reader >> value; m_Data[std::string(propName)] = value; // until we get P0919R2. return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedNumbers::Save(Writer &writer) const { + int GenericSavedData::GenericSavedNumbers::Save(Writer& writer) const { Serializable::Save(writer); - for (const auto &[propName, value] : m_Data) { + for (const auto& [propName, value]: m_Data) { writer.NewPropertyWithValue(propName, value); } return 0; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/GenericSavedData.h b/Source/System/GenericSavedData.h index c664dae448..ae8f4de19b 100644 --- a/Source/System/GenericSavedData.h +++ b/Source/System/GenericSavedData.h @@ -16,7 +16,6 @@ namespace RTE { class GenericSavedEncodedStrings : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -29,12 +28,11 @@ namespace RTE { /// Constructor method used to instantiate a GenericSavedEncodedStrings object to be identical to another, by deep copy, and make it ready for use. /// /// A reference to the GenericSavedEncodedStrings to deep copy. - GenericSavedEncodedStrings(const GenericSavedEncodedStrings &reference) = default; + GenericSavedEncodedStrings(const GenericSavedEncodedStrings& reference) = default; std::unordered_map m_Data; //!< Stored string data. private: - static const std::string c_ClassName; //!< A string with the friendly formatted type name of this object. }; @@ -44,7 +42,6 @@ namespace RTE { class GenericSavedStrings : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -57,12 +54,11 @@ namespace RTE { /// Constructor method used to instantiate a GenericSavedStrings object to be identical to another, by deep copy, and make it ready for use. /// /// A reference to the GenericSavedStrings to deep copy. - GenericSavedStrings(const GenericSavedStrings &reference) = default; + GenericSavedStrings(const GenericSavedStrings& reference) = default; std::unordered_map m_Data; //!< Stored string data. private: - static const std::string c_ClassName; //!< A string with the friendly formatted type name of this object. }; @@ -72,7 +68,6 @@ namespace RTE { class GenericSavedNumbers : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -85,17 +80,15 @@ namespace RTE { /// Constructor method used to instantiate a GenericSavedNumbers object to be identical to another, by deep copy, and make it ready for use. /// /// A reference to the GenericSavedNumbers to deep copy. - GenericSavedNumbers(const GenericSavedNumbers &reference) = default; + GenericSavedNumbers(const GenericSavedNumbers& reference) = default; std::unordered_map m_Data; //!< Stored number data. private: - static const std::string c_ClassName; //!< A string with the friendly formatted type name of this object. }; public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -108,21 +101,20 @@ namespace RTE { /// Constructor method used to instantiate a GenericSavedData object to be identical to another, by deep copy, and make it ready for use. /// /// A reference to the GenericSavedData to deep copy. - GenericSavedData(const GenericSavedData &reference) = default; + GenericSavedData(const GenericSavedData& reference) = default; - void SaveString(const std::string &key, const std::string &value); - const std::string& LoadString(const std::string &key); + void SaveString(const std::string& key, const std::string& value); + const std::string& LoadString(const std::string& key); - void SaveNumber(const std::string &key, float value) { m_SavedNumbers.m_Data[key] = value; }; - float LoadNumber(const std::string &key) { return m_SavedNumbers.m_Data[key]; }; + void SaveNumber(const std::string& key, float value) { m_SavedNumbers.m_Data[key] = value; }; + float LoadNumber(const std::string& key) { return m_SavedNumbers.m_Data[key]; }; - GenericSavedEncodedStrings m_SavedEncodedStrings; //!< Stored encoded string data. - GenericSavedStrings m_SavedStrings; //!< Stored string data. - GenericSavedNumbers m_SavedNumbers; //!< Stored number data. + GenericSavedEncodedStrings m_SavedEncodedStrings; //!< Stored encoded string data. + GenericSavedStrings m_SavedStrings; //!< Stored string data. + GenericSavedNumbers m_SavedNumbers; //!< Stored number data. private: - static const std::string c_ClassName; //!< A string with the friendly formatted type name of this object. }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/GraphicalPrimitive.cpp b/Source/System/GraphicalPrimitive.cpp index 59c229ef01..995bf7cb2a 100644 --- a/Source/System/GraphicalPrimitive.cpp +++ b/Source/System/GraphicalPrimitive.cpp @@ -27,15 +27,17 @@ namespace RTE { const GraphicalPrimitive::PrimitiveType TextPrimitive::c_PrimitiveType = PrimitiveType::Text; const GraphicalPrimitive::PrimitiveType BitmapPrimitive::c_PrimitiveType = PrimitiveType::Bitmap; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GraphicalPrimitive::TranslateCoordinates(Vector targetPos, const Vector &scenePos, Vector &drawLeftPos, Vector &drawRightPos) const { + void GraphicalPrimitive::TranslateCoordinates(Vector targetPos, const Vector& scenePos, Vector& drawLeftPos, Vector& drawRightPos) const { drawLeftPos = scenePos; drawRightPos = scenePos; if (g_SceneMan.SceneWrapsX()) { float sceneWidth = static_cast(g_SceneMan.GetSceneWidth()); - if (targetPos.m_X <= sceneWidth && targetPos.m_X > sceneWidth / 2) { targetPos.m_X -= sceneWidth; } + if (targetPos.m_X <= sceneWidth && targetPos.m_X > sceneWidth / 2) { + targetPos.m_X -= sceneWidth; + } drawLeftPos.m_X = (drawLeftPos.m_X > 0) ? (drawLeftPos.m_X -= sceneWidth) : (drawLeftPos.m_X -= sceneWidth + targetPos.m_X); } drawLeftPos.m_X -= targetPos.m_X; @@ -43,16 +45,18 @@ namespace RTE { if (g_SceneMan.SceneWrapsY()) { float sceneHeight = static_cast(g_SceneMan.GetSceneHeight()); - if (targetPos.m_Y <= sceneHeight && targetPos.m_Y > sceneHeight / 2) { targetPos.m_Y -= sceneHeight; } + if (targetPos.m_Y <= sceneHeight && targetPos.m_Y > sceneHeight / 2) { + targetPos.m_Y -= sceneHeight; + } drawLeftPos.m_Y = (drawLeftPos.m_Y > 0) ? (drawLeftPos.m_Y -= sceneHeight) : (drawLeftPos.m_Y -= sceneHeight + targetPos.m_Y); } drawLeftPos.m_Y -= targetPos.m_Y; drawRightPos.m_Y -= targetPos.m_Y; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LinePrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void LinePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; Vector drawEnd = m_EndPos - targetPos; @@ -71,9 +75,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ArcPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void ArcPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; if (m_Thickness > 1) { @@ -90,7 +94,7 @@ namespace RTE { TranslateCoordinates(targetPos, m_StartPos, drawStartLeft, drawStartRight); if (m_Thickness > 1) { - for (int i = 0; i < m_Thickness; i++){ + for (int i = 0; i < m_Thickness; i++) { arc(drawScreen, drawStartLeft.GetFloorIntX(), drawStartLeft.GetFloorIntY(), ftofix(GetAllegroAngle(m_StartAngle)), ftofix(GetAllegroAngle(m_EndAngle)), (m_Radius - (m_Thickness / 2)) + i, m_Color); arc(drawScreen, drawStartRight.GetFloorIntX(), drawStartRight.GetFloorIntY(), ftofix(GetAllegroAngle(m_StartAngle)), ftofix(GetAllegroAngle(m_EndAngle)), (m_Radius - (m_Thickness / 2)) + i, m_Color); } @@ -101,16 +105,16 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SplinePrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void SplinePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; Vector drawGuideA = m_GuidePointAPos - targetPos; Vector drawGuideB = m_GuidePointBPos - targetPos; Vector drawEnd = m_EndPos - targetPos; - std::array guidePoints = { drawStart.GetFloorIntX(), drawStart.GetFloorIntY(), drawGuideA.GetFloorIntX(), drawGuideA.GetFloorIntY(), drawGuideB.GetFloorIntX(), drawGuideB.GetFloorIntY(), drawEnd.GetFloorIntX(), drawEnd.GetFloorIntY() }; + std::array guidePoints = {drawStart.GetFloorIntX(), drawStart.GetFloorIntY(), drawGuideA.GetFloorIntX(), drawGuideA.GetFloorIntY(), drawGuideB.GetFloorIntX(), drawGuideB.GetFloorIntY(), drawEnd.GetFloorIntX(), drawEnd.GetFloorIntY()}; spline(drawScreen, guidePoints.data(), m_Color); } else { Vector drawStartLeft; @@ -127,16 +131,16 @@ namespace RTE { TranslateCoordinates(targetPos, m_GuidePointBPos, drawGuideBLeft, drawGuideBRight); TranslateCoordinates(targetPos, m_EndPos, drawEndLeft, drawEndRight); - std::array guidePointsLeft = { drawStartLeft.GetFloorIntX(), drawStartLeft.GetFloorIntY(), drawGuideALeft.GetFloorIntX(), drawGuideALeft.GetFloorIntY(), drawGuideBLeft.GetFloorIntX(), drawGuideBLeft.GetFloorIntY(), drawEndLeft.GetFloorIntX(), drawEndLeft.GetFloorIntY() }; - std::array guidePointsRight = { drawStartRight.GetFloorIntX(), drawStartRight.GetFloorIntY(), drawGuideARight.GetFloorIntX(), drawGuideARight.GetFloorIntY(), drawGuideBRight.GetFloorIntX(), drawGuideBRight.GetFloorIntY(), drawEndRight.GetFloorIntX(), drawEndRight.GetFloorIntY() }; + std::array guidePointsLeft = {drawStartLeft.GetFloorIntX(), drawStartLeft.GetFloorIntY(), drawGuideALeft.GetFloorIntX(), drawGuideALeft.GetFloorIntY(), drawGuideBLeft.GetFloorIntX(), drawGuideBLeft.GetFloorIntY(), drawEndLeft.GetFloorIntX(), drawEndLeft.GetFloorIntY()}; + std::array guidePointsRight = {drawStartRight.GetFloorIntX(), drawStartRight.GetFloorIntY(), drawGuideARight.GetFloorIntX(), drawGuideARight.GetFloorIntY(), drawGuideBRight.GetFloorIntX(), drawGuideBRight.GetFloorIntY(), drawEndRight.GetFloorIntX(), drawEndRight.GetFloorIntY()}; spline(drawScreen, guidePointsLeft.data(), m_Color); spline(drawScreen, guidePointsRight.data(), m_Color); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BoxPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void BoxPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; Vector drawEnd = m_EndPos - targetPos; @@ -155,9 +159,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BoxFillPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void BoxFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; Vector drawEnd = m_EndPos - targetPos; @@ -176,11 +180,15 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RoundedBoxPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { - if (m_StartPos.m_X > m_EndPos.m_X) { std::swap(m_StartPos.m_X, m_EndPos.m_X); } - if (m_StartPos.m_Y > m_EndPos.m_Y) { std::swap(m_StartPos.m_Y, m_EndPos.m_Y); } + void RoundedBoxPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { + if (m_StartPos.m_X > m_EndPos.m_X) { + std::swap(m_StartPos.m_X, m_EndPos.m_X); + } + if (m_StartPos.m_Y > m_EndPos.m_Y) { + std::swap(m_StartPos.m_Y, m_EndPos.m_Y); + } if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -224,11 +232,15 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RoundedBoxFillPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { - if (m_StartPos.m_X > m_EndPos.m_X) { std::swap(m_StartPos.m_X, m_EndPos.m_X); } - if (m_StartPos.m_Y > m_EndPos.m_Y) { std::swap(m_StartPos.m_Y, m_EndPos.m_Y); } + void RoundedBoxFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { + if (m_StartPos.m_X > m_EndPos.m_X) { + std::swap(m_StartPos.m_X, m_EndPos.m_X); + } + if (m_StartPos.m_Y > m_EndPos.m_Y) { + std::swap(m_StartPos.m_Y, m_EndPos.m_Y); + } if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -266,9 +278,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CirclePrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void CirclePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; circle(drawScreen, drawStart.GetFloorIntX(), drawStart.GetFloorIntY(), m_Radius, m_Color); @@ -283,9 +295,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CircleFillPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void CircleFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; circlefill(drawScreen, drawStart.GetFloorIntX(), drawStart.GetFloorIntY(), m_Radius, m_Color); @@ -300,9 +312,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void EllipsePrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void EllipsePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; ellipse(drawScreen, drawStart.GetFloorIntX(), drawStart.GetFloorIntY(), m_HorizRadius, m_VertRadius, m_Color); @@ -317,9 +329,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void EllipseFillPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void EllipseFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; ellipsefill(drawScreen, drawStart.GetFloorIntX(), drawStart.GetFloorIntY(), m_HorizRadius, m_VertRadius, m_Color); @@ -334,9 +346,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TrianglePrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void TrianglePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawPointA = m_PointAPos - targetPos; Vector drawPointB = m_PointBPos - targetPos; @@ -365,9 +377,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TriangleFillPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void TriangleFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawPointA = m_PointAPos - targetPos; Vector drawPointB = m_PointBPos - targetPos; @@ -390,9 +402,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PolygonPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void PolygonPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart; Vector drawEnd; @@ -416,9 +428,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PolygonFillPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void PolygonFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { size_t drawPointsSize = m_Vertices.size() * 2; if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { @@ -427,8 +439,8 @@ namespace RTE { std::vector drawPoints = {}; drawPoints.reserve(drawPointsSize); - for (const Vector *vertice : m_Vertices) { - drawPoints.insert(drawPoints.end(), { drawStart.GetFloorIntX() + vertice->GetFloorIntX(), drawStart.GetFloorIntY() + vertice->GetFloorIntY() }); + for (const Vector* vertice: m_Vertices) { + drawPoints.insert(drawPoints.end(), {drawStart.GetFloorIntX() + vertice->GetFloorIntX(), drawStart.GetFloorIntY() + vertice->GetFloorIntY()}); } polygon(drawScreen, m_Vertices.size(), drawPoints.data(), m_Color); } else { @@ -440,30 +452,30 @@ namespace RTE { Vector drawPointLeft; Vector drawPointRight; - for (const Vector *vertice : m_Vertices) { + for (const Vector* vertice: m_Vertices) { TranslateCoordinates(targetPos, m_StartPos + (*vertice), drawPointLeft, drawPointRight); - drawPointsLeft.insert(drawPointsLeft.end(), { drawPointLeft.GetFloorIntX(), drawPointLeft.GetFloorIntY() }); - drawPointsRight.insert(drawPointsRight.end(), { drawPointRight.GetFloorIntX(), drawPointRight.GetFloorIntY() }); + drawPointsLeft.insert(drawPointsLeft.end(), {drawPointLeft.GetFloorIntX(), drawPointLeft.GetFloorIntY()}); + drawPointsRight.insert(drawPointsRight.end(), {drawPointRight.GetFloorIntX(), drawPointRight.GetFloorIntY()}); } polygon(drawScreen, m_Vertices.size(), drawPointsLeft.data(), m_Color); polygon(drawScreen, m_Vertices.size(), drawPointsRight.data(), m_Color); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TextPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void TextPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (m_Text.empty()) { return; } AllegroBitmap playerGUIBitmap(drawScreen); - GUIFont *font = m_IsSmall ? g_FrameMan.GetSmallFont() : g_FrameMan.GetLargeFont(); + GUIFont* font = m_IsSmall ? g_FrameMan.GetSmallFont() : g_FrameMan.GetLargeFont(); Matrix rotation = Matrix(m_RotAngle); Vector targetPosAdjustment = Vector(); - BITMAP *tempDrawBitmap = nullptr; + BITMAP* tempDrawBitmap = nullptr; if (m_BlendMode > DrawBlendMode::NoBlend || m_RotAngle != 0) { int textWidth = font->CalculateWidth(m_Text); int textHeight = font->CalculateHeight(m_Text); @@ -516,22 +528,24 @@ namespace RTE { } } } - if (tempDrawBitmap) { destroy_bitmap(tempDrawBitmap); } + if (tempDrawBitmap) { + destroy_bitmap(tempDrawBitmap); + } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BitmapPrimitive::Draw(BITMAP *drawScreen, const Vector &targetPos) { + void BitmapPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!m_Bitmap) { return; } - BITMAP *bitmapToDraw = create_bitmap_ex(8, m_Bitmap->w, m_Bitmap->h); + BITMAP* bitmapToDraw = create_bitmap_ex(8, m_Bitmap->w, m_Bitmap->h); clear_to_color(bitmapToDraw, ColorKeys::g_MaskColor); draw_sprite(bitmapToDraw, m_Bitmap, 0, 0); if (m_HFlipped || m_VFlipped) { - BITMAP *flipBitmap = create_bitmap_ex(8, bitmapToDraw->w, bitmapToDraw->h); + BITMAP* flipBitmap = create_bitmap_ex(8, bitmapToDraw->w, bitmapToDraw->h); clear_to_color(flipBitmap, ColorKeys::g_MaskColor); if (m_HFlipped && !m_VFlipped) { @@ -572,4 +586,4 @@ namespace RTE { } destroy_bitmap(bitmapToDraw); } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/GraphicalPrimitive.h b/Source/System/GraphicalPrimitive.h index 5d53cb82b7..e2d812488d 100644 --- a/Source/System/GraphicalPrimitive.h +++ b/Source/System/GraphicalPrimitive.h @@ -13,13 +13,12 @@ namespace RTE { class GraphicalPrimitive { public: - - /// - /// Convenience macro to cut down on duplicate methods in classes that extend GraphicalPrimitive. - /// - #define GraphicalPrimitiveOverrideMethods \ - const PrimitiveType GetPrimitiveType() const override { return c_PrimitiveType; } \ - void Draw(BITMAP *drawScreen, const Vector &targetPos) override; +/// +/// Convenience macro to cut down on duplicate methods in classes that extend GraphicalPrimitive. +/// +#define GraphicalPrimitiveOverrideMethods \ + const PrimitiveType GetPrimitiveType() const override { return c_PrimitiveType; } \ + void Draw(BITMAP* drawScreen, const Vector& targetPos) override; /// /// Enumeration of the different primitive types derived from GraphicalPrimitive. @@ -50,7 +49,7 @@ namespace RTE { unsigned char m_Color = 0; //!< Color to draw this primitive with. int m_Player = -1; //!< Player screen to draw this primitive on. DrawBlendMode m_BlendMode = DrawBlendMode::NoBlend; //!< The blending mode that will be used when drawing this primitive. - std::array m_ColorChannelBlendAmounts = { BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend }; //!< The blending amount for each color channel when drawing in blended mode. + std::array m_ColorChannelBlendAmounts = {BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend}; //!< The blending amount for each color channel when drawing in blended mode. /// /// Destructor method used to clean up a GraphicalPrimitive object before deletion from system memory. @@ -72,14 +71,14 @@ namespace RTE { /// I really don't know how to make it simpler, because it has so many special cases and simply wrapping all out-of-the scene coordinates don't work because this way nothing will be ever draw across the seam. /// You're welcome to rewrite this nightmare if you can, I wasted a whole week on this (I can admit that I'm just too dumb for this) ))) /// - void TranslateCoordinates(Vector targetPos, const Vector &scenePos, Vector &drawLeftPos, Vector &drawRightPos) const; + void TranslateCoordinates(Vector targetPos, const Vector& scenePos, Vector& drawLeftPos, Vector& drawRightPos) const; /// /// Draws this primitive on provided bitmap. /// /// Bitmap to draw on. /// Position of graphical primitive. - virtual void Draw(BITMAP *drawScreen, const Vector &targetPos) = 0; + virtual void Draw(BITMAP* drawScreen, const Vector& targetPos) = 0; /// /// Gets the type identifier of this primitive. @@ -88,7 +87,6 @@ namespace RTE { virtual const PrimitiveType GetPrimitiveType() const = 0; private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -100,7 +98,6 @@ namespace RTE { class LinePrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; /// @@ -110,7 +107,7 @@ namespace RTE { /// Start position of the primitive. /// End position of the primitive. /// Color to draw this primitive with. - LinePrimitive(int player, const Vector &startPos, const Vector &endPos, unsigned char color) { + LinePrimitive(int player, const Vector& startPos, const Vector& endPos, unsigned char color) { m_StartPos = startPos; m_EndPos = endPos; m_Color = color; @@ -118,7 +115,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -130,7 +126,6 @@ namespace RTE { class ArcPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; float m_StartAngle = 0; //!< The angle from which the arc begins. @@ -147,8 +142,8 @@ namespace RTE { /// The angle at which the arc drawing ends. /// Radius of the arc primitive. /// Color to draw this primitive with. - ArcPrimitive(int player, const Vector ¢erPos, float startAngle, float endAngle, int radius, int thickness, unsigned char color) : - m_StartAngle(startAngle), m_EndAngle(endAngle), m_Radius(radius), m_Thickness(thickness) { + ArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, int thickness, unsigned char color) : + m_StartAngle(startAngle), m_EndAngle(endAngle), m_Radius(radius), m_Thickness(thickness) { m_StartPos = centerPos; m_Color = color; @@ -156,7 +151,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -168,7 +162,6 @@ namespace RTE { class SplinePrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; Vector m_GuidePointAPos; //!< A guide point that controls the curve of the spline. @@ -183,8 +176,8 @@ namespace RTE { /// The second guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. /// End position of the primitive. /// Color to draw this primitive with. - SplinePrimitive(int player, const Vector &startPos, const Vector &guideA, const Vector &guideB, const Vector &endPos, unsigned char color) : - m_GuidePointAPos(guideA), m_GuidePointBPos(guideB) { + SplinePrimitive(int player, const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color) : + m_GuidePointAPos(guideA), m_GuidePointBPos(guideB) { m_StartPos = startPos; m_EndPos = endPos; @@ -193,7 +186,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -205,7 +197,6 @@ namespace RTE { class BoxPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; /// @@ -215,7 +206,7 @@ namespace RTE { /// Start position of the primitive. Top left corner. /// End position of the primitive. Bottom right corner. /// Color to draw this primitive with. - BoxPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color) { + BoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { m_StartPos = topLeftPos; m_EndPos = bottomRightPos; m_Color = color; @@ -223,7 +214,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -235,7 +225,6 @@ namespace RTE { class BoxFillPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; /// @@ -245,7 +234,7 @@ namespace RTE { /// Start position of the primitive. Top left corner. /// End position of the primitive. Bottom right corner. /// Color to draw this primitive with. - BoxFillPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, unsigned char color) { + BoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { m_StartPos = topLeftPos; m_EndPos = bottomRightPos; m_Color = color; @@ -253,7 +242,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -265,7 +253,6 @@ namespace RTE { class RoundedBoxPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; int m_CornerRadius = 0; //!< The radius of the corners of the box. @@ -278,8 +265,8 @@ namespace RTE { /// End position of the primitive. Bottom right corner. /// The radius of the corners of the box. Smaller radius equals sharper corners. /// Color to draw this primitive with. - RoundedBoxPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color) : - m_CornerRadius(cornerRadius) { + RoundedBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) : + m_CornerRadius(cornerRadius) { m_StartPos = topLeftPos; m_EndPos = bottomRightPos; @@ -288,7 +275,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -300,7 +286,6 @@ namespace RTE { class RoundedBoxFillPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; int m_CornerRadius = 0; //!< The radius of the corners of the box. @@ -313,8 +298,8 @@ namespace RTE { /// End position of the primitive. Bottom right corner. /// The radius of the corners of the box. Smaller radius equals sharper corners. /// Color to draw this primitive with. - RoundedBoxFillPrimitive(int player, const Vector &topLeftPos, const Vector &bottomRightPos, int cornerRadius, unsigned char color) : - m_CornerRadius(cornerRadius) { + RoundedBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) : + m_CornerRadius(cornerRadius) { m_StartPos = topLeftPos; m_EndPos = bottomRightPos; @@ -323,7 +308,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -335,7 +319,6 @@ namespace RTE { class CirclePrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; int m_Radius = 0; //!< Radius of the circle primitive. @@ -347,8 +330,8 @@ namespace RTE { /// Position of this primitive's center. /// Radius of the circle primitive. /// Color to draw this primitive with. - CirclePrimitive(int player, const Vector ¢erPos, int radius, unsigned char color) : - m_Radius(radius) { + CirclePrimitive(int player, const Vector& centerPos, int radius, unsigned char color) : + m_Radius(radius) { m_StartPos = centerPos; m_Color = color; @@ -356,7 +339,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -368,7 +350,6 @@ namespace RTE { class CircleFillPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; int m_Radius = 0; //!< Radius of the circle primitive. @@ -380,8 +361,8 @@ namespace RTE { /// Position of this primitive's center. /// Radius of the circle primitive. /// Color to draw this primitive with. - CircleFillPrimitive(int player, const Vector ¢erPos, int radius, unsigned char color) : - m_Radius(radius) { + CircleFillPrimitive(int player, const Vector& centerPos, int radius, unsigned char color) : + m_Radius(radius) { m_StartPos = centerPos; m_Color = color; @@ -389,7 +370,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -401,7 +381,6 @@ namespace RTE { class EllipsePrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; int m_HorizRadius = 0; //!< The horizontal radius of the ellipse primitive. @@ -415,8 +394,8 @@ namespace RTE { /// Horizontal radius of the ellipse primitive. /// Vertical radius of the ellipse primitive. /// Color to draw this primitive with. - EllipsePrimitive(int player, const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color) : - m_HorizRadius(horizRadius), m_VertRadius(vertRadius) { + EllipsePrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) : + m_HorizRadius(horizRadius), m_VertRadius(vertRadius) { m_StartPos = centerPos; m_Color = color; @@ -424,7 +403,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -436,7 +414,6 @@ namespace RTE { class EllipseFillPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; int m_HorizRadius = 0; //!< The horizontal radius of the ellipse primitive. @@ -449,8 +426,8 @@ namespace RTE { /// Position of this primitive's center. /// Radius of the circle primitive. /// Color to draw this primitive with. - EllipseFillPrimitive(int player, const Vector ¢erPos, int horizRadius, int vertRadius, unsigned char color) : - m_HorizRadius(horizRadius), m_VertRadius(vertRadius) { + EllipseFillPrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) : + m_HorizRadius(horizRadius), m_VertRadius(vertRadius) { m_StartPos = centerPos; m_Color = color; @@ -458,7 +435,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -470,7 +446,6 @@ namespace RTE { class TrianglePrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; Vector m_PointAPos; //!< First point of the triangle. @@ -485,15 +460,14 @@ namespace RTE { /// Position of the second point of the triangle /// Position of the third point of the triangle /// Color to draw this primitive with. - TrianglePrimitive(int player, const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color) : - m_PointAPos(pointA), m_PointBPos(pointB), m_PointCPos(pointC) { + TrianglePrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) : + m_PointAPos(pointA), m_PointBPos(pointB), m_PointCPos(pointC) { m_Color = color; m_Player = player; } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -505,7 +479,6 @@ namespace RTE { class TriangleFillPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; Vector m_PointAPos; //!< First point of the triangle. @@ -520,15 +493,14 @@ namespace RTE { /// Position of the second point of the triangle /// Position of the third point of the triangle /// Color to draw this primitive with. - TriangleFillPrimitive(int player, const Vector &pointA, const Vector &pointB, const Vector &pointC, unsigned char color) : - m_PointAPos(pointA), m_PointBPos(pointB), m_PointCPos(pointC) { + TriangleFillPrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) : + m_PointAPos(pointA), m_PointBPos(pointB), m_PointCPos(pointC) { m_Color = color; m_Player = player; } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -540,10 +512,9 @@ namespace RTE { class PolygonPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; - std::vector m_Vertices = {}; //!< Positions of the vertices of the polygon, relative to the center position. + std::vector m_Vertices = {}; //!< Positions of the vertices of the polygon, relative to the center position. /// /// Constructor method for PolygonPrimitive object. @@ -552,8 +523,8 @@ namespace RTE { /// Start position of the primitive. /// A vector containing the positions of the vertices of the polygon, relative to the center position. /// Color to draw this primitive with. - PolygonPrimitive(int player, const Vector &startPos, unsigned char color, const std::vector &vertices) : - m_Vertices(vertices) { + PolygonPrimitive(int player, const Vector& startPos, unsigned char color, const std::vector& vertices) : + m_Vertices(vertices) { m_StartPos = startPos; m_Color = color; @@ -561,7 +532,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -573,10 +543,9 @@ namespace RTE { class PolygonFillPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; - std::vector m_Vertices = {}; //!< Positions of the vertices of the polygon, relative to the center position. + std::vector m_Vertices = {}; //!< Positions of the vertices of the polygon, relative to the center position. /// /// Constructor method for PolygonFillPrimitive object. @@ -585,8 +554,8 @@ namespace RTE { /// Start position of the primitive. /// A vector containing the positions of the vertices of the polygon, relative to the center position. /// Color to draw this primitive with. - PolygonFillPrimitive(int player, const Vector &startPos, unsigned char color, const std::vector &vertices) : - m_Vertices(vertices) { + PolygonFillPrimitive(int player, const Vector& startPos, unsigned char color, const std::vector& vertices) : + m_Vertices(vertices) { m_StartPos = startPos; m_Color = color; @@ -594,7 +563,6 @@ namespace RTE { } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -606,7 +574,6 @@ namespace RTE { class TextPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; std::string m_Text = ""; //!< String containing text to draw. @@ -623,15 +590,14 @@ namespace RTE { /// Use small or large font. True for small font. /// Alignment of text. /// Angle to rotate text in radians. - TextPrimitive(int player, const Vector &pos, const std::string &text, bool isSmall, int alignment, float rotAngle) : - m_Text(text), m_IsSmall(isSmall), m_Alignment(alignment), m_RotAngle(rotAngle) { + TextPrimitive(int player, const Vector& pos, const std::string& text, bool isSmall, int alignment, float rotAngle) : + m_Text(text), m_IsSmall(isSmall), m_Alignment(alignment), m_RotAngle(rotAngle) { m_StartPos = pos; m_Player = player; } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion @@ -643,10 +609,9 @@ namespace RTE { class BitmapPrimitive : public GraphicalPrimitive { public: - GraphicalPrimitiveOverrideMethods; - BITMAP *m_Bitmap = nullptr; //!< Bitmap to draw. + BITMAP* m_Bitmap = nullptr; //!< Bitmap to draw. float m_RotAngle = 0; //!< Angle to rotate bitmap in radians. bool m_HFlipped = false; //!< Whether the Bitmap to draw should be horizontally flipped. bool m_VFlipped = false; //!< Whether the Bitmap to draw should be vertically flipped. @@ -660,8 +625,8 @@ namespace RTE { /// Angle to rotate BITMAP in radians. /// Whether the BITMAP to draw should be horizontally flipped. /// Whether the BITMAP to draw should be vertically flipped. - BitmapPrimitive(int player, const Vector ¢erPos, BITMAP *bitmap, float rotAngle, bool hFlipped, bool vFlipped) : - m_Bitmap(bitmap), m_RotAngle(rotAngle), m_HFlipped(hFlipped), m_VFlipped(vFlipped) { + BitmapPrimitive(int player, const Vector& centerPos, BITMAP* bitmap, float rotAngle, bool hFlipped, bool vFlipped) : + m_Bitmap(bitmap), m_RotAngle(rotAngle), m_HFlipped(hFlipped), m_VFlipped(vFlipped) { m_StartPos = centerPos; m_Player = player; @@ -677,8 +642,8 @@ namespace RTE { /// Frame number of the MOSprite that will be drawn. /// Whether the BITMAP to draw should be horizontally flipped. /// Whether the BITMAP to draw should be vertically flipped. - BitmapPrimitive(int player, const Vector ¢erPos, const MOSprite *moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped) : - m_Bitmap(moSprite->GetSpriteFrame(frame)), m_RotAngle(rotAngle), m_HFlipped(hFlipped), m_VFlipped(vFlipped) { + BitmapPrimitive(int player, const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped) : + m_Bitmap(moSprite->GetSpriteFrame(frame)), m_RotAngle(rotAngle), m_HFlipped(hFlipped), m_VFlipped(vFlipped) { m_StartPos = centerPos; m_Player = player; @@ -693,17 +658,16 @@ namespace RTE { /// Angle to rotate BITMAP in radians. /// Whether the BITMAP to draw should be horizontally flipped. /// Whether the BITMAP to draw should be vertically flipped. - BitmapPrimitive(int player, const Vector ¢erPos, const std::string &filePath, float rotAngle, bool hFlipped, bool vFlipped) : - m_Bitmap(ContentFile(filePath.c_str()).GetAsBitmap()), m_RotAngle(rotAngle), m_HFlipped(hFlipped), m_VFlipped(vFlipped) { + BitmapPrimitive(int player, const Vector& centerPos, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped) : + m_Bitmap(ContentFile(filePath.c_str()).GetAsBitmap()), m_RotAngle(rotAngle), m_HFlipped(hFlipped), m_VFlipped(vFlipped) { m_StartPos = centerPos; m_Player = player; } private: - static const PrimitiveType c_PrimitiveType; //!< Type identifier of this primitive. }; #pragma endregion -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/InputMapping.cpp b/Source/System/InputMapping.cpp index bbe22cc2f6..cb2578c0c4 100644 --- a/Source/System/InputMapping.cpp +++ b/Source/System/InputMapping.cpp @@ -4,7 +4,7 @@ namespace RTE { const std::string InputMapping::c_ClassName = "InputMapping"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InputMapping::Clear() { m_PresetDescription.clear(); @@ -16,9 +16,9 @@ namespace RTE { m_DirectionMap = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputMapping::Create(const InputMapping &reference) { + int InputMapping::Create(const InputMapping& reference) { m_KeyMap = reference.m_KeyMap; m_MouseButtonMap = reference.m_MouseButtonMap; m_DirectionMapped = reference.m_DirectionMapped; @@ -29,11 +29,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputMapping::ReadProperty(const std::string_view &propName, Reader &reader) { + int InputMapping::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("KeyMap", { reader >> m_KeyMap; }); MatchProperty("MouseButtonMap", { reader >> m_MouseButtonMap; }); MatchProperty("JoyButtonMap", { reader >> m_JoyButtonMap; }); @@ -45,19 +45,25 @@ namespace RTE { reader >> m_DirectionMap; m_DirectionMapped = true; }); - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputMapping::Save(Writer &writer) const { + int InputMapping::Save(Writer& writer) const { Serializable::Save(writer); - if (m_JoyButtonMap < 0 || !m_DirectionMapped) { writer.NewPropertyWithValue("KeyMap", m_KeyMap); } + if (m_JoyButtonMap < 0 || !m_DirectionMapped) { + writer.NewPropertyWithValue("KeyMap", m_KeyMap); + } - if (m_MouseButtonMap >= 0) { writer.NewPropertyWithValue("MouseButtonMap", m_MouseButtonMap); } - if (m_JoyButtonMap >= 0) { writer.NewPropertyWithValue("JoyButtonMap", m_JoyButtonMap); } + if (m_MouseButtonMap >= 0) { + writer.NewPropertyWithValue("MouseButtonMap", m_MouseButtonMap); + } + if (m_JoyButtonMap >= 0) { + writer.NewPropertyWithValue("JoyButtonMap", m_JoyButtonMap); + } if (m_DirectionMapped) { writer.NewPropertyWithValue("AxisMap", m_AxisMap); @@ -66,4 +72,4 @@ namespace RTE { return 0; } -} +} // namespace RTE diff --git a/Source/System/InputMapping.h b/Source/System/InputMapping.h index 332095dd88..40bc2229de 100644 --- a/Source/System/InputMapping.h +++ b/Source/System/InputMapping.h @@ -11,7 +11,6 @@ namespace RTE { class InputMapping : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -26,7 +25,7 @@ namespace RTE { /// /// A reference to the InputMapping to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const InputMapping &reference); + int Create(const InputMapping& reference); #pragma endregion #pragma region Destruction @@ -47,7 +46,7 @@ namespace RTE { /// Sets the description of the input scheme preset that this element is part of, if any preset has been set for this element's scheme. /// /// The description associated with this element by the scheme preset, if any has been set. This string should be empty otherwise. - void SetPresetDescription(const std::string &presetDescription) { m_PresetDescription = presetDescription; } + void SetPresetDescription(const std::string& presetDescription) { m_PresetDescription = presetDescription; } #pragma endregion #pragma region Keyboard Getters and Setters @@ -96,7 +95,11 @@ namespace RTE { /// /// The number of the axis this should be mapped to. /// The number of the direction this should be mapped to. - void SetDirection(int newAxis, int newDirection) { m_DirectionMapped = true; m_AxisMap = newAxis; m_DirectionMap = newDirection; } + void SetDirection(int newAxis, int newDirection) { + m_DirectionMapped = true; + m_AxisMap = newAxis; + m_DirectionMap = newDirection; + } /// /// Gets the joystick button mapping. @@ -118,7 +121,6 @@ namespace RTE { #pragma endregion protected: - std::string m_PresetDescription; //!< The friendly description that is associated with the scheme preset element, if any is set. int m_KeyMap; //!< The keyboard key mapping. @@ -131,7 +133,6 @@ namespace RTE { int m_DirectionMap; //!< The joystick direction mapping. private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. /// @@ -139,5 +140,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif diff --git a/Source/System/InputScheme.cpp b/Source/System/InputScheme.cpp index 9d378d8d26..3c8d4b19a1 100644 --- a/Source/System/InputScheme.cpp +++ b/Source/System/InputScheme.cpp @@ -5,7 +5,7 @@ namespace RTE { const std::string InputScheme::c_ClassName = "InputScheme"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InputScheme::Clear() { m_ActiveDevice = InputDevice::DEVICE_KEYB_ONLY; @@ -14,14 +14,14 @@ namespace RTE { m_JoystickDeadzone = 0.01F; m_DigitalAimSpeed = 1.0F; - for (InputMapping &inputMapping : m_InputMappings) { + for (InputMapping& inputMapping: m_InputMappings) { inputMapping.Reset(); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputScheme::Create(const InputScheme &reference) { + int InputScheme::Create(const InputScheme& reference) { m_ActiveDevice = reference.m_ActiveDevice; m_SchemePreset = reference.m_SchemePreset; m_JoystickDeadzoneType = reference.m_JoystickDeadzoneType; @@ -34,11 +34,11 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputScheme::ReadProperty(const std::string_view &propName, Reader &reader) { + int InputScheme::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("Device", { SetDevice(static_cast(std::stoi(reader.ReadPropValue()))); }); MatchProperty("Preset", { SetPreset(static_cast(std::stoi(reader.ReadPropValue()))); }); MatchProperty("LeftUp", { reader >> m_InputMappings[InputElements::INPUT_L_UP]; }); @@ -71,14 +71,13 @@ namespace RTE { MatchProperty("JoystickDeadzoneType", { SetJoystickDeadzoneType(static_cast(std::stoi(reader.ReadPropValue()))); }); MatchProperty("JoystickDeadzone", { reader >> m_JoystickDeadzone; }); MatchProperty("DigitalAimSpeed", { reader >> m_DigitalAimSpeed; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputScheme::Save(Writer &writer) const { + int InputScheme::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("Device", m_ActiveDevice); @@ -121,7 +120,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InputScheme::ResetToPlayerDefaults(Players player) { switch (player) { @@ -150,7 +149,7 @@ namespace RTE { m_DigitalAimSpeed = 1.0F; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InputScheme::SetPreset(InputPreset schemePreset) { m_SchemePreset = schemePreset; @@ -158,7 +157,7 @@ namespace RTE { if (schemePreset == InputPreset::NoPreset || schemePreset == InputPreset::InputPresetCount) { return; } - for (InputMapping &inputMapping : m_InputMappings) { + for (InputMapping& inputMapping: m_InputMappings) { inputMapping.Reset(); } switch (m_SchemePreset) { @@ -240,11 +239,11 @@ namespace RTE { m_InputMappings[InputElements::INPUT_PREV].SetJoyButton(SDL_CONTROLLER_BUTTON_LEFTSHOULDER); m_InputMappings[InputElements::INPUT_START].SetJoyButton(SDL_CONTROLLER_BUTTON_START); m_InputMappings[InputElements::INPUT_BACK].SetJoyButton(SDL_CONTROLLER_BUTTON_BACK); - //m_InputMappings[InputElements::INPUT_WEAPON_RELOAD].SetKey(); - //m_InputMappings[InputElements::INPUT_WEAPON_PICKUP].SetKey(); - //m_InputMappings[InputElements::INPUT_WEAPON_DROP].SetKey(); - //m_InputMappings[InputElements::INPUT_WEAPON_CHANGE_PREV].SetKey(); - //m_InputMappings[InputElements::INPUT_WEAPON_CHANGE_NEXT].SetKey(); + // m_InputMappings[InputElements::INPUT_WEAPON_RELOAD].SetKey(); + // m_InputMappings[InputElements::INPUT_WEAPON_PICKUP].SetKey(); + // m_InputMappings[InputElements::INPUT_WEAPON_DROP].SetKey(); + // m_InputMappings[InputElements::INPUT_WEAPON_CHANGE_PREV].SetKey(); + // m_InputMappings[InputElements::INPUT_WEAPON_CHANGE_NEXT].SetKey(); break; case InputPreset::PresetGenericDualAnalog: SetPreset(InputPreset::PresetGamepadXbox360); @@ -366,10 +365,10 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string InputScheme::GetMappingName(int whichElement) const { - const InputMapping *inputElement = &(m_InputMappings.at(whichElement)); + const InputMapping* inputElement = &(m_InputMappings.at(whichElement)); if (m_SchemePreset != InputScheme::InputPreset::NoPreset && !inputElement->GetPresetDescription().empty()) { return inputElement->GetPresetDescription(); } @@ -405,7 +404,7 @@ namespace RTE { return ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool InputScheme::CaptureKeyMapping(int whichInput) { for (int whichKey = SDL_SCANCODE_A; whichKey < SDL_NUM_SCANCODES; ++whichKey) { @@ -422,7 +421,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool InputScheme::CaptureJoystickMapping(int whichJoy, int whichInput) { if (whichJoy < 0) { @@ -447,4 +446,4 @@ namespace RTE { } return false; } -} +} // namespace RTE diff --git a/Source/System/InputScheme.h b/Source/System/InputScheme.h index 052f0a4bad..c6a42b46aa 100644 --- a/Source/System/InputScheme.h +++ b/Source/System/InputScheme.h @@ -12,7 +12,6 @@ namespace RTE { class InputScheme : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -43,7 +42,7 @@ namespace RTE { /// /// A reference to the InputScheme to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const InputScheme &reference); + int Create(const InputScheme& reference); #pragma endregion #pragma region Destruction @@ -88,7 +87,7 @@ namespace RTE { /// Gets the InputMappings for this. /// /// The input mappings array, which is INPUT_COUNT large. - std::array * GetInputMappings() { return &m_InputMappings; } + std::array* GetInputMappings() { return &m_InputMappings; } #pragma endregion #pragma region Input Mapping Getters and Setters @@ -182,7 +181,6 @@ namespace RTE { #pragma endregion protected: - InputDevice m_ActiveDevice; //!< The currently active device for this scheme. InputPreset m_SchemePreset; //!< The preset this scheme was last set to, if any. @@ -193,7 +191,6 @@ namespace RTE { std::array m_InputMappings; //!< The input element mappings of this InputScheme. private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. /// @@ -201,5 +198,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Matrix.cpp b/Source/System/Matrix.cpp index 96838b732b..fdf11aa744 100644 --- a/Source/System/Matrix.cpp +++ b/Source/System/Matrix.cpp @@ -4,7 +4,7 @@ namespace RTE { const std::string Matrix::c_ClassName = "Matrix"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Matrix::Clear() { m_Rotation = 0; @@ -17,7 +17,7 @@ namespace RTE { m_Elements[1][1] = 1.0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Matrix::Create() { // Read all the properties @@ -29,7 +29,7 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Matrix::Create(float angle) { m_Rotation = angle; @@ -46,9 +46,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Matrix::Create(const Matrix &reference) { + int Matrix::Create(const Matrix& reference) { m_Rotation = reference.m_Rotation; m_Flipped[X] = reference.m_Flipped[X]; m_Flipped[Y] = reference.m_Flipped[Y]; @@ -57,25 +57,24 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Matrix::ReadProperty(const std::string_view &propName, Reader &reader) { + int Matrix::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("AngleDegrees", { float degAngle; reader >> degAngle; SetDegAngle(degAngle); }); MatchProperty("AngleRadians", { reader >> m_Rotation; }); - - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Matrix::Save(Writer &writer) const { + int Matrix::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("AngleRadians", m_Rotation); @@ -83,37 +82,45 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Matrix::GetRadAngleTo(float otherAngle) const { // Rotate this' angle with the other angle so that the sought after difference angle is between the resulting angle and the x-axis float difference = otherAngle - GetRadAngle(); // "Normalize" difference to range [-PI,PI) - while (difference < -c_PI) { difference += c_TwoPI; } - while (difference >= c_PI) { difference -= c_TwoPI; } + while (difference < -c_PI) { + difference += c_TwoPI; + } + while (difference >= c_PI) { + difference -= c_TwoPI; + } // difference has the signed answer return difference; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Matrix::GetDegAngleTo(float otherAngle) const { // Rotate this' angle with the other angle so that the sought after difference angle is between the resulting angle and the x-axis float difference = otherAngle - GetDegAngle(); // "Normalize" difference to range [-180,180) - while (difference < -180) { difference += 360; } - while (difference >= 180) { difference -= 360; } + while (difference < -180) { + difference += 360; + } + while (difference >= 180) { + difference -= 360; + } // difference has the signed answer return difference; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Matrix & Matrix::operator=(const Matrix &rhs) { + Matrix& Matrix::operator=(const Matrix& rhs) { if (*this == rhs) { return *this; } @@ -125,10 +132,12 @@ namespace RTE { return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector Matrix::operator*(const Vector &rhs) { - if (!m_ElementsUpdated) { UpdateElements(); } + Vector Matrix::operator*(const Vector& rhs) { + if (!m_ElementsUpdated) { + UpdateElements(); + } Vector retVec = rhs; // Apply flipping as set. @@ -141,10 +150,12 @@ namespace RTE { return retVec; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector Matrix::operator/(const Vector &rhs) { - if (!m_ElementsUpdated) { UpdateElements(); } + Vector Matrix::operator/(const Vector& rhs) { + if (!m_ElementsUpdated) { + UpdateElements(); + } Vector retVec = rhs; // Apply flipping as set. @@ -157,7 +168,7 @@ namespace RTE { return retVec; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Matrix Matrix::operator-() { m_Rotation = -m_Rotation; @@ -171,7 +182,7 @@ namespace RTE { return *this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Matrix::UpdateElements() { // Negative angle to Account for upside-down coordinate system. @@ -184,4 +195,4 @@ namespace RTE { m_ElementsUpdated = true; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/Matrix.h b/Source/System/Matrix.h index 35e7d55bd3..174aa0a316 100644 --- a/Source/System/Matrix.h +++ b/Source/System/Matrix.h @@ -12,7 +12,6 @@ namespace RTE { class Matrix : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -31,13 +30,19 @@ namespace RTE { /// Constructor method used to instantiate a Matrix object from an angle. /// /// A float of an angle in radians that this Matrix should be set to represent. - Matrix(float radAng) { Clear(); Create(radAng); } + Matrix(float radAng) { + Clear(); + Create(radAng); + } /// /// Copy constructor method used to instantiate a Matrix object identical to an already existing one. /// /// A Matrix object which is passed in by reference. - Matrix(const Matrix &reference) { Clear(); Create(reference); } + Matrix(const Matrix& reference) { + Clear(); + Create(reference); + } /// /// Makes the Matrix object ready for use. @@ -57,7 +62,7 @@ namespace RTE { /// /// A reference to the Matrix to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Matrix &reference); + int Create(const Matrix& reference); #pragma endregion #pragma region Destruction @@ -102,7 +107,10 @@ namespace RTE { /// Sets the angle that this rotational Matrix should represent. /// /// A float with the new angle, in radians. - void SetRadAngle(float newAngle) { m_Rotation = newAngle; m_ElementsUpdated = false; } + void SetRadAngle(float newAngle) { + m_Rotation = newAngle; + m_ElementsUpdated = false; + } /// /// Returns the angle this rotational Matrix is currently representing. @@ -114,7 +122,10 @@ namespace RTE { /// Sets the angle that this rotational Matrix should represent. /// /// A float with the new angle, in degrees. - void SetDegAngle(float newAngle) { m_Rotation = (newAngle / 180.0F) * c_PI; m_ElementsUpdated = false; } + void SetDegAngle(float newAngle) { + m_Rotation = (newAngle / 180.0F) * c_PI; + m_ElementsUpdated = false; + } /// /// Returns the angle difference between what this is currently representing, to another angle in radians. @@ -145,14 +156,18 @@ namespace RTE { /// /// A Matrix reference. /// A reference to the changed Matrix. - Matrix & operator=(const Matrix &rhs); + Matrix& operator=(const Matrix& rhs); /// /// An assignment operator for setting one Matrix to represent an angle. /// /// A float in radians to set this rotational Matrix to. /// A reference to the changed Matrix. - Matrix & operator=(const float &rhs) { m_Rotation = rhs; m_ElementsUpdated = false; return *this; } + Matrix& operator=(const float& rhs) { + m_Rotation = rhs; + m_ElementsUpdated = false; + return *this; + } /// /// Unary negation overload for single Matrices. @@ -166,7 +181,7 @@ namespace RTE { /// A Matrix reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A boolean indicating whether the two operands are equal or not. - friend bool operator==(const Matrix &lhs, const Matrix &rhs) { return lhs.m_Rotation == rhs.m_Rotation; } + friend bool operator==(const Matrix& lhs, const Matrix& rhs) { return lhs.m_Rotation == rhs.m_Rotation; } /// /// An inequality operator for testing if any two Matrices are unequal. @@ -174,14 +189,18 @@ namespace RTE { /// A Matrix reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A boolean indicating whether the two operands are unequal or not. - friend bool operator!=(const Matrix &lhs, const Matrix &rhs) { return !operator==(lhs, rhs); } + friend bool operator!=(const Matrix& lhs, const Matrix& rhs) { return !operator==(lhs, rhs); } /// /// Self-addition operator overload for a Matrix and a float. /// /// A float reference as the right hand side operand. /// A reference to the resulting Matrix. - Matrix & operator+=(const float &rhs) { m_Rotation += rhs; m_ElementsUpdated = false; return *this; } + Matrix& operator+=(const float& rhs) { + m_Rotation += rhs; + m_ElementsUpdated = false; + return *this; + } /// /// Self-addition operator overload for Matrices. @@ -189,14 +208,22 @@ namespace RTE { /// A Matrix reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A reference to the resulting Matrix (the left one). - friend Matrix & operator+=(Matrix &lhs, const Matrix &rhs) { lhs.m_Rotation += rhs.m_Rotation; lhs.m_ElementsUpdated = false; return lhs; } + friend Matrix& operator+=(Matrix& lhs, const Matrix& rhs) { + lhs.m_Rotation += rhs.m_Rotation; + lhs.m_ElementsUpdated = false; + return lhs; + } /// /// Self-subtraction operator overload for a Matrix and a float. /// /// A float reference as the right hand side operand. /// A reference to the resulting Matrix. - Matrix & operator-=(const float &rhs) { m_Rotation -= rhs; m_ElementsUpdated = false; return *this; } + Matrix& operator-=(const float& rhs) { + m_Rotation -= rhs; + m_ElementsUpdated = false; + return *this; + } /// /// Self-subtraction operator overload for Matrices. @@ -204,14 +231,22 @@ namespace RTE { /// A Matrix reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A reference to the resulting Matrix (the left one). - friend Matrix & operator-=(Matrix &lhs, const Matrix &rhs) { lhs.m_Rotation -= rhs.m_Rotation; lhs.m_ElementsUpdated = false; return lhs; } + friend Matrix& operator-=(Matrix& lhs, const Matrix& rhs) { + lhs.m_Rotation -= rhs.m_Rotation; + lhs.m_ElementsUpdated = false; + return lhs; + } /// /// Self-multiplication operator overload for a Matrix and a float. /// /// A float reference as the right hand side operand. /// A reference to the resulting Matrix. - Matrix & operator*=(const float &rhs) { m_Rotation *= rhs; m_ElementsUpdated = false; return *this; } + Matrix& operator*=(const float& rhs) { + m_Rotation *= rhs; + m_ElementsUpdated = false; + return *this; + } /// /// Self-multiplication operator overload for Matrices. @@ -219,14 +254,18 @@ namespace RTE { /// A Matrix reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A reference to the resulting Matrix (the left one). - friend Matrix & operator*=(Matrix &lhs, const Matrix &rhs) { lhs.m_Rotation *= rhs.m_Rotation; lhs.m_ElementsUpdated = false; return lhs; } + friend Matrix& operator*=(Matrix& lhs, const Matrix& rhs) { + lhs.m_Rotation *= rhs.m_Rotation; + lhs.m_ElementsUpdated = false; + return lhs; + } /// /// self-division operator overload for a Matrix and a float. /// /// A float reference as the right hand side operand. /// A reference to the resulting Matrix. - Matrix & operator/=(const float &rhs) { + Matrix& operator/=(const float& rhs) { if (rhs) { m_Rotation /= rhs; m_ElementsUpdated = false; @@ -240,8 +279,11 @@ namespace RTE { /// A Matrix reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A reference to the resulting Matrix (the left one). - friend Matrix & operator/=(Matrix &lhs, const Matrix &rhs) { - if (rhs.m_Rotation) { lhs.m_Rotation /= rhs.m_Rotation; lhs.m_ElementsUpdated = false; } + friend Matrix& operator/=(Matrix& lhs, const Matrix& rhs) { + if (rhs.m_Rotation) { + lhs.m_Rotation /= rhs.m_Rotation; + lhs.m_ElementsUpdated = false; + } return lhs; } @@ -251,7 +293,7 @@ namespace RTE { /// /// A Vector reference as the right hand side operand. /// The resulting transformed Vector. - Vector operator*(const Vector &rhs); + Vector operator*(const Vector& rhs); /// /// Multiplication operator overload for Vectors with Matrices. @@ -259,14 +301,17 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A reference to the resulting Vector. - friend Vector operator*(const Vector &lhs, const Matrix &rhs) { Matrix m(rhs); return m * lhs; } + friend Vector operator*(const Vector& lhs, const Matrix& rhs) { + Matrix m(rhs); + return m * lhs; + } /// /// Division operator overload for a Matrix and a Vector. The vector will be transformed according to the Matrix's elements. /// /// A Vector reference as the right hand side operand. /// The resulting transformed Vector. - Vector operator/(const Vector &rhs); + Vector operator/(const Vector& rhs); /// /// Division operator overload for Vector:s with Matrices. @@ -274,7 +319,7 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A reference to the resulting Vector. - friend Vector operator/(const Vector &lhs, Matrix &rhs) { return rhs / lhs; } + friend Vector operator/(const Vector& lhs, Matrix& rhs) { return rhs / lhs; } /// /// Self-multiplication operator overload for Vector with a Matrix. @@ -282,7 +327,7 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A reference to the resulting Vector (the left one) - friend Vector & operator*=(Vector &lhs, Matrix &rhs) { return lhs = rhs * lhs; } + friend Vector& operator*=(Vector& lhs, Matrix& rhs) { return lhs = rhs * lhs; } /// /// Self-division operator overload for Vector with a Matrix. @@ -290,11 +335,10 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Matrix reference as the right hand side operand. /// A reference to the resulting Vector (the left one). - friend Vector & operator/=(Vector &lhs, Matrix &rhs) { return lhs = rhs / lhs; } + friend Vector& operator/=(Vector& lhs, Matrix& rhs) { return lhs = rhs / lhs; } #pragma endregion private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. /// @@ -307,5 +351,5 @@ namespace RTE { /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/NetworkMessages.h b/Source/System/NetworkMessages.h index b303904d23..d12b01b0e9 100644 --- a/Source/System/NetworkMessages.h +++ b/Source/System/NetworkMessages.h @@ -296,7 +296,6 @@ namespace RTE { bool ResetActivityVote; bool RestartActivityVote; - int MouseWheelMoved; unsigned int padElement3; @@ -306,5 +305,5 @@ namespace RTE { // Disables the previously set pack pragma. #pragma pack(pop) -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/PathFinder.cpp b/Source/System/PathFinder.cpp index 0eaf990f44..fc8e8c6d50 100644 --- a/Source/System/PathFinder.cpp +++ b/Source/System/PathFinder.cpp @@ -19,7 +19,7 @@ namespace RTE { delete m_Instance; } - MicroPather *m_Instance; + MicroPather* m_Instance; }; thread_local MicroPatherWrapper s_Pather; @@ -29,24 +29,25 @@ namespace RTE { // TODO: Enhance MicroPather to add that capability (or write our own pather)! thread_local float s_DigStrength = 0.0F; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PathNode::PathNode(const Vector &pos) : Pos(pos) { - const Material *outOfBounds = g_SceneMan.GetMaterialFromID(MaterialColorKeys::g_MaterialOutOfBounds); + PathNode::PathNode(const Vector& pos) : + Pos(pos) { + const Material* outOfBounds = g_SceneMan.GetMaterialFromID(MaterialColorKeys::g_MaterialOutOfBounds); for (int i = 0; i < c_MaxAdjacentNodeCount; i++) { AdjacentNodes[i] = nullptr; AdjacentNodeBlockingMaterials[i] = outOfBounds; // Costs are infinite unless recalculated as otherwise. } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PathFinder::Clear() { m_NodeGrid.clear(); m_NodeDimension = SCENEGRIDSIZE; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int PathFinder::Create(int nodeDimension) { RTEAssert(g_SceneMan.GetScene(), "Scene doesn't exist or isn't loaded when creating PathFinder!"); @@ -80,7 +81,7 @@ namespace RTE { nodePos.m_X = sceneWidth - 1.0F; } - // Add the newly created node to the column. + // Add the newly created node to the column. // Warning! Emplace back must be used to ensure this is constructed in-place, as otherwise the Up/Right/Down etc references will be incorrect. m_NodeGrid.emplace_back(nodePos); @@ -93,7 +94,7 @@ namespace RTE { // Assign all the adjacent nodes on each node. GetPathNodeAtGridCoords handles Scene wrapping. for (int x = 0; x < m_GridWidth; ++x) { for (int y = 0; y < m_GridHeight; ++y) { - PathNode &node = *GetPathNodeAtGridCoords(x, y); + PathNode& node = *GetPathNodeAtGridCoords(x, y); node.Up = GetPathNodeAtGridCoords(x, y - 1); node.Right = GetPathNodeAtGridCoords(x + 1, y); @@ -111,23 +112,23 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PathFinder::Destroy() { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MicroPather * PathFinder::GetPather() { + MicroPather* PathFinder::GetPather() { // TODO: cache a collection of pathers. For async pathfinding right now we create a new pather for every thread! if (!s_Pather.m_Instance || s_Pather.m_Instance->GetGraph() != this) { // First time this thread has asked for a pather, let's initialize it delete s_Pather.m_Instance; // Might be reinitialized and Graph ptrs mismatch, in that case delete the old one - + // TODO: test dynamically setting this. The code below sets it based on map area and block size, with a hefty upper limit. - //int sceneArea = m_GridWidth * m_GridHeight; - //unsigned int numberOfBlocksToAllocate = std::min(128000, sceneArea / (m_NodeDimension * m_NodeDimension)); + // int sceneArea = m_GridWidth * m_GridHeight; + // unsigned int numberOfBlocksToAllocate = std::min(128000, sceneArea / (m_NodeDimension * m_NodeDimension)); unsigned int numberOfBlocksToAllocate = 4000; s_Pather.m_Instance = new MicroPather(this, numberOfBlocksToAllocate, PathNode::c_MaxAdjacentNodeCount, false); } @@ -135,13 +136,13 @@ namespace RTE { return s_Pather.m_Instance; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PathFinder::CalculatePath(Vector start, Vector end, std::list &pathResult, float &totalCostResult, float digStrength) { + int PathFinder::CalculatePath(Vector start, Vector end, std::list& pathResult, float& totalCostResult, float digStrength) { ZoneScoped; - + ++m_CurrentPathingRequests; - + // Make sure start and end are within scene bounds. g_SceneMan.ForceBounds(start); g_SceneMan.ForceBounds(end); @@ -163,7 +164,7 @@ namespace RTE { // Do the actual pathfinding, fetch out the list of states that comprise the best path. int result = MicroPather::NO_SOLUTION; - std::vector statePath; + std::vector statePath; // If end node is invalid, there's no path PathNode* endNode = GetPathNodeAtGridCoords(endNodeX, endNodeY); @@ -179,12 +180,12 @@ namespace RTE { if (!statePath.empty()) { // Replace the approximate first point from the pathfound path with the exact starting point. pathResult.push_back(start); - std::vector::iterator itr = statePath.begin(); + std::vector::iterator itr = statePath.begin(); itr++; // Convert from a list of state void pointers to a list of scene position vectors. for (; itr != statePath.end(); ++itr) { - pathResult.push_back((static_cast(*itr))->Pos); + pathResult.push_back((static_cast(*itr))->Pos); } // Adjust the last point to be exactly where the end is supposed to be (really?). @@ -204,20 +205,20 @@ namespace RTE { return result; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::shared_ptr PathFinder::CalculatePathAsync(Vector start, Vector end, float digStrength, PathCompleteCallback callback) { std::shared_ptr pathRequest = std::make_shared(); - const_cast(pathRequest->startPos) = start; - const_cast(pathRequest->targetPos) = end; + const_cast(pathRequest->startPos) = start; + const_cast(pathRequest->targetPos) = end; g_ThreadMan.GetBackgroundThreadPool().push_task([this, start, end, digStrength, callback](std::shared_ptr volRequest) { // Cast away the volatile-ness - only matters outside (and complicates the API otherwise) - PathRequest &request = const_cast(*volRequest); + PathRequest& request = const_cast(*volRequest); int status = this->CalculatePath(start, end, request.path, request.totalCost, digStrength); - + request.status = status; request.pathLength = request.path.size(); @@ -228,18 +229,19 @@ namespace RTE { // Have to set to complete after the callback, so anything that blocks on it knows that the callback will have been called by now // This has the awkward side-effect that the complete flag is actually false during the callback - but that's fine, if it's called we know it's complete anyways request.complete = true; - }, pathRequest); + }, + pathRequest); return pathRequest; - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PathFinder::RecalculateAllCosts() { - RTEAssert(g_SceneMan.GetScene(), "Scene doesn't exist or isn't loaded when recalculating PathFinder!"); + void PathFinder::RecalculateAllCosts() { + RTEAssert(g_SceneMan.GetScene(), "Scene doesn't exist or isn't loaded when recalculating PathFinder!"); // Deadlock until all path requests are complete - while (m_CurrentPathingRequests.load() != 0) { }; + while (m_CurrentPathingRequests.load() != 0) {}; // I hate this copy, but fuck it. std::vector pathNodesIdsVec; @@ -249,18 +251,18 @@ namespace RTE { } UpdateNodeList(pathNodesIdsVec); - } + } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector PathFinder::RecalculateAreaCosts(std::deque &boxList, int nodeUpdateLimit) { + std::vector PathFinder::RecalculateAreaCosts(std::deque& boxList, int nodeUpdateLimit) { ZoneScoped; - + std::unordered_set nodeIDsToUpdate; while (!boxList.empty()) { std::vector nodesInside = GetNodeIdsInBox(boxList.front()); - for (int nodeId : nodesInside) { + for (int nodeId: nodesInside) { nodeIDsToUpdate.insert(nodeId); } @@ -283,16 +285,16 @@ namespace RTE { return nodeVec; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float PathFinder::LeastCostEstimate(void *startState, void *endState) { - return g_SceneMan.ShortestDistance((static_cast(startState))->Pos, (static_cast(endState))->Pos).GetMagnitude() / m_NodeDimension; + float PathFinder::LeastCostEstimate(void* startState, void* endState) { + return g_SceneMan.ShortestDistance((static_cast(startState))->Pos, (static_cast(endState))->Pos).GetMagnitude() / m_NodeDimension; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PathFinder::AdjacentCost(void *state, std::vector *adjacentList) { - const PathNode *node = static_cast(state); + void PathFinder::AdjacentCost(void* state, std::vector* adjacentList) { + const PathNode* node = static_cast(state); micropather::StateCost adjCost; // We do a little trick here, where we radiate out a little percentage of our average cost in all directions. @@ -306,49 +308,49 @@ namespace RTE { // Add cost for digging upwards. if (node->Up && node->Up->m_Navigatable) { adjCost.cost = 1.0F + extraUpCost + (GetMaterialTransitionCost(*node->UpMaterial) * 4.0F) + radiatedCost; // Four times more expensive when digging. - adjCost.state = static_cast(node->Up); + adjCost.state = static_cast(node->Up); adjacentList->push_back(adjCost); } if (node->Right && node->Right->m_Navigatable) { adjCost.cost = 1.0F + GetMaterialTransitionCost(*node->RightMaterial) + radiatedCost; - adjCost.state = static_cast(node->Right); + adjCost.state = static_cast(node->Right); adjacentList->push_back(adjCost); } if (node->Down && node->Down->m_Navigatable) { adjCost.cost = 1.0F + GetMaterialTransitionCost(*node->DownMaterial) + radiatedCost; - adjCost.state = static_cast(node->Down); + adjCost.state = static_cast(node->Down); adjacentList->push_back(adjCost); } if (node->Left && node->Left->m_Navigatable) { adjCost.cost = 1.0F + GetMaterialTransitionCost(*node->LeftMaterial) + radiatedCost; - adjCost.state = static_cast(node->Left); + adjCost.state = static_cast(node->Left); adjacentList->push_back(adjCost); } // Add cost for digging at 45 degrees and for digging upwards. if (node->UpRight && node->UpRight->m_Navigatable) { - adjCost.cost = 1.4F + extraUpCost + (GetMaterialTransitionCost(*node->UpRightMaterial) * 1.4F * 3.0F) + radiatedCost; // Three times more expensive when digging. - adjCost.state = static_cast(node->UpRight); + adjCost.cost = 1.4F + extraUpCost + (GetMaterialTransitionCost(*node->UpRightMaterial) * 1.4F * 3.0F) + radiatedCost; // Three times more expensive when digging. + adjCost.state = static_cast(node->UpRight); adjacentList->push_back(adjCost); } if (node->RightDown && node->RightDown->m_Navigatable) { adjCost.cost = 1.4F + (GetMaterialTransitionCost(*node->RightDownMaterial) * 1.4F) + radiatedCost; - adjCost.state = static_cast(node->RightDown); + adjCost.state = static_cast(node->RightDown); adjacentList->push_back(adjCost); } if (node->DownLeft && node->DownLeft->m_Navigatable) { adjCost.cost = 1.4F + (GetMaterialTransitionCost(*node->DownLeftMaterial) * 1.4F) + radiatedCost; - adjCost.state = static_cast(node->DownLeft); + adjCost.state = static_cast(node->DownLeft); adjacentList->push_back(adjCost); } if (node->LeftUp && node->LeftUp->m_Navigatable) { - adjCost.cost = 1.4F + extraUpCost + (GetMaterialTransitionCost(*node->LeftUpMaterial) * 1.4F * 3.0F) + radiatedCost; // Three times more expensive when digging. - adjCost.state = static_cast(node->LeftUp); + adjCost.cost = 1.4F + extraUpCost + (GetMaterialTransitionCost(*node->LeftUpMaterial) * 1.4F * 3.0F) + radiatedCost; // Three times more expensive when digging. + adjCost.state = static_cast(node->LeftUp); adjacentList->push_back(adjCost); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool PathFinder::PositionsAreTheSamePathNode(const Vector& pos1, const Vector& pos2) const { int startNodeX = std::floor(pos1.m_X / static_cast(m_NodeDimension)); @@ -358,9 +360,9 @@ namespace RTE { return startNodeX == endNodeX && startNodeY == endNodeY; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float PathFinder::GetMaterialTransitionCost(const Material &material) const { + float PathFinder::GetMaterialTransitionCost(const Material& material) const { float strength = material.GetIntegrity(); // Always treat doors as diggable. if (strength > s_DigStrength && material.GetIndex() != MaterialColorKeys::g_MaterialDoor) { @@ -369,22 +371,22 @@ namespace RTE { return strength; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Material * PathFinder::StrongestMaterialAlongLine(const Vector &start, const Vector &end) const { + const Material* PathFinder::StrongestMaterialAlongLine(const Vector& start, const Vector& end) const { return g_SceneMan.CastMaxStrengthRayMaterial(start, end, 0, MaterialColorKeys::g_MaterialAir); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PathFinder::UpdateNodeCosts(PathNode *node) const { + bool PathFinder::UpdateNodeCosts(PathNode* node) const { if (!node) { return false; } - std::array oldMaterials = node->AdjacentNodeBlockingMaterials; + std::array oldMaterials = node->AdjacentNodeBlockingMaterials; - auto getStrongerMaterial = [](const Material *first, const Material *second) { + auto getStrongerMaterial = [](const Material* first, const Material* second) { return first->GetIntegrity() > second->GetIntegrity() ? first : second; }; @@ -408,8 +410,8 @@ namespace RTE { } for (int i = 0; i < PathNode::c_MaxAdjacentNodeCount; ++i) { - const Material *oldMat = oldMaterials[i]; - const Material *newMat = node->AdjacentNodeBlockingMaterials[i]; + const Material* oldMat = oldMaterials[i]; + const Material* newMat = node->AdjacentNodeBlockingMaterials[i]; // Check if the material strength is more than our delta, or if a door has appeared/disappeared (since we handle their costs in a special manner). float delta = std::abs(oldMat->GetIntegrity() - newMat->GetIntegrity()); @@ -424,7 +426,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::vector PathFinder::GetNodeIdsInBox(Box box) { std::vector result; @@ -450,12 +452,12 @@ namespace RTE { return result; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float PathFinder::GetNodeAverageTransitionCost(const PathNode &node) const { + float PathFinder::GetNodeAverageTransitionCost(const PathNode& node) const { float totalCostOfAdjacentNodes = 0.0F; int count = 0; - for (const Material *material : node.AdjacentNodeBlockingMaterials) { + for (const Material* material: node.AdjacentNodeBlockingMaterials) { // Don't use node transition cost, because we don't care about digging. float cost = material->GetIntegrity(); if (cost < std::numeric_limits::max()) { @@ -466,61 +468,66 @@ namespace RTE { return totalCostOfAdjacentNodes / std::max(static_cast(count), 1.0F); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PathFinder::UpdateNodeList(const std::vector &nodeVec) { + bool PathFinder::UpdateNodeList(const std::vector& nodeVec) { ZoneScoped; - + std::atomic anyChange = false; // Update all the costs going out from each node. std::for_each( - std::execution::par_unseq, - nodeVec.begin(), - nodeVec.end(), - [this, &anyChange](int nodeId) { - if (UpdateNodeCosts(&m_NodeGrid[nodeId])) { - anyChange = true; - } - } - ); + std::execution::par_unseq, + nodeVec.begin(), + nodeVec.end(), + [this, &anyChange](int nodeId) { + if (UpdateNodeCosts(&m_NodeGrid[nodeId])) { + anyChange = true; + } + }); if (anyChange) { // UpdateNodeCosts only calculates Materials for Right and Down directions, so each PathNode's Up and Left direction Materials need to be matched to the respective neighbor's opposite direction Materials. // For example, this PathNode's Left Material is its Left neighbor's Right Material. std::for_each( - std::execution::par_unseq, - nodeVec.begin(), - nodeVec.end(), - [this](int nodeId) { - PathNode *node = &m_NodeGrid[nodeId]; - if (node->Right) { node->Right->LeftMaterial = node->RightMaterial; } - if (node->Down) { node->Down->UpMaterial = node->DownMaterial; } - if (node->UpRight) { node->UpRight->DownLeftMaterial = node->UpRightMaterial; } - if (node->RightDown) { node->RightDown->LeftUpMaterial = node->RightDownMaterial; } - } - ); + std::execution::par_unseq, + nodeVec.begin(), + nodeVec.end(), + [this](int nodeId) { + PathNode* node = &m_NodeGrid[nodeId]; + if (node->Right) { + node->Right->LeftMaterial = node->RightMaterial; + } + if (node->Down) { + node->Down->UpMaterial = node->DownMaterial; + } + if (node->UpRight) { + node->UpRight->DownLeftMaterial = node->UpRightMaterial; + } + if (node->RightDown) { + node->RightDown->LeftUpMaterial = node->RightDownMaterial; + } + }); } return anyChange; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PathFinder::MarkBoxNavigatable(Box box, bool navigatable) { std::vector pathNodesInBox = GetNodeIdsInBox(box); std::for_each( - std::execution::par_unseq, - pathNodesInBox.begin(), - pathNodesInBox.end(), - [this, navigatable](int nodeId) { - PathNode* node = &m_NodeGrid[nodeId]; - node->m_Navigatable = navigatable; - } - ); + std::execution::par_unseq, + pathNodesInBox.begin(), + pathNodesInBox.end(), + [this, navigatable](int nodeId) { + PathNode* node = &m_NodeGrid[nodeId]; + node->m_Navigatable = navigatable; + }); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PathFinder::MarkAllNodesNavigatable(bool navigatable) { std::vector pathNodesIdsVec; @@ -530,24 +537,23 @@ namespace RTE { } std::for_each( - std::execution::par_unseq, - pathNodesIdsVec.begin(), - pathNodesIdsVec.end(), - [this, navigatable](int nodeId) { - PathNode* node = &m_NodeGrid[nodeId]; - node->m_Navigatable = navigatable; - } - ); + std::execution::par_unseq, + pathNodesIdsVec.begin(), + pathNodesIdsVec.end(), + [this, navigatable](int nodeId) { + PathNode* node = &m_NodeGrid[nodeId]; + node->m_Navigatable = navigatable; + }); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PathNode * PathFinder::GetPathNodeAtGridCoords(int x, int y) { + PathNode* PathFinder::GetPathNodeAtGridCoords(int x, int y) { int nodeId = ConvertCoordsToNodeId(x, y); return nodeId != -1 ? &m_NodeGrid[nodeId] : nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int PathFinder::ConvertCoordsToNodeId(int x, int y) { if (m_WrapsX) { @@ -566,4 +572,4 @@ namespace RTE { return (y * m_GridWidth) + x; } -} +} // namespace RTE diff --git a/Source/System/PathFinder.h b/Source/System/PathFinder.h index 664e46ba28..88e29418a3 100644 --- a/Source/System/PathFinder.h +++ b/Source/System/PathFinder.h @@ -24,7 +24,7 @@ namespace RTE { Vector targetPos; }; - using PathCompleteCallback = std::function)>; + using PathCompleteCallback = std::function)>; /// /// Contains everything related to a PathNode on the path grid used by PathFinder. @@ -40,34 +40,34 @@ namespace RTE { /// /// Pointers to all adjacent PathNodes, in clockwise order with top first. These are not owned, and may be 0 if adjacent to non-wrapping scene border. /// - std::array AdjacentNodes; - PathNode *&Up = AdjacentNodes[0]; - PathNode *&UpRight = AdjacentNodes[1]; - PathNode *&Right = AdjacentNodes[2]; - PathNode *&RightDown = AdjacentNodes[3]; - PathNode *&Down = AdjacentNodes[4]; - PathNode *&DownLeft = AdjacentNodes[5]; - PathNode *&Left = AdjacentNodes[6]; - PathNode *&LeftUp = AdjacentNodes[7]; + std::array AdjacentNodes; + PathNode*& Up = AdjacentNodes[0]; + PathNode*& UpRight = AdjacentNodes[1]; + PathNode*& Right = AdjacentNodes[2]; + PathNode*& RightDown = AdjacentNodes[3]; + PathNode*& Down = AdjacentNodes[4]; + PathNode*& DownLeft = AdjacentNodes[5]; + PathNode*& Left = AdjacentNodes[6]; + PathNode*& LeftUp = AdjacentNodes[7]; /// /// The strongest material between us and our adjacent PathNodes, in clockwise order with top first. /// - std::array AdjacentNodeBlockingMaterials; - const Material *&UpMaterial = AdjacentNodeBlockingMaterials[0]; - const Material *&UpRightMaterial = AdjacentNodeBlockingMaterials[1]; - const Material *&RightMaterial = AdjacentNodeBlockingMaterials[2]; - const Material *&RightDownMaterial = AdjacentNodeBlockingMaterials[3]; - const Material *&DownMaterial = AdjacentNodeBlockingMaterials[4]; - const Material *&DownLeftMaterial = AdjacentNodeBlockingMaterials[5]; - const Material *&LeftMaterial = AdjacentNodeBlockingMaterials[6]; - const Material *&LeftUpMaterial = AdjacentNodeBlockingMaterials[7]; + std::array AdjacentNodeBlockingMaterials; + const Material*& UpMaterial = AdjacentNodeBlockingMaterials[0]; + const Material*& UpRightMaterial = AdjacentNodeBlockingMaterials[1]; + const Material*& RightMaterial = AdjacentNodeBlockingMaterials[2]; + const Material*& RightDownMaterial = AdjacentNodeBlockingMaterials[3]; + const Material*& DownMaterial = AdjacentNodeBlockingMaterials[4]; + const Material*& DownLeftMaterial = AdjacentNodeBlockingMaterials[5]; + const Material*& LeftMaterial = AdjacentNodeBlockingMaterials[6]; + const Material*& LeftUpMaterial = AdjacentNodeBlockingMaterials[7]; /// /// Constructor method used to instantiate a PathNode object in system memory and make it ready for use. /// /// Absolute position of the center of the PathNode in the scene. - explicit PathNode(const Vector &pos); + explicit PathNode(const Vector& pos); }; /// @@ -76,14 +76,16 @@ namespace RTE { class PathFinder : public Graph { public: - #pragma region Creation /// /// Constructor method used to instantiate a PathFinder object. /// /// The width and height in scene pixels that of each PathNode should represent. /// The block size that the PathNode cache is allocated from. Should be about a fourth of the total number of PathNodes. - PathFinder(int nodeDimension) { Clear(); Create(nodeDimension); } + PathFinder(int nodeDimension) { + Clear(); + Create(nodeDimension); + } /// /// Makes the PathFinder object ready for use. @@ -121,7 +123,7 @@ namespace RTE { /// The total minimum difficulty cost calculated between the two points on the scene. /// What material strength the search is capable of digging through. /// Success or failure, expressed as SOLVED, NO_SOLUTION, or START_END_SAME. - int CalculatePath(Vector start, Vector end, std::list &pathResult, float &totalCostResult, float digStrength); + int CalculatePath(Vector start, Vector end, std::list& pathResult, float& totalCostResult, float digStrength); /// /// Calculates and returns the least difficult path between two points on the current scene. @@ -151,7 +153,7 @@ namespace RTE { /// The deque of Boxes representing the updated areas. /// The maximum number of PathNodes we'll try to update this frame. True PathNode update count can be higher if we received a big box, as we always do at least 1 box. /// The set of PathNode ids that were updated. - std::vector RecalculateAreaCosts(std::deque &boxList, int nodeUpdateLimit); + std::vector RecalculateAreaCosts(std::deque& boxList, int nodeUpdateLimit); /// /// Updates a set of PathNodes, adjusting their transitions. @@ -159,7 +161,7 @@ namespace RTE { /// /// The set of PathNode IDs to update. /// Whether any PathNode costs changed. - bool UpdateNodeList(const std::vector &nodeVec); + bool UpdateNodeList(const std::vector& nodeVec); /// /// Implementation of the abstract interface of Graph. @@ -168,7 +170,7 @@ namespace RTE { /// Pointer to PathNode to start from. OWNERSHIP IS NOT TRANSFERRED! /// PathNode to end up at. OWNERSHIP IS NOT TRANSFERRED! /// The cost of the absolutely fastest possible way between the two points, as if traveled through air all the way. - float LeastCostEstimate(void *startState, void *endState) override; + float LeastCostEstimate(void* startState, void* endState) override; /// /// Implementation of the abstract interface of Graph. @@ -176,7 +178,7 @@ namespace RTE { /// /// Pointer to PathNode to get to cost of all adjacents for. OWNERSHIP IS NOT TRANSFERRED! /// An empty vector which will be filled out with all the valid PathNodes adjacent to the one passed in. If at non-wrapping edge of seam, those non existent PathNodes won't be added. - void AdjacentCost(void *state, std::vector *adjacentList) override; + void AdjacentCost(void* state, std::vector* adjacentList) override; /// /// Returns whether two position represent the same path nodes. @@ -207,15 +209,14 @@ namespace RTE { /// Since void* aren't really human readable, this will print out some concise info without an ending newline. /// /// The state to print out info about. - void PrintStateInfo(void *state) override {} + void PrintStateInfo(void* state) override {} #pragma endregion private: - static constexpr float c_NodeCostChangeEpsilon = 5.0F; //!< The minimum change in a PathNodes's cost for the pathfinder to recognize a change and reset itself. This is so minor changes (e.g. blood particles) don't force constant pathfinder resets. - MicroPather *m_Pather; //!< The actual pathing object that does the pathfinding work. Owned. - std::vector m_NodeGrid; //!< The array of PathNodes representing the grid on the scene. + MicroPather* m_Pather; //!< The actual pathing object that does the pathfinding work. Owned. + std::vector m_NodeGrid; //!< The array of PathNodes representing the grid on the scene. unsigned int m_NodeDimension; //!< The width and height of each PathNode, in pixels on the scene. int m_GridWidth; //!< The width of the pathing grid, in PathNodes. int m_GridHeight; //!< The height of the pathing grid, in PathNodes. @@ -227,7 +228,7 @@ namespace RTE { /// Gets the pather for this thread. Lazily-initialized for each new thread that needs a pather. /// /// The pather for this thread. - MicroPather * GetPather(); + MicroPather* GetPather(); #pragma region Path Cost Updates /// @@ -236,7 +237,7 @@ namespace RTE { /// Origin point. /// Destination point. /// The strongest material. - const Material * StrongestMaterialAlongLine(const Vector &start, const Vector &end) const; + const Material* StrongestMaterialAlongLine(const Vector& start, const Vector& end) const; /// /// Helper function for updating all the values of cost edges going out from a specific PathNodes. @@ -244,7 +245,7 @@ namespace RTE { /// /// The PathNode to update all costs of. It's safe to pass nullptr here. OWNERSHIP IS NOT TRANSFERRED! /// Whether the PathNodes costs changed. - bool UpdateNodeCosts(PathNode *node) const; + bool UpdateNodeCosts(PathNode* node) const; /// /// Helper function for getting the PathNode ids in a Box. @@ -258,14 +259,14 @@ namespace RTE { /// /// The Material to get the transition cost for. /// The transition cost for the Material. - float GetMaterialTransitionCost(const Material &material) const; + float GetMaterialTransitionCost(const Material& material) const; /// /// Gets the average cost for all transitions out of this PathNode, ignoring infinities/unpathable transitions. /// /// The PathNode to get the average transition cost for. /// The average transition cost. - float GetNodeAverageTransitionCost(const PathNode &node) const; + float GetNodeAverageTransitionCost(const PathNode& node) const; #pragma endregion /// @@ -274,7 +275,7 @@ namespace RTE { /// The X coordinate, in PathNodes. /// The Y coordinate, in PathNodes. /// The PathNode at the given coordinates. - PathNode * GetPathNodeAtGridCoords(int x, int y); + PathNode* GetPathNodeAtGridCoords(int x, int y); /// /// Gets the PathNode id at the given coordinates. @@ -290,8 +291,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - PathFinder(const PathFinder &reference) = delete; - PathFinder & operator=(const PathFinder &rhs) = delete; + PathFinder(const PathFinder& reference) = delete; + PathFinder& operator=(const PathFinder& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/PieQuadrant.cpp b/Source/System/PieQuadrant.cpp index 674fb30f31..036f0b5755 100644 --- a/Source/System/PieQuadrant.cpp +++ b/Source/System/PieQuadrant.cpp @@ -4,28 +4,28 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieQuadrant::Clear() { m_Enabled = true; m_Direction = Directions::None; m_MiddlePieSlice.reset(); - for (std::unique_ptr &pieSlice : m_LeftPieSlices) { + for (std::unique_ptr& pieSlice: m_LeftPieSlices) { pieSlice.reset(); } - for (std::unique_ptr &pieSlice : m_RightPieSlices) { + for (std::unique_ptr& pieSlice: m_RightPieSlices) { pieSlice.reset(); } m_SlotsForPieSlices.fill(nullptr); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieQuadrant::Create(const PieQuadrant &reference, const Entity *oldOriginalPieSliceSourceToCheck, const Entity *newOriginalPieSliceSourceToSet) { + void PieQuadrant::Create(const PieQuadrant& reference, const Entity* oldOriginalPieSliceSourceToCheck, const Entity* newOriginalPieSliceSourceToSet) { m_Enabled = reference.m_Enabled; m_Direction = reference.m_Direction; - for (const PieSlice *referencePieSlice : reference.GetFlattenedPieSlices()) { - PieSlice *pieSliceToAdd = dynamic_cast(referencePieSlice->Clone()); + for (const PieSlice* referencePieSlice: reference.GetFlattenedPieSlices()) { + PieSlice* pieSliceToAdd = dynamic_cast(referencePieSlice->Clone()); if (referencePieSlice->GetOriginalSource() == oldOriginalPieSliceSourceToCheck) { pieSliceToAdd->SetOriginalSource(newOriginalPieSliceSourceToSet); } @@ -33,48 +33,62 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector PieQuadrant::GetFlattenedPieSlices(bool inCCWOrder) const { - std::vector pieSlices; + std::vector PieQuadrant::GetFlattenedPieSlices(bool inCCWOrder) const { + std::vector pieSlices; if (inCCWOrder) { for (auto pieSliceReverseIterator = m_RightPieSlices.crbegin(); pieSliceReverseIterator < m_RightPieSlices.crend(); pieSliceReverseIterator++) { - if (*pieSliceReverseIterator) { pieSlices.emplace_back((*pieSliceReverseIterator).get()); } + if (*pieSliceReverseIterator) { + pieSlices.emplace_back((*pieSliceReverseIterator).get()); + } } - if (m_MiddlePieSlice) { pieSlices.emplace_back(m_MiddlePieSlice.get()); } - for (const std::unique_ptr &pieSlice : m_LeftPieSlices) { - if (pieSlice) { pieSlices.emplace_back(pieSlice.get()); } + if (m_MiddlePieSlice) { + pieSlices.emplace_back(m_MiddlePieSlice.get()); + } + for (const std::unique_ptr& pieSlice: m_LeftPieSlices) { + if (pieSlice) { + pieSlices.emplace_back(pieSlice.get()); + } } } else { - if (m_MiddlePieSlice) { pieSlices.emplace_back(m_MiddlePieSlice.get()); } - const std::array, 2> &firstSliceArray = (m_Direction == Directions::Left) ? m_RightPieSlices : m_LeftPieSlices; - const std::array, 2> &secondSliceArray = (m_Direction == Directions::Left) ? m_LeftPieSlices : m_RightPieSlices; + if (m_MiddlePieSlice) { + pieSlices.emplace_back(m_MiddlePieSlice.get()); + } + const std::array, 2>& firstSliceArray = (m_Direction == Directions::Left) ? m_RightPieSlices : m_LeftPieSlices; + const std::array, 2>& secondSliceArray = (m_Direction == Directions::Left) ? m_LeftPieSlices : m_RightPieSlices; for (int i = 0; i < firstSliceArray.size(); i++) { - if (firstSliceArray[i]) { pieSlices.emplace_back(firstSliceArray[i].get()); } - if (secondSliceArray[i]) { pieSlices.emplace_back(secondSliceArray[i].get()); } + if (firstSliceArray[i]) { + pieSlices.emplace_back(firstSliceArray[i].get()); + } + if (secondSliceArray[i]) { + pieSlices.emplace_back(secondSliceArray[i].get()); + } } } return pieSlices; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void PieQuadrant::RealignPieSlices() { if ((m_RightPieSlices[0] && !m_LeftPieSlices[0]) || (m_RightPieSlices[1] && !m_LeftPieSlices[1])) { - for (const PieSlice *pieSliceToRealign : GetFlattenedPieSlices()) { + for (const PieSlice* pieSliceToRealign: GetFlattenedPieSlices()) { AddPieSlice(RemovePieSlice(pieSliceToRealign)); } } m_SlotsForPieSlices.fill(nullptr); - std::vector pieSlices = GetFlattenedPieSlices(true); + std::vector pieSlices = GetFlattenedPieSlices(true); int oddRoundedSliceCount = (2 * static_cast(pieSlices.size() / 2)) + 1; float angleOffset = NormalizeAngleBetween0And2PI(c_DirectionsToRadiansMap.at(m_Direction) - c_QuarterPI); int currentSlot = 0; - for (PieSlice *pieSlice : pieSlices) { + for (PieSlice* pieSlice: pieSlices) { int sliceSlotCount = 1; - if (oddRoundedSliceCount < c_PieQuadrantSlotCount && pieSlice == m_MiddlePieSlice.get()) { sliceSlotCount = (oddRoundedSliceCount == 1) ? c_PieQuadrantSlotCount : c_PieQuadrantSlotCount - 2; } + if (oddRoundedSliceCount < c_PieQuadrantSlotCount && pieSlice == m_MiddlePieSlice.get()) { + sliceSlotCount = (oddRoundedSliceCount == 1) ? c_PieQuadrantSlotCount : c_PieQuadrantSlotCount - 2; + } if ((currentSlot == 0 || m_SlotsForPieSlices.at(currentSlot - 1) == nullptr) && pieSlices.size() % 2 == 0) { currentSlot++; angleOffset = NormalizeAngleBetween0And2PI(angleOffset + c_PieSliceSlotSize); @@ -91,9 +105,9 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieQuadrant::AddPieSlice(PieSlice *pieSliceToAdd) { + bool PieQuadrant::AddPieSlice(PieSlice* pieSliceToAdd) { if (!pieSliceToAdd) { return false; } @@ -106,7 +120,7 @@ namespace RTE { bool bothSidesEqual = ((!m_LeftPieSlices[0] && !m_RightPieSlices[0]) || (m_LeftPieSlices[0] && m_RightPieSlices[0])) && ((!m_LeftPieSlices[1] && !m_RightPieSlices[1]) || (m_LeftPieSlices[1] && m_RightPieSlices[1])); bool leftSideHasMoreSlices = !bothSidesEqual && ((m_LeftPieSlices[0] && !m_RightPieSlices[0]) || m_LeftPieSlices[1]); - std::array, 2> &sliceArrayToAddTo = leftSideHasMoreSlices ? m_RightPieSlices : m_LeftPieSlices; + std::array, 2>& sliceArrayToAddTo = leftSideHasMoreSlices ? m_RightPieSlices : m_LeftPieSlices; sliceArrayToAddTo[sliceArrayToAddTo[0] ? 1 : 0] = std::unique_ptr(pieSliceToAdd); sliceWasAdded = true; } @@ -114,22 +128,22 @@ namespace RTE { return sliceWasAdded; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice * PieQuadrant::RemovePieSlice(const PieSlice *pieSliceToRemove) { + PieSlice* PieQuadrant::RemovePieSlice(const PieSlice* pieSliceToRemove) { if (pieSliceToRemove == m_MiddlePieSlice.get()) { return m_MiddlePieSlice.release(); } - for (std::unique_ptr &pieSlice : m_LeftPieSlices) { + for (std::unique_ptr& pieSlice: m_LeftPieSlices) { if (pieSliceToRemove == pieSlice.get()) { return pieSlice.release(); } } - for (std::unique_ptr &pieSlice : m_RightPieSlices) { + for (std::unique_ptr& pieSlice: m_RightPieSlices) { if (pieSliceToRemove == pieSlice.get()) { return pieSlice.release(); } } return nullptr; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/PieQuadrant.h b/Source/System/PieQuadrant.h index 9e94a8f4bf..ef5fb1700d 100644 --- a/Source/System/PieQuadrant.h +++ b/Source/System/PieQuadrant.h @@ -11,7 +11,6 @@ namespace RTE { class PieQuadrant { public: - static constexpr int c_PieQuadrantSlotCount = 5; //!< The maximum number of PieSlices a PieQuadrant can have. static constexpr float c_PieSliceSlotSize = c_HalfPI / static_cast(c_PieQuadrantSlotCount); //!< The size of one PieSlice slot in PieQuadrants. @@ -21,7 +20,7 @@ namespace RTE { std::unique_ptr m_MiddlePieSlice; //!< A unique_ptr to the middle PieSlice of this PieQuadrant. std::array, c_PieQuadrantSlotCount / 2> m_LeftPieSlices; //!< An array of unique_ptrs to the left side PieSlices of this PieQuadrant. std::array, c_PieQuadrantSlotCount / 2> m_RightPieSlices; //!< An array of unique_ptrs to the right side PieSlices of this PieQuadrant. - std::array m_SlotsForPieSlices; //!< An array representing the slots in this PieQuadrant, via pointers to the PieSlices filling each slot. + std::array m_SlotsForPieSlices; //!< An array representing the slots in this PieQuadrant, via pointers to the PieSlices filling each slot. #pragma region Creation /// @@ -35,7 +34,7 @@ namespace RTE { /// A reference to the PieQuadrant to deep copy. /// A pointer to the old original source to check. This is generally the PieMenu the reference quadrant belongs to. /// A pointer to the new original source to be set for PieSlices whose reference's original source is the old original source to check. This is generally the PieMenu this quadrant belongs to. - void Create(const PieQuadrant &reference, const Entity *oldOriginalPieSliceSourceToCheck, const Entity *newOriginalPieSliceSourceToSet); + void Create(const PieQuadrant& reference, const Entity* oldOriginalPieSliceSourceToCheck, const Entity* newOriginalPieSliceSourceToSet); #pragma endregion #pragma region Destruction @@ -50,7 +49,7 @@ namespace RTE { /// Gets whether or not this PieQuadrant contains the given PieSlice. /// /// Whether or not this PieQuadrant contains the given PieSlice. - bool ContainsPieSlice(const PieSlice *sliceToCheck) const { return sliceToCheck == m_MiddlePieSlice.get() || sliceToCheck == m_LeftPieSlices[0].get() || sliceToCheck == m_RightPieSlices[0].get() || sliceToCheck == m_LeftPieSlices[1].get() || sliceToCheck == m_RightPieSlices[1].get(); } + bool ContainsPieSlice(const PieSlice* sliceToCheck) const { return sliceToCheck == m_MiddlePieSlice.get() || sliceToCheck == m_LeftPieSlices[0].get() || sliceToCheck == m_RightPieSlices[0].get() || sliceToCheck == m_LeftPieSlices[1].get() || sliceToCheck == m_RightPieSlices[1].get(); } /// /// Gets a vector of non-owning pointers to the PieSlices in this PieQuadrant. @@ -58,7 +57,7 @@ namespace RTE { /// Alternatively it can go in CCW order, getting the outermost right slice and moving inwards through the middle and then left slices. /// /// Whether to get flattened slices in counter-clockwise order. Defaults to false. - std::vector GetFlattenedPieSlices(bool inCCWOrder = false) const; + std::vector GetFlattenedPieSlices(bool inCCWOrder = false) const; #pragma endregion #pragma region Concrete Methods @@ -70,25 +69,24 @@ namespace RTE { /// /// Adds the PieSlice to the quadrant. PieSlices are added to the middle, then the left side, then the right side so things fill evenly. Ownership IS transferred! /// - bool AddPieSlice(PieSlice *pieSliceToAdd); + bool AddPieSlice(PieSlice* pieSliceToAdd); /// /// Removes the passed in PieSlice from this PieQuadrant. Ownership IS transferred to the caller! /// /// The PieSlice to be removed from this PieQuadrant. Ownership IS transferred to the caller! - PieSlice * RemovePieSlice(const PieSlice *pieSliceToRemove); + PieSlice* RemovePieSlice(const PieSlice* pieSliceToRemove); #pragma endregion private: - /// /// Clears all the member variables of this PieQuadrant. /// void Clear(); // Disallow the use of some implicit methods. - PieQuadrant(const PieQuadrant &reference) = delete; - PieQuadrant & operator=(const PieQuadrant &rhs) = delete; + PieQuadrant(const PieQuadrant& reference) = delete; + PieQuadrant& operator=(const PieQuadrant& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/RTEError.cpp b/Source/System/RTEError.cpp index ecf27e547b..ffaf58ac10 100644 --- a/Source/System/RTEError.cpp +++ b/Source/System/RTEError.cpp @@ -20,7 +20,7 @@ namespace RTE { std::string RTEError::s_LastIgnoredAssertDescription = ""; std::source_location RTEError::s_LastIgnoredAssertLocation = {}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 /// @@ -29,7 +29,7 @@ namespace RTE { /// Even if we "translate" SE exceptions to C++ exceptions it's still ass and doesn't really work, so this is what it is and it is good enough. /// /// Struct containing information about the exception. This will be provided by the OS exception handler. - static LONG WINAPI RTEWindowsExceptionHandler([[maybe_unused]] EXCEPTION_POINTERS *exceptPtr) { + static LONG WINAPI RTEWindowsExceptionHandler([[maybe_unused]] EXCEPTION_POINTERS* exceptPtr) { // This sorta half-assedly works in x86 because exception handling is slightly different, but since the main target is x64 we can just not care about it. // Something something ESP. ESP is a guitar brand. #ifndef TARGET_MACHINE_X86 @@ -51,35 +51,55 @@ namespace RTE { }; // Returns a string with the type of the exception from the passed in code. - static auto getExceptionDescriptionFromCode = [](const DWORD &exceptCode) -> std::string { + static auto getExceptionDescriptionFromCode = [](const DWORD& exceptCode) -> std::string { switch (exceptCode) { - case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION"; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; - case EXCEPTION_BREAKPOINT: return "EXCEPTION_BREAKPOINT"; - case EXCEPTION_DATATYPE_MISALIGNMENT: return "EXCEPTION_DATATYPE_MISALIGNMENT"; - case EXCEPTION_FLT_DENORMAL_OPERAND: return "EXCEPTION_FLT_DENORMAL_OPERAND"; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; - case EXCEPTION_FLT_INEXACT_RESULT: return "EXCEPTION_FLT_INEXACT_RESULT"; - case EXCEPTION_FLT_INVALID_OPERATION: return "EXCEPTION_FLT_INVALID_OPERATION"; - case EXCEPTION_FLT_OVERFLOW: return "EXCEPTION_FLT_OVERFLOW"; - case EXCEPTION_FLT_STACK_CHECK: return "EXCEPTION_FLT_STACK_CHECK"; - case EXCEPTION_FLT_UNDERFLOW: return "EXCEPTION_FLT_UNDERFLOW"; - case EXCEPTION_ILLEGAL_INSTRUCTION: return "EXCEPTION_ILLEGAL_INSTRUCTION"; - case EXCEPTION_IN_PAGE_ERROR: return "EXCEPTION_IN_PAGE_ERROR"; - case EXCEPTION_INT_DIVIDE_BY_ZERO: return "EXCEPTION_INT_DIVIDE_BY_ZERO"; - case EXCEPTION_INT_OVERFLOW: return "EXCEPTION_INT_OVERFLOW"; - case EXCEPTION_INVALID_DISPOSITION: return "EXCEPTION_INVALID_DISPOSITION"; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; - case EXCEPTION_PRIV_INSTRUCTION: return "EXCEPTION_PRIV_INSTRUCTION"; - case EXCEPTION_SINGLE_STEP: return "EXCEPTION_SINGLE_STEP"; - case EXCEPTION_STACK_OVERFLOW: return "EXCEPTION_STACK_OVERFLOW"; + case EXCEPTION_ACCESS_VIOLATION: + return "EXCEPTION_ACCESS_VIOLATION"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; + case EXCEPTION_BREAKPOINT: + return "EXCEPTION_BREAKPOINT"; + case EXCEPTION_DATATYPE_MISALIGNMENT: + return "EXCEPTION_DATATYPE_MISALIGNMENT"; + case EXCEPTION_FLT_DENORMAL_OPERAND: + return "EXCEPTION_FLT_DENORMAL_OPERAND"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_INEXACT_RESULT: + return "EXCEPTION_FLT_INEXACT_RESULT"; + case EXCEPTION_FLT_INVALID_OPERATION: + return "EXCEPTION_FLT_INVALID_OPERATION"; + case EXCEPTION_FLT_OVERFLOW: + return "EXCEPTION_FLT_OVERFLOW"; + case EXCEPTION_FLT_STACK_CHECK: + return "EXCEPTION_FLT_STACK_CHECK"; + case EXCEPTION_FLT_UNDERFLOW: + return "EXCEPTION_FLT_UNDERFLOW"; + case EXCEPTION_ILLEGAL_INSTRUCTION: + return "EXCEPTION_ILLEGAL_INSTRUCTION"; + case EXCEPTION_IN_PAGE_ERROR: + return "EXCEPTION_IN_PAGE_ERROR"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + return "EXCEPTION_INT_DIVIDE_BY_ZERO"; + case EXCEPTION_INT_OVERFLOW: + return "EXCEPTION_INT_OVERFLOW"; + case EXCEPTION_INVALID_DISPOSITION: + return "EXCEPTION_INVALID_DISPOSITION"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; + case EXCEPTION_PRIV_INSTRUCTION: + return "EXCEPTION_PRIV_INSTRUCTION"; + case EXCEPTION_SINGLE_STEP: + return "EXCEPTION_SINGLE_STEP"; + case EXCEPTION_STACK_OVERFLOW: + return "EXCEPTION_STACK_OVERFLOW"; default: return "UNKNOWN EXCEPTION"; } }; // Attempts to get a symbol name from the exception address. - static auto getSymbolNameFromAddress = [](HANDLE &procHandle, const size_t &exceptAddr) { + static auto getSymbolNameFromAddress = [](HANDLE& procHandle, const size_t& exceptAddr) { if (SymInitialize(procHandle, nullptr, TRUE)) { SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); @@ -115,7 +135,8 @@ namespace RTE { std::string symbolNameAtAddress = getSymbolNameFromAddress(processHandle, exceptionAddress); RTEError::FormatFunctionSignature(symbolNameAtAddress); - exceptionDescription << getExceptionDescriptionFromCode(exceptionCode) << " at address 0x" << std::uppercase << std::hex << exceptionAddress << ".\n\n" << symbolNameAtAddress << std::endl; + exceptionDescription << getExceptionDescriptionFromCode(exceptionCode) << " at address 0x" << std::uppercase << std::hex << exceptionAddress << ".\n\n" + << symbolNameAtAddress << std::endl; RTEStackTrace stackTrace; @@ -125,7 +146,7 @@ namespace RTE { } #endif -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RTEError::SetExceptionHandlers() { // Basic handling for C++ exceptions. Doesn't give us much meaningful information. @@ -135,9 +156,9 @@ namespace RTE { if (currentException) { try { std::rethrow_exception(currentException); - } catch (const std::bad_exception &exception) { + } catch (const std::bad_exception& exception) { RTEError::UnhandledExceptionFunc("Unable to get exception description because: " + std::string(exception.what()) + ".\n"); - } catch (const std::exception &exception) { + } catch (const std::exception& exception) { RTEError::UnhandledExceptionFunc(std::string(exception.what()) + ".\n"); } } else { @@ -158,20 +179,23 @@ namespace RTE { #endif } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::ShowMessageBox(const std::string &message) { + void RTEError::ShowMessageBox(const std::string& message) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "RTE Warning! (>_<)", message.c_str(), nullptr); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool RTEError::ShowAbortMessageBox(const std::string &message) { - enum AbortMessageButton { ButtonInvalid, ButtonExit, ButtonRestart }; + bool RTEError::ShowAbortMessageBox(const std::string& message) { + enum AbortMessageButton { + ButtonInvalid, + ButtonExit, + ButtonRestart + }; std::vector abortMessageBoxButtons = { - SDL_MessageBoxButtonData(SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, AbortMessageButton::ButtonExit, "OK") - }; + SDL_MessageBoxButtonData(SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, AbortMessageButton::ButtonExit, "OK")}; // Don't even show the restart button in debug builds. #ifdef RELEASE_BUILD @@ -182,14 +206,13 @@ namespace RTE { #endif SDL_MessageBoxData abortMessageBox = { - SDL_MESSAGEBOX_ERROR, - g_WindowMan.GetWindow(), - "RTE Aborted! (x_x)", - message.c_str(), - static_cast(abortMessageBoxButtons.size()), - abortMessageBoxButtons.data(), - nullptr - }; + SDL_MESSAGEBOX_ERROR, + g_WindowMan.GetWindow(), + "RTE Aborted! (x_x)", + message.c_str(), + static_cast(abortMessageBoxButtons.size()), + abortMessageBoxButtons.data(), + nullptr}; int pressedButton = AbortMessageButton::ButtonInvalid; SDL_ShowMessageBox(&abortMessageBox, &pressedButton); @@ -197,26 +220,29 @@ namespace RTE { return pressedButton == AbortMessageButton::ButtonRestart; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool RTEError::ShowAssertMessageBox(const std::string &message) { - enum AssertMessageButton { ButtonInvalid, ButtonAbort, ButtonIgnore, ButtonIgnoreAll }; + bool RTEError::ShowAssertMessageBox(const std::string& message) { + enum AssertMessageButton { + ButtonInvalid, + ButtonAbort, + ButtonIgnore, + ButtonIgnoreAll + }; std::vector assertMessageBoxButtons = { - SDL_MessageBoxButtonData(SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, AssertMessageButton::ButtonAbort, "Abort"), - SDL_MessageBoxButtonData(0, AssertMessageButton::ButtonIgnore, "Ignore"), - SDL_MessageBoxButtonData(0, AssertMessageButton::ButtonIgnoreAll, "Ignore All") - }; + SDL_MessageBoxButtonData(SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, AssertMessageButton::ButtonAbort, "Abort"), + SDL_MessageBoxButtonData(0, AssertMessageButton::ButtonIgnore, "Ignore"), + SDL_MessageBoxButtonData(0, AssertMessageButton::ButtonIgnoreAll, "Ignore All")}; SDL_MessageBoxData assertMessageBox = { - SDL_MESSAGEBOX_ERROR, - g_WindowMan.GetWindow(), - "RTE Assert! (x_x)", - message.c_str(), - static_cast(assertMessageBoxButtons.size()), - assertMessageBoxButtons.data(), - nullptr - }; + SDL_MESSAGEBOX_ERROR, + g_WindowMan.GetWindow(), + "RTE Assert! (x_x)", + message.c_str(), + static_cast(assertMessageBoxButtons.size()), + assertMessageBoxButtons.data(), + nullptr}; int pressedButton = AssertMessageButton::ButtonInvalid; SDL_ShowMessageBox(&assertMessageBox, &pressedButton); @@ -228,9 +254,9 @@ namespace RTE { return pressedButton == AssertMessageButton::ButtonAbort; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::UnhandledExceptionFunc(const std::string &description, const std::string &callstack) { + void RTEError::UnhandledExceptionFunc(const std::string& description, const std::string& callstack) { s_CurrentlyAborting = true; std::string exceptionMessage = "Runtime Error due to unhandled exception!\n\n" + description; @@ -282,9 +308,9 @@ namespace RTE { AbortAction; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::AbortFunc(const std::string &description, const std::source_location &srcLocation) { + void RTEError::AbortFunc(const std::string& description, const std::source_location& srcLocation) { s_CurrentlyAborting = true; if (!System::IsInExternalModuleValidationMode()) { @@ -349,9 +375,9 @@ namespace RTE { AbortAction; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::AssertFunc(const std::string &description, const std::source_location &srcLocation) { + void RTEError::AssertFunc(const std::string& description, const std::source_location& srcLocation) { if (System::IsInExternalModuleValidationMode()) { AbortFunc(description, srcLocation); } @@ -369,8 +395,8 @@ namespace RTE { if (!s_IgnoreAllAsserts) { std::string assertMessage = - "Assertion in file '" + fileName + "', line " + lineNum + ",\nin function '" + funcName + "'\nbecause:\n\n" + description + "\n\n" - "You may choose to ignore this and crash immediately\nor at some unexpected point later on.\n\nProceed at your own risk!"; + "Assertion in file '" + fileName + "', line " + lineNum + ",\nin function '" + funcName + "'\nbecause:\n\n" + description + "\n\n" + "You may choose to ignore this and crash immediately\nor at some unexpected point later on.\n\nProceed at your own risk!"; if (ShowAssertMessageBox(assertMessage)) { AbortFunc(description, srcLocation); @@ -387,14 +413,14 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool RTEError::DumpAbortScreen() { int success = -1; if (glReadPixels != nullptr) { - int w ,h; + int w, h; SDL_GL_GetDrawableSize(g_WindowMan.GetWindow(), &w, &h); - if (!(w>0 && h>0)) { + if (!(w > 0 && h > 0)) { return false; } BITMAP* readBuffer = create_bitmap_ex(24, w, h); @@ -417,7 +443,7 @@ namespace RTE { return success == 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool RTEError::DumpAbortSave() { bool success = false; @@ -427,16 +453,14 @@ namespace RTE { return success; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::FormatFunctionSignature(std::string &symbolName) { + void RTEError::FormatFunctionSignature(std::string& symbolName) { // TODO: Expand this with more dumb signatures, or make something that makes more sense. - static const std::array, 3> stlSigs {{ - {std::regex("( >)"), ">"}, - {std::regex("(std::basic_string,std::allocator>)"), "std::string"}, - {std::regex("(class ?std::basic_string,class ?std::allocator>)"), "std::string"} - }}; - for (const auto &[fullSig, simpleSig] : stlSigs) { + static const std::array, 3> stlSigs{{{std::regex("( >)"), ">"}, + {std::regex("(std::basic_string,std::allocator>)"), "std::string"}, + {std::regex("(class ?std::basic_string,class ?std::allocator>)"), "std::string"}}}; + for (const auto& [fullSig, simpleSig]: stlSigs) { symbolName = std::regex_replace(symbolName, fullSig, simpleSig); } for (size_t pos = 0;;) { @@ -450,4 +474,4 @@ namespace RTE { } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/RTEError.h b/Source/System/RTEError.h index a739540d1f..099fa148d7 100644 --- a/Source/System/RTEError.h +++ b/Source/System/RTEError.h @@ -26,7 +26,6 @@ namespace RTE { class RTEError { public: - static bool s_CurrentlyAborting; //!< Flag to prevent a potential recursive fault while attempting to save the game when aborting. static bool s_IgnoreAllAsserts; //!< Whether to skip the assert dialog and just let everything burn at whatever point that happens. static std::string s_LastIgnoredAssertDescription; //!< The last ignored assert message. @@ -41,50 +40,49 @@ namespace RTE { /// Pops up a message box dialog in the OS. For debug purposes mostly. /// /// The string that the message box should display. - static void ShowMessageBox(const std::string &message); + static void ShowMessageBox(const std::string& message); /// /// Abort on unhandled exception function. Will try save the current game, to dump a screenshot, dump the console log and show an abort message. Then quit the program immediately. /// /// Message explaining the exception. /// The call stack in string form. - static void UnhandledExceptionFunc(const std::string &description, const std::string &callstack = ""); + static void UnhandledExceptionFunc(const std::string& description, const std::string& callstack = ""); /// /// Abort on Error function. Will try save the current game, to dump a screenshot, dump the console log and show an abort message. Then quit the program immediately. /// /// Message explaining the reason for aborting. /// std::source_location corresponding to the location of the call site. - [[noreturn]] static void AbortFunc(const std::string &description, const std::source_location &srcLocation); + [[noreturn]] static void AbortFunc(const std::string& description, const std::source_location& srcLocation); /// /// An assert, which will prompt to abort or ignore it. /// /// The description of the assertion. /// std::source_location corresponding to the location of the call site. - static void AssertFunc(const std::string &description, const std::source_location &srcLocation); + static void AssertFunc(const std::string& description, const std::source_location& srcLocation); /// /// Formats function signatures so they're slightly more sane. /// /// Reference to the function signature to format. - static void FormatFunctionSignature(std::string &funcSig); + static void FormatFunctionSignature(std::string& funcSig); private: - /// /// Pops up the abort message box dialog in the OS, notifying the user about a runtime error. /// /// The string that the message box should display. /// Whether to restart the game by launching a new instance, or proceed to exit. - static bool ShowAbortMessageBox(const std::string &message); + static bool ShowAbortMessageBox(const std::string& message); /// /// Pops up the assert message box dialog in the OS, notifying the user about a runtime error. /// /// The string that the message box should display. /// Whether to abort, or ignore the assert and continue execution. - static bool ShowAssertMessageBox(const std::string &message); + static bool ShowAssertMessageBox(const std::string& message); /// /// Saves the current frame to a file. @@ -100,13 +98,13 @@ namespace RTE { }; #define RTEAbort(description) \ - if (!RTEError::s_CurrentlyAborting) { \ - RTEError::AbortFunc(description, std::source_location::current()); \ + if (!RTEError::s_CurrentlyAborting) { \ + RTEError::AbortFunc(description, std::source_location::current()); \ } #define RTEAssert(expression, description) \ - if (!(expression)) { \ - RTEError::AssertFunc(description, std::source_location::current()); \ + if (!(expression)) { \ + RTEError::AssertFunc(description, std::source_location::current()); \ } -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/RTEStackTrace.cpp b/Source/System/RTEStackTrace.cpp index 500d7dab8d..d5e4d311d9 100644 --- a/Source/System/RTEStackTrace.cpp +++ b/Source/System/RTEStackTrace.cpp @@ -2,9 +2,9 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string RTEStackTrace::GetCallStackAsString(const HANDLE &handle, const CONTEXT *context) { + std::string RTEStackTrace::GetCallStackAsString(const HANDLE& handle, const CONTEXT* context) { m_CallstackStream.clear(); if (!handle || !context) { @@ -17,10 +17,10 @@ namespace RTE { return m_CallstackStream.str(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RTEStackTrace::OnOutput(LPCSTR text) { this->StackWalker::OnOutput(text); m_CallstackStream << text; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/RTEStackTrace.h b/Source/System/RTEStackTrace.h index 65c61adb5e..5da8e0abd9 100644 --- a/Source/System/RTEStackTrace.h +++ b/Source/System/RTEStackTrace.h @@ -12,12 +12,12 @@ namespace RTE { class RTEStackTrace : public StackWalker { public: - /// /// Constructor method used to instantiate an RTEStackTrace object in system memory and make it ready for use. /// /// - RTEStackTrace() : StackWalker() {} + RTEStackTrace() : + StackWalker() {} /// /// Destructor method used to clean up a RTEStackTrace object before deletion from system memory. @@ -30,10 +30,9 @@ namespace RTE { /// Handle to the current process. If none provided will get the current thread handle. /// Register data. If none provided will get it from the caller. /// A string with the call stack. - std::string GetCallStackAsString(const HANDLE &handle = nullptr, const CONTEXT *context = nullptr); + std::string GetCallStackAsString(const HANDLE& handle = nullptr, const CONTEXT* context = nullptr); protected: - /// /// Redirects the output string to the member string stream. /// @@ -41,12 +40,11 @@ namespace RTE { void OnOutput(LPCSTR text) override; private: - std::stringstream m_CallstackStream; //!< Call stack output stream. // Disallow the use of some implicit methods. - RTEStackTrace(const RTEStackTrace &reference) = delete; - RTEStackTrace & operator=(const RTEStackTrace &rhs) = delete; + RTEStackTrace(const RTEStackTrace& reference) = delete; + RTEStackTrace& operator=(const RTEStackTrace& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/RTETools.cpp b/Source/System/RTETools.cpp index ac944a3112..efeef0812d 100644 --- a/Source/System/RTETools.cpp +++ b/Source/System/RTETools.cpp @@ -2,12 +2,11 @@ #include "Vector.h" - namespace RTE { RandomGenerator g_RandomGenerator; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SeedRNG() { // Use a constant seed for determinism. @@ -20,17 +19,17 @@ namespace RTE { const uint64_t hugePrime = 18446744073709551557; uint64_t seedResult = 0; - for (char c : seedString) { + for (char c: seedString) { seedResult += static_cast(c) * hugePrime; } - + return static_cast(seedResult); }(); g_RandomGenerator.Seed(constSeed); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float LERP(float scaleStart, float scaleEnd, float startValue, float endValue, float progressScalar) { if (progressScalar <= scaleStart) { @@ -41,7 +40,7 @@ namespace RTE { return startValue + ((progressScalar - scaleStart) * ((endValue - startValue) / (scaleEnd - scaleStart))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float EaseIn(float start, float end, float progressScalar) { if (progressScalar <= 0) { @@ -53,7 +52,7 @@ namespace RTE { return (end - start) * (std::sin(-t * c_HalfPI) + 1) + start; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float EaseOut(float start, float end, float progressScalar) { if (progressScalar <= 0) { @@ -64,15 +63,15 @@ namespace RTE { return (end - start) * -std::sin(-progressScalar * c_HalfPI) + start; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float EaseInOut(float start, float end, float progressScalar) { return start * (2 * std::pow(progressScalar, 3) - 3 * std::pow(progressScalar, 2) + 1) + end * (3 * std::pow(progressScalar, 2) - 2 * std::pow(progressScalar, 3)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Clamp(float &value, float upperLimit, float lowerLimit) { + bool Clamp(float& value, float upperLimit, float lowerLimit) { // Straighten out the limits if (upperLimit < lowerLimit) { float temp = upperLimit; @@ -90,7 +89,7 @@ namespace RTE { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Limit(float value, float upperLimit, float lowerLimit) { // Straighten out the limits @@ -109,7 +108,7 @@ namespace RTE { return value; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float NormalizeAngleBetween0And2PI(float angle) { while (angle < 0) { @@ -118,7 +117,7 @@ namespace RTE { return (angle > c_TwoPI) ? fmodf(angle + c_TwoPI, c_TwoPI) : angle; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float NormalizeAngleBetweenNegativePIAndPI(float angle) { while (angle < 0) { @@ -127,7 +126,7 @@ namespace RTE { return (angle > c_PI) ? fmodf(angle + c_PI, c_TwoPI) - c_PI : angle; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool AngleWithinRange(float angleToCheck, float startAngle, float endAngle) { angleToCheck = NormalizeAngleBetween0And2PI(angleToCheck); @@ -137,7 +136,7 @@ namespace RTE { return endAngle >= startAngle ? (angleToCheck >= startAngle && angleToCheck <= endAngle) : (angleToCheck >= startAngle || angleToCheck <= endAngle); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float ClampAngle(float angleToClamp, float startAngle, float endAngle) { angleToClamp = NormalizeAngleBetween0And2PI(angleToClamp); @@ -154,19 +153,19 @@ namespace RTE { return angleToClamp; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool WithinBox(const Vector &point, float left, float top, float right, float bottom) { + bool WithinBox(const Vector& point, float left, float top, float right, float bottom) { return point.m_X >= left && point.m_X < right && point.m_Y >= top && point.m_Y < bottom; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool WithinBox(const Vector &point, const Vector &boxPos, float width, float height) { + bool WithinBox(const Vector& point, const Vector& boxPos, float width, float height) { return point.m_X >= boxPos.m_X && point.m_X < (boxPos.m_X + width) && point.m_Y >= boxPos.m_Y && point.m_Y < (boxPos.m_Y + height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string RoundFloatToPrecision(float input, int precision, int roundingMode) { if (roundingMode == 0) { @@ -202,16 +201,16 @@ namespace RTE { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // From https://stackoverflow.com/a/66764681, under license https://creativecommons.org/licenses/by-sa/4.0/. Minor modifications - uint64_t Hash(const std::string &text) { + uint64_t Hash(const std::string& text) { constexpr uint64_t fnv_prime = 1099511628211ULL; constexpr uint64_t fnv_offset_basis = 14695981039346656037ULL; - + uint64_t hash = fnv_offset_basis; - - for(auto c: text) { + + for (auto c: text) { hash ^= c; hash *= fnv_prime; } @@ -219,9 +218,9 @@ namespace RTE { return hash; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GetCaseInsensitiveFullPath(const std::string &fullPath) { + std::string GetCaseInsensitiveFullPath(const std::string& fullPath) { if (std::filesystem::exists(fullPath)) { return fullPath; } @@ -235,7 +234,7 @@ namespace RTE { // Iterate over all entries in the path part's directory, // to check if the path part is in there case insensitively - for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { + for (const std::filesystem::path& filesystemEntryPath: std::filesystem::directory_iterator(inspectedPath)) { if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { inspectedPath = filesystemEntryPath; @@ -257,4 +256,4 @@ namespace RTE { return inspectedPath.generic_string(); } -} +} // namespace RTE diff --git a/Source/System/RTETools.h b/Source/System/RTETools.h index b69b4fdcbd..036ce714fa 100644 --- a/Source/System/RTETools.h +++ b/Source/System/RTETools.h @@ -10,7 +10,7 @@ namespace RTE { class Vector; - #pragma region Random Numbers +#pragma region Random Numbers class RandomGenerator { std::mt19937 m_RNG; //!< The random number generator used for all random functions. @@ -64,7 +64,9 @@ namespace RTE { /// Uniformly distributed random number in the range [min, max]. template typename std::enable_if::value, floatType>::type RandomNum(floatType min, floatType max) { - if (max < min) { std::swap(min, max); } + if (max < min) { + std::swap(min, max); + } return (std::uniform_real_distribution(floatType(0.0), std::nextafter(max - min, std::numeric_limits::max()))(m_RNG) + min); } @@ -76,12 +78,14 @@ namespace RTE { /// Uniformly distributed random number in the range [min, max]. template typename std::enable_if::value, intType>::type RandomNum(intType min, intType max) { - if (max < min) { std::swap(min, max); } + if (max < min) { + std::swap(min, max); + } return (std::uniform_int_distribution(intType(0), max - min)(m_RNG) + min); } }; - extern RandomGenerator g_RandomGenerator; //!< The global random number generator used in our simulation thread. + extern RandomGenerator g_RandomGenerator; //!< The global random number generator used in our simulation thread. /// /// Seed global the global random number generators. @@ -119,218 +123,219 @@ namespace RTE { typename std::enable_if::value, intType>::type RandomNum(intType min, intType max) { return g_RandomGenerator.RandomNum(min, max); } - #pragma endregion - +#pragma endregion - #pragma region Interpolation - /// - /// Simple Linear Interpolation, with an added bonus: scaleStart and scaleEnd let you define your scale, where 0 and 1 would be standard scale. - /// This scale is used to normalize your progressScalar value and LERP accordingly. - /// - /// The start of the scale to LERP along. - /// The end of the scale to LERP along. - /// The start value of your LERP. - /// The end value of your LERP. - /// How far your LERP has progressed. Automatically normalized through use of scaleStart and scaleEnd. - /// Interpolated value. - float LERP(float scaleStart, float scaleEnd, float startValue, float endValue, float progressScalar); +#pragma region Interpolation + /// + /// Simple Linear Interpolation, with an added bonus: scaleStart and scaleEnd let you define your scale, where 0 and 1 would be standard scale. + /// This scale is used to normalize your progressScalar value and LERP accordingly. + /// + /// The start of the scale to LERP along. + /// The end of the scale to LERP along. + /// The start value of your LERP. + /// The end value of your LERP. + /// How far your LERP has progressed. Automatically normalized through use of scaleStart and scaleEnd. + /// Interpolated value. + float LERP(float scaleStart, float scaleEnd, float startValue, float endValue, float progressScalar); - /// - /// Nonlinear ease-in interpolation. Starts slow. - /// - /// Start value. - /// End value. - /// Normalized positive progress scalar (0 - 1.0). - /// Interpolated value. - float EaseIn(float start, float end, float progressScalar); + /// + /// Nonlinear ease-in interpolation. Starts slow. + /// + /// Start value. + /// End value. + /// Normalized positive progress scalar (0 - 1.0). + /// Interpolated value. + float EaseIn(float start, float end, float progressScalar); - /// - /// Nonlinear ease-out interpolation. Slows down toward the end. - /// - /// Start value. - /// End value. - /// Normalized positive progress scalar (0 - 1.0). - /// Interpolated value. - float EaseOut(float start, float end, float progressScalar); + /// + /// Nonlinear ease-out interpolation. Slows down toward the end. + /// + /// Start value. + /// End value. + /// Normalized positive progress scalar (0 - 1.0). + /// Interpolated value. + float EaseOut(float start, float end, float progressScalar); - /// - /// Nonlinear ease-in-out interpolation. Slows down in the start and end. - /// - /// Start value. - /// End value. - /// Normalized positive progress scalar (0 - 1.0). - /// Interpolated value. - float EaseInOut(float start, float end, float progressScalar); - #pragma endregion - - #pragma region Clamping - /// - /// Clamps a value between two limit values. - /// - /// Value to clamp. - /// Upper limit of value. - /// Lower limit of value. - /// True if either limit is currently reached, False if not. - bool Clamp(float &value, float upperLimit, float lowerLimit); + /// + /// Nonlinear ease-in-out interpolation. Slows down in the start and end. + /// + /// Start value. + /// End value. + /// Normalized positive progress scalar (0 - 1.0). + /// Interpolated value. + float EaseInOut(float start, float end, float progressScalar); +#pragma endregion + +#pragma region Clamping + /// + /// Clamps a value between two limit values. + /// + /// Value to clamp. + /// Upper limit of value. + /// Lower limit of value. + /// True if either limit is currently reached, False if not. + bool Clamp(float& value, float upperLimit, float lowerLimit); - /// - /// Clamps a value between two limit values. - /// - /// Value to clamp. - /// Upper limit of value. - /// Lower limit of value. - /// Upper/Lower limit value if limit is currently reached, value between limits if not. - float Limit(float value, float upperLimit, float lowerLimit); - #pragma endregion - - #pragma region Rounding - /// - /// Rounds a float to a set fixed point precision (digits after decimal point) with option to always ceil or always floor the remainder. - /// - /// The input float to round. - /// The precision to round to, i.e. the number of digits after the decimal points. - /// Method of rounding to use. 0 for system default, 1 for floored remainder, 2 for ceiled remainder. - /// A string of the float, rounded and displayed to chosen precision. - std::string RoundFloatToPrecision(float input, int precision, int roundingMode = 0); + /// + /// Clamps a value between two limit values. + /// + /// Value to clamp. + /// Upper limit of value. + /// Lower limit of value. + /// Upper/Lower limit value if limit is currently reached, value between limits if not. + float Limit(float value, float upperLimit, float lowerLimit); +#pragma endregion + +#pragma region Rounding + /// + /// Rounds a float to a set fixed point precision (digits after decimal point) with option to always ceil or always floor the remainder. + /// + /// The input float to round. + /// The precision to round to, i.e. the number of digits after the decimal points. + /// Method of rounding to use. 0 for system default, 1 for floored remainder, 2 for ceiled remainder. + /// A string of the float, rounded and displayed to chosen precision. + std::string RoundFloatToPrecision(float input, int precision, int roundingMode = 0); - /// - /// Rounds an integer to the specified nearest multiple. - /// For example, if the arguments are 63 and 5, the returned value will be 65. - /// - /// The number to round to the nearest multiple. - /// The multiple to round to. - /// An integer rounded to the specified nearest multiple. - inline int RoundToNearestMultiple(int num, int multiple) { return static_cast(std::round(static_cast(num) / static_cast(multiple)) * static_cast(multiple)); } - #pragma endregion + /// + /// Rounds an integer to the specified nearest multiple. + /// For example, if the arguments are 63 and 5, the returned value will be 65. + /// + /// The number to round to the nearest multiple. + /// The multiple to round to. + /// An integer rounded to the specified nearest multiple. + inline int RoundToNearestMultiple(int num, int multiple) { return static_cast(std::round(static_cast(num) / static_cast(multiple)) * static_cast(multiple)); } +#pragma endregion - #pragma region Angle Helpers - /// - /// Returns a copy of the angle normalized so it's between 0 and 2PI. - /// - /// The angle to normalize, in radians. - /// The angle, normalized so it's between 0 and 2PI - float NormalizeAngleBetween0And2PI(float angle); +#pragma region Angle Helpers + /// + /// Returns a copy of the angle normalized so it's between 0 and 2PI. + /// + /// The angle to normalize, in radians. + /// The angle, normalized so it's between 0 and 2PI + float NormalizeAngleBetween0And2PI(float angle); - /// - /// Returns a copy of the angle normalized so it's between -PI and PI. - /// - /// The angle to normalize, in radians. - /// The angle, normalized so it's between -PI and PI - float NormalizeAngleBetweenNegativePIAndPI(float angle); + /// + /// Returns a copy of the angle normalized so it's between -PI and PI. + /// + /// The angle to normalize, in radians. + /// The angle, normalized so it's between -PI and PI + float NormalizeAngleBetweenNegativePIAndPI(float angle); - /// - /// Returns whether or not the angle to check is between the start and end angles. Note that, because of how angles work (when normalized), the start angle may be greater than the end angle. - /// - /// The angle to check, in radians. - /// The starting angle for the range. - /// The ending angle for the range. - /// Whether or not the angle to check is between the start and end angle. - bool AngleWithinRange(float angleToCheck, float startAngle, float endAngle); + /// + /// Returns whether or not the angle to check is between the start and end angles. Note that, because of how angles work (when normalized), the start angle may be greater than the end angle. + /// + /// The angle to check, in radians. + /// The starting angle for the range. + /// The ending angle for the range. + /// Whether or not the angle to check is between the start and end angle. + bool AngleWithinRange(float angleToCheck, float startAngle, float endAngle); - /// - /// Clamps the passed in angle between the specified lower and upper limits, in a CCW direction. - /// - /// The angle to clamp. - /// The lower limit for clamping. - /// The upper limit for clamping. - /// The angle, clamped between the start and end angle. - float ClampAngle(float angleToClamp, float startAngle, float endAngle); - #pragma endregion - - #pragma region Detection - /// - /// Tells whether a point is within a specified box. - /// - /// Vector position of the point we're checking. - /// Vector position of the box. - /// Width of the box. - /// Height of the box. - /// True if point is inside box bounds. - bool WithinBox(const Vector &point, const Vector &boxPos, float width, float height); + /// + /// Clamps the passed in angle between the specified lower and upper limits, in a CCW direction. + /// + /// The angle to clamp. + /// The lower limit for clamping. + /// The upper limit for clamping. + /// The angle, clamped between the start and end angle. + float ClampAngle(float angleToClamp, float startAngle, float endAngle); +#pragma endregion + +#pragma region Detection + /// + /// Tells whether a point is within a specified box. + /// + /// Vector position of the point we're checking. + /// Vector position of the box. + /// Width of the box. + /// Height of the box. + /// True if point is inside box bounds. + bool WithinBox(const Vector& point, const Vector& boxPos, float width, float height); - /// - /// Tells whether a point is within a specified box. - /// - /// Vector position of the point we're checking. - /// Position of box left plane (X start). - /// Position of box top plane (Y start). - /// Position of box right plane (X end). - /// Position of box bottom plane (Y end). - /// True if point is inside box bounds. - bool WithinBox(const Vector &point, float left, float top, float right, float bottom); - #pragma endregion - - #pragma region Conversion - /// - /// Returns a corrected angle value that can be used with Allegro fixed point math routines where 256 equals 360 degrees. - /// - /// The angle value to correct. In degrees. - /// A float with the represented angle as full rotations being 256. - inline float GetAllegroAngle(float angleDegrees) { return (angleDegrees / 360) * 256; } + /// + /// Tells whether a point is within a specified box. + /// + /// Vector position of the point we're checking. + /// Position of box left plane (X start). + /// Position of box top plane (Y start). + /// Position of box right plane (X end). + /// Position of box bottom plane (Y end). + /// True if point is inside box bounds. + bool WithinBox(const Vector& point, float left, float top, float right, float bottom); +#pragma endregion + +#pragma region Conversion + /// + /// Returns a corrected angle value that can be used with Allegro fixed point math routines where 256 equals 360 degrees. + /// + /// The angle value to correct. In degrees. + /// A float with the represented angle as full rotations being 256. + inline float GetAllegroAngle(float angleDegrees) { return (angleDegrees / 360) * 256; } - /// - /// Returns the given angle converted from degrees to radians. - /// - /// The angle in degrees to be converted. - /// The converted angle in radians. - inline float DegreesToRadians(float angleDegrees) { return angleDegrees / 180.0F * c_PI; } + /// + /// Returns the given angle converted from degrees to radians. + /// + /// The angle in degrees to be converted. + /// The converted angle in radians. + inline float DegreesToRadians(float angleDegrees) { return angleDegrees / 180.0F * c_PI; } - /// - /// Returns the given angle converted from radians to degrees. - /// - /// The angle in radians to be converted. - /// The converted angle in degrees. - inline float RadiansToDegrees(float angleRadians) { return angleRadians / c_PI * 180.0F; } - #pragma endregion + /// + /// Returns the given angle converted from radians to degrees. + /// + /// The angle in radians to be converted. + /// The converted angle in degrees. + inline float RadiansToDegrees(float angleRadians) { return angleRadians / c_PI * 180.0F; } +#pragma endregion - #pragma region Strings - /// - /// Checks whether two strings are equal when the casing is disregarded. - /// - /// First string. - /// Second string. - /// Whether the two strings are equal case insensitively. - inline bool StringsEqualCaseInsensitive(const std::string_view &strA, const std::string_view &strB) { return std::equal(strA.begin(), strA.end(), strB.begin(), strB.end(), [](char strAChar, char strBChar) { return std::tolower(strAChar) == std::tolower(strBChar); }); } +#pragma region Strings + /// + /// Checks whether two strings are equal when the casing is disregarded. + /// + /// First string. + /// Second string. + /// Whether the two strings are equal case insensitively. + inline bool StringsEqualCaseInsensitive(const std::string_view& strA, const std::string_view& strB) { + return std::equal(strA.begin(), strA.end(), strB.begin(), strB.end(), [](char strAChar, char strBChar) { return std::tolower(strAChar) == std::tolower(strBChar); }); + } - /// - /// If a file "foo/Bar.txt" exists, and this method is passed "FOO/BAR.TXT", then this method will return "foo/Bar.txt". - /// This method's purpose is to enable Linux to get the real path using a case-insensitive search. - /// The real path is used by the Lua file I/O handling methods to ensure full Windows compatibility. - /// - /// Path to case-insensitively translate to a real path. - /// The real path. If the path doesn't exist, it returns the fullPath argument with all the existing parent directories correctly capitalized. - std::string GetCaseInsensitiveFullPath(const std::string &fullPath); + /// + /// If a file "foo/Bar.txt" exists, and this method is passed "FOO/BAR.TXT", then this method will return "foo/Bar.txt". + /// This method's purpose is to enable Linux to get the real path using a case-insensitive search. + /// The real path is used by the Lua file I/O handling methods to ensure full Windows compatibility. + /// + /// Path to case-insensitively translate to a real path. + /// The real path. If the path doesn't exist, it returns the fullPath argument with all the existing parent directories correctly capitalized. + std::string GetCaseInsensitiveFullPath(const std::string& fullPath); - /// - /// Hashes a string in a cross-compiler/platform safe way (std::hash gives different results on different compilers). - /// - /// Text string to hash. - /// The hash result value. - uint64_t Hash(const std::string &text); - #pragma endregion + /// + /// Hashes a string in a cross-compiler/platform safe way (std::hash gives different results on different compilers). + /// + /// Text string to hash. + /// The hash result value. + uint64_t Hash(const std::string& text); +#pragma endregion - #pragma region Misc - /// - /// Convenience method that takes in a double pointer array and returns a std::vector with its contents, because pointers-to-pointers are the devil. The passed in array is deleted in the process so no need to delete it manually. - /// - /// The double pointer to convert to a std::vector. - /// The size of the double pointer array. - template std::vector ConvertDoublePointerToVectorOfPointers(Type **arrayOfType, size_t arraySize) { - std::unique_ptr doublePointerArray = std::unique_ptr(arrayOfType); - std::vector outputVector; - for (size_t i = 0; i < arraySize; ++i) { - outputVector.emplace_back(doublePointerArray[i]); - } - return outputVector; +#pragma region Misc + /// + /// Convenience method that takes in a double pointer array and returns a std::vector with its contents, because pointers-to-pointers are the devil. The passed in array is deleted in the process so no need to delete it manually. + /// + /// The double pointer to convert to a std::vector. + /// The size of the double pointer array. + template std::vector ConvertDoublePointerToVectorOfPointers(Type** arrayOfType, size_t arraySize) { + std::unique_ptr doublePointerArray = std::unique_ptr(arrayOfType); + std::vector outputVector; + for (size_t i = 0; i < arraySize; ++i) { + outputVector.emplace_back(doublePointerArray[i]); } + return outputVector; + } - /// - /// Returns the sign of the given input value. - /// - /// The sign as an integer -1, 0 or +1. - template int Sign(const Type &value) { - return (Type(0) < value) - (Type(0) > value); - } - #pragma endregion -} + /// + /// Returns the sign of the given input value. + /// + /// The sign as an integer -1, 0 or +1. + template int Sign(const Type& value) { + return (Type(0) < value) - (Type(0) > value); + } +#pragma endregion +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Reader.cpp b/Source/System/Reader.cpp index a86a2c47d4..bd204a0034 100644 --- a/Source/System/Reader.cpp +++ b/Source/System/Reader.cpp @@ -5,7 +5,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Reader::Clear() { m_Stream = nullptr; @@ -26,24 +26,24 @@ namespace RTE { m_NonModulePath = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader::Reader(const std::string &fileName, bool overwrites, const ProgressCallback &progressCallback, bool failOK, bool nonModulePath) { + Reader::Reader(const std::string& fileName, bool overwrites, const ProgressCallback& progressCallback, bool failOK, bool nonModulePath) { Clear(); m_NonModulePath = nonModulePath; Create(fileName, overwrites, progressCallback, failOK); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader::Reader(std::unique_ptr &&stream, bool overwrites, const ProgressCallback &progressCallback, bool failOK) { + Reader::Reader(std::unique_ptr&& stream, bool overwrites, const ProgressCallback& progressCallback, bool failOK) { Clear(); Create(std::move(stream), overwrites, progressCallback, failOK); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Reader::Create(const std::string &fileName, bool overwrites, const ProgressCallback &progressCallback, bool failOK) { + int Reader::Create(const std::string& fileName, bool overwrites, const ProgressCallback& progressCallback, bool failOK) { if (fileName.empty()) { return -1; } @@ -65,15 +65,15 @@ namespace RTE { return Create(std::make_unique(m_FilePath), overwrites, progressCallback, failOK); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Reader::Create(std::unique_ptr &&stream, bool overwrites, const ProgressCallback &progressCallback, bool failOK) { + int Reader::Create(std::unique_ptr&& stream, bool overwrites, const ProgressCallback& progressCallback, bool failOK) { m_CanFail = failOK; m_Stream = std::move(stream); - if (!m_CanFail) { - RTEAssert(System::PathExistsCaseSensitive(m_FilePath) && m_Stream->good(), "Failed to open data file \"" + m_FilePath + "\"!"); + if (!m_CanFail) { + RTEAssert(System::PathExistsCaseSensitive(m_FilePath) && m_Stream->good(), "Failed to open data file \"" + m_FilePath + "\"!"); } m_OverwriteExisting = overwrites; @@ -81,19 +81,19 @@ namespace RTE { // Report that we're starting a new file m_ReportProgress = progressCallback; if (m_ReportProgress && m_Stream->good()) { - m_ReportProgress("\t" + m_FileName + " on line " + std::to_string(m_CurrentLine), true); + m_ReportProgress("\t" + m_FileName + " on line " + std::to_string(m_CurrentLine), true); } return m_Stream->good() ? 0 : -1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Reader::GetReadModuleID() const { return (m_DataModuleID < 0) ? g_PresetMan.GetModuleID(m_DataModuleName) : m_DataModuleID; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string Reader::WholeFileAsString() const { std::stringstream stringStream; @@ -101,7 +101,7 @@ namespace RTE { return stringStream.str(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string Reader::ReadLine() { DiscardEmptySpace(); @@ -119,8 +119,12 @@ namespace RTE { break; } - if (m_Stream->eof()) { break; } - if (!m_Stream->good()) { ReportError("Stream failed for some reason"); } + if (m_Stream->eof()) { + break; + } + if (!m_Stream->good()) { + ReportError("Stream failed for some reason"); + } retString.append(1, temp); peek = static_cast(m_Stream->peek()); @@ -128,7 +132,7 @@ namespace RTE { return TrimString(retString); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string Reader::ReadPropName() { DiscardEmptySpace(); @@ -152,10 +156,10 @@ namespace RTE { EndIncludeFile(); break; } - if (!m_Stream->good() || temp == -1) { + if (!m_Stream->good() || temp == -1) { ReportError("Stream failed for some reason"); - EndIncludeFile(); - break; + EndIncludeFile(); + break; } retString.append(1, temp); } @@ -179,7 +183,7 @@ namespace RTE { return retString; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string Reader::ReadPropValue() { std::string fullLine = ReadLine(); @@ -188,7 +192,7 @@ namespace RTE { return TrimString(propValue); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Reader::NextProperty() { if (!DiscardEmptySpace() || m_EndOfStreams) { @@ -203,9 +207,9 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Reader::TrimString(const std::string &stringToTrim) const { + std::string Reader::TrimString(const std::string& stringToTrim) const { if (stringToTrim.empty()) { return ""; } @@ -215,7 +219,7 @@ namespace RTE { return stringToTrim.substr(start, (end - start + 1)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Reader::DiscardEmptySpace() { char peek; @@ -231,17 +235,19 @@ namespace RTE { return EndIncludeFile(); } // Not end-of-file but still got junk back... something went to shit - if (peek == -1) { ReportError("Something went wrong reading the line; make sure it is providing the expected type"); } + if (peek == -1) { + ReportError("Something went wrong reading the line; make sure it is providing the expected type"); + } // Discard spaces if (peek == ' ') { leadingSpaceCount++; m_Stream->ignore(1); - // Discard tabs, and count them + // Discard tabs, and count them } else if (peek == '\t') { indent++; m_Stream->ignore(1); - // Discard newlines and reset the tab count for the new line, also count the lines + // Discard newlines and reset the tab count for the new line, also count the lines } else if (peek == '\n' || peek == '\r') { // So we don't count lines twice when there are both newline and carriage return at the end of lines if (peek == '\n') { @@ -256,14 +262,16 @@ namespace RTE { discardedLine = true; m_Stream->ignore(1); - // Comment line? + // Comment line? } else if (m_Stream->peek() == '/') { char temp = static_cast(m_Stream->get()); // Confirm that it's a comment line, if so discard it and continue if (m_Stream->peek() == '/') { - while (m_Stream->peek() != '\n' && m_Stream->peek() != '\r' && !m_Stream->eof()) { m_Stream->ignore(1); } - // Block comment + while (m_Stream->peek() != '\n' && m_Stream->peek() != '\r' && !m_Stream->eof()) { + m_Stream->ignore(1); + } + // Block comment } else if (m_Stream->peek() == '*') { int openBlockComments = 1; m_BlockCommentOpenTagLines.emplace(m_CurrentLine); @@ -271,7 +279,9 @@ namespace RTE { char temp2 = 0; while (openBlockComments > 0 && !m_Stream->eof()) { temp2 = static_cast(m_Stream->get()); - if (temp2 == '\n') { ++m_CurrentLine; } + if (temp2 == '\n') { + ++m_CurrentLine; + } // Find the matching close tag. if (!(temp2 == '*' && m_Stream->peek() == '/')) { @@ -292,7 +302,7 @@ namespace RTE { ReportError("File stream ended with an open block comment!\nCouldn't find closing tag for block comment opened on line " + std::to_string(m_BlockCommentOpenTagLines.top()) + ".\n"); } - // Not a comment, so it's data, so quit. + // Not a comment, so it's data, so quit. } else { m_Stream->putback(temp); break; @@ -304,31 +314,39 @@ namespace RTE { // This precaution enables us to use DiscardEmptySpace repeatedly without messing up the indentation tracking logic if (discardedLine) { - if (leadingSpaceCount > 0) { ReportError("Encountered space characters used for indentation where a tab character was expected!\nPlease make sure the preset definition structure is correct.\n"); } + if (leadingSpaceCount > 0) { + ReportError("Encountered space characters used for indentation where a tab character was expected!\nPlease make sure the preset definition structure is correct.\n"); + } // Get indentation difference from the last line of the last call to DiscardEmptySpace(), and the last line of this call to DiscardEmptySpace(). m_IndentDifference = indent - m_PreviousIndent; - if (m_IndentDifference > 1) { ReportError("Over indentation detected!\nPlease make sure the preset definition structure is correct.\n"); } + if (m_IndentDifference > 1) { + ReportError("Over indentation detected!\nPlease make sure the preset definition structure is correct.\n"); + } // Save the last tab count m_PreviousIndent = indent; } return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Reader::ReportError(const std::string &errorDesc) const { + void Reader::ReportError(const std::string& errorDesc) const { if (!m_CanFail) { RTEAbort(errorDesc + "\nError happened in " + m_FilePath + " at line " + std::to_string(m_CurrentLine) + "!"); } else { - if (m_ReportProgress) { m_ReportProgress(errorDesc + ", skipping!", true); } + if (m_ReportProgress) { + m_ReportProgress(errorDesc + ", skipping!", true); + } } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Reader::StartIncludeFile() { // Report that we're including a file - if (m_ReportProgress) { m_ReportProgress(m_ReportTabs + m_FileName + " on line " + std::to_string(m_CurrentLine) + " includes:", false); } + if (m_ReportProgress) { + m_ReportProgress(m_ReportTabs + m_FileName + " on line " + std::to_string(m_CurrentLine) + " includes:", false); + } // Get the file path from the current stream before pushing it into the StreamStack, otherwise we can't open a new stream after releasing it because we can't read. std::string includeFilePath = g_PresetMan.GetFullModulePath(ReadPropValue()); @@ -372,10 +390,12 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Reader::EndIncludeFile() { - if (m_ReportProgress) { m_ReportProgress(m_ReportTabs + m_FileName + " - done! " + static_cast(-42), false); } + if (m_ReportProgress) { + m_ReportProgress(m_ReportTabs + m_FileName + " - done! " + static_cast(-42), false); + } if (m_StreamStack.empty()) { m_EndOfStreams = true; @@ -405,4 +425,4 @@ namespace RTE { DiscardEmptySpace(); return true; } -} +} // namespace RTE diff --git a/Source/System/Reader.h b/Source/System/Reader.h index 8848d3f39a..4e9d315426 100644 --- a/Source/System/Reader.h +++ b/Source/System/Reader.h @@ -11,7 +11,6 @@ namespace RTE { class Reader { public: - #pragma region Creation /// /// Constructor method used to instantiate a Reader object in system memory. Create() should be called before using the object. @@ -26,7 +25,7 @@ namespace RTE { /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. /// Whether this Reader is reading from path that is not a DataModule and should just read it as provided. - Reader(const std::string &fileName, bool overwrites = false, const ProgressCallback &progressCallback = nullptr, bool failOK = false, bool nonModulePath = false); + Reader(const std::string& fileName, bool overwrites = false, const ProgressCallback& progressCallback = nullptr, bool failOK = false, bool nonModulePath = false); /// /// Constructor method used to instantiate a Reader object in system memory and make it ready for reading from the passed in file path. @@ -35,7 +34,7 @@ namespace RTE { /// Whether object definitions read here overwrite existing ones with the same names. /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. - Reader(std::unique_ptr &&stream, bool overwrites = false, const ProgressCallback &progressCallback = nullptr, bool failOK = false); + Reader(std::unique_ptr&& stream, bool overwrites = false, const ProgressCallback& progressCallback = nullptr, bool failOK = false); /// /// Makes the Reader object ready for use. @@ -45,7 +44,7 @@ namespace RTE { /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const std::string &fileName, bool overwrites = false, const ProgressCallback &progressCallback = nullptr, bool failOK = false); + int Create(const std::string& fileName, bool overwrites = false, const ProgressCallback& progressCallback = nullptr, bool failOK = false); /// /// Makes the Reader object ready for use. @@ -55,7 +54,7 @@ namespace RTE { /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(std::unique_ptr &&stream, bool overwrites = false, const ProgressCallback &progressCallback = nullptr, bool failOK = false); + int Create(std::unique_ptr&& stream, bool overwrites = false, const ProgressCallback& progressCallback = nullptr, bool failOK = false); #pragma endregion #pragma region Getters and Setters @@ -63,7 +62,7 @@ namespace RTE { /// Gets the name of the DataModule this Reader is reading from. /// /// The name of the DataModule this reader is reading from. - const std::string & GetReadModuleName() const { return m_DataModuleName; } + const std::string& GetReadModuleName() const { return m_DataModuleName; } /// /// Gets the ID of the DataModule this Reader is reading from. If the ID is invalid, attempts to get a valid ID using the DataModule name. @@ -75,7 +74,7 @@ namespace RTE { /// Gets a pointer to the istream of this reader. /// /// A pointer to the istream object for this reader. - std::istream * GetStream() const { return m_Stream.get(); } + std::istream* GetStream() const { return m_Stream.get(); } /// /// Gets the path of the current file this reader is reading from. @@ -157,7 +156,7 @@ namespace RTE { /// /// String to remove whitespace from. /// The string that was passed in, sans whitespace in the front and end. - std::string TrimString(const std::string &stringToTrim) const; + std::string TrimString(const std::string& stringToTrim) const; /// /// Discards all whitespace, newlines and comment lines (which start with '//') so that the next thing to be read will be actual data. @@ -177,7 +176,7 @@ namespace RTE { /// Makes an error message box pop up for the user that tells them something went wrong with the reading, and where. /// /// The message describing what's wrong. - void ReportError(const std::string &errorDesc) const; + void ReportError(const std::string& errorDesc) const; #pragma endregion #pragma region Operator Overloads @@ -186,24 +185,74 @@ namespace RTE { /// /// A reference to the variable that will be filled by the extracted data. /// A Reader reference for further use in an expression. - Reader & operator>>(bool &var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - Reader & operator>>(char &var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - Reader & operator>>(unsigned char &var) { DiscardEmptySpace(); int temp; *m_Stream >> temp; var = temp; return *this; } - Reader & operator>>(short &var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - Reader & operator>>(unsigned short &var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - Reader & operator>>(int &var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - Reader & operator>>(unsigned int &var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - Reader & operator>>(long &var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - Reader & operator>>(unsigned long &var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } + Reader& operator>>(bool& var) { + DiscardEmptySpace(); + *m_Stream >> var; + return *this; + } + Reader& operator>>(char& var) { + DiscardEmptySpace(); + *m_Stream >> var; + return *this; + } + Reader& operator>>(unsigned char& var) { + DiscardEmptySpace(); + int temp; + *m_Stream >> temp; + var = temp; + return *this; + } + Reader& operator>>(short& var) { + DiscardEmptySpace(); + *m_Stream >> var; + return *this; + } + Reader& operator>>(unsigned short& var) { + DiscardEmptySpace(); + *m_Stream >> var; + return *this; + } + Reader& operator>>(int& var) { + DiscardEmptySpace(); + *m_Stream >> var; + return *this; + } + Reader& operator>>(unsigned int& var) { + DiscardEmptySpace(); + *m_Stream >> var; + return *this; + } + Reader& operator>>(long& var) { + DiscardEmptySpace(); + *m_Stream >> var; + return *this; + } + Reader& operator>>(unsigned long& var) { + DiscardEmptySpace(); + *m_Stream >> var; + return *this; + } // Yeah, this is dumb - read as double and cast. // This is because, for whatever fucking reason, iostream can save out floats at a precision that it's then unable to read... - Reader & operator>>(float &var) { DiscardEmptySpace(); double var2; *m_Stream >> var2; var = static_cast(var2); return *this; } - Reader & operator>>(double &var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - Reader & operator>>(std::string &var) { var.assign(ReadLine()); return *this; } + Reader& operator>>(float& var) { + DiscardEmptySpace(); + double var2; + *m_Stream >> var2; + var = static_cast(var2); + return *this; + } + Reader& operator>>(double& var) { + DiscardEmptySpace(); + *m_Stream >> var; + return *this; + } + Reader& operator>>(std::string& var) { + var.assign(ReadLine()); + return *this; + } #pragma endregion protected: - /// /// A struct containing information from the currently used stream. /// @@ -211,10 +260,11 @@ namespace RTE { /// /// Constructor method used to instantiate a StreamInfo object in system memory. /// - StreamInfo(std::istream *stream, const std::string &filePath, int currentLine, int prevIndent) : Stream(stream), FilePath(filePath), CurrentLine(currentLine), PreviousIndent(prevIndent) {} + StreamInfo(std::istream* stream, const std::string& filePath, int currentLine, int prevIndent) : + Stream(stream), FilePath(filePath), CurrentLine(currentLine), PreviousIndent(prevIndent) {} // NOTE: These members are owned by the reader that owns this struct, so are not deleted when this is destroyed. - std::istream *Stream; //!< Currently used stream, is not on the StreamStack until a new stream is opened. + std::istream* Stream; //!< Currently used stream, is not on the StreamStack until a new stream is opened. std::string FilePath; //!< Currently used stream's filepath. int CurrentLine; //!< The line number the stream is on. int PreviousIndent; //!< Count of tabs encountered on the last line DiscardEmptySpace() discarded. @@ -251,7 +301,6 @@ namespace RTE { int m_ObjectEndings; private: - #pragma region Reading Operations /// /// When ReadPropName encounters the property name "IncludeFile", it will automatically call this function to get started reading on that file. @@ -274,8 +323,8 @@ namespace RTE { void Clear(); // Disallow the use of some implicit methods. - Reader(const Reader &reference) = delete; - Reader & operator=(const Reader &rhs) = delete; + Reader(const Reader& reference) = delete; + Reader& operator=(const Reader& rhs) = delete; }; -} +} // namespace RTE #endif diff --git a/Source/System/Serializable.cpp b/Source/System/Serializable.cpp index b79204de37..f9731c8e5b 100644 --- a/Source/System/Serializable.cpp +++ b/Source/System/Serializable.cpp @@ -2,15 +2,17 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Serializable::CreateSerializable(Reader &reader, bool checkType, bool doCreate, bool skipStartingObject) { + int Serializable::CreateSerializable(Reader& reader, bool checkType, bool doCreate, bool skipStartingObject) { if (checkType && reader.ReadPropValue() != GetClassName()) { reader.ReportError("Wrong type in Reader when passed to Serializable::Create()"); return -1; } - if (!skipStartingObject) { reader.StartObject(); } + if (!skipStartingObject) { + reader.StartObject(); + } while (reader.NextProperty()) { SetFormattedReaderPosition("in file " + reader.GetCurrentFilePath() + " on line " + reader.GetCurrentFileLine()); std::string propName = reader.ReadPropName(); @@ -24,39 +26,41 @@ namespace RTE { return doCreate ? Create() : 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Serializable::ReadProperty(const std::string_view &propName, Reader &reader) { + int Serializable::ReadProperty(const std::string_view& propName, Reader& reader) { reader.ReadPropValue(); reader.ReportError("Could not match property '" + std::string(propName) + "'!"); return -1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader & operator>>(Reader &reader, Serializable &operand) { + Reader& operator>>(Reader& reader, Serializable& operand) { operand.Create(reader); return reader; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader & operator>>(Reader &reader, Serializable *operand) { - if (operand) { operand->Create(reader); } + Reader& operator>>(Reader& reader, Serializable* operand) { + if (operand) { + operand->Create(reader); + } return reader; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Writer & operator<<(Writer &writer, const Serializable &operand) { + Writer& operator<<(Writer& writer, const Serializable& operand) { operand.Save(writer); writer.ObjectEnd(); return writer; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Writer & operator<<(Writer &writer, const Serializable *operand) { + Writer& operator<<(Writer& writer, const Serializable* operand) { if (operand) { operand->Save(writer); writer.ObjectEnd(); @@ -65,4 +69,4 @@ namespace RTE { } return writer; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/Serializable.h b/Source/System/Serializable.h index 7db7835dbd..e296ee5d6a 100644 --- a/Source/System/Serializable.h +++ b/Source/System/Serializable.h @@ -17,52 +17,57 @@ namespace RTE { class Serializable { public: - #pragma region Global Macro Definitions - /// - /// Convenience macro to cut down on duplicate ReadProperty and Save methods in classes that extend Serializable. - /// - #define SerializableOverrideMethods \ - int ReadProperty(const std::string_view &propName, Reader &reader) override; \ - int Save(Writer &writer) const override; - - /// - /// Convenience macro to cut down on duplicate GetClassName methods in non-poolable classes that extend Serializable. - /// - #define SerializableClassNameGetter \ - const std::string & GetClassName() const override { return c_ClassName; } - - #define StartPropertyList(failureCase) \ - static std::unordered_map sm_Properties; \ - static bool sm_Initialized = false; \ - int jmpAddress = sm_Initialized ? -1 : -2; \ - if (sm_Initialized) { \ - auto itr = sm_Properties.find(std::string(propName)); \ - if (itr == sm_Properties.end()) { \ - failureCase; \ - } else { \ - jmpAddress = itr->second; \ - } \ - } \ - switch (jmpAddress) { \ - case -1: break; \ - case -2: - - #define MatchForwards(propertyName) \ - sm_Properties[propertyName] = __COUNTER__ + 1; \ - case __COUNTER__: - - #define MatchProperty(propertyName,matchedFunction) \ - sm_Properties[propertyName] = __COUNTER__ + 1; \ - case __COUNTER__: if (sm_Initialized) { { matchedFunction }; break; }; - - #define EndPropertyList } \ - if (!sm_Initialized) { \ - sm_Initialized = true; \ - /* This was the initialization pass, now properly read the value by calling ourselves again */ \ - return ReadProperty(propName, reader); \ - } \ - return 0; +/// +/// Convenience macro to cut down on duplicate ReadProperty and Save methods in classes that extend Serializable. +/// +#define SerializableOverrideMethods \ + int ReadProperty(const std::string_view& propName, Reader& reader) override; \ + int Save(Writer& writer) const override; + +/// +/// Convenience macro to cut down on duplicate GetClassName methods in non-poolable classes that extend Serializable. +/// +#define SerializableClassNameGetter \ + const std::string& GetClassName() const override { return c_ClassName; } + +#define StartPropertyList(failureCase) \ + static std::unordered_map sm_Properties; \ + static bool sm_Initialized = false; \ + int jmpAddress = sm_Initialized ? -1 : -2; \ + if (sm_Initialized) { \ + auto itr = sm_Properties.find(std::string(propName)); \ + if (itr == sm_Properties.end()) { \ + failureCase; \ + } else { \ + jmpAddress = itr->second; \ + } \ + } \ + switch (jmpAddress) { \ + case -1: \ + break; \ + case -2: + +#define MatchForwards(propertyName) \ + sm_Properties[propertyName] = __COUNTER__ + 1; \ + case __COUNTER__: + +#define MatchProperty(propertyName, matchedFunction) \ + sm_Properties[propertyName] = __COUNTER__ + 1; \ + case __COUNTER__: \ + if (sm_Initialized) { \ + {matchedFunction}; \ + break; \ + }; + +#define EndPropertyList \ + } \ + if (!sm_Initialized) { \ + sm_Initialized = true; \ + /* This was the initialization pass, now properly read the value by calling ourselves again */ \ + return ReadProperty(propName, reader); \ + } \ + return 0; #pragma endregion @@ -86,7 +91,7 @@ namespace RTE { /// Whether to do any additional initialization of the object after reading in all the properties from the Reader. This is done by calling Create(). /// Whether there is a class name in the stream to check against to make sure the correct type is being read from the stream. /// Whether to do any additional initialization of the object after reading in all the properties from the Reader. This is done by calling Create(). /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - virtual int Create(Reader &reader, bool checkType = true, bool doCreate = true) { return CreateSerializable(reader, checkType, doCreate, false); } + virtual int Create(Reader& reader, bool checkType = true, bool doCreate = true) { return CreateSerializable(reader, checkType, doCreate, false); } #pragma endregion #pragma region Destruction @@ -122,21 +127,24 @@ namespace RTE { /// An error return value signaling whether the property was successfully read or not. /// 0 means it was read successfully, and any nonzero indicates that a property of that name could not be found in this or base classes. /// - virtual int ReadProperty(const std::string_view &propName, Reader &reader); + virtual int ReadProperty(const std::string_view& propName, Reader& reader); /// /// Saves the complete state of this Serializable to an output stream for later recreation with Create(istream &stream). /// /// A Writer that the Serializable will save itself to. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - virtual int Save(Writer &writer) const { writer.ObjectStart(GetClassName()); return 0; } + virtual int Save(Writer& writer) const { + writer.ObjectStart(GetClassName()); + return 0; + } /// /// Replaces backslashes with forward slashes in file paths to eliminate issues with cross-platform compatibility or invalid escape sequences. /// /// Reference to the file path string to correct slashes in. // TODO: Add a warning log entry if backslashes are found in a data path. Perhaps overwrite them in the ini file itself. - std::string CorrectBackslashesInPath(const std::string &pathToCorrect) const { return std::filesystem::path(pathToCorrect).generic_string(); } + std::string CorrectBackslashesInPath(const std::string& pathToCorrect) const { return std::filesystem::path(pathToCorrect).generic_string(); } #pragma endregion #pragma region Logging @@ -146,7 +154,7 @@ namespace RTE { /// This just acts as an abstract base for child classes to implement. /// /// A string containing the currently read file path and the line being read. - virtual void SetFormattedReaderPosition(const std::string &newPosition) {} + virtual void SetFormattedReaderPosition(const std::string& newPosition) {} #pragma endregion #pragma region Operator Overloads @@ -156,7 +164,7 @@ namespace RTE { /// A Reader reference as the left hand side operand. /// An Serializable reference as the right hand side operand. /// A Reader reference for further use in an expression. - friend Reader & operator>>(Reader &reader, Serializable &operand); + friend Reader& operator>>(Reader& reader, Serializable& operand); /// /// A Reader extraction operator for filling an Serializable from a Reader. @@ -164,7 +172,7 @@ namespace RTE { /// A Reader reference as the left hand side operand. /// An Serializable pointer as the right hand side operand. /// A Reader reference for further use in an expression. - friend Reader & operator>>(Reader &reader, Serializable *operand); + friend Reader& operator>>(Reader& reader, Serializable* operand); /// /// A Writer insertion operator for sending a Serializable to a Writer. @@ -172,7 +180,7 @@ namespace RTE { /// A Writer reference as the left hand side operand. /// A Serializable reference as the right hand side operand. /// A Writer reference for further use in an expression. - friend Writer & operator<<(Writer &writer, const Serializable &operand); + friend Writer& operator<<(Writer& writer, const Serializable& operand); /// /// A Writer insertion operator for sending a Serializable to a Writer. @@ -180,7 +188,7 @@ namespace RTE { /// A Writer reference as the left hand side operand. /// A Serializable pointer as the right hand side operand. /// A Writer reference for further use in an expression. - friend Writer &operator<<(Writer &writer, const Serializable *operand); + friend Writer& operator<<(Writer& writer, const Serializable* operand); #pragma endregion #pragma region Class Info @@ -188,15 +196,14 @@ namespace RTE { /// Gets the class name of this Serializable. /// /// A string with the friendly-formatted type name of this Serializable. - virtual const std::string & GetClassName() const = 0; + virtual const std::string& GetClassName() const = 0; #pragma endregion private: - /// /// Clears all the member variables of this Object, effectively resetting the members of this abstraction level only. /// void Clear() {} }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Shader.cpp b/Source/System/Shader.cpp index 3650f03fa2..9a92e92b34 100644 --- a/Source/System/Shader.cpp +++ b/Source/System/Shader.cpp @@ -12,21 +12,21 @@ namespace RTE { Shader::Shader() : m_ProgramID(0), m_TextureUniform(-1), m_ColorUniform(-1), m_TransformUniform(-1), m_ProjectionUniform(-1) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Shader::Shader(const std::string &vertexFilename, const std::string &fragPath) : + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Shader::Shader(const std::string& vertexFilename, const std::string& fragPath) : m_ProgramID(glCreateProgram()), m_TextureUniform(-1), m_ColorUniform(-1), m_TransformUniform(-1), m_ProjectionUniform(-1) { Compile(vertexFilename, fragPath); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Shader::~Shader() { if (m_ProgramID) { GL_CHECK(glDeleteProgram(m_ProgramID)); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Shader::Compile(const std::string &vertexPath, const std::string &fragPath) { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Shader::Compile(const std::string& vertexPath, const std::string& fragPath) { assert(m_ProgramID != 0); GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); @@ -59,55 +59,55 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::Use() { GL_CHECK(glUseProgram(m_ProgramID)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GLint Shader::GetUniformLocation(const std::string &name) { return glGetUniformLocation(m_ProgramID, name.c_str()); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + GLint Shader::GetUniformLocation(const std::string& name) { return glGetUniformLocation(m_ProgramID, name.c_str()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetBool(const std::string &name, bool value) { GL_CHECK(glUniform1i(glGetUniformLocation(m_ProgramID, name.c_str()), static_cast(value))); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetBool(const std::string& name, bool value) { GL_CHECK(glUniform1i(glGetUniformLocation(m_ProgramID, name.c_str()), static_cast(value))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetInt(const std::string &name, int value) { GL_CHECK(glUniform1i(glGetUniformLocation(m_ProgramID, name.c_str()), value)); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetInt(const std::string& name, int value) { GL_CHECK(glUniform1i(glGetUniformLocation(m_ProgramID, name.c_str()), value)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetFloat(const std::string &name, float value) { GL_CHECK(glUniform1f(glGetUniformLocation(m_ProgramID, name.c_str()), value)); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetFloat(const std::string& name, float value) { GL_CHECK(glUniform1f(glGetUniformLocation(m_ProgramID, name.c_str()), value)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetMatrix4f(const std::string &name, const glm::mat4 &value) { GL_CHECK(glUniformMatrix4fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, GL_FALSE, glm::value_ptr(value))); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetMatrix4f(const std::string& name, const glm::mat4& value) { GL_CHECK(glUniformMatrix4fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, GL_FALSE, glm::value_ptr(value))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetVector2f(const std::string &name, const glm::vec2 &value) { GL_CHECK(glUniform2fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, glm::value_ptr(value))); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetVector2f(const std::string& name, const glm::vec2& value) { GL_CHECK(glUniform2fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, glm::value_ptr(value))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetVector3f(const std::string &name, const glm::vec3 &value) { GL_CHECK(glUniform3fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, glm::value_ptr(value))); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetVector3f(const std::string& name, const glm::vec3& value) { GL_CHECK(glUniform3fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, glm::value_ptr(value))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetVector4f(const std::string &name, const glm::vec4 &value) { GL_CHECK(glUniform4fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, glm::value_ptr(value))); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetVector4f(const std::string& name, const glm::vec4& value) { GL_CHECK(glUniform4fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, glm::value_ptr(value))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetBool(int32_t uniformLoc, bool value) { GL_CHECK(glUniform1i(uniformLoc, value)); } void Shader::SetInt(int32_t uniformLoc, int value) { GL_CHECK(glUniform1i(uniformLoc, value)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetFloat(int32_t uniformLoc, float value) { GL_CHECK(glUniform1f(uniformLoc, value)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetMatrix4f(int32_t uniformLoc, const glm::mat4 &value) { GL_CHECK(glUniformMatrix4fv(uniformLoc, 1, GL_FALSE, glm::value_ptr(value))); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetMatrix4f(int32_t uniformLoc, const glm::mat4& value) { GL_CHECK(glUniformMatrix4fv(uniformLoc, 1, GL_FALSE, glm::value_ptr(value))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetVector2f(int32_t uniformLoc, const glm::vec2 &value) { GL_CHECK(glUniform2fv(uniformLoc, 1, glm::value_ptr(value))); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetVector2f(int32_t uniformLoc, const glm::vec2& value) { GL_CHECK(glUniform2fv(uniformLoc, 1, glm::value_ptr(value))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetVector3f(int32_t uniformLoc, const glm::vec3 &value) { GL_CHECK(glUniform3fv(uniformLoc, 1, glm::value_ptr(value))); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetVector3f(int32_t uniformLoc, const glm::vec3& value) { GL_CHECK(glUniform3fv(uniformLoc, 1, glm::value_ptr(value))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::SetVector4f(int32_t uniformLoc, const glm::vec4 &value) { GL_CHECK(glUniform4fv(uniformLoc, 1, glm::value_ptr(value))); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::SetVector4f(int32_t uniformLoc, const glm::vec4& value) { GL_CHECK(glUniform4fv(uniformLoc, 1, glm::value_ptr(value))); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Shader::CompileShader(GLuint shaderID, const std::string &filename, std::string &error) { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Shader::CompileShader(GLuint shaderID, const std::string& filename, std::string& error) { if (!System::PathExistsCaseSensitive(filename)) { error += "File " + filename + " doesn't exist."; return false; @@ -120,7 +120,7 @@ namespace RTE { std::string dataString = dataStream.str(); - const char *data = dataString.c_str(); + const char* data = dataString.c_str(); GL_CHECK(glShaderSource(shaderID, 1, &data, nullptr)); GL_CHECK(glCompileShader(shaderID)); @@ -140,7 +140,7 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Shader::Link(GLuint vtxShader, GLuint fragShader) { GL_CHECK(glAttachShader(m_ProgramID, vtxShader)); GL_CHECK(glAttachShader(m_ProgramID, fragShader)); @@ -155,8 +155,8 @@ namespace RTE { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Shader::ApplyDefaultUniforms(){ + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Shader::ApplyDefaultUniforms() { Use(); SetInt("rteTexture", 0); SetInt("rtePalette", 1); diff --git a/Source/System/Shader.h b/Source/System/Shader.h index 5c351f047c..e731b405cf 100644 --- a/Source/System/Shader.h +++ b/Source/System/Shader.h @@ -7,23 +7,22 @@ namespace RTE { class Shader { public: - - /// + /// /// Constructs an empty shader program, which can be initialized using `Shader::Compile` /// Shader(); - - /// + + /// /// Constructs a Shader from vertex shader file and fragment shader file. /// /// /// Filepath to the vertex shader file. /// /// - /// Filepath to the fragment shader file. + /// Filepath to the fragment shader file. /// - Shader(const std::string &vertexFilename, const std::string &fragPath); - + Shader(const std::string& vertexFilename, const std::string& fragPath); + /// /// Destructor. /// @@ -38,7 +37,7 @@ namespace RTE { /// /// Filepath to the fragment shader /// - bool Compile(const std::string &vertexFilename, const std::string &fragPath); + bool Compile(const std::string& vertexFilename, const std::string& fragPath); void Use(); #pragma region Uniform handling @@ -51,7 +50,7 @@ namespace RTE { /// /// A GLint containing the location of the requested uniform. /// - int32_t GetUniformLocation(const std::string &name); + int32_t GetUniformLocation(const std::string& name); /// /// Set a boolean uniform value in the active program by name. @@ -62,7 +61,7 @@ namespace RTE { /// /// The boolean value to set the uniform to. /// - void SetBool(const std::string &name, bool value); + void SetBool(const std::string& name, bool value); /// /// Set an integer uniform value in the active program by name. @@ -73,7 +72,7 @@ namespace RTE { /// /// The integer value to set the uniform to. /// - void SetInt(const std::string &name, int value); + void SetInt(const std::string& name, int value); /// /// Set a float uniform value in the active program by name. @@ -84,7 +83,7 @@ namespace RTE { /// /// The float value to set the uniform to. /// - void SetFloat(const std::string &name, float value); + void SetFloat(const std::string& name, float value); /// /// Set a float mat4 uniform value in the active program by name. @@ -95,7 +94,7 @@ namespace RTE { /// /// The float mat4 value to set the uniform to. /// - void SetMatrix4f(const std::string &name, const glm::mat4 &value); + void SetMatrix4f(const std::string& name, const glm::mat4& value); /// /// Set a float vec2 uniform value in the active program by name. @@ -106,7 +105,7 @@ namespace RTE { /// /// The float vec2 value to set the uniform to. /// - void SetVector2f(const std::string &name, const glm::vec2 &value); + void SetVector2f(const std::string& name, const glm::vec2& value); /// /// Set a float vec3 uniform value in the active program by name. @@ -117,7 +116,7 @@ namespace RTE { /// /// The float vec3 value to set the uniform to. /// - void SetVector3f(const std::string &name, const glm::vec3 &value); + void SetVector3f(const std::string& name, const glm::vec3& value); /// /// Set a float vec4 uniform value in the active program by name. @@ -128,7 +127,7 @@ namespace RTE { /// /// The float vec4 value to set the uniform to. /// - void SetVector4f(const std::string &name, const glm::vec4 &value); + void SetVector4f(const std::string& name, const glm::vec4& value); /// /// Set a boolean uniform value in the active program by location. @@ -172,7 +171,7 @@ namespace RTE { /// /// The float mat4 value to set the uniform to. /// - static void SetMatrix4f(int32_t uniformLoc, const glm::mat4 &value); + static void SetMatrix4f(int32_t uniformLoc, const glm::mat4& value); /// /// Set a float vec2 uniform value in the active program by location. @@ -183,7 +182,7 @@ namespace RTE { /// /// The float vec2 value to set the uniform to. /// - static void SetVector2f(int32_t uniformLoc, const glm::vec2 &value); + static void SetVector2f(int32_t uniformLoc, const glm::vec2& value); /// /// Set a float vec3 uniform value in the active program by location. @@ -194,7 +193,7 @@ namespace RTE { /// /// The float vec3 value to set the uniform to. /// - static void SetVector3f(int32_t uniformLoc, const glm::vec3 &value); + static void SetVector3f(int32_t uniformLoc, const glm::vec3& value); /// /// Set a float vec4 uniform value in the active program by location. @@ -205,7 +204,7 @@ namespace RTE { /// /// The float vec4 value to set the uniform to. /// - static void SetVector4f(int32_t uniformLoc, const glm::vec4 &value); + static void SetVector4f(int32_t uniformLoc, const glm::vec4& value); #pragma endregion #pragma region Engine Defined Uniforms @@ -269,9 +268,9 @@ namespace RTE { /// /// Whether compilation was successful. /// - bool CompileShader(uint32_t shaderID, const std::string &data, std::string &error); + bool CompileShader(uint32_t shaderID, const std::string& data, std::string& error); - /// + /// /// Links a shader program from a vertex and fragment shader. /// /// diff --git a/Source/System/Singleton.h b/Source/System/Singleton.h index 26d5e567eb..8d489b012c 100644 --- a/Source/System/Singleton.h +++ b/Source/System/Singleton.h @@ -17,7 +17,6 @@ namespace RTE { class Singleton { public: - /// /// Destructor method used to clean up a Singleton object before deletion. /// @@ -27,7 +26,7 @@ namespace RTE { /// Returns the sole instance of this Singleton. /// /// A reference to the sole instance of this Singleton. - inline static Type & Instance() { return *s_Instance; } + inline static Type& Instance() { return *s_Instance; } /// /// Constructs this Singleton. @@ -35,21 +34,19 @@ namespace RTE { inline static void Construct() { s_Instance = new Type(); } protected: - /// /// Constructor method used to instantiate a Singleton object. /// Singleton() { RTEAssert(!s_Instance, "Trying to create a second instance of a Singleton!"); } private: - - static Type *s_Instance; //!< Pointer to instance of this singleton. + static Type* s_Instance; //!< Pointer to instance of this singleton. // Disallow the use of some implicit methods. - Singleton(const Singleton &reference) = delete; - Singleton & operator=(const Singleton &rhs) = delete; + Singleton(const Singleton& reference) = delete; + Singleton& operator=(const Singleton& rhs) = delete; }; - template Type * Singleton::s_Instance = nullptr; -} + template Type* Singleton::s_Instance = nullptr; +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/SpatialPartitionGrid.cpp b/Source/System/SpatialPartitionGrid.cpp index 51abbfb401..5f1a000671 100644 --- a/Source/System/SpatialPartitionGrid.cpp +++ b/Source/System/SpatialPartitionGrid.cpp @@ -6,7 +6,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SpatialPartitionGrid::Clear() { m_Width = 0; @@ -19,7 +19,7 @@ namespace RTE { m_UsedCellIds.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int SpatialPartitionGrid::Create(int width, int height, int cellSize) { m_Width = width / cellSize; @@ -32,9 +32,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SpatialPartitionGrid::Create(const SpatialPartitionGrid &reference) { + int SpatialPartitionGrid::Create(const SpatialPartitionGrid& reference) { m_Width = reference.m_Width; m_Height = reference.m_Height; m_CellSize = reference.m_CellSize; @@ -44,15 +44,15 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SpatialPartitionGrid::Reset() { - const Activity *activity = g_ActivityMan.GetActivity(); + const Activity* activity = g_ActivityMan.GetActivity(); RTEAssert(activity, "Tried to reset spatial partition grid with no running Activity!"); for (int team = Activity::NoTeam; team < Activity::MaxTeamCount; ++team) { if (team == Activity::NoTeam || activity->TeamActive(team)) { - for (int usedCellId : m_UsedCellIds) { + for (int usedCellId: m_UsedCellIds) { m_Cells[team + 1][usedCellId].clear(); m_PhysicsCells[team + 1][usedCellId].clear(); } @@ -62,13 +62,13 @@ namespace RTE { m_UsedCellIds.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SpatialPartitionGrid::Add(const IntRect &rect, const MovableObject &mo) { - const Activity *activity = g_ActivityMan.GetActivity(); + void SpatialPartitionGrid::Add(const IntRect& rect, const MovableObject& mo) { + const Activity* activity = g_ActivityMan.GetActivity(); RTEAssert(activity, "Tried to add to spatial partition grid with no running Activity!"); - const MovableObject &rootParentMo = *mo.GetRootParent(); + const MovableObject& rootParentMo = *mo.GetRootParent(); int topLeftCellX = rect.m_Left / m_CellSize; int topLeftCellY = rect.m_Top / m_CellSize; int bottomRightCellX = rect.m_Right / m_CellSize; @@ -104,7 +104,7 @@ namespace RTE { } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector SpatialPartitionGrid::GetMOsInBox(const Box &box, int ignoreTeam, bool getsHitByMOsOnly) const { + std::vector SpatialPartitionGrid::GetMOsInBox(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const { RTEAssert(ignoreTeam >= Activity::NoTeam && ignoreTeam < Activity::MaxTeamCount, "Invalid ignoreTeam given to SpatialPartitioningGrid::GetMOsInBox()!"); std::unordered_set potentialMOIDs; @@ -118,11 +118,11 @@ namespace RTE { int bottomRightCellY = static_cast(std::floor(bottomRight.m_Y / static_cast(m_CellSize))); // Note - GetCellIdForCellCoords accounts for wrapping automatically, so we don't have to deal with it here. - auto &cells = getsHitByMOsOnly ? m_PhysicsCells : m_Cells; + auto& cells = getsHitByMOsOnly ? m_PhysicsCells : m_Cells; for (int x = topLeftCellX; x <= bottomRightCellX; x++) { for (int y = topLeftCellY; y <= bottomRightCellY; y++) { - const std::vector &moidsInCell = cells[ignoreTeam + 1][GetCellIdForCellCoords(x, y)]; - for (MOID moid : moidsInCell) { + const std::vector& moidsInCell = cells[ignoreTeam + 1][GetCellIdForCellCoords(x, y)]; + for (MOID moid: moidsInCell) { potentialMOIDs.insert(moid); } } @@ -131,10 +131,10 @@ namespace RTE { std::list wrappedBoxes; g_SceneMan.WrapBox(box, wrappedBoxes); - std::vector MOList; - for (MOID moid : potentialMOIDs) { - MovableObject *mo = g_MovableMan.GetMOFromID(moid); - if (mo && std::any_of(wrappedBoxes.begin(), wrappedBoxes.end(), [&mo](const Box &wrappedBox) { return wrappedBox.IsWithinBox(mo->GetPos()); })) { + std::vector MOList; + for (MOID moid: potentialMOIDs) { + MovableObject* mo = g_MovableMan.GetMOFromID(moid); + if (mo && std::any_of(wrappedBoxes.begin(), wrappedBoxes.end(), [&mo](const Box& wrappedBox) { return wrappedBox.IsWithinBox(mo->GetPos()); })) { MOList.push_back(mo); } } @@ -142,9 +142,9 @@ namespace RTE { return MOList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector SpatialPartitionGrid::GetMOsInRadius(const Vector ¢er, float radius, int ignoreTeam, bool getsHitByMOsOnly) const { + std::vector SpatialPartitionGrid::GetMOsInRadius(const Vector& center, float radius, int ignoreTeam, bool getsHitByMOsOnly) const { RTEAssert(ignoreTeam >= Activity::NoTeam && ignoreTeam < Activity::MaxTeamCount, "Invalid ignoreTeam given to SpatialPartitioningGrid::GetMOsInRadius()!"); std::unordered_set potentialMOIDs; @@ -155,19 +155,19 @@ namespace RTE { int bottomRightCellY = static_cast(std::floor((center.m_Y + radius) / static_cast(m_CellSize))); // Note - GetCellIdForCellCoords accounts for wrapping automatically, so we don't have to deal with it here. - auto &cells = getsHitByMOsOnly ? m_PhysicsCells : m_Cells; + auto& cells = getsHitByMOsOnly ? m_PhysicsCells : m_Cells; for (int x = topLeftCellX; x <= bottomRightCellX; x++) { for (int y = topLeftCellY; y <= bottomRightCellY; y++) { - const std::vector &moidsInCell = cells[ignoreTeam + 1][GetCellIdForCellCoords(x, y)]; - for (MOID moid : moidsInCell) { + const std::vector& moidsInCell = cells[ignoreTeam + 1][GetCellIdForCellCoords(x, y)]; + for (MOID moid: moidsInCell) { potentialMOIDs.insert(moid); } } } - std::vector MOList; - for (MOID moid : potentialMOIDs) { - MovableObject *mo = g_MovableMan.GetMOFromID(moid); + std::vector MOList; + for (MOID moid: potentialMOIDs) { + MovableObject* mo = g_MovableMan.GetMOFromID(moid); if (mo && !g_SceneMan.ShortestDistance(center, mo->GetPos()).MagnitudeIsGreaterThan(radius)) { MOList.push_back(mo); } @@ -176,9 +176,9 @@ namespace RTE { return MOList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector & SpatialPartitionGrid::GetMOIDsAtPosition(int x, int y, int ignoreTeam, bool getsHitByMOsOnly) const { + const std::vector& SpatialPartitionGrid::GetMOIDsAtPosition(int x, int y, int ignoreTeam, bool getsHitByMOsOnly) const { int cellX = x / m_CellSize; int cellY = y / m_CellSize; @@ -187,11 +187,11 @@ namespace RTE { // So let's sanity check this shit. ignoreTeam = ignoreTeam < Activity::NoTeam || ignoreTeam >= Activity::MaxTeamCount ? Activity::NoTeam : ignoreTeam; - auto &cells = getsHitByMOsOnly ? m_PhysicsCells : m_Cells; + auto& cells = getsHitByMOsOnly ? m_PhysicsCells : m_Cells; return cells[ignoreTeam + 1][GetCellIdForCellCoords(cellX, cellY)]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int SpatialPartitionGrid::GetCellIdForCellCoords(int cellX, int cellY) const { // We act like we wrap, even if the Scene doesn't. The only cost is some duplicate collision checks, but that's a minor cost to pay :) @@ -205,4 +205,4 @@ namespace RTE { } return (wrappedY * m_Width) + wrappedX; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/SpatialPartitionGrid.h b/Source/System/SpatialPartitionGrid.h index 9cadcf7727..0d039a72e9 100644 --- a/Source/System/SpatialPartitionGrid.h +++ b/Source/System/SpatialPartitionGrid.h @@ -1,7 +1,7 @@ #ifndef _RTESPATIALPARTITIONGRID_ #define _RTESPATIALPARTITIONGRID_ -//TODO Move Team enum into Constants so we can avoid including Activity here. +// TODO Move Team enum into Constants so we can avoid including Activity here. #include "Activity.h" #include "Constants.h" @@ -20,7 +20,6 @@ namespace RTE { class SpatialPartitionGrid { public: - #pragma region Creation /// /// Constructor method used to instantiate a SpatialPartitionGrid object. @@ -30,7 +29,10 @@ namespace RTE { /// /// Constructor method used to instantiate a SpatialPartitionGrid object. /// - SpatialPartitionGrid(int width, int height, int cellSize) { Clear(); Create(width, height, cellSize); } + SpatialPartitionGrid(int width, int height, int cellSize) { + Clear(); + Create(width, height, cellSize); + } /// /// Makes the SpatialPartitionGrid object ready for use. @@ -43,7 +45,7 @@ namespace RTE { /// /// A reference to the SpatialPartitionGrid to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const SpatialPartitionGrid &reference); + int Create(const SpatialPartitionGrid& reference); #pragma endregion #pragma region Grid Management @@ -57,7 +59,7 @@ namespace RTE { /// /// A rectangle defining the space the MovableObject takes up. /// The MovableObject to add. - void Add(const IntRect &rect, const MovableObject &mo); + void Add(const IntRect& rect, const MovableObject& mo); /// /// Gets a vector of pointers to all MovableObjects within the given Box, who aren't of the ignored team. @@ -66,7 +68,7 @@ namespace RTE { /// The team to ignore when getting MovableObjects. /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. /// A vector of pointers to all MovableObjects within the given Box, who aren't of the ignored team. - std::vector GetMOsInBox(const Box &box, int ignoreTeam, bool getsHitByMOsOnly) const; + std::vector GetMOsInBox(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const; /// /// Get a vector of pointers to all the MovableObjects within the specified radius of the given center point, who aren't of the ignored team. @@ -76,7 +78,7 @@ namespace RTE { /// The team to ignore when getting MovableObjects. /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. /// A vector of pointers to all the MovableObjects within the specified radius of the given center point, who aren't of the ignored team. - std::vector GetMOsInRadius(const Vector ¢er, float radius, int ignoreTeam, bool getsHitByMOsOnly) const; + std::vector GetMOsInRadius(const Vector& center, float radius, int ignoreTeam, bool getsHitByMOsOnly) const; /// /// Gets the MOIDs that are potentially overlapping the given X and Y Scene coordinates. @@ -86,11 +88,10 @@ namespace RTE { /// The team to ignore when getting MOIDs. /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. /// A vector of MOIDs that are potentially overlapping the x and y coordinates. - const std::vector & GetMOIDsAtPosition(int x, int y, int ignoreTeam, bool getsHitByMOsOnly) const; + const std::vector& GetMOIDsAtPosition(int x, int y, int ignoreTeam, bool getsHitByMOsOnly) const; #pragma endregion private: - int m_Width; //!< The width of the SpatialPartitionGrid, in cells. int m_Height; //!< The height of the SpatialPartitionGrid, in cells. int m_CellSize; //!< The size of each of the SpatialPartitionGrid's cells, in pixels. @@ -116,7 +117,7 @@ namespace RTE { void Clear(); // Disallow the use of an implicit method. - SpatialPartitionGrid(const SpatialPartitionGrid &reference) = delete; + SpatialPartitionGrid(const SpatialPartitionGrid& reference) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/StandardIncludes.h b/Source/System/StandardIncludes.h index e31b8f8a27..929636a98e 100644 --- a/Source/System/StandardIncludes.h +++ b/Source/System/StandardIncludes.h @@ -93,7 +93,7 @@ namespace std { /// Custom std::hash specialization to allow using std::array as key in hash table based containers. /// template struct hash> { - size_t operator()(const array &arr) const { + size_t operator()(const array& arr) const { hash hasher; size_t outHash = 0; for (size_t i = 0; i < Size; ++i) { @@ -103,5 +103,5 @@ namespace std { return outHash; } }; -} +} // namespace std #endif diff --git a/Source/System/System.cpp b/Source/System/System.cpp index 76eedacdbc..00455a2d7a 100644 --- a/Source/System/System.cpp +++ b/Source/System/System.cpp @@ -31,11 +31,11 @@ namespace RTE { const std::string System::s_UserdataDirectory = "Userdata/"; const std::string System::s_ModulePackageExtension = ".rte"; const std::string System::s_ZippedModulePackageExtension = ".zip"; - const std::unordered_set System::s_SupportedExtensions = { ".ini", ".txt", ".lua", ".cfg", ".bmp", ".png", ".jpg", ".jpeg", ".wav", ".ogg", ".mp3", ".flac" }; + const std::unordered_set System::s_SupportedExtensions = {".ini", ".txt", ".lua", ".cfg", ".bmp", ".png", ".jpg", ".jpeg", ".wav", ".ogg", ".mp3", ".flac"}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void System::Initialize(const char *thisExePathAndName) { + void System::Initialize(const char* thisExePathAndName) { s_ThisExePathAndName = std::filesystem::path(thisExePathAndName).generic_string(); s_WorkingDirectory = std::filesystem::current_path().generic_string(); @@ -49,54 +49,61 @@ namespace RTE { } // Get the URL of the application bundle CFURLRef bundleURL = CFBundleCopyBundleURL(mainBundle); - + if (!bundleURL) { RTEAbort("Could not copy App Bundle URL, the bundle does not exist!") } // Convert the URL to a C string char pathBuffer[PATH_MAX]; - if (CFURLGetFileSystemRepresentation(bundleURL, true, (UInt8 *)pathBuffer, sizeof(pathBuffer))) { + if (CFURLGetFileSystemRepresentation(bundleURL, true, (UInt8*)pathBuffer, sizeof(pathBuffer))) { // bundlePath now contains the path to the application bundle as a C string auto bundlePath = std::filesystem::path(pathBuffer); - + if (std::filesystem::exists(bundlePath) && bundlePath.extension() == ".app") { auto workingDirPath = bundlePath.parent_path(); std::filesystem::current_path(workingDirPath); s_WorkingDirectory = workingDirPath.generic_string(); } - + } else { CFRelease(bundleURL); RTEAbort("Could not write App Bundle URL to a readable representation! The bundle path may exceed the local PATH_MAX.") } // Release the CFURL object CFRelease(bundleURL); - - + #endif - if (s_WorkingDirectory.back() != '/') { s_WorkingDirectory.append("/"); } + if (s_WorkingDirectory.back() != '/') { + s_WorkingDirectory.append("/"); + } - if (!PathExistsCaseSensitive(s_WorkingDirectory + s_ScreenshotDirectory)) { MakeDirectory(s_WorkingDirectory + s_ScreenshotDirectory); } - if (!PathExistsCaseSensitive(s_WorkingDirectory + s_ModDirectory)) { MakeDirectory(s_WorkingDirectory + s_ModDirectory); } - if (!PathExistsCaseSensitive(s_WorkingDirectory + s_UserdataDirectory)) { MakeDirectory(s_WorkingDirectory + s_UserdataDirectory); } + if (!PathExistsCaseSensitive(s_WorkingDirectory + s_ScreenshotDirectory)) { + MakeDirectory(s_WorkingDirectory + s_ScreenshotDirectory); + } + if (!PathExistsCaseSensitive(s_WorkingDirectory + s_ModDirectory)) { + MakeDirectory(s_WorkingDirectory + s_ModDirectory); + } + if (!PathExistsCaseSensitive(s_WorkingDirectory + s_UserdataDirectory)) { + MakeDirectory(s_WorkingDirectory + s_UserdataDirectory); + } #ifdef _WIN32 // Consider Settings.ini not existing as first time boot, then create quick launch files if they are missing. if (!std::filesystem::exists(s_WorkingDirectory + s_UserdataDirectory + "Settings.ini")) { std::array, 7> quickLaunchFiles = {{ - { "Launch Actor Editor.bat", R"(start "" "Cortex Command.exe" -editor "ActorEditor")" }, - { "Launch Area Editor.bat", R"(start "" "Cortex Command.exe" -editor "AreaEditor")" }, - { "Launch Assembly Editor.bat", R"(start "" "Cortex Command.exe" -editor "AssemblyEditor")" }, - { "Launch Gib Editor.bat", R"(start "" "Cortex Command.exe" -editor "GibEditor")" }, - { "Launch Scene Editor.bat", R"(start "" "Cortex Command.exe" -editor "SceneEditor")" }, + {"Launch Actor Editor.bat", R"(start "" "Cortex Command.exe" -editor "ActorEditor")"}, + {"Launch Area Editor.bat", R"(start "" "Cortex Command.exe" -editor "AreaEditor")"}, + {"Launch Assembly Editor.bat", R"(start "" "Cortex Command.exe" -editor "AssemblyEditor")"}, + {"Launch Gib Editor.bat", R"(start "" "Cortex Command.exe" -editor "GibEditor")"}, + {"Launch Scene Editor.bat", R"(start "" "Cortex Command.exe" -editor "SceneEditor")"}, #ifdef TARGET_MACHINE_X86 - { "Start Dedicated Server x86.bat", R"(start "" "Cortex Command x86.exe" -server 8000)" }, + {"Start Dedicated Server x86.bat", R"(start "" "Cortex Command x86.exe" -server 8000)"}, #else - { "Start Dedicated Server.bat", R"(start "" "Cortex Command.exe" -server 8000)" }, + {"Start Dedicated Server.bat", R"(start "" "Cortex Command.exe" -server 8000)"}, #endif }}; - for (const auto &[fileName, fileContent] : quickLaunchFiles) { + for (const auto& [fileName, fileContent]: quickLaunchFiles) { if (std::filesystem::path filePath = s_WorkingDirectory + fileName; !std::filesystem::exists(filePath)) { std::ofstream fileStream(filePath); fileStream << fileContent; @@ -107,9 +114,9 @@ namespace RTE { #endif } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool System::MakeDirectory(const std::string &pathToMake) { + bool System::MakeDirectory(const std::string& pathToMake) { bool createResult = std::filesystem::create_directory(pathToMake); if (createResult) { std::filesystem::permissions(pathToMake, std::filesystem::perms::owner_all | std::filesystem::perms::group_read | std::filesystem::perms::group_exec | std::filesystem::perms::others_read | std::filesystem::perms::others_exec, std::filesystem::perm_options::add); @@ -117,13 +124,13 @@ namespace RTE { return createResult; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool System::PathExistsCaseSensitive(const std::string &pathToCheck) { + bool System::PathExistsCaseSensitive(const std::string& pathToCheck) { // Use Hash for compiler independent hashing. if (s_CaseSensitive) { if (s_WorkingTree.empty()) { - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::recursive_directory_iterator(s_WorkingDirectory, std::filesystem::directory_options::follow_directory_symlink)) { + for (const std::filesystem::directory_entry& directoryEntry: std::filesystem::recursive_directory_iterator(s_WorkingDirectory, std::filesystem::directory_options::follow_directory_symlink)) { s_WorkingTree.emplace_back(Hash(directoryEntry.path().generic_string().substr(s_WorkingDirectory.length()))); } } @@ -138,7 +145,7 @@ namespace RTE { return std::filesystem::exists(pathToCheck); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void System::EnableLoggingToCLI() { #ifdef _WIN32 @@ -160,10 +167,12 @@ namespace RTE { s_LogToCLI = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void System::PrintLoadingToCLI(const std::string &reportString, bool newItem) { - if (newItem) { std::cout << std::endl; } + void System::PrintLoadingToCLI(const std::string& reportString, bool newItem) { + if (newItem) { + std::cout << std::endl; + } // Overwrite current line std::cout << "\r"; size_t startPos = 0; @@ -200,9 +209,9 @@ namespace RTE { std::cout << unicodedOutput << std::flush; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void System::PrintToCLI(const std::string &stringToPrint) { + void System::PrintToCLI(const std::string& stringToPrint) { #if _LINUX_OR_MACOSX_ std::string outputString = stringToPrint; // Color the words ERROR: and SYSTEM: red @@ -224,9 +233,9 @@ namespace RTE { #endif } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string System::ExtractZippedDataModule(const std::string &zippedModulePath) { + std::string System::ExtractZippedDataModule(const std::string& zippedModulePath) { std::string zippedModuleName = System::GetModDirectory() + std::filesystem::path(zippedModulePath).filename().generic_string(); unzFile zippedModule = unzOpen(zippedModuleName.c_str()); @@ -235,7 +244,9 @@ namespace RTE { if (!zippedModule) { bool makeDirResult = false; - if (!std::filesystem::exists(s_WorkingDirectory + "_FailedExtract")) { makeDirResult = MakeDirectory(s_WorkingDirectory + "_FailedExtract"); } + if (!std::filesystem::exists(s_WorkingDirectory + "_FailedExtract")) { + makeDirResult = MakeDirectory(s_WorkingDirectory + "_FailedExtract"); + } if (makeDirResult) { extractionProgressReport << "Failed to extract Data module from: " + zippedModuleName + " - Moving zip file to failed extract directory!\n"; std::filesystem::rename(s_WorkingDirectory + zippedModuleName, s_WorkingDirectory + "_FailedExtract/" + zippedModuleName); @@ -309,8 +320,10 @@ namespace RTE { if (unzOpenCurrentFile(zippedModule) != UNZ_OK) { extractionProgressReport << "\tSkipped file: " + zippedModuleName + " - Could not open file!\n"; } else { - FILE *outputFile = fopen(outputFileName.c_str(), "wb"); - if (outputFile == nullptr) { extractionProgressReport << "\tSkipped file: " + outputFileName + " - Could not open/create destination file!\n"; } + FILE* outputFile = fopen(outputFileName.c_str(), "wb"); + if (outputFile == nullptr) { + extractionProgressReport << "\tSkipped file: " + outputFileName + " - Could not open/create destination file!\n"; + } // Write the entire file out, reading in buffer size chunks and spitting them out to the output stream. bool abortWrite = false; @@ -323,7 +336,7 @@ namespace RTE { if (bytesRead < 0) { extractionProgressReport << "\tSkipped file: " + outputFileName + " - File is empty or corrupt!\n"; abortWrite = true; - // Sanity check how damn big this file we're writing is becoming. could prevent zip bomb exploits: http://en.wikipedia.org/wiki/Zip_bomb + // Sanity check how damn big this file we're writing is becoming. could prevent zip bomb exploits: http://en.wikipedia.org/wiki/Zip_bomb } else if (totalBytesRead >= s_MaxUnzippedFileSize) { extractionProgressReport << "\tSkipped file: " + outputFileName + " - File is too large, extract it manually!\n"; abortWrite = true; @@ -332,7 +345,7 @@ namespace RTE { break; } fwrite(fileBuffer.data(), bytesRead, 1, outputFile); - // Keep going while bytes are still being read (0 means end of file). + // Keep going while bytes are still being read (0 means end of file). } while (bytesRead > 0 && outputFile); fclose(outputFile); @@ -356,19 +369,19 @@ namespace RTE { return extractionProgressReport.str(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int System::ASCIIFileContainsString(const std::string &filePath, const std::string_view &findString) { + int System::ASCIIFileContainsString(const std::string& filePath, const std::string_view& findString) { std::ifstream inputStream(filePath, std::ios::binary); if (!inputStream.is_open()) { return -1; } else { size_t fileSize = static_cast(std::filesystem::file_size(filePath)); std::vector rawData(fileSize); - inputStream.read(reinterpret_cast(&rawData[0]), fileSize); + inputStream.read(reinterpret_cast(&rawData[0]), fileSize); inputStream.close(); return (std::search(rawData.begin(), rawData.end(), findString.begin(), findString.end()) != rawData.end()) ? 0 : 1; } } -} +} // namespace RTE diff --git a/Source/System/System.h b/Source/System/System.h index e92afefd34..5c0b1e9e7e 100644 --- a/Source/System/System.h +++ b/Source/System/System.h @@ -9,13 +9,12 @@ namespace RTE { class System { public: - #pragma region Creation /// /// Store the current working directory and create any missing subdirectories. /// /// The path and name of this executable. - static void Initialize(const char *thisExePathAndName); + static void Initialize(const char* thisExePathAndName); #pragma endregion #pragma region Program Termination @@ -42,56 +41,56 @@ namespace RTE { /// Gets the absolute path to this executable. /// /// Absolute path to this executable. - static const std::string & GetThisExePathAndName() { return s_ThisExePathAndName; } + static const std::string& GetThisExePathAndName() { return s_ThisExePathAndName; } /// /// Gets the current working directory. /// /// Absolute path to current working directory. - static const std::string & GetWorkingDirectory() { return s_WorkingDirectory; } + static const std::string& GetWorkingDirectory() { return s_WorkingDirectory; } /// /// Gets the game data directory name. /// /// Folder name of the game data directory. - static const std::string & GetDataDirectory() { return s_DataDirectory; } + static const std::string& GetDataDirectory() { return s_DataDirectory; } /// /// Gets the screenshot directory name. /// /// Folder name of the screenshots directory. - static const std::string & GetScreenshotDirectory() { return s_ScreenshotDirectory; } + static const std::string& GetScreenshotDirectory() { return s_ScreenshotDirectory; } /// /// Gets the mod directory name. /// /// Folder name of the mod directory. - static const std::string & GetModDirectory() { return s_ModDirectory; } + static const std::string& GetModDirectory() { return s_ModDirectory; } /// /// Gets the userdata directory name. /// /// Folder name of the userdata directory. - static const std::string & GetUserdataDirectory() { return s_UserdataDirectory; } + static const std::string& GetUserdataDirectory() { return s_UserdataDirectory; } /// /// Gets the extension that determines a directory/file is an RTE module. /// /// String containing the RTE module extension. - static const std::string & GetModulePackageExtension() { return s_ModulePackageExtension; } + static const std::string& GetModulePackageExtension() { return s_ModulePackageExtension; } /// /// Gets the extension that determines a file is a zipped RTE module. /// /// String containing the zipped RTE module extension. - static const std::string & GetZippedModulePackageExtension() { return s_ZippedModulePackageExtension; } + static const std::string& GetZippedModulePackageExtension() { return s_ZippedModulePackageExtension; } /// /// Create a directory. /// /// Path to create. /// Returns 0 if successful. - static bool MakeDirectory(const std::string &pathToMake); + static bool MakeDirectory(const std::string& pathToMake); #pragma endregion #pragma region Filesystem @@ -112,7 +111,7 @@ namespace RTE { /// /// The path to check. /// Whether the file exists. - static bool PathExistsCaseSensitive(const std::string &pathToCheck); + static bool PathExistsCaseSensitive(const std::string& pathToCheck); #pragma endregion #pragma region Command-Line Interface @@ -130,13 +129,13 @@ namespace RTE { /// /// Prints the loading progress report to command-line. /// - static void PrintLoadingToCLI(const std::string &reportString, bool newItem = false); + static void PrintLoadingToCLI(const std::string& reportString, bool newItem = false); /// /// Prints console output to command-line. /// /// - static void PrintToCLI(const std::string &stringToPrint); + static void PrintToCLI(const std::string& stringToPrint); #pragma endregion #pragma region Archived DataModule Handling @@ -145,7 +144,7 @@ namespace RTE { /// /// Path to the module to extract. /// A string containing the progress report of the extraction. - static std::string ExtractZippedDataModule(const std::string &zippedModulePath); + static std::string ExtractZippedDataModule(const std::string& zippedModulePath); #pragma endregion #pragma region Module Validation @@ -166,7 +165,7 @@ namespace RTE { /// Fires up the default browser for the current OS on a specific URL. /// /// A string with the URL to send the browser to. - static void OpenBrowserToURL(const std::string_view &goToURL) { std::system(std::string("start ").append(goToURL).c_str()); } + static void OpenBrowserToURL(const std::string_view& goToURL) { std::system(std::string("start ").append(goToURL).c_str()); } /// /// Searches through an ASCII file on disk for a specific string and tells whether it was found or not. @@ -174,11 +173,10 @@ namespace RTE { /// The path to the ASCII file to search. /// The exact string to look for. Case sensitive! /// 0 if the string was found in the file or 1 if not. -1 if the file was inaccessible. - static int ASCIIFileContainsString(const std::string & filePath, const std::string_view & findString); + static int ASCIIFileContainsString(const std::string& filePath, const std::string_view& findString); #pragma endregion private: - static bool s_Quit; //!< Whether the user requested program termination through GUI or the window close button. static bool s_LogToCLI; //!< Bool to tell whether to print the loading log and anything specified with PrintToCLI to command-line or not. static bool s_ExternalModuleValidation; //!< Whether to run the program in a special mode where it will immediately quit without any messages after either successful loading of all modules or aborting during loading. For use by an external tool. @@ -200,5 +198,5 @@ namespace RTE { static constexpr int s_FileBufferSize = 8192; //!< Buffer to hold data read from the zip file. static constexpr int s_MaxUnzippedFileSize = 104857600; //!< Maximum size of single file being extracted from zip archive (100MiB). }; -} +} // namespace RTE #endif diff --git a/Source/System/Timer.cpp b/Source/System/Timer.cpp index b367e77114..c52c13fa6a 100644 --- a/Source/System/Timer.cpp +++ b/Source/System/Timer.cpp @@ -2,7 +2,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Timer::Clear() { m_StartRealTime = g_TimerMan.GetRealTickCount(); @@ -11,14 +11,14 @@ namespace RTE { m_SimTimeLimit = -1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Timer::Create() { m_TicksPerMS = static_cast(g_TimerMan.GetTicksPerSecond()) * 0.001; return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Timer::Create(double simTimeLimit, double elapsedSimTime) { m_TicksPerMS = static_cast(g_TimerMan.GetTicksPerSecond()) * 0.001; @@ -29,9 +29,9 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Timer::Create(const Timer &reference) { + int Timer::Create(const Timer& reference) { m_StartRealTime = reference.m_StartRealTime; m_StartSimTime = reference.m_StartSimTime; m_RealTimeLimit = reference.m_RealTimeLimit; @@ -39,4 +39,4 @@ namespace RTE { m_TicksPerMS = static_cast(g_TimerMan.GetTicksPerSecond()) * 0.001; return 0; } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/Timer.h b/Source/System/Timer.h index 814ac87ebb..d777083766 100644 --- a/Source/System/Timer.h +++ b/Source/System/Timer.h @@ -11,31 +11,42 @@ namespace RTE { class Timer { public: - #pragma region Creation /// /// Constructor method used to instantiate a Timer object. /// - Timer() { Clear(); Create(); } + Timer() { + Clear(); + Create(); + } /// /// Constructor method used to instantiate a Timer object with a set sim time elapsed. /// /// A unsigned long defining this Timer's sim time limit in ms. - Timer(double simTimeLimit) { Clear(); Create(simTimeLimit); } + Timer(double simTimeLimit) { + Clear(); + Create(simTimeLimit); + } /// /// Constructor method used to instantiate a Timer object with a set sim time elapsed. /// /// A unsigned long defining this Timer's sim time limit in ms. /// A unsigned long defining the amount of time (in ms) that this Timer should start with elapsed. - Timer(double simTimeLimit, double elapsedSimTime) { Clear(); Create(simTimeLimit, elapsedSimTime); } + Timer(double simTimeLimit, double elapsedSimTime) { + Clear(); + Create(simTimeLimit, elapsedSimTime); + } /// /// Copy constructor method used to instantiate a Timer object identical to an already existing one. /// /// A Timer object which is passed in by reference. - Timer(const Timer &reference) { Clear(); Create(reference); } + Timer(const Timer& reference) { + Clear(); + Create(reference); + } /// /// Makes the Timer object ready for use. @@ -63,7 +74,7 @@ namespace RTE { /// /// A reference to the Timer to deep copy. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const Timer &reference); + int Create(const Timer& reference); #pragma endregion #pragma region Destruction @@ -71,7 +82,10 @@ namespace RTE { /// Resets the timer so that the elapsed time is 0 ms. /// // TODO: Figure out why calling Clear() here breaks time. - void Reset() { m_StartRealTime = g_TimerMan.GetRealTickCount(); m_StartSimTime = g_TimerMan.GetSimTickCount(); } + void Reset() { + m_StartRealTime = g_TimerMan.GetRealTickCount(); + m_StartSimTime = g_TimerMan.GetSimTickCount(); + } #pragma endregion #pragma region Real Time @@ -301,8 +315,6 @@ namespace RTE { #pragma endregion protected: - - double m_TicksPerMS; //!< Ticks per MS. int64_t m_StartRealTime; //!< Absolute tick count when this was started in real time. @@ -312,11 +324,10 @@ namespace RTE { int64_t m_SimTimeLimit; //!< Tick count, relative to the start time, when this should indicate end or expired in simulation time. private: - /// /// Clears all the member variables of this Timer, effectively resetting the members of this abstraction level only. /// void Clear(); }; -} +} // namespace RTE #endif \ No newline at end of file diff --git a/Source/System/Vector.cpp b/Source/System/Vector.cpp index 0412f14cac..6a825e07e0 100644 --- a/Source/System/Vector.cpp +++ b/Source/System/Vector.cpp @@ -6,20 +6,20 @@ namespace RTE { const std::string Vector::c_ClassName = "Vector"; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Vector::ReadProperty(const std::string_view &propName, Reader &reader) { + int Vector::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); - + MatchProperty("X", { reader >> m_X; }); MatchProperty("Y", { reader >> m_Y; }); - + EndPropertyList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Vector::Save(Writer &writer) const { + int Vector::Save(Writer& writer) const { Serializable::Save(writer); writer.NewPropertyWithValue("X", m_X); @@ -27,4 +27,4 @@ namespace RTE { return 0; } -} +} // namespace RTE diff --git a/Source/System/Vector.h b/Source/System/Vector.h index cb2e28f7d6..d91fb772c8 100644 --- a/Source/System/Vector.h +++ b/Source/System/Vector.h @@ -6,7 +6,10 @@ namespace RTE { - enum Axes { X = 0, Y = 1 }; + enum Axes { + X = 0, + Y = 1 + }; /// /// A useful 2D float vector. @@ -14,7 +17,6 @@ namespace RTE { class Vector : public Serializable { public: - SerializableClassNameGetter; SerializableOverrideMethods; @@ -32,14 +34,18 @@ namespace RTE { /// /// Float defining the initial X value of this Vector. /// Float defining the initial Y value of this Vector. - inline Vector(const float inputX, const float inputY) : m_X(inputX), m_Y(inputY) {}; + inline Vector(const float inputX, const float inputY) : + m_X(inputX), m_Y(inputY){}; #pragma endregion #pragma region Destruction /// /// Sets both the X and Y of this Vector to zero. /// - inline void Reset() override { m_X = 0.0F; m_Y = 0.0F; } + inline void Reset() override { + m_X = 0.0F; + m_Y = 0.0F; + } #pragma endregion #pragma region Getters and Setters @@ -54,7 +60,10 @@ namespace RTE { /// /// A float value that the X value will be set to. /// Vector reference to this after the operation. - inline Vector & SetX(const float newX) { m_X = newX; return *this; } + inline Vector& SetX(const float newX) { + m_X = newX; + return *this; + } /// /// Gets the Y value of this Vector. @@ -67,7 +76,10 @@ namespace RTE { /// /// A float value that the Y value will be set to. /// Vector reference to this after the operation. - inline Vector & SetY(const float newY) { m_Y = newY; return *this; } + inline Vector& SetY(const float newY) { + m_Y = newY; + return *this; + } /// /// Sets both the X and Y values of this Vector. @@ -75,7 +87,11 @@ namespace RTE { /// A float value that the X value will be set to. /// A float value that the Y value will be set to. /// Vector reference to this after the operation. - inline Vector & SetXY(const float newX, const float newY) { m_X = newX; m_Y = newY; return *this; } + inline Vector& SetXY(const float newX, const float newY) { + m_X = newX; + m_Y = newY; + return *this; + } /// /// Gets the absolute largest of the two elements. Will always be positive. @@ -101,7 +117,10 @@ namespace RTE { /// /// Whether or not to flip the X element or not. /// Vector reference to this after the operation. - inline Vector & FlipX(const bool flipX = true) { *this = GetXFlipped(flipX); return *this; } + inline Vector& FlipX(const bool flipX = true) { + *this = GetXFlipped(flipX); + return *this; + } /// /// Gets a Vector identical to this except that its Y component is flipped. @@ -115,7 +134,10 @@ namespace RTE { /// /// Whether or not to flip the Y element or not. /// Vector reference to this after the operation. - inline Vector & FlipY(const bool flipY = true) { *this = GetYFlipped(flipY); return *this; } + inline Vector& FlipY(const bool flipY = true) { + *this = GetYFlipped(flipY); + return *this; + } /// /// Indicates whether the X component of this Vector is 0. @@ -140,7 +162,7 @@ namespace RTE { /// /// The Vector to compare with. /// Whether the X and Y components of this Vector each have opposite signs to their corresponding components of a passed in Vector. - inline bool IsOpposedTo(const Vector &opp) const { return ((XIsZero() && opp.XIsZero()) || (std::signbit(m_X) != std::signbit(opp.m_X))) && ((YIsZero() && opp.YIsZero()) || (std::signbit(m_Y) != std::signbit(opp.m_Y))); } + inline bool IsOpposedTo(const Vector& opp) const { return ((XIsZero() && opp.XIsZero()) || (std::signbit(m_X) != std::signbit(opp.m_X))) && ((YIsZero() && opp.YIsZero()) || (std::signbit(m_Y) != std::signbit(opp.m_Y))); } #pragma endregion #pragma region Magnitude @@ -175,7 +197,7 @@ namespace RTE { /// /// A float value that the magnitude will be set to. /// Vector reference to this after the operation. - inline Vector & SetMagnitude(const float newMag) { + inline Vector& SetMagnitude(const float newMag) { if (IsZero()) { SetXY(newMag, 0.0F); } else { @@ -189,9 +211,13 @@ namespace RTE { /// /// A float value that the magnitude will be capped by. /// Vector reference to this after the operation. - inline Vector & CapMagnitude(const float capMag) { - if (capMag == 0.0F) { Reset(); } - if (MagnitudeIsGreaterThan(capMag)) { SetMagnitude(capMag); } + inline Vector& CapMagnitude(const float capMag) { + if (capMag == 0.0F) { + Reset(); + } + if (MagnitudeIsGreaterThan(capMag)) { + SetMagnitude(capMag); + } return *this; } @@ -201,8 +227,10 @@ namespace RTE { /// A float value that defines the lower limit for the magnitude of this Vector. /// A float value that defines the upper limit for the magnitude of this Vector. /// A reference to this after the change. - inline Vector & ClampMagnitude(float lowerMagnitudeLimit, float upperMagnitudeLimit) { - if (upperMagnitudeLimit < lowerMagnitudeLimit) { std::swap(upperMagnitudeLimit, lowerMagnitudeLimit); } + inline Vector& ClampMagnitude(float lowerMagnitudeLimit, float upperMagnitudeLimit) { + if (upperMagnitudeLimit < lowerMagnitudeLimit) { + std::swap(upperMagnitudeLimit, lowerMagnitudeLimit); + } if (upperMagnitudeLimit == 0.0F && lowerMagnitudeLimit == 0.0F) { Reset(); } else if (MagnitudeIsLessThan(lowerMagnitudeLimit)) { @@ -223,7 +251,10 @@ namespace RTE { /// Scales this vector to have the same direction but a magnitude of 1.0. /// /// Vector reference to this after the operation. - inline Vector & Normalize() { *this = GetNormalized(); return *this; } + inline Vector& Normalize() { + *this = GetNormalized(); + return *this; + } #pragma endregion #pragma region Rotation @@ -231,7 +262,10 @@ namespace RTE { /// Get this Vector's absolute angle in radians. e.g: when x = 1, y = 0, the value returned here will be 0. x = 0, y = 1 yields -pi/2 here. /// /// The absolute angle in radians, in the interval [-0.5 pi, 1.5 pi). - inline float GetAbsRadAngle() const { const float radAngle = -std::atan2(m_Y, m_X); return (radAngle < -c_HalfPI) ? (radAngle + c_TwoPI) : radAngle; } + inline float GetAbsRadAngle() const { + const float radAngle = -std::atan2(m_Y, m_X); + return (radAngle < -c_HalfPI) ? (radAngle + c_TwoPI) : radAngle; + } /// /// Sets this Vector's absolute angle in radians. @@ -269,7 +303,10 @@ namespace RTE { /// /// The angle in radians to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. /// Vector reference to this after the operation. - inline Vector & RadRotate(const float angle) { *this = GetRadRotatedCopy(angle); return *this; } + inline Vector& RadRotate(const float angle) { + *this = GetRadRotatedCopy(angle); + return *this; + } /// /// Returns a copy of this Vector, rotated relatively by an angle in degrees. @@ -283,14 +320,17 @@ namespace RTE { /// /// The angle in degrees to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. /// Vector reference to this after the operation. - inline Vector & DegRotate(const float angle) { *this = GetDegRotatedCopy(angle); return *this; } + inline Vector& DegRotate(const float angle) { + *this = GetDegRotatedCopy(angle); + return *this; + } /// /// Set this Vector to an absolute rotation based on the absolute rotation of another Vector. /// /// The reference Vector whose absolute angle from positive X (0 degrees) this Vector will be rotated to. /// Vector reference to this after the operation. - inline Vector & AbsRotateTo(const Vector &refVector) { return RadRotate(refVector.GetAbsRadAngle() - GetAbsRadAngle()); } + inline Vector& AbsRotateTo(const Vector& refVector) { return RadRotate(refVector.GetAbsRadAngle() - GetAbsRadAngle()); } /// /// Returns a Vector that is perpendicular to this, rotated PI/2. @@ -302,7 +342,10 @@ namespace RTE { /// Makes this vector perpendicular to its previous state, rotated PI/2. Much faster than RadRotate by PI/2. /// /// Vector reference to this after the operation. - inline Vector & Perpendicularize() { *this = GetPerpendicular(); return *this; } + inline Vector& Perpendicularize() { + *this = GetPerpendicular(); + return *this; + } #pragma endregion #pragma region Rounding @@ -310,25 +353,38 @@ namespace RTE { /// Rounds the X and Y values of this Vector upwards. E.g. 0.49 -> 0.0 and 0.5 -> 1.0. /// /// Vector reference to this after the operation. - inline Vector & Round() { *this = GetRounded(); return *this; } + inline Vector& Round() { + *this = GetRounded(); + return *this; + } /// /// Sets the X and Y of this Vector to the nearest half value. E.g. 1.0 -> 1.5 and 0.9 -> 0.5. /// /// Vector reference to this after the operation. - inline Vector & ToHalf() { m_X = std::round(m_X * 2) / 2; m_Y = std::round(m_Y * 2) / 2; return *this; } + inline Vector& ToHalf() { + m_X = std::round(m_X * 2) / 2; + m_Y = std::round(m_Y * 2) / 2; + return *this; + } /// /// Sets the X and Y of this Vector to the greatest integers that are not greater than their original values. E.g. -1.02 becomes -2.0. /// /// Vector reference to this after the operation. - inline Vector & Floor() { *this = GetFloored(); return *this; } + inline Vector& Floor() { + *this = GetFloored(); + return *this; + } /// /// Sets the X and Y of this Vector to the lowest integers that are not less than their original values. E.g. -1.02 becomes -1.0. /// /// Vector reference to this after the operation. - inline Vector & Ceiling() { *this = GetCeilinged(); return *this; } + inline Vector& Ceiling() { + *this = GetCeilinged(); + return *this; + } /// /// Returns a rounded copy of this Vector. Does not alter this Vector. @@ -391,14 +447,14 @@ namespace RTE { /// /// The Vector which will be the right hand side operand of the dot product operation. /// The resulting dot product scalar float. - inline float Dot(const Vector &rhs) const { return (m_X * rhs.m_X) + (m_Y * rhs.m_Y); } + inline float Dot(const Vector& rhs) const { return (m_X * rhs.m_X) + (m_Y * rhs.m_Y); } /// /// Returns the 2D cross product of this Vector and the passed in Vector. This is really the area of the parallelogram that the two vectors form. /// /// The Vector which will be the right hand side operand of the cross product operation. /// The resulting 2D cross product parallelogram area. - inline float Cross(const Vector &rhs) const { return (m_X * rhs.m_Y) - (rhs.m_X * m_Y); } + inline float Cross(const Vector& rhs) const { return (m_X * rhs.m_Y) - (rhs.m_X * m_Y); } #pragma endregion #pragma region Operator Overloads @@ -407,7 +463,11 @@ namespace RTE { /// /// A Vector reference. /// A reference to the changed Vector. - inline Vector & operator=(const Vector &rhs) { m_X = rhs.m_X; m_Y = rhs.m_Y; return *this; } + inline Vector& operator=(const Vector& rhs) { + m_X = rhs.m_X; + m_Y = rhs.m_Y; + return *this; + } /// /// Unary negation overload for single Vectors. @@ -421,7 +481,7 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// A boolean indicating whether the two operands are equal or not. - inline friend bool operator==(const Vector &lhs, const Vector &rhs) { return lhs.m_X == rhs.m_X && lhs.m_Y == rhs.m_Y; } + inline friend bool operator==(const Vector& lhs, const Vector& rhs) { return lhs.m_X == rhs.m_X && lhs.m_Y == rhs.m_Y; } /// /// An inequality operator for testing if any two Vectors are unequal. @@ -429,7 +489,7 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// A boolean indicating whether the two operands are unequal or not. - inline friend bool operator!=(const Vector &lhs, const Vector &rhs) { return !(lhs == rhs); } + inline friend bool operator!=(const Vector& lhs, const Vector& rhs) { return !(lhs == rhs); } /// /// A stream insertion operator for sending a Vector to an output stream. @@ -437,7 +497,10 @@ namespace RTE { /// An ostream reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// An ostream reference for further use in an expression. - inline friend std::ostream & operator<<(std::ostream &stream, const Vector &operand) { stream << "{" << operand.m_X << ", " << operand.m_Y << "}"; return stream; } + inline friend std::ostream& operator<<(std::ostream& stream, const Vector& operand) { + stream << "{" << operand.m_X << ", " << operand.m_Y << "}"; + return stream; + } /// /// Addition operator overload for Vectors. @@ -445,7 +508,7 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// The resulting Vector. - inline friend Vector operator+(const Vector &lhs, const Vector &rhs) { return Vector(lhs.m_X + rhs.m_X, lhs.m_Y + rhs.m_Y); } + inline friend Vector operator+(const Vector& lhs, const Vector& rhs) { return Vector(lhs.m_X + rhs.m_X, lhs.m_Y + rhs.m_Y); } /// /// Subtraction operator overload for Vectors. @@ -453,14 +516,14 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// The resulting Vector. - inline friend Vector operator-(const Vector &lhs, const Vector &rhs) { return Vector(lhs.m_X - rhs.m_X, lhs.m_Y - rhs.m_Y); } + inline friend Vector operator-(const Vector& lhs, const Vector& rhs) { return Vector(lhs.m_X - rhs.m_X, lhs.m_Y - rhs.m_Y); } /// /// Multiplication operator overload for a Vector and a float. /// /// A float reference as the right hand side operand. /// The resulting Vector. - inline Vector operator*(const float &rhs) const { return Vector(m_X * rhs, m_Y * rhs); } + inline Vector operator*(const float& rhs) const { return Vector(m_X * rhs, m_Y * rhs); } /// /// Multiplication operator overload for Vectors. @@ -468,14 +531,14 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// The resulting Vector. - inline friend Vector operator*(const Vector &lhs, const Vector &rhs) { return Vector(lhs.m_X * rhs.m_X, lhs.m_Y * rhs.m_Y); } + inline friend Vector operator*(const Vector& lhs, const Vector& rhs) { return Vector(lhs.m_X * rhs.m_X, lhs.m_Y * rhs.m_Y); } /// /// Division operator overload for a Vector and a float. /// /// A float reference as the right hand side operand. /// The resulting Vector. - Vector operator/(const float &rhs) const { return (rhs != 0) ? Vector(m_X / rhs, m_Y / rhs) : Vector(0, 0); } + Vector operator/(const float& rhs) const { return (rhs != 0) ? Vector(m_X / rhs, m_Y / rhs) : Vector(0, 0); } /// /// Division operator overload for Vectors. @@ -483,7 +546,7 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// The resulting Vector. - inline friend Vector operator/(const Vector &lhs, const Vector &rhs) { return (rhs.m_X != 0 && rhs.m_Y != 0) ? Vector(lhs.m_X / rhs.m_X, lhs.m_Y / rhs.m_Y) : Vector(0, 0); } + inline friend Vector operator/(const Vector& lhs, const Vector& rhs) { return (rhs.m_X != 0 && rhs.m_Y != 0) ? Vector(lhs.m_X / rhs.m_X, lhs.m_Y / rhs.m_Y) : Vector(0, 0); } /// /// Self-addition operator overload for Vectors. @@ -491,7 +554,11 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// A reference to the resulting Vector (the left one). - inline friend Vector & operator+=(Vector &lhs, const Vector &rhs) { lhs.m_X += rhs.m_X; lhs.m_Y += rhs.m_Y; return lhs; } + inline friend Vector& operator+=(Vector& lhs, const Vector& rhs) { + lhs.m_X += rhs.m_X; + lhs.m_Y += rhs.m_Y; + return lhs; + } /// /// Self-subtraction operator overload for Vectors. @@ -499,14 +566,22 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// A reference to the resulting Vector (the left one). - inline friend Vector & operator-=(Vector &lhs, const Vector &rhs) { lhs.m_X -= rhs.m_X; lhs.m_Y -= rhs.m_Y; return lhs; } + inline friend Vector& operator-=(Vector& lhs, const Vector& rhs) { + lhs.m_X -= rhs.m_X; + lhs.m_Y -= rhs.m_Y; + return lhs; + } /// /// Self-multiplication operator overload for a Vector and a float. /// /// A float reference as the right hand side operand. /// A reference to the resulting Vector. - inline Vector & operator*=(const float &rhs) { m_X *= rhs; m_Y *= rhs; return *this; } + inline Vector& operator*=(const float& rhs) { + m_X *= rhs; + m_Y *= rhs; + return *this; + } /// /// Self-multiplication operator overload for Vectors. @@ -514,14 +589,24 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// A reference to the resulting Vector (the left one). - inline friend Vector & operator*=(Vector &lhs, const Vector &rhs) { lhs.m_X *= rhs.m_X; lhs.m_Y *= rhs.m_Y; return lhs; } + inline friend Vector& operator*=(Vector& lhs, const Vector& rhs) { + lhs.m_X *= rhs.m_X; + lhs.m_Y *= rhs.m_Y; + return lhs; + } /// /// self-division operator overload for a Vector and a float. /// /// A float reference as the right hand side operand. /// A reference to the resulting Vector. - inline Vector & operator/=(const float &rhs) { if (rhs != 0) { m_X /= rhs; m_Y /= rhs; } return *this; } + inline Vector& operator/=(const float& rhs) { + if (rhs != 0) { + m_X /= rhs; + m_Y /= rhs; + } + return *this; + } /// /// Self-division operator overload for Vectors. @@ -529,26 +614,29 @@ namespace RTE { /// A Vector reference as the left hand side operand. /// A Vector reference as the right hand side operand. /// A reference to the resulting Vector (the left one). - inline friend Vector & operator/=(Vector &lhs, const Vector &rhs) { lhs.m_X /= rhs.m_X; lhs.m_Y /= rhs.m_Y; return lhs; } + inline friend Vector& operator/=(Vector& lhs, const Vector& rhs) { + lhs.m_X /= rhs.m_X; + lhs.m_Y /= rhs.m_Y; + return lhs; + } /// /// Array subscripting to access either the X or Y element of this Vector. /// /// An int index indicating which element is requested (X = 0, Y = 1). /// The requested element. - inline const float & operator[](const int &rhs) const { return (rhs == 0) ? m_X : m_Y; } + inline const float& operator[](const int& rhs) const { return (rhs == 0) ? m_X : m_Y; } /// /// Array subscripting to access either the X or Y element of this Vector. /// /// An int index indicating which element is requested (X = 0, Y = 1). /// The requested element. - inline float & operator[](const int &rhs) { return (rhs == 0) ? m_X : m_Y; } + inline float& operator[](const int& rhs) { return (rhs == 0) ? m_X : m_Y; } #pragma endregion private: - static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. }; -} +} // namespace RTE #endif diff --git a/Source/System/Writer.cpp b/Source/System/Writer.cpp index 81be0b5183..a37e9b18fc 100644 --- a/Source/System/Writer.cpp +++ b/Source/System/Writer.cpp @@ -5,7 +5,7 @@ namespace RTE { -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Writer::Clear() { m_Stream = nullptr; @@ -15,23 +15,23 @@ namespace RTE { m_IndentCount = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Writer::Writer(const std::string &fileName, bool append, bool createDir) { - Clear(); + Writer::Writer(const std::string& fileName, bool append, bool createDir) { + Clear(); Create(fileName, append, createDir); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Writer::Writer(std::unique_ptr &&stream) { + Writer::Writer(std::unique_ptr&& stream) { Clear(); Create(std::move(stream)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Writer::Create(const std::string &fileName, bool append, bool createDir) { + int Writer::Create(const std::string& fileName, bool append, bool createDir) { m_FilePath = fileName; // Extract filename and folder path @@ -39,8 +39,8 @@ namespace RTE { m_FileName = m_FilePath.substr(slashPos + 1); m_FolderPath = m_FilePath.substr(0, slashPos + 1); - if (createDir && !std::filesystem::exists(System::GetWorkingDirectory() + m_FolderPath)) { - System::MakeDirectory(System::GetWorkingDirectory() + m_FolderPath); + if (createDir && !std::filesystem::exists(System::GetWorkingDirectory() + m_FolderPath)) { + System::MakeDirectory(System::GetWorkingDirectory() + m_FolderPath); } auto ofStream = std::make_unique(fileName, append ? (std::ios::out | std::ios::app | std::ios::ate) : (std::ios::out | std::ios::trunc)); @@ -48,9 +48,9 @@ namespace RTE { return Create(std::move(ofStream)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Writer::Create(std::unique_ptr &&stream) { + int Writer::Create(std::unique_ptr&& stream) { m_Stream = std::move(stream); if (!m_Stream->good()) { return -1; @@ -59,12 +59,14 @@ namespace RTE { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Writer::NewLine(bool toIndent, int lineCount) const { for (int lines = 0; lines < lineCount; ++lines) { *m_Stream << "\n"; - if (toIndent) { *m_Stream << std::string(m_IndentCount, '\t'); } + if (toIndent) { + *m_Stream << std::string(m_IndentCount, '\t'); + } } } -} \ No newline at end of file +} // namespace RTE \ No newline at end of file diff --git a/Source/System/Writer.h b/Source/System/Writer.h index 07a6c4996e..ee56bf646a 100644 --- a/Source/System/Writer.h +++ b/Source/System/Writer.h @@ -9,7 +9,6 @@ namespace RTE { class Writer { public: - #pragma region Creation /// /// Constructor method used to instantiate a Writer object in system memory. Create() should be called before using the object. @@ -22,13 +21,13 @@ namespace RTE { /// Path to the file to open for writing. If the directory doesn't exist the stream will fail to open. /// Whether to append to the file if it exists, or to overwrite it. /// Whether to create the directory path to the file name before attempting to open the stream, in case it doesn't exist. - Writer(const std::string &fileName, bool append = false, bool createDir = false); + Writer(const std::string& fileName, bool append = false, bool createDir = false); /// /// Constructor method used to instantiate a Writer object in system memory and make it ready for writing to the passed in file path. /// /// Stream to write to. - Writer(std::unique_ptr &&stream); + Writer(std::unique_ptr&& stream); /// /// Makes the Writer object ready for use. @@ -37,14 +36,14 @@ namespace RTE { /// Whether to append to the file if it exists, or to overwrite it. /// Whether to create the directory path to the file name before attempting to open the stream, in case it doesn't exist. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const std::string &fileName, bool append = false, bool createDir = false); + int Create(const std::string& fileName, bool append = false, bool createDir = false); /// /// Makes the Writer object ready for use. /// /// Stream to write to. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(std::unique_ptr &&stream); + int Create(std::unique_ptr&& stream); #pragma endregion #pragma region Getters @@ -72,12 +71,20 @@ namespace RTE { /// Used to specify the start of an object to be written. /// /// The class name of the object about to be written. - void ObjectStart(const std::string &className) { *m_Stream << className; ++m_IndentCount; } + void ObjectStart(const std::string& className) { + *m_Stream << className; + ++m_IndentCount; + } /// /// Used to specify the end of an object that has just been written. /// - void ObjectEnd() { --m_IndentCount; if (m_IndentCount == 0) { NewLine(false, 2); } } + void ObjectEnd() { + --m_IndentCount; + if (m_IndentCount == 0) { + NewLine(false, 2); + } + } /// /// Creates a new line that can be properly indented. @@ -91,27 +98,39 @@ namespace RTE { /// /// The text string to write to the new line. /// Whether to indent the new line or not. - void NewLineString(const std::string &textString, bool toIndent = true) const { NewLine(toIndent); *m_Stream << textString; } + void NewLineString(const std::string& textString, bool toIndent = true) const { + NewLine(toIndent); + *m_Stream << textString; + } /// /// Creates a new line and fills it with slashes to create a divider line for INI. /// /// Whether to indent the new line or not. /// The length of the divider (number of slashes). - void NewDivider(bool toIndent = true, int dividerLength = 72) const { NewLine(toIndent); *m_Stream << std::string(dividerLength, '/'); } + void NewDivider(bool toIndent = true, int dividerLength = 72) const { + NewLine(toIndent); + *m_Stream << std::string(dividerLength, '/'); + } /// /// Creates a new line and writes the name of the property in preparation to writing it's value. /// /// The name of the property to be written. - void NewProperty(const std::string &propName) const { NewLine(); *m_Stream << propName + " = "; } + void NewProperty(const std::string& propName) const { + NewLine(); + *m_Stream << propName + " = "; + } /// /// Creates a new line and writes the name of the specified property, followed by its set value. /// /// The name of the property to be written. /// The value of the property. - template void NewPropertyWithValue(const std::string &propName, const Type &propValue) { NewProperty(propName); *this << propValue; } + template void NewPropertyWithValue(const std::string& propName, const Type& propValue) { + NewProperty(propName); + *this << propValue; + } /// /// Marks that there is a null reference to an object here. @@ -129,12 +148,15 @@ namespace RTE { /// /// Returns the underlying stream. /// - std::ostream * GetStream() { return m_Stream.get(); } + std::ostream* GetStream() { return m_Stream.get(); } /// /// Flushes and closes the output stream of this Writer. This happens automatically at destruction but needs to be called manually if a written file must be read from in the same scope. /// - void EndWrite() { m_Stream->flush(); m_Stream.reset(); } + void EndWrite() { + m_Stream->flush(); + m_Stream.reset(); + } #pragma endregion #pragma region Operator Overloads @@ -143,25 +165,70 @@ namespace RTE { /// /// A reference to the variable that will be written to the ostream. /// A Writer reference for further use in an expression. - Writer & operator<<(const bool &var) { *m_Stream << var; return *this; } - Writer & operator<<(const char &var) { *m_Stream << var; return *this; } - Writer & operator<<(const unsigned char &var) { int temp = var; *m_Stream << temp; return *this; } - Writer & operator<<(const short &var) { *m_Stream << var; return *this; } - Writer & operator<<(const unsigned short &var) { *m_Stream << var; return *this; } - Writer & operator<<(const int &var) { *m_Stream << var; return *this; } - Writer & operator<<(const unsigned int &var) { *m_Stream << var; return *this; } - Writer & operator<<(const long &var) { *m_Stream << var; return *this; } - Writer & operator<<(const long long &var) { *m_Stream << var; return *this; } - Writer & operator<<(const unsigned long &var) { *m_Stream << var; return *this; } - Writer & operator<<(const unsigned long long &var) { *m_Stream << var; return *this; } - Writer & operator<<(const float &var) { *m_Stream << var; return *this; } - Writer & operator<<(const double &var) { *m_Stream << var; return *this; } - Writer & operator<<(const char *var) { *m_Stream << var; return *this; } - Writer & operator<<(const std::string &var) { *m_Stream << var; return *this; } + Writer& operator<<(const bool& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const char& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const unsigned char& var) { + int temp = var; + *m_Stream << temp; + return *this; + } + Writer& operator<<(const short& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const unsigned short& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const int& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const unsigned int& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const long& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const long long& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const unsigned long& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const unsigned long long& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const float& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const double& var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const char* var) { + *m_Stream << var; + return *this; + } + Writer& operator<<(const std::string& var) { + *m_Stream << var; + return *this; + } #pragma endregion protected: - std::unique_ptr m_Stream; //!< Stream used for writing. std::string m_FilePath; //!< Currently used stream's filepath. std::string m_FolderPath; //!< Only the path to the folder that we are writing a file in, excluding the filename. @@ -169,15 +236,14 @@ namespace RTE { int m_IndentCount; //!< Indentation counter. private: - /// /// Clears all the member variables of this Writer, effectively resetting the members of this abstraction level only. /// void Clear(); // Disallow the use of some implicit methods. - Writer(const Writer &reference) = delete; - Writer & operator=(const Writer &rhs) = delete; + Writer(const Writer& reference) = delete; + Writer& operator=(const Writer& rhs) = delete; }; -} +} // namespace RTE #endif \ No newline at end of file From c0357ba959a0e2d21e919311c5b0d70a0f735345 Mon Sep 17 00:00:00 2001 From: HeliumAnt Date: Sat, 20 Jan 2024 22:53:49 +0100 Subject: [PATCH 14/24] fix eof newlines --- Source/Activities/ActorEditor.cpp | 2 +- Source/Activities/ActorEditor.h | 2 +- Source/Activities/AreaEditor.cpp | 2 +- Source/Activities/AreaEditor.h | 2 +- Source/Activities/AssemblyEditor.cpp | 2 +- Source/Activities/AssemblyEditor.h | 2 +- Source/Activities/BaseEditor.cpp | 2 +- Source/Activities/BaseEditor.h | 2 +- Source/Activities/EditorActivity.cpp | 2 +- Source/Activities/EditorActivity.h | 2 +- Source/Activities/GAScripted.cpp | 2 +- Source/Activities/GAScripted.h | 2 +- Source/Activities/GATutorial.h | 2 +- Source/Activities/GibEditor.cpp | 2 +- Source/Activities/GibEditor.h | 2 +- Source/Activities/MultiplayerServerLobby.cpp | 2 +- Source/Activities/SceneEditor.cpp | 2 +- Source/Activities/SceneEditor.h | 2 +- Source/Entities/ACDropShip.h | 2 +- Source/Entities/ACRocket.h | 2 +- Source/Entities/ACraft.cpp | 2 +- Source/Entities/ADSensor.cpp | 2 +- Source/Entities/ADSensor.h | 2 +- Source/Entities/ADoor.cpp | 2 +- Source/Entities/ADoor.h | 2 +- Source/Entities/AEJetpack.cpp | 2 +- Source/Entities/AEJetpack.h | 2 +- Source/Entities/AEmitter.cpp | 2 +- Source/Entities/AEmitter.h | 2 +- Source/Entities/Activity.h | 2 +- Source/Entities/Actor.h | 2 +- Source/Entities/Arm.cpp | 2 +- Source/Entities/Arm.h | 2 +- Source/Entities/AtomGroup.h | 2 +- Source/Entities/Attachable.cpp | 2 +- Source/Entities/Attachable.h | 2 +- Source/Entities/BunkerAssembly.h | 2 +- Source/Entities/BunkerAssemblyScheme.h | 2 +- Source/Entities/Deployment.h | 2 +- Source/Entities/Emission.cpp | 2 +- Source/Entities/Emission.h | 2 +- Source/Entities/Gib.cpp | 2 +- Source/Entities/Gib.h | 2 +- Source/Entities/GlobalScript.cpp | 2 +- Source/Entities/GlobalScript.h | 2 +- Source/Entities/HDFirearm.h | 2 +- Source/Entities/HeldDevice.h | 2 +- Source/Entities/Icon.cpp | 2 +- Source/Entities/Icon.h | 2 +- Source/Entities/Leg.cpp | 2 +- Source/Entities/Leg.h | 2 +- Source/Entities/LimbPath.cpp | 2 +- Source/Entities/LimbPath.h | 2 +- Source/Entities/Loadout.cpp | 2 +- Source/Entities/Loadout.h | 2 +- Source/Entities/MOPixel.h | 2 +- Source/Entities/MOSParticle.h | 2 +- Source/Entities/MOSRotating.h | 2 +- Source/Entities/MOSprite.cpp | 2 +- Source/Entities/MOSprite.h | 2 +- Source/Entities/Magazine.h | 2 +- Source/Entities/MetaPlayer.cpp | 2 +- Source/Entities/MetaPlayer.h | 2 +- Source/Entities/MetaSave.cpp | 2 +- Source/Entities/MetaSave.h | 2 +- Source/Entities/MovableObject.cpp | 2 +- Source/Entities/MovableObject.h | 2 +- Source/Entities/PEmitter.cpp | 2 +- Source/Entities/PEmitter.h | 2 +- Source/Entities/PieSlice.cpp | 2 +- Source/Entities/PieSlice.h | 2 +- Source/Entities/Round.cpp | 2 +- Source/Entities/Round.h | 2 +- Source/Entities/SLBackground.cpp | 2 +- Source/Entities/SLBackground.h | 2 +- Source/Entities/SLTerrain.cpp | 2 +- Source/Entities/SLTerrain.h | 2 +- Source/Entities/Scene.h | 2 +- Source/Entities/SceneLayer.cpp | 2 +- Source/Entities/SceneLayer.h | 2 +- Source/Entities/SoundSet.h | 2 +- Source/Entities/TDExplosive.cpp | 2 +- Source/Entities/TDExplosive.h | 2 +- Source/Entities/TerrainDebris.cpp | 2 +- Source/Entities/TerrainDebris.h | 2 +- Source/Entities/TerrainFrosting.cpp | 2 +- Source/Entities/TerrainFrosting.h | 2 +- Source/Entities/TerrainObject.cpp | 2 +- Source/Entities/TerrainObject.h | 2 +- Source/Entities/ThrownDevice.cpp | 2 +- Source/Entities/ThrownDevice.h | 2 +- Source/Entities/Turret.cpp | 2 +- Source/Entities/Turret.h | 2 +- Source/GUI/GUI.h | 2 +- Source/GUI/GUIBanner.h | 2 +- Source/GUI/GUIButton.cpp | 2 +- Source/GUI/GUIButton.h | 2 +- Source/GUI/GUICheckbox.cpp | 2 +- Source/GUI/GUICheckbox.h | 2 +- Source/GUI/GUICollectionBox.cpp | 2 +- Source/GUI/GUICollectionBox.h | 2 +- Source/GUI/GUIComboBox.cpp | 2 +- Source/GUI/GUIComboBox.h | 2 +- Source/GUI/GUIControl.h | 2 +- Source/GUI/GUIControlFactory.h | 2 +- Source/GUI/GUIControlManager.cpp | 2 +- Source/GUI/GUIControlManager.h | 2 +- Source/GUI/GUIEvent.h | 2 +- Source/GUI/GUIFont.cpp | 2 +- Source/GUI/GUIFont.h | 2 +- Source/GUI/GUIInterface.h | 2 +- Source/GUI/GUILabel.h | 2 +- Source/GUI/GUIListBox.cpp | 2 +- Source/GUI/GUIListBox.h | 2 +- Source/GUI/GUIListPanel.h | 2 +- Source/GUI/GUIManager.h | 2 +- Source/GUI/GUIProgressBar.cpp | 2 +- Source/GUI/GUIProgressBar.h | 2 +- Source/GUI/GUIProperties.h | 2 +- Source/GUI/GUIPropertyPage.h | 2 +- Source/GUI/GUIRadioButton.cpp | 2 +- Source/GUI/GUIRadioButton.h | 2 +- Source/GUI/GUIScrollPanel.cpp | 2 +- Source/GUI/GUIScrollPanel.h | 2 +- Source/GUI/GUIScrollbar.cpp | 2 +- Source/GUI/GUIScrollbar.h | 2 +- Source/GUI/GUISkin.cpp | 2 +- Source/GUI/GUISkin.h | 2 +- Source/GUI/GUISlider.cpp | 2 +- Source/GUI/GUISlider.h | 2 +- Source/GUI/GUISound.h | 2 +- Source/GUI/GUITab.cpp | 2 +- Source/GUI/GUITab.h | 2 +- Source/GUI/GUITextBox.cpp | 2 +- Source/GUI/GUITextBox.h | 2 +- Source/GUI/GUIUtil.h | 2 +- Source/GUI/GUIWriter.cpp | 2 +- Source/GUI/GUIWriter.h | 2 +- Source/GUI/Wrappers/AllegroBitmap.cpp | 2 +- Source/GUI/Wrappers/AllegroBitmap.h | 2 +- Source/GUI/Wrappers/AllegroScreen.cpp | 2 +- Source/GUI/Wrappers/AllegroScreen.h | 2 +- Source/GUI/Wrappers/GUIInputWrapper.cpp | 2 +- Source/Lua/LuaAdapters.cpp | 2 +- Source/Lua/LuaBindingsActivities.cpp | 2 +- Source/Lua/LuaBindingsGUI.cpp | 2 +- Source/Lua/LuaBindingsInput.cpp | 2 +- Source/Lua/LuaBindingsManagers.cpp | 2 +- Source/Lua/LuaBindingsMisc.cpp | 2 +- Source/Lua/LuaBindingsPrimitives.cpp | 2 +- Source/Lua/LuaBindingsSystem.cpp | 2 +- Source/Lua/LuabindObjectWrapper.cpp | 2 +- Source/Lua/LuabindObjectWrapper.h | 2 +- Source/Main.cpp | 2 +- Source/Managers/ActivityMan.h | 2 +- Source/Managers/CameraMan.cpp | 2 +- Source/Managers/CameraMan.h | 2 +- Source/Managers/ConsoleMan.h | 2 +- Source/Managers/FrameMan.cpp | 2 +- Source/Managers/MenuMan.h | 2 +- Source/Managers/MetaMan.cpp | 2 +- Source/Managers/MetaMan.h | 2 +- Source/Managers/MovableMan.h | 2 +- Source/Managers/PerformanceMan.cpp | 2 +- Source/Managers/PerformanceMan.h | 2 +- Source/Managers/PostProcessMan.h | 2 +- Source/Managers/PresetMan.h | 2 +- Source/Managers/PrimitiveMan.cpp | 2 +- Source/Managers/PrimitiveMan.h | 2 +- Source/Managers/SceneMan.cpp | 2 +- Source/Managers/ThreadMan.cpp | 2 +- Source/Managers/UInputMan.cpp | 2 +- Source/Managers/UInputMan.h | 2 +- Source/Managers/WindowMan.cpp | 2 +- Source/Managers/WindowMan.h | 2 +- Source/Menus/AreaEditorGUI.cpp | 2 +- Source/Menus/AreaEditorGUI.h | 2 +- Source/Menus/AreaPickerGUI.cpp | 2 +- Source/Menus/AreaPickerGUI.h | 2 +- Source/Menus/AssemblyEditorGUI.cpp | 2 +- Source/Menus/AssemblyEditorGUI.h | 2 +- Source/Menus/GibEditorGUI.cpp | 2 +- Source/Menus/GibEditorGUI.h | 2 +- Source/Menus/InventoryMenuGUI.cpp | 2 +- Source/Menus/LoadingScreen.h | 2 +- Source/Menus/MainMenuGUI.h | 2 +- Source/Menus/MetagameGUI.h | 2 +- Source/Menus/ModManagerGUI.cpp | 2 +- Source/Menus/ModManagerGUI.h | 2 +- Source/Menus/ObjectPickerGUI.h | 2 +- Source/Menus/PauseMenuGUI.cpp | 2 +- Source/Menus/PauseMenuGUI.h | 2 +- Source/Menus/SaveLoadMenuGUI.cpp | 2 +- Source/Menus/SaveLoadMenuGUI.h | 2 +- Source/Menus/ScenarioActivityConfigGUI.h | 2 +- Source/Menus/ScenarioGUI.h | 2 +- Source/Menus/SceneEditorGUI.cpp | 2 +- Source/Menus/SceneEditorGUI.h | 2 +- Source/Menus/SettingsAudioGUI.cpp | 2 +- Source/Menus/SettingsAudioGUI.h | 2 +- Source/Menus/SettingsGUI.cpp | 2 +- Source/Menus/SettingsGUI.h | 2 +- Source/Menus/SettingsGameplayGUI.cpp | 2 +- Source/Menus/SettingsGameplayGUI.h | 2 +- Source/Menus/SettingsInputGUI.cpp | 2 +- Source/Menus/SettingsInputGUI.h | 2 +- Source/Menus/SettingsInputMappingGUI.h | 2 +- Source/Menus/SettingsInputMappingWizardGUI.h | 2 +- Source/Menus/SettingsMiscGUI.cpp | 2 +- Source/Menus/SettingsMiscGUI.h | 2 +- Source/Menus/SettingsVideoGUI.cpp | 2 +- Source/Menus/TitleScreen.cpp | 2 +- Source/Menus/TitleScreen.h | 2 +- Source/System/AllegroTools.cpp | 2 +- Source/System/AllegroTools.h | 2 +- Source/System/Atom.cpp | 2 +- Source/System/Atom.h | 2 +- Source/System/Box.cpp | 2 +- Source/System/Box.h | 2 +- Source/System/Color.cpp | 2 +- Source/System/Color.h | 2 +- Source/System/ContentFile.cpp | 2 +- Source/System/Controller.h | 2 +- Source/System/DataModule.cpp | 2 +- Source/System/DataModule.h | 2 +- Source/System/Entity.cpp | 2 +- Source/System/Entity.h | 2 +- Source/System/GameVersion.h | 2 +- Source/System/Gamepad.h | 2 +- Source/System/GenericSavedData.cpp | 2 +- Source/System/GenericSavedData.h | 2 +- Source/System/GraphicalPrimitive.cpp | 2 +- Source/System/GraphicalPrimitive.h | 2 +- Source/System/InputScheme.h | 2 +- Source/System/Matrix.cpp | 2 +- Source/System/Matrix.h | 2 +- Source/System/NetworkMessages.h | 2 +- Source/System/PathFinder.h | 2 +- Source/System/PieQuadrant.cpp | 2 +- Source/System/PieQuadrant.h | 2 +- Source/System/RTEError.cpp | 2 +- Source/System/RTEError.h | 2 +- Source/System/RTEStackTrace.cpp | 2 +- Source/System/RTEStackTrace.h | 2 +- Source/System/RTETools.h | 2 +- Source/System/Serializable.cpp | 2 +- Source/System/Serializable.h | 2 +- Source/System/Singleton.h | 2 +- Source/System/SpatialPartitionGrid.cpp | 2 +- Source/System/SpatialPartitionGrid.h | 2 +- Source/System/Timer.cpp | 2 +- Source/System/Timer.h | 2 +- Source/System/Writer.cpp | 2 +- Source/System/Writer.h | 2 +- 254 files changed, 254 insertions(+), 254 deletions(-) diff --git a/Source/Activities/ActorEditor.cpp b/Source/Activities/ActorEditor.cpp index 5d8cbe9f92..1318a7e5e2 100644 --- a/Source/Activities/ActorEditor.cpp +++ b/Source/Activities/ActorEditor.cpp @@ -326,4 +326,4 @@ namespace RTE { return false; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Activities/ActorEditor.h b/Source/Activities/ActorEditor.h index 54ef3e366a..676276ab8d 100644 --- a/Source/Activities/ActorEditor.h +++ b/Source/Activities/ActorEditor.h @@ -236,4 +236,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Activities/AreaEditor.cpp b/Source/Activities/AreaEditor.cpp index 05e736af0a..5a9d87edff 100644 --- a/Source/Activities/AreaEditor.cpp +++ b/Source/Activities/AreaEditor.cpp @@ -669,4 +669,4 @@ namespace RTE { m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Activities/AreaEditor.h b/Source/Activities/AreaEditor.h index 79e82788a4..26378b247d 100644 --- a/Source/Activities/AreaEditor.h +++ b/Source/Activities/AreaEditor.h @@ -272,4 +272,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Activities/AssemblyEditor.cpp b/Source/Activities/AssemblyEditor.cpp index 36c92622a7..20677ef294 100644 --- a/Source/Activities/AssemblyEditor.cpp +++ b/Source/Activities/AssemblyEditor.cpp @@ -716,4 +716,4 @@ namespace RTE { m_pOverwriteNameLabel->SetText(m_pEditorGUI->GetCurrentAssemblyName()); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Activities/AssemblyEditor.h b/Source/Activities/AssemblyEditor.h index 5630e9c5b0..37e714b7c4 100644 --- a/Source/Activities/AssemblyEditor.h +++ b/Source/Activities/AssemblyEditor.h @@ -281,4 +281,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Activities/BaseEditor.cpp b/Source/Activities/BaseEditor.cpp index c4dd83c902..b79e658168 100644 --- a/Source/Activities/BaseEditor.cpp +++ b/Source/Activities/BaseEditor.cpp @@ -361,4 +361,4 @@ namespace RTE { return false; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Activities/BaseEditor.h b/Source/Activities/BaseEditor.h index d481788caa..555004ab79 100644 --- a/Source/Activities/BaseEditor.h +++ b/Source/Activities/BaseEditor.h @@ -213,4 +213,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Activities/EditorActivity.cpp b/Source/Activities/EditorActivity.cpp index cc78f6c319..682b46899b 100644 --- a/Source/Activities/EditorActivity.cpp +++ b/Source/Activities/EditorActivity.cpp @@ -578,4 +578,4 @@ namespace RTE { void EditorActivity::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Activities/EditorActivity.h b/Source/Activities/EditorActivity.h index 72781783c3..aaf129e3ad 100644 --- a/Source/Activities/EditorActivity.h +++ b/Source/Activities/EditorActivity.h @@ -339,4 +339,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Activities/GAScripted.cpp b/Source/Activities/GAScripted.cpp index ab1a7e1f99..ece23f7c4a 100644 --- a/Source/Activities/GAScripted.cpp +++ b/Source/Activities/GAScripted.cpp @@ -565,4 +565,4 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Activities/GAScripted.h b/Source/Activities/GAScripted.h index 5304e70897..080dc6e170 100644 --- a/Source/Activities/GAScripted.h +++ b/Source/Activities/GAScripted.h @@ -304,4 +304,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Activities/GATutorial.h b/Source/Activities/GATutorial.h index 7c62db00ed..f5379470a4 100644 --- a/Source/Activities/GATutorial.h +++ b/Source/Activities/GATutorial.h @@ -311,4 +311,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Activities/GibEditor.cpp b/Source/Activities/GibEditor.cpp index f4c8cd9469..14942cd4cc 100644 --- a/Source/Activities/GibEditor.cpp +++ b/Source/Activities/GibEditor.cpp @@ -843,4 +843,4 @@ namespace RTE { clear_bitmap(g_SceneMan.GetTerrain()->GetBGColorBitmap()); clear_bitmap(g_SceneMan.GetTerrain()->GetMaterialBitmap()); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Activities/GibEditor.h b/Source/Activities/GibEditor.h index a559548273..757f3bb0f4 100644 --- a/Source/Activities/GibEditor.h +++ b/Source/Activities/GibEditor.h @@ -291,4 +291,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Activities/MultiplayerServerLobby.cpp b/Source/Activities/MultiplayerServerLobby.cpp index 4188217955..670beaa6e3 100644 --- a/Source/Activities/MultiplayerServerLobby.cpp +++ b/Source/Activities/MultiplayerServerLobby.cpp @@ -1248,4 +1248,4 @@ namespace RTE { Activity::Draw(pTargetBitmap, targetPos); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Activities/SceneEditor.cpp b/Source/Activities/SceneEditor.cpp index aa26146992..70737d6454 100644 --- a/Source/Activities/SceneEditor.cpp +++ b/Source/Activities/SceneEditor.cpp @@ -787,4 +787,4 @@ namespace RTE { m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes/" + g_SceneMan.GetScene()->GetPresetName()); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Activities/SceneEditor.h b/Source/Activities/SceneEditor.h index b9e1cb84d5..784628a8cd 100644 --- a/Source/Activities/SceneEditor.h +++ b/Source/Activities/SceneEditor.h @@ -277,4 +277,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/ACDropShip.h b/Source/Entities/ACDropShip.h index 5ee6747b27..3324024498 100644 --- a/Source/Entities/ACDropShip.h +++ b/Source/Entities/ACDropShip.h @@ -336,4 +336,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/ACRocket.h b/Source/Entities/ACRocket.h index 3c05a5399b..9434db876a 100644 --- a/Source/Entities/ACRocket.h +++ b/Source/Entities/ACRocket.h @@ -311,4 +311,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/ACraft.cpp b/Source/Entities/ACraft.cpp index 092b7b0c32..4d7754271e 100644 --- a/Source/Entities/ACraft.cpp +++ b/Source/Entities/ACraft.cpp @@ -1018,4 +1018,4 @@ namespace RTE { } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/ADSensor.cpp b/Source/Entities/ADSensor.cpp index d11b802f75..f20266864b 100644 --- a/Source/Entities/ADSensor.cpp +++ b/Source/Entities/ADSensor.cpp @@ -70,4 +70,4 @@ namespace RTE { } return sensedActor; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/ADSensor.h b/Source/Entities/ADSensor.h index a92c1207c6..579592081d 100644 --- a/Source/Entities/ADSensor.h +++ b/Source/Entities/ADSensor.h @@ -103,4 +103,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/ADoor.cpp b/Source/Entities/ADoor.cpp index 57830895bc..beb9d20b72 100644 --- a/Source/Entities/ADoor.cpp +++ b/Source/Entities/ADoor.cpp @@ -593,4 +593,4 @@ namespace RTE { return; } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/ADoor.h b/Source/Entities/ADoor.h index fe1c4f21b3..3fc5fac83e 100644 --- a/Source/Entities/ADoor.h +++ b/Source/Entities/ADoor.h @@ -300,4 +300,4 @@ namespace RTE { ADoor& operator=(const ADoor& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/AEJetpack.cpp b/Source/Entities/AEJetpack.cpp index a64890c8f5..d66fcf09c9 100644 --- a/Source/Entities/AEJetpack.cpp +++ b/Source/Entities/AEJetpack.cpp @@ -228,4 +228,4 @@ namespace RTE { m_JetTimeLeft += g_TimerMan.GetDeltaTimeMS() * m_JetReplenishRate; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/AEJetpack.h b/Source/Entities/AEJetpack.h index 722704fa7e..305b4191f8 100644 --- a/Source/Entities/AEJetpack.h +++ b/Source/Entities/AEJetpack.h @@ -221,4 +221,4 @@ namespace RTE { }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/AEmitter.cpp b/Source/Entities/AEmitter.cpp index d4c018c221..3b50228d34 100644 --- a/Source/Entities/AEmitter.cpp +++ b/Source/Entities/AEmitter.cpp @@ -634,4 +634,4 @@ namespace RTE { m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/AEmitter.h b/Source/Entities/AEmitter.h index 2b37b7921d..5459480983 100644 --- a/Source/Entities/AEmitter.h +++ b/Source/Entities/AEmitter.h @@ -728,4 +728,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/Activity.h b/Source/Entities/Activity.h index 9cf3a80c54..8d58759c9a 100644 --- a/Source/Entities/Activity.h +++ b/Source/Entities/Activity.h @@ -842,4 +842,4 @@ namespace RTE { Activity& operator=(const Activity& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/Actor.h b/Source/Entities/Actor.h index 33c70e0e7f..e12b276318 100644 --- a/Source/Entities/Actor.h +++ b/Source/Entities/Actor.h @@ -1613,4 +1613,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/Arm.cpp b/Source/Entities/Arm.cpp index e1c6ebf32e..c8170a1923 100644 --- a/Source/Entities/Arm.cpp +++ b/Source/Entities/Arm.cpp @@ -400,4 +400,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/Arm.h b/Source/Entities/Arm.h index ea82eeb528..033b6f6dff 100644 --- a/Source/Entities/Arm.h +++ b/Source/Entities/Arm.h @@ -377,4 +377,4 @@ namespace RTE { Arm& operator=(const Arm& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/AtomGroup.h b/Source/Entities/AtomGroup.h index c9328d79b9..564d980fb1 100644 --- a/Source/Entities/AtomGroup.h +++ b/Source/Entities/AtomGroup.h @@ -456,4 +456,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/Attachable.cpp b/Source/Entities/Attachable.cpp index 9f43c75f00..c86309246b 100644 --- a/Source/Entities/Attachable.cpp +++ b/Source/Entities/Attachable.cpp @@ -641,4 +641,4 @@ namespace RTE { wound->AddOrRemovePieSlicesAndListenersFromPieMenu(pieMenuToModify, addToPieMenu); } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/Attachable.h b/Source/Entities/Attachable.h index 2b5358e1e4..141265e425 100644 --- a/Source/Entities/Attachable.h +++ b/Source/Entities/Attachable.h @@ -645,4 +645,4 @@ namespace RTE { Attachable& operator=(const Attachable& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/BunkerAssembly.h b/Source/Entities/BunkerAssembly.h index 36852545c4..cf47b9340a 100644 --- a/Source/Entities/BunkerAssembly.h +++ b/Source/Entities/BunkerAssembly.h @@ -252,4 +252,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/BunkerAssemblyScheme.h b/Source/Entities/BunkerAssemblyScheme.h index ea3b56ab01..ff0cbbed4d 100644 --- a/Source/Entities/BunkerAssemblyScheme.h +++ b/Source/Entities/BunkerAssemblyScheme.h @@ -310,4 +310,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/Deployment.h b/Source/Entities/Deployment.h index 350b70303d..1a524501fc 100644 --- a/Source/Entities/Deployment.h +++ b/Source/Entities/Deployment.h @@ -372,4 +372,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/Emission.cpp b/Source/Entities/Emission.cpp index 817ef6cfef..724bef4fe0 100644 --- a/Source/Entities/Emission.cpp +++ b/Source/Entities/Emission.cpp @@ -160,4 +160,4 @@ namespace RTE { return 0; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/Emission.h b/Source/Entities/Emission.h index 9a7b73ce2b..52bc01f3c1 100644 --- a/Source/Entities/Emission.h +++ b/Source/Entities/Emission.h @@ -310,4 +310,4 @@ namespace RTE { }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/Gib.cpp b/Source/Entities/Gib.cpp index e24a03925b..c136976780 100644 --- a/Source/Entities/Gib.cpp +++ b/Source/Entities/Gib.cpp @@ -93,4 +93,4 @@ namespace RTE { return 0; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/Gib.h b/Source/Entities/Gib.h index 0bc56615cd..5051479235 100644 --- a/Source/Entities/Gib.h +++ b/Source/Entities/Gib.h @@ -161,4 +161,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/GlobalScript.cpp b/Source/Entities/GlobalScript.cpp index 60521a9be0..d2c9175ea7 100644 --- a/Source/Entities/GlobalScript.cpp +++ b/Source/Entities/GlobalScript.cpp @@ -182,4 +182,4 @@ namespace RTE { m_IsActive = false; } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/GlobalScript.h b/Source/Entities/GlobalScript.h index c0876cf329..d3a43db7c5 100644 --- a/Source/Entities/GlobalScript.h +++ b/Source/Entities/GlobalScript.h @@ -149,4 +149,4 @@ namespace RTE { GlobalScript& operator=(const GlobalScript& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/HDFirearm.h b/Source/Entities/HDFirearm.h index 0c45c64837..127a60b288 100644 --- a/Source/Entities/HDFirearm.h +++ b/Source/Entities/HDFirearm.h @@ -995,4 +995,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/HeldDevice.h b/Source/Entities/HeldDevice.h index bae9996d25..4312b9f812 100644 --- a/Source/Entities/HeldDevice.h +++ b/Source/Entities/HeldDevice.h @@ -683,4 +683,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/Icon.cpp b/Source/Entities/Icon.cpp index 3253c8a6ff..466884be63 100644 --- a/Source/Entities/Icon.cpp +++ b/Source/Entities/Icon.cpp @@ -71,4 +71,4 @@ namespace RTE { } Clear(); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/Icon.h b/Source/Entities/Icon.h index 8933377757..364bf73b1f 100644 --- a/Source/Entities/Icon.h +++ b/Source/Entities/Icon.h @@ -119,4 +119,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/Leg.cpp b/Source/Entities/Leg.cpp index 8ca6b5da81..370a2a46e0 100644 --- a/Source/Entities/Leg.cpp +++ b/Source/Entities/Leg.cpp @@ -286,4 +286,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/Leg.h b/Source/Entities/Leg.h index c459aa5146..7d2ab1dd7b 100644 --- a/Source/Entities/Leg.h +++ b/Source/Entities/Leg.h @@ -179,4 +179,4 @@ namespace RTE { Leg& operator=(const Leg& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/LimbPath.cpp b/Source/Entities/LimbPath.cpp index 2214338000..1ab6b095e6 100644 --- a/Source/Entities/LimbPath.cpp +++ b/Source/Entities/LimbPath.cpp @@ -643,4 +643,4 @@ namespace RTE { } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/LimbPath.h b/Source/Entities/LimbPath.h index c5dc9bcd72..b43409336f 100644 --- a/Source/Entities/LimbPath.h +++ b/Source/Entities/LimbPath.h @@ -699,4 +699,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/Loadout.cpp b/Source/Entities/Loadout.cpp index 6833f5ed9b..18604cc71e 100644 --- a/Source/Entities/Loadout.cpp +++ b/Source/Entities/Loadout.cpp @@ -281,4 +281,4 @@ namespace RTE { return pReturnObject; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/Loadout.h b/Source/Entities/Loadout.h index 5d9189e0c2..4d759dd16f 100644 --- a/Source/Entities/Loadout.h +++ b/Source/Entities/Loadout.h @@ -211,4 +211,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/MOPixel.h b/Source/Entities/MOPixel.h index 0a9c4da548..2e8d7c6c6f 100644 --- a/Source/Entities/MOPixel.h +++ b/Source/Entities/MOPixel.h @@ -238,4 +238,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/MOSParticle.h b/Source/Entities/MOSParticle.h index 590c9024ca..703feeb567 100644 --- a/Source/Entities/MOSParticle.h +++ b/Source/Entities/MOSParticle.h @@ -169,4 +169,4 @@ namespace RTE { MOSParticle& operator=(const MOSParticle& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/MOSRotating.h b/Source/Entities/MOSRotating.h index 4975247826..eb900ae0e6 100644 --- a/Source/Entities/MOSRotating.h +++ b/Source/Entities/MOSRotating.h @@ -941,4 +941,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/MOSprite.cpp b/Source/Entities/MOSprite.cpp index 32b6f02e5d..4f5862373e 100644 --- a/Source/Entities/MOSprite.cpp +++ b/Source/Entities/MOSprite.cpp @@ -583,4 +583,4 @@ namespace RTE { } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/MOSprite.h b/Source/Entities/MOSprite.h index adf79d404f..7065f185ad 100644 --- a/Source/Entities/MOSprite.h +++ b/Source/Entities/MOSprite.h @@ -589,4 +589,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/Magazine.h b/Source/Entities/Magazine.h index 0ec35c0393..07e3362686 100644 --- a/Source/Entities/Magazine.h +++ b/Source/Entities/Magazine.h @@ -301,4 +301,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/MetaPlayer.cpp b/Source/Entities/MetaPlayer.cpp index f0d467fa39..9213499cb5 100644 --- a/Source/Entities/MetaPlayer.cpp +++ b/Source/Entities/MetaPlayer.cpp @@ -122,4 +122,4 @@ namespace RTE { return 0; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/MetaPlayer.h b/Source/Entities/MetaPlayer.h index 794f8ab0a5..54f7d6f110 100644 --- a/Source/Entities/MetaPlayer.h +++ b/Source/Entities/MetaPlayer.h @@ -316,4 +316,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/MetaSave.cpp b/Source/Entities/MetaSave.cpp index c812894e51..241b246070 100644 --- a/Source/Entities/MetaSave.cpp +++ b/Source/Entities/MetaSave.cpp @@ -83,4 +83,4 @@ namespace RTE { return 0; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/MetaSave.h b/Source/Entities/MetaSave.h index a554a8e0ac..02b03f97b8 100644 --- a/Source/Entities/MetaSave.h +++ b/Source/Entities/MetaSave.h @@ -106,4 +106,4 @@ namespace RTE { MetaSave& operator=(const MetaSave& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/MovableObject.cpp b/Source/Entities/MovableObject.cpp index 35d49eb81f..642f381372 100644 --- a/Source/Entities/MovableObject.cpp +++ b/Source/Entities/MovableObject.cpp @@ -1288,4 +1288,4 @@ namespace RTE { return true; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/MovableObject.h b/Source/Entities/MovableObject.h index 201093b20e..bb4053e201 100644 --- a/Source/Entities/MovableObject.h +++ b/Source/Entities/MovableObject.h @@ -2111,4 +2111,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/PEmitter.cpp b/Source/Entities/PEmitter.cpp index 574aea1ce9..81bcc656a3 100644 --- a/Source/Entities/PEmitter.cpp +++ b/Source/Entities/PEmitter.cpp @@ -487,4 +487,4 @@ namespace RTE { MOSParticle::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/PEmitter.h b/Source/Entities/PEmitter.h index 74dea7b259..8395f77cb7 100644 --- a/Source/Entities/PEmitter.h +++ b/Source/Entities/PEmitter.h @@ -571,4 +571,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/PieSlice.cpp b/Source/Entities/PieSlice.cpp index 4afa3a38fa..9129333743 100644 --- a/Source/Entities/PieSlice.cpp +++ b/Source/Entities/PieSlice.cpp @@ -204,4 +204,4 @@ namespace RTE { void PieSlice::PieMenuCustomDeleter::operator()(PieMenu* pieMenu) const { pieMenu->Destroy(true); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/PieSlice.h b/Source/Entities/PieSlice.h index bc3316834f..e328227642 100644 --- a/Source/Entities/PieSlice.h +++ b/Source/Entities/PieSlice.h @@ -339,4 +339,4 @@ namespace RTE { PieSlice& operator=(const PieSlice& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/Round.cpp b/Source/Entities/Round.cpp index 0d457a1ef9..05f810a353 100644 --- a/Source/Entities/Round.cpp +++ b/Source/Entities/Round.cpp @@ -132,4 +132,4 @@ namespace RTE { m_ParticleCount--; return tempParticle; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/Round.h b/Source/Entities/Round.h index fc294d5b3a..694044f0b6 100644 --- a/Source/Entities/Round.h +++ b/Source/Entities/Round.h @@ -187,4 +187,4 @@ namespace RTE { Round& operator=(const Round& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/SLBackground.cpp b/Source/Entities/SLBackground.cpp index a52b72426c..680e6b7ee4 100644 --- a/Source/Entities/SLBackground.cpp +++ b/Source/Entities/SLBackground.cpp @@ -247,4 +247,4 @@ namespace RTE { } set_clip_rect(targetBitmap, 0, 0, targetBitmap->w - 1, targetBitmap->h - 1); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/SLBackground.h b/Source/Entities/SLBackground.h index e9a4e22820..c368521edd 100644 --- a/Source/Entities/SLBackground.h +++ b/Source/Entities/SLBackground.h @@ -254,4 +254,4 @@ namespace RTE { SLBackground& operator=(const SLBackground& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/SLTerrain.cpp b/Source/Entities/SLTerrain.cpp index 3d48367f96..6bc50fe73b 100644 --- a/Source/Entities/SLTerrain.cpp +++ b/Source/Entities/SLTerrain.cpp @@ -516,4 +516,4 @@ namespace RTE { break; } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/SLTerrain.h b/Source/Entities/SLTerrain.h index 95b60f33e9..a1ff35a368 100644 --- a/Source/Entities/SLTerrain.h +++ b/Source/Entities/SLTerrain.h @@ -295,4 +295,4 @@ namespace RTE { SLTerrain& operator=(const SLTerrain& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/Scene.h b/Source/Entities/Scene.h index e2bdc348b0..d688344a4c 100644 --- a/Source/Entities/Scene.h +++ b/Source/Entities/Scene.h @@ -1373,4 +1373,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Entities/SceneLayer.cpp b/Source/Entities/SceneLayer.cpp index 18ea2c29de..1e14f1e133 100644 --- a/Source/Entities/SceneLayer.cpp +++ b/Source/Entities/SceneLayer.cpp @@ -601,4 +601,4 @@ namespace RTE { // Force instantiation template class SceneLayerImpl; template class SceneLayerImpl; -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/SceneLayer.h b/Source/Entities/SceneLayer.h index 018f699ecc..ba5952097b 100644 --- a/Source/Entities/SceneLayer.h +++ b/Source/Entities/SceneLayer.h @@ -405,4 +405,4 @@ namespace RTE { static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/SoundSet.h b/Source/Entities/SoundSet.h index 0ceb915b9c..f39f1a7e76 100644 --- a/Source/Entities/SoundSet.h +++ b/Source/Entities/SoundSet.h @@ -224,4 +224,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/TDExplosive.cpp b/Source/Entities/TDExplosive.cpp index 3920ae4fad..99a59f98e8 100644 --- a/Source/Entities/TDExplosive.cpp +++ b/Source/Entities/TDExplosive.cpp @@ -86,4 +86,4 @@ namespace RTE { ThrownDevice::DrawHUD(targetBitmap, targetPos, whichScreen); } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/TDExplosive.h b/Source/Entities/TDExplosive.h index cba784c83d..bdc15d1e53 100644 --- a/Source/Entities/TDExplosive.h +++ b/Source/Entities/TDExplosive.h @@ -108,4 +108,4 @@ namespace RTE { }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/TerrainDebris.cpp b/Source/Entities/TerrainDebris.cpp index d9307888b4..5624997d9b 100644 --- a/Source/Entities/TerrainDebris.cpp +++ b/Source/Entities/TerrainDebris.cpp @@ -249,4 +249,4 @@ namespace RTE { // release_bitmap(terrain->GetMaterialBitmap()); // release_bitmap(terrain->GetFGColorBitmap()); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/TerrainDebris.h b/Source/Entities/TerrainDebris.h index 90c9d65b8f..6f3c837ac7 100644 --- a/Source/Entities/TerrainDebris.h +++ b/Source/Entities/TerrainDebris.h @@ -140,4 +140,4 @@ namespace RTE { void operator=(const TerrainDebris& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/TerrainFrosting.cpp b/Source/Entities/TerrainFrosting.cpp index 82ea45d6d6..a2a2807f7f 100644 --- a/Source/Entities/TerrainFrosting.cpp +++ b/Source/Entities/TerrainFrosting.cpp @@ -86,4 +86,4 @@ namespace RTE { // release_bitmap(fgColorBitmap); // release_bitmap(matBitmap); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/TerrainFrosting.h b/Source/Entities/TerrainFrosting.h index 3bf6defaaf..c83c2d20e2 100644 --- a/Source/Entities/TerrainFrosting.h +++ b/Source/Entities/TerrainFrosting.h @@ -52,4 +52,4 @@ namespace RTE { void operator=(const TerrainFrosting& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/TerrainObject.cpp b/Source/Entities/TerrainObject.cpp index 97274fcdca..e86cfa15ae 100644 --- a/Source/Entities/TerrainObject.cpp +++ b/Source/Entities/TerrainObject.cpp @@ -312,4 +312,4 @@ namespace RTE { g_SceneMan.RegisterTerrainChange(posOnScene.GetFloorIntX(), posOnScene.GetFloorIntY(), m_FGColorBitmap->w, m_FGColorBitmap->h, ColorKeys::g_MaskColor, false); } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/TerrainObject.h b/Source/Entities/TerrainObject.h index 88d0c4b9af..9374419f39 100644 --- a/Source/Entities/TerrainObject.h +++ b/Source/Entities/TerrainObject.h @@ -191,4 +191,4 @@ namespace RTE { void operator=(const TerrainObject& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/ThrownDevice.cpp b/Source/Entities/ThrownDevice.cpp index e5b527c17e..e1b469230e 100644 --- a/Source/Entities/ThrownDevice.cpp +++ b/Source/Entities/ThrownDevice.cpp @@ -135,4 +135,4 @@ namespace RTE { m_Activated = true; } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/ThrownDevice.h b/Source/Entities/ThrownDevice.h index 6314e832c0..9738b68dac 100644 --- a/Source/Entities/ThrownDevice.h +++ b/Source/Entities/ThrownDevice.h @@ -170,4 +170,4 @@ namespace RTE { ThrownDevice& operator=(const ThrownDevice& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Entities/Turret.cpp b/Source/Entities/Turret.cpp index d981ed105f..40c1a63915 100644 --- a/Source/Entities/Turret.cpp +++ b/Source/Entities/Turret.cpp @@ -157,4 +157,4 @@ namespace RTE { }); m_MountedDevices.erase(mountedDeviceIterator); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Entities/Turret.h b/Source/Entities/Turret.h index d1c925cb42..f7205ea4b7 100644 --- a/Source/Entities/Turret.h +++ b/Source/Entities/Turret.h @@ -145,4 +145,4 @@ namespace RTE { Turret& operator=(const Turret& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUI.h b/Source/GUI/GUI.h index 00f6e3031d..29a658a810 100644 --- a/Source/GUI/GUI.h +++ b/Source/GUI/GUI.h @@ -57,4 +57,4 @@ inline void SetRect(GUIRect* rect, int left, int top, int right, int bottom) { #include "GUISound.h" #endif -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIBanner.h b/Source/GUI/GUIBanner.h index 6ea692716e..b97477bcc1 100644 --- a/Source/GUI/GUIBanner.h +++ b/Source/GUI/GUIBanner.h @@ -314,4 +314,4 @@ namespace RTE { }; // namespace RTE -#endif // _GUIBanner_ \ No newline at end of file +#endif // _GUIBanner_ diff --git a/Source/GUI/GUIButton.cpp b/Source/GUI/GUIButton.cpp index 01e825ec23..f6a53ac61f 100644 --- a/Source/GUI/GUIButton.cpp +++ b/Source/GUI/GUIButton.cpp @@ -474,4 +474,4 @@ void GUIButton::ApplyProperties(GUIProperties* Props) { m_Text->SetVerticalOverflowScroll(overflowScroll); BuildBitmap(); -} \ No newline at end of file +} diff --git a/Source/GUI/GUIButton.h b/Source/GUI/GUIButton.h index 5ee89962e9..2111c0a9e0 100644 --- a/Source/GUI/GUIButton.h +++ b/Source/GUI/GUIButton.h @@ -269,4 +269,4 @@ namespace RTE { void BuildBitmap(); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUICheckbox.cpp b/Source/GUI/GUICheckbox.cpp index dc8991bcdf..6c1d623e9a 100644 --- a/Source/GUI/GUICheckbox.cpp +++ b/Source/GUI/GUICheckbox.cpp @@ -307,4 +307,4 @@ void GUICheckbox::ApplyProperties(GUIProperties* Props) { m_Check = Greycheck; } m_Properties.GetValue("Text", &m_Text); -} \ No newline at end of file +} diff --git a/Source/GUI/GUICheckbox.h b/Source/GUI/GUICheckbox.h index 2942f7efef..086f2d1588 100644 --- a/Source/GUI/GUICheckbox.h +++ b/Source/GUI/GUICheckbox.h @@ -210,4 +210,4 @@ namespace RTE { void BuildBitmap(); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUICollectionBox.cpp b/Source/GUI/GUICollectionBox.cpp index 448478a948..d1631aac2e 100644 --- a/Source/GUI/GUICollectionBox.cpp +++ b/Source/GUI/GUICollectionBox.cpp @@ -298,4 +298,4 @@ void GUICollectionBox::ApplyProperties(GUIProperties* Props) { m_DrawType = Image; } m_Properties.GetValue("DrawColor", &m_DrawColor); -} \ No newline at end of file +} diff --git a/Source/GUI/GUICollectionBox.h b/Source/GUI/GUICollectionBox.h index 9b96b49a06..184711f14b 100644 --- a/Source/GUI/GUICollectionBox.h +++ b/Source/GUI/GUICollectionBox.h @@ -234,4 +234,4 @@ namespace RTE { void BuildBitmap(); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIComboBox.cpp b/Source/GUI/GUIComboBox.cpp index 111d1256f2..02ed8f2555 100644 --- a/Source/GUI/GUIComboBox.cpp +++ b/Source/GUI/GUIComboBox.cpp @@ -647,4 +647,4 @@ void GUIComboBoxButton::Destroy() { delete m_DrawBitmap; m_DrawBitmap = nullptr; } -} \ No newline at end of file +} diff --git a/Source/GUI/GUIComboBox.h b/Source/GUI/GUIComboBox.h index 4e4ba8a133..9b8dba0871 100644 --- a/Source/GUI/GUIComboBox.h +++ b/Source/GUI/GUIComboBox.h @@ -443,4 +443,4 @@ namespace RTE { bool m_Pushed; }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIControl.h b/Source/GUI/GUIControl.h index d1f05d1955..616942429e 100644 --- a/Source/GUI/GUIControl.h +++ b/Source/GUI/GUIControl.h @@ -284,4 +284,4 @@ namespace RTE { GUIControlManager* m_ControlManager; }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIControlFactory.h b/Source/GUI/GUIControlFactory.h index 0c566d088c..7b88ad585d 100644 --- a/Source/GUI/GUIControlFactory.h +++ b/Source/GUI/GUIControlFactory.h @@ -18,4 +18,4 @@ namespace RTE { static GUIControl* CreateControl(GUIManager* Manager, GUIControlManager* ControlManager, const std::string& ControlName); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIControlManager.cpp b/Source/GUI/GUIControlManager.cpp index 6f93571321..c7e80cb319 100644 --- a/Source/GUI/GUIControlManager.cpp +++ b/Source/GUI/GUIControlManager.cpp @@ -470,4 +470,4 @@ bool GUIControlManager::Load(const std::string& Filename, bool keepOld) { } return true; -} \ No newline at end of file +} diff --git a/Source/GUI/GUIControlManager.h b/Source/GUI/GUIControlManager.h index 1a659de2b0..4bd4224b20 100644 --- a/Source/GUI/GUIControlManager.h +++ b/Source/GUI/GUIControlManager.h @@ -268,4 +268,4 @@ namespace RTE { void AddEvent(GUIEvent* Event); }; }; // namespace RTE -#endif // _GUICONTROLMANAGER_ \ No newline at end of file +#endif // _GUICONTROLMANAGER_ diff --git a/Source/GUI/GUIEvent.h b/Source/GUI/GUIEvent.h index e900742e42..9024698407 100644 --- a/Source/GUI/GUIEvent.h +++ b/Source/GUI/GUIEvent.h @@ -72,4 +72,4 @@ namespace RTE { int m_Data; }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIFont.cpp b/Source/GUI/GUIFont.cpp index 18b489c338..f30e0de072 100644 --- a/Source/GUI/GUIFont.cpp +++ b/Source/GUI/GUIFont.cpp @@ -446,4 +446,4 @@ void GUIFont::Destroy() { } } m_ColorCache.clear(); -} \ No newline at end of file +} diff --git a/Source/GUI/GUIFont.h b/Source/GUI/GUIFont.h index 6628bdd6a2..94383c0fde 100644 --- a/Source/GUI/GUIFont.h +++ b/Source/GUI/GUIFont.h @@ -177,4 +177,4 @@ namespace RTE { int m_Leading; // Spacing between lines }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIInterface.h b/Source/GUI/GUIInterface.h index e9d10d3ef8..6651320f10 100644 --- a/Source/GUI/GUIInterface.h +++ b/Source/GUI/GUIInterface.h @@ -258,4 +258,4 @@ namespace RTE { }; #pragma endregion } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUILabel.h b/Source/GUI/GUILabel.h index 98febc49fb..fe2daa4c04 100644 --- a/Source/GUI/GUILabel.h +++ b/Source/GUI/GUILabel.h @@ -265,4 +265,4 @@ namespace RTE { Timer m_OverflowScrollTimer; }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIListBox.cpp b/Source/GUI/GUIListBox.cpp index a95819ff64..bea873ea24 100644 --- a/Source/GUI/GUIListBox.cpp +++ b/Source/GUI/GUIListBox.cpp @@ -154,4 +154,4 @@ void GUIListBox::ApplyProperties(GUIProperties* Props) { // Rebuild the bitmap BuildBitmap(true, true); -} \ No newline at end of file +} diff --git a/Source/GUI/GUIListBox.h b/Source/GUI/GUIListBox.h index a89f4dbe5b..e771887dc3 100644 --- a/Source/GUI/GUIListBox.h +++ b/Source/GUI/GUIListBox.h @@ -120,4 +120,4 @@ namespace RTE { private: }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIListPanel.h b/Source/GUI/GUIListPanel.h index 5a9c1744dc..d5cc21c786 100644 --- a/Source/GUI/GUIListPanel.h +++ b/Source/GUI/GUIListPanel.h @@ -565,4 +565,4 @@ namespace RTE { void SelectionListScrolling(int mouseWheelChange); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIManager.h b/Source/GUI/GUIManager.h index 772aa40f8f..31c5a81e30 100644 --- a/Source/GUI/GUIManager.h +++ b/Source/GUI/GUIManager.h @@ -169,4 +169,4 @@ namespace RTE { bool MouseInRect(const GUIRect* Rect, int X, int Y); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIProgressBar.cpp b/Source/GUI/GUIProgressBar.cpp index 68c60ecda3..70ce7566cc 100644 --- a/Source/GUI/GUIProgressBar.cpp +++ b/Source/GUI/GUIProgressBar.cpp @@ -317,4 +317,4 @@ void GUIProgressBar::ApplyProperties(GUIProperties* Props) { // Clamp the value m_Value = std::max(m_Value, m_Minimum); m_Value = std::min(m_Value, m_Maximum); -} \ No newline at end of file +} diff --git a/Source/GUI/GUIProgressBar.h b/Source/GUI/GUIProgressBar.h index 7d1597da4e..2781454489 100644 --- a/Source/GUI/GUIProgressBar.h +++ b/Source/GUI/GUIProgressBar.h @@ -211,4 +211,4 @@ namespace RTE { void BuildBitmap(); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIProperties.h b/Source/GUI/GUIProperties.h index 6e44a6b67b..e5af9ea1b8 100644 --- a/Source/GUI/GUIProperties.h +++ b/Source/GUI/GUIProperties.h @@ -207,4 +207,4 @@ namespace RTE { std::vector m_VariableList; }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIPropertyPage.h b/Source/GUI/GUIPropertyPage.h index 2f09a2b42d..8e1b32d853 100644 --- a/Source/GUI/GUIPropertyPage.h +++ b/Source/GUI/GUIPropertyPage.h @@ -222,4 +222,4 @@ namespace RTE { void BuildBitmap(); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIRadioButton.cpp b/Source/GUI/GUIRadioButton.cpp index f641fc4907..1bcd481099 100644 --- a/Source/GUI/GUIRadioButton.cpp +++ b/Source/GUI/GUIRadioButton.cpp @@ -319,4 +319,4 @@ void GUIRadioButton::ApplyProperties(GUIProperties* Props) { m_Properties.GetValue("Text", &m_Text); m_Properties.GetValue("Checked", &m_Checked); -} \ No newline at end of file +} diff --git a/Source/GUI/GUIRadioButton.h b/Source/GUI/GUIRadioButton.h index f6b0bc3dd5..1d4bc69f55 100644 --- a/Source/GUI/GUIRadioButton.h +++ b/Source/GUI/GUIRadioButton.h @@ -195,4 +195,4 @@ namespace RTE { void BuildBitmap(); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIScrollPanel.cpp b/Source/GUI/GUIScrollPanel.cpp index 64735b42b1..e6f9dcf6b0 100644 --- a/Source/GUI/GUIScrollPanel.cpp +++ b/Source/GUI/GUIScrollPanel.cpp @@ -751,4 +751,4 @@ int GUIScrollPanel::GetSmallChange() const { int GUIScrollPanel::GetValueResolution() const { return m_ValueResolution; -} \ No newline at end of file +} diff --git a/Source/GUI/GUIScrollPanel.h b/Source/GUI/GUIScrollPanel.h index 758abce276..1d2899375a 100644 --- a/Source/GUI/GUIScrollPanel.h +++ b/Source/GUI/GUIScrollPanel.h @@ -313,4 +313,4 @@ namespace RTE { void AdjustValue(int Delta); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIScrollbar.cpp b/Source/GUI/GUIScrollbar.cpp index 1f7d8bfbfb..3f4ba3f82f 100644 --- a/Source/GUI/GUIScrollbar.cpp +++ b/Source/GUI/GUIScrollbar.cpp @@ -162,4 +162,4 @@ void GUIScrollbar::ApplyProperties(GUIProperties* Props) { // Rebuild the bitmap BuildBitmap(true, true); -} \ No newline at end of file +} diff --git a/Source/GUI/GUIScrollbar.h b/Source/GUI/GUIScrollbar.h index ed06ed3a82..e9efa3cbdc 100644 --- a/Source/GUI/GUIScrollbar.h +++ b/Source/GUI/GUIScrollbar.h @@ -151,4 +151,4 @@ namespace RTE { private: }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUISkin.cpp b/Source/GUI/GUISkin.cpp index 2c441d4120..00ff282c2d 100644 --- a/Source/GUI/GUISkin.cpp +++ b/Source/GUI/GUISkin.cpp @@ -405,4 +405,4 @@ void GUISkin::BuildStandardRect(GUIBitmap* Dest, const std::string& Section, int unsigned long GUISkin::ConvertColor(unsigned long color, int targetDepth) { return m_Screen->ConvertColor(color, targetDepth); -} \ No newline at end of file +} diff --git a/Source/GUI/GUISkin.h b/Source/GUI/GUISkin.h index 42b4219cda..24e07e0c7b 100644 --- a/Source/GUI/GUISkin.h +++ b/Source/GUI/GUISkin.h @@ -156,4 +156,4 @@ namespace RTE { GUIBitmap* LoadMousePointer(const std::string& Section); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUISlider.cpp b/Source/GUI/GUISlider.cpp index e9e784b6b2..ea6b7e069a 100644 --- a/Source/GUI/GUISlider.cpp +++ b/Source/GUI/GUISlider.cpp @@ -584,4 +584,4 @@ void GUISlider::SetValueResolution(int valueRes) { if (valueRes >= 1 && valueRes <= m_Maximum - m_Minimum) { m_ValueResolution = valueRes; } -} \ No newline at end of file +} diff --git a/Source/GUI/GUISlider.h b/Source/GUI/GUISlider.h index aa29f79ac1..0a478372d6 100644 --- a/Source/GUI/GUISlider.h +++ b/Source/GUI/GUISlider.h @@ -297,4 +297,4 @@ namespace RTE { void CalculateKnob(); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUISound.h b/Source/GUI/GUISound.h index d1af0093db..2a94a04391 100644 --- a/Source/GUI/GUISound.h +++ b/Source/GUI/GUISound.h @@ -248,4 +248,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUITab.cpp b/Source/GUI/GUITab.cpp index af44cea7b2..e157b38d6b 100644 --- a/Source/GUI/GUITab.cpp +++ b/Source/GUI/GUITab.cpp @@ -321,4 +321,4 @@ void GUITab::ApplyProperties(GUIProperties* Props) { m_Properties.GetValue("Text", &m_Text); m_Properties.GetValue("Selected", &m_Selected); -} \ No newline at end of file +} diff --git a/Source/GUI/GUITab.h b/Source/GUI/GUITab.h index b356550cef..ce44aa25e2 100644 --- a/Source/GUI/GUITab.h +++ b/Source/GUI/GUITab.h @@ -196,4 +196,4 @@ namespace RTE { void BuildBitmap(); }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUITextBox.cpp b/Source/GUI/GUITextBox.cpp index 158b659780..f5e0a83e68 100644 --- a/Source/GUI/GUITextBox.cpp +++ b/Source/GUI/GUITextBox.cpp @@ -185,4 +185,4 @@ void GUITextBox::ApplyProperties(GUIProperties* Props) { // Force a rebuild of the bitmap ChangeSkin(m_Skin); -} \ No newline at end of file +} diff --git a/Source/GUI/GUITextBox.h b/Source/GUI/GUITextBox.h index ed09532ba6..1aead6dd86 100644 --- a/Source/GUI/GUITextBox.h +++ b/Source/GUI/GUITextBox.h @@ -130,4 +130,4 @@ namespace RTE { int m_VAlignment; }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIUtil.h b/Source/GUI/GUIUtil.h index 69a1c82c41..b953f70610 100644 --- a/Source/GUI/GUIUtil.h +++ b/Source/GUI/GUIUtil.h @@ -31,4 +31,4 @@ namespace RTE { static bool SetClipboardText(const std::string& text); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/GUIWriter.cpp b/Source/GUI/GUIWriter.cpp index 3a087fb1af..37c074e74d 100644 --- a/Source/GUI/GUIWriter.cpp +++ b/Source/GUI/GUIWriter.cpp @@ -220,4 +220,4 @@ namespace RTE { *m_Stream << var; return *this; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/GUI/GUIWriter.h b/Source/GUI/GUIWriter.h index b979877961..2f01ac5c98 100644 --- a/Source/GUI/GUIWriter.h +++ b/Source/GUI/GUIWriter.h @@ -138,4 +138,4 @@ namespace RTE { GUIWriter& operator=(const GUIWriter& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/Wrappers/AllegroBitmap.cpp b/Source/GUI/Wrappers/AllegroBitmap.cpp index a3f422eb88..925e10bcce 100644 --- a/Source/GUI/Wrappers/AllegroBitmap.cpp +++ b/Source/GUI/Wrappers/AllegroBitmap.cpp @@ -183,4 +183,4 @@ namespace RTE { rect(m_Bitmap, posX, posY, posX + width - 1, posY + height - 1, color); } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/GUI/Wrappers/AllegroBitmap.h b/Source/GUI/Wrappers/AllegroBitmap.h index 7c0cfd9110..386bd2fe89 100644 --- a/Source/GUI/Wrappers/AllegroBitmap.h +++ b/Source/GUI/Wrappers/AllegroBitmap.h @@ -198,4 +198,4 @@ namespace RTE { AllegroBitmap& operator=(const AllegroBitmap& rhs) = delete; }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/Wrappers/AllegroScreen.cpp b/Source/GUI/Wrappers/AllegroScreen.cpp index 9d373460e1..f6a3bb029e 100644 --- a/Source/GUI/Wrappers/AllegroScreen.cpp +++ b/Source/GUI/Wrappers/AllegroScreen.cpp @@ -77,4 +77,4 @@ namespace RTE { } return color; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/GUI/Wrappers/AllegroScreen.h b/Source/GUI/Wrappers/AllegroScreen.h index 22e8e01ccc..64cac1b21f 100644 --- a/Source/GUI/Wrappers/AllegroScreen.h +++ b/Source/GUI/Wrappers/AllegroScreen.h @@ -91,4 +91,4 @@ namespace RTE { AllegroScreen& operator=(const AllegroScreen& rhs) = delete; }; }; // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/GUI/Wrappers/GUIInputWrapper.cpp b/Source/GUI/Wrappers/GUIInputWrapper.cpp index d763dc4076..a4838e9369 100644 --- a/Source/GUI/Wrappers/GUIInputWrapper.cpp +++ b/Source/GUI/Wrappers/GUIInputWrapper.cpp @@ -261,4 +261,4 @@ namespace RTE { m_MouseButtonsEvents[0] = GUIInput::None; } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuaAdapters.cpp b/Source/Lua/LuaAdapters.cpp index 46c7e768eb..242087162f 100644 --- a/Source/Lua/LuaAdapters.cpp +++ b/Source/Lua/LuaAdapters.cpp @@ -743,4 +743,4 @@ namespace RTE { entityToDelete = nullptr; } #pragma endregion -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuaBindingsActivities.cpp b/Source/Lua/LuaBindingsActivities.cpp index c86d17e86f..e6de5cd4b2 100644 --- a/Source/Lua/LuaBindingsActivities.cpp +++ b/Source/Lua/LuaBindingsActivities.cpp @@ -177,4 +177,4 @@ namespace RTE { luabind::value("ARROWRIGHT", GameActivity::ObjectiveArrowDir::ARROWRIGHT), luabind::value("ARROWUP", GameActivity::ObjectiveArrowDir::ARROWUP)]; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuaBindingsGUI.cpp b/Source/Lua/LuaBindingsGUI.cpp index e680c8f0cf..a80d425937 100644 --- a/Source/Lua/LuaBindingsGUI.cpp +++ b/Source/Lua/LuaBindingsGUI.cpp @@ -97,4 +97,4 @@ namespace RTE { luabind::value("DONEEDITING", SceneEditorGUI::EditorGUIMode::DONEEDITING), luabind::value("EDITORGUIMODECOUNT", SceneEditorGUI::EditorGUIMode::EDITORGUIMODECOUNT)]; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuaBindingsInput.cpp b/Source/Lua/LuaBindingsInput.cpp index 1e1a1c9c4e..016dc61ccb 100644 --- a/Source/Lua/LuaBindingsInput.cpp +++ b/Source/Lua/LuaBindingsInput.cpp @@ -628,4 +628,4 @@ namespace RTE { luabind::value("TRIGGERRIGHT", SDL_CONTROLLER_AXIS_TRIGGERRIGHT), luabind::value("MAX", SDL_CONTROLLER_AXIS_MAX)]; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuaBindingsManagers.cpp b/Source/Lua/LuaBindingsManagers.cpp index 47226dbd3f..1a9ea3bec4 100644 --- a/Source/Lua/LuaBindingsManagers.cpp +++ b/Source/Lua/LuaBindingsManagers.cpp @@ -458,4 +458,4 @@ namespace RTE { .def("MouseButtonReleased", &LuaAdaptersUInputMan::MouseButtonReleased) .def("MouseButtonHeld", &LuaAdaptersUInputMan::MouseButtonHeld); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuaBindingsMisc.cpp b/Source/Lua/LuaBindingsMisc.cpp index d78e870f41..361e9c9d47 100644 --- a/Source/Lua/LuaBindingsMisc.cpp +++ b/Source/Lua/LuaBindingsMisc.cpp @@ -49,4 +49,4 @@ namespace RTE { luabind::value("Transparency", DrawBlendMode::BlendTransparency), luabind::value("BlendModeCount", DrawBlendMode::BlendModeCount)]; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuaBindingsPrimitives.cpp b/Source/Lua/LuaBindingsPrimitives.cpp index b6b291ae9d..57fc5171c4 100644 --- a/Source/Lua/LuaBindingsPrimitives.cpp +++ b/Source/Lua/LuaBindingsPrimitives.cpp @@ -130,4 +130,4 @@ namespace RTE { .def(luabind::constructor()) .def(luabind::constructor()); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuaBindingsSystem.cpp b/Source/Lua/LuaBindingsSystem.cpp index 0e464cd007..2e964d7999 100644 --- a/Source/Lua/LuaBindingsSystem.cpp +++ b/Source/Lua/LuaBindingsSystem.cpp @@ -246,4 +246,4 @@ namespace RTE { luabind::value("NoSolution", micropather::MicroPather::NO_SOLUTION), luabind::value("StartEndSame", micropather::MicroPather::START_END_SAME)]; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuabindObjectWrapper.cpp b/Source/Lua/LuabindObjectWrapper.cpp index 2241458588..e02a9e9b7f 100644 --- a/Source/Lua/LuabindObjectWrapper.cpp +++ b/Source/Lua/LuabindObjectWrapper.cpp @@ -75,4 +75,4 @@ namespace RTE { return LuabindObjectWrapper(copy, m_FilePath, true); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Lua/LuabindObjectWrapper.h b/Source/Lua/LuabindObjectWrapper.h index aef459eaa9..50f4861c40 100644 --- a/Source/Lua/LuabindObjectWrapper.h +++ b/Source/Lua/LuabindObjectWrapper.h @@ -78,4 +78,4 @@ namespace RTE { LuabindObjectWrapper& operator=(const LuabindObjectWrapper& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Main.cpp b/Source/Main.cpp index cc338b0495..27b9ff540b 100644 --- a/Source/Main.cpp +++ b/Source/Main.cpp @@ -507,4 +507,4 @@ int main(int argc, char** argv) { #ifdef _WIN32 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { return main(__argc, __argv); } -#endif \ No newline at end of file +#endif diff --git a/Source/Managers/ActivityMan.h b/Source/Managers/ActivityMan.h index 9db040229e..a6aabc3d0c 100644 --- a/Source/Managers/ActivityMan.h +++ b/Source/Managers/ActivityMan.h @@ -311,4 +311,4 @@ namespace RTE { ActivityMan& operator=(const ActivityMan& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Managers/CameraMan.cpp b/Source/Managers/CameraMan.cpp index 37ceaaf4b8..20a26865e4 100644 --- a/Source/Managers/CameraMan.cpp +++ b/Source/Managers/CameraMan.cpp @@ -283,4 +283,4 @@ namespace RTE { screen.DeltaOffset = screen.Offset - oldOffset; screen.ScrollTimer.Reset(); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Managers/CameraMan.h b/Source/Managers/CameraMan.h index 4fe669d3c1..adec7208b9 100644 --- a/Source/Managers/CameraMan.h +++ b/Source/Managers/CameraMan.h @@ -285,4 +285,4 @@ namespace RTE { CameraMan& operator=(const CameraMan& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Managers/ConsoleMan.h b/Source/Managers/ConsoleMan.h index bf71f7523a..a943c73728 100644 --- a/Source/Managers/ConsoleMan.h +++ b/Source/Managers/ConsoleMan.h @@ -229,4 +229,4 @@ namespace RTE { ConsoleMan& operator=(const ConsoleMan& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Managers/FrameMan.cpp b/Source/Managers/FrameMan.cpp index cb8e15e4bb..bbd65d7ad7 100644 --- a/Source/Managers/FrameMan.cpp +++ b/Source/Managers/FrameMan.cpp @@ -1139,4 +1139,4 @@ namespace RTE { m_NetworkFrameReady = m_NetworkFrameCurrent; m_NetworkFrameCurrent = (m_NetworkFrameCurrent == 0) ? 1 : 0; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Managers/MenuMan.h b/Source/Managers/MenuMan.h index 3fbde9c719..677b1a6ef2 100644 --- a/Source/Managers/MenuMan.h +++ b/Source/Managers/MenuMan.h @@ -114,4 +114,4 @@ namespace RTE { MenuMan& operator=(const MenuMan& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Managers/MetaMan.cpp b/Source/Managers/MetaMan.cpp index b793e85364..6a7e47d9a4 100644 --- a/Source/Managers/MetaMan.cpp +++ b/Source/Managers/MetaMan.cpp @@ -1337,4 +1337,4 @@ namespace RTE { m_pMetaGUI->Draw(pTargetBitmap); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Managers/MetaMan.h b/Source/Managers/MetaMan.h index cca5d38b85..57cd1eabf2 100644 --- a/Source/Managers/MetaMan.h +++ b/Source/Managers/MetaMan.h @@ -591,4 +591,4 @@ namespace RTE { }; } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Managers/MovableMan.h b/Source/Managers/MovableMan.h index d8d049869b..d4c8aec97a 100644 --- a/Source/Managers/MovableMan.h +++ b/Source/Managers/MovableMan.h @@ -1013,4 +1013,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Managers/PerformanceMan.cpp b/Source/Managers/PerformanceMan.cpp index 9d559d7a9a..a0e20ea452 100644 --- a/Source/Managers/PerformanceMan.cpp +++ b/Source/Managers/PerformanceMan.cpp @@ -247,4 +247,4 @@ namespace RTE { g_PerformanceMan.m_SortedScriptTimings = sortedScriptTimings; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Managers/PerformanceMan.h b/Source/Managers/PerformanceMan.h index 448cb27132..2fd695c193 100644 --- a/Source/Managers/PerformanceMan.h +++ b/Source/Managers/PerformanceMan.h @@ -259,4 +259,4 @@ namespace RTE { PerformanceMan& operator=(const PerformanceMan& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Managers/PostProcessMan.h b/Source/Managers/PostProcessMan.h index c0fdf65d11..a2dc7a87df 100644 --- a/Source/Managers/PostProcessMan.h +++ b/Source/Managers/PostProcessMan.h @@ -316,4 +316,4 @@ namespace RTE { PostProcessMan& operator=(const PostProcessMan& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Managers/PresetMan.h b/Source/Managers/PresetMan.h index 42706c3067..b58bc08c8b 100644 --- a/Source/Managers/PresetMan.h +++ b/Source/Managers/PresetMan.h @@ -564,4 +564,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Managers/PrimitiveMan.cpp b/Source/Managers/PrimitiveMan.cpp index 5df7c5491f..a29d08d63a 100644 --- a/Source/Managers/PrimitiveMan.cpp +++ b/Source/Managers/PrimitiveMan.cpp @@ -343,4 +343,4 @@ namespace RTE { } drawing_mode(DRAW_MODE_SOLID, nullptr, 0, 0); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Managers/PrimitiveMan.h b/Source/Managers/PrimitiveMan.h index a265d91df5..e8deb56db9 100644 --- a/Source/Managers/PrimitiveMan.h +++ b/Source/Managers/PrimitiveMan.h @@ -505,4 +505,4 @@ namespace RTE { PrimitiveMan& operator=(const PrimitiveMan& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Managers/SceneMan.cpp b/Source/Managers/SceneMan.cpp index 3d4692e839..6974c4aec6 100644 --- a/Source/Managers/SceneMan.cpp +++ b/Source/Managers/SceneMan.cpp @@ -2713,4 +2713,4 @@ namespace RTE { } return m_IntermediateSettlingBitmaps.back().second; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Managers/ThreadMan.cpp b/Source/Managers/ThreadMan.cpp index bb20f8da3c..42a7ab4c6b 100644 --- a/Source/Managers/ThreadMan.cpp +++ b/Source/Managers/ThreadMan.cpp @@ -45,4 +45,4 @@ namespace RTE { Clear(); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Managers/UInputMan.cpp b/Source/Managers/UInputMan.cpp index 3671072ca9..193e189684 100644 --- a/Source/Managers/UInputMan.cpp +++ b/Source/Managers/UInputMan.cpp @@ -1245,4 +1245,4 @@ namespace RTE { } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Managers/UInputMan.h b/Source/Managers/UInputMan.h index c8c3d39075..e2861f2f04 100644 --- a/Source/Managers/UInputMan.h +++ b/Source/Managers/UInputMan.h @@ -872,4 +872,4 @@ namespace RTE { UInputMan& operator=(const UInputMan& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Managers/WindowMan.cpp b/Source/Managers/WindowMan.cpp index 8ce090df17..c767770663 100644 --- a/Source/Managers/WindowMan.cpp +++ b/Source/Managers/WindowMan.cpp @@ -802,4 +802,4 @@ namespace RTE { FrameMark; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Managers/WindowMan.h b/Source/Managers/WindowMan.h index da7ce6c9bc..89101548e3 100644 --- a/Source/Managers/WindowMan.h +++ b/Source/Managers/WindowMan.h @@ -395,4 +395,4 @@ namespace RTE { WindowMan& operator=(const WindowMan& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/AreaEditorGUI.cpp b/Source/Menus/AreaEditorGUI.cpp index dfc5acd792..3bbbeafc05 100644 --- a/Source/Menus/AreaEditorGUI.cpp +++ b/Source/Menus/AreaEditorGUI.cpp @@ -609,4 +609,4 @@ void AreaEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { // Draw the pie menu m_PieMenu->Draw(pTargetBitmap, targetPos); -} \ No newline at end of file +} diff --git a/Source/Menus/AreaEditorGUI.h b/Source/Menus/AreaEditorGUI.h index ff4d673e7a..169fb0275d 100644 --- a/Source/Menus/AreaEditorGUI.h +++ b/Source/Menus/AreaEditorGUI.h @@ -290,4 +290,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Menus/AreaPickerGUI.cpp b/Source/Menus/AreaPickerGUI.cpp index fdd843bb33..808da95add 100644 --- a/Source/Menus/AreaPickerGUI.cpp +++ b/Source/Menus/AreaPickerGUI.cpp @@ -503,4 +503,4 @@ void AreaPickerGUI::Draw(BITMAP* drawBitmap) const { // Draw the cursor on top of everything if (IsEnabled() && m_pController->IsMouseControlled()) draw_sprite(drawBitmap, s_pCursor, m_CursorPos.GetFloorIntX(), m_CursorPos.GetFloorIntY()); -} \ No newline at end of file +} diff --git a/Source/Menus/AreaPickerGUI.h b/Source/Menus/AreaPickerGUI.h index a3a2fbae00..ff086ef3a1 100644 --- a/Source/Menus/AreaPickerGUI.h +++ b/Source/Menus/AreaPickerGUI.h @@ -282,4 +282,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Menus/AssemblyEditorGUI.cpp b/Source/Menus/AssemblyEditorGUI.cpp index 45dc73ba92..a766194884 100644 --- a/Source/Menus/AssemblyEditorGUI.cpp +++ b/Source/Menus/AssemblyEditorGUI.cpp @@ -960,4 +960,4 @@ bool AssemblyEditorGUI::UpdateBrainPath() { return false; } return true; -} \ No newline at end of file +} diff --git a/Source/Menus/AssemblyEditorGUI.h b/Source/Menus/AssemblyEditorGUI.h index 1d1ae2160d..b8e85a6af8 100644 --- a/Source/Menus/AssemblyEditorGUI.h +++ b/Source/Menus/AssemblyEditorGUI.h @@ -408,4 +408,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Menus/GibEditorGUI.cpp b/Source/Menus/GibEditorGUI.cpp index 18ac0abc9a..89a9c464e1 100644 --- a/Source/Menus/GibEditorGUI.cpp +++ b/Source/Menus/GibEditorGUI.cpp @@ -806,4 +806,4 @@ void GibEditorGUI::UpdatePlacedObjects() { for (std::list::iterator itr = m_PlacedGibs.begin(); itr != m_PlacedGibs.end(); ++itr) { (*itr)->Update(); } -} \ No newline at end of file +} diff --git a/Source/Menus/GibEditorGUI.h b/Source/Menus/GibEditorGUI.h index 3d25286313..347989abed 100644 --- a/Source/Menus/GibEditorGUI.h +++ b/Source/Menus/GibEditorGUI.h @@ -368,4 +368,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Menus/InventoryMenuGUI.cpp b/Source/Menus/InventoryMenuGUI.cpp index 4876550034..1021e0ca54 100644 --- a/Source/Menus/InventoryMenuGUI.cpp +++ b/Source/Menus/InventoryMenuGUI.cpp @@ -1514,4 +1514,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/LoadingScreen.h b/Source/Menus/LoadingScreen.h index 1400f97bc4..68d15faf44 100644 --- a/Source/Menus/LoadingScreen.h +++ b/Source/Menus/LoadingScreen.h @@ -85,4 +85,4 @@ namespace RTE { LoadingScreen& operator=(const LoadingScreen& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/MainMenuGUI.h b/Source/Menus/MainMenuGUI.h index f7aa5f4d6e..43583e2156 100644 --- a/Source/Menus/MainMenuGUI.h +++ b/Source/Menus/MainMenuGUI.h @@ -278,4 +278,4 @@ namespace RTE { MainMenuGUI& operator=(const MainMenuGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/MetagameGUI.h b/Source/Menus/MetagameGUI.h index 3972678476..a994a39ec6 100644 --- a/Source/Menus/MetagameGUI.h +++ b/Source/Menus/MetagameGUI.h @@ -1205,4 +1205,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Menus/ModManagerGUI.cpp b/Source/Menus/ModManagerGUI.cpp index b29edc0fd7..e44fa18232 100644 --- a/Source/Menus/ModManagerGUI.cpp +++ b/Source/Menus/ModManagerGUI.cpp @@ -212,4 +212,4 @@ namespace RTE { void ModManagerGUI::Draw() const { m_GUIControlManager->Draw(); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/ModManagerGUI.h b/Source/Menus/ModManagerGUI.h index ec62385bc7..9c3b5f58ab 100644 --- a/Source/Menus/ModManagerGUI.h +++ b/Source/Menus/ModManagerGUI.h @@ -136,4 +136,4 @@ namespace RTE { ModManagerGUI& operator=(const ModManagerGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/ObjectPickerGUI.h b/Source/Menus/ObjectPickerGUI.h index d78853450d..39182e2e2c 100644 --- a/Source/Menus/ObjectPickerGUI.h +++ b/Source/Menus/ObjectPickerGUI.h @@ -325,4 +325,4 @@ namespace RTE { ObjectPickerGUI& operator=(const ObjectPickerGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/PauseMenuGUI.cpp b/Source/Menus/PauseMenuGUI.cpp index 0bba284d62..a748ea4f3d 100644 --- a/Source/Menus/PauseMenuGUI.cpp +++ b/Source/Menus/PauseMenuGUI.cpp @@ -298,4 +298,4 @@ namespace RTE { } m_GUIControlManager->DrawMouse(); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/PauseMenuGUI.h b/Source/Menus/PauseMenuGUI.h index a4708b94d3..98e1f0b29b 100644 --- a/Source/Menus/PauseMenuGUI.h +++ b/Source/Menus/PauseMenuGUI.h @@ -174,4 +174,4 @@ namespace RTE { PauseMenuGUI& operator=(const PauseMenuGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/SaveLoadMenuGUI.cpp b/Source/Menus/SaveLoadMenuGUI.cpp index f33229c5a2..631f55763d 100644 --- a/Source/Menus/SaveLoadMenuGUI.cpp +++ b/Source/Menus/SaveLoadMenuGUI.cpp @@ -329,4 +329,4 @@ namespace RTE { void SaveLoadMenuGUI::Draw() const { m_GUIControlManager->Draw(); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/SaveLoadMenuGUI.h b/Source/Menus/SaveLoadMenuGUI.h index 73caabca16..448e7ca3c3 100644 --- a/Source/Menus/SaveLoadMenuGUI.h +++ b/Source/Menus/SaveLoadMenuGUI.h @@ -145,4 +145,4 @@ namespace RTE { SaveLoadMenuGUI& operator=(const SaveLoadMenuGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/ScenarioActivityConfigGUI.h b/Source/Menus/ScenarioActivityConfigGUI.h index 733b98781a..a175bd2c88 100644 --- a/Source/Menus/ScenarioActivityConfigGUI.h +++ b/Source/Menus/ScenarioActivityConfigGUI.h @@ -162,4 +162,4 @@ namespace RTE { ScenarioActivityConfigGUI& operator=(const ScenarioActivityConfigGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/ScenarioGUI.h b/Source/Menus/ScenarioGUI.h index cf4af8798d..62ba79d98d 100644 --- a/Source/Menus/ScenarioGUI.h +++ b/Source/Menus/ScenarioGUI.h @@ -217,4 +217,4 @@ namespace RTE { ScenarioGUI& operator=(const ScenarioGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/SceneEditorGUI.cpp b/Source/Menus/SceneEditorGUI.cpp index 24ca42c700..fce9fcc1d7 100644 --- a/Source/Menus/SceneEditorGUI.cpp +++ b/Source/Menus/SceneEditorGUI.cpp @@ -1517,4 +1517,4 @@ bool SceneEditorGUI::UpdateBrainPath() { return false; } return true; -} \ No newline at end of file +} diff --git a/Source/Menus/SceneEditorGUI.h b/Source/Menus/SceneEditorGUI.h index 8d2d998e7a..b31ca55d8a 100644 --- a/Source/Menus/SceneEditorGUI.h +++ b/Source/Menus/SceneEditorGUI.h @@ -382,4 +382,4 @@ namespace RTE { } // namespace RTE -#endif // File \ No newline at end of file +#endif // File diff --git a/Source/Menus/SettingsAudioGUI.cpp b/Source/Menus/SettingsAudioGUI.cpp index 7d0fc940b9..e0dbd816ac 100644 --- a/Source/Menus/SettingsAudioGUI.cpp +++ b/Source/Menus/SettingsAudioGUI.cpp @@ -96,4 +96,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/SettingsAudioGUI.h b/Source/Menus/SettingsAudioGUI.h index ae3edf1614..9e89a30925 100644 --- a/Source/Menus/SettingsAudioGUI.h +++ b/Source/Menus/SettingsAudioGUI.h @@ -77,4 +77,4 @@ namespace RTE { SettingsAudioGUI& operator=(const SettingsAudioGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/SettingsGUI.cpp b/Source/Menus/SettingsGUI.cpp index 2d2b6cb5b1..44e5c39cb7 100644 --- a/Source/Menus/SettingsGUI.cpp +++ b/Source/Menus/SettingsGUI.cpp @@ -206,4 +206,4 @@ namespace RTE { m_GUIControlManager->Draw(); m_GUIControlManager->DrawMouse(); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/SettingsGUI.h b/Source/Menus/SettingsGUI.h index b025955716..dd65341956 100644 --- a/Source/Menus/SettingsGUI.h +++ b/Source/Menus/SettingsGUI.h @@ -111,4 +111,4 @@ namespace RTE { SettingsGUI& operator=(const SettingsGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/SettingsGameplayGUI.cpp b/Source/Menus/SettingsGameplayGUI.cpp index 0a7fccfffb..e000041f68 100644 --- a/Source/Menus/SettingsGameplayGUI.cpp +++ b/Source/Menus/SettingsGameplayGUI.cpp @@ -171,4 +171,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/SettingsGameplayGUI.h b/Source/Menus/SettingsGameplayGUI.h index 79030ea3f2..f2f76e9ef0 100644 --- a/Source/Menus/SettingsGameplayGUI.h +++ b/Source/Menus/SettingsGameplayGUI.h @@ -91,4 +91,4 @@ namespace RTE { SettingsGameplayGUI& operator=(const SettingsGameplayGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/SettingsInputGUI.cpp b/Source/Menus/SettingsInputGUI.cpp index dab27d5b24..1cb04c1efd 100644 --- a/Source/Menus/SettingsInputGUI.cpp +++ b/Source/Menus/SettingsInputGUI.cpp @@ -230,4 +230,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/SettingsInputGUI.h b/Source/Menus/SettingsInputGUI.h index 227db550c0..23a6bb5d8f 100644 --- a/Source/Menus/SettingsInputGUI.h +++ b/Source/Menus/SettingsInputGUI.h @@ -146,4 +146,4 @@ namespace RTE { SettingsInputGUI& operator=(const SettingsInputGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/SettingsInputMappingGUI.h b/Source/Menus/SettingsInputMappingGUI.h index b28a261bfb..4fe312157c 100644 --- a/Source/Menus/SettingsInputMappingGUI.h +++ b/Source/Menus/SettingsInputMappingGUI.h @@ -135,4 +135,4 @@ namespace RTE { SettingsInputMappingGUI& operator=(const SettingsInputMappingGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/SettingsInputMappingWizardGUI.h b/Source/Menus/SettingsInputMappingWizardGUI.h index 896138999f..e56295cda0 100644 --- a/Source/Menus/SettingsInputMappingWizardGUI.h +++ b/Source/Menus/SettingsInputMappingWizardGUI.h @@ -244,4 +244,4 @@ namespace RTE { SettingsInputMappingWizardGUI& operator=(const SettingsInputMappingWizardGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/SettingsMiscGUI.cpp b/Source/Menus/SettingsMiscGUI.cpp index 520f494e9a..74185b656c 100644 --- a/Source/Menus/SettingsMiscGUI.cpp +++ b/Source/Menus/SettingsMiscGUI.cpp @@ -97,4 +97,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/SettingsMiscGUI.h b/Source/Menus/SettingsMiscGUI.h index be88b35ba5..804c646e50 100644 --- a/Source/Menus/SettingsMiscGUI.h +++ b/Source/Menus/SettingsMiscGUI.h @@ -68,4 +68,4 @@ namespace RTE { SettingsMiscGUI& operator=(const SettingsMiscGUI& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/Menus/SettingsVideoGUI.cpp b/Source/Menus/SettingsVideoGUI.cpp index 24e123f9c0..902a5ef8aa 100644 --- a/Source/Menus/SettingsVideoGUI.cpp +++ b/Source/Menus/SettingsVideoGUI.cpp @@ -447,4 +447,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/TitleScreen.cpp b/Source/Menus/TitleScreen.cpp index 645da4f5de..aabb0042ae 100644 --- a/Source/Menus/TitleScreen.cpp +++ b/Source/Menus/TitleScreen.cpp @@ -674,4 +674,4 @@ namespace RTE { set_trans_blender(m_FadeAmount, m_FadeAmount, m_FadeAmount, m_FadeAmount); draw_trans_sprite(g_FrameMan.GetBackBuffer32(), g_FrameMan.GetOverlayBitmap32(), 0, 0); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/Menus/TitleScreen.h b/Source/Menus/TitleScreen.h index 94f5f5fd5e..a565018056 100644 --- a/Source/Menus/TitleScreen.h +++ b/Source/Menus/TitleScreen.h @@ -293,4 +293,4 @@ namespace RTE { TitleScreen& operator=(const TitleScreen& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/AllegroTools.cpp b/Source/System/AllegroTools.cpp index 756cc77bb6..166914fcf7 100644 --- a/Source/System/AllegroTools.cpp +++ b/Source/System/AllegroTools.cpp @@ -33,4 +33,4 @@ namespace RTE { void SetTrueAlphaBlender() { set_blender_mode_ex(_blender_black, _blender_black, _blender_black, TrueAlphaBlender, _blender_black, _blender_black, _blender_black, 0, 0, 0, 0); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/AllegroTools.h b/Source/System/AllegroTools.h index 07e037301e..b41b7a5713 100644 --- a/Source/System/AllegroTools.h +++ b/Source/System/AllegroTools.h @@ -20,4 +20,4 @@ namespace RTE { #pragma endregion } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Atom.cpp b/Source/System/Atom.cpp index 0c09a68e20..6ebc4ad7ef 100644 --- a/Source/System/Atom.cpp +++ b/Source/System/Atom.cpp @@ -1127,4 +1127,4 @@ namespace RTE { } return *this; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/Atom.h b/Source/System/Atom.h index 7c826aad1f..d9d25e7ea5 100644 --- a/Source/System/Atom.h +++ b/Source/System/Atom.h @@ -554,4 +554,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Box.cpp b/Source/System/Box.cpp index a49aee9940..ce06304ede 100644 --- a/Source/System/Box.cpp +++ b/Source/System/Box.cpp @@ -152,4 +152,4 @@ namespace RTE { return (box1.m_Corner.m_X < box2.m_Corner.m_X + box2.m_Width) && (box1.m_Corner.m_X + box1.m_Width > box2.m_Corner.m_X) && (box1.m_Corner.m_Y < box2.m_Corner.m_Y + box2.m_Height) && (box1.m_Corner.m_Y + box1.m_Height > box2.m_Corner.m_Y); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/Box.h b/Source/System/Box.h index ffb1094de8..56d0e82e11 100644 --- a/Source/System/Box.h +++ b/Source/System/Box.h @@ -302,4 +302,4 @@ namespace RTE { } }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Color.cpp b/Source/System/Color.cpp index 414be41984..bbc334b52a 100644 --- a/Source/System/Color.cpp +++ b/Source/System/Color.cpp @@ -69,4 +69,4 @@ namespace RTE { int Color::RecalculateIndex() { return m_Index = makecol8(m_R, m_G, m_B); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/Color.h b/Source/System/Color.h index cf8cb63676..c1e8bd7e45 100644 --- a/Source/System/Color.h +++ b/Source/System/Color.h @@ -167,4 +167,4 @@ namespace RTE { void Clear() { m_R = m_G = m_B = m_Index = 0; } }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/ContentFile.cpp b/Source/System/ContentFile.cpp index acdda8ef3b..24de53ba50 100644 --- a/Source/System/ContentFile.cpp +++ b/Source/System/ContentFile.cpp @@ -414,4 +414,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/Controller.h b/Source/System/Controller.h index b1ce22b4f4..3de2fcab70 100644 --- a/Source/System/Controller.h +++ b/Source/System/Controller.h @@ -455,4 +455,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/DataModule.cpp b/Source/System/DataModule.cpp index 1102d391fb..3079cb9883 100644 --- a/Source/System/DataModule.cpp +++ b/Source/System/DataModule.cpp @@ -541,4 +541,4 @@ namespace RTE { RTEAssert(majorVersionMatch && minorVersionInRange, m_FileName + " was developed for Cortex Command v" + m_SupportedGameVersion->str() + ", so this version of Cortex Command (v" + c_GameVersion.str() + ") may not support it.\n" + contactAuthor); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/DataModule.h b/Source/System/DataModule.h index 67f47c4674..39bd884132 100644 --- a/Source/System/DataModule.h +++ b/Source/System/DataModule.h @@ -418,4 +418,4 @@ namespace RTE { DataModule& operator=(const DataModule& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Entity.cpp b/Source/System/Entity.cpp index 3a7fc9a5b6..86df30c14b 100644 --- a/Source/System/Entity.cpp +++ b/Source/System/Entity.cpp @@ -336,4 +336,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/Entity.h b/Source/System/Entity.h index 438d74c4f7..e4ff20bfe9 100644 --- a/Source/System/Entity.h +++ b/Source/System/Entity.h @@ -494,4 +494,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/GameVersion.h b/Source/System/GameVersion.h index b3f1c81795..1f5b1704c6 100644 --- a/Source/System/GameVersion.h +++ b/Source/System/GameVersion.h @@ -10,4 +10,4 @@ namespace RTE { static const version::Semver200_version c_GameVersion = version::Semver200_version(c_VersionString); #pragma endregion } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Gamepad.h b/Source/System/Gamepad.h index c9e09020e0..2fdb249593 100644 --- a/Source/System/Gamepad.h +++ b/Source/System/Gamepad.h @@ -56,4 +56,4 @@ namespace RTE { bool operator<(const Gamepad& rhs) const { return m_JoystickID < rhs.m_JoystickID; } }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/GenericSavedData.cpp b/Source/System/GenericSavedData.cpp index 05bd06b63e..539b420626 100644 --- a/Source/System/GenericSavedData.cpp +++ b/Source/System/GenericSavedData.cpp @@ -141,4 +141,4 @@ namespace RTE { return 0; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/GenericSavedData.h b/Source/System/GenericSavedData.h index ae8f4de19b..29a6022d3b 100644 --- a/Source/System/GenericSavedData.h +++ b/Source/System/GenericSavedData.h @@ -117,4 +117,4 @@ namespace RTE { static const std::string c_ClassName; //!< A string with the friendly formatted type name of this object. }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/GraphicalPrimitive.cpp b/Source/System/GraphicalPrimitive.cpp index 995bf7cb2a..f677d62c01 100644 --- a/Source/System/GraphicalPrimitive.cpp +++ b/Source/System/GraphicalPrimitive.cpp @@ -586,4 +586,4 @@ namespace RTE { } destroy_bitmap(bitmapToDraw); } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/GraphicalPrimitive.h b/Source/System/GraphicalPrimitive.h index e2d812488d..0307abc5aa 100644 --- a/Source/System/GraphicalPrimitive.h +++ b/Source/System/GraphicalPrimitive.h @@ -670,4 +670,4 @@ namespace RTE { }; #pragma endregion } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/InputScheme.h b/Source/System/InputScheme.h index c6a42b46aa..8eef7b10d5 100644 --- a/Source/System/InputScheme.h +++ b/Source/System/InputScheme.h @@ -199,4 +199,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Matrix.cpp b/Source/System/Matrix.cpp index fdf11aa744..a85df72cab 100644 --- a/Source/System/Matrix.cpp +++ b/Source/System/Matrix.cpp @@ -195,4 +195,4 @@ namespace RTE { m_ElementsUpdated = true; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/Matrix.h b/Source/System/Matrix.h index 174aa0a316..d9a45f0190 100644 --- a/Source/System/Matrix.h +++ b/Source/System/Matrix.h @@ -352,4 +352,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/NetworkMessages.h b/Source/System/NetworkMessages.h index d12b01b0e9..dd9d61c50e 100644 --- a/Source/System/NetworkMessages.h +++ b/Source/System/NetworkMessages.h @@ -306,4 +306,4 @@ namespace RTE { // Disables the previously set pack pragma. #pragma pack(pop) } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/PathFinder.h b/Source/System/PathFinder.h index 88e29418a3..284d131131 100644 --- a/Source/System/PathFinder.h +++ b/Source/System/PathFinder.h @@ -295,4 +295,4 @@ namespace RTE { PathFinder& operator=(const PathFinder& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/PieQuadrant.cpp b/Source/System/PieQuadrant.cpp index 036f0b5755..1c6b610d38 100644 --- a/Source/System/PieQuadrant.cpp +++ b/Source/System/PieQuadrant.cpp @@ -146,4 +146,4 @@ namespace RTE { } return nullptr; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/PieQuadrant.h b/Source/System/PieQuadrant.h index ef5fb1700d..e3c666ed2e 100644 --- a/Source/System/PieQuadrant.h +++ b/Source/System/PieQuadrant.h @@ -89,4 +89,4 @@ namespace RTE { PieQuadrant& operator=(const PieQuadrant& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/RTEError.cpp b/Source/System/RTEError.cpp index ffaf58ac10..4fc5c9d5f6 100644 --- a/Source/System/RTEError.cpp +++ b/Source/System/RTEError.cpp @@ -474,4 +474,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/RTEError.h b/Source/System/RTEError.h index 099fa148d7..28823a7c46 100644 --- a/Source/System/RTEError.h +++ b/Source/System/RTEError.h @@ -107,4 +107,4 @@ namespace RTE { RTEError::AssertFunc(description, std::source_location::current()); \ } } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/RTEStackTrace.cpp b/Source/System/RTEStackTrace.cpp index d5e4d311d9..b47fb95b95 100644 --- a/Source/System/RTEStackTrace.cpp +++ b/Source/System/RTEStackTrace.cpp @@ -23,4 +23,4 @@ namespace RTE { this->StackWalker::OnOutput(text); m_CallstackStream << text; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/RTEStackTrace.h b/Source/System/RTEStackTrace.h index 5da8e0abd9..dbc5d8bf40 100644 --- a/Source/System/RTEStackTrace.h +++ b/Source/System/RTEStackTrace.h @@ -47,4 +47,4 @@ namespace RTE { RTEStackTrace& operator=(const RTEStackTrace& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/RTETools.h b/Source/System/RTETools.h index 036ce714fa..e69f66454b 100644 --- a/Source/System/RTETools.h +++ b/Source/System/RTETools.h @@ -338,4 +338,4 @@ namespace RTE { } #pragma endregion } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Serializable.cpp b/Source/System/Serializable.cpp index f9731c8e5b..97f0376616 100644 --- a/Source/System/Serializable.cpp +++ b/Source/System/Serializable.cpp @@ -69,4 +69,4 @@ namespace RTE { } return writer; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/Serializable.h b/Source/System/Serializable.h index e296ee5d6a..4d7d59127c 100644 --- a/Source/System/Serializable.h +++ b/Source/System/Serializable.h @@ -206,4 +206,4 @@ namespace RTE { void Clear() {} }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Singleton.h b/Source/System/Singleton.h index 8d489b012c..36e886d136 100644 --- a/Source/System/Singleton.h +++ b/Source/System/Singleton.h @@ -49,4 +49,4 @@ namespace RTE { template Type* Singleton::s_Instance = nullptr; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/SpatialPartitionGrid.cpp b/Source/System/SpatialPartitionGrid.cpp index 5f1a000671..bc32a59191 100644 --- a/Source/System/SpatialPartitionGrid.cpp +++ b/Source/System/SpatialPartitionGrid.cpp @@ -205,4 +205,4 @@ namespace RTE { } return (wrappedY * m_Width) + wrappedX; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/SpatialPartitionGrid.h b/Source/System/SpatialPartitionGrid.h index 0d039a72e9..937aad172b 100644 --- a/Source/System/SpatialPartitionGrid.h +++ b/Source/System/SpatialPartitionGrid.h @@ -120,4 +120,4 @@ namespace RTE { SpatialPartitionGrid(const SpatialPartitionGrid& reference) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Timer.cpp b/Source/System/Timer.cpp index c52c13fa6a..e022708dd2 100644 --- a/Source/System/Timer.cpp +++ b/Source/System/Timer.cpp @@ -39,4 +39,4 @@ namespace RTE { m_TicksPerMS = static_cast(g_TimerMan.GetTicksPerSecond()) * 0.001; return 0; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/Timer.h b/Source/System/Timer.h index d777083766..50091118b3 100644 --- a/Source/System/Timer.h +++ b/Source/System/Timer.h @@ -330,4 +330,4 @@ namespace RTE { void Clear(); }; } // namespace RTE -#endif \ No newline at end of file +#endif diff --git a/Source/System/Writer.cpp b/Source/System/Writer.cpp index a37e9b18fc..fce6a592c1 100644 --- a/Source/System/Writer.cpp +++ b/Source/System/Writer.cpp @@ -69,4 +69,4 @@ namespace RTE { } } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Source/System/Writer.h b/Source/System/Writer.h index ee56bf646a..a0927776dd 100644 --- a/Source/System/Writer.h +++ b/Source/System/Writer.h @@ -246,4 +246,4 @@ namespace RTE { Writer& operator=(const Writer& rhs) = delete; }; } // namespace RTE -#endif \ No newline at end of file +#endif From 522463495db6a2943e7c9ace70e8102588ef3528 Mon Sep 17 00:00:00 2001 From: HeliumAnt Date: Sun, 21 Jan 2024 00:15:32 +0100 Subject: [PATCH 15/24] apply doxygen --- Source/Activities/ActorEditor.cpp | 87 - Source/Activities/ActorEditor.h | 210 +- Source/Activities/AreaEditor.cpp | 107 - Source/Activities/AreaEditor.h | 239 +-- Source/Activities/AssemblyEditor.cpp | 115 - Source/Activities/AssemblyEditor.h | 253 +-- Source/Activities/BaseEditor.cpp | 86 - Source/Activities/BaseEditor.h | 193 +- Source/Activities/EditorActivity.cpp | 80 - Source/Activities/EditorActivity.h | 233 +-- Source/Activities/GAScripted.cpp | 119 -- Source/Activities/GAScripted.h | 268 +-- Source/Activities/GATutorial.cpp | 99 - Source/Activities/GATutorial.h | 211 +- Source/Activities/GameActivity.cpp | 218 -- Source/Activities/GameActivity.h | 952 +++------ Source/Activities/GibEditor.cpp | 115 - Source/Activities/GibEditor.h | 252 +-- Source/Activities/MultiplayerGame.cpp | 67 - Source/Activities/MultiplayerGame.h | 171 +- Source/Activities/MultiplayerServerLobby.cpp | 58 - Source/Activities/MultiplayerServerLobby.h | 171 +- Source/Activities/SceneEditor.cpp | 107 - Source/Activities/SceneEditor.h | 241 +-- Source/Entities/ACDropShip.cpp | 72 - Source/Entities/ACDropShip.h | 271 +-- Source/Entities/ACRocket.cpp | 73 - Source/Entities/ACRocket.h | 245 +-- Source/Entities/ACrab.cpp | 175 -- Source/Entities/ACrab.h | 451 ++-- Source/Entities/ACraft.cpp | 165 -- Source/Entities/ACraft.h | 528 ++--- Source/Entities/ADSensor.cpp | 10 - Source/Entities/ADSensor.h | 50 +- Source/Entities/ADoor.cpp | 38 - Source/Entities/ADoor.h | 130 +- Source/Entities/AEJetpack.cpp | 18 - Source/Entities/AEJetpack.h | 114 +- Source/Entities/AEmitter.cpp | 79 - Source/Entities/AEmitter.h | 519 ++--- Source/Entities/AHuman.cpp | 278 --- Source/Entities/AHuman.h | 833 +++----- Source/Entities/Activity.cpp | 74 - Source/Entities/Activity.h | 518 ++--- Source/Entities/Actor.cpp | 204 -- Source/Entities/Actor.h | 1237 +++-------- Source/Entities/Arm.cpp | 34 - Source/Entities/Arm.h | 196 +- Source/Entities/AtomGroup.cpp | 56 - Source/Entities/AtomGroup.h | 327 +-- Source/Entities/Attachable.cpp | 54 - Source/Entities/Attachable.h | 381 +--- Source/Entities/BunkerAssembly.cpp | 83 - Source/Entities/BunkerAssembly.h | 239 +-- Source/Entities/BunkerAssemblyScheme.cpp | 70 - Source/Entities/BunkerAssemblyScheme.h | 262 +-- Source/Entities/Deployment.cpp | 105 - Source/Entities/Deployment.h | 384 ++-- Source/Entities/Emission.cpp | 34 - Source/Entities/Emission.h | 270 +-- Source/Entities/Gib.cpp | 8 - Source/Entities/Gib.h | 74 +- Source/Entities/GlobalScript.cpp | 22 - Source/Entities/GlobalScript.h | 64 +- Source/Entities/HDFirearm.cpp | 164 -- Source/Entities/HDFirearm.h | 780 ++----- Source/Entities/HeldDevice.cpp | 112 - Source/Entities/HeldDevice.h | 592 ++---- Source/Entities/Icon.cpp | 12 - Source/Entities/Icon.h | 46 +- Source/Entities/Leg.cpp | 22 - Source/Entities/Leg.h | 68 +- Source/Entities/LimbPath.cpp | 140 -- Source/Entities/LimbPath.h | 624 ++---- Source/Entities/Loadout.cpp | 50 - Source/Entities/Loadout.h | 183 +- Source/Entities/MOPixel.cpp | 40 - Source/Entities/MOPixel.h | 146 +- Source/Entities/MOSParticle.cpp | 28 - Source/Entities/MOSParticle.h | 92 +- Source/Entities/MOSRotating.cpp | 225 -- Source/Entities/MOSRotating.h | 772 ++----- Source/Entities/MOSprite.cpp | 111 - Source/Entities/MOSprite.h | 541 ++--- Source/Entities/Magazine.cpp | 74 - Source/Entities/Magazine.h | 272 +-- Source/Entities/Material.cpp | 8 - Source/Entities/Material.h | 92 +- Source/Entities/MetaPlayer.cpp | 8 - Source/Entities/MetaPlayer.h | 164 +- Source/Entities/MetaSave.cpp | 10 - Source/Entities/MetaSave.h | 44 +- Source/Entities/MovableObject.cpp | 176 -- Source/Entities/MovableObject.h | 1861 +++++------------ Source/Entities/PEmitter.cpp | 74 - Source/Entities/PEmitter.h | 374 +--- Source/Entities/PieMenu.cpp | 80 - Source/Entities/PieMenu.h | 328 +-- Source/Entities/PieSlice.cpp | 22 - Source/Entities/PieSlice.h | 144 +- Source/Entities/Round.cpp | 12 - Source/Entities/Round.h | 84 +- Source/Entities/SLBackground.cpp | 16 - Source/Entities/SLBackground.h | 120 +- Source/Entities/SLTerrain.cpp | 32 - Source/Entities/SLTerrain.h | 184 +- Source/Entities/Scene.cpp | 406 ---- Source/Entities/Scene.h | 1311 ++++-------- Source/Entities/SceneLayer.cpp | 54 - Source/Entities/SceneLayer.h | 248 +-- Source/Entities/SceneObject.cpp | 88 - Source/Entities/SceneObject.h | 529 ++--- Source/Entities/SoundContainer.cpp | 18 - Source/Entities/SoundContainer.h | 246 +-- Source/Entities/SoundSet.cpp | 26 - Source/Entities/SoundSet.h | 128 +- Source/Entities/TDExplosive.cpp | 14 - Source/Entities/TDExplosive.h | 44 +- Source/Entities/TerrainDebris.cpp | 18 - Source/Entities/TerrainDebris.h | 52 +- Source/Entities/TerrainFrosting.cpp | 8 - Source/Entities/TerrainFrosting.h | 10 +- Source/Entities/TerrainObject.cpp | 22 - Source/Entities/TerrainObject.h | 96 +- Source/Entities/ThrownDevice.cpp | 16 - Source/Entities/ThrownDevice.h | 70 +- Source/Entities/Turret.cpp | 22 - Source/Entities/Turret.h | 68 +- Source/GUI/GUI.h | 14 +- Source/GUI/GUIBanner.cpp | 59 - Source/GUI/GUIBanner.h | 223 +- Source/GUI/GUIButton.cpp | 56 - Source/GUI/GUIButton.h | 219 +- Source/GUI/GUICheckbox.cpp | 42 - Source/GUI/GUICheckbox.h | 175 +- Source/GUI/GUICollectionBox.cpp | 40 - Source/GUI/GUICollectionBox.h | 201 +- Source/GUI/GUIComboBox.cpp | 82 - Source/GUI/GUIComboBox.h | 382 +--- Source/GUI/GUIControl.cpp | 58 - Source/GUI/GUIControl.h | 232 +- Source/GUI/GUIControlFactory.cpp | 2 - Source/GUI/GUIControlFactory.h | 10 +- Source/GUI/GUIControlManager.cpp | 42 - Source/GUI/GUIControlManager.h | 250 +-- Source/GUI/GUIEvent.cpp | 12 - Source/GUI/GUIEvent.h | 49 +- Source/GUI/GUIFont.cpp | 28 - Source/GUI/GUIFont.h | 122 +- Source/GUI/GUIInput.cpp | 24 - Source/GUI/GUIInput.h | 95 +- Source/GUI/GUIInterface.h | 178 +- Source/GUI/GUILabel.cpp | 38 - Source/GUI/GUILabel.h | 215 +- Source/GUI/GUIListBox.cpp | 24 - Source/GUI/GUIListBox.h | 106 +- Source/GUI/GUIListPanel.cpp | 110 - Source/GUI/GUIListPanel.h | 453 +--- Source/GUI/GUIManager.cpp | 28 - Source/GUI/GUIManager.h | 131 +- Source/GUI/GUIPanel.cpp | 122 -- Source/GUI/GUIPanel.h | 521 +---- Source/GUI/GUIProgressBar.cpp | 44 - Source/GUI/GUIProgressBar.h | 182 +- Source/GUI/GUIProperties.cpp | 46 - Source/GUI/GUIProperties.h | 187 +- Source/GUI/GUIPropertyPage.cpp | 46 - Source/GUI/GUIPropertyPage.h | 191 +- Source/GUI/GUIRadioButton.cpp | 40 - Source/GUI/GUIRadioButton.h | 168 +- Source/GUI/GUIReader.cpp | 62 - Source/GUI/GUIReader.h | 86 +- Source/GUI/GUIScrollPanel.cpp | 64 - Source/GUI/GUIScrollPanel.h | 246 +-- Source/GUI/GUIScrollbar.cpp | 30 - Source/GUI/GUIScrollbar.h | 132 +- Source/GUI/GUISkin.cpp | 32 - Source/GUI/GUISkin.h | 144 +- Source/GUI/GUISlider.cpp | 60 - Source/GUI/GUISlider.h | 241 +-- Source/GUI/GUISound.cpp | 4 - Source/GUI/GUISound.h | 120 +- Source/GUI/GUITab.cpp | 40 - Source/GUI/GUITab.h | 168 +- Source/GUI/GUITextBox.cpp | 24 - Source/GUI/GUITextBox.h | 107 +- Source/GUI/GUITextPanel.cpp | 50 - Source/GUI/GUITextPanel.h | 221 +- Source/GUI/GUIUtil.cpp | 6 - Source/GUI/GUIUtil.h | 20 +- Source/GUI/GUIWriter.cpp | 58 - Source/GUI/GUIWriter.h | 66 +- Source/GUI/Wrappers/AllegroBitmap.cpp | 34 - Source/GUI/Wrappers/AllegroBitmap.h | 136 +- Source/GUI/Wrappers/AllegroScreen.cpp | 10 - Source/GUI/Wrappers/AllegroScreen.h | 56 +- Source/GUI/Wrappers/GUIInputWrapper.cpp | 12 - Source/GUI/Wrappers/GUIInputWrapper.h | 26 +- Source/Lua/LuaAdapterDefinitions.h | 198 +- Source/Lua/LuaAdapters.cpp | 120 -- Source/Lua/LuaBindingRegisterDefinitions.h | 30 - Source/Lua/LuaBindingsActivities.cpp | 4 - Source/Lua/LuaBindingsEntities.cpp | 86 - Source/Lua/LuaBindingsGUI.cpp | 6 - Source/Lua/LuaBindingsInput.cpp | 18 - Source/Lua/LuaBindingsManagers.cpp | 30 - Source/Lua/LuaBindingsMisc.cpp | 6 - Source/Lua/LuaBindingsPrimitives.cpp | 32 - Source/Lua/LuaBindingsSystem.cpp | 12 - Source/Lua/LuabindDefinitions.h | 24 +- Source/Lua/LuabindObjectWrapper.cpp | 8 - Source/Lua/LuabindObjectWrapper.h | 18 +- Source/Main.cpp | 16 - Source/Managers/ActivityMan.cpp | 38 - Source/Managers/ActivityMan.h | 160 +- Source/Managers/AudioMan.cpp | 68 - Source/Managers/AudioMan.h | 318 +-- Source/Managers/CameraMan.cpp | 24 - Source/Managers/CameraMan.h | 157 +- Source/Managers/ConsoleMan.cpp | 40 - Source/Managers/ConsoleMan.h | 98 +- Source/Managers/FrameMan.cpp | 58 - Source/Managers/FrameMan.h | 440 ++-- Source/Managers/LuaMan.cpp | 128 -- Source/Managers/LuaMan.h | 346 +-- Source/Managers/MenuMan.cpp | 20 - Source/Managers/MenuMan.h | 34 +- Source/Managers/MetaMan.cpp | 190 -- Source/Managers/MetaMan.h | 550 ++--- Source/Managers/MovableMan.cpp | 309 --- Source/Managers/MovableMan.h | 984 +++------ Source/Managers/NetworkClient.cpp | 62 - Source/Managers/NetworkClient.h | 162 +- Source/Managers/NetworkServer.cpp | 82 - Source/Managers/NetworkServer.h | 272 +-- Source/Managers/PerformanceMan.cpp | 26 - Source/Managers/PerformanceMan.h | 98 +- Source/Managers/PostProcessMan.cpp | 46 - Source/Managers/PostProcessMan.h | 174 +- Source/Managers/PresetMan.cpp | 170 -- Source/Managers/PresetMan.h | 620 ++---- Source/Managers/PrimitiveMan.cpp | 78 - Source/Managers/PrimitiveMan.h | 534 ++--- Source/Managers/SceneMan.cpp | 181 -- Source/Managers/SceneMan.h | 1607 +++++--------- Source/Managers/SettingsMan.cpp | 10 - Source/Managers/SettingsMan.h | 328 +-- Source/Managers/ThreadMan.cpp | 28 - Source/Managers/ThreadMan.h | 94 +- Source/Managers/TimerMan.cpp | 15 - Source/Managers/TimerMan.h | 104 +- Source/Managers/UInputMan.cpp | 87 - Source/Managers/UInputMan.h | 603 ++---- Source/Managers/WindowMan.cpp | 42 - Source/Managers/WindowMan.h | 176 +- Source/Menus/AreaEditorGUI.cpp | 68 - Source/Menus/AreaEditorGUI.h | 225 +- Source/Menus/AreaPickerGUI.cpp | 66 - Source/Menus/AreaPickerGUI.h | 225 +- Source/Menus/AssemblyEditorGUI.cpp | 94 - Source/Menus/AssemblyEditorGUI.h | 320 +-- Source/Menus/BuyMenuGUI.cpp | 133 -- Source/Menus/BuyMenuGUI.h | 687 ++---- Source/Menus/GibEditorGUI.cpp | 84 - Source/Menus/GibEditorGUI.h | 301 +-- Source/Menus/InventoryMenuGUI.cpp | 75 - Source/Menus/InventoryMenuGUI.h | 226 +- Source/Menus/LoadingScreen.cpp | 14 - Source/Menus/LoadingScreen.h | 32 +- Source/Menus/MainMenuGUI.cpp | 50 - Source/Menus/MainMenuGUI.h | 92 +- Source/Menus/MetagameGUI.cpp | 317 --- Source/Menus/MetagameGUI.h | 840 +++----- Source/Menus/ModManagerGUI.cpp | 14 - Source/Menus/ModManagerGUI.h | 54 +- Source/Menus/MultiplayerGameGUI.cpp | 38 - Source/Menus/MultiplayerGameGUI.h | 125 +- Source/Menus/ObjectPickerGUI.cpp | 44 - Source/Menus/ObjectPickerGUI.h | 150 +- Source/Menus/PauseMenuGUI.cpp | 22 - Source/Menus/PauseMenuGUI.h | 56 +- Source/Menus/SaveLoadMenuGUI.cpp | 22 - Source/Menus/SaveLoadMenuGUI.h | 44 +- Source/Menus/ScenarioActivityConfigGUI.cpp | 24 - Source/Menus/ScenarioActivityConfigGUI.h | 60 +- Source/Menus/ScenarioGUI.cpp | 36 - Source/Menus/ScenarioGUI.h | 84 +- Source/Menus/SceneEditorGUI.cpp | 96 - Source/Menus/SceneEditorGUI.h | 286 +-- Source/Menus/SettingsAudioGUI.cpp | 12 - Source/Menus/SettingsAudioGUI.h | 22 +- Source/Menus/SettingsGUI.cpp | 14 - Source/Menus/SettingsGUI.h | 36 +- Source/Menus/SettingsGameplayGUI.cpp | 14 - Source/Menus/SettingsGameplayGUI.h | 24 +- Source/Menus/SettingsInputGUI.cpp | 16 - Source/Menus/SettingsInputGUI.h | 58 +- Source/Menus/SettingsInputMappingGUI.cpp | 24 - Source/Menus/SettingsInputMappingGUI.h | 48 +- .../Menus/SettingsInputMappingWizardGUI.cpp | 44 - Source/Menus/SettingsInputMappingWizardGUI.h | 82 +- Source/Menus/SettingsMiscGUI.cpp | 8 - Source/Menus/SettingsMiscGUI.h | 18 +- Source/Menus/SettingsVideoGUI.cpp | 27 - Source/Menus/SettingsVideoGUI.h | 74 +- Source/Menus/TitleScreen.cpp | 28 - Source/Menus/TitleScreen.h | 74 +- Source/System/AllegroTools.h | 6 - Source/System/Atom.cpp | 38 - Source/System/Atom.h | 293 +-- Source/System/Box.cpp | 28 - Source/System/Box.h | 206 +- Source/System/Color.cpp | 12 - Source/System/Color.h | 82 +- Source/System/Constants.h | 16 - Source/System/ContentFile.cpp | 42 - Source/System/ContentFile.h | 156 +- Source/System/Controller.cpp | 38 - Source/System/Controller.h | 198 +- Source/System/DataModule.cpp | 40 - Source/System/DataModule.h | 237 +-- Source/System/Entity.cpp | 42 - Source/System/Entity.h | 258 +-- Source/System/GLCheck.h | 4 - Source/System/Gamepad.h | 32 +- Source/System/GenericSavedData.cpp | 20 - Source/System/GenericSavedData.h | 32 +- Source/System/GraphicalPrimitive.cpp | 36 - Source/System/GraphicalPrimitive.h | 291 +-- Source/System/InputMapping.cpp | 8 - Source/System/InputMapping.h | 64 +- Source/System/InputScheme.cpp | 18 - Source/System/InputScheme.h | 108 +- Source/System/Matrix.cpp | 26 - Source/System/Matrix.h | 214 +- Source/System/NetworkMessages.h | 46 - Source/System/PathFinder.cpp | 44 - Source/System/PathFinder.h | 169 +- Source/System/PieQuadrant.cpp | 12 - Source/System/PieQuadrant.h | 32 +- Source/System/RTEError.cpp | 22 - Source/System/RTEError.h | 50 +- Source/System/RTEStackTrace.cpp | 4 - Source/System/RTEStackTrace.h | 20 +- Source/System/RTETools.cpp | 32 - Source/System/RTETools.h | 224 +- Source/System/Reader.cpp | 32 - Source/System/Reader.h | 148 +- Source/System/Serializable.cpp | 12 - Source/System/Serializable.h | 99 +- Source/System/Shader.cpp | 21 - Source/System/Shader.h | 192 +- Source/System/Singleton.h | 12 +- Source/System/SpatialPartitionGrid.cpp | 17 - Source/System/SpatialPartitionGrid.h | 68 +- Source/System/StandardIncludes.h | 2 - Source/System/System.cpp | 16 - Source/System/System.h | 104 +- Source/System/Timer.cpp | 8 - Source/System/Timer.h | 194 +- Source/System/Vector.cpp | 4 - Source/System/Vector.h | 390 ++-- Source/System/Writer.cpp | 12 - Source/System/Writer.h | 96 +- 364 files changed, 11809 insertions(+), 41934 deletions(-) diff --git a/Source/Activities/ActorEditor.cpp b/Source/Activities/ActorEditor.cpp index 1318a7e5e2..3aac4083bd 100644 --- a/Source/Activities/ActorEditor.cpp +++ b/Source/Activities/ActorEditor.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ActorEditor.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the ActorEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "ActorEditor.h" #include "PresetMan.h" @@ -35,22 +23,11 @@ namespace RTE { ConcreteClassInfo(ActorEditor, EditorActivity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ActorEditor, effectively - // resetting the members of this abstraction level only. - void ActorEditor::Clear() { m_pEditedActor = 0; m_pPicker = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ActorEditor object ready for use. - int ActorEditor::Create() { if (EditorActivity::Create() < 0) return -1; @@ -60,11 +37,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ActorEditor to be identical to another, by deep copy. - int ActorEditor::Create(const ActorEditor& reference) { if (EditorActivity::Create(reference) < 0) return -1; @@ -75,14 +47,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int ActorEditor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); /* @@ -93,22 +57,11 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this ActorEditor with a Writer for - // later recreation with Create(Reader &reader); - int ActorEditor::Save(Writer& writer) const { EditorActivity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the ActorEditor object. - void ActorEditor::Destroy(bool notInherited) { delete m_pEditedActor; delete m_pPicker; @@ -118,12 +71,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int ActorEditor::Start() { int error = EditorActivity::Start(); @@ -144,33 +91,17 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void ActorEditor::SetPaused(bool pause) { // Override the pause m_Paused = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void ActorEditor::End() { EditorActivity::End(); m_ActivityState = ActivityState::Over; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActorEditor. Supposed to be done every frame - // before drawing. - void ActorEditor::Update() { // And object hasn't been loaded yet, so get the loading picker going if (!m_pEditedActor) { @@ -245,11 +176,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void ActorEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { // Draw the edited actor and pie menu if (m_pEditedActor) { @@ -266,21 +192,10 @@ namespace RTE { EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActorEditor's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void ActorEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { EditorActivity::Draw(pTargetBitmap, targetPos); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reloads the Actor itself and sets up the pie menu to match its setup. - bool ActorEditor::LoadActor(const Entity* pActorToLoad) { if (!pActorToLoad) return false; @@ -312,8 +227,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActorEditor::ReloadActorData() { if (m_pEditedActor) { std::string presetName = m_pEditedActor->GetPresetName(); diff --git a/Source/Activities/ActorEditor.h b/Source/Activities/ActorEditor.h index 676276ab8d..87103a9b0a 100644 --- a/Source/Activities/ActorEditor.h +++ b/Source/Activities/ActorEditor.h @@ -1,18 +1,11 @@ #ifndef _RTEACTOREDITOR_ #define _RTEACTOREDITOR_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ActorEditor.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the ActorEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the ActorEditor class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" @@ -32,183 +25,92 @@ namespace RTE { class GUILabel; class GUIComboBox; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: ActorEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activity for testing and quickly iterating on Actor data definitions. - // Parent(s): EditorActivity. - // Class history: 10/08/2007 ActorEditor Created. - + /// Activity for testing and quickly iterating on Actor data definitions. class ActorEditor : public EditorActivity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(ActorEditor); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: ActorEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a ActorEditor object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a ActorEditor object in system + /// memory. Create() should be called before using the object. ActorEditor() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~ActorEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a ActorEditor object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a ActorEditor object before deletion + /// from system memory. ~ActorEditor() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ActorEditor object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the ActorEditor object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ActorEditor to be identical to another, by deep copy. - // Arguments: A reference to the ActorEditor to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a ActorEditor to be identical to another, by deep copy. + /// @param reference A reference to the ActorEditor to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const ActorEditor& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire ActorEditor, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire ActorEditor, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); EditorActivity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the ActorEditor object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the ActorEditor object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reloads the Actor itself and sets up the pie menu to match its setup. - // Arguments: An Entity Preset of the Actor to load into the editor. OWNERSHIP IS NOT TRANSFERRED! - // Return value: Whether the Actor was loaded successfully from the PresetMan. - + /// Reloads the Actor itself and sets up the pie menu to match its setup. + /// @param pActorToLoad An Entity Preset of the Actor to load into the editor. OWNERSHIP IS NOT TRANSFERRED! + /// @return Whether the Actor was loaded successfully from the PresetMan. bool LoadActor(const Entity* pActorToLoad); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReloadActorData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reloads the ini with the currently edited Actor's definitions. - // Arguments: None. - // Return value: Whether the data file was successfully read. - + /// Reloads the ini with the currently edited Actor's definitions. + /// @return Whether the data file was successfully read. bool ReloadActorData(); // Member variables @@ -219,18 +121,10 @@ namespace RTE { // The picker for selecting which object to load ObjectPickerGUI* m_pPicker; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/AreaEditor.cpp b/Source/Activities/AreaEditor.cpp index 5a9d87edff..9a75b433f5 100644 --- a/Source/Activities/AreaEditor.cpp +++ b/Source/Activities/AreaEditor.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AreaEditor.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the AreaEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "AreaEditor.h" #include "WindowMan.h" @@ -42,22 +30,11 @@ namespace RTE { ConcreteClassInfo(AreaEditor, EditorActivity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this AreaEditor, effectively - // resetting the members of this abstraction level only. - void AreaEditor::Clear() { m_pEditorGUI = 0; m_pNewAreaName = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the AreaEditor object ready for use. - int AreaEditor::Create() { if (EditorActivity::Create() < 0) return -1; @@ -65,11 +42,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a AreaEditor to be identical to another, by deep copy. - int AreaEditor::Create(const AreaEditor& reference) { if (EditorActivity::Create(reference) < 0) return -1; @@ -80,14 +52,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int AreaEditor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); /* @@ -98,22 +62,11 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this AreaEditor with a Writer for - // later recreation with Create(Reader &reader); - int AreaEditor::Save(Writer& writer) const { EditorActivity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the AreaEditor object. - void AreaEditor::Destroy(bool notInherited) { delete m_pEditorGUI; @@ -122,12 +75,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int AreaEditor::Start() { int error = EditorActivity::Start(); @@ -212,33 +159,17 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void AreaEditor::SetPaused(bool pause) { // Override the pause m_Paused = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void AreaEditor::End() { EditorActivity::End(); m_ActivityState = ActivityState::Over; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this AreaEditor. Supposed to be done every frame - // before drawing. - void AreaEditor::Update() { EditorActivity::Update(); @@ -498,29 +429,16 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void AreaEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { m_pEditorGUI->Draw(pTargetBitmap, targetPos); EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this AreaEditor's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void AreaEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { EditorActivity::Draw(pTargetBitmap, targetPos); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AreaEditor::SaveScene(const std::string& saveAsName, bool forceOverwrite) { Scene* editedScene = g_SceneMan.GetScene(); editedScene->SetPresetName(saveAsName); @@ -589,22 +507,12 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateNewDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New dialog box, populates its lists etc. - void AreaEditor::UpdateNewDialog() { // Reset the new Area name text field if (m_pNewAreaName->GetText().empty()) m_pNewAreaName->SetText("Test Area 1"); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateLoadDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Load dialog box, populates its lists etc. - void AreaEditor::UpdateLoadDialog() { // Clear out the control m_pLoadNameCombo->ClearList(); @@ -626,11 +534,6 @@ namespace RTE { m_pLoadNameCombo->SetSelectedIndex(0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateSaveDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save dialog box, populates its lists etc. - void AreaEditor::UpdateSaveDialog() { m_pSaveNameBox->SetText((g_SceneMan.GetScene()->GetPresetName() == "None" || !m_HasEverBeenSaved) ? "New Scene" : g_SceneMan.GetScene()->GetPresetName()); if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) @@ -639,11 +542,6 @@ namespace RTE { m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes"); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateChangesDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save Changes dialog box, populates its lists etc. - void AreaEditor::UpdateChangesDialog() { if (m_HasEverBeenSaved) { dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); @@ -657,11 +555,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateOverwriteDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Overwrite dialog box, populates its lists etc. - void AreaEditor::UpdateOverwriteDialog() { if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); diff --git a/Source/Activities/AreaEditor.h b/Source/Activities/AreaEditor.h index 26378b247d..52f4b183ae 100644 --- a/Source/Activities/AreaEditor.h +++ b/Source/Activities/AreaEditor.h @@ -1,18 +1,11 @@ #ifndef _RTEAREAEDITOR_ #define _RTEAREAEDITOR_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AreaEditor.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the AreaEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the AreaEditor class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" @@ -30,218 +23,104 @@ namespace RTE { class GUILabel; class GUIComboBox; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: AreaEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activity for editing Scene:s' Area:s. - // Parent(s): EditorActivity. - // Class history: 7/21/2008 AreaEditor created, based off SceneEditor - + /// Activity for editing Scene:s' Area:s. class AreaEditor : public EditorActivity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(AreaEditor); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: AreaEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a AreaEditor object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a AreaEditor object in system + /// memory. Create() should be called before using the object. AreaEditor() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~AreaEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a AreaEditor object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a AreaEditor object before deletion + /// from system memory. ~AreaEditor() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the AreaEditor object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the AreaEditor object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a AreaEditor to be identical to another, by deep copy. - // Arguments: A reference to the AreaEditor to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a AreaEditor to be identical to another, by deep copy. + /// @param reference A reference to the AreaEditor to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const AreaEditor& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire AreaEditor, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire AreaEditor, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); EditorActivity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the AreaEditor object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the AreaEditor object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Saves the current Scene to an appropriate ini file, and asks user if they want to overwrite first if scene of this name exists. - /// - /// The name of the new Scene to be saved. - /// Whether to force any existing Scene of that name to be overwritten if it already exists. - /// Whether actually managed to save. Will return false both if a scene of this name already exists, or if other error. + /// @param saveAsName The name of the new Scene to be saved. + /// @param forceOverwrite Whether to force any existing Scene of that name to be overwritten if it already exists. + /// @return Whether actually managed to save. Will return false both if a scene of this name already exists, or if other error. bool SaveScene(const std::string& saveAsName, bool forceOverwrite = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateNewDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the New dialog box, populates its lists etc. void UpdateNewDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateLoadDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Load dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Load dialog box, populates its lists etc. void UpdateLoadDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateSaveDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save dialog box, populates its lists etc. void UpdateSaveDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateChangesDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save Changes dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save Changes dialog box, populates its lists etc. void UpdateChangesDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateOverwriteDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Overwrite dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Overwrite dialog box, populates its lists etc. void UpdateOverwriteDialog() override; // Member variables @@ -255,18 +134,10 @@ namespace RTE { // // Number which // int m_NewAreaNumber; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/AssemblyEditor.cpp b/Source/Activities/AssemblyEditor.cpp index 20677ef294..1d59d5df33 100644 --- a/Source/Activities/AssemblyEditor.cpp +++ b/Source/Activities/AssemblyEditor.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AssemblyEditor.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the AssemblyEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "AssemblyEditor.h" #include "WindowMan.h" @@ -42,22 +30,11 @@ namespace RTE { ConcreteClassInfo(AssemblyEditor, EditorActivity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this AssemblyEditor, effectively - // resetting the members of this abstraction level only. - void AssemblyEditor::Clear() { m_pEditorGUI = 0; m_pModuleCombo = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the AssemblyEditor object ready for use. - int AssemblyEditor::Create() { if (EditorActivity::Create() < 0) return -1; @@ -65,11 +42,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a AssemblyEditor to be identical to another, by deep copy. - int AssemblyEditor::Create(const AssemblyEditor& reference) { if (EditorActivity::Create(reference) < 0) return -1; @@ -80,14 +52,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int AssemblyEditor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); /* @@ -98,22 +62,11 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this AssemblyEditor with a Writer for - // later recreation with Create(Reader &reader); - int AssemblyEditor::Save(Writer& writer) const { EditorActivity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the AssemblyEditor object. - void AssemblyEditor::Destroy(bool notInherited) { delete m_pEditorGUI; @@ -122,12 +75,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int AssemblyEditor::Start() { int error = EditorActivity::Start(); @@ -198,33 +145,17 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void AssemblyEditor::SetPaused(bool pause) { // Override the pause m_Paused = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void AssemblyEditor::End() { EditorActivity::End(); m_ActivityState = ActivityState::Over; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this AssemblyEditor. Supposed to be done every frame - // before drawing. - void AssemblyEditor::Update() { EditorActivity::Update(); @@ -427,33 +358,16 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void AssemblyEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { m_pEditorGUI->Draw(pTargetBitmap, targetPos); EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this AssemblyEditor's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void AssemblyEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { EditorActivity::Draw(pTargetBitmap, targetPos); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildAssembly - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and builds assembly which fits currently selected scheme and returns - // it's pointer. Owhership IS transfered. - BunkerAssembly* AssemblyEditor::BuildAssembly(std::string saveAsName) { // Create new bunker assembly to save BunkerAssembly* pBA = new BunkerAssembly(); @@ -531,8 +445,6 @@ namespace RTE { return pBA; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AssemblyEditor::SaveAssembly(const std::string& saveAsName, bool forceOverwrite) { std::unique_ptr editedAssembly(BuildAssembly(saveAsName)); @@ -601,21 +513,9 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateNewDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - void AssemblyEditor::UpdateNewDialog() { } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateLoadDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Load dialog box, populates its lists etc. - void AssemblyEditor::UpdateLoadDialog() { int scenesIndex = 0; @@ -676,11 +576,6 @@ namespace RTE { m_pLoadNameCombo->SetSelectedIndex(0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateSaveDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save dialog box, populates its lists etc. - void AssemblyEditor::UpdateSaveDialog() { std::string defaultName = ""; @@ -692,11 +587,6 @@ namespace RTE { m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateChangesDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save Changes dialog box, populates its lists etc. - void AssemblyEditor::UpdateChangesDialog() { if (m_HasEverBeenSaved) { dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); @@ -707,11 +597,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateOverwriteDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Overwrite dialog box, populates its lists etc. - void AssemblyEditor::UpdateOverwriteDialog() { m_pOverwriteNameLabel->SetText(m_pEditorGUI->GetCurrentAssemblyName()); } diff --git a/Source/Activities/AssemblyEditor.h b/Source/Activities/AssemblyEditor.h index 37e714b7c4..7adbfad149 100644 --- a/Source/Activities/AssemblyEditor.h +++ b/Source/Activities/AssemblyEditor.h @@ -1,18 +1,11 @@ #ifndef _RTEASSEMBLYEDITOR_ #define _RTEASSEMBLYEDITOR_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AssemblyEditor.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the AssemblyEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the AssemblyEditor class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" #include "BunkerAssembly.h" @@ -31,229 +24,111 @@ namespace RTE { class GUILabel; class GUIComboBox; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: AssemblyEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activity for editing scenes. - // Parent(s): EditorActivity. - // Class history: 8/30/2007 AssemblyEditor created, inheriting directly from Activity. - // 9/17/2007 Spliced out and made to derive from EditorActivty - + /// Activity for editing scenes. + /// 9/17/2007 Spliced out and made to derive from EditorActivty class AssemblyEditor : public EditorActivity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(AssemblyEditor); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: AssemblyEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a AssemblyEditor object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a AssemblyEditor object in system + /// memory. Create() should be called before using the object. AssemblyEditor() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~AssemblyEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a AssemblyEditor object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a AssemblyEditor object before deletion + /// from system memory. ~AssemblyEditor() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the AssemblyEditor object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the AssemblyEditor object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a AssemblyEditor to be identical to another, by deep copy. - // Arguments: A reference to the AssemblyEditor to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a AssemblyEditor to be identical to another, by deep copy. + /// @param reference A reference to the AssemblyEditor to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const AssemblyEditor& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire AssemblyEditor, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire AssemblyEditor, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); EditorActivity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the AssemblyEditor object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the AssemblyEditor object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildAssembly - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and builds assembly which fits currently selected scheme and returns - // it's pointer. Owhership IS transfered. - // Arguments: New assembly name. - // Return value: Built BunkerAssembly - + /// Creates and builds assembly which fits currently selected scheme and returns + /// it's pointer. Owhership IS transfered. + /// @param saveAsName New assembly name. + /// @return Built BunkerAssembly BunkerAssembly* BuildAssembly(std::string saveAsName); - /// /// Saves the current BunkerAssembly to an appropriate ini file, and asks user if they want to overwrite first if a BunkerAssembly of this name exists. - /// - /// The name of the new BunkerAssembly to be saved. - /// Whether to force any existing BunkerAssembly of that name to be overwritten if it already exists. - /// Whether actually managed to save. Will return false both if a BunkerAssembly of this name already exists, or if other error. + /// @param saveAsName The name of the new BunkerAssembly to be saved. + /// @param forceOverwrite Whether to force any existing BunkerAssembly of that name to be overwritten if it already exists. + /// @return Whether actually managed to save. Will return false both if a BunkerAssembly of this name already exists, or if other error. bool SaveAssembly(const std::string& saveAsName, bool forceOverwrite = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateNewDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the New dialog box, populates its lists etc. void UpdateNewDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateLoadDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Load dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Load dialog box, populates its lists etc. void UpdateLoadDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateSaveDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save dialog box, populates its lists etc. void UpdateSaveDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateChangesDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save Changes dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save Changes dialog box, populates its lists etc. void UpdateChangesDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateOverwriteDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Overwrite dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Overwrite dialog box, populates its lists etc. void UpdateOverwriteDialog() override; // Member variables @@ -264,18 +139,10 @@ namespace RTE { GUIComboBox* m_pModuleCombo; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/BaseEditor.cpp b/Source/Activities/BaseEditor.cpp index b79e658168..a1c4daaab0 100644 --- a/Source/Activities/BaseEditor.cpp +++ b/Source/Activities/BaseEditor.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: BaseEditor.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the BaseEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "BaseEditor.h" #include "CameraMan.h" @@ -37,22 +25,11 @@ namespace RTE { ConcreteClassInfo(BaseEditor, Activity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this BaseEditor, effectively - // resetting the members of this abstraction level only. - void BaseEditor::Clear() { m_pEditorGUI = 0; m_NeedSave = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the BaseEditor object ready for use. - int BaseEditor::Create() { if (Activity::Create() < 0) return -1; @@ -60,11 +37,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a BaseEditor to be identical to another, by deep copy. - int BaseEditor::Create(const BaseEditor& reference) { if (Activity::Create(reference) < 0) return -1; @@ -75,14 +47,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int BaseEditor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Activity::ReadProperty(propName, reader)); /* @@ -93,22 +57,11 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this BaseEditor with a Writer for - // later recreation with Create(Reader &reader); - int BaseEditor::Save(Writer& writer) const { Activity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the BaseEditor object. - void BaseEditor::Destroy(bool notInherited) { delete m_pEditorGUI; @@ -117,12 +70,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int BaseEditor::Start() { // Set the split screen config before the Scene (and it SceneLayers, specifially) are loaded g_FrameMan.ResetSplitScreens(false, false); @@ -234,33 +181,17 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void BaseEditor::SetPaused(bool pause) { // Override the pause m_Paused = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void BaseEditor::End() { Activity::End(); m_ActivityState = ActivityState::Over; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this BaseEditor. Supposed to be done every frame - // before drawing. - void BaseEditor::Update() { Activity::Update(); @@ -291,33 +222,16 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void BaseEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { m_pEditorGUI->Draw(pTargetBitmap, targetPos); Activity::DrawGUI(pTargetBitmap, targetPos, which); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this BaseEditor's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void BaseEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { Activity::Draw(pTargetBitmap, targetPos); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SaveScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the current scene to an appropriate ini file, and asks user if - // they want to overwrite first if scene of this name exists. - bool BaseEditor::SaveScene(std::string saveAsName, bool forceOverwrite) { /* // Set the name of the current scene in effect diff --git a/Source/Activities/BaseEditor.h b/Source/Activities/BaseEditor.h index 555004ab79..90133cfabe 100644 --- a/Source/Activities/BaseEditor.h +++ b/Source/Activities/BaseEditor.h @@ -1,18 +1,11 @@ #ifndef _RTEBASEEDITOR_ #define _RTEBASEEDITOR_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: BaseEditor.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the BaseEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the BaseEditor class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" @@ -30,161 +23,85 @@ namespace RTE { class GUILabel; class GUIComboBox; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: BaseEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activity for editing designs only for the bases of each scene in a - // Campaign metagame. Doesn't need all the document-model dlg boxes. - // Parent(s): Activity. - // Class history: 04/30/2010 Created BaseEditor for the campaign base blueprint editing. - + /// Activity for editing designs only for the bases of each scene in a + /// Campaign metagame. Doesn't need all the document-model dlg boxes. class BaseEditor : public Activity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(BaseEditor); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: BaseEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a BaseEditor object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a BaseEditor object in system + /// memory. Create() should be called before using the object. BaseEditor() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~BaseEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a BaseEditor object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a BaseEditor object before deletion + /// from system memory. ~BaseEditor() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the BaseEditor object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the BaseEditor object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a BaseEditor to be identical to another, by deep copy. - // Arguments: A reference to the BaseEditor to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a BaseEditor to be identical to another, by deep copy. + /// @param reference A reference to the BaseEditor to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const BaseEditor& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire BaseEditor, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire BaseEditor, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Activity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the BaseEditor object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the BaseEditor object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SaveScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the current scene to an appropriate ini file, and asks user if - // they want to overwrite first if scene of this name exists. - // Arguments: The name of the new scene to be saved. - // Whetehr to force any existing Scene of that name to be overwritten if - // it already exists. - // Return value: Whether actually managed to save. Will return false both if a scene - // of this name already exists, or if other error. - + /// Saves the current scene to an appropriate ini file, and asks user if + /// they want to overwrite first if scene of this name exists. + /// @param saveAsName The name of the new scene to be saved. + /// @param forceOverwrite Whetehr to force any existing Scene of that name to be overwritten if (default: false) + /// it already exists. + /// @return Whether actually managed to save. Will return false both if a scene + /// of this name already exists, or if other error. bool SaveScene(std::string saveAsName, bool forceOverwrite = false); // Member variables @@ -196,18 +113,10 @@ namespace RTE { // Dirty Scene? bool m_NeedSave; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/EditorActivity.cpp b/Source/Activities/EditorActivity.cpp index 682b46899b..d068fde784 100644 --- a/Source/Activities/EditorActivity.cpp +++ b/Source/Activities/EditorActivity.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: EditorActivity.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the EditorActivity class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "EditorActivity.h" #include "PresetMan.h" @@ -36,12 +24,6 @@ namespace RTE { AbstractClassInfo(EditorActivity, Activity); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this EditorActivity, effectively - // resetting the members of this abstraction level only. - void EditorActivity::Clear() { // Most editors are single player affairs m_MaxPlayerSupport = 1; @@ -80,11 +62,6 @@ namespace RTE { m_pOverwriteNoButton = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the EditorActivity object ready for use. - int EditorActivity::Create() { if (Activity::Create() < 0) return -1; @@ -92,11 +69,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a EditorActivity to be identical to another, by deep copy. - int EditorActivity::Create(const EditorActivity& reference) { if (Activity::Create(reference) < 0) return -1; @@ -113,14 +85,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int EditorActivity::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(Activity::ReadProperty(propName, reader)); /* @@ -131,22 +95,11 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this EditorActivity with a Writer for - // later recreation with Create(Reader &reader); - int EditorActivity::Save(Writer& writer) const { Activity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the EditorActivity object. - void EditorActivity::Destroy(bool notInherited) { delete m_pGUIController; delete m_pGUIInput; @@ -157,12 +110,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int EditorActivity::Start() { int error = 0; // Don't, too different @@ -211,33 +158,17 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void EditorActivity::SetPaused(bool pause) { // Override the pause m_Paused = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void EditorActivity::End() { Activity::End(); m_ActivityState = ActivityState::Over; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this EditorActivity. Supposed to be done every frame - // before drawing. - void EditorActivity::Update() { // Always show teh messages of the editor m_MessageTimer[0].Reset(); @@ -557,11 +488,6 @@ namespace RTE { */ } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void EditorActivity::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { AllegroScreen drawScreen(pTargetBitmap); m_pGUIController->Draw(&drawScreen); @@ -569,12 +495,6 @@ namespace RTE { m_pGUIController->DrawMouse(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this EditorActivity's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void EditorActivity::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { } diff --git a/Source/Activities/EditorActivity.h b/Source/Activities/EditorActivity.h index aaf129e3ad..7a4096a2ef 100644 --- a/Source/Activities/EditorActivity.h +++ b/Source/Activities/EditorActivity.h @@ -1,18 +1,11 @@ #ifndef _RTEEDITORACTIVITY_ #define _RTEEDITORACTIVITY_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: EditorActivity.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the EditorActivity class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the EditorActivity class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "GUISound.h" #include "RTETools.h" #include "ActivityMan.h" @@ -32,19 +25,11 @@ namespace RTE { class GUILabel; class GUIComboBox; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: EditorActivity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activity for editing things; owns and manages all the dialog boxes - // etc for docuemnt model, ie new, load, save, etc. - // Parent(s): Activity. - // Class history: 9/17/2007 EditorActivity created. - + /// Activity for editing things; owns and manages all the dialog boxes + /// etc for docuemnt model, ie new, load, save, etc. class EditorActivity : public Activity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableOverrideMethods; ClassInfoGetters; @@ -61,192 +46,88 @@ namespace RTE { EDITORMODECOUNT }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: EditorActivity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a EditorActivity object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a EditorActivity object in system + /// memory. Create() should be called before using the object. EditorActivity() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~EditorActivity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a EditorActivity object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a EditorActivity object before deletion + /// from system memory. ~EditorActivity() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the EditorActivity object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the EditorActivity object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a EditorActivity to be identical to another, by deep copy. - // Arguments: A reference to the EditorActivity to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a EditorActivity to be identical to another, by deep copy. + /// @param reference A reference to the EditorActivity to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const EditorActivity& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire EditorActivity, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire EditorActivity, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Activity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the EditorActivity object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the EditorActivity object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorMode(EditorMode newMode) { m_EditorMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorMode GetEditorMode() const { return m_EditorMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateNewDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the New dialog box, populates its lists etc. virtual void UpdateNewDialog() {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateLoadDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Load dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Load dialog box, populates its lists etc. virtual void UpdateLoadDialog() {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateSaveDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save dialog box, populates its lists etc. virtual void UpdateSaveDialog() {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateChangesDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save Changes dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save Changes dialog box, populates its lists etc. virtual void UpdateChangesDialog() {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateOverwriteDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Overwrite dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Overwrite dialog box, populates its lists etc. virtual void UpdateOverwriteDialog() {} // Member variables @@ -322,18 +203,10 @@ namespace RTE { // The button for No resposnes to overwrite GUIButton* m_pOverwriteNoButton; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/GAScripted.cpp b/Source/Activities/GAScripted.cpp index ece23f7c4a..17b5930521 100644 --- a/Source/Activities/GAScripted.cpp +++ b/Source/Activities/GAScripted.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GAScripted.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the GAScripted class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "GAScripted.h" #include "SceneMan.h" @@ -40,12 +28,6 @@ namespace RTE { ConcreteClassInfo(GAScripted, GameActivity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this GAScripted, effectively - // resetting the members of this abstraction level only. - void GAScripted::Clear() { m_ScriptPath.clear(); m_LuaClassName.clear(); @@ -53,11 +35,6 @@ namespace RTE { m_PieSlicesToAdd.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GAScripted object ready for use. - int GAScripted::Create() { if (GameActivity::Create() < 0) { return -1; @@ -82,11 +59,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a GAScripted to be identical to another, by deep copy. - int GAScripted::Create(const GAScripted& reference) { if (GameActivity::Create(reference) < 0) { return -1; @@ -104,14 +76,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int GAScripted::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return GameActivity::ReadProperty(propName, reader)); @@ -128,12 +92,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this GAScripted with a Writer for - // later recreation with Create(Reader &reader); - int GAScripted::Save(Writer& writer) const { // Hmm. We should probably be calling this prior to the writer Save, instead of const-casting. const_cast(this)->RunLuaFunction("OnSave"); @@ -150,11 +108,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the GAScripted object. - void GAScripted::Destroy(bool notInherited) { // Delete global scripts for (std::vector::iterator sItr = m_GlobalScriptsList.begin(); sItr < m_GlobalScriptsList.end(); ++sItr) { @@ -170,14 +123,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReloadScripts - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reloads the preset scripts of this object, from the same script file - // path as was originally defined. This will also update the original - // preset in the PresetMan with the updated scripts so future objects - // spawned will use the new scripts. - int GAScripted::ReloadScripts() { if (m_ScriptPath.empty()) { return 0; @@ -206,8 +151,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GAScripted::RefreshActivityFunctions() { m_ScriptFunctions.clear(); if (m_ScriptPath.empty()) { @@ -223,20 +166,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GAScripted::HasSaveFunction() const { return m_ScriptFunctions.find("OnSave") != m_ScriptFunctions.end(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SceneIsCompatible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells if a particular Scene supports this specific Activity on it. - // Usually that means certain Area:s need to be defined in the Scene. - bool GAScripted::SceneIsCompatible(Scene* pScene, int teams) { if (!GameActivity::SceneIsCompatible(pScene, teams)) { return false; @@ -279,8 +212,6 @@ namespace RTE { return g_LuaMan.GetMasterScriptState().GlobalIsDefined("TestScene"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GAScripted::HandleCraftEnteringOrbit(ACraft* orbitedCraft) { GameActivity::HandleCraftEnteringOrbit(orbitedCraft); @@ -292,14 +223,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int GAScripted::Start() { ActivityState initialActivityState = m_ActivityState; @@ -349,11 +272,6 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void GAScripted::SetPaused(bool pause) { GameActivity::SetPaused(pause); @@ -365,11 +283,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void GAScripted::End() { GameActivity::End(); @@ -400,12 +313,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this GAScripted. Supposed to be done every frame - // before drawing. - void GAScripted::Update() { GameActivity::Update(); @@ -434,11 +341,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateGlobalScripts - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates globals scripts loaded with this activity. - void GAScripted::UpdateGlobalScripts(bool lateUpdate) { ZoneScoped; @@ -450,21 +352,10 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void GAScripted::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { GameActivity::DrawGUI(pTargetBitmap, targetPos, which); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this GAScripted's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void GAScripted::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { GameActivity::Draw(pTargetBitmap, targetPos); } @@ -481,12 +372,6 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollectRequiredAreas - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through the script file and checks for any mentions and uses of - // Area:s that are required for this Activity to run in a Scene. - void GAScripted::CollectRequiredAreas() { // Open the script file so we can check it out std::ifstream scriptFile = std::ifstream(g_PresetMan.GetFullModulePath(m_ScriptPath.c_str())); @@ -543,8 +428,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GAScripted::AddPieSlicesToActiveActorPieMenus() { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (m_IsActive[player] && m_IsHuman[player] && m_ControlledActor[player] && m_ViewState[player] != ViewState::DeathWatch && m_ViewState[player] != ViewState::ActorSelect && m_ViewState[player] != ViewState::AIGoToPoint && m_ViewState[player] != ViewState::UnitSelectCircle) { @@ -563,6 +446,4 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - } // namespace RTE diff --git a/Source/Activities/GAScripted.h b/Source/Activities/GAScripted.h index 080dc6e170..4752b75ba4 100644 --- a/Source/Activities/GAScripted.h +++ b/Source/Activities/GAScripted.h @@ -1,18 +1,11 @@ #ifndef _RTEGASCRIPTED_ #define _RTEGASCRIPTED_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GAScripted.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the ActivityMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the ActivityMan class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "GameActivity.h" #include "GlobalScript.h" #include "Box.h" @@ -23,21 +16,13 @@ namespace RTE { class ACraft; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: GAScripted - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Scripted activity - // Parent(s): GameActivity. - // Class history: 07/03/2008 GAScripted created. - + /// Scripted activity class GAScripted : public GameActivity { friend class LuaMan; friend class ActivityMan; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: ScriptFunctionNames("StartActivity", "UpdateActivity", "PauseActivity", "EndActivity", "OnSave", "CraftEnteredOrbit", "OnMessage", "OnGlobalMessage"); @@ -46,219 +31,116 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GAScripted - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GAScripted object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a GAScripted object in system + /// memory. Create() should be called before using the object. GAScripted() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~GAScripted - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a GAScripted object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a GAScripted object before deletion + /// from system memory. ~GAScripted() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GAScripted object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the GAScripted object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GAScripted object ready for use. - // Arguments: The filepath to the script that defines this' Lua-defined derivation - // of this class. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the GAScripted object ready for use. + /// @param scriptPath The filepath to the script that defines this' Lua-defined derivation + /// of this class. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(std::string scriptPath, std::string scriptClassName) { m_ScriptPath = scriptPath; m_LuaClassName = scriptClassName; return Create(); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a GAScripted to be identical to another, by deep copy. - // Arguments: A reference to the GAScripted to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a GAScripted to be identical to another, by deep copy. + /// @param reference A reference to the GAScripted to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const GAScripted& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire GAScripted, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire GAScripted, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Activity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the GAScripted object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the GAScripted object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReloadScripts - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reloads the preset scripts of this object, from the same script file - // path as was originally defined. This will also update the original - // preset in the PresetMan with the updated scripts so future objects - // spawned will use the new scripts. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Reloads the preset scripts of this object, from the same script file + /// path as was originally defined. This will also update the original + /// preset in the PresetMan with the updated scripts so future objects + /// spawned will use the new scripts. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int ReloadScripts() override; - /// /// Refreshes our activity functions to find any changes from script. - /// void RefreshActivityFunctions(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetLuaClassName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the class name of the Lua-derived class defined in this' script. - // Arguments: None. - // Return value: A string with the friendly-formatted Lua type name of this object. - + /// Gets the class name of the Lua-derived class defined in this' script. + /// @return A string with the friendly-formatted Lua type name of this object. const std::string& GetLuaClassName() const { return m_LuaClassName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SceneIsCompatible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells if a particular Scene supports this specific Activity on it. - // Usually that means certain Area:s need to be defined in the Scene. - // Arguments: The Scene to check if it supports this Activiy. Ownership IS NOT TRANSFERRED! - // How many teams we're checking for. Some scenes may support and activity - // but only for a limited number of teams. If -1, not applicable. - // Return value: Whether the Scene has the right stuff. - + /// Tells if a particular Scene supports this specific Activity on it. + /// Usually that means certain Area:s need to be defined in the Scene. + /// @param pScene The Scene to check if it supports this Activiy. Ownership IS NOT TRANSFERRED! + /// @param teams How many teams we're checking for. Some scenes may support and activity (default: -1) + /// but only for a limited number of teams. If -1, not applicable. + /// @return Whether the Scene has the right stuff. bool SceneIsCompatible(Scene* pScene, int teams = -1) override; - /// /// Handles when an ACraft has left the game scene and entered orbit, though does not delete it. Ownership is NOT transferred, as the ACraft's inventory is just 'unloaded'. - /// - /// The ACraft instance that entered orbit. Ownership is NOT transferred! + /// @param orbitedCraft The ACraft instance that entered orbit. Ownership is NOT transferred! void HandleCraftEnteringOrbit(ACraft* orbitedCraft) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateGlobalScripts - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates globals scripts loaded with this activity. - // Arguments: Whether it's an early update, during Activity update, or late update, after MovableMan - // Return value: None. - + /// Updates globals scripts loaded with this activity. + /// @param lateUpdate Whether it's an early update, during Activity update, or late update, after MovableMan void UpdateGlobalScripts(bool lateUpdate); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; int RunLuaFunction(const std::string& functionName, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CollectRequiredAreas - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through the script file and checks for any mentions and uses of - // Area:s that are required for this Activity to run in a Scene. - // Arguments: None. - // Return value: None. - + /// Goes through the script file and checks for any mentions and uses of + /// Area:s that are required for this Activity to run in a Scene. void CollectRequiredAreas(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: InitAIs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does nothing - we do this in script! Just overrides the base behaviour. - // Arguments: None. - // Return value: None. - + /// Does nothing - we do this in script! Just overrides the base behaviour. void InitAIs() override{}; // Member variables @@ -276,29 +158,17 @@ namespace RTE { std::unordered_map> m_ScriptFunctions; //!< A map of LuabindObjectWrappers that hold Lua functions. Used to maintain script execution order and avoid extraneous Lua calls. - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - /// /// Returns whether this GAScripted has an OnSave function, to act as a default for whether saving is allowed or not. - /// - /// Whether this GAScripted has an OnSave function + /// @return Whether this GAScripted has an OnSave function bool HasSaveFunction() const; - /// /// Adds this GAScripted's PieSlices, and any active GlobalScripts' PieSlices, to any active PieMenus. - /// void AddPieSlicesToActiveActorPieMenus(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/GATutorial.cpp b/Source/Activities/GATutorial.cpp index cdc004f1a8..aea32df802 100644 --- a/Source/Activities/GATutorial.cpp +++ b/Source/Activities/GATutorial.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GATutorial.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the GATutorial class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "GATutorial.h" #include "SceneMan.h" @@ -48,12 +36,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this GATutorial, effectively - // resetting the members of this abstraction level only. - void GATutorial::Clear() { m_TutorialPlayer = Players::PlayerOne; @@ -86,11 +68,6 @@ namespace RTE { m_pCPUBrain = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GATutorial object ready for use. - int GATutorial::Create() { if (GameActivity::Create() < 0) return -1; @@ -100,11 +77,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a GATutorial to be identical to another, by deep copy. - int GATutorial::Create(const GATutorial& reference) { if (GameActivity::Create(reference) < 0) return -1; @@ -138,14 +110,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int GATutorial::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return GameActivity::ReadProperty(propName, reader)); /* @@ -158,34 +122,17 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this GATutorial with a Writer for - // later recreation with Create(Reader &reader); - int GATutorial::Save(Writer& writer) const { GameActivity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the GATutorial object. - void GATutorial::Destroy(bool notInherited) { if (!notInherited) GameActivity::Destroy(); Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SceneIsCompatible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells if a particular Scene supports this specific Activity on it. - // Usually that means certain Area:s need to be defined in the Scene. - bool GATutorial::SceneIsCompatible(Scene* pScene, int teams) { if (!GameActivity::SceneIsCompatible(pScene, teams)) return false; @@ -197,12 +144,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int GATutorial::Start() { int error = GameActivity::Start(); @@ -355,11 +296,6 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void GATutorial::SetPaused(bool pause) { GameActivity::SetPaused(pause); @@ -368,11 +304,6 @@ namespace RTE { SetupAreas(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void GATutorial::End() { GameActivity::End(); @@ -421,12 +352,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this GATutorial. Supposed to be done every frame - // before drawing. - void GATutorial::Update() { // Avoid game logic when we're editing if (m_ActivityState == ActivityState::Editing) { @@ -741,11 +666,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void GATutorial::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { GameActivity::DrawGUI(pTargetBitmap, targetPos, which); @@ -771,23 +691,10 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this GATutorial's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void GATutorial::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { GameActivity::Draw(pTargetBitmap, targetPos); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: InitAIs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through all Actor:s currently in the MovableMan and gives each - // one not controlled by a Controller a CAI and appropriate AIMode setting - // based on team and CPU team. - void GATutorial::InitAIs() { Actor* pActor = 0; Actor* pFirstActor = 0; @@ -822,12 +729,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetupAreas - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets up or resets the Tutorial Areas to show the current control - // mappings etc. - void GATutorial::SetupAreas() { int device = g_UInputMan.GetControlScheme(m_TutorialPlayer)->GetDevice(); int preset = g_UInputMan.GetControlScheme(m_TutorialPlayer)->GetPreset(); diff --git a/Source/Activities/GATutorial.h b/Source/Activities/GATutorial.h index f5379470a4..3267333c4b 100644 --- a/Source/Activities/GATutorial.h +++ b/Source/Activities/GATutorial.h @@ -1,18 +1,11 @@ #ifndef _RTEGATUTORIAL_ #define _RTEGATUTORIAL_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GATutorial.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the ActivityMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the ActivityMan class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "GameActivity.h" #include "Box.h" @@ -20,180 +13,92 @@ namespace RTE { class Actor; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: GATutorial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tutorial mission with lots of special triggering logic. - // Parent(s): GameActivity. - // Class history: 10/13/2007 GATutorial created. - + /// Tutorial mission with lots of special triggering logic. class GATutorial : public GameActivity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(GATutorial); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GATutorial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GATutorial object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a GATutorial object in system + /// memory. Create() should be called before using the object. GATutorial() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~GATutorial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a GATutorial object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a GATutorial object before deletion + /// from system memory. ~GATutorial() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GATutorial object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the GATutorial object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a GATutorial to be identical to another, by deep copy. - // Arguments: A reference to the GATutorial to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a GATutorial to be identical to another, by deep copy. + /// @param reference A reference to the GATutorial to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const GATutorial& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire GATutorial, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire GATutorial, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Activity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the GATutorial object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the GATutorial object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SceneIsCompatible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells if a particular Scene supports this specific Activity on it. - // Usually that means certain Area:s need to be defined in the Scene. - // Arguments: The Scene to check if it supports this Activiy. Ownership IS NOT TRANSFERRED! - // How many teams we're checking for. Some scenes may support and activity - // but only for a limited number of teams. If -1, not applicable. - // Return value: Whether the Scene has the right stuff. - + /// Tells if a particular Scene supports this specific Activity on it. + /// Usually that means certain Area:s need to be defined in the Scene. + /// @param pScene The Scene to check if it supports this Activiy. Ownership IS NOT TRANSFERRED! + /// @param teams How many teams we're checking for. Some scenes may support and activity (default: -1) + /// but only for a limited number of teams. If -1, not applicable. + /// @return Whether the Scene has the right stuff. bool SceneIsCompatible(Scene* pScene, int teams = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: InitAIs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through all Actor:s currently in the MovableMan and sets each - // one not controlled by a player to be AI controlled and AIMode setting - // based on team and CPU team. - // Arguments: None. - // Return value: None. - + /// Goes through all Actor:s currently in the MovableMan and sets each + /// one not controlled by a player to be AI controlled and AIMode setting + /// based on team and CPU team. void InitAIs() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetupAreas - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets up or resets the Tutorial Areas to show the current control - // mappings etc. - // Arguments: None. - // Return value: None. - + /// Sets up or resets the Tutorial Areas to show the current control + /// mappings etc. void SetupAreas(); enum TutorialArea { @@ -294,18 +199,10 @@ namespace RTE { // The CPU opponent brain; not owned! Actor* m_pCPUBrain; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/GameActivity.cpp b/Source/Activities/GameActivity.cpp index 8b60ff6dee..751a6b7f2f 100644 --- a/Source/Activities/GameActivity.cpp +++ b/Source/Activities/GameActivity.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GameActivity.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the GameActivity class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "GameActivity.h" #include "CameraMan.h" @@ -49,12 +37,6 @@ namespace RTE { AbstractClassInfo(GameActivity, Activity); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this GameActivity, effectively - // resetting the members of this abstraction level only. - void GameActivity::Clear() { m_CPUTeam = -1; @@ -119,11 +101,6 @@ namespace RTE { m_WinnerTeam = -1; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GameActivity object ready for use. - int GameActivity::Create() { if (Activity::Create() < 0) return -1; @@ -143,11 +120,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a GameActivity to be identical to another, by deep copy. - int GameActivity::Create(const GameActivity& reference) { if (Activity::Create(reference) < 0) return -1; @@ -209,14 +181,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int GameActivity::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Activity::ReadProperty(propName, reader)); @@ -256,12 +220,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this GameActivity with a Writer for - // later recreation with Create(Reader &reader); - int GameActivity::Save(Writer& writer) const { Activity::Save(writer); @@ -280,11 +238,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the GameActivity object. - void GameActivity::Destroy(bool notInherited) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { delete m_InventoryMenuGUI[player]; @@ -306,10 +259,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTeamTech - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets tech module name for specified team. Module must set must be loaded. void GameActivity::SetTeamTech(int team, std::string tech) { if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { if (tech == "-All-" || tech == "-Random-") @@ -324,10 +273,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCrabToHumanSpawnRatio - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns CrabToHumanSpawnRatio for specified module float GameActivity::GetCrabToHumanSpawnRatio(int moduleid) { if (moduleid > -1) { const DataModule* pDataModule = g_PresetMan.GetDataModule(moduleid); @@ -337,11 +282,6 @@ namespace RTE { return 0.25; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCPUTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current CPU-assisted team, if any (NoTeam) - LEGACY function - void GameActivity::SetCPUTeam(int team) { if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { // Set the legacy var @@ -358,8 +298,6 @@ namespace RTE { */ } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GameActivity::IsBuyGUIVisible(int which) const { if (which == -1) { for (short player = Players::PlayerOne; player < this->GetPlayerCount(); player++) { @@ -372,8 +310,6 @@ namespace RTE { return this->GetBuyGUI(which)->IsVisible(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GameActivity::LockControlledActor(Players player, bool lock, Controller::InputMode lockToMode) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { bool prevLock = m_LuaLockActor[player]; @@ -384,12 +320,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SwitchToActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the this to focus player control to a specific Actor for a - // specific team. OWNERSHIP IS NOT TRANSFERRED! - bool GameActivity::SwitchToActor(Actor* pActor, int player, int team) { // Computer players don't focus on any Actor if (!m_IsHuman[player]) @@ -407,12 +337,6 @@ namespace RTE { return Activity::SwitchToActor(pActor, player, team); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SwitchToNextActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the this to focus player control to the next Actor of a - // specific team, other than the current one focused on. - void GameActivity::SwitchToNextActor(int player, int team, Actor* pSkip) { m_InventoryMenuGUI[player]->SetEnabled(false); @@ -427,12 +351,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SwitchToPrevActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces this to focus player control to the previous Actor of a - // specific team, other than the current one focused on. - void GameActivity::SwitchToPrevActor(int player, int team, Actor* pSkip) { m_InventoryMenuGUI[player]->SetEnabled(false); @@ -447,30 +365,14 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddObjectivePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Created an objective point for one of the teams to show until cleared. - void GameActivity::AddObjectivePoint(std::string description, Vector objPos, int whichTeam, ObjectiveArrowDir arrowDir) { m_Objectives.push_back(ObjectivePoint(description, objPos, whichTeam, arrowDir)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: YSortObjectivePoints - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sorts all objective points according to their positions on the Y axis. - void GameActivity::YSortObjectivePoints() { m_Objectives.sort(ObjPointYPosComparison()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddOverridePurchase - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds somehting to the purchase list that will override what is set - // in the buy GUI next time CreateDelivery is called. - int GameActivity::AddOverridePurchase(const SceneObject* pPurchase, int player) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { // Add to purchase list if valid item @@ -503,12 +405,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOverridePurchaseList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: First clears and then adds all the stuff in a Loadout to the override - // purchase list. - int GameActivity::SetOverridePurchaseList(const Loadout* pLoadout, int player) { // First clear out the list ClearOverridePurchase(player); @@ -549,12 +445,6 @@ namespace RTE { return finalListCost; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOverridePurchaseList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: First clears and then adds all the stuff in a Loadout to the override - // purchase list. - int GameActivity::SetOverridePurchaseList(std::string loadoutName, int player) { // Find out the native module of this player int nativeModule = 0; @@ -572,12 +462,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateDelivery - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes the current order out of a player's buy GUI, creates a Delivery - // based off it, and stuffs it into that player's delivery queue. - bool GameActivity::CreateDelivery(int player, int mode, Vector& waypoint, Actor* pTargetMO) { int team = m_Team[player]; if (team == Teams::NoTeam) @@ -767,12 +651,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetupPlayers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Precalculates the player-to-screen index map, counts the number of - // active players, teams etc. - void GameActivity::SetupPlayers() { Activity::SetupPlayers(); @@ -792,12 +670,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int GameActivity::Start() { // Set the split screen config before the Scene (and it SceneLayers, specifially) are loaded int humanCount = GetHumanCount(); @@ -1001,20 +873,10 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void GameActivity::SetPaused(bool pause) { Activity::SetPaused(pause); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void GameActivity::End() { Activity::End(); @@ -1073,12 +935,6 @@ namespace RTE { m_GameOverTimer.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateEditing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: This is a special update step for when any player is still editing the - // scene. - void GameActivity::UpdateEditing() { // Editing the scene, just update the editor guis and see if players are ready to start or not if (m_ActivityState != ActivityState::Editing) @@ -1209,12 +1065,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this GameActivity. Supposed to be done every frame - // before drawing. - void GameActivity::Update() { Activity::Update(); @@ -2010,11 +1860,6 @@ namespace RTE { */ } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void GameActivity::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { if (which < 0 || which >= c_MaxScreenCount) return; @@ -2377,12 +2222,6 @@ namespace RTE { m_pBannerYellow[PoS]->Draw(pTargetBitmap); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this GameActivity's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void GameActivity::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { GUIFont* pLargeFont = g_FrameMan.GetLargeFont(); GUIFont* pSmallFont = g_FrameMan.GetSmallFont(); @@ -2457,13 +2296,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActiveCPUTeamCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns active CPU team count. - // Arguments: None. - // Return value: Returns active CPU team count. - int GameActivity::GetActiveCPUTeamCount() const { int count = 0; @@ -2474,13 +2306,6 @@ namespace RTE { return count; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActiveHumanTeamCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns active human team count. - // Arguments: None. - // Return value: Returns active human team count. - int GameActivity::GetActiveHumanTeamCount() const { int count = 0; @@ -2491,13 +2316,6 @@ namespace RTE { return count; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OtherTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the next other team number from the one passed in, if any. If there - // are more than two teams in this game, then the next one in the series - // will be returned here. - int GameActivity::OtherTeam(int team) { // Only one team in this game, so can't return another one if (m_TeamCount == 1) @@ -2520,12 +2338,6 @@ namespace RTE { return Teams::NoTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OneOrNoneTeamsLeft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether there is one and only one team left this game with - // a brain in its ranks. - bool GameActivity::OneOrNoneTeamsLeft() { // See if only one team remains with any brains int brainTeamCount = 0; @@ -2547,12 +2359,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WhichTeamLeft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates which single team is left, if any. - // Arguments: None. - int GameActivity::WhichTeamLeft() { int whichTeam = Teams::NoTeam; @@ -2575,11 +2381,6 @@ namespace RTE { return Teams::NoTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: NoTeamLeft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether there are NO teams left with any brains at all! - bool GameActivity::NoTeamLeft() { for (int t = Teams::TeamOne; t < Teams::MaxTeamCount; ++t) { if (!m_TeamActive[t]) @@ -2590,13 +2391,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: InitAIs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through all Actor:s currently in the MovableMan and gives each - // one not controlled by a Controller a CAI and appropriate AIMode setting - // based on team and CPU team. - void GameActivity::InitAIs() { Actor* pActor = 0; Actor* pFirstActor = 0; @@ -2629,13 +2423,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DisableAIs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through all Actor:s currently in the MovableMan and gives each - // one not controlled by a Controller a CAI and appropriate AIMode setting - // based on team and CPU team. - void GameActivity::DisableAIs(bool disable, int whichTeam) { Actor* pActor = 0; Actor* pFirstActor = 0; @@ -2657,11 +2444,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Simply draws this' arrow relative to a point on a bitmap. - void GameActivity::ObjectivePoint::Draw(BITMAP* pTargetBitmap, BITMAP* pArrowBitmap, const Vector& arrowPoint, ObjectiveArrowDir arrowDir) { if (!pTargetBitmap || !pArrowBitmap) return; diff --git a/Source/Activities/GameActivity.h b/Source/Activities/GameActivity.h index 0165b71764..5a6031bf9d 100644 --- a/Source/Activities/GameActivity.h +++ b/Source/Activities/GameActivity.h @@ -1,18 +1,11 @@ #ifndef _RTEGAMEACTIVITY_ #define _RTEGAMEACTIVITY_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GameActivity.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the ActivityMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the ActivityMan class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "GUISound.h" #include "RTETools.h" #include "ActivityMan.h" @@ -33,13 +26,7 @@ namespace RTE { class GUIBanner; class Loadout; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: GameActivity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Base class for all GameActivity:s, including game modes and editors. - // Parent(s): Activity. - // Class history: 8/7/2007 GameActivity created. - + /// Base class for all GameActivity:s, including game modes and editors. class GameActivity : public Activity { friend struct ActivityLuaBindings; @@ -60,9 +47,7 @@ namespace RTE { Timer timer; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableOverrideMethods; ClassInfoGetters; @@ -79,133 +64,74 @@ namespace RTE { YELLOW }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GameActivity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GameActivity object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a GameActivity object in system + /// memory. Create() should be called before using the object. GameActivity() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~GameActivity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a GameActivity object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a GameActivity object before deletion + /// from system memory. ~GameActivity() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GameActivity object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the GameActivity object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a GameActivity to be identical to another, by deep copy. - // Arguments: A reference to the GameActivity to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a GameActivity to be identical to another, by deep copy. + /// @param reference A reference to the GameActivity to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const GameActivity& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire GameActivity, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire GameActivity, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Activity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the GameActivity object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the GameActivity object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCPUTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current CPU-assisted team, if any (NoTeam) - LEGACY function - // Arguments: None. - // Return value: The current setting. NoTeam is no team is assisted. - + /// Gets the current CPU-assisted team, if any (NoTeam) - LEGACY function + /// @return The current setting. NoTeam is no team is assisted. int GetCPUTeam() const { return m_CPUTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCPUTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current CPU-assisted team, if any (NoTeam) - LEGACY function - // Arguments: The new setting. NoTeam is no team is assisted. - // Return value: None. - + /// Sets the current CPU-assisted team, if any (NoTeam) - LEGACY function + /// @param team The new setting. NoTeam is no team is assisted. (default: Activity::NoTeam) void SetCPUTeam(int team = Activity::NoTeam); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetObservationTarget - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the observation sceneman scroll targets, for when the game is - // over or a player is in observation mode - // Arguments: The new absolute position to observe. - // Which player to set it for. - // Return value: None. - + /// Sets the observation sceneman scroll targets, for when the game is + /// over or a player is in observation mode + /// @param newTarget The new absolute position to observe. + /// @param player Which player to set it for. (default: 0) void SetObservationTarget(const Vector& newTarget, int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_ObservationTarget[player] = newTarget; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDeathViewTarget - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the player death sceneman scroll targets, for when a player- - // controlled actor dies and the view should go to his last position - // Arguments: The new absolute position to set as death view. - // Which player to set it for. - // Return value: None. - + /// Sets the player death sceneman scroll targets, for when a player- + /// controlled actor dies and the view should go to his last position + /// @param newTarget The new absolute position to set as death view. + /// @param player Which player to set it for. (default: 0) void SetDeathViewTarget(const Vector& newTarget, int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_DeathViewTarget[player] = newTarget; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLandingZone - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the he last selected landing zone. - // Arguments: The new absolute position to set as the last selected landing zone. - // Which player to set it for. - // Return value: None. - + /// Sets the he last selected landing zone. + /// @param newZone The new absolute position to set as the last selected landing zone. + /// @param player Which player to set it for. (default: 0) void SetLandingZone(const Vector& newZone, int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_LandingZone[player] = newZone; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLandingZone - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the he last selected landing zone. - // Arguments: Which player to get it for. - // Return value: The new absolute position to set as the last selected landing zone. - + /// Gets the he last selected landing zone. + /// @param player Which player to get it for. (default: 0) + /// @return The new absolute position to set as the last selected landing zone. Vector GetLandingZone(int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) return m_LandingZone[player]; @@ -213,652 +139,363 @@ namespace RTE { return Vector(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetActorSelectCursor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the actor selection cursor position. - // Arguments: The new absolute position to put the cursor at. - // Which player to set it for. - // Return value: None. - + /// Sets the actor selection cursor position. + /// @param newPos The new absolute position to put the cursor at. + /// @param player Which player to set it for. (default: 0) void SetActorSelectCursor(const Vector& newPos, int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_ActorCursor[player] = newPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBuyGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the an in-game GUI Object for a specific player. - // Arguments: Which player to get the GUI for. - // Return value: A pointer to a BuyMenuGUI. Ownership is NOT transferred! - + /// Gets the an in-game GUI Object for a specific player. + /// @param which Which player to get the GUI for. (default: 0) + /// @return A pointer to a BuyMenuGUI. Ownership is NOT transferred! BuyMenuGUI* GetBuyGUI(unsigned int which = 0) const { return m_pBuyGUI[which]; } - /// /// Checks if the in-game GUI Object is visible for a specific player. - /// - /// Which player to check the GUI for. -1 will check all players. - /// Whether or not the BuyMenuGUI is visible for input player(s). + /// @param which Which player to check the GUI for. -1 will check all players. + /// @return Whether or not the BuyMenuGUI is visible for input player(s). bool IsBuyGUIVisible(int which = 0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the an in-game editor GUI Object for a specific player. - // Arguments: Which player to get the GUI for. - // Return value: A pointer to a SceneEditorGUI. Ownership is NOT transferred! - + /// Gets the an in-game editor GUI Object for a specific player. + /// @param which Which player to get the GUI for. (default: 0) + /// @return A pointer to a SceneEditorGUI. Ownership is NOT transferred! SceneEditorGUI* GetEditorGUI(unsigned int which = 0) const { return m_pEditorGUI[which]; } - /// /// Locks a player controlled actor to a specific controller mode. /// Locking the actor will disable player input, including switching actors. - /// - /// Which player to lock the actor for. - /// Whether to lock or unlock the actor. (Default: true) - /// Which controller mode to lock the actor to. (Default: `CIM_AI`) - /// Whether the (un)lock was performed. + /// @param player Which player to lock the actor for. + /// @param lock Whether to lock or unlock the actor. (Default: true) + /// @param lockToMode Which controller mode to lock the actor to. (Default: `CIM_AI`) + /// @return Whether the (un)lock was performed. bool LockControlledActor(Players player, bool lock = true, Controller::InputMode lockToMode = Controller::InputMode::CIM_AI); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SwitchToActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the this to focus player control to a specific Actor for a - // specific team. OWNERSHIP IS NOT TRANSFERRED! - // Arguments: Which Actor to switch focus to. The team of this Actor will be set - // once it is passed in. Ownership IS NOT TRANSFERRED! The Actor should - // be added to MovableMan already. - // Return value: Whether the focus switch was successful or not. - + /// Forces the this to focus player control to a specific Actor for a + /// specific team. OWNERSHIP IS NOT TRANSFERRED! + /// @param pActor Which Actor to switch focus to. The team of this Actor will be set + /// once it is passed in. Ownership IS NOT TRANSFERRED! The Actor should + /// be added to MovableMan already. + /// @return Whether the focus switch was successful or not. bool SwitchToActor(Actor* pActor, int player = 0, int team = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SwitchToNextActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the this to focus player control to the next Actor of a - // specific team, other than the current one focused on. - // Arguments: Which team to switch to next actor on. - // An actor pointer to skip in the sequence. - // Return value: None. - + /// Forces the this to focus player control to the next Actor of a + /// specific team, other than the current one focused on. + /// @param player Which team to switch to next actor on. + /// @param team An actor pointer to skip in the sequence. void SwitchToNextActor(int player, int team, Actor* pSkip = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SwitchToPrevActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces this to focus player control to the previous Actor of a - // specific team, other than the current one focused on. - // Arguments: Which team to switch to next actor on. - // An actor pointer to skip in the sequence. - // Return value: None. - + /// Forces this to focus player control to the previous Actor of a + /// specific team, other than the current one focused on. + /// @param player Which team to switch to next actor on. + /// @param team An actor pointer to skip in the sequence. void SwitchToPrevActor(int player, int team, Actor* pSkip = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetWinnerTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team is the winner, when the game is over. - // Arguments: The team number of the winning team. 0 is team #1. Negative number - // means the game isn't over yet. - // Return value: None. - + /// Sets which team is the winner, when the game is over. + /// @param winnerTeam The team number of the winning team. 0 is team #1. Negative number + /// means the game isn't over yet. void SetWinnerTeam(int winnerTeam) { m_WinnerTeam = winnerTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetWinnerTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates which team is the winner, when the game is over. - // Arguments: None. - // Return value: The team number of the winning team. 0 is team #1. Negative number - // means the game isn't over yet. - + /// Indicates which team is the winner, when the game is over. + /// @return The team number of the winning team. 0 is team #1. Negative number + /// means the game isn't over yet. int GetWinnerTeam() const { return m_WinnerTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBanner - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets access to the huge banner of any player that can display - // messages which can not be missed or ignored. - // Arguments: Which color banner to get - see the GameActivity::BannerColor enum. - // Which player's banner to get. - // Return value: A pointer to the GUIBanner object that we can - + /// Gets access to the huge banner of any player that can display + /// messages which can not be missed or ignored. + /// @param whichColor Which color banner to get - see the GameActivity::BannerColor enum. (default: YELLOW) + /// @param player Which player's banner to get. (default: Players::PlayerOne) + /// @return A pointer to the GUIBanner object that we can GUIBanner* GetBanner(int whichColor = YELLOW, int player = Players::PlayerOne) { return whichColor == YELLOW ? m_pBannerYellow[player] : m_pBannerRed[player]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLZArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the Area within which a team can land things. - // Arguments: The number of the team we're setting for. - // The Area we're setting to limit their landings within. - // Return value: None. - + /// Sets the Area within which a team can land things. + /// @param team The number of the team we're setting for. + /// @param newArea The Area we're setting to limit their landings within. void SetLZArea(int team, const Scene::Area& newArea) { m_LandingZoneArea[team].Reset(); m_LandingZoneArea[team].Create(newArea); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLZArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the Area within which a team can land things. OWNERSHIP IS NOT TRANSFERRED! - // Arguments: The number of the team we're setting for. - // Return value: The Area we're using to limit their landings within. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the Area within which a team can land things. OWNERSHIP IS NOT TRANSFERRED! + /// @param team The number of the team we're setting for. + /// @return The Area we're using to limit their landings within. OWNERSHIP IS NOT TRANSFERRED! const Scene::Area& GetLZArea(int team) const { return m_LandingZoneArea[team]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBrainLZWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the width of the landing zone box that follows around a player's - // brain. - // Arguments: The number of the in-game player we're setting for. - // The width of the box, in pixels. 0 means disabled. - // Return value: None. - + /// Sets the width of the landing zone box that follows around a player's + /// brain. + /// @param player The number of the in-game player we're setting for. + /// @param width The width of the box, in pixels. 0 means disabled. void SetBrainLZWidth(int player, int width) { m_BrainLZWidth[player] = width; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBrainLZWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the width of the landing zone box that follows around a player's - // brain. - // Arguments: The number of the player we're getting for. - // Return value: The width in pixels of the landing zone. - + /// Gets the width of the landing zone box that follows around a player's + /// brain. + /// @param player The number of the player we're getting for. + /// @return The width in pixels of the landing zone. int GetBrainLZWidth(int player) const { return m_BrainLZWidth[player]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddObjectivePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Created an objective point for one of the teams to show until cleared. - // Arguments: The team number of the team to give objective. 0 is team #1. - // The very short description of what the objective is (three short words max) - // The absolute scene coordiante position of the objective. - // The desired direction of the arrow when the point is on screen. - // Return value: None. - + /// Created an objective point for one of the teams to show until cleared. + /// @param description The team number of the team to give objective. 0 is team #1. + /// @param objPos The very short description of what the objective is (three short words max) + /// @param whichTeam The absolute scene coordiante position of the objective. (default: Teams::TeamOne) + /// @param arrowDir The desired direction of the arrow when the point is on screen. (default: ARROWDOWN) void AddObjectivePoint(std::string description, Vector objPos, int whichTeam = Teams::TeamOne, ObjectiveArrowDir arrowDir = ARROWDOWN); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: YSortObjectivePoints - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sorts all objective points according to their positions on the Y axis. - // Arguments: None. - // Return value: None. - + /// Sorts all objective points according to their positions on the Y axis. void YSortObjectivePoints(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearObjectivePoints - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all objective points previously added, for both teams. - // Arguments: None. - // Return value: None. - + /// Clears all objective points previously added, for both teams. void ClearObjectivePoints() { m_Objectives.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddOverridePurchase - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds somehting to the purchase list that will override what is set - // in the buy guy next time CreateDelivery is called. - // Arguments: The SceneObject preset to add to the override purchase list. OWNERSHIP IS NOT TRANSFERRED! - // Which player's list to add an override purchase item to. - // Return value: The new total value of what's in the override purchase list. - + /// Adds somehting to the purchase list that will override what is set + /// in the buy guy next time CreateDelivery is called. + /// @param pPurchase The SceneObject preset to add to the override purchase list. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player's list to add an override purchase item to. + /// @return The new total value of what's in the override purchase list. int AddOverridePurchase(const SceneObject* pPurchase, int player); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOverridePurchaseList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: First clears and then adds all the stuff in a Loadout to the override - // purchase list. - // Arguments: The Loadout preset to set the override purchase list to reflect. OWNERSHIP IS NOT TRANSFERRED! - // The player we're talking about. - // Return value: The new total value of what's in the override purchase list. - + /// First clears and then adds all the stuff in a Loadout to the override + /// purchase list. + /// @param pLoadout The Loadout preset to set the override purchase list to reflect. OWNERSHIP IS NOT TRANSFERRED! + /// @param player The player we're talking about. + /// @return The new total value of what's in the override purchase list. int SetOverridePurchaseList(const Loadout* pLoadout, int player); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOverridePurchaseList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: First clears and then adds all the stuff in a Loadout to the override - // purchase list. - // Arguments: The name of the Loadout preset to set the override purchase list to - // represent. - // Return value: The new total value of what's in the override purchase list. - + /// First clears and then adds all the stuff in a Loadout to the override + /// purchase list. + /// @param loadoutName The name of the Loadout preset to set the override purchase list to + /// represent. + /// @return The new total value of what's in the override purchase list. int SetOverridePurchaseList(std::string loadoutName, int player); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearOverridePurchase - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all items from a specific player's override purchase list. - // Arguments: Which player's override purchase list to clear. - // Return value: None. - + /// Clears all items from a specific player's override purchase list. + /// @param m_PurchaseOverride[player].clear( Which player's override purchase list to clear. void ClearOverridePurchase(int player) { m_PurchaseOverride[player].clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateDelivery - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes the current order out of a player's buy GUI, creates a Delivery - // based off it, and stuffs it into that player's delivery queue. - // Arguments: Which player to create the delivery for. Cargo AI mode and waypoint. - // Return value: Success or not. - + /// Takes the current order out of a player's buy GUI, creates a Delivery + /// based off it, and stuffs it into that player's delivery queue. + /// @param player Which player to create the delivery for. Cargo AI mode and waypoint. + /// @return Success or not. bool CreateDelivery(int player, int mode, Vector& waypoint) { return CreateDelivery(player, mode, waypoint, NULL); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateDelivery - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes the current order out of a player's buy GUI, creates a Delivery - // based off it, and stuffs it into that player's delivery queue. - // Arguments: Which player to create the delivery for. Cargo AI mode and TargetMO. - // Return value: Success or not. - + /// Takes the current order out of a player's buy GUI, creates a Delivery + /// based off it, and stuffs it into that player's delivery queue. + /// @param player Which player to create the delivery for. Cargo AI mode and TargetMO. + /// @return Success or not. bool CreateDelivery(int player, int mode, Actor* pTargetMO) { Vector point(-1, -1); return CreateDelivery(player, mode, point, pTargetMO); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateDelivery - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes the current order out of a player's buy GUI, creates a Delivery - // based off it, and stuffs it into that player's delivery queue. - // Arguments: Which player to create the delivery for and Cargo AI mode. - // Return value: Success or not. - + /// Takes the current order out of a player's buy GUI, creates a Delivery + /// based off it, and stuffs it into that player's delivery queue. + /// @param player Which player to create the delivery for and Cargo AI mode. + /// @return Success or not. bool CreateDelivery(int player, int mode) { Vector point(-1, -1); return CreateDelivery(player, mode, point, NULL); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateDelivery - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes the current order out of a player's buy GUI, creates a Delivery - // based off it, and stuffs it into that player's delivery queue. - // Arguments: Which player to create the delivery for. - // Return value: Success or not. - + /// Takes the current order out of a player's buy GUI, creates a Delivery + /// based off it, and stuffs it into that player's delivery queue. + /// @param player Which player to create the delivery for. + /// @return Success or not. bool CreateDelivery(int player) { Vector point(-1, -1); return CreateDelivery(player, Actor::AIMODE_SENTRY, point, NULL); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDeliveryCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows how many deliveries this team has pending. - // Arguments: Which team to check the delivery count for. - // Return value: The number of deliveries this team has coming. - + /// Shows how many deliveries this team has pending. + /// @param m_Deliveries[team].size( Which team to check the delivery count for. + /// @return The number of deliveries this team has coming. int GetDeliveryCount(int team) { return m_Deliveries[team].size(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetupPlayers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Precalculates the player-to-screen index map, counts the number of - // active players etc. - // Arguments: None. - // Return value: None. - + /// Precalculates the player-to-screen index map, counts the number of + /// active players etc. void SetupPlayers() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateEditing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: This is a special update step for when any player is still editing the - // scene. - // Arguments: None. - // Return value: None. - + /// This is a special update step for when any player is still editing the + /// scene. void UpdateEditing(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTeamTech - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the name of the tech module selected for this team during scenario setup - // Arguments: Team to return tech module for - // Return value: Tech module name, for example Dummy.rte, or empty string if there is no team + /// Returns the name of the tech module selected for this team during scenario setup + /// @param team Team to return tech module for + /// @return Tech module name, for example Dummy.rte, or empty string if there is no team std::string GetTeamTech(int team) const { return (team >= Teams::TeamOne && team < Teams::MaxTeamCount) ? m_TeamTech[team] : ""; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTeamTech - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets tech module name for specified team. Module must set must be loaded. - // Arguments: Team to set module, module name, for example Dummy.rte - // Return value: None + /// Sets tech module name for specified team. Module must set must be loaded. + /// @param team Team to set module, module name, for example Dummy.rte void SetTeamTech(int team, std::string tech); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TeamIsCPU - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether a specific team is assigned a CPU player in the current game. - // Arguments: Which team index to check. - // Return value: Whether the team is assigned a CPU player in the current activity. + /// Indicates whether a specific team is assigned a CPU player in the current game. + /// @param team Which team index to check. + /// @return Whether the team is assigned a CPU player in the current activity. bool TeamIsCPU(int team) const { return (team >= Teams::TeamOne && team < Teams::MaxTeamCount) ? m_TeamIsCPU[team] : false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActiveCPUTeamCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns active CPU team count. - // Arguments: None. - // Return value: Returns active CPU team count. + /// Returns active CPU team count. + /// @return Returns active CPU team count. int GetActiveCPUTeamCount() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActiveHumanTeamCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns active human team count. - // Arguments: None. - // Return value: Returns active human team count. + /// Returns active human team count. + /// @return Returns active human team count. int GetActiveHumanTeamCount() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetStartingGold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes how much starting gold was selected in scenario setup dialog. 20000 - infinite amount. - // Arguments: Starting gold amount - // Return value: None. + /// Changes how much starting gold was selected in scenario setup dialog. 20000 - infinite amount. + /// @param amount Starting gold amount void SetStartingGold(int amount) { m_StartingGold = amount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetStartingGold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns how much starting gold was selected in scenario setup dialog. 20000 - infinite amount. - // Arguments: None. - // Return value: How much starting gold must be given to human players. + /// Returns how much starting gold was selected in scenario setup dialog. 20000 - infinite amount. + /// @return How much starting gold must be given to human players. int GetStartingGold() { return m_StartingGold; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFogOfWarEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes whether fog of war must be enabled for this activity or not. - // Never hides or reveals anything, just changes internal flag. - // Arguments: New fog of war state. true = enabled. - // Return value: None. + /// Changes whether fog of war must be enabled for this activity or not. + /// Never hides or reveals anything, just changes internal flag. + /// @param enable New fog of war state. true = enabled. + /// Return value: None. void SetFogOfWarEnabled(bool enable) { m_FogOfWarEnabled = enable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFogOfWareEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether fog of war must be enabled for this activity or not. - // Call it to determine whether you should call MakeAllUnseen or not at the start of activity. - // Arguments: None. - // Return value: Whether Fog of war flag was checked during scenario setup dialog. + /// Returns whether fog of war must be enabled for this activity or not. + /// Call it to determine whether you should call MakeAllUnseen or not at the start of activity. + /// @return Whether Fog of war flag was checked during scenario setup dialog. bool GetFogOfWarEnabled() { return m_FogOfWarEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRequireClearPathToOrbit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether player activity requires a cleat path to orbit to place brain - // - // Arguments: None. - // Return value: Whether we need a clear path to orbit to place brains. + /// Tells whether player activity requires a cleat path to orbit to place brain + /// Return value: Whether we need a clear path to orbit to place brains. bool GetRequireClearPathToOrbit() const { return m_RequireClearPathToOrbit; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetRequireClearPathToOrbit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether player activity requires a cleat path to orbit to place brain - // - // Arguments: Whether we need a clear path to orbit to place brains. - // Return value: None. + /// Tells whether player activity requires a cleat path to orbit to place brain + /// @param newvalue Whether we need a clear path to orbit to place brains. + /// Return value: None. void SetRequireClearPathToOrbit(bool newvalue) { m_RequireClearPathToOrbit = newvalue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: int GetDefaultFogOfWar() const { return m_DefaultFogOfWar; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: int GetDefaultRequireClearPathToOrbit() const { return m_DefaultRequireClearPathToOrbit; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: int GetDefaultDeployUnits() const { return m_DefaultDeployUnits; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: int GetDefaultGoldCakeDifficulty() const { return m_DefaultGoldCakeDifficulty; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: int GetDefaultGoldEasyDifficulty() const { return m_DefaultGoldEasyDifficulty; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: int GetDefaultGoldMediumDifficulty() const { return m_DefaultGoldMediumDifficulty; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: int GetDefaultGoldHardDifficulty() const { return m_DefaultGoldHardDifficulty; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: int GetDefaultGoldNutsDifficulty() const { return m_DefaultGoldNutsDifficulty; } - /// /// Gets the default gold for max difficulty. - /// - /// The default gold for max difficulty. + /// @return The default gold for max difficulty. int GetDefaultGoldMaxDifficulty() const { return m_DefaultGoldMaxDifficulty; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: bool GetFogOfWarSwitchEnabled() const { return m_FogOfWarSwitchEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: + /// Method: + /// Description: + /// Return value: bool GetDeployUnitsSwitchEnabled() const { return m_DeployUnitsSwitchEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: - + /// Method: + /// Description: + /// Return value: bool GetGoldSwitchEnabled() const { return m_GoldSwitchEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Arguments: None. - // Return value: - + /// Method: + /// Description: + /// Return value: bool GetRequireClearPathToOrbitSwitchEnabled() const { return m_RequireClearPathToOrbitSwitchEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCrabToHumanSpawnRatio - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns CrabToHumanSpawnRatio for specified module - // Arguments: None. - // Return value: Crab-To-Human spawn ratio value set for specified module, 0.25 is default. - + /// Returns CrabToHumanSpawnRatio for specified module + /// @return Crab-To-Human spawn ratio value set for specified module, 0.25 is default. float GetCrabToHumanSpawnRatio(int moduleid); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDeliveryDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns current delivery delay - // Arguments: None. - // Return value: Returns current delivery delay - + /// Returns current delivery delay + /// @return Returns current delivery delay long GetDeliveryDelay() const { return m_DeliveryDelay; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDeliveryDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets delivery delay - // Arguments: New delivery delay value in ms - // Return value: None - + /// Sets delivery delay + /// @param newDeliveryDelay New delivery delay value in ms void SetDeliveryDelay(long newDeliveryDelay) { m_DeliveryDelay = newDeliveryDelay > 1 ? newDeliveryDelay : 1; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBuyMenuEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether buy menu is enabled in this activity. - // Arguments: True if buy menu enabled false otherwise - // Return value: None. - + /// Returns whether buy menu is enabled in this activity. + /// @param True if buy menu enabled false otherwise bool GetBuyMenuEnabled() const { return m_BuyMenuEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBuyMenuEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether buy menu is enabled in this activity - // Arguments: True to enable buy menu, false otherwise - // Return value: None. - + /// Sets whether buy menu is enabled in this activity + /// @param newValue True to enable buy menu, false otherwise void SetBuyMenuEnabled(bool newValue) { m_BuyMenuEnabled = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNetworkPlayerName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns network player name - // Arguments: Player - // Return value: Network player name - + /// Returns network player name + /// @param player Player + /// @return Network player name std::string& GetNetworkPlayerName(int player); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetNetworkPlayerName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets network player name - // Arguments: Player number, player name - // Return value: None - + /// Sets network player name + /// @param player Player number, player name void SetNetworkPlayerName(int player, std::string name); - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateDelivery - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes the current order out of a player's buy GUI, creates a Delivery - // based off it, and stuffs it into that player's delivery queue. - // Arguments: Which player to create the delivery for. Cargo AI mode waypoint or TargetMO. - // Return value: Success or not. - + /// Takes the current order out of a player's buy GUI, creates a Delivery + /// based off it, and stuffs it into that player's delivery queue. + /// @param player Which player to create the delivery for. Cargo AI mode waypoint or TargetMO. + /// @return Success or not. bool CreateDelivery(int player, int mode, Vector& waypoint, Actor* pTargetMO); - ////////////////////////////////////////////////////////////////////////////////////////// - // Struct: ObjectivePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A struct to keep all data about a mission objective. - // Parent(s): None. - // Class history: 11/17/2008 ObjectivePoint - + /// A struct to keep all data about a mission objective. struct ObjectivePoint { ObjectivePoint() { m_Description.clear(); @@ -873,16 +510,11 @@ namespace RTE { m_ArrowDir = arrowDir; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Simply draws this' arrow relative to a point on a bitmap. - // Arguments: A pointer to the BITMAP to draw on. - // The arrow bitmap to draw, assuming it points downward. - // The absolute position on the bitmap to draw the point of the arrow at. - // Which orientation to draw the arrow in, relative to the point. - // Return value: None. - + /// Simply draws this' arrow relative to a point on a bitmap. + /// @param pTargetBitmap A pointer to the BITMAP to draw on. + /// @param pArrowBitmap The arrow bitmap to draw, assuming it points downward. + /// @param arrowPoint The absolute position on the bitmap to draw the point of the arrow at. + /// @param arrowDir Which orientation to draw the arrow in, relative to the point. (default: ARROWDOWN) void Draw(BITMAP* pTargetBitmap, BITMAP* pArrowBitmap, const Vector& arrowPoint, ObjectiveArrowDir arrowDir = ARROWDOWN); // The description of this objective point @@ -900,67 +532,37 @@ namespace RTE { bool operator()(ObjectivePoint& rhs, ObjectivePoint& lhs) { return rhs.m_ScenePos.m_Y < lhs.m_ScenePos.m_Y; } }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OtherTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the next other team number from the one passed in, if any. If there - // are more than two teams in this game, then the next one in the series - // will be returned here. - // Arguments: The team not to get. - // Return value: The other team's number. - + /// Gets the next other team number from the one passed in, if any. If there + /// are more than two teams in this game, then the next one in the series + /// will be returned here. + /// @param team The team not to get. + /// @return The other team's number. int OtherTeam(int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OneOrNoneTeamsLeft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether there is less than two teams left in this game with - // a brain in its ranks. - // Arguments: None. - // Return value: Whether less than two teams have brains in them left. - + /// Indicates whether there is less than two teams left in this game with + /// a brain in its ranks. + /// @return Whether less than two teams have brains in them left. bool OneOrNoneTeamsLeft(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WhichTeamLeft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates which single team is left, if any. - // Arguments: None. - // Return value: Which team stands alone with any brains in its ranks, if any. NoTeam - // is returned if there's either more than one team, OR there are no - // teams at all left with brains in em. - + /// Indicates which single team is left, if any. + /// @return Which team stands alone with any brains in its ranks, if any. NoTeam + /// is returned if there's either more than one team, OR there are no + /// teams at all left with brains in em. int WhichTeamLeft(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: NoTeamLeft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether there are NO teams left with any brains at all! - // Arguments: None. - // Return value: Whether any team has a brain in it at all. - + /// Indicates whether there are NO teams left with any brains at all! + /// @return Whether any team has a brain in it at all. bool NoTeamLeft(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: InitAIs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through all Actor:s currently in the MovableMan and sets each - // one not controlled by a player to be AI controlled and AIMode setting - // based on team and CPU team. - // Arguments: None. - // Return value: None. - + /// Goes through all Actor:s currently in the MovableMan and sets each + /// one not controlled by a player to be AI controlled and AIMode setting + /// based on team and CPU team. virtual void InitAIs(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DisableAIs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through all Actor:s currently in the MovableMan and disables or - // enables each one with a Controller set to AI input. - // Arguments: Whether to disable or enable them; - // Which team to do this to. If all, then pass Teams::NoTeam - // Return value: None. - + /// Goes through all Actor:s currently in the MovableMan and disables or + /// enables each one with a Controller set to AI input. + /// @param disable Whether to disable or enable them; (default: true) + /// @param whichTeam Which team to do this to. If all, then pass Teams::NoTeam (default: Teams::NoTeam) void DisableAIs(bool disable = true, int whichTeam = Teams::NoTeam); // Member variables @@ -1065,18 +667,10 @@ namespace RTE { std::string m_NetworkPlayerNames[Players::MaxPlayerCount]; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/GibEditor.cpp b/Source/Activities/GibEditor.cpp index 14942cd4cc..9e29c989ab 100644 --- a/Source/Activities/GibEditor.cpp +++ b/Source/Activities/GibEditor.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GibEditor.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the GibEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "GibEditor.h" #include "WindowMan.h" @@ -43,12 +31,6 @@ namespace RTE { ConcreteClassInfo(GibEditor, EditorActivity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this GibEditor, effectively - // resetting the members of this abstraction level only. - void GibEditor::Clear() { m_pEditedObject = 0; m_pTestingObject = 0; @@ -57,11 +39,6 @@ namespace RTE { m_pEditorGUI = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GibEditor object ready for use. - int GibEditor::Create() { if (EditorActivity::Create() < 0) return -1; @@ -69,11 +46,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a GibEditor to be identical to another, by deep copy. - int GibEditor::Create(const GibEditor& reference) { if (EditorActivity::Create(reference) < 0) return -1; @@ -84,14 +56,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int GibEditor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); /* @@ -102,22 +66,11 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this GibEditor with a Writer for - // later recreation with Create(Reader &reader); - int GibEditor::Save(Writer& writer) const { EditorActivity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the GibEditor object. - void GibEditor::Destroy(bool notInherited) { delete m_pEditedObject; delete m_pTestingObject; @@ -128,12 +81,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int GibEditor::Start() { int error = EditorActivity::Start(); @@ -220,33 +167,17 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void GibEditor::SetPaused(bool pause) { // Override the pause m_Paused = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void GibEditor::End() { EditorActivity::End(); m_ActivityState = ActivityState::Over; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this GibEditor. Supposed to be done every frame - // before drawing. - void GibEditor::Update() { // And object hasn't been loaded yet, so get the loading picker going if (!m_pEditedObject && !m_pObjectToLoad) { @@ -594,11 +525,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void GibEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { // Testing mode if (m_EditorMode == EditorActivity::TESTINGOBJECT) { @@ -619,18 +545,10 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this GibEditor's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void GibEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { EditorActivity::Draw(pTargetBitmap, targetPos); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GibEditor::SaveObject(const std::string& saveAsName, bool forceOverwrite) { if (!m_pEditedObject) { return false; @@ -694,12 +612,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StuffEditedGibs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Replaces all the gibs owned by the passed in MOSR with the ones being - // edited that represent the new gibbing setup. - void GibEditor::StuffEditedGibs(MOSRotating* pEditedObject) { if (!pEditedObject) return; @@ -726,11 +638,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateNewDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New dialog box, populates its lists etc. - void GibEditor::UpdateNewDialog() { /* // Only refill modules if empty @@ -784,21 +691,11 @@ namespace RTE { */ } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateLoadDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Load dialog box, populates its lists etc. - void GibEditor::UpdateLoadDialog() { if (m_pObjectToLoad) dynamic_cast(m_pGUIController->GetControl("LoadNameLabel"))->SetText("Load object named " + m_pObjectToLoad->GetPresetName() + "?"); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateSaveDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save dialog box, populates its lists etc. - void GibEditor::UpdateSaveDialog() { if (!m_pEditedObject) return; @@ -808,11 +705,6 @@ namespace RTE { m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/NewData/"); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateChangesDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save Changes dialog box, populates its lists etc. - void GibEditor::UpdateChangesDialog() { if (!m_pEditedObject) return; @@ -826,18 +718,11 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateOverwriteDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Overwrite dialog box, populates its lists etc. - void GibEditor::UpdateOverwriteDialog() { if (m_pEditedObject) m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/NewData/" + m_pEditedObject->GetPresetName() + ".ini"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GibEditor::ClearTestArea() const { clear_bitmap(g_SceneMan.GetTerrain()->GetFGColorBitmap()); clear_bitmap(g_SceneMan.GetTerrain()->GetBGColorBitmap()); diff --git a/Source/Activities/GibEditor.h b/Source/Activities/GibEditor.h index 757f3bb0f4..2b01c8ddbb 100644 --- a/Source/Activities/GibEditor.h +++ b/Source/Activities/GibEditor.h @@ -1,18 +1,11 @@ #ifndef _RTEGIBEDITOR_ #define _RTEGIBEDITOR_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GibEditor.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the GibEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the GibEditor class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" @@ -31,228 +24,109 @@ namespace RTE { class GUILabel; class GUIComboBox; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: GibEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activity for editing gib placement within MOSRotating:s. - // Parent(s): EditorActivity. - // Class history: 9/17/2007 GibEditor Created. - + /// Activity for editing gib placement within MOSRotating:s. class GibEditor : public EditorActivity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(GibEditor); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GibEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GibEditor object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a GibEditor object in system + /// memory. Create() should be called before using the object. GibEditor() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~GibEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a GibEditor object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a GibEditor object before deletion + /// from system memory. ~GibEditor() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GibEditor object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the GibEditor object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a GibEditor to be identical to another, by deep copy. - // Arguments: A reference to the GibEditor to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a GibEditor to be identical to another, by deep copy. + /// @param reference A reference to the GibEditor to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const GibEditor& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire GibEditor, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire GibEditor, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); EditorActivity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the GibEditor object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the GibEditor object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Saves the current object to an appropriate ini file, and asks user if they want to overwrite first if object of this name exists. - /// - /// The name of the new object to be saved. - /// Whether to force any existing Object of that name to be overwritten if it already exists. - /// Whether actually managed to save. Will return false both if an object of this name already exists (and not overwriting), or if other error. + /// @param saveAsName The name of the new object to be saved. + /// @param forceOverwrite Whether to force any existing Object of that name to be overwritten if it already exists. + /// @return Whether actually managed to save. Will return false both if an object of this name already exists (and not overwriting), or if other error. bool SaveObject(const std::string& saveAsName, bool forceOverwrite = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StuffEditedGibs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Replaces all the gibs owned by the passed in MOSR with the ones being - // edited that represent the new gibbing setup. - // Arguments: The MOSRotating to replace the gibs of. Ownership is NOT trnasferred! - // Return value: None. - + /// Replaces all the gibs owned by the passed in MOSR with the ones being + /// edited that represent the new gibbing setup. + /// @param pEditedObject The MOSRotating to replace the gibs of. Ownership is NOT trnasferred! void StuffEditedGibs(MOSRotating* pEditedObject); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateNewDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the New dialog box, populates its lists etc. void UpdateNewDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateLoadDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Load dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Load dialog box, populates its lists etc. void UpdateLoadDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateSaveDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save dialog box, populates its lists etc. void UpdateSaveDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateChangesDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save Changes dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save Changes dialog box, populates its lists etc. void UpdateChangesDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateOverwriteDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Overwrite dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Overwrite dialog box, populates its lists etc. void UpdateOverwriteDialog() override; // Member variables @@ -269,23 +143,13 @@ namespace RTE { // The editor GUI GibEditorGUI* m_pEditorGUI; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - /// /// Clears all the layers of the testing area terrain so nothing that somehow settled lingers between edited object changes and testing phases. - /// void ClearTestArea() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/MultiplayerGame.cpp b/Source/Activities/MultiplayerGame.cpp index ad766d335e..151b1bf2ab 100644 --- a/Source/Activities/MultiplayerGame.cpp +++ b/Source/Activities/MultiplayerGame.cpp @@ -1,13 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MultiplayerGame.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Project: Retro Terrain Engine -// Author(s): - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MultiplayerGame.h" #include "WindowMan.h" @@ -44,12 +34,6 @@ namespace RTE { ConcreteClassInfo(MultiplayerGame, Activity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MultiplayerGame, effectively - // resetting the members of this abstraction level only. - void MultiplayerGame::Clear() { m_pGUIController = 0; m_pGUIInput = 0; @@ -74,22 +58,12 @@ namespace RTE { m_LastMusicPos = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MultiplayerGame object ready for use. - int MultiplayerGame::Create() { if (Activity::Create() < 0) return -1; return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MultiplayerGame to be identical to another, by deep copy. - int MultiplayerGame::Create(const MultiplayerGame& reference) { if (Activity::Create(reference) < 0) return -1; @@ -100,34 +74,15 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int MultiplayerGame::ReadProperty(const std::string_view& propName, Reader& reader) { return Activity::ReadProperty(propName, reader); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this MultiplayerGame with a Writer for - // later recreation with Create(Reader &reader); - int MultiplayerGame::Save(Writer& writer) const { Activity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MultiplayerGame object. - void MultiplayerGame::Destroy(bool notInherited) { g_FrameMan.SetDrawNetworkBackBuffer(false); g_NetworkClient.Disconnect(); @@ -141,12 +96,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int MultiplayerGame::Start() { int error = Activity::Start(); @@ -211,11 +160,6 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void MultiplayerGame::SetPaused(bool pause) { // Override the pause // m_Paused = false; @@ -238,11 +182,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void MultiplayerGame::End() { Activity::End(); @@ -250,12 +189,6 @@ namespace RTE { g_FrameMan.SetDrawNetworkBackBuffer(false); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this MultiplayerGame. Supposed to be done every frame - // before drawing. - void MultiplayerGame::Update() { Activity::Update(); diff --git a/Source/Activities/MultiplayerGame.h b/Source/Activities/MultiplayerGame.h index 66efb10480..e183378d0b 100644 --- a/Source/Activities/MultiplayerGame.h +++ b/Source/Activities/MultiplayerGame.h @@ -1,16 +1,9 @@ #ifndef _RTEMULTIPLAYERGAME_ #define _RTEMULTIPLAYERGAME_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MultiplayerGame.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Project: Retro Terrain Engine -// Author(s): - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Description: +/// Author(s): +/// Inclusions of header files #include "RTETools.h" #include "ActivityMan.h" @@ -28,19 +21,11 @@ namespace RTE { class GUILabel; class GUIComboBox; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: MultiplayerGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activity for editing scenes. - // Parent(s): EditorActivity. - // Class history: 8/30/2007 MultiplayerGame created, inheriting directly from Activity. - // 9/17/2007 Spliced out and made to derive from EditorActivty - + /// Activity for editing scenes. + /// 9/17/2007 Spliced out and made to derive from EditorActivty class MultiplayerGame : public Activity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(MultiplayerGame); @@ -53,130 +38,66 @@ namespace RTE { GAMEPLAY }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MultiplayerGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a MultiplayerGame object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a MultiplayerGame object in system + /// memory. Create() should be called before using the object. MultiplayerGame() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~MultiplayerGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a MultiplayerGame object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a MultiplayerGame object before deletion + /// from system memory. ~MultiplayerGame() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MultiplayerGame object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MultiplayerGame object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MultiplayerGame to be identical to another, by deep copy. - // Arguments: A reference to the MultiplayerGame to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a MultiplayerGame to be identical to another, by deep copy. + /// @param reference A reference to the MultiplayerGame to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const MultiplayerGame& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire MultiplayerGame, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire MultiplayerGame, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Activity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MultiplayerGame object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the MultiplayerGame object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -221,18 +142,10 @@ namespace RTE { // Position of music being played, used to recover playback state after pause double m_LastMusicPos; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/MultiplayerServerLobby.cpp b/Source/Activities/MultiplayerServerLobby.cpp index 670beaa6e3..2248b07861 100644 --- a/Source/Activities/MultiplayerServerLobby.cpp +++ b/Source/Activities/MultiplayerServerLobby.cpp @@ -1,13 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MultiplayerServerLobby.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Project: Retro Terrain Engine -// Author(s): - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MultiplayerServerLobby.h" #include "PresetMan.h" @@ -44,12 +34,6 @@ namespace RTE { ConcreteClassInfo(MultiplayerServerLobby, Activity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MultiplayerServerLobby, effectively - // resetting the members of this abstraction level only. - void MultiplayerServerLobby::Clear() { // m_pEditorGUI = 0; m_pGUIController = 0; @@ -103,11 +87,6 @@ namespace RTE { m_pUIDrawBitmap = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MultiplayerServerLobby object ready for use. - int MultiplayerServerLobby::Create() { if (Activity::Create() < 0) return -1; @@ -115,11 +94,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MultiplayerServerLobby to be identical to another, by deep copy. - int MultiplayerServerLobby::Create(const MultiplayerServerLobby& reference) { if (Activity::Create(reference) < 0) return -1; @@ -130,34 +104,15 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int MultiplayerServerLobby::ReadProperty(const std::string_view& propName, Reader& reader) { return Activity::ReadProperty(propName, reader); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this MultiplayerServerLobby with a Writer for - // later recreation with Create(Reader &reader); - int MultiplayerServerLobby::Save(Writer& writer) const { Activity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MultiplayerServerLobby object. - void MultiplayerServerLobby::Destroy(bool notInherited) { delete m_pGUIController; delete m_pGUIInput; @@ -175,12 +130,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int MultiplayerServerLobby::Start() { int error = GameActivity::Start(); @@ -1024,13 +973,6 @@ namespace RTE { UpdateInput(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateInput - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the user input processing. - // Arguments: None. - // Return value: None. - void MultiplayerServerLobby::UpdateInput() { // Move the dialog to the center of the player 0 screen so it could operate in absoute mouse coordintaes of the player 0 screen // During draw we move the dialog to 0,0 before drawing to draw it properly into the intermediate buffer co we can draw it centered on other player's screens. diff --git a/Source/Activities/MultiplayerServerLobby.h b/Source/Activities/MultiplayerServerLobby.h index 03fa83e5ea..49840cc973 100644 --- a/Source/Activities/MultiplayerServerLobby.h +++ b/Source/Activities/MultiplayerServerLobby.h @@ -1,16 +1,9 @@ #ifndef _RTEMULTIPLAYERSERVERLOBBY_ #define _RTEMULTIPLAYERSERVERLOBBY_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MultiplayerServerLobby.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Project: Retro Terrain Engine -// Author(s): - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Description: +/// Author(s): +/// Inclusions of header files #include "RTETools.h" #include "ActivityMan.h" #include "GameActivity.h" @@ -32,144 +25,74 @@ namespace RTE { class Scene; class Activity; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: MultiplayerServerLobby - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activity for editing scenes. - // Parent(s): EditorActivity. - // Class history: 8/30/2007 MultiplayerServerLobby created, inheriting directly from Activity. - // 9/17/2007 Spliced out and made to derive from EditorActivty - + /// Activity for editing scenes. + /// 9/17/2007 Spliced out and made to derive from EditorActivty class MultiplayerServerLobby : public GameActivity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(MultiplayerServerLobby); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MultiplayerServerLobby - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a MultiplayerServerLobby object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a MultiplayerServerLobby object in system + /// memory. Create() should be called before using the object. MultiplayerServerLobby() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~MultiplayerServerLobby - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a MultiplayerServerLobby object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a MultiplayerServerLobby object before deletion + /// from system memory. ~MultiplayerServerLobby() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MultiplayerServerLobby object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MultiplayerServerLobby object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MultiplayerServerLobby to be identical to another, by deep copy. - // Arguments: A reference to the MultiplayerServerLobby to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a MultiplayerServerLobby to be identical to another, by deep copy. + /// @param reference A reference to the MultiplayerServerLobby to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const MultiplayerServerLobby& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire MultiplayerServerLobby, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire MultiplayerServerLobby, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Activity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MultiplayerServerLobby object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the MultiplayerServerLobby object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; void UpdateInput(); @@ -192,9 +115,7 @@ namespace RTE { int PlayerCount(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // These add on the player and team max counts enum PlayerColumns { @@ -271,18 +192,10 @@ namespace RTE { // The map of Activity:ies, and the Scene:s compatible with each, neither of which are owned here std::map> m_Activities; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Activities/SceneEditor.cpp b/Source/Activities/SceneEditor.cpp index 70737d6454..fab32e5d63 100644 --- a/Source/Activities/SceneEditor.cpp +++ b/Source/Activities/SceneEditor.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneEditor.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the SceneEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "SceneEditor.h" #include "WindowMan.h" @@ -43,12 +31,6 @@ namespace RTE { ConcreteClassInfo(SceneEditor, EditorActivity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this SceneEditor, effectively - // resetting the members of this abstraction level only. - void SceneEditor::Clear() { m_pEditorGUI = 0; m_pNewTerrainCombo = 0; @@ -57,11 +39,6 @@ namespace RTE { m_pNewBG3Combo = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the SceneEditor object ready for use. - int SceneEditor::Create() { if (EditorActivity::Create() < 0) return -1; @@ -69,11 +46,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a SceneEditor to be identical to another, by deep copy. - int SceneEditor::Create(const SceneEditor& reference) { if (EditorActivity::Create(reference) < 0) return -1; @@ -84,14 +56,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int SceneEditor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return EditorActivity::ReadProperty(propName, reader)); /* @@ -102,22 +66,11 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this SceneEditor with a Writer for - // later recreation with Create(Reader &reader); - int SceneEditor::Save(Writer& writer) const { EditorActivity::Save(writer); return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneEditor object. - void SceneEditor::Destroy(bool notInherited) { delete m_pEditorGUI; @@ -126,12 +79,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts this. Creates all the data etc necessary to start - // the activity. - int SceneEditor::Start() { int error = EditorActivity::Start(); @@ -225,33 +172,17 @@ namespace RTE { return error; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - void SceneEditor::SetPaused(bool pause) { // Override the pause m_Paused = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - void SceneEditor::End() { EditorActivity::End(); m_ActivityState = ActivityState::Over; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this SceneEditor. Supposed to be done every frame - // before drawing. - void SceneEditor::Update() { EditorActivity::Update(); @@ -550,29 +481,16 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - void SceneEditor::DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos, int which) { m_pEditorGUI->Draw(pTargetBitmap, targetPos); EditorActivity::DrawGUI(pTargetBitmap, targetPos, which); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this SceneEditor's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void SceneEditor::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { EditorActivity::Draw(pTargetBitmap, targetPos); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneEditor::SaveScene(const std::string& saveAsName, bool forceOverwrite) { Scene* editedScene = g_SceneMan.GetScene(); editedScene->SetPresetName(saveAsName); @@ -641,11 +559,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateNewDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New dialog box, populates its lists etc. - void SceneEditor::UpdateNewDialog() { int scenesIndex = 0; @@ -717,11 +630,6 @@ namespace RTE { m_pNewBG3Combo->SetSelectedIndex(0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateLoadDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Load dialog box, populates its lists etc. - void SceneEditor::UpdateLoadDialog() { // Clear out the control m_pLoadNameCombo->ClearList(); @@ -743,11 +651,6 @@ namespace RTE { m_pLoadNameCombo->SetSelectedIndex(0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateSaveDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save dialog box, populates its lists etc. - void SceneEditor::UpdateSaveDialog() { m_pSaveNameBox->SetText((g_SceneMan.GetScene()->GetPresetName() == "None" || !m_HasEverBeenSaved) ? "New Scene" : g_SceneMan.GetScene()->GetPresetName()); @@ -757,11 +660,6 @@ namespace RTE { m_pSaveModuleLabel->SetText("Will save in " + g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/Scenes"); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateChangesDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save Changes dialog box, populates its lists etc. - void SceneEditor::UpdateChangesDialog() { if (m_HasEverBeenSaved) { dynamic_cast(m_pGUIController->GetControl("ChangesExpLabel"))->SetText("Do you want to save your changes to:"); @@ -775,11 +673,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateOverwriteDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Overwrite dialog box, populates its lists etc. - void SceneEditor::UpdateOverwriteDialog() { if (g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() == c_UserScenesModuleName) m_pOverwriteNameLabel->SetText(g_PresetMan.GetDataModule(m_ModuleSpaceID)->GetFileName() + "/" + g_SceneMan.GetScene()->GetPresetName()); diff --git a/Source/Activities/SceneEditor.h b/Source/Activities/SceneEditor.h index 784628a8cd..225cd0936c 100644 --- a/Source/Activities/SceneEditor.h +++ b/Source/Activities/SceneEditor.h @@ -1,18 +1,11 @@ #ifndef _RTESCENEEDITOR_ #define _RTESCENEEDITOR_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneEditor.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the SceneEditor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the SceneEditor class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "RTETools.h" #include "EditorActivity.h" @@ -30,219 +23,105 @@ namespace RTE { class GUILabel; class GUIComboBox; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: SceneEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activity for editing scenes. - // Parent(s): EditorActivity. - // Class history: 8/30/2007 SceneEditor created, inheriting directly from Activity. - // 9/17/2007 Spliced out and made to derive from EditorActivty - + /// Activity for editing scenes. + /// 9/17/2007 Spliced out and made to derive from EditorActivty class SceneEditor : public EditorActivity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(SceneEditor); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: SceneEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a SceneEditor object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a SceneEditor object in system + /// memory. Create() should be called before using the object. SceneEditor() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~SceneEditor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a SceneEditor object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a SceneEditor object before deletion + /// from system memory. ~SceneEditor() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the SceneEditor object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the SceneEditor object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a SceneEditor to be identical to another, by deep copy. - // Arguments: A reference to the SceneEditor to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a SceneEditor to be identical to another, by deep copy. + /// @param reference A reference to the SceneEditor to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const SceneEditor& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire SceneEditor, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire SceneEditor, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); EditorActivity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneEditor object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneEditor object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorMode(EditorActivity::EditorMode newMode) { m_EditorMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorActivity::EditorMode GetEditorMode() const { return m_EditorMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Start - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Officially starts the game accroding to parameters previously set. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Officially starts the game accroding to parameters previously set. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Start() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Pause - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pauses and unpauses the game. - // Arguments: Whether to pause the game or not. - // Return value: None. - + /// Pauses and unpauses the game. + /// @param pause Whether to pause the game or not. (default: true) void SetPaused(bool pause = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: End - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces the current game's end. - // Arguments: None. - // Return value: None. - + /// Forces the current game's end. void End() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this ActivityMan. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this ActivityMan. Supposed to be done every frame + /// before drawing. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the currently active GUI of a screen to a BITMAP of choice. - // Arguments: A pointer to a screen-sized BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which screen's GUI to draw onto the bitmap. - // Return value: None. - + /// Draws the currently active GUI of a screen to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which screen's GUI to draw onto the bitmap. (default: 0) void DrawGUI(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ActivityMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - // Arguments: A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this ActivityMan's current graphical representation to a + /// BITMAP of choice. This includes all game-related graphics. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. OWNERSHIP IS NOT TRANSFERRED! + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Saves the current Scene to an appropriate ini file, and asks user if they want to overwrite first if scene of this name exists. - /// - /// The name of the new Scene to be saved. - /// Whether to force any existing Scene of that name to be overwritten if it already exists. - /// Whether actually managed to save. Will return false both if a scene of this name already exists, or if other error. + /// @param saveAsName The name of the new Scene to be saved. + /// @param forceOverwrite Whether to force any existing Scene of that name to be overwritten if it already exists. + /// @return Whether actually managed to save. Will return false both if a scene of this name already exists, or if other error. bool SaveScene(const std::string& saveAsName, bool forceOverwrite = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateNewDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the New dialog box, populates its lists etc. void UpdateNewDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateLoadDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Load dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Load dialog box, populates its lists etc. void UpdateLoadDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateSaveDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save dialog box, populates its lists etc. void UpdateSaveDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateChangesDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Save Changes dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Save Changes dialog box, populates its lists etc. void UpdateChangesDialog() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: UpdateOverwriteDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Overwrite dialog box, populates its lists etc. - // Arguments: None. - // Return value: None. - + /// Updates the Overwrite dialog box, populates its lists etc. void UpdateOverwriteDialog() override; // Member variables @@ -260,18 +139,10 @@ namespace RTE { // The combobox which lists all the background SceneLayer:s that can be loaded for a new scene, far/sky GUIComboBox* m_pNewBG3Combo; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Activity, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Activity, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Entities/ACDropShip.cpp b/Source/Entities/ACDropShip.cpp index f4460bf278..dd7758329b 100644 --- a/Source/Entities/ACDropShip.cpp +++ b/Source/Entities/ACDropShip.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ACDropShip.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the ACDropShip class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "ACDropShip.h" #include "AtomGroup.h" #include "Controller.h" @@ -23,12 +11,6 @@ namespace RTE { ConcreteClassInfo(ACDropShip, ACraft, 10); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ACDropShip, effectively - // resetting the members of this abstraction level only. - void ACDropShip::Clear() { m_pBodyAG = 0; m_pRThruster = 0; @@ -46,11 +28,6 @@ namespace RTE { m_HoverHeightModifier = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ACDropShip object ready for use. - int ACDropShip::Create() { if (ACraft::Create() < 0) return -1; @@ -62,11 +39,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ACDropShip to be identical to another, by deep copy. - int ACDropShip::Create(const ACDropShip& reference) { if (reference.m_pRThruster) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRThruster->GetUniqueID()); @@ -124,14 +96,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int ACDropShip::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return ACraft::ReadProperty(propName, reader)); @@ -150,12 +114,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this ACDropShip with a Writer for - // later recreation with Create(Reader &reader); - int ACDropShip::Save(Writer& writer) const { ACraft::Save(writer); @@ -184,11 +142,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the ACDropShip object. - void ACDropShip::Destroy(bool notInherited) { delete m_pBodyAG; @@ -197,12 +150,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAltitude - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the altitide of this' pos (or appropriate low point) over the - // terrain, in pixels. - float ACDropShip::GetAltitude(int max, int accuracy) { // Check altitude both thrusters, and report the one closest to the ground. Vector rPos, lPos; @@ -230,11 +177,6 @@ namespace RTE { return MIN(cAlt, MIN(rAlt, lAlt)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DetectObstacle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks for obstacles in the travel direction. - MOID ACDropShip::DetectObstacle(float distance) { // Check altitude both thrusters, and report the one closest to the ground. Vector rPos, lPos; @@ -278,8 +220,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - void ACDropShip::PreControllerUpdate() { ZoneScoped; @@ -491,8 +431,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACDropShip::SetRightThruster(AEmitter* newThruster) { if (m_pRThruster && m_pRThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pRThruster); @@ -516,8 +454,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACDropShip::SetLeftThruster(AEmitter* newThruster) { if (m_pLThruster && m_pLThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pLThruster); @@ -541,8 +477,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACDropShip::SetURightThruster(AEmitter* newThruster) { if (m_pURThruster && m_pURThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pURThruster); @@ -565,8 +499,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACDropShip::SetULeftThruster(AEmitter* newThruster) { if (m_pULThruster && m_pULThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pULThruster); @@ -589,8 +521,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACDropShip::SetRightHatch(Attachable* newHatch) { if (m_pRHatch && m_pRHatch->IsAttached()) { RemoveAndDeleteAttachable(m_pRHatch); @@ -612,8 +542,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACDropShip::SetLeftHatch(Attachable* newHatch) { if (m_pLHatch && m_pLHatch->IsAttached()) { RemoveAndDeleteAttachable(m_pLHatch); diff --git a/Source/Entities/ACDropShip.h b/Source/Entities/ACDropShip.h index 3324024498..c935172d42 100644 --- a/Source/Entities/ACDropShip.h +++ b/Source/Entities/ACDropShip.h @@ -1,287 +1,164 @@ #ifndef _RTEACDROPSHIP_ #define _RTEACDROPSHIP_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ACDropShip.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the ACDropShip class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the ACDropShip class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "ACraft.h" namespace RTE { class Attachable; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: ACDropShip - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A hovering craft, with two engines on each attached on each end which - // tilt independently of the body to achieve steering. - // Parent(s): ACraft. - // Class history: 12/13/2006 ACDropShip created. - + /// A hovering craft, with two engines on each attached on each end which + /// tilt independently of the body to achieve steering. class ACDropShip : public ACraft { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(ACDropShip); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: ACDropShip - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a ACDropShip object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a ACDropShip object in system + /// memory. Create() should be called before using the object. ACDropShip() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~ACDropShip - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a ACDropShip object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a ACDropShip object before deletion + /// from system memory. ~ACDropShip() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ACDropShip object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the ACDropShip object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ACDropShip to be identical to another, by deep copy. - // Arguments: A reference to the ACDropShip to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a ACDropShip to be identical to another, by deep copy. + /// @param reference A reference to the ACDropShip to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const ACDropShip& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire ACDropShip, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire ACDropShip, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); ACraft::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAltitude - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the altitide of this' pos (or appropriate low point) over the - // terrain, in pixels. - // Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. - // The accuracy within which measurement is acceptable. Higher number - // here means less calculation. - // Return value: The rough altitude over the terrain, in pixels. - + /// Gets the altitide of this' pos (or appropriate low point) over the + /// terrain, in pixels. + /// @param max The max altitude you care to check for. 0 Means check the whole scene's height. (default: 0) + /// @param accuracy The accuracy within which measurement is acceptable. Higher number (default: 0) + /// here means less calculation. + /// @return The rough altitude over the terrain, in pixels. float GetAltitude(int max = 0, int accuracy = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DetectObstacle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks for obstacles in the travel direction. - // Arguments: How far ahead of travel direction to check for obstacles. - // Return value: Which MOID was detected as obstacle. g_NoMOID means nothing was detected. - + /// Checks for obstacles in the travel direction. + /// @param distance How far ahead of travel direction to check for obstacles. + /// @return Which MOID was detected as obstacle. g_NoMOID means nothing was detected. MOID DetectObstacle(float distance); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AutoStabilizing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether this has the means and will try to right itself, or if - // that's up to the Controller to do. - // Arguments: None. - // Return value: Wheter this will try to auto stabilize. - + /// Tells whether this has the means and will try to right itself, or if + /// that's up to the Controller to do. + /// @return Wheter this will try to auto stabilize. bool AutoStabilizing() override { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreControllerUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Update called prior to controller update. Ugly hack. Supposed to be done every frame. void PreControllerUpdate() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMaxPassengers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The recomended, not absolute, maximum number of actors that fit in the - // invetory. Used by the activity AI. - // Arguments: None. - // Return value: An integer with the recomended number of actors that fit in the craft. - // Default is four. - + /// Virtual method: GetMaxPassengers + /// The recomended, not absolute, maximum number of actors that fit in the + /// invetory. Used by the activity AI. + /// @return An integer with the recomended number of actors that fit in the craft. + /// Default is four. int GetMaxPassengers() const override { return m_MaxPassengers > -1 ? m_MaxPassengers : 4; } - /// /// Gets the right side thruster of this ACDropship. - /// - /// A pointer to the right side thruster of this ACDropship. Ownership is NOT transferred. + /// @return A pointer to the right side thruster of this ACDropship. Ownership is NOT transferred. AEmitter* GetRightThruster() const { return m_pRThruster; } - /// /// Sets the right side thruster for this ACDropship. - /// - /// The new thruster to use. + /// @param newThruster The new thruster to use. void SetRightThruster(AEmitter* newThruster); - /// /// Gets the left side thruster of this ACDropship. - /// - /// A pointer to the left side thruster of this ACDropship. Ownership is NOT transferred. + /// @return A pointer to the left side thruster of this ACDropship. Ownership is NOT transferred. AEmitter* GetLeftThruster() const { return m_pLThruster; } - /// /// Sets the left side thruster for this ACDropship. - /// - /// The new thruster to use. + /// @param newThruster The new thruster to use. void SetLeftThruster(AEmitter* newThruster); - /// /// Gets the right side secondary thruster of this ACDropship. - /// - /// A pointer to the right side secondary thruster of this ACDropship. Ownership is NOT transferred. + /// @return A pointer to the right side secondary thruster of this ACDropship. Ownership is NOT transferred. AEmitter* GetURightThruster() const { return m_pURThruster; } - /// /// Sets the right side secondary thruster for this ACDropship. - /// - /// The new thruster to use. + /// @param newThruster The new thruster to use. void SetURightThruster(AEmitter* newThruster); - /// /// Gets the left side secondary thruster of this ACDropship. - /// - /// A pointer to the left side secondary thruster of this ACDropship. Ownership is NOT transferred. + /// @return A pointer to the left side secondary thruster of this ACDropship. Ownership is NOT transferred. AEmitter* GetULeftThruster() const { return m_pULThruster; } - /// /// Sets the left side secondary thruster for this ACDropship. - /// - /// The new thruster to use. + /// @param newThruster The new thruster to use. void SetULeftThruster(AEmitter* newThruster); - /// /// Gets the left side hatch of this ACDropship. - /// - /// A pointer to the left side hatch of this ACDropship. Ownership is NOT transferred. + /// @return A pointer to the left side hatch of this ACDropship. Ownership is NOT transferred. Attachable* GetLeftHatch() const { return m_pLHatch; } - /// /// Sets the left side hatch for this ACDropship. - /// - /// The new hatch to use. + /// @param newHatch The new hatch to use. void SetLeftHatch(Attachable* newHatch); - /// /// Gets the right side hatch of this ACDropship. - /// - /// A pointer to the right side hatch of this ACDropship. Ownership is NOT transferred. + /// @return A pointer to the right side hatch of this ACDropship. Ownership is NOT transferred. Attachable* GetRightHatch() const { return m_pRHatch; } - /// /// Sets the right side hatch for this ACDropship. - /// - /// The new hatch to use. + /// @param newHatch The new hatch to use. void SetRightHatch(Attachable* newHatch); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMaxEngineAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get max engine rotation angle in degrees. - // Arguments: None. - // Return value: Max engine angle in degrees. - + /// Get max engine rotation angle in degrees. + /// @return Max engine angle in degrees. float GetMaxEngineAngle() const { return m_MaxEngineAngle; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetMaxEngineAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets max engine rotation angle in degrees. - // Arguments: Max engine angle in degrees. - // Return value: None. - + /// Sets max engine rotation angle in degrees. + /// @param newAngle Max engine angle in degrees. void SetMaxEngineAngle(float newAngle) { m_MaxEngineAngle = newAngle; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetLateralControlSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the abstract rate of LateralControl change. Default is 6 - // Arguments: None. - // Return value: Current lateral control speed value. - + /// Gets the abstract rate of LateralControl change. Default is 6 + /// @return Current lateral control speed value. float GetLateralControlSpeed() const { return m_LateralControlSpeed; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetLateralControlSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the abstract rate of LateralControl change. Default is 6 - // Arguments: New lateral control speed value. - // Return value: None. - + /// Sets the abstract rate of LateralControl change. Default is 6 + /// @param newSpeed New lateral control speed value. void SetLateralControlSpeed(float newSpeed) { m_LateralControl = newSpeed; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetLateralControl - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets lateral control value -1.0 to 1.0 control of sideways movement. 0 means try to stand still in X. - // Arguments: None. - // Return value: Current lateral control value. - + /// Sets lateral control value -1.0 to 1.0 control of sideways movement. 0 means try to stand still in X. + /// @return Current lateral control value. float GetLateralControl() const { return m_LateralControl; } - /// /// Gets the modifier for height at which this ACDropship should hover above terrain. - /// - /// The modifier for height at which this ACDropship should hover above terrain. + /// @return The modifier for height at which this ACDropship should hover above terrain. float GetHoverHeightModifier() const { return m_HoverHeightModifier; } - /// /// Sets the modifier for height at which this ACDropship should hover above terrain. - /// - /// The new modifier for height at which this ACDropship should hover above terrain. + /// @param newHoverHeightModifier The new modifier for height at which this ACDropship should hover above terrain. void SetHoverHeightModifier(float newHoverHeightModifier) { m_HoverHeightModifier = newHoverHeightModifier; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -315,18 +192,10 @@ namespace RTE { float m_HoverHeightModifier; //!< The modifier for the height at which this ACDropShip should hover above terrain when releasing its cargo. Used in cpp and Lua AI. - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ACDropShip, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this ACDropShip, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/ACRocket.cpp b/Source/Entities/ACRocket.cpp index 7219d80b4c..054370be5d 100644 --- a/Source/Entities/ACRocket.cpp +++ b/Source/Entities/ACRocket.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ACRocket.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the ACRocket class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "ACRocket.h" #include "AtomGroup.h" #include "Attachable.h" @@ -27,12 +15,6 @@ namespace RTE { ConcreteClassInfo(ACRocket, ACraft, 10); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ACRocket, effectively - // resetting the members of this abstraction level only. - void ACRocket::Clear() { // m_pCapsule = 0; m_pRLeg = 0; @@ -55,11 +37,6 @@ namespace RTE { m_MaxGimbalAngle = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ACRocket object ready for use. - int ACRocket::Create() { // Read all the properties if (ACraft::Create() < 0) @@ -80,11 +57,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ACRocket to be identical to another, by deep copy. - int ACRocket::Create(const ACRocket& reference) { if (reference.m_pRLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRLeg->GetUniqueID()); @@ -165,14 +137,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int ACRocket::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return ACraft::ReadProperty(propName, reader)); @@ -207,12 +171,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this ACRocket with a Writer for - // later recreation with Create(Reader &reader); - int ACRocket::Save(Writer& writer) const { ACraft::Save(writer); @@ -248,11 +206,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the ACRocket object. - void ACRocket::Destroy(bool notInherited) { delete m_pBodyAG; delete m_pRFootGroup; @@ -267,12 +220,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAltitude - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the altitide of this' pos (or appropriate low point) over the - // terrain, in pixels. - float ACRocket::GetAltitude(int max, int accuracy) { // Use the main thruster's position as the position ot measure from Vector pos; @@ -284,8 +231,6 @@ namespace RTE { return g_SceneMan.FindAltitude(pos, max, accuracy, true); } - ////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::PreControllerUpdate() { ACraft::PreControllerUpdate(); @@ -424,8 +369,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::Update() { ACraft::Update(); @@ -452,8 +395,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::SetRightLeg(Leg* newLeg) { if (m_pRLeg && m_pRLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pRLeg); @@ -476,8 +417,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::SetLeftLeg(Leg* newLeg) { if (m_pLLeg && m_pLLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pLLeg); @@ -501,8 +440,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::SetMainThruster(AEmitter* newThruster) { if (m_pMThruster && m_pMThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pMThruster); @@ -526,8 +463,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::SetRightThruster(AEmitter* newThruster) { if (m_pRThruster && m_pRThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pRThruster); @@ -551,8 +486,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::SetLeftThruster(AEmitter* newThruster) { if (m_pLThruster && m_pLThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pLThruster); @@ -576,8 +509,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::SetURightThruster(AEmitter* newThruster) { if (m_pURThruster && m_pURThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pURThruster); @@ -601,8 +532,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::SetULeftThruster(AEmitter* newThruster) { if (m_pULThruster && m_pULThruster->IsAttached()) { RemoveAndDeleteAttachable(m_pULThruster); @@ -626,8 +555,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { ACraft::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); diff --git a/Source/Entities/ACRocket.h b/Source/Entities/ACRocket.h index 9434db876a..f7395d7264 100644 --- a/Source/Entities/ACRocket.h +++ b/Source/Entities/ACRocket.h @@ -1,18 +1,11 @@ #ifndef _RTEACROCKET_ #define _RTEACROCKET_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ACRocket.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the ACRocket class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the ACRocket class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "ACraft.h" namespace RTE { @@ -22,15 +15,9 @@ namespace RTE { class Leg; // class LimbPath; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: ACRocket - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A rocket craft, with main engine and rotation of the whole body as a - // means of steering. - // Parent(s): ACraft. - // Class history: 09/02/2004 ARocket created. - // 12/13/2006 ARocket changed names to ACRocket, parent changed to ACraft - + /// A rocket craft, with main engine and rotation of the whole body as a + /// means of steering. + /// 12/13/2006 ARocket changed names to ACRocket, parent changed to ACraft class ACRocket : public ACraft { friend struct EntityLuaBindings; @@ -42,227 +29,137 @@ namespace RTE { GearStateCount }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(ACRocket); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: ACRocket - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a ACRocket object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a ACRocket object in system + /// memory. Create() should be called before using the object. ACRocket() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~ACRocket - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a ACRocket object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a ACRocket object before deletion + /// from system memory. ~ACRocket() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ACRocket object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the ACRocket object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ACRocket to be identical to another, by deep copy. - // Arguments: A reference to the ACRocket to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a ACRocket to be identical to another, by deep copy. + /// @param reference A reference to the ACRocket to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const ACRocket& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire ACRocket, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire ACRocket, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); ACraft::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAltitude - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the altitide of this' pos (or appropriate low point) over the - // terrain, in pixels. - // Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. - // The accuracy within which measurement is acceptable. Higher number - // here means less calculation. - // Return value: The rough altitude over the terrain, in pixels. - + /// Gets the altitide of this' pos (or appropriate low point) over the + /// terrain, in pixels. + /// @param max The max altitude you care to check for. 0 Means check the whole scene's height. (default: 0) + /// @param accuracy The accuracy within which measurement is acceptable. Higher number (default: 0) + /// here means less calculation. + /// @return The rough altitude over the terrain, in pixels. float GetAltitude(int max = 0, int accuracy = 0) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreControllerUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Update called prior to controller update. Ugly hack. Supposed to be done every frame. void PreControllerUpdate() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: Nosssssssne. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. + /// @param Nosssssssne. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ACRocket's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this ACRocket's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMaxPassengers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The recomended, not absolute, maximum number of actors that fit in the - // invetory. Used by the activity AI. - // Arguments: None. - // Return value: An integer with the recomended number of actors that fit in the craft. - // Default is two. - + /// Virtual method: GetMaxPassengers + /// The recomended, not absolute, maximum number of actors that fit in the + /// invetory. Used by the activity AI. + /// @return An integer with the recomended number of actors that fit in the craft. + /// Default is two. int GetMaxPassengers() const override { return m_MaxPassengers > -1 ? m_MaxPassengers : 2; } - /// /// Gets the right leg of this ACRocket. - /// - /// A pointer to the right Leg of this ACRocket. Ownership is NOT transferred. + /// @return A pointer to the right Leg of this ACRocket. Ownership is NOT transferred. Leg* GetRightLeg() const { return m_pRLeg; } - /// /// Sets the right Leg for this ACRocket. - /// - /// The new Leg to use. + /// @param newLeg The new Leg to use. void SetRightLeg(Leg* newLeg); - /// /// Gets the left Leg of this ACRocket. - /// - /// A pointer to the left Leg of this ACRocket. Ownership is NOT transferred. + /// @return A pointer to the left Leg of this ACRocket. Ownership is NOT transferred. Leg* GetLeftLeg() const { return m_pLLeg; } - /// /// Sets the left Leg for this ACRocket. - /// - /// The new Leg to use. + /// @param newLeg The new Leg to use. void SetLeftLeg(Leg* newLeg); - /// /// Gets the main thruster of this ACRocket. - /// - /// A pointer to the main thruster of this ACRocket. Ownership is NOT transferred. + /// @return A pointer to the main thruster of this ACRocket. Ownership is NOT transferred. AEmitter* GetMainThruster() const { return m_pMThruster; } - /// /// Sets the main thruster for this ACRocket. - /// - /// The new thruster to use. + /// @param newThruster The new thruster to use. void SetMainThruster(AEmitter* newThruster); - /// /// Gets the right side thruster of this ACRocket. - /// - /// A pointer to the right side thruster of this ACRocket. Ownership is NOT transferred. + /// @return A pointer to the right side thruster of this ACRocket. Ownership is NOT transferred. AEmitter* GetRightThruster() const { return m_pRThruster; } - /// /// Sets the right side thruster for this ACRocket. - /// - /// The new thruster to use. + /// @param newThruster The new thruster to use. void SetRightThruster(AEmitter* newThruster); - /// /// Gets the left side thruster of this ACRocket. - /// - /// A pointer to the left side thruster of this ACRocket. Ownership is NOT transferred. + /// @return A pointer to the left side thruster of this ACRocket. Ownership is NOT transferred. AEmitter* GetLeftThruster() const { return m_pLThruster; } - /// /// Sets the left side thruster for this ACRocket. - /// - /// The new thruster to use. + /// @param newThruster The new thruster to use. void SetLeftThruster(AEmitter* newThruster); - /// /// Gets the right side secondary thruster of this ACRocket. - /// - /// A pointer to the right side secondary thruster of this ACRocket. Ownership is NOT transferred. + /// @return A pointer to the right side secondary thruster of this ACRocket. Ownership is NOT transferred. AEmitter* GetURightThruster() const { return m_pURThruster; } - /// /// Sets the right side secondary thruster for this ACRocket. - /// - /// The new thruster to use. + /// @param newThruster The new thruster to use. void SetURightThruster(AEmitter* newThruster); - /// /// Gets the left side secondary thruster of this ACRocket. - /// - /// A pointer to the left side secondary thruster of this ACRocket. Ownership is NOT transferred. + /// @return A pointer to the left side secondary thruster of this ACRocket. Ownership is NOT transferred. AEmitter* GetULeftThruster() const { return m_pULThruster; } - /// /// Sets the left side secondary thruster for this ACRocket. - /// - /// The new thruster to use. + /// @param newThruster The new thruster to use. void SetULeftThruster(AEmitter* newThruster); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGearState - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the the landing gear state - // Arguments: None. - // Return value: Current landing gear state. - + /// Method: GetGearState + /// Gets the the landing gear state + /// @return Current landing gear state. unsigned int GetGearState() const { return m_GearState; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -290,18 +187,10 @@ namespace RTE { LimbPath m_Paths[2][GearStateCount]; float m_MaxGimbalAngle; //!< How much the main engine is able to tilt in order to stabilize the rocket. - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ACRocket, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this ACRocket, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/ACrab.cpp b/Source/Entities/ACrab.cpp index 3b742084b8..9dca51ac2d 100644 --- a/Source/Entities/ACrab.cpp +++ b/Source/Entities/ACrab.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ACrab.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the ACrab class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "ACrab.h" #include "AtomGroup.h" @@ -36,12 +24,6 @@ namespace RTE { ConcreteClassInfo(ACrab, Actor, 20); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ACrab, effectively - // resetting the members of this abstraction level only. - void ACrab::Clear() { m_pTurret = 0; m_pLFGLeg = 0; @@ -91,11 +73,6 @@ namespace RTE { m_LockMouseAimInput = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ACrab object ready for use. - int ACrab::Create() { // Read all the properties if (Actor::Create() < 0) { @@ -156,11 +133,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ACrab to be identical to another, by deep copy. - int ACrab::Create(const ACrab& reference) { if (reference.m_pLBGLeg) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLBGLeg->GetUniqueID()); @@ -269,14 +241,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int ACrab::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Actor::ReadProperty(propName, reader)); @@ -369,12 +333,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this ACrab with a Writer for - // later recreation with Create(Reader &reader); - int ACrab::Save(Writer& writer) const { Actor::Save(writer); @@ -424,11 +382,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the ACrab object. - void ACrab::Destroy(bool notInherited) { delete m_pLFGFootGroup; delete m_pLBGFootGroup; @@ -474,12 +427,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetEyePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of this' eye, or equivalent, where look - // vector starts from. - Vector ACrab::GetEyePos() const { if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) return m_pTurret->GetFirstMountedDevice()->GetPos(); @@ -487,8 +434,6 @@ namespace RTE { return m_Pos; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACrab::SetTurret(Turret* newTurret) { if (m_pTurret && m_pTurret->IsAttached()) { RemoveAndDeleteAttachable(m_pTurret); @@ -511,8 +456,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACrab::SetJetpack(AEJetpack* newJetpack) { if (m_pJetpack && m_pJetpack->IsAttached()) { RemoveAndDeleteAttachable(m_pJetpack); @@ -537,8 +480,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACrab::SetLeftFGLeg(Leg* newLeg) { if (m_pLFGLeg && m_pLFGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pLFGLeg); @@ -562,8 +503,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACrab::SetLeftBGLeg(Leg* newLeg) { if (m_pLBGLeg && m_pLBGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pLBGLeg); @@ -587,8 +526,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACrab::SetRightFGLeg(Leg* newLeg) { if (m_pRFGLeg && m_pRFGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pRFGLeg); @@ -611,8 +548,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACrab::SetRightBGLeg(Leg* newLeg) { if (m_pRBGLeg && m_pRBGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pRBGLeg); @@ -635,19 +570,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* ACrab::GetGraphicalIcon() const { return m_GraphicalIcon ? m_GraphicalIcon : (m_pTurret ? m_pTurret->GetSpriteFrame(0) : GetSpriteFrame(0)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - bool ACrab::CollideAtPoint(HitData& hd) { return Actor::CollideAtPoint(hd); @@ -712,8 +638,6 @@ namespace RTE { } */ - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ACrab::HandlePieCommand(PieSlice::SliceType pieSliceIndex) { if (pieSliceIndex != PieSlice::SliceType::NoType) { if (pieSliceIndex == PieSlice::SliceType::Reload) { @@ -736,13 +660,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: GetEquippedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whatever is equipped in the turret, if anything. OWNERSHIP IS NOT TRANSFERRED! - MovableObject* ACrab::GetEquippedItem() const { if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { return m_pTurret->GetFirstMountedDevice(); @@ -751,12 +668,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmIsReady - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held device's current mag is empty on - // ammo or not. - bool ACrab::FirearmIsReady() const { if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { for (const HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { @@ -769,20 +680,10 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmIsEmpty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is out of ammo. - bool ACrab::FirearmIsEmpty() const { return !FirearmIsReady() && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmNeedsReload - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is almost out of ammo. - bool ACrab::FirearmsAreFull() const { if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { for (const HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { @@ -794,8 +695,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - bool ACrab::FirearmNeedsReload() const { if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { for (const HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { @@ -808,11 +707,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmIsSemiAuto - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is semi or full auto. - bool ACrab::FirearmIsSemiAuto() const { if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { HDFirearm* pWeapon = dynamic_cast(m_pTurret->GetFirstMountedDevice()); @@ -821,13 +715,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: ReloadFirearms - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reloads the currently held firearms, if any. - // Arguments: None. - // Return value: None. - void ACrab::ReloadFirearms() { if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { for (HeldDevice* mountedDevice: m_pTurret->GetMountedDevices()) { @@ -838,12 +725,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmActivationDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the currently held device's delay between pulling the trigger - // and activating. - int ACrab::FirearmActivationDelay() const { // Check if the currently held device is already the desired type if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { @@ -855,12 +736,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsWithinRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a point on the scene is within range of the currently - // used device and aiming status, if applicable. - bool ACrab::IsWithinRange(Vector& point) const { if (m_SharpAimMaxedOut) return true; @@ -884,13 +759,6 @@ namespace RTE { return sqrDistance <= (range * range); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Look - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an unseen-revealing ray in the direction of where this is facing. - // Arguments: The degree angle to deviate from the current view point in the ray - // casting. A random ray will be chosen out of this +-range. - bool ACrab::Look(float FOVSpread, float range) { if (!g_SceneMan.AnythingUnseen(m_Team)) return false; @@ -925,13 +793,6 @@ namespace RTE { return g_SceneMan.CastSeeRay(m_Team, aimPos, lookVector, ignored, 25, (int)g_SceneMan.GetUnseenResolution(m_Team).GetSmallest() / 2); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: LookForMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an MO detecting ray in the direction of where the head is looking - // at the time. Factors including head rotation, sharp aim mode, and - // other variables determine how this ray is cast. - MovableObject* ACrab::LookForMOs(float FOVSpread, unsigned char ignoreMaterial, bool ignoreAllTerrain) { MovableObject* pSeenMO = 0; Vector aimPos = m_Pos; @@ -998,8 +859,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void ACrab::PreControllerUpdate() { ZoneScoped; @@ -1417,8 +1276,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void ACrab::Update() { ZoneScoped; @@ -1551,8 +1408,6 @@ namespace RTE { // m_DeepCheck = true/*m_Status == DEAD*/; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACrab::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { Actor::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); @@ -1571,12 +1426,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Actor's current graphical HUD overlay representation to a - // BITMAP of choice. - void ACrab::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { m_HUDStack = -m_CharHeight / 2; @@ -1786,20 +1635,10 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetLimbPathSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get walking limb path speed for the specified preset. - float ACrab::GetLimbPathSpeed(int speedPreset) const { return m_Paths[LEFTSIDE][FGROUND][WALK].GetSpeed(speedPreset); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetLimbPathSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Set walking limb path speed for the specified preset. - void ACrab::SetLimbPathSpeed(int speedPreset, float speed) { m_Paths[LEFTSIDE][FGROUND][WALK].OverrideSpeed(speedPreset, speed); m_Paths[RIGHTSIDE][FGROUND][WALK].OverrideSpeed(speedPreset, speed); @@ -1808,22 +1647,10 @@ namespace RTE { m_Paths[RIGHTSIDE][BGROUND][WALK].OverrideSpeed(speedPreset, speed); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetLimbPathPushForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the force that a limb traveling walking LimbPath can push against - // stuff in the scene with. - float ACrab::GetLimbPathPushForce() const { return m_Paths[LEFTSIDE][FGROUND][WALK].GetDefaultPushForce(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetLimbPathPushForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the default force that a limb traveling walking LimbPath can push against - // stuff in the scene with. - void ACrab::SetLimbPathPushForce(float force) { m_Paths[LEFTSIDE][FGROUND][WALK].OverridePushForce(force); m_Paths[RIGHTSIDE][FGROUND][WALK].OverridePushForce(force); @@ -1832,8 +1659,6 @@ namespace RTE { m_Paths[RIGHTSIDE][BGROUND][WALK].OverridePushForce(force); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ACrab::WhilePieMenuOpenListener(const PieMenu* pieMenu) { int result = Actor::WhilePieMenuOpenListener(pieMenu); diff --git a/Source/Entities/ACrab.h b/Source/Entities/ACrab.h index 8258f884c8..3bef3a1df1 100644 --- a/Source/Entities/ACrab.h +++ b/Source/Entities/ACrab.h @@ -1,18 +1,11 @@ #ifndef _RTEACRAB_ #define _RTEACRAB_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ACrab.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the ACrab class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the ACrab class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Actor.h" #include "LimbPath.h" #include "Leg.h" @@ -24,13 +17,7 @@ namespace RTE { class Turret; class AEJetpack; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: ACrab - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A crab-like actor with four legs. - // Parent(s): Actor. - // Class history: 10/24/2007 ACrab created. - + /// A crab-like actor with four legs. class ACrab : public Actor { friend struct EntityLuaBindings; @@ -46,9 +33,7 @@ namespace RTE { LAYERCOUNT }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(ACrab); @@ -57,423 +42,245 @@ namespace RTE { ClassInfoGetters; DefaultPieMenuNameGetter(HasObjectInGroup("Turrets") ? "Default Turret Pie Menu" : "Default Crab Pie Menu"); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: ACrab - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a ACrab object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a ACrab object in system + /// memory. Create() should be called before using the object. ACrab() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~ACrab - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a ACrab object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a ACrab object before deletion + /// from system memory. ~ACrab() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ACrab object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the ACrab object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ACrab to be identical to another, by deep copy. - // Arguments: A reference to the ACrab to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a ACrab to be identical to another, by deep copy. + /// @param reference A reference to the ACrab to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const ACrab& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire ACrab, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire ACrab, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Actor::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetEyePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of this' eye, or equivalent, where look - // vector starts from. - // Arguments: None. - // Return value: A Vector with the absolute position of this' eye or view point. - + /// Gets the absoltue position of this' eye, or equivalent, where look + /// vector starts from. + /// @return A Vector with the absolute position of this' eye or view point. Vector GetEyePos() const override; - /// /// Gets the Turret of this ACrab. - /// - /// A pointer to Turret of this ACrab. Ownership is NOT transferred! + /// @return A pointer to Turret of this ACrab. Ownership is NOT transferred! Turret* GetTurret() const { return m_pTurret; } - /// /// Sets the Turret for this ACrab. Ownership IS transferred! - /// - /// The new Turret to use. + /// @param newTurret The new Turret to use. void SetTurret(Turret* newTurret); - /// /// Gets the jetpack of this ACrab. - /// - /// A pointer to the jetpack of this ACrab. Ownership is NOT transferred! + /// @return A pointer to the jetpack of this ACrab. Ownership is NOT transferred! AEJetpack* GetJetpack() const { return m_pJetpack; } - /// /// Sets the jetpack for this ACrab. Ownership IS Transferred! - /// - /// The new jetpack to use. + /// @param newJetpack The new jetpack to use. void SetJetpack(AEJetpack* newJetpack); - /// /// Gets the left foreground Leg of this ACrab. - /// - /// A pointer to the left foreground Leg of this ACrab. Ownership is NOT transferred! + /// @return A pointer to the left foreground Leg of this ACrab. Ownership is NOT transferred! Leg* GetLeftFGLeg() const { return m_pLFGLeg; } - /// /// Sets the left foreground Leg for this ACrab. Ownership IS transferred! - /// - /// The new Leg to use. + /// @param newLeg The new Leg to use. void SetLeftFGLeg(Leg* newLeg); - /// /// Gets the left background Leg of this ACrab. - /// - /// A pointer to the left background Leg of this ACrab. Ownership is NOT transferred! + /// @return A pointer to the left background Leg of this ACrab. Ownership is NOT transferred! Leg* GetLeftBGLeg() const { return m_pLBGLeg; } - /// /// Sets the left background Leg for this ACrab. Ownership IS transferred! - /// - /// The new Leg to use. + /// @param newLeg The new Leg to use. void SetLeftBGLeg(Leg* newLeg); - /// /// Gets the right foreground Leg of this ACrab. - /// - /// A pointer to the right foreground Leg of this ACrab. Ownership is NOT transferred! + /// @return A pointer to the right foreground Leg of this ACrab. Ownership is NOT transferred! Leg* GetRightFGLeg() const { return m_pRFGLeg; } - /// /// Sets the right foreground Leg for this ACrab. Ownership IS transferred! - /// - /// The new Leg to use. + /// @param newLeg The new Leg to use. void SetRightFGLeg(Leg* newLeg); - /// /// Gets the right BG Leg of this ACrab. - /// - /// A pointer to the right background Leg of this ACrab. Ownership is NOT transferred! + /// @return A pointer to the right background Leg of this ACrab. Ownership is NOT transferred! Leg* GetRightBGLeg() const { return m_pRBGLeg; } - /// /// Sets the right background Leg for this ACrab. Ownership IS transferred! - /// - /// The new Leg to use. + /// @param newLeg The new Leg to use. void SetRightBGLeg(Leg* newLeg); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - // Arguments: Reference to the HitData struct which describes the collision. This - // will be modified to represent the results of the collision. - // Return value: Whether the collision has been deemed valid. If false, then disregard - // any impulses in the Hitdata. - + /// Calculates the collision response when another MO's Atom collides with + /// this MO's physical representation. The effects will be applied + /// directly to this MO, and also represented in the passed in HitData. + /// @param hitData Reference to the HitData struct which describes the collision. This + /// will be modified to represent the results of the collision. + /// @return Whether the collision has been deemed valid. If false, then disregard + /// any impulses in the Hitdata. bool CollideAtPoint(HitData& hitData) override; - /// /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. - /// - /// The SliceType of the PieSlice being handled. - /// Whether or not the activated PieSlice SliceType was able to be handled. + /// @param pieSliceType The SliceType of the PieSlice being handled. + /// @return Whether or not the activated PieSlice SliceType was able to be handled. bool HandlePieCommand(PieSlice::SliceType pieSliceType) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEquippedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whatever is equipped in the turret, if anything. OWNERSHIP IS NOT TRANSFERRED! - // Arguments: None. - // Return value: The currently equipped item, if any. - + /// Returns whatever is equipped in the turret, if anything. OWNERSHIP IS NOT TRANSFERRED! + /// @return The currently equipped item, if any. MovableObject* GetEquippedItem() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FirearmIsReady - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is ready for use, and has - // ammo etc. - // Arguments: None. - // Return value: Whether a currently HDFirearm (if any) is ready for use. - + /// Indicates whether the currently held HDFirearm's is ready for use, and has + /// ammo etc. + /// @return Whether a currently HDFirearm (if any) is ready for use. bool FirearmIsReady() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FirearmIsEmpty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is out of ammo. - // Arguments: None. - // Return value: Whether a currently HDFirearm (if any) is out of ammo. - + /// Indicates whether the currently held HDFirearm's is out of ammo. + /// @return Whether a currently HDFirearm (if any) is out of ammo. bool FirearmIsEmpty() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FirearmNeedsReload - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is almost out of ammo. - // Arguments: None. - // Return value: Whether a currently HDFirearm (if any) has less than half of ammo left. - + /// Indicates whether the currently held HDFirearm's is almost out of ammo. + /// @return Whether a currently HDFirearm (if any) has less than half of ammo left. bool FirearmNeedsReload() const; - /// /// Gets whether or not all of this ACrab's Turret's HDFirearms are full. - /// - /// Whether or not all of this ACrab's Turret's HDFirearms are full. Will return true if there is no Turret or no HDFirearms. + /// @return Whether or not all of this ACrab's Turret's HDFirearms are full. Will return true if there is no Turret or no HDFirearms. bool FirearmsAreFull() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FirearmIsSemiAuto - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is semi or full auto. - // Arguments: None. - // Return value: Whether a currently HDFirearm (if any) is a semi auto device. - + /// Indicates whether the currently held HDFirearm's is semi or full auto. + /// @return Whether a currently HDFirearm (if any) is a semi auto device. bool FirearmIsSemiAuto() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FirearmActivationDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the currently held device's delay between pulling the trigger - // and activating. - // Arguments: None. - // Return value: Delay in ms or zero if not a HDFirearm. - + /// Returns the currently held device's delay between pulling the trigger + /// and activating. + /// @return Delay in ms or zero if not a HDFirearm. int FirearmActivationDelay() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReloadFirearms - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reloads the currently held firearms, if any. - // Arguments: None. - // Return value: None. - + /// Reloads the currently held firearms, if any. void ReloadFirearms(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsWithinRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a point on the scene is within close range of the currently - // used device and aiming status, if applicable. - // Arguments: A Vector witht he aboslute coordinates of a point to check. - // Return value: Whether the point is within close range of this. - + /// Tells whether a point on the scene is within close range of the currently + /// used device and aiming status, if applicable. + /// @param point A Vector witht he aboslute coordinates of a point to check. + /// @return Whether the point is within close range of this. bool IsWithinRange(Vector& point) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Look - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an unseen-revealing ray in the direction of where this is facing. - // Arguments: The degree angle to deviate from the current view point in the ray - // casting. A random ray will be chosen out of this +-range. - // The range, in pixels, beyond the actors sharp aim that the ray will have. - // Return value: Whether any unseen pixels were revealed by this look. - + /// Casts an unseen-revealing ray in the direction of where this is facing. + /// @param FOVSpread The degree angle to deviate from the current view point in the ray + /// casting. A random ray will be chosen out of this +-range. + /// @param range The range, in pixels, beyond the actors sharp aim that the ray will have. + /// @return Whether any unseen pixels were revealed by this look. bool Look(float FOVSpread, float range) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LookForMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an MO detecting ray in the direction of where the head is looking - // at the time. Factors including head rotation, sharp aim mode, and - // other variables determine how this ray is cast. - // Arguments: The degree angle to deviate from the current view point in the ray - // casting. A random ray will be chosen out of this +-range. - // A specific material ID to ignore (see through) - // Whether to ignore all terrain or not (true means 'x-ray vision'). - // Return value: A pointer to the MO seen while looking. - + /// Casts an MO detecting ray in the direction of where the head is looking + /// at the time. Factors including head rotation, sharp aim mode, and + /// other variables determine how this ray is cast. + /// @param FOVSpread The degree angle to deviate from the current view point in the ray (default: 45) + /// casting. A random ray will be chosen out of this +-range. + /// @param ignoreMaterial A specific material ID to ignore (see through) (default: 0) + /// @param ignoreAllTerrain Whether to ignore all terrain or not (true means 'x-ray vision'). (default: false) + /// @return A pointer to the MO seen while looking. MovableObject* LookForMOs(float FOVSpread = 45, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false); - /// /// Gets the GUI representation of this ACrab, only defaulting to its Turret or body if no GraphicalIcon has been defined. - /// - /// The graphical representation of this ACrab as a BITMAP. + /// @return The graphical representation of this ACrab as a BITMAP. BITMAP* GetGraphicalIcon() const override; - /// /// Gets whether this ACrab has just taken a stride this frame. - /// - /// Whether this ACrab has taken a stride this frame or not. + /// @return Whether this ACrab has taken a stride this frame or not. bool StrideFrame() const { return m_StrideFrame; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreControllerUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Update called prior to controller update. Ugly hack. Supposed to be done every frame. void PreControllerUpdate() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this ACrab's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this ACrab's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Actor's current graphical HUD overlay representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // Which player's screen this is being drawn to. May affect what HUD elements - // get drawn etc. - // Return value: None. - + /// Draws this Actor's current graphical HUD overlay representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param whichScreen Which player's screen this is being drawn to. May affect what HUD elements (default: 0) + /// get drawn etc. void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - /// /// Gets the LimbPath corresponding to the passed in Side, Layer and MovementState values. - /// - /// Whether to get the left or right side. - /// Whether to get foreground or background LimbPath. - /// Which movement state to get the LimbPath for. - /// The LimbPath corresponding to the passed in Layer and MovementState values. + /// @param side Whether to get the left or right side. + /// @param layer Whether to get foreground or background LimbPath. + /// @param movementState Which movement state to get the LimbPath for. + /// @return The LimbPath corresponding to the passed in Layer and MovementState values. LimbPath* GetLimbPath(Side side, Layer layer, MovementState movementState) { return &m_Paths[side][layer][movementState]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLimbPathSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get walking limb path speed for the specified preset. - // Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST - // Return value: Limb path speed for the specified preset in m/s. - + /// Get walking limb path speed for the specified preset. + /// @param speedPreset Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST + /// @return Limb path speed for the specified preset in m/s. float GetLimbPathSpeed(int speedPreset) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLimbPathSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Set walking limb path speed for the specified preset. - // Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST. New speed value in m/s. - // Return value: None. - + /// Set walking limb path speed for the specified preset. + /// @param speedPreset Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST. New speed value in m/s. void SetLimbPathSpeed(int speedPreset, float speed); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLimbPathPushForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the default force that a limb traveling walking LimbPath can push against - // stuff in the scene with. - // Arguments: None. - // Return value: The default set force maximum, in kg * m/s^2. - + /// Gets the default force that a limb traveling walking LimbPath can push against + /// stuff in the scene with. + /// @return The default set force maximum, in kg * m/s^2. float GetLimbPathPushForce() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLimbPathPushForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the default force that a limb traveling walking LimbPath can push against - // stuff in the scene with. - // Arguments: The default set force maximum, in kg * m/s^2. - // Return value: None - + /// Sets the default force that a limb traveling walking LimbPath can push against + /// stuff in the scene with. + /// @param force The default set force maximum, in kg * m/s^2. void SetLimbPathPushForce(float force); - /// /// Gets this ACrab's stride sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ACrab's stride sound. + /// @return The SoundContainer for this ACrab's stride sound. SoundContainer* GetStrideSound() const { return m_StrideSound; } - /// /// Sets this ACrab's stride sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ACrab's stride sound. + /// @param newSound The new SoundContainer for this ACrab's stride sound. void SetStrideSound(SoundContainer* newSound) { m_StrideSound = newSound; } - /// /// Gets the upper limit of this ACrab's aim range. - /// - /// The upper limit of this ACrab's aim range. + /// @return The upper limit of this ACrab's aim range. float GetAimRangeUpperLimit() const { return m_AimRangeUpperLimit; } - /// /// Sets the upper limit of this ACrab's aim range. - /// - /// The new upper limit of this ACrab's aim range. + /// @param aimRangeUpperLimit The new upper limit of this ACrab's aim range. void SetAimRangeUpperLimit(float aimRangeUpperLimit) { m_AimRangeUpperLimit = aimRangeUpperLimit; } - /// /// Gets the lower limit of this ACrab's aim range. - /// - /// The lower limit of this ACrab's aim range. + /// @return The lower limit of this ACrab's aim range. float GetAimRangeLowerLimit() const { return m_AimRangeLowerLimit; } - /// /// Sets the lower limit of this ACrab's aim range. - /// - /// The new lower limit of this ACrab's aim range. + /// @param aimRangeLowerLimit The new lower limit of this ACrab's aim range. void SetAimRangeLowerLimit(float aimRangeLowerLimit) { m_AimRangeLowerLimit = aimRangeLowerLimit; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Function that is called when we get a new movepath. /// This processes and cleans up the movepath. - /// void OnNewMovePath() override; // Member variables @@ -592,26 +399,16 @@ namespace RTE { bool m_LockMouseAimInput; #pragma region Event Handling - /// /// Event listener to be run while this ACrab's PieMenu is opened. - /// - /// The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param pieMenu The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int WhilePieMenuOpenListener(const PieMenu* pieMenu) override; #pragma endregion - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ACrab, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this ACrab, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/ACraft.cpp b/Source/Entities/ACraft.cpp index 4d7754271e..d3127568e3 100644 --- a/Source/Entities/ACraft.cpp +++ b/Source/Entities/ACraft.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ACraft.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the ACraft class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "ACraft.h" #include "AtomGroup.h" @@ -37,12 +25,6 @@ namespace RTE { #define EXITLINESPACING 7 #define EXITSUCKDELAYMS 1500 - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Exit, effectively - // resetting the members of this abstraction level only. - void ACraft::Exit::Clear() { m_Offset.Reset(); m_Velocity.Reset(); @@ -53,11 +35,6 @@ namespace RTE { m_pIncomingMO = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Exit to be identical to another, by deep copy. - int ACraft::Exit::Create(const Exit& reference) { m_Offset = reference.m_Offset; m_Velocity = reference.m_Velocity; @@ -69,11 +46,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Exit object ready for use. - int ACraft::Exit::Create() { if (Serializable::Create() < 0) return -1; @@ -81,14 +53,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int ACraft::Exit::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -101,12 +65,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this Exit with a Writer for - // later recreation with Create(Reader &reader); - int ACraft::Exit::Save(Writer& writer) const { Serializable::Save(writer); @@ -124,12 +82,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CheckIfClear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates whether this exit is currently clear enough of terrain to - // safely put things through without them ending up in the terrain. - bool ACraft::Exit::CheckIfClear(const Vector& pos, Matrix& rot, float size) { Vector notUsed; Vector ray = m_Velocity; @@ -137,14 +89,6 @@ namespace RTE { return m_Clear = !g_SceneMan.CastNotMaterialRay(pos + (m_Offset * rot), ray * rot, g_MaterialAir, notUsed); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SuckInMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Uses cast MO rays to see if anyhting is able to be drawn into this - // exit. If so, it will alter the positiona nd velocity of the objet so - // it flies into the exit until it is sufficiently inside and then it'll - // return the MO here, OWNERHIP NOT TRANSFERRED! It is still in MovableMan! - MOSRotating* ACraft::Exit::SuckInMOs(ACraft* pExitOwner) { if (!pExitOwner || !m_Clear) return 0; @@ -224,12 +168,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ACraft, effectively - // resetting the members of this abstraction level only. - void ACraft::Clear() { m_AIMode = AIMODE_DELIVER; @@ -261,11 +199,6 @@ namespace RTE { m_ScuttleOnDeath = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ACraft object ready for use. - int ACraft::Create() { // Read all the properties if (Actor::Create() < 0) @@ -280,11 +213,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ACraft to be identical to another, by deep copy. - int ACraft::Create(const ACraft& reference) { Actor::Create(reference); @@ -323,14 +251,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int ACraft::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Actor::ReadProperty(propName, reader)); @@ -363,12 +283,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this ACraft with a Writer for - // later recreation with Create(Reader &reader); - int ACraft::Save(Writer& writer) const { Actor::Save(writer); @@ -402,11 +316,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the ACraft object. - void ACraft::Destroy(bool notInherited) { delete m_HatchOpenSound; delete m_HatchCloseSound; @@ -417,12 +326,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total liquidation value of this Actor and all its carried - // gold and inventory. - float ACraft::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const { float totalValue = Actor::GetTotalValue(nativeModule, foreignMult, nativeMult); @@ -436,12 +339,6 @@ namespace RTE { return totalValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this carries a specifically named object in its inventory. - // Also looks through the inventories of potential passengers, as applicable. - bool ACraft::HasObject(std::string objectName) const { if (Actor::HasObject(objectName)) return true; @@ -454,13 +351,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObjectInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically grouped object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - bool ACraft::HasObjectInGroup(std::string groupName) const { if (Actor::HasObjectInGroup(groupName)) return true; @@ -473,11 +363,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this belongs to, and all its inventory too. - void ACraft::SetTeam(int team) { Actor::SetTeam(team); @@ -490,8 +375,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ACraft::HandlePieCommand(PieSlice::SliceType pieSliceIndex) { if (pieSliceIndex != PieSlice::SliceType::NoType) { if (pieSliceIndex == PieSlice::SliceType::Deliver) { @@ -524,15 +407,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OpenHatch - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Opens the hatch doors, if they're closed. - // Arguments: None. - // Return value: None. - void ACraft::OpenHatch() { if (m_HatchState == CLOSED || m_HatchState == CLOSING) { m_HatchState = OPENING; @@ -545,13 +419,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CloseHatch - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Closes the hatch doors, if they're open. - // Arguments: None. - // Return value: None. - void ACraft::CloseHatch() { if (m_HatchState == OPEN || m_HatchState == OPENING) { m_HatchState = CLOSING; @@ -572,13 +439,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: AddInventoryItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an inventory item to this Actor. - // Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! - // Return value: None.. - void ACraft::AddInventoryItem(MovableObject* pItemToAdd) { if (pItemToAdd) { // If the hatch is open, then only add the new item to the intermediate new inventory list @@ -604,14 +464,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DropAllInventory - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Opens the hatches and makes everything in the Rocket fly out, including - // the passenger Actors, one after another. It may not happen - // instantaneously, so check for ejection being complete with - // IsInventoryEmpty(). - void ACraft::DropAllInventory() { if (m_HatchState == OPEN && !m_Exits.empty()) { // Cancel if we're not due to release @@ -730,8 +582,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float ACraft::GetCollectedInventoryMass() const { float inventoryMass = 0.0F; for (const MovableObject* inventoryItem: m_CollectedInventory) { @@ -740,8 +590,6 @@ namespace RTE { return inventoryMass; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACraft::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { if (g_SettingsMan.CrabBombsEnabled() && !s_CrabBombInEffect) { s_CrabBombInEffect = true; @@ -764,19 +612,12 @@ namespace RTE { Actor::GibThis(impactImpulse, movableObjectToIgnore); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACraft::ResetAllTimers() { MOSRotating::ResetAllTimers(); m_FlippedTimer.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this ACraft. Supposed to be done every frame. - void ACraft::Update() { Actor::Update(); @@ -898,12 +739,6 @@ namespace RTE { m_DeepCheck = true /*m_Status == DEAD*/; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Actor's current graphical HUD overlay representation to a - // BITMAP of choice. - void ACraft::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { m_HUDStack = -m_CharHeight / 2; diff --git a/Source/Entities/ACraft.h b/Source/Entities/ACraft.h index 28398613b7..dd954c2619 100644 --- a/Source/Entities/ACraft.h +++ b/Source/Entities/ACraft.h @@ -1,18 +1,11 @@ #ifndef _RTEACRAFT_ #define _RTEACRAFT_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ACraft.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the ACraft class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the ACraft class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Actor.h" #include "LimbPath.h" @@ -20,19 +13,11 @@ struct BITMAP; namespace RTE { - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: ACraft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A flying Actor which carries other things and can drop them. - // Parent(s): Actor. - // Class history: 12/13/2006 ACraft created. - + /// A flying Actor which carries other things and can drop them. class ACraft : public Actor { friend struct EntityLuaBindings; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableOverrideMethods; ClassInfoGetters; @@ -51,140 +36,77 @@ namespace RTE { LEFT }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Nested class: Exit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Something to bundle the properties of ACraft exits together. - // Parent(s): Serializable. - // Class history: 12/19/2006 Exit created. - + /// Something to bundle the properties of ACraft exits together. class Exit : public Serializable { friend class ACraft; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableClassNameGetter; SerializableOverrideMethods; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Exit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Exit object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a Exit object in system + /// memory. Create() should be called before using the object. Exit() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Exit object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the Exit object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Exit to be identical to another, by deep copy. - // Arguments: A reference to the Exit to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a Exit to be identical to another, by deep copy. + /// @param reference A reference to the Exit to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const Exit& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Serializable, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire Serializable, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the position offset of this exit from the position of its ACraft. - // Arguments: None. - // Return value: The coordinates relative to the m_Pos of this' ACraft. - + /// Gets the position offset of this exit from the position of its ACraft. + /// @return The coordinates relative to the m_Pos of this' ACraft. Vector GetOffset() const { return m_Offset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the velocity of anything that exits through this. - // Arguments: None. - // Return value: The velocity vector for anything exiting through this. - + /// Gets the velocity of anything that exits through this. + /// @return The velocity vector for anything exiting through this. Vector GetVelocity() const { return m_Velocity * (1.0F + m_VelSpread * RandomNormalNum()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRadius - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the width from the center tanget created by the velocity vector - // out from the offet point. This times two gives the total width of the - // opening. - // Arguments: None. - // Return value: Half the total width of the opening. - + /// Gets the width from the center tanget created by the velocity vector + /// out from the offet point. This times two gives the total width of the + /// opening. + /// @return Half the total width of the opening. float GetRadius() const { return m_Radius; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the distance this exit can suck in objects from. - // Arguments: None. - // Return value: The sucking range of this. - + /// Gets the distance this exit can suck in objects from. + /// @return The sucking range of this. float GetRange() const { return m_Range; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CheckIfClear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates whether this exit is currently clear enough of terrain to - // safely put things through without them ending up in the terrain. - // Arguments: The position of the parent ACraft of this. - // The rotation of the parent ACraft of this. - // How large (radius) the item is that is supposed to fit. - // Return value: If this has been determined clear to put anything through. - + /// Calculates whether this exit is currently clear enough of terrain to + /// safely put things through without them ending up in the terrain. + /// @param pos The position of the parent ACraft of this. + /// @param rot The rotation of the parent ACraft of this. + /// @param size How large (radius) the item is that is supposed to fit. (default: 20) + /// @return If this has been determined clear to put anything through. bool CheckIfClear(const Vector& pos, Matrix& rot, float size = 20); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsClear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells if this is clear of the terrain to put things through. Faster than - // CheckIfClear(). - // Arguments: None. - // Return value: If this has been determined clear to put anything through. - + /// Tells if this is clear of the terrain to put things through. Faster than + /// CheckIfClear(). + /// @return If this has been determined clear to put anything through. bool IsClear() const { return m_Clear; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SuckInMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Uses cast MO rays to see if anyhting is able to be drawn into this - // exit. If so, it will alter the positiona nd velocity of the objet so - // it flies into the exit until it is sufficiently inside and then it'll - // return the MO here, OWNERHIP NOT TRANSFERRED! It is still in MovableMan! - // Arguments: A pointer to the ACraft owner of this Exit. OWNERSHIP IS NOT TRANSFERRED! - // Return value: If an MO has been fully drawn into the exit, it will be returned here, - // OWNERSHIP NOT TRANSFERRED! - + /// Uses cast MO rays to see if anyhting is able to be drawn into this + /// exit. If so, it will alter the positiona nd velocity of the objet so + /// it flies into the exit until it is sufficiently inside and then it'll + /// return the MO here, OWNERHIP NOT TRANSFERRED! It is still in MovableMan! + /// @param pExitOwner A pointer to the ACraft owner of this Exit. OWNERSHIP IS NOT TRANSFERRED! + /// @return If an MO has been fully drawn into the exit, it will be returned here, + /// OWNERSHIP NOT TRANSFERRED! MOSRotating* SuckInMOs(ACraft* pExitOwner); - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // The offset of this exit relative the position of its ACraft Vector m_Offset; @@ -201,356 +123,204 @@ namespace RTE { // Movable Object that is being drawn into this exit MOSRotating* m_pIncomingMO; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Exit, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Exit, effectively + /// resetting the members of this abstraction level only. void Clear(); }; // Concrete allocation and cloning definitions // EntityAllocation(ACraft) - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: ACraft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a ACraft object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a ACraft object in system + /// memory. Create() should be called before using the object. ACraft() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~ACraft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a ACraft object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a ACraft object before deletion + /// from system memory. ~ACraft() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ACraft object ready for use. - // Arguments: A Reader that the ACraft will create itself with. - // Whether there is a class name in the stream to check against to make - // sure the correct type is being read from the stream. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the ACraft object ready for use. + /// @param A Reader that the ACraft will create itself with. + /// Whether there is a class name in the stream to check against to make + /// sure the correct type is being read from the stream. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a ACraft to be identical to another, by deep copy. - // Arguments: A reference to the ACraft to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a ACraft to be identical to another, by deep copy. + /// @param reference A reference to the ACraft to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const ACraft& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire ACraft, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire ACraft, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Actor::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total liquidation value of this Actor and all its carried - // gold and inventory. - // Arguments: If this is supposed to be adjusted for a specific Tech's subjective - // value, then pass in the native DataModule ID of that tech. 0 means - // no Tech is specified and the base value is returned. - // How much to multiply the value if this happens to be a foreign Tech. - // Return value: The current value of this Actor and all his carried assets. - + /// Gets the total liquidation value of this Actor and all its carried + /// gold and inventory. + /// @param nativeModule If this is supposed to be adjusted for a specific Tech's subjective (default: 0) + /// value, then pass in the native DataModule ID of that tech. 0 means + /// no Tech is specified and the base value is returned. + /// @param foreignMult How much to multiply the value if this happens to be a foreign Tech. (default: 1.0) + /// @return The current value of this Actor and all his carried assets. float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically named object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - // Arguments: The Preset name of the object to look for. - // Return value: Whetehr the object was found carried by this. - + /// Shows whether this is or carries a specifically named object in its + /// inventory. Also looks through the inventories of potential passengers, + /// as applicable. + /// @param objectName The Preset name of the object to look for. + /// @return Whetehr the object was found carried by this. bool HasObject(std::string objectName) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObjectInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically grouped object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - // Arguments: The name of the group to look for. - // Return value: Whetehr the object in the group was found carried by this. - + /// Shows whether this is or carries a specifically grouped object in its + /// inventory. Also looks through the inventories of potential passengers, + /// as applicable. + /// @param groupName The name of the group to look for. + /// @return Whetehr the object in the group was found carried by this. bool HasObjectInGroup(std::string groupName) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetHatchState - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current state of the hatch. - // Arguments: None. - // Return value: An int encoding the hatch state. See the HatchState enum. - + /// Gets the current state of the hatch. + /// @return An int encoding the hatch state. See the HatchState enum. unsigned int GetHatchState() const { return m_HatchState; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this belongs to, and all its inventory too. - // Arguments: The assigned team number. - // Return value: None. - + /// Sets which team this belongs to, and all its inventory too. + /// @param team The assigned team number. void SetTeam(int team) override; - /// /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. - /// - /// The SliceType of the PieSlice being handled. - /// Whether or not the activated PieSlice SliceType was able to be handled. + /// @param pieSliceType The SliceType of the PieSlice being handled. + /// @return Whether or not the activated PieSlice SliceType was able to be handled. bool HandlePieCommand(PieSlice::SliceType pieSliceType) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AutoStabilizing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether this has the means and will try to right itself, or if - // that's up to the Controller to do. - // Arguments: None. - // Return value: Wheter this will try to auto stabilize. - + /// Tells whether this has the means and will try to right itself, or if + /// that's up to the Controller to do. + /// @return Wheter this will try to auto stabilize. virtual bool AutoStabilizing() { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OpenHatch - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Opens the hatch doors, if they're closed or closing. - // Arguments: None. - // Return value: None. - + /// Opens the hatch doors, if they're closed or closing. void OpenHatch(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CloseHatch - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Closes the hatch doors, if they're open or opening. - // Arguments: None. - // Return value: None. - + /// Closes the hatch doors, if they're open or opening. void CloseHatch(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: AddInventoryItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an inventory item to this Actor. - // Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! - // Return value: None.. - + /// Adds an inventory item to this Actor. + /// @param pItemToAdd An pointer to the new item to add. Ownership IS TRANSFERRED! + /// @return None.. void AddInventoryItem(MovableObject* pItemToAdd) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DropAllInventory - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Opens the hatches and makes everything in the Rocket fly out, including - // the passenger Actors, one after another. It may not happen - // instantaneously, so check for ejection being complete with - // IsInventoryEmpty(). - // Arguments: None. - // Return value: None. - + /// Opens the hatches and makes everything in the Rocket fly out, including + /// the passenger Actors, one after another. It may not happen + /// instantaneously, so check for ejection being complete with + /// IsInventoryEmpty(). void DropAllInventory() override; - /// /// Gets the mass of this ACraft's inventory of newly collected items. - /// - /// The mass of this ACraft's newly collected inventory. + /// @return The mass of this ACraft's newly collected inventory. float GetCollectedInventoryMass() const; - /// /// Gets the mass of this ACraft, including the mass of its Attachables, wounds and inventory. - /// - /// The mass of this ACraft, its inventory and all its Attachables and wounds in Kilograms (kg). + /// @return The mass of this ACraft, its inventory and all its Attachables and wounds in Kilograms (kg). float GetMass() const override { return Actor::GetMass() + GetCollectedInventoryMass(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasDelivered - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicated whether this has landed and delivered yet on its current run. - // Arguments: None. - // Return value: Whether this has delivered yet. - + /// Indicated whether this has landed and delivered yet on its current run. + /// @return Whether this has delivered yet. bool HasDelivered() { return m_HasDelivered; } - /// /// Resets all the timers related to this, including the scuttle timer. - /// void ResetAllTimers() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Actor's current graphical HUD overlay representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // Which player's screen this is being drawn to. May affect what HUD elements - // get drawn etc. - // Return value: None. - + /// Draws this Actor's current graphical HUD overlay representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param whichScreen Which player's screen this is being drawn to. May affect what HUD elements (default: 0) + /// get drawn etc. void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMaxPassengers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The recomended, not absolute, maximum number of actors that fit in the - // invetory. Used by the activity AI. - // Arguments: None. - // Return value: An integer with the recomended number of actors that fit in the craft. - // Default is -1 (unknown). - + /// Virtual method: GetMaxPassengers + /// The recomended, not absolute, maximum number of actors that fit in the + /// invetory. Used by the activity AI. + /// @return An integer with the recomended number of actors that fit in the craft. + /// Default is -1 (unknown). virtual int GetMaxPassengers() const { return m_MaxPassengers; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetMaxPassengers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the recomended, not absolute, maximum number of actors that fit in the - // invetory. Used by the activity AI. - // Arguments: An integer with the recomended number of actors that fit in the craft. - // Default is -1 (unknown). - // Return value: None. - + /// Virtual method: SetMaxPassengers + /// Sets the recomended, not absolute, maximum number of actors that fit in the + /// invetory. Used by the activity AI. + /// @param max An integer with the recomended number of actors that fit in the craft. + /// Default is -1 (unknown). virtual void SetMaxPassengers(int max) { m_MaxPassengers = max; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDeliveryDelayMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns delivery delay multiplier. - // Arguments: None. - // Return value: Delivery delay multiplier. - + /// Description: Returns delivery delay multiplier. + /// @return Delivery delay multiplier. float GetDeliveryDelayMultiplier() const { return m_DeliveryDelayMultiplier; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDeliveryDelayMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets delivery delay multiplier. - // Arguments: Delivery delay multiplier. - // Return value: None. - + /// Description: Sets delivery delay multiplier. + /// @param newValue Delivery delay multiplier. void SetDeliveryDelayMultiplier(float newValue) { m_DeliveryDelayMultiplier = newValue; } - /// /// Gets whether this ACraft will scuttle automatically on death. - /// - /// Whether this ACraft will scuttle automatically on death. + /// @return Whether this ACraft will scuttle automatically on death. bool GetScuttleOnDeath() const { return m_ScuttleOnDeath; } - /// /// Sets whether this ACraft will scuttle automatically on death. - /// - /// Whether this ACraft will scuttle automatically on death. + /// @param scuttleOnDeath Whether this ACraft will scuttle automatically on death. void SetScuttleOnDeath(bool scuttleOnDeath) { m_ScuttleOnDeath = scuttleOnDeath; } - /// /// Gets the hatch opening/closing delay of this ACraft. - /// - /// The hatch delay of this ACraft. + /// @return The hatch delay of this ACraft. int GetHatchDelay() const { return m_HatchDelay; } - /// /// Sets the hatch opening/closing delay of this ACraft. - /// - /// The new hatch delay of this ACraft. + /// @param newDelay The new hatch delay of this ACraft. void SetHatchDelay(int newDelay) { m_HatchDelay = newDelay; } - /// /// Destroys this ACraft and creates its specified Gibs in its place with appropriate velocities. Any Attachables are removed and also given appropriate velocities. - /// - /// The impulse (kg * m/s) of the impact causing the gibbing to happen. - /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + /// @param impactImpulse The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// @param movableObjectToIgnore A pointer to an MO which the Gibs and Attachables should not be colliding with. void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr) override; - /// /// Gets this ACraft's hatch opening sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ACraft's hatch opening sound. + /// @return The SoundContainer for this ACraft's hatch opening sound. SoundContainer* GetHatchOpenSound() const { return m_HatchOpenSound; } - /// /// Sets this ACraft's hatch opening sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ACraft's hatch opening sound. + /// @param newSound The new SoundContainer for this ACraft's hatch opening sound. void SetHatchOpenSound(SoundContainer* newSound) { m_HatchOpenSound = newSound; } - /// /// Gets this ACraft's hatch closing sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ACraft's hatch closing sound. + /// @return The SoundContainer for this ACraft's hatch closing sound. SoundContainer* GetHatchCloseSound() const { return m_HatchCloseSound; } - /// /// Sets this ACraft's hatch closing sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ACraft's hatch closing sound. + /// @param newSound The new SoundContainer for this ACraft's hatch closing sound. void SetHatchCloseSound(SoundContainer* newSound) { m_HatchCloseSound = newSound; } - /// /// Gets this ACraft's crash sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ACraft's crash sound. + /// @return The SoundContainer for this ACraft's crash sound. SoundContainer* GetCrashSound() const { return m_CrashSound; } - /// /// Sets this ACraft's crash sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ACraft's crash sound. + /// @param newSound The new SoundContainer for this ACraft's crash sound. void SetCrashSound(SoundContainer* newSound) { m_CrashSound = newSound; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -621,18 +391,10 @@ namespace RTE { // Mutliplier to apply to default delivery time float m_DeliveryDelayMultiplier; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ACraft, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this ACraft, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/ADSensor.cpp b/Source/Entities/ADSensor.cpp index f20266864b..06904a1e9c 100644 --- a/Source/Entities/ADSensor.cpp +++ b/Source/Entities/ADSensor.cpp @@ -5,16 +5,12 @@ namespace RTE { const std::string ADSensor::c_ClassName = "Sensor"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADSensor::Clear() { m_StartOffset.Reset(); m_SensorRay.Reset(); m_Skip = 3; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADSensor::Create(const ADSensor& reference) { m_StartOffset = reference.m_StartOffset; m_SensorRay = reference.m_SensorRay; @@ -23,8 +19,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADSensor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -35,8 +29,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADSensor::Save(Writer& writer) const { Serializable::Save(writer); @@ -50,8 +42,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Actor* ADSensor::SenseActor(const Vector& doorPos, const Matrix& doorRot, bool doorHFlipped, MOID ignoreMOID) { Actor* sensedActor = 0; MOID foundMOID = g_SceneMan.CastMORay(doorPos + m_StartOffset.GetXFlipped(doorHFlipped) * doorRot, m_SensorRay.GetXFlipped(doorHFlipped) * doorRot, ignoreMOID, Activity::NoTeam, 0, true, m_Skip); diff --git a/Source/Entities/ADSensor.h b/Source/Entities/ADSensor.h index 579592081d..16717f13c9 100644 --- a/Source/Entities/ADSensor.h +++ b/Source/Entities/ADSensor.h @@ -8,9 +8,7 @@ namespace RTE { class Actor; - /// /// The ray-casting sensor which triggers the door opening or closing, depending on the team of the Actor that broke the ray. - /// class ADSensor : public Serializable { public: @@ -18,73 +16,53 @@ namespace RTE { SerializableOverrideMethods; #pragma region Creation - /// /// Constructor method used to instantiate an ADSensor object in system memory. Create() should be called before using the object. - /// ADSensor() { Clear(); } - /// /// Makes the ADSensor object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override { return Serializable::Create(); } - /// /// Creates an ADSensor to be identical to another, by deep copy. - /// - /// A reference to the ADSensor to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the ADSensor to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const ADSensor& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up an ADSensor object before deletion from system memory. - /// ~ADSensor() { Destroy(); } - /// /// Destroys and resets (through Clear()) the ADSensor object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy() { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Gets the starting position offset of this ADSensor from the owning ADoor position. - /// - /// The starting coordinates relative to the m_Pos of this' ADoor. + /// @return The starting coordinates relative to the m_Pos of this' ADoor. Vector GetStartOffset() const { return m_StartOffset; } - /// /// Sets the starting position offset of this ADSensor from the owning ADoor position. - /// - /// The new starting coordinates relative to the m_Pos of this' ADoor. + /// @param startOffsetValue The new starting coordinates relative to the m_Pos of this' ADoor. void SetStartOffset(const Vector& startOffsetValue) { m_StartOffset = startOffsetValue; } - /// /// Gets the sensor ray vector out from the start offset's position. - /// - /// The sensor ray vector. + /// @return The sensor ray vector. Vector GetSensorRay() const { return m_SensorRay; } - /// /// Sets the sensor ray vector out from the start offset's position. - /// - /// The new sensor ray vector. + /// @param sensorRayValue The new sensor ray vector. void SetSensorRay(const Vector& sensorRayValue) { m_SensorRay = sensorRayValue; } #pragma endregion #pragma region Concrete Methods - /// /// Casts the ray along the sensor vector and returns any Actor that was found along it. - /// - /// Position of this ADSensor's ADoor. - /// Rotation of this ADSensor's ADoor. - /// Flipping of this ADSensor's ADoor. - /// Which MOID to ignore, if any. - /// The root Actor of the first MOID hit by the sensor ray. 0 if none. + /// @param doorPos Position of this ADSensor's ADoor. + /// @param doorRot Rotation of this ADSensor's ADoor. + /// @param doorHFlipped Flipping of this ADSensor's ADoor. + /// @param ignoreMOID Which MOID to ignore, if any. + /// @return The root Actor of the first MOID hit by the sensor ray. 0 if none. Actor* SenseActor(const Vector& doorPos, const Matrix& doorRot, bool doorHFlipped = false, MOID ignoreMOID = g_NoMOID); #pragma endregion @@ -97,9 +75,7 @@ namespace RTE { private: static const std::string c_ClassName; //!< A string with the friendly formatted type name of this object. - /// /// Clears all the member variables of this ADSensor, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/Entities/ADoor.cpp b/Source/Entities/ADoor.cpp index beb9d20b72..e2feda0c24 100644 --- a/Source/Entities/ADoor.cpp +++ b/Source/Entities/ADoor.cpp @@ -12,8 +12,6 @@ namespace RTE { ConcreteClassInfo(ADoor, Actor, 20); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::Clear() { m_InitialSpriteAnimDuration = 0; m_Sensors.clear(); @@ -53,8 +51,6 @@ namespace RTE { m_CanBeSquished = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADoor::Create(const ADoor& reference) { if (reference.m_Door) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_Door->GetUniqueID()); @@ -101,8 +97,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADoor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Actor::ReadProperty(propName, reader)); @@ -154,8 +148,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADoor::Save(Writer& writer) const { Actor::Save(writer); @@ -197,8 +189,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::Destroy(bool notInherited) { if (m_DoorMoveStartSound) { m_DoorMoveStartSound->Stop(); @@ -225,8 +215,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::SetDoor(Attachable* newDoor) { if (m_DoorMaterialDrawn) { RTEAssert(m_Door, "Door material drawn without an m_Door! This should've been cleared when the door was!"); @@ -250,8 +238,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::DrawDoorMaterial(bool disallowErasingMaterialBeforeDrawing, bool updateMaterialArea) { if (!m_Door || m_DoorMaterialTempErased || !g_SceneMan.GetTerrain() || !g_SceneMan.GetTerrain()->GetMaterialBitmap()) { return; @@ -270,8 +256,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ADoor::EraseDoorMaterial(bool updateMaterialArea) { if (!g_SceneMan.GetTerrain() || !g_SceneMan.GetTerrain()->GetMaterialBitmap()) { return false; @@ -295,8 +279,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::TempEraseOrRedrawDoorMaterial(bool erase) { if (!g_SceneMan.GetTerrain() || !g_SceneMan.GetTerrain()->GetMaterialBitmap()) { return; @@ -314,8 +296,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { if (m_Door && m_Door->IsAttached()) { EraseDoorMaterial(); @@ -325,8 +305,6 @@ namespace RTE { Actor::GibThis(impactImpulse, movableObjectToIgnore); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::CorrectAttachableAndWoundPositionsAndRotations() const { if (m_Door) { m_Door->SetParentOffset(m_ClosedByDefault ? m_ClosedOffset : m_OpenOffset); @@ -335,8 +313,6 @@ namespace RTE { MOSRotating::CorrectAttachableAndWoundPositionsAndRotations(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::OpenDoor() { if (m_DoorState == STOPPED) { SharedDoorControls(); @@ -350,8 +326,6 @@ namespace RTE { m_ResetToDefaultStateTimer.Reset(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::CloseDoor() { if (m_DoorState == STOPPED) { SharedDoorControls(); @@ -365,8 +339,6 @@ namespace RTE { m_ResetToDefaultStateTimer.Reset(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::StopDoor() { if (m_DoorState == OPENING || m_DoorState == CLOSING) { if (m_DrawMaterialLayerWhenOpen || m_DrawMaterialLayerWhenClosed) { @@ -384,8 +356,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::SharedDoorControls() { if (m_DoorState == OPEN || m_DoorState == CLOSED) { if (m_DoorMoveStartSound) { @@ -423,8 +393,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::Update() { ZoneScoped; @@ -476,8 +444,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::UpdateSensors() { const Actor* foundActor = nullptr; bool anySensorInput = false; @@ -509,8 +475,6 @@ namespace RTE { m_SensorTimer.Reset(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::UpdateDoorAttachableActions() { Vector startOffset; Vector endOffset; @@ -579,8 +543,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::DrawHUD(BITMAP* targetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { m_HUDStack = -static_cast(m_CharHeight) / 2; diff --git a/Source/Entities/ADoor.h b/Source/Entities/ADoor.h index 3fc5fac83e..eba5b63cd6 100644 --- a/Source/Entities/ADoor.h +++ b/Source/Entities/ADoor.h @@ -8,9 +8,7 @@ namespace RTE { class Attachable; - /// /// A sliding or swinging door. - /// class ADoor : public Actor { public: @@ -28,40 +26,28 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a ADoor object in system memory. Create() should be called before using the object. - /// ADoor() { Clear(); } - /// /// Makes the ADoor object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override { return Actor::Create(); } - /// /// Creates a ADoor to be identical to another, by deep copy. - /// - /// A reference to the ADoor to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the ADoor to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const ADoor& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a ADoor object before deletion from system memory. - /// ~ADoor() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the ADoor object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Resets the entire ADoor, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Actor::Reset(); @@ -69,145 +55,99 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the moving door Attachable of this ADoor - /// - /// A pointer to the door Attachable of this. Ownership is NOT transferred! + /// @return A pointer to the door Attachable of this. Ownership is NOT transferred! Attachable* GetDoor() const { return m_Door; } - /// /// Sets the moving door Attachable for this ADoor. - /// - /// The new moving door attachable to use. + /// @param newDoor The new moving door attachable to use. void SetDoor(Attachable* newDoor); - /// /// Gets the current state of the door. - /// - /// The current state of this ADoor. See the DoorState enum. + /// @return The current state of this ADoor. See the DoorState enum. DoorState GetDoorState() const { return m_DoorState; } - /// /// Sets whether this ADoor closes (or opens) after a while by default. - /// - /// Whether the door by default goes to a closed position. If not, then it will open after a while. + /// @param closedByDefault Whether the door by default goes to a closed position. If not, then it will open after a while. void SetClosedByDefault(bool closedByDefault) { m_ClosedByDefault = closedByDefault; } - /// /// Tells whether the player can switch control to this at all. - /// - /// Whether a player can control this at all. + /// @return Whether a player can control this at all. bool IsControllable() const override { return false; } - /// /// Gets whether or not this ADoor's door material has been drawn. - /// - /// Whether or not this ADoor's door material has been drawn. + /// @return Whether or not this ADoor's door material has been drawn. bool GetDoorMaterialDrawn() const { return m_DoorMaterialDrawn; } - /// /// Gets this ADoor's door move start sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ADoor's door move start sound. + /// @return The SoundContainer for this ADoor's door move start sound. SoundContainer* GetDoorMoveStartSound() const { return m_DoorMoveStartSound.get(); } - /// /// Sets this ADoor's door move start sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ADoor's door move start sound. + /// @param newSound The new SoundContainer for this ADoor's door move start sound. void SetDoorMoveStartSound(SoundContainer* newSound) { m_DoorMoveStartSound.reset(newSound); } - /// /// Gets this ADoor's door move sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ADoor's door move sound. + /// @return The SoundContainer for this ADoor's door move sound. SoundContainer* GetDoorMoveSound() const { return m_DoorMoveSound.get(); } - /// /// Sets this ADoor's door move sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ADoor's door move sound. + /// @param newSound The new SoundContainer for this ADoor's door move sound. void SetDoorMoveSound(SoundContainer* newSound) { m_DoorMoveSound.reset(newSound); } - /// /// Gets this ADoor's door direction change sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ADoor's door direction change sound. + /// @return The SoundContainer for this ADoor's door direction change sound. SoundContainer* GetDoorDirectionChangeSound() const { return m_DoorDirectionChangeSound.get(); } - /// /// Sets this ADoor's door direction change sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ADoor's door direction change sound. + /// @param newSound The new SoundContainer for this ADoor's door direction change sound. void SetDoorDirectionChangeSound(SoundContainer* newSound) { m_DoorDirectionChangeSound.reset(newSound); } - /// /// Gets this ADoor's door move end sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this ADoor's door move end sound. + /// @return The SoundContainer for this ADoor's door move end sound. SoundContainer* GetDoorMoveEndSound() const { return m_DoorMoveEndSound.get(); } - /// /// Sets this ADoor's door move end sound. Ownership IS transferred! - /// - /// The new SoundContainer for this ADoor's door move end sound. + /// @param newSound The new SoundContainer for this ADoor's door move end sound. void SetDoorMoveEndSound(SoundContainer* newSound) { m_DoorMoveEndSound.reset(newSound); } #pragma endregion #pragma region Concrete Methods - /// /// Opens the door if it's closed. - /// void OpenDoor(); - /// /// Closes the door if it's open. - /// void CloseDoor(); - /// /// Force the door to stop at the exact position it is. - /// void StopDoor(); - /// /// Used to temporarily remove or add back the material drawing of this in the Scene. Used for making pathfinding work through doors. - /// - /// Whether to erase door material (true) or draw it (false). + /// @param erase Whether to erase door material (true) or draw it (false). void TempEraseOrRedrawDoorMaterial(bool erase); - /// /// Resets the sensor Timer for this ADoor, effectively making it ignore Actors. - /// void ResetSensorTimer() { m_SensorTimer.Reset(); } #pragma endregion #pragma region Virtual Override Methods - /// /// Destroys this ADoor and creates its specified Gibs in its place with appropriate velocities. /// Any Attachables are removed and also given appropriate velocities. - /// - /// The impulse (kg * m/s) of the impact causing the gibbing to happen. - /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + /// @param impactImpulse The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// @param movableObjectToIgnore A pointer to an MO which the Gibs and Attachables should not be colliding with. void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr) override; - /// /// Ensures all attachables and wounds are positioned and rotated correctly. Must be run when this ADoor is added to MovableMan to avoid issues with Attachables spawning in at (0, 0). - /// void CorrectAttachableAndWoundPositionsAndRotations() const override; - /// /// Updates this ADoor. Supposed to be done every frame. - /// void Update() override; - /// /// Draws this ADoor's current graphical HUD overlay representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// Which player's screen this is being drawn to. May affect what HUD elements get drawn etc. - /// Whether or not this MovableObject is currently player controlled (not applicable for ADoor) + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param whichScreen Which player's screen this is being drawn to. May affect what HUD elements get drawn etc. + /// @param playerControlled Whether or not this MovableObject is currently player controlled (not applicable for ADoor) void DrawHUD(BITMAP* targetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; #pragma endregion @@ -259,40 +199,28 @@ namespace RTE { private: #pragma region Update Breakdown - /// /// Iterates through the sensor list looking for actors and acts accordingly. Resets to the default state if none are found and past the delay timer. This is called from Update(). - /// void UpdateSensors(); - /// /// Updates the door attachable position and movement based on the current state of this ADoor. This is called from Update(). - /// void UpdateDoorAttachableActions(); #pragma endregion - /// /// Shared method for the door opening/closing sequence. This is called from OpenDoor() and CloseDoor(). - /// void SharedDoorControls(); - /// /// Draws the material under the position of the door attachable, to create terrain collision detection for the doors. - /// - /// Whether to disallow calling EraseDoorMaterial before drawing. Defaults to false, which means normal behaviour applies and this may erase the material before drawing it. - /// Whether to tell the Scene's Terrain that this door has modified the material layer. + /// @param disallowErasingMaterialBeforeDrawing Whether to disallow calling EraseDoorMaterial before drawing. Defaults to false, which means normal behaviour applies and this may erase the material before drawing it. + /// @param updateMaterialArea Whether to tell the Scene's Terrain that this door has modified the material layer. void DrawDoorMaterial(bool disallowErasingMaterialBeforeDrawing = false, bool updateMaterialArea = true); - /// /// Flood-fills the material area under the last position of the door attachable that matches the material index of it. /// This is to get rid of the material footprint made with DrawDoorMaterial when the door part starts to move. - /// - /// Whether to tell the Scene's Terrain that this door has modified the material layer.. - /// Whether the fill erasure was successful (if the same material as the door was found and erased). + /// @param updateMaterialArea Whether to tell the Scene's Terrain that this door has modified the material layer.. + /// @return Whether the fill erasure was successful (if the same material as the door was found and erased). bool EraseDoorMaterial(bool updateMaterialArea = true); - /// /// Clears all the member variables of this ADoor, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/AEJetpack.cpp b/Source/Entities/AEJetpack.cpp index d66fcf09c9..21a050c898 100644 --- a/Source/Entities/AEJetpack.cpp +++ b/Source/Entities/AEJetpack.cpp @@ -7,8 +7,6 @@ namespace RTE { ConcreteClassInfo(AEJetpack, AEmitter, 20); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AEJetpack::Clear() { m_JetpackType = JetpackType::Standard; m_JetTimeTotal = 0.0F; @@ -21,8 +19,6 @@ namespace RTE { m_AdjustsThrottleForWeight = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AEJetpack::Create() { if (Attachable::Create() < 0) { return -1; @@ -35,8 +31,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AEJetpack::Create(const AEJetpack& reference) { AEmitter::Create(reference); @@ -52,8 +46,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AEJetpack::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return AEmitter::ReadProperty(propName, reader)); @@ -85,8 +77,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AEJetpack::Save(Writer& writer) const { AEmitter::Save(writer); @@ -111,8 +101,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AEJetpack::UpdateBurstState(Actor& parentActor) { const Controller& controller = *parentActor.GetController(); @@ -189,8 +177,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AEJetpack::Burst(Actor& parentActor, float fuelUseMultiplier) { parentActor.SetMovementState(Actor::JUMP); @@ -206,8 +192,6 @@ namespace RTE { m_JetTimeLeft -= fuelUsage; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AEJetpack::Thrust(Actor& parentActor, float fuelUseMultiplier) { parentActor.SetMovementState(Actor::JUMP); @@ -218,8 +202,6 @@ namespace RTE { m_JetTimeLeft -= fuelUsage; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AEJetpack::Recharge(Actor& parentActor) { EnableEmission(false); if (parentActor.GetMovementState() == Actor::JUMP) { diff --git a/Source/Entities/AEJetpack.h b/Source/Entities/AEJetpack.h index 305b4191f8..49fc25ed51 100644 --- a/Source/Entities/AEJetpack.h +++ b/Source/Entities/AEJetpack.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// A jetpack MO, which can be used to generate thrust - /// class AEJetpack : public AEmitter { friend struct EntityLuaBindings; @@ -23,157 +21,107 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a AEJetpack object in system memory. Create() should be called before using the object. - /// AEJetpack() { Clear(); } - /// /// Makes the AEJetpack object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a AEJetpack to be identical to another, by deep copy. - /// - /// A reference to the AEJetpack to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the AEJetpack to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const AEJetpack& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a AEJetpack object before deletion from system memory. - /// ~AEJetpack() override { Destroy(true); } - /// /// Resets the entire AEJetpack, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); AEmitter::Reset(); } #pragma endregion - /// /// Updates this AEJetpack from our parent actor. - /// void UpdateBurstState(Actor& parentActor); - /// /// Returns whether or not this jetpack is fully fueled. - /// - /// Whether or not this jetpack is fully fueled. + /// @return Whether or not this jetpack is fully fueled. bool IsFullyFueled() const { return m_JetTimeLeft >= m_JetTimeTotal - (m_JetTimeTotal * std::numeric_limits::epsilon()); } - /// /// Returns whether or not this jetpack is out of fuel. - /// - /// Whether or not this jetpack is out of fuel. + /// @return Whether or not this jetpack is out of fuel. bool IsOutOfFuel() const { return m_JetTimeLeft <= std::numeric_limits::epsilon(); } - /// /// Gets the amount of time this jetpack can fire when filled, in ms. - /// - /// The amount of time this jetpack can fire when it's at max. + /// @return The amount of time this jetpack can fire when it's at max. float GetJetTimeTotal() const { return m_JetTimeTotal; } - /// /// Sets the amount of time this' jetpack can fire when filled, in ms. - /// - /// The amount of time this jetpack can fire when it's at max. + /// @param newValue The amount of time this jetpack can fire when it's at max. void SetJetTimeTotal(float newValue) { m_JetTimeTotal = newValue; } - /// /// Gets the amount of time this jetpack can still fire until out, in ms. - /// - /// The amount of time this jetpack can still fire before running out. + /// @return The amount of time this jetpack can still fire before running out. float GetJetTimeLeft() const { return m_JetTimeLeft; } - /// /// Sets the amount of time this' jetpack can still fire until out, in ms. - /// - /// The amount of time this' jetpack can still fire before running out. + /// @param newValue The amount of time this' jetpack can still fire before running out. void SetJetTimeLeft(float newValue) { m_JetTimeLeft = newValue < m_JetTimeTotal ? newValue : m_JetTimeTotal; } - /// /// Gets the ratio of jetpack time that is left. - /// - /// The ratio of jetpack time that is left. + /// @return The ratio of jetpack time that is left. float GetJetTimeRatio() { return m_JetTimeLeft / m_JetTimeTotal; } - /// /// Gets the rate at which this AHuman's jetpack is replenished during downtime. - /// - /// The rate at which the jetpack is replenished. + /// @return The rate at which the jetpack is replenished. float GetJetReplenishRate() const { return m_JetReplenishRate; } - /// /// Sets the rate at which this AHuman's jetpack is replenished during downtime. - /// - /// The rate at which the jetpack is replenished. + /// @param newValue The rate at which the jetpack is replenished. void SetJetReplenishRate(float newValue) { m_JetReplenishRate = newValue; } - /// /// Gets the rate at which this AHuman's jetpack is replenished during downtime. - /// - /// The rate at which the jetpack is replenished. + /// @return The rate at which the jetpack is replenished. float GetMinimumFuelRatio() const { return m_MinimumFuelRatio; } - /// /// Sets the rate at which this AHuman's jetpack is replenished during downtime. - /// - /// The rate at which the jetpack is replenished. + /// @param newValue The rate at which the jetpack is replenished. void SetMinimumFuelRatio(float newValue) { m_MinimumFuelRatio = newValue; } - /// /// Gets the scalar ratio at which this jetpack's thrust angle follows the aim angle of the user. - /// - /// The ratio at which this jetpack follows the aim angle of the user. + /// @return The ratio at which this jetpack follows the aim angle of the user. float GetJetAngleRange() const { return m_JetAngleRange; } - /// /// Sets the scalar ratio at which this jetpack's thrust angle follows the aim angle of the user. - /// - /// The ratio at which this jetpack follows the aim angle of the user. + /// @param newValue The ratio at which this jetpack follows the aim angle of the user. void SetJetAngleRange(float newValue) { m_JetAngleRange = newValue; } - /// /// Gets the type of this jetpack. - /// - /// The type of this jetpack. + /// @return The type of this jetpack. JetpackType GetJetpackType() const { return m_JetpackType; } - /// /// Sets the type of this jetpack. - /// - /// The new type of this jetpack. + /// @param newType The new type of this jetpack. void SetJetpackType(JetpackType newType) { m_JetpackType = newType; } - /// /// Returns whether the angle of this jetpack can adjust while firing, or if it can only be aimed while off. - /// - /// Whether the angle of this jetpack can adjust while firing. + /// @return Whether the angle of this jetpack can adjust while firing. bool GetCanAdjustAngleWhileFiring() const { return m_CanAdjustAngleWhileFiring; } - /// /// Sets whether the angle of this can adjust while firing, or if it can only be aimed while off. - /// - /// The new value for whether the angle of this jetpack can adjust while firing. + /// @param newValue The new value for whether the angle of this jetpack can adjust while firing. void SetCanAdjustAngleWhileFiring(bool newValue) { m_CanAdjustAngleWhileFiring = newValue; } - /// /// Returns whether this jetpack adjusts it's throttle to balance for extra weight. - /// - /// Whether this jetpack adjusts it's throttle to balance for extra weight. + /// @return Whether this jetpack adjusts it's throttle to balance for extra weight. bool GetAdjustsThrottleForWeight() const { return m_AdjustsThrottleForWeight; } - /// /// Sets whether this jetpack adjusts it's throttle to balance for extra weight. - /// - /// The new value for whether this jetpack adjusts it's throttle to balance for extra weight. + /// @param newValue The new value for whether this jetpack adjusts it's throttle to balance for extra weight. void SetAdjustsThrottleForWeight(bool newValue) { m_AdjustsThrottleForWeight = newValue; } protected: @@ -190,29 +138,21 @@ namespace RTE { bool m_AdjustsThrottleForWeight; //!< Whether or not the jetpack throttle auto-adjusts for weight, at the cost of fuel usage. private: - /// /// The logic to run when bursting. - /// - /// The parent actor using this jetpack. - /// The multiplier to fuel usage rate. + /// @param parentActor The parent actor using this jetpack. + /// @param fuelUseMultiplier The multiplier to fuel usage rate. void Burst(Actor& parentActor, float fuelUseMultiplier); - /// /// The logic to run when thrusting. - /// - /// The parent actor using this jetpack. - /// The multiplier to fuel usage rate. + /// @param parentActor The parent actor using this jetpack. + /// @param fuelUseMultiplier The multiplier to fuel usage rate. void Thrust(Actor& parentActor, float fuelUseMultiplier); - /// /// The logic to run when recharging. - /// - /// The parent actor using this jetpack. + /// @param parentActor The parent actor using this jetpack. void Recharge(Actor& parentActor); - /// /// Clears all the member variables of this AEJetpack, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/AEmitter.cpp b/Source/Entities/AEmitter.cpp index 3b50228d34..40f107c92e 100644 --- a/Source/Entities/AEmitter.cpp +++ b/Source/Entities/AEmitter.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AEmitter.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the AEmitter class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "AEmitter.h" #include "Atom.h" #include "Emission.h" @@ -19,12 +7,6 @@ namespace RTE { ConcreteClassInfo(AEmitter, Attachable, 100); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this AEmitter, effectively - // resetting the members of this abstraction level only. - void AEmitter::Clear() { m_EmissionList.clear(); m_EmissionSound = nullptr; @@ -60,11 +42,6 @@ namespace RTE { m_LoudnessOnEmit = 1.0F; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a AEmitter to be identical to another, by deep copy. - int AEmitter::Create(const AEmitter& reference) { if (reference.m_pFlash) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFlash->GetUniqueID()); @@ -112,14 +89,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int AEmitter::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Attachable::ReadProperty(propName, reader)); @@ -181,12 +150,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this AEmitter with a Writer for - // later recreation with Create(Reader &reader); - int AEmitter::Save(Writer& writer) const { Attachable::Save(writer); @@ -246,11 +209,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the AEmitter object. - void AEmitter::Destroy(bool notInherited) { // Stop playback of sounds gracefully if (m_EmissionSound) { @@ -271,23 +229,12 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetEmissionTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reset the timers of all emissions so they will start/stop at the - // correct relative offsets from now. - void AEmitter::ResetEmissionTimers() { m_LastEmitTmr.Reset(); for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) (*eItr).ResetEmissionTimers(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableEmission - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this AEmitter to start emitting at the set rate, or to stop. - void AEmitter::EnableEmission(bool enable) { if (!m_EmitEnabled && enable) { m_LastEmitTmr.Reset(); @@ -299,11 +246,6 @@ namespace RTE { m_EmitEnabled = enable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EstimateImpulse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the forces this emitter applies on any parent. - float AEmitter::EstimateImpulse(bool burst) { // Calculate the impulse generated by the emissions, once and store the result if ((!burst && m_AvgImpulse < 0) || (burst && m_AvgBurstImpulse < 0)) { @@ -345,8 +287,6 @@ namespace RTE { return m_AvgImpulse * throttleFactor; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float AEmitter::GetTotalParticlesPerMinute() const { float totalPPM = 0; for (const Emission& emission: m_EmissionList) { @@ -355,8 +295,6 @@ namespace RTE { return totalPPM; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AEmitter::GetTotalBurstSize() const { int totalBurstSize = 0; for (const Emission& emission: m_EmissionList) { @@ -365,15 +303,11 @@ namespace RTE { return totalBurstSize; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float AEmitter::GetScaledThrottle(float throttle, float multiplier) const { float throttleFactor = LERP(-1.0f, 1.0f, m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, throttle); return LERP(m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, -1.0f, 1.0f, throttleFactor * multiplier); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AEmitter::SetFlash(Attachable* newFlash) { if (m_pFlash && m_pFlash->IsAttached()) { RemoveAndDeleteAttachable(m_pFlash); @@ -398,13 +332,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this AEmitter. Supposed to be done every frame. - void AEmitter::Update() { Attachable::PreUpdate(); @@ -611,12 +538,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this AEmitter's current graphical representation to a - // BITMAP of choice. - void AEmitter::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, diff --git a/Source/Entities/AEmitter.h b/Source/Entities/AEmitter.h index 5459480983..780384147a 100644 --- a/Source/Entities/AEmitter.h +++ b/Source/Entities/AEmitter.h @@ -1,35 +1,20 @@ #ifndef _RTEAEMITTER_ #define _RTEAEMITTER_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AEmitter.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the AEmitter class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the AEmitter class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Attachable.h" #include "Emission.h" namespace RTE { - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: AEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: An attachable MO that creates and emits particle MO's. - // Parent(s): Attachable. - // Class history: 02/29/2004 AEmitter created. - + /// An attachable MO that creates and emits particle MO's. class AEmitter : public Attachable { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: friend struct EntityLuaBindings; @@ -38,172 +23,92 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: AEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a AEmitter object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a AEmitter object in system + /// memory. Create() should be called before using the object. AEmitter() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~AEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a AEmitter object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a AEmitter object before deletion + /// from system memory. ~AEmitter() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a AEmitter to be identical to another, by deep copy. - // Arguments: A reference to the AEmitter to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a AEmitter to be identical to another, by deep copy. + /// @param reference A reference to the AEmitter to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const AEmitter& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire AEmitter, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire AEmitter, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); MOSRotating::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEmitting - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this AEmitter is currently enabled and emitting. - // Arguments: None. - // Return value: Whether it's emitting or not. - + /// Indicates whether this AEmitter is currently enabled and emitting. + /// @return Whether it's emitting or not. bool IsEmitting() const { return m_EmitEnabled; } - /// /// Returns whether this emitter was emitting last frame. - /// - /// Whether this emitter was emitting last frame. + /// @return Whether this emitter was emitting last frame. bool WasEmitting() const { return m_WasEmitting; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetEmissionTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reset the timers of all emissions so they will start/stop at the - // correct relative offsets from now. - // Arguments: None. - // Return value: None. - + /// Reset the timers of all emissions so they will start/stop at the + /// correct relative offsets from now. void ResetEmissionTimers(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableEmission - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this AEmitter to start emitting at the set rate, or to stop. - // Arguments: Whether to enable or disable emission. - // Return value: None. - + /// Sets this AEmitter to start emitting at the set rate, or to stop. + /// @param enable Whether to enable or disable emission. (default: true) void EnableEmission(bool enable = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EstimateImpulse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the forces this emitter applies on any parent. - // Arguments: Whether to calculate a burst update or not. - // Return value: The approximate impulse generated by the emitter. - + /// Calculates the forces this emitter applies on any parent. + /// @param burst Whether to calculate a burst update or not. (default: false) + /// @return The approximate impulse generated by the emitter. float EstimateImpulse(bool burst = false); - /// /// Gets the rate at which all of the Emissions of this AEmitter, combined, emit their particles. - /// - /// The combined particles per minute of all Emissions in this AEmitter. + /// @return The combined particles per minute of all Emissions in this AEmitter. float GetTotalParticlesPerMinute() const; - /// /// Gets the number of particles that will be emitted by all the Emissions of this AEmitter combined, in one shot when a burst is triggered. - /// - /// The combined burst size of all Emissions in this AEmitter. + /// @return The combined burst size of all Emissions in this AEmitter. int GetTotalBurstSize() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the scale factor that will be applied to the regular spread and - // emission velocity to get the burst particle parameters. - // Arguments: None. - // Return value: The scale factor. - + /// Gets the scale factor that will be applied to the regular spread and + /// emission velocity to get the burst particle parameters. + /// @return The scale factor. float GetBurstScale() const { return m_BurstScale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the angle of direction that the emitted particles will be shot at. - // Arguments: None. - // Return value: A float with the angle in radians. - + /// Gets the angle of direction that the emitted particles will be shot at. + /// @return A float with the angle in radians. float GetEmitAngle() const { return m_EmitAngle.GetRadAngle(); } const Matrix& GetEmitAngleMatrix() const { return m_EmitAngle; } - /// /// Gets the offset of the emission point from this' sprite center, which gets rotated with this. - /// - /// The emission offset. + /// @return The emission offset. Vector GetEmitOffset() const { return m_EmissionOffset; } - /// /// Sets the offset of the emission point from this' sprite center, which gets rotated with this. - /// - /// The new emission offset. + /// @param newOffset The new emission offset. void SetEmitOffset(const Vector& newOffset) { m_EmissionOffset = newOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitVector - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A vector in the direction, including the rotation of the emitter, that - // the emitted particles will be shot at. - // Arguments: None. - // Return value: A unit vector. - + /// A vector in the direction, including the rotation of the emitter, that + /// the emitted particles will be shot at. + /// @return A unit vector. Vector GetEmitVector() const { return Vector(1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRecoilVector - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A vector in the opposite direction, including the rotation of the - // emitter, that the emitted particles will be shot at. - // Arguments: None. - // Return value: A unit vector. - + /// A vector in the opposite direction, including the rotation of the + /// emitter, that the emitted particles will be shot at. + /// @return A unit vector. Vector GetRecoilVector() const { return Vector(-1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstSpacing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the BurstSpacing for this emitter. - // Arguments: None. - // Return value: The BurstSpacing in ms. - + /// Gets the BurstSpacing for this emitter. + /// @return The BurstSpacing in ms. float GetBurstSpacing() const { return m_BurstSpacing; } /* @@ -242,64 +147,45 @@ namespace RTE { float GetEmitVelMax() const { return m_MaxVelocity; } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetThrottle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the normalized throttle scalar which controls how to affect the - // emission rate as per the emisison rate range. Depricated for Lua, use - // the Throttle property instead. - // Arguments: None. - // Return value: A float with the normalized throttle scalar. 1.0 means max throttle, - // 0 means normal, -1.0 means least emission rate. - + /// Gets the normalized throttle scalar which controls how to affect the + /// emission rate as per the emisison rate range. Depricated for Lua, use + /// the Throttle property instead. + /// @return A float with the normalized throttle scalar. 1.0 means max throttle, + /// 0 means normal, -1.0 means least emission rate. float GetThrottle() const { return m_Throttle; } - /// /// Gets the adjusted throttle multiplier that is factored into the emission rate of this AEmitter. - /// - /// The throttle strength as a multiplier. + /// @return The throttle strength as a multiplier. float GetThrottleFactor() const { return LERP(-1.0f, 1.0f, m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, m_Throttle); } - /// /// Gets the throttle value that will achieve a given throttle factor that is factored into the emission rate of this AEmitter. - /// - /// The throttle value that will achieve the given throttle factor. + /// @return The throttle value that will achieve the given throttle factor. float GetThrottleForThrottleFactor(float throttleFactor) const { return LERP(m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, -1.0f, 1.0f, throttleFactor); } - /// /// Returns a scaled throttle value that represents a linear increase of force. /// Because of (bad) reasons, throttle is in the range -1.0F to 1.0F, where -1.0F is "minimum force" and 1.0F is "maximum force". /// 0.0F is "whoever the fuck knows?" force. As such, multiplying throttle by 2 does not mean twice the force emitted, instead it means "whoever the fuck knows?" additional force emitted. /// All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. /// ...this helper function lets us apply a scale to throttle and get a sensible result. - /// - /// The throttle value to be considered. - /// The multiplier to scale by, in terms of absolute force emitted. - /// Adjusted throttle value scaled by the multiplier value. + /// @param throttle The throttle value to be considered. + /// @param multiplier The multiplier to scale by, in terms of absolute force emitted. + /// @return Adjusted throttle value scaled by the multiplier value. float GetScaledThrottle(float throttle, float multiplier) const; - /// /// Gets the negative throttle multiplier of this AEmitter. - /// - /// The negative throttle multiplier of this AEmitter. + /// @return The negative throttle multiplier of this AEmitter. float GetNegativeThrottleMultiplier() const { return m_NegativeThrottleMultiplier; } - /// /// Gets the positive throttle multiplier of this AEmitter. - /// - /// The positive throttle multiplier of this AEmitter. + /// @return The positive throttle multiplier of this AEmitter. float GetPositiveThrottleMultiplier() const { return m_PositiveThrottleMultiplier; } - /// /// Sets the negative throttle multiplier of this AEmitter. - /// - /// The new throttle multiplier of this AEmitter. + /// @param newValue The new throttle multiplier of this AEmitter. void SetNegativeThrottleMultiplier(float newValue) { m_NegativeThrottleMultiplier = newValue; } - /// /// Sets the positive throttle multiplier of this AEmitter. - /// - /// The new throttle multiplier of this AEmitter. + /// @param newValue The new throttle multiplier of this AEmitter. void SetPositiveThrottleMultiplier(float newValue) { m_PositiveThrottleMultiplier = newValue; } /* @@ -325,75 +211,41 @@ namespace RTE { void SetBurstCount(const int count) { m_BurstSize = count; } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the scale factor that will be applied to the regular spread and - // emission velocity to get the burst particle parameters. - // Arguments: The scale factor. - // Return value: None. - + /// Sets the scale factor that will be applied to the regular spread and + /// emission velocity to get the burst particle parameters. + /// @param scale The scale factor. void SetBurstScale(const float scale) { m_BurstScale = scale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstSpacing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the BurstSpacing for this emitter. - // Arguments: The BurstSpacing in ms. - // Return value: None. - + /// Sets the BurstSpacing for this emitter. + /// @param spacing The BurstSpacing in ms. void SetBurstSpacing(const float spacing) { m_BurstSpacing = spacing; } - /// /// Gets the flash of this AEmitter. - /// - /// A pointer to the AEmitter's flash. Ownership is NOT transferred! + /// @return A pointer to the AEmitter's flash. Ownership is NOT transferred! Attachable* GetFlash() const { return m_pFlash; } - /// /// Sets the flash for this AEmitter. Ownership IS transferred! - /// - /// The new flash to use. + /// @param newFlash The new flash to use. void SetFlash(Attachable* newFlash); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFlashScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the display scale factor of the flash effect. This is purely - // visual. - // Arguments: None. - // Return value: The scale factor of the flash draw. - + /// Gets the display scale factor of the flash effect. This is purely + /// visual. + /// @return The scale factor of the flash draw. float GetFlashScale() const { return m_FlashScale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFlashScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the display scale factor of the flash effect. This is purely - // visual. - // Arguments: The scale factor of the flash draw. - // Return value: None. - + /// Sets the display scale factor of the flash effect. This is purely + /// visual. + /// @param flashScale The scale factor of the flash draw. (default: 1.0f) void SetFlashScale(float flashScale = 1.0f) { m_FlashScale = flashScale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the angle of direction that the emitted particles will be shot at. - // Arguments: A float with the angle in radians. - // Return value: None. - + /// Sets the angle of direction that the emitted particles will be shot at. + /// @param m_EmitAngle.SetRadAngle(angle A float with the angle in radians. void SetEmitAngle(const float angle) { m_EmitAngle.SetRadAngle(angle); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetThrottle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the normalized throttle scalar which controls how to affect the - // emission rate as per the emisison rate range. - // Arguments: A float with the normalized throttle scalar. 1.0 means max throttle, - // 0 means normal, -1.0 means least emission rate. - // Return value: None. - + /// Sets the normalized throttle scalar which controls how to affect the + /// emission rate as per the emisison rate range. + /// @param m_Throttle A float with the normalized throttle scalar. 1.0 means max throttle, (default: throttle > 1.0f ? 1.0f : (throttle < -1.0f ? -1.0f : throttle) + /// 0 means normal, -1.0 means least emission rate. void SetThrottle(float throttle) { m_Throttle = throttle > 1.0f ? 1.0f : (throttle < -1.0f ? -1.0f : throttle); } /* @@ -432,219 +284,118 @@ namespace RTE { void SetEmitVelMax(const float maxVel) { m_MaxVelocity = maxVel; } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TriggerBurst - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Triggers a one-shot burst of emissions in the number that has - // previously been set. The burst will happen during the next Update of - // this AEmitter. - // Arguments: None. - // Return value: None. - + /// Triggers a one-shot burst of emissions in the number that has + /// previously been set. The burst will happen during the next Update of + /// this AEmitter. void TriggerBurst() { m_BurstTriggered = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CanTriggerBurst - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if it is possible to trigger a one-shot burst of emissions during - // the next Update of this AEmitter. - // Arguments: None. - // Return value: If it is possible to trigger a burst. - + /// Checks if it is possible to trigger a one-shot burst of emissions during + /// the next Update of this AEmitter. + /// @return If it is possible to trigger a burst. bool CanTriggerBurst() { return m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsSetToBurst - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this AEmitter is set to burst next update or not. - // Arguments: None. - // Return value: Whether a burst is gonna happen or not.. - + /// Indicates whether this AEmitter is set to burst next update or not. + /// @return Whether a burst is gonna happen or not.. bool IsSetToBurst() const { return m_BurstTriggered; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AlarmOnEmit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registers a new AlarmEvent if this emitter has a loudness above zero. - // Arguments: Team that will ignore this AlarmEvent. - // Return value: None. - + /// Registers a new AlarmEvent if this emitter has a loudness above zero. + /// @param Team Team that will ignore this AlarmEvent. void AlarmOnEmit(int Team) const { if (m_LoudnessOnEmit > 0) g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, Team, m_LoudnessOnEmit)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ResetAllTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resest all the timers used by this. Can be emitters, etc. This is to - // prevent backed up emissions to come out all at once while this has been - // held dormant in an inventory. - // Arguments: None. - // Return value: None. - + /// Resest all the timers used by this. Can be emitters, etc. This is to + /// prevent backed up emissions to come out all at once while this has been + /// held dormant in an inventory. void ResetAllTimers() override { Attachable::ResetAllTimers(); m_BurstTimer.Reset(); m_LastEmitTmr.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; void PostUpdate() override { Attachable::PostUpdate(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstDamage - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns burst damage of this emitter. - // Arguments: None. - // Return value: Burst damage of emitter. - + /// Returns burst damage of this emitter. + /// @return Burst damage of emitter. float GetBurstDamage() const { return m_BurstDamage * m_EmitterDamageMultiplier; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstDamage - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets burst damage of this emitter. - // Arguments: Burst damage of emitter. - // Return value: None. - + /// Sets burst damage of this emitter. + /// @param newValue Burst damage of emitter. void SetBurstDamage(float newValue) { m_BurstDamage = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitDamage - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns emit damage of this emitter. - // Arguments: None. - // Return value: Emit damage of emitter. - + /// Returns emit damage of this emitter. + /// @return Emit damage of emitter. float GetEmitDamage() const { return m_EmitDamage * m_EmitterDamageMultiplier; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitDamage - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets emit damage of this emitter. - // Arguments: Emit damage of emitter. - // Return value: None. - + /// Sets emit damage of this emitter. + /// @param newValue Emit damage of emitter. void SetEmitDamage(float newValue) { m_EmitDamage = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitterDamageMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns damage multiplier of this emitter. - // Arguments: None. - // Return value: Damage multiplier of emitter. - + /// Returns damage multiplier of this emitter. + /// @return Damage multiplier of emitter. float GetEmitterDamageMultiplier() const { return m_EmitterDamageMultiplier; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitterDamageMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets damage multiplier of this emitter. - // Arguments: New damage multiplier of emitter - // Return value: None. - + /// Sets damage multiplier of this emitter. + /// @param newValue New damage multiplier of emitter void SetEmitterDamageMultiplier(float newValue) { m_EmitterDamageMultiplier = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this AEmitter's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this AEmitter's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsDamaging - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this emitter deals damage. - // Arguments: None. - // Return value: Returns true if this emitter deals damage. - + /// Indicates whether this emitter deals damage. + /// @return Returns true if this emitter deals damage. bool IsDamaging() { return (m_EmitDamage > 0 || m_BurstDamage > 0) && m_EmitterDamageMultiplier > 0; } - /// /// Gets the number of emissions emitted since emission was last enabled. - /// - /// The number of emissions emitted since emission was last enabled. + /// @return The number of emissions emitted since emission was last enabled. long GetEmitCount() const { return m_EmitCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitCountLimit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of emissions left before emitter is disabled. - // Arguments: None. - // Return value: Returns the number of emissions left before emitter is disabled. - + /// Gets the number of emissions left before emitter is disabled. + /// @return Returns the number of emissions left before emitter is disabled. long GetEmitCountLimit() const { return m_EmitCountLimit; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitCountLimit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the number of emissions left before emitter is disabled. - // Arguments: New number of emissions left - // Return value: None. - + /// Sets the number of emissions left before emitter is disabled. + /// @param newValue New number of emissions left void SetEmitCountLimit(long newValue) { m_EmitCountLimit = newValue; } - /// /// Gets this AEmitter's emission sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this AEmitter's emission sound. + /// @return The SoundContainer for this AEmitter's emission sound. SoundContainer* GetEmissionSound() const { return m_EmissionSound; } - /// /// Sets this AEmitter's emission sound. Ownership IS transferred! - /// - /// The new SoundContainer for this AEmitter's emission sound. + /// @param newSound The new SoundContainer for this AEmitter's emission sound. void SetEmissionSound(SoundContainer* newSound) { m_EmissionSound = newSound; } - /// /// Gets this AEmitter's burst sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this AEmitter's burst sound. + /// @return The SoundContainer for this AEmitter's burst sound. SoundContainer* GetBurstSound() const { return m_BurstSound; } - /// /// Sets this AEmitter's burst sound. Ownership IS transferred! - /// - /// The new SoundContainer for this AEmitter's burst sound. + /// @param newSound The new SoundContainer for this AEmitter's burst sound. void SetBurstSound(SoundContainer* newSound) { m_BurstSound = newSound; } - /// /// Gets this AEmitter's end sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this AEmitter's end sound. + /// @return The SoundContainer for this AEmitter's end sound. SoundContainer* GetEndSound() const { return m_EndSound; } - /// /// Sets this AEmitter's end sound. Ownership IS transferred! - /// - /// The new SoundContainer for this AEmitter's end sound. + /// @param newSound The new SoundContainer for this AEmitter's end sound. void SetEndSound(SoundContainer* newSound) { m_EndSound = newSound; } - /// /// Returns whether this emitter just started emitting this frame. - /// - /// Whether this emitter just started emitting this frame. + /// @return Whether this emitter just started emitting this frame. bool JustStartedEmitting() const { return !m_WasEmitting && m_EmitEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -707,18 +458,10 @@ namespace RTE { // Whether the burst sound follows the emitter bool m_BurstSoundFollowsEmitter; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this AEmitter, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this AEmitter, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/AHuman.cpp b/Source/Entities/AHuman.cpp index 0eb2c67598..e5c66e7378 100644 --- a/Source/Entities/AHuman.cpp +++ b/Source/Entities/AHuman.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AHuman.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the AHuman class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "AHuman.h" #include "AtomGroup.h" @@ -37,12 +25,6 @@ namespace RTE { ConcreteClassInfo(AHuman, Actor, 20); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this AHuman, effectively - // resetting the members of this abstraction level only. - void AHuman::Clear() { m_pHead = 0; m_LookToAimRatio = 0.7F; @@ -108,11 +90,6 @@ namespace RTE { m_JumpTimer.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the AHuman object ready for use. - int AHuman::Create() { if (Actor::Create() < 0) { return -1; @@ -149,11 +126,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a AHuman to be identical to another, by deep copy. - int AHuman::Create(const AHuman& reference) { if (reference.m_pBGArm) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pBGArm->GetUniqueID()); @@ -261,14 +233,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int AHuman::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Actor::ReadProperty(propName, reader)); @@ -334,12 +298,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this AHuman with a Writer for - // later recreation with Create(Reader &reader); - int AHuman::Save(Writer& writer) const { Actor::Save(writer); @@ -401,11 +359,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the AHuman object. - void AHuman::Destroy(bool notInherited) { delete m_pFGHandGroup; delete m_pBGHandGroup; @@ -420,12 +373,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total liquidation value of this Actor and all its carried - // gold and inventory. - float AHuman::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const { float totalValue = Actor::GetTotalValue(nativeModule, foreignMult, nativeMult); @@ -436,12 +383,6 @@ namespace RTE { return totalValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this carries a specifically named object in its inventory. - // Also looks through the inventories of potential passengers, as applicable. - bool AHuman::HasObject(std::string objectName) const { bool found = Actor::HasObject(objectName); @@ -454,13 +395,6 @@ namespace RTE { return found; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObjectInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically grouped object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - bool AHuman::HasObjectInGroup(std::string groupName) const { bool found = Actor::HasObjectInGroup(groupName); @@ -473,11 +407,6 @@ namespace RTE { return found; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetCPUPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of this' brain, or equivalent. - Vector AHuman::GetCPUPos() const { if (m_pHead && m_pHead->IsAttached()) return m_Pos + ((m_pHead->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation) * 1.5); @@ -485,12 +414,6 @@ namespace RTE { return m_Pos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetEyePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of this' eye, or equivalent, where look - // vector starts from. - Vector AHuman::GetEyePos() const { if (m_pHead && m_pHead->IsAttached()) { return m_Pos + m_pHead->GetParentOffset() * 1.2F; @@ -499,8 +422,6 @@ namespace RTE { return m_Pos; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::SetHead(Attachable* newHead) { if (m_pHead && m_pHead->IsAttached()) { RemoveAndDeleteAttachable(m_pHead); @@ -525,8 +446,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::SetJetpack(AEJetpack* newJetpack) { if (m_pJetpack && m_pJetpack->IsAttached()) { RemoveAndDeleteAttachable(m_pJetpack); @@ -550,8 +469,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::SetFGArm(Arm* newArm) { if (m_pFGArm && m_pFGArm->IsAttached()) { RemoveAndDeleteAttachable(m_pFGArm); @@ -576,8 +493,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::SetBGArm(Arm* newArm) { if (m_pBGArm && m_pBGArm->IsAttached()) { RemoveAndDeleteAttachable(m_pBGArm); @@ -601,8 +516,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::SetFGLeg(Leg* newLeg) { if (m_pFGLeg && m_pFGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pFGLeg); @@ -625,8 +538,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::SetBGLeg(Leg* newLeg) { if (m_pBGLeg && m_pBGLeg->IsAttached()) { RemoveAndDeleteAttachable(m_pBGLeg); @@ -650,21 +561,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* AHuman::GetGraphicalIcon() const { return m_GraphicalIcon ? m_GraphicalIcon : (m_pHead ? m_pHead->GetSpriteFrame(0) : GetSpriteFrame(0)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - bool AHuman::CollideAtPoint(HitData& hd) { return Actor::CollideAtPoint(hd); @@ -729,8 +629,6 @@ namespace RTE { } */ - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AHuman::HandlePieCommand(PieSlice::SliceType pieSliceIndex) { if (pieSliceIndex != PieSlice::SliceType::NoType) { if (pieSliceIndex == PieSlice::SliceType::Pickup) { @@ -763,14 +661,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: AddInventoryItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an inventory item to this AHuman. This also puts that item - // directly in the hands of this if they are empty. - void AHuman::AddInventoryItem(MovableObject* pItemToAdd) { // If we have nothing in inventory, and nothing in our hands, just grab this first thing added to us. if (HeldDevice* itemToAddAsHeldDevice = dynamic_cast(pItemToAdd); itemToAddAsHeldDevice && m_Inventory.empty() && m_pFGArm && m_pFGArm->IsAttached() && !m_pFGArm->GetHeldDevice()) { @@ -783,8 +673,6 @@ namespace RTE { EquipShieldInBGArm(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MovableObject* AHuman::SwapNextInventory(MovableObject* inventoryItemToSwapIn, bool muteSound) { MovableObject* swappedInventoryItem = Actor::SwapNextInventory(inventoryItemToSwapIn, muteSound); while (!dynamic_cast(swappedInventoryItem) && !m_Inventory.empty()) { @@ -795,8 +683,6 @@ namespace RTE { return swappedInventoryItem; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MovableObject* AHuman::SwapPrevInventory(MovableObject* inventoryItemToSwapIn) { MovableObject* swappedInventoryItem = Actor::SwapPrevInventory(inventoryItemToSwapIn); while (!dynamic_cast(swappedInventoryItem) && !m_Inventory.empty()) { @@ -807,15 +693,6 @@ namespace RTE { return swappedInventoryItem; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipFirearm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first found firearm - // in the inventory. If the held device already is a firearm, or no - // firearm is in inventory, nothing happens. - bool AHuman::EquipFirearm(bool doEquip) { if (!(m_pFGArm && m_pFGArm->IsAttached())) { return false; @@ -864,13 +741,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipDeviceInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first found device - // of the specified group in the inventory. If the held device already - // is of that group, or no device is in inventory, nothing happens. - bool AHuman::EquipDeviceInGroup(std::string group, bool doEquip) { if (!(m_pFGArm && m_pFGArm->IsAttached())) { return false; @@ -929,13 +799,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipLoadedFirearmInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first loaded HDFirearm - // of the specified group in the inventory. If no such weapon is in the - // inventory, nothing happens. - bool AHuman::EquipLoadedFirearmInGroup(std::string group, std::string excludeGroup, bool doEquip) { if (!(m_pFGArm && m_pFGArm->IsAttached())) { return false; @@ -982,13 +845,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipNamedDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first found device - // of with the specified preset name in the inventory. If the held device already - // is of that preset name, or no device is in inventory, nothing happens. - bool AHuman::EquipNamedDevice(const std::string& moduleName, const std::string& presetName, bool doEquip) { if (!(m_pFGArm && m_pFGArm->IsAttached())) { return false; @@ -1036,13 +892,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipThrowable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first found ThrownDevice - // in the inventory. If the held device already is a ThrownDevice, or no - // ThrownDevice is in inventory, nothing happens. - bool AHuman::EquipThrowable(bool doEquip) { if (!(m_pFGArm && m_pFGArm->IsAttached())) { return false; @@ -1091,8 +940,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - bool AHuman::EquipDiggingTool(bool doEquip) { if (!(m_pFGArm && m_pFGArm->IsAttached())) { return false; @@ -1124,8 +971,6 @@ namespace RTE { return strongestDigger != nullptr; } - ////////////////////////////////////////////////////////////////////////////////////////// - float AHuman::EstimateDigStrength() const { float maxPenetration = Actor::EstimateDigStrength(); @@ -1146,15 +991,6 @@ namespace RTE { return maxPenetration; } - ////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipShield - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first found shield - // in the inventory. If the held device already is a shield, or no - // shield is in inventory, nothing happens. - bool AHuman::EquipShield() { if (!(m_pFGArm && m_pFGArm->IsAttached())) { return false; @@ -1199,13 +1035,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipShieldInBGArm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tries to equip the first shield in inventory to the background arm; - // this only works if nothing is held at all, or the FG arm holds a - // one-handed device, or we're in inventory mode. - bool AHuman::EquipShieldInBGArm() { if (!(m_pBGArm && m_pBGArm->IsAttached())) { return false; @@ -1257,8 +1086,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - bool AHuman::UnequipFGArm() { if (m_pFGArm) { if (HeldDevice* heldDevice = m_pFGArm->GetHeldDevice()) { @@ -1271,8 +1098,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - bool AHuman::UnequipBGArm() { if (m_pBGArm) { if (HeldDevice* heldDevice = m_pBGArm->GetHeldDevice()) { @@ -1285,8 +1110,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - float AHuman::GetEquippedMass() const { float equippedMass = 0; if (MovableObject* fgDevice = GetEquippedItem()) { @@ -1298,12 +1121,6 @@ namespace RTE { return equippedMass; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmIsReady - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held device's current mag is empty on - // ammo or not. - bool AHuman::FirearmIsReady() const { // Check if the currently held device is already the desired type if (m_pFGArm && m_pFGArm->IsAttached()) { @@ -1315,11 +1132,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: ThrowableIsReady - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held ThrownDevice's is ready to go. - bool AHuman::ThrowableIsReady() const { // Check if the currently held thrown device is already the desired type if (m_pFGArm && m_pFGArm->IsAttached()) { @@ -1331,11 +1143,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmIsEmpty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is out of ammo. - bool AHuman::FirearmIsEmpty() const { if (m_pFGArm && m_pFGArm->IsAttached()) { const HDFirearm* pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); @@ -1346,11 +1153,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmNeedsReload - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is almost out of ammo. - bool AHuman::FirearmNeedsReload() const { if (const HDFirearm* fgWeapon = dynamic_cast(GetEquippedItem()); fgWeapon && fgWeapon->NeedsReloading()) { return true; @@ -1361,8 +1163,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AHuman::FirearmsAreReloading(bool onlyIfAllFirearmsAreReloading) const { int reloadingFirearmCount = 0; int totalFirearmCount = 0; @@ -1387,13 +1187,6 @@ namespace RTE { return onlyIfAllFirearmsAreReloading ? reloadingFirearmCount == totalFirearmCount : reloadingFirearmCount > 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmIsSemiAuto - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is semi or full auto. - bool AHuman::FirearmIsSemiAuto() const { if (m_pFGArm && m_pFGArm->IsAttached()) { const HDFirearm* pWeapon = dynamic_cast(m_pFGArm->GetHeldDevice()); @@ -1402,13 +1195,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: ReloadFirearm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reloads the currently held firearm, if any. - // Arguments: None. - // Return value: None. - void AHuman::ReloadFirearms(bool onlyReloadEmptyFirearms) { for (Arm* arm: {m_pFGArm, m_pBGArm}) { if (arm) { @@ -1463,12 +1249,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmActivationDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the currently held device's delay between pulling the trigger - // and activating. - int AHuman::FirearmActivationDelay() const { // Check if the currently held device is already the desired type if (m_pFGArm && m_pFGArm->IsAttached()) { @@ -1480,12 +1260,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsWithinRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a point on the scene is within range of the currently - // used device and aiming status, if applicable. - bool AHuman::IsWithinRange(Vector& point) const { if (m_SharpAimMaxedOut) return true; @@ -1515,13 +1289,6 @@ namespace RTE { return sqrDistance <= (range * range); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Look - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an unseen-revealing ray in the direction of where this is facing. - // Arguments: The degree angle to deviate from the current view point in the ray - // casting. A random ray will be chosen out of this +-range. - bool AHuman::Look(float FOVSpread, float range) { if (!g_SceneMan.AnythingUnseen(m_Team) || m_CanRevealUnseen == false) return false; @@ -1556,11 +1323,6 @@ namespace RTE { return g_SceneMan.CastSeeRay(m_Team, aimPos, lookVector, ignored, 25, (int)g_SceneMan.GetUnseenResolution(m_Team).GetSmallest() / 2); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: LookForGold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts a material detecting ray in the direction of where this is facing. - bool AHuman::LookForGold(float FOVSpread, float range, Vector& foundLocation) const { Vector ray(m_HFlipped ? -range : range, 0); ray.DegRotate(FOVSpread * RandomNormalNum()); @@ -1568,13 +1330,6 @@ namespace RTE { return g_SceneMan.CastMaterialRay(m_Pos, ray, g_MaterialGold, foundLocation, 4); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: LookForMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an MO detecting ray in the direction of where the head is looking - // at the time. Factors including head rotation, sharp aim mode, and - // other variables determine how this ray is cast. - MovableObject* AHuman::LookForMOs(float FOVSpread, unsigned char ignoreMaterial, bool ignoreAllTerrain) { MovableObject* pSeenMO = 0; Vector aimPos = m_Pos; @@ -1607,13 +1362,6 @@ namespace RTE { return pSeenMO; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ResetAllTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resest all the timers used by this. Can be emitters, etc. This is to - // prevent backed up emissions to come out all at once while this has been - // held dormant in an inventory. - void AHuman::ResetAllTimers() { Actor::ResetAllTimers(); @@ -1622,8 +1370,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::OnNewMovePath() { Actor::OnNewMovePath(); @@ -1656,8 +1402,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::UpdateWalkAngle(AHuman::Layer whichLayer) { if (m_Controller.IsState(BODY_JUMP)) { m_WalkAngle[whichLayer] = Matrix(c_QuarterPI * GetFlipFactor()); @@ -1689,8 +1433,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::UpdateCrouching() { if (!m_Controller.IsState(BODY_JUMP) && m_pHead) { float desiredWalkPathYOffset = 0.0F; @@ -1737,8 +1479,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::PreControllerUpdate() { ZoneScoped; @@ -2611,8 +2351,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::Update() { ZoneScoped; @@ -2789,8 +2527,6 @@ namespace RTE { // m_DeepCheck = true/*m_Status == DEAD*/; } - ////////////////////////////////////////////////////////////////////////////////////////// - void AHuman::DrawThrowingReticle(BITMAP* targetBitmap, const Vector& targetPos, float progressScalar) const { const int pointCount = 9; Vector points[pointCount]; @@ -2816,12 +2552,6 @@ namespace RTE { release_bitmap(targetBitmap); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this AHuman's current graphical representation to a - // BITMAP of choice. - void AHuman::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { Actor::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); @@ -2856,12 +2586,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Actor's current graphical HUD overlay representation to a - // BITMAP of choice. - void AHuman::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { m_HUDStack = -m_CharHeight / 2; @@ -3136,8 +2860,6 @@ namespace RTE { m_Paths[BGROUND][WALK].OverridePushForce(force); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AHuman::WhilePieMenuOpenListener(const PieMenu* pieMenu) { int result = Actor::WhilePieMenuOpenListener(pieMenu); diff --git a/Source/Entities/AHuman.h b/Source/Entities/AHuman.h index 9af2ebf22a..c4b3cf1d9f 100644 --- a/Source/Entities/AHuman.h +++ b/Source/Entities/AHuman.h @@ -1,18 +1,11 @@ #ifndef _RTEAHUMAN_ #define _RTEAHUMAN_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AHuman.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the AHuman class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the AHuman class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Actor.h" #include "Arm.h" #include "Leg.h" @@ -24,13 +17,7 @@ namespace RTE { class AEJetpack; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: AHuman - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A humanoid actor. - // Parent(s): Actor. - // Class history: 05/24/2001 AHuman created. - + /// A humanoid actor. class AHuman : public Actor { friend struct EntityLuaBindings; @@ -57,9 +44,7 @@ namespace RTE { BGROUND }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(AHuman); @@ -68,219 +53,135 @@ namespace RTE { ClassInfoGetters; DefaultPieMenuNameGetter("Default Human Pie Menu"); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: AHuman - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a AHuman object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a AHuman object in system + /// memory. Create() should be called before using the object. AHuman() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~AHuman - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a AHuman object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a AHuman object before deletion + /// from system memory. ~AHuman() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the AHuman object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the AHuman object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a AHuman to be identical to another, by deep copy. - // Arguments: A reference to the AHuman to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a AHuman to be identical to another, by deep copy. + /// @param reference A reference to the AHuman to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const AHuman& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire AHuman, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire AHuman, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Actor::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total liquidation value of this Actor and all its carried - // gold and inventory. - // Arguments: If this is supposed to be adjusted for a specific Tech's subjective - // value, then pass in the native DataModule ID of that tech. 0 means - // no Tech is specified and the base value is returned. - // How much to multiply the value if this happens to be a foreign Tech. - // Return value: The current value of this Actor and all his carried assets. - + /// Gets the total liquidation value of this Actor and all its carried + /// gold and inventory. + /// @param nativeModule If this is supposed to be adjusted for a specific Tech's subjective (default: 0) + /// value, then pass in the native DataModule ID of that tech. 0 means + /// no Tech is specified and the base value is returned. + /// @param foreignMult How much to multiply the value if this happens to be a foreign Tech. (default: 1.0) + /// @return The current value of this Actor and all his carried assets. float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically named object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - // Arguments: The Preset name of the object to look for. - // Return value: Whetehr the object was found carried by this. - + /// Shows whether this is or carries a specifically named object in its + /// inventory. Also looks through the inventories of potential passengers, + /// as applicable. + /// @param objectName The Preset name of the object to look for. + /// @return Whetehr the object was found carried by this. bool HasObject(std::string objectName) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObjectInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically grouped object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - // Arguments: The name of the group to look for. - // Return value: Whetehr the object in the group was found carried by this. - + /// Shows whether this is or carries a specifically grouped object in its + /// inventory. Also looks through the inventories of potential passengers, + /// as applicable. + /// @param groupName The name of the group to look for. + /// @return Whetehr the object in the group was found carried by this. bool HasObjectInGroup(std::string groupName) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetCPUPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of this' brain, or equivalent. - // Arguments: None. - // Return value: A Vector with the absolute position of this' brain. - + /// Gets the absoltue position of this' brain, or equivalent. + /// @return A Vector with the absolute position of this' brain. Vector GetCPUPos() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetEyePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of this' eye, or equivalent, where look - // vector starts from. - // Arguments: None. - // Return value: A Vector with the absolute position of this' eye or view point. - + /// Gets the absoltue position of this' eye, or equivalent, where look + /// vector starts from. + /// @return A Vector with the absolute position of this' eye or view point. Vector GetEyePos() const override; - /// /// Gets the head of this AHuman. - /// - /// A pointer to the head of this AHuman. Ownership is NOT transferred. + /// @return A pointer to the head of this AHuman. Ownership is NOT transferred. Attachable* GetHead() const { return m_pHead; } - /// /// Sets the head for this AHuman. - /// - /// The new head to use. + /// @param newHead The new head to use. void SetHead(Attachable* newHead); - /// /// Gets the jetpack of this AHuman. - /// - /// A pointer to the jetpack of this AHuman. Ownership is NOT transferred. + /// @return A pointer to the jetpack of this AHuman. Ownership is NOT transferred. AEJetpack* GetJetpack() const { return m_pJetpack; } - /// /// Sets the jetpack for this AHuman. - /// - /// The new jetpack to use. + /// @param newJetpack The new jetpack to use. void SetJetpack(AEJetpack* newJetpack); - /// /// Gets the foreground Arm of this AHuman. - /// - /// A pointer to the foreground Arm of this AHuman. Ownership is NOT transferred. + /// @return A pointer to the foreground Arm of this AHuman. Ownership is NOT transferred. Arm* GetFGArm() const { return m_pFGArm; } - /// /// Sets the foreground Arm for this AHuman. - /// - /// The new Arm to use. + /// @param newArm The new Arm to use. void SetFGArm(Arm* newArm); - /// /// Gets the background arm of this AHuman. - /// - /// A pointer to the background arm of this AHuman. Ownership is NOT transferred. + /// @return A pointer to the background arm of this AHuman. Ownership is NOT transferred. Arm* GetBGArm() const { return m_pBGArm; } - /// /// Sets the background Arm for this AHuman. - /// - /// The new Arm to use. + /// @param newArm The new Arm to use. void SetBGArm(Arm* newArm); - /// /// Gets the foreground Leg of this AHuman. - /// - /// A pointer to the foreground Leg of this AHuman. Ownership is NOT transferred. + /// @return A pointer to the foreground Leg of this AHuman. Ownership is NOT transferred. Leg* GetFGLeg() const { return m_pFGLeg; } - /// /// Sets the foreground Leg for this AHuman. - /// - /// The new Leg to use. + /// @param newLeg The new Leg to use. void SetFGLeg(Leg* newLeg); - /// /// Gets the background Leg of this AHuman. - /// - /// A pointer to the background Leg of this AHuman. Ownership is NOT transferred. + /// @return A pointer to the background Leg of this AHuman. Ownership is NOT transferred. Leg* GetBGLeg() const { return m_pBGLeg; } - /// /// Sets the background Leg for this AHuman. - /// - /// The new Leg to use. + /// @param newLeg The new Leg to use. void SetBGLeg(Leg* newLeg); - /// /// Gets the foot Attachable of this AHuman's foreground Leg. - /// - /// A pointer to the foot Attachable of this AHuman's foreground Leg. Ownership is NOT transferred! + /// @return A pointer to the foot Attachable of this AHuman's foreground Leg. Ownership is NOT transferred! Attachable* GetFGFoot() const { return m_pFGLeg ? m_pFGLeg->GetFoot() : nullptr; } - /// /// Sets the foot Attachable of this AHuman's foreground Leg. - /// - /// The new foot for this AHuman's foreground Leg to use. + /// @param newFoot The new foot for this AHuman's foreground Leg to use. void SetFGFoot(Attachable* newFoot) { if (m_pFGLeg && m_pFGLeg->IsAttached()) { m_pFGLeg->SetFoot(newFoot); } } - /// /// Gets the foot Attachable of this AHuman's background Leg. - /// - /// A pointer to the foot Attachable of this AHuman's background Leg. Ownership is NOT transferred! + /// @return A pointer to the foot Attachable of this AHuman's background Leg. Ownership is NOT transferred! Attachable* GetBGFoot() const { return m_pBGLeg ? m_pBGLeg->GetFoot() : nullptr; } - /// /// Sets the foot Attachable of this AHuman's background Leg. - /// - /// The new foot for this AHuman's background Leg to use. + /// @param newFoot The new foot for this AHuman's background Leg to use. void SetBGFoot(Attachable* newFoot) { if (m_pBGLeg && m_pBGLeg->IsAttached()) { m_pBGLeg->SetFoot(newFoot); @@ -288,619 +189,389 @@ namespace RTE { } /// Gets this AHuman's UpperBodyState. - /// - /// This AHuman's UpperBodyState. + /// @return This AHuman's UpperBodyState. UpperBodyState GetUpperBodyState() const { return m_ArmsState; } - /// /// Sets this AHuman's UpperBodyState to the new state. - /// - /// This AHuman's new UpperBodyState. + /// @param newUpperBodyState This AHuman's new UpperBodyState. void SetUpperBodyState(UpperBodyState newUpperBodyState) { m_ArmsState = newUpperBodyState; } /// Gets this AHuman's ProneState. - /// - /// This AHuman's ProneState. + /// @return This AHuman's ProneState. ProneState GetProneState() const { return m_ProneState; } - /// /// Sets this AHuman's ProneState to the new state. - /// - /// This AHuman's new ProneState. + /// @param newProneState This AHuman's new ProneState. void SetProneState(ProneState newProneState) { m_ProneState = newProneState; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - // Arguments: Reference to the HitData struct which describes the collision. This - // will be modified to represent the results of the collision. - // Return value: Whether the collision has been deemed valid. If false, then disregard - // any impulses in the Hitdata. - + /// Calculates the collision response when another MO's Atom collides with + /// this MO's physical representation. The effects will be applied + /// directly to this MO, and also represented in the passed in HitData. + /// @param hitData Reference to the HitData struct which describes the collision. This + /// will be modified to represent the results of the collision. + /// @return Whether the collision has been deemed valid. If false, then disregard + /// any impulses in the Hitdata. bool CollideAtPoint(HitData& hitData) override; - /// /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. - /// - /// The SliceType of the PieSlice being handled. - /// Whether or not the activated PieSlice SliceType was able to be handled. + /// @param pieSliceType The SliceType of the PieSlice being handled. + /// @return Whether or not the activated PieSlice SliceType was able to be handled. bool HandlePieCommand(PieSlice::SliceType pieSliceType) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: AddInventoryItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an inventory item to this AHuman. This also puts that item - // directly in the hands of this if they are empty. - // Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! - // Return value: None. - + /// Adds an inventory item to this AHuman. This also puts that item + /// directly in the hands of this if they are empty. + /// @param pItemToAdd An pointer to the new item to add. Ownership IS TRANSFERRED! void AddInventoryItem(MovableObject* pItemToAdd) override; - /// /// Swaps the next MovableObject carried by this AHuman and puts one not currently carried into the back of the inventory of this. /// For safety reasons, this will dump any non-HeldDevice inventory items it finds into MovableMan, ensuring the returned item is a HeldDevice (but not casted to one, for overload purposes). - /// - /// A pointer to the external MovableObject to swap in. Ownership IS transferred. - /// Whether or not to mute the sound on this event. - /// The next HeldDevice in this AHuman's inventory, if there are any. + /// @param inventoryItemToSwapIn A pointer to the external MovableObject to swap in. Ownership IS transferred. + /// @param muteSound Whether or not to mute the sound on this event. + /// @return The next HeldDevice in this AHuman's inventory, if there are any. MovableObject* SwapNextInventory(MovableObject* inventoryItemToSwapIn = nullptr, bool muteSound = false) override; - /// /// Swaps the previous MovableObject carried by this AHuman and puts one not currently carried into the back of the inventory of this. /// For safety reasons, this will dump any non-HeldDevice inventory items it finds into MovableMan, ensuring the returned item is a HeldDevice (but not casted to one, for overload purposes). - /// - /// A pointer to the external MovableObject to swap in. Ownership IS transferred. - /// The previous HeldDevice in this AHuman's inventory, if there are any. + /// @param inventoryItemToSwapIn A pointer to the external MovableObject to swap in. Ownership IS transferred. + /// @return The previous HeldDevice in this AHuman's inventory, if there are any. MovableObject* SwapPrevInventory(MovableObject* inventoryItemToSwapIn = nullptr) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipFirearm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first found firearm - // in the inventory. If the held device already is a firearm, or no - // firearm is in inventory, nothing happens. - // Arguments: Whether to actually equip any matching item found in the inventory, - // or just report that it's there or not. - // Return value: Whether a firearm was successfully switched to, or already held. - + /// Switches the currently held device (if any) to the first found firearm + /// in the inventory. If the held device already is a firearm, or no + /// firearm is in inventory, nothing happens. + /// @param doEquip Whether to actually equip any matching item found in the inventory, (default: true) + /// or just report that it's there or not. + /// @return Whether a firearm was successfully switched to, or already held. bool EquipFirearm(bool doEquip = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipDeviceInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first found device - // of the specified group in the inventory. If the held device already - // is of that group, or no device is in inventory, nothing happens. - // Arguments: The group the device must belong to. - // Whether to actually equip any matching item found in the inventory, - // or just report that it's there or not. - // Return value: Whether a firearm was successfully switched to, or already held. - + /// Switches the currently held device (if any) to the first found device + /// of the specified group in the inventory. If the held device already + /// is of that group, or no device is in inventory, nothing happens. + /// @param group The group the device must belong to. + /// @param doEquip Whether to actually equip any matching item found in the inventory, (default: true) + /// or just report that it's there or not. + /// @return Whether a firearm was successfully switched to, or already held. bool EquipDeviceInGroup(std::string group, bool doEquip = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipLoadedFirearmInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first loaded HDFirearm - // of the specified group in the inventory. If no such weapon is in the - // inventory, nothing happens. - // Arguments: The group the HDFirearm must belong to. "Any" for all groups. - // The group the HDFirearm must *not* belong to. "None" for no group. - // Whether to actually equip any matching item found in the inventory, - // or just report that it's there or not. - // Return value: Whether a firearm was successfully switched to, or already held. - + /// Switches the currently held device (if any) to the first loaded HDFirearm + /// of the specified group in the inventory. If no such weapon is in the + /// inventory, nothing happens. + /// @param group The group the HDFirearm must belong to. "Any" for all groups. + /// @param exludeGroup The group the HDFirearm must *not* belong to. "None" for no group. + /// @param doEquip Whether to actually equip any matching item found in the inventory, (default: true) + /// or just report that it's there or not. + /// @return Whether a firearm was successfully switched to, or already held. bool EquipLoadedFirearmInGroup(std::string group, std::string exludeGroup, bool doEquip = true); - /// /// Switches the equipped HeldDevice (if any) to the first found device with the specified preset name in the inventory. /// If the equipped HeldDevice is of that module and preset name, nothing happens. - /// - /// The preset name of the HeldDevice to equip. - /// Whether to actually equip any matching item found in the inventory, or just report whether or not it's there. - /// Whether a matching HeldDevice was successfully found/switched -o, or already held. + /// @param presetName The preset name of the HeldDevice to equip. + /// @param doEquip Whether to actually equip any matching item found in the inventory, or just report whether or not it's there. + /// @return Whether a matching HeldDevice was successfully found/switched -o, or already held. bool EquipNamedDevice(const std::string& presetName, bool doEquip) { return EquipNamedDevice("", presetName, doEquip); } - /// /// Switches the equipped HeldDevice (if any) to the first found device with the specified module and preset name in the inventory. /// If the equipped HeldDevice is of that module and preset name, nothing happens. - /// - /// The module name of the HeldDevice to equip. - /// The preset name of the HeldDevice to equip. - /// Whether to actually equip any matching item found in the inventory, or just report whether or not it's there. - /// Whether a matching HeldDevice was successfully found/switched -o, or already held. + /// @param moduleName The module name of the HeldDevice to equip. + /// @param presetName The preset name of the HeldDevice to equip. + /// @param doEquip Whether to actually equip any matching item found in the inventory, or just report whether or not it's there. + /// @return Whether a matching HeldDevice was successfully found/switched -o, or already held. bool EquipNamedDevice(const std::string& moduleName, const std::string& presetName, bool doEquip); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipThrowable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first found ThrownDevice - // in the inventory. If the held device already is a ThrownDevice, or no - // ThrownDevice is in inventory, nothing happens. - // Arguments: Whether to actually equip any matching item found in the inventory, - // or just report that it's there or not. - // Return value: Whether a ThrownDevice was successfully switched to, or already held. - + /// Switches the currently held device (if any) to the first found ThrownDevice + /// in the inventory. If the held device already is a ThrownDevice, or no + /// ThrownDevice is in inventory, nothing happens. + /// @param doEquip Whether to actually equip any matching item found in the inventory, (default: true) + /// or just report that it's there or not. + /// @return Whether a ThrownDevice was successfully switched to, or already held. bool EquipThrowable(bool doEquip = true); - /// /// Switches the currently held device (if any) to the strongest digging tool in the inventory. - /// - /// Whether to actually equip the strongest digging tool, or just report whether a digging tool was found. - /// Whether or not the strongest digging tool was successfully equipped. + /// @param doEquip Whether to actually equip the strongest digging tool, or just report whether a digging tool was found. + /// @return Whether or not the strongest digging tool was successfully equipped. bool EquipDiggingTool(bool doEquip = true); - /// /// Estimates what material strength any digger this AHuman is carrying can penetrate. - /// - /// The maximum material strength this AHuman's digger can penetrate, or a default dig strength if they don't have a digger. + /// @return The maximum material strength this AHuman's digger can penetrate, or a default dig strength if they don't have a digger. float EstimateDigStrength() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipShield - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches the currently held device (if any) to the first found shield - // in the inventory. If the held device already is a shield, or no - // shield is in inventory, nothing happens. - // Arguments: None. - // Return value: Whether a shield was successfully switched to, or already held. - + /// Switches the currently held device (if any) to the first found shield + /// in the inventory. If the held device already is a shield, or no + /// shield is in inventory, nothing happens. + /// @return Whether a shield was successfully switched to, or already held. bool EquipShield(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipShieldInBGArm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tries to equip the first shield in inventory to the background arm; - // this only works if nothing is held at all, or the FG arm holds a - // one-handed device, or we're in inventory mode. - // Arguments: None. - // Return value: Whether a shield was successfully equipped in the background arm. - + /// Tries to equip the first shield in inventory to the background arm; + /// this only works if nothing is held at all, or the FG arm holds a + /// one-handed device, or we're in inventory mode. + /// @return Whether a shield was successfully equipped in the background arm. bool EquipShieldInBGArm(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: EquipDualWieldableInBGArm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tries to equip the first dual-wieldable in inventory to the background arm; - // this only works if nothing is held at all, or the FG arm holds a - // one-handed device, or we're in inventory mode. - // Arguments: None. - // Return value: Whether a shield was successfully equipped in the background arm. - + /// Tries to equip the first dual-wieldable in inventory to the background arm; + /// this only works if nothing is held at all, or the FG arm holds a + /// one-handed device, or we're in inventory mode. + /// @return Whether a shield was successfully equipped in the background arm. // bool EquipDualWieldableInBGArm(); - /// /// Gets the throw chargeup progress of this AHuman. - /// - /// The throw chargeup progress, as a scalar from 0 to 1. + /// @return The throw chargeup progress, as a scalar from 0 to 1. float GetThrowProgress() const { return m_ThrowPrepTime > 0 ? static_cast(std::min(m_ThrowTmr.GetElapsedSimTimeMS() / static_cast(m_ThrowPrepTime), 1.0)) : 1.0F; } - /// /// Unequips whatever is in the FG arm and puts it into the inventory. - /// - /// Whether there was anything to unequip. + /// @return Whether there was anything to unequip. bool UnequipFGArm(); - /// /// Unequips whatever is in the BG arm and puts it into the inventory. - /// - /// Whether there was anything to unequip. + /// @return Whether there was anything to unequip. bool UnequipBGArm(); - /// /// Unequips whatever is in either of the arms and puts them into the inventory. - /// void UnequipArms() { UnequipBGArm(); UnequipFGArm(); } - /// /// Gets the FG Arm's HeldDevice. Ownership is NOT transferred. - /// - /// The FG Arm's HeldDevice. + /// @return The FG Arm's HeldDevice. HeldDevice* GetEquippedItem() const { return m_pFGArm ? m_pFGArm->GetHeldDevice() : nullptr; } - /// /// Gets the BG Arm's HeldDevice. Ownership is NOT transferred. - /// - /// The BG Arm's HeldDevice. + /// @return The BG Arm's HeldDevice. HeldDevice* GetEquippedBGItem() const { return m_pBGArm ? m_pBGArm->GetHeldDevice() : nullptr; } - /// /// Gets the total mass of this AHuman's currently equipped devices. - /// - /// The mass of this AHuman's equipped devices. + /// @return The mass of this AHuman's equipped devices. float GetEquippedMass() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: FirearmIsReady - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is ready for use, and has - // ammo etc. - // Arguments: None. - // Return value: Whether a currently HDFirearm (if any) is ready for use. - + /// Indicates whether the currently held HDFirearm's is ready for use, and has + /// ammo etc. + /// @return Whether a currently HDFirearm (if any) is ready for use. bool FirearmIsReady() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ThrowableIsReady - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held ThrownDevice's is ready to go. - // Arguments: None. - // Return value: Whether a currently held ThrownDevice (if any) is ready for use. - + /// Indicates whether the currently held ThrownDevice's is ready to go. + /// @return Whether a currently held ThrownDevice (if any) is ready for use. bool ThrowableIsReady() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FirearmIsEmpty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is out of ammo. - // Arguments: None. - // Return value: Whether a currently HDFirearm (if any) is out of ammo. - + /// Indicates whether the currently held HDFirearm's is out of ammo. + /// @return Whether a currently HDFirearm (if any) is out of ammo. bool FirearmIsEmpty() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FirearmNeedsReload - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether any currently held HDFirearms are almost out of ammo. - // Arguments: None. - // Return value: Whether a currently HDFirearm (if any) has less than half of ammo left. - + /// Indicates whether any currently held HDFirearms are almost out of ammo. + /// @return Whether a currently HDFirearm (if any) has less than half of ammo left. bool FirearmNeedsReload() const; - /// /// Indicates whether currently held HDFirearms are reloading. If the parameter is true, it will only return true if all firearms are reloading, otherwise it will return whether any firearm is reloading. - /// - /// Whether or not currently held HDFirearms are reloading. + /// @return Whether or not currently held HDFirearms are reloading. bool FirearmsAreReloading(bool onlyIfAllFirearmsAreReloading) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FirearmIsSemiAuto - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the currently held HDFirearm's is semi or full auto. - // Arguments: None. - // Return value: Whether a currently HDFirearm (if any) is a semi auto device. - + /// Indicates whether the currently held HDFirearm's is semi or full auto. + /// @return Whether a currently HDFirearm (if any) is a semi auto device. bool FirearmIsSemiAuto() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FirearmActivationDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the currently held device's delay between pulling the trigger - // and activating. - // Arguments: None. - // Return value: Delay in ms or zero if not a HDFirearm. - + /// Returns the currently held device's delay between pulling the trigger + /// and activating. + /// @return Delay in ms or zero if not a HDFirearm. int FirearmActivationDelay() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReloadFirearm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reloads the currently held firearm, if any. Will only reload the BG Firearm if the FG one is full already, to support reloading guns one at a time. - // Arguments: Whether or not to only reload empty fireams. - // Return value: None. - + /// Reloads the currently held firearm, if any. Will only reload the BG Firearm if the FG one is full already, to support reloading guns one at a time. + /// @param onlyReloadEmptyFirearms Whether or not to only reload empty fireams. (default: false) void ReloadFirearms(bool onlyReloadEmptyFirearms = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsWithinRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a point on the scene is within close range of the currently - // used device and aiming status, if applicable. - // Arguments: A Vector with the aboslute coordinates of a point to check. - // Return value: Whether the point is within close range of this. - + /// Tells whether a point on the scene is within close range of the currently + /// used device and aiming status, if applicable. + /// @param point A Vector with the aboslute coordinates of a point to check. + /// @return Whether the point is within close range of this. bool IsWithinRange(Vector& point) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Look - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an unseen-revealing ray in the direction of where this is facing. - // Arguments: The degree angle to deviate from the current view point in the ray - // casting. A random ray will be chosen out of this +-range. - // The range, in pixels, beyond the actors sharp aim that the ray will have. - // Return value: Whether any unseen pixels were revealed by this look. - + /// Casts an unseen-revealing ray in the direction of where this is facing. + /// @param FOVSpread The degree angle to deviate from the current view point in the ray + /// casting. A random ray will be chosen out of this +-range. + /// @param range The range, in pixels, beyond the actors sharp aim that the ray will have. + /// @return Whether any unseen pixels were revealed by this look. bool Look(float FOVSpread, float range) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LookForGold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts a material detecting ray in the direction of where this is facing. - // Arguments: The degree angle to deviate from the current view point in the ray - // casting. A random ray will be chosen out of this +-range. - // The range, in pixels, that the ray will have. - // A Vector which will be filled with the absolute coordinates of any - // found gold. It will be unaltered if false is returned. - // Return value: Whether gold was spotted by this ray cast. If so, foundLocation - // has been filled out with the absolute location of the gold. - + /// Casts a material detecting ray in the direction of where this is facing. + /// @param FOVSpread The degree angle to deviate from the current view point in the ray + /// casting. A random ray will be chosen out of this +-range. + /// @param range The range, in pixels, that the ray will have. + /// @param foundLocation A Vector which will be filled with the absolute coordinates of any + /// found gold. It will be unaltered if false is returned. + /// @return Whether gold was spotted by this ray cast. If so, foundLocation + /// has been filled out with the absolute location of the gold. bool LookForGold(float FOVSpread, float range, Vector& foundLocation) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LookForMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an MO detecting ray in the direction of where the head is looking - // at the time. Factors including head rotation, sharp aim mode, and - // other variables determine how this ray is cast. - // Arguments: The degree angle to deviate from the current view point in the ray - // casting. A random ray will be chosen out of this +-range. - // A specific material ID to ignore (see through) - // Whether to ignore all terrain or not (true means 'x-ray vision'). - // Return value: A pointer to the MO seen while looking. - + /// Casts an MO detecting ray in the direction of where the head is looking + /// at the time. Factors including head rotation, sharp aim mode, and + /// other variables determine how this ray is cast. + /// @param FOVSpread The degree angle to deviate from the current view point in the ray (default: 45) + /// casting. A random ray will be chosen out of this +-range. + /// @param ignoreMaterial A specific material ID to ignore (see through) (default: 0) + /// @param ignoreAllTerrain Whether to ignore all terrain or not (true means 'x-ray vision'). (default: false) + /// @return A pointer to the MO seen while looking. MovableObject* LookForMOs(float FOVSpread = 45, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false); - /// /// Gets the GUI representation of this AHuman, only defaulting to its Head or body if no GraphicalIcon has been defined. - /// - /// The graphical representation of this AHuman as a BITMAP. + /// @return The graphical representation of this AHuman as a BITMAP. BITMAP* GetGraphicalIcon() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ResetAllTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resest all the timers used by this. Can be emitters, etc. This is to - // prevent backed up emissions to come out all at once while this has been - // held dormant in an inventory. - // Arguments: None. - // Return value: None. - + /// Resest all the timers used by this. Can be emitters, etc. This is to + /// prevent backed up emissions to come out all at once while this has been + /// held dormant in an inventory. void ResetAllTimers() override; - /// /// Detects slopes in terrain and updates the walk path rotation for the corresponding Layer accordingly. - /// - /// The Layer in question. + /// @param whichLayer The Layer in question. void UpdateWalkAngle(AHuman::Layer whichLayer); - /// /// Detects overhead ceilings and crouches for them. - /// void UpdateCrouching(); - /// /// Gets the walk path rotation for the specified Layer. - /// - /// The Layer in question. - /// The walk angle in radians. + /// @param whichLayer The Layer in question. + /// @return The walk angle in radians. float GetWalkAngle(AHuman::Layer whichLayer) const { return m_WalkAngle[whichLayer].GetRadAngle(); } - /// /// Sets the walk path rotation for the specified Layer. - /// - /// The Layer in question. - /// The angle to set. + /// @param whichLayer The Layer in question. + /// @param angle The angle to set. void SetWalkAngle(AHuman::Layer whichLayer, float angle) { m_WalkAngle[whichLayer] = Matrix(angle); } - /// /// Gets whether this AHuman has just taken a stride this frame. - /// - /// Whether this AHuman has taken a stride this frame or not. + /// @return Whether this AHuman has taken a stride this frame or not. bool StrideFrame() const { return m_StrideFrame; } - /// /// Gets whether this AHuman is currently attempting to climb something, using arms. - /// - /// Whether this AHuman is currently climbing or not. + /// @return Whether this AHuman is currently climbing or not. bool IsClimbing() const { return m_ArmClimbing[FGROUND] || m_ArmClimbing[BGROUND]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreControllerUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Update called prior to controller update. Ugly hack. Supposed to be done every frame. void PreControllerUpdate() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this AHuman's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this AHuman's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Actor's current graphical HUD overlay representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // Which player's screen this is being drawn to. May affect what HUD elements - // get drawn etc. - // Return value: None. - + /// Draws this Actor's current graphical HUD overlay representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param whichScreen Which player's screen this is being drawn to. May affect what HUD elements (default: 0) + /// get drawn etc. void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - /// /// Gets the LimbPath corresponding to the passed in Layer and MovementState values. - /// - /// Whether to get foreground or background LimbPath. - /// Which movement state to get the LimbPath for. - /// The LimbPath corresponding to the passed in Layer and MovementState values. + /// @param layer Whether to get foreground or background LimbPath. + /// @param movementState Which movement state to get the LimbPath for. + /// @return The LimbPath corresponding to the passed in Layer and MovementState values. LimbPath* GetLimbPath(Layer layer, MovementState movementState) { return &m_Paths[layer][movementState]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLimbPathSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get walking limb path speed for the specified preset. - // Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST - // Return value: Limb path speed for the specified preset in m/s. - + /// Get walking limb path speed for the specified preset. + /// @param speedPreset Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST + /// @return Limb path speed for the specified preset in m/s. float GetLimbPathSpeed(int speedPreset) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLimbPathSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Set walking limb path speed for the specified preset. - // Arguments: Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST. New speed value in m/s. - // Return value: None. - + /// Set walking limb path speed for the specified preset. + /// @param speedPreset Speed preset to set 0 = LimbPath::SLOW, 1 = Limbpath::NORMAL, 2 = LimbPath::FAST. New speed value in m/s. void SetLimbPathSpeed(int speedPreset, float speed); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLimbPathPushForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the default force that a limb traveling walking LimbPath can push against - // stuff in the scene with. - // Arguments: None. - // Return value: The default set force maximum, in kg * m/s^2. - + /// Gets the default force that a limb traveling walking LimbPath can push against + /// stuff in the scene with. + /// @return The default set force maximum, in kg * m/s^2. float GetLimbPathPushForce() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLimbPathPushForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the default force that a limb traveling walking LimbPath can push against - // stuff in the scene with. - // Arguments: The default set force maximum, in kg * m/s^2. - // Return value: None - + /// Sets the default force that a limb traveling walking LimbPath can push against + /// stuff in the scene with. + /// @param force The default set force maximum, in kg * m/s^2. void SetLimbPathPushForce(float force); - /// /// Gets the target rot angle for the given MovementState. - /// - /// The MovementState to get the rot angle target for. - /// The target rot angle for the given MovementState. + /// @param movementState The MovementState to get the rot angle target for. + /// @return The target rot angle for the given MovementState. float GetRotAngleTarget(MovementState movementState) { return m_RotAngleTargets[movementState]; } - /// /// Sets the target rot angle for the given MovementState. - /// - /// The MovementState to get the rot angle target for. - /// The new rot angle target to use. + /// @param movementState The MovementState to get the rot angle target for. + /// @param newRotAngleTarget The new rot angle target to use. void SetRotAngleTarget(MovementState movementState, float newRotAngleTarget) { m_RotAngleTargets[movementState] = newRotAngleTarget; } - /// /// Gets the duration it takes this AHuman to fully charge a throw. - /// - /// The duration it takes to fully charge a throw in MS. + /// @return The duration it takes to fully charge a throw in MS. long GetThrowPrepTime() const { return m_ThrowPrepTime; } - /// /// Sets the duration it takes this AHuman to fully charge a throw. - /// - /// New duration to fully charge a throw in MS. + /// @param newPrepTime New duration to fully charge a throw in MS. void SetThrowPrepTime(long newPrepTime) { m_ThrowPrepTime = newPrepTime; } - /// /// Gets the rate at which this AHuman's Arms will swing with Leg movement, if they're not holding or supporting a HeldDevice. - /// - /// The arm swing rate of this AHuman. + /// @return The arm swing rate of this AHuman. float GetArmSwingRate() const { return m_ArmSwingRate; } - /// /// Sets the rate at which this AHuman's Arms will swing with Leg movement, if they're not holding or supporting a HeldDevice. - /// - /// The new arm swing rate for this AHuman. + /// @param newValue The new arm swing rate for this AHuman. void SetArmSwingRate(float newValue) { m_ArmSwingRate = newValue; } - /// /// Gets the rate at which this AHuman's Arms will sway with Leg movement, if they're holding or supporting a HeldDevice. - /// - /// The device arm sway rate of this AHuman. + /// @return The device arm sway rate of this AHuman. float GetDeviceArmSwayRate() const { return m_DeviceArmSwayRate; } - /// /// Sets the rate at which this AHuman's Arms will sway with Leg movement, if they're holding or supporting a HeldDevice. - /// - /// The new device arm sway rate for this AHuman. + /// @param newValue The new device arm sway rate for this AHuman. void SetDeviceArmSwayRate(float newValue) { m_DeviceArmSwayRate = newValue; } - /// /// Gets this AHuman's max walkpath adjustment upwards to crouch below low ceilings. - /// - /// This AHuman's max walkpath adjustment. + /// @return This AHuman's max walkpath adjustment. float GetMaxWalkPathCrouchShift() const { return m_MaxWalkPathCrouchShift; } - /// /// Sets this AHuman's max walkpath adjustment upwards to crouch below low ceilings. - /// - /// The new value for this AHuman's max walkpath adjustment. + /// @param newValue The new value for this AHuman's max walkpath adjustment. void SetMaxWalkPathCrouchShift(float newValue) { m_MaxWalkPathCrouchShift = newValue; } - /// /// Gets this AHuman's max crouch rotation to duck below low ceilings. - /// - /// This AHuman's max crouch rotation adjustment. + /// @return This AHuman's max crouch rotation adjustment. float GetMaxCrouchRotation() const { return m_MaxCrouchRotation; } - /// /// Sets this AHuman's max crouch rotation to duck below low ceilings. - /// - /// The new value for this AHuman's max crouch rotation adjustment. + /// @param newValue The new value for this AHuman's max crouch rotation adjustment. void SetMaxCrouchRotation(float newValue) { m_MaxCrouchRotation = newValue; } - /// /// Gets this AHuman's current crouch amount. 0.0 == fully standing, 1.0 == fully crouched. - /// - /// This AHuman's current crouch amount. + /// @return This AHuman's current crouch amount. float GetCrouchAmount() const { return (m_WalkPathOffset.m_Y * -1.0F) / m_MaxWalkPathCrouchShift; } - /// /// Gets this AHuman's current crouch amount override. 0.0 == fully standing, 1.0 == fully crouched, -1 == no override. - /// - /// This AHuman's current crouch amount override. + /// @return This AHuman's current crouch amount override. float GetCrouchAmountOverride() const { return m_CrouchAmountOverride; } - /// /// Sets this AHuman's current crouch amount override. - /// - /// The new value for this AHuman's current crouch amount override. + /// @param newValue The new value for this AHuman's current crouch amount override. void SetCrouchAmountOverride(float newValue) { m_CrouchAmountOverride = newValue; } - /// /// Gets this AHuman's stride sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this AHuman's stride sound. + /// @return The SoundContainer for this AHuman's stride sound. SoundContainer* GetStrideSound() const { return m_StrideSound; } - /// /// Sets this AHuman's stride sound. Ownership IS transferred! - /// - /// The new SoundContainer for this AHuman's stride sound. + /// @param newSound The new SoundContainer for this AHuman's stride sound. void SetStrideSound(SoundContainer* newSound) { m_StrideSound = newSound; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Function that is called when we get a new movepath. /// This processes and cleans up the movepath. - /// void OnNewMovePath() override; - /// /// Draws an aiming aid in front of this AHuman for throwing. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// A normalized scalar that determines the magnitude of the reticle, to indicate force in the throw. + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param progressScalar A normalized scalar that determines the magnitude of the reticle, to indicate force in the throw. void DrawThrowingReticle(BITMAP* targetBitmap, const Vector& targetPos = Vector(), float progressScalar = 1.0F) const; // Member variables @@ -1047,26 +718,16 @@ namespace RTE { Timer m_JumpTimer; #pragma region Event Handling - /// /// Event listener to be run while this AHuman's PieMenu is opened. - /// - /// The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param pieMenu The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int WhilePieMenuOpenListener(const PieMenu* pieMenu) override; #pragma endregion - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this AHuman, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this AHuman, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Activity.cpp b/Source/Entities/Activity.cpp index 4166e47a37..aa1c491d54 100644 --- a/Source/Entities/Activity.cpp +++ b/Source/Entities/Activity.cpp @@ -22,8 +22,6 @@ namespace RTE { AbstractClassInfo(Activity, Entity); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::Clear() { m_ActivityState = ActivityState::NotStarted; m_Paused = false; @@ -70,8 +68,6 @@ namespace RTE { m_SavedValues.Reset(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::Create() { if (Entity::Create() < 0) { return -1; @@ -83,8 +79,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::Create(const Activity& reference) { Entity::Create(reference); @@ -132,8 +126,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -229,8 +221,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::Save(Writer& writer) const { Entity::Save(writer); @@ -288,8 +278,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::Start() { // Reseed the RNG for determinism SeedRNG(); @@ -346,8 +334,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::End() { g_AudioMan.FinishIngameLoopingSounds(); // Actor control is automatically disabled when players are set to observation mode, so no need to do anything directly. @@ -357,8 +343,6 @@ namespace RTE { m_ActivityState = ActivityState::Over; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::SetupPlayers() { m_TeamCount = 0; m_PlayerCount = 0; @@ -389,8 +373,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::DeactivatePlayer(int playerToDeactivate) { if (playerToDeactivate < Players::PlayerOne || playerToDeactivate >= Players::MaxPlayerCount || !m_IsActive[playerToDeactivate] || !m_TeamActive[m_Team[playerToDeactivate]]) { return false; @@ -415,8 +397,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::AddPlayer(int playerToAdd, bool isHuman, int team, float funds, const Icon* teamIcon) { if (playerToAdd < Players::PlayerOne || playerToAdd >= Players::MaxPlayerCount || team < Teams::TeamOne || team >= Teams::MaxTeamCount) { return m_PlayerCount; @@ -455,8 +435,6 @@ namespace RTE { return m_PlayerCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::ClearPlayers(bool resetFunds) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { m_IsActive[player] = false; @@ -478,8 +456,6 @@ namespace RTE { m_PlayerCount = m_TeamCount = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::GetHumanCount() const { int humans = 0; for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { @@ -490,8 +466,6 @@ namespace RTE { return humans; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::SetTeamOfPlayer(int player, int team) { if (team < Teams::TeamOne || team >= Teams::MaxTeamCount || player < Players::PlayerOne || player >= Players::MaxPlayerCount) { return; @@ -502,8 +476,6 @@ namespace RTE { m_IsActive[player] = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::PlayerOfScreen(int screen) const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (m_PlayerScreen[player] == screen) { @@ -513,8 +485,6 @@ namespace RTE { return Players::NoPlayer; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Activity::GetTeamName(int whichTeam) const { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { return m_TeamActive[whichTeam] ? m_TeamNames[whichTeam] : "Inactive Team"; @@ -522,8 +492,6 @@ namespace RTE { return ""; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::IsHumanTeam(int whichTeam) const { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { @@ -535,8 +503,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::PlayersInTeamCount(int team) const { int count = 0; for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { @@ -547,8 +513,6 @@ namespace RTE { return count; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::ChangeTeamFunds(float howMuch, int whichTeam) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { m_TeamFunds[whichTeam] += howMuch; @@ -563,8 +527,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::TeamFundsChanged(int whichTeam) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { bool changed = m_FundsChanged[whichTeam]; @@ -574,8 +536,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::UpdatePlayerFundsContribution(int player, float newFunds) { if (player < Players::PlayerOne || player >= Players::MaxPlayerCount || !m_IsActive[player] || !m_TeamActive[m_Team[player]]) { return false; @@ -604,8 +564,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Activity::GetPlayerFundsShare(int player) const { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { return (m_FundsContribution[player] > 0.0F) ? (m_TeamFunds[m_Team[player]] * m_TeamFundsShare[player]) : 0.0F; @@ -613,8 +571,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::SetPlayerBrain(Actor* newBrain, int player) { if ((player >= Players::PlayerOne || player < Players::MaxPlayerCount) && newBrain) { if (newBrain->GetTeam() != m_Team[player]) { @@ -625,8 +581,6 @@ namespace RTE { m_Brain[player] = newBrain; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::AnyBrainWasEvacuated() const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (m_BrainEvacuated[player]) { @@ -636,8 +590,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::IsAssignedBrain(Actor* actor) const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (actor == m_Brain[player]) { @@ -647,8 +599,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::IsBrainOfWhichPlayer(Actor* actor) const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (actor == m_Brain[player]) { @@ -658,8 +608,6 @@ namespace RTE { return Players::NoPlayer; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::IsOtherPlayerBrain(Actor* actor, int player) const { for (int playerToCheck = Players::PlayerOne; playerToCheck < Players::MaxPlayerCount; ++playerToCheck) { if (m_IsActive[playerToCheck] && playerToCheck != player && actor == m_Brain[playerToCheck]) { @@ -669,8 +617,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Activity::GetDifficultyString(int difficulty) { if (difficulty <= DifficultySetting::CakeDifficulty) { return "Cake"; @@ -687,8 +633,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Activity::GetAISkillString(int skill) { if (skill < AISkillSetting::InferiorSkill) { return "Inferior"; @@ -701,8 +645,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::GetTeamAISkill(int team) const { if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { return m_TeamAISkillLevels[team]; @@ -720,8 +662,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::ReassignSquadLeader(const int player, const int team) { if (m_ControlledActor[player]->GetAIMode() == Actor::AIMODE_SQUAD) { MOID leaderID = m_ControlledActor[player]->GetAIMOWaypointID(); @@ -764,8 +704,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::SwitchToActor(Actor* actor, int player, int team) { if (team < Teams::TeamOne || team >= Teams::MaxTeamCount || player < Players::PlayerOne || player >= Players::MaxPlayerCount || !m_IsHuman[player]) { return false; @@ -806,8 +744,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - void Activity::LoseControlOfActor(int player) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { if (Actor* actor = m_ControlledActor[player]; actor && g_MovableMan.IsActor(actor)) { @@ -820,8 +756,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::HandleCraftEnteringOrbit(ACraft* orbitedCraft) { if (!orbitedCraft) { return; @@ -876,8 +810,6 @@ namespace RTE { m_TeamDeaths[orbitedCraftTeam]--; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Activity::GetBrainCount(bool getForHuman) const { int brainCount = 0; @@ -895,8 +827,6 @@ namespace RTE { return brainCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::SwitchToPrevOrNextActor(bool nextActor, int player, int team, const Actor* actorToSkip) { if (team < Teams::TeamOne || team >= Teams::MaxTeamCount || player < Players::PlayerOne || player >= Players::MaxPlayerCount || !m_IsHuman[player]) { return; @@ -928,8 +858,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Activity::Update() { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (m_MessageTimer[player].IsPastSimMS(5000)) { @@ -941,8 +869,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Activity::CanBeUserSaved() const { if (const Scene* scene = g_SceneMan.GetScene(); (scene && scene->IsMetagameInternal()) || g_MetaMan.GameInProgress()) { return false; diff --git a/Source/Entities/Activity.h b/Source/Entities/Activity.h index 8d58759c9a..f9ad75e07b 100644 --- a/Source/Entities/Activity.h +++ b/Source/Entities/Activity.h @@ -10,18 +10,14 @@ namespace RTE { class Scene; class ACraft; - /// /// Base class for all Activities, including game modes and editors. - /// class Activity : public Entity { public: SerializableOverrideMethods; ClassInfoGetters; - /// /// Enumeration for the different states the Activity can be in. - /// enum ActivityState { NoActivity = -1, NotStarted = 0, @@ -33,9 +29,7 @@ namespace RTE { Over }; - /// /// Enumeration for the different teams in an activity. - /// enum Teams { NoTeam = -1, TeamOne = 0, @@ -45,9 +39,7 @@ namespace RTE { MaxTeamCount }; - /// /// Enumeration for the different observations states (camera viewpoint). - /// enum ViewState { Normal = 0, Observe, @@ -61,9 +53,7 @@ namespace RTE { UnitSelectCircle, }; - /// /// Enumeration for the different difficulty settings. - /// enum DifficultySetting { MinDifficulty = 0, CakeDifficulty = 20, @@ -74,9 +64,7 @@ namespace RTE { MaxDifficulty = 100, }; - /// /// Enumeration for the different AI skill settings. - /// enum AISkillSetting { MinSkill = 1, InferiorSkill = 35, @@ -87,35 +75,25 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate an Activity object in system memory. Create() should be called before using the object. - /// Activity() { Clear(); } - /// /// Makes the Activity object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates an Activity to be identical to another, by deep copy. - /// - /// A reference to the Activity to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Activity to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Activity& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up an Activity object before deletion from system memory. - /// ~Activity() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the Activity object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); @@ -125,534 +103,388 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the current Activity state code. See the ActivityState enumeration for values. - /// - /// The current state of this Activity. See ActivityState enumeration. + /// @return The current state of this Activity. See ActivityState enumeration. ActivityState GetActivityState() const { return m_ActivityState; } - /// /// Overrides the current Activity state. Should not be used much, use dedicated state setting functions instead. - /// - /// The new state to set. + /// @param newState The new state to set. void SetActivityState(ActivityState newState) { m_ActivityState = newState; } - /// /// Indicates whether the Activity is currently running or not (not editing, over or paused) - /// - /// Whether the Activity is running or not. + /// @return Whether the Activity is running or not. bool IsRunning() const { return (m_ActivityState == ActivityState::Running || m_ActivityState == ActivityState::Editing) && !m_Paused; } - /// /// Indicates whether the Activity is currently paused or not. - /// - /// Whether the Activity is paused or not. + /// @return Whether the Activity is paused or not. bool IsPaused() const { return m_Paused; } - /// /// Pauses and unpauses the Activity. - /// - /// Whether to pause the Activity or not. + /// @param pause Whether to pause the Activity or not. virtual void SetPaused(bool pause = true) { m_Paused = pause; } - /// /// Indicates whether the Activity is over or not. - /// - /// Whether the Activity is over or not. + /// @return Whether the Activity is over or not. bool IsOver() const { return m_ActivityState == ActivityState::Over; } - /// /// Gets the user-friendly description of this Activity. - /// - /// A string with the user-friendly description of this Activity. + /// @return A string with the user-friendly description of this Activity. std::string GetDescription() const { return m_Description; } - /// /// Gets the max number of players supported by this Activity. - /// - /// The max number of players supported by this Activity. + /// @return The max number of players supported by this Activity. int GetMaxPlayerSupport() const { return m_MaxPlayerSupport; } - /// /// Gets the minimum number of teams with players that this Activity requires. - /// - /// The minimum number of Teams this Activity requires to run. + /// @return The minimum number of Teams this Activity requires to run. int GetMinTeamsRequired() const { return m_MinTeamsRequired; } - /// /// Tells if a particular Scene supports this specific Activity on it. Usually that means certain Areas need to be defined in the Scene. - /// - /// The Scene to check if it supports this Activity. Ownership is NOT transferred! - /// How many teams we're checking for. Some scenes may support and Activity but only for a limited number of teams. If -1, not applicable. - /// Whether the Scene has the right stuff. + /// @param scene The Scene to check if it supports this Activity. Ownership is NOT transferred! + /// @param teams How many teams we're checking for. Some scenes may support and Activity but only for a limited number of teams. If -1, not applicable. + /// @return Whether the Scene has the right stuff. virtual bool SceneIsCompatible(Scene* scene, int teams = -1) { return scene && teams <= m_MinTeamsRequired; } - /// /// Shows in which stage of the Campaign this appears. - /// - /// The stage number in the campaign. -1 means it's not in the campaign. + /// @return The stage number in the campaign. -1 means it's not in the campaign. int GetInCampaignStage() const { return m_InCampaignStage; } - /// /// /// Sets in which stage of the Campaign this appears. - /// - /// The new stage to set. -1 means it doesn't appear in the campaign. + /// @param newStage The new stage to set. -1 means it doesn't appear in the campaign. void SetInCampaignStage(int newStage) { m_InCampaignStage = newStage; } - /// /// Gets the name of the current scene. - /// - /// A string with the instance name of the scene. + /// @return A string with the instance name of the scene. std::string GetSceneName() const { return m_SceneName; } - /// /// Sets the name of the scene this is associated with. - /// - /// The new name of the scene to load next game. + /// @param sceneName The new name of the scene to load next game. void SetSceneName(const std::string sceneName) { m_SceneName = sceneName; } - /// /// Gets whether craft must be considered orbited if they reach the map border on non-wrapped maps. - /// - /// Whether craft are considered orbited when at the border of a non-wrapping map. + /// @return Whether craft are considered orbited when at the border of a non-wrapping map. bool GetCraftOrbitAtTheEdge() const { return m_CraftOrbitAtTheEdge; } - /// /// Sets whether craft must be considered orbited if they reach the map border on non-wrapped maps. - /// - /// Whether to consider orbited or not. + /// @param value Whether to consider orbited or not. void SetCraftOrbitAtTheEdge(bool value) { m_CraftOrbitAtTheEdge = value; } #pragma endregion #pragma region Virtual Override Methods - /// /// Officially starts this Activity. Creates all the data necessary to start the Activity. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int Start(); - /// /// Forces this Activity to end. - /// virtual void End(); - /// /// Updates the state of this Activity. Supposed to be done every frame before drawing. - /// virtual void Update(); - /// /// Draws the currently active GUI of a screen to a BITMAP of choice. - /// - /// A pointer to a screen-sized BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the scene. - /// Which screen's GUI to draw onto the bitmap. + /// @param targetBitmap A pointer to a screen-sized BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. + /// @param whichScreen Which screen's GUI to draw onto the bitmap. virtual void DrawGUI(BITMAP* targetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0) {} - /// /// Draws this Activity's current graphical representation to a BITMAP of choice. This includes all game-related graphics. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the scene. + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. virtual void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector()) {} #pragma endregion #pragma region Player Handling - /// /// Pre-calculates the player-to-screen index map, counts the number of active players etc. - /// virtual void SetupPlayers(); - /// /// Indicates whether a specific player is active in the current game. - /// - /// Which player index to check. - /// Whether the player is active in the current Activity. + /// @param player Which player index to check. + /// @return Whether the player is active in the current Activity. bool PlayerActive(int player) const { return m_IsActive[player]; } - /// /// Turns off a player if they were active. Should only be done if brain etc are already taken care of and disposed of properly. /// Will also deactivate the team this player is on, if there's no other active players still on it. - /// - /// Which player index to deactivate. - /// Whether the player was active before trying to deactivate. + /// @param playerToDeactivate Which player index to deactivate. + /// @return Whether the player was active before trying to deactivate. bool DeactivatePlayer(int playerToDeactivate); - /// /// Sets up a specific player for this Activity, AI or Human. - /// - /// Which player slot to set up - PlayerOne to PlayerFour. - /// Whether this player is Human. - /// Which Team this player belongs to. - /// How many funds this player contributes to its Team's total funds. - /// The team flag icon of this player. Ownership is NOT transferred! - /// The new total number of active players in the current game. + /// @param playerToAdd Which player slot to set up - PlayerOne to PlayerFour. + /// @param isHuman Whether this player is Human. + /// @param team Which Team this player belongs to. + /// @param funds How many funds this player contributes to its Team's total funds. + /// @param teamIcon The team flag icon of this player. Ownership is NOT transferred! + /// @return The new total number of active players in the current game. int AddPlayer(int playerToAdd, bool isHuman, int team, float funds, const Icon* teamIcon = 0); - /// /// Sets all players as not active in the current Activity. - /// - /// Whether to reset the team funds as well. + /// @param resetFunds Whether to reset the team funds as well. void ClearPlayers(bool resetFunds = true); - /// /// Gets the total number of active players in the current Activity, AI or Human. - /// - /// The total number of players in the current Activity. + /// @return The total number of players in the current Activity. int GetPlayerCount() const { return m_PlayerCount; } - /// /// Gets the total number of human players in the current Activity. - /// - /// The total number of players in the current Activity. + /// @return The total number of players in the current Activity. int GetHumanCount() const; - /// /// Indicates whether a specific player is human in the current game, ie not an AI player and has a screen etc. - /// - /// Which player index to check. - /// Whether the player is active as a Human in the current Activity. + /// @param player Which player index to check. + /// @return Whether the player is active as a Human in the current Activity. bool PlayerHuman(int player) const { return m_IsHuman[player]; } - /// /// Gets the current team a specific player belongs to. - /// - /// The player to get the team info on. - /// The team number of the specified player. + /// @param player The player to get the team info on. + /// @return The team number of the specified player. int GetTeamOfPlayer(int player) const { return m_Team[player]; } - /// /// Sets the current team a specific player belongs to. - /// - /// The player to set the team for. - /// The team number to set the player to. + /// @param player The player to set the team for. + /// @param team The team number to set the player to. void SetTeamOfPlayer(int player, int team); - /// /// Converts a player index into a screen index, and only if that player is human. - /// - /// Which player index to convert. - /// The screen index, or -1 if non-human player or no players. + /// @param player Which player index to convert. + /// @return The screen index, or -1 if non-human player or no players. int ScreenOfPlayer(int player) const { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_PlayerScreen[player] : -1; } - /// /// Converts a screen index into a player index, if that screen exists. - /// - /// Which screen index to convert. - /// The player index, or -1 if that screen is not in use. + /// @param screen Which screen index to convert. + /// @return The player index, or -1 if that screen is not in use. int PlayerOfScreen(int screen) const; - /// /// Gets the current viewing state for a specific player. See the ViewState enumeration for values. - /// - /// Which player to get the view state for. - /// The current viewing state of the player. + /// @param whichPlayer Which player to get the view state for. + /// @return The current viewing state of the player. ViewState GetViewState(int whichPlayer = 0) const { return m_ViewState[whichPlayer]; } - /// /// Sets the current viewing state for a specific player. See the ViewState enumeration for values. - /// - /// The state to set to. - /// Which player to set the view state for. + /// @param whichViewState The state to set to. + /// @param whichPlayer Which player to set the view state for. void SetViewState(ViewState whichViewState, int whichPlayer = 0) { m_ViewState[whichPlayer] = whichViewState; } - /// /// Resets the message timer for one player. - /// - /// The player to reset the message timer for. + /// @param player The player to reset the message timer for. void ResetMessageTimer(int player = 0) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_MessageTimer[player].Reset(); } } - /// /// Gets a pointer to the GUI controller of the specified player. - /// - /// Which player to get the Controller of. - /// A pointer to the player's Controller. Ownership is NOT transferred! + /// @param player Which player to get the Controller of. + /// @return A pointer to the player's Controller. Ownership is NOT transferred! Controller* GetPlayerController(int player = 0) { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? &m_PlayerController[player] : nullptr; } #pragma endregion #pragma region Team Handling - /// /// Gets the total number of teams in the current Activity. - /// - /// The total number of teams in the current Activity. + /// @return The total number of teams in the current Activity. int GetTeamCount() const { return m_TeamCount; } - /// /// Gets the name of a specific team. - /// - /// Which team to get the name of. 0 = first team. - /// The current name of that team. + /// @param whichTeam Which team to get the name of. 0 = first team. + /// @return The current name of that team. std::string GetTeamName(int whichTeam = 0) const; - /// /// Sets the name of a specific team. - /// - /// Which team to set the name of. 0 = first team. - /// The name to set it to. + /// @param whichTeam Which team to set the name of. 0 = first team. + /// @param newName The name to set it to. void SetTeamName(int whichTeam, const std::string& newName) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { m_TeamNames[whichTeam] = newName; } } - /// /// Gets the Icon of a specific team. - /// - /// Which team to get the Icon of. 0 = first team. - /// The current Icon of that team. + /// @param whichTeam Which team to get the Icon of. 0 = first team. + /// @return The current Icon of that team. const Icon* GetTeamIcon(int whichTeam = 0) const { return (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) ? &m_TeamIcons[whichTeam] : nullptr; } - /// /// Sets the Icon of a specific team. - /// - /// Which team to set the Icon of. 0 = first team. - /// The Icon to set it to. + /// @param whichTeam Which team to set the Icon of. 0 = first team. + /// @param newIcon The Icon to set it to. void SetTeamIcon(int whichTeam, const Icon& newIcon) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { m_TeamIcons[whichTeam] = newIcon; } } - /// /// Indicates whether a specific team is active in the current game. - /// - /// Which team index to check. - /// Whether the team is active in the current Activity. + /// @param team Which team index to check. + /// @return Whether the team is active in the current Activity. bool TeamActive(int team) const { return (team >= Teams::TeamOne && team < Teams::MaxTeamCount) ? m_TeamActive[team] : false; } - /// /// Sets the given team as active, even if it shouldn't be considered as such normally. Useful for Activities that don't want to define/show all used teams. - /// - /// The team to force as active. + /// @param team The team to force as active. void ForceSetTeamAsActive(int team) { if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { m_TeamActive[team] = true; } } - /// /// Indicates whether a team is player controlled or not. - /// - /// The team number to check. - /// Whether team is player controlled or not. + /// @param team The team number to check. + /// @return Whether team is player controlled or not. bool IsHumanTeam(int team) const; - /// /// Gets the current number of players in a specific team. - /// - /// Which team to get the player count for. - /// The player count in the specified team. + /// @param team Which team to get the player count for. + /// @return The player count in the specified team. int PlayersInTeamCount(int team) const; - /// /// Gets the number of deaths on a specific team so far on the current Activity. - /// - /// Which team to get the death tally of. 0 = first team. - /// The current death count. + /// @param whichTeam Which team to get the death tally of. 0 = first team. + /// @return The current death count. int GetTeamDeathCount(int whichTeam = 0) const { return (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) ? m_TeamDeaths[whichTeam] : 0; } - /// /// Increments the tally of a death of an actor on a specific team. - /// - /// Which team to increase the death count of. 0 = first team. - /// The new death count. - /// The updated death count of the team. + /// @param whichTeam Which team to increase the death count of. 0 = first team. + /// @param howMany The new death count. + /// @return The updated death count of the team. int ReportDeath(int whichTeam = 0, int howMany = 1) { return (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) ? m_TeamDeaths[whichTeam] += howMany : 0; } #pragma endregion #pragma region Funds Handling - /// /// Gets the amount of funds a specific team currently has in the Activity. - /// - /// Which team to get the fund count from. 0 = first team. - /// A float with the funds tally for the requested team. + /// @param whichTeam Which team to get the fund count from. 0 = first team. + /// @return A float with the funds tally for the requested team. float GetTeamFunds(int whichTeam = 0) const { return (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) ? m_TeamFunds[whichTeam] : 0; } - /// /// Sets the amount of funds a specific team currently has in the Activity. - /// - /// Which team to set the fund count for. 0 = first team. - /// A float with the funds tally for the requested team. + /// @param newFunds Which team to set the fund count for. 0 = first team. + /// @param which A float with the funds tally for the requested team. void SetTeamFunds(float newFunds, int whichTeam = 0) { if (whichTeam >= Teams::TeamOne && whichTeam < Teams::MaxTeamCount) { m_TeamFunds[whichTeam] = newFunds; } } - /// /// Changes a team's funds level by a certain amount. - /// - /// The amount with which to change the funds balance. - /// Which team to alter the funds of. 0 = first team. + /// @param howMuch The amount with which to change the funds balance. + /// @param whichTeam Which team to alter the funds of. 0 = first team. void ChangeTeamFunds(float howMuch, int whichTeam = 0); - /// /// Checks whether the team funds changed since last time this was called. This also resets the state, so calling this again on the same team will yield false unless it's been changed again. - /// - /// Which team's funds to check. - /// Whether funds amount changed for this team since last time this was called. + /// @param whichTeam Which team's funds to check. + /// @return Whether funds amount changed for this team since last time this was called. bool TeamFundsChanged(int whichTeam = 0); - /// /// Gets the amount of funds a specific player originally added to his team's collective stash. - /// - /// Which player to check for. - /// A float with the funds originally deposited by this player. + /// @param player Which player to check for. + /// @return A float with the funds originally deposited by this player. float GetPlayerFundsContribution(int player) const { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_FundsContribution[player] : 0; } - /// /// Sets a new amount of starting funds for a player, after he has already been added. - /// - /// Which player slot to update - has to already be active. - /// Updated value of how many funds this player contributes to its Team's total funds. - /// Whether the update was successful. + /// @param player Which player slot to update - has to already be active. + /// @param newFunds Updated value of how many funds this player contributes to its Team's total funds. + /// @return Whether the update was successful. bool UpdatePlayerFundsContribution(int player, float newFunds); - /// /// Gets the share of funds a specific PLAYER currently has in the game, calculated from his original contribution to his team's collective funds. - /// - /// Which player to get the fund count from. - /// A float with the funds tally for the requested player. + /// @param player Which player to get the fund count from. + /// @return A float with the funds tally for the requested player. float GetPlayerFundsShare(int player = 0) const; #pragma endregion #pragma region Brain Handling - /// /// Shows how many human controlled brains are left in this Activity. - /// - /// How many human controlled brains are left in this Activity. + /// @return How many human controlled brains are left in this Activity. int HumanBrainCount() const { return GetBrainCount(true); } - /// /// Shows how many AI controlled brains are left in this Activity. - /// - /// how many AI controlled brains are left in this Activity. + /// @return how many AI controlled brains are left in this Activity. int AIBrainCount() const { return GetBrainCount(false); } - /// /// Gets the current Brain actor for a specific player. - /// - /// Which player to get the brain actor for. - /// A pointer to the Brain Actor. Ownership is NOT transferred! + /// @param player Which player to get the brain actor for. + /// @return A pointer to the Brain Actor. Ownership is NOT transferred! Actor* GetPlayerBrain(int player = 0) const { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_Brain[player] : nullptr; } - /// /// Sets the current Brain actor for a specific player. - /// - /// A pointer to the new brain Actor. Ownership is NOT transferred! - /// Which team to set the brain actor for. + /// @param newBrain A pointer to the new brain Actor. Ownership is NOT transferred! + /// @param player Which team to set the brain actor for. void SetPlayerBrain(Actor* newBrain, int player = 0); - /// /// Shows whether a specific player ever had a Brain yet. - /// - /// Which player to check whether they ever had a Brain. - /// Whether this player ever had a Brain. + /// @param player Which player to check whether they ever had a Brain. + /// @return Whether this player ever had a Brain. bool PlayerHadBrain(int player = 0) const { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_HadBrain[player] : false; } - /// /// Sets to indicate that the player had a Brain at some point. This is to simulate that in automated battle cases. - /// - /// Which player to set whether he had a Brain or not. - /// Whether he should be flagged as having had a Brain. + /// @param player Which player to set whether he had a Brain or not. + /// @param hadBrain Whether he should be flagged as having had a Brain. void SetPlayerHadBrain(int player, bool hadBrain = true) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) m_HadBrain[player] = hadBrain; } - /// /// Shows whether a specific player's Brain was evacuated into orbit so far. - /// - /// Which player to check whether their Brain was evacuated. - /// Whether this player had a Brain that was evacuated. + /// @param player Which player to check whether their Brain was evacuated. + /// @return Whether this player had a Brain that was evacuated. bool BrainWasEvacuated(int player = 0) const { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_BrainEvacuated[player] : false; } - /// /// Sets whether a player's Brain was evacuated during the Activity. - /// - /// Which player to check whether their Brain was evacuated. - /// Whether it was evacuated yet. + /// @param player Which player to check whether their Brain was evacuated. + /// @param evacuated Whether it was evacuated yet. void SetBrainEvacuated(int player = 0, bool evacuated = true) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_BrainEvacuated[player] = evacuated; } } - /// /// Shows whether ANY player evacuated their Brain. - /// - /// Whether any player evacuated their Brain yet. + /// @return Whether any player evacuated their Brain yet. bool AnyBrainWasEvacuated() const; - /// /// Shows whether the passed in actor is the Brain of any player. - /// - /// Which Actor to check for player braininess. - /// Whether any player's Brain or not. + /// @param actor Which Actor to check for player braininess. + /// @return Whether any player's Brain or not. bool IsAssignedBrain(Actor* actor) const; - /// /// Shows which player has a specific actor as a Brain, if any. - /// - /// Which Actor to check for player braininess. - /// Which player has this assigned as a Brain, if any. + /// @param actor Which Actor to check for player braininess. + /// @return Which player has this assigned as a Brain, if any. int IsBrainOfWhichPlayer(Actor* actor) const; - /// /// Shows whether the passed in actor is the Brain of any other player. - /// - /// Which Actor to check for other player braininess. - /// From which player's perspective to check. - /// Whether other player's Brain or not. + /// @param actor Which Actor to check for other player braininess. + /// @param player From which player's perspective to check. + /// @return Whether other player's Brain or not. bool IsOtherPlayerBrain(Actor* actor, int player) const; #pragma endregion #pragma region Difficulty Handling - /// /// Returns string representation of a given difficulty value. - /// - /// Difficulty setting - /// Corresponding difficulty string. + /// @param difficulty Difficulty setting + /// @return Corresponding difficulty string. static std::string GetDifficultyString(int difficulty); - /// /// Gets the current difficulty setting. - /// - /// The current setting. + /// @return The current setting. int GetDifficulty() const { return m_Difficulty; } - /// /// Sets the current difficulty setting. - /// - /// The new difficulty setting. + /// @param newDifficulty The new difficulty setting. void SetDifficulty(int newDifficulty) { m_Difficulty = Limit(newDifficulty, DifficultySetting::MaxDifficulty, DifficultySetting::MinDifficulty); } #pragma endregion #pragma region AI Handling - /// /// Returns string representation of a given AI skill value. - /// - /// AI skill setting. - /// Corresponding AI skill string. + /// @param skill AI skill setting. + /// @return Corresponding AI skill string. static std::string GetAISkillString(int skill); - /// /// Returns skill level for specified team. If team is less than 0 or greater than 3 an average of all teams is returned. - /// - /// Team to get skill level for. - /// Team skill level. + /// @param team Team to get skill level for. + /// @return Team skill level. int GetTeamAISkill(int team) const; - /// /// Sets AI skill level for specified team. - /// - /// The team to set for. - /// AI skill level, 1-100. + /// @param team The team to set for. + /// @param skill AI skill level, 1-100. void SetTeamAISkill(int team, int skill) { if (team >= Teams::TeamOne && team < Teams::MaxTeamCount) { m_TeamAISkillLevels[team] = Limit(skill, AISkillSetting::UnfairSkill, AISkillSetting::MinSkill); @@ -661,101 +493,73 @@ namespace RTE { #pragma endregion #pragma region Actor Handling - /// /// Gets the currently controlled actor of a specific player. - /// - /// Which player to get the controlled actor of. - /// A pointer to the controlled Actor. Ownership is NOT transferred! 0 If no actor is currently controlled by this player. + /// @param player Which player to get the controlled actor of. + /// @return A pointer to the controlled Actor. Ownership is NOT transferred! 0 If no actor is currently controlled by this player. Actor* GetControlledActor(int player = 0) { return (player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_ControlledActor[player] : nullptr; } - /// /// Makes the player's ControlledActor the leader of any squad it is a member of. - /// - /// Player to reassign for. - /// Team of the player. + /// @param player Player to reassign for. + /// @param team Team of the player. void ReassignSquadLeader(const int player, const int team); - /// /// Forces the ActivityMan to focus player control to a specific Actor for a specific team. Ownership is NOT transferred! - /// - /// Which Actor to switch focus to. The team of this Actor will be set once it is passed in. The actor should have been added to MovableMan already. - /// Player to force for. - /// Which team to switch to next actor on. - /// Whether the focus switch was successful or not. + /// @param actor Which Actor to switch focus to. The team of this Actor will be set once it is passed in. The actor should have been added to MovableMan already. + /// @param player Player to force for. + /// @param team Which team to switch to next actor on. + /// @return Whether the focus switch was successful or not. virtual bool SwitchToActor(Actor* actor, int player = 0, int team = 0); - /// /// Forces the Activity to focus player control to the previous Actor of a specific team, other than the current one focused on. - /// - /// Player to force for. - /// Which team to switch to next Actor on. - /// An Actor pointer to skip in the sequence. + /// @param player Player to force for. + /// @param team Which team to switch to next Actor on. + /// @param actorToSkip An Actor pointer to skip in the sequence. virtual void SwitchToPrevActor(int player, int team, Actor* actorToSkip = 0) { SwitchToPrevOrNextActor(false, player, team, actorToSkip); } - /// /// Forces the Activity to focus player control to the next Actor of a specific team, other than the current one focused on. - /// - /// Player to force for. - /// Which team to switch to next Actor on. - /// An Actor pointer to skip in the sequence. + /// @param player Player to force for. + /// @param team Which team to switch to next Actor on. + /// @param actorToSkip An Actor pointer to skip in the sequence. virtual void SwitchToNextActor(int player, int team, Actor* actorToSkip = 0) { SwitchToPrevOrNextActor(true, player, team, actorToSkip); } - /// /// Forces player to lose control of the currently selected Actor, as if it had died. - /// - /// Which player to lose control of their selected Actor. + /// @param player Which player to lose control of their selected Actor. virtual void LoseControlOfActor(int player); - /// /// Handles when an ACraft has left the game scene and entered orbit, though does not delete it. Ownership is NOT transferred, as the ACraft's inventory is just 'unloaded'. - /// - /// The ACraft instance that entered orbit. Ownership is NOT transferred! + /// @param orbitedCraft The ACraft instance that entered orbit. Ownership is NOT transferred! virtual void HandleCraftEnteringOrbit(ACraft* orbitedCraft); #pragma endregion #pragma region Save and Load Handling - /// /// Gets whether or not this Activity can be saved by the user. For this to be true, the Scene must not be MetagameInternal, and the AllowsUserSaving flag must not be disabled. - /// - /// Whether or not this Activity can be saved. + /// @return Whether or not this Activity can be saved. bool CanBeUserSaved() const; - /// /// Gets whether or not this Activity allows the player to manually save. - /// - /// Whether or not this Activity allows the player to manually save. + /// @return Whether or not this Activity allows the player to manually save. bool GetAllowsUserSaving() const { return m_AllowsUserSaving; } - /// /// Sets whether or not this Activity can be manually saved be the player. - /// - /// Whether or not this Activity can be manually saved be the player. + /// @param allowsUserSaving Whether or not this Activity can be manually saved be the player. void SetAllowsUserSaving(bool allowsUserSaving) { m_AllowsUserSaving = allowsUserSaving; } - /// /// Saves a string which will be stored in our ini. - /// - /// The key of the saved string. - /// The string to save. + /// @param key The key of the saved string. + /// @param value The string to save. void SaveString(const std::string& key, const std::string& value) { m_SavedValues.SaveString(key, value); }; - /// /// Loads and returns a previously saved string. - /// - /// The key of the string to load. + /// @param key The key of the string to load. const std::string& LoadString(const std::string& key) { return m_SavedValues.LoadString(key); }; - /// /// Saves a number which will be stored in our ini. - /// - /// The key of the saved number. - /// The number to save. + /// @param key The key of the saved number. + /// @param value The number to save. void SaveNumber(const std::string& key, float value) { m_SavedValues.SaveNumber(key, value); }; - /// /// Loads and returns a previously saved number. - /// - /// The key of the string to load. + /// @param key The key of the string to load. float LoadNumber(const std::string& key) { return m_SavedValues.LoadNumber(key); }; #pragma endregion @@ -808,33 +612,25 @@ namespace RTE { Timer m_MessageTimer[Players::MaxPlayerCount]; //!< Message timer for each player. - /// /// Generic additional saved strings/numbers, which are used for scripts primarily. /// They live here in the base class because GAScripted doesn't have a lua interface although it's a little messy. /// On the bright side, this would allows other parts of the code to add some metadata to stamp extra information onto an activity if needed, that'll be ignored otherwise. - /// GenericSavedData m_SavedValues; private: - /// /// Shared method to get the amount of human or AI controlled brains that are left in this Activity. - /// - /// Whether to get brain count for Human or for AI. True for Human. - /// How many human or AI controlled brains are left in this Activity. + /// @param getForHuman Whether to get brain count for Human or for AI. True for Human. + /// @return How many human or AI controlled brains are left in this Activity. int GetBrainCount(bool getForHuman) const; - /// /// Shared method to force the Activity to focus player control to the previous or next Actor of a specific team, other than the current one focused on. - /// - /// Whether to switch to the previous or the next Actor. True for next Actor. - /// Player to force for. - /// Which team to switch to next Actor on. - /// An Actor pointer to skip in the sequence. + /// @param nextActor Whether to switch to the previous or the next Actor. True for next Actor. + /// @param player Player to force for. + /// @param team Which team to switch to next Actor on. + /// @param skip An Actor pointer to skip in the sequence. void SwitchToPrevOrNextActor(bool nextActor, int player, int team, const Actor* skip = 0); - /// /// Clears all the member variables of this Activity, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Actor.cpp b/Source/Entities/Actor.cpp index 98cdc914b6..1b39b54bc5 100644 --- a/Source/Entities/Actor.cpp +++ b/Source/Entities/Actor.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Actor.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the Actor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Actor.h" #include "UInputMan.h" @@ -50,17 +38,6 @@ namespace RTE { #define ARROWTIME 1000 - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: LuaBindRegister - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registration function for exposing this' members to a LuaBind module. - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Actor, effectively - // resetting the members of this abstraction level only. - void Actor::Clear() { m_Controller.Reset(); m_PlayerControllable = true; @@ -137,11 +114,6 @@ namespace RTE { m_PieMenu.reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Actor object ready for use. - int Actor::Create() { if (MOSRotating::Create() < 0) { return -1; @@ -179,11 +151,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Actor to be identical to another, by deep copy. - int Actor::Create(const Actor& reference) { MOSRotating::Create(reference); @@ -306,14 +273,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int Actor::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MOSRotating::ReadProperty(propName, reader)); @@ -403,12 +362,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this Actor with a Writer for - // later recreation with Create(Reader &reader); - int Actor::Save(Writer& writer) const { MOSRotating::Save(writer); @@ -486,11 +439,6 @@ namespace RTE { MOSRotating::DestroyScriptState(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the Actor object. - void Actor::Destroy(bool notInherited) { delete m_DeviceSwitchSound; delete m_BodyHitSound; @@ -509,8 +457,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Actor::GetInventoryMass() const { float inventoryMass = 0.0F; for (const MovableObject* inventoryItem: m_Inventory) { @@ -519,8 +465,6 @@ namespace RTE { return inventoryMass; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Actor::GetBaseMass() { if (m_BaseMass == std::numeric_limits::infinity()) { if (const Actor* presetActor = static_cast(GetPreset())) { @@ -533,23 +477,10 @@ namespace RTE { return m_BaseMass; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsPlayerControlled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a player is currently controlling this. - bool Actor::IsPlayerControlled() const { return m_Controller.GetInputMode() == Controller::CIM_PLAYER && m_Controller.GetPlayer() >= 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total liquidation value of this Actor and all its carried - // gold and inventory. - float Actor::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const { float totalValue = (GetGoldValue(nativeModule, foreignMult, nativeMult) / 2) + ((GetGoldValue(nativeModule, foreignMult, nativeMult) / 2) * (GetHealth() / GetMaxHealth())); totalValue += GetGoldCarried(); @@ -564,12 +495,6 @@ namespace RTE { return totalValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this carries a specifically named object in its inventory. - // Also looks through the inventories of potential passengers, as applicable. - bool Actor::HasObject(std::string objectName) const { if (MOSRotating::HasObject(objectName)) return true; @@ -582,13 +507,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObjectInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically grouped object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - bool Actor::HasObjectInGroup(std::string groupName) const { if (MOSRotating::HasObjectInGroup(groupName)) return true; @@ -601,11 +519,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this Actor belongs to. - void Actor::SetTeam(int team) { MovableObject::SetTeam(team); @@ -623,11 +536,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetControllerMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this Actor's new Controller input mode. - void Actor::SetControllerMode(Controller::InputMode newMode, int newPlayer) { Controller::InputMode previousControllerMode = m_Controller.GetInputMode(); @@ -641,22 +549,12 @@ namespace RTE { m_NewControlTmr.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SwapControllerModes - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this Actor's Controller mode and gives back what it used to be. - Controller::InputMode Actor::SwapControllerModes(Controller::InputMode newMode, int newPlayer) { Controller::InputMode returnMode = m_Controller.GetInputMode(); SetControllerMode(newMode, newPlayer); return returnMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Look - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an unseen-revealing ray in the direction of where this is facing. - bool Actor::Look(float FOVSpread, float range) { if (!g_SceneMan.AnythingUnseen(m_Team) || m_CanRevealUnseen == false) return false; @@ -693,8 +591,6 @@ namespace RTE { return g_SceneMan.CastSeeRay(m_Team, aimPos, lookVector, ignored, 25, g_SceneMan.GetUnseenResolution(m_Team).GetSmallest() / 2); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Actor::AddGold(float goldOz) { bool isHumanTeam = g_ActivityMan.GetActivity()->IsHumanTeam(m_Team); if (g_SettingsMan.GetAutomaticGoldDeposit() || !isHumanTeam) { @@ -713,8 +609,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Actor::RestDetection() { MOSRotating::RestDetection(); @@ -726,22 +620,11 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: AddAIMOWaypoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an MO in the scene as the next waypoint for this to go to, in order - void Actor::AddAIMOWaypoint(const MovableObject* pMOWaypoint) { if (g_MovableMan.ValidMO(pMOWaypoint)) m_Waypoints.push_back(std::pair(pMOWaypoint->GetPos(), pMOWaypoint)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SwapNextInventory - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Swaps the next MovableObject carried by this Actor and puts one not - // currently carried into the into the back of the inventory of this. - MovableObject* Actor::SwapNextInventory(MovableObject* pSwapIn, bool muteSound) { MovableObject* pRetDev = 0; bool playSound = false; @@ -764,8 +647,6 @@ namespace RTE { return pRetDev; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Actor::RemoveInventoryItem(const std::string& moduleName, const std::string& presetName) { for (std::deque::iterator inventoryIterator = m_Inventory.begin(); inventoryIterator != m_Inventory.end(); ++inventoryIterator) { if ((moduleName.empty() || (*inventoryIterator)->GetModuleName() == moduleName) && (*inventoryIterator)->GetPresetName() == presetName) { @@ -777,8 +658,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MovableObject* Actor::RemoveInventoryItemAtIndex(int inventoryIndex) { if (inventoryIndex >= 0 && inventoryIndex < m_Inventory.size()) { MovableObject* itemAtIndex = m_Inventory[inventoryIndex]; @@ -788,14 +667,6 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SwapPrevInventory - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Swaps the prev MovableObject carried by this Actor and puts one not - // currently carried into the into the back of the inventory of this. - MovableObject* Actor::SwapPrevInventory(MovableObject* pSwapIn) { MovableObject* pRetDev = 0; bool playSound = false; @@ -816,8 +687,6 @@ namespace RTE { return pRetDev; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Actor::SwapInventoryItemsByIndex(int inventoryIndex1, int inventoryIndex2) { if (inventoryIndex1 < 0 || inventoryIndex2 < 0 || inventoryIndex1 >= m_Inventory.size() || inventoryIndex2 >= m_Inventory.size()) { return false; @@ -827,8 +696,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MovableObject* Actor::SetInventoryItemAtIndex(MovableObject* newInventoryItem, int inventoryIndex) { if (!newInventoryItem) { return RemoveInventoryItemAtIndex(inventoryIndex); @@ -844,14 +711,6 @@ namespace RTE { return currentInventoryItemAtIndex; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DropAllInventory - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Ejects all inventory items that this is carrying. It may not happen - // instantaneously, so check for ejection being complete with InventoryEmpty(). - void Actor::DropAllInventory() { MovableObject* pObject = 0; Actor* pPassenger = 0; @@ -921,8 +780,6 @@ namespace RTE { m_Inventory.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - void Actor::DropAllGold() { const Material* goldMaterial = g_SceneMan.GetMaterialFromID(g_MaterialGold); float velMin = 3.0F; @@ -944,8 +801,6 @@ namespace RTE { m_GoldCarried = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - bool Actor::AddToInventoryFront(MovableObject* itemToAdd) { // This function is called often to add stuff we just removed from our hands, which may be set to delete so we need to guard against that lest we crash. if (!itemToAdd || itemToAdd->IsSetToDelete()) { @@ -956,8 +811,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - bool Actor::AddToInventoryBack(MovableObject* itemToAdd) { // This function is called often to add stuff we just removed from our hands, which may be set to delete so we need to guard against that lest we crash. if (!itemToAdd || itemToAdd->IsSetToDelete()) { @@ -968,12 +821,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GibThis - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gibs this, effectively destroying it and creating multiple gibs or - // pieces in its place. - void Actor::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { // Play death sound // TODO: Don't attenuate since death is pretty important.. maybe only make this happen for teh brains @@ -1067,13 +914,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - bool Actor::CollideAtPoint(HitData& hd) { return MOSRotating::CollideAtPoint(hd); @@ -1088,14 +928,6 @@ namespace RTE { // if (Status != ACTIVE) } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ParticlePenetration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Determines whether a particle which has hit this MO will penetrate, - // and if so, whether it gets lodged or exits on the other side of this - // MO. Appropriate effects will be determined and applied ONLY IF there - // was penetration! If not, nothing will be affected. - bool Actor::ParticlePenetration(HitData& hd) { bool penetrated = MOSRotating::ParticlePenetration(hd); @@ -1123,22 +955,10 @@ namespace RTE { return penetrated; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAIModeIcon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the icon bitmap associated with this' current AI mode and team. - BITMAP* Actor::GetAIModeIcon() { return m_apAIIcons[m_AIMode]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetLastMOWaypointID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the ID of the last set AI MO waypoint of this. If none, g_NoMOID is returned. - // Arguments: None. - // Return value: The furthest set AI MO waypoint of this. - MOID Actor::GetAIMOWaypointID() const { if (g_MovableMan.ValidMO(m_pMOMoveTarget)) return m_pMOMoveTarget->GetID(); @@ -1146,11 +966,6 @@ namespace RTE { return g_NoMOID; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateMovePath - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the path to move along to the currently set movetarget. - void Actor::UpdateMovePath() { if (g_SceneMan.GetScene() == nullptr) { return; @@ -1194,17 +1009,10 @@ namespace RTE { m_UpdateMovePath = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Actor::EstimateDigStrength() const { return m_AIBaseDigStrength; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: VerifyMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Verifieis whether all actor's MO has correct IDs. Should be used in Debug mode only. - void Actor::VerifyMOIDs() { std::vector MOIDs; GetMOIDs(MOIDs); @@ -1214,8 +1022,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void Actor::OnNewMovePath() { if (!m_MovePath.empty()) { // Remove the first one; it's our position @@ -1235,8 +1041,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void Actor::PreControllerUpdate() { if (m_PathRequest && m_PathRequest->complete) { m_MovePath = const_cast&>(m_PathRequest->path); @@ -1251,8 +1055,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void Actor::Update() { ZoneScoped; @@ -1468,12 +1270,6 @@ namespace RTE { Update(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Actor's current graphical HUD overlay representation to a - // BITMAP of choice. - void Actor::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { // This should indeed be a local var and not alter a member one in a draw func! Can cause nasty jittering etc if multiple sim updates are done without a drawing in between etc m_HUDStack = -m_CharHeight / 2; diff --git a/Source/Entities/Actor.h b/Source/Entities/Actor.h index e12b276318..8394ea5056 100644 --- a/Source/Entities/Actor.h +++ b/Source/Entities/Actor.h @@ -1,18 +1,11 @@ #ifndef _RTEACTOR_ #define _RTEACTOR_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Actor.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the Actor class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the Actor class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "MOSRotating.h" #include "PieMenu.h" #include "PathFinder.h" @@ -29,19 +22,11 @@ namespace RTE { #define AILINEDOTSPACING 16 - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: Actor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A sprite movable object that is autonomous. - // Parent(s): MOSRotating. - // Class history: 04/13/2001 Actor created. - + /// A sprite movable object that is autonomous. class Actor : public MOSRotating { friend struct EntityLuaBindings; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: enum Status { STABLE = 0, @@ -88,371 +73,203 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Actor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Actor object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a Actor object in system + /// memory. Create() should be called before using the object. Actor() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~Actor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a Actor object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a Actor object before deletion + /// from system memory. ~Actor() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Actor object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the Actor object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Actor to be identical to another, by deep copy. - // Arguments: A reference to the Actor to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a Actor to be identical to another, by deep copy. + /// @param reference A reference to the Actor to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const Actor& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Actor, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire Actor, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); MOSRotating::Reset(); m_MOType = MovableObject::TypeActor; } - /// /// Cleans up and destroys the script state of this object, calling the Destroy callback in lua - /// void DestroyScriptState(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Gets the mass of this Actor's inventory. Does not include any equipped item (for actor subtypes that have that). - /// - /// The mass of this Actor's inventory. + /// @return The mass of this Actor's inventory. float GetInventoryMass() const; - /// /// Gets the mass of this Actor, including the mass of its Attachables, wounds and inventory. - /// - /// The mass of this Actor, its inventory and all its Attachables and wounds in Kilograms (kg). + /// @return The mass of this Actor, its inventory and all its Attachables and wounds in Kilograms (kg). float GetMass() const override { return MOSRotating::GetMass() + GetInventoryMass() + (m_GoldCarried * g_SceneMan.GetKgPerOz()); } - /// /// Gets the mass that this actor had upon spawning, i.e with ini-defined inventory, gold and holding no items - /// - /// The base mass of this Actor, in Kilograms (kg). + /// @return The base mass of this Actor, in Kilograms (kg). float GetBaseMass(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetController - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets this Actor's Controller. Ownership IS NOT transferred! - // Arguments: None. - // Return value: A const pointer to this Actor's Controller. - + /// Gets this Actor's Controller. Ownership IS NOT transferred! + /// @return A const pointer to this Actor's Controller. Controller* GetController() { return &m_Controller; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsPlayerControlled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a player is currently controlling this. - // Arguments: None. - // Return value: Whether a player is controlling this. - + /// Tells whether a player is currently controlling this. + /// @return Whether a player is controlling this. bool IsPlayerControlled() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsControllable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells wheter the player can switch control to this at all - // Arguments: None. - // Return value: Whether a player can control this at all. - + /// Tells wheter the player can switch control to this at all + /// @return Whether a player can control this at all. virtual bool IsControllable() const { return true; } - /// /// Gets whether or not this Actor can be controlled by human players. Note that this does not protect the Actor's Controller from having its input mode forced to CIM_PLAYER (e.g. via Lua). - /// - /// Whether or not this Actor can be controlled by human players. + /// @return Whether or not this Actor can be controlled by human players. bool IsPlayerControllable() const { return m_PlayerControllable; } - /// /// Sets whether or not this Actor can be controlled by human players. - /// - /// Whether or not this Actor should be able to be controlled by human players. + /// @param playerControllable Whether or not this Actor should be able to be controlled by human players. void SetPlayerControllable(bool playerControllable) { m_PlayerControllable = playerControllable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetStatus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the current Status of this. - // Arguments: None. - // Return value: The status. - + /// Returns the current Status of this. + /// @return The status. int GetStatus() const { return m_Status; } - /// /// Gets this Actor's health value. - /// - /// A float describing this Actor's health. + /// @return A float describing this Actor's health. float GetHealth() const { return m_Health; } - /// /// Gets this Actor's previous health value, prior to this frame. - /// - /// A float describing this Actor's previous health. + /// @return A float describing this Actor's previous health. float GetPrevHealth() const { return m_PrevHealth; } - /// /// Gets this Actor's maximum health value. - /// - /// A float describing this Actor's max health. + /// @return A float describing this Actor's max health. float GetMaxHealth() const { return m_MaxHealth; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMaxHealth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets this Actor's maximum health value. - // Arguments: New max health value. - // Return value: None. - + /// Gets this Actor's maximum health value. + /// @param newValue New max health value. void SetMaxHealth(int newValue) { m_MaxHealth = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAimDistance - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the distance between the actor and the view point when not - // sharp aiming. - // Arguments: None. - // Return value: A const int describing how far this actor aims/looks by default. - + /// Gets the distance between the actor and the view point when not + /// sharp aiming. + /// @return A const int describing how far this actor aims/looks by default. int GetAimDistance() const { return m_AimDistance; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetAimDistance - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the distance between the actor and the view point when not - // sharp aiming. - // Arguments: None. - // Return value: A const int describing how far this actor aims/looks by default. - + /// Gets the distance between the actor and the view point when not + /// sharp aiming. + /// @return A const int describing how far this actor aims/looks by default. void SetAimDistance(int newValue) { m_AimDistance = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldCarried - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how many ounces of gold this Actor is carrying. - // Arguments: None. - // Return value: The current amount of carried gold, in Oz. - + /// Gets how many ounces of gold this Actor is carrying. + /// @return The current amount of carried gold, in Oz. float GetGoldCarried() const { return m_GoldCarried; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total liquidation value of this Actor and all its carried - // gold and inventory. - // Arguments: If this is supposed to be adjusted for a specific Tech's subjective - // value, then pass in the native DataModule ID of that tech. 0 means - // no Tech is specified and the base value is returned. - // How much to multiply the value if this happens to be a foreign Tech. - // Return value: The current value of this Actor and all his carried assets. - + /// Gets the total liquidation value of this Actor and all its carried + /// gold and inventory. + /// @param nativeModule If this is supposed to be adjusted for a specific Tech's subjective (default: 0) + /// value, then pass in the native DataModule ID of that tech. 0 means + /// no Tech is specified and the base value is returned. + /// @param foreignMult How much to multiply the value if this happens to be a foreign Tech. (default: 1.0) + /// @return The current value of this Actor and all his carried assets. float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically named object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - // Arguments: The Preset name of the object to look for. - // Return value: Whetehr the object was found carried by this. - + /// Shows whether this is or carries a specifically named object in its + /// inventory. Also looks through the inventories of potential passengers, + /// as applicable. + /// @param objectName The Preset name of the object to look for. + /// @return Whetehr the object was found carried by this. bool HasObject(std::string objectName) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObjectInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically grouped object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - // Arguments: The name of the group to look for. - // Return value: Whetehr the object in the group was found carried by this. - + /// Shows whether this is or carries a specifically grouped object in its + /// inventory. Also looks through the inventories of potential passengers, + /// as applicable. + /// @param groupName The name of the group to look for. + /// @return Whetehr the object in the group was found carried by this. bool HasObjectInGroup(std::string groupName) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAimAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets this Actor's aim angle. - // Arguments: Whether to adjust the angle for flipping or not. - // Return value: The angle, in radians. - + /// Gets this Actor's aim angle. + /// @param adjustForFlipped Whether to adjust the angle for flipping or not. (default: true) + /// @return The angle, in radians. float GetAimAngle(bool adjustForFlipped = true) const { return adjustForFlipped ? FacingAngle(m_AimAngle) : m_AimAngle; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPassengerSlots - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets this Actor's passenger slots. - // Arguments: None. - // Return value: The Actor's passenger plots - + /// Gets this Actor's passenger slots. + /// @return The Actor's passenger plots int GetPassengerSlots() const { return m_PassengerSlots; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetCPUPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of this' brain, or equivalent. - // Arguments: None. - // Return value: A Vector with the absolute position of this' brain. - + /// Gets the absoltue position of this' brain, or equivalent. + /// @return A Vector with the absolute position of this' brain. virtual Vector GetCPUPos() const { return m_Pos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetEyePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of this' eye, or equivalent, where look - // vector starts from. - // Arguments: None. - // Return value: A Vector with the absolute position of this' eye or view point. - + /// Gets the absoltue position of this' eye, or equivalent, where look + /// vector starts from. + /// @return A Vector with the absolute position of this' eye or view point. virtual Vector GetEyePos() const { return m_Pos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAboveHUDPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of the top of this' HUD stack. - // Arguments: None. - // Return value: A Vector with the absolute position of this' HUD stack top point. - + /// Gets the absoltue position of the top of this' HUD stack. + /// @return A Vector with the absolute position of this' HUD stack top point. Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, m_HUDStack + 6); } - /// /// Gets the offset position of the holster where this Actor draws his devices from. - /// - /// The offset position of the holster. + /// @return The offset position of the holster. Vector GetHolsterOffset() const { return m_HolsterOffset; } - /// /// Sets the offset position of the holster where this Actor draws his devices from. - /// - /// A new holster offset. + /// @param newOffset A new holster offset. void SetHolsterOffset(Vector newOffset) { m_HolsterOffset = newOffset; } - /// /// Gets the offset position of where this Actor reloads his devices from. - /// - /// The offset position of the where this Actor reloads his devices from. + /// @return The offset position of the where this Actor reloads his devices from. Vector GetReloadOffset() const { return m_ReloadOffset; } - /// /// Sets the offset position of the where this Actor reloads his devices from. - /// - /// The new offset position of where this Actor reloads his devices from. + /// @param newOffset The new offset position of where this Actor reloads his devices from. void SetReloadOffset(Vector newOffset) { m_ReloadOffset = newOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetViewPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the point at which this actor is viewing, or that the scene frame - // should be centered on if tracking this Actor's view. In absolute scene - // coordinates. - // Arguments: None. - // Return value: The point in absolute scene coordinates. - + /// Gets the point at which this actor is viewing, or that the scene frame + /// should be centered on if tracking this Actor's view. In absolute scene + /// coordinates. + /// @return The point in absolute scene coordinates. Vector GetViewPoint() const { return m_ViewPoint.IsZero() ? m_Pos : m_ViewPoint; } - /// /// Gets the item that is within reach of the Actor at this frame, ready to be be picked up. Ownership is NOT transferred! - /// - /// A pointer to the item that has been determined to be within reach of this Actor, if any. + /// @return A pointer to the item that has been determined to be within reach of this Actor, if any. HeldDevice* GetItemInReach() const { return m_pItemInReach; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetLookVector - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the direction where this is looking/aiming. - // Arguments: None. - // Return value: A Vector with the direction in which this is looking along. - + /// Gets the direction where this is looking/aiming. + /// @return A Vector with the direction in which this is looking along. Vector GetLookVector() const { return m_ViewPoint - GetEyePos(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSharpAimProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the normalized amount of sharp aim that has been achieved by this. - // Arguments: None. - // Return value: Sharp aim progress between 0 - 1.0. 1.0 is fully aimed. - + /// Gets the normalized amount of sharp aim that has been achieved by this. + /// @return Sharp aim progress between 0 - 1.0. 1.0 is fully aimed. float GetSharpAimProgress() const { return m_SharpAimProgress; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the approximate height of this Actor, standing up. - // Arguments: None. - // Return value: A float with the approximate height, in pixels. - + /// Gets the approximate height of this Actor, standing up. + /// @return A float with the approximate height, in pixels. float GetHeight() const { return m_CharHeight; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetControllerMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this Actor's new Controller input mode. - // Arguments: The new input mode. - // The player which will control this if the input mode was set to player. - // Return value: None. - + /// Sets this Actor's new Controller input mode. + /// @param newMode The new input mode. + /// @param newPlayer The player which will control this if the input mode was set to player. (default: -1) void SetControllerMode(Controller::InputMode newMode, int newPlayer = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SwapControllerModes - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this Actor's Controller mode and gives back what it used to be. - // Arguments: The new mode to set to. - // The player which will control this if the input mode was set to player. - // Return value: The old mode that it had before. - + /// Sets this Actor's Controller mode and gives back what it used to be. + /// @param newMode The new mode to set to. + /// @param newPlayer The player which will control this if the input mode was set to player. (default: -1) + /// @return The old mode that it had before. Controller::InputMode SwapControllerModes(Controller::InputMode newMode, int newPlayer = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetStatus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this Actor's status. - // Arguments: A Status enumeration. - // Return value: None. - + /// Sets this Actor's status. + /// @param newStatus A Status enumeration. void SetStatus(Actor::Status newStatus) { m_Status = newStatus; if (newStatus == Actor::Status::UNSTABLE) { @@ -461,206 +278,112 @@ namespace RTE { } /// Gets this Actor's MovementState. - /// - /// This Actor's MovementState. + /// @return This Actor's MovementState. MovementState GetMovementState() const { return m_MoveState; } - /// /// Sets this Actor's MovementState to the new state. - /// - /// This Actor's new MovementState. + /// @param newMovementState This Actor's new MovementState. void SetMovementState(MovementState newMovementState) { m_MoveState = newMovementState; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this Actor belongs to. - // Arguments: The assigned team number. - // Return value: None. - + /// Sets which team this Actor belongs to. + /// @param team The assigned team number. void SetTeam(int team) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetGoldCarried - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets how many ounces of gold this Actor is carrying. - // Arguments: The new amount of carried gold, in Oz. - // Return value: None. - + /// Sets how many ounces of gold this Actor is carrying. + /// @param goldOz The new amount of carried gold, in Oz. void SetGoldCarried(float goldOz) { m_GoldCarried = goldOz; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetAimAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this Actor's aim angle. - // Arguments: A new angle, in radians. - // Return value: None. - + /// Sets this Actor's aim angle. + /// @param newAngle A new angle, in radians. void SetAimAngle(float newAngle) { m_AimAngle = newAngle; Clamp(m_AimAngle, m_AimRange, -m_AimRange); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPassengerSlots - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this Actor's passenger slots. - // Arguments: A new amount of passenger slots. - // Return value: None. - + /// Sets this Actor's passenger slots. + /// @param newPassengerSlots A new amount of passenger slots. void SetPassengerSlots(int newPassengerSlots) { m_PassengerSlots = newPassengerSlots; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetViewPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the point at which this actor is viewing, or that the scene frame - // should be centered on if tracking this Actor's view. In absolute scene - // coordinates. - // Arguments: A new point in absolute scene coords. - // Return value: None. - + /// Sets the point at which this actor is viewing, or that the scene frame + /// should be centered on if tracking this Actor's view. In absolute scene + /// coordinates. + /// @param newPoint A new point in absolute scene coords. void SetViewPoint(Vector newPoint) { m_ViewPoint = newPoint; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetItemInReach - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the item that is within reach of the Actor at this frame, so that - // it may be picked up. Ownership is NOT transferred! - // Arguments: A pointer to the item that has been determined to be within reach of - // this Actor. Ownership is NOT transferred! - // Return value: None. - + /// Sets the item that is within reach of the Actor at this frame, so that + /// it may be picked up. Ownership is NOT transferred! + /// @param pItem A pointer to the item that has been determined to be within reach of + /// this Actor. Ownership is NOT transferred! void SetItemInReach(HeldDevice* pItem) { m_pItemInReach = pItem; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsWithinRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a point on the scene is within range of the currently - // used device and aiming status, if applicable. - // Arguments: A Vector witht he aboslute coordinates of a point to check. - // Return value: Whether the point is within range of this. - + /// Tells whether a point on the scene is within range of the currently + /// used device and aiming status, if applicable. + /// @param point A Vector witht he aboslute coordinates of a point to check. + /// @return Whether the point is within range of this. virtual bool IsWithinRange(Vector& point) const { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Look - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Casts an unseen-revealing ray in the direction of where this is facing. - // Arguments: The degree angle to deviate from the current view point in the ray - // casting. A random ray will be chosen out of this +-range. - // The range, in pixels, that the ray will have. - // Return value: Whether any unseen pixels were revealed by this look. - + /// Casts an unseen-revealing ray in the direction of where this is facing. + /// @param FOVSpread The degree angle to deviate from the current view point in the ray + /// casting. A random ray will be chosen out of this +-range. + /// @param range The range, in pixels, that the ray will have. + /// @return Whether any unseen pixels were revealed by this look. virtual bool Look(float FOVSpread, float range); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddGold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a certain amount of ounces of gold to this' team's total funds. - // Arguments: The amount in Oz with which to change this' team's gold tally. - // Return value: None. - + /// Adds a certain amount of ounces of gold to this' team's total funds. + /// @param goldOz The amount in Oz with which to change this' team's gold tally. void AddGold(float goldOz); - /// /// Does the calculations necessary to detect whether this Actor is at rest or not. IsAtRest() retrieves the answer. - /// void RestDetection() override; - /// /// Adds health points to this Actor's current health value. - /// - /// A float specifying the value to add. - /// The resulting total health of this Actor. + /// @param setHealth A float specifying the value to add. + /// @return The resulting total health of this Actor. const float AddHealth(const float addedHealth) { return m_Health += addedHealth; } - /// /// Sets this Actor's current health value. - /// - /// A float specifying the value to set to. + /// @param setHealth A float specifying the value to set to. void SetHealth(const float setHealth) { m_Health = setHealth; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsStatus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if this Actor is in a specific status. - // Arguments: Which status to check for. - // Return value: A bool with the answer. - + /// Checks if this Actor is in a specific status. + /// @param which Which status to check for. + /// @return A bool with the answer. bool IsStatus(Status which) const { return m_Status == which; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsDead - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if this Actor is dead. - // Arguments: None. - // Return value: A const bool with the answer. - + /// Checks if this Actor is dead. + /// @return A const bool with the answer. bool IsDead() const { return m_Status == DEAD; } - /// /// Tries to handle the activated PieSlice in this object's PieMenu, if there is one, based on its SliceType. - /// - /// The SliceType of the PieSlice being handled. - /// Whether or not the activated PieSlice SliceType was able to be handled. + /// @param pieSliceType The SliceType of the PieSlice being handled. + /// @return Whether or not the activated PieSlice SliceType was able to be handled. virtual bool HandlePieCommand(PieSlice::SliceType pieSliceType) { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAIMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets this' AI mode. - // Arguments: None. - // Return value: The current AI mode. - + /// Gets this' AI mode. + /// @return The current AI mode. int GetAIMode() const { return m_AIMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAIModeIcon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the icon bitmap associated with this' current AI mode and team. - // Arguments: None. - // Return value: The current AI mode icon of this. Ownership is NOT transferred! - + /// Gets the icon bitmap associated with this' current AI mode and team. + /// @return The current AI mode icon of this. Ownership is NOT transferred! BITMAP* GetAIModeIcon(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetAIMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this' AI mode. - // Arguments: The new AI mode. - // Return value: None. - + /// Sets this' AI mode. + /// @param newMode The new AI mode. (default: AIMODE_SENTRY) void SetAIMode(AIMode newMode = AIMODE_SENTRY) { m_AIMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: AddAISceneWaypoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an absolute scene point to the list of waypoints this is going to - // go to, in order - // Arguments: The new scene point this should try to get to after all other waypoints - // are reached. - // Return value: None. - + /// Adds an absolute scene point to the list of waypoints this is going to + /// go to, in order + /// @param m_Waypoints.push_back(std::pair(waypoint, (MovableObject*)NULL)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: AddAIMOWaypoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an MO in the scene as the next waypoint for this to go to, in order - // Arguments: The new MO this should try to get to after all other waypoints are reached. - // OWNERSHIP IS NOT TRANSFERRED! - // Return value: None. - + /// Adds an MO in the scene as the next waypoint for this to go to, in order + /// @param pMOWaypoint The new MO this should try to get to after all other waypoints are reached. + /// OWNERSHIP IS NOT TRANSFERRED! void AddAIMOWaypoint(const MovableObject* pMOWaypoint); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ClearAIWaypoints - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes all AI waypoints and clears the current path to the current - // waypoint. The AI Actor will stop in its tracks. - // Arguments: None. - // Return value: None. - + /// Removes all AI waypoints and clears the current path to the current + /// waypoint. The AI Actor will stop in its tracks. void ClearAIWaypoints() { m_pMOMoveTarget = 0; m_Waypoints.clear(); @@ -669,14 +392,9 @@ namespace RTE { m_MoveVector.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetLastAIWaypoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the last or furthest set AI waypoint of this. If none, this' pos - // is returned. - // Arguments: None. - // Return value: The furthest set AI waypoint of this. - + /// Gets the last or furthest set AI waypoint of this. If none, this' pos + /// is returned. + /// @return The furthest set AI waypoint of this. Vector GetLastAIWaypoint() const { if (!m_Waypoints.empty()) { return m_Waypoints.back().first; @@ -686,83 +404,48 @@ namespace RTE { return m_Pos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetLastMOWaypointID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the ID of the last set AI MO waypoint of this. If none, g_NoMOID is returned. - // Arguments: None. - // Return value: The furthest set AI MO waypoint of this. - + /// Gets the ID of the last set AI MO waypoint of this. If none, g_NoMOID is returned. + /// @return The furthest set AI MO waypoint of this. MOID GetAIMOWaypointID() const; - /// /// Gets the list of waypoints for this Actor. - /// - /// The list of waypoints for this Actor. + /// @return The list of waypoints for this Actor. const std::list>& GetWaypointList() const { return m_Waypoints; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetWaypointsSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how many waypoints this actor have. - // Arguments: None. - // Return value: How many waypoints. - + /// Gets how many waypoints this actor have. + /// @return How many waypoints. int GetWaypointsSize() { return m_Waypoints.size(); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ClearMovePath - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the list of coordinates in this' current MovePath, ie the path - // to the next Waypoint. - // Arguments: None. - // Return value: None. - + /// Clears the list of coordinates in this' current MovePath, ie the path + /// to the next Waypoint. void ClearMovePath() { m_MovePath.clear(); m_MoveTarget = m_Pos; m_MoveVector.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: AddToMovePathBeginning - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a coordinate to the beginning of the MovePath, meaning the one - // closest to this Actor. - // Arguments: The new coordinate to add to the front of the MovePath. - // Return value: None. - + /// Adds a coordinate to the beginning of the MovePath, meaning the one + /// closest to this Actor. + /// @param newCoordinate The new coordinate to add to the front of the MovePath. void AddToMovePathBeginning(Vector newCoordinate) { m_MovePath.push_front(newCoordinate); m_MoveTarget = newCoordinate; m_MoveVector.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: AddToMovePathEnd - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a coordinate to the end of the MovePath, meaning the one - // closest to this Actor's next waypoint. - // Arguments: The new coordinate to add to the end of the MovePath. - // Return value: None. - + /// Adds a coordinate to the end of the MovePath, meaning the one + /// closest to this Actor's next waypoint. + /// @param m_MovePath.push_back(newCoordinate The new coordinate to add to the end of the MovePath. void AddToMovePathEnd(Vector newCoordinate) { m_MovePath.push_back(newCoordinate); } - /// /// Gets the last position in this Actor's move path. - /// - /// The last position in this Actor's move path. + /// @return The last position in this Actor's move path. Vector GetMovePathEnd() const { return m_MovePath.back(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveMovePathBeginning - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a coordinate from the beginning of the MovePath, meaning the - // one closest to this Actor. - // Arguments: None. - // Return value: Whether there was any coordinate to remove. If false, the MovePath - // is empty. - + /// Removes a coordinate from the beginning of the MovePath, meaning the + /// one closest to this Actor. + /// @return Whether there was any coordinate to remove. If false, the MovePath + /// is empty. bool RemoveMovePathBeginning() { if (!m_MovePath.empty()) { m_MovePath.pop_front(); @@ -773,15 +456,10 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: RemoveMovePathEnd - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a coordinate from the end of the MovePath, meaning the - // one farthest from this Actor. - // Arguments: None. - // Return value: Whether there was any coordinate to remove. If false, the MovePath - // is empty. - + /// Removes a coordinate from the end of the MovePath, meaning the + /// one farthest from this Actor. + /// @return Whether there was any coordinate to remove. If false, the MovePath + /// is empty. bool RemoveMovePathEnd() { if (!m_MovePath.empty()) { m_MovePath.pop_back(); @@ -790,69 +468,38 @@ namespace RTE { return false; } - /// /// Gets a pointer to the MovableObject move target of this Actor. - /// - /// A pointer to the MovableObject move target of this Actor. + /// @return A pointer to the MovableObject move target of this Actor. const MovableObject* GetMOMoveTarget() const { return m_pMOMoveTarget; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPerceptiveness - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this' perceptiveness to alarming events going on around him. - // Arguments: The current perceptiveness, 0.0 - 1.0 - // Return value: None. - + /// Sets this' perceptiveness to alarming events going on around him. + /// @param newPerceptiveness The current perceptiveness, 0.0 - 1.0 void SetPerceptiveness(float newPerceptiveness) { m_Perceptiveness = newPerceptiveness; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPerceptiveness - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets this' perceptiveness to alarming events going on around him. - // Arguments: None. - // Return value: The current perceptiveness, 0.0 - 1.0 - + /// Gets this' perceptiveness to alarming events going on around him. + /// @return The current perceptiveness, 0.0 - 1.0 float GetPerceptiveness() const { return m_Perceptiveness; } - /// /// Gets whether this actor is able to reveal unseen areas by looking. - /// - /// Whether this actor can reveal unseen areas. + /// @return Whether this actor can reveal unseen areas. bool GetCanRevealUnseen() const { return m_CanRevealUnseen; } - /// /// Sets whether this actor can reveal unseen areas by looking. - /// - /// Whether this actor can reveal unseen areas. + /// @param newCanRevealUnseen Whether this actor can reveal unseen areas. void SetCanRevealUnseen(bool newCanRevealUnseen) { m_CanRevealUnseen = newCanRevealUnseen; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPainThreshold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this' PainThreshold value above which it will play PainSound - // Arguments: Desired PainThreshold value - // Return value: None. - + /// Sets this' PainThreshold value above which it will play PainSound + /// @param newPainThreshold Desired PainThreshold value void SetPainThreshold(float newPainThreshold) { m_PainThreshold = newPainThreshold; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPainThreshold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets this' PainThreshold value above which it will play PainSound - // Arguments: None. - // Return value: The current PainThreshold - + /// Gets this' PainThreshold value above which it will play PainSound + /// @return The current PainThreshold float GetPainThreshold() const { return m_PainThreshold; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AlarmPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes this alarmed about a certian point on in the scene, overriding - // the current AI mode until a certain time has passed. - // Arguments: The new scene point this should look at and see if anything dangerous - // is there. - // Return value: None. - + /// Makes this alarmed about a certian point on in the scene, overriding + /// the current AI mode until a certain time has passed. + /// @param alarmPoint The new scene point this should look at and see if anything dangerous + /// is there. void AlarmPoint(const Vector& alarmPoint) { if (m_AlarmSound && m_AlarmTimer.IsPastSimTimeLimit()) { m_AlarmSound->Play(alarmPoint); @@ -863,14 +510,9 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAlarmPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets any point on the scene this actor should be alarmed about this frame. - // Arguments: None. - // Return value: The new scene point this should look at and see if anything dangerous - // is there or (0,0) if nothing is alarming. - + /// Gets any point on the scene this actor should be alarmed about this frame. + /// @return The new scene point this should look at and see if anything dangerous + /// is there or (0,0) if nothing is alarming. Vector GetAlarmPoint() { if (m_AlarmTimer.GetElapsedSimTimeMS() > g_TimerMan.GetDeltaTimeMS()) { return Vector(); @@ -878,528 +520,311 @@ namespace RTE { return m_LastAlarmPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: AddInventoryItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an inventory item to this Actor. - // Arguments: An pointer to the new item to add. Ownership IS TRANSFERRED! - // Return value: None.. - + /// Adds an inventory item to this Actor. + /// @param AddToInventoryBack(pItemToAdd An pointer to the new item to add. Ownership IS TRANSFERRED! + /// @return None.. virtual void AddInventoryItem(MovableObject* pItemToAdd) { AddToInventoryBack(pItemToAdd); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveInventoryItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a specified item from the actor's inventory. Only one item is removed at a time. - // Arguments: Preset name of an item to remove. - // Return value: None. - + /// Removes a specified item from the actor's inventory. Only one item is removed at a time. + /// @param RemoveInventoryItem("" Preset name of an item to remove. void RemoveInventoryItem(const std::string& presetName) { RemoveInventoryItem("", presetName); } - /// /// Removes the first inventory item with the given module name and preset name. - /// - /// The module name of the item to remove. - /// The preset name of the item to remove. + /// @param moduleName The module name of the item to remove. + /// @param presetName The preset name of the item to remove. void RemoveInventoryItem(const std::string& moduleName, const std::string& presetName); - /// /// Removes and returns the inventory item at the given index. Ownership IS transferred. - /// - /// The index of the inventory item to remove. - /// An owning pointer to the removed inventory item. + /// @param inventoryIndex The index of the inventory item to remove. + /// @return An owning pointer to the removed inventory item. MovableObject* RemoveInventoryItemAtIndex(int inventoryIndex); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SwapNextInventory - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Swaps the next MovableObject carried by this Actor and puts one not - // currently carried into the into the back of the inventory of this. - // Arguments: A pointer to the external MovableObject to trade in. Ownership IS xferred! - // If 0 is passed in, nothing will be added to the inventory. - // Whether to mute the sound on this event. Override for the loading screen hack. - // Return value: The next MovableObject in this Actor's inventory. Ownership IS xferred! - // If there are no MovableObject:s in inventory, 0 will be returned. - + /// Swaps the next MovableObject carried by this Actor and puts one not + /// currently carried into the into the back of the inventory of this. + /// @param pSwapIn A pointer to the external MovableObject to trade in. Ownership IS xferred! (default: nullptr) + /// @param muteSound If 0 is passed in, nothing will be added to the inventory. (default: false) + /// Whether to mute the sound on this event. Override for the loading screen hack. + /// @return The next MovableObject in this Actor's inventory. Ownership IS xferred! + /// If there are no MovableObject:s in inventory, 0 will be returned. virtual MovableObject* SwapNextInventory(MovableObject* pSwapIn = nullptr, bool muteSound = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SwapPrevInventory - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Swaps the prev MovableObject carried by this Actor and puts one not - // currently carried into the into the back of the inventory of this. - // Arguments: A pointer to the external MovableObject to trade in. Ownership IS xferred! - // If 0 is passed in, nothing will be added to the inventory. - // Return value: The prev MovableObject in this Actor's inventory. Ownership IS xferred! - // If there are no MovableObject:s in inventory, 0 will be returned. - + /// Swaps the prev MovableObject carried by this Actor and puts one not + /// currently carried into the into the back of the inventory of this. + /// @param pSwapIn A pointer to the external MovableObject to trade in. Ownership IS xferred! (default: nullptr) + /// If 0 is passed in, nothing will be added to the inventory. + /// @return The prev MovableObject in this Actor's inventory. Ownership IS xferred! + /// If there are no MovableObject:s in inventory, 0 will be returned. virtual MovableObject* SwapPrevInventory(MovableObject* pSwapIn = nullptr); - /// /// Swaps the inventory items at the given indices. Will return false if a given index is invalid. - /// - /// The index of one item. - /// The index of the other item. - /// Whether or not the swap was successful. + /// @param inventoryIndex1 The index of one item. + /// @param inventoryIndex2 The index of the other item. + /// @return Whether or not the swap was successful. bool SwapInventoryItemsByIndex(int inventoryIndex1, int inventoryIndex2); - /// /// Sets the inventory item at the given index as the new inventory item, and gives back the one that was originally there. /// If an invalid index is given, the new item will be put in the back of the inventory, and nullptr will be returned. - /// - /// The new item that should be at the given inventory index. Cannot be a nullptr. Ownership IS transferred. - /// The inventory index the new item should be placed at. - /// The inventory item that used to be at the inventory index. Ownership IS transferred. + /// @param newInventoryItem The new item that should be at the given inventory index. Cannot be a nullptr. Ownership IS transferred. + /// @param inventoryIndex The inventory index the new item should be placed at. + /// @return The inventory item that used to be at the inventory index. Ownership IS transferred. MovableObject* SetInventoryItemAtIndex(MovableObject* newInventoryItem, int inventoryIndex); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DropAllInventory - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Ejects all inventory items that this is carrying. It may not happen - // instantaneously, so check for ejection being complete with - // IsInventoryEmpty(). - // Arguments: None. - // Return value: None. - + /// Ejects all inventory items that this is carrying. It may not happen + /// instantaneously, so check for ejection being complete with + /// IsInventoryEmpty(). virtual void DropAllInventory(); - /// /// Converts all of the Gold carried by this Actor into MovableObjects and ejects them into the Scene. - /// virtual void DropAllGold(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetInventorySize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells how many things are in the invetory - // Arguments: None. - // Return value: The number of things in the inventory - + /// Tells how many things are in the invetory + /// @return The number of things in the inventory int GetInventorySize() const { return m_Inventory.size(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsInventoryEmpty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether inventory is completely empty - // Arguments: None. - // Return value: Whether inventory is completely empty. - + /// Tells whether inventory is completely empty + /// @return Whether inventory is completely empty. bool IsInventoryEmpty() { return m_Inventory.empty(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetInventory - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the deque of inventory of this. Ownership is NOT transferred. - // Arguments: None. - // Return value: A const pointer to the inventory deque of this. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the deque of inventory of this. Ownership is NOT transferred. + /// @return A const pointer to the inventory deque of this. OWNERSHIP IS NOT TRANSFERRED! const std::deque* GetInventory() const { return &m_Inventory; } - /// /// Returns the maximum total mass this Actor can carry in its inventory. - /// - /// The maximum carriable mass of this Actor. + /// @return The maximum carriable mass of this Actor. float GetMaxInventoryMass() const { return m_MaxInventoryMass; } - /// /// Attempts to add an item to the front of our inventory. - /// - /// Whether we succeeded in adding the item. We may fail if the object doesn't exist or is set to delete. + /// @return Whether we succeeded in adding the item. We may fail if the object doesn't exist or is set to delete. bool AddToInventoryFront(MovableObject* itemToAdd); - /// /// Attempts to add an item to the back of our inventory. - /// - /// Whether we succeeded in adding the item. We may fail if the object doesn't exist or is set to delete. + /// @return Whether we succeeded in adding the item. We may fail if the object doesn't exist or is set to delete. bool AddToInventoryBack(MovableObject* itemToAdd); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAimRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The limit of this actors aiming angle, in each direction, in radians. - // Arguments: None. - // Return value: The arc range of the aiming angle in radians. - // Eg if HalfPI, it means full 180 degree range - + /// The limit of this actors aiming angle, in each direction, in radians. + /// @return The arc range of the aiming angle in radians. + /// Eg if HalfPI, it means full 180 degree range float GetAimRange() const { return m_AimRange; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetAimRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the limit of this actors aiming angle, in each direction, in radians. - // Arguments: The arc range of the aiming angle in radians. - // Eg if HalfPI, it means full 180 degree range - // Return value: None. - + /// Sets the limit of this actors aiming angle, in each direction, in radians. + /// @param range The arc range of the aiming angle in radians. + /// Eg if HalfPI, it means full 180 degree range void SetAimRange(float range) { m_AimRange = range; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawWaypoints - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes this draw its current waypoints and related data on the scene in - // its HUD drawing stage. - // Arguments: Whether to enable or disable the drawing of the waypoints. - // Return value: None. - + /// Makes this draw its current waypoints and related data on the scene in + /// its HUD drawing stage. + /// @param drawWaypoints Whether to enable or disable the drawing of the waypoints. (default: true) void DrawWaypoints(bool drawWaypoints = true) { m_DrawWaypoints = drawWaypoints; } - /// /// Destroys this MOSRotating and creates its specified Gibs in its place with appropriate velocities. /// Any Attachables are removed and also given appropriate velocities. - /// - /// The impulse (kg * m/s) of the impact causing the gibbing to happen. - /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + /// @param impactImpulse The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// @param movableObjectToIgnore A pointer to an MO which the Gibs and Attachables should not be colliding with. void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - // Arguments: Reference to the HitData struct which describes the collision. This - // will be modified to represent the results of the collision. - // Return value: Whether the collision has been deemed valid. If false, then disregard - // any impulses in the Hitdata. - + /// Calculates the collision response when another MO's Atom collides with + /// this MO's physical representation. The effects will be applied + /// directly to this MO, and also represented in the passed in HitData. + /// @param hitData Reference to the HitData struct which describes the collision. This + /// will be modified to represent the results of the collision. + /// @return Whether the collision has been deemed valid. If false, then disregard + /// any impulses in the Hitdata. bool CollideAtPoint(HitData& hitData) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ParticlePenetration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Determines whether a particle which has hit this MO will penetrate, - // and if so, whether it gets lodged or exits on the other side of this - // MO. Appropriate effects will be determined and applied ONLY IF there - // was penetration! If not, nothing will be affected. - // Arguments: The HitData describing the collision in detail, the impulses have to - // have been filled out! - // Return value: Whether the particle managed to penetrate into this MO or not. If - // somehting but a MOPixel or MOSParticle is being passed in as hitor, - // false will trivially be returned here. - + /// Determines whether a particle which has hit this MO will penetrate, + /// and if so, whether it gets lodged or exits on the other side of this + /// MO. Appropriate effects will be determined and applied ONLY IF there + /// was penetration! If not, nothing will be affected. + /// @param hd The HitData describing the collision in detail, the impulses have to + /// have been filled out! + /// @return Whether the particle managed to penetrate into this MO or not. If + /// somehting but a MOPixel or MOSParticle is being passed in as hitor, + /// false will trivially be returned here. bool ParticlePenetration(HitData& hd) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreTravel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does stuff that needs to be done before Travel(). Always call before - // calling Travel. - // Arguments: None. - // Return value: None. - + /// Does stuff that needs to be done before Travel(). Always call before + /// calling Travel. void PreTravel() override { MOSRotating::PreTravel(); m_GoldPicked = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMovePathToUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this' AI's move path to be updated. Will update the path to the - // current waypoint, if any. - // Arguments: None. - // Return value: None. - + /// Sets this' AI's move path to be updated. Will update the path to the + /// current waypoint, if any. void SetMovePathToUpdate() { m_UpdateMovePath = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMovePathSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how many waypoints there are in the MovePath currently - // Arguments: None. - // Return value: The number of waypoints in the MovePath. - + /// Gets how many waypoints there are in the MovePath currently + /// @return The number of waypoints in the MovePath. int GetMovePathSize() const { return m_MovePath.size(); } - /// /// Starts updating this Actor's movepath. - /// virtual void UpdateMovePath(); - /// /// Returns whether we're waiting on a new pending movepath. - /// - /// Whether we're waiting on a new pending movepath. + /// @return Whether we're waiting on a new pending movepath. bool IsWaitingOnNewMovePath() const { return m_PathRequest != nullptr || m_UpdateMovePath; } - /// /// Estimates what material strength this actor can penetrate. - /// - /// The actor's dig strength. + /// @return The actor's dig strength. virtual float EstimateDigStrength() const; - /// /// Gets this Actor's base dig strength, or the strength of terrain they can expect to walk through without tools. - /// - /// The actors base dig strength. + /// @return The actors base dig strength. float GetAIBaseDigStrength() const { return m_AIBaseDigStrength; } - /// /// Sets this Actor's base dig strength, or the strength of terrain they can expect to walk through without tools. - /// - /// The new base dig strength for this Actor. + /// @param newAIBaseDigStrength The new base dig strength for this Actor. void SetAIBaseDigStrength(float newAIBaseDigStrength) { m_AIBaseDigStrength = newAIBaseDigStrength; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreControllerUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Update called prior to controller update. Ugly hack. Supposed to be done every frame. virtual void PreControllerUpdate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: FullUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the full state of this object in one call. (PreControllerUpdate(), Controller::Update(), and Update()) - // Arguments: None. - // Return value: None. - + /// Updates the full state of this object in one call. (PreControllerUpdate(), Controller::Update(), and Update()) virtual void FullUpdate() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetDeploymentID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets deployment ID for this actor - // Arguments: New deployment id. - // Return value: None. - + /// Description: Sets deployment ID for this actor + /// @param newID New deployment id. void SetDeploymentID(unsigned int newID) { m_DeploymentID = newID; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDeploymentID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets deployment ID of this actor - // Arguments: None. - // Return value: Returns deployment id of this actor. - + /// Description: Gets deployment ID of this actor + /// @return Returns deployment id of this actor. unsigned int GetDeploymentID() const { return m_DeploymentID; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetSightDistance - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns actor's sight distance. - // Arguments: None. - // Return value: Returns actor's sight distance. - + /// Description: Returns actor's sight distance. + /// @return Returns actor's sight distance. float GetSightDistance() const { return m_SightDistance; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetSightDistance - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets actor's sight distance. - // Arguments: New sight distance value. - // Return value: None. - + /// Description: Sets actor's sight distance. + /// @param newValue New sight distance value. void SetSightDistance(float newValue) { m_SightDistance = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Actor's current graphical HUD overlay representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // Which player's screen this is being drawn to. May affect what HUD elements - // get drawn etc. - // Return value: None. - + /// Draws this Actor's current graphical HUD overlay representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param whichScreen Which player's screen this is being drawn to. May affect what HUD elements (default: 0) + /// get drawn etc. void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: VerifyMOIDIndex - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Verifieis whether all actor's MO has correct IDs. Should be used in Debug mode only. - // Arguments: None. - // Return value: None. - + /// Verifieis whether all actor's MO has correct IDs. Should be used in Debug mode only. void VerifyMOIDs(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTravelImpulseDamage - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns Threshold for taking damage from travel impulses, in kg * m/s - // Arguments: None. - // Return value: Threshold for taking damage from travel impulses, in kg * m/s - + /// Returns Threshold for taking damage from travel impulses, in kg * m/s + /// @return Threshold for taking damage from travel impulses, in kg * m/s float GetTravelImpulseDamage() const { return m_TravelImpulseDamage; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTravelImpulseDamage - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets Threshold for taking damage from travel impulses, in kg * m/s - // Arguments: Threshold for taking damage from travel impulses, in kg * m/s - // Return value: None. - + /// Sets Threshold for taking damage from travel impulses, in kg * m/s + /// @param value Threshold for taking damage from travel impulses, in kg * m/s void SetTravelImpulseDamage(float value) { m_TravelImpulseDamage = value; } - /// /// Gets this Actor's body hit sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's body hit sound. + /// @return The SoundContainer for this Actor's body hit sound. SoundContainer* GetBodyHitSound() const { return m_BodyHitSound; } - /// /// Sets this Actor's body hit sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's body hit sound. + /// @param newSound The new SoundContainer for this Actor's body hit sound. void SetBodyHitSound(SoundContainer* newSound) { m_BodyHitSound = newSound; } - /// /// Gets this Actor's alarm sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's alarm sound. + /// @return The SoundContainer for this Actor's alarm sound. SoundContainer* GetAlarmSound() const { return m_AlarmSound; } - /// /// Sets this Actor's alarm sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's alarm sound. + /// @param newSound The new SoundContainer for this Actor's alarm sound. void SetAlarmSound(SoundContainer* newSound) { m_AlarmSound = newSound; } - /// /// Gets this Actor's pain sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's pain sound. + /// @return The SoundContainer for this Actor's pain sound. SoundContainer* GetPainSound() const { return m_PainSound; } - /// /// Sets this Actor's pain sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's pain sound. + /// @param newSound The new SoundContainer for this Actor's pain sound. void SetPainSound(SoundContainer* newSound) { m_PainSound = newSound; } - /// /// Gets this Actor's death sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's death sound. + /// @return The SoundContainer for this Actor's death sound. SoundContainer* GetDeathSound() const { return m_DeathSound; } - /// /// Sets this Actor's death sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's death sound. + /// @param newSound The new SoundContainer for this Actor's death sound. void SetDeathSound(SoundContainer* newSound) { m_DeathSound = newSound; } - /// /// Gets this Actor's device switch sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this Actor's device switch sound. + /// @return The SoundContainer for this Actor's device switch sound. SoundContainer* GetDeviceSwitchSound() const { return m_DeviceSwitchSound; } - /// /// Sets this Actor's device switch sound. Ownership IS transferred! - /// - /// The new SoundContainer for this Actor's device switch sound. + /// @param newSound The new SoundContainer for this Actor's device switch sound. void SetDeviceSwitchSound(SoundContainer* newSound) { m_DeviceSwitchSound = newSound; } - /// /// Gets the X and Y thresholds for how fast the actor can travel before losing stability. - /// - /// A Vector with the X and Y thresholds for how fast the actor can travel before losing stability. + /// @return A Vector with the X and Y thresholds for how fast the actor can travel before losing stability. Vector GetStableVel() const { return m_StableVel; } - /// /// Sets the X and Y thresholds for how fast the actor can travel before losing stability. - /// - /// New value for how fast the actor can travel before losing stability on X axis. - /// New value for how fast the actor can travel before losing stability on Y axis. + /// @param newVelX New value for how fast the actor can travel before losing stability on X axis. + /// @param newVelY New value for how fast the actor can travel before losing stability on Y axis. void SetStableVel(float newVelX, float newVelY) { m_StableVel.SetXY(newVelX, newVelY); } - /// /// Sets the X and Y thresholds for how fast the actor can travel before losing stability. - /// - /// Vector with new values for how fast the actor can travel before losing stability on both axis. + /// @param newVelVector Vector with new values for how fast the actor can travel before losing stability on both axis. void SetStableVel(Vector newVelVector) { m_StableVel = newVelVector; } - /// /// Gets the recovery delay from UNSTABLE to STABLE, in MS. - /// - /// The recovery delay, in MS. + /// @return The recovery delay, in MS. int GetStableRecoverDelay() const { return m_StableRecoverDelay; } - /// /// Sets the recovery delay from UNSTABLE to STABLE, in MS. - /// - /// The recovery delay, in MS. + /// @param newRecoverDelay The recovery delay, in MS. void SetStableRecoverDelay(int newRecoverDelay) { m_StableRecoverDelay = newRecoverDelay; } - /// /// Gets the distance in which the Actor will have considered itself to have reached it's waypoint. - /// - /// The move proximity limit. + /// @return The move proximity limit. float GetMoveProximityLimit() const { return m_MoveProximityLimit; } - /// /// Sets the distance in which the Actor will have considered itself to have reached it's waypoint. - /// - /// The move proximity limit. + /// @param newProximityLimit The move proximity limit. void SetMoveProximityLimit(float newProximityLimit) { m_MoveProximityLimit = newProximityLimit; } - /// /// Gets whether or not this Actor has the organic flag set and should be considered as organic. - /// - /// Whether or not this Actor has the organic flag set and should be considered as organic. + /// @return Whether or not this Actor has the organic flag set and should be considered as organic. bool IsOrganic() const { return m_Organic; } - /// /// Gets whether or not this Actor has the mechanical flag set and should be considered as mechanical. - /// - /// Whether or not this Actor has the mechanical flag set and should be considered as mechanical. + /// @return Whether or not this Actor has the mechanical flag set and should be considered as mechanical. bool IsMechanical() const { return m_Mechanical; } - /// /// Gets whether or not this Actor's limb push forces have been disabled. - /// - /// Whether or not this Actor's limb push forces have been disabled. + /// @return Whether or not this Actor's limb push forces have been disabled. bool GetLimbPushForcesAndCollisionsDisabled() const { return m_LimbPushForcesAndCollisionsDisabled; } - /// /// Sets whether or not this Actor's limb push forces should be disabled. - /// - /// Whether or not this Actor's limb push forces should be disabled. + /// @param newLimbPushForcesAndCollisionsDisabled Whether or not this Actor's limb push forces should be disabled. void SetLimbPushForcesAndCollisionsDisabled(bool newLimbPushForcesAndCollisionsDisabled) { m_LimbPushForcesAndCollisionsDisabled = newLimbPushForcesAndCollisionsDisabled; } - /// /// Gets the default PieMenu name for this type. - /// - /// The default PieMenu name for this type. + /// @return The default PieMenu name for this type. virtual std::string GetDefaultPieMenuName() const { return "Default Actor Pie Menu"; } - /// /// Gets a pointer to the PieMenu for this Actor. Ownership is NOT transferred. - /// - /// The PieMenu for this Actor. + /// @return The PieMenu for this Actor. PieMenu* GetPieMenu() const { return m_PieMenu.get(); } - /// /// Sets the PieMenu for this Actor. Ownership IS transferred. - /// - /// The new PieMenu for this Actor. + /// @param newPieMenu The new PieMenu for this Actor. void SetPieMenu(PieMenu* newPieMenu) { m_PieMenu = std::unique_ptr(newPieMenu); m_PieMenu->Create(this); m_PieMenu->AddWhilePieMenuOpenListener(this, std::bind(&Actor::WhilePieMenuOpenListener, this, m_PieMenu.get())); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Function that is called when we get a new movepath. /// This processes and cleans up the movepath. - /// virtual void OnNewMovePath(); // Member variables @@ -1590,20 +1015,12 @@ namespace RTE { bool m_LimbPushForcesAndCollisionsDisabled; // m_PieMenu; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Actor, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Actor, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Arm.cpp b/Source/Entities/Arm.cpp index c8170a1923..5216c6630b 100644 --- a/Source/Entities/Arm.cpp +++ b/Source/Entities/Arm.cpp @@ -9,8 +9,6 @@ namespace RTE { ConcreteClassInfo(Arm, Attachable, 50); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::Clear() { m_MaxLength = 0; m_MoveSpeed = 0; @@ -35,8 +33,6 @@ namespace RTE { m_HeldDeviceThisArmIsTryingToSupport = nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Arm::Create() { if (Attachable::Create() < 0) { return -1; @@ -51,8 +47,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Arm::Create(const Arm& reference) { if (reference.m_HeldDevice) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_HeldDevice->GetUniqueID()); @@ -86,8 +80,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Arm::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Attachable::ReadProperty(propName, reader)); @@ -105,8 +97,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Arm::Save(Writer& writer) const { Attachable::Save(writer); @@ -121,14 +111,10 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::SetHandPos(const Vector& newHandPos) { SetHandCurrentOffset(g_SceneMan.ShortestDistance(m_JointPos, newHandPos, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY())); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::AddHandTarget(const std::string& description, const Vector& handTargetPositionToAdd, float delayAtTarget) { Vector handTargetOffsetToAdd = g_SceneMan.ShortestDistance(m_JointPos, handTargetPositionToAdd, g_SceneMan.SceneWrapsX() || g_SceneMan.SceneWrapsY()); if (!handTargetOffsetToAdd.IsZero()) { @@ -143,8 +129,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::SetHeldDevice(HeldDevice* newHeldDevice) { if (m_HeldDevice && m_HeldDevice->IsAttached()) { RemoveAndDeleteAttachable(m_HeldDevice); @@ -169,22 +153,16 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - HeldDevice* Arm::SwapHeldDevice(HeldDevice* newHeldDevice) { Attachable* previousHeldDevice = RemoveAttachable(m_HeldDevice, false, false); SetHeldDevice(newHeldDevice); return dynamic_cast(previousHeldDevice); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Arm::HandIsCloseToTargetOffset(const Vector& targetOffset) const { return (m_HandCurrentOffset - targetOffset).MagnitudeIsLessThan(m_MaxLength / 10.0F); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::Update() { Attachable::PreUpdate(); @@ -232,8 +210,6 @@ namespace RTE { m_HandIdleRotation = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::UpdateHandCurrentOffset(bool armHasParent, bool heldDeviceIsAThrownDevice) { if (armHasParent) { Vector targetOffset; @@ -303,8 +279,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::AccountForHeldDeviceRecoil(const HeldDevice* heldDevice, Vector& targetOffset) { if (!heldDevice->GetRecoilForce().IsZero()) { float totalGripStrength = m_GripStrength * heldDevice->GetGripStrengthMultiplier(); @@ -342,8 +316,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::AccountForHeldDeviceTerrainClipping(const HeldDevice* heldDevice, Vector& targetOffset) const { Vector newMuzzlePos = (m_JointPos + targetOffset) - RotateOffset(heldDevice->GetJointOffset()) + RotateOffset(heldDevice->GetMuzzleOffset()); Vector midToMuzzle = RotateOffset({heldDevice->GetIndividualRadius(), 0}); @@ -358,16 +330,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::UpdateArmFrame() { float halfMaxLength = m_MaxLength / 2.0F; float newFrame = std::floor(((m_HandCurrentOffset.GetMagnitude() - halfMaxLength) / halfMaxLength) * static_cast(m_FrameCount)); m_Frame = static_cast(std::clamp(newFrame, 0.0F, static_cast(m_FrameCount - 1))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { Attachable::Draw(targetBitmap, targetPos, mode, onlyPhysical); @@ -379,8 +347,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Arm::DrawHand(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode) const { Vector handPos(m_JointPos + m_HandCurrentOffset + (m_Recoiled ? m_RecoilOffset : Vector()) - targetPos); handPos -= Vector(static_cast(m_HandSpriteBitmap->w / 2), static_cast(m_HandSpriteBitmap->h / 2)); diff --git a/Source/Entities/Arm.h b/Source/Entities/Arm.h index 033b6f6dff..024f55b1aa 100644 --- a/Source/Entities/Arm.h +++ b/Source/Entities/Arm.h @@ -7,9 +7,7 @@ namespace RTE { class HeldDevice; - /// /// A detachable arm that can hold HeldDevices. - /// class Arm : public Attachable { public: @@ -18,35 +16,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate an Arm object in system memory. Create() should be called before using the object. - /// Arm() { Clear(); } - /// /// Makes the Arm object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates an Arm to be identical to another, by deep copy. - /// - /// A reference to the Arm to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Arm to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Arm& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up an Arm object before deletion from system memory. - /// ~Arm() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the Arm object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { Attachable::Destroy(); @@ -54,9 +42,7 @@ namespace RTE { Clear(); } - /// /// Resets the entire Arm, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Attachable::Reset(); @@ -64,121 +50,85 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the max length of this Arm when fully extended, i.e. the farthest possible length from its joint position to the hand. - /// - /// The max length of this Arm. + /// @return The max length of this Arm. float GetMaxLength() const { return m_MaxLength; } - /// /// Gets the move speed of this Arm, where 1.0 is instant and 0.0 is none. - /// - /// The move speed of this Arm. + /// @return The move speed of this Arm. float GetMoveSpeed() const { return m_MoveSpeed; } - /// /// Sets the move speed of this Arm, where 1.0 is instant and 0.0 is none. - /// - /// The new move speed of this Arm. + /// @return The new move speed of this Arm. void SetMoveSpeed(float newMoveSpeed) { m_MoveSpeed = newMoveSpeed; } - /// /// Gets the default idle offset of this Arm's hand, i.e. the default offset from the joint position that this Arm will try to move to when not moving towards a position. - /// - /// The idle offset of this Arm's hand. + /// @return The idle offset of this Arm's hand. Vector GetHandIdleOffset() const { return m_HandIdleOffset; } - /// /// Sets the default idle offset of this Arm's hand, i.e. the default offset from the joint position that this Arm will try to move to when not moving towards a position. - /// - /// The new idle offset of this Arm's hand. + /// @param newDefaultIdleOffset The new idle offset of this Arm's hand. void SetHandIdleOffset(const Vector& newDefaultIdleOffset) { m_HandIdleOffset = newDefaultIdleOffset; } - /// /// Gets the rotation that is being applied to this Arm's hand, if it's using an idle offset. - /// - /// The idle rotation of this Arm's hand. + /// @return The idle rotation of this Arm's hand. float GetHandIdleRotation() const { return m_HandIdleRotation; } - /// /// Sets the rotation that is being applied to this Arm's hand, if it's using an idle offset. Note that this value is reset to 0 every update. - /// - /// The new idle rotation of this Arm's hand. + /// @param newHandIdleRotation The new idle rotation of this Arm's hand. void SetHandIdleRotation(float newHandIdleRotation) { m_HandIdleRotation = newHandIdleRotation; } - /// /// Gets the current offset of this Arm's hand, i.e. its distance from the joint position. - /// - /// This current offset of this Arm's hand. + /// @return This current offset of this Arm's hand. Vector GetHandCurrentOffset() const { return m_HandCurrentOffset; } - /// /// Sets the current offset of this Arm's hand, i.e. its distance from the joint position. The value is capped to the max length of the Arm. - /// - /// The new current offset of this Arm's hand. + /// @param newHandOffset The new current offset of this Arm's hand. // TODO maybe don't want this in favor of SetHandPos? void SetHandCurrentOffset(const Vector& newHandOffset) { m_HandCurrentOffset = newHandOffset; m_HandCurrentOffset.CapMagnitude(m_MaxLength); } - /// /// Gets the current position of this Arm's hand in absolute Scene coordinates. - /// - /// The current position of this Arm's hand in absolute Scene coordinates. + /// @return The current position of this Arm's hand in absolute Scene coordinates. Vector GetHandPos() const { return m_JointPos + m_HandCurrentOffset; } - /// /// Sets the current position of this Arm's hand to an absolute scene coordinate. If needed, the set position is modified so its distance from the joint position of the Arm is capped to the max length of the Arm. - /// - /// The new current position of this Arm's hand as absolute scene coordinate. + /// @param newHandPos The new current position of this Arm's hand as absolute scene coordinate. void SetHandPos(const Vector& newHandPos); - /// /// Gets the the strength with which this Arm will grip its HeldDevice. - /// - /// The grip strength of this Arm. + /// @return The grip strength of this Arm. float GetGripStrength() const { return m_GripStrength; } - /// /// Sets the strength with which this Arm will grip its HeldDevice. - /// - /// The new grip strength for this Arm to use. + /// @param newGripStrength The new grip strength for this Arm to use. void SetGripStrength(float newGripStrength) { m_GripStrength = newGripStrength; } - /// /// Gets the the strength with which this Arm will throw a ThrownDevice. - /// - /// The throw strength of this Arm. + /// @return The throw strength of this Arm. float GetThrowStrength() const { return m_ThrowStrength; } - /// /// Sets the strength with which this Arm will throw a ThrownDevice. - /// - /// The new throw strength for this Arm to use. + /// @param newThrowStrength The new throw strength for this Arm to use. void SetThrowStrength(float newThrowStrength) { m_ThrowStrength = newThrowStrength; } #pragma endregion #pragma region Hand Animation Handling - /// /// Adds a HandTarget position, in absolute scene coordinates, to the queue for the Arm to move its hand towards. Target positions are removed from the queue when they're reached (or as close to reached as is possible). - /// - /// The description of this HandTarget, for easy identification. - /// The position, in absolute scene coordinates, to add the queue of hand targets. + /// @param description The description of this HandTarget, for easy identification. + /// @param handTargetPositionToAdd The position, in absolute scene coordinates, to add the queue of hand targets. void AddHandTarget(const std::string& description, const Vector& handTargetPositionToAdd) { AddHandTarget(description, handTargetPositionToAdd, 0); } - /// /// Adds a HandTarget position, in absolute scene coordinates, to the queue for the Arm to move its hand towards. Target positions are removed from the queue when they're reached (or as close to reached as is possible). /// If the target position is very close to the last element in the queue, or has the same name as it, the last element in the queue is updated to avoid filling the queue with similar values. - /// - /// The description of this HandTarget, for easy identification. - /// The position, in absolute scene coordinates, to add the queue of hand targets. - /// The amount of time, in MS, that the hand should wait when it reaches the newly added HandTarget. + /// @param description The description of this HandTarget, for easy identification. + /// @param handTargetPositionToAdd The position, in absolute scene coordinates, to add the queue of hand targets. + /// @param delayAtTarget The amount of time, in MS, that the hand should wait when it reaches the newly added HandTarget. void AddHandTarget(const std::string& description, const Vector& handTargetPositionToAdd, float delayAtTarget); - /// /// Removes this Arm's next HandTarget, if there is one. - /// void RemoveNextHandTarget() { if (!m_HandTargets.empty()) { m_HandTargets.pop(); @@ -186,89 +136,63 @@ namespace RTE { } } - /// /// Gets whether or not this Arm has any HandTargets. - /// - /// Whether or not this Arm has any HandTargets. + /// @return Whether or not this Arm has any HandTargets. bool HasAnyHandTargets() const { return !m_HandTargets.empty(); } - /// /// Gets the number of HandTargets this Arm has. - /// - /// The number of HandTargets this Arm has. + /// @return The number of HandTargets this Arm has. int GetNumberOfHandTargets() const { return m_HandTargets.size(); } - /// /// Gets the name of this Arm's next HandTarget. - /// - /// The name of this Arm's next HandTarget. + /// @return The name of this Arm's next HandTarget. std::string GetNextHandTargetDescription() const { return m_HandTargets.empty() ? "" : m_HandTargets.front().Description; } - /// /// Gets the position, in absolute scene coordinates, of this Arm's next HandTarget. - /// - /// The position of this Arm's next HandTarget. + /// @return The position of this Arm's next HandTarget. Vector GetNextHandTargetPosition() const { return m_HandTargets.empty() ? Vector() : m_HandTargets.front().TargetOffset + m_JointPos; } - /// /// Gets whether or not the hand has reached its current target. This is either the front of the HandTarget queue, or the offset it's currently trying to move to to when it has no HandTargets specified. - /// - /// Whether or not the hand has reached its current target. + /// @return Whether or not the hand has reached its current target. bool GetHandHasReachedCurrentTarget() const { return m_HandHasReachedCurrentTarget; } - /// /// Empties the queue of HandTargets. With the queue empty, the hand will move to its appropriate idle offset. - /// void ClearHandTargets() { m_HandTargets = {}; } #pragma endregion #pragma region HeldDevice Management - /// /// Gets the HeldDevice currently held by this Arm. - /// - /// The HeldDevice currently held by this Arm. Ownership is NOT transferred. + /// @return The HeldDevice currently held by this Arm. Ownership is NOT transferred. HeldDevice* GetHeldDevice() const { return m_HeldDevice; } - /// /// Sets the HeldDevice held by this Arm. - /// - /// The new HeldDevice to be held by this Arm. Ownership IS transferred. + /// @param newHeldDevice The new HeldDevice to be held by this Arm. Ownership IS transferred. void SetHeldDevice(HeldDevice* newHeldDevice); - /// /// Gets the HeldDevice this Arm is trying to support. - /// - /// The HeldDevice this Arm is trying to support. Ownership is NOT transferred. + /// @return The HeldDevice this Arm is trying to support. Ownership is NOT transferred. HeldDevice* GetHeldDeviceThisArmIsTryingToSupport() const { return m_HeldDeviceThisArmIsTryingToSupport; } - /// /// Sets the HeldDevice being this Arm is trying to support. - /// - /// The new HeldDevice this Arm should try to support. Ownership is NOT transferred. + /// @param newHeldDeviceForThisArmToTryToSupport The new HeldDevice this Arm should try to support. Ownership is NOT transferred. void SetHeldDeviceThisArmIsTryingToSupport(HeldDevice* newHeldDeviceThisArmShouldTryToSupport) { m_HeldDeviceThisArmIsTryingToSupport = newHeldDeviceThisArmShouldTryToSupport; } - /// /// Replaces the HeldDevice currently held by this Arm with a new one, and returns the old one. Ownership IS transferred both ways. - /// - /// The new HeldDevice to be held by this Arm. Ownership IS transferred. - /// The HeldDevice that was held by this Arm. Ownership IS transferred. + /// @param newHeldDevice The new HeldDevice to be held by this Arm. Ownership IS transferred. + /// @return The HeldDevice that was held by this Arm. Ownership IS transferred. HeldDevice* SwapHeldDevice(HeldDevice* newHeldDevice); #pragma endregion #pragma region Concrete Methods - /// /// Draws this Arm's hand's graphical representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// Which mode to draw in. See the DrawMode enumeration for available modes. + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param mode Which mode to draw in. See the DrawMode enumeration for available modes. void DrawHand(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor) const; #pragma endregion #pragma region Override Methods - /// /// Does stuff that needs to be done after Update(). - /// void PostTravel() override { if (IsAttached()) { m_AngularVel = 0; @@ -276,29 +200,21 @@ namespace RTE { MOSRotating::PostTravel(); } - /// /// Updates this Arm. Supposed to be done every frame. - /// void Update() override; - /// /// Draws this Arm's current graphical representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// Which mode to draw in. See the DrawMode enumeration for the modes. - /// Whether to not draw any extra 'ghost' items of this Arm. In this case, that means the hand sprite. + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param mode Which mode to draw in. See the DrawMode enumeration for the modes. + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this Arm. In this case, that means the hand sprite. void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion private: - /// /// Struct for storing data about each target in the Arm's queue of HandTargets. - /// struct HandTarget { - /// /// Constructor method used to instantiate a HandTarget object in system memory. - /// HandTarget(const std::string_view& description, const Vector& targetOffset, float delayAtTarget, bool hFlippedWhenTargetWasCreated) : Description(description), TargetOffset(targetOffset), DelayAtTarget(delayAtTarget), HFlippedWhenTargetWasCreated(hFlippedWhenTargetWasCreated) {} @@ -331,45 +247,33 @@ namespace RTE { HeldDevice* m_HeldDevice; //!< A pointer to the HeldDevice this Arm is currently holding. Owned in the MOSRotating Attachables list, kept here for convenience. HeldDevice* m_HeldDeviceThisArmIsTryingToSupport; //!< A pointer to the HeldDevice being supported by this Arm (i.e. this is the background Arm for another HeldDevice). - /// /// Gets whether or not the hand is close to the given offset. - /// - /// The offset to check for closeness to the hand. - /// Whether or not the hand is close to the given offset. + /// @param targetOffset The offset to check for closeness to the hand. + /// @return Whether or not the hand is close to the given offset. bool HandIsCloseToTargetOffset(const Vector& targetOffset) const; #pragma region Update Breakdown - /// /// Updates the current hand offset for this Arm. Should only be called from Update. /// If the Arm is attached, the current hand offset is based on the target offset and move speed, and whether the Arm should idle or not, otherwise it puts it in a reasonable position. - /// - /// Whether or not this Arm has a parent. Passed in for convenient reuse. - /// Whether or not this Arm's HeldDevice is a ThrownDevice. Passed in for convenient reuse. + /// @param hasParent Whether or not this Arm has a parent. Passed in for convenient reuse. + /// @param heldDeviceIsAThrownDevice Whether or not this Arm's HeldDevice is a ThrownDevice. Passed in for convenient reuse. void UpdateHandCurrentOffset(bool armHasParent, bool heldDeviceIsAThrownDevice); - /// /// To be used in UpdateHandCurrentOffset. Applies any recoil accumulated on the HeldDevice to the passed in target offset, so it can be used to affect the hand's target position. - /// - /// The held MO as a HeldDevice, for convenience. - /// The target offset to have recoil applied to it. + /// @param heldDevice The held MO as a HeldDevice, for convenience. + /// @param targetOffset The target offset to have recoil applied to it. void AccountForHeldDeviceRecoil(const HeldDevice* heldDevice, Vector& targetOffset); - /// /// To be used in UpdateHandCurrentOffset. Ensures the HeldDevice won't clip through terrain by modifying the passed in target offset. - /// - /// The held MO as a HeldDevice, for convenience. - /// The target offset to be modified to avoid any terrain clipping. + /// @param heldDevice The held MO as a HeldDevice, for convenience. + /// @param targetOffset The target offset to be modified to avoid any terrain clipping. void AccountForHeldDeviceTerrainClipping(const HeldDevice* heldDevice, Vector& targetOffset) const; - /// /// Updates the frame for this Arm. Should only be called from Update. - /// void UpdateArmFrame(); #pragma endregion - /// /// Clears all the member variables of this Arm, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/AtomGroup.cpp b/Source/Entities/AtomGroup.cpp index 0433b3e839..6bec5473d9 100644 --- a/Source/Entities/AtomGroup.cpp +++ b/Source/Entities/AtomGroup.cpp @@ -17,8 +17,6 @@ namespace RTE { {"Circle", AtomGroup::AreaDistributionType::Circle}, {"Square", AtomGroup::AreaDistributionType::Square}}; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::Clear() { m_Atoms.clear(); m_SubGroups.clear(); @@ -35,8 +33,6 @@ namespace RTE { m_IgnoreMOIDs.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AtomGroup::Create() { if (Entity::Create() < 0) { return -1; @@ -55,8 +51,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AtomGroup::Create(const AtomGroup& reference, bool onlyCopyOwnerAtoms) { Entity::Create(reference); @@ -102,8 +96,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AtomGroup::Create(MOSRotating* ownerMOSRotating, Material const* material, int resolution, int depth) { RTEAssert(ownerMOSRotating, "Trying to generate an AtomGroup for a MOSRotating without a sprite!"); @@ -118,8 +110,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AtomGroup::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -162,8 +152,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AtomGroup::Save(Writer& writer) const { Entity::Save(writer); @@ -195,8 +183,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::Destroy(bool notInherited) { for (const Atom* atom: m_Atoms) { delete atom; @@ -207,8 +193,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::SetAtomList(const std::vector& newAtoms) { for (const Atom* atom: m_Atoms) { delete atom; @@ -216,8 +200,6 @@ namespace RTE { m_Atoms = newAtoms; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float AtomGroup::CalculateMaxRadius() const { float sqrMagnitude = 0.0F; float sqrLongest = 0.0F; @@ -231,8 +213,6 @@ namespace RTE { return std::sqrt(sqrLongest); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::SetOwner(MOSRotating* newOwner) { m_OwnerMOSR = newOwner; for (Atom* atom: m_Atoms) { @@ -240,14 +220,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector AtomGroup::GetAdjustedAtomOffset(const Atom* atom) const { return atom->GetOffset().GetXFlipped(m_OwnerMOSR->m_HFlipped) * m_OwnerMOSR->GetRotMatrix(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float AtomGroup::GetMomentOfInertia() { float currentOwnerMass = (m_OwnerMOSR->GetMass() != 0 ? m_OwnerMOSR->GetMass() : 0.0001F); if (m_MomentOfInertia == 0.0F || std::abs(m_StoredOwnerMass - currentOwnerMass) >= (m_StoredOwnerMass / 10.0F)) { @@ -269,8 +245,6 @@ namespace RTE { return m_MomentOfInertia; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::AddAtoms(const std::vector& atomList, long subgroupID, const Vector& offset, const Matrix& offsetRotation) { if (m_SubGroups.count(subgroupID) == 0) { m_SubGroups.insert({subgroupID, std::vector()}); @@ -294,8 +268,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AtomGroup::RemoveAtoms(long removeID) { std::size_t oldSize = m_Atoms.size(); @@ -319,8 +291,6 @@ namespace RTE { return removedAny; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AtomGroup::UpdateSubAtoms(long subgroupID, const Vector& newOffset, const Matrix& newOffsetRotation) { if (m_SubGroups.empty() || m_SubGroups.count(subgroupID) == 0) { return false; @@ -333,14 +303,10 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float AtomGroup::Travel(float travelTime, bool callOnBounce, bool callOnSink, bool scenePreLocked) { return Travel(m_OwnerMOSR->m_Pos, m_OwnerMOSR->m_Vel, m_OwnerMOSR->m_Rotation, m_OwnerMOSR->m_AngularVel, m_OwnerMOSR->m_DidWrap, m_OwnerMOSR->m_TravelImpulse, m_OwnerMOSR->GetMass(), travelTime, callOnBounce, callOnSink, scenePreLocked); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: Break down and rework this trainwreck. float AtomGroup::Travel(Vector& position, Vector& velocity, Matrix& rotation, float& angularVel, bool& didWrap, Vector& totalImpulse, float mass, float travelTime, bool callOnBounce, bool callOnSink, bool scenePreLocked) { ZoneScoped; @@ -785,8 +751,6 @@ namespace RTE { return timeLeft; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: Break down and rework this dumpsterfire. Vector AtomGroup::PushTravel(Vector& position, const Vector& velocity, float pushForce, bool& didWrap, float travelTime, bool callOnBounce, bool callOnSink, bool scenePreLocked) { ZoneScoped; @@ -1242,8 +1206,6 @@ namespace RTE { return returnPush; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AtomGroup::PushAsLimb(const Vector& jointPos, const Vector& velocity, const Matrix& rotation, LimbPath& limbPath, const float travelTime, bool* restarted, bool affectRotation, Vector rotationOffset, Vector positionOffset) { RTEAssert(m_OwnerMOSR, "Tried to push-as-limb an AtomGroup that has no parent!"); @@ -1317,8 +1279,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::FlailAsLimb(const Vector& ownerPos, const Vector& jointOffset, const float limbRadius, const Vector& velocity, const float angularVel, const float mass, const float travelTime) { RTEAssert(m_OwnerMOSR, "Tried to flail an AtomGroup that has no parent!"); @@ -1339,8 +1299,6 @@ namespace RTE { return; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AtomGroup::InTerrain() const { RTEAssert(m_OwnerMOSR, "Tried to check overlap with terrain for an AtomGroup that has no parent!"); @@ -1363,8 +1321,6 @@ namespace RTE { return penetrates; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float AtomGroup::RatioInTerrain() const { RTEAssert(m_OwnerMOSR, "Tried to check ratio in terrain for an AtomGroup that has no parent!"); @@ -1386,8 +1342,6 @@ namespace RTE { return static_cast(inTerrain) / static_cast(m_Atoms.size()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: Look into breaking this into smaller methods. bool AtomGroup::ResolveTerrainIntersection(Vector& position, unsigned char strongerThan) const { thread_local std::vector intersectingAtoms; @@ -1471,8 +1425,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: Look into breaking this into smaller methods. bool AtomGroup::ResolveMOSIntersection(Vector& position) { if (!m_OwnerMOSR->m_HitsMOs) { @@ -1612,8 +1564,6 @@ namespace RTE { return intersectingAtoms.empty(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float AtomGroup::GetSurfaceArea(int pixelWidth) const { float distributionAmount; @@ -1637,8 +1587,6 @@ namespace RTE { return distributionAmount * m_AreaDistributionSurfaceAreaMultiplier; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::Draw(BITMAP* targetBitmap, const Vector& targetPos, bool useLimbPos, unsigned char color) const { Vector atomPos; Vector normal; @@ -1660,8 +1608,6 @@ namespace RTE { release_bitmap(targetBitmap); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: dan pls. void AtomGroup::GenerateAtomGroup(MOSRotating* ownerMOSRotating) { BITMAP* refSprite = ownerMOSRotating->GetSpriteFrame(); @@ -1867,8 +1813,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AtomGroup::AddAtomToGroup(MOSRotating* ownerMOSRotating, const Vector& spriteOffset, int x, int y, bool calcNormal) { Atom* atomToAdd = new Atom(Vector(static_cast(x) + spriteOffset.GetFloorIntX(), static_cast(y) + spriteOffset.GetFloorIntY()), m_Material, ownerMOSRotating); if (calcNormal) { diff --git a/Source/Entities/AtomGroup.h b/Source/Entities/AtomGroup.h index 564d980fb1..48df80458e 100644 --- a/Source/Entities/AtomGroup.h +++ b/Source/Entities/AtomGroup.h @@ -8,9 +8,7 @@ namespace RTE { class MOSRotating; class LimbPath; - /// /// A group of Atoms that move and interact with the terrain in unison. - /// class AtomGroup : public Entity { public: @@ -19,76 +17,56 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate an AtomGroup object in system memory. Create() should be called before using the object. - /// AtomGroup() { Clear(); } - /// /// Copy constructor method used to instantiate an AtomGroup object identical to an already existing one. - /// - /// An AtomGroup object which is passed in by reference. + /// @param reference An AtomGroup object which is passed in by reference. AtomGroup(const AtomGroup& reference) { Clear(); Create(reference); } - /// /// Makes the AtomGroup object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates an AtomGroup to be identical to another, by deep copy. - /// - /// A reference to the AtomGroup to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the AtomGroup to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const AtomGroup& reference) { return Create(reference, false); } - /// /// Creates an AtomGroup to be identical to another, by deep copy, with the option to only copy Atoms that belong to the reference AtomGroup's owner thereby excluding any Atom subgroups. - /// - /// A reference to the AtomGroup to deep copy. - /// Whether or not to only copy Atoms that belong to the reference AtomGroup's owner directly. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the AtomGroup to deep copy. + /// @param onlyCopyOwnerAtoms Whether or not to only copy Atoms that belong to the reference AtomGroup's owner directly. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const AtomGroup& reference, bool onlyCopyOwnerAtoms); - /// /// Creates an AtomGroup after the silhouette shape of a passed in MOSRotating by dotting the outline of the sprite with Atoms. /// The passed in MOSRotating will also be made the owner of this AtomGroup! Ownership of the MOSRotating is NOT transferred! - /// - /// A pointer to a MOSRotating whose outline will be approximated by Atoms of this AtomGroup, and that will be set as the owner of this AtomGroup. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param ownerMOSRotating A pointer to a MOSRotating whose outline will be approximated by Atoms of this AtomGroup, and that will be set as the owner of this AtomGroup. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(MOSRotating* ownerMOSRotating) { return Create(ownerMOSRotating, m_Material, m_Resolution, m_Depth); } - /// /// Creates an AtomGroup after the silhouette shape of a passed in MOSRotating by dotting the outline of the sprite with Atoms. /// The passed in MOSRotating will also be made the owner of this AtomGroup! Ownership of the MOSRotating is NOT transferred! - /// - /// A pointer to an MOSRotating whose outline will be approximated by Atoms of this AtomGroup, and that will be set as the owner of this AtomGroup. - /// The Material that the Atoms of this AtomGroup should be. - /// Resolution, or density of the Atoms in representing the MOSRotating's outline. Lower value equals higher density. - /// The depth into the sprite that the Atoms should be placed. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param ownerMOSRotating A pointer to an MOSRotating whose outline will be approximated by Atoms of this AtomGroup, and that will be set as the owner of this AtomGroup. + /// @param material The Material that the Atoms of this AtomGroup should be. + /// @param resolution Resolution, or density of the Atoms in representing the MOSRotating's outline. Lower value equals higher density. + /// @param depth The depth into the sprite that the Atoms should be placed. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(MOSRotating* ownerMOSRotating, Material const* material, int resolution = 1, int depth = 0); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up an AtomGroup object before deletion from system memory. - /// ~AtomGroup() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the AtomGroup object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Resets the entire AtomGroup, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Entity::Reset(); @@ -96,132 +74,94 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the current list of Atoms that make up the group. - /// - /// A const reference to the Atom list. + /// @return A const reference to the Atom list. const std::vector& GetAtomList() const { return m_Atoms; } - /// /// Sets the a new list of Atoms that make up the group. - /// - /// List of Atoms that make up the group. + /// @param newAtoms List of Atoms that make up the group. void SetAtomList(const std::vector& newAtoms); - /// /// Gets the current number of Atoms that make up the group. - /// - /// The number of Atoms that make up the group. + /// @return The number of Atoms that make up the group. int GetAtomCount() const { return m_Atoms.size(); } - /// /// Gets max radius of the AtomGroup through the longest magnitude of all the Atom's offsets. - /// - /// The largest magnitude of Atom's offsets, in pixels. + /// @return The largest magnitude of Atom's offsets, in pixels. float CalculateMaxRadius() const; - /// /// Gets the current owner MOSRotating of this AtomGroup. - /// - /// A pointer to the owner. + /// @return A pointer to the owner. MOSRotating* GetOwner() const { return m_OwnerMOSR; } - /// /// Sets the current owner MOSRotating of this AtomGroup. - /// - /// A pointer to the new owner. Ownership is NOT transferred! + /// @param newOwner A pointer to the new owner. Ownership is NOT transferred! void SetOwner(MOSRotating* newOwner); - /// /// Gets the Material of this AtomGroup. - /// - /// A const pointer to the Material. + /// @return A const pointer to the Material. const Material* GetMaterial() const { return (m_Material) ? m_Material : g_SceneMan.GetMaterialFromID(g_MaterialAir); } - /// /// Gets whether this AtomGroup's Atoms are to be automatically generated based on a bitmap, or manually specified. - /// - /// Whether this AtomGroup is auto generated from a bitmap or not. + /// @return Whether this AtomGroup is auto generated from a bitmap or not. bool AutoGenerate() const { return m_AutoGenerate; } - /// /// Gets the resolution (density of Atoms) of this AtomGroup. Higher values mean a less dense and less accurate physical representation of the owner MOSR's graphical representation. - /// - /// The resolution value of this AtomGroup. 0 means the Atoms in this AtomGroup were defined manually. + /// @return The resolution value of this AtomGroup. 0 means the Atoms in this AtomGroup were defined manually. int GetResolution() const { return m_Resolution; } - /// /// Gets the depth Atoms in this AtomGroup are placed off the edge of the owning MOSR's graphical representation outline towards it's center. - /// - /// The depth, in pixels. If 0, Atoms are placed right on the edge of the MOSR outline. + /// @return The depth, in pixels. If 0, Atoms are placed right on the edge of the MOSR outline. int GetDepth() const { return m_Depth; } - /// /// Gets the offset of an Atom in this AtomGroup adjusted to the Owner MOSRotating horizontal flip and rotation. - /// - /// The individual Atom to get the offset for. - /// The offset of an Atom in this AtomGroup adjusted to the Owner MOSRotating horizontal flip and rotation. + /// @param atom The individual Atom to get the offset for. + /// @return The offset of an Atom in this AtomGroup adjusted to the Owner MOSRotating horizontal flip and rotation. Vector GetAdjustedAtomOffset(const Atom* atom) const; - /// /// Gets the current position of this AtomGroup as a limb. - /// - /// Whether to adjust the position for horizontal flip or not. - /// The absolute limb position in the world. + /// @param hFlipped Whether to adjust the position for horizontal flip or not. + /// @return The absolute limb position in the world. Vector GetLimbPos(bool hFlipped = false) const { return m_LimbPos.GetFloored() + m_JointOffset.GetXFlipped(hFlipped); } - /// /// Sets the current position of this AtomGroup as a limb. - /// - /// The Vector with the new absolute position. - /// Whether to adjust the new position for horizontal flip or not. + /// @param newPos The Vector with the new absolute position. + /// @param hFlipped Whether to adjust the new position for horizontal flip or not. void SetLimbPos(const Vector& newPos, bool hFlipped = false) { m_LimbPos = newPos - m_JointOffset.GetXFlipped(hFlipped); } - /// /// Gets the current mass moment of inertia of this AtomGroup. - /// - /// A float with the moment of inertia, in Kg * meter^2. + /// @return A float with the moment of inertia, in Kg * meter^2. float GetMomentOfInertia(); - /// /// Sets the offset of the joint relative to this AtomGroup's origin when used as a limb. - /// - /// The new joint offset. + /// @param newOffset The new joint offset. void SetJointOffset(const Vector& newOffset) { m_JointOffset = newOffset; } #pragma endregion #pragma region Atom Management - /// /// Adds a new Atom into the internal list that makes up this AtomGroup. Ownership of the Atom IS transferred! /// Note, this resets the moment of inertia, which then has to be recalculated. - /// - /// A pointer to an Atom that will pushed onto the end of the list. Ownership IS transferred! - /// The subgroup ID that the new Atom will have within the group. + /// @param newAtom A pointer to an Atom that will pushed onto the end of the list. Ownership IS transferred! + /// @param subgroupID The subgroup ID that the new Atom will have within the group. void AddAtom(Atom* newAtom, long subgroupID = 0) { newAtom->SetSubID(subgroupID); m_Atoms.push_back(newAtom); m_MomentOfInertia = 0.0F; } - /// /// Adds a list of new Atoms to the internal list that makes up this AtomGroup. Ownership of all Atoms in the list IS NOT transferred! - /// - /// A list of pointers to Atoms whose copies will be pushed onto the end of this AtomGroup's list. Ownership IS NOT transferred! - /// The desired subgroup ID for the Atoms being added. - /// An offset that should be applied to all added Atoms. - /// The rotation of the placed Atoms around the specified offset. + /// @param atomList A list of pointers to Atoms whose copies will be pushed onto the end of this AtomGroup's list. Ownership IS NOT transferred! + /// @param subgroupID The desired subgroup ID for the Atoms being added. + /// @param offset An offset that should be applied to all added Atoms. + /// @param offsetRotation The rotation of the placed Atoms around the specified offset. void AddAtoms(const std::vector& atomList, long subgroupID = 0, const Vector& offset = Vector(), const Matrix& offsetRotation = Matrix()); - /// /// Removes all Atoms of a specific subgroup ID from this AtomGroup. - /// - /// The ID of the subgroup of Atoms to remove. - /// Whether any Atoms of that subgroup ID were found and removed. + /// @param removeID The ID of the subgroup of Atoms to remove. + /// @return Whether any Atoms of that subgroup ID were found and removed. bool RemoveAtoms(long removeID); - /// /// Removes all atoms in this AtomGroup, leaving it empty of Atoms. - /// void RemoveAllAtoms() { m_Atoms.clear(); m_SubGroups.clear(); @@ -229,166 +169,131 @@ namespace RTE { m_StoredOwnerMass = 0.0F; } - /// /// Gets whether the AtomGroup contains a subgroup with the given subgroupID. - /// - /// The subgroupID to check for. - /// Whether this AtomGroup contains a subgroup with the given subgroupID. + /// @param subgroupID The subgroupID to check for. + /// @return Whether this AtomGroup contains a subgroup with the given subgroupID. bool ContainsSubGroup(long subgroupID) const { return m_SubGroups.count(subgroupID) != 0; } - /// /// Updates the offsets of a subgroup of Atoms in this AtomGroup. This allows repositioning a subgroup to match the position and rotation of the graphical representation of it's owner MOSR. - /// - /// The desired subgroup ID of the Atoms to update offsets for. - /// The change in offset for the Atoms of the specified subgroup. - /// The rotation of the updated Atoms around the specified offset. - /// Whether any Atoms were found and updated for the specified subgroup. + /// @param subgroupID The desired subgroup ID of the Atoms to update offsets for. + /// @param newOffset The change in offset for the Atoms of the specified subgroup. + /// @param newOffsetRotation The rotation of the updated Atoms around the specified offset. + /// @return Whether any Atoms were found and updated for the specified subgroup. bool UpdateSubAtoms(long subgroupID = 0, const Vector& newOffset = Vector(), const Matrix& newOffsetRotation = Matrix()); #pragma endregion #pragma region Travel - /// /// Makes this AtomGroup travel and react when terrain is hit. Effects are directly applied to the owning MOSRotating. - /// - /// The amount of time in seconds that this AtomGroup is supposed to travel. - /// Whether to call the parent MOSR's OnBounce function upon bouncing against anything or not. - /// Whether to call the parent MOSR's OnSink function upon sinking into anything or not. - /// Whether the Scene has been pre-locked or not. - /// The amount of time remaining of the travelTime passed in, in seconds. This may only possibly be a non-zero if callOnBounce or callOnSink are true. + /// @param travelTime The amount of time in seconds that this AtomGroup is supposed to travel. + /// @param callOnBounce Whether to call the parent MOSR's OnBounce function upon bouncing against anything or not. + /// @param callOnSink Whether to call the parent MOSR's OnSink function upon sinking into anything or not. + /// @param scenePreLocked Whether the Scene has been pre-locked or not. + /// @return The amount of time remaining of the travelTime passed in, in seconds. This may only possibly be a non-zero if callOnBounce or callOnSink are true. float Travel(float travelTime, bool callOnBounce = false, bool callOnSink = false, bool scenePreLocked = false); - /// /// Makes this AtomGroup travel and react when terrain is hit. Effects are applied to the passed in variables. - /// - /// A reference to a Vector with the starting position of the AtomGroup origin. Will be altered according to where the AtomGroup ends up after it's done traveling. - /// A Vector with the total desired velocity of the AtomGroup. Will also be altered according to any collision response. - /// The current rotation Matrix of the AtomGroup. Will be altered accordingly as travel happens. - /// The current desired angular velocity of the owner MOSR, in rad/sec. Will be altered. - /// A bool that will be set to whether the position change involved a wrapping of the scene or not. - /// A float to be filled out with the total magnitudes of all the forces exerted on this through collisions during this frame. - /// The designated mass of the AtomGroup at this time. - /// The amount of time in seconds that this AtomGroup is supposed to travel. - /// Whether to call the parent MOSR's OnBounce function upon bouncing against anything or not. - /// Whether to call the parent MOSR's OnSink function upon sinking into anything or not. - /// Whether the Scene has been pre-locked or not. - /// The amount of time remaining of the travelTime passed in, in seconds. This may only possibly be a non-zero if callOnBounce or callOnSink are true. - /// + /// @param position A reference to a Vector with the starting position of the AtomGroup origin. Will be altered according to where the AtomGroup ends up after it's done traveling. + /// @param velocity A Vector with the total desired velocity of the AtomGroup. Will also be altered according to any collision response. + /// @param rotation The current rotation Matrix of the AtomGroup. Will be altered accordingly as travel happens. + /// @param angularVel The current desired angular velocity of the owner MOSR, in rad/sec. Will be altered. + /// @param didWrap A bool that will be set to whether the position change involved a wrapping of the scene or not. + /// @param totalImpulse A float to be filled out with the total magnitudes of all the forces exerted on this through collisions during this frame. + /// @param mass The designated mass of the AtomGroup at this time. + /// @param travelTime The amount of time in seconds that this AtomGroup is supposed to travel. + /// @param callOnBounce Whether to call the parent MOSR's OnBounce function upon bouncing against anything or not. + /// @param callOnSink Whether to call the parent MOSR's OnSink function upon sinking into anything or not. + /// @param scenePreLocked Whether the Scene has been pre-locked or not. + /// @return The amount of time remaining of the travelTime passed in, in seconds. This may only possibly be a non-zero if callOnBounce or callOnSink are true. + /// @remark /// Pseudocode explaining how this works can be found at: https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/wiki/Notes-on-AtomGroup::Travel. - /// float Travel(Vector& position, Vector& velocity, Matrix& rotation, float& angularVel, bool& didWrap, Vector& totalImpulse, float mass, float travelTime, bool callOnBounce = false, bool callOnSink = false, bool scenePreLocked = false); - /// /// Makes this AtomGroup travel without rotation and react with the scene by pushing against it. - /// - /// A reference to a Vector with the starting position of the AtomGroup origin. Will be altered according to where the AtomGroup ends up after done traveling. - /// A Vector with the total desired velocity of the AtomGroup. - /// The maximum force that the push against other stuff in the scene can have, in Newtons (N). - /// A bool that will be set to whether the position change involved a wrapping of the scene or not. - /// The amount of time in seconds that this AtomGroup is supposed to travel. - /// Whether to call the parent MOSR's OnBounce function upon bouncing against anything or not. - /// Whether to call the parent MOSR's OnSink function upon sinking into anything or not. - /// Whether the Scene has been pre-locked or not. - /// A Vector with the resulting push impulse force, in Newton-second (Ns). + /// @param position A reference to a Vector with the starting position of the AtomGroup origin. Will be altered according to where the AtomGroup ends up after done traveling. + /// @param velocity A Vector with the total desired velocity of the AtomGroup. + /// @param pushForce The maximum force that the push against other stuff in the scene can have, in Newtons (N). + /// @param didWrap A bool that will be set to whether the position change involved a wrapping of the scene or not. + /// @param travelTime The amount of time in seconds that this AtomGroup is supposed to travel. + /// @param callOnBounce Whether to call the parent MOSR's OnBounce function upon bouncing against anything or not. + /// @param callOnSink Whether to call the parent MOSR's OnSink function upon sinking into anything or not. + /// @param scenePreLocked Whether the Scene has been pre-locked or not. + /// @return A Vector with the resulting push impulse force, in Newton-second (Ns). Vector PushTravel(Vector& position, const Vector& velocity, float pushForce, bool& didWrap, float travelTime, bool callOnBounce = false, bool callOnSink = false, bool scenePreLocked = false); - /// /// Makes this AtomGroup travel as a pushing entity relative to the position of the owning MOSRotating. /// If stuff in the scene is hit, resulting forces are be added to the owning MOSRotating's impulse forces. - /// - /// A reference to a Vector with the world position of the limb joint which this AtomGroup is being pushed along. - /// A Vector with the velocity of the owning MOSRotating. - /// A Matrix with the rotation of the owning MOSRotating. - /// A LimbPath which this AtomGroup should travel along. - /// The amount of time in seconds that this AtomGroup is supposed to travel. - /// Pointer to a bool which gets set to true if the LimbPath got restarted during this push. It does NOT get initialized to false! - /// Whether the forces created by this should have rotational leverage on the owner or only have translational effect. - /// The position, relative to the owning actor's position, that we should rotate around. - /// The positional offset to apply to our limb path. - /// Whether the LimbPath passed in could start free of terrain or not. + /// @param jointPos A reference to a Vector with the world position of the limb joint which this AtomGroup is being pushed along. + /// @param velocity A Vector with the velocity of the owning MOSRotating. + /// @param rotation A Matrix with the rotation of the owning MOSRotating. + /// @param limbPath A LimbPath which this AtomGroup should travel along. + /// @param travelTime The amount of time in seconds that this AtomGroup is supposed to travel. + /// @param restarted Pointer to a bool which gets set to true if the LimbPath got restarted during this push. It does NOT get initialized to false! + /// @param affectRotation Whether the forces created by this should have rotational leverage on the owner or only have translational effect. + /// @param rotationOffset The position, relative to the owning actor's position, that we should rotate around. + /// @param rotationOffset The positional offset to apply to our limb path. + /// @return Whether the LimbPath passed in could start free of terrain or not. bool PushAsLimb(const Vector& jointPos, const Vector& velocity, const Matrix& rotation, LimbPath& limbPath, const float travelTime, bool* restarted = nullptr, bool affectRotation = true, Vector rotationOffset = Vector(), Vector positionOffset = Vector()); - /// /// Makes this AtomGroup travel as a lifeless limb, constrained to a radius around the joint pin in the center. - /// - /// A Vector with the world position of the owner MOSRotating. - /// A Vector with the rotated offset of the joint that this should flail around in a radius. - /// The radius/range of the limb this is to simulate. - /// A Vector with the velocity of the owning MOSRotating. - /// A float with the angular velocity in rad/sec of the owning MOSRotating. - /// The mass of this dead weight limb. - /// The amount of time in seconds that this AtomGroup is supposed to travel. + /// @param ownerPos A Vector with the world position of the owner MOSRotating. + /// @param jointOffset A Vector with the rotated offset of the joint that this should flail around in a radius. + /// @param limbRadius The radius/range of the limb this is to simulate. + /// @param velocity A Vector with the velocity of the owning MOSRotating. + /// @param angularVel A float with the angular velocity in rad/sec of the owning MOSRotating. + /// @param mass The mass of this dead weight limb. + /// @param travelTime The amount of time in seconds that this AtomGroup is supposed to travel. void FlailAsLimb(const Vector& ownerPos, const Vector& jointOffset, const float limbRadius, const Vector& velocity, const float angularVel, const float mass, const float travelTime); #pragma endregion #pragma region Collision - /// /// Adds a MOID that this AtomGroup should ignore collisions with during its next Travel sequence. - /// - /// The MOID to add to the ignore list. + /// @param moidToIgnore The MOID to add to the ignore list. void AddMOIDToIgnore(MOID moidToIgnore) { m_IgnoreMOIDs.push_back(moidToIgnore); } - /// /// Checks whether this AtomGroup is set to ignore collisions with a MOSR of a specific MOID. - /// - /// The MOID to check if it is ignored. - /// Whether or not this MOID is being ignored. + /// @param whichMOID The MOID to check if it is ignored. + /// @return Whether or not this MOID is being ignored. bool IsIgnoringMOID(MOID whichMOID) { return (*(m_Atoms.begin()))->IsIgnoringMOID(whichMOID); } - /// /// Clears the list of MOIDs that this AtomGroup is set to ignore collisions with during its next Travel sequence. /// This should be done each frame so that fresh MOIDs can be re-added. (MOIDs are only valid during a frame). - /// void ClearMOIDIgnoreList() { m_IgnoreMOIDs.clear(); } - /// /// Gets whether any of the Atoms in this AtomGroup are on top of terrain pixels. - /// - /// Whether any Atom of this AtomGroup is on top of a terrain pixel. + /// @return Whether any Atom of this AtomGroup is on top of a terrain pixel. bool InTerrain() const; - /// /// Gets the ratio of how many Atoms of this AtomGroup are on top of intact terrain pixels. - /// - /// The ratio of Atoms on top of terrain pixels, from 0 to 1.0. + /// @return The ratio of Atoms on top of terrain pixels, from 0 to 1.0. float RatioInTerrain() const; - /// /// Checks whether any of the Atoms in this AtomGroup are on top of terrain pixels, and if so, attempt to move the OwnerMO out so none of the Atoms are inside any terrain pixels anymore. - /// - /// Current position of the owner MOSR. - /// Only attempt to move out of materials stronger than this specific ID. - /// Whether any intersection was successfully resolved. Will return true even if there wasn't any intersections to begin with. + /// @param position Current position of the owner MOSR. + /// @param strongerThan Only attempt to move out of materials stronger than this specific ID. + /// @return Whether any intersection was successfully resolved. Will return true even if there wasn't any intersections to begin with. bool ResolveTerrainIntersection(Vector& position, unsigned char strongerThan = 0) const; - /// /// Checks whether any of the Atoms in this AtomGroup are on top of MOSprites, and if so, attempt to move the OwnerMO out so none of the Atoms are inside the other MOSprite's silhouette anymore. - /// - /// Current position of the owner MOSR.> - /// Whether all intersections were successfully resolved. + /// @param position Current position of the owner MOSR. + /// @return Whether all intersections were successfully resolved. bool ResolveMOSIntersection(Vector& position); - /// /// Returns the surface area for a given pixel width. - /// - /// Our surface area. + /// @return Our surface area. float GetSurfaceArea(int pixelWidth) const; #pragma endregion #pragma region Debug - /// /// Draws this AtomGroup's current graphical debug representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// Whether to use the limb position of this AtomGroup, or the owner's position. - /// The color to draw the Atoms' pixels as. + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param useLimbPos Whether to use the limb position of this AtomGroup, or the owner's position. + /// @param color The color to draw the Atoms' pixels as. void Draw(BITMAP* targetBitmap, const Vector& targetPos, bool useLimbPos = false, unsigned char color = 34) const; #pragma endregion protected: - /// /// Enumeration for how the AtomGroup's area is distributed. Linear means it acts a 2D line whereas Circle/Square acts as a pseudo-3d circle/square. - /// enum class AreaDistributionType { Linear, Circle, @@ -410,12 +315,10 @@ namespace RTE { bool m_AutoGenerate; //!< Whether the Atoms in this AtomGroup were automatically generated based on a sprite, or manually defined. - /// /// The density of Atoms in this AtomGroup along the outline of the owning MOSR's graphical representation. Higher values mean more pixels are skipped along the outline when placing Atoms. /// For example: a box that is 20x20px will have an outline of 80px, with a resolution value of 10 an Atom will be placed every 10 pixels on this outline, resulting in an AtomGroup that /// consists of 8 Atoms total with 2 Atoms on each plane. Note that the outline isn't actually "unwrapped" and higher values may result in slightly less accurate Atom placement on complex sprites. /// 0 means the Atoms in this AtomGroup were defined manually. 1 means the whole outline will be populated with Atoms, resulting in the most accurate physical representation. - /// int m_Resolution; int m_Depth; //!< The depth Atoms in this AtomGroup are placed off the edge of the owning MOSR's graphical representation outline towards it's center, in pixels. @@ -433,26 +336,20 @@ namespace RTE { private: #pragma region Create Breakdown - /// /// Generates an AtomGroup using the owner MOSRotating's sprite outline. - /// - /// MOSRotating whose outline will be approximated by Atoms of this AtomGroup. + /// @param ownerMOSRotating MOSRotating whose outline will be approximated by Atoms of this AtomGroup. void GenerateAtomGroup(MOSRotating* ownerMOSRotating); - /// /// Create and add an Atom to this AtomGroup's list of Atoms. This is called during GenerateAtomGroup(). - /// - /// MOSRotating whose outline will be approximated by Atoms of this AtomGroup. - /// Sprite offset relative to the owner MOSRotating. - /// X coordinate in the sprite frame. - /// Y coordinate in the sprite frame. - /// Whether to set a normal for the Atom. Should be true for surface Atoms. + /// @param ownerMOSRotating MOSRotating whose outline will be approximated by Atoms of this AtomGroup. + /// @param spriteOffset Sprite offset relative to the owner MOSRotating. + /// @param x X coordinate in the sprite frame. + /// @param y Y coordinate in the sprite frame. + /// @param calcNormal Whether to set a normal for the Atom. Should be true for surface Atoms. void AddAtomToGroup(MOSRotating* ownerMOSRotating, const Vector& spriteOffset, int x, int y, bool calcNormal); #pragma endregion - /// /// Clears all the member variables of this AtomGroup, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/Entities/Attachable.cpp b/Source/Entities/Attachable.cpp index c86309246b..1029c615cb 100644 --- a/Source/Entities/Attachable.cpp +++ b/Source/Entities/Attachable.cpp @@ -11,8 +11,6 @@ namespace RTE { ConcreteClassInfo(Attachable, MOSRotating, 0); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::Clear() { m_Parent = nullptr; m_ParentOffset.Reset(); @@ -53,8 +51,6 @@ namespace RTE { m_PreUpdateHasRunThisFrame = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Attachable::Create() { MOSRotating::Create(); @@ -63,8 +59,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Attachable::Create(const Attachable& reference) { MOSRotating::Create(reference); @@ -107,8 +101,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Attachable::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MOSRotating::ReadProperty(propName, reader)); @@ -150,8 +142,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Attachable::Save(Writer& writer) const { MOSRotating::Save(writer); @@ -182,8 +172,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::TransferJointForces(Vector& jointForces) { if (!m_Parent) { return false; @@ -197,8 +185,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::TransferJointImpulses(Vector& jointImpulses, float jointStiffnessValueToUse, float jointStrengthValueToUse, float gibImpulseLimitValueToUse) { if (!m_Parent) { return false; @@ -250,8 +236,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Attachable::CollectDamage() { if (m_DamageMultiplier != 0) { float totalDamage = m_DamageCount; @@ -268,8 +252,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::SetCollidesWithTerrainWhileAttached(bool collidesWithTerrainWhileAttached) { if (m_CollidesWithTerrainWhileAttached != collidesWithTerrainWhileAttached) { bool previousTerrainCollisionValue = CanCollideWithTerrain(); @@ -281,8 +263,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::CanCollideWithTerrain() const { if (m_CollidesWithTerrainWhileAttached && IsAttached() && GetParent() != GetRootParent()) { if (const Attachable* parentAsAttachable = dynamic_cast(GetParent())) { @@ -292,8 +272,6 @@ namespace RTE { return m_CollidesWithTerrainWhileAttached; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::CollideAtPoint(HitData& hd) { if (m_IgnoresParticlesWhileAttached && m_Parent && !m_Parent->ToDelete() && !dynamic_cast(hd.Body[HITOR])) { return false; @@ -301,8 +279,6 @@ namespace RTE { return MOSRotating::CollideAtPoint(hd); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::ParticlePenetration(HitData& hd) { bool penetrated = MOSRotating::ParticlePenetration(hd); @@ -334,8 +310,6 @@ namespace RTE { return penetrated; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { if (m_Parent) { m_Parent->RemoveAttachable(this, true, true); @@ -343,8 +317,6 @@ namespace RTE { MOSRotating::GibThis(impactImpulse, movableObjectToIgnore); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Attachable::HandlePotentialRadiusAffectingAttachable(const Attachable* attachable) { if (MOSRotating::HandlePotentialRadiusAffectingAttachable(attachable)) { if (IsAttached()) { @@ -355,8 +327,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Attachable::UpdateScripts() { if (m_Parent && !m_AllLoadedScripts.empty() && !ObjectScriptsInitialized()) { RunScriptedFunctionInAppropriateScripts("OnAttach", false, false, {m_Parent}, {}, {}); @@ -365,8 +335,6 @@ namespace RTE { return MOSRotating::UpdateScripts(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::Update() { if (!m_PreUpdateHasRunThisFrame) { PreUpdate(); @@ -425,8 +393,6 @@ namespace RTE { m_PreUpdateHasRunThisFrame = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::PreUpdate() { if (!m_PreUpdateHasRunThisFrame) { if (m_Parent) { @@ -442,8 +408,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::SetMass(const float newMass) { float currentMass = GetMass(); if (newMass != currentMass) { @@ -455,8 +419,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::UpdateAttachableAndWoundMass(float oldAttachableOrWoundMass, float newAttachableOrWoundMass) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; MOSRotating::UpdateAttachableAndWoundMass(oldAttachableOrWoundMass, newAttachableOrWoundMass); @@ -465,8 +427,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::AddAttachable(Attachable* attachable, const Vector& parentOffsetToSet) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; MOSRotating::AddAttachable(attachable, parentOffsetToSet); @@ -475,8 +435,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Attachable* Attachable::RemoveAttachable(Attachable* attachable, bool addToMovableMan, bool addBreakWounds) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; Attachable* removedAttachable = MOSRotating::RemoveAttachable(attachable, addToMovableMan, addBreakWounds); @@ -486,8 +444,6 @@ namespace RTE { return removedAttachable; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::AddWound(AEmitter* woundToAdd, const Vector& parentOffsetToSet, bool checkGibWoundLimit) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; MOSRotating::AddWound(woundToAdd, parentOffsetToSet, checkGibWoundLimit); @@ -496,8 +452,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Attachable::RemoveWounds(int numberOfWoundsToRemove, bool includeAttachablesWithAPositiveDamageMultiplier, bool includeAttachablesWithANegativeDamageMultiplier, bool includeAttachablesWithNoDamageMultiplier) { float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; float result = MOSRotating::RemoveWounds(numberOfWoundsToRemove, includeAttachablesWithAPositiveDamageMultiplier, includeAttachablesWithANegativeDamageMultiplier, includeAttachablesWithNoDamageMultiplier); @@ -507,8 +461,6 @@ namespace RTE { return result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::SetParent(MOSRotating* newParent) { if (newParent == m_Parent) { return; @@ -580,8 +532,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::UpdatePositionAndJointPositionBasedOnOffsets() { if (m_Parent) { m_JointPos = m_Parent->GetPos() + m_Parent->RotateOffset(GetParentOffset()); @@ -592,8 +542,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::AddOrRemoveAtomsFromRootParentAtomGroup(bool addAtoms, bool propagateToChildAttachables) { if (IsAttached()) { MOSRotating* rootParentAsMOSR = dynamic_cast(GetRootParent()); @@ -619,8 +567,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Attachable::AddOrRemovePieSlicesAndListenersFromPieMenu(PieMenu* pieMenuToModify, bool addToPieMenu) { RTEAssert(pieMenuToModify, "Cannot add or remove Attachable PieSlices and listeners from a non-existant PieMenu."); if (addToPieMenu) { diff --git a/Source/Entities/Attachable.h b/Source/Entities/Attachable.h index 141265e425..a254782352 100644 --- a/Source/Entities/Attachable.h +++ b/Source/Entities/Attachable.h @@ -8,9 +8,7 @@ namespace RTE { class AEmitter; - /// /// An articulated, detachable part of an Actor's body. - /// class Attachable : public MOSRotating { friend class MOSRotating; @@ -21,35 +19,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a Attachable object in system memory. Create() should be called before using the object. - /// Attachable() { Clear(); } - /// /// Makes the Attachable object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates an Attachable to be identical to another, by deep copy. - /// - /// A reference to the Attachable to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Attachable to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Attachable& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up an Attachable object before deletion from system memory. - /// ~Attachable() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the Attachable object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { MOSRotating::Destroy(); @@ -57,9 +45,7 @@ namespace RTE { Clear(); } - /// /// Resets the entire Attachable, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); MOSRotating::Reset(); @@ -67,502 +53,357 @@ namespace RTE { #pragma endregion #pragma region Parent Getters and Setters - /// /// Gets the MOSRotating which is the parent of this Attachable. - /// - /// A pointer to the parent of this Attachable. + /// @return A pointer to the parent of this Attachable. MOSRotating* GetParent() override { return m_Parent; } - /// /// Gets the MOSRotating which is the parent of this Attachable. - /// - /// A pointer to the parent of this Attachable. + /// @return A pointer to the parent of this Attachable. const MOSRotating* GetParent() const override { return m_Parent; } - /// /// Indicates whether this Attachable is attached to an MOSRotating parent or not. - /// - /// Whether it's attached or not. + /// @return Whether it's attached or not. bool IsAttached() const { return m_Parent != nullptr; } - /// /// Indicates whether this Attachable is attached to the specified MOSRotating or not. - /// - /// A pointer to which MOSRotating you want to check for. - /// Whether it's attached or not. + /// @param parentToCheck A pointer to which MOSRotating you want to check for. + /// @return Whether it's attached or not. bool IsAttachedTo(const MOSRotating* parentToCheck) const { return m_Parent == parentToCheck; } - /// /// Gets the MO which is the ultimate root parent of this Attachable and its parent. - /// - /// A pointer to the highest root parent of this Attachable. + /// @return A pointer to the highest root parent of this Attachable. MovableObject* GetRootParent() override { return m_Parent ? m_Parent->GetRootParent() : this; } - /// /// Gets the MO which is the ultimate root parent of this Attachable and its parent. - /// - /// A pointer to the highest root parent of this Attachable. + /// @return A pointer to the highest root parent of this Attachable. const MovableObject* GetRootParent() const override { return m_Parent ? m_Parent->GetRootParent() : this; } - /// /// Gets the stored offset between this Attachable's parent's position and the joint position. This should be maintained by the parent. - /// - /// A const reference Vector describing the offset from the parent's position to the joint position. + /// @return A const reference Vector describing the offset from the parent's position to the joint position. const Vector& GetParentOffset() const { return m_ParentOffset; } - /// /// Sets the stored offset between this Attachable's parent's Pos and the joint position. This should be maintained by the parent. - /// - /// A const reference to the new parent offset. + /// @param newParentOffset A const reference to the new parent offset. void SetParentOffset(const Vector& newParentOffset) { m_ParentOffset = newParentOffset; } - /// /// Gets whether this Attachable is to be drawn after (in front of) or before (behind) its parent. - /// - /// Whether this Attachable is to be drawn after its parent or not. + /// @return Whether this Attachable is to be drawn after its parent or not. bool IsDrawnAfterParent() const override { return m_DrawAfterParent; } - /// /// Sets whether this Attachable is to be drawn after (in front of) or before (behind) its parent. - /// - /// Whether this Attachable is to be drawn after its parent. + /// @param drawAfterParent Whether this Attachable is to be drawn after its parent. void SetDrawnAfterParent(bool drawAfterParent) { m_DrawAfterParent = drawAfterParent; } - /// /// Gets whether this Attachable should be drawn normally by its parent. /// Some attachables (e.g. AEmitter flashes) require custom handling for when they should or shouldn't draw, to be done by the specific parent class. - /// - /// Whether this Attachable should be drawn normally by its parent. + /// @return Whether this Attachable should be drawn normally by its parent. bool IsDrawnNormallyByParent() const { return m_DrawnNormallyByParent; } - /// /// Sets whether this Attachable should be drawn normally by its parent. /// Some attachables (e.g. AEmitter flashes) require custom handling for when they should or shouldn't draw, to be done by the specific parent class. - /// - /// Whether this Attachable should be drawn normally by its parent. + /// @param drawnNormallyByParent Whether this Attachable should be drawn normally by its parent. void SetDrawnNormallyByParent(bool drawnNormallyByParent) { m_DrawnNormallyByParent = drawnNormallyByParent; } - /// /// Gets whether this Attachable will be deleted when removed from its parent. Has no effect until the Attachable has been added to a parent. - /// - /// Whether this Attachable is marked to be deleted when removed from its parent or not. + /// @return Whether this Attachable is marked to be deleted when removed from its parent or not. bool GetDeleteWhenRemovedFromParent() const { return m_DeleteWhenRemovedFromParent; } - /// /// Sets whether this Attachable will be deleted when removed from its parent. - /// - /// Whether this Attachable should be deleted when removed from its parent. + /// @param deleteWhenRemovedFromParent Whether this Attachable should be deleted when removed from its parent. void SetDeleteWhenRemovedFromParent(bool deleteWhenRemovedFromParent) { m_DeleteWhenRemovedFromParent = deleteWhenRemovedFromParent; } - /// /// Gets whether this Attachable will gib when removed from its parent. Has no effect until the Attachable has been added to a parent. - /// - /// Whether this Attachable is marked to gib when removed from its parent or not. + /// @return Whether this Attachable is marked to gib when removed from its parent or not. bool GetGibWhenRemovedFromParent() const { return m_GibWhenRemovedFromParent; } - /// /// Sets whether this Attachable will gib when removed from its parent. - /// - /// Whether this Attachable should gib when removed from its parent. + /// @param gibWhenRemovedFromParent Whether this Attachable should gib when removed from its parent. void SetGibWhenRemovedFromParent(bool gibWhenRemovedFromParent) { m_GibWhenRemovedFromParent = gibWhenRemovedFromParent; } - /// /// Gets whether forces transferred from this Attachable should be applied at its parent's offset (rotated to match the parent) where they will produce torque, or directly at its parent's position. - /// - /// Whether forces transferred from this Attachable should be applied at an offset. + /// @return Whether forces transferred from this Attachable should be applied at an offset. bool GetApplyTransferredForcesAtOffset() const { return m_ApplyTransferredForcesAtOffset; } - /// /// Sets whether forces transferred from this Attachable should be applied at its parent's offset (rotated to match the parent) where they will produce torque, or directly at its parent's position. - /// - /// Whether forces transferred from this Attachable should be applied at an offset. + /// @param appliesTransferredForcesAtOffset Whether forces transferred from this Attachable should be applied at an offset. void SetApplyTransferredForcesAtOffset(bool appliesTransferredForcesAtOffset) { m_ApplyTransferredForcesAtOffset = appliesTransferredForcesAtOffset; } #pragma endregion #pragma region Parent Gib Handling Getters and Setters - /// /// Gets the percentage chance that this Attachable will gib when its parent does. 0 means never, 1 means always. - /// - /// A float with the percentage chance this Attachable will gib when its parent gibs. + /// @return A float with the percentage chance this Attachable will gib when its parent gibs. float GetGibWithParentChance() const { return m_GibWithParentChance; } - /// /// Sets the percentage chance that this Attachable will gib when its parent does. 0 means never, 1 means always. - /// - /// A float describing the percentage chance this Attachable will gib when its parent gibs. + /// @param gibWithParentChance A float describing the percentage chance this Attachable will gib when its parent gibs. void SetGibWithParentChance(float gibWithParentChance) { m_GibWithParentChance = gibWithParentChance; } - /// /// Gets the multiplier for how strongly this Attachable's parent's gib blast strength will be applied to it when its parent's gibs - /// - /// A float with the parent gib blast strength multiplier of this Attachable. + /// @return A float with the parent gib blast strength multiplier of this Attachable. float GetParentGibBlastStrengthMultiplier() const { return m_ParentGibBlastStrengthMultiplier; } - /// /// Sets the multiplier for how strongly this Attachable's parent's gib blast strength will be applied to it when its parent's gibs - /// - /// A float describing the parent gib blast strength multiplier of this Attachable. + /// @param parentGibBlastStrengthMultiplier A float describing the parent gib blast strength multiplier of this Attachable. void SetParentGibBlastStrengthMultiplier(float parentGibBlastStrengthMultiplier) { m_ParentGibBlastStrengthMultiplier = parentGibBlastStrengthMultiplier; } #pragma endregion #pragma region Temporary Handling for Wounds, to be Replaced by a Wound Object in Future - /// /// Gets whether or not this Attachable is a wound, as determined by its parent MOSR. - /// - /// Whether or not this Attachable is a wound. + /// @return Whether or not this Attachable is a wound. bool IsWound() const { return m_IsWound; } - /// /// Sets whether or not this Attachable is a wound, to be done by its parent MOSR. - /// - /// Whether or not this Attachable should be a wound. + /// @param isWound Whether or not this Attachable should be a wound. void SetIsWound(bool isWound) { m_IsWound = isWound; } #pragma endregion #pragma region Joint Getters and Setters - /// /// Gets the amount of impulse force the joint of this Attachable can handle before breaking. - /// - /// A float with the max tolerated impulse force in kg * m/s. + /// @return A float with the max tolerated impulse force in kg * m/s. float GetJointStrength() const { return m_JointStrength; } - /// /// Sets the amount of impulse force the joint of this Attachable can handle before breaking. - /// - /// A float describing the max tolerated impulse force in Newtons (kg * m/s). + /// @param jointStrength A float describing the max tolerated impulse force in Newtons (kg * m/s). void SetJointStrength(float jointStrength) { m_JointStrength = jointStrength; } - /// /// Gets the stiffness scalar of the joint of this Attachable, normalized between 0 and 1.0. /// 1.0 means impulse forces on this attachable will be transferred to the parent with 100% strength, 0 means they will not transfer at all. - /// - /// The normalized stiffness scalar of this Attachable's joint. + /// @return The normalized stiffness scalar of this Attachable's joint. float GetJointStiffness() const { return m_JointStiffness; } - /// /// Sets the stiffness scalar of the joint of this Attachable, limited between 0 and 1.0. /// 1.0 means impulse forces on this attachable will be transferred to the parent with 100% strength, 0 means they will not transfer at all. - /// - /// A float describing the normalized stiffness scalar of this Attachable's joint. It will automatically be limited between 0 and 1.0. + /// @param jointStiffness A float describing the normalized stiffness scalar of this Attachable's joint. It will automatically be limited between 0 and 1.0. virtual void SetJointStiffness(float jointStiffness) { m_JointStiffness = std::clamp(jointStiffness, 0.0F, 1.0F); } - /// /// Gets the offset of the joint (the point around which this Attachable and its parent hinge) from this Attachable's center of mass/origin. - /// - /// A const reference Vector describing the offset of the joint relative to this Attachable's origin/center of mass position. + /// @return A const reference Vector describing the offset of the joint relative to this Attachable's origin/center of mass position. const Vector& GetJointOffset() const { return m_JointOffset; } - /// /// Sets the offset of the joint (the point around which this Attachable and its parent hinge) from this Attachable's center of mass/origin. - /// - /// A Vector describing the offset of the joint relative to the this Attachable's origin/center of mass position. + /// @param newJointOffset A Vector describing the offset of the joint relative to the this Attachable's origin/center of mass position. void SetJointOffset(const Vector& newJointOffset) { m_JointOffset = newJointOffset; } - /// /// Gets the absolute position of the joint that the parent of this Attachable sets upon Update(). - /// - /// A Vector describing the current absolute position of the joint. + /// @return A Vector describing the current absolute position of the joint. const Vector& GetJointPos() const { return m_JointPos; } #pragma endregion #pragma region Force Transferral - /// /// Bundles up all the accumulated forces of this Attachable and calculates how they transfer to the joint, and therefore to the parent. /// If the accumulated forces exceed the joint strength of this Attachable, the jointForces Vector will be filled to the limit and false will be returned. /// Additionally, in this case, the Attachable will remove itself from its parent. - /// - /// A vector that will have the forces affecting the joint ADDED to it. - /// False if the Attachable has no parent or its accumulated forces are greater than its joint strength, otherwise true. + /// @param jointForces A vector that will have the forces affecting the joint ADDED to it. + /// @return False if the Attachable has no parent or its accumulated forces are greater than its joint strength, otherwise true. bool TransferJointForces(Vector& jointForces); - /// /// Bundles up all the accumulated impulse forces of this Attachable and calculates how they transfer to the joint, and therefore to the parent. /// If the accumulated impulse forces exceed the joint strength or gib impulse limit of this Attachable, the jointImpulses Vector will be filled up to that limit and false will be returned. /// Additionally, in this case, the Attachable will remove itself from its parent and gib itself if appropriate. - /// - /// A vector that will have the impulse forces affecting the joint ADDED to it. - /// An optional override for the Attachable's joint stiffness for this function call. Primarily used to allow subclasses to perform special behavior. - /// An optional override for the Attachable's joint strength for this function call. Primarily used to allow subclasses to perform special behavior. - /// An optional override for the Attachable's gib impulse limit for this function call. Primarily used to allow subclasses to perform special behavior. - /// False if the Attachable has no parent or its accumulated forces are greater than its joint strength or gib impulse limit, otherwise true. + /// @param jointImpulses A vector that will have the impulse forces affecting the joint ADDED to it. + /// @param jointStiffnessValueToUse An optional override for the Attachable's joint stiffness for this function call. Primarily used to allow subclasses to perform special behavior. + /// @param jointStrengthValueToUse An optional override for the Attachable's joint strength for this function call. Primarily used to allow subclasses to perform special behavior. + /// @param gibImpulseLimitValueToUse An optional override for the Attachable's gib impulse limit for this function call. Primarily used to allow subclasses to perform special behavior. + /// @return False if the Attachable has no parent or its accumulated forces are greater than its joint strength or gib impulse limit, otherwise true. virtual bool TransferJointImpulses(Vector& jointImpulses, float jointStiffnessValueToUse = -1, float jointStrengthValueToUse = -1, float gibImpulseLimitValueToUse = -1); #pragma endregion #pragma region Damage and Wound Management - /// /// Adds the specified number of damage points to this attachable. - /// - /// The amount of damage to add. + /// @param damageAmount The amount of damage to add. void AddDamage(float damageAmount) { m_DamageCount += damageAmount; } - /// /// Calculates the amount of damage this Attachable has sustained since the last time this method was called and returns it, modified by the Attachable's damage multiplier. /// This should normally be called AFTER updating this Attachable in order to get the correct damage for a given frame. - /// - /// A float with the damage accumulated, multiplied by the Attachable's damage multiplier, since the last time this method was called. + /// @return A float with the damage accumulated, multiplied by the Attachable's damage multiplier, since the last time this method was called. float CollectDamage(); - /// /// Gets the AEmitter that represents the wound added to this Attachable when it gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! - /// - /// A const pointer to the break wound AEmitter. + /// @return A const pointer to the break wound AEmitter. const AEmitter* GetBreakWound() const { return m_BreakWound; } - /// /// Sets the AEmitter that represents the wound added to this Attachable when it gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! - /// - /// The AEmitter to use for this Attachable's breakwound. + /// @param breakWound The AEmitter to use for this Attachable's breakwound. void SetBreakWound(AEmitter* breakWound) { m_BreakWound = breakWound; } - /// /// Gets the AEmitter that represents the wound added to this Attachable's parent when this Attachable gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! - /// - /// A const pointer to the parent break wound AEmitter. + /// @return A const pointer to the parent break wound AEmitter. const AEmitter* GetParentBreakWound() const { return m_ParentBreakWound; } - /// /// Sets the AEmitter that represents the wound added to this Attachable's parent when this Attachable gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! - /// - /// The AEmitter to use for the parent's breakwound. + /// @param breakWound The AEmitter to use for the parent's breakwound. void SetParentBreakWound(AEmitter* breakWound) { m_ParentBreakWound = breakWound; } #pragma endregion #pragma region Inherited Value Getters and Setters - /// /// Gets whether or not this Attachable inherits its parent's HFlipped value, i.e. whether it has its HFlipped value reset to match/reverse its parent's every frame, if attached. /// 0 means no inheritance, 1 means normal inheritance, anything else means reversed inheritance (i.e. if the parent's HFlipped value is true, this Attachable's HFlipped value will be false). - /// - /// Whether or not this Attachable inherits its parent's HFlipped value. + /// @return Whether or not this Attachable inherits its parent's HFlipped value. int InheritsHFlipped() const { return m_InheritsHFlipped; } - /// /// Sets whether or not this Attachable inherits its parent's HFlipped value, i.e. whether it has its HFlipped value reset to match/reverse its parent's every frame, if attached. /// 0 means no inheritance, 1 means normal inheritance, anything else means reversed inheritance (i.e. if the parent's HFlipped value is true, this Attachable's HFlipped value will be false). - /// - /// Whether or not to inherit its parent's HFlipped value. + /// @param inheritsRotAngle Whether or not to inherit its parent's HFlipped value. void SetInheritsHFlipped(int inheritsHFlipped) { m_InheritsHFlipped = inheritsHFlipped; } - /// /// Gets whether or not this Attachable inherits its RotAngle from its parent, i.e. whether it has its RotAngle reset to match its parent every frame, if attached. - /// - /// Whether or not this Attachable inherits its parent's RotAngle. + /// @return Whether or not this Attachable inherits its parent's RotAngle. bool InheritsRotAngle() const { return m_InheritsRotAngle; } - /// /// Sets whether or not this Attachable inherits its RotAngle from its parent, i.e. whether it has its RotAngle reset to match its parent every frame, if attached. - /// - /// Whether or not to inherit its parent's RotAngle. + /// @param inheritsRotAngle Whether or not to inherit its parent's RotAngle. void SetInheritsRotAngle(bool inheritsRotAngle) { m_InheritsRotAngle = inheritsRotAngle; } - /// /// Gets the offset of this Attachable's rotation angle from its parent. Only actually applied if the Attachable is set to inherit its parent's rotation angle. - /// - /// This Attachable's inherited rotation angle offset. + /// @return This Attachable's inherited rotation angle offset. float GetInheritedRotAngleOffset() const { return m_InheritedRotAngleOffset; } - /// /// Sets the offset of this Attachable's rotation angle from its parent. Only actually applied if the Attachable is set to inherit its parent's rotation angle. - /// - /// Thee new rotation angle offset for this Attachable. + /// @param inheritedRotAngleOffset Thee new rotation angle offset for this Attachable. void SetInheritedRotAngleOffset(float inheritedRotAngleOffset) { m_InheritedRotAngleOffset = inheritedRotAngleOffset; } - /// /// Gets whether or not this Attachable inherits its Frame from its parent, if attached. - /// - /// Whether or not this Attachable inherits its parent's Frame. + /// @return Whether or not this Attachable inherits its parent's Frame. bool InheritsFrame() const { return m_InheritsFrame; } - /// /// Sets whether or not this Attachable inherits its Frame from its parent, if attached. - /// - /// Whether or not to inherit its parent's Frame. + /// @param inheritsFrame Whether or not to inherit its parent's Frame. void SetInheritsFrame(bool inheritsFrame) { m_InheritsFrame = inheritsFrame; } #pragma endregion #pragma region Collision Management - /// /// Gets the subgroup ID of this' Atoms. - /// - /// The subgroup ID of this' Atoms. + /// @return The subgroup ID of this' Atoms. long GetAtomSubgroupID() const { return m_AtomSubgroupID; } - /// /// Sets the subgroup ID of this' Atoms - /// - /// A long describing the new subgroup id of this' Atoms. + /// @param newID A long describing the new subgroup id of this' Atoms. void SetAtomSubgroupID(long subgroupID = 0) { m_AtomSubgroupID = subgroupID; } - /// /// Gets whether this Attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. /// Attachables with Attachable parents that don't collide with terrain will not collide with terrain. This chains up to the root parent. - /// - /// If true, terrain collisions while attached are enabled and atoms are present in parent AtomGroup. + /// @return If true, terrain collisions while attached are enabled and atoms are present in parent AtomGroup. bool GetCollidesWithTerrainWhileAttached() const { return m_CollidesWithTerrainWhileAttached; } - /// /// Sets whether this Attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. - /// - /// Whether this attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. + /// @param collidesWithTerrainWhileAttached Whether this attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. void SetCollidesWithTerrainWhileAttached(bool collidesWithTerrainWhileAttached); - /// /// Gets whether this Attachable is currently able to collide with terrain, taking into account its terrain collision settings and those of its parent and so on. - /// - /// Whether this Attachable is currently able to collide with terrain, taking into account its terrain collision settings and those of its parent and so on. + /// @return Whether this Attachable is currently able to collide with terrain, taking into account its terrain collision settings and those of its parent and so on. bool CanCollideWithTerrain() const; - /// /// Gets whether this Attachable currently ignores collisions with single-atom particles. - /// - /// >Whether this attachable ignores collisions with single-atom particles. + /// @return >Whether this attachable ignores collisions with single-atom particles. bool GetIgnoresParticlesWhileAttached() const { return m_IgnoresParticlesWhileAttached; } - /// /// Sets whether this Attachable currently ignores collisions with single-atom particles. - /// - /// Whether this attachable ignores collisions with single-atom particles. + /// @param collidesWithTerrainWhileAttached Whether this attachable ignores collisions with single-atom particles. void SetIgnoresParticlesWhileAttached(bool ignoresParticlesWhileAttached) { m_IgnoresParticlesWhileAttached = ignoresParticlesWhileAttached; } #pragma endregion #pragma region Override Methods - /// /// Calculates the collision response when another MO's Atom collides with this MO's physical representation. /// The effects will be applied directly to this MO, and also represented in the passed in HitData. - /// - /// Reference to the HitData struct which describes the collision. This will be modified to represent the results of the collision. - /// Whether the collision has been deemed valid. If false, then disregard any impulses in the HitData. + /// @param hitData Reference to the HitData struct which describes the collision. This will be modified to represent the results of the collision. + /// @return Whether the collision has been deemed valid. If false, then disregard any impulses in the HitData. bool CollideAtPoint(HitData& hitData) override; - /// /// Determines whether a particle which has hit this MO will penetrate, and if so, whether it gets lodged or exits on the other side of this MO. /// Appropriate effects will be determined and applied ONLY IF there was penetration! If not, nothing will be affected. - /// - /// The HitData describing the collision in detail, the impulses have to have been filled out! - /// + /// @param hitData The HitData describing the collision in detail, the impulses have to have been filled out! + /// @return /// Whether the particle managed to penetrate into this MO or not. /// If something other than an MOPixel or MOSParticle is being passed in as the hitor, false will trivially be returned here. - /// bool ParticlePenetration(HitData& hitData) override; - /// /// Destroys this Attachable and creates its specified Gibs in its place with appropriate velocities. /// Any Attachables are removed and also given appropriate velocities. - /// - /// The impulse (kg * m/s) of the impact causing the gibbing to happen. - /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + /// @param impactImpulse The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// @param movableObjectToIgnore A pointer to an MO which the Gibs and Attachables should not be colliding with. void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr) override; - /// /// Checks if the given Attachable should affect radius, and handles it if it should. - /// - /// The Attachable to check. - /// Whether the radius affecting Attachable changed as a result of this call. + /// @param attachable The Attachable to check. + /// @return Whether the radius affecting Attachable changed as a result of this call. bool HandlePotentialRadiusAffectingAttachable(const Attachable* attachable) override; - /// /// Updates this Attachable's Lua scripts. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int UpdateScripts() override; - /// /// Updates this Attachable. Supposed to be done every frame. /// NOTE - Attachable subclasses that do things before calling Attachable::Update should make sure to call Attachable::PreUpdate. - /// void Update() override; #pragma endregion - /// /// Pre-update method that should be run by all Attachable sub-classes that do things before calling Attachable::Update. - /// void PreUpdate(); #pragma region Override Methods for Handling Mass - /// /// Sets the mass of this Attachable. - /// - /// A float specifying the new mass value in Kilograms (kg). + /// @param newMass A float specifying the new mass value in Kilograms (kg). void SetMass(const float newMass) final; - /// /// Updates the total mass of Attachables and wounds for this Attachable, intended to be used when Attachables' masses get modified. Simply subtracts the old mass and adds the new one. - /// - /// The mass the Attachable or wound had before its mass was modified. - /// The up-to-date mass of the Attachable or wound after its mass was modified. + /// @param oldAttachableOrWoundMass The mass the Attachable or wound had before its mass was modified. + /// @param newAttachableOrWoundMass The up-to-date mass of the Attachable or wound after its mass was modified. void UpdateAttachableAndWoundMass(float oldAttachableOrWoundMass, float newAttachableOrWoundMass) final; - /// /// Adds the passed in Attachable the list of Attachables and sets its parent to this Attachable. - /// - /// The Attachable to add. + /// @param attachable The Attachable to add. void AddAttachable(Attachable* attachable) final { MOSRotating::AddAttachable(attachable); } - /// /// Adds the passed in Attachable the list of Attachables, changes its parent offset to the passed in Vector, and sets its parent to this Attachable. - /// - /// The Attachable to add. - /// The Vector to set as the Attachable's parent offset. + /// @param attachable The Attachable to add. + /// @param parentOffsetToSet The Vector to set as the Attachable's parent offset. void AddAttachable(Attachable* attachable, const Vector& parentOffsetToSet) final; - /// /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. - /// - /// The UniqueID of the Attachable to remove. - /// A pointer to the removed Attachable. Ownership IS transferred! + /// @param attachableUniqueID The UniqueID of the Attachable to remove. + /// @return A pointer to the removed Attachable. Ownership IS transferred! Attachable* RemoveAttachable(long attachableUniqueID) final { return MOSRotating::RemoveAttachable(attachableUniqueID); } - /// /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. - /// - /// The UniqueID of the Attachable to remove. - /// Whether or not to add the Attachable to MovableMan once it has been removed. - /// Whether or not to add break wounds to the removed Attachable and this Attachable. - /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! + /// @param attachableUniqueID The UniqueID of the Attachable to remove. + /// @param addToMovableMan Whether or not to add the Attachable to MovableMan once it has been removed. + /// @param addBreakWounds Whether or not to add break wounds to the removed Attachable and this Attachable. + /// @return A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! Attachable* RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds) final { return MOSRotating::RemoveAttachable(attachableUniqueID, addToMovableMan, addBreakWounds); } - /// /// Removes the passed in Attachable and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. - /// - /// The Attachable to remove. - /// A pointer to the removed Attachable. Ownership IS transferred! + /// @param attachable The Attachable to remove. + /// @return A pointer to the removed Attachable. Ownership IS transferred! Attachable* RemoveAttachable(Attachable* attachable) final { return MOSRotating::RemoveAttachable(attachable); } - /// /// Removes the passed in Attachable and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. - /// - /// The Attachable to remove. - /// Whether or not to add the Attachable to MovableMan once it has been removed. - /// Whether or not to add break wounds to the removed Attachable and this Attachable. - /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! + /// @param attachable The Attachable to remove. + /// @param addToMovableMan Whether or not to add the Attachable to MovableMan once it has been removed. + /// @param addBreakWounds Whether or not to add break wounds to the removed Attachable and this Attachable. + /// @return A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! Attachable* RemoveAttachable(Attachable* attachable, bool addToMovableMan, bool addBreakWounds) final; - /// /// Adds the passed in wound AEmitter to the list of wounds and changes its parent offset to the passed in Vector. - /// - /// The wound AEmitter to add. - /// The vector to set as the wound AEmitter's parent offset. - /// Whether to gib this Attachable if adding this wound raises its wound count past its gib wound limit. Defaults to true. + /// @param woundToAdd The wound AEmitter to add. + /// @param parentOffsetToSet The vector to set as the wound AEmitter's parent offset. + /// @param checkGibWoundLimit Whether to gib this Attachable if adding this wound raises its wound count past its gib wound limit. Defaults to true. void AddWound(AEmitter* woundToAdd, const Vector& parentOffsetToSet, bool checkGibWoundLimit = true) final; - /// /// Removes the specified number of wounds from this Attachable, and returns damage caused by these removed wounds. /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. - /// - /// The number of wounds that should be removed. - /// The amount of damage caused by these wounds, taking damage multipliers into account. + /// @param numberOfWoundsToRemove The number of wounds that should be removed. + /// @return The amount of damage caused by these wounds, taking damage multipliers into account. float RemoveWounds(int numberOfWoundsToRemove) final { return MOSRotating::RemoveWounds(numberOfWoundsToRemove); } - /// /// Removes the specified number of wounds from this Attachable, and returns damage caused by these removed wounds. /// Optionally removes wounds from Attachables (and their Attachables, etc.) that match the conditions set by the provided inclusion parameters. - /// - /// The number of wounds that should be removed. - /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this Attachable) when wounded. - /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this Attachable) when wounded. - /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this Attachable) when wounded. - /// The amount of damage caused by these wounds, taking damage multipliers into account. + /// @param numberOfWoundsToRemove The number of wounds that should be removed. + /// @param includePositiveDamageAttachables Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this Attachable) when wounded. + /// @param includeNegativeDamageAttachables Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this Attachable) when wounded. + /// @param includeNoDamageAttachables Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this Attachable) when wounded. + /// @return The amount of damage caused by these wounds, taking damage multipliers into account. float RemoveWounds(int numberOfWoundsToRemove, bool includeAttachablesWithAPositiveDamageMultiplier, bool includeAttachablesWithANegativeDamageMultiplier, bool includeAttachablesWithNoDamageMultiplier) override; #pragma endregion @@ -608,36 +449,26 @@ namespace RTE { float m_PrevRotAngleOffset; //!< The previous frame's difference between this Attachable's RotAngle and it's root parent's RotAngle. bool m_PreUpdateHasRunThisFrame; //!< Whether or not PreUpdate has run this frame. PreUpdate, like Update, should only run once per frame. - /// /// Sets this Attachable's parent MOSRotating, and also sets its Team based on its parent and, if the Attachable is set to collide, adds/removes Atoms to its new/old parent. - /// - /// A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! + /// @param newParent A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! virtual void SetParent(MOSRotating* newParent); private: - /// /// Updates the position of this Attachable based on its parent offset and joint offset. Used during update and when something sets these offsets through setters. - /// void UpdatePositionAndJointPositionBasedOnOffsets(); - /// /// Turns on/off this Attachable's terrain collisions while it is attached by adding/removing its Atoms to/from its root parent's AtomGroup. - /// - /// Whether to add this Attachable's Atoms to the root parent's AtomGroup or remove them. - /// Whether this Atom addition or removal should be propagated to any child Attachables (as appropriate). + /// @param addAtoms Whether to add this Attachable's Atoms to the root parent's AtomGroup or remove them. + /// @param propagateToChildAttachables Whether this Atom addition or removal should be propagated to any child Attachables (as appropriate). void AddOrRemoveAtomsFromRootParentAtomGroup(bool addAtoms, bool propagateToChildAttachables); - /// /// Add or removes this Attachable's PieSlices and PieMenu listeners, as well as those of any of its child Attachables, from the given PieMenu (should be the root parent's PieMenu). /// Note that listeners are only added for Attachables that have at least one script file with the appropriate Lua function. - /// - /// The PieMenu to modify, passed in to keep the recursion simple and clean. - /// Whether to add this Attachable's PieSlices and listeners to, or remove them from, the root parent's PieMenu. + /// @param pieMenuToModify The PieMenu to modify, passed in to keep the recursion simple and clean. + /// @param addToPieMenu Whether to add this Attachable's PieSlices and listeners to, or remove them from, the root parent's PieMenu. void AddOrRemovePieSlicesAndListenersFromPieMenu(PieMenu* pieMenuToModify, bool addToPieMenu); - /// /// Clears all the member variables of this Attachable, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/BunkerAssembly.cpp b/Source/Entities/BunkerAssembly.cpp index 6e8c4fbd67..cf4580d36f 100644 --- a/Source/Entities/BunkerAssembly.cpp +++ b/Source/Entities/BunkerAssembly.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: BunkerAssembly.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the BunkerAssembly class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "BunkerAssembly.h" #include "PresetMan.h" #include "ADoor.h" @@ -21,12 +9,6 @@ namespace RTE { ConcreteClassInfo(BunkerAssembly, SceneObject, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this BunkerAssembly, effectively - // resetting the members of this abstraction level only. - void BunkerAssembly::Clear() { m_FGColorFile.Reset(); m_MaterialFile.Reset(); @@ -44,11 +26,6 @@ namespace RTE { m_ParentSchemeGroup.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the BunkerAssembly object ready for use. - int BunkerAssembly::Create() { if (TerrainObject::Create() < 0) return -1; @@ -56,13 +33,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddPlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds placed object to the internallist of placed objects for this assembly, - // applies it's image to presentation bitmap and sets assembly price accordingly. - // Added scene object MUST have coordinates relative to this assembly. - void BunkerAssembly::AddPlacedObject(SceneObject* pSO) { m_PlacedObjects.push_back(pSO); @@ -103,11 +73,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the BunkerAssembly object ready for use. - int BunkerAssembly::Create(BunkerAssemblyScheme* pScheme) { if (TerrainObject::Create() < 0) return -1; @@ -138,11 +103,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MOPixel to be identical to another, by deep copy. - int BunkerAssembly::Create(const BunkerAssembly& reference) { TerrainObject::Create(reference); @@ -157,14 +117,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int BunkerAssembly::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneObject::ReadProperty(propName, reader)); @@ -241,12 +193,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this BunkerAssembly with a Writer for - // later recreation with Create(Reader &reader); - int BunkerAssembly::Save(Writer& writer) const { SceneObject::Save(writer); @@ -309,11 +255,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the BunkerAssembly object. - void BunkerAssembly::Destroy(bool notInherited) { for (std::list::iterator oItr = m_PlacedObjects.begin(); oItr != m_PlacedObjects.end(); ++oItr) { delete (*oItr); @@ -339,13 +280,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetDeployments - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Retrieves the list of random deployemtns selected to be deployed by this assembly - // based on it's parent scheme MaxDeployments value. This list will always include all - // brain deployments so it can be longer that MaxDeployments. OWNERSHIP NOT TRANSFERRED. - std::vector BunkerAssembly::GetDeployments() { std::vector deploymentsList; std::vector candidatesList; @@ -381,12 +315,6 @@ namespace RTE { return deploymentsList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsOnScenePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this' current graphical representation overlaps - // a point in absolute scene coordinates. - bool BunkerAssembly::IsOnScenePoint(Vector& scenePoint) const { if (!m_pPresentationBitmap) return false; @@ -402,11 +330,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this Actor belongs to. - void BunkerAssembly::SetTeam(int team) { TerrainObject::SetTeam(team); @@ -415,12 +338,6 @@ namespace RTE { (*itr)->SetTeam(team); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this TerrainObject's current graphical representation to a - // BITMAP of choice. - void BunkerAssembly::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { if (!m_FGColorBitmap) RTEAbort("TerrainObject's bitmaps are null when drawing!"); diff --git a/Source/Entities/BunkerAssembly.h b/Source/Entities/BunkerAssembly.h index cf47b9340a..e967ce7e34 100644 --- a/Source/Entities/BunkerAssembly.h +++ b/Source/Entities/BunkerAssembly.h @@ -1,18 +1,11 @@ #ifndef _RTEBUNKERASSEMBLY_ #define _RTEBUNKERASSEMBLY_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: BunkerAssembly.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the BunkerAssembly class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the BunkerAssembly class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "TerrainObject.h" namespace RTE { @@ -20,203 +13,107 @@ namespace RTE { class BunkerAssemblyScheme; class Deployment; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: BunkerAssembly - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: An assembly of a few terrain objects. - // material layer and optional background layer. - // Parent(s): SceneObject. - // Class history: 08/23/2002 BunkerAssembly created. - + /// An assembly of a few terrain objects. + /// material layer and optional background layer. class BunkerAssembly : public TerrainObject { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(BunkerAssembly); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: BunkerAssembly - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a BunkerAssembly object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a BunkerAssembly object in system + /// memory. Create() should be called before using the object. BunkerAssembly() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~BunkerAssembly - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a BunkerAssembly object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a BunkerAssembly object before deletion + /// from system memory. ~BunkerAssembly() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the BunkerAssembly object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the BunkerAssembly object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the BunkerAssembly object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the BunkerAssembly object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(BunkerAssemblyScheme* scheme); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a BunkerAssembly to be identical to another, by deep copy. - // Arguments: A reference to the BunkerAssembly to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a BunkerAssembly to be identical to another, by deep copy. + /// @param reference A reference to the BunkerAssembly to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const BunkerAssembly& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire BunkerAssembly, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire BunkerAssembly, including its inherited members, to + /// their default settings or values. void Reset() override { Clear(); SceneObject::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the BunkerAssembly object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the BunkerAssembly object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetParentAssemblySchemeName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // - // Arguments: None. - // Return value: - // - + /// Description: + /// Return value: std::string GetParentAssemblySchemeName() const { return m_ParentAssemblyScheme; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsOnScenePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this' current graphical representation overlaps - // a point in absolute scene coordinates. - // Arguments: The point in absolute scene coordinates. - // Return value: Whether this' graphical rep overlaps the scene point. - + /// Indicates whether this' current graphical representation overlaps + /// a point in absolute scene coordinates. + /// @param scenePoint The point in absolute scene coordinates. + /// @return Whether this' graphical rep overlaps the scene point. bool IsOnScenePoint(Vector& scenePoint) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDeployments - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Retrieves the list of random deployemtns selected to be deployed by this assembly - // based on it's parent scheme MaxDeployments value. This list will always include all - // brain deployments so it can be longer that MaxDeployments. - // Arguments: None. - // Return value: List of deployments. - + /// Retrieves the list of random deployemtns selected to be deployed by this assembly + /// based on it's parent scheme MaxDeployments value. This list will always include all + /// brain deployments so it can be longer that MaxDeployments. + /// @return List of deployments. std::vector GetDeployments(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this belongs to. - // Arguments: The assigned team number. - // Return value: None. - + /// Sets which team this belongs to. + /// @param team The assigned team number. void SetTeam(int team) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlacedObjects - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the list of SceneObject:s which are placed in this assembly on loading. - // Arguments: None. - // Return value: The list of of placed objects. Ownership is NOT transferred! - + /// Gets the list of SceneObject:s which are placed in this assembly on loading. + /// @return The list of of placed objects. Ownership is NOT transferred! const std::list* GetPlacedObjects() const { return &m_PlacedObjects; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddPlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds placed object to the internallist of placed objects for this assembly, - // applies it's image to presentation bitmap and sets assembly price accordingly. - // Added scene object MUST have coordinates relative to this assembly. - // Arguments: Object to add. - // Return value: None. - + /// Adds placed object to the internallist of placed objects for this assembly, + /// applies it's image to presentation bitmap and sets assembly price accordingly. + /// Added scene object MUST have coordinates relative to this assembly. + /// @param pSO Object to add. void AddPlacedObject(SceneObject* pSO); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGraphicalIcon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a bitmap showing a good identifyable icon of this, for use in - // GUI lists etc. - // Arguments: None. - // Return value: A good identifyable graphical representation of this in a BITMAP, if - // available. If not, 0 is returned. Ownership is NOT TRANSFERRED! - + /// Gets a bitmap showing a good identifyable icon of this, for use in + /// GUI lists etc. + /// @return A good identifyable graphical representation of this in a BITMAP, if + /// available. If not, 0 is returned. Ownership is NOT TRANSFERRED! BITMAP* GetGraphicalIcon() const override { return m_pPresentationBitmap; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSymmetricAssemblyName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the name of an assembly symmetric to this one. - // Arguments: None. - // Return value: Symmetric assembly name. - + /// Gets the name of an assembly symmetric to this one. + /// @return Symmetric assembly name. std::string GetSymmetricAssemblyName() const { return m_SymmetricAssembly; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSymmetricAssemblyName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the name of an assembly symmetric to this one. - // Arguments: Symmetric assembly name. - // Return value: None. - + /// Sets the name of an assembly symmetric to this one. + /// @param newSymmetricAssembly Symmetric assembly name. void SetSymmetricAssemblyName(std::string newSymmetricAssembly) { m_SymmetricAssembly = newSymmetricAssembly; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this TerrainObject's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // like indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this TerrainObject's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// like indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -231,18 +128,10 @@ namespace RTE { // Assembly symmetric to this one std::string m_SymmetricAssembly; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this BunkerAssembly, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this BunkerAssembly, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/BunkerAssemblyScheme.cpp b/Source/Entities/BunkerAssemblyScheme.cpp index 0252f7163b..edabc68f92 100644 --- a/Source/Entities/BunkerAssemblyScheme.cpp +++ b/Source/Entities/BunkerAssemblyScheme.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: BunkerAssemblyScheme.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the BunkerAssemblyScheme class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "BunkerAssemblyScheme.h" #include "PresetMan.h" #include "FrameMan.h" @@ -21,12 +9,6 @@ namespace RTE { ConcreteClassInfo(BunkerAssemblyScheme, SceneObject, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this BunkerAssemblyScheme, effectively - // resetting the members of this abstraction level only. - void BunkerAssemblyScheme::Clear() { m_pPresentationBitmap = 0; m_ChildObjects.clear(); @@ -38,11 +20,6 @@ namespace RTE { m_AssemblyGroup.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the BunkerAssemblyScheme object ready for use. - int BunkerAssemblyScheme::Create() { if (SceneObject::Create() < 0) return -1; @@ -50,11 +27,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MOPixel to be identical to another, by deep copy. - int BunkerAssemblyScheme::Create(const BunkerAssemblyScheme& reference) { SceneObject::Create(reference); @@ -76,14 +48,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int BunkerAssemblyScheme::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneObject::ReadProperty(propName, reader)); @@ -223,12 +187,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this BunkerAssemblyScheme with a Writer for - // later recreation with Create(Reader &reader); - int BunkerAssemblyScheme::Save(Writer& writer) const { SceneObject::Save(writer); @@ -242,11 +200,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the BunkerAssemblyScheme object. - void BunkerAssemblyScheme::Destroy(bool notInherited) { // Probably no need to delete those, as bitmaps are only created when preset is read from file // and then they just copy pointers in via Clone() @@ -258,22 +211,10 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGraphicalIcon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a bitmap showing a good identifyable icon of this, for use in - // GUI lists etc. - BITMAP* BunkerAssemblyScheme::GetGraphicalIcon() const { return m_pIconBitmap; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsOnScenePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this' current graphical representation overlaps - // a point in absolute scene coordinates. - bool BunkerAssemblyScheme::IsOnScenePoint(Vector& scenePoint) const { if (!m_pBitmap) return false; @@ -293,11 +234,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this Actor belongs to. - void BunkerAssemblyScheme::SetTeam(int team) { SceneObject::SetTeam(team); @@ -306,12 +242,6 @@ namespace RTE { (*itr).SetTeam(team); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this BunkerAssemblyScheme's current graphical representation to a - // BITMAP of choice. - void BunkerAssemblyScheme::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { if (!m_pPresentationBitmap) RTEAbort("BunkerAssemblyScheme's bitmaps are null when drawing!"); diff --git a/Source/Entities/BunkerAssemblyScheme.h b/Source/Entities/BunkerAssemblyScheme.h index ff0cbbed4d..cf9645a708 100644 --- a/Source/Entities/BunkerAssemblyScheme.h +++ b/Source/Entities/BunkerAssemblyScheme.h @@ -1,18 +1,11 @@ #ifndef _RTEBUNKERASSEMBLYSCHEME_ #define _RTEBUNKERASSEMBLYSCHEME_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: BunkerAssemblyScheme.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the BunkerAssemblyScheme class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the BunkerAssemblyScheme class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "SceneObject.h" #include "SceneMan.h" @@ -23,19 +16,11 @@ namespace RTE { class ContentFile; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: BunkerAssemblyScheme - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A feature of the terrain, which includes foreground color layer, - // material layer and optional background layer. - // Parent(s): SceneObject. - // Class history: 08/23/2002 BunkerAssemblyScheme created. - + /// A feature of the terrain, which includes foreground color layer, + /// material layer and optional background layer. class BunkerAssemblyScheme : public SceneObject { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Different scheme properties are encoded on colors of scheme bitmap enum SchemeColor { @@ -61,207 +46,108 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: BunkerAssemblyScheme - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a BunkerAssemblyScheme object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a BunkerAssemblyScheme object in system + /// memory. Create() should be called before using the object. BunkerAssemblyScheme() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~BunkerAssemblyScheme - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a BunkerAssemblyScheme object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a BunkerAssemblyScheme object before deletion + /// from system memory. ~BunkerAssemblyScheme() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the BunkerAssemblyScheme object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the BunkerAssemblyScheme object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a BunkerAssemblyScheme to be identical to another, by deep copy. - // Arguments: A reference to the BunkerAssemblyScheme to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a BunkerAssemblyScheme to be identical to another, by deep copy. + /// @param reference A reference to the BunkerAssemblyScheme to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const BunkerAssemblyScheme& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire BunkerAssemblyScheme, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire BunkerAssemblyScheme, including its inherited members, to + /// their default settings or values. void Reset() override { Clear(); SceneObject::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the BunkerAssemblyScheme object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the BunkerAssemblyScheme object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBitmapWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the width this' bitmap. - // Arguments: None. - // Return value: Width of bitmap. - + /// Method: GetBitmapWidth + /// Gets the width this' bitmap. + /// @return Width of bitmap. const int GetBitmapWidth() const { return m_pPresentationBitmap->w; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBitmapHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the height this' material bitmap. - // Arguments: None. - // Return value: Height of 'material' bitmap. - + /// Method: GetBitmapHeight + /// Gets the height this' material bitmap. + /// @return Height of 'material' bitmap. const int GetBitmapHeight() const { return m_pPresentationBitmap->h; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBitmapOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the offset of presentation bitmap - // Arguments: None. - // Return value: Offset of bitmap - + /// Method: GetBitmapOffset + /// Gets the offset of presentation bitmap + /// @return Offset of bitmap const Vector GetBitmapOffset() const { return m_BitmapOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the BITMAP object that this BunkerAssemblyScheme uses for its fore- - // ground color representation. - // Arguments: None. - // Return value: A pointer to the foreground color BITMAP object. Ownership is not - // transferred. - + /// Gets the BITMAP object that this BunkerAssemblyScheme uses for its fore- + /// ground color representation. + /// @return A pointer to the foreground color BITMAP object. Ownership is not + /// transferred. BITMAP* GetBitmap() const { return m_pPresentationBitmap; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the area of the structure bitmap. - // Arguments: None. - // Return value: None. - + /// Gets the area of the structure bitmap. int GetArea() const { return m_pBitmap->h * m_pBitmap->w; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGraphicalIcon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a bitmap showing a good identifyable icon of this, for use in - // GUI lists etc. - // Arguments: None. - // Return value: A good identifyable graphical representation of this in a BITMAP, if - // available. If not, 0 is returned. Ownership is NOT TRANSFERRED! - + /// Gets a bitmap showing a good identifyable icon of this, for use in + /// GUI lists etc. + /// @return A good identifyable graphical representation of this in a BITMAP, if + /// available. If not, 0 is returned. Ownership is NOT TRANSFERRED! BITMAP* GetGraphicalIcon() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this belongs to. - // Arguments: The assigned team number. - // Return value: None. - + /// Sets which team this belongs to. + /// @param team The assigned team number. void SetTeam(int team) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsOnScenePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this' current graphical representation overlaps - // a point in absolute scene coordinates. - // Arguments: The point in absolute scene coordinates. - // Return value: Whether this' graphical rep overlaps the scene point. - + /// Indicates whether this' current graphical representation overlaps + /// a point in absolute scene coordinates. + /// @param scenePoint The point in absolute scene coordinates. + /// @return Whether this' graphical rep overlaps the scene point. bool IsOnScenePoint(Vector& scenePoint) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this BunkerAssemblyScheme's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // like indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this BunkerAssemblyScheme's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// like indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOneTypePerScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether sceneman should select just a single assembly for this scheme - // and use it everywhere on the scene. - // Arguments: None. - // Return value: Whether we allowed to use just one type of assembly for this scheme - + /// Returns whether sceneman should select just a single assembly for this scheme + /// and use it everywhere on the scene. + /// @return Whether we allowed to use just one type of assembly for this scheme bool IsOneTypePerScene() { return m_IsOneTypePerScene; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSymmetricSchemeName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the name of the scheme symmetric to this one. - // Arguments: None. - // Return value: Symmetric scheme name. - + /// Gets the name of the scheme symmetric to this one. + /// @return Symmetric scheme name. std::string GetSymmetricSchemeName() const { return m_SymmetricScheme; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAssemblyGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the name of group to which assemblies linked with this scheme must be added. - // Arguments: None. - // Return value: Assembly group name. - + /// Gets the name of group to which assemblies linked with this scheme must be added. + /// @return Assembly group name. std::string GetAssemblyGroup() const { return m_AssemblyGroup; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLimit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the limit of these schemes per scene. 0 - no limit. - // Arguments: None. - // Return value: Scheme limit. - + /// Returns the limit of these schemes per scene. 0 - no limit. + /// @return Scheme limit. int GetLimit() { return m_Limit; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaxDeployments - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the number of deployments this scheme is allowed to place. - // Arguments: None. - // Return value: Deployments limit. - + /// Returns the number of deployments this scheme is allowed to place. + /// @return Deployments limit. int GetMaxDeployments() const { return m_MaxDeployments; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -289,18 +175,10 @@ namespace RTE { // To which group we should add assemblies linked to this scheme std::string m_AssemblyGroup; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this BunkerAssemblyScheme, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this BunkerAssemblyScheme, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Deployment.cpp b/Source/Entities/Deployment.cpp index a9710c3fb0..f06d43ad03 100644 --- a/Source/Entities/Deployment.cpp +++ b/Source/Entities/Deployment.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Deployment.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the Deployment class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Deployment.h" #include "PresetMan.h" #include "MetaMan.h" @@ -28,12 +16,6 @@ namespace RTE { std::vector Deployment::m_apArrowLeftBitmap; std::vector Deployment::m_apArrowRightBitmap; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Deployment, effectively - // resetting the members of this abstraction level only. - void Deployment::Clear() { m_LoadoutName = "Default"; m_Icon.Reset(); @@ -43,11 +25,6 @@ namespace RTE { m_HFlipped = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Deployment object ready for use. - int Deployment::Create() { if (SceneObject::Create() < 0) return -1; @@ -62,11 +39,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Deployment object ready for use. - int Deployment::Create(std::string loadoutName, const Icon& icon, float spawnRadius) { m_LoadoutName = loadoutName; m_Icon = icon; @@ -85,11 +57,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MOPixel to be identical to another, by deep copy. - int Deployment::Create(const Deployment& reference) { SceneObject::Create(reference); @@ -103,14 +70,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int Deployment::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneObject::ReadProperty(propName, reader)); @@ -124,12 +83,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this Deployment with a Writer for - // later recreation with Create(Reader &reader); - int Deployment::Save(Writer& writer) const { SceneObject::Save(writer); @@ -147,11 +100,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the Deployment object. - void Deployment::Destroy(bool notInherited) { if (!notInherited) @@ -159,27 +107,11 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateDeployedActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the Actor that this Deployment dictates should - // spawn here. Ownership IS transferred!! All items of the Loadout of - // this Deployment will be added to the Actor's inventory as well (and - // also owned by it) - Actor* Deployment::CreateDeployedActor() { float cost; return CreateDeployedActor(-1, cost); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateDeployedActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the Actor that this Deployment dictates should - // spawn here. Ownership IS transferred!! All items of the Loadout of - // this Deployment will be added to the Actor's inventory as well (and - // also owned by it) - Actor* Deployment::CreateDeployedActor(int player, float& costTally) { // The Actor instance we return and pass ownership of Actor* pReturnActor = 0; @@ -238,23 +170,11 @@ namespace RTE { return pReturnActor; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateDeployedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the first Device that Deployment dictates should - // spawn here. Ownership IS transferred!! Only the first Device is created. - SceneObject* Deployment::CreateDeployedObject() { float cost; return CreateDeployedObject(-1, cost); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateDeployedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the first Device that Deployment dictates should - // spawn here. Ownership IS transferred!! Only the first Device is created. - SceneObject* Deployment::CreateDeployedObject(int player, float& costTally) { // The Actor instance we return and pass ownership of SceneObject* pReturnObject = 0; @@ -310,13 +230,6 @@ namespace RTE { return pReturnObject; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DeploymentBlocked - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tests whether the Object this is supposed to spawn/deploy is blocked - // by an already exiting object in the a list being positioned within the - // spawn radius of this. - bool Deployment::DeploymentBlocked(int player, const std::list& existingObjects) { bool blocked = false; @@ -426,12 +339,6 @@ namespace RTE { return blocked; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total liquidation value of a spawn of this, including - // everything carried by it. - float Deployment::GetTotalValue(int nativeModule, float foreignMult, float nativeMult) const { float totalValue = 0; const Actor* pFirstActor = 0; @@ -524,12 +431,6 @@ namespace RTE { return totalValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsOnScenePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this' current graphical representation overlaps - // a point in absolute scene coordinates. - bool Deployment::IsOnScenePoint(Vector& scenePoint) const { if (m_Icon.GetBitmaps8().empty() || !(m_Icon.GetBitmaps8().at(0))) return false; @@ -590,12 +491,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Deployment's current graphical representation to a - // BITMAP of choice. - void Deployment::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { if (m_Icon.GetBitmaps8().empty() || !(m_Icon.GetBitmaps8().at(0))) RTEAbort("Deployment's Icon bitmaps are null when drawing!"); diff --git a/Source/Entities/Deployment.h b/Source/Entities/Deployment.h index 1a524501fc..91d6e1cf8f 100644 --- a/Source/Entities/Deployment.h +++ b/Source/Entities/Deployment.h @@ -1,18 +1,11 @@ #ifndef _RTEDEPLOYMENT_ #define _RTEDEPLOYMENT_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Deployment.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the Deployment class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the Deployment class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "RTETools.h" #include "SceneObject.h" #include "Vector.h" @@ -23,314 +16,183 @@ namespace RTE { class ContentFile; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: Deployment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A special SceneObject that specifies a Loadout of whatever Tech is - // relevant to be placed in a specific location in a Scene. - // Parent(s): SceneObject. - // Class history: 02/27/2012 Deployment created. - + /// A special SceneObject that specifies a Loadout of whatever Tech is + /// relevant to be placed in a specific location in a Scene. class Deployment : public SceneObject { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(Deployment); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Deployment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Deployment object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a Deployment object in system + /// memory. Create() should be called before using the object. Deployment() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~Deployment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a Deployment object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a Deployment object before deletion + /// from system memory. ~Deployment() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Deployment object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the Deployment object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Deployment object ready for use. - // Arguments: The name of the Loadout that this should invoke at this' position. - // Icon that represents this graphically. - // The radius around this deployment that gets checked if another - // actor/item of the same type and name already exists and will block - // re-spawning a new one by this. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the Deployment object ready for use. + /// @param loadoutName The name of the Loadout that this should invoke at this' position. + /// @param icon Icon that represents this graphically. + /// @param spawnRadius The radius around this deployment that gets checked if another + /// actor/item of the same type and name already exists and will block + /// re-spawning a new one by this. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(std::string loadoutName, const Icon& icon, float spawnRadius); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Deployment to be identical to another, by deep copy. - // Arguments: A reference to the Deployment to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a Deployment to be identical to another, by deep copy. + /// @param reference A reference to the Deployment to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const Deployment& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Deployment, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire Deployment, including its inherited members, to + /// their default settings or values. void Reset() override { Clear(); SceneObject::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the Deployment object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the Deployment object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGraphicalIcon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a bitmap showing a good identifyable icon of this, for use in - // GUI lists etc. - // Arguments: None. - // Return value: A good identifyable graphical representation of this in a BITMAP, if - // available. If not, 0 is returned. Ownership is NOT TRANSFERRED! - + /// Gets a bitmap showing a good identifyable icon of this, for use in + /// GUI lists etc. + /// @return A good identifyable graphical representation of this in a BITMAP, if + /// available. If not, 0 is returned. Ownership is NOT TRANSFERRED! BITMAP* GetGraphicalIcon() const override { return !m_Icon.GetBitmaps8().empty() ? m_Icon.GetBitmaps8()[0] : nullptr; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLoadoutName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the name of the Loadout that this Deployment spawns. - // Arguments: None. - // Return value: The name of the Loadout preset that this Deployment spawns. - + /// Gets the name of the Loadout that this Deployment spawns. + /// @return The name of the Loadout preset that this Deployment spawns. const std::string& GetLoadoutName() { return m_LoadoutName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGraphicalIcon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a bitmap showing a good identifyable icon of this. - // Arguments: None. - // Return value: The Icon that represents this graphically. - + /// Gets a bitmap showing a good identifyable icon of this. + /// @return The Icon that represents this graphically. Icon GetIcon() { return m_Icon; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpawnRadius - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the radius around this deployment that gets checked if another - // actor/item of the same type and name already exists and will block - // re-spawning a new one by this - // Arguments: None. - // Return value: The radius this Deployment will be checking within. - + /// Gets the radius around this deployment that gets checked if another + /// actor/item of the same type and name already exists and will block + /// re-spawning a new one by this + /// @return The radius this Deployment will be checking within. float GetSpawnRadius() const { return m_SpawnRadius; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsOnScenePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this' current graphical representation overlaps - // a point in absolute scene coordinates. - // Arguments: The point in absolute scene coordinates. - // Return value: Whether this' graphical rep overlaps the scene point. - + /// Indicates whether this' current graphical representation overlaps + /// a point in absolute scene coordinates. + /// @param scenePoint The point in absolute scene coordinates. + /// @return Whether this' graphical rep overlaps the scene point. bool IsOnScenePoint(Vector& scenePoint) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateDeployedActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the Actor that this Deployment dictates should - // spawn here. Ownership IS transferred!! All items of the Loadout of - // this Deployment will be added to the Actor's inventory as well (and - // also owned by it) - // Arguments: Which in-game player to create the delivery for. - // A float which will be added to with the cost of the stuff returned here. - // Return value: The Actor instance, if any, that this Deployment is supposed to spawn. - // OWNERSHIP IS TRANSFERRED! - + /// Creates and returns the Actor that this Deployment dictates should + /// spawn here. Ownership IS transferred!! All items of the Loadout of + /// this Deployment will be added to the Actor's inventory as well (and + /// also owned by it) + /// @param player Which in-game player to create the delivery for. + /// @param costTally A float which will be added to with the cost of the stuff returned here. + /// @return The Actor instance, if any, that this Deployment is supposed to spawn. + /// OWNERSHIP IS TRANSFERRED! Actor* CreateDeployedActor(int player, float& costTally); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateDeployedActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the Actor that this Deployment dictates should - // spawn here. Ownership IS transferred!! All items of the Loadout of - // this Deployment will be added to the Actor's inventory as well (and - // also owned by it) - // Arguments: Which in-game player to create the delivery for. - // Return value: The Actor instance, if any, that this Deployment is supposed to spawn. - // OWNERSHIP IS TRANSFERRED! - + /// Creates and returns the Actor that this Deployment dictates should + /// spawn here. Ownership IS transferred!! All items of the Loadout of + /// this Deployment will be added to the Actor's inventory as well (and + /// also owned by it) + /// @param Which in-game player to create the delivery for. + /// @return The Actor instance, if any, that this Deployment is supposed to spawn. + /// OWNERSHIP IS TRANSFERRED! Actor* CreateDeployedActor(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateDeployedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the first Device that Deployment dictates should - // spawn here. Ownership IS transferred!! Only the first Device is created. - // Arguments: Which in-game player to create the delivery for. - // A float which will be added to with the cost of the stuff returned here. - // Return value: The Actor instance, if any, that this Deployment is supposed to spawn. - // OWNERSHIP IS TRANSFERRED! - + /// Creates and returns the first Device that Deployment dictates should + /// spawn here. Ownership IS transferred!! Only the first Device is created. + /// @param player Which in-game player to create the delivery for. + /// @param costTally A float which will be added to with the cost of the stuff returned here. + /// @return The Actor instance, if any, that this Deployment is supposed to spawn. + /// OWNERSHIP IS TRANSFERRED! SceneObject* CreateDeployedObject(int player, float& costTally); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateDeployedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the first Device that Deployment dictates should - // spawn here. Ownership IS transferred!! Only the first Device is created. - // Arguments: Which in-game player to create the delivery for. - // Return value: The Actor instance, if any, that this Deployment is supposed to spawn. - // OWNERSHIP IS TRANSFERRED! - + /// Creates and returns the first Device that Deployment dictates should + /// spawn here. Ownership IS transferred!! Only the first Device is created. + /// @param Which in-game player to create the delivery for. + /// @return The Actor instance, if any, that this Deployment is supposed to spawn. + /// OWNERSHIP IS TRANSFERRED! SceneObject* CreateDeployedObject(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DeploymentBlocked - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tests whether the Object this is supposed to spawn/deploy is blocked - // by an already exiting object in the a list being positioned within the - // spawn radius of this. - // Arguments: Which in-game player to create the delivery for. - // A list of SceneObject:s that will be tested against to see if any - // sufficiently similar Object is positioned within the spawn radius of - // this. - // Return value: Whether the deployment spawning is blocked by one of the Objects in - // the list. - + /// Tests whether the Object this is supposed to spawn/deploy is blocked + /// by an already exiting object in the a list being positioned within the + /// spawn radius of this. + /// @param player Which in-game player to create the delivery for. + /// @param existingObjects A list of SceneObject:s that will be tested against to see if any + /// sufficiently similar Object is positioned within the spawn radius of + /// this. + /// @return Whether the deployment spawning is blocked by one of the Objects in + /// the list. bool DeploymentBlocked(int player, const std::list& existingObjects); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the cost to purchase this item, in oz's of gold. - // Arguments: If this is supposed to be adjusted for a specific Tech's subjective - // value, then pass in the native DataModule ID of that tech. 0 means - // no Tech is specified and the base value is returned. - // How much to multiply the value if this happens to be a foreign Tech. - // Return value: The cost, in oz of gold. - + /// Gets the cost to purchase this item, in oz's of gold. + /// @param nativeModule If this is supposed to be adjusted for a specific Tech's subjective (default: 0) + /// value, then pass in the native DataModule ID of that tech. 0 means + /// no Tech is specified and the base value is returned. + /// @param foreignMult How much to multiply the value if this happens to be a foreign Tech. (default: 1.0) + /// @return The cost, in oz of gold. float GetGoldValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override { return GetTotalValue(nativeModule, foreignMult, nativeMult); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldValueOld - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: DOES THE SAME THING AS GetGoldValue, USED ONLY TO PRESERVE LUA COMPATIBILITY - + /// DOES THE SAME THING AS GetGoldValue, USED ONLY TO PRESERVE LUA COMPATIBILITY float GetGoldValueOld(int nativeModule = 0, float foreignMult = 1.0) const override { return GetTotalValue(nativeModule, foreignMult, 1.0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total liquidation value of a spawn of this, including - // everything carried by it. - // Arguments: If this is supposed to be adjusted for a specific Tech's subjective - // value, then pass in the native DataModule ID of that tech. 0 means - // no Tech is specified and the base value is returned. - // How much to multiply the value if this happens to be a foreign Tech. - // Return value: The current value of this and all contained assets. - + /// Gets the total liquidation value of a spawn of this, including + /// everything carried by it. + /// @param nativeModule If this is supposed to be adjusted for a specific Tech's subjective (default: 0) + /// value, then pass in the native DataModule ID of that tech. 0 means + /// no Tech is specified and the base value is returned. + /// @param foreignMult How much to multiply the value if this happens to be a foreign Tech. (default: 1.0) + /// @return The current value of this and all contained assets. float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Return this deployment's unique ID - // Arguments: None. - // Return value: This deployment's ID - + /// Return this deployment's unique ID + /// @return This deployment's ID unsigned int GetID() const { return m_ID; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CloneID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clones id from the specified deployment - // Arguments: Deployment to clone Id from. - // Return value: None - + /// Clones id from the specified deployment + /// @param from Deployment to clone Id from. void CloneID(Deployment* from) { if (from) m_ID = from->GetID(); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: NewID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Generates new random ID for this deployment. - // Arguments: None. - // Return value: None. - + /// Generates new random ID for this deployment. void NewID() { m_ID = RandomNum(1, 0xFFFF); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Deployment's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // like indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this Deployment's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// like indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsHFlipped - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether this MOSprite is being drawn flipped horizontally - // (along the vertical axis), or not. - // Arguments: None. - // Return value: Whether flipped or not. - + /// Returns whether this MOSprite is being drawn flipped horizontally + /// (along the vertical axis), or not. + /// @return Whether flipped or not. bool IsHFlipped() const override { return m_HFlipped; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: SetHFlipped - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this should be drawn flipped horizontally (around the - // vertical axis). - // Arguments: A bool with the new value. - // Return value: None. - + /// Sets whether this should be drawn flipped horizontally (around the + /// vertical axis). + /// @param flipped A bool with the new value. void SetHFlipped(const bool flipped) override { m_HFlipped = flipped; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -351,18 +213,10 @@ namespace RTE { static std::vector m_apArrowLeftBitmap; static std::vector m_apArrowRightBitmap; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Deployment, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Deployment, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Emission.cpp b/Source/Entities/Emission.cpp index 724bef4fe0..69c4b59177 100644 --- a/Source/Entities/Emission.cpp +++ b/Source/Entities/Emission.cpp @@ -1,12 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Emission.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the Emission class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - #include "Emission.h" #include "PresetMan.h" @@ -16,12 +7,6 @@ namespace RTE { ConcreteClassInfo(Emission, Entity, 100); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Emission, effectively - // resetting the members of this abstraction level only. - void Emission::Clear() { m_pEmission = 0; m_PPM = 0; @@ -55,11 +40,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Emission to be identical to another, by deep copy. - int Emission::Create(const Emission& reference) { m_pEmission = reference.m_pEmission; m_PPM = reference.m_PPM; @@ -78,14 +58,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int Emission::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -123,12 +95,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this Emission with a Writer for - // later recreation with Create(Reader &reader); - int Emission::Save(Writer& writer) const { Serializable::Save(writer); diff --git a/Source/Entities/Emission.h b/Source/Entities/Emission.h index 52bc01f3c1..edbd483a4b 100644 --- a/Source/Entities/Emission.h +++ b/Source/Entities/Emission.h @@ -1,263 +1,133 @@ #ifndef _RTEEMISSION_ #define _RTEEMISSION_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Emission.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Something to bundle the properties of an emission together. -// Parent(s): Entity. -// Class history: 09/07/2004 Emission created as struct. -// 07/21/2006 Emission turned into 'Serializable' class. - +/// File: Emission.h +/// Something to bundle the properties of an emission together. +/// 07/21/2006 Emission turned into 'Serializable' class. #include "Serializable.h" #include "MovableObject.h" namespace RTE { - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: Emission - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Something to bundle the properties of an emission together. - // Parent(s): Entity. - + /// Something to bundle the properties of an emission together. class Emission : public Entity { friend class AEmitter; friend class PEmitter; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(Emission); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Emission - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Emission object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a Emission object in system + /// memory. Create() should be called before using the object. Emission() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Emission to be identical to another, by deep copy. - // Arguments: A reference to the Emission to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a Emission to be identical to another, by deep copy. + /// @param reference A reference to the Emission to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const Emission& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Serializable, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire Serializable, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmissionParticlePreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the reference particle to be emitted. Owenership is NOT transferred! - // Arguments: None. - // Return value: A pointer to the particle to be emitted. Not transferred! - + /// Gets the reference particle to be emitted. Owenership is NOT transferred! + /// @return A pointer to the particle to be emitted. Not transferred! const MovableObject* GetEmissionParticlePreset() { return m_pEmission; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rate at which these emissions are made, in particles per minute. - // Arguments: None. - // Return value: The emission rate in PPM. - + /// Gets the rate at which these emissions are made, in particles per minute. + /// @return The emission rate in PPM. float GetRate() const { return m_PPM; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetRate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the rate at which these emissions are made, in particles per minute. - // Arguments: The emission rate in PPM. - // Return value: None. - + /// Sets the rate at which these emissions are made, in particles per minute. + /// @param newPPM The emission rate in PPM. void SetRate(float newPPM) { m_PPM = newPPM; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of extra particles that are bursted at the beginning of - // emission. - // Arguments: None. - // Return value: The burst size. - + /// Gets the number of extra particles that are bursted at the beginning of + /// emission. + /// @return The burst size. int GetBurstSize() const { return m_BurstSize; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the number of extra particles that are bursted at the beginning of - // emission. - // Arguments: The burst size. - // Return value: None. - + /// Sets the number of extra particles that are bursted at the beginning of + /// emission. + /// @param newSize The burst size. void SetBurstSize(int newSize) { m_BurstSize = newSize; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpread - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the angle spread of velocity of the emitted MO's to each side of - // the m_EmitAngle angle. in radians. PI/2 would mean that MO's fly out to - // one side only, with the m_Rotation defining the middle of that half circle. - // Arguments: None. - // Return value: The emission spread in radians. - + /// Gets the angle spread of velocity of the emitted MO's to each side of + /// the m_EmitAngle angle. in radians. PI/2 would mean that MO's fly out to + /// one side only, with the m_Rotation defining the middle of that half circle. + /// @return The emission spread in radians. float GetSpread() const { return m_Spread; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSpread - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the angle spread of velocity of the emitted MO's to each side of - // the m_EmitAngle angle. in radians. PI/2 would mean that MO's fly out to - // one side only, with the m_Rotation defining the middle of that half circle. - // Arguments: The emission spread in radians. - // Return value: None. - + /// Sets the angle spread of velocity of the emitted MO's to each side of + /// the m_EmitAngle angle. in radians. PI/2 would mean that MO's fly out to + /// one side only, with the m_Rotation defining the middle of that half circle. + /// @param newSpread The emission spread in radians. void SetSpread(float newSpread) { m_Spread = newSpread; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMinVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified minimum velocity an emitted MO can have when emitted. - // Arguments: None. - // Return value: The min emission velocity in m/s. - + /// Gets the specified minimum velocity an emitted MO can have when emitted. + /// @return The min emission velocity in m/s. float GetMinVelocity() const { return m_MinVelocity; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMinVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the specified minimum velocity an emitted MO can have when emitted. - // Arguments: The min emission velocity in m/s. - // Return value: None. - + /// Sets the specified minimum velocity an emitted MO can have when emitted. + /// @param newVel The min emission velocity in m/s. void SetMinVelocity(float newVel) { m_MinVelocity = newVel; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaxVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified maximum velocity an emitted MO can have when emitted. - // Arguments: None. - // Return value: The max emission velocity in m/s. - + /// Gets the specified maximum velocity an emitted MO can have when emitted. + /// @return The max emission velocity in m/s. float GetMaxVelocity() const { return m_MaxVelocity; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMaxVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified maximum velocity an emitted MO can have when emitted. - // Arguments: The max emission velocity in m/s. - // Return value: None. - + /// Gets the specified maximum velocity an emitted MO can have when emitted. + /// @param newVel The max emission velocity in m/s. void SetMaxVelocity(float newVel) { m_MaxVelocity = newVel; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLifeVariation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified variation in lifetime of the emitted particles. - // Arguments: None. - // Return value: The life variation rationally expressed.. 0.1 = up to 10% varitaion. - + /// Gets the specified variation in lifetime of the emitted particles. + /// @return The life variation rationally expressed.. 0.1 = up to 10% varitaion. float GetLifeVariation() const { return m_LifeVariation; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLifeVariation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the specified variation in lifetime of the emitted particles. - // Arguments: The life variation rationally expressed.. 0.1 = up to 10% varitaion. - // Return value: None. - + /// Sets the specified variation in lifetime of the emitted particles. + /// @param newVariation The life variation rationally expressed.. 0.1 = up to 10% varitaion. void SetLifeVariation(float newVariation) { m_LifeVariation = newVariation; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PushesEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this emission is supposed to push its emitter back - // because of recoil. - // Arguments: None. - // Return value: Whether recoil pushing is enabled or not for this emitter. - + /// Indicates whether this emission is supposed to push its emitter back + /// because of recoil. + /// @return Whether recoil pushing is enabled or not for this emitter. bool PushesEmitter() const { return m_PushesEmitter; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPushesEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this emission is supposed to push its emitter back - // because of recoil. - // Arguments: Whether recoil pushing is enabled or not for this emitter. - // Return value: None. - + /// Sets whether this emission is supposed to push its emitter back + /// because of recoil. + /// @param newValue Whether recoil pushing is enabled or not for this emitter. void SetPushesEmitter(bool newValue) { m_PushesEmitter = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEmissionTime - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this should be emitting now or not, based on what its - // start and end timers are set to. - // Arguments: None. - // Return value: Whether this should be emitting right now. - + /// Shows whether this should be emitting now or not, based on what its + /// start and end timers are set to. + /// @return Whether this should be emitting right now. bool IsEmissionTime() { return m_StartTimer.IsPastSimTimeLimit() && !m_StopTimer.IsPastSimTimeLimit(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetEmissionTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the emission timers so they start counting time as to wheter - // emissions are clearer. - // Arguments: None. - // Return value: None. - + /// Resets the emission timers so they start counting time as to wheter + /// emissions are clearer. void ResetEmissionTimers() { m_StartTimer.Reset(); m_StopTimer.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: InheritsVelocity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: How much of the root parent's velocity this emission inherit - // Arguments: None. - // Return value: The proportion of the velocity inherited. 0.1 = 10% inheritance. - + /// How much of the root parent's velocity this emission inherit + /// @return The proportion of the velocity inherited. 0.1 = 10% inheritance. float InheritsVelocity() { return m_InheritsVel; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter - // Arguments: None. - // Return value: Returns emission offset. - + /// Gets offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter + /// @return Returns emission offset. Vector GetOffset() const { return m_Offset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter - // Arguments: New offset value. - // Return value: None. - + /// Sets offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter + /// @param offset New offset value. void SetOffset(Vector offset) { m_Offset = offset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables // static const std::string m_sClassName; @@ -294,18 +164,10 @@ namespace RTE { // Offset of the emission point from Emitter's sprite center, which gets rotated with owner Emitter Vector m_Offset; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Emission, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Emission, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Entities/Gib.cpp b/Source/Entities/Gib.cpp index c136976780..763c006ade 100644 --- a/Source/Entities/Gib.cpp +++ b/Source/Entities/Gib.cpp @@ -6,8 +6,6 @@ namespace RTE { const std::string Gib::c_ClassName = "Gib"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Gib::Clear() { m_GibParticle = nullptr; m_Offset.Reset(); @@ -21,8 +19,6 @@ namespace RTE { m_SpreadMode = SpreadMode::SpreadRandom; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Gib::Create(const Gib& reference) { m_GibParticle = reference.m_GibParticle; m_Offset = reference.m_Offset; @@ -38,8 +34,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Gib::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -60,8 +54,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Gib::Save(Writer& writer) const { Serializable::Save(writer); diff --git a/Source/Entities/Gib.h b/Source/Entities/Gib.h index 5051479235..635315295c 100644 --- a/Source/Entities/Gib.h +++ b/Source/Entities/Gib.h @@ -7,9 +7,7 @@ namespace RTE { class MovableObject; - /// /// Something to bundle the properties of Gib piece together. - /// class Gib : public Serializable { friend class GibEditor; friend struct EntityLuaBindings; @@ -18,9 +16,7 @@ namespace RTE { SerializableClassNameGetter; SerializableOverrideMethods; - /// /// Different types of logic for the Gib to use when applying velocity to its GibParticles. - /// enum SpreadMode { SpreadRandom, SpreadEven, @@ -28,115 +24,79 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a Gib object in system memory. Create() should be called before using the object. - /// Gib() { Clear(); } - /// /// Creates a Gib to be identical to another, by deep copy. - /// - /// A reference to the Gib to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Gib to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Gib& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a Gib object before deletion from system memory. - /// ~Gib() override { Destroy(); } - /// /// Destroys and resets (through Clear()) the Gib object. - /// void Destroy() { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Gets the reference particle to be used as a Gib. Ownership is NOT transferred! - /// - /// A pointer to the particle to be used as a Gib. + /// @return A pointer to the particle to be used as a Gib. const MovableObject* GetParticlePreset() const { return m_GibParticle; } - /// /// Sets the reference particle to be used as a Gib. Ownership is NOT transferred! - /// - /// A pointer to the new particle to be used as a Gib. + /// @param newParticlePreset A pointer to the new particle to be used as a Gib. void SetParticlePreset(const MovableObject* newParticlePreset) { m_GibParticle = newParticlePreset; } - /// /// Gets the spawn offset of this Gib from the parent's position. - /// - /// The offset in pixels from the parent's position where this Gib gets spawned. + /// @return The offset in pixels from the parent's position where this Gib gets spawned. Vector GetOffset() const { return m_Offset; } - /// /// Gets the number of copies of the GibParticle object that will be spawned in this Gib. - /// - /// The number of copies of the GibParticle object that will be spawned in this Gib. + /// @return The number of copies of the GibParticle object that will be spawned in this Gib. unsigned int GetCount() const { return m_Count; } - /// /// Gets the angle spread of the spawned GibParticle objects to each side of the parent's angle in radians. /// PI/2 would mean that GibParticles fly out to one side only, with the m_Rotation of the parent defining the middle of that half circle. - /// - /// The GibParticle spread in radians. + /// @return The GibParticle spread in radians. float GetSpread() const { return m_Spread; } - /// /// Gets the specified minimum velocity a GibParticle object can have when spawned. - /// - /// The minimum velocity a GibParticle can have when spawned in m/s. + /// @return The minimum velocity a GibParticle can have when spawned in m/s. float GetMinVelocity() const { return std::min(m_MinVelocity, m_MaxVelocity); } - /// /// Sets the specified minimum velocity a GibParticle object can have when spawned. - /// - /// The new minimum velocity in m/s. + /// @param newMinVelocity The new minimum velocity in m/s. void SetMinVelocity(float newMinVelocity) { m_MinVelocity = newMinVelocity; } - /// /// Gets the specified maximum velocity a GibParticle object can have when spawned. - /// - /// The maximum velocity a GibParticle can have when spawned in m/s. + /// @return The maximum velocity a GibParticle can have when spawned in m/s. float GetMaxVelocity() const { return std::max(m_MinVelocity, m_MaxVelocity); } - /// /// Sets the specified maximum velocity a GibParticle object can have when spawned. - /// - /// The new maximum velocity in m/s. + /// @param newMaxVelocity The new maximum velocity in m/s. void SetMaxVelocity(float newMaxVelocity) { m_MaxVelocity = newMaxVelocity; } - /// /// Gets the specified variation in Lifetime of the GibParticle objects. - /// - /// The life variation rationally expressed. 0.1 = up to 10% variation. + /// @return The life variation rationally expressed. 0.1 = up to 10% variation. float GetLifeVariation() const { return m_LifeVariation; } - /// /// Gets how much of the gibbing parent's velocity this Gib's GibParticles should inherit. - /// - /// The proportion of inherited velocity as a scalar from 0 to 1. + /// @return The proportion of inherited velocity as a scalar from 0 to 1. float InheritsVelocity() const { return m_InheritsVel; } - /// /// Gets whether this Gib's GibParticles should ignore hits with the team of the gibbing parent. - /// - /// Whether this Gib's GibParticles should ignore hits with the team of the gibbing parent. + /// @return Whether this Gib's GibParticles should ignore hits with the team of the gibbing parent. bool IgnoresTeamHits() const { return m_IgnoresTeamHits; } - /// /// Gets this Gib's spread mode, which determines how velocity angles are applied to the GibParticles. - /// - /// The spread mode of this Gib. + /// @return The spread mode of this Gib. SpreadMode GetSpreadMode() const { return m_SpreadMode; } - /// /// Sets this Gib's spread mode, which determines how velocity angles are applied to the GibParticles. - /// - /// The new spread mode of this Gib. See the SpreadMode enumeration. + /// @param newSpreadMode The new spread mode of this Gib. See the SpreadMode enumeration. void SetSpreadMode(SpreadMode newSpreadMode) { m_SpreadMode = (newSpreadMode < SpreadMode::SpreadRandom || newSpreadMode > SpreadMode::SpreadSpiral) ? SpreadMode::SpreadRandom : newSpreadMode; } #pragma endregion @@ -155,9 +115,7 @@ namespace RTE { private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - /// /// Clears all the member variables of this Gib, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/Entities/GlobalScript.cpp b/Source/Entities/GlobalScript.cpp index d2c9175ea7..0d02e32a3d 100644 --- a/Source/Entities/GlobalScript.cpp +++ b/Source/Entities/GlobalScript.cpp @@ -14,8 +14,6 @@ namespace RTE { ConcreteClassInfo(GlobalScript, Entity, 10); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GlobalScript::Clear() { m_ScriptPath.clear(); m_LuaClassName.clear(); @@ -25,8 +23,6 @@ namespace RTE { m_PieSlicesToAdd.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::Create(const GlobalScript& reference) { Entity::Create(reference); @@ -43,8 +39,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -56,8 +50,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::Save(Writer& writer) const { Entity::Save(writer); @@ -72,8 +64,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector>& GlobalScript::GetPieSlicesToAdd() const { static const std::vector> emptyVector; if (!m_HasStarted || !m_IsActive || !g_SettingsMan.IsGlobalScriptEnabled(GetModuleAndPresetName())) { @@ -83,8 +73,6 @@ namespace RTE { return m_PieSlicesToAdd; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::ReloadScripts() { int error = 0; @@ -101,8 +89,6 @@ namespace RTE { return error; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::Start() { if (!g_SettingsMan.IsGlobalScriptEnabled(GetModuleAndPresetName())) { return 0; @@ -122,8 +108,6 @@ namespace RTE { return error; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::Pause(bool pause) const { if (!m_IsActive || !m_HasStarted || !g_SettingsMan.IsGlobalScriptEnabled(GetModuleAndPresetName())) { return 0; @@ -132,8 +116,6 @@ namespace RTE { return g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".PauseScript then " + m_LuaClassName + ":PauseScript(" + (pause ? "true" : "false") + "); end"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GlobalScript::End() const { if (!m_HasStarted) { return 0; @@ -146,8 +128,6 @@ namespace RTE { return g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".EndScript then " + m_LuaClassName + ":EndScript(); end"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GlobalScript::HandleCraftEnteringOrbit(const ACraft* orbitedCraft) { if (!m_IsActive || !!m_HasStarted || orbitedCraft == nullptr || !g_MovableMan.IsActor(orbitedCraft) || !g_SettingsMan.IsGlobalScriptEnabled(GetModuleAndPresetName())) { return; @@ -159,8 +139,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GlobalScript::Update() { if (!m_IsActive) { return; diff --git a/Source/Entities/GlobalScript.h b/Source/Entities/GlobalScript.h index d3a43db7c5..e1cd37de65 100644 --- a/Source/Entities/GlobalScript.h +++ b/Source/Entities/GlobalScript.h @@ -7,9 +7,7 @@ namespace RTE { class ACraft; - /// /// The supporting object for a lua script that can be turned on and off by the player, and run independent of Activities. - /// class GlobalScript : public Entity { friend struct EntityLuaBindings; @@ -19,35 +17,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a GlobalScript object in system memory. Create() should be called before using the object. - /// GlobalScript() { Clear(); } - /// /// Makes the GlobalScript object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override { return 0; } - /// /// Creates an GlobalScript to be identical to another, by deep copy. - /// - /// A reference to the GlobalScript to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the GlobalScript to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const GlobalScript& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a GlobalScript object before deletion from system memory. - /// ~GlobalScript() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the GlobalScript object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); @@ -55,9 +43,7 @@ namespace RTE { Clear(); } - /// /// Resets the entire GlobalScript, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Entity::Reset(); @@ -65,66 +51,46 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets whether or not this GlobalScript is active. Active scripts can be deactivated automatically if it fails to execute it's Update function without errors to avoid filling the console with error messages. - /// - /// Whether or not this GlobalScript is active. + /// @return Whether or not this GlobalScript is active. bool IsActive() const { return m_IsActive; } - /// /// Sets whether or not this GlobalScript should be active. - /// - /// Whether or not this GlobalScript should be active. + /// @param active Whether or not this GlobalScript should be active. void SetActive(bool active) { m_IsActive = active; } - /// /// Gets whether or not this GlobalScript should be updated late, i.e. after the standard MovableMan update. - /// - /// Whether or not this GlobalScript should be updated late. + /// @return Whether or not this GlobalScript should be updated late. bool ShouldLateUpdate() const { return m_LateUpdate; } - /// /// Gets the list of PieSlices this GlobalScript adds to any active Actor PieMenus. - /// - /// The list of PieSilces this GlobalScript adds to any active Actor PieMenus + /// @return The list of PieSilces this GlobalScript adds to any active Actor PieMenus const std::vector>& GetPieSlicesToAdd() const; #pragma endregion #pragma region Concrete Methods - /// /// Reloads the Lua script specified by this GlobalScript. This will also update the original preset in the PresetMan with the update scripts so future objects spawned will use the new scripts. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int ReloadScripts() override; - /// /// Starts this GlobalScript by running the appropriate Lua function. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Start(); - /// /// Pauses and unpauses this GlobalScript by running the appropriate Lua function. - /// - /// Whether or not this GlobalScript should be paused. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param pause Whether or not this GlobalScript should be paused. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Pause(bool pause = true) const; - /// /// Ends this GlobalScript by running the appropriate Lua function - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int End() const; - /// /// Handles when an ACraft has left the game scene and entered orbit by running the appropriate Lua function. Ownership is NOT transferred! - /// - /// The ACraft instance that entered orbit. Ownership is NOT transferred! + /// @param orbitedCraft The ACraft instance that entered orbit. Ownership is NOT transferred! void HandleCraftEnteringOrbit(const ACraft* orbitedCraft); - /// /// Updates the state of this GlobalScript every frame. - /// void Update(); #pragma endregion @@ -139,9 +105,7 @@ namespace RTE { std::vector> m_PieSlicesToAdd; //!< A vector of PieSlices that should be added to any PieMenus opened while this GlobalScript is active. - /// /// Clears all the member variables of this GlobalScript, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/HDFirearm.cpp b/Source/Entities/HDFirearm.cpp index 72c11dda37..804435a3b0 100644 --- a/Source/Entities/HDFirearm.cpp +++ b/Source/Entities/HDFirearm.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: HDFirearm.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the HDFirearm class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "HDFirearm.h" #include "ActivityMan.h" @@ -27,12 +15,6 @@ namespace RTE { ConcreteClassInfo(HDFirearm, HeldDevice, 50); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this HDFirearm, effectively - // resetting the members of this abstraction level only. - void HDFirearm::Clear() { m_pMagazineReference = 0; m_pMagazine = 0; @@ -88,11 +70,6 @@ namespace RTE { m_LegacyCompatibilityRoundsAlwaysFireUnflipped = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Round object ready for use. - int HDFirearm::Create() { if (HeldDevice::Create() < 0) return -1; @@ -103,11 +80,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a HDFirearm to be identical to another, by deep copy. - int HDFirearm::Create(const HDFirearm& reference) { if (reference.m_pMagazine) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pMagazine->GetUniqueID()); @@ -186,14 +158,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int HDFirearm::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return HeldDevice::ReadProperty(propName, reader)); @@ -277,12 +241,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this HDFirearm with a Writer for - // later recreation with Create(Reader &reader); - int HDFirearm::Save(Writer& writer) const { HeldDevice::Save(writer); @@ -357,11 +315,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the HDFirearm object. - void HDFirearm::Destroy(bool notInherited) { if (m_PreFireSound) { m_PreFireSound->Stop(); @@ -402,8 +355,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void HDFirearm::SetMagazine(Magazine* newMagazine) { if (m_pMagazine && m_pMagazine->IsAttached()) { RemoveAndDeleteAttachable(m_pMagazine); @@ -427,8 +378,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void HDFirearm::SetFlash(Attachable* newFlash) { if (m_pFlash && m_pFlash->IsAttached()) { RemoveAndDeleteAttachable(m_pFlash); @@ -452,14 +401,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string HDFirearm::GetNextMagazineName() const { return m_pMagazineReference->GetPresetName(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool HDFirearm::SetNextMagazineName(std::string magName) { const Magazine* pNewMag = dynamic_cast(g_PresetMan.GetEntityPreset("Magazine", magName)); if (pNewMag) { @@ -473,17 +418,10 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRoundInMagCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of rounds still in the loaded magazine. - int HDFirearm::GetRoundInMagCount() const { return m_pMagazine ? m_pMagazine->GetRoundCount() : 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int HDFirearm::GetRoundInMagCapacity() const { if (m_pMagazine) { return m_pMagazine->GetCapacity(); @@ -493,11 +431,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIFireVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the velocity the AI use when aiming this weapon - float HDFirearm::GetAIFireVel() { if (m_AIFireVel < 0 && m_pMagazine) m_AIFireVel = m_pMagazine->GetAIAimVel(); @@ -505,11 +438,6 @@ namespace RTE { return m_AIFireVel; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIBulletLifeTime - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the bullet life time the AI use when aiming this weapon. - unsigned long HDFirearm::GetAIBulletLifeTime() { if (m_AIBulletLifeTime == 0 && m_pMagazine) { const Round* pRound = m_pMagazine->GetNextRound(); @@ -525,11 +453,6 @@ namespace RTE { return m_AIBulletLifeTime; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBulletAccScalar - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. - float HDFirearm::GetBulletAccScalar() { if (m_AIBulletAccScalar < 0 && m_pMagazine) m_AIBulletAccScalar = m_pMagazine->GetBulletAccScalar(); @@ -537,11 +460,6 @@ namespace RTE { return m_AIBulletAccScalar; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIBlastRadius - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the blast radius the AI use when aiming this weapon - float HDFirearm::GetAIBlastRadius() const { int radius = -1; if (m_pMagazine) @@ -558,11 +476,6 @@ namespace RTE { return radius; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIPenetration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how much material the projectiles from this weapon can destory. - float HDFirearm::GetAIPenetration() const { if (m_pMagazine) return m_pMagazine->GetAIAimPenetration(); @@ -570,11 +483,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CompareTrajectories - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Estimates how close the projectiles from two weapons will land. - float HDFirearm::CompareTrajectories(HDFirearm* pWeapon) { if (pWeapon) { // Get AI aim data and cap life time to one second @@ -606,28 +514,14 @@ namespace RTE { return 100000; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMagazinePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of the magazine or other equivalent point of - // this. - Vector HDFirearm::GetMagazinePos() const { return m_Pos + RotateOffset(m_MagOff); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: GetMuzzlePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of the muzzle or other equivalent point of - // this. - Vector HDFirearm::GetMuzzlePos() const { return m_Pos + RotateOffset(m_MuzzleOff); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void HDFirearm::RestDetection() { HeldDevice::RestDetection(); @@ -636,12 +530,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Activate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activates one of this HDFirearm's features. Analogous to 'pulling - // the trigger'. - void HDFirearm::Activate() { bool wasActivated = m_Activated; HeldDevice::Activate(); @@ -659,12 +547,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Deactivate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing - // the trigger'. - void HDFirearm::Deactivate() { bool wasActivated = m_Activated; HeldDevice::Deactivate(); @@ -681,12 +563,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StopActivationSound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Aborts playing of active sound no matter what. Used to silence spinning - // weapons when weapons swapped - void HDFirearm::StopActivationSound() { if (m_ActiveSound && m_ActiveSound->IsBeingPlayed()) m_ActiveSound->Stop(); @@ -698,8 +574,6 @@ namespace RTE { // m_LastFireTmr.SetElapsedSimTimeMS(m_DeactivationDelay + 1); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void HDFirearm::Reload() { if (!m_Reloading && m_Reloadable) { bool hadMagazineBeforeReloading = m_pMagazine != nullptr; @@ -730,13 +604,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: NeedsReloading - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is curtrently in need of being reloaded. - bool HDFirearm::NeedsReloading() const { if (!m_Reloading && m_Reloadable) { if (m_pMagazine) { @@ -749,12 +616,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsFull - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is curtrently full and reloading won't have - // any effect. - bool HDFirearm::IsFull() const { if (!m_Reloading && m_Reloadable) { if (m_pMagazine) { @@ -767,8 +628,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool HDFirearm::IsEmpty() const { if (m_pMagazine) { return m_pMagazine->IsEmpty(); @@ -776,13 +635,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this HDFirearm. Supposed to be done every frame. - void HDFirearm::Update() { HeldDevice::Update(); @@ -1146,21 +998,10 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EstimateDigStrength - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Estimates what material strength the rounds in the magazine can destroy. - float HDFirearm::EstimateDigStrength() const { return m_pMagazine ? m_pMagazine->EstimateDigStrength() : m_pMagazineReference->EstimateDigStrength(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this HDFirearm's current graphical representation to a - // BITMAP of choice. - void HDFirearm::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { if (m_pFlash && m_FireFrame && !m_pFlash->IsDrawnAfterParent() && mode == g_DrawColor && !onlyPhysical) { m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); @@ -1173,11 +1014,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws an aiming aid in front of this HeldDevice. - void HDFirearm::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { if (!m_HUDVisible) return; diff --git a/Source/Entities/HDFirearm.h b/Source/Entities/HDFirearm.h index 127a60b288..0173757f04 100644 --- a/Source/Entities/HDFirearm.h +++ b/Source/Entities/HDFirearm.h @@ -1,36 +1,21 @@ #ifndef _RTEHDFIREARM_ #define _RTEHDFIREARM_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: HDFirearm.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the HDFirearm class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the HDFirearm class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "HeldDevice.h" namespace RTE { class Magazine; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: HDFirearm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A firearm device that fires projectile MO's and discharges shell MO's. - // Parent(s): HeldDevice. - // Class history: 07/1/2002 HDFirearm created. - + /// A firearm device that fires projectile MO's and discharges shell MO's. class HDFirearm : public HeldDevice { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(HDFirearm); @@ -38,816 +23,451 @@ namespace RTE { ClassInfoGetters; AddScriptFunctionNames(HeldDevice, "OnFire", "OnReload"); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: HDFirearm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a HDFirearm object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a HDFirearm object in system + /// memory. Create() should be called before using the object. HDFirearm() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~HDFirearm - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a HDFirearm object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a HDFirearm object before deletion + /// from system memory. ~HDFirearm() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the HDFirearm object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the HDFirearm object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a HDFirearm to be identical to another, by deep copy. - // Arguments: A reference to the HDFirearm to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a HDFirearm to be identical to another, by deep copy. + /// @param reference A reference to the HDFirearm to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const HDFirearm& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire HDFirearm, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire HDFirearm, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); HeldDevice::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetReloadEndOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets reload end offset, in ms. This is how early the ReloadEnd - // sound is played compared to actual end of reload. - // Arguments: None. - // Return value: The reload end offset, in ms. - + /// Gets reload end offset, in ms. This is how early the ReloadEnd + /// sound is played compared to actual end of reload. + /// @return The reload end offset, in ms. int GetReloadEndOffset() const { return m_ReloadEndOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetReloadEndOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets reload end offset, in ms. This is how early the ReloadEnd - // sound is played compared to actual end of reload. - // Arguments: The new reload end offset, in ms. - // Return value: None. - + /// Sets reload end offset, in ms. This is how early the ReloadEnd + /// sound is played compared to actual end of reload. + /// @param newRate The new reload end offset, in ms. void SetReloadEndOffset(int newRate) { m_ReloadEndOffset = newRate; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRateOfFire - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rate of fire of this. This applies even if semi-auto. it - // limits how quickly a new round can be fired after the last. - // Arguments: None. - // Return value: The rate of fire, in rounds per min. - + /// Gets the rate of fire of this. This applies even if semi-auto. it + /// limits how quickly a new round can be fired after the last. + /// @return The rate of fire, in rounds per min. int GetRateOfFire() const { return m_RateOfFire; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetRateOfFire - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the rate of fire of this. This applies even if semi-auto. it - // limits how quickly a new round can be fired after the last. - // Arguments: The new rate of fire, in rounds per min. - // Return value: None. - + /// Sets the rate of fire of this. This applies even if semi-auto. it + /// limits how quickly a new round can be fired after the last. + /// @param newRate The new rate of fire, in rounds per min. void SetRateOfFire(int newRate) { m_RateOfFire = newRate; } - /// /// Gets the minimum time in between shots, in MS. - /// - /// The minimum time in between shots, in MS. + /// @return The minimum time in between shots, in MS. double GetMSPerRound() const { return 60000.0 / static_cast(m_RateOfFire); } - /// /// Gets the Magazine of this HDFirearm. - /// - /// A pointer to Magazine of this HDFirearm. Ownership is NOT transferred! + /// @return A pointer to Magazine of this HDFirearm. Ownership is NOT transferred! Magazine* GetMagazine() const { return m_pMagazine; } - /// /// Sets the Magazine for this HDFirearm. Ownership IS transferred! - /// - /// The new Magazine to use. + /// @param newMagazine The new Magazine to use. void SetMagazine(Magazine* newMagazine); - /// /// Gets the flash of this HDFirearm. - /// - /// A pointer to flash of this HDFirearm. Ownership is NOT transferred! + /// @return A pointer to flash of this HDFirearm. Ownership is NOT transferred! Attachable* GetFlash() const { return m_pFlash; } - /// /// Sets the flash for this HDFirearm. Ownership IS transferred! - /// - /// The new flash to use. + /// @param newTurret The new flash to use. void SetFlash(Attachable* newFlash); - /// /// Gets the preset name of the next Magazine that will be loaded into this gun. - /// - /// The preset name of the next Magazine that will be loaded into this gun. + /// @return The preset name of the next Magazine that will be loaded into this gun. std::string GetNextMagazineName() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetNextMagazineName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the Preset name of the next Magazine that will be loaded into - // this gun. This changes all future mags that will be reloaded. - // Arguments: The preset name of the new Magazine to load into this from now on. - // Return value: Whether the specified magazine was found and successfully prepared. - + /// Sets the Preset name of the next Magazine that will be loaded into + /// this gun. This changes all future mags that will be reloaded. + /// @param magName The preset name of the new Magazine to load into this from now on. + /// @return Whether the specified magazine was found and successfully prepared. bool SetNextMagazineName(std::string magName); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRoundInMagCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of rounds still in the loaded magazine. Negative value - // means infinite ammo. - // Arguments: None. - // Return value: An int with the number of rounds in the magazine currently in this - // HDFirearm. Negative means infinite ammo. - + /// Gets the number of rounds still in the loaded magazine. Negative value + /// means infinite ammo. + /// @return An int with the number of rounds in the magazine currently in this + /// HDFirearm. Negative means infinite ammo. int GetRoundInMagCount() const; - /// /// Gets the maximum RoundCount a Magazine of this HDFirearm can hold. /// If there is no Magazine, it gets the RoundCount of the reference Magazine. - /// - /// An int with the maximum RoundCount the magazine or magazine reference of this HDFirearm can hold. + /// @return An int with the maximum RoundCount the magazine or magazine reference of this HDFirearm can hold. int GetRoundInMagCapacity() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActivationDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the delay before firing. - // Arguments: None. - // Return value: An int with the activation delay in ms. - + /// Gets the delay before firing. + /// @return An int with the activation delay in ms. int GetActivationDelay() const { return m_ActivationDelay; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetActivationDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the delay before firing. - // Arguments: An int with the activation delay in ms. - // Return value: None. - + /// Sets the delay before firing. + /// @param delay An int with the activation delay in ms. void SetActivationDelay(int delay) { m_ActivationDelay = delay; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDeactivationDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the delay between release of activation and another can be started. - // Arguments: None. - // Return value: An int with the delay in ms. - + /// Gets the delay between release of activation and another can be started. + /// @return An int with the delay in ms. int GetDeactivationDelay() const { return m_DeactivationDelay; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDeactivationDelay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the delay between release of activation and another can be started. - // Arguments: An int with the delay in ms. - // Return value: None. - + /// Sets the delay between release of activation and another can be started. + /// @param delay An int with the delay in ms. void SetDeactivationDelay(int delay) { m_DeactivationDelay = delay; }; - /// /// Gets the base time this HDFirearm takes to reload, in milliseconds. - /// - /// The base time this HeldDevice takes to reload, in milliseconds. + /// @return The base time this HeldDevice takes to reload, in milliseconds. int GetBaseReloadTime() const { return m_BaseReloadTime; }; - /// /// Sets the base time this HDFirearm takes to reload, in milliseconds. - /// - /// The base time this HDFirearm should take to reload, in milliseconds. + /// @param delay The base time this HDFirearm should take to reload, in milliseconds. void SetBaseReloadTime(int newReloadTime) { m_BaseReloadTime = newReloadTime; CorrectReloadTimerForSupportAvailable(); }; - /// /// Gets how long this HDFirearm currently takes to reload, in milliseconds. - /// - /// How long this HDFirearm currently takes to reload, in milliseconds. + /// @return How long this HDFirearm currently takes to reload, in milliseconds. int GetReloadTime() const { return m_ReloadTmr.GetSimTimeLimitMS() <= 0 ? m_BaseReloadTime : static_cast(std::floor(m_ReloadTmr.GetSimTimeLimitMS())); }; - /// /// Gets whether or not this HDFirearm allows dual-reload, i.e. if it's one-handed and dual-wieldable, it can reload at the same time as another weapon that also allows dual-reload. - /// - /// Whether or not this HDFirearm allows dual-reload. + /// @return Whether or not this HDFirearm allows dual-reload. bool IsDualReloadable() const { return m_DualReloadable; } - /// /// Sets whether or not this HDFirearm allows dual-reloading. - /// - /// The new value for whether or not this HDFirearm should allow dual-reloading. + /// @param newDualReloadable The new value for whether or not this HDFirearm should allow dual-reloading. void SetDualReloadable(bool newDualReloadable) { m_DualReloadable = newDualReloadable; } - /// /// Gets the multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. - /// - /// The multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. + /// @return The multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. float GetOneHandedReloadTimeMultiplier() const { return m_OneHandedReloadTimeMultiplier; } - /// /// Sets the multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. - /// - /// The new multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. + /// @param newDualReloadTimeMultiplier The new multiplier to be applied to reload time when this HDFirearm is being reloaded one-handed. void SetOneHandedReloadTimeMultiplier(float newOneHandedReloadTimeMultiplier) { m_OneHandedReloadTimeMultiplier = newOneHandedReloadTimeMultiplier; } - /// /// Gets the reload angle this HDFirearm will use when support is available. - /// - /// The reload angle this HDFirearm will use when support is available, in radians. + /// @return The reload angle this HDFirearm will use when support is available, in radians. float GetReloadAngle() const { return m_ReloadAngle; } - /// /// Sets the reload angle this HDFirearm should use when support is available. - /// - /// The new reload angle this HDFirearm should use when support is available. + /// @param newReloadAngle The new reload angle this HDFirearm should use when support is available. void SetReloadAngle(float newReloadAngle) { m_ReloadAngle = newReloadAngle; } - /// /// Gets the reload angle this HDFirearm will use when support is not available. - /// - /// The reload angle this HDFirearm will use when support is not available, in radians. + /// @return The reload angle this HDFirearm will use when support is not available, in radians. float GetOneHandedReloadAngle() const { return m_OneHandedReloadAngle; } - /// /// Sets the reload angle this HDFirearm should use when support is not available. - /// - /// The new reload angle this HDFirearm should use when support is not available. + /// @param newOneHandedReloadAngle The new reload angle this HDFirearm should use when support is not available. void SetOneHandedReloadAngle(float newOneHandedReloadAngle) { m_OneHandedReloadAngle = newOneHandedReloadAngle; } - /// /// Gets the reload angle this HDFirearm is currently using, based on whether or not support is available. - /// - /// The current reload angle of this HDFirearm, in radians. + /// @return The current reload angle of this HDFirearm, in radians. float GetCurrentReloadAngle() const { return m_SupportAvailable ? m_ReloadAngle : m_OneHandedReloadAngle; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetShakeRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the range of normal shaking of entire weapon. - // Arguments: None. - // Return value: A float with the range in degrees. - + /// Gets the range of normal shaking of entire weapon. + /// @return A float with the range in degrees. float GetShakeRange() const { return m_ShakeRange; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetShakeRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the range of normal shaking of entire weapon. - // Arguments: A float with the range in degrees. - // Return value: None. - + /// Sets the range of normal shaking of entire weapon. + /// @param range A float with the range in degrees. void SetShakeRange(float range) { m_ShakeRange = range; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSharpShakeRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the range of shaking of entire weapon during sharp aiming. - // Arguments: None. - // Return value: A float with the range in degrees. - + /// Gets the range of shaking of entire weapon during sharp aiming. + /// @return A float with the range in degrees. float GetSharpShakeRange() const { return m_SharpShakeRange; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSharpShakeRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the range of shaking of entire weapon during sharp aiming. - // Arguments: A float with the range in degrees. - // Return value: None. - + /// Sets the range of shaking of entire weapon during sharp aiming. + /// @param range A float with the range in degrees. void SetSharpShakeRange(float range) { m_SharpShakeRange = range; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNoSupportFactor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the factor for how much more weapon shakes if it isn't supported - // by a second hand. - // Arguments: None. - // Return value: A float with the factor. - + /// Gets the factor for how much more weapon shakes if it isn't supported + /// by a second hand. + /// @return A float with the factor. float GetNoSupportFactor() const { return m_NoSupportFactor; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetNoSupportFactor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the factor for how much more weapon shakes if it isn't supported - // by a second hand. - // Arguments: A float with the factor. - // Return value: None. - + /// Sets the factor for how much more weapon shakes if it isn't supported + /// by a second hand. + /// @param factor A float with the factor. void SetNoSupportFactor(float factor) { m_NoSupportFactor = factor; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetParticleSpreadRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the range of spread angle of fired particles, in one direction. - // Arguments: None. - // Return value: A float with the range in degrees. - + /// Gets the range of spread angle of fired particles, in one direction. + /// @return A float with the range in degrees. float GetParticleSpreadRange() const { return m_ParticleSpreadRange; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetParticleSpreadRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the range of spread angle of fired particles, in one direction. - // Arguments: A float with the range in degrees. - // Return value: None. - + /// Sets the range of spread angle of fired particles, in one direction. + /// @param range A float with the range in degrees. void SetParticleSpreadRange(float range) { m_ParticleSpreadRange = range; }; - /// /// Gets the random velocity variation scalar at which this HDFirearm's shell is to be ejected. - /// - /// A float with the scalar value. + /// @return A float with the scalar value. float GetShellVelVariation() const { return m_ShellVelVariation; } - /// /// Sets the random velocity variation scalar at which this HDFirearm's shell is to be ejected. - /// - /// The new velocity variation scalar. + /// @param newValue The new velocity variation scalar. void SetShellVelVariation(float newVariation) { m_ShellVelVariation = newVariation; } - /// /// Sets the stiffness scalar of the joint of this HDFirearm. Unlike Attachable::SetJointStiffness, there are no limitations on this value. /// 1.0 means impulse forces on this attachable will be transferred to the parent with 100% strength, 0 means they will not transfer at all, negative values will apply negative force, which may behave oddly. - /// - /// A float describing the normalized stiffness scalar of this Attachable's joint. + /// @param jointStiffness A float describing the normalized stiffness scalar of this Attachable's joint. void SetJointStiffness(float jointStiffness) override { m_JointStiffness = jointStiffness; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIFireVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the velocity the AI use when aiming this weapon. - // Arguments: None. - // Return value: A float with the velocity in m/s. - + /// Gets the velocity the AI use when aiming this weapon. + /// @return A float with the velocity in m/s. float GetAIFireVel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIBulletLifeTime - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the bullet life time the AI use when aiming this weapon. - // Arguments: None. - // Return value: A float with the life time in ms. - + /// Gets the bullet life time the AI use when aiming this weapon. + /// @return A float with the life time in ms. unsigned long GetAIBulletLifeTime(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBulletAccScalar - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. - // Arguments: None. - // Return value: A float with the scalar. - + /// Gets the bullet acceleration scalar the AI use when aiming this weapon. + /// @return A float with the scalar. float GetBulletAccScalar(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIBlastRadius - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the blast radius the AI use when aiming this weapon. - // Arguments: None. - // Return value: A float with the blast radius in pixels. - + /// Gets the blast radius the AI use when aiming this weapon. + /// @return A float with the blast radius in pixels. float GetAIBlastRadius() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIPenetration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how much material the projectiles from this weapon can destory. - // Arguments: None. - // Return value: A float with the material strength. - + /// Gets how much material the projectiles from this weapon can destory. + /// @return A float with the material strength. float GetAIPenetration() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CompareTrajectories - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Estimates how close the projectiles from two weapons will land. - // Arguments: A HDFirearm pointer to compare with. - // Return value: A float with the distance in pixels. - + /// Estimates how close the projectiles from two weapons will land. + /// @param pWeapon A HDFirearm pointer to compare with. + /// @return A float with the distance in pixels. float CompareTrajectories(HDFirearm* pWeapon); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMagazinePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of the magazine or other equivalent point of - // this. - // Arguments: None. - // Return value: A vector describing the absolute world coordinates for the magazine - // attachment point of this - + /// Gets the absolute position of the magazine or other equivalent point of + /// this. + /// @return A vector describing the absolute world coordinates for the magazine + /// attachment point of this Vector GetMagazinePos() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMuzzlePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of the muzzle or other equivalent point of - // this. - // Arguments: None. - // Return value: A vector describing the absolute world coordinates for the muzzle point - // of this - + /// Gets the absolute position of the muzzle or other equivalent point of + /// this. + /// @return A vector describing the absolute world coordinates for the muzzle point + /// of this Vector GetMuzzlePos() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMuzzleOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the unrotated relative offset from the position to the muzzle or - // other equivalent point of this. - // Arguments: None. - // Return value: A unrotated vector describing the relative for the muzzle point of - // this from this' position. - + /// Gets the unrotated relative offset from the position to the muzzle or + /// other equivalent point of this. + /// @return A unrotated vector describing the relative for the muzzle point of + /// this from this' position. Vector GetMuzzleOffset() const override { return m_MuzzleOff; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetMuzzleOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the unrotated relative offset from the position to the muzzle or - // other equivalent point of this. - // Arguments: Bew ofsset value. - // Return value: None. - + /// Sets the unrotated relative offset from the position to the muzzle or + /// other equivalent point of this. + /// @param newOffset Bew ofsset value. void SetMuzzleOffset(Vector newOffset) override { m_MuzzleOff = newOffset; } - /// /// Gets this HDFirearm's pre fire sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's pre fire sound. + /// @return The SoundContainer for this HDFirearm's pre fire sound. SoundContainer* GetPreFireSound() const { return m_PreFireSound; } - /// /// Sets this HDFirearm's pre fire sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's pre fire sound. + /// @param newSound The new SoundContainer for this HDFirearm's pre fire sound. void SetPreFireSound(SoundContainer* newSound) { m_PreFireSound = newSound; } - /// /// Gets this HDFirearm's fire sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's fire sound. + /// @return The SoundContainer for this HDFirearm's fire sound. SoundContainer* GetFireSound() const { return m_FireSound; } - /// /// Sets this HDFirearm's fire sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's fire sound. + /// @param newSound The new SoundContainer for this HDFirearm's fire sound. void SetFireSound(SoundContainer* newSound) { m_FireSound = newSound; } - /// /// Gets this HDFirearm's fire echo sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's fire echo sound. + /// @return The SoundContainer for this HDFirearm's fire echo sound. SoundContainer* GetFireEchoSound() const { return m_FireEchoSound; } - /// /// Sets this HDFirearm's fire echo sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's fire echo sound. + /// @param newSound The new SoundContainer for this HDFirearm's fire echo sound. void SetFireEchoSound(SoundContainer* newSound) { m_FireEchoSound = newSound; } - /// /// Gets this HDFirearm's active sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's active sound. + /// @return The SoundContainer for this HDFirearm's active sound. SoundContainer* GetActiveSound() const { return m_ActiveSound; } - /// /// Sets this HDFirearm's active sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's active sound. + /// @param newSound The new SoundContainer for this HDFirearm's active sound. void SetActiveSound(SoundContainer* newSound) { m_ActiveSound = newSound; } - /// /// Gets this HDFirearm's deactivation sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's deactivation sound. + /// @return The SoundContainer for this HDFirearm's deactivation sound. SoundContainer* GetDeactivationSound() const { return m_DeactivationSound; } - /// /// Sets this HDFirearm's deactivation sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's deactivation sound. + /// @param newSound The new SoundContainer for this HDFirearm's deactivation sound. void SetDeactivationSound(SoundContainer* newSound) { m_DeactivationSound = newSound; } - /// /// Gets this HDFirearm's empty sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's empty sound. + /// @return The SoundContainer for this HDFirearm's empty sound. SoundContainer* GetEmptySound() const { return m_EmptySound; } - /// /// Sets this HDFirearm's empty sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's empty sound. + /// @param newSound The new SoundContainer for this HDFirearm's empty sound. void SetEmptySound(SoundContainer* newSound) { m_EmptySound = newSound; } - /// /// Gets this HDFirearm's reload start sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's reload start sound. + /// @return The SoundContainer for this HDFirearm's reload start sound. SoundContainer* GetReloadStartSound() const { return m_ReloadStartSound; } - /// /// Sets this HDFirearm's reload start sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's reload start sound. + /// @param newSound The new SoundContainer for this HDFirearm's reload start sound. void SetReloadStartSound(SoundContainer* newSound) { m_ReloadStartSound = newSound; } - /// /// Gets this HDFirearm's reload end sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this HDFirearm's reload end sound. + /// @return The SoundContainer for this HDFirearm's reload end sound. SoundContainer* GetReloadEndSound() const { return m_ReloadEndSound; } - /// /// Sets this HDFirearm's reload end sound. Ownership IS transferred! - /// - /// The new SoundContainer for this HDFirearm's reload end sound. + /// @param newSound The new SoundContainer for this HDFirearm's reload end sound. void SetReloadEndSound(SoundContainer* newSound) { m_ReloadEndSound = newSound; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ResetAllTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resest all the timers used by this. Can be emitters, etc. This is to - // prevent backed up emissions to come out all at once while this has been - // held dormant in an inventory. - // Arguments: None. - // Return value: None. - + /// Resest all the timers used by this. Can be emitters, etc. This is to + /// prevent backed up emissions to come out all at once while this has been + /// held dormant in an inventory. void ResetAllTimers() override { HeldDevice::ResetAllTimers(); m_LastFireTmr.Reset(); m_ReloadTmr.Reset(); } - /// /// Gets this HDFirearm's reload progress as a scalar from 0 to 1. - /// - /// The reload progress as a scalar from 0 to 1. + /// @return The reload progress as a scalar from 0 to 1. float GetReloadProgress() const { return IsReloading() && m_BaseReloadTime > 0 ? static_cast(m_ReloadTmr.SimTimeLimitProgress()) : 1.0F; } - /// /// Does the calculations necessary to detect whether this HDFirearm is at rest or not. IsAtRest() retrieves the answer. - /// void RestDetection() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Activate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activates one of this HDFirearm's features. Analogous to 'pulling - // the trigger'. - // Arguments: None. - // Return value: None. - + /// Activates one of this HDFirearm's features. Analogous to 'pulling + /// the trigger'. void Activate() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Deactivate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing - // the trigger'. - // Arguments: None. - // Return value: None. - + /// Deactivates one of this HDFirearm's features. Analogous to 'releasing + /// the trigger'. void Deactivate() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StopActivationSound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Aborts playing of active sound no matter what. Used to silence spinning - // weapons when weapons swapped - // Arguments: None. - // Return value: None. - + /// Method: StopActivationSound + /// Aborts playing of active sound no matter what. Used to silence spinning + /// weapons when weapons swapped void StopActivationSound(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reload - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Throws out the currently used Magazine, if any, and puts in a new one - // after the reload delay is up. - // Arguments: None. - // Return value: None. - + /// Throws out the currently used Magazine, if any, and puts in a new one + /// after the reload delay is up. void Reload() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsReloading - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is curtrently being reloaded. - // Arguments: None. - // Return value: Whetehr being reloaded. - + /// Tells whether the device is curtrently being reloaded. + /// @return Whetehr being reloaded. bool IsReloading() const override { return m_Reloading; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DoneReloading - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device just finished reloading this frame. - // Arguments: None. - // Return value: Whether just done reloading this frame. - + /// Tells whether the device just finished reloading this frame. + /// @return Whether just done reloading this frame. bool DoneReloading() const override { return m_DoneReloading; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: NeedsReloading - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is curtrently in need of being reloaded. - // Arguments: None. - // Return value: Whetehr in need of reloading (ie not full). - + /// Tells whether the device is curtrently in need of being reloaded. + /// @return Whetehr in need of reloading (ie not full). bool NeedsReloading() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsFull - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is curtrently full and reloading won't have - // any effect. - // Arguments: None. - // Return value: Whetehr magazine is full or not. - + /// Tells whether the device is curtrently full and reloading won't have + /// any effect. + /// @return Whetehr magazine is full or not. bool IsFull() const override; bool IsEmpty() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsFullAuto - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is fully automatic or not. - // Arguments: None. - // Return value: Whether the player can hold down fire and this will fire repeatedly. - + /// Tells whether the device is fully automatic or not. + /// @return Whether the player can hold down fire and this will fire repeatedly. bool IsFullAuto() const { return m_FullAuto; } - /// /// Gets whether this HDFirearm is set to be reloadable or not. - /// - /// Whether this HDFirearm is reloadable. + /// @return Whether this HDFirearm is reloadable. bool IsReloadable() const { return m_Reloadable; } - /// /// Sets whether this HDFirearm is reloadable or not and halts the reloading process. - /// - /// Whether this HDFirearm is reloadable. + /// @param isReloadable Whether this HDFirearm is reloadable. void SetReloadable(bool isReloadable) { m_Reloadable = isReloadable; m_Reloading = m_Reloading && m_Reloadable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFullAuto - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether the device is fully automatic or not. - // Arguments: New value. - // Return value: None. - + /// Sets whether the device is fully automatic or not. + /// @param newValue New value. void SetFullAuto(bool newValue) { m_FullAuto = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this HDFirearm's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this HDFirearm's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws an aiming aid in front of this HeldDevice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // Which player's screen this is being drawn to. May affect what HUD elements - // get drawn etc. - // Return value: None. - + /// Draws an aiming aid in front of this HeldDevice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param whichScreen Which player's screen this is being drawn to. May affect what HUD elements (default: 0) + /// get drawn etc. void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EstimateDigStrength - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Estimates what material strength one round in the magazine can destroy. - // Arguments: None. - // Return value: The maximum material strength the regular or the tracer round can destroy. - + /// Estimates what material strength one round in the magazine can destroy. + /// @return The maximum material strength the regular or the tracer round can destroy. float EstimateDigStrength() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FiredOnce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Whether at least one round has already been fired during the current activation. - // Arguments: None. - // Return value: Returns true if at least one round has already been fired during the current activation. - + /// Whether at least one round has already been fired during the current activation. + /// @return Returns true if at least one round has already been fired during the current activation. bool FiredOnce() const { return m_FiredOnce; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FiredFrame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Whether at least one round has already been fired during the current frame. - // Arguments: None. - // Return value: Returns true at least one round has already been fired during the current frame. - + /// Whether at least one round has already been fired during the current frame. + /// @return Returns true at least one round has already been fired during the current frame. bool FiredFrame() const { return m_FireFrame; } - /// /// Gets whether this HDFirearm is ready to be fired. - /// - /// Whether this HDFirearm is ready to pop another Round. + /// @return Whether this HDFirearm is ready to pop another Round. bool CanFire() const { return m_LastFireTmr.IsPastSimMS(GetMSPerRound()); } - /// /// Gets whether this HDFirearm is halfway to be fired. Used for evenly spacing out dual-wielded fire. - /// - /// Whether this HDFirearm is halfway to pop another Round. + /// @return Whether this HDFirearm is halfway to pop another Round. bool HalfwayToNextRound() const { return m_LastFireTmr.IsPastSimMS(GetMSPerRound() / 2.0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RoundsFired - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: How many rounds were fired during this frame. - // Arguments: None. - // Return value: Returns the number of rounds fired during this frame. - + /// How many rounds were fired during this frame. + /// @return Returns the number of rounds fired during this frame. int RoundsFired() const { return m_RoundsFired; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsAnimatedManually - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: If true then the m_Frame property is not changed bye the Update function - // Arguments: None. - // Return value: Whether this HDFirearm is animated manually. - + /// If true then the m_Frame property is not changed bye the Update function + /// @return Whether this HDFirearm is animated manually. bool IsAnimatedManually() const { return m_IsAnimatedManually; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetAnimatedManually - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets Whether this HDFirearm is animated manually. - // Arguments: Manual animation flag value. - // Return value: None. - + /// Sets Whether this HDFirearm is animated manually. + /// @param newValue Manual animation flag value. void SetAnimatedManually(bool newValue) { m_IsAnimatedManually = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Sets this Attachable's parent MOSRotating, and also sets its Team based on its parent and, if the Attachable is set to collide, adds/removes Atoms to its new/old parent. /// Additionally, sets this HDFirearm as not firing or reloading, and resets its reload timer. - /// - /// A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! + /// @param newParent A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! void SetParent(MOSRotating* newParent) override { HeldDevice::SetParent(newParent); Deactivate(); @@ -969,23 +589,13 @@ namespace RTE { std::string m_BallisticScriptFunction; */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - /// /// Ensures the reload Timer's time limit is set accordingly, based on whether the HDFirearm has support available. - /// void CorrectReloadTimerForSupportAvailable() { m_ReloadTmr.SetSimTimeLimitMS(static_cast(static_cast(m_BaseReloadTime) * (m_SupportAvailable ? 1.0F : m_OneHandedReloadTimeMultiplier))); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this HDFirearm, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this HDFirearm, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/HeldDevice.cpp b/Source/Entities/HeldDevice.cpp index ce460701cf..9873639a24 100644 --- a/Source/Entities/HeldDevice.cpp +++ b/Source/Entities/HeldDevice.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: HeldDevice.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the HeldDevice class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "HeldDevice.h" #include "CameraMan.h" @@ -30,12 +18,6 @@ namespace RTE { ConcreteClassInfo(HeldDevice, Attachable, 50); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this HeldDevice, effectively - // resetting the members of this abstraction level only. - void HeldDevice::Clear() { m_HeldDeviceType = WEAPON; m_Activated = false; @@ -66,11 +48,6 @@ namespace RTE { m_CollidesWithTerrainWhileAttached = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Round object ready for use. - int HeldDevice::Create() { if (Attachable::Create() < 0) return -1; @@ -122,11 +99,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a HeldDevice to be identical to another, by deep copy. - int HeldDevice::Create(const HeldDevice& reference) { Attachable::Create(reference); @@ -164,14 +136,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int HeldDevice::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Attachable::ReadProperty(propName, reader)); @@ -219,12 +183,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this HeldDevice with a Writer for - // later recreation with Create(Reader &reader); - int HeldDevice::Save(Writer& writer) const { Attachable::Save(writer); /* @@ -257,11 +215,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the HeldDevice object. - void HeldDevice::Destroy(bool notInherited) { if (!notInherited) @@ -269,14 +222,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetStanceOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current position offset of this HeldDevice's joint relative - // from the parent Actor's position, if attached. - // Arguments: None. - // Return value: A const reference to the current stance parent offset. - Vector HeldDevice::GetStanceOffset() const { if (m_SharpAim > 0) { float rotAngleScalar = std::abs(std::sin(GetRootParent()->GetRotAngle())); @@ -286,12 +231,6 @@ namespace RTE { return m_StanceOffset.GetXFlipped(m_HFlipped); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSupportPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of the support handhold that this HeldDevice - // offers. - Vector HeldDevice::GetSupportPos() const { /* Vector rotOff(m_SupportOffset.GetYFlipped(m_HFlipped)); @@ -301,24 +240,14 @@ namespace RTE { return m_Pos + RotateOffset(m_SupportOffset); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMagazinePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of the magazine or other equivalent point of - // this. - Vector HeldDevice::GetMagazinePos() const { return m_Pos; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool HeldDevice::IsBeingHeld() const { return dynamic_cast(m_Parent); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void HeldDevice::RemovePickupableByPresetName(const std::string& actorPresetName) { std::unordered_set::iterator pickupableByPresetNameEntry = m_PickupableByPresetNames.find(actorPresetName); if (pickupableByPresetNameEntry != m_PickupableByPresetNames.end()) { @@ -326,15 +255,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - bool HeldDevice::CollideAtPoint(HitData& hd) { if (!m_GetsHitByMOsWhenHeld && IsBeingHeld()) { return false; @@ -343,11 +263,6 @@ namespace RTE { return Attachable::CollideAtPoint(hd); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Activate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activates this HDFirearm. Analogous to 'pulling the trigger'. - void HeldDevice::Activate() { if (!m_Activated) { m_ActivationTimer.Reset(); @@ -355,18 +270,10 @@ namespace RTE { m_Activated = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Deactivate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing - // the trigger'. - void HeldDevice::Deactivate() { m_Activated = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool HeldDevice::TransferJointImpulses(Vector& jointImpulses, float jointStiffnessValueToUse, float jointStrengthValueToUse, float gibImpulseLimitValueToUse) { MovableObject* parent = m_Parent; if (!parent) { @@ -394,8 +301,6 @@ namespace RTE { return intact; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /* ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Travel @@ -410,11 +315,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this HeldDevice. Supposed to be done every frame. - void HeldDevice::Update() { Attachable::Update(); @@ -453,12 +353,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this HeldDevice's current graphical representation to a - // BITMAP of choice. - void HeldDevice::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, @@ -490,12 +384,6 @@ namespace RTE { */ } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Actor's current graphical HUD overlay representation to a - // BITMAP of choice. - void HeldDevice::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { if (!m_HUDVisible) { return; diff --git a/Source/Entities/HeldDevice.h b/Source/Entities/HeldDevice.h index 4312b9f812..062a4a788b 100644 --- a/Source/Entities/HeldDevice.h +++ b/Source/Entities/HeldDevice.h @@ -1,18 +1,11 @@ #ifndef _RTEHELDDEVICE_ #define _RTEHELDDEVICE_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: HeldDevice.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the HeldDevice class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the HeldDevice class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Attachable.h" #include "Actor.h" @@ -25,597 +18,334 @@ namespace RTE { BOMB, }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: HeldDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: An articulated device that can be weilded by an Actor. - // Parent(s): Attachable. - // Class history: 06/2/2002 HeldDevice created. - // 01/31/2007 Made concrete so Shields can be jsut HeldDevice:s - + /// An articulated device that can be weilded by an Actor. + /// 01/31/2007 Made concrete so Shields can be jsut HeldDevice:s class HeldDevice : public Attachable { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(HeldDevice); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: HeldDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a HeldDevice object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a HeldDevice object in system + /// memory. Create() should be called before using the object. HeldDevice() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~HeldDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a HeldDevice object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a HeldDevice object before deletion + /// from system memory. ~HeldDevice() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the HeldDevice object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the HeldDevice object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a HeldDevice to be identical to another, by deep copy. - // Arguments: A reference to the HeldDevice to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a HeldDevice to be identical to another, by deep copy. + /// @param reference A reference to the HeldDevice to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const HeldDevice& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire HeldDevice, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire HeldDevice, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Attachable::Reset(); m_MOType = MovableObject::TypeHeldDevice; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAboveHUDPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of the top of this' HUD stack. - // Arguments: None. - // Return value: A Vector with the absolute position of this' HUD stack top point. - + /// Gets the absoltue position of the top of this' HUD stack. + /// @return A Vector with the absolute position of this' HUD stack top point. Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, -32); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSupportPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of the support handhold that this HeldDevice - // offers. - // Arguments: None. - // Return value: A vector describing the absolute world coordinates for the support - // position of this HeldDevice. - + /// Gets the absolute position of the support handhold that this HeldDevice + /// offers. + /// @return A vector describing the absolute world coordinates for the support + /// position of this HeldDevice. Vector GetSupportPos() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMagazinePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of the magazine or other equivalent point of - // this. - // Arguments: None. - // Return value: A vector describing the absolute world coordinates for the magazine - // attachment point of this - + /// Gets the absolute position of the magazine or other equivalent point of + /// this. + /// @return A vector describing the absolute world coordinates for the magazine + /// attachment point of this virtual Vector GetMagazinePos() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMuzzlePos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of the muzzle or other equivalent point of - // this. - // Arguments: None. - // Return value: A vector describing the absolute world coordinates for the muzzle point - // of this - + /// Gets the absolute position of the muzzle or other equivalent point of + /// this. + /// @return A vector describing the absolute world coordinates for the muzzle point + /// of this virtual Vector GetMuzzlePos() const { return m_Pos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMuzzleOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the unrotated relative offset from the position to the muzzle or - // other equivalent point of this. - // Arguments: None. - // Return value: A unrotated vector describing the relative for the muzzle point of - // this from this' position. - + /// Gets the unrotated relative offset from the position to the muzzle or + /// other equivalent point of this. + /// @return A unrotated vector describing the relative for the muzzle point of + /// this from this' position. virtual Vector GetMuzzleOffset() const { return Vector(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetMuzzleOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the unrotated relative offset from the position to the muzzle or - // other equivalent point of this. - // Arguments: Bew ofsset value. - // Return value: None. - + /// Sets the unrotated relative offset from the position to the muzzle or + /// other equivalent point of this. + /// @param newOffset Bew ofsset value. virtual void SetMuzzleOffset(Vector newOffset) { /* Actually does something in inherited classes */ } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetStanceOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current position offset of this HeldDevice's joint relative - // from the parent Actor's position, if attached. - // Arguments: None. - // Return value: A const reference to the current stance parent offset. - + /// Gets the current position offset of this HeldDevice's joint relative + /// from the parent Actor's position, if attached. + /// @return A const reference to the current stance parent offset. virtual Vector GetStanceOffset() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetStanceOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current position offset of this HeldDevice's joint relative - // from the parent Actor's position, if attached. - // Arguments: New value. - // Return value: None. - + /// Sets the current position offset of this HeldDevice's joint relative + /// from the parent Actor's position, if attached. + /// @param newValue New value. void SetStanceOffset(Vector newValue) { m_StanceOffset = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetSharpStanceOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current position offset of this HeldDevice's joint relative - // from the parent Actor's position, if attached. - // Arguments: New value. - // Return value: None. - + /// Sets the current position offset of this HeldDevice's joint relative + /// from the parent Actor's position, if attached. + /// @param New value. Vector GetSharpStanceOffset() const { return m_SharpStanceOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetSharpStanceOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current position offset of this HeldDevice's joint relative - // from the parent Actor's position, if attached. - // Arguments: New value. - // Return value: None. - + /// Sets the current position offset of this HeldDevice's joint relative + /// from the parent Actor's position, if attached. + /// @param newValue New value. void SetSharpStanceOffset(Vector newValue) { m_SharpStanceOffset = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetSharpLength - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how much farther an Actor which holds this device can see when - // aiming this HeldDevice sharply. - // Arguments: None. - // Return value: The length in world pixel units. - + /// Gets how much farther an Actor which holds this device can see when + /// aiming this HeldDevice sharply. + /// @return The length in world pixel units. float GetSharpLength() const { return m_MaxSharpLength; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetSharpLength - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets how much farther an Actor which holds this device can see when - // aiming this HeldDevice sharply. - // Arguments: The length in world pixel units. - // Return value: None. - + /// Sets how much farther an Actor which holds this device can see when + /// aiming this HeldDevice sharply. + /// @param newLength The length in world pixel units. void SetSharpLength(float newLength) { m_MaxSharpLength = newLength; } - /// /// Gets whether this HeldDevice can be supported when held. - /// - /// Whether this HeldDevice can be supported when held. + /// @return Whether this HeldDevice can be supported when held. bool IsSupportable() const { return m_Supportable; } - /// /// Sets whether this HeldDevice can be supported when held. - /// - /// Whether this HeldDevice can be supported when held. + /// @param shouldBeSupportable Whether this HeldDevice can be supported when held. void SetSupportable(bool shouldBeSupportable) { m_Supportable = shouldBeSupportable; } - /// /// Gets whether this HeldDevice is currently supported by a second Arm. - /// - /// Whether this HeldDevice is supported or not. + /// @return Whether this HeldDevice is supported or not. bool GetSupported() const { return m_Supportable && m_Supported; } - /// /// Sets whether this HeldDevice is currently supported by a second Arm. - /// - /// Whether this HeldDevice is being supported. + /// @param supported Whether this HeldDevice is being supported. void SetSupported(bool supported) { m_Supported = m_Supportable && supported; } - /// /// Gets whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). - /// - /// Whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). + /// @return Whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). bool GetSupportAvailable() const { return m_Supportable && m_SupportAvailable; } - /// /// Sets whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). - /// - /// Whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). + /// @param supported Whether this HeldDevice's parent has a second Arm available to provide support (or this is on a Turret). void SetSupportAvailable(bool supportAvailable) { m_SupportAvailable = m_Supportable && supportAvailable; } - /// /// Gets whether this HeldDevice while be held at the support offset with the off-hand when reloading. - /// - /// Whether this HeldDevice while be held at the support offset with the off-hand when reloading. + /// @return Whether this HeldDevice while be held at the support offset with the off-hand when reloading. bool GetUseSupportOffsetWhileReloading() const { return m_UseSupportOffsetWhileReloading; } - /// /// Sets whether this HeldDevice while be held at the support offset with the off-hand when reloading. - /// - /// Whether this HeldDevice while be held at the support offset with the off-hand when reloading. + /// @param value Whether this HeldDevice while be held at the support offset with the off-hand when reloading. void SetUseSupportOffsetWhileReloading(bool value) { m_UseSupportOffsetWhileReloading = value; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetSupportOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns support offset. - // Arguments: None. - // Return value: Support offset value. - + /// Returns support offset. + /// @return Support offset value. Vector GetSupportOffset() const { return m_SupportOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetSupportOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets support offset. - // Arguments: New support offset value. - // Return value: None. - + /// Sets support offset. + /// @param newOffset New support offset value. void SetSupportOffset(Vector newOffset) { m_SupportOffset = newOffset; } - /// /// Gets whether this HeldDevice has any limitations on what can pick it up. - /// - /// Whether this HeldDevice has any limitations on what can pick it up. + /// @return Whether this HeldDevice has any limitations on what can pick it up. bool HasPickupLimitations() const { return IsUnPickupable() || !m_PickupableByPresetNames.empty(); } - /// /// Gets whether this HeldDevice cannot be picked up at all. - /// - /// Whether this HeldDevice cannot be picked up at all. + /// @return Whether this HeldDevice cannot be picked up at all. bool IsUnPickupable() const { return m_IsUnPickupable; } - /// /// Sets whether this HeldDevice cannot be picked up at all. - /// - /// Whether this HeldDevice cannot be picked up at all. True means it cannot, false means any other limitations will apply normally. + /// @param shouldBeUnPickupable Whether this HeldDevice cannot be picked up at all. True means it cannot, false means any other limitations will apply normally. void SetUnPickupable(bool shouldBeUnPickupable) { m_IsUnPickupable = shouldBeUnPickupable; } - /// /// Checks whether the given Actor can pick up this HeldDevice. - /// - /// The Actor to check. Ownership is NOT transferred. - /// Whether the given Actor can pick up this HeldDevice. + /// @param actor The Actor to check. Ownership is NOT transferred. + /// @return Whether the given Actor can pick up this HeldDevice. bool IsPickupableBy(const Actor* actor) const { return !HasPickupLimitations() || m_PickupableByPresetNames.find(actor->GetPresetName()) != m_PickupableByPresetNames.end(); } - /// /// Specify that objects with the given PresetName can pick up this HeldDevice. - /// - /// The PresetName of an object that should be able to pick up this HeldDevice. + /// @param presetName The PresetName of an object that should be able to pick up this HeldDevice. void AddPickupableByPresetName(const std::string& presetName) { SetUnPickupable(false); m_PickupableByPresetNames.insert(presetName); } - /// /// Remove allowance for objects with the given PresetName to pick up this HeldDevice. /// Note that if the last allowance is removed, the HeldDevice will no longer have pickup limitations, rather than setting itself as unpickupable. - /// - /// The PresetName of an object that should no longer be able to pick up this HeldDevice. + /// @param actorPresetName The PresetName of an object that should no longer be able to pick up this HeldDevice. void RemovePickupableByPresetName(const std::string& actorPresetName); - /// /// Gets the multiplier for how well this HeldDevice can be gripped by Arms. - /// - /// The grip strength multiplier for this HeldDevice. + /// @return The grip strength multiplier for this HeldDevice. float GetGripStrengthMultiplier() const { return m_GripStrengthMultiplier; } - /// /// Sets the multiplier for how well this HeldDevice can be gripped by Arms. - /// - /// The new grip strength multiplier for this HeldDevice. + /// @param gripStrengthMultiplier The new grip strength multiplier for this HeldDevice. void SetGripStrengthMultiplier(float gripStrengthMultiplier) { m_GripStrengthMultiplier = gripStrengthMultiplier; } - /// /// Gets whether this can get hit by MOs when held. - /// - /// Whether this can get hit by MOs when held. + /// @return Whether this can get hit by MOs when held. bool GetsHitByMOsWhenHeld() const { return m_GetsHitByMOsWhenHeld; } - /// /// Sets whether this can get hit by MOs when held. - /// - /// Whether this can get hit by MOs when held. + /// @param value Whether this can get hit by MOs when held. void SetGetsHitByMOsWhenHeld(bool value) { m_GetsHitByMOsWhenHeld = value; } - /// /// Gets whether this HeldDevice is currently being held or not. - /// - /// Whether this HeldDevice is currently being held or not. + /// @return Whether this HeldDevice is currently being held or not. bool IsBeingHeld() const; - /// /// Gets the visual recoil multiplier. - /// - /// A float with the scalar value. + /// @return A float with the scalar value. float GetVisualRecoilMultiplier() const { return m_VisualRecoilMultiplier; } - /// /// Sets the visual recoil multiplier. - /// - /// The new recoil multiplier scalar. + /// @param value The new recoil multiplier scalar. void SetVisualRecoilMultiplier(float value) { m_VisualRecoilMultiplier = value; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSharpAim - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the degree to which this is being aimed sharp. This will - // affect the accuracy and what GetParentOffset returns. - // Arguments: A normalized scalar between 0 (no sharp aim) to 1.0 (best aim). - // Return value: None. - + /// Sets the degree to which this is being aimed sharp. This will + /// affect the accuracy and what GetParentOffset returns. + /// @param sharpAim A normalized scalar between 0 (no sharp aim) to 1.0 (best aim). void SetSharpAim(float sharpAim) { m_SharpAim = sharpAim; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsWeapon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this is an offensive weapon or not. - // Arguments: None. - // Return value: Offensive weapon or not. - + /// Indicates whether this is an offensive weapon or not. + /// @return Offensive weapon or not. bool IsWeapon() { return m_HeldDeviceType == WEAPON; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsTool - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this is a tool or not. - // Arguments: None. - // Return value: Tool or not. - + /// Indicates whether this is a tool or not. + /// @return Tool or not. bool IsTool() { return m_HeldDeviceType == TOOL; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsShield - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this is a shield or not. - // Arguments: None. - // Return value: Shield or not. - + /// Indicates whether this is a shield or not. + /// @return Shield or not. bool IsShield() { return m_HeldDeviceType == SHIELD; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsDualWieldable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this is a dual wieldable weapon or not. - // Arguments: None. - // Return value: Dual wieldable or not. - + /// Indicates whether this is a dual wieldable weapon or not. + /// @return Dual wieldable or not. bool IsDualWieldable() const { return m_DualWieldable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDualWieldable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this is a dual wieldable weapon or not. - // Arguments: Dual wieldable or not. - // Return value: None. - + /// Sets whether this is a dual wieldable weapon or not. + /// @param isDualWieldable Dual wieldable or not. void SetDualWieldable(bool isDualWieldable) { m_DualWieldable = isDualWieldable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsOneHanded - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this can be held and operated effectively with one - // hand or not. - // Arguments: None. - // Return value: One handed device or not. - + /// Indicates whether this can be held and operated effectively with one + /// hand or not. + /// @return One handed device or not. bool IsOneHanded() const { return m_OneHanded; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOneHanded - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this can be held and operated effectively with one - // hand or not. - // Arguments: New value. - // Return value: None. - + /// Sets whether this can be held and operated effectively with one + /// hand or not. + /// @param newValue New value. void SetOneHanded(bool newValue) { m_OneHanded = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - // Arguments: Reference to the HitData struct which describes the collision. This - // will be modified to represent the results of the collision. - // Return value: Whether the collision has been deemed valid. If false, then disregard - // any impulses in the Hitdata. - + /// Calculates the collision response when another MO's Atom collides with + /// this MO's physical representation. The effects will be applied + /// directly to this MO, and also represented in the passed in HitData. + /// @param hitData Reference to the HitData struct which describes the collision. This + /// will be modified to represent the results of the collision. + /// @return Whether the collision has been deemed valid. If false, then disregard + /// any impulses in the Hitdata. bool CollideAtPoint(HitData& hitData) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Activate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Activates one of this HDFirearm's features. Analogous to 'pulling - // the trigger'. - // Arguments: None. - // Return value: None. - + /// Activates one of this HDFirearm's features. Analogous to 'pulling + /// the trigger'. virtual void Activate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Deactivate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Deactivates one of this HDFirearm's features. Analogous to 'releasing - // the trigger'. - // Arguments: None. - // Return value: None. - + /// Deactivates one of this HDFirearm's features. Analogous to 'releasing + /// the trigger'. virtual void Deactivate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reload - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Throws out the currently used Magazine, if any, and puts in a new one - // after the reload delay is up. - // Arguments: None. - // Return value: None. - + /// Throws out the currently used Magazine, if any, and puts in a new one + /// after the reload delay is up. virtual void Reload() {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsActivated - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is curtrently being activated. - // Arguments: None. - // Return value: Whether being activated. - + /// Tells whether the device is curtrently being activated. + /// @return Whether being activated. virtual bool IsActivated() const { return m_Activated; } - /// /// Gets the activation Timer for this HeldDevice. - /// - /// The activation Timer for this HeldDevice. + /// @return The activation Timer for this HeldDevice. const Timer& GetActivationTimer() const { return m_ActivationTimer; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsReloading - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is curtrently being reloaded. - // Arguments: None. - // Return value: Whetehr being reloaded. - + /// Tells whether the device is curtrently being reloaded. + /// @return Whetehr being reloaded. virtual bool IsReloading() const { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DoneReloading - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device just finished reloading this frame. - // Arguments: None. - // Return value: Whether just done reloading this frame. - + /// Tells whether the device just finished reloading this frame. + /// @return Whether just done reloading this frame. virtual bool DoneReloading() const { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: NeedsReloading - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is curtrently in need of being reloaded. - // Arguments: None. - // Return value: Whetehr in need of reloading (ie not full). - + /// Tells whether the device is curtrently in need of being reloaded. + /// @return Whetehr in need of reloading (ie not full). virtual bool NeedsReloading() const { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsFull - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the device is curtrently full and reloading won't have - // any effect. - // Arguments: None. - // Return value: Whetehr magazine is full or not. - + /// Tells whether the device is curtrently full and reloading won't have + /// any effect. + /// @return Whetehr magazine is full or not. virtual bool IsFull() const { return true; } - /// /// Tells whether this HeldDevice is currently empty of ammo. - /// - /// Whether this HeldDevice is empty. + /// @return Whether this HeldDevice is empty. virtual bool IsEmpty() const { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this HeldDevice's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this HeldDevice's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this' current graphical HUD overlay representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // Which player's screen this is being drawn to. May affect what HUD elements - // get drawn etc. - // Return value: None. - + /// Draws this' current graphical HUD overlay representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param whichScreen Which player's screen this is being drawn to. May affect what HUD elements (default: 0) + /// get drawn etc. void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; - /// /// Resest all the timers used by this. Can be emitters, etc. This is to prevent backed up emissions to come out all at once while this has been held dormant in an inventory. - /// void ResetAllTimers() override { Attachable::ResetAllTimers(); m_ActivationTimer.Reset(); } #pragma region Force Transferral - /// /// Bundles up all the accumulated impulse forces of this HeldDevice and calculates how they transfer to the joint, and therefore to the parent. /// If the accumulated impulse forces exceed the joint strength or gib impulse limit of this HeldDevice, the jointImpulses Vector will be filled up to that limit and false will be returned. /// Additionally, in this case, the HeldDevice will remove itself from its parent, destabilizing said parent if it's an Actor, and gib itself if appropriate. - /// - /// A vector that will have the impulse forces affecting the joint ADDED to it. - /// An optional override for the HeldDevice's joint stiffness for this function call. Primarily used to allow subclasses to perform special behavior. - /// An optional override for the HeldDevice's joint strength for this function call. Primarily used to allow subclasses to perform special behavior. - /// An optional override for the HeldDevice's gib impulse limit for this function call. Primarily used to allow subclasses to perform special behavior. - /// False if the HeldDevice has no parent or its accumulated forces are greater than its joint strength or gib impulse limit, otherwise true. + /// @param jointImpulses A vector that will have the impulse forces affecting the joint ADDED to it. + /// @param jointStiffnessValueToUse An optional override for the HeldDevice's joint stiffness for this function call. Primarily used to allow subclasses to perform special behavior. + /// @param jointStrengthValueToUse An optional override for the HeldDevice's joint strength for this function call. Primarily used to allow subclasses to perform special behavior. + /// @param gibImpulseLimitValueToUse An optional override for the HeldDevice's gib impulse limit for this function call. Primarily used to allow subclasses to perform special behavior. + /// @return False if the HeldDevice has no parent or its accumulated forces are greater than its joint strength or gib impulse limit, otherwise true. bool TransferJointImpulses(Vector& jointImpulses, float jointStiffnessValueToUse = -1, float jointStrengthValueToUse = -1, float gibImpulseLimitValueToUse = -1) override; #pragma endregion - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -662,18 +392,10 @@ namespace RTE { /// The multiplier for visual recoil float m_VisualRecoilMultiplier; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this HeldDevice, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this HeldDevice, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Icon.cpp b/Source/Entities/Icon.cpp index 466884be63..eedb14647b 100644 --- a/Source/Entities/Icon.cpp +++ b/Source/Entities/Icon.cpp @@ -4,8 +4,6 @@ namespace RTE { ConcreteClassInfo(Icon, Entity, 80); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Icon::Clear() { m_BitmapFile.Reset(); m_FrameCount = 0; @@ -13,8 +11,6 @@ namespace RTE { m_BitmapsTrueColor.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Icon::Create() { if (m_BitmapsIndexed.empty() || m_BitmapsTrueColor.empty()) { if (m_BitmapFile.GetDataPath().empty()) { @@ -27,8 +23,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Icon::Create(const Icon& reference) { Entity::Create(reference); @@ -40,8 +34,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Icon::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -51,8 +43,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Icon::Save(Writer& writer) const { Entity::Save(writer); writer.NewProperty("BitmapFile"); @@ -63,8 +53,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Icon::Destroy(bool notInherited) { if (!notInherited) { Entity::Destroy(); diff --git a/Source/Entities/Icon.h b/Source/Entities/Icon.h index 364bf73b1f..0426350b67 100644 --- a/Source/Entities/Icon.h +++ b/Source/Entities/Icon.h @@ -6,9 +6,7 @@ namespace RTE { - /// /// Represents an Icon in the interface that can be loaded and stored from different data modules etc. - /// class Icon : public Entity { public: @@ -17,15 +15,11 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate an Icon object in system memory. Create() should be called before using the object. - /// Icon() { Clear(); } - /// /// Copy constructor method used to instantiate an Icon object identical to an already existing one. - /// - /// An Icon object which is passed in by reference. + /// @param reference An Icon object which is passed in by reference. Icon(const Icon& reference) { if (this != &reference) { Clear(); @@ -33,35 +27,25 @@ namespace RTE { } } - /// /// Makes the Icon object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates an Icon to be identical to another, by deep copy. - /// - /// A reference to the Icon to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Icon to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Icon& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up an Icon object before deletion from system memory. - /// ~Icon() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the Icon object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Resets the entire Icon, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Entity::Reset(); @@ -69,31 +53,23 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the number of frames in this Icon's animation. - /// - /// The number of frames in the animation. + /// @return The number of frames in the animation. unsigned int GetFrameCount() const { return m_FrameCount; } - /// /// Gets the array of 8-bit bitmaps of this Icon, as many as GetFrameCount says. Neither the array nor the BITMAPs are transferred ownership! - /// - /// The BITMAPs in 8bpp of this Icon. + /// @return The BITMAPs in 8bpp of this Icon. std::vector GetBitmaps8() const { return m_BitmapsIndexed; } - /// /// Gets the array of 32-bit bitmaps of this Icon, as many as GetFrameCount says. Neither the array nor the BITMAPs are transferred ownership! - /// - /// The BITMAPs in 32bpp of this Icon. + /// @return The BITMAPs in 32bpp of this Icon. std::vector GetBitmaps32() const { return m_BitmapsTrueColor; } #pragma endregion #pragma region Operator Overloads - /// /// An assignment operator for setting one Icon equal to another. - /// - /// An Icon reference. - /// A reference to the changed Icon. + /// @param rhs An Icon reference. + /// @return A reference to the changed Icon. Icon& operator=(const Icon& rhs) { if (this != &rhs) { Destroy(); @@ -113,9 +89,7 @@ namespace RTE { std::vector m_BitmapsTrueColor; //!< Vector containing the 32bpp BITMAPs of this Icon. BITMAPs are NOT owned! private: - /// /// Clears all the member variables of this Icon, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/Entities/Leg.cpp b/Source/Entities/Leg.cpp index 370a2a46e0..c894505038 100644 --- a/Source/Entities/Leg.cpp +++ b/Source/Entities/Leg.cpp @@ -6,8 +6,6 @@ namespace RTE { ConcreteClassInfo(Leg, Attachable, 50); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Leg::Clear() { m_Foot = nullptr; @@ -27,8 +25,6 @@ namespace RTE { m_MoveSpeed = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Leg::Create() { if (Attachable::Create() < 0) { return -1; @@ -50,8 +46,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Leg::Create(const Leg& reference) { if (reference.m_Foot) { m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_Foot->GetUniqueID()); @@ -77,8 +71,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Leg::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Attachable::ReadProperty(propName, reader)); @@ -98,8 +90,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Leg::Save(Writer& writer) const { Attachable::Save(writer); @@ -119,8 +109,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Leg::SetFoot(Attachable* newFoot) { if (m_Foot && m_Foot->IsAttached()) { RemoveAndDeleteAttachable(m_Foot); @@ -144,8 +132,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - AtomGroup* Leg::GetFootGroupFromFootAtomGroup() { if (!m_Foot) { return nullptr; @@ -198,8 +184,6 @@ namespace RTE { return footGroup; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Leg::Update() { Attachable::PreUpdate(); @@ -225,8 +209,6 @@ namespace RTE { UpdateFootFrameAndRotation(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Leg::UpdateCurrentAnkleOffset() { if (IsAttached()) { Vector targetOffset = g_SceneMan.ShortestDistance(m_JointPos, m_TargetPosition, g_SceneMan.SceneWrapsX()); @@ -244,8 +226,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Leg::UpdateLegRotation() { if (IsAttached()) { m_Rotation = m_AnkleOffset.GetAbsRadAngle() + (m_HFlipped ? c_PI : 0); @@ -265,8 +245,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Leg::UpdateFootFrameAndRotation() { if (m_Foot) { if (IsAttached() && m_AnkleOffset.GetY() > std::abs(m_AnkleOffset.GetX() * 0.3F)) { diff --git a/Source/Entities/Leg.h b/Source/Entities/Leg.h index 7d2ab1dd7b..7aa54b677a 100644 --- a/Source/Entities/Leg.h +++ b/Source/Entities/Leg.h @@ -7,9 +7,7 @@ namespace RTE { class HeldDevice; - /// /// A detachable Leg that will be controlled by LimbPaths. - /// class Leg : public Attachable { public: @@ -18,35 +16,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a Leg object in system memory. Create() should be called before using the object. - /// Leg() { Clear(); } - /// /// Makes the Leg object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a Leg to be identical to another, by deep copy. - /// - /// A reference to the Leg to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Leg to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Leg& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a Leg object before deletion from system memory. - /// ~Leg() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the Leg object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { Attachable::Destroy(); @@ -54,9 +42,7 @@ namespace RTE { Clear(); } - /// /// Resets the entire Leg, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Attachable::Reset(); @@ -64,67 +50,47 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the foot of this Leg. - /// - /// A pointer to foot of this Leg. Ownership is NOT transferred! + /// @return A pointer to foot of this Leg. Ownership is NOT transferred! Attachable* GetFoot() const { return m_Foot; } - /// /// Sets the foot for this Leg. Ownership IS transferred! - /// - /// The new foot to use. + /// @param newFoot The new foot to use. void SetFoot(Attachable* newFoot); - /// /// Gets the min length this of Leg, the minimum allowed length from its joint to its ankle's position. - /// - /// The min length, in pixels, of this Leg. + /// @return The min length, in pixels, of this Leg. float GetMinLength() const { return m_MinExtension; } - /// /// Gets the max length this Leg, the maximum allowed length from its joint to its ankle's position. - /// - /// The max length, in pixels, of this Leg. + /// @return The max length, in pixels, of this Leg. float GetMaxLength() const { return m_MaxExtension; } - /// /// Gets the move speed of this Leg, where 1.0 is instant and 0.0 is no movement. - /// - /// The move speed of this Leg. + /// @return The move speed of this Leg. float GetMoveSpeed() const { return m_MoveSpeed; } - /// /// Sets the move speed of this Leg, where 1.0 is instant and 0.0 is no movement. - /// - /// The new move speed of this Leg. + /// @return The new move speed of this Leg. void SetMoveSpeed(float newMoveSpeed) { m_MoveSpeed = newMoveSpeed; } - /// /// Sets the position this Leg should move towards, in absolute coordinates. - /// - /// The position the Leg should move towards. + /// @param targetPosition The position the Leg should move towards. void SetTargetPosition(const Vector& targetPosition) { m_TargetPosition = targetPosition; } - /// /// Sets whether this Leg will go into idle offset mode if the target appears to be above the joint of the Leg. - /// - /// Whether to enable idling if the target offset is above the joint. + /// @param idle Whether to enable idling if the target offset is above the joint. void EnableIdle(bool idle = true) { m_WillIdle = idle; } #pragma endregion #pragma region Concrete Methods - /// /// Gets a copy of this Leg's foot AtomGroup to be used as an Actor's FootGroup. - /// - /// A copy of this Leg's foot AtomGroup to be used as an Actor's FootGroup. OWNERSHIP IS TRANSFERRED! + /// @return A copy of this Leg's foot AtomGroup to be used as an Actor's FootGroup. OWNERSHIP IS TRANSFERRED! AtomGroup* GetFootGroupFromFootAtomGroup(); #pragma endregion #pragma region Override Methods - /// /// Updates this Leg. Supposed to be done every frame. - /// void Update() override; #pragma endregion @@ -150,28 +116,20 @@ namespace RTE { private: #pragma region Update Breakdown - /// /// Updates the current ankle offset for this Leg. Should only be called from Update. /// If the Leg is attached, the current ankle offset is based on the target offset and move speed, and whether the Leg should idle or not, otherwise it puts it in a reasonable position. - /// void UpdateCurrentAnkleOffset(); - /// /// Updates the rotation of the Leg. Should only be called from Update. /// If the Leg is attached, this applies extra rotation to line up the Leg's sprite with its extension line, otherwise it does nothing. - /// void UpdateLegRotation(); - /// /// Updates the frame and rotation of the Leg's foot Attachable. Should only be called from Update. /// If the Leg is attached, the foot's rotation and frame depend on the ankle offset, otherwise the foot's rotation is set to be perpendicular to the Leg's rotation. - /// void UpdateFootFrameAndRotation(); #pragma endregion - /// /// Clears all the member variables of this Leg, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/LimbPath.cpp b/Source/Entities/LimbPath.cpp index 1ab6b095e6..aacdaefee2 100644 --- a/Source/Entities/LimbPath.cpp +++ b/Source/Entities/LimbPath.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: LimbPath.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the LimbPath class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "LimbPath.h" #include "PresetMan.h" @@ -21,12 +9,6 @@ namespace RTE { ConcreteClassInfo(LimbPath, Entity, 20); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this LimbPath, effectively - // resetting the members of this abstraction level only. - void LimbPath::Clear() { m_Start.Reset(); m_StartSegCount = 0; @@ -55,11 +37,6 @@ namespace RTE { m_HFlipped = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the LimbPath object ready for use. - int LimbPath::Create() { // Read all the properties if (Entity::Create() < 0) @@ -102,11 +79,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a LimbPath to be identical to another, by deep copy. - int LimbPath::Create(const LimbPath& reference) { Entity::Create(reference); @@ -141,14 +113,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int LimbPath::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(Entity::ReadProperty(propName, reader)); @@ -192,12 +156,6 @@ namespace RTE { return (((point - offset) * m_Rotation) + offset) + m_PositionOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this LimbPath with a Writer for - // later recreation with Create(Reader &reader); - int LimbPath::Save(Writer& writer) const { Entity::Save(writer); @@ -223,11 +181,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the LimbPath object. - void LimbPath::Destroy(bool notInherited) { if (!notInherited) @@ -235,12 +188,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetProgressPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the APPROXIMATE scene position that the limb was reported to be - // last frame. - Vector LimbPath::GetProgressPos() { Vector returnVec(m_Start); if (IsStaticPoint()) { @@ -261,11 +208,6 @@ namespace RTE { return m_JointPos + RotatePoint(returnVec); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentSegTarget - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the scene/world position target that the current segment represents. - Vector LimbPath::GetCurrentSegTarget() { Vector returnVec(m_Start); if (IsStaticPoint()) { @@ -285,15 +227,6 @@ namespace RTE { return m_JointPos + RotatePoint(returnVec); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the velocity of the current position on the path. - // Note that this should be called BEFORE GetNextVec() if the - // appropriate matching velocity is to be returned here. If the limb - // doesn't hit the end of a segment before the time chunk runs out, - // the returned move vector is limited by the time chunk. - Vector LimbPath::GetCurrentVel(const Vector& limbPos) { Vector returnVel; Vector distVect = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()); @@ -318,15 +251,6 @@ namespace RTE { return returnVel; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNextTimeChunk - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the time needed to get to the target waypoint of the current - // segment at the current speed, if there are no obstacles. The chunk - // will not exceed the remaining time left on the frame, and will deduct - // itself from the remaining frame time tally (originally set by - // SetFrameTime()). - float LimbPath::GetNextTimeChunk(const Vector& limbPos) { float timeChunk; @@ -353,12 +277,6 @@ namespace RTE { return timeChunk; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReportProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Used to report how much progress was made to getting the limb close to - // the target (the next segment start). - void LimbPath::ReportProgress(const Vector& limbPos) { if (IsStaticPoint()) { const float staticPointEndedThreshold = 1.0F; @@ -391,12 +309,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a value representing the total progress that has been made on - // this entire path. If the path has ended, 0.0 is returned. - float LimbPath::GetTotalProgress() const { if (m_Ended || IsStaticPoint()) return 0.0; @@ -409,14 +321,6 @@ namespace RTE { return prog / m_TotalLength; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRegularProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a value representing the progress that has been made on the - // regular part of this path, ie everything except the starting segments. - // If progress has not been made past the starting segments, < 0 will - // be returned. If the path has ended, 0.0 is returned. - float LimbPath::GetRegularProgress() const { if (m_Ended || IsStaticPoint()) return 0.0; @@ -429,8 +333,6 @@ namespace RTE { return prog / m_RegularLength; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LimbPath::GetCurrentSegmentNumber() const { int progress = 0; if (!m_Ended && !IsStaticPoint()) { @@ -441,14 +343,6 @@ namespace RTE { return progress; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the speed that a limb traveling this LimbPath should have to one - // of the three predefined speed settings. - void LimbPath::SetSpeed(int newSpeed) { if (newSpeed <= SLOW) m_WhichSpeed = SLOW; @@ -458,22 +352,12 @@ namespace RTE { m_WhichSpeed = NORMAL; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OverrideSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the speed that a limb traveling this LimbPath with the specified preset should have. - void LimbPath::OverrideSpeed(int speedPreset, float newSpeed) { if (speedPreset == SLOW || speedPreset == FAST || speedPreset == NORMAL) { m_TravelSpeed[m_WhichSpeed] = newSpeed; } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Terminate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this LimbPath's progress to its end. - void LimbPath::Terminate() { if (IsStaticPoint()) { m_Ended = true; @@ -484,12 +368,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Restart - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Restarts the position tracking of the limb that travels along this - // LimbPath. - void LimbPath::Restart() { m_CurrentSegment = m_Segments.begin(); m_PathTimer.Reset(); @@ -498,18 +376,6 @@ namespace RTE { m_Ended = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RestartFree - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Restarts the position tracking of the limb that travels along this - // LimbPath at a point which does not contain terrain. In doing this, - // a list of potential starting segments are checked and the first to - // yield a starting position that is not in terrain will be picked. - // If none of the candidate starting segments are free of terrain, - // the last one in the list will be picked and false will be returned - // here. The passed in limbPos Vector will be set to the new position of - // the restarted path, if a free spot is found. - bool LimbPath::RestartFree(Vector& limbPos, MOID MOIDToIgnore, int ignoreTeam) { std::deque::iterator prevSeg = m_CurrentSegment; float prevProg = m_SegProgress; @@ -617,12 +483,6 @@ namespace RTE { return m_HFlipped ? -result : result; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this LimbPath's current graphical debug representation to a - // BITMAP of choice. - void LimbPath::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, unsigned char color) const { diff --git a/Source/Entities/LimbPath.h b/Source/Entities/LimbPath.h index b43409336f..23f1e2eb4a 100644 --- a/Source/Entities/LimbPath.h +++ b/Source/Entities/LimbPath.h @@ -1,18 +1,11 @@ #ifndef _RTELIMBPATH_ #define _RTELIMBPATH_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: LimbPath.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the LimbPath class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the LimbPath class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Entity.h" #include "Vector.h" #include "ActivityMan.h" @@ -28,51 +21,28 @@ namespace RTE { FAST }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: LimbPath - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A set of Vector:s making up a motion path for a AtomGroup's limb. The - // path is continuous. - // Parent(s): Entity. - // Class history: 05/25/2001 LimbPath created. - + /// A set of Vector:s making up a motion path for a AtomGroup's limb. The + /// path is continuous. class LimbPath : public Entity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(LimbPath); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: LimbPath - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a LimbPath object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a LimbPath object in system + /// memory. Create() should be called before using the object. LimbPath() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~LimbPath - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a LimbPath object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a LimbPath object before deletion + /// from system memory. ~LimbPath() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the LimbPath object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the LimbPath object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; /* @@ -96,66 +66,40 @@ namespace RTE { const float travelSpeed = 1.0); */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a LimbPath to be identical to another, by deep copy. - // Arguments: A reference to the LimbPath to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a LimbPath to be identical to another, by deep copy. + /// @param reference A reference to the LimbPath to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const LimbPath& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire LimbPath, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire LimbPath, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Entity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the LimbPath object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the LimbPath object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Gets the coordinates where the limb should start at the start of the LimbPath cycle, relative to the owning AtomGroup's local origin. - /// - /// A Vector with the start position. + /// @return A Vector with the start position. const Vector& GetStartOffset() const { return m_Start; } - /// /// Sets the coordinates where the limb should start at the start of the LimbPath cycle, relative to the owning AtomGroup's local origin. - /// - /// A Vector with the new start offset. + /// @param newStartOffset A Vector with the new start offset. void SetStartOffset(const Vector& newStartOffset) { m_Start = newStartOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSegCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of Vector:s the internal array of 'waypoints' or - // segments of this LimbPath. - // Arguments: None. - // Return value: An int with he count. - + /// Gets the number of Vector:s the internal array of 'waypoints' or + /// segments of this LimbPath. + /// @return An int with he count. unsigned int GetSegCount() const { return m_Segments.size(); } - /// /// Gets a pointer to the segment at the given index. Ownership is NOT transferred. - /// - /// The index of the segment to get. - /// A pointer to the segment at the given index. Ownership is NOT transferred. + /// @param segmentIndex The index of the segment to get. + /// @return A pointer to the segment at the given index. Ownership is NOT transferred. Vector* GetSegment(int segmentIndex) { if (segmentIndex >= 0 && segmentIndex < m_Segments.size()) { return &m_Segments.at(segmentIndex); @@ -163,69 +107,39 @@ namespace RTE { return nullptr; } - /// /// Gets whether or not foot collisions should be disabled, i.e. the limbpath's progress is greater than the FootCollisionsDisabledSegment value. - /// - /// Whether or not foot collisions should be disabled for this limbpath at its current progress. + /// @return Whether or not foot collisions should be disabled for this limbpath at its current progress. bool FootCollisionsShouldBeDisabled() const { return m_FootCollisionsDisabledSegment >= 0 && GetSegCount() - GetCurrentSegmentNumber() <= m_FootCollisionsDisabledSegment; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSegProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how far the limb was last reported to be away form the current - // segment target/waypoint. - // Arguments: None. - // Return value: A normalized float describing the progress made toward the current - // segment last frame. 0.5 means it was half the length of the current - // segment away from it. - + /// Gets how far the limb was last reported to be away form the current + /// segment target/waypoint. + /// @return A normalized float describing the progress made toward the current + /// segment last frame. 0.5 means it was half the length of the current + /// segment away from it. float GetSegProgress() const { return m_SegProgress; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetProgressPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the APPROXIMATE scene position that the limb was reported to be - // last frame. This really shouldn't be used by external clients. - // Arguments: None. - // Return value: A Vector with the APPROXIAMTE scene/world coordinates of the limb as - // reported last. - + /// Gets the APPROXIMATE scene position that the limb was reported to be + /// last frame. This really shouldn't be used by external clients. + /// @return A Vector with the APPROXIAMTE scene/world coordinates of the limb as + /// reported last. Vector GetProgressPos(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentSegTarget - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the scene/world position target that the current segment represents. - // Arguments: None. - // Return value: A vector with the scene position of the current segment target. - + /// Gets the scene/world position target that the current segment represents. + /// @return A vector with the scene position of the current segment target. Vector GetCurrentSegTarget(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the velocity of the current position on the path. - // Arguments: The current world coordinate position of the Limb. - // Return value: A Vector with the current move velocity. - + /// Gets the velocity of the current position on the path. + /// @param limbPos The current world coordinate position of the Limb. + /// @return A Vector with the current move velocity. Vector GetCurrentVel(const Vector& limbPos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the speed that a limb traveling this LimbPath should have. - // Arguments: None. - // Return value: A float describing the speed in m/s. - + /// Gets the speed that a limb traveling this LimbPath should have. + /// @return A float describing the speed in m/s. float GetSpeed() const { return m_TravelSpeed[m_WhichSpeed] * m_TravelSpeedMultiplier; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the speed that a limb traveling this LimbPath should have for the specified preset. - // Arguments: Predefined speed preset to set the value for. - // Return value: A float describing the speed in m/s. - + /// Gets the speed that a limb traveling this LimbPath should have for the specified preset. + /// @param speedPreset Predefined speed preset to set the value for. + /// @return A float describing the speed in m/s. float GetSpeed(int speedPreset) const { if (speedPreset == SLOW || speedPreset == NORMAL || speedPreset == FAST) return m_TravelSpeed[speedPreset]; @@ -233,381 +147,217 @@ namespace RTE { return 0; } - /// /// Sets the current travel speed multiplier. - /// - /// The new travel speed multiplier. + /// @param newValue The new travel speed multiplier. void SetTravelSpeedMultiplier(float newValue) { m_TravelSpeedMultiplier = newValue; } - /// /// Gets the current travel speed multiplier. - /// - /// The current travel speed multiplier. + /// @return The current travel speed multiplier. float GetTravelSpeedMultiplier() const { return m_TravelSpeedMultiplier; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPushForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the force that a limb traveling this LimbPath can push against - // stuff in the scene with. It will increase to the double if progress - // isn't made on the segment. - // Arguments: None. - // Return value: The currently set force maximum, in kg * m/s^2. - + /// Gets the force that a limb traveling this LimbPath can push against + /// stuff in the scene with. It will increase to the double if progress + /// isn't made on the segment. + /// @return The currently set force maximum, in kg * m/s^2. float GetPushForce() const { return m_PushForce + (m_PushForce * (m_SegTimer.GetElapsedSimTimeMS() / 500)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPushForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets thedefault, unaltered force that a limb traveling this LimbPath can push against - // stuff in the scene with. - // Arguments: None. - // Return value: The default set force maximum, in kg * m/s^2. - + /// Gets thedefault, unaltered force that a limb traveling this LimbPath can push against + /// stuff in the scene with. + /// @return The default set force maximum, in kg * m/s^2. float GetDefaultPushForce() const { return m_PushForce; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNextTimeChunk - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the time needed to get to the target waypoint of the current - // segment at the current speed, if there are no obstacles. The chunk - // will not exceed the remaining time left on the frame, and will deduct - // itself from the remaining frame time tally (originally set by - // SetFrameTime()). - // Arguments: The current world coordinate position of the Limb. - // Return value: A float describing the time chunk in seconds. - + /// Gets the time needed to get to the target waypoint of the current + /// segment at the current speed, if there are no obstacles. The chunk + /// will not exceed the remaining time left on the frame, and will deduct + /// itself from the remaining frame time tally (originally set by + /// SetFrameTime()). + /// @param limbPos The current world coordinate position of the Limb. + /// @return A float describing the time chunk in seconds. float GetNextTimeChunk(const Vector& limbPos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalPathTime - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total time that this entire path should take to travel along - // with the current speed setting, including the start segments. - // Arguments: None. - // Return value: The total time (ms) this should take to travel along, if unobstructed. - + /// Gets the total time that this entire path should take to travel along + /// with the current speed setting, including the start segments. + /// @return The total time (ms) this should take to travel along, if unobstructed. float GetTotalPathTime() const { return ((m_TotalLength * c_MPP) / (m_TravelSpeed[m_WhichSpeed] * m_TravelSpeedMultiplier)) * 1000; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRegularPathTime - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total time that this path should take to travel along - // with the current speed setting, NOT including the start segments. - // Arguments: None. - // Return value: The total time (ms) this should take to travel along, if unobstructed. - + /// Gets the total time that this path should take to travel along + /// with the current speed setting, NOT including the start segments. + /// @return The total time (ms) this should take to travel along, if unobstructed. float GetRegularPathTime() const { return ((m_RegularLength * c_MPP) / (m_TravelSpeed[m_WhichSpeed] * m_TravelSpeedMultiplier)) * 1000; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalTimeProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the ratio of time since the path was restarted and the total time - // it should take to travel along the path with the current speed setting, - // including the start segments. - // Arguments: None. - // Return value: A positive scalar ratio showing the progress. 0 - 1.0 and beyond. - // If the path has ended, but not been reset, 0 is returned. - + /// Gets the ratio of time since the path was restarted and the total time + /// it should take to travel along the path with the current speed setting, + /// including the start segments. + /// @return A positive scalar ratio showing the progress. 0 - 1.0 and beyond. + /// If the path has ended, but not been reset, 0 is returned. float GetTotalTimeProgress() const { return m_Ended ? 0 : (m_PathTimer.GetElapsedSimTimeMS() / GetTotalPathTime()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRegularTimeProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the ratio of time since the path was restarted and the total time - // it should take to travel along the path with the current speed setting, - // NOT including the start segments. - // Arguments: None. - // Return value: A positive scalar ratio showing the progress. 0 - 1.0 and beyond. - // If the path has ended, but not been reset, 0 is returned. - + /// Gets the ratio of time since the path was restarted and the total time + /// it should take to travel along the path with the current speed setting, + /// NOT including the start segments. + /// @return A positive scalar ratio showing the progress. 0 - 1.0 and beyond. + /// If the path has ended, but not been reset, 0 is returned. float GetRegularTimeProgress() const { return m_Ended ? 0 : (m_PathTimer.GetElapsedSimTimeMS() / GetRegularPathTime()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReportProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Used to report how much progress was made to getting the limb close to - // the target (the current segment waypoint). - // Arguments: The new limb position in world coords. - // Return value: None. - + /// Used to report how much progress was made to getting the limb close to + /// the target (the current segment waypoint). + /// @param limbPos The new limb position in world coords. void ReportProgress(const Vector& limbPos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a value representing the total progress that has been made on - // this entire path. If the path has ended, 0.0 is returned. - // Arguments: None. - // Return value: A float indicating the total progress made on the entire path, from - // 0.0 to 1.0. If the path has ended, 0.0 is returned. - + /// Gets a value representing the total progress that has been made on + /// this entire path. If the path has ended, 0.0 is returned. + /// @return A float indicating the total progress made on the entire path, from + /// 0.0 to 1.0. If the path has ended, 0.0 is returned. float GetTotalProgress() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRegularProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a value representing the progress that has been made on the - // regular part of this path, ie averythign except the starting segments. - // If progress has not been made past the starting segments, < 0 will - // be returned. If the path has ended, 0.0 is returned. - // Arguments: None. - // Return value: A float indicating the total progress made on the regular path, from - // 0.0 to 1.0. If the path has ended, 0.0 is returned. - + /// Gets a value representing the progress that has been made on the + /// regular part of this path, ie averythign except the starting segments. + /// If progress has not been made past the starting segments, < 0 will + /// be returned. If the path has ended, 0.0 is returned. + /// @return A float indicating the total progress made on the regular path, from + /// 0.0 to 1.0. If the path has ended, 0.0 is returned. float GetRegularProgress() const; - /// /// Gets the current segment as a number, rather than an iterator. - /// - /// The current segment as a number. + /// @return The current segment as a number. int GetCurrentSegmentNumber() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSegments - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets a new array of 'waypoints' or segments of this LimbPath. - // Arguments: An int specifying how many segments there are in the following - // segment array. This MUST match the actual size of the array! - // A pointer to the new Vector array. - // Return value: None. - + /// Sets a new array of 'waypoints' or segments of this LimbPath. + /// @param newSpeed An int specifying how many segments there are in the following + /// segment array. This MUST match the actual size of the array! + /// A pointer to the new Vector array. // void SetSegments(const unsigned int segCount, const Vector *newSegments); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCurrentSeg - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current seg pointer to whichever segment in the segment deque. - // Arguments: An int that is an index to a valid element of the internal segment array. - // Return value: None. - + /// Sets the current seg pointer to whichever segment in the segment deque. + /// @param newSpeed An int that is an index to a valid element of the internal segment array. // void SetCurrentSeg(unsigned int currentSeg) { m_CurrentSegment = currentSeg; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the speed that a limb traveling this LimbPath should have to one - // of the three predefined speed settings. - // Arguments: An int specifying which discrete speed setting to use from the Speed - // enumeration. - // Return value: None. - + /// Sets the speed that a limb traveling this LimbPath should have to one + /// of the three predefined speed settings. + /// @param newSpeed An int specifying which discrete speed setting to use from the Speed + /// enumeration. void SetSpeed(int newSpeed); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OverrideSpeed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the speed that a limb traveling this LimbPath with the specified preset should have. - // Arguments: An int specifying which discrete speed setting to use from the Speed - // enumeration. New limb travel speed value. - // Return value: None. - + /// Sets the speed that a limb traveling this LimbPath with the specified preset should have. + /// @param speedPreset An int specifying which discrete speed setting to use from the Speed + /// enumeration. New limb travel speed value. void OverrideSpeed(int speedPreset, float newSpeed); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OverridePushForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the force that a limb traveling this LimbPath can push against - // stuff in the scene with. - // Arguments: The new push force maximum, in kg * m/s^2. - // Return value: None. - + /// Sets the force that a limb traveling this LimbPath can push against + /// stuff in the scene with. + /// @param newForce The new push force maximum, in kg * m/s^2. void OverridePushForce(float newForce) { m_PushForce = newForce; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFrameTime - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the amount of time that will be used by the limb to travel every - // frame. Defined in seconds. - // Arguments: A float describing the time in s. - // Return value: None. - + /// Sets the amount of time that will be used by the limb to travel every + /// frame. Defined in seconds. + /// @param newFrameTime A float describing the time in s. void SetFrameTime(float newFrameTime) { m_TimeLeft = newFrameTime; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetHFlip - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this path is flipped horizontally or not. If being - // flipped the path automatically restarts. - // Arguments: A bool telling this path to be flipped or not. - // Return value: None. - + /// Sets whether this path is flipped horizontally or not. If being + /// flipped the path automatically restarts. + /// @param hflipped A bool telling this path to be flipped or not. void SetHFlip(bool hflipped) { m_HFlipped = hflipped; } - /// /// Gets the h flip. - /// - /// The h flip. + /// @return The h flip. bool GetHFlip() { return m_HFlipped; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetJointPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Informs this LimbPath of the absolute world coordinates of its owning - // Actor's limb's joint for this frame. Needs to be done before - // travelling anyhting along this path each frame. - // Arguments: A Vector with the updated joint position info. - // Return value: None. - + /// Informs this LimbPath of the absolute world coordinates of its owning + /// Actor's limb's joint for this frame. Needs to be done before + /// travelling anyhting along this path each frame. + /// @param jointPos A Vector with the updated joint position info. void SetJointPos(const Vector& jointPos) { m_JointPos = jointPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetJointVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Informs this LimbPath of the current velocity of its owning Actor's - // limb's joint for this frame. Needs to be done before travelling - // anyhting along this path each frame. - // Arguments: A Vector with the updated joint velocity info. - // Return value: None. - + /// Informs this LimbPath of the current velocity of its owning Actor's + /// limb's joint for this frame. Needs to be done before travelling + /// anyhting along this path each frame. + /// @param jointVel A Vector with the updated joint velocity info. void SetJointVel(const Vector& jointVel) { m_JointVel = jointVel; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetRotation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Informs this LimbPath of the current rotation of its owning Actor's - // for this frame. Needs to be done before travelling - // anything along this path each frame. - // Arguments: A Matrix with the updated rotation info. - // Return value: None. - + /// Informs this LimbPath of the current rotation of its owning Actor's + /// for this frame. Needs to be done before travelling + /// anything along this path each frame. + /// @param rotation A Matrix with the updated rotation info. void SetRotation(const Matrix& rotation) { m_Rotation = rotation; m_Rotation.SetXFlipped(m_HFlipped); } - /// /// Sets the new rotation offset. - /// - /// The new rotation offset, in local space. + /// @param rotationOffset The new rotation offset, in local space. void SetRotationOffset(const Vector& rotationOffset) { m_RotationOffset = rotationOffset; } - /// /// Sets the new position offset. - /// - /// The new position offset, in local space. + /// @param rotationOffset The new position offset, in local space. void SetPositionOffset(const Vector& positionOffset) { m_PositionOffset = positionOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FrameDone - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns if GetNextMoveVec() have to be called again or not on this - // frame. If the last call didn't use up all the time moving on the - // current segment because it ended, this will return false. Then - // GetNextMoveVec() needs to be called at least one more time this frame. - // Arguments: None. - // Return value: A bool with the answer. - + /// Returns if GetNextMoveVec() have to be called again or not on this + /// frame. If the last call didn't use up all the time moving on the + /// current segment because it ended, this will return false. Then + /// GetNextMoveVec() needs to be called at least one more time this frame. + /// @return A bool with the answer. bool FrameDone() const { return m_TimeLeft <= 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PathEnded - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the last call to ProgressMade() completed the - // entire path. Use Restart() to start the path over. - // Arguments: None. - // Return value: A bool with the answer. - + /// Indicates whether the last call to ProgressMade() completed the + /// entire path. Use Restart() to start the path over. + /// @return A bool with the answer. bool PathEnded() const { return m_Ended; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PathIsAtStart - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the path has been restarted without making any - // progress yet. - // Arguments: None. - // Return value: A bool with the answer. - + /// Indicates whether the path has been restarted without making any + /// progress yet. + /// @return A bool with the answer. bool PathIsAtStart() const { return m_CurrentSegment == m_Segments.begin() && m_SegProgress == 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Terminate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this LimbPath's progress to its end. - // Arguments: None. - // Return value: None. - + /// Sets this LimbPath's progress to its end. void Terminate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Restart - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Restarts the position tracking of the limb that travels along this - // LimbPath. - // Arguments: None. - // Return value: None. - + /// Restarts the position tracking of the limb that travels along this + /// LimbPath. void Restart(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RestartFree - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Restarts the position tracking of the limb that travels along this - // LimbPath at a point which does not contain terrain. In doing this, - // a list of potential starting segments are checked and the first to - // yield a starting position that is not in terrain will be picked. - // If none of the candidate starting segments are free of terrain, - // the last one in the list will be picked and false will be returned - // here. The passed in limbPos Vector will be set to teh new position of - // the restarted path, if a free spot is found. - // Arguments: Limb scene pos which will be set to the new reset position if a free - // spot was found. - // The root MOID to ignore when looking for a free position. - // To enable ignoring of all MOIDs associated with an object of a specific - // team which also has team ignoring enabled itself. - // Return value: Whether a starting segment that yielded a starting pos free of terrain - // was found or not. - + /// Restarts the position tracking of the limb that travels along this + /// LimbPath at a point which does not contain terrain. In doing this, + /// a list of potential starting segments are checked and the first to + /// yield a starting position that is not in terrain will be picked. + /// If none of the candidate starting segments are free of terrain, + /// the last one in the list will be picked and false will be returned + /// here. The passed in limbPos Vector will be set to teh new position of + /// the restarted path, if a free spot is found. + /// @param limbPos Limb scene pos which will be set to the new reset position if a free + /// spot was found. + /// @param MOIDToIgnore The root MOID to ignore when looking for a free position. (default: g_NoMOID) + /// @param ignoreTeam To enable ignoring of all MOIDs associated with an object of a specific (default: Activity::NoTeam) + /// team which also has team ignoring enabled itself. + /// @return Whether a starting segment that yielded a starting pos free of terrain + /// was found or not. bool RestartFree(Vector& limbPos, MOID MOIDToIgnore = g_NoMOID, int ignoreTeam = Activity::NoTeam); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsInitialized - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicated whether this path is has been created and had some data set - // yet. - // Arguments: None. - // Return value: Whether this has been Create:ed yet. - + /// Indicated whether this path is has been created and had some data set + /// yet. + /// @return Whether this has been Create:ed yet. bool IsInitialized() const { return !m_Start.IsZero() || !m_Segments.empty(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsStaticPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicated whether this path is in fact just a single point to where - // the limb will always be ordered to move toward. IE a standing still - // type limb movement. - // Arguments: None. - // Return value: None. - + /// Indicated whether this path is in fact just a single point to where + /// the limb will always be ordered to move toward. IE a standing still + /// type limb movement. bool IsStaticPoint() const { return m_Segments.empty(); } - /// /// Returns the lowest y position of this LimbPath. - /// - /// The lowest y position of this LimbPath. + /// @return The lowest y position of this LimbPath. float GetLowestY() const; - /// /// Returns the middle x position of this LimbPath. - /// - /// The middle x position of this LimbPath. + /// @return The middle x position of this LimbPath. float GetMiddleX() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this LimbPath's current graphical debug representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // The color to draw the path's pixels as. - // Return value: None. - + /// Draws this LimbPath's current graphical debug representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param color The color to draw the path's pixels as. (default: 34) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), unsigned char color = 34) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: static Entity::ClassInfo m_sClass; @@ -675,25 +425,15 @@ namespace RTE { bool m_Ended; bool m_HFlipped; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this LimbPath, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this LimbPath, effectively + /// resetting the members of this abstraction level only. void Clear(); - /// /// Rotates a point to match our rotation and rotation offset. - /// - /// The point to rotate. - /// The rotated point. + /// @param point The point to rotate. + /// @return The rotated point. Vector RotatePoint(const Vector& point) const; }; diff --git a/Source/Entities/Loadout.cpp b/Source/Entities/Loadout.cpp index 18604cc71e..f7d2557318 100644 --- a/Source/Entities/Loadout.cpp +++ b/Source/Entities/Loadout.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Loadout.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Implementation file for the Loadout class -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Loadout.h" #include "PresetMan.h" #include "MovableObject.h" @@ -19,12 +7,6 @@ namespace RTE { ConcreteClassInfo(Loadout, Entity, 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Loadout, effectively - // resetting the members of this abstraction level only. - void Loadout::Clear() { m_Complete = true; m_pDeliveryCraft = 0; @@ -46,11 +28,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Loadout to be identical to another, by deep copy. - int Loadout::Create(const Loadout& reference) { Entity::Create(reference); @@ -63,14 +40,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int Loadout::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -121,12 +90,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this Loadout with a Writer for - // later recreation with Create(Reader &reader); - int Loadout::Save(Writer& writer) const { Entity::Save(writer); @@ -164,13 +127,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateFirstActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the first Actor that this Loadout has and equips. - // Ownership IS transferred!! All items of the Loadout of this Deployment - // will be added to the Actor's inventory as well (and also owned by it) - Actor* Loadout::CreateFirstActor(int nativeModule, float foreignMult, float nativeMult, float& costTally) const { // The Actor instance we return and pass ownership of Actor* pReturnActor = 0; @@ -252,12 +208,6 @@ namespace RTE { return pReturnActor; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CreateFirstDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the first Device that is defined in this Loadout. - // Ownership IS transferred!! Only the first Device is created. - SceneObject* Loadout::CreateFirstDevice(int nativeModule, float foreignMult, float nativeMult, float& costTally) const { // The Actor instance we return and pass ownership of SceneObject* pReturnObject = 0; diff --git a/Source/Entities/Loadout.h b/Source/Entities/Loadout.h index 4d759dd16f..9385484d88 100644 --- a/Source/Entities/Loadout.h +++ b/Source/Entities/Loadout.h @@ -1,18 +1,11 @@ #ifndef _LOADOUT_ #define _LOADOUT_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Loadout.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loadout class -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Loadout class +/// @author Daniel Tabar +/// dtabar@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files // #include "FrameMan.h" #include "Entity.h" @@ -23,40 +16,23 @@ namespace RTE { class ACraft; class Actor; - ////////////////////////////////////////////////////////////////////////////////////////// - // Nested class: Loadout - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Defines a delivery of Actors, with all their equipment etc. - // Parent(s): Entity. - // Class history: 2/21/2012 Loadout created. - + /// Defines a delivery of Actors, with all their equipment etc. class Loadout : public Entity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(Loadout); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Loadout - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Loadout object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a Loadout object in system + /// memory. Create() should be called before using the object. Loadout() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Loadout - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Copy constructor method used to instantiate a Loadout object - // identical to an already existing one. - // Arguments: A Loadout object which is passed in by reference. - + /// Copy constructor method used to instantiate a Loadout object + /// identical to an already existing one. + /// @param reference A Loadout object which is passed in by reference. Loadout(const Loadout& reference) { if (this != &reference) { Clear(); @@ -64,13 +40,9 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Operator: Loadout assignment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: An assignment operator for setting one Loadout equal to another. - // Arguments: A Loadout reference. - // Return value: A reference to the changed Loadout. - + /// An assignment operator for setting one Loadout equal to another. + /// @param rhs A Loadout reference. + /// @return A reference to the changed Loadout. Loadout& operator=(const Loadout& rhs) { if (this != &rhs) { Destroy(); @@ -79,111 +51,66 @@ namespace RTE { return *this; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Loadout to be identical to another, by deep copy. - // Arguments: A reference to the Loadout to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a Loadout to be identical to another, by deep copy. + /// @param reference A reference to the Loadout to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const Loadout& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Entity, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire Entity, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Entity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsComplete - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this loadout is complete, or some Entity within could - // not be found on load. - // Arguments: None. - // Return value: Complete or not. - + /// Shows whether this loadout is complete, or some Entity within could + /// not be found on load. + /// @return Complete or not. bool IsComplete() { return m_Complete; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateFirstActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the first Actor that this Loadout has and equips. - // Ownership IS transferred!! All items of the Loadout of this Deployment - // will be added to the Actor's inventory as well (and also owned by it) - // Arguments: Which in-game player to create the Actor for. - // A float which will be added to with the cost of the stuff returned here. - // Return value: The Actor instance, if any, that is first defined in this Loadout. - // OWNERSHIP IS TRANSFERRED! - + /// Creates and returns the first Actor that this Loadout has and equips. + /// Ownership IS transferred!! All items of the Loadout of this Deployment + /// will be added to the Actor's inventory as well (and also owned by it) + /// @param nativeModule Which in-game player to create the Actor for. + /// @param foreignMult A float which will be added to with the cost of the stuff returned here. + /// @return The Actor instance, if any, that is first defined in this Loadout. + /// OWNERSHIP IS TRANSFERRED! Actor* CreateFirstActor(int nativeModule, float foreignMult, float nativeMult, float& costTally) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateFirstDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns the first Device that is defined in this Loadout. - // Ownership IS transferred!! Only the first Device is created. - // Arguments: Which in-game player to create the device for. - // A float which will be added to with the cost of the stuff returned here. - // Return value: The SceneObject instance, if any, that this Loadout defines first. - // OWNERSHIP IS TRANSFERRED! - + /// Creates and returns the first Device that is defined in this Loadout. + /// Ownership IS transferred!! Only the first Device is created. + /// @param nativeModule Which in-game player to create the device for. + /// @param foreignMult A float which will be added to with the cost of the stuff returned here. + /// @return The SceneObject instance, if any, that this Loadout defines first. + /// OWNERSHIP IS TRANSFERRED! SceneObject* CreateFirstDevice(int nativeModule, float foreignMult, float nativeMult, float& costTally) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDeliveryCraft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the preset of the delivery craft set for this loadout. Owenership - // is NOT transferred! - // Arguments: None. - // Return value: A pointer to the ACraft preset instance. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the preset of the delivery craft set for this loadout. Owenership + /// is NOT transferred! + /// @return A pointer to the ACraft preset instance. OWNERSHIP IS NOT TRANSFERRED! const ACraft* GetDeliveryCraft() const { return m_pDeliveryCraft; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDeliveryCraft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the preset of the delivery craft set for this loadout. Owenership - // is NOT transferred! - // Arguments: A pointer to the ACraft preset instance. OWNERSHIP IS NOT TRANSFERRED! - // Return value: None. - + /// Sets the preset of the delivery craft set for this loadout. Owenership + /// is NOT transferred! + /// @param pCraft A pointer to the ACraft preset instance. OWNERSHIP IS NOT TRANSFERRED! void SetDeliveryCraft(const ACraft* pCraft) { m_pDeliveryCraft = pCraft; m_Complete = m_Complete && m_pDeliveryCraft; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCargoList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the list of cargo Entity items this Loadout represents. - // Arguments: None. - // Return value: A pointer to the list of cargo Entity items. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the list of cargo Entity items this Loadout represents. + /// @return A pointer to the list of cargo Entity items. OWNERSHIP IS NOT TRANSFERRED! std::list* GetCargoList() { return &m_CargoItems; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddToCargoList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a new Preset to the list of cargo items to be included in this. - // Arguments: A const pointer to the ScneObject preset we want to add to this loadout. - // Return value: None. - + /// Adds a new Preset to the list of cargo items to be included in this. + /// @param pNewItem A const pointer to the ScneObject preset we want to add to this loadout. void AddToCargoList(const SceneObject* pNewItem) { if (pNewItem) m_CargoItems.push_back(pNewItem); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -194,18 +121,10 @@ namespace RTE { // The cargo of this loadout, all preset instances not owned by this std::list m_CargoItems; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Loadout, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Loadout, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Entities/MOPixel.cpp b/Source/Entities/MOPixel.cpp index 1feefb8c06..9199526c44 100644 --- a/Source/Entities/MOPixel.cpp +++ b/Source/Entities/MOPixel.cpp @@ -8,8 +8,6 @@ namespace RTE { ConcreteClassInfo(MOPixel, MovableObject, 2000); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::Clear() { m_Atom = 0; m_Color.Reset(); @@ -20,8 +18,6 @@ namespace RTE { m_Staininess = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::Create() { if (MovableObject::Create() < 0) { return -1; @@ -38,8 +34,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::Create(Color color, const float mass, const Vector& position, const Vector& velocity, Atom* atom, const unsigned long lifetime) { m_Color = color; m_Atom = atom; @@ -53,8 +47,6 @@ namespace RTE { return MovableObject::Create(mass, position, velocity, lifetime); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::Create(const MOPixel& reference) { MovableObject::Create(reference); @@ -70,8 +62,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MovableObject::ReadProperty(propName, reader)); @@ -90,8 +80,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::Save(Writer& writer) const { MovableObject::Save(writer); @@ -109,8 +97,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::Destroy(bool notInherited) { delete m_Atom; @@ -120,24 +106,16 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::GetDrawPriority() const { return m_Atom->GetMaterial()->GetPriority(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Material* MOPixel::GetMaterial() const { return m_Atom->GetMaterial(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::SetAtom(Atom* newAtom) { delete m_Atom; m_Atom = newAtom; m_Atom->SetOwner(this); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::SetLethalRange(float range) { m_LethalRange = range; if (m_MinLethalRange < m_MaxLethalRange) { @@ -145,20 +123,14 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOPixel::GetTrailLength() const { return m_Atom->GetTrailLength(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::SetTrailLength(int trailLength) { m_Atom->SetTrailLength(trailLength); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MOPixel::HitTestAtPixel(int pixelX, int pixelY) const { if (!GetsHitByMOs() || GetRootParent()->GetTraveling()) { return false; @@ -167,8 +139,6 @@ namespace RTE { return m_Pos.GetFloorIntX() == pixelX && m_Pos.GetFloorIntY() == pixelY; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::Travel() { MovableObject::Travel(); @@ -193,8 +163,6 @@ namespace RTE { m_Atom->ClearMOIDIgnoreList(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MOPixel::CollideAtPoint(HitData& hd) { RTEAssert(hd.HitPoint.GetFloored() == m_Pos.GetFloored(), "Collision mismatch in MOPixel::CollideAtPoint!"); RTEAssert(hd.Body[HITOR], "Valid MO not passed into MOPixel::CollideAtPoint!"); @@ -211,8 +179,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::RestDetection() { MovableObject::RestDetection(); @@ -224,8 +190,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::Update() { MovableObject::Update(); @@ -251,8 +215,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { // Don't draw color if this isn't a drawing frame if (!g_TimerMan.DrawnSimUpdate() && mode == g_DrawColor) { @@ -291,8 +253,6 @@ namespace RTE { g_SceneMan.RegisterDrawing(targetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, pixelPos, 1.0F); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOPixel::SetPostScreenEffectToDraw() const { if (m_AgeTimer.GetElapsedSimTimeMS() >= m_EffectStartTime && (m_EffectStopTime == 0 || !m_AgeTimer.IsPastSimMS(m_EffectStopTime))) { if (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY())) { diff --git a/Source/Entities/MOPixel.h b/Source/Entities/MOPixel.h index 2e8d7c6c6f..66a8659780 100644 --- a/Source/Entities/MOPixel.h +++ b/Source/Entities/MOPixel.h @@ -7,9 +7,7 @@ namespace RTE { class Atom; - /// /// A movable object with mass that is graphically represented by a single pixel. - /// class MOPixel : public MovableObject { public: @@ -18,66 +16,50 @@ namespace RTE { SerializableOverrideMethods; #pragma region Creation - /// /// Constructor method used to instantiate a MOPixel object in system memory. Create() should be called before using the object. - /// MOPixel() { Clear(); } - /// /// Convenience constructor to both instantiate a MOPixel in memory and Create it at the same time. - /// - /// A Color object specifying the color of this MOPixel. - /// A float specifying the object's mass in Kilograms (kg). - /// A Vector specifying the initial position. - /// A Vector specifying the initial velocity. - /// An Atom that will collide with the terrain. Ownership IS transferred! - /// The amount of time in ms this MOPixel will exist. 0 means unlimited. + /// @param color A Color object specifying the color of this MOPixel. + /// @param mass A float specifying the object's mass in Kilograms (kg). + /// @param position A Vector specifying the initial position. + /// @param velocity A Vector specifying the initial velocity. + /// @param atom An Atom that will collide with the terrain. Ownership IS transferred! + /// @param lifetime The amount of time in ms this MOPixel will exist. 0 means unlimited. MOPixel(Color color, const float mass, const Vector& position, const Vector& velocity, Atom* atom, const unsigned long lifetime = 0) { Clear(); Create(color, mass, position, velocity, atom, lifetime); } - /// /// Makes the MOPixel object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Makes the MOPixel object ready for use. - /// - /// A Color object specifying the color of this MOPixel. - /// A float specifying the object's mass in Kilograms (kg). - /// A Vector specifying the initial position. - /// A Vector specifying the initial velocity. - /// An Atom that will collide with the terrain. - /// The amount of time in ms this MOPixel will exist. 0 means unlimited. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param color A Color object specifying the color of this MOPixel. + /// @param mass A float specifying the object's mass in Kilograms (kg). + /// @param position A Vector specifying the initial position. + /// @param velocity A Vector specifying the initial velocity. + /// @param atom An Atom that will collide with the terrain. + /// @param lifetime The amount of time in ms this MOPixel will exist. 0 means unlimited. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(Color color, const float mass, const Vector& position, const Vector& velocity, Atom* atom, const unsigned long lifetime = 0); - /// /// Creates a MOPixel to be identical to another, by deep copy. - /// - /// A reference to the MOPixel to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the MOPixel to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const MOPixel& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a MOPixel object before deletion from system memory. - /// ~MOPixel() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the MOPixel object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Resets the entire MOPixel, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); MovableObject::Reset(); @@ -85,132 +67,92 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the drawing priority of this MOPixel, if two things were overlap when copying to the terrain, the higher priority MO would end up getting drawn. - /// - /// The drawing priority of this MOPixel. + /// @return The drawing priority of this MOPixel. int GetDrawPriority() const override; - /// /// Gets the main Material of this MOPixel. - /// - /// The Material of this MOPixel. + /// @return The Material of this MOPixel. const Material* GetMaterial() const override; - /// /// Gets the current Atom of this MOPixel. - /// - /// A const reference to the current Atom. + /// @return A const reference to the current Atom. const Atom* GetAtom() const { return m_Atom; } - /// /// Replaces the current Atom of this MOPixel with a new one. - /// - /// A reference to the new Atom. Ownership IS transferred! + /// @param newAtom A reference to the new Atom. Ownership IS transferred! void SetAtom(Atom* newAtom); - /// /// Gets the color of this MOPixel. - /// - /// A Color object describing the color. + /// @return A Color object describing the color. Color GetColor() const { return m_Color; } - /// /// Sets the color value of this MOPixel. - /// - /// A Color object specifying the new color index value. + /// @param newColor A Color object specifying the new color index value. void SetColor(Color newColor) { m_Color = newColor; } - /// /// Travel distance until the bullet start to lose lethality. - /// - /// The factor that modifies the base value. + /// @return The factor that modifies the base value. float GetMaxLethalRangeFactor() const { return m_MaxLethalRange; } - /// /// Travel distance until the bullet start to lose lethality. - /// - /// The distance in pixels. + /// @param range The distance in pixels. void SetLethalRange(float range); - /// /// Gets the longest a trail can be drawn, in pixels. - /// - /// The new max length, in pixels. If 0, no trail is drawn. + /// @return The new max length, in pixels. If 0, no trail is drawn. int GetTrailLength() const; - /// /// Sets the longest a trail can be drawn, in pixels. - /// - /// The new max length, in pixels. If 0, no trail is drawn. + /// @param trailLength The new max length, in pixels. If 0, no trail is drawn. void SetTrailLength(int trailLength); - /// /// Gets this MOPixel's staininess, which defines how likely a pixel is to stain a surface when it collides with it. - /// - /// This MOPixel's current staininess value. + /// @return This MOPixel's current staininess value. float GetStaininess() const { return m_Staininess; } - /// /// Sets this MOPixel's staininess, which defines how likely a pixel is to stain a surface when it collides with it. - /// - /// The new staininess value. + /// @param staininess The new staininess value. void SetStaininess(float staininess) { m_Staininess = staininess; } - /// /// Whether a set of X, Y coordinates overlap us (in world space). - /// - /// The given X coordinate, in world space. - /// The given Y coordinate, in world space. - /// Whether the given coordinate overlap us. + /// @param pixelX The given X coordinate, in world space. + /// @param pixelY The given Y coordinate, in world space. + /// @return Whether the given coordinate overlap us. bool HitTestAtPixel(int pixelX, int pixelY) const override; #pragma endregion #pragma region Virtual Override Methods - /// /// Travels this MOPixel, using its physical representation. - /// void Travel() override; - /// /// Calculates the collision response when another MO's Atom collides with this MO's physical representation. /// The effects will be applied directly to this MO, and also represented in the passed in HitData. - /// - /// Reference to the HitData struct which describes the collision. This will be modified to represent the results of the collision. - /// Whether the collision has been deemed valid. If false, then disregard any impulses in the HitData. + /// @param hitData Reference to the HitData struct which describes the collision. This will be modified to represent the results of the collision. + /// @return Whether the collision has been deemed valid. If false, then disregard any impulses in the HitData. bool CollideAtPoint(HitData& hitData) override; - /// /// Does the calculations necessary to detect whether this MOPixel is at rest or not. IsAtRest() retrieves the answer. - /// void RestDetection() override; - /// /// Defines what should happen when this MOPixel hits and then bounces off of something. This is called by the owned Atom/AtomGroup of this MOPixel during travel. - /// - /// The HitData describing the collision in detail. - /// Whether the MOPixel should immediately halt any travel going on after this bounce. + /// @param hd The HitData describing the collision in detail. + /// @return Whether the MOPixel should immediately halt any travel going on after this bounce. bool OnBounce(HitData& hd) override { return false; } - /// /// Defines what should happen when this MOPixel hits and then sink into something. This is called by the owned Atom/AtomGroup of this MOPixel during travel. - /// - /// The HitData describing the collision in detail. - /// Whether the MOPixel should immediately halt any travel going on after this sinkage. + /// @param hd The HitData describing the collision in detail. + /// @return Whether the MOPixel should immediately halt any travel going on after this sinkage. bool OnSink(HitData& hd) override { return false; } - /// /// Updates this MOPixel. Supposed to be done every frame. - /// void Update() override; - /// /// Draws this MOPixel's current graphical representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// In which mode to draw in. See the DrawMode enumeration for the modes. - /// Whether to not draw any extra 'ghost' items of this MOPixel, indicator arrows or hovering HUD text and so on. + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MOPixel, indicator arrows or hovering HUD text and so on. void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion @@ -227,14 +169,10 @@ namespace RTE { float m_Staininess; //!< How likely a pixel is to stain a surface when it collides with it. Defaults to 0 (never stain). private: - /// /// Sets the screen effect to draw at the final post-processing stage. - /// void SetPostScreenEffectToDraw() const; - /// /// Clears all the member variables of this MOPixel, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/Entities/MOSParticle.cpp b/Source/Entities/MOSParticle.cpp index 16bf673041..f9270d7a91 100644 --- a/Source/Entities/MOSParticle.cpp +++ b/Source/Entities/MOSParticle.cpp @@ -7,15 +7,11 @@ namespace RTE { ConcreteClassInfo(MOSParticle, MovableObject, 1000); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::Clear() { m_Atom = nullptr; m_SpriteAnimMode = OVERLIFETIME; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSParticle::Create() { if (MOSprite::Create() < 0) { return -1; @@ -26,8 +22,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSParticle::Create(const MOSParticle& reference) { MOSprite::Create(reference); @@ -37,8 +31,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSParticle::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MOSprite::ReadProperty(propName, reader)); @@ -53,8 +45,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSParticle::Save(Writer& writer) const { MOSprite::Save(writer); @@ -67,8 +57,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::Destroy(bool notInherited) { delete m_Atom; @@ -78,24 +66,16 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSParticle::GetDrawPriority() const { return m_Atom->GetMaterial()->GetPriority(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Material* MOSParticle::GetMaterial() const { return m_Atom->GetMaterial(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::SetAtom(Atom* newAtom) { delete m_Atom; m_Atom = newAtom; m_Atom->SetOwner(this); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::RestDetection() { MOSprite::RestDetection(); @@ -107,8 +87,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::Travel() { MOSprite::Travel(); @@ -157,8 +135,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::Update() { MOSprite::Update(); @@ -167,8 +143,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { RTEAssert(!m_aSprite.empty(), "No sprite bitmaps loaded to draw " + GetPresetName()); RTEAssert(m_Frame >= 0 && m_Frame < m_FrameCount, "Frame is out of bounds for " + GetPresetName()); @@ -241,8 +215,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSParticle::SetPostScreenEffectToDraw() const { if (m_AgeTimer.GetElapsedSimTimeMS() >= m_EffectStartTime && (m_EffectStopTime == 0 || !m_AgeTimer.IsPastSimMS(m_EffectStopTime))) { if (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY())) { diff --git a/Source/Entities/MOSParticle.h b/Source/Entities/MOSParticle.h index 703feeb567..d2ed585fa9 100644 --- a/Source/Entities/MOSParticle.h +++ b/Source/Entities/MOSParticle.h @@ -7,9 +7,7 @@ namespace RTE { class Atom; - /// /// A small animated sprite that plays its animation and changes the animation and playback speed when it collides with other things. - /// class MOSParticle : public MOSprite { public: @@ -18,55 +16,41 @@ namespace RTE { SerializableOverrideMethods; #pragma region Creation - /// /// Constructor method used to instantiate a MOSParticle object in system memory. Create() should be called before using the object. - /// MOSParticle() { Clear(); } - /// /// Makes the MOSParticle object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Makes the MOSParticle object ready for use. - /// - /// A pointer to ContentFile that represents the bitmap file that will be used to create the Sprite. - /// The number of frames in the Sprite's animation. - /// A float specifying the object's mass in Kilograms (kg). - /// A Vector specifying the initial position. - /// A Vector specifying the initial velocity. - /// The amount of time in ms this MOSParticle will exist. 0 means unlimited. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param spriteFile A pointer to ContentFile that represents the bitmap file that will be used to create the Sprite. + /// @param frameCount The number of frames in the Sprite's animation. + /// @param mass A float specifying the object's mass in Kilograms (kg). + /// @param position A Vector specifying the initial position. + /// @param velocity A Vector specifying the initial velocity. + /// @param lifetime The amount of time in ms this MOSParticle will exist. 0 means unlimited. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(ContentFile spriteFile, const int frameCount = 1, const float mass = 1, const Vector& position = Vector(0, 0), const Vector& velocity = Vector(0, 0), const unsigned long lifetime = 0) { MOSprite::Create(spriteFile, frameCount, mass, position, velocity, lifetime); return 0; } - /// /// Creates a MOSParticle to be identical to another, by deep copy. - /// - /// A reference to the MOSParticle to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the MOSParticle to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const MOSParticle& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a MOSParticle object before deletion from system memory. - /// ~MOSParticle() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the SceneLayer object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Resets the entire MOSParticle, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); MOSprite::Reset(); @@ -74,76 +58,54 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the drawing priority of this MOSParticle. If two things are overlapping when copying to the terrain, the higher priority MO would end up getting drawn. - /// - /// The drawing priority of this MOSParticle. + /// @return The drawing priority of this MOSParticle. int GetDrawPriority() const override; - /// /// Gets the main material of this MOSParticle. - /// - /// The material of this MOSParticle. + /// @return The material of this MOSParticle. const Material* GetMaterial() const override; - /// /// Gets the current Atom of this MOSParticle. - /// - /// A const reference to the current Atom. + /// @return A const reference to the current Atom. const Atom* GetAtom() const { return m_Atom; } - /// /// Replaces the current Atom of this MOSParticle with a new one. - /// - /// A reference to the new Atom. + /// @param newAtom A reference to the new Atom. void SetAtom(Atom* newAtom); #pragma endregion #pragma region Virtual Override Methods - /// /// Travels this MOSParticle, using its physical representation. - /// void Travel() override; - /// /// Calculates the collision response when another MO's Atom collides with this MO's physical representation. /// The effects will be applied directly to this MO, and also represented in the passed in HitData. - /// - /// Reference to the HitData struct which describes the collision. This will be modified to represent the results of the collision. - /// Whether the collision has been deemed valid. If false, then disregard any impulses in the HitData. + /// @param hitData Reference to the HitData struct which describes the collision. This will be modified to represent the results of the collision. + /// @return Whether the collision has been deemed valid. If false, then disregard any impulses in the HitData. bool CollideAtPoint(HitData& hitData) override { return true; } - /// /// Does the calculations necessary to detect whether this MOSParticle is at rest or not. IsAtRest() retrieves the answer. - /// void RestDetection() override; - /// /// Defines what should happen when this MOSParticle hits and then bounces off of something. This is called by the owned Atom/AtomGroup of this MOSParticle during travel. - /// - /// The HitData describing the collision in detail. - /// Whether the MOSParticle should immediately halt any travel going on after this bounce. + /// @param hd The HitData describing the collision in detail. + /// @return Whether the MOSParticle should immediately halt any travel going on after this bounce. bool OnBounce(HitData& hd) override { return false; } - /// /// Defines what should happen when this MOSParticle hits and then sink into something. This is called by the owned Atom/AtomGroup of this MOSParticle during travel. - /// - /// The HitData describing the collision in detail. - /// Whether the MOSParticle should immediately halt any travel going on after this sinkage. + /// @param hd The HitData describing the collision in detail. + /// @return Whether the MOSParticle should immediately halt any travel going on after this sinkage. bool OnSink(HitData& hd) override { return false; } - /// /// Updates this MOParticle. Supposed to be done every frame. - /// void Update() override; - /// /// Draws this MOSParticle's current graphical representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// In which mode to draw in. See the DrawMode enumeration for the modes. - /// Whether to not draw any extra 'ghost' items of this MOSParticle, indicator arrows or hovering HUD text and so on. + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MOSParticle, indicator arrows or hovering HUD text and so on. void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion @@ -154,14 +116,10 @@ namespace RTE { float m_TimeRest; //!< Accumulated time in seconds that did not cause a frame change. private: - /// /// Sets the screen effect to draw at the final post-processing stage. - /// void SetPostScreenEffectToDraw() const; - /// /// Clears all the member variables of this MOSParticle, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/MOSRotating.cpp b/Source/Entities/MOSRotating.cpp index 2521358760..37390c25ae 100644 --- a/Source/Entities/MOSRotating.cpp +++ b/Source/Entities/MOSRotating.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MOSRotating.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the MOSRotating class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MOSRotating.h" #include "CameraMan.h" @@ -43,12 +31,6 @@ namespace RTE { BITMAP* MOSRotating::m_spTempBitmapS256 = 0; BITMAP* MOSRotating::m_spTempBitmapS512 = 0; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MOSRotating, effectively - // resetting the members of this abstraction level only. - void MOSRotating::Clear() { m_pAtomGroup = 0; m_pDeepGroup = 0; @@ -90,11 +72,6 @@ namespace RTE { m_FlashWhiteTimer.SetRealTimeLimitMS(0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MOSParticle object ready for use. - int MOSRotating::Create() { if (MOSprite::Create() < 0) return -1; @@ -181,11 +158,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MOSParticle object ready for use. - int MOSRotating::Create(ContentFile spriteFile, const int frameCount, const float mass, @@ -204,11 +176,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MOSRotating to be identical to another, by deep copy. - int MOSRotating::Create(const MOSRotating& reference) { MOSprite::Create(reference); @@ -283,14 +250,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int MOSRotating::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MOSprite::ReadProperty(propName, reader)); @@ -359,12 +318,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this MOSRotating with a Writer for - // later recreation with Create(Reader &reader); - int MOSRotating::Save(Writer& writer) const { MOSprite::Save(writer); @@ -408,8 +361,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSRotating::GetGibWoundLimit(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const { int gibWoundLimit = m_GibWoundLimit; if (includePositiveDamageAttachables || includeNegativeDamageAttachables || includeNoDamageAttachables) { @@ -426,8 +377,6 @@ namespace RTE { return gibWoundLimit; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MOSRotating::GetWoundCount(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const { int woundCount = m_Wounds.size(); if (includePositiveDamageAttachables || includeNegativeDamageAttachables || includeNoDamageAttachables) { @@ -444,8 +393,6 @@ namespace RTE { return woundCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Attachable* MOSRotating::GetNearestDetachableAttachableToOffset(const Vector& offset) const { Attachable* nearestAttachable = nullptr; float closestRadius = m_SpriteRadius; @@ -461,8 +408,6 @@ namespace RTE { return nearestAttachable; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::DetachAttachablesFromImpulse(Vector& impulseVector) { float impulseRemainder = impulseVector.GetMagnitude(); // Find the attachable closest to the impact point by using an inverted impulse vector. @@ -485,8 +430,6 @@ namespace RTE { impulseVector.SetMagnitude(impulseRemainder); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::AddWound(AEmitter* woundToAdd, const Vector& parentOffsetToSet, bool checkGibWoundLimit) { if (woundToAdd && !m_ToDelete) { if (checkGibWoundLimit && m_GibWoundLimit > 0 && m_Wounds.size() + 1 >= m_GibWoundLimit) { @@ -513,8 +456,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float MOSRotating::RemoveWounds(int numberOfWoundsToRemove, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) { float damage = 0; int woundCount = GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); @@ -589,13 +530,6 @@ namespace RTE { MovableObject::DestroyScriptState(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MOSRotating object. - void MOSRotating::Destroy(bool notInherited) { delete m_pAtomGroup; delete m_pDeepGroup; @@ -623,8 +557,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::SetAsNoID() { MovableObject::SetAsNoID(); for (Attachable* attachable: m_Attachables) { @@ -632,13 +564,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMaterial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the main Material of this MOSRotating. - Material const* MOSRotating::GetMaterial() const { // if (m_pAtomGroup) return m_pAtomGroup->GetMaterial(); @@ -660,13 +585,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetDrawPriority - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the drawing priority of this MovableObject, if two things were - // overlap when copying to the terrain, the higher priority MO would - // end up getting drawn. - int MOSRotating::GetDrawPriority() const { return INT_MAX; } @@ -699,24 +617,12 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddRecoil - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds recoil effects to this MOSprite. - void MOSRotating::AddRecoil() { m_RecoilOffset.SetXY(1, 0); m_RecoilOffset.RadRotate(m_Rotation.GetRadAngle() + c_PI); m_Recoiled = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - bool MOSRotating::CollideAtPoint(HitData& hd) { if (m_ToDelete) { return false; // TODO: Add a settings flag to enable old school particle sponges! @@ -785,37 +691,15 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnBounce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Defines what should happen when this MovableObject hits and then - // bounces off of something. This is called by the owned Atom/AtomGroup - // of this MovableObject during travel. - bool MOSRotating::OnBounce(HitData& hd) { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnSink - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Defines what should happen when this MovableObject hits and then - // sink into something. This is called by the owned Atom/AtomGroup - // of this MovableObject during travel. - bool MOSRotating::OnSink(HitData& hd) { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ParticlePenetration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Determines whether a particle which has hit this MO will penetrate, - // and if so, whether it gets lodged or exits on the other side of this - // MO. Appropriate effects will be determined and applied ONLY IF there - // was penetration! If not, nothing will be affected. - bool MOSRotating::ParticlePenetration(HitData& hd) { // Only particles can penetrate. if (!(dynamic_cast(hd.Body[HITOR]) || dynamic_cast(hd.Body[HITOR]))) @@ -982,8 +866,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::GibThis(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { if (m_MissionCritical || m_ToDelete) { return; @@ -1016,8 +898,6 @@ namespace RTE { m_ToDelete = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::CreateGibsWhenGibbing(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { if (m_GibScreenShakeAmount != -1.0F) { g_CameraMan.AddScreenShake(m_GibScreenShakeAmount, m_Pos); @@ -1141,8 +1021,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::RemoveAttachablesWhenGibbing(const Vector& impactImpulse, MovableObject* movableObjectToIgnore) { const std::vector nonVolatileAttachablesVectorForLuaSafety{m_Attachables.begin(), m_Attachables.end()}; for (Attachable* attachable: nonVolatileAttachablesVectorForLuaSafety) { @@ -1169,21 +1047,10 @@ namespace RTE { m_Attachables.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: MoveOutOfTerrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks whether any of the Atom:s in this MovableObject are on top of - // terrain pixels, and if so, attempt to move this out so none of this' - // Atoms are on top of the terrain any more. - bool MOSRotating::MoveOutOfTerrain(unsigned char strongerThan) { return m_pAtomGroup->ResolveTerrainIntersection(m_Pos, strongerThan); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::ApplyForces() { float deltaTime = g_TimerMan.GetDeltaTimeSecs(); @@ -1196,8 +1063,6 @@ namespace RTE { MOSprite::ApplyForces(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::ApplyImpulses() { for (const auto& [impulseForceVector, impulseForceOffset]: m_ImpulseForces) { if (!impulseForceOffset.IsZero()) { @@ -1230,15 +1095,6 @@ namespace RTE { MOSprite::ApplyImpulses(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ResetAllTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resest all the timers used by this. Can be emitters, etc. This is to - // prevent backed up emissions to come out all at once while this has been - // held dormant in an inventory. - void MOSRotating::ResetAllTimers() { MovableObject::ResetAllTimers(); @@ -1249,8 +1105,6 @@ namespace RTE { (*attachable)->ResetAllTimers(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::RestDetection() { MOSprite::RestDetection(); @@ -1289,8 +1143,6 @@ namespace RTE { m_PrevAngVel = m_AngularVel; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MOSRotating::IsAtRest() { if (m_RestThreshold < 0 || m_PinStrength != 0) { return false; @@ -1300,8 +1152,6 @@ namespace RTE { return m_RestTimer.IsPastSimMS(m_RestThreshold); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MOSRotating::IsOnScenePoint(Vector& scenePoint) const { if (!m_aSprite[m_Frame]) { return false; @@ -1326,13 +1176,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: EraseFromTerrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Cuts this' silhouette out from the terrain's material and color layers. - void MOSRotating::EraseFromTerrain() { Vector pivot = -m_SpriteOffset; @@ -1352,12 +1195,6 @@ namespace RTE { std::deque pixels = g_SceneMan.GetTerrain()->EraseSilhouette(m_HFlipped ? m_pFlipBitmap : m_aSprite[m_Frame], m_Pos, pivot, m_Rotation, m_Scale, false); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DeepCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if any of this' deep group atmos are on top of the terrain, and - // if so, erases this' silhouette from the terrain. - bool MOSRotating::DeepCheck(bool makeMOPs, int skipMOP, int maxMOPs) { // Check for deep penetration of the terrain and // generate splash of MOPixels accordingly. @@ -1417,14 +1254,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreTravel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does stuff that needs to be done before Travel(). Always call before - // calling Travel. - // Arguments: None. - // Return value: None. - void MOSRotating::PreTravel() { MOSprite::PreTravel(); @@ -1436,11 +1265,6 @@ namespace RTE { #endif } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Travel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Travels this MovableObject, using its physical representation. - void MOSRotating::Travel() { MOSprite::Travel(); @@ -1474,12 +1298,6 @@ namespace RTE { m_pAtomGroup->ClearMOIDIgnoreList(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PostTravel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does stuff that needs to be done after Update(). Always call after - // calling Update. - void MOSRotating::PostTravel() { // Check for stupid velocities to gib instead of outright deletion that MOSprite::PostTravel() will do if (IsTooFast()) { @@ -1526,11 +1344,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MOSRotating. Supposed to be done every frame. - void MOSRotating::Update() { MOSprite::Update(); @@ -1588,8 +1401,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::PostUpdate() { for (auto itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) { (*itr)->PostUpdate(); @@ -1602,8 +1413,6 @@ namespace RTE { MovableObject::PostUpdate(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MOSRotating::DrawMOIDIfOverlapping(MovableObject* pOverlapMO) { if (pOverlapMO == this || !m_GetsHitByMOs || !pOverlapMO->GetsHitByMOs()) { return false; @@ -1621,8 +1430,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO This should just be defined in MOSR instead of having an empty definition in MO. MOSR would need to override UpdateMOID accordingly, but this would clean things up a little. void MOSRotating::UpdateChildMOIDs(std::vector& MOIDIndex, MOID rootMOID, bool makeNewMOID) { MOSprite::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); @@ -1635,8 +1442,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MOSRotating::AttachableIsHardcoded(const Attachable* attachableToCheck) const { if (attachableToCheck->GetParent() != this) { return false; @@ -1646,16 +1451,12 @@ namespace RTE { return m_HardcodedAttachableUniqueIDsAndRemovers.find(attachableUniqueID) != m_HardcodedAttachableUniqueIDsAndRemovers.end() || m_HardcodedAttachableUniqueIDsAndSetters.find(attachableUniqueID) != m_HardcodedAttachableUniqueIDsAndSetters.end(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::AddAttachable(Attachable* attachable) { if (attachable) { AddAttachable(attachable, attachable->GetParentOffset()); } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::AddAttachable(Attachable* attachable, const Vector& parentOffsetToSet) { if (attachable) { RTEAssert(!attachable->IsAttached(), "Tried to add Attachable " + attachable->GetModuleAndPresetName() + " but it already has a parent, " + (attachable->IsAttached() ? attachable->GetParent()->GetModuleAndPresetName() : "ERROR") + "."); @@ -1670,8 +1471,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Attachable* MOSRotating::RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds) { if (MovableObject* attachableAsMovableObject = g_MovableMan.FindObjectByUniqueID(attachableUniqueID)) { return RemoveAttachable(dynamic_cast(attachableAsMovableObject), addToMovableMan, addBreakWounds); @@ -1679,8 +1478,6 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Attachable* MOSRotating::RemoveAttachable(Attachable* attachable, bool addToMovableMan, bool addBreakWounds) { if (!attachable || !attachable->IsAttached()) { return attachable; @@ -1751,15 +1548,11 @@ namespace RTE { return attachable; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::RemoveAndDeleteAttachable(Attachable* attachable) { attachable->SetToDelete(); RemoveAttachable(attachable); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::RemoveOrDestroyAllAttachables(bool destroy) { Attachable* attachable; for (auto attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end();) { @@ -1776,8 +1569,6 @@ namespace RTE { m_Attachables.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::GetMOIDs(std::vector& MOIDs) const { MOIDs.reserve(GetMOIDFootprint()); MOSprite::GetMOIDs(MOIDs); @@ -1788,8 +1579,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::SetWhichMOToNotHit(MovableObject* moToNotHit, float forHowLong) { MOSprite::SetWhichMOToNotHit(moToNotHit, forHowLong); for (Attachable* attachable: m_Attachables) { @@ -1797,14 +1586,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this MOSRotating's current graphical representation to a - // BITMAP of choice. - void MOSRotating::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { RTEAssert(!m_aSprite.empty(), "No sprite bitmaps loaded to draw!"); RTEAssert(m_Frame >= 0 && m_Frame < m_FrameCount, "Frame is out of bounds!"); @@ -2048,8 +1829,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::CorrectAttachableAndWoundPositionsAndRotations() const { for (Attachable* attachable: m_Attachables) { attachable->PreUpdate(); @@ -2065,8 +1844,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MOSRotating::OnSave() { for (AEmitter* wound: m_Wounds) { wound->OnSave(); @@ -2077,8 +1854,6 @@ namespace RTE { MovableObject::OnSave(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MOSRotating::TransferForcesFromAttachable(Attachable* attachable) { bool intact = false; Vector forces; diff --git a/Source/Entities/MOSRotating.h b/Source/Entities/MOSRotating.h index eb900ae0e6..3d4a80c446 100644 --- a/Source/Entities/MOSRotating.h +++ b/Source/Entities/MOSRotating.h @@ -1,18 +1,11 @@ #ifndef _RTEMOSROTATING_ #define _RTEMOSROTATING_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MOSRotating.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the MOSRotating class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the MOSRotating class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "MOSprite.h" #include "Gib.h" #include "PostProcessMan.h" @@ -25,18 +18,10 @@ namespace RTE { class AEmitter; class Attachable; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: MOSRotating - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A sprite movable object that can rotate. - // Parent(s): MOSprite. - // Class history: 05/30/2002 MOSRotating created. - + /// A sprite movable object that can rotate. class MOSRotating : public MOSprite { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: friend class AtomGroup; friend class SLTerrain; @@ -47,796 +32,493 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MOSRotating - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a MOSRotating object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a MOSRotating object in system + /// memory. Create() should be called before using the object. MOSRotating() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~MOSRotating - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a MOSRotating object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a MOSRotating object before deletion + /// from system memory. ~MOSRotating() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MOSRotating object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MOSRotating object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MOSRotating object ready for use. - // Arguments: A pointer to ContentFile that represents the bitmap file that will be - // used to create the Sprite. - // The number of frames in the Sprite's animation. - // A float specifying the object's mass in Kilograms (kg). - // A Vector specifying the initial position. - // A Vector specifying the initial velocity. - // The amount of time in ms this MovableObject will exist. 0 means unlim. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MOSRotating object ready for use. + /// @param spriteFile A pointer to ContentFile that represents the bitmap file that will be + /// used to create the Sprite. + /// @param frameCount The number of frames in the Sprite's animation. (default: 1) + /// @param mass A float specifying the object's mass in Kilograms (kg). (default: 1) + /// @param position A Vector specifying the initial position. (default: Vector(0) + /// @param 0) A Vector specifying the initial velocity. + /// @param velocity The amount of time in ms this MovableObject will exist. 0 means unlim. (default: Vector(0) + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(ContentFile spriteFile, const int frameCount = 1, const float mass = 1, const Vector& position = Vector(0, 0), const Vector& velocity = Vector(0, 0), const unsigned long lifetime = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MOSRotating to be identical to another, by deep copy. - // Arguments: A reference to the MOSRotating to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a MOSRotating to be identical to another, by deep copy. + /// @param reference A reference to the MOSRotating to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const MOSRotating& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire MOSRotating, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire MOSRotating, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); MOSprite::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Gets the radius of this MOSRotating, not including any Attachables. - /// - /// + /// @return float GetIndividualRadius() const { return m_SpriteRadius; } - /// /// Gets the radius of this MOSRotating, including any Attachables. - /// - /// The radius of this MOSRotating, including any Attachables. + /// @return The radius of this MOSRotating, including any Attachables. float GetRadius() const override { return std::max(m_SpriteRadius, m_FarthestAttachableDistanceAndRadius); } - /// /// Gets the diameter of this MOSRotating, not including any Attachables. - /// - /// + /// @return float GetIndividualDiameter() const { return m_SpriteDiameter; } - /// /// Gets the diameter of this MOSRotating, including any Attachables. - /// - /// The diameter of this MOSRotating, including any Attachables. + /// @return The diameter of this MOSRotating, including any Attachables. float GetDiameter() const override { return GetRadius() * 2.0F; } - /// /// Checks if the given Attachable should affect radius, and handles it if it should. - /// - /// The Attachable to check. - /// Whether the radius affecting Attachable changed as a result of this call. + /// @param attachable The Attachable to check. + /// @return Whether the radius affecting Attachable changed as a result of this call. virtual bool HandlePotentialRadiusAffectingAttachable(const Attachable* attachable); - /// /// Gets the mass value of this MOSRotating, not including any Attachables or wounds. - /// - /// The mass of this MOSRotating. + /// @return The mass of this MOSRotating. float GetIndividualMass() const { return MovableObject::GetMass(); } - /// /// Gets the mass value of this MOSRotating, including the mass of all its Attachables and wounds, and their Attachables and so on. - /// - /// The mass of this MOSRotating and all of its Attachables and wounds in Kilograms (kg). + /// @return The mass of this MOSRotating and all of its Attachables and wounds in Kilograms (kg). float GetMass() const override { return MovableObject::GetMass() + m_AttachableAndWoundMass; } - /// /// Updates the total mass of Attachables and wounds for this MOSRotating, intended to be used when Attachables' masses get modified. Simply subtracts the old mass and adds the new one. - /// - /// The mass the Attachable or wound had before its mass was modified. - /// The up-to-date mass of the Attachable or wound after its mass was modified. + /// @param oldAttachableOrWoundMass The mass the Attachable or wound had before its mass was modified. + /// @param newAttachableOrWoundMass The up-to-date mass of the Attachable or wound after its mass was modified. virtual void UpdateAttachableAndWoundMass(float oldAttachableOrWoundMass, float newAttachableOrWoundMass) { m_AttachableAndWoundMass += newAttachableOrWoundMass - oldAttachableOrWoundMass; } - /// /// Gets the MOIDs of this MOSRotating and all its Attachables and Wounds, putting them into the MOIDs vector. - /// - /// The vector that will store all the MOIDs of this MOSRotating. + /// @param MOIDs The vector that will store all the MOIDs of this MOSRotating. void GetMOIDs(std::vector& MOIDs) const override; - /// /// Sets the MOID of this MOSRotating and any Attachables on it to be g_NoMOID (255) for this frame. - /// void SetAsNoID() override; - /// /// Sets this MOSRotating to not hit a specific other MO and all its children even though MO hitting is enabled on this MOSRotating. - /// - /// A pointer to the MO to not be hitting. Null pointer means don't ignore anything. Ownership is NOT transferred! - /// How long, in seconds, to ignore the specified MO. A negative number means forever. + /// @param moToNotHit A pointer to the MO to not be hitting. Null pointer means don't ignore anything. Ownership is NOT transferred! + /// @param forHowLong How long, in seconds, to ignore the specified MO. A negative number means forever. void SetWhichMOToNotHit(MovableObject* moToNotHit = nullptr, float forHowLong = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAtomGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current AtomGroup of this MOSRotating. - // Arguments: None. - // Return value: A const reference to the current AtomGroup. - + /// Gets the current AtomGroup of this MOSRotating. + /// @return A const reference to the current AtomGroup. AtomGroup* GetAtomGroup() { return m_pAtomGroup; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMaterial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the main Material of this MOSRotating. - // Arguments: None. - // Return value: The the Material of this MOSRotating. - + /// Gets the main Material of this MOSRotating. + /// @return The the Material of this MOSRotating. Material const* GetMaterial() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetDrawPriority - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the drawing priority of this MovableObject, if two things were - // overlap when copying to the terrain, the higher priority MO would - // end up getting drawn. - // Arguments: None. - // Return value: The the priority of this MovableObject. Higher number, the higher - // priority. - + /// Gets the drawing priority of this MovableObject, if two things were + /// overlap when copying to the terrain, the higher priority MO would + /// end up getting drawn. + /// @return The the priority of this MovableObject. Higher number, the higher + /// priority. int GetDrawPriority() const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRecoilForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current recoil impulse force Vector of this MOSprite. - // Arguments: None. - // Return value: A const reference to the current recoil impulse force in kg * m/s. - + /// Gets the current recoil impulse force Vector of this MOSprite. + /// @return A const reference to the current recoil impulse force in kg * m/s. const Vector& GetRecoilForce() const { return m_RecoilForce; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRecoilOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current recoil offset Vector of this MOSprite. - // Arguments: None. - // Return value: A const reference to the current recoil offset. - + /// Gets the current recoil offset Vector of this MOSprite. + /// @return A const reference to the current recoil offset. const Vector& GetRecoilOffset() const { return m_RecoilOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGibList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets direct access to the list of object this is to generate upon gibbing. - // Arguments: None. - // Return value: A pointer to the list of gibs. Ownership is NOT transferred! - + /// Gets direct access to the list of object this is to generate upon gibbing. + /// @return A pointer to the list of gibs. Ownership is NOT transferred! std::list* GetGibList() { return &m_Gibs; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddRecoil - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds graphical recoil offset to this MOSprite according to its angle. - // Arguments: None. - // Return value: None. - + /// Adds graphical recoil offset to this MOSprite according to its angle. void AddRecoil(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetRecoil - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds recoil offset to this MOSprite. - // Arguments: A vector with the recoil impulse force in kg * m/s. - // A vector with the recoil offset in pixels. - // Whether recoil should be activated or not for the next Draw(). - // Return value: None. - + /// Adds recoil offset to this MOSprite. + /// @param force A vector with the recoil impulse force in kg * m/s. + /// @param offset A vector with the recoil offset in pixels. + /// @param recoil Whether recoil should be activated or not for the next Draw(). (default: true) void SetRecoil(const Vector& force, const Vector& offset, bool recoil = true) { m_RecoilForce = force; m_RecoilOffset = offset; m_Recoiled = recoil; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsRecoiled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether this MOSprite is currently under the effects of - // recoil. - // Arguments: None. - // Return value: None. - + /// Returns whether this MOSprite is currently under the effects of + /// recoil. bool IsRecoiled() { return m_Recoiled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableDeepCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether or not this MOSRotating should check for deep penetrations - // the terrain or not. - // Arguments: Whether to enable deep penetration checking or not. - // Return value: None. - + /// Sets whether or not this MOSRotating should check for deep penetrations + /// the terrain or not. + /// @param enable Whether to enable deep penetration checking or not. (default: true) void EnableDeepCheck(const bool enable = true) { m_DeepCheck = enable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ForceDeepCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets to force a deep checking of this' silhouette against the terrain - // and create an outline hole in the terrain, generating particles of the - // intersecting pixels in the terrain. - // Arguments: Whether to force a deep penetration check for this sim frame or not.. - // Return value: None. - + /// Sets to force a deep checking of this' silhouette against the terrain + /// and create an outline hole in the terrain, generating particles of the + /// intersecting pixels in the terrain. + /// @param enable Whether to force a deep penetration check for this sim frame or not.. (default: true) void ForceDeepCheck(const bool enable = true) { m_ForceDeepCheck = enable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - // Arguments: Reference to the HitData struct which describes the collision. This - // will be modified to represent the results of the collision. - // Return value: Whether the collision has been deemed valid. If false, then disregard - // any impulses in the Hitdata. - + /// Calculates the collision response when another MO's Atom collides with + /// this MO's physical representation. The effects will be applied + /// directly to this MO, and also represented in the passed in HitData. + /// @param hitData Reference to the HitData struct which describes the collision. This + /// will be modified to represent the results of the collision. + /// @return Whether the collision has been deemed valid. If false, then disregard + /// any impulses in the Hitdata. bool CollideAtPoint(HitData& hitData) override; - /// /// Defines what should happen when this MovableObject hits and then bounces off of something. /// This is called by the owned Atom/AtomGroup of this MovableObject during travel. - /// - /// The HitData describing the collision in detail. - /// Whether the MovableObject should immediately halt any travel going on after this bounce. + /// @param hd The HitData describing the collision in detail. + /// @return Whether the MovableObject should immediately halt any travel going on after this bounce. bool OnBounce(HitData& hd) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: OnSink - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Defines what should happen when this MovableObject hits and then - // sink into something. This is called by the owned Atom/AtomGroup - // of this MovableObject during travel. - // Arguments: The HitData describing the collision in detail. - // Return value: Wheter the MovableObject should immediately halt any travel going on - // after this sinkage. - + /// Defines what should happen when this MovableObject hits and then + /// sink into something. This is called by the owned Atom/AtomGroup + /// of this MovableObject during travel. + /// @param hd The HitData describing the collision in detail. + /// @return Wheter the MovableObject should immediately halt any travel going on + /// after this sinkage. bool OnSink(HitData& hd) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ParticlePenetration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Determines whether a particle which has hit this MO will penetrate, - // and if so, whether it gets lodged or exits on the other side of this - // MO. Appropriate effects will be determined and applied ONLY IF there - // was penetration! If not, nothing will be affected. - // Arguments: The HitData describing the collision in detail, the impulses have to - // have been filled out! - // Return value: Whether the particle managed to penetrate into this MO or not. If - // somehting but a MOPixel or MOSParticle is being passed in as hitor, - // false will trivially be returned here. - + /// Determines whether a particle which has hit this MO will penetrate, + /// and if so, whether it gets lodged or exits on the other side of this + /// MO. Appropriate effects will be determined and applied ONLY IF there + /// was penetration! If not, nothing will be affected. + /// @param hd The HitData describing the collision in detail, the impulses have to + /// have been filled out! + /// @return Whether the particle managed to penetrate into this MO or not. If + /// somehting but a MOPixel or MOSParticle is being passed in as hitor, + /// false will trivially be returned here. virtual bool ParticlePenetration(HitData& hd); - /// /// Destroys this MOSRotating and creates its specified Gibs in its place with appropriate velocities. Any Attachables are removed and also given appropriate velocities. - /// - /// The impulse (kg * m/s) of the impact causing the gibbing to happen. - /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + /// @param impactImpulse The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// @param movableObjectToIgnore A pointer to an MO which the Gibs and Attachables should not be colliding with. virtual void GibThis(const Vector& impactImpulse = Vector(), MovableObject* movableObjectToIgnore = nullptr); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: MoveOutOfTerrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks whether any of the Atom:s in this MovableObject are on top of - // terrain pixels, and if so, attempt to move this out so none of this' - // Atoms are on top of the terrain any more. - // Arguments: Only consider materials stronger than this in the terrain for - // intersections. - // Return value: Whether any intersection was successfully resolved. Will return true - // even if there wasn't any intersections to begin with. - + /// Checks whether any of the Atom:s in this MovableObject are on top of + /// terrain pixels, and if so, attempt to move this out so none of this' + /// Atoms are on top of the terrain any more. + /// @param strongerThan Only consider materials stronger than this in the terrain for (default: g_MaterialAir) + /// intersections. + /// @return Whether any intersection was successfully resolved. Will return true + /// even if there wasn't any intersections to begin with. bool MoveOutOfTerrain(unsigned char strongerThan = g_MaterialAir) override; - /// /// Gathers, clears and applies this MOSRotating's accumulated forces. - /// void ApplyForces() override; - /// /// Gathers, clears and applies this MOSRotating's accumulated impulse forces, gibbing if appropriate. - /// void ApplyImpulses() override; - /// /// Gets the list of Attachables on this MOSRotating. - /// - /// The list of Attachables on this MOSRotating. + /// @return The list of Attachables on this MOSRotating. const std::list& GetAttachables() const { return m_Attachables; } - /// /// Gets whether or not the given Attachable is a hardcoded Attachable (e.g. an Arm, Leg, Turret, etc.) - /// - /// The Attachable to check. - /// Whether or not the Attachable is hardcoded. + /// @param attachableToCheck The Attachable to check. + /// @return Whether or not the Attachable is hardcoded. bool AttachableIsHardcoded(const Attachable* attachableToCheck) const; - /// /// Adds the passed in Attachable the list of Attachables and sets its parent to this MOSRotating. - /// - /// The Attachable to add. + /// @param attachable The Attachable to add. virtual void AddAttachable(Attachable* attachable); - /// /// Adds the passed in Attachable the list of Attachables, changes its parent offset to the passed in Vector, and sets its parent to this MOSRotating. - /// - /// The Attachable to add. - /// The Vector to set as the Attachable's parent offset. + /// @param attachable The Attachable to add. + /// @param parentOffsetToSet The Vector to set as the Attachable's parent offset. virtual void AddAttachable(Attachable* attachable, const Vector& parentOffsetToSet); - /// /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. - /// - /// The UniqueID of the Attachable to remove. - /// A pointer to the removed Attachable. Ownership IS transferred! + /// @param attachableUniqueID The UniqueID of the Attachable to remove. + /// @return A pointer to the removed Attachable. Ownership IS transferred! virtual Attachable* RemoveAttachable(long attachableUniqueID) { return RemoveAttachable(attachableUniqueID, false, false); } - /// /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. - /// - /// The UniqueID of the Attachable to remove. - /// Whether or not to add the Attachable to MovableMan once it has been removed. - /// Whether or not to add break wounds to the removed Attachable and this MOSRotating. - /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! + /// @param attachableUniqueID The UniqueID of the Attachable to remove. + /// @param addToMovableMan Whether or not to add the Attachable to MovableMan once it has been removed. + /// @param addBreakWounds Whether or not to add break wounds to the removed Attachable and this MOSRotating. + /// @return A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! virtual Attachable* RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds); - /// /// Removes the passed in Attachable and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. - /// - /// The Attachable to remove. - /// A pointer to the removed Attachable. Ownership IS transferred! + /// @param attachable The Attachable to remove. + /// @return A pointer to the removed Attachable. Ownership IS transferred! virtual Attachable* RemoveAttachable(Attachable* attachable) { return RemoveAttachable(attachable, false, false); } - /// /// Removes the passed in Attachable and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. - /// - /// The Attachable to remove. - /// Whether or not to add the Attachable to MovableMan once it has been removed. - /// Whether or not to add break wounds to the removed Attachable and this MOSRotating. - /// A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! + /// @param attachable The Attachable to remove. + /// @param addToMovableMan Whether or not to add the Attachable to MovableMan once it has been removed. + /// @param addBreakWounds Whether or not to add break wounds to the removed Attachable and this MOSRotating. + /// @return A pointer to the removed Attachable, if it wasn't added to MovableMan or nullptr if it was. Ownership IS transferred! virtual Attachable* RemoveAttachable(Attachable* attachable, bool addToMovableMan, bool addBreakWounds); - /// /// Removes the passed in Attachable, and sets it to delete so it will go straight to MovableMan and be handled there. - /// - /// The Attacahble to remove and delete. + /// @param attachable The Attacahble to remove and delete. void RemoveAndDeleteAttachable(Attachable* attachable); - /// /// Either removes or deletes all of this MOSRotating's Attachables. - /// - /// Whether to remove or delete the Attachables. Setting this to true deletes them, setting it to false removes them. + /// @param destroy Whether to remove or delete the Attachables. Setting this to true deletes them, setting it to false removes them. void RemoveOrDestroyAllAttachables(bool destroy); - /// /// Gets a damage-transferring, impulse-vulnerable Attachable nearest to the passed in offset. - /// - /// The offset that will be compared to each Attachable's ParentOffset. - /// The nearest detachable Attachable, or nullptr if none was found. + /// @param offset The offset that will be compared to each Attachable's ParentOffset. + /// @return The nearest detachable Attachable, or nullptr if none was found. Attachable* GetNearestDetachableAttachableToOffset(const Vector& offset) const; - /// /// Gibs or detaches any Attachables that would normally gib or detach from the passed in impulses. - /// - /// The impulse vector which determines the Attachables to gib or detach. Will be filled out with the remainder of impulses. + /// @param impulseVector The impulse vector which determines the Attachables to gib or detach. Will be filled out with the remainder of impulses. void DetachAttachablesFromImpulse(Vector& impulseVector); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ResetAllTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resest all the timers used by this. Can be emitters, etc. This is to - // prevent backed up emissions to come out all at once while this has been - // held dormant in an inventory. - // Arguments: None. - // Return value: None. - + /// Resest all the timers used by this. Can be emitters, etc. This is to + /// prevent backed up emissions to come out all at once while this has been + /// held dormant in an inventory. void ResetAllTimers() override; - /// /// Does the calculations necessary to detect whether this MOSRotating is at rest or not. IsAtRest() retrieves the answer. - /// void RestDetection() override; - /// /// Indicates whether this MOSRotating has been at rest with no movement for longer than its RestThreshold. - /// bool IsAtRest() override; - /// /// Indicates whether this MOSRotating's current graphical representation, including its Attachables, overlaps a point in absolute scene coordinates. - /// - /// The point in absolute scene coordinates to check for overlap with. - /// Whether or not this MOSRotating's graphical representation overlaps the given scene point. + /// @param scenePoint The point in absolute scene coordinates to check for overlap with. + /// @return Whether or not this MOSRotating's graphical representation overlaps the given scene point. bool IsOnScenePoint(Vector& scenePoint) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: EraseFromTerrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Cuts this' silhouette out from the terrain's material and color layers. - // Arguments: None. - // Return value: None. - + /// Cuts this' silhouette out from the terrain's material and color layers. void EraseFromTerrain(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DeepCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if any of this' deep group atmos are on top of the terrain, and - // if so, erases this' silhouette from the terrain. - // Arguments: Whether to make any MOPixels from erased terrain pixels at all. - // The size of the gaps between MOPixels knocked loose by the terrain erasure. - // The max number of MOPixel:s to generate as dislodged particles from the - // erased terrain. - // Return value: Whether deep penetration was detected and erasure was done. - + /// Checks if any of this' deep group atmos are on top of the terrain, and + /// if so, erases this' silhouette from the terrain. + /// @param makeMOPs Whether to make any MOPixels from erased terrain pixels at all. (default: true) + /// @param skipMOP The size of the gaps between MOPixels knocked loose by the terrain erasure. (default: 2) + /// @param maxMOP The max number of MOPixel:s to generate as dislodged particles from the (default: 100) + /// erased terrain. + /// @return Whether deep penetration was detected and erasure was done. bool DeepCheck(bool makeMOPs = true, int skipMOP = 2, int maxMOP = 100); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreTravel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does stuff that needs to be done before Travel(). Always call before - // calling Travel. - // Arguments: None. - // Return value: None. - + /// Does stuff that needs to be done before Travel(). Always call before + /// calling Travel. void PreTravel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Travel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Travels this MOSRotatin, using its physical representation. - // Arguments: None. - // Return value: None. - + /// Travels this MOSRotatin, using its physical representation. void Travel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PostTravel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does stuff that needs to be done after Update(). Always call after - // calling Update. - // Arguments: None. - // Return value: None. - + /// Does stuff that needs to be done after Update(). Always call after + /// calling Update. void PostTravel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; void PostUpdate() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawMOIDIfOverlapping - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the MOID representation of this to the SceneMan's MOID layer if - // this is found to potentially overlap another MovableObject. - // Arguments: The MovableObject to check this for overlap against. - // Return value: Whether it was drawn or not. - + /// Draws the MOID representation of this to the SceneMan's MOID layer if + /// this is found to potentially overlap another MovableObject. + /// @param pOverlapMO The MovableObject to check this for overlap against. + /// @return Whether it was drawn or not. bool DrawMOIDIfOverlapping(MovableObject* pOverlapMO) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this MOSRotating's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this MOSRotating's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - /// /// Gets the gib impulse limit for this MOSRotating, i.e. the amount of impulse force required in a frame to gib this MOSRotating. - /// - /// The gib impulse limit of this MOSRotating. + /// @return The gib impulse limit of this MOSRotating. float GetGibImpulseLimit() const { return m_GibImpulseLimit; } - /// /// Sets the gib impulse limit for this MOSRotating, i.e. the amount of impulse force required in a frame to gib this MOSRotating. - /// - /// The new gib impulse limit to use. + /// @param newGibImpulseLimit The new gib impulse limit to use. void SetGibImpulseLimit(float newGibImpulseLimit) { m_GibImpulseLimit = newGibImpulseLimit; } - /// /// Gets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. Does not include any Attachables. - /// - /// + /// @return int GetGibWoundLimit() const { return GetGibWoundLimit(false, false, false); } - /// /// Gets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. /// Optionally adds the gib wound limits of Attachables (and their Attachables, etc.) that match the conditions set by the provided parameters. - /// - /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. - /// The wound limit of this MOSRotating and, optionally, its Attachables. + /// @param includePositiveDamageAttachables Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. + /// @param includeNegativeDamageAttachables Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. + /// @param includeNoDamageAttachables Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. + /// @return The wound limit of this MOSRotating and, optionally, its Attachables. int GetGibWoundLimit(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const; - /// /// Sets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. /// This will not directly trigger gibbing, even if the limit is lower than the current number of wounds. - /// - /// The new gib wound limit to use. + /// @param newLimit The new gib wound limit to use. void SetGibWoundLimit(int newGibWoundLimit) { m_GibWoundLimit = newGibWoundLimit; } - /// /// Gets the rate at which wound count of this MOSRotating will diminish the impulse limit. - /// - /// The rate at which wound count affects the impulse limit. + /// @return The rate at which wound count affects the impulse limit. float GetWoundCountAffectsImpulseLimitRatio() const { return m_WoundCountAffectsImpulseLimitRatio; } - /// /// Gets whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. - /// - /// Whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. + /// @return Whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. bool GetGibAtEndOfLifetime() const { return m_GibAtEndOfLifetime; } - /// /// Sets whether this MOSRotating should gib at the end of its lifetime instead of just being deleted. - /// - /// Whether or not this MOSRotating should gib at the end of its lifetime instead of just being deleted. + /// @param shouldGibAtEndOfLifetime Whether or not this MOSRotating should gib at the end of its lifetime instead of just being deleted. void SetGibAtEndOfLifetime(bool shouldGibAtEndOfLifetime) { m_GibAtEndOfLifetime = shouldGibAtEndOfLifetime; } - /// /// Gets the gib blast strength this MOSRotating, i.e. the strength with which Gibs and Attachables will be launched when this MOSRotating is gibbed. - /// - /// The gib blast strength of this MOSRotating. + /// @return The gib blast strength of this MOSRotating. float GetGibBlastStrength() const { return m_GibBlastStrength; } - /// /// Sets the gib blast strength this MOSRotating, i.e. the strength with which Gibs and Attachables will be launched when this MOSRotating is gibbed. - /// - /// The new gib blast strength to use. + /// @param newGibBlastStrength The new gib blast strength to use. void SetGibBlastStrength(float newGibBlastStrength) { m_GibBlastStrength = newGibBlastStrength; } - /// /// Gets the amount of screenshake this will cause upon gibbing. - /// - /// The amount of screenshake this will cause when gibbing. If -1, this is calculated automatically. + /// @return The amount of screenshake this will cause when gibbing. If -1, this is calculated automatically. float GetGibScreenShakeAmount() const { return m_GibScreenShakeAmount; } - /// /// Gets a const reference to the list of Attachables on this MOSRotating. - /// - /// A const reference to the list of Attachables on this MOSRotating. + /// @return A const reference to the list of Attachables on this MOSRotating. const std::list& GetAttachableList() const { return m_Attachables; } - /// /// Gets a const reference to the list of wounds on this MOSRotating. - /// - /// A const reference to the list of wounds on this MOSRotating. + /// @return A const reference to the list of wounds on this MOSRotating. const std::vector& GetWoundList() const { return m_Wounds; } - /// /// Gets the number of wounds attached to this MOSRotating. /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. - /// The number of wounds on this MOSRotating. - /// + /// @return The number of wounds on this MOSRotating. int GetWoundCount() const { return GetWoundCount(true, false, false); } - /// /// Gets the number of wounds attached to this MOSRotating. /// Optionally adds the wound counts of Attachables (and their Attachables, etc.) that match the conditions set by the provided parameters. - /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. - /// The number of wounds on this MOSRotating and, optionally, its Attachables. - /// + /// @param includePositiveDamageAttachables Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. + /// @param includeNegativeDamageAttachables Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. + /// @param includeNoDamageAttachables Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. + /// @return The number of wounds on this MOSRotating and, optionally, its Attachables. int GetWoundCount(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const; - /// /// Adds the passed in wound AEmitter to the list of wounds and changes its parent offset to the passed in Vector. - /// - /// The wound AEmitter to add. - /// The vector to set as the wound AEmitter's parent offset. - /// Whether to gib this MOSRotating if adding this wound raises its wound count past its gib wound limit. Defaults to true. + /// @param woundToAdd The wound AEmitter to add. + /// @param parentOffsetToSet The vector to set as the wound AEmitter's parent offset. + /// @param checkGibWoundLimit Whether to gib this MOSRotating if adding this wound raises its wound count past its gib wound limit. Defaults to true. virtual void AddWound(AEmitter* woundToAdd, const Vector& parentOffsetToSet, bool checkGibWoundLimit = true); - /// /// Removes the specified number of wounds from this MOSRotating, and returns damage caused by these removed wounds. /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. - /// - /// The number of wounds that should be removed. - /// The amount of damage caused by these wounds, taking damage multipliers into account. + /// @param numberOfWoundsToRemove The number of wounds that should be removed. + /// @return The amount of damage caused by these wounds, taking damage multipliers into account. virtual float RemoveWounds(int numberOfWoundsToRemove) { return RemoveWounds(numberOfWoundsToRemove, true, false, false); } - /// /// Removes the specified number of wounds from this MOSRotating, and returns damage caused by these removed wounds. /// Optionally removes wounds from Attachables (and their Attachables, etc.) that match the conditions set by the provided inclusion parameters. - /// - /// The number of wounds that should be removed. - /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. - /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. - /// The amount of damage caused by these wounds, taking damage multipliers into account. + /// @param numberOfWoundsToRemove The number of wounds that should be removed. + /// @param includePositiveDamageAttachables Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. + /// @param includeNegativeDamageAttachables Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. + /// @param includeNoDamageAttachables Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. + /// @return The amount of damage caused by these wounds, taking damage multipliers into account. virtual float RemoveWounds(int numberOfWoundsToRemove, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables); - /// /// Cleans up and destroys the script state of this object, calling the Destroy callback in lua - /// void DestroyScriptState(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDamageMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets damage multiplier of this attachable. - // Arguments: New multiplier value. - // Return value: None. - + /// Sets damage multiplier of this attachable. + /// @param newValue New multiplier value. void SetDamageMultiplier(float newValue) { m_DamageMultiplier = newValue; m_NoSetDamageMultiplier = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDamageMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns damage multiplier of this attachable. - // Arguments: None. - // Return value: Current multiplier value. - + /// Returns damage multiplier of this attachable. + /// @return Current multiplier value. float GetDamageMultiplier() const { return m_DamageMultiplier; } - /// /// Gets whether the damage multiplier for this MOSRotating has been directly set, or is at its default value. - /// - /// Whether the damage multiplier for this MOSRotating has been set. + /// @return Whether the damage multiplier for this MOSRotating has been set. bool HasNoSetDamageMultiplier() const { return m_NoSetDamageMultiplier; } - /// /// Gets the velocity orientation scalar of this MOSRotating. - /// - /// New scalar value. + /// @return New scalar value. float GetOrientToVel() const { return m_OrientToVel; } - /// /// Sets the velocity orientation scalar of this MOSRotating. - /// - /// New scalar value. + /// @param newValue New scalar value. void SetOrientToVel(float newValue) { m_OrientToVel = newValue; } - /// /// Sets this MOSRotating and all its children to drawn white for a specified amount of time. - /// - /// Duration of flash in real time MS. + /// @param durationMS Duration of flash in real time MS. void FlashWhite(int durationMS = 32) { m_FlashWhiteTimer.SetRealTimeLimitMS(durationMS); m_FlashWhiteTimer.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTravelImpulse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Retrurns the amount of impulse force exerted on this during the last frame. - // Arguments: None. - // Return value: The amount of impulse force exerted on this during the last frame. - + /// Retrurns the amount of impulse force exerted on this during the last frame. + /// @return The amount of impulse force exerted on this during the last frame. Vector GetTravelImpulse() const { return m_TravelImpulse; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTravelImpulse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the amount of impulse force exerted on this during the last frame. - // Arguments: New impulse value - // Return value: None. - + /// Sets the amount of impulse force exerted on this during the last frame. + /// @param impulse New impulse value void SetTravelImpulse(Vector impulse) { m_TravelImpulse = impulse; } - /// /// Gets this MOSRotating's gib sound. Ownership is NOT transferred! - /// - /// The SoundContainer for this MOSRotating's gib sound. + /// @return The SoundContainer for this MOSRotating's gib sound. SoundContainer* GetGibSound() const { return m_GibSound; } - /// /// Sets this MOSRotating's gib sound. Ownership IS transferred! - /// - /// The new SoundContainer for this MOSRotating's gib sound. + /// @param newSound The new SoundContainer for this MOSRotating's gib sound. void SetGibSound(SoundContainer* newSound) { m_GibSound = newSound; } - /// /// Ensures all attachables and wounds are positioned and rotated correctly. Must be run when this MOSRotating is added to MovableMan to avoid issues with Attachables spawning in at (0, 0). - /// virtual void CorrectAttachableAndWoundPositionsAndRotations() const; - /// /// Method to be run when the game is saved via ActivityMan::SaveCurrentGame. Not currently used in metagame or editor saving. - /// void OnSave() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Transfers forces and impulse forces from the given Attachable to this MOSRotating, gibbing and/or removing the Attachable if needed. - /// - /// A pointer to the Attachable to apply forces from. Ownership is NOT transferred! - /// Whether or not the Attachable has been removed, in which case it'll usually be passed to MovableMan. + /// @param attachable A pointer to the Attachable to apply forces from. Ownership is NOT transferred! + /// @return Whether or not the Attachable has been removed, in which case it'll usually be passed to MovableMan. bool TransferForcesFromAttachable(Attachable* attachable); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateChildMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes this MO register itself and all its attached children in the - // MOID register and get ID:s for itself and its children for this frame. - // Arguments: The MOID index to register itself and its children in. - // The MOID of the root MO of this MO, ie the highest parent of this MO. - // 0 means that this MO is the root, ie it is owned by MovableMan. - // Whether this MO should make a new MOID to use for itself, or to use - // the same as the last one in the index (presumably its parent), - // Return value: None. - + /// Makes this MO register itself and all its attached children in the + /// MOID register and get ID:s for itself and its children for this frame. + /// @param MOIDIndex The MOID index to register itself and its children in. + /// @param rootMOID The MOID of the root MO of this MO, ie the highest parent of this MO. (default: g_NoMOID) + /// 0 means that this MO is the root, ie it is owned by MovableMan. + /// @param makeNewMOID Whether this MO should make a new MOID to use for itself, or to use (default: true) + /// the same as the last one in the index (presumably its parent), void UpdateChildMOIDs(std::vector& MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - /// /// Creates the particles specified by this MOSRotating's list of Gibs and adds them to MovableMan with appropriately randomized velocities, based on this MOSRotating's gib blast strength. - /// - /// The impulse (kg * m/s) of the impact that caused the gibbing to happen. - /// A pointer to an MO which the Attachables should not be colliding with. + /// @param impactImpulse The impulse (kg * m/s) of the impact that caused the gibbing to happen. + /// @param movableObjectToIgnore A pointer to an MO which the Attachables should not be colliding with. void CreateGibsWhenGibbing(const Vector& impactImpulse, MovableObject* movableObjectToIgnore); - /// /// Removes all Attachables from this MOSR, deleting them or adding them to MovableMan as appropriate, and giving them randomized velocities based on their properties and this MOSRotating's gib blast strength. - /// - /// The impulse (kg * m/s) of the impact that caused the gibbing to happen. - /// A pointer to an MO which the Attachables should not be colliding with. + /// @param impactImpulse The impulse (kg * m/s) of the impact that caused the gibbing to happen. + /// @param movableObjectToIgnore A pointer to an MO which the Attachables should not be colliding with. void RemoveAttachablesWhenGibbing(const Vector& impactImpulse, MovableObject* movableObjectToIgnore); // Member variables @@ -920,18 +602,10 @@ namespace RTE { static BITMAP* m_spTempBitmapS256; static BITMAP* m_spTempBitmapS512; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MOSRotating, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this MOSRotating, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/MOSprite.cpp b/Source/Entities/MOSprite.cpp index 4f5862373e..0970271e91 100644 --- a/Source/Entities/MOSprite.cpp +++ b/Source/Entities/MOSprite.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MOSprite.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the MOSprite class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MOSprite.h" #include "AEmitter.h" @@ -19,12 +7,6 @@ namespace RTE { AbstractClassInfo(MOSprite, MovableObject); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MOSprite, effectively - // resetting the members of this abstraction level only. - void MOSprite::Clear() { m_SpriteFile.Reset(); m_aSprite.clear(); @@ -50,11 +32,6 @@ namespace RTE { m_pExitWound = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MOSprite object ready for use. - int MOSprite::Create() { if (MovableObject::Create() < 0) return -1; @@ -80,11 +57,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MOSprite object ready for use. - int MOSprite::Create(ContentFile spriteFile, const int frameCount, const float mass, @@ -110,11 +82,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MOSprite to be identical to another, by deep copy. - int MOSprite::Create(const MOSprite& reference) { MovableObject::Create(reference); @@ -146,14 +113,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int MOSprite::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MovableObject::ReadProperty(propName, reader)); @@ -198,10 +157,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetEntryWound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets entry wound emitter for this MOSprite void MOSprite::SetEntryWound(std::string presetName, std::string moduleName) { if (presetName == "") m_pEntryWound = 0; @@ -209,10 +164,6 @@ namespace RTE { m_pEntryWound = dynamic_cast(g_PresetMan.GetEntityPreset("AEmitter", presetName, moduleName)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetExitWound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets exit wound emitter for this MOSprite void MOSprite::SetExitWound(std::string presetName, std::string moduleName) { if (presetName == "") m_pExitWound = 0; @@ -220,30 +171,14 @@ namespace RTE { m_pExitWound = dynamic_cast(g_PresetMan.GetEntityPreset("AEmitter", presetName, moduleName)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetEntryWoundPresetName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns entry wound emitter preset name for this MOSprite - std::string MOSprite::GetEntryWoundPresetName() const { return m_pEntryWound ? m_pEntryWound->GetPresetName() : ""; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetExitWoundPresetName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns exit wound emitter preset name for this MOSprite - std::string MOSprite::GetExitWoundPresetName() const { return m_pExitWound ? m_pExitWound->GetPresetName() : ""; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this MOSprite with a Writer for - // later recreation with Create(Reader &reader); - int MOSprite::Save(Writer& writer) const { MovableObject::Save(writer); // TODO: Make proper save system that knows not to save redundant data! @@ -274,11 +209,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MOSprite object. - void MOSprite::Destroy(bool notInherited) { // delete m_pEntryWound; Not doing this anymore since we're not owning // delete m_pExitWound; @@ -309,11 +239,6 @@ namespace RTE { return is_inside_bitmap(sprite, localX, localY, 0) && _getpixel(sprite, localX, localY) != ColorKeys::g_MaskColor; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFrame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Hard-sets the frame this sprite is supposed to show. - void MOSprite::SetFrame(unsigned int newFrame) { if (newFrame < 0) newFrame = 0; @@ -323,13 +248,6 @@ namespace RTE { m_Frame = newFrame; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetNextFrame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Hard-sets the frame this sprite is supposed to show, to the - // consecutive one after the current one. If currently the last fame is - // this will set it to the be the first, looping the animation. - bool MOSprite::SetNextFrame() { if (++m_Frame >= m_FrameCount) { m_Frame = 0; @@ -338,12 +256,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsOnScenePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this' current graphical representation overlaps - // a point in absolute scene coordinates. - bool MOSprite::IsOnScenePoint(Vector& scenePoint) const { if (!m_aSprite[m_Frame]) return false; @@ -405,35 +317,18 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: RotateOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Rotates a vector offset from this MORotating's position according to - // the rotate angle and flipping. - Vector MOSprite::RotateOffset(const Vector& offset) const { Vector rotOff(offset.GetXFlipped(m_HFlipped)); rotOff *= const_cast(m_Rotation); return rotOff; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UnRotateOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Rotates a vector offset from this MORotating's position according to - // the NEGATIVE rotate angle and takes flipping into account. - Vector MOSprite::UnRotateOffset(const Vector& offset) const { Vector rotOff(offset.GetXFlipped(m_HFlipped)); rotOff /= const_cast(m_Rotation); return rotOff; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure v. method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MOSprite. Supposed to be done every frame. - void MOSprite::Update() { MovableObject::Update(); @@ -490,12 +385,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this MOSprite's current graphical representation to a - // BITMAP of choice. - void MOSprite::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, diff --git a/Source/Entities/MOSprite.h b/Source/Entities/MOSprite.h index 7065f185ad..db82d0c387 100644 --- a/Source/Entities/MOSprite.h +++ b/Source/Entities/MOSprite.h @@ -1,18 +1,11 @@ #ifndef _RTEMOSPRITE_ #define _RTEMOSPRITE_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MOSprite.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the MOSprite class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the MOSprite class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "MovableObject.h" #include "Box.h" @@ -20,237 +13,127 @@ namespace RTE { class AEmitter; - ////////////////////////////////////////////////////////////////////////////////////////// - // Abstract class: MOSprite - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A movable object with mass that is graphically represented by a - // BITMAP. - // Parent(s): MovableObject. - // Class history: 03/18/2001 MOSprite created. - + /// A movable object with mass that is graphically represented by a + /// BITMAP. class MOSprite : public MovableObject { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MOSprite - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a MOSprite object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a MOSprite object in system + /// memory. Create() should be called before using the object. MOSprite() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~MOSprite - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a MOSprite object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a MOSprite object before deletion + /// from system memory. ~MOSprite() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MOSprite object ready for use. - // Arguments: A pointer to ContentFile that represents the bitmap file that will be - // used to create the Sprite. - // The number of frames in the Sprite's animation. - // A float specifying the object's mass in Kilograms (kg). - // A Vector specifying the initial position. - // A Vector specifying the initial velocity. - // The amount of time in ms this MovableObject will exist. 0 means unlim. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MOSprite object ready for use. + /// @param spriteFile A pointer to ContentFile that represents the bitmap file that will be + /// used to create the Sprite. + /// @param frameCount The number of frames in the Sprite's animation. (default: 1) + /// @param mass A float specifying the object's mass in Kilograms (kg). (default: 1) + /// @param position A Vector specifying the initial position. (default: Vector(0) + /// @param 0) A Vector specifying the initial velocity. + /// @param velocity The amount of time in ms this MovableObject will exist. 0 means unlim. (default: Vector(0) + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(ContentFile spriteFile, const int frameCount = 1, const float mass = 1, const Vector& position = Vector(0, 0), const Vector& velocity = Vector(0, 0), const unsigned long lifetime = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MOSprite to be identical to another, by deep copy. - // Arguments: A reference to the MOSprite to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a MOSprite to be identical to another, by deep copy. + /// @param reference A reference to the MOSprite to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const MOSprite& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MOSprite object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MOSprite object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire MOSprite, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire MOSprite, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); MovableObject::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MOSprite object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the MOSprite object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRadius - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the largest radius of this in pixels. - // Arguments: None. - // Return value: The radius from its center to the edge of its graphical representation. - + /// Gets the largest radius of this in pixels. + /// @return The radius from its center to the edge of its graphical representation. float GetRadius() const override { return m_SpriteRadius; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetDiameter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the largest diameter of this in pixels. - // Arguments: None. - // Return value: The largest diameter across its graphical representation. - + /// Gets the largest diameter of this in pixels. + /// @return The largest diameter across its graphical representation. float GetDiameter() const override { return m_SpriteDiameter; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAboveHUDPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of the top of this' HUD stack. - // Arguments: None. - // Return value: A Vector with the absolute position of this' HUD stack top point. - + /// Gets the absoltue position of the top of this' HUD stack. + /// @return A Vector with the absolute position of this' HUD stack top point. Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, -GetRadius()); } // TODO: Improve this one! Really crappy fit - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetBoundingBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the oriented bounding box which is guaranteed to contain this, - // taking rotation etc into account. It's not guaranteed to be fit - // perfectly though. TODO: MAKE FIT BETTER - // Arguments: None. - // Return value: A Box which is guaranteed to contain this. Does nto take wrapping into - // account, and parts of this box may be out of bounds! - + /// Gets the oriented bounding box which is guaranteed to contain this, + /// taking rotation etc into account. It's not guaranteed to be fit + /// perfectly though. TODO MAKE FIT BETTER + /// @return A Box which is guaranteed to contain this. Does nto take wrapping into + /// account, and parts of this box may be out of bounds! Box GetBoundingBox() const { return Box(m_Pos + Vector(-GetRadius(), -GetRadius()), GetDiameter(), GetDiameter()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpriteFrame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a frame of this MOSprite's BITMAP array. - // Arguments: Which frame to get. - // Return value: A pointer to the requested frame of this MOSprite's BITMAP array. - // Ownership is NOT transferred! - + /// Gets a frame of this MOSprite's BITMAP array. + /// @param whichFrame Which frame to get. (default: 0) + /// @return A pointer to the requested frame of this MOSprite's BITMAP array. + /// Ownership is NOT transferred! BITMAP* GetSpriteFrame(int whichFrame = 0) const { return (whichFrame >= 0 && whichFrame < m_FrameCount) ? m_aSprite[whichFrame] : 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpriteWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the width of the bitmap of this MOSprite - // Arguments: 0. - // Return value: Sprite width if loaded. - + /// Gets the width of the bitmap of this MOSprite + /// @return Sprite width if loaded. int GetSpriteWidth() const { return m_aSprite[0] ? m_aSprite[0]->w : 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpriteHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the height of the bitmap of this MOSprite - // Arguments: 0. - // Return value: Sprite height if loaded. - + /// Gets the height of the bitmap of this MOSprite + /// @return Sprite height if loaded. int GetSpriteHeight() const { return m_aSprite[0] ? m_aSprite[0]->h : 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFrameCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of frames in this MOSprite's animation. - // Arguments: None. - // Return value: The frame count. - + /// Gets the number of frames in this MOSprite's animation. + /// @return The frame count. int GetFrameCount() const { return m_FrameCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpriteOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the offset that the BITMAP has from the position of this - // MOSprite. - // Arguments: None. - // Return value: A vector with the offset. - + /// Gets the offset that the BITMAP has from the position of this + /// MOSprite. + /// @return A vector with the offset. Vector GetSpriteOffset() const { return m_SpriteOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsHFlipped - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether this MOSprite is being drawn flipped horizontally - // (along the vertical axis), or not. - // Arguments: None. - // Return value: Whether flipped or not. - + /// Returns whether this MOSprite is being drawn flipped horizontally + /// (along the vertical axis), or not. + /// @return Whether flipped or not. bool IsHFlipped() const override { return m_HFlipped; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRotMatrix - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current rotational Matrix of of this. - // Arguments: None. - // Return value: The rotational Matrix of this MovableObject. - + /// Gets the current rotational Matrix of of this. + /// @return The rotational Matrix of this MovableObject. Matrix GetRotMatrix() const override { return m_Rotation; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRotAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current rotational angle of of this, in radians. - // Arguments: None. - // Return value: The rotational angle of this, in radians. - + /// Gets the current rotational angle of of this, in radians. + /// @return The rotational angle of this, in radians. float GetRotAngle() const override { return m_Rotation.GetRadAngle(); } - /// /// Gets the previous rotational angle of this MOSprite, prior to this frame. - /// - /// The previous rotational angle in radians. + /// @return The previous rotational angle in radians. float GetPrevRotAngle() const { return m_PrevRotation.GetRadAngle(); } - /// /// Whether a set of X, Y coordinates overlap us (in world space). - /// - /// The given X coordinate, in world space. - /// The given Y coordinate, in world space. - /// Whether the given coordinate overlap us. + /// @param pixelX The given X coordinate, in world space. + /// @param pixelY The given Y coordinate, in world space. + /// @return Whether the given coordinate overlap us. bool HitTestAtPixel(int pixelX, int pixelY) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAngularVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current angular velocity of this MovableObject. Positive is - // a counter-clockwise rotation. - // Arguments: None. - // Return value: The angular velocity in radians per second. - + /// Gets the current angular velocity of this MovableObject. Positive is + /// a counter-clockwise rotation. + /// @return The angular velocity in radians per second. float GetAngularVel() const override { return m_AngularVel; } /* Can't do this since sprite is owned by ContentMan. @@ -264,136 +147,72 @@ namespace RTE { void SetSprite(BITMAP *pSprite) { delete m_aSprite; m_aSprite = pSprite; } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSpriteOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the offset that the BITMAP has from the position of this - // MOSprite. - // Arguments: A vector with the new offset. - // Return value: None. - + /// Sets the offset that the BITMAP has from the position of this + /// MOSprite. + /// @param newOffset A vector with the new offset. void SetSpriteOffset(const Vector& newOffset) { m_SpriteOffset = newOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFrame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Hard-sets the frame this sprite is supposed to show. - // Arguments: An unsigned int pecifiying the new frame. - // Return value: None. - + /// Hard-sets the frame this sprite is supposed to show. + /// @param newFrame An unsigned int pecifiying the new frame. void SetFrame(unsigned int newFrame); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetNextFrame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Hard-sets the frame this sprite is supposed to show, to the - // consecutive one after the current one. If currently the last fame is - // this will set it to the be the first, looping the animation. - // Arguments: None. - // Return value: Whether the animation looped or not with this setting. - + /// Hard-sets the frame this sprite is supposed to show, to the + /// consecutive one after the current one. If currently the last fame is + /// this will set it to the be the first, looping the animation. + /// @return Whether the animation looped or not with this setting. bool SetNextFrame(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFrame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells which frame is currently set to show. - // Arguments: None. - // Return value: An unsigned int describing the current frame. - + /// Tells which frame is currently set to show. + /// @return An unsigned int describing the current frame. unsigned int GetFrame() const { return m_Frame; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSpriteAnimMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the animation mode. - // Arguments: The animation mode we want to set. - // Return value: None. - + /// Sets the animation mode. + /// @param animMode The animation mode we want to set. (default: NOANIM) void SetSpriteAnimMode(int animMode = NOANIM) { m_SpriteAnimMode = (SpriteAnimMode)animMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSpriteAnimMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the animation mode. - // Arguments: None. - // Return value: The animation mode currently in effect. - + /// Gets the animation mode. + /// @return The animation mode currently in effect. int GetSpriteAnimMode() const { return m_SpriteAnimMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: SetHFlipped - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this MOSprite should be drawn flipped horizontally - // (along the vertical axis). - // Arguments: A bool with the new value. - // Return value: None. - + /// Sets whether this MOSprite should be drawn flipped horizontally + /// (along the vertical axis). + /// @param flipped A bool with the new value. void SetHFlipped(const bool flipped) override { m_HFlipped = flipped; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetRotAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current absolute angle of rotation of this MovableObject. - // Arguments: The new absolute angle in radians. - // Return value: None. - + /// Sets the current absolute angle of rotation of this MovableObject. + /// @param m_Rotation.SetRadAngle(newAngle The new absolute angle in radians. void SetRotAngle(float newAngle) override { m_Rotation.SetRadAngle(newAngle); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetAngularVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current angular velocity of this MovableObject. Positive is - // a counter clockwise rotation. - // Arguments: The new angular velocity in radians per second. - // Return value: None. - + /// Sets the current angular velocity of this MovableObject. Positive is + /// a counter clockwise rotation. + /// @param newRotVel The new angular velocity in radians per second. void SetAngularVel(float newRotVel) override { m_AngularVel = newRotVel; } - /// /// Gets the GUI representation of this MOSprite, either based on the first frame of its sprite or separately defined icon file. - /// - /// The graphical representation of this MOSprite as a BITMAP. + /// @return The graphical representation of this MOSprite as a BITMAP. BITMAP* GetGraphicalIcon() const override { return m_GraphicalIcon != nullptr ? m_GraphicalIcon : m_aSprite[0]; } - /// /// Gets the width of this MOSprite's GUI icon. - /// - /// The width of the GUI icon bitmap. + /// @return The width of the GUI icon bitmap. int GetIconWidth() const { return GetGraphicalIcon()->w; } - /// /// Gets the height of this MOSprite's GUI icon. - /// - /// The height of the GUI icon bitmap. + /// @return The height of the GUI icon bitmap. int GetIconHeight() const { return GetGraphicalIcon()->h; } - /// /// Forces this MOSprite out of resting conditions. - /// void NotResting() override { MovableObject::NotResting(); m_AngOscillations = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsTooFast - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is moving or rotating stupidly fast in a way - // that will screw up the simulation. - // Arguments: None. - // Return value: Whether this is either moving or rotating too fast. - + /// Indicates whether this MO is moving or rotating stupidly fast in a way + /// that will screw up the simulation. + /// @return Whether this is either moving or rotating too fast. bool IsTooFast() const override { return m_Vel.MagnitudeIsGreaterThan(500.0F) || std::fabs(m_AngularVel) > (2000.0F / (GetRadius() + 1.0F)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: FixTooFast - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Slows the speed of anything that is deemed to be too fast to within - // acceptable rates. - // Arguments: None. - // Return value: None. - + /// Slows the speed of anything that is deemed to be too fast to within + /// acceptable rates. void FixTooFast() override { while (IsTooFast()) { m_Vel *= 0.5F; @@ -401,134 +220,72 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsOnScenePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this' current graphical representation overlaps - // a point in absolute scene coordinates. - // Arguments: The point in absolute scene coordinates. - // Return value: Whether this' graphical rep overlaps the scene point. - + /// Indicates whether this' current graphical representation overlaps + /// a point in absolute scene coordinates. + /// @param scenePoint The point in absolute scene coordinates. + /// @return Whether this' graphical rep overlaps the scene point. bool IsOnScenePoint(Vector& scenePoint) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: RotateOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes a vector which is offset from the center of this when not rotated - // or flipped, and then rotates and/or flips it to transform it into this' - // 'local space', if applicable. - // Arguments: A vector which is supposed to be offset from this' center when upright. - // Return value: The resulting vector whihch has been flipped and rotated as appropriate. - + /// Takes a vector which is offset from the center of this when not rotated + /// or flipped, and then rotates and/or flips it to transform it into this' + /// 'local space', if applicable. + /// @param offset A vector which is supposed to be offset from this' center when upright. + /// @return The resulting vector whihch has been flipped and rotated as appropriate. Vector RotateOffset(const Vector& offset) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UnRotateOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes a vector which is offset from the center of this when not rotated - // or flipped, and then rotates and/or flips it to transform it into this' - // 'local space', but in REVERSE. - // Arguments: A vector which is supposed to be offset from this' center when upright. - // Return value: The resulting vector whihch has been flipped and rotated as appropriate. - + /// Takes a vector which is offset from the center of this when not rotated + /// or flipped, and then rotates and/or flips it to transform it into this' + /// 'local space', but in REVERSE. + /// @param offset A vector which is supposed to be offset from this' center when upright. + /// @return The resulting vector whihch has been flipped and rotated as appropriate. Vector UnRotateOffset(const Vector& offset) const; - /// /// Adjusts an absolute angle based on wether this MOSprite is flipped. - /// - /// The input angle in radians. - /// The output angle in radians, which will be unaltered if this MOSprite is not flipped. + /// @param angle The input angle in radians. + /// @return The output angle in radians, which will be unaltered if this MOSprite is not flipped. float FacingAngle(float angle) const { return (m_HFlipped ? c_PI : 0) + (angle * GetFlipFactor()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetEntryWound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets entry wound emitter for this MOSprite - // Arguments: Emitter preset name and module name - // Return value: None - + /// Sets entry wound emitter for this MOSprite + /// @param presetName Emitter preset name and module name void SetEntryWound(std::string presetName, std::string moduleName); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetExitWound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets exit wound emitter for this MOSprite - // Arguments: Emitter preset name and module name - // Return value: None - + /// Sets exit wound emitter for this MOSprite + /// @param presetName Emitter preset name and module name void SetExitWound(std::string presetName, std::string moduleName); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetEntryWoundPresetName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns entry wound emitter preset name for this MOSprite - // Arguments: None - // Return value: Wound emitter preset name - + /// Returns entry wound emitter preset name for this MOSprite + /// @return Wound emitter preset name std::string GetEntryWoundPresetName() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetExitWoundPresetName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns exit wound emitter preset name for this MOSprite - // Arguments: None - // Return value: Wound emitter preset name - + /// Returns exit wound emitter preset name for this MOSprite + /// @return Wound emitter preset name std::string GetExitWoundPresetName() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetSpriteAnimDuration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns animation duration in ms - // Arguments: None - // Return value: Animation duration in ms - + /// Returns animation duration in ms + /// @return Animation duration in ms int GetSpriteAnimDuration() const { return m_SpriteAnimDuration; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetSpriteAnimDuration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets animation duration in ms - // Arguments: Animation duration in ms - // Return value: Mone - + /// Sets animation duration in ms + /// @param newDuration Animation duration in ms void SetSpriteAnimDuration(int newDuration) { m_SpriteAnimDuration = newDuration; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this MOSprite's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this MOSprite's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetFlipFactor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a positive or negative number value to multiply with for external calculations. - // Arguments: None. - // Return value: 1 for not flipped, -1 for flipped. - + /// Returns a positive or negative number value to multiply with for external calculations. + /// @return 1 for not flipped, -1 for flipped. float GetFlipFactor() const { return m_HFlipped ? -1.0F : 1.0F; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -568,18 +325,10 @@ namespace RTE { // Exit wound template const AEmitter* m_pExitWound; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MOSprite, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this MOSprite, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Magazine.cpp b/Source/Entities/Magazine.cpp index 4e01bfc2bb..0e600bf28a 100644 --- a/Source/Entities/Magazine.cpp +++ b/Source/Entities/Magazine.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Magazine.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the Magazine class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Magazine.h" #include "PresetMan.h" #include "AEmitter.h" @@ -18,12 +6,6 @@ namespace RTE { ConcreteClassInfo(Magazine, Attachable, 50); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Magazine, effectively - // resetting the members of this abstraction level only. - void Magazine::Clear() { m_RoundCount = 0; m_FullCapacity = 0; @@ -40,11 +22,6 @@ namespace RTE { m_CollidesWithTerrainWhileAttached = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Magazine object ready for use. - int Magazine::Create() { if (Attachable::Create() < 0) return -1; @@ -72,11 +49,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Magazine to be identical to another, by deep copy. - int Magazine::Create(const Magazine& reference) { Attachable::Create(reference); @@ -93,14 +65,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int Magazine::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Attachable::ReadProperty(propName, reader)); @@ -117,12 +81,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this Magazine with a Writer for - // later recreation with Create(Reader &reader); - int Magazine::Save(Writer& writer) const { Attachable::Save(writer); @@ -142,11 +100,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the Magazine object. - void Magazine::Destroy(bool notInherited) { if (!notInherited) @@ -154,12 +107,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetNextRound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the next Round preset of ammo in this Magazine, without removing - // it. Ownership IS NOT transferred! - const Round* Magazine::GetNextRound() const { const Round* tempRound = 0; if (m_RoundCount != 0) { @@ -171,12 +118,6 @@ namespace RTE { return tempRound; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PopNextRound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the next Round of ammo in this Magazine, and removes it from the - // stack. Ownership IS transferred! - Round* Magazine::PopNextRound() { Round* tempRound = 0; if (m_RoundCount != 0) { @@ -191,11 +132,6 @@ namespace RTE { return tempRound; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EstimateDigStrength - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Estimates what material strength the rounds in the magazine can destroy. - float Magazine::EstimateDigStrength() const { float maxPenetration = 1; if (m_pTracerRound) { @@ -223,11 +159,6 @@ namespace RTE { return maxPenetration; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBulletAccScalar - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. - float Magazine::GetBulletAccScalar() { const Round* pRound = GetNextRound(); if (pRound) { @@ -239,11 +170,6 @@ namespace RTE { return 1; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this Magazine. Supposed to be done every frame. - void Magazine::Update() { Attachable::Update(); diff --git a/Source/Entities/Magazine.h b/Source/Entities/Magazine.h index 07e3362686..6668445d37 100644 --- a/Source/Entities/Magazine.h +++ b/Source/Entities/Magazine.h @@ -1,260 +1,132 @@ #ifndef _RTEMAGAZINE_ #define _RTEMAGAZINE_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Magazine.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the Magazine class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the Magazine class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Attachable.h" #include "Round.h" namespace RTE { - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: Magazine - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: An Attachable ammo magazine that can hold rounds that can be fired - // by HDFirearm:s. - // Parent(s): Attachable. - // Class history: 07/03/2002 Magazine created. - + /// An Attachable ammo magazine that can hold rounds that can be fired + /// by HDFirearm:s. class Magazine : public Attachable { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Concrete allocation and cloning definitions EntityAllocation(Magazine); SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Magazine - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Magazine object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a Magazine object in system + /// memory. Create() should be called before using the object. Magazine() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~Magazine - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a Magazine object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a Magazine object before deletion + /// from system memory. ~Magazine() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Magazine object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the Magazine object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Magazine to be identical to another, by deep copy. - // Arguments: A reference to the Magazine to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a Magazine to be identical to another, by deep copy. + /// @param reference A reference to the Magazine to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const Magazine& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Magazine, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire Magazine, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Attachable::Reset(); m_CollidesWithTerrainWhileAttached = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetNextRound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the next Round preset of ammo in this Magazine, without removing - // it. Ownership IS NOT transferred! - // Arguments: None. - // Return value: A pointer to the next Round preset of ammo, or 0 if this Magazine is empty. - + /// Gets the next Round preset of ammo in this Magazine, without removing + /// it. Ownership IS NOT transferred! + /// @return A pointer to the next Round preset of ammo, or 0 if this Magazine is empty. const Round* GetNextRound() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PopNextRound - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the next Round of ammo in this Magazine, and removes it from the - // stack. Ownership IS transferred! - // Arguments: None. - // Return value: A pointer to the next Round of ammo, or 0 if this Magazine is empty. - + /// Gets the next Round of ammo in this Magazine, and removes it from the + /// stack. Ownership IS transferred! + /// @return A pointer to the next Round of ammo, or 0 if this Magazine is empty. Round* PopNextRound(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRoundCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns how many rounds are left in this Magazine. - // Arguments: None. - // Return value: The number of rounds left. Negative value means infinite ammo left! - + /// Returns how many rounds are left in this Magazine. + /// @return The number of rounds left. Negative value means infinite ammo left! int GetRoundCount() const { return m_RoundCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetRoundCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets how many rounds are left in this Magazine. - // Arguments: The new number of rounds left. Negative value means infinite ammo! - // Return value: None. - + /// Sets how many rounds are left in this Magazine. + /// @param newCount The new number of rounds left. Negative value means infinite ammo! void SetRoundCount(int newCount) { m_RoundCount = newCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEmpty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether this Magazine is out of rounds. - // Arguments: None. - // Return value: Whether this Magazine is out of rounds or not. - + /// Returns whether this Magazine is out of rounds. + /// @return Whether this Magazine is out of rounds or not. bool IsEmpty() const { return m_FullCapacity >= 0 && m_RoundCount == 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsFull - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether this Magazine has not used up any rounds yet. - // Arguments: None. - // Return value: Whether this Magazine has not used any rounds yet. - + /// Returns whether this Magazine has not used up any rounds yet. + /// @return Whether this Magazine has not used any rounds yet. bool IsFull() const { return m_FullCapacity > 0 ? (m_RoundCount == m_FullCapacity || m_RoundCount < 0) : m_FullCapacity < 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsOverHalfFull - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether this Magazine has not used up half of the rounds yet. - // Arguments: None. - // Return value: Whether this Magazine has not used half of its rounds yet. - + /// Returns whether this Magazine has not used up half of the rounds yet. + /// @return Whether this Magazine has not used half of its rounds yet. bool IsOverHalfFull() const { return m_FullCapacity > 0 ? ((m_RoundCount > (m_FullCapacity / 2)) || m_RoundCount < 0) : m_FullCapacity < 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCapacity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns teh number of rounds this can hold when it's full. - // Arguments: None. - // Return value: The number of rounds this can hold. Negative value means infinite ammo. - + /// Returns teh number of rounds this can hold when it's full. + /// @return The number of rounds this can hold. Negative value means infinite ammo. int GetCapacity() const { return m_FullCapacity; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsDiscardable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Whether this Magazine should be released into the scene when discarded - // or just deleted. - // Arguments: None. - // Return value: Whether this Magazine should be relesed into scene or deleted when released. - + /// Whether this Magazine should be released into the scene when discarded + /// or just deleted. + /// @return Whether this Magazine should be relesed into scene or deleted when released. bool IsDiscardable() const { return m_Discardable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EstimateDigStrength - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Estimates what material strength the rounds in the magazine can destroy. - // Arguments: None. - // Return value: The material strength. - + /// Estimates what material strength the rounds in the magazine can destroy. + /// @return The material strength. float EstimateDigStrength() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAimVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells the AI what muzzle velocity to assume when aiming this weapon. - // Arguments: None. - // Return value: Velocity in m/s. - + /// Tells the AI what muzzle velocity to assume when aiming this weapon. + /// @return Velocity in m/s. float GetAIAimVel() const { return m_AIAimVel; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIAimBlastRadius - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells the AI what distance in pixels from the rounds in this mag round - // are mostly safe. - // Arguments: None. - // Return value: Distance in pixels. - + /// Tells the AI what distance in pixels from the rounds in this mag round + /// are mostly safe. + /// @return Distance in pixels. int GetAIAimBlastRadius() const { return m_AIBlastRadius; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAIAimPenetration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells the AI how much material this projectile can penetrate. - // Arguments: None. - // Return value: The material strenght. - + /// Tells the AI how much material this projectile can penetrate. + /// @return The material strenght. float GetAIAimPenetration() const { return m_AIAimPenetration; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBulletAccScalar - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the bullet acceleration scalar the AI use when aiming this weapon. - // Arguments: None. - // Return value: A float with the scalar. - + /// Gets the bullet acceleration scalar the AI use when aiming this weapon. + /// @return A float with the scalar. float GetBulletAccScalar(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this Magazine's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this Magazine's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -280,18 +152,10 @@ namespace RTE { // Tells the AI what distance in pixels from this round is mostly safe. int m_AIBlastRadius; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Magazine, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Magazine, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Material.cpp b/Source/Entities/Material.cpp index 0af3300309..7fa649ac08 100644 --- a/Source/Entities/Material.cpp +++ b/Source/Entities/Material.cpp @@ -5,8 +5,6 @@ namespace RTE { ConcreteClassInfo(Material, Entity, 0); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Material::Clear() { m_Index = 0; m_Priority = -1; @@ -30,8 +28,6 @@ namespace RTE { m_TerrainBGTexture = nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Material::Create(const Material& reference) { Entity::Create(reference); @@ -59,8 +55,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Material::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -106,8 +100,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Material::Save(Writer& writer) const { Entity::Save(writer); // Materials should never be altered, so no point in saving additional properties when it's a copy diff --git a/Source/Entities/Material.h b/Source/Entities/Material.h index 6e37a692af..9cf8bde994 100644 --- a/Source/Entities/Material.h +++ b/Source/Entities/Material.h @@ -7,9 +7,7 @@ namespace RTE { - /// /// Represents a material and holds all the relevant data. - /// class Material : public Entity { public: @@ -18,15 +16,11 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a Material object in system memory. Create() should be called before using the object. - /// Material() { Clear(); } - /// /// Copy constructor method used to instantiate a Material object identical to an already existing one. - /// - /// A Material object which is passed in by reference. + /// @param reference A Material object which is passed in by reference. Material(const Material& reference) { if (this != &reference) { Clear(); @@ -34,18 +28,14 @@ namespace RTE { } } - /// /// Creates a Material to be identical to another, by deep copy. - /// - /// A reference to the Material to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Material to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Material& reference); #pragma endregion #pragma region Destruction - /// /// Resets the entire Material, including its inherited members, to it's default settings or values. - /// void Reset() override { Clear(); Entity::Reset(); @@ -53,115 +43,79 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the foreground texture bitmap of this Material, if any is associated with it. - /// - /// Pointer to the foreground texture bitmap of this Material. + /// @return Pointer to the foreground texture bitmap of this Material. BITMAP* GetFGTexture() const { return m_TerrainFGTexture; } - /// /// Gets the background texture bitmap of this Material, if any is associated with it. - /// - /// Pointer to the background texture bitmap of this Material. + /// @return Pointer to the background texture bitmap of this Material. BITMAP* GetBGTexture() const { return m_TerrainBGTexture; } - /// /// Gets the index of this Material in the material palette. - /// - /// The index of this Material in the material palette. 0 - 255. + /// @return The index of this Material in the material palette. 0 - 255. unsigned char GetIndex() const { return m_Index; } - /// /// Sets the index of this Material in the material palette to the next specified value. - /// - /// The new index of this Material in the material palette. 0 - 255. + /// @param newIndex The new index of this Material in the material palette. 0 - 255. void SetIndex(unsigned char newIndex) { m_Index = newIndex; } - /// /// Gets the drawing priority of this Material. The higher the number, the higher chances that a pixel of this material will be drawn on top of others. Will default to Integrity if no Priority has been defined. - /// - /// The drawing priority of this Material. + /// @return The drawing priority of this Material. int GetPriority() const { return m_Priority < 0 ? static_cast(std::ceil(m_Integrity)) : m_Priority; } - /// /// Gets the amount of times a dislodged pixel of this Material will attempt to relocate to an open position. - /// - /// The amount of attempts at relocating. + /// @return The amount of attempts at relocating. int GetPiling() const { return m_Piling; } - /// /// The impulse force that a particle needs to knock loose a terrain pixel of this material. In kg * m/s. - /// - /// The impulse force that a particle needs to knock loose a terrain pixel of this material. + /// @return The impulse force that a particle needs to knock loose a terrain pixel of this material. float GetIntegrity() const { return m_Integrity; } - /// /// Gets the scalar value that defines the restitution of this Material. 1.0 = no kinetic energy is lost in a collision, 0.0 = all energy is lost (plastic). - /// - /// A float scalar value that defines the restitution of this Material. + /// @return A float scalar value that defines the restitution of this Material. float GetRestitution() const { return m_Restitution; } - /// /// Gets the scalar value that defines the friction of this Material. 1.0 = will snag onto everything, 0.0 = will glide with no friction. - /// - /// A float scalar value that defines the friction of this Material. + /// @return A float scalar value that defines the friction of this Material. float GetFriction() const { return m_Friction; } - /// /// Gets the scalar value that defines the stickiness of this Material. 1.0 = will stick to everything, 0.0 = will never stick to anything. - /// - /// A float scalar value that defines the stickiness of this Material. + /// @return A float scalar value that defines the stickiness of this Material. float GetStickiness() const { return m_Stickiness; } - /// /// Gets the density of this Material in Kg/L. - /// - /// The density of this Material. + /// @return The density of this Material. float GetVolumeDensity() const { return m_VolumeDensity; } - /// /// Gets the density of this Material in kg/pixel, usually calculated from the KG per Volume L property. - /// - /// The pixel density of this Material. + /// @return The pixel density of this Material. float GetPixelDensity() const { return m_PixelDensity; } - /// /// If this material transforms into something else when settling into the terrain, this will return that different material index. If not, it will just return the regular index of this material. - /// - /// The settling material index of this or the regular index. + /// @return The settling material index of this or the regular index. unsigned char GetSettleMaterial() const { return (m_SettleMaterialIndex != 0) ? m_SettleMaterialIndex : m_Index; } - /// /// Gets the material index to spawn instead of this one for special effects. - /// - /// The material index to spawn instead of this one for special effects. 0 means to spawn the same material as this. + /// @return The material index to spawn instead of this one for special effects. 0 means to spawn the same material as this. unsigned char GetSpawnMaterial() const { return m_SpawnMaterialIndex; } - /// /// Whether this material is scrap material made from gibs of things that have already been blown apart. - /// - /// Whether this material is scrap material. + /// @return Whether this material is scrap material. bool IsScrap() const { return m_IsScrap; } - /// /// Gets the Color of this Material. - /// - /// The color of this material. + /// @return The color of this material. Color GetColor() const { return m_Color; } - /// /// Indicates whether or not to use the Material's own color when a pixel of this Material is knocked loose from the terrain. - /// - /// Whether the Material's color, or the terrain pixel's color should be applied. + /// @return Whether the Material's color, or the terrain pixel's color should be applied. bool UsesOwnColor() const { return m_UseOwnColor; } #pragma endregion #pragma region Operator Overloads - /// /// An assignment operator for setting one Material equal to another. - /// - /// A Material reference. - /// A reference to the changed Material. + /// @param rhs A Material reference. + /// @return A reference to the changed Material. Material& operator=(const Material& rhs) { if (this != &rhs) { Destroy(); @@ -203,9 +157,7 @@ namespace RTE { BITMAP* m_TerrainBGTexture; //!< The background texture of this Material, used when building an SLTerrain. Not owned. private: - /// /// Clears all the member variables of this Material, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/Entities/MetaPlayer.cpp b/Source/Entities/MetaPlayer.cpp index 9213499cb5..bf6fd59ae7 100644 --- a/Source/Entities/MetaPlayer.cpp +++ b/Source/Entities/MetaPlayer.cpp @@ -7,8 +7,6 @@ namespace RTE { ConcreteClassInfo(MetaPlayer, Entity, 0); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MetaPlayer::Clear() { m_Name = ""; m_Team = Activity::NoTeam; @@ -30,8 +28,6 @@ namespace RTE { m_OffensiveTarget = ""; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaPlayer::Create(const MetaPlayer& reference) { Entity::Create(reference); @@ -54,8 +50,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaPlayer::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -85,8 +79,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaPlayer::Save(Writer& writer) const { Entity::Save(writer); diff --git a/Source/Entities/MetaPlayer.h b/Source/Entities/MetaPlayer.h index 54f7d6f110..f9eaf58602 100644 --- a/Source/Entities/MetaPlayer.h +++ b/Source/Entities/MetaPlayer.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// Holds data for a Metagame player aka "Tech" or "House". - /// class MetaPlayer : public Entity { friend class MetagameGUI; @@ -17,15 +15,11 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a MetaPlayer object in system memory. Create() should be called before using the object. - /// MetaPlayer() { Clear(); } - /// /// Copy constructor method used to instantiate a MetaPlayer object identical to an already existing one. - /// - /// A MetaPlayer object which is passed in by reference. + /// @param reference A MetaPlayer object which is passed in by reference. MetaPlayer(const MetaPlayer& reference) { if (this != &reference) { Clear(); @@ -33,30 +27,22 @@ namespace RTE { } } - /// /// Makes the MetaPlayer object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override { return Entity::Create(); } - /// /// Creates a MetaPlayer to be identical to another, by deep copy. - /// - /// A reference to the MetaPlayer to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the MetaPlayer to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const MetaPlayer& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a MetaPlayer object before deletion from system memory. - /// ~MetaPlayer() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the MetaPlayer object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); @@ -64,9 +50,7 @@ namespace RTE { Clear(); } - /// /// Resets the entire MetaPlayer, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Entity::Reset(); @@ -74,207 +58,145 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the name of the MetaPlayer. - /// - /// The name of the player. + /// @return The name of the player. std::string GetName() const { return m_Name; } - /// /// Sets the name of the MetaPlayer. - /// - /// The new name to set. + /// @param newName The new name to set. void SetName(std::string newName) { m_Name = newName; } - /// /// Gets the Team of this MetaPlayer. - /// - /// The Team of this player. + /// @return The Team of this player. int GetTeam() const { return m_Team; } - /// /// Sets the Team of the MetaPlayer. - /// - /// The new team to set. + /// @param newTeam The new team to set. void SetTeam(int newTeam) { m_Team = newTeam; } - /// /// Indicates whether this MetaPlayer is human controlled or not (AI). - /// - /// Whether this player is human controlled. + /// @return Whether this player is human controlled. bool IsHuman() const { return m_Human; } - /// /// Sets whether this MetaPlayer is human controlled or not (AI). - /// - /// Whether this MetaPlayer is human controlled or not. + /// @param human Whether this MetaPlayer is human controlled or not. void SetHuman(bool human) { m_Human = human; } - /// /// Shows which in-game player controls this MetaPlayer is mapped to. - /// - /// The in-game player number this is mapped to. + /// @return The in-game player number this is mapped to. int GetInGamePlayer() const { return m_InGamePlayer; } - /// /// Gets ID of the DataModule that this MetaPlayer is native to. - /// - /// The ID of the DataModule this is native to. + /// @return The ID of the DataModule this is native to. int GetNativeTechModule() const { return m_NativeTechModule; } - /// /// Gets the normalized aggressiveness scalar of this player if an AI. - /// - /// The current aggressiveness scalar, 0 min to 1.0 max. + /// @return The current aggressiveness scalar, 0 min to 1.0 max. float GetAggressiveness() const { return m_Aggressiveness; } - /// /// Sets the normalized aggressiveness scalar of this player if an AI. - /// - /// The new aggressiveness scalar, 0 min to 1.0 max. + /// @param aggressiveness The new aggressiveness scalar, 0 min to 1.0 max. void SetAggressiveness(float aggressiveness) { m_Aggressiveness = aggressiveness; } - /// /// Indicates which round this MetaPlayer made it to. If negative, he is still in the game. - /// - /// Which round the MetaPlayer made it to. + /// @return Which round the MetaPlayer made it to. int GetGameOverRound() const { return m_GameOverRound; } - /// /// Sets which round this MetaPlayer lost out on. If set to negative, it means he's still in the game. - /// - /// The round the MetaPlayer lost out on. + /// @param gameOverRound The round the MetaPlayer lost out on. void SetGameOverRound(int gameOverRound) { m_GameOverRound = gameOverRound; } - /// /// Tells whether this MetaPlayer is out of the game on or before a particular round of the current metagame. - /// - /// Which round to check against. - /// Whether the MetaPlayer was flagged as being out of the game on that or any earlier round. + /// @param whichRound Which round to check against. + /// @return Whether the MetaPlayer was flagged as being out of the game on that or any earlier round. bool IsGameOverByRound(int whichRound) const { return m_GameOverRound >= 0 && m_GameOverRound <= whichRound; } - /// /// Gets the name of the scene this MetaPlayer is targeting for offensive. - /// - /// The name of the Scene this MetaPlayer is targeting. + /// @return The name of the Scene this MetaPlayer is targeting. std::string GetOffensiveTargetName() const { return m_OffensiveTarget; } - /// /// Sets the name of the scene this MetaPlayer is targeting for offensive. - /// - /// The name of the Scene this MetaPlayer is targeting. + /// @param targetName The name of the Scene this MetaPlayer is targeting. void SetOffensiveTargetName(std::string targetName) { m_OffensiveTarget = targetName; } #pragma endregion #pragma region Funds and Costs - /// /// Gets the amount of funds this MetaPlayer currently has in the game. - /// - /// A float with the funds tally for the requested MetaPlayer. + /// @return A float with the funds tally for the requested MetaPlayer. float GetFunds() const { return m_Funds; } - /// /// Sets the amount of funds this MetaPlayer currently has in the game. - /// - /// The new funds value for this MetaPlayer. + /// @param newFunds The new funds value for this MetaPlayer. void SetFunds(float newFunds) { m_Funds = newFunds; } - /// /// Changes this MetaPlayer's funds level by a certain amount. - /// - /// The amount with which to change the funds balance. - /// The new amount of funds of this MetaPlayer. + /// @param howMuch The amount with which to change the funds balance. + /// @return The new amount of funds of this MetaPlayer. float ChangeFunds(float howMuch) { return m_Funds += howMuch; } - /// /// Decreases this player's funds level by a certain absolute amount, and returns that difference as a positive value. /// If the amount isn't available to spend in the funds, the rest of the funds are spent and only that amount is returned. - /// - /// The amount with which to change the funds balance. This should be a positive value to decrease the funds amount. - /// The amount of funds that were spent. + /// @param howMuch The amount with which to change the funds balance. This should be a positive value to decrease the funds amount. + /// @return The amount of funds that were spent. float SpendFunds(float howMuch) { howMuch = std::min(m_Funds, howMuch); m_Funds -= howMuch; return howMuch; } - /// /// Gets the offensive budget of this MetaPlayer for this round, in oz. - /// - /// The offensive budget, in oz. + /// @return The offensive budget, in oz. float GetOffensiveBudget() const { return m_OffensiveBudget; } - /// /// Sets the offensive budget of this MetaPlayer for this round, in oz. - /// - /// The new offensive budget, in oz. + /// @param newBudget The new offensive budget, in oz. void SetOffensiveBudget(float newBudget) { m_OffensiveBudget = newBudget; } - /// /// Gets the multiplier of costs of any Tech items foreign to this MetaPlayer. - /// - /// The scalar multiplier of all costs of foreign tech items. + /// @return The scalar multiplier of all costs of foreign tech items. float GetForeignCostMultiplier() const { return m_ForeignCostMult; } - /// /// Gets the multiplier of costs of any Tech items native to this MetaPlayer. - /// - /// The scalar multiplier of all costs of native tech items. + /// @return The scalar multiplier of all costs of native tech items. float GetNativeCostMultiplier() const { return m_NativeCostMult; } - /// /// Sets the multiplier of costs of any Tech items native to this MetaPlayer. - /// - /// The scalar multiplier of all costs of native tech items. + /// @param newNativeCostMult The scalar multiplier of all costs of native tech items. void SetNativeCostMultiplier(float newNativeCostMult) { m_NativeCostMult = newNativeCostMult; } #pragma endregion #pragma region Brain Pool - /// /// Gets the number of brains in this MetaPlayer's brain pool. - /// - /// The number of brains that are available for deployment. + /// @return The number of brains that are available for deployment. int GetBrainPoolCount() const { return m_BrainPool; } - /// /// Sets the number of brains in this MetaPlayer's brain pool. - /// - /// The number of brains that should be available for deployment. + /// @param brainCount The number of brains that should be available for deployment. void SetBrainPoolCount(int brainCount) { m_BrainPool = brainCount; } - /// /// Alters the number of brains in this MetaPlayer's brain pool. - /// - /// The number of brains to add or remove from the pool. - /// The resulting count after the alteration. + /// @param change The number of brains to add or remove from the pool. + /// @return The resulting count after the alteration. int ChangeBrainPoolCount(int change) { return m_BrainPool += change; } - /// /// Gets the number of brains in this MetaPlayer that are out on the move between the pool and sites. - /// - /// The number of brains that are out in transit. + /// @return The number of brains that are out in transit. int GetBrainsInTransit() const { return m_BrainsInTransit; } - /// /// Sets the number of brains in this MetaPlayer that are out on the move between the pool and sites. - /// - /// The number of brains that are out in transit. + /// @param transitCount The number of brains that are out in transit. void SetBrainsInTransit(int transitCount) { m_BrainsInTransit = transitCount; } - /// /// Alters the number of brains of this MetaPlayer which are traveling. - /// - /// The number of brains to add or remove from transit. - /// The resulting count after the alteration. + /// @param change The number of brains to add or remove from transit. + /// @return The resulting count after the alteration. int ChangeBrainsInTransit(int change) { return m_BrainsInTransit += change; } #pragma endregion #pragma region Operator Overloads - /// /// An assignment operator for setting one MetaPlayer equal to another. - /// - /// A MetaPlayer reference. - /// A reference to the changed MetaPlayer. + /// @param rhs A MetaPlayer reference. + /// @return A reference to the changed MetaPlayer. MetaPlayer& operator=(const MetaPlayer& rhs) { if (this != &rhs) { Destroy(); @@ -310,9 +232,7 @@ namespace RTE { std::string m_OffensiveTarget; //!< Name of the Scene this player is targeting for its offensive this round. private: - /// /// Clears all the member variables of this MetaPlayer, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/Entities/MetaSave.cpp b/Source/Entities/MetaSave.cpp index 241b246070..83579a890c 100644 --- a/Source/Entities/MetaSave.cpp +++ b/Source/Entities/MetaSave.cpp @@ -7,8 +7,6 @@ namespace RTE { ConcreteClassInfo(MetaSave, Entity, 0); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MetaSave::Clear() { m_SavePath.clear(); m_PlayerCount = 0; @@ -17,8 +15,6 @@ namespace RTE { m_SiteCount = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaSave::Create(std::string savePath) { if (Entity::Create() < 0) { return -1; @@ -37,8 +33,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaSave::Create(const MetaSave& reference) { Entity::Create(reference); @@ -51,8 +45,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaSave::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -65,8 +57,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MetaSave::Save(Writer& writer) const { Entity::Save(writer); diff --git a/Source/Entities/MetaSave.h b/Source/Entities/MetaSave.h index 02b03f97b8..a28da5f8ff 100644 --- a/Source/Entities/MetaSave.h +++ b/Source/Entities/MetaSave.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// A light-weight Entity for storing only the necessary info about how to load an entire MetaMan state from disk. - /// class MetaSave : public Entity { public: @@ -16,36 +14,26 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a MetaSave object in system memory. Create() should be called before using the object. - /// MetaSave() { Clear(); } - /// /// Makes the MetaSave object ready for use from the currently loaded MetaMan state. - /// - /// The path of the file to where the MetaMan state should be saved. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param savePath The path of the file to where the MetaMan state should be saved. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(std::string savePath); - /// /// Creates a MetaSave to be identical to another, by deep copy. - /// - /// A reference to the MetaSave to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the MetaSave to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const MetaSave& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a MetaSave object before deletion from system memory. - /// ~MetaSave() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the MetaSave object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); @@ -55,34 +43,24 @@ namespace RTE { #pragma endregion #pragma region Getters - /// /// Gets the full path to the ini file that stores the state of the MetaMan this is associated with. - /// - /// The path to the ini with the MetaMan state info. + /// @return The path to the ini with the MetaMan state info. std::string GetSavePath() const { return m_SavePath; } - /// /// Gets the total number of players this game has (including AIs). - /// - /// The player count. + /// @return The player count. int GetPlayerCount() const { return m_PlayerCount; } - /// /// Gets the difficulty for this game. - /// - /// Difficulty setting. + /// @return Difficulty setting. int GetDifficulty() const { return m_Difficulty; } - /// /// Gets the round number that this game is on. - /// - /// The round count. + /// @return The round count. int GetRoundCount() const { return m_RoundCount; } - /// /// Gets the total number of Scenes this game has. - /// - /// The number of Scenes. + /// @return The number of Scenes. int GetSiteCount() const { return m_SiteCount; } #pragma endregion @@ -96,9 +74,7 @@ namespace RTE { int m_SiteCount; //!< The site count of this game. private: - /// /// Clears all the member variables of this MetaSave, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/MovableObject.cpp b/Source/Entities/MovableObject.cpp index 642f381372..86315d9491 100644 --- a/Source/Entities/MovableObject.cpp +++ b/Source/Entities/MovableObject.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MovableObject.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the MovableObject class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MovableObject.h" #include "ActivityMan.h" @@ -32,12 +20,6 @@ namespace RTE { std::atomic MovableObject::m_UniqueIDCounter = 1; std::string MovableObject::ms_EmptyString = ""; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MovableObject, effectively - // resetting the members of this abstraction level only. - void MovableObject::Clear() { m_MOType = TypeGeneric; m_Mass = 0; @@ -140,11 +122,6 @@ namespace RTE { return *m_ThreadedLuaState; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MovableObject object ready for use. - int MovableObject::Create() { if (SceneObject::Create() < 0) return -1; @@ -167,11 +144,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MovableObject object ready for use. - int MovableObject::Create(const float mass, const Vector& position, const Vector& velocity, @@ -201,11 +173,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MovableObject to be identical to another, by deep copy. - int MovableObject::Create(const MovableObject& reference) { SceneObject::Create(reference); @@ -289,14 +256,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int MovableObject::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneObject::ReadProperty(propName, reader)); @@ -419,12 +378,6 @@ namespace RTE { reader.NextProperty(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this MovableObject to an output stream for - // later recreation with Create(istream &stream); - int MovableObject::Save(Writer& writer) const { SceneObject::Save(writer); // TODO: Make proper save system that knows not to save redundant data! @@ -518,8 +471,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::DestroyScriptState() { if (m_ThreadedLuaState) { std::lock_guard lock(m_ThreadedLuaState->GetMutex()); @@ -535,8 +486,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::Destroy(bool notInherited) { // Unfortunately, shit can still get destroyed at random from Lua states having ownership and their GC deciding to delete it. // This skips the DestroyScriptState call... so there's leftover stale script state that we just can't do shit about. @@ -556,8 +505,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableObject::LoadScript(const std::string& scriptPath, bool loadAsEnabledScript) { if (scriptPath.empty()) { return -1; @@ -598,8 +545,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableObject::ReloadScripts() { if (m_AllLoadedScripts.empty()) { return 0; @@ -629,8 +574,6 @@ namespace RTE { return status; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableObject::InitializeObjectScripts() { std::lock_guard lock(m_ThreadedLuaState->GetMutex()); m_ScriptObjectName = "_ScriptedObjects[\"" + std::to_string(m_UniqueID) + "\"]"; @@ -648,8 +591,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MovableObject::EnableOrDisableScript(const std::string& scriptPath, bool enableScript) { if (m_AllLoadedScripts.empty()) { return false; @@ -676,8 +617,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::EnableOrDisableAllScripts(bool enableScripts) { for (const auto& [scriptPath, scriptIsEnabled]: m_AllLoadedScripts) { if (enableScripts != scriptIsEnabled) { @@ -686,8 +625,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableObject::RunScriptedFunctionInAppropriateScripts(const std::string& functionName, bool runOnDisabledScripts, bool stopOnError, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { int status = 0; @@ -718,8 +655,6 @@ namespace RTE { return status; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableObject::RunFunctionOfScript(const std::string& scriptPath, const std::string& functionName, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments) { if (m_AllLoadedScripts.empty() || !ObjectScriptsInitialized()) { return -1; @@ -741,8 +676,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /* ////////////////////////////////////////////////////////////////////////////////////////// // Constructor: MovableObject @@ -768,24 +701,14 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAltitude - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the altitide of this' pos (or appropriate low point) over the - // terrain, in pixels. - float MovableObject::GetAltitude(int max, int accuracy) { return g_SceneMan.FindAltitude(m_Pos, max, accuracy); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::AddAbsForce(const Vector& force, const Vector& absPos) { m_Forces.push_back(std::make_pair(force, g_SceneMan.ShortestDistance(m_Pos, absPos) * c_MPP)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::AddAbsImpulseForce(const Vector& impulse, const Vector& absPos) { #ifndef RELEASE_BUILD RTEAssert(impulse.GetLargest() < 500000, "HUEG IMPULSE FORCE"); @@ -794,8 +717,6 @@ namespace RTE { m_ImpulseForces.push_back(std::make_pair(impulse, g_SceneMan.ShortestDistance(m_Pos, absPos) * c_MPP)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::RestDetection() { // Translational settling detection. if (m_Vel.Dot(m_PrevVel) < 0) { @@ -808,8 +729,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MovableObject::IsAtRest() { if (m_RestThreshold < 0 || m_PinStrength) { return false; @@ -821,13 +740,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: OnMOHit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Defines what should happen when this MovableObject hits another MO. - // This is called by the owned Atom/AtomGroup of this MovableObject during - // travel. - bool MovableObject::OnMOHit(HitData& hd) { if (hd.RootBody[HITOR] != hd.RootBody[HITEE] && (hd.Body[HITOR] == this || hd.Body[HITEE] == this)) { RunScriptedFunctionInAppropriateScripts("OnCollideWithMO", false, false, {hd.Body[hd.Body[HITOR] == this ? HITEE : HITOR], hd.RootBody[hd.Body[HITOR] == this ? HITEE : HITOR]}); @@ -845,8 +757,6 @@ namespace RTE { RunScriptedFunctionInAppropriateScripts("OnCollideWithTerrain", false, false, {}, {std::to_string(m_TerrainMatHit)}); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector MovableObject::GetTotalForce() { Vector totalForceVector; for (const auto& [force, forceOffset]: m_Forces) { @@ -855,13 +765,6 @@ namespace RTE { return totalForceVector; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ApplyForces - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gathers and applies the global and accumulated forces. Then it clears - // out the force list.Note that this does NOT apply the accumulated - // impulses (impulse forces)! - void MovableObject::ApplyForces() { // Don't apply forces to pinned objects if (m_PinStrength > 0) { @@ -890,13 +793,6 @@ namespace RTE { m_Forces.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ApplyImpulses - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gathers and applies the accumulated impulse forces. Then it clears - // out the impulse list.Note that this does NOT apply the accumulated - // regular forces (non-impulse forces)! - void MovableObject::ApplyImpulses() { // Don't apply forces to pinned objects if (m_PinStrength > 0) { @@ -917,12 +813,6 @@ namespace RTE { m_ImpulseForces.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreTravel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does stuff that needs to be done before Travel(). Always call before - // calling Travel. - void MovableObject::PreTravel() { // Temporarily remove the representation of this from the scene MO sampler if (m_GetsHitByMOs) { @@ -943,20 +833,9 @@ namespace RTE { m_ParticleUniqueIDHit = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Travel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Travels this MovableObject, using its physical representation. - void MovableObject::Travel() { } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PostTravel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does stuff that needs to be done after Travel(). Always call after - // calling Travel. - void MovableObject::PostTravel() { // Toggle whether this gets hit by other AtomGroup MOs depending on whether it's going slower than a set threshold if (m_IgnoresAGHitsWhenSlowerThan > 0) { @@ -1000,16 +879,12 @@ namespace RTE { m_DistanceTravelled += m_Vel.GetMagnitude() * c_PPM * g_TimerMan.GetDeltaTimeSecs(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::Update() { if (m_RandomizeEffectRotAngleEveryFrame) { m_EffectRotAngle = c_PI * 2.0F * RandomNormalNum(); } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { if (mode == g_DrawMOID && m_MOID == g_NoMOID) { return; @@ -1018,8 +893,6 @@ namespace RTE { g_SceneMan.RegisterDrawing(targetBitmap, mode == g_DrawNoMOID ? g_NoMOID : m_MOID, m_Pos - targetPos, 1.0F); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableObject::UpdateScripts() { m_SimUpdatesSinceLastScriptedUpdate++; @@ -1045,8 +918,6 @@ namespace RTE { return status; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::string& MovableObject::GetStringValue(const std::string& key) const { auto itr = m_StringValueMap.find(key); if (itr == m_StringValueMap.end()) { @@ -1056,8 +927,6 @@ namespace RTE { return itr->second; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string MovableObject::GetEncodedStringValue(const std::string& key) const { auto itr = m_StringValueMap.find(key); if (itr == m_StringValueMap.end()) { @@ -1067,8 +936,6 @@ namespace RTE { return base64_decode(itr->second); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - double MovableObject::GetNumberValue(const std::string& key) const { auto itr = m_NumberValueMap.find(key); if (itr == m_NumberValueMap.end()) { @@ -1078,8 +945,6 @@ namespace RTE { return itr->second; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Entity* MovableObject::GetObjectValue(const std::string& key) const { auto itr = m_ObjectValueMap.find(key); if (itr == m_ObjectValueMap.end()) { @@ -1089,77 +954,50 @@ namespace RTE { return itr->second; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::SetStringValue(const std::string& key, const std::string& value) { m_StringValueMap[key] = value; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::SetEncodedStringValue(const std::string& key, const std::string& value) { m_StringValueMap[key] = base64_encode(value, true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::SetNumberValue(const std::string& key, double value) { m_NumberValueMap[key] = value; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::SetObjectValue(const std::string& key, Entity* value) { m_ObjectValueMap[key] = value; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::RemoveStringValue(const std::string& key) { m_StringValueMap.erase(key); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::RemoveNumberValue(const std::string& key) { m_NumberValueMap.erase(key); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableObject::RemoveObjectValue(const std::string& key) { m_ObjectValueMap.erase(key); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MovableObject::StringValueExists(const std::string& key) const { return m_StringValueMap.find(key) != m_StringValueMap.end(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MovableObject::NumberValueExists(const std::string& key) const { return m_NumberValueMap.find(key) != m_NumberValueMap.end(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MovableObject::ObjectValueExists(const std::string& key) const { return m_ObjectValueMap.find(key) != m_ObjectValueMap.end(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableObject::WhilePieMenuOpenListener(const PieMenu* pieMenu) { return RunScriptedFunctionInAppropriateScripts("WhilePieMenuOpen", false, false, {pieMenu}); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this' and its childrens MOID status. Supposed to be done every frame. - void MovableObject::UpdateMOID(std::vector& MOIDIndex, MOID rootMOID, bool makeNewMOID) { // Register the own MOID RegMOID(MOIDIndex, rootMOID, makeNewMOID); @@ -1171,11 +1009,6 @@ namespace RTE { m_MOIDFootprint = MOIDIndex.size() - m_MOID; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector - void MovableObject::GetMOIDs(std::vector& MOIDs) const { if (m_MOID != g_NoMOID) { MOIDs.push_back(m_MOID); @@ -1200,13 +1033,6 @@ namespace RTE { m_LastCollisionSimFrameNumber = g_MovableMan.GetSimUpdateFrameNumber(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: RegMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes this MO register itself in the MOID register and get ID:s for - // itself and its children for this frame. - // BITMAP of choice. - void MovableObject::RegMOID(std::vector& MOIDIndex, MOID rootMOID, bool makeNewMOID) { if (!makeNewMOID && GetParent()) { m_MOID = GetParent()->GetID(); @@ -1222,8 +1048,6 @@ namespace RTE { m_RootMOID = rootMOID == g_NoMOID ? m_MOID : rootMOID; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MovableObject::DrawToTerrain(SLTerrain* terrain) { if (!terrain) { return false; diff --git a/Source/Entities/MovableObject.h b/Source/Entities/MovableObject.h index bb4053e201..b30c9cf546 100644 --- a/Source/Entities/MovableObject.h +++ b/Source/Entities/MovableObject.h @@ -1,18 +1,11 @@ #ifndef _RTEMOVABLEOBJECT_ #define _RTEMOVABLEOBJECT_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MovableObject.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the MovableObject class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files, forward declarations, namespace stuff - +/// Header file for the MovableObject class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files, forward declarations, namespace stuff #include "SceneObject.h" #include "Vector.h" #include "Matrix.h" @@ -32,21 +25,13 @@ namespace RTE { class SLTerrain; class LuaStateWrapper; - ////////////////////////////////////////////////////////////////////////////////////////// - // Abstract class: MovableObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A movable object with mass. - // Parent(s): SceneObject. - // Class history: 03/18/2001 MovableObject created. - + /// A movable object with mass. class MovableObject : public SceneObject { friend class Atom; friend struct EntityLuaBindings; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: ScriptFunctionNames("Create", "Destroy", "Update", "ThreadedUpdate", "SyncedUpdate", "OnScriptDisable", "OnScriptEnable", "OnCollideWithTerrain", "OnCollideWithMO", "WhilePieMenuOpen", "OnSave", "OnMessage", "OnGlobalMessage"); SerializableOverrideMethods; @@ -59,981 +44,539 @@ namespace RTE { TypeThrownDevice }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MovableObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a MovableObject object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a MovableObject object in system + /// memory. Create() should be called before using the object. MovableObject() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~MovableObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a MovableObject object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a MovableObject object before deletion + /// from system memory. ~MovableObject() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MovableObject object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MovableObject object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MovableObject object ready for use. - // Arguments: A float specifying the object's mass in Kilograms (kg). - // A Vector specifying the initial position. - // A Vector specifying the initial velocity. - // The rotation angle in r. - // The angular velocity in r/s. - // The amount of time in ms this MovableObject will exist. 0 means unlim. - // Whether or not this MO will collide with other MO's while travelling. - // Whether or not this MO be collided with bt other MO's during their travel. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MovableObject object ready for use. + /// @param mass A float specifying the object's mass in Kilograms (kg). + /// @param position A Vector specifying the initial position. (default: Vector(0) + /// @param 0) A Vector specifying the initial velocity. + /// @param velocity The rotation angle in r. (default: Vector(0) + /// @param 0) The angular velocity in r/s. + /// @param rotAngle The amount of time in ms this MovableObject will exist. 0 means unlim. (default: 0) + /// @param angleVel Whether or not this MO will collide with other MO's while travelling. (default: 0) + /// @param lifetime Whether or not this MO be collided with bt other MO's during their travel. (default: 0) + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(float mass, const Vector& position = Vector(0, 0), const Vector& velocity = Vector(0, 0), float rotAngle = 0, float angleVel = 0, unsigned long lifetime = 0, bool hitMOs = true, bool getHitByMOs = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MovableObject to be identical to another, by deep copy. - // Arguments: A reference to the MovableObject to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a MovableObject to be identical to another, by deep copy. + /// @param reference A reference to the MovableObject to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const MovableObject& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire MovableObject, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire MovableObject, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); SceneObject::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MovableObject object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the MovableObject object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; #pragma region Script Handling - /// /// Loads the script at the given script path onto the object, checking for appropriately named functions within it. /// If the script contains a Create function and this MO's scripts are running, the Create function will be run immediately. - /// - /// The path to the script to load. - /// Whether or not the script should load as enabled. Defaults to true. - /// 0 on success. -1 if scriptPath is empty. -2 if the script is already loaded. -3 if setup to load the script or modify the global lua state fails. -4 if the script fails to load. + /// @param scriptPath The path to the script to load. + /// @param loadAsEnabledScript Whether or not the script should load as enabled. Defaults to true. + /// @return 0 on success. -1 if scriptPath is empty. -2 if the script is already loaded. -3 if setup to load the script or modify the global lua state fails. -4 if the script fails to load. virtual int LoadScript(const std::string& scriptPath, bool loadAsEnabledScript = true); - /// /// Reloads the all of the scripts on this object. This will also reload scripts for the original preset in PresetMan so future objects spawned will use the new scripts. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int ReloadScripts() final; - /// /// Gets whether or not the object has a script name, and there were no errors when initializing its Lua scripts. If there were, the object would need to be reloaded. - /// - /// Whether or not the object's scripts have been successfully initialized. + /// @return Whether or not the object's scripts have been successfully initialized. bool ObjectScriptsInitialized() const { return !m_ScriptObjectName.empty() && m_ScriptObjectName != "ERROR"; } - /// /// Checks if this MO has any scripts on it. - /// - /// Whether or not this MO has any scripts on it. + /// @return Whether or not this MO has any scripts on it. bool HasAnyScripts() const { return !m_AllLoadedScripts.empty(); } - /// /// Checks if the script at the given path is one of the scripts on this MO. - /// - /// The path to the script to check. - /// Whether or not the script is on this MO. + /// @param scriptPath The path to the script to check. + /// @return Whether or not the script is on this MO. bool HasScript(const std::string& scriptPath) const { return m_AllLoadedScripts.find(scriptPath) != m_AllLoadedScripts.end(); } - /// /// Checks if the script at the given path is one of the enabled scripts on this MO. - /// - /// The path to the script to check. - /// Whether or not the script is enabled on this MO. + /// @param scriptPath The path to the script to check. + /// @return Whether or not the script is enabled on this MO. bool ScriptEnabled(const std::string& scriptPath) const { auto scriptPathIterator = m_AllLoadedScripts.find(scriptPath); return scriptPathIterator != m_AllLoadedScripts.end() && scriptPathIterator->second == true; } - /// /// Enables or dsiableds the script at the given path on this MO. - /// - /// The path to the script to enable or disable - /// Whether to enable the script, or disable it. - /// Whether or not the script was successfully eanbled/disabled. + /// @param scriptPath The path to the script to enable or disable + /// @param enableScript Whether to enable the script, or disable it. + /// @return Whether or not the script was successfully eanbled/disabled. bool EnableOrDisableScript(const std::string& scriptPath, bool enableScript); - /// /// Enables or disables all scripts on this MovableObject. - /// - /// Whether to enable (true) or disable (false) all scripts on this MovableObject. + /// @param enableScripts Whether to enable (true) or disable (false) all scripts on this MovableObject. void EnableOrDisableAllScripts(bool enableScripts); - /// /// Runs the given function in all scripts that have it, with the given arguments, with the ability to not run on disabled scripts and to cease running if there's an error. /// The first argument to the function will always be 'self'. If either argument list is not empty, its entries will be passed into the Lua function in order, with entity arguments first. - /// - /// The name of the function to run. - /// Whether to run the function on disabled scripts. Defaults to false. - /// Whether to stop if there's an error running any script, or simply print it to the console and continue. Defaults to false. - /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. - /// Optional vector of strings, that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param functionName The name of the function to run. + /// @param runOnDisabledScripts Whether to run the function on disabled scripts. Defaults to false. + /// @param stopOnError Whether to stop if there's an error running any script, or simply print it to the console and continue. Defaults to false. + /// @param functionEntityArguments Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. + /// @param functionLiteralArguments Optional vector of strings, that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int RunScriptedFunctionInAppropriateScripts(const std::string& functionName, bool runOnDisabledScripts = false, bool stopOnError = false, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); - /// /// Cleans up and destroys the script state of this object, calling the Destroy callback in lua - /// virtual void DestroyScriptState(); #pragma endregion - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMOType - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the MO type code of this MO. Either Actor, Item, or Generic. - // Arguments: None. - // Return value: An int describing the MO Type code of this MovableObject. - + /// Gets the MO type code of this MO. Either Actor, Item, or Generic. + /// @return An int describing the MO Type code of this MovableObject. int GetMOType() const { return m_MOType; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMass - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the mass value of this MovableObject. - // Arguments: None. - // Return value: A float describing the mass value in Kilograms (kg). - + /// Gets the mass value of this MovableObject. + /// @return A float describing the mass value in Kilograms (kg). virtual float GetMass() const { return m_Mass; } - /// /// Gets the previous position vector of this MovableObject, prior to this frame. - /// - /// A Vector describing the previous position vector. + /// @return A Vector describing the previous position vector. const Vector& GetPrevPos() const { return m_PrevPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the velocity vector of this MovableObject. - // Arguments: None. - // Return value: A Vector describing the current velocity vector. - + /// Gets the velocity vector of this MovableObject. + /// @return A Vector describing the current velocity vector. const Vector& GetVel() const { return m_Vel; } - /// /// Gets the previous velocity vector of this MovableObject, prior to this frame. - /// - /// A Vector describing the previous velocity vector. + /// @return A Vector describing the previous velocity vector. const Vector& GetPrevVel() const { return m_PrevVel; } - /// /// Gets the amount of distance this MO has travelled since its creation, in pixels. - /// - /// The amount of distance this MO has travelled, in pixels. + /// @return The amount of distance this MO has travelled, in pixels. float GetDistanceTravelled() const { return m_DistanceTravelled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAngularVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current angular velocity of this MovableObject. Positive is - // a counter-clockwise rotation. - // Arguments: None. - // Return value: The angular velocity in radians per second. - + /// Gets the current angular velocity of this MovableObject. Positive is + /// a counter-clockwise rotation. + /// @return The angular velocity in radians per second. virtual float GetAngularVel() const { return 0.0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRadius - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the largest radius of this in pixels. - // Arguments: None. - // Return value: The radius from its center to the edge of its graphical representation. - + /// Gets the largest radius of this in pixels. + /// @return The radius from its center to the edge of its graphical representation. virtual float GetRadius() const { return 1.0f; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetDiameter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the largest diameter of this in pixels. - // Arguments: None. - // Return value: The largest diameter across its graphical representation. - + /// Gets the largest diameter of this in pixels. + /// @return The largest diameter across its graphical representation. virtual float GetDiameter() const { return 2.0F; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current scale of this MOSRotating. This is mostly for fun. - // Arguments: None. - // Return value: The normalized scale. - + /// Gets the current scale of this MOSRotating. This is mostly for fun. + /// @return The normalized scale. float GetScale() const { return m_Scale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGlobalAccScalar - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets How this is affected by global effects, from +1.0 to -1.0. - // Something with a negative value will 'float' upward. - // Arguments: None. - // Return value: The global acceleration scalar. - + /// Gets How this is affected by global effects, from +1.0 to -1.0. + /// Something with a negative value will 'float' upward. + /// @return The global acceleration scalar. float GetGlobalAccScalar() const { return m_GlobalAccScalar; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetGlobalAccScalar - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets How this is affected by global effects, from +1.0 to -1.0. - // Something with a negative value will 'float' upward. - // Arguments: The global acceleration scalar. - // Return value: None. - + /// Sets How this is affected by global effects, from +1.0 to -1.0. + /// Something with a negative value will 'float' upward. + /// @param newValue The global acceleration scalar. void SetGlobalAccScalar(float newValue) { m_GlobalAccScalar = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAirResistance - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: How much this is affected by air resistance when traveling over a - // second, 0 to 1.0, with 0 as default - // Arguments: None. - // Return value: The air resistance coefficient. - + /// How much this is affected by air resistance when traveling over a + /// second, 0 to 1.0, with 0 as default + /// @return The air resistance coefficient. float GetAirResistance() const { return m_AirResistance; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetAirResistance - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets how much this is affected by air resistance when traveling over a - // second, 0 to 1.0, with 0 as default - // Arguments: The air resistance coefficient. - // Return value: None. - + /// Sets how much this is affected by air resistance when traveling over a + /// second, 0 to 1.0, with 0 as default + /// @param newValue The air resistance coefficient. void SetAirResistance(float newValue) { m_AirResistance = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAirThreshold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: At which threshold of velocity, in m/s, the effect of AirResistance - // kicks in. - // Arguments: None. - // Return value: The air threshold speed. - + /// At which threshold of velocity, in m/s, the effect of AirResistance + /// kicks in. + /// @return The air threshold speed. float GetAirThreshold() const { return m_AirThreshold; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetAirThreshold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets at which threshold of velocity, in m/s, the effect of AirResistance - // kicks in. - // Arguments: The air threshold speed. - // Return value: None. - + /// Sets at which threshold of velocity, in m/s, the effect of AirResistance + /// kicks in. + /// @param newValue The air threshold speed. void SetAirThreshold(float newValue) { m_AirThreshold = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAge - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets real time age of this MovableObject. - // Arguments: None. - // Return value: A unsigned long describing the current age in ms. - + /// Gets real time age of this MovableObject. + /// @return A unsigned long describing the current age in ms. unsigned long GetAge() const { return m_AgeTimer.GetElapsedSimTimeMS(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLifetime - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the amount of time this MovableObject will exist from creation. - // Arguments: None. - // Return value: A unsigned long describing the current lifetime in ms. 0 means unlimited. - + /// Gets the amount of time this MovableObject will exist from creation. + /// @return A unsigned long describing the current lifetime in ms. 0 means unlimited. unsigned long GetLifetime() const { return m_Lifetime; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the MOID of this MovableObject for this frame. - // Arguments: None. - // Return value: An int specifying the MOID that this MovableObject is - // assigned for the current frame only. - + /// Gets the MOID of this MovableObject for this frame. + /// @return An int specifying the MOID that this MovableObject is + /// assigned for the current frame only. MOID GetID() const { return m_MOID; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRootID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the MOID of the MovableObject which is the root MO of this MO for - // this frame. If same as what GetID returns, then this is owned by - // MovableMan. - // Arguments: None. - // Return value: An int specifying the MOID of the MO that this MovableObject - // is owned by for the current frame only. - + /// Gets the MOID of the MovableObject which is the root MO of this MO for + /// this frame. If same as what GetID returns, then this is owned by + /// MovableMan. + /// @return An int specifying the MOID of the MO that this MovableObject + /// is owned by for the current frame only. MOID GetRootID() const { return m_RootMOID; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMOIDFootprint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how many total (subsequent) MOID's this MO and all its children - // are taking up this frame. ie if this MO has no children, this will - // likely be 1. Note this is only valid for this frame! - // Arguments: None. - // Return value: The number of MOID indices this MO and all its children are taking up. - + /// Gets how many total (subsequent) MOID's this MO and all its children + /// are taking up this frame. ie if this MO has no children, this will + /// likely be 1. Note this is only valid for this frame! + /// @return The number of MOID indices this MO and all its children are taking up. int GetMOIDFootprint() const { return m_MOIDFootprint; } - /// /// Returns whether or not this MovableObject has ever been added to MovableMan. Does not account for removal from MovableMan. - /// - /// Whether or not this MovableObject has ever been added to MovableMan. + /// @return Whether or not this MovableObject has ever been added to MovableMan. bool HasEverBeenAddedToMovableMan() const { return m_HasEverBeenAddedToMovableMan; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetSharpness - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the sharpness factor of this MO. - // Arguments: None. - // Return value: The sharpness factor of this MO. 1.0 means normal sharpness, no alter- - // ation to any of the impulses. - + /// Gets the sharpness factor of this MO. + /// @return The sharpness factor of this MO. 1.0 means normal sharpness, no alter- + /// ation to any of the impulses. float GetSharpness() const { return m_Sharpness; } - /// /// Placeholder method to allow for ease of use with Attachables. Returns nullptr for classes that aren't derived from Attachable. - /// - /// Nothing. + /// @return Nothing. virtual MOSRotating* GetParent() { return nullptr; } - /// /// Placeholder method to allow for ease of use with Attachables. Returns nullptr for classes that aren't derived from Attachable. - /// - /// Nothing. + /// @return Nothing. virtual const MOSRotating* GetParent() const { return nullptr; } - /// /// Returns a pointer to this MO, this is to enable Attachables to get their root nodes. - /// - /// A pointer to this MovableObject. + /// @return A pointer to this MovableObject. virtual MovableObject* GetRootParent() { return this; } - /// /// Returns a pointer to this MO, this is to enable Attachables to get their root nodes. - /// - /// A pointer to this MovableObject. + /// @return A pointer to this MovableObject. virtual const MovableObject* GetRootParent() const { return this; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAltitude - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the altitide of this' pos (or appropriate low point) over the - // terrain, in pixels. - // Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. - // The accuracy within which measurement is acceptable. Higher number - // here means less calculation. - // Return value: The rough altitude over the terrain, in pixels. - + /// Gets the altitide of this' pos (or appropriate low point) over the + /// terrain, in pixels. + /// @param max The max altitude you care to check for. 0 Means check the whole scene's height. (default: 0) + /// @param accuracy The accuracy within which measurement is acceptable. Higher number (default: 0) + /// here means less calculation. + /// @return The rough altitude over the terrain, in pixels. virtual float GetAltitude(int max = 0, int accuracy = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetAboveHUDPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absoltue position of the top of this' HUD stack. - // Arguments: None. - // Return value: A Vector with the absolute position of this' HUD stack top point. - + /// Gets the absoltue position of the top of this' HUD stack. + /// @return A Vector with the absolute position of this' HUD stack top point. virtual Vector GetAboveHUDPos() const { return m_Pos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IntersectionWarning - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this may have started to intersect the terrain since the - // last frame, e g due to flipping. - // Arguments: None. - // Return value: Whether this may have started to intersect the terrain since last frame. - + /// Shows whether this may have started to intersect the terrain since the + /// last frame, e g due to flipping. + /// @return Whether this may have started to intersect the terrain since last frame. bool IntersectionWarning() const { return m_CheckTerrIntersection; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HitsMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets whether this MovableObject is set to collide with other - // MovableObject:s during its travel. - // Arguments: None. - // Return value: Whether this hits other MO's during its travel, or not. - + /// Gets whether this MovableObject is set to collide with other + /// MovableObject:s during its travel. + /// @return Whether this hits other MO's during its travel, or not. bool HitsMOs() const { return m_HitsMOs; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetsHitByMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets whether this MovableObject is set to be able to get hit by other - // MovableObject:s during their travel. - // Arguments: None. - // Return value: Whether this can get hit by MO's, or not. - + /// Gets whether this MovableObject is set to be able to get hit by other + /// MovableObject:s during their travel. + /// @return Whether this can get hit by MO's, or not. bool GetsHitByMOs() const { return m_GetsHitByMOs; } - /// /// Sets the team of this MovableObject. - /// - /// The new team to assign. + /// @param team The new team to assign. void SetTeam(int team) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetIgnoresTeamHits - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this will collide with any other MO of the same team. - // Arguments: Whether this can hit or get hit by other MOs of the same team. - // Return value: None. - + /// Sets whether this will collide with any other MO of the same team. + /// @param ignoreTeam Whether this can hit or get hit by other MOs of the same team. (default: true) void SetIgnoresTeamHits(bool ignoreTeam = true) { m_IgnoresTeamHits = ignoreTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IgnoresTeamHits - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether this will collide with any other MO of the same team. - // Arguments: None. - // Return value: Whether this can hit or get hit by other MOs of the same team. - + /// Tells whether this will collide with any other MO of the same team. + /// @return Whether this can hit or get hit by other MOs of the same team. bool IgnoresTeamHits() const { return m_IgnoresTeamHits; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IgnoresWhichTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells which team this would be ignoring hits with, if we're ignoring - // hits at all. - // Arguments: None. - // Return value: Which team this ignores hits with, if any. - + /// Tells which team this would be ignoring hits with, if we're ignoring + /// hits at all. + /// @return Which team this ignores hits with, if any. int IgnoresWhichTeam() const { return m_IgnoresTeamHits ? m_Team : Activity::NoTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetIgnoresAtomGroupHits - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this will collide with any other MO that uses an - // AtomGroup as a physical representation. This also overrides the - // IgnoresAGHitsWhenSlowerThan property. - // Arguments: Whether this can hit or get hit by other MOs which use AGs. - // Return value: None. - + /// Sets whether this will collide with any other MO that uses an + /// AtomGroup as a physical representation. This also overrides the + /// IgnoresAGHitsWhenSlowerThan property. + /// @param ignoreAG Whether this can hit or get hit by other MOs which use AGs. (default: true) void SetIgnoresAtomGroupHits(bool ignoreAG = true) { m_IgnoresAtomGroupHits = ignoreAG; if (ignoreAG) m_IgnoresAGHitsWhenSlowerThan = -1; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IgnoresAtomGroupHits - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether this will collide with any MO that uses an AtomGroup - // as physical representation. (as opposed to single-atom ones) - // Arguments: None. - // Return value: Whether this can hit or get hit by other MOs that use AGs. - + /// Tells whether this will collide with any MO that uses an AtomGroup + /// as physical representation. (as opposed to single-atom ones) + /// @return Whether this can hit or get hit by other MOs that use AGs. bool IgnoresAtomGroupHits() const { return m_IgnoresAtomGroupHits; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IgnoreTerrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether this will collide with any Terrain - // Arguments: None. - // Return value: Whether this can hit terrain. - + /// Tells whether this will collide with any Terrain + /// @return Whether this can hit terrain. bool IgnoreTerrain() const { return m_IgnoreTerrain; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetIgnoreTerrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this will collide with any Terrain - // Arguments: Whether this can hit terrain. - // Return value: None. - + /// Sets whether this will collide with any Terrain + /// @param ignores Whether this can hit terrain. void SetIgnoreTerrain(bool ignores) { m_IgnoreTerrain = ignores; } - /// /// Gets whether this MO ignores collisions with actors. - /// - /// Whether this MO ignores collisions with actors. + /// @return Whether this MO ignores collisions with actors. bool GetIgnoresActorHits() const { return m_IgnoresActorHits; } - /// /// Sets whether this MO ignores collisions with actors. - /// - /// Whether this MO will ignore collisions with actors. + /// @param value Whether this MO will ignore collisions with actors. void SetIgnoresActorHits(bool value) { m_IgnoresActorHits = value; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: GetMaterial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the main material of this MovableObject. - // Arguments: None. - // Return value: The the material of this MovableObject. - + /// Gets the main material of this MovableObject. + /// @return The the material of this MovableObject. virtual Material const* GetMaterial() const = 0; - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: GetDrawPriority - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the drawing priority of this MovableObject, if two things were - // overlap when copying to the terrain, the higher priority MO would - // end up getting drawn. - // Arguments: None. - // Return value: The the priority of this MovableObject. Higher number, the higher - // priority. - + /// Gets the drawing priority of this MovableObject, if two things were + /// overlap when copying to the terrain, the higher priority MO would + /// end up getting drawn. + /// @return The the priority of this MovableObject. Higher number, the higher + /// priority. virtual int GetDrawPriority() const = 0; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetScreenEffect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the screen effect this has loaded, which can be applied to post - // rendering. Ownership is NOT transferred! - // Arguments: None. - // Return value: The 32bpp screen effect BITMAP. Ownership is NOT transferred! - + /// Gets the screen effect this has loaded, which can be applied to post + /// rendering. Ownership is NOT transferred! + /// @return The 32bpp screen effect BITMAP. Ownership is NOT transferred! BITMAP* GetScreenEffect() const { return m_pScreenEffect; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetScreenEffectHash - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the hash of the path of this object's screen effect file. Used to - // transfer glow effects over network. The hash itself is calculated during - // load. - // Arguments: None. - // Return value: This effect's unique hash. - + /// Gets the hash of the path of this object's screen effect file. Used to + /// transfer glow effects over network. The hash itself is calculated during + /// load. + /// @return This effect's unique hash. size_t GetScreenEffectHash() const { return m_ScreenEffectHash; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetMass - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the mass of this MovableObject. - // Arguments: A float specifying the new mass value in Kilograms (kg). - // Return value: None. - + /// Sets the mass of this MovableObject. + /// @param newMass A float specifying the new mass value in Kilograms (kg). virtual void SetMass(const float newMass) { m_Mass = newMass; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPrevPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the position at the start of the sim update. - // Arguments: A Vector specifying the new 'prev' pos. - // Return value: None. - + /// Sets the position at the start of the sim update. + /// @param newPrevPos A Vector specifying the new 'prev' pos. void SetPrevPos(const Vector& newPrevPos) { m_PrevPos = newPrevPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the velocity vector of this MovableObject. - // Arguments: A Vector specifying the new velocity vector. - // Return value: None. - + /// Sets the velocity vector of this MovableObject. + /// @param newVel A Vector specifying the new velocity vector. void SetVel(const Vector& newVel) { m_Vel = newVel; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetRotAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current absolute angle of rotation of this MovableObject. - // Arguments: The new absolute angle in radians. - // Return value: None. - + /// Sets the current absolute angle of rotation of this MovableObject. + /// @param newAngle The new absolute angle in radians. void SetRotAngle(float newAngle) override {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetEffectRotAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current absolute angle of rotation of this MovableObject's effect. - // Arguments: The new absolute angle in radians. - // Return value: None. - + /// Sets the current absolute angle of rotation of this MovableObject's effect. + /// @param newAngle The new absolute angle in radians. void SetEffectRotAngle(float newAngle) { m_EffectRotAngle = newAngle; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetEffectRotAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current absolute angle of rotation of this MovableObject's effect. - // Arguments: None. - // Return value: The absolute angle in radians. - + /// Gets the current absolute angle of rotation of this MovableObject's effect. + /// @return The absolute angle in radians. float GetEffectRotAngle() const { return m_EffectRotAngle; } - /// /// Gets the starting strength of this MovableObject's effect. - /// - /// The starting strength of the effect, 0-255. + /// @return The starting strength of the effect, 0-255. int GetEffectStartStrength() const { return m_EffectStartStrength; } - /// /// Gets the stopping strength of this MovableObject's effect. - /// - /// The stopping strength of the effect, 0-255. + /// @return The stopping strength of the effect, 0-255. int GetEffectStopStrength() const { return m_EffectStopStrength; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetAngularVel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current angular velocity of this MovableObject. Positive is - // a counter clockwise rotation. - // Arguments: The new angular velocity in radians per second. - // Return value: None. - + /// Sets the current angular velocity of this MovableObject. Positive is + /// a counter clockwise rotation. + /// @param newRotVel The new angular velocity in radians per second. virtual void SetAngularVel(float newRotVel) {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current scale of this MOSRotating. This is mostly for fun. - // Arguments: The new normalized scale. - // Return value: None. - + /// Sets the current scale of this MOSRotating. This is mostly for fun. + /// @param newScale The new normalized scale. void SetScale(float newScale) { m_Scale = newScale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLifetime - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the amount of time this MovableObject will exist. - // Arguments: A unsigned long specifying amount of time in ms. 0 means unlimited life. - // Return value: None. - + /// Sets the amount of time this MovableObject will exist. + /// @param newLifetime A unsigned long specifying amount of time in ms. 0 means unlimited life. (default: 0) void SetLifetime(const int newLifetime = 0) { m_Lifetime = newLifetime; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetAge - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this' age timer to a specific value, in ms. - // Arguments: The new age of this, in MS. - // Return value: None. - + /// Sets this' age timer to a specific value, in ms. + /// @param newAge The new age of this, in MS. (default: 0) { m_AgeTimer.SetElapsedSimTimeMS(newAge) void SetAge(double newAge = 0) { m_AgeTimer.SetElapsedSimTimeMS(newAge); } - /// /// Sets the MOID of this MovableObject to be g_NoMOID (255) for this frame. - /// virtual void SetAsNoID() { m_MOID = g_NoMOID; } - /// /// Sets this MovableObject as having been added to MovableMan. Should only really be done in MovableMan::Add/Remove Actor/Item/Particle. - /// - /// Whether or not this MovableObject has been added to MovableMan. + /// @param addedToMovableMan Whether or not this MovableObject has been added to MovableMan. void SetAsAddedToMovableMan(bool addedToMovableMan = true) { if (addedToMovableMan) { m_HasEverBeenAddedToMovableMan = true; } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetSharpness - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the sharpness factor of this MO. - // Arguments: The sharpness factor of this MO. 1.0 means normal sharpness, no alter- - // ation to any of the impulses. - // Return value: None. - + /// Sets the sharpness factor of this MO. + /// @param sharpness The sharpness factor of this MO. 1.0 means normal sharpness, no alter- + /// ation to any of the impulses. void SetSharpness(const float sharpness) { m_Sharpness = sharpness; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetToHitMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this MovableObject to collide with other MovableObjects during - // travel. - // Arguments: Whether to hit other MO's during travel, or not. - // Return value: None. - + /// Sets this MovableObject to collide with other MovableObjects during + /// travel. + /// @param hitMOs Whether to hit other MO's during travel, or not. (default: true) void SetToHitMOs(bool hitMOs = true) { m_HitsMOs = hitMOs; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetToGetHitByMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this MovableObject to be able to be collided with by other - // MovableObjects during their travel. - // Arguments: Whether this should get hit by other MO's during travel, or not. - // Return value: None. - + /// Sets this MovableObject to be able to be collided with by other + /// MovableObjects during their travel. + /// @param getHitByMOs Whether this should get hit by other MO's during travel, or not. (default: true) void SetToGetHitByMOs(bool getHitByMOs = true) { m_GetsHitByMOs = getHitByMOs; } - /// /// Gets the MO this MO is set not to hit even when MO hitting is enabled on this MO. - /// - /// The MO this MO is set not to hit. + /// @return The MO this MO is set not to hit. const MovableObject* GetWhichMOToNotHit() const { return m_pMOToNotHit; } - /// /// Sets this MO to not hit a specific other MO and all its children even when MO hitting is enabled on this MO. - /// - /// A pointer to the MO to not be hitting. Null pointer means don't ignore anyhting. Ownership is NOT transferred! - /// How long, in seconds, to ignore the specified MO. A negative number means forever. + /// @param moToNotHit A pointer to the MO to not be hitting. Null pointer means don't ignore anyhting. Ownership is NOT transferred! + /// @param forHowLong How long, in seconds, to ignore the specified MO. A negative number means forever. virtual void SetWhichMOToNotHit(MovableObject* moToNotHit = nullptr, float forHowLong = -1) { m_pMOToNotHit = moToNotHit; m_MOIgnoreTimer.Reset(); m_MOIgnoreTimer.SetSimTimeLimitS(forHowLong); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetWrapDoubleDrawing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Enables or disables double drawing of this across wrapping seams. - // Arguments: Wheter to enable or not. - // Return value: None. - + /// Enables or disables double drawing of this across wrapping seams. + /// @param wrapDraw Wheter to enable or not. (default: true) void SetWrapDoubleDrawing(bool wrapDraw = true) { m_WrapDoubleDraw = wrapDraw; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetToSettle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Marks this MovableObject for settling onto the terrain at the end of - // the MovableMan update. - // Arguments: Whether to mark this MO for settling or not. - // Return value: None. - + /// Marks this MovableObject for settling onto the terrain at the end of + /// the MovableMan update. + /// @param toSettle Whether to mark this MO for settling or not. (default: true) void SetToSettle(bool toSettle = true) { m_ToSettle = toSettle; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetToDelete - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Marks this MovableObject for deletion at the end of the MovableMan - // update. - // Arguments: Whether to mark this MO for deletion or not. - // Return value: None. - + /// Marks this MovableObject for deletion at the end of the MovableMan + /// update. + /// @param toDelete Whether to mark this MO for deletion or not. (default: true) void SetToDelete(bool toDelete = true) { m_ToDelete = toDelete; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsSetToDelete - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells if this MovableObject is marked for deletion at the end of the - // update. - // Arguments: None. - // Return value: Whether this is marked for deletion or not. - + /// Tells if this MovableObject is marked for deletion at the end of the + /// update. + /// @return Whether this is marked for deletion or not. bool IsSetToDelete() const { return m_ToDelete; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsMissionCritical - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is mission critical and should therefore NEVER be - // settled or otherwise destroyed during teh course of a mission. - // Arguments: None. - // Return value: Whetehr this should be immune to settling and destruction. - + /// Shows whether this is mission critical and should therefore NEVER be + /// settled or otherwise destroyed during teh course of a mission. + /// @return Whetehr this should be immune to settling and destruction. bool IsMissionCritical() const { return m_MissionCritical; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMissionCritical - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this is mission critical and should therefore NEVER be - // settled or otherwise destroyed during teh course of a mission. - // Arguments: Whether this should be immune to settling and destruction. - // Return value: None. - + /// Sets whether this is mission critical and should therefore NEVER be + /// settled or otherwise destroyed during teh course of a mission. + /// @param missionCritical Whether this should be immune to settling and destruction. void SetMissionCritical(bool missionCritical) { m_MissionCritical = missionCritical; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CanBeSquished - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this can be squished by getting pushed into the ground. - // Arguments: None. - // Return value: Whetehr this should be immune to squishing or not. - + /// Shows whether this can be squished by getting pushed into the ground. + /// @return Whetehr this should be immune to squishing or not. bool CanBeSquished() const { return m_CanBeSquished; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetHUDVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether this Actor's HUD is drawn or not. - // Arguments: None. - // Return value: Whether this' HUD gets drawn or not. - + /// Tells whether this Actor's HUD is drawn or not. + /// @return Whether this' HUD gets drawn or not. void SetHUDVisible(bool visible) { m_HUDVisible = visible; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetHUDVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether this Actor's HUD is drawn or not. - // Arguments: None. - // Return value: Whether this' HUD gets drawn or not. - + /// Tells whether this Actor's HUD is drawn or not. + /// @return Whether this' HUD gets drawn or not. bool GetHUDVisible() const { return m_HUDVisible; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsTooFast - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is moving or rotating stupidly fast in a way - // that will screw up the simulation. - // Arguments: None. - // Return value: Whether this is either moving or rotating too fast. - + /// Indicates whether this MO is moving or rotating stupidly fast in a way + /// that will screw up the simulation. + /// @return Whether this is either moving or rotating too fast. virtual bool IsTooFast() const { return m_Vel.MagnitudeIsGreaterThan(500.0F); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: FixTooFast - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Slows the speed of anyhting that is deemed to be too fast to within - // acceptable rates. - // Arguments: None. - // Return value: None. - + /// Slows the speed of anyhting that is deemed to be too fast to within + /// acceptable rates. virtual void FixTooFast() { if (IsTooFast()) { m_Vel.SetMagnitude(450.0F); } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsGeneric - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is an Generic or not. - // Arguments: None. - // Return value: Whether this MovableObject is of Type Generic or not. - + /// Indicates whether this MO is an Generic or not. + /// @return Whether this MovableObject is of Type Generic or not. bool IsGeneric() const { return m_MOType == TypeGeneric; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is an Actor or not. - // Arguments: None. - // Return value: Whether this MovableObject is of Type Actor or not. - + /// Indicates whether this MO is an Actor or not. + /// @return Whether this MovableObject is of Type Actor or not. bool IsActor() const { return m_MOType == TypeActor; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is a Device or not. - // Arguments: None. - // Return value: Whether this MovableObject is of Type Device (Held or Thrown) or not. - + /// Indicates whether this MO is a Device or not. + /// @return Whether this MovableObject is of Type Device (Held or Thrown) or not. bool IsDevice() const { return m_MOType == TypeHeldDevice || m_MOType == TypeThrownDevice; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsHeldDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is a HeldDevice or not. - // Arguments: None. - // Return value: Whether this MovableObject is of Type HeldDevice or not. - + /// Indicates whether this MO is a HeldDevice or not. + /// @return Whether this MovableObject is of Type HeldDevice or not. // LEGACY CRAP bool IsHeldDevice() const { return m_MOType == TypeHeldDevice || m_MOType == TypeThrownDevice; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsThrownDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is a ThrownDevice or not. - // Arguments: None. - // Return value: Whether this MovableObject is of Type ThrownDevice or not. - + /// Indicates whether this MO is a ThrownDevice or not. + /// @return Whether this MovableObject is of Type ThrownDevice or not. bool IsThrownDevice() const { return m_MOType == TypeThrownDevice; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsGold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is made of Gold or not. - // Arguments: None. - // Return value: Whether this MovableObject is of Gold or not. - + /// Indicates whether this MO is made of Gold or not. + /// @return Whether this MovableObject is of Gold or not. bool IsGold() const { return m_MOType == TypeGeneric && GetMaterial()->GetIndex() == c_GoldMaterialID; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsDrawnAfterParent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MovableObject is to be drawn after - // (in front of) or before (behind) the parent. - // Arguments: None. - // Return value: Whether it's to be drawn after parent or not. - + /// Indicates whether this MovableObject is to be drawn after + /// (in front of) or before (behind) the parent. + /// @return Whether it's to be drawn after parent or not. virtual bool IsDrawnAfterParent() const { return true; } - /// /// Whether a set of X, Y coordinates overlap us (in world space). - /// - /// The given X coordinate, in world space. - /// The given Y coordinate, in world space. - /// Whether the given coordinate overlap us. + /// @param pixelX The given X coordinate, in world space. + /// @param pixelY The given Y coordinate, in world space. + /// @return Whether the given coordinate overlap us. virtual bool HitTestAtPixel(int pixelX, int pixelY) const { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically named object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - // Arguments: The Preset name of the object to look for. - // Return value: Whetehr the object was found carried by this. - + /// Shows whether this is or carries a specifically named object in its + /// inventory. Also looks through the inventories of potential passengers, + /// as applicable. + /// @param objectName The Preset name of the object to look for. + /// @return Whetehr the object was found carried by this. virtual bool HasObject(std::string objectName) const { return m_PresetName == objectName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasObjectInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is or carries a specifically grouped object in its - // inventory. Also looks through the inventories of potential passengers, - // as applicable. - // Arguments: The name of the group to look for. - // Return value: Whetehr the object in the group was found carried by this. - + /// Shows whether this is or carries a specifically grouped object in its + /// inventory. Also looks through the inventories of potential passengers, + /// as applicable. + /// @param const_cast(this)->IsInGroup(groupName The name of the group to look for. + /// @return Whetehr the object in the group was found carried by this. virtual bool HasObjectInGroup(std::string groupName) const { return const_cast(this)->IsInGroup(groupName); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds force to this MovableObject for the next time Update() is called. - // Arguments: An Vector with the external force vector that will be added to this - // MovableObject and affect its path next Update(). In N or kg * m/s^2. - // A Vector with the offset, in METERS, of where the force is being - // applied relative to the center of this MovableObject. - // Return value: None.A - + /// Adds force to this MovableObject for the next time Update() is called. + /// @param force An Vector with the external force vector that will be added to this + /// @param offset MovableObject and affect its path next Update(). In N or kg * m/s^2. (default: Vector()) { m_Forces.push_back(std::make_pair(force) + /// @param offset) A Vector with the offset, in METERS, of where the force is being + /// applied relative to the center of this MovableObject. + /// @return None.A void AddForce(const Vector& force, const Vector& offset = Vector()) { m_Forces.push_back(std::make_pair(force, offset)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddAbsForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds force to this MovableObject for the next time Update() is called. - // Arguments: An Vector with the external force vector that will be added to this - // MovableObject and affect its path next Update(). In N or kg * m/s^2. - // A Vector with the absolute world coordinates, in PIXELS, of where the - // force is being applied to the center of this MovableObject. - // Return value: None. - + /// Adds force to this MovableObject for the next time Update() is called. + /// @param force An Vector with the external force vector that will be added to this + /// @param absPos MovableObject and affect its path next Update(). In N or kg * m/s^2. + /// A Vector with the absolute world coordinates, in PIXELS, of where the + /// force is being applied to the center of this MovableObject. void AddAbsForce(const Vector& force, const Vector& absPos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddImpulseForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds impulse force (or instant momentum) to this MovableObject for - // the next time Update() is called. - // Arguments: An Vector with the impulse force vector that will directly be added - // to this MovableObject's momentum next Update(). In kg * m/s. - // A Vector with the offset, in METERS, of where the impulse is being - // applied relative to the center of this MovableObject. - // Return value: None. - + /// Adds impulse force (or instant momentum) to this MovableObject for + /// the next time Update() is called. + /// @param impulse An Vector with the impulse force vector that will directly be added + /// to this MovableObject's momentum next Update(). In kg * m/s. + /// @param offset A Vector with the offset, in METERS, of where the impulse is being (default: Vector()) + /// applied relative to the center of this MovableObject. void AddImpulseForce(const Vector& impulse, const Vector& offset = Vector()) { #ifndef RELEASE_BUILD @@ -1044,248 +587,138 @@ namespace RTE { m_ImpulseForces.push_back({impulse, offset}); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddAbsImpulseForce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds impulse force (or instant momentum) to this MovableObject for - // the next time Update() is called. - // Arguments: An Vector with the impulse force vector that will directly be added - // to this MovableObject's momentum next Update(). In kg * m/s. - // A Vector with the absolute world coordinates, in PIXELS, of where the - // force is being applied to the center of this MovableObject. - // Return value: None. - + /// Adds impulse force (or instant momentum) to this MovableObject for + /// the next time Update() is called. + /// @param impulse An Vector with the impulse force vector that will directly be added + /// to this MovableObject's momentum next Update(). In kg * m/s. + /// @param absPos A Vector with the absolute world coordinates, in PIXELS, of where the + /// force is being applied to the center of this MovableObject. void AddAbsImpulseForce(const Vector& impulse, const Vector& absPos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ClearForces - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears out all the forces this MO has accumulated during this frame. - // Arguments: None. - // Return value: None. - + /// Clears out all the forces this MO has accumulated during this frame. void ClearForces() { m_Forces.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ClearImpulseForces - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears out all the impulses this MO has accumulated during this frame. - // Arguments: None. - // Return value: None. - + /// Clears out all the impulses this MO has accumulated during this frame. void ClearImpulseForces() { m_ImpulseForces.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPinStrength - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the impulse force threshold which has to be exceeded to - // 'shake loose' this from a 'pinned' state. Pinned MOs don't get moved - // by travel algos. If 0, this isn't pinned. - // Arguments: None. - // Return value: The impulse threshold in kg * (m/s). 0 means no pinning - + /// Gets the impulse force threshold which has to be exceeded to + /// 'shake loose' this from a 'pinned' state. Pinned MOs don't get moved + /// by travel algos. If 0, this isn't pinned. + /// @return The impulse threshold in kg * (m/s). 0 means no pinning float GetPinStrength() const { return m_PinStrength; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPinStrength - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets a impulse force threshold which has to be exceeded to - // 'shake loose' this from a 'pinned' state. Pinned MOs don't get moved - // by travel algos. If 0, this isn't pinned. - // Arguments: The impulse threshold in kg * (m/s). 0 means no pinning - // Return value: None. - + /// Sets a impulse force threshold which has to be exceeded to + /// 'shake loose' this from a 'pinned' state. Pinned MOs don't get moved + /// by travel algos. If 0, this isn't pinned. + /// @param pinStrength The impulse threshold in kg * (m/s). 0 means no pinning void SetPinStrength(float pinStrength) { m_PinStrength = pinStrength; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ResetAllTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resest all the timers used by this. Can be emitters, etc. This is to - // prevent backed up emissions to come out all at once while this has been - // held dormant in an inventory. - // Arguments: None. - // Return value: None. - + /// Resest all the timers used by this. Can be emitters, etc. This is to + /// prevent backed up emissions to come out all at once while this has been + /// held dormant in an inventory. virtual void ResetAllTimers() {} - /// /// Does the calculations necessary to detect whether this MovableObject is at rest or not. IsAtRest() retrieves the answer. - /// virtual void RestDetection(); - /// /// Forces this MovableObject out of resting conditions. - /// virtual void NotResting() { m_RestTimer.Reset(); m_ToSettle = false; m_VelOscillations = 0; } - /// /// Indicates whether this MovableObject has been at rest with no movement for longer than its RestThreshold. - /// virtual bool IsAtRest(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsUpdated - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates wheter this MovableObject has been updated yet during this - // frame. - // Arguments: None. - // Return value: Wheter or not the MovableObject has been updated yet during this frame. - + /// Indicates wheter this MovableObject has been updated yet during this + /// frame. + /// @return Wheter or not the MovableObject has been updated yet during this frame. bool IsUpdated() const { return m_IsUpdated; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: NewFrame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tell this MovableObject that a new frame has started. - // Arguments: None. - // Return value: None. - + /// Tell this MovableObject that a new frame has started. void NewFrame() { m_IsUpdated = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ToSettle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is marked for settling at the end of the - // MovableMan update. - // Arguments: None. - // Return value: Whether this MO is marked for settling ontot the terrain or not. - + /// Indicates whether this MO is marked for settling at the end of the + /// MovableMan update. + /// @return Whether this MO is marked for settling ontot the terrain or not. bool ToSettle() const { return !m_MissionCritical && m_ToSettle; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ToDelete - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO is marked for deletion at the end of the - // MovableMan update. - // Arguments: None. - // Return value: Whether this MO is marked for deletion or not. - + /// Indicates whether this MO is marked for deletion at the end of the + /// MovableMan update. + /// @return Whether this MO is marked for deletion or not. bool ToDelete() const { return m_ToDelete; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DidWrap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this MO moved across the scene wrap seam during the - // last update. - // Arguments: None. - // Return value: Whether this MO wrapped or not. - + /// Indicates whether this MO moved across the scene wrap seam during the + /// last update. + /// @return Whether this MO wrapped or not. bool DidWrap() { return m_DidWrap; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure v. method: CollideAtPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the collision response when another MO's Atom collides with - // this MO's physical representation. The effects will be applied - // directly to this MO, and also represented in the passed in HitData. - // Arguments: Reference to the HitData struct which describes the collision. This - // will be modified to represent the results of the collision. - // Return value: Whether the collision has been deemed valid. If false, then disregard - // any impulses in the Hitdata. - + /// Calculates the collision response when another MO's Atom collides with + /// this MO's physical representation. The effects will be applied + /// directly to this MO, and also represented in the passed in HitData. + /// @param hitData Reference to the HitData struct which describes the collision. This + /// will be modified to represent the results of the collision. + /// @return Whether the collision has been deemed valid. If false, then disregard + /// any impulses in the Hitdata. virtual bool CollideAtPoint(HitData& hitData) = 0; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: OnMOHit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Defines what should happen when this MovableObject hits another MO. - // This is called by the owned Atom/AtomGroup of this MovableObject during - // travel. - // Arguments: The HitData describing the collision in detail. - // Return value: Wheter the MovableObject should immediately halt any travel going on - // after this hit. - + /// Defines what should happen when this MovableObject hits another MO. + /// This is called by the owned Atom/AtomGroup of this MovableObject during + /// travel. + /// @param hd The HitData describing the collision in detail. + /// @return Wheter the MovableObject should immediately halt any travel going on + /// after this hit. bool OnMOHit(HitData& hd); - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure v. method: OnBounce - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Defines what should happen when this MovableObject hits and then - // bounces off of something. This is called by the owned Atom/AtomGroup - // of this MovableObject during travel. - // Arguments: The HitData describing the collision in detail. - // Return value: Wheter the MovableObject should immediately halt any travel going on - // after this bounce. - + /// Defines what should happen when this MovableObject hits and then + /// bounces off of something. This is called by the owned Atom/AtomGroup + /// of this MovableObject during travel. + /// @param hd The HitData describing the collision in detail. + /// @return Wheter the MovableObject should immediately halt any travel going on + /// after this bounce. virtual bool OnBounce(HitData& hd) = 0; - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure v. method: OnSink - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Defines what should happen when this MovableObject hits and then - // sink into something. This is called by the owned Atom/AtomGroup - // of this MovableObject during travel. - // Arguments: The HitData describing the collision in detail. - // Return value: Wheter the MovableObject should immediately halt any travel going on - // after this sinkage. - + /// Defines what should happen when this MovableObject hits and then + /// sink into something. This is called by the owned Atom/AtomGroup + /// of this MovableObject during travel. + /// @param hd The HitData describing the collision in detail. + /// @return Wheter the MovableObject should immediately halt any travel going on + /// after this sinkage. virtual bool OnSink(HitData& hd) = 0; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: MoveOutOfTerrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks whether any of the Atom:s in this MovableObject are on top of - // terrain pixels, and if so, attempt to move this out so none of this' - // Atoms are on top of the terrain any more. - // Arguments: Only consider materials stronger than this in the terrain for - // intersections. - // Return value: Whether any intersection was successfully resolved. Will return true - // even if there wasn't any intersections to begin with. - + /// Checks whether any of the Atom:s in this MovableObject are on top of + /// terrain pixels, and if so, attempt to move this out so none of this' + /// Atoms are on top of the terrain any more. + /// @param strongerThan Only consider materials stronger than this in the terrain for (default: g_MaterialAir) + /// intersections. + /// @return Whether any intersection was successfully resolved. Will return true + /// even if there wasn't any intersections to begin with. virtual bool MoveOutOfTerrain(unsigned char strongerThan = g_MaterialAir) { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: RotateOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Rotates a vector offset from this MORotating's position according to - // the rotate angle and flipping. - // Arguments: A const reference the offset Vector to rotate. - // Return value: A new vector that is the result of the rotation. - + /// Rotates a vector offset from this MORotating's position according to + /// the rotate angle and flipping. + /// @param offset A const reference the offset Vector to rotate. + /// @return A new vector that is the result of the rotation. virtual Vector RotateOffset(const Vector& offset) const { return offset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ApplyForces - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gathers and applies the global and accumulated forces. Then it clears - // out the force list.Note that this does NOT apply the accumulated - // impulses (impulse forces)! - // Arguments: None. - // Return value: None. - + /// Gathers and applies the global and accumulated forces. Then it clears + /// out the force list.Note that this does NOT apply the accumulated + /// impulses (impulse forces)! virtual void ApplyForces(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ApplyImpulses - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gathers and applies the accumulated impulse forces. Then it clears - // out the impulse list.Note that this does NOT apply the accumulated - // regular forces (non-impulse forces)! - // Arguments: None. - // Return value: None. - + /// Gathers and applies the accumulated impulse forces. Then it clears + /// out the impulse list.Note that this does NOT apply the accumulated + /// regular forces (non-impulse forces)! virtual void ApplyImpulses(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetForcesCount() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the number of Forces vectors to apply. - // Arguments: None. - // Return value: Number of entries in Forces list. - + /// Returns the number of Forces vectors to apply. + /// @return Number of entries in Forces list. int GetForcesCount() { return m_Forces.size(); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetForceVector() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns force vector in newtons of the specified Force record. - // Arguments: Force record index to get data from. - // Return value: Force vector in newtons of the specified Force record. - + /// Returns force vector in newtons of the specified Force record. + /// @param n Force record index to get data from. + /// @return Force vector in newtons of the specified Force record. Vector GetForceVector(int n) { if (n > 0 && n < m_Forces.size()) return m_Forces[n].first; @@ -1293,19 +726,13 @@ namespace RTE { return Vector(0, 0); } - /// /// Gets the total sum of all forces applied to this MovableObject in a single Vector. - /// - /// The total sum of all forces applied to this MovableObject. + /// @return The total sum of all forces applied to this MovableObject. virtual Vector GetTotalForce(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetForceOffset() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns offset vector in METERS (not pixels) of the specified Force record. - // Arguments: Force record index to get data from. - // Return value: Offset vector in meters of the specified Force record. - + /// Returns offset vector in METERS (not pixels) of the specified Force record. + /// @param n Force record index to get data from. + /// @return Offset vector in meters of the specified Force record. Vector GetForceOffset(int n) { if (n > 0 && n < m_Forces.size()) return m_Forces[n].second; @@ -1313,52 +740,31 @@ namespace RTE { return Vector(0, 0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetForceVector() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets force vector in newtons of the specified Force record. - // Arguments: Force record index to get data from. New Vector force value in newtons. - // Return value: None. - + /// Sets force vector in newtons of the specified Force record. + /// @param n Force record index to get data from. New Vector force value in newtons. void SetForceVector(int n, Vector v) { if (n > 0 && n < m_Forces.size()) m_Forces[n].first = v; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetForceOffset() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets offset vector in METERS (not pixels) of the specified Force record. - // Arguments: Force record index to get data from. New Vector offset value in meters. - // Return value: None. - + /// Sets offset vector in METERS (not pixels) of the specified Force record. + /// @param n Force record index to get data from. New Vector offset value in meters. void SetForceOffset(int n, Vector v) { if (n > 0 && n < m_Forces.size()) m_Forces[n].second = v; } - /// /// Gets the pairs of impulse forces and their offsets that have to be applied. - /// - /// A constant reference to the deque of impulses for this MovableObject. + /// @return A constant reference to the deque of impulses for this MovableObject. const std::deque>& GetImpulses() { return m_ImpulseForces; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetImpulsesCount() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the number of ImpulseForces vectors to apply. - // Arguments: None. - // Return value: Number of entries in ImpulseForces list. - + /// Returns the number of ImpulseForces vectors to apply. + /// @return Number of entries in ImpulseForces list. int GetImpulsesCount() { return m_ImpulseForces.size(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetImpulseVector() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns Impulse vector in newtons of the specified Impulse record. - // Arguments: Impulse record index to get data from. - // Return value: Impulse vector in newtons of the specified Impulse record. - + /// Returns Impulse vector in newtons of the specified Impulse record. + /// @param n Impulse record index to get data from. + /// @return Impulse vector in newtons of the specified Impulse record. Vector GetImpulseVector(int n) { if (n > 0 && n < m_ImpulseForces.size()) return m_ImpulseForces[n].first; @@ -1366,13 +772,9 @@ namespace RTE { return Vector(0, 0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetImpulseOffset() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns offset vector in METERS (not pixels) of the specified Impulse record. - // Arguments: Impulse record index to get data from. - // Return value: Offset vector in meters of the specified Impulse record. - + /// Returns offset vector in METERS (not pixels) of the specified Impulse record. + /// @param n Impulse record index to get data from. + /// @return Offset vector in meters of the specified Impulse record. Vector GetImpulseOffset(int n) { if (n > 0 && n < m_ImpulseForces.size()) return m_ImpulseForces[n].second; @@ -1380,543 +782,318 @@ namespace RTE { return Vector(0, 0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetImpulseVector() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns offset vector in METERS (not pixels) of the specified Impulse record. - // Arguments: Impulse record index to get data from. - // Return value: Offset vector in meters of the specified Impulse record. - + /// Returns offset vector in METERS (not pixels) of the specified Impulse record. + /// @param n Impulse record index to get data from. + /// @return Offset vector in meters of the specified Impulse record. void SetImpulseVector(int n, Vector v) { if (n > 0 && n < m_ImpulseForces.size()) m_ImpulseForces[n].first = v; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetImpulseOffset() - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets offset vector in METERS (not pixels) of the specified Impulse record. - // Arguments: Impulse record index to get data from. New Vector offset value in meters. - // Return value: None. - + /// Sets offset vector in METERS (not pixels) of the specified Impulse record. + /// @param n Impulse record index to get data from. New Vector offset value in meters. void SetImpulseOffset(int n, Vector v) { if (n > 0 && n < m_ImpulseForces.size()) m_ImpulseForces[n].second = v; } - /// /// Gets the number of Sim updates that run between each script update for this MovableObject. - /// - /// The number of Sim updates that run between each script update for this MovableObject. + /// @return The number of Sim updates that run between each script update for this MovableObject. int GetSimUpdatesBetweenScriptedUpdates() const { return m_SimUpdatesBetweenScriptedUpdates; } - /// /// Sets the number of Sim updates that run between each script update for this MovableObject. - /// - /// The new number of Sim updates that run between each script update for this MovableObject. + /// @param newSimUpdatesBetweenScriptedUpdates The new number of Sim updates that run between each script update for this MovableObject. void SetSimUpdatesBetweenScriptedUpdates(int newSimUpdatesBetweenScriptedUpdates) { m_SimUpdatesBetweenScriptedUpdates = std::max(1, newSimUpdatesBetweenScriptedUpdates); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreTravel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does stuff that needs to be done before Travel(). Always call before - // calling Travel. - // Arguments: None. - // Return value: None. - + /// Does stuff that needs to be done before Travel(). Always call before + /// calling Travel. virtual void PreTravel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Travel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Travels this MovableObject, using its physical representation. - // Arguments: None. - // Return value: None. - + /// Travels this MovableObject, using its physical representation. virtual void Travel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PostTravel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does stuff that needs to be done after Travel(). Always call after - // calling Travel. - // Arguments: None. - // Return value: None. - + /// Does stuff that needs to be done after Travel(). Always call after + /// calling Travel. virtual void PostTravel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PreControllerUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Update called prior to controller update. Ugly hack. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Update called prior to controller update. Ugly hack. Supposed to be done every frame. virtual void PreControllerUpdate(){}; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. This also - // applies and clear the accumulated impulse forces (impulses), and the - // transferred forces of MOs attached to this. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. This also + /// applies and clear the accumulated impulse forces (impulses), and the + /// transferred forces of MOs attached to this. void Update() override; void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - /// /// Updates this MovableObject's Lua scripts. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int UpdateScripts(); - /// /// Gets a const reference to this MOSRotating's map of string values. - /// - /// A const reference to this MOSRotating's map of string values. + /// @return A const reference to this MOSRotating's map of string values. const std::unordered_map& GetStringValueMap() const { return m_StringValueMap; } - /// /// Gets a const reference to this MOSRotating's map of number values. - /// - /// A const reference to this MOSRotating's map of number values. + /// @return A const reference to this MOSRotating's map of number values. const std::unordered_map& GetNumberValueMap() const { return m_NumberValueMap; } - /// /// Returns the string value associated with the specified key or "" if it does not exist. - /// - /// Key to retrieve value. - /// The value associated with the key. + /// @param key Key to retrieve value. + /// @return The value associated with the key. const std::string& GetStringValue(const std::string& key) const; - /// /// Returns an encoded string value associated with the specified key or "" if it does not exist. - /// - /// Key to retrieve value. - /// The value associated with the key. + /// @param key Key to retrieve value. + /// @return The value associated with the key. std::string GetEncodedStringValue(const std::string& key) const; - /// /// Returns the number value associated with the specified key or 0 if it does not exist. - /// - /// Key to retrieve value. - /// The value associated with the key. + /// @param key Key to retrieve value. + /// @return The value associated with the key. double GetNumberValue(const std::string& key) const; - /// /// Returns the entity value associated with the specified key or nullptr if it does not exist. - /// - /// Key to retrieve value. - /// The value associated with the key. + /// @param key Key to retrieve value. + /// @return The value associated with the key. Entity* GetObjectValue(const std::string& key) const; - /// /// Sets the string value associated with the specified key. - /// - /// Key to retrieve value. - /// The new value to be associated with the key. + /// @param key Key to retrieve value. + /// @param value The new value to be associated with the key. void SetStringValue(const std::string& key, const std::string& value); - /// /// Sets the string value associated with the specified key. - /// - /// Key to retrieve value. - /// The new value to be associated with the key. + /// @param key Key to retrieve value. + /// @param value The new value to be associated with the key. void SetEncodedStringValue(const std::string& key, const std::string& value); - /// /// Sets the number value associated with the specified key. - /// - /// Key to retrieve value. - /// The new value to be associated with the key. + /// @param key Key to retrieve value. + /// @param value The new value to be associated with the key. void SetNumberValue(const std::string& key, double value); - /// /// Sets the entity value associated with the specified key. - /// - /// Key to retrieve value. - /// The new value to be associated with the key. + /// @param key Key to retrieve value. + /// @param value The new value to be associated with the key. void SetObjectValue(const std::string& key, Entity* value); - /// /// Remove the string value associated with the specified key. - /// - /// The key to remove. + /// @param key The key to remove. void RemoveStringValue(const std::string& key); - /// /// Remove the number value associated with the specified key. - /// - /// The key to remove. + /// @param key The key to remove. void RemoveNumberValue(const std::string& key); - /// /// Remove the entity value associated with the specified key. - /// - /// The key to remove. + /// @param key The key to remove. void RemoveObjectValue(const std::string& key); - /// /// Checks whether the string value associated with the specified key exists. - /// - /// The key to check. - /// Whether or not there is an associated value for this key. + /// @param key The key to check. + /// @return Whether or not there is an associated value for this key. bool StringValueExists(const std::string& key) const; - /// /// Checks whether the number value associated with the specified key exists. - /// - /// The key to check. - /// Whether or not there is an associated value for this key. + /// @param key The key to check. + /// @return Whether or not there is an associated value for this key. bool NumberValueExists(const std::string& key) const; - /// /// Checks whether the entity value associated with the specified key exists. - /// - /// The key to check. - /// Whether or not there is an associated value for this key. + /// @param key The key to check. + /// @return Whether or not there is an associated value for this key. bool ObjectValueExists(const std::string& key) const; - /// /// Event listener to be run while this MovableObject's PieMenu is opened. - /// - /// The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param pieMenu The PieMenu this event listener needs to listen to. This will always be this' m_PieMenu and only exists for std::bind. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int WhilePieMenuOpenListener(const PieMenu* pieMenu); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this' and its its childrens' MOID's and foorprint. Should - // be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this' and its its childrens' MOID's and foorprint. Should + /// be done every frame. void UpdateMOID(std::vector& MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawMOIDIfOverlapping - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the MOID representation of this to the SceneMan's MOID layer if - // this is found to potentially overlap another MovableObject. - // Arguments: The MovableObject to check this for overlap against. - // Return value: Whether it was drawn or not. - + /// Draws the MOID representation of this to the SceneMan's MOID layer if + /// this is found to potentially overlap another MovableObject. + /// @param pOverlapMO The MovableObject to check this for overlap against. + /// @return Whether it was drawn or not. virtual bool DrawMOIDIfOverlapping(MovableObject* pOverlapMO) { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this' current graphical HUD overlay representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the draw bitmap's upper left corner in the Scene. - // Which player's screen this is being drawn to. May affect what HUD elements - // get drawn etc. - // Return value: None. - + /// Draws this' current graphical HUD overlay representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the draw bitmap's upper left corner in the Scene. (default: Vector()) + /// @param whichScreen Which player's screen this is being drawn to. May affect what HUD elements (default: 0) + /// get drawn etc. virtual void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) { return; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRestThreshold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns current rest threshold for this MO - // Arguments: None - // Return value: Rest threshold of this MO - + /// Returns current rest threshold for this MO + /// @return Rest threshold of this MO int GetRestThreshold() const { return m_RestThreshold; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetRestThreshold - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets current rest threshold for this MO - // Arguments: New rest threshold value - // Return value: None - + /// Sets current rest threshold for this MO + /// @param newRestThreshold New rest threshold value void SetRestThreshold(int newRestThreshold) { m_RestThreshold = newRestThreshold; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Static method: GetNextID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the next unique id for MO's and increments unique ID counter - // Arguments: None. - // Return value: Returns the next unique id. - + /// Returns the next unique id for MO's and increments unique ID counter + /// @return Returns the next unique id. static unsigned long int GetNextUniqueID() { return ++m_UniqueIDCounter; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Static method: GetUniqueID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns this MO's unique persistent ID - // Arguments: None. - // Return value: Returns this MO's unique persistent ID - + /// Returns this MO's unique persistent ID + /// @return Returns this MO's unique persistent ID unsigned long int const GetUniqueID() const { return m_UniqueID; } - /// /// Gets the preset name and unique ID of this MO, often useful for error messages. - /// - /// A string containing the unique ID and preset name of this MO. + /// @return A string containing the unique ID and preset name of this MO. std::string GetPresetNameAndUniqueID() const { return m_PresetName + ", UID: " + std::to_string(m_UniqueID); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DamageOnCollision - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: If not zero applyies specified ammount of damage points to actors on - // collision even without penetration. - // Arguments: None - // Return value: Amount of damage to apply. - + /// If not zero applyies specified ammount of damage points to actors on + /// collision even without penetration. + /// @return Amount of damage to apply. float DamageOnCollision() const { return m_DamageOnCollision; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetDamageOnCollision - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: If not zero applyies specified ammount of damage points to actors on - // collision even without penetration. - // Arguments: Amount of damage to apply. - // Return value: None. - + /// If not zero applyies specified ammount of damage points to actors on + /// collision even without penetration. + /// @param value Amount of damage to apply. void SetDamageOnCollision(float value) { m_DamageOnCollision = value; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DamageOnPenetration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: If not zero applies specified ammount of damage points to actors on - // collision if penetration occured. - // Arguments: None - // Return value: Amount of damage to apply. - + /// If not zero applies specified ammount of damage points to actors on + /// collision if penetration occured. + /// @return Amount of damage to apply. float DamageOnPenetration() const { return m_DamageOnPenetration; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetDamageOnPenetration - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: If not zero applies specified ammount of damage points to actors on - // collision if penetration occured. - // Arguments: Amount of damage to apply. - // Return value: None. - + /// If not zero applies specified ammount of damage points to actors on + /// collision if penetration occured. + /// @param value Amount of damage to apply. void SetDamageOnPenetration(float value) { m_DamageOnPenetration = value; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: WoundDamageMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns damage multiplier transferred to wound inflicted by this object on penetration - // Arguments: None - // Return value: Damage multiplier to apply to wound. - + /// Returns damage multiplier transferred to wound inflicted by this object on penetration + /// @return Damage multiplier to apply to wound. float WoundDamageMultiplier() const { return m_WoundDamageMultiplier; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetWoundDamageMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets damage multiplier transferred to wound inflicted by this object on penetration - // Arguments: New damage multiplier to apply to wound. - // Return value: None. - + /// Sets damage multiplier transferred to wound inflicted by this object on penetration + /// @param value New damage multiplier to apply to wound. void SetWoundDamageMultiplier(float value) { m_WoundDamageMultiplier = value; } - /// /// Gets whether or not this MovableObject should apply wound damage when it collides with another MovableObject. - /// - /// Whether or not this MovableObject should apply wound damage when it collides with another MovableObject. + /// @return Whether or not this MovableObject should apply wound damage when it collides with another MovableObject. bool GetApplyWoundDamageOnCollision() const { return m_ApplyWoundDamageOnCollision; } - /// /// Sets whether or not this MovableObject should apply wound damage when it collides with another MovableObject. - /// - /// Whether or not this MovableObject should apply wound damage on collision. + /// @param applyWoundDamageOnCollision Whether or not this MovableObject should apply wound damage on collision. void SetApplyWoundDamageOnCollision(bool applyWoundDamageOnCollision) { m_ApplyWoundDamageOnCollision = applyWoundDamageOnCollision; } - /// /// Gets whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. - /// - /// Whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. + /// @return Whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. bool GetApplyWoundBurstDamageOnCollision() const { return m_ApplyWoundBurstDamageOnCollision; } - /// /// Sets whether or not this MovableObject should apply burst wound damage when it collides with another MovableObject. - /// - /// Whether or not this MovableObject should apply burst wound damage on collision. + /// @param applyWoundDamageOnCollision Whether or not this MovableObject should apply burst wound damage on collision. void SetApplyWoundBurstDamageOnCollision(bool applyWoundBurstDamageOnCollision) { m_ApplyWoundBurstDamageOnCollision = applyWoundBurstDamageOnCollision; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector - // Arguments: Vector to store MOIDs - // Return value: None. - + /// Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector + /// @param MOIDs Vector to store MOIDs virtual void GetMOIDs(std::vector& MOIDs) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HitWhatMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the ID of the MO hit at the previously taken Travel - // This will only potentially return non-g_NoMOID if this object's Atom is set to - // hit MO's and the MO hit isn't marked to be ignored. - // Arguments: None. - // Return value: The ID of the non-ignored MO, if any, that this object's Atom or AtomGroup is now - // intersecting because of the last Travel taken. - + /// Returns the ID of the MO hit at the previously taken Travel + /// This will only potentially return non-g_NoMOID if this object's Atom is set to + /// hit MO's and the MO hit isn't marked to be ignored. + /// @return The ID of the non-ignored MO, if any, that this object's Atom or AtomGroup is now + /// intersecting because of the last Travel taken. MOID HitWhatMOID() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetHitWhatMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the ID of the MO hit at the previously taken Travel - // This will only potentially return non-g_NoMOID if this object's Atom is set to - // hit MO's and the MO hit isn't marked to be ignored. - // Arguments: The ID of the non-ignored MO, if any, that this object's Atom or AtomGroup is now - // intersecting because of the last Travel taken. - // Return value: None. - + /// Sets the ID of the MO hit at the previously taken Travel + /// This will only potentially return non-g_NoMOID if this object's Atom is set to + /// hit MO's and the MO hit isn't marked to be ignored. + /// @param id The ID of the non-ignored MO, if any, that this object's Atom or AtomGroup is now + /// intersecting because of the last Travel taken. void SetHitWhatMOID(MOID id); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HitWhatMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the unique ID of the particle hit at the previously taken Travel - // Arguments: None. - // Return value: Unique ID of the particle hit at the previously taken Travel - + /// Returns the unique ID of the particle hit at the previously taken Travel + /// @return Unique ID of the particle hit at the previously taken Travel long int HitWhatParticleUniqueID() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HitWhatMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the unique ID of the particle hit at the previously taken Travel - // Arguments: Unique ID of the particle hit at the previously taken Travel. - // Return value: None. - + /// Returns the unique ID of the particle hit at the previously taken Travel + /// @param id Unique ID of the particle hit at the previously taken Travel. void SetHitWhatParticleUniqueID(long int id); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HitWhatTerrMaterial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the terrain material the previously taken Tarvel - // hit, if any. - // Arguments: None. - // Return value: The ID of the material, if any, that this MO hit during the last Travel. - + /// Returns the terrain material the previously taken Tarvel + /// hit, if any. + /// @return The ID of the material, if any, that this MO hit during the last Travel. unsigned char HitWhatTerrMaterial() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetHitWhatTerrMaterial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the terrain material the previously taken Tarvel hit, if any. - // Arguments: The ID of the material, if any, that this MO hit during the last Travel. - // Return value: None. - + /// Sets the terrain material the previously taken Tarvel hit, if any. + /// @param matID The ID of the material, if any, that this MO hit during the last Travel. void SetHitWhatTerrMaterial(unsigned char matID); - /// /// Gets whether this MO's RootParent can GetHitByMOs and is currently traveling. - /// - /// Whether this MO's RootParent can GetHitByMOs and is currently traveling. + /// @return Whether this MO's RootParent can GetHitByMOs and is currently traveling. bool GetTraveling() const { return GetRootParent()->m_IsTraveling; } - /// /// Sets whether this MO's RootParent is currently traveling. - /// - /// Whether this MO's RootParent is currently traveling. + /// @param newValue Whether this MO's RootParent is currently traveling. void SetTraveling(bool newValue) { GetRootParent()->m_IsTraveling = newValue; } - /// /// Draws this MovableObject's graphical and material representations to the specified SLTerrain's respective layers. - /// - /// The SLTerrain to draw this MovableObject to. Ownership is NOT transferred! - /// Whether the object was successfully drawn to the terrain. + /// @param terrain The SLTerrain to draw this MovableObject to. Ownership is NOT transferred! + /// @return Whether the object was successfully drawn to the terrain. bool DrawToTerrain(SLTerrain* terrain); - /// /// Used to get the Lua state that handles our scripts. - /// - /// Our lua state. Can potentially be nullptr if we're not setup yet. + /// @return Our lua state. Can potentially be nullptr if we're not setup yet. LuaStateWrapper* GetLuaState() { return m_ThreadedLuaState; } - /// /// Method to be run when the game is saved via ActivityMan::SaveCurrentGame. Not currently used in metagame or editor saving. - /// virtual void OnSave() { RunScriptedFunctionInAppropriateScripts("OnSave"); } - /// /// Requests a synced update for the MO this frame. - /// virtual void RequestSyncedUpdate() { m_RequestedSyncedUpdate = true; } - /// /// Resets the requested update flag. - /// virtual void ResetRequestedSyncedUpdateFlag() { m_RequestedSyncedUpdate = false; } - /// /// Returns whether this MO has requested a synced update this frame. - /// - /// Whether this MO has requested a synced update this frame. + /// @return Whether this MO has requested a synced update this frame. virtual bool HasRequestedSyncedUpdate() { return m_RequestedSyncedUpdate; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Does necessary work to setup a script object name for this object, allowing it to be accessed in Lua, then runs all of the MO's scripts' Create functions in Lua. - /// - /// 0 on success, -2 if it fails to setup the script object in Lua, and -3 if it fails to run any Create function. + /// @return 0 on success, -2 if it fails to setup the script object in Lua, and -3 if it fails to run any Create function. int InitializeObjectScripts(); - /// /// Runs the given function for the given script, with the given arguments. The first argument to the function will always be 'self'. /// If either argument list is not empty, its entries will be passed into the Lua function in order, with entity arguments first. - /// - /// The path to the script to run. - /// The name of the function to run. - /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. - /// Optional vector of strings, that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param scriptPath The path to the script to run. + /// @param functionName The name of the function to run. + /// @param functionEntityArguments Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. + /// @param functionLiteralArguments Optional vector of strings, that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int RunFunctionOfScript(const std::string& scriptPath, const std::string& functionName, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector()); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: UpdateChildMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes this MO register itself and all its attached children in the - // MOID register and get ID:s for itself and its children for this frame. - // Arguments: The MOID index to register itself and its children in. - // The MOID of the root MO of this MO, ie the highest parent of this MO. - // 0 means that this MO is the root, ie it is owned by MovableMan. - // Whether this MO should make a new MOID to use for itself, or to use - // the same as the last one in the index (presumably its parent), - // Return value: None. - + /// Makes this MO register itself and all its attached children in the + /// MOID register and get ID:s for itself and its children for this frame. + /// @param MOIDIndex The MOID index to register itself and its children in. + /// @param rootMOID The MOID of the root MO of this MO, ie the highest parent of this MO. (default: g_NoMOID) + /// 0 means that this MO is the root, ie it is owned by MovableMan. + /// @param makeNewMOID Whether this MO should make a new MOID to use for itself, or to use (default: true) + /// the same as the last one in the index (presumably its parent), virtual void UpdateChildMOIDs(std::vector& MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: RegMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes this MO register itself in the MOID register and get ID:s for - // itself and its children for this frame. - // BITMAP of choice. - // Arguments: The MOID index to register itself and its children in. - // The MOID of the root MO of this MO, ie the highest parent of this MO. - // 0 means that this MO is the root, ie it is owned by MovableMan. - // Whether this MO should make a new MOID to use for itself, or to use - // the same as the last one in the index (presumably its parent), - // Return value: None. - + /// Makes this MO register itself in the MOID register and get ID:s for + /// itself and its children for this frame. + /// BITMAP of choice. + /// @param MOIDIndex The MOID index to register itself and its children in. + /// @param rootMOID The MOID of the root MO of this MO, ie the highest parent of this MO. (default: g_NoMOID) + /// 0 means that this MO is the root, ie it is owned by MovableMan. + /// @param makeNewMOID Whether this MO should make a new MOID to use for itself, or to use (default: true) + /// the same as the last one in the index (presumably its parent), void RegMOID(std::vector& MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MovableObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Copy constructor method used to instantiate a MovableObject object - // identical to an already existing one. - // Arguments: A MovableObject object which is passed in by reference. - + /// Copy constructor method used to instantiate a MovableObject object + /// identical to an already existing one. + /// @param A MovableObject object which is passed in by reference. // Member variables static Entity::ClassInfo m_sClass; // Global counter with unique ID's @@ -2080,28 +1257,20 @@ namespace RTE { int m_SimUpdatesBetweenScriptedUpdates; //!< The number of Sim updates between each scripted update for this MovableObject. int m_SimUpdatesSinceLastScriptedUpdate; //!< The counter for the current number of Sim updates since this MovableObject last ran a scripted update. - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - /// /// Clears all the member variables of this MovableObject, effectively resetting the members of this abstraction level only. - /// void Clear(); - /// /// Handles reading for custom values, dealing with the various types of custom values. - /// - /// A Reader lined up to the custom value type to be read. + /// @param reader A Reader lined up to the custom value type to be read. void ReadCustomValueProperty(Reader& reader); - /// /// Returns the script state to use for a given script path. /// This will be locked to our thread and safe to use - ensure that it'll be unlocked after use! - /// - /// The path to the script to check for thread safety. - /// A LuaFunction, to use as an early-out check instead of redundantly hashing and checking the filepath string. - /// A script state. + /// @param scriptPath The path to the script to check for thread safety. + /// @param function A LuaFunction, to use as an early-out check instead of redundantly hashing and checking the filepath string. + /// @return A script state. LuaStateWrapper& GetAndLockStateForScript(const std::string& scriptPath, const LuaFunction* function = nullptr); // Disallow the use of some implicit methods. diff --git a/Source/Entities/PEmitter.cpp b/Source/Entities/PEmitter.cpp index 81bcc656a3..56d460ee8e 100644 --- a/Source/Entities/PEmitter.cpp +++ b/Source/Entities/PEmitter.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: PEmitter.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the PEmitter class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "PEmitter.h" #include "Atom.h" #include "RTETools.h" @@ -20,12 +8,6 @@ namespace RTE { ConcreteClassInfo(PEmitter, MOSParticle, 100); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this PEmitter, effectively - // resetting the members of this abstraction level only. - void PEmitter::Clear() { m_EmissionList.clear(); m_EmissionSound.Reset(); @@ -57,11 +39,6 @@ namespace RTE { m_LoudnessOnEmit = 1.0f; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Emission object ready for use. - int PEmitter::Create() { if (MOSParticle::Create() < 0) return -1; @@ -69,11 +46,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a PEmitter to be identical to another, by deep copy. - int PEmitter::Create(const PEmitter& reference) { MOSParticle::Create(reference); @@ -104,14 +76,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int PEmitter::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return MOSParticle::ReadProperty(propName, reader)); @@ -161,12 +125,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this PEmitter with a Writer for - // later recreation with Create(Reader &reader); - int PEmitter::Save(Writer& writer) const { MOSParticle::Save(writer); @@ -218,11 +176,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the PEmitter object. - void PEmitter::Destroy(bool notInherited) { // Stop playback of sounds gracefully if (m_EmissionSound.IsBeingPlayed()) @@ -238,23 +191,12 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetEmissionTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reset the timers of all emissions so they will start/stop at the - // correct relative offsets from now. - void PEmitter::ResetEmissionTimers() { m_LastEmitTmr.Reset(); for (auto eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) (*eItr).ResetEmissionTimers(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableEmission - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this PEmitter to start emitting at the set rate, or to stop. - void PEmitter::EnableEmission(bool enable) { if (!m_EmitEnabled && enable) { m_LastEmitTmr.Reset(); @@ -266,11 +208,6 @@ namespace RTE { m_EmitEnabled = enable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EstimateImpulse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the forces this emitter applies on any parent. - float PEmitter::EstimateImpulse(bool burst) { // Calculate the impulse generated by the emissions, once and store the result if ((!burst && m_AvgImpulse < 0) || (burst && m_AvgBurstImpulse < 0)) { @@ -325,11 +262,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this PEmitter. Supposed to be done every frame. - void PEmitter::Update() { MOSParticle::Update(); @@ -474,12 +406,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this PEmitter's current graphical representation to a - // BITMAP of choice. - void PEmitter::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, diff --git a/Source/Entities/PEmitter.h b/Source/Entities/PEmitter.h index 8395f77cb7..8fe3302c6f 100644 --- a/Source/Entities/PEmitter.h +++ b/Source/Entities/PEmitter.h @@ -1,36 +1,21 @@ #ifndef _RTEPEMITTER_ #define _RTEPEMITTER_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: PEmitter.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the PEmitter class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the PEmitter class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "MOSParticle.h" #include "Emission.h" #include "SoundContainer.h" namespace RTE { - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: PEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A particle MO that creates and emits particle MOs. - // Parent(s): MOSParticle. - // Class history: 02/29/2004 PEmitter created. - + /// A particle MO that creates and emits particle MOs. class PEmitter : public MOSParticle { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: friend struct EntityLuaBindings; @@ -39,108 +24,56 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: PEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a PEmitter object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a PEmitter object in system + /// memory. Create() should be called before using the object. PEmitter() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~PEmitter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a PEmitter object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a PEmitter object before deletion + /// from system memory. ~PEmitter() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the PEmitter object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the PEmitter object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a PEmitter to be identical to another, by deep copy. - // Arguments: A reference to the PEmitter to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a PEmitter to be identical to another, by deep copy. + /// @param reference A reference to the PEmitter to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const PEmitter& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire PEmitter, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire PEmitter, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); MOSParticle::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneLayer object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneLayer object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEmitting - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this PEmitter is currently enabled and emitting. - // Arguments: None. - // Return value: Whether it's emitting or not. - + /// Indicates whether this PEmitter is currently enabled and emitting. + /// @return Whether it's emitting or not. bool IsEmitting() const { return m_EmitEnabled; } - /// /// Returns whether this emitter was emitting last frame. - /// - /// Whether this emitter was emitting last frame. + /// @return Whether this emitter was emitting last frame. bool WasEmitting() const { return m_WasEmitting; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetEmissionTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reset the timers of all emissions so they will start/stop at the - // correct relative offsets from now. - // Arguments: None. - // Return value: None. - + /// Reset the timers of all emissions so they will start/stop at the + /// correct relative offsets from now. void ResetEmissionTimers(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableEmission - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this PEmitter to start emitting at the set rate, or to stop. - // Arguments: Whether to enable or disable emission. - // Return value: None. - + /// Sets this PEmitter to start emitting at the set rate, or to stop. + /// @param enable Whether to enable or disable emission. (default: true) void EnableEmission(bool enable = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EstimateImpulse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the forces this emitter applies on any parent. - // Arguments: Whether to calculate a burst update or not. - // Return value: The approximate impulse generated by the emitter. - + /// Calculates the forces this emitter applies on any parent. + /// @param burst Whether to calculate a burst update or not. (default: false) + /// @return The approximate impulse generated by the emitter. float EstimateImpulse(bool burst = false); /* @@ -166,52 +99,27 @@ namespace RTE { int GetBurstCount() const { return m_BurstSize; } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the scale factor that will be applied to the regular spread and - // emission velocity to get the burst particle parameters. - // Arguments: None. - // Return value: The scale factor. - + /// Gets the scale factor that will be applied to the regular spread and + /// emission velocity to get the burst particle parameters. + /// @return The scale factor. float GetBurstScale() const { return m_BurstScale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the angle of direction that the emitted particles will be shot at. - // Arguments: None. - // Return value: A float with the angle in radians. - + /// Gets the angle of direction that the emitted particles will be shot at. + /// @return A float with the angle in radians. float GetEmitAngle() const { return m_EmitAngle.GetRadAngle(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEmitVector - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A vector in the direction, including the rotation of the emitter, that - // the emitted particles will be shot at. - // Arguments: None. - // Return value: A unit vector. - + /// A vector in the direction, including the rotation of the emitter, that + /// the emitted particles will be shot at. + /// @return A unit vector. Vector GetEmitVector() const { return Vector(1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRecoilVector - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A vector in the opposite direction, including the rotation of the - // emitter, that the emitted particles will be shot at. - // Arguments: None. - // Return value: A unit vector. - + /// A vector in the opposite direction, including the rotation of the + /// emitter, that the emitted particles will be shot at. + /// @return A unit vector. Vector GetRecoilVector() const { return Vector(-1, 0).RadRotate(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBurstSpacing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the BurstSpacing for this emitter. - // Arguments: None. - // Return value: The BurstSpacing in ms. - + /// Gets the BurstSpacing for this emitter. + /// @return The BurstSpacing in ms. float GetBurstSpacing() const { return m_BurstSpacing; } /* @@ -250,28 +158,19 @@ namespace RTE { float GetEmitVelMax() const { return m_MaxVelocity; } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetThrottle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the normalized throttle scalar which controls how to affect the - // emission rate as per the emisison rate range. Depricated for Lua, use - // the Throttle property instead. - // Arguments: None. - // Return value: A float with the normalized throttle scalar. 1.0 means max throttle, - // 0 means normal, -1.0 means least emission rate. - + /// Gets the normalized throttle scalar which controls how to affect the + /// emission rate as per the emisison rate range. Depricated for Lua, use + /// the Throttle property instead. + /// @return A float with the normalized throttle scalar. 1.0 means max throttle, + /// 0 means normal, -1.0 means least emission rate. float GetThrottle() const { return m_Throttle; } - /// /// Gets the adjusted throttle multiplier that is factored into the emission rate of this PEmitter. - /// - /// The throttle strength as a multiplier. + /// @return The throttle strength as a multiplier. float GetThrottleFactor() const { return LERP(-1.0f, 1.0f, m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, m_Throttle); } - /// /// Gets the throttle value that will achieve a given throttle factor that is factored into the emission rate of this AEmitter. - /// - /// The throttle value that will achieve the given throttle factor. + /// @return The throttle value that will achieve the given throttle factor. float GetThrottleForThrottleFactor(float throttleFactor) const { return LERP(m_NegativeThrottleMultiplier, m_PositiveThrottleMultiplier, -1.0f, 1.0f, throttleFactor); } /* @@ -297,59 +196,32 @@ namespace RTE { void SetBurstCount(const int count) { m_BurstSize = count; } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the scale factor that will be applied to the regular spread and - // emission velocity to get the burst particle parameters. - // Arguments: The scale factor. - // Return value: None. - + /// Sets the scale factor that will be applied to the regular spread and + /// emission velocity to get the burst particle parameters. + /// @param scale The scale factor. void SetBurstScale(const float scale) { m_BurstScale = scale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBurstSpacing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the BurstSpacing for this emitter. - // Arguments: The BurstSpacing in ms. - // Return value: None. - + /// Sets the BurstSpacing for this emitter. + /// @param spacing The BurstSpacing in ms. void SetBurstSpacing(const float spacing) { m_BurstSpacing = spacing; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFlashScale - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the display scale factor of the flash effect. This is purely - // visual. - // Arguments: The scale factor of the flash draw. - // Return value: None. - + /// Sets the display scale factor of the flash effect. This is purely + /// visual. + /// @param flashScale The scale factor of the flash draw. (default: 1.0f) void SetFlashScale(float flashScale = 1.0f) { m_FlashScale = flashScale; } - /// /// Gets the display scale factor of the flash effect. This is purely visual. - /// - /// The scale factor of the flash draw. + /// @return The scale factor of the flash draw. float GetFlashScale() const { return m_FlashScale; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEmitAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the angle of direction that the emitted particles will be shot at. - // Arguments: A float with the angle in radians. - // Return value: None. - + /// Sets the angle of direction that the emitted particles will be shot at. + /// @param m_EmitAngle.SetRadAngle(angle A float with the angle in radians. void SetEmitAngle(const float angle) { m_EmitAngle.SetRadAngle(angle); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetThrottle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the normalized throttle scalar which controls how to affect the - // emission rate as per the emisison rate range. - // Arguments: A float with the normalized throttle scalar. 1.0 means max throttle, - // 0 means normal, -1.0 means least emission rate. - // Return value: None. - + /// Sets the normalized throttle scalar which controls how to affect the + /// emission rate as per the emisison rate range. + /// @param m_Throttle A float with the normalized throttle scalar. 1.0 means max throttle, (default: throttle > 1.0f ? 1.0f : (throttle < -1.0f ? -1.0f : throttle) + /// 0 means normal, -1.0 means least emission rate. void SetThrottle(float throttle) { m_Throttle = throttle > 1.0f ? 1.0f : (throttle < -1.0f ? -1.0f : throttle); } /* @@ -388,111 +260,65 @@ namespace RTE { void SetEmitVelMax(const float maxVel) { m_MaxVelocity = maxVel; } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TriggerBurst - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Triggers a one-shot burst of emissions in the number that has - // previously been set. The burst will happen during the next Update of - // this PEmitter. - // Arguments: None. - // Return value: None. - + /// Triggers a one-shot burst of emissions in the number that has + /// previously been set. The burst will happen during the next Update of + /// this PEmitter. void TriggerBurst() { m_BurstTriggered = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CanTriggerBurst - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if it is possible to trigger a one-shot burst of emissions during - // the next Update of this PEmitter. - // Arguments: None. - // Return value: If it is possible to trigger a burst. - + /// Checks if it is possible to trigger a one-shot burst of emissions during + /// the next Update of this PEmitter. + /// @return If it is possible to trigger a burst. bool CanTriggerBurst() { if (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing)) return true; return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsSetToBurst - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this PEmitter is set to burst next update or not. - // Arguments: None. - // Return value: Whether a burst is gonna happen or not.. - + /// Indicates whether this PEmitter is set to burst next update or not. + /// @return Whether a burst is gonna happen or not.. bool IsSetToBurst() const { return m_BurstTriggered; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AlarmOnEmit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registers a new AlarmEvent if this emitter has a loudness above zero. - // Arguments: Team that will ignore this AlarmEvent. - // Return value: None. - + /// Registers a new AlarmEvent if this emitter has a loudness above zero. + /// @param Team Team that will ignore this AlarmEvent. void AlarmOnEmit(int Team) const { if (m_LoudnessOnEmit > 0) g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, Team, m_LoudnessOnEmit)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ResetAllTimers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resest all the timers used by this. Can be emitters, etc. This is to - // prevent backed up emissions to come out all at once while this has been - // held dormant in an inventory. - // Arguments: None. - // Return value: None. - + /// Resest all the timers used by this. Can be emitters, etc. This is to + /// prevent backed up emissions to come out all at once while this has been + /// held dormant in an inventory. void ResetAllTimers() override { m_BurstTimer.Reset(); m_LastEmitTmr.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this MovableObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this MovableObject. Supposed to be done every frame. void Update() override; void PostUpdate() override { MOSParticle::PostUpdate(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this PEmitter's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this PEmitter's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - /// /// Gets the number of emissions left before emitter is disabled. - /// - /// The number of emissions left before emitter is disabled. + /// @return The number of emissions left before emitter is disabled. long GetEmitCountLimit() const { return m_EmitCountLimit; } - /// /// Sets the number of emissions left before emitter is disabled. - /// - /// New number of emissions left. + /// @param newValue New number of emissions left. void SetEmitCountLimit(long newValue) { m_EmitCountLimit = newValue; } - /// /// Returns whether this emitter just started emitting this frame. - /// - /// Whether this emitter just started emitting this frame. + /// @return Whether this emitter just started emitting this frame. bool JustStartedEmitting() const { return !m_WasEmitting && m_EmitEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -550,18 +376,10 @@ namespace RTE { // Whether the burst sound follows the emitter bool m_BurstSoundFollowsEmitter; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this PEmitter, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this PEmitter, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/PieMenu.cpp b/Source/Entities/PieMenu.cpp index d25f8bbbb7..bd6e1bad40 100644 --- a/Source/Entities/PieMenu.cpp +++ b/Source/Entities/PieMenu.cpp @@ -41,8 +41,6 @@ namespace RTE { BITMAP* PieMenu::s_CursorBitmap = nullptr; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::Clear() { m_LargeFont = nullptr; @@ -93,8 +91,6 @@ namespace RTE { m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieMenu::Create() { if (!s_CursorBitmap) { s_CursorBitmap = ContentFile("Base.rte/GUIs/PieMenus/PieCursor.png").GetAsBitmap(); @@ -111,8 +107,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieMenu::Create(const PieMenu& reference) { Entity::Create(reference); @@ -160,8 +154,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::Destroy(bool notInherited) { if (!notInherited) { Entity::Destroy(); @@ -172,8 +164,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieMenu::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -209,8 +199,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieMenu::Save(Writer& writer) const { Entity::Save(writer); @@ -232,8 +220,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::SetOwner(Actor* newOwner) { RTEAssert((newOwner == nullptr) ? true : (newOwner->GetPieMenu() == this || IsSubPieMenu()), "Tried to set Pie Menu owning Actor to Actor with different Pie Menu."); if (m_Owner) { @@ -252,8 +238,6 @@ namespace RTE { m_Owner = newOwner; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Controller* PieMenu::GetController() const { if (m_MenuController) { return m_MenuController; @@ -265,16 +249,12 @@ namespace RTE { return m_Owner->GetController(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::SetPos(const Vector& newPos) { if (g_SceneMan.ShortestDistance(m_CenterPos, newPos, g_SceneMan.SceneWrapsX()).GetMagnitude() > 2.0F) { m_CenterPos = newPos; } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::SetEnabled(bool enable, bool playSounds) { m_MenuMode = MenuMode::Normal; @@ -304,8 +284,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const PieSlice* PieMenu::GetActivatedPieSlice() const { if (m_ActiveSubPieMenu) { return m_ActiveSubPieMenu->GetActivatedPieSlice(); @@ -313,15 +291,11 @@ namespace RTE { return m_ActivatedPieSlice; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice::SliceType PieMenu::GetPieCommand() const { const PieSlice* activatedSlice = m_ActiveSubPieMenu ? m_ActiveSubPieMenu->GetActivatedPieSlice() : m_ActivatedPieSlice; return (activatedSlice == nullptr) ? PieSlice::SliceType::NoType : activatedSlice->GetType(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice* PieMenu::GetFirstPieSliceByPresetName(const std::string& presetName) const { for (PieSlice* pieSlice: m_CurrentPieSlices) { if (pieSlice->GetPresetName() == presetName) { @@ -331,8 +305,6 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice* PieMenu::GetFirstPieSliceByType(PieSlice::SliceType pieSliceType) const { for (PieSlice* pieSlice: m_CurrentPieSlices) { if (pieSlice->GetType() == pieSliceType) { @@ -342,8 +314,6 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::AddPieSlice(PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool allowQuadrantOverflow) { Directions pieSliceDirection = pieSliceToAdd->GetDirection(); if (pieSliceDirection != Directions::Any && !m_PieQuadrants.at(pieSliceDirection).m_Enabled) { @@ -381,8 +351,6 @@ namespace RTE { return sliceWasAdded; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::AddPieSliceIfPresetNameIsUnique(PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource, bool allowQuadrantOverflow) { const std::string& pieSlicePresetName = pieSliceToAdd->GetPresetName(); @@ -407,8 +375,6 @@ namespace RTE { return AddPieSlice(dynamic_cast(pieSliceToAdd->Clone()), pieSliceOriginalSource, allowQuadrantOverflow); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice* PieMenu::RemovePieSlice(const PieSlice* pieSliceToRemove) { PieSlice* removedPieSlice = nullptr; @@ -435,8 +401,6 @@ namespace RTE { return removedPieSlice; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::RemovePieSlicesByPresetName(const std::string& presetNameToRemoveBy) { bool anyPieSlicesRemoved = false; @@ -454,8 +418,6 @@ namespace RTE { return anyPieSlicesRemoved; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::RemovePieSlicesByType(PieSlice::SliceType pieSliceTypeToRemoveBy) { bool anyPieSlicesRemoved = false; @@ -473,8 +435,6 @@ namespace RTE { return anyPieSlicesRemoved; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::RemovePieSlicesByOriginalSource(const Entity* originalSource) { bool anyPieSlicesRemoved = false; @@ -492,8 +452,6 @@ namespace RTE { return anyPieSlicesRemoved; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice* PieMenu::ReplacePieSlice(const PieSlice* pieSliceToReplace, PieSlice* replacementPieSlice) { if (pieSliceToReplace == nullptr) { return nullptr; @@ -561,8 +519,6 @@ namespace RTE { return replacedPieSlice; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::Update() { const Controller* controller = GetController(); @@ -665,8 +621,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::Draw(BITMAP* targetBitmap, const Vector& targetPos) const { Vector drawPos; CalculateDrawPosition(targetBitmap, targetPos, drawPos); @@ -692,8 +646,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::UpdateWobbling() { float innerRadiusChange = static_cast(m_EnableDisableAnimationTimer.GetElapsedRealTimeMS()) / 6.0F; @@ -710,8 +662,6 @@ namespace RTE { m_EnableDisableAnimationTimer.Reset(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::UpdateEnablingAndDisablingProgress() { m_BGBitmapNeedsRedrawing = true; if (m_EnabledState == EnabledState::Enabling) { @@ -733,8 +683,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::HandleAnalogInput(const Vector& input) { const Controller* controller = GetController(); const PieSlice* pieSliceToSelect = nullptr; @@ -761,8 +709,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::HandleDigitalInput() { const Controller* controller = GetController(); if (!controller) { @@ -859,8 +805,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::UpdateSliceActivation() { const Controller* controller = GetController(); if (controller && (controller->IsState(ControlState::PRESS_PRIMARY) || (m_HoveredPieSlice != m_AlreadyActivatedPieSlice && controller->IsState(ControlState::RELEASE_SECONDARY)))) { @@ -898,8 +842,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::UpdatePredrawnMenuBackgroundBitmap() { int centerX = IsSubPieMenu() ? 0 : m_BGBitmap->w / 2; int centerY = IsSubPieMenu() ? 0 : m_BGBitmap->h / 2; @@ -956,8 +898,6 @@ namespace RTE { m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::CalculateDrawPosition(const BITMAP* targetBitmap, const Vector& targetPos, Vector& drawPos) const { drawPos = m_CenterPos - targetPos; if (!targetPos.IsZero()) { @@ -1005,8 +945,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::DrawPieIcons(BITMAP* targetBitmap, const Vector& drawPos) const { for (const PieSlice* pieSlice: m_CurrentPieSlices) { BITMAP* pieSliceIcon = pieSlice->GetAppropriateIcon(pieSlice == m_HoveredPieSlice); @@ -1037,8 +975,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::DrawPieCursorAndPieSliceDescriptions(BITMAP* targetBitmap, const Vector& drawPos) const { int nonLineSeparatorCorrection = m_IconSeparatorMode != IconSeparatorMode::Line ? -(m_BackgroundSeparatorSize) : 0; Vector cursorPos = Vector(static_cast(m_CurrentInnerRadius + nonLineSeparatorCorrection), 0.0F).RadRotate(m_CursorAngle); @@ -1060,8 +996,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::RepopulateAndRealignCurrentPieSlices() { m_CurrentPieSlices.clear(); for (PieQuadrant& pieQuadrant: m_PieQuadrants) { @@ -1093,8 +1027,6 @@ namespace RTE { ExpandPieSliceIntoEmptySpaceIfPossible(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::ExpandPieSliceIntoEmptySpaceIfPossible() { const std::unordered_map nextDirectionForGivenDirection{{Directions::Right, Directions::Up}, {Directions::Up, Directions::Left}, {Directions::Left, Directions::Down}, {Directions::Down, Directions::Right}}; @@ -1113,8 +1045,6 @@ namespace RTE { m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::RecreateBackgroundBitmaps() { if (m_BGBitmap) { destroy_bitmap(m_BGBitmap); @@ -1142,8 +1072,6 @@ namespace RTE { m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::DrawBackgroundPieSliceSeparators(BITMAP* backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float subPieMenuRotationOffset) const { switch (m_IconSeparatorMode) { case IconSeparatorMode::Line: @@ -1189,8 +1117,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::DrawBackgroundPieSliceSeparator(BITMAP* backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float rotAngle, bool isHoveredPieSlice, bool pieSliceHasSubPieMenu, bool drawHalfSizedSeparator) const { Vector separatorOffset; int backgroundSeparatorSize = m_BackgroundSeparatorSize; @@ -1255,8 +1181,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::SetHoveredPieSlice(const PieSlice* pieSliceToSelect, bool moveCursorIconToSlice) { if (pieSliceToSelect == m_HoveredPieSlice) { return false; @@ -1280,8 +1204,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieMenu::PreparePieSliceSubPieMenuForUse(const PieSlice* pieSliceWithSubPieMenu) const { PieMenu* subPieMenu = pieSliceWithSubPieMenu->GetSubPieMenu(); subPieMenu->m_ActivatedPieSlice = nullptr; @@ -1321,8 +1243,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieMenu::PrepareAnalogCursorForEnableOrDisable(bool enable) const { if (Controller* controller = GetController(); controller && (controller->IsMouseControlled() || controller->IsGamepadControlled())) { if (!IsSubPieMenu()) { diff --git a/Source/Entities/PieMenu.h b/Source/Entities/PieMenu.h index 44ea5d8ff9..714af6606c 100644 --- a/Source/Entities/PieMenu.h +++ b/Source/Entities/PieMenu.h @@ -12,9 +12,7 @@ namespace RTE { class GUIFont; class Actor; - /// /// A PieMenu for managing interactions with objects and Actors. - /// class PieMenu : public Entity { friend class PieSlice; @@ -25,52 +23,38 @@ namespace RTE { ClassInfoGetters #pragma region Creation - /// /// Constructor method used to instantiate a PieMenu object in system memory. Create() should be called before using the object. - /// PieMenu() { Clear(); } - /// /// Makes the PieMenu object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Makes the PieMenu object ready for use. - /// - /// The Actor which should act as the owner for this PieMenu. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param owner The Actor which should act as the owner for this PieMenu. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(Actor* owner) { SetOwner(owner); return Create(); } - /// /// Creates a PieMenu to be identical to another, by deep copy. - /// - /// A reference to the Attachable to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Attachable to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const PieMenu& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a PieMenu object before deletion from system memory. - /// ~PieMenu() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the PieMenu object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Resets the entire PieMenu, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Entity::Reset(); @@ -78,94 +62,64 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the owner Actor of this PieMenu. Ownership is NOT transferred! - /// - /// The owner Actor of this PieMenu. Ownership is NOT transferred! + /// @return The owner Actor of this PieMenu. Ownership is NOT transferred! const Actor* GetOwner() const { return m_Owner; } - /// /// Sets the owner Actor of this PieMenu, ensuring this is that Actor's PieMenu, and updates PieSlice sources accordingly. Ownership is NOT transferred! - /// - /// The new owner Actor for this PieMenu. Ownership is NOT transferred! + /// @param newOwner The new owner Actor for this PieMenu. Ownership is NOT transferred! void SetOwner(Actor* newOwner); - /// /// Gets the currently in-use Controller for this PieMenu - either the menu Controller if there's one set, or the owning Actor's Controller. Ownership IS NOT transferred! - /// - /// The currently in-use Controller for this PieMenu. + /// @return The currently in-use Controller for this PieMenu. Controller* GetController() const; - /// /// Sets the menu Controller used by this PieMenu, separate from any Controller on its owner Actor. Ownership is NOT transferred! - /// - /// The new Controller for this PieMenu. Ownership is NOT transferred! + /// @param menuController The new Controller for this PieMenu. Ownership is NOT transferred! void SetMenuController(Controller* menuController) { m_MenuController = menuController; } - /// /// Gets the currently affected MovableObject. Ownership is NOT transferred! - /// - /// The MovableObject this PieMenu affects. Ownership is NOT transferred! + /// @return The MovableObject this PieMenu affects. Ownership is NOT transferred! const MovableObject* GetAffectedObject() const { return m_AffectedObject; } - /// /// Sets the MovableObject this PieMenu should affect. Ownership is NOT transferred! - /// - /// The new MovableObject affected by this PieMenu. Ownership is NOT transferred! + /// @param affectedObject The new MovableObject affected by this PieMenu. Ownership is NOT transferred! void SetAffectedObject(MovableObject* affectedObject) { m_AffectedObject = affectedObject; } - /// /// Gets whether this PieMenu is a sub-PieMenu, i.e. it's owned by a PieSlice. - /// - /// Whether or not this PieMenu is a sub-PieMenu. + /// @return Whether or not this PieMenu is a sub-PieMenu. bool IsSubPieMenu() const { return m_DirectionIfSubPieMenu != Directions::None; } - /// /// Gets the absolute center position of this PieMenu. - /// - /// A Vector describing the current absolute position of this PieMenu in pixels. + /// @return A Vector describing the current absolute position of this PieMenu in pixels. const Vector& GetPos() const { return m_CenterPos; } - /// /// Sets the absolute center position of this PieMenu in the scene. - /// - /// A Vector describing the new absolute position of this PieMenu in pixels, in the scene. + /// @param newPos A Vector describing the new absolute position of this PieMenu in pixels, in the scene. void SetPos(const Vector& newPos); - /// /// Gets the absolute rotation of this PieMenu. - /// - /// A Matrix describing the current absolute rotation of this PieMenu. + /// @return A Matrix describing the current absolute rotation of this PieMenu. const Matrix& GetRotation() const { return m_Rotation; } - /// /// Gets the absolute rotation of this PieMenu in radians. - /// - /// The absolute rotation of this PieMenu in radians. + /// @return The absolute rotation of this PieMenu in radians. float GetRotAngle() const { return m_Rotation.GetRadAngle(); } - /// /// Sets the absolute rotation of this PieMenu. - /// - /// A Matrix describing the new rotation of this PieMenu. + /// @param newRotation A Matrix describing the new rotation of this PieMenu. void SetRotation(const Matrix& newRotation) { m_Rotation = newRotation; } - /// /// Sets the absolute rotation of this PieMenu to the specified rad angle. - /// - /// The angle in radians describing the new rotation of this PieMenu. + /// @param rotAngle The angle in radians describing the new rotation of this PieMenu. void SetRotAngle(float rotAngle) { m_Rotation.SetRadAngle(rotAngle); } - /// /// Gets the full inner radius of this PieMenu. - /// - /// Gets the full inner radius of this PieMenu. + /// @return Gets the full inner radius of this PieMenu. int GetFullInnerRadius() const { return m_FullInnerRadius; } - /// /// Sets the full inner radius of this PieMenu and recreates the background bitmap if it's changed. - /// - /// The new full inner radius of this PieMenu. + /// @param fullInnerRadius The new full inner radius of this PieMenu. void SetFullInnerRadius(int fullInnerRadius) { if (m_FullInnerRadius != fullInnerRadius) { if (m_CurrentInnerRadius == m_FullInnerRadius) { @@ -176,60 +130,42 @@ namespace RTE { } } - /// /// Gets whether or not the PieMenu is enabled or in the process of being enabled, and is not in wobble mode. - /// - /// Whether or not the PieMenu is enabled or in the process of being enabled. + /// @return Whether or not the PieMenu is enabled or in the process of being enabled. bool IsEnabled() const { return (m_EnabledState == EnabledState::Enabled || m_EnabledState == EnabledState::Enabling) && m_MenuMode != MenuMode::Wobble; } - /// /// Gets whether or not the PieMenu is in the process of being enabled. - /// - /// Whether or not the PieMenu is in the process of being enabled. + /// @return Whether or not the PieMenu is in the process of being enabled. bool IsEnabling() const { return m_EnabledState == EnabledState::Enabling; } - /// /// Gets whether or not the PieMenu is in the process of being disabled. - /// - /// Whether or not the PieMenu is in the process of being disabled. + /// @return Whether or not the PieMenu is in the process of being disabled. bool IsDisabling() const { return m_EnabledState == EnabledState::Enabling && m_MenuMode != MenuMode::Wobble; } - /// /// Gets whether or not the PieMenu is in the process of enabling or disabling. - /// - /// Whether or not the PieMenu is in the process of enabling or disabling. + /// @return Whether or not the PieMenu is in the process of enabling or disabling. bool IsEnablingOrDisabling() const { return (m_EnabledState == EnabledState::Enabling || m_EnabledState == EnabledState::Disabling) && m_MenuMode != MenuMode::Wobble; } - /// /// Gets whether or not the PieMenu is at all visible. - /// - /// Whether the PieMenu is visible. + /// @return Whether the PieMenu is visible. bool IsVisible() const { return m_EnabledState != EnabledState::Disabled || m_MenuMode == MenuMode::Freeze || m_MenuMode == MenuMode::Wobble; } - /// /// Gets whether or not the PieMenu is in the normal animation mode. - /// - /// Whether or not the PieMenu is in the normal animation mode. + /// @return Whether or not the PieMenu is in the normal animation mode. bool IsInNormalAnimationMode() const { return m_MenuMode == MenuMode::Normal; } - /// /// Enables or disables the PieMenu and animates it in and out of view. - /// - /// Whether to enable or disable the PieMenu. - /// Whether or not to play appropriate sounds when the menu is enabled or disabled. + /// @param enable Whether to enable or disable the PieMenu. + /// @param playSounds Whether or not to play appropriate sounds when the menu is enabled or disabled. void SetEnabled(bool enable, bool playSounds = true); - /// /// Gets whether this PieMenu has an open sub-PieMenu. - /// - /// Whether or not this PieMenu has an open sub-PieMenu. + /// @return Whether or not this PieMenu has an open sub-PieMenu. bool HasSubPieMenuOpen() const { return m_ActiveSubPieMenu != nullptr; } #pragma endregion #pragma region Special Animation Handling - /// /// Sets this PieMenu to normal MenuMode. - /// void SetAnimationModeToNormal() { if (m_MenuMode != MenuMode::Normal) { m_MenuMode = MenuMode::Normal; @@ -237,9 +173,7 @@ namespace RTE { } } - /// /// Plays the disabling animation, regardless of whether the PieMenu was enabled or not. - /// void DoDisableAnimation() { m_CurrentInnerRadius = m_FullInnerRadius; m_MenuMode = MenuMode::Normal; @@ -247,16 +181,12 @@ namespace RTE { m_EnabledState = EnabledState::Disabling; } - /// /// Plays an animation of the background circle expanding and contracting continuously. The PieMenu is effectively disabled while doing this. /// This animation will continue until the next call to SetEnabled. - /// void Wobble() { m_MenuMode = MenuMode::Wobble; } - /// /// Makes the background circle freeze at a certain radius until SetEnabled is called. The PieMenu is effectively disabled while doing this. - /// - /// The radius to make the background circle freeze at. + /// @param radius The radius to make the background circle freeze at. void FreezeAtRadius(int radius) { m_MenuMode = MenuMode::Freeze; m_CurrentInnerRadius = radius; @@ -265,136 +195,102 @@ namespace RTE { #pragma endregion #pragma region PieSlice Handling - /// /// Gets the activated PieSlice for this PieMenu in the last update. If there is a sub-PieMenu open for this PieMenu, it gets that activated PieSlice instead. - /// - /// The activated PieSlice for this PieMenu. + /// @return The activated PieSlice for this PieMenu. const PieSlice* GetActivatedPieSlice() const; - /// /// Gets the command issued by this PieMenu in the last update, i.e. the PieSlice SliceType of the currently activated PieSlice, or None if no slice was activated. - /// - /// The PieSlice type which has been picked, or None if none has been picked. + /// @return The PieSlice type which has been picked, or None if none has been picked. PieSlice::SliceType GetPieCommand() const; - /// /// Gets a const reference to the vector containing pointers to all the PieSlices in this PieMenu. - /// - /// A const reference to the vector containing pointers to all the PieSlices in this PieMenu. + /// @return A const reference to the vector containing pointers to all the PieSlices in this PieMenu. const std::vector& GetPieSlices() const { return m_CurrentPieSlices; } - /// /// Gets the first found PieSlice with the passed in preset name, if there is one. Ownership is NOT transferred! - /// - /// The preset name to look for. - /// The first found PieSlice with the passed in preset name, or nullptr if there are no PieSlices with that preset name in this PieMenu. + /// @param presetName The preset name to look for. + /// @return The first found PieSlice with the passed in preset name, or nullptr if there are no PieSlices with that preset name in this PieMenu. PieSlice* GetFirstPieSliceByPresetName(const std::string& presetName) const; - /// /// Gets the first found PieSlice with the passed in PieSlice SliceType, if there is one. Ownership is NOT transferred! - /// - /// The type of PieSlice to look for. - /// The first found PieSlice with the passed in PieSlice SliceType, or nullptr if there are no PieSlices with that SliceType in this PieMenu. + /// @param pieSliceType The type of PieSlice to look for. + /// @return The first found PieSlice with the passed in PieSlice SliceType, or nullptr if there are no PieSlices with that SliceType in this PieMenu. PieSlice* GetFirstPieSliceByType(PieSlice::SliceType pieSliceType) const; - /// /// Adds a PieSlice to the PieMenu, setting its original source to the specified sliceSource. Ownership IS transferred! /// The slice will be placed in the appropriate PieQuadrant for its Direction, with Any Direction using the first available PieQuadrant. /// If allowQuadrantOverflow is true, the PieSlice will be added to the next available PieQuadrant in Direction order. /// Note that if the slice could not be added, it will be deleted to avoid memory leaks, and the method will return false. - /// - /// The new PieSlice to add. Ownership IS transferred. - /// The source of the added PieSlice. Should be nullptr for slices not added by Entities. - /// Whether the new PieSlice can be placed in PieQuadrants other than the one specified by its Direction, if that PieQuadrant is full. - /// Whether or not the PieSlice was added successfully. + /// @param pieSliceToAdd The new PieSlice to add. Ownership IS transferred. + /// @param pieSliceOriginalSource The source of the added PieSlice. Should be nullptr for slices not added by Entities. + /// @param allowQuadrantOverflow Whether the new PieSlice can be placed in PieQuadrants other than the one specified by its Direction, if that PieQuadrant is full. + /// @return Whether or not the PieSlice was added successfully. bool AddPieSlice(PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool allowQuadrantOverflow = false); - /// /// Adds a PieSlice to the PieMenu, with the same conditions as AddPieSlice above, but only if no PieSlice exists in this PieMenu with the same PresetName (optionally with the same original source). Ownership IS transferred! - /// - /// The new PieSlice to add. Ownership IS transferred. - /// The source of the added PieSlice. Should be nullptr for slices not added by Entities. - /// Whether all PieSlices in the PieMenu should be checked to see if there are no duplicates, or only those with the same original source. - /// Whether the new PieSlice can be placed in PieQuadrants other than the one specified by its Direction, if that PieQuadrant is full. - /// Whether or not the PieSlice was added successfully. + /// @param pieSliceToAdd The new PieSlice to add. Ownership IS transferred. + /// @param pieSliceOriginalSource The source of the added PieSlice. Should be nullptr for slices not added by Entities. + /// @param onlyCheckPieSlicesWithSameOriginalSource Whether all PieSlices in the PieMenu should be checked to see if there are no duplicates, or only those with the same original source. + /// @param allowQuadrantOverflow Whether the new PieSlice can be placed in PieQuadrants other than the one specified by its Direction, if that PieQuadrant is full. + /// @return Whether or not the PieSlice was added successfully. bool AddPieSliceIfPresetNameIsUnique(PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource = false, bool allowQuadrantOverflow = false); - /// /// Removes and returns the passed in PieSlice from this PieMenu if it's in the PieMenu. Ownership IS transferred to the caller! - /// - /// The PieSlice to remove from this PieMenu. Ownership IS transferred to the caller! - /// The removed PieSlice, if it was in the PieMenu. + /// @param pieSliceToRemove The PieSlice to remove from this PieMenu. Ownership IS transferred to the caller! + /// @return The removed PieSlice, if it was in the PieMenu. PieSlice* RemovePieSlice(const PieSlice* pieSliceToRemove); - /// /// Removes any PieSlices in this PieMenu whose preset name matches the passed in preset name. - /// - /// The preset name to check against. - /// Whether or not any PieSlices were removed from this PieMenu. + /// @param presetNameToRemoveBy The preset name to check against. + /// @return Whether or not any PieSlices were removed from this PieMenu. bool RemovePieSlicesByPresetName(const std::string& presetNameToRemoveBy); - /// /// Removes any PieSlices in this PieMenu whose PieSlice SliceType matches the passed in PieSlice SliceType. - /// - /// The PieSlice SliceType to check against. - /// Whether or not any PieSlices were removed from this PieMenu. + /// @param pieSliceTypeToRemoveBy The PieSlice SliceType to check against. + /// @return Whether or not any PieSlices were removed from this PieMenu. bool RemovePieSlicesByType(PieSlice::SliceType pieSliceTypeToRemoveBy); - /// /// Removes any PieSlices in this PieMenu whose original source matches the passed in Entity. - /// - /// The original source whose PieSlices should be removed. - /// Whether or not any PieSlices were removed from this PieMenu. + /// @param originalSource The original source whose PieSlices should be removed. + /// @return Whether or not any PieSlices were removed from this PieMenu. bool RemovePieSlicesByOriginalSource(const Entity* originalSource); - /// /// Replaces the first PieSlice with the second, ensuring original source, direction, middle slice eligibility, angles and slot count are maintained. /// The existing PieSlice is returned, and ownership IS transferred both ways! - /// - /// The PieSlice that will be replaced. - /// The PieSlice that will replace the existing one. If this is nullptr, the existing one will just be removed. - /// The removed PieSlice, if there is one. Ownership IS transferred! + /// @param pieSliceToReplace The PieSlice that will be replaced. + /// @param replacementPieSlice The PieSlice that will replace the existing one. If this is nullptr, the existing one will just be removed. + /// @return The removed PieSlice, if there is one. Ownership IS transferred! PieSlice* ReplacePieSlice(const PieSlice* pieSliceToReplace, PieSlice* replacementPieSlice); #pragma endregion #pragma region Updating - /// /// Updates the state of this PieMenu each frame. - /// void Update(); - /// /// Draws the PieMenu. - /// - /// A pointer to a BITMAP to draw on. Generally a screen BITMAP. - /// The absolute position of the target bitmap's upper left corner in the scene. + /// @param targetBitmap A pointer to a BITMAP to draw on. Generally a screen BITMAP. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector()) const; #pragma endregion #pragma region Event Handling - /// /// Add the passed in MovableObject and function as a listener to be run while this PieMenu is open. - /// - /// The MovableObject listening. - /// The function to be run on the MovableObject. + /// @param listeningObject The MovableObject listening. + /// @param listenerFunction The function to be run on the MovableObject. void AddWhilePieMenuOpenListener(const MovableObject* listeningObject, const std::function& listenerFunction) { if (listeningObject) { m_WhilePieMenuOpenListeners.try_emplace(listeningObject, listenerFunction); } } - /// /// Removes the passed in MovableObject and its listening function as a listener for when this PieMenu is opened. - /// - /// The MovableObject whose listening function should be removed. - /// Whether or not the MovableObject was found and removed as a listener. + /// @param objectToRemove The MovableObject whose listening function should be removed. + /// @return Whether or not the MovableObject was found and removed as a listener. bool RemoveWhilePieMenuOpenListener(const MovableObject* objectToRemove) { return m_WhilePieMenuOpenListeners.erase(objectToRemove) == 1; } #pragma endregion private: - /// /// Enumeration for enabled states when enabling/disabling the PieMenu. - /// enum class EnabledState { Enabling, Enabled, @@ -402,27 +298,21 @@ namespace RTE { Disabled }; - /// /// Enumeration for the modes a PieMenu can have. - /// enum class MenuMode { Normal, Wobble, Freeze }; - /// /// Enumeration for the different item separator modes available to the PieMenu. - /// enum class IconSeparatorMode { Line, Circle, Square }; - /// /// Enumeration for helping keyboard PieMenu navigation. Specifies the ways the cursor should move from one PieQuadrant to another. - /// enum class MoveToPieQuadrantMode { Start, Middle, @@ -486,124 +376,88 @@ namespace RTE { bool m_BGPieSlicesWithSubPieMenuBitmapNeedsRedrawing; //!< Whether the BG bitmap for PieSlices with sub-PieMenus should be redrawn when the BGBitmap is redrawn. #pragma region Update Breakdown - /// /// Handles the wobbling portion of Update. - /// void UpdateWobbling(); - /// /// Handles the enabling and disabling part of Update. - /// void UpdateEnablingAndDisablingProgress(); - /// /// Handles the analog input when updating. - /// - /// The analog input vector. - /// Whether or not enough input was received to do something. + /// @param input The analog input vector. + /// @return Whether or not enough input was received to do something. bool HandleAnalogInput(const Vector& input); - /// /// Handles the digital input when updating. - /// - /// Whether or not enough input was received to do something. + /// @return Whether or not enough input was received to do something. bool HandleDigitalInput(); - /// /// Handles the slice activation part of Update. - /// void UpdateSliceActivation(); - /// /// Redraws the pre-drawn background bitmap so it's up-to-date. - /// void UpdatePredrawnMenuBackgroundBitmap(); #pragma endregion #pragma region Draw Breakdown - /// /// Handles figuring out the position to draw the PieMenu at, accounting for any Scene seams. - /// - /// A pointer to the BITMAP to draw on. Generally a screen BITMAP. - /// The absolute position of the target bitmap's upper left corner in the scene. - /// Out parameter, a Vector to be filled in with the position at which the PieMenu should be drawn. + /// @param targetBitmap A pointer to the BITMAP to draw on. Generally a screen BITMAP. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. + /// @param drawPos Out parameter, a Vector to be filled in with the position at which the PieMenu should be drawn. void CalculateDrawPosition(const BITMAP* targetBitmap, const Vector& targetPos, Vector& drawPos) const; - /// /// Handles drawing icons for PieSlices' visual representation in the PieMenu. - /// - /// A pointer to the BITMAP to draw on. Generally a screen BITMAP. - /// The seam corrected position at which the PieMenu is being drawn. + /// @param targetBitmap A pointer to the BITMAP to draw on. Generally a screen BITMAP. + /// @param drawPos The seam corrected position at which the PieMenu is being drawn. void DrawPieIcons(BITMAP* targetBitmap, const Vector& drawPos) const; - /// /// Handles drawing the cursor and description text for selected PieSlices. - /// - /// A pointer to the BITMAP to draw on. Generally a screen BITMAP. - /// The seam corrected position at which the PieMenu is being drawn. + /// @param targetBitmap A pointer to the BITMAP to draw on. Generally a screen BITMAP. + /// @param drawPos The seam corrected position at which the PieMenu is being drawn. void DrawPieCursorAndPieSliceDescriptions(BITMAP* targetBitmap, const Vector& drawPos) const; #pragma endregion - /// /// Clears and refills the vector of current PieSlices for this PieMenu. Also realigns PieSlices and expands them into empty space if possible, to ensure everything is properly ready. - /// void RepopulateAndRealignCurrentPieSlices(); - /// /// Expands any PieSlices that border onto another PieQuadrant's unfilled slot, so they visually occupy that empty slot. - /// void ExpandPieSliceIntoEmptySpaceIfPossible(); - /// /// Recreates this PieMenu's background bitmap based on its full inner radius. - /// void RecreateBackgroundBitmaps(); - /// /// Draws the background separators for this PieMenu, based on its IconSeparatorMode, onto the passed in bitmap. - /// - /// The bitmap to draw the separators onto. - /// The center X position of the circle being separated. - /// The center Y position of the circle being separated. - /// The rotation offset used if this is a sub-PieMenu. + /// @param backgroundBitmapToDrawTo The bitmap to draw the separators onto. + /// @param pieCircleCenterX The center X position of the circle being separated. + /// @param pieCircleCenterY The center Y position of the circle being separated. + /// @param subPieMenuRotationOffset The rotation offset used if this is a sub-PieMenu. void DrawBackgroundPieSliceSeparators(BITMAP* backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float subPieMenuRotationOffset) const; - /// /// Draws a background separator, based on this PieMenu's IconSeparatorMode, to the passed in bitmap. - /// - /// The bitmap to draw the separator onto. - /// The center X position of the circle the separator is drawn onto. - /// The center Y position of the circle the separator is drawn onto. - /// The rotation of the separator, not used for all separator types. - /// Whether the separator is being drawn for the PieMenu's hovered PieSlice. - /// Whether the PieSlice whose separator is being drawn has a sub-PieMenu. - /// Whether to draw a half-sized separator or a full-sized one. Defaults to drawing a full-sized one. + /// @param backgroundBitmapToDrawTo The bitmap to draw the separator onto. + /// @param pieCircleCenterX The center X position of the circle the separator is drawn onto. + /// @param pieCircleCenterY The center Y position of the circle the separator is drawn onto. + /// @param rotAngle The rotation of the separator, not used for all separator types. + /// @param isHoveredPieSlice Whether the separator is being drawn for the PieMenu's hovered PieSlice. + /// @param pieSliceHasSubPieMenu Whether the PieSlice whose separator is being drawn has a sub-PieMenu. + /// @param drawHalfSizedSeparator Whether to draw a half-sized separator or a full-sized one. Defaults to drawing a full-sized one. void DrawBackgroundPieSliceSeparator(BITMAP* backgroundBitmapToDrawTo, int pieCircleCenterX, int pieCircleCenterY, float rotAngle, bool isHoveredPieSlice, bool pieSliceHasSubPieMenu, bool drawHalfSizedSeparator = false) const; - /// /// Sets the passed in PieSlice as the hovered PieSlice of this PieMenu. If nullptr is passed in, no PieSlice will be hovered. - /// - /// The PieSlice to consider hovered, if any. Has to be a PieSlice currently in this PieMenu. - /// Whether to also move the cursor icon to the center of the new hovered PieSlice and set it to be drawn (generally for non-mouse inputs). Defaults to false. - /// Whether or not the PieSlice to set as hovered was different from the already hovered PieSlice. + /// @param pieSliceToSetAsHovered The PieSlice to consider hovered, if any. Has to be a PieSlice currently in this PieMenu. + /// @param moveCursorIconToSlice Whether to also move the cursor icon to the center of the new hovered PieSlice and set it to be drawn (generally for non-mouse inputs). Defaults to false. + /// @return Whether or not the PieSlice to set as hovered was different from the already hovered PieSlice. bool SetHoveredPieSlice(const PieSlice* pieSliceToSetAsHovered, bool moveCursorToPieSlice = false); - /// /// Prepares the passed in PieSlice's sub-PieMenu for use by setting flags, moving PieSlices around, and disabling PieQuadrants as appropriate. - /// - /// The PieSlice with a sub-PieMenu that needs to be prepared. - /// Whether the sub-PieMenu was prepared. PieMenus with the SubPieMenu flag already set will not have action taken on them. + /// @param pieSliceWithSubPieMenu The PieSlice with a sub-PieMenu that needs to be prepared. + /// @return Whether the sub-PieMenu was prepared. PieMenus with the SubPieMenu flag already set will not have action taken on them. bool PreparePieSliceSubPieMenuForUse(const PieSlice* pieSliceWithSubPieMenu) const; - /// /// If the Controller for this PieMenu is mouse or gamepad controlled, sets up analog cursor angle limits and positions for when the pie menu is enabled or disabled. Also used when a sub-PieMenu of this PieMenu is disabled. - /// - /// Whether the PieMenu is being enabled or disabled. + /// @param enable Whether the PieMenu is being enabled or disabled. void PrepareAnalogCursorForEnableOrDisable(bool enable) const; - /// /// Clears all the member variables of this PieMenu, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/PieSlice.cpp b/Source/Entities/PieSlice.cpp index 9129333743..6873eb9a6b 100644 --- a/Source/Entities/PieSlice.cpp +++ b/Source/Entities/PieSlice.cpp @@ -8,8 +8,6 @@ namespace RTE { ConcreteClassInfo(PieSlice, Entity, 80); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieSlice::Clear() { m_Type = SliceType::NoType; m_Direction = Directions::Any; @@ -31,8 +29,6 @@ namespace RTE { m_DrawFlippedToMatchAbsoluteAngle = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieSlice::Create() { if (Entity::Create() < 0) { return -1; @@ -44,8 +40,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieSlice::Create(const PieSlice& reference) { Entity::Create(reference); @@ -75,8 +69,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieSlice::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -122,8 +114,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieSlice::Save(Writer& writer) const { Entity::Save(writer); @@ -148,8 +138,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* RTE::PieSlice::GetAppropriateIcon(bool sliceIsSelected) const { if (int iconFrameCount = m_Icon->GetFrameCount(); iconFrameCount > 0) { if (!IsEnabled() && iconFrameCount > 2) { @@ -163,20 +151,14 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieMenu* PieSlice::GetSubPieMenu() const { return m_SubPieMenu.get(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieSlice::SetSubPieMenu(PieMenu* newSubPieMenu) { m_SubPieMenu = std::unique_ptr(newSubPieMenu); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PieSlice::ReloadScripts() { int status = 0; @@ -193,14 +175,10 @@ namespace RTE { return status; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieSlice::RecalculateMidAngle() { m_MidAngle = m_StartAngle + (static_cast(m_SlotCount) * PieQuadrant::c_PieSliceSlotSize / 2.0F); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieSlice::PieMenuCustomDeleter::operator()(PieMenu* pieMenu) const { pieMenu->Destroy(true); } diff --git a/Source/Entities/PieSlice.h b/Source/Entities/PieSlice.h index e328227642..a26889b64d 100644 --- a/Source/Entities/PieSlice.h +++ b/Source/Entities/PieSlice.h @@ -9,9 +9,7 @@ namespace RTE { class PieMenu; - /// /// An individual PieSlice in a PieMenu. - /// class PieSlice : public Entity { public: @@ -19,9 +17,7 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; - /// /// Enumeration for the types of PieSlices. - /// enum SliceType { NoType, // Inventory management @@ -68,237 +64,167 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a PieSlice object in system memory. Create() should be called before using the object. - /// PieSlice() { Clear(); } - /// /// Makes the PieSlice object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a PieSlice to be identical to another, by deep copy. - /// - /// A reference to the PieSlice to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the PieSlice to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const PieSlice& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a PieSlice object before deletion from system memory. - /// ~PieSlice() override { Destroy(true); } - /// /// Resets the entire Serializable, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Gets the SliceType of this PieSlice. - /// - /// The SliceType of this PieSlice. + /// @return The SliceType of this PieSlice. SliceType GetType() const { return m_Type; } - /// /// Sets the SliceType of this PieSlice. - /// - /// The new SliceType of this PieSlice. + /// @param newType The new SliceType of this PieSlice. void SetType(SliceType newType) { m_Type = newType; } - /// /// Gets the Direction of this PieSlice. - /// - /// The Direction of this PieSlice. + /// @return The Direction of this PieSlice. Directions GetDirection() const { return m_Direction; } - /// /// Sets the Direction of this PieSlice. - /// - /// The new Direction of this PieSlice. + /// @param newDirection The new Direction of this PieSlice. void SetDirection(Directions newDirection) { if (newDirection != Directions::None) { m_Direction = newDirection; } } - /// /// Gets whether or not this PieSlice can be the middle PieSlice of a PieQuadrant. - /// - /// Whether or not this PieSlice can be the middle PieSlice of a PieQuadrant. + /// @return Whether or not this PieSlice can be the middle PieSlice of a PieQuadrant. bool GetCanBeMiddleSlice() const { return m_CanBeMiddleSlice; } - /// /// Sets whether or not this PieSlice can be the middle PieSlice of a PieQuadrant. - /// - /// Whether or not this PieSlice can be the middle PieSlice of a PieQuadrant. + /// @param newCanBeMiddleSlice Whether or not this PieSlice can be the middle PieSlice of a PieQuadrant. void SetCanBeMiddleSlice(bool newCanBeMiddleSlice) { m_CanBeMiddleSlice = newCanBeMiddleSlice; } - /// /// Gets the original Entity source of this PieSlice, if there is one. - /// - /// A pointer to the original Entity source of this PieSlice, if there is one. + /// @return A pointer to the original Entity source of this PieSlice, if there is one. const Entity* GetOriginalSource() const { return m_OriginalSource; } - /// /// Sets the original Entity source of this PieSlice. - /// - /// A pointer to the original Entity source of this PieSlice. + /// @param originalSource A pointer to the original Entity source of this PieSlice. void SetOriginalSource(const Entity* originalSource) { m_OriginalSource = originalSource; } - /// /// Gets whether or not this PieSlice is enabled. - /// - /// Whether or not this PieSlice is enabled. + /// @return Whether or not this PieSlice is enabled. bool IsEnabled() const { return m_Enabled; } - /// /// Sets whether or not this PieSlice should be enabled. - /// - /// Whether or not this PieSlice should be enabled. + /// @param enabled Whether or not this PieSlice should be enabled. void SetEnabled(bool enabled) { m_Enabled = enabled; } - /// /// Gets whether or not this PieSlice has a valid Icon. - /// - /// Whether or not this PieSlice has a valid Icon. + /// @return Whether or not this PieSlice has a valid Icon. bool HasIcon() const { return m_Icon && m_Icon->GetFrameCount() > 0; } - /// /// Gets the appropriate icon frame for this PieSlice. - /// - /// Whether or not this PieSlice is selected, which may affect which icon is appropriate. - /// The icon for this PieSlice. + /// @param sliceIsSelected Whether or not this PieSlice is selected, which may affect which icon is appropriate. + /// @return The icon for this PieSlice. BITMAP* GetAppropriateIcon(bool sliceIsSelected = false) const; - /// /// Sets the new Icon for this PieSlice. Ownership IS transferred. - /// - /// The new Icon for this PieSlice. + /// @param newIcon The new Icon for this PieSlice. void SetIcon(Icon* newIcon) { m_Icon = std::unique_ptr(newIcon); } - /// /// Gets the LuabindObjectWrapper for the function this PieSlice should run when activated. - /// - /// The LuabindObjectWrapper this PieSlice should run when activated. + /// @return The LuabindObjectWrapper this PieSlice should run when activated. const LuabindObjectWrapper* GetLuabindFunctionObjectWrapper() const { return m_LuabindFunctionObject.get(); } - /// /// Gets the file path of the Lua file this PieSlice should run when activated, if any. - /// - /// The file path to the script file this PieSlice should load when activated. + /// @return The file path to the script file this PieSlice should load when activated. std::string GetScriptPath() const { return m_LuabindFunctionObject ? m_LuabindFunctionObject->GetFilePath() : ""; } - /// /// Sets the file path of the scripted file this PieSlice should run when activated. - /// - /// The file path of the Lua file this PieSlice should run when activated. + /// @param newScriptPath The file path of the Lua file this PieSlice should run when activated. void SetScriptPath(const std::string& newScriptPath) { m_LuabindFunctionObject = std::make_unique(nullptr, newScriptPath); ReloadScripts(); } - /// /// Gets the name of the Lua function to run when this PieSlice is activated. - /// - /// The name of the Lua function this PieSlice should execute when activated. + /// @return The name of the Lua function this PieSlice should execute when activated. const std::string& GetFunctionName() const { return m_FunctionName; } - /// /// Sets the name of the Lua function to run when this PieSlice is activated as a scripted pie menu option. - /// - /// The name of the Lua function to run when this PieSlice is activated. + /// @param newFunctionName The name of the Lua function to run when this PieSlice is activated. void SetFunctionName(const std::string& newFunctionName) { m_FunctionName = newFunctionName; ReloadScripts(); } // TODO Ideally this would be done with a weak_ptr but I'm not sure how it'll go with LuaMan. Try it out and see - /// /// Gets the sub-PieMenu for this PieSlice if there is one. Ownership is NOT transferred. - /// - /// The sub-PieMenu for this PieSlice if there is one. Ownership is NOT transferred. + /// @return The sub-PieMenu for this PieSlice if there is one. Ownership is NOT transferred. PieMenu* GetSubPieMenu() const; - /// /// Sets the sub-PieMenu for this PieSlice. Ownership IS transferred. - /// - /// The new sub-PieMenu for this PieSlice. Ownership IS transferred. + /// @param newSubPieMenu The new sub-PieMenu for this PieSlice. Ownership IS transferred. void SetSubPieMenu(PieMenu* newSubPieMenu); #pragma endregion #pragma region Angle Getter and Setters - /// /// Gets the start angle this PieSlice's area is set to be at in its pie menu. - /// - /// The start angle of this PieSlice's area. + /// @return The start angle of this PieSlice's area. float GetStartAngle() const { return m_StartAngle; } - /// /// Sets the start angle this PieSlice's area should be at in its pie menu. - /// - /// The start angle to set for the PieSlice's area. + /// @param startAngle The start angle to set for the PieSlice's area. void SetStartAngle(float startAngle) { m_StartAngle = startAngle; RecalculateMidAngle(); } - /// /// Gets the number of slots this PieSlice takes up. - /// - /// The number of slots this PieSlice takes up. + /// @return The number of slots this PieSlice takes up. int GetSlotCount() const { return m_SlotCount; } - /// /// Sets the number of slots this PieSlice takes up. - /// - /// The number of slots this PieSlice should take up. + /// @param slotCount The number of slots this PieSlice should take up. void SetSlotCount(int slotCount) { m_SlotCount = std::max(1, slotCount); RecalculateMidAngle(); } - /// /// Gets the mid angle this PieSlice's area is set to be at in its pie menu. - /// - /// The mid angle of this PieSlice's area. + /// @return The mid angle of this PieSlice's area. float GetMidAngle() const { return m_MidAngle; } - /// /// Sets the mid angle this PieSlice's area should be at in its pie menu. - /// - /// The mid angle to set for the PieSlice's area. + /// @param midAngle The mid angle to set for the PieSlice's area. void SetMidAngle(float midAngle) { m_MidAngle = midAngle; } - /// /// Gets whether or not this PieSlice should draw itself flipped to match its absolute angle (i.e. its angle accounting for its PieMenu's rotation). - /// - /// Whether or not this PieSlice should draw itself flipped to match its absolute angle. + /// @return Whether or not this PieSlice should draw itself flipped to match its absolute angle. bool GetDrawFlippedToMatchAbsoluteAngle() const { return m_DrawFlippedToMatchAbsoluteAngle; } - /// /// Sets whether or not this PieSlice should draw itself flipped to match its absolute angle (i.e. its angle accounting for its PieMenu's rotation). - /// - /// Whether or not this PieSlice should draw itself flipped to match its absolute angle. + /// @param shouldDrawFlippedToMatchAbsoluteAngle Whether or not this PieSlice should draw itself flipped to match its absolute angle. void SetDrawFlippedToMatchAbsoluteAngle(bool shouldDrawFlippedToMatchAbsoluteAngle) { m_DrawFlippedToMatchAbsoluteAngle = shouldDrawFlippedToMatchAbsoluteAngle; } #pragma endregion - /// /// Reloads the the script on this PieSlice. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int ReloadScripts() final; private: - /// /// Custom deleter for PieMenu to avoid include problems with unique_ptr. - /// struct PieMenuCustomDeleter { void operator()(PieMenu* pieMenu) const; }; @@ -324,14 +250,10 @@ namespace RTE { bool m_DrawFlippedToMatchAbsoluteAngle; //!< Whether or not this PieSlice should draw flipped based on its absolute angle (i.e. its angle accounting for its pie menu's rotation). - /// /// Recalculates this PieSlice's mid angle based on its start angle and slot count. - /// void RecalculateMidAngle(); - /// /// Clears all the member variables of this PieSlice, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Round.cpp b/Source/Entities/Round.cpp index 05f810a353..b6a8047c23 100644 --- a/Source/Entities/Round.cpp +++ b/Source/Entities/Round.cpp @@ -6,8 +6,6 @@ namespace RTE { ConcreteClassInfo(Round, Entity, 500); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Round::Clear() { m_Particle = 0; m_ParticleCount = 0; @@ -23,8 +21,6 @@ namespace RTE { m_AIPenetration = -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Round::Create() { if (Entity::Create() < 0) { return -1; @@ -48,8 +44,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Round::Create(const Round& reference) { Entity::Create(reference); @@ -69,8 +63,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Round::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -93,8 +85,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Round::Save(Writer& writer) const { Entity::Save(writer); @@ -125,8 +115,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MovableObject* Round::PopNextParticle() { MovableObject* tempParticle = (m_ParticleCount > 0) ? dynamic_cast(m_Particle->Clone()) : 0; m_ParticleCount--; diff --git a/Source/Entities/Round.h b/Source/Entities/Round.h index 694044f0b6..efe1c02baa 100644 --- a/Source/Entities/Round.h +++ b/Source/Entities/Round.h @@ -7,9 +7,7 @@ namespace RTE { class MovableObject; - /// /// A round containing a number of projectile particles and one shell. - /// class Round : public Entity { public: @@ -18,35 +16,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a Round object in system memory. Create() should be called before using the object. - /// Round() { Clear(); } - /// /// Makes the Round object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a Round to be identical to another, by deep copy. - /// - /// A reference to the Round to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Round to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Round& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a Round object before deletion from system memory. - /// ~Round() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the Round object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); @@ -54,9 +42,7 @@ namespace RTE { Clear(); } - /// /// Resets the entire Round, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Entity::Reset(); @@ -64,96 +50,66 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Returns how many particles are contained within this Round, not counting the shell. - /// - /// The number of particles. + /// @return The number of particles. int ParticleCount() const { return m_ParticleCount; } - /// /// Returns whether this Round is out of particles or not. - /// - /// Whether this Round is out of particles or not. + /// @return Whether this Round is out of particles or not. bool IsEmpty() const { return m_ParticleCount <= 0; } - /// /// Gets the next particle contained in this Round. Ownership is NOT transferred! - /// - /// A pointer to the next particle, or 0 if this Round is empty. + /// @return A pointer to the next particle, or 0 if this Round is empty. const MovableObject* GetNextParticle() const { return (m_ParticleCount > 0) ? m_Particle : 0; } - /// /// Gets the next particle contained in this Round, and removes it from the stack. Ownership IS transferred! - /// - /// A pointer to the next particle, or 0 if this Round is empty. + /// @return A pointer to the next particle, or 0 if this Round is empty. MovableObject* PopNextParticle(); - /// /// Gets the velocity at which this round is to be fired. - /// - /// A float with the velocity in m/s. + /// @return A float with the velocity in m/s. float GetFireVel() const { return m_FireVel; } - /// /// Gets whether or not this Round should inherit velocity from its firer. - /// - /// Whether or not this Round should inherit velocity from its firer. + /// @return Whether or not this Round should inherit velocity from its firer. bool GetInheritsFirerVelocity() const { return m_InheritsFirerVelocity; } - /// /// Gets the separation of particles in this round. - /// - /// A float with the separation range in pixels. + /// @return A float with the separation range in pixels. float GetSeparation() const { return m_Separation; } - /// /// Gets the variation in lifetime of the fired particles in this Round. - /// - /// A float with the life variation scalar. + /// @return A float with the life variation scalar. float GetLifeVariation() const { return m_LifeVariation; } - /// /// Gets the shell casing preset of this Round. Ownership IS NOT transferred! - /// - /// A pointer to the shell casing preset, or 0 if this Round has no shell. + /// @return A pointer to the shell casing preset, or 0 if this Round has no shell. const MovableObject* GetShell() const { return m_Shell; } - /// /// Gets the maximum velocity at which this round's shell is to be ejected. - /// - /// A float with the maximum shell velocity in m/s. + /// @return A float with the maximum shell velocity in m/s. float GetShellVel() const { return m_ShellVel; } - /// /// Shows whether this Round has an extra sound sample to play when fired. - /// - /// Whether the firing Sound of this has been loaded, or the firing Device will make the noise alone. + /// @return Whether the firing Sound of this has been loaded, or the firing Device will make the noise alone. bool HasFireSound() const { return m_FireSound.HasAnySounds(); } - /// /// Gets the extra firing sound of this Round, which can be played in addition to the weapon's own firing sound. OWNERSHIP IS NOT TRANSFERRED! - /// - /// A sound with the firing sample of this round. + /// @return A sound with the firing sample of this round. SoundContainer* GetFireSound() { return &m_FireSound; } #pragma endregion #pragma region AI Properties - /// /// Returns the lifetime of the projectile used by the AI when executing the shooting scripts. - /// - /// The life time in MS used by the AI. + /// @return The life time in MS used by the AI. unsigned long GetAILifeTime() const { return m_AILifeTime; } - /// /// Returns the FireVelocity of the projectile used by the AI when executing the shooting scripts. - /// - /// The FireVelocity in m/s used by the AI. + /// @return The FireVelocity in m/s used by the AI. int GetAIFireVel() const { return m_AIFireVel; } - /// /// Returns the bullet's ability to penetrate material when executing the AI shooting scripts. - /// - /// A value equivalent to Mass * Sharpness * Vel. + /// @return A value equivalent to Mass * Sharpness * Vel. int GetAIPenetration() const { return m_AIPenetration; } #pragma endregion @@ -177,9 +133,7 @@ namespace RTE { int m_AIPenetration; //!< For overriding the bullets ability to penetrate material when executing the AI shooting scripts. private: - /// /// Clears all the member variables of this Round, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/SLBackground.cpp b/Source/Entities/SLBackground.cpp index 680e6b7ee4..6ac68a6878 100644 --- a/Source/Entities/SLBackground.cpp +++ b/Source/Entities/SLBackground.cpp @@ -7,8 +7,6 @@ namespace RTE { ConcreteClassInfo(SLBackground, SceneLayer, 0); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLBackground::Clear() { m_Bitmaps.clear(); m_FrameCount = 1; @@ -32,8 +30,6 @@ namespace RTE { m_IgnoreAutoScale = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLBackground::Create() { SceneLayer::Create(); @@ -58,8 +54,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLBackground::Create(const SLBackground& reference) { SceneLayer::Create(reference); @@ -90,8 +84,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLBackground::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneLayer::ReadProperty(propName, reader)); @@ -123,8 +115,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLBackground::Save(Writer& writer) const { SceneLayer::Save(writer); @@ -145,8 +135,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLBackground::InitScaleFactors() { if (!m_IgnoreAutoScale) { float fitScreenScaleFactor = std::clamp(static_cast(std::min(g_SceneMan.GetSceneHeight(), g_FrameMan.GetPlayerScreenHeight())) / static_cast(m_MainBitmap->h), 1.0F, 2.0F); @@ -167,8 +155,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLBackground::Update() { if (!m_IsAnimatedManually && m_SpriteAnimMode != SpriteAnimMode::NOANIM) { int prevFrame = m_Frame; @@ -214,8 +200,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLBackground::Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment) { SceneLayer::Draw(targetBitmap, targetBox, !IsAutoScrolling()); diff --git a/Source/Entities/SLBackground.h b/Source/Entities/SLBackground.h index c368521edd..7c78d226ba 100644 --- a/Source/Entities/SLBackground.h +++ b/Source/Entities/SLBackground.h @@ -6,9 +6,7 @@ namespace RTE { - /// /// A scrolling background layer of the Scene, placed behind the terrain. - /// class SLBackground : public SceneLayer { friend class NetworkServer; @@ -18,35 +16,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a SLBackground object in system memory. Create() should be called before using the object. - /// SLBackground() { Clear(); } - /// /// Makes the SLBackground object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a SLBackground to be identical to another, by deep copy. - /// - /// A reference to the SLBackground to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the SLBackground to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const SLBackground& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a SLBackground object before deletion from system memory. - /// ~SLBackground() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the SLBackground object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { SceneLayer::Destroy(); @@ -56,160 +44,110 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets whether this SLBackground's animation is handled manually/externally. - /// - /// Whether this SLBackground's animation is handled manually/externally. If true, animation will not be handled during Update(). + /// @return Whether this SLBackground's animation is handled manually/externally. If true, animation will not be handled during Update(). bool IsAnimatedManually() const { return m_IsAnimatedManually; } - /// /// Sets whether this SLBackground is animated manually/externally. - /// - /// Whether this SLBackground is animated manually/externally and should skip animation handling during Update(). + /// @param isAnimatedManually Whether this SLBackground is animated manually/externally and should skip animation handling during Update(). void SetAnimatedManually(bool isAnimatedManually) { m_IsAnimatedManually = isAnimatedManually; } - /// /// Gets the frame number of this SLBackground that is currently set to be drawn. - /// - /// The frame number that is currently set to be drawn. + /// @return The frame number that is currently set to be drawn. int GetFrame() const { return m_Frame; } - /// /// Sets frame number that this SLBackground will draw. - /// - /// The frame number that is supposed to be drawn. + /// @param newFrame The frame number that is supposed to be drawn. void SetFrame(int newFrame) { m_Frame = std::clamp(newFrame, 0, m_FrameCount - 1); } - /// /// Gets the animation mode of this SLBackground. - /// - /// The currently set animation mode. See SpriteAnimMode enumeration. + /// @return The currently set animation mode. See SpriteAnimMode enumeration. int GetSpriteAnimMode() const { return m_SpriteAnimMode; } - /// /// Sets the animation mode of this SLBackground. - /// - /// The new animation mode. See SpirteAnimMode enumeration. + /// @param newAnimMode The new animation mode. See SpirteAnimMode enumeration. void SetSpriteAnimMode(SpriteAnimMode newAnimMode = SpriteAnimMode::NOANIM) { m_SpriteAnimMode = std::clamp(newAnimMode, SpriteAnimMode::NOANIM, SpriteAnimMode::ALWAYSPINGPONG); } - /// /// Gets the time it takes to complete a full animation cycle of this SLBackground. - /// - /// The animation cycle duration, in milliseconds. + /// @return The animation cycle duration, in milliseconds. int GetSpriteAnimDuration() const { return m_SpriteAnimDuration; } - /// /// Sets the time it takes to complete a full animation cycle of this SLBackground. - /// - /// The new animation cycle duration, in milliseconds. + /// @param newDuration The new animation cycle duration, in milliseconds. void SetSpriteAnimDuration(int newDuration) { m_SpriteAnimDuration = newDuration; } - /// /// Gets whether this SLBackground has auto-scrolling enabled and meets the requirements to actually auto-scroll. - /// - /// Whether this has auto-scrolling enabled and meets the requirements to actually auto-scroll. + /// @return Whether this has auto-scrolling enabled and meets the requirements to actually auto-scroll. bool IsAutoScrolling() const { return (m_WrapX && m_CanAutoScrollX) || (m_WrapY && m_CanAutoScrollY); } - /// /// Gets whether auto-scrolling is enabled on the X axis. - /// - /// Whether auto-scrolling is enabled on the X axis. This may be true even if auto-scrolling isn't actually happening due to not meeting requirements. + /// @return Whether auto-scrolling is enabled on the X axis. This may be true even if auto-scrolling isn't actually happening due to not meeting requirements. bool GetAutoScrollX() const { return m_CanAutoScrollX; } - /// /// Sets whether auto-scrolling is enabled on the X axis. - /// - /// Whether auto-scrolling is enabled on the X axis or not. If requirements aren't met, this will not auto-scroll even if set to true. + /// @param autoScroll Whether auto-scrolling is enabled on the X axis or not. If requirements aren't met, this will not auto-scroll even if set to true. void SetAutoScrollX(bool autoScroll) { m_CanAutoScrollX = autoScroll; } - /// /// Gets whether auto-scrolling is enabled on the Y axis. - /// - /// Whether auto-scrolling is enabled on the Y axis. This may be true even if auto-scrolling isn't actually happening due to not meeting requirements. + /// @return Whether auto-scrolling is enabled on the Y axis. This may be true even if auto-scrolling isn't actually happening due to not meeting requirements. bool GetAutoScrollY() const { return m_CanAutoScrollY; } - /// /// Sets whether auto-scrolling is enabled on the Y axis. - /// - /// Whether auto-scrolling is enabled on the Y axis or not. If requirements aren't met, this will not auto-scroll even if set to true. + /// @param autoScroll Whether auto-scrolling is enabled on the Y axis or not. If requirements aren't met, this will not auto-scroll even if set to true. void SetAutoScrollY(bool autoScroll) { m_CanAutoScrollY = autoScroll; } - /// /// Gets the duration between auto-scroll steps. - /// - /// The duration between auto-scroll steps, in milliseconds. + /// @return The duration between auto-scroll steps, in milliseconds. int GetAutoScrollStepInterval() const { return m_AutoScrollStepInterval; } - /// /// Sets the duration between auto-scroll steps. - /// - /// The new duration between auto-scroll steps, in milliseconds. + /// @param newStepInterval The new duration between auto-scroll steps, in milliseconds. void SetAutoScrollStepInterval(int newStepInterval) { m_AutoScrollStepInterval = newStepInterval; } - /// /// Gets the auto-scroll step (pixels to advance per interval) values. - /// - /// A Vector with the auto-scroll step values. + /// @return A Vector with the auto-scroll step values. Vector GetAutoScrollStep() const { return m_AutoScrollStep; } - /// /// Sets the auto-scroll step (pixels to advance per interval) values. - /// - /// A Vector with the new auto-scroll step values. + /// @param newStep A Vector with the new auto-scroll step values. void SetAutoScrollStep(const Vector& newStep) { m_AutoScrollStep = newStep; } - /// /// Gets the auto-scroll step (pixels to advance per interval) value on the X axis. - /// - /// The auto-scroll step value on the X axis. + /// @return The auto-scroll step value on the X axis. float GetAutoScrollStepX() const { return m_AutoScrollStep.GetX(); } - /// /// Sets the auto-scroll step (pixels to advance per interval) value on the X axis. - /// - /// The new auto-scroll step value on the X axis. + /// @param newStepX The new auto-scroll step value on the X axis. void SetAutoScrollStepX(float newStepX) { m_AutoScrollStep.SetX(newStepX); } - /// /// Gets the auto-scroll step (pixels to advance per interval) value on the Y axis. - /// - /// The auto-scroll step value on the Y axis. + /// @return The auto-scroll step value on the Y axis. float GetAutoScrollStepY() const { return m_AutoScrollStep.GetY(); } - /// /// Sets the auto-scroll step (pixels to advance per interval) value on the Y axis. - /// - /// The new auto-scroll step value on the Y axis. + /// @param newStepY The new auto-scroll step value on the Y axis. void SetAutoScrollStepY(float newStepY) { m_AutoScrollStep.SetY(newStepY); } #pragma endregion #pragma region Concrete Methods - /// /// Initializes the scale factors for all auto-scaling modes for this SLBackground, then sets the appropriate factor according to the auto-scaling setting. /// Has to be done during Scene loading to correctly adjust the factors in cases the Scene does not vertically cover the player's whole screen. - /// void InitScaleFactors(); #pragma endregion #pragma region Virtual Override Methods - /// /// Updates the state of this SLBackground. - /// void Update() override; - /// /// Draws this SLBackground's current scrolled position to a bitmap. - /// - /// The bitmap to draw to. - /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. - /// Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. + /// @param targetBitmap The bitmap to draw to. + /// @param targetBox The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// @param offsetNeedsScrollRatioAdjustment Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. void Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment = false) override; #pragma endregion private: - /// /// Enumeration for the different modes of SLBackground auto-scaling. - /// enum LayerAutoScaleMode { AutoScaleOff, FitScreen, @@ -244,9 +182,7 @@ namespace RTE { bool m_IgnoreAutoScale; //!< Whether auto-scaling settings are ignored and the read-in scale factor is used instead. - /// /// Clears all the member variables of this SLBackground, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/SLTerrain.cpp b/Source/Entities/SLTerrain.cpp index 6bc50fe73b..4971f69421 100644 --- a/Source/Entities/SLTerrain.cpp +++ b/Source/Entities/SLTerrain.cpp @@ -13,8 +13,6 @@ namespace RTE { ConcreteClassInfo(SLTerrain, SceneLayer, 0); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLTerrain::Clear() { m_Width = 0; m_Height = 0; @@ -29,8 +27,6 @@ namespace RTE { m_OrbitDirection = Directions::Up; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::Create() { SceneLayer::Create(); @@ -47,8 +43,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::Create(const SLTerrain& reference) { SceneLayer::Create(reference); @@ -79,8 +73,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneLayer::ReadProperty(propName, reader)); @@ -127,8 +119,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::Save(Writer& writer) const { SceneLayer::Save(writer); @@ -179,8 +169,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: Break this down and refactor. void SLTerrain::TexturizeTerrain() { BITMAP* defaultBGLayerTexture = m_DefaultBGTextureFile.GetAsBitmap(); @@ -263,8 +251,6 @@ namespace RTE { }); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::LoadData() { SceneLayer::LoadData(); @@ -297,8 +283,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::SaveData(const std::string& pathBase, bool doAsyncSaves) { if (pathBase.empty()) { return -1; @@ -309,8 +293,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SLTerrain::ClearData() { RTEAssert(SceneLayer::ClearData() == 0, "Failed to clear material bitmap data of an SLTerrain!"); RTEAssert(m_FGColorLayer && m_FGColorLayer->ClearData() == 0, "Failed to clear the foreground color bitmap data of an SLTerrain!"); @@ -318,15 +300,11 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SLTerrain::IsAirPixel(const int pixelX, const int pixelY) const { int checkPixel = GetPixel(pixelX, pixelY); return checkPixel == MaterialColorKeys::g_MaterialAir || checkPixel == MaterialColorKeys::g_MaterialCavity; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SLTerrain::IsBoxBuried(const Box& checkBox) const { bool buried = true; buried = buried && !IsAirPixel(checkBox.GetCorner().GetFloorIntX(), checkBox.GetCorner().GetFloorIntY()); @@ -336,8 +314,6 @@ namespace RTE { return buried; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLTerrain::CleanAir() { std::vector rows(m_MainBitmap->h); // we loop through h first, because we want each thread to have sequential memory that they're touching std::iota(std::begin(rows), std::end(rows), 0); @@ -357,8 +333,6 @@ namespace RTE { }); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLTerrain::CleanAirBox(const Box& box, bool wrapsX, bool wrapsY) { int width = m_MainBitmap->w; int height = m_MainBitmap->h; @@ -398,8 +372,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: OPTIMIZE THIS, IT'S A TIME HOG. MAYBE JSUT STAMP THE OUTLINE AND SAMPLE SOME RANDOM PARTICLES? std::deque SLTerrain::EraseSilhouette(BITMAP* sprite, const Vector& pos, const Vector& pivot, const Matrix& rotation, float scale, bool makeMOPs, int skipMOP, int maxMOPs) { RTEAssert(sprite, "Null BITMAP passed to SLTerrain::EraseSilhouette"); @@ -489,8 +461,6 @@ namespace RTE { return dislodgedMOPixels; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLTerrain::Update() { SceneLayer::Update(); @@ -498,8 +468,6 @@ namespace RTE { m_BGColorLayer->SetOffset(m_Offset); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SLTerrain::Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment) { switch (m_LayerToDraw) { case LayerType::MaterialLayer: diff --git a/Source/Entities/SLTerrain.h b/Source/Entities/SLTerrain.h index a1ff35a368..36d37131c7 100644 --- a/Source/Entities/SLTerrain.h +++ b/Source/Entities/SLTerrain.h @@ -11,9 +11,7 @@ namespace RTE { class TerrainObject; class TerrainDebris; - /// /// Collection of scrolling layers that compose the terrain of the Scene. - /// class SLTerrain : public SceneLayer { public: @@ -21,9 +19,7 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; - /// /// Enumeration for the different type of layers in the SLTerrain. - /// enum class LayerType { ForegroundLayer, BackgroundLayer, @@ -31,35 +27,25 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a SLTerrain object in system memory. Create() should be called before using the object. - /// SLTerrain() { Clear(); } - /// /// Makes the SLTerrain object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a SLTerrain to be identical to another, by deep copy. - /// - /// A reference to the SLTerrain to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the SLTerrain to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const SLTerrain& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a SLTerrain object before deletion from system memory. - /// ~SLTerrain() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the SLTerrain object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { SceneLayer::Destroy(); @@ -69,193 +55,141 @@ namespace RTE { #pragma endregion #pragma region Data Handling - /// /// Whether this SLTerrain's bitmap data is loaded from a file or was generated at runtime. - /// - /// Whether this SLTerrain's bitmap data was loaded from a file or was generated at runtime. + /// @return Whether this SLTerrain's bitmap data was loaded from a file or was generated at runtime. bool IsLoadedFromDisk() const override { return (m_FGColorLayer && m_FGColorLayer->IsLoadedFromDisk()) && (m_BGColorLayer && m_BGColorLayer->IsLoadedFromDisk()); } - /// /// Loads previously specified/created bitmap data into memory. Has to be done before using this SLTerrain if the bitmap was not generated at runtime. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int LoadData() override; - /// /// Saves bitmap data currently in memory to disk. - /// - /// The filepath base to the where to save the Bitmap data. This means everything up to the extension. "FG" and "Mat" etc will be added. - /// Whether or not to save asynchronously. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param pathBase The filepath base to the where to save the Bitmap data. This means everything up to the extension. "FG" and "Mat" etc will be added. + /// @param doAsyncSaves Whether or not to save asynchronously. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int SaveData(const std::string& pathBase, bool doAsyncSaves = true) override; - /// /// Clears out any previously loaded bitmap data from memory. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int ClearData() override; #pragma endregion #pragma region Getters and Setters - /// /// Gets the width of this SLTerrain as determined by the main (material) bitmap. - /// - /// The width of this SLTerrain, in pixels. + /// @return The width of this SLTerrain, in pixels. int GetWidth() const { return m_Width; } - /// /// Gets the height of this SLTerrain as determined by the main (material) bitmap. - /// - /// The height of this SLTerrain, in pixels. + /// @return The height of this SLTerrain, in pixels. int GetHeight() const { return m_Height; } - /// /// Sets the layer of this SLTerrain that should be drawn to the screen when Draw() is called. - /// - /// The layer that should be drawn. See LayerType enumeration. + /// @param layerToDraw The layer that should be drawn. See LayerType enumeration. void SetLayerToDraw(LayerType layerToDraw) { m_LayerToDraw = layerToDraw; } - /// /// Gets the foreground color bitmap of this SLTerrain. - /// - /// A pointer to the foreground color bitmap. + /// @return A pointer to the foreground color bitmap. BITMAP* GetFGColorBitmap() { return m_FGColorLayer->GetBitmap(); } - /// /// Gets the background color bitmap of this SLTerrain. - /// - /// A pointer to the background color bitmap. + /// @return A pointer to the background color bitmap. BITMAP* GetBGColorBitmap() { return m_BGColorLayer->GetBitmap(); } - /// /// Gets the material bitmap of this SLTerrain. - /// - /// A pointer to the material bitmap. + /// @return A pointer to the material bitmap. BITMAP* GetMaterialBitmap() { return m_MainBitmap; } - /// /// Gets a specific pixel from the foreground color bitmap of this. LockBitmaps() must be called before using this method. - /// - /// The X coordinate of the pixel to get. - /// The Y coordinate of the pixel to get. - /// An int specifying the requested pixel's foreground color index. + /// @param pixelX The X coordinate of the pixel to get. + /// @param pixelY The Y coordinate of the pixel to get. + /// @return An int specifying the requested pixel's foreground color index. int GetFGColorPixel(int pixelX, int pixelY) const { return m_FGColorLayer->GetPixel(pixelX, pixelY); } - /// /// Sets a specific pixel on the foreground color bitmap of this SLTerrain to a specific color. LockBitmaps() must be called before using this method. - /// - /// The X coordinate of the pixel to set. - /// The Y coordinate of the pixel to set. - /// The color index to set the pixel to. + /// @param pixelX The X coordinate of the pixel to set. + /// @param pixelY The Y coordinate of the pixel to set. + /// @param materialID The color index to set the pixel to. void SetFGColorPixel(int pixelX, int pixelY, const int materialID) const { m_FGColorLayer->SetPixel(pixelX, pixelY, materialID); } - /// /// Gets a specific pixel from the background color bitmap of this. LockBitmaps() must be called before using this method. - /// - /// The X coordinate of the pixel to get. - /// The Y coordinate of the pixel to get. - /// An int specifying the requested pixel's background color index. + /// @param pixelX The X coordinate of the pixel to get. + /// @param pixelY The Y coordinate of the pixel to get. + /// @return An int specifying the requested pixel's background color index. int GetBGColorPixel(int pixelX, int pixelY) const { return m_BGColorLayer->GetPixel(pixelX, pixelY); } - /// /// Sets a specific pixel on the background color bitmap of this SLTerrain to a specific color. LockBitmaps() must be called before using this method. - /// - /// The X coordinate of the pixel to set. - /// The Y coordinate of the pixel to set. - /// The color index to set the pixel to. + /// @param pixelX The X coordinate of the pixel to set. + /// @param pixelY The Y coordinate of the pixel to set. + /// @param materialID The color index to set the pixel to. void SetBGColorPixel(int pixelX, int pixelY, int materialID) const { m_BGColorLayer->SetPixel(pixelX, pixelY, materialID); } - /// /// Gets a specific pixel from the material bitmap of this SceneLayer. LockBitmaps() must be called before using this method. - /// - /// The X coordinate of the pixel to get. - /// The Y coordinate of the pixel to get. - /// An int specifying the requested pixel's material index. + /// @param pixelX The X coordinate of the pixel to get. + /// @param pixelY The Y coordinate of the pixel to get. + /// @return An int specifying the requested pixel's material index. int GetMaterialPixel(int pixelX, int pixelY) const { return GetPixel(pixelX, pixelY); } - /// /// Sets a specific pixel on the material bitmap of this SLTerrain to a specific material. LockMaterialBitmap() must be called before using this method. - /// - /// The X coordinate of the pixel to set. - /// The Y coordinate of the pixel to set. - /// The material index to set the pixel to. + /// @param pixelX The X coordinate of the pixel to set. + /// @param pixelY The Y coordinate of the pixel to set. + /// @param materialID The material index to set the pixel to. void SetMaterialPixel(int pixelX, int pixelY, int materialID) { SetPixel(pixelX, pixelY, materialID); } - /// /// Indicates whether a terrain pixel is of Air or Cavity material. - /// - /// The X coordinate of the pixel to check. - /// The Y coordinate of the pixel to check. - /// Whether the terrain pixel is of Air or Cavity material. + /// @param pixelX The X coordinate of the pixel to check. + /// @param pixelY The Y coordinate of the pixel to check. + /// @return Whether the terrain pixel is of Air or Cavity material. bool IsAirPixel(int pixelX, int pixelY) const; - /// /// Checks whether a bounding box is completely buried in the terrain. - /// - /// The box to check. - /// Whether the box is completely buried, i.e. no corner sticks out in the Air or Cavity. + /// @param checkBox The box to check. + /// @return Whether the box is completely buried, i.e. no corner sticks out in the Air or Cavity. bool IsBoxBuried(const Box& checkBox) const; #pragma endregion #pragma region Concrete Methods - /// /// Gets a deque of unwrapped boxes which show the areas where the material layer has had objects applied to it since last call to ClearUpdatedMaterialAreas(). - /// - /// Reference to the deque that has been filled with Boxes which are unwrapped and may be out of bounds of the scene! + /// @return Reference to the deque that has been filled with Boxes which are unwrapped and may be out of bounds of the scene! std::deque& GetUpdatedMaterialAreas() { return m_UpdatedMaterialAreas; } - /// /// Adds a notification that an area of the material terrain has been updated. - /// - /// The Box defining the newly updated material area that can be unwrapped and may be out of bounds of the scene. + /// @param newArea The Box defining the newly updated material area that can be unwrapped and may be out of bounds of the scene. void AddUpdatedMaterialArea(const Box& newArea) { m_UpdatedMaterialAreas.emplace_back(newArea); } - /// /// Removes any color pixel in the color layer of this SLTerrain wherever there is an air material pixel in the material layer. - /// void CleanAir(); - /// /// Removes any color pixel in the color layer of this SLTerrain wherever there is an air material pixel in the material layer inside the specified box. - /// - /// Box to clean. - /// Whether the scene is X-wrapped. - /// Whether the scene is Y-wrapped. + /// @param box Box to clean. + /// @param wrapsX Whether the scene is X-wrapped. + /// @param wrapsY Whether the scene is Y-wrapped. void CleanAirBox(const Box& box, bool wrapsX, bool wrapsY); - /// /// Takes a BITMAP and scans through the pixels on this terrain for pixels which overlap with it. Erases them from the terrain and can optionally generate MOPixels based on the erased or 'dislodged' terrain pixels. - /// - /// A pointer to the source BITMAP whose silhouette will be used as a cookie-cutter on the terrain. - /// The position coordinates of the sprite. - /// The pivot coordinate of the sprite. - /// The sprite's current rotation in radians. - /// The sprite's current scale coefficient. - /// Whether to generate any MOPixels from the erased terrain pixels. - /// How many pixels to skip making MOPixels from, between each that gets made. 0 means every pixel turns into an MOPixel. - /// The max number of MOPixels to make, if they are to be made. - /// A deque filled with the MOPixels of the terrain that are now dislodged. This will be empty if makeMOPs is false. Note that ownership of all the MOPixels in the deque IS transferred! + /// @param sprite A pointer to the source BITMAP whose silhouette will be used as a cookie-cutter on the terrain. + /// @param pos The position coordinates of the sprite. + /// @param pivot The pivot coordinate of the sprite. + /// @param rotation The sprite's current rotation in radians. + /// @param scale The sprite's current scale coefficient. + /// @param makeMOPs Whether to generate any MOPixels from the erased terrain pixels. + /// @param skipMOP How many pixels to skip making MOPixels from, between each that gets made. 0 means every pixel turns into an MOPixel. + /// @param maxMOPs The max number of MOPixels to make, if they are to be made. + /// @return A deque filled with the MOPixels of the terrain that are now dislodged. This will be empty if makeMOPs is false. Note that ownership of all the MOPixels in the deque IS transferred! std::deque EraseSilhouette(BITMAP* sprite, const Vector& pos, const Vector& pivot, const Matrix& rotation, float scale, bool makeMOPs = true, int skipMOP = 2, int maxMOPs = 150); - /// /// Returns the direction of the out-of-bounds "orbit" for this scene, where the brain must path to and where dropships/rockets come from. - /// - /// The orbit direction, either Up, Down, Left or Right.. + /// @return The orbit direction, either Up, Down, Left or Right.. Directions GetOrbitDirection() { return m_OrbitDirection; } #pragma endregion #pragma region Virtual Override Methods - /// /// Updates the state of this SLTerrain. - /// void Update() override; - /// /// Draws this SLTerrain's current scrolled position to a bitmap. - /// - /// The bitmap to draw to. - /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. - /// Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. + /// @param targetBitmap The bitmap to draw to. + /// @param targetBox The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// @param offsetNeedsScrollRatioAdjustment Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. void Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment = false) override; #pragma endregion @@ -280,14 +214,10 @@ namespace RTE { Directions m_OrbitDirection; //!< The direction of the out-of-bounds "orbit" for this scene, where the brain must path to and where dropships/rockets come from. - /// /// Applies Material textures to the foreground and background color layers, based on the loaded material layer (main bitmap). - /// void TexturizeTerrain(); - /// /// Clears all the member variables of this SLTerrain, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Scene.cpp b/Source/Entities/Scene.cpp index ebf8644e6f..fe1b1eab14 100644 --- a/Source/Entities/Scene.cpp +++ b/Source/Entities/Scene.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Scene.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the Scene class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Scene.h" #include "PresetMan.h" @@ -54,22 +42,11 @@ namespace RTE { // Holds the path calculated by CalculateScenePath thread_local std::list s_ScenePath; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Area, effectively - // resetting the members of this abstraction level only. - void Scene::Area::Clear() { m_BoxList.clear(); m_Name.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Area to be identical to another, by deep copy. - int Scene::Area::Create(const Area& reference) { for (std::vector::const_iterator itr = reference.m_BoxList.begin(); itr != reference.m_BoxList.end(); ++itr) m_BoxList.push_back(*itr); @@ -79,11 +56,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Area object ready for use. - int Scene::Area::Create() { if (Serializable::Create() < 0) return -1; @@ -91,14 +63,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int Scene::Area::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -111,12 +75,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this Area with a Writer for - // later recreation with Create(Reader &reader); - int Scene::Area::Save(Writer& writer) const { Serializable::Save(writer); @@ -130,11 +88,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: AddBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a Box to this' area coverage. - bool Scene::Area::AddBox(const Box& newBox) { if (newBox.IsEmpty()) return false; @@ -143,8 +96,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Scene::Area::RemoveBox(const Box& boxToRemove) { std::vector::iterator boxToRemoveIterator = std::find(m_BoxList.begin(), m_BoxList.end(), boxToRemove); if (boxToRemoveIterator != m_BoxList.end()) { @@ -154,14 +105,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: HasNoArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this really has no Area at all, ie it doesn't have any - // Box:es with both width and height. - bool Scene::Area::HasNoArea() const { // If no boxes, then yeah we don't have any area if (m_BoxList.empty()) @@ -176,11 +119,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a point is anywhere inside this Area's coverage. - bool Scene::Area::IsInside(const Vector& point) const { std::list wrappedBoxes; for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { @@ -197,12 +135,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsInsideX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the - // X-axis only. - bool Scene::Area::IsInsideX(float pointX) const { std::list wrappedBoxes; for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { @@ -219,12 +151,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsInsideY - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the - // Y-axis only. - bool Scene::Area::IsInsideY(float pointY) const { std::list wrappedBoxes; for (std::vector::const_iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { @@ -241,12 +167,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: MovePointInsideX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Moves a coordinate to the closest value which is within any of this - // Area's Box:es, in the X axis only. - bool Scene::Area::MovePointInsideX(float& pointX, int direction) const { if (HasNoArea() || IsInsideX(pointX)) return false; @@ -298,11 +218,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetBoxInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the first Box encountered in this that contains a specific point. - Box* Scene::Area::GetBoxInside(const Vector& point) { std::list wrappedBoxes; for (std::vector::iterator aItr = m_BoxList.begin(); aItr != m_BoxList.end(); ++aItr) { @@ -320,11 +235,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: RemoveBoxInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes the first Box encountered in this that contains a specific point. - Box Scene::Area::RemoveBoxInside(const Vector& point) { Box returnBox; @@ -347,12 +257,6 @@ namespace RTE { return returnBox; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetCenterPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a center point for this of all the boxes waeighted by their sizes. - // Arguments: None. - Vector Scene::Area::GetCenterPoint() const { Vector areaCenter; @@ -374,11 +278,6 @@ namespace RTE { return areaCenter; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRandomPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a random coordinate contained within any of this' Box:es. - Vector Scene::Area::GetRandomPoint() const { // If no boxes, then can't return valid point if (m_BoxList.empty()) @@ -388,12 +287,6 @@ namespace RTE { return m_BoxList[RandomNum(0, m_BoxList.size() - 1)].GetRandomPoint(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Scene, effectively - // resetting the members of this abstraction level only. - void Scene::Clear() { m_Location.Reset(); m_LocationOffset.Reset(); @@ -454,14 +347,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Scene object ready for use. - // Arguments: The Terrain to use. Ownership IS transferred! - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - int Scene::Create(SLTerrain* pNewTerrain) { m_pTerrain = pNewTerrain; // TODO: allow setting of other stuff too @@ -470,11 +355,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a MOPixel to be identical to another, by deep copy. - int Scene::Create(const Scene& reference) { Entity::Create(reference); @@ -538,12 +418,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: LoadData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Actually loads previously specified/created data into memory. Has - // to be done before using this SceneLayer. - int Scene::LoadData(bool placeObjects, bool initPathfinding, bool placeUnits) { RTEAssert(m_pTerrain, "Terrain not instantiated before trying to load its data!"); @@ -842,12 +716,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ExpandAIPlanAssemblySchemes - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // - int Scene::ExpandAIPlanAssemblySchemes() { std::list newAIPlan; @@ -910,11 +778,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SaveData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves currently loaded bitmap data in memory to disk. - int Scene::SaveData(std::string pathBase, bool doAsyncSaves) { const std::string fullPathBase = g_PresetMan.GetFullModulePath(pathBase); if (fullPathBase.empty()) @@ -970,12 +833,6 @@ namespace RTE { return layerInfos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SavePreview - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves preview bitmap for this scene. - // - int Scene::SavePreview(const std::string& bitmapPath) { // Do not save preview for MetaScenes! if (!m_MetasceneParent.empty()) { @@ -1057,11 +914,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ClearData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears out any previously loaded bitmap data from memory. - int Scene::ClearData() { if (!m_pTerrain) return 0; @@ -1086,14 +938,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int Scene::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -1190,12 +1034,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this Scene with a Writer for - // later recreation with Create(Reader &reader); - int Scene::Save(Writer& writer) const { Entity::Save(writer); @@ -1317,8 +1155,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Scene::SaveSceneObject(Writer& writer, const SceneObject* sceneObjectToSave, bool isChildAttachable, bool saveFullData) const { auto WriteHardcodedAttachableOrNone = [this, &writer, &saveFullData](const std::string& propertyName, const Attachable* harcodedAttachable) { if (harcodedAttachable) { @@ -1569,13 +1405,6 @@ namespace RTE { writer.ObjectEnd(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the Scene object. - void Scene::Destroy(bool notInherited) { delete m_pTerrain; @@ -1614,12 +1443,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: MigrateToModule - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes this an original Preset in a different module than it was before. - // It severs ties deeply to the old module it was saved in. - bool Scene::MigrateToModule(int whichModule) { if (!Entity::MigrateToModule(whichModule)) return false; @@ -1629,12 +1452,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FillUnseenLayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a new SceneLayer for a specific team and fills it with black - // pixels that end up being a specific size on the screen. - void Scene::FillUnseenLayer(Vector pixelSize, int team, bool createNow) { if (team == Activity::NoTeam || !(pixelSize.m_X >= 1.0 && pixelSize.m_Y >= 1.0)) return; @@ -1655,11 +1472,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetUnseenLayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the unseen layer of a specific team. - void Scene::SetUnseenLayer(SceneLayer* pNewLayer, int team) { if (team == Activity::NoTeam || !pNewLayer) return; @@ -1671,11 +1483,6 @@ namespace RTE { m_apUnseenLayer[team]->SetScaleFactor(Vector((float)GetTerrain()->GetBitmap()->w / (float)m_apUnseenLayer[team]->GetBitmap()->w, (float)GetTerrain()->GetBitmap()->h / (float)m_apUnseenLayer[team]->GetBitmap()->h)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearSeenPixels - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the pixels that have been seen on a team's unseen layer. - void Scene::ClearSeenPixels(int team) { if (team != Activity::NoTeam) { // Clear all the pixels off the map, set them to key color @@ -1707,12 +1514,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CleanOrphanPixel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks a specific unseen pixel for only having two or less unseen - // neighbors, and if so, makes it seen. - bool Scene::CleanOrphanPixel(int posX, int posY, NeighborDirection checkingFrom, int team) { if (team == Activity::NoTeam || !m_apUnseenLayer[team]) return false; @@ -1786,8 +1587,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector Scene::GetDimensions() const { if (m_pTerrain) { if (const BITMAP* terrainBitmap = m_pTerrain->GetBitmap()) { @@ -1798,8 +1597,6 @@ namespace RTE { return Vector(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Scene::GetWidth() const { if (m_pTerrain) { if (const BITMAP* terrainBitmap = m_pTerrain->GetBitmap()) { @@ -1810,8 +1607,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Scene::GetHeight() const { if (m_pTerrain) { if (const BITMAP* terrainBitmap = m_pTerrain->GetBitmap()) { @@ -1822,26 +1617,10 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WrapsX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the scene wraps its scrolling around the X axis. - bool Scene::WrapsX() const { return m_pTerrain ? m_pTerrain->WrapsX() : false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WrapsY - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the scene wraps its scrolling around the Y axis. - bool Scene::WrapsY() const { return m_pTerrain ? m_pTerrain->WrapsY() : false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PlaceResidentBrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Places the individual brain of a single player which may be stationed - // on this Scene, and registers them as such in an Activity. - bool Scene::PlaceResidentBrain(int player, Activity& newActivity) { if (m_ResidentBrains[player]) { #ifdef DEBUG_BUILD @@ -1867,12 +1646,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PlaceResidentBrains - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Places the individual brains of the various players which may be - // stationed on this Scene, and registers them as such in an Activity. - int Scene::PlaceResidentBrains(Activity& newActivity) { int found = 0; @@ -1884,13 +1657,6 @@ namespace RTE { return found; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RetrieveResidentBrains - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Looks at the Activity and its players' registered brain Actors, and - // saves them as resident brains for this Scene. Done when a fight is over - // and the survivors remain! - int Scene::RetrieveResidentBrains(Activity& oldActivity) { int found = 0; @@ -1916,8 +1682,6 @@ namespace RTE { return found; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Scene::RetrieveSceneObjects(bool transferOwnership, int onlyTeam, bool noBrains) { int found = 0; @@ -1928,13 +1692,6 @@ namespace RTE { return found; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddPlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a SceneObject to be placed in this scene. Ownership IS transferred! - void Scene::AddPlacedObject(int whichSet, SceneObject* pObjectToAdd, int listOrder) { if (!pObjectToAdd) return; @@ -1957,11 +1714,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemovePlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a SceneObject placed in this scene. - void Scene::RemovePlacedObject(int whichSet, int whichToRemove) { if (m_PlacedObjects[whichSet].empty()) return; @@ -1980,12 +1732,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PickPlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the last placed object that graphically overlaps an absolute - // point in the scene. - const SceneObject* Scene::PickPlacedObject(int whichSet, Vector& scenePoint, int* pListOrderPlace) const { // REVERSE! int i = m_PlacedObjects[whichSet].size() - 1; @@ -2002,20 +1748,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PickPlacedActorInRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the last placed actor object that is closer than range to scenePoint - // - // Arguments: Which set of placed objects to pick from. See the PlacedObjectSets enum. - // The point in absolute scene coordinates that will be used to pick the - // closest placed SceneObject near it. - // The range to check for nearby objects. - // An int which will be filled out with the order place of any found object - // in the list. if nothing is found, it will get a value of -1. - // - // Return value: The closest actor SceneObject, if any. Ownership is NOT transferred! - const SceneObject* Scene::PickPlacedActorInRange(int whichSet, Vector& scenePoint, int range, int* pListOrderPlace) const { SceneObject* pFoundObject = 0; float sqrDistance = static_cast(range * range); @@ -2042,12 +1774,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePlacedObjects - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updated the objects in the placed scene objects list of this. This is - // mostly for the editor to represent the items correctly. - void Scene::UpdatePlacedObjects(int whichSet) { if (whichSet == PLACEONLOAD) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) @@ -2060,11 +1786,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearPlacedObjectSet - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes all entries in a specific set of placed Objects. - int Scene::ClearPlacedObjectSet(int whichSet, bool weHaveOwnership) { if (weHaveOwnership) { for (std::list::iterator itr = m_PlacedObjects[whichSet].begin(); itr != m_PlacedObjects[whichSet].end(); ++itr) { @@ -2077,12 +1798,6 @@ namespace RTE { return count; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetResidentBrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the resident brain Actor of a specific player from this scene, - // if there is any. OWNERSHIP IS NOT TRANSFERRED! - SceneObject* Scene::GetResidentBrain(int player) const { // if (m_ResidentBrains[player]) return m_ResidentBrains[player]; @@ -2093,12 +1808,6 @@ namespace RTE { // } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetResidentBrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the resident brain Actor of a specific player from this scene, - // if there is any. Ownership IS transferred! - void Scene::SetResidentBrain(int player, SceneObject* pNewBrain) { if (MovableObject* asMo = dynamic_cast(m_ResidentBrains[player])) { asMo->DestroyScriptState(); @@ -2107,11 +1816,6 @@ namespace RTE { m_ResidentBrains[player] = pNewBrain; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetResidentBrainCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of brains currently residing in this scene. - int Scene::GetResidentBrainCount() const { int count = 0; for (int p = Players::PlayerOne; p < Players::MaxPlayerCount; ++p) { @@ -2121,11 +1825,6 @@ namespace RTE { return count; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds or modifies an existing area of this Scene. - bool Scene::SetArea(Area& newArea) { for (std::list::iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) { // Try to find an existing area of the same name @@ -2142,12 +1841,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks for the existence of a specific Area identified by a name. - // This won't throw any errors to the console if the Area isn't found. - bool Scene::HasArea(std::string areaName) { for (std::list::iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) { if ((*aItr).GetName() == areaName) @@ -2156,11 +1849,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a specific area box identified by a name. Ownership is NOT transferred! - Scene::Area* Scene::GetArea(const std::string_view& areaName, bool required) { for (Scene::Area& area: m_AreaList) { if (area.GetName() == areaName) { @@ -2175,11 +1863,6 @@ namespace RTE { return nullptr; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a specific Area identified by a name. - bool Scene::RemoveArea(std::string areaName) { for (std::list::iterator aItr = m_AreaList.begin(); aItr != m_AreaList.end(); ++aItr) { if ((*aItr).GetName() == areaName) { @@ -2190,13 +1873,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WithinArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if a point is within a specific named Area of this Scene. If - // no Area of the name is found, this just returns false without error. - // Arguments: The name of the Area to try to check against. - bool Scene::WithinArea(std::string areaName, const Vector& point) const { if (areaName.empty()) return false; @@ -2209,11 +1885,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTeamOwnership - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the team who owns this Scene in a Metagame - void Scene::SetTeamOwnership(int newTeam) { m_OwnedByTeam = newTeam; @@ -2226,12 +1897,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalcBuildBudgetUse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Figure out exactly how much of the build budget would be used if - // as many blueprint objects as can be afforded and exists would be built. - float Scene::CalcBuildBudgetUse(int player, int* pAffordCount, int* pAffordAIPlanCount) const { if (pAffordCount) *pAffordCount = 0; @@ -2343,13 +2008,6 @@ namespace RTE { return fundsAfforded; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyAIPlan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Puts the pre-built AI base plan into effect by transferring as many - // pieces as the current base budget allows from the AI plan to the actual - // blueprints to be built at this Scene. - float Scene::ApplyAIPlan(int player, int* pObjectsApplied) { if (pObjectsApplied) *pObjectsApplied = 0; @@ -2408,13 +2066,6 @@ namespace RTE { return valueOfApplied; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyBuildBudget - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Actually builds as many objects in the specific player's Blueprint - // list as can be afforded by his build budget. The budget is deducted - // accordingly. - float Scene::ApplyBuildBudget(int player, int* pObjectsBuilt) { if (pObjectsBuilt) *pObjectsBuilt = 0; @@ -2622,12 +2273,6 @@ namespace RTE { return fundsSpent; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveAllPlacedActors - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Remove all actors that are in the placed set of objects to load for - // this scene. All except for an optionally specified team, that is. - int Scene::RemoveAllPlacedActors(int exceptTeam) { int removedCount = 0; @@ -2664,12 +2309,6 @@ namespace RTE { return removedCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOwnerOfAllDoors - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the ownership of all doors placed in this scene to a specific team - // Arguments: The team to change the ownership to - int Scene::SetOwnerOfAllDoors(int team, int player) { int changedCount = 0; @@ -2693,12 +2332,6 @@ namespace RTE { return changedCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetPathFinding - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Recalculates all of the pathfinding data. This is very expensive, so - // do very rarely! - void Scene::ResetPathFinding() { GetPathFinder(Activity::Teams::NoTeam)->RecalculateAllCosts(); for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { @@ -2708,20 +2341,12 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - void Scene::BlockUntilAllPathingRequestsComplete() { for (int team = Activity::Teams::NoTeam; team < Activity::Teams::MaxTeamCount; ++team) { while (GetPathFinder(static_cast(team))->GetCurrentPathingRequests() != 0) {}; } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePathFinding - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Recalculates only the areas of the pathfinding data that have been - // marked as outdated. - void Scene::UpdatePathFinding() { ZoneScoped; @@ -2766,12 +2391,6 @@ namespace RTE { m_PathfindingUpdated = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculatePath - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates and returns the least difficult path between two points on - // the current scene. Takes both distance and materials into account. - float Scene::CalculatePath(const Vector& start, const Vector& end, std::list& pathResult, float digStrength, Activity::Teams team) { float totalCostResult = -1; @@ -2809,15 +2428,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Lock - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Locks all dynamic internal scene bitmaps so that manipulaitons of the - // scene's color and matter representations can take place. - // Doing it in a separate method like this is more efficient because - // many bitmap manipulaitons can be performed between a lock and unlock. - // UnlockScene() should always be called after accesses are completed. - void Scene::Lock() { // RTEAssert(!m_Locked, "Hey, locking already locked scene!"); if (!m_Locked) { @@ -2826,14 +2436,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Unlock - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Unlocks the scene's bitmaps and prevents access to display memory. - // Doing it in a separate method like this is more efficient because - // many bitmap accesses can be performed between a lock and an unlock. - // UnlockScene() should only be called after LockScene(). - void Scene::Unlock() { // RTEAssert(m_Locked, "Hey, unlocking already unlocked scene!"); if (m_Locked) { @@ -2842,12 +2444,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Scene. Supposed to be done every frame - // before drawing. - void Scene::Update() { ZoneScoped; @@ -2891,8 +2487,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::unique_ptr& Scene::GetPathFinder(Activity::Teams team) { // Note - we use + 1 when getting pathfinders by index, because our shared NoTeam pathfinder occupies index 0, and the rest come after that. return m_pPathFinders[static_cast(team) + 1]; diff --git a/Source/Entities/Scene.h b/Source/Entities/Scene.h index d688344a4c..165e7983bc 100644 --- a/Source/Entities/Scene.h +++ b/Source/Entities/Scene.h @@ -1,18 +1,11 @@ #ifndef _RTESCENE_ #define _RTESCENE_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Scene.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the Scene class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the Scene class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Entity.h" #include "Box.h" #include "Activity.h" @@ -35,20 +28,12 @@ namespace RTE { std::unique_ptr bitmap; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: Scene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Contains everything that defines a complete scene. - // Parent(s): Entity. - // Class history: 08/02/2006 Scene created. - + /// Contains everything that defines a complete scene. class Scene : public Entity { friend struct EntityLuaBindings; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableOverrideMethods; ClassInfoGetters; @@ -61,13 +46,7 @@ namespace RTE { PLACEDSETSCOUNT }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Nested class: Area - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Something to bundle the properties of scene areas together - // Parent(s): Serializable. - // Class history: 07/18/2008 Area created. - + /// Something to bundle the properties of scene areas together class Area : public Serializable { @@ -77,20 +56,13 @@ namespace RTE { friend struct EntityLuaBindings; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableClassNameGetter; SerializableOverrideMethods; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Area - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Area object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a Area object in system + /// memory. Create() should be called before using the object. Area() { Clear(); Create(); @@ -105,185 +77,106 @@ namespace RTE { Create(reference); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Area object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the Area object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Area to be identical to another, by deep copy. - // Arguments: A reference to the Area to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a Area to be identical to another, by deep copy. + /// @param reference A reference to the Area to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const Area& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Serializable, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire Serializable, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: AddBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a Box to this' area coverage. - // Arguments: The Box to add. A copy will be made and added. - // Return value: Whether the Box was successfully added or not. - + /// Adds a Box to this' area coverage. + /// @param newBox The Box to add. A copy will be made and added. + /// @return Whether the Box was successfully added or not. bool AddBox(const Box& newBox); - /// /// Removes the first Box in the Area that has the same Corner, Width and Height of the passed-in Box. - /// - /// A Box whose values are used to determine what Box to remove. - /// Whether or not a Box was removed. + /// @param boxToRemove A Box whose values are used to determine what Box to remove. + /// @return Whether or not a Box was removed. bool RemoveBox(const Box& boxToRemove); - /// /// Gets the first Box in this Area. - /// - /// The first Box in this Area. + /// @return The first Box in this Area. const Box* GetFirstBox() const { return m_BoxList.empty() ? nullptr : &m_BoxList[0]; } - /// /// Gets the boxes for this area. - /// - /// The boxes in this Area. + /// @return The boxes in this Area. const std::vector& GetBoxes() const { return m_BoxList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: HasNoArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this really has no Area at all, ie it doesn't have any - // Box:es with both width and height. - // Arguments: None. - // Return value: Whether this Area actually covers any area. - + /// Shows whether this really has no Area at all, ie it doesn't have any + /// Box:es with both width and height. + /// @return Whether this Area actually covers any area. bool HasNoArea() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a point is anywhere inside this Area's coverage. - // Arguments: The point to check if it's inside the Area, in absolute scene coordinates. - // Return value: Whether the point is inside any of this Area's Box:es. - + /// Shows whether a point is anywhere inside this Area's coverage. + /// @param point The point to check if it's inside the Area, in absolute scene coordinates. + /// @return Whether the point is inside any of this Area's Box:es. bool IsInside(const Vector& point) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsInsideX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the - // X-axis only. - // Arguments: The x coord to check if it's inside the Area, in absolute scene units. - // Return value: Whether the point is inside any of this Area's Box:es in the X axis. - + /// Shows whether a coordinate is anywhere inside this Area's coverage, in the + /// X-axis only. + /// @param pointX The x coord to check if it's inside the Area, in absolute scene units. + /// @return Whether the point is inside any of this Area's Box:es in the X axis. bool IsInsideX(float pointX) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsInsideY - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a coordinate is anywhere inside this Area's coverage, in the - // Y-axis only. - // Arguments: The x coord to check if it's inside the Area, in absolute scene units. - // Return value: Whether the point is inside any of this Area's Box:es in the Y axis. - + /// Shows whether a coordinate is anywhere inside this Area's coverage, in the + /// Y-axis only. + /// @param pointY The x coord to check if it's inside the Area, in absolute scene units. + /// @return Whether the point is inside any of this Area's Box:es in the Y axis. bool IsInsideY(float pointY) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: MovePointInsideX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Moves a coordinate to the closest value which is within any of this - // Area's Box:es, in the X axis only. - // Arguments: The x coord to transform to the closest inside poistion in the x-axis. - // Which direction to limit the search to. < 0 means can only look in the - // negative dir, 0 means can look in both directions. - // Return value: Whether the point was moved at all to get inside this' x-space. - + /// Moves a coordinate to the closest value which is within any of this + /// Area's Box:es, in the X axis only. + /// @param pointX The x coord to transform to the closest inside poistion in the x-axis. + /// @param direction Which direction to limit the search to. < 0 means can only look in the (default: 0) + /// negative dir, 0 means can look in both directions. + /// @return Whether the point was moved at all to get inside this' x-space. bool MovePointInsideX(float& pointX, int direction = 0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetBoxInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the first Box encountered in this that contains a specific point. - // Arguments: The point to check for Box collision, in absolute scene coordinates. - // Return value: Pointer to the first Box which was found to contain the point. 0 if - // none was found. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the first Box encountered in this that contains a specific point. + /// @param point The point to check for Box collision, in absolute scene coordinates. + /// @return Pointer to the first Box which was found to contain the point. 0 if + /// none was found. OWNERSHIP IS NOT TRANSFERRED! Box* GetBoxInside(const Vector& point); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: RemoveBoxInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes the first Box encountered in this that contains a specific point. - // Arguments: The point to check for Box collision, in absolute scene coordinates. - // Return value: Copy of the Box that was removed. Will be NoArea Box if none was found. - + /// Removes the first Box encountered in this that contains a specific point. + /// @param point The point to check for Box collision, in absolute scene coordinates. + /// @return Copy of the Box that was removed. Will be NoArea Box if none was found. Box RemoveBoxInside(const Vector& point); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetCenterPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a center point for this of all the boxes waeighted by their sizes. - // Arguments: None. - // Return value: A center point of this area, can be outside the actual area though, if - // pulled apart by two separate boxes, for example. 0,0 if this has no Area - + /// Gets a center point for this of all the boxes waeighted by their sizes. + /// @return A center point of this area, can be outside the actual area though, if + /// pulled apart by two separate boxes, for example. 0,0 if this has no Area Vector GetCenterPoint() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRandomPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a random coordinate contained within any of this' Box:es. - // Arguments: None. - // Return value: A random point that is within this Area. 0,0 if this has no Area - + /// Gets a random coordinate contained within any of this' Box:es. + /// @return A random point that is within this Area. 0,0 if this has no Area Vector GetRandomPoint() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the name of the Area - // Arguments: None. - // Return value: The name used to ID this Area. - + /// Gets the name of the Area + /// @return The name used to ID this Area. std::string GetName() const { return m_Name; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // The list of Box:es defining the Area in the owner Scene std::vector m_BoxList; // The name tag of this Area std::string m_Name; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Exit, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Exit, effectively + /// resetting the members of this abstraction level only. void Clear(); }; @@ -308,584 +201,328 @@ namespace RTE { // Concrete allocation and cloning definitions EntityAllocation(Scene) - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: Scene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a Scene object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a Scene object in system + /// memory. Create() should be called before using the object. Scene() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~Scene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a Scene object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a Scene object before deletion + /// from system memory. ~Scene() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the Scene object ready for use. - // Arguments: The Terrain to use. Ownership IS transferred! - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the Scene object ready for use. + /// @param pNewTerrain The Terrain to use. Ownership IS transferred! + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(SLTerrain* pNewTerrain); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a Scene to be identical to another, by deep copy. - // Arguments: A reference to the Scene to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a Scene to be identical to another, by deep copy. + /// @param reference A reference to the Scene to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const Scene& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: LoadData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Actually loads previously specified/created data into memory. Has - // to be done before using this Scene. - // Arguments: Whetehr to actually place out all the sceneobjects associated with the - // Scene's definition. If not, they still remain in the internal placed - // objects list. This avoids messing with the MovableMan at all. - // Whether to do pathfinding init, which should be avoided if we are only - // loading and saving purposes of MetaMan, for example. - // Whether to place actors and deployments (doors not affected). - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Actually loads previously specified/created data into memory. Has + /// to be done before using this Scene. + /// @param placeObjects Whetehr to actually place out all the sceneobjects associated with the (default: true) + /// @param initPathfinding Scene's definition. If not, they still remain in the internal placed (default: true) + /// objects list. This avoids messing with the MovableMan at all. + /// @param placeUnits Whether to do pathfinding init, which should be avoided if we are only (default: true) + /// loading and saving purposes of MetaMan, for example. + /// Whether to place actors and deployments (doors not affected). + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int LoadData(bool placeObjects = true, bool initPathfinding = true, bool placeUnits = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ExpandAIPlanAssemblySchemes - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Replace all assembly shemes by corresponding bunker assemblies in - // AI plan objects set. - // Arguments: None. - // Return value: None. - + /// Replace all assembly shemes by corresponding bunker assemblies in + /// AI plan objects set. int ExpandAIPlanAssemblySchemes(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SaveData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves data currently in memory to disk. - // Arguments: The filepath base to the where to save the Bitmap data. This means - // everything up to the extension. "FG" and "Mat" etc will be added. - // Whether or not to save asynchronously. - // Return value: An error return value signaling success or any particular failure. - // Anything below 0 is an error signal. - + /// Saves data currently in memory to disk. + /// @param pathBase The filepath base to the where to save the Bitmap data. This means + /// everything up to the extension. "FG" and "Mat" etc will be added. + /// @param doAsyncSaves Whether or not to save asynchronously. (default: true) + /// @return An error return value signaling success or any particular failure. + /// Anything below 0 is an error signal. int SaveData(std::string pathBase, bool doAsyncSaves = true); std::vector GetCopiedSceneLayerBitmaps(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SavePreview - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves preview bitmap for this scene. - // - // Arguments: The full filepath the where to save the Bitmap data. - // Return value: None. - + /// Saves preview bitmap for this scene. + /// @param bitmapPath The full filepath the where to save the Bitmap data. int SavePreview(const std::string& bitmapPath); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ClearData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears out any previously loaded bitmap data from memory. - // Arguments: None. - // Return value: An error return value signaling success or any particular failure. - // Anything below 0 is an error signal. - + /// Clears out any previously loaded bitmap data from memory. + /// @return An error return value signaling success or any particular failure. + /// Anything below 0 is an error signal. int ClearData(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Scene, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire Scene, including its inherited members, to + /// their default settings or values. void Reset() override { Clear(); Entity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the Scene object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the Scene object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: MigrateToModule - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes this an original Preset in a different module than it was before. - // It severs ties deeply to the old module it was saved in. - // Arguments: The ID of the new module. - // Return value: Whether the migration was successful. If you tried to migrate to the - // same module it already was in, this would return false. - + /// Makes this an original Preset in a different module than it was before. + /// It severs ties deeply to the old module it was saved in. + /// @param whichModule The ID of the new module. + /// @return Whether the migration was successful. If you tried to migrate to the + /// same module it already was in, this would return false. bool MigrateToModule(int whichModule) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLocation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified location of this scene on the planet view - // Arguments: None. - // Return value: A Vector showing the location of this scene on the planet view. - + /// Gets the specified location of this scene on the planet view + /// @return A Vector showing the location of this scene on the planet view. Vector GetLocation() const { return m_Location; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLocationOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified temporary location offset of this scene on the planet view. - // Arguments: None. - // Return value: A Vector showing the temporary location offset of this scene on the planet view. - + /// Gets the specified temporary location offset of this scene on the planet view. + /// @return A Vector showing the temporary location offset of this scene on the planet view. Vector GetLocationOffset() const { return m_LocationOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLocationOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the specified temporary location offset of this scene on the planet view. - // Arguments: A Vector showing the temporary location offset of this scene on the planet view. - // Return value: None. - + /// Sets the specified temporary location offset of this scene on the planet view. + /// @param newOffset A Vector showing the temporary location offset of this scene on the planet view. void SetLocationOffset(Vector newOffset) { m_LocationOffset = newOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsMetagamePlayable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is compatible with metagame play at all. - // Arguments: None. - // Return value: Whether this can be used on the metagame map. - + /// Shows whether this is compatible with metagame play at all. + /// @return Whether this can be used on the metagame map. bool IsMetagamePlayable() const { return m_MetagamePlayable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsRevealed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this is revealed on the metagame map. - // Arguments: None. - // Return value: Whether this can be seen on the metagame map yet. - + /// Shows whether this is revealed on the metagame map. + /// @return Whether this can be seen on the metagame map yet. bool IsRevealed() const { return m_Revealed; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTeamOwnership - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows which team owns this Scene in a Metagame, if any - // Arguments: None. - // Return value: The team that owns this site in a Metagame, if any - + /// Shows which team owns this Scene in a Metagame, if any + /// @return The team that owns this site in a Metagame, if any int GetTeamOwnership() const { return m_OwnedByTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRoundIncome - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows how much income this Scene pulls in for its owning team each - // round of a metagame. - // Arguments: None. - // Return value: The income in oz that this generates each metagame round. - + /// Shows how much income this Scene pulls in for its owning team each + /// round of a metagame. + /// @return The income in oz that this generates each metagame round. float GetRoundIncome() const { return m_RoundIncome; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBuildBudget - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows how much gold this Scene is budgeted to be built for this round. - // Arguments: Which player's set budget to show. - // Return value: The budget in oz that this is allocated to have built for this round. - + /// Shows how much gold this Scene is budgeted to be built for this round. + /// @param player Which player's set budget to show. + /// @return The budget in oz that this is allocated to have built for this round. float GetBuildBudget(int player) const { return m_BuildBudget[player]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBuildBudgetRatio - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows how much of a player's budget this Scene is allocated to be - // built for this round. - // Arguments: Which player's set budget ratio to show. - // Return value: The budget in normalized ratio that this is allocated last round. - + /// Shows how much of a player's budget this Scene is allocated to be + /// built for this round. + /// @param player Which player's set budget ratio to show. + /// @return The budget in normalized ratio that this is allocated last round. float GetBuildBudgetRatio(int player) const { return m_BuildBudgetRatio[player]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetAutoDesigned - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this should be automatically designed by the AI plan - // even if owned by human players. - // Arguments: What to set the setting to. - // Return value: None. - + /// Sets whether this should be automatically designed by the AI plan + /// even if owned by human players. + /// @param autoDesigned What to set the setting to. (default: true) void SetAutoDesigned(bool autoDesigned = true) { m_AutoDesigned = autoDesigned; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAutoDesigned - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether this should be automatically designed by the AI plan - // even if owned by human players. - // Arguments: None. - // Return value: Whether this should be autodesigned or not. - + /// Tells whether this should be automatically designed by the AI plan + /// even if owned by human players. + /// @return Whether this should be autodesigned or not. bool GetAutoDesigned() const { return m_AutoDesigned; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTotalInvestment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the total defense investment this scene has experienced by all - // teams since the metagame started. - // Arguments: What to set the total investment in gold oz) to. - // Return value: None. - + /// Sets the total defense investment this scene has experienced by all + /// teams since the metagame started. + /// @param totalInvestment What to set the total investment in gold oz) to. void SetTotalInvestment(float totalInvestment) { m_TotalInvestment = totalInvestment; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalInvestment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total defense investment this scene has experienced by all - // teams since the metagame started. - // Arguments: None. - // Return value: The total investment in this scene. - + /// Gets the total defense investment this scene has experienced by all + /// teams since the metagame started. + /// @return The total investment in this scene. float GetTotalInvestment() const { return m_TotalInvestment; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTerrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the SLTerrain. - // Arguments: None. - // Return value: A pointer to the SLTerrain. Ownership is NOT transferred! - + /// Gets the SLTerrain. + /// @return A pointer to the SLTerrain. Ownership is NOT transferred! SLTerrain* GetTerrain() { return m_pTerrain; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBackLayers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets access to the background layer list. - // Arguments: None. - // Return value: A reference to the std::list containing all the background layers. - // Ownership is NOT transferred! - + /// Gets access to the background layer list. + /// @return A reference to the std::list containing all the background layers. + /// Ownership is NOT transferred! std::list& GetBackLayers() { return m_BackLayerList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds area to the list if this scene's areas. - // Arguments: Area to add. - // Return value: None. - + /// Adds area to the list if this scene's areas. + /// @param m_AreaList.push_back(newArea Area to add. void AddArea(Scene::Area& newArea) { m_AreaList.push_back(newArea); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FillUnseenLayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a new SceneLayer for a specific team and fills it with black - // pixels that end up being a specific size on the screen. - // Arguments: A Vector with the desired dimensions of the unseen layer's chunky pixels. - // Which team to get the unseen layer for. - // Whether to create the unseen layers now, or wait until next time - // LoadData is called on this. - // Return value: None. - + /// Creates a new SceneLayer for a specific team and fills it with black + /// pixels that end up being a specific size on the screen. + /// @param pixelSize A Vector with the desired dimensions of the unseen layer's chunky pixels. + /// @param team Which team to get the unseen layer for. (default: Activity::TeamOne) + /// @param createNow Whether to create the unseen layers now, or wait until next time (default: true) + /// LoadData is called on this. void FillUnseenLayer(Vector pixelSize, int team = Activity::TeamOne, bool createNow = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetUnseenLayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the unseen layer of a specific team. - // Arguments: The new SceneLayer to use as the new unseen layer, Ownership IS XFERRED! - // Which team to get the unseen layer for. - // Return value: None. - + /// Sets the unseen layer of a specific team. + /// @param pNewLayer The new SceneLayer to use as the new unseen layer, Ownership IS XFERRED! + /// @param team Which team to get the unseen layer for. (default: Activity::TeamOne) void SetUnseenLayer(SceneLayer* pNewLayer, int team = Activity::TeamOne); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetUnseenLayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the unseen layer of a specific team. - // Arguments: Which team to get the unseen layer for. - // Return value: A pointer to the SceneLayer representing what hasn't been seen by a - // specific team yet. Ownership is NOT transferred! - + /// Gets the unseen layer of a specific team. + /// @param team Which team to get the unseen layer for. (default: Activity::TeamOne) + /// @return A pointer to the SceneLayer representing what hasn't been seen by a + /// specific team yet. Ownership is NOT transferred! SceneLayer* GetUnseenLayer(int team = Activity::TeamOne) const { return team != Activity::NoTeam ? m_apUnseenLayer[team] : 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSeenPixels - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the list of pixels that have been seen on a team's unseen layer. - // Arguments: Which team to get the unseen layer for. - // Return value: The list of pixel coordinates in the unseen layer's scale. - + /// Gets the list of pixels that have been seen on a team's unseen layer. + /// @param team Which team to get the unseen layer for. (default: Activity::TeamOne) + /// @return The list of pixel coordinates in the unseen layer's scale. std::list& GetSeenPixels(int team = Activity::TeamOne) { return m_SeenPixels[team]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearSeenPixels - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the pixels that have been seen on a team's unseen layer. - // Arguments: Which team to get the unseen layer for. - // Return value: None. - + /// Clears the pixels that have been seen on a team's unseen layer. + /// @param team Which team to get the unseen layer for. (default: Activity::TeamOne) void ClearSeenPixels(int team = Activity::TeamOne); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CleanOrphanPixel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks a specific unseen pixel for only having two or less unseen - // neighbors, and if so, makes it seen. - // Arguments: Coordinates to the pixel to check for orphaness. - // The direction we might be checking 'from', ie the neighbor we can - // already assume is seen without poking at the unseen map. - // Which team's unseen layer to check the pixel on. - // Return value: Whether the pixel was deemed to be orphan and thus cleaned up. - + /// Checks a specific unseen pixel for only having two or less unseen + /// neighbors, and if so, makes it seen. + /// @param posX Coordinates to the pixel to check for orphaness. + /// @param posY The direction we might be checking 'from', ie the neighbor we can + /// already assume is seen without poking at the unseen map. + /// @param checkingFrom Which team's unseen layer to check the pixel on. (default: NODIR) + /// @return Whether the pixel was deemed to be orphan and thus cleaned up. bool CleanOrphanPixel(int posX, int posY, NeighborDirection checkingFrom = NODIR, int team = Activity::TeamOne); - /// /// Gets the total dimensions (width and height) of the scene, in pixels. - /// - /// A Vector describing the scene dimensions. + /// @return A Vector describing the scene dimensions. Vector GetDimensions() const; - /// /// Gets the total width of the scene, in pixels. - /// - /// An int describing the scene width. + /// @return An int describing the scene width. int GetWidth() const; - /// /// Gets the total height of the scene, in pixels. - /// - /// An int describing the scene height. + /// @return An int describing the scene height. int GetHeight() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WrapsX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the scene wraps its scrolling around the X axis. - // Arguments: None. - // Return value: Whether the scene wraps around the X axis or not. - + /// Indicates whether the scene wraps its scrolling around the X axis. + /// @return Whether the scene wraps around the X axis or not. bool WrapsX() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WrapsY - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the scene wraps its scrolling around the Y axis. - // Arguments: None. - // Return value: Whether the scene wraps around the Y axis or not. - + /// Indicates whether the scene wraps its scrolling around the Y axis. + /// @return Whether the scene wraps around the Y axis or not. bool WrapsY() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PlaceResidentBrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Places the individual brain of a single player which may be stationed - // on this Scene, and registers them as such in an Activity. - // Arguments: The player's brain to place. - // The Activity to register the placed brains with. OWNERSHIP IS NOT TRANSFERRED! - // Return value: If the brain was successfully found as resident and placed. - + /// Places the individual brain of a single player which may be stationed + /// on this Scene, and registers them as such in an Activity. + /// @param player The player's brain to place. + /// @param newActivity The Activity to register the placed brains with. OWNERSHIP IS NOT TRANSFERRED! + /// @return If the brain was successfully found as resident and placed. bool PlaceResidentBrain(int player, Activity& newActivity); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PlaceResidentBrains - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Places the individual brains of the various players which may be - // stationed on this Scene, and registers them as such in an Activity. - // Arguments: The Activity to register the placed brains with. OWNERSHIP IS NOT TRANSFERRED! - // Return value: How many brains were finally placed. - + /// Places the individual brains of the various players which may be + /// stationed on this Scene, and registers them as such in an Activity. + /// @param newActivity The Activity to register the placed brains with. OWNERSHIP IS NOT TRANSFERRED! + /// @return How many brains were finally placed. int PlaceResidentBrains(Activity& newActivity); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RetrieveResidentBrains - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Looks at the Activity and its players' registered brain Actors, and - // saves them as resident brains for this Scene. Done when a fight is over - // and the survivors remain! - // Arguments: The Activity to check for registered brains. OWNERSHIP IS NOT TRANSFERRED! - // Return value: How many brains were found registered with the passed in Activity. - + /// Looks at the Activity and its players' registered brain Actors, and + /// saves them as resident brains for this Scene. Done when a fight is over + /// and the survivors remain! + /// @param oldActivity The Activity to check for registered brains. OWNERSHIP IS NOT TRANSFERRED! + /// @return How many brains were found registered with the passed in Activity. int RetrieveResidentBrains(Activity& oldActivity); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RetrieveSceneObjects - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sucks up all the Actors, Items and Particles currently active in MovableMan and - // puts them into this' list of objects to place on next load. - // Arguments: The team to only retrieve Actors of. If NoTeam, then all will be grabbed. - // Whether to not get any brains at all. - // Return value: How many objects were found knocking about in the world, and stored. - + /// Sucks up all the Actors, Items and Particles currently active in MovableMan and + /// puts them into this' list of objects to place on next load. + /// @param transferOwnership The team to only retrieve Actors of. If NoTeam, then all will be grabbed. + /// @param onlyTeam Whether to not get any brains at all. (default: -1) + /// @return How many objects were found knocking about in the world, and stored. int RetrieveSceneObjects(bool transferOwnership, int onlyTeam = -1, bool noBrains = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlacedObjects - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the list of SceneObject:s which are placed in this scene on loading. - // Arguments: Which set of placed objects to get. See the PlacedObjectSets enum. - // Return value: The list of of placed objects. Ownership is NOT transferred! - + /// Gets the list of SceneObject:s which are placed in this scene on loading. + /// @param whichSet Which set of placed objects to get. See the PlacedObjectSets enum. + /// @return The list of of placed objects. Ownership is NOT transferred! const std::list* GetPlacedObjects(int whichSet) const { return &m_PlacedObjects[whichSet]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddPlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a SceneObject to be placed in this scene. Ownership IS transferred! - // Arguments: Which set of placed objects to add to. See the PlacedObjectSets enum. - // The SceneObject instance to add, OIT! - // Where in the list the object should be inserted. -1 means at the end - // of the list. - // Return value: None. - + /// Adds a SceneObject to be placed in this scene. Ownership IS transferred! + /// @param whichSet Which set of placed objects to add to. See the PlacedObjectSets enum. + /// @param pObjectToAdd The SceneObject instance to add, OIT! + /// @param listOrder Where in the list the object should be inserted. -1 means at the end (default: -1) + /// of the list. void AddPlacedObject(int whichSet, SceneObject* pObjectToAdd, int listOrder = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemovePlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a SceneObject placed in this scene. - // Arguments: Which set of placed objects to rem from. See the PlacedObjectSets enum. - // The list order number of the object to remove. If -1, the last one is removed. - // Return value: None. - + /// Removes a SceneObject placed in this scene. + /// @param whichSet Which set of placed objects to rem from. See the PlacedObjectSets enum. + /// @param whichToRemove The list order number of the object to remove. If -1, the last one is removed. (default: -1) void RemovePlacedObject(int whichSet, int whichToRemove = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PickPlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the last placed object that graphically overlaps an absolute - // point in the scene. - // Arguments: Which set of placed objects to pick from. See the PlacedObjectSets enum. - // The point in absolute scene coordinates that will be used to pick the - // last placed SceneObject which overlaps it. - // An int which will be filled out with the order place of any found object - // in the list. if nothing is found, it will get a value of -1. - // Return value: The last hit SceneObject, if any. Ownership is NOT transferred! - + /// Returns the last placed object that graphically overlaps an absolute + /// point in the scene. + /// @param whichSet Which set of placed objects to pick from. See the PlacedObjectSets enum. + /// @param scenePoint The point in absolute scene coordinates that will be used to pick the + /// last placed SceneObject which overlaps it. + /// @param pListOrderPlace An int which will be filled out with the order place of any found object (default: 0) + /// in the list. if nothing is found, it will get a value of -1. + /// @return The last hit SceneObject, if any. Ownership is NOT transferred! const SceneObject* PickPlacedObject(int whichSet, Vector& scenePoint, int* pListOrderPlace = 0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PickPlacedActorInRange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the last placed actor object that is closer than range to scenePoint - // - // Arguments: Which set of placed objects to pick from. See the PlacedObjectSets enum. - // The point in absolute scene coordinates that will be used to pick the - // closest placed SceneObject near it. - // The range to check for nearby objects. - // An int which will be filled out with the order place of any found object - // in the list. if nothing is found, it will get a value of -1. - // - // Return value: The closest actor SceneObject, if any. Ownership is NOT transferred! - + /// Returns the last placed actor object that is closer than range to scenePoint + /// @param whichSet Which set of placed objects to pick from. See the PlacedObjectSets enum. + /// @param scenePoint The point in absolute scene coordinates that will be used to pick the + /// closest placed SceneObject near it. + /// @param range The range to check for nearby objects. + /// @param pListOrderPlace An int which will be filled out with the order place of any found object + /// in the list. if nothing is found, it will get a value of -1. + /// @return The closest actor SceneObject, if any. Ownership is NOT transferred! const SceneObject* PickPlacedActorInRange(int whichSet, Vector& scenePoint, int range, int* pListOrderPlace) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePlacedObjects - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updated the objects in the placed scene objects list of this. This is - // mostly for the editor to represent the items correctly. - // Arguments: Which set of placed objects to update. See the PlacedObjectSets enum. - // Return value: None. - + /// Updated the objects in the placed scene objects list of this. This is + /// mostly for the editor to represent the items correctly. + /// @param whichSet Which set of placed objects to update. See the PlacedObjectSets enum. void UpdatePlacedObjects(int whichSet); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearPlacedObjectSet - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes all entries in a specific set of placed Objects. - // Arguments: Which set of placed objects to clear. See the PlacedObjectSets enum. - // Whether or not we have ownership of these items, and should delete them. - // Return value: How many things were removed in teh process of clearing that set. - + /// Removes all entries in a specific set of placed Objects. + /// @param whichSet Which set of placed objects to clear. See the PlacedObjectSets enum. + /// @param weHaveOwnership Whether or not we have ownership of these items, and should delete them. (default: true) + /// @return How many things were removed in teh process of clearing that set. int ClearPlacedObjectSet(int whichSet, bool weHaveOwnership = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetResidentBrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the resident brain Actor of a specific player from this scene, - // if there is any. OWNERSHIP IS NOT TRANSFERRED! - // Arguments: Which player to get the resident brain of. - // Return value: The SO containing the brain, or 0 if there aren't any of that player. - + /// Gets the resident brain Actor of a specific player from this scene, + /// if there is any. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player to get the resident brain of. + /// @return The SO containing the brain, or 0 if there aren't any of that player. SceneObject* GetResidentBrain(int player) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetResidentBrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the resident brain Actor of a specific player from this scene, - // if there is any. Ownership IS transferred! - // Arguments: Which player to set the resident brain of. - // The Actor to set as the resident brain of the specified player. - // Return value: None. - + /// Sets the resident brain Actor of a specific player from this scene, + /// if there is any. Ownership IS transferred! + /// @param player Which player to set the resident brain of. + /// @param pNewBrain The Actor to set as the resident brain of the specified player. void SetResidentBrain(int player, SceneObject* pNewBrain); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetResidentBrainCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of brains currently residing in this scene. - // Arguments: None. - // Return value: The number of resident brains who are installed here. - + /// Gets the number of brains currently residing in this scene. + /// @return The number of resident brains who are installed here. int GetResidentBrainCount() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds one or modifies an existing area of this Scene. - // Arguments: The area to add or modify of the same name in this Scene. Ownership is - // NOT transferred! - // Return value: Whether the specified area was previously defined in this scene. - + /// Adds one or modifies an existing area of this Scene. + /// @param newArea The area to add or modify of the same name in this Scene. Ownership is + /// NOT transferred! + /// @return Whether the specified area was previously defined in this scene. bool SetArea(Area& newArea); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks for the existence of a specific Area identified by a name. - // This won't throw any errors to the console if the Area isn't found. - // Arguments: The name of the Area to try to find in this Scene. - // Return value: Whether the specified area is defined in this Scene. - + /// Checks for the existence of a specific Area identified by a name. + /// This won't throw any errors to the console if the Area isn't found. + /// @param areaName The name of the Area to try to find in this Scene. + /// @return Whether the specified area is defined in this Scene. bool HasArea(std::string areaName); - /// /// Gets a specified Area identified by name. Ownership is NOT transferred! - /// - /// The name of the Area to try to get. - /// Whether the area is required, and should throw an error if not found. - /// A pointer to the Area asked for, or nullptr if no Area of that name was found. + /// @param areaName The name of the Area to try to get. + /// @param required Whether the area is required, and should throw an error if not found. + /// @return A pointer to the Area asked for, or nullptr if no Area of that name was found. Area* GetArea(const std::string_view& areaName, bool required); - /// /// Gets a specified Area identified by name. Ownership is NOT transferred! - /// - /// The name of the Area to try to get. - /// A pointer to the Area asked for, or nullptr if no Area of that name was found. + /// @param areaName The name of the Area to try to get. + /// @return A pointer to the Area asked for, or nullptr if no Area of that name was found. Area* GetArea(const std::string& areaName) { return GetArea(areaName, true); } - /// /// Gets a specified Area identified by name, showing a Lua warning if it's not found. Ownership is NOT transferred! /// Using this function will not add the area to the list of required areas which Scenario GUI uses to show compatible areas. - /// - /// The name of the Area to try to get. - /// A pointer to the Area asked for, or nullptr if no Area of that name was found. + /// @param areaName The name of the Area to try to get. + /// @return A pointer to the Area asked for, or nullptr if no Area of that name was found. Area* GetOptionalArea(const std::string& areaName) { return GetArea(areaName, false); } void AddNavigatableArea(const std::string& areaName) { @@ -897,362 +534,200 @@ namespace RTE { m_NavigatableAreasUpToDate = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a specific Area identified by a name. - // Arguments: The name of the Area to try to remove. - // Return value: Whether an Area of that name was found, and subsequently removed. - + /// Removes a specific Area identified by a name. + /// @param areaName The name of the Area to try to remove. + /// @return Whether an Area of that name was found, and subsequently removed. bool RemoveArea(std::string areaName); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WithinArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if a point is within a specific named Area of this Scene. If - // no Area of the name is found, this just returns false without error. - // Arguments: The name of the Area to try to check against. - // The point to see if it's within the specified Area. - // Return value: Whether any Area of that name was found, AND the point falls within it. - + /// Checks if a point is within a specific named Area of this Scene. If + /// no Area of the name is found, this just returns false without error. + /// @param areaName The name of the Area to try to check against. + /// @param point The point to see if it's within the specified Area. + /// @return Whether any Area of that name was found, AND the point falls within it. bool WithinArea(std::string areaName, const Vector& point) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGlobalAcc - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the global acceleration (in m/s^2) that is applied to all movable - // objects' velocities during every frame. Typically models gravity. - // Arguments: None. - // Return value: A Vector describing the global acceleration. - + /// Gets the global acceleration (in m/s^2) that is applied to all movable + /// objects' velocities during every frame. Typically models gravity. + /// @return A Vector describing the global acceleration. Vector GetGlobalAcc() const { return m_GlobalAcc; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetGlobalAcc - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the global acceleration (in m/s^2) that is applied to all movable - // objects' velocities during every frame. Typically models gravity. - // Arguments: A Vector describing the global acceleration. - // Return value: None. - + /// Sets the global acceleration (in m/s^2) that is applied to all movable + /// objects' velocities during every frame. Typically models gravity. + /// @param newValue A Vector describing the global acceleration. void SetGlobalAcc(Vector newValue) { m_GlobalAcc = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMetasceneParent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns parent scene name of this metascene. - // Arguments: None. - // Return value: Name of a parent scene. - + /// Returns parent scene name of this metascene. + /// @return Name of a parent scene. std::string GetMetasceneParent() const { return m_MetasceneParent; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLocation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the specified location of this Scene in the scene - // Arguments: A Vector with the desired location of this Scene in the scene. - // Return value: None. - + /// Sets the specified location of this Scene in the scene + /// @param newLocation A Vector with the desired location of this Scene in the scene. void SetLocation(const Vector& newLocation) { m_Location = newLocation; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMetagamePlayable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this can be played in the Metagame map at all. - // Arguments: Whether this is compatible with metagame play at all. - // Return value: None. - + /// Sets whether this can be played in the Metagame map at all. + /// @param isPlayable Whether this is compatible with metagame play at all. void SetMetagamePlayable(bool isPlayable) { m_MetagamePlayable = isPlayable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetRevealed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this should show up on the Metagame map yet. - // Arguments: Whether to reveal this on the metagame map or not. - // Return value: None. - + /// Sets whether this should show up on the Metagame map yet. + /// @param isRevealed Whether to reveal this on the metagame map or not. void SetRevealed(bool isRevealed) { m_Revealed = isRevealed; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTeamOwnership - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the team who owns this Scene in a Metagame - // Arguments: The team who should now own this Scene - // Return value: None. - + /// Sets the team who owns this Scene in a Metagame + /// @param newTeam The team who should now own this Scene void SetTeamOwnership(int newTeam); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBuildBudget - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets how much gold this Scene is budgeted to be built for this round. - // Arguments: The player whom is setting the budget. - // The budget in oz that this is allocated to have built for this round. - // Return value: None. - + /// Sets how much gold this Scene is budgeted to be built for this round. + /// @param player The player whom is setting the budget. + /// @param budget The budget in oz that this is allocated to have built for this round. void SetBuildBudget(int player, float budget) { m_BuildBudget[player] = budget; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetBuildBudgetRatio - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets how much of a player's budget this Scene is budgeted to be build - // for each turn. - // Arguments: The player whom is setting the budget ratio. - // The budget in normalized ratio that this is allocated of the total. - // Return value: None. - + /// Sets how much of a player's budget this Scene is budgeted to be build + /// for each turn. + /// @param player The player whom is setting the budget ratio. + /// @param budgetRatio The budget in normalized ratio that this is allocated of the total. void SetBuildBudgetRatio(int player, float budgetRatio) { m_BuildBudgetRatio[player] = budgetRatio; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalcBuildBudgetUse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Figure out exactly how much of the build budget would be used if - // as many blueprint objects as can be afforded and exists would be built. - // Arguments: The player for whom we are calculating this budget use. - // An optional int that will be filled with number of objects that can - // acutally be built. - // An optional int that will be filled with number of objects that can - // built out of the AI plan set, AFTER the blueprints are built. - // Return value: The amount of funds that would be applied to the building of objects. - + /// Figure out exactly how much of the build budget would be used if + /// as many blueprint objects as can be afforded and exists would be built. + /// @param player The player for whom we are calculating this budget use. + /// @param pAffordCount An optional int that will be filled with number of objects that can (default: 0) + /// acutally be built. + /// @param pAffordAIPlanCount An optional int that will be filled with number of objects that can (default: 0) + /// built out of the AI plan set, AFTER the blueprints are built. + /// @return The amount of funds that would be applied to the building of objects. float CalcBuildBudgetUse(int player, int* pAffordCount = 0, int* pAffordAIPlanCount = 0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyAIPlan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Puts the pre-built AI base plan into effect by transferring as many - // pieces as the current base budget allows from the AI plan to the actual - // blueprints to be built at this Scene. - // Arguments: The AI player whom is putting his plans into motion. - // An optional int that will be filled with number of objects that were - // acutally moved from the AI plan to the blueprints. - // Return value: The value of the AI plan objects that were put onto the blueprints. - + /// Puts the pre-built AI base plan into effect by transferring as many + /// pieces as the current base budget allows from the AI plan to the actual + /// blueprints to be built at this Scene. + /// @param player The AI player whom is putting his plans into motion. + /// @param pObjectsApplied An optional int that will be filled with number of objects that were (default: 0) + /// acutally moved from the AI plan to the blueprints. + /// @return The value of the AI plan objects that were put onto the blueprints. float ApplyAIPlan(int player, int* pObjectsApplied = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyBuildBudget - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Actually builds as many objects in the specific player's Blueprint - // list as can be afforded by his build budget. The budget is deducted - // accordingly. - // Arguments: The player whom is using his budget. - // An optional int that will be filled with number of objects that were - // acutally built. - // Return value: The amount of funds that were applied to the building of objects. - + /// Actually builds as many objects in the specific player's Blueprint + /// list as can be afforded by his build budget. The budget is deducted + /// accordingly. + /// @param player The player whom is using his budget. + /// @param pObjectsBuilt An optional int that will be filled with number of objects that were (default: 0) + /// acutally built. + /// @return The amount of funds that were applied to the building of objects. float ApplyBuildBudget(int player, int* pObjectsBuilt = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveAllPlacedActors - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Remove all actors that are in the placed set of objects to load for - // this scene. All except for an optionally specified team, that is. - // Arguments: Remove all actors but of this team. - // Return value: How many actors were actually removed. - + /// Remove all actors that are in the placed set of objects to load for + /// this scene. All except for an optionally specified team, that is. + /// @param exceptTeam Remove all actors but of this team. (default: -1) + /// @return How many actors were actually removed. int RemoveAllPlacedActors(int exceptTeam = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOwnerOfAllDoors - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the ownership of all doors placed in this scene to a specific team - // Arguments: The team to change the ownership to - // The player which placed these doors. - // Return value: How many doors were actually affected. - + /// Sets the ownership of all doors placed in this scene to a specific team + /// @param team The team to change the ownership to + /// @param player The player which placed these doors. + /// @return How many doors were actually affected. int SetOwnerOfAllDoors(int team, int player); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsScanScheduled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a specific team has scheduled an orbital Scan of this. - // Arguments: The team to check for. - // Return value: Whether the scan has been scheduled and paid for. - + /// Tells whether a specific team has scheduled an orbital Scan of this. + /// @param team The team to check for. + /// @return Whether the scan has been scheduled and paid for. bool IsScanScheduled(int team) const { return m_ScanScheduled[team]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetScheduledScan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this to be orbitally scanned by a specific team on next load. - // Arguments: The team to schedule the scan for. - // Whether to actually schedule the scan or clear it. - // Return value: None. - + /// Sets this to be orbitally scanned by a specific team on next load. + /// @param team The team to schedule the scan for. + /// @param scan Whether to actually schedule the scan or clear it. (default: true) void SetScheduledScan(int team, bool scan = true) { m_ScanScheduled[team] = scan; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetPathFinding - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Recalculates all of the pathfinding data. This is very expensive, so - // do very rarely! - // Arguments: None. - // Return value: None. - + /// Recalculates all of the pathfinding data. This is very expensive, so + /// do very rarely! void ResetPathFinding(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BlockUntilAllPathingRequestsComplete - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Blocks this thread until all pathing requests are completed. - // Arguments: None. - // Return value: None. - + /// Blocks this thread until all pathing requests are completed. void BlockUntilAllPathingRequestsComplete(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePathFinding - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Recalculates only the areas of the pathfinding data that have been - // marked as outdated. - // Arguments: None. - // Return value: None. - + /// Recalculates only the areas of the pathfinding data that have been + /// marked as outdated. void UpdatePathFinding(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PathFindingUpdated - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether the pathfinding data has been updated in the last frame. - // Arguments: None. - // Return value: Whether the pathfinding data was recalculated fully or partially. - + /// Tells whether the pathfinding data has been updated in the last frame. + /// @return Whether the pathfinding data was recalculated fully or partially. bool PathFindingUpdated() { return m_PathfindingUpdated; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculatePath - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates and returns the least difficult path between two points on - // the current scene. Takes both distance and materials into account. - // When pathing using the NoTeam pathFinder, no doors are considered passable. - // Arguments: Start and end positions on the scene to find the path between. - // A list which will be filled out with waypoints between the start and end. - // The maximum material strength any actor traveling along the path can dig through. - // The team we're pathing for (doors for this team will be considered passable) - // Return value: The total minimum difficulty cost calculated between the two points on - // the scene. - + /// Calculates and returns the least difficult path between two points on + /// the current scene. Takes both distance and materials into account. + /// When pathing using the NoTeam pathFinder, no doors are considered passable. + /// @param start Start and end positions on the scene to find the path between. + /// @param end A list which will be filled out with waypoints between the start and end. + /// @param pathResult The maximum material strength any actor traveling along the path can dig through. + /// @param digStrength The team we're pathing for (doors for this team will be considered passable) (default: c_PathFindingDefaultDigStrength) + /// @return The total minimum difficulty cost calculated between the two points on + /// the scene. float CalculatePath(const Vector& start, const Vector& end, std::list& pathResult, float digStrength = c_PathFindingDefaultDigStrength, Activity::Teams team = Activity::Teams::NoTeam); - /// /// Asynchronously calculates the least difficult path between two points on the current Scene. Takes both distance and materials into account. /// When pathing using the NoTeam pathFinder, no doors are considered passable. - /// - /// Start position of the pathfinding request. - /// End position of the pathfinding request. - /// The maximum material strength any actor traveling along the path can dig through. - /// The team we're pathing for (doors for this team will be considered passable) - /// A shared pointer to the volatile PathRequest to be used to track whehter the asynchrnous path calculation has been completed, and check its results. + /// @param start Start position of the pathfinding request. + /// @param end End position of the pathfinding request. + /// @param digStrength The maximum material strength any actor traveling along the path can dig through. + /// @param team The team we're pathing for (doors for this team will be considered passable) + /// @return A shared pointer to the volatile PathRequest to be used to track whehter the asynchrnous path calculation has been completed, and check its results. std::shared_ptr CalculatePathAsync(const Vector& start, const Vector& end, float digStrength = c_PathFindingDefaultDigStrength, Activity::Teams team = Activity::Teams::NoTeam, PathCompleteCallback callback = nullptr); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetScenePathSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how many waypoints there are in the ScenePath currently - // Arguments: None. - // Return value: The number of waypoints in the ScenePath. - + /// Gets how many waypoints there are in the ScenePath currently + /// @return The number of waypoints in the ScenePath. int GetScenePathSize() const; std::list& GetScenePath(); - /// /// Returns whether two position represent the same path nodes. - /// - /// First coordinates to compare. - /// Second coordinates to compare. - /// Whether both coordinates represent the same path node. + /// @param pos1 First coordinates to compare. + /// @param pos2 Second coordinates to compare. + /// @return Whether both coordinates represent the same path node. bool PositionsAreTheSamePathNode(const Vector& pos1, const Vector& pos2) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Lock - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Locks all dynamic internal scene bitmaps so that manipulaitons of the - // scene's color and matter representations can take place. - // Doing it in a separate method like this is more efficient because - // many bitmap manipulaitons can be performed between a lock and unlock. - // UnlockScene() should always be called after accesses are completed. - // Arguments: None. - // Return value: None. - + /// Locks all dynamic internal scene bitmaps so that manipulaitons of the + /// scene's color and matter representations can take place. + /// Doing it in a separate method like this is more efficient because + /// many bitmap manipulaitons can be performed between a lock and unlock. + /// UnlockScene() should always be called after accesses are completed. void Lock(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Unlock - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Unlocks the scene's bitmaps and prevents access to display memory. - // Doing it in a separate method like this is more efficient because - // many bitmap accesses can be performed between a lock and an unlock. - // UnlockScene() should only be called after LockScene(). - // Arguments: None. - // Return value: None. - + /// Unlocks the scene's bitmaps and prevents access to display memory. + /// Doing it in a separate method like this is more efficient because + /// many bitmap accesses can be performed between a lock and an unlock. + /// UnlockScene() should only be called after LockScene(). void Unlock(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsLocked - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the entire scene is currently locked or not. - // Arguments: None. - // Return value: Whether the entire scene is currently locked or not. - + /// Indicates whether the entire scene is currently locked or not. + /// @return Whether the entire scene is currently locked or not. bool IsLocked() const { return m_Locked; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Scene. Supposed to be done every frame - // before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this Scene. Supposed to be done every frame + /// before drawing. void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsMetagameInternal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Whether this scene is a temprorary metagame scene and should - // not be used anywhere except in metagame. - // Arguments: None. - // Return value: Whether scene belongs to metagame or not. - + /// Whether this scene is a temprorary metagame scene and should + /// not be used anywhere except in metagame. + /// @return Whether scene belongs to metagame or not. bool IsMetagameInternal() const { return m_IsMetagameInternal; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMetagameInternal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this scene is a temprorary metagame scene and should - // not be used anywhere except in metagame. - // Arguments: New value. - // Return value: None. - + /// Sets whether this scene is a temprorary metagame scene and should + /// not be used anywhere except in metagame. + /// @param newValue New value. void SetMetagameInternal(bool newValue) { m_IsMetagameInternal = newValue; } - /// /// Gets whether this Scene is a saved game Scene copy and should not be used anywhere except for game saving and loading. - /// - /// Whether this Scene is a saved game Scene copy. + /// @return Whether this Scene is a saved game Scene copy. bool IsSavedGameInternal() const { return m_IsSavedGameInternal; } - /// /// Sets whether this Scene is a saved game Scene copy and should not be used anywhere except for game saving and loading. - /// - /// Whether this Scene is a saved game Scene copy. + /// @param newValue Whether this Scene is a saved game Scene copy. void SetSavedGameInternal(bool newValue) { m_IsSavedGameInternal = newValue; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPreviewBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns preview bitmap pointer for this scene. - // Arguments: None. - // Return value: Pointer to preview bitmap. - + /// Returns preview bitmap pointer for this scene. + /// @return Pointer to preview bitmap. BITMAP* GetPreviewBitmap() const { return m_pPreviewBitmap; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Member variables static Entity::ClassInfo m_sClass; @@ -1336,34 +811,22 @@ namespace RTE { std::list m_Deployments; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - /// /// Gets the pathfinder for a given team. - /// - /// The team to get the pathfinder for. NoTeam is valid, and will give a shared pathfinder. - /// A pointer to the pathfinder for the given team. + /// @param team The team to get the pathfinder for. NoTeam is valid, and will give a shared pathfinder. + /// @return A pointer to the pathfinder for the given team. std::unique_ptr& GetPathFinder(Activity::Teams team); - /// /// Serializes the SceneObject via the Writer. Necessary because full serialization doesn't know how to deal with duplicate properties. - /// - /// The Writer being used for serialization. - /// The SceneObject to save. - /// Convenience flag for whether or not this SceneObject is a child Attachable, and certain properties shouldn't be saved. - /// Whether or not to save most data. Turned off for stuff like SceneEditor saves. + /// @param writer The Writer being used for serialization. + /// @param sceneObjectToSave The SceneObject to save. + /// @param isChildAttachable Convenience flag for whether or not this SceneObject is a child Attachable, and certain properties shouldn't be saved. + /// @param saveFullData Whether or not to save most data. Turned off for stuff like SceneEditor saves. void SaveSceneObject(Writer& writer, const SceneObject* sceneObjectToSave, bool isChildAttachable, bool saveFullData) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this Scene, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this Scene, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/SceneLayer.cpp b/Source/Entities/SceneLayer.cpp index 1e14f1e133..868c2e210e 100644 --- a/Source/Entities/SceneLayer.cpp +++ b/Source/Entities/SceneLayer.cpp @@ -13,8 +13,6 @@ namespace RTE { ConcreteClassInfo(SceneLayerTracked, Entity, 0); ConcreteClassInfo(SceneLayer, Entity, 0); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::Clear() { m_BitmapFile.Reset(); @@ -34,8 +32,6 @@ namespace RTE { m_ScaledDimensions.SetXY(1.0F, 1.0F); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template int SceneLayerImpl::Create(const ContentFile& bitmapFile, bool drawMasked, const Vector& offset, bool wrapX, bool wrapY, const Vector& scrollInfo) { m_BitmapFile = bitmapFile; @@ -47,8 +43,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template int SceneLayerImpl::Create(BITMAP* bitmap, bool drawMasked, const Vector& offset, bool wrapX, bool wrapY, const Vector& scrollInfo) { m_MainBitmap = bitmap; @@ -70,8 +64,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template int SceneLayerImpl::Create(const SceneLayerImpl& reference) { Entity::Create(reference); @@ -108,8 +100,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template int SceneLayerImpl::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -121,8 +111,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template int SceneLayerImpl::Save(Writer& writer) const { Entity::Save(writer); @@ -134,8 +122,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::Destroy(bool notInherited) { if (m_MainBitmapOwned) { @@ -150,8 +136,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::InitScrollRatios(bool initForNetworkPlayer, int player) { float mainBitmapWidth = static_cast(m_MainBitmap->w); @@ -188,8 +172,6 @@ namespace RTE { m_ScaledDimensions.SetXY(mainBitmapWidth * m_ScaleFactor.GetX(), mainBitmapHeight * m_ScaleFactor.GetY()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template int SceneLayerImpl::LoadData() { // Load from disk and take ownership. Don't cache because the bitmap will be modified. @@ -203,8 +185,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template int SceneLayerImpl::SaveData(const std::string& bitmapPath, bool doAsyncSaves) { if (bitmapPath.empty()) { @@ -235,8 +215,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template std::unique_ptr SceneLayerImpl::CopyBitmap() { BITMAP* outputBitmap = create_bitmap_ex(bitmap_color_depth(m_MainBitmap), m_MainBitmap->w, m_MainBitmap->h); @@ -247,8 +225,6 @@ namespace RTE { return std::unique_ptr(outputBitmap); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template int SceneLayerImpl::ClearData() { if (m_MainBitmap && m_MainBitmapOwned) { @@ -266,8 +242,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::SetScaleFactor(const Vector& newScale) { m_ScaleFactor = newScale; @@ -276,16 +250,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template int SceneLayerImpl::GetPixel(int pixelX, int pixelY) const { WrapPosition(pixelX, pixelY); return (pixelX < 0 || pixelX >= m_MainBitmap->w || pixelY < 0 || pixelY >= m_MainBitmap->h) ? MaterialColorKeys::g_MaterialAir : _getpixel(m_MainBitmap, pixelX, pixelY); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::SetPixel(int pixelX, int pixelY, int materialID) { RTEAssert(m_MainBitmapOwned, "Trying to set a pixel of a SceneLayer's bitmap which isn't owned!"); @@ -300,15 +270,11 @@ namespace RTE { RegisterDrawing(pixelX, pixelY, pixelX, pixelY); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template bool SceneLayerImpl::IsWithinBounds(const int pixelX, const int pixelY, const int margin) const { return (m_WrapX || (pixelX >= -margin && pixelX < m_MainBitmap->w + margin)) && (m_WrapY || (pixelY >= -margin && pixelY < m_MainBitmap->h + margin)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::ClearBitmap(ColorKeys clearTo) { RTEAssert(m_MainBitmapOwned, "Bitmap not owned! We shouldn't be clearing this!"); @@ -335,8 +301,6 @@ namespace RTE { m_Drawings.clear(); // This was copied into the new thread, so can be safely deleted. } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template bool SceneLayerImpl::WrapPosition(int& posX, int& posY) const { int oldX = posX; @@ -361,8 +325,6 @@ namespace RTE { return oldX != posX || oldY != posY; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template bool SceneLayerImpl::ForceBounds(int& posX, int& posY) const { bool wrapped = false; @@ -408,8 +370,6 @@ namespace RTE { return wrapped; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template bool SceneLayerImpl::ForceBoundsOrWrapPosition(Vector& pos, bool forceBounds) const { int posX = pos.GetFloorIntX(); @@ -420,8 +380,6 @@ namespace RTE { return wrapped; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::RegisterDrawing(int left, int top, int right, int bottom) { if constexpr (TRACK_DRAWINGS) { @@ -429,8 +387,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::RegisterDrawing(const Vector& center, float radius) { if (radius != 0.0F) { @@ -438,8 +394,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment) { RTEAssert(m_MainBitmap, "Data of this SceneLayerImpl has not been loaded before trying to draw!"); @@ -471,8 +425,6 @@ namespace RTE { set_clip_rect(targetBitmap, 0, 0, targetBitmap->w - 1, targetBitmap->h - 1); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::DrawWrapped(BITMAP* targetBitmap, const Box& targetBox, bool drawScaled) const { if (!drawScaled) { @@ -510,8 +462,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::DrawTiled(BITMAP* targetBitmap, const Box& targetBox, bool drawScaled) const { int bitmapWidth = m_ScaledDimensions.GetFloorIntX(); @@ -550,8 +500,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template void SceneLayerImpl::ClearDrawings(BITMAP* bitmap, const std::vector& drawings, ColorKeys clearTo) const { if constexpr (TRACK_DRAWINGS) { @@ -596,8 +544,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Force instantiation template class SceneLayerImpl; template class SceneLayerImpl; diff --git a/Source/Entities/SceneLayer.h b/Source/Entities/SceneLayer.h index ba5952097b..891a7eefbe 100644 --- a/Source/Entities/SceneLayer.h +++ b/Source/Entities/SceneLayer.h @@ -7,9 +7,7 @@ namespace RTE { - /// /// A scrolling layer of the Scene. - /// template class SceneLayerImpl : public Entity { friend class NetworkServer; @@ -19,265 +17,195 @@ namespace RTE { SerializableOverrideMethods; #pragma region Creation - /// /// Constructor method used to instantiate a SceneLayerImpl object in system memory. Create() should be called before using the object. - /// SceneLayerImpl() { Clear(); } - /// /// Makes the SceneLayer object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override { return Entity::Create(); } - /// /// Makes the SceneLayer object ready for use. - /// - /// The ContentFile to load as this SceneLayer's graphical representation. - /// Whether to draw masked (transparent) or not. - /// The initial scroll offset. - /// Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the X axis. - /// Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the Y axis. - /// + /// @param bitmapFile The ContentFile to load as this SceneLayer's graphical representation. + /// @param drawMasked Whether to draw masked (transparent) or not. + /// @param offset The initial scroll offset. + /// @param wrapX Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the X axis. + /// @param wrapY Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the Y axis. + /// @param scrollInfo /// A vector whose components define two different things, depending on wrap arguments. /// If a wrap argument is set to false, the corresponding component here will be interpreted as the width (X) or height (Y) (in pixels) of the total bitmap area that this layer is allowed to scroll across before stopping at an edge. /// If wrapping is set to true, the value in scrollInfo is simply the ratio of offset at which any scroll operations will be done in. /// A special command is if wrap is false and the corresponding component is -1.0, that signals that the own width or height should be used as scrollInfo input. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const ContentFile& bitmapFile, bool drawMasked, const Vector& offset, bool wrapX, bool wrapY, const Vector& scrollInfo); - /// /// Makes the SceneLayer object ready for use. - /// - /// The BITMAP to use for this SceneLayer. Ownership IS transferred! - /// Whether to draw masked (transparent) or not. - /// The initial scroll offset. - /// Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the X axis. - /// Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the Y axis. - /// + /// @param bitmap The BITMAP to use for this SceneLayer. Ownership IS transferred! + /// @param drawMasked Whether to draw masked (transparent) or not. + /// @param offset The initial scroll offset. + /// @param wrapX Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the X axis. + /// @param wrapY Whether the layer should wrap around or stop when scrolling beyond its bitmap's boundaries on the Y axis. + /// @param scrollInfo /// A vector whose components define two different things, depending on wrap arguments. /// If a wrap argument is set to false, the corresponding component here will be interpreted as the width (X) or height (Y) (in pixels) of the total bitmap area that this layer is allowed to scroll across before stopping at an edge. /// If wrapping is set to true, the value in scrollInfo is simply the ratio of offset at which any scroll operations will be done in. /// A special command is if wrap is false and the corresponding component is -1.0, that signals that the own width or height should be used as scrollInfo input. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(BITMAP* bitmap, bool drawMasked, const Vector& offset, bool wrapX, bool wrapY, const Vector& scrollInfo); - /// /// Creates a SceneLayer to be identical to another, by deep copy. - /// - /// A reference to the SceneLayer to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the SceneLayer to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const SceneLayerImpl& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a SceneLayer object before deletion from system memory. - /// ~SceneLayerImpl() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the SceneLayer object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override; #pragma endregion #pragma region Data Handling - /// /// Whether this SceneLayer's bitmap data is loaded from a file or was generated at runtime. - /// - /// Whether this SceneLayer's bitmap data was loaded from a file or not. + /// @return Whether this SceneLayer's bitmap data was loaded from a file or not. virtual bool IsLoadedFromDisk() const { return !m_BitmapFile.GetDataPath().empty(); } - /// /// Loads previously specified/created data into memory. Has to be done before using this SceneLayer if the bitmap was not generated at runtime. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int LoadData(); - /// /// Saves data currently in memory to disk. - /// - /// The filepath to the where to save the bitmap data. - /// Whether or not to save asynchronously. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param bitmapPath The filepath to the where to save the bitmap data. + /// @param doAsyncSaves Whether or not to save asynchronously. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int SaveData(const std::string& bitmapPath, bool doAsyncSaves = true); - /// /// Clears out any previously loaded bitmap data from memory. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int ClearData(); - /// /// Copies the bitmap. - /// - /// The copied bitmap. + /// @return The copied bitmap. std::unique_ptr CopyBitmap(); #pragma endregion #pragma region Getters and Setters - /// /// Gets the scroll offset of this SceneLayer. - /// - /// A Vector with the scroll offset. + /// @return A Vector with the scroll offset. Vector GetOffset() const { return m_Offset; } - /// /// Sets the scroll offset of this SceneLayer. Observe that this offset will be modified by the scroll ratio before applied. - /// - /// The new offset Vector. + /// @param newOffset The new offset Vector. void SetOffset(const Vector& newOffset) { m_Offset = newOffset; } - /// /// Gets the scroll ratio that modifies the offset. - /// - /// A copy of the ratio. + /// @return A copy of the ratio. Vector GetScrollRatio() const { return m_ScrollRatio; } - /// /// Sets the scroll ratio of this SceneLayer. This modifies the offset before any actual scrolling occurs. - /// - /// The new scroll ratio vector. + /// @param newRatio The new scroll ratio vector. void SetScrollRatio(const Vector& newRatio) { m_ScrollRatio = newRatio; } - /// /// Gets the scale factor that this is drawn in. - /// - /// The scale factor of this to the target it is drawn to. (2x if this is half the res, etc.) + /// @return The scale factor of this to the target it is drawn to. (2x if this is half the res, etc.) Vector GetScaleFactor() const { return m_ScaleFactor; } - /// /// Sets the scale that this should be drawn at when using DrawScaled. - /// - /// The new scale factor vector. + /// @param newScale The new scale factor vector. void SetScaleFactor(const Vector& newScale); - /// /// Indicates whether the layer is set to wrap around the X axis when scrolled out of bounds. - /// - /// Whether this SceneLayer wraps on the X axis or not. + /// @return Whether this SceneLayer wraps on the X axis or not. bool WrapsX() const { return m_WrapX; } - /// /// Indicates whether the layer is set to wrap around the Y axis when scrolled out of bounds. - /// - /// Whether this SceneLayer wraps on the Y axis or not. + /// @return Whether this SceneLayer wraps on the Y axis or not. bool WrapsY() const { return m_WrapY; } - /// /// Gets a specific pixel from the bitmap of this SceneLayer. LockBitmaps() must be called before using this method. - /// - /// The X coordinate of the pixel to get. - /// The Y coordinate of the pixel to get. - /// An int specifying the requested pixel's color index. + /// @param pixelX The X coordinate of the pixel to get. + /// @param pixelY The Y coordinate of the pixel to get. + /// @return An int specifying the requested pixel's color index. int GetPixel(int pixelX, int pixelY) const; - /// /// Sets a specific pixel on the bitmap of this SceneLayer to a specific value. LockBitmaps() must be called before using this method. - /// - /// The X coordinate of the pixel to set. - /// The Y coordinate of the pixel to set. - /// The color index to set the pixel to. + /// @param pixelX The X coordinate of the pixel to set. + /// @param pixelY The Y coordinate of the pixel to set. + /// @param materialID The color index to set the pixel to. void SetPixel(int pixelX, int pixelY, int materialID); - /// /// Returns whether the integer coordinates passed in are within the bounds of this SceneLayer. - /// - /// The X coordinates of the pixel. - /// The Y coordinates of the pixel. - /// - /// Whether within bounds or not. + /// @param pixelX The X coordinates of the pixel. + /// @param pixelY The Y coordinates of the pixel. + /// @param margin + /// @return Whether within bounds or not. bool IsWithinBounds(int pixelX, int pixelY, int margin = 0) const; #pragma endregion #pragma region Concrete Methods - /// /// Lock the internal bitmap so it can be accessed by GetPixel() etc. UnlockBitmaps() should always be called after accesses are completed. /// Doing it in a separate method like this is more efficient because many bitmap accesses can be performed between a lock and unlock. - /// void LockBitmaps() { /*acquire_bitmap(m_MainBitmap);*/ } - /// /// Unlocks the internal bitmaps and prevents access to display memory. UnlockBitmaps() should only be called after LockBitmaps(). /// Doing it in a separate method like this is more efficient because many bitmap accesses can be performed between a lock and an unlock. - /// void UnlockBitmaps() { /*release_bitmap(m_MainBitmap);*/ } - /// /// Clears our BITMAP. - /// - /// What color to clear the bitmap to. + /// @param clearTo What color to clear the bitmap to. void ClearBitmap(ColorKeys clearTo); - /// /// Wraps the given position coordinate if it is out of bounds of this SceneLayer and wrapping is enabled on the appropriate axes. /// Does not force the position coordinate within bounds if wrapping is not enabled. - /// - /// The X coordinates of the position to wrap. - /// The Y coordinates of the position to wrap. - /// Whether wrapping was performed or not. + /// @param posX The X coordinates of the position to wrap. + /// @param posY The Y coordinates of the position to wrap. + /// @return Whether wrapping was performed or not. bool WrapPosition(int& posX, int& posY) const; - /// /// Wraps the given position Vector if it is out of bounds of this SceneLayer and wrapping is enabled on the appropriate axes. /// Does not force the position Vector within bounds if wrapping is not enabled. - /// - /// The vector coordinates of the position to wrap. - /// Whether wrapping was performed or not. + /// @param pos The vector coordinates of the position to wrap. + /// @return Whether wrapping was performed or not. bool WrapPosition(Vector& pos) const { return ForceBoundsOrWrapPosition(pos, false); } - /// /// Wraps or bounds a position coordinate if it is out of bounds of the SceneLayer, depending on the wrap settings of this SceneLayer. - /// - /// The X coordinates of the position to wrap. - /// The Y coordinates of the position to wrap. - /// Whether wrapping was performed or not. Does not report on bounding. + /// @param posX The X coordinates of the position to wrap. + /// @param posY The Y coordinates of the position to wrap. + /// @return Whether wrapping was performed or not. Does not report on bounding. bool ForceBounds(int& posX, int& posY) const; - /// /// Wraps or bounds a position coordinate if it is out of bounds of the SceneLayer, depending on the wrap settings of this SceneLayer. - /// - /// The Vector coordinates of the position to wrap. - /// Whether wrapping was performed or not. Does not report on bounding. + /// @param pos The Vector coordinates of the position to wrap. + /// @return Whether wrapping was performed or not. Does not report on bounding. bool ForceBounds(Vector& pos) const { return ForceBoundsOrWrapPosition(pos, true); } #pragma endregion #pragma region Drawing Tracking - /// /// Registers an area of the SceneLayer to be drawn upon. These areas will be cleared when ClearBitmap is called. - /// - /// The position of the left side of the area to be drawn upon. - /// The position of the top of the area to be drawn upon. - /// The position of the right side of the area to be drawn upon. - /// + /// @param left The position of the left side of the area to be drawn upon. + /// @param top The position of the top of the area to be drawn upon. + /// @param right The position of the right side of the area to be drawn upon. + /// @param bottom"The position of the bottom of the area to be drawn upon void RegisterDrawing(int left, int top, int right, int bottom); - /// /// Registers an area of the SceneLayer to be drawn upon. These areas will be cleared when ClearBitmap is called. - /// - /// The position of the center of the area to be drawn upon. - /// The radius of the area to be drawn upon. + /// @param center The position of the center of the area to be drawn upon. + /// @param radius The radius of the area to be drawn upon. void RegisterDrawing(const Vector& center, float radius); #pragma endregion #pragma region Virtual Methods - /// /// Updates the state of this SceneLayer. - /// virtual void Update() {} - /// /// Draws this SceneLayer's current scrolled position to a bitmap. - /// - /// The bitmap to draw to. - /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. - /// Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. + /// @param targetBitmap The bitmap to draw to. + /// @param targetBox The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// @param offsetNeedsScrollRatioAdjustment Whether the offset of this SceneLayer or the passed in offset override need to be adjusted to scroll ratio. virtual void Draw(BITMAP* targetBitmap, Box& targetBox, bool offsetNeedsScrollRatioAdjustment = false); #pragma endregion @@ -306,49 +234,37 @@ namespace RTE { Vector m_ScaleFactor; //!< The scaling factor of this SceneLayer. Used for scaled drawing and adjusting scrolling ratios. Vector m_ScaledDimensions; //!< The dimensions of this SceneLayer adjusted to the scaling factor. - /// /// Initialize the scroll ratios from the scroll info. Must be done after the bitmap has been created. - /// - /// - /// + /// @param initForNetworkPlayer + /// @param player void InitScrollRatios(bool initForNetworkPlayer = false, int player = Players::NoPlayer); - /// /// Wraps or bounds a position coordinate if it is out of bounds of the SceneLayer, depending on the wrap settings of this SceneLayer. - /// - /// The Vector coordinates of the position to wrap. - /// Whether to attempt bounding or wrapping, or just wrapping. - /// Whether wrapping was performed or not. Does not report on bounding. + /// @param pos The Vector coordinates of the position to wrap. + /// @param forceBounds Whether to attempt bounding or wrapping, or just wrapping. + /// @return Whether wrapping was performed or not. Does not report on bounding. bool ForceBoundsOrWrapPosition(Vector& pos, bool forceBounds) const; #pragma region Draw Breakdown - /// /// Performs wrapped drawing of this SceneLayer's bitmap to the screen in cases where it is both wider and taller than the target bitmap. - /// - /// The bitmap to draw to. - /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. - /// Whether to use scaled drawing routines or not. + /// @param targetBitmap The bitmap to draw to. + /// @param targetBox The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// @param drawScaled Whether to use scaled drawing routines or not. void DrawWrapped(BITMAP* targetBitmap, const Box& targetBox, bool drawScaled) const; - /// /// Performs tiled drawing of this SceneLayer's bitmap to the screen in cases where the target bitmap is larger in some dimension. - /// - /// The bitmap to draw to. - /// The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. - /// Whether to use scaled drawing routines or not. + /// @param targetBitmap The bitmap to draw to. + /// @param targetBox The box on the target bitmap to limit drawing to, with the corner of box being where the scroll position lines up. + /// @param drawScaled Whether to use scaled drawing routines or not. void DrawTiled(BITMAP* targetBitmap, const Box& targetBox, bool drawScaled) const; #pragma endregion private: - /// /// Clears any tracked and drawn-to areas. - /// - /// Color to clear to. + /// @param clearTo Color to clear to. void ClearDrawings(BITMAP* bitmap, const std::vector& drawings, ColorKeys clearTo) const; - /// /// Clears all the member variables of this SceneLayer, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. @@ -356,27 +272,21 @@ namespace RTE { void operator=(const SceneLayerImpl& rhs) = delete; }; - /// /// If we track drawings, then disallow getting non-const access to the underlying bitmap - we must draw through special functions on SceneLayer that'll track the drawings. - /// class SceneLayerTracked : public SceneLayerImpl { public: EntityAllocation(SceneLayerTracked); ClassInfoGetters; - /// /// Constructor method used to instantiate a SceneLayerTracked object in system memory. Create() should be called before using the object. - /// SceneLayerTracked() : SceneLayerImpl() {} // TODO: We shouldn't let external users access a non-const version of our bitmap. We should do all drawing to it internally, and track registering our MOID drawings internally too. // However, in the interest of time (and my own sanity), given that the old code already does this, we're not doing that yet. - /// /// Gets the BITMAP that this SceneLayer uses. - /// - /// A pointer to the BITMAP of this SceneLayer. Ownership is NOT transferred! + /// @return A pointer to the BITMAP of this SceneLayer. Ownership is NOT transferred! BITMAP* GetBitmap() const { return m_MainBitmap; } protected: @@ -389,16 +299,12 @@ namespace RTE { EntityAllocation(SceneLayer); ClassInfoGetters; - /// /// Constructor method used to instantiate a SceneLayer object in system memory. Create() should be called before using the object. - /// SceneLayer() : SceneLayerImpl() {} - /// /// Gets the BITMAP that this SceneLayer uses. - /// - /// A pointer to the BITMAP of this SceneLayer. Ownership is NOT transferred! + /// @return A pointer to the BITMAP of this SceneLayer. Ownership is NOT transferred! BITMAP* GetBitmap() const { return m_MainBitmap; } protected: diff --git a/Source/Entities/SceneObject.cpp b/Source/Entities/SceneObject.cpp index dbcb50ed8e..10a34b66cb 100644 --- a/Source/Entities/SceneObject.cpp +++ b/Source/Entities/SceneObject.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneObject.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the SceneObject class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "SceneObject.h" #include "PresetMan.h" #include "Matrix.h" @@ -21,12 +9,6 @@ namespace RTE { AbstractClassInfo(SceneObject, Entity); const std::string SceneObject::SOPlacer::c_ClassName = "SOPlacer"; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this SOPlacer, effectively - // resetting the members of this abstraction level only. - void SceneObject::SOPlacer::Clear() { m_pObjectReference = 0; m_Offset.Reset(); @@ -50,11 +32,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a SOPlacer to be identical to another, by deep copy. - int SceneObject::SOPlacer::Create(const SOPlacer& reference) { m_pObjectReference = reference.m_pObjectReference; m_Offset = reference.m_Offset; @@ -65,14 +42,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int SceneObject::SOPlacer::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -94,12 +63,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this SOPlacer with a Writer for - // later recreation with Create(Reader &reader); - int SceneObject::SOPlacer::Save(Writer& writer) const { Serializable::Save(writer); @@ -126,12 +89,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlacedCopy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes a copy of the preset instance, and applies the placement - // properties of this to it, finally returning it WITH OWNERSHIP. - SceneObject* SceneObject::SOPlacer::GetPlacedCopy(const SceneObject* pParent) const { RTEAssert(m_pObjectReference, "No Object reference to make copy from!"); @@ -169,12 +126,6 @@ namespace RTE { return pCopy; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this SceneObject, effectively - // resetting the members of this abstraction level only. - void SceneObject::Clear() { m_Pos.Reset(); m_OzValue = 0; @@ -211,11 +162,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates an SceneObject object to be identical to another, by deep copy. - int SceneObject::Create(const SceneObject& reference) { Entity::Create(reference); @@ -229,14 +175,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a Reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the Reader's position is untouched. - int SceneObject::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -257,12 +195,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this SceneObject to an output stream for - // later recreation with Create(istream &stream); - int SceneObject::Save(Writer& writer) const { Entity::Save(writer); // TODO: Make proper save system that knows not to save redundant data! @@ -283,11 +215,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneObject object. - void SceneObject::Destroy(bool notInherited) { if (!notInherited) @@ -295,22 +222,11 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the cost to purchase this item, in oz's of gold. - float SceneObject::GetGoldValue(int nativeModule, float foreignMult, float nativeMult) const { // Multiply the value of this according to whether its Tech is native or not to the specified DataModule return m_OzValue * ((m_DefinedInModule > 0 && nativeModule > 0 && m_DefinedInModule != nativeModule) ? foreignMult : nativeMult); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldValueString - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a descriptive string describing the cost to purchase this item, - // in oz's of gold. - std::string SceneObject::GetGoldValueString(int nativeModule, float foreignMult, float nativeMult) const { float subjValue = GetGoldValue(nativeModule, foreignMult, nativeMult); @@ -324,10 +240,6 @@ namespace RTE { return returnString; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawTeamMark - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws team sign this terrain object belongs to. void SceneObject::DrawTeamMark(BITMAP* pTargetBitmap, const Vector& targetPos) const { // Only do HUD if on a team if (m_Team < 0) diff --git a/Source/Entities/SceneObject.h b/Source/Entities/SceneObject.h index 1ee5a2ae86..5503e53b0e 100644 --- a/Source/Entities/SceneObject.h +++ b/Source/Entities/SceneObject.h @@ -1,18 +1,11 @@ #ifndef _RTESCENEOBJECT_ #define _RTESCENEOBJECT_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneObject.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the SceneObject class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files, forward declarations, namespace stuff - +/// Header file for the SceneObject class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files, forward declarations, namespace stuff #include "Entity.h" #include "Matrix.h" @@ -20,26 +13,16 @@ struct BITMAP; namespace RTE { - ////////////////////////////////////////////////////////////////////////////////////////// - // Abstract class: SceneObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The base class shared by Both TerrainObject:s and MovableObject:s, ie - // anything that can be places in a scene. - // Parent(s): Entity. - // Class history: 8/6/2007 SceneObject created. - + /// The base class shared by Both TerrainObject:s and MovableObject:s, ie + /// anything that can be places in a scene. class SceneObject : public Entity { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableOverrideMethods; ClassInfoGetters; - /// /// Enumeration for the different buyable modes of this SceneObject. - /// enum class BuyableMode { NoRestrictions, BuyMenuOnly, @@ -47,132 +30,68 @@ namespace RTE { ScriptOnly }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Nested class: SOPlacer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Acts as a small memory object that only holds a pointer to a reference - // instance and the most essential properties to eventually place a copy - // of that reference when needed. - // Parent(s): Serializable. - // Class history: 11/25/2007 SOPlacer created. - + /// Acts as a small memory object that only holds a pointer to a reference + /// instance and the most essential properties to eventually place a copy + /// of that reference when needed. class SOPlacer : public Serializable { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableClassNameGetter; SerializableOverrideMethods; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: SOPlacer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a SOPlacer object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a SOPlacer object in system + /// memory. Create() should be called before using the object. SOPlacer() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a SOPlacer to be identical to another, by deep copy. - // Arguments: A reference to the SOPlacer to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates a SOPlacer to be identical to another, by deep copy. + /// @param reference A reference to the SOPlacer to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const SOPlacer& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire Serializable, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire Serializable, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetObjectReference - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the object reference to be placed. Owenership is NOT transferred! - // Arguments: None. - // Return value: A pointer to the reference object to be copied and placed. Not transferred! - + /// Gets the object reference to be placed. Owenership is NOT transferred! + /// @return A pointer to the reference object to be copied and placed. Not transferred! const SceneObject* GetObjectReference() { return m_pObjectReference; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the place offset from the parent's position/origin. If in a scene - // this will yield the absolute scene coordinates. - // Arguments: None. - // Return value: The offset in pixels from the parent's position where this gets spawned. - + /// Gets the place offset from the parent's position/origin. If in a scene + /// this will yield the absolute scene coordinates. + /// @return The offset in pixels from the parent's position where this gets spawned. Vector GetOffset() const { return m_Offset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the place offset from the parent's position/origin. - // Arguments: New offset. - // Return value: None. - + /// Sets the place offset from the parent's position/origin. + /// @param newOffset New offset. void SetOffset(Vector newOffset) { m_Offset = newOffset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRotation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rotation angle of the object to be placed, in radians. - // Arguments: None. - // Return value: The placement rotational angle, in radians. - + /// Gets the rotation angle of the object to be placed, in radians. + /// @return The placement rotational angle, in radians. float GetRotation() const { return m_RotAngle; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetHFlipped - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets whether the placement is horizontally flipped or not. - // Arguments: None. - // Return value: The horizontal flipping of the placement. - + /// Gets whether the placement is horizontally flipped or not. + /// @return The horizontal flipping of the placement. bool GetHFlipped() const { return m_HFlipped; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets which team this is to be assigned to when placed. - // Arguments: None. - // Return value: The team number this is to be assigned to when placed. - + /// Gets which team this is to be assigned to when placed. + /// @return The team number this is to be assigned to when placed. int GetTeam() const { return m_Team; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this is to be assigned to when placed. - // Arguments: The team number this is to be assigned to when placed. - // Return value: None. - + /// Sets which team this is to be assigned to when placed. + /// @param team The team number this is to be assigned to when placed. void SetTeam(int team) { m_Team = team; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlacedCopy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes a copy of the preset instance, and applies the placement - // properties of this to it, finally returning it WITH OWNERSHIP. - // Arguments: The parent to place as offset from. If 0 is passed, the placement - // properties will be applied as absolutes instead of relative. - // Return value: The new copy with correct placement applied. OWNERSHIP IS TRANSFERRED! - + /// Makes a copy of the preset instance, and applies the placement + /// properties of this to it, finally returning it WITH OWNERSHIP. + /// @param pParent The parent to place as offset from. If 0 is passed, the placement (default: 0) + /// properties will be applied as absolutes instead of relative. + /// @return The new copy with correct placement applied. OWNERSHIP IS TRANSFERRED! SceneObject* GetPlacedCopy(const SceneObject* pParent = 0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // The pointer to the preset instance, that copies of which will be placed. Not Owned! const SceneObject* m_pObjectReference; @@ -185,345 +104,181 @@ namespace RTE { // The team of the placed object int m_Team; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this SOPlacer, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this SOPlacer, effectively + /// resetting the members of this abstraction level only. void Clear(); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: SceneObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a SceneObject object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a SceneObject object in system + /// memory. Create() should be called before using the object. SceneObject() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~SceneObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a SceneObject object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a SceneObject object before deletion + /// from system memory. ~SceneObject() override { Destroy(true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the SceneObject object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the SceneObject object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates an SceneObject to be identical to another, by deep copy. - // Arguments: A reference to the SceneObject to deep copy. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Creates an SceneObject to be identical to another, by deep copy. + /// @param reference A reference to the SceneObject to deep copy. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(const SceneObject& reference); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire SceneObject, including its inherited members, to their - // default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire SceneObject, including its inherited members, to their + /// default settings or values. void Reset() override { Clear(); Entity::Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Pure V. method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneObject object. - // Arguments: Whether to only destroy the members defined in this derived class, or - // to destroy all inherited members also. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneObject object. + /// @param notInherited Whether to only destroy the members defined in this derived class, or (default: false) + /// to destroy all inherited members also. void Destroy(bool notInherited = false) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the absolute position of this SceneObject. - // Arguments: None. - // Return value: A Vector describing the current absolute position in pixels. - + /// Gets the absolute position of this SceneObject. + /// @return A Vector describing the current absolute position in pixels. const Vector& GetPos() const { return m_Pos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the absolute position of this SceneObject in the scene. - // Arguments: A Vector describing the current absolute position in pixels. - // Return value: None. - + /// Sets the absolute position of this SceneObject in the scene. + /// @param newPos A Vector describing the current absolute position in pixels. void SetPos(const Vector& newPos) { m_Pos = newPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsHFlipped - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether this is being drawn flipped horizontally (around the - // vertical axis), or not. - // Arguments: None. - // Return value: Whether flipped or not. - + /// Returns whether this is being drawn flipped horizontally (around the + /// vertical axis), or not. + /// @return Whether flipped or not. virtual bool IsHFlipped() const { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRotMatrix - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current rotational Matrix of of this MovableObject. - // Arguments: None. - // Return value: The rotational Matrix of this MovableObject. - + /// Gets the current rotational Matrix of of this MovableObject. + /// @return The rotational Matrix of this MovableObject. virtual Matrix GetRotMatrix() const { return Matrix(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetRotAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current rotational angle of of this, in radians. - // Arguments: None. - // Return value: The rotational angle of this, in radians. - + /// Gets the current rotational angle of of this, in radians. + /// @return The rotational angle of this, in radians. virtual float GetRotAngle() const { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virutal method: SetHFlipped - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether this should be drawn flipped horizontally (around the - // vertical axis). - // Arguments: A bool with the new value. - // Return value: None. - + /// Sets whether this should be drawn flipped horizontally (around the + /// vertical axis). + /// @param flipped A bool with the new value. virtual void SetHFlipped(const bool flipped) {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetRotAngle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current absolute angle of rotation of this. - // Arguments: The new absolute angle in radians. - // Return value: None. - + /// Sets the current absolute angle of rotation of this. + /// @param newAngle The new absolute angle in radians. virtual void SetRotAngle(float newAngle) {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which team this belongs to. - // Arguments: The assigned team number. - // Return value: None. - + /// Sets which team this belongs to. + /// @param team The assigned team number. virtual void SetTeam(int team) { m_Team = team; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets which team this belongs to. - // Arguments: None. - // Return value: The currently assigned team number. - + /// Gets which team this belongs to. + /// @return The currently assigned team number. int GetTeam() const { return m_Team; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: SetPlacedByPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which player placed this object in the scene, if any. - // Arguments: The player responsible for placing this is in the scene, if any. - // Return value: None. - + /// Sets which player placed this object in the scene, if any. + /// @param player The player responsible for placing this is in the scene, if any. void SetPlacedByPlayer(int player) { m_PlacedByPlayer = player; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: GetPlacedByPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets which player placed this object in the scene, if any. - // Arguments: None. - // Return value: The player responsible for placing this is in the scene, if any. - + /// Gets which player placed this object in the scene, if any. + /// @return The player responsible for placing this is in the scene, if any. int GetPlacedByPlayer() const { return m_PlacedByPlayer; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the cost to purchase this item, in oz's of gold. - // Arguments: If this is supposed to be adjusted for a specific Tech's subjective - // value, then pass in the native DataModule ID of that tech. 0 means - // no Tech is specified and the base value is returned. - // How much to multiply the value if this happens to be a foreign Tech. - // How much to multiply the value if this happens to be a native Tech. - // Return value: The cost, in oz of gold. - + /// Gets the cost to purchase this item, in oz's of gold. + /// @param nativeModule If this is supposed to be adjusted for a specific Tech's subjective (default: 0) + /// value, then pass in the native DataModule ID of that tech. 0 means + /// no Tech is specified and the base value is returned. + /// @param foreignMult How much to multiply the value if this happens to be a foreign Tech. (default: 1.0) + /// @param nativeMult How much to multiply the value if this happens to be a native Tech. (default: 1.0) + /// @return The cost, in oz of gold. virtual float GetGoldValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetGoldValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the cost to purchase this item, in oz's of gold. - // Arguments: The cost, in oz of gold. - // Return value: None. - + /// Sets the cost to purchase this item, in oz's of gold. + /// @param value The cost, in oz of gold. void SetGoldValue(float value) { m_OzValue = value; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldValueOld - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: DOES THE SAME THING AS GetGoldValue, USED ONLY TO PRESERVE LUA COMPATIBILITY - + /// DOES THE SAME THING AS GetGoldValue, USED ONLY TO PRESERVE LUA COMPATIBILITY virtual float GetGoldValueOld(int nativeModule, float foreignMult) const { return GetGoldValue(nativeModule, foreignMult, 1.0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldValueString - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a descriptive string describing the cost to purchase this item, - // in oz's of gold. - // Arguments: If this is supposed to be adjusted for a specific Tech's subjective - // value, then pass in the native DataModule ID of that tech. 0 means - // no Tech is specified and the base value is returned. - // How much to multiply the value if this happens to be a foreign Tech. - // Return value: The cost, described in a friendly to read string: "100oz", or "Free" - + /// Gets a descriptive string describing the cost to purchase this item, + /// in oz's of gold. + /// @param nativeModule If this is supposed to be adjusted for a specific Tech's subjective (default: 0) + /// value, then pass in the native DataModule ID of that tech. 0 means + /// no Tech is specified and the base value is returned. + /// @param foreignMult How much to multiply the value if this happens to be a foreign Tech. (default: 1.0) + /// @return The cost, described in a friendly to read string: "100oz", or "Free" std::string GetGoldValueString(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total liquidation value of this, including everything inside. - // Arguments: If this is supposed to be adjusted for a specific Tech's subjective - // value, then pass in the native DataModule ID of that tech. 0 means - // no Tech is specified and the base value is returned. - // How much to multiply the value if this happens to be a foreign Tech. - // Return value: The current value of this and all contained assets. - + /// Gets the total liquidation value of this, including everything inside. + /// @param nativeModule If this is supposed to be adjusted for a specific Tech's subjective (default: 0) + /// value, then pass in the native DataModule ID of that tech. 0 means + /// no Tech is specified and the base value is returned. + /// @param foreignMult How much to multiply the value if this happens to be a foreign Tech. (default: 1.0) + /// @return The current value of this and all contained assets. virtual float GetTotalValue(int nativeModule = 0, float foreignMult = 1.0, float nativeMult = 1.0) const { return GetGoldValue(nativeModule, foreignMult, nativeMult); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsBuyable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether this should appear in teh buy menus at all. - // Arguments: None. - // Return value: Buyable or not. - + /// Shows whether this should appear in teh buy menus at all. + /// @return Buyable or not. bool IsBuyable() const { return m_Buyable; } - /// /// Gets the BuyableMode of this SceneObject. - /// - /// The BuyableMode of this SceneObject + /// @return The BuyableMode of this SceneObject BuyableMode GetBuyableMode() const { return m_BuyableMode; } - /// /// Gets whether this SceneObject is available only in the BuyMenu list when buyable. - /// - /// Whether this SceneObject is available only in the BuyMenu list when buyable. + /// @return Whether this SceneObject is available only in the BuyMenu list when buyable. bool IsBuyableInBuyMenuOnly() const { return m_BuyableMode == BuyableMode::BuyMenuOnly; } - /// /// Gets whether this SceneObject is available only in the ObjectPicker list when buyable. - /// - /// Whether this SceneObject is available only in the ObjectPicker list when buyable. + /// @return Whether this SceneObject is available only in the ObjectPicker list when buyable. bool IsBuyableInObjectPickerOnly() const { return m_BuyableMode == BuyableMode::ObjectPickerOnly; } - /// /// Gets whether this SceneObject is available only by lua functions like CreateRandom - /// - /// Whether this SceneObject is available only in the AI list when buyable. + /// @return Whether this SceneObject is available only in the AI list when buyable. bool IsBuyableInScriptOnly() const { return m_BuyableMode == BuyableMode::ScriptOnly; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGraphicalIcon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a bitmap showing a good identifyable icon of this, for use in - // GUI lists etc. - // Arguments: None. - // Return value: A good identifyable graphical representation of this in a BITMAP, if - // available. If not, 0 is returned. Ownership is NOT TRANSFERRED! - + /// Gets a bitmap showing a good identifyable icon of this, for use in + /// GUI lists etc. + /// @return A good identifyable graphical representation of this in a BITMAP, if + /// available. If not, 0 is returned. Ownership is NOT TRANSFERRED! virtual BITMAP* GetGraphicalIcon() const { return nullptr; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: IsOnScenePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether this' current graphical representation overlaps - // a point in absolute scene coordinates. - // Arguments: The point in absolute scene coordinates. - // Return value: Whether this' graphical rep overlaps the scene point. - + /// Indicates whether this' current graphical representation overlaps + /// a point in absolute scene coordinates. + /// @param scenePoint The point in absolute scene coordinates. + /// @return Whether this' graphical rep overlaps the scene point. virtual bool IsOnScenePoint(Vector& scenePoint) const { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates this SceneObject. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates this SceneObject. Supposed to be done every frame. virtual void Update() {} - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: FullUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the full state of this object in one call. - // Arguments: None. - // Return value: None. + /// Updates the full state of this object in one call. virtual void FullUpdate() { Update(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: PostUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Function called after everything has finished updating. - // Arguments: None. - // Return value: None. + /// Function called after everything has finished updating. virtual void PostUpdate(){}; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this SceneObject's current graphical representation to a BITMAP of - // choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // In which mode to draw in. See the DrawMode enumeration for the modes. - // Whether to not draw any extra 'ghost' items of this MovableObject, - // like indicator arrows or hovering HUD text and so on. - // Return value: None. - + /// Draws this SceneObject's current graphical representation to a BITMAP of + /// choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. (default: g_DrawColor) + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, (default: false) + /// like indicator arrows or hovering HUD text and so on. virtual void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const = 0; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: DrawTeamMark - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws team sign this terrain object belongs to. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the Scene. - // Return value: None. - + /// Draws team sign this terrain object belongs to. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. (default: Vector()) void DrawTeamMark(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Forbidding copying SceneObject(const SceneObject& reference) = delete; @@ -545,18 +300,10 @@ namespace RTE { // The player this was placed by in edit mode int m_PlacedByPlayer; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this SceneObject, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this SceneObject, effectively + /// resetting the members of this abstraction level only. void Clear(); }; diff --git a/Source/Entities/SoundContainer.cpp b/Source/Entities/SoundContainer.cpp index f71b53dcb8..3d2f5c456c 100644 --- a/Source/Entities/SoundContainer.cpp +++ b/Source/Entities/SoundContainer.cpp @@ -15,8 +15,6 @@ namespace RTE { {"UI", SoundContainer::BusRouting::UI}, {"Music", SoundContainer::BusRouting::MUSIC}}; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SoundContainer::Clear() { m_TopLevelSoundSet.Destroy(); @@ -40,8 +38,6 @@ namespace RTE { m_PitchVariation = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundContainer::Create(const SoundContainer& reference) { Entity::Create(reference); @@ -68,8 +64,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundContainer::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -130,8 +124,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundContainer::Save(Writer& writer) const { Entity::Save(writer); @@ -178,8 +170,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float SoundContainer::GetLength(LengthOfSoundType type) const { if (!m_SoundPropertiesUpToDate) { // Todo - use a post-load fixup stage instead of lazily initializing shit everywhere... Eugh. @@ -200,8 +190,6 @@ namespace RTE { return lengthMilliseconds; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector SoundContainer::GetSelectedSoundHashes() const { std::vector soundHashes; std::vector flattenedSoundData; @@ -212,8 +200,6 @@ namespace RTE { return soundHashes; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const SoundSet::SoundData* SoundContainer::GetSoundDataForSound(const FMOD::Sound* sound) const { std::vector flattenedSoundData; m_TopLevelSoundSet.GetFlattenedSoundData(flattenedSoundData, false); @@ -225,8 +211,6 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SoundContainer::Play(int player) { if (HasAnySounds()) { if (IsBeingPlayed()) { @@ -241,8 +225,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD_RESULT SoundContainer::UpdateSoundProperties() { FMOD_RESULT result = FMOD_OK; diff --git a/Source/Entities/SoundContainer.h b/Source/Entities/SoundContainer.h index 73322533e5..753d3a36da 100644 --- a/Source/Entities/SoundContainer.h +++ b/Source/Entities/SoundContainer.h @@ -8,9 +8,7 @@ namespace RTE { class Vector; - /// /// A container for sounds that represent a specific sound effect. - /// class SoundContainer : public Entity { public: @@ -18,18 +16,14 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; - /// /// The FMOD channelgroup/bus this sound routes through. - /// enum BusRouting { SFX = 0, // Default diegetic bus for general game SFX. UI = 1, // Menu sounds and other things that shouldn't be affected by diegetic sound processing. MUSIC = 2 // Self-explanatory music bus. }; - /// /// How the SoundContainer should behave when it tries to play again while already playing. - /// enum SoundOverlapMode { OVERLAP = 0, RESTART = 1, @@ -37,35 +31,27 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a SoundContainer object in system memory. Create() should be called before using the object. - /// SoundContainer() { Clear(); } - /// /// Copy constructor method used to instantiate a SoundContainer object identical to an already existing one. - /// - /// A reference to the SoundContainer to deep copy. + /// @param reference A reference to the SoundContainer to deep copy. SoundContainer(const SoundContainer& reference) { Clear(); Create(reference); } - /// /// Creates a SoundContainer to be identical to another, by deep copy. - /// - /// A reference to the SoundContainer to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the SoundContainer to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const SoundContainer& reference); - /// /// Creates a SoundContainer and adds a sound, optionally setting immobility, being affected by global pitch, and bus routing. - /// - /// The path to a sound to add to the first SoundSet of this SoundContainer. - /// Whether this SoundContainer's sounds will be treated as immobile, i.e. they won't be affected by 3D sound manipulation. - /// Whether this SoundContainer's sounds' frequency will be affected by the global pitch. - /// Bus to route this sound to. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param soundFilePath The path to a sound to add to the first SoundSet of this SoundContainer. + /// @param immobile Whether this SoundContainer's sounds will be treated as immobile, i.e. they won't be affected by 3D sound manipulation. + /// @param affectedByGlobalPitch Whether this SoundContainer's sounds' frequency will be affected by the global pitch. + /// @param busRouting Bus to route this sound to. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const std::string& soundFilePath, bool immobile = false, bool affectedByGlobalPitch = true, BusRouting busRouting = BusRouting::SFX) { m_TopLevelSoundSet.AddSound(soundFilePath, true); SetImmobile(immobile); @@ -76,15 +62,11 @@ namespace RTE { #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a SoundContainer object before deletion from system memory. - /// ~SoundContainer() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the SoundContainer object. It doesn't delete the Sound files, since they're owned by ContentFile static maps. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); @@ -92,9 +74,7 @@ namespace RTE { Clear(); } - /// /// Resets the entire SoundContainer, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Entity::Reset(); @@ -102,10 +82,8 @@ namespace RTE { #pragma endregion #pragma region Sound Management Getters and Setters - /// /// Shows whether this SoundContainer's top level SoundSet has any SoundData or SoundSets. - /// - /// Whether this SoundContainer has any sounds. + /// @return Whether this SoundContainer has any sounds. bool HasAnySounds() const { return m_TopLevelSoundSet.HasAnySounds(); } enum class LengthOfSoundType { @@ -113,132 +91,94 @@ namespace RTE { NextPlayed }; - /// /// Gets the length of the sound in this sound container. - /// - /// Whether to get the length of the next played sound, or the maximum length of any sound in this container. - /// The length of this sound, in ms. + /// @param type Whether to get the length of the next played sound, or the maximum length of any sound in this container. + /// @return The length of this sound, in ms. float GetLength(LengthOfSoundType type) const; - /// /// Gets a reference to the top level SoundSet of this SoundContainer, to which all SoundData and sub SoundSets belong. - /// - /// A reference to the top level SoundSet of this SoundContainer. + /// @return A reference to the top level SoundSet of this SoundContainer. SoundSet& GetTopLevelSoundSet() { return m_TopLevelSoundSet; } - /// /// Copies the passed in SoundSet reference into the top level SoundSet of this SoundContainer, effectively making that the new top level SoundSet. - /// - /// A reference to the new top level SoundSet for this SoundContainer. + /// @param newTopLevelSoundSet A reference to the new top level SoundSet for this SoundContainer. void SetTopLevelSoundSet(const SoundSet& newTopLevelSoundSet) { m_TopLevelSoundSet = newTopLevelSoundSet; m_SoundPropertiesUpToDate = false; } - /// /// Gets a vector of hashes of the sounds selected to be played next in this SoundContainer. - /// - /// The currently playing sounds hashes. + /// @return The currently playing sounds hashes. std::vector GetSelectedSoundHashes() const; - /// /// Gets the SoundData object that corresponds to the given FMOD::Sound. If the sound can't be found, it returns a null pointer. - /// - /// The FMOD::Sound to search for. - /// A pointer to the corresponding SoundData or a null pointer. + /// @param sound The FMOD::Sound to search for. + /// @return A pointer to the corresponding SoundData or a null pointer. const SoundSet::SoundData* GetSoundDataForSound(const FMOD::Sound* sound) const; - /// /// Gets the channels playing sounds from this SoundContainer. - /// - /// The channels currently being used. + /// @return The channels currently being used. std::unordered_set const* GetPlayingChannels() const { return &m_PlayingChannels; } - /// /// Indicates whether any sound in this SoundContainer is currently being played. - /// - /// Whether any sounds are playing. + /// @return Whether any sounds are playing. bool IsBeingPlayed() const { return !m_PlayingChannels.empty(); } - /// /// Adds a channel index to the SoundContainer's collection of playing channels. - /// - /// The channel index to add. + /// @param channel The channel index to add. void AddPlayingChannel(int channel) { m_PlayingChannels.insert(channel); } - /// /// Removes a channel index from the SoundContainer's collection of playing channels. - /// - /// The channel index to remove. + /// @param channel The channel index to remove. void RemovePlayingChannel(int channel) { m_PlayingChannels.erase(channel); } - /// /// Gets the SoundOverlapMode of this SoundContainer, which is used to determine how it should behave when it's told to play while already playing. - /// - /// The SoundOverlapMode of this SoundContainer. + /// @return The SoundOverlapMode of this SoundContainer. SoundOverlapMode GetSoundOverlapMode() const { return m_SoundOverlapMode; } - /// /// Sets the SoundOverlapMode of this SoundContainer, which is used to determine how it should behave when it's told to play while already playing. - /// - /// The new SoundOverlapMode this SoundContainer should use. + /// @param newSoundOverlapMode The new SoundOverlapMode this SoundContainer should use. void SetSoundOverlapMode(SoundOverlapMode newSoundOverlapMode) { m_SoundOverlapMode = newSoundOverlapMode; } #pragma endregion #pragma region Sound Property Getters and Setters - /// /// Gets the bus this sound routes to. - /// - /// The bus this sound routes to. + /// @return The bus this sound routes to. BusRouting GetBusRouting() const { return m_BusRouting; } - /// /// Sets the bus this sound routes to. - /// - /// The new bus for this sound to route to. + /// @param newBusRoute The new bus for this sound to route to. void SetBusRouting(BusRouting newBusRoute) { m_BusRouting = newBusRoute; } - /// /// Gets whether the sounds in this SoundContainer should be considered immobile, i.e. always play at the listener's position. - /// - /// Whether or not the sounds in this SoundContainer are immobile. + /// @return Whether or not the sounds in this SoundContainer are immobile. bool IsImmobile() const { return m_Immobile; } - /// /// Sets whether the sounds in this SoundContainer should be considered immobile, i.e. always play at the listener's position. Does not affect currently playing sounds. - /// - /// The new immobile setting. + /// @param immobile The new immobile setting. void SetImmobile(bool immobile) { m_Immobile = immobile; m_SoundPropertiesUpToDate = false; } - /// /// Gets the attenuation start distance of this SoundContainer. - /// - /// A float with the attenuation start distance. + /// @return A float with the attenuation start distance. float GetAttenuationStartDistance() const { return m_AttenuationStartDistance; } - /// /// Sets the attenuation start distance of this SoundContainer. Values < 0 set it to default. Does not affect currently playing sounds. - /// - /// The new attenuation start distance. + /// @param attenuationStartDistance The new attenuation start distance. void SetAttenuationStartDistance(float attenuationStartDistance) { m_AttenuationStartDistance = (attenuationStartDistance < 0) ? c_DefaultAttenuationStartDistance : attenuationStartDistance; m_SoundPropertiesUpToDate = false; } - /// /// Gets the custom pan value of this SoundContainer. - /// - /// A float with the custom pan value. + /// @return A float with the custom pan value. float GetCustomPanValue() const { return m_CustomPanValue; } - /// /// Sets the custom pan value of this SoundContainer. Clamped between -1 and 1. - /// - /// The new custom pan value. + /// @param customPanValue The new custom pan value. void SetCustomPanValue(float customPanValue) { m_CustomPanValue = std::clamp(customPanValue, -1.0f, 1.0f); if (IsBeingPlayed()) { @@ -246,78 +186,56 @@ namespace RTE { } } - /// /// Gets the panning strength multiplier of this SoundContainer. - /// - /// A float with the panning strength multiplier. + /// @return A float with the panning strength multiplier. float GetPanningStrengthMultiplier() const { return m_PanningStrengthMultiplier; } - /// /// Sets the panning strength multiplier of this SoundContainer. - /// - /// The new panning strength multiplier. + /// @param panningStrengthMultiplier The new panning strength multiplier. void SetPanningStrengthMultiplier(float panningStrengthMultiplier) { m_PanningStrengthMultiplier = panningStrengthMultiplier; m_SoundPropertiesUpToDate = false; } - /// /// Gets the looping setting of this SoundContainer. - /// - /// An int with the loop count. + /// @return An int with the loop count. int GetLoopSetting() const { return m_Loops; } - /// /// Sets the looping setting of this SoundContainer. Does not affect currently playing sounds. /// 0 means the sound is set to only play once. -1 means it loops indefinitely. - /// - /// The new loop count. + /// @param loops The new loop count. void SetLoopSetting(int loops) { m_Loops = loops; m_SoundPropertiesUpToDate = false; } - /// /// Gets whether the sounds in this SoundContainer have all had all their properties set appropriately. Used to account for issues with ordering in INI loading. - /// - /// Whether or not the sounds in this SoundContainer have their properties set appropriately. + /// @return Whether or not the sounds in this SoundContainer have their properties set appropriately. bool SoundPropertiesUpToDate() const { return m_SoundPropertiesUpToDate; } - /// /// Gets the current playback priority. - /// - /// The playback priority. + /// @return The playback priority. int GetPriority() const { return m_Priority; } - /// /// Sets the current playback priority. Higher priority (lower value) will make this more likely to make it into mixing on playback. Does not affect currently playing sounds. - /// - /// The new priority. See AudioMan::PRIORITY_* enumeration. + /// @param priority The new priority. See AudioMan::PRIORITY_* enumeration. void SetPriority(int priority) { m_Priority = std::clamp(priority, 0, 256); } - /// /// Gets whether the sounds in this SoundContainer are affected by global pitch changes or not. - /// - /// Whether or not the sounds in this SoundContainer are affected by global pitch changes. + /// @return Whether or not the sounds in this SoundContainer are affected by global pitch changes. bool IsAffectedByGlobalPitch() const { return m_AffectedByGlobalPitch; } - /// /// Sets whether the sounds in this SoundContainer are affected by global pitch changes or not. Does not affect currently playing sounds. - /// - /// The new affected by global pitch setting. + /// @param affectedByGlobalPitch The new affected by global pitch setting. void SetAffectedByGlobalPitch(bool affectedByGlobalPitch) { m_AffectedByGlobalPitch = affectedByGlobalPitch; } - /// /// Gets the position at which this SoundContainer's sound will be played. Note that its individual sounds can be offset from this. - /// - /// The position of this SoundContainer. + /// @return The position of this SoundContainer. const Vector& GetPosition() const { return m_Pos; } - /// /// Sets the position of the SoundContainer's sounds while they're playing. - /// - /// The new position to play the SoundContainer's sounds. - /// Whether this SoundContainer's attenuation setting was successful. + /// @param position The new position to play the SoundContainer's sounds. + /// @return Whether this SoundContainer's attenuation setting was successful. void SetPosition(const Vector& newPosition) { if (!m_Immobile && newPosition != m_Pos) { m_Pos = newPosition; @@ -327,16 +245,12 @@ namespace RTE { } } - /// /// Gets the volume the sounds in this SoundContainer are played at. Note that this does not factor volume changes due to the SoundContainer's position. - /// - /// The volume the sounds in this SoundContainer are played at. + /// @return The volume the sounds in this SoundContainer are played at. float GetVolume() const { return m_Volume; } - /// /// Sets the volume sounds in this SoundContainer should be played at. Note that this does not factor volume changes due to the SoundContainer's position. Does not affect currently playing sounds. - /// - /// The new volume sounds in this SoundContainer should be played at. Limited between 0 and 10. + /// @param newVolume The new volume sounds in this SoundContainer should be played at. Limited between 0 and 10. void SetVolume(float newVolume) { newVolume = std::clamp(newVolume, 0.0F, 10.0F); if (IsBeingPlayed()) { @@ -345,16 +259,12 @@ namespace RTE { m_Volume = newVolume; } - /// /// Gets the pitch the sounds in this SoundContainer are played at. Note that this does not factor in global pitch. - /// - /// The pitch the sounds in this SoundContainer are played at. + /// @return The pitch the sounds in this SoundContainer are played at. float GetPitch() const { return m_Pitch; } - /// /// Sets the pitch sounds in this SoundContainer should be played at and updates any playing instances accordingly. - /// - /// The new pitch sounds in this SoundContainer should be played at. Limited between 0.125 and 8 (8 octaves up or down). + /// @param newPitch The new pitch sounds in this SoundContainer should be played at. Limited between 0.125 and 8 (8 octaves up or down). void SetPitch(float newPitch) { m_Pitch = std::clamp(newPitch, 0.125F, 8.0F); if (IsBeingPlayed()) { @@ -362,81 +272,59 @@ namespace RTE { } } - /// /// Gets the pitch variation the sounds in this SoundContainer are played at. - /// - /// The pitch variation the sounds in this SoundContainer are played at. + /// @return The pitch variation the sounds in this SoundContainer are played at. float GetPitchVariation() const { return m_PitchVariation; } - /// /// Sets the pitch variation the sounds in this SoundContainer are played at. - /// - /// The pitch variation the sounds in this SoundContainer are played at. + /// @param newValue The pitch variation the sounds in this SoundContainer are played at. void SetPitchVariation(float newValue) { m_PitchVariation = newValue; } #pragma endregion #pragma region Playback Controls - /// /// Plays the next sound of this SoundContainer at its current position for all players. - /// - /// Whether this SoundContainer successfully started playing on any channels. + /// @return Whether this SoundContainer successfully started playing on any channels. bool Play() { return Play(-1); } - /// /// Plays the next sound of this container at its current position. - /// - /// The player to start playback of this SoundContainer's sounds for. - /// Whether there were sounds to play and they were able to be played. + /// @param player The player to start playback of this SoundContainer's sounds for. + /// @return Whether there were sounds to play and they were able to be played. bool Play(int player); - /// /// Plays the next sound of this SoundContainer at the given position for all players. - /// - /// The position at which to play the SoundContainer's sounds. - /// Whether this SoundContainer successfully started playing on any channels. + /// @param position The position at which to play the SoundContainer's sounds. + /// @return Whether this SoundContainer successfully started playing on any channels. bool Play(const Vector& position) { return Play(position, -1); } - /// /// Plays the next sound of this SoundContainer with the given attenuation for a specific player. - /// - /// The position at which to play the SoundContainer's sounds. - /// The player to start playback of this SoundContainer's sounds for. - /// Whether this SoundContainer successfully started playing on any channels. + /// @param position The position at which to play the SoundContainer's sounds. + /// @param player The player to start playback of this SoundContainer's sounds for. + /// @return Whether this SoundContainer successfully started playing on any channels. bool Play(const Vector& position, int player) { SetPosition(position); return Play(player); } - /// /// Stops playback of this SoundContainer for all players. - /// - /// Whether this SoundContainer successfully stopped playing. + /// @return Whether this SoundContainer successfully stopped playing. bool Stop() { return Stop(-1); } - /// /// Stops playback of this SoundContainer for a specific player. - /// - /// Player to stop playback of this SoundContainer for. - /// Whether this SoundContainer successfully stopped playing. + /// @param player Player to stop playback of this SoundContainer for. + /// @return Whether this SoundContainer successfully stopped playing. bool Stop(int player) { return (HasAnySounds() && IsBeingPlayed()) ? g_AudioMan.StopSoundContainerPlayingChannels(this, player) : false; } - /// /// Restarts playback of this SoundContainer for all players. - /// - /// Whether this SoundContainer successfully restarted its playback. + /// @return Whether this SoundContainer successfully restarted its playback. bool Restart() { return Restart(-1); } - /// /// Restarts playback of this SoundContainer for a specific player. - /// - /// Player to restart playback of this SoundContainer for. - /// Whether this SoundContainer successfully restarted its playback. + /// @param player Player to restart playback of this SoundContainer for. + /// @return Whether this SoundContainer successfully restarted its playback. bool Restart(int player) { return (HasAnySounds() && IsBeingPlayed()) ? g_AudioMan.StopSoundContainerPlayingChannels(this, player) && g_AudioMan.PlaySoundContainer(this, player) : false; } - /// /// Fades out playback of the SoundContainer to 0 volume. - /// - /// How long the fadeout should take. + /// @param fadeOutTime How long the fadeout should take. void FadeOut(int fadeOutTime = 1000) { if (IsBeingPlayed()) { return g_AudioMan.FadeOutSoundContainerPlayingChannels(this, fadeOutTime); @@ -445,11 +333,9 @@ namespace RTE { #pragma endregion #pragma region Miscellaneous - /// /// Updates all sound properties to match this SoundContainer's settings. /// Necessary because sounds loaded from ini seem to be used directly instead of loaded from PresetMan, so their correctness can't be guaranteed when they're played. - /// - /// The FMOD_RESULT for updating all of the SoundContainer's sounds' properties. If it's not FMOD_OK, something went wrong. + /// @return The FMOD_RESULT for updating all of the SoundContainer's sounds' properties. If it's not FMOD_OK, something went wrong. FMOD_RESULT UpdateSoundProperties(); #pragma endregion @@ -480,9 +366,7 @@ namespace RTE { float m_PitchVariation; //!< The randomized pitch variation of this SoundContainer's sounds. 1 means the sound will vary a full octave both ways. float m_Volume; //!< The current natural volume of this SoundContainer's sounds. - /// /// Clears all the member variables of this SoundContainer, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/Entities/SoundSet.cpp b/Source/Entities/SoundSet.cpp index 1bdb6c999c..a61ec8968d 100644 --- a/Source/Entities/SoundSet.cpp +++ b/Source/Entities/SoundSet.cpp @@ -12,8 +12,6 @@ namespace RTE { {"forwards", SoundSelectionCycleMode::FORWARDS}, {"all", SoundSelectionCycleMode::ALL}}; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SoundSet::Clear() { m_SoundSelectionCycleMode = SoundSelectionCycleMode::RANDOM; m_CurrentSelection = {false, -1}; @@ -22,8 +20,6 @@ namespace RTE { m_SubSoundSets.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundSet::Create(const SoundSet& reference) { m_SoundSelectionCycleMode = reference.m_SoundSelectionCycleMode; m_CurrentSelection = reference.m_CurrentSelection; @@ -39,8 +35,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundSet::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -55,8 +49,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SoundSet::SoundData SoundSet::ReadAndGetSoundData(Reader& reader) { SoundSet::SoundData soundData; @@ -101,8 +93,6 @@ namespace RTE { return soundData; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SoundSet::SoundSelectionCycleMode SoundSet::ReadSoundSelectionCycleMode(Reader& reader) { SoundSelectionCycleMode soundSelectionCycleModeToReturn; std::string soundSelectionCycleModeString = reader.ReadPropValue(); @@ -125,8 +115,6 @@ namespace RTE { return soundSelectionCycleModeToReturn; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SoundSet::Save(Writer& writer) const { Serializable::Save(writer); @@ -159,8 +147,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SoundSet::SaveSoundSelectionCycleMode(Writer& writer, SoundSelectionCycleMode soundSelectionCycleMode) { auto cycleModeMapEntry = std::find_if(c_SoundSelectionCycleModeMap.begin(), c_SoundSelectionCycleModeMap.end(), [&soundSelectionCycleMode = soundSelectionCycleMode](auto element) { return element.second == soundSelectionCycleMode; }); if (cycleModeMapEntry != c_SoundSelectionCycleModeMap.end()) { @@ -170,8 +156,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SoundSet::AddSound(const std::string& soundFilePath, const Vector& offset, float minimumAudibleDistance, float attenuationStartDistance, bool abortGameForInvalidSound) { ContentFile soundFile(soundFilePath.c_str()); FMOD::Sound* soundObject = soundFile.GetAsSound(abortGameForInvalidSound, false); @@ -182,8 +166,6 @@ namespace RTE { m_SoundData.push_back({soundFile, soundObject, offset, minimumAudibleDistance, attenuationStartDistance}); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SoundSet::RemoveSound(const std::string& soundFilePath, bool removeFromSubSoundSets) { auto soundsToRemove = std::remove_if(m_SoundData.begin(), m_SoundData.end(), [&soundFilePath](const SoundSet::SoundData& soundData) { return soundData.SoundFile.GetDataPath() == soundFilePath; }); bool anySoundsToRemove = soundsToRemove != m_SoundData.end(); @@ -198,8 +180,6 @@ namespace RTE { return anySoundsToRemove; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SoundSet::HasAnySounds(bool includeSubSoundSets) const { bool hasAnySounds = !m_SoundData.empty(); if (!hasAnySounds && includeSubSoundSets) { @@ -213,8 +193,6 @@ namespace RTE { return hasAnySounds; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SoundSet::GetFlattenedSoundData(std::vector& flattenedSoundData, bool onlyGetSelectedSoundData) { if (!onlyGetSelectedSoundData || m_SoundSelectionCycleMode == SoundSelectionCycleMode::ALL) { for (SoundData& soundData: m_SoundData) { @@ -232,8 +210,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SoundSet::GetFlattenedSoundData(std::vector& flattenedSoundData, bool onlyGetSelectedSoundData) const { if (!onlyGetSelectedSoundData || m_SoundSelectionCycleMode == SoundSelectionCycleMode::ALL) { for (const SoundData& soundData: m_SoundData) { @@ -251,8 +227,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SoundSet::SelectNextSounds() { if (m_SoundSelectionCycleMode == SoundSelectionCycleMode::ALL) { for (SoundSet& subSoundSet: m_SubSoundSets) { diff --git a/Source/Entities/SoundSet.h b/Source/Entities/SoundSet.h index f39f1a7e76..e6e7e7b6ab 100644 --- a/Source/Entities/SoundSet.h +++ b/Source/Entities/SoundSet.h @@ -7,27 +7,21 @@ namespace RTE { - /// /// A set of sounds, and their selection data. - /// class SoundSet : public Serializable { friend struct EntityLuaBindings; public: SerializableOverrideMethods; - /// /// How the SoundSet should choose the next sound or SoundSet to play when SelectNextSound is called. - /// enum SoundSelectionCycleMode { RANDOM = 0, FORWARDS, ALL }; - /// /// Self-contained struct defining an individual sound in a SoundSet. - /// struct SoundData { ContentFile SoundFile; FMOD::Sound* SoundObject; @@ -37,133 +31,97 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a SoundSet object in system memory. Create() should be called before using the object. - /// SoundSet() { Clear(); } - /// /// Creates a SoundSet to be identical to another, by deep copy. - /// - /// A reference to the SoundSet to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the SoundSet to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const SoundSet& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a SoundSet object before deletion from system memory. - /// ~SoundSet() { Destroy(); } - /// /// Destroys and resets (through Clear()) the SoundSet object. - /// void Destroy() { Clear(); } #pragma endregion #pragma region INI Handling - /// /// Handles reading a SoundData from INI, loading it in as a ContentFile and into FMOD, and reading any of its subproperties. /// Does not add the created SoundData to a this SoundSet. - /// - /// A Reader lined up to the value of the property to be read. - /// SoundData for the newly read sound. + /// @param reader A Reader lined up to the value of the property to be read. + /// @return SoundData for the newly read sound. static SoundData ReadAndGetSoundData(Reader& reader); - /// /// Handles turning a SoundCelectionCycleMode from its user-friendly name in INI to its enum value, using the static SoundSelectionCycleMap. - /// - /// A Reader lined up to the value of the property to be read. - /// The appropriate SoundSelectionCycleMode for the given INI value. + /// @param reader A Reader lined up to the value of the property to be read. + /// @return The appropriate SoundSelectionCycleMode for the given INI value. static SoundSelectionCycleMode ReadSoundSelectionCycleMode(Reader& reader); - /// /// Handles writing the given SoundSelectionCycleMode out to the given Writer, using the static SoundSelectionCycleMap. - /// - /// A Writer filled in with the property to write to. - /// The SoundSelectionCycleMode to write. + /// @param writer A Writer filled in with the property to write to. + /// @param soundSelectionCycleMode The SoundSelectionCycleMode to write. static void SaveSoundSelectionCycleMode(Writer& writer, SoundSelectionCycleMode soundSelectionCycleMode); #pragma endregion #pragma region SoundData and SoundSet Addition - /// /// Adds a new sound to this SoundSet, spitting out a Lua error if it fails. The sound will have default configuration. - /// - /// A path to the new sound to add. This will be handled through PresetMan. + /// @param soundFilePath A path to the new sound to add. This will be handled through PresetMan. void AddSound(const std::string& soundFilePath) { AddSound(soundFilePath, false); } - /// /// Adds a new sound to this SoundSet, either spitting out a Lua error or aborting if it fails. The sound will have default configuration. - /// - /// A path to the new sound to add. This will be handled through PresetMan. - /// Whether to abort the game if the sound couldn't be added, or just show a console error. + /// @param soundFilePath A path to the new sound to add. This will be handled through PresetMan. + /// @param abortGameForInvalidSound Whether to abort the game if the sound couldn't be added, or just show a console error. void AddSound(const std::string& soundFilePath, bool abortGameForInvalidSound) { AddSound(soundFilePath, Vector(), 0, -1, abortGameForInvalidSound); } - /// /// Adds a new sound to this SoundSet, spitting out a Lua error if it fails. The sound will be configured based on parameters. - /// - /// A path to the new sound to add. This will be handled through PresetMan. - /// The offset position to play this sound at, where (0, 0) is no offset. - /// The minimum distance at which this sound will be audible. 0 means there is none, which is normally the case. - /// The attenuation start distance for this sound, -1 sets it to default. + /// @param soundFilePath A path to the new sound to add. This will be handled through PresetMan. + /// @param offset The offset position to play this sound at, where (0, 0) is no offset. + /// @param minimumAudibleDistance The minimum distance at which this sound will be audible. 0 means there is none, which is normally the case. + /// @param attenuationStartDistance The attenuation start distance for this sound, -1 sets it to default. void AddSound(const std::string& soundFilePath, const Vector& offset, float minimumAudibleDistance, float attenuationStartDistance) { AddSound(soundFilePath, offset, minimumAudibleDistance, attenuationStartDistance, false); } - /// /// Adds a new sound to this SoundSet, either spitting out a Lua error or aborting if it fails. The sound will be configured based on parameters. - /// - /// A path to the new sound to add. This will be handled through PresetMan. - /// The offset position to play this sound at, where (0, 0) is no offset. - /// The minimum distance at which this sound will be audible. 0 means there is none, which is normally the case. - /// The attenuation start distance for this sound, -1 sets it to default. - /// Whether to abort the game if the sound couldn't be added, or just show a console error. + /// @param soundFilePath A path to the new sound to add. This will be handled through PresetMan. + /// @param offset The offset position to play this sound at, where (0, 0) is no offset. + /// @param minimumAudibleDistance The minimum distance at which this sound will be audible. 0 means there is none, which is normally the case. + /// @param attenuationStartDistance The attenuation start distance for this sound, -1 sets it to default. + /// @param abortGameForInvalidSound Whether to abort the game if the sound couldn't be added, or just show a console error. void AddSound(const std::string& soundFilePath, const Vector& offset, float minimumAudibleDistance, float attenuationStartDistance, bool abortGameForInvalidSound); - /// /// Removes all instances of the sound with the given filepath from this SoundSet. Does not remove it from any sub-SoundSets. - /// - /// The path to the sound to be removed from this SoundSet. - /// Whether or not a sound with the given filepath was found in this SoundSet. + /// @param soundFilePath The path to the sound to be removed from this SoundSet. + /// @return Whether or not a sound with the given filepath was found in this SoundSet. bool RemoveSound(const std::string& soundFilePath) { return RemoveSound(soundFilePath, false); } - /// /// Removes all instances of the sound with the given filepath from this SoundSet, optionally removing it from all sub-SoundSets as well. - /// - /// The path to the sound to be removed from this SoundSet. - /// Whether or not to remove the sound from any sub-SoundSets as well as this SoundSet. - /// Whether or not a sound with the given filepath was found in this SoundSet or, if set to remove from sub-SoundSets, any of its sub-SoundSets. + /// @param soundFilePath The path to the sound to be removed from this SoundSet. + /// @param removeFromSubSoundSets Whether or not to remove the sound from any sub-SoundSets as well as this SoundSet. + /// @return Whether or not a sound with the given filepath was found in this SoundSet or, if set to remove from sub-SoundSets, any of its sub-SoundSets. bool RemoveSound(const std::string& soundFilePath, bool removeFromSubSoundSets); - /// /// Adds a copy of the given SoundData to this SoundSet. - /// - /// The SoundData to copy to this SoundSet. + /// @param soundDataToAdd The SoundData to copy to this SoundSet. void AddSoundData(const SoundData& soundDataToAdd) { m_SoundData.push_back(soundDataToAdd); } - /// /// Adds a copy of the passed in SoundSet as a sub SoundSet of this SoundSet. Ownership IS transferred! - /// - /// A reference to the SoundSet to be copied in as a sub SoundSet of this SoundSet. Ownership IS transferred! + /// @param soundSetToAdd A reference to the SoundSet to be copied in as a sub SoundSet of this SoundSet. Ownership IS transferred! void AddSoundSet(const SoundSet& soundSetToAdd) { m_SubSoundSets.push_back(soundSetToAdd); } #pragma endregion #pragma region Getters and Setters - /// /// Shows whether this SoundSet has any sounds in it or, optionally its SubSoundSets. - /// - /// Whether this SoundSet has any sounds, according to the conditions. + /// @return Whether this SoundSet has any sounds, according to the conditions. bool HasAnySounds(bool includeSubSoundSets = true) const; - /// /// Gets the current SoundSelectionCycleMode for this SoundSet, which is used to determine what SoundSet to select next time SelectNextSounds is called. - /// - /// The current sound selection cycle mode. + /// @return The current sound selection cycle mode. SoundSelectionCycleMode GetSoundSelectionCycleMode() const { return m_SoundSelectionCycleMode; } - /// /// Sets the SoundSelectionCycleMode for this SoundSet, which is used to determine what SoundSet to select next time SelectNextSounds is called. - /// - /// The new SoundSelectionCycleMode for this SoundSet. + /// @param newSoundSelectionCycleMOde The new SoundSelectionCycleMode for this SoundSet. void SetSoundSelectionCycleMode(SoundSelectionCycleMode newSoundSelectionCycleMode) { m_SoundSelectionCycleMode = newSoundSelectionCycleMode; if (m_SoundSelectionCycleMode == SoundSelectionCycleMode::FORWARDS) { @@ -171,40 +129,30 @@ namespace RTE { } } - /// /// Fills the passed in vector with the flattened SoundData in the SoundSet, optionally only getting currently selected SoundData. - /// - /// A reference vector of SoundData references to be filled with this SoundSet's flattened SoundData. - /// Whether to only get SoundData that is currently selected, or to get all SoundData in this SoundSet. + /// @param flattenedSoundData A reference vector of SoundData references to be filled with this SoundSet's flattened SoundData. + /// @param onlyGetSelectedSoundData Whether to only get SoundData that is currently selected, or to get all SoundData in this SoundSet. void GetFlattenedSoundData(std::vector& flattenedSoundData, bool onlyGetSelectedSoundData); - /// /// Fills the passed in vector with the flattened SoundData in the SoundSet, optionally only getting currently selected SoundData. - /// - /// A reference vector of SoundData references to be filled with this SoundSet's flattened SoundData. - /// Whether to only get SoundData that is currently selected, or to get all SoundData in this SoundSet. + /// @param flattenedSoundData A reference vector of SoundData references to be filled with this SoundSet's flattened SoundData. + /// @param onlyGetSelectedSoundData Whether to only get SoundData that is currently selected, or to get all SoundData in this SoundSet. void GetFlattenedSoundData(std::vector& flattenedSoundData, bool onlyGetSelectedSoundData) const; - /// /// Gets the vector of SubSoundSets for this SoundSet. - /// - /// The vector of SubSoundSets for this SoundSet. + /// @return The vector of SubSoundSets for this SoundSet. std::vector& GetSubSoundSets() { return m_SubSoundSets; } #pragma endregion #pragma region Miscellaneous - /// /// Selects the next sounds of this SoundSet to be played, also selecting them for sub SoundSets as appropriate. - /// - /// False if this SoundSet or any of its sub SoundSets failed to select sounds, or true if everything worked. + /// @return False if this SoundSet or any of its sub SoundSets failed to select sounds, or true if everything worked. bool SelectNextSounds(); #pragma endregion #pragma region Class Info - /// /// Gets the class name of this Serializable. - /// - /// A string with the friendly-formatted type name of this object. + /// @return A string with the friendly-formatted type name of this object. const std::string& GetClassName() const override { return m_sClassName; } #pragma endregion @@ -218,9 +166,7 @@ namespace RTE { std::vector m_SoundData; //!< The SoundData available for selection in this SoundSet. std::vector m_SubSoundSets; //!< The sub SoundSets available for selection in this SoundSet. - /// /// Clears all the member variables of this SoundSet, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/Entities/TDExplosive.cpp b/Source/Entities/TDExplosive.cpp index 99a59f98e8..443f14b23b 100644 --- a/Source/Entities/TDExplosive.cpp +++ b/Source/Entities/TDExplosive.cpp @@ -4,14 +4,10 @@ namespace RTE { ConcreteClassInfo(TDExplosive, ThrownDevice, 50); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TDExplosive::Clear() { m_IsAnimatedManually = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TDExplosive::Create() { if (ThrownDevice::Create() < 0) { return -1; @@ -24,8 +20,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TDExplosive::Create(const TDExplosive& reference) { if (ThrownDevice::Create(reference) < 0) { return -1; @@ -35,8 +29,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TDExplosive::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return ThrownDevice::ReadProperty(propName, reader)); @@ -52,8 +44,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TDExplosive::Save(Writer& writer) const { ThrownDevice::Save(writer); writer.NewProperty("IsAnimatedManually"); @@ -61,8 +51,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TDExplosive::Update() { ThrownDevice::Update(); @@ -79,8 +67,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TDExplosive::DrawHUD(BITMAP* targetBitmap, const Vector& targetPos, int whichScreen, bool playerControlled) { if (m_HUDVisible && !m_Activated) { ThrownDevice::DrawHUD(targetBitmap, targetPos, whichScreen); diff --git a/Source/Entities/TDExplosive.h b/Source/Entities/TDExplosive.h index bdc15d1e53..d18ec18b20 100644 --- a/Source/Entities/TDExplosive.h +++ b/Source/Entities/TDExplosive.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// A thrown device that explodes after its trigger delay is completed after its activation. - /// class TDExplosive : public ThrownDevice { public: @@ -16,35 +14,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a TDExplosive object in system memory. Create() should be called before using the object. - /// TDExplosive() { Clear(); } - /// /// Makes the TDExplosive object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a TDExplosive to be identical to another, by deep copy. - /// - /// A reference to the TDExplosive to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the TDExplosive to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const TDExplosive& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a TDExplosive object before deletion from system memory. - /// ~TDExplosive() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the SceneLayer object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { ThrownDevice::Destroy(); @@ -52,9 +40,7 @@ namespace RTE { Clear(); } - /// /// Resets the entire TDExplosive, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); ThrownDevice::Reset(); @@ -62,32 +48,24 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// If true, then the frame will not be changed automatically during update - /// - /// Whether or not the TDExplosive's Frame will change automatically during update. + /// @return Whether or not the TDExplosive's Frame will change automatically during update. bool IsAnimatedManually() const { return m_IsAnimatedManually; } - /// /// Sets whether this TDExplosive is animated manually. - /// - /// Whether or not to animate manually. + /// @param isAnimatedManually Whether or not to animate manually. void SetAnimatedManually(bool isAnimatedManually) { m_IsAnimatedManually = isAnimatedManually; } #pragma endregion #pragma region Virtual Override Methods - /// /// Updates this MovableObject. Supposed to be done every frame. - /// void Update() override; - /// /// Draws this' current graphical HUD overlay representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// Which player's screen this is being drawn to. May affect what HUD elements get drawn etc. - /// Whether or not this MovableObject is currently player controlled (not applicable for TDExplosive). + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param whichScreen Which player's screen this is being drawn to. May affect what HUD elements get drawn etc. + /// @param playerControlled Whether or not this MovableObject is currently player controlled (not applicable for TDExplosive). void DrawHUD(BITMAP* targetBitmap, const Vector& targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; #pragma endregion @@ -97,9 +75,7 @@ namespace RTE { bool m_IsAnimatedManually; //!< If true m_Frame is not changed during an update hence the animation is done by external Lua code. private: - /// /// Clears all the member variables of this TDExplosive, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/TerrainDebris.cpp b/Source/Entities/TerrainDebris.cpp index 5624997d9b..dee86859d6 100644 --- a/Source/Entities/TerrainDebris.cpp +++ b/Source/Entities/TerrainDebris.cpp @@ -5,8 +5,6 @@ namespace RTE { ConcreteClassInfo(TerrainDebris, Entity, 0); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainDebris::Clear() { m_DebrisFile.Reset(); m_Bitmaps.clear(); @@ -25,8 +23,6 @@ namespace RTE { m_Density = 0.01F; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainDebris::Create() { Entity::Create(); m_DebrisFile.GetAsAnimation(m_Bitmaps, m_BitmapCount); @@ -34,8 +30,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainDebris::Create(const TerrainDebris& reference) { Entity::Create(reference); @@ -59,8 +53,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainDebris::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Entity::ReadProperty(propName, reader)); @@ -90,8 +82,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainDebris::Save(Writer& writer) const { Entity::Save(writer); @@ -113,8 +103,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool TerrainDebris::GetPiecePlacementPosition(SLTerrain* terrain, Box& possiblePiecePosition) const { BITMAP* matBitmap = terrain->GetMaterialBitmap(); int posX = RandomNum(0, matBitmap->w); @@ -149,8 +137,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool TerrainDebris::MaterialPixelIsValidTarget(int materialCheckPixel, int prevMaterialCheckPixel) const { bool checkResult = true; @@ -185,8 +171,6 @@ namespace RTE { return checkResult; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainDebris::DrawToTerrain(SLTerrain* terrain, BITMAP* bitmapToDraw, const Vector& position) const { // Create a square temp bitmap that is larger than the original to avoid clipping if rotating. int dimensions = 10 + std::max(bitmapToDraw->w, bitmapToDraw->h); @@ -227,8 +211,6 @@ namespace RTE { destroy_bitmap(tempDrawBitmap); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainDebris::ScatterOnTerrain(SLTerrain* terrain) { RTEAssert(!m_Bitmaps.empty() && m_BitmapCount > 0, "No bitmaps loaded for terrain debris during TerrainDebris::ScatterOnTerrain!"); diff --git a/Source/Entities/TerrainDebris.h b/Source/Entities/TerrainDebris.h index 6f3c837ac7..33ce33d0c5 100644 --- a/Source/Entities/TerrainDebris.h +++ b/Source/Entities/TerrainDebris.h @@ -10,9 +10,7 @@ namespace RTE { class Box; class SLTerrain; - /// /// Debris objects scattered randomly throughout the terrain. - /// class TerrainDebris : public Entity { public: @@ -21,35 +19,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a TerrainDebris object in system memory. Create() should be called before using the object. - /// TerrainDebris() { Clear(); } - /// /// Makes the TerrainDebris object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a TerrainDebris to be identical to another, by deep copy. - /// - /// A reference to the TerrainDebris to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the TerrainDebris to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const TerrainDebris& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a TerrainDebris object before deletion from system memory. - /// ~TerrainDebris() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the TerrainDebris object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { Entity::Destroy(); @@ -59,17 +47,13 @@ namespace RTE { #pragma endregion #pragma region Concrete Methods - /// /// Places random pieces of this TerrainDebris's at random positions on the specified SLTerrain. - /// - /// The SLTerrain to scatter this TerrainDebris on. Ownership is NOT transferred! + /// @param terrain The SLTerrain to scatter this TerrainDebris on. Ownership is NOT transferred! void ScatterOnTerrain(SLTerrain* terrain); #pragma endregion private: - /// /// Enumeration for the different debris placement modes. - /// enum DebrisPlacementMode { NoPlacementRestrictions, OnSurfaceOnly, @@ -105,34 +89,26 @@ namespace RTE { float m_Density; //!< Approximate density of debris pieces per meter. #pragma region Debris Application Breakdown - /// /// Checks if conditions apply for a debris piece to be placed to the terrain. The actual position is returned via the passed in Box's center position. - /// - /// Pointer to the SLTerrain to check debris placement on. Ownership is NOT transferred! - /// A Box that holds the debris piece's dimensions. The center position of the Box will be modified during checking. - /// True if a valid placement position was found, which means the passed in Box's center or corner positions are good to be used as the piece's drawing position. + /// @param terrain Pointer to the SLTerrain to check debris placement on. Ownership is NOT transferred! + /// @param possiblePiecePosition A Box that holds the debris piece's dimensions. The center position of the Box will be modified during checking. + /// @return True if a valid placement position was found, which means the passed in Box's center or corner positions are good to be used as the piece's drawing position. bool GetPiecePlacementPosition(SLTerrain* terrain, Box& possiblePiecePosition) const; - /// /// Checks whether the passed in pixel color value is of target Material, and if extra conditions apply for it to be valid for placement, depending on DebrisPlacementMode. - /// - /// The pixel color value to check. - /// The previously checked pixel color value to check extra conditions with. Does not apply when DebrisPlacementMode is NoPlacementRestrictions. - /// Whether the passed in pixel color value is valid for debris placement. + /// @param materialCheckPixel The pixel color value to check. + /// @param prevMaterialCheckPixel The previously checked pixel color value to check extra conditions with. Does not apply when DebrisPlacementMode is NoPlacementRestrictions. + /// @return Whether the passed in pixel color value is valid for debris placement. bool MaterialPixelIsValidTarget(int materialCheckPixel, int prevMaterialCheckPixel) const; - /// /// Draws the debris piece bitmap on the terrain at the specified position. Performs flipping and rotating if necessary. - /// - /// Pointer to the SLTerrain to draw the debris piece on. Ownership is NOT transferred! - /// The BITMAP to draw. Ownership is NOT transferred! - /// The position to draw the debris piece on the terrain. + /// @param terrain Pointer to the SLTerrain to draw the debris piece on. Ownership is NOT transferred! + /// @param bitmapToDraw The BITMAP to draw. Ownership is NOT transferred! + /// @param position The position to draw the debris piece on the terrain. void DrawToTerrain(SLTerrain* terrain, BITMAP* bitmapToDraw, const Vector& position) const; #pragma endregion - /// /// Clears all the member variables of this TerrainDebris, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/TerrainFrosting.cpp b/Source/Entities/TerrainFrosting.cpp index a2a2807f7f..64d0747d19 100644 --- a/Source/Entities/TerrainFrosting.cpp +++ b/Source/Entities/TerrainFrosting.cpp @@ -5,8 +5,6 @@ namespace RTE { const std::string TerrainFrosting::c_ClassName = "TerrainFrosting"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainFrosting::Clear() { m_FrostingMaterial.Reset(); m_TargetMaterial.Reset(); @@ -15,8 +13,6 @@ namespace RTE { m_InAirOnly = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainFrosting::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -29,8 +25,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainFrosting::Save(Writer& writer) const { Serializable::Save(writer); @@ -43,8 +37,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainFrosting::FrostTerrain(SLTerrain* terrain) const { BITMAP* frostingTexture = m_FrostingMaterial.GetFGTexture(); BITMAP* fgColorBitmap = terrain->GetFGColorBitmap(); diff --git a/Source/Entities/TerrainFrosting.h b/Source/Entities/TerrainFrosting.h index c83c2d20e2..043afade5e 100644 --- a/Source/Entities/TerrainFrosting.h +++ b/Source/Entities/TerrainFrosting.h @@ -7,9 +7,7 @@ namespace RTE { class SLTerrain; - /// /// A layer of material on top of another material on the terrain. - /// class TerrainFrosting : public Serializable { public: @@ -17,19 +15,15 @@ namespace RTE { SerializableOverrideMethods #pragma region Creation - /// /// Constructor method used to instantiate a TerrainFrosting object in system memory and make it ready for use. - /// TerrainFrosting() { Clear(); } #pragma endregion #pragma region Concrete Methods - /// /// Draws the frosting layer to the specified SLTerrain according to the read-in parameters. - /// - /// The SLTerrain to frost. Ownership is NOT transferred! + /// @param terrain The SLTerrain to frost. Ownership is NOT transferred! void FrostTerrain(SLTerrain* terrain) const; #pragma endregion @@ -42,9 +36,7 @@ namespace RTE { int m_MaxThickness; //!< The maximum height above the target Material, in pixels. bool m_InAirOnly; //!< Whether the frosting only appears where there is air (i.e. does not appear where the terrain background layer is showing). - /// /// Clears all the member variables of this TerrainFrosting, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/TerrainObject.cpp b/Source/Entities/TerrainObject.cpp index e86cfa15ae..70bf1067d4 100644 --- a/Source/Entities/TerrainObject.cpp +++ b/Source/Entities/TerrainObject.cpp @@ -6,8 +6,6 @@ namespace RTE { ConcreteClassInfo(TerrainObject, SceneObject, 0); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainObject::Clear() { m_FGColorFile.Reset(); m_FGColorBitmap = nullptr; @@ -20,8 +18,6 @@ namespace RTE { m_ChildObjects.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainObject::Create() { SceneObject::Create(); @@ -39,8 +35,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainObject::Create(const TerrainObject& reference) { SceneObject::Create(reference); @@ -59,8 +53,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainObject::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return SceneObject::ReadProperty(propName, reader)); @@ -97,8 +89,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TerrainObject::Save(Writer& writer) const { SceneObject::Save(writer); @@ -122,8 +112,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* TerrainObject::GetGraphicalIcon() const { if (m_FGColorBitmap) { // Check several spots on the FG bitmap, to be sure it has parts that aren't transparent. If not, show the background layer instead. @@ -135,8 +123,6 @@ namespace RTE { return m_BGColorBitmap; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainObject::SetTeam(int team) { SceneObject::SetTeam(team); for (SceneObject::SOPlacer& childObject: m_ChildObjects) { @@ -144,8 +130,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool TerrainObject::IsOnScenePoint(Vector& scenePoint) const { // TODO: TAKE CARE OF WRAPPING Vector bitmapPos = m_Pos + m_BitmapOffset; @@ -164,8 +148,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool TerrainObject::PlaceOnTerrain(SLTerrain* terrain) { if (!terrain) { return false; @@ -182,8 +164,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainObject::Draw(BITMAP* targetBitmap, const Vector& targetPos, DrawMode drawMode, bool onlyPhysical) const { std::array drawPos = {m_Pos + m_BitmapOffset - targetPos}; int wrapPasses = 1; @@ -244,8 +224,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TerrainObject::DrawToTerrain(SLTerrain* terrain) { BITMAP* terrainMatBitmap = terrain->GetMaterialBitmap(); BITMAP* terrainBGBitmap = terrain->GetBGColorBitmap(); diff --git a/Source/Entities/TerrainObject.h b/Source/Entities/TerrainObject.h index 9374419f39..762bf16996 100644 --- a/Source/Entities/TerrainObject.h +++ b/Source/Entities/TerrainObject.h @@ -8,9 +8,7 @@ namespace RTE { class SLTerrain; - /// /// A feature of the terrain, which includes foreground color layer, material layer and optional background layer. - /// class TerrainObject : public SceneObject { public: @@ -19,35 +17,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a TerrainObject object in system memory. Create() should be called before using the object. - /// TerrainObject() { Clear(); } - /// /// Makes the TerrainObject object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a TerrainObject to be identical to another, by deep copy. - /// - /// A reference to the TerrainObject to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the TerrainObject to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const TerrainObject& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a TerrainObject object before deletion from system memory. - /// ~TerrainObject() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the TerrainObject object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { SceneObject::Destroy(); @@ -57,103 +45,73 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Returns whether this TerrainObject has any foreground color data. - /// - /// Whether this TerrainOBject has any foreground color data. + /// @return Whether this TerrainOBject has any foreground color data. bool HasFGColorBitmap() const { return m_FGColorBitmap != nullptr; } - /// /// Gets the BITMAP that this TerrainObject uses for its foreground color representation. - /// - /// A pointer to the foreground color BITMAP object. Ownership is NOT transferred! + /// @return A pointer to the foreground color BITMAP object. Ownership is NOT transferred! BITMAP* GetFGColorBitmap() const { return m_FGColorBitmap; } - /// /// Returns whether this TerrainObject has any background color data. - /// - /// Whether this TerrainOBject has any background color data. + /// @return Whether this TerrainOBject has any background color data. bool HasBGColorBitmap() const { return m_BGColorBitmap != nullptr; } - /// /// Gets the BITMAP that this TerrainObject uses for its background color representation, if any. - /// - /// A pointer to the background color BITMAP object. This may be nullptr if there is no BG bitmap. Ownership is NOT transferred! + /// @return A pointer to the background color BITMAP object. This may be nullptr if there is no BG bitmap. Ownership is NOT transferred! BITMAP* GetBGColorBitmap() const { return m_BGColorBitmap; } - /// /// Returns whether this TerrainObject has any material data. - /// - /// Whether this TerrainOBject has any material data. + /// @return Whether this TerrainOBject has any material data. bool HasMaterialBitmap() const { return m_MaterialBitmap != nullptr; } - /// /// Gets the BITMAP that this TerrainObject uses for its material representation, if any. - /// - /// A pointer to the material BITMAP object. Ownership is NOT transferred! + /// @return A pointer to the material BITMAP object. Ownership is NOT transferred! BITMAP* GetMaterialBitmap() const { return m_MaterialBitmap; } - /// /// Gets the offset from the position to the upper left corner of this TerrainObject's BITMAPs. - /// - /// A Vector describing the bitmap offset, in pixels. + /// @return A Vector describing the bitmap offset, in pixels. const Vector& GetBitmapOffset() const { return m_BitmapOffset; } - /// /// Gets the width of the widest BITMAP of this TerrainObject's layers. - /// - /// The width of this TerrainObject. + /// @return The width of this TerrainObject. int GetBitmapWidth() const { return std::max({m_MaterialBitmap ? m_MaterialBitmap->w : 0, m_BGColorBitmap ? m_BGColorBitmap->w : 0, m_FGColorBitmap ? m_FGColorBitmap->w : 0}); } - /// /// Gets the height of the highest BITMAP of this TerrainObject's layers. - /// - /// The height of this TerrainObject. + /// @return The height of this TerrainObject. int GetBitmapHeight() const { return std::max({m_MaterialBitmap ? m_MaterialBitmap->h : 0, m_BGColorBitmap ? m_BGColorBitmap->h : 0, m_FGColorBitmap ? m_FGColorBitmap->h : 0}); } - /// /// Gets the list of child objects that should be placed when this TerrainObject is placed. - /// - /// A reference to the list of child objects. Ownership of the list is NOT transferred! + /// @return A reference to the list of child objects. Ownership of the list is NOT transferred! const std::vector& GetChildObjects() const { return m_ChildObjects; } - /// /// Gets a BITMAP showing a good identifiable icon of this, for use in GUI lists. - /// - /// A good identifiable graphical representation of this in a BITMAP, if available. If not, nullptr is returned. Ownership is NOT transferred! + /// @return A good identifiable graphical representation of this in a BITMAP, if available. If not, nullptr is returned. Ownership is NOT transferred! BITMAP* GetGraphicalIcon() const override; - /// /// Sets which team this TerrainObject belongs to. - /// - /// The assigned team number. + /// @param team The assigned team number. void SetTeam(int team) override; #pragma endregion #pragma region Concrete Methods - /// /// Places this TerrainObject and all its children on the specified SLTerrain. - /// - /// The SLTerrain to place this TerrainObject on. Ownership is NOT transferred! - /// Whether the object was successfully placed on the terrain. + /// @param terrain The SLTerrain to place this TerrainObject on. Ownership is NOT transferred! + /// @return Whether the object was successfully placed on the terrain. bool PlaceOnTerrain(SLTerrain* terrain); #pragma endregion #pragma region Virtual Override Methods - /// /// Indicates whether this TerrainObject's current graphical representation overlaps a point in absolute scene coordinates. - /// - /// The point in absolute scene coordinates. - /// Whether this' graphical rep overlaps the scene point. + /// @param scenePoint The point in absolute scene coordinates. + /// @return Whether this' graphical rep overlaps the scene point. bool IsOnScenePoint(Vector& scenePoint) const override; - /// /// Draws this TerrainObject's current graphical representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// In which mode to draw in. See the DrawMode enumeration for the modes. - /// Whether to not draw any extra 'ghost' items of this TerrainObject, like indicator arrows or hovering HUD text and so on. + /// @param targetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param drawMode In which mode to draw in. See the DrawMode enumeration for the modes. + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this TerrainObject, like indicator arrows or hovering HUD text and so on. void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector(), DrawMode drawMode = DrawMode::g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion @@ -175,15 +133,11 @@ namespace RTE { std::vector m_ChildObjects; //!< The objects that are placed along with this TerrainObject on the Scene. private: - /// /// Draws this TerrainObject's graphical and material representations to the specified SLTerrain's respective layers. - /// - /// The SLTerrain to draw this TerrainObject to. Ownership is NOT transferred! + /// @param terrain The SLTerrain to draw this TerrainObject to. Ownership is NOT transferred! void DrawToTerrain(SLTerrain* terrain); - /// /// Clears all the member variables of this TerrainObject, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/ThrownDevice.cpp b/Source/Entities/ThrownDevice.cpp index e1b469230e..e9913cff91 100644 --- a/Source/Entities/ThrownDevice.cpp +++ b/Source/Entities/ThrownDevice.cpp @@ -7,8 +7,6 @@ namespace RTE { ConcreteClassInfo(ThrownDevice, HeldDevice, 50); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ThrownDevice::Clear() { m_ActivationSound.Reset(); m_StartThrowOffset.Reset(); @@ -20,8 +18,6 @@ namespace RTE { m_StrikerLever = nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ThrownDevice::Create() { if (HeldDevice::Create() < 0) { return -1; @@ -31,8 +27,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ThrownDevice::Create(const ThrownDevice& reference) { HeldDevice::Create(reference); @@ -51,8 +45,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ThrownDevice::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return HeldDevice::ReadProperty(propName, reader)); @@ -68,8 +60,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ThrownDevice::Save(Writer& writer) const { HeldDevice::Save(writer); @@ -93,8 +83,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float ThrownDevice::GetCalculatedMaxThrowVelIncludingArmThrowStrength() { if (m_MaxThrowVel > 0) { return m_MaxThrowVel; @@ -104,8 +92,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ThrownDevice::ResetAllTimers() { double elapsedTime = m_Activated ? m_ActivationTimer.GetElapsedSimTimeMS() : 0; HeldDevice::ResetAllTimers(); @@ -114,8 +100,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ThrownDevice::Activate() { if (!m_Activated) { m_ActivationTimer.Reset(); diff --git a/Source/Entities/ThrownDevice.h b/Source/Entities/ThrownDevice.h index 9738b68dac..3943b397f9 100644 --- a/Source/Entities/ThrownDevice.h +++ b/Source/Entities/ThrownDevice.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// A device that is carried and thrown by Actors. - /// class ThrownDevice : public HeldDevice { public: @@ -16,35 +14,25 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a ThrownDevice object in system memory. Create should be called before using the object. - /// ThrownDevice() { Clear(); } - /// /// Makes the ThrownDevice object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates a ThrownDevice to be identical to another, by deep copy. - /// - /// A reference to the ThrownDevice to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the ThrownDevice to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const ThrownDevice& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a ThrownDevice object before deletion from system memory. - /// ~ThrownDevice() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the SceneLayer object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override { if (!notInherited) { HeldDevice::Destroy(); @@ -52,9 +40,7 @@ namespace RTE { Clear(); } - /// /// Resets the entire ThrownDevice, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Attachable::Reset(); @@ -62,82 +48,56 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the start throw offset of this ThrownDevice's joint relative from the parent Actor's position, if attached. - /// - /// A const reference to the current start throw parent offset. + /// @return A const reference to the current start throw parent offset. Vector GetStartThrowOffset() const { return m_StartThrowOffset; } - /// /// Sets the start throw offset for this ThrownDevice. - /// - /// The new start throw offset. + /// @param startOffset The new start throw offset. void SetStartThrowOffset(Vector startOffset) { m_StartThrowOffset = startOffset; } - /// /// Gets the end throw offset of this ThrownDevice's joint relative from the parent Actor's position, if attached. - /// - /// A const reference to the current end throw parent offset. + /// @return A const reference to the current end throw parent offset. Vector GetEndThrowOffset() const { return m_EndThrowOffset; } - /// /// Sets the end throw offset for this ThrownDevice. - /// - /// The new end throw offset. + /// @param endOffset The new end throw offset. void SetEndThrowOffset(Vector endOffset) { m_EndThrowOffset = endOffset; } - /// /// Gets the minimum throw velocity of this when thrown. - /// - /// The minimum throw velocity of this, in m/s. + /// @return The minimum throw velocity of this, in m/s. float GetMinThrowVel() const { return m_MinThrowVel; } - /// /// Sets the minimum throw velocity of this when thrown. - /// - /// The minimum throw velocity of this, in m/s. + /// @param minThrowVel The minimum throw velocity of this, in m/s. void SetMinThrowVel(float minThrowVel) { m_MinThrowVel = minThrowVel; } - /// /// Gets the maximum throw velocity of this when thrown. - /// - /// The maximum throw velocity of this, in m/s. + /// @return The maximum throw velocity of this, in m/s. float GetMaxThrowVel() const { return m_MaxThrowVel; } - /// /// Sets the maximum throw velocity of this when thrown. - /// - /// The maximum throw velocity of this, in m/s. + /// @param maxThrowVel The maximum throw velocity of this, in m/s. void SetMaxThrowVel(float maxThrowVel) { m_MaxThrowVel = maxThrowVel; } - /// /// Ugly method to deal with lua AI bullshit, by pulling the max throwvel calculation based on arm strength into here. /// If throw velocity is decided by the Arm and not by the ThrownDevice, then the mass of the ThrownDevice and the angular velocity of the root parent Actor will be taken into account. - /// - /// The max throw vel to use. + /// @return The max throw vel to use. float GetCalculatedMaxThrowVelIncludingArmThrowStrength(); - /// /// If true then the explosive will not activate until it's released. - /// - /// Whether this ThrownDevice is supposed to only activate when it's released. + /// @return Whether this ThrownDevice is supposed to only activate when it's released. bool ActivatesWhenReleased() const { return m_ActivatesWhenReleased; } #pragma endregion #pragma region Virtual Override Methods - /// /// Resets all the timers used by this (e.g. emitters, etc). This is to prevent backed up emissions from coming out all at once while this has been held dormant in an inventory. - /// void ResetAllTimers() override; - /// /// Activates this Device as long as it's not set to activate when released or it has no parent. - /// void Activate() override; - /// /// Does the calculations necessary to detect whether this ThrownDevice is at rest or not. IsAtRest() retrieves the answer. - /// void RestDetection() override { HeldDevice::RestDetection(); if (m_Activated) { @@ -160,9 +120,7 @@ namespace RTE { const MovableObject* m_StrikerLever; //!< Striker lever particle MovableObject preset instance. private: - /// /// Clears all the member variables of this ThrownDevice, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Entities/Turret.cpp b/Source/Entities/Turret.cpp index 40c1a63915..6e2613226d 100644 --- a/Source/Entities/Turret.cpp +++ b/Source/Entities/Turret.cpp @@ -7,15 +7,11 @@ namespace RTE { ConcreteClassInfo(Turret, Attachable, 20); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::Clear() { m_MountedDevices.clear(); m_MountedDeviceRotationOffset = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Turret::Create(const Turret& reference) { if (!reference.m_MountedDevices.empty()) { for (const HeldDevice* referenceMountedDevice: reference.m_MountedDevices) { @@ -30,8 +26,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::Destroy(bool notInherited) { if (!notInherited) { Attachable::Destroy(); @@ -42,8 +36,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Turret::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Attachable::ReadProperty(propName, reader)); @@ -54,8 +46,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Turret::Save(Writer& writer) const { Attachable::Save(writer); @@ -69,8 +59,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::SetFirstMountedDevice(HeldDevice* newMountedDevice) { if (HasMountedDevice()) { RemoveAndDeleteAttachable(m_MountedDevices[0]); @@ -95,8 +83,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::AddMountedDevice(HeldDevice* newMountedDevice) { if (newMountedDevice == nullptr) { return; @@ -119,8 +105,6 @@ namespace RTE { newMountedDevice->SetSupportAvailable(true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::Update() { for (HeldDevice* mountedDevice: m_MountedDevices) { mountedDevice->SetRotAngle(m_Rotation.GetRadAngle() + m_MountedDeviceRotationOffset); @@ -128,8 +112,6 @@ namespace RTE { Attachable::Update(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::Draw(BITMAP* pTargetBitmap, const Vector& targetPos, DrawMode mode, bool onlyPhysical) const { Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); // TODO replace this with a relative draw order property or something that lets you organize attachable drawing so it doesn't need special hardcoding crap. Use this for ahuman limbs and arm held mo if possible. @@ -140,8 +122,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::SetParent(MOSRotating* newParent) { Attachable::SetParent(newParent); for (HeldDevice* mountedDevice: m_MountedDevices) { @@ -149,8 +129,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Turret::RemoveMountedDevice(const HeldDevice* mountedDeviceToRemove) { std::vector::iterator mountedDeviceIterator = std::find_if(m_MountedDevices.begin(), m_MountedDevices.end(), [&mountedDeviceToRemove](const HeldDevice* mountedDevice) { return mountedDevice == mountedDeviceToRemove; diff --git a/Source/Entities/Turret.h b/Source/Entities/Turret.h index f7205ea4b7..335e5a2951 100644 --- a/Source/Entities/Turret.h +++ b/Source/Entities/Turret.h @@ -7,9 +7,7 @@ namespace RTE { class HeldDevice; - /// /// An Attachable Turret pod that can hold HeldDevices. - /// class Turret : public Attachable { public: @@ -18,34 +16,24 @@ namespace RTE { ClassInfoGetters; #pragma region Creation - /// /// Constructor method used to instantiate a Turret object in system memory. Create() should be called before using the object. - /// Turret() { Clear(); } - /// /// Creates a Turret to be identical to another, by deep copy. - /// - /// A reference to the Turret to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Turret to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Turret& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a Turret object before deletion from system memory. - /// ~Turret() override { Destroy(true); } - /// /// Destroys and resets (through Clear()) the Turret object. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. void Destroy(bool notInherited = false) override; - /// /// Resets the entire Turret, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); Attachable::Reset(); @@ -53,73 +41,53 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Indicates whether a HeldDevice is mounted or not. - /// - /// Whether or not a HeldDevice is mounted on this Turret. + /// @return Whether or not a HeldDevice is mounted on this Turret. bool HasMountedDevice() const { return !m_MountedDevices.empty(); } - /// /// Gets the first mounted HeldDevice of this Turret, mostly here for Lua convenience. - /// - /// A pointer to mounted HeldDevice of this Turret. Ownership is NOT transferred! + /// @return A pointer to mounted HeldDevice of this Turret. Ownership is NOT transferred! HeldDevice* GetFirstMountedDevice() const { return m_MountedDevices[0]; } - /// /// Sets the first mounted HeldDevice for this Turret, mostly here for Lua convenience. Ownership IS transferred! /// The current first mounted HeldDevice (if there is one) will be dropped and added to MovableMan. - /// - /// The new HeldDevice to use. + /// @param newMountedDevice The new HeldDevice to use. void SetFirstMountedDevice(HeldDevice* newMountedDevice); - /// /// Gets the vector of mounted HeldDevices for this Turret. - /// - /// The vector of mounted HeldDevices for this Turret. + /// @return The vector of mounted HeldDevices for this Turret. const std::vector& GetMountedDevices() const { return m_MountedDevices; } - /// /// Adds a HeldDevice to be mounted on this Turret. Ownership IS transferred! /// Will not remove any other HeldDevices mounted on this Turret. - /// - /// The new HeldDevice to be mounted on this Turret. + /// @param newMountedDevice The new HeldDevice to be mounted on this Turret. void AddMountedDevice(HeldDevice* newMountedDevice); - /// /// Gets the current rotational offset of the mounted HeldDevice from the rest of the Turret. - /// - /// The current rotational offset of the mounted HeldDevice from the rest of the Turret. + /// @return The current rotational offset of the mounted HeldDevice from the rest of the Turret. float GetMountedDeviceRotationOffset() const { return m_MountedDeviceRotationOffset; } - /// /// Sets the current rotational offset of the mounted HeldDevice from the rest of the Turret. - /// - /// The new offset angle in radians, relative from the rest of the Turret. + /// @param newOffsetAngle The new offset angle in radians, relative from the rest of the Turret. void SetMountedDeviceRotationOffset(float newOffsetAngle) { m_MountedDeviceRotationOffset = newOffsetAngle; } #pragma endregion #pragma region Override Methods - /// /// Updates this MovableObject. Supposed to be done every frame. - /// void Update() override; - /// /// Draws this Turret's current graphical representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// In which mode to draw in. See the DrawMode enumeration for the modes. - /// Whether to not draw any extra 'ghost' items of this MovableObject, indicator arrows or hovering HUD text and so on. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the Scene. + /// @param mode In which mode to draw in. See the DrawMode enumeration for the modes. + /// @param onlyPhysical Whether to not draw any extra 'ghost' items of this MovableObject, indicator arrows or hovering HUD text and so on. void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; #pragma endregion protected: - /// /// Sets this Attachable's parent MOSRotating, and also sets its Team based on its parent and, if the Attachable is set to collide, adds/removes Atoms to its new/old parent. /// Additionally, deactivates all MountedDevices. - /// - /// A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! + /// @param newParent A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! void SetParent(MOSRotating* newParent) override; static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. @@ -129,15 +97,11 @@ namespace RTE { std::vector m_MountedDevices; //!< Vector of pointers to the mounted HeldDevices of this Turret, if any. Owned here. float m_MountedDeviceRotationOffset; //!< The relative offset angle (in radians) of the mounted HeldDevice from this Turret's rotation. - /// /// Removes the HeldDevice from this turret's vector of mounted devices if it's in there. This releases the unique_ptr for it, leaving the caller to take care of it. - /// - /// A pointer to the mounted device to remove. + /// @param mountedDeviceToRemove A pointer to the mounted device to remove. void RemoveMountedDevice(const HeldDevice* mountedDeviceToRemove); - /// /// Clears all the member variables of this Turret, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/GUI/GUI.h b/Source/GUI/GUI.h index 29a658a810..689ebb2745 100644 --- a/Source/GUI/GUI.h +++ b/Source/GUI/GUI.h @@ -10,9 +10,7 @@ #endif #pragma region Rectangle Structure -/// /// The GUIRect structure defines a rectangle by the coordinates of its upper-left and lower-right corners. -/// struct GUIRect { long left; long top; @@ -20,14 +18,12 @@ struct GUIRect { long bottom; }; -/// /// Sets the bounds of a GUIRect. -/// -/// Pointer to the GUIRect. -/// Position of top left corner on X axis. -/// Position of top left corner on Y axis. -/// Position of bottom right corner on X axis. -/// Position of bottom right corner on Y axis. +/// @param pRect Pointer to the GUIRect. +/// @param left Position of top left corner on X axis. +/// @param top Position of top left corner on Y axis. +/// @param right Position of bottom right corner on X axis. +/// @param bottom Position of bottom right corner on Y axis. inline void SetRect(GUIRect* rect, int left, int top, int right, int bottom) { rect->left = left; rect->top = top; diff --git a/Source/GUI/GUIBanner.cpp b/Source/GUI/GUIBanner.cpp index 920dc5f902..16345d1391 100644 --- a/Source/GUI/GUIBanner.cpp +++ b/Source/GUI/GUIBanner.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GUIBanner.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: GUIBanner class -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "GUIBanner.h" #include "ContentFile.h" @@ -18,12 +6,6 @@ namespace RTE { std::map GUIBanner::m_sFontCache; std::map GUIBanner::m_sCharCapCache; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIBanner - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIBanner object in system - // memory. - GUIBanner::GUIBanner() { m_pFontImage[REGULAR] = 0; m_pFontImage[BLURRED] = 0; @@ -47,11 +29,6 @@ namespace RTE { m_FrameTimer.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the font from an image file. - bool GUIBanner::Create(const std::string fontFilePath, const std::string fontBlurFilePath, int bitDepth) { // Package the font bitmap paths o they are more easily processed below std::string filePaths[2] = {fontFilePath, fontBlurFilePath}; @@ -175,22 +152,11 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys the font data - void GUIBanner::Destroy() { m_BannerText.clear(); m_BannerChars.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SpaceBetween - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells how much space, in pixels, currently exists between two flying - // characters. - int GUIBanner::SpaceBetween(const FlyingChar& first, FontMode firstMode, const FlyingChar& second, FontMode secondMode) const { if (first.m_PosX < second.m_PosX) return second.m_PosX - first.m_PosX - CalculateWidth(first.m_Character, firstMode); @@ -198,11 +164,6 @@ namespace RTE { return first.m_PosX - second.m_PosX - CalculateWidth(second.m_Character, secondMode); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ShowText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Starts the display animation of a text string in this banner's font. - void GUIBanner::ShowText(const std::string& text, AnimMode mode, long duration, Vector targetSize, float yOnTarget, int flySpeed, int flySpacing) { m_BannerText = text; m_AnimMode = mode; @@ -272,11 +233,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the position of the flying characters of this banner. - void GUIBanner::Update() { double deltaS = m_FrameTimer.GetElapsedRealTimeS(); @@ -361,11 +317,6 @@ namespace RTE { m_FrameTimer.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws text to a bitmap. - void GUIBanner::Draw(BITMAP* pTargetBitmap) { // Only bother drawing if things are visible at all if (m_AnimState < SHOWING || m_AnimState > HIDING) @@ -400,11 +351,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculateWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the width of a piece of text. - int GUIBanner::CalculateWidth(const std::string text, FontMode mode) const { unsigned char c; int Width = 0; @@ -438,11 +384,6 @@ namespace RTE { return Width; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculateWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the width of a piece of text. - int GUIBanner::CalculateWidth(const char Character, FontMode mode) const { unsigned char c = Character; if (c >= 32 && c < m_CharIndexCap) diff --git a/Source/GUI/GUIBanner.h b/Source/GUI/GUIBanner.h index b97477bcc1..d2454a9012 100644 --- a/Source/GUI/GUIBanner.h +++ b/Source/GUI/GUIBanner.h @@ -1,15 +1,10 @@ #ifndef _GUIBANNER_ #define _GUIBANNER_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GUIBanner.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: GUIBanner class -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - +/// GUIBanner class +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com #include "Vector.h" #include "Timer.h" #include "allegro.h" @@ -20,19 +15,11 @@ struct BITMAP; namespace RTE { - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: GUIBanner - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A class to handle the drawing of LARGE text banners that fly across - // the screen, grabbing the player's attention. - // Parent(s): None. - // Class history: 5/7/2011 GUIBanner Created. - + /// A class to handle the drawing of LARGE text banners that fly across + /// the screen, grabbing the player's attention. class GUIBanner { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: enum FontMode { REGULAR = 0, @@ -80,151 +67,80 @@ namespace RTE { float m_Speed; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIBanner - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIBanner object in system - // memory. - // Arguments: None. - // Return value: None. - + /// Constructor method used to instantiate a GUIBanner object in system + /// memory. GUIBanner(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the font from an image file. - // Arguments: Path to the font bitmap file. - // Path to the blurred font bitmap file. - // At which color bit depth to load the font files as. - // Return value: None. - + /// Create the font from an image file. + /// @param fontFilePath Path to the font bitmap file. + /// @param fontBlurFilePath Path to the blurred font bitmap file. + /// @param bitDepth At which color bit depth to load the font files as. bool Create(const std::string fontFilePath, const std::string fontBlurFilePath, int bitDepth); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys the font data - // Arguments: None. - // Return value: None. - + /// Destroys the font data void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBannerText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the currently displayed text string. - // Arguments: None. - // Return value: The currently displayed text string. - + /// Gets the currently displayed text string. + /// @return The currently displayed text string. std::string GetBannerText() const { return m_BannerText; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAnimState - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current state of the overall animation of this banner. - // Arguments: None. - // Return value: The current state of the animation. - + /// Gets the current state of the overall animation of this banner. + /// @return The current state of the animation. AnimState GetAnimState() const { return m_AnimState; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether this banner is currently showing anything on screen. - // Arguments: None. - // Return value: Whether this is showing anything presently. - + /// Tells whether this banner is currently showing anything on screen. + /// @return Whether this is showing anything presently. bool IsVisible() const { return m_AnimState >= SHOWING && m_AnimState <= HIDING; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculateWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the width of a piece of text. - // Arguments: Text. - // Return value: None. - + /// Calculates the width of a piece of text. + /// @param Text Text. int CalculateWidth(const std::string Text, FontMode mode) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculateWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the width of a piece of text. - // Arguments: Character. - // Return value: None. - + /// Calculates the width of a piece of text. + /// @param Character Character. int CalculateWidth(const char Character, FontMode mode) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFontHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the font height. - // Arguments: None. - // Return value: The font height in pixels. - + /// Gets the font height. + /// @return The font height in pixels. int GetFontHeight() const { return m_FontHeight; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetKerning - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get the character kerning (spacing) - // Arguments: None. - // Return value: Spacing between characters, in pixels. 1 = one empty pixel - // between chars, 0 = chars are touching. - + /// Get the character kerning (spacing) + /// @return Spacing between characters, in pixels. 1 = one empty pixel + /// between chars, 0 = chars are touching. int GetKerning() const { return m_Kerning; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetKerning - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Set the character kerning (spacing), in pixels. 1 = one empty pixel - // between chars, 0 = chars are touching. - // Arguments: The new kerning value. - // Return value: None. - + /// Set the character kerning (spacing), in pixels. 1 = one empty pixel + /// between chars, 0 = chars are touching. + /// @param newKerning The new kerning value. (default: 1) void SetKerning(int newKerning = 1) { m_Kerning = newKerning; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SpaceBetween - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells how much space, in pixels, currently exists between two flying - // characters. - // Arguments: The first FlyingChar. - // The font mode of the first character. - // The second FlyingChar. - // The font mode of the second character. - // Return value: The space, in pixels. - + /// Tells how much space, in pixels, currently exists between two flying + /// characters. + /// @param first The first FlyingChar. + /// @param firstMode The font mode of the first character. + /// @param second The second FlyingChar. + /// @param secondMode The font mode of the second character. + /// @return The space, in pixels. int SpaceBetween(const FlyingChar& first, FontMode firstMode, const FlyingChar& second, FontMode secondMode) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ShowText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Starts the display animation of a text string in this banner's font. - // This only needs to be called once per display animation. Any currently - // played animation will be interrupted and the banner restarted. - // Arguments: The text to display. - // The animation mode to display the text in. - // The duration of the animation the text is displayed in. Negative value - // means the text pauses at the display/center until HideText is called. - // The width and height of the bitmap target this will be displayed on. - // The Y position the banner should appear on the target, in normalized - // value. 0.5 = banner midline is centered on halfway down the target. - // The speed at which the characters will fly, in pixels per second. - // The spacing between the flying characters, in pixels. - // Return value: None. - + /// Starts the display animation of a text string in this banner's font. + /// This only needs to be called once per display animation. Any currently + /// played animation will be interrupted and the banner restarted. + /// @param text The text to display. + /// @param mode The animation mode to display the text in. + /// @param duration The duration of the animation the text is displayed in. Negative value + /// means the text pauses at the display/center until HideText is called. + /// @param targetSize The width and height of the bitmap target this will be displayed on. + /// @param yOnTarget The Y position the banner should appear on the target, in normalized + /// value. 0.5 = banner midline is centered on halfway down the target. + /// @param flySpeed The speed at which the characters will fly, in pixels per second. (default: 1500) + /// @param flySpacing The spacing between the flying characters, in pixels. (default: 100) void ShowText(const std::string& text, AnimMode mode, long duration, Vector targetSize, float yOnTarget, int flySpeed = 1500, int flySpacing = 100); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HideText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells the banner to animate away elegantly. Especially useful when - // a ShowText is waiting with a negative duration. - // Arguments: The speed at which the characters will fly, in pixels per second. - // The spacing between the flying characters, in pixels. - // Return value: None. - + /// Tells the banner to animate away elegantly. Especially useful when + /// a ShowText is waiting with a negative duration. + /// @param flySpeed The speed at which the characters will fly, in pixels per second. (default: 1500) + /// @param flySpacing The spacing between the flying characters, in pixels. (default: 100) void HideText(int flySpeed = 1500, int flySpacing = 100) { if (m_AnimState <= SHOW) { m_AnimState = HIDING; @@ -233,40 +149,21 @@ namespace RTE { m_FlySpacing = flySpacing; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Abruptly clears any text without animating it away. Resets this thing. - // Arguments: None. - // Return value: None. - + /// Abruptly clears any text without animating it away. Resets this thing. void ClearText() { m_BannerText.clear(); m_BannerChars.clear(); m_AnimState = NOTSTARTED; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the position of the flying characters of this banner. - // Arguments: None. - // Return value: None. - + /// Updates the position of the flying characters of this banner. void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws text to a bitmap. - // Arguments: The target bitmap to draw to. - // Return value: None. - + /// Draws text to a bitmap. + /// @param pTargetBitmap The target bitmap to draw to. void Draw(BITMAP* pTargetBitmap); - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: // Font bitmap files - not owned BITMAP* m_pFontImage[FONTMODECOUNT]; diff --git a/Source/GUI/GUIButton.cpp b/Source/GUI/GUIButton.cpp index f6a53ac61f..8b48e88c6e 100644 --- a/Source/GUI/GUIButton.cpp +++ b/Source/GUI/GUIButton.cpp @@ -5,8 +5,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIButton::GUIButton(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "BUTTON"; @@ -19,8 +17,6 @@ GUIButton::GUIButton(GUIManager* Manager, GUIControlManager* ControlManager) : m_BorderSizes = nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -66,8 +62,6 @@ void GUIButton::Create(const std::string& Name, int X, int Y, int Width, int Hei } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -114,8 +108,6 @@ void GUIButton::Create(GUIProperties* Props) { m_Text->SetVerticalOverflowScroll(overflowScroll); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::Destroy() { // Free the drawing bitmap if (m_DrawBitmap) { @@ -125,8 +117,6 @@ void GUIButton::Destroy() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -134,8 +124,6 @@ void GUIButton::ChangeSkin(GUISkin* Skin) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::BuildBitmap() { // Free any old bitmap if (m_DrawBitmap) { @@ -234,8 +222,6 @@ void GUIButton::BuildBitmap() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::Draw(GUIScreen* Screen) { GUIRect Rect; int y = 0; @@ -255,8 +241,6 @@ void GUIButton::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::OnMouseDown(int X, int Y, int Buttons, int Modifier) { if (Buttons & MOUSE_LEFT) { SetPushed(true); @@ -267,8 +251,6 @@ void GUIButton::OnMouseDown(int X, int Y, int Buttons, int Modifier) { SetFocus(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::OnMouseUp(int X, int Y, int Buttons, int Modifier) { if (PointInside(X, Y)) { AddEvent(GUIEvent::Notification, Clicked, Buttons); @@ -289,8 +271,6 @@ void GUIButton::OnMouseUp(int X, int Y, int Buttons, int Modifier) { AddEvent(GUIEvent::Notification, UnPushed, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::OnMouseEnter(int X, int Y, int Buttons, int Modifier) { m_Over = true; m_Text->ActivateDeactivateOverflowScroll(true); @@ -298,8 +278,6 @@ void GUIButton::OnMouseEnter(int X, int Y, int Buttons, int Modifier) { AddEvent(GUIEvent::Notification, Focused, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { m_Over = false; if (!m_GotFocus && !m_Pushed) { @@ -308,15 +286,11 @@ void GUIButton::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::OnGainFocus() { GUIPanel::OnGainFocus(); m_Text->ActivateDeactivateOverflowScroll(true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::OnLoseFocus() { GUIPanel::OnLoseFocus(); if (!m_Over && !m_Pushed) { @@ -325,8 +299,6 @@ void GUIButton::OnLoseFocus() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::OnMouseMove(int X, int Y, int Buttons, int Modifier) { if (!(Buttons & MOUSE_LEFT) || !IsCaptured()) { return; @@ -346,39 +318,27 @@ void GUIButton::OnMouseMove(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::OnKeyDown(int KeyCode, int Modifier) { } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIButton::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::StoreProperties() { m_Properties.AddVariable("Text", m_Text->GetText()); m_Properties.AddVariable("HorizontalOverflowScroll", m_Text->GetHorizontalOverflowScroll()); m_Properties.AddVariable("VerticalOverflowScroll", m_Text->GetVerticalOverflowScroll()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::Move(int X, int Y) { GUIPanel::SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::Resize(int Width, int Height) { // Make sure the button isn't too small Width = std::max(Width, m_MinWidth); @@ -389,8 +349,6 @@ void GUIButton::Resize(int Width, int Height) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::SetPushed(bool pushed) { m_Pushed = pushed; if (pushed) { @@ -401,8 +359,6 @@ void GUIButton::SetPushed(bool pushed) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::SetText(const std::string_view& newText, bool noBitmapRebuild) { if (GetText() != newText) { m_Text->SetText(newText); @@ -412,14 +368,10 @@ void GUIButton::SetText(const std::string_view& newText, bool noBitmapRebuild) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::string& GUIButton::GetText() const { return m_Text->GetText(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::SetHorizontalOverflowScroll(bool newOverflowScroll) { m_Text->SetHorizontalOverflowScroll(newOverflowScroll); if (m_Text->GetHorizontalOverflowScroll()) { @@ -427,8 +379,6 @@ void GUIButton::SetHorizontalOverflowScroll(bool newOverflowScroll) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::SetVerticalOverflowScroll(bool newOverflowScroll) { m_Text->SetVerticalOverflowScroll(newOverflowScroll); if (m_Text->GetVerticalOverflowScroll()) { @@ -436,8 +386,6 @@ void GUIButton::SetVerticalOverflowScroll(bool newOverflowScroll) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::SetIcon(BITMAP* newIcon, bool noBitmapRebuild) { if (m_Icon && m_Icon->GetBitmap() != newIcon) { m_Icon->SetBitmap(newIcon); @@ -447,8 +395,6 @@ void GUIButton::SetIcon(BITMAP* newIcon, bool noBitmapRebuild) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::SetIconAndText(BITMAP* newIcon, const std::string_view& newText) { bool bitmapNeedsRebuild = (m_Icon && m_Icon->GetBitmap() != newIcon) || (m_Text && m_Text->GetText() != newText); SetIcon(newIcon, true); @@ -458,8 +404,6 @@ void GUIButton::SetIconAndText(BITMAP* newIcon, const std::string_view& newText) } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIButton::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUIButton.h b/Source/GUI/GUIButton.h index 2111c0a9e0..5722df55dc 100644 --- a/Source/GUI/GUIButton.h +++ b/Source/GUI/GUIButton.h @@ -5,9 +5,7 @@ namespace RTE { class GUILabel; - /// /// A button control class. - /// class GUIButton : public GUIControl, public GUIPanel { public: @@ -19,236 +17,126 @@ namespace RTE { Focused } Notification; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIButton - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIButton object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUIButton object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUIButton(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseMove - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse moves (over the panel, or when captured). - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse moves (over the panel, or when captured). + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseEnter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse enters the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse enters the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseLeave - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse leaves the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse leaves the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnGainFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the panel gains focus. - // Arguments: None. - + /// Called when the panel gains focus. void OnGainFocus() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnLoseFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the panel looses focus. - // Arguments: None. - + /// Called when the panel looses focus. void OnLoseFocus() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnKeyDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when a key goes down. - // Arguments: KeyCode, Modifier. - + /// Called when a key goes down. + /// @param KeyCode KeyCode, Modifier. void OnKeyDown(int KeyCode, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "BUTTON"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - /// /// Gets whether or not this button is currently pushed. - /// - /// Whether or not this button is currently pushed. + /// @return Whether or not this button is currently pushed. bool IsPushed() const { return m_Pushed; } - /// /// Sets whether or not this button is currently pushed. - /// - /// Whether or not this button should be pushed. + /// @param pushed Whether or not this button should be pushed. void SetPushed(bool pushed = false); - /// /// Gets whether or not this button is currently being moused over. - /// - /// Whether or not this button is currently being moused over. + /// @return Whether or not this button is currently being moused over. bool IsMousedOver() const { return m_Over; } - /// /// Gets the text of this GUIButton's GUILabel. - /// - /// The text of this GUIButton's GUILabel. + /// @return The text of this GUIButton's GUILabel. const std::string& GetText() const; - /// /// Sets the text of this GUIButton's GUILabel. - /// - /// The new text for this GUIButton's GUILabel. - /// Lets this method NOT rebuild the button bitmap, even if the icon has changed. Defaults to false and should almost always stay that way. + /// @param newText The new text for this GUIButton's GUILabel. + /// @param noBitmapRebuild Lets this method NOT rebuild the button bitmap, even if the icon has changed. Defaults to false and should almost always stay that way. void SetText(const std::string_view& newText, bool noBitmapRebuild = false); - /// /// Sets whether or not this GUIButton's text should scroll horizontally (right) when it overflows the button. - /// - /// Whether or not this GUIButton's text should scroll horizontally when it overflows. + /// @param newOverflowScroll Whether or not this GUIButton's text should scroll horizontally when it overflows. void SetHorizontalOverflowScroll(bool newOverflowScroll); - /// /// Sets whether or not this GUIButton's text should scroll vertically (down) when it overflows the button. - /// - /// Whether or not this GUIButton's text should scroll vertically when it overflows. + /// @param newOverflowScroll Whether or not this GUIButton's text should scroll vertically when it overflows. void SetVerticalOverflowScroll(bool newOverflowScroll); - /// /// Gets whether or not this GUIButton has an icon with a Bitmap. - /// bool HasIcon() const { return m_Icon->GetBitmap(); } - /// /// Sets the icon for this GUIButton. Ownership is NOT transferred. - /// - /// A pointer to the new icon BITMAP for this GUIButton. - /// Lets this method NOT rebuild the button bitmap, even if the icon has changed. Defaults to false and should almost always stay that way. + /// @param newIcon A pointer to the new icon BITMAP for this GUIButton. + /// @param noBitmapRebuild Lets this method NOT rebuild the button bitmap, even if the icon has changed. Defaults to false and should almost always stay that way. void SetIcon(BITMAP* newIcon, bool noBitmapRebuild = false); - /// /// Helper method to set both text and icon for this GUIButton at the same time. - /// - /// A pointer to the new icon BITMAP for this GUIButton. - /// The new text for this GUIButton's GUILabel. + /// @param newIcon A pointer to the new icon BITMAP for this GUIButton. + /// @param newText The new text for this GUIButton's GUILabel. void SetIconAndText(BITMAP* newIcon, const std::string_view& newText); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: @@ -260,12 +148,7 @@ namespace RTE { std::unique_ptr m_Icon; std::unique_ptr m_BorderSizes; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the button bitmap to draw. - // Arguments: None. - + /// Create the button bitmap to draw. void BuildBitmap(); }; }; // namespace RTE diff --git a/Source/GUI/GUICheckbox.cpp b/Source/GUI/GUICheckbox.cpp index 6c1d623e9a..0fd5c850ac 100644 --- a/Source/GUI/GUICheckbox.cpp +++ b/Source/GUI/GUICheckbox.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICheckbox::GUICheckbox(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "CHECKBOX"; @@ -14,8 +12,6 @@ GUICheckbox::GUICheckbox(GUIManager* Manager, GUIControlManager* ControlManager) m_Mouseover = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -45,8 +41,6 @@ void GUICheckbox::Create(const std::string& Name, int X, int Y, int Width, int H m_Height = std::max(m_Height, m_MinHeight); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -77,12 +71,8 @@ void GUICheckbox::Create(GUIProperties* Props) { Props->GetValue("Text", &m_Text); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::Destroy() {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -90,8 +80,6 @@ void GUICheckbox::ChangeSkin(GUISkin* Skin) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::BuildBitmap() { std::string Filename; unsigned long ColorIndex = 0; @@ -136,8 +124,6 @@ void GUICheckbox::BuildBitmap() { SetRect(&m_ImageRects[3], Values[0], Values[1], Values[0] + Values[2], Values[1] + Values[3]); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::Draw(GUIScreen* Screen) { if (!m_Image) { return; @@ -184,8 +170,6 @@ void GUICheckbox::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::OnMouseDown(int X, int Y, int Buttons, int Modifier) { if (Buttons & MOUSE_LEFT) { // Push the checkbox down @@ -196,8 +180,6 @@ void GUICheckbox::OnMouseDown(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); @@ -214,32 +196,22 @@ void GUICheckbox::OnMouseUp(int X, int Y, int Buttons, int Modifier) { AddEvent(GUIEvent::Notification, UnPushed, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::OnMouseEnter(int X, int Y, int Buttons, int Modifier) { m_Mouseover = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { m_Mouseover = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUICheckbox::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::Move(int X, int Y) { SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::Resize(int Width, int Height) { // Make sure the control isn't too small Width = std::max(Width, m_MinWidth); @@ -250,14 +222,10 @@ void GUICheckbox::Resize(int Width, int Height) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::StoreProperties() { if (m_Check == Unchecked) { m_Properties.AddVariable("Checked", "Unchecked"); @@ -269,32 +237,22 @@ void GUICheckbox::StoreProperties() { m_Properties.AddVariable("Text", m_Text); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::SetText(const std::string& Text) { m_Text = Text; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUICheckbox::GetText() const { return m_Text; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::SetCheck(int Check) { m_Check = Check; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUICheckbox::GetCheck() const { return m_Check; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICheckbox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUICheckbox.h b/Source/GUI/GUICheckbox.h index 086f2d1588..ce109a1e35 100644 --- a/Source/GUI/GUICheckbox.h +++ b/Source/GUI/GUICheckbox.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A checkbox control class. - /// class GUICheckbox : public GUIControl, public GUIPanel { public: @@ -23,174 +21,84 @@ namespace RTE { Changed, } Notification; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUICheckbox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUICheckbox object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUICheckbox object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUICheckbox(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseEnter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse enters the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse enters the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseLeave - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse leaves the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse leaves the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "CHECKBOX"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the text. - // Arguments: Text. - + /// Sets the text. + /// @param Text Text. void SetText(const std::string& Text); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the text. - // Arguments: None. - + /// Gets the text. std::string GetText() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the check state. - // Arguments: Check state. - + /// Sets the check state. + /// @param Check Check state. void SetCheck(int Check); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the check state. - // Arguments: None. - + /// Gets the check state. int GetCheck() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: @@ -201,12 +109,7 @@ namespace RTE { std::string m_Text; int m_Mouseover; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the checkbox bitmap to draw. - // Arguments: None. - + /// Create the checkbox bitmap to draw. void BuildBitmap(); }; }; // namespace RTE diff --git a/Source/GUI/GUICollectionBox.cpp b/Source/GUI/GUICollectionBox.cpp index d1631aac2e..6cbb33a90c 100644 --- a/Source/GUI/GUICollectionBox.cpp +++ b/Source/GUI/GUICollectionBox.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICollectionBox::GUICollectionBox(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "COLLECTIONBOX"; @@ -18,8 +16,6 @@ GUICollectionBox::GUICollectionBox(GUIManager* Manager, GUIControlManager* Contr m_IsContainer = true; // We are a container } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -49,8 +45,6 @@ void GUICollectionBox::Create(const std::string& Name, int X, int Y, int Width, m_Height = std::max(m_Height, m_MinHeight); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -83,15 +77,11 @@ void GUICollectionBox::Create(GUIProperties* Props) { Props->GetValue("DrawColor", &m_DrawColor); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::Destroy() { delete m_Background; delete m_DrawBitmap; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -99,8 +89,6 @@ void GUICollectionBox::ChangeSkin(GUISkin* Skin) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::BuildBitmap() { // Free any old bitmap delete m_DrawBitmap; @@ -112,8 +100,6 @@ void GUICollectionBox::BuildBitmap() { m_Skin->BuildStandardRect(m_DrawBitmap, "CollectionBox_Panel", 0, 0, m_Width, m_Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::Draw(GUIScreen* Screen) { if (m_DrawBackground) { if (m_DrawType == Color) { @@ -140,34 +126,24 @@ void GUICollectionBox::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::OnMouseDown(int X, int Y, int Buttons, int Modifier) { CaptureMouse(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); AddEvent(GUIEvent::Notification, Clicked, Buttons); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::OnMouseMove(int X, int Y, int Buttons, int Modifier) { AddEvent(GUIEvent::Notification, MouseMove, Buttons); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUICollectionBox::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::Move(int X, int Y) { int DX = X - m_X; int DY = Y - m_Y; @@ -189,8 +165,6 @@ void GUICollectionBox::Move(int X, int Y) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::Resize(int Width, int Height) { int OldWidth = m_Width; int OldHeight = m_Height; @@ -242,14 +216,10 @@ void GUICollectionBox::Resize(int Width, int Height) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::SetDrawImage(GUIBitmap* Bitmap) { // Free any old bitmap delete m_DrawBitmap; @@ -257,34 +227,24 @@ void GUICollectionBox::SetDrawImage(GUIBitmap* Bitmap) { m_DrawBitmap = Bitmap; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::SetDrawBackground(bool DrawBack) { m_DrawBackground = DrawBack; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::SetDrawType(int Type) { m_DrawType = Type; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::SetDrawColor(unsigned long Color) { m_DrawColor = Color; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::StoreProperties() { m_Properties.AddVariable("DrawBackground", m_DrawBackground); m_Properties.AddVariable("DrawType", m_DrawType == Color ? "Color" : "Image"); m_Properties.AddVariable("DrawColor", (int)m_DrawColor); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUICollectionBox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUICollectionBox.h b/Source/GUI/GUICollectionBox.h index 184711f14b..001356cd64 100644 --- a/Source/GUI/GUICollectionBox.h +++ b/Source/GUI/GUICollectionBox.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A collection box control class that contains child controls. - /// class GUICollectionBox : public GUIControl, public GUIPanel { public: @@ -22,199 +20,97 @@ namespace RTE { Panel } DrawType; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUICollectionBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUICollectionBox object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUICollectionBox object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUICollectionBox(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~GUICollectionBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up this before deletion from memory. - // Arguments: None. - + /// Destructor method used to clean up this before deletion from memory. ~GUICollectionBox() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and frees this' allocated data - // Arguments: None. - + /// Destroys and frees this' allocated data void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseMove - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse moves (over the panel, or when captured). - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse moves (over the panel, or when captured). + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "COLLECTIONBOX"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDrawImage - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the drawing image bitmap to draw - // Arguments: Bitmap, ownership IS transferred! - + /// Sets the drawing image bitmap to draw + /// @param Bitmap Bitmap, ownership IS transferred! void SetDrawImage(GUIBitmap* Bitmap); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDrawImage - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the drawing image bitmap that is being drawn - // Arguments: Bitmap, ownership IS NOT transferred! - + /// Gets the drawing image bitmap that is being drawn + /// @param Bitmap, ownership IS NOT transferred! GUIBitmap* GetDrawImage() { return m_DrawBitmap; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDrawBackground - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether to draw the background. - // Arguments: Draw. - + /// Sets whether to draw the background. + /// @param DrawBack Draw. void SetDrawBackground(bool DrawBack); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDrawType - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the drawing type. - // Arguments: Type. - + /// Sets the drawing type. + /// @param Type Type. void SetDrawType(int Type); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDrawType - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current drawing type. - // Arguments: None. - // Returns: Type. - + /// Gets the current drawing type. + /// @return Type. int GetDrawType() const { return m_DrawType; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDrawColor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the drawing color. - // Arguments: Color. - + /// Sets the drawing color. + /// @param Color Color. void SetDrawColor(unsigned long Color); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDrawColor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the drawing color. - // Returns: Color. - + /// Gets the drawing color. + /// @return Color. unsigned long GetDrawColor() const { return m_DrawColor; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: @@ -225,12 +121,7 @@ namespace RTE { unsigned long m_DrawColor; GUIBitmap* m_DrawBitmap; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the button bitmap to draw. - // Arguments: None. - + /// Create the button bitmap to draw. void BuildBitmap(); }; }; // namespace RTE diff --git a/Source/GUI/GUIComboBox.cpp b/Source/GUI/GUIComboBox.cpp index 02ed8f2555..4aafb65121 100644 --- a/Source/GUI/GUIComboBox.cpp +++ b/Source/GUI/GUIComboBox.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIComboBox::GUIComboBox(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "COMBOBOX"; @@ -25,8 +23,6 @@ GUIComboBox::GUIComboBox(GUIManager* Manager, GUIControlManager* ControlManager) m_Button = new GUIComboBoxButton(Manager); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -74,8 +70,6 @@ void GUIComboBox::Create(const std::string& Name, int X, int Y, int Width, int H GUIPanel::AddChild(m_Button); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -125,8 +119,6 @@ void GUIComboBox::Create(GUIProperties* Props) { m_TextPanel->SetLocked((m_DropDownStyle == DropDownList)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::Destroy() { // Free the panels if (m_ListPanel) { @@ -150,8 +142,6 @@ void GUIComboBox::Destroy() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::Activate() { m_TextPanel->SetPositionAbs(m_X, m_Y); @@ -165,8 +155,6 @@ void GUIComboBox::Activate() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -193,8 +181,6 @@ void GUIComboBox::ChangeSkin(GUISkin* Skin) { m_Button->ChangeSkin(Skin); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::Draw(GUIScreen* Screen) { // Draw the background m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, nullptr); @@ -207,14 +193,10 @@ void GUIComboBox::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIComboBox::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { assert(Source); @@ -316,41 +298,29 @@ void GUIComboBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::BeginUpdate() { m_ListPanel->BeginUpdate(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::EndUpdate() { m_ListPanel->EndUpdate(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::AddItem(const std::string& Name, const std::string& ExtraText, GUIBitmap* pBitmap, const Entity* pEntity) { m_ListPanel->AddItem(Name, ExtraText, pBitmap, pEntity); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::ClearList() { m_TextPanel->SetText(""); m_ListPanel->ClearList(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::Move(int X, int Y) { GUIPanel::SetPositionAbs(X, Y); m_ListPanel->SetPositionAbs(m_X, m_Y + m_Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::Resize(int Width, int Height) { // Make sure the textbox isn't too small Width = std::max(Width, m_MinWidth); @@ -371,14 +341,10 @@ void GUIComboBox::Resize(int Width, int Height) { ChangeSkin(m_Skin); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::DeleteItem(int Index) { m_ListPanel->DeleteItem(Index); @@ -396,20 +362,14 @@ void GUIComboBox::DeleteItem(int Index) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIComboBox::GetCount() { return m_ListPanel->GetItemList()->size(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIComboBox::GetSelectedIndex() { return m_ListPanel->GetSelectedIndex(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::SetSelectedIndex(int Index) { m_ListPanel->SetSelectedIndex(Index); m_OldSelection = Index; @@ -420,8 +380,6 @@ void GUIComboBox::SetSelectedIndex(int Index) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIComboBox::RollbackSelection() { // Restore the previous selection if (m_OldSelection >= 0 && m_OldSelection < m_ListPanel->GetItemList()->size() && m_OldSelection != m_ListPanel->GetSelectedIndex()) { @@ -436,14 +394,10 @@ bool GUIComboBox::RollbackSelection() { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIListPanel::Item* GUIComboBox::GetItem(int Index) { return m_ListPanel->GetItem(Index); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::SetDropHeight(int Drop) { m_DropHeight = Drop; m_DropHeight = std::max(m_DropHeight, 20); @@ -452,8 +406,6 @@ void GUIComboBox::SetDropHeight(int Drop) { m_ListPanel->SetSize(m_Width, m_DropHeight); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::StoreProperties() { m_Properties.AddVariable("DropHeight", m_DropHeight); if (m_DropDownStyle == DropDownList) { @@ -463,8 +415,6 @@ void GUIComboBox::StoreProperties() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::SetDropDownStyle(int Style) { if (Style == DropDown || Style == DropDownList) { m_DropDownStyle = Style; @@ -473,14 +423,10 @@ void GUIComboBox::SetDropDownStyle(int Style) { m_TextPanel->SetLocked(m_DropDownStyle == DropDownList); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIComboBox::GetDropDownStyle() const { return m_DropDownStyle; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::SetVisible(bool Visible) { _SetVisible(Visible); if (!Visible && m_ListPanel) { @@ -488,14 +434,10 @@ void GUIComboBox::SetVisible(bool Visible) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIComboBox::GetVisible() { return _GetVisible(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::SetEnabled(bool Enabled) { _SetEnabled(Enabled); if (m_ListPanel) { @@ -503,14 +445,10 @@ void GUIComboBox::SetEnabled(bool Enabled) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIComboBox::GetEnabled() { return _GetEnabled(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIComboBox::GetText() { if (m_DropDownStyle != DropDown) { return ""; @@ -521,16 +459,12 @@ std::string GUIComboBox::GetText() { return ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::SetText(const std::string& Text) { if (m_DropDownStyle == DropDown && m_TextPanel) { m_TextPanel->SetText(Text); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); @@ -550,16 +484,12 @@ void GUIComboBox::ApplyProperties(GUIProperties* Props) { ChangeSkin(m_Skin); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIComboBoxButton::GUIComboBoxButton(GUIManager* Manager) : GUIPanel(Manager) { m_DrawBitmap = nullptr; m_Pushed = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBoxButton::ChangeSkin(GUISkin* Skin) { // Free any old bitmap if (m_DrawBitmap) { @@ -599,8 +529,6 @@ void GUIComboBoxButton::ChangeSkin(GUISkin* Skin) { Arrow->DrawTrans(m_DrawBitmap, (m_Width / 2) - (Values[2] / 2) + 1, m_Height + (m_Height / 2) - (Values[3] / 2) + 1, &Rect); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBoxButton::Draw(GUIScreen* Screen) { GUIRect Rect; SetRect(&Rect, 0, m_Pushed ? m_Height : 0, m_Width, m_Pushed ? m_Height * 2 : m_Height); @@ -608,8 +536,6 @@ void GUIComboBoxButton::Draw(GUIScreen* Screen) { m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, &Rect); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBoxButton::Create(int X, int Y, int Width, int Height) { m_X = X; m_Y = Y; @@ -617,8 +543,6 @@ void GUIComboBoxButton::Create(int X, int Y, int Width, int Height) { m_Height = Height; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBoxButton::OnMouseDown(int X, int Y, int Buttons, int Modifier) { if (Buttons & MOUSE_LEFT) { m_Pushed = true; @@ -626,20 +550,14 @@ void GUIComboBoxButton::OnMouseDown(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBoxButton::OnMouseUp(int X, int Y, int Buttons, int Modifier) { m_Pushed = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBoxButton::SetPushed(bool Pushed) { m_Pushed = Pushed; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIComboBoxButton::Destroy() { // Free the drawing bitmap if (m_DrawBitmap) { diff --git a/Source/GUI/GUIComboBox.h b/Source/GUI/GUIComboBox.h index 9b8dba0871..30b9ca71aa 100644 --- a/Source/GUI/GUIComboBox.h +++ b/Source/GUI/GUIComboBox.h @@ -8,14 +8,10 @@ namespace RTE { class GUIComboBoxButton; - /// /// A ComboBox control class. - /// class GUIComboBox : public GUIControl, public GUIPanel { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Notifications enum { @@ -29,324 +25,151 @@ namespace RTE { DropDownList, } DropDownStyles; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIComboBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIComboBox object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUIComboBox object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUIComboBox(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Activate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control is activated and ready for use. - // Arguments: None. - + /// Called when the control is activated and ready for use. void Activate() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetListPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the ListPanel component of the control. - // Arguments: None. - // Returns: The ListPanel component of this ComboBox. - + /// Returns the ListPanel component of the control. + /// @return The ListPanel component of this ComboBox. GUIListPanel* GetListPanel() { return m_ListPanel; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "COMBOBOX"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReceiveSignal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when receiving a signal. - // Arguments: Signal source, Signal code, Signal data. - + /// Called when receiving a signal. + /// @param Source Signal source, Signal code, Signal data. void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BeginUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Locks the control from updating every time a new item is added. - // Arguments: None. - + /// Locks the control from updating every time a new item is added. void BeginUpdate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EndUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: UnLocks the control from updating every time a new item is added. - // Will automatically update the control. - // Arguments: None. - + /// UnLocks the control from updating every time a new item is added. + /// Will automatically update the control. void EndUpdate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Add an item to the list. - // Arguments: Name, Extra text, bitmap to show in the list, extra entity data - + /// Add an item to the list. + /// @param Name Name, Extra text, bitmap to show in the list, extra entity data void AddItem(const std::string& Name, const std::string& ExtraText = "", GUIBitmap* pBitmap = nullptr, const Entity* pEntity = nullptr); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DeleteItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Delete an item from the list. - // Arguments: Item Index. - + /// Delete an item from the list. + /// @param Index Item Index. void DeleteItem(int Index); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the list. - // Arguments: None. - + /// Clears the list. void ClearList(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get the item count. - // Arguments: None. - + /// Get the item count. int GetCount(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSelectedIndex - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get the index of the selected item. - // Arguments: None. - + /// Get the index of the selected item. int GetSelectedIndex(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOldSelectionIndex - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get the index of the previously selected item before the selection is - // made. - // Arguments: None. - + /// Get the index of the previously selected item before the selection is + /// made. int GetOldSelectionIndex() const { return m_OldSelection; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSelectedIndex - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the index of the selected item. - // Arguments: None. - + /// Sets the index of the selected item. void SetSelectedIndex(int Index); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RollbackSelection - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Rolls back the selection to the previous selected item. - // Arguments: None. - // Returns: Whether the rollback worked and was performed. - + /// Rolls back the selection to the previous selected item. + /// @return Whether the rollback worked and was performed. bool RollbackSelection(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the Item structure at the index. - // Arguments: Index. - // Returns: Pointer to the item structure. 0 if the index was invalid. - + /// Returns the Item structure at the index. + /// @param Index Index. + /// @return Pointer to the item structure. 0 if the index was invalid. GUIListPanel::Item* GetItem(int Index); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSelectedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the Item structure at the currently selected index. - // Arguments: Index. - // Returns: Pointer to the item structure. 0 if nothing valid is selected. - + /// Returns the Item structure at the currently selected index. + /// @param GetItem(GetSelectedIndex() Index. + /// @return Pointer to the item structure. 0 if nothing valid is selected. GUIListPanel::Item* GetSelectedItem() { return GetItem(GetSelectedIndex()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDropHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the drop height of the list. - // Arguments: Height. - + /// Sets the drop height of the list. + /// @param Drop Height. void SetDropHeight(int Drop); - /// /// Gets the drop height of the list. - /// - /// The drop height of the list. + /// @return The drop height of the list. int GetDropHeight() const { return m_DropHeight; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDropDownStyle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the drop down style of the combo box. - // Arguments: Style. - + /// Sets the drop down style of the combo box. + /// @param Style Style. void SetDropDownStyle(int Style); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDropDownStyle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the drop down style of the combo box. - // Arguments: None. - + /// Gets the drop down style of the combo box. int GetDropDownStyle() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the visibility of the control. - // Arguments: Visible. - + /// Sets the visibility of the control. + /// @param Visible Visible. void SetVisible(bool Visible) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the visibility of the control. - // Arguments: None. - + /// Gets the visibility of the control. bool GetVisible() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the enabled state of the control. - // Arguments: Enabled. - + /// Sets the enabled state of the control. + /// @param Enabled Enabled. void SetEnabled(bool Enabled) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the enabled state of the control. - // Arguments: None. - + /// Gets the enabled state of the control. bool GetEnabled() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets text (only if style is DropDown). - // Arguments: None. - // Returns: Text. Returns empty string is style is not DropDown. - + /// Gets text (only if style is DropDown). + /// @return Text. Returns empty string is style is not DropDown. std::string GetText(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets text (only if style is DropDown). - // Arguments: Text. - + /// Sets text (only if style is DropDown). + /// @param Text Text. void SetText(const std::string& Text); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsDropped - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether the list is currently dropped down or not. - // Arguments: None. - // Returns: Whether this is currently dropped down and showing the list. - + /// Shows whether the list is currently dropped down or not. + /// @return Whether this is currently dropped down and showing the list. bool IsDropped() { return m_ListPanel->_GetVisible(); } private: @@ -362,9 +185,7 @@ namespace RTE { GUIComboBoxButton* m_Button; }; - /// /// A ComboBoxButton control class. - /// class GUIComboBoxButton : public GUIPanel { public: @@ -373,69 +194,36 @@ namespace RTE { Clicked } Signals; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIComboBoxButton - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIComboBoxButton object in - // system memory. - // Arguments: GUIManager. - + /// Constructor method used to instantiate a GUIComboBoxButton object in + /// system memory. + /// @param Manager GUIManager. explicit GUIComboBoxButton(GUIManager* Manager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the panel. - // Arguments: Position, Size. - + /// Create the panel. + /// @param X Position, Size. void Create(int X, int Y, int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys the button. - // Arguments: None. - + /// Destroys the button. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPushed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the pushed state of the button. - // Arguments: Pushed. - + /// Sets the pushed state of the button. + /// @param Pushed Pushed. void SetPushed(bool Pushed); private: diff --git a/Source/GUI/GUIControl.cpp b/Source/GUI/GUIControl.cpp index 59eec68991..b9fa0cbe74 100644 --- a/Source/GUI/GUIControl.cpp +++ b/Source/GUI/GUIControl.cpp @@ -2,8 +2,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIControl::GUIControl() { m_Skin = nullptr; m_SkinPreset = 1; @@ -13,8 +11,6 @@ GUIControl::GUIControl() { m_IsContainer = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::Create(const std::string& Name, int X, int Y, int Width, int Height) { m_Properties.Clear(); m_Properties.AddVariable("Name", Name); @@ -22,8 +18,6 @@ void GUIControl::Create(const std::string& Name, int X, int Y, int Width, int He m_Properties.AddVariable("ToolTip", ""); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::Create(GUIProperties* Props) { assert(Props); @@ -35,28 +29,18 @@ void GUIControl::Create(GUIProperties* Props) { m_Properties.Update(Props); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::Destroy() {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::Activate() {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::ChangeSkin(GUISkin* Skin) { m_Skin = Skin; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::AddEvent(int Type, int Msg, int Data) { m_ControlManager->AddEvent(new GUIEvent(this, Type, Msg, Data)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIControl::GetName() { std::string Name; m_Properties.GetValue("Name", &Name); @@ -64,8 +48,6 @@ std::string GUIControl::GetName() { return Name; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIControl::GetToolTip() { std::string tip; m_Properties.GetValue("ToolTip", &tip); @@ -73,20 +55,14 @@ std::string GUIControl::GetToolTip() { return tip; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIControl::GetID() const { return m_ControlID; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIControl::GetPanel() { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::AddChild(GUIControl* Control) { assert(Control); @@ -99,14 +75,10 @@ void GUIControl::AddChild(GUIControl* Control) { m_ControlChildren.push_back(Control); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector* GUIControl::GetChildren() { return &m_ControlChildren; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIControl::Save(GUIWriter* W) { std::string OutString = ""; std::string Name; @@ -150,16 +122,10 @@ bool GUIControl::Save(GUIWriter* W) { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::Move(int X, int Y) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::Resize(int Width, int Height) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::GetControlRect(int* X, int* Y, int* Width, int* Height) { // Zero the values for controls that don't override this if (X) { @@ -176,8 +142,6 @@ void GUIControl::GetControlRect(int* X, int* Y, int* Width, int* Height) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIControl::GetAnchor() { int Anchor = 0; std::string Value[4]; @@ -210,12 +174,8 @@ int GUIControl::GetAnchor() { return Anchor; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::StoreProperties() {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::SetVisible(bool Visible) { // Default method is the grab the main panel and directly set its state. Controls that use multiple panels on the same layer will need to override this function GUIPanel* Panel = GetPanel(); @@ -224,8 +184,6 @@ void GUIControl::SetVisible(bool Visible) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIControl::GetVisible() { // See SetVisible() comment GUIPanel* Panel = GetPanel(); @@ -235,8 +193,6 @@ bool GUIControl::GetVisible() { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::SetEnabled(bool Enabled) { // See SetVisible() comment GUIPanel* Panel = GetPanel(); @@ -245,8 +201,6 @@ void GUIControl::SetEnabled(bool Enabled) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIControl::GetEnabled() { // See SetVisible() comment GUIPanel* Panel = GetPanel(); @@ -257,20 +211,14 @@ bool GUIControl::GetEnabled() { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIControl* GUIControl::GetParent() { return m_ControlParent; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIProperties* GUIControl::GetProperties() { return &m_Properties; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::ApplyProperties(GUIProperties* Props) { assert(Props); @@ -305,14 +253,10 @@ void GUIControl::ApplyProperties(GUIProperties* Props) { Resize(Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIControl::IsContainer() { return m_IsContainer; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::RemoveChild(const std::string Name) { // Note: We do NOT free the children because they are still linked in through their panels. This merely removes the control from the list. // This will cause a small memory leak, but this is only designed for the GUI Editor and is a bit of a hack. @@ -327,8 +271,6 @@ void GUIControl::RemoveChild(const std::string Name) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControl::RemoveChildren() { // Note: We do NOT free the children because they are still linked in through their panels. This merely removes the control from the list. // This will cause a small memory leak, but this is only designed for the GUI Editor and is a bit of a hack. diff --git a/Source/GUI/GUIControl.h b/Source/GUI/GUIControl.h index 616942429e..d08699966a 100644 --- a/Source/GUI/GUIControl.h +++ b/Source/GUI/GUIControl.h @@ -7,9 +7,7 @@ namespace RTE { class GUIControlManager; - /// /// A base class inherited by all controls. - /// class GUIControl { public: @@ -21,247 +19,111 @@ namespace RTE { Anchor_Bottom = 0x08 } Anchor; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIControl - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIControl object in - // system memory. - // Arguments: None. - + /// Constructor method used to instantiate a GUIControl object in + /// system memory. GUIControl(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position, Size - + /// Called when the control has been created. + /// @param Name Name, Position, Size virtual void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. virtual void Create(GUIProperties* Props); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. virtual void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: Activate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control is activated and ready for use. - // Arguments: None. - + /// Called when the control is activated and ready for use. virtual void Activate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. virtual void ChangeSkin(GUISkin* Skin); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddEvent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Add a new event to the queue. - // Arguments: Type, Message, Data. - + /// Add a new event to the queue. + /// @param Type Type, Message, Data. void AddEvent(int Type, int Msg, int Data); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control's name. - // Arguments: None. - + /// Gets the control's name. std::string GetName(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetToolTip - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the control's tooltip string. - // Arguments: The new ToolTip for this. - + /// Sets the control's tooltip string. + /// @param m_Properties.SetValue("ToolTip" The new ToolTip for this. void SetToolTip(const std::string& tip) { m_Properties.SetValue("ToolTip", tip); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetToolTip - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control's tooltip string. - // Arguments: None. - + /// Gets the control's tooltip string. std::string GetToolTip(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID std::string GetID() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAnchor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the anchor flags. - // Arguments: None. - + /// Returns the anchor flags. int GetAnchor(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddChild - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a child to this control - // Arguments: Control. - + /// Adds a child to this control + /// @param Control Control. void AddChild(GUIControl* Control); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetChildren - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the children lst - // Arguments: None. - + /// Gets the children lst std::vector* GetChildren(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. virtual GUIPanel* GetPanel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the control properties. - // Arguments: Writer. - // Returns: True if sucessful - + /// Saves the control properties. + /// @param W Writer. + /// @return True if sucessful bool Save(GUIWriter* W); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. virtual void StoreProperties(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. virtual void Move(int X, int Y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. virtual void Resize(int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. virtual void GetControlRect(int* X, int* Y, int* Width, int* Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: SetVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the visibility of the control. - // Arguments: Visible. - + /// Sets the visibility of the control. + /// @param Visible Visible. virtual void SetVisible(bool Visible); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: GetVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the visibility of the control. - // Arguments: None. - + /// Gets the visibility of the control. virtual bool GetVisible(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: SetEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the enabled state of the control. - // Arguments: Enabled. - + /// Sets the enabled state of the control. + /// @param Enabled Enabled. virtual void SetEnabled(bool Enabled); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: GetEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the enabled state of the control. - // Arguments: None. - + /// Gets the enabled state of the control. virtual bool GetEnabled(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetParent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the parent of this control. - // Arguments: None. - + /// Gets the parent of this control. GUIControl* GetParent(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control properties. - // Arguments: None. - + /// Gets the control properties. GUIProperties* GetProperties(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. virtual void ApplyProperties(GUIProperties* Props); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the IsContainer value. - // Arguments: None. - + /// Returns the IsContainer value. bool IsContainer(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveChild - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a child based on name. - // Arguments: Child Name. - + /// Removes a child based on name. + /// @param Name Child Name. void RemoveChild(const std::string Name); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveChildren - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes all the children. - // Arguments: None. - + /// Removes all the children. void RemoveChildren(); protected: diff --git a/Source/GUI/GUIControlFactory.cpp b/Source/GUI/GUIControlFactory.cpp index 81345990ee..7b78dea479 100644 --- a/Source/GUI/GUIControlFactory.cpp +++ b/Source/GUI/GUIControlFactory.cpp @@ -16,8 +16,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIControl* GUIControlFactory::CreateControl(GUIManager* Manager, GUIControlManager* ControlManager, const std::string& ControlName) { // Button if (ControlName.compare(GUIButton::GetControlID()) == 0) { diff --git a/Source/GUI/GUIControlFactory.h b/Source/GUI/GUIControlFactory.h index 7b88ad585d..67f87af7c4 100644 --- a/Source/GUI/GUIControlFactory.h +++ b/Source/GUI/GUIControlFactory.h @@ -3,18 +3,12 @@ namespace RTE { - /// /// A class used to create the different controls based on name. - /// class GUIControlFactory { public: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateControl - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Method used for creating controls - // Arguments: Control Type Name. - + /// Method used for creating controls + /// @param Manager Control Type Name. static GUIControl* CreateControl(GUIManager* Manager, GUIControlManager* ControlManager, const std::string& ControlName); }; }; // namespace RTE diff --git a/Source/GUI/GUIControlManager.cpp b/Source/GUI/GUIControlManager.cpp index c7e80cb319..98aebcf0d2 100644 --- a/Source/GUI/GUIControlManager.cpp +++ b/Source/GUI/GUIControlManager.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIControlManager::GUIControlManager() { m_Screen = nullptr; m_Input = nullptr; @@ -16,8 +14,6 @@ GUIControlManager::GUIControlManager() { m_CursorType = Pointer; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIControlManager::Create(GUIScreen* Screen, GUIInput* Input, const std::string& SkinDir, const std::string& SkinFilename) { assert(Screen && Input); @@ -45,8 +41,6 @@ bool GUIControlManager::Create(GUIScreen* Screen, GUIInput* Input, const std::st return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::Destroy() { // Free the skin if (m_Skin) { @@ -65,8 +59,6 @@ void GUIControlManager::Destroy() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::Clear() { std::vector::iterator it; @@ -93,8 +85,6 @@ void GUIControlManager::Clear() { m_EventQueue.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::ChangeSkin(const std::string& SkinDir, const std::string& SkinFilename) { std::vector::iterator it; @@ -109,8 +99,6 @@ void GUIControlManager::ChangeSkin(const std::string& SkinDir, const std::string } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIControl* GUIControlManager::AddControl(const std::string& Name, const std::string& Type, GUIControl* Parent, int X, int Y, int Width, int Height) { // Skip if we already have a control of this name if (GetControl(Name)) { @@ -145,8 +133,6 @@ GUIControl* GUIControlManager::AddControl(const std::string& Name, const std::st return Control; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIControl* GUIControlManager::AddControl(GUIProperties* Property) { assert(Property); @@ -195,8 +181,6 @@ GUIControl* GUIControlManager::AddControl(GUIProperties* Property) { return Control; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIControl* GUIControlManager::GetControl(const std::string& Name) { std::vector::iterator it; @@ -211,14 +195,10 @@ GUIControl* GUIControlManager::GetControl(const std::string& Name) { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector* GUIControlManager::GetControlList() { return &m_ControlList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIControl* GUIControlManager::GetControlUnderPoint(int pointX, int pointY, GUIControl* pParent, int depth) { // Default to the root object if no parent specified if (!pParent) { @@ -266,8 +246,6 @@ GUIControl* GUIControlManager::GetControlUnderPoint(int pointX, int pointY, GUIC return pParent == m_ControlList.front() ? nullptr : pParent; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::RemoveControl(const std::string& Name, bool RemoveFromParent) { // NOTE: We can't simply remove it because some controls need to remove extra panels and it's silly to add 'remove' to every control to remove their extra panels (ie. Combobox). // Signals and stuff are also linked in so we just remove the controls from the list and not from memory. @@ -294,8 +272,6 @@ void GUIControlManager::RemoveControl(const std::string& Name, bool RemoveFromPa } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::Update(bool ignoreKeyboardEvents) { // Clear the event queue m_EventQueue.clear(); @@ -304,20 +280,14 @@ void GUIControlManager::Update(bool ignoreKeyboardEvents) { m_GUIManager->Update(ignoreKeyboardEvents); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::Draw() { m_GUIManager->Draw(m_Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::Draw(GUIScreen* pScreen) { m_GUIManager->Draw(pScreen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::DrawMouse(GUIScreen* guiScreen) { int MouseX; int MouseY; @@ -343,8 +313,6 @@ void GUIControlManager::DrawMouse(GUIScreen* guiScreen) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIControlManager::GetEvent(GUIEvent* Event) { if (Event && !m_EventQueue.empty()) { @@ -363,8 +331,6 @@ bool GUIControlManager::GetEvent(GUIEvent* Event) { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::AddEvent(GUIEvent* Event) { // Add the event to the queue if (Event) { @@ -372,14 +338,10 @@ void GUIControlManager::AddEvent(GUIEvent* Event) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIControlManager::SetCursor(int CursorType) { m_CursorType = CursorType; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIControlManager::Save(const std::string& Filename) { GUIWriter W; if (W.Create(Filename) != 0) { @@ -392,8 +354,6 @@ bool GUIControlManager::Save(const std::string& Filename) { return Result; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIControlManager::Save(GUIWriter* W) { assert(W); @@ -410,8 +370,6 @@ bool GUIControlManager::Save(GUIWriter* W) { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIControlManager::Load(const std::string& Filename, bool keepOld) { GUIReader reader; const std::string pathFile = g_PresetMan.GetFullModulePath(Filename); diff --git a/Source/GUI/GUIControlManager.h b/Source/GUI/GUIControlManager.h index 4bd4224b20..f47532b0ff 100644 --- a/Source/GUI/GUIControlManager.h +++ b/Source/GUI/GUIControlManager.h @@ -6,9 +6,7 @@ namespace RTE { - /// /// A class used to manage the GUI as a whole and provide the interface between the GUI and the rest of the system. - /// class GUIControlManager { friend class GUIControl; @@ -20,232 +18,124 @@ namespace RTE { HorSize } CursorType; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIControlmanager - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIControlManager object in - // system memory. - // Arguments: None. - + /// Constructor method used to instantiate a GUIControlManager object in + /// system memory. GUIControlManager(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: GUIControlmanager - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a GUIControlManager object in - // system memory. - // Arguments: None. - + /// Destructor method used to clean up a GUIControlManager object in + /// system memory. ~GUIControlManager() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates the data for the control manager - // Arguments: Screen and Input Interfaces, Skin directory - + /// Creates the data for the control manager + /// @param Screen Screen and Input Interfaces, Skin directory bool Create(GUIScreen* Screen, GUIInput* Input, const std::string& SkinDir, const std::string& SkinFilename = "skin.ini"); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Frees all the allocated resources. - // Arguments: None. - + /// Frees all the allocated resources. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the controls. - // Arguments: None. - + /// Clears all the controls. void Clear(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes the skin of the controls. - // Arguments: Skin directory. - + /// Changes the skin of the controls. + /// @param SkinDir Skin directory. void ChangeSkin(const std::string& SkinDir, const std::string& SkinFilename = "skin.ini"); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the Skin object currently in use. - // Arguments: None. - // Returns: A pointer to the currently used skin. Please don't mess it up. - + /// Gets the Skin object currently in use. + /// @return A pointer to the currently used skin. Please don't mess it up. GUISkin* GetSkin() { return m_Skin; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the GUI every frame - // Arguments: Whether keyboard events should be ignored or not. Used to avoid conflicts when custom keyboard handling for GUI elements is preset. - + /// Updates the GUI every frame + /// @param ignoreKeyboardEvents Whether keyboard events should be ignored or not. Used to avoid conflicts when custom keyboard handling for GUI elements is preset. (default: false) void Update(bool ignoreKeyboardEvents = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the GUI to the back buffer. - // Arguments: None. - + /// Draws the GUI to the back buffer. void Draw(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the GUI to the back buffer. - // Arguments: The GUIScreen to draw to, overriding the one passed in on construction - + /// Draws the GUI to the back buffer. + /// @param pScreen The GUIScreen to draw to, overriding the one passed in on construction void Draw(GUIScreen* pScreen); - /// /// Draws the mouse to the backbuffer. - /// - /// The GUIScreen to draw to, overriding the one passed in on construction. + /// @param pScreen The GUIScreen to draw to, overriding the one passed in on construction. void DrawMouse(GUIScreen* guiScreen = nullptr); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableMouse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Enables and disables the mouse completely for this. - // Arguments: Enable? - + /// Enables and disables the mouse completely for this. + /// @param enable Enable? (default: true) { m_GUIManager->EnableMouse(enable) void EnableMouse(bool enable = true) { m_GUIManager->EnableMouse(enable); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPosOnScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the absolute position of this entire GUI on the screen. This is - // useful if the UI's are being drawn in a different area of the screen - // than the top left corner. This will adjust the mouse input to match - // the offset screen location. - // Arguments: The position. - + /// Sets the absolute position of this entire GUI on the screen. This is + /// useful if the UI's are being drawn in a different area of the screen + /// than the top left corner. This will adjust the mouse input to match + /// the offset screen location. + /// @param screenPosX The position. void SetPosOnScreen(int screenPosX, int screenPosY) { m_Input->SetMouseOffset(-screenPosX, -screenPosY); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetManager - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control manager - // Arguments: Name. - // Returns: The manager, ownership is NOT transferred! - + /// Gets the control manager + /// @param Name. + /// @return The manager, ownership is NOT transferred! GUIManager* GetManager() { return m_GUIManager; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddControl - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Manually creates a control. - // Arguments: Name, Type, Position, Size, Parent. - // Returns: GUIControl class created. 0 if not created. - + /// Manually creates a control. + /// @param Name Name, Type, Position, Size, Parent. + /// @return GUIControl class created. 0 if not created. GUIControl* AddControl(const std::string& Name, const std::string& Type, GUIControl* Parent, int X, int Y, int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddControl - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Manually creates a control. - // Arguments: Properties. - // Returns: GUIControl class created. 0 if not created. - + /// Manually creates a control. + /// @param Property Properties. + /// @return GUIControl class created. 0 if not created. GUIControl* AddControl(GUIProperties* Property); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControl - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a control - // Arguments: Name. - // Returns: GUIControl class, or 0 if not found. - + /// Gets a control + /// @param Name Name. + /// @return GUIControl class, or 0 if not found. GUIControl* GetControl(const std::string& Name); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control list - // Arguments: None. - // Returns: vector Pointer. - + /// Gets the control list + /// @return vector Pointer. std::vector* GetControlList(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlUnderPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if a control is under a specific point - // Arguments: The absolute point coordinates to check under. - // Parent to check under. Pass null to default to the root control. - // How many levels of children under the parent to look at. If negative, - // goes as far as it can. - // Returns: GUIControl. NULL if no control under the point - + /// Checks if a control is under a specific point + /// @param pointX The absolute point coordinates to check under. + /// @param pointY Parent to check under. Pass null to default to the root control. + /// @param pParent How many levels of children under the parent to look at. If negative, (default: nullptr) + /// goes as far as it can. + /// @param depth Returns: GUIControl. NULL if no control under the point (default: -1) GUIControl* GetControlUnderPoint(int pointX, int pointY, GUIControl* pParent = nullptr, int depth = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveControl - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a control by name - // Arguments: Name, RemoveFromParent. - // Returns: None. - + /// Removes a control by name + /// @param Name Name, RemoveFromParent. void RemoveControl(const std::string& Name, bool RemoveFromParent); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEvent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets an event from the queue. - // Arguments: Pointer to variable receiving the Event. - // Returns: Returns true when an event was grabbed. - // Returns false when there was no more events in the queue - // OR the Event pointer is 0. - + /// Gets an event from the queue. + /// @param Event Pointer to variable receiving the Event. + /// @return Returns true when an event was grabbed. + /// Returns false when there was no more events in the queue + /// OR the Event pointer is 0. bool GetEvent(GUIEvent* Event); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCursor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the cursor type. - // Arguments: Cursor type. - + /// Sets the cursor type. + /// @param CursorType Cursor type. void SetCursor(int CursorType); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the layout to a file. - // Arguments: Filename. - // Returns: True if successful. - + /// Saves the layout to a file. + /// @param Filename Filename. + /// @return True if successful. bool Save(const std::string& Filename); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the layout to a Writer class. - // Arguments: Writer class. - // Returns: True if successful. - + /// Saves the layout to a Writer class. + /// @param W Writer class. + /// @return True if successful. bool Save(GUIWriter* W); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Load - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads the layout from a file. - // Arguments: Filename. - // Whether to NOT clear out the manager, but just add the controls loaded - // to the existing layout. - // Returns: True if successful. - + /// Loads the layout from a file. + /// @param Filename Filename. + /// @param keepOld Whether to NOT clear out the manager, but just add the controls loaded (default: false) + /// to the existing layout. + /// @return True if successful. bool Load(const std::string& Filename, bool keepOld = false); - /// /// Gets the GUIScreen that this GUIControlManager is drawing itself to. - /// - /// Pointer to the GUIScreen that this GUIControlManager is drawing itself to. + /// @return Pointer to the GUIScreen that this GUIControlManager is drawing itself to. GUIScreen* GetScreen() const { return m_Screen; } private: @@ -259,12 +149,8 @@ namespace RTE { int m_CursorType; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddEvent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Add a new event to the queue. - // Arguments: Event point. - + /// Add a new event to the queue. + /// @param Event Event point. void AddEvent(GUIEvent* Event); }; }; // namespace RTE diff --git a/Source/GUI/GUIEvent.cpp b/Source/GUI/GUIEvent.cpp index 1a3904694c..4e3162875c 100644 --- a/Source/GUI/GUIEvent.cpp +++ b/Source/GUI/GUIEvent.cpp @@ -2,8 +2,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIEvent::GUIEvent() { m_Control = nullptr; m_Type = 0; @@ -11,8 +9,6 @@ GUIEvent::GUIEvent() { m_Data = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIEvent::GUIEvent(GUIControl* Control, int Type, int Msg, int Data) { assert(Control); m_Control = Control; @@ -21,26 +17,18 @@ GUIEvent::GUIEvent(GUIControl* Control, int Type, int Msg, int Data) { m_Data = Data; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIEvent::GetType() const { return m_Type; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIControl* GUIEvent::GetControl() { return m_Control; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIEvent::GetMsg() const { return m_Msg; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIEvent::GetData() const { return m_Data; } diff --git a/Source/GUI/GUIEvent.h b/Source/GUI/GUIEvent.h index 9024698407..4ec8ba5674 100644 --- a/Source/GUI/GUIEvent.h +++ b/Source/GUI/GUIEvent.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A class to hold event information. - /// class GUIEvent { public: @@ -15,54 +13,25 @@ namespace RTE { Notification } EventType; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIEvent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIEvent object in system - // memory. - // Arguments: None. - + /// Constructor method used to instantiate a GUIEvent object in system + /// memory. GUIEvent(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIEvent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIEvent object in system - // memory. - // Arguments: Control, Event type, Msg, Data. - + /// Constructor method used to instantiate a GUIEvent object in system + /// memory. + /// @param Control Control, Event type, Msg, Data. GUIEvent(GUIControl* Control, int Type, int Msg, int Data); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetType - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the event type - // Arguments: None. - + /// Gets the event type int GetType() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMsg - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the msg. - // Arguments: None. - + /// Gets the msg. int GetMsg() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the data. - // Arguments: None. - + /// Gets the data. int GetData() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControl - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the event control. - // Arguments: None. - + /// Gets the event control. GUIControl* GetControl(); private: diff --git a/Source/GUI/GUIFont.cpp b/Source/GUI/GUIFont.cpp index f30e0de072..6a77a70d56 100644 --- a/Source/GUI/GUIFont.cpp +++ b/Source/GUI/GUIFont.cpp @@ -2,8 +2,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIFont::GUIFont(const std::string& Name) { m_Screen = nullptr; m_Font = nullptr; @@ -20,8 +18,6 @@ GUIFont::GUIFont(const std::string& Name) { m_CharIndexCap = 256; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIFont::Load(GUIScreen* Screen, const std::string& Filename) { assert(Screen); @@ -107,8 +103,6 @@ bool GUIFont::Load(GUIScreen* Screen, const std::string& Filename) { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIFont::Draw(GUIBitmap* Bitmap, int X, int Y, const std::string& Text, unsigned long Shadow) { unsigned char c; GUIRect Rect; @@ -163,8 +157,6 @@ void GUIFont::Draw(GUIBitmap* Bitmap, int X, int Y, const std::string& Text, uns } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIFont::DrawAligned(GUIBitmap* Bitmap, int X, int Y, const std::string& Text, int HAlign, int VAlign, int MaxWidth, unsigned long Shadow) { std::string TextLine = Text; int lineStartPos = 0; @@ -245,8 +237,6 @@ void GUIFont::DrawAligned(GUIBitmap* Bitmap, int X, int Y, const std::string& Te } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIFont::SetColor(unsigned long Color) { // Only check the change if the color is different if (Color != m_CurrentColor) { @@ -262,8 +252,6 @@ void GUIFont::SetColor(unsigned long Color) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIFont::CalculateWidth(const std::string& Text) { unsigned char c; int Width = 0; @@ -300,8 +288,6 @@ int GUIFont::CalculateWidth(const std::string& Text) { return WidestLine; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIFont::CalculateWidth(const char Character) { if (Character >= 32 && Character < m_CharIndexCap) { return m_Characters[Character].m_Width + m_Kerning; @@ -309,8 +295,6 @@ int GUIFont::CalculateWidth(const char Character) { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIFont::CalculateHeight(const std::string& Text, int MaxWidth) { if (Text.empty()) { return 0; @@ -353,8 +337,6 @@ int GUIFont::CalculateHeight(const std::string& Text, int MaxWidth) { return Height; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIFont::CacheColor(unsigned long Color) { // Make sure we haven't already cached this color and it isn't a 0 color if (GetFontColor(Color) != nullptr || !Color) { @@ -391,8 +373,6 @@ void GUIFont::CacheColor(unsigned long Color) { m_ColorCache.push_back(FC); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIFont::FontColor* GUIFont::GetFontColor(unsigned long Color) { std::vector::iterator it; FontColor* F = nullptr; @@ -408,26 +388,18 @@ GUIFont::FontColor* GUIFont::GetFontColor(unsigned long Color) { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIFont::GetFontHeight() const { return m_FontHeight; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIFont::GetName() const { return m_Name; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIFont::GetKerning() const { return m_Kerning; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIFont::Destroy() { if (m_Font) { m_Font->Destroy(); diff --git a/Source/GUI/GUIFont.h b/Source/GUI/GUIFont.h index 94383c0fde..05af264b19 100644 --- a/Source/GUI/GUIFont.h +++ b/Source/GUI/GUIFont.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A class to handle the drawing of text. - /// class GUIFont { public: @@ -36,127 +34,61 @@ namespace RTE { GUIBitmap* m_Bitmap; } FontColor; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIFont - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIFont object in system - // memory. - // Arguments: None. - + /// Constructor method used to instantiate a GUIFont object in system + /// memory. explicit GUIFont(const std::string& Name); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Load - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads the font from an image file. - // Arguments: Screen class, Filename of image. - + /// Loads the font from an image file. + /// @param Screen Screen class, Filename of image. bool Load(GUIScreen* Screen, const std::string& Filename); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CacheColor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Pre-Calculates the font using a specific color. - // Arguments: Color. - + /// Pre-Calculates the font using a specific color. + /// @param Color Color. void CacheColor(unsigned long Color); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFontColor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Finds a font color structure from the cache. - // Arguments: Color. - + /// Finds a font color structure from the cache. + /// @param Color Color. FontColor* GetFontColor(unsigned long Color); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws text to a bitmap. - // Arguments: Bitmap, Position, Text, Color, Drop-shadow, 0 = none. - + /// Draws text to a bitmap. + /// @param Bitmap Bitmap, Position, Text, Color, Drop-shadow, 0 = none. void Draw(GUIBitmap* Bitmap, int X, int Y, const std::string& Text, unsigned long Shadow = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawAligned - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws text to a bitmap aligned. - // Arguments: Bitmap, Position, Text. - + /// Draws text to a bitmap aligned. + /// @param Bitmap Bitmap, Position, Text. void DrawAligned(GUIBitmap* Bitmap, int X, int Y, const std::string& Text, int HAlign, int VAlign = Top, int maxWidth = 0, unsigned long Shadow = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetColor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current color. - // Arguments: Color. - + /// Sets the current color. + /// @param Color Color. void SetColor(unsigned long Color); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculateWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the width of a piece of text. - // Arguments: Text. - + /// Calculates the width of a piece of text. + /// @param Text Text. int CalculateWidth(const std::string& Text); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculateWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the width of a piece of text. - // Arguments: Character. - + /// Calculates the width of a piece of text. + /// @param Character Character. int CalculateWidth(const char Character); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculateHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the height of a piece of text, if it's wrapped within a - // max width. - // Arguments: Text, and the max width. If 0, no wrapping is done. - + /// Calculates the height of a piece of text, if it's wrapped within a + /// max width. + /// @param Text Text, and the max width. If 0, no wrapping is done. int CalculateHeight(const std::string& Text, int MaxWidth = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFontHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the font height. - // Arguments: None. - + /// Gets the font height. int GetFontHeight() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the name of the font - // Arguments: None. - + /// Gets the name of the font std::string GetName() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys the font data - // Arguments: None. - + /// Destroys the font data void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetKerning - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get the character kerning (spacing) - // Arguments: None. - + /// Get the character kerning (spacing) int GetKerning() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetKerning - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Set the character kerning (spacing), in pixels. 1 = one empty pixel - // between chars, 0 = chars are touching. - // Arguments: None. - + /// Set the character kerning (spacing), in pixels. 1 = one empty pixel + /// between chars, 0 = chars are touching. void SetKerning(int newKerning = 1) { m_Kerning = newKerning; } private: diff --git a/Source/GUI/GUIInput.cpp b/Source/GUI/GUIInput.cpp index bac777c994..0326cc1df0 100644 --- a/Source/GUI/GUIInput.cpp +++ b/Source/GUI/GUIInput.cpp @@ -2,8 +2,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIInput::m_OverrideInput = false; int GUIInput::m_NetworkMouseButtonsEvents[4][3] = {{-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}}; @@ -13,8 +11,6 @@ int GUIInput::m_PrevNetworkMouseButtonsStates[4][3] = {{-1, -1, -1}, {-1, -1, -1 int GUIInput::m_NetworkMouseX[4] = {0, 0, 0, 0}; int GUIInput::m_NetworkMouseY[4] = {0, 0, 0, 0}; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIInput::GUIInput(int whichPlayer, bool keyJoyMouseCursor) { // Clear all the states memset(m_KeyboardBuffer, 0, sizeof(unsigned char) * KEYBOARD_BUFFER_SIZE); @@ -44,32 +40,22 @@ GUIInput::GUIInput(int whichPlayer, bool keyJoyMouseCursor) { m_MouseWheelChange = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInput::Destroy() {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInput::GetKeyboard(unsigned char* Buffer) const { if (Buffer) { memcpy(Buffer, m_KeyboardBuffer, sizeof(unsigned char) * KEYBOARD_BUFFER_SIZE); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned char GUIInput::GetAsciiState(unsigned char ascii) const { return m_KeyboardBuffer[ascii]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned char GUIInput::GetScanCodeState(unsigned char scancode) const { return m_ScanCodeState[scancode]; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInput::GetMouseButtons(int* Buttons, int* States) const { if (!m_OverrideInput) { if (Buttons) { @@ -97,8 +83,6 @@ void GUIInput::GetMouseButtons(int* Buttons, int* States) const { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInput::SetNetworkMouseButton(int whichPlayer, int state1, int state2, int state3) { if (whichPlayer >= 0 && whichPlayer < 4) { m_OverrideInput = true; @@ -113,8 +97,6 @@ void GUIInput::SetNetworkMouseButton(int whichPlayer, int state1, int state2, in } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInput::GetMousePosition(int* X, int* Y) const { if (m_OverrideInput) { if (m_Player >= 0 && m_Player < 4) { @@ -142,8 +124,6 @@ void GUIInput::GetMousePosition(int* X, int* Y) const { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInput::SetNetworkMouseMovement(int whichPlayer, int x, int y) { if (whichPlayer >= 0 && whichPlayer < 4) { m_OverrideInput = true; @@ -152,14 +132,10 @@ void GUIInput::SetNetworkMouseMovement(int whichPlayer, int x, int y) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInput::Update() { // Do nothing } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIInput::GetModifier() const { return m_Modifier; } diff --git a/Source/GUI/GUIInput.h b/Source/GUI/GUIInput.h index 9327132844..f74e6d15a8 100644 --- a/Source/GUI/GUIInput.h +++ b/Source/GUI/GUIInput.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// An interface class inherited by the different types of input methods. - /// class GUIInput { public: @@ -51,68 +49,42 @@ namespace RTE { Key_PageDown = 0x0000009A } Keys; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIInput - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIInput object in system - // memory. - // Arguments: Whether the keyboard and joysticks also can control the mouse cursor. - + /// Constructor method used to instantiate a GUIInput object in system + /// memory. + /// @param whichPlayer Whether the keyboard and joysticks also can control the mouse cursor. GUIInput(int whichPlayer, bool keyJoyMouseCursor = false); virtual ~GUIInput() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroy the screen - // Arguments: None. - + /// Destroy the screen virtual void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMouseOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the offset for the mouse input to be adjusted by. This should - // be used when the GUI is being drawn somewhere else on the screen than - // the upper left corner. These values should be from the GUI to the upper - // left corner. - // Arguments: The new offset. - + /// Sets the offset for the mouse input to be adjusted by. This should + /// be used when the GUI is being drawn somewhere else on the screen than + /// the upper left corner. These values should be from the GUI to the upper + /// left corner. + /// @param mouseOffsetX The new offset. void SetMouseOffset(int mouseOffsetX, int mouseOffsetY) { m_MouseOffsetX = mouseOffsetX; m_MouseOffsetY = mouseOffsetY; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMouseOffset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the offset for the mouse input to be adjusted by. This should - // These values should be from the GUI to the upper of the screen. - // left corner. - // Arguments: The new offset. - + /// Sets the offset for the mouse input to be adjusted by. This should + /// These values should be from the GUI to the upper of the screen. + /// left corner. + /// @param mouseOffsetX The new offset. void GetMouseOffset(int& mouseOffsetX, int& mouseOffsetY) const { mouseOffsetX = m_MouseOffsetX; mouseOffsetY = m_MouseOffsetY; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Input. - // Arguments: None. - + /// Updates the Input. virtual void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetKeyboard - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Copies the keyboard buffer into an array. The keyboard buffer is - // ordered by ascii code and each entry contains a GUInput::Event enum - // state. - // Arguments: Buffer array. - + /// Copies the keyboard buffer into an array. The keyboard buffer is + /// ordered by ascii code and each entry contains a GUInput::Event enum + /// state. + /// @param Buffer Buffer array. void GetKeyboard(unsigned char* Buffer) const; unsigned char GetAsciiState(unsigned char ascii) const; @@ -124,46 +96,29 @@ namespace RTE { return !m_TextInput.empty(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMouseButtons - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Copies the mouse button states into an array - // Arguments: State array. - + /// Copies the mouse button states into an array + /// @param Events State array. void GetMouseButtons(int* Events, int* States) const; static void SetNetworkMouseButton(int whichPlayer, int state1, int state2, int state3); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMousePosition - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the mouse position - // Arguments: Pointers to store the X and Y coordinates in - + /// Gets the mouse position + /// @param X Pointers to store the X and Y coordinates in void GetMousePosition(int* X, int* Y) const; static void SetNetworkMouseMovement(int whichPlayer, int x, int y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetModifier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the key modifiers. - // Arguments: None. - + /// Gets the key modifiers. int GetModifier() const; - /// /// This function returns how much the mouse scroll wheel has moved. Positive integer is scroll up, negative is scroll down. - /// - /// Mouse scroll wheel movement in integer value. + /// @return Mouse scroll wheel movement in integer value. int GetMouseWheelChange() const { return m_MouseWheelChange; } - /// /// Sets whether the keyboard and joysticks also control the mouse. - /// - /// Whether the keyboard and joysticks also control the mouse or not. + /// @param enableKeyJoyMouseCursor Whether the keyboard and joysticks also control the mouse or not. void SetKeyJoyMouseCursor(bool enableKeyJoyMouseCursor) { m_KeyJoyMouseCursor = enableKeyJoyMouseCursor; } protected: diff --git a/Source/GUI/GUIInterface.h b/Source/GUI/GUIInterface.h index 6651320f10..711f9229de 100644 --- a/Source/GUI/GUIInterface.h +++ b/Source/GUI/GUIInterface.h @@ -8,166 +8,122 @@ struct BITMAP; namespace RTE { #pragma region GUIBitmap - /// /// An interface class inherited by the different types of bitmap methods. - /// class GUIBitmap { public: #pragma region Creation - /// /// Constructor method used to instantiate a GUIBitmap object in system memory. - /// GUIBitmap() = default; #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a GUIBitmap object before deletion from system memory. - /// virtual ~GUIBitmap() = default; - /// /// Destroys and resets the GUIBitmap object. - /// virtual void Destroy() = 0; #pragma endregion #pragma region Getters and Setters - /// /// Gets the path to the data file this GUIBitmap uses. - /// - /// >Path to the data file this GUIBitmap uses. + /// @return >Path to the data file this GUIBitmap uses. virtual std::string GetDataPath() const = 0; - /// /// Gets the underlying BITMAP of this GUIBitmap. - /// - /// The underlying BITMAP of this GUIBitmap. + /// @return The underlying BITMAP of this GUIBitmap. virtual BITMAP* GetBitmap() const = 0; - /// /// Sets the underlying BITMAP for this GUIBitmap. - /// - /// A pointer to the new BITMAP for this GUIBitmap. + /// @param newBitmap A pointer to the new BITMAP for this GUIBitmap. virtual void SetBitmap(BITMAP* newBitmap) = 0; - /// /// Gets the width of the bitmap. - /// - /// The width of the bitmap. + /// @return The width of the bitmap. virtual int GetWidth() const = 0; - /// /// Gets the height of the bitmap. - /// - /// The height of the bitmap. + /// @return The height of the bitmap. virtual int GetHeight() const = 0; - /// /// Gets the number of bits per pixel color depth of the bitmap. - /// - /// The color depth of the bitmap. + /// @return The color depth of the bitmap. virtual int GetColorDepth() const = 0; - /// /// Gets the color of a pixel at a specific point on the bitmap. - /// - /// X position on bitmap. - /// Y position on bitmap. - /// The color of the pixel at the specified point. + /// @param posX X position on bitmap. + /// @param posY Y position on bitmap. + /// @return The color of the pixel at the specified point. virtual unsigned long GetPixel(int posX, int posY) const = 0; - /// /// Sets the color of a pixel at a specific point on the bitmap. - /// - /// X position on bitmap. - /// Y position on bitmap. - /// The color to set the pixel to. + /// @param posX X position on bitmap. + /// @param posY Y position on bitmap. + /// @param pixelColor The color to set the pixel to. virtual void SetPixel(int posX, int posY, unsigned long pixelColor) = 0; - /// /// Sets the color key (mask color) of the bitmap to the color of the pixel in the upper right corner of the bitmap. - /// virtual void SetColorKey() {} - /// /// Sets the color key (mask color) of the bitmap. - /// - /// Color key (mask color). + /// @param colorKey Color key (mask color). virtual void SetColorKey(unsigned long colorKey) {} #pragma endregion #pragma region Clipping - /// /// Gets the clipping rectangle of the bitmap. - /// - /// Pointer to a GUIRect to fill out. + /// @param clippingRect Pointer to a GUIRect to fill out. virtual void GetClipRect(GUIRect* clippingRect) const = 0; - /// /// Sets the clipping rectangle of the bitmap. - /// - /// Pointer to a GUIRect to use as the clipping rectangle, or nullptr for no clipping. + /// @param clippingRect Pointer to a GUIRect to use as the clipping rectangle, or nullptr for no clipping. virtual void SetClipRect(GUIRect* clippingRect) = 0; - /// /// Sets the clipping rectangle of the specified bitmap as the intersection of its current clipping rectangle and the rectangle described by the passed-in GUIRect. - /// - /// Rectangle pointer. + /// @param rect Rectangle pointer. virtual void AddClipRect(GUIRect* rect) = 0; #pragma endregion #pragma region Drawing - /// /// Draw a section of this bitmap onto another bitmap. - /// - /// Bitmap to draw onto. - /// Destination X position. - /// Destination Y position. - /// Source bitmap position and size rectangle. + /// @param destBitmap Bitmap to draw onto. + /// @param destX Destination X position. + /// @param destY Destination Y position. + /// @param srcPosAndSizeRect Source bitmap position and size rectangle. virtual void Draw(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) = 0; - /// /// Draw a section of this bitmap onto another bitmap ignoring color-keyed pixels. - /// - /// Bitmap to draw onto. - /// Destination X position. - /// Destination Y position. - /// Source bitmap position and size rectangle. + /// @param destBitmap Bitmap to draw onto. + /// @param destX Destination X position. + /// @param destY Destination Y position. + /// @param srcPosAndSizeRect Source bitmap position and size rectangle. virtual void DrawTrans(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) = 0; - /// /// Draw this bitmap scaled onto another bitmap ignoring color-keyed pixels. - /// - /// Bitmap to draw onto. - /// Destination X position. - /// Destination Y position. - /// Target width of the bitmap. - /// Target height of the bitmap. + /// @param destBitmap Bitmap to draw onto. + /// @param destX Destination X position. + /// @param destY Destination Y position. + /// @param width Target width of the bitmap. + /// @param height Target height of the bitmap. virtual void DrawTransScaled(GUIBitmap* destBitmap, int destX, int destY, int width, int height) = 0; #pragma endregion #pragma region Primitive Drawing - /// /// Draws a line on this bitmap. - /// - /// Start position on X axis. - /// Start position on Y axis. - /// End position on X axis. - /// End position on Y axis. - /// Color to draw this line with. + /// @param x1 Start position on X axis. + /// @param y1 Start position on Y axis. + /// @param x2 End position on X axis. + /// @param y2 End position on Y axis. + /// @param color Color to draw this line with. virtual void DrawLine(int x1, int y1, int x2, int y2, unsigned long color) = 0; - /// /// Draws a rectangle on this bitmap. - /// - /// Position on X axis. - /// Position on Y axis. - /// Width of rectangle. - /// Height of rectangle. - /// Color to draw this rectangle with. - /// Whether to fill the rectangle with the set color or not. + /// @param posX Position on X axis. + /// @param posY Position on Y axis. + /// @param width Width of rectangle. + /// @param height Height of rectangle. + /// @param color Color to draw this rectangle with. + /// @param filled Whether to fill the rectangle with the set color or not. virtual void DrawRectangle(int posX, int posY, int width, int height, unsigned long color, bool filled) = 0; #pragma endregion @@ -177,79 +133,59 @@ namespace RTE { #pragma endregion #pragma region GUIScreen - /// /// An interface class inherited by the different types of graphics methods. - /// class GUIScreen { public: #pragma region Creation - /// /// Constructor method used to instantiate a GUIScreen object in system memory. - /// GUIScreen() = default; - /// /// Creates a bitmap from a file. - /// - /// File name to create bitmap from. - /// Pointer to the created bitmap. + /// @param fileName File name to create bitmap from. + /// @return Pointer to the created bitmap. virtual GUIBitmap* CreateBitmap(const std::string& fileName) = 0; - /// /// Creates an empty bitmap. - /// - /// Bitmap width. - /// Bitmap height. - /// Pointer to the created bitmap. + /// @param width Bitmap width. + /// @param height Bitmap height. + /// @return Pointer to the created bitmap. virtual GUIBitmap* CreateBitmap(int width, int height) = 0; #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a GUIScreen object before deletion from system memory. - /// virtual ~GUIScreen() = default; - /// /// Destroys and resets the GUIScreen object. - /// virtual void Destroy() = 0; #pragma endregion #pragma region Getters - /// /// Gets the bitmap representing the screen. - /// - /// Pointer to the bitmap representing the screen. + /// @return Pointer to the bitmap representing the screen. virtual GUIBitmap* GetBitmap() const = 0; #pragma endregion #pragma region Pure Virtual Methods - /// /// Draws a bitmap onto the back buffer. - /// - /// The bitmap to draw to this AllegroScreen. - /// Destination X position - /// Destination Y position - /// Source bitmap position and size rectangle. + /// @param guiBitmap The bitmap to draw to this AllegroScreen. + /// @param destX Destination X position + /// @param destY Destination Y position + /// @param srcPosAndSizeRect Source bitmap position and size rectangle. virtual void DrawBitmap(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) = 0; - /// /// Draws a bitmap onto the back buffer ignoring color-keyed pixels. - /// - /// The bitmap to draw to this AllegroScreen. - /// Destination X position - /// Destination Y position - /// Source bitmap position and size rectangle. + /// @param guiBitmap The bitmap to draw to this AllegroScreen. + /// @param destX Destination X position + /// @param destY Destination Y position + /// @param srcPosAndSizeRect Source bitmap position and size rectangle. virtual void DrawBitmapTrans(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) = 0; - /// /// Converts an 8bit palette index to a valid pixel format color. - /// - /// Color value in any bit depth. Will be converted to the format specified. - /// An optional target color depth that will determine what format the color should be converted to. If this is 0, then the current video color depth will be used as target. - /// The converted color. + /// @param color Color value in any bit depth. Will be converted to the format specified. + /// @param targetColorDepth An optional target color depth that will determine what format the color should be converted to. If this is 0, then the current video color depth will be used as target. + /// @return The converted color. virtual unsigned long ConvertColor(unsigned long color, int targetColorDepth = 0) = 0; #pragma endregion diff --git a/Source/GUI/GUILabel.cpp b/Source/GUI/GUILabel.cpp index 96e9b8e6c5..cf659b413d 100644 --- a/Source/GUI/GUILabel.cpp +++ b/Source/GUI/GUILabel.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUILabel::GUILabel(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "LABEL"; @@ -20,8 +18,6 @@ GUILabel::GUILabel(GUIManager* Manager, GUIControlManager* ControlManager) : m_OverflowScrollTimer = Timer(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -51,8 +47,6 @@ void GUILabel::Create(const std::string& Name, int X, int Y, int Width, int Heig m_Height = std::max(m_Height, m_MinHeight); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -101,8 +95,6 @@ void GUILabel::Create(GUIProperties* Props) { Props->GetValue("VerticalOverflowScroll", &m_VerticalOverflowScroll); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -118,15 +110,11 @@ void GUILabel::ChangeSkin(GUISkin* Skin) { m_Font->CacheColor(m_FontColor); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::Draw(GUIScreen* Screen) { Draw(Screen->GetBitmap()); GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::Draw(GUIBitmap* Bitmap, bool overwiteFontColorAndKerning) { // Setup the clipping Bitmap->AddClipRect(GetRect()); @@ -212,8 +200,6 @@ void GUILabel::Draw(GUIBitmap* Bitmap, bool overwiteFontColorAndKerning) { Bitmap->SetClipRect(nullptr); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { if (Buttons & MOUSE_LEFT) { CaptureMouse(); @@ -221,8 +207,6 @@ void GUILabel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::OnMouseUp(int X, int Y, int Buttons, int Modifier) { // If the mouse is over the button, add the clicked notification to the event queue if (PointInside(X, Y) && (Buttons & MOUSE_LEFT) && IsCaptured()) { @@ -232,20 +216,14 @@ void GUILabel::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUILabel::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::Move(int X, int Y) { GUIPanel::SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::Resize(int Width, int Height) { // Make sure the control isn't too small Width = std::max(Width, m_MinWidth); @@ -254,8 +232,6 @@ void GUILabel::Resize(int Width, int Height) { GUIPanel::SetSize(Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUILabel::ResizeHeightToFit() { int newHeight = m_Font->CalculateHeight(m_Text, m_Width); GUIPanel::SetSize(m_Width, newHeight); @@ -263,20 +239,14 @@ int GUILabel::ResizeHeightToFit() { return newHeight; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUILabel::GetTextHeight() { return m_Font->CalculateHeight(m_Text, m_Width); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::SetHorizontalOverflowScroll(bool newOverflowScroll) { m_HorizontalOverflowScroll = newOverflowScroll; if (m_HorizontalOverflowScroll) { @@ -286,8 +256,6 @@ void GUILabel::SetHorizontalOverflowScroll(bool newOverflowScroll) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::SetVerticalOverflowScroll(bool newOverflowScroll) { m_VerticalOverflowScroll = newOverflowScroll; if (m_VerticalOverflowScroll) { @@ -297,8 +265,6 @@ void GUILabel::SetVerticalOverflowScroll(bool newOverflowScroll) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::ActivateDeactivateOverflowScroll(bool activateScroll) { if (OverflowScrollIsEnabled() && activateScroll != OverflowScrollIsActivated()) { m_OverflowScrollState = activateScroll ? OverflowScrollState::WaitAtStart : OverflowScrollState::Deactivated; @@ -306,8 +272,6 @@ void GUILabel::ActivateDeactivateOverflowScroll(bool activateScroll) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::StoreProperties() { m_Properties.AddVariable("Text", m_Text); @@ -329,8 +293,6 @@ void GUILabel::StoreProperties() { m_Properties.AddVariable("VerticalOverflowScroll", m_VerticalOverflowScroll); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUILabel::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUILabel.h b/Source/GUI/GUILabel.h index fe2daa4c04..573e704b39 100644 --- a/Source/GUI/GUILabel.h +++ b/Source/GUI/GUILabel.h @@ -7,9 +7,7 @@ namespace RTE { - /// /// A label control class. - /// class GUILabel : public GUIControl, public GUIPanel { public: @@ -25,234 +23,123 @@ namespace RTE { WaitAtEnd }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUILabel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUILabel object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUILabel object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUILabel(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - /// /// Draws the Label to the given GUIBitmap. - /// - /// The GUIBitmap to draw the label to. - /// Whether to overwrite the font's color and kerning with the stored values. Defaults to true, which is usually what you want. + /// @param Bitmap The GUIBitmap to draw the label to. + /// @param overwiteFontColorAndKerning Whether to overwrite the font's color and kerning with the stored values. Defaults to true, which is usually what you want. void Draw(GUIBitmap* Bitmap, bool overwiteFontColorAndKerning = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "LABEL"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResizeHeightToFit - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resize the height of the label to fit the amount of text it has to - // display. - // Arguments: None. - // Returns: The new height in pixels. - + /// Resize the height of the label to fit the amount of text it has to + /// display. + /// @return The new height in pixels. int ResizeHeightToFit(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the text of the label. - // Arguments: text. - + /// Sets the text of the label. + /// @param text. void SetText(const std::string_view& text) { m_Text = text; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the text of the label. - // Arguments: None. - + /// Gets the text of the label. const std::string& GetText() const { return m_Text; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTextHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows how tall the current text is with the current width and font etc. - // Arguments: None. - // Returns: The text height, in pixels - + /// Shows how tall the current text is with the current width and font etc. + /// @return The text height, in pixels int GetTextHeight(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetHAlignment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the horizontal alignment of the text of this label. - // Arguments: The desired alignment. - + /// Sets the horizontal alignment of the text of this label. + /// @param HAlignment The desired alignment. (default: GUIFont::Left) void SetHAlignment(int HAlignment = GUIFont::Left) { m_HAlignment = HAlignment; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetVAlignment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the vertical alignment of the text of this label. - // Arguments: The desired alignment. - + /// Sets the vertical alignment of the text of this label. + /// @param VAlignment The desired alignment. (default: GUIFont::Top) void SetVAlignment(int VAlignment = GUIFont::Top) { m_VAlignment = VAlignment; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetHAlignment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the horizontal alignment of the text of this label. - // Arguments: None. - + /// Gets the horizontal alignment of the text of this label. int GetHAlignment() const { return m_HAlignment; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetVAlignment - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the vertical alignment of the text of this label. - // Arguments: The desired alignment. - + /// Gets the vertical alignment of the text of this label. + /// @param The desired alignment. int GetVAlignment() const { return m_VAlignment; } - /// /// Gets whether or not this GUILabel should scroll horizontally (right) when it overflows. - /// - /// Whether or not this GUILabel should scroll horizontally when it overflows. + /// @return Whether or not this GUILabel should scroll horizontally when it overflows. bool GetHorizontalOverflowScroll() const { return m_HorizontalOverflowScroll; } - /// /// Sets whether or not this GUILabel should scroll horizontally (right) when it overflows. Mutually exclusive with horizontal overflow scrolling. - /// - /// Whether or not this GUILabel should scroll horizontally when it overflows. + /// @param newOverflowScroll Whether or not this GUILabel should scroll horizontally when it overflows. void SetHorizontalOverflowScroll(bool newOverflowScroll); - /// /// Gets whether or not this GUILabel should scroll vertically (down) when it overflows. - /// - /// Whether or not this GUILabel should scroll vertically when it overflows. + /// @return Whether or not this GUILabel should scroll vertically when it overflows. bool GetVerticalOverflowScroll() const { return m_VerticalOverflowScroll; } - /// /// Sets whether or not this GUILabel should scroll vertically (down) when it overflows. Mutually exclusive with horizontal overflow scrolling. - /// - /// Whether or not this GUILabel should scroll vertically when it overflows. + /// @param newOverflowScroll Whether or not this GUILabel should scroll vertically when it overflows. void SetVerticalOverflowScroll(bool newOverflowScroll); - /// /// Gets whether or not horizontal or vertical overflow scrolling is turned on. - /// - /// Whether or not horizontal or vertical overflow scrolling is turned on. + /// @return Whether or not horizontal or vertical overflow scrolling is turned on. bool OverflowScrollIsEnabled() const { return m_HorizontalOverflowScroll || m_VerticalOverflowScroll; } - /// /// Gets whether or not horizontal/vertical scrolling is happening. - /// - /// Whether or not horizontal/vertical scrolling is happening. + /// @return Whether or not horizontal/vertical scrolling is happening. bool OverflowScrollIsActivated() const { return OverflowScrollIsEnabled() && m_OverflowScrollState != OverflowScrollState::Deactivated; } - /// /// Sets whether or not horizontal/vertical scrolling should be happening. When it's deactivated, text will instantly go back to un-scrolled. - /// - /// Whether the overflow scrolling should activate (true) or deactivate (false). + /// @param activateScroll Whether the overflow scrolling should activate (true) or deactivate (false). void ActivateDeactivateOverflowScroll(bool activateScroll); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: diff --git a/Source/GUI/GUIListBox.cpp b/Source/GUI/GUIListBox.cpp index bea873ea24..092095f935 100644 --- a/Source/GUI/GUIListBox.cpp +++ b/Source/GUI/GUIListBox.cpp @@ -3,16 +3,12 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIListBox::GUIListBox(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIListPanel(Manager) { m_ControlID = "LISTBOX"; m_ControlManager = ControlManager; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -41,8 +37,6 @@ void GUIListBox::Create(const std::string& Name, int X, int Y, int Width, int He GUIListPanel::Create(X, Y, w, h); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -69,26 +63,18 @@ void GUIListBox::Create(GUIProperties* Props) { SetMultiSelect(Multi); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::Destroy() { GUIListPanel::Destroy(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::ChangeSkin(GUISkin* Skin) { GUIListPanel::ChangeSkin(Skin); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::Move(int X, int Y) { SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::Resize(int Width, int Height) { // Make sure the listbox isn't too small Width = std::max(Width, m_MinWidth); @@ -97,26 +83,18 @@ void GUIListBox::Resize(int Width, int Height) { SetSize(Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIListBox::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIListPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::StoreProperties() { m_Properties.AddVariable("MultiSelect", GetMultiSelect()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { if (Source->GetPanelID() == GetPanelID()) { if (Code == GUIListPanel::MouseMove) { @@ -143,8 +121,6 @@ void GUIListBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { GUIListPanel::ReceiveSignal(Source, Code, Data); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListBox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUIListBox.h b/Source/GUI/GUIListBox.h index e771887dc3..49fbe4db47 100644 --- a/Source/GUI/GUIListBox.h +++ b/Source/GUI/GUIListBox.h @@ -5,116 +5,58 @@ namespace RTE { - /// /// A ListBox control class. - /// class GUIListBox : public GUIControl, public GUIListPanel { public: - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIListBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIListBox object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUIListBox object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUIListBox(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "LISTBOX"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReceiveSignal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when receiving a signal. - // Arguments: Signal source, Signal code, Signal data. - + /// Called when receiving a signal. + /// @param Source Signal source, Signal code, Signal data. void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: diff --git a/Source/GUI/GUIListPanel.cpp b/Source/GUI/GUIListPanel.cpp index af891151cc..6b65e0d89b 100644 --- a/Source/GUI/GUIListPanel.cpp +++ b/Source/GUI/GUIListPanel.cpp @@ -5,8 +5,6 @@ using namespace RTE; #define RIGHTTEXTWIDTH 36 -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIListPanel::GUIListPanel(GUIManager* Manager) : GUIPanel(Manager) { m_BaseBitmap = nullptr; @@ -36,8 +34,6 @@ GUIListPanel::GUIListPanel(GUIManager* Manager) : m_MouseScroll = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIListPanel::GUIListPanel() : GUIPanel() { m_BaseBitmap = nullptr; @@ -67,8 +63,6 @@ GUIListPanel::GUIListPanel() : m_MouseScroll = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::Create(int X, int Y, int Width, int Height) { m_X = X; m_Y = Y; @@ -98,8 +92,6 @@ void GUIListPanel::Create(int X, int Y, int Width, int Height) { AddChild(m_VertScroll); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::Destroy() { // Destroy the items std::vector::iterator it; @@ -148,8 +140,6 @@ void GUIListPanel::Destroy() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ClearList() { // Destroy the items std::vector::iterator it; @@ -172,8 +162,6 @@ void GUIListPanel::ClearList() { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::AddItem(const std::string& Name, const std::string& rightText, GUIBitmap* pBitmap, const Entity* pEntity, const int extraIndex, const int offsetX) { Item* I = new Item; I->m_Name = Name; @@ -201,8 +189,6 @@ void GUIListPanel::AddItem(const std::string& Name, const std::string& rightText BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ChangeSkin(GUISkin* Skin) { assert(Skin); @@ -219,8 +205,6 @@ void GUIListPanel::ChangeSkin(GUISkin* Skin) { BuildBitmap(true, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::BuildBitmap(bool UpdateBase, bool UpdateText) { // Gotta update the text if updating the base if (UpdateBase) @@ -291,8 +275,6 @@ void GUIListPanel::BuildBitmap(bool UpdateBase, bool UpdateText) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::BuildDrawBitmap() { // Draw the items std::vector::iterator it; @@ -410,8 +392,6 @@ void GUIListPanel::BuildDrawBitmap() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::Draw(GUIScreen* Screen) { // Draw the base m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, nullptr); @@ -420,8 +400,6 @@ void GUIListPanel::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { m_ExternalCapture = IsCaptured(); @@ -447,8 +425,6 @@ void GUIListPanel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) { if (!m_MouseScroll) { return; @@ -461,8 +437,6 @@ void GUIListPanel::OnMouseWheelChange(int x, int y, int modifier, int mouseWheel } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SelectItem(int X, int Y, int Modifier) { std::vector::iterator it; bool Shift = Modifier & MODI_SHIFT; @@ -582,8 +556,6 @@ void GUIListPanel::SelectItem(int X, int Y, int Modifier) { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnMouseUp(int X, int Y, int Buttons, int Modifier) { if (m_CapturedVert) { m_VertScroll->OnMouseUp(X, Y, Buttons, Modifier); @@ -598,8 +570,6 @@ void GUIListPanel::OnMouseUp(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnMouseMove(int X, int Y, int Buttons, int Modifier) { if (m_CapturedVert) { m_VertScroll->OnMouseMove(X, Y, Buttons, Modifier); @@ -614,34 +584,24 @@ void GUIListPanel::OnMouseMove(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnMouseEnter(int X, int Y, int Buttons, int Modifier) { SendSignal(MouseEnter, Buttons); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { SendSignal(MouseLeave, Buttons); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnDoubleClick(int X, int Y, int Buttons, int Modifier) { if (PointInside(X, Y)) { SendSignal(DoubleClick, Buttons); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::BeginUpdate() { m_UpdateLocked = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::EndUpdate() { m_UpdateLocked = false; @@ -649,8 +609,6 @@ void GUIListPanel::EndUpdate() { ChangeSkin(m_Skin); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ScrollToItem(Item* pItem) { if (pItem && m_VertScroll->_GetVisible()) { int stackHeight = GetStackHeight(pItem); @@ -666,8 +624,6 @@ void GUIListPanel::ScrollToItem(Item* pItem) { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ScrollUp() { const int sensitivity = 80; if (m_VertScroll->_GetVisible()) { @@ -676,8 +632,6 @@ void GUIListPanel::ScrollUp() { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ScrollDown() { const int sensitivity = 80; if (m_VertScroll->_GetVisible()) { @@ -692,8 +646,6 @@ void GUIListPanel::ScrollDown() { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ScrollTo(int position) { if (m_VertScroll->_GetVisible()) { m_VertScroll->SetValue(position); @@ -710,8 +662,6 @@ void GUIListPanel::ScrollTo(int position) { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ScrollToSelected() { if (!m_SelectedList.empty()) { ScrollToItem(*(m_SelectedList.begin())); @@ -719,15 +669,11 @@ void GUIListPanel::ScrollToSelected() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ScrollToTop() { m_VertScroll->SetValue(0); BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ScrollToBottom() { if (m_Items.empty()) { m_VertScroll->SetValue(0); @@ -737,20 +683,14 @@ void GUIListPanel::ScrollToBottom() { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SetSelectionScrollingLoop(bool scrollLoop) { m_LoopSelectionScroll = scrollLoop; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SetMouseScrolling(bool mouseScroll) { m_MouseScroll = mouseScroll; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ScrollBarScrolling(int mouseWheelChange) { int newValue = 0; Item* lastItem = GetItem(GetItemList()->size() - 1); @@ -771,8 +711,6 @@ void GUIListPanel::ScrollBarScrolling(int mouseWheelChange) { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SelectionListScrolling(int scrollDir) { std::size_t itemListSize = GetItemList()->size(); if (!itemListSize || !scrollDir) { @@ -795,8 +733,6 @@ void GUIListPanel::SelectionListScrolling(int scrollDir) { SetSelectedIndex(newItemIndex); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::AdjustScrollbars() { // If the update is locked, don't update the scrollbars if (m_UpdateLocked) { @@ -888,8 +824,6 @@ void GUIListPanel::AdjustScrollbars() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnKeyPress(int KeyCode, int Modifier) { if (m_LastSelected < 0) { return; @@ -960,14 +894,10 @@ void GUIListPanel::OnKeyPress(int KeyCode, int Modifier) { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnKeyDown(int KeyCode, int Modifier) { SendSignal(KeyDown, KeyCode); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIListPanel::PointInsideList(int X, int Y) { bool inside = PointInside(X, Y); // Exclude the scrollbars if we are meant to @@ -977,24 +907,18 @@ bool GUIListPanel::PointInsideList(int X, int Y) { return inside; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnGainFocus() { GUIPanel::OnGainFocus(); BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::OnLoseFocus() { GUIPanel::OnLoseFocus(); BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::ReceiveSignal(GUIPanel* Source, int Code, int Data) { // ChangeValue signal from scrollpanels? assert(Source); @@ -1020,8 +944,6 @@ void GUIListPanel::ReceiveSignal(GUIPanel* Source, int Code, int Data) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SetMultiSelect(bool MultiSelect) { m_MultiSelect = MultiSelect; @@ -1031,14 +953,10 @@ void GUIListPanel::SetMultiSelect(bool MultiSelect) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIListPanel::GetMultiSelect() const { return m_MultiSelect; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SetHotTracking(bool HotTrack) { m_HotTracking = HotTrack; @@ -1048,8 +966,6 @@ void GUIListPanel::SetHotTracking(bool HotTrack) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIListPanel::Item* GUIListPanel::GetSelected() { // Nothing in the list if (m_SelectedList.empty()) { @@ -1059,20 +975,14 @@ GUIListPanel::Item* GUIListPanel::GetSelected() { return m_SelectedList.at(0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector* GUIListPanel::GetSelectionList() { return &m_SelectedList; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector* GUIListPanel::GetItemList() { return &m_Items; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIListPanel::Item* GUIListPanel::GetItem(int Index) { if (Index >= 0 && Index < m_Items.size()) { return m_Items.at(Index); @@ -1080,8 +990,6 @@ GUIListPanel::Item* GUIListPanel::GetItem(int Index) { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIListPanel::Item* GUIListPanel::GetItem(int X, int Y) { int Height = m_Height; if (m_HorzScroll->_GetVisible()) { @@ -1110,8 +1018,6 @@ GUIListPanel::Item* GUIListPanel::GetItem(int X, int Y) { return 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIListPanel::GetItemHeight(Item* pItem) { int height = 0; @@ -1140,8 +1046,6 @@ int GUIListPanel::GetItemHeight(Item* pItem) { return height; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIListPanel::GetStackHeight(Item* pItem) { int height = 0; @@ -1154,8 +1058,6 @@ int GUIListPanel::GetStackHeight(Item* pItem) { return height; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SetItemValues(int Index, Item& item) { if (Index >= 0 && Index < m_Items.size()) { *(m_Items.at(Index)) = item; @@ -1163,8 +1065,6 @@ void GUIListPanel::SetItemValues(int Index, Item& item) { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIListPanel::GetSelectedIndex() { // Nothing in the list if (m_SelectedList.empty()) { @@ -1176,8 +1076,6 @@ int GUIListPanel::GetSelectedIndex() { return I->m_ID; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SetSelectedIndex(int Index) { // Clear the old selection std::vector::iterator it; @@ -1203,8 +1101,6 @@ void GUIListPanel::SetSelectedIndex(int Index) { BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::DeleteItem(int Index) { if (Index >= 0 && Index < m_Items.size()) { const Item* I = m_Items.at(Index); @@ -1241,8 +1137,6 @@ void GUIListPanel::DeleteItem(int Index) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SetSize(int Width, int Height) { GUIPanel::SetSize(Width, Height); @@ -1259,8 +1153,6 @@ void GUIListPanel::SetSize(int Width, int Height) { BuildBitmap(true, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::SetPositionAbs(int X, int Y) { GUIPanel::SetPositionAbs(X, Y); @@ -1272,8 +1164,6 @@ void GUIListPanel::SetPositionAbs(int X, int Y) { AdjustScrollbars(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIListPanel::EnableScrollbars(bool Horizontal, bool Vertical) { m_HorzScrollEnabled = Horizontal; m_VertScrollEnabled = Vertical; diff --git a/Source/GUI/GUIListPanel.h b/Source/GUI/GUIListPanel.h index d5cc21c786..93a82ccfd1 100644 --- a/Source/GUI/GUIListPanel.h +++ b/Source/GUI/GUIListPanel.h @@ -8,9 +8,7 @@ namespace RTE { class Entity; - /// /// A listbox panel class used for controls requiring a listbox. - /// class GUIListPanel : public GUIPanel { public: @@ -54,444 +52,229 @@ namespace RTE { } }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIListPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIListPanel object in - // system memory. - // Arguments: GUIManager. - + /// Constructor method used to instantiate a GUIListPanel object in + /// system memory. + /// @param Manager GUIManager. GUIListPanel(GUIManager* Manager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIListPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIListPanel object in - // system memory. - // Arguments: None. - + /// Constructor method used to instantiate a GUIListPanel object in + /// system memory. GUIListPanel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the listpanel - // Arguments: Position, Size. - + /// Create the listpanel + /// @param X Position, Size. void Create(int X, int Y, int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the panel has been destroyed. - // Arguments: None. - + /// Called when the panel has been destroyed. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Add an item to the list. - // Arguments: Name, extraText, Text which will be displayed right-justified in the item. - // a Bitmap object pointer alternatively pointing to a bitmap to display - // in the list. Ownership IS transferred! - // An Extra menu-specific index that this item may be associated with. - // Object instance associated with the item. Ownership is NOT TRANSFERRED! - + /// Add an item to the list. + /// @param Name Name, extraText, Text which will be displayed right-justified in the item. + /// a Bitmap object pointer alternatively pointing to a bitmap to display + /// in the list. Ownership IS transferred! + /// @param rightText An Extra menu-specific index that this item may be associated with. (default: "") + /// @param pBitmap Object instance associated with the item. Ownership is NOT TRANSFERRED! (default: nullptr) void AddItem(const std::string& Name, const std::string& rightText = "", GUIBitmap* pBitmap = nullptr, const Entity* pEntity = 0, const int extraIndex = -1, const int offsetX = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the list. - // Arguments: None. - + /// Clears the list. void ClearList(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseMove - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse moves (over the panel, or when captured). - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse moves (over the panel, or when captured). + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseEnter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse enters the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse enters the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseLeave - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse leaves the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse leaves the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnDoubleClick - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse has double-clicked on the pane. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse has double-clicked on the pane. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnDoubleClick(int X, int Y, int Buttons, int Modifier) override; - /// /// Called when the mouse scroll wheel is moved. - /// - /// Mouse X position. - /// Mouse Y position. - /// Activated modifier buttons. - /// The amount of wheel movement. Positive is scroll up, negative is scroll down. + /// @param x Mouse X position. + /// @param y Mouse Y position. + /// @param modifier Activated modifier buttons. + /// @param mouseWheelChange The amount of wheel movement. Positive is scroll up, negative is scroll down. void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnKeyPress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when a key is pressed (OnDown & repeating). - // Arguments: KeyCode, Modifier. - + /// Called when a key is pressed (OnDown & repeating). + /// @param KeyCode KeyCode, Modifier. void OnKeyPress(int KeyCode, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnKeyDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when a key goes down. - // Arguments: KeyCode, Modifier. - + /// Called when a key goes down. + /// @param KeyCode KeyCode, Modifier. void OnKeyDown(int KeyCode, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PointInsideList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if a point is inside the panel, but not on the scrollbars if - // they are visible - // Arguments: X, Y Coordinates of point - // Return value: A boolean of the check result - + /// Checks if a point is inside the panel, but not on the scrollbars if + /// they are visible + /// @param X X, Y Coordinates of point + /// @return A boolean of the check result bool PointInsideList(int X, int Y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnGainFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the panel gains focus. - // Arguments: None. - + /// Called when the panel gains focus. void OnGainFocus() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnLoseFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the panel looses focus. - // Arguments: None. - + /// Called when the panel looses focus. void OnLoseFocus() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReceiveSignal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when receiving a signal. - // Arguments: Signal source, Signal code, Signal data. - + /// Called when receiving a signal. + /// @param Source Signal source, Signal code, Signal data. void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BeginUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Locks the control from updating every time a new item is added. - // Arguments: None. - + /// Locks the control from updating every time a new item is added. void BeginUpdate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EndUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: UnLocks the control from updating every time a new item is added. - // Will automatically update the control. - // Arguments: None. - + /// UnLocks the control from updating every time a new item is added. + /// Will automatically update the control. void EndUpdate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMultiSelect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the multi-selection value. - // Arguments: MultiSelect - + /// Sets the multi-selection value. + /// @param MultiSelect MultiSelect void SetMultiSelect(bool MultiSelect); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMultiSelect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the multi-selection value. - // Arguments: None. - + /// Gets the multi-selection value. bool GetMultiSelect() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetHotTracking - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the hot tracking value. - // Arguments: HotTrack. - + /// Sets the hot tracking value. + /// @param HotTrack HotTrack. void SetHotTracking(bool HotTrack); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSelected - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the selected (or first in the selected list) item. - // Arguments: None. - + /// Gets the selected (or first in the selected list) item. Item* GetSelected(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetItemList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the item list. - // Arguments: None. - + /// Gets the item list. std::vector* GetItemList(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets an item at the index. - // Arguments: Index. - + /// Gets an item at the index. + /// @param Index Index. Item* GetItem(int Index); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets an item at a specific absolute screen coordinate, if any. - // Arguments: The absolute screen coordinates to get the item from. - + /// Gets an item at a specific absolute screen coordinate, if any. + /// @param X The absolute screen coordinates to get the item from. Item* GetItem(int X, int Y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetItemHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the drawing height of the item passed in. - // Arguments: Pointer to the Item to get the height of. Ownership is NOT transferred! - + /// Gets the drawing height of the item passed in. + /// @param pItem Pointer to the Item to get the height of. Ownership is NOT transferred! int GetItemHeight(Item* pItem); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetStackHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the height, in pixels, of the stack of items up to a specific one. - // E.g. If the specified one is the first (top) in the list, 0 is returned. - // If the second one is specified, the height of the first is returned. - // If the third is specified, the sum of the first and second items' heights - // is returned. If 0 is passed, the entire stack's height is returned. - // Arguments: Pointer to the Item to get the height up to. Ownership is NOT transferred! - // If 0 is passed, the entire stack's height is returned. - + /// Gets the height, in pixels, of the stack of items up to a specific one. + /// E.g. If the specified one is the first (top) in the list, 0 is returned. + /// If the second one is specified, the height of the first is returned. + /// If the third is specified, the sum of the first and second items' heights + /// is returned. If 0 is passed, the entire stack's height is returned. + /// @param pItem Pointer to the Item to get the height up to. Ownership is NOT transferred! (default: nullptr) + /// If 0 is passed, the entire stack's height is returned. int GetStackHeight(Item* pItem = nullptr); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetScrollVerticalValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the scroll value, in pixels, of the vertical axis. - // Arguments: The scroll value in pixels. - + /// Gets the scroll value, in pixels, of the vertical axis. + /// @param m_VertScroll->GetValue( The scroll value in pixels. int GetScrollVerticalValue() const { return m_VertScroll->GetValue(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetItemValues - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the values of a specific Item. - // Arguments: Index and Item reference. - + /// Sets the values of a specific Item. + /// @param Index Index and Item reference. void SetItemValues(int Index, Item& item); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSelectedIndex - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the selected (or first in the selected list) index. - // Arguments: None. - // Returns: Index, or -1 if there is no items selected. - + /// Gets the selected (or first in the selected list) index. + /// @return Index, or -1 if there is no items selected. int GetSelectedIndex(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSelectedIndex - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Selects the item at the index. - // Arguments: Index. - + /// Selects the item at the index. + /// @param Index Index. void SetSelectedIndex(int Index); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetAlternateDrawMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Selects this to draw its items differently, with lines instead of - // rectangles, etc - // Arguments: The new mode setting. - + /// Selects this to draw its items differently, with lines instead of + /// rectangles, etc + /// @param enableAltDrawMode The new mode setting. (default: true) void SetAlternateDrawMode(bool enableAltDrawMode = true) { m_AlternateDrawMode = enableAltDrawMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSelectionList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the selection list. - // Arguments: None. - + /// Gets the selection list. std::vector* GetSelectionList(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DeleteItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Deletes an item at the index. - // Arguments: Index. - + /// Deletes an item at the index. + /// @param Index Index. void DeleteItem(int Index); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the size of the panel. - // Arguments: Width, Height. - + /// Adjusts the size of the panel. + /// @param Width Width, Height. void SetSize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPositionAbs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the position of the panel. - // Arguments: X, Y. - + /// Adjusts the position of the panel. + /// @param X X, Y. void SetPositionAbs(int X, int Y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableScrollbars - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the enabled state of both scrollbars. - // Arguments: Horz, Vert - + /// Sets the enabled state of both scrollbars. + /// @param Horizontal Horz, Vert void EnableScrollbars(bool Horizontal, bool Vertical); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ScrollToItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the vertical scrollbar to show the specific item in the list. - // Arguments: The item you want to show. - + /// Adjusts the vertical scrollbar to show the specific item in the list. + /// @param pItem The item you want to show. void ScrollToItem(Item* pItem); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ScrollToSelected - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the vertical scrollbar to show the first selected item in the - // list. - // Arguments: None. - + /// Adjusts the vertical scrollbar to show the first selected item in the + /// list. void ScrollToSelected(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ScrollToTop - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the vertical scrollbar to show the top of the list - // Arguments: None. - + /// Adjusts the vertical scrollbar to show the top of the list void ScrollToTop(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ScrollToBottom - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the vertical scrollbar to show the bottom of the list - // Arguments: None. - + /// Adjusts the vertical scrollbar to show the bottom of the list void ScrollToBottom(); - /// /// Scrolls the GUIListPanel up. - /// void ScrollUp(); - /// /// Scrolls the GUIListPanel down. - /// void ScrollDown(); - /// /// Scrolls the the GUIListPanel to a specific position - /// - /// The position to scroll to. + /// @param position The position to scroll to. void ScrollTo(int position); - /// /// Sets whether the scroll panel scrolls in a loop or not. - /// - /// True to scroll in a loop, false to scroll with edge stopping. + /// @param scrollLoop True to scroll in a loop, false to scroll with edge stopping. void SetSelectionScrollingLoop(bool scrollLoop); - /// /// Sets whether the list panel can be scrolled with the mouse scroll wheel. - /// - /// True to enable scrolling, false to disable. + /// @param mouseScroll True to enable scrolling, false to disable. void SetMouseScrolling(bool mouseScroll); - /// /// Sets the thickness (width on vertical, height on horizontal) of the ListPanel's scroll bars and adjusts them to the new thickness. - /// - /// The new scroll bar thickness, in pixels. + /// @param newThickness The new scroll bar thickness, in pixels. void SetScrollBarThickness(int newThickness) { m_ScrollBarThickness = newThickness; AdjustScrollbars(); } - /// /// Sets the padding around the ListPanel's scrollbars and adjusts them to the new padding. Used to better size and position scrollbars within panel bounds, allowing to not overdraw on panel borders. - /// - /// The new scrollbar padding, in pixels. + /// @param newPadding The new scrollbar padding, in pixels. void SetScrollBarPadding(int newPadding) { m_ScrollBarPadding = newPadding; AdjustScrollbars(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Build the bitmap. - // Arguments: UpdateBase, UpdateText. - + /// Build the bitmap. + /// @param UpdateBase UpdateBase, UpdateText. void BuildBitmap(bool UpdateBase, bool UpdateText); private: @@ -528,40 +311,22 @@ namespace RTE { unsigned long m_SelectedColorIndex; unsigned long m_UnselectedColorIndex; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildDrawBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Build the drawing bitmap. - // Arguments: None. - + /// Build the drawing bitmap. void BuildDrawBitmap(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AdjustScrollbars - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the scrollbars. - // Arguments: None. - + /// Adjusts the scrollbars. void AdjustScrollbars(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SelectItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Selects an item based on mouse position. - // Arguments: Mouse Position, Modifier. - + /// Selects an item based on mouse position. + /// @param X Mouse Position, Modifier. void SelectItem(int X, int Y, int Modifier); - /// /// Perform list scrolling through the scrollbar. - /// - /// Amount and direction of scrolling. Positive to scroll up, negative to scroll down. + /// @param MouseWheelChange Amount and direction of scrolling. Positive to scroll up, negative to scroll down. void ScrollBarScrolling(int mouseWheelChange); - /// /// Perform list scrolling by changing the currently selected list item. - /// - /// Amount and direction of scrolling. Positive to scroll up, negative to scroll down. + /// @param MouseWheelChange Amount and direction of scrolling. Positive to scroll up, negative to scroll down. void SelectionListScrolling(int mouseWheelChange); }; }; // namespace RTE diff --git a/Source/GUI/GUIManager.cpp b/Source/GUI/GUIManager.cpp index 97195ec318..b4d2016c7b 100644 --- a/Source/GUI/GUIManager.cpp +++ b/Source/GUI/GUIManager.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIManager::GUIManager(GUIInput* input) { m_Input = input; m_MouseEnabled = true; @@ -23,15 +21,11 @@ GUIManager::GUIManager(GUIInput* input) { m_pTimer = new Timer(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIManager::~GUIManager() { delete m_pTimer; m_pTimer = nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIManager::Clear() { m_PanelList.clear(); m_CapturedPanel = nullptr; @@ -50,8 +44,6 @@ void GUIManager::Clear() { m_LastMouseDown[2] = -99999.0F; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIManager::AddPanel(GUIPanel* panel) { if (panel) { int Z = 0; @@ -70,8 +62,6 @@ void GUIManager::AddPanel(GUIPanel* panel) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIManager::Update(bool ignoreKeyboardEvents) { m_Input->Update(); @@ -272,8 +262,6 @@ void GUIManager::Update(bool ignoreKeyboardEvents) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIManager::Draw(GUIScreen* Screen) { // Go through drawing panels that are invalid std::vector::iterator it; @@ -288,8 +276,6 @@ void GUIManager::Draw(GUIScreen* Screen) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIManager::CaptureMouse(GUIPanel* Panel) { assert(Panel); @@ -301,8 +287,6 @@ void GUIManager::CaptureMouse(GUIPanel* Panel) { m_CapturedPanel->SetCaptureState(true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIManager::ReleaseMouse() { if (m_CapturedPanel) { m_CapturedPanel->SetCaptureState(false); @@ -311,8 +295,6 @@ void GUIManager::ReleaseMouse() { m_CapturedPanel = nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIManager::FindBottomPanel(int X, int Y) { std::vector::iterator it; @@ -329,8 +311,6 @@ GUIPanel* GUIManager::FindBottomPanel(int X, int Y) { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIManager::FindTopPanel(int X, int Y) { std::vector::reverse_iterator it; @@ -347,14 +327,10 @@ GUIPanel* GUIManager::FindTopPanel(int X, int Y) { return nullptr; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIManager::GetPanelID() { return m_UniqueIDCount++; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIManager::MouseInRect(const GUIRect* Rect, int X, int Y) { if (!Rect) { return false; @@ -365,8 +341,6 @@ bool GUIManager::MouseInRect(const GUIRect* Rect, int X, int Y) { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIManager::TrackMouseHover(GUIPanel* Pan, bool Enabled, int Delay) { assert(Pan); m_HoverTrack = Enabled; @@ -376,8 +350,6 @@ void GUIManager::TrackMouseHover(GUIPanel* Pan, bool Enabled, int Delay) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIManager::SetFocus(GUIPanel* Pan) { // Send the LoseFocus event to the old panel (if there is one) if (m_FocusPanel) { diff --git a/Source/GUI/GUIManager.h b/Source/GUI/GUIManager.h index 31c5a81e30..91a2273b3b 100644 --- a/Source/GUI/GUIManager.h +++ b/Source/GUI/GUIManager.h @@ -5,115 +5,56 @@ namespace RTE { class Timer; - /// /// The main manager that handles all the panels and inputs. - /// class GUIManager { public: - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIManager - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIManager object in system - // memory. - // Arguments: Input Interface - + /// Constructor method used to instantiate a GUIManager object in system + /// memory. + /// @param input Input Interface explicit GUIManager(GUIInput* input); - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: GUIManager - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a GUIManager object. - // Arguments: None. - + /// Destructor method used to clean up a GUIManager object. ~GUIManager(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the manager. - // Arguments: None. - + /// Clears the manager. void Clear(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a panel to the list. - // Arguments: Pointer to a panel. - + /// Adds a panel to the list. + /// @param panel Pointer to a panel. void AddPanel(GUIPanel* panel); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the GUI. - // Arguments: Whether keyboard events should be ignored or not. Used to avoid conflicts when custom keyboard handling for GUI elements is preset. - + /// Updates the GUI. + /// @param ignoreKeyboardEvents Whether keyboard events should be ignored or not. Used to avoid conflicts when custom keyboard handling for GUI elements is preset. (default: false) void Update(bool ignoreKeyboardEvents = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draw all the panels - // Arguments: Screen. - + /// Draw all the panels + /// @param Screen Screen. void Draw(GUIScreen* Screen); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableMouse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Enables and disables the mouse completely for this. - // Arguments: Enable? - + /// Enables and disables the mouse completely for this. + /// @param enable Enable? (default: true) void EnableMouse(bool enable = true) { m_MouseEnabled = enable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CaptureMouse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets up capturing a mouse for a panel. - // Arguments: Panel. - + /// Sets up capturing a mouse for a panel. + /// @param Panel Panel. void CaptureMouse(GUIPanel* Panel); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReleaseMouse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Releases a mouse capture. - // Arguments: None. - + /// Releases a mouse capture. void ReleaseMouse(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanelID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a unique ID for a panel. - // Arguments: None. - + /// Gets a unique ID for a panel. int GetPanelID(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetInputController - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the input controller object - // Arguments: None. - + /// Gets the input controller object GUIInput* GetInputController() { return m_Input; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TrackMouseHover - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets up the manager to enable/disable hover tracking of this panel - // Arguments: Panel, Enabled, Delay (milliseconds) - + /// Sets up the manager to enable/disable hover tracking of this panel + /// @param Pan Panel, Enabled, Delay (milliseconds) void TrackMouseHover(GUIPanel* Pan, bool Enabled, int Delay); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Give focus to a panel. - // Arguments: Panel. - + /// Give focus to a panel. + /// @param Pan Panel. void SetFocus(GUIPanel* Pan); private: @@ -142,30 +83,18 @@ namespace RTE { Timer* m_pTimer; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FindBottomPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through the panel list and selects the bottommost - // ('first', render wise) panel on a specific point. - // Arguments: Mouse Position. - + /// Goes through the panel list and selects the bottommost + /// ('first', render wise) panel on a specific point. + /// @param X Mouse Position. GUIPanel* FindBottomPanel(int X, int Y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FindTopPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through the panel list and selects the topmost ('last', render - // wise) panel on a specific point. - // Arguments: Mouse Position. - + /// Goes through the panel list and selects the topmost ('last', render + /// wise) panel on a specific point. + /// @param X Mouse Position. GUIPanel* FindTopPanel(int X, int Y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: MouseInRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if the mouse point is inside a rectangle. - // Arguments: Rectangle, Mouse position. - + /// Checks if the mouse point is inside a rectangle. + /// @param Rect Rectangle, Mouse position. bool MouseInRect(const GUIRect* Rect, int X, int Y); }; }; // namespace RTE diff --git a/Source/GUI/GUIPanel.cpp b/Source/GUI/GUIPanel.cpp index 6bde68721f..9c4e013d5b 100644 --- a/Source/GUI/GUIPanel.cpp +++ b/Source/GUI/GUIPanel.cpp @@ -2,8 +2,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel::GUIPanel(GUIManager* Manager) { Clear(); m_Manager = Manager; @@ -13,14 +11,10 @@ GUIPanel::GUIPanel(GUIManager* Manager) { m_FontKerning = 1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel::GUIPanel() { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::Clear() { m_X = 0; m_Y = 0; @@ -46,8 +40,6 @@ void GUIPanel::Clear() { m_FontKerning = 1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::Setup(GUIManager* manager, int ZPos) { m_Manager = manager; m_ZPos = ZPos; @@ -67,8 +59,6 @@ void GUIPanel::Setup(GUIManager* manager, int ZPos) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::AddChild(GUIPanel* child, bool convertToAbsolutePos) { if (child) { // Convert the child's coordinates into absolute coordinates @@ -101,8 +91,6 @@ void GUIPanel::AddChild(GUIPanel* child, bool convertToAbsolutePos) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::RemoveChild(const GUIPanel* pChild) { // Note: We do NOT free the children because they are still linked in through their controls. This merely removes the panel from the list. // This will cause a small memory leak, but this is only designed for the GUI Editor and is a bit of a hack @@ -116,8 +104,6 @@ void GUIPanel::RemoveChild(const GUIPanel* pChild) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::LoadProperties(GUIProperties* Props) { assert(Props); @@ -130,20 +116,14 @@ void GUIPanel::LoadProperties(GUIProperties* Props) { Props->GetValue("Enabled", &m_Enabled); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::Invalidate() { m_ValidRegion = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIPanel::IsValid() const { return m_ValidRegion; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::Draw(GUIScreen* Screen) { // Validate this panel m_ValidRegion = true; @@ -171,76 +151,44 @@ void GUIPanel::Draw(GUIScreen* Screen) { Screen->GetBitmap()->SetClipRect(nullptr); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnMouseDown(int X, int Y, int Buttons, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnMouseUp(int X, int Y, int Buttons, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnDoubleClick(int X, int Y, int Buttons, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnMouseMove(int X, int Y, int Buttons, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnMouseEnter(int X, int Y, int Buttons, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnMouseLeave(int X, int Y, int Buttons, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnMouseHover(int X, int Y, int Buttons, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnKeyDown(int KeyCode, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnKeyUp(int KeyCode, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnKeyPress(int KeyCode, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnTextInput(std::string_view inputText) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnGainFocus() { m_GotFocus = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::OnLoseFocus() { m_GotFocus = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::CaptureMouse() { m_Manager->CaptureMouse(this); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::ReleaseMouse() { m_Manager->ReleaseMouse(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIPanel::BottomPanelUnderPoint(int x, int y) { if (!PointInside(x, y)) { return nullptr; @@ -266,8 +214,6 @@ GUIPanel* GUIPanel::BottomPanelUnderPoint(int x, int y) { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIPanel::TopPanelUnderPoint(int x, int y) { if (!PointInside(x, y)) { return nullptr; @@ -294,8 +240,6 @@ GUIPanel* GUIPanel::TopPanelUnderPoint(int x, int y) { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIPanel::PointInside(int X, int Y) { // Can't be inside an invisible panel if (!m_Visible) { @@ -311,15 +255,11 @@ bool GUIPanel::PointInside(int X, int Y) { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::SetSize(int Width, int Height) { m_Width = Width; m_Height = Height; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::SetPositionAbs(int X, int Y, bool moveChildren) { int DX = X - m_X; int DY = Y - m_Y; @@ -337,8 +277,6 @@ void GUIPanel::SetPositionAbs(int X, int Y, bool moveChildren) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::SetPositionRel(int X, int Y) { X += m_Parent->GetXPos(); Y += m_Parent->GetYPos(); @@ -357,8 +295,6 @@ void GUIPanel::SetPositionRel(int X, int Y) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::MoveRelative(int dX, int dY) { m_X += dX; m_Y += dY; @@ -371,8 +307,6 @@ void GUIPanel::MoveRelative(int dX, int dY) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::CenterInParent(bool centerX, bool centerY) { int newRelX = m_X - m_Parent->GetXPos(); int newRelY = m_Y - m_Parent->GetYPos(); @@ -387,52 +321,36 @@ void GUIPanel::CenterInParent(bool centerX, bool centerY) { SetPositionRel(newRelX, newRelY); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::_SetVisible(bool Visible) { m_Visible = Visible; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIPanel::_GetVisible() const { return m_Visible; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::_SetEnabled(bool Enabled) { m_Enabled = Enabled; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIPanel::_GetEnabled() const { return m_Enabled; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIPanel::GetWidth() const { return m_Width; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIPanel::GetHeight() const { return m_Height; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIRect* GUIPanel::GetRect() { SetRect(&m_Rect, m_X, m_Y, m_X + m_Width, m_Y + m_Height); return &m_Rect; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::GetRect(int* X, int* Y, int* Width, int* Height) const { if (X) { *X = m_X; @@ -448,88 +366,60 @@ void GUIPanel::GetRect(int* X, int* Y, int* Width, int* Height) const { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIPanel::GetPanelID() const { return m_ID; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::TrackMouseHover(bool Enabled, int Delay) { m_Manager->TrackMouseHover(this, Enabled, Delay); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::SetFocus() { m_Manager->SetFocus(this); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIPanel::HasFocus() const { return m_GotFocus; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::SetCaptureState(bool Captured) { m_Captured = Captured; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIPanel::IsCaptured() const { return m_Captured; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIPanel::IsEnabled() const { return m_Enabled; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::SendSignal(int Code, int Data) { if (m_SignalTarget) { m_SignalTarget->ReceiveSignal(this, Code, Data); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::ReceiveSignal(GUIPanel* Source, int Code, int Data) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::SetSignalTarget(GUIPanel* Target) { if (Target) { m_SignalTarget = Target; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIPanel::GetParentPanel() { return m_Parent; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::SetZPos(int Z) { m_ZPos = Z; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIPanel::GetZPos() const { return m_ZPos; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::ChangeZPosition(int Type) { // If we don't have a parent, get the manager to alter the Z Position if (!m_Parent) { @@ -541,8 +431,6 @@ void GUIPanel::ChangeZPosition(int Type) { m_Parent->_ChangeZ(this, Type); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::_ChangeZ(GUIPanel* Child, int Type) { assert(Child); @@ -590,8 +478,6 @@ void GUIPanel::_ChangeZ(GUIPanel* Child, int Type) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIPanel::ToString() { std::string OutString = ""; @@ -614,8 +500,6 @@ std::string GUIPanel::ToString() { return OutString; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::BuildProperties(GUIProperties* Prop) { assert(Prop); @@ -636,8 +520,6 @@ void GUIPanel::BuildProperties(GUIProperties* Prop) { Prop->AddVariable("Enabled", m_Enabled); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIPanel::WriteValue(const std::string& Name, int Value) { char buf[32]; @@ -651,8 +533,6 @@ std::string GUIPanel::WriteValue(const std::string& Name, int Value) { return OutString; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIPanel::WriteValue(const std::string& Name, bool Value) { std::string OutString = Name; OutString += " = "; @@ -662,8 +542,6 @@ std::string GUIPanel::WriteValue(const std::string& Name, bool Value) { return OutString; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPanel::_ApplyProperties(GUIProperties* Props) { assert(Props); diff --git a/Source/GUI/GUIPanel.h b/Source/GUI/GUIPanel.h index 73136196ef..62cc7d98f5 100644 --- a/Source/GUI/GUIPanel.h +++ b/Source/GUI/GUIPanel.h @@ -6,9 +6,7 @@ namespace RTE { class GUIPanel; class GUIManager; - /// /// A rectangle 'window' in the GUI that recieves mouse and keyboard events. - /// class GUIPanel { public: @@ -35,499 +33,234 @@ namespace RTE { BottomMost, } ZChange; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIPanel object in system - // memory. - // Arguments: Manager. - + /// Constructor method used to instantiate a GUIPanel object in system + /// memory. + /// @param Manager Manager. explicit GUIPanel(GUIManager* Manager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIPanel object in system - // memory. - // Arguments: None. - + /// Constructor method used to instantiate a GUIPanel object in system + /// memory. GUIPanel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the settings. - // Arguments: None. - + /// Clears all the settings. void Clear(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BottomPanelUnderPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Recursively goes down the tree to check the last panel under a point - // Arguments: X, Y Coordinates of point - // Return value: A pointer to the panel. 0 if no panel is under the point - + /// Recursively goes down the tree to check the last panel under a point + /// @param x X, Y Coordinates of point + /// @return A pointer to the panel. 0 if no panel is under the point GUIPanel* BottomPanelUnderPoint(int x, int y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TopPanelUnderPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Recursively goes up the tree from to check the first panel under a point - // Arguments: X, Y Coordinates of point - // Return value: A pointer to the panel. 0 if no panel is under the point - + /// Recursively goes up the tree from to check the first panel under a point + /// @param x X, Y Coordinates of point + /// @return A pointer to the panel. 0 if no panel is under the point GUIPanel* TopPanelUnderPoint(int x, int y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddChild - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a child to this panel - // Arguments: Pointer to the panel to add - + /// Adds a child to this panel + /// @param child Pointer to the panel to add void AddChild(GUIPanel* child, bool convertToAbsolutePos = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveChild - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a child based on name. - // Arguments: Child Name. - + /// Removes a child based on name. + /// @param pChild Child Name. void RemoveChild(const GUIPanel* pChild); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Setup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets up the panel for use with the manager. - // Arguments: Pointer to the manager to use, ZPosition. - + /// Sets up the panel for use with the manager. + /// @param manager Pointer to the manager to use, ZPosition. void Setup(GUIManager* manager, int ZPos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads the base data from a properties page - // Arguments: Pointer to the properties class - + /// Loads the base data from a properties page + /// @param Props Pointer to the properties class void LoadProperties(GUIProperties* Props); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Invalidate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Invalidates the panel - // Arguments: None. - + /// Invalidates the panel void Invalidate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsValid - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if the panel is valid - // Arguments: None. - + /// Checks if the panel is valid bool IsValid() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class virtual void Draw(GUIScreen* Screen); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. virtual void OnMouseDown(int X, int Y, int Buttons, int Modifier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. virtual void OnMouseUp(int X, int Y, int Buttons, int Modifier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnDoubleClick - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse has double-clicked on the pane. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse has double-clicked on the pane. + /// @param X Mouse Position, Mouse Buttons, Modifier. virtual void OnDoubleClick(int X, int Y, int Buttons, int Modifier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnMouseMove - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse moves (over the panel, or when captured). - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse moves (over the panel, or when captured). + /// @param X Mouse Position, Mouse Buttons, Modifier. virtual void OnMouseMove(int X, int Y, int Buttons, int Modifier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnMouseEnter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse enters the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse enters the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. virtual void OnMouseEnter(int X, int Y, int Buttons, int Modifier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnMouseLeave - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse leaves the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse leaves the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. virtual void OnMouseLeave(int X, int Y, int Buttons, int Modifier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnMouseHover - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse is hovering over the panel (has to be enabled) - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse is hovering over the panel (has to be enabled) + /// @param X Mouse Position, Mouse Buttons, Modifier. virtual void OnMouseHover(int X, int Y, int Buttons, int Modifier); - /// /// Called when the mouse scroll wheel is moved. - /// - /// Mouse X position. - /// Mouse Y position. - /// Activated modifier buttons. - /// The amount of wheel movement. Positive is scroll up, negative is scroll down. + /// @param x Mouse X position. + /// @param y Mouse Y position. + /// @param modifier Activated modifier buttons. + /// @param mouseWheelChange The amount of wheel movement. Positive is scroll up, negative is scroll down. virtual void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange){}; - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnKeyDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when a key goes down. - // Arguments: KeyCode, Modifier. - + /// Called when a key goes down. + /// @param KeyCode KeyCode, Modifier. virtual void OnKeyDown(int KeyCode, int Modifier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnKeyUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when a key goes up. - // Arguments: KeyCode, Modifier. - + /// Called when a key goes up. + /// @param KeyCode KeyCode, Modifier. virtual void OnKeyUp(int KeyCode, int Modifier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnKeyPress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when a key is pressed (OnDown & repeating). - // Arguments: KeyCode, Modifier. - + /// Called when a key is pressed (OnDown & repeating). + /// @param KeyCode KeyCode, Modifier. virtual void OnKeyPress(int KeyCode, int Modifier); - /// /// Called when text input is received. - /// - /// The input text being received. + /// @param inputText The input text being received. virtual void OnTextInput(std::string_view inputText); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnGainFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the panel gains focus. - // Arguments: None. - + /// Called when the panel gains focus. virtual void OnGainFocus(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: OnLoseFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the panel looses focus. - // Arguments: None. - + /// Called when the panel looses focus. virtual void OnLoseFocus(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: SetSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the size of the panel. - // Arguments: Width, Height. - + /// Adjusts the size of the panel. + /// @param Width Width, Height. virtual void SetSize(int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: SetPositionAbs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the absolute position of the panel. - // Arguments: X, Y, and whether to move the children too - + /// Adjusts the absolute position of the panel. + /// @param X X, Y, and whether to move the children too virtual void SetPositionAbs(int X, int Y, bool moveChildren = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: SetPositionRel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the position of the panel, relative to its parent. - // Arguments: X, Y. - + /// Sets the position of the panel, relative to its parent. + /// @param X X, Y. virtual void SetPositionRel(int X, int Y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: MoveRelative - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Moves the position of the panel by a relative amount. - // Arguments: X, Y, relative. - + /// Moves the position of the panel by a relative amount. + /// @param dX X, Y, relative. virtual void MoveRelative(int dX, int dY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: CenterInParent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Centers this in its parent, taking this' dimensions into consideration. - // Arguments: Which axes to center. - + /// Centers this in its parent, taking this' dimensions into consideration. + /// @param centerX Which axes to center. virtual void CenterInParent(bool centerX, bool centerY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: ReceiveSignal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when receiving a signal. - // Arguments: Signal source, Signal code, Signal data. - + /// Called when receiving a signal. + /// @param Source Signal source, Signal code, Signal data. virtual void ReceiveSignal(GUIPanel* Source, int Code, int Data); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CaptureMouse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Captures the mouse. - // Arguments: None. - + /// Captures the mouse. void CaptureMouse(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReleaseMouse - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Releases the mouse. - // Arguments: None. - + /// Releases the mouse. void ReleaseMouse(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual Method: SetFont - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the font this panel will be using - // Arguments: The new font, ownership is NOT transferred! - + /// Sets the font this panel will be using + /// @param pFont The new font, ownership is NOT transferred! virtual void SetFont(GUIFont* pFont) { m_Font = pFont; m_ValidRegion = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: _SetVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the visibility of the panel. - // Arguments: Visible. - + /// Sets the visibility of the panel. + /// @param Visible Visible. void _SetVisible(bool Visible); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: _GetVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the visibility of the panel. - // Arguments: None. - + /// Gets the visibility of the panel. bool _GetVisible() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: _SetEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the enabled state of the panel. - // Arguments: Enabled. - + /// Sets the enabled state of the panel. + /// @param Enabled Enabled. void _SetEnabled(bool Enabled); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: _GetEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the enabled state of the panel. - // Arguments: None. - + /// Gets the enabled state of the panel. bool _GetEnabled() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the width of the panel. - // Arguments: None. - + /// Gets the width of the panel. int GetWidth() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the height of the panel. - // Arguments: None. - + /// Gets the height of the panel. int GetHeight() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetXPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the x position of the panel - // Arguments: None. - + /// Gets the x position of the panel int GetXPos() const { return m_X; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetYPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the y position of the panel - // Arguments: None. - + /// Gets the y position of the panel int GetYPos() const { return m_Y; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRelXPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the x position of the panel, relative to its parent - // Arguments: None. - + /// Gets the x position of the panel, relative to its parent int GetRelXPos() const { return m_X - m_Parent->GetXPos(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRelYPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the y position of the panel, relative to its parent - // Arguments: None. - + /// Gets the y position of the panel, relative to its parent int GetRelYPos() const { return m_Y - m_Parent->GetYPos(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the panel. - // Arguments: None. - + /// Gets the rectangle of the panel. GUIRect* GetRect(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the panel. - // Arguments: X, Y, Width, Height - + /// Gets the rectangle of the panel. + /// @param X X, Y, Width, Height void GetRect(int* X, int* Y, int* Width, int* Height) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetParentPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the parent of this panel. - // Arguments: None. - + /// Gets the parent of this panel. GUIPanel* GetParentPanel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanelID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the panel's ID. - // Arguments: None. - + /// Gets the panel's ID. int GetPanelID() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCaptureState - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the panel's captured state. - // Arguments: Captured. - + /// Sets the panel's captured state. + /// @param Captured Captured. void SetCaptureState(bool Captured); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsCaptured - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the panel's captured state. - // Arguments: None. - + /// Gets the panel's captured state. bool IsCaptured() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the panel's enabled state. - // Arguments: None. - + /// Gets the panel's enabled state. bool IsEnabled() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSignalTarget - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the target panel to receive signals. - // Arguments: Target panel. - + /// Sets the target panel to receive signals. + /// @param Target Target panel. void SetSignalTarget(GUIPanel* Target); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PointInside - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if a point is inside the panel - // Arguments: X, Y Coordinates of point - // Return value: A boolean of the check result - + /// Checks if a point is inside the panel + /// @param X X, Y Coordinates of point + /// @return A boolean of the check result virtual bool PointInside(int X, int Y); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the focus of this panel. - // Arguments: None. - + /// Sets the focus of this panel. void SetFocus(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the focus value of the panel. - // Arguments: None. - + /// Gets the focus value of the panel. bool HasFocus() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetZPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the Z index of the panel. - // Arguments: ZPos. - + /// Sets the Z index of the panel. + /// @param Z ZPos. void SetZPos(int Z); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetZPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the Z index of the panel. - // Arguments: None. - + /// Gets the Z index of the panel. int GetZPos() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeZPosition - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes the Z Position of the panel. - // Arguments: Change type. - + /// Changes the Z Position of the panel. + /// @param Type Change type. void ChangeZPosition(int Type); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ToString - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Convert the properties in the panel to a string. - // Arguments: None. - + /// Convert the properties in the panel to a string. std::string ToString(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds this panels properties to a properties class. - // Arguments: GUIProperties. - + /// Adds this panels properties to a properties class. + /// @param Prop GUIProperties. void BuildProperties(GUIProperties* Prop); protected: @@ -548,52 +281,28 @@ namespace RTE { unsigned long m_FontShadow; int m_FontKerning; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SendSignal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sends a signal to the target. - // Arguments: Signal code, Data. - + /// Sends a signal to the target. + /// @param Code Signal code, Data. void SendSignal(int Code, int Data); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TrackMouseHover - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets up the manager to enable/disable hover tracking of this panel - // Arguments: Enabled, Delay (milliseconds) - + /// Sets up the manager to enable/disable hover tracking of this panel + /// @param Enabled Enabled, Delay (milliseconds) void TrackMouseHover(bool Enabled, int Delay); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: _ChangeZ - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes the Z position of a child panel. - // Arguments: Child panel, Change type. - + /// Changes the Z position of a child panel. + /// @param Child Child panel, Change type. void _ChangeZ(GUIPanel* Child, int Type); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WriteValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Writes a single value to string. - // Arguments: Value name, Value. - + /// Writes a single value to string. + /// @param Name Value name, Value. std::string WriteValue(const std::string& Name, int Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WriteValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Writes a single value to string. - // Arguments: Value name, Value. - + /// Writes a single value to string. + /// @param Name Value name, Value. std::string WriteValue(const std::string& Name, bool Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: _ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the panel. - // Arguments: GUIProperties. - + /// Applies new properties to the panel. + /// @param Props GUIProperties. void _ApplyProperties(GUIProperties* Props); private: diff --git a/Source/GUI/GUIProgressBar.cpp b/Source/GUI/GUIProgressBar.cpp index 70ce7566cc..ae57766c69 100644 --- a/Source/GUI/GUIProgressBar.cpp +++ b/Source/GUI/GUIProgressBar.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIProgressBar::GUIProgressBar(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "PROGRESSBAR"; @@ -17,8 +15,6 @@ GUIProgressBar::GUIProgressBar(GUIManager* Manager, GUIControlManager* ControlMa m_Maximum = 100; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -48,8 +44,6 @@ void GUIProgressBar::Create(const std::string& Name, int X, int Y, int Width, in m_Height = std::max(m_Height, m_MinHeight); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -77,8 +71,6 @@ void GUIProgressBar::Create(GUIProperties* Props) { m_Value = std::min(m_Value, m_Maximum); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::Destroy() { // Destroy the drawing bitmap if (m_DrawBitmap) { @@ -95,8 +87,6 @@ void GUIProgressBar::Destroy() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -104,8 +94,6 @@ void GUIProgressBar::ChangeSkin(GUISkin* Skin) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::BuildBitmap() { // Free any old bitmaps if (m_DrawBitmap) { @@ -163,8 +151,6 @@ void GUIProgressBar::BuildBitmap() { m_Skin->GetValue("ProgressBar_Indicator", "Spacing", &m_Spacing); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::Draw(GUIScreen* Screen) { // Draw the base Screen->DrawBitmap(m_DrawBitmap, m_X, m_Y, nullptr); @@ -201,14 +187,10 @@ void GUIProgressBar::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::OnMouseDown(int X, int Y, int Buttons, int Modifier) { CaptureMouse(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); if (PointInside(X, Y)) { @@ -216,24 +198,16 @@ void GUIProgressBar::OnMouseUp(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::OnMouseMove(int X, int Y, int Buttons, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIProgressBar::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::Move(int X, int Y) { GUIPanel::SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::Resize(int Width, int Height) { // Make sure the control isn't too small Width = std::max(Width, m_MinWidth); @@ -245,22 +219,16 @@ void GUIProgressBar::Resize(int Width, int Height) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::StoreProperties() { m_Properties.AddVariable("Minimum", m_Minimum); m_Properties.AddVariable("Maximum", m_Maximum); m_Properties.AddVariable("Value", m_Value); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::SetValue(int Value) { int OldValue = m_Value; m_Value = Value; @@ -275,38 +243,26 @@ void GUIProgressBar::SetValue(int Value) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIProgressBar::GetValue() const { return m_Value; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::SetMinimum(int Minimum) { m_Minimum = Minimum; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIProgressBar::GetMinimum() const { return m_Minimum; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::SetMaximum(int Maximum) { m_Maximum = Maximum; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIProgressBar::GetMaximum() const { return m_Maximum; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProgressBar::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUIProgressBar.h b/Source/GUI/GUIProgressBar.h index 2781454489..249cc354a1 100644 --- a/Source/GUI/GUIProgressBar.h +++ b/Source/GUI/GUIProgressBar.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A progressbar control class. - /// class GUIProgressBar : public GUIControl, public GUIPanel { public: @@ -15,182 +13,87 @@ namespace RTE { Changed } Notification; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIProgressBar - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIProgressBar object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUIProgressBar object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUIProgressBar(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseMove - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse moves (over the panel, or when captured). - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse moves (over the panel, or when captured). + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "PROGRESSBAR"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the value. - // Arguments: Value. - + /// Sets the value. + /// @param Value Value. void SetValue(int Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the value. - // Arguments: None. - + /// Gets the value. int GetValue() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMinimum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the minimum. - // Arguments: Minimum. - + /// Sets the minimum. + /// @param Minimum Minimum. void SetMinimum(int Minimum); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMinimum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the minimum. - // Arguments: None. - + /// Gets the minimum. int GetMinimum() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMaximum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the maximum. - // Arguments: Maximum. - + /// Sets the maximum. + /// @param Maximum Maximum. void SetMaximum(int Maximum); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaximum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the maximum. - // Arguments: None. - + /// Gets the maximum. int GetMaximum() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: @@ -202,12 +105,7 @@ namespace RTE { int m_Value; int m_Spacing; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the progressbar bitmap to draw. - // Arguments: None. - + /// Create the progressbar bitmap to draw. void BuildBitmap(); }; }; // namespace RTE diff --git a/Source/GUI/GUIProperties.cpp b/Source/GUI/GUIProperties.cpp index 622320d54a..a05add064f 100644 --- a/Source/GUI/GUIProperties.cpp +++ b/Source/GUI/GUIProperties.cpp @@ -2,28 +2,20 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIProperties::GUIProperties(const std::string& Name) { m_Name = Name; m_VariableList.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIProperties::GUIProperties() { m_Name = ""; m_VariableList.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIProperties::~GUIProperties() { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProperties::Clear() { // Free the list std::vector::iterator it; @@ -37,8 +29,6 @@ void GUIProperties::Clear() { m_VariableList.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProperties::AddVariable(const std::string& Variable, const std::string& Value) { // If this property already exists, just update it std::string Val; @@ -55,16 +45,12 @@ void GUIProperties::AddVariable(const std::string& Variable, const std::string& m_VariableList.push_back(Prop); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProperties::AddVariable(const std::string& Variable, char* Value) { std::string Val = Value; AddVariable(Variable, Val); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProperties::AddVariable(const std::string& Variable, int Value) { char buf[32]; std::snprintf(buf, sizeof(buf), "%i", Value); @@ -72,15 +58,11 @@ void GUIProperties::AddVariable(const std::string& Variable, int Value) { AddVariable(Variable, Val); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProperties::AddVariable(const std::string& Variable, bool Value) { std::string Val = Value ? "True" : "False"; AddVariable(Variable, Val); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIProperties::SetValue(const std::string& Variable, const std::string& Value) { // Find the property std::vector::iterator it; @@ -99,8 +81,6 @@ bool GUIProperties::SetValue(const std::string& Variable, const std::string& Val return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIProperties::SetValue(const std::string& Variable, int Value) { char buf[64]; std::snprintf(buf, sizeof(buf), "%i", Value); @@ -108,8 +88,6 @@ bool GUIProperties::SetValue(const std::string& Variable, int Value) { return SetValue(Variable, buf); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProperties::Update(GUIProperties* Props, bool Add) { assert(Props); @@ -125,8 +103,6 @@ void GUIProperties::Update(GUIProperties* Props, bool Add) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIProperties::GetValue(const std::string& Variable, std::string* Value) { // Find the property std::vector::iterator it; @@ -145,8 +121,6 @@ bool GUIProperties::GetValue(const std::string& Variable, std::string* Value) { return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIProperties::GetValue(const std::string& Variable, std::string* Array, int MaxArraySize) { assert(Array); @@ -177,8 +151,6 @@ int GUIProperties::GetValue(const std::string& Variable, std::string* Array, int return count; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIProperties::GetValue(const std::string& Variable, int* Array, int MaxArraySize) { assert(Array); @@ -209,8 +181,6 @@ int GUIProperties::GetValue(const std::string& Variable, int* Array, int MaxArra return count; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIProperties::GetValue(const std::string& Variable, int* Value) { assert(Value); @@ -226,8 +196,6 @@ bool GUIProperties::GetValue(const std::string& Variable, int* Value) { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIProperties::GetValue(const std::string& Variable, unsigned long* Value) { assert(Value); @@ -243,8 +211,6 @@ bool GUIProperties::GetValue(const std::string& Variable, unsigned long* Value) return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIProperties::GetValue(const std::string& Variable, bool* Value) { assert(Value); @@ -266,14 +232,10 @@ bool GUIProperties::GetValue(const std::string& Variable, bool* Value) { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIProperties::GetName() const { return m_Name; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIProperties::ToString() { std::string OutString = ""; @@ -290,14 +252,10 @@ std::string GUIProperties::ToString() { return OutString; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIProperties::GetCount() const { return m_VariableList.size(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIProperties::GetVariable(int Index, std::string* Name, std::string* Value) { // Check for a bad index if (Index < 0 || Index >= m_VariableList.size()) { @@ -315,8 +273,6 @@ bool GUIProperties::GetVariable(int Index, std::string* Name, std::string* Value return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIProperties::SetVariable(int Index, const std::string& Name, const std::string& Value) { // Check for a bad index if (Index < 0 || Index >= m_VariableList.size()) { @@ -329,8 +285,6 @@ bool GUIProperties::SetVariable(int Index, const std::string& Name, const std::s return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIProperties::Sort(bool Ascending) { // Simple bubble sort diff --git a/Source/GUI/GUIProperties.h b/Source/GUI/GUIProperties.h index e5af9ea1b8..1e68052fa6 100644 --- a/Source/GUI/GUIProperties.h +++ b/Source/GUI/GUIProperties.h @@ -3,196 +3,99 @@ namespace RTE { - /// /// A class containing properties for controls and skins. - /// class GUIProperties { public: - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIProperties object in - // system memory. - // Arguments: Name of section. - + /// Constructor method used to instantiate a GUIProperties object in + /// system memory. + /// @param Name Name of section. explicit GUIProperties(const std::string& Name); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIProperties object in - // system memory. - // Arguments: None. - + /// Constructor method used to instantiate a GUIProperties object in + /// system memory. GUIProperties(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: GUIProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to free a GUIProperties object in system - // memory. - // Arguments: None. - + /// Destructor method used to free a GUIProperties object in system + /// memory. ~GUIProperties(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the properties. - // Arguments: None. - + /// Clears the properties. void Clear(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddVariable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a new variable to the properties - // Arguments: Variable, Value - + /// Adds a new variable to the properties + /// @param Variable Variable, Value void AddVariable(const std::string& Variable, const std::string& Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddVariable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a new variable to the properties - // Arguments: Variable, Value - + /// Adds a new variable to the properties + /// @param Variable Variable, Value void AddVariable(const std::string& Variable, char* Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddVariable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a new variable to the properties - // Arguments: Variable, Value - + /// Adds a new variable to the properties + /// @param Variable Variable, Value void AddVariable(const std::string& Variable, int Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddVariable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a new variable to the properties - // Arguments: Variable, Value - + /// Adds a new variable to the properties + /// @param Variable Variable, Value void AddVariable(const std::string& Variable, bool Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes the value of a property. - // Arguments: Variable, Value - // Returns: True if the variable was set. Otherwise false. - + /// Changes the value of a property. + /// @param Variable Variable, Value + /// @return True if the variable was set. Otherwise false. bool SetValue(const std::string& Variable, const std::string& Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes the value of a property. - // Arguments: Variable, Value - // Returns: True if the variable was set. Otherwise false. - + /// Changes the value of a property. + /// @param Variable Variable, Value + /// @return True if the variable was set. Otherwise false. bool SetValue(const std::string& Variable, int Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the properties with properties from another instance. - // Arguments: Pointer to a Properties class, whether to add variables. - + /// Updates the properties with properties from another instance. + /// @param Props Pointer to a Properties class, whether to add variables. void Update(GUIProperties* Props, bool Add = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a string value - // Arguments: Variable, String pointer - + /// Gets a string value + /// @param Variable Variable, String pointer bool GetValue(const std::string& Variable, std::string* Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a string array of values - // Arguments: Variable, String array, max size of array - // Returns: Number of elements read - + /// Gets a string array of values + /// @param Variable Variable, String array, max size of array + /// @return Number of elements read int GetValue(const std::string& Variable, std::string* Array, int MaxArraySize); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets an integer array of values - // Arguments: Variable, Integer array, max size of array - // Returns: Number of elements read - + /// Gets an integer array of values + /// @param Variable Variable, Integer array, max size of array + /// @return Number of elements read int GetValue(const std::string& Variable, int* Array, int MaxArraySize); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a single interger - // Arguments: Variable, Integer pointer - + /// Gets a single interger + /// @param Variable Variable, Integer pointer bool GetValue(const std::string& Variable, int* Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a single unsigned interger - // Arguments: Variable, Unsigned Integer pointer - + /// Gets a single unsigned interger + /// @param Variable Variable, Unsigned Integer pointer bool GetValue(const std::string& Variable, unsigned long* Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a boolean value - // Arguments: Variable, Boolean pointer - + /// Gets a boolean value + /// @param Variable Variable, Boolean pointer bool GetValue(const std::string& Variable, bool* Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the property name - + /// Gets the property name std::string GetName() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ToString - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Converts the properties to a string - + /// Converts the properties to a string std::string ToString(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the variable count in the properties - + /// Gets the variable count in the properties int GetCount() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetVariable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a variable based on index - + /// Gets a variable based on index bool GetVariable(int Index, std::string* Name, std::string* Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetVariable - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets a variable based on index - + /// Sets a variable based on index bool SetVariable(int Index, const std::string& Name, const std::string& Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Sort - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sorts the list by variable name - // Arguments: True for ascending, False for descending - + /// Sorts the list by variable name + /// @param Ascending True for ascending, False for descending void Sort(bool Ascending); private: diff --git a/Source/GUI/GUIPropertyPage.cpp b/Source/GUI/GUIPropertyPage.cpp index f26d85ff13..5717ae68d2 100644 --- a/Source/GUI/GUIPropertyPage.cpp +++ b/Source/GUI/GUIPropertyPage.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPropertyPage::GUIPropertyPage(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "PROPERTYPAGE"; @@ -18,8 +16,6 @@ GUIPropertyPage::GUIPropertyPage(GUIManager* Manager, GUIControlManager* Control m_TextPanelList.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -74,8 +70,6 @@ void GUIPropertyPage::Create(const std::string& Name, int X, int Y, int Width, i } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -120,8 +114,6 @@ void GUIPropertyPage::Create(GUIProperties* Props) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::Destroy() { // Free the drawing bitmap if (m_DrawBitmap) { @@ -138,8 +130,6 @@ void GUIPropertyPage::Destroy() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -152,8 +142,6 @@ void GUIPropertyPage::ChangeSkin(GUISkin* Skin) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::BuildBitmap() { // Free any old bitmap if (m_DrawBitmap) { @@ -185,8 +173,6 @@ void GUIPropertyPage::BuildBitmap() { m_LineColor = m_Skin->ConvertColor(m_LineColor, m_DrawBitmap->GetColorDepth()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::Draw(GUIScreen* Screen) { if (m_DrawBitmap) { m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, nullptr); @@ -219,8 +205,6 @@ void GUIPropertyPage::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::OnMouseDown(int X, int Y, int Buttons, int Modifier) { if (Buttons & MOUSE_LEFT) { // Push the button down @@ -232,48 +216,32 @@ void GUIPropertyPage::OnMouseDown(int X, int Y, int Buttons, int Modifier) { SetFocus(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::OnMouseUp(int X, int Y, int Buttons, int Modifier) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::OnMouseEnter(int X, int Y, int Buttons, int Modifier) { } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::OnMouseMove(int X, int Y, int Buttons, int Modifier) { if (!(Buttons & MOUSE_LEFT) || !IsCaptured()) { return; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIPropertyPage::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::Move(int X, int Y) { GUIPanel::SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::Resize(int Width, int Height) { // Make sure the control isn't too small Width = std::max(Width, m_MinWidth); @@ -286,14 +254,10 @@ void GUIPropertyPage::Resize(int Width, int Height) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::StoreProperties() { // Note: This is for saving the control, not related directly to our control type } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::SetPropertyValues(GUIProperties* Props) { m_PageValues.Clear(); m_PageValues.Update(Props, true); @@ -315,14 +279,10 @@ void GUIPropertyPage::SetPropertyValues(GUIProperties* Props) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIProperties* GUIPropertyPage::GetPropertyValues() { return &m_PageValues; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::ReceiveSignal(GUIPanel* Source, int Code, int Data) { assert(Source); @@ -353,8 +313,6 @@ void GUIPropertyPage::ReceiveSignal(GUIPanel* Source, int Code, int Data) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIPropertyPage::InvokeUpdate() { bool Changed = false; @@ -377,8 +335,6 @@ bool GUIPropertyPage::InvokeUpdate() { return Changed; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIPropertyPage::ClearValues() { m_PageValues.Clear(); @@ -390,8 +346,6 @@ void GUIPropertyPage::ClearValues() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIPropertyPage::HasTextFocus() { std::vector::iterator it; for (it = m_TextPanelList.begin(); it != m_TextPanelList.end(); it++) { diff --git a/Source/GUI/GUIPropertyPage.h b/Source/GUI/GUIPropertyPage.h index 8e1b32d853..de589b5a33 100644 --- a/Source/GUI/GUIPropertyPage.h +++ b/Source/GUI/GUIPropertyPage.h @@ -6,9 +6,7 @@ namespace RTE { - /// /// A property page control class. - /// class GUIPropertyPage : public GUIControl, public GUIPanel { public: @@ -18,191 +16,91 @@ namespace RTE { Enter // A text panel has lost focus or the enter key was hit } Notification; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIPropertyPage - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIPropertyPage object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUIPropertyPage object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUIPropertyPage(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseMove - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse moves (over the panel, or when captured). - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse moves (over the panel, or when captured). + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseEnter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse enters the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse enters the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseLeave - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse leaves the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse leaves the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "PROPERTYPAGE"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPropertyValues - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Refreshes the page with new variables & values. - // Arguments: GUIProperties. - + /// Refreshes the page with new variables & values. + /// @param Props GUIProperties. void SetPropertyValues(GUIProperties* Props); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPropertyValues - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the properties in the page. - // Arguments: None. - + /// Gets the properties in the page. GUIProperties* GetPropertyValues(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReceiveSignal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when receiving a signal. - // Arguments: Signal source, Signal code, Signal data. - + /// Called when receiving a signal. + /// @param Source Signal source, Signal code, Signal data. void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearValues - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the property page values. - // Arguments: None. - + /// Clears the property page values. void ClearValues(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: InvokeUpdate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Invokes an explicit update on text panels to property page. - // Arguments: None. - // Returns: Boolean whether or not any values have changed. - + /// Invokes an explicit update on text panels to property page. + /// @return Boolean whether or not any values have changed. bool InvokeUpdate(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HasTextFocus - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks if any of the visible text panels have focus. - // Arguments: None. - + /// Checks if any of the visible text panels have focus. bool HasTextFocus(); private: @@ -213,12 +111,7 @@ namespace RTE { std::vector m_TextPanelList; GUIScrollPanel* m_VertScroll; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the property page bitmap to draw. - // Arguments: None. - + /// Create the property page bitmap to draw. void BuildBitmap(); }; }; // namespace RTE diff --git a/Source/GUI/GUIRadioButton.cpp b/Source/GUI/GUIRadioButton.cpp index 1bcd481099..22d897e6d7 100644 --- a/Source/GUI/GUIRadioButton.cpp +++ b/Source/GUI/GUIRadioButton.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIRadioButton::GUIRadioButton(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "RADIOBUTTON"; @@ -17,8 +15,6 @@ GUIRadioButton::GUIRadioButton(GUIManager* Manager, GUIControlManager* ControlMa m_Text = ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -48,8 +44,6 @@ void GUIRadioButton::Create(const std::string& Name, int X, int Y, int Width, in m_Height = std::max(m_Height, m_MinHeight); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -73,8 +67,6 @@ void GUIRadioButton::Create(GUIProperties* Props) { Props->GetValue("Checked", &m_Checked); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -82,8 +74,6 @@ void GUIRadioButton::ChangeSkin(GUISkin* Skin) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::BuildBitmap() { std::string Filename; unsigned long ColorIndex = 0; @@ -129,8 +119,6 @@ void GUIRadioButton::BuildBitmap() { SetRect(&m_ImageRects[3], Values[0], Values[1], Values[0] + Values[2], Values[1] + Values[3]); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::Draw(GUIScreen* Screen) { if (!m_Image) { return; @@ -179,8 +167,6 @@ void GUIRadioButton::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::OnMouseDown(int X, int Y, int Buttons, int Modifier) { if (Buttons & MOUSE_LEFT) { // Push the checkbox down @@ -191,8 +177,6 @@ void GUIRadioButton::OnMouseDown(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); @@ -204,32 +188,22 @@ void GUIRadioButton::OnMouseUp(int X, int Y, int Buttons, int Modifier) { AddEvent(GUIEvent::Notification, UnPushed, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::OnMouseEnter(int X, int Y, int Buttons, int Modifier) { m_Mouseover = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { m_Mouseover = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIRadioButton::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::Move(int X, int Y) { GUIPanel::SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::Resize(int Width, int Height) { // Make sure the control isn't too small Width = std::max(Width, m_MinWidth); @@ -241,21 +215,15 @@ void GUIRadioButton::Resize(int Width, int Height) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::StoreProperties() { m_Properties.AddVariable("Text", m_Text); m_Properties.AddVariable("Checked", m_Checked); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::SetCheck(bool Check) { // Nothing to do if already in the same state if (m_Checked == Check) { @@ -294,26 +262,18 @@ void GUIRadioButton::SetCheck(bool Check) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIRadioButton::GetCheck() const { return m_Checked; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::SetText(const std::string& Text) { m_Text = Text; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIRadioButton::GetText() const { return m_Text; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIRadioButton::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUIRadioButton.h b/Source/GUI/GUIRadioButton.h index 1d4bc69f55..212834f733 100644 --- a/Source/GUI/GUIRadioButton.h +++ b/Source/GUI/GUIRadioButton.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A radiobutton control class. - /// class GUIRadioButton : public GUIControl, public GUIPanel { public: @@ -16,166 +14,81 @@ namespace RTE { Changed, } Notification; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIRadioButton - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIRadioButton object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUIRadioButton object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUIRadioButton(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseEnter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse enters the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse enters the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseLeave - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse leaves the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse leaves the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "RADIOBUTTON"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the check state. - // Arguments: State. - + /// Sets the check state. + /// @param Check State. void SetCheck(bool Check); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the check state. - // Arguments: None. - + /// Gets the check state. bool GetCheck() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the text. - // Arguments: Text. - + /// Sets the text. + /// @param Text Text. void SetText(const std::string& Text); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the text. - // Arguments: None. - + /// Gets the text. std::string GetText() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: @@ -186,12 +99,7 @@ namespace RTE { int m_Mouseover; std::string m_Text; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the checkbox bitmap to draw. - // Arguments: None. - + /// Create the checkbox bitmap to draw. void BuildBitmap(); }; }; // namespace RTE diff --git a/Source/GUI/GUIReader.cpp b/Source/GUI/GUIReader.cpp index 4b26e02f38..e7f5eb49ff 100644 --- a/Source/GUI/GUIReader.cpp +++ b/Source/GUI/GUIReader.cpp @@ -3,13 +3,9 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader::StreamInfo::StreamInfo(std::ifstream* stream, const std::string& filePath, int currentLine, int prevIndent) : Stream(stream), FilePath(filePath), CurrentLine(currentLine), PreviousIndent(prevIndent) {} - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIReader::Clear() { m_Stream = nullptr; m_FilePath.clear(); @@ -23,14 +19,10 @@ namespace RTE { m_SkipIncludes = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader::GUIReader() { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIReader::Create(const std::string& fileName) { m_FilePath = std::filesystem::path(fileName).generic_string(); @@ -44,38 +36,26 @@ namespace RTE { return m_Stream->good() ? 0 : -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::istream* GUIReader::GetStream() const { return m_Stream.get(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIReader::GetCurrentFilePath() const { return m_FilePath; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIReader::GetCurrentFileLine() const { return std::to_string(m_CurrentLine); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIReader::GetSkipIncludes() const { return m_SkipIncludes; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIReader::SetSkipIncludes(bool skip) { m_SkipIncludes = skip; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIReader::ReadLine() { DiscardEmptySpace(); @@ -105,8 +85,6 @@ namespace RTE { return TrimString(retString); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIReader::ReadPropName() { DiscardEmptySpace(); @@ -153,8 +131,6 @@ namespace RTE { return retString; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIReader::ReadPropValue() { std::string fullLine = ReadLine(); size_t valuePos = fullLine.find_first_of('='); @@ -162,8 +138,6 @@ namespace RTE { return TrimString(propValue); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIReader::NextProperty() { if (!DiscardEmptySpace() || m_EndOfStreams) { return false; @@ -177,8 +151,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIReader::TrimString(const std::string& stringToTrim) const { if (stringToTrim.empty()) { return ""; @@ -189,8 +161,6 @@ namespace RTE { return stringToTrim.substr(start, (end - start + 1)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIReader::DiscardEmptySpace() { char peek; int indent = 0; @@ -269,20 +239,14 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIReader::ReaderOK() const { return m_Stream.get() && !m_Stream->fail() && m_Stream->is_open(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIReader::ReportError(const std::string& errorDesc) const { GUIAbort(errorDesc + "\nError happened in " + m_FilePath + " at line " + std::to_string(m_CurrentLine) + "!"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIReader::StartIncludeFile() { // Get the file path from the current stream before pushing it into the StreamStack, otherwise we can't open a new stream after releasing it because we can't read. std::string includeFilePath = std::filesystem::path(ReadPropValue()).generic_string(); @@ -315,8 +279,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIReader::EndIncludeFile() { if (m_StreamStack.empty()) { m_EndOfStreams = true; @@ -339,24 +301,18 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(bool& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(char& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(unsigned char& var) { DiscardEmptySpace(); int temp; @@ -365,56 +321,42 @@ namespace RTE { return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(short& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(unsigned short& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(int& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(unsigned int& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(long& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(unsigned long& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Yeah, this is dumb - read as double and cast. // This is because, for whatever fucking reason, iostream can save out floats at a precision that it's then unable to read... GUIReader& GUIReader::operator>>(float& var) { @@ -425,16 +367,12 @@ namespace RTE { return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(double& var) { DiscardEmptySpace(); *m_Stream >> var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIReader& GUIReader::operator>>(std::string& var) { var.assign(ReadLine()); return *this; diff --git a/Source/GUI/GUIReader.h b/Source/GUI/GUIReader.h index 579ed6ddd3..091de0c449 100644 --- a/Source/GUI/GUIReader.h +++ b/Source/GUI/GUIReader.h @@ -3,119 +3,85 @@ namespace RTE { - /// /// Reads GUI objects from std::istreams. - /// class GUIReader { public: #pragma region Creation - /// /// Constructor method used to instantiate a GUIReader object in system memory. Create() should be called before using the object. - /// GUIReader(); - /// /// Makes the GUIReader object ready for use. - /// - /// Path to the file to open for reading. If the file doesn't exist the stream will fail to open. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param fileName Path to the file to open for reading. If the file doesn't exist the stream will fail to open. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const std::string& fileName); #pragma endregion #pragma region Getters and Setters - /// /// Gets a pointer to the istream of this reader. - /// - /// A pointer to the istream object for this reader. + /// @return A pointer to the istream object for this reader. std::istream* GetStream() const; - /// /// Gets the path of the current file this reader is reading from. - /// - /// A string with the path, relative from the working directory. + /// @return A string with the path, relative from the working directory. std::string GetCurrentFilePath() const; - /// /// Gets the line of the current file line this reader is reading from. - /// - /// A string with the line number that will be read from next. + /// @return A string with the line number that will be read from next. std::string GetCurrentFileLine() const; - /// /// Returns true if reader was told to skip InlcudeFile statements - /// - /// Returns whether reader was told to skip included files. + /// @return Returns whether reader was told to skip included files. bool GetSkipIncludes() const; - /// /// Set whether this reader should skip included files. - /// - /// - /// The whitespace-trimmed std::string that will hold the next property's name. + /// @return The whitespace-trimmed std::string that will hold the next property's name. std::string ReadPropName(); - /// /// Reads the next property value from the context object GUIReader's stream after eating all whitespace including newlines up till the first newline char. /// Basically gets anything after the last "=" and up to the next newline after that. - /// - /// The whitespace-trimmed std::string that will hold the next property value. + /// @return The whitespace-trimmed std::string that will hold the next property value. std::string ReadPropValue(); - /// /// Lines up the reader with the next property of the current object. - /// - /// Whether there are any more properties to be read by the current object. + /// @return Whether there are any more properties to be read by the current object. bool NextProperty(); - /// /// Takes out whitespace from the beginning and the end of a string. - /// - /// String to remove whitespace from. - /// The string that was passed in, sans whitespace in the front and end. + /// @param stringToTrim String to remove whitespace from. + /// @return The string that was passed in, sans whitespace in the front and end. std::string TrimString(const std::string& stringToTrim) const; - /// /// Discards all whitespace, newlines and comment lines (which start with '//') so that the next thing to be read will be actual data. - /// - /// Whether there is more data to read from the file streams after this eat. + /// @return Whether there is more data to read from the file streams after this eat. bool DiscardEmptySpace(); #pragma endregion #pragma region Reader Status - /// /// Shows whether this is still OK to read from. If file isn't present, etc, this will return false. - /// - /// Whether this GUIReader's stream is OK or not. + /// @return Whether this GUIReader's stream is OK or not. bool ReaderOK() const; - /// /// Makes an error message box pop up for the user that tells them something went wrong with the reading, and where. - /// - /// The message describing what's wrong. + /// @param errorDesc The message describing what's wrong. void ReportError(const std::string& errorDesc) const; #pragma endregion #pragma region Operator Overloads - /// /// Stream extraction operator overloads for all the elemental types. - /// - /// A reference to the variable that will be filled by the extracted data. - /// A GUIReader reference for further use in an expression. + /// @param var A reference to the variable that will be filled by the extracted data. + /// @return A GUIReader reference for further use in an expression. GUIReader& operator>>(bool& var); GUIReader& operator>>(char& var); GUIReader& operator>>(unsigned char& var); @@ -131,13 +97,9 @@ namespace RTE { #pragma endregion protected: - /// /// A struct containing information from the currently used stream. - /// struct StreamInfo { - /// /// Constructor method used to instantiate a StreamInfo object in system memory. - /// StreamInfo(std::ifstream* stream, const std::string& filePath, int currentLine, int prevIndent); // NOTE: These members are owned by the reader that owns this struct, so are not deleted when this is destroyed. @@ -161,32 +123,24 @@ namespace RTE { int m_CurrentLine; //!< The line number the stream is on. bool m_SkipIncludes; //!< Indicates whether reader should skip included files. - /// /// When NextProperty() has returned false, indicating that there were no more properties to read on that object, /// this is incremented until it matches -m_IndentDifference, and then NextProperty will start returning true again. - /// int m_ObjectEndings; private: #pragma region Reading Operations - /// /// When ReadPropName encounters the property name "IncludeFile", it will automatically call this function to get started reading on that file. /// This will create a new stream to the include file. - /// - /// Whether the include file was found and opened ok or not. + /// @return Whether the include file was found and opened ok or not. bool StartIncludeFile(); - /// /// This should be called when end-of-file is detected in an included file stream. /// It will destroy the current stream pop the top stream off the stream stack to resume reading from it instead. - /// - /// Whether there were any stream on the stack to resume. + /// @return Whether there were any stream on the stack to resume. bool EndIncludeFile(); #pragma endregion - /// /// Clears all the member variables of this GUIReader, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/GUI/GUIScrollPanel.cpp b/Source/GUI/GUIScrollPanel.cpp index e6f9dcf6b0..d8c2d1b4a7 100644 --- a/Source/GUI/GUIScrollPanel.cpp +++ b/Source/GUI/GUIScrollPanel.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIScrollPanel::GUIScrollPanel(GUIManager* Manager) : GUIPanel(Manager) { m_Skin = nullptr; @@ -20,8 +18,6 @@ GUIScrollPanel::GUIScrollPanel(GUIManager* Manager) : m_ValueResolution = 1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIScrollPanel::GUIScrollPanel() : GUIPanel() { m_Skin = nullptr; @@ -37,8 +33,6 @@ GUIScrollPanel::GUIScrollPanel() : m_ValueResolution = 1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::Create(int X, int Y, int Width, int Height) { m_X = X; m_Y = Y; @@ -56,8 +50,6 @@ void GUIScrollPanel::Create(int X, int Y, int Width, int Height) { m_ValueResolution = 1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::Destroy() { // Destroy the three bitmaps for (int i = 0; i < 3; i++) { @@ -69,8 +61,6 @@ void GUIScrollPanel::Destroy() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::LoadProps(GUIProperties* Props) { assert(Props); @@ -92,8 +82,6 @@ void GUIScrollPanel::LoadProps(GUIProperties* Props) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::ChangeSkin(GUISkin* Skin) { assert(Skin); @@ -103,8 +91,6 @@ void GUIScrollPanel::ChangeSkin(GUISkin* Skin) { BuildBitmap(true, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::BuildBitmap(bool UpdateSize, bool UpdateKnob) { // It is normal if this function is called but the skin has not been set so we just ignore the call if the skin has not been set if (!m_Skin) { @@ -194,8 +180,6 @@ void GUIScrollPanel::BuildBitmap(bool UpdateSize, bool UpdateKnob) { m_RebuildKnob = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::BuildButton(const std::string& ArrowName, int Y, int Width, int Height) { // Create the buttons m_Skin->BuildStandardRect(m_DrawBitmap[ButtonStates], "ScrollButton_Up", 0, Y, Width, Height); @@ -228,8 +212,6 @@ void GUIScrollPanel::BuildButton(const std::string& ArrowName, int Y, int Width, Arrow->DrawTrans(m_DrawBitmap[ButtonStates], Width + (Width / 2) - (Values[2] / 2) + 1, Y + (Height / 2) - (Values[3] / 2) + 1, &Rect); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::BuildBackground() { std::string Filename; m_Skin->GetValue("ScrollBackground", "Filename", &Filename); @@ -252,16 +234,12 @@ void GUIScrollPanel::BuildBackground() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::BuildKnob(const std::string& Section, int X, int Y, int Width, int Height) { if (m_DrawBitmap[KnobStates]) { m_Skin->BuildStandardRect(m_DrawBitmap[KnobStates], Section, X, Y, Width, Height); } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::SetMinimum(int Min) { m_Minimum = Min; m_Minimum = std::min(m_Minimum, m_Maximum); @@ -270,8 +248,6 @@ void GUIScrollPanel::SetMinimum(int Min) { m_RebuildKnob = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::SetMaximum(int Max) { m_Maximum = Max; m_Maximum = std::max(m_Maximum, m_Minimum); @@ -280,15 +256,11 @@ void GUIScrollPanel::SetMaximum(int Max) { m_RebuildKnob = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::SetValue(int Value) { m_Value = std::clamp(Value, m_Minimum, m_Maximum); CalculateKnob(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::SetPageSize(int PageSize) { m_PageSize = PageSize; m_PageSize = std::max(m_PageSize, 1); @@ -297,8 +269,6 @@ void GUIScrollPanel::SetPageSize(int PageSize) { m_RebuildKnob = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::SetOrientation(int Orientation) { m_Orientation = Orientation; @@ -307,27 +277,19 @@ void GUIScrollPanel::SetOrientation(int Orientation) { m_RebuildSize = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIScrollPanel::GetValue() const { return m_Value; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIScrollPanel::GetPageSize() const { return m_PageSize; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::SetSmallChange(int SmallChange) { m_SmallChange = SmallChange; m_SmallChange = std::max(m_SmallChange, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::Draw(GUIScreen* Screen) { GUIRect Rect; @@ -380,8 +342,6 @@ void GUIScrollPanel::Draw(GUIScreen* Screen) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { m_ButtonPushed[0] = m_ButtonPushed[1] = false; m_GrabbedKnob = false; @@ -490,8 +450,6 @@ void GUIScrollPanel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::OnMouseUp(int X, int Y, int Buttons, int Modifier) { // No more grabs m_ButtonPushed[0] = m_ButtonPushed[1] = false; @@ -501,8 +459,6 @@ void GUIScrollPanel::OnMouseUp(int X, int Y, int Buttons, int Modifier) { SendSignal(Release, Buttons); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::OnMouseMove(int X, int Y, int Buttons, int Modifier) { int KnobTop = 0; int MoveLength = 1; @@ -549,8 +505,6 @@ void GUIScrollPanel::OnMouseMove(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::OnMouseHover(int X, int Y, int Buttons, int Modifier) { // Ignore if the left mouse button is not down if (!(Buttons & MOUSE_LEFT)) { @@ -634,8 +588,6 @@ void GUIScrollPanel::OnMouseHover(int X, int Y, int Buttons, int Modifier) { TrackMouseHover(true, 50); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::SetSize(int Width, int Height) { // If there is no change in size, ignore the call if (m_Width == Width && m_Height == Height) { @@ -648,8 +600,6 @@ void GUIScrollPanel::SetSize(int Width, int Height) { m_RebuildSize = true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::CalculateKnob() { int MoveLength = 1; @@ -691,8 +641,6 @@ void GUIScrollPanel::CalculateKnob() { m_KnobPosition = std::min(m_KnobPosition, MoveLength - m_KnobLength); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::AdjustValue(int Delta) { int OldValue = m_Value; @@ -710,8 +658,6 @@ void GUIScrollPanel::AdjustValue(int Delta) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollPanel::SaveProps(GUIProperties* Props) const { assert(Props); @@ -723,32 +669,22 @@ void GUIScrollPanel::SaveProps(GUIProperties* Props) const { Props->AddVariable("SmallChange", m_SmallChange); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIScrollPanel::GetMinimum() const { return m_Minimum; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIScrollPanel::GetMaximum() const { return m_Maximum; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIScrollPanel::GetOrientation() const { return m_Orientation; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIScrollPanel::GetSmallChange() const { return m_SmallChange; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIScrollPanel::GetValueResolution() const { return m_ValueResolution; } diff --git a/Source/GUI/GUIScrollPanel.h b/Source/GUI/GUIScrollPanel.h index 1d2899375a..a61f67a4b1 100644 --- a/Source/GUI/GUIScrollPanel.h +++ b/Source/GUI/GUIScrollPanel.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A scrollbar panel class used for controls requiring a scrollbar. - /// class GUIScrollPanel : public GUIPanel { public: @@ -29,221 +27,106 @@ namespace RTE { Release }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIScrollPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIScrollPanel object in - // system memory. - // Arguments: GUIManager. - + /// Constructor method used to instantiate a GUIScrollPanel object in + /// system memory. + /// @param Manager GUIManager. explicit GUIScrollPanel(GUIManager* Manager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIScrollPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIScrollPanel object in - // system memory. - // Arguments: None. - + /// Constructor method used to instantiate a GUIScrollPanel object in + /// system memory. GUIScrollPanel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the scrollpanel - // Arguments: Position, Size. - + /// Create the scrollpanel + /// @param X Position, Size. void Create(int X, int Y, int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the panel has been destroyed. - // Arguments: None. - + /// Called when the panel has been destroyed. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseMove - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse moves (over the panel, or when captured). - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse moves (over the panel, or when captured). + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseHover - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse is hovering over the panel (has to be enabled) - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse is hovering over the panel (has to be enabled) + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseHover(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the size of the panel. - // Arguments: Width, Height. - + /// Adjusts the size of the panel. + /// @param Width Width, Height. void SetSize(int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMinimum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the minimum value for the scrollpanel - // Arguments: Minimum value. - + /// Sets the minimum value for the scrollpanel + /// @param Min Minimum value. void SetMinimum(int Min); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMinimum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the minimum value for the scrollpanel - // Arguments: None. - + /// Gets the minimum value for the scrollpanel int GetMinimum() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMaximum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the maximum value for the scrollpanel - // Arguments: Maximum value. - + /// Sets the maximum value for the scrollpanel + /// @param Max Maximum value. void SetMaximum(int Max); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaximum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the maximum value for the scrollpanel - // Arguments: None. - + /// Gets the maximum value for the scrollpanel int GetMaximum() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current value for the scrollpanel - // Arguments: Value. - + /// Sets the current value for the scrollpanel + /// @param Value Value. void SetValue(int Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current value of the scrollpanel. - // Arguments: None. - + /// Gets the current value of the scrollpanel. int GetValue() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPageSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the page size value for the scrollpanel. - // Arguments: PageSize. - + /// Sets the page size value for the scrollpanel. + /// @param PageSize PageSize. void SetPageSize(int PageSize); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPageSize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the size of the page. - // Arguments: None. - + /// Gets the size of the page. int GetPageSize() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOrientation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the orientation of the scrollpanel. - // Arguments: Orientation. - + /// Sets the orientation of the scrollpanel. + /// @param Orientation Orientation. void SetOrientation(int Orientation); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOrientation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the orientation of the scrollpanel. - // Arguments: None. - + /// Gets the orientation of the scrollpanel. int GetOrientation() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSmallChange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the small change value. - // Arguments: SmallChange. - + /// Sets the small change value. + /// @param SmallChange SmallChange. void SetSmallChange(int SmallChange); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSmallChange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the small change value. - // Arguments: None. - + /// Gets the small change value. int GetSmallChange() const; - /// /// Gets the value resolution for this scroll panel. - /// - /// The value resolution + /// @return The value resolution int GetValueResolution() const; protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadProps - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Load values from a property class. - // Arguments: Properties. - + /// Load values from a property class. + /// @param Props Properties. void LoadProps(GUIProperties* Props); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SaveProps - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Save values to a property class. - // Arguments: Properties. - + /// Save values to a property class. + /// @param Props Properties. void SaveProps(GUIProperties* Props) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Build the bitmap. - // Arguments: None. - + /// Build the bitmap. void BuildBitmap(bool UpdateSize, bool UpdateKnob); private: @@ -272,44 +155,21 @@ namespace RTE { int m_GrabbedSide; int m_ValueResolution; //!< How much the value increases/decreases on each mouse wheel change when scrolling. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildButton - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Build a button. - // Arguments: ArrowName, Width, Height. - + /// Build a button. + /// @param ArrowName ArrowName, Width, Height. void BuildButton(const std::string& ArrowName, int Y, int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBackground - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Build the background. - // Arguments: None. - + /// Build the background. void BuildBackground(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildKnob - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Build the knob. - // Arguments: None. - + /// Build the knob. void BuildKnob(const std::string& Section, int X, int Y, int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculateKnob - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculate the knob size and position. - // Arguments: None. - + /// Calculate the knob size and position. void CalculateKnob(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AdjustValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adjusts the value. - // Arguments: Delta movement. - + /// Adjusts the value. + /// @param Delta Delta movement. void AdjustValue(int Delta); }; }; // namespace RTE diff --git a/Source/GUI/GUIScrollbar.cpp b/Source/GUI/GUIScrollbar.cpp index 3f4ba3f82f..a202f53d3e 100644 --- a/Source/GUI/GUIScrollbar.cpp +++ b/Source/GUI/GUIScrollbar.cpp @@ -3,16 +3,12 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIScrollbar::GUIScrollbar(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIScrollPanel(Manager) { m_ControlID = "SCROLLBAR"; m_ControlManager = ControlManager; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -42,8 +38,6 @@ void GUIScrollbar::Create(const std::string& Name, int X, int Y, int Width, int GUIScrollPanel::Create(X, Y, w, h); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -67,26 +61,18 @@ void GUIScrollbar::Create(GUIProperties* Props) { GUIScrollPanel::LoadProps(Props); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::Destroy() { GUIScrollPanel::Destroy(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::ChangeSkin(GUISkin* Skin) { GUIScrollPanel::ChangeSkin(Skin); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUIScrollbar::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::ReceiveSignal(GUIPanel* Source, int Code, int Data) { assert(Source); @@ -96,8 +82,6 @@ void GUIScrollbar::ReceiveSignal(GUIPanel* Source, int Code, int Data) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::OnMouseDown(int X, int Y, int Buttons, int Modifier) { // Capture the mouse CaptureMouse(); @@ -105,8 +89,6 @@ void GUIScrollbar::OnMouseDown(int X, int Y, int Buttons, int Modifier) { GUIScrollPanel::OnMouseDown(X, Y, Buttons, Modifier); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::OnMouseUp(int X, int Y, int Buttons, int Modifier) { // Release the mouse ReleaseMouse(); @@ -114,8 +96,6 @@ void GUIScrollbar::OnMouseUp(int X, int Y, int Buttons, int Modifier) { GUIScrollPanel::OnMouseUp(X, Y, Buttons, Modifier); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) { int oldValue = GetValue(); int newValue = std::clamp(oldValue - (GetValueResolution() * ((mouseWheelChange > 0) ? 1 : -1)), GetMinimum(), GetMaximum() - GetPageSize()); @@ -125,14 +105,10 @@ void GUIScrollbar::OnMouseWheelChange(int x, int y, int modifier, int mouseWheel } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::Move(int X, int Y) { GUIScrollPanel::SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::Resize(int Width, int Height) { // Make sure the control isn't too small Width = std::max(Width, m_MinWidth); @@ -141,20 +117,14 @@ void GUIScrollbar::Resize(int Width, int Height) { GUIScrollPanel::SetSize(Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIScrollPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::StoreProperties() { GUIScrollPanel::SaveProps(&m_Properties); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIScrollbar::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUIScrollbar.h b/Source/GUI/GUIScrollbar.h index e9efa3cbdc..b58eae8d44 100644 --- a/Source/GUI/GUIScrollbar.h +++ b/Source/GUI/GUIScrollbar.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// A Scrollbar control class. - /// class GUIScrollbar : public GUIControl, public GUIScrollPanel { public: @@ -17,135 +15,69 @@ namespace RTE { } Notifications; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUIScrollbar - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUIScrollbar object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUIScrollbar object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUIScrollbar(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - /// /// Called when the mouse scroll wheel is moved. - /// - /// Mouse X position. - /// Mouse Y position. - /// Activated modifier buttons. - /// The amount of wheel movement. Positive is scroll up, negative is scroll down. + /// @param x Mouse X position. + /// @param y Mouse Y position. + /// @param modifier Activated modifier buttons. + /// @param mouseWheelChange The amount of wheel movement. Positive is scroll up, negative is scroll down. void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "SCROLLBAR"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReceiveSignal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when receiving a signal. - // Arguments: Signal source, Signal code, Signal data. - + /// Called when receiving a signal. + /// @param Source Signal source, Signal code, Signal data. void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: diff --git a/Source/GUI/GUISkin.cpp b/Source/GUI/GUISkin.cpp index 00ff282c2d..dbb6c3944b 100644 --- a/Source/GUI/GUISkin.cpp +++ b/Source/GUI/GUISkin.cpp @@ -4,8 +4,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUISkin::GUISkin(GUIScreen* Screen) { m_Screen = Screen; m_FontCache.clear(); @@ -17,14 +15,10 @@ GUISkin::GUISkin(GUIScreen* Screen) { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUISkin::~GUISkin() { Destroy(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISkin::Clear() { m_PropList.clear(); m_ImageCache.clear(); @@ -32,8 +26,6 @@ void GUISkin::Clear() { m_Directory = ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUISkin::Load(const std::string& directory, const std::string& fileName) { // Destroy any previous instances Destroy(); @@ -98,8 +90,6 @@ bool GUISkin::Load(const std::string& directory, const std::string& fileName) { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUISkin::GetValue(const std::string& Section, const std::string& Variable, std::string* Value) { std::vector::iterator it; @@ -116,8 +106,6 @@ bool GUISkin::GetValue(const std::string& Section, const std::string& Variable, return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUISkin::GetValue(const std::string& Section, const std::string& Variable, int* Array, int MaxArraySize) { std::vector::iterator it; @@ -134,8 +122,6 @@ int GUISkin::GetValue(const std::string& Section, const std::string& Variable, i return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUISkin::GetValue(const std::string& Section, const std::string& Variable, int* Value) { std::vector::iterator it; @@ -152,8 +138,6 @@ bool GUISkin::GetValue(const std::string& Section, const std::string& Variable, return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUISkin::GetValue(const std::string& Section, const std::string& Variable, unsigned long* Value) { std::vector::iterator it; @@ -170,8 +154,6 @@ bool GUISkin::GetValue(const std::string& Section, const std::string& Variable, return false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISkin::Destroy() { std::vector::iterator it; @@ -213,14 +195,10 @@ void GUISkin::Destroy() { m_ImageCache.clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIBitmap* GUISkin::CreateBitmap(int Width, int Height) { return m_Screen->CreateBitmap(Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIBitmap* GUISkin::CreateBitmap(const std::string& Filename) { // Add the filename onto the current directory std::string File = m_Directory + Filename; @@ -246,8 +224,6 @@ GUIBitmap* GUISkin::CreateBitmap(const std::string& Filename) { return Bitmap; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIFont* GUISkin::GetFont(const std::string& Name) { // Check if the font is already in the list std::vector::iterator it; @@ -271,8 +247,6 @@ GUIFont* GUISkin::GetFont(const std::string& Name) { return Font; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIBitmap* GUISkin::LoadMousePointer(const std::string& Section) { std::string File; int ColorKey; @@ -291,8 +265,6 @@ GUIBitmap* GUISkin::LoadMousePointer(const std::string& Section) { return Bitmap; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISkin::DrawMouse(int Image, int X, int Y, GUIScreen* guiScreenOverride) { assert(Image >= 0 && Image <= 2); @@ -303,8 +275,6 @@ void GUISkin::DrawMouse(int Image, int X, int Y, GUIScreen* guiScreenOverride) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISkin::BuildStandardRect(GUIBitmap* Dest, const std::string& Section, int X, int Y, int Width, int Height, bool buildBG, bool buildFrame, GUIRect* borderSizes) { // Note: For a control to use a 'Standard Rect' it must use the 8 side names, a filler name and a filename property. @@ -401,8 +371,6 @@ void GUISkin::BuildStandardRect(GUIBitmap* Dest, const std::string& Section, int } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned long GUISkin::ConvertColor(unsigned long color, int targetDepth) { return m_Screen->ConvertColor(color, targetDepth); } diff --git a/Source/GUI/GUISkin.h b/Source/GUI/GUISkin.h index 24e07e0c7b..38b2bcf8f7 100644 --- a/Source/GUI/GUISkin.h +++ b/Source/GUI/GUISkin.h @@ -3,139 +3,75 @@ namespace RTE { - /// /// Skin class used for the controls to get skin details. - /// class GUISkin { public: - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUISkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUISkin object in system - // memory. - // Arguments: GUIScreen Interface. - + /// Constructor method used to instantiate a GUISkin object in system + /// memory. + /// @param Screen GUIScreen Interface. explicit GUISkin(GUIScreen* Screen); - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: GUISkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to free a GUISkin object in system - // memory. - // Arguments: None. - + /// Destructor method used to free a GUISkin object in system + /// memory. ~GUISkin(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Load - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads a skin for a directory - // Arguments: Skin directory and the file within to use - + /// Loads a skin for a directory + /// @param directory Skin directory and the file within to use bool Load(const std::string& directory, const std::string& fileName = "skin.ini"); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the data - // Arguments: None. - + /// Clears all the data void Clear(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Frees the allocated data. - // Arguments: None. - + /// Frees the allocated data. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a string value - // Arguments: Section, Variable, String pointer - + /// Gets a string value + /// @param Section Section, Variable, String pointer bool GetValue(const std::string& Section, const std::string& Variable, std::string* Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets an integer array of values - // Arguments: Section, Variable, Integer array, max size of array - // Returns: Number of elements read - + /// Gets an integer array of values + /// @param Section Section, Variable, Integer array, max size of array + /// @return Number of elements read int GetValue(const std::string& Section, const std::string& Variable, int* Array, int MaxArraySize); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a single integer value. - // Arguments: Section, Variable, Integer pointer - + /// Gets a single integer value. + /// @param Section Section, Variable, Integer pointer bool GetValue(const std::string& Section, const std::string& Variable, int* Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a single unsigned integer value. - // Arguments: Section, Variable, Unsigned Integer pointer - + /// Gets a single unsigned integer value. + /// @param Section Section, Variable, Unsigned Integer pointer bool GetValue(const std::string& Section, const std::string& Variable, unsigned long* Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a blank bitmap. - // Arguments: Width, Height. - + /// Creates a blank bitmap. + /// @param Width Width, Height. GUIBitmap* CreateBitmap(int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CreateBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a bitmap from a filename. - // Arguments: Filename. - + /// Creates a bitmap from a filename. + /// @param Filename Filename. GUIBitmap* CreateBitmap(const std::string& Filename); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFont - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a cached copy of a font. Ownership is NOT transferred! - // Arguments: None. - + /// Gets a cached copy of a font. Ownership is NOT transferred! GUIFont* GetFont(const std::string& Name); - /// /// Draws the mouse onto the screen. - /// - /// Mouse image ID. - /// Horizontal position on the screen. - /// Vertical position on the screen. - /// The GUIScreen to draw to, overriding the one passed in on construction. + /// @param Image Mouse image ID. + /// @param X Horizontal position on the screen. + /// @param Y Vertical position on the screen. + /// @param pScreen The GUIScreen to draw to, overriding the one passed in on construction. void DrawMouse(int Image, int X, int Y, GUIScreen* guiScreenOverride = nullptr); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildStandardRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Builds a bitmap from a standard skin property section. - // Arguments: Destination bitmap, Section name, Position, Size. Whether to draw the - // background and frame, a GUIRect to be filled in with the border sizes of the four sides of the built standard rect. - + /// Builds a bitmap from a standard skin property section. + /// @param Dest Destination bitmap, Section name, Position, Size. Whether to draw the + /// background and frame, a GUIRect to be filled in with the border sizes of the four sides of the built standard rect. void BuildStandardRect(GUIBitmap* Dest, const std::string& Section, int X, int Y, int Width, int Height, bool buildBG = true, bool buildFrame = true, GUIRect* borderSizes = nullptr); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ConvertColor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Converts an 8bit palette index to a valid pixel format. - // Primarily used for development in windowed mode. - // Arguments: Color value in any bit depth. Will be converted to the format specified. - // An optional target color depth that will determine what format the color - // should be converted to. If this is 0, then the current video color depth - // will be used as target. - + /// Converts an 8bit palette index to a valid pixel format. + /// Primarily used for development in windowed mode. + /// @param color Color value in any bit depth. Will be converted to the format specified. + /// @param targetDepth An optional target color depth that will determine what format the color (default: 0) + /// should be converted to. If this is 0, then the current video color depth + /// will be used as target. unsigned long ConvertColor(unsigned long color, int targetDepth = 0); private: @@ -147,12 +83,8 @@ namespace RTE { std::vector m_ImageCache; std::vector m_FontCache; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadMousePointer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads a mouse pointer image & details - // Arguments: Section name. - + /// Loads a mouse pointer image & details + /// @param Section Section name. GUIBitmap* LoadMousePointer(const std::string& Section); }; }; // namespace RTE diff --git a/Source/GUI/GUISlider.cpp b/Source/GUI/GUISlider.cpp index ea6b7e069a..3859519242 100644 --- a/Source/GUI/GUISlider.cpp +++ b/Source/GUI/GUISlider.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUISlider::GUISlider(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "SLIDER"; @@ -22,8 +20,6 @@ GUISlider::GUISlider(GUIManager* Manager, GUIControlManager* ControlManager) : m_ValueResolution = 1; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -52,8 +48,6 @@ void GUISlider::Create(const std::string& Name, int X, int Y, int Width, int Hei CalculateKnob(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -101,8 +95,6 @@ void GUISlider::Create(GUIProperties* Props) { CalculateKnob(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::Destroy() { // Destroy the draw bitmap if (m_DrawBitmap) { @@ -119,8 +111,6 @@ void GUISlider::Destroy() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -128,8 +118,6 @@ void GUISlider::ChangeSkin(GUISkin* Skin) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::BuildBitmap() { // Free any old bitmaps if (m_DrawBitmap) { @@ -188,8 +176,6 @@ void GUISlider::BuildBitmap() { CalculateKnob(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::BuildLine(const std::string& Section, GUIBitmap* SrcImage) { int Values[4]; GUIRect Rect; @@ -235,8 +221,6 @@ void GUISlider::BuildLine(const std::string& Section, GUIBitmap* SrcImage) { SrcImage->Draw(m_DrawBitmap, X, Y, &Rect); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::Draw(GUIScreen* Screen) { int X = 0; int Y = 0; @@ -269,8 +253,6 @@ void GUISlider::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::OnMouseDown(int X, int Y, int Buttons, int Modifier) { CaptureMouse(); SetFocus(); @@ -319,8 +301,6 @@ void GUISlider::OnMouseDown(int X, int Y, int Buttons, int Modifier) { AddEvent(GUIEvent::Notification, Clicked, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); @@ -334,8 +314,6 @@ void GUISlider::OnMouseUp(int X, int Y, int Buttons, int Modifier) { AddEvent(GUIEvent::Notification, Clicked, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::OnMouseMove(int X, int Y, int Buttons, int Modifier) { int MousePos = X; int KnobTop = 0; @@ -389,8 +367,6 @@ void GUISlider::OnMouseMove(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) { m_OldValue = m_Value; @@ -406,14 +382,10 @@ void GUISlider::OnMouseWheelChange(int x, int y, int modifier, int mouseWheelCha } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUISlider::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::CalculateKnob() { if (!m_KnobImage) { return; @@ -428,14 +400,10 @@ void GUISlider::CalculateKnob() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::Move(int X, int Y) { GUIPanel::SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::Resize(int Width, int Height) { // Make sure the control isn't too small Width = std::max(Width, m_MinWidth); @@ -447,42 +415,30 @@ void GUISlider::Resize(int Width, int Height) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::SetOrientation(int Orientation) { m_Orientation = Orientation; BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUISlider::GetOrientation() const { return m_Orientation; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::SetTickDirection(int TickDir) { m_TickDirection = TickDir; BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUISlider::GetTickDirection() const { return m_TickDirection; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::SetMinimum(int Minimum) { if (Minimum != m_Minimum) { m_Minimum = Minimum; @@ -493,14 +449,10 @@ void GUISlider::SetMinimum(int Minimum) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUISlider::GetMinimum() const { return m_Minimum; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::SetMaximum(int Maximum) { if (Maximum != m_Maximum) { m_Maximum = Maximum; @@ -511,14 +463,10 @@ void GUISlider::SetMaximum(int Maximum) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUISlider::GetMaximum() const { return m_Maximum; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::SetValue(int Value) { int OldValue = m_Value; @@ -530,14 +478,10 @@ void GUISlider::SetValue(int Value) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUISlider::GetValue() const { return m_Value; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::StoreProperties() { m_Properties.AddVariable("Value", m_Value); m_Properties.AddVariable("Minimum", m_Minimum); @@ -546,8 +490,6 @@ void GUISlider::StoreProperties() { m_Properties.AddVariable("TickDirection", m_TickDirection == TopLeft ? "TopLeft" : "BottomRight"); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); @@ -578,8 +520,6 @@ void GUISlider::ApplyProperties(GUIProperties* Props) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISlider::SetValueResolution(int valueRes) { if (valueRes >= 1 && valueRes <= m_Maximum - m_Minimum) { m_ValueResolution = valueRes; diff --git a/Source/GUI/GUISlider.h b/Source/GUI/GUISlider.h index 0a478372d6..ec7d3003a0 100644 --- a/Source/GUI/GUISlider.h +++ b/Source/GUI/GUISlider.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A slider control class. - /// class GUISlider : public GUIControl, public GUIPanel { public: @@ -27,229 +25,112 @@ namespace RTE { Clicked } Notification; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUISlider - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUISlider object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUISlider object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUISlider(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseMove - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse moves (over the panel, or when captured). - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse moves (over the panel, or when captured). + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - /// /// Called when the mouse scroll wheel is moved. - /// - /// Mouse X position. - /// Mouse Y position. - /// Activated modifier buttons. - /// The amount of wheel movement. Positive is scroll up, negative is scroll down. + /// @param x Mouse X position. + /// @param y Mouse Y position. + /// @param modifier Activated modifier buttons. + /// @param mouseWheelChange The amount of wheel movement. Positive is scroll up, negative is scroll down. void OnMouseWheelChange(int x, int y, int modifier, int mouseWheelChange) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "SLIDER"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOrientation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the orientation of the slider. - // Arguments: Orientation. - + /// Sets the orientation of the slider. + /// @param Orientation Orientation. void SetOrientation(int Orientation); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOrientation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the orientation of the slider. - // Arguments: None. - + /// Gets the orientation of the slider. int GetOrientation() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetTickDirection - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the direction of the ticks. - // Arguments: TickDir. - + /// Sets the direction of the ticks. + /// @param TickDir TickDir. void SetTickDirection(int TickDir); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTickDirection - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the direction of the ticks. - // Arguments: None. - + /// Gets the direction of the ticks. int GetTickDirection() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMinimum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the minimum value. - // Arguments: Minimum. - + /// Sets the minimum value. + /// @param Minimum Minimum. void SetMinimum(int Minimum); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMinimum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the minimum value. - // Arguments: None. - + /// Gets the minimum value. int GetMinimum() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMaximum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the maximum value. - // Arguments: Maximum. - + /// Sets the maximum value. + /// @param Maximum Maximum. void SetMaximum(int Maximum); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaximum - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the maximum value. - // Arguments: None. - + /// Gets the maximum value. int GetMaximum() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the value. - // Arguments: Value. - + /// Sets the value. + /// @param Value Value. void SetValue(int Value); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetValue - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the value. - // Arguments: None. - + /// Gets the value. int GetValue() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; - /// /// Sets the value resolution for this slider. - /// - /// The new value resolution + /// @param valueRes The new value resolution void SetValueResolution(int valueRes); private: @@ -272,28 +153,14 @@ namespace RTE { int m_EndThickness; int m_OldValue; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the slider bitmap to draw. - // Arguments: None. - + /// Create the slider bitmap to draw. void BuildBitmap(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildLine - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Builds the background line for the slider - // Arguments: Section, SrcImage. - + /// Builds the background line for the slider + /// @param Section Section, SrcImage. void BuildLine(const std::string& Section, GUIBitmap* SrcImage); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CalculateKnob - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the knob position and size. - // Arguments: None. - + /// Calculates the knob position and size. void CalculateKnob(); }; }; // namespace RTE diff --git a/Source/GUI/GUISound.cpp b/Source/GUI/GUISound.cpp index 5974129870..12166535b6 100644 --- a/Source/GUI/GUISound.cpp +++ b/Source/GUI/GUISound.cpp @@ -2,8 +2,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISound::Clear() { m_SplashSound.Reset(); m_EnterMenuSound.Reset(); @@ -34,8 +32,6 @@ namespace RTE { m_PlacementGravel.Reset(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUISound::Initialize() { // Interface sounds should not be pitched to reinforce the appearance of time decoupling between simulation and UI. diff --git a/Source/GUI/GUISound.h b/Source/GUI/GUISound.h index 2a94a04391..8ab0dae1cb 100644 --- a/Source/GUI/GUISound.h +++ b/Source/GUI/GUISound.h @@ -8,197 +8,133 @@ namespace RTE { - /// /// The singleton loader for all GUI sound effects. - /// class GUISound : public Singleton { public: #pragma region Creation - /// /// Constructor method used to instantiate a GUISound object in system memory. Create() should be called before using the object. - /// GUISound() { Clear(); } - /// /// Creates all the GUI sound effects with Sound::Create and their additional samples with Sound::AddSample and makes the GUISound object ready for use. - /// void Initialize(); #pragma endregion #pragma region Destruction - /// /// Destroys and resets (through Clear()) the GUISound object. - /// void Destroy() { Clear(); } - /// /// Destructor method used to clean up a GUISound object before deletion from system memory. - /// ~GUISound() { Destroy(); } #pragma endregion #pragma region Getters - /// /// Gets juicy logo signature jingle Sound. - /// - /// Juicy logo signature jingle Sound. + /// @return Juicy logo signature jingle Sound. SoundContainer* SplashSound() { return &m_SplashSound; } - /// /// Gets SoundContainer for enabling menu. - /// - /// SoundContainer for enabling menu. + /// @return SoundContainer for enabling menu. SoundContainer* EnterMenuSound() { return &m_EnterMenuSound; } - /// /// Gets SoundContainer for disabling menu. - /// - /// SoundContainer for disabling menu. + /// @return SoundContainer for disabling menu. SoundContainer* ExitMenuSound() { return &m_ExitMenuSound; } - /// /// Gets SoundContainer for changing focus. - /// - /// SoundContainer for changing focus. + /// @return SoundContainer for changing focus. SoundContainer* FocusChangeSound() { return &m_FocusChangeSound; } - /// /// Gets SoundContainer for selecting items in list, etc. - /// - /// SoundContainer for selecting items in list, etc. + /// @return SoundContainer for selecting items in list, etc. SoundContainer* SelectionChangeSound() { return &m_SelectionChangeSound; } - /// /// Gets SoundContainer for adding or deleting items in list. - /// - /// SoundContainer for adding or deleting items in list. + /// @return SoundContainer for adding or deleting items in list. SoundContainer* ItemChangeSound() { return &m_ItemChangeSound; } - /// /// Gets SoundContainer for button press. - /// - /// SoundContainer for button press. + /// @return SoundContainer for button press. SoundContainer* ButtonPressSound() { return &m_ButtonPressSound; } - /// /// Gets SoundContainer for button press of going back button. - /// - /// SoundContainer for button press of going back button. + /// @return SoundContainer for button press of going back button. SoundContainer* BackButtonPressSound() { return &m_BackButtonPressSound; } - /// /// Gets SoundContainer for confirming a selection. - /// - /// SoundContainer for confirming a selection. + /// @return SoundContainer for confirming a selection. SoundContainer* ConfirmSound() { return &m_ConfirmSound; } - /// /// Gets SoundContainer for erroneous input. - /// - /// SoundContainer for erroneous input. + /// @return SoundContainer for erroneous input. SoundContainer* UserErrorSound() { return &m_UserErrorSound; } - /// /// Gets SoundContainer for testing volume when adjusting volume sliders. - /// - /// SoundContainer for testing volume when adjusting volume sliders. + /// @return SoundContainer for testing volume when adjusting volume sliders. SoundContainer* TestSound() { return &m_TestSound; } - /// /// Gets SoundContainer for opening pie menu. - /// - /// SoundContainer for opening pie menu. + /// @return SoundContainer for opening pie menu. SoundContainer* PieMenuEnterSound() { return &m_PieMenuEnterSound; } - /// /// Gets SoundContainer for closing pie menu. - /// - /// SoundContainer for closing pie menu. + /// @return SoundContainer for closing pie menu. SoundContainer* PieMenuExitSound() { return &m_PieMenuExitSound; } - /// /// Gets SoundContainer for when PieMenu hover arrow appears or changes slice. - /// - /// SoundContainer for when PieMenu hover arrow appears or changes slice. + /// @return SoundContainer for when PieMenu hover arrow appears or changes slice. SoundContainer* HoverChangeSound() { return &m_HoverChangeSound; } - /// /// Gets SoundContainer for when PieMenu hover arrow appears or changes to a disabled slice. - /// - /// SoundContainer for when PieMenu hover arrow appears or changes to a disabled slice. + /// @return SoundContainer for when PieMenu hover arrow appears or changes to a disabled slice. SoundContainer* HoverDisabledSound() { return &m_HoverDisabledSound; } - /// /// Gets SoundContainer for picking a valid PieMenu slice. - /// - /// SoundContainer for picking a valid PieMenu slice. + /// @return SoundContainer for picking a valid PieMenu slice. SoundContainer* SlicePickedSound() { return &m_SlicePickedSound; } - /// /// Gets SoundContainer for erroneous input in PieMenu. - /// - /// SoundContainer for erroneous input in PieMenu. + /// @return SoundContainer for erroneous input in PieMenu. SoundContainer* DisabledPickedSound() { return &m_DisabledPickedSound; } - /// /// Gets SoundContainer for when the funds of a team changes. - /// - /// SoundContainer for when the funds of a team changes. + /// @return SoundContainer for when the funds of a team changes. SoundContainer* FundsChangedSound() { return &m_FundsChangedSound; } - /// /// Gets SoundContainer for switching between regular (non-brain) actors. - /// - /// SoundContainer for switching between regular (non-brain) actors. + /// @return SoundContainer for switching between regular (non-brain) actors. SoundContainer* ActorSwitchSound() { return &m_ActorSwitchSound; } - /// /// Gets SoundContainer for switching to the brain shortcut. - /// - /// SoundContainer for switching to the brain shortcut. + /// @return SoundContainer for switching to the brain shortcut. SoundContainer* BrainSwitchSound() { return &m_BrainSwitchSound; } - /// /// Gets SoundContainer when camera is traveling between actors. - /// - /// SoundContainer when camera is traveling between actors. + /// @return SoundContainer when camera is traveling between actors. SoundContainer* CameraTravelSound() { return &m_CameraTravelSound; } - /// /// Gets SoundContainer for making an area focus. - /// - /// SoundContainer for making an area focus. + /// @return SoundContainer for making an area focus. SoundContainer* AreaPickedSound() { return &m_AreaPickedSound; } - /// /// Gets SoundContainer for making an object focus. - /// - /// SoundContainer for making an object focus. + /// @return SoundContainer for making an object focus. SoundContainer* ObjectPickedSound() { return &m_ObjectPickedSound; } - /// /// Gets SoundContainer for making a purchase. - /// - /// SoundContainer for making a purchase. + /// @return SoundContainer for making a purchase. SoundContainer* PurchaseMadeSound() { return &m_PurchaseMadeSound; } - /// /// Gets SoundContainer for placement of object to scene. - /// - /// SoundContainer for placement of object to scene. + /// @return SoundContainer for placement of object to scene. SoundContainer* PlacementBlip() { return &m_PlacementBlip; } - /// /// Gets SoundContainer for placement of object to scene. - /// - /// SoundContainer for placement of object to scene. + /// @return SoundContainer for placement of object to scene. SoundContainer* PlacementThud() { return &m_PlacementThud; } - /// /// Gets SoundContainer for gravely placement of object to scene. - /// - /// SoundContainer for gravely placement of object to scene. + /// @return SoundContainer for gravely placement of object to scene. SoundContainer* PlacementGravel() { return &m_PlacementGravel; } #pragma endregion @@ -242,9 +178,7 @@ namespace RTE { SoundContainer m_PlacementThud; //! SoundContainer for placement of object to scene. SoundContainer m_PlacementGravel; //! SoundContainer for gravely placement of object to scene. - /// /// Clears all the member variables of this GUISound, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/GUI/GUITab.cpp b/Source/GUI/GUITab.cpp index e157b38d6b..83e1c93073 100644 --- a/Source/GUI/GUITab.cpp +++ b/Source/GUI/GUITab.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUITab::GUITab(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUIPanel(Manager) { m_ControlID = "TAB"; @@ -17,8 +15,6 @@ GUITab::GUITab(GUIManager* Manager, GUIControlManager* ControlManager) : m_Text = ""; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -48,8 +44,6 @@ void GUITab::Create(const std::string& Name, int X, int Y, int Width, int Height m_Height = std::max(m_Height, m_MinHeight); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -73,8 +67,6 @@ void GUITab::Create(GUIProperties* Props) { Props->GetValue("Checked", &m_Selected); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -82,8 +74,6 @@ void GUITab::ChangeSkin(GUISkin* Skin) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::BuildBitmap() { std::string Filename; unsigned long ColorIndex = 0; @@ -130,8 +120,6 @@ void GUITab::BuildBitmap() { SetRect(&m_ImageRects[3], Values[0], Values[1], Values[0] + Values[2], Values[1] + Values[3]); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::Draw(GUIScreen* Screen) { if (!m_Image) { return; @@ -182,8 +170,6 @@ void GUITab::Draw(GUIScreen* Screen) { GUIPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::OnMouseDown(int X, int Y, int Buttons, int Modifier) { if (Buttons & MOUSE_LEFT) { // Push the checkbox down @@ -194,8 +180,6 @@ void GUITab::OnMouseDown(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); @@ -207,33 +191,23 @@ void GUITab::OnMouseUp(int X, int Y, int Buttons, int Modifier) { AddEvent(GUIEvent::Notification, UnPushed, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::OnMouseEnter(int X, int Y, int Buttons, int Modifier) { m_Mouseover = true; AddEvent(GUIEvent::Notification, Hovered, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::OnMouseLeave(int X, int Y, int Buttons, int Modifier) { m_Mouseover = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUITab::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::Move(int X, int Y) { GUIPanel::SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::Resize(int Width, int Height) { // Make sure the control isn't too small Width = std::max(Width, m_MinWidth); @@ -245,21 +219,15 @@ void GUITab::Resize(int Width, int Height) { BuildBitmap(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUIPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::StoreProperties() { m_Properties.AddVariable("Text", m_Text); m_Properties.AddVariable("Selected", m_Selected); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::SetCheck(bool Check) { // Nothing to do if already in the same state if (m_Selected == Check) { @@ -296,26 +264,18 @@ void GUITab::SetCheck(bool Check) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUITab::GetCheck() const { return m_Selected; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::SetText(const std::string& Text) { m_Text = Text; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUITab::GetText() const { return m_Text; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITab::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUITab.h b/Source/GUI/GUITab.h index ce44aa25e2..0a96d97599 100644 --- a/Source/GUI/GUITab.h +++ b/Source/GUI/GUITab.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A tab control class. - /// class GUITab : public GUIControl, public GUIPanel { public: @@ -17,166 +15,81 @@ namespace RTE { Changed, } Notification; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUITab - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUITab object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUITab object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUITab(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseEnter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse enters the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse enters the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseEnter(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseLeave - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse leaves the panel. - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse leaves the panel. + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseLeave(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "TAB"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StoreProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the control to store the values into properties. - // Arguments: None. - + /// Gets the control to store the values into properties. void StoreProperties() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the check state. - // Arguments: State. - + /// Sets the check state. + /// @param Check State. void SetCheck(bool Check); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the check state. - // Arguments: None. - + /// Gets the check state. bool GetCheck() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the text. - // Arguments: Text. - + /// Sets the text. + /// @param Text Text. void SetText(const std::string& Text); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the text. - // Arguments: None. - + /// Gets the text. std::string GetText() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: @@ -187,12 +100,7 @@ namespace RTE { int m_Mouseover; std::string m_Text; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BuildBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the checkbox bitmap to draw. - // Arguments: None. - + /// Create the checkbox bitmap to draw. void BuildBitmap(); }; }; // namespace RTE diff --git a/Source/GUI/GUITextBox.cpp b/Source/GUI/GUITextBox.cpp index f5e0a83e68..d6d14b7ca0 100644 --- a/Source/GUI/GUITextBox.cpp +++ b/Source/GUI/GUITextBox.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUITextBox::GUITextBox(GUIManager* Manager, GUIControlManager* ControlManager) : GUIControl(), GUITextPanel(Manager) { m_ControlID = "TEXTBOX"; @@ -14,8 +12,6 @@ GUITextBox::GUITextBox(GUIManager* Manager, GUIControlManager* ControlManager) : m_VAlignment = GUIFont::Top; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::Create(const std::string& Name, int X, int Y, int Width, int Height) { GUIControl::Create(Name, X, Y, Width, Height); @@ -40,8 +36,6 @@ void GUITextBox::Create(const std::string& Name, int X, int Y, int Width, int He GUITextPanel::Create(X, Y, w, h); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::Create(GUIProperties* Props) { GUIControl::Create(Props); @@ -86,8 +80,6 @@ void GUITextBox::Create(GUIProperties* Props) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::Destroy() { // Destroy the draw bitmap if (m_DrawBitmap) { @@ -97,8 +89,6 @@ void GUITextBox::Destroy() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::ChangeSkin(GUISkin* Skin) { GUIControl::ChangeSkin(Skin); @@ -119,8 +109,6 @@ void GUITextBox::ChangeSkin(GUISkin* Skin) { GUITextPanel::ChangeSkin(Skin); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::Draw(GUIScreen* Screen) { // Draw the background m_DrawBitmap->Draw(Screen->GetBitmap(), m_X, m_Y, nullptr); @@ -128,20 +116,14 @@ void GUITextBox::Draw(GUIScreen* Screen) { GUITextPanel::Draw(Screen); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIPanel* GUITextBox::GetPanel() { return this; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::Move(int X, int Y) { GUITextPanel::SetPositionAbs(X, Y); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::Resize(int Width, int Height) { // Make sure the control isn't too small Width = std::max(Width, m_MinWidth); @@ -153,14 +135,10 @@ void GUITextBox::Resize(int Width, int Height) { ChangeSkin(m_Skin); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::GetControlRect(int* X, int* Y, int* Width, int* Height) { GUITextPanel::GetRect(X, Y, Width, Height); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { // Clicked if (Code == GUITextPanel::Clicked) { @@ -178,8 +156,6 @@ void GUITextBox::ReceiveSignal(GUIPanel* Source, int Code, int Data) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextBox::ApplyProperties(GUIProperties* Props) { GUIControl::ApplyProperties(Props); diff --git a/Source/GUI/GUITextBox.h b/Source/GUI/GUITextBox.h index 1aead6dd86..1ac341fd80 100644 --- a/Source/GUI/GUITextBox.h +++ b/Source/GUI/GUITextBox.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// A TextBox control class. - /// class GUITextBox : public GUIControl, public GUITextPanel { public: @@ -18,110 +16,55 @@ namespace RTE { Enter } Notification; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUITextBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUITextBox object in - // system memory. - // Arguments: GUIManager, GUIControlManager. - + /// Constructor method used to instantiate a GUITextBox object in + /// system memory. + /// @param Manager GUIManager, GUIControlManager. GUITextBox(GUIManager* Manager, GUIControlManager* ControlManager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Name, Position. - + /// Called when the control has been created. + /// @param Name Name, Position. void Create(const std::string& Name, int X, int Y, int Width = -1, int Height = -1) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been created. - // Arguments: Properties. - + /// Called when the control has been created. + /// @param Props Properties. void Create(GUIProperties* Props) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control has been destroyed. - // Arguments: None. - + /// Called when the control has been destroyed. void Destroy() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the panel of the control. - // Arguments: None. - // Returns: 0 if the control does not have a panel, otherwise the topmost panel. - + /// Returns the panel of the control. + /// @return 0 if the control does not have a panel, otherwise the topmost panel. GUIPanel* GetPanel() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a string representing the control's ID - // Arguments: None. - + /// Returns a string representing the control's ID static std::string GetControlID() { return "TEXTBOX"; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Move - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be moved. - // Arguments: New position. - + /// Called when the control needs to be moved. + /// @param X New position. void Move(int X, int Y) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Resize - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the control needs to be resized. - // Arguments: New size. - + /// Called when the control needs to be resized. + /// @param Width New size. void Resize(int Width, int Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetControlRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the rectangle of the control. - // Arguments: Position, Size. - + /// Gets the rectangle of the control. + /// @param X Position, Size. void GetControlRect(int* X, int* Y, int* Width, int* Height) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReceiveSignal - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when receiving a signal. - // Arguments: Signal source, Signal code, Signal data. - + /// Called when receiving a signal. + /// @param Source Signal source, Signal code, Signal data. void ReceiveSignal(GUIPanel* Source, int Code, int Data) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ApplyProperties - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Applies new properties to the control. - // Arguments: GUIProperties. - + /// Applies new properties to the control. + /// @param Props GUIProperties. void ApplyProperties(GUIProperties* Props) override; private: diff --git a/Source/GUI/GUITextPanel.cpp b/Source/GUI/GUITextPanel.cpp index f34b85ea7a..ae761c5655 100644 --- a/Source/GUI/GUITextPanel.cpp +++ b/Source/GUI/GUITextPanel.cpp @@ -3,8 +3,6 @@ using namespace RTE; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUITextPanel::GUITextPanel(GUIManager* Manager) : GUIPanel(Manager) { m_Font = nullptr; @@ -29,8 +27,6 @@ GUITextPanel::GUITextPanel(GUIManager* Manager) : // TODO: Both constructors use a common clear function?? Same with other panels -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUITextPanel::GUITextPanel() : GUIPanel() { m_Font = nullptr; @@ -54,8 +50,6 @@ GUITextPanel::GUITextPanel() : m_MaxNumericValue = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::Create(int X, int Y, int Width, int Height) { m_X = X; m_Y = Y; @@ -65,8 +59,6 @@ void GUITextPanel::Create(int X, int Y, int Width, int Height) { assert(m_Manager); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::ChangeSkin(GUISkin* Skin) { // Load the font std::string Filename; @@ -95,8 +87,6 @@ void GUITextPanel::ChangeSkin(GUISkin* Skin) { m_CursorColor = Skin->ConvertColor(m_CursorColor); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::Draw(GUIScreen* Screen) { if (!m_Font) return; @@ -148,8 +138,6 @@ void GUITextPanel::Draw(GUIScreen* Screen) { Screen->GetBitmap()->SetClipRect(nullptr); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::OnKeyPress(int KeyCode, int Modifier) { // TODO: Figure out what the "performance bitching" is. // Condition here to stop the compiler bitching about performance @@ -289,8 +277,6 @@ void GUITextPanel::OnKeyPress(int KeyCode, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::OnTextInput(std::string_view inputText) { int minValidKeyCode = 32; int maxValidKeyCode = 126; @@ -319,8 +305,6 @@ void GUITextPanel::OnTextInput(std::string_view inputText) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { SendSignal(MouseDown, Buttons); @@ -364,8 +348,6 @@ void GUITextPanel::OnMouseDown(int X, int Y, int Buttons, int Modifier) { UpdateText(false, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::OnMouseMove(int X, int Y, int Buttons, int Modifier) { if (!(Buttons & MOUSE_LEFT) || !IsCaptured()) { return; @@ -392,15 +374,11 @@ void GUITextPanel::OnMouseMove(int X, int Y, int Buttons, int Modifier) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::OnMouseUp(int X, int Y, int Buttons, int Modifier) { ReleaseMouse(); SendSignal(Clicked, Buttons); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::UpdateText(bool Typing, bool DoIncrement) { if (!m_Font) { return; @@ -443,8 +421,6 @@ void GUITextPanel::UpdateText(bool Typing, bool DoIncrement) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::DoSelection(int Start, int End) { // Start a selection if (!m_GotSelection) { @@ -479,8 +455,6 @@ void GUITextPanel::DoSelection(int Start, int End) { m_SelectionWidth = std::min(m_SelectionWidth, m_Width); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int RTE::GUITextPanel::GetStartOfNextCharacterGroup(const std::string_view& stringToCheck, int currentIndex) const { auto isNormalCharacter = [](char charToCheck) { return (std::isalnum(charToCheck) || charToCheck == '_'); @@ -501,8 +475,6 @@ int RTE::GUITextPanel::GetStartOfNextCharacterGroup(const std::string_view& stri return std::distance(stringToCheck.cbegin(), currentIterator); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int RTE::GUITextPanel::GetStartOfPreviousCharacterGroup(const std::string_view& stringToCheck, int currentIndex) const { auto isNormalCharacter = [](char charToCheck) { return (std::isalnum(charToCheck) || charToCheck == '_'); @@ -525,8 +497,6 @@ int RTE::GUITextPanel::GetStartOfPreviousCharacterGroup(const std::string_view& return std::distance(stringToCheck.cbegin(), currentIterator.base()); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::RemoveSelectionText() { if (!m_GotSelection) { return; @@ -547,8 +517,6 @@ void GUITextPanel::RemoveSelectionText() { m_GotSelection = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::SetCursorPos(int cursorPos) { m_GotSelection = false; @@ -564,8 +532,6 @@ void GUITextPanel::SetCursorPos(int cursorPos) { UpdateText(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUITextPanel::GetSelectionText() const { if (!m_GotSelection) { return ""; @@ -579,8 +545,6 @@ std::string GUITextPanel::GetSelectionText() const { return m_Text.substr(Start, End - Start); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::SetText(const std::string& Text) { m_Text = Text; @@ -597,15 +561,11 @@ void GUITextPanel::SetText(const std::string& Text) { SendSignal(Changed, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::SetRightText(const std::string& rightText) { m_RightText = rightText; SendSignal(Changed, 0); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::SetSelection(int Start, int End) { if (m_Locked) { return; @@ -618,8 +578,6 @@ void GUITextPanel::SetSelection(int Start, int End) { UpdateText(false, false); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUITextPanel::GetSelectionStart() const { // No selection? if (!m_GotSelection) { @@ -629,8 +587,6 @@ int GUITextPanel::GetSelectionStart() const { return m_StartSelection; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUITextPanel::GetSelectionEnd() const { if (!m_GotSelection) { return -1; @@ -638,14 +594,10 @@ int GUITextPanel::GetSelectionEnd() const { return m_EndSelection; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::ClearSelection() { m_GotSelection = false; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUITextPanel::SetLocked(bool Locked) { m_Locked = Locked; @@ -655,8 +607,6 @@ void GUITextPanel::SetLocked(bool Locked) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUITextPanel::GetLocked() const { return m_Locked; } diff --git a/Source/GUI/GUITextPanel.h b/Source/GUI/GUITextPanel.h index dbd42a31d0..fb2a9e7738 100644 --- a/Source/GUI/GUITextPanel.h +++ b/Source/GUI/GUITextPanel.h @@ -3,9 +3,7 @@ namespace RTE { - /// /// A text panel class. - /// class GUITextPanel : public GUIPanel { public: @@ -17,204 +15,101 @@ namespace RTE { Enter } Signals; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUITextPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUITextPanel object in - // system memory. - // Arguments: GUIManager. - + /// Constructor method used to instantiate a GUITextPanel object in + /// system memory. + /// @param Manager GUIManager. explicit GUITextPanel(GUIManager* Manager); - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GUITextPanel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GUITextPanel object in - // system memory. - // Arguments: None. - + /// Constructor method used to instantiate a GUITextPanel object in + /// system memory. GUITextPanel(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Create the panel. - // Arguments: Position, Size. - + /// Create the panel. + /// @param X Position, Size. void Create(int X, int Y, int Width, int Height); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeSkin - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the skin has been changed. - // Arguments: New skin pointer. - + /// Called when the skin has been changed. + /// @param Skin New skin pointer. void ChangeSkin(GUISkin* Skin); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the panel - // Arguments: Screen class - + /// Draws the panel + /// @param Screen Screen class void Draw(GUIScreen* Screen) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseDown - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes down on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes down on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseDown(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseUp - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse goes up on the panel - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse goes up on the panel + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseUp(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnMouseMove - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when the mouse moves (over the panel, or when captured). - // Arguments: Mouse Position, Mouse Buttons, Modifier. - + /// Called when the mouse moves (over the panel, or when captured). + /// @param X Mouse Position, Mouse Buttons, Modifier. void OnMouseMove(int X, int Y, int Buttons, int Modifier) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnKeyPress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Called when a key is pressed (OnDown & repeating). - // Arguments: KeyCode, Modifier. - + /// Called when a key is pressed (OnDown & repeating). + /// @param KeyCode KeyCode, Modifier. void OnKeyPress(int KeyCode, int Modifier) override; void OnTextInput(std::string_view inputText) override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the text in the textpanel. - // Arguments: Text. - + /// Sets the text in the textpanel. + /// @param Text Text. void SetText(const std::string& Text); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetRightText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the extra text which appears right-justified in the textpanel. - // Arguments: Text. - + /// Sets the extra text which appears right-justified in the textpanel. + /// @param rightText Text. void SetRightText(const std::string& rightText); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the text in the textpanel. - // Arguments: None. - + /// Gets the text in the textpanel. std::string GetText() const { return m_Text; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRightText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the extra text which appears right-justified in the textpanel. - // Arguments: None. - + /// Gets the extra text which appears right-justified in the textpanel. std::string GetRightText() const { return m_RightText; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSelection - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the start and end indexes of the selection text. - // Arguments: Start, End. - + /// Sets the start and end indexes of the selection text. + /// @param Start Start, End. void SetSelection(int Start, int End); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSelectionStart - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the start index of the selection. - // Arguments: None. - // Returns: Index of the start of the selection. -1 if no selection - + /// Gets the start index of the selection. + /// @return Index of the start of the selection. -1 if no selection int GetSelectionStart() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSelectionEnd - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the end index of the selection. - // Arguments: None. - // Returns: Index of the end of the selection. -1 if no selection - + /// Gets the end index of the selection. + /// @return Index of the end of the selection. -1 if no selection int GetSelectionEnd() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearSelection - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the selection. Does NOT remove the selection text though. - // Arguments: None. - + /// Clears the selection. Does NOT remove the selection text though. void ClearSelection(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSelectionText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the selection text. - // Arguments: None. - + /// Gets the selection text. std::string GetSelectionText() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveSelectionText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes the characters in the selection. - // Arguments: None. - + /// Removes the characters in the selection. void RemoveSelectionText(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCursorPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets where the cursor should be. This will clear any selection. - // Arguments: The index of the new cursor position. - + /// Sets where the cursor should be. This will clear any selection. + /// @param cursorPos The index of the new cursor position. void SetCursorPos(int cursorPos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLocked - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the locked state on the textbox. - // Arguments: Locked. - + /// Sets the locked state on the textbox. + /// @param Locked Locked. void SetLocked(bool Locked); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLocked - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the locked state on the textbox. - // Arguments: None. - + /// Gets the locked state on the textbox. bool GetLocked() const; - /// /// Sets this text panel to accept numeric symbols only. - /// - /// Whether to accept numeric symbols only or not. + /// @param numericOnly Whether to accept numeric symbols only or not. void SetNumericOnly(bool numericOnly) { m_NumericOnly = numericOnly; } - /// /// Sets this text panel's maximum numeric value when in numeric only mode. - /// - /// The maximum numeric value. 0 means no maximum value. + /// @param maxValue The maximum numeric value. 0 means no maximum value. void SetMaxNumericValue(int maxValue) { m_MaxNumericValue = maxValue; } - /// /// Sets the maximum length of the text this text panel can contain. - /// - /// The maximum length of the text this text panel can contain. + /// @param maxLength The maximum length of the text this text panel can contain. void SetMaxTextLength(int maxLength) { m_MaxTextLength = maxLength; } private: @@ -250,38 +145,26 @@ namespace RTE { bool m_NumericOnly; //!< Whether this text panel only accepts numeric symbols. int m_MaxNumericValue; //!< The maximum numeric value when in numeric only mode. 0 means no maximum value. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateText - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the cursor and start positions. - // Arguments: Typing, Increment. - + /// Updates the cursor and start positions. + /// @param Typing Typing, Increment. (default: false) void UpdateText(bool Typing = false, bool DoIncrement = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DoSelection - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Update the selection. - // Arguments: Start, End. - + /// Update the selection. + /// @param Start Start, End. void DoSelection(int Start, int End); - /// /// Gets the index of the start of the next contiguous group of letters or special characters in the given string, or the end of the string if there is none. /// Generally used to deal with ctrl + arrows style behavior. - /// - /// A string_view of the string to look for the next word in. - /// The index in the string to start looking from. - /// The index of the start of the next contiguous group of letters or special characters in the given string, or the end of the string if there is none. + /// @param stringToCheck A string_view of the string to look for the next word in. + /// @param currentIndex The index in the string to start looking from. + /// @return The index of the start of the next contiguous group of letters or special characters in the given string, or the end of the string if there is none. int GetStartOfNextCharacterGroup(const std::string_view& stringToCheck, int currentIndex) const; - /// /// Gets the index of the start of the previous contiguous group of letters or special characters in the given string, or the end of the string if there is none. /// Generally used to deal with ctrl + arrows style behavior. - /// - /// A string_view of the string to look for the next word in. - /// The index in the string to start looking from. - /// The index of the start of the previous contiguous group of letters or special characters in the given string, or the end of the string if there is none. + /// @param stringToCheck A string_view of the string to look for the next word in. + /// @param currentIndex The index in the string to start looking from. + /// @return The index of the start of the previous contiguous group of letters or special characters in the given string, or the end of the string if there is none. int GetStartOfPreviousCharacterGroup(const std::string_view& stringToCheck, int currentIndex) const; }; }; // namespace RTE diff --git a/Source/GUI/GUIUtil.cpp b/Source/GUI/GUIUtil.cpp index 1a57857468..7276cccf6b 100644 --- a/Source/GUI/GUIUtil.cpp +++ b/Source/GUI/GUIUtil.cpp @@ -4,8 +4,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - char* GUIUtil::TrimString(char* String) { char* ptr = String; // Find the first non-space character @@ -25,8 +23,6 @@ namespace RTE { return ptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIUtil::GetClipboardText(std::string* text) { if (SDL_HasClipboardText()) { *text = SDL_GetClipboardText(); @@ -35,8 +31,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIUtil::SetClipboardText(const std::string& text) { int result = SDL_SetClipboardText(text.c_str()); return result == 0; diff --git a/Source/GUI/GUIUtil.h b/Source/GUI/GUIUtil.h index b953f70610..d8a61aa057 100644 --- a/Source/GUI/GUIUtil.h +++ b/Source/GUI/GUIUtil.h @@ -3,31 +3,23 @@ namespace RTE { - /// /// A utility class with misc static functions for different things. - /// class GUIUtil { public: - /// /// Removes the preceding and ending spaces from a c type string. - /// - /// String to trim. - /// Trimmed string. + /// @param String String to trim. + /// @return Trimmed string. static char* TrimString(char* String); - /// /// Gets the text from the clipboard. - /// - /// Pointer to string receiving the text. - /// True if text was available in the clipboard. + /// @param text Pointer to string receiving the text. + /// @return True if text was available in the clipboard. static bool GetClipboardText(std::string* text); - /// /// Sets the text in the clipboard. - /// - /// String to put into the clipboard. - /// True if text was added to the clipboard. + /// @param text String to put into the clipboard. + /// @return True if text was added to the clipboard. static bool SetClipboardText(const std::string& text); }; } // namespace RTE diff --git a/Source/GUI/GUIWriter.cpp b/Source/GUI/GUIWriter.cpp index 37c074e74d..255e5b23a7 100644 --- a/Source/GUI/GUIWriter.cpp +++ b/Source/GUI/GUIWriter.cpp @@ -2,8 +2,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::Clear() { m_Stream = nullptr; m_FilePath.clear(); @@ -12,14 +10,10 @@ namespace RTE { m_IndentCount = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter::GUIWriter() { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GUIWriter::Create(const std::string& fileName, bool append) { m_FilePath = fileName; @@ -36,33 +30,23 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIWriter::GetFilePath() const { return m_FilePath; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIWriter::GetFileName() const { return m_FileName; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GUIWriter::GetFolderPath() const { return m_FolderPath; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::ObjectStart(const std::string& className) { *m_Stream << className; ++m_IndentCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::ObjectEnd() { --m_IndentCount; if (m_IndentCount == 0) { @@ -70,8 +54,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::NewLine(bool toIndent, int lineCount) const { for (int lines = 0; lines < lineCount; ++lines) { *m_Stream << "\n"; @@ -81,141 +63,101 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::NewLineString(const std::string& textString, bool toIndent) const { NewLine(toIndent); *m_Stream << textString; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::NewDivider(bool toIndent, int dividerLength) const { NewLine(toIndent); *m_Stream << std::string(dividerLength, '/'); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::NewProperty(const std::string& propName) const { NewLine(); *m_Stream << propName + " = "; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool GUIWriter::WriterOK() const { return m_Stream.get() && !m_Stream->fail() && m_Stream->is_open(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIWriter::EndWrite() const { m_Stream->flush(); m_Stream->close(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const bool& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const char& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const unsigned char& var) { int temp = var; *m_Stream << temp; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const short& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const unsigned short& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const int& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const unsigned int& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const long& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const long long& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const unsigned long& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const unsigned long long& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const float& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const double& var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const char* var) { *m_Stream << var; return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIWriter& GUIWriter::operator<<(const std::string& var) { *m_Stream << var; return *this; diff --git a/Source/GUI/GUIWriter.h b/Source/GUI/GUIWriter.h index 2f01ac5c98..3a7db5afdb 100644 --- a/Source/GUI/GUIWriter.h +++ b/Source/GUI/GUIWriter.h @@ -3,106 +3,76 @@ namespace RTE { - /// /// Writes GUI objects to std::ostreams. - /// class GUIWriter { public: #pragma region Creation - /// /// Constructor method used to instantiate a GUIWriter object in system memory. Create() should be called before using the object. - /// GUIWriter(); - /// /// Makes the GUIWriter object ready for use. - /// - /// Path to the file to open for writing. If the directory doesn't exist the stream will fail to open. - /// Whether to append to the file if it exists, or to overwrite it. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param filename Path to the file to open for writing. If the directory doesn't exist the stream will fail to open. + /// @param append Whether to append to the file if it exists, or to overwrite it. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const std::string& fileName, bool append = false); #pragma endregion #pragma region Getters - /// /// Gets the path to the file being written. - /// - /// The full path to the file being written. + /// @return The full path to the file being written. std::string GetFilePath() const; - /// /// Gets the name (without path) of the file being written. - /// - /// The name of file being written. + /// @return The name of file being written. std::string GetFileName() const; - /// /// Gets the folder path (without filename) to where the file is being written. - /// - /// The name of folder being written in. + /// @return The name of folder being written in. std::string GetFolderPath() const; #pragma endregion #pragma region Writing Operations - /// /// Used to specify the start of an object to be written. - /// - /// The class name of the object about to be written. + /// @param className The class name of the object about to be written. void ObjectStart(const std::string& className); - /// /// Used to specify the end of an object that has just been written. - /// void ObjectEnd(); - /// /// Creates a new line that can be properly indented. - /// - /// Whether to indent the new line or not. - /// How many new lines to create. + /// @param toIndent Whether to indent the new line or not. + /// @param lineCount How many new lines to create. void NewLine(bool toIndent = true, int lineCount = 1) const; - /// /// Creates a new line and writes the specified string to it. - /// - /// The text string to write to the new line. - /// Whether to indent the new line or not. + /// @param textString The text string to write to the new line. + /// @param toIndent Whether to indent the new line or not. void NewLineString(const std::string& textString, bool toIndent = true) const; - /// /// Creates a new line and fills it with slashes to create a divider line for INI. - /// - /// Whether to indent the new line or not. - /// The length of the divider (number of slashes). + /// @param toIndent Whether to indent the new line or not. + /// @param dividerLength The length of the divider (number of slashes). void NewDivider(bool toIndent = true, int dividerLength = 72) const; - /// /// Creates a new line and writes the name of the property in preparation to writing it's value. - /// - /// The name of the property to be written. + /// @param propName The name of the property to be written. void NewProperty(const std::string& propName) const; #pragma endregion #pragma region Writer Status - /// /// Shows whether the writer is ready to start accepting data streamed to it. - /// - /// Whether the writer is ready to start accepting data streamed to it or not. + /// @return Whether the writer is ready to start accepting data streamed to it or not. bool WriterOK() const; - /// /// Flushes and closes the output stream of this GUIWriter. This happens automatically at destruction but needs to be called manually if a written file must be read from in the same scope. - /// void EndWrite() const; #pragma endregion #pragma region Operator Overloads - /// /// Elemental types stream insertions. Stream insertion operator overloads for all the elemental types. - /// - /// A reference to the variable that will be written to the ostream. - /// A GUIWriter reference for further use in an expression. + /// @param var A reference to the variable that will be written to the ostream. + /// @return A GUIWriter reference for further use in an expression. GUIWriter& operator<<(const bool& var); GUIWriter& operator<<(const char& var); GUIWriter& operator<<(const unsigned char& var); @@ -128,9 +98,7 @@ namespace RTE { int m_IndentCount; //!< Indentation counter. private: - /// /// Clears all the member variables of this GUIWriter, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/GUI/Wrappers/AllegroBitmap.cpp b/Source/GUI/Wrappers/AllegroBitmap.cpp index 925e10bcce..53d6631723 100644 --- a/Source/GUI/Wrappers/AllegroBitmap.cpp +++ b/Source/GUI/Wrappers/AllegroBitmap.cpp @@ -4,16 +4,12 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::Clear() { m_Bitmap = nullptr; m_BitmapFile.Reset(); m_SelfCreated = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::Create(const std::string& fileName) { m_BitmapFile.Create(fileName.c_str()); m_Bitmap = m_BitmapFile.GetAsBitmap(); @@ -23,8 +19,6 @@ namespace RTE { m_SelfCreated = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::Create(int width, int height, int colorDepth) { m_BitmapFile.Reset(); m_Bitmap = create_bitmap_ex(colorDepth, width, height); @@ -35,8 +29,6 @@ namespace RTE { m_SelfCreated = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::Destroy() { if (m_SelfCreated && m_Bitmap) { destroy_bitmap(m_Bitmap); @@ -44,39 +36,27 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AllegroBitmap::GetWidth() const { return m_Bitmap ? m_Bitmap->w : 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AllegroBitmap::GetHeight() const { return m_Bitmap ? m_Bitmap->h : 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int AllegroBitmap::GetColorDepth() const { return m_Bitmap ? bitmap_color_depth(m_Bitmap) : 8; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned long AllegroBitmap::GetPixel(int posX, int posY) const { return m_Bitmap ? getpixel(m_Bitmap, posX, posY) : 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::SetPixel(int posX, int posY, unsigned long pixelColor) { RTEAssert(m_Bitmap, "Trying to set a pixel on a null bitmap!"); putpixel(m_Bitmap, posX, posY, pixelColor); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::GetClipRect(GUIRect* clippingRect) const { if (m_Bitmap && clippingRect) { int x1; @@ -91,8 +71,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::SetClipRect(GUIRect* clippingRect) { if (!m_Bitmap) { return; @@ -106,8 +84,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::AddClipRect(GUIRect* clippingRect) { if (!m_Bitmap) { return; @@ -121,8 +97,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::Draw(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) { if (!m_Bitmap) { return; @@ -136,8 +110,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::DrawTrans(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) { if (!m_Bitmap) { return; @@ -151,8 +123,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::DrawTransScaled(GUIBitmap* destBitmap, int destX, int destY, int width, int height) { if (!m_Bitmap) { return; @@ -162,8 +132,6 @@ namespace RTE { stretch_sprite(dynamic_cast(destBitmap)->GetBitmap(), m_Bitmap, destX, destY, width, height); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::DrawLine(int x1, int y1, int x2, int y2, unsigned long color) { if (!m_Bitmap) { return; @@ -171,8 +139,6 @@ namespace RTE { line(m_Bitmap, x1, y1, x2, y2, color); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroBitmap::DrawRectangle(int posX, int posY, int width, int height, unsigned long color, bool filled) { if (!m_Bitmap) { return; diff --git a/Source/GUI/Wrappers/AllegroBitmap.h b/Source/GUI/Wrappers/AllegroBitmap.h index 386bd2fe89..2e03a41275 100644 --- a/Source/GUI/Wrappers/AllegroBitmap.h +++ b/Source/GUI/Wrappers/AllegroBitmap.h @@ -6,181 +6,135 @@ namespace RTE { - /// /// Wrapper class to convert raw Allegro BITMAPs to GUI library bitmaps. - /// class AllegroBitmap : public GUIBitmap { public: #pragma region Creation - /// /// Constructor method used to instantiate an AllegroBitmap object in system memory. - /// AllegroBitmap() { Clear(); } - /// /// Constructor method used to instantiate an AllegroBitmap object in system memory and make it ready for use. - /// - /// The underlaying BITMAP of this AllegroBitmap. Ownership is NOT transferred! + /// @param bitmap The underlaying BITMAP of this AllegroBitmap. Ownership is NOT transferred! explicit AllegroBitmap(BITMAP* bitmap) { Clear(); m_Bitmap = bitmap; } - /// /// Creates an AllegroBitmap from a file. - /// - /// File name to get the underlaying BITMAP from. Ownership is NOT transferred! + /// @param fileName File name to get the underlaying BITMAP from. Ownership is NOT transferred! void Create(const std::string& fileName); - /// /// Creates an empty BITMAP that is owned by this AllegroBitmap. - /// - /// Bitmap width. - /// Bitmap height. - /// Bitmap color depth (8 or 32). + /// @param width Bitmap width. + /// @param height Bitmap height. + /// @param colorDepth Bitmap color depth (8 or 32). void Create(int width, int height, int colorDepth = 8); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a AllegroBitmap object before deletion from system memory. - /// ~AllegroBitmap() override { Destroy(); } - /// /// Destroys and resets (through Clear()) the AllegroBitmap object. - /// void Destroy() override; #pragma endregion #pragma region Getters and Setters - /// /// Gets the path to the data file this AllegroBitmap uses. - /// - /// Path to the data file this AllegroBitmap uses. + /// @return Path to the data file this AllegroBitmap uses. std::string GetDataPath() const override { return m_BitmapFile.GetDataPath(); } - /// /// Gets the underlying BITMAP of this AllegroBitmap. - /// - /// The underlying BITMAP of this AllegroBitmap. + /// @return The underlying BITMAP of this AllegroBitmap. BITMAP* GetBitmap() const override { return m_Bitmap; } - /// /// Sets the underlying BITMAP for this AllegroBitmap. Ownership is NOT transferred. - /// - /// A pointer to the new BITMAP for this AllegroBitmap. + /// @param newBitmap A pointer to the new BITMAP for this AllegroBitmap. void SetBitmap(BITMAP* newBitmap) override { Destroy(); m_Bitmap = newBitmap; } - /// /// Gets the width of the bitmap. - /// - /// The width of the bitmap. + /// @return The width of the bitmap. int GetWidth() const override; - /// /// Gets the height of the bitmap. - /// - /// The height of the bitmap. + /// @return The height of the bitmap. int GetHeight() const override; - /// /// Gets the number of bits per pixel color depth of the bitmap. - /// - /// The color depth of the bitmap. + /// @return The color depth of the bitmap. int GetColorDepth() const override; - /// /// Gets the color of a pixel at a specific point on the bitmap. - /// - /// X position on bitmap. - /// Y position on bitmap. - /// The color of the pixel at the specified point. + /// @param posX X position on bitmap. + /// @param posY Y position on bitmap. + /// @return The color of the pixel at the specified point. unsigned long GetPixel(int posX, int posY) const override; - /// /// Sets the color of a pixel at a specific point on the bitmap. - /// - /// X position on bitmap. - /// Y position on bitmap. - /// The color to set the pixel to. + /// @param posX X position on bitmap. + /// @param posY Y position on bitmap. + /// @param pixelColor The color to set the pixel to. void SetPixel(int posX, int posY, unsigned long pixelColor) override; #pragma endregion #pragma region Clipping - /// /// Gets the clipping rectangle of the bitmap. - /// - /// Pointer to a GUIRect to fill out. + /// @param clippingRect Pointer to a GUIRect to fill out. void GetClipRect(GUIRect* clippingRect) const override; - /// /// Sets the clipping rectangle of the bitmap. - /// - /// Pointer to a GUIRect to use as the clipping rectangle, or nullptr for no clipping. + /// @param clippingRect Pointer to a GUIRect to use as the clipping rectangle, or nullptr for no clipping. void SetClipRect(GUIRect* clippingRect) override; - /// /// Sets the clipping rectangle of the bitmap as the intersection of its current clipping rectangle and the passed-in rectangle. - /// - /// Pointer to a GUIRect to add to the existing clipping rectangle. + /// @param clippingRect Pointer to a GUIRect to add to the existing clipping rectangle. void AddClipRect(GUIRect* clippingRect) override; #pragma endregion #pragma region Drawing - /// /// Draw a section of this bitmap onto another bitmap - /// - /// Bitmap to draw onto. - /// Destination X position. - /// Destination Y position. - /// Source bitmap position and size rectangle. + /// @param destBitmap Bitmap to draw onto. + /// @param destX Destination X position. + /// @param destY Destination Y position. + /// @param srcPosAndSizeRect Source bitmap position and size rectangle. void Draw(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) override; - /// /// Draw a section of this bitmap onto another bitmap ignoring color-keyed pixels. - /// - /// Bitmap to draw onto. - /// Destination X position. - /// Destination Y position. - /// Source bitmap position and size rectangle. + /// @param destBitmap Bitmap to draw onto. + /// @param destX Destination X position. + /// @param destY Destination Y position. + /// @param srcPosAndSizeRect Source bitmap position and size rectangle. void DrawTrans(GUIBitmap* destBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) override; - /// /// Draw this bitmap scaled onto another bitmap ignoring color-keyed pixels. - /// - /// Bitmap to draw onto. - /// Destination X position. - /// Destination Y position. - /// Target width of the bitmap. - /// Target height of the bitmap. + /// @param destBitmap Bitmap to draw onto. + /// @param destX Destination X position. + /// @param destY Destination Y position. + /// @param width Target width of the bitmap. + /// @param height Target height of the bitmap. void DrawTransScaled(GUIBitmap* destBitmap, int destX, int destY, int width, int height) override; #pragma endregion #pragma region Primitive Drawing - /// /// Draws a line on this bitmap. - /// - /// Start position on X axis. - /// Start position on Y axis. - /// End position on X axis. - /// End position on Y axis. - /// Color to draw this line with. + /// @param x1 Start position on X axis. + /// @param y1 Start position on Y axis. + /// @param x2 End position on X axis. + /// @param y2 End position on Y axis. + /// @param color Color to draw this line with. void DrawLine(int x1, int y1, int x2, int y2, unsigned long color) override; - /// /// Draws a rectangle on this bitmap. - /// - /// Position on X axis. - /// Position on Y axis. - /// Width of rectangle. - /// Height of rectangle. - /// Color to draw this rectangle with. - /// Whether to fill the rectangle with the set color or not. + /// @param posX Position on X axis. + /// @param posY Position on Y axis. + /// @param width Width of rectangle. + /// @param height Height of rectangle. + /// @param color Color to draw this rectangle with. + /// @param filled Whether to fill the rectangle with the set color or not. void DrawRectangle(int posX, int posY, int width, int height, unsigned long color, bool filled) override; #pragma endregion @@ -189,9 +143,7 @@ namespace RTE { ContentFile m_BitmapFile; //!< The ContentFile the underlaying BITMAP was created from, if created from a file. bool m_SelfCreated; //!< Whether the underlaying BITMAP was created by this and is owned. - /// /// Clears all the member variables of this AllegroBitmap, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/GUI/Wrappers/AllegroScreen.cpp b/Source/GUI/Wrappers/AllegroScreen.cpp index f6a3bb029e..5259cab6f2 100644 --- a/Source/GUI/Wrappers/AllegroScreen.cpp +++ b/Source/GUI/Wrappers/AllegroScreen.cpp @@ -5,8 +5,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIBitmap* AllegroScreen::CreateBitmap(const std::string& fileName) { std::unique_ptr newAllegroBitmap; newAllegroBitmap.reset(new AllegroBitmap()); @@ -15,8 +13,6 @@ namespace RTE { return newAllegroBitmap.release(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIBitmap* AllegroScreen::CreateBitmap(int width, int height) { std::unique_ptr newAllegroBitmap; newAllegroBitmap.reset(new AllegroBitmap()); @@ -25,8 +21,6 @@ namespace RTE { return newAllegroBitmap.release(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroScreen::DrawBitmap(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) { if (!guiBitmap) { return; @@ -40,8 +34,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AllegroScreen::DrawBitmapTrans(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) { if (!guiBitmap) { return; @@ -55,8 +47,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned long AllegroScreen::ConvertColor(unsigned long color, int targetColorDepth) { if (targetColorDepth == 0) { targetColorDepth = get_color_depth(); diff --git a/Source/GUI/Wrappers/AllegroScreen.h b/Source/GUI/Wrappers/AllegroScreen.h index 64cac1b21f..0c200feb51 100644 --- a/Source/GUI/Wrappers/AllegroScreen.h +++ b/Source/GUI/Wrappers/AllegroScreen.h @@ -5,82 +5,62 @@ namespace RTE { - /// /// Wrapper class to convert raw Allegro BITMAPs to GUI library backbuffer bitmaps. - /// class AllegroScreen : public GUIScreen { public: #pragma region Creation - /// /// Constructor method used to instantiate an AllegroScreen object in system memory and make it ready for use. - /// - /// A bitmap that represents the back buffer. Ownership is NOT transferred! + /// @param backBuffer A bitmap that represents the back buffer. Ownership is NOT transferred! explicit AllegroScreen(BITMAP* backBuffer) { m_BackBufferBitmap = std::make_unique(backBuffer); } - /// /// Creates a bitmap from a file. - /// - /// File name to create bitmap from. - /// Pointer to the created bitmap. Ownership IS transferred! + /// @param fileName File name to create bitmap from. + /// @return Pointer to the created bitmap. Ownership IS transferred! GUIBitmap* CreateBitmap(const std::string& fileName) override; - /// /// Creates an empty bitmap. - /// - /// Bitmap width. - /// Bitmap height. - /// Pointer to the created bitmap. Ownership IS transferred! + /// @param width Bitmap width. + /// @param height Bitmap height. + /// @return Pointer to the created bitmap. Ownership IS transferred! GUIBitmap* CreateBitmap(int width, int height) override; #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a AllegroScreen object before deletion from system memory. - /// ~AllegroScreen() override { Destroy(); } - /// /// Destroys the AllegroScreen object. - /// void Destroy() override { m_BackBufferBitmap.reset(); } #pragma endregion #pragma region Getters - /// /// Gets the bitmap representing the screen. - /// - /// Pointer to the bitmap representing the screen. Ownership is NOT transferred! + /// @return Pointer to the bitmap representing the screen. Ownership is NOT transferred! GUIBitmap* GetBitmap() const override { return m_BackBufferBitmap.get(); } #pragma endregion #pragma region Drawing - /// /// Draws a bitmap onto the back buffer. - /// - /// The bitmap to draw to this AllegroScreen. - /// Destination X position - /// Destination Y position - /// Source bitmap position and size rectangle. + /// @param guiBitmap The bitmap to draw to this AllegroScreen. + /// @param destX Destination X position + /// @param destY Destination Y position + /// @param srcPosAndSizeRect Source bitmap position and size rectangle. void DrawBitmap(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) override; - /// /// Draws a bitmap onto the back buffer ignoring color-keyed pixels. - /// - /// The bitmap to draw to this AllegroScreen. - /// Destination X position - /// Destination Y position - /// Source bitmap position and size rectangle. + /// @param guiBitmap The bitmap to draw to this AllegroScreen. + /// @param destX Destination X position + /// @param destY Destination Y position + /// @param srcPosAndSizeRect Source bitmap position and size rectangle. void DrawBitmapTrans(GUIBitmap* guiBitmap, int destX, int destY, GUIRect* srcPosAndSizeRect) override; #pragma endregion #pragma region Virtual Override Methods - /// /// Converts an 8bit palette index to a valid pixel format color. - /// - /// Color value in any bit depth. Will be converted to the format specified. - /// An optional target color depth that will determine what format the color should be converted to. If this is 0, then the current video color depth will be used as target. - /// The converted color. + /// @param color Color value in any bit depth. Will be converted to the format specified. + /// @param targetColorDepth An optional target color depth that will determine what format the color should be converted to. If this is 0, then the current video color depth will be used as target. + /// @return The converted color. unsigned long ConvertColor(unsigned long color, int targetColorDepth = 0) override; #pragma endregion diff --git a/Source/GUI/Wrappers/GUIInputWrapper.cpp b/Source/GUI/Wrappers/GUIInputWrapper.cpp index a4838e9369..f8edf53d9f 100644 --- a/Source/GUI/Wrappers/GUIInputWrapper.cpp +++ b/Source/GUI/Wrappers/GUIInputWrapper.cpp @@ -8,8 +8,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIInputWrapper::GUIInputWrapper(int whichPlayer, bool keyJoyMouseCursor) : GUIInput(whichPlayer, keyJoyMouseCursor) { m_KeyTimer = std::make_unique(); @@ -21,8 +19,6 @@ namespace RTE { m_KeyHoldDuration.fill(-1); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInputWrapper::ConvertKeyEvent(SDL_Scancode sdlKey, int guilibKey, float elapsedS) { int nKeys; const Uint8* sdlKeyState = SDL_GetKeyboardState(&nKeys); @@ -47,8 +43,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInputWrapper::Update() { float keyElapsedTime = static_cast(m_KeyTimer->GetElapsedRealTimeS()); m_KeyTimer->Reset(); @@ -67,8 +61,6 @@ namespace RTE { m_MouseY = static_cast(mousePos.GetY() / static_cast(g_WindowMan.GetResMultiplier())); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInputWrapper::UpdateKeyboardInput(float keyElapsedTime) { // Clear the keyboard buffer, we need it to check for changes. memset(m_KeyboardBuffer, 0, sizeof(uint8_t) * GUIInput::Constants::KEYBOARD_BUFFER_SIZE); @@ -117,8 +109,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInputWrapper::UpdateMouseInput() { int discard; Uint32 buttonState = SDL_GetMouseState(&discard, &discard); @@ -220,8 +210,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GUIInputWrapper::UpdateKeyJoyMouseInput(float keyElapsedTime) { // TODO Try to not use magic numbers throughout this method. float mouseDenominator = g_WindowMan.GetResMultiplier(); diff --git a/Source/GUI/Wrappers/GUIInputWrapper.h b/Source/GUI/Wrappers/GUIInputWrapper.h index cc458d1c0a..0239319192 100644 --- a/Source/GUI/Wrappers/GUIInputWrapper.h +++ b/Source/GUI/Wrappers/GUIInputWrapper.h @@ -8,32 +8,24 @@ namespace RTE { class Timer; - /// /// Wrapper class to translate input handling of whatever library is currently used to valid GUI library input. - /// class GUIInputWrapper : public GUIInput { public: #pragma region Creation - /// /// Constructor method used to instantiate a GUIInputWrapper object in system memory. - /// - /// Which player this GUIInputWrapper will handle input for. -1 means no specific player and will default to player 1. - /// Whether the keyboard and joysticks also can control the mouse cursor. + /// @param whichPlayer Which player this GUIInputWrapper will handle input for. -1 means no specific player and will default to player 1. + /// @param keyJoyMouseCursor Whether the keyboard and joysticks also can control the mouse cursor. GUIInputWrapper(int whichPlayer, bool keyJoyMouseCursor = false); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a GUIInputWrapper object before deletion from system memory. - /// ~GUIInputWrapper() override = default; #pragma endregion #pragma region Virtual Override Methods - /// /// Updates the input. - /// void Update() override; #pragma endregion @@ -44,28 +36,20 @@ namespace RTE { std::unique_ptr m_KeyTimer; //!< Timer for checking key hold duration. std::unique_ptr m_CursorAccelTimer; //!< Timer to calculate the mouse cursor acceleration when it is controller with the keyboard or joysticks. - /// /// Converts from SDL's key push to that used by this GUI lib, with timings for repeats taken into consideration. - /// - /// The key scancode. - /// The corresponding GUIlib scancode - /// The elapsed time since the last update, in seconds. + /// @param sdlKey The key scancode. + /// @param guilibKey The corresponding GUIlib scancode + /// @param elapsedS The elapsed time since the last update, in seconds. void ConvertKeyEvent(SDL_Scancode sdlKey, int guilibKey, float elapsedS); #pragma region Update Breakdown - /// /// Updates the keyboard input. - /// void UpdateKeyboardInput(float keyElapsedTime); - /// /// Updates the mouse input. - /// void UpdateMouseInput(); - /// /// Updates the mouse input using the joystick or keyboard. - /// void UpdateKeyJoyMouseInput(float keyElapsedTime); #pragma endregion diff --git a/Source/Lua/LuaAdapterDefinitions.h b/Source/Lua/LuaAdapterDefinitions.h index 5d5690b4ea..45a3e14d67 100644 --- a/Source/Lua/LuaAdapterDefinitions.h +++ b/Source/Lua/LuaAdapterDefinitions.h @@ -91,12 +91,10 @@ namespace RTE { #pragma region Entity Lua Adapter Macros struct LuaAdaptersEntityCreate { -/// /// Convenience macro to generate preset clone-create adapter functions that will return the exact pre-cast types, so we don't have to do: myNewActor = ToActor(PresetMan:GetPreset("AHuman", "Soldier Light", "All")):Clone() /// But can instead do: myNewActor = CreateActor("Soldier Light", "All"); /// Or even: myNewActor = CreateActor("Soldier Light"); /// Or for a randomly selected Preset within a group: myNewActor = RandomActor("Light Troops"); -/// #define LuaEntityCreateFunctionsDeclarationsForType(TYPE) \ static TYPE* Create##TYPE(std::string preseName, std::string moduleName); \ static TYPE* Create##TYPE(std::string preset); \ @@ -135,9 +133,7 @@ namespace RTE { }; struct LuaAdaptersEntityClone { -/// /// Convenience macro to generate a preset clone adapter function for a type. -/// #define LuaEntityCloneFunctionDeclarationForType(TYPE) \ static TYPE* Clone##TYPE(const TYPE* thisEntity) @@ -176,9 +172,7 @@ namespace RTE { }; struct LuaAdaptersEntityCast { -/// /// Convenience macro to generate type casting adapter functions for a type. -/// #define LuaEntityCastFunctionsDeclarationsForType(TYPE) \ static TYPE* To##TYPE(Entity* entity); \ static const TYPE* ToConst##TYPE(const Entity* entity); \ @@ -227,10 +221,8 @@ namespace RTE { }; struct LuaAdaptersPropertyOwnershipSafetyFaker { -/// /// Special handling for passing ownership through properties. If you try to pass null to this normally, LuaJIT crashes. /// This handling avoids that, and is a bit safer since there's no actual ownership transfer from Lua to C++. -/// #define LuaPropertyOwnershipSafetyFakerFunctionDeclaration(OBJECTTYPE, PROPERTYTYPE, SETTERFUNCTION) \ static void OBJECTTYPE##SETTERFUNCTION(OBJECTTYPE* luaSelfObject, PROPERTYTYPE* objectToSet) @@ -400,32 +392,24 @@ namespace RTE { #pragma region MovableMan Lua Adapters struct LuaAdaptersMovableMan { - /// /// Adds the given MovableObject to MovableMan if it doesn't already exist in there, or prints an error if it does. - /// - /// A reference to MovableMan, provided by Lua. - /// A pointer to the MovableObject to be added. + /// @param movableMan A reference to MovableMan, provided by Lua. + /// @param movableObject A pointer to the MovableObject to be added. static void AddMO(MovableMan& movableMan, MovableObject* movableObject); - /// /// Adds the given Actor to MovableMan if it doesn't already exist in there, or prints an error if it does. - /// - /// A reference to MovableMan, provided by Lua. - /// A pointer to the Actor to be added. + /// @param movableMan A reference to MovableMan, provided by Lua. + /// @param actor A pointer to the Actor to be added. static void AddActor(MovableMan& movableMan, Actor* actor); - /// /// Adds the given item MovableObject (generally a HeldDevice) to MovableMan if it doesn't already exist in there, or prints an error if it does. - /// - /// A reference to MovableMan, provided by Lua. - /// A pointer to the item to be added. + /// @param movableMan A reference to MovableMan, provided by Lua. + /// @param item A pointer to the item to be added. static void AddItem(MovableMan& movableMan, HeldDevice* item); - /// /// Adds the given particle MovableObject to MovableMan if it doesn't already exist in there, or prints an error if it does. - /// - /// A reference to MovableMan, provided by Lua. - /// A pointer to the particle to be added. + /// @param movableMan A reference to MovableMan, provided by Lua. + /// @param particle A pointer to the particle to be added. static void AddParticle(MovableMan& movableMan, MovableObject* particle); static void SendGlobalMessage1(MovableMan& movableMan, const std::string& message); @@ -435,201 +419,155 @@ namespace RTE { #pragma region TimerMan Lua Adapters struct LuaAdaptersTimerMan { - /// /// Gets the current number of ticks that the simulation should be updating with. Lua can't handle int64 (or long long apparently) so we'll expose this specialized function. - /// - /// The current fixed delta time that the simulation should be updating with, in ticks. + /// @return The current fixed delta time that the simulation should be updating with, in ticks. static double GetDeltaTimeTicks(const TimerMan& timerMan); - /// /// Gets the number of ticks per second. Lua can't handle int64 (or long long apparently) so we'll expose this specialized function. - /// - /// The number of ticks per second. + /// @return The number of ticks per second. static double GetTicksPerSecond(const TimerMan& timerMan); }; #pragma endregion #pragma region UInputMan Lua Adapters struct LuaAdaptersUInputMan { - /// /// Gets whether a mouse button is being held down right now. - /// - /// Which button to check for. - /// Whether the mouse button is held or not. + /// @param whichButton Which button to check for. + /// @return Whether the mouse button is held or not. static bool MouseButtonHeld(const UInputMan& uinputMan, int whichButton); - /// /// Gets whether a mouse button was pressed between the last update and the one previous to it. - /// - /// Which button to check for. - /// Whether the mouse button is pressed or not. + /// @param whichButton Which button to check for. + /// @return Whether the mouse button is pressed or not. static bool MouseButtonPressed(const UInputMan& uinputMan, int whichButton); - /// /// Gets whether a mouse button was released between the last update and the one previous to it. - /// - /// Which button to check for. - /// Whether the mouse button is released or not. + /// @param whichButton Which button to check for. + /// @return Whether the mouse button is released or not. static bool MouseButtonReleased(const UInputMan& uinputMan, int whichButton); }; #pragma endregion #pragma region PresetMan Lua Adapters struct LuaAdaptersPresetMan { - /// /// Reloads the specified Entity preset in PresetMan. - /// - /// The preset name of the Entity to reload. - /// The class name of the Entity to reload. - /// The module name of the Entity to reload. - /// Whether or not the Entity was reloaded. + /// @param presetName The preset name of the Entity to reload. + /// @param className The class name of the Entity to reload. + /// @param moduleName The module name of the Entity to reload. + /// @return Whether or not the Entity was reloaded. static bool ReloadEntityPreset1(PresetMan& presetMan, const std::string& presetName, const std::string& className, const std::string& moduleName); - /// /// Reloads the specified Entity preset in PresetMan. - /// - /// The preset name of the Entity to reload. - /// The class name of the Entity to reload. - /// Whether or not the Entity was reloaded. + /// @param presetName The preset name of the Entity to reload. + /// @param className The class name of the Entity to reload. + /// @return Whether or not the Entity was reloaded. static bool ReloadEntityPreset2(PresetMan& presetMan, const std::string& presetName, const std::string& className); - /// /// Gets a list all previously read in (defined) Entities which are associated with a specific group. - /// - /// The group to look for. "All" will look in all. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether to only get those of one specific DataModule (0-n), or all (-1). - /// The list of all Entities with the given group and type in the module. + /// @param group The group to look for. "All" will look in all. + /// @param type The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// @param whichModule Whether to only get those of one specific DataModule (0-n), or all (-1). + /// @return The list of all Entities with the given group and type in the module. static std::list* GetAllEntitiesOfGroup(PresetMan& presetMan, const std::string& group, const std::string& type, int whichModule); static std::list* GetAllEntitiesOfGroup2(PresetMan& presetMan, const std::string& group, const std::string& type) { return GetAllEntitiesOfGroup(presetMan, group, type, -1); } static std::list* GetAllEntitiesOfGroup3(PresetMan& presetMan, const std::string& group) { return GetAllEntitiesOfGroup2(presetMan, group, "All"); } - /// /// Gets a list all previously read in (defined) Entities. - /// - /// The list of all Entities. + /// @return The list of all Entities. static std::list* GetAllEntities(PresetMan& presetMan) { return GetAllEntitiesOfGroup3(presetMan, "All"); } }; #pragma endregion #pragma region SceneMan Lua Adapters struct LuaAdaptersSceneMan { - /// /// Takes a Box and returns a list of Boxes that describe the Box, wrapped appropriately for the current Scene. - /// - /// The Box to wrap. - /// A list of Boxes that make up the Box to wrap, wrapped appropriately for the current Scene. + /// @param boxToWrap The Box to wrap. + /// @return A list of Boxes that make up the Box to wrap, wrapped appropriately for the current Scene. static const std::list* WrapBoxes(SceneMan& sceneMan, const Box& boxToWrap); }; #pragma endregion #pragma region PrimitiveMan Lua Adapters struct LuaAdaptersPrimitiveMan { - /// /// Schedule to draw a polygon primitive. - /// - /// A reference to PrimitiveMan, provided by Lua. - /// Position of primitive's center in Scene coordinates. - /// Color to draw primitive with. - /// A Lua table that contains the positions of the primitive's vertices, relative to the center position. + /// @param primitiveMan A reference to PrimitiveMan, provided by Lua. + /// @param centerPos Position of primitive's center in Scene coordinates. + /// @param color Color to draw primitive with. + /// @param verticesTable A Lua table that contains the positions of the primitive's vertices, relative to the center position. static void DrawPolygonPrimitive(PrimitiveMan& primitiveMan, const Vector& centerPos, int color, const luabind::object& verticesTable); - /// /// Schedule to draw a polygon primitive visible only to a specified player. - /// - /// A reference to PrimitiveMan, provided by Lua. - /// Player screen to draw primitive on. - /// Position of primitive's center in Scene coordinates. - /// Color to draw primitive with. - /// A Lua table that contains the positions of the primitive's vertices, relative to the center position. + /// @param primitiveMan A reference to PrimitiveMan, provided by Lua. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in Scene coordinates. + /// @param color Color to draw primitive with. + /// @param verticesTable A Lua table that contains the positions of the primitive's vertices, relative to the center position. static void DrawPolygonPrimitiveForPlayer(PrimitiveMan& primitiveMan, int player, const Vector& centerPos, int color, const luabind::object& verticesTable); - /// /// Schedule to draw a filled polygon primitive. - /// - /// A reference to PrimitiveMan, provided by Lua. - /// Start position of the primitive in Scene coordinates. - /// Color to draw primitive with. - /// A Lua table that contains the positions of the primitive's vertices, relative to the center position. + /// @param primitiveMan A reference to PrimitiveMan, provided by Lua. + /// @param startPos Start position of the primitive in Scene coordinates. + /// @param color Color to draw primitive with. + /// @param verticesTable A Lua table that contains the positions of the primitive's vertices, relative to the center position. static void DrawPolygonFillPrimitive(PrimitiveMan& primitiveMan, const Vector& startPos, int color, const luabind::object& verticesTable); - /// /// Schedule to draw a filled polygon primitive visible only to a specified player. - /// - /// A reference to PrimitiveMan, provided by Lua. - /// Player screen to draw primitive on. - /// Start position of the primitive in Scene coordinates. - /// Color to draw primitive with. - /// A Lua table that contains the positions of the primitive's vertices, relative to the center position. + /// @param primitiveMan A reference to PrimitiveMan, provided by Lua. + /// @param player Player screen to draw primitive on. + /// @param startPos Start position of the primitive in Scene coordinates. + /// @param color Color to draw primitive with. + /// @param verticesTable A Lua table that contains the positions of the primitive's vertices, relative to the center position. static void DrawPolygonFillPrimitiveForPlayer(PrimitiveMan& primitiveMan, int player, const Vector& startPos, int color, const luabind::object& verticesTable); - /// /// Schedules to draw multiple primitives of varying type with transparency enabled. - /// - /// A reference to PrimitiveMan, provided by Lua. - /// The transparency value the primitives should be drawn at. From 0 (opaque) to 100 (transparent). - /// A Lua table of primitives to schedule drawing for. + /// @param primitiveMan A reference to PrimitiveMan, provided by Lua. + /// @param transValue The transparency value the primitives should be drawn at. From 0 (opaque) to 100 (transparent). + /// @param primitivesTable A Lua table of primitives to schedule drawing for. static void DrawPrimitivesWithTransparency(PrimitiveMan& primitiveMan, int transValue, const luabind::object& primitivesTable); - /// /// Schedule to draw multiple primitives of varying type with blending enabled. - /// - /// A reference to PrimitiveMan, provided by Lua. - /// The blending mode the primitives should be drawn with. See DrawBlendMode enumeration. - /// The blending amount for all the channels. 0-100. - /// A Lua table of primitives to schedule drawing for. + /// @param primitiveMan A reference to PrimitiveMan, provided by Lua. + /// @param blendMode The blending mode the primitives should be drawn with. See DrawBlendMode enumeration. + /// @param blendAmount The blending amount for all the channels. 0-100. + /// @param primitivesTable A Lua table of primitives to schedule drawing for. static void DrawPrimitivesWithBlending(PrimitiveMan& primitiveMan, int blendMode, int blendAmount, const luabind::object& primitivesTable); - /// /// Schedule to draw multiple primitives of varying type with blending enabled. - /// - /// A reference to PrimitiveMan, provided by Lua. - /// The blending mode the primitives should be drawn with. See DrawBlendMode enumeration. - /// The blending amount for the Red channel. 0-100. - /// The blending amount for the Green channel. 0-100. - /// The blending amount for the Blue channel. 0-100. - /// The blending amount for the Alpha channel. 0-100. - /// A Lua table of primitives to schedule drawing for. + /// @param primitiveMan A reference to PrimitiveMan, provided by Lua. + /// @param blendMode The blending mode the primitives should be drawn with. See DrawBlendMode enumeration. + /// @param blendAmountR The blending amount for the Red channel. 0-100. + /// @param blendAmountG The blending amount for the Green channel. 0-100. + /// @param blendAmountB The blending amount for the Blue channel. 0-100. + /// @param blendAmountA The blending amount for the Alpha channel. 0-100. + /// @param primitivesTable A Lua table of primitives to schedule drawing for. static void DrawPrimitivesWithBlendingPerChannel(PrimitiveMan& primitiveMan, int blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const luabind::object& primitivesTable); }; #pragma endregion #pragma region Utility Lua Adapters struct LuaAdaptersUtility { - /// /// Gets the ratio between the physics engine's meters and on-screen pixels. - /// - /// A float describing the current MPP ratio. + /// @return A float describing the current MPP ratio. static float GetMPP(); - /// /// Gets the ratio between on-screen pixels and the physics engine's meters. - /// - /// A float describing the current PPM ratio. + /// @return A float describing the current PPM ratio. static float GetPPM(); - /// /// Gets the ratio between the physics engine's Liters and on-screen pixels. - /// - /// A float describing the current LPP ratio. + /// @return A float describing the current LPP ratio. static float GetLPP(); - /// /// Gets the ratio between the on-screen pixels and the physics engine's Liters. - /// - /// A float describing the current PPL ratio. + /// @return A float describing the current PPL ratio. static float GetPPL(); - /// /// Gets the default pathfinder penetration value that'll allow pathing through corpses, debris, and such stuff. - /// - /// A float describing the default pathfinder penetration value. + /// @return A float describing the default pathfinder penetration value. static float GetPathFindingDefaultDigStrength(); - /// /// Explicit deletion of any Entity instance that Lua owns. It will probably be handled by the GC, but this makes it instantaneous. - /// - /// The Entity to delete. + /// @param entityToDelete The Entity to delete. static void DeleteEntity(Entity* entityToDelete); }; #pragma endregion diff --git a/Source/Lua/LuaAdapters.cpp b/Source/Lua/LuaAdapters.cpp index 242087162f..4ecef33977 100644 --- a/Source/Lua/LuaAdapters.cpp +++ b/Source/Lua/LuaAdapters.cpp @@ -7,8 +7,6 @@ namespace RTE { std::unordered_map> LuaAdaptersEntityCast::s_EntityToLuabindObjectCastFunctions = {}; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - #define LuaEntityCreateFunctionsDefinitionsForType(TYPE) \ TYPE* LuaAdaptersEntityCreate::Create##TYPE(std::string preseName, std::string moduleName) { \ const Entity* entityPreset = g_PresetMan.GetEntityPreset(#TYPE, preseName, moduleName); \ @@ -83,8 +81,6 @@ namespace RTE { LuaEntityCreateFunctionsDefinitionsForType(PieSlice); LuaEntityCreateFunctionsDefinitionsForType(PieMenu); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - #define LuaEntityCloneFunctionDefinitionForType(TYPE) \ TYPE* LuaAdaptersEntityClone::Clone##TYPE(const TYPE* thisEntity) { \ if (thisEntity) { \ @@ -127,8 +123,6 @@ namespace RTE { LuaEntityCloneFunctionDefinitionForType(PieSlice); LuaEntityCloneFunctionDefinitionForType(PieMenu); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - #define LuaEntityCastFunctionsDefinitionsForType(TYPE) \ TYPE* LuaAdaptersEntityCast::To##TYPE(Entity* entity) { \ TYPE* targetType = dynamic_cast(entity); \ @@ -194,8 +188,6 @@ namespace RTE { LuaEntityCastFunctionsDefinitionsForType(PieSlice); LuaEntityCastFunctionsDefinitionsForType(PieMenu); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - #define LuaPropertyOwnershipSafetyFakerFunctionDefinition(OBJECTTYPE, PROPERTYTYPE, SETTERFUNCTION) \ void LuaAdaptersPropertyOwnershipSafetyFaker::OBJECTTYPE##SETTERFUNCTION(OBJECTTYPE* luaSelfObject, PROPERTYTYPE* objectToSet) { \ luaSelfObject->SETTERFUNCTION(objectToSet ? dynamic_cast(objectToSet->Clone()) : nullptr); \ @@ -265,14 +257,10 @@ namespace RTE { LuaPropertyOwnershipSafetyFakerFunctionDefinition(HDFirearm, SoundContainer, SetReloadStartSound); LuaPropertyOwnershipSafetyFakerFunctionDefinition(HDFirearm, SoundContainer, SetReloadEndSound); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersEntity::SetPresetName(Entity* luaSelfObject, const std::string& presetName) { luaSelfObject->SetPresetName(presetName, true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector* LuaAdaptersActor::GetSceneWaypoints(Actor* luaSelfObject) { std::vector* sceneWaypoints = new std::vector(); sceneWaypoints->reserve(luaSelfObject->GetWaypointsSize()); @@ -284,8 +272,6 @@ namespace RTE { return sceneWaypoints; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaAdaptersScene::CalculatePath2(Scene* luaSelfObject, const Vector& start, const Vector& end, bool movePathToGround, float digStrength, Activity::Teams team) { std::list& threadScenePath = luaSelfObject->GetScenePath(); team = std::clamp(team, Activity::Teams::NoTeam, Activity::Teams::TeamFour); @@ -302,8 +288,6 @@ namespace RTE { return -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersScene::CalculatePathAsync2(Scene* luaSelfObject, const luabind::object& callbackParam, const Vector& start, const Vector& end, bool movePathToGround, float digStrength, Activity::Teams team) { team = std::clamp(team, Activity::Teams::NoTeam, Activity::Teams::TeamFour); @@ -338,33 +322,23 @@ namespace RTE { luaSelfObject->CalculatePathAsync(start, end, digStrength, team, callLuaCallback); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersAHuman::ReloadFirearms(AHuman* luaSelfObject) { luaSelfObject->ReloadFirearms(false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float LuaAdaptersSceneObject::GetTotalValue(const SceneObject* luaSelfObject, int nativeModule, float foreignMult) { return luaSelfObject->GetTotalValue(nativeModule, foreignMult, 1.0F); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaAdaptersSceneObject::GetBuyableMode(const SceneObject* luaSelfObject) { return static_cast(luaSelfObject->GetBuyableMode()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersActivity::SendMessage1(Activity* luaSelfObject, const std::string& message) { luabind::object context; SendMessage2(luaSelfObject, message, context); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersActivity::SendMessage2(Activity* luaSelfObject, const std::string& message, luabind::object context) { GAScripted* scriptedActivity = dynamic_cast(luaSelfObject); if (scriptedActivity) { @@ -373,14 +347,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersMovableObject::HasScript(MovableObject* luaSelfObject, const std::string& scriptPath) { return luaSelfObject->HasScript(g_PresetMan.GetFullModulePath(scriptPath)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersMovableObject::AddScript(MovableObject* luaSelfObject, const std::string& scriptPath) { switch (std::string correctedScriptPath = g_PresetMan.GetFullModulePath(scriptPath); luaSelfObject->LoadScript(correctedScriptPath)) { case 0: @@ -407,60 +377,42 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersMovableObject::EnableScript(MovableObject* luaSelfObject, const std::string& scriptPath) { return luaSelfObject->EnableOrDisableScript(g_PresetMan.GetFullModulePath(scriptPath), true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersMovableObject::DisableScript1(MovableObject* luaSelfObject) { std::string currentScriptFilePath(g_LuaMan.GetThreadCurrentLuaState()->GetCurrentlyRunningScriptFilePath()); return luaSelfObject->EnableOrDisableScript(currentScriptFilePath, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersMovableObject::DisableScript2(MovableObject* luaSelfObject, const std::string& scriptPath) { return luaSelfObject->EnableOrDisableScript(g_PresetMan.GetFullModulePath(scriptPath), false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableObject::SendMessage1(MovableObject* luaSelfObject, const std::string& message) { luaSelfObject->RunScriptedFunctionInAppropriateScripts("OnMessage", false, false, {}, {message}); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableObject::SendMessage2(MovableObject* luaSelfObject, const std::string& message, luabind::object context) { LuabindObjectWrapper wrapper(&context, "", false); luaSelfObject->RunScriptedFunctionInAppropriateScripts("OnMessage", false, false, {}, {message}, {&wrapper}); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMOSRotating::GibThis(MOSRotating* luaSelfObject) { luaSelfObject->GibThis(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector* LuaAdaptersMOSRotating::GetWounds1(const MOSRotating* luaSelfObject) { return GetWounds2(luaSelfObject, true, false, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector* LuaAdaptersMOSRotating::GetWounds2(const MOSRotating* luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) { auto* wounds = new std::vector(); GetWoundsImpl(luaSelfObject, includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables, *wounds); return wounds; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMOSRotating::GetWoundsImpl(const MOSRotating* luaSelfObject, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables, std::vector& wounds) { wounds.insert(wounds.end(), luaSelfObject->GetWoundList().begin(), luaSelfObject->GetWoundList().end()); @@ -477,8 +429,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::list* LuaAdaptersBuyMenuGUI::GetOrderList(const BuyMenuGUI* luaSelfObject) { std::list constOrderList; luaSelfObject->GetOrderList(constOrderList); @@ -494,8 +444,6 @@ namespace RTE { return orderList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Attachable* LuaAdaptersAttachable::RemoveFromParent1(Attachable* luaSelfObject) { if (luaSelfObject->IsAttached()) { return luaSelfObject->GetParent()->RemoveAttachable(luaSelfObject); @@ -503,8 +451,6 @@ namespace RTE { return luaSelfObject; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Attachable* LuaAdaptersAttachable::RemoveFromParent2(Attachable* luaSelfObject, bool addToMovableMan, bool addBreakWounds) { if (luaSelfObject->IsAttached()) { return luaSelfObject->GetParent()->RemoveAttachable(luaSelfObject, addToMovableMan, addBreakWounds); @@ -512,38 +458,26 @@ namespace RTE { return luaSelfObject; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersTurret::AddMountedFirearm(Turret* luaSelfObject, HDFirearm* newMountedDevice) { luaSelfObject->AddMountedDevice(newMountedDevice); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersGlobalScript::Deactivate(GlobalScript* luaSelfObject) { luaSelfObject->SetActive(false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPieMenu::AddPieSlice(PieMenu* luaSelfObject, PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource) { return luaSelfObject->AddPieSlice(pieSliceToAdd, pieSliceOriginalSource, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique1(PieMenu* luaSelfObject, PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource) { return luaSelfObject->AddPieSliceIfPresetNameIsUnique(pieSliceToAdd, pieSliceOriginalSource, false, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPieMenu::AddPieSliceIfPresetNameIsUnique2(PieMenu* luaSelfObject, PieSlice* pieSliceToAdd, const Entity* pieSliceOriginalSource, bool onlyCheckPieSlicesWithSameOriginalSource) { return luaSelfObject->AddPieSliceIfPresetNameIsUnique(pieSliceToAdd, pieSliceOriginalSource, onlyCheckPieSlicesWithSameOriginalSource, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::AddMO(MovableMan& movableMan, MovableObject* movableObject) { if (movableMan.ValidMO(movableObject)) { g_ConsoleMan.PrintString("ERROR: Tried to add a MovableObject that already exists in the simulation! " + movableObject->GetPresetName()); @@ -552,8 +486,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::AddActor(MovableMan& movableMan, Actor* actor) { if (movableMan.IsActor(actor)) { g_ConsoleMan.PrintString("ERROR: Tried to add an Actor that already exists in the simulation!" + actor->GetPresetName()); @@ -562,8 +494,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::AddItem(MovableMan& movableMan, HeldDevice* item) { if (movableMan.ValidMO(dynamic_cast(item))) { g_ConsoleMan.PrintString("ERROR: Tried to add an Item that already exists in the simulation!" + item->GetPresetName()); @@ -572,8 +502,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::AddParticle(MovableMan& movableMan, MovableObject* particle) { if (movableMan.ValidMO(particle)) { g_ConsoleMan.PrintString("ERROR: Tried to add a Particle that already exists in the simulation!" + particle->GetPresetName()); @@ -582,8 +510,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::SendGlobalMessage1(MovableMan& movableMan, const std::string& message) { GAScripted* scriptedActivity = dynamic_cast(g_ActivityMan.GetActivity()); if (scriptedActivity) { @@ -593,8 +519,6 @@ namespace RTE { movableMan.RunLuaFunctionOnAllMOs("OnGlobalMessage", true, {}, {message}); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersMovableMan::SendGlobalMessage2(MovableMan& movableMan, const std::string& message, luabind::object context) { LuabindObjectWrapper wrapper(&context, "", false); @@ -606,138 +530,94 @@ namespace RTE { movableMan.RunLuaFunctionOnAllMOs("OnGlobalMessage", true, {}, {message}, {&wrapper}); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - double LuaAdaptersTimerMan::GetDeltaTimeTicks(const TimerMan& timerMan) { return static_cast(timerMan.GetDeltaTimeTicks()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - double LuaAdaptersTimerMan::GetTicksPerSecond(const TimerMan& timerMan) { return static_cast(timerMan.GetTicksPerSecond()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersUInputMan::MouseButtonHeld(const UInputMan& uinputMan, int whichButton) { return uinputMan.MouseButtonHeld(whichButton, Players::PlayerOne); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersUInputMan::MouseButtonPressed(const UInputMan& uinputMan, int whichButton) { return uinputMan.MouseButtonPressed(whichButton, Players::PlayerOne); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersUInputMan::MouseButtonReleased(const UInputMan& uinputMan, int whichButton) { return uinputMan.MouseButtonReleased(whichButton, Players::PlayerOne); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPresetMan::ReloadEntityPreset1(PresetMan& presetMan, const std::string& presetName, const std::string& className, const std::string& moduleName) { return presetMan.ReloadEntityPreset(presetName, className, moduleName); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaAdaptersPresetMan::ReloadEntityPreset2(PresetMan& presetMan, const std::string& presetName, const std::string& className) { return ReloadEntityPreset1(presetMan, presetName, className, ""); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::list* LuaAdaptersPresetMan::GetAllEntitiesOfGroup(PresetMan& presetMan, const std::string& group, const std::string& type, int whichModule) { std::list* entityList = new std::list(); presetMan.GetAllOfGroup(*entityList, group, type, whichModule); return entityList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::list* LuaAdaptersSceneMan::WrapBoxes(SceneMan& sceneMan, const Box& boxToWrap) { std::list* wrappedBoxes = new std::list(); sceneMan.WrapBox(boxToWrap, *wrappedBoxes); return wrappedBoxes; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPolygonPrimitive(PrimitiveMan& primitiveMan, const Vector& centerPos, int color, const luabind::object& verticesTable) { primitiveMan.DrawPolygonOrPolygonFillPrimitive(-1, centerPos, color, ConvertLuaTableToVectorOfType(verticesTable), false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPolygonPrimitiveForPlayer(PrimitiveMan& primitiveMan, int player, const Vector& centerPos, int color, const luabind::object& verticesTable) { primitiveMan.DrawPolygonOrPolygonFillPrimitive(player, centerPos, color, ConvertLuaTableToVectorOfType(verticesTable), false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitive(PrimitiveMan& primitiveMan, const Vector& startPos, int color, const luabind::object& verticesTable) { primitiveMan.DrawPolygonOrPolygonFillPrimitive(-1, startPos, color, ConvertLuaTableToVectorOfType(verticesTable), true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPolygonFillPrimitiveForPlayer(PrimitiveMan& primitiveMan, int player, const Vector& startPos, int color, const luabind::object& verticesTable) { primitiveMan.DrawPolygonOrPolygonFillPrimitive(player, startPos, color, ConvertLuaTableToVectorOfType(verticesTable), true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPrimitivesWithTransparency(PrimitiveMan& primitiveMan, int transValue, const luabind::object& primitivesTable) { primitiveMan.SchedulePrimitivesForBlendedDrawing(DrawBlendMode::BlendTransparency, transValue, transValue, transValue, BlendAmountLimits::MinBlend, ConvertLuaTableToVectorOfType(primitivesTable)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlending(PrimitiveMan& primitiveMan, int blendMode, int blendAmount, const luabind::object& primitivesTable) { primitiveMan.SchedulePrimitivesForBlendedDrawing(static_cast(blendMode), blendAmount, blendAmount, blendAmount, blendAmount, ConvertLuaTableToVectorOfType(primitivesTable)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlendingPerChannel(PrimitiveMan& primitiveMan, int blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const luabind::object& primitivesTable) { primitiveMan.SchedulePrimitivesForBlendedDrawing(static_cast(blendMode), blendAmountR, blendAmountG, blendAmountB, blendAmountA, ConvertLuaTableToVectorOfType(primitivesTable)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float LuaAdaptersUtility::GetMPP() { return c_MPP; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float LuaAdaptersUtility::GetPPM() { return c_PPM; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float LuaAdaptersUtility::GetLPP() { return c_LPP; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float LuaAdaptersUtility::GetPPL() { return c_PPL; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float LuaAdaptersUtility::GetPathFindingDefaultDigStrength() { return c_PathFindingDefaultDigStrength; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaAdaptersUtility::DeleteEntity(Entity* entityToDelete) { delete entityToDelete; entityToDelete = nullptr; diff --git a/Source/Lua/LuaBindingRegisterDefinitions.h b/Source/Lua/LuaBindingRegisterDefinitions.h index b1006a52b4..24d8573c33 100644 --- a/Source/Lua/LuaBindingRegisterDefinitions.h +++ b/Source/Lua/LuaBindingRegisterDefinitions.h @@ -131,51 +131,37 @@ namespace RTE { PER_LUA_BINDING(DrawBlendMode) #pragma region Lua Binding Registration Macros -/// /// Convenience macro for declaring a binding register function. -/// #define LuaBindingRegisterFunctionDeclarationForType(TYPE) \ static luabind::scope Register##TYPE##LuaBindings() -/// /// Convenience macro for defining a binding register function. -/// #define LuaBindingRegisterFunctionDefinitionForType(OWNINGSCOPE, TYPE) \ luabind::scope OWNINGSCOPE::Register##TYPE##LuaBindings() -/// /// Convenience macro for a LuaBind scope definition of an abstract type. -/// #define AbstractTypeLuaClassDefinition(TYPE, PARENTTYPE) \ luabind::class_(#TYPE) \ .property("ClassName", &TYPE::GetClassName) -/// /// Convenience macro for a LuaBind scope definition of a concrete type. -/// #define ConcreteTypeLuaClassDefinition(TYPE, PARENTTYPE) \ luabind::class_(#TYPE) \ .def("Clone", &LuaAdaptersEntityClone::Clone##TYPE, luabind::adopt(luabind::result)) \ .property("ClassName", &TYPE::GetClassName) -/// /// Convenience macro for calling a register function of a type. -/// #define RegisterLuaBindingsOfType(OWNINGSCOPE, TYPE) \ OWNINGSCOPE::Register##TYPE##LuaBindings() -/// /// Convenience macro for calling a register function of an abstract type, along with registering global bindings for adapters relevant to the type. -/// #define RegisterLuaBindingsOfAbstractType(OWNINGSCOPE, TYPE) \ luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (TYPE * (*)(Entity*)) & LuaAdaptersEntityCast::To##TYPE), \ luabind::def((std::string("To") + std::string(#TYPE)).c_str(), (const TYPE* (*)(const Entity*)) & LuaAdaptersEntityCast::ToConst##TYPE), \ luabind::def((std::string("Is") + std::string(#TYPE)).c_str(), (bool (*)(const Entity*)) & LuaAdaptersEntityCast::Is##TYPE), \ OWNINGSCOPE::Register##TYPE##LuaBindings() -/// /// Convenience macro for calling a register function of a concrete type, along with registering global bindings for adapters relevant to the type. -/// #define RegisterLuaBindingsOfConcreteType(OWNINGSCOPE, TYPE) \ luabind::def((std::string("Create") + std::string(#TYPE)).c_str(), (TYPE * (*)(std::string, std::string)) & LuaAdaptersEntityCreate::Create##TYPE, luabind::adopt(luabind::result)), \ luabind::def((std::string("Create") + std::string(#TYPE)).c_str(), (TYPE * (*)(std::string)) & LuaAdaptersEntityCreate::Create##TYPE, luabind::adopt(luabind::result)), \ @@ -188,9 +174,7 @@ namespace RTE { OWNINGSCOPE::Register##TYPE##LuaBindings() #pragma endregion - /// /// Struct that contains Lua binding registration functions for System classes. - /// struct SystemLuaBindings { LuaBindingRegisterFunctionDeclarationForType(Box); LuaBindingRegisterFunctionDeclarationForType(Controller); @@ -200,9 +184,7 @@ namespace RTE { LuaBindingRegisterFunctionDeclarationForType(PathRequest); }; - /// /// Struct that contains Lua binding registration functions for Manager classes. - /// struct ManagerLuaBindings { LuaBindingRegisterFunctionDeclarationForType(ActivityMan); LuaBindingRegisterFunctionDeclarationForType(AudioMan); @@ -221,9 +203,7 @@ namespace RTE { LuaBindingRegisterFunctionDeclarationForType(UInputMan); }; - /// /// Struct that contains Lua binding registration functions for Entity classes. - /// struct EntityLuaBindings { LuaBindingRegisterFunctionDeclarationForType(Entity); LuaBindingRegisterFunctionDeclarationForType(ACDropShip); @@ -270,26 +250,20 @@ namespace RTE { LuaBindingRegisterFunctionDeclarationForType(Turret); }; - /// /// Struct that contains Lua binding registration functions for Activity classes. - /// struct ActivityLuaBindings { LuaBindingRegisterFunctionDeclarationForType(Activity); LuaBindingRegisterFunctionDeclarationForType(GameActivity); }; - /// /// Struct that contains Lua binding registration functions for GUI classes. - /// struct GUILuaBindings { LuaBindingRegisterFunctionDeclarationForType(GUIBanner); LuaBindingRegisterFunctionDeclarationForType(BuyMenuGUI); LuaBindingRegisterFunctionDeclarationForType(SceneEditorGUI); }; - /// /// Struct that contains Lua binding registration functions for GraphicalPrimitive classes. - /// struct PrimitiveLuaBindings { LuaBindingRegisterFunctionDeclarationForType(GraphicalPrimitive); LuaBindingRegisterFunctionDeclarationForType(LinePrimitive); @@ -309,9 +283,7 @@ namespace RTE { LuaBindingRegisterFunctionDeclarationForType(BitmapPrimitive); }; - /// /// Struct that contains Lua binding registration functions for various input enumerations. - /// struct InputLuaBindings { LuaBindingRegisterFunctionDeclarationForType(InputDevice); LuaBindingRegisterFunctionDeclarationForType(InputElements); @@ -324,9 +296,7 @@ namespace RTE { LuaBindingRegisterFunctionDeclarationForType(SDL_GameControllerAxis); }; - /// /// Struct that contains Lua binding registration functions for types that don't really belong in any of the other binding structs. - /// struct MiscLuaBindings { LuaBindingRegisterFunctionDeclarationForType(AlarmEvent); LuaBindingRegisterFunctionDeclarationForType(Directions); diff --git a/Source/Lua/LuaBindingsActivities.cpp b/Source/Lua/LuaBindingsActivities.cpp index e6de5cd4b2..8046246e56 100644 --- a/Source/Lua/LuaBindingsActivities.cpp +++ b/Source/Lua/LuaBindingsActivities.cpp @@ -4,8 +4,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ActivityLuaBindings, Activity) { return AbstractTypeLuaClassDefinition(Activity, Entity) @@ -111,8 +109,6 @@ namespace RTE { luabind::value("UNFAIRSKILL", Activity::AISkillSetting::UnfairSkill)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ActivityLuaBindings, GameActivity) { return luabind::class_("GameActivity") diff --git a/Source/Lua/LuaBindingsEntities.cpp b/Source/Lua/LuaBindingsEntities.cpp index 48f9dd7adf..28ce718056 100644 --- a/Source/Lua/LuaBindingsEntities.cpp +++ b/Source/Lua/LuaBindingsEntities.cpp @@ -4,8 +4,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Entity) { return luabind::class_("Entity") @@ -28,8 +26,6 @@ namespace RTE { .def("IsInGroup", &Entity::IsInGroup); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ACDropShip) { return ConcreteTypeLuaClassDefinition(ACDropShip, ACraft) @@ -48,8 +44,6 @@ namespace RTE { .def("GetAltitude", &ACDropShip::GetAltitude); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ACrab) { return ConcreteTypeLuaClassDefinition(ACrab, Actor) @@ -112,8 +106,6 @@ namespace RTE { luabind::value("LANDJUMP", ACrab::JumpState::LANDJUMP)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ACraft) { return AbstractTypeLuaClassDefinition(ACraft, Actor) @@ -148,8 +140,6 @@ namespace RTE { luabind::value("ASCEND", ACraft::AltitudeMoveState::ASCEND)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ACRocket) { return ConcreteTypeLuaClassDefinition(ACRocket, ACraft) @@ -169,8 +159,6 @@ namespace RTE { luabind::value("GearStateCount", ACRocket::LandingGearState::GearStateCount)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Actor) { return ConcreteTypeLuaClassDefinition(Actor, MOSRotating) @@ -318,8 +306,6 @@ namespace RTE { luabind::value("FOLLOWWAIT", Actor::TeamBlockState::FOLLOWWAIT)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ADoor) { return ConcreteTypeLuaClassDefinition(ADoor, Actor) @@ -343,8 +329,6 @@ namespace RTE { luabind::value("STOPPED", ADoor::DoorState::STOPPED)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, AEmitter) { return ConcreteTypeLuaClassDefinition(AEmitter, Attachable) @@ -385,8 +369,6 @@ namespace RTE { .def("JustStartedEmitting", &AEmitter::JustStartedEmitting); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, AEJetpack) { return ConcreteTypeLuaClassDefinition(AEJetpack, AEmitter) @@ -403,8 +385,6 @@ namespace RTE { luabind::value("JumpPack", AEJetpack::JumpPack)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, AHuman) { return ConcreteTypeLuaClassDefinition(AHuman, Actor) @@ -510,8 +490,6 @@ namespace RTE { luabind::value("LANDJUMP", AHuman::JumpState::LANDJUMP)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Arm) { return ConcreteTypeLuaClassDefinition(Arm, Attachable) @@ -539,8 +517,6 @@ namespace RTE { .def("ClearHandTargets", &Arm::ClearHandTargets); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Attachable) { return ConcreteTypeLuaClassDefinition(Attachable, MOSRotating) @@ -571,8 +547,6 @@ namespace RTE { .def("RemoveFromParent", &LuaAdaptersAttachable::RemoveFromParent2, luabind::adopt(luabind::return_value)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Deployment) { return AbstractTypeLuaClassDefinition(Deployment, SceneObject) @@ -585,8 +559,6 @@ namespace RTE { .def("CreateDeployedObject", (SceneObject * (Deployment::*)()) & Deployment::CreateDeployedObject, luabind::adopt(luabind::result)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Emission) { return AbstractTypeLuaClassDefinition(Emission, Entity) @@ -602,8 +574,6 @@ namespace RTE { .def("ResetEmissionTimers", &Emission::ResetEmissionTimers); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Gib) { return luabind::class_("Gib") @@ -624,16 +594,12 @@ namespace RTE { luabind::value("SpreadSpiral", Gib::SpreadMode::SpreadSpiral)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, GlobalScript) { return AbstractTypeLuaClassDefinition(GlobalScript, Entity) .def("Deactivate", &LuaAdaptersGlobalScript::Deactivate); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, HDFirearm) { return ConcreteTypeLuaClassDefinition(HDFirearm, HeldDevice) @@ -686,8 +652,6 @@ namespace RTE { .def("SetNextMagazineName", &HDFirearm::SetNextMagazineName); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, HeldDevice) { return ConcreteTypeLuaClassDefinition(HeldDevice, Attachable) @@ -731,8 +695,6 @@ namespace RTE { .def("RemovePickupableByPresetName", &HeldDevice::RemovePickupableByPresetName); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Leg) { return ConcreteTypeLuaClassDefinition(Leg, Attachable) @@ -740,8 +702,6 @@ namespace RTE { .property("MoveSpeed", &Leg::GetMoveSpeed, &Leg::SetMoveSpeed); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, LimbPath) { return luabind::class_("LimbPath") @@ -752,8 +712,6 @@ namespace RTE { .def("GetSegment", &LimbPath::GetSegment); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Magazine) { return ConcreteTypeLuaClassDefinition(Magazine, Attachable) @@ -766,8 +724,6 @@ namespace RTE { .property("Discardable", &Magazine::IsDiscardable); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Material) { return luabind::class_("Material") @@ -786,8 +742,6 @@ namespace RTE { .property("IsScrap", &Material::IsScrap); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MetaPlayer) { return luabind::class_("MetaPlayer") @@ -802,8 +756,6 @@ namespace RTE { .def("ChangeBrainPoolCount", &MetaPlayer::ChangeBrainPoolCount); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MOPixel) { return ConcreteTypeLuaClassDefinition(MOPixel, MovableObject) @@ -811,14 +763,10 @@ namespace RTE { .property("Staininess", &MOPixel::GetStaininess, &MOPixel::SetStaininess); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MOSParticle) { return ConcreteTypeLuaClassDefinition(MOSParticle, MOSprite); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MOSprite) { return AbstractTypeLuaClassDefinition(MOSprite, MovableObject) @@ -861,8 +809,6 @@ namespace RTE { luabind::value("ONCOLLIDE", SpriteAnimMode::ONCOLLIDE)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MOSRotating) { return ConcreteTypeLuaClassDefinition(MOSRotating, MOSprite) @@ -921,8 +867,6 @@ namespace RTE { .def("GibThis", &LuaAdaptersMOSRotating::GibThis); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, MovableObject) { return AbstractTypeLuaClassDefinition(MovableObject, SceneObject) @@ -1038,8 +982,6 @@ namespace RTE { .def("RequestSyncedUpdate", &MovableObject::RequestSyncedUpdate); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, PEmitter) { return ConcreteTypeLuaClassDefinition(PEmitter, MOSParticle) @@ -1066,8 +1008,6 @@ namespace RTE { .def("JustStartedEmitting", &PEmitter::JustStartedEmitting); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, PieSlice) { return ConcreteTypeLuaClassDefinition(PieSlice, Entity) @@ -1122,8 +1062,6 @@ namespace RTE { luabind::value("Team4", PieSlice::SliceType::EditorTeam4)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, PieMenu) { return ConcreteTypeLuaClassDefinition(PieMenu, Entity) @@ -1165,8 +1103,6 @@ namespace RTE { .def("ReplacePieSlice", &PieMenu::ReplacePieSlice, luabind::adopt(luabind::result) + luabind::adopt(_3)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Round) { return ConcreteTypeLuaClassDefinition(Round, Entity) @@ -1182,8 +1118,6 @@ namespace RTE { .property("IsEmpty", &Round::IsEmpty); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Scene) { return ConcreteTypeLuaClassDefinition(Scene, Entity) @@ -1235,8 +1169,6 @@ namespace RTE { luabind::value("PLACEDSETSCOUNT", Scene::PlacedObjectSets::PLACEDSETSCOUNT)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SceneArea) { return luabind::class_("Area") @@ -1264,14 +1196,10 @@ namespace RTE { .def("GetRandomPoint", &Scene::Area::GetRandomPoint); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SceneLayer) { return luabind::class_("SceneLayer"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SceneObject) { return AbstractTypeLuaClassDefinition(SceneObject, Entity) @@ -1299,8 +1227,6 @@ namespace RTE { luabind::value("SCRIPTONLY", static_cast(SceneObject::BuyableMode::ScriptOnly))]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SLBackground) { return luabind::class_("SLBackground") @@ -1318,8 +1244,6 @@ namespace RTE { .def("IsAutoScrolling", &SLBackground::IsAutoScrolling); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SoundContainer) { return ConcreteTypeLuaClassDefinition(SoundContainer, Entity) @@ -1362,8 +1286,6 @@ namespace RTE { luabind::value("IGNORE_PLAY", SoundContainer::SoundOverlapMode::IGNORE_PLAY)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, SoundSet) { return luabind::class_("SoundSet") @@ -1386,16 +1308,12 @@ namespace RTE { luabind::value("ALL", SoundSet::SoundSelectionCycleMode::ALL)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, TDExplosive) { return ConcreteTypeLuaClassDefinition(TDExplosive, ThrownDevice) .property("IsAnimatedManually", &TDExplosive::IsAnimatedManually, &TDExplosive::SetAnimatedManually); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, TerrainObject) { return ConcreteTypeLuaClassDefinition(TerrainObject, SceneObject) @@ -1404,8 +1322,6 @@ namespace RTE { .def("GetBitmapHeight", &TerrainObject::GetBitmapHeight); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, ThrownDevice) { return ConcreteTypeLuaClassDefinition(ThrownDevice, HeldDevice) @@ -1417,8 +1333,6 @@ namespace RTE { .def("GetCalculatedMaxThrowVelIncludingArmThrowStrength", &ThrownDevice::GetCalculatedMaxThrowVelIncludingArmThrowStrength); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Turret) { return ConcreteTypeLuaClassDefinition(Turret, Attachable) diff --git a/Source/Lua/LuaBindingsGUI.cpp b/Source/Lua/LuaBindingsGUI.cpp index a80d425937..90461c13d9 100644 --- a/Source/Lua/LuaBindingsGUI.cpp +++ b/Source/Lua/LuaBindingsGUI.cpp @@ -4,8 +4,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(GUILuaBindings, GUIBanner) { return luabind::class_("GUIBanner") @@ -32,8 +30,6 @@ namespace RTE { luabind::value("YELLOW", GameActivity::BannerColor::YELLOW)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(GUILuaBindings, BuyMenuGUI) { return luabind::class_("BuyMenuGUI") @@ -69,8 +65,6 @@ namespace RTE { .def("GetTotalOrderPassengers", &BuyMenuGUI::GetTotalOrderPassengers); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(GUILuaBindings, SceneEditorGUI) { return luabind::class_("SceneEditorGUI") diff --git a/Source/Lua/LuaBindingsInput.cpp b/Source/Lua/LuaBindingsInput.cpp index 016dc61ccb..13ae061f86 100644 --- a/Source/Lua/LuaBindingsInput.cpp +++ b/Source/Lua/LuaBindingsInput.cpp @@ -8,8 +8,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, InputDevice) { return luabind::class_("InputDevice") @@ -22,8 +20,6 @@ namespace RTE { luabind::value("DEVICE_COUNT", InputDevice::DEVICE_COUNT)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, InputElements) { return luabind::class_("InputElements") @@ -52,8 +48,6 @@ namespace RTE { luabind::value("INPUT_COUNT", InputElements::INPUT_COUNT)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, MouseButtons) { return luabind::class_("MouseButtons") @@ -64,8 +58,6 @@ namespace RTE { luabind::value("MAX_MOUSE_BUTTONS", MouseButtons::MAX_MOUSE_BUTTONS)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, JoyButtons) { return luabind::class_("JoyButtons") @@ -85,8 +77,6 @@ namespace RTE { luabind::value("MAX_JOY_BUTTONS", JoyButtons::MAX_JOY_BUTTONS)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, JoyDirections) { return luabind::class_("JoyDirections") @@ -94,8 +84,6 @@ namespace RTE { luabind::value("JOYDIR_TWO", JoyDirections::JOYDIR_TWO)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, SDL_Keycode) { return luabind::class_("Key") @@ -338,8 +326,6 @@ namespace RTE { luabind::value("SLEEP", SDLK_SLEEP)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, SDL_Scancode) { return luabind::class_("Scancode") @@ -590,8 +576,6 @@ namespace RTE { luabind::value("NUM_SCANCODES", SDL_NUM_SCANCODES)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, SDL_GameControllerButton) { return luabind::class_("GamepadButton") @@ -614,8 +598,6 @@ namespace RTE { luabind::value("MAX", SDL_CONTROLLER_BUTTON_MAX)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(InputLuaBindings, SDL_GameControllerAxis) { return luabind::class_("GamepadAxis") diff --git a/Source/Lua/LuaBindingsManagers.cpp b/Source/Lua/LuaBindingsManagers.cpp index 1a9ea3bec4..102074564c 100644 --- a/Source/Lua/LuaBindingsManagers.cpp +++ b/Source/Lua/LuaBindingsManagers.cpp @@ -4,8 +4,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, ActivityMan) { return luabind::class_("ActivityManager") @@ -26,8 +24,6 @@ namespace RTE { .def("LoadGame", &ActivityMan::LoadAndLaunchGame); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, AudioMan) { return luabind::class_("AudioManager") @@ -53,8 +49,6 @@ namespace RTE { .def("PlaySound", (SoundContainer * (AudioMan::*)(const std::string& filePath, const Vector& position, int player)) & AudioMan::PlaySound, luabind::adopt(luabind::result)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, ConsoleMan) { return luabind::class_("ConsoleManager") @@ -64,8 +58,6 @@ namespace RTE { .def("Clear", &ConsoleMan::ClearLog); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, FrameMan) { return luabind::class_("FrameManager") @@ -88,8 +80,6 @@ namespace RTE { .def("SplitStringToFitWidth", &FrameMan::SplitStringToFitWidth); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, MetaMan) { return luabind::class_("MetaManager") @@ -104,8 +94,6 @@ namespace RTE { .def("GetMetaPlayerOfInGamePlayer", &MetaMan::GetMetaPlayerOfInGamePlayer); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, MovableMan) { return luabind::class_("MovableManager") @@ -174,24 +162,18 @@ namespace RTE { .def("AddParticle", &LuaAdaptersMovableMan::AddParticle, luabind::adopt(_2)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, PerformanceMan) { return luabind::class_("PerformanceManager") .property("ShowPerformanceStats", &PerformanceMan::IsShowingPerformanceStats, &PerformanceMan::ShowPerformanceStats); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, PostProcessMan) { return luabind::class_("PostProcessManager") .def("RegisterPostEffect", &PostProcessMan::RegisterPostEffect); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, PresetMan) { return luabind::class_("PresetManager") @@ -224,8 +206,6 @@ namespace RTE { .def("GetFullModulePath", &PresetMan::GetFullModulePath); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, PrimitiveMan) { return luabind::class_("PrimitiveManager") @@ -283,8 +263,6 @@ namespace RTE { .def("DrawPrimitives", &LuaAdaptersPrimitiveMan::DrawPrimitivesWithBlendingPerChannel); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, SceneMan) { return luabind::class_("SceneManager") @@ -354,8 +332,6 @@ namespace RTE { .def("DislodgePixel", &SceneMan::DislodgePixel); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, CameraMan) { return luabind::class_("CameraManager") @@ -372,8 +348,6 @@ namespace RTE { .def("AddScreenShake", (void(CameraMan::*)(float, const Vector&)) & CameraMan::AddScreenShake); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, SettingsMan) { return luabind::class_("SettingsManager") @@ -384,8 +358,6 @@ namespace RTE { .property("AutomaticGoldDeposit", &SettingsMan::GetAutomaticGoldDeposit); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, TimerMan) { return luabind::class_("TimerManager") @@ -403,8 +375,6 @@ namespace RTE { .def("DrawnSimUpdate", &TimerMan::DrawnSimUpdate); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(ManagerLuaBindings, UInputMan) { return luabind::class_("UInputManager") diff --git a/Source/Lua/LuaBindingsMisc.cpp b/Source/Lua/LuaBindingsMisc.cpp index 361e9c9d47..554d860490 100644 --- a/Source/Lua/LuaBindingsMisc.cpp +++ b/Source/Lua/LuaBindingsMisc.cpp @@ -4,8 +4,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(MiscLuaBindings, AlarmEvent) { return luabind::class_("AlarmEvent") @@ -17,8 +15,6 @@ namespace RTE { .def_readwrite("Range", &AlarmEvent::m_Range); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(MiscLuaBindings, Directions) { return luabind::class_("Directions") @@ -30,8 +26,6 @@ namespace RTE { luabind::value("Any", Directions::Any)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(MiscLuaBindings, DrawBlendMode) { return luabind::class_("DrawBlendMode") diff --git a/Source/Lua/LuaBindingsPrimitives.cpp b/Source/Lua/LuaBindingsPrimitives.cpp index 57fc5171c4..fd7e3d56e6 100644 --- a/Source/Lua/LuaBindingsPrimitives.cpp +++ b/Source/Lua/LuaBindingsPrimitives.cpp @@ -4,126 +4,94 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, GraphicalPrimitive) { return luabind::class_("GraphicalPrimitive"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, LinePrimitive) { return luabind::class_("LinePrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, ArcPrimitive) { return luabind::class_("ArcPrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, SplinePrimitive) { return luabind::class_("SplinePrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, BoxPrimitive) { return luabind::class_("BoxPrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, BoxFillPrimitive) { return luabind::class_("BoxFillPrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, RoundedBoxPrimitive) { return luabind::class_("RoundedBoxPrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, RoundedBoxFillPrimitive) { return luabind::class_("RoundedBoxFillPrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, CirclePrimitive) { return luabind::class_("CirclePrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, CircleFillPrimitive) { return luabind::class_("CircleFillPrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, EllipsePrimitive) { return luabind::class_("EllipsePrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, EllipseFillPrimitive) { return luabind::class_("EllipseFillPrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, TrianglePrimitive) { return luabind::class_("TrianglePrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, TriangleFillPrimitive) { return luabind::class_("TriangleFillPrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, TextPrimitive) { return luabind::class_("TextPrimitive") .def(luabind::constructor()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(PrimitiveLuaBindings, BitmapPrimitive) { return luabind::class_("BitmapPrimitive") diff --git a/Source/Lua/LuaBindingsSystem.cpp b/Source/Lua/LuaBindingsSystem.cpp index 2e964d7999..e63e8393c9 100644 --- a/Source/Lua/LuaBindingsSystem.cpp +++ b/Source/Lua/LuaBindingsSystem.cpp @@ -4,8 +4,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, Box) { return luabind::class_("Box") @@ -34,8 +32,6 @@ namespace RTE { .def("IntersectsBox", &Box::IntersectsBox); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, Controller) { return luabind::class_("Controller") @@ -112,8 +108,6 @@ namespace RTE { luabind::value("CIM_INPUTMODECOUNT", Controller::InputMode::CIM_INPUTMODECOUNT)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, DataModule) { return luabind::class_("DataModule") @@ -128,8 +122,6 @@ namespace RTE { .def_readwrite("Presets", &DataModule::m_EntityList, luabind::return_stl_iterator); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, Timer) { return luabind::class_("Timer") @@ -167,8 +159,6 @@ namespace RTE { .def("AlternateSim", &Timer::AlternateSim); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, Vector) { return luabind::class_("Vector") @@ -231,8 +221,6 @@ namespace RTE { .def("SetXY", &Vector::SetXY); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaBindingRegisterFunctionDefinitionForType(SystemLuaBindings, PathRequest) { using namespace micropather; return luabind::class_("PathRequest") diff --git a/Source/Lua/LuabindDefinitions.h b/Source/Lua/LuabindDefinitions.h index d4fae1b310..ee0dcb6481 100644 --- a/Source/Lua/LuabindDefinitions.h +++ b/Source/Lua/LuabindDefinitions.h @@ -17,27 +17,21 @@ #include "luabind/return_reference_to_policy.hpp" namespace luabind { - /// /// Function that extracts the raw pointer from the smart pointer. This is needed when Lua calls member functions on held types, the 'this' pointer must be a raw pointer, it is also needed to allow the smart_pointer to raw_pointer conversion from Lua to C++. - /// - /// The smart pointer to get raw pointer for. - /// Raw pointer of the passed in smart pointer. + /// @param ptr The smart pointer to get raw pointer for. + /// @return Raw pointer of the passed in smart pointer. template Type* get_pointer(boost::shared_ptr& ptr) { return ptr.get(); } - /// /// Can't have global enums in the master state so we use this dummy struct as a class and register the enums under it. - /// struct enum_wrapper {}; } // namespace luabind namespace RTE { - /// /// Derived structs for each of the input enums because we can't register enum_wrapper multiple times under a different name. /// We're doing this so we can access each enum separately by name rather than having all of them accessed from a shared name. /// If this proves to be a hassle then we can easily revert to the shared name access by registering everything under enum_wrapper. - /// struct input_device : public luabind::enum_wrapper {}; struct input_elements : public luabind::enum_wrapper {}; struct mouse_buttons : public luabind::enum_wrapper {}; @@ -50,11 +44,9 @@ namespace RTE { struct directions : public luabind::enum_wrapper {}; struct blend_modes : public luabind::enum_wrapper {}; - /// /// Special callback function for adding file name and line number to error messages when calling functions incorrectly. - /// - /// The Lua master state. - /// An error signal, 1, so Lua correctly reports that there's been an error. + /// @param luaState The Lua master state. + /// @return An error signal, 1, so Lua correctly reports that there's been an error. static int AddFileAndLineToError(lua_State* luaState) { lua_Debug luaDebug; if (lua_getstack(luaState, 2, &luaDebug) > 0) { @@ -73,12 +65,10 @@ namespace RTE { return 1; } - /// /// Converts a Lua table to a C++ vector of the specified type. Ownership of the objects in the Lua table is transferred! - /// - /// The Lua table object to convert to vector. - /// A C++ vector containing all the objects from the Lua table. Ownership is transferred! - /// In case of type mismatch (by specifying wrong type or a mix of types in the Lua table) object_cast will print an error to the console and throw, so no need to check what it returns before emplacing. + /// @param luaTable The Lua table object to convert to vector. + /// @return A C++ vector containing all the objects from the Lua table. Ownership is transferred! + /// @remark In case of type mismatch (by specifying wrong type or a mix of types in the Lua table) object_cast will print an error to the console and throw, so no need to check what it returns before emplacing. template static std::vector ConvertLuaTableToVectorOfType(const luabind::object& luaObject) { std::vector outVector = {}; if (luaObject.is_valid() && luabind::type(luaObject) == LUA_TTABLE) { diff --git a/Source/Lua/LuabindObjectWrapper.cpp b/Source/Lua/LuabindObjectWrapper.cpp index e02a9e9b7f..1e295fec02 100644 --- a/Source/Lua/LuabindObjectWrapper.cpp +++ b/Source/Lua/LuabindObjectWrapper.cpp @@ -16,8 +16,6 @@ namespace RTE { // This is because we may assign an object to another state in a singlethreaded context, before the GC runs in the multithreaded context static std::vector s_QueuedDeletions; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuabindObjectWrapper::ApplyQueuedDeletions() { for (luabind::adl::object* obj: s_QueuedDeletions) { delete obj; @@ -26,8 +24,6 @@ namespace RTE { s_QueuedDeletions.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuabindObjectWrapper::~LuabindObjectWrapper() { if (m_OwnsObject) { static std::mutex mut; @@ -36,8 +32,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - luabind::adl::object GetCopyForStateInternal(const luabind::adl::object& obj, lua_State& targetState) { if (obj.is_valid()) { int type = luabind::type(obj); @@ -68,8 +62,6 @@ namespace RTE { return luabind::adl::object(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuabindObjectWrapper LuabindObjectWrapper::GetCopyForState(lua_State& targetState) const { luabind::adl::object* copy = new luabind::adl::object(GetCopyForStateInternal(*m_LuabindObject, targetState)); return LuabindObjectWrapper(copy, m_FilePath, true); diff --git a/Source/Lua/LuabindObjectWrapper.h b/Source/Lua/LuabindObjectWrapper.h index 50f4861c40..46a956bf89 100644 --- a/Source/Lua/LuabindObjectWrapper.h +++ b/Source/Lua/LuabindObjectWrapper.h @@ -21,21 +21,15 @@ namespace RTE { } #pragma endregion - /// /// A wrapper for luabind objects, to avoid include problems with luabind. - /// class LuabindObjectWrapper { public: #pragma region Creation - /// /// Constructor method used for LuabindObjectWrapper. - /// LuabindObjectWrapper() = default; - /// /// Constructor method used to instantiate a LuabindObjectWrapper object in system memory. - /// explicit LuabindObjectWrapper(luabind::adl::object* luabindObject, const std::string_view& filePath, bool ownsObject = true) : m_LuabindObject(luabindObject), m_FilePath(filePath), m_OwnsObject(ownsObject) {} #pragma endregion @@ -43,28 +37,20 @@ namespace RTE { #pragma region Destruction static void ApplyQueuedDeletions(); - /// /// Destructor method used to clean up a LuabindObjectWrapper object before deletion from system memory. - /// ~LuabindObjectWrapper(); #pragma endregion - /// /// Attempts to copy a luabind object into another state. - /// LuabindObjectWrapper GetCopyForState(lua_State& newState) const; #pragma region Getters - /// /// Gets the LuabindObjectWrapper's luabind object. Ownership is NOT transferred! - /// - /// The LuabindObjectWrapper's luabind object. + /// @return The LuabindObjectWrapper's luabind object. luabind::adl::object* GetLuabindObject() const { return m_LuabindObject; } - /// /// Gets the LuabindObjectWrapper's file path. - /// - /// The LuabindObjectWrapper's file path. + /// @return The LuabindObjectWrapper's file path. const std::string& GetFilePath() const { return m_FilePath; } #pragma endregion diff --git a/Source/Main.cpp b/Source/Main.cpp index 27b9ff540b..0bc536ff5f 100644 --- a/Source/Main.cpp +++ b/Source/Main.cpp @@ -58,8 +58,6 @@ using namespace RTE; namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// /// Initializes all the essential managers. /// @@ -118,8 +116,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// /// Destroys all the managers and frees all loaded data before termination. /// @@ -148,8 +144,6 @@ namespace RTE { #endif } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// /// Command-line argument handling. /// @@ -201,8 +195,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// /// Polls the SDL event queue and passes events to be handled by the relevant managers. /// @@ -243,8 +235,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// /// Game menus loop. /// @@ -282,8 +272,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// /// Game simulation loop. /// @@ -416,8 +404,6 @@ namespace RTE { } } // namespace RTE -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// /// Self-invoking lambda that installs exception handlers before Main is executed. /// @@ -426,8 +412,6 @@ static const bool RTESetExceptionHandlers = []() { return true; }(); -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// /// Implementation of the main function. /// diff --git a/Source/Managers/ActivityMan.cpp b/Source/Managers/ActivityMan.cpp index 38ae2edc62..1a1b6070d5 100644 --- a/Source/Managers/ActivityMan.cpp +++ b/Source/Managers/ActivityMan.cpp @@ -32,8 +32,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::Clear() { m_DefaultActivityType = "GATutorial"; m_DefaultActivityName = "Tutorial Mission"; @@ -51,8 +49,6 @@ namespace RTE { m_LaunchIntoEditor = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::Initialize() { if (g_NetworkServer.IsServerModeEnabled()) { return SetStartMultiplayerServerOverview(); @@ -66,16 +62,12 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::ForceAbortSave() { // Just a utility function we can call in the debugger quickwatch window to force an abort save to occur (great for force-saving the game when it crashes) // Throw ActivityMan::Instance().ForceAbortSave() into a quickwatch window and evaluate :) return SaveCurrentGame("AbortSave"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::SaveCurrentGame(const std::string& fileName) { m_SaveGameTask.wait(); m_SaveGameTask = BS::multi_future(); @@ -168,8 +160,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::LoadAndLaunchGame(const std::string& fileName) { m_SaveGameTask.wait(); @@ -217,15 +207,11 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::SetStartActivity(Activity* newActivity) { RTEAssert(newActivity, "Trying to replace an activity with a null one!"); m_StartActivity.reset(newActivity); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::SetStartTutorialActivity() { SetStartActivity(dynamic_cast(g_PresetMan.GetEntityPreset("GATutorial", "Tutorial Mission")->Clone())); if (GameActivity* gameActivity = dynamic_cast(GetStartActivity())) { @@ -234,8 +220,6 @@ namespace RTE { g_SceneMan.SetSceneToLoad("Tutorial Bunker"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::SetStartEditorActivity(const std::string_view& editorToLaunch) { std::unique_ptr editorActivityToStart = nullptr; @@ -264,8 +248,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::SetStartEditorActivitySetToLaunchInto() { std::array validEditorNames = {"ActorEditor", "GibEditor", "SceneEditor", "AreaEditor", "AssemblyEditor"}; @@ -283,8 +265,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::SetStartMultiplayerActivity() { if (std::unique_ptr multiplayerGame = std::make_unique()) { if (g_MetaMan.GameInProgress()) { @@ -299,8 +279,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::SetStartMultiplayerServerOverview() { g_NetworkServer.Start(); @@ -323,8 +301,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ActivityMan::StartActivity(Activity* activity) { RTEAssert(activity, "Trying to start a null activity!"); @@ -369,8 +345,6 @@ namespace RTE { return error; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ActivityMan::StartActivity(const std::string& className, const std::string& presetName) { if (const Entity* entity = g_PresetMan.GetEntityPreset(className, presetName)) { Activity* newActivity = dynamic_cast(entity->Clone()); @@ -389,8 +363,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::PauseActivity(bool pause, bool skipPauseMenu) { if (!m_Activity) { g_ConsoleMan.PrintString("ERROR: No Activity to pause!"); @@ -424,8 +396,6 @@ namespace RTE { g_ConsoleMan.PrintString("SYSTEM: Activity \"" + m_Activity->GetPresetName() + "\" was " + (pause ? "paused" : "resumed")); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::ResumeActivity() { if (GetActivity()->GetActivityState() != Activity::NotStarted) { m_InActivity = true; @@ -437,8 +407,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ActivityMan::RestartActivity() { m_ActivityNeedsRestart = false; g_ConsoleMan.PrintString("SYSTEM: Activity was reset!"); @@ -471,8 +439,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::EndActivity() const { // TODO: Set the activity pointer to nullptr so it doesn't return junk after being destructed. Do it here, or wherever works without crashing. if (m_Activity) { @@ -486,16 +452,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::LateUpdateGlobalScripts() const { if (GAScripted* scriptedActivity = dynamic_cast(m_Activity.get())) { scriptedActivity->UpdateGlobalScripts(true); } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ActivityMan::Update() { g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::ActivityUpdate); if (m_Activity) { diff --git a/Source/Managers/ActivityMan.h b/Source/Managers/ActivityMan.h index a6aabc3d0c..2351fbad9f 100644 --- a/Source/Managers/ActivityMan.h +++ b/Source/Managers/ActivityMan.h @@ -10,150 +10,104 @@ namespace RTE { - /// /// The singleton manager of the Activities and rules of Cortex Command. - /// class ActivityMan : public Singleton { friend class SettingsMan; public: #pragma region Creation - /// /// Constructor method used to instantiate an ActivityMan object in system memory. Create() should be called before using the object. - /// ActivityMan() { Clear(); } - /// /// Makes the ActivityMan object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. bool Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up an ActivityMan object before deletion from system memory. - /// ~ActivityMan() { Destroy(); } - /// /// Destroys and resets (through Clear()) the ActivityMan object. - /// void Destroy() { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Gets the currently active Activity. Won't be what has been set by SetStartActivity unless RestartActivity has been called since. - /// - /// The currently active Activity. Will be nullptr if no Activity is going. + /// @return The currently active Activity. Will be nullptr if no Activity is going. Activity* GetActivity() const { return m_Activity.get(); } - /// /// Gets the async save game task. - /// - /// The savegame task. + /// @return The savegame task. BS::multi_future& GetSaveGameTask() { return m_SaveGameTask; } - /// /// Indicates whether the game is currently running or not (not editing, over or paused). - /// - /// Whether the game is running or not. + /// @return Whether the game is running or not. bool ActivityRunning() const { return m_Activity && m_Activity->IsRunning(); } - /// /// Indicates whether the game is currently paused or not. - /// - /// Whether the game is paused or not. + /// @return Whether the game is paused or not. bool ActivityPaused() const { return !m_Activity || m_Activity->IsPaused(); } - /// /// Gets whether we are currently in game (as in, not in the main menu or any other out-of-game menus), regardless of its state. - /// - /// Whether we are currently in game, regardless of it's state. + /// @return Whether we are currently in game, regardless of it's state. bool IsInActivity() const { return m_InActivity; } - /// /// Sets whether we are currently in game (as in, not in the main menu or any other out-of-game menus) or not. - /// - /// In game or not. + /// @param isInActivity In game or not. void SetInActivity(bool isInActivity) { m_InActivity = isInActivity; } - /// /// Gets whether the current Activity needs to be restarted. - /// - /// Whether the current Activity needs to be restarted. + /// @return Whether the current Activity needs to be restarted. bool ActivitySetToRestart() const { return m_ActivityNeedsRestart; } - /// /// Sets whether the current Activity needs to be restarted. - /// - /// Restart the Activity or not. + /// @param restartActivity Restart the Activity or not. void SetRestartActivity(bool restartActivity = true) { m_ActivityNeedsRestart = restartActivity; } - /// /// Gets whether the game simulation needs to be started back up after the current Activity was unpaused. - /// - /// Whether the game simulation needs to be started back up after the current Activity was unpaused. + /// @return Whether the game simulation needs to be started back up after the current Activity was unpaused. bool ActivitySetToResume() const { return m_ActivityNeedsResume; } - /// /// Sets the game simulation to be started back up after the current Activity was unpaused. - /// - /// Whether the game simulation is being resumed from the pause menu. + /// @param resumingFromPauseMenu Whether the game simulation is being resumed from the pause menu. void SetResumeActivity(bool resumingFromPauseMenu = false) { m_ActivityNeedsResume = true; m_ResumingActivityFromPauseMenu = resumingFromPauseMenu; } - /// /// Gets whether the pause menu should be skipped when the game simulation is paused. - /// - /// Whether the pause menu should be skipped when the game simulation is paused. + /// @return Whether the pause menu should be skipped when the game simulation is paused. bool SkipPauseMenuWhenPausingActivity() const { return m_SkipPauseMenuWhenPausingActivity; } #pragma endregion #pragma region Default Activity Handling - /// /// Gets the type name of the default Activity to be loaded if nothing else is available. - /// - /// The default Activity type name. + /// @return The default Activity type name. std::string GetDefaultActivityType() const { return m_DefaultActivityType; } - /// /// Sets the type name of the default Activity to be loaded if nothing else is available. - /// - /// The default Activity type name. + /// @param defaultActivityType The default Activity type name. void SetDefaultActivityType(const std::string_view& defaultActivityType) { m_DefaultActivityType = defaultActivityType; } - /// /// Gets the name of the default Activity to be loaded if nothing else is available. - /// - /// The default Activity preset name. + /// @return The default Activity preset name. std::string GetDefaultActivityName() const { return m_DefaultActivityName; } - /// /// Sets the preset name of the default Activity to be loaded if nothing else is available. - /// - /// The default Activity preset name. + /// @param defaultActivityName The default Activity preset name. void SetDefaultActivityName(const std::string_view& defaultActivityName) { m_DefaultActivityName = defaultActivityName; } - /// /// Gets whether the intro and main menu should be skipped on game start and launch directly into the set default Activity instead. - /// - /// Whether the game is set to launch directly into the set default Activity or not. + /// @return Whether the game is set to launch directly into the set default Activity or not. bool IsSetToLaunchIntoActivity() const { return m_LaunchIntoActivity; } - /// /// Gets whether the intro and main menu should be skipped on game start and launch directly into the set editor Activity instead. - /// - /// Whether the game is set to launch directly into the set editor Activity or not. + /// @return Whether the game is set to launch directly into the set editor Activity or not. bool IsSetToLaunchIntoEditor() const { return m_LaunchIntoEditor; } - /// /// Sets the name of the editor to launch directly into. - /// - /// + /// @param editorName void SetEditorToLaunch(const std::string_view& editorName) { if (!editorName.empty()) { m_EditorToLaunch = editorName; @@ -163,119 +117,83 @@ namespace RTE { #pragma endregion #pragma region Saving and Loading - /// /// A utility function we can call in the debugger quickwatch window to force an abort save to occur (great for force-saving the game when it crashes) - /// - /// Whether the game was successfully saved. + /// @return Whether the game was successfully saved. bool ForceAbortSave(); - /// /// Saves the currently running Scene and Activity to a savegame file. Note this only works for GAScripted activities. - /// - /// Path to the file. - /// Whether the game was successfully saved. + /// @param fileName Path to the file. + /// @return Whether the game was successfully saved. bool SaveCurrentGame(const std::string& fileName); - /// /// Loads a saved game, and launches its Scene and Activity. - /// - /// Path to the file. - /// Whether or not the saved game was successfully loaded. + /// @param fileName Path to the file. + /// @return Whether or not the saved game was successfully loaded. bool LoadAndLaunchGame(const std::string& fileName); #pragma endregion #pragma region Activity Start Handling // TODO: Fix crappy naming. None of these actually start anything. Maybe "...ActivityToStart" instead of "...StartActivity". - /// /// Gets the Activity that will be used in the next restart. Ownership is NOT transferred! - /// - /// The Activity to put into effect next time ResetActivity is called. + /// @return The Activity to put into effect next time ResetActivity is called. Activity* GetStartActivity() const { return m_StartActivity.get(); } - /// /// Sets a new Activity to copy for next restart. You have to use RestartActivity to get it going. Ownership IS transferred! - /// - /// The new Activity to put into effect next time ResetActivity is called. + /// @param newActivity The new Activity to put into effect next time ResetActivity is called. void SetStartActivity(Activity* newActivity); - /// /// Loads the "Tutorial Mission" Scene and starts the Tutorial Activity. - /// void SetStartTutorialActivity(); - /// /// Loads "Editor Scene" and starts the given editor Activity. - /// - /// The editor name to put into effect next time ResetActivity is called. + /// @param editorToLaunch The editor name to put into effect next time ResetActivity is called. void SetStartEditorActivity(const std::string_view& editorToLaunch); - /// /// Launch editor Activity specified in command-line argument. - /// - /// Whether a valid editor name was passed in and set to be launched next time ResetActivity is called. + /// @return Whether a valid editor name was passed in and set to be launched next time ResetActivity is called. bool SetStartEditorActivitySetToLaunchInto(); - /// /// Loads "Multiplayer Scene" and starts the MultiplayerGame Activity. - /// - /// Whether the MultiplayerGame Activity was successfully created and set to be launched next time ResetActivity is called. + /// @return Whether the MultiplayerGame Activity was successfully created and set to be launched next time ResetActivity is called. bool SetStartMultiplayerActivity(); - /// /// Launch multiplayer server overview Activity. - /// - /// Whether the server overview Activity was successfully created and set to be launched next time ResetActivity is called. + /// @return Whether the server overview Activity was successfully created and set to be launched next time ResetActivity is called. bool SetStartMultiplayerServerOverview(); #pragma endregion #pragma region Concrete Methods - /// /// Officially starts the Activity passed in. Ownership IS transferred! - /// - /// The new activity to start. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param activity The new activity to start. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal.@return int StartActivity(Activity* activity); - /// /// Officially gets and starts the Activity described. - /// - /// The class name of the Activity to start. - /// The PresetName of the Activity to start. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param className The class name of the Activity to start. + /// @param presetName The PresetName of the Activity to start. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int StartActivity(const std::string& className, const std::string& presetName); - /// /// Pauses/unpauses the game and saving/resuming in-game music if possible, or queuing default music if not. - /// - /// Whether to pause the game or not. - /// Whether the pause menu should be skipped when the game simulation is paused. + /// @param pause Whether to pause the game or not. + /// @param skipPauseMenu Whether the pause menu should be skipped when the game simulation is paused. void PauseActivity(bool pause = true, bool skipPauseMenu = false); - /// /// Start the game simulation back up after the current Activity was unpaused. - /// void ResumeActivity(); - /// /// Completely restarts whatever Activity was last started. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. bool RestartActivity(); - /// /// Forces the current game's end. - /// void EndActivity() const; - /// /// Only updates Global Scripts of the current activity with LateUpdate flag enabled. - /// void LateUpdateGlobalScripts() const; - /// /// Updates the state of this and the current Activity. Supposed to be done every frame before drawing. - /// void Update(); #pragma endregion @@ -301,9 +219,7 @@ namespace RTE { bool m_LaunchIntoEditor; //!< Whether to skip the intro and main menu and launch directly into the set editor Activity instead. std::string_view m_EditorToLaunch; //!< The name of the editor Activity to launch directly into. - /// /// Clears all the member variables of this ActivityMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/AudioMan.cpp b/Source/Managers/AudioMan.cpp index 28b26cf31b..f184cd1c45 100644 --- a/Source/Managers/AudioMan.cpp +++ b/Source/Managers/AudioMan.cpp @@ -12,8 +12,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::Clear() { m_AudioEnabled = false; m_CurrentActivityHumanPlayerPositions.clear(); @@ -47,8 +45,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::Initialize() { FMOD_RESULT audioSystemSetupResult = FMOD::System_Create(&m_AudioSystem); @@ -113,8 +109,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::Destroy() { if (m_AudioEnabled) { StopAll(); @@ -123,8 +117,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::Update() { if (m_AudioEnabled) { @@ -191,8 +183,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::SetGlobalPitch(float pitch, bool includeImmobileSounds, bool includeMusic) { if (!m_AudioEnabled) { return; @@ -209,8 +199,6 @@ namespace RTE { m_SFXChannelGroup->setPitch(m_GlobalPitch); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::SetTempMusicVolume(float volume) { if (m_AudioEnabled && IsMusicPlaying()) { FMOD::Channel* musicChannel; @@ -223,8 +211,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::SetMusicPitch(float pitch) { if (!m_AudioEnabled) { return false; @@ -244,8 +230,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float AudioMan::GetMusicPosition() const { if (m_AudioEnabled && IsMusicPlaying()) { FMOD_RESULT result; @@ -263,8 +247,6 @@ namespace RTE { return 0.0F; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::SetMusicPosition(float position) { if (m_AudioEnabled && IsMusicPlaying()) { FMOD::Channel* musicChannel; @@ -284,8 +266,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::FinishIngameLoopingSounds() { if (m_AudioEnabled) { int numberOfPlayingChannels; @@ -308,8 +288,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::PlayMusic(const char* filePath, int loops, float volumeOverrideIfNotMuted) { if (m_AudioEnabled) { const std::string fullFilePath = g_PresetMan.GetFullModulePath(filePath); @@ -380,8 +358,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::PlayNextStream() { if (m_AudioEnabled && !m_MusicPlayList.empty()) { std::string nextString = m_MusicPlayList.front(); @@ -413,8 +389,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::StopMusic() { if (m_AudioEnabled) { if (m_IsInMultiplayerMode) { @@ -429,8 +403,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::QueueMusicStream(const char* filepath) { if (m_AudioEnabled) { bool isPlaying; @@ -446,8 +418,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SoundContainer* AudioMan::PlaySound(const std::string& filePath, const Vector& position, int player) { if (m_IsInMultiplayerMode) { return nullptr; @@ -462,8 +432,6 @@ namespace RTE { return newSoundContainer; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::GetMusicEvents(int player, std::list& list) { if (player < 0 || player >= c_MaxClients) { return; @@ -478,8 +446,6 @@ namespace RTE { g_SoundEventsListMutex[player].unlock(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::RegisterMusicEvent(int player, NetworkMusicState state, const char* filepath, int loopsOrSilence, float position, float pitch) { if (player == -1) { for (int i = 0; i < c_MaxClients; i++) { @@ -502,8 +468,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::ClearMusicEvents(int player) { if (player == -1 || player >= c_MaxClients) { for (int i = 0; i < c_MaxClients; i++) { @@ -516,8 +480,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::GetSoundEvents(int player, std::list& list) { if (player < 0 || player >= c_MaxClients) { return; @@ -540,8 +502,6 @@ namespace RTE { g_SoundEventsListMutex[player].unlock(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::RegisterSoundEvent(int player, NetworkSoundState state, const SoundContainer* soundContainer, int fadeoutTime) { if (player == -1) { for (int i = 0; i < c_MaxClients; i++) { @@ -590,8 +550,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::ClearSoundEvents(int player) { if (player == -1 || player >= c_MaxClients) { for (int i = 0; i < c_MaxClients; i++) { @@ -604,8 +562,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::PlaySoundContainer(SoundContainer* soundContainer, int player) { if (!m_AudioEnabled || !soundContainer || soundContainer->GetPlayingChannels()->size() >= c_MaxPlayingSoundsPerContainer) { return false; @@ -699,8 +655,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::ChangeSoundContainerPlayingChannelsPosition(const SoundContainer* soundContainer) { if (!m_AudioEnabled || !soundContainer) { return false; @@ -728,8 +682,6 @@ namespace RTE { return result == FMOD_OK; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::ChangeSoundContainerPlayingChannelsVolume(const SoundContainer* soundContainer, float newVolume) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return false; @@ -762,8 +714,6 @@ namespace RTE { return result == FMOD_OK; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::ChangeSoundContainerPlayingChannelsPitch(const SoundContainer* soundContainer) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return false; @@ -786,8 +736,6 @@ namespace RTE { return result == FMOD_OK; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::ChangeSoundContainerPlayingChannelsCustomPanValue(const SoundContainer* soundContainer) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return false; @@ -810,8 +758,6 @@ namespace RTE { return result == FMOD_OK; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AudioMan::StopSoundContainerPlayingChannels(SoundContainer* soundContainer, int player) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return false; @@ -835,8 +781,6 @@ namespace RTE { return result == FMOD_OK; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::FadeOutSoundContainerPlayingChannels(SoundContainer* soundContainer, int fadeOutTime) { if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) { return; @@ -868,8 +812,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::Update3DEffectsForSFXChannels() { int numberOfPlayingChannels; FMOD::Channel* soundChannel; @@ -914,8 +856,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD_RESULT AudioMan::UpdatePositionalEffectsForSoundChannel(FMOD::Channel* soundChannel, const FMOD_VECTOR* positionOverride) const { FMOD_RESULT result = FMOD_OK; @@ -1004,8 +944,6 @@ namespace RTE { return result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD_RESULT F_CALLBACK AudioMan::MusicChannelEndedCallback(FMOD_CHANNELCONTROL* channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void* unusedCommandData1, void* unusedCommandData2) { if (channelControlType == FMOD_CHANNELCONTROL_CHANNEL && callbackType == FMOD_CHANNELCONTROL_CALLBACK_END) { void* userData; @@ -1017,8 +955,6 @@ namespace RTE { return FMOD_OK; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD_RESULT F_CALLBACK AudioMan::SoundChannelEndedCallback(FMOD_CHANNELCONTROL* channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void* unusedCommandData1, void* unusedCommandData2) { if (channelControlType == FMOD_CHANNELCONTROL_CHANNEL && callbackType == FMOD_CHANNELCONTROL_CALLBACK_END) { FMOD::Channel* channel = reinterpret_cast(channelControl); @@ -1050,15 +986,11 @@ namespace RTE { return FMOD_OK; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD_VECTOR AudioMan::GetAsFMODVector(const Vector& vector, float zValue) const { Vector sceneDimensions = g_SceneMan.GetScene() ? g_SceneMan.GetSceneDim() : Vector(); return sceneDimensions.IsZero() ? FMOD_VECTOR{0, 0, zValue} : FMOD_VECTOR{vector.m_X, sceneDimensions.m_Y - vector.m_Y, zValue}; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector AudioMan::GetAsVector(FMOD_VECTOR fmodVector) const { Vector sceneDimensions = g_SceneMan.GetScene() ? g_SceneMan.GetSceneDim() : Vector(); return sceneDimensions.IsZero() ? Vector() : Vector(fmodVector.x, sceneDimensions.m_Y - fmodVector.y); diff --git a/Source/Managers/AudioMan.h b/Source/Managers/AudioMan.h index d112319832..b7daea2502 100644 --- a/Source/Managers/AudioMan.h +++ b/Source/Managers/AudioMan.h @@ -16,26 +16,20 @@ namespace RTE { class SoundContainer; - /// /// The singleton manager of sound effect and music playback. - /// class AudioMan : public Singleton { friend class SettingsMan; friend class SoundContainer; public: - /// /// Hardcoded playback priorities for sounds. Note that sounds don't have to use these specifically; their priority can be anywhere between high and low. - /// enum PlaybackPriority { PRIORITY_HIGH = 0, PRIORITY_NORMAL = 128, PRIORITY_LOW = 256 }; - /// /// Music event states for sending music data from the server to clients during multiplayer games. - /// enum NetworkMusicState { MUSIC_PLAY = 0, MUSIC_STOP, @@ -43,9 +37,7 @@ namespace RTE { MUSIC_SET_PITCH }; - /// /// The data struct used to send music data from the server to clients during multiplayer games. - /// struct NetworkMusicData { unsigned char State; char Path[256]; @@ -54,9 +46,7 @@ namespace RTE { float Pitch; }; - /// /// Sound event states for sending sound data from the server to clients during multiplayer games. - /// enum NetworkSoundState { SOUND_SET_GLOBAL_PITCH = 0, SOUND_PLAY, @@ -69,9 +59,7 @@ namespace RTE { SOUND_FADE_OUT }; - /// /// The data struct used to send sound data from the server to clients during multiplayer games. - /// struct NetworkSoundData { unsigned char State; std::size_t SoundFileHash; @@ -90,84 +78,60 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a AudioMan object in system memory. /// Create() should be called before using the object. - /// AudioMan() { Clear(); } - /// /// Makes the AudioMan object ready for use. - /// - /// Whether the audio system was initialized successfully. If not, no audio will be available. + /// @return Whether the audio system was initialized successfully. If not, no audio will be available. bool Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a AudioMan object before deletion from system memory. - /// ~AudioMan() { Destroy(); } - /// /// Destroys and resets (through Clear()) the AudioMan object. - /// void Destroy(); #pragma endregion #pragma region Concrete Methods - /// /// Updates the state of this AudioMan. Supposed to be done every frame before drawing. - /// void Update(); #pragma endregion #pragma region General Getters and Setters - /// /// Gets the audio management system object used for playing all audio. - /// - /// The audio management system object used by AudioMan for playing audio. + /// @return The audio management system object used by AudioMan for playing audio. FMOD::System* GetAudioSystem() const { return m_AudioSystem; } - /// /// Reports whether audio is enabled. - /// - /// Whether audio is enabled. + /// @return Whether audio is enabled. bool IsAudioEnabled() const { return m_AudioEnabled; } - /// /// Gets the virtual and real playing channel counts, filling in the passed-in out-parameters. - /// - /// The out-parameter that will hold the virtual channel count. - /// The out-parameter that will hold the real channel count. - /// Whether or not the playing channel count was succesfully gotten. + /// @param outVirtualChannelCount The out-parameter that will hold the virtual channel count. + /// @param outRealChannelCount The out-parameter that will hold the real channel count. + /// @return Whether or not the playing channel count was succesfully gotten. bool GetPlayingChannelCount(int* outVirtualChannelCount, int* outRealChannelCount) const { return m_AudioSystem->getChannelsPlaying(outVirtualChannelCount, outRealChannelCount) == FMOD_OK; } - /// /// Returns the total number of virtual audio channels available. - /// - /// The number of virtual audio channels available. + /// @return The number of virtual audio channels available. int GetTotalVirtualChannelCount() const { return c_MaxVirtualChannels; } - /// /// Returns the total number of real audio channels available. - /// - /// The number of real audio channels available. + /// @return The number of real audio channels available. int GetTotalRealChannelCount() const { int channelCount; return m_AudioSystem->getSoftwareChannels(&channelCount) == FMOD_OK ? channelCount : 0; } - /// /// Gets whether all audio is muted or not. - /// - /// Whether all the audio is muted or not. + /// @return Whether all the audio is muted or not. bool GetMasterMuted() const { return m_MuteMaster; } - /// /// Mutes or unmutes all audio. - /// - /// Whether to mute or unmute all the audio. + /// @param muteOrUnmute Whether to mute or unmute all the audio. void SetMasterMuted(bool muteOrUnmute = true) { m_MuteMaster = muteOrUnmute; if (m_AudioEnabled) { @@ -175,16 +139,12 @@ namespace RTE { } } - /// /// Gets the volume of all audio. Does not get music or sounds individual volumes. - /// - /// Current volume scalar value. 0.0-1.0. + /// @return Current volume scalar value. 0.0-1.0. float GetMasterVolume() const { return m_MasterVolume; } - /// /// Sets all the audio to a specific volume. Does not affect music or sounds individual volumes. - /// - /// The desired volume scalar. 0.0-1.0. + /// @param volume The desired volume scalar. 0.0-1.0. void SetMasterVolume(float volume = 1.0F) { m_MasterVolume = std::clamp(volume, 0.0F, 1.0F); if (m_AudioEnabled) { @@ -192,47 +152,35 @@ namespace RTE { } } - /// /// Gets the global pitch scalar value for all sounds and music. - /// - /// The current pitch scalar. Will be > 0. + /// @return The current pitch scalar. Will be > 0. float GetGlobalPitch() const { return m_GlobalPitch; } - /// /// Sets the global pitch multiplier for mobile sounds, optionally setting it for immobile sounds and music. - /// - /// New global pitch, limited to 8 octaves up or down (i.e. 0.125 - 8). Defaults to 1. - /// Whether to include immobile sounds (normally used for GUI and so on) in global pitch modification. Defaults to false. - /// Whether to include the music in global pitch modification. Defaults to false. + /// @param pitch New global pitch, limited to 8 octaves up or down (i.e. 0.125 - 8). Defaults to 1. + /// @param includeImmobileSounds Whether to include immobile sounds (normally used for GUI and so on) in global pitch modification. Defaults to false. + /// @param includeMusic Whether to include the music in global pitch modification. Defaults to false. void SetGlobalPitch(float pitch = 1.0F, bool includeImmobileSounds = false, bool includeMusic = false); - /// /// The strength of the sound panning effect. - /// - /// 0 - 1, where 0 is no panning and 1 is fully panned. + /// @return 0 - 1, where 0 is no panning and 1 is fully panned. float GetSoundPanningEffectStrength() const { return m_SoundPanningEffectStrength; } #pragma endregion #pragma region Music Getters and Setters - /// /// Reports whether any music stream is currently playing. - /// - /// Whether any music stream is currently playing. + /// @return Whether any music stream is currently playing. bool IsMusicPlaying() const { bool isPlayingMusic; return m_AudioEnabled && m_MusicChannelGroup->isPlaying(&isPlayingMusic) == FMOD_OK ? isPlayingMusic : false; } - /// /// Gets whether the music channel is muted or not. - /// - /// Whether the music channel is muted or not. + /// @return Whether the music channel is muted or not. bool GetMusicMuted() const { return m_MuteMusic; } - /// /// Mutes or unmutes the music channel. - /// - /// Whether to mute or unmute the music channel. + /// @param muteOrUnmute Whether to mute or unmute the music channel. void SetMusicMuted(bool muteOrUnmute = true) { m_MuteMusic = muteOrUnmute; if (m_AudioEnabled) { @@ -240,16 +188,12 @@ namespace RTE { } } - /// /// Gets the volume of music. Does not get volume of sounds. - /// - /// Current volume scalar value. 0.0-1.0. + /// @return Current volume scalar value. 0.0-1.0. float GetMusicVolume() const { return m_MusicVolume; } - /// /// Sets the music to a specific volume. Does not affect sounds. - /// - /// The desired volume scalar. 0.0-1.0. + /// @param volume The desired volume scalar. 0.0-1.0. void SetMusicVolume(float volume = 1.0F) { m_MusicVolume = std::clamp(volume, 0.0F, 1.0F); if (m_AudioEnabled) { @@ -257,49 +201,35 @@ namespace RTE { } } - /// /// Sets the music to a specific volume, but it will only last until a new song is played. Useful for fading etc. - /// - /// The desired volume scalar. 0.0-1.0. + /// @param volume The desired volume scalar. 0.0-1.0. void SetTempMusicVolume(float volume = 1.0F); - /// /// Gets the path of the last played music stream. - /// - /// The file path of the last played music stream. + /// @return The file path of the last played music stream. std::string GetMusicPath() const { return m_MusicPath; } - /// /// Sets/updates the frequency/pitch for the music channel. - /// - /// New pitch, a multiplier of the original normal frequency. Keep it > 0. - /// Whether the music channel's pitch was successfully updated. + /// @param pitch New pitch, a multiplier of the original normal frequency. Keep it > 0. + /// @return Whether the music channel's pitch was successfully updated. bool SetMusicPitch(float pitch); - /// /// Gets the position of playback of the current music stream, in seconds. - /// - /// The current position of the current stream playing, in seconds. + /// @return The current position of the current stream playing, in seconds. float GetMusicPosition() const; - /// /// Sets the music to a specific position in the song. - /// - /// The desired position from the start, in seconds. + /// @param position The desired position from the start, in seconds. void SetMusicPosition(float position); #pragma endregion #pragma region Overall Sound Getters and Setters - /// /// Gets whether all the sound effects channels are muted or not. - /// - /// Whether all the sound effects channels are muted or not. + /// @return Whether all the sound effects channels are muted or not. bool GetSoundsMuted() const { return m_MuteSounds; } - /// /// Mutes or unmutes all the sound effects channels. - /// - /// Whether to mute or unmute all the sound effects channels. + /// @param muteOrUnmute Whether to mute or unmute all the sound effects channels. void SetSoundsMuted(bool muteOrUnmute = true) { m_MuteSounds = muteOrUnmute; if (m_AudioEnabled) { @@ -309,16 +239,12 @@ namespace RTE { } } - /// /// Gets the volume of all sounds. Does not get volume of music. - /// - /// Current volume scalar value. 0.0-1.0. + /// @return Current volume scalar value. 0.0-1.0. float GetSoundsVolume() const { return m_SoundsVolume; } - /// /// Sets the volume of all sounds to a specific volume. Does not affect music. - /// - /// The desired volume scalar. 0.0-1.0. + /// @param volume The desired volume scalar. 0.0-1.0. void SetSoundsVolume(float volume = 1.0F) { m_SoundsVolume = volume; if (m_AudioEnabled) { @@ -329,9 +255,7 @@ namespace RTE { #pragma endregion #pragma region Global Playback and Handling - /// /// Stops all playback and clears the music playlist. - /// void StopAll() { if (m_AudioEnabled) { m_MasterChannelGroup->stop(); @@ -339,15 +263,11 @@ namespace RTE { m_MusicPlayList.clear(); } - /// /// Makes all sounds that are looping stop looping, allowing them to play once more then be finished. - /// void FinishIngameLoopingSounds(); - /// /// Pauses all ingame sounds. - /// Whether to pause sounds or resume them. - /// + /// @param pause Whether to pause sounds or resume them. void PauseIngameSounds(bool pause = true) { if (m_AudioEnabled) { m_SFXChannelGroup->setPaused(pause); @@ -356,129 +276,95 @@ namespace RTE { #pragma endregion #pragma region Music Playback and Handling - /// /// Starts playing a certain WAVE, MOD, MIDI, OGG, MP3 file in the music channel. - /// - /// The path to the music file to play. - /// The number of times to loop the song. 0 means play once. -1 means play infinitely until stopped. - /// The volume override for music for this song only, if volume is not muted. < 0 means no override. + /// @param filePath The path to the music file to play. + /// @param loops The number of times to loop the song. 0 means play once. -1 means play infinitely until stopped. + /// @param volumeOverrideIfNotMuted The volume override for music for this song only, if volume is not muted. < 0 means no override. void PlayMusic(const char* filePath, int loops = -1, float volumeOverrideIfNotMuted = -1.0F); - /// /// Plays the next music stream in the queue, if any is queued. - /// void PlayNextStream(); - /// /// Stops playing a the music channel. - /// void StopMusic(); - /// /// Queues up another path to a stream that will be played after the current one is done. /// Can be done several times to queue up many tracks. The last track in the queue will be looped infinitely. - /// - /// The path to the music file to play after the current one. + /// @param filePath The path to the music file to play after the current one. void QueueMusicStream(const char* filePath); - /// /// Queues up a period of silence in the music stream playlist. - /// - /// The number of secs to wait before going to the next stream. + /// @param seconds The number of secs to wait before going to the next stream. void QueueSilence(int seconds) { if (m_AudioEnabled && seconds > 0) { m_MusicPlayList.push_back("@" + std::to_string(seconds)); } } - /// /// Clears the music queue. - /// void ClearMusicQueue() { m_MusicPlayList.clear(); } #pragma endregion #pragma region Lua Sound File Playing - /// /// Starts playing a certain sound file. - /// - /// The path to the sound file to play. - /// The new SoundContainer being played. OWNERSHIP IS TRANSFERRED! + /// @param filePath The path to the sound file to play. + /// @return The new SoundContainer being played. OWNERSHIP IS TRANSFERRED! SoundContainer* PlaySound(const std::string& filePath) { return PlaySound(filePath, Vector(), -1); } - /// /// Starts playing a certain sound file at a certain position for all players. - /// - /// The path to the sound file to play. - /// The new SoundContainer being played. OWNERSHIP IS TRANSFERRED! + /// @param filePath The path to the sound file to play. + /// @return The new SoundContainer being played. OWNERSHIP IS TRANSFERRED! SoundContainer* PlaySound(const std::string& filePath, const Vector& position) { return PlaySound(filePath, position, -1); } - /// /// Starts playing a certain sound file at a certain position for a certain player. - /// - /// The path to the sound file to play. - /// The position at which to play the SoundContainer's sounds. - /// Which player to play the SoundContainer's sounds for, -1 means all players. - /// The new SoundContainer being played. OWNERSHIP IS TRANSFERRED! + /// @param filePath The path to the sound file to play. + /// @param position The position at which to play the SoundContainer's sounds. + /// @param player Which player to play the SoundContainer's sounds for, -1 means all players. + /// @return The new SoundContainer being played. OWNERSHIP IS TRANSFERRED! SoundContainer* PlaySound(const std::string& filePath, const Vector& position, int player); #pragma endregion #pragma region Network Audio Handling - /// /// Returns true if manager is in multiplayer mode. - /// - /// True if in multiplayer mode. + /// @return True if in multiplayer mode. bool IsInMultiplayerMode() const { return m_IsInMultiplayerMode; } - /// /// Sets the multiplayer mode flag. - /// - /// Whether this manager should operate in multiplayer mode. + /// @param value Whether this manager should operate in multiplayer mode. void SetMultiplayerMode(bool value) { m_IsInMultiplayerMode = value; } - /// /// Fills the list with music events happened for the specified network player. - /// - /// Player to get events for. - /// List with events for this player. + /// @param player Player to get events for. + /// @param list List with events for this player. void GetMusicEvents(int player, std::list& list); - /// /// Adds the music event to internal list of music events for the specified player. - /// - /// Player(s) for which the event happened. - /// NetworkMusicState for the event. - /// Music file path to transmit to client. - /// LoopsOrSilence counter or, if state is silence, the length of the silence. - /// Music playback position. - /// Pitch value. + /// @param player Player(s) for which the event happened. + /// @param state NetworkMusicState for the event. + /// @param filepath Music file path to transmit to client. + /// @param loops LoopsOrSilence counter or, if state is silence, the length of the silence. + /// @param position Music playback position. + /// @param pitch Pitch value. void RegisterMusicEvent(int player, NetworkMusicState state, const char* filepath, int loopsOrSilence = 0, float position = 0, float pitch = 1.0F); - /// /// Clears the list of current Music events for the target player. - /// - /// Player to clear music events for. -1 clears for all players + /// @param player Player to clear music events for. -1 clears for all players void ClearMusicEvents(int player); - /// /// Fills the list with sound events happened for the specified network player. - /// - /// Player to get events for. - /// List with events for this player. + /// @param player Player to get events for. + /// @param list List with events for this player. void GetSoundEvents(int player, std::list& list); - /// /// Adds the sound event to the internal list of sound events for the specified player. - /// - /// Player(s) for which the event happened. - /// NetworkSoundState for the event. - /// A pointer to the SoundContainer this event is happening to, or a null pointer for global events. - /// THe amount of time, in MS, to fade out over. This data isn't contained in SoundContainer, so it needs to be passed in separately. + /// @param player Player(s) for which the event happened. + /// @param state NetworkSoundState for the event. + /// @param soundContainer A pointer to the SoundContainer this event is happening to, or a null pointer for global events. + /// @param fadeOutTime THe amount of time, in MS, to fade out over. This data isn't contained in SoundContainer, so it needs to be passed in separately. void RegisterSoundEvent(int player, NetworkSoundState state, const SoundContainer* soundContainer, int fadeOutTime = 0); - /// /// Clears the list of current Sound events for the target player. - /// - /// Player to clear sound events for. -1 clears for all players. + /// @param player Player to clear sound events for. -1 clears for all players. void ClearSoundEvents(int player); #pragma endregion @@ -525,105 +411,77 @@ namespace RTE { private: #pragma region Sound Container Actions and Modifications - /// /// Starts playing the next SoundSet of the given SoundContainer for the give player. - /// - /// Pointer to the SoundContainer to start playing. Ownership is NOT transferred! - /// Which player to play the SoundContainer's sounds for, -1 means all players. Defaults to -1. - /// Whether or not playback of the Sound was successful. + /// @param soundContainer Pointer to the SoundContainer to start playing. Ownership is NOT transferred! + /// @param player Which player to play the SoundContainer's sounds for, -1 means all players. Defaults to -1. + /// @return Whether or not playback of the Sound was successful. bool PlaySoundContainer(SoundContainer* soundContainer, int player = -1); - /// /// Sets/updates the position of a SoundContainer's playing sounds. - /// - /// A pointer to a SoundContainer object. Ownership IS NOT transferred! - /// Whether the position was successfully set. + /// @param soundContainer A pointer to a SoundContainer object. Ownership IS NOT transferred! + /// @return Whether the position was successfully set. bool ChangeSoundContainerPlayingChannelsPosition(const SoundContainer* soundContainer); - /// /// Changes the volume of a SoundContainer's playing sounds. - /// - /// A pointer to a SoundContainer object. Ownership IS NOT transferred! - /// The new volume to play sounds at, between 0 and 1. - /// Whether the volume was successfully updated. + /// @param soundContainer A pointer to a SoundContainer object. Ownership IS NOT transferred! + /// @param newVolume The new volume to play sounds at, between 0 and 1. + /// @return Whether the volume was successfully updated. bool ChangeSoundContainerPlayingChannelsVolume(const SoundContainer* soundContainer, float newVolume); - /// /// Changes the frequency/pitch of a SoundContainer's playing sounds. - /// - /// A pointer to a SoundContainer object. Ownership IS NOT transferred! - /// Whether the pitch was successfully updated. + /// @param soundContainer A pointer to a SoundContainer object. Ownership IS NOT transferred! + /// @return Whether the pitch was successfully updated. bool ChangeSoundContainerPlayingChannelsPitch(const SoundContainer* soundContainer); - /// /// Updates the custom pan value of a SoundContainer's playing sounds. - /// - /// A pointer to a SoundContainer object. Ownership IS NOT transferred! - /// Whether the custom pan value was successfully updated. + /// @param soundContainer A pointer to a SoundContainer object. Ownership IS NOT transferred! + /// @return Whether the custom pan value was successfully updated. bool ChangeSoundContainerPlayingChannelsCustomPanValue(const SoundContainer* soundContainer); - /// /// Stops playing a SoundContainer's playing sounds for a certain player. - /// - /// A pointer to a SoundContainer object6. Ownership is NOT transferred! - /// Which player to stop playing the SoundContainer for. - /// + /// @param soundContainer A pointer to a SoundContainer object6. Ownership is NOT transferred! + /// @param player Which player to stop playing the SoundContainer for. + /// @return bool StopSoundContainerPlayingChannels(SoundContainer* soundContainer, int player); - /// /// Fades out playback a SoundContainer. - /// - /// A pointer to a SoundContainer object. Ownership is NOT transferred! - /// The amount of time, in ms, to fade out over. + /// @param soundContainer A pointer to a SoundContainer object. Ownership is NOT transferred! + /// @param fadeOutTime The amount of time, in ms, to fade out over. void FadeOutSoundContainerPlayingChannels(SoundContainer* soundContainer, int fadeOutTime); #pragma endregion #pragma region 3D Effect Handling - /// /// Updates 3D effects calculations for all sound channels whose SoundContainers isn't immobile. - /// void Update3DEffectsForSFXChannels(); - /// /// Sets or updates the position of the given sound channel so it handles scene wrapping correctly. Also handles volume attenuation and minimum audible distance. - /// - /// The channel whose position should be set or updated. - /// An optional position to set for this sound channel. Done this way to save setting and resetting data in FMOD. - /// Whether the channel's position was succesfully set. + /// @param soundChannel The channel whose position should be set or updated. + /// @param positionToUse An optional position to set for this sound channel. Done this way to save setting and resetting data in FMOD. + /// @return Whether the channel's position was succesfully set. FMOD_RESULT UpdatePositionalEffectsForSoundChannel(FMOD::Channel* soundChannel, const FMOD_VECTOR* positionToUse = nullptr) const; #pragma endregion #pragma region FMOD Callbacks - /// /// A static callback function for FMOD to invoke when the music channel finishes playing. See fmod docs - FMOD_CHANNELCONTROL_CALLBACK for details - /// static FMOD_RESULT F_CALLBACK MusicChannelEndedCallback(FMOD_CHANNELCONTROL* channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void* commandData1, void* commandData2); - /// /// A static callback function for FMOD to invoke when a sound channel finished playing. See fmod docs - FMOD_CHANNELCONTROL_CALLBACK for details - /// static FMOD_RESULT F_CALLBACK SoundChannelEndedCallback(FMOD_CHANNELCONTROL* channelControl, FMOD_CHANNELCONTROL_TYPE channelControlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void* commandData1, void* commandData2); #pragma endregion #pragma region Utility Methods - /// /// Gets the corresponding FMOD_VECTOR for a given RTE Vector. - /// - /// The RTE Vector to get as an FMOD_VECTOR. - /// The FMOD_VECTOR that corresponds to the given RTE Vector. + /// @param vector The RTE Vector to get as an FMOD_VECTOR. + /// @return The FMOD_VECTOR that corresponds to the given RTE Vector. FMOD_VECTOR GetAsFMODVector(const Vector& vector, float zValue = 0) const; - /// /// Gets the corresponding RTE Vector for a given FMOD_VECTOR. - /// - /// The FMOD_VECTOR to get as an RTE Vector. - /// The RTE Vector that corresponds to the given FMOD_VECTOR. + /// @param fmodVector The FMOD_VECTOR to get as an RTE Vector. + /// @return The RTE Vector that corresponds to the given FMOD_VECTOR. Vector GetAsVector(FMOD_VECTOR fmodVector) const; #pragma endregion - /// /// Clears all the member variables of this AudioMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/CameraMan.cpp b/Source/Managers/CameraMan.cpp index 20a26865e4..6d13aeb193 100644 --- a/Source/Managers/CameraMan.cpp +++ b/Source/Managers/CameraMan.cpp @@ -10,8 +10,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::Clear() { m_ScreenShakeStrength = 1.0F; m_ScreenShakeDecay = 50.0F; @@ -36,23 +34,17 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::SetOffset(const Vector& offset, int screenId) { m_Screens[screenId].Offset = offset.GetFloored(); CheckOffset(screenId); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector CameraMan::GetUnwrappedOffset(int screenId) const { const Screen& screen = m_Screens[screenId]; const SLTerrain* terrain = g_SceneMan.GetScene()->GetTerrain(); return Vector(screen.Offset.GetX() + static_cast(terrain->GetBitmap()->w * screen.SeamCrossCount[Axes::X]), screen.Offset.GetY() + static_cast(terrain->GetBitmap()->h * screen.SeamCrossCount[Axes::Y])); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::SetScroll(const Vector& center, int screenId) { Screen& screen = m_Screens[screenId]; if (g_FrameMan.IsInMultiplayerMode()) { @@ -63,14 +55,10 @@ namespace RTE { CheckOffset(screenId); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector CameraMan::GetScrollTarget(int screenId) const { return g_NetworkClient.IsConnectedAndRegistered() ? g_NetworkClient.GetFrameTarget() : m_Screens[screenId].ScrollTarget; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::SetScrollTarget(const Vector& targetCenter, float speed, int screenId) { Screen& screen = m_Screens[screenId]; @@ -87,8 +75,6 @@ namespace RTE { screen.TargetYWrapped = screen.TargetYWrapped || targetYWrapped; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float CameraMan::TargetDistanceScalar(const Vector& point) const { if (!g_SceneMan.GetScene()) { return 0.0F; @@ -123,8 +109,6 @@ namespace RTE { return closestScalar; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::CheckOffset(int screenId) { RTEAssert(g_SceneMan.GetScene(), "Trying to check offset before there is a scene or terrain!"); @@ -153,8 +137,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector CameraMan::GetFrameSize(int screenId) { int frameWidth = g_WindowMan.GetResX(); int frameHeight = g_WindowMan.GetResY(); @@ -170,8 +152,6 @@ namespace RTE { return Vector(static_cast(frameWidth), static_cast(frameHeight)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::ResetAllScreenShake() { for (int screenId = 0; screenId < g_FrameMan.GetScreenCount(); ++screenId) { Screen& screen = m_Screens[screenId]; @@ -180,8 +160,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::AddScreenShake(float magnitude, const Vector& position) { for (int screenId = 0; screenId < g_FrameMan.GetScreenCount(); ++screenId) { Screen& screen = m_Screens[screenId]; @@ -209,8 +187,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CameraMan::Update(int screenId) { Screen& screen = m_Screens[screenId]; const SLTerrain* terrain = g_SceneMan.GetScene()->GetTerrain(); diff --git a/Source/Managers/CameraMan.h b/Source/Managers/CameraMan.h index adec7208b9..bf19b8abdb 100644 --- a/Source/Managers/CameraMan.h +++ b/Source/Managers/CameraMan.h @@ -9,242 +9,173 @@ namespace RTE { - /// /// The singleton manager of the camera for each player. - /// class CameraMan : public Singleton { friend class SettingsMan; public: #pragma region Creation - /// /// Constructor method used to instantiate a CameraMan object in system memory. Create() should be called before using the object. - /// CameraMan() { Clear(); } - /// /// Makes the CameraMan object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Initialize() const { return 0; } #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a CameraMan object before deletion from system memory. - /// ~CameraMan() { Destroy(); } - /// /// Resets the entire CameraMan to their default settings or values. - /// void Reset() { Clear(); } - /// /// Destroys and resets (through Clear()) the CameraMan object. - /// void Destroy() { Clear(); } #pragma endregion #pragma region Screen Handling - /// /// Gets the offset (scroll position) of the terrain. - /// - /// Which screen you want to get the offset of. - /// The offset for the given screen. + /// @param screenId Which screen you want to get the offset of. + /// @return The offset for the given screen. Vector GetOffset(int screenId = 0) const { return m_Screens[screenId].Offset; } - /// /// Sets the offset (scroll position) of the terrain. - /// - /// The new offset value. - /// Which screen you want to set the offset of. + /// @param offset The new offset value. + /// @param screenId Which screen you want to set the offset of. void SetOffset(const Vector& offset, int screenId = 0); - /// /// Gets the difference in current offset and that of the Update() before. - /// - /// The delta offset in pixels. + /// @return The delta offset in pixels. Vector GetDeltaOffset(int screenId = 0) const { return m_Screens[screenId].DeltaOffset; } - /// /// Gets the offset (scroll position) of the terrain, without taking wrapping into account. - /// - /// Which screen you want to get the offset of. - /// The offset for the given screen. + /// @param screenId Which screen you want to get the offset of. + /// @return The offset for the given screen. Vector GetUnwrappedOffset(int screenId = 0) const; - /// /// Sets the offset (scroll position) of the terrain to center on specific world coordinates. /// If the coordinate to center on is close to the terrain border edges, the view will not scroll outside the borders. - /// - /// The coordinates to center the terrain scroll on. - /// Which screen you want to set the offset of. + /// @param center The coordinates to center the terrain scroll on. + /// @param screenId Which screen you want to set the offset of. void SetScroll(const Vector& center, int screenId = 0); - /// /// Gets the team associated with a specific screen. - /// - /// Which screen you want to get the team of. - /// The team associated with the screen. + /// @param screenId Which screen you want to get the team of. + /// @return The team associated with the screen. int GetScreenTeam(int screenId = 0) const { return m_Screens[screenId].ScreenTeam; } - /// /// Sets the team associated with a specific screen. - /// - /// The team to set the screen to. - /// Which screen you want to set the team of. + /// @param team The team to set the screen to. + /// @param screenId Which screen you want to set the team of. void SetScreenTeam(int team, int screenId = 0) { m_Screens[screenId].ScreenTeam = team; } - /// /// Gets the amount that a specific screen is occluded by a GUI panel or something of the sort. /// This will affect how the scroll target translates into the offset of the screen, in order to keep the target centered on the screen. - /// - /// Which screen you want to get the team of. - /// A vector indicating the screen occlusion amount. + /// @param screenId Which screen you want to get the team of. + /// @return A vector indicating the screen occlusion amount. Vector& GetScreenOcclusion(int screenId = 0) { return m_Screens[screenId].ScreenOcclusion; } - /// /// Sets the amount that a specific screen is occluded by a GUI panel or something of the sort. /// This will affect how the scroll target translates into the offset of the screen, in order to keep the target centered on the screen. - /// - /// The amount of occlusion of the screen. - /// Which screen you want to set the occlusion of. + /// @param occlusion The amount of occlusion of the screen. + /// @param screenId Which screen you want to set the occlusion of. void SetScreenOcclusion(const Vector& occlusion, int screenId = 0) { m_Screens[screenId].ScreenOcclusion = occlusion; } - /// /// Gets the currently set scroll target, i.e. where the center of the specific screen is trying to line up with. - /// - /// Which screen to get the target for. - /// Current target vector in Scene coordinates. + /// @param screenId Which screen to get the target for. + /// @return Current target vector in Scene coordinates. Vector GetScrollTarget(int screenId = 0) const; - /// /// Interpolates a smooth scroll of the view from wherever it is now, towards centering on a new scroll target over time. - /// - /// The new target vector in Scene coordinates. - /// The normalized speed at screen the view scrolls. 0 being no movement, and 1.0 being instant movement to the target in one frame. - /// Which screen you want to set the scroll offset of. + /// @param targetCenter The new target vector in Scene coordinates. + /// @param speed The normalized speed at screen the view scrolls. 0 being no movement, and 1.0 being instant movement to the target in one frame. + /// @param screenId Which screen you want to set the scroll offset of. void SetScrollTarget(const Vector& targetCenter, float speed = 0.1F, int screenId = 0); - /// /// Calculates a scalar of how distant a certain point in the world is from the currently closest scroll target of all active screens. - /// - /// The world coordinate point to check distance to/from. - /// + /// @param point The world coordinate point to check distance to/from. + /// @return /// A normalized scalar representing the distance between the closest scroll target of all active screens, to the passed in point. /// 0 means it's the point is within half a screen's width of the target, and 1.0 means it's on the clear opposite side of the scene. - /// float TargetDistanceScalar(const Vector& point) const; - /// /// Makes sure the current offset won't create a view of outside the scene. /// If that is found to be the case, the offset is corrected so that the view rectangle /// is as close to the old offset as possible, but still entirely within the scene world. - /// - /// Which screen you want to check the offset of. + /// @param screenId Which screen you want to check the offset of. void CheckOffset(int screenId = 0); - /// /// Gets the frame width/height for a given screen. - /// - /// Which screen you want to get frame width/height of. - /// The frame width (x) and height (y). + /// @param screenId Which screen you want to get frame width/height of. + /// @return The frame width (x) and height (y). Vector GetFrameSize(int screenId = 0); #pragma endregion #pragma region Screen Shake Getters and Setters - /// /// Gets the screen shake strength multiplier. - /// - /// The screen shake strength multiplier. + /// @return The screen shake strength multiplier. float GetScreenShakeStrength() const { return m_ScreenShakeStrength; } - /// /// Sets the screen shake strength multiplier. - /// - /// New value for the screen shake strength multiplier. + /// @param newValue New value for the screen shake strength multiplier. void SetScreenShakeStrength(float newValue) { m_ScreenShakeStrength = newValue; } - /// /// Gets how quickly screen shake decays, per second. - /// - /// The screen shake decay. + /// @return The screen shake decay. float GetScreenShakeDecay() const { return m_ScreenShakeDecay; } - /// /// Gets the maximum amount of screen shake time, i.e. the number of seconds screen shake will happen until ScreenShakeDecay reduces it to zero. - /// - /// The maximum screen shake time, in seconds. + /// @return The maximum screen shake time, in seconds. float GetMaxScreenShakeTime() const { return m_MaxScreenShakeTime; } - /// /// Gets how much the screen should shake per unit of energy from gibbing (i.e explosions), when screen shake amount is auto-calculated. - /// - /// The default shakiness per unit of gib energy. + /// @return The default shakiness per unit of gib energy. float GetDefaultShakePerUnitOfGibEnergy() const { return m_DefaultShakePerUnitOfGibEnergy; } - /// /// Gets how much the screen should shake per unit of energy for recoil, when screen shake amount is auto-calculated. - /// - /// The default shakiness per unit of recoil energy. + /// @return The default shakiness per unit of recoil energy. float GetDefaultShakePerUnitOfRecoilEnergy() const { return m_DefaultShakePerUnitOfRecoilEnergy; } - /// /// The maximum amount of screen shake recoil can cause, when screen shake is auto-calculated. This is ignored by per-firearm shake settings. - /// - /// The maximum auto-calculated recoil shakiness. + /// @return The maximum auto-calculated recoil shakiness. float GetDefaultShakeFromRecoilMaximum() const { return m_DefaultShakeFromRecoilMaximum; } #pragma endregion #pragma region Screen Shake Actions - /// /// Resets all screen shake and the screen scroll timers that affect it. - /// void ResetAllScreenShake(); - /// /// Increases the magnitude of screen shake. /// This is used for spatially located screen-shake, and will automatically determine which screens have shake applied /// If the screen-shake position is outside our view, it'll gradually weaken and fade away depending on distance. - /// - /// The amount of screen shake. - /// The spatial location of the screen-shake event. + /// @param magnitude The amount of screen shake. + /// @param position The spatial location of the screen-shake event. void AddScreenShake(float magnitude, const Vector& position); - /// /// Increases the magnitude of screen shake. - /// - /// The amount of screen shake to add. - /// Which screen you want to add screen-shake to. + /// @param magnitude The amount of screen shake to add. + /// @param screenId Which screen you want to add screen-shake to. void AddScreenShake(float magnitude, int screenId = 0) { m_Screens[screenId].ScreenShakeMagnitude += magnitude; } - /// /// Sets the magnitude of screen shake. - /// - /// The amount of screen shake. - /// Which screen you want to set screen-shake for. + /// @param magnitude The amount of screen shake. + /// @param screenId Which screen you want to set screen-shake for. void SetScreenShake(float magnitude, int screenId = 0) { m_Screens[screenId].ScreenShakeMagnitude = magnitude; } - /// /// Applies screen shake to be at least magnitude. - /// - /// The amount of screen shake. - /// Which screen you want to set screen-shake for. + /// @param magnitude The amount of screen shake. + /// @param screenId Which screen you want to set screen-shake for. void ApplyScreenShake(float magnitude, int screenId = 0) { m_Screens[screenId].ScreenShakeMagnitude = std::max(magnitude, m_Screens[screenId].ScreenShakeMagnitude); } #pragma endregion #pragma region Concrete Methods - /// /// Updates the state of this CameraMan. Supposed to be done every frame before drawing. - /// void Update(int screenId); #pragma endregion private: - /// /// A screen. Each player should have one of these. - /// /// TODO: This is a struct right now, as it has been torn verbatim out of SceneMan. In future it should be a proper class with methods, instead of CameraMan handling everything. struct Screen { int ScreenTeam = 0; //!< The team associated with this Screen. @@ -275,9 +206,7 @@ namespace RTE { std::array m_Screens; //!< Array with the Screen of each player. - /// /// Clears all the member variables of this CameraMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/ConsoleMan.cpp b/Source/Managers/ConsoleMan.cpp index f59812ceea..14191cd821 100644 --- a/Source/Managers/ConsoleMan.cpp +++ b/Source/Managers/ConsoleMan.cpp @@ -16,8 +16,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::Clear() { m_ConsoleState = ConsoleState::Disabled; m_ReadOnly = false; @@ -38,8 +36,6 @@ namespace RTE { m_ConsoleUseMonospaceFont = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ConsoleMan::Initialize() { if (!m_GUIScreen) { m_GUIScreen = new AllegroScreen(g_FrameMan.GetBackBuffer32()); @@ -81,8 +77,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::Destroy() { if (!g_WindowMan.ResolutionChanged()) { SaveAllText("LogConsole.txt"); @@ -102,8 +96,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::SetEnabled(bool enable) { if (enable && m_ConsoleState != ConsoleState::Enabled && m_ConsoleState != ConsoleState::Enabling) { m_ConsoleState = ConsoleState::Enabling; @@ -114,8 +106,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::SetReadOnly() { if (!m_ReadOnly) { // Save the current input string before changing to the read-only notice so we can restore it when switching back @@ -126,8 +116,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::SetConsoleScreenSize(float screenRatio) { m_ConsoleScreenRatio = Limit(screenRatio, 1.0F, 0.1F); @@ -142,8 +130,6 @@ namespace RTE { m_InputTextBox->Resize(m_ParentBox->GetWidth() - 3, m_InputTextBox->GetHeight()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::SetConsoleUseMonospaceFont(bool useFont) { m_ConsoleUseMonospaceFont = useFont; if (m_GUIControlManager) { @@ -151,8 +137,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::AddLoadWarningLogExtensionMismatchEntry(const std::string& pathToLog, const std::string& readerPosition, const std::string& altFileExtension) { const std::string pathAndAccessLocation = "\"" + pathToLog + "\" referenced " + readerPosition + ". "; std::string newEntry = pathAndAccessLocation + (!altFileExtension.empty() ? "Found and loaded a file with \"" + altFileExtension + "\" extension." : "The file was not loaded."); @@ -166,8 +150,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::SaveLoadWarningLog(const std::string& filePath) { Writer logWriter(filePath.c_str()); if (logWriter.WriterOK()) { @@ -180,8 +162,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::SaveInputLog(const std::string& filePath) { Writer logWriter(filePath.c_str()); if (logWriter.WriterOK()) { @@ -197,8 +177,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ConsoleMan::SaveAllText(const std::string& filePath) { Writer logWriter(filePath.c_str()); if (logWriter.WriterOK()) { @@ -212,16 +190,12 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::ClearLog() { m_InputLog.clear(); m_InputLogPosition = m_InputLog.begin(); m_OutputLog.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::PrintString(const std::string& stringToPrint) { static std::mutex printStringMutex; std::scoped_lock printStringLock(printStringMutex); @@ -232,8 +206,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::ShowShortcuts() { if (!IsEnabled()) { SetEnabled(); @@ -265,8 +237,6 @@ namespace RTE { "F12 - Make a single screenshot"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::Update() { if (g_UInputMan.FlagCtrlState() && g_UInputMan.KeyPressed(SDL_SCANCODE_GRAVE)) { SetReadOnly(); @@ -339,8 +309,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::ConsoleOpenClose() { float travelCompletionDistance; @@ -375,8 +343,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::FeedString(bool feedEmptyString) { char strLine[1024]; std::stringstream inputStream(m_InputTextBox->GetText()); @@ -409,8 +375,6 @@ namespace RTE { m_InputTextBox->SetText(""); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::LoadLoggedInput(bool nextEntry) { if (nextEntry) { // See if we should decrement doubly because the last move was in the opposite direction @@ -446,8 +410,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::RemoveGraveAccents() const { std::string textBoxString = m_InputTextBox->GetText(); if (std::find(textBoxString.begin(), textBoxString.end(), '`') != textBoxString.end()) { @@ -457,8 +419,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ConsoleMan::Draw(BITMAP* targetBitmap) const { if (m_ConsoleState != ConsoleState::Disabled) { AllegroScreen drawScreen(targetBitmap); diff --git a/Source/Managers/ConsoleMan.h b/Source/Managers/ConsoleMan.h index a943c73728..ef552f4993 100644 --- a/Source/Managers/ConsoleMan.h +++ b/Source/Managers/ConsoleMan.h @@ -14,150 +14,104 @@ namespace RTE { class GUITextBox; class GUILabel; - /// /// The singleton manager of the lua console. - /// class ConsoleMan : public Singleton { friend class SettingsMan; public: #pragma region Creation - /// /// Constructor method used to instantiate a ConsoleMan object in system memory. Create() should be called before using the object. - /// ConsoleMan() { Clear(); } - /// /// Makes the ConsoleMan object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a ConsoleMan object before deletion from system memory. - /// ~ConsoleMan() { Destroy(); } - /// /// Destroys and resets (through Clear()) the ConsoleMan object. - /// void Destroy(); #pragma endregion #pragma region Getters and Setters - /// /// Reports whether the console is enabled or not. - /// - /// Whether the console is enabled or not. + /// @return Whether the console is enabled or not. bool IsEnabled() const { return m_ConsoleState == ConsoleState::Enabled || m_ConsoleState == ConsoleState::Enabling; } - /// /// Enables or disables the console. - /// - /// Whether to enable or disable the console. + /// @param enable Whether to enable or disable the console. void SetEnabled(bool enable = true); - /// /// Reports whether the console is in read-only mode or not. - /// - /// Whether the console is in read-only mode or not. + /// @return Whether the console is in read-only mode or not. bool IsReadOnly() const { return m_ReadOnly; } - /// /// Gets how much of the screen that the console is covering when opened. - /// - /// The ratio of the screen that is covered. + /// @return The ratio of the screen that is covered. float GetConsoleScreenSize() const { return m_ConsoleScreenRatio; } - /// /// Sets how much of the screen that the console should cover when opened. - /// - /// The ratio of the screen to cover. 0 - 1.0. + /// @param screenRatio The ratio of the screen to cover. 0 - 1.0. void SetConsoleScreenSize(float screenRatio = 0.3F); - /// /// Gets whether the console text is using the monospace font or the regular proportional one. - /// - /// Whether the console text is using the monospace font or the regular proportional one. + /// @return Whether the console text is using the monospace font or the regular proportional one. bool GetConsoleUseMonospaceFont() const { return m_ConsoleUseMonospaceFont; } - /// /// Sets whether the console text is using the monospace font and changes to the appropriate skin to apply the setting. - /// - /// Whether to use the monospace font or not. + /// @param useFont Whether to use the monospace font or not. void SetConsoleUseMonospaceFont(bool useFont); #pragma endregion #pragma region Logging - /// /// Gets whether the loading warning log has any warnings logged or not. - /// - /// Whether the log has logged warnings. + /// @return Whether the log has logged warnings. bool LoadWarningsExist() const { return !m_LoadWarningLog.empty(); } - /// /// Adds a new file extension mismatch entry to the loading warning log. - /// - /// The path that produced the warning. - /// The file and line currently being loaded. - /// The alternative file extension to the path that produced the warning (e.g. if file is ".bmp", alternative extension is ".png"). + /// @param pathToLog The path that produced the warning. + /// @param readerPosition The file and line currently being loaded. + /// @param altFileExtension The alternative file extension to the path that produced the warning (e.g. if file is ".bmp", alternative extension is ".png"). void AddLoadWarningLogExtensionMismatchEntry(const std::string& pathToLog, const std::string& readerPosition = "", const std::string& altFileExtension = ""); - /// /// Writes the entire loading warning log to a file. - /// - /// The filename of the file to write to. + /// @param filePath The filename of the file to write to. void SaveLoadWarningLog(const std::string& filePath); - /// /// Writes all the input strings to a log in the order they were entered. - /// - /// The filename of the file to write to. + /// @param filePath The filename of the file to write to. void SaveInputLog(const std::string& filePath); - /// /// Writes the entire console buffer to a file. - /// - /// The filename of the file to write to. - /// Whether writing to the file was successful. + /// @param filePath The filename of the file to write to. + /// @return Whether writing to the file was successful. bool SaveAllText(const std::string& filePath); - /// /// Clears all previous input. - /// void ClearLog(); #pragma endregion #pragma region Concrete Methods - /// /// Prints a string into the console. - /// - /// The string to print. + /// @param stringToPrint The string to print. void PrintString(const std::string& stringToPrint); - /// /// Opens the console and prints the shortcut help text. - /// void ShowShortcuts(); - /// /// Updates the state of this ConsoleMan. Supposed to be done every frame before drawing. - /// void Update(); - /// /// Draws this ConsoleMan's current graphical representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. + /// @param targetBitmap A pointer to a BITMAP to draw on. void Draw(BITMAP* targetBitmap) const; #pragma endregion protected: - /// /// Enumeration for console states when enabling/disabling the console. NOTE: This can't be lower down because m_ConsoleState relies on this definition. - /// enum ConsoleState { Enabling = 0, Enabled, @@ -189,39 +143,27 @@ namespace RTE { private: bool m_ConsoleUseMonospaceFont; //!< Whether the console text is using the monospace font. - /// /// Sets the console to read-only mode and enables it. - /// void SetReadOnly(); #pragma region Update Breakdown - /// /// Console open/close animation handling and GUI element enabling/disabling. This is called from Update(). - /// void ConsoleOpenClose(); - /// /// Executes the string currently in the console textbox or multiple strings if a newline character is found. /// The input string is saved to the input log if it's different from the previous string. This is called from Update(). - /// - /// Whether to just pass in an empty string to make a new line. + /// @param feedEmptyString Whether to just pass in an empty string to make a new line. void FeedString(bool feedEmptyString = false); - /// /// Loads a previously entered console string from the input log when pressing up or down. This is called from Update(). - /// - /// Whether to load the next entry in the log (true) or the previous (false). + /// @param nextEntry Whether to load the next entry in the log (true) or the previous (false). void LoadLoggedInput(bool nextEntry); - /// /// Removes any grave accents (`) that are pasted or typed into the textbox by opening/closing it. This is called from Update(). - /// void RemoveGraveAccents() const; #pragma endregion - /// /// Clears all the member variables of this ConsoleMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/FrameMan.cpp b/Source/Managers/FrameMan.cpp index bbd65d7ad7..983f7d913a 100644 --- a/Source/Managers/FrameMan.cpp +++ b/Source/Managers/FrameMan.cpp @@ -43,8 +43,6 @@ namespace RTE { nullptr // Transparency does not rely on the blender setting, it creates a map with the dedicated function instead of with the generic one. }; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::Clear() { m_HSplit = false; m_VSplit = false; @@ -92,8 +90,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::Initialize() { set_color_depth(c_BPP); // Sets the allowed color conversions when loading bitmaps from files @@ -120,8 +116,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::CreateBackBuffers() { int resX = g_WindowMan.GetResX(); int resY = g_WindowMan.GetResY(); @@ -173,8 +167,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::CreatePresetColorTables() { // Create RGB lookup table that supposedly speeds up calculation of other color tables. create_rgb_table(&m_RGBTable, m_DefaultPalette, nullptr); @@ -193,8 +185,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::Destroy() { for (const GUIScreen* guiScreen: m_GUIScreens) { delete guiScreen; @@ -208,8 +198,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::Update() { // Remove all scheduled primitives, those will be re-added by updates from other entities. // This needs to happen here, otherwise if there are multiple sim updates during a single frame duplicates will be added to the primitive queue. @@ -244,8 +232,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::ResetSplitScreens(bool hSplit, bool vSplit) { if (m_PlayerScreen) { release_bitmap(m_PlayerScreen.get()); @@ -278,8 +264,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector FrameMan::GetMiddleOfPlayerScreen(int whichPlayer) { Vector middleOfPlayerScreen; @@ -299,8 +283,6 @@ namespace RTE { return middleOfPlayerScreen; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::GetPlayerFrameBufferWidth(int whichPlayer) const { if (IsInMultiplayerMode()) { if (whichPlayer < 0 || whichPlayer >= c_MaxScreenCount) { @@ -320,8 +302,6 @@ namespace RTE { return m_PlayerScreenWidth; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::GetPlayerFrameBufferHeight(int whichPlayer) const { if (IsInMultiplayerMode()) { if (whichPlayer < 0 || whichPlayer >= c_MaxScreenCount) { @@ -341,14 +321,10 @@ namespace RTE { return m_PlayerScreenHeight; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::CalculateTextHeight(const std::string& text, int maxWidth, bool isSmall) { return isSmall ? GetSmallFont()->CalculateHeight(text, maxWidth) : GetLargeFont()->CalculateHeight(text, maxWidth); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string FrameMan::SplitStringToFitWidth(const std::string& stringToSplit, int widthLimit, bool useSmallFont) { GUIFont* fontToUse = GetFont(useSmallFont, false); auto SplitSingleLineAsNeeded = [this, &widthLimit, &fontToUse](std::string& lineToSplitAsNeeded) { @@ -388,14 +364,10 @@ namespace RTE { return splitString; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::CalculateTextWidth(const std::string& text, bool isSmall) { return isSmall ? GetSmallFont()->CalculateWidth(text) : GetLargeFont()->CalculateWidth(text); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::SetScreenText(const std::string& message, int whichScreen, int blinkInterval, int displayDuration, bool centered) { // See if we can overwrite the previous message if (whichScreen >= 0 && whichScreen < c_MaxScreenCount && m_TextDurationTimer[whichScreen].IsPastRealMS(m_TextDuration[whichScreen])) { @@ -407,8 +379,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::ClearScreenText(int whichScreen) { if (whichScreen >= 0 && whichScreen < c_MaxScreenCount) { m_ScreenText[whichScreen].clear(); @@ -418,8 +388,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::SetColorTable(DrawBlendMode blendMode, std::array colorChannelBlendAmounts) { RTEAssert(blendMode > DrawBlendMode::NoBlend && blendMode < DrawBlendMode::BlendModeCount, "Invalid DrawBlendMode or DrawBlendMode::NoBlend passed into FrameMan::SetColorTable. See DrawBlendMode enumeration for defined values."); @@ -470,8 +438,6 @@ namespace RTE { m_ColorTables[blendMode].at(colorChannelBlendAmounts).second = usedPresetTransparencyTable ? -1 : (g_TimerMan.GetAbsoluteTime() / 10000); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::SetTransTableFromPreset(TransparencyPreset transPreset) { RTEAssert(transPreset == TransparencyPreset::LessTrans || transPreset == TransparencyPreset::HalfTrans || transPreset == TransparencyPreset::MoreTrans, "Undefined transparency preset value passed in. See TransparencyPreset enumeration for defined values."); std::array colorChannelBlendAmounts = {transPreset, transPreset, transPreset, BlendAmountLimits::MinBlend}; @@ -481,8 +447,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::CreateNewNetworkPlayerBackBuffer(int player, int width, int height) { for (int f = 0; f < 2; f++) { m_NetworkBackBufferIntermediate8[f][player] = std::unique_ptr(create_bitmap_ex(8, width, height)); @@ -494,8 +458,6 @@ namespace RTE { m_PlayerScreenHeight = height; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool FrameMan::LoadPalette(const std::string& palettePath) { const std::string fullPalettePath = g_PresetMan.GetFullModulePath(palettePath); BITMAP* tempBitmap = load_bitmap(fullPalettePath.c_str(), m_Palette); @@ -512,8 +474,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::SaveBitmap(SaveBitmapMode modeToSave, const std::string& nameBase, BITMAP* bitmapToSave) { if ((modeToSave == WorldDump || modeToSave == ScenePreviewDump) && !g_ActivityMan.ActivityRunning()) { return 0; @@ -622,8 +582,6 @@ namespace RTE { glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, m_ScreenDumpBuffer->line[0]); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::SaveIndexedPNG(const char* fileName, BITMAP* bitmapToSave) const { // nullptr for the PALETTE parameter here because the bitmap is 32bpp and whatever we index it with will end up wrong anyway. save_png(fileName, bitmapToSave, nullptr); @@ -646,8 +604,6 @@ namespace RTE { return saveResult; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int FrameMan::SharedDrawLine(BITMAP* bitmap, const Vector& start, const Vector& end, int color, int altColor, int skip, int skipStart, bool shortestWrap, bool drawDot, BITMAP* dot) const { RTEAssert(bitmap, "Trying to draw line to null Bitmap"); if (drawDot) { @@ -749,8 +705,6 @@ namespace RTE { return skipped; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIFont* FrameMan::GetFont(bool isSmall, bool trueColor) { size_t colorIndex = trueColor ? 1 : 0; @@ -786,8 +740,6 @@ namespace RTE { return m_LargeFonts[colorIndex]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::UpdateScreenOffsetForSplitScreen(int playerScreen, Vector& screenOffset) const { switch (playerScreen) { case Players::PlayerTwo: @@ -814,8 +766,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::Draw() { ZoneScopedN("Draw"); @@ -967,8 +917,6 @@ namespace RTE { #endif } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::DrawScreenText(int playerScreen, AllegroBitmap playerGUIBitmap) { int textPosY = 0; // Only draw screen text to actual human players @@ -1017,8 +965,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::DrawScreenFlash(int playerScreen, BITMAP* playerGUIBitmap) { if (m_FlashScreenColor[playerScreen] != -1) { // If set to flash for a period of time, first be solid and then start flashing slower @@ -1038,8 +984,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::DrawWorldDump(bool drawForScenePreview) const { float worldBitmapWidth = static_cast(m_WorldDumpBuffer->w); float worldBitmapHeight = static_cast(m_WorldDumpBuffer->h); @@ -1095,8 +1039,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void FrameMan::PrepareFrameForNetwork() { int dx = 0; int dy = 0; diff --git a/Source/Managers/FrameMan.h b/Source/Managers/FrameMan.h index 757469a9a1..f77d942b19 100644 --- a/Source/Managers/FrameMan.h +++ b/Source/Managers/FrameMan.h @@ -18,9 +18,7 @@ namespace RTE { void operator()(BITMAP* bitmap) const; }; - /// /// The singleton manager over the composition of frames. - /// class FrameMan : public Singleton { friend class SettingsMan; friend class WindowMan; @@ -31,463 +29,337 @@ namespace RTE { Vector SLOffset[c_MaxScreenCount][c_MaxLayersStoredForNetwork]; //!< SceneLayer offsets for each screen in online multiplayer. #pragma region Creation - /// /// Constructor method used to instantiate a FrameMan object in system memory. Initialize() should be called before using the object. - /// FrameMan() { Clear(); } - /// /// Makes the FrameMan object ready for use, which is to be used with SettingsMan first. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a FrameMan object before deletion from system memory. - /// ~FrameMan() { Destroy(); } - /// /// Destroys and resets (through Clear()) the FrameMan object. - /// void Destroy(); #pragma endregion #pragma region Concrete Methods - /// /// Updates the state of this FrameMan. Supposed to be done every frame. - /// void Update(); - /// /// Draws the current frame to the screen. - /// void Draw(); #pragma endregion #pragma region Getters - /// /// Gets the 8bpp backbuffer bitmap. - /// - /// A pointer to the BITMAP 8bpp backbuffer. OWNERSHIP IS NOT TRANSFERRED! + /// @return A pointer to the BITMAP 8bpp backbuffer. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetBackBuffer8() const { return m_BackBuffer8.get(); } - /// /// Gets the 32bpp backbuffer bitmap. Make sure you don't do any blending stuff to the 8bpp one! - /// - /// A pointer to the BITMAP 32bpp backbuffer. OWNERSHIP IS NOT TRANSFERRED! + /// @return A pointer to the BITMAP 32bpp backbuffer. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetBackBuffer32() const { return m_BackBuffer32.get(); } - /// /// Gets the 32bpp bitmap that is used for overlaying the screen. - /// - /// A pointer to the overlay BITMAP. OWNERSHIP IS NOT TRANSFERRED! + /// @return A pointer to the overlay BITMAP. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetOverlayBitmap32() const { return m_OverlayBitmap32.get(); } #pragma endregion #pragma region Split-Screen Handling - /// /// Gets whether the screen is split horizontally across the screen, ie as two splitscreens one above the other. - /// - /// Whether or not screen has a horizontal split. + /// @return Whether or not screen has a horizontal split. bool GetHSplit() const { return m_HSplit; } - /// /// Sets whether the screen is split horizontally across the screen, ie as two splitscreens one above the other. - /// - /// Whether or not to have a horizontal split. + /// @param hSplit Whether or not to have a horizontal split. void SetHSplit(bool hSplit) { m_HSplit = hSplit; } - /// /// Gets whether the screen is split vertically across the screen, ie as two splitscreens side by side. - /// - /// Whether screen has a vertical split. + /// @return Whether screen has a vertical split. bool GetVSplit() const { return m_VSplit; } - /// /// Sets whether the screen is split vertically across the screen, ie as two splitscreens side by side. - /// - /// Whether or not to have a vertical split. + /// @param vSplit Whether or not to have a vertical split. void SetVSplit(bool vSplit) { m_VSplit = vSplit; } - /// /// Gets whether the screen is being split vertically when in two player splitscreen, or is default split horizontally. - /// - /// Whether the screen is being split vertically when in two player splitscreen or not. + /// @return Whether the screen is being split vertically when in two player splitscreen or not. bool GetTwoPlayerVSplit() const { return m_TwoPlayerVSplit; } - /// /// Sets whether the screen should be split vertically when in two player splitscreen or default to the horizontal split. - /// - /// Whether the screen should be split vertically when in two player splitscreen or default to the horizontal split. + /// @param vSplit Whether the screen should be split vertically when in two player splitscreen or default to the horizontal split. void SetTwoPlayerVSplit(bool vSplit) { m_TwoPlayerVSplit = vSplit; } - /// /// Sets new values for the split screen configuration. - /// - /// Whether the new setting should be horizontally split (over and under). - /// Whether the new setting should be vertically split (side by side). + /// @param hSplit Whether the new setting should be horizontally split (over and under). + /// @param vSplit Whether the new setting should be vertically split (side by side). void ResetSplitScreens(bool hSplit = false, bool vSplit = false); - /// /// Gets the number of currently active screens, counting all splits. - /// - /// The number of currently active screens. + /// @return The number of currently active screens. int GetScreenCount() const { return m_HSplit || m_VSplit ? (m_HSplit && m_VSplit ? 4 : 2) : 1; } - /// /// Gets the width of the individual player screens. This will only be less than the backbuffer resolution if there are split screens. - /// - /// The width of the player screens. + /// @return The width of the player screens. int GetPlayerScreenWidth() const { return GetPlayerFrameBufferWidth(-1); } - /// /// Gets the height of the individual player screens. This will only be less than the backbuffer resolution if there are split screens. - /// - /// The height of the player screens. + /// @return The height of the player screens. int GetPlayerScreenHeight() const { return GetPlayerFrameBufferHeight(-1); } - /// /// Gets a Vector with the absolute position of the middle of the specified player's screen. - /// - /// Player to get the middle of the screen for. -1 will give the middle of the entire game window. - /// The middle of the screen for the specified player, or the middle of the game window if that player is -1. + /// @param whichPlayer Player to get the middle of the screen for. -1 will give the middle of the entire game window. + /// @return The middle of the screen for the specified player, or the middle of the game window if that player is -1. Vector GetMiddleOfPlayerScreen(int whichPlayer); - /// /// Gets the width of the specified player screen. This will only be less than the backbuffer resolution if there are split screens. - /// - /// Player to get screen width for, only used by multiplayer parts. - /// The width of the specified player screen. + /// @param whichPlayer Player to get screen width for, only used by multiplayer parts. + /// @return The width of the specified player screen. int GetPlayerFrameBufferWidth(int whichPlayer) const; - /// /// Gets the height of the specified player screen. This will only be less than the backbuffer resolution if there are split screens. - /// - /// Player to get screen width for, only used by multiplayer parts. - /// The height of the specified player screen. + /// @param whichPlayer Player to get screen width for, only used by multiplayer parts. + /// @return The height of the specified player screen. int GetPlayerFrameBufferHeight(int whichPlayer) const; #pragma endregion #pragma region Text Handling - /// /// Gets the small font from the GUI engine's current skin. Ownership is NOT transferred! - /// - /// Whether to get the 32bpp color version of the font. - /// A pointer to the requested font, or 0 if no small font was found. + /// @param trueColor Whether to get the 32bpp color version of the font. + /// @return A pointer to the requested font, or 0 if no small font was found. GUIFont* GetSmallFont(bool trueColor = false) { return GetFont(true, trueColor); } - /// /// Gets the large font from the GUI engine's current skin. Ownership is NOT transferred! - /// - /// Whether to get the 32bpp color version of the font. - /// A pointer to the requested font, or 0 if no large font was found. + /// @param trueColor Whether to get the 32bpp color version of the font. + /// @return A pointer to the requested font, or 0 if no large font was found. GUIFont* GetLargeFont(bool trueColor = false) { return GetFont(false, trueColor); } - /// /// Calculates the width of a text string using the given font size. - /// - /// Text string. - /// Whether to use small or large font. - /// Width of the text string. + /// @param text Text string. + /// @param isSmall Whether to use small or large font. + /// @return Width of the text string. int CalculateTextWidth(const std::string& text, bool isSmall); - /// /// Calculates the height of a text string using the given font size. - /// - /// Text string. - /// Maximum width of the text string. - /// Whether to use small or large font. - /// Height of the text string. + /// @param text Text string. + /// @param maxWidth Maximum width of the text string. + /// @param isSmall Whether to use small or large font. + /// @return Height of the text string. int CalculateTextHeight(const std::string& text, int maxWidth, bool isSmall); - /// /// Gets a copy of the passed in string, split into multiple lines as needed to fit within the specified width limit, based on the font to use. - /// - /// The string to get a split copy of. - /// The maximum width each line of the string can be. - /// The font the string will use for calculating the string's width. - /// A copy of the passed in string, split into multiple lines as needed. + /// @param stringToSplit The string to get a split copy of. + /// @param widthLimit The maximum width each line of the string can be. + /// @param fontToUse The font the string will use for calculating the string's width. + /// @return A copy of the passed in string, split into multiple lines as needed. std::string SplitStringToFitWidth(const std::string& stringToSplit, int widthLimit, bool useSmallFont); - /// /// Gets the message to be displayed on top of each player's screen. - /// - /// Which player screen to get message from. - /// Current message shown to player. + /// @param whichScreen Which player screen to get message from. + /// @return Current message shown to player. std::string GetScreenText(int whichScreen = 0) const { return (whichScreen >= 0 && whichScreen < c_MaxScreenCount) ? m_ScreenText[whichScreen] : ""; } - /// /// Sets the message to be displayed on top of each player's screen - /// - /// An std::string that specifies what should be displayed. - /// Which screen you want to set text to. - /// The interval with which the screen will be blinking, in ms. 0 means no blinking. - /// The duration, in MS to force this message to display. No other message can be displayed before this expires. ClearScreenText overrides it though. - /// Vertically centered on the screen. + /// @param message An std::string that specifies what should be displayed. + /// @param whichScreen Which screen you want to set text to. + /// @param blinkInterval The interval with which the screen will be blinking, in ms. 0 means no blinking. + /// @param displayDuration The duration, in MS to force this message to display. No other message can be displayed before this expires. ClearScreenText overrides it though. + /// @param centered Vertically centered on the screen. void SetScreenText(const std::string& message, int whichScreen = 0, int blinkInterval = 0, int displayDuration = -1, bool centered = false); - /// /// Clears the message to be displayed on top of each player's screen. - /// - /// Which screen message to clear. + /// @param whichScreen Which screen message to clear. void ClearScreenText(int whichScreen = 0); #pragma endregion #pragma region Drawing - /// /// Clears the 8bpp backbuffer with black. - /// void ClearBackBuffer8() { clear_to_color(m_BackBuffer8.get(), m_BlackColor); } - /// /// Clears the 32bpp backbuffer with black. - /// void ClearBackBuffer32() { clear_to_color(m_BackBuffer32.get(), 0); } - /// /// Sets a specific color table which is used for any subsequent blended drawing in indexed color modes. - /// - /// The blending mode that will be used in drawing. - /// The color channel blend amounts that will be used to select or create the correct table in the specified blending mode. + /// @param blendMode The blending mode that will be used in drawing. + /// @param colorChannelBlendAmounts The color channel blend amounts that will be used to select or create the correct table in the specified blending mode. void SetColorTable(DrawBlendMode blendMode, std::array colorChannelBlendAmounts); - /// /// Sets a specific pre-calculated transparency table which is used for any subsequent transparency drawing in indexed color modes. - /// - /// The transparency preset value. See the TransparencyPreset enumeration for values. + /// @param transValue The transparency preset value. See the TransparencyPreset enumeration for values. void SetTransTableFromPreset(TransparencyPreset transValue); - /// /// Flashes any of the players' screen with the specified color for this frame. - /// - /// Which screen to flash. - /// What color to flash it. -1 means no color or flash. - /// How long a period to fill the frame with color. If 0, a single-frame flash will happen. + /// @param screen Which screen to flash. + /// @param color What color to flash it. -1 means no color or flash. + /// @param periodMS How long a period to fill the frame with color. If 0, a single-frame flash will happen. void FlashScreen(int screen, int color, float periodMS = 0) { m_FlashScreenColor[screen] = color; m_FlashTimer[screen].SetRealTimeLimitMS(periodMS); m_FlashTimer[screen].Reset(); } - /// /// Draws a line that can be dotted or with other effects. - /// - /// The Bitmap to draw to. Ownership is NOT transferred. - /// The absolute Start point. - /// The absolute end point. - /// The color value of the line. - /// A color to alternate with. Every other pixel drawn will have this if !0. - /// How many pixels to skip drawing between drawn ones. 0 means solid line 2 means there's a gap of two pixels between each drawn one. - /// The start of the skipping phase. If skip is 10 and this is 5, the first dot will be drawn after 5 pixels. - /// Whether the line should take the shortest possible route across scene wraps. - /// The end state of the skipping phase. Eg if 4 is returned here the last dot was placed 4 pixels ago. + /// @param bitmap The Bitmap to draw to. Ownership is NOT transferred. + /// @param start The absolute Start point. + /// @param end The absolute end point. + /// @param color The color value of the line. + /// @param altColor A color to alternate with. Every other pixel drawn will have this if !0. + /// @param skip How many pixels to skip drawing between drawn ones. 0 means solid line 2 means there's a gap of two pixels between each drawn one. + /// @param skipStart The start of the skipping phase. If skip is 10 and this is 5, the first dot will be drawn after 5 pixels. + /// @param shortestWrap Whether the line should take the shortest possible route across scene wraps. + /// @return The end state of the skipping phase. Eg if 4 is returned here the last dot was placed 4 pixels ago. int DrawLine(BITMAP* bitmap, const Vector& start, const Vector& end, int color, int altColor = 0, int skip = 0, int skipStart = 0, bool shortestWrap = false) const { return SharedDrawLine(bitmap, start, end, color, altColor, skip, skipStart, shortestWrap, false, nullptr); } - /// /// Draws a line that can be dotted with bitmaps. - /// - /// The Bitmap to draw to. Ownership is NOT transferred. - /// The absolute Start point. - /// The absolute end point. - /// The bitmap to be used for dots (will be centered). - /// How many pixels to gap between drawing dots. Should be more than 0. - /// The start of the skipping phase. If skip is 10 and this is 5, the first dot will be drawn after 5 pixels. - /// Whether the line should take the shortest possible route across scene wraps. - /// The end state of the skipping phase. Eg if 4 is returned here the last dot was placed 4 pixels ago. + /// @param bitmap The Bitmap to draw to. Ownership is NOT transferred. + /// @param start The absolute Start point. + /// @param end The absolute end point. + /// @param dot The bitmap to be used for dots (will be centered). + /// @param skip How many pixels to gap between drawing dots. Should be more than 0. + /// @param skipStart The start of the skipping phase. If skip is 10 and this is 5, the first dot will be drawn after 5 pixels. + /// @param shortestWrap Whether the line should take the shortest possible route across scene wraps. + /// @return The end state of the skipping phase. Eg if 4 is returned here the last dot was placed 4 pixels ago. int DrawDotLine(BITMAP* bitmap, const Vector& start, const Vector& end, BITMAP* dot, int skip = 0, int skipStart = 0, bool shortestWrap = false) const { return SharedDrawLine(bitmap, start, end, 0, 0, skip, skipStart, shortestWrap, true, dot); } #pragma endregion - /// /// Gets whether or not the HUD is disabled for a given screen. - /// - /// The screen to check for. - /// True if in given screen's HUD is disabled. + /// @param screenId The screen to check for. + /// @return True if in given screen's HUD is disabled. bool IsHudDisabled(int screenId = 0) const { return m_HUDDisabled[screenId]; } - /// /// Sets whether or not the HUD is disabled for a given screen. - /// - /// Whether the HUD should be disabled. - /// The screen to set for. + /// @param value Whether the HUD should be disabled. + /// @param screenId The screen to set for. void SetHudDisabled(bool value, int screenId = 0) { m_HUDDisabled[screenId] = value; } #pragma region Network Handling - /// /// Returns true if this manager is in multiplayer mode, storing the 8bpp backbuffer for network transmission. - /// - /// True if in multiplayer mode. + /// @return True if in multiplayer mode. bool IsInMultiplayerMode() const { return m_StoreNetworkBackBuffer; } - /// /// Sets the multiplayer mode flag, telling the manager to store the 8bpp backbuffer for network transmission. - /// - /// Whether this manager should operate in multiplayer mode. + /// @param value Whether this manager should operate in multiplayer mode. void SetMultiplayerMode(bool value) { m_StoreNetworkBackBuffer = value; } - /// /// Gets the ready 8bpp backbuffer bitmap used to draw network transmitted image on top of everything. - /// - /// Which player screen to get backbuffer bitmap for. - /// A pointer to the 8bpp backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player screen to get backbuffer bitmap for. + /// @return A pointer to the 8bpp backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetNetworkBackBuffer8Ready(int player) const { return m_NetworkBackBufferFinal8[m_NetworkFrameReady][player].get(); } - /// /// Gets the ready 8bpp backbuffer GUI bitmap used to draw network transmitted image on top of everything. - /// - /// Which player screen to get GUI backbuffer bitmap for. - /// A pointer to the 8bpp GUI backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player screen to get GUI backbuffer bitmap for. + /// @return A pointer to the 8bpp GUI backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetNetworkBackBufferGUI8Ready(int player) const { return m_NetworkBackBufferFinalGUI8[m_NetworkFrameReady][player].get(); } - /// /// Gets the current 8bpp backbuffer bitmap used to draw network transmitted image on top of everything. - /// - /// Which player screen to get backbuffer bitmap for. - /// A pointer to the 8bpp backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player screen to get backbuffer bitmap for. + /// @return A pointer to the 8bpp backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetNetworkBackBuffer8Current(int player) const { return m_NetworkBackBufferFinal8[m_NetworkFrameCurrent][player].get(); } - /// /// Gets the current 8bpp backbuffer GUI bitmap used to draw network transmitted image on top of everything. - /// - /// Which player screen to get backbuffer bitmap for. - /// A pointer to the 8bpp GUI backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player screen to get backbuffer bitmap for. + /// @return A pointer to the 8bpp GUI backbuffer BITMAP. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetNetworkBackBufferGUI8Current(int player) const { return m_NetworkBackBufferFinalGUI8[m_NetworkFrameCurrent][player].get(); } - /// /// Gets the ready 8bpp intermediate backbuffer bitmap used to copy network transmitted image to before sending. - /// - /// Which player screen to get intermediate bitmap for. - /// A pointer to the 8bpp intermediate BITMAP. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player screen to get intermediate bitmap for. + /// @return A pointer to the 8bpp intermediate BITMAP. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetNetworkBackBufferIntermediate8Ready(int player) const { return m_NetworkBackBufferIntermediate8[m_NetworkFrameReady][player].get(); } - /// /// Gets the ready 8bpp intermediate backbuffer GUI bitmap used to copy network transmitted image to before sending. - /// - /// Which player screen to get intermediate GUI bitmap for. - /// A pointer to the 8bpp intermediate GUI BITMAP. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player screen to get intermediate GUI bitmap for. + /// @return A pointer to the 8bpp intermediate GUI BITMAP. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetNetworkBackBufferIntermediate8Current(int player) const { return m_NetworkBackBufferIntermediate8[m_NetworkFrameCurrent][player].get(); } - /// /// Gets the current 8bpp intermediate backbuffer bitmap used to copy network transmitted image to before sending. - /// - /// Which player screen to get intermediate bitmap for. - /// A pointer to the 8bpp intermediate BITMAP. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player screen to get intermediate bitmap for. + /// @return A pointer to the 8bpp intermediate BITMAP. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetNetworkBackBufferIntermediateGUI8Ready(int player) const { return m_NetworkBackBufferIntermediateGUI8[m_NetworkFrameReady][player].get(); } - /// /// Gets the current 8bpp intermediate backbuffer GUI bitmap used to copy network transmitted image to before sending. - /// - /// Which player screen to get intermediate GUI bitmap for. - /// A pointer to the 8bpp intermediate GUI BITMAP. OWNERSHIP IS NOT TRANSFERRED! + /// @param player Which player screen to get intermediate GUI bitmap for. + /// @return A pointer to the 8bpp intermediate GUI BITMAP. OWNERSHIP IS NOT TRANSFERRED! BITMAP* GetNetworkBackBufferIntermediateGUI8Current(int player) const { return m_NetworkBackBufferIntermediateGUI8[m_NetworkFrameCurrent][player].get(); } // TODO: Figure out. - /// /// - /// - /// - /// + /// @param screen + /// @return Vector GetTargetPos(int screen) const { return m_TargetPos[m_NetworkFrameReady][screen]; } - /// /// Gets whether we are drawing the contents of the network backbuffers on top of m_BackBuffer8 every frame. - /// - /// Whether we are drawing the contents of the network backbuffers on top of m_BackBuffer8 every frame or not. + /// @return Whether we are drawing the contents of the network backbuffers on top of m_BackBuffer8 every frame or not. bool GetDrawNetworkBackBuffer() const { return m_DrawNetworkBackBuffer; } - /// /// Sets whether to draw the contents of the network backbuffers on top of m_BackBuffer8 every frame. - /// - /// Whether to draw the contents of the network backbuffers on top of m_BackBuffer8 every frame or not. + /// @param value Whether to draw the contents of the network backbuffers on top of m_BackBuffer8 every frame or not. void SetDrawNetworkBackBuffer(bool value) { m_DrawNetworkBackBuffer = value; } - /// /// Gets whether we are dumping the contents of the m_BackBuffer8 to the network backbuffers every frame. - /// - /// Whether the contents of the m_BackBuffer8 are being dumped to the network backbuffers every frame. + /// @return Whether the contents of the m_BackBuffer8 are being dumped to the network backbuffers every frame. bool GetStoreNetworkBackBuffer() const { return m_StoreNetworkBackBuffer; } - /// /// Creates a new set of network backbuffers for the specified player. - /// - /// Player to create new backbuffer for. - /// Width of new backbuffer. - /// Height of new backbuffer + /// @param player Player to create new backbuffer for. + /// @param width Width of new backbuffer. + /// @param height Height of new backbuffer void CreateNewNetworkPlayerBackBuffer(int player, int width, int height); #pragma endregion #pragma region Palette Routines - /// /// Loads a palette from a bitmap file and sets it as the currently used screen palette. - /// - /// String with the path to the palette bitmap file. - /// Whether palette loaded successfully or not. + /// @param palettePath String with the path to the palette bitmap file. + /// @return Whether palette loaded successfully or not. bool LoadPalette(const std::string& palettePath); - /// /// Gets the ContentFile describing the location of the color palette. - /// - /// An reference to a ContentFile which described the palette location. + /// @return An reference to a ContentFile which described the palette location. const ContentFile& GetPaletteFile() const { return m_PaletteFile; } - /// /// Fades the palette in from black at a specified speed. - /// - /// Speed specified from (slowest) 1 - 64 (fastest). + /// @param fadeSpeed Speed specified from (slowest) 1 - 64 (fastest). void FadeInPalette(int fadeSpeed = 1) { PALETTE pal; get_palette(pal); fade_in(pal, Limit(fadeSpeed, 64, 1)); } - /// /// Fades the palette out to black at a specified speed. - /// - /// Speed specified from (slowest) 1 - 64 (fastest). + /// @param fadeSpeed Speed specified from (slowest) 1 - 64 (fastest). void FadeOutPalette(int fadeSpeed = 1) { fade_out(Limit(fadeSpeed, 64, 1)); } #pragma endregion #pragma region Screen Capture - /// /// Dumps a bitmap to a 8bpp PNG file. - /// - /// The individual bitmap that will be dumped. - /// The filename of the file to save to, WITHOUT EXTENSION. - /// 0 for success, anything below 0 is a sign of failure. + /// @param bitmap The individual bitmap that will be dumped. + /// @param nameBase The filename of the file to save to, WITHOUT EXTENSION. + /// @return 0 for success, anything below 0 is a sign of failure. int SaveBitmapToPNG(BITMAP* bitmap, const char* nameBase) { return SaveBitmap(SingleBitmap, nameBase, bitmap); } - /// /// Dumps a bitmap of the screen backbuffer to a 8bpp PNG file. - /// - /// The filename of the file to save to, WITHOUT EXTENSION. - /// 0 for success, anything below 0 is a sign of failure. + /// @param nameBase The filename of the file to save to, WITHOUT EXTENSION. + /// @return 0 for success, anything below 0 is a sign of failure. int SaveScreenToPNG(const char* nameBase) { return SaveBitmap(ScreenDump, nameBase); } - /// /// Dumps a bitmap of everything on the scene to a PNG file. - /// - /// The filename of the file to save to, WITHOUT EXTENSION. - /// 0 for success, anything below 0 is a sign of failure. + /// @param nameBase The filename of the file to save to, WITHOUT EXTENSION. + /// @return 0 for success, anything below 0 is a sign of failure. int SaveWorldToPNG(const char* nameBase) { return SaveBitmap(WorldDump, nameBase); } - /// /// Dumps a miniature screenshot of the whole scene to be used as a preview to a PNG file. - /// - /// The filename of the file to save to, WITHOUT EXTENSION. - /// 0 for success, anything below 0 is a sign of failure. + /// @param nameBase The filename of the file to save to, WITHOUT EXTENSION. + /// @return 0 for success, anything below 0 is a sign of failure. int SaveWorldPreviewToPNG(const char* nameBase) { return SaveBitmap(ScenePreviewDump, nameBase); } #pragma endregion private: - /// /// Enumeration with different settings for the SaveBitmap() method. - /// enum SaveBitmapMode { SingleBitmap, ScreenDump, @@ -509,10 +381,8 @@ namespace RTE { int m_BlackColor; //!< Palette index for the black color. int m_AlmostBlackColor; //!< Palette index for the closest to black color. - /// /// Color tables for blended drawing in indexed color mode. /// The key is an array of the RGBA values. The value is a pair of the color table itself and a time stamp of when it was last accessed for use during color table pruning. - /// std::array, std::pair>, DrawBlendMode::BlendModeCount> m_ColorTables; Timer m_ColorTablePruneTimer; //!< Timer for pruning unused color tables to prevent ridiculous memory usage. @@ -562,111 +432,83 @@ namespace RTE { std::mutex m_NetworkBitmapLock[c_MaxScreenCount]; //!< Mutex lock for thread safe updating of the network backbuffer bitmaps. #pragma region Initialize Breakdown - /// /// Creates all the frame buffer bitmaps to be used by FrameMan. This is called during Initialize(). - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int CreateBackBuffers(); - /// /// Creates the RGB lookup table and color table presets for drawing with transparency in indexed color mode. This is called during Initialize(). - /// void CreatePresetColorTables(); #pragma endregion #pragma region Draw Breakdown - /// /// Updates the drawing position of each player screen on the backbuffer when split screen is active. This is called during Draw(). - /// - /// The player screen to update offset for. - /// Vector representing the screen offset. + /// @param playerScreen The player screen to update offset for. + /// @param screenOffset Vector representing the screen offset. void UpdateScreenOffsetForSplitScreen(int playerScreen, Vector& screenOffset) const; - /// /// Draws all the text messages to the specified player screen. This is called during Draw(). - /// - /// The player screen the text will be shown on. - /// The bitmap the text will be drawn on. + /// @param playerScreen The player screen the text will be shown on. + /// @param playerGUIBitmap The bitmap the text will be drawn on. void DrawScreenText(int playerScreen, AllegroBitmap playerGUIBitmap); - /// /// Draws the screen flash effect to the specified player screen with parameters set by FlashScreen(). This is called during Draw(). - /// - /// The player screen the flash effect will be shown to. - /// The bitmap the flash effect will be drawn on. + /// @param playerScreen The player screen the flash effect will be shown to. + /// @param playerGUIBitmap The bitmap the flash effect will be drawn on. void DrawScreenFlash(int playerScreen, BITMAP* playerGUIBitmap); - /// /// Renders current frame and marks it ready for network transmission. This is called during Draw(). - /// void PrepareFrameForNetwork(); #pragma endregion #pragma region Screen Capture - /// /// Draws the current frame of the whole scene to a temporary buffer that is later saved as a screenshot. - /// - /// If true will skip drawing objects, post-effects and sky gradient in the WorldDump. To be used for dumping scene preview images. + /// @param drawForScenePreview If true will skip drawing objects, post-effects and sky gradient in the WorldDump. To be used for dumping scene preview images. void DrawWorldDump(bool drawForScenePreview = false) const; - /// /// Shared method for saving screenshots or individual bitmaps. - /// - /// What is being saved. See SaveBitmapMode enumeration for a list of modes. - /// + /// @param modeToSave What is being saved. See SaveBitmapMode enumeration for a list of modes. + /// @param nameBase /// The name of the file that is being saved, WITHOUT EXTENSION. /// Eg, If "Test" is passed in, this function will save to Test000.bmp, if that file does not already exist. If it does exist, it will attempt 001, and so on. - /// - /// The individual bitmap that will be dumped. 0 or nullptr if not in SingleBitmap mode. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param bitmapToSave The individual bitmap that will be dumped. 0 or nullptr if not in SingleBitmap mode. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int SaveBitmap(SaveBitmapMode modeToSave, const std::string& nameBase, BITMAP* bitmapToSave = nullptr); - /// /// Saves the front buffer to the screen dump buffer. - /// void SaveScreenToBitmap(); - /// /// Saves a BITMAP as an 8bpp bitmap file that is indexed with the specified palette. - /// - /// The full name of the file that is being saved. Path and everything included. - /// The BITMAP that is being saved into a file. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - /// + /// @param fileName The full name of the file that is being saved. Path and everything included. + /// @param bitmapToSave The BITMAP that is being saved into a file. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @remark /// This method is a workaround to Allegro being unable to set a color conversion mode when saving files. /// It works by first saving the 32bpp bitmap as is, then loading it back under the REDUCE_TO_256 color conversion mode, blitting it to a fresh bitmap and saving it again with the passed in palette. /// The re-blitted bitmap is properly 8bpp and will be indexed correctly. The old saved file is deleted in the process before the new one is saved. - /// int SaveIndexedPNG(const char* fileName, BITMAP* bitmapToSave) const; #pragma endregion - /// /// Shared method for drawing lines to avoid duplicate code. Will by called by either DrawLine() or DrawDotLine(). - /// - /// The Bitmap to draw to. Ownership is NOT transferred. - /// The absolute Start point. - /// The absolute end point. - /// The color value of the line. - /// A color to alternate with. Every other pixel drawn will have this if !0. - /// How many pixels to skip drawing between drawn ones. 0 means solid line 2 means there's a gap of two pixels between each drawn one. Should be more than 0 for dots. - /// The start of the skipping phase. If skip is 10 and this is 5, the first dot will be drawn after 5 pixels. - /// Whether the line should take the shortest possible route across scene wraps. - /// Whether to draw a regular line or a dot line. True for dot line. - /// The bitmap to be used for dots (will be centered). - /// The end state of the skipping phase. Eg if 4 is returned here the last dot was placed 4 pixels ago. + /// @param bitmap The Bitmap to draw to. Ownership is NOT transferred. + /// @param start The absolute Start point. + /// @param end The absolute end point. + /// @param color The color value of the line. + /// @param altColor A color to alternate with. Every other pixel drawn will have this if !0. + /// @param skip How many pixels to skip drawing between drawn ones. 0 means solid line 2 means there's a gap of two pixels between each drawn one. Should be more than 0 for dots. + /// @param skipStart The start of the skipping phase. If skip is 10 and this is 5, the first dot will be drawn after 5 pixels. + /// @param shortestWrap Whether the line should take the shortest possible route across scene wraps. + /// @param drawDot Whether to draw a regular line or a dot line. True for dot line. + /// @param dot The bitmap to be used for dots (will be centered). + /// @return The end state of the skipping phase. Eg if 4 is returned here the last dot was placed 4 pixels ago. int SharedDrawLine(BITMAP* bitmap, const Vector& start, const Vector& end, int color, int altColor = 0, int skip = 0, int skipStart = 0, bool shortestWrap = false, bool drawDot = false, BITMAP* dot = nullptr) const; - /// /// Gets the requested font from the GUI engine's current skin. Ownership is NOT transferred! - /// - /// Size of font to get. True for small font, false for large font. - /// Whether to get the 32bpp color version of the font. - /// A pointer to the requested font, or 0 if no font was found. + /// @param isSmall Size of font to get. True for small font, false for large font. + /// @param trueColor Whether to get the 32bpp color version of the font. + /// @return A pointer to the requested font, or 0 if no font was found. GUIFont* GetFont(bool isSmall, bool trueColor); - /// /// Clears all the member variables of this FrameMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index 0e6aa9734d..3afd5d374a 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -11,8 +11,6 @@ namespace RTE { const std::unordered_set LuaMan::c_FileAccessModes = {"r", "r+", "w", "w+", "a", "a+", "rt", "wt"}; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::Clear() { m_State = nullptr; m_TempEntity = nullptr; @@ -21,8 +19,6 @@ namespace RTE { m_CurrentlyRunningScriptPath = ""; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::Initialize() { m_State = luaL_newstate(); luabind::open(m_State); @@ -248,38 +244,26 @@ namespace RTE { "_TriggerAsyncPathCallback = function(id, param) if _AsyncPathCallbacks[id] ~= nil then _AsyncPathCallbacks[id](param); _AsyncPathCallbacks[id] = nil; end end\n"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::Destroy() { lua_close(m_State); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::SelectRand(int minInclusive, int maxInclusive) { return m_RandomGenerator.RandomNum(minInclusive, maxInclusive); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - double LuaStateWrapper::RangeRand(double minInclusive, double maxInclusive) { return m_RandomGenerator.RandomNum(minInclusive, maxInclusive); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - double LuaStateWrapper::NormalRand() { return m_RandomGenerator.RandomNormalNum(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - double LuaStateWrapper::PosRand() { return m_RandomGenerator.RandomNum(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Passthrough LuaMan Functions const std::vector* LuaStateWrapper::DirectoryList(const std::string& path) { return g_LuaMan.DirectoryList(path); } const std::vector* LuaStateWrapper::FileList(const std::string& path) { return g_LuaMan.FileList(path); } @@ -300,14 +284,10 @@ namespace RTE { void LuaStateWrapper::FileWriteLine(int fileIndex, const std::string& line) { return g_LuaMan.FileWriteLine(fileIndex, line); } bool LuaStateWrapper::FileEOF(int fileIndex) { return g_LuaMan.FileEOF(fileIndex); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::Clear() { m_OpenedFiles.fill(nullptr); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::Initialize() { m_MasterScriptState.Initialize(); @@ -322,40 +302,28 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaStateWrapper& LuaMan::GetMasterScriptState() { return m_MasterScriptState; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaStatesArray& LuaMan::GetThreadedScriptStates() { return m_ScriptStates; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - thread_local LuaStateWrapper* s_luaStateOverride = nullptr; LuaStateWrapper* LuaMan::GetThreadLuaStateOverride() const { return s_luaStateOverride; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::SetThreadLuaStateOverride(LuaStateWrapper* luaState) { s_luaStateOverride = luaState; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - thread_local LuaStateWrapper* s_currentLuaState = nullptr; LuaStateWrapper* LuaMan::GetThreadCurrentLuaState() const { return s_currentLuaState; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LuaStateWrapper* LuaMan::GetAndLockFreeScriptState() { if (s_luaStateOverride) { // We're creating this object in a multithreaded environment, ensure that it's assigned to the same script state as us @@ -383,8 +351,6 @@ namespace RTE { return &m_ScriptStates[ourState]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::ClearUserModuleCache() { m_GarbageCollectionTask.wait(); @@ -399,15 +365,11 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::AddLuaScriptCallback(const std::function& callback) { std::scoped_lock lock(m_ScriptCallbacksMutex); m_ScriptCallbacks.emplace_back(callback); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::ExecuteLuaScriptCallbacks() { std::vector> callbacks; @@ -422,8 +384,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::unordered_map LuaMan::GetScriptTimings() const { std::unordered_map timings = m_MasterScriptState.GetScriptTimings(); for (const LuaStateWrapper& luaState: m_ScriptStates) { @@ -436,8 +396,6 @@ namespace RTE { return timings; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::Destroy() { for (int i = 0; i < c_MaxOpenFiles; ++i) { FileClose(i); @@ -445,38 +403,26 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::ClearUserModuleCache() { luaL_dostring(m_State, "for m, n in pairs(package.loaded) do if type(n) == \"boolean\" then package.loaded[m] = nil; end; end;"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::ClearLuaScriptCache() { m_ScriptCache.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Entity* LuaStateWrapper::GetTempEntity() const { return m_TempEntity; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::SetTempEntity(Entity* entity) { m_TempEntity = entity; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector& LuaStateWrapper::GetTempEntityVector() const { return m_TempEntityVector; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::SetTempEntityVector(const std::vector& entityVector) { m_TempEntityVector.reserve(entityVector.size()); for (const Entity* entity: entityVector) { @@ -484,8 +430,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::SetLuaPath(const std::string& filePath) { const std::string moduleName = g_PresetMan.GetModuleNameFromPath(filePath); const std::string moduleFolder = g_PresetMan.IsModuleOfficial(moduleName) ? System::GetDataDirectory() : System::GetModDirectory(); @@ -506,14 +450,10 @@ namespace RTE { lua_pop(m_State, 1); // get rid of package table from top of stack. } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::unordered_map& LuaStateWrapper::GetScriptTimings() const { return m_ScriptTimings; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFunctionString(const std::string& functionName, const std::string& selfObjectName, const std::vector& variablesToSafetyCheck, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments) { std::stringstream scriptString; if (!variablesToSafetyCheck.empty()) { @@ -569,8 +509,6 @@ namespace RTE { return result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptString(const std::string& scriptString, bool consoleErrors) { if (scriptString.empty()) { return -1; @@ -598,8 +536,6 @@ namespace RTE { return error; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFunctionObject(const LuabindObjectWrapper* functionObject, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { int status = 0; @@ -673,8 +609,6 @@ namespace RTE { return status; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFile(const std::string& filePath, bool consoleErrors, bool doInSandboxedEnvironment) { const std::string fullScriptPath = g_PresetMan.GetFullModulePath(filePath); if (fullScriptPath.empty()) { @@ -751,8 +685,6 @@ namespace RTE { return error; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaStateWrapper::RetrieveFunctions(const std::string& funcObjectName, const std::vector& functionNamesToLookFor, std::unordered_map& outFunctionNamesAndObjects) { std::lock_guard lock(m_Mutex); s_currentLuaState = this; @@ -783,8 +715,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFileAndRetrieveFunctions(const std::string& filePath, const std::vector& functionNamesToLookFor, std::unordered_map& outFunctionNamesAndObjects, bool forceReload) { static bool disableCaching = false; forceReload = forceReload || disableCaching; @@ -815,8 +745,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::Update() { for (MovableObject* mo: m_AddedRegisteredMOs) { m_RegisteredMOs.insert(mo); @@ -824,14 +752,10 @@ namespace RTE { m_AddedRegisteredMOs.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::ClearScriptTimings() { m_ScriptTimings.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaStateWrapper::ExpressionIsTrue(const std::string& expression, bool consoleErrors) { if (expression.empty()) { return false; @@ -858,8 +782,6 @@ namespace RTE { return result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::SavePointerAsGlobal(void* objectToSave, const std::string& globalName) { std::lock_guard lock(m_Mutex); @@ -869,8 +791,6 @@ namespace RTE { lua_setglobal(m_State, globalName.c_str()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaStateWrapper::GlobalIsDefined(const std::string& globalName) { std::lock_guard lock(m_Mutex); @@ -884,8 +804,6 @@ namespace RTE { return isDefined; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaStateWrapper::TableEntryIsDefined(const std::string& tableName, const std::string& indexName) { std::lock_guard lock(m_Mutex); @@ -906,27 +824,19 @@ namespace RTE { return isDefined; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaStateWrapper::ErrorExists() const { return !m_LastError.empty(); ; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string LuaStateWrapper::GetLastError() const { return m_LastError; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaStateWrapper::ClearErrors() { m_LastError.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string LuaStateWrapper::DescribeLuaStack() { int indexOfTopOfStack = lua_gettop(m_State); if (indexOfTopOfStack == 0) { @@ -957,8 +867,6 @@ namespace RTE { return stackDescription.str(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector* LuaMan::DirectoryList(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); auto* directoryPaths = new std::vector(); @@ -978,8 +886,6 @@ namespace RTE { return directoryPaths; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector* LuaMan::FileList(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); auto* filePaths = new std::vector(); @@ -999,8 +905,6 @@ namespace RTE { return filePaths; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::FileExists(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { @@ -1012,8 +916,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::DirectoryExists(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { @@ -1025,15 +927,11 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: Move to ModuleMan, once the ModuleMan PR has been merged bool LuaMan::IsValidModulePath(const std::string& path) { return (path.find("..") == std::string::npos) && (path.find(System::GetModulePackageExtension()) != std::string::npos); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaMan::FileOpen(const std::string& path, const std::string& accessMode) { if (c_FileAccessModes.find(accessMode) == c_FileAccessModes.end()) { g_ConsoleMan.PrintString("ERROR: Cannot open file, invalid file access mode specified."); @@ -1105,8 +1003,6 @@ namespace RTE { return -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::FileClose(int fileIndex) { if (fileIndex > -1 && fileIndex < c_MaxOpenFiles && m_OpenedFiles.at(fileIndex)) { fclose(m_OpenedFiles[fileIndex]); @@ -1114,16 +1010,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::FileCloseAll() { for (int file = 0; file < c_MaxOpenFiles; ++file) { FileClose(file); } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::FileRemove(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { @@ -1138,8 +1030,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::DirectoryCreate(const std::string& path, bool recursive) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { @@ -1158,8 +1048,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::DirectoryRemove(const std::string& path, bool recursive) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { @@ -1180,8 +1068,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::FileRename(const std::string& oldPath, const std::string& newPath) { std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath); std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath); @@ -1203,8 +1089,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::DirectoryRename(const std::string& oldPath, const std::string& newPath) { std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath); std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath); @@ -1226,8 +1110,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string LuaMan::FileReadLine(int fileIndex) { if (fileIndex > -1 && fileIndex < c_MaxOpenFiles && m_OpenedFiles.at(fileIndex)) { char buf[4096]; @@ -1240,8 +1122,6 @@ namespace RTE { return ""; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::FileWriteLine(int fileIndex, const std::string& line) { if (fileIndex > -1 && fileIndex < c_MaxOpenFiles && m_OpenedFiles.at(fileIndex)) { if (fputs(line.c_str(), m_OpenedFiles[fileIndex]) == EOF) { @@ -1252,8 +1132,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::FileEOF(int fileIndex) { if (fileIndex > -1 && fileIndex < c_MaxOpenFiles && m_OpenedFiles.at(fileIndex)) { return feof(m_OpenedFiles[fileIndex]); @@ -1262,8 +1140,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::Update() { ZoneScoped; @@ -1279,8 +1155,6 @@ namespace RTE { LuabindObjectWrapper::ApplyQueuedDeletions(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::StartAsyncGarbageCollection() { ZoneScoped; @@ -1304,8 +1178,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LuaMan::ClearScriptTimings() { m_MasterScriptState.ClearScriptTimings(); for (LuaStateWrapper& luaState: m_ScriptStates) { diff --git a/Source/Managers/LuaMan.h b/Source/Managers/LuaMan.h index d38bbdecbc..12949582f1 100644 --- a/Source/Managers/LuaMan.h +++ b/Source/Managers/LuaMan.h @@ -17,273 +17,197 @@ namespace RTE { class LuabindObjectWrapper; class MovableObject; - /// /// A single lua state. Multiple of these can exist at once for multithreaded scripting. - /// class LuaStateWrapper { public: #pragma region Creation - /// /// Constructor method used to instantiate a LuaStateWrapper object in system memory. Initialize() should be called before using the object. - /// LuaStateWrapper() { Clear(); } - /// /// Makes the LuaStateWrapper object ready for use. - /// void Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a LuaStateWrapper object before deletion from system memory. - /// ~LuaStateWrapper() { Destroy(); } - /// /// Destroys and resets (through Clear()) the LuaStateWrapper object. - /// void Destroy(); #pragma endregion #pragma region Getters and Setters - /// /// Gets a temporary Entity that can be accessed in the Lua state. - /// - /// The temporary entity. Ownership is NOT transferred! + /// @return The temporary entity. Ownership is NOT transferred! Entity* GetTempEntity() const; - /// /// Sets a temporary Entity that can be accessed in the Lua state. - /// - /// The temporary entity. Ownership is NOT transferred! + /// @param entity The temporary entity. Ownership is NOT transferred! void SetTempEntity(Entity* entity); - /// /// Gets the temporary vector of Entities that can be accessed in the Lua state. - /// - /// The temporary vector of entities. Ownership is NOT transferred! + /// @return The temporary vector of entities. Ownership is NOT transferred! const std::vector& GetTempEntityVector() const; - /// /// Sets a temporary vector of Entities that can be accessed in the Lua state. These Entities are const_cast so they're non-const, for ease-of-use in Lua. - /// - /// The temporary vector of entities. Ownership is NOT transferred! + /// @param entityVector The temporary vector of entities. Ownership is NOT transferred! void SetTempEntityVector(const std::vector& entityVector); - /// /// Sets the proper package.path for the script to run. - /// - /// The path to the file to load and run. + /// @param filePath The path to the file to load and run. void SetLuaPath(const std::string& filePath); - /// /// Gets this LuaStateWrapper's internal lua state. - /// - /// This LuaStateWrapper's internal lua state. + /// @return This LuaStateWrapper's internal lua state. lua_State* GetLuaState() { return m_State; }; - /// /// Gets m_ScriptTimings. - /// - /// m_ScriptTimings. + /// @return m_ScriptTimings. const std::unordered_map& GetScriptTimings() const; - /// /// Gets the currently running script filepath, if applicable. - /// - /// The currently running script filepath. May return inaccurate values for non-MO scripts due to weirdness in setup. + /// @return The currently running script filepath. May return inaccurate values for non-MO scripts due to weirdness in setup. std::string_view GetCurrentlyRunningScriptFilePath() const { return m_CurrentlyRunningScriptPath; } #pragma endregion #pragma region Script Responsibility Handling - /// /// Registers an MO as using us. - /// - /// The MO to register with us. Ownership is NOT transferred! + /// @param moToRegister The MO to register with us. Ownership is NOT transferred! void RegisterMO(MovableObject* moToRegister) { m_AddedRegisteredMOs.insert(moToRegister); } - /// /// Unregisters an MO as using us. - /// - /// The MO to unregister as using us. Ownership is NOT transferred! + /// @param moToUnregister The MO to unregister as using us. Ownership is NOT transferred! void UnregisterMO(MovableObject* moToUnregister) { m_RegisteredMOs.erase(moToUnregister); m_AddedRegisteredMOs.erase(moToUnregister); } - /// /// Gets a list of the MOs registed as using us. - /// - /// The MOs registed as using us. + /// @return The MOs registed as using us. const std::unordered_set& GetRegisteredMOs() const { return m_RegisteredMOs; } #pragma endregion #pragma region Script Execution Handling - /// /// Runs the given Lua function with optional safety checks and arguments. The first argument to the function will always be the self object. /// If either argument list has entries, they will be passed into the function in order, with entity arguments first. - /// - /// The name that gives access to the function in the global Lua namespace. - /// The name that gives access to the self object in the global Lua namespace. - /// Optional vector of strings that should be safety checked in order before running the Lua function. Defaults to empty. - /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. - /// Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc. Defaults to empty. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param functionName The name that gives access to the function in the global Lua namespace. + /// @param selfObjectName The name that gives access to the self object in the global Lua namespace. + /// @param variablesToSafetyCheck Optional vector of strings that should be safety checked in order before running the Lua function. Defaults to empty. + /// @param functionEntityArguments Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. + /// @param functionLiteralArguments Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc. Defaults to empty. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int RunScriptFunctionString(const std::string& functionName, const std::string& selfObjectName, const std::vector& variablesToSafetyCheck = std::vector(), const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector()); - /// /// Takes a string containing a script snippet and runs it on the state. - /// - /// The string with the script snippet. - /// Whether to report any errors to the console immediately. - /// Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. + /// @param scriptString The string with the script snippet. + /// @param consoleErrors Whether to report any errors to the console immediately. + /// @return Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. int RunScriptString(const std::string& scriptString, bool consoleErrors = true); - /// /// Runs the given Lua function object. The first argument to the function will always be the self object. /// If either argument list has entries, they will be passed into the function in order, with entity arguments first. - /// - /// The LuabindObjectWrapper containing the Lua function to be run. - /// The name of the global Lua table that gives access to the self object. - /// The key for this object in the respective global Lua table. - /// Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. - /// Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param functionObjectWrapper The LuabindObjectWrapper containing the Lua function to be run. + /// @param selfGlobalTableName The name of the global Lua table that gives access to the self object. + /// @param selfGlobalTableKey The key for this object in the respective global Lua table. + /// @param functionEntityArguments Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. + /// @param functionLiteralArguments Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int RunScriptFunctionObject(const LuabindObjectWrapper* functionObjectWrapper, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); - /// /// Opens and loads a file containing a script and runs it on the state. - /// - /// The path to the file to load and run. - /// Whether to report any errors to the console immediately. - /// Whether to do it in a sandboxed environment, or the global environment. - /// Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. + /// @param filePath The path to the file to load and run. + /// @param consoleErrors Whether to report any errors to the console immediately. + /// @param doInSandboxedEnvironment Whether to do it in a sandboxed environment, or the global environment. + /// @return Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. int RunScriptFile(const std::string& filePath, bool consoleErrors = true, bool doInSandboxedEnvironment = true); - /// /// Retrieves all of the specified functions that exist into the output map, and refreshes the cache. - /// - /// The path to the file to load and run. - /// The vector of strings defining the function names to be retrieved. - /// The map of function names to LuabindObjectWrappers to be retrieved from the script that was run. - /// Returns whether functions were successfully retrieved. + /// @param filePath The path to the file to load and run. + /// @param functionNamesToLookFor The vector of strings defining the function names to be retrieved. + /// @param outFunctionNamesAndObjects The map of function names to LuabindObjectWrappers to be retrieved from the script that was run. + /// @return Returns whether functions were successfully retrieved. bool RetrieveFunctions(const std::string& functionObjectName, const std::vector& functionNamesToLookFor, std::unordered_map& outFunctionNamesAndObjects); - /// /// Opens and loads a file containing a script and runs it on the state, then retrieves all of the specified functions that exist into the output map. - /// - /// The path to the file to load and run. - /// The vector of strings defining the function names to be retrieved. - /// The map of function names to LuabindObjectWrappers to be retrieved from the script that was run. - /// Whether caching shouldn't be used. - /// Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. + /// @param filePath The path to the file to load and run. + /// @param functionNamesToLookFor The vector of strings defining the function names to be retrieved. + /// @param outFunctionNamesAndObjects The map of function names to LuabindObjectWrappers to be retrieved from the script that was run. + /// @param noCaching Whether caching shouldn't be used. + /// @return Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. int RunScriptFileAndRetrieveFunctions(const std::string& filePath, const std::vector& functionNamesToLookFor, std::unordered_map& outFunctionNamesAndObjects, bool forceReload = false); #pragma endregion #pragma region Concrete Methods - /// /// Updates this Lua state. - /// void Update(); - /// /// Clears m_ScriptTimings. - /// void ClearScriptTimings(); #pragma endregion #pragma region MultiThreading - /// /// Gets the mutex to lock this lua state. - /// std::recursive_mutex& GetMutex() { return m_Mutex; }; #pragma endregion #pragma region - /// /// Gets whether the given Lua expression evaluates to true or false. - /// - /// The string with the expression to evaluate. - /// Whether to report any errors to the console immediately. - /// Whether the expression was true. + /// @param expression The string with the expression to evaluate. + /// @param consoleErrors Whether to report any errors to the console immediately. + /// @return Whether the expression was true. bool ExpressionIsTrue(const std::string& expression, bool consoleErrors); - /// /// Takes a pointer to an object and saves it in the Lua state as a global of a specified variable name. - /// - /// The pointer to the object to save. Ownership is NOT transferred! - /// The name of the global var in the Lua state to save the pointer to. + /// @param objectToSave The pointer to the object to save. Ownership is NOT transferred! + /// @param globalName The name of the global var in the Lua state to save the pointer to. void SavePointerAsGlobal(void* objectToSave, const std::string& globalName); - /// /// Checks if there is anything defined on a specific global var in Lua. - /// - /// The name of the global var in the Lua state to check. - /// Whether that global var has been defined yet in the Lua state. + /// @param globalName The name of the global var in the Lua state to check. + /// @return Whether that global var has been defined yet in the Lua state. bool GlobalIsDefined(const std::string& globalName); - /// /// Checks if there is anything defined in a specific index of a table. - /// - /// The name of the table to look inside. - /// The name of the index to check inside that table. - /// Whether that table var has been defined yet in the Lua state. + /// @param tableName The name of the table to look inside. + /// @param indexName The name of the index to check inside that table. + /// @return Whether that table var has been defined yet in the Lua state. bool TableEntryIsDefined(const std::string& tableName, const std::string& indexName); - /// /// Clears internal Lua package tables from all user-defined modules. Those must be reloaded with ReloadAllScripts(). - /// void ClearUserModuleCache(); - /// /// Clears the Lua script cache. - /// void ClearLuaScriptCache(); #pragma endregion #pragma region Error Handling - /// /// Tells whether there are any errors reported waiting to be read. - /// - /// Whether errors exist. + /// @return Whether errors exist. bool ErrorExists() const; - /// /// Returns the last error message from executing scripts. - /// - /// The error string with hopefully meaningful info about what went wrong. + /// @return The error string with hopefully meaningful info about what went wrong. std::string GetLastError() const; - /// /// Clears the last error message, so the Lua state will not be considered to have any errors until the next time there's a script error. - /// void ClearErrors(); #pragma endregion private: - /// /// Gets a random integer between minInclusive and maxInclusive. - /// - /// A random integer between minInclusive and maxInclusive. + /// @return A random integer between minInclusive and maxInclusive. int SelectRand(int minInclusive, int maxInclusive); - /// /// Gets a random real between minInclusive and maxInclusive. - /// - /// A random real between minInclusive and maxInclusive. + /// @return A random real between minInclusive and maxInclusive. double RangeRand(double minInclusive, double maxInclusive); - /// /// Gets a random number between -1 and 1. - /// - /// A random number between -1 and 1. + /// @return A random number between -1 and 1. double NormalRand(); - /// /// Gets a random number between 0 and 1. - /// - /// A random number between 0 and 1. + /// @return A random number between 0 and 1. double PosRand(); #pragma region Passthrough LuaMan Functions @@ -307,15 +231,11 @@ namespace RTE { bool FileEOF(int fileIndex); #pragma endregion - /// /// Generates a string that describes the current state of the Lua stack, for debugging purposes. - /// - /// A string that describes the current state of the Lua stack. + /// @return A string that describes the current state of the Lua stack. std::string DescribeLuaStack(); - /// /// Clears all the member variables of this LuaStateWrapper, effectively resetting the members of this abstraction level only. - /// void Clear(); std::unordered_set m_RegisteredMOs; //!< The objects using our lua state. @@ -343,236 +263,168 @@ namespace RTE { typedef std::vector LuaStatesArray; - /// /// The singleton manager of each Lua state. - /// class LuaMan : public Singleton { friend class SettingsMan; friend class LuaStateWrapper; public: #pragma region Creation - /// /// Constructor method used to instantiate a LuaMan object in system memory. Initialize() should be called before using the object. - /// LuaMan() { Clear(); } - /// /// Makes the LuaMan object ready for use. - /// void Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a LuaMan object before deletion from system memory. - /// ~LuaMan() { Destroy(); } - /// /// Destroys and resets (through Clear()) the LuaMan object. - /// void Destroy(); #pragma endregion #pragma region Lua State Handling - /// /// Returns our master script state (where activies, global scripts etc run). - /// - /// The master script state. + /// @return The master script state. LuaStateWrapper& GetMasterScriptState(); - /// /// Returns our threaded script states which movable objects use. - /// - /// A list of threaded script states. + /// @return A list of threaded script states. LuaStatesArray& GetThreadedScriptStates(); - /// /// Gets the current thread lua state override that new objects created will be assigned to. - /// - /// The current lua state to force objects to be assigned to. + /// @return The current lua state to force objects to be assigned to. LuaStateWrapper* GetThreadLuaStateOverride() const; - /// /// Forces all new MOs created in this thread to be assigned to a particular lua state. /// This is to ensure that objects created in threaded Lua environments can be safely used. - /// - /// The lua state to force objects to be assigned to. + /// @param luaState The lua state to force objects to be assigned to. void SetThreadLuaStateOverride(LuaStateWrapper* luaState); - /// /// Gets the current thread lua state that is running. - /// - /// The current lua state that is running. + /// @return The current lua state that is running. LuaStateWrapper* GetThreadCurrentLuaState() const; - /// /// Returns a free threaded script states to assign a movableobject to. /// This will be locked to our thread and safe to use - ensure that it'll be unlocked after use! - /// - /// A script state. + /// @return A script state. LuaStateWrapper* GetAndLockFreeScriptState(); - /// /// Clears internal Lua package tables from all user-defined modules. Those must be reloaded with ReloadAllScripts(). - /// void ClearUserModuleCache(); - /// /// Adds a function to be called prior to executing lua scripts. This is used to callback into lua from other threads safely. - /// - /// The callback function that will be executed. + /// @param callback The callback function that will be executed. void AddLuaScriptCallback(const std::function& callback); - /// /// Executes and clears all pending script callbacks. - /// void ExecuteLuaScriptCallbacks(); - /// /// Gets m_ScriptTimings. - /// - /// m_ScriptTimings. + /// @return m_ScriptTimings. const std::unordered_map GetScriptTimings() const; #pragma endregion #pragma region File I/O Handling - /// /// Returns a vector of all the directories in path, which is relative to the working directory. - /// - /// Directory path relative to the working directory. - /// A vector of the directories in path. + /// @param path Directory path relative to the working directory. + /// @return A vector of the directories in path. const std::vector* DirectoryList(const std::string& path); - /// /// Returns a vector of all the files in path, which is relative to the working directory. - /// - /// Directory path relative to the working directory. - /// A vector of the files in path. + /// @param path Directory path relative to the working directory. + /// @return A vector of the files in path. const std::vector* FileList(const std::string& path); - /// /// Returns whether or not the specified file exists. You can only check for files inside .rte folders in the working directory. - /// - /// Path to the file. All paths are made absolute by adding current working directory to the specified path. - /// Whether or not the specified file exists. + /// @param path Path to the file. All paths are made absolute by adding current working directory to the specified path. + /// @return Whether or not the specified file exists. bool FileExists(const std::string& path); - /// /// Returns whether or not the specified directory exists. You can only check for directories inside .rte folders in the working directory. - /// - /// Path to the directory. All paths are made absolute by adding current working directory to the specified path. - /// Whether or not the specified file exists. + /// @param path Path to the directory. All paths are made absolute by adding current working directory to the specified path. + /// @return Whether or not the specified file exists. bool DirectoryExists(const std::string& path); - /// /// Returns whether or not the path refers to an accessible file or directory. You can only check for files or directories inside .rte directories in the working directory. - /// - /// Path to the file or directory. All paths are made absolute by adding current working directory to the specified path. - /// Whether or not the specified file exists. + /// @param path Path to the file or directory. All paths are made absolute by adding current working directory to the specified path. + /// @return Whether or not the specified file exists. bool IsValidModulePath(const std::string& path); - /// /// Opens a file or creates one if it does not exist, depending on access mode. You can open files only inside .rte folders in the working directory. You can't open more that c_MaxOpenFiles file simultaneously. /// On Linux will attempt to open a file case insensitively. - /// - /// Path to the file. All paths are made absolute by adding current working directory to the specified path. - /// File access mode. See 'fopen' for list of modes. - /// File index in the opened files array. + /// @param path Path to the file. All paths are made absolute by adding current working directory to the specified path. + /// @param mode File access mode. See 'fopen' for list of modes. + /// @return File index in the opened files array. int FileOpen(const std::string& path, const std::string& accessMode); - /// /// Closes a previously opened file. - /// - /// File index in the opened files array. + /// @param fileIndex File index in the opened files array. void FileClose(int fileIndex); - /// /// Closes all previously opened files. - /// void FileCloseAll(); - /// /// Removes a file. - /// - /// Path to the file. All paths are made absolute by adding current working directory to the specified path. - /// Whether or not the file was removed. + /// @param path Path to the file. All paths are made absolute by adding current working directory to the specified path. + /// @return Whether or not the file was removed. bool FileRemove(const std::string& path); - /// /// Creates a directory, optionally recursively. - /// - /// Path to the directory to be created. All paths are made absolute by adding current working directory to the specified path. - /// Whether to recursively create parent directories. - /// Whether or not the directory was removed. + /// @param path Path to the directory to be created. All paths are made absolute by adding current working directory to the specified path. + /// @param recursive Whether to recursively create parent directories. + /// @return Whether or not the directory was removed. bool DirectoryCreate(const std::string& path, bool recursive); - /// /// Removes a directory, optionally recursively. - /// - /// Path to the directory to be removed. All paths are made absolute by adding current working directory to the specified path. - /// Whether to recursively remove files and directories. - /// Whether or not the directory was removed. + /// @param path Path to the directory to be removed. All paths are made absolute by adding current working directory to the specified path. + /// @param recursive Whether to recursively remove files and directories. + /// @return Whether or not the directory was removed. bool DirectoryRemove(const std::string& path, bool recursive); - /// /// Moves or renames the file oldPath to newPath. /// In order to get consistent behavior across Windows and Linux across all 4 combinations of oldPath and newPath being a directory/file, /// the newPath isn't allowed to already exist. - /// - /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. - /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. - /// Whether or not renaming succeeded. + /// @param oldPath Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. + /// @param newPath Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. + /// @return Whether or not renaming succeeded. bool FileRename(const std::string& oldPath, const std::string& newPath); - /// /// Moves or renames the directory oldPath to newPath. /// In order to get consistent behavior across Windows and Linux across all 4 combinations of oldPath and newPath being a directory/file, /// the newPath isn't allowed to already exist. - /// - /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. - /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. - /// Whether or not renaming succeeded. + /// @param oldPath Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. + /// @param newPath Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. + /// @return Whether or not renaming succeeded. bool DirectoryRename(const std::string& oldPath, const std::string& newPath); - /// /// Reads a line from a file. - /// - /// File index in the opened files array. - /// Line from file, or empty string on error. + /// @param fileIndex File index in the opened files array. + /// @return Line from file, or empty string on error. std::string FileReadLine(int fileIndex); - /// /// Writes a text line to a file. - /// - /// File index in the opened files array. - /// String to write. + /// @param fileIndex File index in the opened files array. + /// @param line String to write. void FileWriteLine(int fileIndex, const std::string& line); - /// /// Returns true if end of file was reached. - /// - /// File index in the opened files array. - /// Whether or not EOF was reached. + /// @param fileIndex File index in the opened files array. + /// @return Whether or not EOF was reached. bool FileEOF(int fileIndex); #pragma endregion #pragma region Concrete Methods - /// /// Updates the state of this LuaMan. - /// void Update(); - /// /// Asynchronously enforces a GC run to occur. - /// void StartAsyncGarbageCollection(); #pragma endregion - /// /// Clears Script Timings. - /// void ClearScriptTimings(); private: @@ -591,9 +443,7 @@ namespace RTE { BS::multi_future m_GarbageCollectionTask; - /// /// Clears all the member variables of this LuaMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/MenuMan.cpp b/Source/Managers/MenuMan.cpp index 0308254725..a4dc7373df 100644 --- a/Source/Managers/MenuMan.cpp +++ b/Source/Managers/MenuMan.cpp @@ -21,8 +21,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MenuMan::Initialize(bool firstTimeInit) { m_ActiveMenu = ActiveMenu::MenusDisabled; @@ -43,8 +41,6 @@ namespace RTE { g_MetaMan.GetGUI()->Create(m_MenuController.get()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MenuMan::Reinitialize() { g_MetaMan.GetGUI()->Destroy(); @@ -57,8 +53,6 @@ namespace RTE { Initialize(false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MenuMan::SetActiveMenu() { ActiveMenu newActiveMenu = ActiveMenu::MenusDisabled; @@ -108,8 +102,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MenuMan::HandleTransitionIntoMenuLoop() { if (g_MetaMan.GameInProgress()) { if (g_ActivityMan.SkipPauseMenuWhenPausingActivity()) { @@ -135,8 +127,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MenuMan::Update() { m_TitleScreen->Update(); SetActiveMenu(); @@ -173,8 +163,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MenuMan::UpdateMainMenu() const { switch (m_MainMenu->Update()) { case MainMenuGUI::MainMenuUpdateResult::MetaGameStarted: @@ -205,8 +193,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MenuMan::UpdateScenarioMenu() const { switch (m_ScenarioMenu->Update()) { case ScenarioGUI::ScenarioMenuUpdateResult::BackToMain: @@ -228,8 +214,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MenuMan::UpdateMetaGameMenu() const { g_MetaMan.GetGUI()->SetStationOrbitPos(m_TitleScreen->GetStationPos()); g_MetaMan.Update(); @@ -247,8 +231,6 @@ namespace RTE { return g_MetaMan.GetGUI()->QuitProgram(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MenuMan::UpdatePauseMenu() const { switch (m_PauseMenu->Update()) { case PauseMenuGUI::PauseMenuUpdateResult::ActivityResumed: @@ -263,8 +245,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MenuMan::Draw() const { g_FrameMan.ClearBackBuffer32(); diff --git a/Source/Managers/MenuMan.h b/Source/Managers/MenuMan.h index 677b1a6ef2..e2d1ed6c5d 100644 --- a/Source/Managers/MenuMan.h +++ b/Source/Managers/MenuMan.h @@ -15,52 +15,36 @@ namespace RTE { class ScenarioGUI; class PauseMenuGUI; - /// /// The singleton manager responsible for handling all the out-of-game menu screens (main menu, scenario menu, etc.). - /// class MenuMan : public Singleton { public: #pragma region Creation - /// /// Constructor method used to instantiate a MenuMan object in system memory. Initialize() should be called before using the object. - /// MenuMan() = default; - /// /// Makes the MenuMan object ready for use. - /// - /// Whether this is initializing for the first time, meaning the game is booting up, so the loading screen needs to be shown and all module loading should happen. + /// @param firstTimeInit Whether this is initializing for the first time, meaning the game is booting up, so the loading screen needs to be shown and all module loading should happen. void Initialize(bool firstTimeInit = true); - /// /// Reinitializes all the Main Menu GUIs after a resolution change. Must be done otherwise the GUIs retain the original resolution settings and become all screwy. - /// void Reinitialize(); #pragma endregion #pragma region Concrete Methods - /// /// Sets the appropriate TitleScreen transition before entering the menu loop. - /// void HandleTransitionIntoMenuLoop(); - /// /// Updates the MenuMan state. - /// - /// Whether the MenuMan update has reached a state where the menu loop should be exited so the simulation loop can proceed. + /// @return Whether the MenuMan update has reached a state where the menu loop should be exited so the simulation loop can proceed. bool Update(); - /// /// Draws the MenuMan to the screen. - /// void Draw() const; #pragma endregion private: - /// /// Enumeration for the different menu screens that are active based on transition states. - /// enum ActiveMenu { MenusDisabled, MainMenuActive, @@ -81,31 +65,21 @@ namespace RTE { std::unique_ptr m_PauseMenu; //!< The game pause menu screen. #pragma region Updates - /// /// Sets the active menu screen to be enabled, updated and drawn to the screen, besides the title screen which is always active. - /// void SetActiveMenu(); - /// /// Updates the main menu screen and handles the update results. - /// - /// Whether the program was set to be terminated by the user through the main menu screen. + /// @return Whether the program was set to be terminated by the user through the main menu screen. bool UpdateMainMenu() const; - /// /// Updates the scenario menu screen and handles the update results. - /// void UpdateScenarioMenu() const; - /// /// Updates the MetaGame menu screen and handles the update results. - /// - /// Whether the program was set to be terminated by the user through the MetaGame menu screen. + /// @return Whether the program was set to be terminated by the user through the MetaGame menu screen. bool UpdateMetaGameMenu() const; - /// /// Updates the pause menu screen and handles the update results. - /// void UpdatePauseMenu() const; #pragma endregion diff --git a/Source/Managers/MetaMan.cpp b/Source/Managers/MetaMan.cpp index 6a7e47d9a4..bfcf126594 100644 --- a/Source/Managers/MetaMan.cpp +++ b/Source/Managers/MetaMan.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MetaMan.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the MetaMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MetaMan.h" #include "MetaSave.h" #include "PresetMan.h" @@ -29,12 +17,6 @@ namespace RTE { const std::string MetaMan::c_ClassName = "MetaMan"; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MetaMan, effectively - // resetting the members of this abstraction level only. - void MetaMan::Clear() { m_pMetaGUI = 0; m_GameState = NOGAME; @@ -60,11 +42,6 @@ namespace RTE { m_TeamAISkill[team] = Activity::DefaultSkill; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MetaMan object ready for use. - int MetaMan::Initialize() { // if (Serializable::Create() < 0) // return -1; @@ -76,11 +53,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: NewGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Wipes any current and sets up a new game based on a size parameter. - int MetaMan::NewGame(int gameSize) { // Grab a random selection of Scene presets from all available std::list scenePresets; @@ -164,14 +136,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: EndGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Wipes any current metagame and sets things back to as if program start. - // Arguments: None. - // Return value: An error return value signaling success or any particular failure. - // Anything below 0 is an error signal. - int MetaMan::EndGame() { // Reset metagame UI m_pMetaGUI->SetToStartNewGame(); @@ -199,11 +163,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Load - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Load a MetaMan from disk out of the special MetaMan.rte data module - int MetaMan::Load(const MetaSave* pSave) { if (!pSave) return -1; @@ -235,14 +194,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a Reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the Reader's position is untouched. - int MetaMan::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -284,12 +235,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this MetaMan to an output stream for - // later recreation with Create(Reader &reader); - int MetaMan::Save(Writer& writer) const { Serializable::Save(writer); @@ -347,12 +292,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SaveSceneData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the bitmap data of all Scenes of this Metagame that are currently - // loaded. - int MetaMan::SaveSceneData(std::string pathBase) { for (std::vector::const_iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { // Only save the data of revealed scenes that have already had their layers built and saved into files @@ -365,12 +304,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadSceneData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads the bitmap data of all Scenes of this Metagame that have once - // been saved to files. - int MetaMan::LoadSceneData() { for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { // Only load the data of revealed scenes that have already had their layers built and saved into files @@ -383,12 +316,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearSceneData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the bitmap data of all Scenes of this Metagame that have once - // been saved to files. - int MetaMan::ClearSceneData() { for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { if ((*sItr)->ClearData() < 0) @@ -397,11 +324,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MetaMan object. - void MetaMan::Destroy() { delete m_pMetaGUI; @@ -413,11 +335,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlayerTurn - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows which player's turn is now or coming up. - int MetaMan::GetPlayerTurn() const { // Player 1's turn is coming up on this round if (g_MetaMan.m_GameState <= PLAYER1TURN) @@ -430,11 +347,6 @@ namespace RTE { return g_MetaMan.m_GameState - PLAYER1TURN; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMetaPlayerOfInGamePlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the MetaPlayer playing a specific in-game player, if any. - MetaPlayer* MetaMan::GetMetaPlayerOfInGamePlayer(int inGamePlayer) { for (std::vector::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { if ((*itr).GetInGamePlayer() == inGamePlayer) @@ -445,11 +357,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNextSceneOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the next Scene in play that is owned by a specific player. - const Scene* MetaMan::GetNextSceneOfPlayer(int player, const Scene* pStartScene) const { if (m_Scenes.empty()) return 0; @@ -483,12 +390,6 @@ namespace RTE { return pFoundScene; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalBrainCountOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total number of brains that a player has, including ones of - // his that are resident down on sites. - int MetaMan::GetTotalBrainCountOfPlayer(int metaPlayer, bool countPoolsOnly) const { if (metaPlayer <= Players::NoPlayer || metaPlayer >= Players::MaxPlayerCount) return 0; @@ -509,11 +410,6 @@ namespace RTE { return brainCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldCountOfTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total gold funds of all the players of a specific team combined. - int MetaMan::GetGoldCountOfTeam(int team) const { if (team <= Activity::NoTeam || team >= m_TeamCount) return 0; @@ -528,11 +424,6 @@ namespace RTE { return goldTotal; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSceneCountOfTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total number of bases that any specific team owns. - int MetaMan::GetSceneCountOfTeam(int team) const { if (team <= Activity::NoTeam || team >= m_TeamCount) return 0; @@ -548,12 +439,6 @@ namespace RTE { return sceneCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalBrainCountOfTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total number of brains that a team has, including ones that - // are resident down on sites. - int MetaMan::GetTotalBrainCountOfTeam(int team, bool countPoolsOnly) const { if (team <= Activity::NoTeam || team >= m_TeamCount) return 0; @@ -569,12 +454,6 @@ namespace RTE { return brainCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnlyTeamWithAnyBrainPoolLeft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates which team, if any, is the only one left with brains in its - // pool. - int MetaMan::OnlyTeamWithAnyBrainPoolLeft() { // See if only one team remains with any brains int brainTeamCount = 0; @@ -657,12 +536,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: NoBrainsLeftInAnyPool - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether there are no brains left in any active player's pool - // at all. This does NOT count deployed brain in bases. - bool MetaMan::NoBrainsLeftInAnyPool() { // Go through all players and check each for any brains in any pool for (std::vector::iterator mpItr = m_Players.begin(); mpItr != m_Players.end(); ++mpItr) { @@ -672,12 +545,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WhichTeamIsLeading - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates which single team has the most owned bases, and if there's a - // tie between two teams, total owned gold funds is used as a tiebreaker. - int MetaMan::WhichTeamIsLeading() { int leaderTeam = Activity::NoTeam; bool tiedTeams[Activity::MaxTeamCount]; @@ -733,11 +600,6 @@ namespace RTE { return leaderTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSceneIncomeOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total income from all scenes owned by a specific player. - float MetaMan::GetSceneIncomeOfPlayer(int metaPlayer) const { float totalIncome = 0; @@ -749,11 +611,6 @@ namespace RTE { return totalIncome; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBudgetedRatioOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the ratio of funds already allocated to budgets of this player. - float MetaMan::GetBudgetedRatioOfPlayer(int metaPlayer, const Scene* pException, bool includeOffensive, bool includeDefensive) const { float totalAllocated = 0; @@ -773,12 +630,6 @@ namespace RTE { return totalAllocated / m_Players[metaPlayer].GetFunds(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSuspend - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Suspends or unsuspends the game so exclusive GUIs and menus can be - // shown - void MetaMan::SetSuspend(bool suspend) { if (suspend && !m_Suspended) { m_Suspended = true; @@ -789,11 +640,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WhichTeamOwnsAllSites - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks whether one team has ownership of all revealed sites. - int MetaMan::WhichTeamOwnsAllSites() { int owner = Activity::NoTeam; for (std::vector::iterator sItr = m_Scenes.begin(); sItr != m_Scenes.end(); ++sItr) { @@ -817,11 +663,6 @@ namespace RTE { return owner; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsGameOver - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks for game over condition - bool MetaMan::IsGameOver() { // This is the old condition of all sites being conquered // if (m_RevealedScenes >= m_Scenes.size() && WhichTeamOwnsAllSites() != Activity::NoTeam) @@ -836,11 +677,6 @@ namespace RTE { return false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TotalScenePresets - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Yields a set of ALL eligible Scene presets for a new game. - int MetaMan::TotalScenePresets(std::list* pScenes) { int totalCount = 0; // Get the list of ALL read-in Scene presets @@ -884,11 +720,6 @@ namespace RTE { return totalCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SelectScenePresets - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Yields a set of randomly selected Scene presets for a new game. - int MetaMan::SelectScenePresets(int gameSize, std::list* pSelected) { // Get the list of ALL eligible read-in Scene presets std::list scenePresets; @@ -922,11 +753,6 @@ namespace RTE { return gameSize; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearActivities - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears out all the lined-up activities for the current round. - void MetaMan::ClearActivities() { for (std::vector::iterator aItr = m_RoundOffensives.begin(); aItr != m_RoundOffensives.end(); ++aItr) delete (*aItr); @@ -934,11 +760,6 @@ namespace RTE { m_CurrentOffensive = 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AIPlayerTurn - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does all the things an AI player needs to do during his turn. - void MetaMan::AIPlayerTurn(int metaPlayer) { if (metaPlayer < 0 || metaPlayer >= m_Players.size()) return; @@ -1068,11 +889,6 @@ namespace RTE { // TODO: Pay for and schedule to scan a random unfriendly site to keep things fair } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this. Supposed to be done every frame before drawing. - void MetaMan::Update() { m_pMetaGUI->Update(); @@ -1316,12 +1132,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this MetaMan's current graphical representation to a - // BITMAP of choice. This includes all game-related graphics. - void MetaMan::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { /* GUIFont *pLargeFont = g_FrameMan.GetLargeFont(); diff --git a/Source/Managers/MetaMan.h b/Source/Managers/MetaMan.h index 57cd1eabf2..360c5eb7d0 100644 --- a/Source/Managers/MetaMan.h +++ b/Source/Managers/MetaMan.h @@ -1,18 +1,11 @@ #ifndef _RTEMETAMAN_ #define _RTEMETAMAN_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MetaMan.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the MetaMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the MetaMan class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "RTETools.h" #include "Singleton.h" #define g_MetaMan MetaMan::Instance() @@ -39,20 +32,12 @@ namespace RTE { class MetaSave; class Scene; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: MetaMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The singleton manager of the Metagame of Cortex Command, ie the - // games played out in the campaign screen. - // Parent(s): Singleton, serializable - // Class history: 10/10/2009 MetaMan created. - + /// The singleton manager of the Metagame of Cortex Command, ie the + /// games played out in the campaign screen. class MetaMan : public Singleton, public Serializable { friend struct ManagerLuaBindings; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: friend class MetagameGUI; friend class MetaSave; @@ -76,455 +61,242 @@ namespace RTE { GAMEOVER }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MetaMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a MetaMan object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a MetaMan object in system + /// memory. Create() should be called before using the object. MetaMan() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~MetaMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a MetaMan object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a MetaMan object before deletion + /// from system memory. ~MetaMan() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MetaMan object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MetaMan object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Initialize(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: NewGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Wipes any current and sets up a new game based on a size parameter. - // Arguments: The size of the new Metagame, which will affect how - // many Scenes/Sites will ultimately be used. - // Return value: An error return value signaling success or any particular failure. - // Anything below 0 is an error signal. - + /// Wipes any current and sets up a new game based on a size parameter. + /// @param gameSize The size of the new Metagame, which will affect how (default: 3) + /// many Scenes/Sites will ultimately be used. + /// @return An error return value signaling success or any particular failure. + /// Anything below 0 is an error signal. int NewGame(int gameSize = 3); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EndGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Wipes any current metagame and sets things back to as if program start. - // Arguments: None. - // Return value: An error return value signaling success or any particular failure. - // Anything below 0 is an error signal. - + /// Wipes any current metagame and sets things back to as if program start. + /// @return An error return value signaling success or any particular failure. + /// Anything below 0 is an error signal. int EndGame(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Load - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Load a Metagame from disk out of the special Metagame.rte data module - // Arguments: The MetaSave object to load from - Ownership Is Not Transferred! - // Return value: An error return value signaling success or any particular failure. - // Anything below 0 is an error signal. - + /// Load a Metagame from disk out of the special Metagame.rte data module + /// @param pSave The MetaSave object to load from - Ownership Is Not Transferred! + /// @return An error return value signaling success or any particular failure. + /// Anything below 0 is an error signal. int Load(const MetaSave* pSave); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SaveSceneData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the bitmap data of all Scenes of this Metagame that are currently - // loaded. - // Arguments: The filepath base to the where to save the Bitmap data. This means - // everything up to and including the unique name of the game. - // Return value: An error return value signaling success or any particular failure. - // Anything below 0 is an error signal. - + /// Saves the bitmap data of all Scenes of this Metagame that are currently + /// loaded. + /// @param pathBase The filepath base to the where to save the Bitmap data. This means + /// everything up to and including the unique name of the game. + /// @return An error return value signaling success or any particular failure. + /// Anything below 0 is an error signal. int SaveSceneData(std::string pathBase); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadSceneData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads the bitmap data of all Scenes of this Metagame that have once - // been saved to files. - // Arguments: None. - // Return value: An error return value signaling success or any particular failure. - // Anything below 0 is an error signal. - + /// Loads the bitmap data of all Scenes of this Metagame that have once + /// been saved to files. + /// @return An error return value signaling success or any particular failure. + /// Anything below 0 is an error signal. int LoadSceneData(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearSceneData - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the bitmap data of all Scenes of this Metagame that have once - // been saved to files. - // Arguments: None. - // Return value: An error return value signaling success or any particular failure. - // Anything below 0 is an error signal. - + /// Clears the bitmap data of all Scenes of this Metagame that have once + /// been saved to files. + /// @return An error return value signaling success or any particular failure. + /// Anything below 0 is an error signal. int ClearSceneData(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire MetaMan, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire MetaMan, including its inherited members, to + /// their default settings or values. void Reset() override { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MetaMan object. - // Return value: None. - + /// Destroys and resets (through Clear()) the MetaMan object. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetGameName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the name of the currently played Metagame. It's what's used when - // saving to disk. - // Arguments: The Metagame's name. - // Return value: None. - + /// Sets the name of the currently played Metagame. It's what's used when + /// saving to disk. + /// @param newName The Metagame's name. void SetGameName(std::string newName) { m_GameName = newName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGameName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the name of the currently played Metagame. It's what's used when - // saving to disk. - // Arguments: None. - // Return value: The name of the current metagame. - + /// Gets the name of the currently played Metagame. It's what's used when + /// saving to disk. + /// @return The name of the current metagame. std::string GetGameName() const { return m_GameName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the GUI controller of this Metagame. - // Arguments: None. - // Return value: The GUI controller of the metagame. - + /// Gets the GUI controller of this Metagame. + /// @return The GUI controller of the metagame. MetagameGUI* GetGUI() { return m_pMetaGUI; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlayerTurn - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows which player's turn is now or coming up. - // Arguments: None. - // Return value: The player who is currently doing his turn, or coming up next in an - // intermediate phase. - + /// Shows which player's turn is now or coming up. + /// @return The player who is currently doing his turn, or coming up next in an + /// intermediate phase. int GetPlayerTurn() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlayerCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets current number of MetaPlayers - // Arguments: None - // Return value: The number of meta players in the current game. - + /// Gets current number of MetaPlayers + /// @return The number of meta players in the current game. int GetPlayerCount() const { return m_Players.size(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTeamOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the designated team of a specific player - // Arguments: Which player. - // Return value: The team of that player. - + /// Gets the designated team of a specific player + /// @param metaPlayer Which player. + /// @return The team of that player. int GetTeamOfPlayer(int metaPlayer) const { return metaPlayer >= Players::PlayerOne && metaPlayer < m_Players.size() ? m_Players[metaPlayer].GetTeam() : Activity::NoTeam; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the specified MetaPlayer - // Arguments: Which player. - // Return value: The requested MetaPlayer - + /// Gets the specified MetaPlayer + /// @param metaPlayer Which player. + /// @return The requested MetaPlayer MetaPlayer* GetPlayer(int metaPlayer) { return (metaPlayer >= Players::PlayerOne && metaPlayer < m_Players.size()) ? &(m_Players[metaPlayer]) : 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMetaPlayerOfInGamePlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the MetaPlayer playing a specific in-game player, if any. - // Arguments: Which in-game player to translate into a metaplayer. - // Return value: The requested MetaPlayer, if any is playing that in-game player. If not - // 0 is returned. - + /// Gets the MetaPlayer playing a specific in-game player, if any. + /// @param inGamePlayer Which in-game player to translate into a metaplayer. + /// @return The requested MetaPlayer, if any is playing that in-game player. If not + /// 0 is returned. MetaPlayer* GetMetaPlayerOfInGamePlayer(int inGamePlayer); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTeamIcon - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the flag Icon of a specific team - // Arguments: The team to get the Team icon of. - // Return value: A reference to the Icon. - + /// Gets the flag Icon of a specific team + /// @param team The team to get the Team icon of. + /// @return A reference to the Icon. Icon& GetTeamIcon(int team) { return m_TeamIcons[team]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNextSceneOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the next Scene in play that is owned by a specific player. - // Arguments: The player to get the next owned Scene of. - // The Scene to start searching from in the current roster of Scenes, OWNERSHIP IS NOT TRANSFERRED! - // Return value: A pointer to the next Scene found in the sequence. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the next Scene in play that is owned by a specific player. + /// @param metaPlayer The player to get the next owned Scene of. + /// @param pScene The Scene to start searching from in the current roster of Scenes, OWNERSHIP IS NOT TRANSFERRED! (default: 0) + /// @return A pointer to the next Scene found in the sequence. OWNERSHIP IS NOT TRANSFERRED! const Scene* GetNextSceneOfPlayer(int metaPlayer, const Scene* pScene = 0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalBrainCountOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total number of brains that a player has, including ones of - // his that are resident down on sites. - // Arguments: The metagame player to get the total brain count from. - // Whether to only count the brains in the pools, or to also include all - // resident brains as well. - // Return value: The total number of brains that belong to the metagame player. - + /// Gets the total number of brains that a player has, including ones of + /// his that are resident down on sites. + /// @param metaPlayer The metagame player to get the total brain count from. + /// @param countPoolsOnly Whether to only count the brains in the pools, or to also include all (default: false) + /// resident brains as well. + /// @return The total number of brains that belong to the metagame player. int GetTotalBrainCountOfPlayer(int metaPlayer, bool countPoolsOnly = false) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGoldCountOfTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total gold funds of all the players of a specific team combined. - // Arguments: The metagame team to get the total gold funds of. - // Return value: The total amount of ounces of gold this team has. - + /// Gets the total gold funds of all the players of a specific team combined. + /// @param team The metagame team to get the total gold funds of. + /// @return The total amount of ounces of gold this team has. int GetGoldCountOfTeam(int team) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSceneCountOfTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total number of bases that any specific team owns. - // Arguments: The team to get the scene/site ownership count of. - // Return value: The count of scenes owned by this team. - + /// Gets the total number of bases that any specific team owns. + /// @param team The team to get the scene/site ownership count of. + /// @return The count of scenes owned by this team. int GetSceneCountOfTeam(int team) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalBrainCountOfTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total number of brains that a team has, including ones that - // are resident down on sites. - // Arguments: The metagame team to get the total brain count from. - // Whether to only count the brains in the pools, or to also include all - // resident brains as well. - // Return value: The total number of brains that belong to the metagame team. - + /// Gets the total number of brains that a team has, including ones that + /// are resident down on sites. + /// @param team The metagame team to get the total brain count from. + /// @param countPoolsOnly Whether to only count the brains in the pools, or to also include all (default: false) + /// resident brains as well. + /// @return The total number of brains that belong to the metagame team. int GetTotalBrainCountOfTeam(int team, bool countPoolsOnly = false) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OnlyTeamWithAnyBrainPoolLeft - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates which team, if any, is the only one left with brains in its - // pool. - // Arguments: None. - // Return value: Which team, if any, is the sole remaining with any brains left in its - // players' brain pools. - + /// Indicates which team, if any, is the only one left with brains in its + /// pool. + /// @return Which team, if any, is the sole remaining with any brains left in its + /// players' brain pools. int OnlyTeamWithAnyBrainPoolLeft(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: NoBrainsLeftInAnyPool - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether there are no brains left in any active player's pool - // at all. This does NOT count deployed brain in bases. - // Arguments: None. - // Return value: Whether there are no brains left in any player's brain pool. - + /// Indicates whether there are no brains left in any active player's pool + /// at all. This does NOT count deployed brain in bases. + /// @return Whether there are no brains left in any player's brain pool. bool NoBrainsLeftInAnyPool(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WhichTeamIsLeading - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates which single team has the most owned bases, and if there's a - // tie between two teams, total owned gold funds is used as a tiebreaker. - // Arguments: None. - // Return value: Which team is currently in the lead. - + /// Indicates which single team has the most owned bases, and if there's a + /// tie between two teams, total owned gold funds is used as a tiebreaker. + /// @return Which team is currently in the lead. int WhichTeamIsLeading(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSceneIncomeOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total income from all scenes owned by a specific metaPlayer. - // Arguments: The metagame player to get the total scene income from. - // Return value: The amount of income, in oz, the player made this round from its scenes. - + /// Gets the total income from all scenes owned by a specific metaPlayer. + /// @param metaPlayer The metagame player to get the total scene income from. + /// @return The amount of income, in oz, the player made this round from its scenes. float GetSceneIncomeOfPlayer(int metaPlayer) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetBudgetedRatioOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the ratio of funds already allocated to budgets of this player. - // Arguments: The metagame player to get the budget ratio of. - // A scene to exclude from the tally, if any. - // Whether to count the money allocated for offensive action. - // Whether to count the money allocated for defensive actions. - // Return value: The amount, in ratio, that this player already has allocated. - + /// Gets the ratio of funds already allocated to budgets of this player. + /// @param metaPlayer The metagame player to get the budget ratio of. + /// @param pException A scene to exclude from the tally, if any. (default: 0) + /// @param includeOffensive Whether to count the money allocated for offensive action. (default: true) + /// @param includeDefensive Whether to count the money allocated for defensive actions. (default: true) + /// @return The amount, in ratio, that this player already has allocated. float GetBudgetedRatioOfPlayer(int metaPlayer, const Scene* pException = 0, bool includeOffensive = true, bool includeDefensive = true) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRemainingFundsOfPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the count of funds still unbudgeted and available of a player. - // Arguments: The metagame player to get the unallocated funds of. - // A scene to exclude from the tally, if any. - // Whether to count the money allocated for offensive action as remaining. - // Whether to count the money allocated for defensive action as remaining. - // Return value: The amount, in oz, that this player unallocated and unused this turn. - + /// Gets the count of funds still unbudgeted and available of a player. + /// @param metaPlayer The metagame player to get the unallocated funds of. + /// @param pException A scene to exclude from the tally, if any. (default: 0) + /// @param deductOffensive Whether to count the money allocated for offensive action as remaining. (default: false) + /// @param deductDefensive Whether to count the money allocated for defensive action as remaining. (default: false) const { return m_Players[metaPlayer].GetFunds() - m_Players[metaPlayer].GetFunds() * GetBudgetedRatioOfPlayer(metaPlayer) + /// @return The amount, in oz, that this player unallocated and unused this turn. float GetRemainingFundsOfPlayer(int metaPlayer, const Scene* pException = 0, bool deductOffensive = false, bool deductDefensive = false) const { return m_Players[metaPlayer].GetFunds() - m_Players[metaPlayer].GetFunds() * GetBudgetedRatioOfPlayer(metaPlayer, pException, !deductOffensive, !deductDefensive); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GameInProgress - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether a game is currently in progress - // Arguments: None. - // Return value: Whether a game is going or not. - + /// Shows whether a game is currently in progress + /// @return Whether a game is going or not. bool GameInProgress() { return m_GameState >= GAMEINTRO && m_GameState <= ENDROUND; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsSuspended - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether game is suspended or not. - // Arguments: None. - // Return value: Whether suspended or not. - + /// Shows whether game is suspended or not. + /// @return Whether suspended or not. bool IsSuspended() { return m_Suspended; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSuspend - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Suspends or unsuspends the game so exclusive GUIs and menus can be - // shown. - // Arguments: Whether to suspend or not. - // Return value: None. - + /// Suspends or unsuspends the game so exclusive GUIs and menus can be + /// shown. + /// @param suspend Whether to suspend or not. void SetSuspend(bool suspend); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsActivePlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks wheter a certain player index is valid for the current game - // Arguments: None. - // Return value: Whether the player index passed in is active for the current game. - + /// Checks wheter a certain player index is valid for the current game + /// @return Whether the player index passed in is active for the current game. bool IsActivePlayer(int metaPlayer) { return metaPlayer >= Players::PlayerOne && metaPlayer < m_Players.size(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsActiveTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks wheter a certain team index is valid for the current game - // Arguments: None. - // Return value: Whether the team index passed in is active for the current game. - + /// Checks wheter a certain team index is valid for the current game + /// @return Whether the team index passed in is active for the current game. bool IsActiveTeam(int team) { return team >= Activity::TeamOne && team < m_TeamCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WhichTeamOwnsAllSites - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks whether one team has ownership of all revealed sites. - // Arguments: None. - // Return value: Which team has all sites, if any. If not NoTeam is returned. - + /// Checks whether one team has ownership of all revealed sites. + /// @return Which team has all sites, if any. If not NoTeam is returned. int WhichTeamOwnsAllSites(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsGameOver - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks for game over condition - // Arguments: None. - // Return value: Whether the game over conditions have been met - + /// Checks for game over condition + /// @return Whether the game over conditions have been met bool IsGameOver(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GameIsSaved - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether the game has been saved and no data loss will result - // if program is quit right now. - // Arguments: None. - // Return value: Whether the game is saved. - + /// Shows whether the game has been saved and no data loss will result + /// if program is quit right now. + /// @return Whether the game is saved. bool GameIsSaved() { return m_GameSaved || m_GameState <= NOGAME || m_GameState >= GAMEOVER; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TotalScenePresets - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Yields a set of ALL eligible Scene presets for a new game. - // Arguments: The list to fill with all the eligible presets. If no list is passed - // it will be ignored. Presets returned in list are NOT OWNED there. - // Return value: The count of total number preset scenes eligible for gameplay. - + /// Yields a set of ALL eligible Scene presets for a new game. + /// @param pScenes The list to fill with all the eligible presets. If no list is passed (default: 0) + /// it will be ignored. Presets returned in list are NOT OWNED there. + /// @return The count of total number preset scenes eligible for gameplay. int TotalScenePresets(std::list* pScenes = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SelectScenePresets - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Yields a set of randomly selected Scene presets for a new game. - // Arguments: The size of the set. - // The list to fill with the selected presets, depending on currently - // set player numbers and loaded eligible scenes. If no list is passed - // it will be ignored. Presets returned in list are NOT OWNED there. - // Return value: The count of selected preset scenes. - + /// Yields a set of randomly selected Scene presets for a new game. + /// @param gameSize The size of the set. + /// @param pSelected The list to fill with the selected presets, depending on currently (default: 0) + /// set player numbers and loaded eligible scenes. If no list is passed + /// it will be ignored. Presets returned in list are NOT OWNED there. + /// @return The count of selected preset scenes. int SelectScenePresets(int gameSize, std::list* pSelected = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearActivities - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears out all the lined-up activities for the current round. - // Arguments: None. - // Return value: None. - + /// Clears out all the lined-up activities for the current round. void ClearActivities(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AIPlayerTurn - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Does all the things an AI metaPlayer needs to do during his turn. - // Arguments: Which AI metaPlayer we're going to process. - // Return value: None. - + /// Does all the things an AI metaPlayer needs to do during his turn. + /// @param metaPlayer Which AI metaPlayer we're going to process. void AIPlayerTurn(int metaPlayer); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this and the current Metagame. Supposed to be - // done every frame before drawing. - // Arguments: None. - // Return value: None. - + /// Updates the state of this and the current Metagame. Supposed to be + /// done every frame before drawing. void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this MetaMan's current graphical representation to a BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this MetaMan's current graphical representation to a BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()); - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // GUI controller, owned MetagameGUI* m_pMetaGUI; @@ -569,20 +341,12 @@ namespace RTE { // Timer for measuring how long each phase has gone for Timer m_PhaseTimer; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MetaMan, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this MetaMan, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/MovableMan.cpp b/Source/Managers/MovableMan.cpp index 6172abddb2..c93d6af18d 100644 --- a/Source/Managers/MovableMan.cpp +++ b/Source/Managers/MovableMan.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MovableMan.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the MovableMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MovableMan.h" #include "PrimitiveMan.h" @@ -53,12 +41,6 @@ namespace RTE { bool operator()(MovableObject* pRhs, MovableObject* pLhs) { return pRhs->GetPos().m_X < pLhs->GetPos().m_X; } }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MovableMan, effectively - // resetting the members of this abstraction level only. - void MovableMan::Clear() { m_Actors.clear(); m_ContiguousActorIDs.clear(); @@ -87,11 +69,6 @@ namespace RTE { m_MOSubtractionEnabled = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MovableMan object ready for use. - int MovableMan::Initialize() { // TODO: Increase this number, or maybe only for certain classes? Entity::ClassInfo::FillAllPools(); @@ -99,14 +76,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: ReadProperty - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a property value from a reader stream. If the name isn't - // recognized by this class, then ReadProperty of the parent class - // is called. If the property isn't recognized by any of the base classes, - // false is returned, and the reader's position is untouched. - int MovableMan::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -119,12 +88,6 @@ namespace RTE { EndPropertyList; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Save - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the complete state of this MovableMan with a Writer for - // later recreation with Create(Reader &reader); - int MovableMan::Save(Writer& writer) const { Serializable::Save(writer); @@ -139,11 +102,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MovableMan object. - void MovableMan::Destroy() { for (std::deque::iterator it1 = m_Actors.begin(); it1 != m_Actors.end(); ++it1) delete (*it1); @@ -155,12 +113,6 @@ namespace RTE { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMOFromID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a MO from its MOID. Note that MOID's are only valid during the - // same frame as they were assigned to the MOs! - MovableObject* MovableMan::GetMOFromID(MOID whichID) { if (whichID != g_NoMOID && whichID != 0 && whichID < m_MOIDIndex.size()) { return m_MOIDIndex[whichID]; @@ -168,8 +120,6 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MOID MovableMan::GetMOIDPixel(int pixelX, int pixelY, const std::vector& moidList) { // Note - We loop through the MOs in reverse to make sure that the topmost (last drawn) MO that overlaps the specified coordinates is the one returned. for (auto itr = moidList.rbegin(), itrEnd = moidList.rend(); itr < itrEnd; ++itr) { @@ -191,15 +141,6 @@ namespace RTE { return g_NoMOID; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RegisterObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registers an object in a global Map collection so it could be found later with FindObjectByUniqueId - // Arguments: MO to register. - // Return value: None. - void MovableMan::RegisterObject(MovableObject* mo) { if (!mo) { return; @@ -209,13 +150,6 @@ namespace RTE { m_KnownObjects[mo->GetUniqueID()] = mo; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UnregisterObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes an object from the global lookup collection - // Arguments: MO to remove. - // Return value: None. - void MovableMan::UnregisterObject(MovableObject* mo) { if (!mo) { return; @@ -225,28 +159,18 @@ namespace RTE { m_KnownObjects.erase(mo->GetUniqueID()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector* MovableMan::GetMOsInBox(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const { std::vector* vectorForLua = new std::vector(); *vectorForLua = std::move(g_SceneMan.GetMOIDGrid().GetMOsInBox(box, ignoreTeam, getsHitByMOsOnly)); return vectorForLua; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector* MovableMan::GetMOsInRadius(const Vector& centre, float radius, int ignoreTeam, bool getsHitByMOsOnly) const { std::vector* vectorForLua = new std::vector(); *vectorForLua = std::move(g_SceneMan.GetMOIDGrid().GetMOsInRadius(centre, radius, ignoreTeam, getsHitByMOsOnly)); return vectorForLua; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PurgeAllMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears out all MovableObject:s out of this. Effectively empties the world - // of anything moving, without resetting all of this' settings. - void MovableMan::PurgeAllMOs() { for (std::deque::iterator itr = m_Actors.begin(); itr != m_Actors.end(); ++itr) { (*itr)->DestroyScriptState(); @@ -292,12 +216,6 @@ namespace RTE { // m_KnownObjects.clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNextActorInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the first Actor in the internal Actor list that is - // of a specifc group, alternatively the first one AFTER a specific actor! - Actor* MovableMan::GetNextActorInGroup(std::string group, Actor* pAfterThis) { if (group.empty()) return 0; @@ -344,12 +262,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPrevActorInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the last Actor in the internal Actor list that is - // of a specifc group, alternatively the last one BEFORE a specific actor! - Actor* MovableMan::GetPrevActorInGroup(std::string group, Actor* pBeforeThis) { if (group.empty()) return 0; @@ -396,12 +308,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNextTeamActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the first Actor in the internal Actor list that is - // of a specifc team, alternatively the first one AFTER a specific actor! - Actor* MovableMan::GetNextTeamActor(int team, Actor* pAfterThis) { if (team < Activity::TeamOne || team >= Activity::MaxTeamCount || m_ActorRoster[team].empty()) return 0; @@ -477,12 +383,6 @@ namespace RTE { return *aIt; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPrevTeamActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the last Actor in the internal Actor list that is - // of a specifc team, alternatively the last one BEFORE a specific actor! - Actor* MovableMan::GetPrevTeamActor(int team, Actor* pBeforeThis) { if (team < Activity::TeamOne || team >= Activity::MaxTeamCount || m_Actors.empty() || m_ActorRoster[team].empty()) return 0; @@ -558,12 +458,6 @@ namespace RTE { return *aIt; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetClosestTeamActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to an Actor in the internal Actor list that is of a - // specifc team and closest to a specific scene point. - Actor* MovableMan::GetClosestTeamActor(int team, int player, const Vector& scenePoint, int maxRadius, Vector& getDistance, bool onlyPlayerControllableActors, const Actor* excludeThis) { if (team < Activity::NoTeam || team >= Activity::MaxTeamCount || m_Actors.empty() || m_ActorRoster[team].empty()) return 0; @@ -610,12 +504,6 @@ namespace RTE { return pClosestActor; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetClosestEnemyActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to an Actor in the internal Actor list that is is not of - // the specified team and closest to a specific scene point. - Actor* MovableMan::GetClosestEnemyActor(int team, const Vector& scenePoint, int maxRadius, Vector& getDistance) { if (team < Activity::NoTeam || team >= Activity::MaxTeamCount || m_Actors.empty()) return 0; @@ -643,12 +531,6 @@ namespace RTE { return pClosestActor; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetClosestActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to an Actor in the internal Actor list that is closest - // to a specific scene point. - Actor* MovableMan::GetClosestActor(const Vector& scenePoint, int maxRadius, Vector& getDistance, const Actor* pExcludeThis) { if (m_Actors.empty()) return 0; @@ -676,12 +558,6 @@ namespace RTE { return pClosestActor; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetClosestBrainActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the brain actor of a specific team that is closest to - // a scene point. - Actor* MovableMan::GetClosestBrainActor(int team, const Vector& scenePoint) const { if (team < Activity::TeamOne || team >= Activity::MaxTeamCount || m_ActorRoster[team].empty()) return 0; @@ -706,12 +582,6 @@ namespace RTE { return pClosestBrain; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetClosestOtherBrainActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the brain actor NOT of a specific team that is closest - // to a scene point. - Actor* MovableMan::GetClosestOtherBrainActor(int notOfTeam, const Vector& scenePoint) const { if (notOfTeam < Activity::TeamOne || notOfTeam >= Activity::MaxTeamCount || m_Actors.empty()) return 0; @@ -735,12 +605,6 @@ namespace RTE { return pClosestBrain; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetUnassignedBrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the brain actor of a specific team. - // Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. - Actor* MovableMan::GetUnassignedBrain(int team) const { if (/*m_Actors.empty() || */ m_ActorRoster[team].empty()) return 0; @@ -762,8 +626,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MovableMan::AddMO(MovableObject* movableObjectToAdd) { if (!movableObjectToAdd) { return false; @@ -781,8 +643,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::AddActor(Actor* actorToAdd) { if (actorToAdd) { actorToAdd->SetAsAddedToMovableMan(); @@ -813,8 +673,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::AddItem(HeldDevice* itemToAdd) { if (itemToAdd) { g_ActivityMan.GetActivity()->ForceSetTeamAsActive(itemToAdd->GetTeam()); @@ -838,8 +696,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::AddParticle(MovableObject* particleToAdd) { if (particleToAdd) { g_ActivityMan.GetActivity()->ForceSetTeamAsActive(particleToAdd->GetTeam()); @@ -868,13 +724,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes an Actor from the internal list of MO:s. After the Actor is - // removed, ownership is effectively released and transferred to whatever - // client called this method. - Actor* MovableMan::RemoveActor(MovableObject* pActorToRem) { Actor* removed = nullptr; @@ -906,13 +755,6 @@ namespace RTE { return removed; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a pickup-able MovableObject item from the internal list of - // MO:s. After the item is removed, ownership is effectively released and - // transferred to whatever client called this method. - MovableObject* MovableMan::RemoveItem(MovableObject* pItemToRem) { MovableObject* removed = nullptr; @@ -943,13 +785,6 @@ namespace RTE { return removed; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveParticle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a MovableObject from the internal list of MO:s. After the - // MO is removed, ownership is effectively released and transferred to - // whatever client called this method. - MovableObject* MovableMan::RemoveParticle(MovableObject* pMOToRem) { MovableObject* removed = nullptr; @@ -980,13 +815,6 @@ namespace RTE { return removed; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddActorToTeamRoster - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds actor to internal team roster - // Arguments: Pointer to actor - // Return value: None. - void MovableMan::AddActorToTeamRoster(Actor* pActorToAdd) { if (!pActorToAdd) { return; @@ -1004,13 +832,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveActorToTeamRoster - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes actor from internal team roster - // Arguments: Pointer to actor - // Return value: None. - void MovableMan::RemoveActorFromTeamRoster(Actor* pActorToRem) { if (!pActorToRem) { return; @@ -1025,11 +846,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeActorTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes actor team and updates team rosters. - void MovableMan::ChangeActorTeam(Actor* pActor, int team) { if (!pActor) { return; @@ -1055,13 +871,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ValidateMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through and checks that all MOID's have valid MO pointers - // associated with them. This shuold only be used for testing, as it will - // crash the app if validation fails. - bool MovableMan::ValidateMOIDs() { #ifdef DEBUG_BUILD for (const MovableObject* mo: m_MOIDIndex) { @@ -1071,12 +880,6 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ValidMO - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MovableObject pointer points to an - // MO that's currently active in the simulation, and kept by this MovableMan. - bool MovableMan::ValidMO(const MovableObject* pMOToCheck) { bool exists = m_ValidActors.find(pMOToCheck) != m_ValidActors.end() || m_ValidItems.find(pMOToCheck) != m_ValidItems.end() || @@ -1085,42 +888,18 @@ namespace RTE { return pMOToCheck && exists; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MovableObject is an active Actor kept - // by this MovableMan or not. - bool MovableMan::IsActor(const MovableObject* pMOToCheck) { return pMOToCheck && m_ValidActors.find(pMOToCheck) != m_ValidActors.end(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MovableObject is an active Item kept - // by this MovableMan or not. - bool MovableMan::IsDevice(const MovableObject* pMOToCheck) { return pMOToCheck && m_ValidItems.find(pMOToCheck) != m_ValidItems.end(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsParticle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MovableObject is an active Item kept - // by this MovableMan or not. - bool MovableMan::IsParticle(const MovableObject* pMOToCheck) { return pMOToCheck && m_ValidParticles.find(pMOToCheck) != m_ValidParticles.end(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsOfActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MOID is that of an MO which either is - // or is parented to an active Actor by this MovableMan, or not. - bool MovableMan::IsOfActor(MOID checkMOID) { if (checkMOID == g_NoMOID) return false; @@ -1151,8 +930,6 @@ namespace RTE { return found; } - ////////////////////////////////////////////////////////////////////////////////////////// - int MovableMan::GetContiguousActorID(const Actor* actor) const { auto itr = m_ContiguousActorIDs.find(actor); if (itr == m_ContiguousActorIDs.end()) { @@ -1162,11 +939,6 @@ namespace RTE { return itr->second; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRootMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Produces the root MOID of the MOID of a potential child MO to another MO. - MOID MovableMan::GetRootMOID(MOID checkMOID) { MovableObject* pMO = GetMOFromID(checkMOID); if (pMO) @@ -1175,13 +947,6 @@ namespace RTE { return g_NoMOID; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveMO - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a MovableObject from the any and all internal lists of MO:s. - // After the MO is removed, ownership is effectively released and - // transferred to whatever client called this method. - bool MovableMan::RemoveMO(MovableObject* pMOToRem) { if (pMOToRem) { if (RemoveActor(pMOToRem)) @@ -1195,8 +960,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableMan::KillAllTeamActors(int teamToKill) const { int killCount = 0; @@ -1217,8 +980,6 @@ namespace RTE { return killCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableMan::KillAllEnemyActors(int teamNotToKill) const { int killCount = 0; @@ -1239,8 +1000,6 @@ namespace RTE { return killCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableMan::GetAllActors(bool transferOwnership, std::list& actorList, int onlyTeam, bool noBrains) { int addedCount = 0; @@ -1283,8 +1042,6 @@ namespace RTE { return addedCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableMan::GetAllItems(bool transferOwnership, std::list& itemList) { int addedCount = 0; @@ -1310,8 +1067,6 @@ namespace RTE { return addedCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableMan::GetAllParticles(bool transferOwnership, std::list& particleList) { int addedCount = 0; @@ -1337,8 +1092,6 @@ namespace RTE { return addedCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int MovableMan::GetTeamMOIDCount(int team) const { if (team > Activity::NoTeam && team < Activity::MaxTeamCount) return m_TeamMOIDCount[team]; @@ -1346,8 +1099,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::OpenAllDoors(bool open, int team) const { for (std::deque actorDeque: {m_Actors, m_AddedActors}) { for (Actor* actor: actorDeque) { @@ -1367,8 +1118,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: Completely tear out and delete this. // It shouldn't belong to MovableMan, instead it probably ought to be on the pathfinder. On that note, pathfinders shouldn't be part of the scene! // AIMan? PathingMan? Something like that. Ideally, we completely tear out this hack, and allow for doors in a completely different way. @@ -1382,19 +1131,11 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::RegisterAlarmEvent(const AlarmEvent& newEvent) { std::lock_guard lock(m_AddedAlarmEventsMutex); m_AddedAlarmEvents.push_back(newEvent); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RedrawOverlappingMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces all objects potnetially overlapping a specific MO to re-draw - // this MOID representations onto the MOID bitmap. - void MovableMan::RedrawOverlappingMOIDs(MovableObject* pOverlapsThis) { for (std::deque::iterator aIt = m_Actors.begin(); aIt != m_Actors.end(); ++aIt) { (*aIt)->DrawMOIDIfOverlapping(pOverlapsThis); @@ -1409,8 +1150,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void callLuaFunctionOnMORecursive(MovableObject* mo, const std::string& functionName, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { if (MOSRotating* mosr = dynamic_cast(mo)) { for (auto attachablrItr = mosr->GetAttachableList().begin(); attachablrItr != mosr->GetAttachableList().end();) { @@ -1433,8 +1172,6 @@ namespace RTE { mo->RunScriptedFunctionInAppropriateScripts(functionName, false, false, functionEntityArguments, functionLiteralArguments, functionObjectArguments); }; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::RunLuaFunctionOnAllMOs(const std::string& functionName, bool includeAdded, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { if (includeAdded) { for (Actor* actor: m_AddedActors) { @@ -1463,8 +1200,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void reloadLuaFunctionsOnMORecursive(MovableObject* mo) { if (MOSRotating* mosr = dynamic_cast(mo)) { for (auto attachablrItr = mosr->GetAttachableList().begin(); attachablrItr != mosr->GetAttachableList().end();) { @@ -1487,8 +1222,6 @@ namespace RTE { mo->ReloadScripts(); }; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::ReloadLuaScripts() { for (Actor* actor: m_AddedActors) { reloadLuaFunctionsOnMORecursive(actor); @@ -1515,11 +1248,6 @@ namespace RTE { } } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this MovableMan. Supposed to be done every frame. - void MovableMan::Update() { ZoneScoped; @@ -1922,8 +1650,6 @@ if (g_TimerMan.DrawnSimUpdate()) } } -////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::Travel() { ZoneScoped; @@ -1981,8 +1707,6 @@ void MovableMan::Travel() { } } -////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::UpdateControllers() { ZoneScoped; @@ -2016,8 +1740,6 @@ void MovableMan::UpdateControllers() { g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ActorsAI); } -////////////////////////////////////////////////////////////////////////////////////////// - void MovableMan::PreControllerUpdate() { ZoneScoped; @@ -2038,12 +1760,6 @@ void MovableMan::PreControllerUpdate() { g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::ParticlesUpdate); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawMatter -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MovableMan's all MO's current material representations to a -// BITMAP of choice. - void MovableMan::DrawMatter(BITMAP* pTargetBitmap, Vector& targetPos) { // Draw objects to accumulation bitmap for (std::deque::iterator aIt = --m_Actors.end(); aIt != --m_Actors.begin(); --aIt) @@ -2053,13 +1769,6 @@ void MovableMan::DrawMatter(BITMAP* pTargetBitmap, Vector& targetPos) { (*parIt)->Draw(pTargetBitmap, targetPos, g_DrawMaterial); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: VerifyMOIDIndex -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Verifieis whether all elements of MOID index has correct ID. Should be used in Debug mode only. -// Arguments: None. -// Return value: None. - void MovableMan::VerifyMOIDIndex() { int count = 0; for (std::vector::iterator aIt = m_MOIDIndex.begin(); aIt != m_MOIDIndex.end(); ++aIt) { @@ -2083,12 +1792,6 @@ void MovableMan::VerifyMOIDIndex() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateDrawMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the MOIDs of all current MOs and draws their ID's to a BITMAP -// of choice. - void MovableMan::UpdateDrawMOIDs(BITMAP* pTargetBitmap) { ZoneScoped; @@ -2153,12 +1856,6 @@ void MovableMan::UpdateDrawMOIDs(BITMAP* pTargetBitmap) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MovableMan's current graphical representation to a -// BITMAP of choice. - void MovableMan::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { ZoneScoped; @@ -2189,12 +1886,6 @@ void MovableMan::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawHUD -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this MovableMan's current graphical representation to a -// BITMAP of choice. - void MovableMan::DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos, int which, bool playerControlled) { ZoneScoped; diff --git a/Source/Managers/MovableMan.h b/Source/Managers/MovableMan.h index d4c8aec97a..22b0c3a697 100644 --- a/Source/Managers/MovableMan.h +++ b/Source/Managers/MovableMan.h @@ -1,18 +1,11 @@ #ifndef _RTEMOVABLEMAN_ #define _RTEMOVABLEMAN_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MovableMan.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the MovableMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the MovableMan class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Serializable.h" #include "Singleton.h" #include "Activity.h" @@ -32,13 +25,7 @@ namespace RTE { class Box; class LuabindObjectWrapper; - ////////////////////////////////////////////////////////////////////////////////////////// - // Struct: AlarmEvent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A struct to keep all data about a an alarming event for the AI Actors. - // Parent(s): None. - // Class history: 10/3/2008 AlarmEvent created. - + /// A struct to keep all data about a an alarming event for the AI Actors. struct AlarmEvent { AlarmEvent() { m_ScenePos.Reset(); @@ -56,754 +43,453 @@ namespace RTE { float m_Range; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: MovableMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The singleton manager of all movable objects in the RTE. - // Parent(s): Singleton, Serializable. - // Class history: 12/25/2001 MovableMan created. - + /// The singleton manager of all movable objects in the RTE. class MovableMan : public Singleton, public Serializable { friend class SettingsMan; friend struct ManagerLuaBindings; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableClassNameGetter; SerializableOverrideMethods; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MovableMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a MovableMan object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a MovableMan object in system + /// memory. Create() should be called before using the object. MovableMan() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~MovableMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a MovableMan object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a MovableMan object before deletion + /// from system memory. ~MovableMan() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MovableMan object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MovableMan object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Initialize(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire MovableMan, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire MovableMan, including its inherited members, to + /// their default settings or values. void Reset() override { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MovableMan object. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the MovableMan object. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMOFromID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a MO from its MOID. Note that MOID's are only valid during the - // same frame as they were assigned to the MOs! - // Arguments: The MOID to get the matching MO from. - // Return value: A pointer to the requested MovableObject instance. 0 if no MO with that - // MOID was found. 0 if 0 was passed in as MOID (no MOID). Ownership is - // *NOT* transferred!! - + /// Gets a MO from its MOID. Note that MOID's are only valid during the + /// same frame as they were assigned to the MOs! + /// @param whichID The MOID to get the matching MO from. + /// @return A pointer to the requested MovableObject instance. 0 if no MO with that + /// MOID was found. 0 if 0 was passed in as MOID (no MOID). Ownership is + /// *NOT* transferred!! MovableObject* GetMOFromID(MOID whichID); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMOIDCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of MOID's currently in use this frame. - // Arguments: None. - // Return value: The count of MOIDs in use this frame. - + /// Gets the number of MOID's currently in use this frame. + /// @return The count of MOIDs in use this frame. int GetMOIDCount() { return m_MOIDIndex.size(); } - /// /// Gets a MOID from pixel coordinates in the Scene. - /// - /// The X coordinate of the Scene pixel to get the MOID of. - /// The Y coordinate of the Scene pixel to get the MOID of. - /// The collection of MOIDs to check the against the specified coordinates. - /// The topmost MOID currently at the specified pixel coordinates. + /// @param pixelX The X coordinate of the Scene pixel to get the MOID of. + /// @param pixelY The Y coordinate of the Scene pixel to get the MOID of. + /// @param moidList The collection of MOIDs to check the against the specified coordinates. + /// @return The topmost MOID currently at the specified pixel coordinates. MOID GetMOIDPixel(int pixelX, int pixelY, const std::vector& moidList); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTeamMOIDCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns MO count for specified team - // Arguments: Team to count MO's - // Return value: MO's count owned by this team - + /// Returns MO count for specified team + /// @param team Team to count MO's + /// @return MO's count owned by this team int GetTeamMOIDCount(int team) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PurgeAllMOs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears out all MovableObject:s out of this. Effectively empties the world - // of anything moving, without resetting all of this' settings. - // Arguments: None. - // Return value: None. - + /// Clears out all MovableObject:s out of this. Effectively empties the world + /// of anything moving, without resetting all of this' settings. void PurgeAllMOs(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNextActorInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the first Actor in the internal Actor list that is - // of a specifc group, alternatively the first one AFTER a specific actor! - // Arguments: Which group to try to get an Actor for. - // A pointer to an Actor to use as starting point in the forward search. - // Ownership NOT xferred! - // Return value: An Actor pointer to the requested team's first Actor encountered - // in the list. 0 if there are no Actors of that team. - + /// Get a pointer to the first Actor in the internal Actor list that is + /// of a specifc group, alternatively the first one AFTER a specific actor! + /// @param group Which group to try to get an Actor for. + /// @param pAfterThis A pointer to an Actor to use as starting point in the forward search. (default: 0) + /// Ownership NOT xferred! + /// @return An Actor pointer to the requested team's first Actor encountered + /// in the list. 0 if there are no Actors of that team. Actor* GetNextActorInGroup(std::string group, Actor* pAfterThis = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPrevActorInGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the last Actor in the internal Actor list that is - // of a specifc group, alternatively the last one BEFORE a specific actor! - // Arguments: Which group to try to get an Actor for. - // A pointer to an Actor to use as starting point in the backward search. - // Ownership NOT xferred! - // Return value: An Actor pointer to the requested team's last Actor encountered - // in the list. 0 if there are no Actors of that team. - + /// Get a pointer to the last Actor in the internal Actor list that is + /// of a specifc group, alternatively the last one BEFORE a specific actor! + /// @param group Which group to try to get an Actor for. + /// @param pBeforeThis A pointer to an Actor to use as starting point in the backward search. (default: 0) + /// Ownership NOT xferred! + /// @return An Actor pointer to the requested team's last Actor encountered + /// in the list. 0 if there are no Actors of that team. Actor* GetPrevActorInGroup(std::string group, Actor* pBeforeThis = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTeamRoster - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the list of all actors on one team, ordered by their X positions. - // Arguments: Which team to try to get the roster for. - // Return value: A pointer to the list of all the actors on the specified team, sorted - // ascending by their X posistions. Ownership of the list or contained - // actors is NOT transferred! - + /// Gets the list of all actors on one team, ordered by their X positions. + /// @param team Which team to try to get the roster for. (default: 0) { return &(m_ActorRoster[team]) + /// @return A pointer to the list of all the actors on the specified team, sorted + /// ascending by their X posistions. Ownership of the list or contained + /// actors is NOT transferred! std::list* GetTeamRoster(int team = 0) { return &(m_ActorRoster[team]); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNextTeamActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the first Actor in the internal Actor list that is - // of a specifc team, alternatively the first one AFTER a specific actor! - // Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. - // A pointer to an Actor to use as starting point in the forward search. - // Ownership NOT xferred! - // Return value: An Actor pointer to the requested team's first Actor encountered - // in the list. 0 if there are no Actors of that team. - + /// Get a pointer to the first Actor in the internal Actor list that is + /// of a specifc team, alternatively the first one AFTER a specific actor! + /// @param team Which team to try to get an Actor for. 0 means first team, 1 means 2nd. (default: 0) + /// @param pAfterThis A pointer to an Actor to use as starting point in the forward search. (default: 0) + /// Ownership NOT xferred! + /// @return An Actor pointer to the requested team's first Actor encountered + /// in the list. 0 if there are no Actors of that team. Actor* GetNextTeamActor(int team = 0, Actor* pAfterThis = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPrevTeamActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the last Actor in the internal Actor list that is - // of a specifc team, alternatively the last one BEFORE a specific actor! - // Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. - // A pointer to an Actor to use as starting point in the backward search. - // Ownership NOT xferred! - // Return value: An Actor pointer to the requested team's last Actor encountered - // in the list. 0 if there are no Actors of that team. - + /// Get a pointer to the last Actor in the internal Actor list that is + /// of a specifc team, alternatively the last one BEFORE a specific actor! + /// @param team Which team to try to get an Actor for. 0 means first team, 1 means 2nd. (default: 0) + /// @param pBeforeThis A pointer to an Actor to use as starting point in the backward search. (default: 0) + /// Ownership NOT xferred! + /// @return An Actor pointer to the requested team's last Actor encountered + /// in the list. 0 if there are no Actors of that team. Actor* GetPrevTeamActor(int team = 0, Actor* pBeforeThis = 0); - /// /// Get a pointer to an Actor in the internal Actor list that is of a specifc team and closest to a specific scene point. - /// - /// Which team to try to get an Actor for. 0 means first team, 1 means 2nd. - /// The player to get the Actor for. This affects which brain can be marked. - /// The Scene point to search for the closest to. - /// The maximum radius around that scene point to search. - /// A Vector to be filled out with the distance of the returned closest to the search point. Will be unaltered if no object was found within radius. - /// An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! - /// An Actor pointer to the requested team's Actor closest to the Scene point, but not outside the max radius. If no Actor other than the excluded one was found within the radius of the point, nullptr is returned. + /// @param team Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + /// @param player The player to get the Actor for. This affects which brain can be marked. + /// @param scenePoint The Scene point to search for the closest to. + /// @param maxRadius The maximum radius around that scene point to search. + /// @param getDistance A Vector to be filled out with the distance of the returned closest to the search point. Will be unaltered if no object was found within radius. + /// @param excludeThis An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! + /// @return An Actor pointer to the requested team's Actor closest to the Scene point, but not outside the max radius. If no Actor other than the excluded one was found within the radius of the point, nullptr is returned. Actor* GetClosestTeamActor(int team, int player, const Vector& scenePoint, int maxRadius, Vector& getDistance, const Actor* excludeThis = nullptr) { return GetClosestTeamActor(team, player, scenePoint, maxRadius, getDistance, false, excludeThis); } - /// /// Get a pointer to an Actor in the internal Actor list that is of a specifc team and closest to a specific scene point. - /// - /// Which team to try to get an Actor for. 0 means first team, 1 means 2nd. - /// The player to get the Actor for. This affects which brain can be marked. - /// The Scene point to search for the closest to. - /// The maximum radius around that scene point to search. - /// A Vector to be filled out with the distance of the returned closest to the search point. Will be unaltered if no object was found within radius. - /// Whether to only get Actors that are flagged as player controllable. - /// An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! - /// An Actor pointer to the requested team's Actor closest to the Scene point, but not outside the max radius. If no Actor other than the excluded one was found within the radius of the point, nullptr is returned. + /// @param team Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + /// @param player The player to get the Actor for. This affects which brain can be marked. + /// @param scenePoint The Scene point to search for the closest to. + /// @param maxRadius The maximum radius around that scene point to search. + /// @param getDistance A Vector to be filled out with the distance of the returned closest to the search point. Will be unaltered if no object was found within radius. + /// @param onlyPlayerControllableActors Whether to only get Actors that are flagged as player controllable. + /// @param excludeThis An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! + /// @return An Actor pointer to the requested team's Actor closest to the Scene point, but not outside the max radius. If no Actor other than the excluded one was found within the radius of the point, nullptr is returned. Actor* GetClosestTeamActor(int team, int player, const Vector& scenePoint, int maxRadius, Vector& getDistance, bool onlyPlayerControllableActors, const Actor* excludeThis = nullptr); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetClosestEnemyActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to an Actor in the internal Actor list that is is not of - // the specified team and closest to a specific scene point. - // Arguments: Which team to try to get an enemy Actor for. NoTeam means all teams. - // The Scene point to search for the closest to. - // The maximum radius around that scene point to search. - // A Vector to be filled out with the distance of the returned closest to - // the search point. Will be unaltered if no object was found within radius. - // Return value: An Actor pointer to the enemy closest to the Scene - // point, but not outside the max radius. If no Actor - // was found within the radius of the point, 0 is returned. - + /// Get a pointer to an Actor in the internal Actor list that is is not of + /// the specified team and closest to a specific scene point. + /// @param team Which team to try to get an enemy Actor for. NoTeam means all teams. + /// @param scenePoint The Scene point to search for the closest to. + /// @param maxRadius The maximum radius around that scene point to search. + /// @param getDistance A Vector to be filled out with the distance of the returned closest to + /// the search point. Will be unaltered if no object was found within radius. + /// @return An Actor pointer to the enemy closest to the Scene + /// point, but not outside the max radius. If no Actor + /// was found within the radius of the point, 0 is returned. Actor* GetClosestEnemyActor(int team, const Vector& scenePoint, int maxRadius, Vector& getDistance); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFirstTeamActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to first best Actor in the internal Actor list that is - // of a specifc team. - // Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. - // The player to get the Actor for. This affects which brain can be marked. - // Return value: An Actor pointer to the first one of the requested team. If no Actor - // is in that team, 0 is returned. - + /// Get a pointer to first best Actor in the internal Actor list that is + /// of a specifc team. + /// @param team Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + /// @param player The player to get the Actor for. This affects which brain can be marked. + /// @return An Actor pointer to the first one of the requested team. If no Actor + /// is in that team, 0 is returned. Actor* GetFirstTeamActor(int team, int player) { Vector temp; return GetClosestTeamActor(team, player, Vector(), 10000000, temp); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetClosestActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to an Actor in the internal Actor list that is closest - // to a specific scene point. - // Arguments: Which team to try to get an Actor for. 0 means first team, 1 means 2nd. - // The Scene point to search for the closest to. - // The maximum radius around that scene point to search. - // A Vector to be filled out with the distance of the returned closest to - // the search point. Will be unaltered if no object was found within radius. - // An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! - // Return value: An Actor pointer to the requested Actor closest to the Scene - // point, but not outside the max radius. If no Actor other than the - // excluded one was found within the radius of the point, 0 is returned. - + /// Get a pointer to an Actor in the internal Actor list that is closest + /// to a specific scene point. + /// @param scenePoint Which team to try to get an Actor for. 0 means first team, 1 means 2nd. + /// @param maxRadius The Scene point to search for the closest to. + /// @param getDistance The maximum radius around that scene point to search. + /// @param pExcludeThis A Vector to be filled out with the distance of the returned closest to (default: 0) + /// the search point. Will be unaltered if no object was found within radius. + /// An Actor to exclude from the search. OWNERSHIP IS NOT TRANSFERRED! + /// @return An Actor pointer to the requested Actor closest to the Scene + /// point, but not outside the max radius. If no Actor other than the + /// excluded one was found within the radius of the point, 0 is returned. Actor* GetClosestActor(const Vector& scenePoint, int maxRadius, Vector& getDistance, const Actor* pExcludeThis = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetClosestBrainActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the brain actor of a specific team that is closest to - // a scene point. OWNERSHIP IS NOT TRANSFERRED! - // Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. - // The point in the scene where to look for the closest opposite team brain. - // Return value: An Actor pointer to the requested team's brain closest to the point. - // 0 if there are no brains of that team. OWNERSHIP IS NOT TRANSFERRED! - + /// Get a pointer to the brain actor of a specific team that is closest to + /// a scene point. OWNERSHIP IS NOT TRANSFERRED! + /// @param team Which team to try to get the brain for. 0 means first team, 1 means 2nd. + /// @param scenePoint The point in the scene where to look for the closest opposite team brain. + /// @return An Actor pointer to the requested team's brain closest to the point. + /// 0 if there are no brains of that team. OWNERSHIP IS NOT TRANSFERRED! Actor* GetClosestBrainActor(int team, const Vector& scenePoint) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFirstBrainActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the brain actor of a specific team that is closest to - // a scene point. OWNERSHIP IS NOT TRANSFERRED! - // Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. - // The point in the scene where to look for the closest opposite team brain. - // Return value: An Actor pointer to the requested team's brain closest to the point. - // 0 if there are no brains of that team. OWNERSHIP IS NOT TRANSFERRED! - + /// Get a pointer to the brain actor of a specific team that is closest to + /// a scene point. OWNERSHIP IS NOT TRANSFERRED! + /// @param GetClosestBrainActor(team Which team to try to get the brain for. 0 means first team, 1 means 2nd. + /// @param Vector() The point in the scene where to look for the closest opposite team brain. + /// @return An Actor pointer to the requested team's brain closest to the point. + /// 0 if there are no brains of that team. OWNERSHIP IS NOT TRANSFERRED! Actor* GetFirstBrainActor(int team) const { return GetClosestBrainActor(team, Vector()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetClosestOtherBrainActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the brain actor NOT of a specific team that is closest - // to a scene point. OWNERSHIP IS NOT TRANSFERRED! - // Arguments: Which team to NOT get the brain for. 0 means first team, 1 means 2nd. - // The point where to look for the closest brain not of this team. - // Return value: An Actor pointer to the requested brain closest to the point. - // 0 if there are no brains not on that team. OWNERSHIP IS NOT TRANSFERRED! - + /// Get a pointer to the brain actor NOT of a specific team that is closest + /// to a scene point. OWNERSHIP IS NOT TRANSFERRED! + /// @param notOfTeam Which team to NOT get the brain for. 0 means first team, 1 means 2nd. + /// @param scenePoint The point where to look for the closest brain not of this team. + /// @return An Actor pointer to the requested brain closest to the point. + /// 0 if there are no brains not on that team. OWNERSHIP IS NOT TRANSFERRED! Actor* GetClosestOtherBrainActor(int notOfTeam, const Vector& scenePoint) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetFirstOtherBrainActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the brain actor NOT of a specific team. OWNERSHIP IS NOT TRANSFERRED! - // Arguments: Which team to NOT get the brain for. 0 means first team, 1 means 2nd. - // Return value: An Actor pointer to the requested brain of that team. - // 0 if there are no brains not on that team. OWNERSHIP IS NOT TRANSFERRED! - + /// Get a pointer to the brain actor NOT of a specific team. OWNERSHIP IS NOT TRANSFERRED! + /// @param GetClosestOtherBrainActor(notOfTeam Which team to NOT get the brain for. 0 means first team, 1 means 2nd. + /// @return An Actor pointer to the requested brain of that team. + /// 0 if there are no brains not on that team. OWNERSHIP IS NOT TRANSFERRED! Actor* GetFirstOtherBrainActor(int notOfTeam) const { return GetClosestOtherBrainActor(notOfTeam, Vector()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetUnassignedBrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Get a pointer to the first brain actor of a specific team which hasn't - // been assigned to a player yet. - // Arguments: Which team to try to get the brain for. 0 means first team, 1 means 2nd. - // Return value: An Actor pointer to the requested team's first brain encountered - // in the list that hasn't been assigned to a player. 0 if there are no - // unassigned brains of that team. - + /// Get a pointer to the first brain actor of a specific team which hasn't + /// been assigned to a player yet. + /// @param team Which team to try to get the brain for. 0 means first team, 1 means 2nd. (default: 0) + /// @return An Actor pointer to the requested team's first brain encountered + /// in the list that hasn't been assigned to a player. 0 if there are no + /// unassigned brains of that team. Actor* GetUnassignedBrain(int team = 0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActorCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of actors currently held. - // Arguments: None. - // Return value: The number of actors. - + /// Gets the number of actors currently held. + /// @return The number of actors. long GetActorCount() const { return m_Actors.size(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetParticleCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the number of particles (MOPixel:s) currently held. - // Arguments: None. - // Return value: The number of particles. - + /// Gets the number of particles (MOPixel:s) currently held. + /// @return The number of particles. long GetParticleCount() const { return m_Particles.size(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSplashRatio - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the global setting for how much splash MOPixels should be created - // an MO penetrates the terrain deeply. - // Arguments: None. - // Return value: A float with the global splash amount setting, form 1.0 to 0.0. - + /// Gets the global setting for how much splash MOPixels should be created + /// an MO penetrates the terrain deeply. + /// @return A float with the global splash amount setting, form 1.0 to 0.0. float GetSplashRatio() const { return m_SplashRatio; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMaxDroppedItems - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the max number of dropped items that will be reached before the - // first dropped with be copied to the terrain. - // Arguments: An int spefifying the limit. - // Return value: None. - + /// Sets the max number of dropped items that will be reached before the + /// first dropped with be copied to the terrain. + /// @param newLimit An int spefifying the limit. void SetMaxDroppedItems(int newLimit) { m_MaxDroppedItems = newLimit; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaxDroppedItems - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the max number of dropped items that will be reached before the - // first dropped with be copied to the terrain. - // Arguments: None. - // Return value: An int spefifying the limit. - + /// Gets the max number of dropped items that will be reached before the + /// first dropped with be copied to the terrain. + /// @return An int spefifying the limit. int GetMaxDroppedItems() const { return m_MaxDroppedItems; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SortTeamRoster - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets this to draw HUD lines for a specific team's roster this frame. - // Arguments: Which team to have lines drawn of. - // Return value: None. - + /// Sets this to draw HUD lines for a specific team's roster this frame. + /// @param team Which team to have lines drawn of. void SortTeamRoster(int team) { m_SortTeamRoster[team] = true; } - /// /// Adds a MovableObject to this, after it is determined what it is and the best way to add it is. E.g. if it's an Actor, it will be added as such. Ownership IS transferred! - /// - /// A pointer to the MovableObject to add. Ownership IS transferred! - /// Whether the MovableObject was successfully added or not. Note that Ownership IS transferred either way, but the MovableObject will be deleted if this is not successful. + /// @param movableObjectToAdd A pointer to the MovableObject to add. Ownership IS transferred! + /// @return Whether the MovableObject was successfully added or not. Note that Ownership IS transferred either way, but the MovableObject will be deleted if this is not successful. bool AddMO(MovableObject* movableObjectToAdd); - /// /// Adds an Actor to the internal list of Actors. Destruction and deletion will be taken care of automatically. Ownership IS transferred! - /// - /// A pointer to the Actor to add. Ownership IS transferred! + /// @param actorToAdd A pointer to the Actor to add. Ownership IS transferred! void AddActor(Actor* actorToAdd); - /// /// Adds a pickup-able item to the internal list of items. Destruction and deletion will be taken care of automatically. Ownership IS transferred! - /// - /// A pointer to the item to add. Ownership IS transferred! + /// @param itemToAdd A pointer to the item to add. Ownership IS transferred! void AddItem(HeldDevice* itemToAdd); - /// /// Adds a MovableObject to the internal list of particles. Destruction and deletion will be taken care of automatically. Ownership IS transferred! - /// - /// A pointer to the MovableObject to add. Ownership is transferred! + /// @param particleToAdd A pointer to the MovableObject to add. Ownership is transferred! void AddParticle(MovableObject* particleToAdd); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes an Actor from the internal list of MO:s. After the Actor is - // removed, ownership is effectively released and transferred to whatever - // client called this method. - // Arguments: A pointer to the MovableObject to remove. - // Return value: Whether the object was found in the particle list, and consequently - // removed. If the particle entry wasn't found, false is returned. - + /// Removes an Actor from the internal list of MO:s. After the Actor is + /// removed, ownership is effectively released and transferred to whatever + /// client called this method. + /// @param pActorToRem A pointer to the MovableObject to remove. + /// @return Whether the object was found in the particle list, and consequently + /// removed. If the particle entry wasn't found, false is returned. Actor* RemoveActor(MovableObject* pActorToRem); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a pickup-able MovableObject item from the internal list of - // MO:s. After the item is removed, ownership is effectively released and - // transferred to whatever client called this method. - // Arguments: A pointer to the MovableObject to remove. - // Return value: Whether the object was found in the particle list, and consequently - // removed. If the particle entry wasn't found, false is returned. - + /// Removes a pickup-able MovableObject item from the internal list of + /// MO:s. After the item is removed, ownership is effectively released and + /// transferred to whatever client called this method. + /// @param pItemToRem A pointer to the MovableObject to remove. + /// @return Whether the object was found in the particle list, and consequently + /// removed. If the particle entry wasn't found, false is returned. MovableObject* RemoveItem(MovableObject* pItemToRem); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveParticle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a MovableObject from the internal list of MO:s. After the - // MO is removed, ownership is effectively released and transferred to - // whatever client called this method. - // Arguments: A pointer to the MovableObject to remove. - // Return value: Whether the object was found in the particle list, and consequently - // removed. If the particle entry wasn't found, false is returned. - + /// Removes a MovableObject from the internal list of MO:s. After the + /// MO is removed, ownership is effectively released and transferred to + /// whatever client called this method. + /// @param pMOToRem A pointer to the MovableObject to remove. + /// @return Whether the object was found in the particle list, and consequently + /// removed. If the particle entry wasn't found, false is returned. MovableObject* RemoveParticle(MovableObject* pMOToRem); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeActorTeam - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes actor team and updates team rosters. - // Arguments: Pointer to actor, new team value - // Return value: None. - + /// Changes actor team and updates team rosters. + /// @param pActor Pointer to actor, new team value void ChangeActorTeam(Actor* pActor, int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddActorToTeamRoster - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds actor to internal team roster - // Arguments: Pointer to actor - // Return value: None. - + /// Adds actor to internal team roster + /// @param pActorToAdd Pointer to actor void AddActorToTeamRoster(Actor* pActorToAdd); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveActorToTeamRoster - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes actor from internal team roster - // Arguments: Pointer to actor - // Return value: None. - + /// Removes actor from internal team roster + /// @param pActorToRem Pointer to actor void RemoveActorFromTeamRoster(Actor* pActorToRem); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ValidateMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Goes through and checks that all MOID's have valid MO pointers - // associated with them. This shuold only be used for testing, as it will - // crash the app if validation fails. - // Arguments: None. - // Return value: All MOIDs valid. - + /// Goes through and checks that all MOID's have valid MO pointers + /// associated with them. This shuold only be used for testing, as it will + /// crash the app if validation fails. + /// @return All MOIDs valid. bool ValidateMOIDs(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ValidMO - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MovableObject pointer points to an - // MO that's currently active in the simulation, and kept by this - // MovableMan. Internal optimization is made so that the same MO can - // efficiently be checked many times during the same frame. - // Arguments: A pointer to the MovableObject to check for being actively kept by - // this MovableMan. - // Return value: Whether the MO instance was found in the active list or not. - + /// Indicates whether the passed in MovableObject pointer points to an + /// MO that's currently active in the simulation, and kept by this + /// MovableMan. Internal optimization is made so that the same MO can + /// efficiently be checked many times during the same frame. + /// @param pMOToCheck A pointer to the MovableObject to check for being actively kept by + /// this MovableMan. + /// @return Whether the MO instance was found in the active list or not. bool ValidMO(const MovableObject* pMOToCheck); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MovableObject is an active Actor kept - // by this MovableMan or not. - // Arguments: A pointer to the MovableObject to check for Actorness. - // Return value: Whether the object was found in the Actor list or not. - + /// Indicates whether the passed in MovableObject is an active Actor kept + /// by this MovableMan or not. + /// @param pMOToCheck A pointer to the MovableObject to check for Actorness. + /// @return Whether the object was found in the Actor list or not. bool IsActor(const MovableObject* pMOToCheck); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsDevice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MovableObject is an active Item kept - // by this MovableMan or not. - // Arguments: A pointer to the MovableObject to check for Itemness. - // Return value: Whether the object was found in the Item list or not. - + /// Indicates whether the passed in MovableObject is an active Item kept + /// by this MovableMan or not. + /// @param pMOToCheck A pointer to the MovableObject to check for Itemness. + /// @return Whether the object was found in the Item list or not. bool IsDevice(const MovableObject* pMOToCheck); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsParticle - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MovableObject is an active Item kept - // by this MovableMan or not. - // Arguments: A pointer to the MovableObject to check for Itemness. - // Return value: Whether the object was found in the Particle list or not. - + /// Indicates whether the passed in MovableObject is an active Item kept + /// by this MovableMan or not. + /// @param pMOToCheck A pointer to the MovableObject to check for Itemness. + /// @return Whether the object was found in the Particle list or not. bool IsParticle(const MovableObject* pMOToCheck); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsOfActor - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the passed in MOID is that of an MO which either is - // or is parented to an active Actor by this MovableMan, or not. - // Arguments: An MOID to check for Actorness. - // Return value: Whether the object was found or owned by an MO in the Actor list or not. - + /// Indicates whether the passed in MOID is that of an MO which either is + /// or is parented to an active Actor by this MovableMan, or not. + /// @param checkMOID An MOID to check for Actorness. + /// @return Whether the object was found or owned by an MO in the Actor list or not. bool IsOfActor(MOID checkMOID); - /// /// Gives a unique, contiguous id per-actor. This is regenerated every frame. - /// - /// The actor to get a contiguous id for. - /// A contiguous id for the actor. Returns -1 if the actor doesn't exist in MovableMan. - /// This function is used for AI throttling. + /// @param actor The actor to get a contiguous id for. + /// @return A contiguous id for the actor. Returns -1 if the actor doesn't exist in MovableMan. + /// @remark This function is used for AI throttling. int GetContiguousActorID(const Actor* actor) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRootMOID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Produces the root MOID of the MOID of a potential child MO to another MO. - // Arguments: An MOID to get the root MOID of. - // Return value: The MOID of the root MO of the MO the passed-in MOID represents. This - // will be the same as the MOID passed in if the MO is a root itself. It will - // be equal to g_NoMOID if the MOID isn't allocated to an MO. - + /// Produces the root MOID of the MOID of a potential child MO to another MO. + /// @param checkMOID An MOID to get the root MOID of. + /// @return The MOID of the root MO of the MO the passed-in MOID represents. This + /// will be the same as the MOID passed in if the MO is a root itself. It will + /// be equal to g_NoMOID if the MOID isn't allocated to an MO. MOID GetRootMOID(MOID checkMOID); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveMO - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a MovableObject from the any and all internal lists of MO:s. - // After the MO is removed, ownership is effectively released and - // transferred to whatever client called this method. - // Arguments: A pointer to the MovableObject to remove. - // Return value: Whether the object was found in MovableMan's custody, and consequently - // removed. If the MO entry wasn't found, false is returned. - + /// Removes a MovableObject from the any and all internal lists of MO:s. + /// After the MO is removed, ownership is effectively released and + /// transferred to whatever client called this method. + /// @param pMOToRem A pointer to the MovableObject to remove. + /// @return Whether the object was found in MovableMan's custody, and consequently + /// removed. If the MO entry wasn't found, false is returned. bool RemoveMO(MovableObject* pMOToRem); - /// /// Kills and destroys all Actors of a specific Team. - /// - /// The team to annihilate. If NoTeam is passed in, then NO Actors die. - /// How many Actors were killed. + /// @param teamToKill The team to annihilate. If NoTeam is passed in, then NO Actors die. + /// @return How many Actors were killed. int KillAllTeamActors(int teamToKill) const; - /// /// Kills and destroys all enemy Actors of a specific Team. - /// - /// The team to NOT annihilate. If NoTeam is passed in, then ALL Actors die. - /// How many Actors were killed. + /// @param teamNotToKill The team to NOT annihilate. If NoTeam is passed in, then ALL Actors die. + /// @return How many Actors were killed. int KillAllEnemyActors(int teamNotToKill = Activity::NoTeam) const; - /// /// Adds all Actors in MovableMan to the given list. - /// - /// Whether or not ownership of the Actors should be transferred from MovableMan to the list. - /// The list to be filled with Actors. - /// The team to get Actors of. If NoTeam, then all teams will be used. - /// Whether or not to get brain Actors. - /// The number of Actors added to the list. + /// @param transferOwnership Whether or not ownership of the Actors should be transferred from MovableMan to the list. + /// @param actorList The list to be filled with Actors. + /// @param onlyTeam The team to get Actors of. If NoTeam, then all teams will be used. + /// @param noBrains Whether or not to get brain Actors. + /// @return The number of Actors added to the list. int GetAllActors(bool transferOwnership, std::list& actorList, int onlyTeam = -1, bool noBrains = false); - /// - /// Whether or not ownershp of the items shoudl be transferred from MovableMan to the list. - /// The list to be filled with items. - /// The number of items added to the list. + /// @param transferOwnership Whether or not ownershp of the items shoudl be transferred from MovableMan to the list. + /// @param itemList The list to be filled with items. + /// @return The number of items added to the list. int GetAllItems(bool transferOwnership, std::list& itemList); - /// /// Adds all particles in MovableMan to the given list. - /// - /// Whether or not ownership of the particles should be transferred from MovableMan to the list. - /// The list to be filled with particles. - /// The number of particles added to the list. + /// @param transferOwnership Whether or not ownership of the particles should be transferred from MovableMan to the list. + /// @param particleList The list to be filled with particles. + /// @return The number of particles added to the list. int GetAllParticles(bool transferOwnership, std::list& particleList); - /// /// Opens all doors and keeps them open until this is called again with false. - /// - /// Whether to open all doors (true), or close all doors (false). - /// Which team to open doors for. NoTeam means all teams. + /// @param open Whether to open all doors (true), or close all doors (false). + /// @param team Which team to open doors for. NoTeam means all teams. void OpenAllDoors(bool open = true, int team = Activity::NoTeam) const; - /// /// Temporarily erases or redraws any material door representations of a specific team. /// Used to make pathfinding work better, allowing Actors to navigate through firendly bases despite the door material layer. - /// - /// Whether to erase door material, thereby overriding it, or redraw it and undo the override. - /// Which team to do this for, NoTeam means all teams. + /// @param eraseDoorMaterial Whether to erase door material, thereby overriding it, or redraw it and undo the override. + /// @param team Which team to do this for, NoTeam means all teams. void OverrideMaterialDoors(bool eraseDoorMaterial, int team = Activity::NoTeam) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RegisterAlarmEvent - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registers an AlarmEvent to notify things around that somehting alarming - // like a gunshot or explosion just happened. - // Arguments: The AlarmEvent to register. - // Return value: None. - + /// Registers an AlarmEvent to notify things around that somehting alarming + /// like a gunshot or explosion just happened. + /// @param newEvent The AlarmEvent to register. void RegisterAlarmEvent(const AlarmEvent& newEvent); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAlarmEvents - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the list of AlarmEvent:s from last frame's update. - // Arguments: None. - // Return value: The const list of AlarmEvent:s. - + /// Gets the list of AlarmEvent:s from last frame's update. + /// @return The const list of AlarmEvent:s. const std::vector& GetAlarmEvents() const { return m_AlarmEvents; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsParticleSettlingEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whetehr particles are set to get copied to the terrain upon - // settling - // Arguments: None. - // Return value: Whether enabled or not. - + /// Shows whetehr particles are set to get copied to the terrain upon + /// settling + /// @return Whether enabled or not. bool IsParticleSettlingEnabled() { return m_SettlingEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnableParticleSettling - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether particles will get copied into the terrain upon them - // settling down. - // Arguments: Whether to enable or not. - // Return value: None. - + /// Sets whether particles will get copied into the terrain upon them + /// settling down. + /// @param enable Whether to enable or not. (default: true) void EnableParticleSettling(bool enable = true) { m_SettlingEnabled = enable; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsMOSubtractionEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether MO's sihouettes can get subtracted from the terrain at all. - // Arguments: None. - // Return value: Whether enabled or not. - + /// Shows whether MO's sihouettes can get subtracted from the terrain at all. + /// @return Whether enabled or not. bool IsMOSubtractionEnabled() { return m_MOSubtractionEnabled; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RedrawOverlappingMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces all objects potnetially overlapping a specific MO to re-draw - // this MOID representations onto the MOID bitmap. - // Arguments: A pointer to the MO to check for overlaps against. Ownerhip is NOT - // transferred. - // Return value: None. - + /// Forces all objects potnetially overlapping a specific MO to re-draw + /// this MOID representations onto the MOID bitmap. + /// @param pOverlapsThis A pointer to the MO to check for overlaps against. Ownerhip is NOT + /// transferred. void RedrawOverlappingMOIDs(MovableObject* pOverlapsThis); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this MovableMan. Supposed to be done every frame. - // Arguments: None. - // Return value: None. - + /// Updates the state of this MovableMan. Supposed to be done every frame. void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawMatter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this MovableMan's all MO's current material representations to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this MovableMan's all MO's current material representations to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. void DrawMatter(BITMAP* pTargetBitmap, Vector& targetPos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateDrawMOIDs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the MOIDs of all current MOs and draws their ID's to a BITMAP - // of choice. If there are more than 255 MO's to draw, some will not be. - // Arguments: A pointer to a BITMAP to draw on. - // Return value: None. - + /// Updates the MOIDs of all current MOs and draws their ID's to a BITMAP + /// of choice. If there are more than 255 MO's to draw, some will not be. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. void UpdateDrawMOIDs(BITMAP* pTargetBitmap); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this MovableMan's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws this MovableMan's current graphical representation to a + /// BITMAP of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawHUD - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the HUDs of all MovableObject:s of this MovableMan to a BITMAP - // of choice. - // Arguments: A pointer to a BITMAP to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Which player's screen is being drawn. Tis affects which actor's HUDs - // get drawn. - // Return value: None. - + /// Draws the HUDs of all MovableObject:s of this MovableMan to a BITMAP + /// of choice. + /// @param pTargetBitmap A pointer to a BITMAP to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) + /// @param which Which player's screen is being drawn. Tis affects which actor's HUDs (default: 0) + /// get drawn. void DrawHUD(BITMAP* pTargetBitmap, const Vector& targetPos = Vector(), int which = 0, bool playerControlled = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: VerifyMOIDIndex - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Verifieis whether all elements of MOID index has correct ID. Should be used in Debug mode only. - // Arguments: None. - // Return value: None. - + /// Verifieis whether all elements of MOID index has correct ID. Should be used in Debug mode only. void VerifyMOIDIndex(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RegisterObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registers an object in a global Map collection so it could be found later with FindObjectByUniqueId - // Arguments: MO to register. - // Return value: None. - + /// Registers an object in a global Map collection so it could be found later with FindObjectByUniqueId + /// @param mo MO to register. void RegisterObject(MovableObject* mo); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UnregisterObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes an object from the global lookup collection - // Arguments: MO to remove. - // Return value: None. - + /// Removes an object from the global lookup collection + /// @param mo MO to remove. void UnregisterObject(MovableObject* mo); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FindObjectByUniqueId - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Uses a global lookup map to find an object by it's unique id. - // Arguments: Unique Id to look for. - // Return value: Object found or 0 if not found any. - + /// Uses a global lookup map to find an object by it's unique id. + /// @param id Unique Id to look for. + /// @return Object found or 0 if not found any. MovableObject* FindObjectByUniqueID(long int id) { if (m_KnownObjects.count(id) > 0) return m_KnownObjects[id]; @@ -811,88 +497,60 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetKnownObjectsCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the size of the object registry collection - // Arguments: None. - // Return value: Size of the objects registry. - + /// Returns the size of the object registry collection + /// @return Size of the objects registry. unsigned int GetKnownObjectsCount() { return m_KnownObjects.size(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSimUpdateFrameNumber - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the current sim update frame number - // Arguments: None. - // Return value: Current sim update frame number. - + /// Returns the current sim update frame number + /// @return Current sim update frame number. unsigned int GetSimUpdateFrameNumber() const { return m_SimUpdateFrameNumber; } - /// /// Gets pointers to the MOs that are within the given Box, and whose team is not ignored. - /// - /// The Box to get MOs within. - /// The team to ignore. - /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. - /// Pointers to the MOs that are within the given Box, and whose team is not ignored. + /// @param box The Box to get MOs within. + /// @param ignoreTeam The team to ignore. + /// @param getsHitByMOsOnly Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. + /// @return Pointers to the MOs that are within the given Box, and whose team is not ignored. const std::vector* GetMOsInBox(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const; - /// /// Gets pointers to the MOs that are within the given Box, and whose team is not ignored. - /// - /// The Box to get MOs within. - /// The team to ignore. - /// Pointers to the MOs that are within the given Box, and whose team is not ignored. + /// @param box The Box to get MOs within. + /// @param ignoreTeam The team to ignore. + /// @return Pointers to the MOs that are within the given Box, and whose team is not ignored. const std::vector* GetMOsInBox(const Box& box, int ignoreTeam) const { return GetMOsInBox(box, ignoreTeam, false); } - /// /// Gets pointers to the MOs that are within the given Box. - /// - /// The Box to get MOs within. - /// Pointers to the MOs that are within the given Box. + /// @param box The Box to get MOs within. + /// @return Pointers to the MOs that are within the given Box. const std::vector* GetMOsInBox(const Box& box) const { return GetMOsInBox(box, Activity::NoTeam); } - /// /// Gets pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. - /// - /// The position to check for MOs in. - /// The radius to check for MOs within. - /// The team to ignore. - /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. - /// Pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. + /// @param centre The position to check for MOs in. + /// @param radius The radius to check for MOs within. + /// @param ignoreTeam The team to ignore. + /// @param getsHitByMOsOnly Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. + /// @return Pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. const std::vector* GetMOsInRadius(const Vector& centre, float radius, int ignoreTeam, bool getsHitByMOsOnly) const; - /// /// Gets pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. - /// - /// The position to check for MOs in. - /// The radius to check for MOs within. - /// The team to ignore. - /// Pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. + /// @param centre The position to check for MOs in. + /// @param radius The radius to check for MOs within. + /// @param ignoreTeam The team to ignore. + /// @return Pointers to the MOs that are within the specified radius of the given centre position, and whose team is not ignored. const std::vector* GetMOsInRadius(const Vector& centre, float radius, int ignoreTeam) const { return GetMOsInRadius(centre, radius, ignoreTeam, false); } - /// /// Gets pointers to the MOs that are within the specified radius of the given centre position. - /// - /// The position to check for MOs in. - /// The radius to check for MOs within. - /// Pointers to the MOs that are within the specified radius of the given centre position. + /// @param centre The position to check for MOs in. + /// @param radius The radius to check for MOs within. + /// @return Pointers to the MOs that are within the specified radius of the given centre position. const std::vector* GetMOsInRadius(const Vector& centre, float radius) const { return GetMOsInRadius(centre, radius, Activity::NoTeam); } - /// /// Runs a lua function on all MOs in the simulation, including owned child MOs. - /// void RunLuaFunctionOnAllMOs(const std::string& functionName, bool includeAdded, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); - /// /// Clears all cached lua functions on all MOs, including owned child MOs. - /// void ReloadLuaScripts(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // All actors in the scene std::deque m_Actors; @@ -971,39 +629,25 @@ namespace RTE { // Global map which stores all objects so they could be foud by their unique ID std::map m_KnownObjects; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MovableMan, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this MovableMan, effectively + /// resetting the members of this abstraction level only. void Clear(); - /// /// Travels all of our MOs, updating their location/velocity/physical characteristics. - /// void Travel(); - /// /// Updates the controllers of all the actors we own. /// This is needed for a tricky reason - we want the controller from the activity to override the normal controller state /// So we need to update the controller state prior to activity, so the changes from activity are layered on top. - /// void UpdateControllers(); - /// /// Updates all things that need to be done before we update the controllers. /// This is needed because of a very awkward and ugly old code path where controllers were updated in the middle of update, and various mods relied of this behaviour for actions that were therefore delayed by a frame /// Ideally we wouldn't need this, but this is all very fragile code and I'd prefer to avoid breaking things. - /// void PreControllerUpdate(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/NetworkClient.cpp b/Source/Managers/NetworkClient.cpp index 23a5c2378f..51bba73208 100644 --- a/Source/Managers/NetworkClient.cpp +++ b/Source/Managers/NetworkClient.cpp @@ -16,8 +16,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::Clear() { m_LastInputSentTime = 0; m_ReceivedData = 0; @@ -60,8 +58,6 @@ namespace RTE { m_ServerSounds.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int NetworkClient::Initialize() { // Record the first client that connects to us so we can pass it to the ping function m_ClientID = RakNet::UNASSIGNED_SYSTEM_ADDRESS; @@ -70,8 +66,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::Connect(std::string serverName, unsigned short serverPort, std::string playerName) { g_ConsoleMan.PrintString("CLIENT: Connecting to " + serverName); @@ -85,8 +79,6 @@ namespace RTE { g_ConsoleMan.PrintString((connectionAttempt == RakNet::CONNECTION_ATTEMPT_STARTED) ? "CLIENT: Connect request sent" : "CLIENT: Unable to connect"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ConnectNAT(RakNet::SystemAddress address) { g_ConsoleMan.PrintString("CLIENT: Connecting to server through NAT"); g_ConsoleMan.PrintString(address.ToString()); @@ -97,8 +89,6 @@ namespace RTE { g_ConsoleMan.PrintString((connectionAttempt == RakNet::CONNECTION_ATTEMPT_STARTED) ? "CLIENT: Connect request sent" : "CLIENT: Unable to connect"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::Disconnect() { if (m_IsConnected || m_IsRegistered) { SendDisconnectMsg(); @@ -112,8 +102,6 @@ namespace RTE { g_ConsoleMan.PrintString("CLIENT: Disconnect"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::PerformNATPunchThrough(std::string serviceServerName, unsigned short serviceServerPort, std::string playerName, std::string serverName, std::string serverPassword) { m_UseNATPunchThroughService = true; @@ -136,8 +124,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - RakNet::SystemAddress NetworkClient::ConnectBlocking(RakNet::RakPeerInterface* rakPeer, const char* address, unsigned short port) { if (rakPeer->Connect(address, port, nullptr, 0) != RakNet::CONNECTION_ATTEMPT_STARTED) { return RakNet::UNASSIGNED_SYSTEM_ADDRESS; @@ -157,8 +143,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned char NetworkClient::GetPacketIdentifier(RakNet::Packet* packet) const { if (packet == nullptr) { return 255; @@ -171,8 +155,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::SendRegisterMsg() { MsgRegister msg = {}; msg.Id = ID_CLT_REGISTER; @@ -183,15 +165,11 @@ namespace RTE { g_ConsoleMan.PrintString("CLIENT: Registration Sent"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveAcceptedMsg() { g_ConsoleMan.PrintString("CLIENT: Registration accepted."); m_IsRegistered = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::SendDisconnectMsg() { MsgRegister msg = {}; msg.Id = ID_CLT_DISCONNECT; @@ -199,8 +177,6 @@ namespace RTE { g_ConsoleMan.PrintString("CLIENT: Disconnection Sent"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::SendServerGUIDRequest(RakNet::SystemAddress address, std::string serverName, std::string serverPassword) { MsgGetServerRequest msg = {}; msg.Id = ID_NAT_SERVER_GET_SERVER_GUID; @@ -209,8 +185,6 @@ namespace RTE { m_Client->Send((const char*)&msg, sizeof(RTE::MsgGetServerRequest), IMMEDIATE_PRIORITY, RELIABLE, 0, address, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveServerGUIDAnswer(RakNet::Packet* packet) { const MsgGetServerAnswer* msg = (MsgGetServerAnswer*)packet->data; m_ServerGUID.FromString(msg->ServerGuid); @@ -219,8 +193,6 @@ namespace RTE { g_ConsoleMan.PrintString(m_ServerGUID.ToString()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::SendInputMsg() { MsgInput msg = {}; msg.Id = ID_CLT_INPUT; @@ -266,8 +238,6 @@ namespace RTE { m_Client->Send((const char*)&msg, sizeof(msg), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, m_ServerID, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveFrameSetupMsg(RakNet::Packet* packet) { const MsgFrameSetup* frameData = (MsgFrameSetup*)packet->data; if (frameData->FrameNumber >= c_FramesToRemember) { @@ -295,8 +265,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveFrameLineMsg(RakNet::Packet* packet) { const MsgFrameLine* frameData = (MsgFrameLine*)packet->data; int lineNumber = frameData->LineNumber; @@ -337,8 +305,6 @@ namespace RTE { release_bitmap(bmp); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveFrameBoxMsg(RakNet::Packet* packet) { const MsgFrameBox* frameData = (MsgFrameBox*)packet->data; @@ -446,8 +412,6 @@ namespace RTE { release_bitmap(bmp); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::SendSceneAcceptedMsg() { MsgRegister msg = {}; msg.Id = ID_CLT_SCENE_ACCEPTED; @@ -455,8 +419,6 @@ namespace RTE { g_ConsoleMan.PrintString("CLIENT: Scene ACK Sent"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveSceneMsg(RakNet::Packet* packet) { const MsgSceneLine* frameData = (MsgSceneLine*)packet->data; if (frameData->SceneId != m_SceneID) { @@ -496,15 +458,11 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveSceneEndMsg() { g_ConsoleMan.PrintString("CLIENT: Scene received."); SendSceneAcceptedMsg(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveSceneSetupMsg(RakNet::Packet* packet) { clear_to_color(g_FrameMan.GetNetworkBackBufferIntermediateGUI8Ready(0), g_MaskColor); clear_to_color(g_FrameMan.GetNetworkBackBufferGUI8Ready(0), g_MaskColor); @@ -578,8 +536,6 @@ namespace RTE { g_ConsoleMan.PrintString("CLIENT: Scene setup accepted"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::SendSceneSetupAcceptedMsg() { MsgRegister msg; msg.Id = ID_CLT_SCENE_SETUP_ACCEPTED; @@ -587,8 +543,6 @@ namespace RTE { g_ConsoleMan.PrintString("CLIENT: Scene setup ACK Sent"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveTerrainChangeMsg(RakNet::Packet* packet) { const MsgTerrainChange* frameData = (MsgTerrainChange*)packet->data; if (frameData->SceneId != m_SceneID) { @@ -624,8 +578,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceivePostEffectsMsg(RakNet::Packet* packet) { MsgPostEffects* msg = (MsgPostEffects*)packet->data; const PostEffectNetworkData* effDataPtr = (PostEffectNetworkData*)((char*)msg + sizeof(MsgPostEffects)); @@ -644,8 +596,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveSoundEventsMsg(RakNet::Packet* packet) { MsgSoundEvents* msg = (MsgSoundEvents*)packet->data; const AudioMan::NetworkSoundData* soundDataPointer = (AudioMan::NetworkSoundData*)((char*)msg + sizeof(MsgSoundEvents)); @@ -723,8 +673,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::ReceiveMusicEventsMsg(RakNet::Packet* packet) { MsgMusicEvents* msg = (MsgMusicEvents*)packet->data; const AudioMan::NetworkMusicData* musicDataPointer = (AudioMan::NetworkMusicData*)((char*)msg + sizeof(MsgMusicEvents)); @@ -762,8 +710,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::DrawBackgrounds(BITMAP* targetBitmap) { for (int i = m_ActiveBackgroundLayers - 1; i >= 0; i--) { if (m_BackgroundBitmaps[i] != 0) { @@ -944,14 +890,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::DrawPostEffects(int frame) { g_PostProcessMan.SetNetworkPostEffectsList(0, m_PostEffects[frame]); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::DrawFrame(int frameNumber, bool useInterlacing, bool clearFramebuffer) { BITMAP* src_bmp = g_FrameMan.GetNetworkBackBufferIntermediate8Ready(0); BITMAP* dst_bmp = g_FrameMan.GetNetworkBackBuffer8Ready(0); @@ -1052,8 +994,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::Update() { HandleNetworkPackets(); @@ -1157,8 +1097,6 @@ namespace RTE { #endif } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkClient::HandleNetworkPackets() { std::string msg; diff --git a/Source/Managers/NetworkClient.h b/Source/Managers/NetworkClient.h index 91504c3f58..64791aff29 100644 --- a/Source/Managers/NetworkClient.h +++ b/Source/Managers/NetworkClient.h @@ -23,118 +23,86 @@ namespace RTE { class SoundContainer; - /// /// The centralized singleton manager of the network multiplayer client. - /// class NetworkClient : public Singleton { friend class SettingsMan; public: #pragma region Creation - /// /// Constructor method used to instantiate a NetworkClient object in system memory. Create() should be called before using the object. - /// NetworkClient() { Clear(); Initialize(); } - /// /// Makes the NetworkClient object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a NetworkClient object before deletion from system memory. - /// ~NetworkClient() { Destroy(); } - /// /// Destroys and resets (through Clear()) the NetworkClient object. - /// void Destroy() { Clear(); } #pragma endregion #pragma region Getters - /// /// Gets whether the client is connected and registered to a server. - /// - /// Whether the client is connected and registered to a server. + /// @return Whether the client is connected and registered to a server. bool IsConnectedAndRegistered() const { return m_IsConnected && m_IsRegistered; } - /// /// Gets scene width for network client. - /// - /// Current scene width. + /// @return Current scene width. int GetSceneWidth() const { return m_SceneWidth; } - /// /// Gets scene height for network client. - /// - /// Current scene height. + /// @return Current scene height. int GetSceneHeight() const { return m_SceneHeight; } - /// /// Indicates whether the scene wraps its scrolling around the X axis for network client. - /// - /// Whether the scene wraps around the X axis or not. + /// @return Whether the scene wraps around the X axis or not. bool SceneWrapsX() const { return m_SceneWrapsX; } - /// /// Get the coordinates of the center of the current frame. - /// - /// A vector containing the X/Y coordinates of the frame target. + /// @return A vector containing the X/Y coordinates of the frame target. const Vector& GetFrameTarget() const { return m_TargetPos[m_CurrentFrameNum]; } #pragma endregion #pragma region Concrete Methods - /// /// Updates the state of this NetworkClient. Supposed to be done every frame. - /// void Update(); #pragma endregion #pragma region Connection Handling - /// /// Connects the client to a server. - /// - /// Server name (or address) to connect to. - /// Server port. - /// Player name to be used in network game. + /// @param serverName Server name (or address) to connect to. + /// @param serverPort Server port. + /// @param playerName Player name to be used in network game. void Connect(std::string serverName, unsigned short serverPort, std::string playerName); - /// /// Connects the client to a server through a NAT service. - /// - /// The NAT service address. + /// @param address The NAT service address. void ConnectNAT(RakNet::SystemAddress address); - /// /// Disconnects the client from the currently connected server. - /// void Disconnect(); - /// /// Connects to a NAT service and performs punch-through. - /// - /// NAT service server name (or address) to use for punch-through. - /// NAT service server port. - /// Player name to be used in network game. - /// Server name (or address) to connect to. - /// Server password. + /// @param serviceServerName NAT service server name (or address) to use for punch-through. + /// @param serviceServerPort NAT service server port. + /// @param playerName Player name to be used in network game. + /// @param serverName Server name (or address) to connect to. + /// @param serverPassword Server password. void PerformNATPunchThrough(std::string serviceServerName, unsigned short serviceServerPort, std::string playerName, std::string serverName, std::string serverPassword); - /// /// - /// - /// - /// - /// - /// + /// @param rakPeer + /// @param address + /// @param port + /// @return RakNet::SystemAddress ConnectBlocking(RakNet::RakPeerInterface* rakPeer, const char* address, unsigned short port); #pragma endregion @@ -198,155 +166,105 @@ namespace RTE { private: #pragma region Update Breakdown - /// /// - /// void HandleNetworkPackets(); #pragma endregion #pragma region Network Event Handling - /// /// - /// - /// - /// + /// @param packet + /// @return unsigned char GetPacketIdentifier(RakNet::Packet* packet) const; - /// /// - /// void SendRegisterMsg(); - /// /// - /// void ReceiveAcceptedMsg(); - /// /// - /// void SendDisconnectMsg(); - /// /// - /// - /// - /// - /// + /// @param address + /// @param serverName + /// @param serverPassword void SendServerGUIDRequest(RakNet::SystemAddress address, std::string serverName, std::string serverPassword); - /// /// - /// - /// + /// @param packet void ReceiveServerGUIDAnswer(RakNet::Packet* packet); - /// /// - /// void SendInputMsg(); - /// /// - /// - /// + /// @param packet void ReceiveFrameSetupMsg(RakNet::Packet* packet); - /// /// - /// - /// + /// @param packet void ReceiveFrameLineMsg(RakNet::Packet* packet); - /// /// - /// - /// + /// @param packet void ReceiveFrameBoxMsg(RakNet::Packet* packet); - /// /// - /// void SendSceneAcceptedMsg(); - /// /// - /// - /// + /// @param packet void ReceiveSceneMsg(RakNet::Packet* packet); - /// /// - /// void ReceiveSceneEndMsg(); - /// /// - /// - /// + /// @param packet void ReceiveSceneSetupMsg(RakNet::Packet* packet); - /// /// - /// void SendSceneSetupAcceptedMsg(); - /// /// Receive and handle a packet of terrain change data. - /// - /// The packet to handle. + /// @param packet The packet to handle. void ReceiveTerrainChangeMsg(RakNet::Packet* packet); - /// /// Receive and handle a packet of post-effect data. - /// - /// The packet to handle. + /// @param packet The packet to handle. void ReceivePostEffectsMsg(RakNet::Packet* packet); - /// /// Receive and handle a packet of sound event data. - /// - /// The packet to handle. + /// @param packet The packet to handle. void ReceiveSoundEventsMsg(RakNet::Packet* packet); - /// /// Receive and handle a packet of music event data. - /// - /// The packet to handle. + /// @param packet The packet to handle. void ReceiveMusicEventsMsg(RakNet::Packet* packet); #pragma endregion #pragma region Drawing - /// /// - /// - /// + /// @param targetBitmap void DrawBackgrounds(BITMAP* targetBitmap); - /// /// - /// - /// + /// @param frame void DrawPostEffects(int frame); - /// /// - /// - /// - /// - /// + /// @param frameNumber + /// @param useInterlacing + /// @param clearFramebuffer void DrawFrame(int frameNumber, bool useInterlacing, bool clearFramebuffer); #pragma endregion - /// /// Gets the ping time between the client and the server. - /// - /// The ping time between the client and the server. + /// @return The ping time between the client and the server. int GetPing() const { return IsConnectedAndRegistered() ? m_Client->GetLastPing(m_ServerID) : 0; } - /// /// Clears all the member variables of this NetworkClient, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/NetworkServer.cpp b/Source/Managers/NetworkServer.cpp index c004e44263..1d8a7eb886 100644 --- a/Source/Managers/NetworkServer.cpp +++ b/Source/Managers/NetworkServer.cpp @@ -25,8 +25,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::BackgroundSendThreadFunction(NetworkServer* server, short player) { const int sleepTime = 1000000 / server->m_EncodingFps; while (server->IsServerModeEnabled() && server->IsPlayerConnected(player)) { @@ -46,8 +44,6 @@ namespace RTE { server->SetThreadExitReason(player, NetworkServer::THREAD_FINISH); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::Clear() { m_SleepWhenIdle = false; m_SimSleepWhenIdle = false; @@ -147,8 +143,6 @@ namespace RTE { m_LastPackedReceived = nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int NetworkServer::Initialize() { m_IsInServerMode = false; m_ServerPort = ""; @@ -191,8 +185,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::Destroy() { // Send a signal that server is going to shutdown m_IsInServerMode = false; @@ -218,8 +210,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool NetworkServer::ReadyForSimulation() { short playersReady = 0; short playersTotal = 0; @@ -236,8 +226,6 @@ namespace RTE { return playersReady >= playersTotal; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SetServerPort(const std::string& newPort) { bool useDefault = false; for (const char& stringChar: newPort) { @@ -250,8 +238,6 @@ namespace RTE { m_ServerPort = useDefault ? "8000" : newPort; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::Start() { RakNet::SocketDescriptor socketDescriptors[1]; socketDescriptors[0].port = atoi(m_ServerPort.c_str()); @@ -299,8 +285,6 @@ namespace RTE { m_Server->SetUnreliableTimeout(50); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::LockScene(bool isLocked) { for (int i = 0; i < c_MaxClients; i++) { if (isLocked) { @@ -311,8 +295,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ResetScene() { m_SceneID++; for (int i = 0; i < c_MaxClients; i++) { @@ -323,8 +305,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::RegisterTerrainChange(NetworkTerrainChange terrainChange) { if (m_IsInServerMode) { for (short player = 0; player < c_MaxClients; player++) { @@ -337,8 +317,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned char NetworkServer::GetPacketIdentifier(RakNet::Packet* packet) const { if (packet == 0) { return 255; @@ -351,8 +329,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveNewIncomingConnection(RakNet::Packet* packet) { std::string msg; RakNet::SystemAddress clientID; @@ -399,16 +375,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendAcceptedMsg(short player) { MsgAccepted msg; msg.Id = ID_SRV_ACCEPTED; m_Server->Send((const char*)&msg, sizeof(MsgAccepted), HIGH_PRIORITY, RELIABLE_SEQUENCED, 0, m_ClientConnections[player].ClientId, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveDisconnection(RakNet::Packet* packet) { std::string msg = "ID_CONNECTION_LOST from"; msg += packet->systemAddress.ToString(true); @@ -430,8 +402,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveRegisterMsg(RakNet::Packet* packet) { std::string msg; const MsgRegister* msgReg = (MsgRegister*)packet->data; @@ -478,8 +448,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendNATServerRegistrationMsg(RakNet::SystemAddress address) { MsgRegisterServer msg = {}; msg.Id = ID_NAT_SERVER_REGISTER_SERVER; @@ -494,8 +462,6 @@ namespace RTE { m_Server->Send((const char*)&msg, payloadSize, IMMEDIATE_PRIORITY, RELIABLE, 0, address, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveInputMsg(RakNet::Packet* packet) { const MsgInput* m = (MsgInput*)packet->data; @@ -566,8 +532,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ProcessInputMsg(short player, MsgInput msg) { if (player >= 0 && player < c_MaxClients) { Vector input; @@ -608,16 +572,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ClearInputMessages(short player) { if (player >= 0 && player < c_MaxClients) { m_InputMessages[player].clear(); } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendSoundData(short player) { std::list events; g_AudioMan.GetSoundEvents(player, events); @@ -673,8 +633,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendMusicData(short player) { std::list events; g_AudioMan.GetMusicEvents(player, events); @@ -724,8 +682,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendSceneSetupData(short player) { MsgSceneSetup msgSceneSetup = {}; msgSceneSetup.Id = ID_SRV_SCENE_SETUP; @@ -797,8 +753,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveSceneSetupDataAccepted(RakNet::Packet* packet) { short player = -1; @@ -815,8 +769,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendSceneData(short player) { // Check for congestion RakNet::RakNetStatistics rns; @@ -932,8 +884,6 @@ namespace RTE { SendSceneEndMsg(player); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ClearTerrainChangeQueue(short player) { m_Mutex[player].lock(); while (!m_PendingTerrainChanges[player].empty()) { @@ -945,8 +895,6 @@ namespace RTE { m_Mutex[player].unlock(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool NetworkServer::NeedToProcessTerrainChanges(short player) { bool result; @@ -957,8 +905,6 @@ namespace RTE { return result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ProcessTerrainChanges(short player) { m_Mutex[player].lock(); while (!m_PendingTerrainChanges[player].empty()) { @@ -1006,8 +952,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendTerrainChangeMsg(short player, NetworkTerrainChange terrainChange) { if (terrainChange.w == 1 && terrainChange.h == 1) { MsgTerrainChange msg = {}; @@ -1092,8 +1036,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ReceiveSceneAcceptedMsg(RakNet::Packet* packet) { for (short player = 0; player < c_MaxClients; player++) { if (m_ClientConnections[player].ClientId == packet->systemAddress) { @@ -1102,16 +1044,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendSceneEndMsg(short player) { MsgSceneEnd msg = {}; msg.Id = ID_SRV_SCENE_END; m_Server->Send((const char*)&msg, sizeof(MsgSceneSetup), HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_ClientConnections[player].ClientId, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::CreateBackBuffer(short player, int w, int h) { m_BackBuffer8[player] = create_bitmap_ex(8, w, h); m_BackBufferGUI8[player] = create_bitmap_ex(8, w, h); @@ -1128,8 +1066,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::ClearBackBuffer(int player, int w, int h) { clear_to_color(m_BackBuffer8[player], ColorKeys::g_MaskColor); clear_to_color(m_BackBufferGUI8[player], ColorKeys::g_MaskColor); @@ -1143,8 +1079,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::DestroyBackBuffer(short player) { if (m_BackBuffer8) { destroy_bitmap(m_BackBuffer8[player]); @@ -1165,8 +1099,6 @@ namespace RTE { m_PixelLineBuffersGUIPrev[player] = nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendFrameSetupMsg(short player) { MsgFrameSetup msgFrameSetup; msgFrameSetup.Id = ID_SRV_FRAME_SETUP; @@ -1199,8 +1131,6 @@ namespace RTE { m_SendSceneData[player] = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::SendPostEffectData(short player) { std::list effects; g_PostProcessMan.GetNetworkPostEffectsList(player, effects); @@ -1255,8 +1185,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int NetworkServer::SendFrame(short player) { long long currentTicks = g_TimerMan.GetRealTickCount(); double fps = static_cast(m_EncodingFps); @@ -1651,8 +1579,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::UpdateStats(short player) { long long currentTicks = g_TimerMan.GetRealTickCount(); @@ -1690,8 +1616,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::DrawStatisticsData() { int midX = g_WindowMan.GetResX() / 2; @@ -1841,8 +1765,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - RakNet::SystemAddress NetworkServer::ConnectBlocking(RakNet::RakPeerInterface* rakPeer, const char* address, unsigned short port) { if (rakPeer->Connect(address, port, 0, 0) != RakNet::CONNECTION_ATTEMPT_STARTED) { return RakNet::UNASSIGNED_SYSTEM_ADDRESS; @@ -1863,8 +1785,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::Update(bool processInput) { HandleNetworkPackets(); @@ -1967,8 +1887,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void NetworkServer::HandleNetworkPackets() { for (RakNet::Packet* packet = m_Server->Receive(); packet; m_Server->DeallocatePacket(packet), packet = m_Server->Receive()) { m_LastPackedReceived->Reset(); diff --git a/Source/Managers/NetworkServer.h b/Source/Managers/NetworkServer.h index 8a14d3111f..abff481a44 100644 --- a/Source/Managers/NetworkServer.h +++ b/Source/Managers/NetworkServer.h @@ -21,25 +21,19 @@ namespace RTE { class Timer; - /// /// The centralized singleton manager of the network multiplayer server. - /// class NetworkServer : public Singleton { friend class SettingsMan; public: - /// /// - /// enum NetworkServerStats { STAT_CURRENT = 0, STAT_SHOWN, MAX_STAT_RECORDS = 5 }; - /// /// - /// enum ThreadExitReasons { NORMAL = 0, THREAD_FINISH, @@ -49,9 +43,7 @@ namespace RTE { LOCKED }; - /// /// Struct for registering terrain change events for network transmission. - /// struct NetworkTerrainChange { int x; int y; @@ -62,140 +54,98 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a NetworkServer object in system memory. This will call Create() so it shouldn't be called after. - /// NetworkServer() { Clear(); Initialize(); } - /// /// Makes the NetworkServer object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a NetworkServer object before deletion from system memory. - /// ~NetworkServer() { Destroy(); } - /// /// Destroys and resets (through Clear()) the NetworkServer object. - /// void Destroy(); #pragma endregion #pragma region Getters and Setters - /// /// Gets whether server mode is enabled or not. - /// - /// Whether server mode is enabled or not. + /// @return Whether server mode is enabled or not. bool IsServerModeEnabled() const { return m_IsInServerMode; } - /// /// Enables server mode. - /// void EnableServerMode() { m_IsInServerMode = true; } - /// /// - /// - /// + /// @return bool ReadyForSimulation(); - /// /// Gets the network player's name. - /// - /// The player to check for. - /// A string with the network player's name. + /// @param player The player to check for. + /// @return A string with the network player's name. std::string& GetPlayerName(short player) { return m_ClientConnections[player].PlayerName; } - /// /// Gets whether the specified player is connected to the server or not. - /// - /// The player to check for. - /// Whether the player is connected to the server or not. + /// @param player The player to check for. + /// @return Whether the player is connected to the server or not. bool IsPlayerConnected(short player) const { return m_ClientConnections[player].IsActive; } - /// /// Sets the port this server will be using. - /// - /// The new port to set. + /// @param newPort The new port to set. void SetServerPort(const std::string& newPort); - /// /// Sets whether interlacing is used to reduce bandwidth usage or not. - /// - /// Whether to use interlacing or not. + /// @param newMode Whether to use interlacing or not. void SetInterlacingMode(bool newMode) { m_UseInterlacing = newMode; } - /// /// Sets the duration this thread should be put to sleep for in milliseconds. - /// - /// The player to set for. - /// Milliseconds to sleep for. + /// @param player The player to set for. + /// @param msecs Milliseconds to sleep for. void SetMSecsToSleep(short player, int msecs) { m_MSecsToSleep[player] = msecs; }; - /// /// Gets the ping time of the specified player. - /// - /// The player to get for. - /// The ping time of the player. + /// @param player The player to get for. + /// @return The ping time of the player. unsigned short GetPing(short player) const { return m_Ping[player]; } - /// /// Gets whether server puts threads to sleep if it didn't receive anything for 10 seconds to reduce CPU load. - /// - /// Whether threads will be put to sleep when server isn't receiving any data or not. + /// @return Whether threads will be put to sleep when server isn't receiving any data or not. bool GetServerSleepWhenIdle() const { return m_SleepWhenIdle; } - /// /// Gets whether the server will try to put the thread to sleep to reduce CPU load if the sim frame took less time to complete than it should at 30 fps. - /// - /// Whether threads will be put to sleep if server completed frame faster than it normally should or not. + /// @return Whether threads will be put to sleep if server completed frame faster than it normally should or not. bool GetServerSimSleepWhenIdle() const { return m_SimSleepWhenIdle; } #pragma endregion #pragma region Concrete Methods - /// /// Start server, open ports etc. - /// void Start(); - /// /// Updates the state of this NetworkServer. Supposed to be done every frame before drawing. - /// - /// Whether to process packets of player input data or not. + /// @param processInput Whether to process packets of player input data or not. void Update(bool processInput = false); #pragma endregion #pragma region Network Scene Handling - /// /// - /// - /// + /// @param isLocked void LockScene(bool isLocked); - /// /// - /// void ResetScene(); - /// /// - /// - /// + /// @param terrainChange void RegisterTerrainChange(NetworkTerrainChange terrainChange); #pragma endregion protected: - /// /// - /// struct ClientConnection { bool IsActive; //!< RakNet::SystemAddress ClientId; //!< @@ -246,11 +196,9 @@ namespace RTE { bool m_UseDeltaCompression; //!< Whether to use delta compression methods and conserve bandwidth. int m_HighCompressionLevel; //!< Compression level. 10 is optimal, 12 is highest. - /// /// Acceleration factor, higher values consume more bandwidth but less CPU. /// The larger the acceleration value, the faster the algorithm, but also lesser the compression. It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. /// An acceleration value of "1" is the same as regular LZ4_compress_default(). Values <= 0 will be replaced by ACCELERATION_DEFAULT(currently == 1, see lz4 documentation). - /// int m_FastAccelerationFactor; bool m_UseInterlacing; //!< Use interlacing to heavily reduce bandwidth usage at the cost of visual degradation (unusable at 30 fps, but may be suitable at 60 fps). @@ -293,10 +241,8 @@ namespace RTE { std::unique_ptr m_LastPackedReceived; //!< - /// /// Transmit frames as blocks instead of lines. Provides better compression at the cost of higher CPU usage. /// Though the compression is quite high it is recommended that Width * Height are less than MTU size or about 1500 bytes or packets may be fragmented by network hardware or dropped completely. - /// bool m_TransmitAsBoxes; int m_BoxWidth; //!< Width of the transmitted CPU block. Different values may improve bandwidth usage. int m_BoxHeight; //!< Height of the transmitted CPU block. Different values may improve bandwidth usage. @@ -344,258 +290,182 @@ namespace RTE { private: #pragma region Thread Handling - /// /// - /// - /// - /// + /// @param server + /// @param player static void BackgroundSendThreadFunction(NetworkServer* server, short player); - /// /// - /// - /// - /// + /// @param player + /// @param reason void SetThreadExitReason(short player, int reason) { m_ThreadExitReason[player] = reason; }; #pragma endregion #pragma region Network Event Handling - /// /// - /// - /// - /// + /// @param packet + /// @return unsigned char GetPacketIdentifier(RakNet::Packet* packet) const; - /// /// - /// - /// + /// @param packet void ReceiveNewIncomingConnection(RakNet::Packet* packet); - /// /// - /// - /// + /// @param player void SendAcceptedMsg(short player); - /// /// - /// - /// + /// @param packet void ReceiveDisconnection(RakNet::Packet* packet); - /// /// - /// - /// + /// @param packet void ReceiveRegisterMsg(RakNet::Packet* packet); - /// /// - /// - /// + /// @param addr void SendNATServerRegistrationMsg(RakNet::SystemAddress address); - /// /// - /// - /// + /// @param packet void ReceiveInputMsg(RakNet::Packet* packet); - /// /// - /// - /// - /// + /// @param player + /// @param msg void ProcessInputMsg(short player, MsgInput msg); - /// /// - /// - /// + /// @param player void ClearInputMessages(short player); - /// /// - /// - /// + /// @param player void SendSoundData(short player); - /// /// - /// - /// + /// @param player void SendMusicData(short player); #pragma endregion #pragma region Network Scene Handling - /// /// - /// - /// - /// + /// @param player + /// @return bool IsSceneAvailable(short player) const { return m_SceneAvailable[player]; } - /// /// - /// - /// - /// + /// @param player + /// @return bool NeedToSendSceneSetupData(short player) const { return m_SendSceneSetupData[player]; } - /// /// - /// - /// + /// @param player void SendSceneSetupData(short player); - /// /// - /// - /// + /// @param packet void ReceiveSceneSetupDataAccepted(RakNet::Packet* packet); - /// /// - /// - /// - /// + /// @param player + /// @return bool NeedToSendSceneData(short player) const { return m_SendSceneData[player]; } - /// /// - /// - /// + /// @param player void SendSceneData(short player); - /// /// - /// - /// + /// @param player void ClearTerrainChangeQueue(short player); - /// /// - /// - /// - /// + /// @param player + /// @return bool NeedToProcessTerrainChanges(short player); - /// /// - /// - /// + /// @param player void ProcessTerrainChanges(short player); - /// /// - /// - /// - /// + /// @param player + /// @param terrainChange void SendTerrainChangeMsg(short player, NetworkTerrainChange terrainChange); - /// /// - /// - /// + /// @param packet void ReceiveSceneAcceptedMsg(RakNet::Packet* packet); - /// /// - /// - /// + /// @param player void SendSceneEndMsg(short player); #pragma endregion #pragma region Network Frame Handling and Drawing - /// /// - /// - /// - /// - /// + /// @param player + /// @param w + /// @param h void CreateBackBuffer(short player, int w, int h); - /// /// - /// - /// - /// - /// + /// @param player + /// @param w + /// @param h void ClearBackBuffer(int player, int w, int h); - /// /// - /// - /// + /// @param player void DestroyBackBuffer(short player); - /// /// - /// - /// + /// @param player void SendFrameSetupMsg(short player); - /// /// - /// - /// - /// + /// @param player + /// @return bool SendFrameData(short player) const { return m_SendFrameData[player]; } - /// /// - /// - /// + /// @param player void SendPostEffectData(short player); - /// /// - /// - /// - /// + /// @param player + /// @return int SendFrame(short player); #pragma endregion #pragma region Network Stats Handling - /// /// - /// - /// + /// @param player void UpdateStats(short player); - /// /// - /// void DrawStatisticsData(); #pragma endregion #pragma region Update Breakdown - /// /// - /// void HandleNetworkPackets(); #pragma endregion - /// /// Gets the Globally Unique Identifier of the server. - /// - /// The GUID of the server. + /// @return The GUID of the server. RakNet::RakNetGUID GetServerGUID() const { return m_Server->GetGuidFromSystemAddress(RakNet::UNASSIGNED_SYSTEM_ADDRESS); } - /// /// - /// - /// - /// - /// - /// + /// @param rakPeer + /// @param address + /// @param port + /// @return RakNet::SystemAddress ConnectBlocking(RakNet::RakPeerInterface* rakPeer, const char* address, unsigned short port); - /// /// Clears all the member variables of this NetworkServer, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/PerformanceMan.cpp b/Source/Managers/PerformanceMan.cpp index a0e20ea452..50d28a95e0 100644 --- a/Source/Managers/PerformanceMan.cpp +++ b/Source/Managers/PerformanceMan.cpp @@ -13,8 +13,6 @@ namespace RTE { thread_local std::array s_PerfMeasureStart; //!< Current measurement start time in microseconds. thread_local std::array s_PerfMeasureStop; //!< Current measurement stop time in microseconds. - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::Clear() { m_ShowPerfStats = false; m_AdvancedPerfStats = true; @@ -31,8 +29,6 @@ namespace RTE { m_CurrentPing = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::Initialize() { m_SimUpdateTimer = std::make_unique(); @@ -44,21 +40,15 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::StartPerformanceMeasurement(PerformanceCounters counter) { s_PerfMeasureStart[counter] = g_TimerMan.GetAbsoluteTime(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::StopPerformanceMeasurement(PerformanceCounters counter) { s_PerfMeasureStop[counter] = g_TimerMan.GetAbsoluteTime(); AddPerformanceSample(counter, s_PerfMeasureStop[counter] - s_PerfMeasureStart[counter]); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::NewPerformanceSample() { m_Sample++; if (m_Sample >= c_MaxSamples) { @@ -71,8 +61,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::CalculateSamplePercentages() { for (int counter = 0; counter < PerformanceCounters::PerfCounterCount; ++counter) { int samplePercentage = static_cast(static_cast(m_PerfData[counter][m_Sample]) / static_cast(m_PerfData[counter][PerformanceCounters::SimTotal]) * 100); @@ -80,8 +68,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - uint64_t PerformanceMan::GetPerformanceCounterAverage(PerformanceCounters counter) const { uint64_t totalPerformanceMeasurement = 0; int sample = m_Sample; @@ -95,8 +81,6 @@ namespace RTE { return totalPerformanceMeasurement / c_Average; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::CalculateTimeAverage(std::deque& timeMeasurements, float& avgResult, float newTimeMeasurement) const { timeMeasurements.emplace_back(newTimeMeasurement); while (timeMeasurements.size() > c_MSPAverageSampleSize) { @@ -109,16 +93,12 @@ namespace RTE { avgResult /= static_cast(timeMeasurements.size()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::UpdateMSPF(long long measuredUpdateTime, long long measuredDrawTime) { CalculateTimeAverage(m_MSPUs, m_MSPUAverage, static_cast(measuredUpdateTime / 1000)); CalculateTimeAverage(m_MSPDs, m_MSPDAverage, static_cast(measuredDrawTime / 1000)); CalculateTimeAverage(m_MSPFs, m_MSPFAverage, static_cast((measuredUpdateTime + measuredDrawTime) / 1000)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::Draw(BITMAP* bitmapToDrawTo) { if (m_ShowPerfStats) { AllegroBitmap drawBitmap(bitmapToDrawTo); @@ -176,8 +156,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::DrawPeformanceGraphs(AllegroBitmap& bitmapToDrawTo) { CalculateSamplePercentages(); @@ -228,15 +206,11 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::DrawCurrentPing() const { AllegroBitmap allegroBitmap(g_FrameMan.GetBackBuffer8()); g_FrameMan.GetLargeFont()->DrawAligned(&allegroBitmap, g_FrameMan.GetBackBuffer8()->w - 25, g_FrameMan.GetBackBuffer8()->h - 14, "PING: " + std::to_string(m_CurrentPing), GUIFont::Right); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PerformanceMan::UpdateSortedScriptTimings(const std::unordered_map& scriptTimings) { std::vector> sortedScriptTimings; for (auto it = scriptTimings.begin(); it != scriptTimings.end(); it++) { diff --git a/Source/Managers/PerformanceMan.h b/Source/Managers/PerformanceMan.h index 2fd695c193..0b1c1bb609 100644 --- a/Source/Managers/PerformanceMan.h +++ b/Source/Managers/PerformanceMan.h @@ -12,16 +12,12 @@ namespace RTE { class AllegroBitmap; - /// /// Singleton manager responsible for all performance stats counting and drawing. - /// class PerformanceMan : public Singleton { friend class SettingsMan; public: - /// /// Enumeration of all available performance counters. - /// enum PerformanceCounters { SimTotal = 0, ActorsAI, @@ -34,105 +30,73 @@ namespace RTE { PerfCounterCount }; - /// /// Used to store Lua script execution timing information. - /// struct ScriptTiming { long long m_Time; int m_CallCount; }; #pragma region Creation - /// /// Constructor method used to instantiate a PerformanceMan object in system memory. Create() should be called before using the object. - /// PerformanceMan() { Clear(); } - /// /// Makes the PerformanceMan object ready for use. - /// void Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a PerformanceMan object before deletion from system memory. - /// ~PerformanceMan() { Destroy(); } - /// /// Destroys and resets (through Clear()) the PerformanceMan object. - /// void Destroy() { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Tells whether to display the performance stats on-screen or not. - /// - /// Whether to show the performance stats or not. + /// @return Whether to show the performance stats or not. bool IsShowingPerformanceStats() const { return m_ShowPerfStats; } - /// /// Sets whether to display the performance stats on-screen or not. - /// - /// Whether to show the performance stats or not. + /// @param showStats Whether to show the performance stats or not. void ShowPerformanceStats(bool showStats = true) { m_ShowPerfStats = showStats; } - /// /// Tells whether to display the performance graphs on-screen or not. - /// - /// Whether to show the performance graphs or not. + /// @return Whether to show the performance graphs or not. bool AdvancedPerformanceStatsEnabled() const { return m_AdvancedPerfStats; } - /// /// Sets whether to display the performance graphs on-screen or not. - /// - /// Whether to show the performance graphs or not. + /// @param showGraphs Whether to show the performance graphs or not. void ShowAdvancedPerformanceStats(bool showGraphs = true) { m_AdvancedPerfStats = showGraphs; } - /// /// Gets the average of the MSPU reading buffer, calculated each update. - /// - /// The average value of the MSPU reading buffer. + /// @return The average value of the MSPU reading buffer. float GetMSPSUAverage() const { return m_MSPSUAverage; } - /// /// Gets the average of the MSPF reading buffer, calculated each frame. - /// - /// The average value of the MSPF reading buffer. + /// @return The average value of the MSPF reading buffer. float GetMSPFAverage() const { return m_MSPFAverage; } #pragma endregion #pragma region Performance Counter Handling - /// /// Moves sample counter to next sample and clears it's values. - /// void NewPerformanceSample(); - /// /// Saves current absolute time in microseconds as a start of performance measurement. - /// - /// Counter to start measurement for. + /// @param counter Counter to start measurement for. void StartPerformanceMeasurement(PerformanceCounters counter); - /// /// Saves current absolute time in microseconds as an end of performance measurement. The difference is added to the value of current performance sample. - /// - /// Counter to stop and updated measurement for. + /// @param counter Counter to stop and updated measurement for. void StopPerformanceMeasurement(PerformanceCounters counter); - /// /// Sets the current ping value to display. - /// - /// Ping value to display. + /// @param ping Ping value to display. void SetCurrentPing(int ping) { m_CurrentPing = ping; } #pragma endregion #pragma region Concrete Methods - /// /// Clears current performance timings. - /// void ResetPerformanceTimings() { m_MSPSUs.clear(); m_MSPFs.clear(); @@ -140,41 +104,29 @@ namespace RTE { m_MSPDs.clear(); } - /// /// Resets the sim update timer. - /// void ResetSimUpdateTimer() const { m_SimUpdateTimer->Reset(); } - /// /// Updates the frame time measurements and recalculates the averages. Supposed to be done every game loop iteration. - /// - /// The total sim update time measured in the game loop iteration. - /// The total draw time measured in the game loop iteration. + /// @param measuredUpdateTime The total sim update time measured in the game loop iteration. + /// @param measuredDrawTime The total draw time measured in the game loop iteration. void UpdateMSPF(long long measuredUpdateTime, long long measuredDrawTime); - /// /// Updates the individual sim update time measurements and recalculates the average. Supposed to be done every sim update. - /// void UpdateMSPSU() { CalculateTimeAverage(m_MSPSUs, m_MSPSUAverage, static_cast(m_SimUpdateTimer->GetElapsedRealTimeMS())); m_SimUpdateTimer->Reset(); } - /// /// Draws the performance stats to the screen. - /// - /// The BITMAP to draw the performance stats to. + /// @param bitmapToDrawTo The BITMAP to draw the performance stats to. void Draw(BITMAP* bitmapToDrawTo); - /// /// Draws the current ping value to the screen. - /// void DrawCurrentPing() const; #pragma endregion - /// /// Updates m_SortedScriptTimings so PerformanceMan::Draw() can list how long scripts took. - /// void UpdateSortedScriptTimings(const std::unordered_map& scriptTimings); protected: @@ -216,42 +168,30 @@ namespace RTE { private: #pragma region Performance Counter Handling - /// /// Adds provided value to current sample of specified performance counter. - /// - /// Counter to update. - /// Value to add to this counter. + /// @param counter Counter to update. + /// @param value Value to add to this counter. void AddPerformanceSample(PerformanceCounters counter, uint64_t value) { m_PerfData[counter].at(m_Sample) += value; } - /// /// Calculates current sample's percentages from SIM_TOTAL for all performance counters and stores them to m_PerfPercenrages. - /// void CalculateSamplePercentages(); - /// /// Returns an average value of c_Average last samples for specified performance counter. - /// - /// Counter to get average value from. - /// An average value for specified counter. + /// @param counter Counter to get average value from. + /// @return An average value for specified counter. uint64_t GetPerformanceCounterAverage(PerformanceCounters counter) const; #pragma endregion - /// /// Stores the new time measurement into the specified deque, recalculates the average and stores it in the specified variable. - /// - /// The deque of time measurements to store the new measurement in and to recalculate the average with. - /// The variable the recalculated average should be stored in. - /// The new time measurement to store. + /// @param timeMeasurements The deque of time measurements to store the new measurement in and to recalculate the average with. + /// @param avgResult The variable the recalculated average should be stored in. + /// @param newTimeMeasurement The new time measurement to store. void CalculateTimeAverage(std::deque& timeMeasurements, float& avgResult, float newTimeMeasurement) const; - /// /// Draws the performance graphs to the screen. This will be called by Draw() if advanced performance stats are enabled. - /// void DrawPeformanceGraphs(AllegroBitmap& bitmapToDrawTo); - /// /// Clears all the member variables of this PerformanceMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/PostProcessMan.cpp b/Source/Managers/PostProcessMan.cpp index 67b2a2865e..8ee4f74340 100644 --- a/Source/Managers/PostProcessMan.cpp +++ b/Source/Managers/PostProcessMan.cpp @@ -16,8 +16,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::Clear() { m_PostScreenEffects.clear(); m_PostSceneEffects.clear(); @@ -41,8 +39,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PostProcessMan::Initialize() { InitializeGLPointers(); CreateGLBackBuffers(); @@ -72,8 +68,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::InitializeGLPointers() { glGenTextures(1, &m_BackBuffer8); glGenTextures(1, &m_BackBuffer32); @@ -94,8 +88,6 @@ namespace RTE { glBindVertexArray(0); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::DestroyGLPointers() { glDeleteTextures(1, &m_BackBuffer8); glDeleteTextures(1, &m_BackBuffer32); @@ -110,8 +102,6 @@ namespace RTE { glDeleteBuffers(1, &m_VertexBuffer); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::CreateGLBackBuffers() { glBindTexture(GL_TEXTURE_2D, m_BackBuffer8); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g_FrameMan.GetBackBuffer8()->w, g_FrameMan.GetBackBuffer8()->h, 0, GL_RED, GL_UNSIGNED_BYTE, 0); @@ -131,8 +121,6 @@ namespace RTE { m_ProjectionMatrix = glm::ortho(0.0F, static_cast(g_FrameMan.GetBackBuffer8()->w), 0.0F, static_cast(g_FrameMan.GetBackBuffer8()->h), -1.0F, 1.0F); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::UpdatePalette() { glBindTexture(GL_TEXTURE_1D, m_Palette8Texture); std::array palette; @@ -148,8 +136,6 @@ namespace RTE { glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::LazyInitBitmap(BITMAP* bitmap) { m_BitmapTextures.emplace_back(new GLBitmapInfo); glGenTextures(1, &m_BitmapTextures.back()->m_Texture); @@ -163,8 +149,6 @@ namespace RTE { glGenerateMipmap(GL_TEXTURE_2D); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::Destroy() { for (std::pair tempBitmapEntry: m_TempEffectBitmaps) { destroy_bitmap(tempBitmapEntry.second); @@ -175,8 +159,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::AdjustEffectsPosToPlayerScreen(int playerScreen, BITMAP* targetBitmap, const Vector& targetBitmapOffset, std::list& screenRelativeEffectsList, std::list& screenRelativeGlowBoxesList) { int screenOcclusionOffsetX = g_CameraMan.GetScreenOcclusion(playerScreen).GetFloorIntX(); int screenOcclusionOffsetY = g_CameraMan.GetScreenOcclusion(playerScreen).GetFloorIntY(); @@ -203,8 +185,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::RegisterPostEffect(const Vector& effectPos, BITMAP* effect, size_t hash, int strength, float angle) { // These effects get applied when there's a drawn frame that followed one or more sim updates. // They are not only registered on drawn sim updates; flashes and stuff could be missed otherwise if they occur on undrawn sim updates. @@ -214,8 +194,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PostProcessMan::GetPostScreenEffectsWrapped(const Vector& boxPos, int boxWidth, int boxHeight, std::list& effectsList, int team) { bool found = false; @@ -248,8 +226,6 @@ namespace RTE { return found; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* PostProcessMan::GetTempEffectBitmap(BITMAP* bitmap) const { // Get the largest dimension of the bitmap and convert it to a multiple of 16, i.e. 16, 32, etc int bitmapSizeNeeded = static_cast(std::ceil(static_cast(std::max(bitmap->w, bitmap->h)) / 16.0F)) * 16; @@ -263,8 +239,6 @@ namespace RTE { return correspondingBitmapSizeEntry->second; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::RegisterGlowDotEffect(const Vector& effectPos, DotGlowColor color, int strength) { // These effects only apply only once per drawn sim update, and only on the first frame drawn after one or more sim updates if (color != NoDot && g_TimerMan.DrawnSimUpdate() && g_TimerMan.SimUpdatesSinceDrawn() >= 0) { @@ -272,8 +246,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PostProcessMan::GetGlowAreasWrapped(const Vector& boxPos, int boxWidth, int boxHeight, std::list& areaList) const { bool foundAny = false; Vector intRectPosRelativeToBox; @@ -302,8 +274,6 @@ namespace RTE { return foundAny; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::GetNetworkPostEffectsList(int whichScreen, std::list& outputList) { ScreenRelativeEffectsMutex.at(whichScreen).lock(); outputList.clear(); @@ -313,8 +283,6 @@ namespace RTE { ScreenRelativeEffectsMutex.at(whichScreen).unlock(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::SetNetworkPostEffectsList(int whichScreen, std::list& inputList) { ScreenRelativeEffectsMutex.at(whichScreen).lock(); m_ScreenRelativeEffects.at(whichScreen).clear(); @@ -324,8 +292,6 @@ namespace RTE { ScreenRelativeEffectsMutex.at(whichScreen).unlock(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PostProcessMan::GetPostScreenEffects(Vector boxPos, int boxWidth, int boxHeight, std::list& effectsList, int team) { bool found = false; bool unseen = false; @@ -347,8 +313,6 @@ namespace RTE { return found; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PostProcessMan::GetPostScreenEffects(int left, int top, int right, int bottom, std::list& effectsList, int team) { bool found = false; bool unseen = false; @@ -368,8 +332,6 @@ namespace RTE { return found; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* PostProcessMan::GetDotGlowEffect(DotGlowColor whichColor) const { switch (whichColor) { case NoDot: @@ -386,8 +348,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - size_t PostProcessMan::GetDotGlowEffectHash(DotGlowColor whichColor) const { switch (whichColor) { case NoDot: @@ -404,8 +364,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::PostProcess() { UpdatePalette(); @@ -449,8 +407,6 @@ namespace RTE { m_PostScreenEffects.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::DrawDotGlowEffects() { int startX = 0; int startY = 0; @@ -513,8 +469,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PostProcessMan::DrawPostScreenEffects() { BITMAP* effectBitmap = nullptr; float effectPosX = 0; diff --git a/Source/Managers/PostProcessMan.h b/Source/Managers/PostProcessMan.h index a2dc7a87df..b9c6ce3d69 100644 --- a/Source/Managers/PostProcessMan.h +++ b/Source/Managers/PostProcessMan.h @@ -11,16 +11,12 @@ #define g_PostProcessMan PostProcessMan::Instance() namespace RTE { - /// /// Struct for storing GL information in the BITMAP->extra field. - /// struct GLBitmapInfo { GLuint m_Texture; }; - /// /// Structure for storing a post-process screen effect to be applied at the last stage of 32bpp rendering. - /// struct PostEffect { BITMAP* m_Bitmap = nullptr; //!< The bitmap to blend, not owned. size_t m_BitmapHash = 0; //!< Hash used to transmit glow events over the network. @@ -28,59 +24,41 @@ namespace RTE { int m_Strength = 128; //!< Scalar float for how hard to blend it in, 0 - 255. Vector m_Pos; //!< Post effect position. Can be relative to the scene, or to the screen, depending on context. - /// /// Constructor method used to instantiate a PostEffect object in system memory. - /// PostEffect(const Vector& pos, BITMAP* bitmap, size_t bitmapHash, int strength, float angle) : m_Bitmap(bitmap), m_BitmapHash(bitmapHash), m_Angle(angle), m_Strength(strength), m_Pos(pos) {} }; - /// /// Singleton manager responsible for all 32bpp post-process effect drawing. - /// class PostProcessMan : public Singleton { public: #pragma region Creation - /// /// Constructor method used to instantiate a PostProcessMan object in system memory. Create() should be called before using the object. - /// PostProcessMan() { Clear(); } - /// /// Makes the PostProcessMan object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Initialize(); - /// /// (Re-)Initializes the GL backbuffers to the current render resolution for post-processing. - /// void CreateGLBackBuffers(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a PostProcessMan object before deletion from system memory. - /// ~PostProcessMan() { Destroy(); } - /// /// Destroys and resets (through Clear()) the PostProcessMan object. - /// void Destroy(); - /// /// Clears the list of registered post-processing screen effects and glow boxes. - /// void ClearScreenPostEffects() { m_PostScreenEffects.clear(); m_PostScreenGlowBoxes.clear(); } - /// /// Clears the list of registered post-processing scene effects and glow areas. - /// void ClearScenePostEffects() { m_PostSceneEffects.clear(); m_GlowAreas.clear(); @@ -88,114 +66,90 @@ namespace RTE { #pragma endregion #pragma region Concrete Methods - /// /// Takes the current state of the 8bpp back-buffer, copies it, and adds post-processing effects on top like glows etc. - /// void PostProcess(); - /// /// Adjusts the offsets of all effects relative to the specified player screen and adds them to the total screen effects list so they can be drawn in PostProcess(). - /// - /// Player screen to adjust effect offsets for. - /// Bitmap representing the player screen. - /// The position of the specified player's draw screen on the backbuffer. - /// List of the specified player's accumulated post effects for this frame. - /// List of the specified player's accumulated glow boxes for this frame. + /// @param playerScreen Player screen to adjust effect offsets for. + /// @param targetBitmap Bitmap representing the player screen. + /// @param targetBitmapOffset The position of the specified player's draw screen on the backbuffer. + /// @param screenRelativeEffectsList List of the specified player's accumulated post effects for this frame. + /// @param screenRelativeGlowBoxesList List of the specified player's accumulated glow boxes for this frame. void AdjustEffectsPosToPlayerScreen(int playerScreen, BITMAP* targetBitmap, const Vector& targetBitmapOffset, std::list& screenRelativeEffectsList, std::list& screenRelativeGlowBoxesList); #pragma endregion #pragma region Post Effect Handling - /// /// Registers a post effect to be added at the very last stage of 32bpp rendering by the FrameMan. - /// - /// The absolute scene coordinates of the center of the effect. - /// A 32bpp BITMAP screen should be drawn centered on the above scene location in the final frame buffer. Ownership is NOT transferred! - /// Hash value of the effect for transmitting over the network. - /// The intensity level this effect should have when blended in post. 0 - 255. - /// The angle this effect should be rotated at in radians. + /// @param effectPos The absolute scene coordinates of the center of the effect. + /// @param effect A 32bpp BITMAP screen should be drawn centered on the above scene location in the final frame buffer. Ownership is NOT transferred! + /// @param hash Hash value of the effect for transmitting over the network. + /// @param strength The intensity level this effect should have when blended in post. 0 - 255. + /// @param angle The angle this effect should be rotated at in radians. void RegisterPostEffect(const Vector& effectPos, BITMAP* effect, size_t hash, int strength = 255, float angle = 0); - /// /// Gets all screen effects that are located within a box in the scene. /// Their coordinates will be returned relative to the upper left corner of the box passed in here. Wrapping of the box will be taken care of. - /// - /// The top left coordinates of the box to get post effects for. - /// The width of the box. - /// The height of the box. - /// The list to add the screen effects that fall within the box to. The coordinates of the effects returned here will be relative to the boxPos passed in above. - /// The team whose unseen layer should obscure the screen effects here. - /// Whether any active post effects were found in that box. + /// @param boxPos The top left coordinates of the box to get post effects for. + /// @param boxWidth The width of the box. + /// @param boxHeight The height of the box. + /// @param effectsList The list to add the screen effects that fall within the box to. The coordinates of the effects returned here will be relative to the boxPos passed in above. + /// @param team The team whose unseen layer should obscure the screen effects here. + /// @return Whether any active post effects were found in that box. bool GetPostScreenEffectsWrapped(const Vector& boxPos, int boxWidth, int boxHeight, std::list& effectsList, int team = -1); - /// /// Gets a temporary bitmap of specified size to rotate post effects in. - /// - /// Size of bitmap to get. - /// Pointer to the temporary bitmap. + /// @param bitmapSize Size of bitmap to get. + /// @return Pointer to the temporary bitmap. BITMAP* GetTempEffectBitmap(BITMAP* bitmap) const; #pragma endregion #pragma region Post Pixel Glow Handling - /// /// Registers a specific IntRect to be post-processed and have special pixel colors lit up by glow effects in it. - /// - /// The IntRect to have special color pixels glow in, in scene coordinates. + /// @param glowArea The IntRect to have special color pixels glow in, in scene coordinates. void RegisterGlowArea(const IntRect& glowArea) { if (g_TimerMan.DrawnSimUpdate() && g_TimerMan.SimUpdatesSinceDrawn() >= 0) { m_GlowAreas.push_back(glowArea); } } - /// /// Creates an IntRect and registers it to be post-processed and have special pixel colors lit up by glow effects in it. - /// - /// The center of the IntRect. - /// The radius around it to add as an area. + /// @param center The center of the IntRect. + /// @param radius The radius around it to add as an area. void RegisterGlowArea(const Vector& center, float radius) { RegisterGlowArea(IntRect(static_cast(center.m_X - radius), static_cast(center.m_Y - radius), static_cast(center.m_X + radius), static_cast(center.m_Y + radius))); } - /// /// Registers a specific glow dot effect to be added at the very last stage of 32bpp rendering by the FrameMan. - /// - /// The absolute scene coordinates of the center of the effect. - /// Which glow dot color to register, see the DotGlowColor enumerator. - /// The intensity level this effect should have when blended in post. 0 - 255. + /// @param effectPos The absolute scene coordinates of the center of the effect. + /// @param color Which glow dot color to register, see the DotGlowColor enumerator. + /// @param strength The intensity level this effect should have when blended in post. 0 - 255. void RegisterGlowDotEffect(const Vector& effectPos, DotGlowColor color, int strength = 255); - /// /// Gets all glow areas that affect anything within a box in the scene. /// Their coordinates will be returned relative to the upper left corner of the box passed in here. Wrapping of the box will be taken care of. - /// - /// The top left coordinates of the box to get post effects for. - /// The width of the box. - /// The height of the box. - /// The list to add the glow Boxes that intersect to. The coordinates of the Boxes returned here will be relative to the boxPos passed in above. - /// Whether any active post effects were found in that box. + /// @param boxPos The top left coordinates of the box to get post effects for. + /// @param boxWidth The width of the box. + /// @param boxHeight The height of the box. + /// @param areaList The list to add the glow Boxes that intersect to. The coordinates of the Boxes returned here will be relative to the boxPos passed in above. + /// @return Whether any active post effects were found in that box. bool GetGlowAreasWrapped(const Vector& boxPos, int boxWidth, int boxHeight, std::list& areaList) const; #pragma endregion #pragma region Network Post Effect Handling - /// /// Copies the specified player's screen relative post effects list of this PostProcessMan to the referenced list. Used for sending post effect data over the network. - /// - /// Which player screen to get list for. - /// Reference to the list of post effects to copy into. + /// @param whichScreen Which player screen to get list for. + /// @param outputList Reference to the list of post effects to copy into. void GetNetworkPostEffectsList(int whichScreen, std::list& outputList); - /// /// Copies the player's screen relative post effects from the referenced list to the list of this PostProcessMan. Used for receiving post effect data over the network. - /// - /// Which player screen to set list for. - /// Reference to the list of post effects to copy from. + /// @param whichScreen Which player screen to set list for. + /// @param inputList Reference to the list of post effects to copy from. void SetNetworkPostEffectsList(int whichScreen, std::list& inputList); #pragma endregion - /// /// Gets the backbuffer texture for indexed drawings. - /// - /// The opengl backbuffer texture for indexed drawings. + /// @return The opengl backbuffer texture for indexed drawings. GLuint GetPostProcessColorBuffer() { return m_BackBuffer32; } protected: @@ -233,82 +187,60 @@ namespace RTE { std::unique_ptr m_PostProcessShader; //!< Shader for drawing bitmap post effects. #pragma region Post Effect Handling - /// /// Gets all screen effects that are located within a box in the scene. Their coordinates will be returned relative to the upper left corner of the box passed in here. - /// - /// The top left coordinates of the box to get post effects for. - /// The width of the box. - /// The height of the box. - /// The list to add the screen effects that fall within the box to. The coordinates of the effects returned here will be relative to the boxPos passed in above. - /// The team whose unseen area should block the glows. - /// Whether any active post effects were found in that box. + /// @param boxPos The top left coordinates of the box to get post effects for. + /// @param boxWidth The width of the box. + /// @param boxHeight The height of the box. + /// @param effectsList The list to add the screen effects that fall within the box to. The coordinates of the effects returned here will be relative to the boxPos passed in above. + /// @param team The team whose unseen area should block the glows. + /// @return Whether any active post effects were found in that box. bool GetPostScreenEffects(Vector boxPos, int boxWidth, int boxHeight, std::list& effectsList, int team = -1); - /// /// Gets all screen effects that are located within a box in the scene. Their coordinates will be returned relative to the upper left corner of the box passed in here. - /// - /// Position of box left plane (X start). - /// Position of box top plane (Y start). - /// Position of box right plane (X end). - /// Position of box bottom plane (Y end). - /// The list to add the screen effects that fall within the box to. The coordinates of the effects returned here will be relative to the boxPos passed in above. - /// The team whose unseen area should block the glows. - /// Whether any active post effects were found in that box. + /// @param left Position of box left plane (X start). + /// @param top Position of box top plane (Y start). + /// @param right Position of box right plane (X end). + /// @param bottom Position of box bottom plane (Y end). + /// @param effectsList The list to add the screen effects that fall within the box to. The coordinates of the effects returned here will be relative to the boxPos passed in above. + /// @param team The team whose unseen area should block the glows. + /// @return Whether any active post effects were found in that box. bool GetPostScreenEffects(int left, int top, int right, int bottom, std::list& effectsList, int team = -1); #pragma endregion #pragma region Post Pixel Glow Handling - /// /// Gets a specific standard dot glow effect for making pixels glow. - /// - /// Which of the dot glow colors to get, see the DotGlowColor enumerator. - /// The requested glow dot BITMAP. + /// @param which Which of the dot glow colors to get, see the DotGlowColor enumerator. + /// @return The requested glow dot BITMAP. BITMAP* GetDotGlowEffect(DotGlowColor whichColor) const; - /// /// Gets the hash value of a specific standard dot glow effect for making pixels glow. - /// - /// Which of the dot glow colors to get, see the DotGlowColor enumerator. - /// The hash value of the requested glow dot BITMAP. + /// @param which Which of the dot glow colors to get, see the DotGlowColor enumerator. + /// @return The hash value of the requested glow dot BITMAP. size_t GetDotGlowEffectHash(DotGlowColor whichColor) const; #pragma endregion #pragma region PostProcess Breakdown - /// /// Draws all the glow dot effects on pixels registered inside glow boxes for this frame. This is called from PostProcess(). - /// void DrawDotGlowEffects(); - /// /// Draws all the glow effects registered for this frame. This is called from PostProcess(). - /// void DrawPostScreenEffects(); #pragma endregion - /// /// Clears all the member variables of this PostProcessMan, effectively resetting the members of this abstraction level only. - /// void Clear(); - /// /// Initializes all the GL pointers used by this PostProcessMan. - /// void InitializeGLPointers(); - /// /// Destroys all the GL pointers used by this PostProcessMan. - /// void DestroyGLPointers(); - /// /// Updates the palette texture with the current palette. - /// void UpdatePalette(); - /// /// Creates and upload a new GL texture. The texture pointer is stored in the BITMAP->extra field. - /// - /// The bitmap to create a texture for. + /// @param bitmap The bitmap to create a texture for. void LazyInitBitmap(BITMAP* bitmap); // Disallow the use of some implicit methods. diff --git a/Source/Managers/PresetMan.cpp b/Source/Managers/PresetMan.cpp index fdecb4bf99..2b7960ab2f 100644 --- a/Source/Managers/PresetMan.cpp +++ b/Source/Managers/PresetMan.cpp @@ -1,18 +1,6 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: PresetMan.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the PresetMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - // Suppress compiler warning about unrecognized escape sequence on line 183 #pragma warning(disable : 4129) -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "PresetMan.h" #include "DataModule.h" #include "SceneObject.h" @@ -35,12 +23,6 @@ namespace RTE { {c_UserConquestSavesModuleName, "Conquest Saves"}, {c_UserScriptedSavesModuleName, "Scripted Activity Saves"}}}; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this PresetMan, effectively - // resetting the members of this abstraction level only. - void PresetMan::Clear() { m_pDataModules.clear(); m_DataModuleIDs.clear(); @@ -71,11 +53,6 @@ namespace RTE { } */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the PresetMan entity. - void PresetMan::Destroy() { for (std::vector::iterator dmItr = m_pDataModules.begin(); dmItr != m_pDataModules.end(); ++dmItr) { delete (*dmItr); @@ -84,8 +61,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PresetMan::LoadDataModule(const std::string& moduleName, bool official, bool userdata, const ProgressCallback& progressCallback) { if (moduleName.empty()) { return false; @@ -135,8 +110,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PresetMan::LoadAllDataModules() { auto moduleLoadTimerStart = std::chrono::steady_clock::now(); @@ -198,31 +171,16 @@ namespace RTE { return true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDataModule - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a specific loaded DataModule - const DataModule* PresetMan::GetDataModule(int whichModule) { RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); return m_pDataModules[whichModule]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDataModuleName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a name specific loaded DataModule - const std::string PresetMan::GetDataModuleName(int whichModule) { RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); return m_pDataModules[whichModule]->GetFileName(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetModuleID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the ID of a loaded DataModule. - int PresetMan::GetModuleID(std::string moduleName) { // Lower-case search name so we can match up against the already-lowercase names in m_DataModuleIDs std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), ::tolower); @@ -272,8 +230,6 @@ namespace RTE { return -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string PresetMan::GetModuleNameFromPath(const std::string& dataPath) const { if (dataPath.empty()) { return ""; @@ -296,8 +252,6 @@ namespace RTE { return moduleName; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PresetMan::GetModuleIDFromPath(const std::string& dataPath) { if (dataPath.empty()) { return -1; @@ -305,14 +259,10 @@ namespace RTE { return GetModuleID(GetModuleNameFromPath(dataPath)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PresetMan::IsModuleOfficial(const std::string& moduleName) const { return std::find(c_OfficialModules.begin(), c_OfficialModules.end(), moduleName) != c_OfficialModules.end(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PresetMan::IsModuleUserdata(const std::string& moduleName) const { auto userdataModuleItr = std::find_if(c_UserdataModules.begin(), c_UserdataModules.end(), [&moduleName](const auto& userdataModulesEntry) { @@ -321,8 +271,6 @@ namespace RTE { return userdataModuleItr != c_UserdataModules.end(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string PresetMan::GetFullModulePath(const std::string& modulePath) const { // Note: Mods may use mixed path separators, which aren't supported on non Windows systems. // Since Windows supports both forward and backslash separators it's safe to replace all backslashes with forward slashes. @@ -342,25 +290,12 @@ namespace RTE { return (pathTopDir == moduleTopDir) ? modulePathGeneric : moduleTopDir + modulePathGeneric; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddEntityPreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an Entity instance's pointer and name associations to the - // internal list of already read in Entity:s. Ownership is NOT transferred! - // If there already is an instance defined, nothing happens. If there - // is not, a clone is made of the passed-in Entity and added to the library. - bool PresetMan::AddEntityPreset(Entity* pEntToAdd, int whichModule, bool overwriteSame, std::string readFromFile) { RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); return m_pDataModules[whichModule]->AddEntityPreset(pEntToAdd, overwriteSame, readFromFile); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEntityPreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a previously read in (defined) Entity, by type and instance name. - const Entity* PresetMan::GetEntityPreset(std::string type, std::string preset, int whichModule) { RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); @@ -397,16 +332,6 @@ namespace RTE { return pRetEntity; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEntityPreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads an instance of an Entity that will be used as preset - // to later on be used to generate more instances of the same state. - // Will check if there was already one of the same class and instance - // name read in previously, and will return that one if found, or - // add the newly read in one to this PresetMan's list if not found to - // exist there previously. Ownership is NOT transferred! - const Entity* PresetMan::GetEntityPreset(Reader& reader) { // The reader is aware of which DataModule it is reading within int whichModule = reader.GetReadModuleID(); @@ -454,13 +379,6 @@ namespace RTE { return pReturnPreset; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReadReflectedPreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads an preset of an Entity and tries to add it to the list of - // read-in presets. Regardless of whether there is a name collision, - // the read-in preset will be returned, ownership TRANSFERRED! - Entity* PresetMan::ReadReflectedPreset(Reader& reader) { // The reader is aware of which DataModule it's reading within int whichModule = reader.GetReadModuleID(); @@ -495,11 +413,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAllOfType - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds to a list all previously read in (defined) Entitys, by type. - bool PresetMan::GetAllOfType(std::list& entityList, std::string type, int whichModule) { if (type.empty()) return false; @@ -521,12 +434,6 @@ namespace RTE { return foundAny; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAllOfTypeInModuleSpace - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds to a list all previously read in (defined) Entitys which are - // of a specific type, and only exist in a specific module space. - bool PresetMan::GetAllOfTypeInModuleSpace(std::list& entityList, std::string type, int whichModuleSpace) { if (type.empty()) return false; @@ -549,8 +456,6 @@ namespace RTE { return foundAny; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PresetMan::GetAllOfGroups(std::list& entityList, const std::vector& groups, const std::string& type, int whichModule) { RTEAssert(!groups.empty(), "Looking for empty groups in PresetMan::GetAllOfGroups!"); bool foundAny = false; @@ -566,8 +471,6 @@ namespace RTE { return foundAny; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PresetMan::GetAllNotOfGroups(std::list& entityList, const std::vector& groups, const std::string& type, int whichModule) { if (groups.empty()) { RTEAbort("Looking for empty groups in PresetMan::GetAllNotOfGroups!"); @@ -588,12 +491,6 @@ namespace RTE { return foundAny; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRandomOfGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a previously read in (defined) Entity which is randomly - // selected from a specific group. - Entity* PresetMan::GetRandomOfGroup(std::string group, std::string type, int whichModule) { RTEAssert(!group.empty(), "Looking for empty group!"); @@ -631,12 +528,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRandomOfGroupFromTech - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a previously read in (defined) Entity which is randomly - // selected from a specific group. - Entity* PresetMan::GetRandomBuyableOfGroupFromTech(std::string group, std::string type, int whichModule) { RTEAssert(!group.empty(), "Looking for empty group!"); @@ -730,13 +621,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAllOfGroupInModuleSpace - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds to a list all previously read in (defined) Entitys which are - // associated with a specific group, and only exist in a specific module - // space. - bool PresetMan::GetAllOfGroupInModuleSpace(std::list& entityList, std::string group, std::string type, int whichModuleSpace) { RTEAssert(!group.empty(), "Looking for empty group!"); @@ -758,13 +642,6 @@ namespace RTE { return foundAny; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRandomOfGroupInModuleSpace - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a previously read in (defined) Entity which is associated with - // a specific group, randomly selected and only exist in a specific module - // space. - Entity* PresetMan::GetRandomOfGroupInModuleSpace(std::string group, std::string type, int whichModuleSpace) { RTEAssert(!group.empty(), "Looking for empty group!"); @@ -802,11 +679,6 @@ namespace RTE { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEntityDataLocation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the data file path of a previously read in (defined) Entity. - std::string PresetMan::GetEntityDataLocation(std::string type, std::string preset, int whichModule) { RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); @@ -835,8 +707,6 @@ namespace RTE { return pRetPath; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PresetMan::ReloadAllScripts() const { g_LuaMan.ClearUserModuleCache(); for (const DataModule* dataModule: m_pDataModules) { @@ -846,8 +716,6 @@ namespace RTE { g_ConsoleMan.PrintString("SYSTEM: Scripts reloaded!"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PresetMan::ReloadEntityPreset(const std::string& presetName, const std::string& className, const std::string& moduleName, bool storeReloadedPresetDataForQuickReloading) { if (className.empty() || presetName.empty()) { g_ConsoleMan.PrintString("ERROR: Trying to reload Entity preset without specifying preset name or type!"); @@ -895,8 +763,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PresetMan::QuickReloadEntityPreset() { for (const std::string& entityPresetInfoEntry: m_LastReloadedEntityPresetInfo) { if (entityPresetInfoEntry.empty()) { @@ -907,25 +773,12 @@ namespace RTE { return ReloadEntityPreset(m_LastReloadedEntityPresetInfo[0], m_LastReloadedEntityPresetInfo[1], m_LastReloadedEntityPresetInfo[2]); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddMaterialMapping - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a Material mapping local to a DataModule. This is used for when - // multiple DataModule:s are loading conflicting Material:s, and need to - // resolve the conflicts by mapping their materials to ID's different than - // those specified in the data files. - bool PresetMan::AddMaterialMapping(int fromID, int toID, int whichModule) { RTEAssert(whichModule >= m_OfficialModuleCount && whichModule < m_pDataModules.size(), "Tried to make a material mapping in an offical or out-of-bounds DataModule!"); return m_pDataModules[whichModule]->AddMaterialMapping(fromID, toID); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RegisterGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registers the existence of an Entity group, and in which module. - void PresetMan::RegisterGroup(std::string newGroup, int whichModule) { RTEAssert(whichModule >= 0 && whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); @@ -938,11 +791,6 @@ namespace RTE { m_pDataModules[whichModule]->RegisterGroup(newGroup); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGroups - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the list of all groups registered in a specific module. - bool PresetMan::GetGroups(std::list& groupList, int whichModule, std::string withType) const { RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); @@ -981,12 +829,6 @@ namespace RTE { return foundAny; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetModuleSpaceGroups - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Fills out a list with all groups registered in all official, PLUS a - // a specific non-official module as well. - bool PresetMan::GetModuleSpaceGroups(std::list& groupList, int whichModule, std::string withType) const { RTEAssert(whichModule < (int)m_pDataModules.size(), "Tried to access an out of bounds data module number!"); @@ -1024,20 +866,10 @@ namespace RTE { return foundAny; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLoadout - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns actor defined in the specified loadout. - Actor* PresetMan::GetLoadout(std::string loadoutName, std::string module, bool spawnDropShip) { return GetLoadout(loadoutName, GetModuleID(module), spawnDropShip); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLoadout - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns actor defined in the specified loadout. - Actor* PresetMan::GetLoadout(std::string loadoutName, int moduleNumber, bool spawnDropShip) { if (spawnDropShip) { // Find the Loadout that this Deployment is referring to @@ -1072,8 +904,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PresetMan::FindAndExtractZippedModules() const { for (const std::filesystem::directory_entry& directoryEntry: std::filesystem::directory_iterator(System::GetWorkingDirectory() + System::GetModDirectory())) { std::string zippedModulePath = std::filesystem::path(directoryEntry).generic_string(); diff --git a/Source/Managers/PresetMan.h b/Source/Managers/PresetMan.h index b58bc08c8b..f4400cf6e2 100644 --- a/Source/Managers/PresetMan.h +++ b/Source/Managers/PresetMan.h @@ -1,18 +1,11 @@ #ifndef _RTEPRESETMAN_ #define _RTEPRESETMAN_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: PresetMan.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the PresetMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the PresetMan class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Entity.h" #include "Singleton.h" @@ -23,497 +16,332 @@ namespace RTE { class Actor; class DataModule; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: PresetMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The singleton manager of all presets of Entity:s in the RTE. - // The presets serve as a respository of Entity instances with specific - // and unique and initial runtime data. - // Parent(s): Singleton, Serializable. - // Class history: 12/25/2001 PresetMan created. - // Class history: 05/30/2008 Changed name to PresetMan. - + /// The singleton manager of all presets of Entity:s in the RTE. + /// The presets serve as a respository of Entity instances with specific + /// and unique and initial runtime data. class PresetMan : public Singleton { friend struct ManagerLuaBindings; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: PresetMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a PresetMan entity in system - // memory. Create() should be called before using the entity. - // Arguments: None. - + /// Constructor method used to instantiate a PresetMan entity in system + /// memory. Create() should be called before using the entity. PresetMan() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~PresetMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a PresetMan entity before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a PresetMan entity before deletion + /// from system memory. ~PresetMan() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the PresetMan entity ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the PresetMan entity ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Initialize() { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire PresetMan, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire PresetMan, including its inherited members, to + /// their default settings or values. void Reset() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the PresetMan entity. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the PresetMan entity. void Destroy(); - /// /// Reads an entire DataModule and adds it to this. NOTE that official modules can't be loaded after any non-official ones! - /// - /// The module name to read, e.g. "Base.rte". - /// Whether this module is 'official' or third party. If official, it has to not have any name conflicts with any other official module. - /// Whether this module is a userdata module. If true, will be treated as an unofficial module. - /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. - /// Whether the DataModule was read and added correctly. + /// @param moduleName The module name to read, e.g. "Base.rte". + /// @param official Whether this module is 'official' or third party. If official, it has to not have any name conflicts with any other official module. + /// @param userdata Whether this module is a userdata module. If true, will be treated as an unofficial module. + /// @param progressCallback A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. + /// @return Whether the DataModule was read and added correctly. bool LoadDataModule(const std::string& moduleName, bool official, bool userdata = false, const ProgressCallback& progressCallback = nullptr); - /// /// Reads an entire DataModule and adds it to this. NOTE that official modules can't be loaded after any non-official ones! - /// - /// The module name to read, e.g. "Base.rte". - /// Whether the DataModule was read and added correctly. + /// @param moduleName The module name to read, e.g. "Base.rte". + /// @return Whether the DataModule was read and added correctly. bool LoadDataModule(const std::string& moduleName) { return LoadDataModule(moduleName, false); } - /// /// Loads all the official data modules individually with LoadDataModule, then proceeds to look for any non-official modules and loads them as well. - /// - /// + /// @return bool LoadAllDataModules(); - /// /// Sets the single module to be loaded after the official modules. This will be the ONLY non-official module to be loaded. - /// - /// Name of the module to load. + /// @param moduleName Name of the module to load. void SetSingleModuleToLoad(std::string moduleName) { m_SingleModuleToLoad = moduleName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDataModule - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a specific loaded DataModule - // Arguments: The ID of the module to get. - // Return value: The requested DataModule. Ownership is NOT transferred! - + /// Gets a specific loaded DataModule + /// @param whichModule The ID of the module to get. (default: 0) + /// @return The requested DataModule. Ownership is NOT transferred! const DataModule* GetDataModule(int whichModule = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDataModuleName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a name specific loaded DataModule - // Arguments: The ID of the module to get. - // Return value: The requested DataModule name. - + /// Gets a name specific loaded DataModule + /// @param whichModule The ID of the module to get. (default: 0) + /// @return The requested DataModule name. const std::string GetDataModuleName(int whichModule = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetModuleID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the ID of a loaded DataModule. - // Arguments: The name of the DataModule to get the ID from, including the ".rte" - // Return value: The requested ID. If no module of the name was found, -1 will be returned. - + /// Gets the ID of a loaded DataModule. + /// @param moduleName The name of the DataModule to get the ID from, including the ".rte" + /// @return The requested ID. If no module of the name was found, -1 will be returned. int GetModuleID(std::string moduleName); - /// /// Gets the Name of a loaded DataModule, from a full data file path. - /// - /// The full path to a data file inside the data module id you want to get. - /// The requested Name. If no module of the name was found, "" will be returned. + /// @param dataPath The full path to a data file inside the data module id you want to get. + /// @return The requested Name. If no module of the name was found, "" will be returned. std::string GetModuleNameFromPath(const std::string& dataPath) const; - /// /// Gets the ID of a loaded DataModule from a full data file path. - /// - /// The full path to a data file inside the data module ID you want to get. - /// The requested ID. If no module of the name was found, -1 will be returned. + /// @param dataPath The full path to a data file inside the data module ID you want to get. + /// @return The requested ID. If no module of the name was found, -1 will be returned. int GetModuleIDFromPath(const std::string& dataPath); - /// /// Returns whether or not the module is vanilla. - /// - /// The name of the module to check, in the form "[moduleName].rte" - /// True if the module is an official data module, otherwise false. + /// @param moduleName The name of the module to check, in the form "[moduleName].rte" + /// @return True if the module is an official data module, otherwise false. bool IsModuleOfficial(const std::string& moduleName) const; - /// /// Returns whether or not the module is vanilla. - /// - /// The name of the module to check, in the form "[moduleName].rte" - /// True if the module is a listed user data module, otherwise false. + /// @param moduleName The name of the module to check, in the form "[moduleName].rte" + /// @return True if the module is a listed user data module, otherwise false. bool IsModuleUserdata(const std::string& moduleName) const; - /// /// Returns the Full path to the module including Data/, Userdata/ or Mods/. - /// - /// The Path to be completed. - /// The complete path to the file, including Data/, Userdata/ or Mods/ based on whether or not it's part of an official module or userdata. + /// @param modulePath The Path to be completed. + /// @return The complete path to the file, including Data/, Userdata/ or Mods/ based on whether or not it's part of an official module or userdata. std::string GetFullModulePath(const std::string& modulePath) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalModuleCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total number of modules loaded so far, official or not. - // Arguments: None. - // Return value: The number of modules loaded so far, both official and non. - + /// Gets the total number of modules loaded so far, official or not. + /// @return The number of modules loaded so far, both official and non. int GetTotalModuleCount() { return m_pDataModules.size(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOfficialModuleCount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total number of OFFICIAL modules loaded so far. - // Arguments: None. - // Return value: The number of official modules loaded so far. - + /// Gets the total number of OFFICIAL modules loaded so far. + /// @return The number of official modules loaded so far. int GetOfficialModuleCount() { return m_OfficialModuleCount; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddEntityPreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an Entity instance's pointer and name associations to the - // internal list of already read in Entity:s. Ownership is NOT transferred! - // If there already is an instance defined, nothing happens. If there - // is not, a clone is made of the passed-in Entity and added to the library. - // Arguments: A pointer to the Entity derived instance to add. It should be created - // from a Reader. Ownership is NOT transferred! - // Which module to add the entity to. - // Whether to overwrite if an instance of the EXACT same TYPE and name - // was found. If one of the same name but not the exact type, false - // is returned regardless and nothing will have been added. - // The file this instance was read from, or where it should be written. - // If "Same" is passed as the file path read from, an overwritten instance - // will keep the old one's file location entry. - // Return value: Whether or not a copy of the passed-in instance was successfully inserted - // into the module. False will be returned if there already was an instance - // of that class and instance name inserted previously, unless overwritten. - + /// Adds an Entity instance's pointer and name associations to the + /// internal list of already read in Entity:s. Ownership is NOT transferred! + /// If there already is an instance defined, nothing happens. If there + /// is not, a clone is made of the passed-in Entity and added to the library. + /// @param pEntToAdd A pointer to the Entity derived instance to add. It should be created + /// from a Reader. Ownership is NOT transferred! + /// @param whichModule Which module to add the entity to. (default: 0) + /// @param overwriteSame Whether to overwrite if an instance of the EXACT same TYPE and name (default: false) + /// was found. If one of the same name but not the exact type, false + /// is returned regardless and nothing will have been added. + /// @param readFromFile The file this instance was read from, or where it should be written. (default: "Same") + /// If "Same" is passed as the file path read from, an overwritten instance + /// will keep the old one's file location entry. + /// @return Whether or not a copy of the passed-in instance was successfully inserted + /// into the module. False will be returned if there already was an instance + /// of that class and instance name inserted previously, unless overwritten. bool AddEntityPreset(Entity* pEntToAdd, int whichModule = 0, bool overwriteSame = false, std::string readFromFile = "Same"); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEntityPreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a previously read in (defined) Entity, by type and instance name. - // Arguments: The type name of the derived Entity. Ownership is NOT transferred! - // The instance name of the derived Entity instance. - // Which module to try to get the entity from. If it's not found there, - // the official modules will be searched also. -1 means search ALL modules! - // Return value: A pointer to the requested Entity instance. 0 if no Entity with that - // derived type or instance name was found. Ownership is NOT transferred! - + /// Gets a previously read in (defined) Entity, by type and instance name. + /// @param type The type name of the derived Entity. Ownership is NOT transferred! + /// @param preset The instance name of the derived Entity instance. + /// @param whichModule Which module to try to get the entity from. If it's not found there, (default: -1) + /// the official modules will be searched also. -1 means search ALL modules! + /// @return A pointer to the requested Entity instance. 0 if no Entity with that + /// derived type or instance name was found. Ownership is NOT transferred! const Entity* GetEntityPreset(std::string type, std::string preset, int whichModule = -1); // Helper for passing in string module name instead of ID const Entity* GetEntityPreset(std::string type, std::string preset, std::string module) { return GetEntityPreset(type, preset, GetModuleID(module)); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEntityPreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads an instance of an Entity that will be used as preset - // to later on be used to generate more instances of the same state. - // Will check if there was already one of the same class and instance - // name read in previously, and will return that one if found, or - // add the newly read in one to this PresetMan's list if not found to - // exist there previously. Ownership is NOT transferred! - // Arguments: The Reader which is about to read in an instance reference. It'll make - // this look in the same module as it's reading from. - // Return value: A const pointer to the Entity instance read in. 0 if there was an - // error, or the instance name was 'None'. Ownership is NOT transferred! - + /// Reads an instance of an Entity that will be used as preset + /// to later on be used to generate more instances of the same state. + /// Will check if there was already one of the same class and instance + /// name read in previously, and will return that one if found, or + /// add the newly read in one to this PresetMan's list if not found to + /// exist there previously. Ownership is NOT transferred! + /// @param reader The Reader which is about to read in an instance reference. It'll make + /// this look in the same module as it's reading from. + /// @return A const pointer to the Entity instance read in. 0 if there was an + /// error, or the instance name was 'None'. Ownership is NOT transferred! const Entity* GetEntityPreset(Reader& reader); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ReadReflectedPreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reads a preset of an Entity and tries to add it to the list of - // read-in instances. Regardless of whether there is a name collision, - // the read-in preset will be returned, ownership TRANSFERRED! - // Arguments: The Reader which is about to read in a preset. - // Return value: A pointer to the Entity preset read in. 0 if there was an - // error, or the instance name was 'None'. Ownership IS transferred! - + /// Reads a preset of an Entity and tries to add it to the list of + /// read-in instances. Regardless of whether there is a name collision, + /// the read-in preset will be returned, ownership TRANSFERRED! + /// @param reader The Reader which is about to read in a preset. + /// @return A pointer to the Entity preset read in. 0 if there was an + /// error, or the instance name was 'None'. Ownership IS transferred! Entity* ReadReflectedPreset(Reader& reader); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAllOfType - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds to a list all previously read in (defined) Entitys, by type. - // Arguments: Reference to a list which will get all matching Entity:s added to it. - // Ownership of the list or the Entitys placed in it are NOT transferred! - // The type name of the Entitys you want. - // Whether to only get those of one specific DataModule (0-n), or all (-1). - // Return value: Whether any Entity:s were found and added to the list. - + /// Adds to a list all previously read in (defined) Entitys, by type. + /// @param entityList Reference to a list which will get all matching Entity:s added to it. + /// @param type Ownership of the list or the Entitys placed in it are NOT transferred! + /// @param whichModule The type name of the Entitys you want. (default: -1) + /// Whether to only get those of one specific DataModule (0-n), or all (-1). + /// @return Whether any Entity:s were found and added to the list. bool GetAllOfType(std::list& entityList, std::string type, int whichModule = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAllOfTypeInModuleSpace - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds to a list all previously read in (defined) Entitys which are - // of a specific type, and only exist in a specific module space. - // Arguments: Reference to a list which will get all matching Entity:s added to it. - // Ownership of the list or the Entitys placed in it are NOT transferred! - // The type name of the Entitys you want. - // Which module to get the instances for, in addition to all groups in - // official modules loaded earlier than the one specified here. -1 means - // get ALL groups ever reg'd. - // Return value: Whether any Entity:s were found and added to the list. - + /// Adds to a list all previously read in (defined) Entitys which are + /// of a specific type, and only exist in a specific module space. + /// @param entityList Reference to a list which will get all matching Entity:s added to it. + /// @param type Ownership of the list or the Entitys placed in it are NOT transferred! + /// @param whichModuleSpace The type name of the Entitys you want. + /// Which module to get the instances for, in addition to all groups in + /// official modules loaded earlier than the one specified here. -1 means + /// get ALL groups ever reg'd. + /// @return Whether any Entity:s were found and added to the list. bool GetAllOfTypeInModuleSpace(std::list& entityList, std::string type, int whichModuleSpace); - /// /// Adds to a list all previously read in (defined) Entities which are associated with a specific group. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The group to look for. "All" will look in all. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether to only get those of one specific DataModule (0-n), or all (-1). - /// Whether any Entities were found and added to the list. + /// @param entityList Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// @param group The group to look for. "All" will look in all. + /// @param type The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// @param whichModule Whether to only get those of one specific DataModule (0-n), or all (-1). + /// @return Whether any Entities were found and added to the list. bool GetAllOfGroup(std::list& entityList, const std::string& group, const std::string& type = "All", int whichModule = -1) { return GetAllOfGroups(entityList, {group}, type, whichModule); } - /// /// Adds to a list all previously read in (defined) Entities which are associated with several specific groups. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The groups to look for. "All" will look in all. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether to only get those of one specific DataModule (0-n), or all (-1). - /// Whether any Entities were found and added to the list. + /// @param entityList Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// @param groups The groups to look for. "All" will look in all. + /// @param type The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// @param whichModule Whether to only get those of one specific DataModule (0-n), or all (-1). + /// @return Whether any Entities were found and added to the list. bool GetAllOfGroups(std::list& entityList, const std::vector& groups, const std::string& type = "All", int whichModule = -1); - /// /// Adds to a list all previously read in (defined) Entities which are not associated with a specific group. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The group to exclude. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether to only get those of one specific DataModule (0-n), or all (-1). - /// Whether any Entities were found and added to the list. + /// @param entityList Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// @param group The group to exclude. + /// @param type The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// @param whichModule Whether to only get those of one specific DataModule (0-n), or all (-1). + /// @return Whether any Entities were found and added to the list. bool GetAllNotOfGroup(std::list& entityList, const std::string& group, const std::string& type = "All", int whichModule = -1) { return GetAllNotOfGroups(entityList, {group}, type, whichModule); } - /// /// Adds to a list all previously read in (defined) Entities which are not associated with several specific groups. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The groups to exclude. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether to only get those of one specific DataModule (0-n), or all (-1). - /// Whether any Entities were found and added to the list. + /// @param entityList Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// @param groups The groups to exclude. + /// @param type The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// @param whichModule Whether to only get those of one specific DataModule (0-n), or all (-1). + /// @return Whether any Entities were found and added to the list. bool GetAllNotOfGroups(std::list& entityList, const std::vector& groups, const std::string& type = "All", int whichModule = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRandomOfGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a previously read in (defined) Entity which is randomly - // selected from a specific group. - // Arguments: The group to randomly select an Entity from. "All" will look in all. - // The name of the least common denominator type of the Entitys you want. - // "All" will look at all types. - // Whether to only get those of one specific DataModule (0-n), or all (-1). - // Return value: The Entity preset that was randomly selected. Ownership is NOT transferred! - + /// Returns a previously read in (defined) Entity which is randomly + /// selected from a specific group. + /// @param group The group to randomly select an Entity from. "All" will look in all. + /// @param type The name of the least common denominator type of the Entitys you want. (default: "All") + /// "All" will look at all types. + /// @param whichModule Whether to only get those of one specific DataModule (0-n), or all (-1). (default: -1) + /// @return The Entity preset that was randomly selected. Ownership is NOT transferred! Entity* GetRandomOfGroup(std::string group, std::string type = "All", int whichModule = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRandomOfGroupFromTech - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a previously read in (defined) Entity which is randomly - // selected from a specific group only if it belongs to some tech. - // Arguments: The group to randomly select an Entity from. "All" will look in all. - // The name of the least common denominator type of the Entitys you want. - // "All" will look at all types. - // Whether to only get those of one specific DataModule (0-n), or all (-1) - // or all modules uncluding non-tech ones. - // Return value: The Entity preset that was randomly selected. Ownership is NOT transferred! - + /// Returns a previously read in (defined) Entity which is randomly + /// selected from a specific group only if it belongs to some tech. + /// @param group The group to randomly select an Entity from. "All" will look in all. + /// @param type The name of the least common denominator type of the Entitys you want. (default: "All") + /// "All" will look at all types. + /// @param whichModule Whether to only get those of one specific DataModule (0-n), or all (-1) (default: -1) + /// or all modules uncluding non-tech ones. + /// @return The Entity preset that was randomly selected. Ownership is NOT transferred! Entity* GetRandomBuyableOfGroupFromTech(std::string group, std::string type = "All", int whichModule = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetAllOfGroupInModuleSpace - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds to a list all previously read in (defined) Entitys which are - // associated with a specific group, and only exist in a specific module - // space. - // Arguments: Reference to a list which will get all matching Entity:s added to it. - // Ownership of the list or the Entitys placed in it are NOT transferred! - // The group to look for. "All" will look in all. - // The name of the least common denominator type of the Entitys you want. - // "All" will look at all types. - // Which module to get the instances for, in addition to all groups in - // official modules loaded earlier than the one specified here. -1 means - // get ALL groups ever reg'd. - // Return value: Whether any Entity:s were found and added to the list. - + /// Adds to a list all previously read in (defined) Entitys which are + /// associated with a specific group, and only exist in a specific module + /// space. + /// @param entityList Reference to a list which will get all matching Entity:s added to it. + /// @param group Ownership of the list or the Entitys placed in it are NOT transferred! + /// @param type The group to look for. "All" will look in all. + /// @param whichModuleSpace The name of the least common denominator type of the Entitys you want. + /// "All" will look at all types. + /// Which module to get the instances for, in addition to all groups in + /// official modules loaded earlier than the one specified here. -1 means + /// get ALL groups ever reg'd. + /// @return Whether any Entity:s were found and added to the list. bool GetAllOfGroupInModuleSpace(std::list& entityList, std::string group, std::string type, int whichModuleSpace); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRandomOfGroupInModuleSpace - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a previously read in (defined) Entity which is associated with - // a specific group, randomly selected and only exist in a specific module - // space. - // Arguments: Ownership of the list or the Entitys placed in it are NOT transferred! - // The group to randomly select from. "All" will look in all. - // The name of the least common denominator type of the Entity:s you want. - // "All" will look at all types. - // Which module to get the instances for, in addition to all groups in - // official modules loaded earlier than the one specified here. -1 means - // get ALL groups ever reg'd. - // Return value: The randomly select preset, if any was found with thse search params. - // Ownership is NOT transferred! - + /// Returns a previously read in (defined) Entity which is associated with + /// a specific group, randomly selected and only exist in a specific module + /// space. + /// @param group Ownership of the list or the Entitys placed in it are NOT transferred! + /// @param type The group to randomly select from. "All" will look in all. + /// @param whichModuleSpace The name of the least common denominator type of the Entity:s you want. + /// "All" will look at all types. + /// Which module to get the instances for, in addition to all groups in + /// official modules loaded earlier than the one specified here. -1 means + /// get ALL groups ever reg'd. + /// @return The randomly select preset, if any was found with thse search params. + /// Ownership is NOT transferred! Entity* GetRandomOfGroupInModuleSpace(std::string group, std::string type, int whichModuleSpace); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEntityDataLocation - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the data file path of a previously read in (defined) Entity. - // Arguments: The type name of the derived Entity. Ownership is NOT transferred! - // The preset name of the derived Entity preset. - // Which module to try to get the entity from. If it's not found there, - // the official modules will be searched also. - // Return value: The file path of the data file that the specified Entity was read from. - // If no Entity of that description was found, "" is returned. - + /// Gets the data file path of a previously read in (defined) Entity. + /// @param type The type name of the derived Entity. Ownership is NOT transferred! + /// @param preset The preset name of the derived Entity preset. + /// @param whichModule Which module to try to get the entity from. If it's not found there, + /// the official modules will be searched also. + /// @return The file path of the data file that the specified Entity was read from. + /// If no Entity of that description was found, "" is returned. std::string GetEntityDataLocation(std::string type, std::string preset, int whichModule); - /// /// Reloads all scripted Entity Presets with the latest version of their respective script files. - /// void ReloadAllScripts() const; - /// /// Reloads an Entity preset and all related presets with the latest version of their respective files. /// Stores the passed in Entity preset info for later re-use in PresetMan::QuickReloadEntityPreset. - /// - /// The name of the preset to reload. - /// The type of the preset to reload, to avoid any ambiguity. - /// The DataModule the preset to reload is defined in. - /// Whether or not to store the reloaded entity preset data for quick reloading. - /// Whether reloading the preset was successful. + /// @param presetName The name of the preset to reload. + /// @param className The type of the preset to reload, to avoid any ambiguity. + /// @param dataModule The DataModule the preset to reload is defined in. + /// @param storeReloadedPresetDataForQuickReloading Whether or not to store the reloaded entity preset data for quick reloading. + /// @return Whether reloading the preset was successful. bool ReloadEntityPreset(const std::string& presetName, const std::string& className, const std::string& dataModule, bool storeReloadedPresetDataForQuickReloading = true); - /// /// Reloads the previously reloaded Entity preset and all related presets with the latest version of their respective files. - /// - /// Whether reloading the preset was successful. + /// @return Whether reloading the preset was successful. bool QuickReloadEntityPreset(); - /// /// Gets whether or not ReloadEntityPreset was called this update. - /// - /// Whether or not ReloadEntityPreset was called this update. + /// @return Whether or not ReloadEntityPreset was called this update. bool GetReloadEntityPresetCalledThisUpdate() const { return m_ReloadEntityPresetCalledThisUpdate; } - /// /// Resets whether or not ReloadEntityPreset was called this update. - /// void ClearReloadEntityPresetCalledThisUpdate() { m_ReloadEntityPresetCalledThisUpdate = false; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddMaterialMapping - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a Material mapping local to a DataModule. This is used for when - // multiple DataModule:s are loading conflicting Material:s, and need to - // resolve the conflicts by mapping their materials to ID's different than - // those specified in the data files. - // Arguments: The material ID to map from. - // The material ID to map to. - // The ID of the DataModule we are making the mapping for. This should be - // a non-official module as mapping shouldn't be needed in the official - // modules. - // Return value: Whether this created a new mapping which didn't override a previous - // material mapping. - + /// Adds a Material mapping local to a DataModule. This is used for when + /// multiple DataModule:s are loading conflicting Material:s, and need to + /// resolve the conflicts by mapping their materials to ID's different than + /// those specified in the data files. + /// @param fromID The material ID to map from. + /// @param toID The material ID to map to. + /// @param whichModule The ID of the DataModule we are making the mapping for. This should be + /// a non-official module as mapping shouldn't be needed in the official + /// modules. + /// @return Whether this created a new mapping which didn't override a previous + /// material mapping. bool AddMaterialMapping(int fromID, int toID, int whichModule); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RegisterGroup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registers the existence of an Entity group, and in which module. - // Arguments: The group to register. - // The ID of the module in which at least one entity of this group can be - // found. - // global register. - // Return value: None. - + /// Registers the existence of an Entity group, and in which module. + /// @param newGroup The group to register. + /// @param whichModule The ID of the module in which at least one entity of this group can be + /// found. + /// global register. void RegisterGroup(std::string newGroup, int whichModule); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGroups - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Fills out a list with all groups registered in a specific module. - // Arguments: The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! - // Which module to get the groups for. -1 means get ALL groups ever reg'd. - // Pass a type name here and only groups with entitys of that type will be - // be included. "All" means don't consider what types are in the groups. - // Return value: Whether any groups were found and thus added to the list. - + /// Fills out a list with all groups registered in a specific module. + /// @param groupList The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! + /// @param whichModule Which module to get the groups for. -1 means get ALL groups ever reg'd. (default: -1) + /// @param withType Pass a type name here and only groups with entitys of that type will be (default: "All") + /// be included. "All" means don't consider what types are in the groups. + /// @return Whether any groups were found and thus added to the list. bool GetGroups(std::list& groupList, int whichModule = -1, std::string withType = "All") const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetModuleSpaceGroups - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Fills out a list with all groups registered in all official modules, - // PLUS a specific non-official module as well. - // Arguments: The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! - // Which module to get the groups for, in addition to all groups in - // official modules loaded earlier than the one specified here. -1 means - // get ALL groups ever reg'd. - // Pass a type name here and only groups with entitys of that type will be - // be included. "All" means don't consider what types are in the groups. - // Return value: Whether any groups were found and thus added to the list. - + /// Fills out a list with all groups registered in all official modules, + /// PLUS a specific non-official module as well. + /// @param groupList The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! + /// @param whichModule Which module to get the groups for, in addition to all groups in + /// official modules loaded earlier than the one specified here. -1 means + /// get ALL groups ever reg'd. + /// @param withType Pass a type name here and only groups with entitys of that type will be (default: "All") + /// be included. "All" means don't consider what types are in the groups. + /// @return Whether any groups were found and thus added to the list. bool GetModuleSpaceGroups(std::list& groupList, int whichModule, std::string withType = "All") const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLoadout - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns actor defined in the specified loadout. - // Arguments: Loadout preset name, module name, whether or not spawn delivery craft defined for that loadout - // Return value: Created actor if matching loadout was found or 0. OWNERSHIP IS TRANSFERED. - + /// Creates and returns actor defined in the specified loadout. + /// @param loadoutName Loadout preset name, module name, whether or not spawn delivery craft defined for that loadout + /// @return Created actor if matching loadout was found or 0. OWNERSHIP IS TRANSFERED. Actor* GetLoadout(std::string loadoutName, std::string module, bool spawnDropShip); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLoadout - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates and returns actor defined in the specified loadout. - // Arguments: Loadout preset name, module id, whether or not spawn delivery craft defined for that loadout - // Return value: Created actor if matching loadout was found or 0. OWNERSHIP IS TRANSFERED. - + /// Creates and returns actor defined in the specified loadout. + /// @param loadoutName Loadout preset name, module id, whether or not spawn delivery craft defined for that loadout + /// @return Created actor if matching loadout was found or 0. OWNERSHIP IS TRANSFERED. Actor* GetLoadout(std::string loadoutName, int moduleNumber, bool spawnDropShip); - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Owned and loaded DataModule:s std::vector m_pDataModules; @@ -532,9 +360,7 @@ namespace RTE { // This is just a handy total of all the groups registered in all the individual DataModule:s std::list m_TotalGroupRegister; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: static const std::array c_OfficialModules; // Array storing the names of all the official modules. static const std::array, 3> c_UserdataModules; // Array storing the names of all the userdata modules. @@ -542,19 +368,11 @@ namespace RTE { std::array m_LastReloadedEntityPresetInfo; //!< Array storing the last reloaded Entity preset info (ClassName, PresetName and DataModule). Used for quick reloading via key combination. bool m_ReloadEntityPresetCalledThisUpdate; //!< A flag for whether or not ReloadEntityPreset was called this update. - /// /// Iterates through the working directory to find any files matching the zipped module package extension (.rte.zip) and proceeds to extract them. - /// void FindAndExtractZippedModules() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this PresetMan, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this PresetMan, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/PrimitiveMan.cpp b/Source/Managers/PrimitiveMan.cpp index a29d08d63a..3e44b3d478 100644 --- a/Source/Managers/PrimitiveMan.cpp +++ b/Source/Managers/PrimitiveMan.cpp @@ -9,8 +9,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::unique_ptr PrimitiveMan::MakeUniqueOfAppropriateTypeFromPrimitiveRawPtr(GraphicalPrimitive* primitive) { switch (primitive->GetPrimitiveType()) { case GraphicalPrimitive::PrimitiveType::Line: @@ -48,15 +46,11 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::SchedulePrimitive(std::unique_ptr&& primitive) { std::lock_guard lock(m_Mutex); m_ScheduledPrimitives.emplace_back(std::move(primitive)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::SchedulePrimitivesForBlendedDrawing(DrawBlendMode blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const std::vector& primitives) { if (blendMode < DrawBlendMode::NoBlend || blendMode >= DrawBlendMode::BlendModeCount) { g_ConsoleMan.PrintString("ERROR: Encountered invalid blending mode when attempting to draw primitives! Drawing will be skipped! See the DrawBlendMode enumeration for valid modes."); @@ -74,8 +68,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawLinePrimitive(int player, const Vector& startPos, const Vector& endPos, unsigned char color, int thickness) { if (thickness > 1) { Vector dirVector = g_SceneMan.ShortestDistance(startPos, endPos, g_SceneMan.SceneWrapsX()).SetMagnitude(static_cast(thickness - 1) / 2.0F).Perpendicularize(); @@ -91,164 +83,110 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawArcPrimitive(const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, startAngle, endAngle, radius, 1, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawArcPrimitive(const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness) { SchedulePrimitive(std::make_unique(-1, centerPos, startAngle, endAngle, radius, thickness, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, startAngle, endAngle, radius, 1, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness) { SchedulePrimitive(std::make_unique(player, centerPos, startAngle, endAngle, radius, thickness, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawSplinePrimitive(const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color) { SchedulePrimitive(std::make_unique(-1, startPos, guideA, guideB, endPos, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawSplinePrimitive(int player, const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color) { SchedulePrimitive(std::make_unique(player, startPos, guideA, guideB, endPos, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBoxPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { SchedulePrimitive(std::make_unique(-1, topLeftPos, bottomRightPos, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { SchedulePrimitive(std::make_unique(player, topLeftPos, bottomRightPos, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBoxFillPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { SchedulePrimitive(std::make_unique(-1, topLeftPos, bottomRightPos, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { SchedulePrimitive(std::make_unique(player, topLeftPos, bottomRightPos, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawRoundedBoxPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, topLeftPos, bottomRightPos, cornerRadius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawRoundedBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) { SchedulePrimitive(std::make_unique(player, topLeftPos, bottomRightPos, cornerRadius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawRoundedBoxFillPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, topLeftPos, bottomRightPos, cornerRadius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawRoundedBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) { SchedulePrimitive(std::make_unique(player, topLeftPos, bottomRightPos, cornerRadius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawCirclePrimitive(const Vector& centerPos, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, radius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawCirclePrimitive(int player, const Vector& centerPos, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, radius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawCircleFillPrimitive(const Vector& centerPos, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, radius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawCircleFillPrimitive(int player, const Vector& centerPos, int radius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, radius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawEllipsePrimitive(const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, horizRadius, vertRadius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawEllipsePrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, horizRadius, vertRadius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawEllipseFillPrimitive(const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) { SchedulePrimitive(std::make_unique(-1, centerPos, horizRadius, vertRadius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawEllipseFillPrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) { SchedulePrimitive(std::make_unique(player, centerPos, horizRadius, vertRadius, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTrianglePrimitive(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) { SchedulePrimitive(std::make_unique(-1, pointA, pointB, pointC, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTrianglePrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) { SchedulePrimitive(std::make_unique(player, pointA, pointB, pointC, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTriangleFillPrimitive(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) { SchedulePrimitive(std::make_unique(-1, pointA, pointB, pointC, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTriangleFillPrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) { SchedulePrimitive(std::make_unique(player, pointA, pointB, pointC, color)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawPolygonOrPolygonFillPrimitive(int player, const Vector& startPos, unsigned char color, const std::vector& vertices, bool filled) { if (vertices.size() < 2) { g_ConsoleMan.PrintString("ERROR: Polygon primitive should have at least 2 vertices! Drawing will be skipped!"); @@ -261,52 +199,36 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTextPrimitive(const Vector& start, const std::string& text, bool isSmall, int alignment) { SchedulePrimitive(std::make_unique(-1, start, text, isSmall, alignment, 0)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTextPrimitive(const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle) { SchedulePrimitive(std::make_unique(-1, start, text, isSmall, alignment, rotAngle)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTextPrimitive(int player, const Vector& start, const std::string& text, bool isSmall, int alignment) { SchedulePrimitive(std::make_unique(player, start, text, isSmall, alignment, 0)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawTextPrimitive(int player, const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle) { SchedulePrimitive(std::make_unique(player, start, text, isSmall, alignment, rotAngle)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBitmapPrimitive(int player, const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped) { SchedulePrimitive(std::make_unique(player, centerPos, moSprite, rotAngle, frame, hFlipped, vFlipped)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawBitmapPrimitive(int player, const Vector& centerPos, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped) { SchedulePrimitive(std::make_unique(player, centerPos, filePath, rotAngle, hFlipped, vFlipped)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawIconPrimitive(int player, const Vector& centerPos, Entity* entity) { if (const MOSprite* moSprite = dynamic_cast(entity)) { SchedulePrimitive(std::make_unique(player, centerPos, moSprite->GetGraphicalIcon(), 0, false, false)); } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PrimitiveMan::DrawPrimitives(int player, BITMAP* targetBitmap, const Vector& targetPos) const { ZoneScoped; diff --git a/Source/Managers/PrimitiveMan.h b/Source/Managers/PrimitiveMan.h index e8deb56db9..99cfbf3ea9 100644 --- a/Source/Managers/PrimitiveMan.h +++ b/Source/Managers/PrimitiveMan.h @@ -10,474 +10,374 @@ namespace RTE { class Entity; - /// /// Singleton manager responsible for all primitive drawing. - /// class PrimitiveMan : public Singleton { public: #pragma region Creation - /// /// Constructor method used to instantiate a PrimitiveMan object in system memory. - /// PrimitiveMan() { ClearPrimitivesQueue(); } #pragma endregion #pragma region Destruction - /// /// Delete all scheduled primitives, called on every FrameMan sim update. - /// void ClearPrimitivesQueue() { m_ScheduledPrimitives.clear(); } #pragma endregion #pragma region Primitive Drawing - /// /// Draws all stored primitives on the screen for specified player. - /// - /// Player to draw for. - /// Bitmap to draw on. - /// Position to draw. + /// @param player Player to draw for. + /// @param targetBitmap Bitmap to draw on. + /// @param targetPos Position to draw. void DrawPrimitives(int player, BITMAP* targetBitmap, const Vector& targetPos) const; #pragma endregion #pragma region Primitive Draw Scheduling - /// /// Schedule to draw multiple primitives of varying type with blending enabled. - /// - /// The blending mode to use when drawing each primitive. - /// The blending amount for the Red channel. 0-100. - /// The blending amount for the Green channel. 0-100. - /// The blending amount for the Blue channel. 0-100. - /// The blending amount for the Alpha channel. 0-100. - /// A vector of primitives to schedule drawing for. + /// @param blendMode The blending mode to use when drawing each primitive. + /// @param blendAmountR The blending amount for the Red channel. 0-100. + /// @param blendAmountG The blending amount for the Green channel. 0-100. + /// @param blendAmountB The blending amount for the Blue channel. 0-100. + /// @param blendAmountA The blending amount for the Alpha channel. 0-100. + /// @param primitives A vector of primitives to schedule drawing for. void SchedulePrimitivesForBlendedDrawing(DrawBlendMode blendMode, int blendAmountR, int blendAmountG, int blendAmountB, int blendAmountA, const std::vector& primitives); - /// /// Schedule to draw a line primitive. - /// - /// Start position of primitive in scene coordinates. - /// End position of primitive in scene coordinates. - /// Color to draw primitive with. + /// @param startPos Start position of primitive in scene coordinates. + /// @param endPos End position of primitive in scene coordinates. + /// @param color Color to draw primitive with. void DrawLinePrimitive(const Vector& startPos, const Vector& endPos, unsigned char color) { DrawLinePrimitive(-1, startPos, endPos, color, 1); } - /// /// Schedule to draw a line primitive with the option to change thickness. - /// - /// Start position of primitive in scene coordinates. - /// End position of primitive in scene coordinates. - /// Color to draw primitive with. - /// Thickness of the line in pixels. + /// @param startPos Start position of primitive in scene coordinates. + /// @param endPos End position of primitive in scene coordinates. + /// @param color Color to draw primitive with. + /// @param thickness Thickness of the line in pixels. void DrawLinePrimitive(const Vector& startPos, const Vector& endPos, unsigned char color, int thickness) { DrawLinePrimitive(-1, startPos, endPos, color, thickness); } - /// /// Schedule to draw a line primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Start position of primitive in scene coordinates. - /// End position of primitive in scene coordinates. - /// Color to draw primitive with. + /// @param player Player screen to draw primitive on. + /// @param startPos Start position of primitive in scene coordinates. + /// @param endPos End position of primitive in scene coordinates. + /// @param color Color to draw primitive with. void DrawLinePrimitive(int player, const Vector& startPos, const Vector& endPos, unsigned char color) { DrawLinePrimitive(player, startPos, endPos, color, 1); } - /// /// Schedule to draw a line primitive visible only to a specified player with the option to change thickness. - /// - /// Player screen to draw primitive on. - /// Start position of primitive in scene coordinates. - /// End position of primitive in scene coordinates. - /// Color to draw primitive with. - /// Thickness of the line in pixels. + /// @param player Player screen to draw primitive on. + /// @param startPos Start position of primitive in scene coordinates. + /// @param endPos End position of primitive in scene coordinates. + /// @param color Color to draw primitive with. + /// @param thickness Thickness of the line in pixels. void DrawLinePrimitive(int player, const Vector& startPos, const Vector& endPos, unsigned char color, int thickness); - /// /// Schedule to draw an arc primitive. - /// - /// Position of primitive's center in scene coordinates. - /// The angle from which the arc drawing begins. - /// The angle at which the arc drawing ends. - /// Radius of the arc primitive. - /// Color to draw primitive with. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param startAngle The angle from which the arc drawing begins. + /// @param endAngle The angle at which the arc drawing ends. + /// @param radius Radius of the arc primitive. + /// @param color Color to draw primitive with. void DrawArcPrimitive(const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color); - /// /// Schedule to draw an arc primitive with the option to change thickness. - /// - /// Position of primitive's center in scene coordinates. - /// The angle from which the arc drawing begins. - /// The angle at which the arc drawing ends. - /// Radius of the arc primitive. - /// Color to draw primitive with. - /// Thickness of the arc in pixels. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param startAngle The angle from which the arc drawing begins. + /// @param endAngle The angle at which the arc drawing ends. + /// @param radius Radius of the arc primitive. + /// @param color Color to draw primitive with. + /// @param thickness Thickness of the arc in pixels. void DrawArcPrimitive(const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness); - /// /// Schedule to draw an arc primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// The angle from which the arc drawing begins. - /// The angle at which the arc drawing ends. - /// Radius of the arc primitive. - /// Color to draw primitive with. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param startAngle The angle from which the arc drawing begins. + /// @param endAngle The angle at which the arc drawing ends. + /// @param radius Radius of the arc primitive. + /// @param color Color to draw primitive with. void DrawArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color); - /// /// Schedule to draw an arc primitive visible only to a specified player with the option to change thickness. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// The angle from which the arc drawing begins. - /// The angle at which the arc drawing ends. - /// Radius of the arc primitive. - /// Color to draw primitive with. - /// Thickness of the arc in pixels. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param startAngle The angle from which the arc drawing begins. + /// @param endAngle The angle at which the arc drawing ends. + /// @param radius Radius of the arc primitive. + /// @param color Color to draw primitive with. + /// @param thickness Thickness of the arc in pixels. void DrawArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, unsigned char color, int thickness); - /// /// Schedule to draw a Bezier spline primitive. - /// - /// Start position of primitive in scene coordinates. - /// The first guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. - /// The second guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. - /// End position of primitive in scene coordinates. - /// Color to draw primitive with. + /// @param startPos Start position of primitive in scene coordinates. + /// @param guideA The first guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. + /// @param guideB The second guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. + /// @param endPos End position of primitive in scene coordinates. + /// @param color Color to draw primitive with. void DrawSplinePrimitive(const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color); - /// /// Schedule to draw a Bezier spline primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Start position of primitive in scene coordinates. - /// The first guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. - /// The second guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. - /// End position of primitive in scene coordinates. - /// Color to draw primitive with. + /// @param player Player screen to draw primitive on. + /// @param startPos Start position of primitive in scene coordinates. + /// @param guideA The first guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. + /// @param guideB The second guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. + /// @param endPos End position of primitive in scene coordinates. + /// @param color Color to draw primitive with. void DrawSplinePrimitive(int player, const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color); - /// /// Schedule to draw a box primitive. - /// - /// Start position of primitive in scene coordinates. Top left corner. - /// End position of primitive in scene coordinates. Bottom right corner. - /// Color to draw primitive with. + /// @param topLeftPos Start position of primitive in scene coordinates. Top left corner. + /// @param bottomRightPos End position of primitive in scene coordinates. Bottom right corner. + /// @param color Color to draw primitive with. void DrawBoxPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color); - /// /// Schedule to draw a box primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Start position of primitive in scene coordinates. Top left corner. - /// End position of primitive in scene coordinates. Bottom right corner. - /// Color to draw primitive with. + /// @param player Player screen to draw primitive on. + /// @param topLeftPos Start position of primitive in scene coordinates. Top left corner. + /// @param bottomRightPos End position of primitive in scene coordinates. Bottom right corner. + /// @param color Color to draw primitive with. void DrawBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color); - /// /// Schedule to draw a filled box primitive. - /// - /// Start position of primitive in scene coordinates. Top left corner. - /// End position of primitive in scene coordinates. Bottom right corner. - /// Color to draw primitive with. + /// @param topLeftPos Start position of primitive in scene coordinates. Top left corner. + /// @param bottomRightPos End position of primitive in scene coordinates. Bottom right corner. + /// @param color Color to draw primitive with. void DrawBoxFillPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color); - /// /// Schedule to draw a filled box primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Start position of primitive in scene coordinates. Top left corner. - /// End position of primitive in scene coordinates. Bottom right corner. - /// Color to draw primitive with. + /// @param player Player screen to draw primitive on. + /// @param topLeftPos Start position of primitive in scene coordinates. Top left corner. + /// @param bottomRightPos End position of primitive in scene coordinates. Bottom right corner. + /// @param color Color to draw primitive with. void DrawBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color); - /// /// Schedule to draw a rounded box primitive. - /// - /// Start position of primitive in scene coordinates. Top left corner. - /// End position of primitive in scene coordinates. Bottom right corner. - /// The radius of the corners of the box. Smaller radius equals sharper corners. - /// Color to draw primitive with. + /// @param topLeftPos Start position of primitive in scene coordinates. Top left corner. + /// @param bottomRightPos End position of primitive in scene coordinates. Bottom right corner. + /// @param cornerRadius The radius of the corners of the box. Smaller radius equals sharper corners. + /// @param color Color to draw primitive with. void DrawRoundedBoxPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color); - /// /// Schedule to draw a rounded box primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Start position of primitive in scene coordinates. Top left corner. - /// End position of primitive in scene coordinates. Bottom right corner. - /// The radius of the corners of the box. Smaller radius equals sharper corners. - /// Color to draw primitive with. + /// @param player Player screen to draw primitive on. + /// @param topLeftPos Start position of primitive in scene coordinates. Top left corner. + /// @param bottomRightPos End position of primitive in scene coordinates. Bottom right corner. + /// @param cornerRadius The radius of the corners of the box. Smaller radius equals sharper corners. + /// @param color Color to draw primitive with. void DrawRoundedBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color); - /// /// Schedule to draw a filled rounded box primitive. - /// - /// Start position of primitive in scene coordinates. Top left corner. - /// End position of primitive in scene coordinates. Bottom right corner. - /// The radius of the corners of the box. Smaller radius equals sharper corners. - /// Color to draw primitive with. + /// @param topLeftPos Start position of primitive in scene coordinates. Top left corner. + /// @param bottomRightPos End position of primitive in scene coordinates. Bottom right corner. + /// @param cornerRadius The radius of the corners of the box. Smaller radius equals sharper corners. + /// @param color Color to draw primitive with. void DrawRoundedBoxFillPrimitive(const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color); - /// /// Schedule to draw a filled rounded box primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Start position of primitive in scene coordinates. Top left corner. - /// End position of primitive in scene coordinates. Bottom right corner. - /// The radius of the corners of the box. Smaller radius equals sharper corners. - /// Color to draw primitive with. + /// @param player Player screen to draw primitive on. + /// @param topLeftPos Start position of primitive in scene coordinates. Top left corner. + /// @param bottomRightPos End position of primitive in scene coordinates. Bottom right corner. + /// @param cornerRadius The radius of the corners of the box. Smaller radius equals sharper corners. + /// @param color Color to draw primitive with. void DrawRoundedBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color); - /// /// Schedule to draw a circle primitive. - /// - /// Position of primitive's center in scene coordinates. - /// Radius of circle primitive. - /// Color to draw primitive with. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param radius Radius of circle primitive. + /// @param color Color to draw primitive with. void DrawCirclePrimitive(const Vector& centerPos, int radius, unsigned char color); - /// /// Schedule to draw a circle primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// Radius of circle primitive. - /// Color to draw primitive with. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param radius Radius of circle primitive. + /// @param color Color to draw primitive with. void DrawCirclePrimitive(int player, const Vector& centerPos, int radius, unsigned char color); - /// /// Schedule to draw a filled circle primitive. - /// - /// Position of primitive's center in scene coordinates. - /// Radius of circle primitive. - /// Color to fill primitive with. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param radius Radius of circle primitive. + /// @param color Color to fill primitive with. void DrawCircleFillPrimitive(const Vector& centerPos, int radius, unsigned char color); - /// /// Schedule to draw a filled circle primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// Radius of circle primitive. - /// Color to fill primitive with. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param radius Radius of circle primitive. + /// @param color Color to fill primitive with. void DrawCircleFillPrimitive(int player, const Vector& centerPos, int radius, unsigned char color); - /// /// Schedule to draw an ellipse primitive. - /// - /// Position of primitive's center in scene coordinates. - /// Horizontal radius of the ellipse primitive. - /// Vertical radius of the ellipse primitive. - /// Color to draw primitive with. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param horizRadius Horizontal radius of the ellipse primitive. + /// @param vertRadius Vertical radius of the ellipse primitive. + /// @param color Color to draw primitive with. void DrawEllipsePrimitive(const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color); - /// /// Schedule to draw an ellipse primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// Horizontal radius of the ellipse primitive. - /// Vertical radius of the ellipse primitive. - /// Color to draw primitive with. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param horizRadius Horizontal radius of the ellipse primitive. + /// @param vertRadius Vertical radius of the ellipse primitive. + /// @param color Color to draw primitive with. void DrawEllipsePrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color); - /// /// Schedule to draw a filled ellipse primitive. - /// - /// Position of primitive's center in scene coordinates. - /// Horizontal radius of the ellipse primitive. - /// Vertical radius of the ellipse primitive. - /// Color to fill primitive with. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param horizRadius Horizontal radius of the ellipse primitive. + /// @param vertRadius Vertical radius of the ellipse primitive. + /// @param color Color to fill primitive with. void DrawEllipseFillPrimitive(const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color); - /// /// Schedule to draw a filled ellipse primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// Horizontal radius of the ellipse primitive. - /// Vertical radius of the ellipse primitive. - /// Color to fill primitive with. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param horizRadius Horizontal radius of the ellipse primitive. + /// @param vertRadius Vertical radius of the ellipse primitive. + /// @param color Color to fill primitive with. void DrawEllipseFillPrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color); - /// /// Schedule to draw a triangle primitive. - /// - /// Position of the first point of the triangle in scene coordinates. - /// Position of the second point of the triangle in scene coordinates. - /// Position of the third point of the triangle in scene coordinates. - /// Color to fill primitive with. + /// @param pointA Position of the first point of the triangle in scene coordinates. + /// @param pointB Position of the second point of the triangle in scene coordinates. + /// @param pointC Position of the third point of the triangle in scene coordinates. + /// @param color Color to fill primitive with. void DrawTrianglePrimitive(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color); - /// /// Schedule to draw a triangle primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of the first point of the triangle in scene coordinates. - /// Position of the second point of the triangle in scene coordinates. - /// Position of the third point of the triangle in scene coordinates. - /// Color to fill primitive with. + /// @param player Player screen to draw primitive on. + /// @param pointA Position of the first point of the triangle in scene coordinates. + /// @param pointB Position of the second point of the triangle in scene coordinates. + /// @param pointC Position of the third point of the triangle in scene coordinates. + /// @param color Color to fill primitive with. void DrawTrianglePrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color); - /// /// Schedule to draw a filled triangle primitive. - /// - /// Position of the first point of the triangle in scene coordinates. - /// Position of the second point of the triangle in scene coordinates. - /// Position of the third point of the triangle in scene coordinates. - /// Color to fill primitive with. + /// @param pointA Position of the first point of the triangle in scene coordinates. + /// @param pointB Position of the second point of the triangle in scene coordinates. + /// @param pointC Position of the third point of the triangle in scene coordinates. + /// @param color Color to fill primitive with. void DrawTriangleFillPrimitive(const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color); - /// /// Schedule to draw a filled triangle primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of the first point of the triangle in scene coordinates. - /// Position of the second point of the triangle in scene coordinates. - /// Position of the third point of the triangle in scene coordinates. - /// Color to fill primitive with. + /// @param player Player screen to draw primitive on. + /// @param pointA Position of the first point of the triangle in scene coordinates. + /// @param pointB Position of the second point of the triangle in scene coordinates. + /// @param pointC Position of the third point of the triangle in scene coordinates. + /// @param color Color to fill primitive with. void DrawTriangleFillPrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color); - /// /// Schedule to draw a polygon primitive visible only to a specified player. - /// - /// Player screen to draw primitive on, or -1 for all players. - /// Start position of the primitive in scene coordinates. - /// Color to draw primitive with. - /// A vector containing the positions of the vertices of the polygon, relative to the center position. - /// Whether a PolygonFillPrimitive should be scheduled instead of PolygonPrimitive. + /// @param player Player screen to draw primitive on, or -1 for all players. + /// @param startPos Start position of the primitive in scene coordinates. + /// @param color Color to draw primitive with. + /// @param vertices A vector containing the positions of the vertices of the polygon, relative to the center position. + /// @param filled Whether a PolygonFillPrimitive should be scheduled instead of PolygonPrimitive. void DrawPolygonOrPolygonFillPrimitive(int player, const Vector& startPos, unsigned char color, const std::vector& vertices, bool filled); - /// /// Schedule to draw a text primitive. - /// - /// Start position of primitive in scene coordinates. - /// Text string to draw. - /// Use small or large font. True for small font. - /// Alignment of text. + /// @param start Start position of primitive in scene coordinates. + /// @param text Text string to draw. + /// @param isSmall Use small or large font. True for small font. + /// @param alignment Alignment of text. void DrawTextPrimitive(const Vector& start, const std::string& text, bool isSmall, int alignment); - /// /// Schedule to draw a text primitive. - /// - /// Start position of primitive in scene coordinates. - /// Text string to draw. - /// Use small or large font. True for small font. - /// Alignment of text. - /// Angle to rotate text in radians. + /// @param start Start position of primitive in scene coordinates. + /// @param text Text string to draw. + /// @param isSmall Use small or large font. True for small font. + /// @param alignment Alignment of text. + /// @param rotAngle Angle to rotate text in radians. void DrawTextPrimitive(const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle); - /// /// Schedule to draw a text primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Start position of primitive in scene coordinates. - /// Text string to draw. - /// Use small or large font. True for small font. - /// Alignment of text. + /// @param player Player screen to draw primitive on. + /// @param start Start position of primitive in scene coordinates. + /// @param text Text string to draw. + /// @param isSmall Use small or large font. True for small font. + /// @param alignment Alignment of text. void DrawTextPrimitive(int player, const Vector& start, const std::string& text, bool isSmall, int alignment); - /// /// Schedule to draw a text primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Start position of primitive in scene coordinates. - /// Text string to draw. - /// Use small or large font. True for small font. - /// Alignment of text. - /// Angle to rotate text in radians. + /// @param player Player screen to draw primitive on. + /// @param start Start position of primitive in scene coordinates. + /// @param text Text string to draw. + /// @param isSmall Use small or large font. True for small font. + /// @param alignment Alignment of text. + /// @param rotAngle Angle to rotate text in radians. void DrawTextPrimitive(int player, const Vector& start, const std::string& text, bool isSmall, int alignment, float rotAngle); - /// /// Schedule to draw a bitmap primitive. - /// - /// Position of primitive's center in scene coordinates. - /// A MOSprite to draw BITMAP from. - /// Rotation angle in radians. - /// Frame to draw. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param moSprite A MOSprite to draw BITMAP from. + /// @param rotAngle Rotation angle in radians. + /// @param frame Frame to draw. void DrawBitmapPrimitive(const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame) { DrawBitmapPrimitive(-1, centerPos, moSprite, rotAngle, frame, false, false); } - /// /// Schedule to draw a bitmap primitive with the option to flip the primitive horizontally and vertically. - /// - /// Position of primitive's center in scene coordinates. - /// A MOSprite to draw BITMAP from. - /// Rotation angle in radians. - /// Frame to draw. - /// Whether to flip the sprite horizontally. - /// Whether to flip the sprite vertically. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param moSprite A MOSprite to draw BITMAP from. + /// @param rotAngle Rotation angle in radians. + /// @param frame Frame to draw. + /// @param hFlipped Whether to flip the sprite horizontally. + /// @param vFlipped Whether to flip the sprite vertically. void DrawBitmapPrimitive(const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped) { DrawBitmapPrimitive(-1, centerPos, moSprite, rotAngle, frame, hFlipped, vFlipped); } - /// /// Schedule to draw a bitmap primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// A MOSprite to draw BITMAP from. - /// Rotation angle in radians. - /// Frame to draw. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param moSprite A MOSprite to draw BITMAP from. + /// @param rotAngle Rotation angle in radians. + /// @param frame Frame to draw. void DrawBitmapPrimitive(int player, const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame) { DrawBitmapPrimitive(player, centerPos, moSprite, rotAngle, frame, false, false); } - /// /// Schedule to draw a bitmap primitive visible only to a specified player with the option to flip the primitive horizontally or vertically. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// A MOSprite to draw BITMAP from. - /// Rotation angle in radians. - /// Frame to draw. - /// Whether to flip the sprite horizontally. - /// Whether to flip the sprite vertically. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param moSprite A MOSprite to draw BITMAP from. + /// @param rotAngle Rotation angle in radians. + /// @param frame Frame to draw. + /// @param hFlipped Whether to flip the sprite horizontally. + /// @param vFlipped Whether to flip the sprite vertically. void DrawBitmapPrimitive(int player, const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped); - /// /// Schedule to draw a bitmap primitive. - /// - /// Position of primitive's center in scene coordinates. - /// Path to the bitmap to draw. - /// Rotation angle in radians. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param filePath Path to the bitmap to draw. + /// @param rotAngle Rotation angle in radians. void DrawBitmapPrimitive(const Vector& centerPos, const std::string& filePath, float rotAngle) { DrawBitmapPrimitive(-1, centerPos, filePath, rotAngle, false, false); } - /// /// Schedule to draw a bitmap primitive with the option to flip the primitive horizontally and vertically. - /// - /// Position of primitive's center in scene coordinates. - /// An entity to draw sprite from. - /// Rotation angle in radians. - /// Whether to flip the sprite horizontally. - /// Whether to flip the sprite vertically. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param filePath An entity to draw sprite from. + /// @param rotAngle Rotation angle in radians. + /// @param hFlipped Whether to flip the sprite horizontally. + /// @param vFlipped Whether to flip the sprite vertically. void DrawBitmapPrimitive(const Vector& centerPos, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped) { DrawBitmapPrimitive(-1, centerPos, filePath, rotAngle, hFlipped, vFlipped); } - /// /// Schedule to draw a bitmap primitive visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// Path to the bitmap to draw. - /// Rotation angle in radians. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param filePath Path to the bitmap to draw. + /// @param rotAngle Rotation angle in radians. void DrawBitmapPrimitive(int player, const Vector& centerPos, const std::string& filePath, float rotAngle) { DrawBitmapPrimitive(player, centerPos, filePath, rotAngle, false, false); } - /// /// Schedule to draw a bitmap primitive visible only to a specified player with the option to flip the primitive horizontally or vertically. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// Path to the bitmap to draw. - /// Rotation angle in radians. - /// Whether to flip the sprite horizontally. - /// Whether to flip the sprite vertically. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param filePath Path to the bitmap to draw. + /// @param rotAngle Rotation angle in radians. + /// @param hFlipped Whether to flip the sprite horizontally. + /// @param vFlipped Whether to flip the sprite vertically. void DrawBitmapPrimitive(int player, const Vector& centerPos, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped); - /// /// Schedule to draw the GUI icon of an object. - /// - /// Position of primitive's center in scene coordinates. - /// An entity to draw sprite from. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param entity An entity to draw sprite from. void DrawIconPrimitive(const Vector& centerPos, Entity* entity) { DrawIconPrimitive(-1, centerPos, entity); } - /// /// Schedule to draw the GUI icon of an object, visible only to a specified player. - /// - /// Player screen to draw primitive on. - /// Position of primitive's center in scene coordinates. - /// An entity to draw sprite from. + /// @param player Player screen to draw primitive on. + /// @param centerPos Position of primitive's center in scene coordinates. + /// @param entity An entity to draw sprite from. void DrawIconPrimitive(int player, const Vector& centerPos, Entity* entity); #pragma endregion @@ -486,18 +386,14 @@ namespace RTE { std::deque> m_ScheduledPrimitives; //!< List of graphical primitives scheduled to draw this frame, cleared every frame during FrameMan::Draw(). private: - /// /// Constructs a unique_ptr of the appropriate derived type from the passed in GraphicalPrimitive raw pointer. /// This is used for preparing primitives constructed in Lua for scheduling. - /// - /// Raw pointer to the GraphicalPrimitive object to make unique. - /// A unique_ptr of the appropriate derived GraphicalPrimitive type. Ownership is transferred! + /// @param primitive Raw pointer to the GraphicalPrimitive object to make unique. + /// @return A unique_ptr of the appropriate derived GraphicalPrimitive type. Ownership is transferred! std::unique_ptr MakeUniqueOfAppropriateTypeFromPrimitiveRawPtr(GraphicalPrimitive* primitive); - /// /// Safely schedules a primite to draw in a thread-safe manner. - /// - /// A unique ptr of the primitive to schedule for drawing. + /// @param primitive A unique ptr of the primitive to schedule for drawing. void SchedulePrimitive(std::unique_ptr&& primitive); // Disallow the use of some implicit methods. diff --git a/Source/Managers/SceneMan.cpp b/Source/Managers/SceneMan.cpp index 6974c4aec6..e0eb414649 100644 --- a/Source/Managers/SceneMan.cpp +++ b/Source/Managers/SceneMan.cpp @@ -1,14 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneMan.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the SceneMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files #include "NetworkServer.h" #include "NetworkClient.h" @@ -47,8 +36,6 @@ namespace RTE { // Stored as a thread-local instead of in the class, because multithreaded Lua scripts will interfere otherwise thread_local Vector s_LastRayHitPos; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::Clear() { m_DefaultSceneName = "Tutorial Bunker"; m_pSceneToLoad = nullptr; @@ -82,8 +69,6 @@ namespace RTE { m_ScrapCompactingHeight = 25; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::Initialize() const { // Can't create these earlier in the static declaration because allegro_init needs to be called before create_bitmap m_IntermediateSettlingBitmaps = { @@ -99,8 +84,6 @@ namespace RTE { {512, create_bitmap_ex(8, 512, 512)}}; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::Create(std::string readerFile) { Reader* reader = new Reader(); if (reader->Create(readerFile.c_str())) @@ -112,8 +95,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Material* SceneMan::AddMaterialCopy(Material* mat) { Material* matCopy = dynamic_cast(mat->Clone()); if (matCopy) @@ -122,8 +103,6 @@ namespace RTE { return matCopy; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::LoadScene(Scene* pNewScene, bool placeObjects, bool placeUnits) { if (!pNewScene) { return -1; @@ -205,8 +184,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::SetSceneToLoad(std::string sceneName, bool placeObjects, bool placeUnits) { // Use the name passed in to load the preset requested const Scene* pSceneRef = dynamic_cast(g_PresetMan.GetEntityPreset("Scene", sceneName)); @@ -222,8 +199,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::LoadScene() { // In case we have no set Scene reference to load from, do something graceful about it if (!m_pSceneToLoad) { @@ -241,8 +216,6 @@ namespace RTE { return LoadScene(dynamic_cast(m_pSceneToLoad->Clone()), m_PlaceObjects, m_PlaceUnits); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::LoadScene(std::string sceneName, bool placeObjects, bool placeUnits) { // First retrieve and set up the preset reference int error = SetSceneToLoad(sceneName, placeObjects, placeUnits); @@ -253,8 +226,6 @@ namespace RTE { return error; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -307,8 +278,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::Save(Writer& writer) const { g_ConsoleMan.PrintString("ERROR: Tried to save SceneMan, screen does not make sense"); @@ -321,8 +290,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::Destroy() { for (int i = 0; i < c_PaletteEntriesNumber; ++i) delete m_apMatPalette[i]; @@ -343,8 +310,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector SceneMan::GetSceneDim() const { if (m_pCurrentScene) { RTEAssert(m_pCurrentScene->GetTerrain() && m_pCurrentScene->GetTerrain()->GetBitmap(), "Trying to get terrain info before there is a scene or terrain!"); @@ -353,8 +318,6 @@ namespace RTE { return Vector(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::GetSceneWidth() const { if (g_NetworkClient.IsConnectedAndRegistered()) { return g_NetworkClient.GetSceneWidth(); @@ -365,8 +328,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::GetSceneHeight() const { // RTEAssert(m_pCurrentScene, "Trying to get terrain info before there is a scene or terrain!"); if (m_pCurrentScene) @@ -374,8 +335,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::SceneWrapsX() const { if (g_NetworkClient.IsConnectedAndRegistered()) { return g_NetworkClient.SceneWrapsX(); @@ -386,16 +345,12 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::SceneWrapsY() const { if (m_pCurrentScene) return m_pCurrentScene->WrapsY(); return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Directions SceneMan::GetSceneOrbitDirection() const { if (m_pCurrentScene) { SLTerrain* terrain = m_pCurrentScene->GetTerrain(); @@ -407,8 +362,6 @@ namespace RTE { return Directions::Up; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SLTerrain* SceneMan::GetTerrain() { // RTEAssert(m_pCurrentScene, "Trying to get terrain matter before there is a scene or terrain!"); if (m_pCurrentScene) { @@ -418,27 +371,19 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* SceneMan::GetMOColorBitmap() const { return m_pMOColorLayer->GetBitmap(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* SceneMan::GetDebugBitmap() const { RTEAssert(m_pDebugLayer, "Tried to get debug bitmap but debug layer doesn't exist. Note that the debug layer is only created under certain circumstances."); return m_pDebugLayer->GetBitmap(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* SceneMan::GetMOIDBitmap() const { return m_pMOIDLayer->GetBitmap(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TEMP! bool SceneMan::MOIDClearCheck() { BITMAP* pMOIDMap = m_pMOIDLayer->GetBitmap(); @@ -456,8 +401,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - unsigned char SceneMan::GetTerrMatter(int pixelX, int pixelY) { RTEAssert(m_pCurrentScene, "Trying to get terrain matter before there is a scene or terrain!"); @@ -484,8 +427,6 @@ namespace RTE { return getpixel(pTMatBitmap, pixelX, pixelY); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MOID SceneMan::GetMOIDPixel(int pixelX, int pixelY, int ignoreTeam) { WrapPosition(pixelX, pixelY); @@ -516,8 +457,6 @@ namespace RTE { return moid; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Material const* SceneMan::GetMaterial(const std::string& matName) { std::map::iterator itr = m_MatNameMap.find(matName); if (itr == m_MatNameMap.end()) { @@ -527,15 +466,11 @@ namespace RTE { return m_apMatPalette.at((*itr).second); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector SceneMan::GetGlobalAcc() const { RTEAssert(m_pCurrentScene, "Trying to get terrain matter before there is a scene or terrain!"); return m_pCurrentScene->GetGlobalAcc(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::LockScene() { // RTEAssert(!m_pCurrentScene->IsLocked(), "Hey, locking already locked scene!"); if (m_pCurrentScene && !m_pCurrentScene->IsLocked()) { @@ -545,8 +480,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::UnlockScene() { // RTEAssert(m_pCurrentScene->IsLocked(), "Hey, unlocking already unlocked scene!"); if (m_pCurrentScene && m_pCurrentScene->IsLocked()) { @@ -556,15 +489,11 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::SceneIsLocked() const { RTEAssert(m_pCurrentScene, "Trying to check if scene is locked before there is a scene or terrain!"); return m_pCurrentScene->IsLocked(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::RegisterDrawing(const BITMAP* bitmap, int moid, int left, int top, int right, int bottom) { if (m_pMOColorLayer && m_pMOColorLayer->GetBitmap() == bitmap) { m_pMOColorLayer->RegisterDrawing(left, top, right, bottom); @@ -581,16 +510,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::RegisterDrawing(const BITMAP* bitmap, int moid, const Vector& center, float radius) { if (radius != 0.0F) { RegisterDrawing(bitmap, moid, static_cast(std::floor(center.m_X - radius)), static_cast(std::floor(center.m_Y - radius)), static_cast(std::floor(center.m_X + radius)), static_cast(std::floor(center.m_Y + radius))); } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::ClearAllMOIDDrawings() { #ifdef DRAW_MOID_LAYER m_pMOIDLayer->ClearBitmap(g_NoMOID); @@ -599,8 +524,6 @@ namespace RTE { #endif } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::WillPenetrate(const int posX, const int posY, const Vector& impulse) { @@ -614,8 +537,6 @@ namespace RTE { return impulse.MagnitudeIsGreaterThan(integrity); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::RemoveOrphans(int posX, int posY, int radius, int maxArea, bool remove) { if (radius > MAXORPHANRADIUS) radius = MAXORPHANRADIUS; @@ -630,8 +551,6 @@ namespace RTE { return area; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::RemoveOrphans(int posX, int posY, int centerPosX, int centerPosY, int accumulatedArea, int radius, int maxArea, bool remove) { @@ -717,8 +636,6 @@ namespace RTE { return area; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::RegisterTerrainChange(int x, int y, int w, int h, unsigned char color, bool back) { if (!g_NetworkServer.IsServerModeEnabled()) return; @@ -815,8 +732,6 @@ namespace RTE { g_NetworkServer.RegisterTerrainChange(tc); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::TryPenetrate(int posX, int posY, const Vector& impulse, @@ -967,8 +882,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MovableObject* SceneMan::DislodgePixel(int posX, int posY) { int materialID = getpixel(m_pCurrentScene->GetTerrain()->GetMaterialBitmap(), posX, posY); if (materialID <= MaterialColorKeys::g_MaterialAir) { @@ -999,8 +912,6 @@ namespace RTE { return pixelMO; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::MakeAllUnseen(Vector pixelSize, const int team) { RTEAssert(m_pCurrentScene, "Messing with scene before the scene exists!"); if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) @@ -1009,8 +920,6 @@ namespace RTE { m_pCurrentScene->FillUnseenLayer(pixelSize, team); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::LoadUnseenLayer(std::string bitmapPath, int team) { ContentFile bitmapFile(bitmapPath.c_str()); SceneLayer* pUnseenLayer = new SceneLayer(); @@ -1024,8 +933,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::AnythingUnseen(const int team) { RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when checking if anything is unseen!"); @@ -1033,8 +940,6 @@ namespace RTE { // TODO: Actually check all pixels on the map too? } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector SceneMan::GetUnseenResolution(const int team) const { RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when getting unseen resolution!"); if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) @@ -1047,8 +952,6 @@ namespace RTE { return Vector(1, 1); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::IsUnseen(const int posX, const int posY, const int team) { RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when checking if a position is unseen!"); if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) @@ -1066,8 +969,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::RevealUnseen(const int posX, const int posY, const int team) { RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when revealing an unseen position!"); if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) @@ -1098,8 +999,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::RestoreUnseen(const int posX, const int posY, const int team) { RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when making a position unseen!"); if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) @@ -1130,8 +1029,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::RevealUnseenBox(const int posX, const int posY, const int width, const int height, const int team) { RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when revealing an unseen area!"); if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) @@ -1151,8 +1048,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::RestoreUnseenBox(const int posX, const int posY, const int width, const int height, const int team) { RTEAssert(m_pCurrentScene, "Checking scene before the scene exists when making an area unseen!"); if (team < Activity::TeamOne || team >= Activity::MaxTeamCount) @@ -1172,8 +1067,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO Every raycast should use some shared line drawing method (or maybe something more efficient if it exists, that needs looking into) instead of having a ton of duplicated code. bool SceneMan::CastUnseenRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip, bool reveal) { if (!m_pCurrentScene->GetUnseenLayer(team)) @@ -1270,20 +1163,14 @@ namespace RTE { return affectedAny; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::CastSeeRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip) { return CastUnseenRay(team, start, ray, endPos, strengthLimit, skip, true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::CastUnseeRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip) { return CastUnseenRay(team, start, ray, endPos, strengthLimit, skip, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::CastMaterialRay(const Vector& start, const Vector& ray, unsigned char material, Vector& result, int skip, bool wrap) { int hitCount = 0, error, dom, sub, domSteps, skipped = skip; @@ -1366,8 +1253,6 @@ namespace RTE { return foundPixel; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float SceneMan::CastMaterialRay(const Vector& start, const Vector& ray, unsigned char material, int skip) { Vector result; if (CastMaterialRay(start, ray, material, result, skip)) { @@ -1380,8 +1265,6 @@ namespace RTE { return -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::CastNotMaterialRay(const Vector& start, const Vector& ray, unsigned char material, Vector& result, int skip, bool checkMOs) { int hitCount = 0, error, dom, sub, domSteps, skipped = skip; int intPos[2], delta[2], delta2[2], increment[2]; @@ -1463,8 +1346,6 @@ namespace RTE { return foundPixel; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float SceneMan::CastNotMaterialRay(const Vector& start, const Vector& ray, unsigned char material, int skip, bool checkMOs) { Vector result; if (CastNotMaterialRay(start, ray, material, result, skip, checkMOs)) { @@ -1477,8 +1358,6 @@ namespace RTE { return -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float SceneMan::CastStrengthSumRay(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial) { Vector ray = g_SceneMan.ShortestDistance(start, end); float strengthSum = 0; @@ -1560,14 +1439,10 @@ namespace RTE { return strengthSum; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float SceneMan::CastMaxStrengthRay(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial) { return CastMaxStrengthRayMaterial(start, end, skip, ignoreMaterial)->GetIntegrity(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Material* SceneMan::CastMaxStrengthRayMaterial(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial) { Vector ray = g_SceneMan.ShortestDistance(start, end); const Material* strongestMaterial = GetMaterialFromID(MaterialColorKeys::g_MaterialAir); @@ -1651,8 +1526,6 @@ namespace RTE { return strongestMaterial; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::CastStrengthRay(const Vector& start, const Vector& ray, float strength, Vector& result, int skip, unsigned char ignoreMaterial, bool wrap) { int hitCount = 0, error, dom, sub, domSteps, skipped = skip; int intPos[2], delta[2], delta2[2], increment[2]; @@ -1746,8 +1619,6 @@ namespace RTE { return foundPixel; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::CastWeaknessRay(const Vector& start, const Vector& ray, float strength, Vector& result, int skip, bool wrap) { int hitCount = 0, error, dom, sub, domSteps, skipped = skip; int intPos[2], delta[2], delta2[2], increment[2]; @@ -1838,8 +1709,6 @@ namespace RTE { return foundPixel; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MOID SceneMan::CastMORay(const Vector& start, const Vector& ray, MOID ignoreMOID, int ignoreTeam, unsigned char ignoreMaterial, bool ignoreAllTerrain, int skip) { int hitCount = 0, error, dom, sub, domSteps, skipped = skip; int intPos[2], delta[2], delta2[2], increment[2]; @@ -1950,8 +1819,6 @@ namespace RTE { return g_NoMOID; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::CastFindMORay(const Vector& start, const Vector& ray, MOID targetMOID, Vector& resultPos, unsigned char ignoreMaterial, bool ignoreAllTerrain, int skip) { int hitCount = 0, error, dom, sub, domSteps, skipped = skip; int intPos[2], delta[2], delta2[2], increment[2]; @@ -2044,8 +1911,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float SceneMan::CastObstacleRay(const Vector& start, const Vector& ray, Vector& obstaclePos, Vector& freePos, MOID ignoreMOID, int ignoreTeam, unsigned char ignoreMaterial, int skip) { int hitCount = 0, error, dom, sub, domSteps, skipped = skip; int intPos[2], delta[2], delta2[2], increment[2]; @@ -2174,15 +2039,11 @@ namespace RTE { return -1.0F; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Vector& SceneMan::GetLastRayHitPos() { // The absolute end position of the last ray cast return s_LastRayHitPos; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float SceneMan::FindAltitude(const Vector& from, int max, int accuracy, bool fromSceneOrbitDirection) { // TODO: Also make this avoid doors Vector temp(from); @@ -2206,16 +2067,12 @@ namespace RTE { return orbitDirection == Directions::Up ? result : g_SceneMan.GetSceneHeight() - result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::OverAltitude(const Vector& point, int threshold, int accuracy) { Vector temp(point); ForceBounds(temp); return g_SceneMan.CastNotMaterialRay(temp, Vector(0, threshold), g_MaterialAir, accuracy) < 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector SceneMan::MovePointToGround(const Vector& from, int maxAltitude, int accuracy) { // Todo, instead of a nograv area maybe best to tag certain areas as NoGrav. As otherwise it's tricky to keep track of when things are removed if (m_pCurrentScene) { @@ -2240,8 +2097,6 @@ namespace RTE { return groundPoint; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::IsWithinBounds(const int pixelX, const int pixelY, const int margin) { if (m_pCurrentScene) return m_pCurrentScene->GetTerrain()->IsWithinBounds(pixelX, pixelY, margin); @@ -2249,15 +2104,11 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::ForceBounds(int& posX, int& posY) { RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); return m_pCurrentScene->GetTerrain()->ForceBounds(posX, posY); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::ForceBounds(Vector& pos) { RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); @@ -2272,15 +2123,11 @@ namespace RTE { return wrapped; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::WrapPosition(int& posX, int& posY) { RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); return m_pCurrentScene->GetTerrain()->WrapPosition(posX, posY); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::WrapPosition(Vector& pos) { RTEAssert(m_pCurrentScene, "Trying to access scene before there is one!"); @@ -2295,8 +2142,6 @@ namespace RTE { return wrapped; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector SceneMan::SnapPosition(const Vector& pos, bool snap) { Vector snappedPos = pos; @@ -2308,8 +2153,6 @@ namespace RTE { return snappedPos; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector SceneMan::ShortestDistance(Vector pos1, Vector pos2, bool checkBounds) { if (!m_pCurrentScene) return Vector(); @@ -2346,8 +2189,6 @@ namespace RTE { return distance; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float SceneMan::ShortestDistanceX(float val1, float val2, bool checkBounds, int direction) { if (!m_pCurrentScene) return 0; @@ -2384,8 +2225,6 @@ namespace RTE { return distance; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float SceneMan::ShortestDistanceY(float val1, float val2, bool checkBounds, int direction) { if (!m_pCurrentScene) return 0; @@ -2422,8 +2261,6 @@ namespace RTE { return distance; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::ObscuredPoint(int x, int y, int team) { bool obscured = m_pCurrentScene->GetTerrain()->GetPixel(x, y) != g_MaterialAir || GetMOIDPixel(x, y, Activity::NoTeam) != g_NoMOID; @@ -2433,8 +2270,6 @@ namespace RTE { return obscured; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::WrapRect(const IntRect& wrapRect, std::list& outputList) { // Always add at least one copy of the unwrapped rect int addedTimes = 1; @@ -2479,8 +2314,6 @@ namespace RTE { return addedTimes; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SceneMan::WrapBox(const Box& wrapBox, std::list& outputList) { // Unflip the input box, or checking will be tedious Box flipBox(wrapBox); @@ -2525,8 +2358,6 @@ namespace RTE { return addedTimes; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneMan::AddSceneObject(SceneObject* sceneObject) { bool result = false; if (sceneObject) { @@ -2544,8 +2375,6 @@ namespace RTE { return result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::Update(int screenId) { ZoneScoped; @@ -2585,8 +2414,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::Draw(BITMAP* targetBitmap, BITMAP* targetGUIBitmap, const Vector& targetPos, bool skipBackgroundLayers, bool skipTerrain) { ZoneScoped; @@ -2677,8 +2504,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::ClearMOColorLayer() { m_pMOColorLayer->ClearBitmap(g_MaskColor); if (m_pDebugLayer) { @@ -2686,8 +2511,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::ClearSeenPixels() { if (!m_pCurrentScene) return; @@ -2696,14 +2519,10 @@ namespace RTE { m_pCurrentScene->ClearSeenPixels(team); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneMan::ClearCurrentScene() { m_pCurrentScene = nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* SceneMan::GetIntermediateBitmapForSettlingIntoTerrain(int moDiameter) const { int bitmapSizeNeeded = static_cast(std::ceil(static_cast(moDiameter) / 16.0F)) * 16; for (const auto& [bitmapSize, bitmapPtr]: m_IntermediateSettlingBitmaps) { diff --git a/Source/Managers/SceneMan.h b/Source/Managers/SceneMan.h index e4a781aee1..c61b5c2835 100644 --- a/Source/Managers/SceneMan.h +++ b/Source/Managers/SceneMan.h @@ -1,18 +1,11 @@ #ifndef _RTESCENEMAN_ #define _RTESCENEMAN_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneMan.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the SceneMan class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the SceneMan class. +/// @author Daniel Tabar +/// data@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "Serializable.h" #include "Timer.h" #include "Box.h" @@ -50,543 +43,318 @@ namespace RTE { #define SCENESNAPSIZE 12 #define MAXORPHANRADIUS 11 - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: SceneMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The singleton manager of all terrain and backgrounds in the RTE. - // Parent(s): Singleton, Serializable. - // Class history: 12/25/2001 SceneMan created. - + /// The singleton manager of all terrain and backgrounds in the RTE. class SceneMan : public Singleton, public Serializable { friend class SettingsMan; - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: SerializableClassNameGetter; SerializableOverrideMethods; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: SceneMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a SceneMan object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a SceneMan object in system + /// memory. Create() should be called before using the object. SceneMan() { m_pOrphanSearchBitmap = 0; Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~SceneMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a SceneMan object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a SceneMan object before deletion + /// from system memory. ~SceneMan() { Destroy(); } - /// /// Makes the SceneMan object ready for use. - /// void Initialize() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the SceneMan object ready for use. - // Arguments: A string with the filepath to a Reader file from screen this SceneMan's - // data should be created. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the SceneMan object ready for use. + /// @param readerFile A string with the filepath to a Reader file from screen this SceneMan's + /// data should be created. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(std::string readerFile); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetDefaultSceneName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the instance name of the default Scene to be loaded if nothing - // else is available. - // Arguments: The default scene instance name. - // Return value: None. - + /// Sets the instance name of the default Scene to be loaded if nothing + /// else is available. + /// @param defaultSceneName The default scene instance name. void SetDefaultSceneName(std::string defaultSceneName) { m_DefaultSceneName = defaultSceneName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDefaultSceneName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the name of the default A to be loaded if nothing - // else is available. - // Arguments: None. - // Return value: The default Scene instance name. - + /// Gets the name of the default A to be loaded if nothing + /// else is available. + /// @return The default Scene instance name. std::string GetDefaultSceneName() const { return m_DefaultSceneName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Actually loads a new Scene into memory. has to be done before using - // this object. - // Arguments: The instance of the Scene, ownership IS transferred! - // Whether the scene should actually apply all its SceneObject:s placed - // in its definition. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Actually loads a new Scene into memory. has to be done before using + /// this object. + /// @param pNewScene The instance of the Scene, ownership IS transferred! + /// @param placeObjects Whether the scene should actually apply all its SceneObject:s placed (default: true) + /// in its definition. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int LoadScene(Scene* pNewScene, bool placeObjects = true, bool placeUnits = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSceneToLoad - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Stores a Scene reference to be loaded later into the SceneMan. - // Arguments: The instance reference of the Scene, ownership IS NOT (!!) transferred! - // Whether the scene should actually apply all its SceneObject:s placed - // in its definition. - // Return value: None. - + /// Stores a Scene reference to be loaded later into the SceneMan. + /// @param pLoadScene The instance reference of the Scene, ownership IS NOT (!!) transferred! + /// @param placeObjects Whether the scene should actually apply all its SceneObject:s placed (default: true) + /// in its definition. void SetSceneToLoad(const Scene* pLoadScene, bool placeObjects = true, bool placeUnits = true) { m_pSceneToLoad = pLoadScene; m_PlaceObjects = placeObjects; m_PlaceUnits = placeUnits; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetSceneToLoad - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets a scene to load later, by preset name. - // Arguments: The name of the Scene preset instance to load. - // Whether the scene should actually apply all its SceneObject:s placed - // in its definition. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Sets a scene to load later, by preset name. + /// @param sceneName The name of the Scene preset instance to load. + /// @param placeObjects Whether the scene should actually apply all its SceneObject:s placed (default: true) + /// in its definition. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int SetSceneToLoad(std::string sceneName, bool placeObjects = true, bool placeUnits = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSceneToLoad - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the stored Scene reference to be loaded later into the SceneMan. - // Arguments: None. - // Return value: The instance reference of the Scene, ownership IS NOT (!!) transferred! - + /// Gets the stored Scene reference to be loaded later into the SceneMan. + /// @return The instance reference of the Scene, ownership IS NOT (!!) transferred! const Scene* GetSceneToLoad() { return m_pSceneToLoad; } - /// /// Gets whether objects are placed when the Scene is initially started. Used for saving/loading games. - /// - /// Whether objects are placed when the Scene is initially started. + /// @return Whether objects are placed when the Scene is initially started. bool GetPlaceObjectsOnLoad() const { return m_PlaceObjects; } - /// /// Gets whether units are placed when the Scene is initially started. Used for saving/loading games. - /// - /// Whether units are placed when the Scene is initially started. + /// @return Whether units are placed when the Scene is initially started. bool GetPlaceUnitsOnLoad() const { return m_PlaceUnits; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Actually loads the Scene set to be loaded in SetSceneToLoad. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Actually loads the Scene set to be loaded in SetSceneToLoad. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int LoadScene(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads a Scene right now, by preset name. - // Arguments: The name of the Scene preset instance to load. - // Whether the scene should actually apply all its SceneObject:s placed - // in its definition. - // Whether the scene should actually deploy all units placed in its definition. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Loads a Scene right now, by preset name. + /// @param sceneName The name of the Scene preset instance to load. + /// @param placeObjects Whether the scene should actually apply all its SceneObject:s placed (default: true) + /// in its definition. + /// @param placeUnits Whether the scene should actually deploy all units placed in its definition. (default: true) + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int LoadScene(std::string sceneName, bool placeObjects = true, bool placeUnits = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads a Scene right now, by preset name. - // Arguments: The name of the Scene preset instance to load. - // Whether the scene should actually apply all its SceneObject:s placed - // in its definition. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Loads a Scene right now, by preset name. + /// @param sceneName The name of the Scene preset instance to load. + /// @param placeObjects Whether the scene should actually apply all its SceneObject:s placed (default: true) { return LoadScene(sceneName) + /// in its definition. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int LoadScene(std::string sceneName, bool placeObjects = true) { return LoadScene(sceneName, placeObjects, true); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire SceneMan, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire SceneMan, including its inherited members, to + /// their default settings or values. void Reset() override { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneMan object. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneMan object. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the currently loaded scene, if any. - // Arguments: None. - // Return value: The scene, ownership IS NOT TRANSFERRED! - + /// Gets the currently loaded scene, if any. + /// @return The scene, ownership IS NOT TRANSFERRED! Scene* GetScene() const { return m_pCurrentScene; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSceneDim - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total dimensions (width and height) of the scene, in pixels. - // Arguments: None. - // Return value: A Vector describing the scene dimensions. - + /// Gets the total dimensions (width and height) of the scene, in pixels. + /// @return A Vector describing the scene dimensions. Vector GetSceneDim() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSceneWidth - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total width of the scene, in pixels. - // Arguments: None. - // Return value: An int describing the scene width. - + /// Gets the total width of the scene, in pixels. + /// @return An int describing the scene width. int GetSceneWidth() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetSceneHeight - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total height of the scene, in pixels. - // Arguments: None. - // Return value: An int describing the scene width. - + /// Gets the total height of the scene, in pixels. + /// @return An int describing the scene width. int GetSceneHeight() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaterialPalette - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets access to the whole material palette array of 256 entries. - // Arguments: None. - // Return value: A const reference to the material palette array. - + /// Gets access to the whole material palette array of 256 entries. + /// @return A const reference to the material palette array. const std::array& GetMaterialPalette() const { return m_apMatPalette; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaterial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a specific material by name. Ownership is NOT transferred! - // Arguments: The string name of the Material to get. - // Return value: A pointer to the requested material, or 0 if no material with that - // name was found. - + /// Gets a specific material by name. Ownership is NOT transferred! + /// @param matName The string name of the Material to get. + /// @return A pointer to the requested material, or 0 if no material with that + /// name was found. Material const* GetMaterial(const std::string& matName); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMaterialFromID - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a specific material from the material palette. Ownership is NOT - // transferred! - // Arguments: The unsigned char index specifying screen material to get (0-255). - // Return value: A reference to the requested material. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets a specific material from the material palette. Ownership is NOT + /// transferred! + /// @param screen The unsigned char index specifying screen material to get (0-255). + /// @return A reference to the requested material. OWNERSHIP IS NOT TRANSFERRED! Material const* GetMaterialFromID(unsigned char screen) { return screen >= 0 && screen < c_PaletteEntriesNumber && m_apMatPalette[screen] ? m_apMatPalette[screen] : m_apMatPalette[g_MaterialAir]; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SceneWrapsX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the scene wraps its scrolling around the X axis. - // Arguments: None. - // Return value: Whether the scene wraps around the X axis or not. - + /// Indicates whether the scene wraps its scrolling around the X axis. + /// @return Whether the scene wraps around the X axis or not. bool SceneWrapsX() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SceneWrapsY - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the scene wraps its scrolling around the Y axis. - // Arguments: None. - // Return value: Whether the scene wraps around the Y axis or not. - + /// Indicates whether the scene wraps its scrolling around the Y axis. + /// @return Whether the scene wraps around the Y axis or not. bool SceneWrapsY() const; - /// /// Gets the orbit direction for the current scene. - /// - /// The orbit direction for the current scene. + /// @return The orbit direction for the current scene. Directions GetSceneOrbitDirection() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTerrain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the SLTerrain, or 0 if no scene is loaded. - // Arguments: None. - // Return value: A pointer to the SLTerrain. Ownership is NOT transferred! - + /// Gets the SLTerrain, or 0 if no scene is loaded. + /// @return A pointer to the SLTerrain. Ownership is NOT transferred! SLTerrain* GetTerrain(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMOColorBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the bitmap of the intermediary collection SceneLayer that all - // MovableObject:s draw themselves onto before it itself gets drawn onto - // the screen back buffer. - // Arguments: None. - // Return value: A BITMAP pointer to the MO bitmap. Ownership is NOT transferred! - + /// Gets the bitmap of the intermediary collection SceneLayer that all + /// MovableObject:s draw themselves onto before it itself gets drawn onto + /// the screen back buffer. + /// @return A BITMAP pointer to the MO bitmap. Ownership is NOT transferred! BITMAP* GetMOColorBitmap() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDebugBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the bitmap of the SceneLayer that debug graphics is drawn onto. - // Will only return valid BITMAP if building with DEBUG_BUILD. - // Arguments: None. - // Return value: A BITMAP pointer to the debug bitmap. Ownership is NOT transferred! - + /// Gets the bitmap of the SceneLayer that debug graphics is drawn onto. + /// Will only return valid BITMAP if building with DEBUG_BUILD. + /// @return A BITMAP pointer to the debug bitmap. Ownership is NOT transferred! BITMAP* GetDebugBitmap() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMOIDBitmap - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the bitmap of the SceneLayer that all MovableObject:s draw thir - // current (for the frame only!) MOID's onto. - // Arguments: None. - // Return value: A BITMAP pointer to the MO bitmap. Ownership is NOT transferred! - + /// Gets the bitmap of the SceneLayer that all MovableObject:s draw thir + /// current (for the frame only!) MOID's onto. + /// @return A BITMAP pointer to the MO bitmap. Ownership is NOT transferred! BITMAP* GetMOIDBitmap() const; // TEMP! - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: MOIDClearCheck - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes sure the MOID bitmap layer is completely of NoMOID color. - // If found to be not, dumps MOID layer and the FG actor color layer for - // debugging. - // Arguments: None. - // Return value: Was it clear? - + /// Makes sure the MOID bitmap layer is completely of NoMOID color. + /// If found to be not, dumps MOID layer and the FG actor color layer for + /// debugging. + /// @return Was it clear? bool MOIDClearCheck(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLayerDrawMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current drawing mode of the SceneMan. - // Arguments: None. - // Return value: The current layer draw mode, see the LayerDrawMode enumeration for the - // different possible mode settings. - + /// Gets the current drawing mode of the SceneMan. + /// @return The current layer draw mode, see the LayerDrawMode enumeration for the + /// different possible mode settings. int GetLayerDrawMode() const { return m_LayerDrawMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTerrMatter - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets a specific pixel from the total material representation of - // this Scene. LockScene() must be called before using this method. - // Arguments: The X and Y coordinates of screen material pixel to get. - // Return value: An unsigned char specifying the requested pixel's material index. - + /// Gets a specific pixel from the total material representation of + /// this Scene. LockScene() must be called before using this method. + /// @param pixelX The X and Y coordinates of screen material pixel to get. + /// @return An unsigned char specifying the requested pixel's material index. unsigned char GetTerrMatter(int pixelX, int pixelY); - /// /// Gets a MOID from pixel coordinates in the Scene. LockScene() must be called before using this method. - /// - /// The X coordinate of the Scene pixel to test. - /// The Y coordinate of the Scene pixel to test. - /// The team to ignore. - /// The MOID currently at the specified pixel coordinates. + /// @param pixelX The X coordinate of the Scene pixel to test. + /// @param pixelY The Y coordinate of the Scene pixel to test. + /// @param ignoreTeam The team to ignore. + /// @return The MOID currently at the specified pixel coordinates. MOID GetMOIDPixel(int pixelX, int pixelY, int ignoreTeam); - /// /// Gets a MOID from pixel coordinates in the Scene. LockScene() must be called before using this method. - /// - /// The X coordinate of the Scene pixel to test. - /// The Y coordinate of the Scene pixel to test. - /// The MOID currently at the specified pixel coordinates. + /// @param pixelX The X coordinate of the Scene pixel to test. + /// @param pixelY The Y coordinate of the Scene pixel to test. + /// @return The MOID currently at the specified pixel coordinates. MOID GetMOIDPixel(int pixelX, int pixelY) { return GetMOIDPixel(pixelX, pixelY, Activity::NoTeam); } - /// /// Gets this Scene's MOID SpatialPartitionGrid. - /// - /// This Scene's MOID SpatialPartitionGrid. + /// @return This Scene's MOID SpatialPartitionGrid. const SpatialPartitionGrid& GetMOIDGrid() const { return m_MOIDsGrid; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGlobalAcc - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the global acceleration (in m/s^2) that is applied to all movable - // objects' velocities during every frame. Typically models gravity. - // Arguments: None. - // Return value: A Vector describing the global acceleration. - + /// Gets the global acceleration (in m/s^2) that is applied to all movable + /// objects' velocities during every frame. Typically models gravity. + /// @return A Vector describing the global acceleration. Vector GetGlobalAcc() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOzPerKg - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how many Ounces there are in a metric Kilogram - // Arguments: None. - // Return value: A float describing the Oz/Kg ratio. - + /// Gets how many Ounces there are in a metric Kilogram + /// @return A float describing the Oz/Kg ratio. float GetOzPerKg() const { return 35.27396; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetKgPerOz - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets how many metric Kilograms there are in an Ounce. - // Arguments: None. - // Return value: A float describing the Kg/Oz ratio. - + /// Gets how many metric Kilograms there are in an Ounce. + /// @return A float describing the Kg/Oz ratio. float GetKgPerOz() const { return 0.02834952; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetLayerDrawMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the drawing mode of the SceneMan, to easily view what's going on - // in the different internal SceneLayer:s. - // Arguments: The layer mode to draw in, see the LayerDrawMode enumeration for the - // different possible settings. - // Return value: None. - + /// Sets the drawing mode of the SceneMan, to easily view what's going on + /// in the different internal SceneLayer:s. + /// @param mode The layer mode to draw in, see the LayerDrawMode enumeration for the + /// different possible settings. void SetLayerDrawMode(int mode) { m_LayerDrawMode = mode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LockScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Locks all dynamic internal scene bitmaps so that manipulaitons of the - // scene's color and matter representations can take place. - // Doing it in a separate method like this is more efficient because - // many bitmap manipulaitons can be performed between a lock and unlock. - // UnlockScene() should always be called after accesses are completed. - // Arguments: None. - // Return value: None. - + /// Locks all dynamic internal scene bitmaps so that manipulaitons of the + /// scene's color and matter representations can take place. + /// Doing it in a separate method like this is more efficient because + /// many bitmap manipulaitons can be performed between a lock and unlock. + /// UnlockScene() should always be called after accesses are completed. void LockScene(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UnlockScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Unlocks the scene's bitmaps and prevents access to display memory. - // Doing it in a separate method like this is more efficient because - // many bitmap accesses can be performed between a lock and an unlock. - // UnlockScene() should only be called after LockScene(). - // Arguments: None. - // Return value: None. - + /// Unlocks the scene's bitmaps and prevents access to display memory. + /// Doing it in a separate method like this is more efficient because + /// many bitmap accesses can be performed between a lock and an unlock. + /// UnlockScene() should only be called after LockScene(). void UnlockScene(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SceneIsLocked - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Indicates whether the entire scene is currently locked or not. - // Arguments: None. - // Return value: Whether the entire scene is currently locked or not. - + /// Indicates whether the entire scene is currently locked or not. + /// @return Whether the entire scene is currently locked or not. bool SceneIsLocked() const; - /// /// Registers an area to be drawn upon, so it can be tracked and cleared later. - /// - /// The bitmap being drawn upon. - /// The MOID, if we're drawing MOIDs. - /// The left boundary of the draw area. - /// The top boundary of the drawn area. - /// The right boundary of the draw area. - /// The bottom boundary of the draw area. + /// @param bitmap The bitmap being drawn upon. + /// @param moid The MOID, if we're drawing MOIDs. + /// @param left The left boundary of the draw area. + /// @param top The top boundary of the drawn area. + /// @param right The right boundary of the draw area. + /// @param bottom The bottom boundary of the draw area. void RegisterDrawing(const BITMAP* bitmap, int moid, int left, int top, int right, int bottom); - /// /// Registers an area of to be drawn upon, so it can be tracked and cleared later. - /// - /// The bitmap being drawn upon. - /// The MOID, if we're drawing MOIDs. - /// The centre position of the drawn area. - /// The radius of the drawn area. + /// @param bitmap The bitmap being drawn upon. + /// @param moid The MOID, if we're drawing MOIDs. + /// @param center The centre position of the drawn area. + /// @param radius The radius of the drawn area. void RegisterDrawing(const BITMAP* bitmap, int moid, const Vector& center, float radius); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearAllMOIDDrawings - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all registered drawn areas of the MOID layer to the g_NoMOID - // color and clears the registrations too. Should be done each sim update. - // Arguments: None. - // Return value: None. - + /// Clears all registered drawn areas of the MOID layer to the g_NoMOID + /// color and clears the registrations too. Should be done each sim update. void ClearAllMOIDDrawings(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WillPenetrate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Test whether a pixel of the scene would be knocked loose and - // turned into a MO by another particle of a certain material going at a - // certain velocity. Scene needs to be locked to do this! - // Arguments: The X and Y coords of the scene pixel that is collided with. - // The velocity of the incoming particle. - // The mass of the incoming particle. - // Return value: A bool indicating wether the scene pixel would be knocked loose or - // not. If the pixel location specified happens to be of the air - // material (0) false will be returned here. - + /// Test whether a pixel of the scene would be knocked loose and + /// turned into a MO by another particle of a certain material going at a + /// certain velocity. Scene needs to be locked to do this! + /// @param posX The X and Y coords of the scene pixel that is collided with. + /// @param posY The velocity of the incoming particle. + /// @param velocity The mass of the incoming particle. + /// @return A bool indicating wether the scene pixel would be knocked loose or + /// not. If the pixel location specified happens to be of the air + /// material (0) false will be returned here. bool WillPenetrate(const int posX, const int posY, const Vector& velocity, const float mass) { return WillPenetrate(posX, posY, velocity * mass); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WillPenetrate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Test whether a pixel of the scene would be knocked loose and - // turned into a MO by a certian impulse force. Scene needs to be locked - // to do this! - // Arguments: The X and Y coords of the scene pixel that is collided with. - // The impulse force vector, in Kg * m/s. - // Return value: A bool indicating wether the scene pixel would be knocked loose or - // not. If the pixel location specified happens to be of the air - // material (0) false will be returned here. - + /// Test whether a pixel of the scene would be knocked loose and + /// turned into a MO by a certian impulse force. Scene needs to be locked + /// to do this! + /// @param posX The X and Y coords of the scene pixel that is collided with. + /// @param posY The impulse force vector, in Kg * m/s. + /// @return A bool indicating wether the scene pixel would be knocked loose or + /// not. If the pixel location specified happens to be of the air + /// material (0) false will be returned here. bool WillPenetrate(const int posX, const int posY, const Vector& impulse); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TryPenetrate - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculate whether a pixel of the scene would be knocked loose and - // turned into a MO by another particle of a certain material going at a - // certain velocity. If so, the incoming particle will knock loose the - // specified pixel in the scene and momentarily take its place. - // Scene needs to be locked to do this! - // Arguments: The X and Y coord of the scene pixel that is to be collided with. - // The impulse force exerted on the terrain pixel. If this magnitude - // exceeds the strength threshold of the material of the terrain pixel - // hit, the terrain pixel will be knocked loose an turned into an MO. - // The velocity of the the point hitting the terrain here. - // A float reference screen will be set to the factor with screen to - // multiply the collision velocity to get the resulting retardation - // (negative acceleration) that occurs when a penetration happens. - // The normalized probability ratio between 0.0 and 1.0 that determines - // the chance of a penetration to remove a pixel from the scene and - // thus replace it with and air pixel. 1.0 = always, 0.0 = never. - // How many consecutive penetrations in a row immediately before this try. - // The size of the area to look for orphaned terrain elements. - // Max area or orphaned area to remove. - // Orphan area removal trigger rate. - // Return value: A bool indicating wether the scene pixel was knocked loose or not. - // If the pixel location specified happens to be of the air material (0) - // false will be returned here. - + /// Calculate whether a pixel of the scene would be knocked loose and + /// turned into a MO by another particle of a certain material going at a + /// certain velocity. If so, the incoming particle will knock loose the + /// specified pixel in the scene and momentarily take its place. + /// Scene needs to be locked to do this! + /// @param posX The X and Y coord of the scene pixel that is to be collided with. + /// @param posY The impulse force exerted on the terrain pixel. If this magnitude + /// exceeds the strength threshold of the material of the terrain pixel + /// hit, the terrain pixel will be knocked loose an turned into an MO. + /// @param impulse The velocity of the the point hitting the terrain here. + /// @param velocity A float reference screen will be set to the factor with screen to + /// multiply the collision velocity to get the resulting retardation + /// (negative acceleration) that occurs when a penetration happens. + /// @param retardation The normalized probability ratio between 0.0 and 1.0 that determines + /// the chance of a penetration to remove a pixel from the scene and + /// thus replace it with and air pixel. 1.0 = always, 0.0 = never. + /// @param airRatio How many consecutive penetrations in a row immediately before this try. + /// @param numPenetrations The size of the area to look for orphaned terrain elements. (default: 0) + /// @param removeOrphansRadius Max area or orphaned area to remove. (default: 0) + /// @param removeOrphansMaxArea Orphan area removal trigger rate. (default: 0) + /// @return A bool indicating wether the scene pixel was knocked loose or not. + /// If the pixel location specified happens to be of the air material (0) + /// false will be returned here. bool TryPenetrate(int posX, int posY, const Vector& impulse, @@ -598,33 +366,25 @@ namespace RTE { const int removeOrphansMaxArea = 0, const float removeOrphansRate = 0.0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveOrphans - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the area of an orphaned region at specified coordinates. - // Arguments: Coordinates to check for region, whether the orphaned region should be converted into MOPixels and region removed. - // Area of orphaned object calculated during recursve function call to check if we're out of limits - // Size of the are to look for orphaned objects - // Max area of orphaned object to remove - // Whether to actually remove orphaned pixels or not - // Whether to clear internal terrain tracking bitmap or not - // Return value: The area of orphaned region at posX,posY - + /// Returns the area of an orphaned region at specified coordinates. + /// @param posX Coordinates to check for region, whether the orphaned region should be converted into MOPixels and region removed. + /// @param posY Area of orphaned object calculated during recursve function call to check if we're out of limits + /// @param radius Size of the are to look for orphaned objects + /// @param maxArea Max area of orphaned object to remove + /// @param remove Whether to actually remove orphaned pixels or not (default: false) + /// Whether to clear internal terrain tracking bitmap or not + /// @return The area of orphaned region at posX,posY int RemoveOrphans(int posX, int posY, int radius, int maxArea, bool remove = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveOrphans - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the area of an orphaned region at specified coordinates. - // Arguments: Coordinates to check for region, whether the orphaned region should be converted into MOPixels and region removed. - // Coordinates of initial terrain penetration to check, which serves as a center of orphaned object detection. - // Area of orphaned object calculated during recursve function call to check if we're out of limits - // Size of the are to look for orphaned objects - // Max area of orphaned object to remove - // Whether to actually remove orphaned pixels or not - // Whether to clear internal terrain tracking bitmap or not - // Return value: The area of orphaned region at posX,posY - + /// Returns the area of an orphaned region at specified coordinates. + /// @param posX Coordinates to check for region, whether the orphaned region should be converted into MOPixels and region removed. + /// @param posY Coordinates of initial terrain penetration to check, which serves as a center of orphaned object detection. + /// @param centerPosX Area of orphaned object calculated during recursve function call to check if we're out of limits + /// @param centerPosY Size of the are to look for orphaned objects + /// @param accumulatedArea Max area of orphaned object to remove + /// @param radius Whether to actually remove orphaned pixels or not + /// @param maxArea Whether to clear internal terrain tracking bitmap or not + /// @return The area of orphaned region at posX,posY int RemoveOrphans(int posX, int posY, int centerPosX, @@ -634,568 +394,402 @@ namespace RTE { int maxArea, bool remove = false); - /// /// Removes a pixel from the terrain and adds it to MovableMan. - /// - /// The X coordinate of the terrain pixel. - /// The Y coordinate of the terrain pixel. - /// The newly dislodged pixel, if one was found. + /// @param posX The X coordinate of the terrain pixel. + /// @param posX The Y coordinate of the terrain pixel. + /// @return The newly dislodged pixel, if one was found. MovableObject* DislodgePixel(int posX, int posY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: MakeAllUnseen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets one team's view of the scene to be unseen, using a generated map - // of a specific resolution chunkiness. - // Arguments: The dimensions of the pixels that should make up the unseen layer. - // The team we're talking about. - // Return value: None. - + /// Sets one team's view of the scene to be unseen, using a generated map + /// of a specific resolution chunkiness. + /// @param pixelSize The dimensions of the pixels that should make up the unseen layer. + /// @param team The team we're talking about. void MakeAllUnseen(Vector pixelSize, const int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: MakeAllSeen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets one team's view of the scene to be all seen. - // Arguments: The team we're talking about. - // Return value: None. - + /// Sets one team's view of the scene to be all seen. + /// @param team The team we're talking about. void MakeAllSeen(const int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadUnseenLayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads a bitmap from file and use it as the unseen layer for a team. - // Arguments: The path to the bitmap to use as the unseen layer. - // Which team we're talking about. - // Return value: Whether the loading was successful or not. - + /// Loads a bitmap from file and use it as the unseen layer for a team. + /// @param bitmapPath The path to the bitmap to use as the unseen layer. + /// @param team Which team we're talking about. + /// @return Whether the loading was successful or not. bool LoadUnseenLayer(std::string bitmapPath, const int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AnythingUnseen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a team has anything still unseen on the scene. - // Arguments: The team we're talking about. - // Return value: A bool indicating whether that team has anyhting yet unseen. - + /// Tells whether a team has anything still unseen on the scene. + /// @param team The team we're talking about. + /// @return A bool indicating whether that team has anyhting yet unseen. bool AnythingUnseen(const int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetUnseenResolution - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows what the resolution factor of the unseen map to the entire Scene - // is, in both axes. - // Arguments: The team we're talking about. - // Return value: A vector witht he factors in each element representing the factors. - + /// Shows what the resolution factor of the unseen map to the entire Scene + /// is, in both axes. + /// @param team The team we're talking about. + /// @return A vector witht he factors in each element representing the factors. Vector GetUnseenResolution(const int team) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsUnseen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks whether a pixel is in an unseen area on of a specific team. - // Arguments: The X and Y coords of the scene pixel that is to be checked. - // The team we're talking about. - // Return value: A bool indicating whether that point is yet unseen. - + /// Checks whether a pixel is in an unseen area on of a specific team. + /// @param posX The X and Y coords of the scene pixel that is to be checked. + /// @param posY The team we're talking about. + /// @return A bool indicating whether that point is yet unseen. bool IsUnseen(const int posX, const int posY, const int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RevealUnseen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reveals a pixel on the unseen map for a specific team, if there is any. - // Arguments: The X and Y coord of the scene pixel that is to be revealed. - // The team to reveal for. - // Return value: A bool indicating whether there was an unseen pixel revealed there. - + /// Reveals a pixel on the unseen map for a specific team, if there is any. + /// @param posX The X and Y coord of the scene pixel that is to be revealed. + /// @param posY The team to reveal for. + /// @return A bool indicating whether there was an unseen pixel revealed there. bool RevealUnseen(const int posX, const int posY, const int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RestoreUnseen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Hides a pixel on the unseen map for a specific team, if there is any. - // Arguments: The X and Y coord of the scene pixel that is to be revealed. - // The team to hide for. - // Return value: A bool indicating whether there was a seen pixel hidden there. - + /// Hides a pixel on the unseen map for a specific team, if there is any. + /// @param posX The X and Y coord of the scene pixel that is to be revealed. + /// @param posY The team to hide for. + /// @return A bool indicating whether there was a seen pixel hidden there. bool RestoreUnseen(const int posX, const int posY, const int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RevealUnseenBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reveals a box on the unseen map for a specific team, if there is any. - // Arguments: The X and Y coords of the upper left corner of the box to be revealed. - // The width and height of the box to be revealed, in scene units (pixels) - // The team to reveal for. - // Return value: None. - + /// Reveals a box on the unseen map for a specific team, if there is any. + /// @param posX The X and Y coords of the upper left corner of the box to be revealed. + /// @param posY The width and height of the box to be revealed, in scene units (pixels) + /// @param width The team to reveal for. void RevealUnseenBox(const int posX, const int posY, const int width, const int height, const int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RestoreUnseenBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Restores a box on the unseen map for a specific team, if there is any. - // Arguments: The X and Y coords of the upper left corner of the box to be revealed. - // The width and height of the box to be restored, in scene units (pixels) - // The team to restore for. - // Return value: None. - + /// Restores a box on the unseen map for a specific team, if there is any. + /// @param posX The X and Y coords of the upper left corner of the box to be revealed. + /// @param posY The width and height of the box to be restored, in scene units (pixels) + /// @param width The team to restore for. void RestoreUnseenBox(const int posX, const int posY, const int width, const int height, const int team); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastUnseenRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and reveals or hides pixels on the unseen layer of a team - // as long as the accumulated material strengths traced through the terrain - // don't exceed a specific value. - // Arguments: The team to see for. - // The starting position. - // The vector to trace along. - // A Vector that will be set to the position of where the sight ray was - // terminated. If it reached the end, it will be set to the end of the ray. - // The material strength limit where - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Whether the ray should reveal or restore unseen layer - // Return value: Whether any unseen pixels were revealed as a result of this seeing. - + /// Traces along a vector and reveals or hides pixels on the unseen layer of a team + /// as long as the accumulated material strengths traced through the terrain + /// don't exceed a specific value. + /// @param team The team to see for. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param endPos A Vector that will be set to the position of where the sight ray was + /// terminated. If it reached the end, it will be set to the end of the ray. + /// @param strengthLimit The material strength limit where + /// @param skip For every pixel checked along the line, how many to skip between them + /// for optimization reasons. 0 = every pixel is checked. + /// @param reveal Whether the ray should reveal or restore unseen layer + /// @return Whether any unseen pixels were revealed as a result of this seeing. bool CastUnseenRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip, bool reveal); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastSeeRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and reveals pixels on the unseen layer of a team - // as long as the accumulated material strengths traced through the terrain - // don't exceed a specific value. - // Arguments: The team to see for. - // The starting position. - // The vector to trace along. - // A Vector that will be set to the position of where the sight ray was - // terminated. If it reached the end, it will be set to the end of the ray. - // The material strength limit where - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Return value: Whether any unseen pixels were revealed as a result of this seeing. - + /// Traces along a vector and reveals pixels on the unseen layer of a team + /// as long as the accumulated material strengths traced through the terrain + /// don't exceed a specific value. + /// @param team The team to see for. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param endPos A Vector that will be set to the position of where the sight ray was + /// terminated. If it reached the end, it will be set to the end of the ray. + /// @param strengthLimit The material strength limit where + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @return Whether any unseen pixels were revealed as a result of this seeing. bool CastSeeRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastUnseeRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and hides pixels on the unseen layer of a team - // as long as the accumulated material strengths traced through the terrain - // don't exceed a specific value. - // Arguments: The team to see for. - // The starting position. - // The vector to trace along. - // A Vector that will be set to the position of where the sight ray was - // terminated. If it reached the end, it will be set to the end of the ray. - // The material strength limit where - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Return value: Whether any unseen pixels were revealed as a result of this seeing. - + /// Traces along a vector and hides pixels on the unseen layer of a team + /// as long as the accumulated material strengths traced through the terrain + /// don't exceed a specific value. + /// @param team The team to see for. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param endPos A Vector that will be set to the position of where the sight ray was + /// terminated. If it reached the end, it will be set to the end of the ray. + /// @param strengthLimit The material strength limit where + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @return Whether any unseen pixels were revealed as a result of this seeing. bool CastUnseeRay(int team, const Vector& start, const Vector& ray, Vector& endPos, int strengthLimit, int skip = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastMaterialRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and gets the location of the first encountered - // pixel of a specific material in the terrain. - // Arguments: The starting position. - // The vector to trace along. - // The material ID to look for. - // A reference to the vector screen will be filled out with the absolute - // location of the found terrain pixel of the above material. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Whetehr the ray should wrap around the scene if it crosses a seam. - // Return value: Whether the material was found along the ray. If not, the fourth - // parameter will not have been altered (and may still not be 0!) - + /// Traces along a vector and gets the location of the first encountered + /// pixel of a specific material in the terrain. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param material The material ID to look for. + /// @param result A reference to the vector screen will be filled out with the absolute + /// location of the found terrain pixel of the above material. + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @param wrap Whetehr the ray should wrap around the scene if it crosses a seam. (default: true) + /// @return Whether the material was found along the ray. If not, the fourth + /// parameter will not have been altered (and may still not be 0!) bool CastMaterialRay(const Vector& start, const Vector& ray, unsigned char material, Vector& result, int skip = 0, bool wrap = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastMaterialRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and returns how far along that ray there is an - // encounter with a pixel of a specific material in the terrain. - // Arguments: The starting position. - // The vector to trace along. - // The material ID to look for. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Return value: How far along, in pixel units, the ray the material pixel was encountered. - // If no pixel of the right material was found, < 0 is returned. - + /// Traces along a vector and returns how far along that ray there is an + /// encounter with a pixel of a specific material in the terrain. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param material The material ID to look for. + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @return How far along, in pixel units, the ray the material pixel was encountered. + /// If no pixel of the right material was found, < 0 is returned. float CastMaterialRay(const Vector& start, const Vector& ray, unsigned char material, int skip = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastNotMaterialRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and gets the location of the first encountered - // pixel that is NOT of a specific material in the scene's terrain. - // Arguments: The starting position. - // The vector to trace along. - // The material ID to find something OTHER than. - // A reference to the vector screen will be filled out with the absolute - // location of the found terrain pixel of the above material. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Whether to check for MO layer collisions as well, not just terrain. - // Return value: Whether the a pixel other than the material was found along the ray. - // If not, the fourth parameter will not have been altered (and may still not be 0!) - + /// Traces along a vector and gets the location of the first encountered + /// pixel that is NOT of a specific material in the scene's terrain. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param material The material ID to find something OTHER than. + /// @param result A reference to the vector screen will be filled out with the absolute + /// location of the found terrain pixel of the above material. + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @param checkMOs Whether to check for MO layer collisions as well, not just terrain. (default: false) + /// @return Whether the a pixel other than the material was found along the ray. + /// If not, the fourth parameter will not have been altered (and may still not be 0!) bool CastNotMaterialRay(const Vector& start, const Vector& ray, unsigned char material, Vector& result, int skip = 0, bool checkMOs = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastNotMaterialRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and returns how far along that ray there is an - // encounter with a pixel of OTHER than a specific material in the terrain. - // Arguments: The starting position. - // The vector to trace along. - // The material ID to find something OTHER than. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Whether to check for MO layer collisions as well, not just terrain. - // Return value: How far along, in pixel units, the ray the pixel of any other material - // was encountered. If no pixel of the right material was found, < 0 is returned. - + /// Traces along a vector and returns how far along that ray there is an + /// encounter with a pixel of OTHER than a specific material in the terrain. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param material The material ID to find something OTHER than. + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @param checkMOs Whether to check for MO layer collisions as well, not just terrain. (default: false) + /// @return How far along, in pixel units, the ray the pixel of any other material + /// was encountered. If no pixel of the right material was found, < 0 is returned. float CastNotMaterialRay(const Vector& start, const Vector& ray, unsigned char material, int skip = 0, bool checkMOs = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastStrengthSumRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and returns how the sum of all encountered pixels' - // material strength values. This will take wrapping into account. - // Arguments: The starting position. - // The ending position. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // A material ID to ignore, IN ADDITION to Air. - // Return value: The sum of all encountered pixels' material strength vales. So if it was - // all Air, then 0 is returned (Air's strength value is 0). - + /// Traces along a vector and returns how the sum of all encountered pixels' + /// material strength values. This will take wrapping into account. + /// @param start The starting position. + /// @param end The ending position. + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @param ignoreMaterial A material ID to ignore, IN ADDITION to Air. (default: g_MaterialAir) + /// @return The sum of all encountered pixels' material strength vales. So if it was + /// all Air, then 0 is returned (Air's strength value is 0). float CastStrengthSumRay(const Vector& start, const Vector& end, int skip = 0, unsigned char ignoreMaterial = g_MaterialAir); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastMaxStrengthRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and returns the strongest of all encountered pixels' - // material strength values. - // This will take wrapping into account. - // Arguments: The starting position. - // The ending position. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // A material ID to ignore, IN ADDITION to Air. This defaults to doors, for legacy script purposes - // Return value: The max of all encountered pixels' material strength vales. So if it was - // all Air, then 0 is returned (Air's strength value is 0). - + /// Traces along a vector and returns the strongest of all encountered pixels' + /// material strength values. + /// This will take wrapping into account. + /// @param start The starting position. + /// @param end The ending position. + /// @param skip For every pixel checked along the line, how many to skip between them + /// for optimization reasons. 0 = every pixel is checked. + /// @param ignoreMaterial A material ID to ignore, IN ADDITION to Air. This defaults to doors, for legacy script purposes + /// @return The max of all encountered pixels' material strength vales. So if it was + /// all Air, then 0 is returned (Air's strength value is 0). // We use two accessors instead of default parameters, for lua compat float CastMaxStrengthRay(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial); float CastMaxStrengthRay(const Vector& start, const Vector& end, int skip) { return CastMaxStrengthRay(start, end, skip, g_MaterialDoor); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastMaxStrengthRayMaterial - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and returns the strongest of all encountered pixels' materials - // This will take wrapping into account. - // Arguments: The starting position. - // The ending position. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // A material ID to ignore, IN ADDITION to Air. - // Return value: The strongest material encountered + /// Traces along a vector and returns the strongest of all encountered pixels' materials + /// This will take wrapping into account. + /// @param start The starting position. + /// @param end The ending position. + /// @param skip For every pixel checked along the line, how many to skip between them + /// for optimization reasons. 0 = every pixel is checked. + /// @param ignoreMaterial A material ID to ignore, IN ADDITION to Air. + /// @return The strongest material encountered const Material* CastMaxStrengthRayMaterial(const Vector& start, const Vector& end, int skip, unsigned char ignoreMaterial); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastStrengthRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and shows where along that ray there is an - // encounter with a pixel of a material with strength more than or equal - // to a specific value. - // Arguments: The starting position. - // The vector to trace along. - // The strength value of screen any found to be equal or more than will - // terminate the ray. - // A reference to the vector screen will be filled out with the absolute - // location of the found terrain pixel of less than or equal to above strength. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // A material ID to ignore, IN ADDITION to Air. - // Whetehr the ray should wrap around the scene if it crosses a seam. - // Return value: Whether a material of equal or more strength was found along the ray. - // If not, the fourth parameter have been set to last position of the ray. - + /// Traces along a vector and shows where along that ray there is an + /// encounter with a pixel of a material with strength more than or equal + /// to a specific value. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param strength The strength value of screen any found to be equal or more than will + /// terminate the ray. + /// @param result A reference to the vector screen will be filled out with the absolute + /// location of the found terrain pixel of less than or equal to above strength. + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @param ignoreMaterial A material ID to ignore, IN ADDITION to Air. (default: g_MaterialAir) + /// @param wrap Whetehr the ray should wrap around the scene if it crosses a seam. (default: true) + /// @return Whether a material of equal or more strength was found along the ray. + /// If not, the fourth parameter have been set to last position of the ray. bool CastStrengthRay(const Vector& start, const Vector& ray, float strength, Vector& result, int skip = 0, unsigned char ignoreMaterial = g_MaterialAir, bool wrap = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastWeaknessRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and shows where along that ray there is an - // encounter with a pixel of a material with strength less than or equal - // to a specific value. - // Arguments: The starting position. - // The vector to trace along. - // The strength value of screen any found to be equal or less than will - // terminate the ray. - // A reference to the vector screen will be filled out with the absolute - // location of the found terrain pixel of less than or equal to above strength. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Whetehr the ray should wrap around the scene if it crosses a seam. - // Return value: Whether a material of equal or less strength was found along the ray. - // If not, the fourth parameter have been set to last position of the ray. - + /// Traces along a vector and shows where along that ray there is an + /// encounter with a pixel of a material with strength less than or equal + /// to a specific value. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param strength The strength value of screen any found to be equal or less than will + /// terminate the ray. + /// @param result A reference to the vector screen will be filled out with the absolute + /// location of the found terrain pixel of less than or equal to above strength. + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @param wrap Whetehr the ray should wrap around the scene if it crosses a seam. (default: true) + /// @return Whether a material of equal or less strength was found along the ray. + /// If not, the fourth parameter have been set to last position of the ray. bool CastWeaknessRay(const Vector& start, const Vector& ray, float strength, Vector& result, int skip = 0, bool wrap = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastMORay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and returns MOID of the first non-ignored - // non-NoMOID MO encountered. If a non-air terrain pixel is encountered - // first, g_NoMOID will be returned. - // Arguments: The starting position. - // The vector to trace along. - // An MOID to ignore. Any child MO's of this MOID will also be ignored. - // To enable ignoring of all MOIDs associated with an object of a specific - // team which also has team ignoring enabled itself. - // A specific material ID to ignore hits with. - // Whether to ignore all terrain hits or not. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Return value: The MOID of the hit non-ignored MO, or g_NoMOID if terrain or no MO was hit. - + /// Traces along a vector and returns MOID of the first non-ignored + /// non-NoMOID MO encountered. If a non-air terrain pixel is encountered + /// first, g_NoMOID will be returned. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param ignoreMOID An MOID to ignore. Any child MO's of this MOID will also be ignored. (default: g_NoMOID) + /// @param ignoreTeam To enable ignoring of all MOIDs associated with an object of a specific (default: Activity::NoTeam) + /// team which also has team ignoring enabled itself. + /// @param ignoreMaterial A specific material ID to ignore hits with. (default: 0) + /// @param ignoreAllTerrain Whether to ignore all terrain hits or not. (default: false) + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @return The MOID of the hit non-ignored MO, or g_NoMOID if terrain or no MO was hit. MOID CastMORay(const Vector& start, const Vector& ray, MOID ignoreMOID = g_NoMOID, int ignoreTeam = Activity::NoTeam, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false, int skip = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastFindMORay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and shows where a specific MOID has been found. - // Arguments: The starting position. - // The vector to trace along. - // An MOID to find. Any child MO's of this MOID will also be found. ------------ ??? - // A reference to the vector screen will be filled out with the absolute - // location of the found MO pixel of the above MOID. - // A specific material ID to ignore hits with. - // Whether to ignore all terrain hits or not. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Return value: Whether the target MOID was found along the ray or not. - + /// Traces along a vector and shows where a specific MOID has been found. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param targetMOID An MOID to find. Any child MO's of this MOID will also be found. ------------ ??? + /// @param resultPos A reference to the vector screen will be filled out with the absolute + /// location of the found MO pixel of the above MOID. + /// @param ignoreMaterial A specific material ID to ignore hits with. (default: 0) + /// @param ignoreAllTerrain Whether to ignore all terrain hits or not. (default: false) + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @return Whether the target MOID was found along the ray or not. bool CastFindMORay(const Vector& start, const Vector& ray, MOID targetMOID, Vector& resultPos, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false, int skip = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CastObstacleRay - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Traces along a vector and returns the length of how far the trace went - // without hitting any non-ignored terrain material or MOID at all. - // Arguments: The starting position. - // The vector to trace along. - // A reference to the vector screen will be filled out with the absolute - // location of the first obstacle, or the end of the ray if none was hit. - // A reference to the vector screen will be filled out with the absolute - // location of the last free position before hitting an obstacle, or the - // end of the ray if none was hit. This is only altered if thre are any - // free pixels encountered. - // An MOID to ignore. Any child MO's of this MOID will also be ignored. - // To enable ignoring of all MOIDs associated with an object of a specific - // team which also has team ignoring enabled itself. - // A specific material ID to ignore hits with. - // For every pixel checked along the line, how many to skip between them - // for optimization reasons. 0 = every pixel is checked. - // Return value: How far along, in pixel units, the ray the pixel of any obstacle was - // encountered. If no pixel of the right material was found, < 0 is returned. - // If an obstacle on the starting position was encountered, 0 is returned. - + /// Traces along a vector and returns the length of how far the trace went + /// without hitting any non-ignored terrain material or MOID at all. + /// @param start The starting position. + /// @param ray The vector to trace along. + /// @param obstaclePos A reference to the vector screen will be filled out with the absolute + /// location of the first obstacle, or the end of the ray if none was hit. + /// @param freePos A reference to the vector screen will be filled out with the absolute + /// location of the last free position before hitting an obstacle, or the + /// end of the ray if none was hit. This is only altered if thre are any + /// free pixels encountered. + /// @param ignoreMOID An MOID to ignore. Any child MO's of this MOID will also be ignored. (default: g_NoMOID) + /// @param ignoreTeam To enable ignoring of all MOIDs associated with an object of a specific (default: Activity::NoTeam) + /// team which also has team ignoring enabled itself. + /// @param ignoreMaterial A specific material ID to ignore hits with. (default: 0) + /// @param skip For every pixel checked along the line, how many to skip between them (default: 0) + /// for optimization reasons. 0 = every pixel is checked. + /// @return How far along, in pixel units, the ray the pixel of any obstacle was + /// encountered. If no pixel of the right material was found, < 0 is returned. + /// If an obstacle on the starting position was encountered, 0 is returned. float CastObstacleRay(const Vector& start, const Vector& ray, Vector& obstaclePos, Vector& freePos, MOID ignoreMOID = g_NoMOID, int ignoreTeam = Activity::NoTeam, unsigned char ignoreMaterial = 0, int skip = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLastRayHitPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the abosulte pos of where the last cast ray hit somehting. - // Arguments: None. - // Return value: A vector with the absolute pos of where the last ray cast hit somehting. - + /// Gets the abosulte pos of where the last cast ray hit somehting. + /// @return A vector with the absolute pos of where the last ray cast hit somehting. const Vector& GetLastRayHitPos(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FindAltitude - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the altitide of a certain point above the terrain, measured - // in pixels. - // Arguments: The max altitude you care to check for. 0 Means check the whole scene's height. - // The accuracy within screen measurement is acceptable. Higher number - // here means less calculation. - // Return value: The altitude over the terrain, in pixels. - + /// Calculates the altitide of a certain point above the terrain, measured + /// in pixels. + /// @param from The max altitude you care to check for. 0 Means check the whole scene's height. + /// @param max The accuracy within screen measurement is acceptable. Higher number + /// here means less calculation. + /// @return The altitude over the terrain, in pixels. float FindAltitude(const Vector& from, int max, int accuracy, bool fromSceneOrbitDirection); float FindAltitude(const Vector& from, int max, int accuracy) { return FindAltitude(from, max, accuracy, false); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: OverAltitude - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the altitide of a certain point above the terrain, measured - // in pixels, and then tells if that point is over a certain value. - // Arguments: The altitude threshold you want to check for. - // The accuracy within screen measurement is acceptable. Higher number - // here means less costly. - // Return value: Whether the point is over the threshold altitude or not. - + /// Calculates the altitide of a certain point above the terrain, measured + /// in pixels, and then tells if that point is over a certain value. + /// @param point The altitude threshold you want to check for. + /// @param threshold The accuracy within screen measurement is acceptable. Higher number + /// here means less costly. + /// @return Whether the point is over the threshold altitude or not. bool OverAltitude(const Vector& point, int threshold, int accuracy = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: MovePointToGround - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes an arbitrary point in the air and calculates it to be straight - // down at a certain maximum distance from the ground. - // Arguments: The point to start from. Should be in the air, or the same point will - // be returned (null operation) - // The max altitude in px you want the point to be above the ground. - // The accuracy within screen measurement is acceptable. Higher number - // here means less calculation. - // Return value: The new point screen is no higher than accuracy + max altitude over - // the terrain. - + /// Takes an arbitrary point in the air and calculates it to be straight + /// down at a certain maximum distance from the ground. + /// @param from The point to start from. Should be in the air, or the same point will + /// be returned (null operation) + /// @param maxAltitude The max altitude in px you want the point to be above the ground. (default: 0) + /// @param accuracy The accuracy within screen measurement is acceptable. Higher number (default: 0) + /// here means less calculation. + /// @return The new point screen is no higher than accuracy + max altitude over + /// the terrain. Vector MovePointToGround(const Vector& from, int maxAltitude = 0, int accuracy = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsWithinBounds - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether the integer coordinates passed in are within the - // bounds of the current Scene, considering its wrapping. - // Arguments: Int coordinates. - // A margin - // Return value: Whether within bounds or not, considering wrapping. - + /// Returns whether the integer coordinates passed in are within the + /// bounds of the current Scene, considering its wrapping. + /// @param pixelX Int coordinates. + /// @param pixelY A margin + /// @return Whether within bounds or not, considering wrapping. bool IsWithinBounds(const int pixelX, const int pixelY, const int margin = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ForceBounds - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Wraps or bounds a position coordinate if it is off bounds of the - // Scene, depending on the wrap settings of this Scene. - // Arguments: The X and Y coordinates of the position to wrap, if needed. - // Return value: Whether wrapping was performed or not. (Does not report on bounding) - + /// Wraps or bounds a position coordinate if it is off bounds of the + /// Scene, depending on the wrap settings of this Scene. + /// @param posX The X and Y coordinates of the position to wrap, if needed. + /// @return Whether wrapping was performed or not. (Does not report on bounding) bool ForceBounds(int& posX, int& posY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ForceBounds - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Wraps or bounds a position coordinate if it is off bounds of the - // Scene, depending on the wrap settings of this Scene. - // Arguments: The vector coordinates of the position to wrap, if needed. - // Return value: Whether wrapping was performed or not. (Does not report on bounding) - + /// Wraps or bounds a position coordinate if it is off bounds of the + /// Scene, depending on the wrap settings of this Scene. + /// @param pos The vector coordinates of the position to wrap, if needed. + /// @return Whether wrapping was performed or not. (Does not report on bounding) bool ForceBounds(Vector& pos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WrapPosition - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Only wraps a position coordinate if it is off bounds of the Scene - // and wrapping in the corresponding axes are turned on. - // Arguments: The X and Y coordinates of the position to wrap, if needed. - // Return value: Whether wrapping was performed or not. - + /// Only wraps a position coordinate if it is off bounds of the Scene + /// and wrapping in the corresponding axes are turned on. + /// @param posX The X and Y coordinates of the position to wrap, if needed. + /// @return Whether wrapping was performed or not. bool WrapPosition(int& posX, int& posY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WrapPosition - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Only wraps a position coordinate if it is off bounds of the Scene - // and wrapping in the corresponding axes are turned on. - // Arguments: The vector coordinates of the position to wrap, if needed. - // Return value: Whether wrapping was performed or not. - + /// Only wraps a position coordinate if it is off bounds of the Scene + /// and wrapping in the corresponding axes are turned on. + /// @param pos The vector coordinates of the position to wrap, if needed. + /// @return Whether wrapping was performed or not. bool WrapPosition(Vector& pos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SnapPosition - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a position snapped to the current scene grid. - // Arguments: The vector coordinates of the position to snap. - // Whether to actually snap or not. This is useful for cleaner toggle code. - // Return value: The new snapped position. - + /// Returns a position snapped to the current scene grid. + /// @param pos The vector coordinates of the position to snap. + /// @param snap Whether to actually snap or not. This is useful for cleaner toggle code. (default: true) + /// @return The new snapped position. Vector SnapPosition(const Vector& pos, bool snap = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ShortestDistance - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the shortest distance between two points in scene - // coordinates, taking into account all wrapping and out of bounds of the - // two points. - // Arguments: The two Vector coordinates of the two positions to find the shortest - // distance between. - // Whether to check if the passed in points are outside the scene, and to - // wrap them if they are. - // Return value: The resulting vector screen shows the shortest distance, spanning over - // wrapping borders etc. Basically the ideal pos2 - pos1. - + /// Calculates the shortest distance between two points in scene + /// coordinates, taking into account all wrapping and out of bounds of the + /// two points. + /// @param pos1 The two Vector coordinates of the two positions to find the shortest + /// distance between. + /// @param pos2 Whether to check if the passed in points are outside the scene, and to + /// wrap them if they are. + /// @return The resulting vector screen shows the shortest distance, spanning over + /// wrapping borders etc. Basically the ideal pos2 - pos1. Vector ShortestDistance(Vector pos1, Vector pos2, bool checkBounds = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ShortestDistanceX - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the shortest distance between two x values in scene - // coordinates, taking into account all wrapping and out of bounds of the - // two values. - // Arguments: The X coordinates of the two values to find the shortest distance between. - // Whether to check if the passed in points are outside the scene, and to - // wrap them if they are. - // Whether to constrain the distance to only be in a certain direction: - // 0 means no constraint, < 0 means only look in the negative dir, etc. - // If the scene doesn't wrap in the constraint's direction, the constraint - // will be ignored. - // Return value: The resulting X value screen shows the shortest distance, spanning over - // wrapping borders etc. Basically the ideal val2 - val1. - + /// Calculates the shortest distance between two x values in scene + /// coordinates, taking into account all wrapping and out of bounds of the + /// two values. + /// @param val1 The X coordinates of the two values to find the shortest distance between. + /// @param val2 Whether to check if the passed in points are outside the scene, and to + /// wrap them if they are. + /// @param checkBounds Whether to constrain the distance to only be in a certain direction: (default: false) + /// 0 means no constraint, < 0 means only look in the negative dir, etc. + /// @param direction If the scene doesn't wrap in the constraint's direction, the constraint (default: 0) + /// will be ignored. + /// @return The resulting X value screen shows the shortest distance, spanning over + /// wrapping borders etc. Basically the ideal val2 - val1. float ShortestDistanceX(float val1, float val2, bool checkBounds = false, int direction = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ShortestDistanceY - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Calculates the shortest distance between two Y values in scene - // coordinates, taking into account all wrapping and out of bounds of the - // two values. - // Arguments: The Y coordinates of the two values to find the shortest distance between. - // Whether to check if the passed in points are outside the scene, and to - // wrap them if they are. - // Whether to constrain the distance to only be in a certain direction: - // 0 means no constraint, < 0 means only look in the negative dir, etc. - // If the scene doesn't wrap in the constraint's direction, the constraint - // will be ignored. - // Return value: The resulting Y value screen shows the shortest distance, spanning over - // wrapping borders etc. Basically the ideal val2 - val1. - + /// Calculates the shortest distance between two Y values in scene + /// coordinates, taking into account all wrapping and out of bounds of the + /// two values. + /// @param val1 The Y coordinates of the two values to find the shortest distance between. + /// @param val2 Whether to check if the passed in points are outside the scene, and to + /// wrap them if they are. + /// @param checkBounds Whether to constrain the distance to only be in a certain direction: (default: false) + /// 0 means no constraint, < 0 means only look in the negative dir, etc. + /// @param direction If the scene doesn't wrap in the constraint's direction, the constraint (default: 0) + /// will be ignored. + /// @return The resulting Y value screen shows the shortest distance, spanning over + /// wrapping borders etc. Basically the ideal val2 - val1. float ShortestDistanceY(float val1, float val2, bool checkBounds = false, int direction = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ObscuredPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a point on the scene is obscured by MOID or Terrain - // non-air material. - // Arguments: The point on the scene to check. - // Wheter to also check for unseen areas of a specific team. -1 means - // don't check this. - // Return value: Whether that point is obscured/obstructed or not. - + /// Tells whether a point on the scene is obscured by MOID or Terrain + /// non-air material. + /// @param point The point on the scene to check. + /// @param team Wheter to also check for unseen areas of a specific team. -1 means (default: -1) { return ObscuredPoint(point.GetFloorIntX()) + /// don't check this. + /// @return Whether that point is obscured/obstructed or not. bool ObscuredPoint(Vector& point, int team = -1) { return ObscuredPoint(point.GetFloorIntX(), point.GetFloorIntY()); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ObscuredPoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a point on the scene is obscured by MOID or Terrain - // non-air material. - // Arguments: The point on the scene to check. - // Wheter to also check for unseen areas of a specific team. -1 means - // don't check this. - // Return value: Whether that point is obscured/obstructed or not. - + /// Tells whether a point on the scene is obscured by MOID or Terrain + /// non-air material. + /// @param x The point on the scene to check. + /// @param y Wheter to also check for unseen areas of a specific team. -1 means + /// don't check this. + /// @return Whether that point is obscured/obstructed or not. bool ObscuredPoint(int x, int y, int team = -1); /* @@ -1210,137 +804,84 @@ namespace RTE { bool SceneRectsIntersect(int x, int y); */ - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WrapRect - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes a rect and adds all possible wrapped appearances of that rect - // to a passed-in list. IF if a passed in rect straddles the seam of a - // wrapped scene axis, it will be added twice to the output list. If it - // doesn't straddle any seam, it will be only added once. - // Arguments: The IntRect to check for wrapping of and add to the output list below. - // A reference to a list of IntRect:s that will only be added to, never - // cleared. - // Return value: How many additions of the passed in rect was added to the list. 1 if - // no wrapping need was detected, up to 4 possible (if straddling both seams) - + /// Takes a rect and adds all possible wrapped appearances of that rect + /// to a passed-in list. IF if a passed in rect straddles the seam of a + /// wrapped scene axis, it will be added twice to the output list. If it + /// doesn't straddle any seam, it will be only added once. + /// @param wrapRect The IntRect to check for wrapping of and add to the output list below. + /// @param outputList A reference to a list of IntRect:s that will only be added to, never + /// cleared. + /// @return How many additions of the passed in rect was added to the list. 1 if + /// no wrapping need was detected, up to 4 possible (if straddling both seams) int WrapRect(const IntRect& wrapRect, std::list& outputList); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: WrapBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes a Box and adds all possible scenewrapped appearances of that Box - // to a passed-in list. IF if a passed in rect straddles the seam of a - // wrapped scene axis, it will be added twice to the output list. If it - // doesn't straddle any seam, it will be only added once. - // Arguments: The IntRect to check for wrapping of and add to the output list below. - // A reference to a list of IntRect:s that will only be added to, never - // cleared. - // Return value: How many additions of the passed in Box was added to the list. 1 if - // no wrapping need was detected, up to 4 possible (if straddling both seams) - + /// Takes a Box and adds all possible scenewrapped appearances of that Box + /// to a passed-in list. IF if a passed in rect straddles the seam of a + /// wrapped scene axis, it will be added twice to the output list. If it + /// doesn't straddle any seam, it will be only added once. + /// @param wrapBox The IntRect to check for wrapping of and add to the output list below. + /// @param outputList A reference to a list of IntRect:s that will only be added to, never + /// cleared. + /// @return How many additions of the passed in Box was added to the list. 1 if + /// no wrapping need was detected, up to 4 possible (if straddling both seams) int WrapBox(const Box& wrapBox, std::list& outputList); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddSceneObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Takes any scene object and adds it to the scene in the appropriate way. - // If it's a TerrainObject, then it gets applied to the terrain, if it's - // an MO, it gets added to the correct type group in MovableMan. - // Arguments: The SceneObject to add. Ownership IS transferred! - // Return value: Whether the SceneObject was successfully added or not. Either way, - // ownership was transferred. If no success, the object was deleted. - + /// Takes any scene object and adds it to the scene in the appropriate way. + /// If it's a TerrainObject, then it gets applied to the terrain, if it's + /// an MO, it gets added to the correct type group in MovableMan. + /// @param pObject The SceneObject to add. Ownership IS transferred! + /// @return Whether the SceneObject was successfully added or not. Either way, + /// ownership was transferred. If no success, the object was deleted. bool AddSceneObject(SceneObject* pObject); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this SceneMan. Supposed to be done every frame - // before drawing. - // Arguments: Which screen to update for. - // Return value: None. - + /// Updates the state of this SceneMan. Supposed to be done every frame + /// before drawing. + /// @param screenId Which screen to update for. (default: 0) void Update(int screenId = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this SceneMan's current graphical representation to a - // BITMAP of choice. - // Arguments: A pointer to a BITMAP to draw on, appropriately sized for the split - // screen segment. - // The offset into the scene where the target bitmap's upper left corner - // is located. - // Return value: None. - + /// Draws this SceneMan's current graphical representation to a + /// BITMAP of choice. + /// @param targetBitmap A pointer to a BITMAP to draw on, appropriately sized for the split + /// screen segment. + /// @param targetGUIBitmap The offset into the scene where the target bitmap's upper left corner + /// is located. void Draw(BITMAP* targetBitmap, BITMAP* targetGUIBitmap, const Vector& targetPos = Vector(), bool skipBackgroundLayers = false, bool skipTerrain = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearMOColorLayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the color MO layer. Should be done every frame. - // Arguments: None. - // Return value: None. - + /// Clears the color MO layer. Should be done every frame. void ClearMOColorLayer(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearSeenPixels - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the list of pixels on the unseen map that have been revealed. - // Arguments: None. - // Return value: None. - + /// Clears the list of pixels on the unseen map that have been revealed. void ClearSeenPixels(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddMaterialCopy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Creates a copy of passed material and stores it into internal vector - // to make sure there's only one material owner - // Arguments: Material to add. - // Return value: Pointer to stored material. - + /// Creates a copy of passed material and stores it into internal vector + /// to make sure there's only one material owner + /// @param mat Material to add. + /// @return Pointer to stored material. Material* AddMaterialCopy(Material* mat); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RegisterTerrainChange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Registers terrain change event for the network server to be then sent to clients. - // Arguments: x,y - scene coordinates of change, w,h - size of the changed region, - // color - changed color for one-pixel events, - // back - if true, then background bitmap was changed if false then foreground. - // Return value: None. - + /// Registers terrain change event for the network server to be then sent to clients. + /// @param x,y - scene coordinates of change, w,h - size of the changed region, + /// color - changed color for one-pixel events, + /// back - if true, then background bitmap was changed if false then foreground. void RegisterTerrainChange(int x, int y, int w, int h, unsigned char color, bool back); - /// /// Gets an intermediate bitmap that is used for drawing a settled MovableObject into the terrain. - /// - /// The diameter of the MovableObject to calculate the required bitmap size. - /// Pointer to the temp BITMAP of the appropriate size. Ownership is NOT transferred! + /// @param moDiameter The diameter of the MovableObject to calculate the required bitmap size. + /// @return Pointer to the temp BITMAP of the appropriate size. Ownership is NOT transferred! BITMAP* GetIntermediateBitmapForSettlingIntoTerrain(int moDiameter) const; - /// /// Sets the current scene pointer to null - /// void ClearCurrentScene(); - /// /// Gets the maximum height of a column of scrap terrain to collapse, when the bottom pixel is knocked loose. - /// - /// The compacting height of scrap terrain. + /// @return The compacting height of scrap terrain. int GetScrapCompactingHeight() const { return m_ScrapCompactingHeight; } - /// /// Sets the maximum height of a column of scrap terrain to collapse, when the bottom pixel is knocked loose. - /// - /// The new compacting height, in pixels. + /// @param newHeight The new compacting height, in pixels. void SetScrapCompactingHeight(int newHeight) { m_ScrapCompactingHeight = newHeight; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: static std::vector> m_IntermediateSettlingBitmaps; //!< Intermediate bitmaps of different sizes that are used to draw settled MovableObjects into the terrain. @@ -1402,20 +943,12 @@ namespace RTE { int m_ScrapCompactingHeight; //!< The maximum height of a column of scrap terrain to collapse, when the bottom pixel is knocked loose. - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this SceneMan, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this SceneMan, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/SettingsMan.cpp b/Source/Managers/SettingsMan.cpp index f356bfec5f..b7c7956f28 100644 --- a/Source/Managers/SettingsMan.cpp +++ b/Source/Managers/SettingsMan.cpp @@ -15,8 +15,6 @@ namespace RTE { const std::string SettingsMan::c_ClassName = "SettingsMan"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsMan::Clear() { m_SettingsPath = System::GetUserdataDirectory() + "Settings.ini"; m_SettingsNeedOverwrite = false; @@ -72,8 +70,6 @@ namespace RTE { m_EnabledGlobalScripts.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SettingsMan::Initialize() { if (const char* settingsTempPath = std::getenv("CCCP_SETTINGSPATH")) { m_SettingsPath = std::string(settingsTempPath); @@ -105,15 +101,11 @@ namespace RTE { return failureCode; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsMan::UpdateSettingsFile() const { Writer settingsWriter(m_SettingsPath); g_SettingsMan.Save(settingsWriter); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SettingsMan::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -235,8 +227,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SettingsMan::Save(Writer& writer) const { Serializable::Save(writer); diff --git a/Source/Managers/SettingsMan.h b/Source/Managers/SettingsMan.h index 240b3ca679..f431c9d8d0 100644 --- a/Source/Managers/SettingsMan.h +++ b/Source/Managers/SettingsMan.h @@ -8,9 +8,7 @@ namespace RTE { - /// /// The singleton manager over the application and misc settings. - /// class SettingsMan : public Singleton, public Serializable { public: @@ -18,510 +16,346 @@ namespace RTE { SerializableOverrideMethods; #pragma region Creation - /// /// Constructor method used to instantiate a SettingsMan object in system memory. Initialize() should be called before using the object. - /// SettingsMan() { Clear(); } - /// /// Makes the SettingsMan object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Initialize(); #pragma endregion #pragma region Destruction - /// /// Resets the entire SettingsMan, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); } #pragma endregion #pragma region Settings Manager Operations - /// /// Gets whether Settings.ini needs to be overwritten with the complete list of settings or not. Will be true only if Settings.ini was created with default values on first load or after settings delete. - /// - /// Whether Settings.ini needs to be overwritten with the complete list of settings or not. + /// @return Whether Settings.ini needs to be overwritten with the complete list of settings or not. bool SettingsNeedOverwrite() const { return m_SettingsNeedOverwrite; } - /// /// Sets Settings.ini to be overwritten during the boot sequence for overrides to be applied (e.g. resolution validation). - /// void SetSettingsNeedOverwrite() { m_SettingsNeedOverwrite = true; } - /// /// Overwrites the settings file to save changes made from within the game. - /// void UpdateSettingsFile() const; #pragma endregion #pragma region Engine Settings - /// /// Returns whether LuaJit is disabled or not. - /// - /// Whether LuaJIT is disabled or not. + /// @return Whether LuaJIT is disabled or not. bool DisableLuaJIT() const { return m_DisableLuaJIT; } - /// /// Returns the recommended MOID count. If this amount is exceeded then some units may be removed at the start of the activity. - /// - /// Recommended MOID count. + /// @return Recommended MOID count. int RecommendedMOIDCount() const { return m_RecommendedMOIDCount; } - /// /// Gets whether simplified collision detection (reduced MOID layer sampling) is enabled. - /// - /// Whether simplified collision detection is enabled or not. + /// @return Whether simplified collision detection is enabled or not. bool SimplifiedCollisionDetection() const { return m_SimplifiedCollisionDetection; } - /// /// Gets the Scene background layer auto-scaling mode. - /// - /// The Scene background layer auto-scaling mode. 0 for off, 1 for fit screen dimensions and 2 for always upscaled to x2. + /// @return The Scene background layer auto-scaling mode. 0 for off, 1 for fit screen dimensions and 2 for always upscaled to x2. int GetSceneBackgroundAutoScaleMode() const { return m_SceneBackgroundAutoScaleMode; } - /// /// Sets the Scene background layer auto-scaling mode. - /// - /// The new Scene background layer auto-scaling mode. 0 for off, 1 for fit screen dimensions and 2 for always upscaled to x2. + /// @param newMode The new Scene background layer auto-scaling mode. 0 for off, 1 for fit screen dimensions and 2 for always upscaled to x2. void SetSceneBackgroundAutoScaleMode(int newMode) { m_SceneBackgroundAutoScaleMode = std::clamp(newMode, 0, 2); } - /// /// Gets whether faction BuyMenu theme support is disabled. - /// - /// Whether faction BuyMenu theme support is disabled. + /// @return Whether faction BuyMenu theme support is disabled. bool FactionBuyMenuThemesDisabled() const { return m_DisableFactionBuyMenuThemes; } - /// /// Sets whether faction BuyMenu theme support is disabled. - /// - /// Whether faction BuyMenu theme support is disabled or not. + /// @param disable Whether faction BuyMenu theme support is disabled or not. void SetFactionBuyMenuThemesDisabled(bool disable) { m_DisableFactionBuyMenuThemes = disable; } - /// /// Gets whether custom cursor support in faction BuyMenu themes is disabled. - /// - /// Whether faction BuyMenu theme support is disabled. + /// @return Whether faction BuyMenu theme support is disabled. bool FactionBuyMenuThemeCursorsDisabled() const { return m_DisableFactionBuyMenuThemeCursors; } - /// /// Sets whether custom cursor support in faction BuyMenu themes is disabled. - /// - /// Whether custom cursor support in faction BuyMenu themes is disabled or not. + /// @param disable Whether custom cursor support in faction BuyMenu themes is disabled or not. void SetFactionBuyMenuThemeCursorsDisabled(bool disable) { m_DisableFactionBuyMenuThemeCursors = disable; } - /// /// Gets the PathFinder grid node size. - /// - /// The PathFinder grid node size. + /// @return The PathFinder grid node size. int GetPathFinderGridNodeSize() const { return m_PathFinderGridNodeSize; } - /// /// Returns whether or not any experimental settings are used. - /// - /// Whether or not any experimental settings are used. + /// @return Whether or not any experimental settings are used. bool GetAnyExperimentalSettingsEnabled() const { return false; } - /// /// Gets the AI update interval. - /// - /// How often Actor's AI is updated, in simulation updates. + /// @return How often Actor's AI is updated, in simulation updates. int GetAIUpdateInterval() const { return m_AIUpdateInterval; } - /// /// Sets the AI update interval. - /// - /// How often Actor's AI will now be updated, in simulation updates. + /// @param newAIUpdateInterval How often Actor's AI will now be updated, in simulation updates. void SetAIUpdateInterval(int newAIUpdateInterval) { m_AIUpdateInterval = newAIUpdateInterval; } - /// /// Gets how many threaded Lua states we'll use. -1 represents no override, which defaults to the maximum number of concurrent hardware threads. - /// - /// How many threaded Lua states we'll use. + /// @return How many threaded Lua states we'll use. int GetNumberOfLuaStatesOverride() const { return m_NumberOfLuaStatesOverride; } - /// /// Gets whether pathing requests will be forced to immediately complete for the next frame, or if they can take multiple frames to calculate. - /// - /// Whether pathing requests will be forced to immediately complete for the next frame + /// @return Whether pathing requests will be forced to immediately complete for the next frame bool GetForceImmediatePathingRequestCompletion() const { return m_ForceImmediatePathingRequestCompletion; } #pragma endregion #pragma region Gameplay Settings - /// /// Returns true if endless MetaGame mode is enabled. - /// - /// Whether endless mode is enabled via settings. + /// @return Whether endless mode is enabled via settings. bool EndlessMetaGameMode() const { return m_EndlessMetaGameMode; } - /// /// Sets whether endless MetaGame mode is enabled or not. - /// - /// Whether endless MetaGame mode is enabled or not. + /// @param enable Whether endless MetaGame mode is enabled or not. void SetEndlessMetaGameMode(bool enable) { m_EndlessMetaGameMode = enable; } - /// /// Whether we need to play blips when unseen layer is revealed. - /// - /// Whether we need to play blips when unseen layer is revealed. + /// @return Whether we need to play blips when unseen layer is revealed. bool BlipOnRevealUnseen() const { return m_BlipOnRevealUnseen; } - /// /// Sets whether we need to play blips when unseen layer is revealed. - /// - /// New value for Blip on reveal unseen option. + /// @param newValue New value for Blip on reveal unseen option. void SetBlipOnRevealUnseen(bool newValue) { m_BlipOnRevealUnseen = newValue; } - /// /// Gets the range in which devices on Scene will show the pick-up HUD. - /// - /// The range in which devices on Scene will show the pick-up HUD, in pixels. 0 means HUDs are hidden, -1 means unlimited range. + /// @return The range in which devices on Scene will show the pick-up HUD, in pixels. 0 means HUDs are hidden, -1 means unlimited range. float GetUnheldItemsHUDDisplayRange() const { return m_UnheldItemsHUDDisplayRange; } - /// /// Sets the range in which devices on Scene will show the pick-up HUD. - /// - /// The new range in which devices on Scene will show the pick-up HUD, in pixels. 0 means HUDs are hidden, -1 means unlimited range. + /// @param newRadius The new range in which devices on Scene will show the pick-up HUD, in pixels. 0 means HUDs are hidden, -1 means unlimited range. void SetUnheldItemsHUDDisplayRange(float newRadius) { m_UnheldItemsHUDDisplayRange = std::floor(newRadius); } - /// /// Gets whether or not devices on Scene should always show their pick-up HUD when the player is in strategic mode. - /// - /// Whether or not devices on Scene should always show their pick-up HUD when the player is in strategic mode. + /// @return Whether or not devices on Scene should always show their pick-up HUD when the player is in strategic mode. bool AlwaysDisplayUnheldItemsInStrategicMode() const { return m_AlwaysDisplayUnheldItemsInStrategicMode; } - /// /// Sets whether or not devices on Scene should always show their pick-up HUD when the player is in strategic mode. - /// - /// Whether or not devices on Scene should always show their pick-up HUD when the player is in strategic mode. + /// @param shouldShowUnheldItemsInStrategicMode Whether or not devices on Scene should always show their pick-up HUD when the player is in strategic mode. void SetAlwaysDisplayUnheldItemsInStrategicMode(bool shouldShowUnheldItemsInStrategicMode) { m_AlwaysDisplayUnheldItemsInStrategicMode = shouldShowUnheldItemsInStrategicMode; } - /// /// Gets the number of MS a PieSlice with a sub-PieMenu needs to be hovered over for the sub-PieMenu to open. - /// - /// The number of MS a PieSlice with a sub-PieMenu needs to be hovered over for the sub-PieMenu to open. + /// @return The number of MS a PieSlice with a sub-PieMenu needs to be hovered over for the sub-PieMenu to open. int GetSubPieMenuHoverOpenDelay() const { return m_SubPieMenuHoverOpenDelay; } - /// /// Sets the number of MS a PieSlice with a sub-PieMenu needs to be hovered over for the sub-PieMenu to open. - /// - /// The number of MS a PieSlice with a sb-PieMenu needs to be hovered over for the sub-PieMenu to open. + /// @param newSubPieMenuHoverOpenDelay The number of MS a PieSlice with a sb-PieMenu needs to be hovered over for the sub-PieMenu to open. void SetSubPieMenuHoverOpenDelay(int newSubPieMenuHoverOpenDelay) { m_SubPieMenuHoverOpenDelay = newSubPieMenuHoverOpenDelay; } - /// /// Whether red and white flashes appear when brain is damaged. - /// - /// Whether red and white flashes appear when brain is damaged. + /// @return Whether red and white flashes appear when brain is damaged. bool FlashOnBrainDamage() const { return m_FlashOnBrainDamage; } - /// /// Sets whether red and white flashes appear when brain is damaged. - /// - /// New value for Flash on brain damage setting. + /// @param newValue New value for Flash on brain damage setting. void SetFlashOnBrainDamage(bool newValue) { m_FlashOnBrainDamage = newValue; } - /// /// Whether we need to show items from other factions in buy menu GUI. - /// - /// True if we need to show foreign items. + /// @return True if we need to show foreign items. bool ShowForeignItems() const { return m_ShowForeignItems; } - /// /// Set whether we need to show items from other factions in buy menu GUI. - /// - /// If we need to show foreign items. + /// @param newValue If we need to show foreign items. void SetShowForeignItems(bool newValue) { m_ShowForeignItems = newValue; } - /// /// Gets whether the crab bomb effect is enabled or not. - /// - /// Whether the crab bomb effect is enabled or not. False means releasing whatever number of crabs will do nothing except release whatever number of crabs. + /// @return Whether the crab bomb effect is enabled or not. False means releasing whatever number of crabs will do nothing except release whatever number of crabs. bool CrabBombsEnabled() const { return m_EnableCrabBombs; } - /// /// Sets whether the crab bomb effect is enabled or not. - /// - /// Enable the crab bomb effect or not. False means releasing whatever number of crabs will do nothing except release whatever number of crabs. + /// @param enable Enable the crab bomb effect or not. False means releasing whatever number of crabs will do nothing except release whatever number of crabs. void SetCrabBombsEnabled(bool enable) { m_EnableCrabBombs = enable; } - /// /// Gets the number of crabs needed to be released at once to trigger the crab bomb effect. - /// - /// The number of crabs needed to be released at once to trigger the crab bomb effect. + /// @return The number of crabs needed to be released at once to trigger the crab bomb effect. int GetCrabBombThreshold() const { return m_CrabBombThreshold; } - /// /// Sets the number of crabs needed to be released at once to trigger the crab bomb effect. - /// - /// The new number of crabs needed to be released at once to trigger the crab bomb effect. + /// @param newThreshold The new number of crabs needed to be released at once to trigger the crab bomb effect. void SetCrabBombThreshold(int newThreshold) { m_CrabBombThreshold = newThreshold; } - /// /// Gets whether the HUD of enemy Actors is set to be visible to the player or not. - /// - /// Whether the HUD of enemy Actors is visible to the player. + /// @return Whether the HUD of enemy Actors is visible to the player. bool ShowEnemyHUD() const { return m_ShowEnemyHUD; } - /// /// Sets whether the HUD of enemy Actors should to be visible to the player or not. - /// - /// Whether the HUD of enemy Actors should be visible to the player or not. + /// @param showHUD Whether the HUD of enemy Actors should be visible to the player or not. void SetShowEnemyHUD(bool showHUD) { m_ShowEnemyHUD = showHUD; } - /// /// Gets whether smart BuyMenu navigation is enabled, meaning swapping to equipment mode and back will change active tabs in the BuyMenu. - /// - /// Whether smart BuyMenu navigation is enabled or not. + /// @return Whether smart BuyMenu navigation is enabled or not. bool SmartBuyMenuNavigationEnabled() const { return m_EnableSmartBuyMenuNavigation; } - /// /// Sets whether smart BuyMenu navigation is enabled, meaning swapping to equipment mode and back will change active tabs in the BuyMenu. - /// - /// Whether to enable smart BuyMenu navigation or not. + /// @param enable Whether to enable smart BuyMenu navigation or not. void SetSmartBuyMenuNavigation(bool enable) { m_EnableSmartBuyMenuNavigation = enable; } - /// /// Gets whether gold gathered by Actors is automatically added into team funds. - /// - /// Whether gold gathered by Actors is automatically added into team funds. + /// @return Whether gold gathered by Actors is automatically added into team funds. bool GetAutomaticGoldDeposit() const { return m_AutomaticGoldDeposit; } #pragma endregion #pragma region Network Settings - /// /// Gets the player name that is used in network multiplayer matches. - /// - /// String with the network player name. + /// @return String with the network player name. std::string GetPlayerNetworkName() const { return m_PlayerNetworkName; } - /// /// Sets the player name that will be used in network multiplayer matches. - /// - /// String with the new player name to use. + /// @param newName String with the new player name to use. void SetPlayerNetworkName(const std::string& newName) { m_PlayerNetworkName = newName.empty() ? "Dummy" : newName; } - /// /// Gets the LAN server address to connect to. - /// - /// The current LAN server address to connect to. + /// @return The current LAN server address to connect to. std::string GetNetworkServerAddress() const { return m_NetworkServerAddress; } - /// /// Sets the LAN server address to connect to. - /// - /// New LAN server address to connect to. + /// @param newName New LAN server address to connect to. void SetNetworkServerAddress(const std::string& newAddress) { m_NetworkServerAddress = newAddress.empty() ? "127.0.0.1:8000" : newAddress; } - /// /// Gets the NAT punch-through server address. - /// - /// The current NAT punch-through server address to connect to. + /// @return The current NAT punch-through server address to connect to. std::string& GetNATServiceAddress() { return m_NATServiceAddress; } - /// /// Sets the NAT punch-through server address. - /// - /// New NAT punch-through server address to connect to. + /// @param newValue New NAT punch-through server address to connect to. void SetNATServiceAddress(const std::string& newAddress) { m_NATServiceAddress = newAddress.empty() ? "127.0.0.1:61111" : newAddress; } - /// /// Gets the server name used when connecting via NAT punch-through service. - /// - /// Name of the NAT punch-through server. + /// @return Name of the NAT punch-through server. std::string& GetNATServerName() { return m_NATServerName; } - /// /// Sets the server name to use when connecting via NAT punch-through service. - /// - /// New NAT punch-through server name. + /// @param newValue New NAT punch-through server name. void SetNATServerName(const std::string& newName) { m_NATServerName = newName.empty() ? "DefaultServerName" : newName; } - /// /// Gets the server password to use when connecting via NAT punch-through service. - /// - /// The server password to use when connecting via NAT punch-through service. + /// @return The server password to use when connecting via NAT punch-through service. std::string& GetNATServerPassword() { return m_NATServerPassword; } - /// /// Sets the server password to use when connecting via NAT punch-through service. - /// - /// New password to use when connecting via NAT punch-through service. + /// @param newValue New password to use when connecting via NAT punch-through service. void SetNATServerPassword(const std::string& newValue) { m_NATServerPassword = newValue.empty() ? "DefaultServerPassword" : newValue; } - /// /// Gets whether or not experimental multiplayer speedboosts should be used. - /// - /// Whether or not experimental multiplayer speedboosts should be used. + /// @return Whether or not experimental multiplayer speedboosts should be used. bool UseExperimentalMultiplayerSpeedBoosts() const { return m_UseExperimentalMultiplayerSpeedBoosts; } - /// /// Sets whether or not experimental multiplayer speedboosts should be used. - /// - /// Whether or not experimental multiplayer speedboosts should be used. + /// @param newValue Whether or not experimental multiplayer speedboosts should be used. void SetUseExperimentalMultiplayerSpeedBoosts(bool newValue) { m_UseExperimentalMultiplayerSpeedBoosts = newValue; } #pragma endregion #pragma region Editor Settings - /// /// Returns the list of visible assembly groups. - /// - /// List of visible assembly groups. + /// @return List of visible assembly groups. std::list GetVisibleAssemblyGroupsList() const { return m_VisibleAssemblyGroupsList; } - /// /// Whether editors will allow to select Base.rte as a module to save in - /// - /// True of editors are allowed to select Base.rte as a module to save in. + /// @return True of editors are allowed to select Base.rte as a module to save in. bool AllowSavingToBase() const { return m_AllowSavingToBase; } - /// /// Whether we need to show MetaScenes in editors and scenario UI. - /// - /// True if we need to show MetaScenes. + /// @return True if we need to show MetaScenes. bool ShowMetascenes() const { return m_ShowMetaScenes; } #pragma endregion #pragma region Mod and Script Management - /// /// Gets the map of mods which are disabled. - /// - /// Map of mods which are disabled. + /// @return Map of mods which are disabled. std::unordered_map& GetDisabledModsMap() { return m_DisabledMods; } - /// /// Gets whether the specified mod is disabled in the settings. - /// - /// Mod to check. - /// Whether the mod is disabled via settings. + /// @param modModule Mod to check. + /// @return Whether the mod is disabled via settings. bool IsModDisabled(const std::string& modModule) const { return (m_DisabledMods.find(modModule) != m_DisabledMods.end()) ? m_DisabledMods.at(modModule) : false; } - /// /// Gets the map of global scripts which are enabled. - /// - /// Map of global scripts which are enabled. + /// @return Map of global scripts which are enabled. std::unordered_map& GetEnabledGlobalScriptMap() { return m_EnabledGlobalScripts; } - /// /// Gets whether the specified global script is enabled in the settings. - /// - /// Global script to check. - /// Whether the global script is enabled via settings. + /// @param scriptName Global script to check. + /// @return Whether the global script is enabled via settings. bool IsGlobalScriptEnabled(const std::string& scriptName) const { return (m_EnabledGlobalScripts.find(scriptName) != m_EnabledGlobalScripts.end()) ? m_EnabledGlobalScripts.at(scriptName) : false; } #pragma endregion #pragma region Misc Settings - /// /// Gets whether the game intro is set to be skipped on game startup or not. - /// - /// Whether intro is set to be skipped or not. + /// @return Whether intro is set to be skipped or not. bool SkipIntro() const { return m_SkipIntro; } - /// /// Sets whether the game intro should be skipped on game startup or not. - /// - /// Whether to skip game intro or not. + /// @param play Whether to skip game intro or not. void SetSkipIntro(bool play) { m_SkipIntro = play; } - /// /// Gets whether tooltip display on certain UI elements is enabled or not. - /// - /// Whether tooltips are displayed or not. + /// @return Whether tooltips are displayed or not. bool ShowToolTips() const { return m_ShowToolTips; } - /// /// Sets whether to display tooltips on certain UI elements or not. - /// - /// Whether to display tooltips or not. + /// @param showToolTips Whether to display tooltips or not. void SetShowToolTips(bool showToolTips) { m_ShowToolTips = showToolTips; } - /// /// Gets whether to draw AtomGroup visualizations or not. - /// - /// Whether to draw AtomGroup visualizations or not. + /// @return Whether to draw AtomGroup visualizations or not. bool DrawAtomGroupVisualizations() const { return m_DrawAtomGroupVisualizations; } - /// /// Sets whether to draw AtomGroup visualizations or not. - /// - /// Whether to draw AtomGroup visualizations or not. + /// @param drawAtomGroupVisualizations Whether to draw AtomGroup visualizations or not. void SetDrawAtomGroupVisualizations(bool drawAtomGroupVisualizations) { m_DrawAtomGroupVisualizations = drawAtomGroupVisualizations; } - /// /// Gets whether to draw HandGroup and FootGroup visualizations or not. - /// - /// Whether to draw HandGroup and FootGroup visualizations or not. + /// @return Whether to draw HandGroup and FootGroup visualizations or not. bool DrawHandAndFootGroupVisualizations() const { return m_DrawHandAndFootGroupVisualizations; } - /// /// Sets whether to draw HandGroup and FootGroup visualizations or not. - /// - /// Whether to draw HandGroup and FootGroup visualizations or not. + /// @param drawHandAndFootGroupVisualizations Whether to draw HandGroup and FootGroup visualizations or not. void SetDrawHandAndFootGroupVisualizations(bool drawHandAndFootGroupVisualizations) { m_DrawHandAndFootGroupVisualizations = drawHandAndFootGroupVisualizations; } - /// /// Gets whether to draw LimbPath visualizations or not. - /// - /// Whether to draw LimbPath visualizations or not. + /// @return Whether to draw LimbPath visualizations or not. bool DrawLimbPathVisualizations() const { return m_DrawLimbPathVisualizations; } - /// /// Sets whether to draw LimbPath visualizations or not. - /// - /// Whether to draw AtomGroup visualizations or not. + /// @param drawAtomGroupVisualizations Whether to draw AtomGroup visualizations or not. void SetDrawLimbPathVisualizations(bool drawLimbPathVisualizations) { m_DrawLimbPathVisualizations = drawLimbPathVisualizations; } - /// /// Gets whether debug print mode is enabled or not. - /// - /// Whether debug print mode is enabled or not. + /// @return Whether debug print mode is enabled or not. bool PrintDebugInfo() const { return m_PrintDebugInfo; } - /// /// Sets print debug info mode. - /// - /// New debug print mode value. + /// @param printDebugInfo New debug print mode value. void SetPrintDebugInfo(bool printDebugInfo) { m_PrintDebugInfo = printDebugInfo; } - /// /// Gets whether displaying the reader progress report during module loading is disabled or not. - /// - /// Whether the reader progress report is being displayed during module loading or not. + /// @return Whether the reader progress report is being displayed during module loading or not. bool GetLoadingScreenProgressReportDisabled() const { return m_DisableLoadingScreenProgressReport; } - /// /// Sets whether the reader progress report should be displayed during module loading or not. - /// - /// Whether to display the reader progress report during module loading or not. + /// @param disable Whether to display the reader progress report during module loading or not. void SetLoadingScreenProgressReportDisabled(bool disable) { m_DisableLoadingScreenProgressReport = disable; } - /// /// Gets how accurately the reader progress report tells what line it's reading during module loading. - /// - /// How accurately the reader progress report tells what line it's reading during module loading. + /// @return How accurately the reader progress report tells what line it's reading during module loading. int LoadingScreenProgressReportPrecision() const { return m_LoadingScreenProgressReportPrecision; } - /// /// Gets the multiplier value for the transition durations between different menus. - /// - /// The multiplier value for the transition durations between different menus. Lower values equal faster transitions. + /// @return The multiplier value for the transition durations between different menus. Lower values equal faster transitions. float GetMenuTransitionDurationMultiplier() const { return m_MenuTransitionDurationMultiplier; } - /// /// Sets the multiplier value for the transition durations between different menus. - /// - /// New multiplier value for the transition durations between different menus. Lower values equal faster transitions. + /// @param newSpeed New multiplier value for the transition durations between different menus. Lower values equal faster transitions. void SetMenuTransitionDurationMultiplier(float newSpeed) { m_MenuTransitionDurationMultiplier = std::max(0.0F, newSpeed); } - /// /// Gets whether the duration of module loading (extraction included) is being measured or not. For benchmarking purposes. - /// - /// Whether duration is being measured or not. + /// @return Whether duration is being measured or not. bool IsMeasuringModuleLoadTime() const { return m_MeasureModuleLoadTime; } - /// /// Sets whether the duration of module loading (extraction included) should be measured or not. For benchmarking purposes. - /// - /// Whether duration should be measured or not. + /// @param measure Whether duration should be measured or not. void MeasureModuleLoadTime(bool measure) { m_MeasureModuleLoadTime = measure; } #pragma endregion @@ -583,9 +417,7 @@ namespace RTE { std::string m_SettingsPath; //!< String containing the Path to the Settings.ini file. - /// /// Clears all the member variables of this SettingsMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/ThreadMan.cpp b/Source/Managers/ThreadMan.cpp index 42a7ab4c6b..7df1e603d5 100644 --- a/Source/Managers/ThreadMan.cpp +++ b/Source/Managers/ThreadMan.cpp @@ -1,46 +1,18 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ThreadMan.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the ThreadMan class. -// Project: Retro Terrain Engine -// Author(s): -// -// - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "ThreadMan.h" using namespace std; namespace RTE { - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ThreadMan, effectively - // resetting the members of this abstraction level only. - void ThreadMan::Clear() { m_PriorityThreadPool.reset(); m_BackgroundThreadPool.reset(std::thread::hardware_concurrency() / 2); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ThreadMan object ready for use. - int ThreadMan::Create() { return 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the ThreadMan object. - void ThreadMan::Destroy() { Clear(); } diff --git a/Source/Managers/ThreadMan.h b/Source/Managers/ThreadMan.h index 69f2be9b07..5fa41f9aec 100644 --- a/Source/Managers/ThreadMan.h +++ b/Source/Managers/ThreadMan.h @@ -1,18 +1,9 @@ #ifndef _RTEThreadMan_ #define _RTEThreadMan_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: ThreadMan.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the ThreadMan class. -// Project: Retro Terrain Engine -// Author(s): -// -// - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Header file for the ThreadMan class. +/// Author(s): +/// Inclusions of header files #include "Singleton.h" #define g_ThreadMan ThreadMan::Instance() @@ -20,95 +11,48 @@ namespace RTE { - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: ThreadMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: The centralized singleton manager of all threads. - // Parent(s): Singleton - // Class history: 03/29/2014 ThreadMan created. - + /// The centralized singleton manager of all threads. class ThreadMan : public Singleton { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: ThreadMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a ThreadMan object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a ThreadMan object in system + /// memory. Create() should be called before using the object. ThreadMan() { Clear(); Create(); } - /// /// Makes the TimerMan object ready for use. - /// void Initialize(){}; - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~ThreadMan - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a ThreadMan object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a ThreadMan object before deletion + /// from system memory. virtual ~ThreadMan() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the ThreadMan object ready for use. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the ThreadMan object ready for use. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. virtual int Create(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Virtual method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire ThreadMan, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire ThreadMan, including its inherited members, to + /// their default settings or values. virtual void Reset() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the ThreadMan object. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the ThreadMan object. void Destroy(); BS::thread_pool& GetPriorityThreadPool() { return m_PriorityThreadPool; } BS::thread_pool& GetBackgroundThreadPool() { return m_BackgroundThreadPool; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this ThreadMan, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this ThreadMan, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/TimerMan.cpp b/Source/Managers/TimerMan.cpp index 49b3315572..4a0cad188d 100644 --- a/Source/Managers/TimerMan.cpp +++ b/Source/Managers/TimerMan.cpp @@ -12,8 +12,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TimerMan::Clear() { m_StartTime = std::chrono::steady_clock::now(); m_TicksPerSecond = 1000000; @@ -31,7 +29,6 @@ namespace RTE { m_SimPaused = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TimerMan::Initialize() { // Get the frequency of ticks/s for this machine m_TicksPerSecond = 1000000; @@ -42,26 +39,18 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - long long TimerMan::GetAbsoluteTime() const { return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float TimerMan::GetRealToSimCap() const { return c_RealToSimCap; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float TimerMan::GetAIDeltaTimeSecs() const { return m_DeltaTimeS * static_cast(g_SettingsMan.GetAIUpdateInterval()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TimerMan::ResetTime() { m_StartTime = std::chrono::steady_clock::now(); @@ -74,8 +63,6 @@ namespace RTE { m_TimeScale = 1.0F; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TimerMan::UpdateSim() { if (TimeForSimUpdate()) { // Transfer ticks from the accumulator to the sim time ticks. @@ -92,8 +79,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TimerMan::Update() { long long prevTime = m_RealTimeTicks; m_RealTimeTicks = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_StartTime).count(); diff --git a/Source/Managers/TimerMan.h b/Source/Managers/TimerMan.h index b3826bf090..8491f0eb06 100644 --- a/Source/Managers/TimerMan.h +++ b/Source/Managers/TimerMan.h @@ -7,169 +7,119 @@ namespace RTE { - /// /// The centralized singleton manager of all Timers and overall timekeeping in RTE. /// Uses QueryPerformanceCounter for sub-ms resolution timers and the model described in http://www.gaffer.org/game-physics/fix-your-timestep. - /// class TimerMan : public Singleton { public: #pragma region Creation - /// /// Constructor method used to instantiate a TimerMan object in system memory. Initialize() should be called before using this object. - /// TimerMan() { Clear(); Initialize(); }; - /// /// Makes the TimerMan object ready for use. - /// void Initialize(); #pragma endregion #pragma region Destruction - /// /// Destroys and resets (through Clear()) the TimerMan object. - /// void Destroy() { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Gets the current time stamp in microseconds unrelated to TimerMan updates. Can be used to measure time intervals during a single frame update. - /// - /// Current time stamp in microseconds. + /// @return Current time stamp in microseconds. long long GetAbsoluteTime() const; - /// /// Sets the sim to be paused, ie no real time ticks will be transferred to the sim accumulator while this is set to true. /// This also clears the accumulator, to avoid the case where the sim may update while paused when behind schedule. - /// - /// Whether the sim should be paused or not. + /// @param pause Whether the sim should be paused or not. void PauseSim(bool pause = false) { m_SimPaused = pause; if (pause) m_SimAccumulator = 0.0F; } - /// /// Tells whether there is enough sim time accumulated to do at least one physics update. - /// - /// Whether there is enough sim time to do a physics update. + /// @return Whether there is enough sim time to do a physics update. bool TimeForSimUpdate() const { return m_SimAccumulator >= m_DeltaTime; } - /// /// Tells whether the current simulation update will be drawn in a frame. Use this to check if it is necessary to draw purely graphical things during the sim update. - /// - /// Whether this is the last sim update before a frame with its results will appear. + /// @return Whether this is the last sim update before a frame with its results will appear. bool DrawnSimUpdate() const { return m_DrawnSimUpdate; } - /// /// Tells how many sim updates have been performed since the last one that ended up being a drawn frame. /// If negative, it means no sim updates have happened, and a same frame will be drawn again. - /// - /// The number of pure sim updates that have happened since the last drawn. + /// @return The number of pure sim updates that have happened since the last drawn. int SimUpdatesSinceDrawn() const { return m_SimUpdatesSinceDrawn; } - /// /// Gets the simulation speed over real time. - /// - /// The value of the simulation speed over real time. + /// @return The value of the simulation speed over real time. float GetSimSpeed() const { return m_SimSpeed; } - /// /// Gets a time scale factor which will be used to speed up or slow down the progress of the simulation time in relation to the real world time. - /// - /// A factor between the real world time, and the simulation time. + /// @return A factor between the real world time, and the simulation time. float GetTimeScale() const { return m_TimeScale; } - /// /// Sets a time scale factor which will be used to speed up or slow down the progress of the simulation time in relation to the real world time. - /// - /// A factor between the real world time, and the simulation time. A value of 2.0 means simulation runs twice as fast as normal. + /// @param timeScale A factor between the real world time, and the simulation time. A value of 2.0 means simulation runs twice as fast as normal. void SetTimeScale(float timeScale = 1.0F) { m_TimeScale = timeScale; } - /// /// Gets the cap of the amount of seconds which can be transferred from the real time to the simulated time in one update. - /// - /// A float describing the current cap in seconds. + /// @return A float describing the current cap in seconds. float GetRealToSimCap() const; - /// /// Gets the number of ticks per second (the resolution of the timer). - /// - /// The number of ticks per second. + /// @return The number of ticks per second. long long GetTicksPerSecond() const { return m_TicksPerSecond; } - /// /// Gets a current global real time measured in ticks from the start of the simulation up to the last Update of this TimerMan. Use TickFrequency to determine how many ticks go in a second. - /// - /// The number of ticks passed since the simulation started. + /// @return The number of ticks passed since the simulation started. long long GetRealTickCount() const { return m_RealTimeTicks; } - /// /// Gets a current global simulation time measured in ticks from the start of the simulation up to the last Update of this TimerMan. Use TickFrequency to determine how many ticks go in a second. - /// - /// The number of ticks passed since the simulation started. + /// @return The number of ticks passed since the simulation started. long long GetSimTickCount() const { return m_SimTimeTicks; } - /// /// Gets a current global simulation time, measured in sim updates, from the start of the simulation up to the last Update of this TimerMan. - /// - /// The number of simulation updates that have occurred since the simulation started. + /// @return The number of simulation updates that have occurred since the simulation started. long long GetSimUpdateCount() const { return m_SimUpdateCount; } - /// /// Gets a current global simulation time measured in ms ticks from the start of the simulation up to the last UpdateSim of this TimerMan. - /// - /// The number of ms passed since the simulation started. + /// @return The number of ms passed since the simulation started. long long GetSimTimeMS() const { return static_cast((static_cast(m_SimTimeTicks) / static_cast(m_TicksPerSecond)) * 0.001F); } - /// /// Gets the current number of ticks that the simulation should be updating with. - /// - /// The current fixed delta time that the simulation should be updating with, in ticks. + /// @return The current fixed delta time that the simulation should be updating with, in ticks. long long GetDeltaTimeTicks() const { return m_DeltaTime; } - /// /// Sets the number of ticks that a simulation update delta time should take. - /// - /// The new delta time in ticks. + /// @param newDelta The new delta time in ticks. void SetDeltaTimeTicks(int newDelta) { m_DeltaTime = newDelta; m_DeltaTimeS = static_cast(m_DeltaTime) / static_cast(m_TicksPerSecond); } - /// /// Gets the current fixed delta time of the simulation updates, in ms. - /// - /// The current fixed delta time that the simulation should be updating with, in ms. + /// @return The current fixed delta time that the simulation should be updating with, in ms. float GetDeltaTimeMS() const { return m_DeltaTimeS * 1000; } - /// /// Gets the current fixed delta time of the simulation updates, in seconds. - /// - /// The current fixed delta time that the simulation should be updating with, in seconds. + /// @return The current fixed delta time that the simulation should be updating with, in seconds. float GetDeltaTimeSecs() const { return m_DeltaTimeS; } - /// /// Gets the current fixed delta time of AI updates, in seconds. - /// - /// The current fixed delta time of AI updates, in seconds. + /// @return The current fixed delta time of AI updates, in seconds. float GetAIDeltaTimeSecs() const; - /// /// Gets the current fixed delta time of AI updates, in ms. - /// - /// The current fixed delta time of AI updates, in ms. + /// @return The current fixed delta time of AI updates, in ms. float GetAIDeltaTimeMS() const { return GetAIDeltaTimeSecs() * 1000; }; - /// /// Sets the number of seconds that a simulation update delta time should take. - /// - /// The new delta time in seconds. + /// @param newDelta The new delta time in seconds. void SetDeltaTimeSecs(float newDelta) { m_DeltaTimeS = newDelta; m_DeltaTime = static_cast(m_DeltaTimeS * static_cast(m_TicksPerSecond)); @@ -177,27 +127,19 @@ namespace RTE { #pragma endregion #pragma region Concrete Methods - /// /// Resets the measured real and simulation times to 0. - /// void ResetTime(); - /// /// Updates the simulation time to represent the current amount of simulation time passed from the start of the simulation up to the last update. - /// void UpdateSim(); - /// /// Updates the real time ticks based on the actual clock time and adds it to the accumulator which the simulation ticks will draw from in whole DeltaTime-sized chunks. - /// void Update(); #pragma endregion #pragma region Network Handling - /// /// Gets the duration the thread should be put to sleep. This is used when ServerSimSleepWhenIdle is true to put the thread to sleep if the sim frame is finished faster than it usually should. - /// - /// The duration the thread should be put to sleep. + /// @return The duration the thread should be put to sleep. long long GetTimeToSleep() const { return (m_DeltaTime - m_SimAccumulator) / 2; }; #pragma endregion @@ -222,9 +164,7 @@ namespace RTE { bool m_SimPaused; //!< Simulation paused; no real time ticks will go to the sim accumulator. private: - /// /// Clears all the member variables of this TimerMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/UInputMan.cpp b/Source/Managers/UInputMan.cpp index 193e189684..88cc3b5bae 100644 --- a/Source/Managers/UInputMan.cpp +++ b/Source/Managers/UInputMan.cpp @@ -24,8 +24,6 @@ namespace RTE { std::vector UInputMan::s_PrevJoystickStates(Players::MaxPlayerCount); std::vector UInputMan::s_ChangedJoystickStates(Players::MaxPlayerCount); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::Clear() { m_SkipHandlingSpecialInput = false; m_TextInput.clear(); @@ -87,8 +85,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int UInputMan::Initialize() { int numKeys; const Uint8* keyboardState = SDL_GetKeyboardState(&numKeys); @@ -132,8 +128,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::LoadDeviceIcons() { m_DeviceIcons[InputDevice::DEVICE_KEYB_ONLY] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Keyboard")); m_DeviceIcons[InputDevice::DEVICE_MOUSE_KEYB] = dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device Mouse")); @@ -143,8 +137,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector UInputMan::AnalogMoveValues(int whichPlayer) { Vector moveValues(0, 0); InputDevice device = m_ControlScheme.at(whichPlayer).GetDevice(); @@ -163,8 +155,6 @@ namespace RTE { return moveValues; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector UInputMan::AnalogAimValues(int whichPlayer) { InputDevice device = m_ControlScheme.at(whichPlayer).GetDevice(); @@ -191,8 +181,6 @@ namespace RTE { return aimValues; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector UInputMan::GetMenuDirectional() { Vector allInput(0, 0); for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { @@ -239,8 +227,6 @@ namespace RTE { return allInput; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::AnyKeyOrJoyInput() const { bool input = AnyKeyPress(); if (!input) { @@ -249,8 +235,6 @@ namespace RTE { return input; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::AnyPress() const { bool pressed = false; @@ -267,8 +251,6 @@ namespace RTE { return pressed; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::AnyStartPress(bool includeSpacebar) { if (KeyPressed(SDLK_ESCAPE) || (includeSpacebar && KeyPressed(SDLK_SPACE))) { return true; @@ -281,8 +263,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::AnyBackPress() { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { if (ElementPressed(player, InputElements::INPUT_BACK)) { @@ -292,8 +272,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::AnyKeyPress() const { for (size_t testKey = SDL_SCANCODE_A; testKey < SDL_NUM_SCANCODES; ++testKey) { if (s_PrevKeyStates[testKey] && s_ChangedKeyStates[testKey]) { @@ -303,8 +281,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int UInputMan::MouseUsedByPlayer() const { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { if (m_ControlScheme[player].GetDevice() == InputDevice::DEVICE_MOUSE_KEYB) { @@ -314,8 +290,6 @@ namespace RTE { return Players::NoPlayer; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::DisableMouseMoving(bool disable) { if (disable) { SDL_SetRelativeMouseMode(SDL_FALSE); @@ -327,8 +301,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector UInputMan::GetMouseMovement(int whichPlayer) const { if (IsInMultiplayerMode() && whichPlayer >= Players::PlayerOne && whichPlayer < Players::MaxPlayerCount) { return m_NetworkAccumulatedRawMouseMovement[whichPlayer]; @@ -339,8 +311,6 @@ namespace RTE { return Vector(0, 0); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::SetMouseValueMagnitude(float magCap, int whichPlayer) { if (IsInMultiplayerMode() && whichPlayer >= Players::PlayerOne && whichPlayer < Players::MaxPlayerCount) { m_NetworkAnalogMoveData[whichPlayer].CapMagnitude(m_MouseTrapRadius * magCap); @@ -348,8 +318,6 @@ namespace RTE { m_AnalogMouseData.SetMagnitude(m_MouseTrapRadius * magCap); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::SetMouseValueAngle(float angle, int whichPlayer) { if (IsInMultiplayerMode() && whichPlayer >= Players::PlayerOne && whichPlayer < Players::MaxPlayerCount) { m_NetworkAnalogMoveData[whichPlayer].SetAbsRadAngle(angle); @@ -357,8 +325,6 @@ namespace RTE { m_AnalogMouseData.SetAbsRadAngle(angle); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::SetMousePos(const Vector& newPos, int whichPlayer) const { // Only mess with the mouse if the original mouse position is not above the screen and may be grabbing the title bar of the game window if (!m_DisableMouseMoving && !m_TrapMousePos && (whichPlayer == Players::NoPlayer || m_ControlScheme.at(whichPlayer).GetDevice() == InputDevice::DEVICE_MOUSE_KEYB)) { @@ -366,8 +332,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::AnyMouseButtonPress() const { for (int button = MouseButtons::MOUSE_LEFT; button < MouseButtons::MAX_MOUSE_BUTTONS; ++button) { if (MouseButtonPressed(button, -1)) { @@ -377,8 +341,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::TrapMousePos(bool trap, int whichPlayer) { if (!IsInMultiplayerMode() && (whichPlayer == Players::NoPlayer || m_ControlScheme.at(whichPlayer).GetDevice() == InputDevice::DEVICE_MOUSE_KEYB)) { m_TrapMousePos = trap; @@ -387,8 +349,6 @@ namespace RTE { m_TrapMousePosPerPlayer[whichPlayer] = trap; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::ForceMouseWithinBox(int x, int y, int width, int height, int whichPlayer) const { // Only mess with the mouse if the original mouse position is not above the screen and may be grabbing the title bar of the game window. if (g_WindowMan.AnyWindowHasFocus() && !m_DisableMouseMoving && !m_TrapMousePos && (whichPlayer == Players::NoPlayer || m_ControlScheme[whichPlayer].GetDevice() == InputDevice::DEVICE_MOUSE_KEYB)) { @@ -424,8 +384,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::ForceMouseWithinPlayerScreen(bool force, int whichPlayer) { float resMultiplier = g_WindowMan.GetResMultiplier(); @@ -470,8 +428,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int UInputMan::GetJoystickAxisCount(int whichJoy) const { if (whichJoy >= 0 && whichJoy < s_PrevJoystickStates.size()) { return s_PrevJoystickStates[whichJoy].m_Axis.size(); @@ -479,8 +435,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int UInputMan::WhichJoyButtonHeld(int whichJoy) const { if (whichJoy >= 0 && whichJoy < s_PrevJoystickStates.size()) { for (int button = 0; button < s_PrevJoystickStates[whichJoy].m_Buttons.size(); ++button) { @@ -493,8 +447,6 @@ namespace RTE { return JoyButtons::JOY_NONE; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int UInputMan::WhichJoyButtonPressed(int whichJoy) const { if (whichJoy >= 0 && whichJoy < s_PrevJoystickStates.size()) { for (int button = 0; button < s_PrevJoystickStates[whichJoy].m_Buttons.size(); ++button) { @@ -506,8 +458,6 @@ namespace RTE { return JoyButtons::JOY_NONE; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float UInputMan::AnalogAxisValue(int whichJoy, int whichAxis) const { if (whichJoy < s_PrevJoystickStates.size() && whichAxis < s_PrevJoystickStates[whichJoy].m_Axis.size()) { if (s_PrevJoystickStates[whichJoy].m_JoystickID != -1) { @@ -518,8 +468,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::AnyJoyInput(bool checkForPresses) const { int gamepadIndex = 0; for (const Gamepad& gamepad: s_PrevJoystickStates) { @@ -546,8 +494,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::AnyJoyButtonPress(int whichJoy) const { for (int button = 0; button < s_PrevJoystickStates[whichJoy].m_Buttons.size(); ++button) { if (JoyButtonPressed(whichJoy, button)) { @@ -557,16 +503,12 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector UInputMan::GetNetworkAccumulatedRawMouseMovement(int player) { Vector accumulatedMovement = m_NetworkAccumulatedRawMouseMovement[player]; m_NetworkAccumulatedRawMouseMovement[player].Reset(); return accumulatedMovement; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::ClearNetworkAccumulatedStates() { for (int inputState = InputState::Pressed; inputState < InputState::InputStateCount; inputState++) { for (int element = InputElements::INPUT_L_UP; element < InputElements::INPUT_COUNT; element++) { @@ -575,8 +517,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::GetInputElementState(int whichPlayer, int whichElement, InputState whichState) { if (IsInMultiplayerMode() && whichPlayer >= Players::PlayerOne && whichPlayer < Players::MaxPlayerCount) { return GetNetworkInputElementState(whichPlayer, whichElement, whichState); @@ -618,8 +558,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::GetMenuButtonState(int whichButton, InputState whichState) { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { bool buttonState = false; @@ -638,8 +576,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::GetKeyboardButtonState(SDL_Scancode scancodeToTest, InputState whichState) const { if (m_DisableKeyboard && (scancodeToTest >= SDL_SCANCODE_0 && scancodeToTest < SDL_SCANCODE_ESCAPE)) { return false; @@ -657,8 +593,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::GetMouseButtonState(int whichPlayer, int whichButton, InputState whichState) const { if (whichButton < MouseButtons::MOUSE_LEFT || whichButton >= MouseButtons::MAX_MOUSE_BUTTONS) { return false; @@ -704,8 +638,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::GetJoystickButtonState(int whichJoy, int whichButton, InputState whichState) const { if (whichJoy < 0 || whichJoy >= s_PrevJoystickStates.size() || whichButton < 0 || whichButton >= s_PrevJoystickStates[whichJoy].m_Buttons.size()) { return false; @@ -728,8 +660,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool UInputMan::GetJoystickDirectionState(int whichJoy, int whichAxis, int whichDir, InputState whichState) const { if (whichJoy < 0 || whichJoy >= s_PrevJoystickStates.size() || whichAxis < 0 || whichAxis >= s_PrevJoystickStates[whichJoy].m_DigitalAxis.size()) { return false; @@ -765,14 +695,10 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::QueueInputEvent(const SDL_Event& inputEvent) { m_EventQueue.emplace_back(inputEvent); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int UInputMan::Update() { m_LastDeviceWhichControlledGUICursor = InputDevice::DEVICE_KEYB_ONLY; @@ -912,8 +838,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::HandleSpecialInput() { // If we launched into editor directly, skip the logic and quit quickly. if (g_ActivityMan.IsSetToLaunchIntoEditor() && KeyPressed(SDLK_ESCAPE)) { @@ -1023,8 +947,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::UpdateMouseInput() { // Detect and store mouse movement input, translated to analog stick emulation int mousePlayer = MouseUsedByPlayer(); @@ -1049,7 +971,6 @@ namespace RTE { } } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::UpdateJoystickAxis(std::vector::iterator device, int axis, int value) { if (device != s_PrevJoystickStates.end()) { @@ -1119,8 +1040,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::UpdateJoystickDigitalAxis() { for (size_t i = 0; i < s_PrevJoystickStates.size(); ++i) { for (size_t axis = 0; axis < s_PrevJoystickStates[i].m_DigitalAxis.size(); ++axis) { @@ -1140,8 +1059,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void UInputMan::HandleGamepadHotPlug(int deviceIndex) { SDL_Joystick* controller = nullptr; int controllerIndex = 0; @@ -1192,7 +1109,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::UpdateNetworkMouseMovement() { for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; player++) { if (!m_NetworkAccumulatedRawMouseMovement[player].IsZero()) { @@ -1219,7 +1135,6 @@ namespace RTE { m_NetworkMouseWheelState[player] = 0; } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::SetNetworkInputElementState(int player, int element, bool newState) { if (element >= InputElements::INPUT_L_UP && element < InputElements::INPUT_COUNT && player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_NetworkServerChangedInputElementState[player][element] = (newState != m_NetworkServerPreviousInputElementState[player][element]); @@ -1227,7 +1142,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::SetNetworkMouseButtonState(int player, int whichButton, InputState whichState, bool newState) { if (whichButton >= MouseButtons::MOUSE_LEFT && whichButton < MouseButtons::MAX_MOUSE_BUTTONS && player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_NetworkServerChangedMouseButtonState[player][whichButton] = (newState != m_NetworkServerPreviousMouseButtonState[player][whichButton]); @@ -1235,7 +1149,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void UInputMan::StoreInputEventsForNextUpdate() { // Store pressed and released events to be picked by NetworkClient during its update. These will be cleared after update so we don't care about false but we store the result regardless. for (int inputState = InputState::Pressed; inputState < InputState::InputStateCount; inputState++) { diff --git a/Source/Managers/UInputMan.h b/Source/Managers/UInputMan.h index e2861f2f04..2acd327637 100644 --- a/Source/Managers/UInputMan.h +++ b/Source/Managers/UInputMan.h @@ -18,16 +18,12 @@ namespace RTE { class Icon; - /// /// The singleton manager responsible for handling user input. - /// class UInputMan : public Singleton { friend class SettingsMan; public: - /// /// Enumeration for the mouse cursor actions in menus. - /// enum MenuCursorButtons { MENU_PRIMARY, MENU_SECONDARY, @@ -35,627 +31,452 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a UInputMan object in system memory. Create() should be called before using the object. - /// UInputMan() { Clear(); } - /// /// Makes the UInputMan object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a UInputMan object before deletion from system memory. - /// ~UInputMan() { Destroy(); } - /// /// Destroys and resets (through Clear()) the UInputMan object. - /// void Destroy() { Clear(); } #pragma endregion #pragma region Concrete Methods - /// /// Loads the input device icons from loaded presets. Can't do this during Create() because the presets don't exist so this will be called from MenuMan::Initialize() after modules are loaded. - /// void LoadDeviceIcons(); - /// /// Adds an (input) SDL_Event to the Event queue for processing on Update. - /// - /// The SDL input event to queue. + /// @param inputEvent The SDL input event to queue. void QueueInputEvent(const SDL_Event& inputEvent); - /// /// Updates the state of this UInputMan. Supposed to be done every frame. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Update(); #pragma endregion #pragma region Control Scheme and Input Mapping Handling - /// /// Sets whether to skip handling any special input (F1-F12, etc.) to avoid shenanigans during manual input mapping. - /// - /// Whether to skip handling special input or not. + /// @param skip Whether to skip handling special input or not. void SetSkipHandlingSpecialInput(bool skip) { m_SkipHandlingSpecialInput = skip; } - /// /// Gets the currently used input device of the specified player. - /// - /// Which player to get input device for. - /// A number value representing the currently used input device of this player. See InputDevice enumeration for values. + /// @param whichPlayer Which player to get input device for. + /// @return A number value representing the currently used input device of this player. See InputDevice enumeration for values. int GetInputDevice(int whichPlayer) const { return m_ControlScheme.at(whichPlayer).GetDevice(); } - /// /// Access a specific player's control scheme. - /// - /// Which player to get the scheme for. - /// A pointer to the requested player's control scheme. Ownership is NOT transferred! + /// @param whichPlayer Which player to get the scheme for. + /// @return A pointer to the requested player's control scheme. Ownership is NOT transferred! InputScheme* GetControlScheme(int whichPlayer) { return IsInMultiplayerMode() ? &m_ControlScheme[Players::PlayerOne] : &m_ControlScheme.at(whichPlayer); } - /// /// Get the current device Icon of a specific player's scheme. - /// - /// Which player to get the scheme device icon of. - /// A const pointer to the requested player's control scheme icon. Ownership is NOT transferred! + /// @param whichPlayer Which player to get the scheme device icon of. + /// @return A const pointer to the requested player's control scheme icon. Ownership is NOT transferred! const Icon* GetSchemeIcon(int whichPlayer) const { return (whichPlayer < Players::PlayerOne || whichPlayer >= Players::MaxPlayerCount) ? nullptr : m_DeviceIcons[m_ControlScheme.at(whichPlayer).GetDevice()]; } - /// /// Get the current device Icon of a specific device. - /// - /// Which device to get the icon of. - /// A const pointer to the requested device's control scheme icon. Ownership is NOT transferred! + /// @param whichDevice Which device to get the icon of. + /// @return A const pointer to the requested device's control scheme icon. Ownership is NOT transferred! const Icon* GetDeviceIcon(int whichDevice) const { return (whichDevice < InputDevice::DEVICE_KEYB_ONLY || whichDevice > InputDevice::DEVICE_GAMEPAD_4) ? nullptr : m_DeviceIcons[whichDevice]; } #pragma endregion #pragma region General Input Handling - /// /// Gets the last device which affected GUI cursor position. - /// - /// The last device which affected GUI cursor position. + /// @return The last device which affected GUI cursor position. InputDevice GetLastDeviceWhichControlledGUICursor() const { return m_LastDeviceWhichControlledGUICursor; } - /// /// Gets the analog moving values of a specific player's control scheme. - /// - /// Which player to check for. - /// The analog axis values ranging between -1.0 to 1.0, in both axes. + /// @param whichPlayer Which player to check for. + /// @return The analog axis values ranging between -1.0 to 1.0, in both axes. Vector AnalogMoveValues(int whichPlayer = 0); - /// /// Gets the analog aiming values of a specific player's control scheme. - /// - /// Which player to check for. - /// The analog axis values ranging between -1.0 to 1.0, in both axes. + /// @param whichPlayer Which player to check for. + /// @return The analog axis values ranging between -1.0 to 1.0, in both axes. Vector AnalogAimValues(int whichPlayer = 0); - /// /// Gets whether a specific input element was held during the last update. - /// - /// Which player to check for. - /// Which element to check for. - /// Whether the element is held or not. + /// @param whichPlayer Which player to check for. + /// @param whichElement Which element to check for. + /// @return Whether the element is held or not. bool ElementHeld(int whichPlayer, int whichElement) { return GetInputElementState(whichPlayer, whichElement, InputState::Held); } - /// /// Gets whether a specific input element was depressed between the last update and the one previous to it. - /// - /// Which player to check for. - /// Which element to check for. - /// Whether the element is pressed or not. + /// @param whichPlayer Which player to check for. + /// @param whichElement Which element to check for. + /// @return Whether the element is pressed or not. bool ElementPressed(int whichPlayer, int whichElement) { return GetInputElementState(whichPlayer, whichElement, InputState::Pressed); } - /// /// Gets whether a specific input element was released between the last update and the one previous to it. - /// - /// Which player to check for. - /// Which element to check for. - /// Whether the element is released or not. + /// @param whichPlayer Which player to check for. + /// @param whichElement Which element to check for. + /// @return Whether the element is released or not. bool ElementReleased(int whichPlayer, int whichElement) { return GetInputElementState(whichPlayer, whichElement, InputState::Released); } - /// /// Gets the generic direction input from any and all players which can affect a shared menu cursor. Normalized to 1.0 max. - /// - /// The vector with the directional input from any or all players. + /// @return The vector with the directional input from any or all players. Vector GetMenuDirectional(); - /// /// Gets whether any generic button with the menu cursor is held down. - /// - /// Which generic menu cursor button to check for. - /// Whether the button is held or not. + /// @param whichButton Which generic menu cursor button to check for. + /// @return Whether the button is held or not. bool MenuButtonHeld(int whichButton) { return GetMenuButtonState(whichButton, InputState::Held); } - /// /// Gets whether any generic button with the menu cursor was pressed between previous update and this. - /// - /// Which generic menu cursor button to check for. - /// Whether the button is pressed or not. + /// @param whichButton Which generic menu cursor button to check for. + /// @return Whether the button is pressed or not. bool MenuButtonPressed(int whichButton) { return GetMenuButtonState(whichButton, InputState::Pressed); } - /// /// Gets whether any generic button with the menu cursor was released between previous update and this. - /// - /// Which generic menu cursor button to check for. - /// Whether the button is released or not. + /// @param whichButton Which generic menu cursor button to check for. + /// @return Whether the button is released or not. bool MenuButtonReleased(int whichButton) { return GetMenuButtonState(whichButton, InputState::Released); } - /// /// Gets whether there is any input at all, keyboard or buttons or D-pad. - /// - /// Whether any buttons of pads are pressed at all. + /// @return Whether any buttons of pads are pressed at all. bool AnyKeyOrJoyInput() const; - /// /// Gets whether there are any key, button, or D-pad presses at all. MUST call Update before calling this for it to work properly! - /// - /// Whether any buttons of pads have been pressed at all since last frame. + /// @return Whether any buttons of pads have been pressed at all since last frame. bool AnyPress() const; - /// /// Gets whether there are any start key/button presses at all. MUST call Update before calling this for it to work properly! - /// - /// Whether to check for space bar presses or not. - /// Whether any start buttons or keys have been pressed at all since last frame. + /// @param includeSpacebar Whether to check for space bar presses or not. + /// @return Whether any start buttons or keys have been pressed at all since last frame. bool AnyStartPress(bool includeSpacebar = true); - /// /// Gets whether there are any back button presses at all. MUST call Update before calling this for it to work properly! - /// - /// Whether any back buttons have been pressed at all since last frame. + /// @return Whether any back buttons have been pressed at all since last frame. bool AnyBackPress(); - /// /// Gets the state of the Ctrl key. - /// - /// The state of the Ctrl key. + /// @return The state of the Ctrl key. bool FlagCtrlState() const { return ((SDL_GetModState() & KMOD_CTRL) > 0) ? true : false; } - /// /// Gets the state of the Alt key. - /// - /// The state of the Alt key. + /// @return The state of the Alt key. bool FlagAltState() const { return ((SDL_GetModState() & KMOD_ALT) > 0) ? true : false; } - /// /// Gets the state of the Shift key. - /// - /// The state of the Shift key. + /// @return The state of the Shift key. bool FlagShiftState() const { return ((SDL_GetModState() & KMOD_SHIFT) > 0) ? true : false; } #pragma endregion #pragma region Keyboard Handling - /// /// Temporarily disables most of the keyboard keys. This is used when typing into a dialog box is required. - /// - /// Whether to disable most keys or not. + /// @param disable Whether to disable most keys or not. void DisableKeys(bool disable = true) { m_DisableKeyboard = disable; } - /// /// Gets whether a key is being held right now, by scancode. - /// - /// A scancode to test. See SDL_Scancode enumeration. - /// Whether the key is held or not. + /// @param scancodeToTest A scancode to test. See SDL_Scancode enumeration. + /// @return Whether the key is held or not. bool KeyHeld(SDL_Scancode scancodeToTest) const { return GetKeyboardButtonState(scancodeToTest, InputState::Held); } - /// /// Gets whether a key is being held right now, by keycode. - /// - /// A keycode to test. See SDL_KeyCode enumeration. - /// Whether the key is held or not. + /// @param keycodeToTest A keycode to test. See SDL_KeyCode enumeration. + /// @return Whether the key is held or not. bool KeyHeld(SDL_Keycode keycodeToTest) const { return KeyHeld(SDL_GetScancodeFromKey(keycodeToTest)); } - /// /// Gets whether a key was pressed between the last update and the one previous to it, by scancode. - /// - /// A scancode to test. See SDL_Scancode enumeration. - /// Whether the key is pressed or not. + /// @param scancodeToTest A scancode to test. See SDL_Scancode enumeration. + /// @return Whether the key is pressed or not. bool KeyPressed(SDL_Scancode scancodeToTest) const { return GetKeyboardButtonState(scancodeToTest, InputState::Pressed); } - /// /// Gets whether a key was pressed between the last update and the one previous to it, by keycode. - /// - /// A keycode to test. See SDL_KeyCode enumeration. - /// Whether the key is pressed or not. + /// @param keycodeToTest A keycode to test. See SDL_KeyCode enumeration. + /// @return Whether the key is pressed or not. bool KeyPressed(SDL_Keycode keycodeToTest) const { return KeyPressed(SDL_GetScancodeFromKey(keycodeToTest)); } - /// /// Gets whether a key was released between the last update and the one previous to it, by scancode. - /// - /// A scancode to test. See SDL_Scancode enumeration. - /// Whether the key is released or not. + /// @param scancodeToTest A scancode to test. See SDL_Scancode enumeration. + /// @return Whether the key is released or not. bool KeyReleased(SDL_Scancode scancodeToTest) const { return GetKeyboardButtonState(scancodeToTest, InputState::Released); } - /// /// Gets whether a key was released between the last update and the one previous to it, by keycode. - /// - /// A keycode to test. See SDL_KeyCode enumeration. - /// Whether the key is released or not. + /// @param keycodeToTest A keycode to test. See SDL_KeyCode enumeration. + /// @return Whether the key is released or not. bool KeyReleased(SDL_Keycode keycodeToTest) const { return KeyReleased(SDL_GetScancodeFromKey(keycodeToTest)); } - /// /// Return true if there are any keyboard button presses at all. - /// - /// Whether any keyboard buttons have been pressed at all since last frame. + /// @return Whether any keyboard buttons have been pressed at all since last frame. bool AnyKeyPress() const; - /// /// Fills the given string with the text input since the last frame (if any). - /// - /// The std::string to fill. - /// Whether there is text input. + /// @param text The std::string to fill. + /// @return Whether there is text input. bool GetTextInput(std::string& text) const { text = m_TextInput; return !m_TextInput.empty(); } - /// /// Returns whether text input events are available. - /// bool HasTextInput() const { return !m_TextInput.empty(); } - /// /// Returns the current text input. - /// - /// The current text input. + /// @return The current text input. const std::string& GetTextInput() const { return m_TextInput; } #pragma endregion #pragma region Mouse Handling - /// /// Reports which player is using the mouse for control at this time if any. - /// - /// Which player is using the mouse. If no one is then -1 is returned. + /// @return Which player is using the mouse. If no one is then -1 is returned. int MouseUsedByPlayer() const; - /// /// Will temporarily disable positioning of the mouse. /// This is so that when focus is switched back to the game window, it avoids having the window fly away because the user clicked the title bar of the window. - /// - /// Whether to disable mouse positioning or not. + /// @param disable Whether to disable mouse positioning or not. void DisableMouseMoving(bool disable = true); - /// /// Get the absolute mouse position in window coordinates. - /// - /// The absolute mouse position. + /// @return The absolute mouse position. Vector GetAbsoluteMousePosition() const { return m_AbsoluteMousePos; } - /// /// Set the absolute mouse position (e.g. for player input mouse movement). Does not move the system cursor. - /// - /// The new mouse position. + /// @param pos The new mouse position. void SetAbsoluteMousePosition(const Vector& pos) { m_AbsoluteMousePos = pos; } - /// /// Gets the relative movement of the mouse since last update. Only returns true if the selected player is actually using the mouse. - /// - /// Which player to get movement for. If the player doesn't use the mouse this always returns a zero vector. - /// The relative mouse movements, in both axes. + /// @param whichPlayer Which player to get movement for. If the player doesn't use the mouse this always returns a zero vector. + /// @return The relative mouse movements, in both axes. Vector GetMouseMovement(int whichPlayer = -1) const; - /// /// Set the mouse's analog emulation output to be of a specific normalized magnitude. - /// - /// The normalized magnitude, between 0 and 1.0. - /// Which player to set magnitude for. Only relevant when in online multiplayer mode. + /// @param magCap The normalized magnitude, between 0 and 1.0. + /// @param whichPlayer Which player to set magnitude for. Only relevant when in online multiplayer mode. void SetMouseValueMagnitude(float magCap, int whichPlayer = Players::NoPlayer); - /// /// Sets the mouse's analog emulation output to be in a specific direction. - /// - /// The direction, in radians. - /// Which player to set magnitude for. Only relevant when in online multiplayer mode. + /// @param angle The direction, in radians. + /// @param whichPlayer Which player to set magnitude for. Only relevant when in online multiplayer mode. void SetMouseValueAngle(float angle, int whichPlayer = Players::NoPlayer); - /// /// Sets the absolute screen position of the mouse cursor. - /// - /// Where to place the mouse. - /// Which player is trying to control the mouse. Only the player with actual control over the mouse will be affected. -1 means do it regardless of player. + /// @param newPos Where to place the mouse. + /// @param whichPlayer Which player is trying to control the mouse. Only the player with actual control over the mouse will be affected. -1 means do it regardless of player. void SetMousePos(const Vector& newPos, int whichPlayer = -1) const; - /// /// Gets mouse sensitivity while in Activity. - /// - /// The current mouse sensitivity. + /// @return The current mouse sensitivity. float GetMouseSensitivity() const { return m_MouseSensitivity; } - /// /// Sets mouse sensitivity while in Activity. - /// - /// New sensitivity value. + /// @param sensitivity New sensitivity value. void SetMouseSensitivity(float sensitivity) { m_MouseSensitivity = std::clamp(sensitivity, 0.1F, 2.0F); } - /// /// Gets whether a mouse button is being held down right now. - /// - /// Which button to check for. - /// Which player to check for. - /// Whether the mouse button is held or not. + /// @param whichButton Which button to check for. + /// @param whichPlayer Which player to check for. + /// @return Whether the mouse button is held or not. bool MouseButtonHeld(int whichButton, int whichPlayer = Players::PlayerOne) const { return GetMouseButtonState(whichPlayer, whichButton, InputState::Held); } - /// /// Gets whether a mouse button was pressed between the last update and the one previous to it. - /// - /// Which button to check for. - /// Which player to check for. - /// Whether the mouse button is pressed or not. + /// @param whichButton Which button to check for. + /// @param whichPlayer Which player to check for. + /// @return Whether the mouse button is pressed or not. bool MouseButtonPressed(int whichButton, int whichPlayer = Players::PlayerOne) const { return GetMouseButtonState(whichPlayer, whichButton, InputState::Pressed); } - /// /// Gets whether a mouse button was released between the last update and the one previous to it. - /// - /// Which button to check for. - /// Which player to check for. - /// Whether the mouse button is released or not. + /// @param whichButton Which button to check for. + /// @param whichPlayer Which player to check for. + /// @return Whether the mouse button is released or not. bool MouseButtonReleased(int whichButton, int whichPlayer = Players::PlayerOne) const { return GetMouseButtonState(whichPlayer, whichButton, InputState::Released); } - /// /// Gets whether the mouse wheel has been moved past the threshold limit in either direction this frame. - /// - /// The direction the mouse wheel has been moved which is past that threshold. 0 means not past, negative means moved down, positive means moved up. + /// @return The direction the mouse wheel has been moved which is past that threshold. 0 means not past, negative means moved down, positive means moved up. int MouseWheelMoved() const { return m_MouseWheelChange; } - /// /// Gets the relative mouse wheel position for the specified player. - /// - /// The player to get mouse wheel position for. - /// The relative mouse wheel position for the specified player. + /// @param player The player to get mouse wheel position for. + /// @return The relative mouse wheel position for the specified player. int MouseWheelMovedByPlayer(int player) const { return (IsInMultiplayerMode() && player >= Players::PlayerOne && player < Players::MaxPlayerCount) ? m_NetworkMouseWheelState[player] : m_MouseWheelChange; } - /// /// Return true if there are any mouse button presses at all. - /// - /// Whether any mouse buttons have been pressed at all since last frame. + /// @return Whether any mouse buttons have been pressed at all since last frame. bool AnyMouseButtonPress() const; - /// /// Sets the mouse to be trapped in the middle of the screen so it doesn't go out and click on other windows etc. /// This is usually used when the cursor is invisible and only relative mouse movements are used. - /// - /// Whether to trap the mouse or not. - /// + /// @param trap Whether to trap the mouse or not. + /// @param whichPlayer /// Which player is trying to control the mouse. /// Only the player with actual control over the mouse will affect its trapping here. -1 means change mouse trapping regardless of player. - /// void TrapMousePos(bool trap = true, int whichPlayer = -1); - /// /// Forces the mouse within a box on the screen. - /// - /// X value of the top left corner of the screen box to keep the mouse within, relative to the top left corner of the player's screen. - /// Y value of the top left corner of the screen box to keep the mouse within, relative to the top left corner of the player's screen. - /// The width of the box. - /// The height of the box. - /// Which player is trying to control the mouse. Only the player with actual control over the mouse will be affected. -1 means do it regardless of player. + /// @param x X value of the top left corner of the screen box to keep the mouse within, relative to the top left corner of the player's screen. + /// @param y Y value of the top left corner of the screen box to keep the mouse within, relative to the top left corner of the player's screen. + /// @param width The width of the box. + /// @param height The height of the box. + /// @param whichPlayer Which player is trying to control the mouse. Only the player with actual control over the mouse will be affected. -1 means do it regardless of player. void ForceMouseWithinBox(int x, int y, int width, int height, int whichPlayer = Players::NoPlayer) const; #pragma endregion #pragma region Joystick Handling - /// /// Gets the number of active joysticks. - /// - /// The number of active joysticks. + /// @return The number of active joysticks. int GetJoystickCount() const { return (m_NumJoysticks > Players::MaxPlayerCount) ? Players::MaxPlayerCount : m_NumJoysticks; } - /// /// Gets the index number of a joystick from InputDevice. Basically just subtract 2 from the passed in value because the Allegro joystick indices are 0-3 and ours are 2-5. - /// - /// The InputDevice to get index from. - /// The corrected index. A non-joystick device will result in an out of range value returned which will not affect any active joysticks. + /// @param device The InputDevice to get index from. + /// @return The corrected index. A non-joystick device will result in an out of range value returned which will not affect any active joysticks. int GetJoystickIndex(InputDevice device) const { return (device >= InputDevice::DEVICE_GAMEPAD_1 && device < InputDevice::DEVICE_COUNT) ? device - InputDevice::DEVICE_GAMEPAD_1 : InputDevice::DEVICE_COUNT; } - /// /// Gets the number of axes of the specified joystick. - /// - /// Joystick to check. - /// The number of axes of the joystick. + /// @param whichJoy Joystick to check. + /// @return The number of axes of the joystick. int GetJoystickAxisCount(int whichJoy) const; - /// /// Gets whether the specified joystick is active. The joystick number does not correspond to the player number. - /// - /// Joystick to check for. - /// Whether the specified joystick is active. + /// @param joystickNumber Joystick to check for. + /// @return Whether the specified joystick is active. bool JoystickActive(int joystickNumber) const { return joystickNumber >= Players::PlayerOne && joystickNumber < Players::MaxPlayerCount && s_PrevJoystickStates[joystickNumber].m_JoystickID != -1; } - /// /// Gets whether a joystick button is being held down right now. - /// - /// Which joystick to check for. - /// Which joystick button to check for. - /// Whether the joystick button is held or not. + /// @param whichJoy Which joystick to check for. + /// @param whichButton Which joystick button to check for. + /// @return Whether the joystick button is held or not. bool JoyButtonHeld(int whichJoy, int whichButton) const { return GetJoystickButtonState(whichJoy, whichButton, InputState::Held); } - /// /// Shows the first joystick button which is currently down. - /// - /// Which joystick to check for. - /// The first button in the sequence of button enumerations that is held at the time of calling this. JOY_NONE means none. + /// @param whichJoy Which joystick to check for. + /// @return The first button in the sequence of button enumerations that is held at the time of calling this. JOY_NONE means none. int WhichJoyButtonHeld(int whichJoy) const; - /// /// Gets whether a joystick button was pressed between the last update and the one previous to it. - /// - /// Which joystick to check for. - /// Which joystick button to check for. - /// Whether the joystick button is pressed or not. + /// @param whichJoy Which joystick to check for. + /// @param whichButton Which joystick button to check for. + /// @return Whether the joystick button is pressed or not. bool JoyButtonPressed(int whichJoy, int whichButton) const { return GetJoystickButtonState(whichJoy, whichButton, InputState::Pressed); } - /// /// Shows the first joystick button which was pressed down since last frame. - /// - /// Which joystick to check for. - /// The first button in the sequence of button enumerations that is pressed since the previous frame. JOY_NONE means none. + /// @param whichJoy Which joystick to check for. + /// @return The first button in the sequence of button enumerations that is pressed since the previous frame. JOY_NONE means none. int WhichJoyButtonPressed(int whichJoy) const; - /// /// Gets whether a joystick button was released between the last update and the one previous to it. - /// - /// Which joystick to check for. - /// Which joystick button to check for. - /// Whether the joystick button is released or not. + /// @param whichJoy Which joystick to check for. + /// @param whichButton Which joystick button to check for. + /// @return Whether the joystick button is released or not. bool JoyButtonReleased(int whichJoy, int whichButton) const { return GetJoystickButtonState(whichJoy, whichButton, InputState::Released); } - /// /// Gets whether a joystick axis is being held down in a specific direction right now. Two adjacent directions can be held down to produce diagonals. - /// - /// Which joystick to check for. - /// Which joystick stick axis to check for. - /// Which direction to check for. - /// Whether the stick axis is held in the specified direction or not. + /// @param whichJoy Which joystick to check for. + /// @param whichAxis Which joystick stick axis to check for. + /// @param whichDir Which direction to check for. + /// @return Whether the stick axis is held in the specified direction or not. bool JoyDirectionHeld(int whichJoy, int whichAxis, int whichDir) const { return GetJoystickDirectionState(whichJoy, whichAxis, whichDir, InputState::Held); } - /// /// Gets whether a joystick axis direction was pressed between the last update and the one previous to it. - /// - /// Which joystick to check for. - /// Which joystick stick axis to check for. - /// Which direction to check for. - /// Whether the stick axis is pressed or not. + /// @param whichJoy Which joystick to check for. + /// @param whichAxis Which joystick stick axis to check for. + /// @param whichDir Which direction to check for. + /// @return Whether the stick axis is pressed or not. bool JoyDirectionPressed(int whichJoy, int whichAxis, int whichDir) const { return GetJoystickDirectionState(whichJoy, whichAxis, whichDir, InputState::Pressed); } - /// /// Gets whether a joystick axis direction was released between the last update and the one previous to it. - /// - /// Which joystick to check for. - /// Which joystick stick axis to check for. - /// Which direction to check for. - /// Whether the stick axis is released or not. + /// @param whichJoy Which joystick to check for. + /// @param whichAxis Which joystick stick axis to check for. + /// @param whichDir Which direction to check for. + /// @return Whether the stick axis is released or not. bool JoyDirectionReleased(int whichJoy, int whichAxis, int whichDir) const { return GetJoystickDirectionState(whichJoy, whichAxis, whichDir, InputState::Released); } - /// /// Gets the normalized value of a certain joystick's stick's axis. - /// - /// Which joystick to check for. - /// Which joystick stick axis to check for. - /// The analog axis value ranging between -1.0 to 1.0, or 0.0 to 1.0 if it's a throttle type control. + /// @param whichJoy Which joystick to check for. + /// @param whichAxis Which joystick stick axis to check for. + /// @return The analog axis value ranging between -1.0 to 1.0, or 0.0 to 1.0 if it's a throttle type control. float AnalogAxisValue(int whichJoy = 0, int whichAxis = 0) const; - /// /// Gets whether there is any joystick input at all, buttons or D-pad. - /// - /// Whether to check specifically for presses since last frame. - /// Whether any buttons of pads are pressed at all or since the last frame. + /// @param checkForPresses Whether to check specifically for presses since last frame. + /// @return Whether any buttons of pads are pressed at all or since the last frame. bool AnyJoyInput(bool checkForPresses = false) const; - /// /// Return true if there are any joystick presses at all, buttons or D-pad. - /// - /// Whether any buttons or pads have been pressed at all since last frame. + /// @return Whether any buttons or pads have been pressed at all since last frame. bool AnyJoyPress() const { return AnyJoyInput(true); } - /// /// Gets whether there are any joystick button presses at all, but not D-pad input, for a specific joystick. - /// - /// Which joystick to check for. - /// Whether any joystick buttons have been pressed at all since last frame, of a specific joystick. + /// @param whichJoy Which joystick to check for. + /// @return Whether any joystick buttons have been pressed at all since last frame, of a specific joystick. bool AnyJoyButtonPress(int whichJoy) const; #pragma endregion #pragma region Network Handling - /// /// Returns true if manager is in multiplayer mode. - /// - /// True if in multiplayer mode. + /// @return True if in multiplayer mode. bool IsInMultiplayerMode() const { return m_OverrideInput; } - /// /// Sets the multiplayer mode flag. - /// - /// Whether this manager should operate in multiplayer mode. + /// @param value Whether this manager should operate in multiplayer mode. void SetMultiplayerMode(bool value) { m_OverrideInput = value; } - /// /// Gets the position of the mouse for a player during network multiplayer. - /// - /// The player to get for. - /// The position of the mouse for the specified player + /// @param player The player to get for. + /// @return The position of the mouse for the specified player Vector GetNetworkAccumulatedRawMouseMovement(int player); - /// /// Sets the position of the mouse for a player during network multiplayer. - /// - /// The player to set for. - /// The new position of the mouse. + /// @param player The player to set for. + /// @param input The new position of the mouse. void SetNetworkMouseMovement(int player, const Vector& input) { m_NetworkAccumulatedRawMouseMovement[player] += input; } - /// /// Sets whether an input element is held by a player during network multiplayer. - /// - /// Which player to set for. - /// Which input element to set for. - /// The new state of the input element. True or false. + /// @param player Which player to set for. + /// @param element Which input element to set for. + /// @param state The new state of the input element. True or false. void SetNetworkInputElementState(int player, int element, bool state); - /// /// Sets whether a mouse button is held by a player during network multiplayer. - /// - /// Which player to set for. - /// Which mouse button to set for. - /// The new state of the mouse button. True or false. + /// @param player Which player to set for. + /// @param whichButton Which mouse button to set for. + /// @param state The new state of the mouse button. True or false. void SetNetworkMouseButtonHeldState(int player, int whichButton, bool state) { SetNetworkMouseButtonState(player, whichButton, InputState::Held, state); } - /// /// Sets whether a mouse button is pressed by a player during network multiplayer. - /// - /// Which player to set for. - /// Which mouse button to set for. - /// The new state of the mouse button. True or false. + /// @param player Which player to set for. + /// @param whichButton Which mouse button to set for. + /// @param state The new state of the mouse button. True or false. void SetNetworkMouseButtonPressedState(int player, int whichButton, bool state) { SetNetworkMouseButtonState(player, whichButton, InputState::Pressed, state); } - /// /// Sets whether a mouse button is released by a player during network multiplayer. - /// - /// Which player to set for. - /// Which mouse button to set for. - /// The new state of the mouse button. True or false. + /// @param player Which player to set for. + /// @param whichButton Which mouse button to set for. + /// @param state The new state of the mouse button. True or false. void SetNetworkMouseButtonReleasedState(int player, int whichButton, bool state) { SetNetworkMouseButtonState(player, whichButton, InputState::Released, state); } - /// /// Sets the state of the mouse wheel for a player during network multiplayer. - /// - /// The player to set for. - /// The new state of the mouse wheel. + /// @param player The player to set for. + /// @param state The new state of the mouse wheel. void SetNetworkMouseWheelState(int player, int state) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_NetworkMouseWheelState[player] += state; } } - /// /// Gets whether the specified input element is pressed during network multiplayer. - /// - /// The input element to check for. - /// Whether the specified input element is pressed or not. + /// @param element The input element to check for. + /// @return Whether the specified input element is pressed or not. bool NetworkAccumulatedElementPressed(int element) const { return NetworkAccumulatedElementState(element, InputState::Pressed); } - /// /// Gets whether the specified input element is released during network multiplayer. - /// - /// The input element to check for. - /// Whether the specified input element is released or not. + /// @param element The input element to check for. + /// @return Whether the specified input element is released or not. bool NetworkAccumulatedElementReleased(int element) const { return NetworkAccumulatedElementState(element, InputState::Released); } - /// /// Clears all the accumulated input element states. - /// void ClearNetworkAccumulatedStates(); #pragma endregion private: - /// /// Enumeration for the different states an input element or button can be in. - /// enum InputState { Held, Pressed, @@ -701,10 +522,8 @@ namespace RTE { bool m_DisableKeyboard; //!< Temporarily disable all keyboard input reading. bool m_DisableMouseMoving; //!< Temporary disable for positioning the mouse, for when the game window is not in focus. - /// /// This is set when focus is switched back to the game window and will cause the m_DisableMouseMoving to switch to false when the mouse button is RELEASED. /// This is to avoid having the window fly away because the user clicked the title bar. - /// bool m_PrepareToEnableMouseMoving; bool m_NetworkAccumulatedElementState[InputElements::INPUT_COUNT][InputState::InputStateCount]; //!< The state of a client input element during network multiplayer. @@ -725,146 +544,108 @@ namespace RTE { static constexpr int c_AxisDigitalReleasedThreshold = c_AxisDigitalPressedThreshold - 100; //!< Digital Axis release threshold, to debounce values. #pragma region Mouse Handling - /// /// Forces the mouse within a specific player's screen area. /// Player 1 will always be in the upper-left corner, Player 3 will always be in the lower-left corner, Player 4 will always be in the lower-right quadrant. /// Player 2 will either be in the lower-left corner or the upper-right corner depending on vertical/horizontal splitting. - /// - /// Which player's screen to constrain the mouse to. Only the player with actual control over the mouse will be affected. + /// @param whichPlayer Which player's screen to constrain the mouse to. Only the player with actual control over the mouse will be affected. void ForceMouseWithinPlayerScreen(bool force, int whichPlayer); #pragma endregion #pragma region Input State Handling - /// /// Gets whether an input element is in the specified state. - /// - /// Which player to check for. See Players enumeration. - /// Which element to check for. See InputElements enumeration. - /// Which state to check for. See InputState enumeration. - /// Whether the element is in the specified state or not. + /// @param whichPlayer Which player to check for. See Players enumeration. + /// @param whichElement Which element to check for. See InputElements enumeration. + /// @param whichState Which state to check for. See InputState enumeration. + /// @return Whether the element is in the specified state or not. bool GetInputElementState(int whichPlayer, int whichElement, InputState whichState); bool GetNetworkInputElementState(int whichPlayer, int whichElement, InputState whichState); - /// /// Gets whether any generic button with the menu cursor is in the specified state. - /// - /// Which menu button to check for. See MenuButtons enumeration. - /// Which state to check for. See InputState enumeration. - /// Whether the menu button is in the specified state or not. + /// @param whichButton Which menu button to check for. See MenuButtons enumeration. + /// @param whichState Which state to check for. See InputState enumeration. + /// @return Whether the menu button is in the specified state or not. bool GetMenuButtonState(int whichButton, InputState whichState); - /// /// Gets whether a keyboard key is in the specified state. - /// - /// A scancode to test. See SDL_Scancode enumeration. - /// Which state to check for. See InputState enumeration. - /// Whether the keyboard key is in the specified state or not. + /// @param scancodeToTest A scancode to test. See SDL_Scancode enumeration. + /// @param whichState Which state to check for. See InputState enumeration. + /// @return Whether the keyboard key is in the specified state or not. bool GetKeyboardButtonState(SDL_Scancode scancodeToTest, InputState whichState) const; - /// /// Gets whether a mouse button is in the specified state. - /// - /// Which player to check for. See Players enumeration. - /// Which mouse button to check for. See MouseButtons enumeration. - /// Which state to check for. See InputState enumeration. - /// Whether the mouse button is in the specified state or not. + /// @param whichPlayer Which player to check for. See Players enumeration. + /// @param whichButton Which mouse button to check for. See MouseButtons enumeration. + /// @param whichState Which state to check for. See InputState enumeration. + /// @return Whether the mouse button is in the specified state or not. bool GetMouseButtonState(int whichPlayer, int whichButton, InputState whichState) const; - /// /// Gets whether a multiplayer mouse button is in the specified state. - /// - /// Which player to check for. See Players enumeration. - /// Which mouse button to check for. See MouseButtons enumeration. - /// Which state to check for. See InputState enumeration. - /// Whether the mouse button is in the specified state or not. + /// @param whichPlayer Which player to check for. See Players enumeration. + /// @param whichButton Which mouse button to check for. See MouseButtons enumeration. + /// @param whichState Which state to check for. See InputState enumeration. + /// @return Whether the mouse button is in the specified state or not. bool GetNetworkMouseButtonState(int whichPlayer, int whichButton, InputState whichState) const; - /// /// Gets whether a joystick button is in the specified state. - /// - /// Which joystick to check for. - /// Which joystick button to check for. See JoyButtons enumeration. - /// Which state to check for. See InputState enumeration. - /// Whether the joystick button is in the specified state or not. + /// @param whichJoy Which joystick to check for. + /// @param whichButton Which joystick button to check for. See JoyButtons enumeration. + /// @param whichState Which state to check for. See InputState enumeration. + /// @return Whether the joystick button is in the specified state or not. bool GetJoystickButtonState(int whichJoy, int whichButton, InputState whichState) const; - /// /// Gets whether a joystick axis direction is in the specified state or not. - /// - /// Which joystick to check for. - /// Which joystick stick axis to check for. - /// Which direction to check for. See JoyDirections enumeration. - /// Which state to check for. See InputState enumeration. - /// Whether the joystick stick axis is in the specified state or not. + /// @param whichJoy Which joystick to check for. + /// @param whichAxis Which joystick stick axis to check for. + /// @param whichDir Which direction to check for. See JoyDirections enumeration. + /// @param whichState Which state to check for. See InputState enumeration. + /// @return Whether the joystick stick axis is in the specified state or not. bool GetJoystickDirectionState(int whichJoy, int whichAxis, int whichDir, InputState whichState) const; - /// /// Sets a mouse button for a player to the specified state during network multiplayer. - /// - /// Which player to set for. See Players enumeration. - /// Which mouse button to set for. See MouseButtons enumeration. - /// Which input state to set. See InputState enumeration. - /// The new state of the specified InputState. True or false. + /// @param player Which player to set for. See Players enumeration. + /// @param whichButton Which mouse button to set for. See MouseButtons enumeration. + /// @param whichState Which input state to set. See InputState enumeration. + /// @param newState The new state of the specified InputState. True or false. void SetNetworkMouseButtonState(int player, int whichButton, InputState whichState, bool newState); - /// /// Gets whether an input element is in the specified state during network multiplayer. - /// - /// Which element to check for. See InputElements enumeration. - /// Which state to check for. See InputState enumeration. - /// Whether the element is in the specified state or not. + /// @param element Which element to check for. See InputElements enumeration. + /// @param whichState Which state to check for. See InputState enumeration. + /// @return Whether the element is in the specified state or not. bool NetworkAccumulatedElementState(int element, InputState whichState) const { return (element < InputElements::INPUT_L_UP || element >= InputElements::INPUT_COUNT) ? false : m_NetworkAccumulatedElementState[element][whichState]; } #pragma endregion #pragma region Update Breakdown - /// /// Capture and handle special key shortcuts and combinations. This is called from Update(). - /// void HandleSpecialInput(); - /// /// Handles the mouse input in network multiplayer. This is called from Update(). - /// void UpdateNetworkMouseMovement(); - /// /// Clear all NetworkServerChanged* arrays. - /// void ClearNetworkChangedState(); - /// /// Handles the mouse input. This is called from Update(). - /// void UpdateMouseInput(); - /// /// Handles a joystick axis input. This is called from Update(). - /// void UpdateJoystickAxis(std::vector::iterator device, int axis, int newValue); - /// /// Updates simulated digital joystick axis. This is called from Update(). - /// void UpdateJoystickDigitalAxis(); - /// /// Connect a joystick or gamepad device and add it to the joystick list if a slot is available (up to max player count). - /// - /// The device index (generated by the connected event or a value up to SDL_NumJoysticks()). + /// @param deviceIndex The device index (generated by the connected event or a value up to SDL_NumJoysticks()). void HandleGamepadHotPlug(int deviceIndex); - /// /// Stores all the input events that happened during this update to be compared to in the next update. This is called from Update(). - /// void StoreInputEventsForNextUpdate(); #pragma endregion - /// /// Clears all the member variables of this UInputMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Managers/WindowMan.cpp b/Source/Managers/WindowMan.cpp index c767770663..25fb28391d 100644 --- a/Source/Managers/WindowMan.cpp +++ b/Source/Managers/WindowMan.cpp @@ -30,8 +30,6 @@ namespace RTE { void SDLContextDeleter::operator()(SDL_GLContext context) const { SDL_GL_DeleteContext(context); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::Clear() { m_EventQueue.clear(); m_FocusEventsDispatchedByMovingBetweenWindows = false; @@ -67,22 +65,16 @@ namespace RTE { m_UseMultiDisplays = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::ClearMultiDisplayData() { m_MultiDisplayTextureOffsets.clear(); m_MultiDisplayProjections.clear(); m_MultiDisplayWindows.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - WindowMan::WindowMan() { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - WindowMan::~WindowMan() = default; void WindowMan::Destroy() { @@ -93,8 +85,6 @@ namespace RTE { glDeleteFramebuffers(1, &m_ScreenBufferFBO); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::Initialize() { m_NumDisplays = SDL_GetNumVideoDisplays(); @@ -129,8 +119,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::CreatePrimaryWindow() { std::string windowTitle = "Cortex Command Community Project"; @@ -222,8 +210,6 @@ namespace RTE { TracyGpuContext; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::CreateBackBufferTexture() { glBindTexture(GL_TEXTURE_2D, m_BackBuffer32Texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_ResX, m_ResY, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); @@ -235,8 +221,6 @@ namespace RTE { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int WindowMan::GetWindowResX() { int w, h; SDL_GL_GetDrawableSize(m_PrimaryWindow.get(), &w, &h); @@ -263,8 +247,6 @@ namespace RTE { SDL_GL_SetSwapInterval(sdlEnableVSync); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::UpdatePrimaryDisplayInfo() { m_PrimaryWindowDisplayIndex = SDL_GetWindowDisplayIndex(m_PrimaryWindow.get()); @@ -302,8 +284,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::MapDisplays(bool updatePrimaryDisplayInfo) { auto setSingleDisplayMode = [this](const std::string& errorMsg = "") { m_MaxResX = m_PrimaryWindowDisplayWidth; @@ -403,8 +383,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::ValidateResolution(int& resX, int& resY, float& resMultiplier) const { if (resX < c_MinResX || resY < c_MinResY) { resX = c_MinResX; @@ -438,8 +416,6 @@ namespace RTE { m_PrimaryWindowViewport = std::make_unique(offsetX, windowH - offsetY - height, width, height); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::AttemptToRevertToPreviousResolution(bool revertToDefaults) { auto setDefaultResSettings = [this]() { m_ResX = c_DefaultResX; @@ -472,8 +448,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::ChangeResolution(int newResX, int newResY, float newResMultiplier, bool fullscreen, bool displaysAlreadyMapped) { if (m_ResX == newResX && m_ResY == newResY && glm::epsilonEqual(m_ResMultiplier, newResMultiplier, glm::epsilon()) && m_Fullscreen == fullscreen) { @@ -538,8 +512,6 @@ namespace RTE { g_ConsoleMan.PrintString("SYSTEM: " + std::string(!recoveredToPreviousSettings ? "Switched to different resolution." : "Failed to switch to different resolution. Reverted to previous settings.")); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::ToggleFullscreen() { bool fullscreen = !m_Fullscreen; @@ -569,8 +541,6 @@ namespace RTE { SetViewportLetterboxed(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool WindowMan::ChangeResolutionToMultiDisplayFullscreen(float resMultiplier) { if (!m_CanMultiDisplayFullscreen) { return false; @@ -628,8 +598,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::DisplaySwitchIn(SDL_Window* windowThatShouldTakeInputFocus) const { g_UInputMan.DisableMouseMoving(false); g_UInputMan.DisableKeys(false); @@ -647,8 +615,6 @@ namespace RTE { SDL_ShowCursor(SDL_DISABLE); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::DisplaySwitchOut() const { g_UInputMan.DisableMouseMoving(true); g_UInputMan.DisableKeys(true); @@ -658,8 +624,6 @@ namespace RTE { SDL_SetCursor(nullptr); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::QueueWindowEvent(const SDL_Event& windowEvent) { if (g_UInputMan.IsInMultiplayerMode()) { return; @@ -667,8 +631,6 @@ namespace RTE { m_EventQueue.emplace_back(windowEvent); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::Update() { // Some bullshit we have to deal with to correctly focus windows in multi-display fullscreen so mouse binding/unbinding works correctly. Not relevant for single window. // This is SDL's fault for not having handling to raise a window so it's top-most without taking focus of it. @@ -722,8 +684,6 @@ namespace RTE { m_EventQueue.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::ClearRenderer() { glBindFramebuffer(GL_FRAMEBUFFER, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -735,8 +695,6 @@ namespace RTE { m_DrawPostProcessBuffer = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void WindowMan::UploadFrame() { TracyGpuZone("Upload Frame"); glDisable(GL_DEPTH_TEST); diff --git a/Source/Managers/WindowMan.h b/Source/Managers/WindowMan.h index 89101548e3..723129120f 100644 --- a/Source/Managers/WindowMan.h +++ b/Source/Managers/WindowMan.h @@ -34,217 +34,147 @@ namespace RTE { void operator()(void* context) const; }; - /// /// The singleton manager over the game window and display of frames. - /// class WindowMan : public Singleton { friend class SettingsMan; public: #pragma region Creation - /// /// Constructor method used to instantiate a WindowMan object in system memory. Initialize() should be called before using the object. - /// WindowMan(); - /// /// Makes the WindowMan object ready for use. - /// void Initialize(); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a WindowMan object before deletion from system memory. - /// ~WindowMan(); - /// /// Clean up GL pointers. - /// void Destroy(); #pragma endregion #pragma region Getters and Setters - /// /// Gets a pointer to the primary game window. OWNERSHIP IS NOT TRANSFERRED! - /// - /// Pointer to the primary game window. + /// @return Pointer to the primary game window. SDL_Window* GetWindow() const { return m_PrimaryWindow.get(); } - /// /// Gets whether any of the game windows is currently in focus. - /// - /// Whether any of the game windows is currently in focus. + /// @return Whether any of the game windows is currently in focus. bool AnyWindowHasFocus() const { return m_AnyWindowHasFocus; } - /// /// Gets the maximum horizontal resolution the game can be resized to. - /// - /// The maximum horizontal resolution the game can be resized to. + /// @return The maximum horizontal resolution the game can be resized to. int GetMaxResX() const { return m_MaxResX; } - /// /// Gets the maximum vertical resolution the game can be resized to. - /// - /// The maximum vertical resolution the game can be resized to. + /// @return The maximum vertical resolution the game can be resized to. int GetMaxResY() const { return m_MaxResY; } - /// /// Gets the horizontal resolution the game is currently sized at. - /// - /// The horizontal resolution the game is currently sized at, in pixels. + /// @return The horizontal resolution the game is currently sized at, in pixels. int GetResX() const { return m_ResX; } - /// /// Gets the vertical resolution the game is currently sized at. - /// - /// The vertical resolution the game is currently sized at, in pixels. + /// @return The vertical resolution the game is currently sized at, in pixels. int GetResY() const { return m_ResY; } - /// /// Gets the horizontal resolution the game window is currently sized at, in pixels. - /// - /// The horizontal resolution the game window is currently sized at, in pixels. + /// @return The horizontal resolution the game window is currently sized at, in pixels. int GetWindowResX(); - /// /// Gets the vertical resolution the game window is currently sized at, in pixels. - /// - /// The vertical resolution the game window is currently sized at, in pixels. + /// @return The vertical resolution the game window is currently sized at, in pixels. int GetWindowResY(); - /// /// Gets how many times the game resolution is currently being multiplied and the backbuffer stretched across for better readability. - /// - /// What multiple the game resolution is currently sized at. + /// @return What multiple the game resolution is currently sized at. float GetResMultiplier() const { return m_ResMultiplier; } - /// /// Gets whether VSync is enabled. - /// - /// Whether VSync is enabled. + /// @return Whether VSync is enabled. bool GetVSyncEnabled() const { return m_EnableVSync; } - /// /// Sets whether VSync is enabled. - /// - /// Whether to enable VSync. + /// @param enable Whether to enable VSync. void SetVSyncEnabled(bool enable); - /// /// Gets whether the game window is currently in fullscreen. - /// - /// Whether the game window is currently in fullscreen. + /// @return Whether the game window is currently in fullscreen. bool IsFullscreen() { return m_Fullscreen; } - /// /// Gets whether the multi-display arrangement should be used or whether only the display the main window is currently positioned at should be used for fullscreen. - /// - /// Whether the multi-display arrangement is used. + /// @return Whether the multi-display arrangement is used. bool GetUseMultiDisplays() const { return m_UseMultiDisplays; } - /// /// Sets whether the multi-display arrangement should be used or whether only the display the main window is currently positioned at should be used for fullscreen. - /// - /// Whether the multi-display arrangement should be used + /// @param use Whether the multi-display arrangement should be used void SetUseMultiDisplays(bool use) { m_UseMultiDisplays = use; } - /// /// Checks whether the current resolution settings fully cover all the available displays. - /// - /// Whether the current resolution settings fully cover all the available displays. + /// @return Whether the current resolution settings fully cover all the available displays. bool FullyCoversAllDisplays() const { return m_NumDisplays > 1 && (m_ResX * m_ResMultiplier == m_MaxResX) && (m_ResY * m_ResMultiplier == m_MaxResY); } - /// /// Gets the absolute left-most position in the OS display arrangement. Used for correcting mouse position in multi-display fullscreen when the left-most display is not primary. - /// - /// The absolute left-most position in the OS display arrangement. + /// @return The absolute left-most position in the OS display arrangement. int GetDisplayArrangementAbsOffsetX() const { return std::abs(m_DisplayArrangementLeftMostOffset); } - /// /// Gets the absolute top-most position in the OS display arrangement. Used for correcting mouse position in multi-display fullscreen when the left-most display is not primary. - /// - /// The absolute top-most position in the OS display arrangement. + /// @return The absolute top-most position in the OS display arrangement. int GetDisplayArrangementAbsOffsetY() const { return std::abs(m_DisplayArrangementTopMostOffset); } - /// /// Get the screen buffer texture. - /// - /// The screen buffer texture. + /// @return The screen buffer texture. GLuint GetScreenBufferTexture() const { return m_ScreenBufferTexture; } #pragma endregion #pragma region Resolution Change Handling - /// /// Attempts to figure our what the hell the OS display arrangement is and what are the resolution capabilities for single or multi-display fullscreen. - /// - /// Whether to update the stored info of the display the primary window is currently positioned at. + /// @param updatePrimaryDisplayInfo Whether to update the stored info of the display the primary window is currently positioned at. void MapDisplays(bool updatePrimaryDisplayInfo = true); - /// /// Gets the horizontal resolution of the display the primary game window is currently positioned at. - /// - /// The horizontal resolution of the display the primary game window is currently positioned at. + /// @return The horizontal resolution of the display the primary game window is currently positioned at. int GetPrimaryWindowDisplayWidth() const { return m_PrimaryWindowDisplayWidth; } - /// /// Gets the vertical resolution of the display the primary game window is currently positioned at. - /// - /// The vertical resolution of the display the primary game window is currently positioned at. + /// @return The vertical resolution of the display the primary game window is currently positioned at. int GetPrimaryWindowDisplayHeight() const { return m_PrimaryWindowDisplayHeight; } - /// /// Gets whether the game resolution was changed. - /// - /// Whether the game resolution was changed. + /// @return Whether the game resolution was changed. bool ResolutionChanged() const { return m_ResolutionChanged; } - /// /// Switches the game resolution to the specified dimensions. - /// - /// New width to resize to. - /// New height to resize to. - /// Whether the new resolution should be upscaled. - /// Whether to skip mapping displays because they were already mapped elsewhere. + /// @param newResX New width to resize to. + /// @param newResY New height to resize to. + /// @param upscaled Whether the new resolution should be upscaled. + /// @param displaysAlreadyMapped Whether to skip mapping displays because they were already mapped elsewhere. void ChangeResolution(int newResX, int newResY, float newResMultiplier = 1.0f, bool fullscreen = false, bool displaysAlreadyMapped = false); - /// /// Toggles between windowed and fullscreen mode (single display). - /// void ToggleFullscreen(); - /// /// Completes the resolution change by resetting the flag. - /// void CompleteResolutionChange() { m_ResolutionChanged = false; } #pragma endregion #pragma region Concrete Methods - /// /// Adds an SDL_Event to the Event queue for processing on Update. - /// - /// The SDL window event to queue. + /// @param windowEvent The SDL window event to queue. void QueueWindowEvent(const SDL_Event& windowEvent); - /// /// Updates the state of this WindowMan. - /// void Update(); - /// /// Clears the primary renderer, or all the renderers if in multi-display fullscreen. - /// void ClearRenderer(); - /// /// Set this Frame to draw the game. To be set before UploadFrame. Resets on ClearRenderer. - /// void DrawPostProcessBuffer() { m_DrawPostProcessBuffer = true; } - /// /// Copies the BackBuffer32 content to GPU and shows it on screen. - /// void UploadFrame(); #pragma endregion @@ -301,93 +231,67 @@ namespace RTE { bool m_DrawPostProcessBuffer; //!< Whether to draw the PostProcessBuffer while not in Activity. Resets on Frame Clear. #pragma region Initialize Breakdown - /// /// Creates the main game window. - /// void CreatePrimaryWindow(); - /// /// Initializes all opengl objects (textures, vbo, vao, and fbo). - /// void InitializeOpenGL(); - /// /// Creates the main game window renderer's drawing surface. - /// void CreateBackBufferTexture(); #pragma endregion #pragma region Resolution Handling void SetViewportLetterboxed(); - /// /// Updates the stored info of the display the primary window is currently positioned at. - /// void UpdatePrimaryDisplayInfo(); - /// /// Gets the maximum available window bounds for a decorated window on the specified display. /// May not provide accurate results if the window is in fullscreen or has just been created. - /// - /// The display to get the bounds for. - /// The maximum available window bounds for a decorated window on the specified display. + /// @param display The display to get the bounds for. + /// @return The maximum available window bounds for a decorated window on the specified display. SDL_Rect GetUsableBoundsWithDecorations(int display); - /// /// Calculates whether the given resolution and multiplier would create a maximized window. - /// - /// Game window width to check. - /// Game window height to check. - /// Game window resolution multiplier to check. - /// Whether the given resolution and multiplier create a maximized window. + /// @param resX Game window width to check. + /// @param resY Game window height to check. + /// @param resMultiplier Game window resolution multiplier to check. + /// @return Whether the given resolution and multiplier create a maximized window. bool IsResolutionMaximized(int resX, int resY, float resMultiplier); - /// /// Checks whether the passed in resolution settings make sense. If not, overrides them to prevent crashes or unexpected behavior. - /// - /// Game window width to check. - /// Game window height to check. - /// Game window resolution multiplier to check. + /// @param resX Game window width to check. + /// @param resY Game window height to check. + /// @param resMultiplier Game window resolution multiplier to check. void ValidateResolution(int& resX, int& resY, float& resMultiplier) const; - /// /// Attempts to revert to the previous resolution settings if the new ones failed for whatever reason. Will recursively attempt to revert to defaults if previous settings fail as well. - /// - /// Whether to attempt to revert to defaults. Will be set by this. + /// @param revertToDefaults Whether to attempt to revert to defaults. Will be set by this. void AttemptToRevertToPreviousResolution(bool revertToDefaults = false); #pragma endregion #pragma region Multi-Display Handling - /// /// Clears all the multi-display data, resetting the game to a single-window-single-display state. - /// void ClearMultiDisplayData(); - /// /// Resize the window to enable fullscreen on multiple displays, using the arrangement info gathered during display mapping. - /// - /// Requested resolution multiplier. - /// Whether all displays were created successfully. + /// @param resMultiplier Requested resolution multiplier. + /// @return Whether all displays were created successfully. bool ChangeResolutionToMultiDisplayFullscreen(float resMultiplier); #pragma endregion #pragma region Display Switch Handling - /// /// Handles focus gain when switching back to the game window. - /// - /// The window that should take focus of input after all the windows are raised. This is only relevant in multi-display fullscreen. + /// @param windowThatShouldTakeInputFocus The window that should take focus of input after all the windows are raised. This is only relevant in multi-display fullscreen. void DisplaySwitchIn(SDL_Window* windowThatShouldTakeInputFocus) const; - /// /// Handles focus loss when switching away from the game window. /// Will temporarily disable positioning of the mouse so that when focus is switched back to the game window, the game window won't fly away because the user clicked the title bar of the window. - /// void DisplaySwitchOut() const; #pragma endregion - /// /// Clears all the member variables of this WindowMan, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/AreaEditorGUI.cpp b/Source/Menus/AreaEditorGUI.cpp index 3bbbeafc05..5f16678f31 100644 --- a/Source/Menus/AreaEditorGUI.cpp +++ b/Source/Menus/AreaEditorGUI.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AreaEditorGUI.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Implementation file for the AreaEditorGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datArealms.com -// http://www.datArealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "AreaEditorGUI.h" #include "CameraMan.h" @@ -30,12 +18,6 @@ using namespace RTE; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AreaEditorGUI, effectively -// resetting the members of this abstraction level only. - void AreaEditorGUI::Clear() { m_pController = 0; m_FullFeatured = false; @@ -57,11 +39,6 @@ void AreaEditorGUI::Clear() { m_pBoxToBlink = 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AreaEditorGUI Area ready for use. - int AreaEditorGUI::Create(Controller* pController, bool fullFeatured, int whichModuleSpace) { RTEAssert(pController, "No controller sent to AreaEditorGUI on creation!"); m_pController = pController; @@ -96,55 +73,26 @@ int AreaEditorGUI::Create(Controller* pController, bool fullFeatured, int whichM return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AreaEditorGUI Area. - void AreaEditorGUI::Destroy() { delete m_pPicker; Clear(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! - void AreaEditorGUI::SetController(Controller* pController) { m_pController = pController; m_PieMenu->SetMenuController(pController); m_pPicker->SetController(pController); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. - void AreaEditorGUI::SetPosOnScreen(int newPosX, int newPosY) { m_pPicker->SetPosOnScreen(newPosX, newPosY); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActivatedPieSlice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets any Pie menu slice command activated last update. - PieSlice::SliceType AreaEditorGUI::GetActivatedPieSlice() const { return m_PieMenu->GetPieCommand(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCurrentArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the currently selected Area of this Editor. Ownership IS NOT -// transferred! - void AreaEditorGUI::SetCurrentArea(Scene::Area* pArea) { if (!pArea) return; @@ -163,21 +111,10 @@ void AreaEditorGUI::SetCurrentArea(Scene::Area* pArea) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePickerList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the list that the GUI's Area picker has, from the current -// scene state. - void AreaEditorGUI::UpdatePickerList(std::string selectAreaName) { m_pPicker->UpdateAreasList(selectAreaName); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame - void AreaEditorGUI::Update() { // Update the user controller // m_pController->Update(); @@ -524,11 +461,6 @@ void AreaEditorGUI::Update() { g_CameraMan.SetScrollTarget(m_CursorPos, 0.3, g_ActivityMan.GetActivity()->ScreenOfPlayer(m_pController->GetPlayer())); } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu - void AreaEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { // Done or can't, so don't draw the UI if (!m_pCurrentArea || m_EditorGUIMode == DONEEDITING) diff --git a/Source/Menus/AreaEditorGUI.h b/Source/Menus/AreaEditorGUI.h index 169fb0275d..0263f1fbd2 100644 --- a/Source/Menus/AreaEditorGUI.h +++ b/Source/Menus/AreaEditorGUI.h @@ -1,18 +1,11 @@ #ifndef _AREAEDITORGUI_ #define _AREAEDITORGUI_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AreaEditorGUI.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: AreaEditorGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// AreaEditorGUI class +/// @author Daniel Tabar +/// dtabar@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files // #include "FrameMan.h" #include "Scene.h" #include "Timer.h" @@ -28,18 +21,10 @@ namespace RTE { class AreaPickerGUI; class PieMenu; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: AreaEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A full menu system that represents the scene editing GUI for Cortex Command - // Parent(s): None. - // Class history: 7/08/2007 AreaEditorGUI Created. - + /// A full menu system that represents the scene editing GUI for Cortex Command class AreaEditorGUI { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Different modes of this editor enum EditorGUIMode { @@ -53,175 +38,87 @@ namespace RTE { EDITORGUIMODECOUNT }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: AreaEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a AreaEditorGUI Area in system - // memory. Create() should be called before using the Area. - // Arguments: None. - + /// Constructor method used to instantiate a AreaEditorGUI Area in system + /// memory. Create() should be called before using the Area. AreaEditorGUI() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~AreaEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a AreaEditorGUI Area before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a AreaEditorGUI Area before deletion + /// from system memory. ~AreaEditorGUI() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the AreaEditorGUI Area ready for use. - // Arguments: A poitner to a Controller which will control this Menu. Ownership is - // NOT TRANSFERRED! - // Whether the editor should have all the features enabled, like load/save - // and undo capabilities. - // Which module space that this eidtor will be able to pick Areas from. - // -1 means all modules. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the AreaEditorGUI Area ready for use. + /// @param pController A poitner to a Controller which will control this Menu. Ownership is + /// @param fullFeatured NOT TRANSFERRED! (default: false) + /// @param whichModuleSpace Whether the editor should have all the features enabled, like load/save (default: -1) + /// and undo capabilities. + /// Which module space that this eidtor will be able to pick Areas from. + /// -1 means all modules. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(Controller* pController, bool fullFeatured = false, int whichModuleSpace = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire AreaEditorGUI, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire AreaEditorGUI, including its inherited members, to + /// their default settings or values. void Reset() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the AreaEditorGUI Area. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the AreaEditorGUI Area. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetController - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the controller used by this. The ownership of the controller is - // NOT transferred! - // Arguments: The new controller for this menu. Ownership is NOT transferred - // Return value: None. - + /// Sets the controller used by this. The ownership of the controller is + /// NOT transferred! + /// @param pController The new controller for this menu. Ownership is NOT transferred void SetController(Controller* pController); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPosOnScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets where on the screen that this GUI is being drawn to. If upper - // left corner, then 0, 0. This will affect the way the mouse is positioned - // etc. - // Arguments: The new screen position of this entire GUI. - + /// Sets where on the screen that this GUI is being drawn to. If upper + /// left corner, then 0, 0. This will affect the way the mouse is positioned + /// etc. + /// @param newPosX The new screen position of this entire GUI. void SetPosOnScreen(int newPosX, int newPosY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCursorPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the absolute scene coordinates of the cursor of this Editor. - // Arguments: The new cursor position in absolute scene units. - // Return value: None. - + /// Sets the absolute scene coordinates of the cursor of this Editor. + /// @param newCursorPos The new cursor position in absolute scene units. void SetCursorPos(const Vector& newCursorPos) { m_CursorPos = newCursorPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActivatedPieSlice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets any Pie menu slice command activated last update. - // Arguments: None. - // Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. - + /// Gets any Pie menu slice command activated last update. + /// @return The enum'd int of any slice activated. See the PieSlice::SliceType enum. PieSlice::SliceType GetActivatedPieSlice() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCurrentArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the currently selected Area of this Editor. Ownership IS NOT - // transferred! - // Arguments: The new area for this to work with, if any. OWNERSHIP IS NOT TRANSFERRED! - // Return value: None. - + /// Sets the currently selected Area of this Editor. Ownership IS NOT + /// transferred! + /// @param pArea The new area for this to work with, if any. OWNERSHIP IS NOT TRANSFERRED! void SetCurrentArea(Scene::Area* pArea); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the currently held Area in the cursor of this Editor. Ownership - // IS NOT transferred! - // Arguments: None. - // Return value: The currently held Area, if any. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the currently held Area in the cursor of this Editor. Ownership + /// IS NOT transferred! + /// @return The currently held Area, if any. OWNERSHIP IS NOT TRANSFERRED! Scene::Area* GetCurrentArea() { return m_pCurrentArea; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EditMade - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether an edit on the scene was made in the last Update. - // Arguments: None. - // Return value: Whether any edit was made. - + /// Shows whether an edit on the scene was made in the last Update. + /// @return Whether any edit was made. bool EditMade() const { return m_EditMade; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePickerList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the list that the GUI's Area picker has, from the current - // scene state. - // Arguments: The name of the Area to leave selected after the list is updated. - // Return value: None. - + /// Updates the list that the GUI's Area picker has, from the current + /// scene state. + /// @param selectAreaName The name of the Area to leave selected after the list is updated. (default: "") void UpdatePickerList(std::string selectAreaName = ""); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Menu each frame - // Arguments: None. - // Return value: None. - + /// Updates the state of this Menu each frame void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the editor - // Arguments: The bitmap to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws the editor + /// @param pTargetBitmap The bitmap to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: enum BlinkMode { NOBLINK = 0, @@ -269,18 +166,10 @@ namespace RTE { // Currently placed scene Area to make blink when drawing it. NOT OWNED. const Box* m_pBoxToBlink; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this AreaEditorGUI, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this AreaEditorGUI, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/AreaPickerGUI.cpp b/Source/Menus/AreaPickerGUI.cpp index 808da95add..699168bedc 100644 --- a/Source/Menus/AreaPickerGUI.cpp +++ b/Source/Menus/AreaPickerGUI.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AreaPickerGUI.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Implementation file for the AreaPickerGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "AreaPickerGUI.h" #include "CameraMan.h" @@ -38,12 +26,6 @@ using namespace RTE; BITMAP* RTE::AreaPickerGUI::s_pCursor = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AreaPickerGUI, effectively -// resetting the members of this abstraction level only. - void AreaPickerGUI::Clear() { m_pController = 0; m_pGUIScreen = 0; @@ -63,11 +45,6 @@ void AreaPickerGUI::Clear() { m_CursorPos.Reset(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AreaPickerGUI area ready for use. - int AreaPickerGUI::Create(Controller* pController, std::string onlyOfType) { RTEAssert(pController, "No controller sent to AreaPickerGUI on creation!"); m_pController = pController; @@ -131,11 +108,6 @@ int AreaPickerGUI::Create(Controller* pController, std::string onlyOfType) { return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AreaPickerGUI area. - void AreaPickerGUI::Destroy() { delete m_pGUIController; delete m_pGUIInput; @@ -144,11 +116,6 @@ void AreaPickerGUI::Destroy() { Clear(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Enables or disables the menu. This will animate it in and out of view. - void AreaPickerGUI::SetEnabled(bool enable) { if (enable && m_PickerEnabled != ENABLED && m_PickerEnabled != ENABLING) { // If we're not split screen horizontally, then stretch out the layout for all the relevant controls @@ -183,22 +150,10 @@ void AreaPickerGUI::SetEnabled(bool enable) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. - void AreaPickerGUI::SetPosOnScreen(int newPosX, int newPosY) { m_pGUIController->SetPosOnScreen(newPosX, newPosY); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetNextArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the next area in the areas list, even if the picker is disabled. - Scene::Area* AreaPickerGUI::GetNextArea() { m_SelectedAreaIndex++; // Loop around @@ -215,11 +170,6 @@ Scene::Area* AreaPickerGUI::GetNextArea() { return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPrevArea -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the prev area in the areas list, even if the picker is disabled. - Scene::Area* AreaPickerGUI::GetPrevArea() { m_SelectedAreaIndex--; // Loop around @@ -236,12 +186,6 @@ Scene::Area* AreaPickerGUI::GetPrevArea() { return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateAreasList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds all areas of a specific type already defined in PresetMan -// to the current Areas list - void AreaPickerGUI::UpdateAreasList(std::string selectAreaName) { m_pAreasList->ClearList(); @@ -267,11 +211,6 @@ void AreaPickerGUI::UpdateAreasList(std::string selectAreaName) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame - void AreaPickerGUI::Update() { // Enable mouse input if the controller allows it m_pGUIController->EnableMouse(m_pController->IsMouseControlled()); @@ -491,11 +430,6 @@ void AreaPickerGUI::Update() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu - void AreaPickerGUI::Draw(BITMAP* drawBitmap) const { AllegroScreen drawScreen(drawBitmap); m_pGUIController->Draw(&drawScreen); diff --git a/Source/Menus/AreaPickerGUI.h b/Source/Menus/AreaPickerGUI.h index ff086ef3a1..759189a2fd 100644 --- a/Source/Menus/AreaPickerGUI.h +++ b/Source/Menus/AreaPickerGUI.h @@ -1,18 +1,11 @@ #ifndef _AREAPICKERGUI_ #define _AREAPICKERGUI_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AreaPickerGUI.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: AreaPickerGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// AreaPickerGUI class +/// @author Daniel Tabar +/// dtabar@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files // #include "FrameMan.h" #include "Timer.h" #include "Controller.h" @@ -31,189 +24,91 @@ namespace RTE { class GUIButton; class GUILabel; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: AreaPickerGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A GUI for picking isntance areas in Cortex Command - // Parent(s): None. - // Class history: 7/16/2007 AreaPickerGUI Created. - + /// A GUI for picking isntance areas in Cortex Command class AreaPickerGUI { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: AreaPickerGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a AreaPickerGUI area in system - // memory. Create() should be called before using the area. - // Arguments: None. - + /// Constructor method used to instantiate a AreaPickerGUI area in system + /// memory. Create() should be called before using the area. AreaPickerGUI() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~AreaPickerGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a AreaPickerGUI area before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a AreaPickerGUI area before deletion + /// from system memory. ~AreaPickerGUI() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the AreaPickerGUI area ready for use. - // Arguments: A poitner to a Controller which will control this Menu. Ownership is - // NOT TRANSFERRED! - // Which lowest common denominator type to be showing. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the AreaPickerGUI area ready for use. + /// @param pController A poitner to a Controller which will control this Menu. Ownership is + /// @param onlyOfType NOT TRANSFERRED! (default: "All") + /// Which lowest common denominator type to be showing. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(Controller* pController, std::string onlyOfType = "All"); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire AreaPickerGUI, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire AreaPickerGUI, including its inherited members, to + /// their default settings or values. void Reset() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the AreaPickerGUI area. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the AreaPickerGUI area. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetController - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the controller used by this. The ownership of the controller is - // NOT transferred! - // Arguments: The new controller for this menu. Ownership is NOT transferred - // Return value: None. - + /// Sets the controller used by this. The ownership of the controller is + /// NOT transferred! + /// @param pController The new controller for this menu. Ownership is NOT transferred void SetController(Controller* pController) { m_pController = pController; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Enables or disables the menu. This will animate it in and out of view. - // Arguments: Whether to enable or disable the menu. - // Return value: None. - + /// Enables or disables the menu. This will animate it in and out of view. + /// @param enable Whether to enable or disable the menu. (default: true) void SetEnabled(bool enable = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the menu is enabled or not. - // Arguments: None. - // Return value: None. - + /// Reports whether the menu is enabled or not. bool IsEnabled() const { return m_PickerEnabled == ENABLED || m_PickerEnabled == ENABLING; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the menu is at all visible or not. - // Arguments: None. - // Return value: None. - + /// Reports whether the menu is at all visible or not. bool IsVisible() const { return m_PickerEnabled != DISABLED; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPosOnScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets where on the screen that this GUI is being drawn to. If upper - // left corner, then 0, 0. This will affect the way the mouse is positioned - // etc. - // Arguments: The new screen position of this entire GUI. - + /// Sets where on the screen that this GUI is being drawn to. If upper + /// left corner, then 0, 0. This will affect the way the mouse is positioned + /// etc. + /// @param newPosX The new screen position of this entire GUI. void SetPosOnScreen(int newPosX, int newPosY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AreaPicked - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether and which Area has been picked by the player. This - // may happen even though the player isn't done with the picker. (ie - // a different area is picked each time the user selects something else - // in the areas list). - // Arguments: None. - // Return value: Whether an area has been picked bt the player. 0 if not. Ownership - // is NOT transferred! - + /// Reports whether and which Area has been picked by the player. This + /// may happen even though the player isn't done with the picker. (ie + /// a different area is picked each time the user selects something else + /// in the areas list). + /// @return Whether an area has been picked bt the player. 0 if not. Ownership + /// is NOT transferred! Scene::Area* AreaPicked() { return m_pPickedArea; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DonePicking - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the user has finished using the picker, and the final - // picked Area is returned. - // Arguments: None. - // Return value: Whether an area has been positively and finally picked bt the player. - // 0 if not. Ownership is NOT transferred! - + /// Reports whether the user has finished using the picker, and the final + /// picked Area is returned. + /// @return Whether an area has been positively and finally picked bt the player. + /// 0 if not. Ownership is NOT transferred! Scene::Area* DonePicking() { return !IsEnabled() && m_pPickedArea ? m_pPickedArea : 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetNextArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the next area in the areas list, even if the picker is disabled. - // Arguments: None. - // Return value: The next area in the picker list, looping around if necessary. - // 0 if no area can be selected. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the next area in the areas list, even if the picker is disabled. + /// @return The next area in the picker list, looping around if necessary. + /// 0 if no area can be selected. OWNERSHIP IS NOT TRANSFERRED! Scene::Area* GetNextArea(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPrevArea - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the prev area in the areas list, even if the picker is disabled. - // Arguments: None. - // Return value: The prev area in the picker list, looping around if necessary. - // 0 if no area can be selected. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the prev area in the areas list, even if the picker is disabled. + /// @return The prev area in the picker list, looping around if necessary. + /// 0 if no area can be selected. OWNERSHIP IS NOT TRANSFERRED! Scene::Area* GetPrevArea(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateAreasList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds all areas of the currently selected group to the Areas list. - // Arguments: The name of the Area to leave selected after the list is updated. - // Return value: None. - + /// Adds all areas of the currently selected group to the Areas list. + /// @param selectAreaName The name of the Area to leave selected after the list is updated. (default: "") void UpdateAreasList(std::string selectAreaName = ""); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Menu each frame - // Arguments: None. - // Return value: None. - + /// Updates the state of this Menu each frame void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the menu - // Arguments: The bitmap to draw on. - // Return value: None. - + /// Draws the menu + /// @param drawBitmap The bitmap to draw on. void Draw(BITMAP* drawBitmap) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: enum PickerEnabled { ENABLING = 0, @@ -261,18 +156,10 @@ namespace RTE { // Screen position of the cursor Vector m_CursorPos; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this AreaPickerGUI, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this AreaPickerGUI, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/AssemblyEditorGUI.cpp b/Source/Menus/AssemblyEditorGUI.cpp index a766194884..a529d6c620 100644 --- a/Source/Menus/AssemblyEditorGUI.cpp +++ b/Source/Menus/AssemblyEditorGUI.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AssemblyEditorGUI.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Implementation file for the AssemblyEditorGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "AssemblyEditorGUI.h" #include "CameraMan.h" @@ -44,12 +32,6 @@ using namespace RTE; BITMAP* AssemblyEditorGUI::s_pValidPathDot = 0; BITMAP* AssemblyEditorGUI::s_pInvalidPathDot = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this AssemblyEditorGUI, effectively -// resetting the members of this abstraction level only. - void AssemblyEditorGUI::Clear() { m_pController = 0; m_FeatureSet = ONLOADEDIT; @@ -84,11 +66,6 @@ void AssemblyEditorGUI::Clear() { m_CurrentAssemblyName.clear(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AssemblyEditorGUI object ready for use. - int AssemblyEditorGUI::Create(Controller* pController, FeatureSets featureSet, int whichModuleSpace, int nativeTechModule, float foreignCostMult) { RTEAssert(pController, "No controller sent to AssemblyEditorGUI on creation!"); m_pController = pController; @@ -152,11 +129,6 @@ int AssemblyEditorGUI::Create(Controller* pController, FeatureSets featureSet, i return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the AssemblyEditorGUI object. - void AssemblyEditorGUI::Destroy() { delete m_pPicker; delete m_pCurrentObject; @@ -165,35 +137,16 @@ void AssemblyEditorGUI::Destroy() { Clear(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! - void AssemblyEditorGUI::SetController(Controller* pController) { m_pController = pController; m_PieMenu->SetMenuController(pController); m_pPicker->SetController(pController); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. - void AssemblyEditorGUI::SetPosOnScreen(int newPosX, int newPosY) { m_pPicker->SetPosOnScreen(newPosX, newPosY); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCurrentObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the new Object to be held at the cursor of this Editor. Ownership -// IS transferred! - bool AssemblyEditorGUI::SetCurrentObject(SceneObject* pNewObject) { if (m_pCurrentObject == pNewObject) return true; @@ -220,31 +173,14 @@ bool AssemblyEditorGUI::SetCurrentObject(SceneObject* pNewObject) { return true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActivatedPieSlice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets any Pie menu slice command activated last update. - PieSlice::SliceType AssemblyEditorGUI::GetActivatedPieSlice() const { return m_PieMenu->GetPieCommand(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule space to be picking objects from. If -1, then -// let the player pick from all loaded modules. - void AssemblyEditorGUI::SetModuleSpace(int moduleSpaceID) { m_pPicker->SetModuleSpace(moduleSpaceID); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetNativeTechModule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule ID should be treated as the native tech of the -// user of this menu. - void AssemblyEditorGUI::SetNativeTechModule(int whichModule) { if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) { m_NativeTechModule = whichModule; @@ -252,35 +188,16 @@ void AssemblyEditorGUI::SetNativeTechModule(int whichModule) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetForeignCostMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the multiplier of the cost of any foreign Tech items. - void AssemblyEditorGUI::SetForeignCostMultiplier(float newMultiplier) { m_ForeignCostMult = newMultiplier; m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TestBrainResidence -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether the resident brain is currently placed into a valid -// location in this scene, based on whether there is a clear path to the -// sky above it. This forces the editor into place brain mode with the -// current resident brain if the current placement is no bueno. It also -// removes the faulty brain from residence in the scene! - bool AssemblyEditorGUI::TestBrainResidence(bool noBrainIsOK) { // Brain is fine, leave it be return false; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame - void AssemblyEditorGUI::Update() { // Update the user controller // m_pController->Update(); @@ -859,11 +776,6 @@ void AssemblyEditorGUI::Update() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu - void AssemblyEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { // Done, so don't draw the UI if (m_EditorGUIMode == DONEEDITING) @@ -937,12 +849,6 @@ void AssemblyEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) con m_PieMenu->Draw(pTargetBitmap, targetPos); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateBrainPath -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the brain path to the current resident brain, if any. If -// there's none, the path is cleared. - bool AssemblyEditorGUI::UpdateBrainPath() { // First see if we have a brain in hand if (m_pCurrentObject && m_pCurrentObject->IsInGroup("Brains")) { diff --git a/Source/Menus/AssemblyEditorGUI.h b/Source/Menus/AssemblyEditorGUI.h index b8e85a6af8..8c92223549 100644 --- a/Source/Menus/AssemblyEditorGUI.h +++ b/Source/Menus/AssemblyEditorGUI.h @@ -1,18 +1,11 @@ #ifndef _ASSEMBLYEDITORGUI_ #define _ASSEMBLYEDITORGUI_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: AssemblyEditorGUI.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: AssemblyEditorGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// AssemblyEditorGUI class +/// @author Daniel Tabar +/// dtabar@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files // #include "FrameMan.h" #include "Timer.h" #include "Vector.h" @@ -29,18 +22,10 @@ namespace RTE { class ObjectPickerGUI; class PieMenu; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: AssemblyEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A full menu system that represents the scene editing GUI for Cortex Command - // Parent(s): None. - // Class history: 7/08/2007 AssemblyEditorGUI Created. - + /// A full menu system that represents the scene editing GUI for Cortex Command class AssemblyEditorGUI { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: enum FeatureSets { ONLOADEDIT = 0 @@ -58,257 +43,130 @@ namespace RTE { EDITORGUIMODECOUNT }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: AssemblyEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a AssemblyEditorGUI object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a AssemblyEditorGUI object in system + /// memory. Create() should be called before using the object. AssemblyEditorGUI() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~AssemblyEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a AssemblyEditorGUI object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a AssemblyEditorGUI object before deletion + /// from system memory. ~AssemblyEditorGUI() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the AssemblyEditorGUI object ready for use. - // Arguments: A poitner to a Controller which will control this Menu. Ownership is - // NOT TRANSFERRED! - // Whether the editor should have all the features enabled, like load/save - // and undo capabilities. - // Which module space that this eidtor will be able to pick objects from. - // -1 means all modules. - // Which Tech module that will be presented as the native one to the player. - // The multiplier of all foreign techs' costs. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the AssemblyEditorGUI object ready for use. + /// @param pController A poitner to a Controller which will control this Menu. Ownership is + /// @param featureSet NOT TRANSFERRED! (default: ONLOADEDIT) + /// @param whichModuleSpace Whether the editor should have all the features enabled, like load/save (default: -1) + /// and undo capabilities. + /// @param nativeTechModule Which module space that this eidtor will be able to pick objects from. (default: 0) + /// -1 means all modules. + /// @param foreignCostMult Which Tech module that will be presented as the native one to the player. (default: 1.0) + /// The multiplier of all foreign techs' costs. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(Controller* pController, FeatureSets featureSet = ONLOADEDIT, int whichModuleSpace = -1, int nativeTechModule = 0, float foreignCostMult = 1.0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire AssemblyEditorGUI, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire AssemblyEditorGUI, including its inherited members, to + /// their default settings or values. void Reset() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the AssemblyEditorGUI object. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the AssemblyEditorGUI object. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetController - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the controller used by this. The ownership of the controller is - // NOT transferred! - // Arguments: The new controller for this menu. Ownership is NOT transferred - // Return value: None. - + /// Sets the controller used by this. The ownership of the controller is + /// NOT transferred! + /// @param pController The new controller for this menu. Ownership is NOT transferred void SetController(Controller* pController); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPosOnScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets where on the screen that this GUI is being drawn to. If upper - // left corner, then 0, 0. This will affect the way the mouse is positioned - // etc. - // Arguments: The new screen position of this entire GUI. - + /// Sets where on the screen that this GUI is being drawn to. If upper + /// left corner, then 0, 0. This will affect the way the mouse is positioned + /// etc. + /// @param newPosX The new screen position of this entire GUI. void SetPosOnScreen(int newPosX, int newPosY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCursorPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the absolute scene coordinates of the cursor of this Editor. - // Arguments: The new cursor position in absolute scene units. - // Return value: None. - + /// Sets the absolute scene coordinates of the cursor of this Editor. + /// @param newCursorPos The new cursor position in absolute scene units. void SetCursorPos(const Vector& newCursorPos) { m_CursorPos = newCursorPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCurrentObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the new Object to be held at the cursor of this Editor. Ownership - // IS transferred! - // Arguments: The new Object to be held by the cursor. Ownership IS transferred! - // Return value: Whether the cursor holds a valid object after setting. - + /// Sets the new Object to be held at the cursor of this Editor. Ownership + /// IS transferred! + /// @param pNewObject The new Object to be held by the cursor. Ownership IS transferred! + /// @return Whether the cursor holds a valid object after setting. bool SetCurrentObject(SceneObject* pNewObject); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActivatedPieSlice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets any Pie menu slice command activated last update. - // Arguments: None. - // Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. - + /// Gets any Pie menu slice command activated last update. + /// @return The enum'd int of any slice activated. See the PieSlice::SliceType enum. PieSlice::SliceType GetActivatedPieSlice() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the currently held Object in the cursor of this Editor. Ownership - // IS NOT transferred! - // Arguments: None. - // Return value: The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the currently held Object in the cursor of this Editor. Ownership + /// IS NOT transferred! + /// @return The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! const SceneObject* GetCurrentObject() const { return m_pCurrentObject; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetModuleSpace - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which DataModule space to be picking objects from. If -1, then - // let the player pick from all loaded modules. - // Arguments: The ID of the module to let the player pick objects from. All official - // modules' objects will alwayws be presented, in addition to the one - // passed in here. - // Return value: None. - + /// Sets which DataModule space to be picking objects from. If -1, then + /// let the player pick from all loaded modules. + /// @param moduleSpaceID The ID of the module to let the player pick objects from. All official (default: -1) + /// modules' objects will alwayws be presented, in addition to the one + /// passed in here. void SetModuleSpace(int moduleSpaceID = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetNativeTechModule - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which DataModule ID should be treated as the native tech of the - // user of this menu. - // Arguments: The module ID to set as the native one. 0 means everything is native. - // Return value: None. - + /// Sets which DataModule ID should be treated as the native tech of the + /// user of this menu. + /// @param whichModule The module ID to set as the native one. 0 means everything is native. void SetNativeTechModule(int whichModule); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetForeignCostMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the multiplier of the cost of any foreign Tech items. - // Arguments: The scalar multiplier of the costs of foreign Tech items. - // Return value: None. - + /// Sets the multiplier of the cost of any foreign Tech items. + /// @param newMultiplier The scalar multiplier of the costs of foreign Tech items. void SetForeignCostMultiplier(float newMultiplier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EditMade - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether an edit on the scene was made in the last Update. - // Arguments: None. - // Return value: Whether any edit was made. - + /// Shows whether an edit on the scene was made in the last Update. + /// @return Whether any edit was made. bool EditMade() const { return m_EditMade; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TestBrainResidence - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks whether the resident brain is currently placed into a valid - // location in this scene, based on whether there is a clear path to the - // sky above it. This forces the editor into place brain mode with the - // current resident brain if the current placement is no bueno. It also - // removes the faulty brain from residence in the scene! - // Arguments: Whether it's OK if we dont' have a brain right now - ie don't force - // into isntallation mode if no brain was found. - // Return value: Whether a resident brain was found, AND found in a valid location! - + /// Checks whether the resident brain is currently placed into a valid + /// location in this scene, based on whether there is a clear path to the + /// sky above it. This forces the editor into place brain mode with the + /// current resident brain if the current placement is no bueno. It also + /// removes the faulty brain from residence in the scene! + /// @param noBrainIsOK Whether it's OK if we dont' have a brain right now - ie don't force (default: false) + /// into isntallation mode if no brain was found. + /// @return Whether a resident brain was found, AND found in a valid location! bool TestBrainResidence(bool noBrainIsOK = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Menu each frame - // Arguments: None. - // Return value: None. - + /// Updates the state of this Menu each frame void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the editor - // Arguments: The bitmap to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws the editor + /// @param pTargetBitmap The bitmap to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentAssemblyScheme - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a pointer to currently selected assembly scheme - // Arguments: None. - // Return value: Pointer to current assembly scheme. - + /// Returns a pointer to currently selected assembly scheme + /// @return Pointer to current assembly scheme. BunkerAssemblyScheme* GetCurrentAssemblyScheme() { return m_pCurrentScheme; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentAssemblyName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the name of currently edited assembly - // Arguments: None. - // Return value: Name of currently edited assembly. - + /// Returns the name of currently edited assembly + /// @return Name of currently edited assembly. std::string GetCurrentAssemblyName() { return m_CurrentAssemblyName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCurrentAssemblyScheme - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets new name of currently edited assembly - // Arguments: New name for assembly. - // Return value: None. - + /// Sets new name of currently edited assembly + /// @param newName New name for assembly. void SetCurrentAssemblyName(std::string newName) { m_CurrentAssemblyName = newName; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePieMenu - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the PieMenu config based ont eh current editor state. - // Arguments: None. - // Return value: None. - + /// Updates the PieMenu config based ont eh current editor state. void UpdatePieMenu(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateBrainPath - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the brain path to the current brain in cursor or resident - // in the scene, if any. If there's none, the path is cleared. - // Arguments: None. - // Return value: Whether a resident brain was found in the scene. - + /// Updates the brain path to the current brain in cursor or resident + /// in the scene, if any. If there's none, the path is cleared. + /// @return Whether a resident brain was found in the scene. bool UpdateBrainPath(); enum BlinkMode { @@ -387,18 +245,10 @@ namespace RTE { // Edited assembly name std::string m_CurrentAssemblyName; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this AssemblyEditorGUI, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this AssemblyEditorGUI, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/BuyMenuGUI.cpp b/Source/Menus/BuyMenuGUI.cpp index 9a8f6a9251..0842a8fdbe 100644 --- a/Source/Menus/BuyMenuGUI.cpp +++ b/Source/Menus/BuyMenuGUI.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: BuyMenuGUI.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Implementation file for the BuyMenuGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "BuyMenuGUI.h" #include "CameraMan.h" @@ -50,12 +38,6 @@ BITMAP* RTE::BuyMenuGUI::s_pCursor = 0; const std::string BuyMenuGUI::c_DefaultBannerImagePath = "Base.rte/GUIs/BuyMenu/BuyMenuBanner.png"; const std::string BuyMenuGUI::c_DefaultLogoImagePath = "Base.rte/GUIs/BuyMenu/BuyMenuLogo.png"; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this BuyMenuGUI, effectively -// resetting the members of this abstraction level only. - void BuyMenuGUI::Clear() { m_pController = 0; m_pGUIScreen = 0; @@ -132,11 +114,6 @@ void BuyMenuGUI::Clear() { m_LastEquipmentTab = SHIELDS; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the BuyMenuGUI object ready for use. - int BuyMenuGUI::Create(Controller* pController) { RTEAssert(pController, "No controller sent to BuyMenyGUI on creation!"); m_pController = pController; @@ -290,11 +267,6 @@ int BuyMenuGUI::Create(Controller* pController) { return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the BuyMenuGUI object. - void BuyMenuGUI::Destroy() { delete m_pGUIController; delete m_pGUIInput; @@ -305,38 +277,28 @@ void BuyMenuGUI::Destroy() { Clear(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::SetBannerImage(const std::string& imagePath) { ContentFile bannerFile((imagePath.empty() ? c_DefaultBannerImagePath : imagePath).c_str()); m_Banner->SetDrawImage(new AllegroBitmap(bannerFile.GetAsBitmap())); m_Banner->SetDrawType(GUICollectionBox::Image); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::SetLogoImage(const std::string& imagePath) { ContentFile logoFile((imagePath.empty() ? c_DefaultLogoImagePath : imagePath).c_str()); m_Logo->SetDrawImage(new AllegroBitmap(logoFile.GetAsBitmap())); m_Logo->SetDrawType(GUICollectionBox::Image); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::ClearCartList() { m_pCartList->ClearList(); m_ListItemIndex = 0; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::AddCartItem(const std::string& name, const std::string& rightText, GUIBitmap* pBitmap, const Entity* pEntity, const int extraIndex) { m_pCartList->AddItem(name, rightText, pBitmap, pEntity, extraIndex); UpdateItemNestingLevels(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::DuplicateCartItem(const int itemIndex) { if (m_pCartList->GetItemList()->empty()) { return; @@ -378,8 +340,6 @@ void BuyMenuGUI::DuplicateCartItem(const int itemIndex) { m_pCartList->SetSelectedIndex(itemIndex); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool BuyMenuGUI::LoadAllLoadoutsFromFile() { // First clear out all loadouts m_Loadouts.clear(); @@ -474,13 +434,6 @@ bool BuyMenuGUI::LoadAllLoadoutsFromFile() { return true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: SaveAllLoadoutsToFile -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves all the loadouts to appropriate file on disk. Does NOT save -// any named presets which will be loaded from the standard preset -// loadouts first anyway. - bool BuyMenuGUI::SaveAllLoadoutsToFile() { // Nothing to save if (m_Loadouts.empty()) @@ -513,11 +466,6 @@ bool BuyMenuGUI::SaveAllLoadoutsToFile() { return true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Enables or disables the menu. This will animate it in and out of view. - void BuyMenuGUI::SetEnabled(bool enable) { if (enable && m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) { if (g_FrameMan.IsInMultiplayerMode()) { @@ -584,22 +532,10 @@ void BuyMenuGUI::SetEnabled(bool enable) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. - void BuyMenuGUI::SetPosOnScreen(int newPosX, int newPosY) { m_pGUIController->SetPosOnScreen(newPosX, newPosY); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMetaPlayer -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which MetaPlayer uses this menu, if any. - void BuyMenuGUI::SetMetaPlayer(int metaPlayer) { if (metaPlayer >= Players::PlayerOne && metaPlayer < g_MetaMan.GetPlayerCount()) { m_MetaPlayer = metaPlayer; @@ -608,8 +544,6 @@ void BuyMenuGUI::SetMetaPlayer(int metaPlayer) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::SetNativeTechModule(int whichModule) { if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) { m_NativeTechModule = whichModule; @@ -639,12 +573,6 @@ void BuyMenuGUI::SetNativeTechModule(int whichModule) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetModuleExpanded -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether a data module shown in the item menu should be expanded -// or not. - void BuyMenuGUI::SetModuleExpanded(int whichModule, bool expanded) { int moduleCount = g_PresetMan.GetTotalModuleCount(); if (whichModule > 0 && whichModule < moduleCount) { @@ -659,11 +587,6 @@ void BuyMenuGUI::SetModuleExpanded(int whichModule, bool expanded) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOrderList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return the list of things currently in the purchase order list box. - bool BuyMenuGUI::GetOrderList(std::list& listToFill) const { if (m_pCartList->GetItemList()->empty()) return false; @@ -742,8 +665,6 @@ float BuyMenuGUI::GetTotalCost(bool includeDelivery) const { return totalCost; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float BuyMenuGUI::GetTotalOrderMass() const { float totalMass = 0.0F; @@ -759,8 +680,6 @@ float BuyMenuGUI::GetTotalOrderMass() const { return totalMass; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float BuyMenuGUI::GetCraftMass() { float totalMass = 0; @@ -771,13 +690,6 @@ float BuyMenuGUI::GetCraftMass() { return totalMass; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetTotalOrderPassengers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return the total number of passengers in the order box. -// Arguments: None. -// Return value: The total number of passengers. - int BuyMenuGUI::GetTotalOrderPassengers() const { int passengers = 0; for (std::vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr) { @@ -790,8 +702,6 @@ int BuyMenuGUI::GetTotalOrderPassengers() const { return passengers; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::EnableEquipmentSelection(bool enabled) { if (enabled != m_SelectingEquipment && g_SettingsMan.SmartBuyMenuNavigationEnabled()) { m_SelectingEquipment = enabled; @@ -816,8 +726,6 @@ void BuyMenuGUI::EnableEquipmentSelection(bool enabled) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::UpdateItemNestingLevels() { const int ownedDeviceOffsetX = 8; @@ -835,8 +743,6 @@ void BuyMenuGUI::UpdateItemNestingLevels() { m_pCartList->BuildBitmap(false, true); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::RefreshTabDisabledStates() { bool smartBuyMenuNavigationDisabled = !g_SettingsMan.SmartBuyMenuNavigationEnabled(); m_pCategoryTabs[CRAFT]->SetEnabled(smartBuyMenuNavigationDisabled || !m_SelectingEquipment); @@ -849,13 +755,6 @@ void BuyMenuGUI::RefreshTabDisabledStates() { m_pCategoryTabs[SETS]->SetEnabled(smartBuyMenuNavigationDisabled || !m_SelectingEquipment); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame - void BuyMenuGUI::Update() { // Enable mouse input if the controller allows it m_pGUIController->EnableMouse(m_pController->IsMouseControlled()); @@ -1900,8 +1799,6 @@ void BuyMenuGUI::Update() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::Draw(BITMAP* drawBitmap) const { AllegroScreen drawScreen(drawBitmap); m_pGUIController->Draw(&drawScreen); @@ -1964,11 +1861,6 @@ void BuyMenuGUI::FocusChange() } */ -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: CategoryChange -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes sure all things that to happen when category is changed, happens. - void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs) { // Re-set the GUI manager's focus on the tabs if we're supposed to // We don't want to do that if we're just refreshing the same category, like in the case of of expanding a module group item @@ -2081,11 +1973,6 @@ void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs) { m_pShopList->ScrollToSelected(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: SaveCurrentLoadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the current loadout into a Set. - void BuyMenuGUI::SaveCurrentLoadout() { Loadout newSet; @@ -2109,11 +1996,6 @@ void BuyMenuGUI::SaveCurrentLoadout() { m_pSaveButton->SetFocus(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: DeployLoadout -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Loads the loadout set into the cart, replacing whatever's there now. - bool BuyMenuGUI::DeployLoadout(int index) { if (index < 0 || index >= m_Loadouts.size()) return false; @@ -2196,8 +2078,6 @@ bool BuyMenuGUI::DeployLoadout(int index) { return true; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::AddObjectsToItemList(std::vector>& moduleList, const std::string& type, const std::vector& groups, bool excludeGroups) { while (moduleList.size() < g_PresetMan.GetTotalModuleCount()) { moduleList.emplace_back(); @@ -2291,11 +2171,6 @@ void BuyMenuGUI::AddObjectsToItemList(std::vector>& moduleLis } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddPresetsToItemList -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds all loadout presets' representations to the item GUI list. - void BuyMenuGUI::AddPresetsToItemList() { GUIBitmap* pItemBitmap = 0; std::string loadoutLabel; @@ -2344,15 +2219,11 @@ void BuyMenuGUI::AddPresetsToItemList() { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::UpdateTotalCostLabel(int whichTeam) { std::string display = "Cost: " + RoundFloatToPrecision(GetTotalOrderCost(), 0, 2) + "/" + RoundFloatToPrecision(g_ActivityMan.GetActivity()->GetTeamFunds(whichTeam), 0); m_pCostLabel->SetText(display); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::UpdateTotalMassLabel(const ACraft* pCraft, GUILabel* pLabel) const { if (!pLabel) { return; @@ -2371,8 +2242,6 @@ void BuyMenuGUI::UpdateTotalMassLabel(const ACraft* pCraft, GUILabel* pLabel) co pLabel->SetText(display); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::UpdateTotalPassengersLabel(const ACraft* pCraft, GUILabel* pLabel) const { if (!pLabel) { return; @@ -2391,8 +2260,6 @@ void BuyMenuGUI::UpdateTotalPassengersLabel(const ACraft* pCraft, GUILabel* pLab pLabel->SetText(display); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BuyMenuGUI::TryPurchase() { int player = m_pController->GetPlayer(); // Switch to the Craft category to give the user a hint diff --git a/Source/Menus/BuyMenuGUI.h b/Source/Menus/BuyMenuGUI.h index 4c7b1344c2..5b084e2834 100644 --- a/Source/Menus/BuyMenuGUI.h +++ b/Source/Menus/BuyMenuGUI.h @@ -1,18 +1,11 @@ #ifndef _BUYMENUGUI_ #define _BUYMENUGUI_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: BuyMenuGUI.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: BuyMenuGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// BuyMenuGUI class +/// @author Daniel Tabar +/// dtabar@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files // #include "FrameMan.h" #include "Timer.h" #include "Controller.h" @@ -36,538 +29,277 @@ namespace RTE { class MovableObject; class ACraft; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: BuyMenuGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A full menu system that represents a purchasing GUI for Cortex Command - // Parent(s): None. - // Class history: 8/22/2006 BuyMenuGUI Created. - + /// A full menu system that represents a purchasing GUI for Cortex Command class BuyMenuGUI { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: BuyMenuGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a BuyMenuGUI object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a BuyMenuGUI object in system + /// memory. Create() should be called before using the object. BuyMenuGUI() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~BuyMenuGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a BuyMenuGUI object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a BuyMenuGUI object before deletion + /// from system memory. ~BuyMenuGUI() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the BuyMenuGUI object ready for use. - // Arguments: A poitner to a Controller which will control this Menu. Ownership is - // NOT TRANSFERRED! - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the BuyMenuGUI object ready for use. + /// @param pController A poitner to a Controller which will control this Menu. Ownership is + /// NOT TRANSFERRED! + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(Controller* pController); - ////////////////////////////////////////////////////////////////////////////////////////// - // method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire BuyMenuGUI, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire BuyMenuGUI, including its inherited members, to + /// their default settings or values. void Reset() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the BuyMenuGUI object. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the BuyMenuGUI object. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadAllLoadoutsFromFile - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads or re-loads all the loadout presets from the appropriate files - // on disk. This will first clear out all current loadout presets! - // Arguments: None. - // Return value: Success or not. - + /// Loads or re-loads all the loadout presets from the appropriate files + /// on disk. This will first clear out all current loadout presets! + /// @return Success or not. bool LoadAllLoadoutsFromFile(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SaveAllLoadoutsToFile - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves all the loadouts to appropriate file on disk. Does NOT save - // any named presets which will be loaded from the standard preset - // loadouts first anyway. - // Arguments: None. - // Return value: Success or not. - + /// Saves all the loadouts to appropriate file on disk. Does NOT save + /// any named presets which will be loaded from the standard preset + /// loadouts first anyway. + /// @return Success or not. bool SaveAllLoadoutsToFile(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetController - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the controller used by this. The ownership of the controller is - // NOT transferred! - // Arguments: The new controller for this menu. Ownership is NOT transferred - // Return value: None. - + /// Sets the controller used by this. The ownership of the controller is + /// NOT transferred! + /// @param pController The new controller for this menu. Ownership is NOT transferred void SetController(Controller* pController) { m_pController = pController; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Enables or disables the menu. This will animate it in and out of view. - // Arguments: Whether to enable or disable the menu. - // Return value: None. - + /// Enables or disables the menu. This will animate it in and out of view. + /// @param enable Whether to enable or disable the menu. (default: true) void SetEnabled(bool enable = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the menu is enabled or not. - // Arguments: None. - // Return value: None. - + /// Reports whether the menu is enabled or not. bool IsEnabled() const { return m_MenuEnabled == ENABLED || m_MenuEnabled == ENABLING; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the menu is at all visible or not. - // Arguments: None. - // Return value: None. - + /// Reports whether the menu is at all visible or not. bool IsVisible() const { return m_MenuEnabled == ENABLED || m_MenuEnabled == ENABLING || m_MenuEnabled == DISABLING; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPosOnScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets where on the screen that this GUI is being drawn to. If upper - // left corner, then 0, 0. This will affect the way the mouse is positioned - // etc. - // Arguments: The new screen position of this entire GUI. - + /// Sets where on the screen that this GUI is being drawn to. If upper + /// left corner, then 0, 0. This will affect the way the mouse is positioned + /// etc. + /// @param newPosX The new screen position of this entire GUI. void SetPosOnScreen(int newPosX, int newPosY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetMetaPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which MetaPlayer uses this menu, if any. - // Arguments: The index of the MetaPlayer that uses this menu. - // Return value: None. - + /// Sets which MetaPlayer uses this menu, if any. + /// @param metaPlayer The index of the MetaPlayer that uses this menu. void SetMetaPlayer(int metaPlayer); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetMetaPlayer - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets which MetaPlayer uses this menu, if any. - // Arguments: None. - // Return value: Metaplayer who owns this buy menu - + /// Gets which MetaPlayer uses this menu, if any. + /// @return Metaplayer who owns this buy menu int GetMetaPlayer() const { return m_MetaPlayer; } - /// /// Sets which DataModule ID should be treated as the native tech of the user of this menu. /// This will also apply the DataModule's faction BuyMenu theme, if applicable. - /// - /// The module ID to set as the native one. 0 means everything is native. + /// @param whichModule The module ID to set as the native one. 0 means everything is native. void SetNativeTechModule(int whichModule); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetForeignCostMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the multiplier of the cost of any foreign Tech items. - // Arguments: The scalar multiplier of the costs of foreign Tech items. - // Return value: None. - + /// Sets the multiplier of the cost of any foreign Tech items. + /// @param newMultiplier The scalar multiplier of the costs of foreign Tech items. void SetForeignCostMultiplier(float newMultiplier) { m_ForeignCostMult = newMultiplier; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetModuleExpanded - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether a data module shown in the item menu should be expanded - // or not. - // Arguments: The module ID to set as expanded. - // Whether should be expanded or not. - // Return value: None. - + /// Sets whether a data module shown in the item menu should be expanded + /// or not. + /// @param whichModule The module ID to set as expanded. + /// @param expanded Whether should be expanded or not. (default: true) void SetModuleExpanded(int whichModule, bool expanded = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PurchaseMade - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether a purchase was made during the last Update. - // Arguments: None. - // Return value: Wheter the BUY button was pressed or not during the last update. - + /// Reports whether a purchase was made during the last Update. + /// @return Wheter the BUY button was pressed or not during the last update. bool PurchaseMade() const { return m_PurchaseMade; } - /// /// Gets the width of the current delivery craft. - /// - /// The width of the delivery craft, in pixels. + /// @return The width of the delivery craft, in pixels. int GetDeliveryWidth() const { return m_DeliveryWidth; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOrderList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Return the list of things currently in the purchase order list box. - // Arguments: A reference to a an empty list to fill with the Object:s ordered. - // Ownership of the Object:s is NOT TRANSFERRED! - // Return value: Whetehr any items were put in the list at all. false if there are no - // items in the order listbox. - + /// Return the list of things currently in the purchase order list box. + /// @param listToFill A reference to a an empty list to fill with the Object:s ordered. + /// Ownership of the Object:s is NOT TRANSFERRED! + /// @return Whetehr any items were put in the list at all. false if there are no + /// items in the order listbox. bool GetOrderList(std::list& listToFill) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetLoadoutPresets - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Return the list of loadouts currently saved as presets. - // Arguments: None. - // Return value: A reference to the list of loadout presets. - + /// Return the list of loadouts currently saved as presets. + /// @return A reference to the list of loadout presets. std::vector& GetLoadoutPresets() { return m_Loadouts; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SaveCurrentLoadout - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Saves the current loadout into a Set. - // Arguments: None. - // Return value: None. - + /// Saves the current loadout into a Set. void SaveCurrentLoadout(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetDeliveryCraftPreset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Return the intended delivery vehicle instance from the order. - // Arguments: None. - // Return value: The poiner to the specified delivery craft instance. Note that this is - // just PresetMan's const pointer, so ownership is NOT transferred! - + /// Return the intended delivery vehicle instance from the order. + /// @return The poiner to the specified delivery craft instance. Note that this is + /// just PresetMan's const pointer, so ownership is NOT transferred! const SceneObject* GetDeliveryCraftPreset() { return m_pSelectedCraft; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalCost - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Return the total cost of everything listed in the order box. - // Arguments: Whether or not to include delivery cost. - // Return value: The total cost in ounces of gold. - + /// Return the total cost of everything listed in the order box. + /// @param includeDelivery Whether or not to include delivery cost. (default: true) + /// @return The total cost in ounces of gold. float GetTotalCost(bool includeDelivery = true) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalOrderCost - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Return the total cost of everything listed in the order box, including delivery costs. - // Arguments: None. - // Return value: The total cost in ounces of gold. - + /// Return the total cost of everything listed in the order box, including delivery costs. + /// @return The total cost in ounces of gold. float GetTotalOrderCost() const { return GetTotalCost(true); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalCartCost - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Return the total cost of everything listed in the order box, excluding delivery costs. - // Arguments: None. - // Return value: The total cost in ounces of gold. - + /// Return the total cost of everything listed in the order box, excluding delivery costs. + /// @return The total cost in ounces of gold. float GetTotalCartCost() const { return GetTotalCost(false); } - /// /// Return the total mass of all items listed in the order box. - /// - /// The total mass (in kg) of the BuyMenu's cart. + /// @return The total mass (in kg) of the BuyMenu's cart. float GetTotalOrderMass() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCraftMass - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Return mass of craft used in the order box. - // Arguments: None. - // Return value: The total mass in kg. - + /// Return mass of craft used in the order box. + /// @return The total mass in kg. float GetCraftMass(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetTotalOrderPassengers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Return teh total number of passengers in the order box. - // Arguments: None. - // Return value: The total number of passengers. - + /// Return teh total number of passengers in the order box. + /// @return The total number of passengers. int GetTotalOrderPassengers() const; - /// /// Enable or disable the equipment selection mode for this BuyMenuGUI. - /// - /// Whether or not equipment selection mode should be enabled. + /// @param enabled Whether or not equipment selection mode should be enabled. void EnableEquipmentSelection(bool enabled); - /// /// Updates the nesting level for every item in the cart. - /// void UpdateItemNestingLevels(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Menu each frame - // Arguments: None. - // Return value: None. - + /// Updates the state of this Menu each frame void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the menu - // Arguments: The bitmap to draw on. - // Return value: None. - + /// Draws the menu + /// @param drawBitmap The bitmap to draw on. void Draw(BITMAP* drawBitmap) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnforceMaxPassengersConstraint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether passenger count constraints are enforced by this buy menu. - // Arguments: None. - // Return value: True if passenger constraints are enforced by this menu, false otherwise - + /// Method: EnforceMaxPassengersConstraint + /// Tells whether passenger count constraints are enforced by this buy menu. + /// @return True if passenger constraints are enforced by this menu, false otherwise bool EnforceMaxPassengersConstraint() const { return m_EnforceMaxPassengersConstraint; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEnforceMaxPassengersConstraint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether passenger count constraints are enforced by this buy menu. - // Arguments: True to enforce passenger constraints by this menu, false otherwise - // Return value: None. - + /// Method: SetEnforceMaxPassengersConstraint + /// Sets whether passenger count constraints are enforced by this buy menu. + /// @param enforce True to enforce passenger constraints by this menu, false otherwise void SetEnforceMaxPassengersConstraint(bool enforce) { m_EnforceMaxPassengersConstraint = enforce; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EnforceMaxMassConstraint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether mass constraints are enforced by this buy menu. - // Arguments: True if mass constraints are enforced by this menu, false otherwise - // Return value: None. - + /// Method: EnforceMaxMassConstraint + /// Sets whether mass constraints are enforced by this buy menu. + /// @param True if mass constraints are enforced by this menu, false otherwise bool EnforceMaxMassConstraint() const { return m_EnforceMaxMassConstraint; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEnforceMaxMassConstraint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets whether mass constraints are enforced by this buy menu. - // Arguments: True to enforce mass constraints by this menu, false otherwise - // Return value: None. - + /// Method: SetEnforceMaxMassConstraint + /// Sets whether mass constraints are enforced by this buy menu. + /// @param enforce True to enforce mass constraints by this menu, false otherwise void SetEnforceMaxMassConstraint(bool enforce) { m_EnforceMaxMassConstraint = enforce; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddAllowedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an item to the list of allowed items. - // If the list is not empty then everything not in the list is removed from the buy menu - // Items will be removed from the buy menu when it's called, category changed or after a ForceRefresh(). - // Arguments: Full preset name to add. - // Return value: None. - + /// Method: AddAllowedItem + /// Adds an item to the list of allowed items. + /// If the list is not empty then everything not in the list is removed from the buy menu + /// Items will be removed from the buy menu when it's called, category changed or after a ForceRefresh(). + /// @param presetName Full preset name to add. void AddAllowedItem(std::string presetName) { m_AllowedItems[presetName] = true; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveAllowedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes an item from the list of allowed items. - // Arguments: Full preset name to remove. - // Return value: None. - + /// Method: RemoveAllowedItem + /// Removes an item from the list of allowed items. + /// @param m_AllowedItems.erase(presetName Full preset name to remove. void RemoveAllowedItem(std::string presetName) { m_AllowedItems.erase(presetName); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearAllowedItems - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the list of allowed items - // Arguments: None. - // Return value: None. - + /// Method: ClearAllowedItems + /// Clears the list of allowed items void ClearAllowedItems() { m_AllowedItems.clear(); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsAllowedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns true if the item is in allowed list - // Arguments: Full preset name. - // Return value: None. - + /// Method: IsAllowedItem + /// Returns true if the item is in allowed list + /// @param ! Full preset name. (default: m_AllowedItems.end() bool IsAllowedItem(std::string presetName) { return m_AllowedItems.find(presetName) != m_AllowedItems.end(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddAlwaysAllowedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an item to the list of always allowed items. This list overrides all previous constraints. - // Arguments: Full preset name to add. - // Return value: None. - + /// Method: AddAlwaysAllowedItem + /// Adds an item to the list of always allowed items. This list overrides all previous constraints. + /// @param presetName Full preset name to add. void AddAlwaysAllowedItem(std::string presetName) { m_AlwaysAllowedItems[presetName] = true; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveAlwaysAllowedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes an item from the list of always allowed items. - // Arguments: Full preset name to remove. - // Return value: None. - + /// Method: RemoveAlwaysAllowedItem + /// Removes an item from the list of always allowed items. + /// @param m_AlwaysAllowedItems.erase(presetName Full preset name to remove. void RemoveAlwaysAllowedItem(std::string presetName) { m_AlwaysAllowedItems.erase(presetName); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearAlwaysAllowedItems - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the list of allowed items - // Arguments: None. - // Return value: None. - + /// Method: ClearAlwaysAllowedItems + /// Clears the list of allowed items void ClearAlwaysAllowedItems() { m_AlwaysAllowedItems.clear(); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsAlwaysAllowedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns true if the item is in always allowed list - // Arguments: Full preset name. - // Return value: None. - + /// Method: IsAlwaysAllowedItem + /// Returns true if the item is in always allowed list + /// @param ! Full preset name. (default: m_AlwaysAllowedItems.end() bool IsAlwaysAllowedItem(std::string presetName) { return m_AlwaysAllowedItems.find(presetName) != m_AlwaysAllowedItems.end(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddProhibitedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds an item prohibited to buy from the buy menu. - // The item will be removed from the buy menu when it's called, category changed or after a ForceRefresh(). - // Arguments: Full preset name to add. - // Return value: None. - + /// Method: AddProhibitedItem + /// Adds an item prohibited to buy from the buy menu. + /// The item will be removed from the buy menu when it's called, category changed or after a ForceRefresh(). + /// @param presetName Full preset name to add. void AddProhibitedItem(std::string presetName) { m_ProhibitedItems[presetName] = true; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveProhibitedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes item from the list of prohibited items - // Arguments: Full preset name to remove. - // Return value: None. - + /// Method: RemoveProhibitedItem + /// Removes item from the list of prohibited items + /// @param m_ProhibitedItems.erase(presetName Full preset name to remove. void RemoveProhibitedItem(std::string presetName) { m_ProhibitedItems.erase(presetName); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearProhibitedItems - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears the list of prohibited items - // Arguments: None. - // Return value: None. - + /// Method: ClearProhibitedItems + /// Clears the list of prohibited items void ClearProhibitedItems() { m_ProhibitedItems.clear(); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsProhibitedItem - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns true if the item is in prohibited list - // Arguments: Full preset name. - // Return value: None. - + /// Method: IsProhibitedItem + /// Returns true if the item is in prohibited list + /// @param ! Full preset name. (default: m_ProhibitedItems.end() bool IsProhibitedItem(std::string presetName) { return m_ProhibitedItems.find(presetName) != m_ProhibitedItems.end(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ForceRefresh - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Forces a refresh update of the list of buy menu items - // Arguments: None. - // Return value: None. - + /// Method: ForceRefresh + /// Forces a refresh update of the list of buy menu items void ForceRefresh() { CategoryChange(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ClearCartList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clear the cart out of items selected for purchase - // Arguments: None. - // Return value: None. - + /// Method: ClearCartList + /// Clear the cart out of items selected for purchase void ClearCartList(); - /// /// Adds an item to the cart. - /// - /// The name shown for the item. - /// The text that is shown right-aligned on the item (typically the cost of the item). - /// The sprite image rendered for the item. This takes ownership! - /// The entity that this item refers to and will create when bought. - /// Extra index for special indexing or reference that the item is associated with. Menu-specific. + /// @param name The name shown for the item. + /// @param rightText The text that is shown right-aligned on the item (typically the cost of the item). + /// @param pBitmap The sprite image rendered for the item. This takes ownership! + /// @param pEntity The entity that this item refers to and will create when bought. + /// @param extraIndex Extra index for special indexing or reference that the item is associated with. Menu-specific. void AddCartItem(const std::string& name, const std::string& rightText = "", GUIBitmap* pBitmap = nullptr, const Entity* pEntity = 0, const int extraIndex = -1); - /// /// Duplicates an item in the cart. - /// - /// The index of the item to duplicate. + /// @param itemIndex The index of the item to duplicate. void DuplicateCartItem(const int itemIndex); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadDefaultLoadoutToCart - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads the default loadout to the cart - // Arguments: None. - // Return value: None. - + /// Method: LoadDefaultLoadoutToCart + /// Loads the default loadout to the cart void LoadDefaultLoadoutToCart() { DeployLoadout(0); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOnlyShowOwnedItems - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: If set to true only owned items will be shown in buy menu. Overriden by AlwaysAllowed list. - // Arguments: Value. - // Return value: None. - + /// Method: SetOnlyShowOwnedItems + /// If set to true only owned items will be shown in buy menu. Overriden by AlwaysAllowed list. + /// @param value Value. void SetOnlyShowOwnedItems(bool value) { m_OnlyShowOwnedItems = value; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOnlyShowOwnedItems - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns whether only owned items will be shown in buy menu. Overriden by AlwaysAllowed list. - // Arguments: None. - // Return value: Whether only owned items will be shown in buy menu. - + /// Method: GetOnlyShowOwnedItems + /// Returns whether only owned items will be shown in buy menu. Overriden by AlwaysAllowed list. + /// @return Whether only owned items will be shown in buy menu. bool GetOnlyShowOwnedItems() const { return m_OnlyShowOwnedItems; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetOwnedItemsAmount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the amount of specified items to be owned in this buy menu - // Arguments: Full preset name of item to own. Amount of owned items. - // Return value: None. - + /// Method: SetOwnedItemsAmount + /// Sets the amount of specified items to be owned in this buy menu + /// @param presetName Full preset name of item to own. Amount of owned items. void SetOwnedItemsAmount(std::string presetName, int amount) { m_OwnedItems[presetName] = amount; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetOwnedItemsAmount - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the amount of specified items owned in this buy menu - // Arguments: Full preset name of item. - // Return value: Amount of owned items. - + /// Method: GetOwnedItemsAmount + /// Returns the amount of specified items owned in this buy menu + /// @param presetName Full preset name of item. + /// @return Amount of owned items. int GetOwnedItemsAmount(std::string presetName) { if (m_OwnedItems.find(presetName) != m_OwnedItems.end()) return m_OwnedItems[presetName]; @@ -575,106 +307,59 @@ namespace RTE { return 0; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CommitPurchase - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Deducts 1 piece of owned item and return true if purchase can be made or false if the item is out of stock. - // Arguments: Full preset name of item. - // Return value: Whether the purchase can be conducted or the item is out of stock. - + /// Method: CommitPurchase + /// Deducts 1 piece of owned item and return true if purchase can be made or false if the item is out of stock. + /// @param presetName Full preset name of item. + /// @return Whether the purchase can be conducted or the item is out of stock. bool CommitPurchase(std::string presetName); #pragma region Faction Theme Handling - /// /// Changes the banner image to the one specified. If none is specified, resets it to the default banner image. - /// - /// Path to image to set as banner. + /// @param imagePath Path to image to set as banner. void SetBannerImage(const std::string& imagePath); - /// /// Changes the logo image to the one specified. If none is specified, resets it to the default logo image. - /// - /// Path to image to set as logo. + /// @param imagePath Path to image to set as logo. void SetLogoImage(const std::string& imagePath); #pragma endregion - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CategoryChange - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes sure all things that to happen when category is changed, happens. - // Arguments: Wheter to change focus to the category tabs or not. - // Return value: None. - + /// Makes sure all things that to happen when category is changed, happens. + /// @param focusOnCategoryTabs Wheter to change focus to the category tabs or not. (default: true) void CategoryChange(bool focusOnCategoryTabs = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DeployLoadout - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Loads the loadout set into the cart, replacing whatever's there now. - // Arguments: The index of the loadout to load. - // Return value: Whether it was loaded successfully or not. - + /// Loads the loadout set into the cart, replacing whatever's there now. + /// @param index The index of the loadout to load. + /// @return Whether it was loaded successfully or not. bool DeployLoadout(int index); - /// /// Adds all objects of a specific type already defined in PresetMan to the current shop/item list. They will be grouped into the different data modules they were read from. - /// - /// Reference to the data module vector of entity lists to add the items to. - /// The name of the class to add all objects of. "" or "All" looks for all. - /// The name of the groups to add all objects of. An empty vector or "All" looks for all. - /// Whether the specified groups should be excluded, meaning all objects NOT associated with the groups will be added. + /// @param moduleList Reference to the data module vector of entity lists to add the items to. + /// @param type The name of the class to add all objects of. "" or "All" looks for all. + /// @param groups The name of the groups to add all objects of. An empty vector or "All" looks for all. + /// @param excludeGroups Whether the specified groups should be excluded, meaning all objects NOT associated with the groups will be added. void AddObjectsToItemList(std::vector>& moduleList, const std::string& type = "", const std::vector& groups = {}, bool excludeGroups = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddPresetsToItemList - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds all loadout presets' representations to the item GUI list. - // Arguments: None. - // Return value: None. - + /// Adds all loadout presets' representations to the item GUI list. void AddPresetsToItemList(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateTotalCostLabel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the text of the total cost label to reflect the total cost of - // all the items in teh order box. - // Arguments: The team to display the total funds of. - // Return value: None. - + /// Updates the text of the total cost label to reflect the total cost of + /// all the items in teh order box. + /// @param whichTeam The team to display the total funds of. (default: 0) void UpdateTotalCostLabel(int whichTeam = 0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateTotalMassLabel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the text of the specified label to reflect the total mass of - // all the items in the order box. - // Arguments: Craft to read MaxMass from. Label to update. - // Return value: None. - + /// Updates the text of the specified label to reflect the total mass of + /// all the items in the order box. + /// @param pCraft Craft to read MaxMass from. Label to update. void UpdateTotalMassLabel(const ACraft* pCraft, GUILabel* pLabel) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateTotalPassengersLabel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the text of the specified label to reflect the total passenger count of - // all the items in teh order box. - // Arguments: Craft to read MaxPassengers from. Label to update. - // Return value: None. - + /// Updates the text of the specified label to reflect the total passenger count of + /// all the items in teh order box. + /// @param pCraft Craft to read MaxPassengers from. Label to update. void UpdateTotalPassengersLabel(const ACraft* pCraft, GUILabel* pLabel) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TryPurchase - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Attempts to make a purchase with everything already set up. - // Arguments: None. - // Return value: None. - + /// Attempts to make a purchase with everything already set up. void TryPurchase(); enum MenuEnabled { @@ -845,26 +530,16 @@ namespace RTE { // A map of owned items, for which the gold will not be deducted when bought std::map m_OwnedItems; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: static const std::string c_DefaultBannerImagePath; //!< Path to the default banner image. static const std::string c_DefaultLogoImagePath; //!< Path to the default logo image. - /// /// Refresh tab disabled states, so tabs get properly enabled/disabled based on whether or not equipment selection mode is enabled. - /// void RefreshTabDisabledStates(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this BuyMenuGUI, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this BuyMenuGUI, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/GibEditorGUI.cpp b/Source/Menus/GibEditorGUI.cpp index 89a9c464e1..d149b38b2d 100644 --- a/Source/Menus/GibEditorGUI.cpp +++ b/Source/Menus/GibEditorGUI.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GibEditorGUI.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Implementation file for the GibEditorGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "GibEditorGUI.h" #include "CameraMan.h" @@ -32,12 +20,6 @@ using namespace RTE; #define MAXZOOMFACTOR 5 #define MINZOOMFACTOR 1 -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this GibEditorGUI, effectively -// resetting the members of this abstraction level only. - void GibEditorGUI::Clear() { m_pController = 0; m_EditMade = false; @@ -64,11 +46,6 @@ void GibEditorGUI::Clear() { m_pObjectToBlink = 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the GibEditorGUI object ready for use. - int GibEditorGUI::Create(Controller* pController, int whichModuleSpace) { RTEAssert(pController, "No controller sent to GibEditorGUI on creation!"); m_pController = pController; @@ -104,11 +81,6 @@ int GibEditorGUI::Create(Controller* pController, int whichModuleSpace) { return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the GibEditorGUI object. - void GibEditorGUI::Destroy() { delete m_pPicker; @@ -122,53 +94,24 @@ void GibEditorGUI::Destroy() { Clear(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! - void GibEditorGUI::SetController(Controller* pController) { m_pController = pController; m_PieMenu->SetMenuController(pController); m_pPicker->SetController(pController); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. - void GibEditorGUI::SetPosOnScreen(int newPosX, int newPosY) { m_pPicker->SetPosOnScreen(newPosX, newPosY); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActivatedPieSlice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets any Pie menu slice command activated last update. - PieSlice::SliceType GibEditorGUI::GetActivatedPieSlice() const { return m_PieMenu->GetPieCommand(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule space to be picking objects from. If -1, then -// let the player pick from all loaded modules. - void GibEditorGUI::SetModuleSpace(int moduleSpaceID) { m_pPicker->SetModuleSpace(moduleSpaceID); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame - void GibEditorGUI::Update() { // Update the user controller // m_pController->Update(); @@ -632,11 +575,6 @@ void GibEditorGUI::Update() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu - void GibEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { // Done, so don't draw the UI if (m_EditorGUIMode == DONEEDITING) @@ -729,11 +667,6 @@ void GibEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { m_PieMenu->Draw(pTargetBitmap, targetPos); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds a MovableObject to be placed in this scene. Ownership IS transferred! - void GibEditorGUI::AddPlacedObject(MovableObject* pObjectToAdd, int listOrder) { if (!pObjectToAdd) return; @@ -751,11 +684,6 @@ void GibEditorGUI::AddPlacedObject(MovableObject* pObjectToAdd, int listOrder) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemovePlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a MovableObject placed in this scene. - void GibEditorGUI::RemovePlacedObject(int whichToRemove) { if (m_PlacedGibs.empty()) return; @@ -774,12 +702,6 @@ void GibEditorGUI::RemovePlacedObject(int whichToRemove) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PickPlacedObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the last placed object that graphically overlaps an absolute -// point in the scene. - const MovableObject* GibEditorGUI::PickPlacedObject(Vector& scenePoint, int* pListOrderPlace) const { // REVERSE! int i = m_PlacedGibs.size() - 1; @@ -796,12 +718,6 @@ const MovableObject* GibEditorGUI::PickPlacedObject(Vector& scenePoint, int* pLi return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlacedObjects -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updated the objects in the placed scene objects list of this. This is -// mostly for the editor to represent the items correctly. - void GibEditorGUI::UpdatePlacedObjects() { for (std::list::iterator itr = m_PlacedGibs.begin(); itr != m_PlacedGibs.end(); ++itr) { (*itr)->Update(); diff --git a/Source/Menus/GibEditorGUI.h b/Source/Menus/GibEditorGUI.h index 347989abed..46478ed484 100644 --- a/Source/Menus/GibEditorGUI.h +++ b/Source/Menus/GibEditorGUI.h @@ -1,18 +1,11 @@ #ifndef _GIBEDITORGUI_ #define _GIBEDITORGUI_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: GibEditorGUI.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: GibEditorGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// GibEditorGUI class +/// @author Daniel Tabar +/// dtabar@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files // #include "FrameMan.h" #include "Timer.h" #include "Vector.h" @@ -28,18 +21,10 @@ namespace RTE { class ObjectPickerGUI; class PieMenu; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: GibEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A placement part of the gib editor which manages the pie menu and picker. - // Parent(s): None. - // Class history: 9/16/2007 GibEditorGUI Created. - + /// A placement part of the gib editor which manages the pie menu and picker. class GibEditorGUI { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Different modes of this editor enum EditorGUIMode { @@ -56,239 +41,121 @@ namespace RTE { EDITORGUIMODECOUNT }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: GibEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a GibEditorGUI object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a GibEditorGUI object in system + /// memory. Create() should be called before using the object. GibEditorGUI() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~GibEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a GibEditorGUI object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a GibEditorGUI object before deletion + /// from system memory. ~GibEditorGUI() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the GibEditorGUI object ready for use. - // Arguments: A poitner to a Controller which will control this Menu. Ownership is - // NOT TRANSFERRED! - // Which module space that this eidtor will be able to pick objects from. - // -1 means all modules. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the GibEditorGUI object ready for use. + /// @param pController A poitner to a Controller which will control this Menu. Ownership is + /// @param whichModuleSpace NOT TRANSFERRED! (default: -1) + /// Which module space that this eidtor will be able to pick objects from. + /// -1 means all modules. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(Controller* pController, int whichModuleSpace = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire GibEditorGUI, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire GibEditorGUI, including its inherited members, to + /// their default settings or values. void Reset() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the GibEditorGUI object. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the GibEditorGUI object. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetController - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the controller used by this. The ownership of the controller is - // NOT transferred! - // Arguments: The new controller for this menu. Ownership is NOT transferred - // Return value: None. - + /// Sets the controller used by this. The ownership of the controller is + /// NOT transferred! + /// @param pController The new controller for this menu. Ownership is NOT transferred void SetController(Controller* pController); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPosOnScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets where on the screen that this GUI is being drawn to. If upper - // left corner, then 0, 0. This will affect the way the mouse is positioned - // etc. - // Arguments: The new screen position of this entire GUI. - + /// Sets where on the screen that this GUI is being drawn to. If upper + /// left corner, then 0, 0. This will affect the way the mouse is positioned + /// etc. + /// @param newPosX The new screen position of this entire GUI. void SetPosOnScreen(int newPosX, int newPosY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCursorPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the absolute scene coordinates of the cursor of this Editor. - // Arguments: The new cursor position in absolute scene units. - // Return value: None. - + /// Sets the absolute scene coordinates of the cursor of this Editor. + /// @param newCursorPos The new cursor position in absolute scene units. void SetCursorPos(const Vector& newCursorPos) { m_CursorPos = newCursorPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCurrentGib - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the new Object to be held at the gib cursor of this Editor. Ownership - // IS transferred! - // Arguments: The new Object to be held by the cursor. Ownership IS transferred! - // Return value: None. - + /// Sets the new Object to be held at the gib cursor of this Editor. Ownership + /// IS transferred! + /// @param pNewGibObject The new Object to be held by the cursor. Ownership IS transferred! void SetCurrentGib(MovableObject* pNewGibObject) { m_pCurrentGib = pNewGibObject; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActivatedPieSlice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets any Pie menu slice command activated last update. - // Arguments: None. - // Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. - + /// Gets any Pie menu slice command activated last update. + /// @return The enum'd int of any slice activated. See the PieSlice::SliceType enum. PieSlice::SliceType GetActivatedPieSlice() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlacedGibs - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the list of already placed gibs of the currently edited object. - // Ownership of neither list not objects IS NOT transferred! - // Arguments: None. - // Return value: The current list of placed gibs. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the list of already placed gibs of the currently edited object. + /// Ownership of neither list not objects IS NOT transferred! + /// @return The current list of placed gibs. OWNERSHIP IS NOT TRANSFERRED! std::list* GetPlacedGibs() { return &m_PlacedGibs; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentGib - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the currently held Object in the cursor of this Editor. Ownership - // IS NOT transferred! - // Arguments: None. - // Return value: The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the currently held Object in the cursor of this Editor. Ownership + /// IS NOT transferred! + /// @return The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! const MovableObject* GetCurrentGib() { return m_pCurrentGib; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetObjectToLoad - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns a refernece instance of an object if it has been picked to be - // loaded into the editor. Ownership is NOT transferred. - // Arguments: None. - // Return value: Reference instance of the picked object to be loaded. 0 if nothing. OWNERSHIP IS NOT TRANSFERRED! - // was picked since last update. - + /// Returns a refernece instance of an object if it has been picked to be + /// loaded into the editor. Ownership is NOT transferred. + /// @return Reference instance of the picked object to be loaded. 0 if nothing. OWNERSHIP IS NOT TRANSFERRED! + /// was picked since last update. const MOSRotating* GetObjectToLoad() { return m_pObjectToLoad; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetModuleSpace - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which DataModule space to be picking objects from. If -1, then - // let the player pick from all loaded modules. - // Arguments: The ID of the module to let the player pick objects from. All official - // modules' objects will alwayws be presented, in addition to the one - // passed in here. - // Return value: None. - + /// Sets which DataModule space to be picking objects from. If -1, then + /// let the player pick from all loaded modules. + /// @param moduleSpaceID The ID of the module to let the player pick objects from. All official (default: -1) + /// modules' objects will alwayws be presented, in addition to the one + /// passed in here. void SetModuleSpace(int moduleSpaceID = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EditMade - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether an edit on the scene was made in the last Update. - // Arguments: None. - // Return value: Whether any edit was made. - + /// Shows whether an edit on the scene was made in the last Update. + /// @return Whether any edit was made. bool EditMade() const { return m_EditMade; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Menu each frame - // Arguments: None. - // Return value: None. - + /// Updates the state of this Menu each frame void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the editor - // Arguments: The bitmap to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws the editor + /// @param pTargetBitmap The bitmap to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AddPlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Adds a MovableObject to be placed in the editor. Ownership IS transferred! - // Arguments: The MovableOjbect instace to add, OIT! - // Where in the list the object should be inserted. -1 means at the end - // of the list. - // Return value: None. - + /// Adds a MovableObject to be placed in the editor. Ownership IS transferred! + /// @param pObjectToAdd The MovableOjbect instace to add, OIT! + /// @param listOrder Where in the list the object should be inserted. -1 means at the end (default: -1) + /// of the list. void AddPlacedObject(MovableObject* pObjectToAdd, int listOrder = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemovePlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a MovableObject placed in this editor. - // Arguments: The list order number of the object to remove. If -1, the last one is removed. - // Return value: None. - + /// Removes a MovableObject placed in this editor. + /// @param whichToRemove The list order number of the object to remove. If -1, the last one is removed. (default: -1) void RemovePlacedObject(int whichToRemove = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PickPlacedObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Returns the last placed object that graphically overlaps an absolute - // point in the scene. Note that the placed object don't really exist in - // the scene, but in the editor. Their own Pos's are used. - // Arguments: The point in absolute scene coordinates that will be used to pick the - // last placed MovableObject which overlaps it. - // An int which will be filled out with the order place of any found object - // in the list. if nothing is found, it will get a value of -1. - // Return value: The last hit MovableObject, if any. Ownership is NOT transferred! - + /// Returns the last placed object that graphically overlaps an absolute + /// point in the scene. Note that the placed object don't really exist in + /// the scene, but in the editor. Their own Pos's are used. + /// @param scenePoint The point in absolute scene coordinates that will be used to pick the + /// last placed MovableObject which overlaps it. + /// @param pListOrderPlace An int which will be filled out with the order place of any found object (default: 0) + /// in the list. if nothing is found, it will get a value of -1. + /// @return The last hit MovableObject, if any. Ownership is NOT transferred! const MovableObject* PickPlacedObject(Vector& scenePoint, int* pListOrderPlace = 0) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePlacedObjects - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updated the objects in the placed scene objects list of this. This is - // mostly for the editor to represent the items correctly. - // Arguments: None. - // Return value: None. - + /// Updated the objects in the placed scene objects list of this. This is + /// mostly for the editor to represent the items correctly. void UpdatePlacedObjects(); enum BlinkMode { @@ -347,18 +214,10 @@ namespace RTE { // Currently placed scene object to make blink when drawing it. NOT OWNED. const MovableObject* m_pObjectToBlink; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this GibEditorGUI, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this GibEditorGUI, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/InventoryMenuGUI.cpp b/Source/Menus/InventoryMenuGUI.cpp index 1021e0ca54..59d16abdc6 100644 --- a/Source/Menus/InventoryMenuGUI.cpp +++ b/Source/Menus/InventoryMenuGUI.cpp @@ -30,8 +30,6 @@ namespace RTE { BITMAP* InventoryMenuGUI::s_CursorBitmap = nullptr; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::CarouselItemBox::GetIconsAndMass(std::vector& itemIcons, float& totalItemMass, const std::vector>* equippedItems) const { if (IsForEquippedItems) { itemIcons.reserve(equippedItems->size()); @@ -51,8 +49,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::Clear() { m_SmallFont = nullptr; m_LargeFont = nullptr; @@ -123,8 +119,6 @@ namespace RTE { m_GUIInventoryItemsScrollbar = nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InventoryMenuGUI::Create(Controller* activityPlayerController, Actor* inventoryActor, MenuMode menuMode) { RTEAssert(activityPlayerController, "No controller sent to InventoryMenuGUI on creation!"); RTEAssert(c_ItemsPerRow % 2 == 1, "Don't you dare use an even number of items per inventory row, you filthy animal!"); @@ -148,7 +142,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void InventoryMenuGUI::Destroy() { destroy_bitmap(m_CarouselBitmap.release()); @@ -157,8 +150,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InventoryMenuGUI::SetupCarouselMode() { for (std::unique_ptr& carouselItemBox: m_CarouselItemBoxes) { carouselItemBox = std::make_unique(); @@ -192,8 +183,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InventoryMenuGUI::SetupFullOrTransferMode() { if (!m_GUIControlManager) { m_GUIControlManager = std::make_unique(); @@ -275,8 +264,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::SetInventoryActor(Actor* newInventoryActor) { m_InventoryActor = newInventoryActor; if (m_InventoryActor) { @@ -288,8 +275,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::SetEnabled(bool enable) { if (!m_MenuController || !m_InventoryActor) { return; @@ -317,16 +302,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool InventoryMenuGUI::EnableIfNotEmpty() { bool shouldEnable = !m_InventoryActorEquippedItems.empty() || (m_InventoryActor && !m_InventoryActor->IsInventoryEmpty()); SetEnabled(shouldEnable); return shouldEnable; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::ClearSelectedItem() { if (m_GUISelectedItem) { m_GUISelectedItem->Button->OnLoseFocus(); @@ -334,8 +315,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::SetSelectedItem(GUIButton* selectedItemButton, MovableObject* selectedItemObject, int inventoryIndex, int equippedItemIndex, bool isBeingDragged) { if (!selectedItemButton || !selectedItemObject || (inventoryIndex < 0 && equippedItemIndex < 0)) { ClearSelectedItem(); @@ -352,8 +331,6 @@ namespace RTE { m_GUISelectedItem->DragHoldCount = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::Update() { if (IsEnabled() && (!m_MenuController || !m_InventoryActor || !g_MovableMan.ValidMO(m_InventoryActor))) { SetEnabled(false); @@ -402,8 +379,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::Draw(BITMAP* targetBitmap, const Vector& targetPos) const { Vector drawPos = m_CenterPos - targetPos; @@ -429,8 +404,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateCarouselMode() { if (!CarouselModeReadyForUse()) { SetupCarouselMode(); @@ -488,8 +461,6 @@ namespace RTE { UpdateCarouselItemBoxSizesAndPositions(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateCarouselItemBoxSizesAndPositions() { float halfMassFontHeight = static_cast(m_SmallFont->GetFontHeight() / 2); int carouselIndex = 0; @@ -546,8 +517,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateFullMode() { if (!FullOrTransferModeReadyForUse()) { SetupFullOrTransferMode(); @@ -619,8 +588,6 @@ namespace RTE { UpdateFullModeNonItemButtonIconsAndHighlightWidths(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateFullModeEquippedItemButtons() { const MovableObject* equippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first : nullptr; const MovableObject* offhandEquippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).second : nullptr; @@ -655,8 +622,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateFullModeScrollbar(const std::deque* inventory) { if (inventory->size() > c_FullViewPageItemLimit) { m_GUIInventoryItemsScrollbar->SetMaximum(static_cast(std::ceil(static_cast(inventory->size() - c_FullViewPageItemLimit) / static_cast(c_ItemsPerRow))) + 1); @@ -684,8 +649,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateFullModeInventoryItemButtons(const std::deque* inventory) { int startIndex = m_GUIInventoryItemsScrollbar->GetValue() * c_ItemsPerRow; int lastPopulatedIndex = static_cast(inventory->size() - 1); @@ -715,8 +678,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateFullModeInformationText(const std::deque* inventory) { if (!m_GUIShowInformationText && m_GUIInformationText->GetVisible()) { m_GUIInformationText->SetVisible(false); @@ -750,8 +711,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateFullModeNonItemButtonIconsAndHighlightWidths() { std::vector> buttonsToCheckIconsFor = { {m_GUIInformationToggleButton, m_GUIInformationToggleButtonIcon}, @@ -777,14 +736,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::UpdateTransferMode() { // TODO Make Transfer mode } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::HandleInput() { if (m_MenuController->IsState(ControlState::PRESS_SECONDARY)) { SetEnabled(false); @@ -842,8 +797,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool InventoryMenuGUI::HandleMouseInput() { int mouseX; int mouseY; @@ -968,8 +921,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::HandleNonMouseInput() { if (!m_NonMouseHighlightedButton || !m_NonMouseHighlightedButton->GetVisible() || (!m_GUIShowEmptyRows && m_NonMouseHighlightedButton->GetParent() == m_GUIInventoryItemsBox && m_InventoryActor->IsInventoryEmpty())) { m_NonMouseHighlightedButton = m_GUIEquippedItemButton; @@ -1022,8 +973,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Directions InventoryMenuGUI::GetNonMouseButtonControllerMovement() { bool pressUp = m_MenuController->IsState(ControlState::PRESS_UP) || m_MenuController->IsState(ControlState::SCROLL_UP); bool pressDown = m_MenuController->IsState(ControlState::PRESS_DOWN) || m_MenuController->IsState(ControlState::SCROLL_DOWN); @@ -1057,8 +1006,6 @@ namespace RTE { return Directions::None; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIButton* InventoryMenuGUI::HandleNonMouseUpInput() { GUIButton* nextButtonToHighlight = nullptr; @@ -1092,8 +1039,6 @@ namespace RTE { return nextButtonToHighlight; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIButton* InventoryMenuGUI::HandleNonMouseDownInput() { GUIButton* nextButtonToHighlight = nullptr; @@ -1131,8 +1076,6 @@ namespace RTE { return nextButtonToHighlight; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIButton* InventoryMenuGUI::HandleNonMouseLeftInput() { GUIButton* nextButtonToHighlight = nullptr; @@ -1164,8 +1107,6 @@ namespace RTE { return nextButtonToHighlight; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUIButton* InventoryMenuGUI::HandleNonMouseRightInput() { GUIButton* nextButtonToHighlight = nullptr; @@ -1204,8 +1145,6 @@ namespace RTE { return nextButtonToHighlight; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::HandleItemButtonPressOrHold(GUIButton* pressedButton, MovableObject* buttonObject, int buttonEquippedItemIndex, bool buttonHeld) { if (buttonHeld && m_GUISelectedItem) { return; @@ -1274,8 +1213,6 @@ namespace RTE { pressedButton->OnLoseFocus(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool InventoryMenuGUI::SwapEquippedItemAndInventoryItem(int equippedItemIndex, int inventoryItemIndex) { if (!m_InventoryActorIsHuman) { g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); @@ -1312,8 +1249,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::ReloadSelectedItem() { // for a in MovableMan.Actors do print(ToAHuman(a).EquippedBGItem:SetOneHanded(true)) end if (!m_InventoryActorIsHuman) { return; @@ -1337,8 +1272,6 @@ namespace RTE { m_GUIReloadButton->OnLoseFocus(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DropSelectedItem(const Vector* dropDirection) { auto LaunchInventoryItem = [this, &dropDirection](MovableObject* itemToLaunch) { Vector itemPosition = m_InventoryActor->GetPos(); @@ -1374,8 +1307,6 @@ namespace RTE { m_GUIDropButton->OnLoseFocus(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DrawCarouselMode(BITMAP* targetBitmap, const Vector& drawPos) const { clear_to_color(m_CarouselBitmap.get(), g_MaskColor); clear_to_color(m_CarouselBGBitmap.get(), g_MaskColor); @@ -1428,8 +1359,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DrawCarouselItemBoxBackground(const CarouselItemBox& itemBoxToDraw) const { auto DrawBox = [](BITMAP* targetBitmap, const Vector& boxTopLeftCorner, const Vector& boxBottomRightCorner, int color, bool roundedLeftSide, bool roundedRightSide) { if (roundedLeftSide) { @@ -1452,8 +1381,6 @@ namespace RTE { DrawBox(m_CarouselBGBitmap.get(), itemBoxToDraw.Pos + (itemBoxToDraw.RoundedAndBorderedSides.first ? m_CarouselBackgroundBoxBorderSize : Vector(0, m_CarouselBackgroundBoxBorderSize.GetY())), itemBoxToDraw.Pos + itemBoxToDraw.CurrentSize - spriteZeroIndexSizeOffset - (itemBoxToDraw.RoundedAndBorderedSides.second ? m_CarouselBackgroundBoxBorderSize : Vector(0, m_CarouselBackgroundBoxBorderSize.GetY())), m_CarouselBackgroundBoxColor, itemBoxToDraw.RoundedAndBorderedSides.first, itemBoxToDraw.RoundedAndBorderedSides.second); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DrawCarouselItemBoxForeground(const CarouselItemBox& itemBoxToDraw, AllegroBitmap* carouselAllegroBitmap) const { std::vector itemIcons; float totalItemMass = 0; @@ -1489,8 +1416,6 @@ namespace RTE { m_SmallFont->DrawAligned(carouselAllegroBitmap, itemBoxToDraw.IconCenterPosition.GetFloorIntX(), itemBoxToDraw.IconCenterPosition.GetFloorIntY() - ((itemBoxToDraw.CurrentSize.GetFloorIntY() + m_SmallFont->GetFontHeight()) / 2) + 1, massString.c_str(), GUIFont::Centre); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::DrawFullMode(BITMAP* targetBitmap, const Vector& drawPos) const { m_GUITopLevelBox->SetPositionAbs(drawPos.GetFloorIntX(), drawPos.GetFloorIntY()); diff --git a/Source/Menus/InventoryMenuGUI.h b/Source/Menus/InventoryMenuGUI.h index 30c6783a81..1f7ce95143 100644 --- a/Source/Menus/InventoryMenuGUI.h +++ b/Source/Menus/InventoryMenuGUI.h @@ -22,15 +22,11 @@ namespace RTE { class GUIButton; class GUIScrollbar; - /// /// A GUI menu for managing inventories. - /// class InventoryMenuGUI { public: - /// /// Enumeration for the modes an InventoryMenuGUI can have. - /// enum class MenuMode { Carousel, Full, @@ -38,137 +34,97 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate an InventoryMenuGUI object in system memory. Create() should be called before using the object. - /// InventoryMenuGUI() { Clear(); } - /// /// Makes the InventoryMenuGUI object ready for use. - /// - /// A pointer to a Controller which will control this Menu. Ownership is NOT transferred! - /// The Actor whose inventory this GUI will display. Ownership is NOT transferred! - /// The mode this menu should use when it's enabled. Defaults to Carousel. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param activityPlayerController A pointer to a Controller which will control this Menu. Ownership is NOT transferred! + /// @param inventoryActor The Actor whose inventory this GUI will display. Ownership is NOT transferred! + /// @param menuMode The mode this menu should use when it's enabled. Defaults to Carousel. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(Controller* activityPlayerController, Actor* inventoryActor = nullptr, MenuMode menuMode = MenuMode::Carousel); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up an InventoryMenuGUI object before deletion from system memory. - /// ~InventoryMenuGUI() { Destroy(); } - /// /// Destroys and resets the InventoryMenuGUI object. - /// void Destroy(); #pragma endregion #pragma region Getters and Setters - /// /// Sets the controller used by this. The ownership of the controller is NOT transferred! - /// - /// The new controller for this menu. Ownership is NOT transferred! + /// @param controller The new controller for this menu. Ownership is NOT transferred! void SetController(Controller* controller) { m_MenuController = controller; } - /// /// Gets the Actor whose inventory this GUI will display. The ownership of the Actor is NOT transferred! - /// - /// The Actor whose inventory this GUI will display. Ownership is NOT transferred! + /// @return The Actor whose inventory this GUI will display. Ownership is NOT transferred! const Actor* GetInventoryActor() const { return m_InventoryActor; } - /// /// Sets the Actor whose inventory this GUI will display. The ownership of the Actor is NOT transferred! - /// - /// The new Actor whose inventory this GUI will display. Ownership is NOT transferred! + /// @param actor The new Actor whose inventory this GUI will display. Ownership is NOT transferred! void SetInventoryActor(Actor* newInventoryActor); - /// /// Gets the MenuMode this InventoryMenuGUI is currently in. - /// - /// The current MenuMode of this InventoryMenuGUI. + /// @return The current MenuMode of this InventoryMenuGUI. MenuMode GetMenuMode() const { return m_MenuMode; } - /// /// Sets the MenuMode for this InventoryMenuGUI to be in. - /// - /// The new MenuMode of this InventoryMenuGUI. + /// @param newMenuMode The new MenuMode of this InventoryMenuGUI. void SetMenuMode(MenuMode newMenuMode) { m_MenuMode = newMenuMode; } - /// /// Gets whether the menu is enabled or not. - /// - /// Whether the menu is enabled. + /// @return Whether the menu is enabled. bool IsEnabled() const { return m_EnabledState == EnabledState::Enabled || m_EnabledState == EnabledState::Enabling; } - /// /// Gets whether the menu is enabled and not in carousel mode or not. - /// - /// Whether the menu is enabled and not in carousel mode + /// @return Whether the menu is enabled and not in carousel mode bool IsEnabledAndNotCarousel() const { return IsEnabled() && m_MenuMode != MenuMode::Carousel; } - /// /// Gets whether the menu is in the process of enabling or disabling. - /// - /// + /// @return bool IsEnablingOrDisabling() const { return m_EnabledState == EnabledState::Enabling || m_EnabledState == EnabledState::Disabling; } - /// /// Gets whether the menu is at all visible or not. - /// - /// Whether the menu is visible. + /// @return Whether the menu is visible. bool IsVisible() const { return m_EnabledState != EnabledState::Disabled; } - /// /// Enables or disables the menu and animates it in and out of view. - /// - /// Whether to enable or disable the menu. + /// @param enable Whether to enable or disable the menu. void SetEnabled(bool enable); - /// /// Enables or disables the InventoryMenuGUI based on whether or not it's empty (i.e. its actor has equipped or inventory items). - /// - /// Whether it tried to enable InventoryMenuGUI (true) or disable it (false). + /// @return Whether it tried to enable InventoryMenuGUI (true) or disable it (false). bool EnableIfNotEmpty(); #pragma endregion #pragma region Full And Transfer Mode Getters and Setters - /// /// Clears the selected item for this InventoryMenuGUI. - /// void ClearSelectedItem(); - /// /// Sets the selected item for this InventoryMenuGUI. /// If the button pointer or object pointer are null, or both indices are null, the selected item will instead be cleared. - /// - /// A pointer to the GUIButton that is selected. - /// A pointer to the MovableObject that is selected. - /// The index of this selected item in the displayed inventory. -1 means the item is not in the inventory. - /// The index of this selected item in the vector of equipped items. -1 means the item is not in the inventory. - /// Whether or not the selected item is being dragged. + /// @param selectedItemButton A pointer to the GUIButton that is selected. + /// @param selectedItemObject A pointer to the MovableObject that is selected. + /// @param inventoryIndex The index of this selected item in the displayed inventory. -1 means the item is not in the inventory. + /// @param equippedItemIndex The index of this selected item in the vector of equipped items. -1 means the item is not in the inventory. + /// @param isBeingDragged Whether or not the selected item is being dragged. void SetSelectedItem(GUIButton* selectedItemButton, MovableObject* selectedItemObject, int inventoryIndex, int equippedItemIndex, bool isBeingDragged); #pragma endregion #pragma region Updating - /// /// Updates the state of this GUI each frame. - /// void Update(); - /// /// Draws the GUI. - /// - /// A pointer to a BITMAP to draw on. Generally a screen BITMAP. - /// The absolute position of the target bitmap's upper left corner in the scene. + /// @param targetBitmap A pointer to a BITMAP to draw on. Generally a screen BITMAP. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. void Draw(BITMAP* targetBitmap, const Vector& targetPos = Vector()) const; #pragma endregion private: - /// /// A struct containing all information required to drawn and animate a carousel item box in Carousel MenuMode. - /// struct CarouselItemBox { MovableObject* Item; //!< A pointer to the item being displayed in the CarouselItemBox. bool IsForEquippedItems; //!< Whether or not this CarouselItemBox is for displaying equipped items. @@ -178,19 +134,15 @@ namespace RTE { Vector IconCenterPosition; //!< The calculated position for the center of the icon this CarouselItemBox is displaying. std::pair RoundedAndBorderedSides; //!< Whether the left and right sides, respectively, of this CarouselItemBox should have rounded corners and a border. - /// /// Fills the passed in vector of Bitmaps and float with the appropriate graphical icons and mass for the Item of this CarouselItemBox. /// If this CarouselItemBox IsForEquippedItem, it will instead used the passed in pointer to the vector of equipped items. - /// - /// A vector of Bitmaps to be filled in with the icon(s) for this CarouselItemBox's item(s). - /// A float to be filled in with the total mass of this CarouselItemBox's item(s). - /// A pointer to the vector of sets of equipped items to be used if the CarouselItemBox IsForEquippedItem. + /// @param itemIcons A vector of Bitmaps to be filled in with the icon(s) for this CarouselItemBox's item(s). + /// @param totalItemMass A float to be filled in with the total mass of this CarouselItemBox's item(s). + /// @param equippedItems A pointer to the vector of sets of equipped items to be used if the CarouselItemBox IsForEquippedItem. void GetIconsAndMass(std::vector& itemIcons, float& totalItemMass, const std::vector>* equippedItems) const; }; - /// /// A struct containing all information required to describe a selected item in Full/Transfer MenuMode. - /// struct GUISelectedItem { GUIButton* Button; //!< A pointer to the button for this GUISelectedItem. MovableObject* Object; //!< A pointer to the MovableObject for this GUISelectedItem. Should always match up with what the Button is displaying. @@ -199,16 +151,12 @@ namespace RTE { bool IsBeingDragged; //!< Whether or not the GUISelectedItem is currently being dragged. int DragHoldCount; //!< How long dragging has been held for, used to determine if the GUISelectedItem was actually dragged or just clicked. - /// /// Whether this selected item was being dragged for long enough that it matters. This helps make dragging not cause problems during instant clicks and releases. - /// - /// Whether this selected item was being dragged for long enough that it matters. + /// @return Whether this selected item was being dragged for long enough that it matters. bool DragWasHeldForLongEnough() const { return IsBeingDragged && DragHoldCount > 10; } }; - /// /// Enumeration for enabled states when enabling/disabling the InventoryMenuGUI. - /// enum class EnabledState { Enabling, Enabled, @@ -216,9 +164,7 @@ namespace RTE { Disabled }; - /// /// Enumeration for which direction an inventory is being swapped in. - /// enum class CarouselAnimationDirection { Left = -1, None, @@ -289,9 +235,7 @@ namespace RTE { const Icon* m_GUIDropButtonIcon; //!< A pointer to the PresetMan pie icon for dropping items, used here for the drop button. Not Owned here. std::vector> m_GUIInventoryItemButtons; //!< A vector of pairs of MovableObject pointers and GUIButton pointers, connecting inventory GUIButtons to their corresponding MovableObjects. - /// /// GUI elements that make up the full mode InventoryMenuGUI. - /// std::unique_ptr m_GUIControlManager; std::unique_ptr m_GUIScreen; std::unique_ptr m_GUIInput; @@ -308,198 +252,136 @@ namespace RTE { GUIScrollbar* m_GUIInventoryItemsScrollbar; #pragma region Create Breakdown - /// /// Gets whether the InventoryMenuGUI is ready to be activated in Carousel MenuMode. If it's not, that mode should be setup. - /// - /// Whether or not the InventoryMenuGUI is ready to be activated in carousel MenuMode. + /// @return Whether or not the InventoryMenuGUI is ready to be activated in carousel MenuMode. bool CarouselModeReadyForUse() const { return m_CarouselBitmap && m_CarouselBGBitmap; } - /// /// Makes the InventoryMenuGUI object ready for Carousel MenuMode use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int SetupCarouselMode(); - /// /// Gets whether the InventoryMenuGUI is ready to be activated in Full or Transfer MenuMode. If it's not, that mode should be setup. - /// - /// Whether or not the InventoryMenuGUI is ready to be activated in Full or Transfer MenuMode. + /// @return Whether or not the InventoryMenuGUI is ready to be activated in Full or Transfer MenuMode. bool FullOrTransferModeReadyForUse() const { return m_GUIControlManager && m_GUIScreen && m_GUIInput; } - /// /// Makes the InventoryMenuGUI object ready for Full or Transfer MenuMode use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int SetupFullOrTransferMode(); #pragma endregion #pragma region Update Breakdown - /// /// Handles MenuMode specific updating for when the InventoryMenuGUI is in Carousel MenuMode. - /// void UpdateCarouselMode(); - /// /// Handles sorting out positions and sizes of CarouselItemBoxes when in Carousel MenuMode. - /// void UpdateCarouselItemBoxSizesAndPositions(); - /// /// Handles MenuMode specific updating for when the InventoryMenuGUI is in Full MenuMode. - /// void UpdateFullMode(); - /// /// Handles visibility and content for equipped item buttons in Full MenuMode. Does not deal with button input. - /// void UpdateFullModeEquippedItemButtons(); - /// /// Handles hiding and showing the scrollbar, and calculating everything related to it in Full MenuMode. - /// - /// A pointer to the inventory this InventoryMenuGUI is displaying. + /// @param inventory A pointer to the inventory this InventoryMenuGUI is displaying. void UpdateFullModeScrollbar(const std::deque* inventory); - /// /// Handles content and enabled status for inventory item buttons in Full MenuMode. Does not deal with button input. - /// - /// A pointer to the inventory this InventoryMenuGUI is displaying. + /// @param inventory A pointer to the inventory this InventoryMenuGUI is displaying. void UpdateFullModeInventoryItemButtons(const std::deque* inventory); - /// /// Handles everything for displaying information text in Full MenuMode. - /// - /// A pointer to the inventory this InventoryMenuGUI is displaying. + /// @param inventory A pointer to the inventory this InventoryMenuGUI is displaying. void UpdateFullModeInformationText(const std::deque* inventory); - /// /// Handles updating icons and widths to support higlighting, for non-item buttons in Full MenuMode. - /// void UpdateFullModeNonItemButtonIconsAndHighlightWidths(); - /// /// Handles MenuMode specific updating for when the InventoryMenuGUI is in Transfer MenuMode. - /// void UpdateTransferMode(); #pragma endregion #pragma region GUI Input Handling - /// /// Player input handling for all devices. - /// void HandleInput(); - /// /// Player mouse input event handling of the GUIControls of this InventoryMenuGUI. - /// - /// Whether the mouse was released this frame after being held long enough to count, useful to prevent duplicate button handling when releasing the left click. + /// @return Whether the mouse was released this frame after being held long enough to count, useful to prevent duplicate button handling when releasing the left click. bool HandleMouseInput(); - /// /// Player keyboard or gamepad input event handling of the GUIControls of this InventoryMenuGUI. - /// void HandleNonMouseInput(); - /// /// Gets any keyboard or gamepad directional input. - /// - /// The direction of found input. Priority matches ordering of the Directions enumeration. + /// @return The direction of found input. Priority matches ordering of the Directions enumeration. Directions GetNonMouseButtonControllerMovement(); - /// /// Breakdown of HandleNonMouseInput for handling pressing/holding up. - /// - /// The next button to highlight, based on input handling. + /// @return The next button to highlight, based on input handling. GUIButton* HandleNonMouseUpInput(); - /// /// Breakdown of HandleNonMouseInput for handling pressing/holding down. - /// - /// The next button to highlight, based on input handling. + /// @return The next button to highlight, based on input handling. GUIButton* HandleNonMouseDownInput(); - /// /// Breakdown of HandleNonMouseInput for handling pressing/holding left. - /// - /// The next button to highlight, based on input handling. + /// @return The next button to highlight, based on input handling. GUIButton* HandleNonMouseLeftInput(); - /// /// Breakdown of HandleNonMouseInput for handling pressing/holding right. - /// - /// The next button to highlight, based on input handling. + /// @return The next button to highlight, based on input handling. GUIButton* HandleNonMouseRightInput(); - /// /// Handles item button press command events from the GUIControls of this InventoryMenuGUI by selecting/de-selecting the corresponding item. - /// - /// A pointer to the GUIButton that was pressed. - /// A pointer to the MovableObject the GUIButton represents. - /// The index of this button in the vector of equipped items if applicable. The default value of -1 means it's not an equipped item. - /// Whether or not the button was held. + /// @param pressedButton A pointer to the GUIButton that was pressed. + /// @param buttonObject A pointer to the MovableObject the GUIButton represents. + /// @param buttonEquippedItemIndex The index of this button in the vector of equipped items if applicable. The default value of -1 means it's not an equipped item. + /// @param buttonHeld Whether or not the button was held. void HandleItemButtonPressOrHold(GUIButton* pressedButton, MovableObject* buttonObject, int buttonEquippedItemIndex = -1, bool buttonHeld = false); #pragma endregion #pragma region GUI Button Actions // TODO implement swap mode - /// /// Handles set swap button press command events from the GUIControls of this InventoryMenuGUI. - /// // void SwapEquippedItemSet() {} - /// /// Swaps the equipped item at the given equipped item index with one in the inventory Actor's inventory at the given inventory item index. /// Accounts for either index pointing to empty buttons and any other potential complications. - /// - /// The index of the equipped item being swapped out. - /// The index in the inventory of the item being swapped in. - /// Whether the inventory item at the given inventory item index was successfully equipped to the given equipped item index. + /// @param equippedItemIndex The index of the equipped item being swapped out. + /// @param inventoryItemIndex The index in the inventory of the item being swapped in. + /// @return Whether the inventory item at the given inventory item index was successfully equipped to the given equipped item index. bool SwapEquippedItemAndInventoryItem(int equippedItemIndex, int inventoryItemIndex); - /// /// Reloads the selected item if it is equipped, or swaps to it and then reloads it if it isn't. - /// void ReloadSelectedItem(); - /// /// Drops the selected item, rotating its drop velocity in the direction of the passed in dropDirection Vector. - /// - /// A pointer to a Vector containing the direction the selected item should be dropped in. Nullptr means standard dropping will be used. + /// @param dropDirection A pointer to a Vector containing the direction the selected item should be dropped in. Nullptr means standard dropping will be used. void DropSelectedItem(const Vector* dropDirection = nullptr); #pragma endregion #pragma region Draw Breakdown - /// /// Draws the InventoryMenuGUI when it's in Carousel MenuMode. - /// - /// A pointer to a BITMAP to draw on. Generally a screen BITMAP. - /// The position at which to draw the carousel. + /// @param targetBitmap A pointer to a BITMAP to draw on. Generally a screen BITMAP. + /// @param drawPos The position at which to draw the carousel. void DrawCarouselMode(BITMAP* targetBitmap, const Vector& drawPos) const; - /// /// Draws the InventoryMenuGUI when it's in Full MenuMode. - /// - /// A pointer to a BITMAP to draw on. Generally a screen BITMAP. - /// The position at which to draw the GUI. + /// @param targetBitmap A pointer to a BITMAP to draw on. Generally a screen BITMAP. + /// @param drawPos The position at which to draw the GUI. void DrawFullMode(BITMAP* targetBitmap, const Vector& drawPos) const; - /// /// Draws the specified CarouselItemBox's background to the carousel background Bitmap. - /// - /// The CarouselItemBox to draw. + /// @param itemBoxToDraw The CarouselItemBox to draw. void DrawCarouselItemBoxBackground(const CarouselItemBox& itemBoxToDraw) const; - /// /// Draws the specified CarouselItemBox's item(s) and mass text to the carousel Bitmap. - /// - /// The CarouselItemBox to draw. - /// An AllegroBitmap of the bitmap the CarouselItemBox should draw its foreground to. Used for drawing mass strings, and predefined to avoid needless creation. + /// @param itemBoxToDraw The CarouselItemBox to draw. + /// @param carouselAllegroBitmap An AllegroBitmap of the bitmap the CarouselItemBox should draw its foreground to. Used for drawing mass strings, and predefined to avoid needless creation. void DrawCarouselItemBoxForeground(const CarouselItemBox& itemBoxToDraw, AllegroBitmap* carouselAllegroBitmap) const; #pragma endregion - /// /// Clears all the member variables of this InventoryMenuGUI, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/LoadingScreen.cpp b/Source/Menus/LoadingScreen.cpp index 06a6efe3f9..e638e3639a 100644 --- a/Source/Menus/LoadingScreen.cpp +++ b/Source/Menus/LoadingScreen.cpp @@ -14,8 +14,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::Clear() { m_LoadingLogWriter = nullptr; m_LoadingSplashBitmap = nullptr; @@ -24,8 +22,6 @@ namespace RTE { m_ProgressListboxPosY = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool progressReportDisabled) { GUIControlManager loadingScreenManager; RTEAssert(loadingScreenManager.Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "LoadingScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/LoadingScreenSkin.ini"); @@ -59,8 +55,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::CreateLoadingSplash(int xOffset) { if (m_LoadingSplashBitmap) { destroy_bitmap(m_LoadingSplashBitmap); @@ -79,8 +73,6 @@ namespace RTE { loadingSplash.Draw(m_LoadingSplashBitmap, loadingSplashTargetBox); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::CreateProgressReportListbox(GUIControlManager* parentControlManager) { dynamic_cast(parentControlManager->GetControl("root"))->SetSize(g_WindowMan.GetResX(), g_WindowMan.GetResY()); GUIListBox* listBox = dynamic_cast(parentControlManager->GetControl("ProgressBox")); @@ -104,8 +96,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::Destroy() { destroy_bitmap(m_LoadingSplashBitmap); if (m_ProgressListboxBitmap) { @@ -114,8 +104,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::LoadingSplashProgressReport(const std::string& reportString, bool newItem) { if (System::IsLoggingToCLI()) { System::PrintLoadingToCLI(reportString, newItem); @@ -149,8 +137,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LoadingScreen::DrawLoadingSplash() { draw_sprite(g_FrameMan.GetBackBuffer32(), m_LoadingSplashBitmap, 0, 0); } diff --git a/Source/Menus/LoadingScreen.h b/Source/Menus/LoadingScreen.h index 68d15faf44..7c4df7eaed 100644 --- a/Source/Menus/LoadingScreen.h +++ b/Source/Menus/LoadingScreen.h @@ -12,58 +12,42 @@ namespace RTE { class GUIControlManager; class Writer; - /// /// Handling for the loading screen composition and loading progress box when starting the game. - /// class LoadingScreen : public Singleton { public: #pragma region Creation - /// /// Constructor method used to instantiate a LoadingScreen object in system memory. - /// LoadingScreen() { Clear(); } - /// /// Makes the LoadingScreen object ready for use. - /// - /// Pointer to a GUIScreen interface that will be used by this LoadingScreen's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this LoadingScreen's GUIControlManager. Ownership is NOT transferred! - /// Whether the loading screen progress report is disabled meaning GUI elements and adjustments relevant to it can be skipped. + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this LoadingScreen's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this LoadingScreen's GUIControlManager. Ownership is NOT transferred! + /// @param progressReportDisabled Whether the loading screen progress report is disabled meaning GUI elements and adjustments relevant to it can be skipped. void Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool progressReportDisabled); - /// /// Creates the loading splash screen and draws the composed frame to the LoadingSplashBitmap. - /// - /// Horizontal offset of the loading splash screen. + /// @param xOffset Horizontal offset of the loading splash screen. void CreateLoadingSplash(int xOffset = 0); - /// /// Creates the GUIListBox that the progress report will be drawn to, if not disabled through the settings file to speed up loading times. /// As it turned out, a massive amount of time is spent updating the GUI control and flipping the frame buffers. - /// - /// Pointer to the parent GUIControlManager which owns all the GUIControls of this LoadingScreen. Ownership is NOT transferred! + /// @param parentControlManager Pointer to the parent GUIControlManager which owns all the GUIControls of this LoadingScreen. Ownership is NOT transferred! void CreateProgressReportListbox(GUIControlManager* parentControlManager); #pragma endregion #pragma region Destruction - /// /// Destroys and resets (through Clear()) the LoadingScreen object. - /// void Destroy(); #pragma endregion #pragma region Concrete Methods - /// /// Updates the loading progress report and draws it to the screen if not disabled through the settings file. - /// - /// The string to print in the report and log. - /// Whether to start a new line in the log writer and to scroll the bitmap. + /// @param reportString The string to print in the report and log. + /// @param newItem Whether to start a new line in the log writer and to scroll the bitmap. static void LoadingSplashProgressReport(const std::string& reportString, bool newItem = false); - /// /// Draws the loading splash to the screen. - /// void DrawLoadingSplash(); #pragma endregion @@ -75,9 +59,7 @@ namespace RTE { int m_ProgressListboxPosX; //!< Position of the progress report box on X axis. int m_ProgressListboxPosY; //!< Position of the progress report box on Y axis. - /// /// Clears all the member variables of this LoadingScreen, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/MainMenuGUI.cpp b/Source/Menus/MainMenuGUI.cpp index 7582440fe8..93d8bfe0d1 100644 --- a/Source/Menus/MainMenuGUI.cpp +++ b/Source/Menus/MainMenuGUI.cpp @@ -20,8 +20,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::Clear() { m_RootBoxMaxWidth = 0; @@ -54,8 +52,6 @@ namespace RTE { m_MainScreenPrevHoveredButtonIndex = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { m_MainMenuScreenGUIControlManager = std::make_unique(); RTEAssert(m_MainMenuScreenGUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuScreenSkin.ini"); @@ -90,8 +86,6 @@ namespace RTE { SetActiveMenuScreen(g_WindowMan.ResolutionChanged() ? MenuScreen::SettingsScreen : MenuScreen::MainScreen, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::CreateMainScreen() { m_MainMenuScreens[MenuScreen::MainScreen] = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("MainScreen")); m_MainMenuScreens[MenuScreen::MainScreen]->CenterInParent(true, false); @@ -123,8 +117,6 @@ namespace RTE { m_VersionLabel->SetPositionAbs(10, g_WindowMan.GetResY() - m_VersionLabel->GetTextHeight() - 5); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::CreateMetaGameNoticeScreen() { m_MainMenuScreens[MenuScreen::MetaGameNoticeScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("MetaScreen")); m_MainMenuScreens[MenuScreen::MetaGameNoticeScreen]->CenterInParent(true, false); @@ -133,8 +125,6 @@ namespace RTE { m_MainMenuButtons[MenuButton::MetaGameContinueButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonContinue")); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::CreateEditorsScreen() { m_MainMenuScreens[MenuScreen::EditorScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("EditorScreen")); m_MainMenuScreens[MenuScreen::EditorScreen]->CenterInParent(true, false); @@ -146,8 +136,6 @@ namespace RTE { m_MainMenuButtons[MenuButton::ActorEditorButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("ButtonActorEditor")); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::CreateCreditsScreen() { m_MainMenuScreens[MenuScreen::CreditsScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("CreditsScreen")); m_MainMenuScreens[MenuScreen::CreditsScreen]->Resize(m_MainMenuScreens[MenuScreen::CreditsScreen]->GetWidth(), g_WindowMan.GetResY()); @@ -173,8 +161,6 @@ namespace RTE { m_CreditsTextLabel->ResizeHeightToFit(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::CreateQuitScreen() { m_MainMenuScreens[MenuScreen::QuitScreen] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("QuitConfirmBox")); m_MainMenuScreens[MenuScreen::QuitScreen]->CenterInParent(true, false); @@ -183,8 +169,6 @@ namespace RTE { m_MainMenuButtons[MenuButton::QuitCancelButton] = dynamic_cast(m_SubMenuScreenGUIControlManager->GetControl("QuitCancelButton")); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HideAllScreens() { for (GUICollectionBox* menuScreen: m_MainMenuScreens) { if (menuScreen) { @@ -194,8 +178,6 @@ namespace RTE { m_MenuScreenChange = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::SetActiveMenuScreen(MenuScreen screenToShow, bool playButtonPressSound) { if (screenToShow != m_ActiveMenuScreen) { HideAllScreens(); @@ -213,8 +195,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::ShowMainScreen() { m_VersionLabel->SetVisible(true); @@ -227,8 +207,6 @@ namespace RTE { m_MenuScreenChange = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::ShowMetaGameNoticeScreen() { m_MainMenuScreens[MenuScreen::MetaGameNoticeScreen]->SetVisible(true); m_MainMenuScreens[MenuScreen::MetaGameNoticeScreen]->GUIPanel::AddChild(m_MainMenuButtons[MenuButton::BackToMainButton]); @@ -252,8 +230,6 @@ namespace RTE { m_MenuScreenChange = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::ShowEditorsScreen() { m_MainMenuScreens[MenuScreen::EditorScreen]->SetVisible(true); m_MainMenuScreens[MenuScreen::EditorScreen]->GUIPanel::AddChild(m_MainMenuButtons[MenuButton::BackToMainButton]); @@ -264,8 +240,6 @@ namespace RTE { m_MenuScreenChange = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::ShowCreditsScreen() { m_MainMenuScreens[MenuScreen::CreditsScreen]->SetVisible(true); m_MainMenuScreens[MenuScreen::CreditsScreen]->GUIPanel::AddChild(m_MainMenuButtons[MenuButton::BackToMainButton]); @@ -281,8 +255,6 @@ namespace RTE { m_MenuScreenChange = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::ShowQuitScreenOrQuit() { if (m_ActiveMenuScreen != MenuScreen::QuitScreen && g_ActivityMan.GetActivity() && (g_ActivityMan.GetActivity()->GetActivityState() == Activity::Running || g_ActivityMan.GetActivity()->GetActivityState() == Activity::Editing)) { SetActiveMenuScreen(MenuScreen::QuitScreen); @@ -293,8 +265,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::ShowAndBlinkResumeButton() { if (!m_MainMenuButtons[MenuButton::ResumeButton]->GetVisible()) { m_ResumeButtonBlinkTimer.Reset(); @@ -311,8 +281,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MainMenuGUI::RollCredits() { int scrollDuration = m_CreditsTextLabel->GetHeight() * 50; float scrollDist = static_cast(m_CreditsScrollPanel->GetHeight() + m_CreditsTextLabel->GetHeight()); @@ -325,8 +293,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MainMenuGUI::MainMenuUpdateResult MainMenuGUI::Update() { m_UpdateResult = MainMenuUpdateResult::NoEvent; @@ -387,8 +353,6 @@ namespace RTE { return m_UpdateResult; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HandleBackNavigation(bool backButtonPressed) { if ((!m_ActiveDialogBox || m_ActiveDialogBox == m_MainMenuScreens[MenuScreen::QuitScreen]) && (backButtonPressed || g_UInputMan.KeyPressed(SDLK_ESCAPE))) { if (m_ActiveMenuScreen != MenuScreen::MainScreen) { @@ -411,8 +375,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool MainMenuGUI::HandleInputEvents() { if (m_ActiveMenuScreen == MenuScreen::MainScreen) { int mouseX = 0; @@ -451,8 +413,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HandleMainScreenInputEvents(const GUIControl* guiEventControl) { if (guiEventControl == m_MainMenuButtons[MenuButton::MetaGameButton]) { if (!m_MetaGameNoticeShown) { @@ -486,8 +446,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HandleMetaGameNoticeScreenInputEvents(const GUIControl* guiEventControl) { if (guiEventControl == m_MainMenuButtons[MenuButton::PlayTutorialButton]) { m_UpdateResult = MainMenuUpdateResult::ActivityStarted; @@ -498,8 +456,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HandleEditorsScreenInputEvents(const GUIControl* guiEventControl) { std::string editorToStart; if (guiEventControl == m_MainMenuButtons[MenuButton::SceneEditorButton]) { @@ -521,8 +477,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::HandleQuitScreenInputEvents(const GUIControl* guiEventControl) { if (guiEventControl == m_MainMenuButtons[MenuButton::QuitConfirmButton]) { m_UpdateResult = MainMenuUpdateResult::Quit; @@ -532,8 +486,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::UpdateMainScreenHoveredButton(const GUIButton* hoveredButton) { int hoveredButtonIndex = -1; if (hoveredButton) { @@ -554,8 +506,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void MainMenuGUI::Draw() { // Early return to avoid single frame flicker when title screen goes into transition from the meta notice screen to meta config screen. if (m_UpdateResult == MainMenuUpdateResult::MetaGameStarted) { diff --git a/Source/Menus/MainMenuGUI.h b/Source/Menus/MainMenuGUI.h index 43583e2156..5d34ff6967 100644 --- a/Source/Menus/MainMenuGUI.h +++ b/Source/Menus/MainMenuGUI.h @@ -17,15 +17,11 @@ namespace RTE { class GUILabel; class GUIControl; - /// /// Handling for the main menu screen composition and sub-menu interaction. - /// class MainMenuGUI { public: - /// /// Enumeration for the results of the MainMenuGUI input and event update. - /// enum class MainMenuUpdateResult { NoEvent, MetaGameStarted, @@ -38,41 +34,31 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a MainMenuGUI object in system memory and makes it ready for use. - /// - /// Pointer to a GUIScreen interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! MainMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { Clear(); Create(guiScreen, guiInput); } - /// /// Makes the MainMenuGUI object ready for use. - /// - /// Pointer to a GUIScreen interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this MainMenuGUI's GUIControlManager. Ownership is NOT transferred! void Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput); #pragma endregion #pragma region Concrete Methods - /// /// Updates the MainMenuGUI state. - /// - /// The result of the MainMenuGUI input and event update. See MainMenuUpdateResult enumeration. + /// @return The result of the MainMenuGUI input and event update. See MainMenuUpdateResult enumeration. MainMenuUpdateResult Update(); - /// /// Draws the MainMenuGUI to the screen. - /// void Draw(); #pragma endregion private: - /// /// Enumeration for the different sub-menu screens of the main menu. - /// enum MenuScreen { MainScreen, MetaGameNoticeScreen, @@ -85,9 +71,7 @@ namespace RTE { ScreenCount }; - /// /// Enumeration for all the different buttons of the main menu and sub-menus. - /// enum MenuButton { MetaGameButton, ScenarioButton, @@ -138,9 +122,7 @@ namespace RTE { GUIButton* m_MainScreenHoveredButton; //!< The currently hovered main screen button. int m_MainScreenPrevHoveredButtonIndex; //!< The index of the previously hovered main screen button in the main menu button array. - /// /// GUI elements that compose the main menu screen. - /// GUILabel* m_VersionLabel; GUILabel* m_CreditsTextLabel; GUICollectionBox* m_CreditsScrollPanel; @@ -148,129 +130,85 @@ namespace RTE { std::array m_MainMenuButtons; #pragma region Create Breakdown - /// /// Creates all the elements that compose the main menu screen. - /// void CreateMainScreen(); - /// /// Creates all the elements that compose the MetaGame notice menu screen. - /// void CreateMetaGameNoticeScreen(); - /// /// Creates all the elements that compose the editor selection menu screen. - /// void CreateEditorsScreen(); - /// /// Creates all the elements that compose the credits menu screen. - /// void CreateCreditsScreen(); - /// /// Creates all the elements that compose the quit confirmation menu screen. - /// void CreateQuitScreen(); #pragma endregion #pragma region Menu Screen Handling - /// /// Hides all main menu screens. - /// void HideAllScreens(); - /// /// Sets the MainMenuGUI to display a menu screen. - /// - /// Which menu screen to display. See MenuScreen enumeration. - /// Whether to play a sound if the menu screen change is triggered by a button press. + /// @param screenToShow Which menu screen to display. See MenuScreen enumeration. + /// @param playButtonPressSound Whether to play a sound if the menu screen change is triggered by a button press. void SetActiveMenuScreen(MenuScreen screenToShow, bool playButtonPressSound = true); - /// /// Makes the main menu screen visible to be interacted with by the player. - /// void ShowMainScreen(); - /// /// Makes the MetaGame notice menu screen visible to be interacted with by the player. - /// void ShowMetaGameNoticeScreen(); - /// /// Makes the editor selection menu screen visible to be interacted with by the player. - /// void ShowEditorsScreen(); - /// /// Makes the credits menu screen visible to be interacted with by the player and resets the scrolling timer for the credits. - /// void ShowCreditsScreen(); - /// /// Makes the quit confirmation menu screen visible to be interacted with by the player if a game is in progress, or immediately sets the UpdateResult to Quit if not. - /// void ShowQuitScreenOrQuit(); - /// /// Makes the resume game button visible to be interacted with by the player if a game is in progress and animates it (blinking). - /// void ShowAndBlinkResumeButton(); - /// /// Progresses the credits scrolling. - /// - /// Whether the credits finished scrolling. + /// @return Whether the credits finished scrolling. bool RollCredits(); #pragma endregion #pragma region Update Breakdown - /// /// Handles returning to the main menu from one of the sub-menus if the player requested to return via the back button or the esc key. Also handles closing active dialog boxes with the esc key. - /// - /// Whether the player requested to return to the main menu from one of the sub-menus via back button. + /// @param backButtonPressed Whether the player requested to return to the main menu from one of the sub-menus via back button. void HandleBackNavigation(bool backButtonPressed); - /// /// Handles the player interaction with the MainMenuGUI GUI elements. - /// - /// Whether the player requested to return to the main menu from one of the sub-menus. + /// @return Whether the player requested to return to the main menu from one of the sub-menus. bool HandleInputEvents(); - /// /// Handles the player interaction with the main screen GUI elements. - /// - /// Pointer to the GUI element that the player interacted with. + /// @param guiEventControl Pointer to the GUI element that the player interacted with. void HandleMainScreenInputEvents(const GUIControl* guiEventControl); - /// /// Handles the player interaction with the MetaGame notice screen GUI elements. - /// - /// Pointer to the GUI element that the player interacted with. + /// @param guiEventControl Pointer to the GUI element that the player interacted with. void HandleMetaGameNoticeScreenInputEvents(const GUIControl* guiEventControl); - /// /// Handles the player interaction with the editor selection screen GUI elements. - /// - /// Pointer to the GUI element that the player interacted with. + /// @param guiEventControl Pointer to the GUI element that the player interacted with. void HandleEditorsScreenInputEvents(const GUIControl* guiEventControl); - /// /// Handles the player interaction with the quit screen GUI elements. - /// - /// Pointer to the GUI element that the player interacted with. + /// @param guiEventControl Pointer to the GUI element that the player interacted with. void HandleQuitScreenInputEvents(const GUIControl* guiEventControl); - /// /// Updates the currently hovered main screen button text to give the hovered visual and updates the previously hovered button to remove the hovered visual. - /// - /// Pointer to the currently hovered main screen button, if any. Acquired by GUIControlManager::GetControlUnderPoint. + /// @param hoveredButton Pointer to the currently hovered main screen button, if any. Acquired by GUIControlManager::GetControlUnderPoint. void UpdateMainScreenHoveredButton(const GUIButton* hoveredButton); #pragma endregion - /// /// Clears all the member variables of this MainMenuGUI, effectively resetting the members of this object. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/MetagameGUI.cpp b/Source/Menus/MetagameGUI.cpp index a62c667d28..66bee69527 100644 --- a/Source/Menus/MetagameGUI.cpp +++ b/Source/Menus/MetagameGUI.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MetagameGUI.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Implementation file for the MetagameGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MetagameGUI.h" #include "WindowMan.h" @@ -66,11 +54,6 @@ using namespace RTE; const std::string MetagameGUI::c_ClassName = "MetagameGUI"; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a neat animation over a site to show it changing team ownership. - void MetagameGUI::SiteTarget::Draw(BITMAP* drawBitmap) const { if (!drawBitmap) return; @@ -132,12 +115,6 @@ void MetagameGUI::SiteTarget::Draw(BITMAP* drawBitmap) const { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MetagameGUI, effectively -// resetting the members of this abstraction level only. - void MetagameGUI::Clear() { m_RootBoxMaxWidth = 0; @@ -299,12 +276,6 @@ void MetagameGUI::Clear() { m_StationPosOnOrbit.Reset(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetToStartNewGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets internal state of GUI to show 'Start new campaign' screen -// Arguments: None. -// Return value: None. void MetagameGUI::SetToStartNewGame() { g_MetaMan.SetSuspend(true); HideAllScreens(); @@ -313,11 +284,6 @@ void MetagameGUI::SetToStartNewGame() { SwitchToScreen(MetagameGUI::NEWDIALOG); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MetagameGUI object ready for use. - int MetagameGUI::Create() { for (int metaPlayer = Players::PlayerOne; metaPlayer < Players::MaxPlayerCount; ++metaPlayer) { m_ActionSiteLines[metaPlayer].clear(); @@ -332,11 +298,6 @@ int MetagameGUI::Create() { return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MetagameGUI object ready for use. - int MetagameGUI::Create(Controller* pController) { RTEAssert(pController, "No controller sent to MetagameGUI on creation!"); m_pController = pController; @@ -614,10 +575,6 @@ int MetagameGUI::Create(Controller* pController) { return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: MoveLocationsIntoTheScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Moves any locations closer to the center of the planet if they were left out void MetagameGUI::MoveLocationsIntoTheScreen() { // Clear offsets for (std::vector::iterator pItr = g_MetaMan.m_Scenes.begin(); pItr != g_MetaMan.m_Scenes.end(); ++pItr) @@ -677,14 +634,6 @@ void MetagameGUI::MoveLocationsIntoTheScreen() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a Reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the Reader's position is untouched. - int MetagameGUI::ReadProperty(const std::string_view& propName, Reader& reader) { Vector tempPos; @@ -719,12 +668,6 @@ int MetagameGUI::ReadProperty(const std::string_view& propName, Reader& reader) EndPropertyList; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this MetagameGUI to an output stream for -// later recreation with Create(Reader &reader); - int MetagameGUI::Save(Writer& writer) const { Serializable::Save(writer); @@ -738,11 +681,6 @@ int MetagameGUI::Save(Writer& writer) const { return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MetagameGUI object. - void MetagameGUI::Destroy() { delete m_pGUIController; delete m_pGUIInput; @@ -756,20 +694,10 @@ void MetagameGUI::Destroy() { Clear(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGUIControlManager -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the GUIControlManager owned and used by this. - GUIControlManager* MetagameGUI::GetGUIControlManager() { return m_pGUIController; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetEnabled -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Enables or disables the menu. This will animate it in and out of view. - void MetagameGUI::SetEnabled(bool enable) { if (enable && m_MenuEnabled != ENABLED && m_MenuEnabled != ENABLING) { m_MenuEnabled = ENABLING; @@ -811,11 +739,6 @@ void MetagameGUI::SetEnabled(bool enable) { m_ScreenChange = true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SelectScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tries to select a specifically named scene on the metagame field. - void MetagameGUI::SelectScene(Scene* pScene) { m_pSelectedScene = pScene; @@ -851,11 +774,6 @@ void MetagameGUI::SelectScene(Scene* pScene) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SelectScene -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Tries to select a specifically named scene on the metagame field. - bool MetagameGUI::SelectScene(std::string sceneName) { for (std::vector::iterator sItr = g_MetaMan.m_Scenes.begin(); sItr != g_MetaMan.m_Scenes.end(); ++sItr) { // Only allow selection if the Scene is revealed yet! @@ -868,11 +786,6 @@ bool MetagameGUI::SelectScene(std::string sceneName) { return false; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwitchToScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Switches to showing a specific menu screen/mode. - void MetagameGUI::SwitchToScreen(int newScreen) { RTEAssert(newScreen >= ROOTBOX && newScreen < SCREENCOUNT, "Tried to switch to an out of bounds screen!"); @@ -889,11 +802,6 @@ void MetagameGUI::SwitchToScreen(int newScreen) { g_MetaMan.SetSuspend(m_MenuScreen != ROOTBOX && m_MenuScreen != STATSDIALOG && m_MenuScreen != SCENEINFOBOX); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRoundName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes a round number into a nice friendly text string. "ONE" for 1 etc - std::string MetagameGUI::GetRoundName(int roundNumber) { if (roundNumber < 12) { if (roundNumber == 0) @@ -926,12 +834,6 @@ std::string MetagameGUI::GetRoundName(int roundNumber) { return std::string(numStr); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: StartNewGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attempts to start a new Metagame using the settings set in the -// New Game dialog box. - bool MetagameGUI::StartNewGame() { // Prepare the UI for the game intro/start UpdatePlayerSetup(); @@ -1118,12 +1020,6 @@ bool MetagameGUI::StartNewGame() { return true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: LoadGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attempts to load a Metagame from disk using the settings set in the -// Load Game dialog box. - bool MetagameGUI::LoadGame() { // Get the MetaSave to load from the previously temporarily saved combobox selection if (m_pSelectedGameToLoad) { @@ -1168,12 +1064,6 @@ bool MetagameGUI::LoadGame() { return false; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveGame -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attempts to save a Metagame to disk using the settings set in the -// Save Game dialog box. - bool MetagameGUI::SaveGame(std::string saveName, std::string savePath, bool resaveSceneData) { const std::string fullSavePath = g_PresetMan.GetFullModulePath(savePath); // If specified, first load all bitmap data of all Scenes in the current Metagame that have once saved em, so we can re-save them to the new files @@ -1226,12 +1116,6 @@ bool MetagameGUI::SaveGame(std::string saveName, std::string savePath, bool resa return true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SaveGameFromDialog -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attempts to save a Metagame to disk using the settings set in the -// Save Game dialog box. - bool MetagameGUI::SaveGameFromDialog() { std::string saveName; std::string savePath; @@ -1262,11 +1146,6 @@ bool MetagameGUI::SaveGameFromDialog() { return SaveGame(saveName, savePath, true); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame - void MetagameGUI::Update() { // Update the input controller m_pController->Update(); @@ -1766,11 +1645,6 @@ void MetagameGUI::Update() { m_PrevMousePos = mousePos; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu - void MetagameGUI::Draw(BITMAP* drawBitmap) { // Don't draw site lines and dots if we're in the menus if (!g_MetaMan.IsSuspended()) { @@ -1903,13 +1777,6 @@ void MetagameGUI::Draw(BITMAP* drawBitmap) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateInput -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the user input processing. -// Arguments: None. -// Return value: None. - void MetagameGUI::UpdateInput() { // If esc pressed, show campaign dialog if applicable if (g_UInputMan.KeyPressed(SDLK_ESCAPE)) { @@ -2545,11 +2412,6 @@ void MetagameGUI::UpdateInput() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: HideAllScreens -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Hides all menu screens, so one can easily be unhidden and shown only. - void MetagameGUI::HideAllScreens() { for (int iscreen = 0; iscreen < SCREENCOUNT; ++iscreen) { if (m_apScreenBox[iscreen] && iscreen != ROOTBOX) @@ -2584,11 +2446,6 @@ void MetagameGUI::HideAllScreens() { m_ScreenChange = true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: KeepBoxOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes sure a specific box doesn't end up moved completely off-screen. - void MetagameGUI::KeepBoxOnScreen(GUICollectionBox* pBox, int margin) { if (margin < 0) { // Use the dimensions of the box itself to prevent it from at all going outside the screen @@ -2613,12 +2470,6 @@ void MetagameGUI::KeepBoxOnScreen(GUICollectionBox* pBox, int margin) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CompletedActivity -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Handles what happens after an Activity within the Metagame was -// run and completed fully. - void MetagameGUI::CompletedActivity() { Activity* pDoneActivity = dynamic_cast(g_ActivityMan.GetActivity()); GAScripted* pDoneScriptedActivity = dynamic_cast(g_ActivityMan.GetActivity()); @@ -2750,13 +2601,6 @@ void MetagameGUI::CompletedActivity() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AutoResolveOffensive -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Automatically resolves an offensive fight without actually launching -// and going through an Activity. Will randomly determine who won and -// what the consequences are. - bool MetagameGUI::AutoResolveOffensive(GAScripted* pOffensive, Scene* pScene, bool brainCheck) { bool changedOwnership = false; const Loadout* pLoadout = 0; @@ -2972,11 +2816,6 @@ bool MetagameGUI::AutoResolveOffensive(GAScripted* pOffensive, Scene* pScene, bo return changedOwnership; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateSiteRevealing -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the New Site Revealing animation - void MetagameGUI::UpdateSiteRevealing() { // First set up all the targets we'll be animating into view if (g_MetaMan.m_StateChanged) { @@ -3055,11 +2894,6 @@ void MetagameGUI::UpdateSiteRevealing() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateSiteChangeAnim -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates team ownership change animations, if any. - void MetagameGUI::UpdateSiteChangeAnim() { // Continue animating the existing markers double animInterval = 600; @@ -3087,11 +2921,6 @@ void MetagameGUI::UpdateSiteChangeAnim() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateIncomeCounting -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Count Income animation - void MetagameGUI::UpdateIncomeCounting(bool initOverride) { // First set up all the site lines we should be animating into view if (g_MetaMan.m_StateChanged || initOverride) { @@ -3516,11 +3345,6 @@ void MetagameGUI::UpdateIncomeCounting(bool initOverride) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateHumanPlayerTurn -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates a human player's turn - void MetagameGUI::UpdateHumanPlayerTurn(int metaPlayer) { // In-game player - IMPORTANT to pass this to the Scenes, and not the metaplayer int player = g_MetaMan.m_Players[metaPlayer].GetInGamePlayer(); @@ -3554,11 +3378,6 @@ void MetagameGUI::UpdateHumanPlayerTurn(int metaPlayer) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateBaseBuilding -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the Base Building animation - void MetagameGUI::UpdateBaseBuilding() { m_pPhaseLabel->SetText("Building Bases"); @@ -3804,12 +3623,6 @@ void MetagameGUI::UpdateBaseBuilding() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetupOffensives -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets up the Activities that represent all the offensive actions of -// the teams this round. - void MetagameGUI::SetupOffensives() { // Clear out the old ones, if any g_MetaMan.ClearActivities(); @@ -3897,11 +3710,6 @@ void MetagameGUI::SetupOffensives() { */ } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateOffensives -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the offensive actions animation - void MetagameGUI::UpdateOffensives() { char str[256]; @@ -4562,11 +4370,6 @@ void MetagameGUI::UpdateOffensives() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FinalizeOffensive -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Finishes one battle in the UpdateOffensives and moves onto the next. - bool MetagameGUI::FinalizeOffensive() { // No Offensives this round? then skip all this business if (g_MetaMan.m_RoundOffensives.empty() || g_MetaMan.m_CurrentOffensive < 0 || g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || !m_pAnimScene) { @@ -4636,11 +4439,6 @@ bool MetagameGUI::FinalizeOffensive() { return true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResetBattleInfo -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Hides and resets all battle info labels and panels - void MetagameGUI::ResetBattleInfo() { int mp = 0; for (std::vector::iterator mpItr = g_MetaMan.m_Players.begin(); mpItr != g_MetaMan.m_Players.end(); ++mpItr) { @@ -4663,12 +4461,6 @@ void MetagameGUI::ResetBattleInfo() { m_PostBattleReview = false; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateBattleQuads -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates which player get placed in which quad around a fought-over -// site. - void MetagameGUI::UpdateBattleQuads(Vector targetPos) { // Start with a clean slate for (int q = Players::PlayerOne; q < Players::MaxPlayerCount; ++q) @@ -4702,12 +4494,6 @@ void MetagameGUI::UpdateBattleQuads(Vector targetPos) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePreBattleAttackers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the animation and display of the info for the current -// offensive battle being next in line for this round. - void MetagameGUI::UpdatePreBattleAttackers(float progress) { // Sanity check that we have any offensive battle activity and scene to display around if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || @@ -4843,12 +4629,6 @@ void MetagameGUI::UpdatePreBattleAttackers(float progress) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePreBattleDefenders -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the animation and display of the info for the current -// offensive battle's defenders being next in line for this round. - void MetagameGUI::UpdatePreBattleDefenders(float progress) { // Sanity check that we have any offensive battle activity and scene to display around if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || @@ -4972,12 +4752,6 @@ void MetagameGUI::UpdatePreBattleDefenders(float progress) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePostBattleRetreaters -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the animation and display of the info for the current -// offensive battle's retreating brains going back to their pools - void MetagameGUI::UpdatePostBattleRetreaters(float progress) { // Sanity check that we have any offensive battle activity and scene to display around if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || @@ -5114,12 +4888,6 @@ void MetagameGUI::UpdatePostBattleRetreaters(float progress) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePostBattleResidents -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the animation and display of the info for the current done -// offensive battle's winning brains going back into the site. - void MetagameGUI::UpdatePostBattleResidents(float progress) { // Sanity check that we have any offensive battle activity and scene to display around if (g_MetaMan.m_CurrentOffensive >= g_MetaMan.m_RoundOffensives.size() || @@ -5325,12 +5093,6 @@ void MetagameGUI::UpdatePostBattleResidents(float progress) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlayerActionLines -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the action lines as per what the player has chosen to do -// during the current turn so far. - float MetagameGUI::UpdatePlayerActionLines(int metaPlayer) //, bool addUnallocated) { // Make sure we're in a player turn phase @@ -5373,11 +5135,6 @@ float MetagameGUI::UpdatePlayerActionLines(int metaPlayer) //, bool addUnallocat return meterStart; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateScenesBox -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the contents of the scene selection box. - void MetagameGUI::UpdateScenesBox(bool sceneChanged) { // Always show the info box if something is selected if (m_pSelectedScene && !g_MetaMan.IsSuspended()) { @@ -5559,11 +5316,6 @@ void MetagameGUI::UpdateScenesBox(bool sceneChanged) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateAISkillSliders -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates AI skill sliders and labels for all players. - void MetagameGUI::UpdateAISkillSliders(int player) { if (player >= Players::PlayerOne && player < Players::MaxPlayerCount) { m_apPlayerAISkillLabel[player]->SetText(Activity::GetAISkillString(m_apPlayerAISkillSlider[player]->GetValue())); @@ -5577,11 +5329,6 @@ void MetagameGUI::UpdateAISkillSliders(int player) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateGameSizeLabels -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the game size label of the new game dialog - void MetagameGUI::UpdateGameSizeLabels() { // How many players do we have set to go int playerCount = 0; @@ -5630,11 +5377,6 @@ void MetagameGUI::UpdateGameSizeLabels() { m_pDifficultyLabel->SetText("Difficulty: Nuts!"); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlayerSetup -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the player setup controls of the new game dialog - void MetagameGUI::UpdatePlayerSetup() { int humanPlayers = 0; int totalPlayers = 0; @@ -5705,11 +5447,6 @@ void MetagameGUI::UpdatePlayerSetup() { UpdateGameSizeLabels(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlayerBars -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the floating player bars with current funds, flag, etc. - void MetagameGUI::UpdatePlayerBars() { if (g_MetaMan.m_GameState >= MetaMan::NOGAME && g_MetaMan.m_GameState <= MetaMan::ENDROUND && !g_MetaMan.IsSuspended()) { int metaPlayer = 0; @@ -5830,11 +5567,6 @@ void MetagameGUI::UpdatePlayerBars() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdateSiteHoverLabel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the floating label over a planet site. - void MetagameGUI::UpdateSiteNameLabel(bool visible, std::string text, const Vector& location, float height) { // Set up the hover label to appear over any hovered scene location m_pScenePlanetLabel->SetVisible(visible); @@ -5858,11 +5590,6 @@ void MetagameGUI::UpdateSiteNameLabel(bool visible, std::string text, const Vect } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: PlayerTextIndication -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Starts an animation of a label showing a text string over a player bar - void MetagameGUI::PlayerTextIndication(int metaPlayer, std::string text, const Vector& screenPos, double animLengthMS) { m_apFundsChangeLabel[metaPlayer]->SetText(text); m_apFundsChangeLabel[metaPlayer]->SetHAlignment(GUIFont::Centre); @@ -5877,11 +5604,6 @@ void MetagameGUI::PlayerTextIndication(int metaPlayer, std::string text, const V m_apFundsChangeTimer[metaPlayer].SetRealTimeLimitMS(animLengthMS); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FundsChangeIndication -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Starts an animation of a label showing funds changing for a player - void MetagameGUI::FundsChangeIndication(int metaPlayer, float change, const Vector& screenPos, double animLengthMS) { char str[256]; std::snprintf(str, sizeof(str), change >= 1.0 ? "%c +%.0f oz" : (change <= -1.0 ? "%c %.0f oz" : "%c %.0f oz"), -58, change); @@ -5898,11 +5620,6 @@ void MetagameGUI::FundsChangeIndication(int metaPlayer, float change, const Vect m_apFundsChangeTimer[metaPlayer].SetRealTimeLimitMS(animLengthMS); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BrainsChangeIndication -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Starts an animation of a label showing brains changing for a metaPlayer - void MetagameGUI::BrainsChangeIndication(int metaPlayer, int change, const Vector& screenPos, int fontAlignment, double animLengthMS) { char str[256]; // [Brain Icon] [X] Number @@ -5922,11 +5639,6 @@ void MetagameGUI::BrainsChangeIndication(int metaPlayer, int change, const Vecto m_apBrainsChangeTimer[metaPlayer].SetRealTimeLimitMS(animLengthMS); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveSiteLine -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specific index siteline out of a vector. - bool MetagameGUI::RemoveSiteLine(std::vector& lineList, int removeIndex) { if (lineList.empty()) return false; @@ -5942,11 +5654,6 @@ bool MetagameGUI::RemoveSiteLine(std::vector& lineList, int removeInde return removed; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetPlayerLineFunds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the total funds of all visible lines of a specific player. - float MetagameGUI::GetPlayerLineFunds(std::vector& lineList, int metaPlayer, bool onlyVisible) { if (metaPlayer < Players::PlayerOne || metaPlayer >= g_MetaMan.m_Players.size()) return 0; @@ -5960,12 +5667,6 @@ float MetagameGUI::GetPlayerLineFunds(std::vector& lineList, int metaP return totalFunds; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: UpdatePlayerLineRatios -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the site line meter ratios of a player based on their fund -// amounts and visibilty. - void MetagameGUI::UpdatePlayerLineRatios(std::vector& lineList, int metaPlayer, bool onlyVisible, float total) { if (metaPlayer < Players::PlayerOne || metaPlayer >= g_MetaMan.m_Players.size()) return; @@ -5984,12 +5685,6 @@ void MetagameGUI::UpdatePlayerLineRatios(std::vector& lineList, int me } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawGlowLine -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a fancy thick flickering line to point out scene points on the -// planet. - void MetagameGUI::DrawGlowLine(BITMAP* drawBitmap, const Vector& start, const Vector& end, int color) { int blendAmount = 210 + RandomNum(-15, 15); set_screen_blender(blendAmount, blendAmount, blendAmount, blendAmount); @@ -6015,12 +5710,6 @@ void MetagameGUI::DrawGlowLine(BITMAP* drawBitmap, const Vector& start, const Ve line(drawBitmap, start.m_X, start.m_Y - 1, end.m_X, end.m_Y - 1, color); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawScreenLineToSitePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a fancy thick flickering lines to point out scene points on the -// planet, FROM an arbitrary screen point. - bool MetagameGUI::DrawScreenLineToSitePoint(BITMAP* drawBitmap, const Vector& screenPoint, const Vector& planetPoint, @@ -6136,12 +5825,6 @@ bool MetagameGUI::DrawScreenLineToSitePoint(BITMAP* drawBitmap, return totalSegments <= onlyFirstSegments && totalSegments <= onlyLastSegments; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawPlayerLineToSitePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws a fancy thick flickering lines to point out scene points on the -// planet, FROM a floating player bar, showing a certain ratio. - bool MetagameGUI::DrawPlayerLineToSitePoint(BITMAP* drawBitmap, int metaPlayer, float startMeterAt, diff --git a/Source/Menus/MetagameGUI.h b/Source/Menus/MetagameGUI.h index a994a39ec6..4b2c8e8f2e 100644 --- a/Source/Menus/MetagameGUI.h +++ b/Source/Menus/MetagameGUI.h @@ -1,18 +1,11 @@ #ifndef _METAGAMEGUI_ #define _METAGAMEGUI_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MetagameGUI.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: MetagameGUI class -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// MetagameGUI class +/// @author Daniel Tabar +/// dtabar@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files #include "ActivityMan.h" #include "Timer.h" #include "GUIBanner.h" @@ -39,18 +32,10 @@ namespace RTE { class Activity; class GAScripted; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: MetagameGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A full menu system that represents the metagame GUI for Cortex Command - // Parent(s): Serializable. - // Class history: 8/22/2008 MetagameGUI Created. - + /// A full menu system that represents the metagame GUI for Cortex Command class MetagameGUI : public Serializable { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations SerializableClassNameGetter SerializableOverrideMethods @@ -154,679 +139,348 @@ namespace RTE { m_AnimTimer.Reset(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws this SiteTarget onto a bitmap of choice. - // Arguments: The bitmap to draw to. - // Return value: None. - + /// Draws this SiteTarget onto a bitmap of choice. + /// @param drawBitmap The bitmap to draw to. void Draw(BITMAP* drawBitmap) const; }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MetagameGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a MetagameGUI object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a MetagameGUI object in system + /// memory. Create() should be called before using the object. MetagameGUI() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~MetagameGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a MetagameGUI object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a MetagameGUI object before deletion + /// from system memory. ~MetagameGUI() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MetagameGUI object ready for use. - // Arguments: A poitner to a Controller which will control this Menu. Ownership is - // NOT TRANSFERRED! - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MetagameGUI object ready for use. + /// @param pController A poitner to a Controller which will control this Menu. Ownership is + /// NOT TRANSFERRED! + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(Controller* pController); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire MetagameGUI, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire MetagameGUI, including its inherited members, to + /// their default settings or values. void Reset() override { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MetagameGUI object. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the MetagameGUI object. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetGUIControlManager - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the GUIControlManager owned and used by this. - // Arguments: None. - // Return value: The GUIControlManager. Ownership is not transferred! - + /// Gets the GUIControlManager owned and used by this. + /// @return The GUIControlManager. Ownership is not transferred! GUIControlManager* GetGUIControlManager(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Enables or disables the menu. This will animate it in and out of view. - // Arguments: Whether to enable or disable the menu. - // Return value: None. - + /// Enables or disables the menu. This will animate it in and out of view. + /// @param enable Whether to enable or disable the menu. (default: true) void SetEnabled(bool enable = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsEnabled - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the menu is enabled or not. - // Arguments: None. - // Return value: None. - + /// Reports whether the menu is enabled or not. bool IsEnabled() { return m_MenuEnabled == ENABLED || m_MenuEnabled == ENABLING; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SwitchToScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Switches to showing a specific menu screen/mode. - // Arguments: The MenuScreen to switch to. - // Return value: None. - + /// Switches to showing a specific menu screen/mode. + /// @param newScreen The MenuScreen to switch to. void SwitchToScreen(int newScreen); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetRoundName - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes a round number into a nice friendly text string. "ONE" for 1 etc - // Arguments: The number of the round to convert to a string. - // Return value: The friendly text string for that round. - + /// Makes a round number into a nice friendly text string. "ONE" for 1 etc + /// @param roundNumber The number of the round to convert to a string. + /// @return The friendly text string for that round. std::string GetRoundName(int roundNumber); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPlanetInfo - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets where the planet is on the scren and its other data so the menu - // can overlay properly on it. - // Arguments: The absolute screen coordinates of the planet's center. - // The radius, in screen pixel units, of the planet. - // Return value: None. - + /// Sets where the planet is on the scren and its other data so the menu + /// can overlay properly on it. + /// @param center The absolute screen coordinates of the planet's center. + /// @param radius The radius, in screen pixel units, of the planet. void SetPlanetInfo(const Vector& center, float radius) { m_PlanetCenter = center; m_PlanetRadius = radius; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SelectScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets a specific scene as the currently selected one. OWNERSHIP IS NOT TRANSFERRED! - // Arguments: The Scene to set as selected. Ownership is NOT transferred. - // Return value: None. - + /// Sets a specific scene as the currently selected one. OWNERSHIP IS NOT TRANSFERRED! + /// @param pScene The Scene to set as selected. Ownership is NOT transferred. void SelectScene(Scene* pScene); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SelectScene - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tries to select a specifically named scene on the metagame field. - // Arguments: The name of the Scene to try to find and select. - // Return value: Whether mission was found and selected. - + /// Tries to select a specifically named scene on the metagame field. + /// @param sceneName The name of the Scene to try to find and select. + /// @return Whether mission was found and selected. bool SelectScene(std::string sceneName); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ContinuePhase - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the player has decided to continue to next phase of the - // round of the current game. - // Arguments: None. - // Return value: Whether the player just decided to continue this frame - + /// Reports whether the player has decided to continue to next phase of the + /// round of the current game. + /// @return Whether the player just decided to continue this frame bool ContinuePhase() { return m_ContinuePhase; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ActivityRestarted - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the player has decided to restart an activity this frame. - // All parameters for the new game has been fed into ActivityMan already. - // Arguments: None. - // Return value: Whether the activity should be restarted. - + /// Reports whether the player has decided to restart an activity this frame. + /// All parameters for the new game has been fed into ActivityMan already. + /// @return Whether the activity should be restarted. bool ActivityRestarted() { return m_ActivityRestarted; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ActivityResumed - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the player has decided to resume the current activity. - // Arguments: None. - // Return value: Whether the activity should be resumed. - + /// Reports whether the player has decided to resume the current activity. + /// @return Whether the activity should be resumed. bool ActivityResumed() { return m_ActivityResumed; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BackToMain - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the player has decided to go back to the main menu. - // Arguments: None. - // Return value: Whether we should go back to main menu. - + /// Reports whether the player has decided to go back to the main menu. + /// @return Whether we should go back to main menu. bool BackToMain() { return m_BackToMain; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: QuitProgram - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Reports whether the player has decided to quit the program. - // Arguments: None. - // Return value: Whether the program has been commanded to shit down by the user. - + /// Reports whether the player has decided to quit the program. + /// @return Whether the program has been commanded to shit down by the user. bool QuitProgram() { return m_Quit; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: StartNewGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Attempts to start a new Metagame using the settings set in the - // New Game dialog box. - // Arguments: None. - // Return value: Whether the game was able to be set up with the current settings. - + /// Attempts to start a new Metagame using the settings set in the + /// New Game dialog box. + /// @return Whether the game was able to be set up with the current settings. bool StartNewGame(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: LoadGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Attempts to load a Metagame from disk using the settings set in the - // Load Game dialog box. - // Arguments: None. - // Return value: Whether the game was able to be loaded with the current settings. - + /// Attempts to load a Metagame from disk using the settings set in the + /// Load Game dialog box. + /// @return Whether the game was able to be loaded with the current settings. bool LoadGame(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SaveGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Just saves out the MetaGame and all its Scene data as-is to a specific - // location. - // Arguments: The name of the save game to create or overwrite here. - // The full path of the ini that we want to save the Metagame state to. - // Whether to load all the scene data that is on disk first so it will - // be re-saved to the new location here. - // Return value: Whether the game was able to be saved there. - + /// Just saves out the MetaGame and all its Scene data as-is to a specific + /// location. + /// @param saveName The name of the save game to create or overwrite here. + /// @param savePath The full path of the ini that we want to save the Metagame state to. + /// @param resaveSceneData Whether to load all the scene data that is on disk first so it will (default: false) + /// be re-saved to the new location here. + /// @return Whether the game was able to be saved there. bool SaveGame(std::string saveName, std::string savePath, bool resaveSceneData = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SaveGameFromDialog - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Attempts to save a Metagame to disk using the settings set in the - // Save Game dialog box. - // Arguments: None. - // Return value: Whether the game was able to be saved with the current settings. - + /// Attempts to save a Metagame to disk using the settings set in the + /// Save Game dialog box. + /// @return Whether the game was able to be saved with the current settings. bool SaveGameFromDialog(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Menu each frame - // Arguments: None. - // Return value: None. - + /// Updates the state of this Menu each frame void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the menu - // Arguments: The bitmap to draw on. - // Return value: None. - + /// Draws the menu + /// @param drawBitmap The bitmap to draw on. void Draw(BITMAP* drawBitmap); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetToStartNewGame - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets internal state of GUI to show 'Start new campaign' screen - // Arguments: None. - // Return value: None. + /// Method: SetToStartNewGame + /// Resets internal state of GUI to show 'Start new campaign' screen void SetToStartNewGame(); - /// /// Sets where the station is located on the planet orbit. - /// - /// The position of the station on the planet orbit. + /// @param newStationPos The position of the station on the planet orbit. void SetStationOrbitPos(const Vector& newStationPos) { m_StationPosOnOrbit = newStationPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MetaMan object ready for use -> this is acutally a light - // and not complete version of the one that takes a controller. - // It is only for init after reading stuff from file as a Serializable. - // Arguments: None. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MetaMan object ready for use -> this is acutally a light + /// and not complete version of the one that takes a controller. + /// It is only for init after reading stuff from file as a Serializable. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create() override; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateInput - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the user input processing. - // Arguments: None. - // Return value: None. - + /// Updates the user input processing. void UpdateInput(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: HideAllScreens - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Hides all menu screens, so one can easily be unhidden and shown only. - // Arguments: None. - // Return value: None. - + /// Hides all menu screens, so one can easily be unhidden and shown only. void HideAllScreens(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: KeepBoxOnScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes sure a specific box doesn't end up moved completely off-screen. - // Arguments: The GUICollectionBox to adjust, if necessary. - // The amount of margin to allow the box to stay within. If negative, - // the width/height of the box itself are used. - // Return value: None. - + /// Makes sure a specific box doesn't end up moved completely off-screen. + /// @param pBox The GUICollectionBox to adjust, if necessary. + /// @param margin The amount of margin to allow the box to stay within. If negative, (default: 10) + /// the width/height of the box itself are used. void KeepBoxOnScreen(GUICollectionBox* pBox, int margin = 10); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ChangeAnimMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Changes the animation mode - // Arguments: None. - // Return value: None. - + /// Changes the animation mode void ChangeAnimMode(int newMode) { m_AnimMode = newMode; m_AnimModeChange = true; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: NewAnimMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks for and switches off the new animation mode flag - // Arguments: None. - // Return value: None. - + /// Checks for and switches off the new animation mode flag bool NewAnimMode() { bool changed = m_AnimModeChange; m_AnimModeChange = false; return changed; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: CompletedActivity - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Handles what happens after an Activity within the Metagame was - // run and completed fully. - // Arguments: None. - // Return value: None. - + /// Handles what happens after an Activity within the Metagame was + /// run and completed fully. void CompletedActivity(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: AutoResolveOffensive - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Automatically resolves an offensive fight without actually launching - // and going through an Activity. Will randomly determine who won and - // what the consequences are. - // Arguments: The Offsenive Activity to resolve and manipulate accordingly. OWNERSHIP IS NOT TRANSFERRED! - // The Scene this Offensive is supposed to take place on. OWNERSHIP IS NOT TRANSFERRED! - // Whether to check the validity of all players based on whether they - // have brains remaining alive. If false, all active players will be - // instead be flagged as having had brains at some point. - // Return value: Whether the ownership of the relevant Scene changed due to this. - + /// Automatically resolves an offensive fight without actually launching + /// and going through an Activity. Will randomly determine who won and + /// what the consequences are. + /// @param pOffensive The Offsenive Activity to resolve and manipulate accordingly. OWNERSHIP IS NOT TRANSFERRED! + /// @param pScene The Scene this Offensive is supposed to take place on. OWNERSHIP IS NOT TRANSFERRED! + /// @param brainCheck Whether to check the validity of all players based on whether they (default: false) + /// have brains remaining alive. If false, all active players will be + /// instead be flagged as having had brains at some point. + /// @return Whether the ownership of the relevant Scene changed due to this. bool AutoResolveOffensive(GAScripted* pOffensive, Scene* pScene, bool brainCheck = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateSiteRevealing - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the New Site Revealing animation - // Arguments: None. - // Return value: None. - + /// Updates the New Site Revealing animation void UpdateSiteRevealing(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateSiteChangeAnim - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates team ownership change animations, if any. - // Arguments: None. - // Return value: None. - + /// Updates team ownership change animations, if any. void UpdateSiteChangeAnim(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateIncomeCounting - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Count Income animation - // Arguments: Whether to just set up the lines and funds as if we had a new round. Also skips changing funds to avoid an income/cost duplication glitch when saving a game at the start of a round. - // Return value: None. - + /// Updates the Count Income animation + /// @param initOverride Whether to just set up the lines and funds as if we had a new round. Also skips changing funds to avoid an income/cost duplication glitch when saving a game at the start of a round. (default: false) void UpdateIncomeCounting(bool initOverride = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateHumanPlayerTurn - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates a human player's turn. - // Arguments: Which metaplayer' turn it is - // Return value: None. - + /// Updates a human player's turn. + /// @param metaPlayer Which metaplayer' turn it is void UpdateHumanPlayerTurn(int metaPlayer); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateBaseBuilding - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the Base Building animation - // Arguments: None. - // Return value: None. - + /// Updates the Base Building animation void UpdateBaseBuilding(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetupOffensives - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets up the Activities that represent all the offensive actions of - // the teams this round. - // Arguments: None. - // Return value: None. - + /// Sets up the Activities that represent all the offensive actions of + /// the teams this round. void SetupOffensives(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateOffensives - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the offensive actions animation - // Arguments: None. - // Return value: None. - + /// Updates the offensive actions animation void UpdateOffensives(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FinalizeOffensive - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Finishes one battle in the UpdateOffensives and moves onto the next. - // Arguments: None. - // Return value: If there are any more battles after the one that was just finalized. - + /// Finishes one battle in the UpdateOffensives and moves onto the next. + /// @return If there are any more battles after the one that was just finalized. bool FinalizeOffensive(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: ResetBattleInfo - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Hides and resets all battle info labels and panels - // Arguments: None. - // Return value: None. - + /// Hides and resets all battle info labels and panels void ResetBattleInfo(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateBattleQuads - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates which player get placed in which quad around a fought-over - // site. - // Arguments: The absolutel screen position of the target site. - // Return value: None. - + /// Updates which player get placed in which quad around a fought-over + /// site. + /// @param targetPos The absolutel screen position of the target site. void UpdateBattleQuads(Vector targetPos); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePreBattleAttackers - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the animation and display of the info for the current - // offensive battle's attackers being next in line for this round. - // Arguments: The normalized scalar which will set the desired progress of the - // total animation. 0 means nothing is shown, because it is at the start - // of the animation where brain icons start moving around. - // Return value: None. - + /// Updates the animation and display of the info for the current + /// offensive battle's attackers being next in line for this round. + /// @param progress The normalized scalar which will set the desired progress of the + /// total animation. 0 means nothing is shown, because it is at the start + /// of the animation where brain icons start moving around. void UpdatePreBattleAttackers(float progress); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePreBattleDefenders - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the animation and display of the info for the current - // offensive battle's defenders being next in line for this round. - // Arguments: The normalized scalar which will set the desired progress of the - // total animation. 0 means nothing is shown, because it is at the start - // of the animation where brain icons start moving around. - // Return value: None. - + /// Updates the animation and display of the info for the current + /// offensive battle's defenders being next in line for this round. + /// @param progress The normalized scalar which will set the desired progress of the + /// total animation. 0 means nothing is shown, because it is at the start + /// of the animation where brain icons start moving around. void UpdatePreBattleDefenders(float progress); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePostBattleRetreaters - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the animation and display of the info for the current - // offensive battle's retreating brains going back to their pools - // Arguments: The normalized scalar which will set the desired progress of the - // total animation. 0 means nothing has happened, because it is at the - // start of the animation where brain icons start moving around. - // Return value: None. - + /// Updates the animation and display of the info for the current + /// offensive battle's retreating brains going back to their pools + /// @param progress The normalized scalar which will set the desired progress of the + /// total animation. 0 means nothing has happened, because it is at the + /// start of the animation where brain icons start moving around. void UpdatePostBattleRetreaters(float progress); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePostBattleResidents - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the animation and display of the info for the current done - // offensive battle's winning brains going back into the site. - // Arguments: The normalized scalar which will set the desired progress of the - // total animation. 0 means nothing has happened, because it is at the - // start of the animation where brain icons start moving around. - // Return value: None. - + /// Updates the animation and display of the info for the current done + /// offensive battle's winning brains going back into the site. + /// @param progress The normalized scalar which will set the desired progress of the + /// total animation. 0 means nothing has happened, because it is at the + /// start of the animation where brain icons start moving around. void UpdatePostBattleResidents(float progress); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePlayerActionLines - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the action lines as per what the player has chosen to do - // during the current turn so far. - // Arguments: The metaplayer we want to update the lines for. - // Also add a line for the unallocated funds the player hasn't used for - // anyhting else yet. - NOPE, NOT IMPL YET - // Return value: The meter start that remains after all the lines are added. - + /// Updates the action lines as per what the player has chosen to do + /// during the current turn so far. + /// @param player The metaplayer we want to update the lines for. + /// Also add a line for the unallocated funds the player hasn't used for + /// anyhting else yet. - NOPE, NOT IMPL YET + /// @return The meter start that remains after all the lines are added. float UpdatePlayerActionLines(int player); //, bool addUnallocated = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateScenesBox - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the contents of the scene selection box. - // Arguments: Whether the selected has changed and should refresh the box completely. - // Return value: None. - + /// Updates the contents of the scene selection box. + /// @param sceneChanged Whether the selected has changed and should refresh the box completely. (default: false) void UpdateScenesBox(bool sceneChanged = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateGameSizeLabels - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the game size labels of the new game dialog - // Arguments: None. - // Return value: None. - + /// Updates the game size labels of the new game dialog void UpdateGameSizeLabels(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateAISkillSliders - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates AI skill sliders and labels for all players. - // Arguments: Which player's slider was changed. - // Return value: None. - + /// Updates AI skill sliders and labels for all players. + /// @param player Which player's slider was changed. void UpdateAISkillSliders(int player); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePlayerSetup - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the player setup controls of the new game dialog - // Arguments: None. - // Return value: None. - + /// Updates the player setup controls of the new game dialog void UpdatePlayerSetup(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePlayerBars - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the floating player bars with current funds, flag, etc. - // Arguments: None. - // Return value: None. - + /// Updates the floating player bars with current funds, flag, etc. void UpdatePlayerBars(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdateSiteHoverLabel - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the floating label over a planet site. - // Arguments: Label is visible. - // Text to show above the location. - // The location in planetary coords. - // How high above the location to show the text, adjustment from a good default. - // Return value: None. - + /// Updates the floating label over a planet site. + /// @param visible Label is visible. + /// @param text Text to show above the location. (default: "") + /// @param location The location in planetary coords. (default: Vector()) + /// @param height How high above the location to show the text, adjustment from a good default. (default: 1.0) void UpdateSiteNameLabel(bool visible, std::string text = "", const Vector& location = Vector(), float height = 1.0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: PlayerTextIndication - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Starts an animation of a label showing a text string over a player bar - // Arguments: Which player the indication is relevant to - // The string to display. - // Where, in screen coords the change should be indicated. The CENTER of - // the floating label will line up with this pos. - // How long, in MS, that the animation should linger - // Return value: None. - + /// Starts an animation of a label showing a text string over a player bar + /// @param player Which player the indication is relevant to + /// @param text The string to display. + /// @param screenPos Where, in screen coords the change should be indicated. The CENTER of + /// the floating label will line up with this pos. + /// @param animLengthMS How long, in MS, that the animation should linger void PlayerTextIndication(int player, std::string text, const Vector& screenPos, double animLengthMS); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: FundsChangeIndication - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Starts an animation of a label showing funds changing for a player - // Arguments: Which player the change is relevant to - // The change in funds to display. - // Where, in screen coords the change should be indicated. The RIGHTMOST - // UPPER CORNER of the floating label will line up with this pos. - // How long, in MS, that the animation should linger - // Return value: None. - + /// Starts an animation of a label showing funds changing for a player + /// @param player Which player the change is relevant to + /// @param change The change in funds to display. + /// @param screenPos Where, in screen coords the change should be indicated. The RIGHTMOST + /// @param animLengthMS UPPER CORNER of the floating label will line up with this pos. + /// How long, in MS, that the animation should linger void FundsChangeIndication(int player, float change, const Vector& screenPos, double animLengthMS); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: BrainsChangeIndication - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Starts an animation of a label showing brains changing for a player - // Arguments: Which player the change is relevant to - // The change in brains to display. - // Where, in screen coords the change should be indicated. The LEFTMOST - // UPPER CORNER of the floating label will line up with this pos. - // How long, in MS, that the animation should linger - // The horizontal font alignment of the change. - // Return value: None. - + /// Starts an animation of a label showing brains changing for a player + /// @param player Which player the change is relevant to + /// @param change The change in brains to display. + /// @param screenPos Where, in screen coords the change should be indicated. The LEFTMOST + /// @param fontAlignment UPPER CORNER of the floating label will line up with this pos. + /// @param animLengthMS How long, in MS, that the animation should linger + /// The horizontal font alignment of the change. void BrainsChangeIndication(int player, int change, const Vector& screenPos, int fontAlignment, double animLengthMS); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: IsSiteLineVisible - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Tells whether a SiteLine can be considered visible. - // Arguments: The SiteLine to check. - // Return value: Whether visible. - + /// Tells whether a SiteLine can be considered visible. + /// @param sl The SiteLine to check. + /// @return Whether visible. bool IsSiteLineVisible(SiteLine& sl) { return sl.m_OnlyFirstSegments != 0 && sl.m_OnlyLastSegments != 0; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: RemoveSiteLine - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Removes a specific index siteline out of a vector. - // Arguments: The vector of SiteLine:s to remove from. - // The index of the siteline to remove - // Return value: Whether the line was removed or not. - + /// Removes a specific index siteline out of a vector. + /// @param lineList The vector of SiteLine:s to remove from. + /// @param removeIndex The index of the siteline to remove + /// @return Whether the line was removed or not. bool RemoveSiteLine(std::vector& lineList, int removeIndex); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetPlayerLineFunds - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the total funds of all visible lines of a specific player. - // Arguments: A vector with SiteLine:s which may contain other players' lines too. - // Which player's lines to check for. - // Only count the funds of visible lines. - // Return value: The total funds, in oz. - + /// Gets the total funds of all visible lines of a specific player. + /// @param lineList A vector with SiteLine:s which may contain other players' lines too. + /// @param player Which player's lines to check for. + /// @param onlyVisible Only count the funds of visible lines. (default: true) + /// @return The total funds, in oz. float GetPlayerLineFunds(std::vector& lineList, int player, bool onlyVisible = true); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: UpdatePlayerLineRatios - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the site line meter ratios of a player based on their fund - // amounts and visibilty. - // Arguments: A vector with SiteLine:s which may contain other players' lines too. - // Which player's lines to update. - // Whetehr to only care about visible lines. - // The total funds to be calculating the ratios against. If negative, - // the total line amounts is what will be used. - // Return value: None. - + /// Updates the site line meter ratios of a player based on their fund + /// amounts and visibilty. + /// @param lineList A vector with SiteLine:s which may contain other players' lines too. + /// @param player Which player's lines to update. + /// @param onlyVisible Whetehr to only care about visible lines. (default: true) + /// @param total The total funds to be calculating the ratios against. If negative, (default: -1) + /// the total line amounts is what will be used. void UpdatePlayerLineRatios(std::vector& lineList, int player, bool onlyVisible = true, float total = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawGlowLine - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws a fancy thick flickering line to point out scene points on the - // planet. - // Arguments: The bitmap to draw to. - // The start and end Vector:s for the line, in absolute screen coordinates. - // The color to draw the line in. Use makecol(r, g, b) to create the color - // Return value: None. - + /// Draws a fancy thick flickering line to point out scene points on the + /// planet. + /// @param drawBitmap The bitmap to draw to. + /// @param start The start and end Vector:s for the line, in absolute screen coordinates. + /// @param end The color to draw the line in. Use makecol(r, g, b) to create the color static void DrawGlowLine(BITMAP* drawBitmap, const Vector& start, const Vector& end, int color); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawScreenLineToSitePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws a fancy thick flickering lines to point out scene points on the - // planet, FROM an arbitrary screen point. - // Arguments: The bitmap to draw to. - // The point on the screen to point from, in screen coordinates. - // The point on the planet to point at, in planet coordinates. - // The color of the line. - // How many of the segments from the start (the start of the line) to draw. - // How many of the segments from the end (site circle) to draw. -1 is all. - // The height of the 'channel' above and below that the lines will go around - // the player bar. - // What size factor from 'normal' should the circle's diameter be drawn. - // Return value: Whether all segments of the line were drawn with the segment params. - + /// Draws a fancy thick flickering lines to point out scene points on the + /// planet, FROM an arbitrary screen point. + /// @param drawBitmap The bitmap to draw to. + /// @param screenPoint The point on the screen to point from, in screen coordinates. + /// @param planetPoint The point on the planet to point at, in planet coordinates. + /// @param color The color of the line. + /// @param onlyFirstSegments How many of the segments from the start (the start of the line) to draw. (default: -1) + /// @param onlyLastSegments How many of the segments from the end (site circle) to draw. -1 is all. (default: -1) + /// @param channelHeight The height of the 'channel' above and below that the lines will go around (default: 80) + /// the player bar. + /// @param circleSize What size factor from 'normal' should the circle's diameter be drawn. (default: 1.0) + /// @return Whether all segments of the line were drawn with the segment params. bool DrawScreenLineToSitePoint(BITMAP* drawBitmap, const Vector& screenPoint, const Vector& planetPoint, @@ -837,26 +491,22 @@ namespace RTE { float circleSize = 1.0, bool squareSite = false) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawPlayerLineToSitePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws a fancy thick flickering lines to point out scene points on the - // planet, FROM a floating player bar, showing a certain ratio. - // Arguments: The bitmap to draw to. - // The player whose floating bar we draw from. - // The start percentage of the meter to indicate, from 0 to 1.0 - // The actual percentage of the meter to indicate, from 0 to 1.0 - // The point on the planet to point at, in planet coordinates. - // The color of the line. - // How many of the segments from the start (the player floater) to draw. - // How many of the segments from the end (site circle) to draw. -1 is all. - // The height of the 'channel' above and below that the lines will go around - // the player bar. - // What size factor from 'normal' should the circle's diameter be drawn. - // Whether the circle should instead be a squareSite! - // Whether to draw the meter (FirstSegment == 1) no matter what - // Return value: Whether all segments of the line were drawn with the segment params. - + /// Draws a fancy thick flickering lines to point out scene points on the + /// planet, FROM a floating player bar, showing a certain ratio. + /// @param drawBitmap The bitmap to draw to. + /// @param player The player whose floating bar we draw from. + /// @param startMeterAt The start percentage of the meter to indicate, from 0 to 1.0 + /// @param meterAmount The actual percentage of the meter to indicate, from 0 to 1.0 + /// @param planetPoint The point on the planet to point at, in planet coordinates. + /// @param color The color of the line. + /// @param onlyFirstSegments How many of the segments from the start (the player floater) to draw. (default: -1) + /// @param onlyLastSegments How many of the segments from the end (site circle) to draw. -1 is all. (default: -1) + /// @param channelHeight The height of the 'channel' above and below that the lines will go around (default: 60) + /// the player bar. + /// @param circleSize What size factor from 'normal' should the circle's diameter be drawn. (default: 1.0) + /// @param squareSite Whether the circle should instead be a squareSite! (default: false) + /// @param drawMeterOverride Whether to draw the meter (FirstSegment == 1) no matter what (default: false) + /// @return Whether all segments of the line were drawn with the segment params. bool DrawPlayerLineToSitePoint(BITMAP* drawBitmap, int player, float startMeterAt, @@ -870,26 +520,18 @@ namespace RTE { bool squareSite = false, bool drawMeterOverride = false) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: DrawPlayerLineToSitePoint - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws a fancy thick flickering lines to point out scene points on the - // planet, FROM a floating player bar, showing a certain ratio. - // Arguments: The bitmap to draw to. - // The SiteLine struct with all the parameters this needs. - // Whether to draw the meter (FirstSegment == 1) no matter what - // Return value: Whether all segments of the line were drawn with the segment params. - + /// Draws a fancy thick flickering lines to point out scene points on the + /// planet, FROM a floating player bar, showing a certain ratio. + /// @param drawBitmap The bitmap to draw to. + /// @param sl The SiteLine struct with all the parameters this needs. + /// @param drawMeterOverride Whether to draw the meter (FirstSegment == 1) no matter what (default: false) const { return DrawPlayerLineToSitePoint(drawBitmap) + /// @return Whether all segments of the line were drawn with the segment params. bool DrawPlayerLineToSitePoint(BITMAP* drawBitmap, const SiteLine& sl, bool drawMeterOverride = false) const { return DrawPlayerLineToSitePoint(drawBitmap, sl.m_Player, sl.m_StartMeterAt, sl.m_MeterAmount, sl.m_PlanetPoint.GetFloored(), sl.m_Color, sl.m_OnlyFirstSegments, sl.m_OnlyLastSegments, sl.m_ChannelHeight, sl.m_CircleSize, sl.m_Square, drawMeterOverride); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: MoveLocationsIntoTheScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Moves any locations closer to the ceonter of the planet if they were left out - // of the screen due to low display resolution. - // Arguments: None. - // Return value: None. - + /// Moves any locations closer to the ceonter of the planet if they were left out + /// of the screen due to low display resolution. + /// Arguments: None. + /// Return value: None. void MoveLocationsIntoTheScreen(); enum MenuEnabled { @@ -1182,20 +824,12 @@ namespace RTE { Vector m_StationPosOnOrbit; //!< The position of the station on the planet orbit. - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MetagameGUI, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this MetagameGUI, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/ModManagerGUI.cpp b/Source/Menus/ModManagerGUI.cpp index e44fa18232..c0a750622b 100644 --- a/Source/Menus/ModManagerGUI.cpp +++ b/Source/Menus/ModManagerGUI.cpp @@ -16,8 +16,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ModManagerGUI::ModManagerGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); @@ -58,8 +56,6 @@ namespace RTE { m_ScriptsListFetched = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ModManagerGUI::PopulateKnownModsList() { for (int i = 0; i < g_PresetMan.GetTotalModuleCount(); ++i) { if (i >= g_PresetMan.GetOfficialModuleCount() && i < g_PresetMan.GetTotalModuleCount()) { @@ -92,8 +88,6 @@ namespace RTE { m_ModsListFetched = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ModManagerGUI::PopulateKnownScriptsList() { std::list globalScriptList; g_PresetMan.GetAllOfType(globalScriptList, "GlobalScript"); @@ -113,8 +107,6 @@ namespace RTE { m_ScriptsListFetched = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ModManagerGUI::ToggleMod() { int index = m_ModsListBox->GetSelectedIndex(); if (index > -1) { @@ -141,8 +133,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ModManagerGUI::ToggleScript() { int index = m_ScriptsListBox->GetSelectedIndex(); if (index > -1) { @@ -169,8 +159,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ModManagerGUI::HandleInputEvents() { if (!ListsFetched()) { PopulateKnownModsList(); @@ -207,8 +195,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ModManagerGUI::Draw() const { m_GUIControlManager->Draw(); } diff --git a/Source/Menus/ModManagerGUI.h b/Source/Menus/ModManagerGUI.h index 9c3b5f58ab..d649c1242d 100644 --- a/Source/Menus/ModManagerGUI.h +++ b/Source/Menus/ModManagerGUI.h @@ -10,78 +10,58 @@ namespace RTE { class GUIButton; class GUIListBox; - /// /// Integrated mod and script manager user interface composition and handling. - /// class ModManagerGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a ModManagerGUI object in system memory and make it ready for use. - /// - /// Pointer to a GUIScreen interface that will be used by this ModManagerGUI's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this ModManagerGUI's GUIControlManager. Ownership is NOT transferred! - /// Whether this SettingsGUI is part of ModManagerGUI and should have a slightly different layout. + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this ModManagerGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this ModManagerGUI's GUIControlManager. Ownership is NOT transferred! + /// @param createForPauseMenu Whether this SettingsGUI is part of ModManagerGUI and should have a slightly different layout. ModManagerGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu = false); #pragma endregion #pragma region Concrete Methods - /// /// Handles the player interaction with the ModManagerGUI GUI elements. - /// - /// Whether the player requested to return to the main menu. + /// @return Whether the player requested to return to the main menu. bool HandleInputEvents(); - /// /// Draws the ModManagerGUI to the screen. - /// void Draw() const; #pragma endregion private: - /// /// Struct containing information about a valid mod DataModule. - /// struct ModRecord { std::string ModulePath; //!< Mod DataModule path. std::string ModuleName; //!< Mod ModuleName. std::string Description; //!< Mod description. bool Disabled; //!< Whether the mod is disabled through the settings file or not. - /// /// Makes GUI displayable string with mod info. - /// - /// String with mod info. + /// @return String with mod info. std::string GetDisplayString() const { return (Disabled ? "- " : "+ ") + ModulePath + " - " + ModuleName; } - /// /// Comparison operator for sorting the KnownMods list alphabetically by path with std::sort. - /// - /// ModRecord to compare with. - /// Bool with result of the alphabetical comparison. + /// @param rhs ModRecord to compare with. + /// @return Bool with result of the alphabetical comparison. bool operator<(const ModRecord& rhs) const { return ModulePath < rhs.ModulePath; } }; - /// /// Struct containing information about a valid GlobalScript. - /// struct ScriptRecord { std::string PresetName; //!< Script PresetName. std::string Description; //!< Script description. bool Enabled; //!< Whether the script is enabled through the settings file or not. - /// /// Makes GUI displayable string with script info. - /// - /// String with script info. + /// @return String with script info. std::string GetDisplayString() const { return (!Enabled ? "- " : "+ ") + PresetName; } - /// /// Comparison operator for sorting the KnownScripts list alphabetically by PresetName with std::sort. - /// - /// ScriptRecord to compare with. - /// Bool with result of the alphabetical comparison. + /// @param rhs ScriptRecord to compare with. + /// @return Bool with result of the alphabetical comparison. bool operator<(const ScriptRecord& rhs) const { return PresetName < rhs.PresetName; } }; @@ -93,9 +73,7 @@ namespace RTE { bool m_ModsListFetched; //!< Whether the known mods list was fetched, even if no valid mod DataModules were added to it. bool m_ScriptsListFetched; //!< Whether the known scripts list was fetched, even if no valid GlobalScripts were added to it. - /// /// GUI elements that compose the Mod Manager menu screen. - /// GUIButton* m_BackToMainButton; GUIButton* m_ToggleModButton; GUIButton* m_ToggleScriptButton; @@ -104,30 +82,20 @@ namespace RTE { GUILabel* m_ModOrScriptDescriptionLabel; #pragma region Mod and Script Handling - /// /// Gets whether both lists were fetched, even if nothing valid was added to them. - /// - /// Whether both lists were fetched, even if nothing valid was added to them. + /// @return Whether both lists were fetched, even if nothing valid was added to them. bool ListsFetched() const { return m_ModsListFetched && m_ScriptsListFetched; } - /// /// Fills the KnownMods list with all valid mod DataModules, then fills the ModsListBox using it. - /// void PopulateKnownModsList(); - /// /// Fills the KnownScripts list with all valid GlobalScripts, then fills the ScriptsListBox using it. - /// void PopulateKnownScriptsList(); - /// /// Turns currently selected mod on and off and changes GUI elements accordingly. - /// void ToggleMod(); - /// /// Turns currently selected script on and off and changes GUI elements accordingly. - /// void ToggleScript(); #pragma endregion diff --git a/Source/Menus/MultiplayerGameGUI.cpp b/Source/Menus/MultiplayerGameGUI.cpp index 733b237de5..a3ad6e7d66 100644 --- a/Source/Menus/MultiplayerGameGUI.cpp +++ b/Source/Menus/MultiplayerGameGUI.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MultiplayerGameGUI.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Implementation file for the MultiplayerGameGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MultiplayerGameGUI.h" #include "FrameMan.h" @@ -17,21 +5,10 @@ using namespace std; using namespace RTE; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this MultiplayerGameGUI, effectively -// resetting the members of this abstraction level only. - void MultiplayerGameGUI::Clear() { m_pController = 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MultiplayerGameGUI object ready for use. - int MultiplayerGameGUI::Create(Controller* pController) { AAssert(pController, "No controller sent to MultiplayerGameGUI on creation!"); m_pController = pController; @@ -43,27 +20,12 @@ int MultiplayerGameGUI::Create(Controller* pController) { return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the MultiplayerGameGUI object. - void MultiplayerGameGUI::Destroy() { Clear(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame - void MultiplayerGameGUI::Update() { } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu - void MultiplayerGameGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { } diff --git a/Source/Menus/MultiplayerGameGUI.h b/Source/Menus/MultiplayerGameGUI.h index cf73b715ae..b88619448a 100644 --- a/Source/Menus/MultiplayerGameGUI.h +++ b/Source/Menus/MultiplayerGameGUI.h @@ -1,16 +1,9 @@ #ifndef _MULTIPLAYERGAMEGUI_ #define _MULTIPLAYERGAMEGUI_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: MultiplayerGameGUI.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: -// Project: GUI Library -// Author(s): - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// Description: +/// Author(s): +/// Inclusions of header files // #include "FrameMan.h" #include "Timer.h" #include "Vector.h" @@ -23,18 +16,11 @@ namespace RTE { class ObjectPickerGUI; class PieMenuGUI; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: MultiplayerGameGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: - // Parent(s): None. - // Class history: - + /// Description: + /// Class history: class MultiplayerGameGUI { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: // Different modes of this editor enum GUIMode { @@ -42,98 +28,51 @@ namespace RTE { ACTIVE = 1 }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: MultiplayerGameGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a MultiplayerGameGUI object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a MultiplayerGameGUI object in system + /// memory. Create() should be called before using the object. MultiplayerGameGUI() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~MultiplayerGameGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a MultiplayerGameGUI object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a MultiplayerGameGUI object before deletion + /// from system memory. ~MultiplayerGameGUI() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the MultiplayerGameGUI object ready for use. - // Arguments: A poitner to a Controller which will control this Menu. Ownership is - // NOT TRANSFERRED! - // Whether the editor should have all the features enabled, like load/save - // and undo capabilities. - // Which module space that this eidtor will be able to pick objects from. - // -1 means all modules. - // Which Tech module that will be presented as the native one to the player. - // The multiplier of all foreign techs' costs. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the MultiplayerGameGUI object ready for use. + /// @param pController A poitner to a Controller which will control this Menu. Ownership is + /// NOT TRANSFERRED! + /// Whether the editor should have all the features enabled, like load/save + /// and undo capabilities. + /// Which module space that this eidtor will be able to pick objects from. + /// -1 means all modules. + /// Which Tech module that will be presented as the native one to the player. + /// The multiplier of all foreign techs' costs. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(Controller* pController); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire MultiplayerGameGUI, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire MultiplayerGameGUI, including its inherited members, to + /// their default settings or values. void Reset() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the MultiplayerGameGUI object. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the MultiplayerGameGUI object. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Menu each frame - // Arguments: None. - // Return value: None. - + /// Updates the state of this Menu each frame void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the editor - // Arguments: The bitmap to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws the editor + /// @param pTargetBitmap The bitmap to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: // Controller which conrols this menu. Not owned Controller* m_pController; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this MultiplayerGameGUI, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this MultiplayerGameGUI, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/ObjectPickerGUI.cpp b/Source/Menus/ObjectPickerGUI.cpp index 07d017db46..9e372d2f7a 100644 --- a/Source/Menus/ObjectPickerGUI.cpp +++ b/Source/Menus/ObjectPickerGUI.cpp @@ -27,8 +27,6 @@ namespace RTE { BITMAP* ObjectPickerGUI::s_Cursor = nullptr; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::Clear() { m_GUIScreen = nullptr; m_GUIInput = nullptr; @@ -58,8 +56,6 @@ namespace RTE { m_ExpandedModules[0] = true; // Base.rte is always expanded } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ObjectPickerGUI::Create(Controller* controller, int whichModuleSpace, const std::string_view& onlyOfType) { RTEAssert(controller, "No controller sent to ObjectPickerGUI on creation!"); m_Controller = controller; @@ -129,8 +125,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::SetEnabled(bool enable) { if (enable && m_PickerState != PickerState::Enabled && m_PickerState != PickerState::Enabling) { m_PickerState = PickerState::Enabling; @@ -151,8 +145,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::SetNativeTechModule(int whichModule) { if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) { m_NativeTechModuleID = whichModule; @@ -181,8 +173,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ObjectPickerGUI::SetListFocus(PickerFocus listToFocusOn) { if (listToFocusOn == m_PickerFocus) { return false; @@ -201,8 +191,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ObjectPickerGUI::SelectGroupByName(const std::string_view& groupName) { for (const GUIListPanel::Item* groupListItem: *m_GroupsList->GetItemList()) { if (groupListItem->m_Name == groupName) { @@ -214,8 +202,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::SelectGroupByIndex(int groupIndex, bool updateObjectsList) { m_SelectedGroupIndex = (groupIndex < 0) ? m_ShownGroupIndex : groupIndex; m_GroupsList->SetSelectedIndex(m_SelectedGroupIndex); @@ -229,8 +215,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::SelectNextOrPrevGroup(bool selectPrev) { int groupIndex = m_SelectedGroupIndex; if (selectPrev) { @@ -247,8 +231,6 @@ namespace RTE { SelectGroupByIndex(groupIndex); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::UpdateGroupsList() { m_GroupsList->ClearList(); bool showAssemblySchemes = dynamic_cast(g_ActivityMan.GetActivity()); @@ -300,8 +282,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const SceneObject* ObjectPickerGUI::GetSelectedObject() { if (const GUIListPanel::Item* selectedItem = m_ObjectsList->GetSelected()) { return dynamic_cast(selectedItem->m_pEntity); @@ -309,8 +289,6 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::SelectObjectByIndex(int objectIndex, bool playSelectionSound) { m_SelectedObjectIndex = objectIndex; m_ObjectsList->SetSelectedIndex(m_SelectedObjectIndex); @@ -319,8 +297,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::SelectNextOrPrevObject(bool getPrev) { int objectIndex = m_SelectedObjectIndex; if (getPrev) { @@ -337,8 +313,6 @@ namespace RTE { SelectObjectByIndex(objectIndex); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::AddObjectsListModuleGroup(int moduleID) { const DataModule* dataModule = g_PresetMan.GetDataModule(moduleID); std::string moduleName = dataModule->GetFriendlyName(); @@ -347,8 +321,6 @@ namespace RTE { m_ObjectsList->AddItem(moduleName, m_ExpandedModules.at(moduleID) ? "-" : "+", dataModuleIcon, nullptr, moduleID); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::SetObjectsListModuleGroupExpanded(int moduleID, bool expanded) { if (moduleID > 0 && moduleID < m_ExpandedModules.size()) { m_ExpandedModules.at(moduleID) = expanded; @@ -358,8 +330,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::ToggleObjectsListModuleGroupExpansion(int moduleID) { if (moduleID > 0 && moduleID < m_ExpandedModules.size()) { m_ExpandedModules.at(moduleID) = !m_ExpandedModules.at(moduleID); @@ -368,8 +338,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::ShowDescriptionPopupBox() { std::string description = ""; GUIListPanel::Item* objectListItem = m_ObjectsList->GetSelected(); @@ -395,8 +363,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::UpdateObjectsList(bool selectTop) { m_ObjectsList->ClearList(); std::vector> moduleList(g_PresetMan.GetTotalModuleCount(), std::list()); @@ -457,8 +423,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::Update() { m_PopupBox->SetVisible(false); @@ -488,8 +452,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ObjectPickerGUI::HandleInput() { bool objectPickedOrPickerClosed = false; if (m_Controller->IsMouseControlled()) { @@ -556,8 +518,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ObjectPickerGUI::HandleMouseEvents() { int mousePosX; int mousePosY; @@ -608,8 +568,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::AnimateOpenClose() { if (m_PickerState == PickerState::Enabling) { m_ParentBox->SetVisible(true); @@ -641,8 +599,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ObjectPickerGUI::Draw(BITMAP* drawBitmap) const { AllegroScreen drawScreen(drawBitmap); m_GUIControlManager->Draw(&drawScreen); diff --git a/Source/Menus/ObjectPickerGUI.h b/Source/Menus/ObjectPickerGUI.h index 39182e2e2c..08cac7451e 100644 --- a/Source/Menus/ObjectPickerGUI.h +++ b/Source/Menus/ObjectPickerGUI.h @@ -16,71 +16,51 @@ namespace RTE { class GUILabel; class SceneObject; - /// /// A GUI for picking object instances for placement to the Scene in various editors. - /// class ObjectPickerGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a ObjectPickerGUI object in system memory. Create() should be called before using the object. - /// ObjectPickerGUI() { Clear(); } - /// /// Makes the ObjectPickerGUI object ready for use. - /// - /// A pointer to a Controller which will control this Menu. Ownership is NOT transferred! - /// Which DataModule space to be picking from. -1 means pick from all objects loaded in all DataModules. - /// Which lowest common denominator type to be showing. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param controller A pointer to a Controller which will control this Menu. Ownership is NOT transferred! + /// @param whichModuleSpace Which DataModule space to be picking from. -1 means pick from all objects loaded in all DataModules. + /// @param onlyOfType Which lowest common denominator type to be showing. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(Controller* controller, int whichModuleSpace = -1, const std::string_view& onlyOfType = "All"); #pragma endregion #pragma region Destruction - /// /// Resets the entire ObjectPickerGUI, including its inherited members, to their default settings or values. - /// void Reset() { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Reports whether the menu is at all visible or not. - /// - /// + /// @return bool IsVisible() const { return m_PickerState != PickerState::Disabled; } - /// /// Reports whether the menu is enabled or not. - /// - /// + /// @return bool IsEnabled() const { return m_PickerState == PickerState::Enabled || m_PickerState == PickerState::Enabling; } - /// /// Enables or disables the menu. This will animate it in and out of view. - /// - /// Whether to enable or disable the menu. + /// @param enable Whether to enable or disable the menu. void SetEnabled(bool enable = true); - /// /// Sets the controller used by this. The ownership of the controller is NOT transferred! - /// - /// The new controller for this menu. Ownership is NOT transferred! + /// @param controller The new controller for this menu. Ownership is NOT transferred! void SetController(Controller* controller) { m_Controller = controller; } - /// /// Sets where on the screen that this GUI is being drawn to. If upper left corner, then 0, 0. This will affect the way the mouse is positioned etc. - /// - /// The new X position of this entire GUI on the screen. - /// The new Y position of this entire GUI on the screen. + /// @param newPosX The new X position of this entire GUI on the screen. + /// @param newPosY The new Y position of this entire GUI on the screen. void SetPosOnScreen(int newPosX, int newPosY) const { m_GUIControlManager->SetPosOnScreen(newPosX, newPosY); } - /// /// Sets which DataModule space to be picking objects from. If -1, then let the player pick from all loaded modules. - /// - /// The ID of the module to let the player pick objects from. All official module objects will always be presented, in addition to the one passed in here. + /// @param newModuleSpaceID The ID of the module to let the player pick objects from. All official module objects will always be presented, in addition to the one passed in here. void SetModuleSpace(int newModuleSpaceID = -1) { if (newModuleSpaceID != m_ModuleSpaceID) { m_ModuleSpaceID = newModuleSpaceID; @@ -88,87 +68,65 @@ namespace RTE { } } - /// /// Sets which DataModule space to be picking objects from. If -1, then let the player pick from all loaded modules. - /// - /// The ID of the module to let the player pick objects from. All official module objects will always be presented, in addition to the one passed in here. + /// @param showType The ID of the module to let the player pick objects from. All official module objects will always be presented, in addition to the one passed in here. void ShowOnlyType(const std::string_view& showType = "All") { m_ShowType = showType; UpdateGroupsList(); } - /// /// Sets which DataModule ID should be treated as the native tech of the user of this menu. /// This will also apply the DataModule's faction BuyMenu theme skin and background color for visual consistency, if applicable. - /// - /// The module ID to set as the native one. 0 means everything is native. + /// @param whichModule The module ID to set as the native one. 0 means everything is native. void SetNativeTechModule(int whichModule); - /// /// Sets the multiplier of the cost of any foreign Tech items. - /// - /// The scalar multiplier of the costs of foreign Tech items. + /// @param newMultiplier The scalar multiplier of the costs of foreign Tech items. void SetForeignCostMultiplier(float newMultiplier) { m_ForeignCostMult = newMultiplier; } - /// /// Selects the specified group name in the groups list and updates the objects list to show the group's objects. - /// - /// The name of the group to select in the picker. - /// Whether the group was found and switched to successfully. + /// @param groupName The name of the group to select in the picker. + /// @return Whether the group was found and switched to successfully. bool SelectGroupByName(const std::string_view& groupName); #pragma endregion #pragma region Object Picking Handling - /// /// Gets the next object in the objects list, even if the picker is disabled. - /// - /// The next object in the picker list, looping around if necessary. If the next object is an invalid SceneObject (e.g. a module subgroup) then this will recurse until a valid object is found. + /// @return The next object in the picker list, looping around if necessary. If the next object is an invalid SceneObject (e.g. a module subgroup) then this will recurse until a valid object is found. const SceneObject* GetNextObject() { SelectNextOrPrevObject(false); const SceneObject* object = GetSelectedObject(); return object ? object : GetNextObject(); } - /// /// Gets the previous object in the objects list, even if the picker is disabled. - /// - /// The previous object in the picker list, looping around if necessary. If the previous object is an invalid SceneObject (e.g. a module subgroup) then this will recurse until a valid object is found. + /// @return The previous object in the picker list, looping around if necessary. If the previous object is an invalid SceneObject (e.g. a module subgroup) then this will recurse until a valid object is found. const SceneObject* GetPrevObject() { SelectNextOrPrevObject(true); const SceneObject* object = GetSelectedObject(); return object ? object : GetPrevObject(); } - /// /// Reports whether and which object has been picked by the player. There may be an object picked even when the player is not done with the picker, as scrolling through objects (but not mousing over them) picks them. - /// - /// A pointer to the object picked by the player, or nullptr if none was picked. Ownership is NOT transferred! + /// @return A pointer to the object picked by the player, or nullptr if none was picked. Ownership is NOT transferred! const SceneObject* ObjectPicked() const { return m_PickedObject; } - /// /// Reports whether the player has finished using the picker, and the final picked object is returned. - /// - /// The object the player picked before they closed the picker, or nullptr if none was picked. Ownership is NOT transferred!< / returns> + /// @return The object the player picked before they closed the picker, or nullptr if none was picked. Ownership is NOT transferred! const SceneObject* DonePicking() const { return (!IsEnabled() && m_PickedObject) ? m_PickedObject : nullptr; } #pragma endregion #pragma region Concrete Methods - /// /// Updates the state of this ObjectPickerGUI each frame. - /// void Update(); - /// /// Draws the ObjectPickerGUI to the specified BITMAP. - /// - /// The BITMAP to draw on. + /// @param drawBitmap The BITMAP to draw on. void Draw(BITMAP* drawBitmap) const; #pragma endregion private: - /// /// Enumeration for ObjectPicker states when enabling/disabling the ObjectPicker. - /// enum class PickerState { Enabling, Enabled, @@ -176,9 +134,7 @@ namespace RTE { Disabled }; - /// /// Enumeration for the ObjectPicker columns ListBox focus states. - /// enum class PickerFocus { GroupList, ObjectList @@ -217,107 +173,75 @@ namespace RTE { std::vector m_ExpandedModules; //!< The modules that have been expanded in the item list. #pragma region General List Handling - /// /// Sets the currently focused list in the picker. For list item highlighting and non-mouse input handling. - /// - /// The list to focus on. See PickerFocus enumeration. - /// Whether a focus change was made or not. + /// @param listToFocusOn The list to focus on. See PickerFocus enumeration. + /// @return Whether a focus change was made or not. bool SetListFocus(PickerFocus listToFocusOn); #pragma endregion #pragma region Group List Handling - /// /// Selects the specified group index in the groups list and updates the objects list to show the group's objects. - /// - /// The group index to select. Removing any selection (with index -1) will be overridden and the currently shown group will be selected instead. - /// Whether to update the objects list after making the selection or not. + /// @param groupIndex The group index to select. Removing any selection (with index -1) will be overridden and the currently shown group will be selected instead. + /// @param updateObjectsList Whether to update the objects list after making the selection or not. void SelectGroupByIndex(int groupIndex, bool updateObjectsList = true); - /// /// Selects the next or previous group from the one that is currently selected in the groups list. - /// - /// Whether to select the previous group. Next group will be selected by default. + /// @param selectPrev Whether to select the previous group. Next group will be selected by default. void SelectNextOrPrevGroup(bool selectPrev = false); - /// /// Adds all groups with a specific type already defined in PresetMan that are within the set ModuleSpaceID and aren't empty to the current groups list. - /// void UpdateGroupsList(); #pragma endregion #pragma region Object List Handling - /// /// Gets the SceneObject from the currently selected index in the objects list. Ownership is NOT transferred! - /// - /// The SceneObject of the currently selected index in the objects list. Nullptr if no valid object is selected (eg. a module subgroup). + /// @return The SceneObject of the currently selected index in the objects list. Nullptr if no valid object is selected (eg. a module subgroup). const SceneObject* GetSelectedObject(); - /// /// Selects the specified object index in the objects list. - /// - /// The object index to select. - /// Whether to play the selection change sound or not. + /// @param objectIndex The object index to select. + /// @param playSelectionSound Whether to play the selection change sound or not. void SelectObjectByIndex(int objectIndex, bool playSelectionSound = true); - /// /// Selects the next or previous object from the one that is currently selected in the objects list. - /// - /// Whether to select the previous object. Next object will be selected by default. + /// @param getPrev Whether to select the previous object. Next object will be selected by default. void SelectNextOrPrevObject(bool getPrev = false); - /// /// Add the expandable DataModule group separator in the objects list with appropriate name and icon. - /// - /// The DataModule ID to add group separator for. + /// @param moduleID The DataModule ID to add group separator for. void AddObjectsListModuleGroup(int moduleID); - /// /// Sets whether a DataModule group separator shown in the objects list should be expanded or collapsed. - /// - /// The module ID to set as expanded or collapsed. - /// Whether should be expanded or not. + /// @param moduleID The module ID to set as expanded or collapsed. + /// @param expanded Whether should be expanded or not. void SetObjectsListModuleGroupExpanded(int moduleID, bool expanded = true); - /// /// Toggles the expansion/collapse of a DataModule group separator in the objects list. - /// - /// The module ID to toggle for. + /// @param moduleID The module ID to toggle for. void ToggleObjectsListModuleGroupExpansion(int moduleID); - /// /// Displays the popup box with the description of the selected item in the objects list. - /// void ShowDescriptionPopupBox(); - /// /// Adds all objects of the currently selected group to the objects list. - /// - /// Whether to reset the selection to the top of the list when we're done updating this. + /// @param selectTop Whether to reset the selection to the top of the list when we're done updating this. void UpdateObjectsList(bool selectTop = true); #pragma endregion #pragma region Update Breakdown - /// /// Player input handling for all types of input devices. - /// - /// True if the picker was set to be closed or by making a selection from the objects list. + /// @return True if the picker was set to be closed or by making a selection from the objects list. bool HandleInput(); - /// /// Player mouse input event handling of the GUIControls of this ObjectPickerGUI. - /// - /// True if the picker was set to be closed by clicking off it or by making a selection from the objects list. + /// @return True if the picker was set to be closed by clicking off it or by making a selection from the objects list. bool HandleMouseEvents(); - /// /// Open/close animation handling and GUI element enabling/disabling. - /// void AnimateOpenClose(); #pragma endregion - /// /// Clears all the member variables of this ObjectPickerGUI, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/PauseMenuGUI.cpp b/Source/Menus/PauseMenuGUI.cpp index a748ea4f3d..b267e4be9f 100644 --- a/Source/Menus/PauseMenuGUI.cpp +++ b/Source/Menus/PauseMenuGUI.cpp @@ -21,8 +21,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::Clear() { m_GUIControlManager = nullptr; m_ActiveDialogBox = nullptr; @@ -49,8 +47,6 @@ namespace RTE { m_PauseMenuButtons.fill(nullptr); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuScreenSkin.ini"); @@ -94,8 +90,6 @@ namespace RTE { m_ModManagerMenu = std::make_unique(guiScreen, guiInput, true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::SetBackButtonTargetName(const std::string& menuName) { std::string newButtonText = "Back to " + menuName + " Menu"; @@ -111,8 +105,6 @@ namespace RTE { m_PauseMenuButtons[PauseMenuButton::BackToMainButton]->CenterInParent(true, false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::EnableOrDisablePauseMenuFeatures() { bool disableModManager = true; @@ -135,8 +127,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::SetActiveMenuScreen(PauseMenuScreen screenToShow, bool playButtonPressSound) { if (screenToShow != m_ActiveMenuScreen) { m_ActiveMenuScreen = screenToShow; @@ -149,8 +139,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PauseMenuGUI::PauseMenuUpdateResult PauseMenuGUI::Update() { m_UpdateResult = PauseMenuUpdateResult::NoEvent; @@ -183,8 +171,6 @@ namespace RTE { return m_UpdateResult; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::HandleBackNavigation(bool backButtonPressed) { if (!m_ActiveDialogBox && (backButtonPressed || g_UInputMan.KeyPressed(SDLK_ESCAPE))) { if (m_ActiveMenuScreen != PauseMenuScreen::MainScreen) { @@ -205,8 +191,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PauseMenuGUI::HandleInputEvents() { if (m_ActiveMenuScreen == PauseMenuScreen::MainScreen) { int mousePosX; @@ -239,8 +223,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::UpdateHoveredButton(const GUIButton* hoveredButton) { int hoveredButtonIndex = -1; if (hoveredButton) { @@ -261,8 +243,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::BlinkResumeButton() { if (m_HoveredButton && m_HoveredButton == m_PauseMenuButtons[PauseMenuButton::ResumeButton]) { m_PauseMenuButtons[PauseMenuButton::ResumeButton]->SetText(m_ResumeButtonBlinkTimer.AlternateReal(500) ? m_ButtonHoveredText[PauseMenuButton::ResumeButton] : "]" + m_ButtonHoveredText[PauseMenuButton::ResumeButton] + "["); @@ -271,8 +251,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PauseMenuGUI::Draw() { g_WindowMan.DrawPostProcessBuffer(); blit(m_BackdropBitmap, g_FrameMan.GetBackBuffer32(), 0, 0, 0, 0, m_BackdropBitmap->w, m_BackdropBitmap->h); diff --git a/Source/Menus/PauseMenuGUI.h b/Source/Menus/PauseMenuGUI.h index 98e1f0b29b..cddc060368 100644 --- a/Source/Menus/PauseMenuGUI.h +++ b/Source/Menus/PauseMenuGUI.h @@ -16,15 +16,11 @@ namespace RTE { class ModManagerGUI; class SaveLoadMenuGUI; - /// /// Handling for the pause menu screen composition and interaction. - /// class PauseMenuGUI { public: - /// /// Enumeration for the results of the PauseMenuGUI input and event update. - /// enum class PauseMenuUpdateResult { NoEvent, BackToMain, @@ -32,54 +28,40 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a PauseMenuGUI object in system memory and make it ready for use. - /// - /// Pointer to a GUIScreen interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! PauseMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { Clear(); Create(guiScreen, guiInput); } - /// /// Makes the PauseMenuGUI object ready for use. - /// - /// Pointer to a GUIScreen interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this PauseMenuGUI's GUIControlManager. Ownership is NOT transferred! void Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput); #pragma endregion #pragma region Setters - /// /// Sets the "Back to Main Menu" button text to the menu we will be going back to. - /// - /// The target menu name, e.g. "Conquest" will result in "Back to Conquest Menu". + /// @param menuName The target menu name, e.g. "Conquest" will result in "Back to Conquest Menu". void SetBackButtonTargetName(const std::string& menuName); #pragma endregion #pragma region Concrete Methods - /// /// Enables or disables buttons depending on the current Activity. - /// void EnableOrDisablePauseMenuFeatures(); - /// /// Updates the PauseMenuGUI state. - /// - /// The result of the PauseMenuGUI input and event update. See PauseMenuUpdateResult enumeration. + /// @return The result of the PauseMenuGUI input and event update. See PauseMenuUpdateResult enumeration. PauseMenuUpdateResult Update(); - /// /// Draws the PauseMenuGUI to the screen. - /// void Draw(); #pragma endregion private: - /// /// Enumeration for the different sub-menu screens of the pause menu. - /// enum PauseMenuScreen { MainScreen, SaveOrLoadGameScreen, @@ -88,9 +70,7 @@ namespace RTE { ScreenCount }; - /// /// Enumeration for all the different buttons of the pause menu. - /// enum PauseMenuButton { BackToMainButton, SaveOrLoadGameButton, @@ -124,49 +104,35 @@ namespace RTE { bool m_SavingButtonsDisabled; //!< Whether the save and load buttons are disabled and hidden. bool m_ModManagerButtonDisabled; //!< Whether the mod manager button is disabled and hidden. - /// /// GUI elements that compose the pause menu screen. - /// GUICollectionBox* m_PauseMenuBox; std::array m_PauseMenuButtons; #pragma region Menu Screen Handling - /// /// Sets the PauseMenuGUI to display a menu screen. - /// - /// Which menu screen to display. See PauseMenuScreen enumeration. - /// Whether to play a sound if the menu screen change is triggered by a button press. + /// @param screenToShow Which menu screen to display. See PauseMenuScreen enumeration. + /// @param playButtonPressSound Whether to play a sound if the menu screen change is triggered by a button press. void SetActiveMenuScreen(PauseMenuScreen screenToShow, bool playButtonPressSound = true); #pragma endregion #pragma region Update Breakdown - /// /// Handles returning to the pause menu from one of the sub-menus if the player requested to return via the back button or the esc key. Also handles closing active dialog boxes with the esc key. - /// - /// Whether the player requested to return to the pause menu from one of the sub-menus via back button. + /// @param backButtonPressed Whether the player requested to return to the pause menu from one of the sub-menus via back button. void HandleBackNavigation(bool backButtonPressed); - /// /// Handles the player interaction with the PauseMenuGUI GUI elements. - /// - /// Whether the player requested to return to the main menu. + /// @return Whether the player requested to return to the main menu. bool HandleInputEvents(); - /// /// Updates the currently hovered button text to give the hovered visual and updates the previously hovered button to remove the hovered visual. - /// - /// Pointer to the currently hovered button, if any. Acquired by GUIControlManager::GetControlUnderPoint. + /// @param hoveredButton Pointer to the currently hovered button, if any. Acquired by GUIControlManager::GetControlUnderPoint. void UpdateHoveredButton(const GUIButton* hoveredButton); - /// /// Animates (blinking) the resume game button. - /// void BlinkResumeButton(); #pragma endregion - /// /// Clears all the member variables of this PauseMenuGUI, effectively resetting the members of this object. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/SaveLoadMenuGUI.cpp b/Source/Menus/SaveLoadMenuGUI.cpp index 631f55763d..e718bdbc5d 100644 --- a/Source/Menus/SaveLoadMenuGUI.cpp +++ b/Source/Menus/SaveLoadMenuGUI.cpp @@ -21,8 +21,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SaveLoadMenuGUI::SaveLoadMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); @@ -74,8 +72,6 @@ namespace RTE { SwitchToConfirmDialogMode(ConfirmDialogMode::None); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::PopulateSaveGamesList() { m_SaveGames.clear(); m_SaveGameName->SetText(""); @@ -125,8 +121,6 @@ namespace RTE { UpdateSaveGamesGUIList(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::UpdateSaveGamesGUIList() { const std::string& currentOrder = m_OrderByComboBox->GetSelectedItem()->m_Name; if (currentOrder == "Name") { @@ -171,8 +165,6 @@ namespace RTE { m_SaveGamesListBox->ScrollToTop(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SaveLoadMenuGUI::LoadSave() { bool success = g_ActivityMan.LoadAndLaunchGame(m_SaveGameName->GetText()); @@ -185,8 +177,6 @@ namespace RTE { return success; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::CreateSave() { bool success = g_ActivityMan.SaveCurrentGame(m_SaveGameName->GetText()); if (success) { @@ -198,8 +188,6 @@ namespace RTE { PopulateSaveGamesList(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::DeleteSave() { std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + m_SaveGameName->GetText() + ".ccsave"; @@ -209,8 +197,6 @@ namespace RTE { PopulateSaveGamesList(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::UpdateButtonEnabledStates() { bool allowSave = g_ActivityMan.GetActivity() && g_ActivityMan.GetActivity()->GetAllowsUserSaving() && m_SaveGameName->GetText() != ""; @@ -242,8 +228,6 @@ namespace RTE { m_ActivityCannotBeSavedLabel->SetVisible(g_ActivityMan.GetActivity() && !g_ActivityMan.GetActivity()->GetAllowsUserSaving()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::SwitchToConfirmDialogMode(ConfirmDialogMode mode) { m_ConfirmDialogMode = mode; @@ -262,8 +246,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SaveLoadMenuGUI::HandleInputEvents(PauseMenuGUI* pauseMenu) { m_GUIControlManager->Update(); @@ -317,15 +299,11 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::Refresh() { PopulateSaveGamesList(); UpdateButtonEnabledStates(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SaveLoadMenuGUI::Draw() const { m_GUIControlManager->Draw(); } diff --git a/Source/Menus/SaveLoadMenuGUI.h b/Source/Menus/SaveLoadMenuGUI.h index 448e7ca3c3..451bb99cfa 100644 --- a/Source/Menus/SaveLoadMenuGUI.h +++ b/Source/Menus/SaveLoadMenuGUI.h @@ -17,38 +17,28 @@ namespace RTE { class GUIComboBox; class GUICollectionBox; - /// /// Integrated savegame user interface composition and handling. - /// class SaveLoadMenuGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a SaveLoadMenuGUI object in system memory and make it ready for use. - /// - /// Pointer to a GUIScreen interface that will be used by this SaveLoadMenuGUI's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this SaveLoadMenuGUI's GUIControlManager. Ownership is NOT transferred! - /// Whether this SettingsGUI is part of SaveLoadMenuGUI and should have a slightly different layout. + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this SaveLoadMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this SaveLoadMenuGUI's GUIControlManager. Ownership is NOT transferred! + /// @param createForPauseMenu Whether this SettingsGUI is part of SaveLoadMenuGUI and should have a slightly different layout. SaveLoadMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu = false); #pragma endregion #pragma region Concrete Methods - /// /// Handles the player interaction with the SaveLoadMenuGUI GUI elements. - /// - /// Pointer to the pause menu, if we're being called from the pause menu. Ownership is NOT transferred! - /// Whether the player requested to return to the main menu. + /// @param pauseMenu Pointer to the pause menu, if we're being called from the pause menu. Ownership is NOT transferred! + /// @return Whether the player requested to return to the main menu. bool HandleInputEvents(PauseMenuGUI* pauseMenu = nullptr); - /// /// Causes a refresh of the save files. - /// void Refresh(); - /// /// Draws the SaveLoadMenuGUI to the screen. - /// void Draw() const; #pragma endregion @@ -59,9 +49,7 @@ namespace RTE { ConfirmDelete }; - /// /// Struct containing information about a valid Savegame. - /// struct SaveRecord { std::filesystem::path SavePath; //!< Savegame filepath. std::filesystem::file_time_type SaveDate; //!< Last modified date. @@ -75,9 +63,7 @@ namespace RTE { bool m_SaveGamesFetched; //!< Whether the savegames list has been fetched. - /// /// GUI elements that compose the Mod Manager menu screen. - /// GUICollectionBox* m_SaveGameMenuBox; GUIButton* m_BackToMainButton; GUITextBox* m_SaveGameName; @@ -97,47 +83,31 @@ namespace RTE { GUIButton* m_CancelButton; #pragma region Savegame Handling - /// /// Gets whether both lists were fetched, even if nothing valid was added to them. - /// - /// Whether save games were fetched, even if nothing valid was added to them. + /// @return Whether save games were fetched, even if nothing valid was added to them. bool ListsFetched() const { return m_SaveGamesFetched; } - /// /// Fills the SaveGames list with all valid savegames. - /// void PopulateSaveGamesList(); - /// /// Updates the SaveGamesListBox GUI. - /// void UpdateSaveGamesGUIList(); - /// /// Loads the currently selected savefile. - /// - /// Whether a same was succesfully loaded. + /// @return Whether a same was succesfully loaded. bool LoadSave(); - /// /// Creates a new savefile (or overwrites the existing one) with the name from the textbox. - /// void CreateSave(); - /// /// Deletes the savefile with the name from the textbox. - /// void DeleteSave(); #pragma endregion - /// /// Updates buttons and sets whether or not they should be enabled. - /// void UpdateButtonEnabledStates(); - /// /// Shows confirmation box for overwrite or delete. - /// void SwitchToConfirmDialogMode(ConfirmDialogMode mode); // Disallow the use of some implicit methods. diff --git a/Source/Menus/ScenarioActivityConfigGUI.cpp b/Source/Menus/ScenarioActivityConfigGUI.cpp index 272fda0dd8..18069d1990 100644 --- a/Source/Menus/ScenarioActivityConfigGUI.cpp +++ b/Source/Menus/ScenarioActivityConfigGUI.cpp @@ -19,8 +19,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ScenarioActivityConfigGUI::ScenarioActivityConfigGUI(GUIControlManager* parentControlManager) : m_GUIControlManager(parentControlManager) { m_ActivityConfigBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxActivityConfig")); @@ -69,8 +67,6 @@ namespace RTE { m_TechListFetched = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioActivityConfigGUI::PopulateTechComboBoxes() { for (int team = Activity::Teams::TeamOne; team < Activity::Teams::MaxTeamCount; ++team) { m_TeamTechComboBoxes[team]->GetListPanel()->AddItem("-All-", "", nullptr, nullptr, -2); @@ -90,14 +86,10 @@ namespace RTE { m_TechListFetched = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ScenarioActivityConfigGUI::IsEnabled() const { return m_ActivityConfigBox->GetEnabled() && m_ActivityConfigBox->GetVisible(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioActivityConfigGUI::SetEnabled(bool enable, const Activity* selectedActivity, Scene* selectedScene) { m_ActivityConfigBox->SetEnabled(enable); m_ActivityConfigBox->SetVisible(enable); @@ -132,8 +124,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioActivityConfigGUI::ResetActivityConfigBox() { m_ActivityDifficultyLabel->SetText(" " + Activity::GetDifficultyString(m_ActivityDifficultySlider->GetValue())); @@ -205,8 +195,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioActivityConfigGUI::StartGame() { GameActivity* gameActivity = dynamic_cast(m_SelectedActivity->Clone()); @@ -247,8 +235,6 @@ namespace RTE { g_ActivityMan.SetStartActivity(gameActivity); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ScenarioActivityConfigGUI::Update(int mouseX, int mouseY) { UpdatePlayerTeamSetupCell(mouseX, mouseY); @@ -295,8 +281,6 @@ namespace RTE { return HandleInputEvents(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioActivityConfigGUI::UpdateStartingGoldSliderAndLabel() { if (!m_StartingGoldAdjustedManually) { if (m_ActivityDifficultySlider->GetValue() <= Activity::DifficultySetting::CakeDifficulty && m_SelectedActivity->GetDefaultGoldCakeDifficulty() > -1) { @@ -327,8 +311,6 @@ namespace RTE { m_StartingGoldLabel->SetText(goldString); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioActivityConfigGUI::UpdatePlayerTeamSetupCell(int mouseX, int mouseY) { if (const GUICollectionBox* hoveredCell = dynamic_cast(m_GUIControlManager->GetControlUnderPoint(mouseX, mouseY, m_PlayersAndTeamsConfigBox, 1))) { int hoveredPlayer = PlayerColumns::PlayerColumnCount; @@ -354,8 +336,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioActivityConfigGUI::HandleClickOnPlayerTeamSetupCell(int clickedPlayer, int clickedTeam) { m_PlayerBoxes.at(clickedPlayer).at(clickedTeam)->SetDrawType(GUICollectionBox::Image); const Icon* playerIcon = (clickedPlayer != PlayerColumns::PlayerCPU) ? g_UInputMan.GetSchemeIcon(clickedPlayer) : dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Device CPU")); @@ -416,8 +396,6 @@ namespace RTE { g_GUISound.FocusChangeSound()->Play(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ScenarioActivityConfigGUI::HandleInputEvents() { GUIEvent guiEvent; while (m_GUIControlManager->GetEvent(&guiEvent)) { @@ -454,8 +432,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioActivityConfigGUI::Draw() { m_GUIControlManager->Draw(); diff --git a/Source/Menus/ScenarioActivityConfigGUI.h b/Source/Menus/ScenarioActivityConfigGUI.h index a175bd2c88..f5a1b85b93 100644 --- a/Source/Menus/ScenarioActivityConfigGUI.h +++ b/Source/Menus/ScenarioActivityConfigGUI.h @@ -16,63 +16,47 @@ namespace RTE { class GUISlider; class GUIEvent; - /// /// Handling for the scenario Activity configuration screen composition and interaction. - /// class ScenarioActivityConfigGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a ScenarioActivityConfigGUI object in system memory and make it ready for use. - /// - /// Pointer to the parent GUIControlManager which owns all the GUIControls of this ScenarioActivityConfigGUI. Ownership is NOT transferred! + /// @param parentControlManager Pointer to the parent GUIControlManager which owns all the GUIControls of this ScenarioActivityConfigGUI. Ownership is NOT transferred! explicit ScenarioActivityConfigGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters - /// /// Gets whether this ScenarioActivityConfigGUI is currently visible and enabled. - /// - /// Whether this ScenarioActivityConfigGUI is currently visible and enabled. + /// @return Whether this ScenarioActivityConfigGUI is currently visible and enabled. bool IsEnabled() const; - /// /// Enables or disables the ScenarioActivityConfigGUI. - /// - /// Show and enable or hide and disable the ScenarioActivityConfigGUI. - /// Pointer to the Activity this ScenarioActivityConfigGUI will be configuring for. - /// Pointer to the Scene the passed in Activity will be using. + /// @param enable Show and enable or hide and disable the ScenarioActivityConfigGUI. + /// @param selectedActivity Pointer to the Activity this ScenarioActivityConfigGUI will be configuring for. + /// @param selectedScene Pointer to the Scene the passed in Activity will be using. void SetEnabled(bool enable, const Activity* selectedActivity = nullptr, Scene* selectedScene = nullptr); #pragma endregion #pragma region Concrete Methods - /// /// Updates the ScenarioActivityConfigGUI state. - /// - /// Mouse X position. - /// Mouse Y position. - /// Whether the player started a new game through the ScenarioActivityConfigGUI. + /// @param mouseX Mouse X position. + /// @param mouseY Mouse Y position. + /// @return Whether the player started a new game through the ScenarioActivityConfigGUI. bool Update(int mouseX, int mouseY); - /// /// Draws the ScenarioActivityConfigGUI to the screen. - /// void Draw(); #pragma endregion private: - /// /// Enumeration for all the player columns in the player setup box. "Extends" the Players enumeration by adding an entry for the CPU player. - /// enum PlayerColumns { PlayerCPU = Players::MaxPlayerCount, PlayerColumnCount }; - /// /// Enumeration for all the team rows in the player setup box. "Extends" the Teams enumeration by adding an entry for unused (disabled) Team. - /// enum TeamRows { DisabledTeam = Activity::Teams::MaxTeamCount, TeamRowCount @@ -91,9 +75,7 @@ namespace RTE { bool m_TechListFetched; //!< Whether the tech list was fetched and each team's ComboBox was populated with it, even if no valid tech modules were added. - /// /// GUI elements that compose the Activity setup box. - /// GUICollectionBox* m_ActivityConfigBox; GUILabel* m_StartErrorLabel; GUIButton* m_StartGameButton; @@ -115,45 +97,31 @@ namespace RTE { std::array m_TeamAISkillSliders; #pragma region Activity Configuration Screen Handling - /// /// Fills each team's Tech ComboBox with all valid Tech DataModules. - /// void PopulateTechComboBoxes(); - /// /// Resets the configuration screen to the selected Activity's default settings and enables/disables attribute settings accordingly, making the configuration screen ready for interaction. - /// void ResetActivityConfigBox(); - /// /// Sets up and starts the currently selected Activity with the configured settings. - /// void StartGame(); - /// /// Updates the starting gold slider to the Activity difficulty setting (when applicable) and updates the value in the label according to the value in the slider. - /// - /// + /// @return void UpdateStartingGoldSliderAndLabel(); - /// /// Updates the currently hovered cell in the players and teams config box to apply the hovered visual and removes the hovered visual from any other cells. Also handles clicking on cells. - /// - /// Mouse X position. - /// Mouse Y position. + /// @param mouseX Mouse X position. + /// @param mouseY Mouse Y position. void UpdatePlayerTeamSetupCell(int mouseX, int mouseY); - /// /// Handles the player interaction with a cell in the players and teams config box. - /// - /// The player box that was clicked. - /// The team box that was clicked. + /// @param clickedPlayer The player box that was clicked. + /// @param clickedTeam The team box that was clicked. void HandleClickOnPlayerTeamSetupCell(int clickedPlayer, int clickedTeam); - /// /// Handles the player interaction with the ScenarioActivityConfigGUI GUI elements. - /// - /// Whether the player started a new game through the ScenarioActivityConfigGUI. + /// @return Whether the player started a new game through the ScenarioActivityConfigGUI. bool HandleInputEvents(); #pragma endregion diff --git a/Source/Menus/ScenarioGUI.cpp b/Source/Menus/ScenarioGUI.cpp index f86bce4526..610eae823a 100644 --- a/Source/Menus/ScenarioGUI.cpp +++ b/Source/Menus/ScenarioGUI.cpp @@ -23,8 +23,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::Clear() { m_RootBoxMaxWidth = 0; @@ -51,8 +49,6 @@ namespace RTE { m_DrawDefaultScenePreview = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); @@ -77,8 +73,6 @@ namespace RTE { m_ActivityConfigBox = std::make_unique(m_GUIControlManager.get()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::CreateActivityInfoBox() { m_ActivityInfoBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxActivityInfo")); m_ActivityInfoBox->SetPositionRel(16, 16); @@ -88,8 +82,6 @@ namespace RTE { m_ActivityDescriptionLabel->SetFont(m_GUIControlManager->GetSkin()->GetFont("FontSmall.png")); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::CreateSceneInfoBox() { m_SceneInfoBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxSceneInfo")); m_SceneInfoBox->SetPositionRel(m_RootBox->GetWidth() - m_SceneInfoBox->GetWidth() - 16, 16); @@ -109,8 +101,6 @@ namespace RTE { m_ScenePreviewBitmap->Create(c_ScenePreviewWidth, c_ScenePreviewHeight, 32); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::SetEnabled(const Vector& center, float radius) { bool centerChanged = (center != m_PlanetCenter); m_PlanetCenter = center; @@ -130,8 +120,6 @@ namespace RTE { m_ScenePreviewAnimTimer.Reset(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::SetDraggedBox(int mouseX, int mouseY) { GUICollectionBox* hoveredBox = dynamic_cast(m_GUIControlManager->GetControlUnderPoint(mouseX, mouseY, m_RootBox, 1)); const GUIControl* hoveredControl = m_GUIControlManager->GetControlUnderPoint(mouseX, mouseY, hoveredBox, 1); @@ -141,8 +129,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::SetSelectedActivity(const Activity* newSelectedActivity) { m_SelectedActivity = newSelectedActivity; m_ActivityScenes = nullptr; @@ -168,8 +154,6 @@ namespace RTE { m_ActivityInfoBox->Resize(m_ActivityInfoBox->GetWidth(), m_ActivityDescriptionLabel->ResizeHeightToFit() + 60); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::SetSelectedScene(Scene* newSelectedScene) { m_SelectedScene = newSelectedScene; if (m_SelectedScene) { @@ -202,8 +186,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::DragBox(int mouseX, int mouseY) { if (m_DraggedBox) { m_DraggedBox->MoveRelative(mouseX - m_PrevMousePos.GetFloorIntX(), mouseY - m_PrevMousePos.GetFloorIntY()); @@ -219,8 +201,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::FetchActivitiesAndScenesLists() { int prevSelectedActivityIndex = m_ActivitySelectComboBox->GetSelectedIndex(); Scene* prevSelectedScene = m_SelectedScene; @@ -271,8 +251,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::AdjustSitePointOffsetsOnPlanet(const std::vector& sceneList) const { for (Scene* sceneListEntry: sceneList) { int sceneYPos = (m_PlanetCenter + sceneListEntry->GetLocation() + sceneListEntry->GetLocationOffset()).GetFloorIntY(); @@ -327,8 +305,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::CalculateLinesToSitePoint() { m_LineToSitePoints.clear(); if (!m_SelectedScene) { @@ -400,8 +376,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ScenarioGUI::ScenarioMenuUpdateResult ScenarioGUI::Update() { m_UpdateResult = ScenarioMenuUpdateResult::NoEvent; @@ -455,8 +429,6 @@ namespace RTE { return m_UpdateResult; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::UpdateHoveredSitePointLabel(int mouseX, int mouseY) { bool foundAnyHover = false; if (m_ActivityScenes && !m_DraggedBox && !m_ActivityInfoBox->PointInside(mouseX, mouseY) && !m_SceneInfoBox->PointInside(mouseX, mouseY)) { @@ -489,8 +461,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::HandleInputEvents(int mouseX, int mouseY) { GUIEvent guiEvent; while (m_GUIControlManager->GetEvent(&guiEvent)) { @@ -535,8 +505,6 @@ namespace RTE { m_PrevMousePos.SetXY(static_cast(mouseX), static_cast(mouseY)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::Draw() const { // Early return to avoid single frame flicker before title screen goes into fadeout. if (m_UpdateResult == ScenarioMenuUpdateResult::ActivityStarted) { @@ -561,8 +529,6 @@ namespace RTE { m_GUIControlManager->DrawMouse(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::DrawSitePoints(BITMAP* drawBitmap) const { int blendAmount = 0; for (const Scene* scenePointer: *m_ActivityScenes) { @@ -589,8 +555,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ScenarioGUI::DrawLinesToSitePoint(BITMAP* drawBitmap) const { int blendAmount = 0; int drawColor = c_GUIColorWhite; diff --git a/Source/Menus/ScenarioGUI.h b/Source/Menus/ScenarioGUI.h index 62ba79d98d..6b4fe7447a 100644 --- a/Source/Menus/ScenarioGUI.h +++ b/Source/Menus/ScenarioGUI.h @@ -17,15 +17,11 @@ namespace RTE { class GUIButton; class GUILabel; - /// /// Handling for the scenario menu screen composition and sub-menu interaction. - /// class ScenarioGUI { public: - /// /// Enumeration for the results of the ScenarioGUI input and event update. - /// enum class ScenarioMenuUpdateResult { NoEvent, BackToMain, @@ -34,43 +30,33 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate this ScenarioGUI object in system memory. - /// - /// Pointer to a GUIScreen interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! ScenarioGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput) { Clear(); Create(guiScreen, guiInput); } - /// /// Makes the ScenarioGUI object ready for use. - /// - /// Pointer to a GUIScreen interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this ScenarioGUI's GUIControlManager. Ownership is NOT transferred! void Create(AllegroScreen* guiScreen, GUIInputWrapper* guiInput); #pragma endregion #pragma region Setters - /// /// Enables the GUI elements for the menu, sets the planet coordinates on the screen so the menu can properly overlay it, and fetches the Scenes and Activities lists. - /// - /// The absolute screen coordinates of the planet's center. - /// The radius, in screen pixel units, of the planet. + /// @param center The absolute screen coordinates of the planet's center. + /// @param radius The radius, in screen pixel units, of the planet. void SetEnabled(const Vector& center, float radius); #pragma endregion #pragma region Concrete Methods - /// /// Updates the ScenarioGUI state. - /// - /// The result of the ScenarioGUI input and event update. See ScenarioMenuUpdateResult enumeration. + /// @return The result of the ScenarioGUI input and event update. See ScenarioMenuUpdateResult enumeration. ScenarioMenuUpdateResult Update(); - /// /// Draws the ScenarioGUI to the screen. - /// void Draw() const; #pragma endregion @@ -102,9 +88,7 @@ namespace RTE { std::unique_ptr m_ActivityConfigBox; //!< The Activity configuration box. - /// /// GUI elements that compose the scenario menu screen. - /// GUICollectionBox* m_RootBox; GUICollectionBox* m_ActivityConfigBoxRootBox; GUIButton* m_BackToMainButton; @@ -122,94 +106,66 @@ namespace RTE { GUILabel* m_SitePointNameLabel; #pragma region Create Breakdown - /// /// Creates all the elements that compose the Activity info box. - /// void CreateActivityInfoBox(); - /// /// Creates all the elements that compose the Scene info box. - /// void CreateSceneInfoBox(); #pragma endregion #pragma region Scenario Menu Handling - /// /// Sets the CollectionBox that is currently being dragged, if applicable. - /// - /// Mouse X position. - /// Mouse Y position. + /// @param mouseX Mouse X position. + /// @param mouseY Mouse Y position. void SetDraggedBox(int mouseX, int mouseY); - /// /// Sets the selected Activity, refreshes the compatible Scenes on the planet and updates the Activity info box appropriately. - /// - /// The new selected Activity. + /// @param newSelectedActivity The new selected Activity. void SetSelectedActivity(const Activity* newSelectedActivity); - /// /// Sets the currently selected Scene and updates the Scene info box appropriately. - /// - /// The new selected Scene. + /// @param newSelectedScene The new selected Scene. void SetSelectedScene(Scene* newSelectedScene); - /// /// Moves the CollectionBox that is selected as being dragged, if any. - /// - /// Mouse X position to calculate box position. - /// Mouse Y position to calculate box position. + /// @param mouseX Mouse X position to calculate box position. + /// @param mouseY Mouse Y position to calculate box position. void DragBox(int mouseX, int mouseY); - /// /// Fetches all the available Scenes and Activity presets from PresetMan. - /// void FetchActivitiesAndScenesLists(); - /// /// Adjusts the positions of the site points on the planet if they don't fit the screen or overlap. - /// - /// Vector of Scenes to adjust positions for. + /// @param sceneList Vector of Scenes to adjust positions for. void AdjustSitePointOffsetsOnPlanet(const std::vector& sceneList) const; - /// /// Calculates how to draw lines from the Scene info box to the selected site point on the planet. - /// void CalculateLinesToSitePoint(); #pragma endregion #pragma region Update Breakdown - /// /// Displays the site name label if the mouse is over a site point. - /// - /// Mouse X position. - /// Mouse Y position. + /// @param mouseX Mouse X position. + /// @param mouseY Mouse Y position. void UpdateHoveredSitePointLabel(int mouseX, int mouseY); - /// /// Handles the player interaction with the ScenarioGUI GUI elements. - /// - /// Mouse X position. - /// Mouse Y position. + /// @param mouseX Mouse X position. + /// @param mouseY Mouse Y position. void HandleInputEvents(int mouseX, int mouseY); #pragma endregion #pragma region Draw Breakdown - /// /// Draws the site points on top of the planet. - /// - /// The bitmap to draw on. + /// @param drawBitmap The bitmap to draw on. void DrawSitePoints(BITMAP* drawBitmap) const; - /// /// Draws fancy thick flickering lines from the Scene info box to the selected scene point on the planet. - /// - /// The bitmap to draw to. + /// @param drawBitmap The bitmap to draw to. void DrawLinesToSitePoint(BITMAP* drawBitmap) const; #pragma endregion - /// /// Clears all the member variables of this ScenarioGUI, effectively resetting the members of this object. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/SceneEditorGUI.cpp b/Source/Menus/SceneEditorGUI.cpp index fce9fcc1d7..a7f465f9d5 100644 --- a/Source/Menus/SceneEditorGUI.cpp +++ b/Source/Menus/SceneEditorGUI.cpp @@ -1,15 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneEditorGUI.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Implementation file for the SceneEditorGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "SceneEditorGUI.h" #include "CameraMan.h" @@ -45,12 +33,6 @@ using namespace RTE; BITMAP* SceneEditorGUI::s_pValidPathDot = 0; BITMAP* SceneEditorGUI::s_pInvalidPathDot = 0; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this SceneEditorGUI, effectively -// resetting the members of this abstraction level only. - void SceneEditorGUI::Clear() { m_pController = 0; m_FeatureSet = INGAMEEDIT; @@ -84,11 +66,6 @@ void SceneEditorGUI::Clear() { m_PathRequest.reset(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the SceneEditorGUI object ready for use. - int SceneEditorGUI::Create(Controller* pController, FeatureSets featureSet, int whichModuleSpace, int nativeTechModule, float foreignCostMult) { RTEAssert(pController, "No controller sent to SceneEditorGUI on creation!"); m_pController = pController; @@ -149,11 +126,6 @@ int SceneEditorGUI::Create(Controller* pController, FeatureSets featureSet, int return 0; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneEditorGUI object. - void SceneEditorGUI::Destroy() { delete m_pPicker; @@ -162,20 +134,12 @@ void SceneEditorGUI::Destroy() { Clear(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetController -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the controller used by this. The ownership of the controller is -// NOT transferred! - void SceneEditorGUI::SetController(Controller* pController) { m_pController = pController; m_PieMenu->SetMenuController(pController); m_pPicker->SetController(pController); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneEditorGUI::SetFeatureSet(SceneEditorGUI::FeatureSets newFeatureSet) { m_FeatureSet = newFeatureSet; if (m_PieMenu) { @@ -203,25 +167,10 @@ void SceneEditorGUI::SetFeatureSet(SceneEditorGUI::FeatureSets newFeatureSet) { m_PieMenu->SetMenuController(m_pController); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetPosOnScreen -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets where on the screen that this GUI is being drawn to. If upper -// left corner, then 0, 0. This will affect the way the mouse is positioned -// etc. - void SceneEditorGUI::SetPosOnScreen(int newPosX, int newPosY) { m_pPicker->SetPosOnScreen(newPosX, newPosY); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetCurrentObject -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the new Object to be held at the cursor of this Editor. Ownership -// IS transferred! - bool SceneEditorGUI::SetCurrentObject(SceneObject* pNewObject) { if (m_pCurrentObject == pNewObject) return true; @@ -245,31 +194,14 @@ bool SceneEditorGUI::SetCurrentObject(SceneObject* pNewObject) { return true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetActivatedPieSlice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets any Pie menu slice command activated last update. - PieSlice::SliceType SceneEditorGUI::GetActivatedPieSlice() const { return m_PieMenu->GetPieCommand(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetModuleSpace -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule space to be picking objects from. If -1, then -// let the player pick from all loaded modules. - void SceneEditorGUI::SetModuleSpace(int moduleSpaceID) { m_pPicker->SetModuleSpace(moduleSpaceID); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetNativeTechModule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets which DataModule ID should be treated as the native tech of the -// user of this menu. - void SceneEditorGUI::SetNativeTechModule(int whichModule) { if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) { m_NativeTechModule = whichModule; @@ -277,25 +209,11 @@ void SceneEditorGUI::SetNativeTechModule(int whichModule) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetForeignCostMultiplier -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the multiplier of the cost of any foreign Tech items. - void SceneEditorGUI::SetForeignCostMultiplier(float newMultiplier) { m_ForeignCostMult = newMultiplier; m_pPicker->SetForeignCostMultiplier(m_ForeignCostMult); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TestBrainResidence -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Checks whether the resident brain is currently placed into a valid -// location in this scene, based on whether there is a clear path to the -// sky above it. This forces the editor into place brain mode with the -// current resident brain if the current placement is no bueno. It also -// removes the faulty brain from residence in the scene! - bool SceneEditorGUI::TestBrainResidence(bool noBrainIsOK) { // Do we have a resident at all? SceneObject* pBrain = g_SceneMan.GetScene()->GetResidentBrain(m_pController->GetPlayer()); @@ -349,11 +267,6 @@ bool SceneEditorGUI::TestBrainResidence(bool noBrainIsOK) { return true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates the state of this Menu each frame - void SceneEditorGUI::Update() { // Update the user controller // m_pController->Update(); @@ -1259,11 +1172,6 @@ void SceneEditorGUI::Update() { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws the menu - void SceneEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const { // Done, so don't draw the UI if (m_EditorGUIMode == DONEEDITING) @@ -1412,8 +1320,6 @@ void SceneEditorGUI::Draw(BITMAP* pTargetBitmap, const Vector& targetPos) const m_PieMenu->Draw(pTargetBitmap, targetPos); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SceneEditorGUI::UpdateBrainSkyPathAndCost(Vector brainPos) { if (!m_RequireClearPathToOrbit) { m_PathRequest.reset(); @@ -1504,8 +1410,6 @@ void SceneEditorGUI::UpdateBrainSkyPathAndCost(Vector brainPos) { }); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SceneEditorGUI::UpdateBrainPath() { if (m_pCurrentObject && m_pCurrentObject->IsInGroup("Brains")) { UpdateBrainSkyPathAndCost(m_CursorPos); diff --git a/Source/Menus/SceneEditorGUI.h b/Source/Menus/SceneEditorGUI.h index b31ca55d8a..403090a0bf 100644 --- a/Source/Menus/SceneEditorGUI.h +++ b/Source/Menus/SceneEditorGUI.h @@ -1,18 +1,11 @@ #ifndef _SCENEEDITORGUI_ #define _SCENEEDITORGUI_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: SceneEditorGUI.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: SceneEditorGUI class -// Project: GUI Library -// Author(s): Daniel Tabar -// dtabar@datarealms.com -// http://www.datarealms.com - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - +/// SceneEditorGUI class +/// @author Daniel Tabar +/// dtabar@datarealms.com +/// http://www.datarealms.com +/// Inclusions of header files // #include "FrameMan.h" #include "Timer.h" #include "Vector.h" @@ -28,18 +21,10 @@ namespace RTE { class ObjectPickerGUI; class PieMenu; - ////////////////////////////////////////////////////////////////////////////////////////// - // Class: SceneEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: A full menu system that represents the scene editing GUI for Cortex Command - // Parent(s): None. - // Class history: 7/08/2007 SceneEditorGUI Created. - + /// A full menu system that represents the scene editing GUI for Cortex Command class SceneEditorGUI { - ////////////////////////////////////////////////////////////////////////////////////////// - // Public member variable, method and friend function declarations - + /// Public member variable, method and friend function declarations public: enum FeatureSets { ONLOADEDIT = 0, @@ -63,229 +48,122 @@ namespace RTE { EDITORGUIMODECOUNT }; - ////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: SceneEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Constructor method used to instantiate a SceneEditorGUI object in system - // memory. Create() should be called before using the object. - // Arguments: None. - + /// Constructor method used to instantiate a SceneEditorGUI object in system + /// memory. Create() should be called before using the object. SceneEditorGUI() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Destructor: ~SceneEditorGUI - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destructor method used to clean up a SceneEditorGUI object before deletion - // from system memory. - // Arguments: None. - + /// Destructor method used to clean up a SceneEditorGUI object before deletion + /// from system memory. ~SceneEditorGUI() { Destroy(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Create - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Makes the SceneEditorGUI object ready for use. - // Arguments: A poitner to a Controller which will control this Menu. Ownership is - // NOT TRANSFERRED! - // Whether the editor should have all the features enabled, like load/save - // and undo capabilities. - // Which module space that this eidtor will be able to pick objects from. - // -1 means all modules. - // Which Tech module that will be presented as the native one to the player. - // The multiplier of all foreign techs' costs. - // Return value: An error return value signaling sucess or any particular failure. - // Anything below 0 is an error signal. - + /// Makes the SceneEditorGUI object ready for use. + /// @param pController A poitner to a Controller which will control this Menu. Ownership is + /// @param featureSet NOT TRANSFERRED! (default: INGAMEEDIT) + /// @param whichModuleSpace Whether the editor should have all the features enabled, like load/save (default: -1) + /// and undo capabilities. + /// @param nativeTechModule Which module space that this eidtor will be able to pick objects from. (default: 0) + /// -1 means all modules. + /// @param foreignCostMult Which Tech module that will be presented as the native one to the player. (default: 1.0) + /// The multiplier of all foreign techs' costs. + /// @return An error return value signaling sucess or any particular failure. + /// Anything below 0 is an error signal. int Create(Controller* pController, FeatureSets featureSet = INGAMEEDIT, int whichModuleSpace = -1, int nativeTechModule = 0, float foreignCostMult = 1.0); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Reset - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Resets the entire SceneEditorGUI, including its inherited members, to - // their default settings or values. - // Arguments: None. - // Return value: None. - + /// Resets the entire SceneEditorGUI, including its inherited members, to + /// their default settings or values. void Reset() { Clear(); } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Destroy - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Destroys and resets (through Clear()) the SceneEditorGUI object. - // Arguments: None. - // Return value: None. - + /// Destroys and resets (through Clear()) the SceneEditorGUI object. void Destroy(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetController - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the controller used by this. The ownership of the controller is - // NOT transferred! - // Arguments: The new controller for this menu. Ownership is NOT transferred - // Return value: None. - + /// Sets the controller used by this. The ownership of the controller is + /// NOT transferred! + /// @param pController The new controller for this menu. Ownership is NOT transferred void SetController(Controller* pController); - /// /// Sets the FeatureSet for this SceneEditorGUI, and sets up the PieMenu accordingly. - /// - /// The new FeatureSet for this SceneEditorGUI. + /// @param newFeatureSet The new FeatureSet for this SceneEditorGUI. void SetFeatureSet(SceneEditorGUI::FeatureSets newFeatureSet); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetPosOnScreen - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets where on the screen that this GUI is being drawn to. If upper - // left corner, then 0, 0. This will affect the way the mouse is positioned - // etc. - // Arguments: The new screen position of this entire GUI. - + /// Sets where on the screen that this GUI is being drawn to. If upper + /// left corner, then 0, 0. This will affect the way the mouse is positioned + /// etc. + /// @param newPosX The new screen position of this entire GUI. void SetPosOnScreen(int newPosX, int newPosY); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCursorPos - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the absolute scene coordinates of the cursor of this Editor. - // Arguments: The new cursor position in absolute scene units. - // Return value: None. - + /// Sets the absolute scene coordinates of the cursor of this Editor. + /// @param newCursorPos The new cursor position in absolute scene units. void SetCursorPos(const Vector& newCursorPos) { m_CursorPos = newCursorPos; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetCurrentObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the new Object to be held at the cursor of this Editor. Ownership - // IS transferred! - // Arguments: The new Object to be held by the cursor. Ownership IS transferred! - // Return value: Whether the cursor holds a valid object after setting. - + /// Sets the new Object to be held at the cursor of this Editor. Ownership + /// IS transferred! + /// @param pNewObject The new Object to be held by the cursor. Ownership IS transferred! + /// @return Whether the cursor holds a valid object after setting. bool SetCurrentObject(SceneObject* pNewObject); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetActivatedPieSlice - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets any Pie menu slice command activated last update. - // Arguments: None. - // Return value: The enum'd int of any slice activated. See the PieSlice::SliceType enum. - + /// Gets any Pie menu slice command activated last update. + /// @return The enum'd int of any slice activated. See the PieSlice::SliceType enum. PieSlice::SliceType GetActivatedPieSlice() const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetCurrentObject - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the currently held Object in the cursor of this Editor. Ownership - // IS NOT transferred! - // Arguments: None. - // Return value: The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! - + /// Gets the currently held Object in the cursor of this Editor. Ownership + /// IS NOT transferred! + /// @return The currently held object, if any. OWNERSHIP IS NOT TRANSFERRED! const SceneObject* GetCurrentObject() const { return m_pCurrentObject; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the current mode of this editor. - // Arguments: The new mode to set to, see the EditorGUIMode enum. - // Return value: None. - + /// Sets the current mode of this editor. + /// @param newMode The new mode to set to, see the EditorGUIMode enum. void SetEditorGUIMode(EditorGUIMode newMode) { m_EditorGUIMode = newMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: GetEditorMode - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Gets the current mode of this editor. - // Arguments: None. - // Return value: The current mode this is set to; see the EditorGUIMode enum. - + /// Gets the current mode of this editor. + /// @return The current mode this is set to; see the EditorGUIMode enum. EditorGUIMode GetEditorGUIMode() const { return m_EditorGUIMode; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetModuleSpace - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which DataModule space to be picking objects from. If -1, then - // let the player pick from all loaded modules. - // Arguments: The ID of the module to let the player pick objects from. All official - // modules' objects will alwayws be presented, in addition to the one - // passed in here. - // Return value: None. - + /// Sets which DataModule space to be picking objects from. If -1, then + /// let the player pick from all loaded modules. + /// @param moduleSpaceID The ID of the module to let the player pick objects from. All official (default: -1) + /// modules' objects will alwayws be presented, in addition to the one + /// passed in here. void SetModuleSpace(int moduleSpaceID = -1); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetNativeTechModule - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets which DataModule ID should be treated as the native tech of the - // user of this menu. - // Arguments: The module ID to set as the native one. 0 means everything is native. - // Return value: None. - + /// Sets which DataModule ID should be treated as the native tech of the + /// user of this menu. + /// @param whichModule The module ID to set as the native one. 0 means everything is native. void SetNativeTechModule(int whichModule); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: SetForeignCostMultiplier - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Sets the multiplier of the cost of any foreign Tech items. - // Arguments: The scalar multiplier of the costs of foreign Tech items. - // Return value: None. - + /// Sets the multiplier of the cost of any foreign Tech items. + /// @param newMultiplier The scalar multiplier of the costs of foreign Tech items. void SetForeignCostMultiplier(float newMultiplier); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: EditMade - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Shows whether an edit on the scene was made in the last Update. - // Arguments: None. - // Return value: Whether any edit was made. - + /// Shows whether an edit on the scene was made in the last Update. + /// @return Whether any edit was made. bool EditMade() const { return m_EditMade; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: TestBrainResidence - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Checks whether the resident brain is currently placed into a valid - // location in this scene, based on whether there is a clear path to the - // sky above it. This forces the editor into place brain mode with the - // current resident brain if the current placement is no bueno. It also - // removes the faulty brain from residence in the scene! - // Arguments: Whether it's OK if we dont' have a brain right now - ie don't force - // into isntallation mode if no brain was found. - // Return value: Whether a resident brain was found, AND found in a valid location! - + /// Checks whether the resident brain is currently placed into a valid + /// location in this scene, based on whether there is a clear path to the + /// sky above it. This forces the editor into place brain mode with the + /// current resident brain if the current placement is no bueno. It also + /// removes the faulty brain from residence in the scene! + /// @param noBrainIsOK Whether it's OK if we dont' have a brain right now - ie don't force (default: false) + /// into isntallation mode if no brain was found. + /// @return Whether a resident brain was found, AND found in a valid location! bool TestBrainResidence(bool noBrainIsOK = false); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Update - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Updates the state of this Menu each frame - // Arguments: None. - // Return value: None. - + /// Updates the state of this Menu each frame void Update(); - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Draw - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Draws the editor - // Arguments: The bitmap to draw on. - // The absolute position of the target bitmap's upper left corner in the scene. - // Return value: None. - + /// Draws the editor + /// @param pTargetBitmap The bitmap to draw on. + /// @param targetPos The absolute position of the target bitmap's upper left corner in the scene. (default: Vector()) void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) const; - ////////////////////////////////////////////////////////////////////////////////////////// - // Protected member variable and method declarations - + /// Protected member variable and method declarations protected: - /// /// Updates the path to the current brain in the cursor or resident in the scene, if any. If there's none, the path is cleared. - /// - /// Whether a brain was found in the cursor or the scene. + /// @return Whether a brain was found in the cursor or the scene. bool UpdateBrainPath(); - /// /// Updates the path from the designated position to orbit, and its cost. - /// - /// The designated position of the brain. + /// @param brainPos The designated position of the brain. void UpdateBrainSkyPathAndCost(Vector brainPos); enum BlinkMode { @@ -361,18 +239,10 @@ namespace RTE { // The current pathfinding request std::shared_ptr m_PathRequest; - ////////////////////////////////////////////////////////////////////////////////////////// - // Private member variable and method declarations - + /// Private member variable and method declarations private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Method: Clear - ////////////////////////////////////////////////////////////////////////////////////////// - // Description: Clears all the member variables of this SceneEditorGUI, effectively - // resetting the members of this abstraction level only. - // Arguments: None. - // Return value: None. - + /// Clears all the member variables of this SceneEditorGUI, effectively + /// resetting the members of this abstraction level only. void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/SettingsAudioGUI.cpp b/Source/Menus/SettingsAudioGUI.cpp index e0dbd816ac..4c1d060150 100644 --- a/Source/Menus/SettingsAudioGUI.cpp +++ b/Source/Menus/SettingsAudioGUI.cpp @@ -9,8 +9,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsAudioGUI::SettingsAudioGUI(GUIControlManager* parentControlManager) : m_GUIControlManager(parentControlManager) { m_AudioSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxAudioSettings")); @@ -35,39 +33,29 @@ namespace RTE { UpdateSoundVolumeControls(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsAudioGUI::SetEnabled(bool enable) const { m_AudioSettingsBox->SetVisible(enable); m_AudioSettingsBox->SetEnabled(enable); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsAudioGUI::UpdateMasterVolumeControls() { int masterVolume = static_cast(std::round(g_AudioMan.GetMasterVolume() * 100)); m_MasterVolumeLabel->SetText("Volume: " + std::to_string(masterVolume)); m_MasterVolumeSlider->SetValue(masterVolume); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsAudioGUI::UpdateMusicVolumeControls() { int musicVolume = static_cast(std::round(g_AudioMan.GetMusicVolume() * 100)); m_MusicVolumeLabel->SetText("Volume: " + std::to_string(musicVolume)); m_MusicVolumeSlider->SetValue(musicVolume); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsAudioGUI::UpdateSoundVolumeControls() { int soundVolume = static_cast(std::round(g_AudioMan.GetSoundsVolume() * 100)); m_SoundVolumeLabel->SetText("Volume: " + std::to_string(soundVolume)); m_SoundVolumeSlider->SetValue(soundVolume); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsAudioGUI::HandleInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Notification) { if (guiEvent.GetControl() == m_MasterVolumeSlider) { diff --git a/Source/Menus/SettingsAudioGUI.h b/Source/Menus/SettingsAudioGUI.h index 9e89a30925..b21018336e 100644 --- a/Source/Menus/SettingsAudioGUI.h +++ b/Source/Menus/SettingsAudioGUI.h @@ -10,40 +10,30 @@ namespace RTE { class GUICheckbox; class GUIEvent; - /// /// Handling for audio settings through the game settings user interface. - /// class SettingsAudioGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a SettingsAudioGUI object in system memory and make it ready for use. - /// - /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsAudioGUI. Ownership is NOT transferred! + /// @param parentControlManager Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsAudioGUI. Ownership is NOT transferred! explicit SettingsAudioGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Concrete Methods - /// /// Enables or disables the SettingsAudioGUI. - /// - /// Show and enable or hide and disable the SettingsAudioGUI. + /// @param enable Show and enable or hide and disable the SettingsAudioGUI. void SetEnabled(bool enable = true) const; - /// /// Handles the player interaction with the AudioVideoGUI GUI elements. - /// - /// The GUIEvent containing information about the player interaction with an element. + /// @param guiEvent The GUIEvent containing information about the player interaction with an element. void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. - /// /// GUI elements that compose the audio settings menu screen. - /// GUICollectionBox* m_AudioSettingsBox; GUILabel* m_MasterVolumeLabel; GUISlider* m_MasterVolumeSlider; @@ -56,19 +46,13 @@ namespace RTE { GUICheckbox* m_SoundMuteCheckbox; #pragma region Audio Settings Handling - /// /// Updates the position of the master volume slider and volume value label, based on what the AudioMan is currently set to. - /// void UpdateMasterVolumeControls(); - /// /// Updates the position of the music volume slider and volume value label, based on what the AudioMan is currently set to. - /// void UpdateMusicVolumeControls(); - /// /// Updates the position of the sound volume slider and volume value label, based on what the AudioMan is currently set to. - /// void UpdateSoundVolumeControls(); #pragma endregion diff --git a/Source/Menus/SettingsGUI.cpp b/Source/Menus/SettingsGUI.cpp index 44e5c39cb7..7aed598ae7 100644 --- a/Source/Menus/SettingsGUI.cpp +++ b/Source/Menus/SettingsGUI.cpp @@ -10,8 +10,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsGUI::SettingsGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu) { m_GUIControlManager = std::make_unique(); RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); @@ -54,8 +52,6 @@ namespace RTE { m_SettingsMenuTabs[m_ActiveSettingsMenuScreen]->SetCheck(true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICollectionBox* SettingsGUI::GetActiveDialogBox() const { GUICollectionBox* activeDialogBox = nullptr; switch (m_ActiveSettingsMenuScreen) { @@ -73,8 +69,6 @@ namespace RTE { return activeDialogBox; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGUI::CloseActiveDialogBox() const { switch (m_ActiveSettingsMenuScreen) { case SettingsMenuScreen::VideoSettingsMenu: @@ -90,8 +84,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGUI::DisableSettingsMenuNavigation(bool disable) const { m_BackToMainButton->SetEnabled(!disable); for (GUITab* settingsTabberTab: m_SettingsMenuTabs) { @@ -99,8 +91,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGUI::SetActiveSettingsMenuScreen(SettingsMenuScreen activeMenu, bool playButtonPressSound) { m_VideoSettingsMenu->SetEnabled(false); m_AudioSettingsMenu->SetEnabled(false); @@ -137,8 +127,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsGUI::HandleInputEvents() { m_GUIControlManager->Update(); @@ -200,8 +188,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGUI::Draw() const { m_GUIControlManager->Draw(); m_GUIControlManager->DrawMouse(); diff --git a/Source/Menus/SettingsGUI.h b/Source/Menus/SettingsGUI.h index dd65341956..e3a6d64573 100644 --- a/Source/Menus/SettingsGUI.h +++ b/Source/Menus/SettingsGUI.h @@ -15,57 +15,41 @@ namespace RTE { class GUIButton; class GUITab; - /// /// Handling for the settings menu screen composition and sub-menu interaction. - /// class SettingsGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a SettingsGUI object in system memory and make it ready for use. - /// - /// Pointer to a GUIScreen interface that will be used by this SettingsGUI's GUIControlManager. Ownership is NOT transferred! - /// Pointer to a GUIInput interface that will be used by this SettingsGUI's GUIControlManager. Ownership is NOT transferred! - /// Whether this SettingsGUI is part of PauseMenuGUI and should have a slightly different layout. + /// @param guiScreen Pointer to a GUIScreen interface that will be used by this SettingsGUI's GUIControlManager. Ownership is NOT transferred! + /// @param guiInput Pointer to a GUIInput interface that will be used by this SettingsGUI's GUIControlManager. Ownership is NOT transferred! + /// @param createForPauseMenu Whether this SettingsGUI is part of PauseMenuGUI and should have a slightly different layout. SettingsGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu = false); #pragma endregion #pragma region Getters - /// /// Gets the currently active GUICollectionBox of this SettingsGUI or any of its sub-menus that acts as a dialog box and requires disabling navigation and drawing an overlay. - /// - /// Pointer to the GUICollectionBox that is the currently active dialog box. Ownership is NOT transferred! + /// @return Pointer to the GUICollectionBox that is the currently active dialog box. Ownership is NOT transferred! GUICollectionBox* GetActiveDialogBox() const; #pragma endregion #pragma region Concrete Methods - /// /// Closes the currently active GUICollectionBox that acts as a dialog box by hiding it. If the active dialog box is a sub-menu, disables it. - /// void CloseActiveDialogBox() const; - /// /// Sets the currently active settings menu screen again to refresh it. This is used in case textboxes were left empty or focused on settings screen exit via back button or esc. - /// void RefreshActiveSettingsMenuScreen() { SetActiveSettingsMenuScreen(m_ActiveSettingsMenuScreen, false); } - /// /// Handles the player interaction with the SettingsGUI GUI elements. - /// - /// Whether the player requested to return to the main menu. + /// @return Whether the player requested to return to the main menu. bool HandleInputEvents(); - /// /// Draws the SettingsGUI to the screen. - /// void Draw() const; #pragma endregion private: - /// /// Enumeration for the different sub-menu screens of the settings menu. - /// enum SettingsMenuScreen { VideoSettingsMenu, AudioSettingsMenu, @@ -85,24 +69,18 @@ namespace RTE { std::unique_ptr m_GameplaySettingsMenu; //!< The gameplay settings sub-menu. std::unique_ptr m_MiscSettingsMenu; //!< The misc settings sub-menu. - /// /// GUI elements that compose the settings menu screen. - /// GUICollectionBox* m_SettingsTabberBox; GUIButton* m_BackToMainButton; std::array m_SettingsMenuTabs; #pragma region Settings Menu Handling - /// /// Disables the settings menu tabber and back buttons. This is used when a settings sub-menu dialog box is active. - /// void DisableSettingsMenuNavigation(bool disable) const; - /// /// Sets the SettingsGUI to display a settings menu screen. - /// - /// Which settings menu screen to display. See the SettingsMenuScreen enumeration. - /// Whether to play a sound if the menu screen change is triggered by a button/tab press. + /// @param activeMenu Which settings menu screen to display. See the SettingsMenuScreen enumeration. + /// @param playButtonPressSound Whether to play a sound if the menu screen change is triggered by a button/tab press. void SetActiveSettingsMenuScreen(SettingsMenuScreen activeMenu, bool playButtonPressSound = true); #pragma endregion diff --git a/Source/Menus/SettingsGameplayGUI.cpp b/Source/Menus/SettingsGameplayGUI.cpp index e000041f68..8936e04a5e 100644 --- a/Source/Menus/SettingsGameplayGUI.cpp +++ b/Source/Menus/SettingsGameplayGUI.cpp @@ -12,8 +12,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsGameplayGUI::SettingsGameplayGUI(GUIControlManager* parentControlManager) : m_GUIControlManager(parentControlManager) { m_GameplaySettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxGameplaySettings")); @@ -77,8 +75,6 @@ namespace RTE { UpdateScreenShakeStrength(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGameplayGUI::SetEnabled(bool enable) { m_GameplaySettingsBox->SetVisible(enable); m_GameplaySettingsBox->SetEnabled(enable); @@ -89,8 +85,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGameplayGUI::UpdateMaxUnheldItemsTextbox() { if (m_MaxUnheldItemsTextbox->GetText().empty()) { m_MaxUnheldItemsTextbox->SetText(std::to_string(g_MovableMan.GetMaxDroppedItems())); @@ -99,8 +93,6 @@ namespace RTE { m_GameplaySettingsBox->SetFocus(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGameplayGUI::UpdateCrabBombThresholdTextbox() { if (m_CrabBombThresholdTextbox->GetText().empty()) { m_CrabBombThresholdTextbox->SetText(std::to_string(g_SettingsMan.GetCrabBombThreshold())); @@ -112,8 +104,6 @@ namespace RTE { m_GameplaySettingsBox->SetFocus(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGameplayGUI::UpdateUnheldItemsHUDDisplayRange() { int newValue = m_UnheldItemsHUDDisplayRangeSlider->GetValue(); if (newValue < 3) { @@ -128,16 +118,12 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGameplayGUI::UpdateScreenShakeStrength() { int newValue = m_ScreenShakeStrengthSlider->GetValue(); m_ScreenShakeStrengthLabel->SetText(std::to_string(newValue) + "%"); g_CameraMan.SetScreenShakeStrength(static_cast(newValue) / 100.0F); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsGameplayGUI::HandleInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Notification) { if (guiEvent.GetControl() == m_FlashOnBrainDamageCheckbox) { diff --git a/Source/Menus/SettingsGameplayGUI.h b/Source/Menus/SettingsGameplayGUI.h index f2f76e9ef0..f572e46f5c 100644 --- a/Source/Menus/SettingsGameplayGUI.h +++ b/Source/Menus/SettingsGameplayGUI.h @@ -11,40 +11,30 @@ namespace RTE { class GUILabel; class GUIEvent; - /// /// Handling for gameplay settings through the game settings user interface. - /// class SettingsGameplayGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a SettingsGameplayGUI object in system memory and make it ready for use. - /// - /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsGameplayGUI. Ownership is NOT transferred! + /// @param parentControlManager Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsGameplayGUI. Ownership is NOT transferred! explicit SettingsGameplayGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Concrete Methods - /// /// Enables or disables the SettingsGameplayGUI. - /// - /// Show and enable or hide and disable the SettingsGameplayGUI. + /// @param enable Show and enable or hide and disable the SettingsGameplayGUI. void SetEnabled(bool enable = true); - /// /// Handles the player interaction with the SettingsInputGUI GUI elements. - /// - /// The GUIEvent containing information about the player interaction with an element. + /// @param guiEvent The GUIEvent containing information about the player interaction with an element. void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. - /// /// GUI elements that compose the gameplay settings menu screen. - /// GUICollectionBox* m_GameplaySettingsBox; GUICheckbox* m_FlashOnBrainDamageCheckbox; GUICheckbox* m_BlipOnRevealUnseenCheckbox; @@ -65,24 +55,16 @@ namespace RTE { GUILabel* m_ScreenShakeStrengthLabel; #pragma region Gameplay Settings Handling - /// /// Updates the MaxUnheldItems textbox to override any invalid input, applies the setting value and removes its focus. - /// void UpdateMaxUnheldItemsTextbox(); - /// /// Updates the CrabBombThreshold textbox to override any invalid input, applies the setting value and removes its focus. - /// void UpdateCrabBombThresholdTextbox(); - /// /// Updates the UnheldItemsHUDDisplayRange setting and label according to the slider value. - /// void UpdateUnheldItemsHUDDisplayRange(); - /// /// Updates the Screen Shake strength setting and label according to the slider value. - /// void UpdateScreenShakeStrength(); #pragma endregion diff --git a/Source/Menus/SettingsInputGUI.cpp b/Source/Menus/SettingsInputGUI.cpp index 1cb04c1efd..a85aa71b54 100644 --- a/Source/Menus/SettingsInputGUI.cpp +++ b/Source/Menus/SettingsInputGUI.cpp @@ -10,8 +10,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsInputGUI::SettingsInputGUI(GUIControlManager* parentControlManager) : m_GUIControlManager(parentControlManager) { m_InputSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxInputSettings")); @@ -40,15 +38,11 @@ namespace RTE { m_InputMappingConfigMenu = std::make_unique(parentControlManager); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputGUI::SetEnabled(bool enable) const { m_InputSettingsBox->SetVisible(enable); m_InputSettingsBox->SetEnabled(enable); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputGUI::ResetPlayerInputSettings(int player) { if (m_PlayerInputSettingsBoxes.at(player).ResetControlsButton->GetText() == "Reset") { // Only one player's reset button can be pending confirmation at a time, so cancel any other pending confirmations. @@ -74,8 +68,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputGUI::SetPlayerNextOrPrevInputDevice(int player, bool nextDevice) { int currentDevice = static_cast(g_UInputMan.GetControlScheme(player)->GetDevice()); @@ -95,8 +87,6 @@ namespace RTE { ShowOrHidePlayerInputDeviceSensitivityControls(player); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputGUI::UpdatePlayerSelectedDeviceLabel(int player) { std::string deviceLabel; @@ -125,8 +115,6 @@ namespace RTE { m_PlayerInputSettingsBoxes.at(player).SelectedDeviceLabel->SetText(deviceLabel); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputGUI::ShowOrHidePlayerInputDeviceSensitivityControls(int player) { m_PlayerInputSettingsBoxes.at(player).SensitivityLabel->SetVisible(false); m_PlayerInputSettingsBoxes.at(player).SensitivitySlider->SetVisible(false); @@ -158,8 +146,6 @@ namespace RTE { UpdatePlayerInputSensitivityControlValues(player); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputGUI::UpdatePlayerInputSensitivityControlValues(int player) { switch (g_UInputMan.GetControlScheme(player)->GetDevice()) { case InputDevice::DEVICE_KEYB_ONLY: @@ -192,8 +178,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputGUI::HandleInputEvents(GUIEvent& guiEvent) { if (m_InputMappingConfigMenu->IsEnabled()) { m_InputMappingConfigMenu->HandleInputEvents(guiEvent); diff --git a/Source/Menus/SettingsInputGUI.h b/Source/Menus/SettingsInputGUI.h index 23a6bb5d8f..3ca9e2781c 100644 --- a/Source/Menus/SettingsInputGUI.h +++ b/Source/Menus/SettingsInputGUI.h @@ -16,75 +16,53 @@ namespace RTE { class GUIRadioButton; class GUIEvent; - /// /// Handling for player input settings through the game settings user interface. - /// class SettingsInputGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a SettingsInputGUI object in system memory and make it ready for use. - /// - /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsInputGUI. Ownership is NOT transferred! + /// @param parentControlManager Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsInputGUI. Ownership is NOT transferred! explicit SettingsInputGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters - /// /// Enables or disables the SettingsInputGUI. - /// - /// Show and enable or hide and disable the SettingsInputGUI. + /// @param enable Show and enable or hide and disable the SettingsInputGUI. void SetEnabled(bool enable = true) const; - /// /// Gets the currently active GUICollectionBox that acts as a dialog box and requires disabling navigation and drawing an overlay. - /// - /// Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! + /// @return Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! GUICollectionBox* GetActiveDialogBox() const { return m_InputMappingConfigMenu->GetActiveDialogBox(); } - /// /// Closes the currently active GUICollectionBox that acts as a dialog box by hiding it. If the active dialog box is a sub-menu, disables it. - /// void CloseActiveDialogBox() const { m_InputMappingConfigMenu->CloseActiveDialogBox(); } #pragma endregion #pragma region Input Config Wizard Handling - /// /// Gets whether the player is currently manually configuring an InputMapping through the input mapping menu screen. - /// - /// Whether the player is currently manually configuring an InputMapping through the input mapping menu screen. + /// @return Whether the player is currently manually configuring an InputMapping through the input mapping menu screen. bool InputMappingConfigIsConfiguringManually() const { return m_InputMappingConfigMenu->IsConfiguringManually(); } - /// /// Handles input capture logic of the input mapping menu screen manual configuration sequence. - /// void HandleMappingConfigManualConfiguration() const { m_InputMappingConfigMenu->HandleManualConfigSequence(); } - /// /// Gets whether the player is currently manually configuring the InputScheme through the input mapping wizard. - /// - /// Whether the player is currently manually configuring the InputScheme through the input mapping wizard. + /// @return Whether the player is currently manually configuring the InputScheme through the input mapping wizard. bool InputConfigWizardIsConfiguringManually() const { return m_InputMappingConfigMenu->GetInputConfigWizardMenu()->IsConfiguringManually(); } - /// /// Handles input capture logic of the input mapping wizard manual configuration sequence. - /// void HandleConfigWizardManualConfiguration() const { m_InputMappingConfigMenu->GetInputConfigWizardMenu()->HandleManualConfigSequence(); } #pragma endregion #pragma region Concrete Methods - /// /// Handles the player interaction with the SettingsInputGUI GUI elements. - /// - /// The GUIEvent containing information about the player interaction with an element. + /// @param guiEvent The GUIEvent containing information about the player interaction with an element. void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: - /// /// Struct containing GUI elements that compose the input settings box of a player. - /// struct PlayerInputSettingsBox { GUILabel* SelectedDeviceLabel; GUIButton* NextDeviceButton; @@ -102,42 +80,30 @@ namespace RTE { std::unique_ptr m_InputMappingConfigMenu; //!< The input mapping configuration sub-menu. - /// /// GUI elements that compose the input settings menu screen. - /// GUICollectionBox* m_InputSettingsBox; std::array m_PlayerInputSettingsBoxes; #pragma region Input Settings Handling - /// /// Resets the player input settings to the defaults. - /// - /// The player to reset input settings for. + /// @param player The player to reset input settings for. void ResetPlayerInputSettings(int player); - /// /// Changes the player's input device in the InputScheme and proceeds to update the device labels accordingly. - /// - /// The player to change input device for. - /// Whether to change to the next or previous input device. + /// @param player The player to change input device for. + /// @param nextDevice Whether to change to the next or previous input device. void SetPlayerNextOrPrevInputDevice(int player, bool nextDevice); - /// /// Updates the currently selected input device label of a player according to the InputScheme. - /// - /// The player to update selected input device label for. + /// @param player The player to update selected input device label for. void UpdatePlayerSelectedDeviceLabel(int player); - /// /// Enables the input sensitivity controls for a player if applicable to the selected input device. - /// - /// The player to enable input sensitivity controls for. + /// @param player The player to enable input sensitivity controls for. void ShowOrHidePlayerInputDeviceSensitivityControls(int player); - /// /// Updates the input sensitivity controls of a player according to the InputScheme. - /// - /// The player to update input sensitivity control values for. + /// @param player The player to update input sensitivity control values for. void UpdatePlayerInputSensitivityControlValues(int player); #pragma endregion diff --git a/Source/Menus/SettingsInputMappingGUI.cpp b/Source/Menus/SettingsInputMappingGUI.cpp index c345a3f38a..954ae40371 100644 --- a/Source/Menus/SettingsInputMappingGUI.cpp +++ b/Source/Menus/SettingsInputMappingGUI.cpp @@ -12,8 +12,6 @@ namespace RTE { std::array SettingsInputMappingGUI::m_InputElementsUsedByMouse = {InputElements::INPUT_FIRE, InputElements::INPUT_PIEMENU_ANALOG, InputElements::INPUT_AIM, InputElements::INPUT_AIM_UP, InputElements::INPUT_AIM_DOWN, InputElements::INPUT_AIM_LEFT, InputElements::INPUT_AIM_RIGHT}; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsInputMappingGUI::SettingsInputMappingGUI(GUIControlManager* parentControlManager) : m_GUIControlManager(parentControlManager) { m_InputMappingSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPlayerInputMapping")); @@ -50,14 +48,10 @@ namespace RTE { m_InputElementCapturingInput = InputElements::INPUT_COUNT; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingGUI::IsEnabled() const { return m_InputMappingSettingsBox->GetVisible() && m_InputMappingSettingsBox->GetEnabled(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingGUI::SetEnabled(bool enable, int player) { m_InputMappingSettingsBox->SetVisible(enable); m_InputMappingSettingsBox->SetEnabled(enable); @@ -76,8 +70,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICollectionBox* SettingsInputMappingGUI::GetActiveDialogBox() const { if (m_InputConfigWizardMenu->IsEnabled()) { return m_InputConfigWizardMenu->GetActiveDialogBox(); @@ -87,8 +79,6 @@ namespace RTE { return (m_InputMappingSettingsBox->GetEnabled() && m_InputMappingSettingsBox->GetVisible()) ? m_InputMappingSettingsBox : nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingGUI::CloseActiveDialogBox() { if (m_InputConfigWizardMenu->IsEnabled()) { m_InputConfigWizardMenu->SetEnabled(false); @@ -99,14 +89,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingGUI::IsConfiguringManually() const { return m_ConfiguringManually && m_InputMappingCaptureBox->GetVisible() && m_InputMappingCaptureBox->GetEnabled(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingGUI::ShowInputMappingCaptureBox(InputElements inputElement) { m_InputMappingSettingsBox->SetEnabled(false); m_InputMappingCaptureBox->SetVisible(true); @@ -118,8 +104,6 @@ namespace RTE { g_UInputMan.SetSkipHandlingSpecialInput(true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingGUI::HideInputMappingCaptureBox() { m_InputMappingSettingsBox->SetEnabled(true); m_InputMappingCaptureBox->SetVisible(false); @@ -129,8 +113,6 @@ namespace RTE { g_UInputMan.SetSkipHandlingSpecialInput(false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingGUI::UpdateMappingButtonLabels() { const std::array* inputMappings = m_ConfiguringPlayerInputScheme->GetInputMappings(); for (int i = 0; i < InputElements::INPUT_COUNT; ++i) { @@ -145,16 +127,12 @@ namespace RTE { m_InputMapScrollingBoxScrollbar->SetPageSize(m_InputMapScrollingBoxScrollbar->GetMaximum() / 2); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingGUI::UpdateScrollingInputBoxScrollPosition() { int scrollbarValue = m_InputMapScrollingBoxScrollbar->GetValue(); m_InputMapScrollingBox->SetPositionRel(m_InputMapScrollingBox->GetRelXPos(), m_InputMapScrollingBox->GetRelYPos() + (m_LastInputMapScrollingBoxScrollbarValue - scrollbarValue)); m_LastInputMapScrollingBoxScrollbarValue = scrollbarValue; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingGUI::HandleInputEvents(GUIEvent& guiEvent) { if (m_InputConfigWizardMenu->IsEnabled()) { if (m_InputConfigWizardMenu->HandleInputEvents(guiEvent)) { @@ -191,8 +169,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingGUI::HandleManualConfigSequence() { bool inputCaptured = false; if (g_UInputMan.KeyReleased(SDLK_DELETE)) { diff --git a/Source/Menus/SettingsInputMappingGUI.h b/Source/Menus/SettingsInputMappingGUI.h index 4fe312157c..1e5a5769c0 100644 --- a/Source/Menus/SettingsInputMappingGUI.h +++ b/Source/Menus/SettingsInputMappingGUI.h @@ -13,68 +13,48 @@ namespace RTE { class GUIScrollbar; class GUIEvent; - /// /// Handling for player input mapping settings through the game settings user interface. - /// class SettingsInputMappingGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a SettingsInputMappingGUI object in system memory and make it ready for use. - /// - /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsInputMappingGUI. Ownership is NOT transferred! + /// @param parentControlManager Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsInputMappingGUI. Ownership is NOT transferred! explicit SettingsInputMappingGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters - /// /// Gets whether this SettingsInputMappingGUI is currently visible and enabled. - /// - /// Whether this SettingsInputMappingGUI is currently visible and enabled. + /// @return Whether this SettingsInputMappingGUI is currently visible and enabled. bool IsEnabled() const; - /// /// Enables or disables the SettingsInputMappingGUI. - /// - /// Show and enable or hide and disable the SettingsInputMappingGUI. - /// The player this SettingsInputMappingGUI is configuring input mapping for. + /// @param enable Show and enable or hide and disable the SettingsInputMappingGUI. + /// @param player The player this SettingsInputMappingGUI is configuring input mapping for. void SetEnabled(bool enable = true, int player = 0); - /// /// Gets the currently active GUICollectionBox that acts as a dialog box and requires disabling navigation and drawing an overlay. - /// - /// Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! + /// @return Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! GUICollectionBox* GetActiveDialogBox() const; - /// /// Closes the currently active GUICollectionBox that acts as a dialog box by hiding it. If the active dialog box is a sub-menu, disables it. - /// void CloseActiveDialogBox(); - /// /// Gets whether this SettingsInputMappingGUI needs to capture input for manual configuration. - /// - /// Whether this SettingsInputMappingGUI needs to capture input for manual configuration. + /// @return Whether this SettingsInputMappingGUI needs to capture input for manual configuration. bool IsConfiguringManually() const; - /// /// Gets the SettingsInputMappingWizardGUI of this SettingsInputMappingGUI. - /// - /// Pointer to the SettingsInputMappingWizardGUI of this SettingsInputMappingGUI. Ownership is NOT transferred! + /// @return Pointer to the SettingsInputMappingWizardGUI of this SettingsInputMappingGUI. Ownership is NOT transferred! SettingsInputMappingWizardGUI* GetInputConfigWizardMenu() { return m_InputConfigWizardMenu.get(); } #pragma endregion #pragma region Concrete Methods - /// /// Handles the player interaction with the SettingsInputMappingGUI GUI elements. - /// - /// The GUIEvent containing information about the player interaction with an element. + /// @param guiEvent The GUIEvent containing information about the player interaction with an element. void HandleInputEvents(GUIEvent& guiEvent); - /// /// Handles capturing input and updating the manual input configuration sequence. - /// void HandleManualConfigSequence(); #pragma endregion @@ -93,9 +73,7 @@ namespace RTE { std::unique_ptr m_InputConfigWizardMenu; //!< The input mapping config wizard. - /// /// GUI elements that compose the input mapping settings menu screen. - /// GUICollectionBox* m_InputMappingSettingsBox; GUILabel* m_InputMappingSettingsLabel; GUIButton* m_CloseMappingBoxButton; @@ -108,25 +86,17 @@ namespace RTE { std::array m_InputMapButton; #pragma region Input Mapping Settings Handling - /// /// Shows and enables the input mapping capture box, starting the input capture sequence. - /// - /// + /// @param inputElement void ShowInputMappingCaptureBox(InputElements inputElement); - /// /// Hides and disables the input mapping capture box, ending the input capture sequence. - /// void HideInputMappingCaptureBox(); - /// /// Updates the mapping button key labels with the configuring player's InputScheme mappings. - /// void UpdateMappingButtonLabels(); - /// /// Updates the input mapping scrolling box scroll position. - /// void UpdateScrollingInputBoxScrollPosition(); #pragma endregion diff --git a/Source/Menus/SettingsInputMappingWizardGUI.cpp b/Source/Menus/SettingsInputMappingWizardGUI.cpp index c7d1762d1b..f81593012b 100644 --- a/Source/Menus/SettingsInputMappingWizardGUI.cpp +++ b/Source/Menus/SettingsInputMappingWizardGUI.cpp @@ -9,8 +9,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::Clear() { m_ConfiguringPlayer = Players::NoPlayer; m_ConfiguringPlayerScheme = nullptr; @@ -35,8 +33,6 @@ namespace RTE { m_DualAnalogXBDiagramBitmaps.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsInputMappingWizardGUI::SettingsInputMappingWizardGUI(GUIControlManager* parentControlManager) : m_GUIControlManager(parentControlManager) { Clear(); @@ -55,8 +51,6 @@ namespace RTE { CreatePresetSelectionScreen(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::CreateManualConfigScreen() { m_WizardManualConfigScreen.ManualConfigBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxWizardManualConfig")); @@ -74,8 +68,6 @@ namespace RTE { m_WizardManualConfigScreen.DiscardOrApplyConfigButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonDiscardOrApply")); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::CreatePresetSelectionScreen() { m_WizardPresetSelectScreen.PresetSelectBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxWizardPresets")); m_WizardPresetSelectScreen.CloseWizardButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonCloseWizardBox")); @@ -96,14 +88,10 @@ namespace RTE { dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxPresetAnalogXB360Diagram"))->SetDrawImage(new AllegroBitmap(m_DualAnalogXBDiagramBitmaps[0])); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingWizardGUI::IsEnabled() const { return m_InputWizardScreenBox->GetVisible() && m_InputWizardScreenBox->GetEnabled(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::SetEnabled(bool enable, int player, InputScheme* playerScheme) { m_InputWizardScreenBox->SetVisible(enable); m_InputWizardScreenBox->SetEnabled(enable); @@ -134,20 +122,14 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICollectionBox* SettingsInputMappingWizardGUI::GetActiveDialogBox() const { return (m_InputWizardScreenBox->GetEnabled() && m_InputWizardScreenBox->GetVisible()) ? m_InputWizardScreenBox : nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingWizardGUI::IsConfiguringManually() const { return m_ConfiguringManually && m_WizardManualConfigScreen.ManualConfigBox->GetVisible() && m_WizardManualConfigScreen.ManualConfigBox->GetEnabled(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::ShowManualConfigScreen() { m_WizardPresetSelectScreen.PresetSelectBox->SetVisible(false); m_WizardPresetSelectScreen.PresetSelectBox->SetEnabled(false); @@ -192,8 +174,6 @@ namespace RTE { m_ConfiguringManually = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::ShowPresetSelectionScreen() { m_WizardManualConfigScreen.ManualConfigBox->SetVisible(false); m_WizardManualConfigScreen.ManualConfigBox->SetEnabled(false); @@ -202,8 +182,6 @@ namespace RTE { m_WizardPresetSelectScreen.PresetSelectBox->SetEnabled(true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::ResetManualConfigSequence() { m_ConfigFinished = false; m_ConfigStep = 0; @@ -212,8 +190,6 @@ namespace RTE { m_NewInputScheme.SetDevice(m_ConfiguringDevice); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::ApplyGamepadInputPreset(GamepadType gamepadType) { switch (gamepadType) { case GamepadType::DPad: @@ -233,8 +209,6 @@ namespace RTE { m_NewInputSchemeApplied = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::ApplyManuallyConfiguredScheme() { m_ConfiguringPlayerScheme->SetDevice(m_NewInputScheme.GetDevice()); m_ConfiguringPlayerScheme->SetPreset(InputScheme::InputPreset::NoPreset); @@ -255,8 +229,6 @@ namespace RTE { m_ConfiguringManually = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingWizardGUI::HandleInputEvents(GUIEvent& guiEvent) { if (m_WizardManualConfigScreen.ManualConfigBox->GetVisible()) { HandleManualConfigScreenInputEvents(guiEvent); @@ -270,8 +242,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::HandleManualConfigScreenInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Command) { if (guiEvent.GetControl() == m_WizardManualConfigScreen.PrevConfigStepButton) { @@ -304,8 +274,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::HandlePresetSelectScreenInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Command) { if (guiEvent.GetControl() == m_WizardPresetSelectScreen.CloseWizardButton) { @@ -333,8 +301,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::HandleManualConfigSequence() { if (m_ConfigStepChange) { HandleManualConfigStepChange(); @@ -376,8 +342,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsInputMappingWizardGUI::HandleManualConfigStepChange() { int configuringDeviceSteps = 0; @@ -421,8 +385,6 @@ namespace RTE { m_WizardManualConfigScreen.NextConfigStepButton->SetVisible(m_ConfigStep < configuringDeviceSteps - 1); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingWizardGUI::UpdateKeyboardConfigSequence() { switch (m_ConfigStep) { case 0: @@ -593,8 +555,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingWizardGUI::UpdateMouseAndKeyboardConfigSequence() { switch (m_ConfigStep) { case 0: @@ -717,8 +677,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingWizardGUI::UpdateGamepadDPadConfigSequence() { switch (m_ConfigStep) { case 0: @@ -851,8 +809,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsInputMappingWizardGUI::UpdateGamepadAnalogConfigSequence() { switch (m_ConfigStep) { case 0: diff --git a/Source/Menus/SettingsInputMappingWizardGUI.h b/Source/Menus/SettingsInputMappingWizardGUI.h index e56295cda0..3fbcb02477 100644 --- a/Source/Menus/SettingsInputMappingWizardGUI.h +++ b/Source/Menus/SettingsInputMappingWizardGUI.h @@ -15,74 +15,54 @@ namespace RTE { class GUIButton; class GUIEvent; - /// /// Handling for the user input mapping wizard through the game settings user interface. - /// class SettingsInputMappingWizardGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a SettingsInputMappingWizardGUI object in system memory and make it ready for use. - /// - /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsInputMappingWizardGUI. Ownership is NOT transferred! + /// @param parentControlManager Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsInputMappingWizardGUI. Ownership is NOT transferred! explicit SettingsInputMappingWizardGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters - /// /// Gets whether this SettingsInputMappingWizardGUI is currently visible and enabled. - /// - /// Whether this SettingsInputMappingWizardGUI is currently visible and enabled. + /// @return Whether this SettingsInputMappingWizardGUI is currently visible and enabled. bool IsEnabled() const; - /// /// Enables or disables the SettingsInputMappingWizardGUI. - /// - /// Show and enable or hide and disable the SettingsInputMappingWizardGUI. - /// The player this SettingsInputMappingWizardGUI is mapping inputs for. + /// @param enable Show and enable or hide and disable the SettingsInputMappingWizardGUI. + /// @param player The player this SettingsInputMappingWizardGUI is mapping inputs for. void SetEnabled(bool enable = true, int player = 0, InputScheme* playerScheme = nullptr); - /// /// Gets the currently active GUICollectionBox that acts as a dialog box and requires disabling navigation and drawing an overlay. - /// - /// Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! + /// @return Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! GUICollectionBox* GetActiveDialogBox() const; - /// /// Gets whether this SettingsInputMappingWizardGUI needs to capture input for manual configuration. - /// - /// Whether this SettingsInputMappingWizardGUI needs to capture input for manual configuration. + /// @return Whether this SettingsInputMappingWizardGUI needs to capture input for manual configuration. bool IsConfiguringManually() const; #pragma endregion #pragma region Concrete Methods - /// /// Handles the player interaction with the SettingsInputMappingWizardGUI GUI elements. - /// - /// The GUIEvent containing information about the player interaction with an element. - /// Whether this SettingsInputMappingGUI changed the input scheme of the configuring player. + /// @param guiEvent The GUIEvent containing information about the player interaction with an element. + /// @return Whether this SettingsInputMappingGUI changed the input scheme of the configuring player. bool HandleInputEvents(GUIEvent& guiEvent); - /// /// Handles updating and progressing the manual input configuration sequence. - /// void HandleManualConfigSequence(); #pragma endregion private: - /// /// Enumeration for the different types of gamepads that can be configured. - /// enum GamepadType { DPad, AnalogDualShock, AnalogXbox }; - /// /// Struct containing GUI elements that compose the input mapping wizard manual configuration menu screen. - /// struct WizardManualConfigScreen { GUICollectionBox* ManualConfigBox; GUILabel* ConfigDeviceTypeLabel; @@ -98,9 +78,7 @@ namespace RTE { GUIButton* DiscardOrApplyConfigButton; }; - /// /// Struct containing GUI elements that compose the input mapping wizard preset selection menu screen. - /// struct WizardPresetSelectScreen { GUICollectionBox* PresetSelectBox; GUIButton* CloseWizardButton; @@ -144,99 +122,67 @@ namespace RTE { WizardManualConfigScreen m_WizardManualConfigScreen; //!< The manual input configuration menu screen. WizardPresetSelectScreen m_WizardPresetSelectScreen; //!< The preset selection menu screen. - /// /// GUI elements that compose the input mapping wizard menu screen. - /// GUICollectionBox* m_InputWizardScreenBox; GUILabel* m_InputWizardTitleLabel; #pragma region Create Breakdown - /// /// Creates all the elements that compose the manual input configuration box. - /// void CreateManualConfigScreen(); - /// /// Creates all the elements that compose the gamepad input preset selection box. - /// void CreatePresetSelectionScreen(); #pragma endregion #pragma region Input Mapping Wizard Handling - /// /// Makes the manual input configuration menu screen visible to be interacted with by the player. - /// void ShowManualConfigScreen(); - /// /// Makes the gamepad input preset selection menu screen visible to be interacted with by the player. - /// void ShowPresetSelectionScreen(); - /// /// Clears the InputScheme that was configured during manual configuration and resets the sequence to the first step. - /// void ResetManualConfigSequence(); - /// /// Applies the manually configured InputScheme as the active InputScheme of the configuring player. - /// void ApplyManuallyConfiguredScheme(); - /// /// Applies a gamepad InputScheme preset as the active InputScheme of the configuring player. - /// void ApplyGamepadInputPreset(GamepadType gamepadType); #pragma endregion #pragma region Input Event Handling Breakdown - /// /// Handles the player interaction with the SettingsInputMappingWizardGUI's WizardManualConfigScreen GUI elements. - /// - /// The GUIEvent containing information about the player interaction with an element. + /// @param guiEvent The GUIEvent containing information about the player interaction with an element. void HandleManualConfigScreenInputEvents(GUIEvent& guiEvent); - /// /// Handles the player interaction with the SettingsInputMappingWizardGUI's WizardPresetSelectScreen GUI elements. - /// - /// The GUIEvent containing information about the player interaction with an element. + /// @param guiEvent The GUIEvent containing information about the player interaction with an element. void HandlePresetSelectScreenInputEvents(GUIEvent& guiEvent); #pragma endregion #pragma region Input Configuration Sequence Handling Breakdown - /// /// Handles step changes in the manual input configuration sequence. - /// void HandleManualConfigStepChange(); - /// /// Handles capturing input and progressing the keyboard only configuration sequence. - /// - /// Whether input was captured and the sequence needs to progress. + /// @return Whether input was captured and the sequence needs to progress. bool UpdateKeyboardConfigSequence(); - /// /// Handles capturing input and progressing the mouse and keyboard configuration sequence. - /// - /// Whether input was captured and the sequence needs to progress. + /// @return Whether input was captured and the sequence needs to progress. bool UpdateMouseAndKeyboardConfigSequence(); - /// /// Handles capturing input and progressing the D-Pad type gamepad configuration sequence. - /// - /// Whether input was captured and the sequence needs to progress. + /// @return Whether input was captured and the sequence needs to progress. bool UpdateGamepadDPadConfigSequence(); - /// /// Handles capturing input and progressing the dual-analog type gamepad (DualShock/Xbox) configuration sequence. - /// - /// Whether input was captured and the sequence needs to progress. + /// @return Whether input was captured and the sequence needs to progress. bool UpdateGamepadAnalogConfigSequence(); #pragma endregion - /// /// Clears all the member variables of this SettingsInputMappingWizardGUI, effectively resetting the members of this object. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/Menus/SettingsMiscGUI.cpp b/Source/Menus/SettingsMiscGUI.cpp index 74185b656c..199e8f32c0 100644 --- a/Source/Menus/SettingsMiscGUI.cpp +++ b/Source/Menus/SettingsMiscGUI.cpp @@ -11,8 +11,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsMiscGUI::SettingsMiscGUI(GUIControlManager* parentControlManager) : m_GUIControlManager(parentControlManager) { m_MiscSettingsBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionBoxMiscSettings")); @@ -48,15 +46,11 @@ namespace RTE { m_SceneBackgroundAutoScaleSlider->SetValue(g_SettingsMan.GetSceneBackgroundAutoScaleMode()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsMiscGUI::SetEnabled(bool enable) const { m_MiscSettingsBox->SetVisible(enable); m_MiscSettingsBox->SetEnabled(enable); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsMiscGUI::UpdateSceneBackgroundAutoScaleLabel() { switch (g_SettingsMan.GetSceneBackgroundAutoScaleMode()) { case 1: @@ -71,8 +65,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsMiscGUI::HandleInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Notification) { if (guiEvent.GetControl() == m_SkipIntroCheckbox) { diff --git a/Source/Menus/SettingsMiscGUI.h b/Source/Menus/SettingsMiscGUI.h index 804c646e50..c74da1f397 100644 --- a/Source/Menus/SettingsMiscGUI.h +++ b/Source/Menus/SettingsMiscGUI.h @@ -10,40 +10,30 @@ namespace RTE { class GUISlider; class GUIEvent; - /// /// Handling for misc settings through the game settings user interface. - /// class SettingsMiscGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a SettingsMiscGUI object in system memory and make it ready for use. - /// - /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsMiscGUI. Ownership is NOT transferred! + /// @param parentControlManager Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsMiscGUI. Ownership is NOT transferred! explicit SettingsMiscGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Concrete Methods - /// /// Enables or disables the SettingsMiscGUI. - /// - /// Show and enable or hide and disable the SettingsMiscGUI. + /// @param enable Show and enable or hide and disable the SettingsMiscGUI. void SetEnabled(bool enable = true) const; - /// /// Handles the player interaction with the SettingsMiscGUI GUI elements. - /// - /// The GUIEvent containing information about the player interaction with an element. + /// @param guiEvent The GUIEvent containing information about the player interaction with an element. void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. - /// /// GUI elements that compose the misc settings menu screen. - /// GUICollectionBox* m_MiscSettingsBox; GUICheckbox* m_SkipIntroCheckbox; GUICheckbox* m_ShowToolTipsCheckbox; @@ -57,9 +47,7 @@ namespace RTE { GUISlider* m_SceneBackgroundAutoScaleSlider; #pragma region Misc Settings Handling - /// /// Updates the Scene background auto-scale label according to the setting. - /// void UpdateSceneBackgroundAutoScaleLabel(); #pragma endregion diff --git a/Source/Menus/SettingsVideoGUI.cpp b/Source/Menus/SettingsVideoGUI.cpp index 902a5ef8aa..cfe38fe7aa 100644 --- a/Source/Menus/SettingsVideoGUI.cpp +++ b/Source/Menus/SettingsVideoGUI.cpp @@ -30,8 +30,6 @@ namespace RTE { #endif } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SettingsVideoGUI::SettingsVideoGUI(GUIControlManager* parentControlManager) : m_GUIControlManager(parentControlManager) { m_NewResX = g_WindowMan.GetResX(); @@ -83,8 +81,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::CreatePresetResolutionBox() { m_PresetResolutionBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionPresetResolution")); m_PresetResolutionComboBox = dynamic_cast(m_GUIControlManager->GetControl("ComboPresetResolution")); @@ -95,8 +91,6 @@ namespace RTE { PopulateResolutionsComboBox(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::CreateCustomResolutionBox() { m_CustomResolutionBox = dynamic_cast(m_GUIControlManager->GetControl("CollectionCustomResolution")); m_CustomResolutionBox->SetVisible(false); @@ -126,8 +120,6 @@ namespace RTE { m_CustomResolutionMessageLabel->SetVisible(false); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::SetEnabled(bool enable) const { m_VideoSettingsBox->SetVisible(enable); m_VideoSettingsBox->SetEnabled(enable); @@ -147,14 +139,10 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GUICollectionBox* SettingsVideoGUI::GetActiveDialogBox() const { return (m_ResolutionChangeDialogBox->GetEnabled() && m_ResolutionChangeDialogBox->GetVisible()) ? m_ResolutionChangeDialogBox : nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::CloseActiveDialogBox() const { if (m_ResolutionChangeDialogBox->GetEnabled() && m_ResolutionChangeDialogBox->GetVisible()) { m_ResolutionChangeDialogBox->SetVisible(false); @@ -162,8 +150,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool SettingsVideoGUI::IsSupportedResolution(int width, int height) const { if ((width >= c_MinResX && height >= c_MinResY) && (width <= g_WindowMan.GetMaxResX() && height <= g_WindowMan.GetMaxResY())) { return true; @@ -171,8 +157,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::PopulateResolutionsComboBox() { m_PresetResolutions.clear(); m_PresetResolutionComboBox->ClearList(); @@ -221,8 +205,6 @@ namespace RTE { m_CustomResolutionMultiplierComboBox->SetSelectedIndex(0); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::UpdateCustomResolutionLimits() { g_WindowMan.MapDisplays(); @@ -239,7 +221,6 @@ namespace RTE { m_CustomResolutionHeightTextBox->SetText(std::to_string(newMaxResY)); } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsVideoGUI::ApplyNewResolution(bool displaysWereMapped) { bool needWarning = (g_WindowMan.GetResX() != m_NewResX) && (g_WindowMan.GetResY() != m_NewResY); @@ -258,8 +239,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::ApplyQuickChangeResolution(ResolutionQuickChangeType resolutionChangeType) { g_WindowMan.MapDisplays(); @@ -298,8 +277,6 @@ namespace RTE { ApplyNewResolution(true); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::ApplyPresetResolution() { int presetResListEntryID = m_PresetResolutionComboBox->GetSelectedIndex(); if (presetResListEntryID >= 0) { @@ -318,8 +295,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::ApplyCustomResolution() { m_CustomResolutionMessageLabel->SetVisible(false); m_NewFullscreen = m_FullscreenCheckbox->GetCheck(); @@ -366,8 +341,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SettingsVideoGUI::HandleInputEvents(GUIEvent& guiEvent) { if (guiEvent.GetType() == GUIEvent::Command) { if (guiEvent.GetMsg() == GUIButton::Pushed) { diff --git a/Source/Menus/SettingsVideoGUI.h b/Source/Menus/SettingsVideoGUI.h index 599ebd55d8..4c486a2f1e 100644 --- a/Source/Menus/SettingsVideoGUI.h +++ b/Source/Menus/SettingsVideoGUI.h @@ -13,51 +13,37 @@ namespace RTE { class GUITextBox; class GUIEvent; - /// /// Handling for video settings through the game settings user interface. - /// class SettingsVideoGUI { public: #pragma region Creation - /// /// Constructor method used to instantiate a SettingsVideoGUI object in system memory and make it ready for use. - /// - /// Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsVideoGUI. Ownership is NOT transferred! + /// @param parentControlManager Pointer to the parent GUIControlManager which owns all the GUIControls of this SettingsVideoGUI. Ownership is NOT transferred! explicit SettingsVideoGUI(GUIControlManager* parentControlManager); #pragma endregion #pragma region Getters and Setters - /// /// Enables or disables the SettingsVideoGUI. - /// - /// Show and enable or hide and disable the SettingsVideoGUI. + /// @param enable Show and enable or hide and disable the SettingsVideoGUI. void SetEnabled(bool enable = true) const; - /// /// Gets the currently active GUICollectionBox that acts as a dialog box and requires disabling navigation and drawing an overlay. - /// - /// Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! + /// @return Pointer to the GUICollectionBox that is the currently active dialog box, if any. Ownership is NOT transferred! GUICollectionBox* GetActiveDialogBox() const; - /// /// Closes the currently active GUICollectionBox that acts as a dialog box by hiding it. If the active dialog box is a sub-menu, disables it. - /// void CloseActiveDialogBox() const; #pragma endregion #pragma region Concrete Methods - /// /// Handles the player interaction with the SettingsVideoGUI GUI elements. - /// - /// The GUIEvent containing information about the player interaction with an element. + /// @param guiEvent The GUIEvent containing information about the player interaction with an element. void HandleInputEvents(GUIEvent& guiEvent); #pragma endregion private: - /// /// Enumeration for the different types of quick resolution change options. - /// enum ResolutionQuickChangeType { Windowed, Fullscreen, @@ -65,34 +51,26 @@ namespace RTE { QuickChangeTypeCount }; - /// /// Struct containing information about a supported resolution preset. - /// struct PresetResolutionRecord { int Width; //!< Resolution width. int Height; //!< Resolution height. float Scale; //!< Whether resolution is upscaled. - /// /// Constructor method to instantiate a PresetResolutionRecord object in system memory and make it ready for use. - /// - /// Resolution width. - /// Resolution height. - /// Whether resolution is upscaled. + /// @param width Resolution width. + /// @param height Resolution height. + /// @param upscaled Whether resolution is upscaled. PresetResolutionRecord(int width, int height, float scale) : Width(width), Height(height), Scale(scale) {} - /// /// Makes UI displayable string with resolution info. - /// - /// String with resolution info. + /// @return String with resolution info. std::string GetDisplayString() const; - /// /// Comparison operator for eliminating duplicates and sorting in the temporary PresetResolutionRecord std::sets during PopulateResolutionsComboBox. - /// - /// The PresetResolutionRecord to compare with. - /// Bool with the result of the comparison. + /// @param rhs The PresetResolutionRecord to compare with. + /// @return Bool with the result of the comparison. bool operator<(const PresetResolutionRecord& rhs) const { if (Width == rhs.Width && Height == rhs.Height) { return Scale > rhs.Scale; @@ -112,9 +90,7 @@ namespace RTE { float m_NewResMultiplier; //!< How much the new resolution should be upscaled when changing resolution. bool m_NewFullscreen; //!< Whether the game will be windowed or fullscreen. - /// /// GUI elements that compose the video settings menu screen. - /// GUICollectionBox* m_VideoSettingsBox; GUIRadioButton* m_TwoPlayerSplitscreenHSplitRadioButton; GUIRadioButton* m_TwoPlayerSplitscreenVSplitRadioButton; @@ -139,61 +115,41 @@ namespace RTE { std::array m_ResolutionQuickToggleButtons; #pragma region Create Breakdown - /// /// Creates all the elements that compose the preset resolution selection box. - /// void CreatePresetResolutionBox(); - /// /// Creates all the elements that compose the custom resolution controls box. - /// void CreateCustomResolutionBox(); #pragma endregion #pragma region Video Settings Handling - /// /// Checks whether the passed in width and height values can be used as a valid resolution setting. - /// - /// Resolution width. - /// Resolution height. - /// Whether the resolution is supported or not. + /// @param width Resolution width. + /// @param height Resolution height. + /// @return Whether the resolution is supported or not. bool IsSupportedResolution(int width, int height) const; - /// /// Fills the preset resolutions set and combo box with scaled resolutions down to c_MinRes. Defaults the combobox to the closest resolution to c_DefaultRes. - /// void PopulateResolutionsComboBox(); - /// /// Creates Resolution multipliers down to c_DefaultRes. - /// void PopulateResMultplierComboBox(); - /// /// Remaps the displays to get the new maximum resolution values to update the numeric limits on the custom resolution textboxes. - /// void UpdateCustomResolutionLimits(); - /// /// Attempts to change the resolution using the new values set by this SettingsVideoGUI, or if an Activity is running, first prompts to end it. - /// - /// Whether displays were mapped during interaction with this SettingsVideoGUI. + /// @param displaysWereMapped Whether displays were mapped during interaction with this SettingsVideoGUI. void ApplyNewResolution(bool displaysWereMapped = false); - /// /// Attempts to change the resolution using the new values set by the appropriate quick change type. - /// - /// The type of quick resolution change to apply. See ResolutionQuickChangeType enumeration. + /// @param resolutionChangeType The type of quick resolution change to apply. See ResolutionQuickChangeType enumeration. void ApplyQuickChangeResolution(ResolutionQuickChangeType resolutionChangeType); - /// /// Attempts to change the resolution using the new values set from the selected preset resolution. - /// void ApplyPresetResolution(); - /// /// Attempts to change the resolution using the new values set from the custom resolution controls. - /// void ApplyCustomResolution(); #pragma endregion diff --git a/Source/Menus/TitleScreen.cpp b/Source/Menus/TitleScreen.cpp index aabb0042ae..0f6e453a1f 100644 --- a/Source/Menus/TitleScreen.cpp +++ b/Source/Menus/TitleScreen.cpp @@ -12,8 +12,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::Clear() { m_FadeAmount = 0; @@ -68,8 +66,6 @@ namespace RTE { m_IntroSlides.fill(nullptr); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::Create(AllegroScreen* guiScreen) { m_TitleScreenMaxWidth = g_WindowMan.FullyCoversAllDisplays() ? g_WindowMan.GetPrimaryWindowDisplayWidth() / g_WindowMan.GetResMultiplier() : g_WindowMan.GetResX(); @@ -94,8 +90,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::CreateTitleElements() { m_DataRealmsLogo = ContentFile("Base.rte/GUIs/Title/Intro/DRLogo5x.png").GetAsBitmap(); m_FmodLogo = ContentFile("Base.rte/GUIs/Title/Intro/FMODLogo.png").GetAsBitmap(); @@ -145,8 +139,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::CreateIntroSequenceSlides() { std::string highRes = (g_WindowMan.GetResY() >= 680) ? "HD" : ""; for (int slideNum = 0; slideNum < m_IntroSlides.size(); ++slideNum) { @@ -154,8 +146,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::Update() { if (m_SectionSwitch) { m_SectionTimer.Reset(); @@ -200,8 +190,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::UpdateIntroLogoSequence(bool skipSection) { if (skipSection && m_IntroSequenceState != IntroSequence::FmodLogoFadeOut) { m_SectionSwitch = true; @@ -256,8 +244,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::UpdateIntroSlideshowSequence(bool skipSlideshow) { if (skipSlideshow && (m_IntroSequenceState > IntroSequence::SlideshowFadeIn && m_IntroSequenceState != IntroSequence::MainMenuAppear)) { m_SectionSwitch = true; @@ -376,8 +362,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::UpdateIntroPreMainMenuSequence() { switch (m_IntroSequenceState) { case IntroSequence::GameLogoAppear: @@ -423,8 +407,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::UpdateTitleTransitions() { static const float endDelay = 0.2F * g_SettingsMan.GetMenuTransitionDurationMultiplier(); @@ -547,8 +529,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::Draw() { if (!m_FinishedPlayingIntro) { if (m_IntroSequenceState >= IntroSequence::SlideshowFadeIn) { @@ -594,8 +574,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::DrawTitleScreenScene() { // This only needs to be done once, but bitmaps can be reloaded which effectively undoes this, so just do it all the time to not deal with flags and checks. set_write_alpha_blender(); @@ -626,8 +604,6 @@ namespace RTE { m_Station.Draw(g_FrameMan.GetBackBuffer32()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::DrawGameLogo() { m_GameLogo.Draw(g_FrameMan.GetBackBuffer32()); m_GameLogoGlow.SetPos(m_GameLogo.GetPos()); @@ -636,8 +612,6 @@ namespace RTE { m_GameLogoGlow.Draw(g_FrameMan.GetBackBuffer32(), Vector(), DrawMode::g_DrawTrans); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::DrawSlideshowSlide() { int slide = static_cast(m_IntroSequenceState) - static_cast(IntroSequence::ShowSlide1); Vector slidePos(static_cast((m_TitleScreenMaxWidth - m_IntroSlides.at(slide)->w) / 2), static_cast((g_WindowMan.GetResY() / 2) - (m_IntroSlides.at(slide)->h / 2))); @@ -668,8 +642,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TitleScreen::DrawOverlayEffectBitmap() const { set_trans_blender(m_FadeAmount, m_FadeAmount, m_FadeAmount, m_FadeAmount); draw_trans_sprite(g_FrameMan.GetBackBuffer32(), g_FrameMan.GetOverlayBitmap32(), 0, 0); diff --git a/Source/Menus/TitleScreen.h b/Source/Menus/TitleScreen.h index a565018056..1e50e5b2b4 100644 --- a/Source/Menus/TitleScreen.h +++ b/Source/Menus/TitleScreen.h @@ -12,15 +12,11 @@ namespace RTE { class AllegroScreen; class GUIFont; - /// /// Handling for the title screen scene composition, intro sequence and transitions between menu screens. - /// class TitleScreen { public: - /// /// Enumeration for the different transition (scrolling) states of the title screen. - /// enum class TitleTransition { MainMenu, MainMenuToScenario, @@ -43,33 +39,25 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a TitleScreen object in system memory and make it ready for use. - /// - /// Pointer to a GUIScreen interface that will be used to create this TitleScreen's GUIFont. Ownership is NOT transferred! + /// @param guiScreen Pointer to a GUIScreen interface that will be used to create this TitleScreen's GUIFont. Ownership is NOT transferred! explicit TitleScreen(AllegroScreen* guiScreen) { Clear(); Create(guiScreen); } - /// /// Makes the TitleScreen object ready for use. - /// - /// Pointer to a GUIScreen interface that will be used to create this TitleScreen's GUIFont. Ownership is NOT transferred! + /// @param guiScreen Pointer to a GUIScreen interface that will be used to create this TitleScreen's GUIFont. Ownership is NOT transferred! void Create(AllegroScreen* guiScreen); #pragma endregion #pragma region Getters and Setters - /// /// Gets the current title transition state. - /// - /// The current title transition state. See TitleTransition enumeration for values. + /// @return The current title transition state. See TitleTransition enumeration for values. TitleTransition GetTitleTransitionState() const { return m_TitleTransitionState; } - /// /// Sets the target title transition state and, if different from the current, sets the section switch to trigger the transition. - /// - /// The target title transition state. + /// @param newTransitionState The target title transition state. void SetTitleTransitionState(TitleTransition newTransitionState) { if (newTransitionState != m_TitleTransitionState) { m_TitleTransitionState = newTransitionState; @@ -77,51 +65,37 @@ namespace RTE { } } - /// /// Sets the title transition to a pending state, stores the orbit timer elapsed time and resets the fade screen blend value. /// This is used to correctly restart transition states after breaking out of the game loop back to the menu loop. - /// void SetTitlePendingTransition() { m_TitleTransitionState = TitleTransition::TransitionPending; m_StationOrbitTimerElapsedTime = static_cast(m_StationOrbitTimer.GetElapsedRealTimeS()); m_FadeAmount = 0; } - /// /// Gets the position of the planet on the title screen scene. - /// - /// Vector with the position of the planet on the title screen scene. + /// @return Vector with the position of the planet on the title screen scene. Vector GetPlanetPos() const { return m_PlanetPos; } - /// /// Gets the radius of the planet. - /// - /// The radius of the planet. + /// @return The radius of the planet. float GetPlanetRadius() const { return m_PlanetRadius; } - /// /// Gets the position of the station on the planet orbit. - /// - /// Vector with the position of the station on the planet orbit. + /// @return Vector with the position of the station on the planet orbit. Vector GetStationPos() const { return m_StationOffset; } #pragma endregion #pragma region Concrete Methods - /// /// Updates the TitleScreen state. - /// void Update(); - /// /// Draws the TitleScreen to the screen. - /// void Draw(); #pragma endregion private: - /// /// Enumeration for the different states of the intro sequence. - /// enum class IntroSequence { DataRealmsLogoFadeIn, DataRealmsLogoDisplay, @@ -146,13 +120,9 @@ namespace RTE { MainMenuAppear }; - /// /// Struct that holds information for a title screen scene backdrop star. - /// struct Star { - /// /// Enumeration for the different Star sizes. - /// enum class StarSize { StarSmall, StarLarge, @@ -217,75 +187,51 @@ namespace RTE { std::array m_IntroSlides; //!< Array that contains all the slideshow slide bitmaps. Not Owned. #pragma region Create Breakdown - /// /// Creates all the elements that compose the title screen scene. - /// void CreateTitleElements(); - /// /// Creates the intro sequence slideshow slides. - /// void CreateIntroSequenceSlides(); #pragma endregion #pragma region Title Scene Handling - /// /// Sets the duration of a new section and resets the switch. - /// - /// The duration of the new section, in seconds. + /// @param newDuration The duration of the new section, in seconds. void SetSectionDurationAndResetSwitch(float newDuration) { m_SectionDuration = newDuration; m_SectionSwitch = false; } - /// /// Updates the title screen transition states and scrolls the title screen scene accordingly. - /// void UpdateTitleTransitions(); - /// /// Draws the whole title screen scene to the screen. - /// void DrawTitleScreenScene(); - /// /// Draws the game logo and effects to the screen. - /// void DrawGameLogo(); - /// /// Draws the overlay bitmap for fade in/out or darkening effects to the screen. - /// void DrawOverlayEffectBitmap() const; #pragma endregion #pragma region Intro Sequence Handling - /// /// Updates the state of the intro sequence logo splash. - /// - /// Whether the current section of the logo splash sequence should be skipped. + /// @param skipSection Whether the current section of the logo splash sequence should be skipped. void UpdateIntroLogoSequence(bool skipSection = false); - /// /// Updates the state of the intro sequence slideshow. - /// - /// Whether the whole slideshow should be skipped. + /// @param skipSlideshow Whether the whole slideshow should be skipped. void UpdateIntroSlideshowSequence(bool skipSlideshow = false); - /// /// Updates the state of the intro pre-main menu sequence. - /// void UpdateIntroPreMainMenuSequence(); - /// /// Draws the current slideshow sequence slide to the screen. - /// void DrawSlideshowSlide(); #pragma endregion - /// /// Clears all the member variables of this TitleScreen, effectively resetting the members of this object. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/System/AllegroTools.h b/Source/System/AllegroTools.h index b41b7a5713..2e7ce14996 100644 --- a/Source/System/AllegroTools.h +++ b/Source/System/AllegroTools.h @@ -1,21 +1,15 @@ #ifndef _RTEALLEGROTOOLS_H_ #define _RTEALLEGROTOOLS_H_ -/// /// Contains hacks and workarounds for missing or wrong allegro functionality. /// Note: Prefer fixing in allegro itself over adding hacks here. -/// namespace RTE { #pragma region True Alpha Blending - /// /// Workaround for allegro's missing true alpha blender, use instead of set_alpha_blender when alpha values are desired or necessary after draw: /// ``` set_blender_mode_ex(_blender_black, _blender_black, _blender_black, TrueAlphaBlender, _blender_black, _blender_black, _blender_black, 0, 0, 0, 0);``` - /// unsigned long TrueAlphaBlender(unsigned long x, unsigned long y, unsigned long n); - /// /// Sets the 32bit allegro blender mode to TrueAlphaBlender - /// void SetTrueAlphaBlender(); #pragma endregion } // namespace RTE diff --git a/Source/System/Atom.cpp b/Source/System/Atom.cpp index 6ebc4ad7ef..f594a85130 100644 --- a/Source/System/Atom.cpp +++ b/Source/System/Atom.cpp @@ -21,8 +21,6 @@ namespace RTE { // This forms a circle around the Atom's offset center, to check for mask color pixels in order to determine the normal at the Atom's position. const int Atom::s_NormalChecks[c_NormalCheckCount][2] = {{0, -3}, {1, -3}, {2, -2}, {3, -1}, {3, 0}, {3, 1}, {2, 2}, {1, 3}, {0, 3}, {-1, 3}, {-2, 2}, {-3, 1}, {-3, 0}, {-3, -1}, {-2, -2}, {-1, -3}}; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Atom::Clear() { m_Offset.Reset(); m_OriginalOffset.Reset(); @@ -62,8 +60,6 @@ namespace RTE { // m_Delta[m_Dom] = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::Create(const Vector& offset, Material const* material, MovableObject* owner, Color trailColor, int trailLength) { m_Offset = m_OriginalOffset = offset; // Use the offset as normal for now @@ -77,8 +73,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::Create(const Atom& reference) { m_Offset = reference.m_Offset; m_OriginalOffset = reference.m_OriginalOffset; @@ -96,8 +90,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -119,8 +111,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::Save(Writer& writer) const { Serializable::Save(writer); @@ -134,8 +124,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void* Atom::GetPoolMemory() { std::lock_guard guard(s_MemoryPoolMutex); @@ -156,8 +144,6 @@ namespace RTE { return foundMemory; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Atom::FillPool(int fillAmount) { // Default to the set block allocation size if fillAmount is 0 if (fillAmount <= 0) { @@ -173,8 +159,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::ReturnPoolMemory(void* returnedMemory) { if (!returnedMemory) { return false; @@ -189,8 +173,6 @@ namespace RTE { return s_InstancesInUse; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Atom::CalculateNormal(BITMAP* sprite, Vector spriteCenter) { RTEAssert(sprite, "Trying to set up Atom normal without passing in bitmap"); @@ -231,8 +213,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Atom::IsIgnoringMOID(MOID whichMOID) { if (whichMOID == m_IgnoreMOID) { return true; @@ -272,8 +252,6 @@ namespace RTE { return ignored; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Atom::MOHitResponse() { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -346,8 +324,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - HitData& Atom::TerrHitResponse() { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -422,8 +398,6 @@ namespace RTE { return m_LastHit; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Atom::SetupPos(Vector startPos) { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -450,8 +424,6 @@ namespace RTE { return m_MOIDHit != g_NoMOID || m_TerrainMatHit != g_MaterialAir; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::SetupSeg(Vector startPos, Vector trajectory, float stepRatio) { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); m_TerrainMatHit = g_MaterialAir; @@ -505,8 +477,6 @@ namespace RTE { return m_Delta[m_Dom] - m_DomSteps; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Atom::StepForward(int numSteps) { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -593,8 +563,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Atom::StepBack() { RTEAssert(m_OwnerMO, "Stepping an Atom without a parent MO!"); @@ -618,8 +586,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Atom::Travel(float travelTime, bool autoTravel, bool scenePreLocked) { ZoneScoped; @@ -1074,8 +1040,6 @@ namespace RTE { return hitCount; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void HitData::Clear() { HitPoint.Reset(); VelDiff.Reset(); @@ -1098,8 +1062,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - HitData& HitData::operator=(const HitData& rhs) { if (this == &rhs) { return *this; diff --git a/Source/System/Atom.h b/Source/System/Atom.h index d9d25e7ea5..c5b00bb485 100644 --- a/Source/System/Atom.h +++ b/Source/System/Atom.h @@ -16,9 +16,7 @@ namespace RTE { }; #pragma region HitData - /// /// A struct to keep all data about a rigid body collision in one package. - /// struct HitData { MovableObject* Body[2]; //!< Pointers to the two hitting bodies. If hitee is 0, that means collision with terrain. The HitData struct doesn't own these. @@ -44,33 +42,23 @@ namespace RTE { float ImpulseFactor[2]; //!< The factor by which the final impulse vector should be scaled to account for multiple hits in the same step. bool Terminate[2]; //!< Whether either of the bodies should be stopped and deleted as a result of the collision. - /// /// Constructor method used to instantiate a HitData object in system memory. - /// HitData() { Clear(); } - /// /// Resets the entire HitData object to the default settings or values. - /// void Reset() { Clear(); } - /// /// An assignment operator for setting one HitData equal to another. - /// - /// A HitData reference. - /// A reference to the changed HitData. + /// @param rhs A HitData reference. + /// @return A reference to the changed HitData. HitData& operator=(const HitData& rhs); - /// /// Clears all the member variables of this HitData, effectively resetting the members of this abstraction level only. - /// void Clear(); }; #pragma endregion - /// /// A point (pixel) that tests for collisions with a BITMAP's drawn pixels, ie not the mask color. Owned and operated by other objects. - /// class Atom : public Serializable { public: @@ -78,15 +66,11 @@ namespace RTE { SerializableOverrideMethods; #pragma region Creation - /// /// Constructor method used to instantiate an Atom object in system memory. Create() should be called before using the object. - /// Atom() { Clear(); } - /// /// Copy constructor method used to instantiate an Atom object identical to an already existing one. - /// - /// An Atom object which is passed in by reference. + /// @param reference An Atom object which is passed in by reference. Atom(const Atom& reference) { if (this != &reference) { Clear(); @@ -94,385 +78,276 @@ namespace RTE { } } - /// /// Convenience constructor to both instantiate an Atom in memory and Create it at the same time. - /// - /// An offset Vector that will be used to offset collision calculations. - /// A Material that defines what material this Atom is made of. - /// The owner MovableObject of this Atom. Ownership is NOT transferred! - /// The trail color. - /// The trail length. If 0, no trail will be drawn. + /// @param offset An offset Vector that will be used to offset collision calculations. + /// @param material A Material that defines what material this Atom is made of. + /// @param owner The owner MovableObject of this Atom. Ownership is NOT transferred! + /// @param trailColor The trail color. + /// @param trailLength The trail length. If 0, no trail will be drawn. Atom(const Vector& offset, Material const* material, MovableObject* owner, Color trailColor = Color(), int trailLength = 0) { Clear(); Create(offset, material, owner, trailColor, trailLength); } - /// /// Convenience constructor to both instantiate an Atom in memory and Create it at the same time. - /// - /// An offset Vector that will be used to offset collision calculations. - /// The material ID of the Material that defines what this Atom is made of. - /// The owner MovableObject of this Atom. Ownership is NOT transferred! - /// The trail color. - /// The trail length. If 0, no trail will be drawn. + /// @param offset An offset Vector that will be used to offset collision calculations. + /// @param materialID The material ID of the Material that defines what this Atom is made of. + /// @param owner The owner MovableObject of this Atom. Ownership is NOT transferred! + /// @param trailColor The trail color. + /// @param trailLength The trail length. If 0, no trail will be drawn. Atom(const Vector& offset, unsigned char materialID, MovableObject* owner, Color trailColor = Color(), int trailLength = 0) { Clear(); Create(offset, g_SceneMan.GetMaterialFromID(materialID), owner, trailColor, trailLength); } - /// /// Creates an Atom to be identical to another, by deep copy. - /// - /// A reference to the Atom to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Atom to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Atom& reference); - /// /// Makes the Atom object ready for use. - /// - /// An offset Vector that will be used to offset collision calculations. - /// A Material that defines what material this Atom is made of. - /// The owner MovableObject of this Atom. Ownership is NOT transferred! - /// The trail color. - /// The trail length. If 0, no trail will be drawn. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param offset An offset Vector that will be used to offset collision calculations. + /// @param material A Material that defines what material this Atom is made of. + /// @param owner The owner MovableObject of this Atom. Ownership is NOT transferred! + /// @param trailColor The trail color. + /// @param trailLength The trail length. If 0, no trail will be drawn. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Vector& offset, Material const* material, MovableObject* owner, Color trailColor = Color(), int trailLength = 0); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up an Atom object before deletion from system memory. - /// ~Atom() { Destroy(); } - /// /// Destroys and resets (through Clear()) the Atom object. - /// void Destroy() { Clear(); } #pragma endregion #pragma region Memory Management - /// /// Grabs from the pre-allocated pool, an available chunk of memory the exact size of an Atom. OWNERSHIP IS TRANSFERRED! - /// - /// A pointer to the pre-allocated pool memory. OWNERSHIP IS TRANSFERRED! + /// @return A pointer to the pre-allocated pool memory. OWNERSHIP IS TRANSFERRED! static void* GetPoolMemory(); - /// /// Adds a certain number of newly allocated instances to this' pool. - /// - /// The number of instances to fill with. If 0 is specified, the set refill amount will be used. + /// @param fillAmount The number of instances to fill with. If 0 is specified, the set refill amount will be used. static void FillPool(int fillAmount = 0); - /// /// Returns a raw chunk of memory back to the pre-allocated available pool. - /// - /// The raw chunk of memory that is being returned. Needs to be the same size as an Atom. OWNERSHIP IS TRANSFERRED! - /// The count of outstanding memory chunks after this was returned. + /// @param returnedMemory The raw chunk of memory that is being returned. Needs to be the same size as an Atom. OWNERSHIP IS TRANSFERRED! + /// @return The count of outstanding memory chunks after this was returned. static int ReturnPoolMemory(void* returnedMemory); #pragma endregion #pragma region Getters and Setters - /// /// Gets the current owner MovableObject of this AtomGroup. - /// - /// A const pointer to the owner. + /// @return A const pointer to the owner. const MovableObject* GetOwner() const { return m_OwnerMO; } - /// /// Sets the current owner MovableObject of this AtomGroup. - /// - /// A pointer to the new owner. Ownership is NOT transferred! + /// @param newOwner A pointer to the new owner. Ownership is NOT transferred! void SetOwner(MovableObject* newOwner) { m_OwnerMO = newOwner; } - /// /// Gets the group ID of this Atom. - /// - /// The group ID of this Atom. + /// @return The group ID of this Atom. long GetSubID() const { return m_SubgroupID; } - /// /// Sets the subgroup ID of this Atom. - /// - /// The new subgroup ID of this Atom. + /// @param newID The new subgroup ID of this Atom. void SetSubID(long newID = 0) { m_SubgroupID = newID; } - /// /// Gets the material of this Atom. - /// - /// The material of this Atom. + /// @return The material of this Atom. Material const* GetMaterial() const { return m_Material ? m_Material : g_SceneMan.GetMaterialFromID(g_MaterialAir); } - /// /// Sets the material of this Atom. - /// - /// The new material of this Atom. + /// @param newMat The new material of this Atom. void SetMaterial(const Material* newMat) { m_Material = newMat; } - /// /// Gets the Color of this Atom's trail. - /// - /// A Color object describing the trail color. + /// @return A Color object describing the trail color. Color GetTrailColor() const { return m_TrailColor; } - /// /// Sets the color value of this Atom's trail. - /// - /// A Color object specifying the new trail color. + /// @param newTrailColor A Color object specifying the new trail color. void SetTrailColor(Color newTrailColor) { m_TrailColor = newTrailColor; } - /// /// Gets the longest a trail can be drawn, in pixels. - /// - /// The new max length, in pixels. If 0, no trail is drawn. + /// @return The new max length, in pixels. If 0, no trail is drawn. int GetTrailLength() const { return m_TrailLength; } - /// /// Sets the longest a trail can be drawn, in pixels. - /// - /// The new max length, in pixels. If 0, no trail is drawn. + /// @param trailLength The new max length, in pixels. If 0, no trail is drawn. void SetTrailLength(const int trailLength) { m_TrailLength = trailLength; } - /// /// Gets the length variation of this Atom's trail. - /// - /// The length variation of this Atom's trail. + /// @return The length variation of this Atom's trail. float GetTrailLengthVariation() const { return m_TrailLengthVariation; } - /// /// Sets the length variation scalar of a trail. - /// - /// The new length variation scalar, 0 meaning no variation and 1 meaning full variation. + /// @param trailLengthVariation The new length variation scalar, 0 meaning no variation and 1 meaning full variation. void SetTrailLengthVariation(float trailLengthVariation) { m_TrailLengthVariation = trailLengthVariation; } - /// /// Gets the offset vector that was first set for this Atom. The GetOffset may have additional offsets baked into it if this is part of an group. - /// - /// The original offset Vector. + /// @return The original offset Vector. const Vector& GetOriginalOffset() const { return m_OriginalOffset; } - /// /// Gets the offset vector. - /// - /// The current offset Vector. + /// @return The current offset Vector. const Vector& GetOffset() const { return m_Offset; } - /// /// Sets a new offset vector for the collision calculations. - /// - /// A const reference to a Vector that will be used as offset. + /// @param newOffset A const reference to a Vector that will be used as offset. void SetOffset(const Vector& newOffset) { m_Offset = newOffset; } - /// /// Gets the surface normal of this vector, if it has been successfully calculated. If not, it'll be a 0 vector. - /// - /// The current normalized surface normal Vector of this. + /// @return The current normalized surface normal Vector of this. const Vector& GetNormal() const { return m_Normal; } #pragma endregion #pragma region Concrete Methods - /// /// Sets up the normal of this atom, based on its position on a sprite's bitmap. /// It will check pixels around it and see if they are inside the object or not, and infer a collision normal based on that. THIS ONLY WORKS IF THE ATOM IS ON THE SURFACE OF THE SPRITE! - /// - /// The bitmap to check against. Ownership IS NOT transferred! - /// Where on the bitmap the center of the object is. This atom's offset will be applied automatically before checking for its normal. - /// Whether normal was successfully derived from the bitmap. If not, then a provisional one is derived from the offset. + /// @param sprite The bitmap to check against. Ownership IS NOT transferred! + /// @param spriteCenter Where on the bitmap the center of the object is. This atom's offset will be applied automatically before checking for its normal. + /// @return Whether normal was successfully derived from the bitmap. If not, then a provisional one is derived from the offset. bool CalculateNormal(BITMAP* sprite, Vector spriteCenter); #pragma endregion #pragma region Collision - /// /// Gets the stored data struct on the last collision experienced by this Atom. - /// - /// The Vector describing the velocity of this Atom at the last time it hit something. + /// @return The Vector describing the velocity of this Atom at the last time it hit something. HitData& GetHitData() { return m_LastHit; } - /// /// Sets the HitData struct this Atom uses to represent the last hit it experienced. - /// - /// A reference to a HitData struct that will be copied to the Atom's. + /// @param newHitData A reference to a HitData struct that will be copied to the Atom's. void SetHitData(const HitData& newHitData) { m_LastHit = newHitData; } - /// /// Checks whether this Atom is set to ignore collisions with the terrain. - /// - /// Whether or not this is ignoring hits the with terrain. + /// @return Whether or not this is ignoring hits the with terrain. bool IsIgnoringTerrain() const { return m_TerrainHitsDisabled; } - /// /// Checks whether this Atom is temporarily ignoring all MO hits. - /// - /// Whether or not this is ignoring all MO hits. + /// @return Whether or not this is ignoring all MO hits. bool IsIgnoringAllMOs() const { return m_MOHitsDisabled; } - /// /// Checks whether this Atom is set to ignore collisions with a MO of a specific MOID. - /// - /// The MOID to check if it is ignored. - /// Whether or not this MOID is being ignored. + /// @param whichMOID The MOID to check if it is ignored. + /// @return Whether or not this MOID is being ignored. bool IsIgnoringMOID(MOID whichMOID); - /// /// Adds a MOID that this Atom should ignore collisions with during its next travel sequence. - /// - /// The MOID to add to the ignore list. + /// @param ignore The MOID to add to the ignore list. void AddMOIDToIgnore(MOID ignore) { m_IgnoreMOIDs.push_back(ignore); } - /// /// AtomGroup may set this shared list of ignored MOIDs to avoid setting and removing ignored MOIDs for every atom one by one. The list is maintained only by AtomGroup, Atom never owns it. - /// - /// New MOIDs list to ignore. + /// @param ignoreMOIDsByGroup New MOIDs list to ignore. void SetIgnoreMOIDsByGroup(std::vector const* ignoreMOIDsByGroup) { m_IgnoreMOIDsByGroup = ignoreMOIDsByGroup; }; - /// /// Clear the list of MOIDs that this Atom is set to ignore collisions with during its next travel sequence. /// This should be done each frame so that fresh MOIDs can be re-added. (MOIDs are only valid during a frame). - /// void ClearMOIDIgnoreList() { m_IgnoreMOIDs.clear(); } - /// /// Gets the number of consecutive penetrations of Terrain that this Atom has successfully made, ending with wherever it is now. - /// - /// The number of consecutive penetrations. Resets to 0 as soon as penetration streak ends. + /// @return The number of consecutive penetrations. Resets to 0 as soon as penetration streak ends. int GetNumPenetrations() const { return m_NumPenetrations; } - /// /// Returns the ID of the MO hit at the previously taken step by TakeStep. This will potentially return g_NoMOID if this Atom is not set to hit MO's or the MO hit is marked to be ignored. - /// - /// The ID of the non-ignored MO, if any, that this Atom is now intersecting because of the last step taken. + /// @return The ID of the non-ignored MO, if any, that this Atom is now intersecting because of the last step taken. MOID HitWhatMOID() const { return m_MOIDHit; } - /// /// Calculates the collision response with another MO, if the last step taken forward resulted in an intersection with a non-ignored MO. /// Note that one step backward should be taken after the intersecting step so that the Atom is not intersecting the hit MO anymore. /// The hit response is calculated and appropriate forces are applied to the hit MO and the impulse force imposed on the owner MO are returned, and also stored in this Atom to be later retrieved with GetHitImpulse(). - /// - /// Collision data should already be set by SetHitData(). - /// Whether this collision is valid and should be considered further. + /// @remark Collision data should already be set by SetHitData(). + /// @return Whether this collision is valid and should be considered further. bool MOHitResponse(); - /// /// Returns the terrain material the previously taken step by TakeStep hit, if any. - /// - /// The ID of the material, if any, that this MO hit during the last step. + /// @return The ID of the material, if any, that this MO hit during the last step. unsigned char HitWhatTerrMaterial() { return m_TerrainMatHit; } - /// /// Calculates the collision response with the Terrain, if the last step taken forward resulted in an intersection with the Terrain. /// Note that one step backward should be taken after the intersecting step so that the Atom is not intersecting the Terrain anymore, and the hit response calculations can be made accurately. /// The resulting impulse vector is also stored in this Atom to be later retrieved with GetHitImpulse(). - /// - /// Collision data should already be set by SetHitData(). - /// The resulting HitData of this Atom with all the information about the collision filled out. + /// @remark Collision data should already be set by SetHitData(). + /// @return The resulting HitData of this Atom with all the information about the collision filled out. HitData& TerrHitResponse(); #pragma endregion #pragma region Travel - /// /// Sets this Atom's initial position up for a straight segment of a trajectory to step through. This is to be done before SetupSeg. /// It will report whether the atom's position is inside the terrain or another MO. It also disables this' terrain and/or MO collisions when traveling, until the travel encounters an air/noMO pixel again. - /// - /// A Vector with the position to start from, in scene coordinates. - /// If the start pos is already intersecting the terrain or an MO, then this will return true. Do collision response as needed. + /// @param startPos A Vector with the position to start from, in scene coordinates. + /// @return If the start pos is already intersecting the terrain or an MO, then this will return true. Do collision response as needed. bool SetupPos(Vector startPos); - /// /// Gets the absolute current position of this Atom, in scene coordinates. This includes the rotated offset, and any steps that may have been taken on a segment. - /// - /// The current position of the Atom, with the offset baked in. + /// @return The current position of the Atom, with the offset baked in. Vector GetCurrentPos() const { return Vector(m_IntPos[X], m_IntPos[Y]); } - /// /// Sets this Atom up for a straight segment of a trajectory to step through. If this Atom find the startPos to be on an MO, it will ignore any collisions with that MO for the entire segment. /// The Scene MUST BE LOCKED before calling this! - /// - /// A Vector with the position to start from, in scene coordinates. - /// A Vector with the trajectory to travel during this next segment. - /// The ratio between the actual steps to be taken and the future calls to TakeStep(). - /// The total number of steps this will take in the newly set-up segment. + /// @param startPos A Vector with the position to start from, in scene coordinates. + /// @param trajectory A Vector with the trajectory to travel during this next segment. + /// @param stepRatio The ratio between the actual steps to be taken and the future calls to TakeStep(). + /// @return The total number of steps this will take in the newly set-up segment. int SetupSeg(Vector startPos, Vector trajectory, float stepRatio = 1.0); - /// /// Gets the segment trajectory currently set by SetupSeg. - /// - /// The currently traversed segment trajectory. + /// @return The currently traversed segment trajectory. Vector GetSegTraj() const { return m_SegTraj; } - /// /// Indicates the total original length of the current segment set by SetupSeg. - /// - /// The length of the currently traversed segment. + /// @return The length of the currently traversed segment. float GetSegLength() const { return m_SegTraj.GetMagnitude(); } - /// /// Takes one step along the trajectory segment set up by SetupSeg(). The Scene MUST BE LOCKED before calling this! - /// - /// The number of steps to take. - /// + /// @param numSteps The number of steps to take. + /// @return /// Whether anything was hit during the steps or not. MO hits will only be reported if this Atom is set to hit them, and the MO hit is not ignored by this Atom (if the segment started on that MO). /// Also if terrain hits are temporarily disabled, they will not be reported. - /// bool StepForward(int numSteps = 1); - /// /// Takes one step back, or undos the step, if any, previously taken along the trajectory segment set up by SetupSeg(). - /// - /// CAUTION: The previous HitWhat vars are not reset to what they previously were! + /// @remark CAUTION: The previous HitWhat vars are not reset to what they previously were! void StepBack(); - /// /// Gets the normalized ratio of how many steps are actually taken to how many calls to TakeStep are made. - /// - /// A normalized float describing the step ratio. + /// @return A normalized float describing the step ratio. float GetStepRatio() const { return m_StepRatio; } - /// /// Sets the ratio of how many steps are actually taken to how many calls to TakeStep are made. - /// - /// A float specifying the new step ratio. + /// @param newStepRatio A float specifying the new step ratio. void SetStepRatio(float newStepRatio) { m_StepRatio = newStepRatio; } - /// /// Indicates how many more steps remain to be taken to traverse the entire trajectory segment. - /// - /// The number of steps that remain to be taken on the set trajectory segment. + /// @return The number of steps that remain to be taken on the set trajectory segment. int GetStepsLeft() const { return m_Delta[m_Dom] - m_DomSteps; } - /// /// Tells this Atom that the previous travel move's fractional error isn't valid for consecutive travel moves. - /// void ChangedDir() { m_ChangedDir = true; } - /// /// Uses the current state of the owning MovableObject to determine if there are any collisions in the path of its travel during this frame, and if so, apply all collision responses to the MO. - /// - /// The amount of time in s that this Atom is allowed to travel. - /// A bool specifying if the end position result should be moved along the trajectory if no terrain is hit. - /// Whether the Scene has been pre-locked or not. - /// The number of hits against terrain that were made during the travel. + /// @param travelTime The amount of time in s that this Atom is allowed to travel. + /// @param autoTravel A bool specifying if the end position result should be moved along the trajectory if no terrain is hit. + /// @param scenePreLocked Whether the Scene has been pre-locked or not. + /// @return The number of hits against terrain that were made during the travel. int Travel(float travelTime, bool autoTravel = true, bool scenePreLocked = false); #pragma endregion #pragma region Operator Overloads - /// /// New operator overload for the pool allocation of Atoms. - /// - /// - /// + /// @param size + /// @return static void* operator new(size_t size) { return Atom::GetPoolMemory(); } - /// /// Delete operator overload for the pool deallocation of Atoms. - /// - /// + /// @param instance static void operator delete(void* instance) { Atom::ReturnPoolMemory(instance); } - /// /// An assignment operator for setting one Atom equal to another. - /// - /// An Atom reference. - /// A reference to the changed Atom. + /// @param rhs An Atom reference. + /// @return A reference to the changed Atom. Atom& operator=(const Atom& rhs) { if (this != &rhs) { Destroy(); @@ -548,9 +423,7 @@ namespace RTE { private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. - /// /// Clears all the member variables of this Atom, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/System/Box.cpp b/Source/System/Box.cpp index ce06304ede..2d920ba5a7 100644 --- a/Source/System/Box.cpp +++ b/Source/System/Box.cpp @@ -4,8 +4,6 @@ namespace RTE { const std::string Box::c_ClassName = "Box"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool IntRect::IntersectionCut(const IntRect& rhs) { if (Intersects(rhs)) { m_Left = std::max(m_Left, rhs.m_Left); @@ -17,8 +15,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::Create(const Vector& corner1, const Vector& corner2) { m_Corner = corner1; m_Width = corner2.m_X - corner1.m_X; @@ -27,8 +23,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::Create(float x1, float y1, float x2, float y2) { m_Corner.SetXY(x1, y1); m_Width = x2 - x1; @@ -37,8 +31,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::Create(const Vector& corner, float width, float height) { m_Corner = corner; m_Width = width; @@ -47,8 +39,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::Create(const Box& reference) { m_Corner = reference.m_Corner; m_Width = reference.m_Width; @@ -57,8 +47,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -69,8 +57,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Box::Save(Writer& writer) const { Serializable::Save(writer); @@ -81,8 +67,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Box::Unflip() { if (m_Width < 0) { m_Width = -m_Width; @@ -94,8 +78,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Box::IsWithinBox(const Vector& point) const { return !IsEmpty() && (((m_Width > 0 && point.m_X >= m_Corner.m_X && point.m_X < (m_Corner.m_X + m_Width)) || (m_Width < 0 && point.m_X < m_Corner.m_X && point.m_X >= (m_Corner.m_X + m_Width))) && @@ -103,22 +85,16 @@ namespace RTE { (m_Height < 0 && point.m_Y < m_Corner.m_Y && point.m_Y <= (m_Corner.m_Y + m_Height))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Box::IsWithinBoxX(float pointX) const { return !IsEmpty() && ((m_Width > 0 && pointX >= m_Corner.m_X && pointX < (m_Corner.m_X + m_Width)) || (m_Width < 0 && pointX < m_Corner.m_X && pointX >= (m_Corner.m_X + m_Width))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Box::IsWithinBoxY(float pointY) const { return !IsEmpty() && ((m_Height > 0 && pointY >= m_Corner.m_Y && pointY < (m_Corner.m_Y + m_Height)) || (m_Height < 0 && pointY < m_Corner.m_Y && pointY <= (m_Corner.m_Y + m_Height))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Box::GetWithinBoxX(float pointX) const { if (m_Width > 0) { return Limit(pointX, m_Corner.m_X + m_Width - 1, m_Corner.m_X); @@ -128,8 +104,6 @@ namespace RTE { return m_Corner.m_X; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Box::GetWithinBoxY(float pointY) const { if (m_Height > 0) { return Limit(pointY, m_Corner.m_Y + m_Height - 1, m_Corner.m_Y); @@ -139,8 +113,6 @@ namespace RTE { return m_Corner.m_Y; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Box::IntersectsBox(const Box& rhs) { if (IsEmpty() || rhs.IsEmpty()) { return false; diff --git a/Source/System/Box.h b/Source/System/Box.h index 56d0e82e11..8a79f1b8bb 100644 --- a/Source/System/Box.h +++ b/Source/System/Box.h @@ -6,9 +6,7 @@ namespace RTE { - /// /// A simple rectangle with integer coordinates. - /// struct IntRect { int m_Left = 0; //!< X position of the IntRect top left corner. @@ -16,35 +14,27 @@ namespace RTE { int m_Right = 0; //!< X position of the IntRect bottom right corner. int m_Bottom = 0; //!< Y position of the IntRect bottom right corner. - /// /// Constructor method used to instantiate an IntRect object from four int values defining the initial corners of this IntRect. - /// - /// X position of the IntRect top left corner. - /// Y position of the IntRect top left corner. - /// X position of the IntRect bottom right corner. - /// Y position of the IntRect bottom right corner. + /// @param left X position of the IntRect top left corner. + /// @param top Y position of the IntRect top left corner. + /// @param right X position of the IntRect bottom right corner. + /// @param bottom Y position of the IntRect bottom right corner. IntRect(int left, int top, int right, int bottom) : m_Left(left), m_Top(top), m_Right(right), m_Bottom(bottom) {} - /// /// Checks whether this IntRect is intersecting another one. - /// - /// The other IntRect to check for intersection with. - /// Whether this IntRect is intersecting another one. + /// @param rhs The other IntRect to check for intersection with. + /// @return Whether this IntRect is intersecting another one. bool Intersects(const IntRect& rhs) const { return m_Left < rhs.m_Right && m_Right > rhs.m_Left && m_Top < rhs.m_Bottom && m_Bottom > rhs.m_Top; } - /// /// If this and the passed in IntRect intersect, this will be modified to represent the boolean AND of the two. /// If they don't intersect, nothing happens and false is returned. - /// - /// THe other IntRect to cut against. - /// Whether an intersection was detected and this was cut down to the AND of the two IntRects. + /// @param rhs THe other IntRect to cut against. + /// @return Whether an intersection was detected and this was cut down to the AND of the two IntRects. bool IntersectionCut(const IntRect& rhs); }; - /// /// A useful 2D axis-aligned rectangle class. - /// class Box : public Serializable { public: @@ -56,216 +46,156 @@ namespace RTE { float m_Height; //!< Height of this box. #pragma region Creation - /// /// Constructor method used to instantiate a Box object. - /// Box() { Clear(); } - /// /// Constructor method used to instantiate a Box object from two points. - /// - /// Vector position of the upper left corner of this box. - /// Vector position of the lower right corner of this box. + /// @param corner1 Vector position of the upper left corner of this box. + /// @param corner2 Vector position of the lower right corner of this box. Box(const Vector& corner1, const Vector& corner2) { Create(corner1, corner2); } - /// /// Constructor method used to instantiate a Box object from four float values defining the initial corners of this Box. - /// - /// X position of box upper left corner. - /// Y position of box upper left corner. - /// X position of box lower right corner. - /// Y position of box lower right corner. + /// @param x1 X position of box upper left corner. + /// @param y1 Y position of box upper left corner. + /// @param x2 X position of box lower right corner. + /// @param y2 Y position of box lower right corner. Box(float x1, float y1, float x2, float y2) { Create(x1, y1, x2, y2); } - /// /// Constructor method used to instantiate a Box object from one point and two dimensions. /// They can be negative but it will affect the interpretation of which corner is defined. The Box will always return positive values for width and height. - /// - /// Vector position of the upper left corner of this box. - /// Width of this box. - /// Height of this box. + /// @param corner Vector position of the upper left corner of this box. + /// @param width Width of this box. + /// @param height Height of this box. Box(const Vector& corner, float width, float height) { Create(corner, width, height); } - /// /// Copy constructor method used to instantiate a Box object identical to an already existing one. - /// - /// A Box object which is passed in by reference. + /// @param reference A Box object which is passed in by reference. Box(const Box& reference) { Create(reference); } - /// /// Makes the Box object ready for use. - /// - /// Vector position of the upper left corner of this box. - /// Vector position of the lower right corner of this box. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param corner1 Vector position of the upper left corner of this box. + /// @param corner2 Vector position of the lower right corner of this box. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Vector& corner1, const Vector& corner2); - /// /// Makes the Box object ready for use. - /// - /// X position of box upper left corner. - /// Y position of box upper left corner. - /// X position of box lower right corner. - /// Y position of box lower right corner. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param x1 X position of box upper left corner. + /// @param y1 Y position of box upper left corner. + /// @param x2 X position of box lower right corner. + /// @param y2 Y position of box lower right corner. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(float x1, float y1, float x2, float y2); - /// /// Makes the Box object ready for use. - /// - /// Vector position of the upper left corner of this box. - /// Width of this box. - /// Height of this box. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param corner Vector position of the upper left corner of this box. + /// @param width Width of this box. + /// @param height Height of this box. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Vector& corner, float width, float height); - /// /// Creates a Box to be identical to another, by deep copy. - /// - /// A reference to the Box to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Box to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Box& reference); #pragma endregion #pragma region Destruction - /// /// Resets the entire Box object to the default settings or values. - /// void Reset() override { Clear(); } #pragma endregion #pragma region Concrete Methods - /// /// Makes the corner of this box represent the upper left corner, and both width and height will end up positive. - /// void Unflip(); #pragma endregion #pragma region Getters and Setters - /// /// Shows if this box has 0 width OR 0 height, ie can't contain anything. - /// - /// Whether either width OR height are 0. + /// @return Whether either width OR height are 0. bool IsEmpty() const { return (m_Width == 0 || m_Height == 0); }; - /// /// Gets the primary corner of this box. - /// - /// A Vector with the primary corner of this box. + /// @return A Vector with the primary corner of this box. Vector GetCorner() const { return m_Corner; } - /// /// Sets the primary corner of this box. - /// - /// A Vector with the new primary corner. + /// @param newCorner A Vector with the new primary corner. void SetCorner(const Vector& newCorner) { m_Corner = newCorner; } - /// /// Gets the center point of this Box' area, in absolute Scene coordinates. - /// - /// The center point. + /// @return The center point. Vector GetCenter() const { return Vector(m_Corner.m_X + (m_Width / 2), m_Corner.m_Y + (m_Height / 2)); } - /// /// Sets the primary corner of this box, by specifying where the center ought to be. - /// - /// A Vector with the new center point. + /// @param newCenter A Vector with the new center point. void SetCenter(const Vector& newCenter) { m_Corner.SetXY(newCenter.m_X - (m_Width / 2), newCenter.m_Y - (m_Height / 2)); } - /// /// Gets the width of this box. Note that this can be negative if the box hasn't been righted with Unflip(). - /// - /// A float value that represents the width value of this Box. + /// @return A float value that represents the width value of this Box. float GetWidth() const { return m_Width; } - /// /// Sets the width of this box. Note that this can be negative if the box hasn't been righted with Unflip(). - /// - /// A float value that represents the width value of this Box. + /// @param width A float value that represents the width value of this Box. void SetWidth(float width) { m_Width = width; } - /// /// Gets the height of this box. Note that this can be negative if the box hasn't been righted with Unflip(). - /// - /// A float value that represents the height value of this Box. + /// @return A float value that represents the height value of this Box. float GetHeight() const { return m_Height; } - /// /// Sets the height of this box. Note that this can be negative if the box hasn't been righted with Unflip(). - /// - /// A float value that represents the height value of this Box. + /// @param height A float value that represents the height value of this Box. void SetHeight(float height) { m_Height = height; } - /// /// Gets the width times the height. - /// - /// The width times the height. + /// @return The width times the height. float GetArea() const { return m_Width * m_Height; } - /// /// Gets a random point within this box. - /// - /// The random point within the box. + /// @return The random point within the box. Vector GetRandomPoint() const { return Vector(m_Corner.m_X + RandomNum(0.0F, m_Width), m_Corner.m_Y + RandomNum(0.0F, m_Height)); } #pragma endregion #pragma region Detection - /// /// Tells whether another box intersects this one. - /// - /// The other Box to check for intersection with. - /// Intersecting the other box or not. + /// @param rhs The other Box to check for intersection with. + /// @return Intersecting the other box or not. bool IntersectsBox(const Box& rhs); - /// /// Tells whether a point is within the Box or not, taking potential flipping into account. - /// - /// The Vector describing the point to test for within box bounds. - /// Inside the box or not. False if the box IsEmpty() + /// @param point The Vector describing the point to test for within box bounds. + /// @return Inside the box or not. False if the box IsEmpty() bool IsWithinBox(const Vector& point) const; - /// /// Tells whether an X coordinate is within the Box's X-range or not, taking potential flipping into account. - /// - /// The coordinate describing the X value to test for within box bounds. - /// Inside the box or not in the X axis. False if the box IsEmpty() + /// @param pointX The coordinate describing the X value to test for within box bounds. + /// @return Inside the box or not in the X axis. False if the box IsEmpty() bool IsWithinBoxX(float pointX) const; - /// /// Tells whether an Y coordinate is within the Box's Y-range or not, taking potential flipping into account. - /// - /// The coordinate describing the Y value to test for within box bounds. - /// Inside the box or not in the Y axis. False if the box IsEmpty() + /// @param pointY The coordinate describing the Y value to test for within box bounds. + /// @return Inside the box or not in the Y axis. False if the box IsEmpty() bool IsWithinBoxY(float pointY) const; - /// /// Returns a copy of a point constrained inside this box. - /// - /// The Vector describing the point to constrain inside the box. - /// The resulting point inside the box. + /// @param point The Vector describing the point to constrain inside the box. + /// @return The resulting point inside the box. Vector GetWithinBox(const Vector& point) const { return Vector(GetWithinBoxX(point.m_X), GetWithinBoxY(point.m_Y)); } - /// /// Returns an X value constrained inside the Box and returns it. - /// - /// The X value to constrain inside the Box. - /// The constrained value. + /// @param pointX The X value to constrain inside the Box. + /// @return The constrained value. float GetWithinBoxX(float pointX) const; - /// /// Returns an Y value constrained inside the Box and returns it. - /// - /// The Y value to constrain inside the Box. - /// The constrained value. + /// @param pointY The Y value to constrain inside the Box. + /// @return The constrained value. float GetWithinBoxY(float pointY) const; #pragma endregion #pragma region Operator Overloads - /// /// An assignment operator for setting one Box equal to another. - /// - /// A Box reference. - /// A reference to the changed Box. + /// @param rhs A Box reference. + /// @return A reference to the changed Box. Box& operator=(const Box& rhs) { if (*this != rhs) { Create(rhs); @@ -273,29 +203,23 @@ namespace RTE { return *this; } - /// /// An equality operator for testing if any two Boxes are equal. - /// - /// A Box reference as the left hand side operand. - /// A Box reference as the right hand side operand. - /// A boolean indicating whether the two operands are equal or not. + /// @param lhs A Box reference as the left hand side operand. + /// @param rhs A Box reference as the right hand side operand. + /// @return A boolean indicating whether the two operands are equal or not. friend bool operator==(const Box& lhs, const Box& rhs) { return lhs.m_Corner == rhs.m_Corner && lhs.m_Width == rhs.m_Width && lhs.m_Height == rhs.m_Height; } - /// /// An inequality operator for testing if any two Boxes are unequal. - /// - /// A Box reference as the left hand side operand. - /// A Box reference as the right hand side operand. - /// A boolean indicating whether the two operands are unequal or not. + /// @param lhs A Box reference as the left hand side operand. + /// @param rhs A Box reference as the right hand side operand. + /// @return A boolean indicating whether the two operands are unequal or not. friend bool operator!=(const Box& lhs, const Box& rhs) { return lhs.m_Corner != rhs.m_Corner || lhs.m_Width != rhs.m_Width || lhs.m_Height != rhs.m_Height; } #pragma endregion private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. - /// /// Clears all the member variables of this Box, effectively resetting the members of this abstraction level only. - /// void Clear() { m_Corner.Reset(); m_Width = m_Height = 0; diff --git a/Source/System/Color.cpp b/Source/System/Color.cpp index bbc334b52a..86a08c73ee 100644 --- a/Source/System/Color.cpp +++ b/Source/System/Color.cpp @@ -5,8 +5,6 @@ namespace RTE { const std::string Color::c_ClassName = "Color"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Color::Create() { if (Serializable::Create()) { return -1; @@ -15,8 +13,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Color::Create(int inputR, int inputG, int inputB) { SetR(inputR); SetG(inputG); @@ -25,8 +21,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Color::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -38,8 +32,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Color::Save(Writer& writer) const { Serializable::Save(writer); @@ -50,8 +42,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Color::SetRGBWithIndex(int index) { m_Index = std::clamp(index, 0, 255); @@ -64,8 +54,6 @@ namespace RTE { m_B = rgbColor.b * 4; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Color::RecalculateIndex() { return m_Index = makecol8(m_R, m_G, m_B); } diff --git a/Source/System/Color.h b/Source/System/Color.h index c1e8bd7e45..02c06365f7 100644 --- a/Source/System/Color.h +++ b/Source/System/Color.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// A class representing a RGB color value. - /// class Color : public Serializable { public: @@ -15,127 +13,95 @@ namespace RTE { SerializableOverrideMethods; #pragma region Creation - /// /// Constructor method used to instantiate a Color object. - /// Color() { Clear(); } - /// /// Constructor method used to instantiate a Color object from RGB values. - /// - /// Initial Red value of this color. - /// Initial Green value of this color. - /// Initial Blue value of this color. + /// @param R Initial Red value of this color. + /// @param G Initial Green value of this color. + /// @param B Initial Blue value of this color. Color(int R, int G, int B) { Clear(); Create(R, G, B); } - /// /// Constructor method used to instantiate a Color object from an entry in the current color palette. - /// - /// Palette index entry to create this color from. + /// @param index Palette index entry to create this color from. Color(int index) { Clear(); SetRGBWithIndex(index); } - /// /// Copy constructor method used to instantiate a Color object identical to an already existing one. - /// - /// A Color object which is passed in by reference. + /// @param reference A Color object which is passed in by reference. Color(const Color& reference) { Clear(); Create(reference.m_R, reference.m_G, reference.m_B); } - /// /// Makes the Color object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Makes the Color object ready for use. - /// - /// Initial Red value of this color. - /// Initial Green value of this color. - /// Initial Blue value of this color. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param R Initial Red value of this color. + /// @param G Initial Green value of this color. + /// @param B Initial Blue value of this color. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(int inputR, int inputG, int inputB); #pragma endregion #pragma region Destruction - /// /// Sets RGB of this Color to zero. - /// void Reset() override { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Gets the entry in the current color palette that most closely matches this Color's RGB values. - /// - /// The color entry index number. + /// @return The color entry index number. int GetIndex() const { return m_Index; } - /// /// Sets all three RGB values of this Color, using an index from the current color palette. - /// - /// The index of the palette entry that this Color object's RGB values should be set to. + /// @param index The index of the palette entry that this Color object's RGB values should be set to. void SetRGBWithIndex(int index); - /// /// Gets the red value of this Color. - /// - /// An integer value that represents the R value of this Color. 0 - 255. + /// @return An integer value that represents the R value of this Color. 0 - 255. int GetR() const { return m_R; } - /// /// Sets the red value of this Color. - /// - /// An integer value that the R value will be set to, between 0 and 255. + /// @param newR An integer value that the R value will be set to, between 0 and 255. void SetR(int newR) { m_R = std::clamp(newR, 0, 255); m_Index = 0; } - /// /// Gets the green value of this Color. - /// - /// An integer value that represents the green value of this Color. 0 - 255. + /// @return An integer value that represents the green value of this Color. 0 - 255. int GetG() const { return m_G; } - /// /// Sets the green value of this Color. - /// - /// An integer value that the green value will be set to, between 0 and 255. + /// @param newG An integer value that the green value will be set to, between 0 and 255. void SetG(int newG) { m_G = std::clamp(newG, 0, 255); m_Index = 0; } - /// /// Gets the blue value of this Color. - /// - /// An integer value that represents the blue value of this Color. 0 - 255. + /// @return An integer value that represents the blue value of this Color. 0 - 255. int GetB() const { return m_B; } - /// /// Sets the blue value of this Color. - /// - /// An integer value that the blue value will be set to, between 0 and 255. + /// @param newB An integer value that the blue value will be set to, between 0 and 255. void SetB(int newB) { m_B = std::clamp(newB, 0, 255); m_Index = 0; } - /// /// Sets all three RGB values of this Color. - /// - /// Integer value that the Red value will be set to, between 0 and 255. - /// Integer value that the Green value will be set to, between 0 and 255. - /// Integer value that the Blue value will be set to, between 0 and 255. + /// @param newR Integer value that the Red value will be set to, between 0 and 255. + /// @param newG Integer value that the Green value will be set to, between 0 and 255. + /// @param newB Integer value that the Blue value will be set to, between 0 and 255. void SetRGB(int newR, int newG, int newB) { SetR(newR); SetG(newG); @@ -145,10 +111,8 @@ namespace RTE { #pragma endregion #pragma region Concrete Methods - /// /// Causes recalculation of the nearest index even though there might be one cached or not. - /// - /// The new color entry index number. + /// @return The new color entry index number. int RecalculateIndex(); #pragma endregion @@ -161,9 +125,7 @@ namespace RTE { private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. - /// /// Clears all the member variables of this Color, effectively resetting the members of this abstraction level only. - /// void Clear() { m_R = m_G = m_B = m_Index = 0; } }; } // namespace RTE diff --git a/Source/System/Constants.h b/Source/System/Constants.h index cc91d106a5..288f640634 100644 --- a/Source/System/Constants.h +++ b/Source/System/Constants.h @@ -181,9 +181,7 @@ namespace RTE { #pragma endregion #pragma region Input Constants - /// /// Enumeration for different types of input devices. - /// enum InputDevice { DEVICE_KEYB_ONLY = 0, DEVICE_MOUSE_KEYB, @@ -194,9 +192,7 @@ namespace RTE { DEVICE_COUNT }; - /// /// Enumeration for different elements the input scheme is composed of. - /// enum InputElements { INPUT_L_UP = 0, INPUT_L_DOWN, @@ -258,9 +254,7 @@ namespace RTE { "Analog Aim Right" // INPUT_R_RIGHT }; - /// /// Enumeration for mouse button types. - /// enum MouseButtons { MOUSE_NONE = -1, MOUSE_LEFT = 1, @@ -269,9 +263,7 @@ namespace RTE { MAX_MOUSE_BUTTONS }; - /// /// Enumeration for joystick button types. - /// enum JoyButtons { JOY_NONE = -1, JOY_1 = 0, @@ -298,19 +290,15 @@ namespace RTE { MAX_JOY_BUTTONS }; - /// /// Enumeration for joystick direction types. - /// enum JoyDirections { JOYDIR_ONE = 0, JOYDIR_TWO }; - /// /// Enumeration for joystick dead zone types. /// Square deadzone cuts-off any input from every axis separately. For example if x-axis has less than 20% input and y-axis has more, x-axis input is ignored. /// Circle uses a round zone to capture stick position on both axis then cut-off if this position is inside the round dead zone. - /// enum DeadZoneType { CIRCLE = 0, SQUARE = 1 @@ -318,9 +306,7 @@ namespace RTE { #pragma endregion #pragma region Global Enumerations - /// /// Enumeration all available players. - /// enum Players { NoPlayer = -1, PlayerOne = 0, @@ -330,9 +316,7 @@ namespace RTE { MaxPlayerCount }; - /// /// Enumeration and supporting maps for cardinal directions, as well as None and Any. - /// enum Directions { None = -1, Up, diff --git a/Source/System/ContentFile.cpp b/Source/System/ContentFile.cpp index 24de53ba50..c75ce31552 100644 --- a/Source/System/ContentFile.cpp +++ b/Source/System/ContentFile.cpp @@ -17,8 +17,6 @@ namespace RTE { std::unordered_map ContentFile::s_LoadedSamples; std::unordered_map ContentFile::s_PathHashes; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::Clear() { m_DataPath.clear(); m_DataPathExtension.clear(); @@ -31,16 +29,12 @@ namespace RTE { m_ImageFileInfo.fill(-1); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::Create(const char* filePath) { SetDataPath(filePath); return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::Create(const ContentFile& reference) { m_DataPath = reference.m_DataPath; m_DataPathExtension = reference.m_DataPathExtension; @@ -50,8 +44,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::FreeAllLoaded() { for (int depth = BitDepths::Eight; depth < BitDepths::BitDepthCount; ++depth) { for (const auto& [bitmapPath, bitmapPtr]: s_LoadedBitmaps[depth]) { @@ -60,8 +52,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -70,8 +60,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::Save(Writer& writer) const { Serializable::Save(writer); @@ -82,14 +70,10 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::GetDataModuleID() const { return (m_DataModuleID < 0) ? g_PresetMan.GetModuleIDFromPath(m_DataPath) : m_DataModuleID; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::SetDataPath(const std::string& newDataPath) { m_DataPath = g_PresetMan.GetFullModulePath(newDataPath); m_DataPathExtension = std::filesystem::path(m_DataPath).extension().string(); @@ -103,21 +87,15 @@ namespace RTE { m_DataModuleID = g_PresetMan.GetModuleIDFromPath(m_DataPath); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - size_t ContentFile::GetHash() const { return Hash(m_DataPath); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::SetFormattedReaderPosition(const std::string& newPosition) { m_FormattedReaderPosition = newPosition; m_DataPathAndReaderPosition = m_DataPath + "\n" + newPosition; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ContentFile::GetImageFileInfo(ImageFileInfoType infoTypeToGet) { bool fetchFileInfo = false; for (const int& fileInfoEntry: m_ImageFileInfo) { @@ -142,8 +120,6 @@ namespace RTE { return m_ImageFileInfo[infoTypeToGet]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::ReadAndStorePNGFileInfo(FILE* imageFile) { std::array fileSignature = {}; @@ -168,8 +144,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::ReadAndStoreBMPFileInfo(FILE* imageFile) { std::array bmpSignature = {0x42, 0x4D}; // { 'B', 'M' }. std::array fileSignature = {}; @@ -203,8 +177,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::ReloadAllBitmaps() { for (const std::unordered_map& bitmapCache: s_LoadedBitmaps) { for (const auto& [filePath, oldBitmap]: bitmapCache) { @@ -214,8 +186,6 @@ namespace RTE { g_ConsoleMan.PrintString("SYSTEM: Sprites reloaded"); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* ContentFile::GetAsBitmap(int conversionMode, bool storeBitmap, const std::string& dataPathToSpecificFrame) { if (m_DataPath.empty()) { return nullptr; @@ -255,8 +225,6 @@ namespace RTE { return returnBitmap; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::GetAsAnimation(std::vector& vectorToFill, int frameCount, int conversionMode) { if (m_DataPath.empty() || frameCount < 1) { return; @@ -285,8 +253,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BITMAP* ContentFile::LoadAndReleaseBitmap(int conversionMode, const std::string& dataPathToSpecificFrame) { if (m_DataPath.empty()) { return nullptr; @@ -306,8 +272,6 @@ namespace RTE { return returnBitmap; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD::Sound* ContentFile::GetAsSound(bool abortGameForInvalidSound, bool asyncLoading) { if (m_DataPath.empty() || !g_AudioMan.IsAudioEnabled()) { return nullptr; @@ -326,8 +290,6 @@ namespace RTE { return returnSample; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FMOD::Sound* ContentFile::LoadAndReleaseSound(bool abortGameForInvalidSound, bool asyncLoading) { if (m_DataPath.empty() || !g_AudioMan.IsAudioEnabled()) { return nullptr; @@ -370,8 +332,6 @@ namespace RTE { return returnSample; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::ReloadBitmap(const std::string& filePath, int conversionMode) { const int bitDepth = (conversionMode == COLORCONV_8_TO_32) ? BitDepths::ThirtyTwo : BitDepths::Eight; @@ -396,8 +356,6 @@ namespace RTE { destroy_bitmap(newBitmap); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ContentFile::AddAlphaChannel(BITMAP* bitmap) { if (!bitmap) { return; diff --git a/Source/System/ContentFile.h b/Source/System/ContentFile.h index 89331690de..4b28c38a64 100644 --- a/Source/System/ContentFile.h +++ b/Source/System/ContentFile.h @@ -10,9 +10,7 @@ struct BITMAP; namespace RTE { - /// /// A representation of a content file that is stored directly on disk. - /// class ContentFile : public Serializable { public: @@ -20,193 +18,141 @@ namespace RTE { SerializableOverrideMethods; #pragma region Creation - /// /// Constructor method used to instantiate a ContentFile object in system memory. Create() should be called before using the object. - /// ContentFile() { Clear(); } - /// /// Constructor method used to instantiate a ContentFile object in system memory, and also do a Create() in the same line. - /// - /// A string defining the path to where the content file itself is located. + /// @param filePath A string defining the path to where the content file itself is located. explicit ContentFile(const char* filePath) { Clear(); Create(filePath); } - /// /// Constructor method used to instantiate a ContentFile object in system memory from a hash value of the file path, and also do a Create() in the same line. - /// - /// A hash value containing the path to where the content file itself is located. + /// @param hash A hash value containing the path to where the content file itself is located. explicit ContentFile(size_t hash) { Clear(); Create(GetPathFromHash(hash).c_str()); } - /// /// Makes the ContentFile object ready for use. - /// - /// A string defining the path to where the content file itself is located. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param filePath A string defining the path to where the content file itself is located. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const char* filePath); - /// /// Creates a ContentFile to be identical to another, by deep copy. - /// - /// A reference to the ContentFile to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the ContentFile to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const ContentFile& reference); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a ContentFile object before deletion from system memory. - /// ~ContentFile() override = default; - /// /// Resets the entire ContentFile, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); } - /// /// Frees all loaded data used by all ContentFile instances. This should ONLY be done when quitting the app, or after everything else is completely destroyed. - /// static void FreeAllLoaded(); #pragma endregion #pragma region Getters and Setters - /// /// Gets the ID of the Data Module this file is inside. - /// - /// The ID of the DataModule containing this' file. + /// @return The ID of the DataModule containing this' file. int GetDataModuleID() const; - /// /// Gets the file path of the content file represented by this ContentFile object. - /// - /// A string with the file name path. + /// @return A string with the file name path. const std::string& GetDataPath() const { return m_DataPath; } - /// /// Sets the file path of the content file represented by this ContentFile object. - /// - /// A string with the new file name path. + /// @param newDataPath A string with the new file name path. void SetDataPath(const std::string& newDataPath); - /// /// Creates a hash value out of a path to a ContentFile. - /// - /// Hash value of a path to a ContentFile. + /// @return Hash value of a path to a ContentFile. size_t GetHash() const; - /// /// Converts hash values into file paths to ContentFiles. - /// - /// Hash value to get file path from. - /// Path to ContentFile. + /// @param hash Hash value to get file path from. + /// @return Path to ContentFile. static std::string GetPathFromHash(size_t hash) { return (s_PathHashes.find(hash) == s_PathHashes.end()) ? "" : s_PathHashes[hash]; } #pragma endregion #pragma region Logging - /// /// Gets the file and line that are currently being read. Formatted to be used for logging warnings and errors. - /// - /// A string containing the currently read file path and the line being read. + /// @return A string containing the currently read file path and the line being read. const std::string& GetFormattedReaderPosition() const { return m_FormattedReaderPosition; } - /// /// Sets the file and line that are currently being read. Formatted to be used for logging warnings and errors. - /// - /// A string containing the currently read file path and the line being read. + /// @param newPosition A string containing the currently read file path and the line being read. void SetFormattedReaderPosition(const std::string& newPosition) override; #pragma endregion #pragma region Image Info Getters - /// /// Gets whether the data file at this ContentFile's path is a supported image format. - /// - /// Whether the data file at this ContentFile's path is a supported image format. + /// @return Whether the data file at this ContentFile's path is a supported image format. bool DataPathIsImageFile() const { return m_DataPathIsImageFile; } - /// /// Gets the bit depth of the image file at this ContentFile's path. - /// - /// The bit depth of the image file at this ContentFile's path, or -1 if the file is not an image format. + /// @return The bit depth of the image file at this ContentFile's path, or -1 if the file is not an image format. int GetImageBitDepth() { return m_DataPathIsImageFile ? GetImageFileInfo(ImageFileInfoType::ImageBitDepth) : -1; } - /// /// Gets the width of the image file at this ContentFile's path. - /// - /// The width of the image file at this ContentFile's path, in pixels, or -1 if the file is not an image format. + /// @return The width of the image file at this ContentFile's path, in pixels, or -1 if the file is not an image format. int GetImageWidth() { return m_DataPathIsImageFile ? GetImageFileInfo(ImageFileInfoType::ImageWidth) : -1; } - /// /// Gets the height of the image file at this ContentFile's path. - /// - /// The height of the image file at this ContentFile's path, in pixels, or -1 if the file is not an image format. + /// @return The height of the image file at this ContentFile's path, in pixels, or -1 if the file is not an image format. int GetImageHeight() { return m_DataPathIsImageFile ? GetImageFileInfo(ImageFileInfoType::ImageHeight) : -1; } #pragma endregion #pragma region Data Handling - /// /// Reloads all BITMAPs in the cache from disk, allowing any changes to be reflected at runtime. - /// static void ReloadAllBitmaps(); - /// /// Gets the data represented by this ContentFile object as an Allegro BITMAP, loading it into the static maps if it's not already loaded. Note that ownership of the BITMAP is NOT transferred! - /// - /// The Allegro color conversion mode to use when loading this bitmap. - /// Whether to store the BITMAP in the relevant static map after loading it or not. If this is false, ownership of the BITMAP IS transferred! - /// Path to a specific frame when loading an animation to avoid overwriting the original preset DataPath when loading each frame. - /// Pointer to the BITMAP loaded from disk. + /// @param conversionMode The Allegro color conversion mode to use when loading this bitmap. + /// @param storeBitmap Whether to store the BITMAP in the relevant static map after loading it or not. If this is false, ownership of the BITMAP IS transferred! + /// @param dataPathToSpecificFrame Path to a specific frame when loading an animation to avoid overwriting the original preset DataPath when loading each frame. + /// @return Pointer to the BITMAP loaded from disk. BITMAP* GetAsBitmap(int conversionMode = 0, bool storeBitmap = true, const std::string& dataPathToSpecificFrame = ""); - /// /// Fills an existing vector of Allegro BITMAPs representing each frame in the animation with the data represented by this ContentFile object. /// It loads the BITMAPs into the static maps if they're not already loaded. Ownership of the BITMAPs is NOT transferred! - /// - /// The existing vector of Allegro BITMAPs to fill. - /// The number of frames to attempt to load, more than 1 frame will mean 00# is appended to DataPath to handle naming conventions. - /// The Allegro color conversion mode to use when loading this bitmap. + /// @param vectorToFill The existing vector of Allegro BITMAPs to fill. + /// @param frameCount The number of frames to attempt to load, more than 1 frame will mean 00# is appended to DataPath to handle naming conventions. + /// @param conversionMode The Allegro color conversion mode to use when loading this bitmap. void GetAsAnimation(std::vector& vectorToFill, int frameCount = 1, int conversionMode = 0); - /// /// Gets the data represented by this ContentFile object as a vector of Allegro BITMAPs, each representing a frame in the animation. /// It loads the BITMAPs into the static maps if they're not already loaded. Ownership of the BITMAPs is NOT transferred! - /// - /// The number of frames to attempt to load, more than 1 frame will mean 00# is appended to DataPath to handle naming conventions. - /// The Allegro color conversion mode to use when loading this bitmap. - /// A vector of BITMAP pointers loaded from the disk. + /// @param frameCount The number of frames to attempt to load, more than 1 frame will mean 00# is appended to DataPath to handle naming conventions. + /// @param conversionMode The Allegro color conversion mode to use when loading this bitmap. + /// @return A vector of BITMAP pointers loaded from the disk. std::vector GetAsAnimation(int frameCount = 1, int conversionMode = 0) { std::vector returnBitmaps; GetAsAnimation(returnBitmaps, frameCount, conversionMode); return returnBitmaps; } - /// /// Gets the data represented by this ContentFile object as an FMOD FSOUND_SAMPLE, loading it into the static maps if it's not already loaded. Ownership of the FSOUND_SAMPLE is NOT transferred! - /// - /// Whether to abort the game if the sound couldn't be added, or just show a console error. Default true. - /// Whether to enable FMOD asynchronous loading or not. Should be disabled for loading audio files with Lua AddSound. - /// Pointer to the FSOUND_SAMPLE loaded from disk. + /// @param abortGameForInvalidSound Whether to abort the game if the sound couldn't be added, or just show a console error. Default true. + /// @param asyncLoading Whether to enable FMOD asynchronous loading or not. Should be disabled for loading audio files with Lua AddSound. + /// @return Pointer to the FSOUND_SAMPLE loaded from disk. FMOD::Sound* GetAsSound(bool abortGameForInvalidSound = true, bool asyncLoading = true); #pragma endregion private: - /// /// Enumeration for loading BITMAPs by bit depth. NOTE: This can't be lower down because s_LoadedBitmaps relies on this definition. - /// enum BitDepths { Eight = 0, ThirtyTwo, BitDepthCount }; - /// /// Enumeration for the image file information types that can be stored. - /// enum ImageFileInfoType { ImageBitDepth, ImageWidth, @@ -233,60 +179,44 @@ namespace RTE { int m_DataModuleID; //!< Data Module ID of where this was loaded from. #pragma region Image Info Getters - /// /// Gets the specified image info from this ContentFile's data file on disk. - /// - /// The image info type to get. See ImageFileInfoType enumeration. - /// An integer value with the requested image info. + /// @param infoTypeToGet The image info type to get. See ImageFileInfoType enumeration. + /// @return An integer value with the requested image info. int GetImageFileInfo(ImageFileInfoType infoTypeToGet); - /// /// Reads a PNG file from disk and stores the relevant information without actually loading the whole file into memory. - /// - /// Pointer to the file stream to read. + /// @param imageFile Pointer to the file stream to read. void ReadAndStorePNGFileInfo(FILE* imageFile); - /// /// Reads a BMP file from disk and stores the relevant information without actually loading the whole file into memory. - /// - /// Pointer to the file stream to read. + /// @param imageFile Pointer to the file stream to read. void ReadAndStoreBMPFileInfo(FILE* imageFile); #pragma endregion #pragma region Data Handling - /// /// Loads and transfers the data represented by this ContentFile object as an Allegro BITMAP. Ownership of the BITMAP IS transferred! /// Note that this is relatively slow since it reads the data from disk each time. - /// - /// The Allegro color conversion mode to use when loading this bitmap. Only applies the first time a bitmap is loaded from the disk. - /// Path to a specific frame when loading an animation to avoid overwriting the original preset DataPath when loading each frame. - /// Pointer to the BITMAP loaded from disk. + /// @param conversionMode The Allegro color conversion mode to use when loading this bitmap. Only applies the first time a bitmap is loaded from the disk. + /// @param dataPathToSpecificFrame Path to a specific frame when loading an animation to avoid overwriting the original preset DataPath when loading each frame. + /// @return Pointer to the BITMAP loaded from disk. BITMAP* LoadAndReleaseBitmap(int conversionMode = 0, const std::string& dataPathToSpecificFrame = ""); - /// /// Loads and transfers the data represented by this ContentFile object as an FMOD FSOUND_SAMPLE. Ownership of the FSOUND_SAMPLE is NOT transferred! - /// - /// Whether to abort the game if the sound couldn't be added, or just show a console error. Default true. - /// Whether to enable FMOD asynchronous loading or not. Should be disabled for loading audio files with Lua AddSound. - /// Pointer to the FSOUND_SAMPLE loaded from disk. + /// @param abortGameForInvalidSound Whether to abort the game if the sound couldn't be added, or just show a console error. Default true. + /// @param asyncLoading Whether to enable FMOD asynchronous loading or not. Should be disabled for loading audio files with Lua AddSound. + /// @return Pointer to the FSOUND_SAMPLE loaded from disk. FMOD::Sound* LoadAndReleaseSound(bool abortGameForInvalidSound = true, bool asyncLoading = true); - /// /// Reloads a specific BITMAP in the cache from disk, allowing any changes to be reflected at runtime. - /// - /// The filepath to the bitmap we want to reload. - /// The Allegro color conversion mode to use when reloading this bitmap. + /// @param filePath The filepath to the bitmap we want to reload. + /// @param conversionMode The Allegro color conversion mode to use when reloading this bitmap. static void ReloadBitmap(const std::string& filePath, int conversionMode = 0); - /// /// Set alpha value of non mask color pixels to 255 for 32-bit bitmaps. (WARN: would override existing alpha values!) - /// static void AddAlphaChannel(BITMAP* bitmap); #pragma endregion - /// /// Clears all the member variables of this ContentFile, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/System/Controller.cpp b/Source/System/Controller.cpp index fad67c4503..22b02c0d2a 100644 --- a/Source/System/Controller.cpp +++ b/Source/System/Controller.cpp @@ -7,8 +7,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::Clear() { m_ControlStates.fill(false); m_AnalogMove.Reset(); @@ -34,8 +32,6 @@ namespace RTE { m_KeyAccelTimer.Reset(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Controller::Create(InputMode mode, Actor* controlledActor) { m_InputMode = mode; m_ControlledActor = controlledActor; @@ -46,8 +42,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Controller::Create(const Controller& reference) { for (int i = 0; i < ControlState::CONTROLSTATECOUNT; ++i) { m_ControlStates[i] = reference.m_ControlStates[i]; @@ -72,8 +66,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Controller::RelativeCursorMovement(Vector& cursorPos, float moveScale) const { bool altered = false; @@ -114,26 +106,18 @@ namespace RTE { return altered; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Controller::GetDigitalAimSpeed() const { return m_Player != Players::NoPlayer ? g_UInputMan.GetControlScheme(m_Player)->GetDigitalAimSpeed() : 1.0F; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Controller::IsMouseControlled() const { return m_Player != Players::NoPlayer && g_UInputMan.GetControlScheme(m_Player)->GetDevice() == InputDevice::DEVICE_MOUSE_KEYB; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Controller::IsKeyboardOnlyControlled() const { return m_Player != Players::NoPlayer && g_UInputMan.GetControlScheme(m_Player)->GetDevice() == InputDevice::DEVICE_KEYB_ONLY; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Controller::IsGamepadControlled() const { bool isGamepadControlled = false; if (m_Player != Players::NoPlayer) { @@ -145,14 +129,10 @@ namespace RTE { return isGamepadControlled; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Controller::GetTeam() const { return m_ControlledActor ? m_ControlledActor->GetTeam() : m_Team; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::SetTeam(short team) { if (m_ControlledActor) { m_ControlledActor->SetTeam(team); @@ -160,8 +140,6 @@ namespace RTE { m_Team = team; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::Update() { if (IsDisabled()) { return; @@ -191,8 +169,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::ResetCommandState() { // Reset all command states. m_ControlStates.fill(false); @@ -202,8 +178,6 @@ namespace RTE { m_MouseMovement.Reset(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::GetInputFromPlayer() { ResetCommandState(); @@ -214,8 +188,6 @@ namespace RTE { UpdatePlayerInput(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Controller::ShouldUpdateAIThisFrame() const { if (IsDisabled() || m_InputMode != InputMode::CIM_AI) { return false; @@ -231,8 +203,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Controller& Controller::operator=(const Controller& rhs) { if (this == &rhs) { return *this; @@ -242,15 +212,11 @@ namespace RTE { return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::Override(const Controller& otherController) { RTEAssert(otherController.m_ControlledActor == m_ControlledActor, "Overriding a controller with a mismatched controlled actor!"); *this = otherController; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::UpdatePlayerInput() { UpdatePlayerPieMenuInput(); @@ -304,8 +270,6 @@ namespace RTE { UpdatePlayerAnalogInput(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::UpdatePlayerPieMenuInput() { // Holding of the switch buttons disables aiming later if (g_UInputMan.ElementHeld(m_Player, InputElements::INPUT_NEXT)) { @@ -403,8 +367,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Controller::UpdatePlayerAnalogInput() { // ANALOG joystick values Vector move = g_UInputMan.AnalogMoveValues(m_Player); diff --git a/Source/System/Controller.h b/Source/System/Controller.h index 3de2fcab70..596f3892d4 100644 --- a/Source/System/Controller.h +++ b/Source/System/Controller.h @@ -65,9 +65,7 @@ namespace RTE { CONTROLSTATECOUNT }; - /// /// A class controlling MovableObjects through either player input, networking, scripting, AI, etc. - /// class Controller { public: @@ -85,94 +83,72 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a Controller object in system memory. Create() should be called before using the object. - /// Controller() { Clear(); } - /// /// Constructor method used to instantiate a Controller object in system memory. Create() should be called before using the object. - /// - /// The controller input mode, like AI, player etc. - /// The Actor this is supposed to control. Ownership is NOT transferred! + /// @param mode The controller input mode, like AI, player etc. + /// @param controlledActor The Actor this is supposed to control. Ownership is NOT transferred! Controller(InputMode mode, Actor* controlledActor) { Clear(); Create(mode, controlledActor); } - /// /// Constructor method used to instantiate a Controller object in system memory. Create() should be called before using the object. - /// - /// The controller input mode, like AI, player etc. - /// Which human player is controlling this. + /// @param mode The controller input mode, like AI, player etc. + /// @param player Which human player is controlling this. Controller(InputMode mode, int player = Players::PlayerOne) { Clear(); Create(mode, player); } - /// /// Copy constructor method used to instantiate a Controller object identical to an already existing one. - /// - /// A Controller object which is passed in by reference. + /// @param reference A Controller object which is passed in by reference. Controller(const Controller& reference) { if (this != &reference) { Create(reference); } } - /// /// Makes the Controller object ready for use. - /// - /// The controller input mode, like AI, player etc. - /// The Actor this is supposed to control. Ownership is NOT transferred! - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param mode The controller input mode, like AI, player etc. + /// @param controlledActor The Actor this is supposed to control. Ownership is NOT transferred! + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(InputMode mode, Actor* controlledActor); - /// /// Makes the Controller object ready for use. - /// - /// The controller input mode, like AI, player etc. - /// Which player is controlling this. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param mode The controller input mode, like AI, player etc. + /// @param player Which player is controlling this. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(InputMode mode, int player) { m_InputMode = mode; m_Player = player; return 0; } - /// /// Creates a Controller to be identical to another, by deep copy. - /// - /// A reference to the Controller to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Controller to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Controller& reference); #pragma endregion #pragma region Destruction - /// /// Resets the entire Controller, including its inherited members, to their default settings or values. - /// void Reset() { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Shortcut to indicate if in player input mode. - /// - /// If you want to check if it's controlled by a player, AND that player is someone else than a specific one, pass in that player number here. - /// Whether input mode is set to player input. + /// @param otherThanPlayer If you want to check if it's controlled by a player, AND that player is someone else than a specific one, pass in that player number here. + /// @return Whether input mode is set to player input. bool IsPlayerControlled(int otherThanPlayer = Players::NoPlayer) const { return (m_InputMode == InputMode::CIM_PLAYER && (otherThanPlayer < Players::PlayerOne || m_Player != otherThanPlayer)); } - /// /// Shows whether this controller is disabled. - /// - /// Whether disabled. + /// @return Whether disabled. bool IsDisabled() const { return m_InputMode == InputMode::CIM_DISABLED || m_Disabled; } - /// /// Sets whether this is a disabled controller that doesn't give any new output. - /// - /// Disabled or not. + /// @param disabled Disabled or not. void SetDisabled(bool disabled = true) { if (m_Disabled != disabled) { m_ReleaseTimer.Reset(); @@ -181,33 +157,25 @@ namespace RTE { m_Disabled = disabled; } - /// /// Shows whether the current controller is in a specific state. - /// - /// What control state to check for. - /// Whether the controller is in the specified state. + /// @param controlState What control state to check for. + /// @return Whether the controller is in the specified state. bool IsState(ControlState controlState) const { return m_ControlStates[controlState]; }; - /// /// Sets one of this controller's states. - /// - /// Value of the state being set. + /// @param controlStat Which state to set. + /// @param setting Value of the state being set. void SetState(ControlState controlState, bool setting = true) { RTEAssert(controlState >= 0 && controlState < ControlState::CONTROLSTATECOUNT, "Control state out of whack"); m_ControlStates[controlState] = setting; }; - /// /// Gets the current mode of input for this Controller. - /// - /// The InputMode that this controller is currently using. + /// @return The InputMode that this controller is currently using. InputMode GetInputMode() const { return m_InputMode; } - /// /// Sets the mode of input for this Controller. - /// - /// The new InputMode for this controller to use. + /// @param newMode The new InputMode for this controller to use. void SetInputMode(InputMode newMode) { if (m_InputMode != newMode) { m_ReleaseTimer.Reset(); @@ -215,108 +183,74 @@ namespace RTE { m_InputMode = newMode; } - /// /// Gets the analog movement input data. - /// - /// A vector with the analog movement data, both axes ranging form -1.0 to 1.0. + /// @return A vector with the analog movement data, both axes ranging form -1.0 to 1.0. Vector GetAnalogMove() const { return m_AnalogMove; } - /// /// Sets the analog movement vector state of this. - /// - /// The new analog movement vector. + /// @param newMove The new analog movement vector. void SetAnalogMove(const Vector& newMove) { m_AnalogMove = newMove; } - /// /// Gets the analog aiming input data. - /// - /// A vector with the analog aiming data, both axes ranging form -1.0 to 1.0. + /// @return A vector with the analog aiming data, both axes ranging form -1.0 to 1.0. Vector GetAnalogAim() const { return m_AnalogAim; } - /// /// Sets the analog aiming vector state of this. - /// - /// The new analog aiming vector. + /// @param newAim The new analog aiming vector. void SetAnalogAim(const Vector& newAim) { m_AnalogAim = newAim; } - /// /// Gets the analog menu input data. - /// - /// A vector with the analog menu data, both axes ranging form -1.0 to 1.0. + /// @return A vector with the analog menu data, both axes ranging form -1.0 to 1.0. Vector GetAnalogCursor() const { return m_AnalogCursor; } - /// /// Sets the analog cursor to the specified position. - /// - /// The position the analog cursor should be set to. + /// @param newAnalogCursor The position the analog cursor should be set to. void SetAnalogCursor(const Vector& newAnalogCursor) { m_AnalogCursor = newAnalogCursor; } - /// /// Sets the analog cursor angle limits for the given player (does nothing for player -1). The limit end is always CCW from the limit start. - /// - /// The starting angle limit for the analog cursor. - /// The ending angle limit for the analog cursor. + /// @param angleLimitStart The starting angle limit for the analog cursor. + /// @param angleLimitEnd The ending angle limit for the analog cursor. void SetAnalogCursorAngleLimits(float angleLimitStart, float angleLimitEnd) { m_AnalogCursorAngleLimits = {{angleLimitStart, angleLimitEnd}, true}; } - /// /// Clears the analog cursor aim limits for the given player (does nothing for player -1). - /// void ClearAnalogCursorAngleLimits() { m_AnalogCursorAngleLimits.second = false; } - /// /// Adds relative movement to a passed-in vector. Uses the appropriate input method currently of this. - /// - /// The vector to alter. - /// The scale of the input. 1.0 is 'normal'. - /// Whether the vector was altered or not. + /// @param cursorPos The vector to alter. + /// @param moveScale The scale of the input. 1.0 is 'normal'. + /// @return Whether the vector was altered or not. bool RelativeCursorMovement(Vector& cursorPos, float moveScale = 1.0F) const; - /// /// Indicates whether this is listening to mouse input at all. - /// - /// Whether this is using mouse input at all. + /// @return Whether this is using mouse input at all. bool IsMouseControlled() const; - /// /// Indicates whether this is only listening to keyboard input. - /// - /// Whether this is only using keyboard input. + /// @return Whether this is only using keyboard input. bool IsKeyboardOnlyControlled() const; - /// /// Indicates whether this is listening to gamepad at all. - /// - /// Whether this is using gamepad input at all. + /// @return Whether this is using gamepad input at all. bool IsGamepadControlled() const; - /// /// Gets the relative movement of the mouse since last update. - /// - /// The relative mouse movements, in both axes. + /// @return The relative mouse movements, in both axes. const Vector& GetMouseMovement() const { return m_MouseMovement; } - /// /// Get the digital aim speed multiplier of the scheme associated with this Controller. - /// - /// The digital aim speed set to the scheme of this Controller. + /// @return The digital aim speed set to the scheme of this Controller. float GetDigitalAimSpeed() const; - /// /// Gets the player this is listening. Unlike GetPlayer, this ignores the input mode. - /// - /// The player this is listening to, regardless of input mode. + /// @return The player this is listening to, regardless of input mode. int GetPlayerRaw() const { return m_Player; } - /// /// Gets which player's input this is listening to, if in player input mode. - /// - /// The player number, or -1 if not in player input mode. + /// @return The player number, or -1 if not in player input mode. int GetPlayer() const { return (m_InputMode == InputMode::CIM_PLAYER) ? m_Player : Players::NoPlayer; } - /// /// Sets which player's input this is listening to, and will enable player input mode. - /// - /// The player number. + /// @param player The player number. void SetPlayer(int player) { m_Player = player; if (m_Player >= Players::PlayerOne) { @@ -324,59 +258,43 @@ namespace RTE { } } - /// /// Gets the Team number using this controller. - /// - /// An int representing the team which this Controller belongs to. 0 is the first team. 0 if no team is using it. + /// @return An int representing the team which this Controller belongs to. 0 is the first team. 0 if no team is using it. int GetTeam() const; - /// /// Sets the team which is controlling this Controller's controlled Actor. - /// - /// The team number. 0 is the first team. + /// @param team The team number. 0 is the first team. void SetTeam(short team); - /// /// Gets which Actor is being controlled by this. 0 if none. - /// - /// A pointer to the Actor which is being controlled by this. Ownership is NOT transferred! + /// @return A pointer to the Actor which is being controlled by this. Ownership is NOT transferred! Actor* GetControlledActor() const { return m_ControlledActor; } - /// /// Sets which Actor is supposed to be controlled by this. - /// - /// A pointer to a an Actor which is being controlled by this. Ownership is NOT transferred! + /// @param controlledActor A pointer to a an Actor which is being controlled by this. Ownership is NOT transferred! void SetControlledActor(Actor* controlledActor = nullptr) { m_ControlledActor = controlledActor; } - /// /// Returns whether the AI should be updated this frame. - /// - /// Whether the AI should be updated this frame. + /// @return Whether the AI should be updated this frame. bool ShouldUpdateAIThisFrame() const; #pragma endregion #pragma region Virtual Override Methods - /// /// Updates this Controller. Supposed to be done every frame. - /// void Update(); #pragma endregion #pragma region Operator Overloads - /// /// An assignment operator for setting one Controller equal to another. - /// - /// A Controller reference. - /// A reference to the changed Controller. + /// @param rhs A Controller reference. + /// @return A reference to the changed Controller. Controller& operator=(const Controller& rhs); #pragma endregion #pragma region Misc - /// /// Overrides this controller, setting it to match another controller. This is useful for multithreading, where the Lua script can use a copied controller in a multi-threaded context, before overriding the controller in a single-threaded context. /// This is exposed to Lua API to be clear, whereas ownership relies on operator overloading is rather temperamental :) - /// - /// The other controller's state to copy. Ownership is not transferred + /// @param otherController The other controller's state to copy. Ownership is not transferred void Override(const Controller& otherController); #pragma endregion @@ -390,18 +308,14 @@ namespace RTE { Actor* m_ControlledActor; //!< The actor controlled by this. - /// /// The last player this controlled. This is necessary so we still have some control after controlled's death. /// If this is -1, no player is controlling/ed, even if in player control input mode. - /// int m_Player; int m_Team; //!< The last team this controlled. This is necessary so we still have some control after controlled's death. - /// /// These are hacks to make the switch to brain shortcut work without immediately switching away by /// detecting the release of the previous and next buttons after pressing them both down to get to the brain. - /// bool m_NextIgnore; bool m_PrevIgnore; @@ -422,36 +336,24 @@ namespace RTE { private: #pragma region Update Breakdown - /// /// Updates the player's inputs portion of this Controller. For breaking down Update into more comprehensible chunks. /// This method will call both UpdatePlayerPieMenuInput and UpdatePlayerAnalogInput. - /// void UpdatePlayerInput(); - /// /// Updates the player's PieMenu inputs portion of this Controller. For breaking down Update into more comprehensible chunks. - /// void UpdatePlayerPieMenuInput(); - /// /// Updates the player's analog inputs portion of this Controller. For breaking down Update into more comprehensible chunks. - /// void UpdatePlayerAnalogInput(); - /// /// Clears the command state, meaning no input is given and our actor will be idle. - /// void ResetCommandState(); - /// /// Requests and applies input from the player. - /// void GetInputFromPlayer(); #pragma endregion - /// /// Clears all the member variables of this Controller, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/System/DataModule.cpp b/Source/System/DataModule.cpp index 3079cb9883..1582d8519c 100644 --- a/Source/System/DataModule.cpp +++ b/Source/System/DataModule.cpp @@ -10,8 +10,6 @@ namespace RTE { const std::string DataModule::c_ClassName = "DataModule"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void DataModule::Clear() { m_IsUserdata = false; m_FileName.clear(); @@ -35,8 +33,6 @@ namespace RTE { m_IsMerchant = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::Create(const std::string& moduleName, const ProgressCallback& progressCallback) { m_FileName = std::filesystem::path(moduleName).generic_string(); m_ModuleID = g_PresetMan.GetModuleID(moduleName); @@ -78,8 +74,6 @@ namespace RTE { return -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::CreateOnDiskAsUserdata(const std::string& moduleName, const std::string_view& friendlyName, bool ignoreMissingItems, bool scanFolderContents) { std::string moduleNameWithPackageExtension = System::GetUserdataDirectory() + moduleName + (moduleName.ends_with(System::GetModulePackageExtension()) ? "" : System::GetModulePackageExtension()); if (Writer writer(moduleNameWithPackageExtension + "/Index.ini", false, true); writer.WriterOK()) { @@ -96,8 +90,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void DataModule::Destroy() { for (const PresetEntry& preset: m_PresetList) { delete preset.m_EntityPreset; @@ -106,8 +98,6 @@ namespace RTE { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::ReadModuleProperties(const std::string& moduleName, const ProgressCallback& progressCallback) { m_FileName = moduleName; m_ModuleID = g_PresetMan.GetModuleID(moduleName); @@ -128,8 +118,6 @@ namespace RTE { return -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(if (!g_PresetMan.GetEntityPreset(reader)) { reader.ReportError("Could not understand Preset type!"); }) @@ -216,8 +204,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::Save(Writer& writer) const { Serializable::Save(writer); @@ -242,8 +228,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string DataModule::GetEntityDataLocation(const std::string& exactType, const std::string& instance) { const Entity* foundEntity = GetEntityPreset(exactType, instance); if (foundEntity == nullptr) { @@ -261,8 +245,6 @@ namespace RTE { return ""; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Entity* DataModule::GetEntityPreset(const std::string& exactType, const std::string& instance) { if (exactType.empty() || instance == "None" || instance.empty()) { return nullptr; @@ -278,8 +260,6 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::AddEntityPreset(Entity* entityToAdd, bool overwriteSame, const std::string& readFromFile) { // Fail if the entity is unnamed or it's not the original preset. // TODO If we're overwriting, we may not want to fail if it's not the original preset, this needs to be investigated @@ -330,8 +310,6 @@ namespace RTE { return entityAdded; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::GetGroupsWithType(std::list& groupList, const std::string& withType) { bool foundAny = false; @@ -362,8 +340,6 @@ namespace RTE { return foundAny; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::GetAllOfOrNotOfGroups(std::list& entityList, const std::string& type, const std::vector& groups, bool excludeGroups) { if (groups.empty()) { return false; @@ -401,8 +377,6 @@ namespace RTE { return foundAny; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::GetAllOfType(std::list& entityList, const std::string& type) { if (type.empty()) { return false; @@ -419,8 +393,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::AddMaterialMapping(unsigned char fromID, unsigned char toID) { RTEAssert(fromID > 0 && fromID < c_PaletteEntriesNumber && toID > 0 && toID < c_PaletteEntriesNumber, "Tried to make an out-of-bounds Material mapping"); @@ -430,8 +402,6 @@ namespace RTE { return clear; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::LoadScripts() const { if (m_ScriptPath.empty()) { return 0; @@ -445,8 +415,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void DataModule::ReloadAllScripts() const { for (const PresetEntry& presetListEntry: m_PresetList) { presetListEntry.m_EntityPreset->ReloadScripts(); @@ -454,8 +422,6 @@ namespace RTE { LoadScripts(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int DataModule::FindAndRead(const ProgressCallback& progressCallback) { int result = 0; const std::string directoryToScan = g_PresetMan.GetFullModulePath(m_FileName); @@ -473,8 +439,6 @@ namespace RTE { return result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: This method is almost identical to GetEntityPreset, except it doesn't return a const Entity *. // Investigate if the latter needs to return const (based on what's using it) and if not, get rid of this and replace its uses. At the very least, consider renaming this // See https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/87 @@ -493,8 +457,6 @@ namespace RTE { return nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool DataModule::AddToTypeMap(Entity* entityToAdd) { if (!entityToAdd || entityToAdd->GetPresetName() == "None" || entityToAdd->GetPresetName().empty()) { return false; @@ -515,8 +477,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void DataModule::CheckSupportedGameVersion() const { if (*m_SupportedGameVersion == c_GameVersion) { return; diff --git a/Source/System/DataModule.h b/Source/System/DataModule.h index 39bd884132..228704fee7 100644 --- a/Source/System/DataModule.h +++ b/Source/System/DataModule.h @@ -15,9 +15,7 @@ namespace RTE { class Entity; - /// /// A representation of a DataModule containing zero or many Material, Effect, Ammo, Device, Actor, or Scene definitions. - /// class DataModule : public Serializable { friend struct SystemLuaBindings; @@ -25,10 +23,8 @@ namespace RTE { SerializableClassNameGetter; SerializableOverrideMethods; - /// /// Struct that holds data about the custom BuyMenu/ObjectPicker theme of this DataModule. /// Themes are used when a DataModule is considered a faction and is selected to be played as in an Activity. - /// struct BuyMenuTheme { std::string SkinFilePath = ""; //!< Path to the custom BuyMenu skin file. std::string BannerImagePath = ""; //!< Path to the custom BuyMenu banner image. @@ -37,281 +33,204 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate a DataModule object in system memory. Create() should be called before using the object. - /// DataModule() { Clear(); } - /// /// Constructor method used to instantiate a DataModule object in system memory, and also do a Create() in the same line. /// Create() should therefore not be called after using this constructor. - /// - /// A string defining the path to where the content file itself is located, either within the package file, or directly on the disk. - /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. + /// @param moduleName A string defining the path to where the content file itself is located, either within the package file, or directly on the disk. + /// @param progressCallback A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. DataModule(const std::string& moduleName, const ProgressCallback& progressCallback = nullptr) { Clear(); Create(moduleName, progressCallback); } - /// /// Makes the DataModule object ready for use. This needs to be called after PresetMan is created. /// This looks for an "index.ini" within the specified .rte directory and loads all the defined objects in that index file. - /// - /// A string defining the name of this DataModule, e.g. "MyModule.rte". - /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param moduleName A string defining the name of this DataModule, e.g. "MyModule.rte". + /// @param progressCallback A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const std::string& moduleName, const ProgressCallback& progressCallback = nullptr); - /// /// Creates a new DataModule directory with "Index.ini" on disk to be used for userdata. Does NOT instantiate the newly created DataModule. - /// - /// File/folder name of the data module, e.g. "MyMod.rte". - /// Friendly name of the data module, e.g. "My Weapons Mod". - /// Whether module loader should scan for any .ini's inside module folder instead of loading files defined in IncludeFile only. - /// Whether module loader should ignore missing items in this module. - /// Whether the DataModule was successfully created on disk. + /// @param moduleName File/folder name of the data module, e.g. "MyMod.rte". + /// @param friendlyName Friendly name of the data module, e.g. "My Weapons Mod". + /// @param scanFolderContents Whether module loader should scan for any .ini's inside module folder instead of loading files defined in IncludeFile only. + /// @param ignoreMissingItems Whether module loader should ignore missing items in this module. + /// @return Whether the DataModule was successfully created on disk. static bool CreateOnDiskAsUserdata(const std::string& moduleName, const std::string_view& friendlyName, bool scanFolderContents = false, bool ignoreMissingItems = false); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a DataModule object before deletion from system memory. - /// ~DataModule() override { Destroy(); } - /// /// Destroys and resets (through Clear()) the DataModule object. - /// void Destroy(); - /// /// Resets the entire DataModule, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); } #pragma endregion #pragma region INI Handling - /// /// Read module specific properties from index.ini without processing IncludeFiles and loading the whole module. - /// - /// A string defining the name of this DataModule, e.g. "MyModule.rte". - /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param moduleName A string defining the name of this DataModule, e.g. "MyModule.rte". + /// @param progressCallback A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int ReadModuleProperties(const std::string& moduleName, const ProgressCallback& progressCallback = nullptr); - /// /// Returns true if loader should ignore missing items in this module. - /// - /// True if loader should ignore missing items in this module. + /// @return True if loader should ignore missing items in this module. bool GetIgnoreMissingItems() const { return m_IgnoreMissingItems; } #pragma endregion #pragma region Module Information Getters - /// /// Gets whether this DataModule is a userdata module. - /// - /// Whether this DataModule is used for userdata written by the game. + /// @return Whether this DataModule is used for userdata written by the game. bool IsUserdata() const { return m_IsUserdata; } - /// /// Sets this DataModule as a userdata module. - /// void SetAsUserdata() { m_IsUserdata = true; } - /// /// Gets the file name of this DataModule, e.g. "MyMod.rte". - /// - /// A string with the data module file name. + /// @return A string with the data module file name. const std::string& GetFileName() const { return m_FileName; } - /// /// Gets the friendly name of this DataModule, e.g. "My Great Mod". - /// - /// A string with the data module's friendly name. + /// @return A string with the data module's friendly name. const std::string& GetFriendlyName() const { return m_FriendlyName; } - /// /// Gets the author name of this DataModule, e.g. "Data Realms, LLC". - /// - /// A string with the author's name. + /// @return A string with the author's name. const std::string& GetAuthor() const { return m_Author; } - /// /// Gets the description of this DataModule's contents. - /// - /// A string with the description. + /// @return A string with the description. const std::string& GetDescription() const { return m_Description; } - /// /// Gets whether this DataModule is considered a faction. - /// - /// Whether this DataModule is considered a faction or not. + /// @return Whether this DataModule is considered a faction or not. bool IsFaction() const { return m_IsFaction; } - /// /// Gets whether this DataModule is considered a merchant. - /// - /// Whether this DataModule is considered a merchant or not. + /// @return Whether this DataModule is considered a merchant or not. bool IsMerchant() const { return m_IsMerchant; } - /// /// Gets the version number of this DataModule. - /// - /// An int with the version number, starting at 1. + /// @return An int with the version number, starting at 1. int GetVersionNumber() const { return m_Version; } - /// /// Gets the BITMAP that visually represents this DataModule, for use in menus. - /// - /// BITMAP pointer that might have the icon. 0 is very possible. + /// @return BITMAP pointer that might have the icon. 0 is very possible. BITMAP* GetIcon() const { return m_Icon; } - /// /// Returns crab-to-human spawn ration for this tech. - /// - /// Crab-to-human spawn ration value. + /// @return Crab-to-human spawn ration value. float GetCrabToHumanSpawnRatio() const { return m_CrabToHumanSpawnRatio; } - /// /// Gets the faction BuyMenu theme data of this DataModule. - /// - /// The faction BuyMenu theme information of this DataModule + /// @return The faction BuyMenu theme information of this DataModule const BuyMenuTheme& GetFactionBuyMenuTheme() const { return m_BuyMenuTheme; } #pragma endregion #pragma region Entity Mapping - /// /// Gets the data file path of a previously read in (defined) Entity. - /// - /// The type name of the derived Entity. Ownership is NOT transferred! - /// The instance name of the derived Entity instance. - /// The file path of the data file that the specified Entity was read from. If no Entity of that description was found, "" is returned. + /// @param exactType The type name of the derived Entity. Ownership is NOT transferred! + /// @param instance The instance name of the derived Entity instance. + /// @return The file path of the data file that the specified Entity was read from. If no Entity of that description was found, "" is returned. std::string GetEntityDataLocation(const std::string& exactType, const std::string& instance); - /// /// Gets a previously read in (defined) Entity, by exact type and instance name. Ownership is NOT transferred! - /// - /// The exact type name of the derived Entity instance to get. - /// The instance name of the derived Entity instance. - /// A pointer to the requested Entity instance. 0 if no Entity with that derived type or instance name was found. Ownership is NOT transferred! + /// @param exactType The exact type name of the derived Entity instance to get. + /// @param instance The instance name of the derived Entity instance. + /// @return A pointer to the requested Entity instance. 0 if no Entity with that derived type or instance name was found. Ownership is NOT transferred! const Entity* GetEntityPreset(const std::string& exactType, const std::string& instance); - /// /// Adds an Entity instance's pointer and name associations to the internal list of already read in Entities. Ownership is NOT transferred! /// If there already is an instance defined, nothing happens. If there is not, a clone is made of the passed-in Entity and added to the library. - /// - /// A pointer to the Entity derived instance to add. It should be created from a Reader. Ownership is NOT transferred! - /// + /// @param entityToAdd A pointer to the Entity derived instance to add. It should be created from a Reader. Ownership is NOT transferred! + /// @param overwriteSame /// Whether to overwrite if an instance of the EXACT same TYPE and name was found. /// If one of the same name but not the exact type, false is returned regardless and nothing will have been added. - /// - /// + /// @param readFromFile /// The file the instance was read from, or where it should be written. /// If "Same" is passed as the file path read from, an overwritten instance will keep the old one's file location entry. - /// - /// + /// @return /// Whether or not a copy of the passed-in instance was successfully inserted into the module. /// False will be returned if there already was an instance of that class and instance name inserted previously, unless overwritten. - /// bool AddEntityPreset(Entity* entityToAdd, bool overwriteSame = false, const std::string& readFromFile = "Same"); - /// /// Gets the list of all registered Entity groups of this. - /// - /// The list of all groups. Ownership is not transferred. + /// @return The list of all groups. Ownership is not transferred. const std::list* GetGroupRegister() const { return &m_GroupRegister; } - /// /// Registers the existence of an Entity group in this module. - /// - /// The group to register. + /// @param newGroup The group to register. void RegisterGroup(const std::string& newGroup) { m_GroupRegister.push_back(newGroup); m_GroupRegister.sort(); m_GroupRegister.unique(); } - /// /// Fills out a list with all groups registered with this that contain any objects of a specific type and it derivatives. - /// - /// The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! - /// The name of the type to only get groups of. - /// Whether any groups with the specified type were found. + /// @param groupList The list that all found groups will be ADDED to. OWNERSHIP IS NOT TRANSFERRED! + /// @param withType The name of the type to only get groups of. + /// @return Whether any groups with the specified type were found. bool GetGroupsWithType(std::list& groupList, const std::string& withType); - /// /// Adds to a list all previously read in (defined) Entities which are associated with several specific groups. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// A list of groups to look for. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether any Entities were found and added to the list. + /// @param entityList Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// @param groups A list of groups to look for. + /// @param type The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// @return Whether any Entities were found and added to the list. bool GetAllOfGroups(std::list& entityList, const std::vector& groups, const std::string& type) { return GetAllOfOrNotOfGroups(entityList, type, groups, false); } - /// /// Adds to a list all previously read in (defined) Entities which are not associated with several specific groups. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// A list of groups to exclude. - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether any Entities were found and added to the list. + /// @param entityList Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// @param group A list of groups to exclude. + /// @param type The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// @return Whether any Entities were found and added to the list. bool GetAllNotOfGroups(std::list& entityList, const std::vector& groups, const std::string& type) { return GetAllOfOrNotOfGroups(entityList, type, groups, true); } - /// /// Adds to a list all previously read in (defined) Entities, by inexact type. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// Whether any Entities were found and added to the list. + /// @param objectList Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// @param type The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// @return Whether any Entities were found and added to the list. bool GetAllOfType(std::list& objectList, const std::string& type); #pragma endregion #pragma region Material Mapping - /// /// Gets a Material mapping local to this DataModule. /// This is used for when multiple DataModules are loading conflicting Materials, and need to resolve the conflicts by mapping their materials to ID's different than those specified in the data files. - /// - /// The material ID to get the mapping for. - /// The material ID that the passed in ID is mapped to, if any. 0 if no mapping present. + /// @param materialID The material ID to get the mapping for. + /// @return The material ID that the passed in ID is mapped to, if any. 0 if no mapping present. unsigned char GetMaterialMapping(unsigned char materialID) const { return m_MaterialMappings.at(materialID); } - /// /// Gets the entire Material mapping array local to this DataModule. - /// - /// A const reference to the entire local mapping array, 256 unsigned chars. Ownership is NOT transferred! + /// @return A const reference to the entire local mapping array, 256 unsigned chars. Ownership is NOT transferred! const std::array& GetAllMaterialMappings() const { return m_MaterialMappings; } - /// /// Adds a Material mapping local to a DataModule. /// This is used for when multiple DataModules are loading conflicting Materials, and need to resolve the conflicts by mapping their materials to ID's different than those specified in the data files. - /// - /// The material ID to map from. - /// The material ID to map to. - /// Whether this created a new mapping which didn't override a previous material mapping. + /// @param fromID The material ID to map from. + /// @param toID The material ID to map to. + /// @return Whether this created a new mapping which didn't override a previous material mapping. bool AddMaterialMapping(unsigned char fromID, unsigned char toID); #pragma endregion #pragma region Lua Script Handling - /// /// Loads the preset scripts of this object, from a specified path. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int LoadScripts() const; - /// /// Reloads all scripted Entity Presets with the latest version of their respective script files. - /// void ReloadAllScripts() const; #pragma endregion protected: - /// /// Holds and owns the actual object instance pointer, and the location of the data file it was read from, as well as where in that file. - /// struct PresetEntry { - /// /// Constructor method used to instantiate a PresetEntry object in system memory. - /// PresetEntry(Entity* preset, const std::string& file) : m_EntityPreset(preset), m_FileReadFrom(file) {} @@ -345,72 +264,56 @@ namespace RTE { std::list m_GroupRegister; //!< List of all Entity groups ever registered in this, all uniques. std::array m_MaterialMappings; //!< Material mappings local to this DataModule. - /// /// Ordered list of all owned Entity instances, ordered by the sequence of their reading - really now with overwriting?. /// This is used to be able to write back all of them in proper order into their respective files in the DataModule when writing this. /// The Entity instances ARE owned by this list. - /// std::list m_PresetList; - /// /// Map of class names and map of instance template names and actual Entity instances that were read for this DataModule. /// An Entity instance of a derived type will be placed in EACH of EVERY of its parent class' maps here. /// There can be multiple entries of the same instance name in any of the type sub-maps, but only ONE whose exact class is that of the type-list! /// The Entity instances are NOT owned by this map. - /// std::unordered_map>> m_TypeMap; private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. #pragma region INI Handling - /// /// Checks the module's supported game version against the current game version to ensure compatibility. - /// void CheckSupportedGameVersion() const; - /// /// If ScanFolderContents is enabled in this DataModule's Index.ini, looks for any ini files in the top-level directory of the module and reads all of them in alphabetical order. - /// - /// A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param progressCallback A function pointer to a function that will be called and sent a string with information about the progress of this DataModule's creation. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int FindAndRead(const ProgressCallback& progressCallback = nullptr); #pragma endregion #pragma region Entity Mapping - /// /// Shared method for filling a list with all previously read in (defined) Entities which are or are not associated with a specific group or groups. - /// - /// Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! - /// The name of the least common denominator type of the Entities you want. "All" will look at all types. - /// The groups to look for. - /// Whether Entities belonging to the specified group or groups should be excluded. - /// Whether any Entities were found and added to the list. + /// @param entityList Reference to a list which will get all matching Entities added to it. Ownership of the list or the Entities placed in it are NOT transferred! + /// @param type The name of the least common denominator type of the Entities you want. "All" will look at all types. + /// @param groups The groups to look for. + /// @param excludeGroups Whether Entities belonging to the specified group or groups should be excluded. + /// @return Whether any Entities were found and added to the list. bool GetAllOfOrNotOfGroups(std::list& entityList, const std::string& type, const std::vector& groups, bool excludeGroups); - /// /// Checks if the type map has an instance added of a specific name and exact type. /// Does not check if any parent types with that name has been added. If found, that instance is returned, otherwise 0. - /// - /// The exact type name to look for. - /// The exact PresetName to look for. - /// The found Entity Preset of the exact type and name, if found. + /// @param exactType The exact type name to look for. + /// @param presetName The exact PresetName to look for. + /// @return The found Entity Preset of the exact type and name, if found. Entity* GetEntityIfExactType(const std::string& exactType, const std::string& presetName); - /// /// Adds a newly added preset instance to the type map, where it will end up in every type-list of every class it derived from as well. /// I.e the "Entity" map will contain every single instance added to this. /// This will NOT check if duplicates are added to any type-list, so please use GetEntityIfExactType to check this beforehand. /// Dupes are allowed if there are no more than one of the exact class and name. - /// - /// The new object instance to add. OWNERSHIP IS NOT TRANSFERRED! - /// Whether the Entity was added successfully or not. + /// @param entityToAdd The new object instance to add. OWNERSHIP IS NOT TRANSFERRED! + /// @return Whether the Entity was added successfully or not. bool AddToTypeMap(Entity* entityToAdd); #pragma endregion - /// /// Clears all the member variables of this DataModule, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/System/Entity.cpp b/Source/System/Entity.cpp index 86df30c14b..846abe2226 100644 --- a/Source/System/Entity.cpp +++ b/Source/System/Entity.cpp @@ -9,8 +9,6 @@ namespace RTE { Entity::ClassInfo Entity::m_sClass("Entity"); Entity::ClassInfo* Entity::ClassInfo::s_ClassHead = 0; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Entity::Clear() { m_PresetName = "None"; m_IsOriginalPreset = false; @@ -20,14 +18,10 @@ namespace RTE { m_RandomWeight = 100; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::Create() { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::Create(const Entity& reference) { m_PresetName = reference.m_PresetName; // Note how m_IsOriginalPreset is NOT assigned, automatically indicating that the copy is not an original Preset! @@ -41,8 +35,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList( // Search for a property name match failed! @@ -102,8 +94,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::Save(Writer& writer) const { Serializable::Save(writer); @@ -128,8 +118,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::SavePresetCopy(Writer& writer) const { // Can only save out copies with this if (m_IsOriginalPreset) { @@ -143,14 +131,10 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Entity* Entity::GetPreset() const { return g_PresetMan.GetEntityPreset(GetClassName(), GetPresetName(), m_DefinedInModule); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Entity::GetModuleAndPresetName() const { if (m_DefinedInModule < 0) { return GetPresetName(); @@ -163,8 +147,6 @@ namespace RTE { return dataModule->GetFileName() + "/" + GetPresetName(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Entity::GetModuleName() const { if (m_DefinedInModule >= 0) { if (const DataModule* dataModule = g_PresetMan.GetDataModule(m_DefinedInModule)) { @@ -174,8 +156,6 @@ namespace RTE { return ""; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Entity::MigrateToModule(int whichModule) { if (m_DefinedInModule == whichModule) { return false; @@ -185,8 +165,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader& operator>>(Reader& reader, Entity& operand) { // Get this before reading Entity, since if it's the last one in its datafile, the stream will show the parent file instead std::string objectFilePath = reader.GetCurrentFilePath(); @@ -197,8 +175,6 @@ namespace RTE { return reader; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader& operator>>(Reader& reader, Entity* operand) { if (operand) { // Get this before reading Entity, since if it's the last one in its datafile, the stream will show the parent file instead @@ -212,8 +188,6 @@ namespace RTE { return reader; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Entity::ClassInfo::ClassInfo(const std::string& name, ClassInfo* parentInfo, MemoryAllocate allocFunc, MemoryDeallocate deallocFunc, Entity* (*newFunc)(), int allocBlockCount) : m_Name(name), m_ParentInfo(parentInfo), @@ -227,8 +201,6 @@ namespace RTE { m_PoolAllocBlockCount = (allocBlockCount > 0) ? allocBlockCount : 10; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::list Entity::ClassInfo::GetClassNames() { std::list retList; for (const ClassInfo* itr = s_ClassHead; itr != 0; itr = itr->m_NextClass) { @@ -237,8 +209,6 @@ namespace RTE { return retList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Entity::ClassInfo* Entity::ClassInfo::GetClass(const std::string& name) { if (name.empty() || name == "None") { return 0; @@ -251,8 +221,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Entity::ClassInfo::FillAllPools(int fillAmount) { for (ClassInfo* itr = s_ClassHead; itr != 0; itr = itr->m_NextClass) { if (itr->IsConcrete()) { @@ -261,8 +229,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Entity::ClassInfo::FillPool(int fillAmount) { // Default to the set block allocation size if fillAmount is 0 if (fillAmount <= 0) { @@ -277,8 +243,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Entity::ClassInfo::IsClassOrChildClassOf(const ClassInfo* classInfoToCheck) const { if (GetName() == classInfoToCheck->GetName()) { return true; @@ -288,8 +252,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void* Entity::ClassInfo::GetPoolMemory() { std::lock_guard guard(m_Mutex); @@ -312,8 +274,6 @@ namespace RTE { return foundMemory; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Entity::ClassInfo::ReturnPoolMemory(void* returnedMemory) { if (!returnedMemory) { return 0; @@ -327,8 +287,6 @@ namespace RTE { return m_InstancesInUse; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Entity::ClassInfo::DumpPoolMemoryInfo(const Writer& fileWriter) { for (const ClassInfo* itr = s_ClassHead; itr != nullptr; itr = itr->m_NextClass) { if (itr->IsConcrete()) { diff --git a/Source/System/Entity.h b/Source/System/Entity.h index e4ff20bfe9..2d82882d9c 100644 --- a/Source/System/Entity.h +++ b/Source/System/Entity.h @@ -19,18 +19,14 @@ namespace RTE { #define ConcreteSubClassInfo(TYPE, SUPER, PARENT, BLOCKCOUNT) \ Entity::ClassInfo SUPER::TYPE::m_sClass(#TYPE, &PARENT::m_sClass, SUPER::TYPE::Allocate, SUPER::TYPE::Deallocate, SUPER::TYPE::NewInstance, BLOCKCOUNT); -/// /// Convenience macro to cut down on duplicate ClassInfo methods in classes that extend Entity. -/// #define ClassInfoGetters \ const Entity::ClassInfo& GetClass() const override { return m_sClass; } \ const std::string& GetClassName() const override { return m_sClass.GetName(); } -/// /// Static method used in conjunction with ClassInfo to allocate an Entity. /// This function is passed into the constructor of this Entity's static ClassInfo's constructor, so that it can instantiate MovableObjects. -/// -/// A pointer to the newly dynamically allocated Entity. Ownership is transferred as well. +/// @return A pointer to the newly dynamically allocated Entity. Ownership is transferred as well. #define EntityAllocation(TYPE) \ static void* operator new(size_t size) { return TYPE::m_sClass.GetPoolMemory(); } \ static void operator delete(void* instance) { TYPE::m_sClass.ReturnPoolMemory(instance); } \ @@ -50,9 +46,7 @@ namespace RTE { } #pragma endregion - /// /// Whether to draw the colors, or own material property, or to clear the corresponding non-key-color pixels of the Entity being drawn with key-color pixels on the target. - /// enum DrawMode { g_DrawColor, g_DrawMaterial, @@ -64,9 +58,7 @@ namespace RTE { g_DrawAlpha }; - /// /// The base class that specifies certain common creation/destruction patterns and simple reflection support for virtually all RTE classes. - /// class Entity : public Serializable { friend class DataModule; @@ -74,125 +66,91 @@ namespace RTE { SerializableOverrideMethods; #pragma region ClassInfo - /// /// The class that describes each subclass of Entity. There should be one ClassInfo static instance for every Entity child. - /// class ClassInfo { friend class Entity; public: #pragma region Creation - /// /// Constructor method used to instantiate a ClassInfo Entity. - /// - /// A friendly-formatted name of the Entity that is going to be represented by this ClassInfo. - /// Pointer to the parent class' info. 0 if this describes a root class. - /// Function pointer to the raw allocation function of the derived's size. If the represented Entity subclass isn't concrete, pass in 0. - /// Function pointer to the raw deallocation function of memory. If the represented Entity subclass isn't concrete, pass in 0. - /// Function pointer to the new instance factory. If the represented Entity subclass isn't concrete, pass in 0. - /// The number of new instances to fill the pre-allocated pool with when it runs out. + /// @param name A friendly-formatted name of the Entity that is going to be represented by this ClassInfo. + /// @param parentInfo Pointer to the parent class' info. 0 if this describes a root class. + /// @param allocFunc Function pointer to the raw allocation function of the derived's size. If the represented Entity subclass isn't concrete, pass in 0. + /// @param deallocFunc Function pointer to the raw deallocation function of memory. If the represented Entity subclass isn't concrete, pass in 0. + /// @param newFunc Function pointer to the new instance factory. If the represented Entity subclass isn't concrete, pass in 0. + /// @param allocBlockCount The number of new instances to fill the pre-allocated pool with when it runs out. ClassInfo(const std::string& name, ClassInfo* parentInfo = 0, MemoryAllocate allocFunc = 0, MemoryDeallocate deallocFunc = 0, Entity* (*newFunc)() = 0, int allocBlockCount = 10); #pragma endregion #pragma region Getters - /// /// Gets the name of this ClassInfo. - /// - /// A string with the friendly-formatted name of this ClassInfo. + /// @return A string with the friendly-formatted name of this ClassInfo. const std::string& GetName() const { return m_Name; } - /// /// Gets the names of all ClassInfos in existence. - /// - /// A list of the names. + /// @return A list of the names. static std::list GetClassNames(); - /// /// Gets the ClassInfo of a particular RTE class corresponding to a friendly-formatted string name. - /// - /// The friendly name of the desired ClassInfo. - /// A pointer to the requested ClassInfo, or 0 if none that matched the name was found. Ownership is NOT transferred! + /// @param name The friendly name of the desired ClassInfo. + /// @return A pointer to the requested ClassInfo, or 0 if none that matched the name was found. Ownership is NOT transferred! static const ClassInfo* GetClass(const std::string& name); - /// /// Gets the ClassInfo which describes the parent of this. - /// - /// A pointer to the parent ClassInfo. 0 if this is a root class. + /// @return A pointer to the parent ClassInfo. 0 if this is a root class. const ClassInfo* GetParent() const { return m_ParentInfo; } - /// /// Gets whether or not this ClassInfo is the same as, or a parent of the ClassInfo corresponding to the given class name. - /// - /// The name of the class to check for. - /// Whether or not this ClassInfo is the same as, or a parent of corresponding ClassInfo for the given class. + /// @param classNameToCheck The name of the class to check for. + /// @return Whether or not this ClassInfo is the same as, or a parent of corresponding ClassInfo for the given class. bool IsClassOrParentClassOf(const std::string& classNameToCheck) const { return GetClass(classNameToCheck)->IsClassOrChildClassOf(this); } - /// /// Gets whether or not this ClassInfo is the same as, or a parent of the given ClassInfo. - /// - /// The name of the class to check for. - /// Whether or not this ClassInfo is the same as, or a parent of the given ClassInfo. + /// @param classNameToCheck The name of the class to check for. + /// @return Whether or not this ClassInfo is the same as, or a parent of the given ClassInfo. bool IsClassOrParentClassOf(const ClassInfo* classInfoToCheck) const { return classInfoToCheck->IsClassOrChildClassOf(this); } - /// /// Gets whether or not this ClassInfo is the same as, or a child of the ClassInfo corresponding to the given class name. - /// - /// The name of the class to check for. - /// Whether or not this ClassInfo is the same as, or a child of corresponding ClassInfo for the given class. + /// @param classNameToCheck The name of the class to check for. + /// @return Whether or not this ClassInfo is the same as, or a child of corresponding ClassInfo for the given class. bool IsClassOrChildClassOf(const std::string& classNameToCheck) const { return IsClassOrChildClassOf(GetClass(classNameToCheck)); } - /// /// Gets whether or not this ClassInfo is the same as, or a child of the given ClassInfo. - /// - /// The name of the class to check for. - /// Whether or not this ClassInfo is the same as, or a child of the given ClassInfo. + /// @param classNameToCheck The name of the class to check for. + /// @return Whether or not this ClassInfo is the same as, or a child of the given ClassInfo. bool IsClassOrChildClassOf(const ClassInfo* classInfoToCheck) const; #pragma endregion #pragma region Memory Management - /// /// Grabs from the pre-allocated pool, an available chunk of memory the exact size of the Entity this ClassInfo represents. OWNERSHIP IS TRANSFERRED! - /// - /// A pointer to the pre-allocated pool memory. OWNERSHIP IS TRANSFERRED! + /// @return A pointer to the pre-allocated pool memory. OWNERSHIP IS TRANSFERRED! void* GetPoolMemory(); - /// /// Returns a raw chunk of memory back to the pre-allocated available pool. - /// - /// The raw chunk of memory that is being returned. Needs to be the same size as the type this ClassInfo describes. OWNERSHIP IS TRANSFERRED! - /// The count of outstanding memory chunks after this was returned. + /// @param returnedMemory The raw chunk of memory that is being returned. Needs to be the same size as the type this ClassInfo describes. OWNERSHIP IS TRANSFERRED! + /// @return The count of outstanding memory chunks after this was returned. int ReturnPoolMemory(void* returnedMemory); - /// /// Writes a bunch of useful debug info about the memory pools to a file. - /// - /// The writer to write info to. + /// @param fileWriter The writer to write info to. static void DumpPoolMemoryInfo(const Writer& fileWriter); - /// /// Adds a certain number of newly allocated instances to this' pool. - /// - /// The number of instances to fill the pool with. If 0 is specified, the set refill amount will be used. + /// @param fillAmount The number of instances to fill the pool with. If 0 is specified, the set refill amount will be used. void FillPool(int fillAmount = 0); - /// /// Adds a certain number of newly allocated instances to all pools. - /// - /// The number of instances to fill the pool with. If 0 is specified, the set refill amount will be used. + /// @param fillAmount The number of instances to fill the pool with. If 0 is specified, the set refill amount will be used. static void FillAllPools(int fillAmount = 0); #pragma endregion #pragma region Entity Allocation - /// /// Returns whether the represented Entity subclass is concrete or not, that is if it can create new instances through NewInstance(). - /// - /// Whether the represented Entity subclass is concrete or not. + /// @return Whether the represented Entity subclass is concrete or not. bool IsConcrete() const { return (m_Allocate != 0) ? true : false; } - /// /// Dynamically allocates an instance of the Entity subclass that this ClassInfo represents. If the Entity isn't concrete, 0 will be returned. - /// - /// A pointer to the dynamically allocated Entity. Ownership is transferred. If the represented Entity subclass isn't concrete, 0 will be returned. + /// @return A pointer to the dynamically allocated Entity. Ownership is transferred. If the represented Entity subclass isn't concrete, 0 will be returned. virtual Entity* NewInstance() const { return IsConcrete() ? m_NewInstance() : 0; } #pragma endregion @@ -222,38 +180,28 @@ namespace RTE { #pragma endregion #pragma region Creation - /// /// Constructor method used to instantiate a Entity in system memory. Create() should be called before using the Entity. - /// Entity() { Clear(); } - /// /// Makes the Entity ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Creates an Entity to be identical to another, by deep copy. - /// - /// A reference to the Entity to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Entity to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int Create(const Entity& reference); - /// /// Makes the Serializable ready for use. - /// - /// A Reader that the Serializable will create itself from. - /// Whether there is a class name in the stream to check against to make sure the correct type is being read from the stream. - /// Whether to do any additional initialization of the object after reading in all the properties from the Reader. This is done by calling Create(). - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reader A Reader that the Serializable will create itself from. + /// @param checkType Whether there is a class name in the stream to check against to make sure the correct type is being read from the stream. + /// @param doCreate Whether to do any additional initialization of the object after reading in all the properties from the Reader. This is done by calling Create(). + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(Reader& reader, bool checkType = true, bool doCreate = true) override { return Serializable::Create(reader, checkType, doCreate); } - /// /// Uses a passed-in instance, or creates a new one, and makes it identical to this. - /// - /// A pointer to an instance to make identical to this. If 0 is passed in, a new instance is made inside here, and ownership of it IS returned! - /// An Entity pointer to the newly cloned-to instance. Ownership IS transferred! + /// @param cloneTo A pointer to an instance to make identical to this. If 0 is passed in, a new instance is made inside here, and ownership of it IS returned! + /// @return An Entity pointer to the newly cloned-to instance. Ownership IS transferred! virtual Entity* Clone(Entity* cloneTo = nullptr) const { RTEAbort("Attempt to clone an abstract or unclonable type!"); return nullptr; @@ -261,63 +209,45 @@ namespace RTE { #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a Entity before deletion from system memory. - /// virtual ~Entity() { Destroy(true); } - /// /// Destroys and resets (through Clear()) the Entity. - /// - /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + /// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. virtual void Destroy(bool notInherited = false) { Clear(); } - /// /// Resets the entire Entity, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); } #pragma endregion #pragma region INI Handling - /// /// Only saves out a Preset reference of this to the stream. /// Is only applicable to objects that are not original presets and haven't been altered since they were copied from their original. - /// - /// A Writer that the Entity will save itself to. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param writer A Writer that the Entity will save itself to. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int SavePresetCopy(Writer& writer) const; #pragma endregion #pragma region Getters and Setters - /// /// Shows the ID of the DataModule this Entity has been defined in. - /// - /// The ID of the module, or -1 if it hasn't been defined in any. + /// @return The ID of the module, or -1 if it hasn't been defined in any. int GetModuleID() const { return m_DefinedInModule; } - /// /// Sets the module this Entity was supposed to be defined in. - /// - /// The ID of the module, or -1 if it hasn't been defined in any. + /// @param whichModule The ID of the module, or -1 if it hasn't been defined in any. void SetModuleID(int whichModule) { m_DefinedInModule = whichModule; } - /// /// Gets this Entity's data preset. - /// - /// This Entity's data preset. + /// @return This Entity's data preset. const Entity* GetPreset() const; - /// /// Gets the name of this Entity's data Preset. - /// - /// A string reference with the instance name of this Entity. + /// @return A string reference with the instance name of this Entity. const std::string& GetPresetName() const { return m_PresetName; } - /// /// Sets the name of this Entity's data Preset. - /// - /// A string reference with the instance name of this Entity. - /// Whether this method was called from Lua, in which case this change is cosmetic only and shouldn't affect scripts. + /// @param newName A string reference with the instance name of this Entity. + /// @param calledFromLua Whether this method was called from Lua, in which case this change is cosmetic only and shouldn't affect scripts. // TODO: Replace the calledFromLua flag with some DisplayName property // TODO: Figure out how to handle if same name was set, still make it wasgivenname = true? virtual void SetPresetName(const std::string& newName, bool calledFromLua = false) { /*if (m_PresetName != newName) { m_IsOriginalPreset = true; }*/ @@ -325,147 +255,107 @@ namespace RTE { m_PresetName = newName; } - /// /// Gets the plain text description of this Entity's data Preset. - /// - /// A string reference with the plain text description name of this Preset. + /// @return A string reference with the plain text description name of this Preset. const std::string& GetDescription() const { return m_PresetDescription; } - /// /// Sets the plain text description of this Entity's data Preset. Shouldn't be more than a couple of sentences. - /// - /// A string reference with the preset description. + /// @param newDesc A string reference with the preset description. void SetDescription(const std::string& newDesc) { m_PresetDescription = newDesc; } - /// /// Gets the name of this Entity's data Preset, preceded by the name of the Data Module it was defined in, separated with a '/'. - /// - /// A string with the module and instance name of this Entity. + /// @return A string with the module and instance name of this Entity. std::string GetModuleAndPresetName() const; - /// /// Gets the name of this Entity's Data Module it was defined in. - /// - /// A string with the module of this Entity. + /// @return A string with the module of this Entity. std::string GetModuleName() const; - /// /// Indicates whether this Entity was explicitly given a new instance name upon creation, or if it was just copied/inherited implicitly. - /// - /// Whether this Entity was given a new Preset Name upon creation. + /// @return Whether this Entity was given a new Preset Name upon creation. bool IsOriginalPreset() const { return m_IsOriginalPreset; } - /// /// Sets IsOriginalPreset flag to indicate that the object should be saved as CopyOf. - /// void ResetOriginalPresetFlag() { m_IsOriginalPreset = false; } #pragma endregion #pragma region Logging - /// /// Gets the file and line that are currently being read. Formatted to be used for logging warnings and errors. - /// - /// A string containing the currently read file path and the line being read. + /// @return A string containing the currently read file path and the line being read. const std::string& GetFormattedReaderPosition() const { return m_FormattedReaderPosition; } - /// /// Sets the file and line that are currently being read. Formatted to be used for logging warnings and errors. - /// - /// A string containing the currently read file path and the line being read. + /// @param newPosition A string containing the currently read file path and the line being read. void SetFormattedReaderPosition(const std::string& newPosition) override { m_FormattedReaderPosition = newPosition; } #pragma endregion #pragma region Virtual Override Methods - /// /// Makes this an original Preset in a different module than it was before. It severs ties deeply to the old module it was saved in. - /// - /// The ID of the new module. - /// Whether the migration was successful. If you tried to migrate to the same module it already was in, this would return false. + /// @param whichModule The ID of the new module. + /// @return Whether the migration was successful. If you tried to migrate to the same module it already was in, this would return false. virtual bool MigrateToModule(int whichModule); #pragma endregion #pragma region Groups - /// /// Gets the set of groups this is member of. - /// - /// A pointer to a list of strings which describes the groups this is added to. Ownership is NOT transferred! + /// @return A pointer to a list of strings which describes the groups this is added to. Ownership is NOT transferred! const std::unordered_set* GetGroups() const { return &m_Groups; } - /// /// Gets whether this is part of a specific group or not. - /// - /// A string which describes the group to check for. - /// Whether this Entity is in the specified group or not. + /// @param whichGroup A string which describes the group to check for. + /// @return Whether this Entity is in the specified group or not. bool IsInGroup(const std::string& whichGroup) const { return whichGroup == "None" ? false : (whichGroup == "All" || whichGroup == "Any" || m_Groups.contains(whichGroup)); } - /// /// Adds this Entity to a new grouping. - /// - /// A string which describes the group to add this to. Duplicates will be ignored. + /// @param newGroup A string which describes the group to add this to. Duplicates will be ignored. void AddToGroup(const std::string& newGroup) { m_Groups.emplace(newGroup); } - /// /// Removes this Entity from the specified grouping. - /// - /// A string which describes the group to remove this from. + /// @param groupToRemoveFrom A string which describes the group to remove this from. void RemoveFromGroup(const std::string& groupToRemoveFrom) { m_Groups.erase(groupToRemoveFrom); } - /// /// Returns random weight used in PresetMan::GetRandomBuyableOfGroupFromTech. - /// - /// This item's random weight from 0 to 100. + /// @return This item's random weight from 0 to 100. int GetRandomWeight() const { return m_RandomWeight; } #pragma endregion #pragma region Lua Script Handling - /// /// Reloads the Preset scripts of this Entity, from the same script file path as was originally defined. /// This will also update the original Preset in the PresetMan with the updated scripts so future objects spawned will use the new scripts. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int ReloadScripts() { return 0; } #pragma endregion #pragma region Operator Overloads - /// /// A stream insertion operator for sending a Entity to an output stream. - /// - /// An ostream reference as the left hand side operand. - /// A Entity reference as the right hand side operand. - /// An ostream reference for further use in an expression. + /// @param stream An ostream reference as the left hand side operand. + /// @param operand A Entity reference as the right hand side operand. + /// @return An ostream reference for further use in an expression. friend std::ostream& operator<<(std::ostream& stream, const Entity& operand) { stream << operand.GetPresetName() << ", " << operand.GetClassName(); return stream; } - /// /// A Reader extraction operator for filling an Entity from a Reader. - /// - /// A Reader reference as the left hand side operand. - /// An Entity reference as the right hand side operand. - /// A Reader reference for further use in an expression. + /// @param reader A Reader reference as the left hand side operand. + /// @param operand An Entity reference as the right hand side operand. + /// @return A Reader reference for further use in an expression. friend Reader& operator>>(Reader& reader, Entity& operand); - /// /// A Reader extraction operator for filling an Entity from a Reader. - /// - /// A Reader reference as the left hand side operand. - /// An Entity pointer as the right hand side operand. - /// A Reader reference for further use in an expression. + /// @param reader A Reader reference as the left hand side operand. + /// @param operand An Entity pointer as the right hand side operand. + /// @return A Reader reference for further use in an expression. friend Reader& operator>>(Reader& reader, Entity* operand); #pragma endregion #pragma region Class Info - /// /// Gets the ClassInfo instance of this Entity. - /// - /// A reference to the ClassInfo of this' class. + /// @return A reference to the ClassInfo of this' class. virtual const Entity::ClassInfo& GetClass() const { return m_sClass; } - /// /// Gets the class name of this Entity. - /// - /// A string with the friendly-formatted type name of this Entity. + /// @return A string with the friendly-formatted type name of this Entity. virtual const std::string& GetClassName() const { return m_sClass.GetName(); } #pragma endregion @@ -488,9 +378,7 @@ namespace RTE { Entity& operator=(const Entity& rhs) { return *this; } private: - /// /// Clears all the member variables of this Entity, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/System/GLCheck.h b/Source/System/GLCheck.h index 4e2038c7d0..8107c4eaeb 100644 --- a/Source/System/GLCheck.h +++ b/Source/System/GLCheck.h @@ -1,15 +1,11 @@ #ifndef _RTEGLCHECK_H_ #define _RTEGLCHECK_H_ -///< summary> /// Debug function to print GL errors to the console from: /// https : // stackoverflow.com/questions/11256470/define-a-macro-to-facilitate-opengl-command-debugging -/// void CheckOpenGLError(const char* stmt, const char* fname, int line); -///< summary> /// Debug macro to be used for all GL calls. -/// #ifdef DEBUG #define GL_CHECK(stmt) \ do { \ diff --git a/Source/System/Gamepad.h b/Source/System/Gamepad.h index 2fdb249593..5ba2faaa25 100644 --- a/Source/System/Gamepad.h +++ b/Source/System/Gamepad.h @@ -6,9 +6,7 @@ namespace RTE { - /// /// Structure for storing SDL_GameController or SDL_Joystick states. - /// struct Gamepad { int m_DeviceIndex = -1; //!< The SDL device index, used when reopening devices as SDL will attempt to reassign joysticks to the same index. SDL_JoystickID m_JoystickID = -1; //!< The joystick ID for event handling. @@ -17,42 +15,32 @@ namespace RTE { std::vector m_Buttons; //!< Array of button states. #pragma region Creation - /// /// Constructor method used to instantiate a Gamepad object in system memory and make it ready for use. - /// Gamepad() = default; - /// /// Constructor method used to instantiate a Gamepad object in system memory and make it ready for use. - /// - /// The SDL device index. - /// The joystick ID for event handling. - /// Number of analog axis. - /// Number of buttons. + /// @param deviceIndex The SDL device index. + /// @param id The joystick ID for event handling. + /// @param numAxis Number of analog axis. + /// @param numButtons Number of buttons. Gamepad(int deviceIndex, SDL_JoystickID id, int numAxis, int numButtons) : m_DeviceIndex(deviceIndex), m_JoystickID(id), m_Axis(numAxis), m_DigitalAxis(numAxis), m_Buttons(numButtons) {} #pragma endregion #pragma region Operator Overloads - /// /// Equality operator for testing if any two Gamepads are equal by ID. - /// - /// The ID to check equality with. - /// A boolean indicating whether the two operands are equal or not. + /// @param joystickID The ID to check equality with. + /// @return A boolean indicating whether the two operands are equal or not. bool operator==(int joystickID) const { return m_JoystickID == joystickID; } - /// /// Equality operator for testing if any two Gamepads are equal. - /// - /// A Gamepad reference as the right hand side operand. - /// A boolean indicating whether the two operands are equal or not. + /// @param rhs A Gamepad reference as the right hand side operand. + /// @return A boolean indicating whether the two operands are equal or not. bool operator==(const Gamepad& rhs) const { return m_JoystickID == rhs.m_JoystickID; } - /// /// Comparison operator for sorting Gamepads by ID. - /// - /// A Gamepad reference as the right hand side operand. - /// A boolean indicating the comparison result. + /// @param rhs A Gamepad reference as the right hand side operand. + /// @return A boolean indicating the comparison result. bool operator<(const Gamepad& rhs) const { return m_JoystickID < rhs.m_JoystickID; } }; } // namespace RTE diff --git a/Source/System/GenericSavedData.cpp b/Source/System/GenericSavedData.cpp index 539b420626..c0c18bd86d 100644 --- a/Source/System/GenericSavedData.cpp +++ b/Source/System/GenericSavedData.cpp @@ -9,8 +9,6 @@ namespace RTE { const std::string GenericSavedData::GenericSavedStrings::c_ClassName = "GenericSavedStrings"; const std::string GenericSavedData::GenericSavedNumbers::c_ClassName = "GenericSavedNumbers"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -21,8 +19,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::Save(Writer& writer) const { Serializable::Save(writer); @@ -33,8 +29,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GenericSavedData::SaveString(const std::string& key, const std::string& value) { if (value.length() == 0) { m_SavedEncodedStrings.m_Data[key] = value; @@ -69,8 +63,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::string& GenericSavedData::LoadString(const std::string& key) { const std::string* loadString = &m_SavedStrings.m_Data[key]; if (*loadString == "") { @@ -79,16 +71,12 @@ namespace RTE { return *loadString; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedEncodedStrings::ReadProperty(const std::string_view& propName, Reader& reader) { std::string value = reader.ReadPropValue(); m_Data[std::string(propName)] = base64_decode(value); // until we get P0919R2. return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedEncodedStrings::Save(Writer& writer) const { Serializable::Save(writer); @@ -101,15 +89,11 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedStrings::ReadProperty(const std::string_view& propName, Reader& reader) { m_Data[std::string(propName)] = reader.ReadPropValue(); // until we get P0919R2. return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedStrings::Save(Writer& writer) const { Serializable::Save(writer); @@ -121,8 +105,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedNumbers::ReadProperty(const std::string_view& propName, Reader& reader) { float value; reader >> value; @@ -130,8 +112,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int GenericSavedData::GenericSavedNumbers::Save(Writer& writer) const { Serializable::Save(writer); diff --git a/Source/System/GenericSavedData.h b/Source/System/GenericSavedData.h index 29a6022d3b..fe5e3c8c30 100644 --- a/Source/System/GenericSavedData.h +++ b/Source/System/GenericSavedData.h @@ -5,29 +5,21 @@ namespace RTE { - /// /// Helper class to save generic data. - /// class GenericSavedData : public Serializable { - /// /// Helper class to save generic encoded string data, that can safely include newlines and = and other characters INI doesn't like. - /// class GenericSavedEncodedStrings : public Serializable { public: SerializableClassNameGetter; SerializableOverrideMethods; - /// /// Constructor method used to instantiate a GenericSavedStrings object in system memory and make it ready for use. - /// GenericSavedEncodedStrings() = default; - /// /// Constructor method used to instantiate a GenericSavedEncodedStrings object to be identical to another, by deep copy, and make it ready for use. - /// - /// A reference to the GenericSavedEncodedStrings to deep copy. + /// @param reference A reference to the GenericSavedEncodedStrings to deep copy. GenericSavedEncodedStrings(const GenericSavedEncodedStrings& reference) = default; std::unordered_map m_Data; //!< Stored string data. @@ -36,24 +28,18 @@ namespace RTE { static const std::string c_ClassName; //!< A string with the friendly formatted type name of this object. }; - /// /// Helper class to save generic string data. - /// class GenericSavedStrings : public Serializable { public: SerializableClassNameGetter; SerializableOverrideMethods; - /// /// Constructor method used to instantiate a GenericSavedStrings object in system memory and make it ready for use. - /// GenericSavedStrings() = default; - /// /// Constructor method used to instantiate a GenericSavedStrings object to be identical to another, by deep copy, and make it ready for use. - /// - /// A reference to the GenericSavedStrings to deep copy. + /// @param reference A reference to the GenericSavedStrings to deep copy. GenericSavedStrings(const GenericSavedStrings& reference) = default; std::unordered_map m_Data; //!< Stored string data. @@ -62,24 +48,18 @@ namespace RTE { static const std::string c_ClassName; //!< A string with the friendly formatted type name of this object. }; - /// /// Helper class to save generic number data. - /// class GenericSavedNumbers : public Serializable { public: SerializableClassNameGetter; SerializableOverrideMethods; - /// /// Constructor method used to instantiate a GenericSavedNumbers object in system memory and make it ready for use. - /// GenericSavedNumbers() = default; - /// /// Constructor method used to instantiate a GenericSavedNumbers object to be identical to another, by deep copy, and make it ready for use. - /// - /// A reference to the GenericSavedNumbers to deep copy. + /// @param reference A reference to the GenericSavedNumbers to deep copy. GenericSavedNumbers(const GenericSavedNumbers& reference) = default; std::unordered_map m_Data; //!< Stored number data. @@ -92,15 +72,11 @@ namespace RTE { SerializableClassNameGetter; SerializableOverrideMethods; - /// /// Constructor method used to instantiate a GenericSavedData object in system memory and make it ready for use. - /// GenericSavedData() = default; - /// /// Constructor method used to instantiate a GenericSavedData object to be identical to another, by deep copy, and make it ready for use. - /// - /// A reference to the GenericSavedData to deep copy. + /// @param reference A reference to the GenericSavedData to deep copy. GenericSavedData(const GenericSavedData& reference) = default; void SaveString(const std::string& key, const std::string& value); diff --git a/Source/System/GraphicalPrimitive.cpp b/Source/System/GraphicalPrimitive.cpp index f677d62c01..a17cdce22b 100644 --- a/Source/System/GraphicalPrimitive.cpp +++ b/Source/System/GraphicalPrimitive.cpp @@ -27,8 +27,6 @@ namespace RTE { const GraphicalPrimitive::PrimitiveType TextPrimitive::c_PrimitiveType = PrimitiveType::Text; const GraphicalPrimitive::PrimitiveType BitmapPrimitive::c_PrimitiveType = PrimitiveType::Bitmap; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void GraphicalPrimitive::TranslateCoordinates(Vector targetPos, const Vector& scenePos, Vector& drawLeftPos, Vector& drawRightPos) const { drawLeftPos = scenePos; drawRightPos = scenePos; @@ -54,8 +52,6 @@ namespace RTE { drawRightPos.m_Y -= targetPos.m_Y; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void LinePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -75,8 +71,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ArcPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -105,8 +99,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SplinePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -138,8 +130,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BoxPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -159,8 +149,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BoxFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -180,8 +168,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RoundedBoxPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (m_StartPos.m_X > m_EndPos.m_X) { std::swap(m_StartPos.m_X, m_EndPos.m_X); @@ -232,8 +218,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RoundedBoxFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (m_StartPos.m_X > m_EndPos.m_X) { std::swap(m_StartPos.m_X, m_EndPos.m_X); @@ -278,8 +262,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CirclePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -295,8 +277,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void CircleFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -312,8 +292,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void EllipsePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -329,8 +307,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void EllipseFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart = m_StartPos - targetPos; @@ -346,8 +322,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TrianglePrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawPointA = m_PointAPos - targetPos; @@ -377,8 +351,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TriangleFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawPointA = m_PointAPos - targetPos; @@ -402,8 +374,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PolygonPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!g_SceneMan.SceneWrapsX() && !g_SceneMan.SceneWrapsY()) { Vector drawStart; @@ -428,8 +398,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PolygonFillPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { size_t drawPointsSize = m_Vertices.size() * 2; @@ -463,8 +431,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void TextPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (m_Text.empty()) { return; @@ -533,8 +499,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void BitmapPrimitive::Draw(BITMAP* drawScreen, const Vector& targetPos) { if (!m_Bitmap) { return; diff --git a/Source/System/GraphicalPrimitive.h b/Source/System/GraphicalPrimitive.h index 0307abc5aa..00c91ccb98 100644 --- a/Source/System/GraphicalPrimitive.h +++ b/Source/System/GraphicalPrimitive.h @@ -6,23 +6,17 @@ namespace RTE { #pragma region Graphical Primitive - /// /// Class used to schedule drawing of graphical primitives created from Lua. /// All coordinates passed to GraphicalPrimitive objects are Scene coordinates. - /// class GraphicalPrimitive { public: -/// /// Convenience macro to cut down on duplicate methods in classes that extend GraphicalPrimitive. -/// #define GraphicalPrimitiveOverrideMethods \ const PrimitiveType GetPrimitiveType() const override { return c_PrimitiveType; } \ void Draw(BITMAP* drawScreen, const Vector& targetPos) override; - /// /// Enumeration of the different primitive types derived from GraphicalPrimitive. - /// enum class PrimitiveType { None, // Not derived (base GraphicalPrimitive). Line, @@ -51,39 +45,30 @@ namespace RTE { DrawBlendMode m_BlendMode = DrawBlendMode::NoBlend; //!< The blending mode that will be used when drawing this primitive. std::array m_ColorChannelBlendAmounts = {BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend, BlendAmountLimits::MinBlend}; //!< The blending amount for each color channel when drawing in blended mode. - /// /// Destructor method used to clean up a GraphicalPrimitive object before deletion from system memory. - /// virtual ~GraphicalPrimitive() = default; - /// /// Translates coordinates from scene to this bitmap offset producing two coordinates. - /// - /// Target position. - /// Position on scene. - /// 'Left' position of bitmap on scene with negative values as if scene seam is 0,0. - /// 'Right' position of bitmap on scene with positive values. - /// + /// @param targetPos Target position. + /// @param scenePos Position on scene. + /// @param drawLeftPos 'Left' position of bitmap on scene with negative values as if scene seam is 0,0. + /// @param drawRightPos 'Right' position of bitmap on scene with positive values. + /// @remark /// Unfortunately it's hard to explain how this works. It tries to represent scene bitmap as two parts with center in 0,0. /// Right part is just plain visible part with coordinates from [0, scenewidth] and left part is imaginary bitmap as if we traversed it across the seam right-to-left with coordinates [0, -scenewidth]. /// So in order to be drawn each screen coordinates calculated twice for left and right 'bitmaps' and then one of them either flies away off-screen or gets drawn on the screen. /// When we cross the seam either left or right part is actually drawn in the bitmap, and negative coordinates of right part are compensated by view point offset coordinates when we cross the seam right to left. /// I really don't know how to make it simpler, because it has so many special cases and simply wrapping all out-of-the scene coordinates don't work because this way nothing will be ever draw across the seam. /// You're welcome to rewrite this nightmare if you can, I wasted a whole week on this (I can admit that I'm just too dumb for this) ))) - /// void TranslateCoordinates(Vector targetPos, const Vector& scenePos, Vector& drawLeftPos, Vector& drawRightPos) const; - /// /// Draws this primitive on provided bitmap. - /// - /// Bitmap to draw on. - /// Position of graphical primitive. + /// @param drawScreen Bitmap to draw on. + /// @param targetPos Position of graphical primitive. virtual void Draw(BITMAP* drawScreen, const Vector& targetPos) = 0; - /// /// Gets the type identifier of this primitive. - /// - /// The type identifier of this primitive. + /// @return The type identifier of this primitive. virtual const PrimitiveType GetPrimitiveType() const = 0; private: @@ -92,21 +77,17 @@ namespace RTE { #pragma endregion #pragma region Line Primitive - /// /// Class used to schedule drawing of line primitives created from Lua. - /// class LinePrimitive : public GraphicalPrimitive { public: GraphicalPrimitiveOverrideMethods; - /// /// Constructor method for LinePrimitive object. - /// - /// Player screen to draw this primitive on. - /// Start position of the primitive. - /// End position of the primitive. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param startPos Start position of the primitive. + /// @param end End position of the primitive. + /// @param color Color to draw this primitive with. LinePrimitive(int player, const Vector& startPos, const Vector& endPos, unsigned char color) { m_StartPos = startPos; m_EndPos = endPos; @@ -120,9 +101,7 @@ namespace RTE { #pragma endregion #pragma region Arc Primitive - /// /// Class used to schedule drawing of arc line primitives created from Lua. - /// class ArcPrimitive : public GraphicalPrimitive { public: @@ -133,15 +112,13 @@ namespace RTE { int m_Radius = 0; //!< Radius of the arc primitive. int m_Thickness = 0; //!< Thickness of the arc primitive in pixels. - /// /// Constructor method for ArcPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of this primitive's center. - /// The angle from which the arc drawing begins. - /// The angle at which the arc drawing ends. - /// Radius of the arc primitive. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param centerPos Position of this primitive's center. + /// @param startAngle The angle from which the arc drawing begins. + /// @param endAngle The angle at which the arc drawing ends. + /// @param radius Radius of the arc primitive. + /// @param color Color to draw this primitive with. ArcPrimitive(int player, const Vector& centerPos, float startAngle, float endAngle, int radius, int thickness, unsigned char color) : m_StartAngle(startAngle), m_EndAngle(endAngle), m_Radius(radius), m_Thickness(thickness) { @@ -156,9 +133,7 @@ namespace RTE { #pragma endregion #pragma region Spline Primitive - /// /// Class used to schedule drawing of spline primitives created from Lua. - /// class SplinePrimitive : public GraphicalPrimitive { public: @@ -167,15 +142,13 @@ namespace RTE { Vector m_GuidePointAPos; //!< A guide point that controls the curve of the spline. Vector m_GuidePointBPos; //!< A guide point that controls the curve of the spline. - /// /// Constructor method for SplinePrimitive object. - /// - /// Player screen to draw this primitive on. - /// Start position of the primitive. - /// The first guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. - /// The second guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. - /// End position of the primitive. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param startPos Start position of the primitive. + /// @param guideA The first guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. + /// @param guideB The second guide point that controls the curve of the spline. The spline won't necessarily pass through this point, but it will affect it's shape. + /// @param endPos End position of the primitive. + /// @param color Color to draw this primitive with. SplinePrimitive(int player, const Vector& startPos, const Vector& guideA, const Vector& guideB, const Vector& endPos, unsigned char color) : m_GuidePointAPos(guideA), m_GuidePointBPos(guideB) { @@ -191,21 +164,17 @@ namespace RTE { #pragma endregion #pragma region Box Primitive - /// /// Class used to schedule drawing of box primitives created from Lua. - /// class BoxPrimitive : public GraphicalPrimitive { public: GraphicalPrimitiveOverrideMethods; - /// /// Constructor method for BoxPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Start position of the primitive. Top left corner. - /// End position of the primitive. Bottom right corner. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param topLeftPos Start position of the primitive. Top left corner. + /// @param bottomRightPos End position of the primitive. Bottom right corner. + /// @param color Color to draw this primitive with. BoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { m_StartPos = topLeftPos; m_EndPos = bottomRightPos; @@ -219,21 +188,17 @@ namespace RTE { #pragma endregion #pragma region Filled Box Primitive - /// /// Class used to schedule drawing of filled box primitives created from Lua. - /// class BoxFillPrimitive : public GraphicalPrimitive { public: GraphicalPrimitiveOverrideMethods; - /// /// Constructor method for BoxFillPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Start position of the primitive. Top left corner. - /// End position of the primitive. Bottom right corner. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param topLeftPos Start position of the primitive. Top left corner. + /// @param bottomRightPos End position of the primitive. Bottom right corner. + /// @param color Color to draw this primitive with. BoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, unsigned char color) { m_StartPos = topLeftPos; m_EndPos = bottomRightPos; @@ -247,9 +212,7 @@ namespace RTE { #pragma endregion #pragma region Rounded Box Primitive - /// /// Class used to schedule drawing of box with round corners primitives created from Lua. - /// class RoundedBoxPrimitive : public GraphicalPrimitive { public: @@ -257,14 +220,12 @@ namespace RTE { int m_CornerRadius = 0; //!< The radius of the corners of the box. - /// /// Constructor method for RoundedBoxPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Start position of the primitive. Top left corner. - /// End position of the primitive. Bottom right corner. - /// The radius of the corners of the box. Smaller radius equals sharper corners. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param topLeftPos Start position of the primitive. Top left corner. + /// @param bottomRightPos End position of the primitive. Bottom right corner. + /// @param cornerRadius The radius of the corners of the box. Smaller radius equals sharper corners. + /// @param color Color to draw this primitive with. RoundedBoxPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) : m_CornerRadius(cornerRadius) { @@ -280,9 +241,7 @@ namespace RTE { #pragma endregion #pragma region Filled Rounded Box Primitive - /// /// Class used to schedule drawing of filled box with round corners primitives created from Lua. - /// class RoundedBoxFillPrimitive : public GraphicalPrimitive { public: @@ -290,14 +249,12 @@ namespace RTE { int m_CornerRadius = 0; //!< The radius of the corners of the box. - /// /// Constructor method for RoundedBoxFillPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Start position of the primitive. Top left corner. - /// End position of the primitive. Bottom right corner. - /// The radius of the corners of the box. Smaller radius equals sharper corners. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param topLeftPos Start position of the primitive. Top left corner. + /// @param bottomRightPos End position of the primitive. Bottom right corner. + /// @param cornerRadius The radius of the corners of the box. Smaller radius equals sharper corners. + /// @param color Color to draw this primitive with. RoundedBoxFillPrimitive(int player, const Vector& topLeftPos, const Vector& bottomRightPos, int cornerRadius, unsigned char color) : m_CornerRadius(cornerRadius) { @@ -313,9 +270,7 @@ namespace RTE { #pragma endregion #pragma region Cicrcle Primitive - /// /// Class used to schedule drawing of circle primitives created from Lua. - /// class CirclePrimitive : public GraphicalPrimitive { public: @@ -323,13 +278,11 @@ namespace RTE { int m_Radius = 0; //!< Radius of the circle primitive. - /// /// Constructor method for CirclePrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of this primitive's center. - /// Radius of the circle primitive. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param centerPos Position of this primitive's center. + /// @param radius Radius of the circle primitive. + /// @param color Color to draw this primitive with. CirclePrimitive(int player, const Vector& centerPos, int radius, unsigned char color) : m_Radius(radius) { @@ -344,9 +297,7 @@ namespace RTE { #pragma endregion #pragma region Filled Circle Primitive - /// /// Class used to schedule drawing of filled circle primitives created from Lua - /// class CircleFillPrimitive : public GraphicalPrimitive { public: @@ -354,13 +305,11 @@ namespace RTE { int m_Radius = 0; //!< Radius of the circle primitive. - /// /// Constructor method for CircleFillPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of this primitive's center. - /// Radius of the circle primitive. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param centerPos Position of this primitive's center. + /// @param radius Radius of the circle primitive. + /// @param color Color to draw this primitive with. CircleFillPrimitive(int player, const Vector& centerPos, int radius, unsigned char color) : m_Radius(radius) { @@ -375,9 +324,7 @@ namespace RTE { #pragma endregion #pragma region Ellipse Primitive - /// /// Class used to schedule drawing of ellipse primitives created from Lua. - /// class EllipsePrimitive : public GraphicalPrimitive { public: @@ -386,14 +333,12 @@ namespace RTE { int m_HorizRadius = 0; //!< The horizontal radius of the ellipse primitive. int m_VertRadius = 0; //!< The vertical radius of the ellipse primitive. - /// /// Constructor method for EllipsePrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of this primitive's center. - /// Horizontal radius of the ellipse primitive. - /// Vertical radius of the ellipse primitive. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param centerPos Position of this primitive's center. + /// @param horizRadius Horizontal radius of the ellipse primitive. + /// @param vertRadius Vertical radius of the ellipse primitive. + /// @param color Color to draw this primitive with. EllipsePrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) : m_HorizRadius(horizRadius), m_VertRadius(vertRadius) { @@ -408,9 +353,7 @@ namespace RTE { #pragma endregion #pragma region Filled Ellipse Primitive - /// /// Class used to schedule drawing of filled ellipse primitives created from Lua - /// class EllipseFillPrimitive : public GraphicalPrimitive { public: @@ -419,13 +362,11 @@ namespace RTE { int m_HorizRadius = 0; //!< The horizontal radius of the ellipse primitive. int m_VertRadius = 0; //!< The vertical radius of the ellipse primitive. - /// /// Constructor method for EllipseFillPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of this primitive's center. - /// Radius of the circle primitive. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param centerPos Position of this primitive's center. + /// @param radius Radius of the circle primitive. + /// @param color Color to draw this primitive with. EllipseFillPrimitive(int player, const Vector& centerPos, int horizRadius, int vertRadius, unsigned char color) : m_HorizRadius(horizRadius), m_VertRadius(vertRadius) { @@ -440,9 +381,7 @@ namespace RTE { #pragma endregion #pragma region Triangle Primitive - /// /// Class used to schedule drawing of triangle primitives created from Lua. - /// class TrianglePrimitive : public GraphicalPrimitive { public: @@ -452,14 +391,12 @@ namespace RTE { Vector m_PointBPos; //!< Second point of the triangle. Vector m_PointCPos; //!< Third point of the triangle. - /// /// Constructor method for TrianglePrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of the first point of the triangle. - /// Position of the second point of the triangle - /// Position of the third point of the triangle - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param pointA Position of the first point of the triangle. + /// @param pointB Position of the second point of the triangle + /// @param pointC Position of the third point of the triangle + /// @param color Color to draw this primitive with. TrianglePrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) : m_PointAPos(pointA), m_PointBPos(pointB), m_PointCPos(pointC) { @@ -473,9 +410,7 @@ namespace RTE { #pragma endregion #pragma region Filled Triangle Primitive - /// /// Class used to schedule drawing of filled triangle primitives created from Lua. - /// class TriangleFillPrimitive : public GraphicalPrimitive { public: @@ -485,14 +420,12 @@ namespace RTE { Vector m_PointBPos; //!< Second point of the triangle. Vector m_PointCPos; //!< Third point of the triangle. - /// /// Constructor method for TriangleFillPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of the first point of the triangle. - /// Position of the second point of the triangle - /// Position of the third point of the triangle - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param pointA Position of the first point of the triangle. + /// @param pointB Position of the second point of the triangle + /// @param pointC Position of the third point of the triangle + /// @param color Color to draw this primitive with. TriangleFillPrimitive(int player, const Vector& pointA, const Vector& pointB, const Vector& pointC, unsigned char color) : m_PointAPos(pointA), m_PointBPos(pointB), m_PointCPos(pointC) { @@ -506,9 +439,7 @@ namespace RTE { #pragma endregion #pragma region Polygon Primitive - /// /// Class used to schedule drawing of polygon primitives created from Lua. - /// class PolygonPrimitive : public GraphicalPrimitive { public: @@ -516,13 +447,11 @@ namespace RTE { std::vector m_Vertices = {}; //!< Positions of the vertices of the polygon, relative to the center position. - /// /// Constructor method for PolygonPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Start position of the primitive. - /// A vector containing the positions of the vertices of the polygon, relative to the center position. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param startPos Start position of the primitive. + /// @param vertices A vector containing the positions of the vertices of the polygon, relative to the center position. + /// @param color Color to draw this primitive with. PolygonPrimitive(int player, const Vector& startPos, unsigned char color, const std::vector& vertices) : m_Vertices(vertices) { @@ -537,9 +466,7 @@ namespace RTE { #pragma endregion #pragma region Filled Polygon Primitive - /// /// Class used to schedule drawing of filled polygon primitives created from Lua. - /// class PolygonFillPrimitive : public GraphicalPrimitive { public: @@ -547,13 +474,11 @@ namespace RTE { std::vector m_Vertices = {}; //!< Positions of the vertices of the polygon, relative to the center position. - /// /// Constructor method for PolygonFillPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Start position of the primitive. - /// A vector containing the positions of the vertices of the polygon, relative to the center position. - /// Color to draw this primitive with. + /// @param player Player screen to draw this primitive on. + /// @param startPos Start position of the primitive. + /// @param vertices A vector containing the positions of the vertices of the polygon, relative to the center position. + /// @param color Color to draw this primitive with. PolygonFillPrimitive(int player, const Vector& startPos, unsigned char color, const std::vector& vertices) : m_Vertices(vertices) { @@ -568,9 +493,7 @@ namespace RTE { #pragma endregion #pragma region Text Primitive - /// /// Class used to schedule drawing of text primitives created from Lua. - /// class TextPrimitive : public GraphicalPrimitive { public: @@ -581,15 +504,13 @@ namespace RTE { int m_Alignment = 0; //!< Alignment of text. float m_RotAngle = 0; //!< Angle to rotate text in radians. - /// /// Constructor method for TextPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of this primitive. - /// String containing text to draw. - /// Use small or large font. True for small font. - /// Alignment of text. - /// Angle to rotate text in radians. + /// @param player Player screen to draw this primitive on. + /// @param pos Position of this primitive. + /// @param text String containing text to draw. + /// @param isSmall Use small or large font. True for small font. + /// @param alignment Alignment of text. + /// @param rotAngle Angle to rotate text in radians. TextPrimitive(int player, const Vector& pos, const std::string& text, bool isSmall, int alignment, float rotAngle) : m_Text(text), m_IsSmall(isSmall), m_Alignment(alignment), m_RotAngle(rotAngle) { @@ -603,9 +524,7 @@ namespace RTE { #pragma endregion #pragma region Bitmap Primitive - /// /// Class used to schedule drawing of bitmap primitives created from Lua. - /// class BitmapPrimitive : public GraphicalPrimitive { public: @@ -616,15 +535,13 @@ namespace RTE { bool m_HFlipped = false; //!< Whether the Bitmap to draw should be horizontally flipped. bool m_VFlipped = false; //!< Whether the Bitmap to draw should be vertically flipped. - /// /// Constructor method for BitmapPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of this primitive's center. - /// BITMAP to draw. - /// Angle to rotate BITMAP in radians. - /// Whether the BITMAP to draw should be horizontally flipped. - /// Whether the BITMAP to draw should be vertically flipped. + /// @param player Player screen to draw this primitive on. + /// @param centerPos Position of this primitive's center. + /// @param bitmap BITMAP to draw. + /// @param rotAngle Angle to rotate BITMAP in radians. + /// @param hFlipped Whether the BITMAP to draw should be horizontally flipped. + /// @param vFlipped Whether the BITMAP to draw should be vertically flipped. BitmapPrimitive(int player, const Vector& centerPos, BITMAP* bitmap, float rotAngle, bool hFlipped, bool vFlipped) : m_Bitmap(bitmap), m_RotAngle(rotAngle), m_HFlipped(hFlipped), m_VFlipped(vFlipped) { @@ -632,16 +549,14 @@ namespace RTE { m_Player = player; } - /// /// Constructor method for BitmapPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of this primitive's center. - /// The MOSprite to get the BITMAP to draw from. - /// Angle to rotate BITMAP in radians. - /// Frame number of the MOSprite that will be drawn. - /// Whether the BITMAP to draw should be horizontally flipped. - /// Whether the BITMAP to draw should be vertically flipped. + /// @param player Player screen to draw this primitive on. + /// @param centerPos Position of this primitive's center. + /// @param moSprite The MOSprite to get the BITMAP to draw from. + /// @param rotAngle Angle to rotate BITMAP in radians. + /// @param frame Frame number of the MOSprite that will be drawn. + /// @param hFlipped Whether the BITMAP to draw should be horizontally flipped. + /// @param vFlipped Whether the BITMAP to draw should be vertically flipped. BitmapPrimitive(int player, const Vector& centerPos, const MOSprite* moSprite, float rotAngle, int frame, bool hFlipped, bool vFlipped) : m_Bitmap(moSprite->GetSpriteFrame(frame)), m_RotAngle(rotAngle), m_HFlipped(hFlipped), m_VFlipped(vFlipped) { @@ -649,15 +564,13 @@ namespace RTE { m_Player = player; } - /// /// Constructor method for BitmapPrimitive object. - /// - /// Player screen to draw this primitive on. - /// Position of this primitive's center. - /// The path to get the BITMAP to draw from. - /// Angle to rotate BITMAP in radians. - /// Whether the BITMAP to draw should be horizontally flipped. - /// Whether the BITMAP to draw should be vertically flipped. + /// @param player Player screen to draw this primitive on. + /// @param centerPos Position of this primitive's center. + /// @param filePath The path to get the BITMAP to draw from. + /// @param rotAngle Angle to rotate BITMAP in radians. + /// @param hFlipped Whether the BITMAP to draw should be horizontally flipped. + /// @param vFlipped Whether the BITMAP to draw should be vertically flipped. BitmapPrimitive(int player, const Vector& centerPos, const std::string& filePath, float rotAngle, bool hFlipped, bool vFlipped) : m_Bitmap(ContentFile(filePath.c_str()).GetAsBitmap()), m_RotAngle(rotAngle), m_HFlipped(hFlipped), m_VFlipped(vFlipped) { diff --git a/Source/System/InputMapping.cpp b/Source/System/InputMapping.cpp index cb2578c0c4..dfb66c6c1e 100644 --- a/Source/System/InputMapping.cpp +++ b/Source/System/InputMapping.cpp @@ -4,8 +4,6 @@ namespace RTE { const std::string InputMapping::c_ClassName = "InputMapping"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InputMapping::Clear() { m_PresetDescription.clear(); m_KeyMap = 0; @@ -16,8 +14,6 @@ namespace RTE { m_DirectionMap = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputMapping::Create(const InputMapping& reference) { m_KeyMap = reference.m_KeyMap; m_MouseButtonMap = reference.m_MouseButtonMap; @@ -29,8 +25,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputMapping::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -49,8 +43,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputMapping::Save(Writer& writer) const { Serializable::Save(writer); diff --git a/Source/System/InputMapping.h b/Source/System/InputMapping.h index 40bc2229de..ae49f29ee0 100644 --- a/Source/System/InputMapping.h +++ b/Source/System/InputMapping.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// A map between an input element and specific input device elements. - /// class InputMapping : public Serializable { public: @@ -15,108 +13,78 @@ namespace RTE { SerializableOverrideMethods; #pragma region Creation - /// /// Constructor method used to instantiate an InputMapping object in system memory. Create() should be called before using the object. - /// InputMapping() { Clear(); } - /// /// Creates an InputMapping to be identical to another, by deep copy. - /// - /// A reference to the InputMapping to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the InputMapping to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const InputMapping& reference); #pragma endregion #pragma region Destruction - /// /// Resets the entire InputMapping, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Gets the description of the input scheme preset that this element is part of, if any preset has been set for this element's scheme. - /// - /// The description associated with this element by the scheme preset, if any has been set. This string is empty otherwise. + /// @return The description associated with this element by the scheme preset, if any has been set. This string is empty otherwise. std::string GetPresetDescription() const { return m_PresetDescription; } - /// /// Sets the description of the input scheme preset that this element is part of, if any preset has been set for this element's scheme. - /// - /// The description associated with this element by the scheme preset, if any has been set. This string should be empty otherwise. + /// @param presetDescription The description associated with this element by the scheme preset, if any has been set. This string should be empty otherwise. void SetPresetDescription(const std::string& presetDescription) { m_PresetDescription = presetDescription; } #pragma endregion #pragma region Keyboard Getters and Setters - /// /// Gets the keyboard key mapping. - /// - /// The keyboard key this is mapped to. + /// @return The keyboard key this is mapped to. int GetKey() const { return m_KeyMap; } - /// /// Sets the keyboard button mapping. - /// - /// The scan code of the new key to map to. + /// @param newKey The scan code of the new key to map to. void SetKey(int newKey) { m_KeyMap = newKey; } #pragma endregion #pragma region Mouse Getters and Setters - /// /// Gets the mouse button mapping. - /// - /// The number of the mouse button this is mapped to. + /// @return The number of the mouse button this is mapped to. int GetMouseButton() const { return m_MouseButtonMap; } - /// /// Sets the mouse button mapping. - /// - /// The number of the mouse button this should be mapped to. + /// @param newButton The number of the mouse button this should be mapped to. void SetMouseButton(int newButton) { m_MouseButtonMap = newButton; } #pragma endregion #pragma region Joystick Getters and Setters - /// /// Shows whether this is mapped to a joy direction or not. - /// - /// Joy direction mapped or not. + /// @return Joy direction mapped or not. bool JoyDirMapped() const { return m_DirectionMapped; } - /// /// Gets the joystick stick number that this is mapped to. - /// - /// The direction, UInputMan::JOYDIR_ONE or UInputMan::JOYDIR_TWO. + /// @return The direction, UInputMan::JOYDIR_ONE or UInputMan::JOYDIR_TWO. int GetDirection() const { return m_DirectionMap; } - /// /// Sets the joystick direction mapping. - /// - /// The number of the axis this should be mapped to. - /// The number of the direction this should be mapped to. + /// @param newAxis The number of the axis this should be mapped to. + /// @param newDirection The number of the direction this should be mapped to. void SetDirection(int newAxis, int newDirection) { m_DirectionMapped = true; m_AxisMap = newAxis; m_DirectionMap = newDirection; } - /// /// Gets the joystick button mapping. - /// - /// The number of the joystick button this is mapped to. + /// @return The number of the joystick button this is mapped to. int GetJoyButton() const { return m_JoyButtonMap; } - /// /// Sets the joystick button mapping. - /// - /// The number of the joystick button this should be mapped to. + /// @param newButton The number of the joystick button this should be mapped to. void SetJoyButton(int newButton) { m_JoyButtonMap = newButton; } - /// /// Gets the joystick axis number that this is mapped to. - /// - /// The joystick axis number. + /// @return The joystick axis number. int GetAxis() const { return m_AxisMap; } #pragma endregion @@ -135,9 +103,7 @@ namespace RTE { private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - /// /// Clears all the member variables of this InputMapping, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/System/InputScheme.cpp b/Source/System/InputScheme.cpp index 3c8d4b19a1..e8092c95d9 100644 --- a/Source/System/InputScheme.cpp +++ b/Source/System/InputScheme.cpp @@ -5,8 +5,6 @@ namespace RTE { const std::string InputScheme::c_ClassName = "InputScheme"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InputScheme::Clear() { m_ActiveDevice = InputDevice::DEVICE_KEYB_ONLY; m_SchemePreset = InputPreset::NoPreset; @@ -19,8 +17,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputScheme::Create(const InputScheme& reference) { m_ActiveDevice = reference.m_ActiveDevice; m_SchemePreset = reference.m_SchemePreset; @@ -34,8 +30,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputScheme::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -75,8 +69,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int InputScheme::Save(Writer& writer) const { Serializable::Save(writer); @@ -120,8 +112,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InputScheme::ResetToPlayerDefaults(Players player) { switch (player) { case Players::PlayerOne: @@ -149,8 +139,6 @@ namespace RTE { m_DigitalAimSpeed = 1.0F; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InputScheme::SetPreset(InputPreset schemePreset) { m_SchemePreset = schemePreset; @@ -365,8 +353,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string InputScheme::GetMappingName(int whichElement) const { const InputMapping* inputElement = &(m_InputMappings.at(whichElement)); if (m_SchemePreset != InputScheme::InputPreset::NoPreset && !inputElement->GetPresetDescription().empty()) { @@ -404,8 +390,6 @@ namespace RTE { return ""; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool InputScheme::CaptureKeyMapping(int whichInput) { for (int whichKey = SDL_SCANCODE_A; whichKey < SDL_NUM_SCANCODES; ++whichKey) { // Don't allow mapping special keys used by UInputMan. @@ -421,8 +405,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool InputScheme::CaptureJoystickMapping(int whichJoy, int whichInput) { if (whichJoy < 0) { return false; diff --git a/Source/System/InputScheme.h b/Source/System/InputScheme.h index 8eef7b10d5..89efe8f8e4 100644 --- a/Source/System/InputScheme.h +++ b/Source/System/InputScheme.h @@ -6,18 +6,14 @@ namespace RTE { - /// /// A complete input configuration scheme description for a single player. - /// class InputScheme : public Serializable { public: SerializableClassNameGetter; SerializableOverrideMethods; - /// /// Enumeration for different input scheme presets. - /// enum InputPreset { NoPreset, PresetArrowKeys, @@ -32,151 +28,107 @@ namespace RTE { }; #pragma region Creation - /// /// Constructor method used to instantiate an InputScheme object in system memory. Create() should be called before using the object. - /// InputScheme() { Clear(); } - /// /// Creates an InputScheme to be identical to another, by deep copy. - /// - /// A reference to the InputScheme to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the InputScheme to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const InputScheme& reference); #pragma endregion #pragma region Destruction - /// /// Resets the entire InputScheme, including its inherited members, to their default settings or values. - /// void Reset() override { Clear(); } - /// /// Resets this InputScheme to the specified player's default input device and mappings. - /// - /// The preset player defaults this InputScheme should reset to. + /// @param player The preset player defaults this InputScheme should reset to. void ResetToPlayerDefaults(Players player); #pragma endregion #pragma region Getters and Setters - /// /// Gets the InputDevice that this scheme is using. - /// - /// The InputDevice of this scheme. See InputDevice enumeration. + /// @return The InputDevice of this scheme. See InputDevice enumeration. InputDevice GetDevice() const { return m_ActiveDevice; } - /// /// Sets the InputDevice this scheme is supposed to use. - /// - /// The InputDevice this scheme should use. See InputDevice enumeration. + /// @param activeDevice The InputDevice this scheme should use. See InputDevice enumeration. void SetDevice(InputDevice activeDevice = InputDevice::DEVICE_KEYB_ONLY) { m_ActiveDevice = activeDevice; } - /// /// Gets the InputPreset that this scheme is using. - /// - /// The InputPreset of this scheme. See InputPreset enumeration. + /// @return The InputPreset of this scheme. See InputPreset enumeration. InputPreset GetPreset() const { return m_SchemePreset; } - /// /// Sets up a specific preset scheme that is sensible and recommended. - /// - /// The preset number to set the scheme to match. See InputPreset enumeration. + /// @param schemePreset The preset number to set the scheme to match. See InputPreset enumeration. void SetPreset(InputPreset schemePreset = InputPreset::NoPreset); - /// /// Gets the InputMappings for this. - /// - /// The input mappings array, which is INPUT_COUNT large. + /// @return The input mappings array, which is INPUT_COUNT large. std::array* GetInputMappings() { return &m_InputMappings; } #pragma endregion #pragma region Input Mapping Getters and Setters - /// /// Gets the name of the key/mouse/joystick button/direction that a particular input element is mapped to. - /// - /// Which input element to look up. - /// A string with the appropriate clear text description of the mapped thing. + /// @param whichElement Which input element to look up. + /// @return A string with the appropriate clear text description of the mapped thing. std::string GetMappingName(int whichElement) const; - /// /// Gets which keyboard key is mapped to a specific input element. - /// - /// Which input element to look up. - /// Which keyboard key is mapped to the specified element. + /// @param whichInput Which input element to look up. + /// @return Which keyboard key is mapped to the specified element. int GetKeyMapping(int whichInput) const { return m_InputMappings.at(whichInput).GetKey(); } - /// /// Sets a keyboard key as mapped to a specific input element. - /// - /// Which input element to map to. - /// The scan code of which keyboard key to map to above input element. + /// @param whichInput Which input element to map to. + /// @param whichKey The scan code of which keyboard key to map to above input element. void SetKeyMapping(int whichInput, int whichKey) { m_InputMappings.at(whichInput).SetKey(whichKey); } - /// /// Gets which joystick button is mapped to a specific input element. - /// - /// Which input element to look up. - /// Which joystick button is mapped to the specified element. + /// @param whichInput Which input element to look up. + /// @return Which joystick button is mapped to the specified element. int GetJoyButtonMapping(int whichInput) const { return m_InputMappings.at(whichInput).GetJoyButton(); } - /// /// Sets a joystick button as mapped to a specific input element. - /// - /// Which input element to map to. - /// Which joystick button to map to the specified input element. + /// @param whichInput Which input element to map to. + /// @param whichButton Which joystick button to map to the specified input element. void SetJoyButtonMapping(int whichInput, int whichButton) { m_InputMappings.at(whichInput).SetJoyButton(whichButton); } - /// /// Get the deadzone value for this control scheme. - /// - /// Joystick dead zone from 0.0 to 1.0. + /// @return Joystick dead zone from 0.0 to 1.0. float GetJoystickDeadzone() const { return m_JoystickDeadzone; } - /// /// Set the deadzone value for this control scheme. - /// - /// Joystick dead zone from 0.0 to 1.0. + /// @param deadzoneValue Joystick dead zone from 0.0 to 1.0. void SetJoystickDeadzone(float deadzoneValue) { m_JoystickDeadzone = deadzoneValue; } - /// /// Get the DeadZoneType for this control scheme. - /// - /// The DeadZoneType this scheme is set to use. See DeadZoneType enumeration. + /// @return The DeadZoneType this scheme is set to use. See DeadZoneType enumeration. DeadZoneType GetJoystickDeadzoneType() const { return m_JoystickDeadzoneType; } - /// /// Set the DeadZoneType for this control scheme. - /// - /// The DeadZoneType this scheme should use. See DeadZoneType enumeration. + /// @param deadzoneType The DeadZoneType this scheme should use. See DeadZoneType enumeration. void SetJoystickDeadzoneType(DeadZoneType deadzoneType) { m_JoystickDeadzoneType = deadzoneType; } - /// /// Get the digital aim speed multiplier for this control scheme. - /// - /// The digital aim speed set to this scheme. + /// @return The digital aim speed set to this scheme. float GetDigitalAimSpeed() const { return m_DigitalAimSpeed; } #pragma endregion #pragma region Input Mapping Capture Handling - /// /// Clears all mappings for a specific input element. - /// - /// Which input element to clear all mappings of. + /// @param whichInput Which input element to clear all mappings of. void ClearMapping(int whichInput) { m_InputMappings.at(whichInput).Reset(); } - /// /// Checks for any key press this frame and creates an input mapping accordingly. - /// - /// Which input element to map for. - /// Whether there were any key presses this frame and therefore whether a mapping was successfully captured or not. + /// @param whichInput Which input element to map for. + /// @return Whether there were any key presses this frame and therefore whether a mapping was successfully captured or not. bool CaptureKeyMapping(int whichInput); - /// /// Checks for any button or direction press this frame and creates an input mapping accordingly. - /// - /// Which joystick to scan for button and stick presses. - /// Which input element to map for. - /// Whether there were any button or stick presses this frame and therefore whether a mapping was successfully captured or not. + /// @param whichJoy Which joystick to scan for button and stick presses. + /// @param whichInput Which input element to map for. + /// @return Whether there were any button or stick presses this frame and therefore whether a mapping was successfully captured or not. bool CaptureJoystickMapping(int whichJoy, int whichInput); #pragma endregion @@ -193,9 +145,7 @@ namespace RTE { private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this object. - /// /// Clears all the member variables of this InputScheme, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/System/Matrix.cpp b/Source/System/Matrix.cpp index a85df72cab..73aa25abc2 100644 --- a/Source/System/Matrix.cpp +++ b/Source/System/Matrix.cpp @@ -4,8 +4,6 @@ namespace RTE { const std::string Matrix::c_ClassName = "Matrix"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Matrix::Clear() { m_Rotation = 0; m_Flipped[X] = false; @@ -17,8 +15,6 @@ namespace RTE { m_Elements[1][1] = 1.0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Matrix::Create() { // Read all the properties if (Serializable::Create() < 0) { @@ -29,8 +25,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Matrix::Create(float angle) { m_Rotation = angle; m_ElementsUpdated = true; @@ -46,8 +40,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Matrix::Create(const Matrix& reference) { m_Rotation = reference.m_Rotation; m_Flipped[X] = reference.m_Flipped[X]; @@ -57,8 +49,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Matrix::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -72,8 +62,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Matrix::Save(Writer& writer) const { Serializable::Save(writer); @@ -82,8 +70,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Matrix::GetRadAngleTo(float otherAngle) const { // Rotate this' angle with the other angle so that the sought after difference angle is between the resulting angle and the x-axis float difference = otherAngle - GetRadAngle(); @@ -100,8 +86,6 @@ namespace RTE { return difference; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Matrix::GetDegAngleTo(float otherAngle) const { // Rotate this' angle with the other angle so that the sought after difference angle is between the resulting angle and the x-axis float difference = otherAngle - GetDegAngle(); @@ -118,8 +102,6 @@ namespace RTE { return difference; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Matrix& Matrix::operator=(const Matrix& rhs) { if (*this == rhs) { return *this; @@ -132,8 +114,6 @@ namespace RTE { return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector Matrix::operator*(const Vector& rhs) { if (!m_ElementsUpdated) { UpdateElements(); @@ -150,8 +130,6 @@ namespace RTE { return retVec; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector Matrix::operator/(const Vector& rhs) { if (!m_ElementsUpdated) { UpdateElements(); @@ -168,8 +146,6 @@ namespace RTE { return retVec; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Matrix Matrix::operator-() { m_Rotation = -m_Rotation; @@ -182,8 +158,6 @@ namespace RTE { return *this; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Matrix::UpdateElements() { // Negative angle to Account for upside-down coordinate system. const float CosAngle = std::cos(-m_Rotation); diff --git a/Source/System/Matrix.h b/Source/System/Matrix.h index d9a45f0190..5704d406b9 100644 --- a/Source/System/Matrix.h +++ b/Source/System/Matrix.h @@ -6,9 +6,7 @@ namespace RTE { - /// /// A 2x2 matrix to rotate 2D Vectors with. - /// class Matrix : public Serializable { public: @@ -21,250 +19,190 @@ namespace RTE { bool m_ElementsUpdated; //!< Whether the elements are currently updated to the set angle. #pragma region Creation - /// /// Constructor method used to instantiate a Matrix object. - /// Matrix() { Clear(); } - /// /// Constructor method used to instantiate a Matrix object from an angle. - /// - /// A float of an angle in radians that this Matrix should be set to represent. + /// @param radAng A float of an angle in radians that this Matrix should be set to represent. Matrix(float radAng) { Clear(); Create(radAng); } - /// /// Copy constructor method used to instantiate a Matrix object identical to an already existing one. - /// - /// A Matrix object which is passed in by reference. + /// @param reference A Matrix object which is passed in by reference. Matrix(const Matrix& reference) { Clear(); Create(reference); } - /// /// Makes the Matrix object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create() override; - /// /// Makes the Matrix object ready for use. - /// - /// The float angle in radians which this rotational matrix should represent. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param angle The float angle in radians which this rotational matrix should represent. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(float angle); - /// /// Creates a Matrix to be identical to another, by deep copy. - /// - /// A reference to the Matrix to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Matrix to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Matrix& reference); #pragma endregion #pragma region Destruction - /// /// Resets this Matrix to an identity Matrix, representing a 0 angle. - /// void Reset() { Clear(); } #pragma endregion #pragma region Getters and Setters - /// /// Gets whether or not this Matrix also mirrors the X component of any Vector it is multiplied with. - /// - /// A bool with the setting whether flipping the X components or not. + /// @return A bool with the setting whether flipping the X components or not. bool GetXFlipped() const { return m_Flipped[X]; } - /// /// Sets whether or not this Matrix should also mirror the X component of any Vector it is multiplied with. - /// - /// A bool with the setting whether to flip the X components or not. + /// @param flipX A bool with the setting whether to flip the X components or not. void SetXFlipped(bool flipX = true) { m_Flipped[X] = flipX; } - /// /// Gets whether or not this Matrix also mirrors the Y component of any Vector it is multiplied with. - /// - /// A bool with the setting whether flipping the Y components or not. + /// @return A bool with the setting whether flipping the Y components or not. bool GetYFlipped() const { return m_Flipped[Y]; } - /// /// Sets whether or not this Matrix should also mirror the Y component of any Vector it is multiplied with. - /// - /// A bool with the setting whether to flip the Y components or not. + /// @param flipY A bool with the setting whether to flip the Y components or not. void SetYFlipped(bool flipY = true) { m_Flipped[Y] = flipY; } - /// /// Returns the angle this rotational Matrix is currently representing. - /// - /// A float with the represented angle in radians. + /// @return A float with the represented angle in radians. float GetRadAngle() const { return m_Rotation; } - /// /// Sets the angle that this rotational Matrix should represent. - /// - /// A float with the new angle, in radians. + /// @param newAngle A float with the new angle, in radians. void SetRadAngle(float newAngle) { m_Rotation = newAngle; m_ElementsUpdated = false; } - /// /// Returns the angle this rotational Matrix is currently representing. - /// - /// A float with the represented angle in degrees. + /// @return A float with the represented angle in degrees. float GetDegAngle() const { return (m_Rotation / c_PI) * 180.0F; } - /// /// Sets the angle that this rotational Matrix should represent. - /// - /// A float with the new angle, in degrees. + /// @param newAngle A float with the new angle, in degrees. void SetDegAngle(float newAngle) { m_Rotation = (newAngle / 180.0F) * c_PI; m_ElementsUpdated = false; } - /// /// Returns the angle difference between what this is currently representing, to another angle in radians. /// It will wrap and normalize and give the smallest angle difference between this and the passed in. - /// - /// A float with the angle to get the difference to from this, in radians. - /// A float with the difference angle between this and the passed-in angle. + /// @param otherAngle A float with the angle to get the difference to from this, in radians. + /// @return A float with the difference angle between this and the passed-in angle. float GetRadAngleTo(float otherAngle) const; - /// /// Returns the angle difference between what this is currently representing, to another angle in degrees. /// It will wrap and normalize and give the smallest angle difference between this and the passed in. - /// - /// A float with the angle to get the difference to from this, in degrees. - /// A float with the difference angle between this and the passed-in angle. + /// @param otherAngle A float with the angle to get the difference to from this, in degrees. + /// @return A float with the difference angle between this and the passed-in angle. float GetDegAngleTo(float otherAngle) const; - /// /// Returns the angle this rotational Matrix is currently representing. - /// - /// A float with the represented angle as full rotations being 256. + /// @return A float with the represented angle as full rotations being 256. float GetAllegroAngle() const { return (m_Rotation / c_PI) * -128.0F; } #pragma endregion #pragma region Operator Overloads - /// /// An assignment operator for setting one Matrix equal to another. - /// - /// A Matrix reference. - /// A reference to the changed Matrix. + /// @param rhs A Matrix reference. + /// @return A reference to the changed Matrix. Matrix& operator=(const Matrix& rhs); - /// /// An assignment operator for setting one Matrix to represent an angle. - /// - /// A float in radians to set this rotational Matrix to. - /// A reference to the changed Matrix. + /// @param rhs A float in radians to set this rotational Matrix to. + /// @return A reference to the changed Matrix. Matrix& operator=(const float& rhs) { m_Rotation = rhs; m_ElementsUpdated = false; return *this; } - /// /// Unary negation overload for single Matrices. - /// - /// The resulting Matrix. + /// @return The resulting Matrix. Matrix operator-(); - /// /// An equality operator for testing if any two Matrices are equal. - /// - /// A Matrix reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A boolean indicating whether the two operands are equal or not. + /// @param lhs A Matrix reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A boolean indicating whether the two operands are equal or not. friend bool operator==(const Matrix& lhs, const Matrix& rhs) { return lhs.m_Rotation == rhs.m_Rotation; } - /// /// An inequality operator for testing if any two Matrices are unequal. - /// - /// A Matrix reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A boolean indicating whether the two operands are unequal or not. + /// @param lhs A Matrix reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A boolean indicating whether the two operands are unequal or not. friend bool operator!=(const Matrix& lhs, const Matrix& rhs) { return !operator==(lhs, rhs); } - /// /// Self-addition operator overload for a Matrix and a float. - /// - /// A float reference as the right hand side operand. - /// A reference to the resulting Matrix. + /// @param rhs A float reference as the right hand side operand. + /// @return A reference to the resulting Matrix. Matrix& operator+=(const float& rhs) { m_Rotation += rhs; m_ElementsUpdated = false; return *this; } - /// /// Self-addition operator overload for Matrices. - /// - /// A Matrix reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A reference to the resulting Matrix (the left one). + /// @param lhs A Matrix reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A reference to the resulting Matrix (the left one). friend Matrix& operator+=(Matrix& lhs, const Matrix& rhs) { lhs.m_Rotation += rhs.m_Rotation; lhs.m_ElementsUpdated = false; return lhs; } - /// /// Self-subtraction operator overload for a Matrix and a float. - /// - /// A float reference as the right hand side operand. - /// A reference to the resulting Matrix. + /// @param rhs A float reference as the right hand side operand. + /// @return A reference to the resulting Matrix. Matrix& operator-=(const float& rhs) { m_Rotation -= rhs; m_ElementsUpdated = false; return *this; } - /// /// Self-subtraction operator overload for Matrices. - /// - /// A Matrix reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A reference to the resulting Matrix (the left one). + /// @param lhs A Matrix reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A reference to the resulting Matrix (the left one). friend Matrix& operator-=(Matrix& lhs, const Matrix& rhs) { lhs.m_Rotation -= rhs.m_Rotation; lhs.m_ElementsUpdated = false; return lhs; } - /// /// Self-multiplication operator overload for a Matrix and a float. - /// - /// A float reference as the right hand side operand. - /// A reference to the resulting Matrix. + /// @param rhs A float reference as the right hand side operand. + /// @return A reference to the resulting Matrix. Matrix& operator*=(const float& rhs) { m_Rotation *= rhs; m_ElementsUpdated = false; return *this; } - /// /// Self-multiplication operator overload for Matrices. - /// - /// A Matrix reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A reference to the resulting Matrix (the left one). + /// @param lhs A Matrix reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A reference to the resulting Matrix (the left one). friend Matrix& operator*=(Matrix& lhs, const Matrix& rhs) { lhs.m_Rotation *= rhs.m_Rotation; lhs.m_ElementsUpdated = false; return lhs; } - /// /// self-division operator overload for a Matrix and a float. - /// - /// A float reference as the right hand side operand. - /// A reference to the resulting Matrix. + /// @param rhs A float reference as the right hand side operand. + /// @return A reference to the resulting Matrix. Matrix& operator/=(const float& rhs) { if (rhs) { m_Rotation /= rhs; @@ -273,12 +211,10 @@ namespace RTE { return *this; } - /// /// Self-division operator overload for Matrices. - /// - /// A Matrix reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A reference to the resulting Matrix (the left one). + /// @param lhs A Matrix reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A reference to the resulting Matrix (the left one). friend Matrix& operator/=(Matrix& lhs, const Matrix& rhs) { if (rhs.m_Rotation) { lhs.m_Rotation /= rhs.m_Rotation; @@ -287,68 +223,52 @@ namespace RTE { return lhs; } - /// /// Multiplication operator overload for a Matrix and a Vector. The vector will be transformed according to the Matrix's elements. /// Flipping, if set, is performed before rotating. - /// - /// A Vector reference as the right hand side operand. - /// The resulting transformed Vector. + /// @param rhs A Vector reference as the right hand side operand. + /// @return The resulting transformed Vector. Vector operator*(const Vector& rhs); - /// /// Multiplication operator overload for Vectors with Matrices. - /// - /// A Vector reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A reference to the resulting Vector. + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A reference to the resulting Vector. friend Vector operator*(const Vector& lhs, const Matrix& rhs) { Matrix m(rhs); return m * lhs; } - /// /// Division operator overload for a Matrix and a Vector. The vector will be transformed according to the Matrix's elements. - /// - /// A Vector reference as the right hand side operand. - /// The resulting transformed Vector. + /// @param rhs A Vector reference as the right hand side operand. + /// @return The resulting transformed Vector. Vector operator/(const Vector& rhs); - /// /// Division operator overload for Vector:s with Matrices. - /// - /// A Vector reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A reference to the resulting Vector. + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A reference to the resulting Vector. friend Vector operator/(const Vector& lhs, Matrix& rhs) { return rhs / lhs; } - /// /// Self-multiplication operator overload for Vector with a Matrix. - /// - /// A Vector reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A reference to the resulting Vector (the left one) + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A reference to the resulting Vector (the left one) friend Vector& operator*=(Vector& lhs, Matrix& rhs) { return lhs = rhs * lhs; } - /// /// Self-division operator overload for Vector with a Matrix. - /// - /// A Vector reference as the left hand side operand. - /// A Matrix reference as the right hand side operand. - /// A reference to the resulting Vector (the left one). + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Matrix reference as the right hand side operand. + /// @return A reference to the resulting Vector (the left one). friend Vector& operator/=(Vector& lhs, Matrix& rhs) { return lhs = rhs / lhs; } #pragma endregion private: static const std::string c_ClassName; //!< A string with the friendly-formatted type name of this. - /// /// Makes the elements of this matrix update to represent the set angle. - /// void UpdateElements(); - /// /// Clears all the member variables of this Matrix, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/System/NetworkMessages.h b/Source/System/NetworkMessages.h index dd9d61c50e..68f6582b84 100644 --- a/Source/System/NetworkMessages.h +++ b/Source/System/NetworkMessages.h @@ -6,9 +6,7 @@ namespace RTE { - /// /// Enumeration for the different types of network message IDs. - /// enum CustomMessageIDTypes { ID_CUSTOM_START = ID_USER_PACKET_ENUM, ID_NAT_SERVER_REGISTER_SERVER, @@ -42,9 +40,7 @@ namespace RTE { // TODO: Figure out all these and add comments. - /// /// - /// struct MsgRegisterServer { unsigned char Id; @@ -53,16 +49,12 @@ namespace RTE { char ServerGuid[64]; }; - /// /// - /// struct MsgRegisterServerAccepted { unsigned char Id; }; - /// /// - /// struct MsgGetServerRequest { unsigned char Id; @@ -70,25 +62,19 @@ namespace RTE { char ServerPassword[64]; }; - /// /// - /// struct MsgGetServerAnswer { unsigned char Id; char ServerGuid[64]; }; - /// /// - /// struct MsgGetServerNoAnswer { unsigned char Id; }; - /// /// - /// struct MsgRegister { unsigned char Id; @@ -98,9 +84,7 @@ namespace RTE { char Name[64]; }; - /// /// - /// struct MsgFrameSetup { unsigned char Id; unsigned char FrameNumber; @@ -118,9 +102,7 @@ namespace RTE { float OffsetY[c_MaxLayersStoredForNetwork]; }; - /// /// - /// struct MsgFrameLine { unsigned char Id; unsigned char FrameNumber; @@ -131,9 +113,7 @@ namespace RTE { unsigned short int UncompressedSize; }; - /// /// - /// struct MsgFrameBox { unsigned char Id; @@ -143,23 +123,17 @@ namespace RTE { unsigned short int DataSize; }; - /// /// - /// struct MsgDisconnect { unsigned char Id; }; - /// /// - /// struct MsgAccepted { unsigned char Id; }; - /// /// - /// struct LightweightSceneLayer { size_t BitmapHash; bool DrawTrans; @@ -185,9 +159,7 @@ namespace RTE { unsigned char FillDownColor; }; - /// /// - /// struct MsgSceneSetup { unsigned char Id; unsigned char SceneId; @@ -199,9 +171,7 @@ namespace RTE { LightweightSceneLayer BackgroundLayers[c_MaxLayersStoredForNetwork]; }; - /// /// - /// struct MsgSceneLine { unsigned char Id; unsigned char SceneId; @@ -213,23 +183,17 @@ namespace RTE { unsigned short int UncompressedSize; }; - /// /// - /// struct MsgSceneEnd { unsigned char Id; }; - /// /// - /// struct MsgSceneAccepted { unsigned char Id; }; - /// /// - /// struct MsgTerrainChange { unsigned char Id; @@ -244,9 +208,7 @@ namespace RTE { unsigned short int UncompressedSize; }; - /// /// - /// struct PostEffectNetworkData { short int X; short int Y; @@ -255,36 +217,28 @@ namespace RTE { float Angle; }; - /// /// - /// struct MsgPostEffects { unsigned char Id; unsigned char FrameNumber; int PostEffectsCount; }; - /// /// - /// struct MsgSoundEvents { unsigned char Id; unsigned char FrameNumber; int SoundEventsCount; }; - /// /// - /// struct MsgMusicEvents { unsigned char Id; unsigned char FrameNumber; int MusicEventsCount; }; - /// /// - /// struct MsgInput { unsigned char Id; diff --git a/Source/System/PathFinder.cpp b/Source/System/PathFinder.cpp index fc8e8c6d50..acd7c5c051 100644 --- a/Source/System/PathFinder.cpp +++ b/Source/System/PathFinder.cpp @@ -29,8 +29,6 @@ namespace RTE { // TODO: Enhance MicroPather to add that capability (or write our own pather)! thread_local float s_DigStrength = 0.0F; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PathNode::PathNode(const Vector& pos) : Pos(pos) { const Material* outOfBounds = g_SceneMan.GetMaterialFromID(MaterialColorKeys::g_MaterialOutOfBounds); @@ -40,15 +38,11 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PathFinder::Clear() { m_NodeGrid.clear(); m_NodeDimension = SCENEGRIDSIZE; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PathFinder::Create(int nodeDimension) { RTEAssert(g_SceneMan.GetScene(), "Scene doesn't exist or isn't loaded when creating PathFinder!"); @@ -112,14 +106,10 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PathFinder::Destroy() { Clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MicroPather* PathFinder::GetPather() { // TODO: cache a collection of pathers. For async pathfinding right now we create a new pather for every thread! if (!s_Pather.m_Instance || s_Pather.m_Instance->GetGraph() != this) { @@ -136,8 +126,6 @@ namespace RTE { return s_Pather.m_Instance; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PathFinder::CalculatePath(Vector start, Vector end, std::list& pathResult, float& totalCostResult, float digStrength) { ZoneScoped; @@ -205,8 +193,6 @@ namespace RTE { return result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::shared_ptr PathFinder::CalculatePathAsync(Vector start, Vector end, float digStrength, PathCompleteCallback callback) { std::shared_ptr pathRequest = std::make_shared(); @@ -235,8 +221,6 @@ namespace RTE { return pathRequest; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PathFinder::RecalculateAllCosts() { RTEAssert(g_SceneMan.GetScene(), "Scene doesn't exist or isn't loaded when recalculating PathFinder!"); @@ -253,8 +237,6 @@ namespace RTE { UpdateNodeList(pathNodesIdsVec); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector PathFinder::RecalculateAreaCosts(std::deque& boxList, int nodeUpdateLimit) { ZoneScoped; @@ -285,14 +267,10 @@ namespace RTE { return nodeVec; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float PathFinder::LeastCostEstimate(void* startState, void* endState) { return g_SceneMan.ShortestDistance((static_cast(startState))->Pos, (static_cast(endState))->Pos).GetMagnitude() / m_NodeDimension; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PathFinder::AdjacentCost(void* state, std::vector* adjacentList) { const PathNode* node = static_cast(state); micropather::StateCost adjCost; @@ -350,8 +328,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PathFinder::PositionsAreTheSamePathNode(const Vector& pos1, const Vector& pos2) const { int startNodeX = std::floor(pos1.m_X / static_cast(m_NodeDimension)); int startNodeY = std::floor(pos1.m_Y / static_cast(m_NodeDimension)); @@ -360,8 +336,6 @@ namespace RTE { return startNodeX == endNodeX && startNodeY == endNodeY; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float PathFinder::GetMaterialTransitionCost(const Material& material) const { float strength = material.GetIntegrity(); // Always treat doors as diggable. @@ -371,14 +345,10 @@ namespace RTE { return strength; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const Material* PathFinder::StrongestMaterialAlongLine(const Vector& start, const Vector& end) const { return g_SceneMan.CastMaxStrengthRayMaterial(start, end, 0, MaterialColorKeys::g_MaterialAir); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PathFinder::UpdateNodeCosts(PathNode* node) const { if (!node) { return false; @@ -426,8 +396,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector PathFinder::GetNodeIdsInBox(Box box) { std::vector result; @@ -452,8 +420,6 @@ namespace RTE { return result; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float PathFinder::GetNodeAverageTransitionCost(const PathNode& node) const { float totalCostOfAdjacentNodes = 0.0F; int count = 0; @@ -468,8 +434,6 @@ namespace RTE { return totalCostOfAdjacentNodes / std::max(static_cast(count), 1.0F); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PathFinder::UpdateNodeList(const std::vector& nodeVec) { ZoneScoped; @@ -513,8 +477,6 @@ namespace RTE { return anyChange; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PathFinder::MarkBoxNavigatable(Box box, bool navigatable) { std::vector pathNodesInBox = GetNodeIdsInBox(box); std::for_each( @@ -527,8 +489,6 @@ namespace RTE { }); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PathFinder::MarkAllNodesNavigatable(bool navigatable) { std::vector pathNodesIdsVec; pathNodesIdsVec.reserve(m_NodeGrid.size()); @@ -546,15 +506,11 @@ namespace RTE { }); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PathNode* PathFinder::GetPathNodeAtGridCoords(int x, int y) { int nodeId = ConvertCoordsToNodeId(x, y); return nodeId != -1 ? &m_NodeGrid[nodeId] : nullptr; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int PathFinder::ConvertCoordsToNodeId(int x, int y) { if (m_WrapsX) { x = x % m_GridWidth; diff --git a/Source/System/PathFinder.h b/Source/System/PathFinder.h index 284d131131..7485e0ae15 100644 --- a/Source/System/PathFinder.h +++ b/Source/System/PathFinder.h @@ -11,9 +11,7 @@ namespace RTE { class Scene; class Material; - /// /// Information required to make an async pathing request. - /// struct PathRequest { bool complete = false; int status = MicroPather::NO_SOLUTION; @@ -26,9 +24,7 @@ namespace RTE { using PathCompleteCallback = std::function)>; - /// /// Contains everything related to a PathNode on the path grid used by PathFinder. - /// struct PathNode { static constexpr int c_MaxAdjacentNodeCount = 8; //!< The maximum number of adjacent PathNodes to any given PathNode. Thusly, also the number of directions for PathNodes to be in. @@ -37,9 +33,7 @@ namespace RTE { bool m_Navigatable; //!< Whether this node can be navigated through. - /// /// Pointers to all adjacent PathNodes, in clockwise order with top first. These are not owned, and may be 0 if adjacent to non-wrapping scene border. - /// std::array AdjacentNodes; PathNode*& Up = AdjacentNodes[0]; PathNode*& UpRight = AdjacentNodes[1]; @@ -50,9 +44,7 @@ namespace RTE { PathNode*& Left = AdjacentNodes[6]; PathNode*& LeftUp = AdjacentNodes[7]; - /// /// The strongest material between us and our adjacent PathNodes, in clockwise order with top first. - /// std::array AdjacentNodeBlockingMaterials; const Material*& UpMaterial = AdjacentNodeBlockingMaterials[0]; const Material*& UpRightMaterial = AdjacentNodeBlockingMaterials[1]; @@ -63,152 +55,115 @@ namespace RTE { const Material*& LeftMaterial = AdjacentNodeBlockingMaterials[6]; const Material*& LeftUpMaterial = AdjacentNodeBlockingMaterials[7]; - /// /// Constructor method used to instantiate a PathNode object in system memory and make it ready for use. - /// - /// Absolute position of the center of the PathNode in the scene. + /// @param pos Absolute position of the center of the PathNode in the scene. explicit PathNode(const Vector& pos); }; - /// /// A class encapsulating and implementing the MicroPather A* pathfinding library. - /// class PathFinder : public Graph { public: #pragma region Creation - /// /// Constructor method used to instantiate a PathFinder object. - /// - /// The width and height in scene pixels that of each PathNode should represent. - /// The block size that the PathNode cache is allocated from. Should be about a fourth of the total number of PathNodes. + /// @param nodeDimension The width and height in scene pixels that of each PathNode should represent. + /// @param allocate The block size that the PathNode cache is allocated from. Should be about a fourth of the total number of PathNodes. PathFinder(int nodeDimension) { Clear(); Create(nodeDimension); } - /// /// Makes the PathFinder object ready for use. - /// - /// The width and height in scene pixels that of each PathNode should represent. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param nodeDimension The width and height in scene pixels that of each PathNode should represent. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(int nodeDimension); #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a PathFinder object before deletion. - /// ~PathFinder() override { Destroy(); } - /// /// Destroys and resets (through Clear()) this PathFinder object. - /// void Destroy(); - /// /// Resets the entire PathFinder object to the default settings or values. - /// void Reset() { Clear(); } #pragma endregion #pragma region PathFinding - /// /// Calculates and returns the least difficult path between two points on the current scene. /// This is synchronous, and will block the current thread! - /// - /// Start positions on the scene to find the path between. - /// End positions on the scene to find the path between. - /// A list which will be filled out with waypoints between the start and end. - /// The total minimum difficulty cost calculated between the two points on the scene. - /// What material strength the search is capable of digging through. - /// Success or failure, expressed as SOLVED, NO_SOLUTION, or START_END_SAME. + /// @param start Start positions on the scene to find the path between. + /// @param end End positions on the scene to find the path between. + /// @param pathResult A list which will be filled out with waypoints between the start and end. + /// @param totalCostResult The total minimum difficulty cost calculated between the two points on the scene. + /// @param digStrength What material strength the search is capable of digging through. + /// @return Success or failure, expressed as SOLVED, NO_SOLUTION, or START_END_SAME. int CalculatePath(Vector start, Vector end, std::list& pathResult, float& totalCostResult, float digStrength); - /// /// Calculates and returns the least difficult path between two points on the current scene. /// This is asynchronous and thus will not block the current thread. - /// - /// Start positions on the scene to find the path between. - /// End positions on the scene to find the path between. - /// What material strength the search is capable of digging through. - /// The callback function to be run when the path calculation is completed. - /// A shared pointer to the volatile PathRequest to be used to track whether the asynchronous path calculation has been completed, and check its results. + /// @param start Start positions on the scene to find the path between. + /// @param end End positions on the scene to find the path between. + /// @param digStrength What material strength the search is capable of digging through. + /// @param callback The callback function to be run when the path calculation is completed. + /// @return A shared pointer to the volatile PathRequest to be used to track whether the asynchronous path calculation has been completed, and check its results. std::shared_ptr CalculatePathAsync(Vector start, Vector end, float digStrength, PathCompleteCallback callback = nullptr); // /// Returns how many pathfinding requests are currently active. - /// - /// How many pathfinding requests are currently active. + /// @return How many pathfinding requests are currently active. int GetCurrentPathingRequests() const { return m_CurrentPathingRequests.load(); } - /// /// Recalculates all the costs between all the PathNodes by tracing lines in the material layer and summing all the material strengths for each encountered pixel. Also resets the pather itself. - /// void RecalculateAllCosts(); - /// /// Recalculates the costs between all the PathNodes touching a deque of specific rectangular areas (which will be wrapped). Also resets the pather itself, if necessary. - /// - /// The deque of Boxes representing the updated areas. - /// The maximum number of PathNodes we'll try to update this frame. True PathNode update count can be higher if we received a big box, as we always do at least 1 box. - /// The set of PathNode ids that were updated. + /// @param boxList The deque of Boxes representing the updated areas. + /// @param nodeUpdateLimit The maximum number of PathNodes we'll try to update this frame. True PathNode update count can be higher if we received a big box, as we always do at least 1 box. + /// @return The set of PathNode ids that were updated. std::vector RecalculateAreaCosts(std::deque& boxList, int nodeUpdateLimit); - /// /// Updates a set of PathNodes, adjusting their transitions. /// This does NOT update the pather, which is required if PathNode costs changed. - /// - /// The set of PathNode IDs to update. - /// Whether any PathNode costs changed. + /// @param nodeVec The set of PathNode IDs to update. + /// @return Whether any PathNode costs changed. bool UpdateNodeList(const std::vector& nodeVec); - /// /// Implementation of the abstract interface of Graph. /// Gets the least possible cost to get from PathNode A to B, if it all was air. - /// - /// Pointer to PathNode to start from. OWNERSHIP IS NOT TRANSFERRED! - /// PathNode to end up at. OWNERSHIP IS NOT TRANSFERRED! - /// The cost of the absolutely fastest possible way between the two points, as if traveled through air all the way. + /// @param startState Pointer to PathNode to start from. OWNERSHIP IS NOT TRANSFERRED! + /// @param endState PathNode to end up at. OWNERSHIP IS NOT TRANSFERRED! + /// @return The cost of the absolutely fastest possible way between the two points, as if traveled through air all the way. float LeastCostEstimate(void* startState, void* endState) override; - /// /// Implementation of the abstract interface of Graph. /// Gets the cost to go to any adjacent PathNode of the one passed in. - /// - /// Pointer to PathNode to get to cost of all adjacents for. OWNERSHIP IS NOT TRANSFERRED! - /// An empty vector which will be filled out with all the valid PathNodes adjacent to the one passed in. If at non-wrapping edge of seam, those non existent PathNodes won't be added. + /// @param state Pointer to PathNode to get to cost of all adjacents for. OWNERSHIP IS NOT TRANSFERRED! + /// @param adjacentList An empty vector which will be filled out with all the valid PathNodes adjacent to the one passed in. If at non-wrapping edge of seam, those non existent PathNodes won't be added. void AdjacentCost(void* state, std::vector* adjacentList) override; - /// /// Returns whether two position represent the same path nodes. - /// - /// First coordinates to compare. - /// Second coordinates to compare. - /// Whether both coordinates represent the same path node. + /// @param pos1 First coordinates to compare. + /// @param pos2 Second coordinates to compare. + /// @return Whether both coordinates represent the same path node. bool PositionsAreTheSamePathNode(const Vector& pos1, const Vector& pos2) const; - /// /// Marks a box as being navigatable or not. - /// - /// The Box of which all PathNodes that should have their navigatable status changed. - /// Whether or not the nodes in this box should be navigatable. + /// @param box The Box of which all PathNodes that should have their navigatable status changed. + /// @param navigatable Whether or not the nodes in this box should be navigatable. void MarkBoxNavigatable(Box box, bool navigatable); - /// /// Marks a box as being navigatable or not. - /// - /// The Box of which all PathNodes that should have their navigatable status changed. - /// Whether or not the nodes in this box should be navigatable. + /// @param box The Box of which all PathNodes that should have their navigatable status changed. + /// @param navigatable Whether or not the nodes in this box should be navigatable. void MarkAllNodesNavigatable(bool navigatable); #pragma endregion #pragma region Misc - /// /// Implementation of the abstract interface of Graph. This function is only used in DEBUG mode - it dumps output to stdout. /// Since void* aren't really human readable, this will print out some concise info without an ending newline. - /// - /// The state to print out info about. + /// @param state The state to print out info about. void PrintStateInfo(void* state) override {} #pragma endregion @@ -224,70 +179,52 @@ namespace RTE { bool m_WrapsY; //!< Whether the pathing grid wraps on the Y axis. std::atomic m_CurrentPathingRequests; //!< The number of active async pathing requests. - /// /// Gets the pather for this thread. Lazily-initialized for each new thread that needs a pather. - /// - /// The pather for this thread. + /// @return The pather for this thread. MicroPather* GetPather(); #pragma region Path Cost Updates - /// /// Helper function for getting the strongest material we need to path though between PathNodes. - /// - /// Origin point. - /// Destination point. - /// The strongest material. + /// @param start Origin point. + /// @param end Destination point. + /// @return The strongest material. const Material* StrongestMaterialAlongLine(const Vector& start, const Vector& end) const; - /// /// Helper function for updating all the values of cost edges going out from a specific PathNodes. /// This does NOT update the pather, which is required before solving more paths after calling this. - /// - /// The PathNode to update all costs of. It's safe to pass nullptr here. OWNERSHIP IS NOT TRANSFERRED! - /// Whether the PathNodes costs changed. + /// @param node The PathNode to update all costs of. It's safe to pass nullptr here. OWNERSHIP IS NOT TRANSFERRED! + /// @return Whether the PathNodes costs changed. bool UpdateNodeCosts(PathNode* node) const; - /// /// Helper function for getting the PathNode ids in a Box. - /// - /// The Box of which all PathNodes it touches should be returned. - /// A list of the PathNode ids inside the box. + /// @param box The Box of which all PathNodes it touches should be returned. + /// @return A list of the PathNode ids inside the box. std::vector GetNodeIdsInBox(Box box); - /// /// Gets the cost for transitioning through this Material. - /// - /// The Material to get the transition cost for. - /// The transition cost for the Material. + /// @param material The Material to get the transition cost for. + /// @return The transition cost for the Material. float GetMaterialTransitionCost(const Material& material) const; - /// /// Gets the average cost for all transitions out of this PathNode, ignoring infinities/unpathable transitions. - /// - /// The PathNode to get the average transition cost for. - /// The average transition cost. + /// @param node The PathNode to get the average transition cost for. + /// @return The average transition cost. float GetNodeAverageTransitionCost(const PathNode& node) const; #pragma endregion - /// /// Gets the PathNode at the given coordinates. - /// - /// The X coordinate, in PathNodes. - /// The Y coordinate, in PathNodes. - /// The PathNode at the given coordinates. + /// @param x The X coordinate, in PathNodes. + /// @param y The Y coordinate, in PathNodes. + /// @return The PathNode at the given coordinates. PathNode* GetPathNodeAtGridCoords(int x, int y); - /// /// Gets the PathNode id at the given coordinates. - /// - /// The X coordinate, in PathNodes. - /// The Y coordinate, in PathNodes. - /// The PathNode id at the given coordinates. + /// @param x The X coordinate, in PathNodes. + /// @param y The Y coordinate, in PathNodes. + /// @return The PathNode id at the given coordinates. int ConvertCoordsToNodeId(int x, int y); - /// /// Clears all the member variables of this PathFinder, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/System/PieQuadrant.cpp b/Source/System/PieQuadrant.cpp index 1c6b610d38..588a2cc88a 100644 --- a/Source/System/PieQuadrant.cpp +++ b/Source/System/PieQuadrant.cpp @@ -4,8 +4,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieQuadrant::Clear() { m_Enabled = true; m_Direction = Directions::None; @@ -19,8 +17,6 @@ namespace RTE { m_SlotsForPieSlices.fill(nullptr); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieQuadrant::Create(const PieQuadrant& reference, const Entity* oldOriginalPieSliceSourceToCheck, const Entity* newOriginalPieSliceSourceToSet) { m_Enabled = reference.m_Enabled; m_Direction = reference.m_Direction; @@ -33,8 +29,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector PieQuadrant::GetFlattenedPieSlices(bool inCCWOrder) const { std::vector pieSlices; if (inCCWOrder) { @@ -69,8 +63,6 @@ namespace RTE { return pieSlices; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void PieQuadrant::RealignPieSlices() { if ((m_RightPieSlices[0] && !m_LeftPieSlices[0]) || (m_RightPieSlices[1] && !m_LeftPieSlices[1])) { for (const PieSlice* pieSliceToRealign: GetFlattenedPieSlices()) { @@ -105,8 +97,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool PieQuadrant::AddPieSlice(PieSlice* pieSliceToAdd) { if (!pieSliceToAdd) { return false; @@ -128,8 +118,6 @@ namespace RTE { return sliceWasAdded; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - PieSlice* PieQuadrant::RemovePieSlice(const PieSlice* pieSliceToRemove) { if (pieSliceToRemove == m_MiddlePieSlice.get()) { return m_MiddlePieSlice.release(); diff --git a/Source/System/PieQuadrant.h b/Source/System/PieQuadrant.h index e3c666ed2e..22be3834b1 100644 --- a/Source/System/PieQuadrant.h +++ b/Source/System/PieQuadrant.h @@ -5,9 +5,7 @@ namespace RTE { - /// /// A quadrant in a PieMenu, for easy PieSlice organization. - /// class PieQuadrant { public: @@ -23,65 +21,47 @@ namespace RTE { std::array m_SlotsForPieSlices; //!< An array representing the slots in this PieQuadrant, via pointers to the PieSlices filling each slot. #pragma region Creation - /// /// Constructor method used to instantiate a PieQuadrant object in system memory and make it ready for use. - /// PieQuadrant() { Clear(); } - /// /// Creates a PieQuadrant to be identical to another, by deep copy. - /// - /// A reference to the PieQuadrant to deep copy. - /// A pointer to the old original source to check. This is generally the PieMenu the reference quadrant belongs to. - /// A pointer to the new original source to be set for PieSlices whose reference's original source is the old original source to check. This is generally the PieMenu this quadrant belongs to. + /// @param reference A reference to the PieQuadrant to deep copy. + /// @param oldOriginalPieSliceSourceToCheck A pointer to the old original source to check. This is generally the PieMenu the reference quadrant belongs to. + /// @param newOriginalPieSliceSourceToSet A pointer to the new original source to be set for PieSlices whose reference's original source is the old original source to check. This is generally the PieMenu this quadrant belongs to. void Create(const PieQuadrant& reference, const Entity* oldOriginalPieSliceSourceToCheck, const Entity* newOriginalPieSliceSourceToSet); #pragma endregion #pragma region Destruction - /// /// Resets this PieQuadrant and deletes all slices in it. - /// void Reset() { Clear(); } #pragma endregion #pragma region Getters - /// /// Gets whether or not this PieQuadrant contains the given PieSlice. - /// - /// Whether or not this PieQuadrant contains the given PieSlice. + /// @return Whether or not this PieQuadrant contains the given PieSlice. bool ContainsPieSlice(const PieSlice* sliceToCheck) const { return sliceToCheck == m_MiddlePieSlice.get() || sliceToCheck == m_LeftPieSlices[0].get() || sliceToCheck == m_RightPieSlices[0].get() || sliceToCheck == m_LeftPieSlices[1].get() || sliceToCheck == m_RightPieSlices[1].get(); } - /// /// Gets a vector of non-owning pointers to the PieSlices in this PieQuadrant. /// The vector of PieSlices will default to INI order, i.e. starting with the middle and interleaving left and right slices in order. /// Alternatively it can go in CCW order, getting the outermost right slice and moving inwards through the middle and then left slices. - /// - /// Whether to get flattened slices in counter-clockwise order. Defaults to false. + /// @param inCCWOrder Whether to get flattened slices in counter-clockwise order. Defaults to false. std::vector GetFlattenedPieSlices(bool inCCWOrder = false) const; #pragma endregion #pragma region Concrete Methods - /// /// Balances this PieQuadrant be removing and re-adding all PieSlices if needed, then aligns all PieSlices in this PieQuadrant, setting their angle and size details. - /// void RealignPieSlices(); - /// /// Adds the PieSlice to the quadrant. PieSlices are added to the middle, then the left side, then the right side so things fill evenly. Ownership IS transferred! - /// bool AddPieSlice(PieSlice* pieSliceToAdd); - /// /// Removes the passed in PieSlice from this PieQuadrant. Ownership IS transferred to the caller! - /// - /// The PieSlice to be removed from this PieQuadrant. Ownership IS transferred to the caller! + /// @param pieSliceToRemove The PieSlice to be removed from this PieQuadrant. Ownership IS transferred to the caller! PieSlice* RemovePieSlice(const PieSlice* pieSliceToRemove); #pragma endregion private: - /// /// Clears all the member variables of this PieQuadrant. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/System/RTEError.cpp b/Source/System/RTEError.cpp index 4fc5c9d5f6..bd805a1d66 100644 --- a/Source/System/RTEError.cpp +++ b/Source/System/RTEError.cpp @@ -20,8 +20,6 @@ namespace RTE { std::string RTEError::s_LastIgnoredAssertDescription = ""; std::source_location RTEError::s_LastIgnoredAssertLocation = {}; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - #ifdef _WIN32 /// /// Custom exception handler for Windows SEH. @@ -146,8 +144,6 @@ namespace RTE { } #endif - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::SetExceptionHandlers() { // Basic handling for C++ exceptions. Doesn't give us much meaningful information. [[maybe_unused]] static const std::terminate_handler terminateHandler = []() { @@ -179,14 +175,10 @@ namespace RTE { #endif } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::ShowMessageBox(const std::string& message) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "RTE Warning! (>_<)", message.c_str(), nullptr); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool RTEError::ShowAbortMessageBox(const std::string& message) { enum AbortMessageButton { ButtonInvalid, @@ -220,8 +212,6 @@ namespace RTE { return pressedButton == AbortMessageButton::ButtonRestart; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool RTEError::ShowAssertMessageBox(const std::string& message) { enum AssertMessageButton { ButtonInvalid, @@ -254,8 +244,6 @@ namespace RTE { return pressedButton == AssertMessageButton::ButtonAbort; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::UnhandledExceptionFunc(const std::string& description, const std::string& callstack) { s_CurrentlyAborting = true; @@ -308,8 +296,6 @@ namespace RTE { AbortAction; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::AbortFunc(const std::string& description, const std::source_location& srcLocation) { s_CurrentlyAborting = true; @@ -375,8 +361,6 @@ namespace RTE { AbortAction; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::AssertFunc(const std::string& description, const std::source_location& srcLocation) { if (System::IsInExternalModuleValidationMode()) { AbortFunc(description, srcLocation); @@ -413,8 +397,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool RTEError::DumpAbortScreen() { int success = -1; if (glReadPixels != nullptr) { @@ -443,8 +425,6 @@ namespace RTE { return success == 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool RTEError::DumpAbortSave() { bool success = false; if (g_ActivityMan.GetActivity() && g_ActivityMan.GetActivity()->CanBeUserSaved()) { @@ -453,8 +433,6 @@ namespace RTE { return success; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEError::FormatFunctionSignature(std::string& symbolName) { // TODO: Expand this with more dumb signatures, or make something that makes more sense. static const std::array, 3> stlSigs{{{std::regex("( >)"), ">"}, diff --git a/Source/System/RTEError.h b/Source/System/RTEError.h index 28823a7c46..5302f4e304 100644 --- a/Source/System/RTEError.h +++ b/Source/System/RTEError.h @@ -20,9 +20,7 @@ namespace RTE { - /// /// Class for runtime error handling. - /// class RTEError { public: @@ -31,69 +29,49 @@ namespace RTE { static std::string s_LastIgnoredAssertDescription; //!< The last ignored assert message. static std::source_location s_LastIgnoredAssertLocation; //!< The last ignored assert call site. - /// /// Sets custom handlers for C++ and platform specific exceptions. - /// static void SetExceptionHandlers(); - /// /// Pops up a message box dialog in the OS. For debug purposes mostly. - /// - /// The string that the message box should display. + /// @param message The string that the message box should display. static void ShowMessageBox(const std::string& message); - /// /// Abort on unhandled exception function. Will try save the current game, to dump a screenshot, dump the console log and show an abort message. Then quit the program immediately. - /// - /// Message explaining the exception. - /// The call stack in string form. + /// @param description Message explaining the exception. + /// @param callstack The call stack in string form. static void UnhandledExceptionFunc(const std::string& description, const std::string& callstack = ""); - /// /// Abort on Error function. Will try save the current game, to dump a screenshot, dump the console log and show an abort message. Then quit the program immediately. - /// - /// Message explaining the reason for aborting. - /// std::source_location corresponding to the location of the call site. + /// @param description Message explaining the reason for aborting. + /// @param srcLocation std::source_location corresponding to the location of the call site. [[noreturn]] static void AbortFunc(const std::string& description, const std::source_location& srcLocation); - /// /// An assert, which will prompt to abort or ignore it. - /// - /// The description of the assertion. - /// std::source_location corresponding to the location of the call site. + /// @param description The description of the assertion. + /// @param srcLocation std::source_location corresponding to the location of the call site. static void AssertFunc(const std::string& description, const std::source_location& srcLocation); - /// /// Formats function signatures so they're slightly more sane. - /// - /// Reference to the function signature to format. + /// @param funcSig Reference to the function signature to format. static void FormatFunctionSignature(std::string& funcSig); private: - /// /// Pops up the abort message box dialog in the OS, notifying the user about a runtime error. - /// - /// The string that the message box should display. - /// Whether to restart the game by launching a new instance, or proceed to exit. + /// @param message The string that the message box should display. + /// @return Whether to restart the game by launching a new instance, or proceed to exit. static bool ShowAbortMessageBox(const std::string& message); - /// /// Pops up the assert message box dialog in the OS, notifying the user about a runtime error. - /// - /// The string that the message box should display. - /// Whether to abort, or ignore the assert and continue execution. + /// @param message The string that the message box should display. + /// @return Whether to abort, or ignore the assert and continue execution. static bool ShowAssertMessageBox(const std::string& message); - /// /// Saves the current frame to a file. - /// - /// Whether the file was saved successfully. + /// @return Whether the file was saved successfully.@return static bool DumpAbortScreen(); - /// /// Attempts to save the current running Activity, so the player can hopefully resume where they were. - /// - /// Whether the Activity was saved successfully. + /// @return Whether the Activity was saved successfully. static bool DumpAbortSave(); }; diff --git a/Source/System/RTEStackTrace.cpp b/Source/System/RTEStackTrace.cpp index b47fb95b95..b669be718f 100644 --- a/Source/System/RTEStackTrace.cpp +++ b/Source/System/RTEStackTrace.cpp @@ -2,8 +2,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string RTEStackTrace::GetCallStackAsString(const HANDLE& handle, const CONTEXT* context) { m_CallstackStream.clear(); @@ -17,8 +15,6 @@ namespace RTE { return m_CallstackStream.str(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void RTEStackTrace::OnOutput(LPCSTR text) { this->StackWalker::OnOutput(text); m_CallstackStream << text; diff --git a/Source/System/RTEStackTrace.h b/Source/System/RTEStackTrace.h index dbc5d8bf40..cf759364c6 100644 --- a/Source/System/RTEStackTrace.h +++ b/Source/System/RTEStackTrace.h @@ -5,38 +5,28 @@ namespace RTE { - /// /// Class for dumping stack traces on Windows. /// Wraps the StackWalker class which uses WinAPI Debug Help Library routines. - /// class RTEStackTrace : public StackWalker { public: - /// /// Constructor method used to instantiate an RTEStackTrace object in system memory and make it ready for use. - /// - /// + /// @param options RTEStackTrace() : StackWalker() {} - /// /// Destructor method used to clean up a RTEStackTrace object before deletion from system memory. - /// ~RTEStackTrace() override = default; - /// /// Gets the current call stack as a string. - /// - /// Handle to the current process. If none provided will get the current thread handle. - /// Register data. If none provided will get it from the caller. - /// A string with the call stack. + /// @param handle Handle to the current process. If none provided will get the current thread handle. + /// @param context Register data. If none provided will get it from the caller. + /// @return A string with the call stack. std::string GetCallStackAsString(const HANDLE& handle = nullptr, const CONTEXT* context = nullptr); protected: - /// /// Redirects the output string to the member string stream. - /// - /// The output string. Provided by the base class method. + /// @param text The output string. Provided by the base class method. void OnOutput(LPCSTR text) override; private: diff --git a/Source/System/RTETools.cpp b/Source/System/RTETools.cpp index efeef0812d..149c574f73 100644 --- a/Source/System/RTETools.cpp +++ b/Source/System/RTETools.cpp @@ -6,8 +6,6 @@ namespace RTE { RandomGenerator g_RandomGenerator; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SeedRNG() { // Use a constant seed for determinism. static constexpr uint32_t constSeed = []() { @@ -29,8 +27,6 @@ namespace RTE { g_RandomGenerator.Seed(constSeed); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float LERP(float scaleStart, float scaleEnd, float startValue, float endValue, float progressScalar) { if (progressScalar <= scaleStart) { return startValue; @@ -40,8 +36,6 @@ namespace RTE { return startValue + ((progressScalar - scaleStart) * ((endValue - startValue) / (scaleEnd - scaleStart))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float EaseIn(float start, float end, float progressScalar) { if (progressScalar <= 0) { return start; @@ -52,8 +46,6 @@ namespace RTE { return (end - start) * (std::sin(-t * c_HalfPI) + 1) + start; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float EaseOut(float start, float end, float progressScalar) { if (progressScalar <= 0) { return start; @@ -63,14 +55,10 @@ namespace RTE { return (end - start) * -std::sin(-progressScalar * c_HalfPI) + start; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float EaseInOut(float start, float end, float progressScalar) { return start * (2 * std::pow(progressScalar, 3) - 3 * std::pow(progressScalar, 2) + 1) + end * (3 * std::pow(progressScalar, 2) - 2 * std::pow(progressScalar, 3)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Clamp(float& value, float upperLimit, float lowerLimit) { // Straighten out the limits if (upperLimit < lowerLimit) { @@ -89,8 +77,6 @@ namespace RTE { return false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float Limit(float value, float upperLimit, float lowerLimit) { // Straighten out the limits if (upperLimit < lowerLimit) { @@ -108,8 +94,6 @@ namespace RTE { return value; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float NormalizeAngleBetween0And2PI(float angle) { while (angle < 0) { angle += c_TwoPI; @@ -117,8 +101,6 @@ namespace RTE { return (angle > c_TwoPI) ? fmodf(angle + c_TwoPI, c_TwoPI) : angle; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float NormalizeAngleBetweenNegativePIAndPI(float angle) { while (angle < 0) { angle += c_TwoPI; @@ -126,8 +108,6 @@ namespace RTE { return (angle > c_PI) ? fmodf(angle + c_PI, c_TwoPI) - c_PI : angle; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool AngleWithinRange(float angleToCheck, float startAngle, float endAngle) { angleToCheck = NormalizeAngleBetween0And2PI(angleToCheck); startAngle = NormalizeAngleBetween0And2PI(startAngle); @@ -136,8 +116,6 @@ namespace RTE { return endAngle >= startAngle ? (angleToCheck >= startAngle && angleToCheck <= endAngle) : (angleToCheck >= startAngle || angleToCheck <= endAngle); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float ClampAngle(float angleToClamp, float startAngle, float endAngle) { angleToClamp = NormalizeAngleBetween0And2PI(angleToClamp); @@ -153,20 +131,14 @@ namespace RTE { return angleToClamp; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool WithinBox(const Vector& point, float left, float top, float right, float bottom) { return point.m_X >= left && point.m_X < right && point.m_Y >= top && point.m_Y < bottom; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool WithinBox(const Vector& point, const Vector& boxPos, float width, float height) { return point.m_X >= boxPos.m_X && point.m_X < (boxPos.m_X + width) && point.m_Y >= boxPos.m_Y && point.m_Y < (boxPos.m_Y + height); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string RoundFloatToPrecision(float input, int precision, int roundingMode) { if (roundingMode == 0) { std::stringstream floatStream; @@ -201,8 +173,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // From https://stackoverflow.com/a/66764681, under license https://creativecommons.org/licenses/by-sa/4.0/. Minor modifications uint64_t Hash(const std::string& text) { constexpr uint64_t fnv_prime = 1099511628211ULL; @@ -218,8 +188,6 @@ namespace RTE { return hash; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string GetCaseInsensitiveFullPath(const std::string& fullPath) { if (std::filesystem::exists(fullPath)) { return fullPath; diff --git a/Source/System/RTETools.h b/Source/System/RTETools.h index e69f66454b..16c504d948 100644 --- a/Source/System/RTETools.h +++ b/Source/System/RTETools.h @@ -15,53 +15,41 @@ namespace RTE { std::mt19937 m_RNG; //!< The random number generator used for all random functions. public: - /// /// Seed the random number generator. - /// void Seed(uint64_t seed) { m_RNG.seed(seed); }; - /// /// Function template which returns a uniformly distributed random number in the range [-1, 1]. - /// - /// Uniformly distributed random number in the range [-1, 1]. + /// @return Uniformly distributed random number in the range [-1, 1]. template typename std::enable_if::value, floatType>::type RandomNormalNum() { return std::uniform_real_distribution(floatType(-1.0), std::nextafter(floatType(1.0), std::numeric_limits::max()))(m_RNG); } - /// /// Function template specialization for int types which returns a uniformly distributed random number in the range [-1, 1]. - /// - /// Uniformly distributed random number in the range [-1, 1]. + /// @return Uniformly distributed random number in the range [-1, 1]. template typename std::enable_if::value, intType>::type RandomNormalNum() { return std::uniform_int_distribution(intType(-1), intType(1))(m_RNG); } - /// /// Function template which returns a uniformly distributed random number in the range [0, 1]. - /// - /// Uniformly distributed random number in the range [0, 1]. + /// @return Uniformly distributed random number in the range [0, 1]. template typename std::enable_if::value, floatType>::type RandomNum() { return std::uniform_real_distribution(floatType(0.0), std::nextafter(floatType(1.0), std::numeric_limits::max()))(m_RNG); } - /// /// Function template specialization for int types which returns a uniformly distributed random number in the range [0, 1]. - /// - /// Uniformly distributed random number in the range [0, 1]. + /// @return Uniformly distributed random number in the range [0, 1]. template typename std::enable_if::value, intType>::type RandomNum() { return std::uniform_int_distribution(intType(0), intType(1))(m_RNG); } - /// /// Function template which returns a uniformly distributed random number in the range [min, max]. - /// - /// Lower boundary of the range to pick a number from. - /// Upper boundary of the range to pick a number from. - /// Uniformly distributed random number in the range [min, max]. + /// @param min Lower boundary of the range to pick a number from. + /// @param max Upper boundary of the range to pick a number from. + /// @return Uniformly distributed random number in the range [min, max]. template typename std::enable_if::value, floatType>::type RandomNum(floatType min, floatType max) { if (max < min) { @@ -70,12 +58,10 @@ namespace RTE { return (std::uniform_real_distribution(floatType(0.0), std::nextafter(max - min, std::numeric_limits::max()))(m_RNG) + min); } - /// /// Function template specialization for int types which returns a uniformly distributed random number in the range [min, max]. - /// - /// Lower boundary of the range to pick a number from. - /// Upper boundary of the range to pick a number from. - /// Uniformly distributed random number in the range [min, max]. + /// @param min Lower boundary of the range to pick a number from. + /// @param max Upper boundary of the range to pick a number from. + /// @return Uniformly distributed random number in the range [min, max]. template typename std::enable_if::value, intType>::type RandomNum(intType min, intType max) { if (max < min) { @@ -87,9 +73,7 @@ namespace RTE { extern RandomGenerator g_RandomGenerator; //!< The global random number generator used in our simulation thread. - /// /// Seed global the global random number generators. - /// void SeedRNG(); // TODO: Maybe remove these passthrough functions and force the user to manually specify if they want the simulation thread random, @@ -126,200 +110,158 @@ namespace RTE { #pragma endregion #pragma region Interpolation - /// /// Simple Linear Interpolation, with an added bonus: scaleStart and scaleEnd let you define your scale, where 0 and 1 would be standard scale. /// This scale is used to normalize your progressScalar value and LERP accordingly. - /// - /// The start of the scale to LERP along. - /// The end of the scale to LERP along. - /// The start value of your LERP. - /// The end value of your LERP. - /// How far your LERP has progressed. Automatically normalized through use of scaleStart and scaleEnd. - /// Interpolated value. + /// @param scaleStart The start of the scale to LERP along. + /// @param scaleEnd The end of the scale to LERP along. + /// @param startValue The start value of your LERP. + /// @param endValue The end value of your LERP. + /// @param progressScalar How far your LERP has progressed. Automatically normalized through use of scaleStart and scaleEnd. + /// @return Interpolated value. float LERP(float scaleStart, float scaleEnd, float startValue, float endValue, float progressScalar); - /// /// Nonlinear ease-in interpolation. Starts slow. - /// - /// Start value. - /// End value. - /// Normalized positive progress scalar (0 - 1.0). - /// Interpolated value. + /// @param start Start value. + /// @param end End value. + /// @param progressScalar Normalized positive progress scalar (0 - 1.0). + /// @return Interpolated value. float EaseIn(float start, float end, float progressScalar); - /// /// Nonlinear ease-out interpolation. Slows down toward the end. - /// - /// Start value. - /// End value. - /// Normalized positive progress scalar (0 - 1.0). - /// Interpolated value. + /// @param start Start value. + /// @param end End value. + /// @param progressScalar Normalized positive progress scalar (0 - 1.0). + /// @return Interpolated value. float EaseOut(float start, float end, float progressScalar); - /// /// Nonlinear ease-in-out interpolation. Slows down in the start and end. - /// - /// Start value. - /// End value. - /// Normalized positive progress scalar (0 - 1.0). - /// Interpolated value. + /// @param start Start value. + /// @param end End value. + /// @param progressScalar Normalized positive progress scalar (0 - 1.0). + /// @return Interpolated value. float EaseInOut(float start, float end, float progressScalar); #pragma endregion #pragma region Clamping - /// /// Clamps a value between two limit values. - /// - /// Value to clamp. - /// Upper limit of value. - /// Lower limit of value. - /// True if either limit is currently reached, False if not. + /// @param value Value to clamp. + /// @param upperLimit Upper limit of value. + /// @param lowerLimit Lower limit of value. + /// @return True if either limit is currently reached, False if not. bool Clamp(float& value, float upperLimit, float lowerLimit); - /// /// Clamps a value between two limit values. - /// - /// Value to clamp. - /// Upper limit of value. - /// Lower limit of value. - /// Upper/Lower limit value if limit is currently reached, value between limits if not. + /// @param value Value to clamp. + /// @param upperLimit Upper limit of value. + /// @param lowerLimit Lower limit of value. + /// @return Upper/Lower limit value if limit is currently reached, value between limits if not. float Limit(float value, float upperLimit, float lowerLimit); #pragma endregion #pragma region Rounding - /// /// Rounds a float to a set fixed point precision (digits after decimal point) with option to always ceil or always floor the remainder. - /// - /// The input float to round. - /// The precision to round to, i.e. the number of digits after the decimal points. - /// Method of rounding to use. 0 for system default, 1 for floored remainder, 2 for ceiled remainder. - /// A string of the float, rounded and displayed to chosen precision. + /// @param inputFloat The input float to round. + /// @param precision The precision to round to, i.e. the number of digits after the decimal points. + /// @param roundingMode Method of rounding to use. 0 for system default, 1 for floored remainder, 2 for ceiled remainder. + /// @return A string of the float, rounded and displayed to chosen precision. std::string RoundFloatToPrecision(float input, int precision, int roundingMode = 0); - /// /// Rounds an integer to the specified nearest multiple. /// For example, if the arguments are 63 and 5, the returned value will be 65. - /// - /// The number to round to the nearest multiple. - /// The multiple to round to. - /// An integer rounded to the specified nearest multiple. + /// @param num The number to round to the nearest multiple. + /// @param multiple The multiple to round to. + /// @return An integer rounded to the specified nearest multiple. inline int RoundToNearestMultiple(int num, int multiple) { return static_cast(std::round(static_cast(num) / static_cast(multiple)) * static_cast(multiple)); } #pragma endregion #pragma region Angle Helpers - /// /// Returns a copy of the angle normalized so it's between 0 and 2PI. - /// - /// The angle to normalize, in radians. - /// The angle, normalized so it's between 0 and 2PI + /// @param angle The angle to normalize, in radians. + /// @return The angle, normalized so it's between 0 and 2PI float NormalizeAngleBetween0And2PI(float angle); - /// /// Returns a copy of the angle normalized so it's between -PI and PI. - /// - /// The angle to normalize, in radians. - /// The angle, normalized so it's between -PI and PI + /// @param angle The angle to normalize, in radians. + /// @return The angle, normalized so it's between -PI and PI float NormalizeAngleBetweenNegativePIAndPI(float angle); - /// /// Returns whether or not the angle to check is between the start and end angles. Note that, because of how angles work (when normalized), the start angle may be greater than the end angle. - /// - /// The angle to check, in radians. - /// The starting angle for the range. - /// The ending angle for the range. - /// Whether or not the angle to check is between the start and end angle. + /// @param angleToCheck The angle to check, in radians. + /// @param startAngle The starting angle for the range. + /// @param endAngle The ending angle for the range. + /// @return Whether or not the angle to check is between the start and end angle. bool AngleWithinRange(float angleToCheck, float startAngle, float endAngle); - /// /// Clamps the passed in angle between the specified lower and upper limits, in a CCW direction. - /// - /// The angle to clamp. - /// The lower limit for clamping. - /// The upper limit for clamping. - /// The angle, clamped between the start and end angle. + /// @param angleToClamp The angle to clamp. + /// @param startAngle The lower limit for clamping. + /// @param endAngle The upper limit for clamping. + /// @return The angle, clamped between the start and end angle. float ClampAngle(float angleToClamp, float startAngle, float endAngle); #pragma endregion #pragma region Detection - /// /// Tells whether a point is within a specified box. - /// - /// Vector position of the point we're checking. - /// Vector position of the box. - /// Width of the box. - /// Height of the box. - /// True if point is inside box bounds. + /// @param point Vector position of the point we're checking. + /// @param boxPos Vector position of the box. + /// @param width Width of the box. + /// @param height Height of the box. + /// @return True if point is inside box bounds. bool WithinBox(const Vector& point, const Vector& boxPos, float width, float height); - /// /// Tells whether a point is within a specified box. - /// - /// Vector position of the point we're checking. - /// Position of box left plane (X start). - /// Position of box top plane (Y start). - /// Position of box right plane (X end). - /// Position of box bottom plane (Y end). - /// True if point is inside box bounds. + /// @param point Vector position of the point we're checking. + /// @param left Position of box left plane (X start). + /// @param top Position of box top plane (Y start). + /// @param right Position of box right plane (X end). + /// @param bottom Position of box bottom plane (Y end). + /// @return True if point is inside box bounds. bool WithinBox(const Vector& point, float left, float top, float right, float bottom); #pragma endregion #pragma region Conversion - /// /// Returns a corrected angle value that can be used with Allegro fixed point math routines where 256 equals 360 degrees. - /// - /// The angle value to correct. In degrees. - /// A float with the represented angle as full rotations being 256. + /// @param angleDegrees The angle value to correct. In degrees. + /// @return A float with the represented angle as full rotations being 256. inline float GetAllegroAngle(float angleDegrees) { return (angleDegrees / 360) * 256; } - /// /// Returns the given angle converted from degrees to radians. - /// - /// The angle in degrees to be converted. - /// The converted angle in radians. + /// @param angleDegrees The angle in degrees to be converted. + /// @return The converted angle in radians. inline float DegreesToRadians(float angleDegrees) { return angleDegrees / 180.0F * c_PI; } - /// /// Returns the given angle converted from radians to degrees. - /// - /// The angle in radians to be converted. - /// The converted angle in degrees. + /// @param angleRadians The angle in radians to be converted. + /// @return The converted angle in degrees. inline float RadiansToDegrees(float angleRadians) { return angleRadians / c_PI * 180.0F; } #pragma endregion #pragma region Strings - /// /// Checks whether two strings are equal when the casing is disregarded. - /// - /// First string. - /// Second string. - /// Whether the two strings are equal case insensitively. + /// @param strA First string. + /// @param strB Second string. + /// @return Whether the two strings are equal case insensitively. inline bool StringsEqualCaseInsensitive(const std::string_view& strA, const std::string_view& strB) { return std::equal(strA.begin(), strA.end(), strB.begin(), strB.end(), [](char strAChar, char strBChar) { return std::tolower(strAChar) == std::tolower(strBChar); }); } - /// /// If a file "foo/Bar.txt" exists, and this method is passed "FOO/BAR.TXT", then this method will return "foo/Bar.txt". /// This method's purpose is to enable Linux to get the real path using a case-insensitive search. /// The real path is used by the Lua file I/O handling methods to ensure full Windows compatibility. - /// - /// Path to case-insensitively translate to a real path. - /// The real path. If the path doesn't exist, it returns the fullPath argument with all the existing parent directories correctly capitalized. + /// @param fullPath Path to case-insensitively translate to a real path. + /// @return The real path. If the path doesn't exist, it returns the fullPath argument with all the existing parent directories correctly capitalized. std::string GetCaseInsensitiveFullPath(const std::string& fullPath); - /// /// Hashes a string in a cross-compiler/platform safe way (std::hash gives different results on different compilers). - /// - /// Text string to hash. - /// The hash result value. + /// @param text Text string to hash. + /// @return The hash result value. uint64_t Hash(const std::string& text); #pragma endregion #pragma region Misc - /// /// Convenience method that takes in a double pointer array and returns a std::vector with its contents, because pointers-to-pointers are the devil. The passed in array is deleted in the process so no need to delete it manually. - /// - /// The double pointer to convert to a std::vector. - /// The size of the double pointer array. + /// @param arrayOfType The double pointer to convert to a std::vector. + /// @param arraySize The size of the double pointer array. template std::vector ConvertDoublePointerToVectorOfPointers(Type** arrayOfType, size_t arraySize) { std::unique_ptr doublePointerArray = std::unique_ptr(arrayOfType); std::vector outputVector; @@ -329,10 +271,8 @@ namespace RTE { return outputVector; } - /// /// Returns the sign of the given input value. - /// - /// The sign as an integer -1, 0 or +1. + /// @return The sign as an integer -1, 0 or +1. template int Sign(const Type& value) { return (Type(0) < value) - (Type(0) > value); } diff --git a/Source/System/Reader.cpp b/Source/System/Reader.cpp index bd204a0034..1e5c8e2807 100644 --- a/Source/System/Reader.cpp +++ b/Source/System/Reader.cpp @@ -5,8 +5,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Reader::Clear() { m_Stream = nullptr; m_FilePath.clear(); @@ -26,23 +24,17 @@ namespace RTE { m_NonModulePath = false; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader::Reader(const std::string& fileName, bool overwrites, const ProgressCallback& progressCallback, bool failOK, bool nonModulePath) { Clear(); m_NonModulePath = nonModulePath; Create(fileName, overwrites, progressCallback, failOK); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader::Reader(std::unique_ptr&& stream, bool overwrites, const ProgressCallback& progressCallback, bool failOK) { Clear(); Create(std::move(stream), overwrites, progressCallback, failOK); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Reader::Create(const std::string& fileName, bool overwrites, const ProgressCallback& progressCallback, bool failOK) { if (fileName.empty()) { return -1; @@ -65,8 +57,6 @@ namespace RTE { return Create(std::make_unique(m_FilePath), overwrites, progressCallback, failOK); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Reader::Create(std::unique_ptr&& stream, bool overwrites, const ProgressCallback& progressCallback, bool failOK) { m_CanFail = failOK; @@ -87,22 +77,16 @@ namespace RTE { return m_Stream->good() ? 0 : -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Reader::GetReadModuleID() const { return (m_DataModuleID < 0) ? g_PresetMan.GetModuleID(m_DataModuleName) : m_DataModuleID; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Reader::WholeFileAsString() const { std::stringstream stringStream; stringStream << m_Stream->rdbuf(); return stringStream.str(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Reader::ReadLine() { DiscardEmptySpace(); @@ -132,8 +116,6 @@ namespace RTE { return TrimString(retString); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Reader::ReadPropName() { DiscardEmptySpace(); @@ -183,8 +165,6 @@ namespace RTE { return retString; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Reader::ReadPropValue() { std::string fullLine = ReadLine(); size_t valuePos = fullLine.find_first_of('='); @@ -192,8 +172,6 @@ namespace RTE { return TrimString(propValue); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Reader::NextProperty() { if (!DiscardEmptySpace() || m_EndOfStreams) { return false; @@ -207,8 +185,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string Reader::TrimString(const std::string& stringToTrim) const { if (stringToTrim.empty()) { return ""; @@ -219,8 +195,6 @@ namespace RTE { return stringToTrim.substr(start, (end - start + 1)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Reader::DiscardEmptySpace() { char peek; int indent = 0; @@ -328,8 +302,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Reader::ReportError(const std::string& errorDesc) const { if (!m_CanFail) { RTEAbort(errorDesc + "\nError happened in " + m_FilePath + " at line " + std::to_string(m_CurrentLine) + "!"); @@ -340,8 +312,6 @@ namespace RTE { } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Reader::StartIncludeFile() { // Report that we're including a file if (m_ReportProgress) { @@ -390,8 +360,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Reader::EndIncludeFile() { if (m_ReportProgress) { m_ReportProgress(m_ReportTabs + m_FileName + " - done! " + static_cast(-42), false); diff --git a/Source/System/Reader.h b/Source/System/Reader.h index 4e9d315426..0c16d9fa91 100644 --- a/Source/System/Reader.h +++ b/Source/System/Reader.h @@ -5,186 +5,134 @@ namespace RTE { using ProgressCallback = std::function; //!< Convenient name definition for the progress report callback function. - /// /// Reads RTE objects from std::istreams. - /// class Reader { public: #pragma region Creation - /// /// Constructor method used to instantiate a Reader object in system memory. Create() should be called before using the object. - /// Reader() { Clear(); } - /// /// Constructor method used to instantiate a Reader object in system memory and make it ready for reading from the passed in file path. - /// - /// Path to the file to open for reading. If the file doesn't exist the stream will fail to open. - /// Whether object definitions read here overwrite existing ones with the same names. - /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. - /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. - /// Whether this Reader is reading from path that is not a DataModule and should just read it as provided. + /// @param fileName Path to the file to open for reading. If the file doesn't exist the stream will fail to open. + /// @param overwrites Whether object definitions read here overwrite existing ones with the same names. + /// @param progressCallback A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. + /// @param failOK Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. + /// @param nonModulePath Whether this Reader is reading from path that is not a DataModule and should just read it as provided. Reader(const std::string& fileName, bool overwrites = false, const ProgressCallback& progressCallback = nullptr, bool failOK = false, bool nonModulePath = false); - /// /// Constructor method used to instantiate a Reader object in system memory and make it ready for reading from the passed in file path. - /// - /// Stream to read from. - /// Whether object definitions read here overwrite existing ones with the same names. - /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. - /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. + /// @param stream Stream to read from. + /// @param overwrites Whether object definitions read here overwrite existing ones with the same names. + /// @param progressCallback A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. + /// @param failOK Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. Reader(std::unique_ptr&& stream, bool overwrites = false, const ProgressCallback& progressCallback = nullptr, bool failOK = false); - /// /// Makes the Reader object ready for use. - /// - /// Path to the file to open for reading. If the file doesn't exist the stream will fail to open. - /// Whether object definitions read here overwrite existing ones with the same names. - /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. - /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param fileName Path to the file to open for reading. If the file doesn't exist the stream will fail to open. + /// @param overwrites Whether object definitions read here overwrite existing ones with the same names. + /// @param progressCallback A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. + /// @param failOK Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const std::string& fileName, bool overwrites = false, const ProgressCallback& progressCallback = nullptr, bool failOK = false); - /// /// Makes the Reader object ready for use. - /// - /// Stream to read from. - /// Whether object definitions read here overwrite existing ones with the same names. - /// A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. - /// Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param stream Stream to read from. + /// @param overwrites Whether object definitions read here overwrite existing ones with the same names. + /// @param progressCallback A function pointer to a function that will be called and sent a string with information about the progress of this Reader's reading. + /// @param failOK Whether it's ok for the file to not be there, ie we're only trying to open, and if it's not there, then fail silently. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(std::unique_ptr&& stream, bool overwrites = false, const ProgressCallback& progressCallback = nullptr, bool failOK = false); #pragma endregion #pragma region Getters and Setters - /// /// Gets the name of the DataModule this Reader is reading from. - /// - /// The name of the DataModule this reader is reading from. + /// @return The name of the DataModule this reader is reading from. const std::string& GetReadModuleName() const { return m_DataModuleName; } - /// /// Gets the ID of the DataModule this Reader is reading from. If the ID is invalid, attempts to get a valid ID using the DataModule name. - /// - /// The DataModule ID that this Reader is reading from. + /// @return The DataModule ID that this Reader is reading from. int GetReadModuleID() const; - /// /// Gets a pointer to the istream of this reader. - /// - /// A pointer to the istream object for this reader. + /// @return A pointer to the istream object for this reader. std::istream* GetStream() const { return m_Stream.get(); } - /// /// Gets the path of the current file this reader is reading from. - /// - /// A string with the path, relative from the working directory. + /// @return A string with the path, relative from the working directory. std::string GetCurrentFilePath() const { return m_FilePath; } - /// /// Gets the line of the current file line this reader is reading from. - /// - /// A string with the line number that will be read from next. + /// @return A string with the line number that will be read from next. std::string GetCurrentFileLine() const { return std::to_string(m_CurrentLine); } - /// /// Shows whether objects read from this will be overwriting any existing ones with the same names. - /// - /// Whether this overwrites or not. + /// @return Whether this overwrites or not. bool GetPresetOverwriting() const { return m_OverwriteExisting; } - /// /// Sets whether objects read from this will be overwriting any existing ones with the same names. - /// - /// Whether this should overwrite existing definitions or not. + /// @param overwrites Whether this should overwrite existing definitions or not. void SetPresetOverwriting(bool overwrites = true) { m_OverwriteExisting = overwrites; } - /// /// Returns true if reader was told to skip InlcudeFile statements - /// - /// Returns whether reader was told to skip included files. + /// @return Returns whether reader was told to skip included files. bool GetSkipIncludes() const { return m_SkipIncludes; }; - /// /// Set whether this reader should skip included files. - /// - /// To make reader skip included files pass true, pass false otherwise. + /// @param skip To make reader skip included files pass true, pass false otherwise. void SetSkipIncludes(bool skip) { m_SkipIncludes = skip; }; #pragma endregion #pragma region Reading Operations - /// /// Reads a file and constructs a string from all its contents. - /// - /// A string containing the whole file contents. + /// @return A string containing the whole file contents. std::string WholeFileAsString() const; - /// /// Reads the rest of the line from the context object Reader's stream current location. - /// - /// The std::string that will hold the line's contents. + /// @return The std::string that will hold the line's contents. std::string ReadLine(); - /// /// Reads the next property name from the context object Reader's stream after eating all whitespace including newlines up till the first newline char. /// Basically gets anything between the last newline before text to the next "=" after that. - /// - /// The whitespace-trimmed std::string that will hold the next property's name. + /// @return The whitespace-trimmed std::string that will hold the next property's name. std::string ReadPropName(); - /// /// Reads the next property value from the context object Reader's stream after eating all whitespace including newlines up till the first newline char. /// Basically gets anything after the last "=" and up to the next newline after that. - /// - /// The whitespace-trimmed std::string that will hold the next property value. + /// @return The whitespace-trimmed std::string that will hold the next property value. std::string ReadPropValue(); - /// /// Lines up the reader with the next property of the current object. - /// - /// Whether there are any more properties to be read by the current object. + /// @return Whether there are any more properties to be read by the current object. bool NextProperty(); - /// /// Notifies the reader that we're starting reading a new object, by making it expect another level of indentation. - /// void StartObject() { --m_ObjectEndings; } - /// /// Takes out whitespace from the beginning and the end of a string. - /// - /// String to remove whitespace from. - /// The string that was passed in, sans whitespace in the front and end. + /// @param stringToTrim String to remove whitespace from. + /// @return The string that was passed in, sans whitespace in the front and end. std::string TrimString(const std::string& stringToTrim) const; - /// /// Discards all whitespace, newlines and comment lines (which start with '//') so that the next thing to be read will be actual data. - /// - /// Whether there is more data to read from the file streams after this eat. + /// @return Whether there is more data to read from the file streams after this eat. bool DiscardEmptySpace(); #pragma endregion #pragma region Reader Status - /// /// Shows whether this is still OK to read from. If file isn't present, etc, this will return false. - /// - /// Whether this Reader's stream is OK or not. + /// @return Whether this Reader's stream is OK or not. bool ReaderOK() const { return m_Stream.get() && m_Stream->good(); } - /// /// Makes an error message box pop up for the user that tells them something went wrong with the reading, and where. - /// - /// The message describing what's wrong. + /// @param errorDesc The message describing what's wrong. void ReportError(const std::string& errorDesc) const; #pragma endregion #pragma region Operator Overloads - /// /// Stream extraction operator overloads for all the elemental types. - /// - /// A reference to the variable that will be filled by the extracted data. - /// A Reader reference for further use in an expression. + /// @param var A reference to the variable that will be filled by the extracted data. + /// @return A Reader reference for further use in an expression. Reader& operator>>(bool& var) { DiscardEmptySpace(); *m_Stream >> var; @@ -253,13 +201,9 @@ namespace RTE { #pragma endregion protected: - /// /// A struct containing information from the currently used stream. - /// struct StreamInfo { - /// /// Constructor method used to instantiate a StreamInfo object in system memory. - /// StreamInfo(std::istream* stream, const std::string& filePath, int currentLine, int prevIndent) : Stream(stream), FilePath(filePath), CurrentLine(currentLine), PreviousIndent(prevIndent) {} @@ -294,32 +238,24 @@ namespace RTE { std::stack m_BlockCommentOpenTagLines; // /// When NextProperty() has returned false, indicating that there were no more properties to read on that object, /// this is incremented until it matches -m_IndentDifference, and then NextProperty will start returning true again. - /// int m_ObjectEndings; private: #pragma region Reading Operations - /// /// When ReadPropName encounters the property name "IncludeFile", it will automatically call this function to get started reading on that file. /// This will create a new stream to the include file. - /// - /// Whether the include file was found and opened ok or not. + /// @return Whether the include file was found and opened ok or not. bool StartIncludeFile(); - /// /// This should be called when end-of-file is detected in an included file stream. /// It will destroy the current stream pop the top stream off the stream stack to resume reading from it instead. - /// - /// Whether there were any stream on the stack to resume. + /// @return Whether there were any stream on the stack to resume. bool EndIncludeFile(); #pragma endregion - /// /// Clears all the member variables of this Reader, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. diff --git a/Source/System/Serializable.cpp b/Source/System/Serializable.cpp index 97f0376616..b3b891c9e2 100644 --- a/Source/System/Serializable.cpp +++ b/Source/System/Serializable.cpp @@ -2,8 +2,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Serializable::CreateSerializable(Reader& reader, bool checkType, bool doCreate, bool skipStartingObject) { if (checkType && reader.ReadPropValue() != GetClassName()) { reader.ReportError("Wrong type in Reader when passed to Serializable::Create()"); @@ -26,23 +24,17 @@ namespace RTE { return doCreate ? Create() : 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Serializable::ReadProperty(const std::string_view& propName, Reader& reader) { reader.ReadPropValue(); reader.ReportError("Could not match property '" + std::string(propName) + "'!"); return -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader& operator>>(Reader& reader, Serializable& operand) { operand.Create(reader); return reader; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Reader& operator>>(Reader& reader, Serializable* operand) { if (operand) { operand->Create(reader); @@ -50,16 +42,12 @@ namespace RTE { return reader; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Writer& operator<<(Writer& writer, const Serializable& operand) { operand.Save(writer); writer.ObjectEnd(); return writer; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Writer& operator<<(Writer& writer, const Serializable* operand) { if (operand) { operand->Save(writer); diff --git a/Source/System/Serializable.h b/Source/System/Serializable.h index 4d7d59127c..1d35855cf3 100644 --- a/Source/System/Serializable.h +++ b/Source/System/Serializable.h @@ -10,24 +10,18 @@ namespace RTE { - /// /// This base class specifies common creation/destruction patterns associated with reading and writing member data from disk. /// Is only intended to be inherited from in one level. - /// class Serializable { public: #pragma region Global Macro Definitions -/// /// Convenience macro to cut down on duplicate ReadProperty and Save methods in classes that extend Serializable. -/// #define SerializableOverrideMethods \ int ReadProperty(const std::string_view& propName, Reader& reader) override; \ int Save(Writer& writer) const override; -/// /// Convenience macro to cut down on duplicate GetClassName methods in non-poolable classes that extend Serializable. -/// #define SerializableClassNameGetter \ const std::string& GetClassName() const override { return c_ClassName; } @@ -72,137 +66,104 @@ namespace RTE { #pragma endregion #pragma region Creation - /// /// Constructor method used to instantiate a Serializable object in system memory. Create() should be called before using the object. - /// Serializable() = default; - /// /// Makes the Serializable object ready for use, usually after all necessary properties have been set with Create(Reader). - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int Create() { return 0; } - /// /// Makes the Serializable object ready for use. - /// - /// A Reader that the Serializable will create itself from. - /// Whether there is a class name in the stream to check against to make sure the correct type is being read from the stream. - /// Whether to do any additional initialization of the object after reading in all the properties from the Reader. This is done by calling Create(). - /// A Reader that the Serializable will create itself from. - /// Whether there is a class name in the stream to check against to make sure the correct type is being read from the stream. - /// Whether to do any additional initialization of the object after reading in all the properties from the Reader. This is done by calling Create(). - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reader A Reader that the Serializable will create itself from. + /// @param checkType Whether there is a class name in the stream to check against to make sure the correct type is being read from the stream. + /// @param doCreate Whether to do any additional initialization of the object after reading in all the properties from the Reader. This is done by calling Create(). + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int Create(Reader& reader, bool checkType = true, bool doCreate = true) { return CreateSerializable(reader, checkType, doCreate, false); } #pragma endregion #pragma region Destruction - /// /// Destructor method used to clean up a Serializable object before deletion from system memory. - /// virtual ~Serializable() = default; - /// /// Resets the entire Serializable, including its inherited members, to their default settings or values. - /// virtual void Reset() { Clear(); } #pragma endregion #pragma region INI Handling - /// /// Reads a property value from a Reader stream. /// If the name isn't recognized by this class, then ReadProperty of the parent class is called. /// If the property isn't recognized by any of the base classes, false is returned, and the Reader's position is untouched. - /// - /// The name of the property to be read. - /// A Reader lined up to the value of the property to be read. - /// + /// @param propName The name of the property to be read. + /// @param reader A Reader lined up to the value of the property to be read. + /// @return /// An error return value signaling whether the property was successfully read or not. /// 0 means it was read successfully, and any nonzero indicates that a property of that name could not be found in this or base classes. - /// virtual int ReadProperty(const std::string_view& propName, Reader& reader); - /// /// Saves the complete state of this Serializable to an output stream for later recreation with Create(istream &stream). - /// - /// A Writer that the Serializable will save itself to. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param writer A Writer that the Serializable will save itself to. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. virtual int Save(Writer& writer) const { writer.ObjectStart(GetClassName()); return 0; } - /// /// Replaces backslashes with forward slashes in file paths to eliminate issues with cross-platform compatibility or invalid escape sequences. - /// - /// Reference to the file path string to correct slashes in. + /// @param pathToCorrect Reference to the file path string to correct slashes in. // TODO: Add a warning log entry if backslashes are found in a data path. Perhaps overwrite them in the ini file itself. std::string CorrectBackslashesInPath(const std::string& pathToCorrect) const { return std::filesystem::path(pathToCorrect).generic_string(); } #pragma endregion #pragma region Logging - /// /// Sets the file and line that are currently being read. Formatted to be used for logging warnings and errors. /// Because we're often used as a parent for basic types (i.e, Vector, Matrix, Color), where we don't want to spend any time doing string construction we don't actually store this data here. /// This just acts as an abstract base for child classes to implement. - /// - /// A string containing the currently read file path and the line being read. + /// @param newPosition A string containing the currently read file path and the line being read. virtual void SetFormattedReaderPosition(const std::string& newPosition) {} #pragma endregion #pragma region Operator Overloads - /// /// A Reader extraction operator for filling a Serializable from a Reader. - /// - /// A Reader reference as the left hand side operand. - /// An Serializable reference as the right hand side operand. - /// A Reader reference for further use in an expression. + /// @param reader A Reader reference as the left hand side operand. + /// @param operand An Serializable reference as the right hand side operand. + /// @return A Reader reference for further use in an expression. friend Reader& operator>>(Reader& reader, Serializable& operand); - /// /// A Reader extraction operator for filling an Serializable from a Reader. - /// - /// A Reader reference as the left hand side operand. - /// An Serializable pointer as the right hand side operand. - /// A Reader reference for further use in an expression. + /// @param reader A Reader reference as the left hand side operand. + /// @param operand An Serializable pointer as the right hand side operand. + /// @return A Reader reference for further use in an expression. friend Reader& operator>>(Reader& reader, Serializable* operand); - /// /// A Writer insertion operator for sending a Serializable to a Writer. - /// - /// A Writer reference as the left hand side operand. - /// A Serializable reference as the right hand side operand. - /// A Writer reference for further use in an expression. + /// @param writer A Writer reference as the left hand side operand. + /// @param operand A Serializable reference as the right hand side operand. + /// @return A Writer reference for further use in an expression. friend Writer& operator<<(Writer& writer, const Serializable& operand); - /// /// A Writer insertion operator for sending a Serializable to a Writer. - /// - /// A Writer reference as the left hand side operand. - /// A Serializable pointer as the right hand side operand. - /// A Writer reference for further use in an expression. + /// @param writer A Writer reference as the left hand side operand. + /// @param operand A Serializable pointer as the right hand side operand. + /// @return A Writer reference for further use in an expression. friend Writer& operator<<(Writer& writer, const Serializable* operand); #pragma endregion #pragma region Class Info - /// /// Gets the class name of this Serializable. - /// - /// A string with the friendly-formatted type name of this Serializable. + /// @return A string with the friendly-formatted type name of this Serializable. virtual const std::string& GetClassName() const = 0; #pragma endregion private: - /// /// Clears all the member variables of this Object, effectively resetting the members of this abstraction level only. - /// void Clear() {} }; } // namespace RTE diff --git a/Source/System/Shader.cpp b/Source/System/Shader.cpp index 07c9b13811..cc0f7ecb05 100644 --- a/Source/System/Shader.cpp +++ b/Source/System/Shader.cpp @@ -12,20 +12,17 @@ namespace RTE { Shader::Shader() : m_ProgramID(0), m_TextureUniform(-1), m_ColorUniform(-1), m_TransformUniform(-1), m_ProjectionUniform(-1) {} - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Shader::Shader(const std::string& vertexFilename, const std::string& fragPath) : m_ProgramID(glCreateProgram()), m_TextureUniform(-1), m_ColorUniform(-1), m_TransformUniform(-1), m_ProjectionUniform(-1) { Compile(vertexFilename, fragPath); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Shader::~Shader() { if (m_ProgramID) { GL_CHECK(glDeleteProgram(m_ProgramID)); } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Shader::Compile(const std::string& vertexPath, const std::string& fragPath) { assert(m_ProgramID != 0); GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); @@ -59,54 +56,38 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::Use() { GL_CHECK(glUseProgram(m_ProgramID)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// GLint Shader::GetUniformLocation(const std::string& name) { return glGetUniformLocation(m_ProgramID, name.c_str()); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetBool(const std::string& name, bool value) { GL_CHECK(glUniform1i(glGetUniformLocation(m_ProgramID, name.c_str()), static_cast(value))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetInt(const std::string& name, int value) { GL_CHECK(glUniform1i(glGetUniformLocation(m_ProgramID, name.c_str()), value)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetFloat(const std::string& name, float value) { GL_CHECK(glUniform1f(glGetUniformLocation(m_ProgramID, name.c_str()), value)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetMatrix4f(const std::string& name, const glm::mat4& value) { GL_CHECK(glUniformMatrix4fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, GL_FALSE, glm::value_ptr(value))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetVector2f(const std::string& name, const glm::vec2& value) { GL_CHECK(glUniform2fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, glm::value_ptr(value))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetVector3f(const std::string& name, const glm::vec3& value) { GL_CHECK(glUniform3fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, glm::value_ptr(value))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetVector4f(const std::string& name, const glm::vec4& value) { GL_CHECK(glUniform4fv(glGetUniformLocation(m_ProgramID, name.c_str()), 1, glm::value_ptr(value))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetBool(int32_t uniformLoc, bool value) { GL_CHECK(glUniform1i(uniformLoc, value)); } void Shader::SetInt(int32_t uniformLoc, int value) { GL_CHECK(glUniform1i(uniformLoc, value)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetFloat(int32_t uniformLoc, float value) { GL_CHECK(glUniform1f(uniformLoc, value)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetMatrix4f(int32_t uniformLoc, const glm::mat4& value) { GL_CHECK(glUniformMatrix4fv(uniformLoc, 1, GL_FALSE, glm::value_ptr(value))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetVector2f(int32_t uniformLoc, const glm::vec2& value) { GL_CHECK(glUniform2fv(uniformLoc, 1, glm::value_ptr(value))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetVector3f(int32_t uniformLoc, const glm::vec3& value) { GL_CHECK(glUniform3fv(uniformLoc, 1, glm::value_ptr(value))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::SetVector4f(int32_t uniformLoc, const glm::vec4& value) { GL_CHECK(glUniform4fv(uniformLoc, 1, glm::value_ptr(value))); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Shader::CompileShader(GLuint shaderID, const std::string& filename, std::string& error) { if (!System::PathExistsCaseSensitive(filename)) { error += "File " + filename + " doesn't exist."; @@ -140,7 +121,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Shader::Link(GLuint vtxShader, GLuint fragShader) { GL_CHECK(glAttachShader(m_ProgramID, vtxShader)); GL_CHECK(glAttachShader(m_ProgramID, fragShader)); @@ -155,7 +135,6 @@ namespace RTE { return true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Shader::ApplyDefaultUniforms() { Use(); SetInt("rteTexture", 0); diff --git a/Source/System/Shader.h b/Source/System/Shader.h index e731b405cf..21c8671bc9 100644 --- a/Source/System/Shader.h +++ b/Source/System/Shader.h @@ -7,286 +7,186 @@ namespace RTE { class Shader { public: - /// /// Constructs an empty shader program, which can be initialized using `Shader::Compile` - /// Shader(); - /// /// Constructs a Shader from vertex shader file and fragment shader file. - /// - /// + /// @param vertexFilename /// Filepath to the vertex shader file. - /// - /// + /// @param fragPath /// Filepath to the fragment shader file. - /// Shader(const std::string& vertexFilename, const std::string& fragPath); - /// /// Destructor. - /// virtual ~Shader(); - /// /// Create this shader from a vertex shader file and a fragment shader file. - /// - /// + /// @param vertexFilename /// Filepath to the vertex shader. - /// - /// + /// @param fragPath /// Filepath to the fragment shader - /// bool Compile(const std::string& vertexFilename, const std::string& fragPath); void Use(); #pragma region Uniform handling - /// /// Returns the location of a uniform given by name. - /// - /// + /// @param name /// String containing the name of a uniform in this shader program. - /// - /// + /// @return /// A GLint containing the location of the requested uniform. - /// int32_t GetUniformLocation(const std::string& name); - /// /// Set a boolean uniform value in the active program by name. - /// - /// + /// @param name /// The name of the uniform to set. - /// - /// + /// @param value /// The boolean value to set the uniform to. - /// void SetBool(const std::string& name, bool value); - /// /// Set an integer uniform value in the active program by name. - /// - /// + /// @param name /// The name of the uniform to set. - /// - /// + /// @param value /// The integer value to set the uniform to. - /// void SetInt(const std::string& name, int value); - /// /// Set a float uniform value in the active program by name. - /// - /// + /// @param name /// The name of the uniform to set. - /// - /// + /// @param value /// The float value to set the uniform to. - /// void SetFloat(const std::string& name, float value); - /// /// Set a float mat4 uniform value in the active program by name. - /// - /// + /// @param name /// The name of the uniform to set. - /// - /// + /// @param value /// The float mat4 value to set the uniform to. - /// void SetMatrix4f(const std::string& name, const glm::mat4& value); - /// /// Set a float vec2 uniform value in the active program by name. - /// - /// + /// @param name /// The name of the uniform to set. - /// - /// + /// @param value /// The float vec2 value to set the uniform to. - /// void SetVector2f(const std::string& name, const glm::vec2& value); - /// /// Set a float vec3 uniform value in the active program by name. - /// - /// + /// @param name /// The name of the uniform to set. - /// - /// + /// @param value /// The float vec3 value to set the uniform to. - /// void SetVector3f(const std::string& name, const glm::vec3& value); - /// /// Set a float vec4 uniform value in the active program by name. - /// - /// + /// @param name /// The name of the uniform to set. - /// - /// + /// @param value /// The float vec4 value to set the uniform to. - /// void SetVector4f(const std::string& name, const glm::vec4& value); - /// /// Set a boolean uniform value in the active program by location. - /// - /// + /// @param uniformLoc /// The location of the uniform to set. - /// - /// + /// @param value /// The boolean value to set the uniform to. - /// static void SetBool(int32_t uniformLoc, bool value); - /// /// Set an integer uniform value in the active program by location. - /// - /// + /// @param uniformLoc /// The location of the uniform to set. - /// - /// + /// @param value /// The integer value to set the uniform to. - /// static void SetInt(int32_t uniformLoc, int value); - /// /// Set a float uniform value in the active program by location. - /// - /// + /// @param uniformLoc /// The location of the uniform to set. - /// - /// + /// @param value /// The float value to set the uniform to. - /// static void SetFloat(int32_t uniformLoc, float value); - /// /// Set a float mat4 uniform value in the active program by location. - /// - /// + /// @param uniformLoc /// The location of the uniform to set. - /// - /// + /// @param value /// The float mat4 value to set the uniform to. - /// static void SetMatrix4f(int32_t uniformLoc, const glm::mat4& value); - /// /// Set a float vec2 uniform value in the active program by location. - /// - /// + /// @param uniformLoc /// The location of the uniform to set. - /// - /// + /// @param value /// The float vec2 value to set the uniform to. - /// static void SetVector2f(int32_t uniformLoc, const glm::vec2& value); - /// /// Set a float vec3 uniform value in the active program by location. - /// - /// + /// @param uniformLoc /// The location of the uniform to set. - /// - /// + /// @param value /// The float vec3 value to set the uniform to. - /// static void SetVector3f(int32_t uniformLoc, const glm::vec3& value); - /// /// Set a float vec4 uniform value in the active program by location. - /// - /// + /// @param uniformLoc /// The location of the uniform to set. - /// - /// + /// @param value /// The float vec4 value to set the uniform to. - /// static void SetVector4f(int32_t uniformLoc, const glm::vec4& value); #pragma endregion #pragma region Engine Defined Uniforms - /// /// Get the location of the texture sampler uniform. - /// - /// + /// @return /// The location of the texture uniform. This may be -1 if the shader doesn't use textures, in which case the value will be ignored. - /// int GetTextureUniform() { return m_TextureUniform; } - /// /// Get the location of the color modifier uniform. - /// - /// + /// @return /// The location of the color modifier uniform. This may be -1 if the shader doesn't use the color mod, in which case the value will be ignored. - /// int GetColorUniform() { return m_ColorUniform; } - /// /// Get the location of the transformation matrix uniform. - /// - /// + /// @return /// The location of the transformation matrix uniform. This may be -1 if the shader doesn't use transforms, in which case the value will be ignored. - /// int GetTransformUniform() { return m_TransformUniform; } - /// /// Get the location of the transformation matrix uniform. - /// - /// + /// @return /// The location of the UV transformation matrix uniform. This may be -1 if the shader doesn't use UV transforms, in which case the value will be ignored. - /// int GetUVTransformUniform() { return m_UVTransformUniform; } - /// /// Get the location of the projection matrix uniform. - /// - /// + /// @return /// The location of the color modifier uniform. This may be -1 if the shader doesn't apply projection, in which case the value will be ignored. - /// int GetProjectionUniform() { return m_ProjectionUniform; } #pragma endregion private: uint32_t m_ProgramID; - /// /// Compiles a shader component from a data string. - /// - /// + /// @param shaderID /// ID of the shader component to compile. - /// - /// + /// @param data /// The shader code string. - /// - /// + /// @param error /// String to contain error data returned by opengl during compilation. - /// - /// + /// @return /// Whether compilation was successful. - /// bool CompileShader(uint32_t shaderID, const std::string& data, std::string& error); - /// /// Links a shader program from a vertex and fragment shader. - /// - /// + /// @param vtxShader /// The compiled vertex shader. - /// - /// + /// @param fragShader /// The compiled fragment shader. - /// - /// + /// @return /// Whether linking was successful. - /// bool Link(uint32_t vtxShader, uint32_t fragShader); - /// /// Sets default values for the shader uniforms (may not persist across frames!) - /// void ApplyDefaultUniforms(); int m_TextureUniform; //!< Location of the texture uniform (sampler2d rteTexture). diff --git a/Source/System/Singleton.h b/Source/System/Singleton.h index 36e886d136..9415fd5df6 100644 --- a/Source/System/Singleton.h +++ b/Source/System/Singleton.h @@ -7,36 +7,26 @@ namespace RTE { template - /// /// Anything derived from this class will adhere to the Singleton pattern. /// To convert any class into a Singleton, do the three following steps: /// 1. Publicly derive your class MyClass from Singleton. /// 2. Make sure to instantiate MyClass once before using it. /// 3. Call MyClass::Instance() to use the MyClass object from anywhere. - /// class Singleton { public: - /// /// Destructor method used to clean up a Singleton object before deletion. - /// ~Singleton() = default; - /// /// Returns the sole instance of this Singleton. - /// - /// A reference to the sole instance of this Singleton. + /// @return A reference to the sole instance of this Singleton. inline static Type& Instance() { return *s_Instance; } - /// /// Constructs this Singleton. - /// inline static void Construct() { s_Instance = new Type(); } protected: - /// /// Constructor method used to instantiate a Singleton object. - /// Singleton() { RTEAssert(!s_Instance, "Trying to create a second instance of a Singleton!"); } private: diff --git a/Source/System/SpatialPartitionGrid.cpp b/Source/System/SpatialPartitionGrid.cpp index bc32a59191..cbf283d22f 100644 --- a/Source/System/SpatialPartitionGrid.cpp +++ b/Source/System/SpatialPartitionGrid.cpp @@ -6,8 +6,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SpatialPartitionGrid::Clear() { m_Width = 0; m_Height = 0; @@ -19,8 +17,6 @@ namespace RTE { m_UsedCellIds.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SpatialPartitionGrid::Create(int width, int height, int cellSize) { m_Width = width / cellSize; m_Height = height / cellSize; @@ -32,8 +28,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SpatialPartitionGrid::Create(const SpatialPartitionGrid& reference) { m_Width = reference.m_Width; m_Height = reference.m_Height; @@ -44,8 +38,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SpatialPartitionGrid::Reset() { const Activity* activity = g_ActivityMan.GetActivity(); RTEAssert(activity, "Tried to reset spatial partition grid with no running Activity!"); @@ -62,8 +54,6 @@ namespace RTE { m_UsedCellIds.clear(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void SpatialPartitionGrid::Add(const IntRect& rect, const MovableObject& mo) { const Activity* activity = g_ActivityMan.GetActivity(); RTEAssert(activity, "Tried to add to spatial partition grid with no running Activity!"); @@ -102,7 +92,6 @@ namespace RTE { } } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::vector SpatialPartitionGrid::GetMOsInBox(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const { RTEAssert(ignoreTeam >= Activity::NoTeam && ignoreTeam < Activity::MaxTeamCount, "Invalid ignoreTeam given to SpatialPartitioningGrid::GetMOsInBox()!"); @@ -142,8 +131,6 @@ namespace RTE { return MOList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::vector SpatialPartitionGrid::GetMOsInRadius(const Vector& center, float radius, int ignoreTeam, bool getsHitByMOsOnly) const { RTEAssert(ignoreTeam >= Activity::NoTeam && ignoreTeam < Activity::MaxTeamCount, "Invalid ignoreTeam given to SpatialPartitioningGrid::GetMOsInRadius()!"); @@ -176,8 +163,6 @@ namespace RTE { return MOList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector& SpatialPartitionGrid::GetMOIDsAtPosition(int x, int y, int ignoreTeam, bool getsHitByMOsOnly) const { int cellX = x / m_CellSize; int cellY = y / m_CellSize; @@ -191,8 +176,6 @@ namespace RTE { return cells[ignoreTeam + 1][GetCellIdForCellCoords(cellX, cellY)]; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int SpatialPartitionGrid::GetCellIdForCellCoords(int cellX, int cellY) const { // We act like we wrap, even if the Scene doesn't. The only cost is some duplicate collision checks, but that's a minor cost to pay :) int wrappedX = cellX % m_Width; diff --git a/Source/System/SpatialPartitionGrid.h b/Source/System/SpatialPartitionGrid.h index 937aad172b..4ac10952de 100644 --- a/Source/System/SpatialPartitionGrid.h +++ b/Source/System/SpatialPartitionGrid.h @@ -14,80 +14,60 @@ namespace RTE { struct IntRect; class MovableObject; - /// /// A spatial partitioning grid, used to optimize MOID collision checks. - /// class SpatialPartitionGrid { public: #pragma region Creation - /// /// Constructor method used to instantiate a SpatialPartitionGrid object. - /// SpatialPartitionGrid() { Clear(); } - /// /// Constructor method used to instantiate a SpatialPartitionGrid object. - /// SpatialPartitionGrid(int width, int height, int cellSize) { Clear(); Create(width, height, cellSize); } - /// /// Makes the SpatialPartitionGrid object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(int width, int height, int cellSize); - /// /// Creates a SpatialPartitionGrid to be identical to another, by deep copy. - /// - /// A reference to the SpatialPartitionGrid to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the SpatialPartitionGrid to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const SpatialPartitionGrid& reference); #pragma endregion #pragma region Grid Management - /// /// Resets the spatial partitioning grid, removing everything from it - /// void Reset(); - /// /// Adds the given MovableObject to this SpatialPartitionGrid. - /// - /// A rectangle defining the space the MovableObject takes up. - /// The MovableObject to add. + /// @param rect A rectangle defining the space the MovableObject takes up. + /// @param mo The MovableObject to add. void Add(const IntRect& rect, const MovableObject& mo); - /// /// Gets a vector of pointers to all MovableObjects within the given Box, who aren't of the ignored team. - /// - /// The Box to get MovableObjects within. - /// The team to ignore when getting MovableObjects. - /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. - /// A vector of pointers to all MovableObjects within the given Box, who aren't of the ignored team. + /// @param box The Box to get MovableObjects within. + /// @param ignoreTeam The team to ignore when getting MovableObjects. + /// @param getsHitByMOsOnly Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. + /// @return A vector of pointers to all MovableObjects within the given Box, who aren't of the ignored team. std::vector GetMOsInBox(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const; - /// /// Get a vector of pointers to all the MovableObjects within the specified radius of the given center point, who aren't of the ignored team. - /// - /// The center point to get MovableObjects around. - /// The radius to get MovableObjects within. - /// The team to ignore when getting MovableObjects. - /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. - /// A vector of pointers to all the MovableObjects within the specified radius of the given center point, who aren't of the ignored team. + /// @param center The center point to get MovableObjects around. + /// @param radius The radius to get MovableObjects within. + /// @param ignoreTeam The team to ignore when getting MovableObjects. + /// @param getsHitByMOsOnly Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. + /// @return A vector of pointers to all the MovableObjects within the specified radius of the given center point, who aren't of the ignored team. std::vector GetMOsInRadius(const Vector& center, float radius, int ignoreTeam, bool getsHitByMOsOnly) const; - /// /// Gets the MOIDs that are potentially overlapping the given X and Y Scene coordinates. - /// - /// The X coordinate to check. - /// The Y coordinate to check. - /// The team to ignore when getting MOIDs. - /// Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. - /// A vector of MOIDs that are potentially overlapping the x and y coordinates. + /// @param x The X coordinate to check. + /// @param y The Y coordinate to check. + /// @param ignoreTeam The team to ignore when getting MOIDs. + /// @param getsHitByMOsOnly Whether to only include MOs that have GetsHitByMOs enabled, or all MOs. + /// @return A vector of MOIDs that are potentially overlapping the x and y coordinates. const std::vector& GetMOIDsAtPosition(int x, int y, int ignoreTeam, bool getsHitByMOsOnly) const; #pragma endregion @@ -103,17 +83,13 @@ namespace RTE { tsl::hopscotch_set m_UsedCellIds; //!< Set of used cell Ids, maintained to avoid wasting time looping through and clearing unused cells. - /// /// Gets the Id of the cell at the given SpatialPartitionGrid coordinates, automatically accounting for wrapping. - /// - /// The x coordinate of the cell to get the Id of. - /// The y coordinate of the cell to get the Id of. - /// The Id of the cell at the given SpatialPartitionGrid coordinates. + /// @param cellX The x coordinate of the cell to get the Id of. + /// @param cellY The y coordinate of the cell to get the Id of. + /// @return The Id of the cell at the given SpatialPartitionGrid coordinates. int GetCellIdForCellCoords(int cellX, int cellY) const; - /// /// Clears all the member variables of this SpatialPartitionGrid. - /// void Clear(); // Disallow the use of an implicit method. diff --git a/Source/System/StandardIncludes.h b/Source/System/StandardIncludes.h index 929636a98e..a5530e3942 100644 --- a/Source/System/StandardIncludes.h +++ b/Source/System/StandardIncludes.h @@ -89,9 +89,7 @@ namespace std { - /// /// Custom std::hash specialization to allow using std::array as key in hash table based containers. - /// template struct hash> { size_t operator()(const array& arr) const { hash hasher; diff --git a/Source/System/System.cpp b/Source/System/System.cpp index 30fc6465da..a8099d291c 100644 --- a/Source/System/System.cpp +++ b/Source/System/System.cpp @@ -33,8 +33,6 @@ namespace RTE { const std::string System::s_ZippedModulePackageExtension = ".zip"; const std::unordered_set System::s_SupportedExtensions = {".ini", ".txt", ".lua", ".cfg", ".bmp", ".png", ".jpg", ".jpeg", ".wav", ".ogg", ".mp3", ".flac"}; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void System::Initialize(const char* thisExePathAndName) { s_ThisExePathAndName = std::filesystem::path(thisExePathAndName).generic_string(); @@ -114,8 +112,6 @@ namespace RTE { #endif } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool System::MakeDirectory(const std::string& pathToMake) { bool createResult = std::filesystem::create_directory(pathToMake); if (createResult) { @@ -124,8 +120,6 @@ namespace RTE { return createResult; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool System::PathExistsCaseSensitive(const std::string& pathToCheck) { // Use Hash for compiler independent hashing. if (s_CaseSensitive) { @@ -145,8 +139,6 @@ namespace RTE { return std::filesystem::exists(pathToCheck); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void System::EnableLoggingToCLI() { #ifdef _WIN32 // Create a console instance for the current process @@ -167,8 +159,6 @@ namespace RTE { s_LogToCLI = true; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void System::PrintLoadingToCLI(const std::string& reportString, bool newItem) { if (newItem) { std::cout << std::endl; @@ -209,8 +199,6 @@ namespace RTE { std::cout << unicodedOutput << std::flush; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void System::PrintToCLI(const std::string& stringToPrint) { #if _LINUX_OR_MACOSX_ std::string outputString = stringToPrint; @@ -233,8 +221,6 @@ namespace RTE { #endif } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - std::string System::ExtractZippedDataModule(const std::string& zippedModulePath) { std::string zippedModuleName = System::GetModDirectory() + std::filesystem::path(zippedModulePath).filename().generic_string(); @@ -369,8 +355,6 @@ namespace RTE { return extractionProgressReport.str(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int System::ASCIIFileContainsString(const std::string& filePath, const std::string_view& findString) { std::ifstream inputStream(filePath, std::ios::binary); if (!inputStream.is_open()) { diff --git a/Source/System/System.h b/Source/System/System.h index 5c0b1e9e7e..159d18d97e 100644 --- a/Source/System/System.h +++ b/Source/System/System.h @@ -3,176 +3,124 @@ namespace RTE { - /// /// Class for the system functionality. - /// class System { public: #pragma region Creation - /// /// Store the current working directory and create any missing subdirectories. - /// - /// The path and name of this executable. + /// @param thisExePathAndName The path and name of this executable. static void Initialize(const char* thisExePathAndName); #pragma endregion #pragma region Program Termination - /// /// Gets whether the program was set to be terminated by the user. - /// - /// Whether the program was set to be terminated by the user. + /// @return Whether the program was set to be terminated by the user. static bool IsSetToQuit() { return s_Quit; } - /// /// Sets the program to be terminated. - /// - /// Terminate or not. + /// @param quitOrNot Terminate or not. static void SetQuit(bool quitOrNot = true) { s_Quit = quitOrNot; } - /// /// Sets termination when the close button (X) is pressed on the program window. - /// static void WindowCloseButtonHandler() { SetQuit(); } #pragma endregion #pragma region Directories - /// /// Gets the absolute path to this executable. - /// - /// Absolute path to this executable. + /// @return Absolute path to this executable. static const std::string& GetThisExePathAndName() { return s_ThisExePathAndName; } - /// /// Gets the current working directory. - /// - /// Absolute path to current working directory. + /// @return Absolute path to current working directory. static const std::string& GetWorkingDirectory() { return s_WorkingDirectory; } - /// /// Gets the game data directory name. - /// - /// Folder name of the game data directory. + /// @return Folder name of the game data directory. static const std::string& GetDataDirectory() { return s_DataDirectory; } - /// /// Gets the screenshot directory name. - /// - /// Folder name of the screenshots directory. + /// @return Folder name of the screenshots directory. static const std::string& GetScreenshotDirectory() { return s_ScreenshotDirectory; } - /// /// Gets the mod directory name. - /// - /// Folder name of the mod directory. + /// @return Folder name of the mod directory. static const std::string& GetModDirectory() { return s_ModDirectory; } - /// /// Gets the userdata directory name. - /// - /// Folder name of the userdata directory. + /// @return Folder name of the userdata directory. static const std::string& GetUserdataDirectory() { return s_UserdataDirectory; } - /// /// Gets the extension that determines a directory/file is an RTE module. - /// - /// String containing the RTE module extension. + /// @return String containing the RTE module extension. static const std::string& GetModulePackageExtension() { return s_ModulePackageExtension; } - /// /// Gets the extension that determines a file is a zipped RTE module. - /// - /// String containing the zipped RTE module extension. + /// @return String containing the zipped RTE module extension. static const std::string& GetZippedModulePackageExtension() { return s_ZippedModulePackageExtension; } - /// /// Create a directory. - /// - /// Path to create. - /// Returns 0 if successful. + /// @param path Path to create. + /// @return Returns 0 if successful. static bool MakeDirectory(const std::string& pathToMake); #pragma endregion #pragma region Filesystem - /// /// Gets whether case sensitivity is enforced when checking for file existence. - /// - /// Whether case sensitivity is enforced. + /// @return Whether case sensitivity is enforced. static bool FilePathsCaseSensitive() { return s_CaseSensitive; } - /// /// Sets whether case sensitivity should be enforced when checking for file existence. - /// - /// Whether case sensitivity should be enforced or not. + /// @param enable Whether case sensitivity should be enforced or not. static void EnableFilePathCaseSensitivity(bool enable) { s_CaseSensitive = enable; } - /// /// Checks if a file exists. On case sensitive filesystems returns std::filesystem::exists, otherwise the working directory will be checked for a matching file. - /// - /// The path to check. - /// Whether the file exists. + /// @param pathToCheck The path to check. + /// @return Whether the file exists. static bool PathExistsCaseSensitive(const std::string& pathToCheck); #pragma endregion #pragma region Command-Line Interface - /// /// Tells whether printing loading progress report and console to command-line is enabled or not. - /// - /// Whether printing to command-line is enabled or not. + /// @return Whether printing to command-line is enabled or not. static bool IsLoggingToCLI() { return s_LogToCLI; } - /// /// Enables printing the loading progress report and console to command-line. For Windows, also allocates a console instance and redirects cout to it. - /// static void EnableLoggingToCLI(); - /// /// Prints the loading progress report to command-line. - /// static void PrintLoadingToCLI(const std::string& reportString, bool newItem = false); - /// /// Prints console output to command-line. - /// - /// + /// @param inputString static void PrintToCLI(const std::string& stringToPrint); #pragma endregion #pragma region Archived DataModule Handling - /// /// Extracts all files from a zipped DataModule, overwriting any corresponding files already existing. - /// - /// Path to the module to extract. - /// A string containing the progress report of the extraction. + /// @param zippedModulePath Path to the module to extract. + /// @return A string containing the progress report of the extraction. static std::string ExtractZippedDataModule(const std::string& zippedModulePath); #pragma endregion #pragma region Module Validation - /// /// Gets Whether the program is running in module validation mode that is used by an external tool. - /// - /// Whether the program is running in module validation mode. + /// @return Whether the program is running in module validation mode. static bool IsInExternalModuleValidationMode() { return s_ExternalModuleValidation; } - /// /// Sets the program to run in module validation mode to be used by an external tool. - /// static void EnableExternalModuleValidationMode() { s_ExternalModuleValidation = true; } #pragma endregion #pragma region Misc - /// /// Fires up the default browser for the current OS on a specific URL. - /// - /// A string with the URL to send the browser to. + /// @param goToURL A string with the URL to send the browser to. static void OpenBrowserToURL(const std::string_view& goToURL) { std::system(std::string("start ").append(goToURL).c_str()); } - /// /// Searches through an ASCII file on disk for a specific string and tells whether it was found or not. - /// - /// The path to the ASCII file to search. - /// The exact string to look for. Case sensitive! - /// 0 if the string was found in the file or 1 if not. -1 if the file was inaccessible. + /// @param The path to the ASCII file to search. + /// @param The exact string to look for. Case sensitive! + /// @return 0 if the string was found in the file or 1 if not. -1 if the file was inaccessible. static int ASCIIFileContainsString(const std::string& filePath, const std::string_view& findString); #pragma endregion diff --git a/Source/System/Timer.cpp b/Source/System/Timer.cpp index e022708dd2..c36ad6d68b 100644 --- a/Source/System/Timer.cpp +++ b/Source/System/Timer.cpp @@ -2,8 +2,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Timer::Clear() { m_StartRealTime = g_TimerMan.GetRealTickCount(); m_StartSimTime = g_TimerMan.GetSimTickCount(); @@ -11,15 +9,11 @@ namespace RTE { m_SimTimeLimit = -1; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Timer::Create() { m_TicksPerMS = static_cast(g_TimerMan.GetTicksPerSecond()) * 0.001; return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Timer::Create(double simTimeLimit, double elapsedSimTime) { m_TicksPerMS = static_cast(g_TimerMan.GetTicksPerSecond()) * 0.001; SetSimTimeLimitMS(simTimeLimit); @@ -29,8 +23,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Timer::Create(const Timer& reference) { m_StartRealTime = reference.m_StartRealTime; m_StartSimTime = reference.m_StartSimTime; diff --git a/Source/System/Timer.h b/Source/System/Timer.h index 50091118b3..abfb579e17 100644 --- a/Source/System/Timer.h +++ b/Source/System/Timer.h @@ -5,82 +5,62 @@ namespace RTE { - /// /// A precise timer for FPS sync etc. - /// class Timer { public: #pragma region Creation - /// /// Constructor method used to instantiate a Timer object. - /// Timer() { Clear(); Create(); } - /// /// Constructor method used to instantiate a Timer object with a set sim time elapsed. - /// - /// A unsigned long defining this Timer's sim time limit in ms. + /// @param simTimeLimit A unsigned long defining this Timer's sim time limit in ms. Timer(double simTimeLimit) { Clear(); Create(simTimeLimit); } - /// /// Constructor method used to instantiate a Timer object with a set sim time elapsed. - /// - /// A unsigned long defining this Timer's sim time limit in ms. - /// A unsigned long defining the amount of time (in ms) that this Timer should start with elapsed. + /// @param simTimeLimit A unsigned long defining this Timer's sim time limit in ms. + /// @param elapsedSimTime A unsigned long defining the amount of time (in ms) that this Timer should start with elapsed. Timer(double simTimeLimit, double elapsedSimTime) { Clear(); Create(simTimeLimit, elapsedSimTime); } - /// /// Copy constructor method used to instantiate a Timer object identical to an already existing one. - /// - /// A Timer object which is passed in by reference. + /// @param reference A Timer object which is passed in by reference. Timer(const Timer& reference) { Clear(); Create(reference); } - /// /// Makes the Timer object ready for use. - /// - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(); - /// /// Makes the Timer object ready for use. - /// - /// A unsigned long defining this Timer's sim time limit in ms. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param simTimeLimit A unsigned long defining this Timer's sim time limit in ms. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(double simTimeLimit) { return Create(simTimeLimit, 0); } - /// /// Makes the Timer object ready for use. - /// - /// A unsigned long defining this Timer's sim time limit in ms. - /// A unsigned long defining the amount of time (in ms) that this Timer should start with elapsed. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param simTimeLimit A unsigned long defining this Timer's sim time limit in ms. + /// @param elapsedSimTime A unsigned long defining the amount of time (in ms) that this Timer should start with elapsed. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(double simTimeLimit, double elapsedSimTime); - /// /// Creates a Timer to be identical to another, by deep copy. - /// - /// A reference to the Timer to deep copy. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param reference A reference to the Timer to deep copy. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const Timer& reference); #pragma endregion #pragma region Destruction - /// /// Resets the timer so that the elapsed time is 0 ms. - /// // TODO: Figure out why calling Clear() here breaks time. void Reset() { m_StartRealTime = g_TimerMan.GetRealTickCount(); @@ -89,228 +69,160 @@ namespace RTE { #pragma endregion #pragma region Real Time - /// /// Gets the start real time value of this Timer. - /// - /// An int64 value that represents the amount of real time in ms from when windows was started to when Reset() of this Timer was called. + /// @return An int64 value that represents the amount of real time in ms from when windows was started to when Reset() of this Timer was called. int64_t GetStartRealTimeMS() const { return m_StartRealTime; } - /// /// Sets the start real time value of this Timer. - /// - /// An int64 with the new real time value (ms since the OS was started). + /// @param newStartTime An int64 with the new real time value (ms since the OS was started). void SetStartRealTimeMS(const int64_t newStartTime) { m_StartRealTime = newStartTime * m_TicksPerMS; } - /// /// Gets the real time limit value of this Timer, RELATVE to the start time. /// This is when the timer is supposed to show that it has 'expired' or reached whatever time limit it is supposed to keep track of. - /// - /// A positive double with the real time limit relative to the start time. + /// @return A positive double with the real time limit relative to the start time. double GetRealTimeLimitMS() const { return m_RealTimeLimit / m_TicksPerMS; } - /// /// Sets the real time limit value of this Timer, RELATVE to the start time. /// This is when the timer is supposed to show that it has 'expired' or reached whatever time limit it is supposed to keep track of. - /// - /// A positive double with the new real time limit relative to the start time. + /// @param newTimeLimit A positive double with the new real time limit relative to the start time. void SetRealTimeLimitMS(double newTimeLimit) { m_RealTimeLimit = newTimeLimit * m_TicksPerMS; } - /// /// Gets the real time limit value of this Timer, RELATVE to the start time. /// This is when the timer is supposed to show that it has 'expired' or reached whatever time limit it is supposed to keep track of. - /// - /// A positive double with the real time limit relative to the start time. + /// @return A positive double with the real time limit relative to the start time. double GetRealTimeLimitS() const { return m_RealTimeLimit / static_cast(g_TimerMan.GetTicksPerSecond()); } - /// /// Sets the real time limit value of this Timer, RELATVE to the start time. /// This is when the timer is supposed to show that it has 'expired' or reached whatever time limit it is supposed to keep track of. - /// - /// A positive double with the new real time limit relative to the start time. + /// @param newTimeLimit A positive double with the new real time limit relative to the start time. void SetRealTimeLimitS(double newTimeLimit) { m_RealTimeLimit = newTimeLimit * static_cast(g_TimerMan.GetTicksPerSecond()); } - /// /// Gets the elapsed real time in ms since this Timer was Reset(). - /// - /// A unsigned long value that represents the elapsed real time since Reset() in ms. + /// @return A unsigned long value that represents the elapsed real time since Reset() in ms. double GetElapsedRealTimeMS() const { return static_cast(g_TimerMan.GetRealTickCount() - m_StartRealTime) / m_TicksPerMS; } - /// /// Sets the start real time value of this Timer, in seconds. - /// - /// An int64 with the new elapsed time value. + /// @param newElapsedRealTime An int64 with the new elapsed time value. void SetElapsedRealTimeMS(const double newElapsedRealTime) { m_StartRealTime = g_TimerMan.GetRealTickCount() - (newElapsedRealTime * m_TicksPerMS); } - /// /// Gets the elapsed real time in seconds since this Timer was Reset(). - /// - /// A double value that represents the elapsed real time since Reset() in s. + /// @return A double value that represents the elapsed real time since Reset() in s. double GetElapsedRealTimeS() const { return static_cast(g_TimerMan.GetRealTickCount() - m_StartRealTime) / static_cast(g_TimerMan.GetTicksPerSecond()); } - /// /// Sets the start real time value of this Timer. - /// - /// An int64 with the new elapsed time value in seconds. + /// @param newElapsedRealTime An int64 with the new elapsed time value in seconds. void SetElapsedRealTimeS(const double newElapsedRealTime) { m_StartRealTime = g_TimerMan.GetRealTickCount() - (newElapsedRealTime * static_cast(g_TimerMan.GetTicksPerSecond())); } - /// /// Returns how much time in ms that there is left till this Timer reaches a certain time limit. - /// - /// A unsigned long specifying till when there is time. - /// A unsigned long with the time left till the passed in value, or negative if this Timer is already past that point in time. + /// @param when A unsigned long specifying till when there is time. + /// @return A unsigned long with the time left till the passed in value, or negative if this Timer is already past that point in time. unsigned long LeftTillRealMS(int64_t when) { return when - GetElapsedRealTimeMS(); } - /// /// Returns true if the elapsed real time is past a certain amount of time relative to this' start. - /// - /// A long specifying the threshold amount of real time in ms. - /// A bool only yielding true if the elapsed real time is greater than the passed in value. + /// @param limit A long specifying the threshold amount of real time in ms. + /// @return A bool only yielding true if the elapsed real time is greater than the passed in value. bool IsPastRealMS(long limit) { return GetElapsedRealTimeMS() > limit; } - /// /// Returns how much time in ms that there is left till this Timer reaches a certain time limit previously set by SetRealTimeLimitMS. - /// - /// How many MS left till the real time limit, or negative if this Timer is already past that point in time. + /// @return How many MS left till the real time limit, or negative if this Timer is already past that point in time. double LeftTillRealTimeLimitMS() { return GetRealTimeLimitMS() - GetElapsedRealTimeMS(); } - /// /// Returns how much time in ms that there is left till this Timer reaches a certain time limit previously set by SetRealTimeLimitS. - /// - /// How many S left till the real time limit, or negative if this Timer is already past that point in time. + /// @return How many S left till the real time limit, or negative if this Timer is already past that point in time. double LeftTillRealTimeLimitS() { return GetRealTimeLimitS() - GetElapsedRealTimeS(); } - /// /// Returns true if the elapsed real time is past a certain amount of time after the start previously set by SetRealTimeLimit. - /// - /// A bool only yielding true if the elapsed real time is greater than the set limit value. If no limit has been set, this returns false. + /// @return A bool only yielding true if the elapsed real time is greater than the set limit value. If no limit has been set, this returns false. bool IsPastRealTimeLimit() const { return (m_RealTimeLimit == 0) ? true : (m_RealTimeLimit > 0 && (g_TimerMan.GetRealTickCount() - m_StartRealTime) > m_RealTimeLimit); } - /// /// Returns how much progress has been made toward the set time limit previously set by SetRealTimeLimitMS. /// 0 means no progress, 1.0 means the timer has reached, or is beyond the limit. - /// - /// A normalized scalar between 0.0 - 1.0 showing the progress toward the limit. + /// @return A normalized scalar between 0.0 - 1.0 showing the progress toward the limit. double RealTimeLimitProgress() const { return (m_RealTimeLimit == 0) ? 1.0 : (std::min(1.0, GetElapsedRealTimeMS() / (m_RealTimeLimit / m_TicksPerMS))); } - /// /// Returns true or false, depending on whether the elapsed time falls in one of two repeating intervals which divide it. /// This is useful for blink animations etc. - /// - /// An int with the alternating period in ms. The time specified here is how long it will take for the switch to alternate. - /// Whether the elapsed time is in the first state or not. + /// @param period An int with the alternating period in ms. The time specified here is how long it will take for the switch to alternate. + /// @return Whether the elapsed time is in the first state or not. bool AlternateReal(int period) const { return (static_cast(GetElapsedRealTimeMS()) % (period * 2)) > period; } #pragma endregion #pragma region Simulation Time - /// /// Gets the start time value of this Timer. - /// - /// An int64 value that represents the amount of time in ticks from when windows was started to when Reset() of this Timer was called. + /// @return An int64 value that represents the amount of time in ticks from when windows was started to when Reset() of this Timer was called. int64_t GetStartSimTimeMS() const { return m_StartSimTime; } - /// /// Sets the start time value of this Timer, in ticks - /// - /// An int64 with the new time value (ms since windows was started). + /// @param newStartTime An int64 with the new time value (ms since windows was started). void SetStartSimTimeMS(const int64_t newStartTime) { m_StartSimTime = newStartTime * m_TicksPerMS; } - /// /// Sets the sim time limit value of this Timer, RELATVE to the start time. /// This is when the timer is supposed to show that it has 'expired' or reached whatever time limit it is supposed to keep track of. - /// - /// A positive double with the sim time limit relative to the start time. + /// @return A positive double with the sim time limit relative to the start time. double GetSimTimeLimitMS() const { return m_SimTimeLimit / m_TicksPerMS; } - /// /// Sets the sim time limit value of this Timer, RELATVE to the start time. /// This is when the timer is supposed to show that it has 'expired' or reached whatever time limit it is supposed to keep track of. - /// - /// A positive double with the new sim time limit relative to the start time. + /// @param newTimeLimit A positive double with the new sim time limit relative to the start time. void SetSimTimeLimitMS(double newTimeLimit) { m_SimTimeLimit = newTimeLimit * m_TicksPerMS; } - /// /// Sets the sim time limit value of this Timer, RELATVE to the start time. /// This is when the timer is supposed to show that it has 'expired' or reached whatever time limit it is supposed to keep track of. - /// - /// A positive double with the sim time limit relative to the start time. + /// @return A positive double with the sim time limit relative to the start time. double GetSimTimeLimitS() const { return m_SimTimeLimit / static_cast(g_TimerMan.GetTicksPerSecond()); } - /// /// Sets the sim time limit value of this Timer, RELATVE to the start time. /// This is when the timer is supposed to show that it has 'expired' or reached whatever time limit it is supposed to keep track of. - /// - /// A positive double with the new sim time limit relative to the start time. + /// @param newTimeLimit A positive double with the new sim time limit relative to the start time. void SetSimTimeLimitS(double newTimeLimit) { m_SimTimeLimit = newTimeLimit * static_cast(g_TimerMan.GetTicksPerSecond()); } - /// /// Gets the elapsed time in ms since this Timer was Reset(). - /// - /// A unsigned long value that represents the elapsed time since Reset() in ms. + /// @return A unsigned long value that represents the elapsed time since Reset() in ms. double GetElapsedSimTimeMS() const { return static_cast(g_TimerMan.GetSimTickCount() - m_StartSimTime) / m_TicksPerMS; } - /// /// Sets the start time value of this Timer, in ms. - /// - /// A double with the new elapsed time value. + /// @param newElapsedSimTime A double with the new elapsed time value. void SetElapsedSimTimeMS(const double newElapsedSimTime) { m_StartSimTime = g_TimerMan.GetSimTickCount() - (newElapsedSimTime * m_TicksPerMS); } - /// /// Gets the elapsed time in s since this Timer was Reset(). - /// - /// A unsigned long value that represents the elapsed time since Reset() in s. + /// @return A unsigned long value that represents the elapsed time since Reset() in s. double GetElapsedSimTimeS() const { return static_cast(g_TimerMan.GetSimTickCount() - m_StartSimTime) / static_cast(g_TimerMan.GetTicksPerSecond()); } - /// /// Sets the start time value of this Timer, in seconds. - /// - /// An int64 with the new elapsed time value in seconds. + /// @param newElapsedSimTime An int64 with the new elapsed time value in seconds. void SetElapsedSimTimeS(const double newElapsedSimTime) { m_StartSimTime = g_TimerMan.GetSimTickCount() - (newElapsedSimTime * static_cast(g_TimerMan.GetTicksPerSecond())); } - /// /// Returns how much time in ms that there is left till this Timer reaches a certain time limit.a certain time limit. - /// - /// A unsigned long specifying till when there is time. - /// A unsigned long with the time left till the passed in value, or negative if this Timer is already past that point in time. + /// @param when A unsigned long specifying till when there is time. + /// @return A unsigned long with the time left till the passed in value, or negative if this Timer is already past that point in time. double LeftTillSimMS(double when) const { return when - GetElapsedSimTimeMS(); } - /// /// Returns true if the elapsed time is past a certain amount of time. - /// - /// A unsigned long specifying the threshold amount of time in ms. - /// A bool only yielding true if the elapsed time is greater than the passed in value. + /// @param limit A unsigned long specifying the threshold amount of time in ms. + /// @return A bool only yielding true if the elapsed time is greater than the passed in value. bool IsPastSimMS(double limit) const { return GetElapsedSimTimeMS() > limit; } - /// /// Returns how much time in ms that there is left till this Timer reaches a certain time limit previously set by SetSimTimeLimitMS. - /// - /// How many MS left till the sim time limit, or negative if this Timer is already past that point in time. + /// @return How many MS left till the sim time limit, or negative if this Timer is already past that point in time. double LeftTillSimTimeLimitMS() const { return GetSimTimeLimitMS() - GetElapsedSimTimeMS(); } - /// /// Returns how much time in ms that there is left till this Timer reaches a certain time limit previously set by SetSimTimeLimitS. - /// - /// How many S left till the real time limit, or negative if this Timer is already past that point in time. + /// @return How many S left till the real time limit, or negative if this Timer is already past that point in time. double LeftTillSimTimeLimitS() const { return GetSimTimeLimitS() - GetElapsedSimTimeS(); } - /// /// Returns true if the elapsed sim time is past a certain amount of time after the start previously set by SetSimTimeLimit. - /// - /// A bool only yielding true if the elapsed real time is greater than the set limit value. If no limit has been set, this returns false. + /// @return A bool only yielding true if the elapsed real time is greater than the set limit value. If no limit has been set, this returns false. bool IsPastSimTimeLimit() const { return (m_SimTimeLimit == 0) ? true : (m_SimTimeLimit > 0 && (g_TimerMan.GetSimTickCount() - m_StartSimTime) > m_SimTimeLimit); } - /// /// Returns how much progress has been made toward the set time limit previously set by SetSimTimeLimitMS. /// 0 means no progress, 1.0 means the timer has reached, or is beyond the limit. - /// - /// A normalized scalar between 0.0 - 1.0 showing the progress toward the limit. + /// @return A normalized scalar between 0.0 - 1.0 showing the progress toward the limit. double SimTimeLimitProgress() const { return (m_SimTimeLimit == 0) ? 1.0 : (std::min(1.0, GetElapsedSimTimeMS() / (m_SimTimeLimit / m_TicksPerMS))); } - /// /// Returns true or false, depending on whether the elapsed time falls in one of two repeating intervals which divide it. /// This is useful for blink animations etc. - /// - /// An int with the alternating period in ms. The time specified here is how long it will take for the switch to alternate. - /// Whether the elapsed time is in the first state or not. + /// @param period An int with the alternating period in ms. The time specified here is how long it will take for the switch to alternate. + /// @return Whether the elapsed time is in the first state or not. bool AlternateSim(int period) const { return (period == 0) ? true : (static_cast(GetElapsedSimTimeMS()) % (period * 2)) > period; } #pragma endregion @@ -324,9 +236,7 @@ namespace RTE { int64_t m_SimTimeLimit; //!< Tick count, relative to the start time, when this should indicate end or expired in simulation time. private: - /// /// Clears all the member variables of this Timer, effectively resetting the members of this abstraction level only. - /// void Clear(); }; } // namespace RTE diff --git a/Source/System/Vector.cpp b/Source/System/Vector.cpp index 6a825e07e0..7f5f315315 100644 --- a/Source/System/Vector.cpp +++ b/Source/System/Vector.cpp @@ -6,8 +6,6 @@ namespace RTE { const std::string Vector::c_ClassName = "Vector"; - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Vector::ReadProperty(const std::string_view& propName, Reader& reader) { StartPropertyList(return Serializable::ReadProperty(propName, reader)); @@ -17,8 +15,6 @@ namespace RTE { EndPropertyList; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Vector::Save(Writer& writer) const { Serializable::Save(writer); diff --git a/Source/System/Vector.h b/Source/System/Vector.h index d91fb772c8..ddbc1e96d1 100644 --- a/Source/System/Vector.h +++ b/Source/System/Vector.h @@ -11,9 +11,7 @@ namespace RTE { Y = 1 }; - /// /// A useful 2D float vector. - /// class Vector : public Serializable { public: @@ -24,24 +22,18 @@ namespace RTE { float m_Y = 0.0F; //!< Y value of this vector. #pragma region Creation - /// /// Constructor method used to instantiate a Vector object with values (0, 0). - /// inline Vector() = default; - /// /// Constructor method used to instantiate a Vector object from X and Y values. - /// - /// Float defining the initial X value of this Vector. - /// Float defining the initial Y value of this Vector. + /// @param inputX Float defining the initial X value of this Vector. + /// @param inputY Float defining the initial Y value of this Vector. inline Vector(const float inputX, const float inputY) : m_X(inputX), m_Y(inputY){}; #pragma endregion #pragma region Destruction - /// /// Sets both the X and Y of this Vector to zero. - /// inline void Reset() override { m_X = 0.0F; m_Y = 0.0F; @@ -49,154 +41,114 @@ namespace RTE { #pragma endregion #pragma region Getters and Setters - /// /// Gets the X value of this Vector. - /// - /// A float value that represents the X value of this Vector. + /// @return A float value that represents the X value of this Vector. inline float GetX() const { return m_X; } - /// /// Sets the X value of this Vector. - /// - /// A float value that the X value will be set to. - /// Vector reference to this after the operation. + /// @param newX A float value that the X value will be set to. + /// @return Vector reference to this after the operation. inline Vector& SetX(const float newX) { m_X = newX; return *this; } - /// /// Gets the Y value of this Vector. - /// - /// A float value that represents the Y value of this Vector. + /// @return A float value that represents the Y value of this Vector. inline float GetY() const { return m_Y; } - /// /// Sets the Y value of this Vector. - /// - /// A float value that the Y value will be set to. - /// Vector reference to this after the operation. + /// @param newY A float value that the Y value will be set to. + /// @return Vector reference to this after the operation. inline Vector& SetY(const float newY) { m_Y = newY; return *this; } - /// /// Sets both the X and Y values of this Vector. - /// - /// A float value that the X value will be set to. - /// A float value that the Y value will be set to. - /// Vector reference to this after the operation. + /// @param newX A float value that the X value will be set to. + /// @param newY A float value that the Y value will be set to. + /// @return Vector reference to this after the operation. inline Vector& SetXY(const float newX, const float newY) { m_X = newX; m_Y = newY; return *this; } - /// /// Gets the absolute largest of the two elements. Will always be positive. - /// - /// A float describing the largest value of the two, but not the magnitude. + /// @return A float describing the largest value of the two, but not the magnitude. inline float GetLargest() const { return std::max(std::abs(m_X), std::abs(m_Y)); } - /// /// Gets the absolute smallest of the two elements. Will always be positive. - /// - /// A float describing the smallest value of the two, but not the magnitude. + /// @return A float describing the smallest value of the two, but not the magnitude. inline float GetSmallest() const { return std::min(std::abs(m_X), std::abs(m_Y)); } - /// /// Gets a Vector identical to this except that its X component is flipped. - /// - /// Whether to flip the X axis of the return vector or not. - /// A copy of this vector with flipped X axis. + /// @param xFlip Whether to flip the X axis of the return vector or not. + /// @return A copy of this vector with flipped X axis. inline Vector GetXFlipped(const bool xFlip = true) const { return Vector(xFlip ? -m_X : m_X, m_Y); } - /// /// Flips the X element of this Vector. - /// - /// Whether or not to flip the X element or not. - /// Vector reference to this after the operation. + /// @param flipX Whether or not to flip the X element or not. + /// @return Vector reference to this after the operation. inline Vector& FlipX(const bool flipX = true) { *this = GetXFlipped(flipX); return *this; } - /// /// Gets a Vector identical to this except that its Y component is flipped. - /// - /// Whether to flip the Y axis of the return vector or not. - /// A copy of this vector with flipped Y axis. + /// @param yFlip Whether to flip the Y axis of the return vector or not. + /// @return A copy of this vector with flipped Y axis. inline Vector GetYFlipped(const bool yFlip = true) const { return Vector(m_X, yFlip ? -m_Y : m_Y); } - /// /// Flips the Y element of this Vector. - /// - /// Whether or not to flip the Y element or not. - /// Vector reference to this after the operation. + /// @param flipY Whether or not to flip the Y element or not. + /// @return Vector reference to this after the operation. inline Vector& FlipY(const bool flipY = true) { *this = GetYFlipped(flipY); return *this; } - /// /// Indicates whether the X component of this Vector is 0. - /// - /// Whether the X component of this Vector is 0. + /// @return Whether the X component of this Vector is 0. inline bool XIsZero() const { return m_X == 0; } - /// /// Indicates whether the Y component of this Vector is 0. - /// - /// Whether the Y component of this Vector is 0. + /// @return Whether the Y component of this Vector is 0. inline bool YIsZero() const { return m_Y == 0; } - /// /// Indicates whether both X and Y components of this Vector are 0. - /// - /// Whether both X and Y components of this Vector are 0. + /// @return Whether both X and Y components of this Vector are 0. inline bool IsZero() const { return XIsZero() && YIsZero(); } - /// /// Indicates whether the X and Y components of this Vector each have opposite signs to their corresponding components of a passed in Vector. - /// - /// The Vector to compare with. - /// Whether the X and Y components of this Vector each have opposite signs to their corresponding components of a passed in Vector. + /// @param opp The Vector to compare with. + /// @return Whether the X and Y components of this Vector each have opposite signs to their corresponding components of a passed in Vector. inline bool IsOpposedTo(const Vector& opp) const { return ((XIsZero() && opp.XIsZero()) || (std::signbit(m_X) != std::signbit(opp.m_X))) && ((YIsZero() && opp.YIsZero()) || (std::signbit(m_Y) != std::signbit(opp.m_Y))); } #pragma endregion #pragma region Magnitude - /// /// Gets the squared magnitude of this Vector. - /// - /// A float describing the squared magnitude. + /// @return A float describing the squared magnitude. inline float GetSqrMagnitude() const { return m_X * m_X + m_Y * m_Y; } - /// /// Gets the magnitude of this Vector. - /// - /// A float describing the magnitude. + /// @return A float describing the magnitude. inline float GetMagnitude() const { return std::sqrt(GetSqrMagnitude()); } - /// /// Gets whether this Vector's magnitude is less than the specified value. - /// - /// A float value that this Vector's magnitude will be compared against. - /// Whether this Vector's magnitude is less than the specified value. + /// @param magnitude A float value that this Vector's magnitude will be compared against. + /// @return Whether this Vector's magnitude is less than the specified value. inline bool MagnitudeIsLessThan(float magnitude) const { return GetSqrMagnitude() < magnitude * magnitude; } - /// /// Gets whether this Vector's magnitude is greater than the specified value. - /// - /// A float value that this Vector's magnitude will be compared against. - /// Whether this Vector's magnitude is greater than the specified value. + /// @param magnitude A float value that this Vector's magnitude will be compared against. + /// @return Whether this Vector's magnitude is greater than the specified value. inline bool MagnitudeIsGreaterThan(float magnitude) const { return GetSqrMagnitude() > magnitude * magnitude; } - /// /// Sets the magnitude of this Vector. A negative magnitude will invert the Vector's direction. - /// - /// A float value that the magnitude will be set to. - /// Vector reference to this after the operation. + /// @param newMag A float value that the magnitude will be set to. + /// @return Vector reference to this after the operation. inline Vector& SetMagnitude(const float newMag) { if (IsZero()) { SetXY(newMag, 0.0F); @@ -206,11 +158,9 @@ namespace RTE { return *this; } - /// /// Caps the magnitude of this Vector to a max value and keeps its angle intact. - /// - /// A float value that the magnitude will be capped by. - /// Vector reference to this after the operation. + /// @param capMag A float value that the magnitude will be capped by. + /// @return Vector reference to this after the operation. inline Vector& CapMagnitude(const float capMag) { if (capMag == 0.0F) { Reset(); @@ -221,12 +171,10 @@ namespace RTE { return *this; } - /// /// Clamps the magnitude of this Vector between the upper and lower limits, and keeps its angle intact. - /// - /// A float value that defines the lower limit for the magnitude of this Vector. - /// A float value that defines the upper limit for the magnitude of this Vector. - /// A reference to this after the change. + /// @param lowerMagnitudeLimit A float value that defines the lower limit for the magnitude of this Vector. + /// @param upperMagnitudeLimit A float value that defines the upper limit for the magnitude of this Vector. + /// @return A reference to this after the change. inline Vector& ClampMagnitude(float lowerMagnitudeLimit, float upperMagnitudeLimit) { if (upperMagnitudeLimit < lowerMagnitudeLimit) { std::swap(upperMagnitudeLimit, lowerMagnitudeLimit); @@ -241,16 +189,12 @@ namespace RTE { return *this; } - /// /// Returns a Vector that has the same direction as this but with a magnitude of 1.0. - /// - /// A normalized copy of this vector. + /// @return A normalized copy of this vector. inline Vector GetNormalized() const { return *this / GetMagnitude(); } - /// /// Scales this vector to have the same direction but a magnitude of 1.0. - /// - /// Vector reference to this after the operation. + /// @return Vector reference to this after the operation. inline Vector& Normalize() { *this = GetNormalized(); return *this; @@ -258,38 +202,28 @@ namespace RTE { #pragma endregion #pragma region Rotation - /// /// Get this Vector's absolute angle in radians. e.g: when x = 1, y = 0, the value returned here will be 0. x = 0, y = 1 yields -pi/2 here. - /// - /// The absolute angle in radians, in the interval [-0.5 pi, 1.5 pi). + /// @return The absolute angle in radians, in the interval [-0.5 pi, 1.5 pi). inline float GetAbsRadAngle() const { const float radAngle = -std::atan2(m_Y, m_X); return (radAngle < -c_HalfPI) ? (radAngle + c_TwoPI) : radAngle; } - /// /// Sets this Vector's absolute angle in radians. - /// - /// The Vector's new absolute angle in radians. + /// @param newAbsRadAngle The Vector's new absolute angle in radians. inline void SetAbsRadAngle(float newAbsRadAngle) { RadRotate(newAbsRadAngle - GetAbsRadAngle()); } - /// /// Get this Vector's absolute angle in degrees. e.g: when x = 1, y = 0, the value returned here will be 0. x = 0, y = 1 yields -90 here. - /// - /// The absolute angle in degrees, in the interval [-90, 270). + /// @return The absolute angle in degrees, in the interval [-90, 270). inline float GetAbsDegAngle() const { return GetAbsRadAngle() / c_PI * 180.0F; } - /// /// Sets this Vector's absolute angle in degrees. - /// - /// The Vector's new absolute angle in degrees. + /// @param newAbsDegAngle The Vector's new absolute angle in degrees. inline void SetAbsDegAngle(float newAbsDegAngle) { DegRotate(newAbsDegAngle - GetAbsDegAngle()); } - /// /// Returns a copy of this Vector, rotated relatively by an angle in radians. - /// - /// The angle in radians to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. - /// A rotated copy of this Vector. + /// @param angle The angle in radians to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. + /// @return A rotated copy of this Vector. inline Vector GetRadRotatedCopy(const float angle) { Vector returnVector = *this; const float adjustedAngle = -angle; @@ -298,50 +232,38 @@ namespace RTE { return returnVector; } - /// /// Rotate this Vector relatively by an angle in radians. - /// - /// The angle in radians to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. - /// Vector reference to this after the operation. + /// @param angle The angle in radians to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. + /// @return Vector reference to this after the operation. inline Vector& RadRotate(const float angle) { *this = GetRadRotatedCopy(angle); return *this; } - /// /// Returns a copy of this Vector, rotated relatively by an angle in degrees. - /// - /// The angle in degrees to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. - /// A rotated copy of this Vector. + /// @param angle The angle in degrees to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. + /// @return A rotated copy of this Vector. inline Vector GetDegRotatedCopy(const float angle) { return GetRadRotatedCopy(angle * c_PI / 180.0F); }; - /// /// Rotate this Vector relatively by an angle in degrees. - /// - /// The angle in degrees to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. - /// Vector reference to this after the operation. + /// @param angle The angle in degrees to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. + /// @return Vector reference to this after the operation. inline Vector& DegRotate(const float angle) { *this = GetDegRotatedCopy(angle); return *this; } - /// /// Set this Vector to an absolute rotation based on the absolute rotation of another Vector. - /// - /// The reference Vector whose absolute angle from positive X (0 degrees) this Vector will be rotated to. - /// Vector reference to this after the operation. + /// @param refVector The reference Vector whose absolute angle from positive X (0 degrees) this Vector will be rotated to. + /// @return Vector reference to this after the operation. inline Vector& AbsRotateTo(const Vector& refVector) { return RadRotate(refVector.GetAbsRadAngle() - GetAbsRadAngle()); } - /// /// Returns a Vector that is perpendicular to this, rotated PI/2. - /// - /// A Vector that is perpendicular to this, rotated PI/2. + /// @return A Vector that is perpendicular to this, rotated PI/2. inline Vector GetPerpendicular() const { return Vector(m_Y, -m_X); } - /// /// Makes this vector perpendicular to its previous state, rotated PI/2. Much faster than RadRotate by PI/2. - /// - /// Vector reference to this after the operation. + /// @return Vector reference to this after the operation. inline Vector& Perpendicularize() { *this = GetPerpendicular(); return *this; @@ -349,257 +271,195 @@ namespace RTE { #pragma endregion #pragma region Rounding - /// /// Rounds the X and Y values of this Vector upwards. E.g. 0.49 -> 0.0 and 0.5 -> 1.0. - /// - /// Vector reference to this after the operation. + /// @return Vector reference to this after the operation. inline Vector& Round() { *this = GetRounded(); return *this; } - /// /// Sets the X and Y of this Vector to the nearest half value. E.g. 1.0 -> 1.5 and 0.9 -> 0.5. - /// - /// Vector reference to this after the operation. + /// @return Vector reference to this after the operation. inline Vector& ToHalf() { m_X = std::round(m_X * 2) / 2; m_Y = std::round(m_Y * 2) / 2; return *this; } - /// /// Sets the X and Y of this Vector to the greatest integers that are not greater than their original values. E.g. -1.02 becomes -2.0. - /// - /// Vector reference to this after the operation. + /// @return Vector reference to this after the operation. inline Vector& Floor() { *this = GetFloored(); return *this; } - /// /// Sets the X and Y of this Vector to the lowest integers that are not less than their original values. E.g. -1.02 becomes -1.0. - /// - /// Vector reference to this after the operation. + /// @return Vector reference to this after the operation. inline Vector& Ceiling() { *this = GetCeilinged(); return *this; } - /// /// Returns a rounded copy of this Vector. Does not alter this Vector. - /// - /// A rounded copy of this Vector. + /// @return A rounded copy of this Vector. inline Vector GetRounded() const { return Vector(std::round(m_X), std::round(m_Y)); } - /// /// Returns the rounded integer X value of this Vector. - /// - /// An int value that represents the X value of this Vector. + /// @return An int value that represents the X value of this Vector. inline int GetRoundIntX() const { return static_cast(std::round(m_X)); } - /// /// Returns the rounded integer Y value of this Vector. - /// - /// An int value that represents the Y value of this Vector. + /// @return An int value that represents the Y value of this Vector. inline int GetRoundIntY() const { return static_cast(std::round(m_Y)); } - /// /// Returns a floored copy of this Vector. Does not alter this Vector. - /// - /// A floored copy of this Vector. + /// @return A floored copy of this Vector. inline Vector GetFloored() const { return Vector(std::floor(m_X), std::floor(m_Y)); } - /// /// Returns the greatest integer that is not greater than the X value of this Vector. - /// - /// An int value that represents the X value of this Vector. + /// @return An int value that represents the X value of this Vector. inline int GetFloorIntX() const { return static_cast(std::floor(m_X)); } - /// /// Returns the greatest integer that is not greater than the Y value of this Vector. - /// - /// An int value that represents the Y value of this Vector. + /// @return An int value that represents the Y value of this Vector. inline int GetFloorIntY() const { return static_cast(std::floor(m_Y)); } - /// /// Returns a ceilinged copy of this Vector. Does not alter this Vector. - /// - /// A ceilinged copy of this Vector. + /// @return A ceilinged copy of this Vector. inline Vector GetCeilinged() const { return Vector(std::ceil(m_X), std::ceil(m_Y)); } - /// /// Returns the lowest integer that is not less than the X value of this Vector. - /// - /// An int value that represents the X value of this Vector. + /// @return An int value that represents the X value of this Vector. inline int GetCeilingIntX() const { return static_cast(std::ceil(m_X)); } - /// /// Returns the lowest integer that is not less than the Y value of this Vector. - /// - /// An int value that represents the Y value of this Vector. + /// @return An int value that represents the Y value of this Vector. inline int GetCeilingIntY() const { return static_cast(std::ceil(m_Y)); } #pragma endregion #pragma region Vector Products - /// /// Returns the dot product of this Vector and the passed in Vector. - /// - /// The Vector which will be the right hand side operand of the dot product operation. - /// The resulting dot product scalar float. + /// @param rhs The Vector which will be the right hand side operand of the dot product operation. + /// @return The resulting dot product scalar float. inline float Dot(const Vector& rhs) const { return (m_X * rhs.m_X) + (m_Y * rhs.m_Y); } - /// /// Returns the 2D cross product of this Vector and the passed in Vector. This is really the area of the parallelogram that the two vectors form. - /// - /// The Vector which will be the right hand side operand of the cross product operation. - /// The resulting 2D cross product parallelogram area. + /// @param rhs The Vector which will be the right hand side operand of the cross product operation. + /// @return The resulting 2D cross product parallelogram area. inline float Cross(const Vector& rhs) const { return (m_X * rhs.m_Y) - (rhs.m_X * m_Y); } #pragma endregion #pragma region Operator Overloads - /// /// Copy assignment operator for Vectors. - /// - /// A Vector reference. - /// A reference to the changed Vector. + /// @param rhs A Vector reference. + /// @return A reference to the changed Vector. inline Vector& operator=(const Vector& rhs) { m_X = rhs.m_X; m_Y = rhs.m_Y; return *this; } - /// /// Unary negation overload for single Vectors. - /// - /// The resulting Vector. + /// @return The resulting Vector. inline Vector operator-() { return Vector(-m_X, -m_Y); } - /// /// An equality operator for testing if any two Vectors are equal. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// A boolean indicating whether the two operands are equal or not. + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return A boolean indicating whether the two operands are equal or not. inline friend bool operator==(const Vector& lhs, const Vector& rhs) { return lhs.m_X == rhs.m_X && lhs.m_Y == rhs.m_Y; } - /// /// An inequality operator for testing if any two Vectors are unequal. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// A boolean indicating whether the two operands are unequal or not. + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return A boolean indicating whether the two operands are unequal or not. inline friend bool operator!=(const Vector& lhs, const Vector& rhs) { return !(lhs == rhs); } - /// /// A stream insertion operator for sending a Vector to an output stream. - /// - /// An ostream reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// An ostream reference for further use in an expression. + /// @param stream An ostream reference as the left hand side operand. + /// @param operand A Vector reference as the right hand side operand. + /// @return An ostream reference for further use in an expression. inline friend std::ostream& operator<<(std::ostream& stream, const Vector& operand) { stream << "{" << operand.m_X << ", " << operand.m_Y << "}"; return stream; } - /// /// Addition operator overload for Vectors. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// The resulting Vector. + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return The resulting Vector. inline friend Vector operator+(const Vector& lhs, const Vector& rhs) { return Vector(lhs.m_X + rhs.m_X, lhs.m_Y + rhs.m_Y); } - /// /// Subtraction operator overload for Vectors. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// The resulting Vector. + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return The resulting Vector. inline friend Vector operator-(const Vector& lhs, const Vector& rhs) { return Vector(lhs.m_X - rhs.m_X, lhs.m_Y - rhs.m_Y); } - /// /// Multiplication operator overload for a Vector and a float. - /// - /// A float reference as the right hand side operand. - /// The resulting Vector. + /// @param rhs A float reference as the right hand side operand. + /// @return The resulting Vector. inline Vector operator*(const float& rhs) const { return Vector(m_X * rhs, m_Y * rhs); } - /// /// Multiplication operator overload for Vectors. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// The resulting Vector. + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return The resulting Vector. inline friend Vector operator*(const Vector& lhs, const Vector& rhs) { return Vector(lhs.m_X * rhs.m_X, lhs.m_Y * rhs.m_Y); } - /// /// Division operator overload for a Vector and a float. - /// - /// A float reference as the right hand side operand. - /// The resulting Vector. + /// @param rhs A float reference as the right hand side operand. + /// @return The resulting Vector. Vector operator/(const float& rhs) const { return (rhs != 0) ? Vector(m_X / rhs, m_Y / rhs) : Vector(0, 0); } - /// /// Division operator overload for Vectors. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// The resulting Vector. + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return The resulting Vector. inline friend Vector operator/(const Vector& lhs, const Vector& rhs) { return (rhs.m_X != 0 && rhs.m_Y != 0) ? Vector(lhs.m_X / rhs.m_X, lhs.m_Y / rhs.m_Y) : Vector(0, 0); } - /// /// Self-addition operator overload for Vectors. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// A reference to the resulting Vector (the left one). + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return A reference to the resulting Vector (the left one). inline friend Vector& operator+=(Vector& lhs, const Vector& rhs) { lhs.m_X += rhs.m_X; lhs.m_Y += rhs.m_Y; return lhs; } - /// /// Self-subtraction operator overload for Vectors. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// A reference to the resulting Vector (the left one). + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return A reference to the resulting Vector (the left one). inline friend Vector& operator-=(Vector& lhs, const Vector& rhs) { lhs.m_X -= rhs.m_X; lhs.m_Y -= rhs.m_Y; return lhs; } - /// /// Self-multiplication operator overload for a Vector and a float. - /// - /// A float reference as the right hand side operand. - /// A reference to the resulting Vector. + /// @param rhs A float reference as the right hand side operand. + /// @return A reference to the resulting Vector. inline Vector& operator*=(const float& rhs) { m_X *= rhs; m_Y *= rhs; return *this; } - /// /// Self-multiplication operator overload for Vectors. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// A reference to the resulting Vector (the left one). + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return A reference to the resulting Vector (the left one). inline friend Vector& operator*=(Vector& lhs, const Vector& rhs) { lhs.m_X *= rhs.m_X; lhs.m_Y *= rhs.m_Y; return lhs; } - /// /// self-division operator overload for a Vector and a float. - /// - /// A float reference as the right hand side operand. - /// A reference to the resulting Vector. + /// @param rhs A float reference as the right hand side operand. + /// @return A reference to the resulting Vector. inline Vector& operator/=(const float& rhs) { if (rhs != 0) { m_X /= rhs; @@ -608,30 +468,24 @@ namespace RTE { return *this; } - /// /// Self-division operator overload for Vectors. - /// - /// A Vector reference as the left hand side operand. - /// A Vector reference as the right hand side operand. - /// A reference to the resulting Vector (the left one). + /// @param lhs A Vector reference as the left hand side operand. + /// @param rhs A Vector reference as the right hand side operand. + /// @return A reference to the resulting Vector (the left one). inline friend Vector& operator/=(Vector& lhs, const Vector& rhs) { lhs.m_X /= rhs.m_X; lhs.m_Y /= rhs.m_Y; return lhs; } - /// /// Array subscripting to access either the X or Y element of this Vector. - /// - /// An int index indicating which element is requested (X = 0, Y = 1). - /// The requested element. + /// @param rhs An int index indicating which element is requested (X = 0, Y = 1). + /// @return The requested element. inline const float& operator[](const int& rhs) const { return (rhs == 0) ? m_X : m_Y; } - /// /// Array subscripting to access either the X or Y element of this Vector. - /// - /// An int index indicating which element is requested (X = 0, Y = 1). - /// The requested element. + /// @param rhs An int index indicating which element is requested (X = 0, Y = 1). + /// @return The requested element. inline float& operator[](const int& rhs) { return (rhs == 0) ? m_X : m_Y; } #pragma endregion diff --git a/Source/System/Writer.cpp b/Source/System/Writer.cpp index fce6a592c1..a26f24af4a 100644 --- a/Source/System/Writer.cpp +++ b/Source/System/Writer.cpp @@ -5,8 +5,6 @@ namespace RTE { - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Writer::Clear() { m_Stream = nullptr; m_FilePath.clear(); @@ -15,22 +13,16 @@ namespace RTE { m_IndentCount = 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Writer::Writer(const std::string& fileName, bool append, bool createDir) { Clear(); Create(fileName, append, createDir); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Writer::Writer(std::unique_ptr&& stream) { Clear(); Create(std::move(stream)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Writer::Create(const std::string& fileName, bool append, bool createDir) { m_FilePath = fileName; @@ -48,8 +40,6 @@ namespace RTE { return Create(std::move(ofStream)); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int Writer::Create(std::unique_ptr&& stream) { m_Stream = std::move(stream); if (!m_Stream->good()) { @@ -59,8 +49,6 @@ namespace RTE { return 0; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Writer::NewLine(bool toIndent, int lineCount) const { for (int lines = 0; lines < lineCount; ++lines) { *m_Stream << "\n"; diff --git a/Source/System/Writer.h b/Source/System/Writer.h index a0927776dd..6539e29747 100644 --- a/Source/System/Writer.h +++ b/Source/System/Writer.h @@ -3,82 +3,60 @@ namespace RTE { - /// /// Writes RTE objects to std::ostreams. - /// class Writer { public: #pragma region Creation - /// /// Constructor method used to instantiate a Writer object in system memory. Create() should be called before using the object. - /// Writer() { Clear(); } - /// /// Constructor method used to instantiate a Writer object in system memory and make it ready for writing to the passed in file path. - /// - /// Path to the file to open for writing. If the directory doesn't exist the stream will fail to open. - /// Whether to append to the file if it exists, or to overwrite it. - /// Whether to create the directory path to the file name before attempting to open the stream, in case it doesn't exist. + /// @param filename Path to the file to open for writing. If the directory doesn't exist the stream will fail to open. + /// @param append Whether to append to the file if it exists, or to overwrite it. + /// @param createDir Whether to create the directory path to the file name before attempting to open the stream, in case it doesn't exist. Writer(const std::string& fileName, bool append = false, bool createDir = false); - /// /// Constructor method used to instantiate a Writer object in system memory and make it ready for writing to the passed in file path. - /// - /// Stream to write to. + /// @param stream Stream to write to. Writer(std::unique_ptr&& stream); - /// /// Makes the Writer object ready for use. - /// - /// Path to the file to open for writing. If the directory doesn't exist the stream will fail to open. - /// Whether to append to the file if it exists, or to overwrite it. - /// Whether to create the directory path to the file name before attempting to open the stream, in case it doesn't exist. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param filename Path to the file to open for writing. If the directory doesn't exist the stream will fail to open. + /// @param append Whether to append to the file if it exists, or to overwrite it. + /// @param createDir Whether to create the directory path to the file name before attempting to open the stream, in case it doesn't exist. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(const std::string& fileName, bool append = false, bool createDir = false); - /// /// Makes the Writer object ready for use. - /// - /// Stream to write to. - /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + /// @param stream Stream to write to. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int Create(std::unique_ptr&& stream); #pragma endregion #pragma region Getters - /// /// Gets the path to the file being written. - /// - /// The full path to the file being written. + /// @return The full path to the file being written. std::string GetFilePath() const { return m_FilePath; } - /// /// Gets the name (without path) of the file being written. - /// - /// The name of file being written. + /// @return The name of file being written. std::string GetFileName() const { return m_FileName; } - /// /// Gets the folder path (without filename) to where the file is being written. - /// - /// The name of folder being written in. + /// @return The name of folder being written in. std::string GetFolderPath() const { return m_FolderPath; } #pragma endregion #pragma region Writing Operations - /// /// Used to specify the start of an object to be written. - /// - /// The class name of the object about to be written. + /// @param className The class name of the object about to be written. void ObjectStart(const std::string& className) { *m_Stream << className; ++m_IndentCount; } - /// /// Used to specify the end of an object that has just been written. - /// void ObjectEnd() { --m_IndentCount; if (m_IndentCount == 0) { @@ -86,73 +64,55 @@ namespace RTE { } } - /// /// Creates a new line that can be properly indented. - /// - /// Whether to indent the new line or not. - /// How many new lines to create. + /// @param toIndent Whether to indent the new line or not. + /// @param lineCount How many new lines to create. void NewLine(bool toIndent = true, int lineCount = 1) const; - /// /// Creates a new line and writes the specified string to it. - /// - /// The text string to write to the new line. - /// Whether to indent the new line or not. + /// @param textString The text string to write to the new line. + /// @param toIndent Whether to indent the new line or not. void NewLineString(const std::string& textString, bool toIndent = true) const { NewLine(toIndent); *m_Stream << textString; } - /// /// Creates a new line and fills it with slashes to create a divider line for INI. - /// - /// Whether to indent the new line or not. - /// The length of the divider (number of slashes). + /// @param toIndent Whether to indent the new line or not. + /// @param dividerLength The length of the divider (number of slashes). void NewDivider(bool toIndent = true, int dividerLength = 72) const { NewLine(toIndent); *m_Stream << std::string(dividerLength, '/'); } - /// /// Creates a new line and writes the name of the property in preparation to writing it's value. - /// - /// The name of the property to be written. + /// @param propName The name of the property to be written. void NewProperty(const std::string& propName) const { NewLine(); *m_Stream << propName + " = "; } - /// /// Creates a new line and writes the name of the specified property, followed by its set value. - /// - /// The name of the property to be written. - /// The value of the property. + /// @param propName The name of the property to be written. + /// @param propValue The value of the property. template void NewPropertyWithValue(const std::string& propName, const Type& propValue) { NewProperty(propName); *this << propValue; } - /// /// Marks that there is a null reference to an object here. - /// void NoObject() const { *m_Stream << "None"; } #pragma endregion #pragma region Writer Status - /// /// Shows whether the writer is ready to start accepting data streamed to it. - /// - /// Whether the writer is ready to start accepting data streamed to it or not. + /// @return Whether the writer is ready to start accepting data streamed to it or not. bool WriterOK() const { return m_Stream.get() && m_Stream->good(); } - /// /// Returns the underlying stream. - /// std::ostream* GetStream() { return m_Stream.get(); } - /// /// Flushes and closes the output stream of this Writer. This happens automatically at destruction but needs to be called manually if a written file must be read from in the same scope. - /// void EndWrite() { m_Stream->flush(); m_Stream.reset(); @@ -160,11 +120,9 @@ namespace RTE { #pragma endregion #pragma region Operator Overloads - /// /// Elemental types stream insertions. Stream insertion operator overloads for all the elemental types. - /// - /// A reference to the variable that will be written to the ostream. - /// A Writer reference for further use in an expression. + /// @param var A reference to the variable that will be written to the ostream. + /// @return A Writer reference for further use in an expression. Writer& operator<<(const bool& var) { *m_Stream << var; return *this; @@ -236,9 +194,7 @@ namespace RTE { int m_IndentCount; //!< Indentation counter. private: - /// /// Clears all the member variables of this Writer, effectively resetting the members of this abstraction level only. - /// void Clear(); // Disallow the use of some implicit methods. From 56bd587cc2247c83ef0c2a272071fbd65a03c607 Mon Sep 17 00:00:00 2001 From: Causeless Date: Sun, 21 Jan 2024 23:36:09 +0000 Subject: [PATCH 16/24] re-run clang format --- Source/Entities/Scene.cpp | 8 ++++---- Source/Managers/ActivityMan.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Entities/Scene.cpp b/Source/Entities/Scene.cpp index 3ff2cc59f1..082a8cd71c 100644 --- a/Source/Entities/Scene.cpp +++ b/Source/Entities/Scene.cpp @@ -831,10 +831,10 @@ std::vector Scene::GetCopiedSceneLayerBitmaps() { char str[64]; for (int team = Activity::TeamOne; team < Activity::MaxTeamCount; ++team) { - if (m_apUnseenLayer[team]) - { - m_apUnseenLayer[team] - } + if (m_apUnseenLayer[team]) + { + m_apUnseenLayer[team] + } } */ diff --git a/Source/Managers/ActivityMan.cpp b/Source/Managers/ActivityMan.cpp index a43fc21c48..3f8fa2964c 100644 --- a/Source/Managers/ActivityMan.cpp +++ b/Source/Managers/ActivityMan.cpp @@ -108,7 +108,7 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { modifiableScene->MigrateToModule(g_PresetMan.GetModuleID(c_UserScriptedSavesModuleName)); modifiableScene->SetSavedGameInternal(true); - // Make sure the terrain is also treated as an original preset, otherwise it will screw up if we save then load then save again, since it'll try to be a CopyOf of itself. + // Make sure the terrain is also treated as an original preset, otherwise it will screw up if we save then load then save again, since it'll try to be a CopyOf of itself. modifiableScene->GetTerrain()->SetPresetName(fileName); modifiableScene->GetTerrain()->MigrateToModule(g_PresetMan.GetModuleID(c_UserScriptedSavesModuleName)); From 2bc4bdd94bfcda906072a690bcb5dd03f309985c Mon Sep 17 00:00:00 2001 From: Causeless Date: Sun, 6 Jul 2025 14:48:47 +0100 Subject: [PATCH 17/24] added comments so I kinda vaguely remember what I need to do --- Source/Entities/Scene.cpp | 2 ++ Source/Managers/ActivityMan.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/Source/Entities/Scene.cpp b/Source/Entities/Scene.cpp index 833defe2ee..21d7e8ae8c 100644 --- a/Source/Entities/Scene.cpp +++ b/Source/Entities/Scene.cpp @@ -850,6 +850,8 @@ int Scene::SaveData(std::string pathBase, bool doAsyncSaves) { std::vector Scene::GetCopiedSceneLayerBitmaps() { std::vector layerInfos; + // TODO- implement this. Basically just copy each scene layer to the vector + // This is for saving- we block to copy these layers, but it means the actual saving (compression, write to disk etc) can be done async /* // Save Terrain's data m_pTerrain diff --git a/Source/Managers/ActivityMan.cpp b/Source/Managers/ActivityMan.cpp index 0a48e78fea..6af6ec3099 100644 --- a/Source/Managers/ActivityMan.cpp +++ b/Source/Managers/ActivityMan.cpp @@ -82,6 +82,8 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { return false; } + // THIS BLOCK OF CODE NEEDS ZIPPIFIED! + /* // TODO, save to a zip instead of a directory std::filesystem::create_directory(g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + fileName); @@ -90,6 +92,7 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { g_ConsoleMan.PrintString("ERROR: Failed to save scene bitmaps while saving!"); return false; } + */ // We need a copy of our scene, because we have to do some fixup to remove PLACEONLOAD items and only keep the current MovableMan state. std::unique_ptr modifiableScene(dynamic_cast(scene->Clone())); @@ -165,6 +168,8 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { bool ActivityMan::LoadAndLaunchGame(const std::string& fileName) { m_SaveGameTask.wait(); + // TODO- this needs to load a zip! + std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + fileName + "/Save.ini"; if (!std::filesystem::exists(saveFilePath)) { From 8093ff58878dc6f8095ce6d17d000af63be33677 Mon Sep 17 00:00:00 2001 From: Causeless Date: Sun, 6 Jul 2025 16:13:39 +0100 Subject: [PATCH 18/24] A little progress --- Source/Entities/SLTerrain.cpp | 6 ++++++ Source/Entities/SLTerrain.h | 4 ++++ Source/Entities/Scene.cpp | 9 +++------ Source/Entities/SceneLayer.cpp | 2 +- Source/Entities/SceneLayer.h | 2 +- Source/Managers/ActivityMan.cpp | 19 +++++++++++++++++-- 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/Source/Entities/SLTerrain.cpp b/Source/Entities/SLTerrain.cpp index c86b98fd1b..c8710987fe 100644 --- a/Source/Entities/SLTerrain.cpp +++ b/Source/Entities/SLTerrain.cpp @@ -308,6 +308,12 @@ int SLTerrain::SaveData(const std::string& pathBase, bool doAsyncSaves) { return 0; } +void SLTerrain::CopyBitmapData(std::vector& layerInfos) const { + layerInfos.emplace_back(std::string("Mat"), SceneLayer::CopyBitmap()); + layerInfos.emplace_back(std::string("FG"), m_FGColorLayer->CopyBitmap()); + layerInfos.emplace_back(std::string("BG"), m_BGColorLayer->CopyBitmap()); +} + int SLTerrain::ClearData() { RTEAssert(SceneLayer::ClearData() == 0, "Failed to clear material bitmap data of an SLTerrain!"); RTEAssert(m_FGColorLayer && m_FGColorLayer->ClearData() == 0, "Failed to clear the foreground color bitmap data of an SLTerrain!"); diff --git a/Source/Entities/SLTerrain.h b/Source/Entities/SLTerrain.h index d558cc7348..227c2d9b3a 100644 --- a/Source/Entities/SLTerrain.h +++ b/Source/Entities/SLTerrain.h @@ -68,6 +68,10 @@ namespace RTE { /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int SaveData(const std::string& pathBase, bool doAsyncSaves = true) override; + /// Copies bitmap data into layerInfos. + /// @param layerInfos List of SceneLayerInfo to emplace our copied data into. + void CopyBitmapData(std::vector& layerInfos) const; + /// Clears out any previously loaded bitmap data from memory. /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int ClearData() override; diff --git a/Source/Entities/Scene.cpp b/Source/Entities/Scene.cpp index 21d7e8ae8c..eb511c397d 100644 --- a/Source/Entities/Scene.cpp +++ b/Source/Entities/Scene.cpp @@ -850,11 +850,8 @@ int Scene::SaveData(std::string pathBase, bool doAsyncSaves) { std::vector Scene::GetCopiedSceneLayerBitmaps() { std::vector layerInfos; - // TODO- implement this. Basically just copy each scene layer to the vector - // This is for saving- we block to copy these layers, but it means the actual saving (compression, write to disk etc) can be done async - /* // Save Terrain's data - m_pTerrain + m_pTerrain->CopyBitmapData(layerInfos); // Don't bother saving background layers to disk, as they are never altered @@ -864,10 +861,10 @@ std::vector Scene::GetCopiedSceneLayerBitmaps() { { if (m_apUnseenLayer[team]) { - m_apUnseenLayer[team] + std::snprintf(str, sizeof(str), "US T%d", team); + layerInfos.emplace_back(std::string(str), m_apUnseenLayer[team]->CopyBitmap()); } } - */ return layerInfos; } diff --git a/Source/Entities/SceneLayer.cpp b/Source/Entities/SceneLayer.cpp index 2247da66c0..f70b58c6ea 100644 --- a/Source/Entities/SceneLayer.cpp +++ b/Source/Entities/SceneLayer.cpp @@ -246,7 +246,7 @@ int SceneLayerImpl::SaveData(const std::string& } template -std::unique_ptr SceneLayerImpl::CopyBitmap() { +std::unique_ptr SceneLayerImpl::CopyBitmap() const { BITMAP* outputBitmap = create_bitmap_ex(bitmap_color_depth(m_MainBitmap), m_MainBitmap->w, m_MainBitmap->h); if (m_MainBitmap) { outputBitmap = create_bitmap_ex(bitmap_color_depth(m_MainBitmap), m_MainBitmap->w, m_MainBitmap->h); diff --git a/Source/Entities/SceneLayer.h b/Source/Entities/SceneLayer.h index 4281dc0e2f..5dec8872dc 100644 --- a/Source/Entities/SceneLayer.h +++ b/Source/Entities/SceneLayer.h @@ -92,7 +92,7 @@ namespace RTE { /// Copies the bitmap. /// @return The copied bitmap. - std::unique_ptr CopyBitmap(); + std::unique_ptr CopyBitmap() const; #pragma endregion #pragma region Getters and Setters diff --git a/Source/Managers/ActivityMan.cpp b/Source/Managers/ActivityMan.cpp index 6af6ec3099..6512c2810d 100644 --- a/Source/Managers/ActivityMan.cpp +++ b/Source/Managers/ActivityMan.cpp @@ -136,7 +136,10 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { writer->NewPropertyWithValue("PlaceUnitsIfSceneIsRestarted", g_SceneMan.GetPlaceUnitsOnLoad()); writer->NewPropertyWithValue("Scene", modifiableScene.get()); - auto saveWriterData = [&](Writer* writerToSave) { + // Get BITMAPS so save into our zip + std::vector sceneLayerInfos = scene->GetCopiedSceneLayerBitmaps(); + + auto saveWriterData = [&](Writer* writerToSave, std::vector&& sceneLayerInfos) { std::stringstream* stream = static_cast(writerToSave->GetStream()); stream->flush(); @@ -150,13 +153,25 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { zipWriteInFileInZip(zippedSaveFile, streamAsString.data(), streamAsString.size()); zipCloseFileInZip(zippedSaveFile); + PALETTE palette; + get_palette(palette); + + for (const SceneLayerInfo& layerInfo : sceneLayerInfos) + { + // Allego lacks the fucking ability to save/load png from a byte stream + // AAAAAAAAAAAAAAAAAAAAAAAAAAA + //zipOpenNewFileInZip(zippedSaveFile, (fileName + " " + layerInfo.name + ".png").c_str(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, defaultCompression); + //zipWriteInFileInZip(zippedSaveFile, streamAsString.data(), streamAsString.size()); + //zipCloseFileInZip(zippedSaveFile); + } + zipClose(zippedSaveFile, fileName.c_str()); delete writerToSave; }; // For some reason I can't std::move a unique ptr in, so just releasing and deleting manually... - m_SaveGameTask.push_back(g_ThreadMan.GetBackgroundThreadPool().submit(saveWriterData, writer.release())); + m_SaveGameTask.push_back(g_ThreadMan.GetBackgroundThreadPool().submit(saveWriterData, writer.release(), std::move(sceneLayerInfos))); // We didn't transfer ownership, so we must be very careful that sceneAltered's deletion doesn't touch the stuff we got from MovableMan. modifiableScene->ClearPlacedObjectSet(Scene::PlacedObjectSets::PLACEONLOAD, false); From 5863694a23fe6ce21128c77aad295058c48b5e93 Mon Sep 17 00:00:00 2001 From: Causeless Date: Sun, 6 Jul 2025 17:22:40 +0100 Subject: [PATCH 19/24] a vague probably non-working way to save to memory buffer --- .../addons/loadpng/loadpng.h | 3 + .../addons/loadpng/savepng.c | 125 ++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/external/sources/allegro 4.4.3.1-custom/addons/loadpng/loadpng.h b/external/sources/allegro 4.4.3.1-custom/addons/loadpng/loadpng.h index 4aedb0cbb9..86a9d16f5b 100644 --- a/external/sources/allegro 4.4.3.1-custom/addons/loadpng/loadpng.h +++ b/external/sources/allegro 4.4.3.1-custom/addons/loadpng/loadpng.h @@ -69,6 +69,9 @@ APNG_FUNC(int, save_png, (AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pa /* Save a bitmap to a PACKFILE in PNG format. */ APNG_FUNC(int, save_png_pf, (PACKFILE *pack, BITMAP *bmp, AL_CONST RGB *pal)); +/* Save a bitmap to a memory buffer in PNG format. */ +APNG_FUNC(int, save_memory_png, (void *buffer, BITMAP *bmp, AL_CONST RGB *pal)); + /* Adds `PNG' to Allegro's internal file type table. * You can then just use load_bitmap and save_bitmap as usual. */ diff --git a/external/sources/allegro 4.4.3.1-custom/addons/loadpng/savepng.c b/external/sources/allegro 4.4.3.1-custom/addons/loadpng/savepng.c index ab45006590..8ba5fb1164 100644 --- a/external/sources/allegro 4.4.3.1-custom/addons/loadpng/savepng.c +++ b/external/sources/allegro 4.4.3.1-custom/addons/loadpng/savepng.c @@ -292,6 +292,131 @@ static int really_save_png(PACKFILE *fp, BITMAP *bmp, AL_CONST RGB *pal) return -1; } +/* save_memory_png: + * Writes a non-interlaced, no-frills PNG, taking the usual save_xyz + * parameters. Returns non-zero on error. + */ +static int save_memory_png(void *buffer, BITMAP *bmp, AL_CONST RGB *pal) +{ + jmp_buf jmpbuf; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + int depth; + int colour_type; + + depth = bitmap_color_depth(bmp); + if (depth == 8 && !pal) + return -1; + + /* Create and initialize the png_struct with the + * desired error handler functions. + */ + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + (void *)NULL, NULL, NULL); + if (!png_ptr) + goto Error; + + /* Allocate/initialize the image information data. */ + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + goto Error; + + /* Set error handling. */ + if (setjmp(jmpbuf)) { + /* If we get here, we had a problem reading the file. */ + goto Error; + } + png_set_error_fn(png_ptr, jmpbuf, user_error_fn, NULL); + + /* Use memory routines. */ + png_set_write_fn(png_ptr, buffer, NULL, NULL); + + /* Set the image information here. Width and height are up to 2^31, + * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on + * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, + * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, + * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or + * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST + * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. + */ + if (depth == 8) + colour_type = PNG_COLOR_TYPE_PALETTE; + else if (depth == 32) + colour_type = PNG_COLOR_TYPE_RGB_ALPHA; + else + colour_type = PNG_COLOR_TYPE_RGB; + + /* Set compression level. */ + png_set_compression_level(png_ptr, _png_compression_level); + + png_set_IHDR(png_ptr, info_ptr, bmp->w, bmp->h, 8, colour_type, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + /* Set the palette if there is one. Required for indexed-color images. */ + if (colour_type == PNG_COLOR_TYPE_PALETTE) { + png_color palette[256]; + int i; + + for (i = 0; i < 256; i++) { + palette[i].red = pal[i].r; /* 64 -> 256 */ + palette[i].green = pal[i].g; + palette[i].blue = pal[i].b; + } + + /* Set palette colors. */ + png_set_PLTE(png_ptr, info_ptr, palette, 256); + } + + /* Optionally write comments into the image ... Nah. */ + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* Once we write out the header, the compression type on the text + * chunks gets changed to PNG_TEXT_COMPRESSION_NONE_WR or + * PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again + * at the end. + */ + + /* Save the data. */ + switch (depth) { + case 8: + if (!save_indexed(png_ptr, bmp)) + goto Error; + break; + case 15: + case 16: + case 24: + if (!save_rgb(png_ptr, bmp)) + goto Error; + break; + case 32: + if (!save_rgba(png_ptr, bmp)) + goto Error; + break; + default: + ASSERT(FALSE); + goto Error; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + return 0; + + Error: + + if (png_ptr) { + if (info_ptr) + png_destroy_write_struct(&png_ptr, &info_ptr); + else + png_destroy_write_struct(&png_ptr, NULL); + } + + return -1; +} int save_png(AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal) { From 4de5aa1c0a6a8e125035b3298bc271f6e814da09 Mon Sep 17 00:00:00 2001 From: Causeless Date: Mon, 7 Jul 2025 21:43:39 +0100 Subject: [PATCH 20/24] saving zips! need to do loading next --- RTEA.vcxproj | 52 ++-- RTEA.vcxproj.filters | 9 +- Source/Managers/ActivityMan.cpp | 52 +++- .../_Bin/allegro-debug-32.lib | Bin 5634812 -> 5950612 bytes .../_Bin/allegro-debug-release-32.lib | Bin 6548040 -> 6840014 bytes .../_Bin/allegro-debug-release.lib | Bin 6933400 -> 7227600 bytes .../_Bin/allegro-debug.lib | Bin 6140782 -> 6458436 bytes .../_Bin/allegro-release-32.lib | Bin 1366918 -> 1370588 bytes .../_Bin/allegro-release.lib | Bin 2300212 -> 2305286 bytes .../_Bin/allegrogl-debug-32.lib | Bin 1585140 -> 1593068 bytes .../_Bin/allegrogl-debug-release-32.lib | Bin 2007586 -> 2014454 bytes .../_Bin/allegrogl-debug-release.lib | Bin 2092616 -> 2099126 bytes .../_Bin/allegrogl-debug.lib | Bin 1651642 -> 1655122 bytes .../_Bin/allegrogl-release-32.lib | Bin 1023592 -> 1023758 bytes .../_Bin/allegrogl-release.lib | Bin 1170142 -> 1174056 bytes .../_Bin/loadpng-debug-32.lib | Bin 90326 -> 97286 bytes .../_Bin/loadpng-debug-release-32.lib | Bin 89208 -> 96580 bytes .../_Bin/loadpng-debug-release.lib | Bin 94526 -> 100754 bytes .../_Bin/loadpng-debug.lib | Bin 99144 -> 105282 bytes .../_Bin/loadpng-release-32.lib | Bin 16848 -> 18362 bytes .../_Bin/loadpng-release.lib | Bin 21294 -> 26096 bytes .../addons/loadpng/loadpng.h | 7 +- .../addons/loadpng/savepng.c | 76 ++--- .../allegro 4.4.3.1-custom/allegro.vcxproj | 12 +- .../allegro 4.4.3.1-custom/allegrogl.vcxproj | 12 +- .../allegro 4.4.3.1-custom/loadpng.vcxproj | 12 +- external/sources/fmem/.appveyor.yml | 62 ++++ .../sources/fmem/.cmake/FindCriterion.cmake | 27 ++ .../workflows/cmake-multi-platform.yml | 113 +++++++ external/sources/fmem/.gitignore | 157 ++++++++++ external/sources/fmem/.travis.yml | 31 ++ external/sources/fmem/CMakeLists.txt | 209 +++++++++++++ external/sources/fmem/LICENSE | 21 ++ external/sources/fmem/README.md | 24 ++ external/sources/fmem/_Bin/ALL_BUILD.vcxproj | 184 ++++++++++++ .../fmem/_Bin/ALL_BUILD.vcxproj.filters | 8 + .../sources/fmem/_Bin/DartConfiguration.tcl | 106 +++++++ external/sources/fmem/_Bin/INSTALL.vcxproj | 208 +++++++++++++ .../sources/fmem/_Bin/INSTALL.vcxproj.filters | 13 + external/sources/fmem/_Bin/ZERO_CHECK.vcxproj | 180 +++++++++++ .../fmem/_Bin/ZERO_CHECK.vcxproj.filters | 24 ++ .../fmem/_Bin/fmem.dir/Debug/fmem.lib.recipe | 11 + .../Debug/fmem.tlog/CL.command.1.tlog | Bin 0 -> 1496 bytes .../fmem.dir/Debug/fmem.tlog/CL.read.1.tlog | Bin 0 -> 27054 bytes .../fmem.dir/Debug/fmem.tlog/CL.write.1.tlog | Bin 0 -> 838 bytes .../fmem.dir/Debug/fmem.tlog/Cl.items.tlog | 1 + .../fmem.tlog/CustomBuild.command.1.tlog | 10 + .../Debug/fmem.tlog/CustomBuild.read.1.tlog | 27 ++ .../Debug/fmem.tlog/CustomBuild.write.1.tlog | 2 + .../Debug/fmem.tlog/Lib-link.read.1.tlog | Bin 0 -> 678 bytes .../Debug/fmem.tlog/Lib-link.write.1.tlog | Bin 0 -> 568 bytes .../Debug/fmem.tlog/Lib.command.1.tlog | Bin 0 -> 734 bytes .../Debug/fmem.tlog/fmem.lastbuildstate | 2 + .../_Bin/fmem.dir/MinSizeRel/fmem.lib.recipe | 11 + .../MinSizeRel/fmem.tlog/CL.command.1.tlog | Bin 0 -> 1524 bytes .../MinSizeRel/fmem.tlog/CL.read.1.tlog | Bin 0 -> 27054 bytes .../MinSizeRel/fmem.tlog/CL.write.1.tlog | Bin 0 -> 590 bytes .../MinSizeRel/fmem.tlog/Cl.items.tlog | 1 + .../fmem.tlog/CustomBuild.command.1.tlog | 10 + .../fmem.tlog/CustomBuild.read.1.tlog | 27 ++ .../fmem.tlog/CustomBuild.write.1.tlog | 2 + .../MinSizeRel/fmem.tlog/Lib-link.read.1.tlog | Bin 0 -> 698 bytes .../fmem.tlog/Lib-link.write.1.tlog | Bin 0 -> 588 bytes .../MinSizeRel/fmem.tlog/Lib.command.1.tlog | Bin 0 -> 764 bytes .../MinSizeRel/fmem.tlog/fmem.lastbuildstate | 2 + .../fmem.dir/RelWithDebInfo/fmem.lib.recipe | 11 + .../fmem.tlog/CL.command.1.tlog | Bin 0 -> 1556 bytes .../RelWithDebInfo/fmem.tlog/CL.read.1.tlog | Bin 0 -> 27054 bytes .../RelWithDebInfo/fmem.tlog/CL.write.1.tlog | Bin 0 -> 874 bytes .../RelWithDebInfo/fmem.tlog/Cl.items.tlog | 1 + .../fmem.tlog/CustomBuild.command.1.tlog | 10 + .../fmem.tlog/CustomBuild.read.1.tlog | 27 ++ .../fmem.tlog/CustomBuild.write.1.tlog | 2 + .../fmem.tlog/Lib-link.read.1.tlog | Bin 0 -> 714 bytes .../fmem.tlog/Lib-link.write.1.tlog | Bin 0 -> 604 bytes .../fmem.tlog/Lib.command.1.tlog | Bin 0 -> 788 bytes .../fmem.tlog/fmem.lastbuildstate | 2 + .../_Bin/fmem.dir/Release/fmem.lib.recipe | 11 + .../Release/fmem.tlog/CL.command.1.tlog | Bin 0 -> 1506 bytes .../fmem.dir/Release/fmem.tlog/CL.read.1.tlog | Bin 0 -> 27054 bytes .../Release/fmem.tlog/CL.write.1.tlog | Bin 0 -> 584 bytes .../fmem.dir/Release/fmem.tlog/Cl.items.tlog | 1 + .../fmem.tlog/CustomBuild.command.1.tlog | 10 + .../Release/fmem.tlog/CustomBuild.read.1.tlog | 27 ++ .../fmem.tlog/CustomBuild.write.1.tlog | 2 + .../Release/fmem.tlog/Lib-link.read.1.tlog | Bin 0 -> 686 bytes .../Release/fmem.tlog/Lib-link.write.1.tlog | Bin 0 -> 576 bytes .../Release/fmem.tlog/Lib.command.1.tlog | Bin 0 -> 746 bytes .../Release/fmem.tlog/fmem.lastbuildstate | 2 + external/sources/fmem/_Bin/fmem.sln | 67 +++++ external/sources/fmem/_Bin/fmem.vcxproj | 281 ++++++++++++++++++ .../sources/fmem/_Bin/fmem.vcxproj.filters | 16 + external/sources/fmem/_Bin/gen/fmem.h | 69 +++++ .../_Bin/x64/Debug/ALL_BUILD/ALL_BUILD.recipe | 14 + .../ALL_BUILD.tlog/ALL_BUILD.lastbuildstate | 2 + .../ALL_BUILD.tlog/CustomBuild.command.1.tlog | 10 + .../ALL_BUILD.tlog/CustomBuild.read.1.tlog | 27 ++ .../ALL_BUILD.tlog/CustomBuild.write.1.tlog | 2 + .../x64/Debug/ZERO_CHECK/ZERO_CHECK.recipe | 11 + .../CustomBuild.command.1.tlog | 10 + .../ZERO_CHECK.tlog/CustomBuild.read.1.tlog | 28 ++ .../ZERO_CHECK.tlog/CustomBuild.write.1.tlog | 2 + .../ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate | 2 + .../x64/MinSizeRel/ALL_BUILD/ALL_BUILD.recipe | 14 + .../ALL_BUILD.tlog/ALL_BUILD.lastbuildstate | 2 + .../ALL_BUILD.tlog/CustomBuild.command.1.tlog | 10 + .../ALL_BUILD.tlog/CustomBuild.read.1.tlog | 27 ++ .../ALL_BUILD.tlog/CustomBuild.write.1.tlog | 2 + .../MinSizeRel/ZERO_CHECK/ZERO_CHECK.recipe | 11 + .../CustomBuild.command.1.tlog | 10 + .../ZERO_CHECK.tlog/CustomBuild.read.1.tlog | 28 ++ .../ZERO_CHECK.tlog/CustomBuild.write.1.tlog | 2 + .../ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate | 2 + .../RelWithDebInfo/ALL_BUILD/ALL_BUILD.recipe | 14 + .../ALL_BUILD.tlog/ALL_BUILD.lastbuildstate | 2 + .../ALL_BUILD.tlog/CustomBuild.command.1.tlog | 10 + .../ALL_BUILD.tlog/CustomBuild.read.1.tlog | 27 ++ .../ALL_BUILD.tlog/CustomBuild.write.1.tlog | 2 + .../ZERO_CHECK/ZERO_CHECK.recipe | 11 + .../CustomBuild.command.1.tlog | 10 + .../ZERO_CHECK.tlog/CustomBuild.read.1.tlog | 28 ++ .../ZERO_CHECK.tlog/CustomBuild.write.1.tlog | 2 + .../ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate | 2 + .../x64/Release/ALL_BUILD/ALL_BUILD.recipe | 14 + .../ALL_BUILD.tlog/ALL_BUILD.lastbuildstate | 2 + .../ALL_BUILD.tlog/CustomBuild.command.1.tlog | 10 + .../ALL_BUILD.tlog/CustomBuild.read.1.tlog | 27 ++ .../ALL_BUILD.tlog/CustomBuild.write.1.tlog | 2 + .../x64/Release/ZERO_CHECK/ZERO_CHECK.recipe | 11 + .../CustomBuild.command.1.tlog | 10 + .../ZERO_CHECK.tlog/CustomBuild.read.1.tlog | 28 ++ .../ZERO_CHECK.tlog/CustomBuild.write.1.tlog | 2 + .../ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate | 2 + external/sources/fmem/include/fmem.h | 69 +++++ external/sources/fmem/include/fmem.h.in | 69 +++++ external/sources/fmem/src/alloc.c | 61 ++++ external/sources/fmem/src/alloc.h | 21 ++ external/sources/fmem/src/fmem-fopencookie.c | 127 ++++++++ external/sources/fmem/src/fmem-funopen.c | 137 +++++++++ .../sources/fmem/src/fmem-open_memstream.c | 41 +++ external/sources/fmem/src/fmem-tmpfile.c | 75 +++++ .../sources/fmem/src/fmem-winapi-tmpfile.c | 124 ++++++++ external/sources/fmem/test/CMakeLists.txt | 67 +++++ external/sources/fmem/test/tests.c | 118 ++++++++ 144 files changed, 3480 insertions(+), 105 deletions(-) create mode 100644 external/sources/fmem/.appveyor.yml create mode 100644 external/sources/fmem/.cmake/FindCriterion.cmake create mode 100644 external/sources/fmem/.github/workflows/cmake-multi-platform.yml create mode 100644 external/sources/fmem/.gitignore create mode 100644 external/sources/fmem/.travis.yml create mode 100644 external/sources/fmem/CMakeLists.txt create mode 100644 external/sources/fmem/LICENSE create mode 100644 external/sources/fmem/README.md create mode 100644 external/sources/fmem/_Bin/ALL_BUILD.vcxproj create mode 100644 external/sources/fmem/_Bin/ALL_BUILD.vcxproj.filters create mode 100644 external/sources/fmem/_Bin/DartConfiguration.tcl create mode 100644 external/sources/fmem/_Bin/INSTALL.vcxproj create mode 100644 external/sources/fmem/_Bin/INSTALL.vcxproj.filters create mode 100644 external/sources/fmem/_Bin/ZERO_CHECK.vcxproj create mode 100644 external/sources/fmem/_Bin/ZERO_CHECK.vcxproj.filters create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.lib.recipe create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/CL.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/CL.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/CL.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/Cl.items.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/Lib-link.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/Lib-link.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/Lib.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Debug/fmem.tlog/fmem.lastbuildstate create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.lib.recipe create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/CL.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/CL.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/CL.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/Cl.items.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/Lib-link.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/Lib-link.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/Lib.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/MinSizeRel/fmem.tlog/fmem.lastbuildstate create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.lib.recipe create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/CL.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/CL.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/CL.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/Cl.items.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/Lib-link.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/Lib-link.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/Lib.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/RelWithDebInfo/fmem.tlog/fmem.lastbuildstate create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.lib.recipe create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/CL.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/CL.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/CL.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/Cl.items.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/Lib-link.read.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/Lib-link.write.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/Lib.command.1.tlog create mode 100644 external/sources/fmem/_Bin/fmem.dir/Release/fmem.tlog/fmem.lastbuildstate create mode 100644 external/sources/fmem/_Bin/fmem.sln create mode 100644 external/sources/fmem/_Bin/fmem.vcxproj create mode 100644 external/sources/fmem/_Bin/fmem.vcxproj.filters create mode 100644 external/sources/fmem/_Bin/gen/fmem.h create mode 100644 external/sources/fmem/_Bin/x64/Debug/ALL_BUILD/ALL_BUILD.recipe create mode 100644 external/sources/fmem/_Bin/x64/Debug/ALL_BUILD/ALL_BUILD.tlog/ALL_BUILD.lastbuildstate create mode 100644 external/sources/fmem/_Bin/x64/Debug/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Debug/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Debug/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Debug/ZERO_CHECK/ZERO_CHECK.recipe create mode 100644 external/sources/fmem/_Bin/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ALL_BUILD/ALL_BUILD.recipe create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ALL_BUILD/ALL_BUILD.tlog/ALL_BUILD.lastbuildstate create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ZERO_CHECK/ZERO_CHECK.recipe create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/MinSizeRel/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ALL_BUILD/ALL_BUILD.recipe create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ALL_BUILD/ALL_BUILD.tlog/ALL_BUILD.lastbuildstate create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ZERO_CHECK/ZERO_CHECK.recipe create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/RelWithDebInfo/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate create mode 100644 external/sources/fmem/_Bin/x64/Release/ALL_BUILD/ALL_BUILD.recipe create mode 100644 external/sources/fmem/_Bin/x64/Release/ALL_BUILD/ALL_BUILD.tlog/ALL_BUILD.lastbuildstate create mode 100644 external/sources/fmem/_Bin/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Release/ZERO_CHECK/ZERO_CHECK.recipe create mode 100644 external/sources/fmem/_Bin/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog create mode 100644 external/sources/fmem/_Bin/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate create mode 100644 external/sources/fmem/include/fmem.h create mode 100644 external/sources/fmem/include/fmem.h.in create mode 100644 external/sources/fmem/src/alloc.c create mode 100644 external/sources/fmem/src/alloc.h create mode 100644 external/sources/fmem/src/fmem-fopencookie.c create mode 100644 external/sources/fmem/src/fmem-funopen.c create mode 100644 external/sources/fmem/src/fmem-open_memstream.c create mode 100644 external/sources/fmem/src/fmem-tmpfile.c create mode 100644 external/sources/fmem/src/fmem-winapi-tmpfile.c create mode 100644 external/sources/fmem/test/CMakeLists.txt create mode 100644 external/sources/fmem/test/tests.c diff --git a/RTEA.vcxproj b/RTEA.vcxproj index bc8a3fa286..bf1f926331 100644 --- a/RTEA.vcxproj +++ b/RTEA.vcxproj @@ -161,7 +161,7 @@ /bigobj /Zm300 %(AdditionalOptions) /bigobj %(AdditionalOptions) Disabled - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;DEBUG_BUILD;DEBUGMODE;TARGET_MACHINE_X86;%(PreprocessorDefinitions) false EnableFastChecks @@ -186,7 +186,7 @@ winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic-32.lib;libminizip-32.lib;libpng16-static-32.lib;fmodL_vc.lib;allegro-debug-32.lib;loadpng-debug-32.lib;liblz4_debug_static.lib;luajit-debug-32.lib;luabind-debug-32.lib;raknet-debug-32.lib;sdl2main-debug-32.lib;sdl2-debug-32.lib;opengl32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win\x86;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win\x86;%(AdditionalLibraryDirectories) DebugFull $(OutDir)$(TargetName).pdb Windows @@ -214,7 +214,7 @@ /bigobj /Zm300 %(AdditionalOptions) /bigobj %(AdditionalOptions) Disabled - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;DEBUG_BUILD;DEBUGMODE;%(PreprocessorDefinitions) false EnableFastChecks @@ -238,9 +238,9 @@ false - winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;allegro-debug.lib;loadpng-debug.lib;liblz4_debug_static.lib;luajit-debug.lib;luabind-debug.lib;raknet-debug.lib;SDL3_image-static-debug.lib;SDL3-static-debug.lib;opengl32.lib;dbghelp.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;fmem.lib;allegro-debug.lib;loadpng-debug.lib;liblz4_debug_static.lib;luajit-debug.lib;luabind-debug.lib;raknet-debug.lib;SDL3_image-static-debug.lib;SDL3-static-debug.lib;opengl32.lib;dbghelp.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win;%(AdditionalLibraryDirectories) DebugFull $(OutDir)$(TargetName).pdb Windows @@ -267,7 +267,7 @@ /bigobj /Zm300 %(AdditionalOptions) /bigobj %(AdditionalOptions) Disabled - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;MIN_DEBUG_BUILD;DEBUGMODE;TARGET_MACHINE_X86;%(PreprocessorDefinitions) false EnableFastChecks @@ -292,7 +292,7 @@ winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic-32.lib;libminizip-32.lib;libpng16-static-32.lib;fmodL_vc.lib;allegro-debug-32.lib;loadpng-debug-32.lib;liblz4_debug_static.lib;luajit-debug-release-32.lib;luabind-debug-32.lib;raknet-debug-32.lib;opengl32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win\x86;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win\x86;%(AdditionalLibraryDirectories) DebugFull $(OutDir)$(TargetName).pdb Windows @@ -320,7 +320,7 @@ /bigobj /Zm300 %(AdditionalOptions) /bigobj %(AdditionalOptions) Disabled - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;MIN_DEBUG_BUILD;DEBUGMODE;%(PreprocessorDefinitions) false EnableFastChecks @@ -344,9 +344,9 @@ false - winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;allegro-debug.lib;loadpng-debug.lib;liblz4_debug_static.lib;luajit-debug-release.lib;luabind-debug.lib;raknet-debug.lib;SDL3-static-debug.lib;SDL3_image-static-debug.lib;opengl32.lib;dbghelp.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;fmem.lib;allegro-debug.lib;loadpng-debug.lib;liblz4_debug_static.lib;luajit-debug-release.lib;luabind-debug.lib;raknet-debug.lib;SDL3-static-debug.lib;SDL3_image-static-debug.lib;opengl32.lib;dbghelp.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win;%(AdditionalLibraryDirectories) DebugFull $(OutDir)$(TargetName).pdb Windows @@ -376,7 +376,7 @@ true Speed false - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;DEBUG_RELEASE_BUILD;NDEBUG;TRACY_ENABLE;TRACY_ON_DEMAND;TARGET_MACHINE_X86;%(PreprocessorDefinitions) Sync MultiThreadedDLL @@ -404,7 +404,7 @@ winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic-32.lib;libminizip-32.lib;libpng16-static-32.lib;fmodL_vc.lib;allegro-debug-release-32.lib;loadpng-debug-release-32.lib;liblz4_release_static.lib;luajit-debug-release-32.lib;luabind-debug-release-32.lib;raknet-release-32.lib;opengl32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win\x86;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win\x86;%(AdditionalLibraryDirectories) true Windows true @@ -438,7 +438,7 @@ true Speed false - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;TRACY_ENABLE;TRACY_ON_DEMAND;PROFILING_BUILD;NDEBUG;TARGET_MACHINE_X86;%(PreprocessorDefinitions) Sync MultiThreadedDLL @@ -466,7 +466,7 @@ winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic-32.lib;libminizip-32.lib;libpng16-static-32.lib;fmodL_vc.lib;allegro-debug-release-32.lib;loadpng-debug-release-32.lib;liblz4_release_static.lib;luajit-debug-release-32.lib;luabind-debug-release-32.lib;raknet-release-32.lib;opengl32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win\x86;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win\x86;%(AdditionalLibraryDirectories) true Windows true @@ -500,7 +500,7 @@ true Speed false - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;DEBUG_RELEASE_BUILD;NDEBUG;TRACY_ENABLE;TRACY_ON_DEMAND;%(PreprocessorDefinitions) Sync MultiThreadedDLL @@ -527,9 +527,9 @@ #undef GetClassName - winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;allegro-debug-release.lib;loadpng-debug-release.lib;liblz4_release_static.lib;luajit-debug-release.lib;luabind-debug-release.lib;raknet-release.lib;opengl32.lib;SDL3-static.lib;SDL3_image-static.lib;dbghelp.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;fmem.lib;allegro-debug-release.lib;loadpng-debug-release.lib;liblz4_release_static.lib;luajit-debug-release.lib;luabind-debug-release.lib;raknet-release.lib;opengl32.lib;SDL3-static.lib;SDL3_image-static.lib;dbghelp.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win;%(AdditionalLibraryDirectories) true Windows true @@ -562,7 +562,7 @@ true Speed true - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;TRACY_ENABLE;TRACY_ON_DEMAND;PROFILING_BUILD;NDEBUG;%(PreprocessorDefinitions) Sync MultiThreadedDLL @@ -589,9 +589,9 @@ #undef GetClassName - winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;allegro-debug-release.lib;loadpng-debug-release.lib;liblz4_release_static.lib;luajit-debug-release.lib;luabind-debug-release.lib;raknet-release.lib;opengl32.lib;SDL3-static.lib;SDL3_image-static.lib;dbghelp.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;fmem.lib;allegro-debug-release.lib;loadpng-debug-release.lib;liblz4_release_static.lib;luajit-debug-release.lib;luabind-debug-release.lib;raknet-release.lib;opengl32.lib;SDL3-static.lib;SDL3_image-static.lib;dbghelp.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win;%(AdditionalLibraryDirectories) true Windows true @@ -624,7 +624,7 @@ true Speed true - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;RELEASE_BUILD;NDEBUG;TRACY_ENABLE;TRACY_ON_DEMAND;TRACY_ONLY_LOCALHOST;TRACY_NO_BROADCAST;TARGET_MACHINE_X86;%(PreprocessorDefinitions) Sync MultiThreadedDLL @@ -650,7 +650,7 @@ winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic-32.lib;libminizip-32.lib;libpng16-static-32.lib;fmodL_vc.lib;allegro-release-32.lib;loadpng-release-32.lib;liblz4_release_static.lib;luajit-release-32.lib;luabind-release-32.lib;raknet-release-32.lib;opengl32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win\x86;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win\x86;%(AdditionalLibraryDirectories) DebugFull Windows true @@ -684,7 +684,7 @@ true Speed true - $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public + $(ProjectDir);$(ProjectDir)\Source;$(ProjectDir)Source\System;$(ProjectDir)Source\Lua;$(ProjectDir)Source\Entities;$(ProjectDir)Source\Activities;$(ProjectDir)Source\Managers;$(ProjectDir)Source\Menus;$(ProjectDir)Source\GUI;$(ProjectDir)Source\GUI\Wrappers;$(ProjectDir)Source\GUI\imgui;$(ProjectDir)Source\GUI\imgui\backends;$(ProjectDir)Source\Renderer;$(ProjectDir)external\include\boost_1_75;$(ProjectDir)external\include\glm-0.9.9.8;$(ProjectDir)external\include\glad-2.0.0-beta;$(ProjectDir)external\include\LZ4-1.9.3\LZ4;$(ProjectDir)external\include\fmod-2.2.13;$(ProjectDir)external\include\thread-pool-3.5.0\include;$(ProjectDir)external\include\hopscotch-map-2.3.1\include;$(ProjectDir)external\sources\libpng-1.6.40\include;$(ProjectDir)external\sources\zlib-ng-2.1.3\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\include;$(ProjectDir)external\sources\allegro 4.4.3.1-custom\addons\loadpng;$(ProjectDir)external\sources\SDL3-3.2.10\include;$(ProjectDir)external\sources\SDL3_image-3.2.4\include;$(ProjectDir)external\sources\LuaJIT-2.1\src;$(ProjectDir)external\sources\luabind-0.7.1\;$(ProjectDir)external\sources\luabind-0.7.1\luabind;$(ProjectDir)external\sources\RakNet\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\include;$(ProjectDir)external\sources\minizip-ng-4.0.0\src;$(ProjectDir)external\sources\tracy\public;$(ProjectDir)external\sources\fmem\include _ITERATOR_DEBUG_LEVEL=0;ZLIB_COMPAT;_WINDOWS;WIN32;RELEASE_BUILD;NDEBUG;TRACY_ENABLE;TRACY_ON_DEMAND;TRACY_ONLY_LOCALHOST;TRACY_NO_BROADCAST;%(PreprocessorDefinitions) Sync MultiThreadedDLL @@ -709,9 +709,9 @@ #undef GetClassName - winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;allegro-release.lib;loadpng-release.lib;liblz4_release_static.lib;luajit-release.lib;luabind-release.lib;raknet-release.lib;opengl32.lib;SDL3-static.lib;SDL3_image-static.lib;dbghelp.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;dinput8.lib;ddraw.lib;dxguid.lib;dsound.lib;imm32.lib;setupapi.lib;version.lib;zlibstatic.lib;libminizip.lib;libpng16-static.lib;fmod_vc.lib;fmem.lib;allegro-release.lib;loadpng-release.lib;liblz4_release_static.lib;luajit-release.lib;luabind-release.lib;raknet-release.lib;opengl32.lib;SDL3-static.lib;SDL3_image-static.lib;dbghelp.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) - external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\lib\win;%(AdditionalLibraryDirectories) + external\sources\zlib-ng-2.1.3\_Bin;external\sources\libpng-1.6.40\_Bin;external\sources\allegro 4.4.3.1-custom\_Bin;external\sources\SDL3-3.2.10\_Bin;external\sources\SDL3_image-3.2.4\_Bin;external\sources\luabind-0.7.1\_Bin;external\sources\LuaJIT-2.1\_Bin;external\sources\RakNet\_Bin;external\sources\minizip-ng-4.0.0\_Bin;external\sources\fmem\_Bin\Release;external\lib\win;%(AdditionalLibraryDirectories) DebugFull Windows true @@ -1483,4 +1483,4 @@ - + \ No newline at end of file diff --git a/RTEA.vcxproj.filters b/RTEA.vcxproj.filters index 14673a2d77..819004bd7d 100644 --- a/RTEA.vcxproj.filters +++ b/RTEA.vcxproj.filters @@ -484,9 +484,6 @@ Entities - - System - System @@ -657,6 +654,12 @@ Renderer\raylib + + + + + + diff --git a/Source/Managers/ActivityMan.cpp b/Source/Managers/ActivityMan.cpp index 6512c2810d..b407db1008 100644 --- a/Source/Managers/ActivityMan.cpp +++ b/Source/Managers/ActivityMan.cpp @@ -28,6 +28,8 @@ #include "zip.h" #include "unzip.h" +#include "fmem.h" + using namespace RTE; ActivityMan::ActivityMan() { @@ -109,13 +111,6 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { modifiableScene->GetTerrain()->SetPresetName(fileName); modifiableScene->GetTerrain()->MigrateToModule(g_PresetMan.GetModuleID(c_UserScriptedSavesModuleName)); - // Create zip sav file - zipFile zippedSaveFile = zipOpen((g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + fileName + ".ccsave").c_str(), APPEND_STATUS_CREATE); - if (!zippedSaveFile) { - g_ConsoleMan.PrintString("ERROR: Couldn't create zip save file!"); - return false; - } - std::unique_ptr iniStream = std::make_unique(); // Block the main thread for a bit to let the Writer access the relevant data. @@ -137,9 +132,11 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { writer->NewPropertyWithValue("Scene", modifiableScene.get()); // Get BITMAPS so save into our zip - std::vector sceneLayerInfos = scene->GetCopiedSceneLayerBitmaps(); + // I tired std::moving this into the function directly but threadpool really doesn't like that + std::vector* sceneLayerInfos = new std::vector(); + *sceneLayerInfos = std::move(scene->GetCopiedSceneLayerBitmaps()); - auto saveWriterData = [&](Writer* writerToSave, std::vector&& sceneLayerInfos) { + auto saveWriterData = [fileName, sceneLayerInfos](Writer* writerToSave) { std::stringstream* stream = static_cast(writerToSave->GetStream()); stream->flush(); @@ -148,6 +145,13 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { zip_fileinfo zfi = {0}; + // Create zip sav file + zipFile zippedSaveFile = zipOpen((g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/" + fileName + ".ccsave").c_str(), APPEND_STATUS_CREATE); + if (!zippedSaveFile) { + g_ConsoleMan.PrintString("ERROR: Couldn't create zip save file!"); + return; + } + const int defaultCompression = 6; zipOpenNewFileInZip(zippedSaveFile, (fileName + ".ini").c_str(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, defaultCompression); zipWriteInFileInZip(zippedSaveFile, streamAsString.data(), streamAsString.size()); @@ -156,22 +160,38 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) { PALETTE palette; get_palette(palette); - for (const SceneLayerInfo& layerInfo : sceneLayerInfos) + for (const SceneLayerInfo& layerInfo : *sceneLayerInfos) { - // Allego lacks the fucking ability to save/load png from a byte stream - // AAAAAAAAAAAAAAAAAAAAAAAAAAA - //zipOpenNewFileInZip(zippedSaveFile, (fileName + " " + layerInfo.name + ".png").c_str(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, defaultCompression); - //zipWriteInFileInZip(zippedSaveFile, streamAsString.data(), streamAsString.size()); - //zipCloseFileInZip(zippedSaveFile); + // A bit of a finicky workaround, but to save a png to memory we create a memory stream and send that into allegro to save into + fmem memStructure; + fmem_init(&memStructure); + + // Save the png to our memory stream + FILE* stream = fmem_open(&memStructure, "w"); + save_stream_png(stream, layerInfo.bitmap.get(), palette); + fflush(stream); + + // Actually get the memory + void* buffer; + size_t size; + fmem_mem(&memStructure, &buffer, &size); + + zipOpenNewFileInZip(zippedSaveFile, (fileName + " " + layerInfo.name + ".png").c_str(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, defaultCompression); + zipWriteInFileInZip(zippedSaveFile, static_cast(buffer), size); + zipCloseFileInZip(zippedSaveFile); + + fclose(stream); + fmem_term(&memStructure); } zipClose(zippedSaveFile, fileName.c_str()); delete writerToSave; + delete sceneLayerInfos; }; // For some reason I can't std::move a unique ptr in, so just releasing and deleting manually... - m_SaveGameTask.push_back(g_ThreadMan.GetBackgroundThreadPool().submit(saveWriterData, writer.release(), std::move(sceneLayerInfos))); + m_SaveGameTask.push_back(g_ThreadMan.GetBackgroundThreadPool().submit(saveWriterData, writer.release())); // We didn't transfer ownership, so we must be very careful that sceneAltered's deletion doesn't touch the stuff we got from MovableMan. modifiableScene->ClearPlacedObjectSet(Scene::PlacedObjectSets::PLACEONLOAD, false); diff --git a/external/sources/allegro 4.4.3.1-custom/_Bin/allegro-debug-32.lib b/external/sources/allegro 4.4.3.1-custom/_Bin/allegro-debug-32.lib index 06bed6bcb3200cab8b269798dbceb0073d429f5e..572d93611c75ee134d29ad7246ba4ddae6c58a3a 100644 GIT binary patch delta 1417753 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%0DA27$H2*sf3 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

c5 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%0Apt*@qud_TdC&AAU{QhX*P9 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 a&1iy%Kf|;n&&MNH@Xeh6d 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 z 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 delta 1150894 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�WB)m#c|H*e;{EFBNx5aaR@hlj43=Tsf@$2zlit z66PBO$ILe{goSv2#tr(gUa-=lVzlHDsW4 z&J_y8c00}mtj)>SB!c!A5ME{cZITE(8i49>u0$e@Tm{0rig?#0(nvXcNbu%?D=!hM zAE*N7s!9Z{N}vL~3y9bNkRh0h0K&r#u7^bUNC4qU12;h;Y)k^e69jIhM1M->!JIRc zVo1?UDLfR%ykd-ZDxW@qRU&4%y!x&twB*NRzr=wg-)AbjE;9Az)HVa}|FzD|i_CZF z2L6-LD^45_&oV|&7#TzIZPYs32sU5tK%-_Gy5D@pk8vmY6{g<2-@#o9pxLtxAJgY_ z*lc4Ool(B7h2AgnDM!oCG3rBmB5RHjNc$g5_n_I&d6lEq46_&gczb#vT|dWYWlEhl z*NE&=5#sRlI)HeW-BVI=`dvC6@^LWSn{$nz3L`{H9yYc={dBHT9cO*qpKDa9 zblH~)LdDEv2_Mu=H(GZd7IWXHlja%d+neiJioc#`95MU0kTsScEo&=`1(k==yJyy<1*xV2@TqRQ-feK_uTmJ{{cJ@`O{H1l>OhWfc3(U{%Zl`K{qfDt9N0 zxl6{lTq>k3<{J}e%EwPur1w8Bj|ujiNH3bT(Zi+q?tJ5%xnKqQEbN)Lkhc}uqtGWn zu?Y1cr-9f@WFZ%TSYvD<-vP0X3T(@wE=4>~Aoj6_?go%Z5eg(yL;|sniVx`xghnR{ zVg>?=#G`;j;)x;{p;J}p0y=rI@g!yw&M!9JFVN6|VK0}3_$$QvL4r$FsIfv>3Uya# zutLu(G)P6uOc^RTWACVhx&w zq$~6UP#}9%EuKY>ci(mmP8lEWyRW&E=Ox!0t*;w5f7)nQ^FVC=%UJS!Y;*ldzh2?v z0vGDfPXBiE?ncE8wy&YP`o`Ew>1FKdX-zkmw4^yJ4R&_t%c&Vv=%$s%S?4e2TnnJB ztX2Gq-(F?-n9KcCVcc3SnGW9TU6cOwy797KVr(oru@6=yW0R7S<9uk=-db2K8)*(I zKD@@rae8$=?A*_qd>yjlTsmYe)**Mc#&$&9Gxtt(+giB}X?dESU5mxW zv<5W5k0 z;)|sMH@}EE`xiyvXTYl_i@>GHA z;j4b37WP4HIE$9Ng9iXrOg`i|!rXK=wS8lWuuPo|du+4mkS_wNxBxDiO=rAd4z~2g zOY=HtwjjL^$)*sKC!D`ObJ~n%WSc6{r#Bit4=+eRI8HSsv$KcUGn6*V>>O=g;ZJXF zG}wWB<07j0m^RXVnVk)+7H!{V1X-R#Zu>Mkag&i}zJ=oiH(~80CxBMnY|N^-7-i{$ z)PzxeW))X+*Gw%I4#<4_#uOjw&QEwjsCFR$YGMRcNp`{ zO!D1bNYg8T&e(7G(#+jPLl<5`v4DMTa4l#0^~uT}bn|W_fknTv8_|0Q(4Y_S5n4=# ze_&*>672iHSk8h6?m=*$0J?CGF&M$ty+#9?KfoQU`FoA_EP~r%ta!FW{h-z3AI4{8 z)<5kv8X-mchlarfW`Ah3W$`B;z~Wkk53mT;H-LWqq0xoK`N(L^;>`F+WSX!KnfBwE z2GG&_jE?dWLiQT|^!t5S!|#=2^`Kt+jTTH~_I^a~&$E@%{HghX5x_(S9l!#x$Tr$M zAb@T;V9zm~&R^x_ZaF7zZLfpIJ1kznA;=miv(7$*d$^cRIb_7pPun_q&;y5zG!_dN z9c>;YFSO-h`-KKtS_x?z4~vU+JA!zF18B+-d#=qb2YHm@ua6ipW}E_fu0}O9x4k-A z1a9Ce0qJHEHtcEqF6&~ zOt1aba5mrbq`4RB$Ki>I4R_DQf?B3uW}QE{iiXv*2vb|Jesr3eQy#t8dR%6h_()&^E5MBC9t<&HtXo zw+~_2o*8Fv;>i}@%pZ8tlFj}RrWAS|Qp_8?_@W!E?`OKf`=+nqmaF8T5`xE6k54eo%SSc7Zq&(AN|;V;fDaKoW`Y-tYa2}NuL za1~S=*eaZXg|qJIa0o#@dUj6a4wA|b-;bqS8K1GXOaW2D*HJ@%io8wW&bhGJD;=gSG)q(5f`Jm$Dlx!+o(voB4Jqch%hY4PBx`u}1TZXn(17LZDd zYPeLOC1;|_uqglEx3XsBKifUJ=ih0j>}zZl9(^=_b1kiJ8F3SW2RzFDqm{C^VrrH9 zjZ}YPk*tR>JHOLv+e+6CPH->C!%XQZG}43c=Sr%gQ_m0mZ zY?BDPje%a`8JjRk!4M3;17UE7!gxrekw74fjldZaX{0*P9L_~cq>)shS)8jY5St!m z>c$wHA#n?duwferqd9P$BogaT(>d1{97EWz4fHbSMoEPI+CXePiT&CVVZSyI2A$v* zNQ4^;RDcm8$hCkB!9Ea146Ht29!MhWyQ^R~Yn3e#7StrdthM4w6z5z~*q*{_g+y4b zkO=#_6?ctIPY)9rd)SRUl}Ln@D~Yfn9^*O6i{&j5jKBiT<6NjjSY!j5%eh#Iuy6=8 zoO2^3LZN|1fP*oB0IbC@2tzg!p%xT3S#ivU`bfk>3U7gsLL#wd!$M&KN^vdFTO7qx zyebjQg-8UW0YLLHRe~g6FbD*z0YI;D?mLNKH2~;U&iy12o^;GcGtqcaCBl*_5E~Q2 zV5&qgm@1J*GO#McLa{a_5$5>)kpJ^M$rX?c!TOj)u$zcUQ^Bz{8-}ol9___wo?^5_ zSm=`owzw4cwc>18SixKpip7>)7_xHv)C^&H?1Unj?PyLCTV=bcmw^%?iA1o)6==8M z;w}-MWuPTo^je9q=m@lebNeKMRT`j`oco3&Tt6OtET6C9q_aeLxPVr3E>t2cX92y= zxeST0K^ABY=ekLR8v*nN=SE9}4YELQa&EpvcxHmo{{4lMTR<`d8{|N2LeNT%Nd$Z2 zKyPvGrbIA$pxbSgdr5@VYoNDzycmfz(hR7GbFC!8zE`02oJ;Q~;h$REaa`M>%eF)Z z(u77Waa!1rP@Se0b*@{y-P?9Qn0|X9vLY>*65>LO#>7>mXX9-3*qGh4w`w%jV2mu7z^!Umo| z*yxl}VFT#6V4z61&tUt8gaml!l=4X&;dOh1t^IuSP>9%OoKpK;7d(j+VrPmME ziJ)iFZF5uwWYZ;k19`e%Gi<9B-IIR&p`e#kLRRhSm&&RSz4nm^TUHri`3*{i^`oBq zMOaQl+i@mNpWlF;hf6-Try%G%pS^{*i*DHGNMGzFM;V<$=dO zFs0q7zlipIW6{yqawZ>LJ*oRTuZ}xc`^HVF7PocvicJT*ezkY@%34j^f3mxm-CurX zoY&N=qu1Kw-~D7@yKkora)Yk+Pi7CP6?bCf^mb2Q zICI~h_8V+Zu=CxOOV+*k#ZJ5b%39|`XM^?)o$y*ym(UNt{dLiky%TL2;Wm2#5-!x) z_RW=rPIiB&<+&So#x$L2PhV1FLDt(Fea8rY{dHbHwcPdUraRq!47^zPmFqVL&Aat# zy;(2+W!MWxm#=Da{qn7SZ(?$y&BB%Mk#WzYu4?er?XU%V8}97m=H7O5QQW4=RoBq> zn%Sy4tWvnJ)q&TjshhhYcaPFnn%knB!?XH!rOWi>wBlcy+u}6$@puK<@5GrIdU0w; zTZ+kjf?!B>mp(P=m`=9Lf<(4^RII>>y_`vj$%*a{GfDAr_^$I+IvEFhuF$SM%p_KQ zm`RRLigIL9QWO)Nm)|q2X>r}owm_%2rVTP0Hg8zJepZ9V84c3o)8je}?VUF?J#J7= zPI_FsoMCxAd#A^>?L8z19CqaA_s*c17Xm+_Pj|IN$2D!3(Yke`Rt=grYVms$=iU!( z$bY!X{B1RY(P+q!%pEgBpXrZs6P zHj4$drta=hVzb!J4%jS)^8$C^1oyfJ;yuctyVSKKCiQz$t(z^>yl)Vn!>3u@Y|iG+ zgT;h>G6dHdBDRk;0&Y4)Y#(a}j?LEZN2N{u1GDw-J!A8Ic(xwf$CBx_R%y=ocnIY) z=6Z#)O&jc=u%-1eZS!S-clkni88sY#(X>sr?MY1MFX|AVNY7^50&#M@M_SDQQz7l1 zgVQ9dcDIF@hSQ@zwDI?1pG%gIJsRxSHIxqTZVN5Xc=kvwz+Y>+s=F<_lt53qqo>V_ zcI#ntgXnYnLn@ixq`3p!z3H4DwkVvwZtCss@5QiUj z)xB&>Airhj_>Od8Z(DWKEA*q@whYq->ek2BtsoB_PPQ9~4J_Et?%yuF%74kh?}K3) z``P37k^UFa=VkJyqj>CR_eY^fc0b?UM1F)fh6bK)V70LFV#fYVh?l8EKaPc(ojY^~7)`9s%Siv`BQ`Eb!eD_jD10&Jcat_7F~;xJr{5esj_#Xz$#1%<@mvT!im$#93lodS0} z_3UelG_MY&wfo{@*@$)@YV)CE`r5Xm+LsNsg_zdTZvEJMq|x#HYaSj5za z*<5Kx|I*Pz2H5z@N&NvL`sM+M{)C8L;~Wfa>>XqqMlTPwMbntMO>oMpmf-dvv$F$j zd@0F1NTeP<2&tRMVvV%9(OrXVXJmBOwYdK{5SJD#^}+=4V7%cd<~M_p(kqm9$wA6$ z^rakIGrwkNPKV~_@tgaExR+!cRu%$d{pSr4$t*)`ZF#aGNLGWsG{n}*{JbYUcTD%E z*APdnq&Vl=qUiR$DL5_gGK_s7oBFx7P`d6|h`Z@|nwx8@>&?!nVhd}jo;74nQ|Yc; zo4+ZO9&L+Fe^+vCKG^X7)q<>A-|^d$voRou|jn!&(@w*a~x_27m6FQ^P%bq zqS^VP$bIrf9i7Za%;xHbY$&sO?1}t_+wweDhcacv2Mv)YHPuV@rg!sg?xq$((Ba{> zcOK-6X8`*~WWFDc5c$>~iF{kCd-K_Y=y(PK>5NgLnDa*2x|#Cmtx>j)zqLvvPE!5{ zJF4UYP)Ms9DRjte7w5;TK#x~}{#~m;>k4fV=9O|4NdKLyK>v@uH-V3$X!relhGY_E zl8_A&LYC>CED*NIp0I{Zb_mE0vLm~MMMMN92r4^aEl|LKh=?EofgrMrg2<~uK~YhI zD=0`%L{yajuj;9(Btu@0@BN>1@44r`^C3Ln`PJGr-PPUI)%6s}r$FKIDbNaf3KULH zfmX<;KsDu4AXe@v&;kd#ihn-^!d2%0anZ>qMD}C3lLISxCF;N89jkvo1&Ww-?|o;B z4z(*$@p{Ps_V1@a{N9KidopwwE9`6gm*2(uzws#$+p*eS$?^Cb;Hc=YED; zv%)?0m*^o-R}9vcppVA29foQ7qaRN6jua=nj-fQXzAi$s2))wyg+9eg#T7X&Taini zB)mN#36A@4UCu*oS|dqt`N2H`lh{LvTR|8`M>aPlVQqX#R_#UHy75q(wnGvIdzE%d zX_4N1dMVFklq5|3mxR$*P@>4iwO=BA2_v!~KJZQbfD&m7m8>6(TN}H~XH zZJW}Lf?T|j;Ce`j{ADUUI6%X-u1bXW!z5X?g`kEa#ZpOD?P*X0p>2?ap9xf7Xgeff z-ASuEzZ^;UA-G2lJaj;DsD&Un&5&f(x`2{JHr*w`}+Ny6hG5FTfsw4;*nj?@W>c%RFkR=&XdT#~RHJt$RVW0wT)xu6uGHI#&>N+6y! z-0Vt%BUd~W!{LSE@+9Gikc9WEK$*04?{h>%tM(ZvLuB)XB)kU#N*CG;QbH>FDI-_E(x#YDUG`u=Ak&CKzxMB zM>r4WWD~xFIa#$l?6r-BHdm5WTMB9JZ>l8`wj{s^aw3d?K9|6=vXx$`PwGxnvXKmBI0&>D*U+gI{ zARld|B%8KI67~U53z6arN!SNK*+RP^32)JXnhTBZWt^~FDTwE6(^g7?e-#(d4iU#R zes)ecrX?W>sI}0zM_5j9#|3I7wC5zj9T%vj&~{70YnIq~>_QtZ39lc5a)fqHQb(LH zfH*YqcUjW^@25^aKmB;4=fbPKm|onW9(#6b8*ldOL3;}}@Uj!@1Rb(3rPooqzF@Ji z#Nb4K7IfJD6lpQ1Qv-{N4%-`>FbVLdBlZ;Y6&tH_)cz%gbNr9lTNdRUv)3@0s<2&C z;zQVs>b1#rZJkrJS25lSu z?=7^Q_@A^Rpgux7p|o=#v@24CncVpqirRqsiWD6{luJKQKfZTcv_VRo0O~IyPEi_P z6pvpp@N>CE8xL}bw2y&e1Iu3A zq6RkOG3Owm`GBb1B0z(M7NfLe&=8?zC~Xi3j{vZl`5<(?p!J~oE)j7Pkm|Y)gX#D0ZyG{y$T?r zqes1VfSe-YZV*-L2#Bi17lov1T>vGCcwd2%1^MFwgsNqLMhGndM8!1%Jt(y1Zq222 z2aXhCZ?~xNWfiHp3PCuVpjwAODI(r+5LN445LJsWyGYf#3`!O8Zi3PTMd3Pzs+9~H zCA17sARnd<)j9x23$d#Zxi%CuMrb3I#xI+wTBkrbC*kirC|$(+7(~_j7DUy$38HGD zUl!K}7BLSgQ>1WW9ur?$48La@f~k@CtfFy3>#Vd1Aj;+uMfsrdBHk($Z?_6Ppr{!1 zkcf9)#rsKxx_(#4U){n#R3=tYiXwjHMol;gG(p6B1Voiy0Lm5GQl)JIO%&P-N_!VX zE%*fp|9Jmb1=nzNNbrljERm!-h#J!XQDZg)Q7be7H4^dKf%wG)j&~4M@Nv*2p*;nn z3O)^*EVO5pwj1P{BE%vkeh#7<{s=-1k#z_z3hCJ3my!G;!J@^1sDjBLs$g>vRj?C? zUtn0YJ|KSiVbNxSsDjHu4-1X)1$(K6+dxx=xKnAzL5~RSq|&Z~sDfs6oK!&vh>jBe zK7VtOA`L_pYzd+Yb_G!d2Y|8>5B=XLU<;9CF^Foo2{cV;FMz0m2SASs?TFIe1MxGc zMf*@`cR^IaU>K@kV-OuLe9eHCB1LDAgX+}}NHh{eHJk!!B_igrw&(1tOsk4MJ!iL= zYcHHVKYT~To@n=M0>lxYO@lhEP2P6&&CA|M?rjWeV?dODZV*$1CF}$Vc|pu6(hXV%g_TU6Ocb zwx)u@d)-qN6y8OisGwv(Iaprd*=Q$3bIF`}6pgKbJ15$- ze6ow>6_M<6c}1l93MUAawJ5CITD`I`eoL+5h*Wo~;-sM@k<3f{66Na{SY8pyGDW+W zuVQw2MO3jYAzJK7PYLfdYE56|26R0dgC>$oqEy!r7JbV$py4D|GlszczS9^GB&EBk{a%z zWTx?>zkI4pkx>ODi+8-`lr*QaormIbCin1=oT-C7l$4Bdo=Vm<4<$8eu7{GI!nd}3 z@#$&nJQY{QRu3sNZI7ptanw`EI#W?ecV=AhRIi1s_a)?CKxp z&G(yiY-6{mP}cl_S2gP~9CVquT==ym8}+5#Fulftc4jA7!jW5K8=4Ok4^C{uN_cQ8 zJ9f2O$GEqV%i_N`9jI`l9>F?xGg;Z4uk3ym=LF#m-Za+XYkQ#iMS~6a+TOqn7a)ti zw!dY1lJ)w=J{OZ0iY|U*?`NvMh;ITuUSzaT1I!L(WyA#VATB?SKe%t0cG=zn$q!t% zJ1sm2^B+nt+Y|MU;ITNB^@SY=I}-LG*r~9%RK&cGBMp0ovD{Cyy!gs8n!BWd1{;3G z-oVy^i{fKiu`Df|?Y&};M>!X-*fY$-YO+Bs>({h=gZ4&wso&X;v(jmPf#y)WVfsC0 z@*G1#o?#LGTH-Cz@9pi7cJ}x7BzEvozfkt(_x2|Bd2J7)CaYnO!@danIxJ6g0`>;% zQZpYJ^bZ=sHh$+-owdGdZ}`t<5V+qnH6^g5x^euqQY^+bD)v1M7c9rS`}G_(JJ1n;nk^Wq-(_sgb%>3C=I-!Gf~*S&0d z=SNI4*dnJH#9-9(@4RffNlr6}kkbrS(KLeynr5&{PBW+_rx~m*H_hNC`jLN6GvJe3 z|2@q>wE@!AMoWm7i9KYYx+vqUbnbxgTe6%%-lI-^7G z4Z;MT{;c?mD*tR5*WZ2hN`n4pPBXZ^%F>YyelVeit2M5;O3*#mz#q*p;6yVFIN^OJ zTn&*HEXk^6D2-1Mrg%M+#-|IDHcn}|N;`w0hcHoExEJnpIpTdiT)1(Y5XtT1vtrZl&!|8XumZczo!B6NYE`I$yXdLpB#A!6mgMytRnC z4IXOKk|g2rpwjrtO*|A&)j%;K9v`US^fw=%-MgDsSd)+CJ(#5zb*!W3nf>dy*MZB_ zrbMv|b@eEAtFGRfy}zzjOi`bDdW_l84(q?onI6bj*=yswu(R*NoWRE2{2or)hzem@ zJ|v7}PdCsbi;5cP4NZ0Ud%QF*TLQv;KZfwGgD~0v`W;kDkcCxq>a(k_N2}2wWFaU} z1ii+dcj^o5*@amIUO(_!nwL!Q;bKG{&^lkQ3TwpF$Wmp8w8!rM}1uebQ| z`=t8F_t^TjtJ&G}fsrh5bB-@NlGNSGqNa2YW-rca8O#!yLJnS4yjJ-Q(VCNeX49g(FPs>sZ;S5(rR$xaU?DLvamNpa@z6~2;+2BqpZOcBF@pACEUFWVH@8O>@JVDzQBA_Eoq=0AzzHH&Y2qt@)xvF7l}@z2m00|$eJ9+s zbfU$tioivk+2A|(8J0hgvTGNfDB8+LHy`PO_awWDgjVFIUF(Wx7C9_$Au=75gHiu( z6vhkO+YR;TP6Z}*$N2dDtZ;FHVeW%TO77LEM%`bwI@NcQ{gHNj4|?HcItFWI^$<6> zKJ&nfdT4COo=n}mx(Cbqp<29ogS1LmIk8p6syU|dY<4&z6~RLjUo^*xF#^75&Ri=knq#rCg6;9~<}Nl?%IyXlE#-5? z#w++AyVL0EM~m%jf`uCnv)P}P^cn9jmh>t1r!{yw1<)EirvhjVo|e^U4W2jQ-Gf)B z6_yv}Yxb;yXR8*xJ%eZso`vWc|ITeuKrpSr^JK7CgU4(N5soe}^02X1bb`smx+TNU z#Z#eT4IT`v-hgV(!*YRGpi{s`-_{1|3P;Ocnkkwy%HA>f3e(>xbZ3& z)vp~Z_}b*h0$N5_l3uK!lyl1VKFj~Y8tLK8r1G`Jgolk)gDSf<`|szyXYkni^U8MI z%AI_;Uoo+=PgC#l3-T8}J^uRrlS!=LX4R&wWUF^2%`RS@_|J3nvxVh-8rh$(SCq+0 z7uO15gY;HS*pbd%E$qZh{V|sB>-2N|j=Q-Mbn-JD%TH^6Pq=Sd54hCdwnYq0ruy zWYsQvQ*k!!swAtp4QwLf@!LR7R_!K;rjy>5WYt1&qsQlc*fc&Zi<4DL0rANhHm!Mv z#J_nTh!@}o(EIOYLxkm)X7|n0M_5f~nZD9d3n%r#UnSIH-_4D-vg6D2XiST|vE*_h0+ar~NoKkfl=_bsMkz-VXjzypn;;YvEM z75~l-E!02Z$;+={G0H+~SWH{2FJQlv^u=E z1H=azHBU^z`M+MK7qjC(3xA+HxH_AVn6jJY`Nw&gk@@w|vm<_{ha>RWCKzKC1ut2~9Tn)P8yQbw|RfuiZ;l3|znL>Tv!2eOb0;w^qD6 zU`i9)@Ckt}A8gd6s8#;ZwKrF{eI)Xo&%YnPrRDkOP9z>3+0L3dZ{4TWTE5+4PVM#U z(*{R)tUzJf$~vvm2YW6-!S7Zh%HN~2KCAUuKfW{Z9cS0&`>*n)DBv=3wLZ=dw~0a? zb9#CUmJ|)y$q<|WDm?bM7Nah(ZfW;7QR$m4?`>ws5V7be(~p}fEM>u#tkn}Mu4Hjy ztva%x&p&eM90Dr4LK%d1If&6^Kul|9J$FE1rgbauVo)l~igad>z}W@?stK1@qT zG|Dn*32awBJbH?yBUTpB(h;LJv4UrHy4g=XByRSDi|@m9qC=Wi;Dw_SpVnS1zk{`g z?GCz_b`4Eh>vR*l@vNR+W9o!)Q-+Qcarl3W@Ygca#5OjJiSu~}kL};Xmx(w0b)Sp) zQi3n~YhHSl@3{I(O#6SuDKQ=+H>~*ZxXd4M;OsOn*5MP~kNbq}^NHTT=I1`lHPebq znebk?N#DYkl&Srk{vP+4dFMI3fq7pTtG8LtOysY^KR9~ah=+!ZAAyw|Y(aPK-o0D4 z81!v?JA=Wc$+_cLc0zc#Wh%B3d2QdUPcpk=nf-a>6&}uVp4XoYq+~(J ztkzDwIr8bh6Qg+uov~9N%D1CrC$g!9vJ3Ub=6Rv)-Q9XSwyIFC&m)~E)HnHr82qfG zu|c`^D7Ii1ex;~zwq}>!*k@iSwd%O*Q2Kd$`B_F&(|YaJg_GF9yODV$R%hO=HzNE(WA@@ zL)j7^BizCxBDZ;a^lkoqz=At53HvVWF4&J?cf)=Q`wDC@^4J4whusUC0=o~k1#A&) zS6Iv~&wBv&0PGAFvmZ(jV?pc&g;`&GnP z)VRJM7Dnm)sy^1jk9kBo^{Sree?RymWH=P|W6^YB4*>b6tm!y=v9SeFQS3+yocedN z`w!@2#ZnW<=fnX$(R4q%et;?z{u-Lufu8$_9?Ay1ravflrq}fDytW5_<+VNP~^oZVp-I?X|WdYxL`|=pMM^GuJ z%jsv17u9~!!dnAT>mAh-z59S0vR(V4!UE9Os8&I5m3I^e*Iu^ss4kYnNcBo4@f-pqZq+JXfbUoS6bdY^}OrK-<2cn>a+~az8@7KXoMWuuJv?spt{LtVZ z?2cEDV|U~_-yJ){?qIcE*Yiy0*}B*DSo316RGn)Jx9}Lqu;g{Ud;RwjbD>4cI}f`U z_B!km*l5IA3Y!Lt_X+ZNZY!wZnTWzaWj{KbdjidpJ+vWtf}f1Qgm9Mk7Jk)JC-k8f zo&p6r-_RR*^T6jTHv7sq(CoG03;GRe_9kzl*-N_h@~u|(G3hq%7uZX%0f_qrEYJ4qf7(bnZ+SLSqG+V) zvVB&cmd*1EHBVqdFPxA6$oBGgw=}~J;>VW0t;evV(5?KZpNqe`=&4T2p6YG%RBuA# zpR(p-i{8fObDigP5P!Smu~GcCKE}cWu(8=^u#54)cs68jR7h}NY>!7X9z275xXzoA zziL!3oLxPm&osS9&3FPb|DYN3|Do5g@Tkan`#-R+6+>@~cOBn@Z3=r8wi#?K#DQ0t zykyvHSe|VwQ3E#anKW@^=DdTp8=RU!JJ_R0SRZZojy_4ar9c6<-$CPXy(c9sJ?rol z?E{JT4Fldqy&nPd?H`Z72AF~NuKpN5D1tuLYg%|1@~K^n#UA-I3}@Sm z_31pH8_oS|xbta!7Cqf~HsLHzMxJ%KCl6;hJ9YNnJO;lf>*AuiRQtHEFN^*JtM8zD zES(q@$|4T7#R~Wz_VhC+<6L_lcVJ!b7~ahCzMjeZuk82rmOSq1&srK34J%8DXlLKY znHDhu&mjhyr^h*5O^G?P*mR8ivx#?%nrzECeVphA&!bC5tWoE2pkhuOvY5?2%(eTo z8MTu9#2pp#%KU(09c_wb$@scJ=JarO@B?fY5B!h<7k!8bTX09pKFQ38 zV$r9o`+D<7j{DgwAL~tUAyoRYZiqJc1T7`6JI(&#Y~CmOajti|NS9CcPqASExIFw+ zpT+g!PxVH@2mEn?jojRQv^|zZeuf>0>j}`gtrpHYeWt&FC4_E%rq?q~XZq)Q3z%V_ z>knY@pktrw4P9wCo$z&)UgfvW_^b-1?`wFg;FBlAf0W;s$?yCvvhv~0<#%^}UFCOo ze*9Ig^6^uvimxusouCg1+Mlw3eEHoSULe1_Gc5IpA0&U<9ysyw4QegFyVLiO-`(lg z~rO@J|{Gx`d;cHz+Q@otN}}Cf9DX)`K@su&ZG2hg}Uj z50=3$fL#mA-z;1Q%hw@T5BnwT2H4+VpM~X~$O=p@El&q*gisfD6KpE%bFl4TH^UBt zeI9lW>=syD3Gg+&^Kd1gZG&A7iJLm0&-35!2pSBzJXV_O@ZP-SI_zB7H(*!5o`ii4 z_AS`$u%}@U!JdJA5T~hkV7dF(cVVZ)y50lK0lW{p4E8+iv#=k*?uGpr)*n0QMc6Rd zPhoY~&tMZ_KZk7!TLRk#_AA&?uwTPYg8c?|2JE-6PrzP=T?=~!c289d0DcEJ2jP3z zi?G*W`Fk9tuu-r#U=v_}giV6|3APdJO<4YB-p{b3U~j?BgZ%}TzmxYH>>IGZ!}6Ew z{)Bb%1Hp`NSTpR4HWxH&{Pde%ADdb;4re zO&)*!tr~29SiAt7HwG5daPn5bhQM-ng`u#QV5`G^2^#@hH5JEVB%l^-3~YT^J8Vl> z1GWdO19l*6EG&OVD2`41z$b*+H`VY8z`>zD3}^6fbc5NC_|Rn!hQPESoWj4+yVP`d z9t&i2ci*ss1np{VZ1yhF&@RTfKW4zZ^Y?0$0Nxf-QhO0{bfLQP|gEkHZ$j zz7EUFdP7t!I2b$nW_-ooBR*3d?8LX|_ych<`K`W#dT$*|aoU1%aa%r}ow=-cMlT+5 zMQ^CPJGC8VO6>mrqx?RJjX|^^w>&{Mz$&)wiXLNH#ZDs>=C><)B+f8J;osrZ%1_HH zuc8LQxPHE>PvWPZ%xeh1X@Ac(=pk_0bWPvD^|?Rj3xfH@79TZnZvhsev|mTd4o-^q zvSJ>t=7$K?aUQ#_;{ayG2($1sNPqjfo*cIdQKG7#OMs1r{RY+!djnR7<;fka+rroo z_SwWRU#q(u)MrYm-XI{fY{+Khh$-yY4cul2=2s2C(u=mLtn`MSz%0;seeE|eJb>++ zaRb{?1J_wMXmqE?kNN_>eI-9a_d@cY5D)Rk{iG*zz2GO@Y`wzX{Yf9o~j*tmAEc z7?%rgll&Ez@3Q81^y;kkuXF=E?^g=LEph|1h6`}1clwR=yx*XkFnRkoeGJbl@^{j6 zeur+xmi|r+c2Vkqcc51ZXW4i3`>kTMX2|&5aU)o_pY)nM{r)?YKET<-V9jph%FZG( z31!Yd^zQyS=z{R8i?=T|)}pU}7_&!Xp~#p2zdOM)Hr9Bz|NqoJGe&+3QY`B-vz{Wi#)5pYG{E2a&yd0{n-RhW*o+pYG`7KJ z)IzKSh-E$#hUf4;;oKgG@QGm6{ESA?V{5vrIvxcMnlxhAgh|6kOv0p0{~#91Ue2j+ z6s`6}UXgQE%t5)M9-TaB>V(n5MqnPHZx9ALNBS8}XaYz0W4g%Bz6U>O&B@`L`E?(fiqp)Ors!;xp5LP2@hu}E!6Y5P zU9CzE-^N&l!?(*;nv#=jBY$vnr31J>Y&5rQjvvh}yW>Z5%clC%+_IZ+1c$k3rY5Z0 z8qDljEFHj=1h7(TR)VP-bG{g#K;GY4;m7QgP?{R{Td0^C_5fyGHO5c%NpFoVn74*;0`BY$XP${x)TKd9npd{1Ce160#hfendc$+H zZk_c6Qykj}W8Q>^wS0EjO*|0dwkN zg|9m}L&N9&9`*Y7pX|{Ft-9XtP6J%#vld;DTVgcLF?$}1;Pk(=@WFx|l|G8t>$!$Qf9_pi}>g8CZOl!sQJJr=bsa}nQo{2crrxXG_%OSYxPSfn4$58EWY4{M{U3K2hq-kl zShH9BvsiJO5q56{yUn~G&f52d2i+TV>@Dy9tni3;+toY#;n%T~cUIXi`hSHBxJnjP z{jZ!w$Nu^?96$DKe539xZ(_^7_qH{k^`%dq@bE*!8~i`w|Lr+ed?suYpBLvxp5X3w ziSD@igVRYa z%kLD{k>#W}Ys2!cruneNiy}I*($%3>R&}$NzpDwZM@!J}cfub{%Hl+mvN&0_y}0xy z4Fg4jtb7PaY8d>H8oxxRco>(F+E!eCcNRI{l!UcGCBaD=Urv+7!B++7+q-6$BD7F)HzesUG61G#4Rhvp~RmhSv6A)N@CNzC0Vr~rBzp2J*7F7b`#WB6qg-F zgw2#>6-!w1P`-pE%I9d+&LOy;NMc8iz{#e?O0sGrl{Qvsc}knBv|>vKm$ZJfe}PDEmRVAGo|Gz?F8rnN`a?EoRC5iR=rf(aHW-i z2IBbRy&h(*3=~P6lCV0d(pD(#S*1OvH0}(J(jHV=I4n>Ird`oD8Zyr9IZ zN-I^`a62tI=%W=%f_G3!aQUcHs7(uz1hSqJ)TXtRWYzjBZIIHYC~cb3?tn%iXP1xGt~MdgfRZq+T4|et%oE$6Y3)o7gcUidrN60O8ZJ_wHwmvj_{l-37&H$Vc}k-T~^v1rSUoN z4~aH!4VpmxU%o)z3M(a9#jN@XqGi02XzfQVJ}n6sWJ((h$`viMU6M^Jl!TMD((WkD znM`X$;y_JC`4dGUc~Z1$rzF|5GfLx&fKC#HbdrQ0OcFX&rR`JN8Ko5~Ei{$ZgY?k` zOS0kkmk1xPO8i7=rAoW0H2zlc6j7PElCZN%LM}@CL}{f;yQ#Fybg>?!3w!HaLNxz8 zNqD_LX}6W;mqANNVu5r?R;>qUs;EqXB%8Kbl2!XyX`dnjPxfu@U13U5M$-BuFZ@gf?}1(+!t|t`)Q%p*C%h zB&#+Cv_WVONwR8>g4PLbrX&;xdPZoABw4kGnxPEd|Jk%j96>nSNwR5=D{YC=mMd+8 z(l#mWWu@&_+F_*~SK2#Dd#@SFr-DCI;uk974@&z{X@4rs+}xcuK$1-hR$8>u45c+t zTC!VnY0Z?_(k*ITl-6Bo1C=&ZX%8uFlG0`=?Qx|oQQC5)Z4l(*&A&;BFH5p%yOnlW zX~&iJj?&&!+GV9(Rhn-bw+H4Pl5AQZNjPt`(Qy4M_JDOt+^QmOSK5B19aLJe(#|O@ z_dfEGYr=3X4um5a>*!#mI*|b_pOHf)}rDZ6svC`Tqt)tRERHBStZ#i1D57ZmqT?TquHY(n69olHeU) z5>D1iyREdy`+2TB#VAR*d6R^@ZMO!W>Pox=@)Jqg^&rAUrX+Y?QQCf`T?F}y6t#O2 z!N0FlD47bk2#nnoc1AqphnB0_1WK*1tOXm28$7A^^6Wl9^Tw0uyA zNO4h;O)HUvp_e`siXn+UD4$0}HV{Kal0A}e?Jf!LOZKHuo8~JC18AV?BE>XGxUZFj z23FbyrQHG55GmUAbFW|I>Ld}vB1&ANw1c29k;Jz@kxlcHgxfQvO;Oq!P`F6(l_cEY zO2T0`z`fp3HW7CxG#pq{Bq@}HTWd+U5P!hE-jJ6h92=krkz$G@92=6*YbfoE(n>+| zM2aQ@iE!Y9TwJtjYn8ZO64pyn+76|?sfrE)~TPq1}q?Fdf zrNlhY0*xOqXC%Qhk0hKKhG1eOLUCr0gmFF47NI3dvTA9d%|gqPWYt=Oo)cO-N$3MW zyfpNG`4TZ!Ig~qF=2aLh31gL#tlCkfolsifFnak8_rH>G|0@ZvE)SrfVtSiKSNWu^TsGiU+OR{QrLG^`JWegFXYk(REO_ziN5Y$j;S(2<;H;_|kJtbMS zv7jV=4Pw(K5TX^H040lv^CiKv5hz7y&q=as`$4HfJ1EJjodu-{?Yt!HGoW;#-IQe2 zs*fe795$^csBs2@|5IMbXU-8xu|=aM7=iwnrupa$qZ{62dV7YktIEPQw^_HXmg+?- zXBvSfEam+CEF+bDJj=+2WAa)qV+(u2W(#E{-{5uM3oc{4NoV)v89MuUmQjs8l4tm{ zIeA7h)Hi2B#XJ7~?3@epxPHkq{=_RAw`OAsRx-262u;zXABJF1-O^3o1#sr%f6jw)L}vMjTNk9b&MB#VZJEggZZL_ zTl3u|^f-iirYFi3yq$t1Rv(?(af8@TR%ON?O13$62g!4h5XV$n{!|;H&Yt2 zj73I$He!*nlnp+D&G=;z3TeyY7aN8tmbG7O#41lwK5Xt{BPp=4tmCVTL=N@X8;gy4 zs!R*>T2ekzzKqmnNrgyrD@0o9&Z$DABCn;G02mZT&79-hrY*oyH_r6lI=%LldX_M(~mbC^MY-6+58foT-r?3`< z7A#M-#_;tERTb;z#RfecQG+r#ZxI;;Fk}!@6uZ`_Vm9To+Rqq|)C>{(54NP8*WI&? zZ;Bmq&Z)asYOzE$SJ>FM_{Bid#+xT&Y&JM*S2C|)_x>}0r?=*h5 zV7_RbeMVy!KRH^&RTmLInp@&8R9pa%b`sx-&V%guvuK~-e}3MwXjkw*h294711D57 zncP|pMG+u=p<&VLC`v8*a-Z>lw~Ke$7HvIleTZHF@spTE<9>9A-d3UK6Q_*M;ZQV3b zKB^F^&0%d08xO>h9D{Zzxi*OE*Z@RZkj_pXHqwG9>QWVzG5;gRvFcQp_i)2Vu|EPu zvoDVr39?a)5O?PMX*?Cv9#jRtvPJ6xY9(saA4FM2VG!l= zHi&Zhj3*)d8bsOL1W`736g9>RWz!erBdRbEL{*p$@)ep(X?zL+rCp_H8;GiUNYM#U2EWy?XzwWT zJQsy_MQNoV+NQf8s*Fz!H#GuP6;)^sl0Os3Dzw2$n+&oEZJN?%*FgDH*Ch~%o&!-` zcY>(kx0F__w9i3QnHwtfHi)X{^sTrQL>b(#MJs-*5vtV!QXz?ooFK}&If%09 zqUcc&f1ll=%>q$Xmw~DYZMD*#15wWV6deL(i0YpVNBI=|juO965if(N3crA;3U?JX z!stYxD6Tn(s?Z%2B(&a28v_a!+61NX=^a$AYd(+);XX=9d{JrpK~(T*6?zs#^$)>t z2c?Jrkyck}$x3SqqFhFULPTXA0-@3_H2-5j%4VLTr69_14TvhZO@$UJ?FfjnKB?#i zC{z@33q&nbrIx#lU`62|+5aT~DVye?`r_B=ueAA~>LSA>Aj)tXh@Tg6c2nAZ5Tz{! z(I$PV=sOT?(oLoP?owj4NOzJNAll;EAgUIh14A?h6ebFu0HVq)1ceJNUuk?cOiiI} zRoX5PRq!nkuORxr4}etMPg1mKzbVZx%3W|Yh_)#XL|ZaaX%8vQrL-rNwhTnoe-*^9 zA}rcrP%Ga5g=%jD4~a&=Fw75qbmR)IiTbr{MzN>O_de;p5}E)ca*f6yUO z#&8h+A#`>$$|vzDC9VZgh8q>V1&S0Iz5}8RKLJr%KEnt9kfABYt@(gRs|lj~bVcn! zQ9M7F)&(eQ2$D4fQR3N3drE0*L9&91_JHsYRXCyOyrOH0?t&<-znzndH-8)uIuf^3 z)I$XG(4mSRR-p?Nty7^dDSAVNexc~53N`EQDpc1|J{24bktj_CH&xVAg^p1)LxnC- z^epI0jc>?iP%BWV_Oc577?dVLuYeYaP}dK@y+Zs$QGnsDVFYNu2<5Y&_*jrddr;8? zP)m``V@i8U(F%}Vq<9uY*V!+s(ET9%5WN38ro;~veGEzziLWT)?pf%!^KsDFo<$1; z9Tf4JfnrU(f_*@53GD$;6Hur&4n*6&4Mcm{Yakclk3c%u{sg@$5?gSyLKFg`f@_2J zh|mV0R|I9M(59g7LTeA20}9o8g6u+j07R`YOlf&8;As*31c>Uo3e-X*UaulP2cpDB zL2ru`Z-6Mpc@U-eRK@!e^qPprf44J&yg`(Lzbn}snk!U`0#ZZ==%9$04f=M|)9z?(KC!pOT$=53M z2hcl0`w1kgieH9OSU^n?8mdKu>>^$Qh^k)~L{&&~Yc6e>in!G+YP%F21<{tAQQ9Yp zz5-DpKPc@-5EXJ8L_dTDBe6tbAj+=}Xg}}&EZPJh+8y+;qFEr?y2VO+M$vXfzk*N> z($=Ws&c&{%A&8300#Pe81yKcC)j|1Ga9fCUymtoCPtr@#a1doP7DP=w5kx=EV=8nm z=v`5n`5>x315t`~Ao_9kfT+^0LqMv+F%VVZq|y=~P{d{+iq}q2ZxB^$n9?RGdJIHq zpHSL-5dNXHmcrnu<8Fu75Q4UVsFC)9sJJ&kbWpyd=ra}Xnu_;}(gJWFLur#ilwY>e zIx1}lh)?xI!Rvrj$n%PJgDCWKP_c;j6^NSkCg`ltepOmPJxzO0Xdxhqr-P{Z<3Y_3 z8Y*TzQngZC5NT(~0#R{ILD(2ns}+d$g^r5)f~e6af+(9jr7cj}5)kFQ5=1$#0a4By zKs;w$|Ggm5qU{Ay)(1gU>*FBW&CaOM^Pu-d1wRJSc3uWiifbU+&8p&BlL`(8QO=1V zs&p3Dc>fowHCJLQ5Y@Uph!S@OQLXO>QG4`PG#W%%=YpsTQ$bYeJQX@0bWT)y35Y7a zj=O!QBpV@8t#^Z{hKCegQ1mT`s`ZnifCg?Y5=3e1E3KuX?kaRB=sd5COB)HK+B^dK zK-6%C(w2cf6xwQ~Z3a=LUj#KnXsEUiMAhOGR;fMS1yN-_QyQPJO4?;m5cPjQ04ed0 zAZqH{AZlv<8Ae-Q1C!$22A#8IFb zg2scM1cho-L1{vJ67-Cqg`g!ObS-G9pe>+$L3=>!M7*P*^@7fTHcUx+s!-4WcAJgLslq?GF`?FRVrt^aIiLa19W(LL_Lf$fd5LCMvWGsEF@y z7L5U^{?95p2#OJ*Zz?(uqO7m0c)zO9KR`huMU_-{e*Pe;U%oVvxN5 zeL+RsrGoc@=-7Bog}wpWBeHoLMB8~DL{<0+^tlMV4WjL|U^k}}HW0OCEf7^ECLJ9P z6_NmvYV8D3Q>TKcsT+eRn-(A{xFd)P?g1(h1@{F}!6QMGVjPItcMgbhUJjzPu60T* z08w#UKvctRAgbUl5M@{dqADB%Q8uSRmqZob1yL0~1yPDGKvV@jZ=Q1YL4S_4{Qj3e zo}hZgk_atPX$?SBtyB;t&ID1lnuDm2HXy22R}fXJ59kX~tp`9EK~RB+cM7yo&^gd1LAOAZpE=XLaX}zT8>X~cE+B1P z9Ei4#&-ka*5mYD3eJ&gZ8VU{l z-$dXOBKQ=DPLS_`D4TAL+-HWNiY9_6^dyK*F#N3mqRT4uCq)5`-33R3sEo!SdH>s% zBdTE0dV(nHTo7eAL(u{ix=B$idglWoZ5P4w9*X)Y8m@@HdO&IUE=Kf(qGfJ!X`9@rZB?{i(P2fW6n&uR z8xU3NXAo`E?@FtN{)P&URg|bGRS>q{qBT=uS4BM)4N=73h@cFoDVnEfiJ~=%wkX;M ziWY7828gP9x*3jt5-&ldtZym0qsY?S9U7=eSCpVAMNw--9TfFbG+2>qjKXP(o&r&| z)`F<6FDkkQqH7zUY~iCEE$9crZi9oZA?OImqNOUWH3*3i+6jc)284D4HQ@c9MH>S= zD#QsO%EkqvBu{}55%D&HjtSZVqImm2_=l_y!z6<&+6B;YL7##s-Zc>A_cI6=3W!&& z1f9&}Prsfx!BajKv%h_37n&?ylw0Yn>;q2e_MQJ2#m^qh#-1+)Z& z{;xlfdiPNv`n9KkHj9XPpyvfG0&Nkr6ZE5?J)l;gQ0+?)rA>!GdrvzM?bPFyHb-d- zK!;o+n@uWs7l^VsrnF+ET?0{uHCnq1sRtr01$0Q{HxRT>&?H4`6uqSAgVrdY5(l?& zXQ+dy3iXxNL}~YdC`D%w71v*BlT^I*pg2*57ZrT~q7=6KB)POmH)?ehwNlgpM2*=O zMAaItLiwvT6zWo;^Hpd*h^o3lY1L1GLH1)T$ zp${74iVlx77J2grTG$zV@jsCtsFu*|_&-=sEdCD>#4qDQ1@WD(x*#Y1uOTQK|Az^} zuOMD?uxRZRbyAcA!buqA_Tc+SxWGP28~~~*w82Un4zdYt6#mEQABo4|e~On2q70{i zDBd&>#hanx<$);PTo8}f+ahj7DdHk2TEve**{oDrF^EpG=MXGmfvMOc&cZa*aMjX%zPVE3>~B}QY@WEOSF804(qX4ds~5lecwod7-aX;$3R z8_%S_{#&bnUvD0L0tv)t$hvFRr~OYASLRpOva$fnCYo8dkho~mI%coi#nH1EJ7sGx zbXOq<_^u#75VT@k@OWu<%`7zDR3#M@KGIrRL21TgqA&DfH(tsPb8&Yyyx)KBNU5qG zE$_nPdE>7N2eBeXAWxeCHoDp3J2iloEU^%Vi)GzH65$6QNwLpv-`BwSux33jblt za$X0ev_hP%SX@@i!CzU{`|zzt1?2?3`BumXOE_zND#R(lw*eKD>-aXff^rAnhF4Ik zn$h))svy-sQ;w^k7*KL6C|GS(9*^!e%Z3tIp^Dt~xHtr1IAVPVn8FG>@MJT#>nKTV zZM3cKzHf}$CM+Ffmq~_UkL3q-nKB;A(F)3RD0VboS)9303Mweepp>Es%ip;pzXImC24N5@;r9BjWf+~yC3rb!Eg}ZNeqQ@sq2!>m=lQs3cWDZ# zSW2YurJ{0cl~2|mN_J#qiYq9-IN0s>@`V{taw{l}pd78BbcPbByHkns2SLhF5-)!ul>7?HES^yX zWjPeh;m(Ms+62X3LD>bR#92Ph5h%q;O6h@Q=OC44C<)4CD7l&Cm76>xoLS2nwyF;z zRye||LveD2{rsIVa?nxP%k$#_uVEqZXmiSdlyy*wD=1iPSSx5*KF(n%r4^J9q2#sU zg}nB?F_y19rdhPuPF+$`L3i+#D^-NT&yQaC)ykA)D1l1B7p*TkiZ!CMkohF@d`?M4 zkH*0r9;qiMbpVR7{%pFqo3D9x1wmjUIa z^pqqx0jQ{?WA*=vN@i^jCDobgsU&B6Dp{R8l=LLmI1ec!W1gpy$rtG^SL^hQtTi4= zW-9yuREU$AS>mB&C3)c&E}u_UvY*RSN(rr~WH_Cf9!j#aou`tLlyQD_5h8l&tL@ky11H!iMGJq$OSTR8oD>9aM;uQLmzsnUs{{sbmh|&TY#j%S_4| z`ZIP#v&K=s6PnDIj&QnR}?p9nCB4rd-jFja}+V82PAN5d@ zoTogLtaK;uYAYzokseA$T5S&{ zGc~n>lHp8A%@zvEr_Le6nVQTm5Ad%{$?D^wq$LmcRI+kCl=RdY9!iFjJD$Nmm2*bM zN>W@(%5)ZZM9R$C?xAF5@rw%lbEisjW}NU)l2VI3l;q@#9!g5eWvSr!<6UD$QcC(w zk4Sj7g#HzxDl9e2&qGN|3inXbow1%uhSNjINNeI&T(bC#jCO8GDw*j$JeADB9!i#T zoTrjGt)i0bOrPtaB&ER-pWHB)*!tw8%ymj4B{_4ehmw-C$5Tl;>Y=12pYc%Ak}h~E zSznbYRe9qlr)QK}ShqK@#(D8Hb0qV>w(T-n?-X@lFz{j7h zyk%Tq#@_0QMGby6)>z8Bu??%4nwFWGZ0DcB)=nm;W}Xp0^6s;6zQQJU^{=%m+z}ge z8y9CMaJh4E0Df3}R}sRLwdP*T-KC)3lCx zs}4J&t(_74^8!3aS;(>%)G;h$7Gg^0A{MwfA)4hJa(G$REW#l4B9_ksKL@|InC1R{ zpJBPS7!NR)u+rn{hNTHcRy!>fiL8x~;c^VGo>|IrcvuOzB%hTu>}XixmthseWt8S& z@Ds}^4ZnVQdpRzaRTHXmUo3}`HPnl-qMMl1#wu#HXo#E1ra?-@MfTSpDn7LDC-Jv14ZIPQ0Fy1hSY+wbyCd8Y=Hn5z4 z2Jz;Y4XXnhB$zg^ydSDz$#`i~q-`v1D``8*RQJnVdP{wfv?GOuuh7{oyv!3eu##$M zxIA33xHhodS7JKEok3-nXr>^Tzg};?1B3hF2sYT)5#|@CW#;kWI&N9gLy57>KGZLe z?e}#Ang<#zZ!6w!{mj=9VYs`(XWQ6ARUO~)uzprY1JieGzSS|7%eSnKNn9>Y z?H9^s*^uOWw$|q8%EP|1ITrA+seVXugRKv6)MSVK94urDt`l=7jC)kL1S!Qx-R5vM z%HLt5*(x0hGV;l-0aSq(|yOleFYlK#xr z20EICZ$?q$hD@F^h)2f|q%G(&%G(moeh75bFr!>ckRz@pk2QJ9kV!Hc_f*0?CL_3Y zkRzT)8yDngYDTh`@I74AXVQqN7^)->#ZXITv5ALA)O1))yrlRDg#W;LMmRdl@Fnb@y{^_TN2|i{J9>6@0($N!n?7KW(F^M1a7Q& zds1WY@5!QZWIdwnj@i6b3t)pp{A5(q7IY9zR2a^Zbw_h=^krHYo2onNM_7g+i);A(Mq!o~59XH30Wodm~1^NZnZUxK3zzJC5q*X3+UZO3*q+N4PxM>pwLCN2aHTi!=O zIbA#TZQHfWp!Qwv>-v`tWP%g09aBb+A2B(1#4rpP`Udr9%dWOE;3%f#Mz`v#m$q(i zTEn_ckGzkSz8B-g%L}iIJ$WEYit`&E!OPoPuDmv_+qS{rmv2yQ2G=m@?C68NyexUx zEwB_sURYGTd8Q`(iQLk-KaqHIa}&$YgfA8o`4hQlB7Y*i%)+0D`2rloaMvJ-a1g`o z%qq+dKO^Q}tB`|{hrP(b$aOE_V8jg1Fa_zhcylQ{#k4^D@fMbyVT(`v20!xO@g*`E zE@N6D-6bD#CNjX6oQYh5>lp4#WI$DNCh|p9awaku&SL6Ae;sZ$_ra-1CY;4^=OVAz z$TLlSKjBrxJQYr3g0Xf%sUIt;gZPd7$@$14f8l(j1ioXqgPYC)O!&I#naT zIj5?T-<+;+2vZN6w>FUc=EMY%-<-`s?9WDl@#bqm!hcRmuy6%qo)s+o=p>%O*yG=E z36mT`{&wcT125mIZ$ijL%zdHccV`vcYBfOdp9_~U38w18W6X}~!uyUnp$02Vf|Hk* z*sjy&1amxG#KhK%H*X4KgSlUv1Qz7woq$CG&MvBwV17NEobI%!DV**csmb61673fu zoamVIB3Mo`zMqXCr#roCk<*>xTI6)6LnJxfITgwBQxLCK6w66M%{E4{+%$amie|-G zu(P6BazjUa;&qHI{vD4y6JuDxtN0;)g7cW{ehDV{jDg1;^A$S_Ohfh$>a3K1FVTgQ z9OEY(&(X2?uT`S~JtvQtG;u&+@yzRvBJ9E*i}BSSDE{gf3&Nlz@WFgRiX~@53i({ zOKlyMxQY=@U;f%{Mew~2j@G>)s74~ZG@8QOza=$iz;Xnt@fJU(9 z59^hb0pB_kS>Q8Go3O(BdsY%I+PYv=zjmzj*IKvBT&YZ)*e~wh4f^llb%@P=q|1(? zMco`uQ(1-K`z4NLr`IUwhzJgT;H|!bhnTZ7eBd{{k>4U#JOFoO6$6uI`V1?}aCR5J zTKBeq+aXaUE}8z?@z4lX&FNjq4oY0;r}C8}uT zz`HTk?#%|Sh$=bW|04%P&JU|pt?^I#;feXZMzGd<8c$z6!m*kaIC46$X5&*ku2QZw-0SqUr7&9l0kBpO+%P-1B3O-=V$;Mr^3c^C+7}z8& zQj!%;m1#~p*7uWy_5HX5m=;Kkwem1z#|dlYNx~w1pq9|EP^ToU(<#ZS@j)#fie*hD zS+%*i;AtgN6idQlc#^RG9L6hns7>SUiaB8cv^+cmXf2YQk%W3lvT6&fQm9Q^DhW-lLnt$(qClbCCRF-SK3CU zT~^vvr3KG)%1W!GEWkg$d&|OJ0WiG8hkAl z9uFGs0S1Z`+#L!ha)-hR>+IpdEolQJVTDT2Adw=}g{Qb2v8Jdbt2Q1qUuY90VP8=3 z@>RUe*mVbsZ1{6tPH>1L2`+$Yx;2+1tMmT1W4#LgeFx9CG-+Pmoh^U=>&mXnoCet5z7Wd2Ne|%5Ct?yl^O)3hysd= z2>AVPnMnwN#ohh(+ue`({gU&abKkwMzj^2U?>Xlahw@X$f(ISCqYm9Uhwh?7cgLZl zGlb}XNFxc>w+|4~ND72Bk^;%nRfq06MHbd&egx0Ij%<_yVOxPFpj9iqCXg(B3^bAH zJ`o7XbAg^^x|0IQ(vLusnC=&WWXUU%2h<=-w?IfF8j1B!VGGhaArv8BCeT!-YblT{ z^#ZamU7kR)G#1FpbQ1+a*%Y8@Ot(ZJSy~4)o$1yKBunoD&7eAq^pSwFbP#AJTkxnr z$e;-H9MjRNBoxWg??CxXM{^8Ngan~c^hnXaP=QeL0cbGOB?%-;Er5nFT^oihv~xee zp-eeYAX%CYG>qwH2_#D|0S#xmR|Jx!9Y7COuzOZR{Zn2u&8pa{u8 zX;y+!Oc@0wMM&8RRLFD<1(Kz%K%<$ir$ERb4>X493I+N{@)$bNw{^Tov?e|i+-o}h z^y3X%>M;F=K-D2~cDgW&`2(! z{7S>DW(SG@>Wfx?sg^@m7pNuEbphgidjZiyqLKOnHDj&g9j#M<=rxSsETHDBbqx@% zIWH(n0r^U{l3PpkX4L@_y;L8n3MF~xl~9^vQ6~MC>LZXN@N9`5)>WRTGnvyd$wU2( zj%-}2*OBi!C3&dA$$`l9p{jOd+HPk*-vhWEa5!kn0BaM-1EgS?z7x54LGdP*xzR49 zbV{N(`F5GUA@#PXcA4X_z5}C-{m9JS&OW}lu&hq#HV85bayYrZvPm?#U95jz9jhnP zzU&^S3L&Lmb|-xKR#=WYp0MlISjbjn$a4J{lKy3PZ&H$!>+H&RFp=-Uwz9@^`ltQR+I zDK@6DSiYgyn8u`3pA=1Yt_Rf4OJ>O0(n-axk3>BjmTcQKmC z=WF$oXj_3-N}Nhh)_A4RJ|z07x+dGtFX^LM$MO?#aVj10J{iXvOV;UotBw=j>5260 z`;yehKl>5nK*t!z**}gwr^ojYBt84bN&8py9Upwh>r-iAUD$BRsC$s zdyD5i^G)c|7WO%ww)b|_ReiVo>jsv=wzm_^TizJdbNI;2mt4bd%scse>g{14Ef{v> z6IJF9*(2cU*T>TgUuy!;9<3 zT#j<-f9Tr-`H6l3&fl#JYv^v7ceH=sH9NjockQ$7KmYY!c%^w?0 zX}8et@Z7FnF4)vFx!|<*ZsWr*?~K_tu;theog1xb*F4;?YFE>31Kd2w;%t;PCpC;d ze@+(EsHJHWU+u|eO`gI?Nd{N)7S_?;AWXb@~` z@wz@t?daU_g|2Gb$T#&KP9D>-82ubOQxtA{z7#cvm6ViJx(51ZpYxHDj+dMJH0dei z@FsoqGpT8b^l@Hk;ncKbYZXmKLQz#sQpx?A^!Vh`s+!boq~M(<0k*+!>pyZyXw@XE zX`7~v8|OA@p4FsLQlmun*54=*?w3Z1-2c)jkvU)*B{msZI40lL?LEEhRE69FyO-`D z_W~HXM_$BiT}JMa&0E3l(Nxm&@i6K|e?IZKUcv5dEhOcwoOHe~;YF((9>BNf@vlUzz^;UzJ~e0gI1CvP<3S6eivu;%9X^rJW$8@u=iIo zKCG37s4zQu6^Z&*V{$pT8d2_K+NNfD%~`+;t4Yx;HQZ(&V=Jz$=KR!W*rR!m$4_PD z;hFr&HhsYT{M6bP+NY7NL$q!rYP;S|y+KD}w!^vpIqAM#|0?Nx*58Nt?9ex*4NvXR z$7%QCVDgV@$*AG}OFl+1?_r~q@EBP|Bck*Sv*okHKi)GGiO>(U=YP+5A{r_#3K7e;Sw2ODExf>h$1zG$lTJv`6 z-{7rbpYqnepQ80k+ParF4BNvSHtj(}89BR0e}Xsc`;0f#_#6!fNcQLYbG#vWFK>8u zFB-lgukF>_ctgQH-tg`|9Fc?f9}$xCmA)@O8rW_RlC=;27VWoh{O0|<>+Sv6=R;zj z-#S>SOAWIB3w>?ABDXL3(m7vZ>BE(d(EV#+U4lrXGW%Nkm+^iZ%CO}}_#wJprr*j( z-Fkqxs=q?(QIhbL{YZozd{F-#?=k-nZ}{pE8gTy{*8jvCoR9E^)<@8= zm8eeYBVHv(^eRvw(Wmq&r08qCGx=}{PW${rNgkTZxO9TST^`jZ(km+B7D*1{9|#*W25w$a5O zw&?UzSaheJY&e6Fx3Sd{&(r!}=#ag>)+Zy30sIoa9H`$igyfyUjfU+RaHdkP?Y!5G zGk>pF>$B)J5v1H%tZN7D6=6GZ7JI>WBZY)t(EAYGIlYmtaQlJoxQ5S(6`tdJwBa1O zej-+QuJQ^m=u;erArp@bY-QmE-U&xdM~+?4d#HBul{$UvSZM}ncoFxwIDjPYqFCQW zv4a<}zFlH{7b~yta>ZfMT;Yek^<{LzDO`9NopzHBlMP`czR)0(bC>mZ>0$fk3TRU- zFzY)k@Tp?~FWZ^#^zQ6LU%9H^pgyM~#ouFzJ>X#429h-f~&5s1%&}W8p#fbdVZ{)C%g}sOO zDUiY>Pv8Dz`?TiaI43v8g6l3Co8|(%-8mzejNF?_@r`?JlgaNl^sjLO&*0(b13O|k z*ikp%0VMl!Xj4@`GHpj#)FXI0)s7a;IY&3Z_Zhe^ax?o>l$*oa7BwBGVr*PG zDOlp-tXYp2U9fSCkD3G;j`C5vB*sUnf=G6{Nl_0~aX#vpit$m%kba<9w4y$$W^|OQ z6Ir|zRL*g777OFuGa&@fdwh>&7 ztCmqn&w+ZQ6w*B{r;wU^a0-cda0)3L99t^di{W7Q3pc5Jh-!2LMZM6AGe~Daxlsmb zpf_WX@C((!wXPD=6sO~Hh@;(L<|uL0(zlorM4$LFK2ViTZeSGk+cg+}q>3lmZuq*v zkF$dYf6fjL_h)=v4D}j44o?>mzsxruS85=vVCZ}FcgNmcnYY$Lx zP0{@bD7m;;MHR-G*F#~PdF_Co;S3PE;UwZW%)ZEs-~{YD@V-0_0h=Dl3E0v|PQccV zVg#(Jlq9sr6<9l(^RFAD8UISJHmDg7*WG{{@Zmh%eEe?2K;=OL$&qW4|e zR$Nt}?VhRl270br#ka@d<3P5Ic<_S!dsKM&Uc>VLbv~R?XT!Y>)ewvSl~j2(rx|o~ zU&^%YBG;B_+K{{ficE@#e>Ub+rr@hjuuOhBfqCQB}bruM<;RFJ&=+0YFuvQ@OOF+~m>MHrm9D!cqi1KZh z9LlQ#$x^(Qr^`T9Vu4V*Rv?sT!pmbFwpnI@Kzjkjv6awz929}}5(pdZY1a)C2nCpc zEOl9zJAf2n4PG39R}=_Lszc}Ojj%P=BUd2Q#}x=XD_+xRE6Owqgj)AN39QE(0)ZeE z2qo(*cvGg$T4{_xD5nIJ$htU#=%WaQ_5{LNbm$H_bisJ>Phvg#3xty>kSt~4g^sqO z!k<96dVrFlL&5Pw0&1mW0$~%t2-0R8LV<8*K=h@tm9!NIRi6a{{fqmEwqoxDLe)Q@ zbk-vZlk+ITbtDi@SO|3Bd!Q_!5D0~`fHGK@a)Gqcd4W*%6g&ZK)k^gQLiIX_uCqfo z8;E)wv2y}p=LC|a7!ZB5RV&4XWBn9@WpgN73M5ODf$Fni_6US3*aFFtXC!acO1=V7 z0?)3a{2lF!D*z~yjq-*-I7R}Y;M$ES-mH~=76|Nnv|UG|KWS%dAW#D~3=Q_A2=&1Q z0>h}Y>wXbPmb~?Lou5G1K!+}tAq(3=KrjV3TyF@3Efh$W?izTjR#M^NN0*bOngVI1 z2!}4&p-Xq@Xh!5lY~(Eh;ffLn6sUsdA8iH)iiaOX*g}C&I@6)c1Il82uBbon~hQ=5DJbj6;PJW0JUaa&Iu$-KLE91x*r9SrQd)$ zFx_2&WJ!*J!OL`B0^t||wP(5rfpF;od1P0EfA{70d-=!OF$M1W$7AVcc%PFAelX=yD^<} zEKd$0OFlqdna*Dz{6+vh#dHRN@c0Jm!gR?3;V}f%ndzDegr^40g4>uWO9eu8*E;rD zBg+LsbytCKwt;%F9zP3&O^vfpgK!oIS3gisww#|pvJ?T-gXwevVZH%PjM{`LX<}50 z@B>rVJ`1HlAUvQ2lBG|9@>q|(0?86hW7>!5jteA97l3**-DQDLoEGRArn^Ov1rU{f znji*W!nFWY$d;pt zJ}Ih1jK?1KD%zP=)b!0x{f7F?8`9Sa&5tynD-q)!83t2SRX(>%cSA$!k*mb#M$=$6 zIxrq&?1TNZ((i*HqacTq@7y~?lfgXiL+B$Y})5GT7 z$6!-gCT)0a<_ypGg6+iSTWvph|Hbi52G|MAg$1?G#LpS8+I4X5b_CO%IN5y8=7!nl z7VlSXci9!(w&B7(Uk+~DTYt_@V3vQmtwBr0t=-nmV@o$i|Gdd(!=({_J2d#i8olNp zd%E@1{&t=b+ets9SwBOqsw5-jwyxVrMw$wT)a-BI^dVUro;H(w(ciG#IVE9B7}*{H z_vOc%GiunD<{Nxm?q>>1hZt%j?mQXSBG1euLLiexZ4YSQU_Sb3SG^a)io$?-F*Xj0;f zs%jEfSJ9+elGayMrEIRENlV>URg=E2iY7hnSXE8N`6`->^y~L)Qsd(-cdDooP?7Jx zeN9bB^sk~xOpL0kNsg_eNlKx4ohlu()TFegRW!-zx!tR%QsR46(WE90s;WsIQ$>@O zYOShCx5bYzYzl5};_>{Diq*St9^ArL+6BFZ;92{_r zL<2k)yg9>e+K?}8rg;@PIdk1obnw!9Y3WrIOV%&21<0y z;FRxa;2&o4D9yiMoaE<_@%}j;h3PyCx9}{!4*DCSdmhiF=PN~9+Mrl6n=cTF1$NCw z=;>@8ZTSQ6M{{`F9JF1>zln2Mw58@_JmnT49+mRRC27&ln(g!OmO7tg(J;-r^I@qi z;3V|e1xOdTfba1I;Oz@(C!OY}g&?pOaeY5LIvW-76}85SM&W+?pom!KA)fUd@K1|L zDGhdI#C66CjJVd|Xh_{(AZ42(bs8K2>4d=a{rj0EjKkKfT7tx}OGv>DPo3*)z;7;* zNT1E=(P|r_Sby+VR7{xEpDrO~mA)Po>N7%K1WkWi(P?Pbr)wCFhGy=?Y8Lzn>ibRy zl{UB@okc`Ptws@LdA>jmg^nz4hn#S09at39&N(3m#XT!h9m zys^(BgE!;%3X6E-yG3X$=Z$xZ@KZ$T!I&2eiL~c4FQD-(Z~W^EhPtfr$P(U@HGU(8 zT`C%1SjHQVEJM$8yfM7k(3SNZM;twMB&u^J!q14oO?93xrdlq>NL*pK{`Uwy1Vkm& zHbx`Pt`dL8!|FzHzOKO&s_qF@_iv%<#+4XM>V|@bZh zM^JTd3#u-mf~wm~U-=$sYxF==%zst%(-W%hzbJm`2~}4SpZbKV>q*MKajGVCl~HxO z-ZfOy^gl(_9cr#iCwUjLWm4Wg8*^>;#AK7A?ZzliJVKTpF+>u1 zowJAK64-@uJi^=Ik27}^apsO9S(<=1>Ap-iSs+<@9;hGF%@atLih=qw-AaKF?gIqR z1jc(!AX(ZB)Q9P|sNlJ#5aCKt<}u}`0?E=BK)spnD}iJw)Wz=QjTA_hX!$oO!Fx~pUr2!x_Qh)e>q6tFHw0V$HDZ-7QI-M0e4Sq#MemOl%G z-SD;hH=P9nLkX12`tB1*W|SNCj=M^o;dh`&D|Hh{mP%;-a;{t_5MsZ8sQ<@RGWgq_ z-DZK{s}Hd2X!caPoGgt2>cV<#6G$u3^59el7c1yBinJ0fug!H~xa_IB9n9Sz0kzUF zfv{b0ZEc)DM3j`WYAXy3tLo56W z7$%%TAo!sL0?X*oO#q^_f~&MsAg#1VAX&NtDvgs4G?hC=I7UF@*vMG|A!HF~3e)ux z2*)h~r*$e*PJogkSy}?bH;V`)OK$^N*>d!alA=o9=YPWa{K^R!!m4G&a@x>H)rEX~ z+VGpIr|rEnhIgD)E6B)mh6vTOwuR>mGn`Z_NsWt!#+WSJ<)R@?6-gFcG^`Gujt9fM z1<5d~xBuoduZu^0dGveVxm+GfvM(7jR0(9#B`o_JdHa&#FM&_rw_t1M(I0%L{;nk% zc-hdBlwLM;Q}rP3R}2kQMYiTw497fc4Z&^H5PRw`jR(pD(nynmdNAEwAWDq*OT^aV zmSKu>RTuijJ8(n8YW;IA^y7C8c@@!4B);+>PPnzF;Sv;bFl}#(LPQs=j*M zmCR$_7P2y3}G zy3`q^8AHfDAL9y3mqkgV`>uTRik;7>-*LvZ&$`vyW#==JHnq`s{W8~N55P(_yfbND zhjGL0d`9Wf57WKdoA%iGjQWG>uO7Kv%CN^@eO&wN6HQx-H_Z zEN9=Xd$QZ{x`p4`1Fmkrp(M8Tdb-h!6|G!!yD#3|@Va*3?$>r(?F32vk2?I#_|BOt zZZYf14$Pe&y{=C8tgbD756Q7LDeAl8X7M8Hl(C`De-eIcmw_ajI$gIDC>`G|O?;=O zdA(fl?v`DjHO)TLsLyie*T}LO#*UTvlpD{t&vJ&FcxYuh#UmZw;bNSC{E1GNNcP7e z!NuQnb7SFIlwZlbKQyb)v|2IbjG;{pO1l)yrVOKQ}pcayB%@vWL>wQt`nr%9V;Z67bLiyU|{dKK~cK<`E-@5^XRG8?9J zCsTAr-}^(o662E-Dnh+{n44dPgVun%`O9@igG`II?NnnPQ3mnS8@ZdmpdO3ylD`0t z=}{lCjM?NFy-}xmzX=XaQ*!#520Be6+_|C6xT~C{YotY5#x>)v@1?*6%`5rQUuurT z-B3_Jx=#z-%`I8pLgwy%s%3?{`RwM)k?kyNm`LPqGi!i_}=L z#I#o2YrO|}?w{~lM|D)Wk-8?M$Nf=T6MJA?V+p-3x$vp_eKjmbL3NTG_zIKa5tT-BU8z4ozOcHG z4c&OgthFL$Asn#^X_y5sLKm*O^H7B$G7D9>gsfpSk|9ycBN=9j(1p8N9;z?{*}`b3 z!W|Eu?P{DS&vtbaAq+H?Rwn7N9MKdDMCd{}eEM>RqVgv3ubC7cx6lN)>*K^N>=JPc zEqL6*4quk=O4WdDOhEYO^CE7+na3?m_2Y30pW$WuapDw^ezkN94(=kBFU+LA?ava1 zJsrT~6ix*2IEAi(EKZ>;kj5n_Ao*EXLOLc-3;YH3@yUQ5AnEUHZ+L~{GXBRiMl&|R8=xNMZajQCU4jyCbVPvd4Di(k=0~I zvIj57B7n62)mY7HSxTak#`SwtGovFsXe23XA68AT zhvt2Hg;-C8RhOe}b(?C^NB!%vJz>48GdDlD5C3?|r+<;l6d?@xq~Y718Kf*l7xdr; z{=E!Or60SrD1OtkW|bUa|8L?I{$%RDYBFGT_%hOwvZ7(^ylSWgcJuM%=$#W79Y`o9I zqhf{wfg;S46-Z`9oD57yC%I50OQV5|OgCO2S(*xDW;*JIpa`jgfE1=%A`qNOKqjVJ zM;)FN%F;$CYcb_J0%5u;-dOp@l%2;t4ru&;f|Df}y7t%M7fGeuzj5$1T32Q_sl(G2o*eU?b%EW&9bp{PB<{VzVO1?v^2b=Cxsq+l z5{gjcCaSV=N)C=QjuE!#RVA408{4o3DO=eIulQfqPqlqG-e^$y(KATPpx_k3kwxqG zWRRN^j0yK!kqHy4T9E@M8uOh}660-OPBeB={afw9MU#yEs@i^um2&vY%=T;D5_y!k z?N>S?-r&0h4zNSGr{-s3Pw}Z=k>=xgL!Rd2D;$JzjnJ(rU!)E3&Gigbr?wz@hmEaC z{1l_JdRz-WFX=Q4yKh>sc}be9z}KPwz*(oh4c(v2`_A%eteR>Jewc-Jb1E#f*L6f= zf#tO+h@@JKW8eaM-C{JR#lY1(cuamFLTh-g^S9weA2YlF-!pt?^8Jup@itGtzbU@Q zhLCp0jUgn;iamk}XR;cn(-my6Vg+vpk>ggjf@!8&{;vDbcl7WAl0Cy%^U-TtQDtqr z#rL`)WaCz^5Dk5(Vr9pt8Rx3=bmZB(CLNhQ-RPm+h-WYVASgXuRg=6o-Pl{-8um^8 zgo+I8vDnjLBm4AsWF5!2?c+^RH6As*v97$)@y>oxSWy}N z-l?=sZ@SLsW*YNVZ8M#uku04B;^SQq z2s2F_x}T}3%vVCw1n>oY1VU6l5Z+g@S!Dtts$U>k3V=1pE$$G35R3@KEqJp)vXlg5 zV4IaDkSr|&q9empULhdnF*%g4Idt0`x}6SP77Po_RKaG^G~W~7qjRoR1|2cs$ zO9+TN5q=a1vBE%bB3RH_1=AJ~hieQFA8>*|h|ksV8t$0oBoJmf2?Q4dP%PVocLb89 zZ9sGa0OrRCBuifc#W3ALfiPzV$WoIjZvj$-X(g_9x5;dQ5GE@SCZafWw465eMIc-f zD20teGm=yECtL>}_Z8oO7r7#8kyvMD1d;aHYD{#S5$$3-ztcFt&BuSx2dQJ<%KX4? zg_9opjagN!XZ4nP#a|e8qF}tO`4`4)CrfgA1~v_6%}$c0ro^YvORlm8q+=CLVtjlR zO;T!BRZUu6RZV(-hbFFYOg}>_;^W*!XET3gY^uJ`+zm{~h|j2?I=Q*))0C7PGB%d4 zg4d;H?zVd5>LFu<%W4cdfvoKi?oZmEi}G>DHR;~~2_~~h=3%2wo!*4nwmIrSO}JUR zu8AbAZ^F&mO~CI$53?4z?iMd@-^REfqb>Rqg?R&rkTMS19liT+wx<^7u0=pB=&0~O+MK3YvX7- zQvy~j*r4pA^+L$quZGk z7Prd}orDz`6+)VxGB)SdW0WQUDc^Lks81OK{t2d4B#L`tLOwAe|7jEQ<~f*v7XPF(IFrkWWm= z%Eif^n2`T5CM213+vxSM;zj?XRIA-|%pvi0)K8Pr%~2XuXKGBm12erXZ^4i}kEgab z{#>Payu?wYm68RLrD;G0rh85xUZROU>ye=eI*Mdz4UmrMUKR);2C)11c+^s)2z`P0 z3;ap7FeJ57xlqbdq?4Vd)e9s`aX=+tqZEr87B< zw^=%q)~Rg@O^s83Q{MfSSV&@GYJ8d_w}dAn z9=+QmdjYv&$!;W1&P1XqTwQw_k{`?_ZjALJ+uzF4%Z+guZ)GDvT1(QfmPw~^#vJ@Q zZTXDSHo!_dp6S1QGE9N)?J6?;uV^PS{TE<6Lvy&>U5v+Fsd@+Y*`qvD? zBCkUIj}VfJYMUNPNS1qKyOHa)O>UIOyItE9hf*rFVohU(wdJ}QaXtlohB3=6HQ*tZ zTearaMr<+t{6X3tH`dg6;BJN?6;sDFov!MwI;J@F+8}bej)@YWuQf56vr9|?&;7zz2{7LaN2 zCPqTO6mObKiu84yy>R!k8Z>kRz%4dRFzGyNV(m0QfbME38J=M30%P@)1XBYIZNYYF z5>3xWAHfndl;9X-3FKYKmm%G;%wHir$l63?_5P4iopE3Ye@isYaTul9uLdGRbdrgY zy{{yh`r4UITDrAfvMI;UvH5m`G@ydZ{3zKpLXGcnDW-7sGrq;?J%UMoiYfjv`OM%v zTn#OnckA7*ebZ*W+qUY|87VK^e1k(seyXXZ$ZB3CE5Z}I^od>iPur#HOw$u;vywFw zJ-@ni*lbR1-ZoiF%8pbs^YzIz;L6t2lWD+}-{r`o%ey|A23(PY;XipAFpB+|JpX^R zOMON*>+HybP@m+^@9ava1tT{?S$iJ~O}bo;*DOkMahj78U(`WypWm1y!s%ldDSJC*Vqzj&V={61_vi5rzJQXCcGe{(`ZDuRup8E@3i0r8E3a4+vb{hlqhE)C*b1B?;uI124hd~`IJxBet8rrsT*n1oEs%c7e{d_`v&mOqe$sI zlTQ5y+J93_YNtYy>lJ5GYYXXjcwjJuH^U~ipJ)pe-y_9$MSQPaNRv;R)JaV0$hK+O z&g%Mwr2hhwN!_N9lzYeNc%8+bh1~t}8Akszbr#nyG*wq;@t1`rAGJY8+!nz}u_%b7 zFEWiv{tXAB0FDK%FQR89`fU`PD_UQAIo-jxkz@HRYFY{HcYGg{kNMn@EW2Ju4i#hBl|jUrm=?RR{pLf?UeZ3vM32W7`yR8SQzpI*X5aYH zAQ8(=;TSS=xoMNSA@VIO$B?T)>aQ@34xmE@N&^Z<4u}8PR~%ikeg*tGI69xLFcqtR z4<>z9!uRNAo4L|dL#2LNN0zU`5X-Qmt4s}j_}Q?ZoB2T`aJA_jtZ(0H({1%nIt1VWO`C&XLi36s(()zKSh1n>vY{6d z#@hc9b`!hz!%L=UbnyF_hy-T^-=0M+J48jRcOVix5z)AQh!ddE)ALn4 zdRn7K5xe?|O3e$sp@Vr#v9$n|V`M16QMHy50|#|fLB?aPa~Ni}%f z^rRZRn43h_hGN)Bemrbi<G)$K7id3JRd-IB#Dnh^E^yhpfwC#ksFgn?$0OFTv0huCI=wYv!i()fUl4$6N9n9 z@HzBF_Te)Xzy$U~Zp=p0N$KF~GA`aYnpI6?@qb?#w10iAvwtkn;h&809Y(gf1!s`* z-_PRSXa$RN@|8RCwV{0RioPfe3`ZXn|_1h!R{?c`5;rogYVFA3O+1n;{Om) zolhE#jjk@(zmt1!62Hp1nkJUCfF-UixuEhOUX3HFS%}r#%iW_rNW+c8ssyr+8r8%2 z-~p%M*ioa#_8IfY`Q`sX6Jrh5ZYzTTyp zGr$~^Wxf5Y$*$4oA5HSmlg5qjA~}a!wjtg_U1YMVclYe#Ep23S`FkgC%ZtKYgjYBi z{FGCqh|h|rNR~!GXJ)!Wfn;e4kivA+1d^rsKqjUu5=fR#0`c+61;Q+9yu4GZ+Ev;^ z-9;3lgqBdEo(=pmTo)@4l6C?Gur73RIz`ACC=eVfct7Ggvp|?%4upxgSjjelFh^V< zlqZ5ShPGl>yg@+JT?4|wK$ooop~{;;NJt5TnYN;?oj~wT!7qbXZS=4TgoKm=$x1q*~^i$HY30jA>%BufK< z>N4F>fn;eMP#n`eD-eq8;eD8oM~m@Mgd^?A=V#zZTLi?R76=|DWP%H0!_X|C6d~&X zCcg_2I4KhDT!|-qONoz9pqIpbnj~`-O+s>V6-{DdRuxTBeA_CTl;m#rYb^1p zY5nh4#iwQzRMDg-Pp+y-oqfM1At8QgRZU_^RZZF(RWz2wlr2?MiD^5lYBI{IXp$38 zR@Ee5s;Wu7SyhuR;Tc`&xF)1z#M9{=l~t+f!BsSA@idH~@-k_O@hURvohJUa-X4mJ z=Dr}1ki_)V_;`mW&6`ajCBM6-gNY>iFCXM9D@gX!&g+Pg&D z`w_XAXwsW_EDzLaUg|4Jd;9WvNSA?k^%I%xKGZv_ekB`U%T+YB`{Rbo=kt>a?m0QD zJLFdsbm@~%qWqN{^*P9IH73oE`I7Vt@cssR7i^yM06sNo0yf8M0QZgU2mUirC5Ho) z>Y_^G0u>(`mys2y#Hr`P>k+8*@)c8&@Z-t-VXeujK*gw@Ad_b_opr<%q?l=++#vLs z4l^@Isc$%qUz=frM{}py9XO4sV#LV)`S1d1eQz-ZITNHrVwmD!r3W3LS1<;c5k%$( zEA3q9@&%-*NFA(s8~cWp-3eCSr;BX~!D2Im$fXb^JD|P~9GD4`r|`M3uZiTw(Zpa4 z9+tFQubN6@+O4D}x;+QaTTNvp-S%OjO6wZ#IJ5K)##4xW-hwwNRMB}+eKa(;aXHO` zXDL)^q&^o+GQyNkRoO%pu9%`l_CYERelBmQXlC;Nx?#u{3ND?=>w#Jna+)^8y< zK>iH*I^^$=xW(8kq_-gd!1s-iE*NJMWDUr7AnQWfAk!eXKxRU|2bm4I4YD2NcF2K{ zJ0NK~on4SLXZUWU*|pLnIG;kX6LJsa0m!|O<&gUzzlX#|T7QE45|VOh2O#OLe?_wA z#KVsmo9XO{1I<2xI$VL|%U>HtlD4s#Zkl?~J|9GOMko#3&*7x?DJ<+Wkwm+8_9qdM zN?UfQBl!v56NytW8;3JesmCU`6%Oo6qU$v9BWo3e_w4PUReSZR%~_(MnHMHCN~)oic@}Di8D{dTNeFPoL+i%X@O(=hpv8tY>P4~LsXVpIMoeQk~I+$ zr^MP0vN2>o$R?1JAe%xifoul346-%k7mzr0*6$#5AYJhW*&dS8jk%C?%4{ddHjtem z2SRp%L`i1pDM%ViiDF_F>n4C6P<#Q|6Y>TmYKmEZg6s|HgDua4jD_q2*&eblWCzH6 z$nKDXA?Zu%5XdQzLm_FDwq!#ibq%Y(p$oi0%A+y}OO43z;R#04n>;QQkvJ>P@$gz;WKu&`E6>$YpANRU#wZI^ZsmBc9E*E_b|_S zQ&m4QZAVyCh=NDen8CyIM;GMx!+WHg?*(*k)wx+a(r2<$V0ERWnrITR*rGskZ$esE zlA9ChjOm}*f!Xyn|959mvLvb+7w#mrx^VBPvxa*|EgI$(RsBwUTOh5b)s=bXRR>8Q zd{uQI8|%0#>Q!zuS1Qu!`0i3Gs-dJ0g!*H5?(21vneSgUji`2`^#{_SB?cO!<(~iV zFdHLp4@?a$dhGjRi%**hwCaT3s0w7=EaGDr@R?4fS|Pwu631| zBpma{;RW6inN-twCe=+mld8H-4esL|&r_+YZ`R=azzC5`^*w5eI0a6*oEUTHcM?|h38Uj7r+yot_k1{XZXr} z>Z5sJAg3M9@!YDtf@p44llreBx#~5MT(xm9=N-24^FGIJ+K=N%=`rHI6I0udEu!)uY{E z`NTW^&wIkw6YCX^s0ZDVzJ*?EACkXwfW>ss?INyg(*qhR+7rFj! zuCWap5$MVUp0VuZmQRiY|D-9NV4jCR8v0b-$KRwr=?r9Z1;TNf^V%4eeguE zx>Ms@DE<#N{a4Z}gGlL9jq0I7MOfs6dj!$~$yc4jdpAR?ic|6qd zf&nF9*;7WEVO9evN9@qucS?MaR7f377IgQ+b>4CR-inNlCKnR)x;!e#Xfe@c4 zkSvAab(=dnqXd#A%P>Ih)uf(qinP*JfnM@Vv5M71=MYAqj1j5D$1P3fUnzU6bg$X1}c|bbW;|+l@ z+fyJ}a`LddwOs{50ehf$HcB9ng+f`11~fCJQ6N;c2dc$%$pXnzBcODqYa);=1iN7`?Zfi+;^VBmZQFNiezb`1&}&7wbB%UWN9uC_X{r+NS0Osalh~yfpEJvjUBq?4&Cz(-8_fxU5D;{iYypLmdYH;LyiSY;ZbVGR!5`L&E$AbV zEX4q|X1X|m{xvTBLqnBQYLbO1_qIJFlvowX`#8>xSl*5BCfBE#U2NV3ii=A16bTrm z7*+A4{wO6{?d49gJzM&d!cod1)mox1R7R*`$*4jlFwo@^wh`5?ir^L6J=1d=g!J7G z+g}QmKU_&%iJKc)|EzLL`vIP9^a83+>8;B3%j3*L(@@gbn$&ar%3J4b+bTL3i);Gz~Iu58VYn=i_A2}Ln29TvC zgNp%MF;oK7nju=}k6v9GX$KI!rZmz4AbKTfq;G)e)vA%c2clQ2M!Ew;uThO8;{ix- z35^s0l*Ldq5WW00(n{RhQ9%BZm&J=eto@}aK=j$_FD(S(`&kUc_pSsem396rke)5~ zHjsg#Z9sgF_W{vIp+-6eL?4tI={!&!hAsicGISM)K4{u#r0ak&OnDP1jv)^`1dGXhG*TE)3PWAyT_xoglt5yc8x1GGltgbbXR6YWdaeK5Z0^Yx zWdZF~Ft1abDv+$1*J+Zv1oOxaB$~)6t5PrAlOFGSy}FFfFC5)#_{fpH?u$jTXXUQn z^5o|CX8E_al-t>|3zk7&#OzFuw$J!}zU<9==YIM*f7ije+lw^P39qx?dVRh3Zv8qt zrw5n+*7-xn4Dh7s$^$dNDWx*r&ay?-&e3~N2BixQMg1}7vmTrQUP$Ims})G<)XbD^ z2WBXJRhC;g*m#woG+f&p@L6~Xs;o(;*XRh`l5`oCkvp9p@-Obw(9@BETd!gnda9P( zr=h3uMrBPx0tSk*U~?<29wX6n?$D&uOJ^F6bj8pa&}^!#$)KB9exHVJq8qMrN3RUJ ziFGP#5)yF1?D4`S>{wYs0yeU&^4=#TU`I}ptWQEh$;erXoAZ!ui}J~&SxU@(bLedw zXDNMDPHBmWwt(4+?jM@7o_!(o3z9ZZc`BrCLPkn@RK=%a%KCQAQ$Zn#Nog4gtTj(= ztMX$^TjEoAGH|Bt_}GpHd0#`sj^GjN)|f=iS9qlV=OjUI)XU9q4hA%Z+qEqzn2&kp zt8inVZ^sKuMdiSUn8O%0ncnYl?Imeady+M`mQM3B@Vs2&vp|VpZ3lAk1n)rGBIGa? zR^I{6@QxMj>pNnOJM1yqH@uT1xpXF_3vg`Obgm%hdUmcTKXpoA`m2!Fg_CttaUoCd z!spD-2QKPDN)}>_mC*eoNd$iDh)Gf(E>a#^d`d0?XNREsup+Q4&j#7r7AdkyJ<^|a zTdbJKn4um%WZ`0^X^j$GGdPAU!v=)3Tu06?2CYK%Fe6wt#fd>A>kb}7F)t`@CeQ+% zefo_VJTAX?e*b~_qw#pv`kn`MM;mCSHKxWdme%(q(-OBON^Br)rORU;jFT^25&a~R z)=QMBYT1dLwI%wK(@PW&4PEgb*>-)2vO{HAspd4)Ymf&d&{U9LL4E^y5Hb)~#vw>L zCFdw)0^~7BnqKm2NP6X*glr6X3X<9>ry;vQo`KAREQcHoc@~mR<2eU87xFxQ%d9T~ zT!7+T$cvEsAumCG3wZ_78C&}uq$lK6$S}z7A!|ea0GS4P4YE1pb;yp8Hz0dL{s=i3 z@+Ra+$e$r8;qoiwvyiuNXss52-=LTY33kw0gelZ7ngYr3Vt#(F7UTSm4kCsA4#jIxG$BWoxq%pb?17bx+Hh7zJgs4+=;8&STYSIWBO7-<4x6qYNQA=Qk; z&mZw!S}Q=)7-OSr(-lgg`e%@@E6{Zu$k!E06E%jevr-vMhhDx?IqTok55F$$+qUo6 zyIpoJCN;VFmIjjrtCVb!c+cnu)j@1|Zu_>~TeQ#V-72Sfdyo!pzJ5M5<347(xcLqs z*_T7@zI>HJ5^5Wx;j=Hl(IKFCQPWoUB|8{ila9E)(umEPwrD2RfYMB&b80uJ>c20q z1^4bQiOq~g39amOBzzc(neeZ{Q+iCrXF_)*2b#f6epJn7LaREF#Y-_6`8YX?g;fTU zkO8rQtetCARCUQT`fV(^fp6+hFq@TDN%Plm$N6{?T@T~$)^Mk@k1O}{&*fFoR9Aj;K_u|9f-YVwRUjHrhR)s&d zdWZ3U+C12~6&uVP$l!siaUlQuefvG*s)-bMXbuB-@oI-x<01ch0SSM`eg9}5{q_gF zhyO8N_s8?>{=Ia2@kgBkt4s+0H&-^R?uvN$e0MgLz4UI~oh0fbPrO|Aac@qFepBK3 z-P`2x;Mv-4sXgRD?Z%t6MASrSZ5J<6b|$vEf_eY%3Z+%UUr-Ny@fOAJ!JYZ9bkL7@ za6j-2?MprKJISJu=3PnA=s<6>^N12adi|}ghh;Kes`17tT-vcPH>jDwXR}j;DX-8` z=K^ND3M5N2fT){6D?KleEWH3k9S&MLzl9-WD8PF$ACFE!;^XBybe-vJcCK6xWh6Sg zN;d`4O1A`(CHnSCTeVVMfn=%JpK>}%|VGdms-ka0d>I?$OQhlIwrn5WC|3QysFH+-*3i-z;xYu<0>Bk$y zcWs=BqE9Vm+MJJjD*aFTdgPbC3;OLvN&HfU#G3deam*%)|FQ31(oUHyv z*+gIaE7yQ4Wi{YTL_V)%pe|hWQhg|1LGsQkq2$U;5K#? zNgnDP9oe{4uOr`eO7c*JlLL|ILygqtrgOBMAnCg4hZ=C$6gJPW3H?sCgvyTcu{@;o-liFif$QN%fM%T27G-^^(?T=volo*}i^KO&2=i@ZwR%Xfpqzl7u&N z-|2~T>wQV;<8OTgInXht`mH}Mww{`sbX~`Z_hqH;gSWrkUG~U2@VzayT*&$>N&`y= zzpg7HJ)U~q?o(^Cu=K5kKR9=@SF# o~c7*?s4YK|P0$%zVkU)HOZl{O1R*?M--j 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%0AqeIHBnUs{w6EKD7E( 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 diff --git a/external/sources/allegro 4.4.3.1-custom/_Bin/allegro-debug-release-32.lib b/external/sources/allegro 4.4.3.1-custom/_Bin/allegro-debug-release-32.lib index a79f14ec041773df7df218d215991cdbe46248dd..7769139149d35bf4aca2667fd04aadc19d11f0d4 100644 GIT binary patch delta 1516308 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

!3clUAvC|pg%lm*IN0+Y&b{~8E-$aYdgtQpi9;k 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

5J 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

xkyDsZc;G0zmEJ7#A!2lOGj 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� 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 Ze9yUwvL671%14B30?pgrhkkqh*;2Sn9 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�()LyFAMA)m1A=+Iij84iU8k> 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

-Js}Xj=Q%{E74WX58IPX6X@}F5p)%aL5p~u+ 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 orveu*8F*q0JJDX|L@yDYJr68i(L z2I?If(Z1^4`DreYHYJwCpIgw&#DjBs2iXTnbf}E^n8fT7dqQF>CH9iUUX@t6#CA#S zq{Plh?566X_WDDDCTwl0)sEF%2G(3+DH7`_u`UuDAh808jhEO&iIqug30!uyy`Glf zb28#P65AuOLlQe8u}c#BQDVPI>`#ex#urn)xvrYaz)FWmuuOs-@cmXJrfV((GfOO6 zVx(?NjW|;IV9uNqc5~<@P{e*M{@`VGe(Grfp%csI`53qus z?^b?sfOVZQ+2=Vz%Pi-YSZkn!e2LE<)qe{et3LBaw)2s6XP#HCvt=D;FQ`Vp$je7t zn}%>>Ryq;NGONKkyFJ?atNq@-?h>jLgKViMq3ct07I*h`q)aCU zxGOvyyobB{y6^4@pN{am`??r3W#_wgwAb~{b@#o2MAYw9L$0J&i{)*StO3Ll^WuBP zTI+k#HWRYp!>?qI6$*zg2*6N2aGW(w(U-Fk#O;pd?~J!T6>*Tw6y#=NAfFfU{1rX8 zU6a_eJoYJ=#w&iv>hB2^Lbwck)kD_UaN=E2kD*3?1iG-(u`%B%Ah7)OL)LAI=Px31 z4qr9FdV=rE2Yuf$(dy>q=INvN^wD|n4GvqBa*O-V&WYk5PPA^oCfK-1*60TGq;<>| zfbBiD40q?c=#Fc-TH7XBH^yE?7;&{IDsi=_Nb=v({D1HvPuZf&=S{XA*V%XC73qcm zBP-WjP(~>+`tnhzfqf=1`Ua}lw-Tdopo(3W7<~g(EZj|X8JS6Q8CZ(y>WhL&DONX8 zNT%j8vYrwfF0s)PE0WkOi9IQ?r+LcT4NUfTB(O(DIwY|p5<4NWQxa3~f#@b$#a(k5 zm`P&v9i&hL%aT}ciIHANgwjWJkOardh-70`OiCuupu4Z^$f}|``#RBnFQxk`3E(42~SR;wiCsB=;C9ysd%a_;RDo4| zt0Qv`eBB~zm~xT7y~r9>=Zdqax@5mw$w9Z0iMJKLY)c}P-qVDJs7G|&`;m7|Xk6j{ zZbIV<|92A_SNOdXnx=S((pXkK?CjTGG*)#@ZWi<5TS2aT-)U{qoam+$LNgHJY^mR6 zZ}A1E85QxQkeyP5xfbV+`q`~`=W$jK_pdS6*vG#el6$P32hJg7Vd4=6Ht%dX)K;ek zWz3U3CJvsE8gg~&Ga1vF2=+9yP^S?Q`#Z}(UQe*vqd*^uGLMNF(hycCjsguDA;7Dc z7+vN~5II|BJ4JX48dzGC*2 zK%)Y)0PmpNqw_!p*aa9bszH-IwP{M>pF$k#cy~%*<Ih_e925pH1}O$?ZLZ_${m+N1>jW_TExGCgkCN74@lxp zAA_V@IRyzi6uSc1gkN1@-7|<*d^#ek5pS&OGLTAd4I3`8LWvbgtW;vqp)FECO{>J7 zfsK^dScxr=m{VfwB({-%y2@(G zIw^rOGSX#<(S%T~g&rdZwHBV5%fJF97RsZZv}W2%B(Ok6S}8Fuv6m$Fs>F6l?0t#R zgibB=jKpa2reZY`3v?40Klh|H)}E?*@Q~VQE(6P#*dU2bmDmi4(SSwG{Ar23AhDMv z_NK&WL_;YI?3n7JF~&b6cveRIPGWybjD|F7VqeW=WC0RuE-{+Wsqs2Wtc%17BsLtb z05#%72^R7_#TJwOISFjgTn4sBVigiQBC%r<^T8*dvc<7y&1GQEOYB97y)Lo0B=(8w zazo-u?mNeltecv^_wKVLDoc2m$Q%=2?`QMmy`QqSk=*2Gp0e7-$RT14MhcjLrIEx8 zY!FGz!1hkejpNU)!78Sc`kE;2SRd(Leq@ccn-1TH{3*Ff9X#zCH#Xn$ zwcD(If~mdjO>2q=P8FWrWo-e%cF=BXN{88{HFHKdGjF47r#?ONHVq&0aANrey7Xlj zg5)@-VAv2P#tga63^LM0HkrI&G>XsNZH+PJRrf5j+`?3@sGN66&k5~XGiR)Jwi$zk z>Lo!-(Eb?FP!Y(stmrm!F3W?#D{-RPC(N1YrMFbm>1H<_=8HHo6v8VO;V4YwJ(z5H ze{7Ak)5w5&byv?$BM0h$@jjv-6-xa75?(Jr4riYXdogTb9|DmEo zrVw#_NXLHUUA9^ReW@OV($eV@^Z=uIRX4?po8Gh5;oE_cB%goVi_vNxpj z_jeB`)SkqHcYn{?Sv_1-EK*h?MOz#$n!z`}XWhoJFj~hq0ON>3Y!pspmoO)v^S(9O zca&nmT=#KA48j8y!-X3-8c>IoVM6ILXR}x2_z7Al152q>c+N&kZ~{oHxK81A4M;sy zoR_63=3aq+i~)-dXJQlCM@*-7|6vMs{{`s&SKeiC$XndcLbOJa=>D_c66tHZ|I@-B zsdbfD3ls^gxQW_h#A=z;ajPb1vP7I#h~bWvbQp=@XZBiidw!3q`Xiwy7Cwi`UZ0M9 zxy~k0C$83LJ~Epu##J;D$P<<5pfo^ZWwL7O+;OB}6AVQyudsF#Ytu9vzbT@5^P0u| zgCoG0#no9>YOnAQq*fGtJa4|w>J{obTBU~42qvC2#@aBgt_BPiRZA3E&?q2_hgM=C zC0oGl1xzEdES^R1XaSQ6AN8#CNPcvgwVqya?B_#*@tKKf*UC-#jGukO8pE6Kx0dqL zSNnVMg9EI7-1%OTkKpy|)_1)>KxMJS>iC4O{lIF=2}jm=XC2Xy=*At?z*u<| zrau-y4uv0`x;Rmb_H?l6=%AL0Wk{?$Pcrp0WjQ1;Uq)IbG1_xb6Vfp(av9kki5-#HF&=Oxz-08$ zxgr^Oo!F5kdoKxCHJ5?WVJkJKArhM>F*<9dmPn>G)Oc$p_MXJ{N$d-WeJwE^x_G1w zLv@MBt!5adxr{i*f8tC)ik*&FsU?h%k?4q(iqTPDHQ_voJua~qCH9KM-jW!d^i|V- zA+fI|c2#xJddbfcG~jEh7F^{QB3iS_5Rssc=@(1dMABsdyF0JSpXHJ5=ENoD|K;GJ}Mk5$A7yR& zlzvwUM-x7}96~Dthcta7PJidu&Y5nsM3CuERwaXa>kDq(6bE4UE^#X*Ef2_e+6;8` z!(6@aZR}YJ{UTU!y@hSd9>}qere6dm7>jPhm?C<5^u}WUvKsrBfzCQO#H%awqD6v> zZrHY@gTS|Xi-W+T``_g(^5TWR^$&DIQRDcg!`4pvFz7b<_OP`fU%n+VlIQKQ`qRK= z>>dnM%A4{0kF8Pe)EB$)SN2#VbVHkS{xQZbn>u3bVqc0KEyq@i2fzF=_Q}@b0B$`} z?huMrr9PdprOE-Ugh!x%8h{fBu{q5|N6Hge2Qmm zG#}Q-65viN0Z(z|l&z(arhLzdC^)I`q@_)4fI@b{)}ZI5Z#{Jrl_L04_*-BjeY)D8 z77Fkzq6VTj9VGixHS$O3l-*H{=Ij9a(~2i8!AVr+E|F+1dW~t~vybIiVU3d3-_|tgLk5x%Rhg%2>9=~CFO52b%!cVO=3NH>F zg`k|NJ5sAwiqdPR^Od~kr`G5QYK12#Hm0=dLISozh2M*(a7*~ZU4*9tdc!(ctMC`M zOSK;%@M?VY1F$9Q&PHRrLK#qbT$JxA+L8LbFyVh3iz_K;o(`h7rd1G}wZWCCGz2n$ zeOKFCL*;T3`c);2XCAe-;xVsVJd8=Tx5J%BtuX6IXeaHaag%)4QENoNFCtH}jHaGo zzRoWoa==(og=CpWE}y&%@E`$qkg;?5x@Fcjd`=}sqo-cCbVm&m_gkm&p-r>>bjo$!KV*7n}5QReUHEj#h-zB!@%>@jOtEa}DY)2Vtf>O#_>I0%v& zHx%*>e&voWi7$M^;!_X-KRrVn9(K@tKN^zmBL?y}$XLifv~UlUTMvF}(}s`@AX`Dw zaH|PqE@V?ky0ZkxhxqHqt;Kd)9>_ozMlw+*Trf8!F^j}#Wk8KbLj!Ud7!3wgtXN`n z`zrRl#OR!rijn3XHSL$GYa9wbDZ$G!;!hGoTWN^{HJ5=kk{Iqp%doe^XmKG!RFIYw z$Yo?>`KP}Gn0V^v)}S6cC0eez4D6u9K9<;*5~CG{43T4v#PoVz*cnGZUPYGCspvZ? z0w(O53yb&?>s@~Ob8Ef_eel|x#0L-4!F-aK4$dHn=^#Ieh2+)zTAysw%9`DV@*huH z!)ZUxv&vfRAvfL(D4niA{^a-8q;S_Z-NPtJ!1(>qw%vz3{XmDHa8kqXx{kw5`#EC) zbmP}nl^ZlM3-KlfEpCdPBS$ z3)Z&JqmQY+oEy>hioUuJ`mG%5xURr#u(RNN>B*T*6Fw}ary0O+-N(-9U0ORgF1z!40Zc2OUkbi>t(J zK1E+dEz{UZZ4z*In^0{$XvH3#{AFd0I{_Z{Dw7Kg#PE9>Mndggvbfv$0bC+i0 z^vk|G{GQES9dqZ$`eX_3)(Z!4X+&r>?O1*Ml{GrFy>CyWPWp6{J)N8QP z(hj?>>WcP{BD|gAjh-_M^rAv^>__1~4GJCgFVuM&M4TB|&mKN0w>f{JZ?;zoY&zij z8EhVC+QZE9VYUwzAa{wbst}TPl1=d>Bn1&=( z6ooBBjrX%dY`2zHVWUOagYiE1!Rv;Nc7APBPO$x15uJuaD*bG&@Hf)c+L|3TYsBcW zq7CUSAaz;j7Ie|r9C{pMqbsy?k7wl?+dU(yGYxO!C@qbvN@tZs){V`F(P%T?Jf}jo zZ@bt93E)MMh*lL;bIB^#Zj7!TTCuhwQ)NY{ea)gG;X0QVv{iW61M#rwm8Q0?65&-! zB-VmTFc>=rMR>iH_AqkU4V81fIDE}2n~yn3#B~=pL3zgse-fTt6TFU{0*cVlV6f>L z_kn~cD(oW?)HeDoBpAS_yqFVg{z2q0PHPrdZRX((p#g%-aVoWe828^13HoYPa3v_g z+E72SHe;t%2&$QDd8SGvpdwph+bizwTx;w3sYs-bys4gQLqCQ@gT${#y)m^G^mMn2 z`0CCUtvspmA4l94G^73(_5MJl=qKx~rYJ?d`-^%Dvrrh=$$HmTp$*orV|nHCAnmCp z$h*i~)R%P-evsW(Z$vnm2PJFu>RaY|nFr!QVn-3XQ_-}vfnlO7Ma^IsZZr~QH-y!5 zTaaGw_?1ujXh1A&dUbK&#B}zrx!%gR{K{=>ta3vvL*e}N@X`?xyzrv|F}jbmXw?+$ zWf4vHE{uKsX^Ya$tH)Dj=gdsGLFM9d835Lc_NevKAe1iG&}J~duzE!FghTg4BO=b- z(a2~jU8B)6&Vat=#A)^p0bp$k7fG z14>61$Xv*7kbNMjuY=Ye$56iJ)tm@ErUuF~R|6Xbj6MouAW7B0c*rV9sPJ)o3;8hQ zdCgC6J^86Sq3~+WkF)h`0{k?@r*L}N$#2A6Q#|^v!P2d;2Rs>)-gQ{KwL55G00v*q`r6`Mb9|-ws~{J^|1Bg;SUE00E`hubxfJp?B*k~b1EC6G zLc>-;Hie`LCqr^bdS7A7*Fi7((~u6xXWPMn!WoSEI_L%80J#YAc}PTJ8zG59ia*B| zNXqDa$d`3|(m+cLPcWH-1_hu7UD4h~7Oc4ptd+#tNsMMjYP=kYO_dl{UPY*Zj00=& zIDh5wJ|=#?Rj;7vZzM`BLird3xlGOR2LHiiHW{0sZYluNiLHB?QYT7)?psYoH>O5< zPGU4nQn7dVgsFKZpFbr4y&nA+_KoBV7xrzOI$ZCHH(GNUSfRwotZ!FQv!^BY9FNx9 zOwk7pkJti?Ov1cXr zlIo&OOrok%5(E2KMm#F9%M$xZVkWHksfEO9E|rz4Vzh~==4X}hawXdK`pQnNlqgpgS{$PUt*0V)=gqP zcL2}joId!W|9HRl{T?3e(iilm{zFr7gH^>Fe%zlwu=>a!Us|7XbbDm$ ztBdy6U-j~L?L4kOY^eM+YC*{tY1e&o6QUxMuBSZq+CP2{d+hs|sT(reXZSwx{)JQZ zV)f(KJzBZJ@A!_f{(bEs)$Zf#jc74pGS6?5ocgvSWKizzO z&+$jgO#5$l_I>Bn*nU%3*3=%E-3yxzxSacr{^SJvfhS%a`PPUnAIS+6X&S0N**G+$MRM!p7`jN)P@FIy7%)WP$7VO`Vo2@=S}=Zj z`+M^Bh1sgzz(&1PyMe!;df)bfNj|e`^KJ_&cMgN`rFp7tKiarD&nqtVj5mxzGwc|I z_1@7!O^+cGwR~wjvYjaO?gWhU!-B;`tl<=^cYKgv3-t&!^u+x%FGYvHfLFbyj|ymp zY*sB`Z0|BPTCs@cL7({NpA^b{__De-H&4%}8GHW))dZnOATlX_RSm5|sK>I`7`ynU zY6B1{8EjgRy+2j|Q1f@O8~&~^DQL(+XgpSHroAV05OD|D9DMgZ#=bwy7X{klbXAoi zrJ*tI{EOpIZS=L8YcPsTKcnUv6NE&0XYm?bA~i%&9=FgeJAYIYet;kKt3N?=)J?wj z(?L;&DQK5=f1uy4QQPGr27TW_ebo_vB*Jw}+^~^)-kVqrT~truXJ6BsxPLJ2dNT$~ zZR7FaPBk_ezd=r?G30!=nZkE$GMbD*nEn6SOksvj3SXCnKA5OKKHU|zt|u}X*EY)E z7q%R+KKgM#wXAi#BG?w8R|@|7T8(yx*aH8JLL*Z3c&|bueC18haU_%*&Hs<}8f|-^ z*GT9!I(4@11HDEM^cod*cW zX9=^SeWO9#-_auE9!rA{Y#bI%_@Ai^p;wksM}czZUfw)r zPwyk#zCF8+y(j|9IG7#{#!N{yd`K<>O9fUSur``Y)r2%eV0pmEWf100XqVl{rU4_D z0i;vLh6!wi=K8nVk=m`mLKExSe(?QU)U4Hx6#cU`BKA>X!tghu5oZuBtN4bqwJ=_| zB+@V8W?4ZsAJoTY(j$~d56%s>(?-V}6?Fw%DEiIuqILzbH?NGhMEuAPwWXZ5f@V8vH z38CU)zX=wDNdJ)&e4|Qz-g%ISL4-<`# z@lH3rKC=p?CZkmTXh0dxt)-^z+p$wfz}TH5hnX5=LD-cdS&dGIuw&TPs^VbZWmOA#&SPGwS}aH!LPD~YAqTP?MbbSf}-zc3w?y6 z)T*K;`)N_wM%0&U%m_S9f&GlL*~IZyS;}gq=&n?q8>+vK5`kDVa3;?_}3ks>vc$l-_4AtZ4;$WI(;@)LoA{PdK`@5YA~*pj?kqYz5ooAUzO)4XwkHONRi`&o!) zWF2|Hp}r>La0x&&Y=PAjO;jy4263Fp1^$}EUgu|~_cZY`7#rkXr+Ne#!PVBH$Med_ zXq?^}ieqd$yXH1FZj(VfdG8_Cl-2L#1x0&er$No$sJRS`6z!;3H2-+0ZLV>@1P*fl zF1=Ft{3hlgV-j9OHCifvX&CnBY13Q<9@bn2RwA(l+_0{niPudX(U{xT^$Rk3WARXp z?#r)c=9-M{Bmnaq?`J>Bm$n?y*sgfGqPc4>1B;YcjKqWqIx@l=`LERM&5*=m8~}CJ zUE)>;L|)YhFVTxtZm}KhD!IioRBo}Ch)UWqVoP#DbIcSa^NNdETpZ*wyNmNL#4IMW zyOtYJiE+}eYz`h4v3R{HO+lFGvi?FI#xd86q_NM$lR8-4Jz;(K_{`X4e8NauTugPA zIN4I2R(xaj=1VJ)RavpH`MVHKWW@_BvV(cok+yJ{96nFo9O?Q)hm*Pt8vdsFp+M(; z=f@Sd{40L*x@-{ci~bdtycX8$jy77@OIetqE7_+f9Y~)#i>Y%GB^KjI&J`+D@R6I^ zX{Rs}Mbh_=u6xO|`|b%Rr5SgVXX&uq-RbXEn&E@|cHI+xkJ5}du|a>A={!*hsuU$A zf>wp-sDwtqq|vtE)t^}1n!F2~PP@_vjQXr25OHiL z{u2;VQ=rdpruZyVr!|_Q;mb##Y0BzfnCGpYk(0nt%tp6L(9GXQaqPH7-bZuhyQ9EIp<@Ru@JJI)1pT|DGknDUY=x&&D{W zm7(P;Do4*k0ibeeu@Q^wMOEd8+N;$d$LVaY=uB0W9}7fI!7`UM!mr*U^@cQ>XBe-? z-K8+mEg@H+*D-KYZ2%YHX}AtYtM{YeHFyM1X$dny!d>Adc>ZIAvBkrxvBeYAwTAMTTWOyB?T2iUZasXwyhX38%UAf6L11hm%o{Ib zz;0W(QqDI#WDC_B=FaD_WIQ;=YlIh{yxZ1`4>_G(S153ds}fbOrDF~eHL3MiD7<2T zHQ2sc!zns_0*FZ20&W-U}J9r7W_ zd5~mjcs?Xq7^YW)EDXO0i4(LAdeAc<4@1s`q;C=V>3Na=q~@n_BKa?Be)>T?3V$_Z zG2}0dCr`9Bw9{k5DO^Vn$OVwSH9rvo$v;5z57zv{G(Y{IDEwhavR*s|axr8PsAouw=+mc!retN-SdslNAXtbkY#08~L=oOS$ zHh(Itr^!fjSvBEI{?yBvra`YsV24aqA+ZAz`(9!{NbGls-H})v20Cg14Y~48pec2z z1V(Bu1G7u4L}D*V>{XsW*=90+CV?-x9RR=dNp=w5KG_yDs1L?OY9aZW%fQA;jLt@> zx9^nL6B1h|v5gYjCb6A7dJ4+^Rst9J`g$1TWlymMr4p%8EjB`P!I-ba(j?YfVz3=c zse?bw4zjP5C{Y;I3T~7bQ5aQhr^L!7_NBy5O6K zQNf-vqJ8Yrv4+*KX>I+Y)A{h; zIO$oMlI<6D8_O6N>6J{psLXl`Lo+By!!kn2L4S<=O0&3gs;xQyvbQxLj7}lYLdHe0 zknt+ss3je8%ZjVGo7o!Qp3Y0TG&9eIHM|<;H-7^#9AWO{{3zk*%>EdlIV&(&o!Paf zD6FQnc3LX$2A@}CtKR{N*7hJeOE`G*EB#j(nL0b+>{RJq`>M;GOD-yuu1(2)ofD_5 zyMf2`*BsVPv&D_aB1u=&4yzx2iv!S=U7{ahY7J`Gt{5vX8{u2xmRpwjjbF5bO2QHb z>2N!k4Q-mV#?s08l`{{=-MsV#-=JvczS84!I*l0vb!_t~I&{MI04@JlCdKc_d&*SWh&2jDYttd5Sov#Z6}HyX3ELrhiuF&Sz91gv<7 z#bsy1@6IFE=4NmFJV_$w1y?CS-^2Iw)km!SaQWRwaPR=ZoTBG~j!9uXK^uRRLQ_F3 zs}R40n%Tp^)&CIlDx9IRK`-KlU(U6*^oqns+o)DtpKG10#ywy-2l+>$S}Pej+9QH- zHh?u#8>>&VMf0t3?TN{34EO=Mro3Sc#LGBG?WewEI3#(hm-w}Or2`zFTO)&Oae_Kh zorqIfJ(S4;gO|%U8o+!_hS1?@#8Td+ZsJFxh`|iS8{>ZeDMk2Y!0DJ8tdyx> zIN4(htZ__B$@T~!_K+;=ar{6XsT^dVEU;P`RvU#B*V$fL)as@a6-z7Vjn^rbWwehPZo`7G9QyT&`r#w znK#%0$5(_{Qvq3$vT{UtoL!Cz8HUyG!mdgAd{d!vk=}maWcJ`B^b~ z{0hWE4^Nyli$59~?)&`q0@i0^z65A$ zy!aBRb4)Jsf#_@RpeoG7NZ!^yH5z%EsrA%jr8LO+C>ol8C%DcKiQ{@Ug*&<9?N<_3(WZMWlZwISl<7M@@GJt1eibdG!{Ee^L>w>t@=p|&B5Uk{LNN- z;_=(jzirrdeU0tGDLjWZYJx(?;z)G1pO@>R8qiE$DuF`Wi#rMw4^AZrQMm zllE}$M*Ly=v(0ZK18xz2A?)7o140|m=X1YG_U8cuvn{fQh0(Km^uB9!TU`6?q!z_4 zIIJz%k&mpw#(>2|Y=hGpr!HjaPHQZp-WE8mF&-s;=0{P=KVKxqMsgO}fmPo35d z>LB+4D!35ff9`yVo_8O>B4HE#xvxK3FArA+#PKt2u{F>{Z2oK%e%Z*X^Ri!Ler49a zp$okfC!Ktm2TEgvK-;;96_;6macH>N5M*if;_QKwo^cCMV)*3J+3yO^lqU% zjt24TD|LJmD13JEQ#|Mw`B8#&c1U2qNvuXbw1)~gbDQx=Mc991;dN>7=TeMph)&a52JQzqp&QlmI_Qcszb z2h8re8G6d3WJ$~>v3!xDHCiCSLRoO8#7ZPqDzS2jt(Mpe65A}X_a*kR#JCotx!X4i z`+k%?I`|Uzg~Iji34bj8JK#yxz~c7V!*LqgGTI*D_y(UpSLUTFO-SK>1l~@tdxO^v zs3VfOI^wp=f@y%V`Vw5OT<_BkX(-r#0ik^E2fb?6RtGfU@_D zkNNX}U41*;<*Cw}d|w@p?V8QKtG$E<*v2RE45GJg!Yzi&{)Vk0E&A%SK6N>Q-Kh^PsYv;j>F6SmZA`1MhM?8 zP}>K(`ao@e5-wX@J;+Z9>#-7!Rxhz>E3L7es%g`h@59}Yc%Zsa-d7hp^}9Zew({}c zOX-M5A$ElZmQrZPVM2V&n&T4fDCn=g`lM&Q>-9Z{;&k!9>Kh;;zTb0bmwMOhc@Ax+ z{Db$_eTshG3F;K>MKeM7&<-??RQ#cARbrM$tDkw10NpQyTWgVKmt~&ODSu z>gYOsf51XsV+gqHe)z%t&c@BP4Pr$r?TyHL$XKSOAwUQ@674bzp9s=xgeq&4@8rfq zaG~E|qqztB8^P%dL3os52=q>cEPW$9k$n1TwpW}>C5^xmf<~H=k2n^_8E?{x9On6E!mUSRruE4RX~H;Gw-tPj z=eO%B`>i~81XDJHbN$um?RJix<=Tpzp*-_2X4Qoq$2U5Uj~x=nX&)u_U!#%4~ zvYKD_;!{A5&q(G?4q|kNBU4j_LEVTp7nm&Z_gqJ#vlozeo zp@a4&ta6PtFg^&?Yj_he{5jF~QdHZC276$c3}9zsazlHa;DyZkL@UZ2o*xQ%M>ic) z6>+>SPf0*iwD%!(i)d^Q_bQQ)#zz;#M@w5^=-BZW&Kn>>gJ>WZrZo>Bjukwt9^(b3 z@dC8%5z|wOWjRf#QoiH`=JEpc=tKe9ghAFrjx7G<1>)u4M1jXKxJad$(v>^1@G?42 z`uTY=n};pha9Lr8$8`^5d#+i1)kj%n0gM$qOM9{nYpvz%k@vC#+jiwm*6oOv>Up>Ulc%(;E3(I!@Er26>Y4Cx9k`Vqf9x3ECdC zkDl+R=Sd%d`qQ|RSGpnniaLP0K>L8M2JHk&lh+xPX1xpO3D9n!RiNEL=`3px(A%I7 zfcj$OJwc;Dsa$(dYNwBZmDg%r``<%{v+&;x8JbAJ1Av|q{^sba@HZ#QWyx|Rdl48; z!~O-%g?v#6fQ&9g#ZM$gT^HCniT%Q&Cc~H^9=#G3Td}vd56Cocl)y`@>ZyU2)So2q zvz{_3e@g7S#7NR8S~9cYf3`qavbKXmSepm!JvE?GgVyD>s9pngJ6d*_Nq9JmK zAh7Qw_Ory!NsOE%h;pQC5OvMia|nz!L;{PH7;T6IM%y5yNbNxTAOTus!5o%;L$f&M zO5jmhh|X10akIiC_L9V2lh`{FtB}}t5<4cba}xVSVvs`7J&4Ej7D+71U_E74D4LDv zF{w60XSztNkHjdNjVSko#GaH`4!C|%cetKH$ODNzh|~k3@@xstl?BTr_PE3-jE!h; z6Q$UuqO8!0m<`tX-0%Ezn<4c6rak+RXhb4ALG+PCc55@ZseK}14dmR9`B3<-BX3%* z=Kr#M#7Bo4jKH*u|7}Y6x8>tK{L-Ru#eMyLgA&5-)Vp5KphOvfxj__`Akn(QHtn(o zsqgd1D;3}FvNj`E_3WqB*-b0_cUyOQN~ilD2&emg`>j27fBRV#&+fMttEx{$z<;bg z4CH7r>mbGp&$5L?4eePuQMi@eb`ayQ;4b$oZXL7^l-~AdAF@V!!Oi|tmDZ$Kl7>-O zEa5iDiHUIk(Q$Xo*qKUeiH};y#{OtcXFC_!o7v`Kw5~6q&bSiJx&PEJ!1UjBjqo57 zTLj-BT}GhsAhc6piOcrf(IfZ3>tVnW;xAHv;%X`G;4s7vpS01< zz_TGItXb+3rnX0fuh?I$9vs0D_?$(k1&tq-WMh)6gTDZ8F4|*pBUR2YhWuw#lKvJFhPs_O9gjs^jlsp+%St zgN`=1-+26<|7}!C?8#)_&k)W#(5|3_=e@D);X+DzQ}2cf3tPHCer|;zS>*MIpnhP2 zig?Ax)PS`=;9Gy^lr`EnN$n2fs}f}cYku0=tZj(CQwqQFz_m$`DEPou93Jy?^87 zM{vfB``8VQ=55tY`4br57-1CJr^=e_y^c@ZV8P0j^UQczg{&JOnnacHY-f!Y84)a* zIB{tfh8Cg3Dy_-p#ruSxucx2Ej|Z|doK@J&r^bh|%F1kC*8Pn2X_kE$u})8&v4%Bv z@^*P&mA{e3J7mUVcDt_~&^xN)9Kmiq3l^=@uI#9ccYz%MMjY5dP~w6=2R)_di4&vz zB|U#d&(kjB5b*1)>a4YY5cT&5WNC`r!A%b_YycR3)M>OhQvJZ0TsRQ(NiX+ zxx`wt#b}KAd_1)E;6oDqMwVQJx4)>pSWn?dg~Z5oKy>+aiEX763({aBxbGM1UA1Y& z-b>atJXZA8U&Z^&@3MG*wIhnJ-H^+8+keF4b3DE2Q=f_#FI%fUeTiGgw+sjngD{dH@kXHS?@;P>>aHJLsQIFfta8m_M7 z-v`GHjDcQ_?oWJup~-Lcu3t#}x6SFLde@V&NxENXasddR8jy_zh_TOD16y z;W|)xp4o0$Llb6F9tNR}@R}_^Ocr5*szQ20f6dgHU^ABD8)bnc5l-P_*r~<#5JwvS zdX%F)eRF8*T*DhClT+cYQ`C>ciI1re!n9Q?pq8%t#?9m9NU^w|-zQuRfK9+d_#*Gd z2i7e=5N!mhdFaD~WHA7d3wIyN`h8^$h@^2p!K(`o@Fag(#q+`g{FDryA%(U#cH8wm zzOm80S- zj9A#WVWP2RiQVKaT7+j=SXM$zXuEBV45FR1f{zE?g+5P5SjlZ`Oc2?((0X>?{4cf* zu=g_@h9Cqju$5I-uTbHLlxjNpwb}SeP2pcD_Ma;3>gTa9KC(s`Rp(KbeFw%7283{f z0ydK$aZnqtoi(V=yfT~nh*zy5D)#ig*?#`Cfax7XKlABq++AyGL}w&nK?j3z{6OHB zFcZ`OYGo%5XUDX0ARB<}V$dMaC7|RyeiCW5{FxU+D!%l0Xgo!N%pK+>{d5g2eJX+dtgEV7Vt$dp6O9_0dr%cMv5f)ggJv<8NT3ui8Q3sQ9bs$DE*P|s0XOBPJ9-MP^6*v^^~ti2QYM8g z`vgXYeF7_z*c?xmVbr3`9q|nnrMj{&cEiTbISX5Ysju3b^2pI@468}jBH6B2?H0pD zBm2XsJqCa1u%T28{n-y;RK!Et7JPpweUuLn+ipQ4OVrqrz=d=+O>b{ zUgqtx!W*iqRqpA^43gw1f>Sm`>0ZZEYMd=jGMrX z+d|d8+=i$zN=(2YvE4KJD*e^r>Pu|q7W+OHXVT_3FELc18|~F@aBwc<_2YQiK%<}1 zR~@NtQoW3CM^FYX<*Heuq2=+(7#)@Wbgt|<*q@Gf~GLUm{Vb!#R*QF3`Ldhcr zu!qkaI_G~+G!$s;ro!UC>2`b5R^J#O$4Lx-LptB3(WjBb%_MLh47vV#wNzNXaWCmO zgSy@yZ;=(-?LJAO?VWJWzM4-6O{$oX&#+8h>K1BwSarez zO<0^AHO|9xN;HlZCnekMfavu#KA2ar-G83#Y@|hmi#tYQri5N;0&m%$ooS?H#FJo@ zWIJ?Z^(1ba&o2gh>_WSDF(%EhjjYY4I|PdmDmn9xGWDU-LWP#p5yZ zS^4)uoTe_u^iwC!<38>w4p9p@qecq}za>Q&n&HSOZ)=g7jF(4WrMZ&+DHxS1bhH}2 z_ubZL_jsW!^sEtVK8GtC6@R$UpQm)kbh)k3+&5yYT5F+^AM+X}xfI3KkFj>Aa(gwk zfGzB)Sqy&}+4q53T<5nSQmh+{SO#AB`8L6idM|$%Wd7$~9$$j1NoN%FyCG$}0UDvziPo)bN{;C6Y{_ z`sxT@_U60xX{;nzo2Cw@a?Ok#^kUvtJ|RAAvtvYrVaZ6gdbHM)UAvH7$f`rM)_HC- zxS_mQof3#F%;?W;Hg~XIUf6Kfm8W28^yhAGBAXei#d*5Z+@MeK2sj9zIh%VzImR0&F=z-qikh4DE+#|fzq=s0Hrkm zkHp;9HRbn-yu+M@zw1veS=Ea-!vmqDk3k~JLS%R9;KWFcrR=)<6-8F>Ws zCMb>!IPZW$a_^*Nx&+h*lR)i}q+=GSALwjQ;31(iJ_Z%-H8&LBe8iBTPCp; z5_?^w$hY%W7HI*;TPaI@&k~Ek*oS~K6mMt;6#8ydc zjl{l|*kOs0VVeFB$TCgLg+ou7l`@I#lNed6iE{TOW`r@Az;gAJS;>>wPKmuIu~4wk zB!o9B5lA^G3Hv%ENv2^|lJt~GS%efdZB`cRDM&vh_LRgnNbE(4y(O`?CH9HL_DSrU zF%mo?!P62uE3w}t_J_poO3dKzo`oiQ%B)0DidkQ^hdSoWmNCPI!S{+^!r_1cl@#N0 zud7$uK}4=Bk1{3PBI`DYQwM`*#JPTMk>lzYlCU>mzU@)6?dr`7#^I#|9waB1)25ia;%&WN;qkbwjht8^~M(ld7wGmsjILjk| z?5yxBz+ahqTdA{2GqJ#&EFbeSeo2F2PiIzKVI3i(SqX{*xqgByaA>^D|I> z5Z|;ABe?=yB{d`M2xxoPjh7jTy;CHPWPHfE>Ny+}_CigN#?Baj8=+$h)i+M=pqIPR z-B;XoMx#)jvzZroB@u?1*z`#}Px}e6%i78FN6?k8c;{$GBRc2J*?qE$D&l40)leml zt-fWAh_P^Bib7Ux)DZ0zL_=rs)Q(adt4?3=G8O1mYrGs%dE>OLbLt}}7N53CmAu@3 z2oT(Hsm7LY$VcxFxFMd8T6Bf_FY?DB=RCm2KwH!@-bNsH8$o2{7Mey+ zMp&ptsEII3oDY9A;)%Iu^;66^un3(}oYNe#!sZqKTHEE8sPo z@NUvLg$dd)glKI?k&;;T0-IOpEBrFa@5RjWwZT@l*226m^ZE{9-`0&k2NLBA4-_Om zRg_k|Ha$<#_*e|>0e1wY3vAVoS#j!bEG0iLoB}9$`xu!8{g1mODXn%Hx z4pqF5b$b&MztNw2uhN-UtJ{k_E)Max&~%*2K8-Yqq+21~SI6Ihz}ANyYOD3ipa*aW zS$g_7L*t|;i`}o2mO0MSIOl;@f-VI;0!qL46QFN^o(8R8UmUV0wW&h33fT*wXFxCM zd75R)-vvDfYG7|BXfZ)F-nW2mOJcOKq6Tn+Sz>D?_M*gI zW$)h}U>S8l0+=GbBFTtEA21C%c1vP+B}NB`M7hR#$^`L-8|%QPEwEcshe=?hEHqDI z3nfO|AJNzfwrR&83!9J#iSuhtNSv$XrCN5vX|wdF;1Z%Ni8Ux?f;7sF^_AFAiH(rh zM2Ssdjgr93KPQ0=Y{sMZ6bBtq7p>C~be1%OJ`d|-M=JuQ)1*1 zNtE+NYJeyhDnW{EBQQFPA_}&VSh~deN{k;rXw9}IYw+-KEW4e$OiJdPFz2ysPiT~C zj9Rz@u8XSXO+h?w?#+*s(wwRB!U^!Yr@`%&CkM97*c-bJOH(I6}0>4If>|T^n4^2(igLEPpvInhpp?N z^=&g39{?$@#{q(?^B}LUcFXJCiUg9^Z}-$9{O_06A0t&ld{p68&OhPYImGseta?K~ zagLz0)$I2UT3A~gHLvFShFa(wssffiM*4>HG)OV1ACWBga6sk2K1$VEM_!g|SXCPP@w&0c zW9$g%itb<)e8QiP=w>m6izPDwTrauZzpStpXZ*o=CX=2cqT_1ZIv@YNlwKH(NYNX3 z9LMtTViT!1u?$2i%|oa?DA-DClnTUu`m1kCaJb&}`f3ldQ@y`_eYFP(K<>Z(Uuut; zeiU0>+8gptP^SPZWa+YBhF>NR06}cWWvfrZR9@^=T?_I!*K82Wa);wbVLCbiM-84( zjfO8+SQjk^7K9IV(KL4SWOj%e!7iW7UKFw0Z~=OewvY$A1@H%OJIn;zxd77sc9=f$ zkwr=qYPyu?I~ox)R2`x|3;VOCQaO@NMRDk&gU%@lWz1xwQR$B393zy^VeXZwwon}K z`M6e{i-_SV6Ydn-rcnLZWa%6BrpQ(+ce0`*Ma-qtrV-B3}>XysbWVWKq${(CZBrgWUx&6(1{T-AnUGM_M`yH%w5j;$~ znQ3wTBs4(pNoltj>ECLs@|jz9?a;k-z2R{QnwQb=%y7Ti1PR)MaBH%N$4nTKu>UB< zmvM&zJ&j?IQUC=^TV6)h^QkW#{zFY*7PL)7_kDy^0EY6WIZY^;Q;CBwss$ICWd zgP)zfX7gx6#Ngy{*-a}+8$c2nQZ6Du+FILW8->2BO{(o zoknIdT~{7qt9ob|&Bbb|)5whD7Z-YbgjT;(XDMq9l`5-grG>E<&t&&t({N^fOee#? z*Wh#Nzqo&~CcyFXbrz$(g~jS#{dMc)arH%&)|^n#2>hbj31>FQMZnM?UioF+ML=8S zM=aPoSdu~NBzRN^SKLaO9A0h`wn>ffBg|K};0?DMKf`QHt6M4KZd@p3@G`+)f?THJ z>RBjdmI|fJUapiGF^vB#nqsm456Y*<`O5eV%Er2}iKjXP`kKyos^{HnUM{~gN&z0z zc~*HYJ2aMzIB?2ceOq~mPXcMf>elIbmG?EKn3p=tr;m2wqC_#?8p(7Eq0V{ulpPNA zdTLF)NjCw}HrMHt!K4W~9fLnSJluiOchzaTk74oOLbY>OsCM>p)sDJ}6{Kt7h7WY@ z&XQq5yR)QDZ6jVOEUqvwg4L#LrRo`~8L5`Cap@XNfij>cnj>^Qnp-7QXkd=*E%;d< zAH$LZ_vw1R>#fC(Kh1|$w=bd%0R6djHpHdVpL-wW&mUV|{u4d#E>Em7{kij%y6c38 z`e=S$^r3;~XJH?$yXqkGg}*%%Cz-&&Uz2kWDC{boXFy@^;rvz4|Dor}lAg->phuq2 z5|}_?bLoUhkm3s(0%`({0QCojsf~i;8qNgJyP%yxaf;p9RpcE`4GFwIxhe@(U(SVk zelaNJmw{3Rm!5x8&#%_=PwV+tK!Z@8UOvz*I4eMdK|cfy0VNITuW08KC{z_(v-FGy zG(WWDq0eyAPl17wF$v{KWkh*W5#g)gq#q&WBS6=KCWAf?iepI12GDfSO`!ciUj!Wh z`V#11&@G@@pl^Ya+5Qet`muiCKw>l!)F3JDaO~eX4)k@<381?{i$JNzw7c2^>IB^f zN~)#LK%WBr59lUPe9fJEK@Wm{q34h2`BR`&?h+`@*ElbOI(|Un8WP7qYeB(%IB$Yt zv&2Y--V=ph zkl1dC(GwCk_*!CzB}UIl)IB4y8i|n}P}B{=Y#>ED%utcUC|c+#_)(BpH;FwUv3!XY zNUT(1OC`2VVml>V-B+3_4XEqy4m~Nc(Bg*g1*)BC&fCqeEAsT!5Z}FAa&sh!hC;Y=OiUN$fd^Z9vK)8r&zr1G3;n ziCvbM(Z}5xZ#{)mZW0TZSSN{flUR<#hD)p{T7q*VxI|*hnEE^{DATjx?(lnw9+xHm zlGsg&c|wIJ=F3-43HKBNBli?y$mE_vU|AA#No=LWJ{Ku^P+v(f8VWv9xtX3aDGMaF zNMgGswpU`eBz9L~8WbO*y`g%_q->Sg4y5!46^NbYFup=U^b~Z>NRd?4thCirCS^WS zqQMe9Wm1+)tXyKxO6&!Ry&_>^6lGv{j`%PjuC3Z(*A<)K& zPLrCDQYHmLEQ+&m)zQ3cfsc|iZ1|i)sVtlY>5*STO~8Qa*%C&wJb zKU{%~!w-7Fk`RY}c7r@<6nVJdo+A+DAmKm*o2hNMFhp5bdQ@3gpMa$o`Q3PW2^|Td z20tYXibQA;v(A$i5xwXG6=D_Y4Y!l}5IW4>&5ilBq%rh?sq4Ms8;HY^5o^0w8HL{I zI>aj66*|OR*Fn-D!ky$v_O7P&cAQ+6eS7}>LC3C^_AS;LvY29exRc#(Qkoh!xWJ0D^`yl!* zI-Y;tczn3%wkxZOdJVm2)>Wusmo27ti)+j08C>7>BT*GZjZR=nz^-9%E8?~HtdF@e zU>8m!Qlj)0_bLL#XsuL?db4kAS{ujJW!}2V^PsWpR|>x||5+M3JdT0(BIz1ph z^fuU>P(+Y=L#NSxPQ8K-Swti$@0d=*ErzU9jQAu*?8?8o_Z&{ZRKY*xDpzJSo;1|G zO!1H50(MQMDXu^)tZr`~l^$=)AO#8K}$sbt+}W~iN#de`ge zX${IDb~r~1_NRVOc`}0fghX2%xvDmmqz6JRb`Slz!1uAFBE*Lbf91w&$`TmSnv}EZ zR%I*X9VTPGJUe4zD~D=Ly{^EjGy>}79of-r?d+^stXHnqqwx-d7tT}`D~X6Sav83m z%_=L;)uJ5}j8-L9IS)IW=MmrK1!=F^?$w;D3xFyeJ=px3FFbsSB0uutNS`?T)h~a1x5!kW_Fy zZzlx-wBz>Sc3Qqf|t=??l%4}R{mpFbAu7$K_|p9x-$BLP)66>hB7*NOr94DU1*C`S1@O~ z*2I7#O`}I?&D3UW^^@a+*`^l@ih6jDvopWFWm zof)J*w^-({dii`l6?I}5GJ~Z*cm08U9>sN>@sESN_YYiNrx&B5{MCt-q9MJ)fYb0< z%~;cW*{zx17_Eo}-n2Jy&^LBEbkg)Dod)`ko+lMH<*^p{buwLo0k5+O=tH2iAd5k1 zJv{=NrC%pyJmqur{4h`|m#5=&1dH04B=QdD6dfo8or()&fdVm(^I6b0KsSOe0^JH) z3QDUD;vMHv&@xc+y+iqPdY+DIEsNtM3o6 zFyASILSm0f^ha6pFNxhWuvZJT=`owJ=){d*(NiXcL=ytr%eL*$Nny{Y4r$GU`3N+X zWp%Kpq(WMy7c0|KCX!g`7=&CrWm3MB*tg8N10s@tBydj_A~m~cox}u`f=eEWk#Im@ zSrW5JEMH=JQq)~8v1cUqENkBph6=Rf7lmlYFB+rWy})R5 zFEHBN3ye1R0;A2nz-V(XundW1O3Ws)T#2n0sdW0zNbs~QcvfOVu{Rbs8KI|43VDAN z*m|bDgC{)*A1wip)vV~r5sCdEv9l7pD6u~z_P4~q_37<-=_#B$l319;X#b46(e}Ef z1Utxroh8;=V*Mm$m)J0gO_x}a#2)#3%Rj>goIV)#_nv;&N_OqDJip*Tqe%-td+o38 z7PbGCyns`Vo1PmEPgv0I&&bDPy5AeWY4IiRhnj!*S#D_Hb3YzS>dlIO8+CL*sL$@e zim!i-zcDoF`4n~kQ;DA&-uQDzyDkU1$3$M6+P`&^XI7srtazm|dg;@t4{zDdzqZS>%yPNdSvZ$pr&)^IphVaA8f{>fA>HMt&T!d8?`V#mF{K_F12F=!3axb!EDL?A+JR zI%jRhua*C$Pnc0ylsI)_@DdLdkqQ*n-_th=ws3KU-Fq35`?Bf>@yY&}eOuf*-jCjb z_@`7K3VMmvL=BAaz~Y&~YBslz@%x%C;BWrEui$oPm#vBCL@a?~b|$n#j9(Pq8Cd?x zhv)MKsDl|_L$$OD1$HbP?8QEuhK5NKLv?+}m#$!YKftI%zyf^DHeQd2@uM$4 zt}7|84b5e-TML?adO(`7vBElC>#uepbw)Ik#_T;2Q}>*IkA_I0M<& zg<8CNy`rX2n_&n{QJOzIY2KW%)2BW>sdq1OAP38Kbq2&&CF7xS!|%yk5vcf z`LpN`2YIm_v8o?S+ng4}(vfYN)Uk7Nr`Wo`lrEi8x}|hW?%HW>QTOn$E*(2|;TOl5 zhN#Vy-`Y%{;x|NPhZnZWFkxq*JdKy_(*xM1nOd~x$LR2NX4{<^Z+Z`7cxNE?QG;3a zOs$#MEd0R#&S6zov!cEBNAcKWd^w)Qo^Bm&I*OUge-Otvr?DL` zo1;yiVcs5|hWvc?yG(Osk1pF6QiKa)G z!C$Yzu6`qn{XHw%`x{jCe??&haXp&(;661S@9@o3jWU+!^Ym8|Qn$4^LbH9(ZJCqykMfyM2=i-)?SX)yM!HwM!_Q^h* zxYp|jmR>carMKlctj~W|n7Y$q@gh!R_&IUEz-7HXyjxws`nW2tn)aYOxz&miTFb^2 zYt6iEwWPdP+1UNIIInYNRY~(_J8;1(&mTuiL)lyVZOy!YiBOgFXqD}J*=#XYLqxDJ zT2-dEXLtA8qRl6144qWvRu`4ce5h-*F9dGQx~j?}eOdNrwr0lLgTQA`j2e)B4HL0b zQOSDY{~T#{I;;9r^<8H)Y9;a!uDQ6{xOxU z?h6`iU}r$p_gHeKXDdUNkrkkLn{#*uvBpo2=Mr|4Sy!!po-a9(AF)~kk%a>|#jzF=&rKEO5&Ky$B~Sm{Y~OM}_W;?APk7tQQ}$h?+@WIwj|7vwAb zSiu*zmWEOOtl%`t)%dfTU(n3sjo9IfxE>Y2;;7t?0CxBas38#DL%Ej&nfg2OzX!6_ zRmd+5Vi}i_Z`v3ymW)_%eV$Qya}6m8F+OQR`VzF%OhCO zFyy0~!d(aGJ58BN_<%@u_%F~ckxU&9d~g)A5q>s`%^Zn*riJbO8~KYC7BmX^iM=s0~|v z6L?Ttb|xSBSK6|aF`g}ZlLyj{_)mXL0`cPobJhpx2t7Rr>85(xhV(=|U4-Ukw z1&@38Vw&&hcN!G&zI1WZ0ygeDOK^i~#kH~)HmE{|XX+>xyR2PMgKEW%F^puI!J}cF z!@Z_8=r%REQL`IV!S-I%qy|+8GLLT9eC>TDW)$1;gC(Frx7i!l`k@9@C|Dml3h}82 z4sTE`jAk@k#(Fla*4{Gn7N3sEiK(ewle@6h?elil<@9o~IVHIrM z^BcB)#1FJvEBG`k^Xkraj7W}d(2&kNm^qOpzce_%LA6qDM>XsLZ|<1XuyNVW+8$|8 zm-sr^@u;UcC3p6uS+hs?oiw?ic>0`J$|d&A%o#k?sNCp|o8 z_UJw{CPLD6);6gkc0xv9wL!N${9pj$X3y=}lZ|WUF|i?4pE=xXGK)Rp|6JXVETv_$ z77b~F@c}0n7(*LW%l6vfDJ*GG2d@UzI&-1(!wssymu1zwwt)?*r5=xM*eb#AYZMD~ zb_ioTIvTpwO=itykA}UmFqy>->S~RL*JM`NCm4I3f8AMq!qTuiAO5TDhpcLRO8tg{ zc6-2O^t0xI26gL_$0HK=py#r6}~wWs16w)U`;QX}Tf zDwr^TbW!p6=~E|+7MY<0^JCkiJ@dy&L$E2e*>o%V2$i zauM+*jx>^ohyhd{o*|mxO6pHA`Eq5Z!!1PZ!OYv#1k8xaN}`m2awjsW_oxPhaf)$I zx!((P-Id>j47&QkpbKZ;ssm!sm>+A>BRh_@->Zc=gh>*vq?ObzeE)wiL`prfJeS9B zT8h@ndnkIl2v>`6MecUp;hQ@8z=fh~aIA5kvG}%@bMG)>Hqjx;5*){ym*mQf3aB{l z%4`x)u?H?HoIREDv`5r{iqFgK2G{<8zMoSZ;>`1tmk+u-{~TI(-Z-~Nv6*sV z>UA|Ow|3zg^qD%!&h*HRcaY>Z7F}zC|MW+P@32Yd3yqADcGkOI-zaH&z3cUjlD5^m z{$EB(uQ8iFE7(N+p!@@GkTaTLh7_&} zD~AwVgWMB;#Y#t((j+B@RWHtJ#QjS|?k5~}OGY?LBhwBVCN%~TG|RYR?Z94p(bkLo zHP_aX#TIt*VOi!ppYR#HwK_wSoxH+iR+A9d)|Xm=m=bQ%e!iF%&-UDC?!z`Uw);^0 zBp*k6UbGR+f%@{pXba$`Pt;Hmc9t%BNo$v6UiN8qASQnaoZ$wk)43_i9FEWB^~kP8 z7(n&nrX*xaw3=UUuXaU%(hqRTu1z|-)>D8lp7^Wr~5 z7>SAJ2bYe#3_7fvJH_(+OFZDx;NiT*e=yB6VCA(?-no?HfqW9l$Vp6MI~=WA1lRkE z`LM{SNf<{>Rb!MUET|`3NIdjNmdWxfpNh6HZ5oe5QPdH#2@>W3t{4w`@cOsrfwM(>iv_b%u)^@&iU;XgZ$X7mD#4CEPg9`=6iSnU$!UH z7V0^c2F!jQmK7d3h>v}Oen(Zw;Q10fgbCznB24{)?OX>x6W_8ih43>`6hGeE%cpdZ zpSPLSq-I5uH2!mV-195-@e6Ri!ZwH6irARbT4Pq!xJNJx4YLI{qeI#B=QfiWuIJrm zHWe@%`VR^>T|L6D8~+*OThs;>hUx`}z$U~}l8v#nIC=WE5?d+SK;24i2 zJ%Nd!D3zyWL+$JarFNjW!7rHtNifToa9L&oPI)talD!_bTFmqf6TlEQ z?@PE=x~?-8tEw$Id#^tXx8K_%`z%S z0_1&AwDh3FV8EnfD<$@{#I{ImyTm?{*r#mT467yPPYIBhKyia`EDzDMNH*kBru%a_*zRbKP>^qenE-;-$?*Rx%8%MB=(oYaA;5O7LM!b<;aP% z=&FSco{2zyQ2@tO}gUxM5N_3;65Pnk7OQPzs1t+2jmo6td?ler#utQ9a8L zEBT{}m?|7O^`uD9SD1g2eLeGe%sQ;1^;s>#8;tQc6SJE%rUMBn_k{?31=`ATYexrh z8}7iMSqKcu-Im=zj378I%S1#7AMVZ!jykUD@gLxgBVfsE=t3&kz}jxeJz0iaxnE;A z11tB7q;m+__m*cNxaMx^H@H){9UCDssImCcy~D2jpz~ld!6O=v zgU4)}aC-78mVQ~YL`Rn$Fo2<)e*z}-FnCWV9uh$;!0Be*Uid7_NVkQzgjbIz=*}LE zmm`#?MRtaYo>XD02it@FsUK9H z3`=*4Ae@$~n(+USAsCHxmD~G`y;Hpuu#+t9JWkJ7dp$Q#1n5|Nr&P^!n4jmy12Z6QD z2e;K3w%;Sz%*M9RJQuHU7_3d05~sYvJ4GEG&v9W>O*YO?^89E_iCs5dufu#5iavUO zJkLd{iEaE{i)%4nB|b9`%vFWnb@G`ddJ~4mCc-THc^=i1|5oUk5C=v*ILPlv{;GvZ z_!>MZ#7pCOnr!-XlWz~VHMD4SE*RWQVI5D6NRJ8R34j1bAo|-JM)$Gk0M#b`IgNa_ zD9U#9mL@^itUpH(Z1Xo>gIDFPJhK)j9jv?naoF2A_9FZA$?OQLjb~D@KZxVj@pL<4 zU}*D0gP>_#%y%OjPkb=RlVcQ?hPDr6S z^aUH&8Po*Y6Vx9x0~B7ooY|l_b>%%|U4@ zTYw$|Z3#--1IiQ2O8K*To*n_^FX{O!dY)A4RGyYUU8iT+8k8PF8&EtM2aga+tY$j^ zw6NQQ!oG+XYzayoNdxTwIv5l@nUh3Uoj~c4bOxnI)dlng&~BicK`9_8Jt3ULcWwcF z0F+qWo}ha`soXaz-s9BJdA6cf>*nVTJPVyPV`tB{zcnBkeD08tQDoCD;Hi2FXCNh3 zBC&Fbt(F*dMbv#oVh1F4h}EIW8XV#X1|J|whU+Pl(o14}Bt~qoC`W8CrA&%L zVr3G0Nn)=_jM!jN_fv_T6Dju1b5{S@e`Eo2k}VoHgWV970${@QH==Z;N~8<(dl0JT?(}Id`;C7=lY|i=Hxpk#d*&NMZ*i_9d%)dZ1<09SNvd=b|HYpj~u?oDK+# z4zvrby~HvlHc(=D63b_|(dGW-5?Cz@y&$p85~H*3;s$iqUECD@4|HrV8#e_W@a{-J z1=}hLk+Hg{m>{t(5=&!GJa4tIfj6|)F=Xi|N-|lJj;;&rHFom?Y}CJ#z%g0qPl;WZ zm=R2{sOZfGUd*vL{->azIkM1jJ%wPO5_?c$ z^05u|bmaO$7@Pxf<*?zd?q!eqD03!BVeo8m4p=_|+=q?;ljGooyW}$XG5naNBzTmN z1TW98)rG-eo6~U+*fpq59(?G}HGWIcMMadR-gGp&e#R!kSX|}8*;YE% z3rBtPTEZ6~JlxsBn(Z+lG~E32xEI9CxGC<2Gp)E)S>bIJ%@n^WBEFG0Ar>u@ke2?4 zDGva}L&tkiQRvkk3+ldpzeC|hz(1;Y{eGsL+WDZ~^?FQswBm~&!hKO;Q3d`^P>Cwz zWb3S~I99RQ7DQ)Refs3{%PqjXMM*wO2#JZ05?)%@@iI2_z7xknkoV`&~_& zk*l#f5%lNkQ>b$ozr{50%H?R^&3+1YvlW*tdy|L@hZLDQ*4BYlt_HvE^}aQc#Kesp zt-t^d!baIgDMyeH>JafUoiG}RRI$f#aUn6C)V07)qcoBWj*FqV-M8JE-3cRHkGUE4 z8N9Kv5EA>t4pa;67!IHr2d$jzq`)@YA#ZhWB@XjzH^8!uf>b_1c@-z-*iOGLAzkS6 z4c>tBZq)Bjc*%Dll<$vSV49A*gVDbZ)Dv5S{wxEM(MaNtNFF>yX~o%VnxJhQ+rXv2 zedzTAi$lz=6LUD9KNw=MS8!Z}tD~8o$$KG$*>zIiIpC6mStYDc zhw{NDVq?o6CG>{{B^SQi$CR-2%MqmwTbd=QNdp{p9E^%hQA6w(pPQ+=? zVY@#@G~r%27yj4CFt*{nY_k{raP=V0zBKB06fmKzD9qMnDoqzw1(yTw0UeM0UQk+u z`#|S{eg;bN--DnI(9c0jK@Wj02mKP1egWTrz7C3kI%%*+K)(n54)hq?u-n?ofoV{V zA!`Ob4%!&>D5wRLs?qP^6lf;sRZwDut3YYwXFzE|p9Nh8dLDEY=ta==pud1_(DAoH zsov+H)Q%szk%>LgTj92xQSktg!56!go-*O(;>O6KCS5ft1rnPiu~LcA(-Y;Mk=V0q zD~F^w{*?LavW&OE-M(2*nJ7k|-j7s?4VIWyVq+yXL1K?f%q1}LsnjI2DvR#3aY*&ndB)S4CV~y72SYn8M6#%iXqR=sk{lwaqp}Avk z3~e2g>ftWgMNi>ii^Te~yL%wSogsnQY*SNPruhpAe9fu>uw0fKV$Sq*w>6NR*o%6_ z5}41v_;|c!)GHErLr<9$I?Fgk%qDU-5VVsrvgU=+?)l-t3oB5ld; z_~xu|QC2kjv^S2>_IqJu7t*S4h_bEZTw7ACjmPP>5=ETu8AK81c4e$BQGHzm<}Qk{ zc(Nsdwy=upv9`8|Z(h+X-nQ2h!qM}sZS_N&kHC|2|D8b^H%o1U(>gpDJbeo>S^VAf z1r}5k((dp6g~U3q74+(*x8eQQ?~n9K?fj?S^?H$Bl?qQq>0RfXl~tzNg4At%M7uiL zR(sMkJl)MU%bNtm-WeG7g+$hhXmS$kPqaLVJ(yujWHUEswW#L4T`> z0>v9 zmA(=d&-M3fU(E7hL##HRaJN`awD%eOMf8VVO)zm;iJn2IwL!$=rSJ4h%&$kM`1|7V zQYd(157oxZnypLF929{h0*9fD%5=Erz0GnoTauYRDbPcLlf&^ED8ZAc)vr1DBs9NI z@Q*Vs)`1)oEF|@p{zDkV4r?|`u$5;@)z0`WMN&VX z;RSmT=uj!Pz4ca(N~Zee?iB>d!y>bay+){lL^FA_#=1;#8dMYn!c22zinv z5xN4#rX^UYY;e6iIBxg|j(W%efN95HXhj#h?RynJ9p*pO);*6zS2)JcMOSx0(~&pe z$Ucn-PV@OE7oQ9Msl~^_Sq2>EK+t8#HwLA(3YU{y1q)e<^FYO>p|*ULdA~Cx z#6cgxK`3BGG)8wEQWD4r?2yE+NbH)#?n%sucM;XV>2y7XZ>Ge^qpfJKm&E7`1&p=3 zHr_wRAq$kr0xwDIHFgaVVPk%hz|XSK6^UJALlD_@6giR-x1vv)=u#JnrAchC#HQTODzq(j6Y2kIUbcl+6m7RZ<|P$tK6T%SNcSy9^zl=+s0i3y$UlK`=WS=OPAJgQ4QB{))&?A;7$~aKpb7kmW&x)@at8?&awcU%X#5WOc&*1tC0+fy<#HoTYV8E^bS329f z(H7hu0vq^6^P9Zfqfvhn<3O%*_`ZNme)k1zDqg};&Zoik@t@g#z4qz7e3^G14$EFZ z0%9f-E|Fff7yRu*-XuEg?(A?^6z^<)6+8WBwk7mDgiD>9R4jorj(6k>E@qDG^}79*ya zqQ(+iY>=oiMg2eP>@x@C=HAG?`QHC~zi;OEgSGdw>nU?)pS{+4maVTmucow-#ODEM?h(%Aev$VfFIIxEZv!ha5=P2v>fX$lf4-==^0O9nuv`pI#-&R zM^*C4`kZD(AthSyEvVbl#CqNsw^CLbGfawhb~Hi*{2=s^QuIQ@Q>#^SJOSJ{I+U4K zq%}<%Pt+){u0D1^yJI!VtM61%pA^=mdjB`* z95i%QByApi5lGYFJZ`T`Rokgp`8}}ZwWr%&nkv0kWlB@EQHI7=1wMt?4ILt3H1v9Y za?oa|)Q*NPk?L`cFIfm&l=8t+IbFa~sZB8Y;JSLU#v`65Kb7Fa9{!Z+7vheUAsIQU zVKuo9I}*#J^(pnmFUAzo^JCv{0=7e5m24xu{G?8r;Q*RS1Gys~CSlg>($P#eFSRsk z)yCCDscBDKwu1ml;`Or+$o`xHE($%b*+#RVw)#P{s8Zrumd<~?IXmZ!& z$$@n3%?x)hV&KLjLm8()$`3+b&&tMRx_W|044>Xo1-jb2!aza8Mjgv^PuL@nOq&qE4CJv?A{+;eZ@+4 z>D{h6L{eD%Zl{H8Y*i|Zpn;=T!JqIKE5p>5p6ps$ zHg!ZMTH=9g;!lr7EWnClAhA;>>HqO9Euv?%9@4<ks#K4jO2iOH zck)=s`jF#PLS#`m5=1T6O0JLqNfV&&@f65mkhmp^Mne)~+>BR))BFnQZpX`0(fg1y zAW64R`9qK|LDD-N<>{uU{MV3Fj~;}xA<24+@^tUcgM0uvACkTU3m}Prjp~tk-gL-N zNGu?W+CgGADMcNSz{X+`-Hpp3>84%*NwEo5Leky14iaf$JtP^UZh(A8EuVY#sCIo8Gar1@3M>St~gOPH+Vy+gK`vgOh^MRx8Ik zD_9B(C&Ia*3P78#ywabs*DX})Emi7K)B!$`4GOkH!FDRx=L&XK!G7i`GGP5h0e$eE z$vgCyQ=s}&ux1L@Rl(?8mQT8ug7sCfX$m$&!4@dk5~T8Z;~fgPQ>pl&f*n$@?-lHd zg3&HApEQ{s@cxK0pJNuRpbU|+O;mU`LL0m{^GYTvJR6~lf>k5MW9LD`=eI&GvJqA* zm9{9@+iZ1zywQ5TlhLS9v@(sUmQ$R|p3kF-?k+;9*G#GRXLNV@O1%L}z3B=zOTiZN z6!9*;s(|a1iksNi@8CZBLjj;!l}|u>Ib|axDA+0mTdQE-E7%nUn^?zmv{U62_|_F{ zzJjgbDZ0;AE8rHT;@b+gN5S?f*l`6rrC{GE*m(uJsbF^$>{kVQj1>EMXGXn36D{82 z87<-(ezUcqi54%lv+dbqw@)~%IM)2bOE>P!w7z$x?#%tWteOvv{(fKj_x-xYbzksW z_O8>mox2>5@>i`Z7yMRm3tpF&)acjvO52Tj1Hr7b3V{4u6&a4g2&88FZl28 zmOlIUIWMgGeB`;!GAmx;*c~H^>VEd_p4}~H*#GDoP}oy{*7m)2`oK@rzHLjle>e5i zfQ~P9iYi>UwY==9>Vp%_G^tn4&3V{xZ}8piz1urk=f)MbyWP?M<5OLWu1?%pIXk}O z`+L1xzcyr5jH-9;)lD1Udw<@OKYE?)ap?83_PxIz)M8uxlRtfHoA*ok@89STEFV3} zyWr=m(ccW-fAj2ZJfNgc6)hf%Q`f0)BE%CCOoVtJs`Nu`==Y00?<5Xp8Oy*7w{~2R zMonyHocNBd-ks4veb^3RU^v$u*#hFRDYR|#IxAa%1`^j@EH1=!&UF`J%xECyGJkDa zfDI`;>2u!kF!;P8Kuo9i(5Su9`%osjgBb5ghk&}wv1@=WE49=&Ve0oXV3K|5DVv@G z7CjwE>eOV{(_5msW522O>6YD-$780tBo8)Fqi<>%_v*>R5^WtK2ky}518ixPf~THv z+Uos|oU+boBr1IWAYgtieaLC>0D0&;i_hq~*dTGHT*DPFD z^{L4s7Zur3Dy(9Wdo|_1kws3k3zx=j6DytCD1c&gezaK-MsH`+4rbI>^?#=tf!v^X zpwZz3au;xFd+vli3MY_z(FWIiH$pTWWRAo=M?G#hT16n2dmJy|XG$ti^d5@hzbyH_ zOQ7bruMn{B0;iRuWy874RqSz^r9WUna+|&WX?I_VJnqYT2)gq#CyzUW6`zxdJgy&> zcOT%UA@aCy;DUV9g_Fk(!m{RoPc`zmKd`#F-ISBZ-NgJ~j~0bFAdib!uJv*1k2_;W z3sG?HfQjIT5}FC+{+u+)Oekk2l>09rlnY&vVkVTU1s57P zdBFH+CY0m+YGy*YzvRv{6UzM=rJ$Kmu2wu-W7+*o$~!^C>V@;aI6W)3>+@%`6w&{cOaAcL(P z*t5ijqHK*V2-RH-$N-Btk*b&1a4}E`oP*AeGyqipL&$vTwgU~FEqmJ$cb%Drt`*LV z0LMr&$0rM$#Zul(Luazq(&4Tl2LxSYs;8NTj?RUdhRy<%7dR0UAfW>K-%mrQ$0R{R z*U`(~g;{xKx)FZngUQY`x7O!V7r#PV6pBkI$H>_V@s5 zn9FdCsP;HURC^pFt00cGkVR zK8_IuAIFG-k7EZF>y3uCNrM z+r!Mmhm+udg}4FI%)@8m;e*|m#KVVsB*B8kt+CKC?_Oyxovi?L#+f2UKAs}ReG}%T zV8mC#>jf(q@s)5afpuM#?PD`h0j9FvL0x?O)+xXyr4{)N@Kz_-hdsOcu>0p=Ij?y~ z@6x$F*3WW>atd_N3PwwKD&=f^yxvR&gXO>6_6??sP4+3-ljXu@oG`esSN5g5`1xTS z%bNtqDcH0tSPORigu#b7_DOTGVG6pEwH}=l!MgNGYZOIvdAz-dl_sAC(dBW>Q^6W5 zSP1)WbdHZ7dEW9$J(Wrm6>KWI6baAm`tRypnDd%!7xvTsbQhaTO8Heb zgsATL=q(hiwSsk4uoULji5Axiz?4eFe8)R`L&3-@onuE8>~jS>t6*f6&g=c6U_^+= zF)KL*r+uEH#dVMZ5>Xzn*h0aGD34=Alt(E}l*h3_3PwbE93!GUUXO_KI7UQy93!GU zjuBB_3<}>;;GLT%tkoF{bv)oFws}9PgJoS#6?=5%XA_cZM%p z5HD)jiXoXq7{fpq<7T`uBy+sP8q;TZrf+?6f8~b4+@Fb@wHp8-4?_CD!F?_2k%Rkz z;TSZBv&oE15Y@)IBQiS>kxWiO<`yE75yxkSK}3wt^dT4b^zoTj>-?*uU`6P#2k)>V zL9AoEQernS&)tmv81{Z20{DN)kT*o(mx1^B=PSO}kT5KUSpGTN@WH$b3m^ z>bfj*v6}5)oN8A%)z%^LSTTqLJ31D(Z+*1*tV7rG;-7*miQ$F3nu-%#f}aHcD*Kg# z$pvI7;C8uBKGX>wlurVGVDUE&T@iN${3(9~e?99vhXpW{6@pI&AG_rQPwS|sQOCj7 z{Z($_>gSjKCa&f*_zz-yTe zt$ZB(ih#53;P-O|gUcg0XeWc65Dno>K5i=!w|I&}S@~<3%`H(mmVJks%x`6;J9r0T zuZ4O^-dW_{O8eOKA?Ig2<0V6I+{MB+Ouc&fr|@xAOQrOzhk1C7a!=6th~!Oy5A{RO z>+0>tqr4i`OMRgyzW)hTd_7bukaaUP-U->0E?Gn7u`1Iq;wT(}3*khnlRXIq;fhq> z08d>`LPkSrC2S^~mOT|tzPhYvS*phG2e^|a!Pj)Q+;Y_JACsX!7(Au%~Ml1>15WYVuF z{NHf>#z~adt;?l`+((&a{y@qHNe#%4wv*IAkEujUCxLuf`E)FpQ$7Ni#C?+H;zccA zky@I15`{%lo^#F&j*~KN(1jI9c{uT1)w{)YoCYU2eOd|qKdf zhhV`n-pqjiM7KOkuZvQh6!~PB4tyY8G)`zYPC7fSl}`Fd`5a6!m35hw8EzpKeZwxi zliGmgZOQN#gG)vyH(?dW(mOF-v#yrx)TJ~>)}dKfCsv-p|?@*7BR$ZzHRMLADj5-PtY=YNv(bZhtm$E6V9uT3=1f=(qAN}izp0B9*k72F_c zAi7|IkVG}t3^EQ9UW!F@mYP5k>vAyUi;(bHEGmM8J7&>L$S_FaybFgcmdj~dm)e&> zQh)TF&lFgU2YgiauFqT>wI2oa4Yd(Il2clN_INq=m4f}EV81g*PXsu@XmX_}Ii(dk zD_9Ei8krVhLwmHmr=2VZu|`;fjhQa&;3w%WtjosCMmE7%tMT$MR!EUS4v)kNZl;3H zkyBb>gMz)GU>_*hhin*ve%KP@4)5-%1&iL4*+Jsi;Rtc;P)aKdms2*vCLkfhXwGKuA)*N zT43bCn&%;y>x!!gc@iy)5mXQ4$&c3Irk4nLI@-!4JhM&%sm95309oSXSr2LAg zn7B5>QzF`FyBBq7X!5wndTze0H10D*JCBHH2NUV|0}eB%7?jD+(CpMDnw@4c&Cb9@ z#Ihre-1r|kcD|4}c4!IL8RR+=oAT60<7S!&c9xjtP?PM3NOt0%&UcaKMnpSVH%!Ng zDG|pGCe=nF>jULZHNKq)OvhpRukucEGxRTDXIUcKsmZkC{}oLx>jtsj;+K zoY00Amp)RBFkDj;L9r-Y#^oW3&u2+;_Ns^^=b#uWB&voJNzQ;Tq_*2YlJl8xN!YDi z0#YOk!WUu(AzC5HxgufQwxo)TWdmmX8P?Lr%26yy)raES1+MTe@>x8CRbQO z0{aL)Jc12fL0+LBPEj`J4B8`A;BQQIQI+-*1$?dO9g%KR-e3q0gZOZ~rIF|hw^phb z0-W>VSXdN2e_A5;6US=;yex*JT@+ek-IReiUd zr}ApVIO#G>J<%ha-i>J6jA&Zu(-toRR&3OxOuxo-i*`l!pHbvwpgbGpz1ga*8D4+R zl0(1YLr~`;B(d9EfyBB*VacJJnDX>%K;;#3{vjl>;`}P->0YKhy%SMAdMu5A)IpAf z41fe%4(t7Erhg$lL5UG(JmeP0DUj13=Rr<~r2B3LBzbpZ>$zx;oTuBG^7OVt3Buyd_l6Y+BE+ihDdDx@K z4D#HIEN;{yvQ7a7PEomB?Fw7`TV_n56T0k(W494paKFp#h$%XwqJ;$XG z6hCs&YB{A9h$x3wr>`m>=#qlbLV{zD6%0QqGUh6$Y$PTfUJtQ}a6+#q@SZ}CQD-hQW zrECP^y5Sgc-Ege0f)UpZ$7U!PaoqqTi>oDU(VVV6-HFr&2mn$aP)aKhsSU@jD42;F z-9{kw0hMY6BDJBEjX>%Hj=9Sz8^Mb;Dbf3MC$kq`iAZgDB_g%q*f0fqQNgAt7;)Y3 zdc<|Z+Y;9e$KGaZmuLBO{zd`LE0u04*c}BEv8Boz5*-fj%wNG8vWn$d9Sf%`0P)%I zO2lWw8xoxj$JQy>CI#E0VEYt|SZ;V*+Lz^RiRFf4#B#$iV!1(zbS*@4!$G3C;T4JI zhGP?9jKQ&~a*EU3aExefc)b-$z10dvEH}I!vE1-_dlYORQiU8Onj2n`Xl^+6je?z5 zFrvBP^@!$%*ZWn$9xIp&mh!xwhn&(1ChD7;g+IJ||NOWDiTb8+z?8!#?wbn@5|bx1 zT7KKaeKSby{Nc#0ou(hjczUv_x9xXF|E2R77YZ&;Zc zVR8Akch3*bcHTF8&kYmz&He@{-3Je|{oTZUbII@PYx~Oj&nVv9#qmc!`>>V9O_yK% zbZXeGE=g|(9=v>bs?D&holEwf!8b_yfcnO2ctPlp{?0G`r`$KzY?6iE`_Bk)8ce#& z)~$5^Qvw`ornS_2%Pcsye0_$vjK|{DEdLD)590_+eFxE&ejMoYG{IsOtMm!9*{C!y1qH2eI6?v+sc=v z$2YK4S@z&eF&=0=s|VuS^kEh2eSEds(fUwdaJu*7tuO9Pw$x1S2Q|uIraP14Tjxzw zm_HO-t?b|;J3s9?v~NEQTf?KI&b6CRd}$085@V(I+HELbG!|Rx6Qyzu?OLm*@Ue4o z>RRGOc+s?yyY{|XbSm`4w)Lq(K6Js|4whDD0Zf=FF#jDsK3Z!uh?yk_adTM3CL3Sv zDm;`{&cQ42LKc1})mKX!wv|f-L07^q20Hp`)A6)jiv+|S7tm3p1UqVIcNX*zT26yc>B`$Ng} zpg`4>ADGTbAE-iPLS29Fq}Y3?R3;!`06qz=dSs0mFnUan4z+L%U%a#BFjjirpk_gJ z^>uBD>E<69Z-}zcO|Wy)vTp|@#)H|Wtf!R=J5^U7OcB}-hI9~9S;QAP5!n4KaMsrs zlUYS@QZMli8&&K-P)uZH4TIj)((Ax-J5txNjK>y%s$_vVRj311%LF$4t~OA0U0?_A zAwL^|hdV&}s@Nq+5T}QCR0pY25n~Y58(+bKL}#&oxp&@N$H@2-sU1reN4!=sa@(h) zyChhho3uT7M9$sq*yowf4qUbfQaPzv8I7_<%@#kwsO>b&zY}`>oRwZd@gOU9kg}!B zu`>oqux1%QpwG{K-P^t=(4rhpaZ2hII>6g&~f^bqsUb zmW?dW3RHEsWBC_s0#%iEtnxZ0JI9`#xrTCY2j)-pN*&mlTaYS8Ob_)oIx_Jt@^>BC z;)}@7a$<2mB44jA82!T$ngnh_Hc?P_%@B$9*!X%;TEY6YM8WLDld&6>6QKC70O zc$44IT8(wERZ#63WVGs8yPF6{W2UDW*?)kUo@T6$RO)e29zD9^E@^ZQNgQ82FWup#|=@Qi_=1Qu6PCzUN4 z-@vKXo#s8$X}@7Z286YJEe< zKj5B`ddFH_b~7tYbyk|O$$5UFu{>2rtTasw9W8e6O+m8!T6QZHbEubf4lWXo_Ja## zLK?C^vk@GzpGPUJv>&%yaCIFJXzTKB`*UgAnq0jzch%h`*o*r2zBSlS$!6sfRd{8# z&Q4^*)AZfQOzU`>-dEKrl-(q`IF!{%hrAog8mH?+#lg(!puv+}OcyPUzDw)C18N$xCuoizh{1HZx}jr*@6by ze(d*jLmQ)dsJ=ov1HX*G5swXL?js>T3}-D!dPl%46K7x)@4Vuk-pN=xQoocszO*&b zmAw$0?PeS~O8=P@nsP!&b}Qr5G5X_DkBJj_kAq2~$GIfYEd@Io{l$eX@cs2?HKzEPy#B+?zRa0Y65G-W$T z_G`*clRVIr-J5|{gZLpVN^D|jY&}!|RJ3^2Nnn-Rk_^mhX@sMOHc)X#yfI7vmZcrp z;JW~zE9$`Vx9FYYizBKGrO3GIXMAD$K$hqDY(#tUop*;04Sx1f1(1j{CQHiC^OiAj`fL3hKY?$|}Ws4?E$qEDci zumgj$mlzL}>TgS49NXVReN5+5l95xMUdevVxa*9*x74fKx4hTpB++X&N%T7YTl6}bdJSN|P0%-F zYiH;hv5TLC+8YDD)3;D2TX0@~j2%UpI!;`v?_ogkokz+f>v8d#_&szfn+eAOM*|qJ$@cU67UKX}q zA%6bF(eA4cw{!U+x@|X~8xMzV+n)b%Y)hZ=x%-oPe9^0QdaSs0aj!9JJ$^X#Lg+ZL z=hw#3`wC|ajYw^}>S;;D2f}9u=3k4PwS*O12r66i=*6Vp<6mwjM(IYrF#I>0;DJti z@+@8*;^doj^Fo8e&D=FZeh!(l*6Xf&<2jSvPKavv=J(%edVfmd`Uyin+gYkRzU-4v z;!>J~KA8RRvlSU_;y1iLYhoLW4ad7n1aRn^cxg?D0S zx<%F(_p;OD8U+}?x}jfXW!)?yCL$)hWiwe76VWUxQc=a|ggcFg4gT>uyKvSJukF(r z&n)O-HgsX7`95410#KSbDb>J4*kIk#iswH_hacl z=`A&%Bj5T3rfcNrTW1>@W(*gE$LtK1ucdOQ5wLt2!yFxTzS=iYK4?6?`x98KuDxF! zJO=_M2*U4^*>=}F-#XJ!d=vi$vC1WOek`^_oTb$)=vq=ux|TZ74me4_{~EzJ3a(?{@7I>swFx~5?c$1sO8 zOm`Ow9ry(ZFMWmZ8DH}Xx(Db%2N#ByF0oHeWYt$)xF#uBu3cl{b=CD*`8k87>c?wb z%R)vv@BBSlmi7lktA8G2z{$b25qo;x;KvG@BqhLAEyEqwSh2t8DV)HcC|wTr_lbX2 z+v1c6T~K;lmyQNQNBsU-!v?V|yv+`?_9alY-iG;wCIza(W$jD3tbOSrYhQknwJ&pJ z?Te4BeJPW*FBY=)Ws|IZxhHF1X3N?auexjX$rO{qn1EFM- zY&UnofS#J0kG}fy)RnPYTykyuCxuu39Qy0M>sSyT4dBWb&j!q&j(bl7CX%Ypp&_+j z9sCn3`04Vo%{#7cym;-}`Ga+@hj#NTo%UT!r*}_ldlVqbNkisD=Or|d<&!)b$WA?l z@+B*XWzccn3}R<~gPhTbRrbL7@@vdS^@beX818<+otwZ(7Wp@tu!vsjK;MUWw$Kub zK6CKheG#$+{zxg)qnTMLGgf}vg94~_+t8I+-eYR@Vx6(edqAxkK#gHm%KR57Wt!G8 z{7;4pFe_z9mvL!O$NnsQLrTwDeWfD)@E%sH2J523YNu?JEb84+098r6##{X0))70HiJ6)VvDN{`; z!=jzrPhx8`TYbgW>Kez@h!)ULMcNr((HOQr#Z6>)Lk(KiCo8Oju_DxPTO^&$jlvPK zx~2_Ep8!|3UHf6WcM27YN)rOWX%Zm zGoVI!UzD2;wGt^C4AiNLkBC)d8Ju8LZMygl?Do8*Kx2Bg0a`uzGD36YX4K^xju8^~ zem@V*HgtsB!>XHMiltgQ0`}Voy;pqA$-atu)eolTWb~ZaA|V1Q`>-f=HG`u+D?fZztaG z+Aj@O#*#q>Q6GXI4J z=lVY3&#I{I)o3y$ob8Fvu5U-X*VMHdGKJ>jV=NkJP)SN6sH>K+l}Xti#?bMGwN`XI zXcNUhfp#jBkueO5w_vbJGlSOspOZdMsPxtmy`4 zOOqAGbRX;CAkm@fJ}y~<+qkPVS0fZ>#mezRQ|4y8zRr+G)XQ<3 z4c>H-p*0&*M|y5lzsU75Dpf$zGu>v5Zk?R)<3+|V|dr~JHkp%_VhX2ty{mX zfqnb;esFl_mxF&Dx^l&#pHAAg)%DlJW_rb*8~f4AA8!r0KjKnA)LDnC4Ktm0Z1W8K zuvgUkzbs99(QjhLt{$lcLtB3Q_I+o&3GbxkZyS8y&)06ts!O|5fppFfU#*U#(i zNguwy`WLThKkcYr_D1uN9({AqCu}}*>h7}ZM_(Jt>`xnXHPv%{IBP(?lW?P){# zKd9%TqFS`9p`O#-5WwGcGMr=k5*V)yv}uWtYAjG zrRHmcAZ(SDbc=5EvDADBC0%fL*5z21FLV3`YNL=|EY`P804dTg_X6iaKdz|jgEEZ) zKM3Iixr(o1PFqV&hk=4Hcc`RlW2IDM{0O)ijg)HCNmoPfHBt~xu!wIBekI?cc;f`P zLA@vyYl~345gW!0rb#M77Hj8fsd-X_U#02nB0RdJ#f)(}8181V(se$*Y%TRL0IIp= zFLS>p@_YK}WyJhmLV7kxX`yFby!gDNltJH%N z_3+MC{Gz{$uOvRRSVo694|=Lb?}2yQ2U0&y;poSCFSMdZcrC`x>}f95YJ5@<-a5sV za(zmhTWUN$#m4tpNv+4ip@q|go(JW{_q?BD7_Ht#oYxz?pIMT3l!eP(aB;Vc4ixWJ z)86Us{>|Duk7wPZOv<{x_*Ozs=fNgjFbp;8>HZEcsq>c%W<8x*PY2ID)!(S6Q(wnj z^>6Cwnv;vzG`3-R1OJq?aL0`a4?Vlm5=htiMxsa?JX>{Bgsc&%eR! zR7DCT?BOM|KC#_BYp<;_`%{_ysm%UV&;7i~Fo*j#hx@LkbTO;$WG5}NKUG}8kbl>I z?Efd#-IOP0)!ms%7X4VdqfhO8(kRL|SKYy#Du)%dvdLr%3$!+ra$q}Nw#Z_!?>2&W z(~oiESkaEADJ9!HKVfUnx6WbZH?uRC?OyIdAh8lT+e2M zw@li^Cr>QeTGQdrw8cL%l=NtPi=XNps!>kbAyNX+Cl7yP@u$O|sl9s`+tnZ|lpX2< zd-Owv$-UXFW?2brW5cYY*zuAyir;^eq%qD3%mV8%sn+tBgt%%xM;F-BuWXbxOVR+n z7{U*4IZ5a&-y;#>g32}b0zK9;d$M^>0}B+ydX8n2gS>FIR0-ks$oxySk9lK;WtF6o_(qj$qq zsPJ6VfSL2_I=*L85!qh2-AteM*(Z_utj`92RbG;PIZNEJbH%=!18*L=wSQ2VF^}ag z+VSlVV{^jJdLC=?-5)KNY?(KDeupVmvG1LKEj7()$>lH0{F5GT?Q(FV>)CgIf9&zf z$&Wn7ZvFv`GZUt+pC0Sjw9%3zHE z-v08fw;L7Qz3H=gX5Vjr8*|{MdSpoV$}g|BKX6;$@P++rz8uoK$8@WWrxHGH`_1-m zwl!N5c5H2C%z;>kUoQrJxpto4>2*KGWU3eUd9b_v zVrr;}MzQl}4GG$&9k~P7$mA*quItI%fold%@P-ubz}2veAo!+Zc-TD~W?GxE5Dk_}< zEzDF&6=ZYH0zS`Ep^ci(De_qOA}F&ea5TTa#GS4rzpboUaJikwN%BbNxn?m)@s>); z7cDV%#xE0u3#+(Kl^e?YtQUmw8@V#+9CX0t8=;xq3(P+DdyD z+OmD5-l?y7{1NnPM|kI@=={wi&?|k)9Pg#dsw&kNpbvx<5mERJMuiRV;IjFiJ8TW$ zjCjL;sHXB6CDYZ_tQXA6pT9%-)7C4` zto$)6fBt6W&&V%9wD@l-f1YvDS}H4fg7AzXB@fnnX68Jzla`q|4?3I0m*Z>azGG7I z*f9H7(#=j% z;9-$9V-Cjsjk8OXPC?Ummok+rk~}oDd5SJU)|Vl5c_M;$`6Eemsg2}aHi<-+4S1K5 zpVO4c>>7SfqocFeQooX)6L*?p@NEy!HhZRY5P9+F7fP{-BsF1d1<3(nUYlZHL4S`t6Z3jaADdac`tR!K*h6R zY#_;ia5kIdjBvI&0}a<@=lB_a&d6S(Xc*wEtI>YPNv z$3Vi@$2cV~JKOpnJa{ay0o(^Q>5D=i(l{z^TF8qv$~7>Yt?t3|2W)Elpq{Bc4l%kf zX1c6-4j8iZqMF(0QyNM>P<#4jt0lb^i2W9G=bY;9Pfan>uxdS>#YolNpPJMt4+yX3 z{=_mevYqKP?Hq-hB!DiV@9xprHJqA^W3wfvCZ~znl2cRI#O%qEl4sitoMf9w_6fGZJDJ(oGF#q$FvCHoAgF+M84nrjlJ<6PE)$~TJS=Pg^m7LG`FYC?UK43w)m(zXZB0|ZhXtq z@E7(hd-YQ>b6v``SI7R|vTry4$&=@P9us*yW#Zj2mR-XfgcE&|TSx8a*yrbn%w|n5 z&W`BWYNl?$)hAbax(@uM``%`|h9|7{SHI`fBY$z9TlFHRRi5m#W}Cx{zFY2>*;Y;u znHX=Ed@Uum=!<)U_kW~qH06u8_Sae1HE^`f2ubMeH{p%Z#q1&U+!p(0525NFLN+XPMT%K@WL6%Tl}FF# zU}#nz4PXds)}1*PMY^%lXjP1Lba-e~l$qhr%<%U-0e{TOBSf(J?^YfyIFoHw9{rul zBYP?6QU^7=RWHl7aOD5XYNKGj$q%)W+E{Wo`>LcYnOgb3Mr{-#&yUnbiyl0m+9>DO z=TRG#IdZkp%V+)fi1j=X@XmCA`5Mv<;KP|$-ZP7;?I;wE$Ze}QzBsiNr{93Bqb6C zkd#PKd;m#_)W$vMd6h`fUOCUJL~6oEkfJ+%sO@GuqG^d*W=s+mfAK8X+{n5}cu0Ogo3URjXJ9Nq5w0D`A*-?tZ z;rTU5ZQA8LhbD$Br=^o1el_m)XD`}Fx$iSSTNwQ+|&ZclSJw=mLl$!G#nxq5J zY5glrk}Wh%3XvYyB;kZfnxsg&j=shN**SlqO1jrG=Q&hK&86-nRg!dL6R%yZ^hw?$ zbDl$=)QpA+FnUhRd0u_e#G)MNle`Qk289P+-z0#r$#XOxdI6 z_tnmg!^8LOOP#ay=8}GFw+o5kdzBF&Wx>>W`Ba=sZpWNEx zkgrLfl+m~I*8}?1Jr{HCeB*u@oxA+_oA>lOKIij)GwG8$EolDh_O)yGnDj}XXxzrR zZhJIf;Dv+*sz2;EX&#&oOd45dkd4KIIt}*ju$vOpE_QzLS6_$vTp9A&uCGV>JlS;X zo6yN4P5Pwu(MeY>P1GEJ9(~fGIXTtzNhO!P-91`1Zyv39A=yfCq*6M;n1GGCsPd=}^+tNBv0|GT+P+Lw^=lFY&r7EBu>$4~)P!rt_p_1CO(mVFPY8VQDFRrp@ zw*c)iBu}+R%#v7Ez6NoqsOV^H$>N+qs}r$;@M)aDBBr$RSGmNqbby*o@q*ylkxf~g zQ(twfBWF*$i#DbIaqOc>OLE+v^`a4VPEFX6B{@;hdt~hL2@})Vx`QDh8VAHITH(Yh zW(L&6a87g<`) zwJoc>j?qW3@@p1Bq6O1k$DA0#8QnsyoA#ItY85*$@hc`_3h3RA?Z?Om&;G%futnA~QY{Kgt%^5@Zw4=ZmIikWPIazPKJmCi`X3=${L-_NulcFPUhH(eP8MJ?a&8qgYGUo_S?i?+o$|^ zUA277Huv13UlyP1H9=igfA`7Tw<}AhjkRlYwqe}|O(&c3Qw-Na`&|;w-*Bx+4ehi4 zg=w1>m;BP_(N3HW=@Sc`TtVoN{?A4;XGQBmEZNRqoLpJyMhhELQ1khxa()-9f|@Tn zorAro=zrLYdKb(yK~;NEPKmK#rh&2eQ0x`a$DSR$sCJjOrhbX%TT?p!AWLy4^A8IR zsB;WeA0`OGepaRn^3ysa`yRqCoJ?ZrMC#HK#AOM|f-u>@%Fj3Q)sBb7zdy*xST`2C z*~U)`Mw#o~Kv9z~u(DDgA1S_qW<`HQd>SB;>^OD6J<@l8AZ#AOrxVOXZ;iXA4|0Z~ zf9DR?h|m6P2dj=B?nO7j&PmIT7X>U~J=^wc$>x2Y6ZXumRtM~4MSPJHA=Xh-O;19U%@19W4kY2na)9o#`p>t+)mGY}`s*F8BXWRlcR4_}Q4Y{;*YNpvxR%NRx|_-Yx{n01 z9pB+5%8&zeUzG!NPs5JZFvLff<8zOY<8wR6@wqqf_}tI%{(HcBH=UkXc)EDXX*|4y zdU84gY1$BL6KdWNGjE6;G*iO-XOu7_CM4R`YEk{~?WIW^G}0u<{i0eJ95Uc$$4YNlFr>f zWNQoD&#;NV2HRTn_7Yh7o;=sW2H2$`4@MiIk(|;Bu}JA8tb?4=3cZo?m#}_vN-Gp1 zg*QgDohhfZ!WyLTb_i^PoYD&WkZK@d2j!F$&64&5lUxS`+Pb{k{#@F&CRgvwU3GT} zHevp~i(a)MC~9*DsapnP1CrUd?Cqr*70h1I=K88W2xf0-b3;`J9ohc$JWu1#+T4Aj z#LR&gL7@lRmzfu2yl9)-N7SFjM2k}$TPM3X%sYg1>tqjy`R^lf%L4E^R2PDOe|f5H zcJVK>4vh0Fj@M;mJ}ocK@|d3{W>_4`Ous=@W*&CAIS!3xJ`%^ZWaaj`Zb7ILd|~Ec zM~g!ae!?sE^T&&kCuZIg=XMEyIL0vhZGjyn40{39 z9d>Kb@)b+#xgDaOPP}`wf;l+kPAeQc^O424)&my>fAw;f?JG9(yNksgJzhHQu)Ex0 z_d$maT{8seLN0#Hl9OqpeYt8C9=DH05QMKTMKNq0_|B1s~BiQ4(bW+L%DY|DgLOrDD zuFwdPNYOo`5#o{Zmr^-M(S4~A#ww*{O6oeLSY@;HdWG19#J-^v_ZdQ<%T634gxQd+ zQSR2XDv|=#Q`IQ%gL1xeEeQRPdJ7{}?|-jUPA&N!cm0Lf*Nm=W^N|W@Ym}-uKz`)N z%5(DESYM}HkH!eciqB}#R)`Ri1f5t_B3OmbJf@Ys+%#=7y@B zRl(wC?7)-8o&l?ID0FD1LB~j)Ocw2gKp_)dzb2eyWuNu*FB#lk!Nl7-*Qb#k4Ieyf@-Hisk zFID!GCm_vprAw}_J3SuhcC;7bom`L#mhv}Pxl68VVXTzdCpGer4vs22;kveS8cRvU zlBCm44V|QXtjO>!`2r`f874|6#YJgXR~qDxr%#`zNc|p@`q9yCD2$O#HuW)0DjSQt zJiLq04p)J04jLdsD%d7C3!Xv@+MO^e; zkFZ`+r8OwB6M_YuFjR1tj({#5ue=4fQUjr*5F|_yeArXh+)T|fTTLBXHOp9==`F5h z3#aAk#QChUcPlS(5i4`c4P;|%^SoKt!dwme)h##I%GXxZ*s87A;;r55FbLb0C*N0xLq~WGTOz&GF35DWvB+hAFxQxfGIOimrgP#(6{4 zB0EUjutmO*YaspQd;>Y(P|nkBMCIX->mZxU`54Fz$kQFL5z+wpCM2Evw*;ZEXfP6Q z0~iX4JHBWPWEtd(kUJsgK)wt4GUP7EMUeQW6w!APA<>X$5};Yp7st6)aA{QWY#i!G_Y`RsbHrS z?3RMvQ?TC^?5Tpe<6*`xoR^%^3PHXK7_5K^3f7s;sM99GrdRnDQQjnk)L9G)tRL>$hG#gkq&a8phIt+@n>4FjbbAj zC$j!4{Yir42aRUoW$4~5{-F>Yr)I}><;qW|P)$0O_tDEk| z3IYh7Zb?1x-+MS~-D#Lx-zv&hY;9{}#oPjOqud*cb?`k1!DRagF<7~@5H4d9BI^^F zyCrF?G;Y;T`#d>60N0Fu>R+!>{zi>FezBy_0G9rwt2^5sn`vFo)HX#rax8D|2u0%|8$r^wq5Kp%KLfHc^0OclA!%8X z1X%)!*Oa1_kWC@iLgG#;+92mQ$@wjia5*d5Cg*oR!hfZRh%kW{79Bu>Iz9-Ac@-U% z^PfY~KxZMNAT2TXXh?buYYyoF*#goBlG-xZn)x)&9al(;6{3~25pv~}R=WK;Mk^Fb;g?3Kw_3ptD%cSPyQ5&0 z3PwNayg&Ly=lzB76g`sZ$DM;6m5NCUR-|ArDcDB}_OXJA7N$YS7mN4TLBSFg>?H-8 zi&P=6cvu0CDHZA0j!JC=KRKlp$`ovuf*n+_BMNp#!M;?mOA2;X!75E%+XxR7P{i*R zA6TtsrOR@?na7yC{8y_%cE+BeN@A{Mh3n&wVdwG3u=nquRmNeHtGfgBsc zLP(wtVf$0TgE}%K*H(21rWzOX+>A>@avO?l+VWg$s}VRtT;Hm<$qS*_X*s~o*x5Um z3=DH*2i!7!+4->CNeI}+{IBFC8#jdKwi7%5ipvXQF`OP+JyJ?5*x_n$j4nCH=<0H; znSvP-rTv_9-DL2Uc>et`FT)$9A5VE~IQ3J+itHUr3*FeUpe4Og@r8-6xBuC}+4MjY{@mhAofu1oS))#={A6n{wk0Yz z(blxZ;9F+qGo(@iKtl z13?(REO;HOq?TlO70eDy$qO)^iq73A)(dME9ucXl`b4y777-H>6W+2JlWfakA|skf zrG2zru$OVC@vy-!+bGF@x~ zOHJ`09P(f?Et7(&LAY?qOnO|FJ{l3o*Iw|KJeWkd?*c9)b&c zhI^VDD{hsW_77%c|L73ul!zDm^tdh^4Tg?*yjnXsv$F6u?`q7pWPYJZ&jb>aY{|OI zwq%vEE!iB|mdsnWB`bB{?vg6mm~5kLOmnK~3U6ZZJ!ewi+rLr~Icd|8EQ`wsABiWiP zUxy|3b6Ar-k*&!RWoxqY{?zWTt;zbx)@1M0r}}^GFWD}D>ixAf*~hXqnTKpm_MU7_ zX5Wz7|Fys5GTE9eM7AdTIFNlFpBt#s%hqJqWoxpcM%-W0M>Z!bkj=^9hjZIQIW{?~VhG0}cTJ zX@?-F2!hzL0E(TaB3Bf_7ESDmEzvuJCX&-cQ=F(NlJ=5lEJ=(eFUgZs(>3L#YSeUN z5=~WO`G3AMdjpc>B_!|t+W%IM@62arXJ=<;XLslO`F^Kx63e=C__!8PnS69IADwK} z@X;l|>1Q2u9S1wSsAJ2FuGDv|m$s-M`m8#AbTS{E><@c%_`hb=ImBUW8H>=zA#2JZ zdpKzryf0-LZ4a@vpX9sP6U$RVTn@~enK2bZY@Z;-qJ>PHNk-b7Af*#93YnC_2nn7U z&9YO-q+EiKmFFYH3x!MysjZVZ=FvhXe;dyXo+ReZpS&nzz$~`up`IhS32H{U=lB>;jk#jwkBd5cmImbZSlS*m zv6U|@O3E!UERBOgtgM2xXwa5*-N810VC%rFAHk*6;f)Cae!Za!2w-1rOo+{133Y%0 z|7fELJWSg2&8*4SkS6j82cjFW_5WABRu5+qo8EWeW${6Dzy0L(BgM$mjo#DyL$@Exu-&l zp7)&c6j*ZKSu^B9=!lm*@8a8M=v!@5FqJ%z@2 zz5``1<3f$aGt4>?D$P@ChO7Fe&}+H^K$rV&OCzC;>oqIKbG@coGuTs*!dZSr0l3!K zzidsPEKk1m!v6H1h|%|)rzYAHy2fS5dA0USwQfYuvx`ssdRcxom;72;qj+JSV5%iA z4W*_(Usn`?s)r`#DHo`Pl?y$_pwdNV9H{mgGaYGQud|+_h{cD0?O?1`sI7y`vtMh5 z0qK2t>HE>t$)`bJtbHaR9cA^|vnF57eVR8=Xz|HGwWoA7z0wb*pYa%?itBW>PZ_`f z{EjDkzVj@N$jg04kOtrXwSy83u`K;i-8i#nTy$undo&$0=LOWbSIJq=)1d^G`#_+AMt~PZvW4*G3m&Na)r~ z8%>p>N_Ze&6x2jpP8TSs`-P1M4qfxS`ASD9mRnIK{1oL3^s!M;IL6~{j#4gH`YH{| zb&6drQnsrFP$~%uV<68vQ=NrYyV zPK+)cza5ojx-w`@D!~bTS1D5p)o#i`*cB-ffAjPm{-&{TZ4N^{hPRJ z@I|1ES4M!_mca|vDl6%c#NZ}sodp`}(NIpe)eg3q<6od9VtJo-qp8zx$Dfz*^R~i6 zJd-~^YpQ^Vzl|&UI;TfwJ-{=(jHF;C7d>npF2A5-cr*VKC)*u-*IuAqBq{7erP$`aHXmOajH^}p9_Fq%#Nf+4ww9eEgp1k0h3{(65mG;>6oyo+T=*%D3e?mglNBXBPQ z$mHGvN%3u3{6Q^Fw>_oP9exeubC9bcX#}_y@=ZuOz$cJ&fX^Ymh&A z;%Bt@d8#L6pizP{7$I+f4AbHfTHFeG6Yz9M@Krc9gh6&i9B1XugQN-&HH0cal#g2> zCqv#2N%T~T)0;iTr)%-KD%y#Fg#hXysqazzD#$w_>E5RJM##G$$&3W8<)&Nke#qUB z4?vPJ#tz75Aa_E(0QnFk@l&Z{UqDjDjzgj~-NYQ(4XM!e--|#n&LDS=#%ZbQUX`WOt(qw4vFoR7!hfxjwaHA$OEHbT!RKD$UXu|oOi z=|h`$|0j^{{k9r_P`_sB(VZxrhHyUdlB|rw9o@6pw-r^23a^FSCz!rDzxcMI_fYz* zpf5nsd13OUQ*OrZL;T1T$Odw*Gq^RagzdKy-&kGQflKYJ!*TaxJCE;%Hp&8}JsydD z1;=;qTXs`oDo=ItPp6@HkF8X!Fxcv#M5}rw7v96w!~XG-?rUU4LH2+|>d*9cMaEn7 zrVc(8wzg_0%(oUO^(?HiI*r|SncW2Aor9}wQEYaA(-4d&9X7M}bukA9oc=Fy$jGcK zhDPQk@lwZvog9D{WAVOESCp*jgaMMGg!4RBT=_`;4T1YoY0a@&;ziluH;P_$XTVr! z+Fqxb-96nNVYtGq4`3(G+Y-Q5{$RR2(XbFNxcoI)w?tuc3}x{~y?Yxmk^6&mtnR#s ziF_YtBCA`)OZN7)BmDIN%Ug?=>=@!8Cqx8Sn}fTUg9n>~vABDJ*=#fiTUlbNEhVvl z*S{NDHxAdlqjIUzU)hG2`{VeC@W(pv7rhY25w9_nWv#Wfs~*F5Y<#Vqs>QvpvEpN| zF@0sT{2QC)-`XsF7uhvMweh|sA8D37Mr9d~m9}Y3Z_q^9rOkWj%bReU`Q0SB&05A7 zMRDVKN>3?wU&tiLQINQpZX0AWBy|~zS8DNU$RUW+Yw%FWxsdH37eb4<#BsNuI(C>)d$PWSMK4%vB??#S&X7u{w#}B(V(=yIW%S zNo<$I9+lX$B9w=kydc51WX5+T_KC#4kQl~9?Q{+7`I~H~UG!iJ!8JyM@CVy=NZRNT zLK{8U{CTRFsk&FdGMyf5D#l^jsGNIvsB!;h4>sjDBe?nh3IS8TMMQ744Iw0FqI52oz%GNso!Zrl~Ua>ku)*C_U#@y6SqT66rl znoE>7Jv*Lxzg_LY)BDb_sk6*icB@SJe&wtyM<0rO_0pBci*~oaC1i1B&;>IMGJN|D zqICN_JkFaErOgG}4nHsHe>qD64Vt}@V@om9M?XEu4?i>LBSD=edhv2Wou+4aAQGSd z`0Y7@I!)x_B2Jy&?YPZm(#4uUkM7pP+PF2+qt~xUjEc_g*`+5>Y-C42x#(hc^j-NF zKv9k72Z$;*VMkvwkbB-VI*^iF$lTADCkNuLSz3ti=^-pDE-b<1M0^WI6JwEJM|Z`A zT~?$h4?0<=7t0e&^rgC@8lE{Pa(Xn&YgJ+}wwna|))~xVu_go_LD}19W5$3rJU%SZ z^f)@#U2_zrY5}Xf!`4<;x#-_Xj~;vH@6e;8nd>fFk&hm|qgw|(^BbA?^-nd2f7rMb z6F-5hYzPRa2LrJzi#}DwazR6|6wSRRw)_M5xC{znNk_pGz9WboJTlrxkM_}{=U~Ai z-M^6@{p{91PmeDC5ra$~ezdiw?<_pzHehs((!!e&{-a(v%RBpM(>~gCgO4`-S7_5Q zE#gc19B@u%oSItND=iRf*cp4X9k#557PX;|F^&FOhQ&a3TPNlZR9n<`J_npW#x&gY zbk+50^rNa?6Cc~MPV_^^$JpTs+06?Id%`e^GQzpeeXW7L_d+0 zTH;&Qo(HxYs~bIV?yma0t660`CTD%X_IAk&byfQ))Ph3IoFCYh>3XNL_dkGxiFxVl zXOd5(v)F_1*x4hDd+cNz+jk3QXX}ds*zr=kiM3wQuL-+(_u~g`cME@>!T8*qPyRe( zcdz`=R;{{fkAvGA;n@$BZ#0s?5A$@50TX((v}8qQeA<7f1$A?lhu{i;%O{CPXdAupkGw4KQ3l9y(z zKjF!@mZ20B@+wh+_tk;1TdJEfB{Xpb9ZU&*#2mI}fj;z+LFdUbh zq-4*F>HF5q*3}iAnz9xZ2nl_pKo$CFtpYkO%SK%&k4T5M9ToKKTQvw338;Na-*C!8 z>}6D^806(ss|KBe%hJVrzPMRsmQY&^G}f!o0Ck0@;9Haw>qTb=r_)u1Pt^cVBn6UR z^o%=&HmNH*n~p1G9*#?^FFK2w!JAd?YoflP#orF9J*}%5l71d!XHbj>liStt>Kd2+ znu>G9*OZ)H6|kyQU1ihN?$v|h{4wf+uHh;BiO!H8FhBQnXwgZeJ@LgCbo|r$(3RkP zDY8U5{R+5b4Q)6Y5Og5)k&+8Mu>8yg<$^Ll)brA+aZ#Q(nC&B5oU8VEJ*r7Z9HJfZ z0quyta=@#kJJiBibm_AUwK}_<;Ee3aXfKi&+)^wM*sZfiEU=tALq7q zk=9P=>kBb8lZ=>AxfG!k9@>VGP}dXN$jUL|V729;cM(eDA)?|X@sK|{mnd&2LINZI zDO3<|*+k#y*w@um#uYQB{9~7ocG-*G)S$e-r%1^06nLnsmdL7ce zF1)Cm{ATH&BK;!8ep@K!nKchHkBpvWK!M8+0Mc zbg-tc2K~22^(xkv@U&e}(_}y;J`ZXbxb0#Gj#|Rm{h1vS1VOq~pApjAsh_UgtB%zL zDu;iIB#mi)Y?pCOe^)41r^4gcQDvDVNt=TLc@8HoOn0{2U6tI`sUNM{;q#?XB~tHv zB&*8AS<`3Dg{Dbx5kmPc-Srr72YEtY3Nk|lRKC|KQxrdSnX+E(h=Nm>X8sYEeNCev) zVTL$5=kKK4iHjbuuH=WJ6CEvDpC5|c)Izt9 zt(%_6-Bx}t&g11!bo1rnmmp2{L6N0kwz#yNR3(BZbX5bO zG`FLFy{p);lW@F?u7%C1UXM1}tYmUNc1Z{ad#EZ{-Nh*CbtO;hu-?K7a6jh4HC_*x zthJ+#71ox@(P$}j#)2fA1_!+XtZaXAWSpL#W&!7|&}ozFP6ao3D9F&9(CuehXx76U z45->V7k6?OZ}fBDNJN!&RlTDE_3m9}z0qv+YxHodK26;3C|3I8NdEwndy83@U`Fn9 z#c{faIE$q<`^SAH{>C8pd9yx*E$=uXoVhR90{su04gTg3f400;X@q))c;Qz6&N!0U zXe3g(=06v1h4bBTI3$W+-p;H~HJi9YUjGb!A#`kKerb$u`Bm(;YH6|2rGjZzo0 z6K8rvvBT$W{z3ZXPnz|CC{|;e>ke?Ayseq_RIa~1=*o_){?d{teejiQL?GnKTPP5% zs|{vhyGF*Sg+wZEYo;dslpiyM6U(F7_Umi`>GP-!mHu!Wm4<6HO?gal%dQ>78~8ma zV28kH;&K)?1iou#srF86a~dAy*b!GT%`1uwD_x9{4gY#HtvrhS3`cL#lNI_67aPK(D3L%oG5tLLXsT` z#mVI+#an4{qA*iD3i3M0ILNh-?I3Zs?#__cLw3>PG?-Gnw-%>?lHvnYw9^d;&`?Y} z(kM#_Wsro^P)T?dGX1k3piDIjNlT-KxbmYx#&|PUYM~ z<|>qtydrOeBs-5=A)kcAWpOuXaT=5<{-PGA!It80K-NRjh3dr|8@19Fkw{G-vSn%^lhT|XKlqQ* zD6vOJ&B< z5}Piu*%DhJu~ib=EU~*J_PoSilGsNQ`wStC@E&CJ_JB|=WKz;4)>&e?66-IqB@$aE zv4avjA~CDK_ZTT!$fR81lAv3HPf6@KiTMS1cMjA-CZ$?plO^^yiEWWs1s;r|dpord zmWa_pF02tF!A&yb?Go#a&n~fZKP_ZZhDoegVh)K_O6*dJ&6L;@i7lfLo-61e*GTYM znQ^1UZj;#k65A%RXC$^yVlewJ!(7TJ%q9iYy0{%d3G4_`arftzz>Xju0na!K9BaEm zSCjYB-0_$*3P1pJM&pDb!I(8&ht}1c#e`9gXRsAMwlyOH&x`aaACZOGA}kK0t{R=C zYwB$qR_UHkh^MdCjD>pvzMpi`4U)gbQ+T!_^pUYyy3j|kyQ|@&fS`9VO_SxRIm^R2 z<04kYSv*f;QfSq{)@sAgmQ`b0(;>Vhe|ZGInwiaypxGhxMXM^FcW|iCM^Hb0ByAGJ zoW%npFVsX_c-vFc{;X#oHPSP2bXJB0O#iGJk)W;`n9%I3nCrAHL#<#BoVLZ725a^T z5m{{iXF6JgqBb0)l z5tB9ESr2;5Byy}0p#&Zx=57)X(VU2thsd9JTOPUtAd`Fb@=pMZ3)X~IB+&f!o^%YTxm)8kV!O?Uu9L37gtsuWdX0A%+t;e`D5 zpKW2OsoIyj*uOizRsHa|_EUZWzj`2?1|PytkLiHo0&S-CV1~^eUd{;sWd|fV7DQI`XbwY)|RF_sj|ci zrES?>TeXP{4vazcIr{`JJ`|?(MUnkean3Xa!@r{G;L3#QlV?tvJePlxMkB80Inw!u zot2sgMyuWln0of^Ia{)NBRhA_7OQq;G3RX)n6s+4nH{d~?GLMg2hHI@Z1x2keX(sa zha1d(ty%sj10!^M6fE!EfqO0w)UPpvhj*?Ri}8e)8ekM5J)Il<-B+1)CjK4BhF`EH z8vT~1n!|$FH>)Q^>0_7su_FyB5xNAGWnMp_HQWETJ<#|y3>C~eGUwIGJ9$xgpLxqu zkFo65wuC_a@`Ys88xSMDHa9eWlLe|<*#7F?(SFOH$MP!_j4~RQe?#Ae>&}%#r(Bt& zQ;NeAQtCud(hwMU+en*qmL(k?IfZ_Pmi%!@QqbV!or%^ngfUw0Zac zNQ-+-6KH&-%^T0vaX0KS77eE~>d*9TM)$FSJ)yHtbm4=88yVnxf!h#lf^4rS?%N=T zK;Ev!AB60HIDJJ?I(0OP?}wy(2O)<-9)-lM%fF6ri}8=549JtfDgGTK#eahAOmEKA z1qtwj1mLDln*~X>1KE)D8cXp4NIFm{Bps+6G6!-YWLL-~T70P%U#7*^Yw>!>?#Q{8Hza+e<5XR zmY5%&9iq~KTF9hCN-RcV?IhL#p;X!h)*lk=B{P;vY_vbQvG2&-0rsCrowzs<3s7{* zU>is-$Y76>JdnZO4YX%s^{CRr_Nd*7Ci@DV4v)p8g$Y)%mej1rOSmCdA7rA*ZbB_K0D63Ab$FxyGI6EzIm)-+q|g}H}8!4pdRJ(?U(iq z>lTjBGvs5$Yii!WlEUmm%rr?w&+g>M&kPmD3}g*(6E|iE@;>!jE!)S+Zp;m5n-{b< z`c(yDjWA)*a5~Ix(!t$(c2*Z_RSI|SeQ>pxR4U3NliAmuGZRb=IOf!;7{#ZNxAz279&Vhz(_k|(gS&fI zqj0jeVR_#m+h6zgezvv!UwC^zxv(^bS)=VEeC7;)!JMIQtlekM;4^3VcbhXzwxV18 zS9*K*nJrA7>7`HQ`{i8LA|~3&^0;{|+M#S$+~gMR5Ee9cMvHc6xI59wcKkCwv}Jqw z%ohHN*+LKtxu(?T?Y;N7mifE(*)C}QYq=rIj?4Xm{sf2CXS*=4@LxJ_Kk`ZUnJpR& zdAIa=d!ILXZszYa?E#vwmZEnK=+PEQM9RVJI)rF%%v@v&se zaE-~95Z2Vg7^x66*X=Kxu!QFO++};u-C;qtYgD#*U^fGIxgJcDX{6H5BO7O*`w_Ry@@ddDt+!^*ewv7D0eY*&_pv@V`!EB-k3U=MSNF}11NCU zU7^`6c!rW^baww}dt28FxYDF%9^lDOffWL*1Uxkno}y^|iO}6Iz`UViB+LR1y6t zl$d@V<_!ZCWth-4hwe6(UZ{x{p3fKPU^_6SZeWk|D1Y&xFVavnKLyWg!bl*QmqS&} z%Nyy5TZLoPjXTTMl-s)-GGR-=UM;t$wIhuXIl8mK?R)6%y=aN&3r{7Ug4*YwFh+pi za;gm5x!2w+>ai~iYEKzz`r^FzP!4Zm1_ zUZ3KW@6u-JM346>mnR|Ardj$g=<#_tdUJX_Rf-CCLw>-0il_A@H+Q6{#bh+L>AFnl zZZCu0><+jOKcK3jes8iEuMD^=&k*6zL}hG9R-}IWHf=VZgEB?UClEYx$>H-wt~kS zoq#WA;~e(HR)6Pk7oJU&!NNYbwTV5%@lfr|yvMu+toik<`dNFVKk+f)Z-bpYQ5qSK zgJT=qq!fxLkeQq_o3@MDwQ!milGH>VyjqUX4cA z;pJ)|+@q7FK@xRJ&3!oW&go3=;`^lY%1|FBR)qqWdLq5?s6=8XW;t;!(DeMYqUQ6+ z8J?M}1w<9YSXq~0k;6%QN?N5rkQcli3wi`>Bh{Jccz2`YeTD8tMKiMprNA*_0=J+d2VQQ>C^{lTV;gBxtGu>^5UiOSdpYgc&CU=N~5@@z;~KaXu0 zYj2yiiD!8OKp47oZ%|ef`R;TPMGFwexrZ_tzrSJV(7b~$5Z=MBVA+-S1hd|~!|WGg z4hWEL4jUBa+TO0U@DXlw-!BLU8^5%fS)XzCNcNy(nPZ&A-I4Wl~KphC|+dIqNGSf?s`0c-fi*536bfK?c5=uW!^5+qVi zTBf_=TEstxybkg!NN@t&RPZ`TjM|(_{IeFvwczn!Na7Y-A#Z}DZoL7L)btx62SeTp z>43x&!{sKmGyzot;J*DRPJI~}+?QzSbjB2)4p|R5SBq0Ora1LjN+*3k#ods1Lau_m z3zA68_d-&SxgU}^$q$g8e=7nz0PKd`3HdlAaerTgd>HaI$X$?cLS6~^F(h$-X}rQH z$o2bsA;}~3Q;xjY8b1fw3G#W!JjfRy3m{)6>I3N&Ux6gf>;cGf$k!nq zkZ(fLjfu>t?CdPo9CWu~ts_N~g!PO!%fZi_03-H4qhitjD4eiEyc*aV5u>4<&jvhkqzCf+82 zJ7lIuB=(r#03VgWr;Qw7mi9e}!2R^z0t>W|Nhy}tNVXA9x7pJHeIi^NCF*sNZBibQ z*kf|<=OpIk0GpK0C3alqJ1a2-_m*e|vlhauyu><*5F2)tEjWifG>Y@jlNqWcHc4W$ zB{rW8`@xny_}56kYoy=fGT#aI6bf^B>AOKnAFv-p)&D4w_d0ElAms+R z?-q$|m)N%w`%z*&L0iDli2cw<1krtKv`~<;SYmr5_PE4|{UFYk*bk!8`A}aAtWXP? zlsbv6k=Ra&?LtUn5ZmznC3YUGg-ps+iA|T-B8gogu~iaVEwP&=c8kRBmDpB^?E$@2 zJMiNY+$XV@C3aY1A4`n$WQD(NP-QLL<#q~pxlM)}+~qcropLY7Ckl;@xtHS;lCN!YoPDB#9Br3_duo~D zF?X2)Z`;^U_pAH))eO3LKaJ=bNiV$$)}AU12;FVYDkE0w>%!&slsZGNUnz^%-18dS zIMv=2%V@ne)gGs-9M8U*YR@DYKh55LtXAFLI7ji~PcM8a_!*T3{7vQ|dVGlwi8BaU zIi{%I4+$eei5#PkkR%?;L`dVJ-j5*eBfRU0jXoEMEkYQsj&fVGbmF3V-DFcfqUtr> zzbO~BKhh^ROaBEI6>rJ3jDS=J-v6%0Zx7xo`{3}!YO>^= znwaIE_U?XoJHUI$x?@=Ao%^)LBMV!w_C|bU%aBO_XE_wg@~7J))eKfK-JYxtXGbcE z#LJRe%r;K9C-~_Xk-zEeu0;v{m-9W0>|}~9MxDpqP3sm2ztjQrDt9s6lVz4ph-Paa znc%PYb8iv3w{Z)?)@*a>gg||w8L#hWi{sR`?9dE*qQz+r?r9DlZVn!A4$d+McO=i$ z9b-nQ7R8DN&QfOMMQIO~YtaGepvDNgruW^fiBIaJVqVY2_pk|C+{?CHz$`yFqLRJj zOuPZ{&6~VUizA)wTt6XlB%b?x^SI%7IM8xp#$Z3rTTm$gVI~b(3Kf@jrV& z691E^lT-j1%~1g~;m`|`c%^xeGqm)}wRC#V%m=;_vH)@|IN3w}#LuU$Jb4fj;k@ zeg+dA_#gNkLq*o!rG3)^w5#K4y!3!^<@d^sN9=1ZA6upm*aoC%AEu%P9b;nHK^ z$Wi98tOfQO{VwGywmj3;gMAa)BS4+a$~Lu2X8X5dS&eCB~pi=N+P6*>S!V)d*eLT`32XX<2Uv^s&!G20VZ=~0lK-yM@1 zv3^Qt3)|azWCE=*P{@iF*)8fSR{xl@HM{-cK4v2|w#C4bKC(OVXCwR*?&f)r`1Exz zhdcv$Eu^2SxUYx2fH*#NsYTtJNkBdV2`0Pyah9;y-iswgrbXnWVibr49;9^CLU1`R zv0)OMEwTA5>P&8Y4n5+@}$fP_ev8N>Vs>EJrUoQr~J1`WdoQ*#@Loq;zB8s#S zEYc)4Sz2|yS@sTnswO$P*O)WOJ+C`?=FR~puX|$n z8)=Tm{7?Cx-fLTCd?%yb%z#@j>yWgo4_bh4zvTEP&FJ0VZ*@K{U{jKB&;V_R>YK;0 zq%TK>GYcrZ+>dz>&5+Q0)Mcj^vYp?@fZbb@W>hWgn_F$kW@_(1BpyBJSI+&I>-zY! zqzjpCgBpNhPbK84P*%Sw3ho7_1{u}7-LBi<4}k3-teV-^L(~A4m>8eNVwTxA1$4{q zmE9|=XEz@kxMoHpp>5qmJ*Tsc%YTOr9K{~L(q8Oi1OEj!@cS!$Y~bNOHt@fj4XpS0 z*uWE;4rpNLI+=A_-{F2laFF{Unwvo*%5m&8qMjFt6FYnfx-bw>-Px9EJ(&4#h6JZBKg+%Vk7 z2KKRm#}1e!oQp4LQ9tsrfvx09`1i=CWq%t!m33a$rDe`;eQe;d17}_IOg(8ZPGS28 zgg19U?(<|`@|*7M)lV}fv2U(VZdu>+v4Jn{dG*utuV9uT`W%*bnO|Is#z<<)Q5O-b zC(fENW7f>EoLQa!zs-|5TXcty4gBZ5m$R`V;SW-Tv)EHl*zaNcCSVqI-gmZ$YcPGiXp_O$=Wlwn#?^j}eipG3O1t4>0wIb}GOcV(KG*8EZZ9%cA-P8p73 zE1$Ake7s>FZ#ao`gFfD{k2egBX|J$Lp~#}m%OCOahW~f+hO?n-{=>XsypO!Au;37T zS56)lYFr`@Q%I191(w0`BGcl7h&(KSOV}+TaKK9}VFBFB#=Qc7Si%B0N*dNUkZVMD>FSNRLw;76`4Mjna)d0C)LU!Qq|l_ zVhIv!D>32+i}DI2M*Lubjg;7>B19~dnG#$gGcJ=D(TK&)L?fndq8JNouf&LAEU*tGMigUU)O;r-NREX?Msh4nA(Ij3y}E80wg}Nz=)45FybQ%jQGd`BRaCc zh>k2Uq9Y58=*R*iIuBU1>zfr*bSK;k2djKoJ481az>Mto#}?U5MKkp)I{ zWRZ{P$O0oevcQOrEU;#DWY?eKBmev8$gV%dNB&cEODm@?y>a!^Ot-YaHdbE@p@&E5FB4-p@J8ZP=!+-OSATmc5ma ztqdQEKDIKwo%-0yxXXTZ;- zA9Fl6!bOZ{kwT1Tk((IL0wcz=z=-iIuo{WID6v;1Mnq_lkBHD>Um`*ajEK+zBO)|H zG*l52T7Z%Xy>QHohjL?EH)leh9TUFuu?Lf9Of4BQ!0>$c!Kas9_vVNLYt)`0#czc^ zytB{3w>Dfqsp89Py+r6GpB3$2()((nCiAV42yIX=fRCEH}*^MtLm*NZ?O8o{gRE3;}moHu+7>2ZA}=alyCYd%J+R(-UIps zc6e>D!FV1(>wau@d{_d@`(ThkH?|+Ea;LRrYmeCt{xkbwwZ%Nxkev*PXVyc5494hu z7%Amz1t5#D3xI12*s){wwz|&?m~~Rz&^*!~J*2>DAKQT5V_Uv*G-o!SZ%~7ydnTXon@~=&)y3Z8Y_alCl%GM2pOwh3-km@d$Rp^(dE7G%SWbZ&v zN2~tHo8#=OEL{p-EGk^TE_>sNSzBM7J7R$U2TQgMn_2PoHjJ6e+1{i2G+me<+et^c z4{Pw9$5HzkS<)bs`Ucza5t1+QXAKnH!7PJukO5fv?_=b;Hh`Uk?3jXnYSq6GA9`%< z-0tVEi`(M3MSb(Fg1x&Rn2Dv<;O#K@1w?f&v!-!9}u>50)t6{93((edk`NcTJ{;gOU<@>M|Di6F}IIE}l zyl^aLf%r4w>?Fm*BiK5M-xtA_pFsROSXzw6{)Lh30L4FuWXma@)fzny@msMN#y61t zqS%~cz~7EyC%;2{b~FncgScM|+eva`3{wg3+=dVpr}NGGuAURUh7xmasnjj!p28lEjkERHcgZkIPZm(Ti%6I8&KfiZ0c-6I&#~Hsk_0P9n z`Dp%q5jOLrp;;%t%RK$fXW{VJ(3WxL@`bi6iO%|Vn4?nH`XQP2zcu|^Hv8M7`|jEP z$z6v({q$f%tIskWi943P-)qprubIZei0ASY7DlZS(T-J;)7+oR z4xE9Umc~wwMROV3vpM4-?d{o-agblNhdU9!)Px@~8d&h7trCGn7?4sB{tmytcVN|5 z`}b$1fny(P5mSV->8jp{ZyEVFDXpYses_J$@KEf1a7l?}5D~ z`iK_YIk-eEbBEo~JFR6ql=(QqK92DGKg9OsT{x>+AK9WV?BfV`D=Qc|v^nJ$jak#8 zcJy(CnPt4m*`hY{afAgKw|wF3%&D_yGxbzKA-jEPKvaub(Z>-ct0K2L%%6=iIa>A- z3$v_gEo$tY+ry?Z*Y4geqqXy)d2Mlg8O!_F+Oit{1(wTRVR8)v8;*z8+uVe|myp4BJl>*@}2aj_%41Y<0XNQ&(kSkHFk@Q zBZJsoZIO!d-)!qxF|uN5=Xb(;FYM!;`TG3JchU}gH{*(`+f3mXLRXyLx#ja8@9X>d z@ExrRmQFrY`qNvL+xp(qYI<5}+`$#jgeM+CBk`?qrim6 z)sbSFj;W{|6G-_ALEeAj$lS1BXNlzUFFVG@n`V$jS3BR+r9w;u_4Ag^rJ0-HJ&)OQ; zr*=m_-LeiW)#1ogSF&=4V>nF$-QjRd@AA`aQ_p9-w62|Z5@^P==Q5m|Pb?eq?H#kO ze_`(|RG4qSKFQ-p6EVi7ejV8{r{lP!7IDXlk=?pxb(P$r5I&)kT*Wrqqua0(*99Bc zNq&qeylp4?Je(U&^OeTGUoPT`< zc5{{ES5{#6rw)geD_H(DNm+k*4r#WJkmMsI`3OmWk&qNP6>~_vHA2#>+9c8?+9Z-e zn?$MrA!)iJwd2R_Iq|98O-G!6`};NZWA4+x7i<2oeeRYsI;#H-xQT_vp9LTbz=TNwM7$k2QCSn%w{d@2U2@m!=#q%deedaQ<(Mj;Ej#5 zD%q)R+qI+!=9{9L%)lD-O;I&G_UqZ9E$@52DJtI-mG%?~%N;c} zyXBTRe{qV6?ft}I=-KoC?kp9bjN@LRXD%EP+@d>F&f`%dSz5H~Um#XtZmfQHX?%xP zD$AM~Wn^o=j}K%gk_Q)1=w&v0s5OYYRa_fCIFTJr8Qh93I-MRoddh#4g%u2k7`01N zlu@2>=4C_K2RAuVl4;Sr3pG|xkwsq^t$($w^mBYsDV{>C(j7Orc1DU?^D;|X?}%3i zu>KG(nuDUv%Ku{h(2ipIkGNwr4S0!$TQzwB>4I5?2_u9GHQcJnp-sHZwE{*TezaNn zUo4Qy)1{X=_WpWDWC&G)4o1^)3oPvXdPjz8XIZy9BH52mII{w2wVfdR6s8XDk;pc$ z$7(%&w>bO*=?J+zVcqIJ9WXKGu(NhIJFZs)*_?`^NdG)aQ~cN0J0f(!6WDB>GfHP3 z!?Mphq73>@W&>Myt~i=yg_j!qJM*l@-DMjbI+d*%X%A@QbzN1(lNy&gn}*4%XZwV+ zpc}HyQ>gBza6VMONm|A-MRC*IodePe>4fYAITn(RL-A}aj`ay?p=!P!8s#5@_zcJi zkTW5vO=cWW|B)5?d^>rL4m(j{Uj~9oaYMI<;k+pRX|M z7Fk&Ot&Wjl)NnYSTo!#3t2*WA8B84-3r?ogexoiL!QQylk(To7-MS_P-MSdr>S|IJ zKg&W+J7V?Yo*u%xHk&@SGQed;>9jTN*VMP?a)%Hi%-$GV8KYZhVPBngB(SL49ntD|>ewafEY@())}Hm-;xO`V zoJS=yZ#RD1ZxPnx>d)4#M=z#Hcif4rzG_sYzqp~a{w-=j{X0^fs&((N)V-tioy~fd z-C;tU+KqjHB@fxkddIOgJ$Xqu4yIhl4aO8LJ{gM_{gBt1?d{dgz>aTqny1iYO!ICH z%uQ{#9MTH87LvO2I!Nlq6z`$MF>%gUXGT}&`O)>UOr+wT3VAc+0>}*t+f)ws58du` zOmfXaKY*nmI@UZbWKwD+cD2N|N^FP3-jdk65<4TY^AZa|rxeF%rG-pNcM&q+z`a@A zT^JI{mzc;kSLV7_V%JOTeu-_9*mDvii*72#q~xG0i1K=9A(Jv!VhbcjKDUsM?YYa* zEoiKWpjK7v>;*@=A)ZQ0dZqcNK{Sq7YdUyZ=e~zURR=|<4~p;<8*&?JT8D0b5euA} zFDz*PIr1pA&zLKn&!IzZby$Gx?IWlMJUr)>D`X__^W9bY@&yY z`^67&y53lsET?C16YX+unRxhyt_0V`tyMo@YKfiW<$RcjTY*w3=Y$>aY5Cf zvRC3)L-T@sTRwjJw3#gK9tRApuPibI(+wz&dWF)B)tBZZGV6u}6MJ$)pAh!tJDnrg z{*!%8tawp>V}JSVP?o^qi4NDJU)X$J>QrrIH_;L>=;q78(@Jl7JiQl;rjx}TIFJMV z*_^_n$XJZh*ur`4R>)n-CY#&z6_iMZvm$IF`x`nf2hb(3nwFBLLbn9QC<-?QHO>L6{NqBciTC=Y! zBwb62)6*HZpqthM90Ez#p&umGu|MQ^NMP=XT70&aPPgMA;42`pAG=KNOk|_(bws$x zNSV0kM6ziWZ9qAdCg7u1PEcz2zdvO%4y-4dWT0G@v^`}<8z9=sJ zymkj(6B7DJLvG=z`TAAneuHX{sf)kDfd(sd$ih_>bf1x zP95HgA8qtBqZbakJ4vp^uW5Q1jS@Un7zHd%Mbo4)LCu!Na zJ*atq@ea5tc=IA`3)J48#0Tn-RekT!TzbHqyV0aZt5#}E`P)e!cJYd zaDfI9MOnr6N7y36M)8O!G8&oZv6HKZMa5E{e4doY%ZZ}Wc|4Sb;RQ2(GLMAFk)w*o z6EKE_U^H0%Y`buOtJ%+B)*Hpjkom0aye+|Bj3)+$m(3VMmKkm(PUaNFs=e8fhl>;R z!R~8W(vgyAo%>2weQ!!z<_dI1u(uv9#iEzqp2VQW7qk2a0P073*)>l&li0i4@mf1V6^mg9DWzJ- zgaxI&7!4;$*u$jIAVQ%ag$5CUZIjr85_>~pZ%gbmiG3+CGw%K*;X=hCf;fm(3k4}D z5*s415fYm%vH23CVFv}Gf;UTyh8=<3E3t^V61p`*9FxvxWZQ4a-^KKs7f zH2IxJ7TmNweBblClGeYJ7F;-f=K8$x+fUniUa@-WJ$1NHeEW6c2+=*7TL%Tr!44{Z zcRSjN1w8qZp!E+qrkm+)o05jt{rytz=!WHmI#bN_7JzH4tk^dGA$IbSvS>Ar?dZ}j z*-YawZTFR4k;U@kQltHdz){G~zLXlX`@@IvHPD=+(le`zl{WH!BRDEstDAFFM&~th zRGu&39F@SEaFp5saDv3L!%7kvr>L~W@b%9joTB1~YprZ$J6k!EIYVXpFsy4jTrgB- zuPZPZZyk;ok`avzmHZKK!2zQZWgDlCk2<2w7dgoZ3yo~gDykg6=^y78 zCQUv%iqE9UXVUanO`6ghKu5_|&OSDZkB!pcGnn$RQA&HILFrR#?9F!AvJzUPdh)SRd~B58U%k_}4rg3z2Ydi06v4M-yc@~9c+|dIbHsLprdpO9Nn@-%Kv-lC@GOe^}F*P?isCK zyujx$HrqU)51ajsBZ-xC7`(;1%;z%ZC>|9$+8!&E0{>{w*Q194xWREp2DnG zR~lTHJ5;Rr(Y6WdieN~>$oI`EEo_CPEkBdeC{^UEg>1UN2kAoBf%T6N%5Rqb8BfRD zIJT4c)$5^??b=LBNt0q!iFNp0Zed$b!fnw?HfKpvJAIlmiZytO!&%(74wLF;)vt6) z_PYu6h_S5tN#`2&0bswr*t^5UT8%t5_B%&w@K%!c7f1S!N5SB&F!e03TCUhfQr1~V zjDDzBuU{hc`t81V97}>#7vH>-|I4+wck=%lR{xl@HGA0-XP!cpZ?4%dQ-o%p2q<|- zht}Q=TQg2rDbeD{!{ehNiLkO5lJYNs#6-WFT6!iVHHt=9*}d!mXD2lW&cQW)h(QaP zl>QPMAhFXDJI8wbU4bv;skQFrpk@49B`;hh@09_72HS$ZYX zyL$A_MFAvtyaxMsTC@xdNLV9^*8sE7-}`iX+*fpB+YTN1KddWk*}ZI~rTO&>mwj zmU#PFx#(wCpG7~j@$8LCr>nHMx1U|ds$;uHvU7|3n5R(9oA)!!bGxbO&_8)UTMJ43 z3w_324oPv+MN-@Wc?0C-kd&X);0%)P_?scAMKlJgWHgt;jZn>T$p(!H?}2<1`~n>YO)o-VtD z4s3^{R`3tL`9zk1&Bh-iDI%6wSm-^DL{_Le!x{+^)Xs`>rBc9_k4T7OC*~)GsL||5 zdQwayMZ&%itcU!tXk-lH*_T33s|Syb^p^!;R1HeQs-93w{gOAqJ1lavLK&oD9nSN1 zQOw{?tUXi`=}*^IQF<~Jw1}`tJR(>Yq^2Fs(sa&5|Ix&j2sE*i<9o-d6&hV)$uH>= zPyp~vj&Wj4+kaCZDCv<)?1q7?#rN`#0l@tgz?$&5!O_O-;ml~@*ZOjJye(p3wYlm{fX zQ({j@tU+S$NbCcI*eZi_ncAB9`#GHx=;jDTv2+orch%#EYou85qpf2`Vl!eXGU(aCA_&GCm%LEZ&x5emOtz#JvO$cCj1vtNHqA<^FaIR*ZK1>N< zxh7|47xffkj?w{Xy9-y+tNNw`Z9zN_avLP|i0zQnlPOM5L5dTjg5uR$d?Msd#Aie9 zf+T(+slpdPJ_<=sOG;l(3v@hy!1Vy23wPfJ`47mgkX#l1#qQ%l&K0WbSmKziKaMkR5Z0*E?aXh{?YB0SkdE`x*Ts@x^ z`vpdNogG3m*j$}yruPUHzmM151~+3^oP%*yCJp-pFFxKKI#7bSDwFO|f@3D#JD#gD z{i^V^PhxXgcS&ShL!AMJ9re?=i96Ln@7 z`o|BYLY;}LsC{^QVRzFEO`)W;|Iy@&KGS;H4YlTF8=tsRR8+>k1v z(Y$}czf+^xDZ=T~X!=(C@U8gq-@f8UMx67HXfzKV8U3dI!q4jh( zV5p7K!kZC}Mi_5!+#2JLn&F4~6rVm%k*vX|`1C10eTvV?!YX~o<$ghb!WEEIFWT83EmZ=2)=R1kyN=umE$FzvU z^xvZD+-(@-d|AhK-0f_&{>%8s$ok%&-S%kmKp6^Pp?9?@ZW1k-+9#6j7>2p<{^y37 zN82!^M^ljh8Kr0XZz(;;tTE&XttSs~t>+!XovHs3t*4saIF(8hd$st{Ia~3gt*M?N z2JveAnx_8^`!`vv`uDY-?JexZ5l*S~Y_E*`HLYi&;uKoXGeu65Umwiw{zB`yq}WMX zPj0P0o+{zfdj3-D+0nvcM>&7PTz+sAX+4{DH?H>lV3afSKd<(T?9|xzf^hw=!H*P2 zR{UtILb`VvKMEmIIW|pyf~T7)6KULksrKxkS?lWE)-$eI0WQ_CIbf08B z@6~in8k(4=)PA9^iuQ~}@v*_ML%-r-Hs`c0T+QbSRkk)L z)!?GuKtI}=Dvd~k`7nM>)2X)w@UcrMm7t5cD7_PuMh304P^8`8EWHltO-DKy?6c%n z?ZQH6_MUe5JAP@JLUqMhO`#e>9ha1dFm{4l2ez(XM(g#dy+Zu{p^Q^X*kw~nVuCTf zhHa;kkKg%SaqFkGbr)bMx@R92{Dt9V?`3m{{45$6XKrtMHzU=z+IV(0QmZ+W;X z9xs7Pc&IRKuOelwo-LnYkJC+5*nz6vt=azl#Q{3^R<`}VlBhsG_YG!!0NJ~8%hi8u z?GO+`Gm$g{+0^)5+NMf!GfuzPFZEh80e@JSZe8?Lh3n0NF;PFjboK$g0Pj%O803Dom zxCfF7+6qZ(UnumHA#9r7*^QLGga4?~cZ}-QvpTepaM~htuglnrZ?unRkyXz2iNB+{ zWn-(H@q@{mh1iw6Sy0HNWJ;`y#D+<%SYi%|RkAq-XM9dSy|=IeEo4%PB{ouGQzbT? zeO~3fLT$q)S39rKZR*HQR>RT3sSFl20dhbl%O<%wla)a*34Wsr-j|$fImM?adzdZTAI6FjUR2eB2rbGg!mT32SAc4_SaOi zq;jRMM(ML5cR{X%B-JcEs7N(S8cWJ|FXRJ|Pe48i`2sa4`l9=v0Jv&)&hB+Bit-7q&}_hWaAkA7lwSbkjR*stR+N!qsfvmPCWJR6ud$#3`a+*K7g9^d|mY8GnF z=BnACo3J-}nDWfurm^gzNk zotm|aZQ73hM=Gc}h|?q#vZqi%HSqq1e^W_1=(J_KylBgIWh+Uts9Uz)iMRrNZfoo_ zJ8jvkG*BrkEZRly(FuyQ>YnWoRPXm;4(&#TOH3j9bqM>V?n{CjMwW7gWaxou zgq0@Nj>Yxrd_lU)|EMPFxr}G~w(Fnm+dkX3{a@d={dU|x+P8f!u*bi=Z(H(v(VuCe z_IDh+tvkloHqyNz!NQX782T-+%)l2qT*0T-SXB)E5He~<#(}vGB zWdD&c`XAYlT{rjtvm3HT@|>AcvfbwYm0eka0_vK)wC`$~#C_=hXh+t&d*ij_-L6L_ zg}qe1C1ox4YP+O4R*-~ds{9|_q^nX22ZY|%0lJG8adriT9I4sCZuH;~t) zunr}K;o~1~Tkys!(@No+sN!LO{7b|dwX<64c+i~PJFBH)DO;*vj9*MGRWGf;LRC$g ze>={{z~N5AH{6@LuEU<}i>!$n>6#-|z>S(kx~O9nO~D+j6n7H4vR-C5io?gN=lVZy z&5o_!)VBp^LDu+&Dd6xpBHwhH~R{V^w32Hmy(9h*9e#fzVPssDM^Y*!l0Qf{Uc*G_Z37FQa?x^5Qd6}CFEIB!w`v79fIQIl13bJdK+ zKcSpD9FXnvn(t6R&hKG>BA=Ao)K%rwM_36D0oN$$kfn@4(@n*U5-)};wEGU)13J4g zHQAVeI4KUBQsSo6xW^S$M|jhlo1VI5>zj`H=Un;3er-s$v%S z>Np0FrQT{jTB&2r?OLgA6|L0rTT-u=N)ZmQmqZ2cruw+@cG5H_MO2P$@j5jQb%Apz z8?Y|mJiz9F^8wqcX5Kzxb4NgI8Hrm=**+@)5?~e}wvfbSrjIM4=5#T2da^QV?380; zD(8eW_k+15`w5zRg$?=a%9lK?c zz0`Soi+^^$CWi0SX0Ad*n#9k}=fffPEmQW#zSm;s|49k8l~6+UwCn5fmclS~$MmP| z&MK%}N4S~eresVf?EgJSnmaHKf!)%ZrL*K;4(3AsF%ZwyFu zT*s{=sTF;EwtHpz_{}yQXxFaPDwNwh)!i)&4w0tEuck*mxTTgWe)aY&!B?){{>gFm_5$i~C#8&EnqU@Pz5RkP>*&tylrrw^grUAf;qUkI zbqO}P2Z(#LDdl1J80z{;PhVXEdW7>cdIizi-Kn9tI(w;lb@nWZ-P`U@S7$>LcmjED zP4zY}5k40^>hgxtR&ysOn?&JqTjc^$;{N!~GgsI3Xj=(@8v!|%n*cE~5oXRGHm3n@ z2J8pOJI@0FdFOc;An!bnRQa6b4KQ<9yRgwI8ZYlWb3rOQ&xD=lgVfNJ>TBGECc#z2 zDD9zCQ)+PLIS1i_iW#Ke9JGfH8eg!hIP_F8gB0bURd&!`a?r9Jv>guGZU=26sv!Jh zrSta`6e4+-iXnCm+F=Lnn1goCLA&6f-FMI)IB518omD8>s%ilw0r2**?D2J5)cm(EPho)0;YU!)`rqyt~eGMbj5|J<}UQQqzi5r5A2IY@*lFf-5Z?b=a2%x6pX< zu6*rUV%uH$E83M`l2PB!zk0pu%AP&n3!N_BEFkAP zdTq4(t;7)h4#Z~?-ZzhTi!f9G-Z&i(AEi^=QbUA(7VrnqWnO9uq1@BWT=au_qek~$ z5)~JrKL)g2KT5ml8Da=TYEAud&}sm!SZxe3lt*zd9)LzML~OeoeuKY$XeH+1bi1%( z2xUDg=i*i-Lz1qIlxV~q&tStC*jP9U1Des4`<5|;!nWY;^rq-o{l|*n4Z~q!>R4!J zk5w!jy_V*pkH_32dpsQ_Q;30&JNTjHoqR!9FwR50F8!h;t(vG@KW+E{7H(wX+UYEs z_cnsaEr%D^`(;Z~#AFJq(>p?6I9ZZzPNsyg#-XKPS2IPDzMdlPzBZJD**FzfT~AdM z&Ak_Txag~akU5QVkpt#NsBd3meC{_BHu0-MBNEF8>W`pGJwKm@eUTcW*DOL$zK9Ap z=t2xGu-*se0ZWv~zX2x?A;MlKy3HBLx6J>7LFwFAm;B&~+~Bw0(_e$S{W3}$TsH(~ zwj{}hYBu-{<{Nkn@Z1XM!mnHsj-olS5;N4*v^&@wVrUN5^KYZn*3di-c_~~DSc~A^ z6?`MVbyFL{>e40%<%<=A4K4JT?n27#&!cvS5$%Q_N>cTmWZkC;F|5EC`&CG^`uk}) z^ISf|B;<3&V_%f{m!Cry{|IHQ2u2-1z!#3%Z)wC@$JN#4rmM`hSO80j?=^vH?6uzmAJ1t}CTh1^jz3N8C^% zfSi&h+(35SB=4{|WC`+q$4yBpyiF_2VIc-~-|Qbf{G_<&SG=E*gs8 zs0&_VD&yjI5QHGTh6<*JhP#c`YoysQFxSZOUkSn?H@Ls)8EoW67wxW*mbim}X6cS1 zItFuPDky9gN*U*xxw!2%YNVD}H%ffl7`!A<@8b4~NhAH?siE_4;?DQ1Q?QchrIEUN zD^`qr;_Pj2jpXTzP=n0D#$E^{sEkIMSVjaAZ0w9sJ_NxN*P#`VDcHCkgtz=PQu{y+ z-D*-H*l-jQa|%-5C4$atlz-?xKqkxu0S_Wr3Py%knw8)Dt(j zNv)`9OZk0*V zO4Iz-jlx}hz^Y1Xzex?(w4luuqSqPFyrdQ=IgLae{?J8fTS;WT?HZ+BgkIwZLzMQa zMESR1o{C!26fi_f=K(caDK6PHTH97f>9AgjAIS*Q?OGbtW4d?sq|e$WFPR_z>h0n0 z9v<1IiQ6|XY-}~G+YcLK(Oc2(3%Y2n*_AePDC=A?wm_(j^c3C-OspH(ML@d$IF{ z3wOJmeP`*^Omt(HLuuuGcvCHmvf14yVeGDVtEdI%EBE_!ll%8ekHvm6A#lp}7A;=# zocisu=#V8otI*>&52x_o(xN+rv-S6Ge7m96YYS#P%9UEx?51tt_ptD@8^#9Fdc&Q4 z|LcdWe!hT1R&}GO;81PHb^m?U>;ARsb^l!|()?eM3->D0c?)uJW+jTZx<={mq7}Z` z@Y&7$&u-?=e|AB|vkNMoorijM9_rb7sCw=-<6>*ns2f*@?p${XERk|IZd@2sA_vcI z=6`lG|I;sYd3GM^KYbo5<=51*C2Fw$_phbEBROMBl%#?ebiFCXJGf+LTMIVn(`j^% zsL@OM;3dJrU^zL@`@5R5CNIUSNw9_zB3oJL*2a|9H1C(-(u|d&(EQe=7C)-vNjK+t zRi%^+?Q>)`%LbZTR&GdPeSO@>W|rsDE43q@TjCm%LNDo?lqyk5=D%Ne2#Ix?X6ZhO zc^Tp9W=-CT^p<`a%Ka(Ejb1DxccnOcXn6xwrO)#Vl#_H6xVWb&b5I}_-LYc#Gv-c42`g#>Gf3gU?g?526~p!Hz_4%# zdref#AX$Lj7qoO0Gf4cd^RI&Tl8PCmg}{CjG*U5x^qvOWM86B-HV_#zNM8WMY9sj&uVMyiIkWH|_h+&zPc)bfu%?wPoq~FnNS_ zdMsTJlS8z}s*xdFj@4SsR6ks9Ty_Mnis2iM&qwMcR`qOgG&fuxsC75x{T43Q)M!I1 zQ(Pn*w}_z>h6`dSgW=5>nina@YSQRqHZCho-$u$`>tBRlFPLe>t%Qn9`>wp4#C}z& zAn(&=hF0b~jg`%9hFnLm=8L!GmETq@W7)U9TLRmR&zY~O#(a1FlhW`_#pldV6MPe> zO3r_YJZ>P8C`go1Q8sJR>E()Yc`Q?|sVHyZI9e*n6LB7?eNDL>-K!)gYoe)PWjWH^ z21&`nmxgaBL&~Tu*Mh^hE6df{;pxh9R|~n)K1PmkNt%vSBch@nfG%=HwJ*Riq)H>WialKqx zK&6-#Cr9a3{J9V0AUYB!o9u-1ak9*%+&ErN*G6zD2O}a}uNk&y90_AH|EAQSJvC5s z+T$r}*`nlp2*+!9O}T=ebA70pj+}O{Lf@W~eM|XCM)QM*j~+cwzt5Hf=s``{o5S*~ zC1;A-Ik&b_J2x>z?L5K|wewbOgpoiYb!1+XZdgaIhk_kj2gGPvT}KWt+o)KImDE%C z^C4kW*gDkRjg#BEUw_)Qs7YBp7a@X;t}8d!a-+zouLL`fA%e|ih+vP_M+hnOsJ^oLenu;Q~iYigt zVrFb2ieGgq_naKWc~bW|d8V!{=Rd*_`OWZ4eB8q9`9Gp6463Oku-W*W`8xz(X=S+W zVX>c&HXxSDC(5zGoMp-{yWfy8qXyX2<$n_@SOprMh&&!i>k{R_vNezrR_C+sI7w+xb_bvrR?S zqFYVnWn9FwlToA(E7OK#*zH5#B+C(K_Dh<{jkLqf6h7Z4jMAFP?koxhHIu{H|Jlvt zc{v?__#5SMsjR%OWMg(HPvZaJ2@0!jRFn;nCl^=5&16d<-snZ`C=1HR1k&@Ra_CP7z)&)3!RXN&YXz-t{naTxY5DlL$^B1 z?Gd5IU6j1Gc9DP9Bynx@Q@vowWzrO%-MhB6U{GsgU0}`dIrEp+qQ%`1Tqlud^x_wC zSz7Up>_T_D$%iz!CB3^G;K7lYfUhsUp_Fw}wnp^n+YfcZXx@Rk_c9VbdT55IBZ!Xp zRWD6p0g?WC-XBNWr}U6(i*~&;6$LfEE}dogNnQFQ6@hnA0$+PnE<=qv%dIJ_vxlD2 zy2~}`j4Tg~nSub6Ah^0eK#=96o@UNHd(61i^Os5KagBWF_79PsG|wWpi)N?xxJ9rp z_}PmwSuwJiGQzr))o%v@kqx)X-a3)9As}$OW;x5_50|A6JvEFq}}6^aB9T)TF2Zh;9cZx`PJDhn=F^ zW1t)l37a!et`U)h7%Sa~B=OE4=R3n;_=2R;-YyzS|ID>KcQ5g+xt=(>7)0$)efea2 zJk@x=Qp|ixH9uFH%)vGM^?WP>Wwd;d+)&Sn`6dIAm+R02hHL9k&Mfso{D(2;XRYEDQK!nK2L_%LkSnOyjcRl-j>; zz?E)|Q|{=@MF&20wA>0|7L1l7(s>J(%V>%!6zl?(hvodU^WQDT@5AF3{J2boWKl-9 z+f{4K;!_~Vj?QD`RqmV}_g_NtdsZR;iGZuCP+f-Fs?>cVvZD!2n<&r4X0czU+(=V7 zuWP3KlC~MAj3pzd)GiGy=<(-l6=^Qjv~x#zsyDeG+vU@R}o z?xiau;_9f)Xy#6^GClvY9GPja#4|Yw_}Me0c;RnR*p5nXujF?3KNjQjXqA6<|IDh{ zZa-czmv(sgoZSM&Op-&Z+wIM_lhuOrR|~?KljQ(6 zAwT>P7nUEK^iuGXwW`=^mSA>_JF;#OaG79cl;t%K~f(1jVSL~C~px1-EoW2QZO7@x)@RGMh9TT zxbezjISzu)ZwV&y(R65u9Br^?=LmI>5<;@OKM$iu59nJoVl$e#qll(PBqwmzWRZMc zgwy*K_}_~bydtNRS_W4dZXvWH-&MV)Ecev}1BJXQd+XDYymT`qUa1m6DG$8#B)^IQ zJG)|L$w>zBiqDO^)Qz)H1o1-Jg{eBP+V8t|{w!gr6QW zA9lNJnOshE4BOv87KB%)Qw)bxr~3>)txo(P;!90UOmP4>5 zneQX7d$Uohq3MW1i1T!pmqcUE3$~8+TgU``+lI56t1<|avawqof{91X07~Xb%2O+kmN$<#! zp6uZ9={?RHyd$?|2Vv{w`y%*q8<3MY=3SrRs>;-J13YL$Gd9TA4J=c+HcvFuc^9_` z%J?wEK*(7h(@|ZsPJ;b>cjebuqUp&!ozo)EenBSMpE<*CzXQ%=2PmD<-yguo%B=ed?2kg%qoq7Uju(&=`Ns!&*r~5YTl2yp{(y6e~aSt!A%8YniLa5A)RYG-?G6=stdW0Ap z{Xq89VvIX~W}N^%pY}sWxqm2Uh-UP{P86~so?d0xC7wQH_+C7n-YLfhaTQm7*$|_4 zdEtfW8(LQRU2<6sCbSJcmghm-eDSe7F_xx^LhK_4(!cE zHnN?#gGfMlJxXCXq#k7)gc~S|9hCR@&4oCUq+VnCiK*x}5Cl9GolDk}azH80_i^~T z^Q56C{q(8qEgZ0Q`|N=|fQ%;d$OeA)Wq`V$$vZ{JzkIHQ?DvHda-A;_a!(~en5(?; z)ZoRu#U4CE5rsid4?QfYjCjKjS#$T0d{MKVzB(*7idvhKpZ}e=*EZ;-zRRe)Ky$se z<#(NtyrsrHD7Q4-x|SLo!Q6a1jX5IE?#107R(M4P=P6kpzV7(!6Nl&P7gc|@$IZ@} zzXyC&DE^bi<7L`Q}2g1(aXxrk*^ zUdmvB!{eAa7_9N4yS+RbL%iB=GUb8Tv|{`m@a<}O?9a=^_}7JaR(!hyK5K2%DN+cc zv~RF1+FeN;rd83j@f-PlwC-MK<;qUOwhd>+6zkiwaty{D#&dGRR?dUCEYxA1cNC4d zi@HvxF^}gX&YzQLn}1HuDIJXh6YF1&85zZXD+f7-<1Zuj{Iho$`xK)X`SJV(M|;K3 z&ga*S_-8k7E@u9%n3)$sob7j2%vvm@eEuzJ0*dVWZ{--5%-IXZE*n4Q^-%l0jssRr ztbIQ-;oci9ej3w%RF*V#z=oSk2JN)D?07#nrRkgRwJ!b9rA9CQv1`tpcg~J2eAGOu z;qI@>{IQ?Ab5BY0XUUc;(}3l6y9-PVBv`Lc!{eWBSd=TB$$Z<)`^yZJ70P z(w7r%O`f-`%e!9S*WsVH6WK8ckas8PO>;E`&#i*zsex7vY z{DSW?_rIC2T$jB+>(az>6E7WU)vQsPoQFC3V)pZATUAH)ENxzlj z-?}>ZkacInbMZH{lQPbvcDt~rRMgR>o0pomoc?}Zm$%xzd8_Z0PfP8%;ySh9rGm*P8g9*bsnN8g z{-5ud-f88%MH7x(x_SP3(-zMs4eoX=W9^JXFL$UhAo%)=Hl=Dp&#YT3 zntuf=1wNA>(>uy-PX&>^BQ!hc$e4V&n~=m;Zg zdqj4|Ey&Py&E6OsY-ozM;@U%!TBZuEjZ!<%w(h1vy^Z3hJr`y}e7}HlBDM`nt&@xv`gh#FEl2l2R`!7+!;z%wTH%3#yB!@=wQ|?d^8L_y*@84Sdk34K?<#3z4}O9r z!$v0$La>q+vZ=^}KAW(d1iDKZL;jR0iYqMWbjcJ2=vh4tT`CQQ&;ts0r7n0QZ3&n#%KfQ~700^fJ zDcU!?=OfO|4r69{*vZBi5H254lw$%AM5m+JFDRhh>k>oAJ2~D(-{=@LUQW=_w-ARp zurT^0!aOB(n#{`(hZd)xiF5{m;}=SaE`fezY#W1(rc4i4x^J*F(v~|*aamC~hp#|g z#93%@{~TUH8+DdavZJH4v1gYssIA(oufj$uOj6x7gFEvtaMudjLf-CC+MX&Lq{0`@ zQi6viN;eZ0)5lcFPWS7G@+&o5p>Yj>!bNKucCA@C%o&IK2bQHB-j+~}4|VWs?@yU;b#IGB z;tpGbt6^hnWq#GBik?K(GM>o~_f zK&u?ShjwND1<>c~l*9Hv>4aWG@05@ z0re5>Id2MQl?E&cXjpILOB8UJ?)8`-DdWcr2jAXw`K`h$SI*{_z8c#zGR zF~}KElEc~-t32E-O(SgZrv-w-pH7y;B~Fxr>P z(dGoNDDAOwiVn`8K%s-9)dW#|GVHw)B$RL}SHm*EKT*Ot1eFesN8b=d`{uV0+I$h| zm=j9o=Aeg%QGBQPXiXS&Q$a2d6%4M_AB9u?Rlw8;N#Jf1GqD;fMSOI*eKHc0Q$HnnqLUBS2QJFhxx5&TFLz6ij>$6{#>X? z9oXK>m7wke`oT)HlFel+QzAFQ{};t9YD zRXh~@jk#WsW`fG>9UGv0KcN(nV7Xn(p8l)0r+<+{xa}uDvthL zx~}4LfPb&z+~?m=arExeO%-1Od>ZO0C#DYY=_<}Ujx$ue4e*)f=8C0`Fxvep z?dkTqihWCz^_o**gG%HezkYNot(fQQRU%ja$|3EN)x3hAD{rRVn_bEl%NFxamrk_e zHDkFF#rM3fONkl^@k5!EaV);X*^g&*BVhE% z^xos;j4{224D2l{hR#fCLWxUCwWhRM(WVk*7$2MN@tCK*hYZB6*RtYk`j8=`MyK~4 z96R7&6I}m4Je56$R`h7urbLC1J*xcMG;eXmk||L9H;-3)pZ74AC`tdllhw;9V^hWV zN)$Qq*ynDSO0AddOKMA5OG|>p?IfD(5MoHU^dDnOMd?9&{WDj8C){4iyEIwS&dq{)10C4 zB@>>Ap}b^Re944k{v_l2q7g!TR78am<;wgENhRwGm3rA;=Ubv+ke7YQ`)+dglDVoM zU9w8ip}wVLBMzmq){8D#8N*d8T2bEWR&V&o*xvtIuLI%JhvMFBZ7ETX#6JisS#hnb zS*>I_g26_~)J;T%_&1*bZ=j*kt$JLbw6AJZq@(StyP~edyg=Twbe?=At}8R@k$24q zGu;lVRGQ+h)ohsOogP`J6Xw6j2Af*9&SJ3*Xm_eEdj8`^|(#tAlkd^`4C}=BG z49X9z+JVJOSlk67V+QFIuv|g=M#T)`lEBS^#v3$@86;mUmA)rveAbCEgTybFy)S6B zRm>oz0Kw+FwUngK(DF*aeB8?w~u-A@*_;NrGEYTd4*1v=);bdmYS*L-pMk- zFs+~0u>23akwG-Olclj{5*2i^B)MdN;O#p)lz1YI$%B#n#FlFUmROU67j2#Zwo?l{yh?WWxKrr7|@V zC9ENo-PPjLE(LC}>nfhk!(CxnOJMc`#A>^Q49&WM$oc}ZuWYXrAlnN9WP4Qr*&fHM zoQ57gat|{W!YwZV8Kp~PdlVX+$pT9u@U}Q(xGK~^YvG{b^6p7Df^erU9v$t}wZ^@q zZZpQ*n9=3Zj4nTUg*KI>oH0M;?3vNl9d~~oNWP7VOk#}MgBpSVm){>i0T4&prKM)pGy z#aTw!wBce=Jh;j=m?fTGO2R)&5xvw3|12Z*5-(V>WYkMT@UPg*;!35kdBIF!F9pVO zNiQwOzhaT4wZdiDrMNgqg*HoYChy$^fvD6mh|S8%8>L)lIpc6o*JJ!Yus&$NN7xg5 z-Y0W*-y*K)z(Q&8`P8X1e>M2_&_s>4LQvK(I1Jbgy`v=;5bMl`B)>m^R<3TSl{&=Jd1#xy=`LwY0gXrHmU( ze!f`x$hd}O%CJ;}LE|GG7H0`rn+40#i(2{7?&)DJ<(CP5c~sX%LO}z&Yehj#5KIGY z?vMSJbZVPw3Dm5kfvJ|DOm6FF3{LKPRToj@sMi+E(l`&&mSZe_yzr@aGkIh!(@O2k zeoh=-9tB66wa#Qjdv1rL%tg+49TiMt)^_Fu*f~w$C$Hz~jVAX&rqZQNE@hY>Wak&L z_4Q4qO(tC#lPhlOZD%&%{3P;RT4a_e?VvW4`03UF_KHK|sBlERaR!HFmVp9$SpsO& zObeb`A7F8DT&L=T!s9a=_b9(|WEnd6v)<&vWy%7^9}d5u2eKtFvpUd$Iw^~DYcODU zKwR`DZct@2FDRa6RByz;{(M7d@M| z0}j404SCg>=xQu(mFn-)uFO^uVNt$o2YN~cxKAYF0L zt~+SlBPjMe` zSMW9JaWD3#b1{CGsvs2gKhgtSc=n6Fg4u(MgdJG$y+X^=Ea6HE?c#m{Wv`KX4WSO- zCWO(^b>&>>Qa_8I8y4R2bEELZZKLTUQyl}%OSkAfIzZkxGP{X}(3@5U*nG%PJ;^{v z!3=CA+KeCkQxdF&WwnB`T2dv1i5eKLIYH0$veYfbL36chLlb*hLiHTS?M8aDm!*0@ zQ{Zh;qOAe(qHR_Oz(ha`{m{$ude~RU>^>kFr86pKkXZFtp?$BR8WH8=GSN0w$_f=T zNTOfz#YNQaUAy7J=;k%<&3NgCI#7a#(g%$Rz^ zfA$_p%7Q$_-;6G|v-1Ul4yi3VwaM%py;yUN*QWhC9RJJoM+I_YL-PIn@7U1%5u5g~ z!CtF!+Ws^<(qU($FQeOEdx#HXUW8=nzmh2 zBWYf0efLb9kBEohRy?V|K})Ogm`8cs#jO+LD1JXXOweB zEqBoPQh;8e#sKx!jsD?*_cfF z##0-Hz2a#I!+r5ID;tx`CG=4?j*}e7JCki$;HD{{VKXduGz%$hrlpqVfMa|R49=mM zmPDR}dgoY9xUOB}s2Gu`BI00N7v8q1e=$@3LUgS-x}SboxRXcITU*9A28bxULI z9B9wT9=Ps)bx2tpM6$RbUi9d7%lm9`%W_zpLl>7@Uew<7rnEOK@jC1?Q~L4x0Tj0y z%CNiNu#}~QjD|qip&Yv& zt1Wv(>_kS*qc&@hmi|vm%ZWA4Y3WZV)*&t5t7%b(tba}mp9gqiS}v$*;l27NriD)% zuxs|34+G5j%d~vB-Z?FEX~PDjWx!L?LhZJ-DVLYB(XzFua0XEATqNkanjm$U{pSSn zVTUIs=)9UBK8Wzd1o2S{cFkV%Nr&lwnV`V;ESE$r)@(!7iO-AMX3^=m)M|ZT=_D94 zcYraQKG=a3p&X@pFS$}Hj+!O;m)3`(J;3tyot96vTmiT4hUFZ(wA=EFW-)z}ZwUxg zrwbLb78b8(3(0$r#owJCe%FVQZI8v>lTVYdH+*`e(3|p)28KPM zvUQ|@y_RMi_w>DJcpRPn`w*R3v}B)Ufu^5YO1*7(m*V?ct~7qXXgxG{zvUuZ9(@3o z7t@;uEDPzqrg8+WV5=`5wD{AmZneDg-Z^NQFMNIOb6D(0Lq4}0)=Z-YU;K|G(!-ue z&W>qMD>ISC5L$A`62J-Fe8{q1W89BI$a%@T!NZANss9m6t#Gf@-FUdfX1(iWJ$l#6 z?2Q50U9TQIjF(`PEPHAB5ldH(#~7qJYS~wVCqkd&(~I@mivm01pz)Oatf27~J*Nfs z65TqA**>41BnKR z+gTb(E5*_50!uaT!5UwzXl#=Lq;I5nsS?fw5}vU-y3gWC_X{j0-E_@3O26P8N@-nf zrf~b1?i=B2FERGv6qFU_by5OlA8rxpmw;4gqyZRcC`$pa33epUIKt_jW0paIUm(YN zLT%9M@Pcn^k)$4yAI)D2b-dhBRvdar;R#iUj_JC z=C!V84u3dw{877(#jh6*O?Z7m-G&ccErTzOsA@d=rvB`!&kg%z{lL*_j~4YCa%5cT z&qGU>tvt}HmPa?utBu~f|EBM?S8iOMJ+stCzdNbTo3C@3-)Z%d?MbC;YYsICOV9TF zW7C}O>C>)$-lp}e9qnJNcyaaZt{s=l%NDg6)~K#q=E>T>b^PY@udjVD_RD46Q?D2r z?|o%&;OE0O4I8)VRJ|kVJFRy9H^Gaq$Zxh?zqap=4yoBMPncS8tln2W!yeiB$FiRr zxt~Jo%&l9qcaJY87e)lRSMz;k_H8?VaMF~EzVCQcsr2FS0R!c;DPMowrs>$GndK+< zE&FAwdp|yUKPqW*Nbcx5u|0==F!1QA`}#4S?c81BZ9@;PjyU}20}uK7uV*WlZQ)vF zXK0^5xBcfmYDukKU!U4{@9b5j>$prhJnn7HyIuKcS*2>j z61r6x6F++ChTf+?yFT!XZ>F+5DEiU+Us)zO=@=xgtlHR>H3M+r@*!;~CPW{fvJ5uy zu$Y_jYgKQhQo;K@1IRYEVx);za(Jd@EVnn z!OQRo$c=mAXn!j1%8Cm$Alf5RaZ`#-RgDmQBwW|4h~EqRehMF_!OOVl8xMdS9w45B zF!Fm6rh(9f8mjuh65_*Q7=#a3Ug_Xd^=pSiSzx3%w@)E1bj*+TKREs=&H;8ht1O^O!;&%ho}d=Z2H2}K?cfqdGY>E!WySw$XK&X%MF zlbk#rN8xu=k8e-LTZ_{ak6E@QO~-N2>59ig5aZ|>*nF5J`1-!!kDsmh%XR47Y;0Z3 z6K|5F6gG2l{b?S~l~ew7sE(`vTb_>_;1)7l509k7i-b!O+5-iPuuii?(GBFcHo_q`WwjHX8wc4uVb9uV4#@FjV;Qh?9i} zAj~UJGMOzrEP(#cF((UEF{jQyu2|Rr3)Q}qq;J0D=R-nEAp!{}&?TNyvXI$_(Yd{N z+QI%7Ly%GD6gGE{L%+qG!h?Hvu?l)$@n94pu=_rAT7FgP zZWTme{I6K#c~Hdf{Rdv(_(SlG%;(opT4->y3|=>(u;lVCZhEaoO2>OC@k3y5dHf zgt%41_F6MMa55jSqzF%rVR&#QK(%r-EZW8Gt#TU4hLy1MQQpBqaceJzG^wE)92!St zg_WupLwpzJWTj2GM!JO8Mc7Iy2oq@=G-FMBNs@=D2?<324(XD@z4q<=1Yt^ z{Hr)cwXljt`YFySG4I7|qw9rt1+F9m{=2#-X=;+NLcv-@ySnivQhyD7&f$2Bl`ssv#=F%q_tW$v*5|J)*8vD zy_zE#C_dd~n9{SN!;QQw^jwNY8rV?{L$KR-a$>LPq>+wyQP^(X;Qt3*RW6G*?`|5Y za}T@UQBf|ssGbU2$S=1BJvGw$R6OLe6fe@WgX;iUBlVXNJAN7nS^Emi*;Y7N8657> z7y@QR8mRb%ljeN0K)34{!=LM`p_S$^#efsatE7;hj#tww01+M}Fo{4`1< zC5B0I^+#;!?7+GNFslGsL*HBu}dW^rWLjj0-`{xl~~hD_5) z-Df!2C_7Ul^_eMbh(cQe^Tb(-1zLe8soWaQ)<_w%mB)$MFY6y|hXV%jJEi zv(D5&4@^}DYg{%g`AOPeN&iH}Ys0fgaH6!UjEZtkl1WkSIb{+`J(^*3^olMTdTn7> z@h}g0Dk?wMJQbCm94|$s=Y*G{(lf+cQR&f`6_uVQ0e&=&l0gpPX!-E zrDv3nqSEt;k5K8+`uIZc4g7V!ibmO8Uqv%2xs0NB_e~i^Ln^MUqM8IvE1`$i$WPJ3 zd-e#oN8XK)qAHkWWJc1+ixjumi;U6zfVFKXL=VH7X zK3=S1JQN=`IiO~&Wo<7HdMF`^gC(Tu-}k9D{hUx5z;HK}K{XD|9Zd(j8VVI@1r zYd?tItwaZm`3yRR{p?yYSC1b;pI^1d=At9%niqn)7nz`!*1O2mp>A}^^hy4E$JI-A z@En?JC3EmEpHweJw5jIgd1K3$SR3t`?kLo7dk-HvboekuV>U5k;J>OXYi`hKKzzv` z>420NKq<|tl}sPbu4d(5qRRK*R5_j3F1Q0FTna0huH%1C?=+ptmub6(R%H0SL2>%- z?^8-v8yn#TokE)3zCDG^&&)o7%4K|5;lp+%)>9R;!pn`dA~ika`Fr zwsi$&kj?_bf*~SvUd0U3@4!+8?U9N>_XsPGST}^dV8-ayy@Y_wEE37R;iCfs7(>hbq%igcN3{cC!G zj`wWx1~;;H)nta_#6b{3;gCG=VK+tmc*Bu@)4=x(rmWLREgWLH0}EO}k0fik5T0qnqF!e*7}&1kcs_!~`Jm(^>sC*mnX^5f zi6emXhpbg7ZoSo&>NT;7ON84uv5wUJ-^dl6nk&sYR~D+daz)J*^_be9bLFy{D}W+b z)FV?*%#};Xm6pgAUdLd2y!Lz(&QTWehnjtxe)M+si@rdI8(TLO{vEV_&aY}4)=lf z<6yet7Or~J@%GmDJRet>aEkvYDN3oHPOZBxbbHntD6Kc5f=e zRy78J`x7XVQRWzHFx@&<#hr!^MNCkD z)eNgf)ZDm{NHBy(3PT8ujFAu)KYHgO&AmjXx{S826}5Kb7$u7iGej2sFa|d7h}`j` zT4Sw0^9kW9&s*y-w6_&K#p!IWH2Y*llb%;rxzRjt9i`FstW1&P;lU5y)M>o+9x{ho zzhGUMsVEn+4oS}O{5JyQBHP>5ezKFhy08 z^lEoMC)qv!k?#1_8lM`!nzGxgUB2RV{(ar?LgUen-wAI%F+p?0vey*`9`)#P>tejC z&8I_s5O|QJmzo3PA(URI9P!XXFLZ?zYyh|l#@q1Is2X56iKdvv&`UtL71kvRgEG`K^XPb2yP$MMvH$UrJQ zXN{vzH(E`ZENyY1)^(x@&A`=U(n9^ut$3wZ#}F>*CY_Oa&MJKN@kO%96)mlnNhdlp z=QhTp3V5!qVbXf29z@t1Y#QQZ=SP{`OPh2CldcW7SXY^E;jfBBNqVw&MfkTt5MjEEI={4mPm1Nd9eEP5` zi3cD0wXW<(ThC|~6LpkXhAk*2*b%V{ms+sEon=7hjE=x9ZdEU^{>u=Qy zql1~&ikWVxuaWqSLj9F7dtid%{Dkub&3W_C`3JQ&-fqg4MWvU1zJC<5qMBsZ~L)rc8O>O5`{qjmJhzCoaF@_9?jy?4vc1VT*OE;<|yvWP+iRE;toaToSMqO)!E7MN0Y9G+Wp!evnS@{ zjN}J7dvnG-Q2p&HhRH#SOBbA}MIZKi%>60%yjuL2)8&3lm!I+<>W*K5hVzt9y;^)0 zvp4@&onsiC)5YC>HHh)YDUZBb+!upj<1byQ!y2m(UiGZm*cwUGR$6_Z8w{V7A!{}; zWw@K;pe=ASHQQ2hD9%}^bXAr5BH%se>ay>?BFuKvOER#p(-0On0 z(t4Y87y*Yp#fHA8NgHC)x~k(_=Mk_ves$@}gI9fHle-urV@)m|jaT`pR`k&(H@p`9 zxF}MGuDSq|i-FF+ZuJSU4`%g$F_=vg9#>EXvsHzmhSOly%_KZrph3{uyxwvNku8QlWT?V{hAs$vG|T?Z}K zK|AT7op#W8B&oPfQ8D!AGL~svUxZAA06uZ&71_OeV8?EJe zK=5FrwGzYVO&AMdV9;z6%zz`i#O4bVO|D7(Jt>4UPev^&@8!egTSxz_$7 za`U%gK;|dvFXK=n$CO+54_8==`{2a^6X@ zcSFOlvco**`v6p)>jY-lN6tlyE#CC#BkP1G3|ZBEr4M%~I}k^ASVw68YZ>FNWX$D{ zkulgg5*dT>ugDlckuewtbH=o{3xp?SjJ<6?DPvUL;~Aq)DCyUatYKmzNs&8%4Qq)( z&W(I)?9;cJ>=`wD4=QzA=Mfr@Ed~~=0legBuR%Ot>{E>IT!RAWyus$9#gyyBCupSi zMg8?5|9#fa(MT`tvxfIjcbIsFw8@KL9T|^sy zVZSxl&psDRbFNEH4cTuKSJ+A$6oRZRr^KRyK$0cW4EekJx3dY(ct{_34K z^OSWePrS^h{}U6hE%d3c&8+uBpMg~WaK?I5$k?n45ct^4SiumR8M_6Hp~4F&20Ven zk~H(2^>xgJcVEONDCAJWx7M%_``T}5lQEo&|8e<4;g_&&RwPztJzb(;ZAI)<&oBHZ zF~W6uYK+{d(naeM%#ua4P(&hH_SK~)MGG@zj+WNms4bUJIiJ!yyOKR#H7{H9L{8nj zqU4mvRVAmY3Mg_af`(tkzSnQwS;l(IQ*<%)c<;rVn?ajZ7@(L8WvvAx=6Kz|Cjyr4__TJhbvl8w8$-0yK zl>b0Ont#vw88@WN+s;ujDh)~Zll6uFR70`{Z23iPNVR`ML;4*D2JT`nH2XJY8RI|D znh@|d`sfd98BX@GKdhZZGtPCjiI#MfAzIRH0b|HpZ;QpFL0oSaet6Qj_)FVcToNy6 zu#AG%v`m9#6~K=eqIW;7!LkZqrW+tunqG6m$`fEUtu4T*MPc3y3p7V->-Ds@B{mX8 z9GmNG{r^6WCZ%n4)Y~?(=o1k{yAlxu6cI!(DTbIv+SWFMl6NN=BJb`A7(-=^wpi^0v~4}onr5`+p;`HxY~dBuUd#=m8Wkd_N*V

k@iW-8@joAB37 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 delta 1268114 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

csXkW3!l8YJWSeq58jgRnm8Xp0V~83|{LXNO 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

B-mgIL8~SON$LuZ79R> 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�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 E1z!9>ZU6uP diff --git a/external/sources/allegro 4.4.3.1-custom/_Bin/allegro-debug-release.lib b/external/sources/allegro 4.4.3.1-custom/_Bin/allegro-debug-release.lib index 0f862d120324114cab213827328164f75368b2b5..9f2ce24ae9a969421a3051ed793ffd81439e2b2c 100644 GIT binary patch delta 1584167 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
mR|%P#ZBE9%Uc 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μOWh5{tClhGt=Z<2DZVa??B#n==v&^6!r}bl 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

$MVk8+_`Cpnuw@8M2$k!5YuUg zbQeB%>!<=d|32-bEyD9(c5gVg^5)p;p?+ym7aLc(G$?gg`J$NO0!`iYYd^0WQP#VA z<>!4jTc7EgyFK>g^+_A1cdU9*5nt{5+VV~MUA?&V4PU&OTUIAC{=(_19xq0p>GRh6 zn=xN@t*eY5uf7;KYuAjep`Y*F)xFB@-9u}%t^CR0ELW8&A+r0l%IO}Jeq5K~u|4+N zqfeWEa_{EvT|3@(H##*9`eT%A=9UHx^A5ySKl|puk|jy&?@UcBT

`$z`((-*(vp{(EeFB3se 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